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/etherdevice.h> |
14 | #include <linux/of_mdio.h> |
15 | #include <linux/of_net.h> |
16 | #include "bgmac.h" |
17 | |
18 | static inline bool bgmac_is_bcm4707_family(struct bcma_device *core) |
19 | { |
20 | switch (core->bus->chipinfo.id) { |
21 | case BCMA_CHIP_ID_BCM4707: |
22 | case BCMA_CHIP_ID_BCM47094: |
23 | case BCMA_CHIP_ID_BCM53018: |
24 | return true; |
25 | default: |
26 | return false; |
27 | } |
28 | } |
29 | |
30 | /************************************************** |
31 | * BCMA bus ops |
32 | **************************************************/ |
33 | |
34 | static u32 bcma_bgmac_read(struct bgmac *bgmac, u16 offset) |
35 | { |
36 | return bcma_read32(core: bgmac->bcma.core, offset); |
37 | } |
38 | |
39 | static void bcma_bgmac_write(struct bgmac *bgmac, u16 offset, u32 value) |
40 | { |
41 | bcma_write32(core: bgmac->bcma.core, offset, value); |
42 | } |
43 | |
44 | static u32 bcma_bgmac_idm_read(struct bgmac *bgmac, u16 offset) |
45 | { |
46 | return bcma_aread32(core: bgmac->bcma.core, offset); |
47 | } |
48 | |
49 | static void bcma_bgmac_idm_write(struct bgmac *bgmac, u16 offset, u32 value) |
50 | { |
51 | return bcma_awrite32(core: bgmac->bcma.core, offset, value); |
52 | } |
53 | |
54 | static bool bcma_bgmac_clk_enabled(struct bgmac *bgmac) |
55 | { |
56 | return bcma_core_is_enabled(core: bgmac->bcma.core); |
57 | } |
58 | |
59 | static void bcma_bgmac_clk_enable(struct bgmac *bgmac, u32 flags) |
60 | { |
61 | bcma_core_enable(core: bgmac->bcma.core, flags); |
62 | } |
63 | |
64 | static void bcma_bgmac_cco_ctl_maskset(struct bgmac *bgmac, u32 offset, |
65 | u32 mask, u32 set) |
66 | { |
67 | struct bcma_drv_cc *cc = &bgmac->bcma.core->bus->drv_cc; |
68 | |
69 | bcma_chipco_chipctl_maskset(cc, offset, mask, set); |
70 | } |
71 | |
72 | static u32 bcma_bgmac_get_bus_clock(struct bgmac *bgmac) |
73 | { |
74 | struct bcma_drv_cc *cc = &bgmac->bcma.core->bus->drv_cc; |
75 | |
76 | return bcma_pmu_get_bus_clock(cc); |
77 | } |
78 | |
79 | static void bcma_bgmac_cmn_maskset32(struct bgmac *bgmac, u16 offset, u32 mask, |
80 | u32 set) |
81 | { |
82 | bcma_maskset32(cc: bgmac->bcma.cmn, offset, mask, set); |
83 | } |
84 | |
85 | static int bcma_phy_connect(struct bgmac *bgmac) |
86 | { |
87 | struct phy_device *phy_dev; |
88 | char bus_id[MII_BUS_ID_SIZE + 3]; |
89 | |
90 | /* DT info should be the most accurate */ |
91 | phy_dev = of_phy_get_and_connect(dev: bgmac->net_dev, np: bgmac->dev->of_node, |
92 | hndlr: bgmac_adjust_link); |
93 | if (phy_dev) |
94 | return 0; |
95 | |
96 | /* Connect to the PHY */ |
97 | if (bgmac->mii_bus && bgmac->phyaddr != BGMAC_PHY_NOREGS) { |
98 | snprintf(buf: bus_id, size: sizeof(bus_id), PHY_ID_FMT, bgmac->mii_bus->id, |
99 | bgmac->phyaddr); |
100 | phy_dev = phy_connect(dev: bgmac->net_dev, bus_id, handler: bgmac_adjust_link, |
101 | interface: PHY_INTERFACE_MODE_MII); |
102 | if (IS_ERR(ptr: phy_dev)) { |
103 | dev_err(bgmac->dev, "PHY connection failed\n" ); |
104 | return PTR_ERR(ptr: phy_dev); |
105 | } |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | /* Assume a fixed link to the switch port */ |
111 | return bgmac_phy_connect_direct(bgmac); |
112 | } |
113 | |
114 | static const struct bcma_device_id bgmac_bcma_tbl[] = { |
115 | BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_4706_MAC_GBIT, |
116 | BCMA_ANY_REV, BCMA_ANY_CLASS), |
117 | BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_MAC_GBIT, BCMA_ANY_REV, |
118 | BCMA_ANY_CLASS), |
119 | {}, |
120 | }; |
121 | MODULE_DEVICE_TABLE(bcma, bgmac_bcma_tbl); |
122 | |
123 | /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipattach */ |
124 | static int bgmac_probe(struct bcma_device *core) |
125 | { |
126 | struct bcma_chipinfo *ci = &core->bus->chipinfo; |
127 | struct ssb_sprom *sprom = &core->bus->sprom; |
128 | struct mii_bus *mii_bus; |
129 | struct bgmac *bgmac; |
130 | const u8 *mac; |
131 | int err; |
132 | |
133 | bgmac = bgmac_alloc(dev: &core->dev); |
134 | if (!bgmac) |
135 | return -ENOMEM; |
136 | |
137 | bgmac->bcma.core = core; |
138 | bgmac->dma_dev = core->dma_dev; |
139 | bgmac->irq = core->irq; |
140 | |
141 | bcma_set_drvdata(core, drvdata: bgmac); |
142 | |
143 | err = of_get_ethdev_address(np: bgmac->dev->of_node, dev: bgmac->net_dev); |
144 | if (err == -EPROBE_DEFER) |
145 | return err; |
146 | |
147 | /* If no MAC address assigned via device tree, check SPROM */ |
148 | if (err) { |
149 | switch (core->core_unit) { |
150 | case 0: |
151 | mac = sprom->et0mac; |
152 | break; |
153 | case 1: |
154 | mac = sprom->et1mac; |
155 | break; |
156 | case 2: |
157 | mac = sprom->et2mac; |
158 | break; |
159 | default: |
160 | dev_err(bgmac->dev, "Unsupported core_unit %d\n" , |
161 | core->core_unit); |
162 | err = -ENOTSUPP; |
163 | goto err; |
164 | } |
165 | eth_hw_addr_set(dev: bgmac->net_dev, addr: mac); |
166 | } |
167 | |
168 | /* On BCM4706 we need common core to access PHY */ |
169 | if (core->id.id == BCMA_CORE_4706_MAC_GBIT && |
170 | !core->bus->drv_gmac_cmn.core) { |
171 | dev_err(bgmac->dev, "GMAC CMN core not found (required for BCM4706)\n" ); |
172 | err = -ENODEV; |
173 | goto err; |
174 | } |
175 | bgmac->bcma.cmn = core->bus->drv_gmac_cmn.core; |
176 | |
177 | switch (core->core_unit) { |
178 | case 0: |
179 | bgmac->phyaddr = sprom->et0phyaddr; |
180 | break; |
181 | case 1: |
182 | bgmac->phyaddr = sprom->et1phyaddr; |
183 | break; |
184 | case 2: |
185 | bgmac->phyaddr = sprom->et2phyaddr; |
186 | break; |
187 | } |
188 | bgmac->phyaddr &= BGMAC_PHY_MASK; |
189 | if (bgmac->phyaddr == BGMAC_PHY_MASK) { |
190 | dev_err(bgmac->dev, "No PHY found\n" ); |
191 | err = -ENODEV; |
192 | goto err; |
193 | } |
194 | dev_info(bgmac->dev, "Found PHY addr: %d%s\n" , bgmac->phyaddr, |
195 | bgmac->phyaddr == BGMAC_PHY_NOREGS ? " (NOREGS)" : "" ); |
196 | |
197 | if (!bgmac_is_bcm4707_family(core) && |
198 | !(ci->id == BCMA_CHIP_ID_BCM53573 && core->core_unit == 1)) { |
199 | struct phy_device *phydev; |
200 | |
201 | mii_bus = bcma_mdio_mii_register(bgmac); |
202 | if (IS_ERR(ptr: mii_bus)) { |
203 | err = PTR_ERR(ptr: mii_bus); |
204 | goto err; |
205 | } |
206 | bgmac->mii_bus = mii_bus; |
207 | |
208 | phydev = mdiobus_get_phy(bus: bgmac->mii_bus, addr: bgmac->phyaddr); |
209 | if (ci->id == BCMA_CHIP_ID_BCM53573 && phydev && |
210 | (phydev->drv->phy_id & phydev->drv->phy_id_mask) == PHY_ID_BCM54210E) |
211 | phydev->dev_flags |= PHY_BRCM_EN_MASTER_MODE; |
212 | } |
213 | |
214 | if (core->bus->hosttype == BCMA_HOSTTYPE_PCI) { |
215 | dev_err(bgmac->dev, "PCI setup not implemented\n" ); |
216 | err = -ENOTSUPP; |
217 | goto err1; |
218 | } |
219 | |
220 | bgmac->has_robosw = !!(sprom->boardflags_lo & BGMAC_BFL_ENETROBO); |
221 | if (bgmac->has_robosw) |
222 | dev_warn(bgmac->dev, "Support for Roboswitch not implemented\n" ); |
223 | |
224 | if (sprom->boardflags_lo & BGMAC_BFL_ENETADM) |
225 | dev_warn(bgmac->dev, "Support for ADMtek ethernet switch not implemented\n" ); |
226 | |
227 | /* Feature Flags */ |
228 | switch (ci->id) { |
229 | /* BCM 471X/535X family */ |
230 | case BCMA_CHIP_ID_BCM4716: |
231 | bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; |
232 | fallthrough; |
233 | case BCMA_CHIP_ID_BCM47162: |
234 | bgmac->feature_flags |= BGMAC_FEAT_FLW_CTRL2; |
235 | bgmac->feature_flags |= BGMAC_FEAT_SET_RXQ_CLK; |
236 | break; |
237 | case BCMA_CHIP_ID_BCM5357: |
238 | case BCMA_CHIP_ID_BCM53572: |
239 | bgmac->feature_flags |= BGMAC_FEAT_SET_RXQ_CLK; |
240 | bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; |
241 | bgmac->feature_flags |= BGMAC_FEAT_FLW_CTRL1; |
242 | bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_PHY; |
243 | if ((ci->id == BCMA_CHIP_ID_BCM5357 && ci->pkg == BCMA_PKG_ID_BCM47186) || |
244 | (ci->id == BCMA_CHIP_ID_BCM53572 && ci->pkg == BCMA_PKG_ID_BCM47188)) { |
245 | bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_RGMII; |
246 | bgmac->feature_flags |= BGMAC_FEAT_IOST_ATTACHED; |
247 | } |
248 | if (ci->id == BCMA_CHIP_ID_BCM5357 && ci->pkg == BCMA_PKG_ID_BCM5358) |
249 | bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_EPHYRMII; |
250 | break; |
251 | case BCMA_CHIP_ID_BCM53573: |
252 | bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; |
253 | bgmac->feature_flags |= BGMAC_FEAT_SET_RXQ_CLK; |
254 | if (ci->pkg == BCMA_PKG_ID_BCM47189) |
255 | bgmac->feature_flags |= BGMAC_FEAT_IOST_ATTACHED; |
256 | if (core->core_unit == 0) { |
257 | bgmac->feature_flags |= BGMAC_FEAT_CC4_IF_SW_TYPE; |
258 | if (ci->pkg == BCMA_PKG_ID_BCM47189) |
259 | bgmac->feature_flags |= |
260 | BGMAC_FEAT_CC4_IF_SW_TYPE_RGMII; |
261 | } else if (core->core_unit == 1) { |
262 | bgmac->feature_flags |= BGMAC_FEAT_IRQ_ID_OOB_6; |
263 | bgmac->feature_flags |= BGMAC_FEAT_CC7_IF_TYPE_RGMII; |
264 | } |
265 | break; |
266 | case BCMA_CHIP_ID_BCM4749: |
267 | bgmac->feature_flags |= BGMAC_FEAT_SET_RXQ_CLK; |
268 | bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; |
269 | bgmac->feature_flags |= BGMAC_FEAT_FLW_CTRL1; |
270 | bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_PHY; |
271 | if (ci->pkg == 10) { |
272 | bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_RGMII; |
273 | bgmac->feature_flags |= BGMAC_FEAT_IOST_ATTACHED; |
274 | } |
275 | break; |
276 | /* bcm4707_family */ |
277 | case BCMA_CHIP_ID_BCM4707: |
278 | case BCMA_CHIP_ID_BCM47094: |
279 | case BCMA_CHIP_ID_BCM53018: |
280 | bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; |
281 | bgmac->feature_flags |= BGMAC_FEAT_NO_RESET; |
282 | bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500; |
283 | break; |
284 | default: |
285 | bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; |
286 | bgmac->feature_flags |= BGMAC_FEAT_SET_RXQ_CLK; |
287 | } |
288 | |
289 | if (!bgmac_is_bcm4707_family(core) && core->id.rev > 2) |
290 | bgmac->feature_flags |= BGMAC_FEAT_MISC_PLL_REQ; |
291 | |
292 | if (core->id.id == BCMA_CORE_4706_MAC_GBIT) { |
293 | bgmac->feature_flags |= BGMAC_FEAT_CMN_PHY_CTL; |
294 | bgmac->feature_flags |= BGMAC_FEAT_NO_CLR_MIB; |
295 | } |
296 | |
297 | if (core->id.rev >= 4) { |
298 | bgmac->feature_flags |= BGMAC_FEAT_CMDCFG_SR_REV4; |
299 | bgmac->feature_flags |= BGMAC_FEAT_TX_MASK_SETUP; |
300 | bgmac->feature_flags |= BGMAC_FEAT_RX_MASK_SETUP; |
301 | } |
302 | |
303 | bgmac->read = bcma_bgmac_read; |
304 | bgmac->write = bcma_bgmac_write; |
305 | bgmac->idm_read = bcma_bgmac_idm_read; |
306 | bgmac->idm_write = bcma_bgmac_idm_write; |
307 | bgmac->clk_enabled = bcma_bgmac_clk_enabled; |
308 | bgmac->clk_enable = bcma_bgmac_clk_enable; |
309 | bgmac->cco_ctl_maskset = bcma_bgmac_cco_ctl_maskset; |
310 | bgmac->get_bus_clock = bcma_bgmac_get_bus_clock; |
311 | bgmac->cmn_maskset32 = bcma_bgmac_cmn_maskset32; |
312 | bgmac->phy_connect = bcma_phy_connect; |
313 | |
314 | err = bgmac_enet_probe(bgmac); |
315 | if (err) |
316 | goto err1; |
317 | |
318 | return 0; |
319 | |
320 | err1: |
321 | bcma_mdio_mii_unregister(mii_bus: bgmac->mii_bus); |
322 | err: |
323 | bcma_set_drvdata(core, NULL); |
324 | |
325 | return err; |
326 | } |
327 | |
328 | static void bgmac_remove(struct bcma_device *core) |
329 | { |
330 | struct bgmac *bgmac = bcma_get_drvdata(core); |
331 | |
332 | bcma_mdio_mii_unregister(mii_bus: bgmac->mii_bus); |
333 | bgmac_enet_remove(bgmac); |
334 | bcma_set_drvdata(core, NULL); |
335 | } |
336 | |
337 | static struct bcma_driver bgmac_bcma_driver = { |
338 | .name = KBUILD_MODNAME, |
339 | .id_table = bgmac_bcma_tbl, |
340 | .probe = bgmac_probe, |
341 | .remove = bgmac_remove, |
342 | }; |
343 | |
344 | static int __init bgmac_init(void) |
345 | { |
346 | int err; |
347 | |
348 | err = bcma_driver_register(&bgmac_bcma_driver); |
349 | if (err) |
350 | return err; |
351 | pr_info("Broadcom 47xx GBit MAC driver loaded\n" ); |
352 | |
353 | return 0; |
354 | } |
355 | |
356 | static void __exit bgmac_exit(void) |
357 | { |
358 | bcma_driver_unregister(drv: &bgmac_bcma_driver); |
359 | } |
360 | |
361 | module_init(bgmac_init) |
362 | module_exit(bgmac_exit) |
363 | |
364 | MODULE_AUTHOR("Rafał Miłecki" ); |
365 | MODULE_LICENSE("GPL" ); |
366 | |