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_AQR405	0x03a1b4b0
 25
 26#define PHY_AQUANTIA_FEATURES	(SUPPORTED_10000baseT_Full | \
 27				 SUPPORTED_1000baseT_Full | \
 28				 SUPPORTED_100baseT_Full | \
 29				 PHY_DEFAULT_FEATURES)
 30
 31static int aquantia_config_aneg(struct phy_device *phydev)
 32{
 33	phydev->supported = PHY_AQUANTIA_FEATURES;
 34	phydev->advertising = phydev->supported;
 35
 36	return 0;
 37}
 38
 39static int aquantia_aneg_done(struct phy_device *phydev)
 40{
 41	int reg;
 42
 43	reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
 44	return (reg < 0) ? reg : (reg & BMSR_ANEGCOMPLETE);
 45}
 46
 47static int aquantia_config_intr(struct phy_device *phydev)
 48{
 49	int err;
 50
 51	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
 52		err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 1);
 53		if (err < 0)
 54			return err;
 55
 56		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 1);
 57		if (err < 0)
 58			return err;
 59
 60		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0x1001);
 61	} else {
 62		err = phy_write_mmd(phydev, MDIO_MMD_AN, 0xd401, 0);
 63		if (err < 0)
 64			return err;
 65
 66		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff00, 0);
 67		if (err < 0)
 68			return err;
 69
 70		err = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xff01, 0);
 71	}
 72
 73	return err;
 74}
 75
 76static int aquantia_ack_interrupt(struct phy_device *phydev)
 77{
 78	int reg;
 79
 80	reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xcc01);
 81	return (reg < 0) ? reg : 0;
 82}
 83
 84static int aquantia_read_status(struct phy_device *phydev)
 85{
 86	int reg;
 87
 88	reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
 89	reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
 90	if (reg & MDIO_STAT1_LSTATUS)
 91		phydev->link = 1;
 92	else
 93		phydev->link = 0;
 94
 95	reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800);
 96	mdelay(10);
 97	reg = phy_read_mmd(phydev, MDIO_MMD_AN, 0xc800);
 98
 99	switch (reg) {
100	case 0x9:
101		phydev->speed = SPEED_2500;
102		break;
103	case 0x5:
104		phydev->speed = SPEED_1000;
105		break;
106	case 0x3:
107		phydev->speed = SPEED_100;
108		break;
109	case 0x7:
110	default:
111		phydev->speed = SPEED_10000;
112		break;
113	}
114	phydev->duplex = DUPLEX_FULL;
115
116	return 0;
117}
118
119static struct phy_driver aquantia_driver[] = {
120{
121	.phy_id		= PHY_ID_AQ1202,
122	.phy_id_mask	= 0xfffffff0,
123	.name		= "Aquantia AQ1202",
124	.features	= PHY_AQUANTIA_FEATURES,
125	.flags		= PHY_HAS_INTERRUPT,
126	.aneg_done	= aquantia_aneg_done,
127	.config_aneg    = aquantia_config_aneg,
128	.config_intr	= aquantia_config_intr,
129	.ack_interrupt	= aquantia_ack_interrupt,
130	.read_status	= aquantia_read_status,
131},
132{
133	.phy_id		= PHY_ID_AQ2104,
134	.phy_id_mask	= 0xfffffff0,
135	.name		= "Aquantia AQ2104",
136	.features	= PHY_AQUANTIA_FEATURES,
137	.flags		= PHY_HAS_INTERRUPT,
138	.aneg_done	= aquantia_aneg_done,
139	.config_aneg    = aquantia_config_aneg,
140	.config_intr	= aquantia_config_intr,
141	.ack_interrupt	= aquantia_ack_interrupt,
142	.read_status	= aquantia_read_status,
143},
144{
145	.phy_id		= PHY_ID_AQR105,
146	.phy_id_mask	= 0xfffffff0,
147	.name		= "Aquantia AQR105",
148	.features	= PHY_AQUANTIA_FEATURES,
149	.flags		= PHY_HAS_INTERRUPT,
150	.aneg_done	= aquantia_aneg_done,
151	.config_aneg    = aquantia_config_aneg,
152	.config_intr	= aquantia_config_intr,
153	.ack_interrupt	= aquantia_ack_interrupt,
154	.read_status	= aquantia_read_status,
155},
156{
157	.phy_id		= PHY_ID_AQR405,
158	.phy_id_mask	= 0xfffffff0,
159	.name		= "Aquantia AQR405",
160	.features	= PHY_AQUANTIA_FEATURES,
161	.flags		= PHY_HAS_INTERRUPT,
162	.aneg_done	= aquantia_aneg_done,
163	.config_aneg    = aquantia_config_aneg,
164	.config_intr	= aquantia_config_intr,
165	.ack_interrupt	= aquantia_ack_interrupt,
166	.read_status	= aquantia_read_status,
167},
168};
169
170module_phy_driver(aquantia_driver);
171
172static struct mdio_device_id __maybe_unused aquantia_tbl[] = {
173	{ PHY_ID_AQ1202, 0xfffffff0 },
174	{ PHY_ID_AQ2104, 0xfffffff0 },
175	{ PHY_ID_AQR105, 0xfffffff0 },
176	{ PHY_ID_AQR405, 0xfffffff0 },
177	{ }
178};
179
180MODULE_DEVICE_TABLE(mdio, aquantia_tbl);
181
182MODULE_DESCRIPTION("Aquantia PHY driver");
183MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
184MODULE_LICENSE("GPL v2");