#include "bpfilter.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/timeout.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <net/if.h>
#include <net/if_media.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_clock.h>
#include <dev/ofw/ofw_gpio.h>
#include <dev/ofw/ofw_misc.h>
#include <dev/ofw/ofw_pinctrl.h>
#include <dev/ofw/ofw_regulator.h>
#include <dev/ofw/fdt.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#include <netinet/in.h>
#include <netinet/if_ether.h>
#define DMA_CONFIG 0x0000
#define DMA_CONFIG_DMA_64BIT_MODE (1U << 18)
#define DMA_CONFIG_STRICT_BURST (1U << 17)
#define DMA_CONFIG_BURST_LENGTH_MASK (0x7f << 1)
#define DMA_CONFIG_BURST_LENGTH_16 (0x10 << 1)
#define DMA_CONFIG_SOFTWARE_RESET (1U << 0)
#define DMA_CTRL 0x0004
#define DMA_CTRL_START_STOP_RX_DMA (1U << 1)
#define DMA_CTRL_START_STOP_TX_DMA (1U << 0)
#define DMA_STATUS_IRQ 0x0008
#define DMA_STATUS_IRQ_RX_MISSED_FRAME (1U << 7)
#define DMA_STATUS_IRQ_RX_DMA_STOPPED (1U << 6)
#define DMA_STATUS_IRQ_RX_DES_UNAVAILABLE (1U << 5)
#define DMA_STATUS_IRQ_RX_TRANSFER_DONE (1U << 4)
#define DMA_STATUS_IRQ_TX_TRANSFER_DONE (1U << 0)
#define DMA_INTR_ENABLE 0x000c
#define DMA_INTR_ENABLE_RX_MISSED_FRAME (1U << 7)
#define DMA_INTR_ENABLE_RX_DMA_STOPPED (1U << 6)
#define DMA_INTR_ENABLE_RX_DES_UNAVAILABLE (1U << 5)
#define DMA_INTR_ENABLE_RX_TRANSFER_DONE (1U << 4)
#define DMA_INTR_ENABLE_TX_TRANSFER_DONE (1U << 0)
#define DMA_TRANSMIT_AUTO_POLL_COUNTER 0x0010
#define DMA_TRANSMIT_POLL_DEMAND 0x0014
#define DMA_RECEIVE_POLL_DEMAND 0x0018
#define DMA_TRANSMIT_BASE_ADDRESS 0x001c
#define DMA_RECEIVE_BASE_ADDRESS 0x0020
#define DMA_RECEIVE_IRQ_MITIGATION 0x002c
#define DMA_RECEIVE_IRQ_MITIGATION_FRAME_COUNTER_SHIFT 0
#define DMA_RECEIVE_IRQ_MITIGATION_TIMEOUT_COUNTER_SHIFT 8
#define DMA_RECEIVE_IRQ_MITIGATION_MITIGATION_ENABLE (1U << 31)
#define MAC_GLOBAL_CTRL 0x0100
#define MAC_GLOBAL_CTRL_DUPLEX_MODE (1U << 2)
#define MAC_GLOBAL_CTRL_SPEED_MASK (0x3 << 0)
#define MAC_GLOBAL_CTRL_SPEED_10 (0x0 << 0)
#define MAC_GLOBAL_CTRL_SPEED_100 (0x1 << 0)
#define MAC_GLOBAL_CTRL_SPEED_1000 (0x2 << 0)
#define MAC_TRANSMIT_CTRL 0x0104
#define MAC_TRANSMIT_CTRL_IFG_LEN_MASK (0x7 << 4)
#define MAC_TRANSMIT_CTRL_TX_AUTO_RETRY (1U << 3)
#define MAC_TRANSMIT_CTRL_TX_ENABLE (1U << 0)
#define MAC_RECEIVE_CTRL 0x0108
#define MAC_RECEIVE_CTRL_STORE_FORWARD (1U << 3)
#define MAC_RECEIVE_CTRL_RX_ENABLE (1U << 0)
#define MAC_MAXIMUM_FRAME_SIZE 0x010c
#define MAC_TRANSMIT_JABBER_SIZE 0x0110
#define MAC_RECEIVE_JABBER_SIZE 0x0114
#define MAC_ADDR_CTRL 0x0118
#define MAC_ADDR_CTRL_PROMISCUOUS_MODE (1U << 8)
#define MAC_ADDR_CTRL_MAC_ADDR1_ENABLE (1U << 0)
#define MAC_ADDR1_HI 0x0120
#define MAC_ADDR1_ME 0x0124
#define MAC_ADDR1_LO 0x0128
#define MAC_MULTICAST_HASH_TABLE1 0x0150
#define MAC_MULTICAST_HASH_TABLE2 0x0154
#define MAC_MULTICAST_HASH_TABLE3 0x0158
#define MAC_MULTICAST_HASH_TABLE4 0x015c
#define MAC_MDIO_CTRL 0x01a0
#define MAC_MDIO_CTRL_START_MDIO_TRANS (1U << 15)
#define MAC_MDIO_CTRL_MDIO_READ_WRITE (1U << 10)
#define MAC_MDIO_CTRL_REGISTER_ADDRESS_SHIFT 5
#define MAC_MDIO_CTRL_PHY_ADDRESS_SHIFT 0
#define MAC_MDIO_DATA 0x01a4
#define MAC_TRANSMIT_FIFO_ALMOST_FULL 0x01c0
#define MAC_TRANSMIT_PACKET_START_THRESHOLD 0x01c4
#define MAC_RECEIVE_PACKET_START_THRESHOLD 0x01c8
#define MAC_INTR_ENABLE 0x01e4
#define APMU_EMAC_CLK_RST_CTRL 0x0000
#define APMU_EMAC_AXI_MST_ID (1U << 13)
#define APMU_EMAC_PHY_INTR_EN (1U << 12)
#define APMU_EMAC_RGMII_TXC_SRC_SEL (1U << 8)
#define APMU_EMAC_RGMII_DLINE 0x0004
#define APMU_EMAC_RGMII_DLINE_TX_DELAY_MASK (0xff << 24)
#define APMU_EMAC_RGMII_DLINE_TX_DELAY_SHIFT 8
#define APMU_EMAC_RGMII_DLINE_TX_STEP_MASK (0x3 << 20)
#define APMU_EMAC_RGMII_DLINE_TX_STEP_15P6 (0x0 << 20)
#define APMU_EMAC_RGMII_DLINE_TX_EN (1U << 16)
#define APMU_EMAC_RGMII_DLINE_RX_DELAY_MASK (0xff << 8)
#define APMU_EMAC_RGMII_DLINE_RX_DELAY_SHIFT 8
#define APMU_EMAC_RGMII_DLINE_RX_STEP_MASK (0x3 << 4)
#define APMU_EMAC_RGMII_DLINE_RX_STEP_15P6 (0x0 << 4)
#define APMU_EMAC_RGMII_DLINE_RX_EN (1U << 0)
struct smte_desc {
uint32_t sd_desc0;
uint32_t sd_desc1;
uint32_t sd_addr1;
uint32_t sd_addr2;
};
#define RX_DESC0_FRAME_PACKET_LENGTH_MASK (0x3fff << 0)
#define RX_DESC0_FRAME_PACKET_LENGTH_SHIFT 0
#define RX_DESC0_FRAME_RUNT (1U << 15)
#define RX_DESC0_FRAME_CRC_ERR (1U << 20)
#define RX_DESC0_FRAME_MAX_LEN_ERR (1U << 21)
#define RX_DESC0_FRAME_JABBER_ERR (1U << 22)
#define RX_DESC0_FRAME_LENGTH_ERR (1U << 23)
#define RX_DESC0_OWN (1U << 31)
#define RX_DESC1_SIZE1_MASK (0xfffff << 0)
#define RX_DESC1_SIZE1_SHIFT 0
#define RX_DESC1_SIZE2_MASK (0xfffff << 12)
#define RX_DESC1_SIZE2_SHIFT 12
#define RX_DESC1_END_RING (1U << 26)
#define TX_DESC0_OWN (1U << 31)
#define TX_DESC1_SIZE1_MASK (0xfff << 0)
#define TX_DESC1_SIZE1_SHIFT 0
#define TX_DESC1_SIZE2_MASK (0xfff << 12)
#define TX_DESC1_SIZE2_SHIFT 12
#define TX_DESC1_END_RING (1U << 26)
#define TX_DESC1_FIRST_SEGMENT (1U << 29)
#define TX_DESC1_LAST_SEGMENT (1U << 30)
#define TX_DESC1_INTERRUPT_ON_COMPLETION (1U << 31)
#define HREAD4(sc, reg) \
(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
#define HWRITE4(sc, reg, val) \
bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define HSET4(sc, reg, bits) \
HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
#define HCLR4(sc, reg, bits) \
HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
struct smte_buf {
bus_dmamap_t sb_map;
struct mbuf *sb_m;
};
#define SMTE_NTXDESC 1024
#define SMTE_NTXSEGS 16
#define SMTE_NRXDESC 1024
struct smte_dmamem {
bus_dmamap_t sdm_map;
bus_dma_segment_t sdm_seg;
size_t sdm_size;
caddr_t sdm_kva;
};
#define SMTE_DMA_MAP(_sdm) ((_sdm)->sdm_map)
#define SMTE_DMA_LEN(_sdm) ((_sdm)->sdm_size)
#define SMTE_DMA_DVA(_sdm) ((_sdm)->sdm_map->dm_segs[0].ds_addr)
#define SMTE_DMA_KVA(_sdm) ((void *)(_sdm)->sdm_kva)
struct smte_softc {
struct device sc_dev;
int sc_node;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
bus_dma_tag_t sc_dmat;
struct regmap *sc_apmu;
uint32_t sc_apmu_offset;
void *sc_ih;
struct arpcom sc_ac;
#define sc_lladdr sc_ac.ac_enaddr
struct mii_data sc_mii;
#define sc_media sc_mii.mii_media
int sc_link;
int sc_phyloc;
uint32_t sc_rx_delay;
uint32_t sc_tx_delay;
struct smte_dmamem *sc_txring;
struct smte_buf *sc_txbuf;
struct smte_desc *sc_txdesc;
int sc_tx_prod;
int sc_tx_cons;
struct smte_dmamem *sc_rxring;
struct smte_buf *sc_rxbuf;
struct smte_desc *sc_rxdesc;
int sc_rx_prod;
struct if_rxring sc_rx_ring;
int sc_rx_cons;
struct timeout sc_tick;
};
#define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
int smte_match(struct device *, void *, void *);
void smte_attach(struct device *, struct device *, void *);
void smte_init(struct smte_softc *sc);
void smte_phy_setup_emac(struct smte_softc *);
void smte_phy_setup_gmac(struct smte_softc *);
const struct cfattach smte_ca = {
sizeof(struct smte_softc), smte_match, smte_attach
};
struct cfdriver smte_cd = {
NULL, "smte", DV_IFNET
};
uint32_t smte_read(struct smte_softc *, bus_addr_t);
void smte_write(struct smte_softc *, bus_addr_t, uint32_t);
int smte_ioctl(struct ifnet *, u_long, caddr_t);
void smte_start(struct ifqueue *);
int smte_media_change(struct ifnet *);
void smte_media_status(struct ifnet *, struct ifmediareq *);
int smte_mii_readreg(struct device *, int, int);
void smte_mii_writereg(struct device *, int, int, int);
void smte_mii_statchg(struct device *);
void smte_lladdr_write(struct smte_softc *);
void smte_tick(void *);
void smte_rxtick(void *);
int smte_intr(void *);
void smte_tx_proc(struct smte_softc *);
void smte_rx_proc(struct smte_softc *);
void smte_up(struct smte_softc *);
void smte_down(struct smte_softc *);
void smte_iff(struct smte_softc *);
int smte_encap(struct smte_softc *, struct mbuf *, int *, int *);
void smte_stop_dma(struct smte_softc *);
struct smte_dmamem *
smte_dmamem_alloc(struct smte_softc *, bus_size_t, bus_size_t);
void smte_dmamem_free(struct smte_softc *, struct smte_dmamem *);
struct mbuf *smte_alloc_mbuf(struct smte_softc *, bus_dmamap_t);
void smte_fill_rx_ring(struct smte_softc *);
int
smte_match(struct device *parent, void *cfdata, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "spacemit,k1-emac");
}
void
smte_attach(struct device *parent, struct device *self, void *aux)
{
struct smte_softc *sc = (void *)self;
struct fdt_attach_args *faa = aux;
char phy_mode[16] = { 0 };
struct ifnet *ifp;
uint32_t apmu[2];
uint32_t phy;
int mii_flags = 0;
int node;
if (faa->fa_nreg < 1) {
printf(": no registers\n");
return;
}
sc->sc_iot = faa->fa_iot;
if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
printf(": can't map registers\n");
return;
}
sc->sc_dmat = faa->fa_dmat;
sc->sc_node = faa->fa_node;
if (OF_getpropintarray(sc->sc_node, "spacemit,apmu",
apmu, sizeof(apmu)) != sizeof(apmu)) {
printf(": no apmu register\n");
goto unmap;
}
sc->sc_apmu = regmap_byphandle(apmu[0]);
sc->sc_apmu_offset = apmu[1];
if (sc->sc_apmu == NULL) {
printf(": can't get apmu registers\n");
goto unmap;
}
OF_getprop(sc->sc_node, "phy-mode", phy_mode, sizeof(phy_mode));
if (strcmp(phy_mode, "rgmii") == 0)
mii_flags |= MIIF_SETDELAY;
else if (strcmp(phy_mode, "rgmii-rxid") == 0)
mii_flags |= MIIF_SETDELAY | MIIF_RXID;
else if (strcmp(phy_mode, "rgmii-txid") == 0)
mii_flags |= MIIF_SETDELAY | MIIF_TXID;
else if (strcmp(phy_mode, "rgmii-id") == 0)
mii_flags |= MIIF_SETDELAY | MIIF_RXID | MIIF_TXID;
sc->sc_rx_delay =
OF_getpropint(sc->sc_node, "rx-internal-delay-ps", 0);
sc->sc_tx_delay =
OF_getpropint(sc->sc_node, "tx-internal-delay-ps", 0);
phy = OF_getpropint(sc->sc_node, "phy-handle", 0);
node = OF_getnodebyphandle(phy);
if (node)
sc->sc_phyloc = OF_getpropint(node, "reg", MII_PHY_ANY);
else
sc->sc_phyloc = MII_PHY_ANY;
sc->sc_mii.mii_node = node;
OF_getprop(faa->fa_node, "local-mac-address",
&sc->sc_lladdr, ETHER_ADDR_LEN);
printf(": address %s\n", ether_sprintf(sc->sc_lladdr));
smte_init(sc);
sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_NET | IPL_MPSAFE,
smte_intr, sc, sc->sc_dev.dv_xname);
if (sc->sc_ih == NULL) {
printf("%s: can't establish interrupt\n", sc->sc_dev.dv_xname);
goto unmap;
}
timeout_set(&sc->sc_tick, smte_tick, sc);
ifp = &sc->sc_ac.ac_if;
ifp->if_softc = sc;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_xflags = IFXF_MPSAFE;
ifp->if_ioctl = smte_ioctl;
ifp->if_qstart = smte_start;
ifq_init_maxlen(&ifp->if_snd, SMTE_NTXDESC - 1);
memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
ifp->if_capabilities = IFCAP_VLAN_MTU;
sc->sc_mii.mii_ifp = ifp;
sc->sc_mii.mii_readreg = smte_mii_readreg;
sc->sc_mii.mii_writereg = smte_mii_writereg;
sc->sc_mii.mii_statchg = smte_mii_statchg;
ifmedia_init(&sc->sc_media, 0, smte_media_change, smte_media_status);
mii_attach(self, &sc->sc_mii, 0xffffffff, sc->sc_phyloc,
MII_OFFSET_ANY, MIIF_NOISOLATE | mii_flags);
if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
printf("%s: no PHY found!\n", sc->sc_dev.dv_xname);
ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL);
ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL);
} else
ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_AUTO);
if_attach(ifp);
ether_ifattach(ifp);
return;
unmap:
bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
}
void
smte_mdio_bus_init(struct smte_softc *sc)
{
uint32_t *reset_gpio;
int reset_gpiolen;
int node;
node = OF_getnodebyname(sc->sc_node, "mdio-bus");
if (!node)
return;
reset_gpiolen = OF_getproplen(node, "reset-gpios");
if (reset_gpiolen <= 0)
return;
reset_gpio = malloc(reset_gpiolen, M_TEMP, M_WAITOK);
OF_getpropintarray(node, "reset-gpios", reset_gpio, reset_gpiolen);
gpio_controller_config_pin(reset_gpio, GPIO_CONFIG_OUTPUT);
gpio_controller_set_pin(reset_gpio, 0);
free(reset_gpio, M_TEMP, reset_gpiolen);
}
void
smte_init(struct smte_softc *sc)
{
uint32_t rx_delay, tx_delay;
uint32_t val;
pinctrl_byname(sc->sc_node, "default");
clock_enable(sc->sc_node, NULL);
reset_deassert(sc->sc_node, NULL);
smte_mdio_bus_init(sc);
smte_stop_dma(sc);
val = regmap_read_4(sc->sc_apmu,
sc->sc_apmu_offset + APMU_EMAC_CLK_RST_CTRL);
val |= APMU_EMAC_AXI_MST_ID;
regmap_write_4(sc->sc_apmu,
sc->sc_apmu_offset + APMU_EMAC_CLK_RST_CTRL, val);
rx_delay = (sc->sc_rx_delay * 10 + 78) / 156;
tx_delay = (sc->sc_tx_delay * 10 + 78) / 156;
val = regmap_read_4(sc->sc_apmu,
sc->sc_apmu_offset + APMU_EMAC_RGMII_DLINE);
val = APMU_EMAC_RGMII_DLINE_RX_EN | APMU_EMAC_RGMII_DLINE_TX_EN;
val &= ~APMU_EMAC_RGMII_DLINE_RX_STEP_MASK;
val &= ~APMU_EMAC_RGMII_DLINE_RX_DELAY_MASK;
val |= APMU_EMAC_RGMII_DLINE_RX_STEP_15P6;
val |= rx_delay << APMU_EMAC_RGMII_DLINE_RX_DELAY_SHIFT;
val &= ~APMU_EMAC_RGMII_DLINE_TX_STEP_MASK;
val &= ~APMU_EMAC_RGMII_DLINE_TX_DELAY_MASK;
val |= APMU_EMAC_RGMII_DLINE_TX_STEP_15P6;
val |= tx_delay << APMU_EMAC_RGMII_DLINE_TX_DELAY_SHIFT;
regmap_write_4(sc->sc_apmu,
sc->sc_apmu_offset + APMU_EMAC_RGMII_DLINE, val);
smte_lladdr_write(sc);
HWRITE4(sc, MAC_ADDR_CTRL, MAC_ADDR_CTRL_MAC_ADDR1_ENABLE);
HWRITE4(sc, MAC_MULTICAST_HASH_TABLE1, 0);
HWRITE4(sc, MAC_MULTICAST_HASH_TABLE2, 0);
HWRITE4(sc, MAC_MULTICAST_HASH_TABLE3, 0);
HWRITE4(sc, MAC_MULTICAST_HASH_TABLE4, 0);
HWRITE4(sc, MAC_TRANSMIT_FIFO_ALMOST_FULL, 0x1f8);
HWRITE4(sc, MAC_TRANSMIT_PACKET_START_THRESHOLD, 1518);
HWRITE4(sc, MAC_RECEIVE_PACKET_START_THRESHOLD, 12);
HWRITE4(sc, MAC_MAXIMUM_FRAME_SIZE, ETHER_MAX_LEN);
HWRITE4(sc, MAC_TRANSMIT_JABBER_SIZE, ETHER_MAX_DIX_LEN);
HWRITE4(sc, MAC_RECEIVE_JABBER_SIZE, ETHER_MAX_DIX_LEN);
HWRITE4(sc, DMA_RECEIVE_IRQ_MITIGATION,
(64 << DMA_RECEIVE_IRQ_MITIGATION_FRAME_COUNTER_SHIFT) |
((600 * 312) << DMA_RECEIVE_IRQ_MITIGATION_TIMEOUT_COUNTER_SHIFT) |
DMA_RECEIVE_IRQ_MITIGATION_MITIGATION_ENABLE);
HWRITE4(sc, DMA_CONFIG, DMA_CONFIG_SOFTWARE_RESET);
delay(10000);
HWRITE4(sc, DMA_CONFIG, 0);
delay(10000);
HWRITE4(sc, DMA_CONFIG, DMA_CONFIG_STRICT_BURST |
DMA_CONFIG_DMA_64BIT_MODE | DMA_CONFIG_BURST_LENGTH_16);
}
void
smte_lladdr_write(struct smte_softc *sc)
{
HWRITE4(sc, MAC_ADDR1_HI,
sc->sc_lladdr[1] << 8 | sc->sc_lladdr[0] << 0);
HWRITE4(sc, MAC_ADDR1_ME,
sc->sc_lladdr[3] << 8 | sc->sc_lladdr[2] << 0);
HWRITE4(sc, MAC_ADDR1_LO,
sc->sc_lladdr[5] << 8 | sc->sc_lladdr[4] << 0);
}
void
smte_start(struct ifqueue *ifq)
{
struct ifnet *ifp = ifq->ifq_if;
struct smte_softc *sc = ifp->if_softc;
struct mbuf *m;
int error, idx, left, used;
if (!sc->sc_link) {
ifq_purge(ifq);
return;
}
idx = sc->sc_tx_prod;
left = sc->sc_tx_cons;
if (left <= idx)
left += SMTE_NTXDESC;
left -= idx;
used = 0;
for (;;) {
if (used + SMTE_NTXSEGS + 1 > left) {
ifq_set_oactive(ifq);
break;
}
m = ifq_dequeue(ifq);
if (m == NULL)
break;
error = smte_encap(sc, m, &idx, &used);
if (error == EFBIG) {
m_freem(m);
ifp->if_oerrors++;
continue;
}
#if NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
#endif
}
if (used > 0) {
sc->sc_tx_prod = idx;
ifp->if_timer = 5;
HWRITE4(sc, DMA_TRANSMIT_POLL_DEMAND, 1);
}
}
int
smte_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
{
struct smte_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)addr;
int error = 0, s;
s = splnet();
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
if (ifp->if_flags & IFF_RUNNING)
error = ENETRESET;
else
smte_up(sc);
} else {
if (ifp->if_flags & IFF_RUNNING)
smte_down(sc);
}
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
break;
case SIOCGIFRXR:
error = if_rxr_ioctl((struct if_rxrinfo *)ifr->ifr_data,
NULL, MCLBYTES, &sc->sc_rx_ring);
break;
default:
error = ether_ioctl(ifp, &sc->sc_ac, cmd, addr);
break;
}
if (error == ENETRESET) {
if (ifp->if_flags & IFF_RUNNING)
smte_iff(sc);
error = 0;
}
splx(s);
return error;
}
int
smte_media_change(struct ifnet *ifp)
{
struct smte_softc *sc = ifp->if_softc;
if (LIST_FIRST(&sc->sc_mii.mii_phys))
mii_mediachg(&sc->sc_mii);
return 0;
}
void
smte_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct smte_softc *sc = ifp->if_softc;
if (LIST_FIRST(&sc->sc_mii.mii_phys)) {
mii_pollstat(&sc->sc_mii);
ifmr->ifm_active = sc->sc_mii.mii_media_active;
ifmr->ifm_status = sc->sc_mii.mii_media_status;
}
}
int
smte_mii_readreg(struct device *self, int phy, int reg)
{
struct smte_softc *sc = (void *)self;
uint32_t ctrl;
int timo;
HWRITE4(sc, MAC_MDIO_DATA, 0);
HWRITE4(sc, MAC_MDIO_CTRL, MAC_MDIO_CTRL_START_MDIO_TRANS |
MAC_MDIO_CTRL_MDIO_READ_WRITE |
reg << MAC_MDIO_CTRL_REGISTER_ADDRESS_SHIFT |
phy << MAC_MDIO_CTRL_PHY_ADDRESS_SHIFT);
for (timo = 100; timo > 0; timo--) {
ctrl = HREAD4(sc, MAC_MDIO_CTRL);
if ((ctrl & MAC_MDIO_CTRL_START_MDIO_TRANS) == 0)
return HREAD4(sc, MAC_MDIO_DATA);
delay(100);
}
printf("%s: MII read timeout\n", sc->sc_dev.dv_xname);
return 0;
}
void
smte_mii_writereg(struct device *self, int phy, int reg, int val)
{
struct smte_softc *sc = (void *)self;
uint32_t ctrl;
int timo;
HWRITE4(sc, MAC_MDIO_DATA, val);
HWRITE4(sc, MAC_MDIO_CTRL, MAC_MDIO_CTRL_START_MDIO_TRANS |
reg << MAC_MDIO_CTRL_REGISTER_ADDRESS_SHIFT |
phy << MAC_MDIO_CTRL_PHY_ADDRESS_SHIFT);
for (timo = 100; timo > 0; timo--) {
ctrl = HREAD4(sc, MAC_MDIO_CTRL);
if ((ctrl & MAC_MDIO_CTRL_START_MDIO_TRANS) == 0)
return;
delay(100);
}
printf("%s: MII write timeout\n", sc->sc_dev.dv_xname);
}
void
smte_mii_statchg(struct device *self)
{
struct smte_softc *sc = (void *)self;
uint32_t ctrl;
ctrl = HREAD4(sc, MAC_GLOBAL_CTRL);
ctrl &= ~MAC_GLOBAL_CTRL_SPEED_MASK;
switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) {
case IFM_1000_SX:
case IFM_1000_LX:
case IFM_1000_CX:
case IFM_1000_T:
ctrl |= MAC_GLOBAL_CTRL_SPEED_1000;
sc->sc_link = 1;
break;
case IFM_100_TX:
ctrl |= MAC_GLOBAL_CTRL_SPEED_100;
sc->sc_link = 1;
break;
case IFM_10_T:
ctrl |= MAC_GLOBAL_CTRL_SPEED_10;
sc->sc_link = 1;
break;
default:
sc->sc_link = 0;
return;
}
if (sc->sc_link == 0)
return;
if ((sc->sc_mii.mii_media_active & IFM_GMASK) == IFM_FDX)
ctrl |= MAC_GLOBAL_CTRL_DUPLEX_MODE;
else
ctrl &= ~MAC_GLOBAL_CTRL_DUPLEX_MODE;
HWRITE4(sc, MAC_GLOBAL_CTRL, ctrl);
}
void
smte_tick(void *arg)
{
struct smte_softc *sc = arg;
int s;
s = splnet();
mii_tick(&sc->sc_mii);
splx(s);
timeout_add_sec(&sc->sc_tick, 1);
}
int
smte_intr(void *arg)
{
struct smte_softc *sc = arg;
uint32_t stat;
stat = HREAD4(sc, DMA_STATUS_IRQ);
if (stat & DMA_STATUS_IRQ_RX_TRANSFER_DONE ||
stat & DMA_STATUS_IRQ_RX_MISSED_FRAME)
smte_rx_proc(sc);
if (stat & DMA_STATUS_IRQ_TX_TRANSFER_DONE)
smte_tx_proc(sc);
HWRITE4(sc, DMA_STATUS_IRQ, stat);
return 1;
}
void
smte_tx_proc(struct smte_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct smte_desc *txd;
struct smte_buf *txb;
int idx, txfree;
bus_dmamap_sync(sc->sc_dmat, SMTE_DMA_MAP(sc->sc_txring), 0,
SMTE_DMA_LEN(sc->sc_txring),
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
txfree = 0;
while (sc->sc_tx_cons != sc->sc_tx_prod) {
idx = sc->sc_tx_cons;
KASSERT(idx < SMTE_NTXDESC);
txd = &sc->sc_txdesc[idx];
if (txd->sd_desc0 & TX_DESC0_OWN)
break;
txb = &sc->sc_txbuf[idx];
if (txb->sb_m != NULL) {
bus_dmamap_sync(sc->sc_dmat, txb->sb_map, 0,
txb->sb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat, txb->sb_map);
m_freem(txb->sb_m);
txb->sb_m = NULL;
}
txfree++;
if (sc->sc_tx_cons == (SMTE_NTXDESC - 1))
sc->sc_tx_cons = 0;
else
sc->sc_tx_cons++;
}
if (sc->sc_tx_cons == sc->sc_tx_prod)
ifp->if_timer = 0;
if (txfree) {
if (ifq_is_oactive(&ifp->if_snd))
ifq_restart(&ifp->if_snd);
}
}
void
smte_rx_proc(struct smte_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct smte_desc *rxd;
struct smte_buf *rxb;
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
struct mbuf *m;
int idx, len, cnt, put;
if ((ifp->if_flags & IFF_RUNNING) == 0)
return;
bus_dmamap_sync(sc->sc_dmat, SMTE_DMA_MAP(sc->sc_rxring), 0,
SMTE_DMA_LEN(sc->sc_rxring),
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
cnt = if_rxr_inuse(&sc->sc_rx_ring);
put = 0;
while (put < cnt) {
idx = sc->sc_rx_cons;
KASSERT(idx < SMTE_NRXDESC);
rxd = &sc->sc_rxdesc[idx];
if (rxd->sd_desc0 & RX_DESC0_OWN)
break;
len = rxd->sd_desc0 & RX_DESC0_FRAME_PACKET_LENGTH_MASK;
rxb = &sc->sc_rxbuf[idx];
KASSERT(rxb->sb_m != NULL);
bus_dmamap_sync(sc->sc_dmat, rxb->sb_map, 0,
len, BUS_DMASYNC_POSTREAD);
bus_dmamap_unload(sc->sc_dmat, rxb->sb_map);
m = rxb->sb_m;
rxb->sb_m = NULL;
if (len < ETHER_CRC_LEN || len > m->m_len ||
rxd->sd_desc0 & RX_DESC0_FRAME_RUNT ||
rxd->sd_desc0 & RX_DESC0_FRAME_CRC_ERR ||
rxd->sd_desc0 & RX_DESC0_FRAME_MAX_LEN_ERR ||
rxd->sd_desc0 & RX_DESC0_FRAME_JABBER_ERR ||
rxd->sd_desc0 & RX_DESC0_FRAME_LENGTH_ERR) {
ifp->if_ierrors++;
m_freem(m);
} else {
len -= ETHER_CRC_LEN;
m->m_pkthdr.len = m->m_len = len;
ml_enqueue(&ml, m);
}
put++;
if (sc->sc_rx_cons == (SMTE_NRXDESC - 1))
sc->sc_rx_cons = 0;
else
sc->sc_rx_cons++;
}
if_rxr_put(&sc->sc_rx_ring, put);
if (ifiq_input(&ifp->if_rcv, &ml))
if_rxr_livelocked(&sc->sc_rx_ring);
smte_fill_rx_ring(sc);
bus_dmamap_sync(sc->sc_dmat, SMTE_DMA_MAP(sc->sc_rxring), 0,
SMTE_DMA_LEN(sc->sc_rxring),
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
}
void
smte_up(struct smte_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct smte_buf *txb, *rxb;
int i;
sc->sc_txring = smte_dmamem_alloc(sc,
SMTE_NTXDESC * sizeof(struct smte_desc), 8);
sc->sc_txdesc = SMTE_DMA_KVA(sc->sc_txring);
sc->sc_txbuf = malloc(sizeof(struct smte_buf) * SMTE_NTXDESC,
M_DEVBUF, M_WAITOK);
for (i = 0; i < SMTE_NTXDESC; i++) {
txb = &sc->sc_txbuf[i];
bus_dmamap_create(sc->sc_dmat, MCLBYTES, SMTE_NTXSEGS,
MCLBYTES, 0, BUS_DMA_WAITOK, &txb->sb_map);
txb->sb_m = NULL;
}
bus_dmamap_sync(sc->sc_dmat, SMTE_DMA_MAP(sc->sc_txring),
0, SMTE_DMA_LEN(sc->sc_txring), BUS_DMASYNC_PREWRITE);
sc->sc_tx_prod = sc->sc_tx_cons = 0;
HWRITE4(sc, DMA_TRANSMIT_BASE_ADDRESS, SMTE_DMA_DVA(sc->sc_txring));
sc->sc_rxring = smte_dmamem_alloc(sc,
SMTE_NRXDESC * sizeof(struct smte_desc), 8);
sc->sc_rxdesc = SMTE_DMA_KVA(sc->sc_rxring);
sc->sc_rxbuf = malloc(sizeof(struct smte_buf) * SMTE_NRXDESC,
M_DEVBUF, M_WAITOK);
for (i = 0; i < SMTE_NRXDESC; i++) {
rxb = &sc->sc_rxbuf[i];
bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
MCLBYTES, 0, BUS_DMA_WAITOK, &rxb->sb_map);
rxb->sb_m = NULL;
}
if_rxr_init(&sc->sc_rx_ring, 2, SMTE_NRXDESC);
sc->sc_rx_prod = sc->sc_rx_cons = 0;
smte_fill_rx_ring(sc);
bus_dmamap_sync(sc->sc_dmat, SMTE_DMA_MAP(sc->sc_rxring),
0, SMTE_DMA_LEN(sc->sc_rxring),
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
HWRITE4(sc, DMA_RECEIVE_BASE_ADDRESS, SMTE_DMA_DVA(sc->sc_rxring));
if (LIST_FIRST(&sc->sc_mii.mii_phys))
mii_mediachg(&sc->sc_mii);
smte_iff(sc);
HSET4(sc, DMA_INTR_ENABLE, DMA_INTR_ENABLE_TX_TRANSFER_DONE |
DMA_INTR_ENABLE_RX_TRANSFER_DONE);
HSET4(sc, DMA_INTR_ENABLE, DMA_INTR_ENABLE_RX_MISSED_FRAME);
HCLR4(sc, MAC_TRANSMIT_CTRL, MAC_TRANSMIT_CTRL_IFG_LEN_MASK);
HSET4(sc, MAC_TRANSMIT_CTRL,
MAC_TRANSMIT_CTRL_TX_ENABLE | MAC_TRANSMIT_CTRL_TX_AUTO_RETRY);
HSET4(sc, MAC_RECEIVE_CTRL,
MAC_RECEIVE_CTRL_RX_ENABLE | MAC_RECEIVE_CTRL_STORE_FORWARD);
HWRITE4(sc, DMA_TRANSMIT_AUTO_POLL_COUNTER, 0);
HSET4(sc, DMA_CTRL, DMA_CTRL_START_STOP_TX_DMA);
HSET4(sc, DMA_CTRL, DMA_CTRL_START_STOP_RX_DMA);
ifp->if_flags |= IFF_RUNNING;
ifq_clr_oactive(&ifp->if_snd);
timeout_add_sec(&sc->sc_tick, 1);
}
void
smte_down(struct smte_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct smte_buf *txb, *rxb;
int i;
timeout_del(&sc->sc_tick);
ifp->if_flags &= ~IFF_RUNNING;
ifq_clr_oactive(&ifp->if_snd);
ifp->if_timer = 0;
smte_stop_dma(sc);
intr_barrier(sc->sc_ih);
ifq_barrier(&ifp->if_snd);
for (i = 0; i < SMTE_NTXDESC; i++) {
txb = &sc->sc_txbuf[i];
if (txb->sb_m != NULL) {
bus_dmamap_sync(sc->sc_dmat, txb->sb_map, 0,
txb->sb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat, txb->sb_map);
m_freem(txb->sb_m);
}
bus_dmamap_destroy(sc->sc_dmat, txb->sb_map);
}
smte_dmamem_free(sc, sc->sc_txring);
free(sc->sc_txbuf, M_DEVBUF, 0);
for (i = 0; i < SMTE_NRXDESC; i++) {
rxb = &sc->sc_rxbuf[i];
if (rxb->sb_m != NULL) {
bus_dmamap_sync(sc->sc_dmat, rxb->sb_map, 0,
rxb->sb_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
bus_dmamap_unload(sc->sc_dmat, rxb->sb_map);
m_freem(rxb->sb_m);
}
bus_dmamap_destroy(sc->sc_dmat, rxb->sb_map);
}
smte_dmamem_free(sc, sc->sc_rxring);
free(sc->sc_rxbuf, M_DEVBUF, 0);
}
void
smte_iff(struct smte_softc *sc)
{
struct arpcom *ac = &sc->sc_ac;
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct ether_multi *enm;
struct ether_multistep step;
uint32_t crc, hashbit, hashreg;
uint16_t hash[4];
uint32_t val;
smte_lladdr_write(sc);
val = MAC_ADDR_CTRL_MAC_ADDR1_ENABLE;
ifp->if_flags &= ~IFF_ALLMULTI;
memset(hash, 0, sizeof(hash));
if (ifp->if_flags & IFF_PROMISC) {
ifp->if_flags |= IFF_ALLMULTI;
val |= MAC_ADDR_CTRL_PROMISCUOUS_MODE;
} else if (ac->ac_multirangecnt > 0) {
ifp->if_flags |= IFF_ALLMULTI;
memset(hash, 0xff, sizeof(hash));
} else {
ETHER_FIRST_MULTI(step, ac, enm);
while (enm != NULL) {
crc = ether_crc32_be(enm->enm_addrlo,
ETHER_ADDR_LEN) >> 26;
hashreg = (crc >> 4);
hashbit = (crc & 0xf);
hash[hashreg] |= (1 << hashbit);
ETHER_NEXT_MULTI(step, enm);
}
}
HWRITE4(sc, MAC_MULTICAST_HASH_TABLE1, hash[0]);
HWRITE4(sc, MAC_MULTICAST_HASH_TABLE2, hash[1]);
HWRITE4(sc, MAC_MULTICAST_HASH_TABLE3, hash[2]);
HWRITE4(sc, MAC_MULTICAST_HASH_TABLE4, hash[3]);
HWRITE4(sc, MAC_ADDR_CTRL, val);
}
int
smte_encap(struct smte_softc *sc, struct mbuf *m, int *idx, int *used)
{
struct smte_desc *txd, *txd_start;
bus_dmamap_t map;
int cur, frag, i;
cur = frag = *idx;
map = sc->sc_txbuf[cur].sb_map;
if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT)) {
if (m_defrag(m, M_DONTWAIT))
return EFBIG;
if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT))
return EFBIG;
}
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
BUS_DMASYNC_PREWRITE);
txd = txd_start = &sc->sc_txdesc[frag];
for (i = 0; i < map->dm_nsegs; i++) {
txd->sd_addr1 = map->dm_segs[i].ds_addr;
txd->sd_desc1 = map->dm_segs[i].ds_len;
if (frag == (SMTE_NRXDESC - 1))
txd->sd_desc1 |= TX_DESC1_END_RING;
if (i == 0)
txd->sd_desc1 |= TX_DESC1_FIRST_SEGMENT;
if (i == (map->dm_nsegs - 1))
txd->sd_desc1 |= TX_DESC1_LAST_SEGMENT |
TX_DESC1_INTERRUPT_ON_COMPLETION;
if (i != 0)
txd->sd_desc0 = TX_DESC0_OWN;
bus_dmamap_sync(sc->sc_dmat, SMTE_DMA_MAP(sc->sc_txring),
frag * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREWRITE);
cur = frag;
if (frag == (SMTE_NTXDESC - 1)) {
txd = &sc->sc_txdesc[0];
frag = 0;
} else {
txd++;
frag++;
}
KASSERT(frag != sc->sc_tx_cons);
}
txd_start->sd_desc0 = TX_DESC0_OWN;
bus_dmamap_sync(sc->sc_dmat, SMTE_DMA_MAP(sc->sc_txring),
*idx * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREWRITE);
KASSERT(sc->sc_txbuf[cur].sb_m == NULL);
sc->sc_txbuf[*idx].sb_map = sc->sc_txbuf[cur].sb_map;
sc->sc_txbuf[cur].sb_map = map;
sc->sc_txbuf[cur].sb_m = m;
*idx = frag;
*used += map->dm_nsegs;
return 0;
}
void
smte_stop_dma(struct smte_softc *sc)
{
HWRITE4(sc, MAC_INTR_ENABLE, 0);
HWRITE4(sc, DMA_INTR_ENABLE, 0);
HWRITE4(sc, MAC_TRANSMIT_CTRL, 0);
HWRITE4(sc, MAC_RECEIVE_CTRL, 0);
HWRITE4(sc, DMA_CTRL, 0);
}
struct smte_dmamem *
smte_dmamem_alloc(struct smte_softc *sc, bus_size_t size, bus_size_t align)
{
struct smte_dmamem *sdm;
int nsegs;
sdm = malloc(sizeof(*sdm), M_DEVBUF, M_WAITOK | M_ZERO);
sdm->sdm_size = size;
if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &sdm->sdm_map) != 0)
goto sdmfree;
if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &sdm->sdm_seg, 1,
&nsegs, BUS_DMA_WAITOK) != 0)
goto destroy;
if (bus_dmamem_map(sc->sc_dmat, &sdm->sdm_seg, nsegs, size,
&sdm->sdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0)
goto free;
if (bus_dmamap_load(sc->sc_dmat, sdm->sdm_map, sdm->sdm_kva, size,
NULL, BUS_DMA_WAITOK) != 0)
goto unmap;
memset(sdm->sdm_kva, 0, size);
return sdm;
unmap:
bus_dmamem_unmap(sc->sc_dmat, sdm->sdm_kva, size);
free:
bus_dmamem_free(sc->sc_dmat, &sdm->sdm_seg, 1);
destroy:
bus_dmamap_destroy(sc->sc_dmat, sdm->sdm_map);
sdmfree:
free(sdm, M_DEVBUF, 0);
return NULL;
}
void
smte_dmamem_free(struct smte_softc *sc, struct smte_dmamem *sdm)
{
bus_dmamem_unmap(sc->sc_dmat, sdm->sdm_kva, sdm->sdm_size);
bus_dmamem_free(sc->sc_dmat, &sdm->sdm_seg, 1);
bus_dmamap_destroy(sc->sc_dmat, sdm->sdm_map);
free(sdm, M_DEVBUF, 0);
}
struct mbuf *
smte_alloc_mbuf(struct smte_softc *sc, bus_dmamap_t map)
{
struct mbuf *m = NULL;
m = MCLGETL(NULL, M_DONTWAIT, MCLBYTES);
if (m == NULL)
return NULL;
m->m_len = m->m_pkthdr.len = MCLBYTES;
m_adj(m, ETHER_ALIGN);
if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT) != 0) {
printf("%s: could not load mbuf DMA map", DEVNAME(sc));
m_freem(m);
return NULL;
}
bus_dmamap_sync(sc->sc_dmat, map, 0,
m->m_pkthdr.len, BUS_DMASYNC_PREREAD);
return m;
}
void
smte_fill_rx_ring(struct smte_softc *sc)
{
struct smte_desc *rxd;
struct smte_buf *rxb;
u_int slots;
for (slots = if_rxr_get(&sc->sc_rx_ring, SMTE_NRXDESC);
slots > 0; slots--) {
rxb = &sc->sc_rxbuf[sc->sc_rx_prod];
rxb->sb_m = smte_alloc_mbuf(sc, rxb->sb_map);
if (rxb->sb_m == NULL)
break;
rxd = &sc->sc_rxdesc[sc->sc_rx_prod];
rxd->sd_desc1 = rxb->sb_map->dm_segs[0].ds_len;
rxd->sd_addr1 = rxb->sb_map->dm_segs[0].ds_addr;
if (sc->sc_rx_prod == (SMTE_NRXDESC - 1))
rxd->sd_desc1 |= RX_DESC1_END_RING;
bus_dmamap_sync(sc->sc_dmat, SMTE_DMA_MAP(sc->sc_rxring),
sc->sc_rx_prod * sizeof(*rxd), sizeof(*rxd),
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
rxd->sd_desc0 = RX_DESC0_OWN;
bus_dmamap_sync(sc->sc_dmat, SMTE_DMA_MAP(sc->sc_rxring),
sc->sc_rx_prod * sizeof(*rxd), sizeof(*rxd),
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
if (sc->sc_rx_prod == (SMTE_NRXDESC - 1))
sc->sc_rx_prod = 0;
else
sc->sc_rx_prod++;
}
if_rxr_put(&sc->sc_rx_ring, slots);
}