Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  1/*
  2 * Driver for Aquantia PHY
  3 *
  4 * Author: Shaohui Xie <Shaohui.Xie@freescale.com>
  5 *
  6 * Copyright 2015 Freescale Semiconductor, Inc.
  7 *
  8 * This file is licensed under the terms of the GNU General Public License
  9 * version 2.  This program is licensed "as is" without any warranty of any
 10 * kind, whether express or implied.
 11 */
 12
 13#include <linux/kernel.h>
 14#include <linux/module.h>
 15#include <linux/delay.h>
 16#include <linux/mii.h>
 17#include <linux/ethtool.h>
 18#include <linux/phy.h>
 19#include <linux/mdio.h>
 20
 21#define PHY_ID_AQ1202	0x03a1b445
 22#define PHY_ID_AQ2104	0x03a1b460
 23#define PHY_ID_AQR105	0x03a1b4a2
 24#define PHY_ID_AQR106	0x03a1b4d0
 25#define PHY_ID_AQR107	0x03a1b4e0
 26#define PHY_ID_AQR405	0x03a1b4b0
 27
 28#define PHY_AQUANTIA_FEATURES	(SUPPORTED_10000baseT_Full | \
 29				 SUPPORTED_1000baseT_Full | \
 30				 SUPPORTED_100baseT_Full | \
 31				 PHY_DEFAULT_FEATURES)
 32
 33static int aquantia_config_aneg(struct phy_device *phydev)
 34{
 35	phydev->supported = PHY_AQUANTIA_FEATURES;
 36	phydev->advertising = phydev->supported;
 37
 38	return 0;
 39}
 40
 41static int aquantia_config_intr(struct phy_device *phydev)
 42{
 43	int err;
 44
 45	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
 46		err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 1);
 47		if (err < 0)
 48			return err;
 49
 50		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 1);
 51		if (err < 0)
 52			return err;
 53
 54		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0x1001);
 55	} else {
 56		err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 0);
 57		if (err < 0)
 58			return err;
 59
 60		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 0);
 61		if (err < 0)
 62			return err;
 63
 64		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0);
 65	}
 66
 67	return err;
 68}
 69
 70static int aquantia_ack_interrupt(struct phy_device *phydev)
 71{
 72	int reg;
 73
 74	reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xcc01);
 75	return (reg < 0) ? reg : 0;
 76}
 77
 78static int aquantia_read_status(struct phy_device *phydev)
 79{
 80	int reg;
 81
 82	reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
 83	reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
 84	if (reg & MDIO_STAT1_LSTATUS)
 85		phydev->link = 1;
 86	else
 87		phydev->link = 0;
 88
 89	reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800);
 90	mdelay(10);
 91	reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800);
 92
 93	switch (reg) {
 94	case 0x9:
 95		phydev->speed = SPEED_2500;
 96		break;
 97	case 0x5:
 98		phydev->speed = SPEED_1000;
 99		break;
100	case 0x3:
101		phydev->speed = SPEED_100;
102		break;
103	case 0x7:
104	default:
105		phydev->speed = SPEED_10000;
106		break;
107	}
108	phydev->duplex = DUPLEX_FULL;
109
110	return 0;
111}
112
113static struct phy_driver aquantia_driver[] = {
114{
115	.phy_id		= PHY_ID_AQ1202,
116	.phy_id_mask	= 0xfffffff0,
117	.name		= "Aquantia AQ1202",
118	.features	= PHY_AQUANTIA_FEATURES,
119	.flags		= PHY_HAS_INTERRUPT,
120	.aneg_done	= genphy_c45_aneg_done,
121	.config_aneg    = aquantia_config_aneg,
122	.config_intr	= aquantia_config_intr,
123	.ack_interrupt	= aquantia_ack_interrupt,
124	.read_status	= aquantia_read_status,
125},
126{
127	.phy_id		= PHY_ID_AQ2104,
128	.phy_id_mask	= 0xfffffff0,
129	.name		= "Aquantia AQ2104",
130	.features	= PHY_AQUANTIA_FEATURES,
131	.flags		= PHY_HAS_INTERRUPT,
132	.aneg_done	= genphy_c45_aneg_done,
133	.config_aneg    = aquantia_config_aneg,
134	.config_intr	= aquantia_config_intr,
135	.ack_interrupt	= aquantia_ack_interrupt,
136	.read_status	= aquantia_read_status,
137},
138{
139	.phy_id		= PHY_ID_AQR105,
140	.phy_id_mask	= 0xfffffff0,
141	.name		= "Aquantia AQR105",
142	.features	= PHY_AQUANTIA_FEATURES,
143	.flags		= PHY_HAS_INTERRUPT,
144	.aneg_done	= genphy_c45_aneg_done,
145	.config_aneg    = aquantia_config_aneg,
146	.config_intr	= aquantia_config_intr,
147	.ack_interrupt	= aquantia_ack_interrupt,
148	.read_status	= aquantia_read_status,
149},
150{
151	.phy_id		= PHY_ID_AQR106,
152	.phy_id_mask	= 0xfffffff0,
153	.name		= "Aquantia AQR106",
154	.features	= PHY_AQUANTIA_FEATURES,
155	.flags		= PHY_HAS_INTERRUPT,
156	.aneg_done	= genphy_c45_aneg_done,
157	.config_aneg    = aquantia_config_aneg,
158	.config_intr	= aquantia_config_intr,
159	.ack_interrupt	= aquantia_ack_interrupt,
160	.read_status	= aquantia_read_status,
161},
162{
163	.phy_id		= PHY_ID_AQR107,
164	.phy_id_mask	= 0xfffffff0,
165	.name		= "Aquantia AQR107",
166	.features	= PHY_AQUANTIA_FEATURES,
167	.flags		= PHY_HAS_INTERRUPT,
168	.aneg_done	= genphy_c45_aneg_done,
169	.config_aneg    = aquantia_config_aneg,
170	.config_intr	= aquantia_config_intr,
171	.ack_interrupt	= aquantia_ack_interrupt,
172	.read_status	= aquantia_read_status,
173},
174{
175	.phy_id		= PHY_ID_AQR405,
176	.phy_id_mask	= 0xfffffff0,
177	.name		= "Aquantia AQR405",
178	.features	= PHY_AQUANTIA_FEATURES,
179	.flags		= PHY_HAS_INTERRUPT,
180	.aneg_done	= genphy_c45_aneg_done,
181	.config_aneg    = aquantia_config_aneg,
182	.config_intr	= aquantia_config_intr,
183	.ack_interrupt	= aquantia_ack_interrupt,
184	.read_status	= aquantia_read_status,
185},
186};
187
188module_phy_driver(aquantia_driver);
189
190static struct mdio_device_id __maybe_unused aquantia_tbl[] = {
191	{ PHY_ID_AQ1202, 0xfffffff0 },
192	{ PHY_ID_AQ2104, 0xfffffff0 },
193	{ PHY_ID_AQR105, 0xfffffff0 },
194	{ PHY_ID_AQR106, 0xfffffff0 },
195	{ PHY_ID_AQR107, 0xfffffff0 },
196	{ PHY_ID_AQR405, 0xfffffff0 },
197	{ }
198};
199
200MODULE_DEVICE_TABLE(mdio, aquantia_tbl);
201
202MODULE_DESCRIPTION("Aquantia PHY driver");
203MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
204MODULE_LICENSE("GPL v2");