blob: ab19ecd4e404f6860115ff10ea2972f8b28aeff2 [file] [log] [blame]
/*
* Copyright (c) 2023 Arm Limited (or its affiliates). All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/net/ethernet.h>
#include <zephyr/net/phy.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/sys_clock.h>
#include <zephyr/drivers/mdio.h>
#include <zephyr/logging/log.h>
#include "eth_smsc91x_priv.h"
#define DT_DRV_COMPAT smsc_lan91c111
LOG_MODULE_REGISTER(eth_smsc91x, CONFIG_ETHERNET_LOG_LEVEL);
#define SMSC_LOCK(sc) k_mutex_lock(&(sc)->lock, K_FOREVER)
#define SMSC_UNLOCK(sc) k_mutex_unlock(&(sc)->lock)
#define HW_CYCLE_PER_US (sys_clock_hw_cycles_per_sec() / 1000000UL)
#define TX_ALLOC_WAIT_TIME 100
#define MAX_IRQ_LOOPS 8
/*
* MII
*/
#define MDO MGMT_MDO
#define MDI MGMT_MDI
#define MDC MGMT_MCLK
#define MDIRPHY MGMT_MDOE
#define MDIRHOST 0
#define MII_IDLE_DETECT_CYCLES 32
#define MII_COMMAND_START 0x01
#define MII_COMMAND_READ 0x02
#define MII_COMMAND_WRITE 0x01
#define MII_COMMAND_ACK 0x02
static const char *smsc_chip_ids[16] = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
/* 9 */ "SMSC LAN91C11",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
struct smsc_data {
mm_reg_t smsc_reg;
unsigned int irq;
unsigned int smsc_chip;
unsigned int smsc_rev;
unsigned int smsc_mask;
uint8_t mac[6];
struct k_mutex lock;
struct k_work isr_work;
};
struct eth_config {
DEVICE_MMIO_ROM;
const struct device *phy_dev;
};
struct eth_context {
DEVICE_MMIO_RAM;
struct net_if *iface;
struct smsc_data sc;
};
static uint8_t tx_buffer[NET_ETH_MAX_FRAME_SIZE];
static uint8_t rx_buffer[NET_ETH_MAX_FRAME_SIZE];
static ALWAYS_INLINE void delay(int us)
{
k_busy_wait(us);
}
static ALWAYS_INLINE void smsc_select_bank(struct smsc_data *sc, uint16_t bank)
{
sys_write16(bank & BSR_BANK_MASK, sc->smsc_reg + BSR);
}
static ALWAYS_INLINE unsigned int smsc_current_bank(struct smsc_data *sc)
{
return FIELD_GET(BSR_BANK_MASK, sys_read16(sc->smsc_reg + BSR));
}
static void smsc_mmu_wait(struct smsc_data *sc)
{
__ASSERT((smsc_current_bank(sc) == 2), "%s called when not in bank 2", __func__);
while (sys_read16(sc->smsc_reg + MMUCR) & MMUCR_BUSY) {
;
}
}
static ALWAYS_INLINE uint8_t smsc_read_1(struct smsc_data *sc, int offset)
{
return sys_read8(sc->smsc_reg + offset);
}
static ALWAYS_INLINE uint16_t smsc_read_2(struct smsc_data *sc, int offset)
{
return sys_read16(sc->smsc_reg + offset);
}
static ALWAYS_INLINE void smsc_read_multi_2(struct smsc_data *sc, int offset, uint16_t *datap,
uint16_t count)
{
while (count--) {
*datap++ = sys_read16(sc->smsc_reg + offset);
}
}
static ALWAYS_INLINE void smsc_write_1(struct smsc_data *sc, int offset, uint8_t val)
{
sys_write8(val, sc->smsc_reg + offset);
}
static ALWAYS_INLINE void smsc_write_2(struct smsc_data *sc, int offset, uint16_t val)
{
sys_write16(val, sc->smsc_reg + offset);
}
static ALWAYS_INLINE void smsc_write_multi_2(struct smsc_data *sc, int offset, uint16_t *datap,
uint16_t count)
{
while (count--) {
sys_write16(*datap++, sc->smsc_reg + offset);
}
}
static uint32_t smsc_mii_bitbang_read(struct smsc_data *sc)
{
uint16_t val;
__ASSERT(FIELD_GET(BSR_BANK_MASK, smsc_read_2(sc, BSR)) == 3,
"%s called with bank %d (!=3)", __func__,
FIELD_GET(BSR_BANK_MASK, smsc_read_2(sc, BSR)));
val = smsc_read_2(sc, MGMT);
delay(1); /* Simulate a timing sequence */
return val;
}
static void smsc_mii_bitbang_write(struct smsc_data *sc, uint16_t val)
{
__ASSERT(FIELD_GET(BSR_BANK_MASK, smsc_read_2(sc, BSR)) == 3,
"%s called with bank %d (!=3)", __func__,
FIELD_GET(BSR_BANK_MASK, smsc_read_2(sc, BSR)));
smsc_write_2(sc, MGMT, val);
delay(1); /* Simulate a timing sequence */
}
static void smsc_miibus_sync(struct smsc_data *sc)
{
int i;
uint32_t v;
v = MDIRPHY | MDO;
smsc_mii_bitbang_write(sc, v);
for (i = 0; i < MII_IDLE_DETECT_CYCLES; i++) {
smsc_mii_bitbang_write(sc, v | MDC);
smsc_mii_bitbang_write(sc, v);
}
}
static void smsc_miibus_sendbits(struct smsc_data *sc, uint32_t data, int nbits)
{
int i;
uint32_t v;
v = MDIRPHY;
smsc_mii_bitbang_write(sc, v);
for (i = 1 << (nbits - 1); i != 0; i >>= 1) {
if (data & i) {
v |= MDO;
} else {
v &= ~MDO;
}
smsc_mii_bitbang_write(sc, v);
smsc_mii_bitbang_write(sc, v | MDC);
smsc_mii_bitbang_write(sc, v);
}
}
static int smsc_miibus_readreg(struct smsc_data *sc, int phy, int reg)
{
int i, err, val;
irq_disable(sc->irq);
SMSC_LOCK(sc);
smsc_select_bank(sc, 3);
smsc_miibus_sync(sc);
smsc_miibus_sendbits(sc, MII_COMMAND_START, 2);
smsc_miibus_sendbits(sc, MII_COMMAND_READ, 2);
smsc_miibus_sendbits(sc, phy, 5);
smsc_miibus_sendbits(sc, reg, 5);
/* Switch direction to PHY -> host */
smsc_mii_bitbang_write(sc, MDIRHOST);
smsc_mii_bitbang_write(sc, MDIRHOST | MDC);
smsc_mii_bitbang_write(sc, MDIRHOST);
/* Check for error. */
err = smsc_mii_bitbang_read(sc) & MDI;
/* Idle clock. */
smsc_mii_bitbang_write(sc, MDIRHOST | MDC);
smsc_mii_bitbang_write(sc, MDIRHOST);
val = 0;
for (i = 0; i < 16; i++) {
val <<= 1;
/* Read data prior to clock low-high transition. */
if (err == 0 && (smsc_mii_bitbang_read(sc) & MDI) != 0) {
val |= 1;
}
smsc_mii_bitbang_write(sc, MDIRHOST | MDC);
smsc_mii_bitbang_write(sc, MDIRHOST);
}
/* Set direction to host -> PHY, without a clock transition. */
smsc_mii_bitbang_write(sc, MDIRPHY);
SMSC_UNLOCK(sc);
irq_enable(sc->irq);
return (err == 0 ? val : 0);
}
static void smsc_miibus_writereg(struct smsc_data *sc, int phy, int reg, uint16_t val)
{
irq_disable(sc->irq);
SMSC_LOCK(sc);
smsc_select_bank(sc, 3);
smsc_miibus_sync(sc);
smsc_miibus_sendbits(sc, MII_COMMAND_START, 2);
smsc_miibus_sendbits(sc, MII_COMMAND_WRITE, 2);
smsc_miibus_sendbits(sc, phy, 5);
smsc_miibus_sendbits(sc, reg, 5);
smsc_miibus_sendbits(sc, MII_COMMAND_ACK, 2);
smsc_miibus_sendbits(sc, val, 16);
smsc_mii_bitbang_write(sc, MDIRPHY);
SMSC_UNLOCK(sc);
irq_enable(sc->irq);
}
static void smsc_reset(struct smsc_data *sc)
{
uint16_t ctr;
/*
* Mask all interrupts
*/
smsc_select_bank(sc, 2);
smsc_write_1(sc, MSK, 0);
/*
* Tell the device to reset
*/
smsc_select_bank(sc, 0);
smsc_write_2(sc, RCR, RCR_SOFT_RST);
/*
* Set up the configuration register
*/
smsc_select_bank(sc, 1);
smsc_write_2(sc, CR, CR_EPH_POWER_EN);
delay(1);
/*
* Turn off transmit and receive.
*/
smsc_select_bank(sc, 0);
smsc_write_2(sc, TCR, 0);
smsc_write_2(sc, RCR, 0);
/*
* Set up the control register
*/
smsc_select_bank(sc, 1);
ctr = smsc_read_2(sc, CTR);
ctr |= CTR_LE_ENABLE | CTR_AUTO_RELEASE;
smsc_write_2(sc, CTR, ctr);
/*
* Reset the MMU
*/
smsc_select_bank(sc, 2);
smsc_mmu_wait(sc);
smsc_write_2(sc, MMUCR, FIELD_PREP(MMUCR_CMD_MASK, MMUCR_CMD_MMU_RESET));
smsc_mmu_wait(sc);
}
static void smsc_enable(struct smsc_data *sc)
{
/*
* Set up the receive/PHY control register.
*/
smsc_select_bank(sc, 0);
smsc_write_2(sc, RPCR,
RPCR_ANEG | RPCR_DPLX | RPCR_SPEED |
FIELD_PREP(RPCR_LSA_MASK, RPCR_LED_LINK_ANY) |
FIELD_PREP(RPCR_LSB_MASK, RPCR_LED_ACT_ANY));
/*
* Set up the transmit and receive control registers.
*/
smsc_write_2(sc, TCR, TCR_TXENA | TCR_PAD_EN);
smsc_write_2(sc, RCR, RCR_RXEN | RCR_STRIP_CRC);
/*
* Clear all interrupt status
*/
smsc_select_bank(sc, 2);
smsc_write_1(sc, ACK, 0);
/*
* Set up the interrupt mask
*/
smsc_select_bank(sc, 2);
sc->smsc_mask = RCV_INT;
smsc_write_1(sc, MSK, sc->smsc_mask);
}
static int smsc_check(struct smsc_data *sc)
{
uint16_t val;
val = smsc_read_2(sc, BSR);
if (FIELD_GET(BSR_IDENTIFY_MASK, val) != BSR_IDENTIFY) {
LOG_ERR("Identification value not in BSR");
return -ENODEV;
}
smsc_write_2(sc, BSR, 0);
val = smsc_read_2(sc, BSR);
if (FIELD_GET(BSR_IDENTIFY_MASK, val) != BSR_IDENTIFY) {
LOG_ERR("Identification value not in BSR after write");
return -ENODEV;
}
smsc_select_bank(sc, 3);
val = smsc_read_2(sc, REV);
val = FIELD_GET(REV_CHIP_MASK, val);
if (smsc_chip_ids[val] == NULL) {
LOG_ERR("Unknown chip revision: %d", val);
return -ENODEV;
}
return 0;
}
static void smsc_recv_pkt(struct eth_context *data)
{
struct net_pkt *pkt;
unsigned int packet, status, len;
struct smsc_data *sc = &data->sc;
uint16_t val16;
int ret;
smsc_select_bank(sc, 2);
packet = smsc_read_1(sc, FIFO_RX);
while ((packet & FIFO_EMPTY) == 0) {
/*
* Point to the start of the packet.
*/
smsc_select_bank(sc, 2);
smsc_write_1(sc, PNR, packet);
smsc_write_2(sc, PTR, PTR_READ | PTR_RCV | PTR_AUTO_INCR);
/*
* Grab status and packet length.
*/
status = smsc_read_2(sc, DATA0);
val16 = smsc_read_2(sc, DATA0);
len = FIELD_GET(RX_LEN_MASK, val16);
if (len < PKT_CTRL_DATA_LEN) {
LOG_WRN("rxlen(%d) too short", len);
} else {
len -= PKT_CTRL_DATA_LEN;
if (status & RX_ODDFRM) {
len += 1;
}
if (len > NET_ETH_MAX_FRAME_SIZE) {
LOG_WRN("rxlen(%d) too large", len);
goto _mmu_release;
}
/*
* Check for errors.
*/
if (status & (RX_TOOSHORT | RX_TOOLNG | RX_BADCRC | RX_ALIGNERR)) {
LOG_WRN("status word (0x%04x) indicate some error", status);
goto _mmu_release;
}
/*
* Pull the packet out of the device.
*/
smsc_select_bank(sc, 2);
smsc_write_1(sc, PNR, packet);
/*
* Pointer start from 4 because we have already read status and len from
* RX_FIFO
*/
smsc_write_2(sc, PTR, 4 | PTR_READ | PTR_RCV | PTR_AUTO_INCR);
smsc_read_multi_2(sc, DATA0, (uint16_t *)rx_buffer, len / 2);
if (len & 1) {
rx_buffer[len - 1] = smsc_read_1(sc, DATA0);
}
pkt = net_pkt_rx_alloc_with_buffer(data->iface, len, AF_UNSPEC, 0,
K_NO_WAIT);
if (!pkt) {
LOG_ERR("Failed to obtain RX buffer");
goto _mmu_release;
}
ret = net_pkt_write(pkt, rx_buffer, len);
if (ret) {
net_pkt_unref(pkt);
LOG_WRN("net_pkt_write return %d", ret);
goto _mmu_release;
}
ret = net_recv_data(data->iface, pkt);
if (ret) {
LOG_WRN("net_recv_data return %d", ret);
net_pkt_unref(pkt);
}
}
_mmu_release:
/*
* Tell the device we're done
*/
smsc_mmu_wait(sc);
smsc_write_2(sc, MMUCR, FIELD_PREP(MMUCR_CMD_MASK, MMUCR_CMD_RELEASE));
smsc_mmu_wait(sc);
packet = smsc_read_1(sc, FIFO_RX);
}
sc->smsc_mask |= RCV_INT;
smsc_write_1(sc, MSK, sc->smsc_mask);
}
static int smsc_send_pkt(struct smsc_data *sc, uint8_t *buf, uint16_t len)
{
unsigned int polling_count;
uint8_t packet;
SMSC_LOCK(sc);
/*
* Request memory
*/
smsc_select_bank(sc, 2);
smsc_mmu_wait(sc);
smsc_write_2(sc, MMUCR, FIELD_PREP(MMUCR_CMD_MASK, MMUCR_CMD_TX_ALLOC));
/*
* Polling if the allocation succeeds.
*/
for (polling_count = TX_ALLOC_WAIT_TIME; polling_count > 0; polling_count--) {
if (smsc_read_1(sc, IST) & ALLOC_INT) {
break;
}
delay(1);
}
if (polling_count == 0) {
SMSC_UNLOCK(sc);
LOG_WRN("Alloc TX mem timeout");
return -1;
}
packet = smsc_read_1(sc, ARR);
if (packet & ARR_FAILED) {
SMSC_UNLOCK(sc);
LOG_WRN("Alloc TX mem failed");
return -1;
}
/*
* Tell the device to write to our packet number.
*/
smsc_write_1(sc, PNR, packet);
smsc_write_2(sc, PTR, PTR_AUTO_INCR);
/*
* Tell the device how long the packet is (include control data).
*/
smsc_write_2(sc, DATA0, 0);
smsc_write_2(sc, DATA0, len + PKT_CTRL_DATA_LEN);
smsc_write_multi_2(sc, DATA0, (uint16_t *)buf, len / 2);
/* Push out the control byte and the odd byte if needed. */
if (len & 1) {
smsc_write_2(sc, DATA0, (CTRL_ODD << 8) | buf[len - 1]);
} else {
smsc_write_2(sc, DATA0, 0);
}
/*
* Enqueue the packet.
*/
smsc_mmu_wait(sc);
smsc_write_2(sc, MMUCR, FIELD_PREP(MMUCR_CMD_MASK, MMUCR_CMD_ENQUEUE));
/*
* Unmask the TX empty interrupt
*/
sc->smsc_mask |= (TX_EMPTY_INT | TX_INT);
smsc_write_1(sc, MSK, sc->smsc_mask);
SMSC_UNLOCK(sc);
/*
* Finish up
*/
return 0;
}
static void smsc_isr_task(struct k_work *item)
{
struct smsc_data *sc = CONTAINER_OF(item, struct smsc_data, isr_work);
struct eth_context *data = CONTAINER_OF(sc, struct eth_context, sc);
uint8_t status;
unsigned int mem_info, ephsr, packet, tcr;
SMSC_LOCK(sc);
for (int loop_count = 0; loop_count < MAX_IRQ_LOOPS; loop_count++) {
smsc_select_bank(sc, 0);
mem_info = smsc_read_2(sc, MIR);
smsc_select_bank(sc, 2);
status = smsc_read_1(sc, IST);
LOG_DBG("INT 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x", status,
smsc_read_1(sc, MSK), mem_info, smsc_read_2(sc, FIFO));
status &= sc->smsc_mask;
if (!status) {
break;
}
/*
* Transmit error
*/
if (status & TX_INT) {
/*
* Kill off the packet if there is one.
*/
packet = smsc_read_1(sc, FIFO_TX);
if ((packet & FIFO_EMPTY) == 0) {
smsc_select_bank(sc, 2);
smsc_write_1(sc, PNR, packet);
smsc_write_2(sc, PTR, PTR_READ | PTR_AUTO_INCR);
smsc_select_bank(sc, 0);
ephsr = smsc_read_2(sc, EPHSR);
if ((ephsr & EPHSR_TX_SUC) == 0) {
LOG_WRN("bad packet, EPHSR: 0x%04x", ephsr);
}
smsc_select_bank(sc, 2);
smsc_mmu_wait(sc);
smsc_write_2(sc, MMUCR,
FIELD_PREP(MMUCR_CMD_MASK, MMUCR_CMD_RELEASE_PKT));
smsc_select_bank(sc, 0);
tcr = smsc_read_2(sc, TCR);
tcr |= TCR_TXENA | TCR_PAD_EN;
smsc_write_2(sc, TCR, tcr);
}
/*
* Ack the interrupt
*/
smsc_select_bank(sc, 2);
smsc_write_1(sc, ACK, TX_INT);
}
/*
* Receive
*/
if (status & RCV_INT) {
smsc_write_1(sc, ACK, RCV_INT);
smsc_recv_pkt(data);
}
/*
* Transmit empty
*/
if (status & TX_EMPTY_INT) {
smsc_write_1(sc, ACK, TX_EMPTY_INT);
sc->smsc_mask &= ~TX_EMPTY_INT;
}
}
smsc_select_bank(sc, 2);
smsc_write_1(sc, MSK, sc->smsc_mask);
SMSC_UNLOCK(sc);
}
static int smsc_init(struct smsc_data *sc)
{
int ret;
unsigned int val;
ret = smsc_check(sc);
if (ret) {
return ret;
}
SMSC_LOCK(sc);
smsc_reset(sc);
SMSC_UNLOCK(sc);
smsc_select_bank(sc, 3);
val = smsc_read_2(sc, REV);
sc->smsc_chip = FIELD_GET(REV_CHIP_MASK, val);
sc->smsc_rev = FIELD_GET(REV_REV_MASK, val);
smsc_select_bank(sc, 1);
sc->mac[0] = smsc_read_1(sc, IAR0);
sc->mac[1] = smsc_read_1(sc, IAR1);
sc->mac[2] = smsc_read_1(sc, IAR2);
sc->mac[3] = smsc_read_1(sc, IAR3);
sc->mac[4] = smsc_read_1(sc, IAR4);
sc->mac[5] = smsc_read_1(sc, IAR5);
return 0;
}
static const struct device *eth_get_phy(const struct device *dev)
{
const struct eth_config *cfg = dev->config;
return cfg->phy_dev;
}
static void phy_link_state_changed(const struct device *phy_dev, struct phy_link_state *state,
void *user_data)
{
const struct device *dev = user_data;
struct eth_context *data = dev->data;
if (state->is_up) {
net_eth_carrier_on(data->iface);
} else {
net_eth_carrier_off(data->iface);
}
}
static enum ethernet_hw_caps eth_smsc_get_caps(const struct device *dev)
{
ARG_UNUSED(dev);
return (ETHERNET_LINK_10BASE_T
| ETHERNET_LINK_100BASE_T
#if defined(CONFIG_NET_PROMISCUOUS_MODE)
| ETHERNET_PROMISC_MODE
#endif
);
}
static int eth_tx(const struct device *dev, struct net_pkt *pkt)
{
struct eth_context *data = dev->data;
struct smsc_data *sc = &data->sc;
uint16_t len;
len = net_pkt_get_len(pkt);
if (net_pkt_read(pkt, tx_buffer, len)) {
LOG_WRN("read pkt failed");
return -1;
}
return smsc_send_pkt(sc, tx_buffer, len);
}
static int eth_smsc_set_config(const struct device *dev,
enum ethernet_config_type type,
const struct ethernet_config *config)
{
int ret = 0;
switch (type) {
#if defined(CONFIG_NET_PROMISCUOUS_MODE)
case ETHERNET_CONFIG_TYPE_PROMISC_MODE:
struct eth_context *data = dev->data;
struct smsc_data *sc = &data->sc;
uint8_t reg_val;
SMSC_LOCK(sc);
smsc_select_bank(sc, 0);
reg_val = smsc_read_1(sc, RCR);
if (config->promisc_mode && !(reg_val & RCR_PRMS)) {
smsc_write_1(sc, RCR, reg_val | RCR_PRMS);
} else if (!config->promisc_mode && (reg_val & RCR_PRMS)) {
smsc_write_1(sc, RCR, reg_val & ~RCR_PRMS);
} else {
ret = -EALREADY;
}
SMSC_UNLOCK(sc);
break;
#endif
default:
ret = -ENOTSUP;
break;
}
return ret;
}
static void eth_initialize(struct net_if *iface)
{
const struct device *dev = net_if_get_device(iface);
struct eth_context *data = dev->data;
const struct eth_config *cfg = dev->config;
const struct device *phy_dev = cfg->phy_dev;
struct smsc_data *sc = &data->sc;
ethernet_init(iface);
net_if_carrier_off(iface);
smsc_reset(sc);
smsc_enable(sc);
LOG_INF("MAC %02x:%02x:%02x:%02x:%02x:%02x", sc->mac[0], sc->mac[1], sc->mac[2], sc->mac[3],
sc->mac[4], sc->mac[5]);
net_if_set_link_addr(iface, sc->mac, sizeof(sc->mac), NET_LINK_ETHERNET);
data->iface = iface;
if (device_is_ready(phy_dev)) {
phy_link_callback_set(phy_dev, phy_link_state_changed, (void *)dev);
} else {
LOG_ERR("PHY device not ready");
}
}
static const struct ethernet_api api_funcs = {
.iface_api.init = eth_initialize,
.get_capabilities = eth_smsc_get_caps,
.get_phy = eth_get_phy,
.set_config = eth_smsc_set_config,
.send = eth_tx,
};
static void eth_smsc_isr(const struct device *dev)
{
struct eth_context *data = dev->data;
struct smsc_data *sc = &data->sc;
uint32_t curbank;
curbank = smsc_current_bank(sc);
/*
* Block interrupts in order to let smsc91x_isr_task to kick in
*/
smsc_select_bank(sc, 2);
smsc_write_1(sc, MSK, 0);
smsc_select_bank(sc, curbank);
k_work_submit(&(sc->isr_work));
}
int eth_init(const struct device *dev)
{
struct eth_context *data = (struct eth_context *)dev->data;
struct smsc_data *sc = &data->sc;
int ret;
ret = k_mutex_init(&sc->lock);
if (ret) {
return ret;
}
k_work_init(&sc->isr_work, smsc_isr_task);
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), eth_smsc_isr, DEVICE_DT_INST_GET(0),
0);
DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
sc->smsc_reg = DEVICE_MMIO_GET(dev);
sc->irq = DT_INST_IRQN(0);
smsc_init(sc);
irq_enable(DT_INST_IRQN(0));
return 0;
}
static struct eth_context eth_0_context;
static struct eth_config eth_0_config = {
DEVICE_MMIO_ROM_INIT(DT_PARENT(DT_DRV_INST(0))),
.phy_dev = DEVICE_DT_GET(DT_INST_PHANDLE(0, phy_handle)),
};
ETH_NET_DEVICE_DT_INST_DEFINE(0,
eth_init, NULL, &eth_0_context,
&eth_0_config, CONFIG_ETH_INIT_PRIORITY,
&api_funcs, NET_ETH_MTU);
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT smsc_lan91c111_mdio
struct mdio_smsc_config {
const struct device *eth_dev;
};
static void mdio_smsc_bus_disable(const struct device *dev)
{
ARG_UNUSED(dev);
}
static void mdio_smsc_bus_enable(const struct device *dev)
{
ARG_UNUSED(dev);
}
static int mdio_smsc_read(const struct device *dev, uint8_t prtad, uint8_t devad, uint16_t *data)
{
const struct mdio_smsc_config *cfg = dev->config;
const struct device *eth_dev = cfg->eth_dev;
struct eth_context *eth_data = eth_dev->data;
struct smsc_data *sc = &eth_data->sc;
*data = smsc_miibus_readreg(sc, prtad, devad);
return 0;
}
static int mdio_smsc_write(const struct device *dev, uint8_t prtad, uint8_t devad, uint16_t data)
{
const struct mdio_smsc_config *cfg = dev->config;
const struct device *eth_dev = cfg->eth_dev;
struct eth_context *eth_data = eth_dev->data;
struct smsc_data *sc = &eth_data->sc;
smsc_miibus_writereg(sc, prtad, devad, data);
return 0;
}
static const struct mdio_driver_api mdio_smsc_api = {
.bus_disable = mdio_smsc_bus_disable,
.bus_enable = mdio_smsc_bus_enable,
.read = mdio_smsc_read,
.write = mdio_smsc_write,
};
const struct mdio_smsc_config mdio_smsc_config_0 = {
.eth_dev = DEVICE_DT_GET(DT_CHILD(DT_INST_PARENT(0), ethernet)),
};
DEVICE_DT_INST_DEFINE(0, NULL, NULL, NULL, &mdio_smsc_config_0, POST_KERNEL,
CONFIG_MDIO_INIT_PRIORITY, &mdio_smsc_api);