This patch adds network driver for APM X-Gene SoC ethernet.
Signed-off-by: Iyappan Subramanian <isubramanian@xxxxxxx>
Signed-off-by: Ravi Patel <rapatel@xxxxxxx>
Signed-off-by: Keyur Chudgar <kchudgar@xxxxxxx>
---
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/apm/Kconfig | 1 +
drivers/net/ethernet/apm/Makefile | 5 +
drivers/net/ethernet/apm/xgene/Kconfig | 9 +
drivers/net/ethernet/apm/xgene/Makefile | 6 +
drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 807 +++++++++++++++++++
drivers/net/ethernet/apm/xgene/xgene_enet_hw.h | 353 ++++++++
drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 936 ++++++++++++++++++++++
drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 131 +++
10 files changed, 2250 insertions(+)
create mode 100644 drivers/net/ethernet/apm/Kconfig
create mode 100644 drivers/net/ethernet/apm/Makefile
create mode 100644 drivers/net/ethernet/apm/xgene/Kconfig
create mode 100644 drivers/net/ethernet/apm/xgene/Makefile
create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_main.c
create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_main.h
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
new file mode 100644
index 0000000..421a841
--- /dev/null
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
+
+struct xgene_enet_desc_ring *xgene_enet_setup_ring(
+ struct xgene_enet_desc_ring *ring)
+{
+ u32 size = ring->size;
+ u32 i, data;
+ u64 *desc;
+
+ xgene_enet_clr_ring_state(ring);
+ xgene_enet_set_ring_state(ring);
+ xgene_enet_set_ring_id(ring);
+
+ ring->slots = IS_FP(ring->id) ? size / 16 : size / 32;
+
+ if (IS_FP(ring->id) || RING_OWNER(ring) != RING_OWNER_CPU)
+ goto out;
+
+ for (i = 0; i < ring->slots; i++) {
+ desc = (u64 *)&ring->desc[i];
+ desc[EMPTY_SLOT_INDEX] = EMPTY_SLOT;
+ }
+
+ xgene_enet_ring_rd32(ring, CSR_RING_NE_INT_MODE, &data);
+ data |= (1 << (31 - RING_BUFNUM(ring)));
+ xgene_enet_ring_wr32(ring, CSR_RING_NE_INT_MODE, data);
+
+out:
+ return ring;
+}
+
+void xgene_enet_clear_ring(struct xgene_enet_desc_ring *ring)
+{
+ u32 data;
+
+ if (IS_FP(ring->id) || RING_OWNER(ring) != RING_OWNER_CPU)
+ goto out;
+
+ xgene_enet_ring_rd32(ring, CSR_RING_NE_INT_MODE, &data);
+ data &= ~(u32) (1 << (31 - RING_BUFNUM(ring)));
+ xgene_enet_ring_wr32(ring, CSR_RING_NE_INT_MODE, data);
+
+out:
+ xgene_enet_clr_desc_ring_id(ring);
+ xgene_enet_clr_ring_state(ring);
+}
+
+
+static int xgene_enet_phy_connect(struct net_device *ndev)
+{
+ struct xgene_enet_pdata *pdata = netdev_priv(ndev);
+ struct device_node *phy_np;
+ struct phy_device *phy_dev;
+ int ret = 0;
+
+ struct device *dev = &pdata->pdev->dev;
+
+ phy_np = of_parse_phandle(dev->of_node, "phy-handle", 0);
+
+ if (!phy_np) {
+ netdev_dbg(ndev, "No phy-handle found\n");
+ ret = -ENODEV;
+ }
+
+ phy_dev = of_phy_connect(ndev, phy_np, &xgene_enet_adjust_link,
+ 0, pdata->phy_mode);
+ if (!phy_dev) {
+ netdev_err(ndev, "Could not connect to PHY\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+out:
+ pdata->phy_link = 0;
+ pdata->phy_speed = 0;
+ pdata->phy_dev = phy_dev;
+
+ return ret;
+}
+
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
new file mode 100644
index 0000000..a4c0a14
--- /dev/null
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
+
+static inline u32 get_bits(u32 val, u32 start, u32 end)
+{
+ return (val & GENMASK(end, start)) >> start;
+}
+
+#define CSR_RING_ID 0x00000008
+#define OVERWRITE BIT(31)
+#define IS_BUFFER_POOL BIT(20)
+#define PREFETCH_BUF_EN BIT(21)
+#define CSR_RING_ID_BUF 0x0000000c
+#define CSR_RING_NE_INT_MODE 0x0000017c
+#define CSR_RING_CONFIG 0x0000006c
+#define CSR_RING_WR_BASE 0x00000070
+#define NUM_RING_CONFIG 5
+#define BUFPOOL_MODE 3
+#define RM3 3
+#define INC_DEC_CMD_ADDR 0x2c
+#define IS_FP(x) ((x & 0x0020) ? 1 : 0)
+#define UDP_HDR_SIZE 2
+
+#define CREATE_MASK(pos, len) GENMASK(pos+len-1, pos)
+#define CREATE_MASK_ULL(pos, len) GENMASK_ULL(pos+len-1, pos)
+
+/* Empty slot soft signature */
+#define EMPTY_SLOT_INDEX 1
+#define EMPTY_SLOT ~0ULL
+
+#define RING_BUFNUM(q) (q->id & 0x003F)
+#define RING_OWNER(q) ((q->id & 0x03C0) >> 6)
+#define BUF_LEN_CODE_2K 0x5000
+
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
new file mode 100644
index 0000000..0feb571
--- /dev/null
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+
+static int xgene_enet_create_desc_rings(struct net_device *ndev)
+{
+ struct xgene_enet_pdata *pdata = netdev_priv(ndev);
+ struct device *dev = &pdata->pdev->dev;
+ struct xgene_enet_desc_ring *rx_ring, *tx_ring, *cp_ring;
+ struct xgene_enet_desc_ring *buf_pool = NULL;
+ u32 ring_num = 0;
+ u32 ring_id;
+ int ret = 0;
+
+ /* allocate rx descriptor ring */
+ ring_id = (RING_OWNER_CPU << 6) | RING_BUFNUM_REGULAR;
+ rx_ring = xgene_enet_create_desc_ring(ndev, ring_num++,
+ RING_CFGSIZE_16KB, ring_id);
+ if (IS_ERR_OR_NULL(rx_ring)) {
+ ret = PTR_ERR(rx_ring);
+ goto err;
+ }
+
+ /* allocate buffer pool for receiving packets */
+ ring_id = (RING_OWNER_ETH0 << 6) | RING_BUFNUM_BUFPOOL;
+ buf_pool = xgene_enet_create_desc_ring(ndev, ring_num++,
+ RING_CFGSIZE_2KB, ring_id);
+ if (IS_ERR_OR_NULL(buf_pool)) {
+ ret = PTR_ERR(buf_pool);
+ goto err;
+ }
+
+ rx_ring->nbufpool = NUM_BUFPOOL;
+ rx_ring->buf_pool = buf_pool;
+ rx_ring->irq = pdata->rx_irq;
+ buf_pool->rx_skb = devm_kcalloc(dev, buf_pool->slots,
+ sizeof(struct sk_buff *), GFP_KERNEL);
+ if (!buf_pool->rx_skb) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ buf_pool->dst_ring_num = xgene_enet_dst_ring_num(buf_pool);
+ rx_ring->buf_pool = buf_pool;
+ pdata->rx_ring = rx_ring;
+
+ /* allocate tx descriptor ring */
+ ring_id = (RING_OWNER_ETH0 << 6) | RING_BUFNUM_REGULAR;
+ tx_ring = xgene_enet_create_desc_ring(ndev, ring_num++,
+ RING_CFGSIZE_16KB, ring_id);
+ if (IS_ERR_OR_NULL(tx_ring)) {
+ ret = PTR_ERR(tx_ring);
+ goto err;
+ }
+ pdata->tx_ring = tx_ring;
+
+ cp_ring = pdata->rx_ring;
+ cp_ring->cp_skb = devm_kcalloc(dev, tx_ring->slots,
+ sizeof(struct sk_buff *), GFP_KERNEL);
+ if (!cp_ring->cp_skb) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ pdata->tx_ring->cp_ring = cp_ring;
+ pdata->tx_ring->dst_ring_num = xgene_enet_dst_ring_num(cp_ring);
+
+ pdata->tx_qcnt_hi = pdata->tx_ring->slots / 2;
+ pdata->cp_qcnt_hi = pdata->rx_ring->slots / 2;
+ pdata->cp_qcnt_low = pdata->cp_qcnt_hi / 2;
+
+ return 0;
+
+err:
+ xgene_enet_delete_desc_rings(pdata);
+ return ret;
+}
+
+
+static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
+{
+ struct platform_device *pdev;
+ struct net_device *ndev;
+ struct device *dev;
+ struct resource *res;
+ void *base_addr;
+ const char *mac;
+ int ret = 0;
+
+ pdev = pdata->pdev;
+ dev = &pdev->dev;
+ ndev = pdata->ndev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "Resource IORESOURCE_MEM 0 not defined\n");
+ ret = -ENODEV;
+ goto out;
+ }
+ pdata->base_addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pdata->base_addr)) {
+ dev_err(dev, "Unable to retrieve ENET Port CSR region\n");
+ return PTR_ERR(pdata->base_addr);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(dev, "Resource IORESOURCE_MEM 1 not defined\n");
+ ret = -ENODEV;
+ goto out;
+ }
+ pdata->ring_csr_addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pdata->ring_csr_addr)) {
+ dev_err(dev, "Unable to retrieve ENET Ring CSR region\n");
+ return PTR_ERR(pdata->ring_csr_addr);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!res) {
+ dev_err(dev, "Resource IORESOURCE_MEM 2 not defined\n");
+ ret = -ENODEV;
+ goto out;
+ }
+ pdata->ring_cmd_addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pdata->ring_cmd_addr)) {
+ dev_err(dev, "Unable to retrieve ENET Ring command region\n");
+ return PTR_ERR(pdata->ring_cmd_addr);
+ }
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret <= 0) {
+ dev_err(dev, "Unable to get ENET Rx IRQ\n");
+ goto out;
+ }
+ pdata->rx_irq = ret;
+
+ mac = of_get_mac_address(dev->of_node);
+ if (mac)
+ memcpy(ndev->dev_addr, mac, ndev->addr_len);
+ else
+ eth_hw_addr_random(ndev);
+ memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len);
+
+ pdata->phy_mode = of_get_phy_mode(pdev->dev.of_node);
+ if (pdata->phy_mode < 0) {
+ dev_err(dev, "Incorrect phy-connection-type in DTS\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pdata->clk = devm_clk_get(&pdev->dev, NULL);
+ ret = IS_ERR(pdata->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "can't get clock\n");
+ goto out;
+ }
+
+ base_addr = pdata->base_addr;
+ pdata->eth_csr_addr = base_addr + BLOCK_ETH_CSR_OFFSET;
+ pdata->eth_ring_if_addr = base_addr + BLOCK_ETH_RING_IF_OFFSET;
+ pdata->eth_diag_csr_addr = base_addr + BLOCK_ETH_DIAG_CSR_OFFSET;
+ pdata->mcx_mac_addr = base_addr + BLOCK_ETH_MAC_OFFSET;
+ pdata->mcx_stats_addr = base_addr + BLOCK_ETH_STATS_OFFSET;
+ pdata->mcx_mac_csr_addr = base_addr + BLOCK_ETH_MAC_CSR_OFFSET;
+ pdata->rx_buff_cnt = NUM_PKT_BUF;
+out:
+ return ret;
+}
+
+static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata)
+{
+ struct net_device *ndev = pdata->ndev;
+ struct xgene_enet_desc_ring *buf_pool;
+ int ret = 0;
+
+ xgene_enet_reset(pdata);
+
+ xgene_gmac_tx_disable(pdata);
+ xgene_gmac_rx_disable(pdata);
+
+ ret = xgene_enet_create_desc_rings(ndev);
+ if (ret) {
+ netdev_err(ndev, "Error in ring configuration\n");
+ goto out;
+ }
+
+ /* setup buffer pool */
+ buf_pool = pdata->rx_ring->buf_pool;
+ xgene_enet_init_bufpool(buf_pool);
+ ret = xgene_enet_refill_bufpool(buf_pool, pdata->rx_buff_cnt);
+ if (ret)
+ goto out;
+
+ xgene_enet_cle_bypass(pdata, xgene_enet_dst_ring_num(pdata->rx_ring),
+ RING_BUFNUM(buf_pool) - 0x20, 0);
+ xgene_gmac_init(pdata, SPEED_1000);
+out:
+ return ret;
+}