#define ETH_SWITCH_HEADER_LEN 2
-static int ag71xx_tx_packets(struct ag71xx *ag, bool flush);
+static int ag71xx_tx_packets(struct ag71xx *ag, bool flush, int budget);
static inline unsigned int ag71xx_max_frame_len(unsigned int mtu)
{
{
struct ag71xx_ring *ring = &ag->tx_ring;
int ring_size = BIT(ring->order);
- int ring_mask = ring_size - 1;
+ int ring_mask = BIT(ring->order) - 1;
int i;
for (i = 0; i < ring_size; i++) {
return -ENOMEM;
tx->descs_cpu = dma_alloc_coherent(&ag->pdev->dev, ring_size * AG71XX_DESC_SIZE,
- &tx->descs_dma, GFP_ATOMIC);
+ &tx->descs_dma, GFP_KERNEL);
if (!tx->descs_cpu) {
kfree(tx->buf);
tx->buf = NULL;
return -ENOMEM;
}
- rx->buf = &tx->buf[BIT(tx->order)];
+ rx->buf = &tx->buf[tx_size];
rx->descs_cpu = ((void *)tx->descs_cpu) + tx_size * AG71XX_DESC_SIZE;
rx->descs_dma = tx->descs_dma + tx_size * AG71XX_DESC_SIZE;
mii_reg = ag71xx_rr(ag, AG71XX_REG_MII_CFG);
rx_ds = ag71xx_rr(ag, AG71XX_REG_RX_DESC);
- ag71xx_tx_packets(ag, true);
+ ag71xx_tx_packets(ag, true, 0);
reset_control_assert(ag->mac_reset);
udelay(10);
udelay(100);
}
+static void ag71xx_bit_set(void __iomem *reg, u32 bit)
+{
+ u32 val;
+
+ val = __raw_readl(reg) | bit;
+ __raw_writel(val, reg);
+ __raw_readl(reg);
+}
+
+static void ag71xx_bit_clear(void __iomem *reg, u32 bit)
+{
+ u32 val;
+
+ val = __raw_readl(reg) & ~bit;
+ __raw_writel(val, reg);
+ __raw_readl(reg);
+}
+
+static void ag71xx_sgmii_serdes_init_qca956x(struct device_node *np)
+{
+ struct device_node *np_dev;
+ void __iomem *gmac_base;
+ u32 serdes_cal;
+ u32 t;
+
+ np = of_get_child_by_name(np, "gmac-config");
+ if (!np)
+ return;
+
+ if (of_property_read_u32(np, "serdes-cal", &serdes_cal))
+ /* By default, use middle value for resistor calibration */
+ serdes_cal = 0x7;
+
+ np_dev = of_parse_phandle(np, "device", 0);
+ if (!np_dev)
+ goto out;
+
+ gmac_base = of_iomap(np_dev, 0);
+ if (!gmac_base) {
+ pr_err("%pOF: can't map GMAC registers\n", np_dev);
+ goto err_iomap;
+ }
+
+ t = __raw_readl(gmac_base + QCA956X_GMAC_REG_SGMII_CONFIG);
+ t &= ~(QCA956X_SGMII_CONFIG_MODE_CTRL_MASK << QCA956X_SGMII_CONFIG_MODE_CTRL_SHIFT);
+ t |= QCA956X_SGMII_CONFIG_MODE_CTRL_SGMII_MAC;
+ __raw_writel(t, gmac_base + QCA956X_GMAC_REG_SGMII_CONFIG);
+
+ pr_debug("%pOF: fixup SERDES calibration to value %i\n",
+ np_dev, serdes_cal);
+ t = __raw_readl(gmac_base + QCA956X_GMAC_REG_SGMII_SERDES);
+ t &= ~(QCA956X_SGMII_SERDES_RES_CALIBRATION_MASK
+ << QCA956X_SGMII_SERDES_RES_CALIBRATION_SHIFT);
+ t |= (serdes_cal & QCA956X_SGMII_SERDES_RES_CALIBRATION_MASK)
+ << QCA956X_SGMII_SERDES_RES_CALIBRATION_SHIFT;
+ __raw_writel(t, gmac_base + QCA956X_GMAC_REG_SGMII_SERDES);
+
+ ath79_pll_wr(QCA956X_PLL_ETH_SGMII_SERDES_REG,
+ QCA956X_PLL_ETH_SGMII_SERDES_LOCK_DETECT
+ | QCA956X_PLL_ETH_SGMII_SERDES_EN_PLL);
+
+ t = __raw_readl(gmac_base + QCA956X_GMAC_REG_SGMII_SERDES);
+
+ /* missing in QCA u-boot code, clear before setting */
+ t &= ~(QCA956X_SGMII_SERDES_CDR_BW_MASK
+ << QCA956X_SGMII_SERDES_CDR_BW_SHIFT |
+ QCA956X_SGMII_SERDES_TX_DR_CTRL_MASK
+ << QCA956X_SGMII_SERDES_TX_DR_CTRL_SHIFT |
+ QCA956X_SGMII_SERDES_VCO_REG_MASK
+ << QCA956X_SGMII_SERDES_VCO_REG_SHIFT);
+
+ t |= (3 << QCA956X_SGMII_SERDES_CDR_BW_SHIFT) |
+ (1 << QCA956X_SGMII_SERDES_TX_DR_CTRL_SHIFT) |
+ QCA956X_SGMII_SERDES_PLL_BW |
+ QCA956X_SGMII_SERDES_EN_SIGNAL_DETECT |
+ QCA956X_SGMII_SERDES_FIBER_SDO |
+ (3 << QCA956X_SGMII_SERDES_VCO_REG_SHIFT);
+
+ __raw_writel(t, gmac_base + QCA956X_GMAC_REG_SGMII_SERDES);
+
+ ath79_device_reset_clear(QCA956X_RESET_SGMII_ANALOG);
+ ath79_device_reset_clear(QCA956X_RESET_SGMII);
+
+ while (!(__raw_readl(gmac_base + QCA956X_GMAC_REG_SGMII_SERDES)
+ & QCA956X_SGMII_SERDES_LOCK_DETECT_STATUS))
+ ;
+
+ iounmap(gmac_base);
+err_iomap:
+ of_node_put(np_dev);
+out:
+ of_node_put(np);
+}
+
+static void ag71xx_sgmii_init_qca955x(struct device_node *np)
+{
+ struct device_node *np_dev;
+ void __iomem *gmac_base;
+ u32 mr_an_status;
+ u32 sgmii_status;
+ u8 tries = 0;
+ int err = 0;
+
+ np = of_get_child_by_name(np, "gmac-config");
+ if (!np)
+ return;
+
+ np_dev = of_parse_phandle(np, "device", 0);
+ if (!np_dev)
+ goto out;
+
+ gmac_base = of_iomap(np_dev, 0);
+ if (!gmac_base) {
+ pr_err("%pOF: can't map GMAC registers\n", np_dev);
+ err = -ENOMEM;
+ goto err_iomap;
+ }
+
+ mr_an_status = __raw_readl(gmac_base + QCA955X_GMAC_REG_MR_AN_STATUS);
+ if (!(mr_an_status & QCA955X_MR_AN_STATUS_AN_ABILITY))
+ goto sgmii_out;
+
+ /* SGMII reset sequence */
+ __raw_writel(QCA955X_SGMII_RESET_RX_CLK_N_RESET,
+ gmac_base + QCA955X_GMAC_REG_SGMII_RESET);
+ __raw_readl(gmac_base + QCA955X_GMAC_REG_SGMII_RESET);
+ udelay(10);
+
+ ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET,
+ QCA955X_SGMII_RESET_HW_RX_125M_N);
+ udelay(10);
+
+ ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET,
+ QCA955X_SGMII_RESET_RX_125M_N);
+ udelay(10);
+
+ ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET,
+ QCA955X_SGMII_RESET_TX_125M_N);
+ udelay(10);
+
+ ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET,
+ QCA955X_SGMII_RESET_RX_CLK_N);
+ udelay(10);
+
+ ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET,
+ QCA955X_SGMII_RESET_TX_CLK_N);
+ udelay(10);
+
+ /*
+ * The following is what QCA has to say about what happens here:
+ *
+ * Across resets SGMII link status goes to weird state.
+ * If SGMII_DEBUG register reads other than 0x1f or 0x10,
+ * we are for sure in a bad state.
+ *
+ * Issue a PHY reset in MR_AN_CONTROL to keep going.
+ */
+ do {
+ ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_MR_AN_CONTROL,
+ QCA955X_MR_AN_CONTROL_PHY_RESET |
+ QCA955X_MR_AN_CONTROL_AN_ENABLE);
+ udelay(200);
+ ag71xx_bit_clear(gmac_base + QCA955X_GMAC_REG_MR_AN_CONTROL,
+ QCA955X_MR_AN_CONTROL_PHY_RESET);
+ mdelay(300);
+ sgmii_status = __raw_readl(gmac_base + QCA955X_GMAC_REG_SGMII_DEBUG) &
+ QCA955X_SGMII_DEBUG_TX_STATE_MASK;
+
+ if (tries++ >= 20) {
+ pr_err("ag71xx: max retries for SGMII fixup exceeded\n");
+ break;
+ }
+ } while (!(sgmii_status == 0xf || sgmii_status == 0x10));
+
+sgmii_out:
+ iounmap(gmac_base);
+err_iomap:
+ of_node_put(np_dev);
+out:
+ of_node_put(np);
+}
+
+static void ag71xx_mux_select_sgmii_qca956x(struct device_node *np)
+{
+ struct device_node *np_dev;
+ void __iomem *gmac_base;
+ u32 t;
+
+ np = of_get_child_by_name(np, "gmac-config");
+ if (!np)
+ return;
+
+ np_dev = of_parse_phandle(np, "device", 0);
+ if (!np_dev)
+ goto out;
+
+ gmac_base = of_iomap(np_dev, 0);
+ if (!gmac_base) {
+ pr_err("%pOF: can't map GMAC registers\n", np_dev);
+ goto err_iomap;
+ }
+
+ t = __raw_readl(gmac_base + QCA956X_GMAC_REG_ETH_CFG);
+ t |= QCA956X_ETH_CFG_GE0_SGMII;
+ __raw_writel(t, gmac_base + QCA956X_GMAC_REG_ETH_CFG);
+
+ iounmap(gmac_base);
+err_iomap:
+ of_node_put(np_dev);
+out:
+ of_node_put(np);
+}
+
static void ath79_mii_ctrl_set_if(struct ag71xx *ag, unsigned int mii_if)
{
u32 t;
mii_if = AR71XX_MII0_CTRL_IF_GMII;
break;
case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
mii_if = AR71XX_MII0_CTRL_IF_RGMII;
break;
case PHY_INTERFACE_MODE_RMII:
mii_if = AR71XX_MII1_CTRL_IF_RMII;
break;
case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
mii_if = AR71XX_MII1_CTRL_IF_RGMII;
break;
default:
of_device_is_compatible(np, "qca,qca9550-eth") ||
of_device_is_compatible(np, "qca,qca9560-eth")) {
ath79_set_pllval(ag);
+ if (of_property_read_bool(np, "qca955x-sgmii-fixup"))
+ ag71xx_sgmii_init_qca955x(np);
}
}
* The wr, rr functions cannot be used since this hidden register
* is outside of the normal ag71xx register block.
*/
- void __iomem *dam = ioremap_nocache(0xb90001bc, 0x4);
+ void __iomem *dam = ioremap(0xb90001bc, 0x4);
if (dam) {
__raw_writel(__raw_readl(dam) & ~BIT(27), dam);
(void)__raw_readl(dam);
static void ag71xx_hw_disable(struct ag71xx *ag)
{
- unsigned long flags;
-
- spin_lock_irqsave(&ag->lock, flags);
-
netif_stop_queue(ag->dev);
ag71xx_hw_stop(ag);
napi_disable(&ag->napi);
del_timer_sync(&ag->oom_timer);
- spin_unlock_irqrestore(&ag->lock, flags);
-
ag71xx_rings_cleanup(ag);
}
static int ag71xx_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct ag71xx *ag = netdev_priv(dev);
- int ret;
- switch (cmd) {
- case SIOCETHTOOL:
- if (ag->phy_dev == NULL)
- break;
-
- spin_lock_irq(&ag->lock);
- ret = phy_ethtool_ioctl(ag->phy_dev, (void *) ifr->ifr_data);
- spin_unlock_irq(&ag->lock);
- return ret;
+ switch (cmd) {
case SIOCSIFHWADDR:
if (copy_from_user
(dev->dev_addr, ifr->ifr_data, sizeof(dev->dev_addr)))
return -EOPNOTSUPP;
}
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0))
-static void ag71xx_oom_timer_handler(unsigned long data)
-{
- struct net_device *dev = (struct net_device *) data;
- struct ag71xx *ag = netdev_priv(dev);
-#else
static void ag71xx_oom_timer_handler(struct timer_list *t)
{
struct ag71xx *ag = from_timer(ag, t, oom_timer);
-#endif
napi_schedule(&ag->napi);
}
-static void ag71xx_tx_timeout(struct net_device *dev)
+static void ag71xx_tx_timeout(struct net_device *dev, unsigned int txqueue)
{
struct ag71xx *ag = netdev_priv(dev);
return false;
}
-static int ag71xx_tx_packets(struct ag71xx *ag, bool flush)
+static int ag71xx_tx_packets(struct ag71xx *ag, bool flush, int budget)
{
struct ag71xx_ring *ring = &ag->tx_ring;
bool dma_stuck = false;
if (!skb)
continue;
- dev_kfree_skb_any(skb);
+ napi_consume_skb(skb, budget);
ring->buf[i].skb = NULL;
bytes_compl += ring->buf[i].len;
unsigned int offset = ag->rx_buf_offset;
int ring_mask = BIT(ring->order) - 1;
int ring_size = BIT(ring->order);
- struct sk_buff_head queue;
+ struct list_head rx_list;
+ struct sk_buff *next;
struct sk_buff *skb;
int done = 0;
DBG("%s: rx packets, limit=%d, curr=%u, dirty=%u\n",
dev->name, limit, ring->curr, ring->dirty);
-
- skb_queue_head_init(&queue);
+ INIT_LIST_HEAD(&rx_list);
while (done < limit) {
unsigned int i = ring->curr & ring_mask;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pktlen;
- skb = build_skb(ring->buf[i].rx_buf, ag71xx_buffer_size(ag));
+ skb = napi_build_skb(ring->buf[i].rx_buf, ag71xx_buffer_size(ag));
if (!skb) {
skb_free_frag(ring->buf[i].rx_buf);
goto next;
} else {
skb->dev = dev;
skb->ip_summed = CHECKSUM_NONE;
- __skb_queue_tail(&queue, skb);
+ list_add_tail(&skb->list, &rx_list);
}
next:
ag71xx_ring_rx_refill(ag);
- while ((skb = __skb_dequeue(&queue)) != NULL) {
+ list_for_each_entry_safe(skb, next, &rx_list, list)
skb->protocol = eth_type_trans(skb, dev);
- netif_receive_skb(skb);
- }
+ netif_receive_skb_list(&rx_list);
DBG("%s: rx finish, curr=%u, dirty=%u, done=%d\n",
dev->name, ring->curr, ring->dirty, done);
int tx_done;
int rx_done;
- tx_done = ag71xx_tx_packets(ag, false);
+ tx_done = ag71xx_tx_packets(ag, false, limit);
DBG("%s: processing RX ring\n", dev->name);
rx_done = ag71xx_rx_packets(ag, limit);
struct net_device *dev;
struct resource *res;
struct ag71xx *ag;
- const void *mac_addr;
u32 max_frame_len;
int tx_size, err;
if (!res)
return -EINVAL;
+ if (of_property_read_bool(np, "qca956x-serdes-fixup")) {
+ ag71xx_sgmii_serdes_init_qca956x(np);
+ ag71xx_sgmii_init_qca955x(np);
+ }
+
err = ag71xx_setup_gmac(np);
if (err)
return err;
ag->pllregmap = NULL;
}
- ag->mac_base = devm_ioremap_nocache(&pdev->dev, res->start,
- res->end - res->start + 1);
+ ag->mac_base = devm_ioremap(&pdev->dev, res->start,
+ res->end - res->start + 1);
if (!ag->mac_base)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res) {
- ag->mii_base = devm_ioremap_nocache(&pdev->dev, res->start,
+ ag->mii_base = devm_ioremap(&pdev->dev, res->start,
res->end - res->start + 1);
if (!ag->mii_base)
return -ENOMEM;
}
+ /* ensure that HW is in manual polling mode before interrupts are
+ * activated. Otherwise ag71xx_interrupt might call napi_schedule
+ * before it is initialized by netif_napi_add.
+ */
+ ag71xx_int_disable(ag, AG71XX_INT_POLL);
+
dev->irq = platform_get_irq(pdev, 0);
err = devm_request_irq(&pdev->dev, dev->irq, ag71xx_interrupt,
0x0, dev_name(&pdev->dev), dev);
INIT_DELAYED_WORK(&ag->restart_work, ag71xx_restart_work_func);
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0))
- init_timer(&ag->oom_timer);
- ag->oom_timer.data = (unsigned long) dev;
- ag->oom_timer.function = ag71xx_oom_timer_handler;
-#else
timer_setup(&ag->oom_timer, ag71xx_oom_timer_handler, 0);
-#endif
tx_size = AG71XX_TX_RING_SIZE_DEFAULT;
ag->rx_ring.order = ag71xx_ring_size_order(AG71XX_RX_RING_SIZE_DEFAULT);
dev->min_mtu = 68;
dev->max_mtu = max_frame_len - ag71xx_max_frame_len(0);
- if (of_device_is_compatible(np, "qca,ar7240-eth"))
+ if (of_device_is_compatible(np, "qca,ar7240-eth") ||
+ of_device_is_compatible(np, "qca,ar7241-eth") ||
+ of_device_is_compatible(np, "qca,ar7242-eth") ||
+ of_device_is_compatible(np, "qca,ar9330-eth") ||
+ of_device_is_compatible(np, "qca,ar9340-eth") ||
+ of_device_is_compatible(np, "qca,qca9530-eth") ||
+ of_device_is_compatible(np, "qca,qca9550-eth") ||
+ of_device_is_compatible(np, "qca,qca9560-eth"))
ag->tx_hang_workaround = 1;
ag->rx_buf_offset = NET_SKB_PAD;
ag->stop_desc->ctrl = 0;
ag->stop_desc->next = (u32) ag->stop_desc_dma;
- mac_addr = of_get_mac_address(np);
- if (mac_addr)
- memcpy(dev->dev_addr, mac_addr, ETH_ALEN);
- if (!mac_addr || !is_valid_ether_addr(dev->dev_addr)) {
+ of_get_mac_address(np, dev->dev_addr);
+ if (!is_valid_ether_addr(dev->dev_addr)) {
dev_err(&pdev->dev, "invalid MAC address, using random address\n");
eth_random_addr(dev->dev_addr);
}
- ag->phy_if_mode = of_get_phy_mode(np);
- if (ag->phy_if_mode < 0) {
+ err = of_get_phy_mode(np, &ag->phy_if_mode);
+ if (err < 0) {
dev_err(&pdev->dev, "missing phy-mode property in DT\n");
return ag->phy_if_mode;
}
+ if (of_device_is_compatible(np, "qca,qca9560-eth") &&
+ ag->phy_if_mode == PHY_INTERFACE_MODE_SGMII)
+ ag71xx_mux_select_sgmii_qca956x(np);
+
if (of_property_read_u32(np, "qca,mac-idx", &ag->mac_idx))
ag->mac_idx = -1;
if (ag->mii_base)