Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * drivers/net/phy/at803x.c
  3 *
  4 * Driver for Atheros 803x PHY
  5 *
  6 * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
  7 *
  8 * This program is free software; you can redistribute  it and/or modify it
  9 * under  the terms of  the GNU General  Public License as published by the
 10 * Free Software Foundation;  either version 2 of the  License, or (at your
 11 * option) any later version.
 12 */
 13
 14#include <linux/phy.h>
 15#include <linux/module.h>
 16#include <linux/string.h>
 17#include <linux/netdevice.h>
 18#include <linux/etherdevice.h>
 19
 20#define AT803X_INTR_ENABLE			0x12
 21#define AT803X_INTR_STATUS			0x13
 22#define AT803X_WOL_ENABLE			0x01
 23#define AT803X_DEVICE_ADDR			0x03
 24#define AT803X_LOC_MAC_ADDR_0_15_OFFSET		0x804C
 25#define AT803X_LOC_MAC_ADDR_16_31_OFFSET	0x804B
 26#define AT803X_LOC_MAC_ADDR_32_47_OFFSET	0x804A
 27#define AT803X_MMD_ACCESS_CONTROL		0x0D
 28#define AT803X_MMD_ACCESS_CONTROL_DATA		0x0E
 29#define AT803X_FUNC_DATA			0x4003
 30#define AT803X_INER				0x0012
 31#define AT803X_INER_INIT			0xec00
 32#define AT803X_INSR				0x0013
 33#define AT803X_DEBUG_ADDR			0x1D
 34#define AT803X_DEBUG_DATA			0x1E
 35#define AT803X_DEBUG_SYSTEM_MODE_CTRL		0x05
 36#define AT803X_DEBUG_RGMII_TX_CLK_DLY		BIT(8)
 37
 38MODULE_DESCRIPTION("Atheros 803x PHY driver");
 39MODULE_AUTHOR("Matus Ujhelyi");
 40MODULE_LICENSE("GPL");
 41
 42static int at803x_set_wol(struct phy_device *phydev,
 43			  struct ethtool_wolinfo *wol)
 44{
 45	struct net_device *ndev = phydev->attached_dev;
 46	const u8 *mac;
 47	int ret;
 48	u32 value;
 49	unsigned int i, offsets[] = {
 50		AT803X_LOC_MAC_ADDR_32_47_OFFSET,
 51		AT803X_LOC_MAC_ADDR_16_31_OFFSET,
 52		AT803X_LOC_MAC_ADDR_0_15_OFFSET,
 53	};
 54
 55	if (!ndev)
 56		return -ENODEV;
 57
 58	if (wol->wolopts & WAKE_MAGIC) {
 59		mac = (const u8 *) ndev->dev_addr;
 60
 61		if (!is_valid_ether_addr(mac))
 62			return -EFAULT;
 63
 64		for (i = 0; i < 3; i++) {
 65			phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
 66				  AT803X_DEVICE_ADDR);
 67			phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
 68				  offsets[i]);
 69			phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
 70				  AT803X_FUNC_DATA);
 71			phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
 72				  mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
 73		}
 74
 75		value = phy_read(phydev, AT803X_INTR_ENABLE);
 76		value |= AT803X_WOL_ENABLE;
 77		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
 78		if (ret)
 79			return ret;
 80		value = phy_read(phydev, AT803X_INTR_STATUS);
 81	} else {
 82		value = phy_read(phydev, AT803X_INTR_ENABLE);
 83		value &= (~AT803X_WOL_ENABLE);
 84		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
 85		if (ret)
 86			return ret;
 87		value = phy_read(phydev, AT803X_INTR_STATUS);
 88	}
 89
 90	return ret;
 91}
 92
 93static void at803x_get_wol(struct phy_device *phydev,
 94			   struct ethtool_wolinfo *wol)
 95{
 96	u32 value;
 97
 98	wol->supported = WAKE_MAGIC;
 99	wol->wolopts = 0;
100
101	value = phy_read(phydev, AT803X_INTR_ENABLE);
102	if (value & AT803X_WOL_ENABLE)
103		wol->wolopts |= WAKE_MAGIC;
104}
105
106static int at803x_suspend(struct phy_device *phydev)
107{
108	int value;
109	int wol_enabled;
110
111	mutex_lock(&phydev->lock);
112
113	value = phy_read(phydev, AT803X_INTR_ENABLE);
114	wol_enabled = value & AT803X_WOL_ENABLE;
115
116	value = phy_read(phydev, MII_BMCR);
117
118	if (wol_enabled)
119		value |= BMCR_ISOLATE;
120	else
121		value |= BMCR_PDOWN;
122
123	phy_write(phydev, MII_BMCR, value);
124
125	mutex_unlock(&phydev->lock);
126
127	return 0;
128}
129
130static int at803x_resume(struct phy_device *phydev)
131{
132	int value;
133
134	mutex_lock(&phydev->lock);
135
136	value = phy_read(phydev, MII_BMCR);
137	value &= ~(BMCR_PDOWN | BMCR_ISOLATE);
138	phy_write(phydev, MII_BMCR, value);
139
140	mutex_unlock(&phydev->lock);
141
142	return 0;
143}
144
145static int at803x_config_init(struct phy_device *phydev)
146{
147	int val;
148	int ret;
149	u32 features;
150
151	features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI |
152		   SUPPORTED_FIBRE | SUPPORTED_BNC;
153
154	val = phy_read(phydev, MII_BMSR);
155	if (val < 0)
156		return val;
157
158	if (val & BMSR_ANEGCAPABLE)
159		features |= SUPPORTED_Autoneg;
160	if (val & BMSR_100FULL)
161		features |= SUPPORTED_100baseT_Full;
162	if (val & BMSR_100HALF)
163		features |= SUPPORTED_100baseT_Half;
164	if (val & BMSR_10FULL)
165		features |= SUPPORTED_10baseT_Full;
166	if (val & BMSR_10HALF)
167		features |= SUPPORTED_10baseT_Half;
168
169	if (val & BMSR_ESTATEN) {
170		val = phy_read(phydev, MII_ESTATUS);
171		if (val < 0)
172			return val;
173
174		if (val & ESTATUS_1000_TFULL)
175			features |= SUPPORTED_1000baseT_Full;
176		if (val & ESTATUS_1000_THALF)
177			features |= SUPPORTED_1000baseT_Half;
178	}
179
180	phydev->supported = features;
181	phydev->advertising = features;
182
183	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
184		ret = phy_write(phydev, AT803X_DEBUG_ADDR,
185				AT803X_DEBUG_SYSTEM_MODE_CTRL);
186		if (ret)
187			return ret;
188		ret = phy_write(phydev, AT803X_DEBUG_DATA,
189				AT803X_DEBUG_RGMII_TX_CLK_DLY);
190		if (ret)
191			return ret;
192	}
193
194	return 0;
195}
196
197static int at803x_ack_interrupt(struct phy_device *phydev)
198{
199	int err;
200
201	err = phy_read(phydev, AT803X_INSR);
202
203	return (err < 0) ? err : 0;
204}
205
206static int at803x_config_intr(struct phy_device *phydev)
207{
208	int err;
209	int value;
210
211	value = phy_read(phydev, AT803X_INER);
212
213	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
214		err = phy_write(phydev, AT803X_INER,
215				value | AT803X_INER_INIT);
216	else
217		err = phy_write(phydev, AT803X_INER, 0);
218
219	return err;
220}
221
222static struct phy_driver at803x_driver[] = {
223{
224	/* ATHEROS 8035 */
225	.phy_id		= 0x004dd072,
226	.name		= "Atheros 8035 ethernet",
227	.phy_id_mask	= 0xffffffef,
228	.config_init	= at803x_config_init,
229	.set_wol	= at803x_set_wol,
230	.get_wol	= at803x_get_wol,
231	.suspend	= at803x_suspend,
232	.resume		= at803x_resume,
233	.features	= PHY_GBIT_FEATURES,
234	.flags		= PHY_HAS_INTERRUPT,
235	.config_aneg	= genphy_config_aneg,
236	.read_status	= genphy_read_status,
237	.driver		= {
238		.owner = THIS_MODULE,
239	},
240}, {
241	/* ATHEROS 8030 */
242	.phy_id		= 0x004dd076,
243	.name		= "Atheros 8030 ethernet",
244	.phy_id_mask	= 0xffffffef,
245	.config_init	= at803x_config_init,
246	.set_wol	= at803x_set_wol,
247	.get_wol	= at803x_get_wol,
248	.suspend	= at803x_suspend,
249	.resume		= at803x_resume,
250	.features	= PHY_GBIT_FEATURES,
251	.flags		= PHY_HAS_INTERRUPT,
252	.config_aneg	= genphy_config_aneg,
253	.read_status	= genphy_read_status,
254	.driver		= {
255		.owner = THIS_MODULE,
256	},
257}, {
258	/* ATHEROS 8031 */
259	.phy_id		= 0x004dd074,
260	.name		= "Atheros 8031 ethernet",
261	.phy_id_mask	= 0xffffffef,
262	.config_init	= at803x_config_init,
263	.set_wol	= at803x_set_wol,
264	.get_wol	= at803x_get_wol,
265	.suspend	= at803x_suspend,
266	.resume		= at803x_resume,
267	.features	= PHY_GBIT_FEATURES,
268	.flags		= PHY_HAS_INTERRUPT,
269	.config_aneg	= genphy_config_aneg,
270	.read_status	= genphy_read_status,
271	.ack_interrupt  = &at803x_ack_interrupt,
272	.config_intr    = &at803x_config_intr,
273	.driver		= {
274		.owner = THIS_MODULE,
275	},
276} };
277
278static int __init atheros_init(void)
279{
280	return phy_drivers_register(at803x_driver,
281				    ARRAY_SIZE(at803x_driver));
282}
283
284static void __exit atheros_exit(void)
285{
286	return phy_drivers_unregister(at803x_driver,
287				      ARRAY_SIZE(at803x_driver));
288}
289
290module_init(atheros_init);
291module_exit(atheros_exit);
292
293static struct mdio_device_id __maybe_unused atheros_tbl[] = {
294	{ 0x004dd076, 0xffffffef },
295	{ 0x004dd074, 0xffffffef },
296	{ 0x004dd072, 0xffffffef },
297	{ }
298};
299
300MODULE_DEVICE_TABLE(mdio, atheros_tbl);