Linux Audio

Check our new training course

Loading...
v4.17
  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");
v4.10.11
  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");