Commit | Line | Data |
---|---|---|
0ca7111a MU |
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); |