#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/qcom_ess_edma/qcom_ess_edma_var.h>
#include <dev/qcom_ess_edma/qcom_ess_edma_reg.h>
#include <dev/qcom_ess_edma/qcom_ess_edma_hw.h>
#include <dev/qcom_ess_edma/qcom_ess_edma_debug.h>
int
qcom_ess_edma_hw_reset(struct qcom_ess_edma_softc *sc)
{
EDMA_LOCK_ASSERT(sc);
device_printf(sc->sc_dev, "%s: called, TODO!\n", __func__);
return (0);
}
int
qcom_ess_edma_hw_get_tx_intr_moderation(struct qcom_ess_edma_softc *sc,
uint32_t *usec)
{
uint32_t reg;
EDMA_LOCK_ASSERT(sc);
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_IRQ_MODRT_TIMER_INIT);
reg = reg >> EDMA_IRQ_MODRT_TX_TIMER_SHIFT;
reg &= EDMA_IRQ_MODRT_TIMER_MASK;
*usec = reg * 2;
return (0);
}
int
qcom_ess_edma_hw_set_tx_intr_moderation(struct qcom_ess_edma_softc *sc,
uint32_t usec)
{
uint32_t reg;
usec = usec / 2;
EDMA_LOCK_ASSERT(sc);
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_IRQ_MODRT_TIMER_INIT);
reg &= ~(EDMA_IRQ_MODRT_TIMER_MASK << EDMA_IRQ_MODRT_TX_TIMER_SHIFT);
reg |= (usec & EDMA_IRQ_MODRT_TIMER_MASK)
<< EDMA_IRQ_MODRT_TX_TIMER_SHIFT;
EDMA_REG_WRITE(sc, EDMA_REG_IRQ_MODRT_TIMER_INIT, reg);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_set_rx_intr_moderation(struct qcom_ess_edma_softc *sc,
uint32_t usec)
{
uint32_t reg;
EDMA_LOCK_ASSERT(sc);
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_IRQ_MODRT_TIMER_INIT);
reg &= ~(EDMA_IRQ_MODRT_TIMER_MASK << EDMA_IRQ_MODRT_RX_TIMER_SHIFT);
reg |= (usec & EDMA_IRQ_MODRT_TIMER_MASK)
<< EDMA_IRQ_MODRT_RX_TIMER_SHIFT;
EDMA_REG_WRITE(sc, EDMA_REG_IRQ_MODRT_TIMER_INIT, reg);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_intr_disable(struct qcom_ess_edma_softc *sc)
{
int i;
for (i = 0; i < QCOM_ESS_EDMA_NUM_TX_IRQS; i++) {
EDMA_REG_WRITE(sc, EDMA_REG_TX_INT_MASK_Q(i), 0);
}
for (i = 0; i < QCOM_ESS_EDMA_NUM_RX_IRQS; i++) {
EDMA_REG_WRITE(sc, EDMA_REG_RX_INT_MASK_Q(i), 0);
}
EDMA_REG_WRITE(sc, EDMA_REG_MISC_IMR, 0);
EDMA_REG_WRITE(sc, EDMA_REG_WOL_IMR, 0);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_intr_rx_intr_set_enable(struct qcom_ess_edma_softc *sc,
int rxq, bool state)
{
EDMA_REG_WRITE(sc, EDMA_REG_RX_INT_MASK_Q(rxq), state ? 1 : 0);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_intr_tx_intr_set_enable(struct qcom_ess_edma_softc *sc,
int txq, bool state)
{
EDMA_REG_WRITE(sc, EDMA_REG_TX_INT_MASK_Q(txq), state ? 1 : 0);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_intr_enable(struct qcom_ess_edma_softc *sc)
{
int i;
EDMA_REG_WRITE(sc, EDMA_REG_TX_ISR, 0xffff);
for (i = 0; i < QCOM_ESS_EDMA_NUM_TX_IRQS; i++) {
EDMA_REG_WRITE(sc, EDMA_REG_TX_INT_MASK_Q(i),
sc->sc_config.tx_intr_mask);
}
EDMA_REG_WRITE(sc, EDMA_REG_RX_ISR, 0xff);
for (i = 0; i < QCOM_ESS_EDMA_NUM_RX_IRQS; i++) {
EDMA_REG_WRITE(sc, EDMA_REG_RX_INT_MASK_Q(i),
sc->sc_config.rx_intr_mask);
}
EDMA_REG_WRITE(sc, EDMA_REG_MISC_IMR, 0);
EDMA_REG_WRITE(sc, EDMA_REG_WOL_IMR, 0);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_intr_status_clear(struct qcom_ess_edma_softc *sc)
{
EDMA_REG_WRITE(sc, EDMA_REG_RX_ISR, 0xff);
EDMA_REG_WRITE(sc, EDMA_REG_TX_ISR, 0xffff);
EDMA_REG_WRITE(sc, EDMA_REG_MISC_ISR, 0x1fff);
EDMA_REG_WRITE(sc, EDMA_REG_WOL_ISR, 0x1);
return (0);
}
int
qcom_ess_edma_hw_intr_rx_ack(struct qcom_ess_edma_softc *sc, int rx_queue)
{
EDMA_RING_LOCK_ASSERT(&sc->sc_rx_ring[rx_queue]);
EDMA_REG_WRITE(sc, EDMA_REG_RX_ISR, (1U << rx_queue));
(void) EDMA_REG_READ(sc, EDMA_REG_RX_ISR);
return (0);
}
int
qcom_ess_edma_hw_intr_tx_ack(struct qcom_ess_edma_softc *sc, int tx_queue)
{
EDMA_RING_LOCK_ASSERT(&sc->sc_tx_ring[tx_queue]);
EDMA_REG_WRITE(sc, EDMA_REG_TX_ISR, (1U << tx_queue));
(void) EDMA_REG_READ(sc, EDMA_REG_TX_ISR);
return (0);
}
int
qcom_ess_edma_hw_configure_rss_table(struct qcom_ess_edma_softc *sc)
{
int i;
for (i = 0; i < EDMA_NUM_IDT; i++) {
EDMA_REG_WRITE(sc, EDMA_REG_RSS_IDT(i), EDMA_RSS_IDT_VALUE);
}
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_configure_load_balance_table(struct qcom_ess_edma_softc *sc)
{
EDMA_REG_WRITE(sc, EDMA_REG_LB_RING, EDMA_LB_REG_VALUE);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_configure_tx_virtual_queue(struct qcom_ess_edma_softc *sc)
{
EDMA_REG_WRITE(sc, EDMA_REG_VQ_CTRL0, EDMA_VQ_REG_VALUE);
EDMA_REG_WRITE(sc, EDMA_REG_VQ_CTRL1, EDMA_VQ_REG_VALUE);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_configure_default_axi_transaction_size(
struct qcom_ess_edma_softc *sc)
{
EDMA_REG_WRITE(sc, EDMA_REG_AXIW_CTRL_MAXWRSIZE,
EDMA_AXIW_MAXWRSIZE_VALUE);
return (0);
}
int
qcom_ess_edma_hw_stop_txrx_queues(struct qcom_ess_edma_softc *sc)
{
uint32_t reg;
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_RXQ_CTRL);
reg &= ~EDMA_RXQ_CTRL_EN;
EDMA_REG_WRITE(sc, EDMA_REG_RXQ_CTRL, reg);
EDMA_REG_BARRIER_WRITE(sc);
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_TXQ_CTRL);
reg &= ~EDMA_TXQ_CTRL_TXQ_EN;
EDMA_REG_WRITE(sc, EDMA_REG_TXQ_CTRL, reg);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_stop(struct qcom_ess_edma_softc *sc)
{
int ret;
EDMA_LOCK_ASSERT(sc);
ret = qcom_ess_edma_hw_intr_disable(sc);
if (ret != 0) {
QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_INTERRUPT,
"%s: hw_intr_disable failed (%d)\n",
__func__,
ret);
}
ret = qcom_ess_edma_hw_intr_status_clear(sc);
if (ret != 0) {
QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_INTERRUPT,
"%s: hw_intr_status_clear failed (%d)\n",
__func__,
ret);
}
ret = qcom_ess_edma_hw_stop_txrx_queues(sc);
if (ret != 0) {
QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_INTERRUPT,
"%s: hw_stop_txrx_queues failed (%d)\n",
__func__,
ret);
}
return (0);
}
int
qcom_ess_edma_hw_rfd_prod_index_update(struct qcom_ess_edma_softc *sc,
int queue, int idx)
{
uint32_t reg;
EDMA_RING_LOCK_ASSERT(&sc->sc_rx_ring[queue]);
QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_RX_RING_MGMT,
"%s: called; q=%d idx=0x%x\n",
__func__, queue, idx);
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_RFD_IDX_Q(queue));
QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_RX_RING_MGMT,
"%s: q=%d reg was 0x%08x\n", __func__, queue, reg);
reg &= ~EDMA_RFD_PROD_IDX_BITS;
reg |= idx;
EDMA_REG_WRITE(sc, EDMA_REG_RFD_IDX_Q(queue), reg);
QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_RX_RING_MGMT,
"%s: q=%d reg now 0x%08x\n", __func__, queue, reg);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_rfd_get_cons_index(struct qcom_ess_edma_softc *sc, int queue)
{
uint32_t reg;
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_RFD_IDX_Q(queue));
return (reg >> EDMA_RFD_CONS_IDX_SHIFT) & EDMA_RFD_CONS_IDX_MASK;
}
int
qcom_ess_edma_hw_rfd_sw_cons_index_update(struct qcom_ess_edma_softc *sc,
int queue, int idx)
{
EDMA_RING_LOCK_ASSERT(&sc->sc_rx_ring[queue]);
EDMA_REG_WRITE(sc, EDMA_REG_RX_SW_CONS_IDX_Q(queue), idx);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_setup(struct qcom_ess_edma_softc *sc)
{
uint32_t reg;
EDMA_LOCK_ASSERT(sc);
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_INTR_CTRL);
reg &= ~(1 << EDMA_INTR_SW_IDX_W_TYP_SHIFT);
reg |= sc->sc_state.intr_sw_idx_w << EDMA_INTR_SW_IDX_W_TYP_SHIFT;
EDMA_REG_WRITE(sc, EDMA_REG_INTR_CTRL, reg);
EDMA_REG_WRITE(sc, EDMA_REG_WOL_CTRL, 0);
reg = (EDMA_TX_IMT << EDMA_IRQ_MODRT_TX_TIMER_SHIFT);
reg |= (EDMA_RX_IMT << EDMA_IRQ_MODRT_RX_TIMER_SHIFT);
EDMA_REG_WRITE(sc, EDMA_REG_IRQ_MODRT_TIMER_INIT, reg);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_setup_tx(struct qcom_ess_edma_softc *sc)
{
uint32_t reg;
EDMA_LOCK_ASSERT(sc);
reg = (EDMA_TPD_BURST << EDMA_TXQ_NUM_TPD_BURST_SHIFT);
reg |= EDMA_TXQ_CTRL_TPD_BURST_EN;
reg |= (EDMA_TXF_BURST << EDMA_TXQ_TXF_BURST_NUM_SHIFT);
EDMA_REG_WRITE(sc, EDMA_REG_TXQ_CTRL, reg);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_setup_rx(struct qcom_ess_edma_softc *sc)
{
uint32_t reg;
EDMA_LOCK_ASSERT(sc);
EDMA_REG_WRITE(sc, EDMA_REG_RSS_TYPE, sc->sc_config.rss_type);
reg = (EDMA_RFD_BURST << EDMA_RXQ_RFD_BURST_NUM_SHIFT);
reg |= (EDMA_RFD_THR << EDMA_RXQ_RFD_PF_THRESH_SHIFT);
reg |= (EDMA_RFD_LTHR << EDMA_RXQ_RFD_LOW_THRESH_SHIFT);
EDMA_REG_WRITE(sc, EDMA_REG_RX_DESC1, reg);
reg = EDMA_FIFO_THRESH_128_BYTE;
reg |= EDMA_RXQ_CTRL_RMV_VLAN;
EDMA_REG_WRITE(sc, EDMA_REG_RXQ_CTRL, reg);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_setup_txrx_desc_rings(struct qcom_ess_edma_softc *sc)
{
uint32_t reg, i, idx;
int len;
EDMA_LOCK_ASSERT(sc);
for (i = 0; i < QCOM_ESS_EDMA_NUM_TX_RINGS; i++) {
QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_TX_RING_MGMT,
"TXQ[%d]: ring paddr=0x%08lx\n",
i, sc->sc_tx_ring[i].hw_desc_paddr);
EDMA_REG_WRITE(sc, EDMA_REG_TPD_BASE_ADDR_Q(i),
sc->sc_tx_ring[i].hw_desc_paddr);
reg = EDMA_REG_READ(sc, EDMA_REG_TPD_IDX_Q(i));
idx = (reg >> EDMA_TPD_CONS_IDX_SHIFT) & 0xffff;
sc->sc_tx_ring[i].next_to_fill = idx;
sc->sc_tx_ring[i].next_to_clean = idx;
reg &= ~(EDMA_TPD_PROD_IDX_MASK << EDMA_TPD_PROD_IDX_SHIFT);
reg |= idx;
EDMA_REG_WRITE(sc, EDMA_REG_TPD_IDX_Q(i), reg);
EDMA_REG_WRITE(sc, EDMA_REG_TX_SW_CONS_IDX_Q(i), idx);
EDMA_REG_WRITE(sc, EDMA_REG_TPD_RING_SIZE,
sc->sc_config.tx_ring_count & EDMA_TPD_RING_SIZE_MASK);
}
for (i = 0; i < QCOM_ESS_EDMA_NUM_RX_RINGS; i++) {
QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_RX_RING_MGMT,
"RXQ[%d]: ring paddr=0x%08lx\n",
i, sc->sc_rx_ring[i].hw_desc_paddr);
EDMA_REG_WRITE(sc, EDMA_REG_RFD_BASE_ADDR_Q(i),
sc->sc_rx_ring[i].hw_desc_paddr);
}
EDMA_REG_BARRIER_WRITE(sc);
len = sc->sc_config.rx_buf_size;
if (sc->sc_config.rx_buf_ether_align)
len -= ETHER_ALIGN;
reg = (len & EDMA_RX_BUF_SIZE_MASK)
<< EDMA_RX_BUF_SIZE_SHIFT;
reg |= (sc->sc_config.rx_ring_count & EDMA_RFD_RING_SIZE_MASK)
<< EDMA_RFD_RING_SIZE_SHIFT;
EDMA_REG_WRITE(sc, EDMA_REG_RX_DESC0, reg);
EDMA_REG_WRITE(sc, EDMA_REG_TXF_WATER_MARK, 0);
EDMA_REG_BARRIER_WRITE(sc);
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_TX_SRAM_PART);
reg |= 1 << EDMA_LOAD_PTR_SHIFT;
EDMA_REG_WRITE(sc, EDMA_REG_TX_SRAM_PART, reg);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_tx_enable(struct qcom_ess_edma_softc *sc)
{
uint32_t reg;
EDMA_LOCK_ASSERT(sc);
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_TXQ_CTRL);
reg |= EDMA_TXQ_CTRL_TXQ_EN;
EDMA_REG_WRITE(sc, EDMA_REG_TXQ_CTRL, reg);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_rx_enable(struct qcom_ess_edma_softc *sc)
{
EDMA_LOCK_ASSERT(sc);
uint32_t reg;
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_RXQ_CTRL);
reg |= EDMA_RXQ_CTRL_EN;
EDMA_REG_WRITE(sc, EDMA_REG_RXQ_CTRL, reg);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_tx_read_tpd_cons_idx(struct qcom_ess_edma_softc *sc,
int queue_id, uint16_t *idx)
{
uint32_t reg;
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_TPD_IDX_Q(queue_id));
*idx = (reg >> EDMA_TPD_CONS_IDX_SHIFT) & EDMA_TPD_CONS_IDX_MASK;
return (0);
}
int
qcom_ess_edma_hw_tx_update_tpd_prod_idx(struct qcom_ess_edma_softc *sc,
int queue_id, uint16_t idx)
{
uint32_t reg;
EDMA_REG_BARRIER_READ(sc);
reg = EDMA_REG_READ(sc, EDMA_REG_TPD_IDX_Q(queue_id));
reg &= ~EDMA_TPD_PROD_IDX_BITS;
reg |= (idx & EDMA_TPD_PROD_IDX_MASK) << EDMA_TPD_PROD_IDX_SHIFT;
EDMA_REG_WRITE(sc, EDMA_REG_TPD_IDX_Q(queue_id), reg);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}
int
qcom_ess_edma_hw_tx_update_cons_idx(struct qcom_ess_edma_softc *sc,
int queue_id, uint16_t idx)
{
EDMA_REG_WRITE(sc, EDMA_REG_TX_SW_CONS_IDX_Q(queue_id), idx);
EDMA_REG_BARRIER_WRITE(sc);
return (0);
}