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(&eth.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(&eth.EthHandle, eth.DMATxDscrTab, eth.Tx_Buff, ETH_TXBUFNB);
147 
148     /* Initialize Rx Descriptors list: Chain Mode  */
149     HAL_ETH_DMARxDescListInit(&eth.EthHandle, eth.DMARxDscrTab, eth.Rx_Buff, ETH_RXBUFNB);
150 
151     /* Enable MAC and DMA transmission and reception */
152     HAL_ETH_Start(&eth.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(&eth.EthHandle, PHY_MICR, &regvalue);
160 
161     regvalue |= (PHY_MICR_INT_EN | PHY_MICR_INT_OE);
162 
163     /* Enable Interrupts */
164     HAL_ETH_WritePHYRegister(&eth.EthHandle, PHY_MICR, regvalue );
165 
166     /* Read Register Configuration */
167     HAL_ETH_ReadPHYRegister(&eth.EthHandle, PHY_MISR, &regvalue);
168 
169     regvalue |= PHY_MISR_LINK_INT_EN;
170 
171     /* Enable Interrupt on change of link status */
172     HAL_ETH_WritePHYRegister(&eth.EthHandle, PHY_MISR, regvalue);
173 #endif
174 
175     /* set up an event to block the rx thread on */
176     event_init(&eth.rx_event, false, EVENT_FLAG_AUTOUNSIGNAL);
177 
178     /* start worker thread */
179     thread_resume(thread_create("eth_rx", &eth_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(&eth.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(&eth.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(&eth.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(&eth.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(&eth.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(&eth.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(&eth.rx_event);
271         if (event_err >= NO_ERROR) {
272 #endif
273             // XXX probably race with the event here
274             while (HAL_ETH_GetReceivedFrame_IT(&eth.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