]>
Commit | Line | Data |
---|---|---|
23e7578c PCM |
1 | /* |
2 | * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com> | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | * | |
6 | */ | |
7 | #include <common.h> | |
8 | #include <errno.h> | |
9 | #include <dm.h> | |
10 | #include <net.h> | |
11 | #include <miiphy.h> | |
12 | #include <console.h> | |
13 | #include <wait_bit.h> | |
14 | #include <asm/gpio.h> | |
15 | ||
16 | #include "pic32_eth.h" | |
17 | ||
18 | #define MAX_RX_BUF_SIZE 1536 | |
19 | #define MAX_RX_DESCR PKTBUFSRX | |
20 | #define MAX_TX_DESCR 2 | |
21 | ||
22 | DECLARE_GLOBAL_DATA_PTR; | |
23 | ||
24 | struct pic32eth_dev { | |
25 | struct eth_dma_desc rxd_ring[MAX_RX_DESCR]; | |
26 | struct eth_dma_desc txd_ring[MAX_TX_DESCR]; | |
27 | u32 rxd_idx; /* index of RX desc to read */ | |
28 | /* regs */ | |
29 | struct pic32_ectl_regs *ectl_regs; | |
30 | struct pic32_emac_regs *emac_regs; | |
31 | /* Phy */ | |
32 | struct phy_device *phydev; | |
33 | phy_interface_t phyif; | |
34 | u32 phy_addr; | |
35 | struct gpio_desc rst_gpio; | |
36 | }; | |
37 | ||
38 | void __weak board_netphy_reset(void *dev) | |
39 | { | |
40 | struct pic32eth_dev *priv = dev; | |
41 | ||
42 | if (!dm_gpio_is_valid(&priv->rst_gpio)) | |
43 | return; | |
44 | ||
45 | /* phy reset */ | |
46 | dm_gpio_set_value(&priv->rst_gpio, 0); | |
47 | udelay(300); | |
48 | dm_gpio_set_value(&priv->rst_gpio, 1); | |
49 | udelay(300); | |
50 | } | |
51 | ||
52 | /* Initialize mii(MDIO) interface, discover which PHY is | |
53 | * attached to the device, and configure it properly. | |
54 | */ | |
55 | static int pic32_mii_init(struct pic32eth_dev *priv) | |
56 | { | |
57 | struct pic32_ectl_regs *ectl_p = priv->ectl_regs; | |
58 | struct pic32_emac_regs *emac_p = priv->emac_regs; | |
59 | ||
60 | /* board phy reset */ | |
61 | board_netphy_reset(priv); | |
62 | ||
63 | /* disable RX, TX & all transactions */ | |
64 | writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr); | |
65 | ||
66 | /* wait till busy */ | |
67 | wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, | |
68 | CONFIG_SYS_HZ, false); | |
69 | ||
70 | /* turn controller ON to access PHY over MII */ | |
71 | writel(ETHCON_ON, &ectl_p->con1.set); | |
72 | ||
73 | mdelay(10); | |
74 | ||
75 | /* reset MAC */ | |
76 | writel(EMAC_SOFTRESET, &emac_p->cfg1.set); /* reset assert */ | |
77 | mdelay(10); | |
78 | writel(EMAC_SOFTRESET, &emac_p->cfg1.clr); /* reset deassert */ | |
79 | ||
80 | /* initialize MDIO/MII */ | |
81 | if (priv->phyif == PHY_INTERFACE_MODE_RMII) { | |
82 | writel(EMAC_RMII_RESET, &emac_p->supp.set); | |
83 | mdelay(10); | |
84 | writel(EMAC_RMII_RESET, &emac_p->supp.clr); | |
85 | } | |
86 | ||
87 | return pic32_mdio_init(PIC32_MDIO_NAME, (ulong)&emac_p->mii); | |
88 | } | |
89 | ||
90 | static int pic32_phy_init(struct pic32eth_dev *priv, struct udevice *dev) | |
91 | { | |
92 | struct mii_dev *mii; | |
93 | ||
94 | mii = miiphy_get_dev_by_name(PIC32_MDIO_NAME); | |
95 | ||
96 | /* find & connect PHY */ | |
97 | priv->phydev = phy_connect(mii, priv->phy_addr, | |
98 | dev, priv->phyif); | |
99 | if (!priv->phydev) { | |
100 | printf("%s: %s: Error, PHY connect\n", __FILE__, __func__); | |
101 | return 0; | |
102 | } | |
103 | ||
104 | /* Wait for phy to complete reset */ | |
105 | mdelay(10); | |
106 | ||
107 | /* configure supported modes */ | |
108 | priv->phydev->supported = SUPPORTED_10baseT_Half | | |
109 | SUPPORTED_10baseT_Full | | |
110 | SUPPORTED_100baseT_Half | | |
111 | SUPPORTED_100baseT_Full | | |
112 | SUPPORTED_Autoneg; | |
113 | ||
114 | priv->phydev->advertising = ADVERTISED_10baseT_Half | | |
115 | ADVERTISED_10baseT_Full | | |
116 | ADVERTISED_100baseT_Half | | |
117 | ADVERTISED_100baseT_Full | | |
118 | ADVERTISED_Autoneg; | |
119 | ||
120 | priv->phydev->autoneg = AUTONEG_ENABLE; | |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
125 | /* Configure MAC based on negotiated speed and duplex | |
126 | * reported by PHY. | |
127 | */ | |
128 | static int pic32_mac_adjust_link(struct pic32eth_dev *priv) | |
129 | { | |
130 | struct phy_device *phydev = priv->phydev; | |
131 | struct pic32_emac_regs *emac_p = priv->emac_regs; | |
132 | ||
133 | if (!phydev->link) { | |
134 | printf("%s: No link.\n", phydev->dev->name); | |
135 | return -EINVAL; | |
136 | } | |
137 | ||
138 | if (phydev->duplex) { | |
139 | writel(EMAC_FULLDUP, &emac_p->cfg2.set); | |
140 | writel(FULLDUP_GAP_TIME, &emac_p->ipgt.raw); | |
141 | } else { | |
142 | writel(EMAC_FULLDUP, &emac_p->cfg2.clr); | |
143 | writel(HALFDUP_GAP_TIME, &emac_p->ipgt.raw); | |
144 | } | |
145 | ||
146 | switch (phydev->speed) { | |
147 | case SPEED_100: | |
148 | writel(EMAC_RMII_SPD100, &emac_p->supp.set); | |
149 | break; | |
150 | case SPEED_10: | |
151 | writel(EMAC_RMII_SPD100, &emac_p->supp.clr); | |
152 | break; | |
153 | default: | |
154 | printf("%s: Speed was bad\n", phydev->dev->name); | |
155 | return -EINVAL; | |
156 | } | |
157 | ||
158 | printf("pic32eth: PHY is %s with %dbase%s, %s\n", | |
159 | phydev->drv->name, phydev->speed, | |
160 | (phydev->port == PORT_TP) ? "T" : "X", | |
161 | (phydev->duplex) ? "full" : "half"); | |
162 | ||
163 | return 0; | |
164 | } | |
165 | ||
166 | static void pic32_mac_init(struct pic32eth_dev *priv, u8 *macaddr) | |
167 | { | |
168 | struct pic32_emac_regs *emac_p = priv->emac_regs; | |
169 | u32 stat = 0, v; | |
170 | u64 expire; | |
171 | ||
172 | v = EMAC_TXPAUSE | EMAC_RXPAUSE | EMAC_RXENABLE; | |
173 | writel(v, &emac_p->cfg1.raw); | |
174 | ||
175 | v = EMAC_EXCESS | EMAC_AUTOPAD | EMAC_PADENABLE | | |
176 | EMAC_CRCENABLE | EMAC_LENGTHCK | EMAC_FULLDUP; | |
177 | writel(v, &emac_p->cfg2.raw); | |
178 | ||
179 | /* recommended back-to-back inter-packet gap for 10 Mbps half duplex */ | |
180 | writel(HALFDUP_GAP_TIME, &emac_p->ipgt.raw); | |
181 | ||
182 | /* recommended non-back-to-back interpacket gap is 0xc12 */ | |
183 | writel(0xc12, &emac_p->ipgr.raw); | |
184 | ||
185 | /* recommended collision window retry limit is 0x370F */ | |
186 | writel(0x370f, &emac_p->clrt.raw); | |
187 | ||
188 | /* set maximum frame length: allow VLAN tagged frame */ | |
189 | writel(0x600, &emac_p->maxf.raw); | |
190 | ||
191 | /* set the mac address */ | |
192 | writel(macaddr[0] | (macaddr[1] << 8), &emac_p->sa2.raw); | |
193 | writel(macaddr[2] | (macaddr[3] << 8), &emac_p->sa1.raw); | |
194 | writel(macaddr[4] | (macaddr[5] << 8), &emac_p->sa0.raw); | |
195 | ||
196 | /* default, enable 10 Mbps operation */ | |
197 | writel(EMAC_RMII_SPD100, &emac_p->supp.clr); | |
198 | ||
199 | /* wait until link status UP or deadline elapsed */ | |
200 | expire = get_ticks() + get_tbclk() * 2; | |
201 | for (; get_ticks() < expire;) { | |
202 | stat = phy_read(priv->phydev, priv->phy_addr, MII_BMSR); | |
203 | if (stat & BMSR_LSTATUS) | |
204 | break; | |
205 | } | |
206 | ||
207 | if (!(stat & BMSR_LSTATUS)) | |
208 | printf("MAC: Link is DOWN!\n"); | |
209 | ||
210 | /* delay to stabilize before any tx/rx */ | |
211 | mdelay(10); | |
212 | } | |
213 | ||
214 | static void pic32_mac_reset(struct pic32eth_dev *priv) | |
215 | { | |
216 | struct pic32_emac_regs *emac_p = priv->emac_regs; | |
217 | struct mii_dev *mii; | |
218 | ||
219 | /* Reset MAC */ | |
220 | writel(EMAC_SOFTRESET, &emac_p->cfg1.raw); | |
221 | mdelay(10); | |
222 | ||
223 | /* clear reset */ | |
224 | writel(0, &emac_p->cfg1.raw); | |
225 | ||
226 | /* Reset MII */ | |
227 | mii = priv->phydev->bus; | |
228 | if (mii && mii->reset) | |
229 | mii->reset(mii); | |
230 | } | |
231 | ||
232 | /* initializes the MAC and PHY, then establishes a link */ | |
233 | static void pic32_ctrl_reset(struct pic32eth_dev *priv) | |
234 | { | |
235 | struct pic32_ectl_regs *ectl_p = priv->ectl_regs; | |
236 | u32 v; | |
237 | ||
238 | /* disable RX, TX & any other transactions */ | |
239 | writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr); | |
240 | ||
241 | /* wait till busy */ | |
242 | wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, | |
243 | CONFIG_SYS_HZ, false); | |
244 | /* decrement received buffcnt to zero. */ | |
245 | while (readl(&ectl_p->stat.raw) & ETHSTAT_BUFCNT) | |
246 | writel(ETHCON_BUFCDEC, &ectl_p->con1.set); | |
247 | ||
248 | /* clear any existing interrupt event */ | |
249 | writel(0xffffffff, &ectl_p->irq.clr); | |
250 | ||
251 | /* clear RX/TX start address */ | |
252 | writel(0xffffffff, &ectl_p->txst.clr); | |
253 | writel(0xffffffff, &ectl_p->rxst.clr); | |
254 | ||
255 | /* clear the receive filters */ | |
256 | writel(0x00ff, &ectl_p->rxfc.clr); | |
257 | ||
258 | /* set the receive filters | |
259 | * ETH_FILT_CRC_ERR_REJECT | |
260 | * ETH_FILT_RUNT_REJECT | |
261 | * ETH_FILT_UCAST_ACCEPT | |
262 | * ETH_FILT_MCAST_ACCEPT | |
263 | * ETH_FILT_BCAST_ACCEPT | |
264 | */ | |
265 | v = ETHRXFC_BCEN | ETHRXFC_MCEN | ETHRXFC_UCEN | | |
266 | ETHRXFC_RUNTEN | ETHRXFC_CRCOKEN; | |
267 | writel(v, &ectl_p->rxfc.set); | |
268 | ||
269 | /* turn controller ON to access PHY over MII */ | |
270 | writel(ETHCON_ON, &ectl_p->con1.set); | |
271 | } | |
272 | ||
273 | static void pic32_rx_desc_init(struct pic32eth_dev *priv) | |
274 | { | |
275 | struct pic32_ectl_regs *ectl_p = priv->ectl_regs; | |
276 | struct eth_dma_desc *rxd; | |
277 | u32 idx, bufsz; | |
278 | ||
279 | priv->rxd_idx = 0; | |
280 | for (idx = 0; idx < MAX_RX_DESCR; idx++) { | |
281 | rxd = &priv->rxd_ring[idx]; | |
282 | ||
283 | /* hw owned */ | |
284 | rxd->hdr = EDH_NPV | EDH_EOWN | EDH_STICKY; | |
285 | ||
286 | /* packet buffer address */ | |
287 | rxd->data_buff = virt_to_phys(net_rx_packets[idx]); | |
288 | ||
289 | /* link to next desc */ | |
290 | rxd->next_ed = virt_to_phys(rxd + 1); | |
291 | ||
292 | /* reset status */ | |
293 | rxd->stat1 = 0; | |
294 | rxd->stat2 = 0; | |
295 | ||
296 | /* decrement bufcnt */ | |
297 | writel(ETHCON_BUFCDEC, &ectl_p->con1.set); | |
298 | } | |
299 | ||
300 | /* link last descr to beginning of list */ | |
301 | rxd->next_ed = virt_to_phys(&priv->rxd_ring[0]); | |
302 | ||
303 | /* flush rx ring */ | |
304 | flush_dcache_range((ulong)priv->rxd_ring, | |
305 | (ulong)priv->rxd_ring + sizeof(priv->rxd_ring)); | |
306 | ||
307 | /* set rx desc-ring start address */ | |
308 | writel((ulong)virt_to_phys(&priv->rxd_ring[0]), &ectl_p->rxst.raw); | |
309 | ||
310 | /* RX Buffer size */ | |
311 | bufsz = readl(&ectl_p->con2.raw); | |
312 | bufsz &= ~(ETHCON_RXBUFSZ << ETHCON_RXBUFSZ_SHFT); | |
313 | bufsz |= ((MAX_RX_BUF_SIZE / 16) << ETHCON_RXBUFSZ_SHFT); | |
314 | writel(bufsz, &ectl_p->con2.raw); | |
315 | ||
316 | /* enable the receiver in hardware which allows hardware | |
317 | * to DMA received pkts to the descriptor pointer address. | |
318 | */ | |
319 | writel(ETHCON_RXEN, &ectl_p->con1.set); | |
320 | } | |
321 | ||
322 | static int pic32_eth_start(struct udevice *dev) | |
323 | { | |
324 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
325 | struct pic32eth_dev *priv = dev_get_priv(dev); | |
326 | ||
327 | /* controller */ | |
328 | pic32_ctrl_reset(priv); | |
329 | ||
330 | /* reset MAC */ | |
331 | pic32_mac_reset(priv); | |
332 | ||
333 | /* configure PHY */ | |
334 | phy_config(priv->phydev); | |
335 | ||
336 | /* initialize MAC */ | |
337 | pic32_mac_init(priv, &pdata->enetaddr[0]); | |
338 | ||
339 | /* init RX descriptor; TX descriptors are handled in xmit */ | |
340 | pic32_rx_desc_init(priv); | |
341 | ||
342 | /* Start up & update link status of PHY */ | |
343 | phy_startup(priv->phydev); | |
344 | ||
345 | /* adjust mac with phy link status */ | |
346 | return pic32_mac_adjust_link(priv); | |
347 | } | |
348 | ||
349 | static void pic32_eth_stop(struct udevice *dev) | |
350 | { | |
351 | struct pic32eth_dev *priv = dev_get_priv(dev); | |
352 | struct pic32_ectl_regs *ectl_p = priv->ectl_regs; | |
353 | struct pic32_emac_regs *emac_p = priv->emac_regs; | |
354 | ||
355 | /* Reset the phy if the controller is enabled */ | |
356 | if (readl(&ectl_p->con1.raw) & ETHCON_ON) | |
357 | phy_reset(priv->phydev); | |
358 | ||
359 | /* Shut down the PHY */ | |
360 | phy_shutdown(priv->phydev); | |
361 | ||
362 | /* Stop rx/tx */ | |
363 | writel(ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr); | |
364 | mdelay(10); | |
365 | ||
366 | /* reset MAC */ | |
367 | writel(EMAC_SOFTRESET, &emac_p->cfg1.raw); | |
368 | ||
369 | /* clear reset */ | |
370 | writel(0, &emac_p->cfg1.raw); | |
371 | mdelay(10); | |
372 | ||
373 | /* disable controller */ | |
374 | writel(ETHCON_ON, &ectl_p->con1.clr); | |
375 | mdelay(10); | |
376 | ||
377 | /* wait until everything is down */ | |
378 | wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false, | |
379 | 2 * CONFIG_SYS_HZ, false); | |
380 | ||
381 | /* clear any existing interrupt event */ | |
382 | writel(0xffffffff, &ectl_p->irq.clr); | |
383 | } | |
384 | ||
385 | static int pic32_eth_send(struct udevice *dev, void *packet, int length) | |
386 | { | |
387 | struct pic32eth_dev *priv = dev_get_priv(dev); | |
388 | struct pic32_ectl_regs *ectl_p = priv->ectl_regs; | |
389 | struct eth_dma_desc *txd; | |
390 | u64 deadline; | |
391 | ||
392 | txd = &priv->txd_ring[0]; | |
393 | ||
394 | /* set proper flags & length in descriptor header */ | |
395 | txd->hdr = EDH_SOP | EDH_EOP | EDH_EOWN | EDH_BCOUNT(length); | |
396 | ||
397 | /* pass buffer address to hardware */ | |
398 | txd->data_buff = virt_to_phys(packet); | |
399 | ||
400 | debug("%s: %d / .hdr %x, .data_buff %x, .stat %x, .nexted %x\n", | |
401 | __func__, __LINE__, txd->hdr, txd->data_buff, txd->stat2, | |
402 | txd->next_ed); | |
403 | ||
404 | /* cache flush (packet) */ | |
405 | flush_dcache_range((ulong)packet, (ulong)packet + length); | |
406 | ||
407 | /* cache flush (txd) */ | |
408 | flush_dcache_range((ulong)txd, (ulong)txd + sizeof(*txd)); | |
409 | ||
410 | /* pass descriptor table base to h/w */ | |
411 | writel(virt_to_phys(txd), &ectl_p->txst.raw); | |
412 | ||
413 | /* ready to send enabled, hardware can now send the packet(s) */ | |
414 | writel(ETHCON_TXRTS | ETHCON_ON, &ectl_p->con1.set); | |
415 | ||
416 | /* wait until tx has completed and h/w has released ownership | |
417 | * of the tx descriptor or timeout elapsed. | |
418 | */ | |
419 | deadline = get_ticks() + get_tbclk(); | |
420 | for (;;) { | |
421 | /* check timeout */ | |
422 | if (get_ticks() > deadline) | |
423 | return -ETIMEDOUT; | |
424 | ||
425 | if (ctrlc()) | |
426 | return -EINTR; | |
427 | ||
428 | /* tx completed ? */ | |
429 | if (readl(&ectl_p->con1.raw) & ETHCON_TXRTS) { | |
430 | udelay(1); | |
431 | continue; | |
432 | } | |
433 | ||
434 | /* h/w not released ownership yet? */ | |
435 | invalidate_dcache_range((ulong)txd, (ulong)txd + sizeof(*txd)); | |
436 | if (!(txd->hdr & EDH_EOWN)) | |
437 | break; | |
438 | } | |
439 | ||
440 | return 0; | |
441 | } | |
442 | ||
443 | static int pic32_eth_recv(struct udevice *dev, int flags, uchar **packetp) | |
444 | { | |
445 | struct pic32eth_dev *priv = dev_get_priv(dev); | |
446 | struct eth_dma_desc *rxd; | |
447 | u32 idx = priv->rxd_idx; | |
448 | u32 rx_count; | |
449 | ||
450 | /* find the next ready to receive */ | |
451 | rxd = &priv->rxd_ring[idx]; | |
452 | ||
453 | invalidate_dcache_range((ulong)rxd, (ulong)rxd + sizeof(*rxd)); | |
454 | /* check if owned by MAC */ | |
455 | if (rxd->hdr & EDH_EOWN) | |
456 | return -EAGAIN; | |
457 | ||
458 | /* Sanity check on header: SOP and EOP */ | |
459 | if ((rxd->hdr & (EDH_SOP | EDH_EOP)) != (EDH_SOP | EDH_EOP)) { | |
460 | printf("%s: %s, rx pkt across multiple descr\n", | |
461 | __FILE__, __func__); | |
462 | return 0; | |
463 | } | |
464 | ||
465 | debug("%s: %d /idx %i, hdr=%x, data_buff %x, stat %x, nexted %x\n", | |
466 | __func__, __LINE__, idx, rxd->hdr, | |
467 | rxd->data_buff, rxd->stat2, rxd->next_ed); | |
468 | ||
469 | /* Sanity check on rx_stat: OK, CRC */ | |
470 | if (!RSV_RX_OK(rxd->stat2) || RSV_CRC_ERR(rxd->stat2)) { | |
471 | debug("%s: %s: Error, rx problem detected\n", | |
472 | __FILE__, __func__); | |
473 | return 0; | |
474 | } | |
475 | ||
476 | /* invalidate dcache */ | |
477 | rx_count = RSV_RX_COUNT(rxd->stat2); | |
478 | invalidate_dcache_range((ulong)net_rx_packets[idx], | |
479 | (ulong)net_rx_packets[idx] + rx_count); | |
480 | ||
481 | /* Pass the packet to protocol layer */ | |
482 | *packetp = net_rx_packets[idx]; | |
483 | ||
484 | /* increment number of bytes rcvd (ignore CRC) */ | |
485 | return rx_count - 4; | |
486 | } | |
487 | ||
488 | static int pic32_eth_free_pkt(struct udevice *dev, uchar *packet, int length) | |
489 | { | |
490 | struct pic32eth_dev *priv = dev_get_priv(dev); | |
491 | struct pic32_ectl_regs *ectl_p = priv->ectl_regs; | |
492 | struct eth_dma_desc *rxd; | |
493 | int idx = priv->rxd_idx; | |
494 | ||
495 | /* sanity check */ | |
496 | if (packet != net_rx_packets[idx]) { | |
497 | printf("rxd_id %d: packet is not matched,\n", idx); | |
498 | return -EAGAIN; | |
499 | } | |
500 | ||
501 | /* prepare for receive */ | |
502 | rxd = &priv->rxd_ring[idx]; | |
503 | rxd->hdr = EDH_STICKY | EDH_NPV | EDH_EOWN; | |
504 | ||
505 | flush_dcache_range((ulong)rxd, (ulong)rxd + sizeof(*rxd)); | |
506 | ||
507 | /* decrement rx pkt count */ | |
508 | writel(ETHCON_BUFCDEC, &ectl_p->con1.set); | |
509 | ||
510 | debug("%s: %d / idx %i, hdr %x, data_buff %x, stat %x, nexted %x\n", | |
511 | __func__, __LINE__, idx, rxd->hdr, rxd->data_buff, | |
512 | rxd->stat2, rxd->next_ed); | |
513 | ||
514 | priv->rxd_idx = (priv->rxd_idx + 1) % MAX_RX_DESCR; | |
515 | ||
516 | return 0; | |
517 | } | |
518 | ||
519 | static const struct eth_ops pic32_eth_ops = { | |
520 | .start = pic32_eth_start, | |
521 | .send = pic32_eth_send, | |
522 | .recv = pic32_eth_recv, | |
523 | .free_pkt = pic32_eth_free_pkt, | |
524 | .stop = pic32_eth_stop, | |
525 | }; | |
526 | ||
527 | static int pic32_eth_probe(struct udevice *dev) | |
528 | { | |
529 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
530 | struct pic32eth_dev *priv = dev_get_priv(dev); | |
531 | const char *phy_mode; | |
532 | void __iomem *iobase; | |
533 | fdt_addr_t addr; | |
534 | fdt_size_t size; | |
535 | int offset = 0; | |
536 | int phy_addr = -1; | |
537 | ||
e160f7d4 SG |
538 | addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg", |
539 | &size); | |
23e7578c PCM |
540 | if (addr == FDT_ADDR_T_NONE) |
541 | return -EINVAL; | |
542 | ||
543 | iobase = ioremap(addr, size); | |
544 | pdata->iobase = (phys_addr_t)addr; | |
545 | ||
546 | /* get phy mode */ | |
547 | pdata->phy_interface = -1; | |
e160f7d4 SG |
548 | phy_mode = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "phy-mode", |
549 | NULL); | |
23e7578c PCM |
550 | if (phy_mode) |
551 | pdata->phy_interface = phy_get_interface_by_name(phy_mode); | |
552 | if (pdata->phy_interface == -1) { | |
553 | debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode); | |
554 | return -EINVAL; | |
555 | } | |
556 | ||
557 | /* get phy addr */ | |
e160f7d4 | 558 | offset = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev), |
23e7578c PCM |
559 | "phy-handle"); |
560 | if (offset > 0) | |
561 | phy_addr = fdtdec_get_int(gd->fdt_blob, offset, "reg", -1); | |
562 | ||
563 | /* phy reset gpio */ | |
150c5afe | 564 | gpio_request_by_name_nodev(dev_ofnode(dev), "reset-gpios", 0, |
23e7578c PCM |
565 | &priv->rst_gpio, GPIOD_IS_OUT); |
566 | ||
567 | priv->phyif = pdata->phy_interface; | |
568 | priv->phy_addr = phy_addr; | |
569 | priv->ectl_regs = iobase; | |
570 | priv->emac_regs = iobase + PIC32_EMAC1CFG1; | |
571 | ||
572 | pic32_mii_init(priv); | |
573 | ||
574 | return pic32_phy_init(priv, dev); | |
575 | } | |
576 | ||
577 | static int pic32_eth_remove(struct udevice *dev) | |
578 | { | |
579 | struct pic32eth_dev *priv = dev_get_priv(dev); | |
580 | struct mii_dev *bus; | |
581 | ||
582 | dm_gpio_free(dev, &priv->rst_gpio); | |
583 | phy_shutdown(priv->phydev); | |
584 | free(priv->phydev); | |
585 | bus = miiphy_get_dev_by_name(PIC32_MDIO_NAME); | |
586 | mdio_unregister(bus); | |
587 | mdio_free(bus); | |
588 | iounmap(priv->ectl_regs); | |
589 | return 0; | |
590 | } | |
591 | ||
592 | static const struct udevice_id pic32_eth_ids[] = { | |
593 | { .compatible = "microchip,pic32mzda-eth" }, | |
594 | { } | |
595 | }; | |
596 | ||
597 | U_BOOT_DRIVER(pic32_ethernet) = { | |
598 | .name = "pic32_ethernet", | |
599 | .id = UCLASS_ETH, | |
600 | .of_match = pic32_eth_ids, | |
601 | .probe = pic32_eth_probe, | |
602 | .remove = pic32_eth_remove, | |
603 | .ops = &pic32_eth_ops, | |
604 | .priv_auto_alloc_size = sizeof(struct pic32eth_dev), | |
605 | .platdata_auto_alloc_size = sizeof(struct eth_pdata), | |
606 | }; |