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