1 /*
2 * Copyright (c) 2017-2018 ARM Limited
3 * Copyright (c) 2018 Linaro Limited
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #define DT_DRV_COMPAT smsc_lan9220
9
10 /* SMSC911x/SMSC9220 driver. Partly based on mbedOS driver. */
11
12 #define LOG_MODULE_NAME eth_smsc911x
13 #define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL
14
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
17
18 #include <soc.h>
19 #include <zephyr/device.h>
20 #include <errno.h>
21 #include <zephyr/init.h>
22 #include <zephyr/kernel.h>
23 #include <zephyr/sys/__assert.h>
24 #include <zephyr/net/net_core.h>
25 #include <zephyr/net/net_pkt.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <zephyr/sys/sys_io.h>
30 #include <zephyr/net/ethernet.h>
31 #include <zephyr/irq.h>
32 #include "ethernet/eth_stats.h"
33
34 #ifdef CONFIG_SHARED_IRQ
35 #include <zephyr/shared_irq.h>
36 #endif
37
38 #include "eth_smsc911x_priv.h"
39
40 #define RESET_TIMEOUT 10
41 #define PHY_RESET_TIMEOUT K_MSEC(100)
42 #define REG_WRITE_TIMEOUT 50
43
44 /* Controller has only one PHY with address 1 */
45 #define PHY_ADDR 1
46
47 struct eth_context {
48 struct net_if *iface;
49 uint8_t mac[6];
50 #if defined(CONFIG_NET_STATISTICS_ETHERNET)
51 struct net_stats_eth stats;
52 #endif
53 };
54
55 /* SMSC911x helper functions */
56
smsc_mac_regread(uint8_t reg,uint32_t * val)57 static int smsc_mac_regread(uint8_t reg, uint32_t *val)
58 {
59 uint32_t cmd = MAC_CSR_CMD_BUSY | MAC_CSR_CMD_READ | reg;
60
61 SMSC9220->MAC_CSR_CMD = cmd;
62
63 while ((SMSC9220->MAC_CSR_CMD & MAC_CSR_CMD_BUSY) != 0) {
64 }
65
66 *val = SMSC9220->MAC_CSR_DATA;
67
68 return 0;
69 }
70
smsc_mac_regwrite(uint8_t reg,uint32_t val)71 static int smsc_mac_regwrite(uint8_t reg, uint32_t val)
72 {
73 uint32_t cmd = MAC_CSR_CMD_BUSY | MAC_CSR_CMD_WRITE | reg;
74
75 SMSC9220->MAC_CSR_DATA = val;
76
77 SMSC9220->MAC_CSR_CMD = cmd;
78
79 while ((SMSC9220->MAC_CSR_CMD & MAC_CSR_CMD_BUSY) != 0) {
80 }
81
82 return 0;
83 }
84
smsc_phy_regread(uint8_t regoffset,uint32_t * data)85 int smsc_phy_regread(uint8_t regoffset, uint32_t *data)
86 {
87 uint32_t val = 0U;
88 uint32_t phycmd = 0U;
89 unsigned int time_out = REG_WRITE_TIMEOUT;
90
91 if (smsc_mac_regread(SMSC9220_MAC_MII_ACC, &val) < 0) {
92 return -1;
93 }
94
95 if (val & MAC_MII_ACC_MIIBZY) {
96 *data = 0U;
97 return -EBUSY;
98 }
99
100 phycmd = 0U;
101 phycmd |= PHY_ADDR << 11;
102 phycmd |= (regoffset & 0x1F) << 6;
103 phycmd |= MAC_MII_ACC_READ;
104 phycmd |= MAC_MII_ACC_MIIBZY; /* Operation start */
105
106 if (smsc_mac_regwrite(SMSC9220_MAC_MII_ACC, phycmd)) {
107 return -1;
108 }
109
110 val = 0U;
111 do {
112 k_sleep(K_MSEC(1));
113 time_out--;
114 if (smsc_mac_regread(SMSC9220_MAC_MII_ACC, &val)) {
115 return -1;
116 }
117 } while (time_out != 0U && (val & MAC_MII_ACC_MIIBZY));
118
119 if (time_out == 0U) {
120 return -ETIMEDOUT;
121 }
122
123 if (smsc_mac_regread(SMSC9220_MAC_MII_DATA, data) < 0) {
124 return -1;
125 }
126
127 return 0;
128 }
129
smsc_phy_regwrite(uint8_t regoffset,uint32_t data)130 int smsc_phy_regwrite(uint8_t regoffset, uint32_t data)
131 {
132 uint32_t val = 0U;
133 uint32_t phycmd = 0U;
134 unsigned int time_out = REG_WRITE_TIMEOUT;
135
136 if (smsc_mac_regread(SMSC9220_MAC_MII_ACC, &val) < 0) {
137 return -1;
138 }
139
140 if (val & MAC_MII_ACC_MIIBZY) {
141 return -EBUSY;
142 }
143
144 if (smsc_mac_regwrite(SMSC9220_MAC_MII_DATA, data & 0xFFFF) < 0) {
145 return -1;
146 }
147
148 phycmd |= PHY_ADDR << 11;
149 phycmd |= (regoffset & 0x1F) << 6;
150 phycmd |= MAC_MII_ACC_WRITE;
151 phycmd |= MAC_MII_ACC_MIIBZY; /* Operation start */
152
153 if (smsc_mac_regwrite(SMSC9220_MAC_MII_ACC, phycmd) < 0) {
154 return -1;
155 }
156
157 do {
158 k_sleep(K_MSEC(1));
159 time_out--;
160 if (smsc_mac_regread(SMSC9220_MAC_MII_ACC, &phycmd)) {
161 return -1;
162 }
163 } while (time_out != 0U && (phycmd & MAC_MII_ACC_MIIBZY));
164
165 if (time_out == 0U) {
166 return -ETIMEDOUT;
167 }
168
169 return 0;
170 }
171
smsc_read_mac_address(uint8_t * mac)172 static int smsc_read_mac_address(uint8_t *mac)
173 {
174 uint32_t tmp;
175 int res;
176
177 res = smsc_mac_regread(SMSC9220_MAC_ADDRL, &tmp);
178 if (res < 0) {
179 return res;
180 }
181
182 mac[0] = (uint8_t)(tmp >> 0);
183 mac[1] = (uint8_t)(tmp >> 8);
184 mac[2] = (uint8_t)(tmp >> 16);
185 mac[3] = (uint8_t)(tmp >> 24);
186
187 res = smsc_mac_regread(SMSC9220_MAC_ADDRH, &tmp);
188 if (res < 0) {
189 return res;
190 }
191
192 mac[4] = (uint8_t)(tmp >> 0);
193 mac[5] = (uint8_t)(tmp >> 8);
194
195 return 0;
196 }
197
smsc_check_id(void)198 static int smsc_check_id(void)
199 {
200 uint32_t id = SMSC9220->ID_REV;
201
202 /* If bottom and top halves of the word are the same,
203 * the hardware is (likely) not present.
204 */
205 if (((id >> 16) & 0xFFFF) == (id & 0xFFFF)) {
206 return -1;
207 }
208
209 switch (((id >> 16) & 0xFFFF)) {
210 case 0x9220: /* SMSC9220 on MPS2 */
211 case 0x0118: /* SMS9118 as emulated by QEMU */
212 break;
213
214 default:
215 return -1;
216 }
217
218 return 0;
219 }
220
smsc_soft_reset(void)221 static int smsc_soft_reset(void)
222 {
223 unsigned int time_out = RESET_TIMEOUT;
224
225 SMSC9220->HW_CFG |= HW_CFG_SRST;
226
227 do {
228 k_sleep(K_MSEC(1));
229 time_out--;
230 } while (time_out != 0U && (SMSC9220->HW_CFG & HW_CFG_SRST));
231
232 if (time_out == 0U) {
233 return -1;
234 }
235
236 return 0;
237 }
238
smsc_set_txfifo(unsigned int val)239 void smsc_set_txfifo(unsigned int val)
240 {
241 /* 2kb minimum, 14kb maximum */
242 if (val >= 2U && val <= 14U) {
243 SMSC9220->HW_CFG = val << 16;
244 }
245 }
246
smsc_init_irqs(void)247 void smsc_init_irqs(void)
248 {
249 SMSC9220->INT_EN = 0;
250 /* Clear all interrupts */
251 SMSC9220->INT_STS = 0xFFFFFFFF;
252 /* Polarity config which works with QEMU */
253 /* IRQ deassertion at 220 usecs and master IRQ enable */
254 SMSC9220->IRQ_CFG = 0x22000111;
255 }
256
smsc_check_phy(void)257 static int smsc_check_phy(void)
258 {
259 uint32_t phyid1, phyid2;
260
261 if (smsc_phy_regread(SMSC9220_PHY_ID1, &phyid1)) {
262 return -1;
263 }
264
265 if (smsc_phy_regread(SMSC9220_PHY_ID2, &phyid2)) {
266 return -1;
267 }
268
269 return ((phyid1 == 0xFFFF && phyid2 == 0xFFFF) ||
270 (phyid1 == 0x0 && phyid2 == 0x0));
271 }
272
smsc_reset_phy(void)273 int smsc_reset_phy(void)
274 {
275 uint32_t val;
276
277 if (smsc_phy_regread(SMSC9220_PHY_BCONTROL, &val)) {
278 return -1;
279 }
280
281 val |= 1 << 15;
282
283 if (smsc_phy_regwrite(SMSC9220_PHY_BCONTROL, val)) {
284 return -1;
285 }
286
287 return 0;
288 }
289
290 /**
291 * Advertise all speeds and pause capabilities
292 */
smsc_advertise_caps(void)293 void smsc_advertise_caps(void)
294 {
295 uint32_t aneg_adv = 0U;
296
297 smsc_phy_regread(SMSC9220_PHY_ANEG_ADV, &aneg_adv);
298 aneg_adv |= 0xDE0;
299
300 smsc_phy_regwrite(SMSC9220_PHY_ANEG_ADV, aneg_adv);
301 smsc_phy_regread(SMSC9220_PHY_ANEG_ADV, &aneg_adv);
302 }
303
smsc_establish_link(void)304 void smsc_establish_link(void)
305 {
306 uint32_t bcr = 0U;
307 uint32_t hw_cfg = 0U;
308
309 smsc_phy_regread(SMSC9220_PHY_BCONTROL, &bcr);
310 bcr |= (1 << 12) | (1 << 9);
311 smsc_phy_regwrite(SMSC9220_PHY_BCONTROL, bcr);
312 smsc_phy_regread(SMSC9220_PHY_BCONTROL, &bcr);
313
314 hw_cfg = SMSC9220->HW_CFG;
315 hw_cfg &= 0xF0000;
316 hw_cfg |= (1 << 20);
317 SMSC9220->HW_CFG = hw_cfg;
318 }
319
smsc_enable_xmit(void)320 static inline void smsc_enable_xmit(void)
321 {
322 SMSC9220->TX_CFG = 0x2 /*TX_CFG_TX_ON*/;
323 }
324
smsc_enable_mac_xmit(void)325 void smsc_enable_mac_xmit(void)
326 {
327 uint32_t mac_cr = 0U;
328
329 smsc_mac_regread(SMSC9220_MAC_CR, &mac_cr);
330
331 mac_cr |= (1 << 3); /* xmit enable */
332 mac_cr |= (1 << 28); /* Heartbeat disable */
333
334 smsc_mac_regwrite(SMSC9220_MAC_CR, mac_cr);
335 }
336
smsc_enable_mac_recv(void)337 void smsc_enable_mac_recv(void)
338 {
339 uint32_t mac_cr = 0U;
340
341 smsc_mac_regread(SMSC9220_MAC_CR, &mac_cr);
342 mac_cr |= (1 << 2); /* Recv enable */
343 smsc_mac_regwrite(SMSC9220_MAC_CR, mac_cr);
344 }
345
smsc_init(void)346 int smsc_init(void)
347 {
348 unsigned int phyreset = 0U;
349
350 if (smsc_check_id() < 0) {
351 return -1;
352 }
353
354 if (smsc_soft_reset() < 0) {
355 return -1;
356 }
357
358 smsc_set_txfifo(5);
359
360 /* Sets automatic flow control thresholds, and backpressure */
361 /* threshold to defaults specified. */
362 SMSC9220->AFC_CFG = 0x006E3740;
363
364 /* May need to initialize EEPROM/read MAC from it on real HW. */
365
366 /* Configure GPIOs as LED outputs. */
367 SMSC9220->GPIO_CFG = 0x70070000;
368
369 smsc_init_irqs();
370
371 /* Configure MAC addresses here if needed. */
372
373 if (smsc_check_phy() < 0) {
374 return -1;
375 }
376
377 if (smsc_reset_phy() < 0) {
378 return -1;
379 }
380
381 k_sleep(PHY_RESET_TIMEOUT);
382 /* Checking whether phy reset completed successfully.*/
383 if (smsc_phy_regread(SMSC9220_PHY_BCONTROL, &phyreset)) {
384 return 1;
385 }
386
387 if (phyreset & (1 << 15)) {
388 return 1;
389 }
390
391 smsc_advertise_caps();
392 /* bit [12] of BCONTROL seems self-clearing. */
393 /* Although it's not so in the manual. */
394 smsc_establish_link();
395
396 /* Interrupt threshold */
397 SMSC9220->FIFO_INT = 0xFF000000;
398
399 smsc_enable_mac_xmit();
400 smsc_enable_xmit();
401 SMSC9220->RX_CFG = 0;
402 smsc_enable_mac_recv();
403
404 /* Rx status FIFO level irq threshold */
405 SMSC9220->FIFO_INT &= ~(0xFF); /* Clear 2 bottom nibbles */
406
407 /* This sleep is compulsory otherwise txmit/receive will fail. */
408 k_sleep(K_MSEC(2000));
409
410 return 0;
411 }
412
413 /* Driver functions */
414
eth_smsc911x_get_capabilities(const struct device * dev)415 static enum ethernet_hw_caps eth_smsc911x_get_capabilities(const struct device *dev)
416 {
417 ARG_UNUSED(dev);
418
419 return ETHERNET_LINK_10BASE_T | ETHERNET_LINK_100BASE_T;
420 }
421
422 #if defined(CONFIG_NET_STATISTICS_ETHERNET)
get_stats(const struct device * dev)423 static struct net_stats_eth *get_stats(const struct device *dev)
424 {
425 struct eth_context *context = dev->data;
426
427 return &context->stats;
428 }
429 #endif
430
eth_initialize(struct net_if * iface)431 static void eth_initialize(struct net_if *iface)
432 {
433 const struct device *dev = net_if_get_device(iface);
434 struct eth_context *context = dev->data;
435
436 LOG_DBG("eth_initialize");
437
438 smsc_read_mac_address(context->mac);
439
440 SMSC9220->INT_EN |= BIT(SMSC9220_INTERRUPT_RXSTATUS_FIFO_LEVEL);
441
442 net_if_set_link_addr(iface, context->mac, sizeof(context->mac),
443 NET_LINK_ETHERNET);
444
445 context->iface = iface;
446
447 ethernet_init(iface);
448 }
449
smsc_write_tx_fifo(const uint8_t * buf,uint32_t len,bool is_last)450 static int smsc_write_tx_fifo(const uint8_t *buf, uint32_t len, bool is_last)
451 {
452 uint32_t *buf32;
453
454 __ASSERT_NO_MSG(((uintptr_t)buf & 3) == 0);
455
456 if (is_last) {
457 /* Last fragment may be not full */
458 len = (len + 3) & ~3;
459 }
460
461 if ((len & 3) != 0U || len == 0U) {
462 LOG_ERR("Chunk size not aligned: %u", len);
463 return -1;
464 }
465
466 buf32 = (uint32_t *)buf;
467 len /= 4U;
468 do {
469 SMSC9220->TX_DATA_PORT = *buf32++;
470 } while (--len);
471
472 return 0;
473 }
474
eth_tx(const struct device * dev,struct net_pkt * pkt)475 static int eth_tx(const struct device *dev, struct net_pkt *pkt)
476 {
477 uint16_t total_len = net_pkt_get_len(pkt);
478 static uint8_t tx_buf[NET_ETH_MAX_FRAME_SIZE] __aligned(4);
479 uint32_t txcmd_a, txcmd_b;
480 uint32_t tx_stat;
481 int res;
482
483 txcmd_a = (1/*is_first_segment*/ << 13) | (1/*is_last_segment*/ << 12)
484 | total_len;
485 /* Use len as a tag */
486 txcmd_b = total_len << 16 | total_len;
487 SMSC9220->TX_DATA_PORT = txcmd_a;
488 SMSC9220->TX_DATA_PORT = txcmd_b;
489
490 if (net_pkt_read(pkt, tx_buf, total_len)) {
491 goto error;
492 }
493
494 res = smsc_write_tx_fifo(tx_buf, total_len, true);
495 if (res < 0) {
496 goto error;
497 }
498
499 tx_stat = SMSC9220->TX_STAT_PORT;
500 LOG_DBG("TX_STAT: %x", tx_stat);
501
502 return 0;
503
504 error:
505 LOG_ERR("Writing pkt to FIFO failed");
506 return -1;
507 }
508
509 static const struct ethernet_api api_funcs = {
510 .iface_api.init = eth_initialize,
511
512 .get_capabilities = eth_smsc911x_get_capabilities,
513 .send = eth_tx,
514 #if defined(CONFIG_NET_STATISTICS_ETHERNET)
515 .get_stats = get_stats,
516 #endif
517 };
518
smsc_discard_pkt(void)519 static void smsc_discard_pkt(void)
520 {
521 /* TODO: */
522 /* Datasheet p.43: */
523 /* When performing a fast-forward, there must be at least 4 DWORDs
524 * of data in the RX data FIFO for the packet being discarded. For
525 * less than 4 DWORDs do not use RX_FFWD. In this case data must be
526 * read from the RX data FIFO and discarded using standard PIO read
527 * operations.
528 */
529 SMSC9220->RX_DP_CTRL = RX_DP_CTRL_RX_FFWD;
530 }
531
smsc_wait_discard_pkt(void)532 static inline void smsc_wait_discard_pkt(void)
533 {
534 while ((SMSC9220->RX_DP_CTRL & RX_DP_CTRL_RX_FFWD) != 0) {
535 }
536 }
537
smsc_read_rx_fifo(struct net_pkt * pkt,uint32_t len)538 static int smsc_read_rx_fifo(struct net_pkt *pkt, uint32_t len)
539 {
540 uint32_t buf32;
541
542 __ASSERT_NO_MSG((len & 3) == 0U && len >= 4U);
543
544 len /= 4U;
545
546 do {
547 buf32 = SMSC9220->RX_DATA_PORT;
548
549 if (net_pkt_write(pkt, &buf32, sizeof(uint32_t))) {
550 return -1;
551 }
552 } while (--len);
553
554 return 0;
555 }
556
smsc_recv_pkt(const struct device * dev,uint32_t pkt_size)557 static struct net_pkt *smsc_recv_pkt(const struct device *dev,
558 uint32_t pkt_size)
559 {
560 struct eth_context *context = dev->data;
561 struct net_pkt *pkt;
562 uint32_t rem_size;
563
564 /* Round up to next DWORD size */
565 rem_size = (pkt_size + 3) & ~3;
566 /* Don't account for FCS when filling net pkt */
567 rem_size -= 4U;
568
569 pkt = net_pkt_rx_alloc_with_buffer(context->iface, rem_size,
570 AF_UNSPEC, 0, K_NO_WAIT);
571 if (!pkt) {
572 LOG_ERR("Failed to obtain RX buffer");
573 smsc_discard_pkt();
574 return NULL;
575 }
576
577 if (smsc_read_rx_fifo(pkt, rem_size) < 0) {
578 smsc_discard_pkt();
579 net_pkt_unref(pkt);
580 return NULL;
581 }
582
583 /* Discard FCS */
584 {
585 uint32_t __unused dummy = SMSC9220->RX_DATA_PORT;
586 }
587
588 /* Adjust len of the last buf down for DWORD alignment */
589 if (pkt_size & 3) {
590 net_pkt_update_length(pkt, net_pkt_get_len(pkt) -
591 (4 - (pkt_size & 3)));
592 }
593
594 return pkt;
595 }
596
eth_smsc911x_isr(const struct device * dev)597 static void eth_smsc911x_isr(const struct device *dev)
598 {
599 uint32_t int_status = SMSC9220->INT_STS;
600 struct eth_context *context = dev->data;
601
602 LOG_DBG("%s: INT_STS=%x INT_EN=%x", __func__,
603 int_status, SMSC9220->INT_EN);
604
605 if (int_status & BIT(SMSC9220_INTERRUPT_RXSTATUS_FIFO_LEVEL)) {
606 struct net_pkt *pkt;
607 uint32_t pkt_size, val;
608 uint32_t rx_stat;
609
610 val = SMSC9220->RX_FIFO_INF;
611 uint32_t pkt_pending = BFIELD(val, RX_FIFO_INF_RXSUSED);
612
613 LOG_DBG("in RX FIFO: pkts: %u, bytes: %u",
614 pkt_pending,
615 BFIELD(val, RX_FIFO_INF_RXDUSED));
616
617 /* Ack rxstatus_fifo_level only when no packets pending. The
618 * idea is to serve 1 packet per interrupt (e.g. to allow
619 * higher priority interrupts to fire) by keeping interrupt
620 * pending for as long as there're packets in FIFO. And when
621 * there's none, finally acknowledge it.
622 */
623 if (pkt_pending == 0U) {
624 goto done;
625 }
626
627 int_status &= ~BIT(SMSC9220_INTERRUPT_RXSTATUS_FIFO_LEVEL);
628
629 /* Make sure that any previously started discard op is
630 * finished.
631 */
632 smsc_wait_discard_pkt();
633
634 rx_stat = SMSC9220->RX_STAT_PORT;
635 pkt_size = BFIELD(rx_stat, RX_STAT_PORT_PKT_LEN);
636 LOG_DBG("pkt sz: %u", pkt_size);
637
638 pkt = smsc_recv_pkt(dev, pkt_size);
639
640 LOG_DBG("out RX FIFO: pkts: %u, bytes: %u",
641 SMSC9220_BFIELD(RX_FIFO_INF, RXSUSED),
642 SMSC9220_BFIELD(RX_FIFO_INF, RXDUSED));
643
644 if (pkt != NULL) {
645 int res = net_recv_data(context->iface, pkt);
646
647 if (res < 0) {
648 LOG_ERR("net_recv_data: %d", res);
649 net_pkt_unref(pkt);
650 }
651 }
652 }
653
654 done:
655 /* Ack pending interrupts */
656 SMSC9220->INT_STS = int_status;
657
658 }
659
660 /* Bindings to the platform */
661
eth_init(const struct device * dev)662 int eth_init(const struct device *dev)
663 {
664 IRQ_CONNECT(DT_INST_IRQN(0),
665 DT_INST_IRQ(0, priority),
666 eth_smsc911x_isr, DEVICE_DT_INST_GET(0), 0);
667
668 int ret = smsc_init();
669
670 if (ret != 0) {
671 LOG_ERR("smsc911x failed to initialize");
672 return -ENODEV;
673 }
674
675 irq_enable(DT_INST_IRQN(0));
676
677 return ret;
678 }
679
680 static struct eth_context eth_0_context;
681
682 ETH_NET_DEVICE_DT_INST_DEFINE(0,
683 eth_init, NULL, ð_0_context,
684 NULL /*ð_config_0*/, CONFIG_ETH_INIT_PRIORITY, &api_funcs,
685 NET_ETH_MTU /*MTU*/);
686