xref: /openbmc/linux/drivers/net/phy/at803x.c (revision 8fdff1dc)
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 
31 MODULE_DESCRIPTION("Atheros 803x PHY driver");
32 MODULE_AUTHOR("Matus Ujhelyi");
33 MODULE_LICENSE("GPL");
34 
35 static void at803x_set_wol_mac_addr(struct phy_device *phydev)
36 {
37 	struct net_device *ndev = phydev->attached_dev;
38 	const u8 *mac;
39 	unsigned int i, offsets[] = {
40 		AT803X_LOC_MAC_ADDR_32_47_OFFSET,
41 		AT803X_LOC_MAC_ADDR_16_31_OFFSET,
42 		AT803X_LOC_MAC_ADDR_0_15_OFFSET,
43 	};
44 
45 	if (!ndev)
46 		return;
47 
48 	mac = (const u8 *) ndev->dev_addr;
49 
50 	if (!is_valid_ether_addr(mac))
51 		return;
52 
53 	for (i = 0; i < 3; i++) {
54 		phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
55 				  AT803X_DEVICE_ADDR);
56 		phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
57 				  offsets[i]);
58 		phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
59 				  AT803X_FUNC_DATA);
60 		phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
61 				  mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
62 	}
63 }
64 
65 static int at803x_config_init(struct phy_device *phydev)
66 {
67 	int val;
68 	u32 features;
69 	int status;
70 
71 	features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI |
72 		   SUPPORTED_FIBRE | SUPPORTED_BNC;
73 
74 	val = phy_read(phydev, MII_BMSR);
75 	if (val < 0)
76 		return val;
77 
78 	if (val & BMSR_ANEGCAPABLE)
79 		features |= SUPPORTED_Autoneg;
80 	if (val & BMSR_100FULL)
81 		features |= SUPPORTED_100baseT_Full;
82 	if (val & BMSR_100HALF)
83 		features |= SUPPORTED_100baseT_Half;
84 	if (val & BMSR_10FULL)
85 		features |= SUPPORTED_10baseT_Full;
86 	if (val & BMSR_10HALF)
87 		features |= SUPPORTED_10baseT_Half;
88 
89 	if (val & BMSR_ESTATEN) {
90 		val = phy_read(phydev, MII_ESTATUS);
91 		if (val < 0)
92 			return val;
93 
94 		if (val & ESTATUS_1000_TFULL)
95 			features |= SUPPORTED_1000baseT_Full;
96 		if (val & ESTATUS_1000_THALF)
97 			features |= SUPPORTED_1000baseT_Half;
98 	}
99 
100 	phydev->supported = features;
101 	phydev->advertising = features;
102 
103 	/* enable WOL */
104 	at803x_set_wol_mac_addr(phydev);
105 	status = phy_write(phydev, AT803X_INTR_ENABLE, AT803X_WOL_ENABLE);
106 	status = phy_read(phydev, AT803X_INTR_STATUS);
107 
108 	return 0;
109 }
110 
111 /* ATHEROS 8035 */
112 static struct phy_driver at8035_driver = {
113 	.phy_id		= 0x004dd072,
114 	.name		= "Atheros 8035 ethernet",
115 	.phy_id_mask	= 0xffffffef,
116 	.config_init	= at803x_config_init,
117 	.features	= PHY_GBIT_FEATURES,
118 	.flags		= PHY_HAS_INTERRUPT,
119 	.config_aneg	= &genphy_config_aneg,
120 	.read_status	= &genphy_read_status,
121 	.driver		= {
122 		.owner = THIS_MODULE,
123 	},
124 };
125 
126 /* ATHEROS 8030 */
127 static struct phy_driver at8030_driver = {
128 	.phy_id		= 0x004dd076,
129 	.name		= "Atheros 8030 ethernet",
130 	.phy_id_mask	= 0xffffffef,
131 	.config_init	= at803x_config_init,
132 	.features	= PHY_GBIT_FEATURES,
133 	.flags		= PHY_HAS_INTERRUPT,
134 	.config_aneg	= &genphy_config_aneg,
135 	.read_status	= &genphy_read_status,
136 	.driver		= {
137 		.owner = THIS_MODULE,
138 	},
139 };
140 
141 static int __init atheros_init(void)
142 {
143 	int ret;
144 
145 	ret = phy_driver_register(&at8035_driver);
146 	if (ret)
147 		goto err1;
148 
149 	ret = phy_driver_register(&at8030_driver);
150 	if (ret)
151 		goto err2;
152 
153 	return 0;
154 
155 err2:
156 	phy_driver_unregister(&at8035_driver);
157 err1:
158 	return ret;
159 }
160 
161 static void __exit atheros_exit(void)
162 {
163 	phy_driver_unregister(&at8035_driver);
164 	phy_driver_unregister(&at8030_driver);
165 }
166 
167 module_init(atheros_init);
168 module_exit(atheros_exit);
169 
170 static struct mdio_device_id __maybe_unused atheros_tbl[] = {
171 	{ 0x004dd076, 0xffffffef },
172 	{ 0x004dd072, 0xffffffef },
173 	{ }
174 };
175 
176 MODULE_DEVICE_TABLE(mdio, atheros_tbl);
177