#include "bpfilter.h"
#include "kstat.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
#if NKSTAT > 0
#include <sys/kstat.h>
#endif
#include <netinet/in.h>
#include <netinet/if_ether.h>
#define GMAC_MAC_CONF 0x0000
#define GMAC_MAC_CONF_JD (1 << 22)
#define GMAC_MAC_CONF_BE (1 << 21)
#define GMAC_MAC_CONF_DCRS (1 << 16)
#define GMAC_MAC_CONF_PS (1 << 15)
#define GMAC_MAC_CONF_FES (1 << 14)
#define GMAC_MAC_CONF_LM (1 << 12)
#define GMAC_MAC_CONF_DM (1 << 11)
#define GMAC_MAC_CONF_TE (1 << 3)
#define GMAC_MAC_CONF_RE (1 << 2)
#define GMAC_MAC_FRM_FILT 0x0004
#define GMAC_MAC_FRM_FILT_PM (1 << 4)
#define GMAC_MAC_FRM_FILT_HMC (1 << 2)
#define GMAC_MAC_FRM_FILT_PR (1 << 0)
#define GMAC_HASH_TAB_HI 0x0008
#define GMAC_HASH_TAB_LO 0x000c
#define GMAC_GMII_ADDR 0x0010
#define GMAC_GMII_ADDR_PA_SHIFT 11
#define GMAC_GMII_ADDR_GR_SHIFT 6
#define GMAC_GMII_ADDR_CR_SHIFT 2
#define GMAC_GMII_ADDR_CR_MASK 0xf
#define GMAC_GMII_ADDR_CR_DIV_42 0
#define GMAC_GMII_ADDR_CR_DIV_62 1
#define GMAC_GMII_ADDR_CR_DIV_16 2
#define GMAC_GMII_ADDR_CR_DIV_26 3
#define GMAC_GMII_ADDR_CR_DIV_102 4
#define GMAC_GMII_ADDR_CR_DIV_124 5
#define GMAC_GMII_ADDR_GW (1 << 1)
#define GMAC_GMII_ADDR_GB (1 << 0)
#define GMAC_GMII_DATA 0x0014
#define GMAC_VERSION 0x0020
#define GMAC_VERSION_SNPS_MASK 0xff
#define GMAC_INT_MASK 0x003c
#define GMAC_INT_MASK_LPIIM (1 << 10)
#define GMAC_INT_MASK_PIM (1 << 3)
#define GMAC_INT_MASK_RIM (1 << 0)
#define GMAC_MAC_ADDR0_HI 0x0040
#define GMAC_MAC_ADDR0_LO 0x0044
#define GMAC_MAC_MMC_CTRL 0x0100
#define GMAC_MAC_MMC_CTRL_ROR (1 << 2)
#define GMAC_MAC_MMC_CTRL_CR (1 << 0)
#define GMAC_MMC_RX_INT_MSK 0x010c
#define GMAC_MMC_TX_INT_MSK 0x0110
#define GMAC_MMC_TXOCTETCNT_GB 0x0114
#define GMAC_MMC_TXFRMCNT_GB 0x0118
#define GMAC_MMC_TXUNDFLWERR 0x0148
#define GMAC_MMC_TXCARERR 0x0160
#define GMAC_MMC_TXOCTETCNT_G 0x0164
#define GMAC_MMC_TXFRMCNT_G 0x0168
#define GMAC_MMC_RXFRMCNT_GB 0x0180
#define GMAC_MMC_RXOCTETCNT_GB 0x0184
#define GMAC_MMC_RXOCTETCNT_G 0x0188
#define GMAC_MMC_RXMCFRMCNT_G 0x0190
#define GMAC_MMC_RXCRCERR 0x0194
#define GMAC_MMC_RXLENERR 0x01c8
#define GMAC_MMC_RXFIFOOVRFLW 0x01d4
#define GMAC_MMC_IPC_INT_MSK 0x0200
#define GMAC_BUS_MODE 0x1000
#define GMAC_BUS_MODE_8XPBL (1 << 24)
#define GMAC_BUS_MODE_USP (1 << 23)
#define GMAC_BUS_MODE_RPBL_MASK (0x3f << 17)
#define GMAC_BUS_MODE_RPBL_SHIFT 17
#define GMAC_BUS_MODE_FB (1 << 16)
#define GMAC_BUS_MODE_PBL_MASK (0x3f << 8)
#define GMAC_BUS_MODE_PBL_SHIFT 8
#define GMAC_BUS_MODE_SWR (1 << 0)
#define GMAC_TX_POLL_DEMAND 0x1004
#define GMAC_RX_DESC_LIST_ADDR 0x100c
#define GMAC_TX_DESC_LIST_ADDR 0x1010
#define GMAC_STATUS 0x1014
#define GMAC_STATUS_MMC (1 << 27)
#define GMAC_STATUS_RI (1 << 6)
#define GMAC_STATUS_TU (1 << 2)
#define GMAC_STATUS_TI (1 << 0)
#define GMAC_OP_MODE 0x1018
#define GMAC_OP_MODE_RSF (1 << 25)
#define GMAC_OP_MODE_TSF (1 << 21)
#define GMAC_OP_MODE_FTF (1 << 20)
#define GMAC_OP_MODE_TTC_MASK (0x7 << 14)
#define GMAC_OP_MODE_TTC_64 (0x0 << 14)
#define GMAC_OP_MODE_TTC_128 (0x1 << 14)
#define GMAC_OP_MODE_ST (1 << 13)
#define GMAC_OP_MODE_RTC_MASK (0x3 << 3)
#define GMAC_OP_MODE_RTC_64 (0x0 << 3)
#define GMAC_OP_MODE_RTC_128 (0x3 << 3)
#define GMAC_OP_MODE_OSF (1 << 2)
#define GMAC_OP_MODE_SR (1 << 1)
#define GMAC_INT_ENA 0x101c
#define GMAC_INT_ENA_NIE (1 << 16)
#define GMAC_INT_ENA_RIE (1 << 6)
#define GMAC_INT_ENA_TUE (1 << 2)
#define GMAC_INT_ENA_TIE (1 << 0)
#define GMAC_AXI_BUS_MODE 0x1028
#define GMAC_AXI_BUS_MODE_WR_OSR_LMT_MASK (0xf << 20)
#define GMAC_AXI_BUS_MODE_WR_OSR_LMT_SHIFT 20
#define GMAC_AXI_BUS_MODE_RD_OSR_LMT_MASK (0xf << 16)
#define GMAC_AXI_BUS_MODE_RD_OSR_LMT_SHIFT 16
#define GMAC_AXI_BUS_MODE_BLEN_256 (1 << 7)
#define GMAC_AXI_BUS_MODE_BLEN_128 (1 << 6)
#define GMAC_AXI_BUS_MODE_BLEN_64 (1 << 5)
#define GMAC_AXI_BUS_MODE_BLEN_32 (1 << 4)
#define GMAC_AXI_BUS_MODE_BLEN_16 (1 << 3)
#define GMAC_AXI_BUS_MODE_BLEN_8 (1 << 2)
#define GMAC_AXI_BUS_MODE_BLEN_4 (1 << 1)
#define GMAC_HW_FEATURE 0x1058
#define GMAC_HW_FEATURE_ENHDESSEL (1 << 24)
struct dwge_desc {
uint32_t sd_status;
uint32_t sd_len;
uint32_t sd_addr;
uint32_t sd_next;
};
#define TDES0_DB (1 << 0)
#define TDES0_UF (1 << 1)
#define TDES0_ED (1 << 2)
#define TDES0_CC_MASK (0xf << 3)
#define TDES0_CC_SHIFT 3
#define TDES0_EC (1 << 8)
#define TDES0_LC (1 << 9)
#define TDES0_NC (1 << 10)
#define TDES0_PCE (1 << 12)
#define TDES0_JT (1 << 14)
#define TDES0_IHE (1 << 16)
#define TDES0_OWN (1U << 31)
#define ETDES0_TCH (1 << 20)
#define ETDES0_FS (1 << 28)
#define ETDES0_LS (1 << 29)
#define ETDES0_IC (1 << 30)
#define RDES0_PE (1 << 0)
#define RDES0_CE (1 << 1)
#define RDES0_RE (1 << 3)
#define RDES0_RWT (1 << 4)
#define RDES0_FT (1 << 5)
#define RDES0_LC (1 << 6)
#define RDES0_IPC (1 << 7)
#define RDES0_LS (1 << 8)
#define RDES0_FS (1 << 9)
#define RDES0_OE (1 << 11)
#define RDES0_SAF (1 << 13)
#define RDES0_DE (1 << 14)
#define RDES0_ES (1 << 15)
#define RDES0_FL_MASK 0x3fff
#define RDES0_FL_SHIFT 16
#define RDES0_AFM (1 << 30)
#define RDES0_OWN (1U << 31)
#define TDES1_TBS1 (0xfff << 0)
#define TDES1_TCH (1 << 24)
#define TDES1_DC (1 << 26)
#define TDES1_CIC_MASK (0x3 << 27)
#define TDES1_CIC_IP (1 << 27)
#define TDES1_CIC_NO_PSE (2 << 27)
#define TDES1_CIC_FULL (3 << 27)
#define TDES1_FS (1 << 29)
#define TDES1_LS (1 << 30)
#define TDES1_IC (1U << 31)
#define RDES1_RBS1 (0xfff << 0)
#define RDES1_RCH (1 << 24)
#define RDES1_DIC (1U << 31)
#define ERDES1_RCH (1 << 14)
struct dwge_buf {
bus_dmamap_t tb_map;
struct mbuf *tb_m;
};
#define DWGE_NTXDESC 512
#define DWGE_NTXSEGS 16
#define DWGE_NRXDESC 512
struct dwge_dmamem {
bus_dmamap_t tdm_map;
bus_dma_segment_t tdm_seg;
size_t tdm_size;
caddr_t tdm_kva;
};
#define DWGE_DMA_MAP(_tdm) ((_tdm)->tdm_map)
#define DWGE_DMA_LEN(_tdm) ((_tdm)->tdm_size)
#define DWGE_DMA_DVA(_tdm) ((_tdm)->tdm_map->dm_segs[0].ds_addr)
#define DWGE_DMA_KVA(_tdm) ((void *)(_tdm)->tdm_kva)
struct dwge_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;
void *sc_ih;
struct if_device sc_ifd;
struct arpcom sc_ac;
#define sc_lladdr sc_ac.ac_enaddr
struct mii_data sc_mii;
#define sc_media sc_mii.mii_media
uint64_t sc_fixed_media;
int sc_link;
int sc_phyloc;
int sc_force_thresh_dma_mode;
int sc_enh_desc;
int sc_defrag;
struct dwge_dmamem *sc_txring;
struct dwge_buf *sc_txbuf;
struct dwge_desc *sc_txdesc;
int sc_tx_prod;
int sc_tx_cons;
struct dwge_dmamem *sc_rxring;
struct dwge_buf *sc_rxbuf;
struct dwge_desc *sc_rxdesc;
int sc_rx_prod;
struct if_rxring sc_rx_ring;
int sc_rx_cons;
struct timeout sc_tick;
struct timeout sc_rxto;
uint32_t sc_clk;
bus_size_t sc_clk_sel;
uint32_t sc_clk_sel_125;
uint32_t sc_clk_sel_25;
uint32_t sc_clk_sel_2_5;
#if NKSTAT > 0
struct mutex sc_kstat_mtx;
struct kstat *sc_kstat;
#endif
};
#define DEVNAME(_s) ((_s)->sc_dev.dv_xname)
int dwge_match(struct device *, void *, void *);
void dwge_attach(struct device *, struct device *, void *);
void dwge_setup_allwinner(struct dwge_softc *);
void dwge_setup_rockchip(struct dwge_softc *);
const struct cfattach dwge_ca = {
sizeof(struct dwge_softc), dwge_match, dwge_attach
};
struct cfdriver dwge_cd = {
NULL, "dwge", DV_IFNET
};
void dwge_reset_phy(struct dwge_softc *);
uint32_t dwge_read(struct dwge_softc *, bus_addr_t);
void dwge_write(struct dwge_softc *, bus_addr_t, uint32_t);
int dwge_ioctl(struct ifnet *, u_long, caddr_t);
void dwge_start(struct ifqueue *);
void dwge_watchdog(struct ifnet *);
int dwge_media_change(struct ifnet *);
void dwge_media_status(struct ifnet *, struct ifmediareq *);
int dwge_mii_readreg(struct device *, int, int);
void dwge_mii_writereg(struct device *, int, int, int);
void dwge_mii_statchg(struct device *);
void dwge_lladdr_read(struct dwge_softc *, uint8_t *);
void dwge_lladdr_write(struct dwge_softc *);
void dwge_tick(void *);
void dwge_rxtick(void *);
int dwge_intr(void *);
void dwge_tx_proc(struct dwge_softc *);
void dwge_rx_proc(struct dwge_softc *);
void dwge_up(struct dwge_softc *);
void dwge_down(struct dwge_softc *);
void dwge_iff(struct dwge_softc *);
int dwge_encap(struct dwge_softc *, struct mbuf *, int *, int *);
void dwge_reset(struct dwge_softc *);
void dwge_stop_dma(struct dwge_softc *);
struct dwge_dmamem *
dwge_dmamem_alloc(struct dwge_softc *, bus_size_t, bus_size_t);
void dwge_dmamem_free(struct dwge_softc *, struct dwge_dmamem *);
struct mbuf *dwge_alloc_mbuf(struct dwge_softc *, bus_dmamap_t);
void dwge_fill_rx_ring(struct dwge_softc *);
#if NKSTAT > 0
int dwge_kstat_read(struct kstat *);
void dwge_kstat_attach(struct dwge_softc *);
#endif
int
dwge_match(struct device *parent, void *cfdata, void *aux)
{
struct fdt_attach_args *faa = aux;
return (OF_is_compatible(faa->fa_node, "allwinner,sun7i-a20-gmac") ||
OF_is_compatible(faa->fa_node, "amlogic,meson-axg-dwmac") ||
OF_is_compatible(faa->fa_node, "amlogic,meson-g12a-dwmac") ||
OF_is_compatible(faa->fa_node, "rockchip,rk3288-gmac") ||
OF_is_compatible(faa->fa_node, "rockchip,rk3308-mac") ||
OF_is_compatible(faa->fa_node, "rockchip,rk3328-gmac") ||
OF_is_compatible(faa->fa_node, "rockchip,rk3399-gmac") ||
OF_is_compatible(faa->fa_node, "snps,dwmac"));
}
void
dwge_attach(struct device *parent, struct device *self, void *aux)
{
struct dwge_softc *sc = (void *)self;
struct fdt_attach_args *faa = aux;
struct ifnet *ifp = &sc->sc_ac.ac_if;
uint32_t phy, phy_supply;
uint32_t axi_config;
uint32_t mode, pbl;
uint32_t version;
uint32_t feature;
int node;
sc->sc_node = faa->fa_node;
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("%s: cannot map registers\n", self->dv_xname);
return;
}
sc->sc_dmat = faa->fa_dmat;
phy = OF_getpropint(faa->fa_node, "phy", 0);
if (phy == 0)
phy = OF_getpropint(faa->fa_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;
pinctrl_byname(faa->fa_node, "default");
clock_set_assigned(faa->fa_node);
clock_enable(faa->fa_node, "stmmaceth");
reset_deassert(faa->fa_node, "stmmaceth");
if (OF_is_compatible(faa->fa_node, "rockchip,rk3288-gmac") ||
OF_is_compatible(faa->fa_node, "rockchip,rk3308-mac") ||
OF_is_compatible(faa->fa_node, "rockchip,rk3328-gmac") ||
OF_is_compatible(faa->fa_node, "rockchip,rk3399-gmac")) {
clock_enable(faa->fa_node, "mac_clk_rx");
clock_enable(faa->fa_node, "mac_clk_tx");
clock_enable(faa->fa_node, "aclk_mac");
clock_enable(faa->fa_node, "pclk_mac");
}
delay(5000);
version = dwge_read(sc, GMAC_VERSION);
printf(": rev 0x%02x", version & GMAC_VERSION_SNPS_MASK);
if ((version & GMAC_VERSION_SNPS_MASK) > 0x35) {
feature = dwge_read(sc, GMAC_HW_FEATURE);
if (feature & GMAC_HW_FEATURE_ENHDESSEL)
sc->sc_enh_desc = 1;
}
if (OF_is_compatible(faa->fa_node, "starfive,jh7100-gmac") ||
OF_is_compatible(faa->fa_node, "starfive,jh7100-dwmac"))
sc->sc_defrag = 1;
phy_supply = OF_getpropint(faa->fa_node, "phy-supply", 0);
if (phy_supply)
regulator_enable(phy_supply);
dwge_reset_phy(sc);
node = OF_getnodebyname(faa->fa_node, "fixed-link");
if (node) {
ifp->if_baudrate = IF_Mbps(OF_getpropint(node, "speed", 0));
switch (OF_getpropint(node, "speed", 0)) {
case 1000:
sc->sc_fixed_media = IFM_ETHER | IFM_1000_T;
break;
case 100:
sc->sc_fixed_media = IFM_ETHER | IFM_100_TX;
break;
default:
sc->sc_fixed_media = IFM_ETHER | IFM_AUTO;
break;
}
if (OF_getpropbool(node, "full-duplex")) {
ifp->if_link_state = LINK_STATE_FULL_DUPLEX;
sc->sc_fixed_media |= IFM_FDX;
} else {
ifp->if_link_state = LINK_STATE_UP;
}
}
sc->sc_clk = clock_get_frequency(faa->fa_node, "stmmaceth");
if (sc->sc_clk > 250000000)
sc->sc_clk = GMAC_GMII_ADDR_CR_DIV_124;
else if (sc->sc_clk > 150000000)
sc->sc_clk = GMAC_GMII_ADDR_CR_DIV_102;
else if (sc->sc_clk > 100000000)
sc->sc_clk = GMAC_GMII_ADDR_CR_DIV_62;
else if (sc->sc_clk > 60000000)
sc->sc_clk = GMAC_GMII_ADDR_CR_DIV_42;
else if (sc->sc_clk > 35000000)
sc->sc_clk = GMAC_GMII_ADDR_CR_DIV_26;
else
sc->sc_clk = GMAC_GMII_ADDR_CR_DIV_16;
if (OF_getprop(faa->fa_node, "local-mac-address",
&sc->sc_lladdr, ETHER_ADDR_LEN) != ETHER_ADDR_LEN)
dwge_lladdr_read(sc, sc->sc_lladdr);
printf(", address %s\n", ether_sprintf(sc->sc_lladdr));
timeout_set(&sc->sc_tick, dwge_tick, sc);
timeout_set(&sc->sc_rxto, dwge_rxtick, sc);
ifp->if_softc = sc;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_xflags = IFXF_MPSAFE;
ifp->if_ioctl = dwge_ioctl;
ifp->if_qstart = dwge_start;
ifp->if_watchdog = dwge_watchdog;
ifq_init_maxlen(&ifp->if_snd, DWGE_NTXDESC - 1);
bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
ifp->if_capabilities = IFCAP_VLAN_MTU;
sc->sc_mii.mii_ifp = ifp;
sc->sc_mii.mii_readreg = dwge_mii_readreg;
sc->sc_mii.mii_writereg = dwge_mii_writereg;
sc->sc_mii.mii_statchg = dwge_mii_statchg;
ifmedia_init(&sc->sc_media, 0, dwge_media_change, dwge_media_status);
if (OF_is_compatible(faa->fa_node, "allwinner,sun7i-a20-gmac"))
dwge_setup_allwinner(sc);
if (OF_is_compatible(faa->fa_node, "rockchip,rk3288-gmac") ||
OF_is_compatible(faa->fa_node, "rockchip,rk3308-mac") ||
OF_is_compatible(faa->fa_node, "rockchip,rk3328-gmac") ||
OF_is_compatible(faa->fa_node, "rockchip,rk3399-gmac"))
dwge_setup_rockchip(sc);
if (OF_getpropbool(faa->fa_node, "snps,force_thresh_dma_mode"))
sc->sc_force_thresh_dma_mode = 1;
dwge_reset(sc);
dwge_write(sc, GMAC_MAC_CONF, dwge_read(sc, GMAC_MAC_CONF) |
GMAC_MAC_CONF_JD | GMAC_MAC_CONF_BE | GMAC_MAC_CONF_DCRS);
mode = dwge_read(sc, GMAC_BUS_MODE);
mode |= GMAC_BUS_MODE_USP;
if (!OF_getpropbool(faa->fa_node, "snps,no-pbl-x8"))
mode |= GMAC_BUS_MODE_8XPBL;
mode &= ~(GMAC_BUS_MODE_RPBL_MASK | GMAC_BUS_MODE_PBL_MASK);
pbl = OF_getpropint(faa->fa_node, "snps,pbl", 8);
mode |= pbl << GMAC_BUS_MODE_RPBL_SHIFT;
mode |= pbl << GMAC_BUS_MODE_PBL_SHIFT;
if (OF_getpropbool(faa->fa_node, "snps,fixed-burst"))
mode |= GMAC_BUS_MODE_FB;
dwge_write(sc, GMAC_BUS_MODE, mode);
axi_config = OF_getpropint(faa->fa_node, "snps,axi-config", 0);
node = OF_getnodebyphandle(axi_config);
if (node) {
uint32_t blen[7] = { 0 };
uint32_t osr_lmt;
int i;
mode = dwge_read(sc, GMAC_AXI_BUS_MODE);
osr_lmt = OF_getpropint(node, "snps,wr_osr_lmt", 1);
mode &= ~GMAC_AXI_BUS_MODE_WR_OSR_LMT_MASK;
mode |= (osr_lmt << GMAC_AXI_BUS_MODE_WR_OSR_LMT_SHIFT);
osr_lmt = OF_getpropint(node, "snps,rd_osr_lmt", 1);
mode &= ~GMAC_AXI_BUS_MODE_RD_OSR_LMT_MASK;
mode |= (osr_lmt << GMAC_AXI_BUS_MODE_RD_OSR_LMT_SHIFT);
OF_getpropintarray(node, "snps,blen", blen, sizeof(blen));
for (i = 0; i < nitems(blen); i++) {
switch (blen[i]) {
case 256:
mode |= GMAC_AXI_BUS_MODE_BLEN_256;
break;
case 128:
mode |= GMAC_AXI_BUS_MODE_BLEN_128;
break;
case 64:
mode |= GMAC_AXI_BUS_MODE_BLEN_64;
break;
case 32:
mode |= GMAC_AXI_BUS_MODE_BLEN_32;
break;
case 16:
mode |= GMAC_AXI_BUS_MODE_BLEN_16;
break;
case 8:
mode |= GMAC_AXI_BUS_MODE_BLEN_8;
break;
case 4:
mode |= GMAC_AXI_BUS_MODE_BLEN_4;
break;
}
}
dwge_write(sc, GMAC_AXI_BUS_MODE, mode);
}
if (sc->sc_fixed_media == 0) {
mii_attach(self, &sc->sc_mii, 0xffffffff, sc->sc_phyloc,
(sc->sc_phyloc == MII_PHY_ANY) ? 0 : MII_OFFSET_ANY, 0);
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);
} else {
ifmedia_add(&sc->sc_media, sc->sc_fixed_media, 0, NULL);
ifmedia_set(&sc->sc_media, sc->sc_fixed_media);
sc->sc_mii.mii_statchg(self);
}
if_attach(ifp);
ether_ifattach(ifp);
#if NKSTAT > 0
dwge_kstat_attach(sc);
#endif
dwge_write(sc, GMAC_INT_ENA, 0);
dwge_write(sc, GMAC_INT_MASK,
GMAC_INT_MASK_LPIIM | GMAC_INT_MASK_PIM | GMAC_INT_MASK_RIM);
dwge_write(sc, GMAC_MMC_IPC_INT_MSK, 0xffffffff);
sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_NET | IPL_MPSAFE,
dwge_intr, sc, sc->sc_dev.dv_xname);
if (sc->sc_ih == NULL)
printf("%s: can't establish interrupt\n", sc->sc_dev.dv_xname);
sc->sc_ifd.if_node = faa->fa_node;
sc->sc_ifd.if_ifp = ifp;
if_register(&sc->sc_ifd);
}
void
dwge_reset_phy(struct dwge_softc *sc)
{
uint32_t *gpio;
uint32_t delays[3];
int active = 1;
int len;
len = OF_getproplen(sc->sc_node, "snps,reset-gpio");
if (len <= 0)
return;
gpio = malloc(len, M_TEMP, M_WAITOK);
OF_getpropintarray(sc->sc_node, "snps,reset-gpio", gpio, len);
if (OF_getpropbool(sc->sc_node, "snps-reset-active-low"))
active = 0;
delays[0] = delays[1] = delays[2] = 0;
OF_getpropintarray(sc->sc_node, "snps,reset-delays-us", delays,
sizeof(delays));
gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT);
gpio_controller_set_pin(gpio, !active);
delay(delays[0]);
gpio_controller_set_pin(gpio, active);
delay(delays[1]);
gpio_controller_set_pin(gpio, !active);
delay(delays[2]);
free(gpio, M_TEMP, len);
}
uint32_t
dwge_read(struct dwge_softc *sc, bus_addr_t addr)
{
return bus_space_read_4(sc->sc_iot, sc->sc_ioh, addr);
}
void
dwge_write(struct dwge_softc *sc, bus_addr_t addr, uint32_t data)
{
bus_space_write_4(sc->sc_iot, sc->sc_ioh, addr, data);
}
void
dwge_lladdr_read(struct dwge_softc *sc, uint8_t *lladdr)
{
uint32_t machi, maclo;
machi = dwge_read(sc, GMAC_MAC_ADDR0_HI);
maclo = dwge_read(sc, GMAC_MAC_ADDR0_LO);
lladdr[0] = (maclo >> 0) & 0xff;
lladdr[1] = (maclo >> 8) & 0xff;
lladdr[2] = (maclo >> 16) & 0xff;
lladdr[3] = (maclo >> 24) & 0xff;
lladdr[4] = (machi >> 0) & 0xff;
lladdr[5] = (machi >> 8) & 0xff;
}
void
dwge_lladdr_write(struct dwge_softc *sc)
{
dwge_write(sc, GMAC_MAC_ADDR0_HI,
sc->sc_lladdr[5] << 8 | sc->sc_lladdr[4] << 0);
dwge_write(sc, GMAC_MAC_ADDR0_LO,
sc->sc_lladdr[3] << 24 | sc->sc_lladdr[2] << 16 |
sc->sc_lladdr[1] << 8 | sc->sc_lladdr[0] << 0);
}
void
dwge_start(struct ifqueue *ifq)
{
struct ifnet *ifp = ifq->ifq_if;
struct dwge_softc *sc = ifp->if_softc;
struct mbuf *m;
int error, idx, left, used;
if (!(ifp->if_flags & IFF_RUNNING))
return;
if (ifq_is_oactive(&ifp->if_snd))
return;
if (ifq_empty(&ifp->if_snd))
return;
if (!sc->sc_link)
return;
idx = sc->sc_tx_prod;
left = sc->sc_tx_cons;
if (left <= idx)
left += DWGE_NTXDESC;
left -= idx;
used = 0;
for (;;) {
if (used + DWGE_NTXSEGS + 1 > left) {
ifq_set_oactive(ifq);
break;
}
m = ifq_dequeue(ifq);
if (m == NULL)
break;
error = dwge_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 (sc->sc_tx_prod != idx) {
sc->sc_tx_prod = idx;
ifp->if_timer = 5;
dwge_write(sc, GMAC_TX_POLL_DEMAND, 0xffffffff);
}
}
int
dwge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
{
struct dwge_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
dwge_up(sc);
} else {
if (ifp->if_flags & IFF_RUNNING)
dwge_down(sc);
}
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
if (sc->sc_fixed_media != 0)
error = ENOTTY;
else
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)
dwge_iff(sc);
error = 0;
}
splx(s);
return (error);
}
void
dwge_watchdog(struct ifnet *ifp)
{
printf("%s\n", __func__);
}
int
dwge_media_change(struct ifnet *ifp)
{
struct dwge_softc *sc = ifp->if_softc;
if (LIST_FIRST(&sc->sc_mii.mii_phys))
mii_mediachg(&sc->sc_mii);
return (0);
}
void
dwge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct dwge_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
dwge_mii_readreg(struct device *self, int phy, int reg)
{
struct dwge_softc *sc = (void *)self;
int n;
dwge_write(sc, GMAC_GMII_ADDR,
sc->sc_clk << GMAC_GMII_ADDR_CR_SHIFT |
phy << GMAC_GMII_ADDR_PA_SHIFT |
reg << GMAC_GMII_ADDR_GR_SHIFT |
GMAC_GMII_ADDR_GB);
for (n = 0; n < 1000; n++) {
if ((dwge_read(sc, GMAC_GMII_ADDR) & GMAC_GMII_ADDR_GB) == 0)
return dwge_read(sc, GMAC_GMII_DATA);
delay(10);
}
printf("%s: mii_read timeout\n", sc->sc_dev.dv_xname);
return (0);
}
void
dwge_mii_writereg(struct device *self, int phy, int reg, int val)
{
struct dwge_softc *sc = (void *)self;
int n;
dwge_write(sc, GMAC_GMII_DATA, val);
dwge_write(sc, GMAC_GMII_ADDR,
sc->sc_clk << GMAC_GMII_ADDR_CR_SHIFT |
phy << GMAC_GMII_ADDR_PA_SHIFT |
reg << GMAC_GMII_ADDR_GR_SHIFT |
GMAC_GMII_ADDR_GW | GMAC_GMII_ADDR_GB);
for (n = 0; n < 1000; n++) {
if ((dwge_read(sc, GMAC_GMII_ADDR) & GMAC_GMII_ADDR_GB) == 0)
return;
delay(10);
}
printf("%s: mii_write timeout\n", sc->sc_dev.dv_xname);
}
void
dwge_mii_statchg(struct device *self)
{
struct dwge_softc *sc = (void *)self;
uint32_t conf;
uint64_t media_active;
conf = dwge_read(sc, GMAC_MAC_CONF);
conf &= ~(GMAC_MAC_CONF_PS | GMAC_MAC_CONF_FES);
media_active = sc->sc_fixed_media;
if (media_active == 0)
media_active = sc->sc_mii.mii_media_active;
switch (IFM_SUBTYPE(media_active)) {
case IFM_1000_SX:
case IFM_1000_LX:
case IFM_1000_CX:
case IFM_1000_T:
sc->sc_link = 1;
break;
case IFM_100_TX:
conf |= GMAC_MAC_CONF_PS | GMAC_MAC_CONF_FES;
sc->sc_link = 1;
break;
case IFM_10_T:
conf |= GMAC_MAC_CONF_PS;
sc->sc_link = 1;
break;
default:
sc->sc_link = 0;
return;
}
if (sc->sc_link == 0)
return;
conf &= ~GMAC_MAC_CONF_DM;
if ((media_active & IFM_GMASK) == IFM_FDX)
conf |= GMAC_MAC_CONF_DM;
dwge_write(sc, GMAC_MAC_CONF, conf);
}
void
dwge_tick(void *arg)
{
struct dwge_softc *sc = arg;
int s;
s = splnet();
mii_tick(&sc->sc_mii);
splx(s);
timeout_add_sec(&sc->sc_tick, 1);
}
void
dwge_rxtick(void *arg)
{
struct dwge_softc *sc = arg;
uint32_t mode;
int s;
s = splnet();
mode = dwge_read(sc, GMAC_OP_MODE);
dwge_write(sc, GMAC_OP_MODE, mode & ~GMAC_OP_MODE_SR);
bus_dmamap_sync(sc->sc_dmat, DWGE_DMA_MAP(sc->sc_rxring),
0, DWGE_DMA_LEN(sc->sc_rxring),
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
dwge_write(sc, GMAC_RX_DESC_LIST_ADDR, 0);
sc->sc_rx_prod = sc->sc_rx_cons = 0;
dwge_fill_rx_ring(sc);
bus_dmamap_sync(sc->sc_dmat, DWGE_DMA_MAP(sc->sc_rxring),
0, DWGE_DMA_LEN(sc->sc_rxring),
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
dwge_write(sc, GMAC_RX_DESC_LIST_ADDR, DWGE_DMA_DVA(sc->sc_rxring));
dwge_write(sc, GMAC_OP_MODE, mode);
splx(s);
}
int
dwge_intr(void *arg)
{
struct dwge_softc *sc = arg;
uint32_t reg;
reg = dwge_read(sc, GMAC_STATUS);
dwge_write(sc, GMAC_STATUS, reg);
if (reg & GMAC_STATUS_RI)
dwge_rx_proc(sc);
if (reg & GMAC_STATUS_TI ||
reg & GMAC_STATUS_TU)
dwge_tx_proc(sc);
#if NKSTAT > 0
if (reg & GMAC_STATUS_MMC) {
mtx_enter(&sc->sc_kstat_mtx);
dwge_kstat_read(sc->sc_kstat);
mtx_leave(&sc->sc_kstat_mtx);
}
#endif
return (1);
}
void
dwge_tx_proc(struct dwge_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct dwge_desc *txd;
struct dwge_buf *txb;
int idx, txfree;
bus_dmamap_sync(sc->sc_dmat, DWGE_DMA_MAP(sc->sc_txring), 0,
DWGE_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 < DWGE_NTXDESC);
txd = &sc->sc_txdesc[idx];
if (txd->sd_status & TDES0_OWN)
break;
txb = &sc->sc_txbuf[idx];
if (txb->tb_m) {
bus_dmamap_sync(sc->sc_dmat, txb->tb_map, 0,
txb->tb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat, txb->tb_map);
m_freem(txb->tb_m);
txb->tb_m = NULL;
}
txfree++;
if (sc->sc_tx_cons == (DWGE_NTXDESC - 1))
sc->sc_tx_cons = 0;
else
sc->sc_tx_cons++;
txd->sd_status = sc->sc_enh_desc ? ETDES0_TCH : 0;
}
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
dwge_rx_proc(struct dwge_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct dwge_desc *rxd;
struct dwge_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, DWGE_DMA_MAP(sc->sc_rxring), 0,
DWGE_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 < DWGE_NRXDESC);
rxd = &sc->sc_rxdesc[idx];
if (rxd->sd_status & RDES0_OWN)
break;
len = (rxd->sd_status >> RDES0_FL_SHIFT) & RDES0_FL_MASK;
rxb = &sc->sc_rxbuf[idx];
KASSERT(rxb->tb_m);
bus_dmamap_sync(sc->sc_dmat, rxb->tb_map, 0,
len, BUS_DMASYNC_POSTREAD);
bus_dmamap_unload(sc->sc_dmat, rxb->tb_map);
m = rxb->tb_m;
rxb->tb_m = NULL;
if (rxd->sd_status & RDES0_ES) {
ifp->if_ierrors++;
m_freem(m);
} else {
len -= ETHER_CRC_LEN;
KASSERT(len > 0);
m->m_pkthdr.len = m->m_len = len;
ml_enqueue(&ml, m);
}
put++;
if (sc->sc_rx_cons == (DWGE_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);
dwge_fill_rx_ring(sc);
bus_dmamap_sync(sc->sc_dmat, DWGE_DMA_MAP(sc->sc_rxring), 0,
DWGE_DMA_LEN(sc->sc_rxring),
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
}
void
dwge_up(struct dwge_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct dwge_buf *txb, *rxb;
uint32_t mode;
int i;
sc->sc_txring = dwge_dmamem_alloc(sc,
DWGE_NTXDESC * sizeof(struct dwge_desc), 8);
sc->sc_txdesc = DWGE_DMA_KVA(sc->sc_txring);
sc->sc_txbuf = malloc(sizeof(struct dwge_buf) * DWGE_NTXDESC,
M_DEVBUF, M_WAITOK);
for (i = 0; i < DWGE_NTXDESC; i++) {
txb = &sc->sc_txbuf[i];
bus_dmamap_create(sc->sc_dmat, MCLBYTES, DWGE_NTXSEGS,
MCLBYTES, 0, BUS_DMA_WAITOK, &txb->tb_map);
txb->tb_m = NULL;
sc->sc_txdesc[i].sd_next =
DWGE_DMA_DVA(sc->sc_txring) +
((i+1) % DWGE_NTXDESC) * sizeof(struct dwge_desc);
if (sc->sc_enh_desc)
sc->sc_txdesc[i].sd_status = ETDES0_TCH;
else
sc->sc_txdesc[i].sd_len = TDES1_TCH;
}
bus_dmamap_sync(sc->sc_dmat, DWGE_DMA_MAP(sc->sc_txring),
0, DWGE_DMA_LEN(sc->sc_txring), BUS_DMASYNC_PREWRITE);
sc->sc_tx_prod = sc->sc_tx_cons = 0;
dwge_write(sc, GMAC_TX_DESC_LIST_ADDR, DWGE_DMA_DVA(sc->sc_txring));
sc->sc_rxring = dwge_dmamem_alloc(sc,
DWGE_NRXDESC * sizeof(struct dwge_desc), 8);
sc->sc_rxdesc = DWGE_DMA_KVA(sc->sc_rxring);
sc->sc_rxbuf = malloc(sizeof(struct dwge_buf) * DWGE_NRXDESC,
M_DEVBUF, M_WAITOK);
for (i = 0; i < DWGE_NRXDESC; i++) {
rxb = &sc->sc_rxbuf[i];
bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
MCLBYTES, 0, BUS_DMA_WAITOK, &rxb->tb_map);
rxb->tb_m = NULL;
sc->sc_rxdesc[i].sd_next =
DWGE_DMA_DVA(sc->sc_rxring) +
((i+1) % DWGE_NRXDESC) * sizeof(struct dwge_desc);
sc->sc_rxdesc[i].sd_len =
sc->sc_enh_desc ? ERDES1_RCH : RDES1_RCH;
}
if_rxr_init(&sc->sc_rx_ring, 2, DWGE_NRXDESC);
sc->sc_rx_prod = sc->sc_rx_cons = 0;
dwge_fill_rx_ring(sc);
bus_dmamap_sync(sc->sc_dmat, DWGE_DMA_MAP(sc->sc_rxring),
0, DWGE_DMA_LEN(sc->sc_rxring),
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
dwge_write(sc, GMAC_RX_DESC_LIST_ADDR, DWGE_DMA_DVA(sc->sc_rxring));
dwge_lladdr_write(sc);
if (LIST_FIRST(&sc->sc_mii.mii_phys))
mii_mediachg(&sc->sc_mii);
dwge_iff(sc);
ifp->if_flags |= IFF_RUNNING;
ifq_clr_oactive(&ifp->if_snd);
dwge_write(sc, GMAC_INT_ENA, GMAC_INT_ENA_NIE |
GMAC_INT_ENA_RIE | GMAC_INT_ENA_TIE | GMAC_INT_ENA_TUE);
mode = dwge_read(sc, GMAC_OP_MODE);
if (sc->sc_force_thresh_dma_mode) {
mode &= ~(GMAC_OP_MODE_TSF | GMAC_OP_MODE_TTC_MASK);
mode |= GMAC_OP_MODE_TTC_128;
mode &= ~(GMAC_OP_MODE_RSF | GMAC_OP_MODE_RTC_MASK);
mode |= GMAC_OP_MODE_RTC_128;
} else {
mode |= GMAC_OP_MODE_TSF | GMAC_OP_MODE_OSF;
mode |= GMAC_OP_MODE_RSF;
}
dwge_write(sc, GMAC_OP_MODE, mode | GMAC_OP_MODE_ST | GMAC_OP_MODE_SR);
dwge_write(sc, GMAC_MAC_CONF, dwge_read(sc, GMAC_MAC_CONF) |
GMAC_MAC_CONF_TE | GMAC_MAC_CONF_RE);
if (sc->sc_fixed_media == 0)
timeout_add_sec(&sc->sc_tick, 1);
}
void
dwge_down(struct dwge_softc *sc)
{
struct ifnet *ifp = &sc->sc_ac.ac_if;
struct dwge_buf *txb, *rxb;
uint32_t dmactrl;
int i;
timeout_del(&sc->sc_rxto);
if (sc->sc_fixed_media == 0)
timeout_del(&sc->sc_tick);
ifp->if_flags &= ~IFF_RUNNING;
ifq_clr_oactive(&ifp->if_snd);
ifp->if_timer = 0;
dwge_stop_dma(sc);
dwge_write(sc, GMAC_MAC_CONF, dwge_read(sc,
GMAC_MAC_CONF) & ~(GMAC_MAC_CONF_TE | GMAC_MAC_CONF_RE));
dmactrl = dwge_read(sc, GMAC_OP_MODE);
dmactrl &= ~(GMAC_OP_MODE_ST | GMAC_OP_MODE_SR);
dwge_write(sc, GMAC_OP_MODE, dmactrl);
dwge_write(sc, GMAC_INT_ENA, 0);
intr_barrier(sc->sc_ih);
ifq_barrier(&ifp->if_snd);
for (i = 0; i < DWGE_NTXDESC; i++) {
txb = &sc->sc_txbuf[i];
if (txb->tb_m) {
bus_dmamap_sync(sc->sc_dmat, txb->tb_map, 0,
txb->tb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat, txb->tb_map);
m_freem(txb->tb_m);
}
bus_dmamap_destroy(sc->sc_dmat, txb->tb_map);
}
dwge_dmamem_free(sc, sc->sc_txring);
free(sc->sc_txbuf, M_DEVBUF, 0);
for (i = 0; i < DWGE_NRXDESC; i++) {
rxb = &sc->sc_rxbuf[i];
if (rxb->tb_m) {
bus_dmamap_sync(sc->sc_dmat, rxb->tb_map, 0,
rxb->tb_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
bus_dmamap_unload(sc->sc_dmat, rxb->tb_map);
m_freem(rxb->tb_m);
}
bus_dmamap_destroy(sc->sc_dmat, rxb->tb_map);
}
dwge_dmamem_free(sc, sc->sc_rxring);
free(sc->sc_rxbuf, M_DEVBUF, 0);
}
static uint32_t
bitrev32(uint32_t x)
{
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
return (x >> 16) | (x << 16);
}
void
dwge_iff(struct dwge_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, hash[2], hashbit, hashreg;
uint32_t reg;
reg = 0;
ifp->if_flags &= ~IFF_ALLMULTI;
bzero(hash, sizeof(hash));
if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0) {
ifp->if_flags |= IFF_ALLMULTI;
reg |= GMAC_MAC_FRM_FILT_PM;
if (ifp->if_flags & IFF_PROMISC)
reg |= GMAC_MAC_FRM_FILT_PR;
} else {
reg |= GMAC_MAC_FRM_FILT_HMC;
ETHER_FIRST_MULTI(step, ac, enm);
while (enm != NULL) {
crc = ether_crc32_le(enm->enm_addrlo,
ETHER_ADDR_LEN) & 0x7f;
crc = bitrev32(~crc) >> 26;
hashreg = (crc >> 5);
hashbit = (crc & 0x1f);
hash[hashreg] |= (1 << hashbit);
ETHER_NEXT_MULTI(step, enm);
}
}
dwge_lladdr_write(sc);
dwge_write(sc, GMAC_HASH_TAB_HI, hash[1]);
dwge_write(sc, GMAC_HASH_TAB_LO, hash[0]);
dwge_write(sc, GMAC_MAC_FRM_FILT, reg);
}
int
dwge_encap(struct dwge_softc *sc, struct mbuf *m, int *idx, int *used)
{
struct dwge_desc *txd, *txd_start;
bus_dmamap_t map;
int cur, frag, i;
cur = frag = *idx;
map = sc->sc_txbuf[cur].tb_map;
if (sc->sc_defrag) {
if (m_defrag(m, M_DONTWAIT))
return (ENOBUFS);
}
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_addr = map->dm_segs[i].ds_addr;
if (sc->sc_enh_desc) {
txd->sd_status = ETDES0_TCH;
txd->sd_len = map->dm_segs[i].ds_len;
if (i == 0)
txd->sd_status |= ETDES0_FS;
if (i == (map->dm_nsegs - 1))
txd->sd_status |= ETDES0_LS | ETDES0_IC;
} else {
txd->sd_status = 0;
txd->sd_len = map->dm_segs[i].ds_len | TDES1_TCH;
if (i == 0)
txd->sd_len |= TDES1_FS;
if (i == (map->dm_nsegs - 1))
txd->sd_len |= TDES1_LS | TDES1_IC;
}
if (i != 0)
txd->sd_status |= TDES0_OWN;
bus_dmamap_sync(sc->sc_dmat, DWGE_DMA_MAP(sc->sc_txring),
frag * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREWRITE);
cur = frag;
if (frag == (DWGE_NTXDESC - 1)) {
txd = &sc->sc_txdesc[0];
frag = 0;
} else {
txd++;
frag++;
}
KASSERT(frag != sc->sc_tx_cons);
}
txd_start->sd_status |= TDES0_OWN;
bus_dmamap_sync(sc->sc_dmat, DWGE_DMA_MAP(sc->sc_txring),
*idx * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREWRITE);
KASSERT(sc->sc_txbuf[cur].tb_m == NULL);
sc->sc_txbuf[*idx].tb_map = sc->sc_txbuf[cur].tb_map;
sc->sc_txbuf[cur].tb_map = map;
sc->sc_txbuf[cur].tb_m = m;
*idx = frag;
*used += map->dm_nsegs;
return (0);
}
void
dwge_reset(struct dwge_softc *sc)
{
int n;
dwge_stop_dma(sc);
dwge_write(sc, GMAC_BUS_MODE, dwge_read(sc, GMAC_BUS_MODE) |
GMAC_BUS_MODE_SWR);
for (n = 0; n < 30000; n++) {
if ((dwge_read(sc, GMAC_BUS_MODE) &
GMAC_BUS_MODE_SWR) == 0)
return;
delay(10);
}
printf("%s: reset timeout\n", sc->sc_dev.dv_xname);
}
void
dwge_stop_dma(struct dwge_softc *sc)
{
uint32_t dmactrl;
dmactrl = dwge_read(sc, GMAC_OP_MODE);
dmactrl &= ~GMAC_OP_MODE_ST;
dmactrl |= GMAC_OP_MODE_FTF;
dwge_write(sc, GMAC_OP_MODE, dmactrl);
}
struct dwge_dmamem *
dwge_dmamem_alloc(struct dwge_softc *sc, bus_size_t size, bus_size_t align)
{
struct dwge_dmamem *tdm;
int nsegs;
tdm = malloc(sizeof(*tdm), M_DEVBUF, M_WAITOK | M_ZERO);
tdm->tdm_size = size;
if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &tdm->tdm_map) != 0)
goto tdmfree;
if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &tdm->tdm_seg, 1,
&nsegs, BUS_DMA_WAITOK) != 0)
goto destroy;
if (bus_dmamem_map(sc->sc_dmat, &tdm->tdm_seg, nsegs, size,
&tdm->tdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0)
goto free;
if (bus_dmamap_load(sc->sc_dmat, tdm->tdm_map, tdm->tdm_kva, size,
NULL, BUS_DMA_WAITOK) != 0)
goto unmap;
bzero(tdm->tdm_kva, size);
return (tdm);
unmap:
bus_dmamem_unmap(sc->sc_dmat, tdm->tdm_kva, size);
free:
bus_dmamem_free(sc->sc_dmat, &tdm->tdm_seg, 1);
destroy:
bus_dmamap_destroy(sc->sc_dmat, tdm->tdm_map);
tdmfree:
free(tdm, M_DEVBUF, 0);
return (NULL);
}
void
dwge_dmamem_free(struct dwge_softc *sc, struct dwge_dmamem *tdm)
{
bus_dmamem_unmap(sc->sc_dmat, tdm->tdm_kva, tdm->tdm_size);
bus_dmamem_free(sc->sc_dmat, &tdm->tdm_seg, 1);
bus_dmamap_destroy(sc->sc_dmat, tdm->tdm_map);
free(tdm, M_DEVBUF, 0);
}
struct mbuf *
dwge_alloc_mbuf(struct dwge_softc *sc, bus_dmamap_t map)
{
struct mbuf *m = NULL;
m = MCLGETL(NULL, M_DONTWAIT, MCLBYTES);
if (!m)
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
dwge_fill_rx_ring(struct dwge_softc *sc)
{
struct dwge_desc *rxd;
struct dwge_buf *rxb;
u_int slots;
for (slots = if_rxr_get(&sc->sc_rx_ring, DWGE_NRXDESC);
slots > 0; slots--) {
rxb = &sc->sc_rxbuf[sc->sc_rx_prod];
rxb->tb_m = dwge_alloc_mbuf(sc, rxb->tb_map);
if (rxb->tb_m == NULL)
break;
rxd = &sc->sc_rxdesc[sc->sc_rx_prod];
rxd->sd_len = rxb->tb_map->dm_segs[0].ds_len;
rxd->sd_len |= sc->sc_enh_desc ? ERDES1_RCH : RDES1_RCH;
rxd->sd_addr = rxb->tb_map->dm_segs[0].ds_addr;
rxd->sd_status = RDES0_OWN;
if (sc->sc_rx_prod == (DWGE_NRXDESC - 1))
sc->sc_rx_prod = 0;
else
sc->sc_rx_prod++;
}
if_rxr_put(&sc->sc_rx_ring, slots);
if (if_rxr_inuse(&sc->sc_rx_ring) == 0)
timeout_add(&sc->sc_rxto, 1);
}
void
dwge_setup_allwinner(struct dwge_softc *sc)
{
char phy_mode[8];
uint32_t freq;
OF_getprop(sc->sc_node, "phy-mode", phy_mode, sizeof(phy_mode));
if (strcmp(phy_mode, "mii") == 0)
freq = 25000000;
else
freq = 125000000;
clock_set_frequency(sc->sc_node, "allwinner_gmac_tx", freq);
}
#define RK3308_GRF_MAC_CON0 0x04a0
#define RK3308_MAC_SPEED_100M ((0x1 << 0) << 16 | (0x1 << 0))
#define RK3308_MAC_SPEED_10M ((0x1 << 0) << 16 | (0x0 << 0))
#define RK3308_INTF_SEL_RMII ((0x1 << 4) << 16 | (0x1 << 4))
#define RK3288_GRF_SOC_CON1 0x0248
#define RK3288_GMAC_PHY_INTF_SEL_RGMII ((0x7 << 6) << 16 | (0x1 << 6))
#define RK3288_GMAC_PHY_INTF_SEL_RMII ((0x7 << 6) << 16 | (0x4 << 6))
#define RK3288_RMII_MODE_RMII ((1 << 14) << 16 | (1 << 14))
#define RK3288_RMII_MODE_MII ((1 << 14) << 16 | (0 << 14))
#define RK3288_GMAC_CLK_SEL_125 ((0x3 << 12) << 16 | (0x0 << 12))
#define RK3288_GMAC_CLK_SEL_25 ((0x3 << 12) << 16 | (0x3 << 12))
#define RK3288_GMAC_CLK_SEL_2_5 ((0x3 << 12) << 16 | (0x2 << 12))
#define RK3288_GRF_SOC_CON3 0x0250
#define RK3288_GMAC_RXCLK_DLY_ENA ((1 << 15) << 16 | (1 << 15))
#define RK3288_GMAC_CLK_RX_DL_CFG(val) ((0x7f << 7) << 16 | ((val) << 7))
#define RK3288_GMAC_TXCLK_DLY_ENA ((1 << 14) << 16 | (1 << 14))
#define RK3288_GMAC_CLK_TX_DL_CFG(val) ((0x7f << 0) << 16 | ((val) << 0))
#define RK3328_GRF_MAC_CON0 0x0900
#define RK3328_GMAC_CLK_RX_DL_CFG(val) ((0x7f << 7) << 16 | ((val) << 7))
#define RK3328_GMAC_CLK_TX_DL_CFG(val) ((0x7f << 0) << 16 | ((val) << 0))
#define RK3328_GRF_MAC_CON1 0x0904
#define RK3328_GMAC_PHY_INTF_SEL_RGMII ((0x7 << 4) << 16 | (0x1 << 4))
#define RK3328_GMAC_PHY_INTF_SEL_RMII ((0x7 << 4) << 16 | (0x4 << 4))
#define RK3328_RMII_MODE_RMII ((1 << 9) << 16 | (1 << 9))
#define RK3328_RMII_MODE_MII ((1 << 9) << 16 | (0 << 9))
#define RK3328_GMAC_CLK_SEL_125 ((0x3 << 11) << 16 | (0x0 << 11))
#define RK3328_GMAC_CLK_SEL_25 ((0x3 << 11) << 16 | (0x3 << 11))
#define RK3328_GMAC_CLK_SEL_2_5 ((0x3 << 11) << 16 | (0x2 << 11))
#define RK3328_GMAC_RXCLK_DLY_ENA ((1 << 1) << 16 | (1 << 1))
#define RK3328_GMAC_TXCLK_DLY_ENA ((1 << 0) << 16 | (1 << 0))
#define RK3399_GRF_SOC_CON5 0xc214
#define RK3399_GMAC_PHY_INTF_SEL_RGMII ((0x7 << 9) << 16 | (0x1 << 9))
#define RK3399_GMAC_PHY_INTF_SEL_RMII ((0x7 << 9) << 16 | (0x4 << 9))
#define RK3399_RMII_MODE_RMII ((1 << 6) << 16 | (1 << 6))
#define RK3399_RMII_MODE_MII ((1 << 6) << 16 | (0 << 6))
#define RK3399_GMAC_CLK_SEL_125 ((0x3 << 4) << 16 | (0x0 << 4))
#define RK3399_GMAC_CLK_SEL_25 ((0x3 << 4) << 16 | (0x3 << 4))
#define RK3399_GMAC_CLK_SEL_2_5 ((0x3 << 4) << 16 | (0x2 << 4))
#define RK3399_GRF_SOC_CON6 0xc218
#define RK3399_GMAC_RXCLK_DLY_ENA ((1 << 15) << 16 | (1 << 15))
#define RK3399_GMAC_CLK_RX_DL_CFG(val) ((0x7f << 8) << 16 | ((val) << 8))
#define RK3399_GMAC_TXCLK_DLY_ENA ((1 << 7) << 16 | (1 << 7))
#define RK3399_GMAC_CLK_TX_DL_CFG(val) ((0x7f << 0) << 16 | ((val) << 0))
void dwge_mii_statchg_rockchip(struct device *);
void
dwge_setup_rockchip(struct dwge_softc *sc)
{
struct regmap *rm;
uint32_t grf;
int tx_delay, rx_delay;
char clock_mode[8];
grf = OF_getpropint(sc->sc_node, "rockchip,grf", 0);
rm = regmap_byphandle(grf);
if (rm == NULL)
return;
tx_delay = OF_getpropint(sc->sc_node, "tx_delay", 0x30);
rx_delay = OF_getpropint(sc->sc_node, "rx_delay", 0x10);
if (OF_is_compatible(sc->sc_node, "rockchip,rk3288-gmac")) {
regmap_write_4(rm, RK3288_GRF_SOC_CON1,
RK3288_GMAC_PHY_INTF_SEL_RGMII | RK3288_RMII_MODE_MII);
regmap_write_4(rm, RK3288_GRF_SOC_CON3,
RK3288_GMAC_TXCLK_DLY_ENA | RK3288_GMAC_RXCLK_DLY_ENA |
RK3288_GMAC_CLK_TX_DL_CFG(tx_delay) |
RK3288_GMAC_CLK_RX_DL_CFG(rx_delay));
sc->sc_clk_sel = RK3288_GRF_SOC_CON1;
sc->sc_clk_sel_2_5 = RK3288_GMAC_CLK_SEL_2_5;
sc->sc_clk_sel_25 = RK3288_GMAC_CLK_SEL_25;
sc->sc_clk_sel_125 = RK3288_GMAC_CLK_SEL_125;
} else if (OF_is_compatible(sc->sc_node, "rockchip,rk3308-mac")) {
regmap_write_4(rm, RK3308_GRF_MAC_CON0,
RK3308_INTF_SEL_RMII | RK3308_MAC_SPEED_100M);
OF_getprop(sc->sc_node, "clock_in_out", clock_mode,
sizeof(clock_mode));
if (strcmp(clock_mode, "output") == 0) {
clock_set_frequency(sc->sc_node, "stmmaceth",
50000000);
sc->sc_clk = GMAC_GMII_ADDR_CR_DIV_26;
}
sc->sc_clk_sel = RK3308_GRF_MAC_CON0;
sc->sc_clk_sel_2_5 = RK3308_MAC_SPEED_10M;
sc->sc_clk_sel_25 = RK3308_MAC_SPEED_100M;
} else if (OF_is_compatible(sc->sc_node, "rockchip,rk3328-gmac")) {
regmap_write_4(rm, RK3328_GRF_MAC_CON1,
RK3328_GMAC_PHY_INTF_SEL_RGMII | RK3328_RMII_MODE_MII);
regmap_write_4(rm, RK3328_GRF_MAC_CON0,
RK3328_GMAC_CLK_TX_DL_CFG(tx_delay) |
RK3328_GMAC_CLK_RX_DL_CFG(rx_delay));
regmap_write_4(rm, RK3328_GRF_MAC_CON1,
RK3328_GMAC_TXCLK_DLY_ENA | RK3328_GMAC_RXCLK_DLY_ENA);
sc->sc_clk_sel = RK3328_GRF_MAC_CON1;
sc->sc_clk_sel_2_5 = RK3328_GMAC_CLK_SEL_2_5;
sc->sc_clk_sel_25 = RK3328_GMAC_CLK_SEL_25;
sc->sc_clk_sel_125 = RK3328_GMAC_CLK_SEL_125;
} else {
regmap_write_4(rm, RK3399_GRF_SOC_CON5,
RK3399_GMAC_PHY_INTF_SEL_RGMII | RK3399_RMII_MODE_MII);
regmap_write_4(rm, RK3399_GRF_SOC_CON6,
RK3399_GMAC_TXCLK_DLY_ENA | RK3399_GMAC_RXCLK_DLY_ENA |
RK3399_GMAC_CLK_TX_DL_CFG(tx_delay) |
RK3399_GMAC_CLK_RX_DL_CFG(rx_delay));
sc->sc_clk_sel = RK3399_GRF_SOC_CON5;
sc->sc_clk_sel_2_5 = RK3399_GMAC_CLK_SEL_2_5;
sc->sc_clk_sel_25 = RK3399_GMAC_CLK_SEL_25;
sc->sc_clk_sel_125 = RK3399_GMAC_CLK_SEL_125;
}
sc->sc_mii.mii_statchg = dwge_mii_statchg_rockchip;
}
void
dwge_mii_statchg_rockchip(struct device *self)
{
struct dwge_softc *sc = (void *)self;
struct regmap *rm;
uint32_t grf;
uint32_t gmac_clk_sel = 0;
uint64_t media_active;
dwge_mii_statchg(self);
grf = OF_getpropint(sc->sc_node, "rockchip,grf", 0);
rm = regmap_byphandle(grf);
if (rm == NULL)
return;
media_active = sc->sc_fixed_media;
if (media_active == 0)
media_active = sc->sc_mii.mii_media_active;
switch (IFM_SUBTYPE(media_active)) {
case IFM_10_T:
gmac_clk_sel = sc->sc_clk_sel_2_5;
break;
case IFM_100_TX:
gmac_clk_sel = sc->sc_clk_sel_25;
break;
case IFM_1000_T:
gmac_clk_sel = sc->sc_clk_sel_125;
break;
}
regmap_write_4(rm, sc->sc_clk_sel, gmac_clk_sel);
}
#if NKSTAT > 0
struct dwge_counter {
const char *c_name;
enum kstat_kv_unit c_unit;
uint32_t c_reg;
};
const struct dwge_counter dwge_counters[] = {
{ "tx octets total", KSTAT_KV_U_BYTES, GMAC_MMC_TXOCTETCNT_GB },
{ "tx frames total", KSTAT_KV_U_PACKETS, GMAC_MMC_TXFRMCNT_GB },
{ "tx underflow", KSTAT_KV_U_PACKETS, GMAC_MMC_TXUNDFLWERR },
{ "tx carrier err", KSTAT_KV_U_PACKETS, GMAC_MMC_TXCARERR },
{ "tx good octets", KSTAT_KV_U_BYTES, GMAC_MMC_TXOCTETCNT_G },
{ "tx good frames", KSTAT_KV_U_PACKETS, GMAC_MMC_TXFRMCNT_G },
{ "rx frames total", KSTAT_KV_U_PACKETS, GMAC_MMC_RXFRMCNT_GB },
{ "rx octets total", KSTAT_KV_U_BYTES, GMAC_MMC_RXOCTETCNT_GB },
{ "rx good octets", KSTAT_KV_U_BYTES, GMAC_MMC_RXOCTETCNT_G },
{ "rx good mcast", KSTAT_KV_U_PACKETS, GMAC_MMC_RXMCFRMCNT_G },
{ "rx crc errors", KSTAT_KV_U_PACKETS, GMAC_MMC_RXCRCERR },
{ "rx len errors", KSTAT_KV_U_PACKETS, GMAC_MMC_RXLENERR },
{ "rx fifo err", KSTAT_KV_U_PACKETS, GMAC_MMC_RXFIFOOVRFLW },
};
void
dwge_kstat_attach(struct dwge_softc *sc)
{
struct kstat *ks;
struct kstat_kv *kvs;
int i;
mtx_init(&sc->sc_kstat_mtx, IPL_NET);
dwge_write(sc, GMAC_MAC_MMC_CTRL, GMAC_MAC_MMC_CTRL_ROR |
GMAC_MAC_MMC_CTRL_CR);
ks = kstat_create(DEVNAME(sc), 0, "dwge-stats", 0,
KSTAT_T_KV, 0);
if (ks == NULL)
return;
kvs = mallocarray(nitems(dwge_counters), sizeof(*kvs), M_DEVBUF,
M_WAITOK | M_ZERO);
for (i = 0; i < nitems(dwge_counters); i++) {
kstat_kv_unit_init(&kvs[i], dwge_counters[i].c_name,
KSTAT_KV_T_COUNTER64, dwge_counters[i].c_unit);
}
kstat_set_mutex(ks, &sc->sc_kstat_mtx);
ks->ks_softc = sc;
ks->ks_data = kvs;
ks->ks_datalen = nitems(dwge_counters) * sizeof(*kvs);
ks->ks_read = dwge_kstat_read;
sc->sc_kstat = ks;
kstat_install(ks);
}
int
dwge_kstat_read(struct kstat *ks)
{
struct kstat_kv *kvs = ks->ks_data;
struct dwge_softc *sc = ks->ks_softc;
int i;
for (i = 0; i < nitems(dwge_counters); i++)
kstat_kv_u64(&kvs[i]) += dwge_read(sc, dwge_counters[i].c_reg);
getnanouptime(&ks->ks_updated);
return 0;
}
#endif