1 | /* |
2 | * Driver for (BCM4706)? GBit MAC core on BCMA bus. |
3 | * |
4 | * Copyright (C) 2012 Rafał Miłecki <zajec5@gmail.com> |
5 | * |
6 | * Licensed under the GNU/GPL. See COPYING for details. |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | |
11 | #include <linux/bcma/bcma.h> |
12 | #include <linux/brcmphy.h> |
13 | #include <linux/of_mdio.h> |
14 | #include "bgmac.h" |
15 | |
16 | static bool bcma_mdio_wait_value(struct bcma_device *core, u16 reg, u32 mask, |
17 | u32 value, int timeout) |
18 | { |
19 | u32 val; |
20 | int i; |
21 | |
22 | for (i = 0; i < timeout / 10; i++) { |
23 | val = bcma_read32(core, offset: reg); |
24 | if ((val & mask) == value) |
25 | return true; |
26 | udelay(10); |
27 | } |
28 | dev_err(&core->dev, "Timeout waiting for reg 0x%X\n" , reg); |
29 | return false; |
30 | } |
31 | |
32 | /************************************************** |
33 | * PHY ops |
34 | **************************************************/ |
35 | |
36 | static u16 bcma_mdio_phy_read(struct bgmac *bgmac, u8 phyaddr, u8 reg) |
37 | { |
38 | struct bcma_device *core; |
39 | u16 phy_access_addr; |
40 | u16 phy_ctl_addr; |
41 | u32 tmp; |
42 | |
43 | BUILD_BUG_ON(BGMAC_PA_DATA_MASK != BCMA_GMAC_CMN_PA_DATA_MASK); |
44 | BUILD_BUG_ON(BGMAC_PA_ADDR_MASK != BCMA_GMAC_CMN_PA_ADDR_MASK); |
45 | BUILD_BUG_ON(BGMAC_PA_ADDR_SHIFT != BCMA_GMAC_CMN_PA_ADDR_SHIFT); |
46 | BUILD_BUG_ON(BGMAC_PA_REG_MASK != BCMA_GMAC_CMN_PA_REG_MASK); |
47 | BUILD_BUG_ON(BGMAC_PA_REG_SHIFT != BCMA_GMAC_CMN_PA_REG_SHIFT); |
48 | BUILD_BUG_ON(BGMAC_PA_WRITE != BCMA_GMAC_CMN_PA_WRITE); |
49 | BUILD_BUG_ON(BGMAC_PA_START != BCMA_GMAC_CMN_PA_START); |
50 | BUILD_BUG_ON(BGMAC_PC_EPA_MASK != BCMA_GMAC_CMN_PC_EPA_MASK); |
51 | BUILD_BUG_ON(BGMAC_PC_MCT_MASK != BCMA_GMAC_CMN_PC_MCT_MASK); |
52 | BUILD_BUG_ON(BGMAC_PC_MCT_SHIFT != BCMA_GMAC_CMN_PC_MCT_SHIFT); |
53 | BUILD_BUG_ON(BGMAC_PC_MTE != BCMA_GMAC_CMN_PC_MTE); |
54 | |
55 | if (bgmac->bcma.core->id.id == BCMA_CORE_4706_MAC_GBIT) { |
56 | core = bgmac->bcma.core->bus->drv_gmac_cmn.core; |
57 | phy_access_addr = BCMA_GMAC_CMN_PHY_ACCESS; |
58 | phy_ctl_addr = BCMA_GMAC_CMN_PHY_CTL; |
59 | } else { |
60 | core = bgmac->bcma.core; |
61 | phy_access_addr = BGMAC_PHY_ACCESS; |
62 | phy_ctl_addr = BGMAC_PHY_CNTL; |
63 | } |
64 | |
65 | tmp = bcma_read32(core, offset: phy_ctl_addr); |
66 | tmp &= ~BGMAC_PC_EPA_MASK; |
67 | tmp |= phyaddr; |
68 | bcma_write32(core, offset: phy_ctl_addr, value: tmp); |
69 | |
70 | tmp = BGMAC_PA_START; |
71 | tmp |= phyaddr << BGMAC_PA_ADDR_SHIFT; |
72 | tmp |= reg << BGMAC_PA_REG_SHIFT; |
73 | bcma_write32(core, offset: phy_access_addr, value: tmp); |
74 | |
75 | if (!bcma_mdio_wait_value(core, reg: phy_access_addr, BGMAC_PA_START, value: 0, |
76 | timeout: 1000)) { |
77 | dev_err(&core->dev, "Reading PHY %d register 0x%X failed\n" , |
78 | phyaddr, reg); |
79 | return 0xffff; |
80 | } |
81 | |
82 | return bcma_read32(core, offset: phy_access_addr) & BGMAC_PA_DATA_MASK; |
83 | } |
84 | |
85 | /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphywr */ |
86 | static int bcma_mdio_phy_write(struct bgmac *bgmac, u8 phyaddr, u8 reg, |
87 | u16 value) |
88 | { |
89 | struct bcma_device *core; |
90 | u16 phy_access_addr; |
91 | u16 phy_ctl_addr; |
92 | u32 tmp; |
93 | |
94 | if (bgmac->bcma.core->id.id == BCMA_CORE_4706_MAC_GBIT) { |
95 | core = bgmac->bcma.core->bus->drv_gmac_cmn.core; |
96 | phy_access_addr = BCMA_GMAC_CMN_PHY_ACCESS; |
97 | phy_ctl_addr = BCMA_GMAC_CMN_PHY_CTL; |
98 | } else { |
99 | core = bgmac->bcma.core; |
100 | phy_access_addr = BGMAC_PHY_ACCESS; |
101 | phy_ctl_addr = BGMAC_PHY_CNTL; |
102 | } |
103 | |
104 | tmp = bcma_read32(core, offset: phy_ctl_addr); |
105 | tmp &= ~BGMAC_PC_EPA_MASK; |
106 | tmp |= phyaddr; |
107 | bcma_write32(core, offset: phy_ctl_addr, value: tmp); |
108 | |
109 | bcma_write32(core: bgmac->bcma.core, BGMAC_INT_STATUS, BGMAC_IS_MDIO); |
110 | if (bcma_read32(core: bgmac->bcma.core, BGMAC_INT_STATUS) & BGMAC_IS_MDIO) |
111 | dev_warn(&core->dev, "Error setting MDIO int\n" ); |
112 | |
113 | tmp = BGMAC_PA_START; |
114 | tmp |= BGMAC_PA_WRITE; |
115 | tmp |= phyaddr << BGMAC_PA_ADDR_SHIFT; |
116 | tmp |= reg << BGMAC_PA_REG_SHIFT; |
117 | tmp |= value; |
118 | bcma_write32(core, offset: phy_access_addr, value: tmp); |
119 | |
120 | if (!bcma_mdio_wait_value(core, reg: phy_access_addr, BGMAC_PA_START, value: 0, |
121 | timeout: 1000)) { |
122 | dev_err(&core->dev, "Writing to PHY %d register 0x%X failed\n" , |
123 | phyaddr, reg); |
124 | return -ETIMEDOUT; |
125 | } |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphyinit */ |
131 | static void bcma_mdio_phy_init(struct bgmac *bgmac) |
132 | { |
133 | struct bcma_chipinfo *ci = &bgmac->bcma.core->bus->chipinfo; |
134 | u8 i; |
135 | |
136 | /* For some legacy hardware we do chipset-based PHY initialization here |
137 | * without even detecting PHY ID. It's hacky and should be cleaned as |
138 | * soon as someone can test it. |
139 | */ |
140 | if (ci->id == BCMA_CHIP_ID_BCM5356) { |
141 | for (i = 0; i < 5; i++) { |
142 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x1f, value: 0x008b); |
143 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x15, value: 0x0100); |
144 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x1f, value: 0x000f); |
145 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x12, value: 0x2aaa); |
146 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x1f, value: 0x000b); |
147 | } |
148 | return; |
149 | } |
150 | if ((ci->id == BCMA_CHIP_ID_BCM5357 && ci->pkg != 10) || |
151 | (ci->id == BCMA_CHIP_ID_BCM4749 && ci->pkg != 10) || |
152 | (ci->id == BCMA_CHIP_ID_BCM53572 && ci->pkg != 9)) { |
153 | struct bcma_drv_cc *cc = &bgmac->bcma.core->bus->drv_cc; |
154 | |
155 | bcma_chipco_chipctl_maskset(cc, offset: 2, mask: ~0xc0000000, set: 0); |
156 | bcma_chipco_chipctl_maskset(cc, offset: 4, mask: ~0x80000000, set: 0); |
157 | for (i = 0; i < 5; i++) { |
158 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x1f, value: 0x000f); |
159 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x16, value: 0x5284); |
160 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x1f, value: 0x000b); |
161 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x17, value: 0x0010); |
162 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x1f, value: 0x000f); |
163 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x16, value: 0x5296); |
164 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x17, value: 0x1073); |
165 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x17, value: 0x9073); |
166 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x16, value: 0x52b6); |
167 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x17, value: 0x9273); |
168 | bcma_mdio_phy_write(bgmac, phyaddr: i, reg: 0x1f, value: 0x000b); |
169 | } |
170 | return; |
171 | } |
172 | |
173 | /* For all other hw do initialization using PHY subsystem. */ |
174 | if (bgmac->net_dev && bgmac->net_dev->phydev) |
175 | phy_init_hw(phydev: bgmac->net_dev->phydev); |
176 | } |
177 | |
178 | /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphyreset */ |
179 | static int bcma_mdio_phy_reset(struct mii_bus *bus) |
180 | { |
181 | struct bgmac *bgmac = bus->priv; |
182 | u8 phyaddr = bgmac->phyaddr; |
183 | |
184 | if (phyaddr == BGMAC_PHY_NOREGS) |
185 | return 0; |
186 | |
187 | bcma_mdio_phy_write(bgmac, phyaddr, MII_BMCR, BMCR_RESET); |
188 | udelay(100); |
189 | if (bcma_mdio_phy_read(bgmac, phyaddr, MII_BMCR) & BMCR_RESET) |
190 | dev_err(bgmac->dev, "PHY reset failed\n" ); |
191 | bcma_mdio_phy_init(bgmac); |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | /************************************************** |
197 | * MII |
198 | **************************************************/ |
199 | |
200 | static int bcma_mdio_mii_read(struct mii_bus *bus, int mii_id, int regnum) |
201 | { |
202 | return bcma_mdio_phy_read(bgmac: bus->priv, phyaddr: mii_id, reg: regnum); |
203 | } |
204 | |
205 | static int bcma_mdio_mii_write(struct mii_bus *bus, int mii_id, int regnum, |
206 | u16 value) |
207 | { |
208 | return bcma_mdio_phy_write(bgmac: bus->priv, phyaddr: mii_id, reg: regnum, value); |
209 | } |
210 | |
211 | struct mii_bus *bcma_mdio_mii_register(struct bgmac *bgmac) |
212 | { |
213 | struct bcma_device *core = bgmac->bcma.core; |
214 | struct mii_bus *mii_bus; |
215 | struct device_node *np; |
216 | int err; |
217 | |
218 | mii_bus = mdiobus_alloc(); |
219 | if (!mii_bus) { |
220 | err = -ENOMEM; |
221 | goto err; |
222 | } |
223 | |
224 | mii_bus->name = "bcma_mdio mii bus" ; |
225 | sprintf(buf: mii_bus->id, fmt: "%s-%d-%d" , "bcma_mdio" , core->bus->num, |
226 | core->core_unit); |
227 | mii_bus->priv = bgmac; |
228 | mii_bus->read = bcma_mdio_mii_read; |
229 | mii_bus->write = bcma_mdio_mii_write; |
230 | mii_bus->reset = bcma_mdio_phy_reset; |
231 | mii_bus->parent = &core->dev; |
232 | mii_bus->phy_mask = ~(1 << bgmac->phyaddr); |
233 | |
234 | np = of_get_child_by_name(node: core->dev.of_node, name: "mdio" ); |
235 | |
236 | err = of_mdiobus_register(mdio: mii_bus, np); |
237 | of_node_put(node: np); |
238 | if (err) { |
239 | dev_err(&core->dev, "Registration of mii bus failed\n" ); |
240 | goto err_free_bus; |
241 | } |
242 | |
243 | return mii_bus; |
244 | |
245 | err_free_bus: |
246 | mdiobus_free(bus: mii_bus); |
247 | err: |
248 | return ERR_PTR(error: err); |
249 | } |
250 | EXPORT_SYMBOL_GPL(bcma_mdio_mii_register); |
251 | |
252 | void bcma_mdio_mii_unregister(struct mii_bus *mii_bus) |
253 | { |
254 | if (!mii_bus) |
255 | return; |
256 | |
257 | mdiobus_unregister(bus: mii_bus); |
258 | mdiobus_free(bus: mii_bus); |
259 | } |
260 | EXPORT_SYMBOL_GPL(bcma_mdio_mii_unregister); |
261 | |
262 | MODULE_AUTHOR("Rafał Miłecki" ); |
263 | MODULE_LICENSE("GPL" ); |
264 | |