Karel Gardas 508af74f79 if_stmac: fix compilation error (related to crc32_raw move in FreeBSD)
FreeBSD in 2019 moved crc32 function into separate gsb_crc32.h header
file and probably after libbsd sync with this change if_stmac got broken.
2022-06-04 19:54:09 +02:00

1010 lines
22 KiB
C

/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (C) 2020 embedded brains Gmb_h (http://www.embedded-brains.de)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <machine/rtems-bsd-kernel-space.h>
#include <bsp.h>
#ifdef LIBBSP_ARM_STM32H7_BSP_H
#include <sys/param.h>
#include <sys/types.h>
#include <sys/bus.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/gsb_crc32.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_var.h>
#include <machine/bus.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <rtems/bsd/local/miibus_if.h>
#include <stm32h7xx_hal.h>
#include <rtems/bsd/bsd.h>
#include <rtems/irq-extension.h>
#include <rtems/score/armv7m.h>
#define RX_DESC_COUNT 64
#define TX_DESC_COUNT 256
#define STMAC_LOCK(sc) mtx_lock(&(sc)->mtx)
#define STMAC_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define STMAC_TXLOCK(sc) mtx_lock(&(sc)->tx_mtx)
#define STMAC_TXUNLOCK(sc) mtx_unlock(&(sc)->tx_mtx)
struct stmac_desc {
__IO uint32_t DESC0;
__IO uint32_t DESC1;
__IO uint32_t DESC2;
__IO uint32_t DESC3;
struct mbuf *m;
};
struct stmac_softc {
ETH_HandleTypeDef heth;
uint8_t mac_addr[6];
struct ifnet *ifp;
struct mtx mtx;
device_t miibus;
struct mtx mii_mtx;
struct mii_data *mii_softc;
u_int mii_media_active;
u_int mii_media_status;
struct callout tick_callout;
int if_flags;
/* RX */
struct stmac_desc *rx_desc_ring;
uint32_t rx_idx;
bool rx_do_alloc;
/* TX */
struct mtx tx_mtx;
struct stmac_desc *tx_desc_ring;
uint32_t tx_idx_head;
uint32_t tx_idx_tail;
};
static void stmac_init_locked(struct stmac_softc *);
static void stmac_stop_locked(struct stmac_softc *);
struct mbuf *
stmac_new_mbuf(struct ifnet *ifp)
{
struct mbuf *m;
m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
if (m != NULL) {
m->m_data = mtod(m, char *) + ETHER_ALIGN;
m->m_pkthdr.rcvif = ifp;
rtems_cache_invalidate_multiple_data_lines(m->m_data, m->m_len);
}
return m;
}
static void
stmac_tick(void *arg)
{
struct stmac_softc *sc;
struct ifnet *ifp;
sc = arg;
ifp = sc->ifp;
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
return;
}
mii_tick(sc->mii_softc);
callout_reset(&sc->tick_callout, hz, stmac_tick, sc);
}
static uint32_t
stmac_rx_desc3(uint32_t idx)
{
if (idx % 16 != 0) {
return (ETH_DMARXNDESCRF_OWN | ETH_DMARXNDESCRF_BUF1V);
}
return (ETH_DMARXNDESCRF_OWN | ETH_DMARXNDESCRF_BUF1V |
ETH_DMARXNDESCRF_IOC);
}
static void
stmac_rx_setup_desc(struct stmac_softc *sc)
{
uint32_t idx;
bool do_alloc;
ETH_TypeDef *regs;
do_alloc = sc->rx_do_alloc;
sc->rx_do_alloc = false;
sc->rx_idx = 0;
for (idx = 0; idx < RX_DESC_COUNT; ++idx) {
struct stmac_desc *desc;
struct mbuf *m;
desc = &sc->rx_desc_ring[idx];
if (do_alloc) {
m = stmac_new_mbuf(sc->ifp);
BSD_ASSERT(m != NULL);
desc->m = m;
} else {
m = desc->m;
}
WRITE_REG(desc->DESC0, mtod(m, uint32_t));
WRITE_REG(desc->DESC1, 0x0);
WRITE_REG(desc->DESC2, 0x0);
WRITE_REG(desc->DESC3, stmac_rx_desc3(idx));
}
regs = sc->heth.Instance;
WRITE_REG(regs->DMACRDRLR, RX_DESC_COUNT -1);
WRITE_REG(regs->DMACRDLAR, (uint32_t)&sc->rx_desc_ring[0]);
WRITE_REG(regs->DMACRDTPR,
(uint32_t)&sc->rx_desc_ring[RX_DESC_COUNT - 1]);
}
static void
stmac_tx_setup_desc(struct stmac_softc *sc)
{
uint32_t idx;
ETH_TypeDef *regs;
sc->tx_idx_head = 0;
sc->tx_idx_tail = 0;
for (idx = 0; idx < TX_DESC_COUNT; ++idx) {
struct stmac_desc *desc;
desc = &sc->tx_desc_ring[idx];
WRITE_REG(desc->DESC0, 0x0);
WRITE_REG(desc->DESC1, 0x0);
WRITE_REG(desc->DESC2, 0x0);
WRITE_REG(desc->DESC3, 0x0);
desc->m = NULL;
}
regs = sc->heth.Instance;
WRITE_REG(regs->DMACTDRLR, TX_DESC_COUNT -1);
WRITE_REG(regs->DMACTDLAR, (uint32_t)&sc->tx_desc_ring[0]);
WRITE_REG(regs->DMACTDTPR, (uint32_t)&sc->tx_desc_ring[0]);
}
static void
stmac_init_locked(struct stmac_softc *sc)
{
struct ifnet *ifp;
ETH_TypeDef *regs;
ifp = sc->ifp;
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
return;
}
ifp->if_drv_flags |= IFF_DRV_RUNNING;
stmac_rx_setup_desc(sc);
stmac_tx_setup_desc(sc);
HAL_ETH_Start(&sc->heth);
regs = sc->heth.Instance;
/* Enable interrupts */
SET_BIT(regs->DMACIER, ETH_DMACIER_NIE | ETH_DMACIER_RIE |
ETH_DMACIER_FBEE | ETH_DMACIER_AIE);
mii_mediachg(sc->mii_softc);
callout_reset(&sc->tick_callout, hz, stmac_tick, sc);
}
static void
stmac_tx_reclaim_all(struct stmac_softc *sc)
{
uint32_t idx;
for (idx = 0; idx < TX_DESC_COUNT; ++idx) {
struct stmac_desc *desc;
struct mbuf *m;
desc = &sc->tx_desc_ring[idx];
m = desc->m;
desc->m = NULL;
m_freem(m);
}
}
static void
stmac_stop_locked(struct stmac_softc *sc)
{
struct ifnet *ifp;
ETH_TypeDef *regs;
rtems_interrupt_server_entry server_entry;
rtems_status_code status;
rtems_name name;
uint64_t t0;
ifp = sc->ifp;
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
return;
}
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
regs = sc->heth.Instance;
/* Disable interrupts */
WRITE_REG(regs->DMACIER, 0);
/* Disable the DMA transmission and reception */
CLEAR_BIT(regs->DMACTCR, ETH_DMACTCR_ST);
CLEAR_BIT(regs->DMACRCR, ETH_DMACRCR_SR);
/* Wait for DMA done */
t0 = rtems_clock_get_uptime_nanoseconds();
while ((regs->MTLTQDR & (ETH_MTLTQDR_TXQSTS |
ETH_MTLTQDR_TRCSTS)) != 0 ||
(regs->MTLRQDR & (ETH_MTLRQDR_RXQSTS |
ETH_MTLRQDR_PRXQ)) != 0) {
if (rtems_clock_get_uptime_nanoseconds() - t0 > 500000) {
break;
}
}
/* Disable the MAC reception and transmission */
CLEAR_BIT(regs->MACCR, ETH_MACCR_RE | ETH_MACCR_TE);
/* Make sure no receive interrupt is in progress */
status = rtems_object_get_classic_name(rtems_task_self(), &name);
BSD_ASSERT(status == RTEMS_SUCCESSFUL);
if (name != rtems_build_name('I', 'R', 'Q', 'S')) {
status = rtems_interrupt_server_entry_initialize(
RTEMS_INTERRUPT_SERVER_DEFAULT, &server_entry);
BSD_ASSERT(status == RTEMS_SUCCESSFUL);
rtems_interrupt_server_entry_destroy(&server_entry);
}
stmac_tx_reclaim_all(sc);
}
static uint8_t
stmac_bitreverse(uint8_t x)
{
static const uint8_t nibbletab[] = {
0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
};
return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]);
}
static uint64_t
stmac_hash_bit(const uint8_t *eaddr)
{
uint32_t crc;
/*
* Thanks to the excellent documentation from STM it was easy to figure
* this out.
*/
crc = 0;
crc = crc32_raw(eaddr, ETHER_ADDR_LEN, ~crc);
crc = stmac_bitreverse((uint8_t)~crc);
return ((uint64_t)1 << (crc >> 2));
}
static void
stmac_rx_setup_filter(struct stmac_softc *sc)
{
struct ifmultiaddr *ifma;
struct ifnet *ifp;
ETH_TypeDef *regs;
uint32_t macpfr;
uint64_t machtr;
const uint8_t *eaddr;
ifp = sc->ifp;
macpfr = ETH_MACPFR_HMC;
if ((ifp->if_flags & IFF_PROMISC) != 0) {
macpfr |= ETH_MACPFR_PR;
}
machtr = 0;
if ((ifp->if_flags & IFF_ALLMULTI) != 0) {
machtr = ~machtr;
} else {
if_maddr_rlock(ifp);
CK_STAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) {
if (ifma->ifma_addr->sa_family != AF_LINK) {
continue;
}
eaddr = LLADDR((struct sockaddr_dl *)ifma->ifma_addr);
machtr |= stmac_hash_bit(eaddr);
}
if_maddr_runlock(ifp);
}
regs = sc->heth.Instance;
eaddr = IF_LLADDR(ifp);
regs->MACA0LR = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) |
(eaddr[3] << 24);
regs->MACA0HR = eaddr[4] | (eaddr[5] << 8);
regs->MACHT0R = (uint32_t)machtr;
regs->MACHT1R = (uint32_t)(machtr >> 32);
regs->MACPFR = macpfr;
}
static int
stmac_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data)
{
struct stmac_softc *sc;
int error;
struct mii_data *mii;
sc = ifp->if_softc;
error = 0;
switch (cmd) {
case SIOCSIFFLAGS:
STMAC_LOCK(sc);
if ((ifp->if_flags & IFF_UP) != 0) {
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
if ((ifp->if_flags ^ sc->if_flags) &
(IFF_PROMISC | IFF_ALLMULTI))
stmac_rx_setup_filter(sc);
} else {
stmac_init_locked(sc);
}
} else {
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
STMAC_TXLOCK(sc);
stmac_stop_locked(sc);
STMAC_TXUNLOCK(sc);
}
}
sc->if_flags = ifp->if_flags;
STMAC_UNLOCK(sc);
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
STMAC_LOCK(sc);
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
stmac_rx_setup_filter(sc);
}
STMAC_UNLOCK(sc);
break;
case SIOCSIFMEDIA:
case SIOCGIFMEDIA:
mii = sc->mii_softc;
if (mii != NULL) {
error = ifmedia_ioctl(ifp, (struct ifreq *)data, &mii->mii_media, cmd);
} else {
error = ether_ioctl(ifp, cmd, data);
}
break;
default:
error = ether_ioctl(ifp, cmd, data);
break;
}
return (error);
}
static void
stmac_init(void *arg)
{
struct stmac_softc *sc;
sc = arg;
STMAC_LOCK(sc);
STMAC_TXLOCK(sc);
stmac_init_locked(sc);
STMAC_TXUNLOCK(sc);
STMAC_UNLOCK(sc);
}
static void
stmac_media_status(struct ifnet *ifp, struct ifmediareq *ifmr_p)
{
struct stmac_softc *sc;
struct mii_data *mii;
sc = ifp->if_softc;
STMAC_LOCK(sc);
mii = sc->mii_softc;
if (mii != NULL) {
mii_pollstat(mii);
ifmr_p->ifm_active = mii->mii_media_active;
ifmr_p->ifm_status = mii->mii_media_status;
}
STMAC_UNLOCK(sc);
}
static int
stmac_media_change(struct ifnet *ifp)
{
struct stmac_softc *sc;
int error;
sc = ifp->if_softc;
STMAC_LOCK(sc);
if (sc->mii_softc != NULL) {
error = mii_mediachg(sc->mii_softc);
} else {
error = ENXIO;
}
STMAC_UNLOCK(sc);
return (error);
}
static int
stmac_probe(device_t dev)
{
device_set_desc(dev, "STM MAC");
return (BUS_PROBE_DEFAULT);
}
static void
stmac_rx_checksum(struct mbuf *m, uint32_t desc1)
{
if ((desc1 & (ETH_DMARXNDESCWBF_IPCE | ETH_DMARXNDESCWBF_IPCB |
ETH_DMARXNDESCWBF_IPHE)) == 0) {
uint32_t pt;
pt = desc1 & ETH_DMARXNDESCWBF_PT;
if (pt == ETH_DMARXNDESCWBF_PT_UDP ||
pt == ETH_DMARXNDESCWBF_PT_TCP) {
m->m_pkthdr.csum_flags |=
CSUM_IP_CHECKED |
CSUM_IP_VALID |
CSUM_DATA_VALID |
CSUM_PSEUDO_HDR;
m->m_pkthdr.csum_data = 0xffff;
} else if ((desc1 & ETH_DMARXNDESCWBF_IPV4) != 0) {
m->m_pkthdr.csum_flags |=
CSUM_IP_CHECKED |
CSUM_IP_VALID;
m->m_pkthdr.csum_data = 0xffff;
}
}
}
static void
stmac_rx_interrupt(struct stmac_softc *sc, ETH_TypeDef *regs)
{
struct ifnet *ifp;
uint32_t idx;
struct stmac_desc *desc_ring;
struct stmac_desc *desc;
ifp = sc->ifp;
desc_ring = sc->rx_desc_ring;
idx = sc->rx_idx;
desc = &desc_ring[idx];
while (true) {
uint32_t desc3;
struct mbuf *m;
/*
* Make sure the interrupt is cleared and no longer pending
* before we read the descriptor status.
*/
regs->DMACSR = ETH_DMACSR_RI | ETH_DMACSR_NIS;
regs->DMACSR;
_ARMV7M_NVIC_Clear_pending(ETH_IRQn);
desc3 = desc->DESC3;
if ((desc3 & ETH_DMARXNDESCRF_OWN) != 0) {
break;
}
m = stmac_new_mbuf(ifp);
if (m != NULL) {
uint32_t mask;
uint32_t set;
uint32_t len;
mask = ETH_DMARXNDESCWBF_CTXT | ETH_DMARXNDESCWBF_FD |
ETH_DMARXNDESCWBF_LD | ETH_DMARXNDESCWBF_ES;
set = ETH_DMARXNDESCWBF_FD | ETH_DMARXNDESCWBF_LD;
len = desc3 & ETH_DMARXNDESCWBF_PL;
if ((desc3 & mask) == set && len > ETHER_CRC_LEN) {
struct mbuf *rx;
rx = desc->m;
stmac_rx_checksum(rx, desc->DESC1);
rx->m_len = len;
rx->m_pkthdr.len = len;
(*ifp->if_input)(ifp, rx);
} else {
if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
m_freem(m);
m = desc->m;
}
} else {
if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
m = desc->m;
}
WRITE_REG(desc->DESC0, mtod(m, uint32_t));
WRITE_REG(desc->DESC1, 0x0);
WRITE_REG(desc->DESC2, 0x0);
WRITE_REG(desc->DESC3, stmac_rx_desc3(idx));
desc->m = m;
_ARM_Data_synchronization_barrier();
WRITE_REG(regs->DMACRDTPR, (uint32_t)desc);
idx = (idx + 1) % RX_DESC_COUNT;
desc = &desc_ring[idx];
}
sc->rx_idx = idx;
}
static void
stmac_reset(struct stmac_softc *sc)
{
HAL_StatusTypeDef hal_status;
ETH_TypeDef *regs;
hal_status = HAL_ETH_Init(&sc->heth);
BSD_ASSERT(hal_status == HAL_OK);
regs = sc->heth.Instance;
/* Set descriptor skip length according to struct stmac_desc */
MODIFY_REG(regs->DMACCR, ETH_DMACCR_DSL, ETH_DMACCR_DSL_32BIT);
/*
* FIXME: Just use a random value. It is not clear which clock is used
* here.
*/
regs->DMACRIWTR = 0x80;
}
static void
stmac_interrupt(void *arg)
{
struct stmac_softc *sc;
ETH_TypeDef *regs;
uint32_t dmacsr;
sc = arg;
regs = sc->heth.Instance;
dmacsr = regs->DMACSR;
/* This is almost always a receive interrupt */
stmac_rx_interrupt(sc, regs);
if (__predict_false((dmacsr & ETH_DMACSR_FBE) != 0)) {
regs->DMACSR = ETH_DMACSR_FBE | ETH_DMACSR_AIS;
STMAC_LOCK(sc);
STMAC_TXLOCK(sc);
stmac_stop_locked(sc);
stmac_reset(sc);
stmac_init_locked(sc);
STMAC_TXUNLOCK(sc);
STMAC_UNLOCK(sc);
}
}
static void
stmac_tx_reclaim(struct stmac_softc *sc, struct ifnet *ifp)
{
uint32_t head_idx;
uint32_t tail_idx;
struct stmac_desc *desc_ring;
head_idx = sc->tx_idx_head;
tail_idx = sc->tx_idx_tail;
desc_ring = sc->tx_desc_ring;
while (head_idx != tail_idx) {
struct stmac_desc *tail_desc;
uint32_t desc3;
tail_desc = &desc_ring[tail_idx];
desc3 = tail_desc->DESC3;
if ((desc3 & ETH_DMATXNDESCWBF_OWN) != 0) {
break;
}
if ((desc3 & ETH_DMATXNDESCWBF_LD) != 0) {
struct mbuf *m;
ift_counter cnt;
if ((desc3 & ETH_DMATXNDESCWBF_ES) == 0) {
cnt = IFCOUNTER_OPACKETS;
} else {
cnt = IFCOUNTER_OERRORS;
}
if_inc_counter(ifp, cnt, 1);
m = tail_desc->m;
tail_desc->m = NULL;
m_freem(m);
}
tail_idx = (tail_idx + 1) % TX_DESC_COUNT;
}
sc->tx_idx_tail = tail_idx;
}
static void
stmac_cache_flush(uintptr_t begin, uintptr_t size)
{
uintptr_t end;
uintptr_t mask;
/* Align begin and end of the data to a cache line */
end = begin + size;
mask = CPU_CACHE_LINE_BYTES - 1;
begin &= ~mask;
end = (end + mask) & ~mask;
rtems_cache_flush_multiple_data_lines((void *)begin, end - begin);
}
static int
stmac_tx_enqueue(struct stmac_softc *sc, struct ifnet *ifp, struct mbuf *m)
{
uint32_t head_idx;
uint32_t tail_idx;
uint32_t capacity;
uint32_t new_head_idx;
uint32_t idx;
struct stmac_desc *desc_ring;
struct stmac_desc *desc;
ETH_TypeDef *regs;
uint32_t bufs;
uint32_t desc2;
uint32_t desc3;
struct mbuf *n;
int csum_flags;
head_idx = sc->tx_idx_head;
tail_idx = sc->tx_idx_tail;
capacity = 2 * ((tail_idx - head_idx - 1) % TX_DESC_COUNT);
idx = head_idx;
desc_ring = sc->tx_desc_ring;
desc2 = 0;
bufs = 0;
n = m;
do {
uintptr_t size;
desc = &desc_ring[idx];
size = (uintptr_t)n->m_len;
if (__predict_true(size > 0)) {
uintptr_t begin;
++bufs;
if (__predict_false(bufs > capacity)) {
return (ENOBUFS);
}
begin = mtod(n, uintptr_t);
if (bufs % 2 == 1) {
desc->DESC0 = begin;
desc2 = size;
} else {
desc->DESC1 = begin;
desc->DESC2 = (size << 16) | desc2;
idx = (idx + 1) % TX_DESC_COUNT;
}
stmac_cache_flush(begin, size);
}
n = n->m_next;
} while (n != NULL);
if (bufs % 2 == 1) {
desc->DESC1 = 0;
desc->DESC2 = desc2;
idx = (idx + 1) % TX_DESC_COUNT;
}
new_head_idx = idx;
sc->tx_idx_head = new_head_idx;
idx = (idx - 1) % TX_DESC_COUNT;
desc = &desc_ring[idx];
desc->m = m;
desc3 = ETH_DMATXNDESCRF_OWN | ETH_DMATXNDESCRF_LD | m->m_pkthdr.len;
csum_flags = m->m_pkthdr.csum_flags;
if ((csum_flags & (CSUM_TCP | CSUM_UDP | CSUM_TCP_IPV6 |
CSUM_UDP_IPV6)) != 0) {
desc3 |= ETH_DMATXNDESCRF_CIC_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
} else if ((csum_flags & CSUM_IP) != 0) {
desc3 |= ETH_DMATXNDESCRF_CIC_IPHDR_INSERT;
}
while (idx != head_idx) {
desc->DESC3 = desc3;
desc3 &= ~ETH_DMATXNDESCRF_LD;
idx = (idx - 1) % TX_DESC_COUNT;
desc = &desc_ring[idx];
}
desc->DESC3 = ETH_DMATXNDESCRF_FD | desc3;
_ARM_Data_synchronization_barrier();
regs = sc->heth.Instance;
WRITE_REG(regs->DMACTDTPR, (uint32_t)&desc_ring[new_head_idx]);
return (0);
}
static int
stmac_transmit(struct ifnet *ifp, struct mbuf *m)
{
struct stmac_softc *sc;
int error;
sc = ifp->if_softc;
STMAC_TXLOCK(sc);
error = stmac_tx_enqueue(sc, ifp, m);
stmac_tx_reclaim(sc, ifp);
if (__predict_false(error != 0)) {
error = stmac_tx_enqueue(sc, ifp, m);
if (error != 0) {
m_freem(m);
if_inc_counter(ifp, IFCOUNTER_OQDROPS, 1);
}
}
STMAC_TXUNLOCK(sc);
return (error);
}
static int
stmac_transmit_no_link(struct ifnet *ifp, struct mbuf *m)
{
(void)ifp;
(void)m;
return (ENETDOWN);
}
static void
stmac_qflush(struct ifnet *ifp)
{
(void)ifp;
}
static int
stmac_attach(device_t dev)
{
struct stmac_softc *sc;
struct ifnet *ifp;
int error;
struct stmac_desc *descs;
ETH_TypeDef *regs;
rtems_status_code status;
sc = device_get_softc(dev);
sc->rx_do_alloc = true;
mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF);
mtx_init(&sc->tx_mtx, "stmac tx", MTX_NETWORK_LOCK, MTX_DEF);
callout_init_mtx(&sc->tick_callout, &sc->mtx, 0);
sc->ifp = ifp = if_alloc(IFT_ETHER);
ifp->if_softc = sc;
regs = ETH;
sc->heth.Instance = regs;
descs = rtems_cache_coherent_allocate(sizeof(*descs) *
(RX_DESC_COUNT + TX_DESC_COUNT), 4, 0);
BSD_ASSERT(descs != NULL);
sc->tx_desc_ring = &descs[0];
sc->rx_desc_ring = &descs[TX_DESC_COUNT];
rtems_bsd_get_mac_address(device_get_name(dev), device_get_unit(dev),
&sc->mac_addr[0]);
sc->heth.Init.MACAddr = &sc->mac_addr[0];
sc->heth.Init.MediaInterface = HAL_ETH_RMII_MODE;
sc->heth.Init.RxBuffLen = 1524;
stmac_reset(sc);
if_initname(ifp, "stm", device_get_unit(dev));
ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST;
ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6;
ifp->if_capenable = ifp->if_capabilities;
ifp->if_hwassist = CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_TCP_IPV6 | CSUM_UDP_IPV6;
ifp->if_transmit = stmac_transmit_no_link;
ifp->if_qflush = stmac_qflush;
ifp->if_ioctl = stmac_ioctl;
ifp->if_init = stmac_init;
IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1);
ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1;
IFQ_SET_READY(&ifp->if_snd);
error = mii_attach(dev, &sc->miibus, ifp, stmac_media_change,
stmac_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY,
MII_OFFSET_ANY, 0);
if (error == 0) {
sc->mii_softc = device_get_softc(sc->miibus);
}
ether_ifattach(ifp, &sc->heth.Init.MACAddr[0]);
status = rtems_interrupt_server_handler_install(
RTEMS_INTERRUPT_SERVER_DEFAULT, ETH_IRQn, "stmac",
RTEMS_INTERRUPT_SHARED, stmac_interrupt, sc);
BSD_ASSERT(status == RTEMS_SUCCESSFUL);
return (0);
}
static int
stmac_miibus_read(device_t dev, int phy, int reg)
{
struct stmac_softc *sc;
uint32_t val;
HAL_StatusTypeDef hal_status;
sc = device_get_softc(dev);
hal_status = HAL_ETH_ReadPHYRegister(&sc->heth, (uint32_t)phy,
(uint32_t)reg, &val);
if (hal_status != HAL_OK) {
return (-1);
}
return ((int)val);
}
static int
stmac_miibus_write(device_t dev, int phy, int reg, int val)
{
struct stmac_softc *sc;
HAL_StatusTypeDef hal_status;
sc = device_get_softc(dev);
hal_status = HAL_ETH_WritePHYRegister(&sc->heth, (uint32_t)phy,
(uint32_t)reg, (uint32_t)val);
if (hal_status != HAL_OK) {
return (-1);
}
return (0);
}
static void
stmac_miibus_statchg(device_t dev)
{
struct stmac_softc *sc;
struct mii_data *mii;
u_int active;
u_int status;
ETH_TypeDef *regs;
uint32_t maccr;
sc = device_get_softc(dev);
mii = device_get_softc(sc->miibus);
active = mii->mii_media_active;
status = mii->mii_media_status;
if (sc->mii_media_active == active && sc->mii_media_status == status) {
return;
}
sc->mii_media_active = active;
sc->mii_media_status = status;
regs = sc->heth.Instance;
maccr = regs->MACCR;
if ((status & IFM_ACTIVE) != 0) {
if_settransmitfn(sc->ifp, stmac_transmit);
if ((IFM_OPTIONS(active) & IFM_FDX) != 0) {
maccr |= ETH_MACCR_DM;
} else {
maccr &= ~ETH_MACCR_DM;
}
if (IFM_SUBTYPE(active) != IFM_10_T) {
maccr |= ETH_MACCR_FES;
} else {
maccr &= ~ETH_MACCR_FES;
}
} else {
maccr |= ETH_MACCR_DM | ETH_MACCR_FES;
}
regs->MACCR = maccr;
}
static device_method_t stmac_methods[] = {
DEVMETHOD(device_probe, stmac_probe),
DEVMETHOD(device_attach, stmac_attach),
DEVMETHOD(miibus_readreg, stmac_miibus_read),
DEVMETHOD(miibus_writereg, stmac_miibus_write),
DEVMETHOD(miibus_statchg, stmac_miibus_statchg),
DEVMETHOD_END
};
driver_t stmac_driver = {
"stmac",
stmac_methods,
sizeof(struct stmac_softc)
};
static devclass_t stmac_devclass;
DRIVER_MODULE(stmac, nexus, stmac_driver, stmac_devclass, 0, 0);
DRIVER_MODULE(miibus, stmac, miibus_driver, miibus_devclass, 0, 0);
MODULE_DEPEND(stmac, ether, 1, 1, 1);
MODULE_DEPEND(stmac, miibus, 1, 1, 1);
#endif /* LIBBSP_ARM_STM32H7_BSP_H */