1 /*
2 * Copyright (c) 2015 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 /*
9 * COPYRIGHT(c) 2015 STMicroelectronics
10 *
11 * Redistribution and use in source and binary forms, with or without modification,
12 * are permitted provided that the following conditions are met:
13 * 1. Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * 3. Neither the name of STMicroelectronics nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 ******************************************************************************
34 */
35
36 #include <lk/err.h>
37 #include <lk/debug.h>
38 #include <assert.h>
39 #include <lk/trace.h>
40 #include <target.h>
41 #include <lk/compiler.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <dev/gpio.h>
45 #include <kernel/event.h>
46 #include <kernel/thread.h>
47 #include <arch/ops.h>
48 #include <arch/arm/cm.h>
49 #include <platform.h>
50 #include <platform/stm32.h>
51 #include <platform/eth.h>
52
53 #if WITH_LIB_MINIP
54 #include <lib/minip.h>
55 #include <lib/pktbuf.h>
56 #endif
57
58 #define LOCAL_TRACE 0
59
60 /* LAN8742A PHY Address*/
61 #define LAN8742A_PHY_ADDRESS 0x00
62 /* DP83848 PHY Address*/
63 #define DP83848_PHY_ADDRESS 0x01
64 /* KSZ8721 PHY Address*/
65 #define KSZ8721_PHY_ADDRESS 0x01
66
67 struct eth_status {
68 ETH_HandleTypeDef EthHandle;
69
70 eth_phy_itf eth_phy;
71 event_t rx_event;
72
73 /* allocated directly out of DTCM below */
74 ETH_DMADescTypeDef *DMARxDscrTab; // ETH_RXBUFNB
75 ETH_DMADescTypeDef *DMATxDscrTab; // ETH_TXBUFNB
76 uint8_t *Rx_Buff; // ETH_RXBUFNB * ETH_RX_BUF_SIZE
77 uint8_t *Tx_Buff; // ETH_TXBUFNB * ETH_TX_BUF_SIZE
78 };
79
80 static struct eth_status eth;
81
82 static int eth_rx_worker(void *arg);
83
84 #if WITH_LIB_MINIP
85 static int eth_send_raw_pkt(pktbuf_t *p);
86 #endif
87
eth_init(const uint8_t * mac_addr,eth_phy_itf eth_phy)88 status_t eth_init(const uint8_t *mac_addr, eth_phy_itf eth_phy) {
89 LTRACE_ENTRY;
90
91 DEBUG_ASSERT(mac_addr);
92
93 eth.eth_phy = eth_phy;
94
95 /* Enable ETHERNET clock */
96 __HAL_RCC_ETH_CLK_ENABLE();
97
98 eth.EthHandle.Instance = ETH;
99 eth.EthHandle.Init.MACAddr = (uint8_t *)mac_addr;
100 eth.EthHandle.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
101 eth.EthHandle.Init.Speed = ETH_SPEED_100M;
102 eth.EthHandle.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
103 switch (eth_phy) {
104 case PHY_DP83848:
105 eth.EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_MII;
106 eth.EthHandle.Init.PhyAddress = DP83848_PHY_ADDRESS;
107 break;
108 case PHY_LAN8742A:
109 eth.EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
110 eth.EthHandle.Init.PhyAddress = LAN8742A_PHY_ADDRESS;
111 break;
112 case PHY_KSZ8721:
113 eth.EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
114 eth.EthHandle.Init.PhyAddress = KSZ8721_PHY_ADDRESS;
115 break;
116 default:
117 return ERR_NOT_CONFIGURED;
118 }
119
120 eth.EthHandle.Init.RxMode = ETH_RXINTERRUPT_MODE;
121 //eth.EthHandle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; // XXX icmp checksums corrupted if stack stuff valid checksum
122 eth.EthHandle.Init.ChecksumMode = ETH_CHECKSUM_BY_SOFTWARE;
123
124 /* configure ethernet peripheral (GPIOs, clocks, MAC, DMA) */
125 if (HAL_ETH_Init(ð.EthHandle) != HAL_OK)
126 return ERR_NOT_CONFIGURED;
127
128 /* allocate descriptor and buffer memory from DTCM */
129 /* XXX do in a more generic way */
130 #if MEMBASE == 0x20000000
131 #error DTCM will collide with MEMBASE
132 #endif
133 addr_t tcm_ptr = RAMDTCM_BASE;
134
135 eth.DMATxDscrTab = (void *)tcm_ptr;
136 tcm_ptr += sizeof(*eth.DMATxDscrTab) * ETH_TXBUFNB;
137 eth.DMARxDscrTab = (void *)tcm_ptr;
138 tcm_ptr += sizeof(*eth.DMARxDscrTab) * ETH_RXBUFNB;
139
140 eth.Tx_Buff = (void *)tcm_ptr;
141 tcm_ptr += ETH_TX_BUF_SIZE * ETH_TXBUFNB;
142 eth.Rx_Buff = (void *)tcm_ptr;
143 tcm_ptr += ETH_RX_BUF_SIZE * ETH_RXBUFNB;
144
145 /* Initialize Tx Descriptors list: Chain Mode */
146 HAL_ETH_DMATxDescListInit(ð.EthHandle, eth.DMATxDscrTab, eth.Tx_Buff, ETH_TXBUFNB);
147
148 /* Initialize Rx Descriptors list: Chain Mode */
149 HAL_ETH_DMARxDescListInit(ð.EthHandle, eth.DMARxDscrTab, eth.Rx_Buff, ETH_RXBUFNB);
150
151 /* Enable MAC and DMA transmission and reception */
152 HAL_ETH_Start(ð.EthHandle);
153
154 #if 0
155 // XXX DP83848 specific
156 /**** Configure PHY to generate an interrupt when Eth Link state changes ****/
157 /* Read Register Configuration */
158 uint32_t regvalue;
159 HAL_ETH_ReadPHYRegister(ð.EthHandle, PHY_MICR, ®value);
160
161 regvalue |= (PHY_MICR_INT_EN | PHY_MICR_INT_OE);
162
163 /* Enable Interrupts */
164 HAL_ETH_WritePHYRegister(ð.EthHandle, PHY_MICR, regvalue );
165
166 /* Read Register Configuration */
167 HAL_ETH_ReadPHYRegister(ð.EthHandle, PHY_MISR, ®value);
168
169 regvalue |= PHY_MISR_LINK_INT_EN;
170
171 /* Enable Interrupt on change of link status */
172 HAL_ETH_WritePHYRegister(ð.EthHandle, PHY_MISR, regvalue);
173 #endif
174
175 /* set up an event to block the rx thread on */
176 event_init(ð.rx_event, false, EVENT_FLAG_AUTOUNSIGNAL);
177
178 /* start worker thread */
179 thread_resume(thread_create("eth_rx", ð_rx_worker, NULL, HIGH_PRIORITY, DEFAULT_STACK_SIZE));
180
181 /* enable interrupts */
182 HAL_NVIC_EnableIRQ(ETH_IRQn);
183
184 LTRACE_EXIT;
185
186 return NO_ERROR;
187 }
188
stm32_ETH_IRQ(void)189 void stm32_ETH_IRQ(void) {
190 arm_cm_irq_entry();
191
192 HAL_ETH_IRQHandler(ð.EthHandle);
193
194 arm_cm_irq_exit(true);
195 }
196
197 /**
198 * @brief Ethernet Rx Transfer completed callback
199 * @param heth: ETH handle
200 * @retval None
201 */
HAL_ETH_RxCpltCallback(ETH_HandleTypeDef * heth)202 void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth) {
203 event_signal(ð.rx_event, false);
204 }
205
eth_send(const void * buf,size_t len)206 static status_t eth_send(const void *buf, size_t len) {
207 status_t err;
208 __IO ETH_DMADescTypeDef *DmaTxDesc;
209
210 LTRACEF("buf %p, len %zu\n", buf, len);
211
212 DmaTxDesc = eth.EthHandle.TxDesc;
213
214 /* is the buffer available? */
215 if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != 0) {
216 LTRACEF("tx buffer not available\n");
217 err = ERR_IO;
218 goto error;
219 }
220
221 uint8_t *buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr);
222 memcpy(buffer, buf, len);
223
224 HAL_StatusTypeDef e = HAL_ETH_TransmitFrame(ð.EthHandle, len);
225
226 err = (e == HAL_OK) ? NO_ERROR : ERR_IO;
227
228 error:
229 /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
230 if ((eth.EthHandle.Instance->DMASR & ETH_DMASR_TUS) != 0) {
231 /* Clear TUS ETHERNET DMA flag */
232 eth.EthHandle.Instance->DMASR = ETH_DMASR_TUS;
233
234 /* Resume DMA transmission*/
235 eth.EthHandle.Instance->DMATPDR = 0;
236 }
237
238 return err;
239 }
240
eth_rx_worker(void * arg)241 static int eth_rx_worker(void *arg) {
242 for (;;) {
243 #if 0
244 status_t event_err = event_wait_timeout(ð.rx_event, 1000);
245 if (event_err == ERR_TIMED_OUT) {
246 /* periodically poll the phys status register */
247 /* XXX specific to DP83848 */
248 uint32_t val;
249
250 /* Read PHY_MISR */
251 /* seems to take about 30 usecs */
252 HAL_ETH_ReadPHYRegister(ð.EthHandle, PHY_MISR, &val);
253
254 /* Check whether the link interrupt has occurred or not */
255 if (val & PHY_LINK_INTERRUPT) {
256 /* Read PHY_SR*/
257 HAL_ETH_ReadPHYRegister(ð.EthHandle, PHY_SR, &val);
258
259 /* Check whether the link is up or down*/
260 if (val & PHY_LINK_STATUS) {
261 printf("eth: link up\n");
262 //netif_set_link_up(link_arg->netif);
263 } else {
264 printf("eth: link down\n");
265 //netif_set_link_down(link_arg->netif);
266 }
267 }
268 } else {
269 #else
270 status_t event_err = event_wait(ð.rx_event);
271 if (event_err >= NO_ERROR) {
272 #endif
273 // XXX probably race with the event here
274 while (HAL_ETH_GetReceivedFrame_IT(ð.EthHandle) == HAL_OK) {
275 LTRACEF("got packet len %u, buffer %p, seg count %u\n", eth.EthHandle.RxFrameInfos.length,
276 (void *)eth.EthHandle.RxFrameInfos.buffer,
277 eth.EthHandle.RxFrameInfos.SegCount);
278
279 #if WITH_LIB_MINIP
280 /* allocate a pktbuf header, point it at our rx buffer, and pass up the stack */
281 pktbuf_t *p = pktbuf_alloc_empty();
282 if (p) {
283 pktbuf_add_buffer(p, (void *)eth.EthHandle.RxFrameInfos.buffer, eth.EthHandle.RxFrameInfos.length,
284 0, 0, NULL, NULL);
285 p->dlen = eth.EthHandle.RxFrameInfos.length;
286
287 minip_rx_driver_callback(p);
288
289 pktbuf_free(p, true);
290 }
291 #endif
292
293 /* Release descriptors to DMA */
294 /* Point to first descriptor */
295 __IO ETH_DMADescTypeDef *dmarxdesc;
296
297 dmarxdesc = eth.EthHandle.RxFrameInfos.FSRxDesc;
298 /* Set Own bit in Rx descriptors: gives the buffers back to DMA */
299 for (uint i=0; i< eth.EthHandle.RxFrameInfos.SegCount; i++) {
300 dmarxdesc->Status |= ETH_DMARXDESC_OWN;
301 dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
302 }
303
304 /* Clear Segment_Count */
305 eth.EthHandle.RxFrameInfos.SegCount =0;
306
307 /* When Rx Buffer unavailable flag is set: clear it and resume reception */
308 if ((eth.EthHandle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) {
309 /* Clear RBUS ETHERNET DMA flag */
310 eth.EthHandle.Instance->DMASR = ETH_DMASR_RBUS;
311 /* Resume DMA reception */
312 eth.EthHandle.Instance->DMARPDR = 0;
313 }
314 }
315 }
316 }
317
318 return 0;
319 }
320
321 #if WITH_LIB_MINIP
322
323 status_t stm32_eth_send_minip_pkt(pktbuf_t *p) {
324 LTRACEF("p %p, dlen %zu, eof %u\n", p, p->dlen, p->flags & PKTBUF_FLAG_EOF);
325
326 DEBUG_ASSERT(p && p->dlen);
327
328 if (!(p->flags & PKTBUF_FLAG_EOF)) {
329 /* can't handle multi part packets yet */
330 PANIC_UNIMPLEMENTED;
331
332 return ERR_NOT_IMPLEMENTED;
333 }
334
335 status_t err = eth_send(p->data, p->dlen);
336
337 pktbuf_free(p, true);
338
339 return err;
340 }
341
342 #endif
343
344
345