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