Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  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_aneg_done(struct phy_device *phydev)
 42{
 43	int reg;
 44
 45	reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
 46	return (reg < 0) ? reg : (reg & BMSR_ANEGCOMPLETE);
 47}
 48
 49static int aquantia_config_intr(struct phy_device *phydev)
 50{
 51	int err;
 52
 53	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
 54		err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 1);
 55		if (err < 0)
 56			return err;
 57
 58		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 1);
 59		if (err < 0)
 60			return err;
 61
 62		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0x1001);
 63	} else {
 64		err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 0);
 65		if (err < 0)
 66			return err;
 67
 68		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 0);
 69		if (err < 0)
 70			return err;
 71
 72		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0);
 73	}
 74
 75	return err;
 76}
 77
 78static int aquantia_ack_interrupt(struct phy_device *phydev)
 79{
 80	int reg;
 81
 82	reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xcc01);
 83	return (reg < 0) ? reg : 0;
 84}
 85
 86static int aquantia_read_status(struct phy_device *phydev)
 87{
 88	int reg;
 89
 90	reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
 91	reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
 92	if (reg & MDIO_STAT1_LSTATUS)
 93		phydev->link = 1;
 94	else
 95		phydev->link = 0;
 96
 97	reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800);
 98	mdelay(10);
 99	reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800);
100
101	switch (reg) {
102	case 0x9:
103		phydev->speed = SPEED_2500;
104		break;
105	case 0x5:
106		phydev->speed = SPEED_1000;
107		break;
108	case 0x3:
109		phydev->speed = SPEED_100;
110		break;
111	case 0x7:
112	default:
113		phydev->speed = SPEED_10000;
114		break;
115	}
116	phydev->duplex = DUPLEX_FULL;
117
118	return 0;
119}
120
121static struct phy_driver aquantia_driver[] = {
122{
123	.phy_id		= PHY_ID_AQ1202,
124	.phy_id_mask	= 0xfffffff0,
125	.name		= "Aquantia AQ1202",
126	.features	= PHY_AQUANTIA_FEATURES,
127	.flags		= PHY_HAS_INTERRUPT,
128	.aneg_done	= aquantia_aneg_done,
129	.config_aneg    = aquantia_config_aneg,
130	.config_intr	= aquantia_config_intr,
131	.ack_interrupt	= aquantia_ack_interrupt,
132	.read_status	= aquantia_read_status,
133},
134{
135	.phy_id		= PHY_ID_AQ2104,
136	.phy_id_mask	= 0xfffffff0,
137	.name		= "Aquantia AQ2104",
138	.features	= PHY_AQUANTIA_FEATURES,
139	.flags		= PHY_HAS_INTERRUPT,
140	.aneg_done	= aquantia_aneg_done,
141	.config_aneg    = aquantia_config_aneg,
142	.config_intr	= aquantia_config_intr,
143	.ack_interrupt	= aquantia_ack_interrupt,
144	.read_status	= aquantia_read_status,
145},
146{
147	.phy_id		= PHY_ID_AQR105,
148	.phy_id_mask	= 0xfffffff0,
149	.name		= "Aquantia AQR105",
150	.features	= PHY_AQUANTIA_FEATURES,
151	.flags		= PHY_HAS_INTERRUPT,
152	.aneg_done	= aquantia_aneg_done,
153	.config_aneg    = aquantia_config_aneg,
154	.config_intr	= aquantia_config_intr,
155	.ack_interrupt	= aquantia_ack_interrupt,
156	.read_status	= aquantia_read_status,
157},
158{
159	.phy_id		= PHY_ID_AQR106,
160	.phy_id_mask	= 0xfffffff0,
161	.name		= "Aquantia AQR106",
162	.features	= PHY_AQUANTIA_FEATURES,
163	.flags		= PHY_HAS_INTERRUPT,
164	.aneg_done	= aquantia_aneg_done,
165	.config_aneg    = aquantia_config_aneg,
166	.config_intr	= aquantia_config_intr,
167	.ack_interrupt	= aquantia_ack_interrupt,
168	.read_status	= aquantia_read_status,
169},
170{
171	.phy_id		= PHY_ID_AQR107,
172	.phy_id_mask	= 0xfffffff0,
173	.name		= "Aquantia AQR107",
174	.features	= PHY_AQUANTIA_FEATURES,
175	.flags		= PHY_HAS_INTERRUPT,
176	.aneg_done	= aquantia_aneg_done,
177	.config_aneg    = aquantia_config_aneg,
178	.config_intr	= aquantia_config_intr,
179	.ack_interrupt	= aquantia_ack_interrupt,
180	.read_status	= aquantia_read_status,
181},
182{
183	.phy_id		= PHY_ID_AQR405,
184	.phy_id_mask	= 0xfffffff0,
185	.name		= "Aquantia AQR405",
186	.features	= PHY_AQUANTIA_FEATURES,
187	.flags		= PHY_HAS_INTERRUPT,
188	.aneg_done	= aquantia_aneg_done,
189	.config_aneg    = aquantia_config_aneg,
190	.config_intr	= aquantia_config_intr,
191	.ack_interrupt	= aquantia_ack_interrupt,
192	.read_status	= aquantia_read_status,
193},
194};
195
196module_phy_driver(aquantia_driver);
197
198static struct mdio_device_id __maybe_unused aquantia_tbl[] = {
199	{ PHY_ID_AQ1202, 0xfffffff0 },
200	{ PHY_ID_AQ2104, 0xfffffff0 },
201	{ PHY_ID_AQR105, 0xfffffff0 },
202	{ PHY_ID_AQR106, 0xfffffff0 },
203	{ PHY_ID_AQR107, 0xfffffff0 },
204	{ PHY_ID_AQR405, 0xfffffff0 },
205	{ }
206};
207
208MODULE_DEVICE_TABLE(mdio, aquantia_tbl);
209
210MODULE_DESCRIPTION("Aquantia PHY driver");
211MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
212MODULE_LICENSE("GPL v2");