162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Renesas Ethernet Switch device driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2022 Renesas Electronics Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/etherdevice.h> 1162306a36Sopenharmony_ci#include <linux/iopoll.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/net_tstamp.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_mdio.h> 1762306a36Sopenharmony_ci#include <linux/of_net.h> 1862306a36Sopenharmony_ci#include <linux/phy/phy.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2162306a36Sopenharmony_ci#include <linux/rtnetlink.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/spinlock.h> 2462306a36Sopenharmony_ci#include <linux/sys_soc.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "rswitch.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int rswitch_reg_wait(void __iomem *addr, u32 offs, u32 mask, u32 expected) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci u32 val; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci return readl_poll_timeout_atomic(addr + offs, val, (val & mask) == expected, 3362306a36Sopenharmony_ci 1, RSWITCH_TIMEOUT_US); 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic void rswitch_modify(void __iomem *addr, enum rswitch_reg reg, u32 clear, u32 set) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci iowrite32((ioread32(addr + reg) & ~clear) | set, addr + reg); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Common Agent block (COMA) */ 4262306a36Sopenharmony_cistatic void rswitch_reset(struct rswitch_private *priv) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci iowrite32(RRC_RR, priv->addr + RRC); 4562306a36Sopenharmony_ci iowrite32(RRC_RR_CLR, priv->addr + RRC); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void rswitch_clock_enable(struct rswitch_private *priv) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci iowrite32(RCEC_ACE_DEFAULT | RCEC_RCE, priv->addr + RCEC); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void rswitch_clock_disable(struct rswitch_private *priv) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci iowrite32(RCDC_RCD, priv->addr + RCDC); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic bool rswitch_agent_clock_is_enabled(void __iomem *coma_addr, int port) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci u32 val = ioread32(coma_addr + RCEC); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (val & RCEC_RCE) 6362306a36Sopenharmony_ci return (val & BIT(port)) ? true : false; 6462306a36Sopenharmony_ci else 6562306a36Sopenharmony_ci return false; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void rswitch_agent_clock_ctrl(void __iomem *coma_addr, int port, int enable) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci u32 val; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (enable) { 7362306a36Sopenharmony_ci val = ioread32(coma_addr + RCEC); 7462306a36Sopenharmony_ci iowrite32(val | RCEC_RCE | BIT(port), coma_addr + RCEC); 7562306a36Sopenharmony_ci } else { 7662306a36Sopenharmony_ci val = ioread32(coma_addr + RCDC); 7762306a36Sopenharmony_ci iowrite32(val | BIT(port), coma_addr + RCDC); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int rswitch_bpool_config(struct rswitch_private *priv) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci u32 val; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci val = ioread32(priv->addr + CABPIRM); 8662306a36Sopenharmony_ci if (val & CABPIRM_BPR) 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci iowrite32(CABPIRM_BPIOG, priv->addr + CABPIRM); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return rswitch_reg_wait(priv->addr, CABPIRM, CABPIRM_BPR, CABPIRM_BPR); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void rswitch_coma_init(struct rswitch_private *priv) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci iowrite32(CABPPFLC_INIT_VALUE, priv->addr + CABPPFLC0); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* R-Switch-2 block (TOP) */ 10062306a36Sopenharmony_cistatic void rswitch_top_init(struct rswitch_private *priv) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci int i; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci for (i = 0; i < RSWITCH_MAX_NUM_QUEUES; i++) 10562306a36Sopenharmony_ci iowrite32((i / 16) << (GWCA_INDEX * 8), priv->addr + TPEMIMC7(i)); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* Forwarding engine block (MFWD) */ 10962306a36Sopenharmony_cistatic void rswitch_fwd_init(struct rswitch_private *priv) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci int i; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* For ETHA */ 11462306a36Sopenharmony_ci for (i = 0; i < RSWITCH_NUM_PORTS; i++) { 11562306a36Sopenharmony_ci iowrite32(FWPC0_DEFAULT, priv->addr + FWPC0(i)); 11662306a36Sopenharmony_ci iowrite32(0, priv->addr + FWPBFC(i)); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci for (i = 0; i < RSWITCH_NUM_PORTS; i++) { 12062306a36Sopenharmony_ci iowrite32(priv->rdev[i]->rx_queue->index, 12162306a36Sopenharmony_ci priv->addr + FWPBFCSDC(GWCA_INDEX, i)); 12262306a36Sopenharmony_ci iowrite32(BIT(priv->gwca.index), priv->addr + FWPBFC(i)); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* For GWCA */ 12662306a36Sopenharmony_ci iowrite32(FWPC0_DEFAULT, priv->addr + FWPC0(priv->gwca.index)); 12762306a36Sopenharmony_ci iowrite32(FWPC1_DDE, priv->addr + FWPC1(priv->gwca.index)); 12862306a36Sopenharmony_ci iowrite32(0, priv->addr + FWPBFC(priv->gwca.index)); 12962306a36Sopenharmony_ci iowrite32(GENMASK(RSWITCH_NUM_PORTS - 1, 0), priv->addr + FWPBFC(priv->gwca.index)); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* Gateway CPU agent block (GWCA) */ 13362306a36Sopenharmony_cistatic int rswitch_gwca_change_mode(struct rswitch_private *priv, 13462306a36Sopenharmony_ci enum rswitch_gwca_mode mode) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci int ret; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (!rswitch_agent_clock_is_enabled(priv->addr, priv->gwca.index)) 13962306a36Sopenharmony_ci rswitch_agent_clock_ctrl(priv->addr, priv->gwca.index, 1); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci iowrite32(mode, priv->addr + GWMC); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ret = rswitch_reg_wait(priv->addr, GWMS, GWMS_OPS_MASK, mode); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (mode == GWMC_OPC_DISABLE) 14662306a36Sopenharmony_ci rswitch_agent_clock_ctrl(priv->addr, priv->gwca.index, 0); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return ret; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int rswitch_gwca_mcast_table_reset(struct rswitch_private *priv) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci iowrite32(GWMTIRM_MTIOG, priv->addr + GWMTIRM); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return rswitch_reg_wait(priv->addr, GWMTIRM, GWMTIRM_MTR, GWMTIRM_MTR); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int rswitch_gwca_axi_ram_reset(struct rswitch_private *priv) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci iowrite32(GWARIRM_ARIOG, priv->addr + GWARIRM); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return rswitch_reg_wait(priv->addr, GWARIRM, GWARIRM_ARR, GWARIRM_ARR); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic bool rswitch_is_any_data_irq(struct rswitch_private *priv, u32 *dis, bool tx) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci u32 *mask = tx ? priv->gwca.tx_irq_bits : priv->gwca.rx_irq_bits; 16862306a36Sopenharmony_ci int i; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci for (i = 0; i < RSWITCH_NUM_IRQ_REGS; i++) { 17162306a36Sopenharmony_ci if (dis[i] & mask[i]) 17262306a36Sopenharmony_ci return true; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return false; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void rswitch_get_data_irq_status(struct rswitch_private *priv, u32 *dis) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci int i; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci for (i = 0; i < RSWITCH_NUM_IRQ_REGS; i++) { 18362306a36Sopenharmony_ci dis[i] = ioread32(priv->addr + GWDIS(i)); 18462306a36Sopenharmony_ci dis[i] &= ioread32(priv->addr + GWDIE(i)); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void rswitch_enadis_data_irq(struct rswitch_private *priv, int index, bool enable) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci u32 offs = enable ? GWDIE(index / 32) : GWDID(index / 32); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci iowrite32(BIT(index % 32), priv->addr + offs); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void rswitch_ack_data_irq(struct rswitch_private *priv, int index) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci u32 offs = GWDIS(index / 32); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci iowrite32(BIT(index % 32), priv->addr + offs); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int rswitch_next_queue_index(struct rswitch_gwca_queue *gq, bool cur, int num) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci int index = cur ? gq->cur : gq->dirty; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (index + num >= gq->ring_size) 20762306a36Sopenharmony_ci index = (index + num) % gq->ring_size; 20862306a36Sopenharmony_ci else 20962306a36Sopenharmony_ci index += num; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return index; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int rswitch_get_num_cur_queues(struct rswitch_gwca_queue *gq) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci if (gq->cur >= gq->dirty) 21762306a36Sopenharmony_ci return gq->cur - gq->dirty; 21862306a36Sopenharmony_ci else 21962306a36Sopenharmony_ci return gq->ring_size - gq->dirty + gq->cur; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic bool rswitch_is_queue_rxed(struct rswitch_gwca_queue *gq) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct rswitch_ext_ts_desc *desc = &gq->rx_ring[gq->dirty]; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if ((desc->desc.die_dt & DT_MASK) != DT_FEMPTY) 22762306a36Sopenharmony_ci return true; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return false; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int rswitch_gwca_queue_alloc_skb(struct rswitch_gwca_queue *gq, 23362306a36Sopenharmony_ci int start_index, int num) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci int i, index; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci for (i = 0; i < num; i++) { 23862306a36Sopenharmony_ci index = (i + start_index) % gq->ring_size; 23962306a36Sopenharmony_ci if (gq->skbs[index]) 24062306a36Sopenharmony_ci continue; 24162306a36Sopenharmony_ci gq->skbs[index] = netdev_alloc_skb_ip_align(gq->ndev, 24262306a36Sopenharmony_ci PKT_BUF_SZ + RSWITCH_ALIGN - 1); 24362306a36Sopenharmony_ci if (!gq->skbs[index]) 24462306a36Sopenharmony_ci goto err; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cierr: 25062306a36Sopenharmony_ci for (i--; i >= 0; i--) { 25162306a36Sopenharmony_ci index = (i + start_index) % gq->ring_size; 25262306a36Sopenharmony_ci dev_kfree_skb(gq->skbs[index]); 25362306a36Sopenharmony_ci gq->skbs[index] = NULL; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return -ENOMEM; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic void rswitch_gwca_queue_free(struct net_device *ndev, 26062306a36Sopenharmony_ci struct rswitch_gwca_queue *gq) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci int i; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (!gq->dir_tx) { 26562306a36Sopenharmony_ci dma_free_coherent(ndev->dev.parent, 26662306a36Sopenharmony_ci sizeof(struct rswitch_ext_ts_desc) * 26762306a36Sopenharmony_ci (gq->ring_size + 1), gq->rx_ring, gq->ring_dma); 26862306a36Sopenharmony_ci gq->rx_ring = NULL; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci for (i = 0; i < gq->ring_size; i++) 27162306a36Sopenharmony_ci dev_kfree_skb(gq->skbs[i]); 27262306a36Sopenharmony_ci } else { 27362306a36Sopenharmony_ci dma_free_coherent(ndev->dev.parent, 27462306a36Sopenharmony_ci sizeof(struct rswitch_ext_desc) * 27562306a36Sopenharmony_ci (gq->ring_size + 1), gq->tx_ring, gq->ring_dma); 27662306a36Sopenharmony_ci gq->tx_ring = NULL; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci kfree(gq->skbs); 28062306a36Sopenharmony_ci gq->skbs = NULL; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic void rswitch_gwca_ts_queue_free(struct rswitch_private *priv) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct rswitch_gwca_queue *gq = &priv->gwca.ts_queue; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci dma_free_coherent(&priv->pdev->dev, 28862306a36Sopenharmony_ci sizeof(struct rswitch_ts_desc) * (gq->ring_size + 1), 28962306a36Sopenharmony_ci gq->ts_ring, gq->ring_dma); 29062306a36Sopenharmony_ci gq->ts_ring = NULL; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int rswitch_gwca_queue_alloc(struct net_device *ndev, 29462306a36Sopenharmony_ci struct rswitch_private *priv, 29562306a36Sopenharmony_ci struct rswitch_gwca_queue *gq, 29662306a36Sopenharmony_ci bool dir_tx, int ring_size) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci int i, bit; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci gq->dir_tx = dir_tx; 30162306a36Sopenharmony_ci gq->ring_size = ring_size; 30262306a36Sopenharmony_ci gq->ndev = ndev; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci gq->skbs = kcalloc(gq->ring_size, sizeof(*gq->skbs), GFP_KERNEL); 30562306a36Sopenharmony_ci if (!gq->skbs) 30662306a36Sopenharmony_ci return -ENOMEM; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (!dir_tx) { 30962306a36Sopenharmony_ci rswitch_gwca_queue_alloc_skb(gq, 0, gq->ring_size); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci gq->rx_ring = dma_alloc_coherent(ndev->dev.parent, 31262306a36Sopenharmony_ci sizeof(struct rswitch_ext_ts_desc) * 31362306a36Sopenharmony_ci (gq->ring_size + 1), &gq->ring_dma, GFP_KERNEL); 31462306a36Sopenharmony_ci } else { 31562306a36Sopenharmony_ci gq->tx_ring = dma_alloc_coherent(ndev->dev.parent, 31662306a36Sopenharmony_ci sizeof(struct rswitch_ext_desc) * 31762306a36Sopenharmony_ci (gq->ring_size + 1), &gq->ring_dma, GFP_KERNEL); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (!gq->rx_ring && !gq->tx_ring) 32162306a36Sopenharmony_ci goto out; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci i = gq->index / 32; 32462306a36Sopenharmony_ci bit = BIT(gq->index % 32); 32562306a36Sopenharmony_ci if (dir_tx) 32662306a36Sopenharmony_ci priv->gwca.tx_irq_bits[i] |= bit; 32762306a36Sopenharmony_ci else 32862306a36Sopenharmony_ci priv->gwca.rx_irq_bits[i] |= bit; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ciout: 33362306a36Sopenharmony_ci rswitch_gwca_queue_free(ndev, gq); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return -ENOMEM; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic void rswitch_desc_set_dptr(struct rswitch_desc *desc, dma_addr_t addr) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci desc->dptrl = cpu_to_le32(lower_32_bits(addr)); 34162306a36Sopenharmony_ci desc->dptrh = upper_32_bits(addr) & 0xff; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic dma_addr_t rswitch_desc_get_dptr(const struct rswitch_desc *desc) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci return __le32_to_cpu(desc->dptrl) | (u64)(desc->dptrh) << 32; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic int rswitch_gwca_queue_format(struct net_device *ndev, 35062306a36Sopenharmony_ci struct rswitch_private *priv, 35162306a36Sopenharmony_ci struct rswitch_gwca_queue *gq) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci int ring_size = sizeof(struct rswitch_ext_desc) * gq->ring_size; 35462306a36Sopenharmony_ci struct rswitch_ext_desc *desc; 35562306a36Sopenharmony_ci struct rswitch_desc *linkfix; 35662306a36Sopenharmony_ci dma_addr_t dma_addr; 35762306a36Sopenharmony_ci int i; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci memset(gq->tx_ring, 0, ring_size); 36062306a36Sopenharmony_ci for (i = 0, desc = gq->tx_ring; i < gq->ring_size; i++, desc++) { 36162306a36Sopenharmony_ci if (!gq->dir_tx) { 36262306a36Sopenharmony_ci dma_addr = dma_map_single(ndev->dev.parent, 36362306a36Sopenharmony_ci gq->skbs[i]->data, PKT_BUF_SZ, 36462306a36Sopenharmony_ci DMA_FROM_DEVICE); 36562306a36Sopenharmony_ci if (dma_mapping_error(ndev->dev.parent, dma_addr)) 36662306a36Sopenharmony_ci goto err; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci desc->desc.info_ds = cpu_to_le16(PKT_BUF_SZ); 36962306a36Sopenharmony_ci rswitch_desc_set_dptr(&desc->desc, dma_addr); 37062306a36Sopenharmony_ci desc->desc.die_dt = DT_FEMPTY | DIE; 37162306a36Sopenharmony_ci } else { 37262306a36Sopenharmony_ci desc->desc.die_dt = DT_EEMPTY | DIE; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci rswitch_desc_set_dptr(&desc->desc, gq->ring_dma); 37662306a36Sopenharmony_ci desc->desc.die_dt = DT_LINKFIX; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci linkfix = &priv->gwca.linkfix_table[gq->index]; 37962306a36Sopenharmony_ci linkfix->die_dt = DT_LINKFIX; 38062306a36Sopenharmony_ci rswitch_desc_set_dptr(linkfix, gq->ring_dma); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci iowrite32(GWDCC_BALR | (gq->dir_tx ? GWDCC_DCP(GWCA_IPV_NUM) | GWDCC_DQT : 0) | GWDCC_EDE, 38362306a36Sopenharmony_ci priv->addr + GWDCC_OFFS(gq->index)); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cierr: 38862306a36Sopenharmony_ci if (!gq->dir_tx) { 38962306a36Sopenharmony_ci for (i--, desc = gq->tx_ring; i >= 0; i--, desc++) { 39062306a36Sopenharmony_ci dma_addr = rswitch_desc_get_dptr(&desc->desc); 39162306a36Sopenharmony_ci dma_unmap_single(ndev->dev.parent, dma_addr, PKT_BUF_SZ, 39262306a36Sopenharmony_ci DMA_FROM_DEVICE); 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return -ENOMEM; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic void rswitch_gwca_ts_queue_fill(struct rswitch_private *priv, 40062306a36Sopenharmony_ci int start_index, int num) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct rswitch_gwca_queue *gq = &priv->gwca.ts_queue; 40362306a36Sopenharmony_ci struct rswitch_ts_desc *desc; 40462306a36Sopenharmony_ci int i, index; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci for (i = 0; i < num; i++) { 40762306a36Sopenharmony_ci index = (i + start_index) % gq->ring_size; 40862306a36Sopenharmony_ci desc = &gq->ts_ring[index]; 40962306a36Sopenharmony_ci desc->desc.die_dt = DT_FEMPTY_ND | DIE; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic int rswitch_gwca_queue_ext_ts_fill(struct net_device *ndev, 41462306a36Sopenharmony_ci struct rswitch_gwca_queue *gq, 41562306a36Sopenharmony_ci int start_index, int num) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 41862306a36Sopenharmony_ci struct rswitch_ext_ts_desc *desc; 41962306a36Sopenharmony_ci dma_addr_t dma_addr; 42062306a36Sopenharmony_ci int i, index; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci for (i = 0; i < num; i++) { 42362306a36Sopenharmony_ci index = (i + start_index) % gq->ring_size; 42462306a36Sopenharmony_ci desc = &gq->rx_ring[index]; 42562306a36Sopenharmony_ci if (!gq->dir_tx) { 42662306a36Sopenharmony_ci dma_addr = dma_map_single(ndev->dev.parent, 42762306a36Sopenharmony_ci gq->skbs[index]->data, PKT_BUF_SZ, 42862306a36Sopenharmony_ci DMA_FROM_DEVICE); 42962306a36Sopenharmony_ci if (dma_mapping_error(ndev->dev.parent, dma_addr)) 43062306a36Sopenharmony_ci goto err; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci desc->desc.info_ds = cpu_to_le16(PKT_BUF_SZ); 43362306a36Sopenharmony_ci rswitch_desc_set_dptr(&desc->desc, dma_addr); 43462306a36Sopenharmony_ci dma_wmb(); 43562306a36Sopenharmony_ci desc->desc.die_dt = DT_FEMPTY | DIE; 43662306a36Sopenharmony_ci desc->info1 = cpu_to_le64(INFO1_SPN(rdev->etha->index)); 43762306a36Sopenharmony_ci } else { 43862306a36Sopenharmony_ci desc->desc.die_dt = DT_EEMPTY | DIE; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cierr: 44562306a36Sopenharmony_ci if (!gq->dir_tx) { 44662306a36Sopenharmony_ci for (i--; i >= 0; i--) { 44762306a36Sopenharmony_ci index = (i + start_index) % gq->ring_size; 44862306a36Sopenharmony_ci desc = &gq->rx_ring[index]; 44962306a36Sopenharmony_ci dma_addr = rswitch_desc_get_dptr(&desc->desc); 45062306a36Sopenharmony_ci dma_unmap_single(ndev->dev.parent, dma_addr, PKT_BUF_SZ, 45162306a36Sopenharmony_ci DMA_FROM_DEVICE); 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return -ENOMEM; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic int rswitch_gwca_queue_ext_ts_format(struct net_device *ndev, 45962306a36Sopenharmony_ci struct rswitch_private *priv, 46062306a36Sopenharmony_ci struct rswitch_gwca_queue *gq) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci int ring_size = sizeof(struct rswitch_ext_ts_desc) * gq->ring_size; 46362306a36Sopenharmony_ci struct rswitch_ext_ts_desc *desc; 46462306a36Sopenharmony_ci struct rswitch_desc *linkfix; 46562306a36Sopenharmony_ci int err; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci memset(gq->rx_ring, 0, ring_size); 46862306a36Sopenharmony_ci err = rswitch_gwca_queue_ext_ts_fill(ndev, gq, 0, gq->ring_size); 46962306a36Sopenharmony_ci if (err < 0) 47062306a36Sopenharmony_ci return err; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci desc = &gq->rx_ring[gq->ring_size]; /* Last */ 47362306a36Sopenharmony_ci rswitch_desc_set_dptr(&desc->desc, gq->ring_dma); 47462306a36Sopenharmony_ci desc->desc.die_dt = DT_LINKFIX; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci linkfix = &priv->gwca.linkfix_table[gq->index]; 47762306a36Sopenharmony_ci linkfix->die_dt = DT_LINKFIX; 47862306a36Sopenharmony_ci rswitch_desc_set_dptr(linkfix, gq->ring_dma); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci iowrite32(GWDCC_BALR | (gq->dir_tx ? GWDCC_DCP(GWCA_IPV_NUM) | GWDCC_DQT : 0) | 48162306a36Sopenharmony_ci GWDCC_ETS | GWDCC_EDE, 48262306a36Sopenharmony_ci priv->addr + GWDCC_OFFS(gq->index)); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return 0; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic int rswitch_gwca_linkfix_alloc(struct rswitch_private *priv) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci int i, num_queues = priv->gwca.num_queues; 49062306a36Sopenharmony_ci struct rswitch_gwca *gwca = &priv->gwca; 49162306a36Sopenharmony_ci struct device *dev = &priv->pdev->dev; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci gwca->linkfix_table_size = sizeof(struct rswitch_desc) * num_queues; 49462306a36Sopenharmony_ci gwca->linkfix_table = dma_alloc_coherent(dev, gwca->linkfix_table_size, 49562306a36Sopenharmony_ci &gwca->linkfix_table_dma, GFP_KERNEL); 49662306a36Sopenharmony_ci if (!gwca->linkfix_table) 49762306a36Sopenharmony_ci return -ENOMEM; 49862306a36Sopenharmony_ci for (i = 0; i < num_queues; i++) 49962306a36Sopenharmony_ci gwca->linkfix_table[i].die_dt = DT_EOS; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return 0; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic void rswitch_gwca_linkfix_free(struct rswitch_private *priv) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct rswitch_gwca *gwca = &priv->gwca; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (gwca->linkfix_table) 50962306a36Sopenharmony_ci dma_free_coherent(&priv->pdev->dev, gwca->linkfix_table_size, 51062306a36Sopenharmony_ci gwca->linkfix_table, gwca->linkfix_table_dma); 51162306a36Sopenharmony_ci gwca->linkfix_table = NULL; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int rswitch_gwca_ts_queue_alloc(struct rswitch_private *priv) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct rswitch_gwca_queue *gq = &priv->gwca.ts_queue; 51762306a36Sopenharmony_ci struct rswitch_ts_desc *desc; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci gq->ring_size = TS_RING_SIZE; 52062306a36Sopenharmony_ci gq->ts_ring = dma_alloc_coherent(&priv->pdev->dev, 52162306a36Sopenharmony_ci sizeof(struct rswitch_ts_desc) * 52262306a36Sopenharmony_ci (gq->ring_size + 1), &gq->ring_dma, GFP_KERNEL); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (!gq->ts_ring) 52562306a36Sopenharmony_ci return -ENOMEM; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci rswitch_gwca_ts_queue_fill(priv, 0, TS_RING_SIZE); 52862306a36Sopenharmony_ci desc = &gq->ts_ring[gq->ring_size]; 52962306a36Sopenharmony_ci desc->desc.die_dt = DT_LINKFIX; 53062306a36Sopenharmony_ci rswitch_desc_set_dptr(&desc->desc, gq->ring_dma); 53162306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->gwca.ts_info_list); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic struct rswitch_gwca_queue *rswitch_gwca_get(struct rswitch_private *priv) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct rswitch_gwca_queue *gq; 53962306a36Sopenharmony_ci int index; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci index = find_first_zero_bit(priv->gwca.used, priv->gwca.num_queues); 54262306a36Sopenharmony_ci if (index >= priv->gwca.num_queues) 54362306a36Sopenharmony_ci return NULL; 54462306a36Sopenharmony_ci set_bit(index, priv->gwca.used); 54562306a36Sopenharmony_ci gq = &priv->gwca.queues[index]; 54662306a36Sopenharmony_ci memset(gq, 0, sizeof(*gq)); 54762306a36Sopenharmony_ci gq->index = index; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return gq; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic void rswitch_gwca_put(struct rswitch_private *priv, 55362306a36Sopenharmony_ci struct rswitch_gwca_queue *gq) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci clear_bit(gq->index, priv->gwca.used); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int rswitch_txdmac_alloc(struct net_device *ndev) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 56162306a36Sopenharmony_ci struct rswitch_private *priv = rdev->priv; 56262306a36Sopenharmony_ci int err; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci rdev->tx_queue = rswitch_gwca_get(priv); 56562306a36Sopenharmony_ci if (!rdev->tx_queue) 56662306a36Sopenharmony_ci return -EBUSY; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci err = rswitch_gwca_queue_alloc(ndev, priv, rdev->tx_queue, true, TX_RING_SIZE); 56962306a36Sopenharmony_ci if (err < 0) { 57062306a36Sopenharmony_ci rswitch_gwca_put(priv, rdev->tx_queue); 57162306a36Sopenharmony_ci return err; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return 0; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic void rswitch_txdmac_free(struct net_device *ndev) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci rswitch_gwca_queue_free(ndev, rdev->tx_queue); 58262306a36Sopenharmony_ci rswitch_gwca_put(rdev->priv, rdev->tx_queue); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic int rswitch_txdmac_init(struct rswitch_private *priv, int index) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct rswitch_device *rdev = priv->rdev[index]; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return rswitch_gwca_queue_format(rdev->ndev, priv, rdev->tx_queue); 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int rswitch_rxdmac_alloc(struct net_device *ndev) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 59562306a36Sopenharmony_ci struct rswitch_private *priv = rdev->priv; 59662306a36Sopenharmony_ci int err; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci rdev->rx_queue = rswitch_gwca_get(priv); 59962306a36Sopenharmony_ci if (!rdev->rx_queue) 60062306a36Sopenharmony_ci return -EBUSY; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci err = rswitch_gwca_queue_alloc(ndev, priv, rdev->rx_queue, false, RX_RING_SIZE); 60362306a36Sopenharmony_ci if (err < 0) { 60462306a36Sopenharmony_ci rswitch_gwca_put(priv, rdev->rx_queue); 60562306a36Sopenharmony_ci return err; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic void rswitch_rxdmac_free(struct net_device *ndev) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci rswitch_gwca_queue_free(ndev, rdev->rx_queue); 61662306a36Sopenharmony_ci rswitch_gwca_put(rdev->priv, rdev->rx_queue); 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int rswitch_rxdmac_init(struct rswitch_private *priv, int index) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci struct rswitch_device *rdev = priv->rdev[index]; 62262306a36Sopenharmony_ci struct net_device *ndev = rdev->ndev; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci return rswitch_gwca_queue_ext_ts_format(ndev, priv, rdev->rx_queue); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic int rswitch_gwca_hw_init(struct rswitch_private *priv) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci int i, err; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci err = rswitch_gwca_change_mode(priv, GWMC_OPC_DISABLE); 63262306a36Sopenharmony_ci if (err < 0) 63362306a36Sopenharmony_ci return err; 63462306a36Sopenharmony_ci err = rswitch_gwca_change_mode(priv, GWMC_OPC_CONFIG); 63562306a36Sopenharmony_ci if (err < 0) 63662306a36Sopenharmony_ci return err; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci err = rswitch_gwca_mcast_table_reset(priv); 63962306a36Sopenharmony_ci if (err < 0) 64062306a36Sopenharmony_ci return err; 64162306a36Sopenharmony_ci err = rswitch_gwca_axi_ram_reset(priv); 64262306a36Sopenharmony_ci if (err < 0) 64362306a36Sopenharmony_ci return err; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci iowrite32(GWVCC_VEM_SC_TAG, priv->addr + GWVCC); 64662306a36Sopenharmony_ci iowrite32(0, priv->addr + GWTTFC); 64762306a36Sopenharmony_ci iowrite32(lower_32_bits(priv->gwca.linkfix_table_dma), priv->addr + GWDCBAC1); 64862306a36Sopenharmony_ci iowrite32(upper_32_bits(priv->gwca.linkfix_table_dma), priv->addr + GWDCBAC0); 64962306a36Sopenharmony_ci iowrite32(lower_32_bits(priv->gwca.ts_queue.ring_dma), priv->addr + GWTDCAC10); 65062306a36Sopenharmony_ci iowrite32(upper_32_bits(priv->gwca.ts_queue.ring_dma), priv->addr + GWTDCAC00); 65162306a36Sopenharmony_ci iowrite32(GWCA_TS_IRQ_BIT, priv->addr + GWTSDCC0); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci iowrite32(GWTPC_PPPL(GWCA_IPV_NUM), priv->addr + GWTPC0); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci for (i = 0; i < RSWITCH_NUM_PORTS; i++) { 65662306a36Sopenharmony_ci err = rswitch_rxdmac_init(priv, i); 65762306a36Sopenharmony_ci if (err < 0) 65862306a36Sopenharmony_ci return err; 65962306a36Sopenharmony_ci err = rswitch_txdmac_init(priv, i); 66062306a36Sopenharmony_ci if (err < 0) 66162306a36Sopenharmony_ci return err; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci err = rswitch_gwca_change_mode(priv, GWMC_OPC_DISABLE); 66562306a36Sopenharmony_ci if (err < 0) 66662306a36Sopenharmony_ci return err; 66762306a36Sopenharmony_ci return rswitch_gwca_change_mode(priv, GWMC_OPC_OPERATION); 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic int rswitch_gwca_hw_deinit(struct rswitch_private *priv) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci int err; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci err = rswitch_gwca_change_mode(priv, GWMC_OPC_DISABLE); 67562306a36Sopenharmony_ci if (err < 0) 67662306a36Sopenharmony_ci return err; 67762306a36Sopenharmony_ci err = rswitch_gwca_change_mode(priv, GWMC_OPC_RESET); 67862306a36Sopenharmony_ci if (err < 0) 67962306a36Sopenharmony_ci return err; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return rswitch_gwca_change_mode(priv, GWMC_OPC_DISABLE); 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic int rswitch_gwca_halt(struct rswitch_private *priv) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci int err; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci priv->gwca_halt = true; 68962306a36Sopenharmony_ci err = rswitch_gwca_hw_deinit(priv); 69062306a36Sopenharmony_ci dev_err(&priv->pdev->dev, "halted (%d)\n", err); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return err; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic bool rswitch_rx(struct net_device *ndev, int *quota) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 69862306a36Sopenharmony_ci struct rswitch_gwca_queue *gq = rdev->rx_queue; 69962306a36Sopenharmony_ci struct rswitch_ext_ts_desc *desc; 70062306a36Sopenharmony_ci int limit, boguscnt, num, ret; 70162306a36Sopenharmony_ci struct sk_buff *skb; 70262306a36Sopenharmony_ci dma_addr_t dma_addr; 70362306a36Sopenharmony_ci u16 pkt_len; 70462306a36Sopenharmony_ci u32 get_ts; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (*quota <= 0) 70762306a36Sopenharmony_ci return true; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci boguscnt = min_t(int, gq->ring_size, *quota); 71062306a36Sopenharmony_ci limit = boguscnt; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci desc = &gq->rx_ring[gq->cur]; 71362306a36Sopenharmony_ci while ((desc->desc.die_dt & DT_MASK) != DT_FEMPTY) { 71462306a36Sopenharmony_ci dma_rmb(); 71562306a36Sopenharmony_ci pkt_len = le16_to_cpu(desc->desc.info_ds) & RX_DS; 71662306a36Sopenharmony_ci skb = gq->skbs[gq->cur]; 71762306a36Sopenharmony_ci gq->skbs[gq->cur] = NULL; 71862306a36Sopenharmony_ci dma_addr = rswitch_desc_get_dptr(&desc->desc); 71962306a36Sopenharmony_ci dma_unmap_single(ndev->dev.parent, dma_addr, PKT_BUF_SZ, DMA_FROM_DEVICE); 72062306a36Sopenharmony_ci get_ts = rdev->priv->ptp_priv->tstamp_rx_ctrl & RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT; 72162306a36Sopenharmony_ci if (get_ts) { 72262306a36Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps; 72362306a36Sopenharmony_ci struct timespec64 ts; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci shhwtstamps = skb_hwtstamps(skb); 72662306a36Sopenharmony_ci memset(shhwtstamps, 0, sizeof(*shhwtstamps)); 72762306a36Sopenharmony_ci ts.tv_sec = __le32_to_cpu(desc->ts_sec); 72862306a36Sopenharmony_ci ts.tv_nsec = __le32_to_cpu(desc->ts_nsec & cpu_to_le32(0x3fffffff)); 72962306a36Sopenharmony_ci shhwtstamps->hwtstamp = timespec64_to_ktime(ts); 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci skb_put(skb, pkt_len); 73262306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 73362306a36Sopenharmony_ci napi_gro_receive(&rdev->napi, skb); 73462306a36Sopenharmony_ci rdev->ndev->stats.rx_packets++; 73562306a36Sopenharmony_ci rdev->ndev->stats.rx_bytes += pkt_len; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci gq->cur = rswitch_next_queue_index(gq, true, 1); 73862306a36Sopenharmony_ci desc = &gq->rx_ring[gq->cur]; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (--boguscnt <= 0) 74162306a36Sopenharmony_ci break; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci num = rswitch_get_num_cur_queues(gq); 74562306a36Sopenharmony_ci ret = rswitch_gwca_queue_alloc_skb(gq, gq->dirty, num); 74662306a36Sopenharmony_ci if (ret < 0) 74762306a36Sopenharmony_ci goto err; 74862306a36Sopenharmony_ci ret = rswitch_gwca_queue_ext_ts_fill(ndev, gq, gq->dirty, num); 74962306a36Sopenharmony_ci if (ret < 0) 75062306a36Sopenharmony_ci goto err; 75162306a36Sopenharmony_ci gq->dirty = rswitch_next_queue_index(gq, false, num); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci *quota -= limit - boguscnt; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci return boguscnt <= 0; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cierr: 75862306a36Sopenharmony_ci rswitch_gwca_halt(rdev->priv); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci return 0; 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic int rswitch_tx_free(struct net_device *ndev, bool free_txed_only) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 76662306a36Sopenharmony_ci struct rswitch_gwca_queue *gq = rdev->tx_queue; 76762306a36Sopenharmony_ci struct rswitch_ext_desc *desc; 76862306a36Sopenharmony_ci dma_addr_t dma_addr; 76962306a36Sopenharmony_ci struct sk_buff *skb; 77062306a36Sopenharmony_ci int free_num = 0; 77162306a36Sopenharmony_ci int size; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci for (; rswitch_get_num_cur_queues(gq) > 0; 77462306a36Sopenharmony_ci gq->dirty = rswitch_next_queue_index(gq, false, 1)) { 77562306a36Sopenharmony_ci desc = &gq->tx_ring[gq->dirty]; 77662306a36Sopenharmony_ci if (free_txed_only && (desc->desc.die_dt & DT_MASK) != DT_FEMPTY) 77762306a36Sopenharmony_ci break; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci dma_rmb(); 78062306a36Sopenharmony_ci size = le16_to_cpu(desc->desc.info_ds) & TX_DS; 78162306a36Sopenharmony_ci skb = gq->skbs[gq->dirty]; 78262306a36Sopenharmony_ci if (skb) { 78362306a36Sopenharmony_ci dma_addr = rswitch_desc_get_dptr(&desc->desc); 78462306a36Sopenharmony_ci dma_unmap_single(ndev->dev.parent, dma_addr, 78562306a36Sopenharmony_ci size, DMA_TO_DEVICE); 78662306a36Sopenharmony_ci dev_kfree_skb_any(gq->skbs[gq->dirty]); 78762306a36Sopenharmony_ci gq->skbs[gq->dirty] = NULL; 78862306a36Sopenharmony_ci free_num++; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci desc->desc.die_dt = DT_EEMPTY; 79162306a36Sopenharmony_ci rdev->ndev->stats.tx_packets++; 79262306a36Sopenharmony_ci rdev->ndev->stats.tx_bytes += size; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci return free_num; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic int rswitch_poll(struct napi_struct *napi, int budget) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci struct net_device *ndev = napi->dev; 80162306a36Sopenharmony_ci struct rswitch_private *priv; 80262306a36Sopenharmony_ci struct rswitch_device *rdev; 80362306a36Sopenharmony_ci unsigned long flags; 80462306a36Sopenharmony_ci int quota = budget; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci rdev = netdev_priv(ndev); 80762306a36Sopenharmony_ci priv = rdev->priv; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ciretry: 81062306a36Sopenharmony_ci rswitch_tx_free(ndev, true); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (rswitch_rx(ndev, "a)) 81362306a36Sopenharmony_ci goto out; 81462306a36Sopenharmony_ci else if (rdev->priv->gwca_halt) 81562306a36Sopenharmony_ci goto err; 81662306a36Sopenharmony_ci else if (rswitch_is_queue_rxed(rdev->rx_queue)) 81762306a36Sopenharmony_ci goto retry; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci netif_wake_subqueue(ndev, 0); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (napi_complete_done(napi, budget - quota)) { 82262306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 82362306a36Sopenharmony_ci rswitch_enadis_data_irq(priv, rdev->tx_queue->index, true); 82462306a36Sopenharmony_ci rswitch_enadis_data_irq(priv, rdev->rx_queue->index, true); 82562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ciout: 82962306a36Sopenharmony_ci return budget - quota; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cierr: 83262306a36Sopenharmony_ci napi_complete(napi); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci return 0; 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic void rswitch_queue_interrupt(struct net_device *ndev) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (napi_schedule_prep(&rdev->napi)) { 84262306a36Sopenharmony_ci spin_lock(&rdev->priv->lock); 84362306a36Sopenharmony_ci rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false); 84462306a36Sopenharmony_ci rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false); 84562306a36Sopenharmony_ci spin_unlock(&rdev->priv->lock); 84662306a36Sopenharmony_ci __napi_schedule(&rdev->napi); 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic irqreturn_t rswitch_data_irq(struct rswitch_private *priv, u32 *dis) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct rswitch_gwca_queue *gq; 85362306a36Sopenharmony_ci int i, index, bit; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci for (i = 0; i < priv->gwca.num_queues; i++) { 85662306a36Sopenharmony_ci gq = &priv->gwca.queues[i]; 85762306a36Sopenharmony_ci index = gq->index / 32; 85862306a36Sopenharmony_ci bit = BIT(gq->index % 32); 85962306a36Sopenharmony_ci if (!(dis[index] & bit)) 86062306a36Sopenharmony_ci continue; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci rswitch_ack_data_irq(priv, gq->index); 86362306a36Sopenharmony_ci rswitch_queue_interrupt(gq->ndev); 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci return IRQ_HANDLED; 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic irqreturn_t rswitch_gwca_irq(int irq, void *dev_id) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct rswitch_private *priv = dev_id; 87262306a36Sopenharmony_ci u32 dis[RSWITCH_NUM_IRQ_REGS]; 87362306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci rswitch_get_data_irq_status(priv, dis); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (rswitch_is_any_data_irq(priv, dis, true) || 87862306a36Sopenharmony_ci rswitch_is_any_data_irq(priv, dis, false)) 87962306a36Sopenharmony_ci ret = rswitch_data_irq(priv, dis); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci return ret; 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic int rswitch_gwca_request_irqs(struct rswitch_private *priv) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci char *resource_name, *irq_name; 88762306a36Sopenharmony_ci int i, ret, irq; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci for (i = 0; i < GWCA_NUM_IRQS; i++) { 89062306a36Sopenharmony_ci resource_name = kasprintf(GFP_KERNEL, GWCA_IRQ_RESOURCE_NAME, i); 89162306a36Sopenharmony_ci if (!resource_name) 89262306a36Sopenharmony_ci return -ENOMEM; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci irq = platform_get_irq_byname(priv->pdev, resource_name); 89562306a36Sopenharmony_ci kfree(resource_name); 89662306a36Sopenharmony_ci if (irq < 0) 89762306a36Sopenharmony_ci return irq; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci irq_name = devm_kasprintf(&priv->pdev->dev, GFP_KERNEL, 90062306a36Sopenharmony_ci GWCA_IRQ_NAME, i); 90162306a36Sopenharmony_ci if (!irq_name) 90262306a36Sopenharmony_ci return -ENOMEM; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci ret = devm_request_irq(&priv->pdev->dev, irq, rswitch_gwca_irq, 90562306a36Sopenharmony_ci 0, irq_name, priv); 90662306a36Sopenharmony_ci if (ret < 0) 90762306a36Sopenharmony_ci return ret; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci return 0; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic void rswitch_ts(struct rswitch_private *priv) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct rswitch_gwca_queue *gq = &priv->gwca.ts_queue; 91662306a36Sopenharmony_ci struct rswitch_gwca_ts_info *ts_info, *ts_info2; 91762306a36Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 91862306a36Sopenharmony_ci struct rswitch_ts_desc *desc; 91962306a36Sopenharmony_ci struct timespec64 ts; 92062306a36Sopenharmony_ci u32 tag, port; 92162306a36Sopenharmony_ci int num; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci desc = &gq->ts_ring[gq->cur]; 92462306a36Sopenharmony_ci while ((desc->desc.die_dt & DT_MASK) != DT_FEMPTY_ND) { 92562306a36Sopenharmony_ci dma_rmb(); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci port = TS_DESC_DPN(__le32_to_cpu(desc->desc.dptrl)); 92862306a36Sopenharmony_ci tag = TS_DESC_TSUN(__le32_to_cpu(desc->desc.dptrl)); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci list_for_each_entry_safe(ts_info, ts_info2, &priv->gwca.ts_info_list, list) { 93162306a36Sopenharmony_ci if (!(ts_info->port == port && ts_info->tag == tag)) 93262306a36Sopenharmony_ci continue; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci memset(&shhwtstamps, 0, sizeof(shhwtstamps)); 93562306a36Sopenharmony_ci ts.tv_sec = __le32_to_cpu(desc->ts_sec); 93662306a36Sopenharmony_ci ts.tv_nsec = __le32_to_cpu(desc->ts_nsec & cpu_to_le32(0x3fffffff)); 93762306a36Sopenharmony_ci shhwtstamps.hwtstamp = timespec64_to_ktime(ts); 93862306a36Sopenharmony_ci skb_tstamp_tx(ts_info->skb, &shhwtstamps); 93962306a36Sopenharmony_ci dev_consume_skb_irq(ts_info->skb); 94062306a36Sopenharmony_ci list_del(&ts_info->list); 94162306a36Sopenharmony_ci kfree(ts_info); 94262306a36Sopenharmony_ci break; 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci gq->cur = rswitch_next_queue_index(gq, true, 1); 94662306a36Sopenharmony_ci desc = &gq->ts_ring[gq->cur]; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci num = rswitch_get_num_cur_queues(gq); 95062306a36Sopenharmony_ci rswitch_gwca_ts_queue_fill(priv, gq->dirty, num); 95162306a36Sopenharmony_ci gq->dirty = rswitch_next_queue_index(gq, false, num); 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_cistatic irqreturn_t rswitch_gwca_ts_irq(int irq, void *dev_id) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci struct rswitch_private *priv = dev_id; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (ioread32(priv->addr + GWTSDIS) & GWCA_TS_IRQ_BIT) { 95962306a36Sopenharmony_ci iowrite32(GWCA_TS_IRQ_BIT, priv->addr + GWTSDIS); 96062306a36Sopenharmony_ci rswitch_ts(priv); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci return IRQ_HANDLED; 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci return IRQ_NONE; 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic int rswitch_gwca_ts_request_irqs(struct rswitch_private *priv) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci int irq; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci irq = platform_get_irq_byname(priv->pdev, GWCA_TS_IRQ_RESOURCE_NAME); 97362306a36Sopenharmony_ci if (irq < 0) 97462306a36Sopenharmony_ci return irq; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci return devm_request_irq(&priv->pdev->dev, irq, rswitch_gwca_ts_irq, 97762306a36Sopenharmony_ci 0, GWCA_TS_IRQ_NAME, priv); 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci/* Ethernet TSN Agent block (ETHA) and Ethernet MAC IP block (RMAC) */ 98162306a36Sopenharmony_cistatic int rswitch_etha_change_mode(struct rswitch_etha *etha, 98262306a36Sopenharmony_ci enum rswitch_etha_mode mode) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci int ret; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (!rswitch_agent_clock_is_enabled(etha->coma_addr, etha->index)) 98762306a36Sopenharmony_ci rswitch_agent_clock_ctrl(etha->coma_addr, etha->index, 1); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci iowrite32(mode, etha->addr + EAMC); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci ret = rswitch_reg_wait(etha->addr, EAMS, EAMS_OPS_MASK, mode); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (mode == EAMC_OPC_DISABLE) 99462306a36Sopenharmony_ci rswitch_agent_clock_ctrl(etha->coma_addr, etha->index, 0); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci return ret; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic void rswitch_etha_read_mac_address(struct rswitch_etha *etha) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci u32 mrmac0 = ioread32(etha->addr + MRMAC0); 100262306a36Sopenharmony_ci u32 mrmac1 = ioread32(etha->addr + MRMAC1); 100362306a36Sopenharmony_ci u8 *mac = ða->mac_addr[0]; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci mac[0] = (mrmac0 >> 8) & 0xFF; 100662306a36Sopenharmony_ci mac[1] = (mrmac0 >> 0) & 0xFF; 100762306a36Sopenharmony_ci mac[2] = (mrmac1 >> 24) & 0xFF; 100862306a36Sopenharmony_ci mac[3] = (mrmac1 >> 16) & 0xFF; 100962306a36Sopenharmony_ci mac[4] = (mrmac1 >> 8) & 0xFF; 101062306a36Sopenharmony_ci mac[5] = (mrmac1 >> 0) & 0xFF; 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic void rswitch_etha_write_mac_address(struct rswitch_etha *etha, const u8 *mac) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci iowrite32((mac[0] << 8) | mac[1], etha->addr + MRMAC0); 101662306a36Sopenharmony_ci iowrite32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], 101762306a36Sopenharmony_ci etha->addr + MRMAC1); 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic int rswitch_etha_wait_link_verification(struct rswitch_etha *etha) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci iowrite32(MLVC_PLV, etha->addr + MLVC); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci return rswitch_reg_wait(etha->addr, MLVC, MLVC_PLV, 0); 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic void rswitch_rmac_setting(struct rswitch_etha *etha, const u8 *mac) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci u32 val; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci rswitch_etha_write_mac_address(etha, mac); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci switch (etha->speed) { 103462306a36Sopenharmony_ci case 100: 103562306a36Sopenharmony_ci val = MPIC_LSC_100M; 103662306a36Sopenharmony_ci break; 103762306a36Sopenharmony_ci case 1000: 103862306a36Sopenharmony_ci val = MPIC_LSC_1G; 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci case 2500: 104162306a36Sopenharmony_ci val = MPIC_LSC_2_5G; 104262306a36Sopenharmony_ci break; 104362306a36Sopenharmony_ci default: 104462306a36Sopenharmony_ci return; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci iowrite32(MPIC_PIS_GMII | val, etha->addr + MPIC); 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic void rswitch_etha_enable_mii(struct rswitch_etha *etha) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci rswitch_modify(etha->addr, MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK, 105362306a36Sopenharmony_ci MPIC_PSMCS(etha->psmcs) | MPIC_PSMHT(0x06)); 105462306a36Sopenharmony_ci rswitch_modify(etha->addr, MPSM, 0, MPSM_MFF_C45); 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic int rswitch_etha_hw_init(struct rswitch_etha *etha, const u8 *mac) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci int err; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci err = rswitch_etha_change_mode(etha, EAMC_OPC_DISABLE); 106262306a36Sopenharmony_ci if (err < 0) 106362306a36Sopenharmony_ci return err; 106462306a36Sopenharmony_ci err = rswitch_etha_change_mode(etha, EAMC_OPC_CONFIG); 106562306a36Sopenharmony_ci if (err < 0) 106662306a36Sopenharmony_ci return err; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci iowrite32(EAVCC_VEM_SC_TAG, etha->addr + EAVCC); 106962306a36Sopenharmony_ci rswitch_rmac_setting(etha, mac); 107062306a36Sopenharmony_ci rswitch_etha_enable_mii(etha); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci err = rswitch_etha_wait_link_verification(etha); 107362306a36Sopenharmony_ci if (err < 0) 107462306a36Sopenharmony_ci return err; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci err = rswitch_etha_change_mode(etha, EAMC_OPC_DISABLE); 107762306a36Sopenharmony_ci if (err < 0) 107862306a36Sopenharmony_ci return err; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci return rswitch_etha_change_mode(etha, EAMC_OPC_OPERATION); 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic int rswitch_etha_set_access(struct rswitch_etha *etha, bool read, 108462306a36Sopenharmony_ci int phyad, int devad, int regad, int data) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci int pop = read ? MDIO_READ_C45 : MDIO_WRITE_C45; 108762306a36Sopenharmony_ci u32 val; 108862306a36Sopenharmony_ci int ret; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (devad == 0xffffffff) 109162306a36Sopenharmony_ci return -ENODEV; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci writel(MMIS1_CLEAR_FLAGS, etha->addr + MMIS1); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci val = MPSM_PSME | MPSM_MFF_C45; 109662306a36Sopenharmony_ci iowrite32((regad << 16) | (devad << 8) | (phyad << 3) | val, etha->addr + MPSM); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci ret = rswitch_reg_wait(etha->addr, MMIS1, MMIS1_PAACS, MMIS1_PAACS); 109962306a36Sopenharmony_ci if (ret) 110062306a36Sopenharmony_ci return ret; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci rswitch_modify(etha->addr, MMIS1, MMIS1_PAACS, MMIS1_PAACS); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci if (read) { 110562306a36Sopenharmony_ci writel((pop << 13) | (devad << 8) | (phyad << 3) | val, etha->addr + MPSM); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci ret = rswitch_reg_wait(etha->addr, MMIS1, MMIS1_PRACS, MMIS1_PRACS); 110862306a36Sopenharmony_ci if (ret) 110962306a36Sopenharmony_ci return ret; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci ret = (ioread32(etha->addr + MPSM) & MPSM_PRD_MASK) >> 16; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci rswitch_modify(etha->addr, MMIS1, MMIS1_PRACS, MMIS1_PRACS); 111462306a36Sopenharmony_ci } else { 111562306a36Sopenharmony_ci iowrite32((data << 16) | (pop << 13) | (devad << 8) | (phyad << 3) | val, 111662306a36Sopenharmony_ci etha->addr + MPSM); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci ret = rswitch_reg_wait(etha->addr, MMIS1, MMIS1_PWACS, MMIS1_PWACS); 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci return ret; 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cistatic int rswitch_etha_mii_read_c45(struct mii_bus *bus, int addr, int devad, 112562306a36Sopenharmony_ci int regad) 112662306a36Sopenharmony_ci{ 112762306a36Sopenharmony_ci struct rswitch_etha *etha = bus->priv; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci return rswitch_etha_set_access(etha, true, addr, devad, regad, 0); 113062306a36Sopenharmony_ci} 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_cistatic int rswitch_etha_mii_write_c45(struct mii_bus *bus, int addr, int devad, 113362306a36Sopenharmony_ci int regad, u16 val) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci struct rswitch_etha *etha = bus->priv; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci return rswitch_etha_set_access(etha, false, addr, devad, regad, val); 113862306a36Sopenharmony_ci} 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci/* Call of_node_put(port) after done */ 114162306a36Sopenharmony_cistatic struct device_node *rswitch_get_port_node(struct rswitch_device *rdev) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci struct device_node *ports, *port; 114462306a36Sopenharmony_ci int err = 0; 114562306a36Sopenharmony_ci u32 index; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci ports = of_get_child_by_name(rdev->ndev->dev.parent->of_node, 114862306a36Sopenharmony_ci "ethernet-ports"); 114962306a36Sopenharmony_ci if (!ports) 115062306a36Sopenharmony_ci return NULL; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci for_each_child_of_node(ports, port) { 115362306a36Sopenharmony_ci err = of_property_read_u32(port, "reg", &index); 115462306a36Sopenharmony_ci if (err < 0) { 115562306a36Sopenharmony_ci port = NULL; 115662306a36Sopenharmony_ci goto out; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci if (index == rdev->etha->index) { 115962306a36Sopenharmony_ci if (!of_device_is_available(port)) 116062306a36Sopenharmony_ci port = NULL; 116162306a36Sopenharmony_ci break; 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ciout: 116662306a36Sopenharmony_ci of_node_put(ports); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci return port; 116962306a36Sopenharmony_ci} 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_cistatic int rswitch_etha_get_params(struct rswitch_device *rdev) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci u32 max_speed; 117462306a36Sopenharmony_ci int err; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci if (!rdev->np_port) 117762306a36Sopenharmony_ci return 0; /* ignored */ 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci err = of_get_phy_mode(rdev->np_port, &rdev->etha->phy_interface); 118062306a36Sopenharmony_ci if (err) 118162306a36Sopenharmony_ci return err; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci err = of_property_read_u32(rdev->np_port, "max-speed", &max_speed); 118462306a36Sopenharmony_ci if (!err) { 118562306a36Sopenharmony_ci rdev->etha->speed = max_speed; 118662306a36Sopenharmony_ci return 0; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci /* if no "max-speed" property, let's use default speed */ 119062306a36Sopenharmony_ci switch (rdev->etha->phy_interface) { 119162306a36Sopenharmony_ci case PHY_INTERFACE_MODE_MII: 119262306a36Sopenharmony_ci rdev->etha->speed = SPEED_100; 119362306a36Sopenharmony_ci break; 119462306a36Sopenharmony_ci case PHY_INTERFACE_MODE_SGMII: 119562306a36Sopenharmony_ci rdev->etha->speed = SPEED_1000; 119662306a36Sopenharmony_ci break; 119762306a36Sopenharmony_ci case PHY_INTERFACE_MODE_USXGMII: 119862306a36Sopenharmony_ci rdev->etha->speed = SPEED_2500; 119962306a36Sopenharmony_ci break; 120062306a36Sopenharmony_ci default: 120162306a36Sopenharmony_ci return -EINVAL; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci return 0; 120562306a36Sopenharmony_ci} 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_cistatic int rswitch_mii_register(struct rswitch_device *rdev) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci struct device_node *mdio_np; 121062306a36Sopenharmony_ci struct mii_bus *mii_bus; 121162306a36Sopenharmony_ci int err; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci mii_bus = mdiobus_alloc(); 121462306a36Sopenharmony_ci if (!mii_bus) 121562306a36Sopenharmony_ci return -ENOMEM; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci mii_bus->name = "rswitch_mii"; 121862306a36Sopenharmony_ci sprintf(mii_bus->id, "etha%d", rdev->etha->index); 121962306a36Sopenharmony_ci mii_bus->priv = rdev->etha; 122062306a36Sopenharmony_ci mii_bus->read_c45 = rswitch_etha_mii_read_c45; 122162306a36Sopenharmony_ci mii_bus->write_c45 = rswitch_etha_mii_write_c45; 122262306a36Sopenharmony_ci mii_bus->parent = &rdev->priv->pdev->dev; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci mdio_np = of_get_child_by_name(rdev->np_port, "mdio"); 122562306a36Sopenharmony_ci err = of_mdiobus_register(mii_bus, mdio_np); 122662306a36Sopenharmony_ci if (err < 0) { 122762306a36Sopenharmony_ci mdiobus_free(mii_bus); 122862306a36Sopenharmony_ci goto out; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci rdev->etha->mii = mii_bus; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ciout: 123462306a36Sopenharmony_ci of_node_put(mdio_np); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci return err; 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_cistatic void rswitch_mii_unregister(struct rswitch_device *rdev) 124062306a36Sopenharmony_ci{ 124162306a36Sopenharmony_ci if (rdev->etha->mii) { 124262306a36Sopenharmony_ci mdiobus_unregister(rdev->etha->mii); 124362306a36Sopenharmony_ci mdiobus_free(rdev->etha->mii); 124462306a36Sopenharmony_ci rdev->etha->mii = NULL; 124562306a36Sopenharmony_ci } 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cistatic void rswitch_adjust_link(struct net_device *ndev) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 125162306a36Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci if (phydev->link != rdev->etha->link) { 125462306a36Sopenharmony_ci phy_print_status(phydev); 125562306a36Sopenharmony_ci if (phydev->link) 125662306a36Sopenharmony_ci phy_power_on(rdev->serdes); 125762306a36Sopenharmony_ci else if (rdev->serdes->power_count) 125862306a36Sopenharmony_ci phy_power_off(rdev->serdes); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci rdev->etha->link = phydev->link; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci if (!rdev->priv->etha_no_runtime_change && 126362306a36Sopenharmony_ci phydev->speed != rdev->etha->speed) { 126462306a36Sopenharmony_ci rdev->etha->speed = phydev->speed; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci rswitch_etha_hw_init(rdev->etha, rdev->ndev->dev_addr); 126762306a36Sopenharmony_ci phy_set_speed(rdev->serdes, rdev->etha->speed); 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cistatic void rswitch_phy_remove_link_mode(struct rswitch_device *rdev, 127362306a36Sopenharmony_ci struct phy_device *phydev) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci if (!rdev->priv->etha_no_runtime_change) 127662306a36Sopenharmony_ci return; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci switch (rdev->etha->speed) { 127962306a36Sopenharmony_ci case SPEED_2500: 128062306a36Sopenharmony_ci phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Full_BIT); 128162306a36Sopenharmony_ci phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_100baseT_Full_BIT); 128262306a36Sopenharmony_ci break; 128362306a36Sopenharmony_ci case SPEED_1000: 128462306a36Sopenharmony_ci phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_2500baseX_Full_BIT); 128562306a36Sopenharmony_ci phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_100baseT_Full_BIT); 128662306a36Sopenharmony_ci break; 128762306a36Sopenharmony_ci case SPEED_100: 128862306a36Sopenharmony_ci phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_2500baseX_Full_BIT); 128962306a36Sopenharmony_ci phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Full_BIT); 129062306a36Sopenharmony_ci break; 129162306a36Sopenharmony_ci default: 129262306a36Sopenharmony_ci break; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci phy_set_max_speed(phydev, rdev->etha->speed); 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic int rswitch_phy_device_init(struct rswitch_device *rdev) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci struct phy_device *phydev; 130162306a36Sopenharmony_ci struct device_node *phy; 130262306a36Sopenharmony_ci int err = -ENOENT; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci if (!rdev->np_port) 130562306a36Sopenharmony_ci return -ENODEV; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci phy = of_parse_phandle(rdev->np_port, "phy-handle", 0); 130862306a36Sopenharmony_ci if (!phy) 130962306a36Sopenharmony_ci return -ENODEV; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci /* Set phydev->host_interfaces before calling of_phy_connect() to 131262306a36Sopenharmony_ci * configure the PHY with the information of host_interfaces. 131362306a36Sopenharmony_ci */ 131462306a36Sopenharmony_ci phydev = of_phy_find_device(phy); 131562306a36Sopenharmony_ci if (!phydev) 131662306a36Sopenharmony_ci goto out; 131762306a36Sopenharmony_ci __set_bit(rdev->etha->phy_interface, phydev->host_interfaces); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci phydev = of_phy_connect(rdev->ndev, phy, rswitch_adjust_link, 0, 132062306a36Sopenharmony_ci rdev->etha->phy_interface); 132162306a36Sopenharmony_ci if (!phydev) 132262306a36Sopenharmony_ci goto out; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci phy_set_max_speed(phydev, SPEED_2500); 132562306a36Sopenharmony_ci phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT); 132662306a36Sopenharmony_ci phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_10baseT_Full_BIT); 132762306a36Sopenharmony_ci phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT); 132862306a36Sopenharmony_ci phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT); 132962306a36Sopenharmony_ci rswitch_phy_remove_link_mode(rdev, phydev); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci phy_attached_info(phydev); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci err = 0; 133462306a36Sopenharmony_ciout: 133562306a36Sopenharmony_ci of_node_put(phy); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci return err; 133862306a36Sopenharmony_ci} 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_cistatic void rswitch_phy_device_deinit(struct rswitch_device *rdev) 134162306a36Sopenharmony_ci{ 134262306a36Sopenharmony_ci if (rdev->ndev->phydev) 134362306a36Sopenharmony_ci phy_disconnect(rdev->ndev->phydev); 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cistatic int rswitch_serdes_set_params(struct rswitch_device *rdev) 134762306a36Sopenharmony_ci{ 134862306a36Sopenharmony_ci int err; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci err = phy_set_mode_ext(rdev->serdes, PHY_MODE_ETHERNET, 135162306a36Sopenharmony_ci rdev->etha->phy_interface); 135262306a36Sopenharmony_ci if (err < 0) 135362306a36Sopenharmony_ci return err; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci return phy_set_speed(rdev->serdes, rdev->etha->speed); 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic int rswitch_ether_port_init_one(struct rswitch_device *rdev) 135962306a36Sopenharmony_ci{ 136062306a36Sopenharmony_ci int err; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (!rdev->etha->operated) { 136362306a36Sopenharmony_ci err = rswitch_etha_hw_init(rdev->etha, rdev->ndev->dev_addr); 136462306a36Sopenharmony_ci if (err < 0) 136562306a36Sopenharmony_ci return err; 136662306a36Sopenharmony_ci if (rdev->priv->etha_no_runtime_change) 136762306a36Sopenharmony_ci rdev->etha->operated = true; 136862306a36Sopenharmony_ci } 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci err = rswitch_mii_register(rdev); 137162306a36Sopenharmony_ci if (err < 0) 137262306a36Sopenharmony_ci return err; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci err = rswitch_phy_device_init(rdev); 137562306a36Sopenharmony_ci if (err < 0) 137662306a36Sopenharmony_ci goto err_phy_device_init; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci rdev->serdes = devm_of_phy_get(&rdev->priv->pdev->dev, rdev->np_port, NULL); 137962306a36Sopenharmony_ci if (IS_ERR(rdev->serdes)) { 138062306a36Sopenharmony_ci err = PTR_ERR(rdev->serdes); 138162306a36Sopenharmony_ci goto err_serdes_phy_get; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci err = rswitch_serdes_set_params(rdev); 138562306a36Sopenharmony_ci if (err < 0) 138662306a36Sopenharmony_ci goto err_serdes_set_params; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci return 0; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cierr_serdes_set_params: 139162306a36Sopenharmony_cierr_serdes_phy_get: 139262306a36Sopenharmony_ci rswitch_phy_device_deinit(rdev); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cierr_phy_device_init: 139562306a36Sopenharmony_ci rswitch_mii_unregister(rdev); 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci return err; 139862306a36Sopenharmony_ci} 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic void rswitch_ether_port_deinit_one(struct rswitch_device *rdev) 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci rswitch_phy_device_deinit(rdev); 140362306a36Sopenharmony_ci rswitch_mii_unregister(rdev); 140462306a36Sopenharmony_ci} 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_cistatic int rswitch_ether_port_init_all(struct rswitch_private *priv) 140762306a36Sopenharmony_ci{ 140862306a36Sopenharmony_ci int i, err; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci rswitch_for_each_enabled_port(priv, i) { 141162306a36Sopenharmony_ci err = rswitch_ether_port_init_one(priv->rdev[i]); 141262306a36Sopenharmony_ci if (err) 141362306a36Sopenharmony_ci goto err_init_one; 141462306a36Sopenharmony_ci } 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci rswitch_for_each_enabled_port(priv, i) { 141762306a36Sopenharmony_ci err = phy_init(priv->rdev[i]->serdes); 141862306a36Sopenharmony_ci if (err) 141962306a36Sopenharmony_ci goto err_serdes; 142062306a36Sopenharmony_ci } 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci return 0; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_cierr_serdes: 142562306a36Sopenharmony_ci rswitch_for_each_enabled_port_continue_reverse(priv, i) 142662306a36Sopenharmony_ci phy_exit(priv->rdev[i]->serdes); 142762306a36Sopenharmony_ci i = RSWITCH_NUM_PORTS; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_cierr_init_one: 143062306a36Sopenharmony_ci rswitch_for_each_enabled_port_continue_reverse(priv, i) 143162306a36Sopenharmony_ci rswitch_ether_port_deinit_one(priv->rdev[i]); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci return err; 143462306a36Sopenharmony_ci} 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_cistatic void rswitch_ether_port_deinit_all(struct rswitch_private *priv) 143762306a36Sopenharmony_ci{ 143862306a36Sopenharmony_ci int i; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci for (i = 0; i < RSWITCH_NUM_PORTS; i++) { 144162306a36Sopenharmony_ci phy_exit(priv->rdev[i]->serdes); 144262306a36Sopenharmony_ci rswitch_ether_port_deinit_one(priv->rdev[i]); 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_cistatic int rswitch_open(struct net_device *ndev) 144762306a36Sopenharmony_ci{ 144862306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 144962306a36Sopenharmony_ci unsigned long flags; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci phy_start(ndev->phydev); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci napi_enable(&rdev->napi); 145462306a36Sopenharmony_ci netif_start_queue(ndev); 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci spin_lock_irqsave(&rdev->priv->lock, flags); 145762306a36Sopenharmony_ci rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, true); 145862306a36Sopenharmony_ci rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, true); 145962306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->priv->lock, flags); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci if (bitmap_empty(rdev->priv->opened_ports, RSWITCH_NUM_PORTS)) 146262306a36Sopenharmony_ci iowrite32(GWCA_TS_IRQ_BIT, rdev->priv->addr + GWTSDIE); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci bitmap_set(rdev->priv->opened_ports, rdev->port, 1); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci return 0; 146762306a36Sopenharmony_ci}; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_cistatic int rswitch_stop(struct net_device *ndev) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 147262306a36Sopenharmony_ci struct rswitch_gwca_ts_info *ts_info, *ts_info2; 147362306a36Sopenharmony_ci unsigned long flags; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci netif_tx_stop_all_queues(ndev); 147662306a36Sopenharmony_ci bitmap_clear(rdev->priv->opened_ports, rdev->port, 1); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci if (bitmap_empty(rdev->priv->opened_ports, RSWITCH_NUM_PORTS)) 147962306a36Sopenharmony_ci iowrite32(GWCA_TS_IRQ_BIT, rdev->priv->addr + GWTSDID); 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci list_for_each_entry_safe(ts_info, ts_info2, &rdev->priv->gwca.ts_info_list, list) { 148262306a36Sopenharmony_ci if (ts_info->port != rdev->port) 148362306a36Sopenharmony_ci continue; 148462306a36Sopenharmony_ci dev_kfree_skb_irq(ts_info->skb); 148562306a36Sopenharmony_ci list_del(&ts_info->list); 148662306a36Sopenharmony_ci kfree(ts_info); 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci spin_lock_irqsave(&rdev->priv->lock, flags); 149062306a36Sopenharmony_ci rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false); 149162306a36Sopenharmony_ci rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false); 149262306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->priv->lock, flags); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci phy_stop(ndev->phydev); 149562306a36Sopenharmony_ci napi_disable(&rdev->napi); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci return 0; 149862306a36Sopenharmony_ci}; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cistatic netdev_tx_t rswitch_start_xmit(struct sk_buff *skb, struct net_device *ndev) 150162306a36Sopenharmony_ci{ 150262306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 150362306a36Sopenharmony_ci struct rswitch_gwca_queue *gq = rdev->tx_queue; 150462306a36Sopenharmony_ci netdev_tx_t ret = NETDEV_TX_OK; 150562306a36Sopenharmony_ci struct rswitch_ext_desc *desc; 150662306a36Sopenharmony_ci dma_addr_t dma_addr; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci if (rswitch_get_num_cur_queues(gq) >= gq->ring_size - 1) { 150962306a36Sopenharmony_ci netif_stop_subqueue(ndev, 0); 151062306a36Sopenharmony_ci return NETDEV_TX_BUSY; 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci if (skb_put_padto(skb, ETH_ZLEN)) 151462306a36Sopenharmony_ci return ret; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci dma_addr = dma_map_single(ndev->dev.parent, skb->data, skb->len, DMA_TO_DEVICE); 151762306a36Sopenharmony_ci if (dma_mapping_error(ndev->dev.parent, dma_addr)) 151862306a36Sopenharmony_ci goto err_kfree; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci gq->skbs[gq->cur] = skb; 152162306a36Sopenharmony_ci desc = &gq->tx_ring[gq->cur]; 152262306a36Sopenharmony_ci rswitch_desc_set_dptr(&desc->desc, dma_addr); 152362306a36Sopenharmony_ci desc->desc.info_ds = cpu_to_le16(skb->len); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci desc->info1 = cpu_to_le64(INFO1_DV(BIT(rdev->etha->index)) | 152662306a36Sopenharmony_ci INFO1_IPV(GWCA_IPV_NUM) | INFO1_FMT); 152762306a36Sopenharmony_ci if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { 152862306a36Sopenharmony_ci struct rswitch_gwca_ts_info *ts_info; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci ts_info = kzalloc(sizeof(*ts_info), GFP_ATOMIC); 153162306a36Sopenharmony_ci if (!ts_info) 153262306a36Sopenharmony_ci goto err_unmap; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 153562306a36Sopenharmony_ci rdev->ts_tag++; 153662306a36Sopenharmony_ci desc->info1 |= cpu_to_le64(INFO1_TSUN(rdev->ts_tag) | INFO1_TXC); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci ts_info->skb = skb_get(skb); 153962306a36Sopenharmony_ci ts_info->port = rdev->port; 154062306a36Sopenharmony_ci ts_info->tag = rdev->ts_tag; 154162306a36Sopenharmony_ci list_add_tail(&ts_info->list, &rdev->priv->gwca.ts_info_list); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci skb_tx_timestamp(skb); 154462306a36Sopenharmony_ci } 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci dma_wmb(); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci desc->desc.die_dt = DT_FSINGLE | DIE; 154962306a36Sopenharmony_ci wmb(); /* gq->cur must be incremented after die_dt was set */ 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci gq->cur = rswitch_next_queue_index(gq, true, 1); 155262306a36Sopenharmony_ci rswitch_modify(rdev->addr, GWTRC(gq->index), 0, BIT(gq->index % 32)); 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci return ret; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_cierr_unmap: 155762306a36Sopenharmony_ci dma_unmap_single(ndev->dev.parent, dma_addr, skb->len, DMA_TO_DEVICE); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cierr_kfree: 156062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci return ret; 156362306a36Sopenharmony_ci} 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_cistatic struct net_device_stats *rswitch_get_stats(struct net_device *ndev) 156662306a36Sopenharmony_ci{ 156762306a36Sopenharmony_ci return &ndev->stats; 156862306a36Sopenharmony_ci} 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_cistatic int rswitch_hwstamp_get(struct net_device *ndev, struct ifreq *req) 157162306a36Sopenharmony_ci{ 157262306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 157362306a36Sopenharmony_ci struct rcar_gen4_ptp_private *ptp_priv; 157462306a36Sopenharmony_ci struct hwtstamp_config config; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci ptp_priv = rdev->priv->ptp_priv; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci config.flags = 0; 157962306a36Sopenharmony_ci config.tx_type = ptp_priv->tstamp_tx_ctrl ? HWTSTAMP_TX_ON : 158062306a36Sopenharmony_ci HWTSTAMP_TX_OFF; 158162306a36Sopenharmony_ci switch (ptp_priv->tstamp_rx_ctrl & RCAR_GEN4_RXTSTAMP_TYPE) { 158262306a36Sopenharmony_ci case RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT: 158362306a36Sopenharmony_ci config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; 158462306a36Sopenharmony_ci break; 158562306a36Sopenharmony_ci case RCAR_GEN4_RXTSTAMP_TYPE_ALL: 158662306a36Sopenharmony_ci config.rx_filter = HWTSTAMP_FILTER_ALL; 158762306a36Sopenharmony_ci break; 158862306a36Sopenharmony_ci default: 158962306a36Sopenharmony_ci config.rx_filter = HWTSTAMP_FILTER_NONE; 159062306a36Sopenharmony_ci break; 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci return copy_to_user(req->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; 159462306a36Sopenharmony_ci} 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_cistatic int rswitch_hwstamp_set(struct net_device *ndev, struct ifreq *req) 159762306a36Sopenharmony_ci{ 159862306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 159962306a36Sopenharmony_ci u32 tstamp_rx_ctrl = RCAR_GEN4_RXTSTAMP_ENABLED; 160062306a36Sopenharmony_ci struct hwtstamp_config config; 160162306a36Sopenharmony_ci u32 tstamp_tx_ctrl; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci if (copy_from_user(&config, req->ifr_data, sizeof(config))) 160462306a36Sopenharmony_ci return -EFAULT; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci if (config.flags) 160762306a36Sopenharmony_ci return -EINVAL; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci switch (config.tx_type) { 161062306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 161162306a36Sopenharmony_ci tstamp_tx_ctrl = 0; 161262306a36Sopenharmony_ci break; 161362306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 161462306a36Sopenharmony_ci tstamp_tx_ctrl = RCAR_GEN4_TXTSTAMP_ENABLED; 161562306a36Sopenharmony_ci break; 161662306a36Sopenharmony_ci default: 161762306a36Sopenharmony_ci return -ERANGE; 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci switch (config.rx_filter) { 162162306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 162262306a36Sopenharmony_ci tstamp_rx_ctrl = 0; 162362306a36Sopenharmony_ci break; 162462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 162562306a36Sopenharmony_ci tstamp_rx_ctrl |= RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT; 162662306a36Sopenharmony_ci break; 162762306a36Sopenharmony_ci default: 162862306a36Sopenharmony_ci config.rx_filter = HWTSTAMP_FILTER_ALL; 162962306a36Sopenharmony_ci tstamp_rx_ctrl |= RCAR_GEN4_RXTSTAMP_TYPE_ALL; 163062306a36Sopenharmony_ci break; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci rdev->priv->ptp_priv->tstamp_tx_ctrl = tstamp_tx_ctrl; 163462306a36Sopenharmony_ci rdev->priv->ptp_priv->tstamp_rx_ctrl = tstamp_rx_ctrl; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci return copy_to_user(req->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; 163762306a36Sopenharmony_ci} 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_cistatic int rswitch_eth_ioctl(struct net_device *ndev, struct ifreq *req, int cmd) 164062306a36Sopenharmony_ci{ 164162306a36Sopenharmony_ci if (!netif_running(ndev)) 164262306a36Sopenharmony_ci return -EINVAL; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci switch (cmd) { 164562306a36Sopenharmony_ci case SIOCGHWTSTAMP: 164662306a36Sopenharmony_ci return rswitch_hwstamp_get(ndev, req); 164762306a36Sopenharmony_ci case SIOCSHWTSTAMP: 164862306a36Sopenharmony_ci return rswitch_hwstamp_set(ndev, req); 164962306a36Sopenharmony_ci default: 165062306a36Sopenharmony_ci return phy_mii_ioctl(ndev->phydev, req, cmd); 165162306a36Sopenharmony_ci } 165262306a36Sopenharmony_ci} 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_cistatic const struct net_device_ops rswitch_netdev_ops = { 165562306a36Sopenharmony_ci .ndo_open = rswitch_open, 165662306a36Sopenharmony_ci .ndo_stop = rswitch_stop, 165762306a36Sopenharmony_ci .ndo_start_xmit = rswitch_start_xmit, 165862306a36Sopenharmony_ci .ndo_get_stats = rswitch_get_stats, 165962306a36Sopenharmony_ci .ndo_eth_ioctl = rswitch_eth_ioctl, 166062306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 166162306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 166262306a36Sopenharmony_ci}; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_cistatic int rswitch_get_ts_info(struct net_device *ndev, struct ethtool_ts_info *info) 166562306a36Sopenharmony_ci{ 166662306a36Sopenharmony_ci struct rswitch_device *rdev = netdev_priv(ndev); 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci info->phc_index = ptp_clock_index(rdev->priv->ptp_priv->clock); 166962306a36Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 167062306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_SOFTWARE | 167162306a36Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE | 167262306a36Sopenharmony_ci SOF_TIMESTAMPING_TX_HARDWARE | 167362306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 167462306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 167562306a36Sopenharmony_ci info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); 167662306a36Sopenharmony_ci info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci return 0; 167962306a36Sopenharmony_ci} 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_cistatic const struct ethtool_ops rswitch_ethtool_ops = { 168262306a36Sopenharmony_ci .get_ts_info = rswitch_get_ts_info, 168362306a36Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 168462306a36Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 168562306a36Sopenharmony_ci}; 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_cistatic const struct of_device_id renesas_eth_sw_of_table[] = { 168862306a36Sopenharmony_ci { .compatible = "renesas,r8a779f0-ether-switch", }, 168962306a36Sopenharmony_ci { } 169062306a36Sopenharmony_ci}; 169162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, renesas_eth_sw_of_table); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_cistatic void rswitch_etha_init(struct rswitch_private *priv, int index) 169462306a36Sopenharmony_ci{ 169562306a36Sopenharmony_ci struct rswitch_etha *etha = &priv->etha[index]; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci memset(etha, 0, sizeof(*etha)); 169862306a36Sopenharmony_ci etha->index = index; 169962306a36Sopenharmony_ci etha->addr = priv->addr + RSWITCH_ETHA_OFFSET + index * RSWITCH_ETHA_SIZE; 170062306a36Sopenharmony_ci etha->coma_addr = priv->addr; 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci /* MPIC.PSMCS = (clk [MHz] / (MDC frequency [MHz] * 2) - 1. 170362306a36Sopenharmony_ci * Calculating PSMCS value as MDC frequency = 2.5MHz. So, multiply 170462306a36Sopenharmony_ci * both the numerator and the denominator by 10. 170562306a36Sopenharmony_ci */ 170662306a36Sopenharmony_ci etha->psmcs = clk_get_rate(priv->clk) / 100000 / (25 * 2) - 1; 170762306a36Sopenharmony_ci} 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_cistatic int rswitch_device_alloc(struct rswitch_private *priv, int index) 171062306a36Sopenharmony_ci{ 171162306a36Sopenharmony_ci struct platform_device *pdev = priv->pdev; 171262306a36Sopenharmony_ci struct rswitch_device *rdev; 171362306a36Sopenharmony_ci struct net_device *ndev; 171462306a36Sopenharmony_ci int err; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci if (index >= RSWITCH_NUM_PORTS) 171762306a36Sopenharmony_ci return -EINVAL; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci ndev = alloc_etherdev_mqs(sizeof(struct rswitch_device), 1, 1); 172062306a36Sopenharmony_ci if (!ndev) 172162306a36Sopenharmony_ci return -ENOMEM; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, &pdev->dev); 172462306a36Sopenharmony_ci ether_setup(ndev); 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci rdev = netdev_priv(ndev); 172762306a36Sopenharmony_ci rdev->ndev = ndev; 172862306a36Sopenharmony_ci rdev->priv = priv; 172962306a36Sopenharmony_ci priv->rdev[index] = rdev; 173062306a36Sopenharmony_ci rdev->port = index; 173162306a36Sopenharmony_ci rdev->etha = &priv->etha[index]; 173262306a36Sopenharmony_ci rdev->addr = priv->addr; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci ndev->base_addr = (unsigned long)rdev->addr; 173562306a36Sopenharmony_ci snprintf(ndev->name, IFNAMSIZ, "tsn%d", index); 173662306a36Sopenharmony_ci ndev->netdev_ops = &rswitch_netdev_ops; 173762306a36Sopenharmony_ci ndev->ethtool_ops = &rswitch_ethtool_ops; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci netif_napi_add(ndev, &rdev->napi, rswitch_poll); 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci rdev->np_port = rswitch_get_port_node(rdev); 174262306a36Sopenharmony_ci rdev->disabled = !rdev->np_port; 174362306a36Sopenharmony_ci err = of_get_ethdev_address(rdev->np_port, ndev); 174462306a36Sopenharmony_ci of_node_put(rdev->np_port); 174562306a36Sopenharmony_ci if (err) { 174662306a36Sopenharmony_ci if (is_valid_ether_addr(rdev->etha->mac_addr)) 174762306a36Sopenharmony_ci eth_hw_addr_set(ndev, rdev->etha->mac_addr); 174862306a36Sopenharmony_ci else 174962306a36Sopenharmony_ci eth_hw_addr_random(ndev); 175062306a36Sopenharmony_ci } 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci err = rswitch_etha_get_params(rdev); 175362306a36Sopenharmony_ci if (err < 0) 175462306a36Sopenharmony_ci goto out_get_params; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci if (rdev->priv->gwca.speed < rdev->etha->speed) 175762306a36Sopenharmony_ci rdev->priv->gwca.speed = rdev->etha->speed; 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci err = rswitch_rxdmac_alloc(ndev); 176062306a36Sopenharmony_ci if (err < 0) 176162306a36Sopenharmony_ci goto out_rxdmac; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci err = rswitch_txdmac_alloc(ndev); 176462306a36Sopenharmony_ci if (err < 0) 176562306a36Sopenharmony_ci goto out_txdmac; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci return 0; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ciout_txdmac: 177062306a36Sopenharmony_ci rswitch_rxdmac_free(ndev); 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ciout_rxdmac: 177362306a36Sopenharmony_ciout_get_params: 177462306a36Sopenharmony_ci netif_napi_del(&rdev->napi); 177562306a36Sopenharmony_ci free_netdev(ndev); 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci return err; 177862306a36Sopenharmony_ci} 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_cistatic void rswitch_device_free(struct rswitch_private *priv, int index) 178162306a36Sopenharmony_ci{ 178262306a36Sopenharmony_ci struct rswitch_device *rdev = priv->rdev[index]; 178362306a36Sopenharmony_ci struct net_device *ndev = rdev->ndev; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci rswitch_txdmac_free(ndev); 178662306a36Sopenharmony_ci rswitch_rxdmac_free(ndev); 178762306a36Sopenharmony_ci netif_napi_del(&rdev->napi); 178862306a36Sopenharmony_ci free_netdev(ndev); 178962306a36Sopenharmony_ci} 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_cistatic int rswitch_init(struct rswitch_private *priv) 179262306a36Sopenharmony_ci{ 179362306a36Sopenharmony_ci int i, err; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci for (i = 0; i < RSWITCH_NUM_PORTS; i++) 179662306a36Sopenharmony_ci rswitch_etha_init(priv, i); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci rswitch_clock_enable(priv); 179962306a36Sopenharmony_ci for (i = 0; i < RSWITCH_NUM_PORTS; i++) 180062306a36Sopenharmony_ci rswitch_etha_read_mac_address(&priv->etha[i]); 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci rswitch_reset(priv); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci rswitch_clock_enable(priv); 180562306a36Sopenharmony_ci rswitch_top_init(priv); 180662306a36Sopenharmony_ci err = rswitch_bpool_config(priv); 180762306a36Sopenharmony_ci if (err < 0) 180862306a36Sopenharmony_ci return err; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci rswitch_coma_init(priv); 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci err = rswitch_gwca_linkfix_alloc(priv); 181362306a36Sopenharmony_ci if (err < 0) 181462306a36Sopenharmony_ci return -ENOMEM; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci err = rswitch_gwca_ts_queue_alloc(priv); 181762306a36Sopenharmony_ci if (err < 0) 181862306a36Sopenharmony_ci goto err_ts_queue_alloc; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci for (i = 0; i < RSWITCH_NUM_PORTS; i++) { 182162306a36Sopenharmony_ci err = rswitch_device_alloc(priv, i); 182262306a36Sopenharmony_ci if (err < 0) { 182362306a36Sopenharmony_ci for (i--; i >= 0; i--) 182462306a36Sopenharmony_ci rswitch_device_free(priv, i); 182562306a36Sopenharmony_ci goto err_device_alloc; 182662306a36Sopenharmony_ci } 182762306a36Sopenharmony_ci } 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci rswitch_fwd_init(priv); 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci err = rcar_gen4_ptp_register(priv->ptp_priv, RCAR_GEN4_PTP_REG_LAYOUT_S4, 183262306a36Sopenharmony_ci RCAR_GEN4_PTP_CLOCK_S4); 183362306a36Sopenharmony_ci if (err < 0) 183462306a36Sopenharmony_ci goto err_ptp_register; 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci err = rswitch_gwca_request_irqs(priv); 183762306a36Sopenharmony_ci if (err < 0) 183862306a36Sopenharmony_ci goto err_gwca_request_irq; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci err = rswitch_gwca_ts_request_irqs(priv); 184162306a36Sopenharmony_ci if (err < 0) 184262306a36Sopenharmony_ci goto err_gwca_ts_request_irq; 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci err = rswitch_gwca_hw_init(priv); 184562306a36Sopenharmony_ci if (err < 0) 184662306a36Sopenharmony_ci goto err_gwca_hw_init; 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci err = rswitch_ether_port_init_all(priv); 184962306a36Sopenharmony_ci if (err) 185062306a36Sopenharmony_ci goto err_ether_port_init_all; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci rswitch_for_each_enabled_port(priv, i) { 185362306a36Sopenharmony_ci err = register_netdev(priv->rdev[i]->ndev); 185462306a36Sopenharmony_ci if (err) { 185562306a36Sopenharmony_ci rswitch_for_each_enabled_port_continue_reverse(priv, i) 185662306a36Sopenharmony_ci unregister_netdev(priv->rdev[i]->ndev); 185762306a36Sopenharmony_ci goto err_register_netdev; 185862306a36Sopenharmony_ci } 185962306a36Sopenharmony_ci } 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci rswitch_for_each_enabled_port(priv, i) 186262306a36Sopenharmony_ci netdev_info(priv->rdev[i]->ndev, "MAC address %pM\n", 186362306a36Sopenharmony_ci priv->rdev[i]->ndev->dev_addr); 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci return 0; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_cierr_register_netdev: 186862306a36Sopenharmony_ci rswitch_ether_port_deinit_all(priv); 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_cierr_ether_port_init_all: 187162306a36Sopenharmony_ci rswitch_gwca_hw_deinit(priv); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_cierr_gwca_hw_init: 187462306a36Sopenharmony_cierr_gwca_ts_request_irq: 187562306a36Sopenharmony_cierr_gwca_request_irq: 187662306a36Sopenharmony_ci rcar_gen4_ptp_unregister(priv->ptp_priv); 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_cierr_ptp_register: 187962306a36Sopenharmony_ci for (i = 0; i < RSWITCH_NUM_PORTS; i++) 188062306a36Sopenharmony_ci rswitch_device_free(priv, i); 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_cierr_device_alloc: 188362306a36Sopenharmony_ci rswitch_gwca_ts_queue_free(priv); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_cierr_ts_queue_alloc: 188662306a36Sopenharmony_ci rswitch_gwca_linkfix_free(priv); 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci return err; 188962306a36Sopenharmony_ci} 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_cistatic const struct soc_device_attribute rswitch_soc_no_speed_change[] = { 189262306a36Sopenharmony_ci { .soc_id = "r8a779f0", .revision = "ES1.0" }, 189362306a36Sopenharmony_ci { /* Sentinel */ } 189462306a36Sopenharmony_ci}; 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_cistatic int renesas_eth_sw_probe(struct platform_device *pdev) 189762306a36Sopenharmony_ci{ 189862306a36Sopenharmony_ci const struct soc_device_attribute *attr; 189962306a36Sopenharmony_ci struct rswitch_private *priv; 190062306a36Sopenharmony_ci struct resource *res; 190162306a36Sopenharmony_ci int ret; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "secure_base"); 190462306a36Sopenharmony_ci if (!res) { 190562306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid resource\n"); 190662306a36Sopenharmony_ci return -EINVAL; 190762306a36Sopenharmony_ci } 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 191062306a36Sopenharmony_ci if (!priv) 191162306a36Sopenharmony_ci return -ENOMEM; 191262306a36Sopenharmony_ci spin_lock_init(&priv->lock); 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci priv->clk = devm_clk_get(&pdev->dev, NULL); 191562306a36Sopenharmony_ci if (IS_ERR(priv->clk)) 191662306a36Sopenharmony_ci return PTR_ERR(priv->clk); 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci attr = soc_device_match(rswitch_soc_no_speed_change); 191962306a36Sopenharmony_ci if (attr) 192062306a36Sopenharmony_ci priv->etha_no_runtime_change = true; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci priv->ptp_priv = rcar_gen4_ptp_alloc(pdev); 192362306a36Sopenharmony_ci if (!priv->ptp_priv) 192462306a36Sopenharmony_ci return -ENOMEM; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 192762306a36Sopenharmony_ci priv->pdev = pdev; 192862306a36Sopenharmony_ci priv->addr = devm_ioremap_resource(&pdev->dev, res); 192962306a36Sopenharmony_ci if (IS_ERR(priv->addr)) 193062306a36Sopenharmony_ci return PTR_ERR(priv->addr); 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci priv->ptp_priv->addr = priv->addr + RCAR_GEN4_GPTP_OFFSET_S4; 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); 193562306a36Sopenharmony_ci if (ret < 0) { 193662306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 193762306a36Sopenharmony_ci if (ret < 0) 193862306a36Sopenharmony_ci return ret; 193962306a36Sopenharmony_ci } 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci priv->gwca.index = AGENT_INDEX_GWCA; 194262306a36Sopenharmony_ci priv->gwca.num_queues = min(RSWITCH_NUM_PORTS * NUM_QUEUES_PER_NDEV, 194362306a36Sopenharmony_ci RSWITCH_MAX_NUM_QUEUES); 194462306a36Sopenharmony_ci priv->gwca.queues = devm_kcalloc(&pdev->dev, priv->gwca.num_queues, 194562306a36Sopenharmony_ci sizeof(*priv->gwca.queues), GFP_KERNEL); 194662306a36Sopenharmony_ci if (!priv->gwca.queues) 194762306a36Sopenharmony_ci return -ENOMEM; 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 195062306a36Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci ret = rswitch_init(priv); 195362306a36Sopenharmony_ci if (ret < 0) { 195462306a36Sopenharmony_ci pm_runtime_put(&pdev->dev); 195562306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 195662306a36Sopenharmony_ci return ret; 195762306a36Sopenharmony_ci } 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci device_set_wakeup_capable(&pdev->dev, 1); 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci return ret; 196262306a36Sopenharmony_ci} 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_cistatic void rswitch_deinit(struct rswitch_private *priv) 196562306a36Sopenharmony_ci{ 196662306a36Sopenharmony_ci int i; 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci rswitch_gwca_hw_deinit(priv); 196962306a36Sopenharmony_ci rcar_gen4_ptp_unregister(priv->ptp_priv); 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci rswitch_for_each_enabled_port(priv, i) { 197262306a36Sopenharmony_ci struct rswitch_device *rdev = priv->rdev[i]; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci unregister_netdev(rdev->ndev); 197562306a36Sopenharmony_ci rswitch_ether_port_deinit_one(rdev); 197662306a36Sopenharmony_ci phy_exit(priv->rdev[i]->serdes); 197762306a36Sopenharmony_ci } 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci for (i = 0; i < RSWITCH_NUM_PORTS; i++) 198062306a36Sopenharmony_ci rswitch_device_free(priv, i); 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci rswitch_gwca_ts_queue_free(priv); 198362306a36Sopenharmony_ci rswitch_gwca_linkfix_free(priv); 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci rswitch_clock_disable(priv); 198662306a36Sopenharmony_ci} 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_cistatic int renesas_eth_sw_remove(struct platform_device *pdev) 198962306a36Sopenharmony_ci{ 199062306a36Sopenharmony_ci struct rswitch_private *priv = platform_get_drvdata(pdev); 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci rswitch_deinit(priv); 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci pm_runtime_put(&pdev->dev); 199562306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci platform_set_drvdata(pdev, NULL); 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci return 0; 200062306a36Sopenharmony_ci} 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_cistatic struct platform_driver renesas_eth_sw_driver_platform = { 200362306a36Sopenharmony_ci .probe = renesas_eth_sw_probe, 200462306a36Sopenharmony_ci .remove = renesas_eth_sw_remove, 200562306a36Sopenharmony_ci .driver = { 200662306a36Sopenharmony_ci .name = "renesas_eth_sw", 200762306a36Sopenharmony_ci .of_match_table = renesas_eth_sw_of_table, 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci}; 201062306a36Sopenharmony_cimodule_platform_driver(renesas_eth_sw_driver_platform); 201162306a36Sopenharmony_ciMODULE_AUTHOR("Yoshihiro Shimoda"); 201262306a36Sopenharmony_ciMODULE_DESCRIPTION("Renesas Ethernet Switch device driver"); 201362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2014