1511f6c1aSRoger Quadros // SPDX-License-Identifier: GPL-2.0 2511f6c1aSRoger Quadros 3511f6c1aSRoger Quadros /* Texas Instruments ICSSM Ethernet Driver 4511f6c1aSRoger Quadros * 5511f6c1aSRoger Quadros * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/ 6511f6c1aSRoger Quadros * 7511f6c1aSRoger Quadros */ 8511f6c1aSRoger Quadros 9511f6c1aSRoger Quadros #include <linux/etherdevice.h> 10511f6c1aSRoger Quadros #include <linux/genalloc.h> 11511f6c1aSRoger Quadros #include <linux/if_bridge.h> 12511f6c1aSRoger Quadros #include <linux/if_hsr.h> 13511f6c1aSRoger Quadros #include <linux/if_vlan.h> 14511f6c1aSRoger Quadros #include <linux/interrupt.h> 15511f6c1aSRoger Quadros #include <linux/kernel.h> 16511f6c1aSRoger Quadros #include <linux/mfd/syscon.h> 17511f6c1aSRoger Quadros #include <linux/module.h> 18511f6c1aSRoger Quadros #include <linux/net_tstamp.h> 19511f6c1aSRoger Quadros #include <linux/of.h> 20511f6c1aSRoger Quadros #include <linux/of_irq.h> 21511f6c1aSRoger Quadros #include <linux/of_mdio.h> 22511f6c1aSRoger Quadros #include <linux/of_net.h> 23511f6c1aSRoger Quadros #include <linux/platform_device.h> 24511f6c1aSRoger Quadros #include <linux/phy.h> 25511f6c1aSRoger Quadros #include <linux/remoteproc/pruss.h> 26511f6c1aSRoger Quadros #include <linux/ptp_classify.h> 27511f6c1aSRoger Quadros #include <linux/regmap.h> 28511f6c1aSRoger Quadros #include <linux/remoteproc.h> 29511f6c1aSRoger Quadros #include <net/pkt_cls.h> 30511f6c1aSRoger Quadros 31511f6c1aSRoger Quadros #include "icssm_prueth.h" 32a99b5657SRoger Quadros #include "../icssg/icssg_mii_rt.h" 33a99b5657SRoger Quadros 34a99b5657SRoger Quadros #define OCMC_RAM_SIZE (SZ_64K) 35a99b5657SRoger Quadros 36a99b5657SRoger Quadros #define TX_START_DELAY 0x40 37a99b5657SRoger Quadros #define TX_CLK_DELAY_100M 0x6 38*e15472e8SRoger Quadros #define HR_TIMER_TX_DELAY_US 100 39*e15472e8SRoger Quadros 40*e15472e8SRoger Quadros static void icssm_prueth_write_reg(struct prueth *prueth, 41*e15472e8SRoger Quadros enum prueth_mem region, 42*e15472e8SRoger Quadros unsigned int reg, u32 val) 43*e15472e8SRoger Quadros { 44*e15472e8SRoger Quadros writel_relaxed(val, prueth->mem[region].va + reg); 45*e15472e8SRoger Quadros } 46a99b5657SRoger Quadros 47a99b5657SRoger Quadros /* Below macro is for 1528 Byte Frame support, to Allow even with 48a99b5657SRoger Quadros * Redundancy tag 49a99b5657SRoger Quadros */ 50a99b5657SRoger Quadros #define PRUSS_MII_RT_RX_FRMS_MAX_SUPPORT_EMAC (VLAN_ETH_FRAME_LEN + \ 51a99b5657SRoger Quadros ETH_FCS_LEN + \ 52a99b5657SRoger Quadros ICSSM_LRE_TAG_SIZE) 53a99b5657SRoger Quadros 54a99b5657SRoger Quadros /* ensure that order of PRUSS mem regions is same as enum prueth_mem */ 55a99b5657SRoger Quadros static enum pruss_mem pruss_mem_ids[] = { PRUSS_MEM_DRAM0, PRUSS_MEM_DRAM1, 56a99b5657SRoger Quadros PRUSS_MEM_SHRD_RAM2 }; 57a99b5657SRoger Quadros 58a99b5657SRoger Quadros static const struct prueth_queue_info queue_infos[][NUM_QUEUES] = { 59a99b5657SRoger Quadros [PRUETH_PORT_QUEUE_HOST] = { 60a99b5657SRoger Quadros [PRUETH_QUEUE1] = { 61a99b5657SRoger Quadros P0_Q1_BUFFER_OFFSET, 62a99b5657SRoger Quadros HOST_QUEUE_DESC_OFFSET, 63a99b5657SRoger Quadros P0_Q1_BD_OFFSET, 64a99b5657SRoger Quadros P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE), 65a99b5657SRoger Quadros }, 66a99b5657SRoger Quadros [PRUETH_QUEUE2] = { 67a99b5657SRoger Quadros P0_Q2_BUFFER_OFFSET, 68a99b5657SRoger Quadros HOST_QUEUE_DESC_OFFSET + 8, 69a99b5657SRoger Quadros P0_Q2_BD_OFFSET, 70a99b5657SRoger Quadros P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE), 71a99b5657SRoger Quadros }, 72a99b5657SRoger Quadros [PRUETH_QUEUE3] = { 73a99b5657SRoger Quadros P0_Q3_BUFFER_OFFSET, 74a99b5657SRoger Quadros HOST_QUEUE_DESC_OFFSET + 16, 75a99b5657SRoger Quadros P0_Q3_BD_OFFSET, 76a99b5657SRoger Quadros P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE), 77a99b5657SRoger Quadros }, 78a99b5657SRoger Quadros [PRUETH_QUEUE4] = { 79a99b5657SRoger Quadros P0_Q4_BUFFER_OFFSET, 80a99b5657SRoger Quadros HOST_QUEUE_DESC_OFFSET + 24, 81a99b5657SRoger Quadros P0_Q4_BD_OFFSET, 82a99b5657SRoger Quadros P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE), 83a99b5657SRoger Quadros }, 84a99b5657SRoger Quadros }, 85a99b5657SRoger Quadros [PRUETH_PORT_QUEUE_MII0] = { 86a99b5657SRoger Quadros [PRUETH_QUEUE1] = { 87a99b5657SRoger Quadros P1_Q1_BUFFER_OFFSET, 88a99b5657SRoger Quadros P1_Q1_BUFFER_OFFSET + ((QUEUE_1_SIZE - 1) * 89a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 90a99b5657SRoger Quadros P1_Q1_BD_OFFSET, 91a99b5657SRoger Quadros P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), 92a99b5657SRoger Quadros }, 93a99b5657SRoger Quadros [PRUETH_QUEUE2] = { 94a99b5657SRoger Quadros P1_Q2_BUFFER_OFFSET, 95a99b5657SRoger Quadros P1_Q2_BUFFER_OFFSET + ((QUEUE_2_SIZE - 1) * 96a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 97a99b5657SRoger Quadros P1_Q2_BD_OFFSET, 98a99b5657SRoger Quadros P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), 99a99b5657SRoger Quadros }, 100a99b5657SRoger Quadros [PRUETH_QUEUE3] = { 101a99b5657SRoger Quadros P1_Q3_BUFFER_OFFSET, 102a99b5657SRoger Quadros P1_Q3_BUFFER_OFFSET + ((QUEUE_3_SIZE - 1) * 103a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 104a99b5657SRoger Quadros P1_Q3_BD_OFFSET, 105a99b5657SRoger Quadros P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), 106a99b5657SRoger Quadros }, 107a99b5657SRoger Quadros [PRUETH_QUEUE4] = { 108a99b5657SRoger Quadros P1_Q4_BUFFER_OFFSET, 109a99b5657SRoger Quadros P1_Q4_BUFFER_OFFSET + ((QUEUE_4_SIZE - 1) * 110a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 111a99b5657SRoger Quadros P1_Q4_BD_OFFSET, 112a99b5657SRoger Quadros P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), 113a99b5657SRoger Quadros }, 114a99b5657SRoger Quadros }, 115a99b5657SRoger Quadros [PRUETH_PORT_QUEUE_MII1] = { 116a99b5657SRoger Quadros [PRUETH_QUEUE1] = { 117a99b5657SRoger Quadros P2_Q1_BUFFER_OFFSET, 118a99b5657SRoger Quadros P2_Q1_BUFFER_OFFSET + ((QUEUE_1_SIZE - 1) * 119a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 120a99b5657SRoger Quadros P2_Q1_BD_OFFSET, 121a99b5657SRoger Quadros P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), 122a99b5657SRoger Quadros }, 123a99b5657SRoger Quadros [PRUETH_QUEUE2] = { 124a99b5657SRoger Quadros P2_Q2_BUFFER_OFFSET, 125a99b5657SRoger Quadros P2_Q2_BUFFER_OFFSET + ((QUEUE_2_SIZE - 1) * 126a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 127a99b5657SRoger Quadros P2_Q2_BD_OFFSET, 128a99b5657SRoger Quadros P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), 129a99b5657SRoger Quadros }, 130a99b5657SRoger Quadros [PRUETH_QUEUE3] = { 131a99b5657SRoger Quadros P2_Q3_BUFFER_OFFSET, 132a99b5657SRoger Quadros P2_Q3_BUFFER_OFFSET + ((QUEUE_3_SIZE - 1) * 133a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 134a99b5657SRoger Quadros P2_Q3_BD_OFFSET, 135a99b5657SRoger Quadros P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), 136a99b5657SRoger Quadros }, 137a99b5657SRoger Quadros [PRUETH_QUEUE4] = { 138a99b5657SRoger Quadros P2_Q4_BUFFER_OFFSET, 139a99b5657SRoger Quadros P2_Q4_BUFFER_OFFSET + ((QUEUE_4_SIZE - 1) * 140a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 141a99b5657SRoger Quadros P2_Q4_BD_OFFSET, 142a99b5657SRoger Quadros P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), 143a99b5657SRoger Quadros }, 144a99b5657SRoger Quadros }, 145a99b5657SRoger Quadros }; 146a99b5657SRoger Quadros 147a99b5657SRoger Quadros static const struct prueth_queue_desc queue_descs[][NUM_QUEUES] = { 148a99b5657SRoger Quadros [PRUETH_PORT_QUEUE_HOST] = { 149a99b5657SRoger Quadros { .rd_ptr = P0_Q1_BD_OFFSET, .wr_ptr = P0_Q1_BD_OFFSET, }, 150a99b5657SRoger Quadros { .rd_ptr = P0_Q2_BD_OFFSET, .wr_ptr = P0_Q2_BD_OFFSET, }, 151a99b5657SRoger Quadros { .rd_ptr = P0_Q3_BD_OFFSET, .wr_ptr = P0_Q3_BD_OFFSET, }, 152a99b5657SRoger Quadros { .rd_ptr = P0_Q4_BD_OFFSET, .wr_ptr = P0_Q4_BD_OFFSET, }, 153a99b5657SRoger Quadros }, 154a99b5657SRoger Quadros [PRUETH_PORT_QUEUE_MII0] = { 155a99b5657SRoger Quadros { .rd_ptr = P1_Q1_BD_OFFSET, .wr_ptr = P1_Q1_BD_OFFSET, }, 156a99b5657SRoger Quadros { .rd_ptr = P1_Q2_BD_OFFSET, .wr_ptr = P1_Q2_BD_OFFSET, }, 157a99b5657SRoger Quadros { .rd_ptr = P1_Q3_BD_OFFSET, .wr_ptr = P1_Q3_BD_OFFSET, }, 158a99b5657SRoger Quadros { .rd_ptr = P1_Q4_BD_OFFSET, .wr_ptr = P1_Q4_BD_OFFSET, }, 159a99b5657SRoger Quadros }, 160a99b5657SRoger Quadros [PRUETH_PORT_QUEUE_MII1] = { 161a99b5657SRoger Quadros { .rd_ptr = P2_Q1_BD_OFFSET, .wr_ptr = P2_Q1_BD_OFFSET, }, 162a99b5657SRoger Quadros { .rd_ptr = P2_Q2_BD_OFFSET, .wr_ptr = P2_Q2_BD_OFFSET, }, 163a99b5657SRoger Quadros { .rd_ptr = P2_Q3_BD_OFFSET, .wr_ptr = P2_Q3_BD_OFFSET, }, 164a99b5657SRoger Quadros { .rd_ptr = P2_Q4_BD_OFFSET, .wr_ptr = P2_Q4_BD_OFFSET, }, 165a99b5657SRoger Quadros } 166a99b5657SRoger Quadros }; 167a99b5657SRoger Quadros 168a99b5657SRoger Quadros static void icssm_prueth_hostconfig(struct prueth *prueth) 169a99b5657SRoger Quadros { 170a99b5657SRoger Quadros void __iomem *sram_base = prueth->mem[PRUETH_MEM_SHARED_RAM].va; 171a99b5657SRoger Quadros void __iomem *sram; 172a99b5657SRoger Quadros 173a99b5657SRoger Quadros /* queue size lookup table */ 174a99b5657SRoger Quadros sram = sram_base + HOST_QUEUE_SIZE_ADDR; 175a99b5657SRoger Quadros writew(HOST_QUEUE_1_SIZE, sram); 176a99b5657SRoger Quadros writew(HOST_QUEUE_2_SIZE, sram + 2); 177a99b5657SRoger Quadros writew(HOST_QUEUE_3_SIZE, sram + 4); 178a99b5657SRoger Quadros writew(HOST_QUEUE_4_SIZE, sram + 6); 179a99b5657SRoger Quadros 180a99b5657SRoger Quadros /* queue information table */ 181a99b5657SRoger Quadros sram = sram_base + HOST_Q1_RX_CONTEXT_OFFSET; 182a99b5657SRoger Quadros memcpy_toio(sram, queue_infos[PRUETH_PORT_QUEUE_HOST], 183a99b5657SRoger Quadros sizeof(queue_infos[PRUETH_PORT_QUEUE_HOST])); 184a99b5657SRoger Quadros 185a99b5657SRoger Quadros /* buffer offset table */ 186a99b5657SRoger Quadros sram = sram_base + HOST_QUEUE_OFFSET_ADDR; 187a99b5657SRoger Quadros writew(P0_Q1_BUFFER_OFFSET, sram); 188a99b5657SRoger Quadros writew(P0_Q2_BUFFER_OFFSET, sram + 2); 189a99b5657SRoger Quadros writew(P0_Q3_BUFFER_OFFSET, sram + 4); 190a99b5657SRoger Quadros writew(P0_Q4_BUFFER_OFFSET, sram + 6); 191a99b5657SRoger Quadros 192a99b5657SRoger Quadros /* buffer descriptor offset table*/ 193a99b5657SRoger Quadros sram = sram_base + HOST_QUEUE_DESCRIPTOR_OFFSET_ADDR; 194a99b5657SRoger Quadros writew(P0_Q1_BD_OFFSET, sram); 195a99b5657SRoger Quadros writew(P0_Q2_BD_OFFSET, sram + 2); 196a99b5657SRoger Quadros writew(P0_Q3_BD_OFFSET, sram + 4); 197a99b5657SRoger Quadros writew(P0_Q4_BD_OFFSET, sram + 6); 198a99b5657SRoger Quadros 199a99b5657SRoger Quadros /* queue table */ 200a99b5657SRoger Quadros sram = sram_base + HOST_QUEUE_DESC_OFFSET; 201a99b5657SRoger Quadros memcpy_toio(sram, queue_descs[PRUETH_PORT_QUEUE_HOST], 202a99b5657SRoger Quadros sizeof(queue_descs[PRUETH_PORT_QUEUE_HOST])); 203a99b5657SRoger Quadros } 204a99b5657SRoger Quadros 205a99b5657SRoger Quadros static void icssm_prueth_mii_init(struct prueth *prueth) 206a99b5657SRoger Quadros { 207a99b5657SRoger Quadros struct regmap *mii_rt; 208a99b5657SRoger Quadros u32 rxcfg_reg, rxcfg; 209a99b5657SRoger Quadros u32 txcfg_reg, txcfg; 210a99b5657SRoger Quadros 211a99b5657SRoger Quadros mii_rt = prueth->mii_rt; 212a99b5657SRoger Quadros 213a99b5657SRoger Quadros rxcfg = PRUSS_MII_RT_RXCFG_RX_ENABLE | 214a99b5657SRoger Quadros PRUSS_MII_RT_RXCFG_RX_DATA_RDY_MODE_DIS | 215a99b5657SRoger Quadros PRUSS_MII_RT_RXCFG_RX_L2_EN | 216a99b5657SRoger Quadros PRUSS_MII_RT_RXCFG_RX_CUT_PREAMBLE | 217a99b5657SRoger Quadros PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS; 218a99b5657SRoger Quadros 219a99b5657SRoger Quadros /* Configuration of Port 0 Rx */ 220a99b5657SRoger Quadros rxcfg_reg = PRUSS_MII_RT_RXCFG0; 221a99b5657SRoger Quadros 222a99b5657SRoger Quadros regmap_write(mii_rt, rxcfg_reg, rxcfg); 223a99b5657SRoger Quadros 224a99b5657SRoger Quadros /* Configuration of Port 1 Rx */ 225a99b5657SRoger Quadros rxcfg_reg = PRUSS_MII_RT_RXCFG1; 226a99b5657SRoger Quadros 227a99b5657SRoger Quadros rxcfg |= PRUSS_MII_RT_RXCFG_RX_MUX_SEL; 228a99b5657SRoger Quadros 229a99b5657SRoger Quadros regmap_write(mii_rt, rxcfg_reg, rxcfg); 230a99b5657SRoger Quadros 231a99b5657SRoger Quadros txcfg = PRUSS_MII_RT_TXCFG_TX_ENABLE | 232a99b5657SRoger Quadros PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE | 233a99b5657SRoger Quadros PRUSS_MII_RT_TXCFG_TX_32_MODE_EN | 234a99b5657SRoger Quadros (TX_START_DELAY << PRUSS_MII_RT_TXCFG_TX_START_DELAY_SHIFT) | 235a99b5657SRoger Quadros (TX_CLK_DELAY_100M << PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT); 236a99b5657SRoger Quadros 237a99b5657SRoger Quadros /* Configuration of Port 0 Tx */ 238a99b5657SRoger Quadros txcfg_reg = PRUSS_MII_RT_TXCFG0; 239a99b5657SRoger Quadros 240a99b5657SRoger Quadros regmap_write(mii_rt, txcfg_reg, txcfg); 241a99b5657SRoger Quadros 242a99b5657SRoger Quadros txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; 243a99b5657SRoger Quadros 244a99b5657SRoger Quadros /* Configuration of Port 1 Tx */ 245a99b5657SRoger Quadros txcfg_reg = PRUSS_MII_RT_TXCFG1; 246a99b5657SRoger Quadros 247a99b5657SRoger Quadros regmap_write(mii_rt, txcfg_reg, txcfg); 248a99b5657SRoger Quadros 249a99b5657SRoger Quadros txcfg_reg = PRUSS_MII_RT_RX_FRMS0; 250a99b5657SRoger Quadros 251a99b5657SRoger Quadros /* Min frame length should be set to 64 to allow receive of standard 252a99b5657SRoger Quadros * Ethernet frames such as PTP, LLDP that will not have the tag/rct. 253a99b5657SRoger Quadros * Actual size written to register is size - 1 per TRM. This also 254a99b5657SRoger Quadros * includes CRC/FCS. 255a99b5657SRoger Quadros */ 256a99b5657SRoger Quadros txcfg = FIELD_PREP(PRUSS_MII_RT_RX_FRMS_MIN_FRM_MASK, 257a99b5657SRoger Quadros (PRUSS_MII_RT_RX_FRMS_MIN_FRM - 1)); 258a99b5657SRoger Quadros 259a99b5657SRoger Quadros /* For EMAC, set Max frame size to 1528 i.e size with VLAN. 260a99b5657SRoger Quadros * Actual size written to register is size - 1 as per TRM. 261a99b5657SRoger Quadros * Since driver support run time change of protocol, driver 262a99b5657SRoger Quadros * must overwrite the values based on Ethernet type. 263a99b5657SRoger Quadros */ 264a99b5657SRoger Quadros txcfg |= FIELD_PREP(PRUSS_MII_RT_RX_FRMS_MAX_FRM_MASK, 265a99b5657SRoger Quadros (PRUSS_MII_RT_RX_FRMS_MAX_SUPPORT_EMAC - 1)); 266a99b5657SRoger Quadros 267a99b5657SRoger Quadros regmap_write(mii_rt, txcfg_reg, txcfg); 268a99b5657SRoger Quadros 269a99b5657SRoger Quadros txcfg_reg = PRUSS_MII_RT_RX_FRMS1; 270a99b5657SRoger Quadros 271a99b5657SRoger Quadros regmap_write(mii_rt, txcfg_reg, txcfg); 272a99b5657SRoger Quadros } 273a99b5657SRoger Quadros 274a99b5657SRoger Quadros static void icssm_prueth_clearmem(struct prueth *prueth, enum prueth_mem region) 275a99b5657SRoger Quadros { 276a99b5657SRoger Quadros memset_io(prueth->mem[region].va, 0, prueth->mem[region].size); 277a99b5657SRoger Quadros } 278a99b5657SRoger Quadros 279a99b5657SRoger Quadros static void icssm_prueth_hostinit(struct prueth *prueth) 280a99b5657SRoger Quadros { 281a99b5657SRoger Quadros /* Clear shared RAM */ 282a99b5657SRoger Quadros icssm_prueth_clearmem(prueth, PRUETH_MEM_SHARED_RAM); 283a99b5657SRoger Quadros 284a99b5657SRoger Quadros /* Clear OCMC RAM */ 285a99b5657SRoger Quadros icssm_prueth_clearmem(prueth, PRUETH_MEM_OCMC); 286a99b5657SRoger Quadros 287a99b5657SRoger Quadros /* Clear data RAMs */ 288a99b5657SRoger Quadros if (prueth->eth_node[PRUETH_MAC0]) 289a99b5657SRoger Quadros icssm_prueth_clearmem(prueth, PRUETH_MEM_DRAM0); 290a99b5657SRoger Quadros if (prueth->eth_node[PRUETH_MAC1]) 291a99b5657SRoger Quadros icssm_prueth_clearmem(prueth, PRUETH_MEM_DRAM1); 292a99b5657SRoger Quadros 293a99b5657SRoger Quadros /* Initialize host queues in shared RAM */ 294a99b5657SRoger Quadros icssm_prueth_hostconfig(prueth); 295a99b5657SRoger Quadros 296a99b5657SRoger Quadros /* Configure MII_RT */ 297a99b5657SRoger Quadros icssm_prueth_mii_init(prueth); 298a99b5657SRoger Quadros } 299a99b5657SRoger Quadros 300*e15472e8SRoger Quadros /* This function initialize the driver in EMAC mode 301a99b5657SRoger Quadros * based on eth_type 302a99b5657SRoger Quadros */ 303a99b5657SRoger Quadros static void icssm_prueth_init_ethernet_mode(struct prueth *prueth) 304a99b5657SRoger Quadros { 305a99b5657SRoger Quadros icssm_prueth_hostinit(prueth); 306a99b5657SRoger Quadros } 307a99b5657SRoger Quadros 308*e15472e8SRoger Quadros static void icssm_prueth_port_enable(struct prueth_emac *emac, bool enable) 309*e15472e8SRoger Quadros { 310*e15472e8SRoger Quadros struct prueth *prueth = emac->prueth; 311*e15472e8SRoger Quadros void __iomem *port_ctrl; 312*e15472e8SRoger Quadros void __iomem *ram; 313*e15472e8SRoger Quadros 314*e15472e8SRoger Quadros ram = prueth->mem[emac->dram].va; 315*e15472e8SRoger Quadros port_ctrl = ram + PORT_CONTROL_ADDR; 316*e15472e8SRoger Quadros writeb(!!enable, port_ctrl); 317*e15472e8SRoger Quadros } 318*e15472e8SRoger Quadros 319a99b5657SRoger Quadros static int icssm_prueth_emac_config(struct prueth_emac *emac) 320a99b5657SRoger Quadros { 321a99b5657SRoger Quadros struct prueth *prueth = emac->prueth; 322a99b5657SRoger Quadros u32 sharedramaddr, ocmcaddr; 323a99b5657SRoger Quadros void __iomem *dram_base; 324a99b5657SRoger Quadros void __iomem *mac_addr; 325a99b5657SRoger Quadros void __iomem *dram; 326*e15472e8SRoger Quadros void __iomem *sram; 327a99b5657SRoger Quadros 328a99b5657SRoger Quadros /* PRU needs local shared RAM address for C28 */ 329a99b5657SRoger Quadros sharedramaddr = ICSS_LOCAL_SHARED_RAM; 330a99b5657SRoger Quadros /* PRU needs real global OCMC address for C30*/ 331a99b5657SRoger Quadros ocmcaddr = (u32)prueth->mem[PRUETH_MEM_OCMC].pa; 332*e15472e8SRoger Quadros sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; 333a99b5657SRoger Quadros 334a99b5657SRoger Quadros /* Clear data RAM */ 335a99b5657SRoger Quadros icssm_prueth_clearmem(prueth, emac->dram); 336a99b5657SRoger Quadros 337a99b5657SRoger Quadros dram_base = prueth->mem[emac->dram].va; 338a99b5657SRoger Quadros 339a99b5657SRoger Quadros /* setup mac address */ 340a99b5657SRoger Quadros mac_addr = dram_base + PORT_MAC_ADDR; 341a99b5657SRoger Quadros memcpy_toio(mac_addr, emac->mac_addr, 6); 342a99b5657SRoger Quadros 343a99b5657SRoger Quadros /* queue information table */ 344a99b5657SRoger Quadros dram = dram_base + TX_CONTEXT_Q1_OFFSET_ADDR; 345a99b5657SRoger Quadros memcpy_toio(dram, queue_infos[emac->port_id], 346a99b5657SRoger Quadros sizeof(queue_infos[emac->port_id])); 347a99b5657SRoger Quadros 348a99b5657SRoger Quadros /* queue table */ 349a99b5657SRoger Quadros dram = dram_base + PORT_QUEUE_DESC_OFFSET; 350a99b5657SRoger Quadros memcpy_toio(dram, queue_descs[emac->port_id], 351a99b5657SRoger Quadros sizeof(queue_descs[emac->port_id])); 352a99b5657SRoger Quadros 353*e15472e8SRoger Quadros emac->rx_queue_descs = sram + HOST_QUEUE_DESC_OFFSET; 354*e15472e8SRoger Quadros emac->tx_queue_descs = dram; 355*e15472e8SRoger Quadros 356a99b5657SRoger Quadros /* Set in constant table C28 of PRU0 to ICSS Shared memory */ 357a99b5657SRoger Quadros pru_rproc_set_ctable(emac->pru, PRU_C28, sharedramaddr); 358a99b5657SRoger Quadros 359a99b5657SRoger Quadros /* Set in constant table C30 of PRU0 to OCMC memory */ 360a99b5657SRoger Quadros pru_rproc_set_ctable(emac->pru, PRU_C30, ocmcaddr); 361a99b5657SRoger Quadros 362a99b5657SRoger Quadros return 0; 363a99b5657SRoger Quadros } 364511f6c1aSRoger Quadros 365511f6c1aSRoger Quadros /* called back by PHY layer if there is change in link state of hw port*/ 366511f6c1aSRoger Quadros static void icssm_emac_adjust_link(struct net_device *ndev) 367511f6c1aSRoger Quadros { 368511f6c1aSRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 369511f6c1aSRoger Quadros struct phy_device *phydev = emac->phydev; 370*e15472e8SRoger Quadros struct prueth *prueth = emac->prueth; 371511f6c1aSRoger Quadros bool new_state = false; 372*e15472e8SRoger Quadros enum prueth_mem region; 373511f6c1aSRoger Quadros unsigned long flags; 374*e15472e8SRoger Quadros u32 port_status = 0; 375*e15472e8SRoger Quadros u32 txcfg, mask; 376*e15472e8SRoger Quadros u32 delay; 377511f6c1aSRoger Quadros 378511f6c1aSRoger Quadros spin_lock_irqsave(&emac->lock, flags); 379511f6c1aSRoger Quadros 380511f6c1aSRoger Quadros if (phydev->link) { 381511f6c1aSRoger Quadros /* check the mode of operation */ 382511f6c1aSRoger Quadros if (phydev->duplex != emac->duplex) { 383511f6c1aSRoger Quadros new_state = true; 384511f6c1aSRoger Quadros emac->duplex = phydev->duplex; 385511f6c1aSRoger Quadros } 386511f6c1aSRoger Quadros if (phydev->speed != emac->speed) { 387511f6c1aSRoger Quadros new_state = true; 388511f6c1aSRoger Quadros emac->speed = phydev->speed; 389511f6c1aSRoger Quadros } 390511f6c1aSRoger Quadros if (!emac->link) { 391511f6c1aSRoger Quadros new_state = true; 392511f6c1aSRoger Quadros emac->link = 1; 393511f6c1aSRoger Quadros } 394511f6c1aSRoger Quadros } else if (emac->link) { 395511f6c1aSRoger Quadros new_state = true; 396511f6c1aSRoger Quadros emac->link = 0; 397511f6c1aSRoger Quadros } 398511f6c1aSRoger Quadros 399*e15472e8SRoger Quadros if (new_state) { 400511f6c1aSRoger Quadros phy_print_status(phydev); 401*e15472e8SRoger Quadros region = emac->dram; 402*e15472e8SRoger Quadros 403*e15472e8SRoger Quadros /* update phy/port status information based on PHY values*/ 404*e15472e8SRoger Quadros if (emac->link) { 405*e15472e8SRoger Quadros port_status |= PORT_LINK_MASK; 406*e15472e8SRoger Quadros 407*e15472e8SRoger Quadros icssm_prueth_write_reg(prueth, region, PHY_SPEED_OFFSET, 408*e15472e8SRoger Quadros emac->speed); 409*e15472e8SRoger Quadros 410*e15472e8SRoger Quadros delay = TX_CLK_DELAY_100M; 411*e15472e8SRoger Quadros delay = delay << PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT; 412*e15472e8SRoger Quadros mask = PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_MASK; 413*e15472e8SRoger Quadros 414*e15472e8SRoger Quadros if (emac->port_id) 415*e15472e8SRoger Quadros txcfg = PRUSS_MII_RT_TXCFG1; 416*e15472e8SRoger Quadros else 417*e15472e8SRoger Quadros txcfg = PRUSS_MII_RT_TXCFG0; 418*e15472e8SRoger Quadros 419*e15472e8SRoger Quadros regmap_update_bits(prueth->mii_rt, txcfg, mask, delay); 420*e15472e8SRoger Quadros } 421*e15472e8SRoger Quadros 422*e15472e8SRoger Quadros writeb(port_status, prueth->mem[region].va + 423*e15472e8SRoger Quadros PORT_STATUS_OFFSET); 424*e15472e8SRoger Quadros } 425511f6c1aSRoger Quadros 426511f6c1aSRoger Quadros if (emac->link) { 427511f6c1aSRoger Quadros /* reactivate the transmit queue if it is stopped */ 428511f6c1aSRoger Quadros if (netif_running(ndev) && netif_queue_stopped(ndev)) 429511f6c1aSRoger Quadros netif_wake_queue(ndev); 430511f6c1aSRoger Quadros } else { 431511f6c1aSRoger Quadros if (!netif_queue_stopped(ndev)) 432511f6c1aSRoger Quadros netif_stop_queue(ndev); 433511f6c1aSRoger Quadros } 434511f6c1aSRoger Quadros 435511f6c1aSRoger Quadros spin_unlock_irqrestore(&emac->lock, flags); 436511f6c1aSRoger Quadros } 437511f6c1aSRoger Quadros 438*e15472e8SRoger Quadros static unsigned int 439*e15472e8SRoger Quadros icssm_get_buff_desc_count(const struct prueth_queue_info *queue) 440*e15472e8SRoger Quadros { 441*e15472e8SRoger Quadros unsigned int buffer_desc_count; 442*e15472e8SRoger Quadros 443*e15472e8SRoger Quadros buffer_desc_count = queue->buffer_desc_end - 444*e15472e8SRoger Quadros queue->buffer_desc_offset; 445*e15472e8SRoger Quadros buffer_desc_count /= BD_SIZE; 446*e15472e8SRoger Quadros buffer_desc_count++; 447*e15472e8SRoger Quadros 448*e15472e8SRoger Quadros return buffer_desc_count; 449*e15472e8SRoger Quadros } 450*e15472e8SRoger Quadros 451*e15472e8SRoger Quadros static void icssm_get_block(struct prueth_queue_desc __iomem *queue_desc, 452*e15472e8SRoger Quadros const struct prueth_queue_info *queue, 453*e15472e8SRoger Quadros int *write_block, int *read_block) 454*e15472e8SRoger Quadros { 455*e15472e8SRoger Quadros *write_block = (readw(&queue_desc->wr_ptr) - 456*e15472e8SRoger Quadros queue->buffer_desc_offset) / BD_SIZE; 457*e15472e8SRoger Quadros *read_block = (readw(&queue_desc->rd_ptr) - 458*e15472e8SRoger Quadros queue->buffer_desc_offset) / BD_SIZE; 459*e15472e8SRoger Quadros } 460*e15472e8SRoger Quadros 461*e15472e8SRoger Quadros /** 462*e15472e8SRoger Quadros * icssm_emac_rx_irq - EMAC Rx interrupt handler 463*e15472e8SRoger Quadros * @irq: interrupt number 464*e15472e8SRoger Quadros * @dev_id: pointer to net_device 465*e15472e8SRoger Quadros * 466*e15472e8SRoger Quadros * EMAC Interrupt handler - we only schedule NAPI and not process any packets 467*e15472e8SRoger Quadros * here. 468*e15472e8SRoger Quadros * 469*e15472e8SRoger Quadros * Return: IRQ_HANDLED if the interrupt handled 470*e15472e8SRoger Quadros */ 471*e15472e8SRoger Quadros static irqreturn_t icssm_emac_rx_irq(int irq, void *dev_id) 472*e15472e8SRoger Quadros { 473*e15472e8SRoger Quadros struct net_device *ndev = (struct net_device *)dev_id; 474*e15472e8SRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 475*e15472e8SRoger Quadros 476*e15472e8SRoger Quadros if (likely(netif_running(ndev))) { 477*e15472e8SRoger Quadros /* disable Rx system event */ 478*e15472e8SRoger Quadros disable_irq_nosync(emac->rx_irq); 479*e15472e8SRoger Quadros napi_schedule(&emac->napi); 480*e15472e8SRoger Quadros } 481*e15472e8SRoger Quadros 482*e15472e8SRoger Quadros return IRQ_HANDLED; 483*e15472e8SRoger Quadros } 484*e15472e8SRoger Quadros 485*e15472e8SRoger Quadros /** 486*e15472e8SRoger Quadros * icssm_prueth_tx_enqueue - queue a packet to firmware for transmission 487*e15472e8SRoger Quadros * 488*e15472e8SRoger Quadros * @emac: EMAC data structure 489*e15472e8SRoger Quadros * @skb: packet data buffer 490*e15472e8SRoger Quadros * @queue_id: priority queue id 491*e15472e8SRoger Quadros * 492*e15472e8SRoger Quadros * Return: 0 (Success) 493*e15472e8SRoger Quadros */ 494*e15472e8SRoger Quadros static int icssm_prueth_tx_enqueue(struct prueth_emac *emac, 495*e15472e8SRoger Quadros struct sk_buff *skb, 496*e15472e8SRoger Quadros enum prueth_queue_id queue_id) 497*e15472e8SRoger Quadros { 498*e15472e8SRoger Quadros struct prueth_queue_desc __iomem *queue_desc; 499*e15472e8SRoger Quadros const struct prueth_queue_info *txqueue; 500*e15472e8SRoger Quadros struct net_device *ndev = emac->ndev; 501*e15472e8SRoger Quadros unsigned int buffer_desc_count; 502*e15472e8SRoger Quadros int free_blocks, update_block; 503*e15472e8SRoger Quadros bool buffer_wrapped = false; 504*e15472e8SRoger Quadros int write_block, read_block; 505*e15472e8SRoger Quadros void *src_addr, *dst_addr; 506*e15472e8SRoger Quadros int pkt_block_size; 507*e15472e8SRoger Quadros void __iomem *dram; 508*e15472e8SRoger Quadros int txport, pktlen; 509*e15472e8SRoger Quadros u16 update_wr_ptr; 510*e15472e8SRoger Quadros u32 wr_buf_desc; 511*e15472e8SRoger Quadros void *ocmc_ram; 512*e15472e8SRoger Quadros 513*e15472e8SRoger Quadros dram = emac->prueth->mem[emac->dram].va; 514*e15472e8SRoger Quadros if (eth_skb_pad(skb)) { 515*e15472e8SRoger Quadros if (netif_msg_tx_err(emac) && net_ratelimit()) 516*e15472e8SRoger Quadros netdev_err(ndev, "packet pad failed\n"); 517*e15472e8SRoger Quadros return -ENOMEM; 518*e15472e8SRoger Quadros } 519*e15472e8SRoger Quadros 520*e15472e8SRoger Quadros /* which port to tx: MII0 or MII1 */ 521*e15472e8SRoger Quadros txport = emac->tx_port_queue; 522*e15472e8SRoger Quadros src_addr = skb->data; 523*e15472e8SRoger Quadros pktlen = skb->len; 524*e15472e8SRoger Quadros /* Get the tx queue */ 525*e15472e8SRoger Quadros queue_desc = emac->tx_queue_descs + queue_id; 526*e15472e8SRoger Quadros txqueue = &queue_infos[txport][queue_id]; 527*e15472e8SRoger Quadros 528*e15472e8SRoger Quadros buffer_desc_count = icssm_get_buff_desc_count(txqueue); 529*e15472e8SRoger Quadros 530*e15472e8SRoger Quadros /* the PRU firmware deals mostly in pointers already 531*e15472e8SRoger Quadros * offset into ram, we would like to deal in indexes 532*e15472e8SRoger Quadros * within the queue we are working with for code 533*e15472e8SRoger Quadros * simplicity, calculate this here 534*e15472e8SRoger Quadros */ 535*e15472e8SRoger Quadros icssm_get_block(queue_desc, txqueue, &write_block, &read_block); 536*e15472e8SRoger Quadros 537*e15472e8SRoger Quadros if (write_block > read_block) { 538*e15472e8SRoger Quadros free_blocks = buffer_desc_count - write_block; 539*e15472e8SRoger Quadros free_blocks += read_block; 540*e15472e8SRoger Quadros } else if (write_block < read_block) { 541*e15472e8SRoger Quadros free_blocks = read_block - write_block; 542*e15472e8SRoger Quadros } else { /* they are all free */ 543*e15472e8SRoger Quadros free_blocks = buffer_desc_count; 544*e15472e8SRoger Quadros } 545*e15472e8SRoger Quadros 546*e15472e8SRoger Quadros pkt_block_size = DIV_ROUND_UP(pktlen, ICSS_BLOCK_SIZE); 547*e15472e8SRoger Quadros if (pkt_block_size > free_blocks) /* out of queue space */ 548*e15472e8SRoger Quadros return -ENOBUFS; 549*e15472e8SRoger Quadros 550*e15472e8SRoger Quadros /* calculate end BD address post write */ 551*e15472e8SRoger Quadros update_block = write_block + pkt_block_size; 552*e15472e8SRoger Quadros 553*e15472e8SRoger Quadros /* Check for wrap around */ 554*e15472e8SRoger Quadros if (update_block >= buffer_desc_count) { 555*e15472e8SRoger Quadros update_block %= buffer_desc_count; 556*e15472e8SRoger Quadros buffer_wrapped = true; 557*e15472e8SRoger Quadros } 558*e15472e8SRoger Quadros 559*e15472e8SRoger Quadros /* OCMC RAM is not cached and write order is not important */ 560*e15472e8SRoger Quadros ocmc_ram = (__force void *)emac->prueth->mem[PRUETH_MEM_OCMC].va; 561*e15472e8SRoger Quadros dst_addr = ocmc_ram + txqueue->buffer_offset + 562*e15472e8SRoger Quadros (write_block * ICSS_BLOCK_SIZE); 563*e15472e8SRoger Quadros 564*e15472e8SRoger Quadros /* Copy the data from socket buffer(DRAM) to PRU buffers(OCMC) */ 565*e15472e8SRoger Quadros if (buffer_wrapped) { /* wrapped around buffer */ 566*e15472e8SRoger Quadros int bytes = (buffer_desc_count - write_block) * ICSS_BLOCK_SIZE; 567*e15472e8SRoger Quadros int remaining; 568*e15472e8SRoger Quadros 569*e15472e8SRoger Quadros /* bytes is integral multiple of ICSS_BLOCK_SIZE but 570*e15472e8SRoger Quadros * entire packet may have fit within the last BD 571*e15472e8SRoger Quadros * if pkt_info.length is not integral multiple of 572*e15472e8SRoger Quadros * ICSS_BLOCK_SIZE 573*e15472e8SRoger Quadros */ 574*e15472e8SRoger Quadros if (pktlen < bytes) 575*e15472e8SRoger Quadros bytes = pktlen; 576*e15472e8SRoger Quadros 577*e15472e8SRoger Quadros /* copy non-wrapped part */ 578*e15472e8SRoger Quadros memcpy(dst_addr, src_addr, bytes); 579*e15472e8SRoger Quadros 580*e15472e8SRoger Quadros /* copy wrapped part */ 581*e15472e8SRoger Quadros src_addr += bytes; 582*e15472e8SRoger Quadros remaining = pktlen - bytes; 583*e15472e8SRoger Quadros dst_addr = ocmc_ram + txqueue->buffer_offset; 584*e15472e8SRoger Quadros memcpy(dst_addr, src_addr, remaining); 585*e15472e8SRoger Quadros } else { 586*e15472e8SRoger Quadros memcpy(dst_addr, src_addr, pktlen); 587*e15472e8SRoger Quadros } 588*e15472e8SRoger Quadros 589*e15472e8SRoger Quadros /* update first buffer descriptor */ 590*e15472e8SRoger Quadros wr_buf_desc = (pktlen << PRUETH_BD_LENGTH_SHIFT) & 591*e15472e8SRoger Quadros PRUETH_BD_LENGTH_MASK; 592*e15472e8SRoger Quadros writel(wr_buf_desc, dram + readw(&queue_desc->wr_ptr)); 593*e15472e8SRoger Quadros 594*e15472e8SRoger Quadros /* update the write pointer in this queue descriptor, the firmware 595*e15472e8SRoger Quadros * polls for this change so this will signal the start of transmission 596*e15472e8SRoger Quadros */ 597*e15472e8SRoger Quadros update_wr_ptr = txqueue->buffer_desc_offset + (update_block * BD_SIZE); 598*e15472e8SRoger Quadros writew(update_wr_ptr, &queue_desc->wr_ptr); 599*e15472e8SRoger Quadros 600*e15472e8SRoger Quadros return 0; 601*e15472e8SRoger Quadros } 602*e15472e8SRoger Quadros 603*e15472e8SRoger Quadros void icssm_parse_packet_info(struct prueth *prueth, u32 buffer_descriptor, 604*e15472e8SRoger Quadros struct prueth_packet_info *pkt_info) 605*e15472e8SRoger Quadros { 606*e15472e8SRoger Quadros pkt_info->shadow = !!(buffer_descriptor & PRUETH_BD_SHADOW_MASK); 607*e15472e8SRoger Quadros pkt_info->port = (buffer_descriptor & PRUETH_BD_PORT_MASK) >> 608*e15472e8SRoger Quadros PRUETH_BD_PORT_SHIFT; 609*e15472e8SRoger Quadros pkt_info->length = (buffer_descriptor & PRUETH_BD_LENGTH_MASK) >> 610*e15472e8SRoger Quadros PRUETH_BD_LENGTH_SHIFT; 611*e15472e8SRoger Quadros pkt_info->broadcast = !!(buffer_descriptor & PRUETH_BD_BROADCAST_MASK); 612*e15472e8SRoger Quadros pkt_info->error = !!(buffer_descriptor & PRUETH_BD_ERROR_MASK); 613*e15472e8SRoger Quadros pkt_info->lookup_success = !!(buffer_descriptor & 614*e15472e8SRoger Quadros PRUETH_BD_LOOKUP_SUCCESS_MASK); 615*e15472e8SRoger Quadros pkt_info->flood = !!(buffer_descriptor & PRUETH_BD_SW_FLOOD_MASK); 616*e15472e8SRoger Quadros pkt_info->timestamp = !!(buffer_descriptor & PRUETH_BD_TIMESTAMP_MASK); 617*e15472e8SRoger Quadros } 618*e15472e8SRoger Quadros 619*e15472e8SRoger Quadros /** 620*e15472e8SRoger Quadros * icssm_emac_rx_packet - EMAC Receive function 621*e15472e8SRoger Quadros * 622*e15472e8SRoger Quadros * @emac: EMAC data structure 623*e15472e8SRoger Quadros * @bd_rd_ptr: Buffer descriptor read pointer 624*e15472e8SRoger Quadros * @pkt_info: packet information structure 625*e15472e8SRoger Quadros * @rxqueue: Receive queue information structure 626*e15472e8SRoger Quadros * 627*e15472e8SRoger Quadros * Get a packet from receive queue 628*e15472e8SRoger Quadros * 629*e15472e8SRoger Quadros * Return: 0 (Success) 630*e15472e8SRoger Quadros */ 631*e15472e8SRoger Quadros int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, 632*e15472e8SRoger Quadros struct prueth_packet_info *pkt_info, 633*e15472e8SRoger Quadros const struct prueth_queue_info *rxqueue) 634*e15472e8SRoger Quadros { 635*e15472e8SRoger Quadros struct net_device *ndev = emac->ndev; 636*e15472e8SRoger Quadros unsigned int buffer_desc_count; 637*e15472e8SRoger Quadros int read_block, update_block; 638*e15472e8SRoger Quadros unsigned int actual_pkt_len; 639*e15472e8SRoger Quadros bool buffer_wrapped = false; 640*e15472e8SRoger Quadros void *src_addr, *dst_addr; 641*e15472e8SRoger Quadros struct sk_buff *skb; 642*e15472e8SRoger Quadros int pkt_block_size; 643*e15472e8SRoger Quadros void *ocmc_ram; 644*e15472e8SRoger Quadros 645*e15472e8SRoger Quadros /* the PRU firmware deals mostly in pointers already 646*e15472e8SRoger Quadros * offset into ram, we would like to deal in indexes 647*e15472e8SRoger Quadros * within the queue we are working with for code 648*e15472e8SRoger Quadros * simplicity, calculate this here 649*e15472e8SRoger Quadros */ 650*e15472e8SRoger Quadros buffer_desc_count = icssm_get_buff_desc_count(rxqueue); 651*e15472e8SRoger Quadros read_block = (*bd_rd_ptr - rxqueue->buffer_desc_offset) / BD_SIZE; 652*e15472e8SRoger Quadros pkt_block_size = DIV_ROUND_UP(pkt_info->length, ICSS_BLOCK_SIZE); 653*e15472e8SRoger Quadros 654*e15472e8SRoger Quadros /* calculate end BD address post read */ 655*e15472e8SRoger Quadros update_block = read_block + pkt_block_size; 656*e15472e8SRoger Quadros 657*e15472e8SRoger Quadros /* Check for wrap around */ 658*e15472e8SRoger Quadros if (update_block >= buffer_desc_count) { 659*e15472e8SRoger Quadros update_block %= buffer_desc_count; 660*e15472e8SRoger Quadros if (update_block) 661*e15472e8SRoger Quadros buffer_wrapped = true; 662*e15472e8SRoger Quadros } 663*e15472e8SRoger Quadros 664*e15472e8SRoger Quadros /* calculate new pointer in ram */ 665*e15472e8SRoger Quadros *bd_rd_ptr = rxqueue->buffer_desc_offset + (update_block * BD_SIZE); 666*e15472e8SRoger Quadros 667*e15472e8SRoger Quadros actual_pkt_len = pkt_info->length; 668*e15472e8SRoger Quadros 669*e15472e8SRoger Quadros /* Allocate a socket buffer for this packet */ 670*e15472e8SRoger Quadros skb = netdev_alloc_skb_ip_align(ndev, actual_pkt_len); 671*e15472e8SRoger Quadros if (!skb) { 672*e15472e8SRoger Quadros if (netif_msg_rx_err(emac) && net_ratelimit()) 673*e15472e8SRoger Quadros netdev_err(ndev, "failed rx buffer alloc\n"); 674*e15472e8SRoger Quadros return -ENOMEM; 675*e15472e8SRoger Quadros } 676*e15472e8SRoger Quadros 677*e15472e8SRoger Quadros dst_addr = skb->data; 678*e15472e8SRoger Quadros 679*e15472e8SRoger Quadros /* OCMC RAM is not cached and read order is not important */ 680*e15472e8SRoger Quadros ocmc_ram = (__force void *)emac->prueth->mem[PRUETH_MEM_OCMC].va; 681*e15472e8SRoger Quadros 682*e15472e8SRoger Quadros /* Get the start address of the first buffer from 683*e15472e8SRoger Quadros * the read buffer description 684*e15472e8SRoger Quadros */ 685*e15472e8SRoger Quadros src_addr = ocmc_ram + rxqueue->buffer_offset + 686*e15472e8SRoger Quadros (read_block * ICSS_BLOCK_SIZE); 687*e15472e8SRoger Quadros 688*e15472e8SRoger Quadros /* Copy the data from PRU buffers(OCMC) to socket buffer(DRAM) */ 689*e15472e8SRoger Quadros if (buffer_wrapped) { /* wrapped around buffer */ 690*e15472e8SRoger Quadros int bytes = (buffer_desc_count - read_block) * ICSS_BLOCK_SIZE; 691*e15472e8SRoger Quadros int remaining; 692*e15472e8SRoger Quadros /* bytes is integral multiple of ICSS_BLOCK_SIZE but 693*e15472e8SRoger Quadros * entire packet may have fit within the last BD 694*e15472e8SRoger Quadros * if pkt_info.length is not integral multiple of 695*e15472e8SRoger Quadros * ICSS_BLOCK_SIZE 696*e15472e8SRoger Quadros */ 697*e15472e8SRoger Quadros if (pkt_info->length < bytes) 698*e15472e8SRoger Quadros bytes = pkt_info->length; 699*e15472e8SRoger Quadros 700*e15472e8SRoger Quadros /* copy non-wrapped part */ 701*e15472e8SRoger Quadros memcpy(dst_addr, src_addr, bytes); 702*e15472e8SRoger Quadros 703*e15472e8SRoger Quadros /* copy wrapped part */ 704*e15472e8SRoger Quadros dst_addr += bytes; 705*e15472e8SRoger Quadros remaining = actual_pkt_len - bytes; 706*e15472e8SRoger Quadros 707*e15472e8SRoger Quadros src_addr = ocmc_ram + rxqueue->buffer_offset; 708*e15472e8SRoger Quadros memcpy(dst_addr, src_addr, remaining); 709*e15472e8SRoger Quadros src_addr += remaining; 710*e15472e8SRoger Quadros } else { 711*e15472e8SRoger Quadros memcpy(dst_addr, src_addr, actual_pkt_len); 712*e15472e8SRoger Quadros src_addr += actual_pkt_len; 713*e15472e8SRoger Quadros } 714*e15472e8SRoger Quadros 715*e15472e8SRoger Quadros skb_put(skb, actual_pkt_len); 716*e15472e8SRoger Quadros 717*e15472e8SRoger Quadros /* send packet up the stack */ 718*e15472e8SRoger Quadros skb->protocol = eth_type_trans(skb, ndev); 719*e15472e8SRoger Quadros netif_receive_skb(skb); 720*e15472e8SRoger Quadros 721*e15472e8SRoger Quadros /* update stats */ 722*e15472e8SRoger Quadros emac->stats.rx_bytes += actual_pkt_len; 723*e15472e8SRoger Quadros emac->stats.rx_packets++; 724*e15472e8SRoger Quadros 725*e15472e8SRoger Quadros return 0; 726*e15472e8SRoger Quadros } 727*e15472e8SRoger Quadros 728*e15472e8SRoger Quadros static int icssm_emac_rx_packets(struct prueth_emac *emac, int budget) 729*e15472e8SRoger Quadros { 730*e15472e8SRoger Quadros struct prueth_queue_desc __iomem *queue_desc; 731*e15472e8SRoger Quadros const struct prueth_queue_info *rxqueue; 732*e15472e8SRoger Quadros struct prueth *prueth = emac->prueth; 733*e15472e8SRoger Quadros struct prueth_packet_info pkt_info; 734*e15472e8SRoger Quadros int start_queue, end_queue; 735*e15472e8SRoger Quadros void __iomem *shared_ram; 736*e15472e8SRoger Quadros u16 bd_rd_ptr, bd_wr_ptr; 737*e15472e8SRoger Quadros u16 update_rd_ptr; 738*e15472e8SRoger Quadros u8 overflow_cnt; 739*e15472e8SRoger Quadros u32 rd_buf_desc; 740*e15472e8SRoger Quadros int used = 0; 741*e15472e8SRoger Quadros int i, ret; 742*e15472e8SRoger Quadros 743*e15472e8SRoger Quadros shared_ram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; 744*e15472e8SRoger Quadros 745*e15472e8SRoger Quadros start_queue = emac->rx_queue_start; 746*e15472e8SRoger Quadros end_queue = emac->rx_queue_end; 747*e15472e8SRoger Quadros 748*e15472e8SRoger Quadros /* skip Rx if budget is 0 */ 749*e15472e8SRoger Quadros if (!budget) 750*e15472e8SRoger Quadros return 0; 751*e15472e8SRoger Quadros 752*e15472e8SRoger Quadros /* search host queues for packets */ 753*e15472e8SRoger Quadros for (i = start_queue; i <= end_queue; i++) { 754*e15472e8SRoger Quadros queue_desc = emac->rx_queue_descs + i; 755*e15472e8SRoger Quadros rxqueue = &queue_infos[PRUETH_PORT_HOST][i]; 756*e15472e8SRoger Quadros 757*e15472e8SRoger Quadros overflow_cnt = readb(&queue_desc->overflow_cnt); 758*e15472e8SRoger Quadros if (overflow_cnt > 0) { 759*e15472e8SRoger Quadros emac->stats.rx_over_errors += overflow_cnt; 760*e15472e8SRoger Quadros /* reset to zero */ 761*e15472e8SRoger Quadros writeb(0, &queue_desc->overflow_cnt); 762*e15472e8SRoger Quadros } 763*e15472e8SRoger Quadros 764*e15472e8SRoger Quadros bd_rd_ptr = readw(&queue_desc->rd_ptr); 765*e15472e8SRoger Quadros bd_wr_ptr = readw(&queue_desc->wr_ptr); 766*e15472e8SRoger Quadros 767*e15472e8SRoger Quadros /* while packets are available in this queue */ 768*e15472e8SRoger Quadros while (bd_rd_ptr != bd_wr_ptr) { 769*e15472e8SRoger Quadros /* get packet info from the read buffer descriptor */ 770*e15472e8SRoger Quadros rd_buf_desc = readl(shared_ram + bd_rd_ptr); 771*e15472e8SRoger Quadros icssm_parse_packet_info(prueth, rd_buf_desc, &pkt_info); 772*e15472e8SRoger Quadros 773*e15472e8SRoger Quadros if (pkt_info.length <= 0) { 774*e15472e8SRoger Quadros /* a packet length of zero will cause us to 775*e15472e8SRoger Quadros * never move the read pointer ahead, locking 776*e15472e8SRoger Quadros * the driver, so we manually have to move it 777*e15472e8SRoger Quadros * to the write pointer, discarding all 778*e15472e8SRoger Quadros * remaining packets in this queue. This should 779*e15472e8SRoger Quadros * never happen. 780*e15472e8SRoger Quadros */ 781*e15472e8SRoger Quadros update_rd_ptr = bd_wr_ptr; 782*e15472e8SRoger Quadros emac->stats.rx_length_errors++; 783*e15472e8SRoger Quadros } else if (pkt_info.length > EMAC_MAX_FRM_SUPPORT) { 784*e15472e8SRoger Quadros /* if the packet is too large we skip it but we 785*e15472e8SRoger Quadros * still need to move the read pointer ahead 786*e15472e8SRoger Quadros * and assume something is wrong with the read 787*e15472e8SRoger Quadros * pointer as the firmware should be filtering 788*e15472e8SRoger Quadros * these packets 789*e15472e8SRoger Quadros */ 790*e15472e8SRoger Quadros update_rd_ptr = bd_wr_ptr; 791*e15472e8SRoger Quadros emac->stats.rx_length_errors++; 792*e15472e8SRoger Quadros } else { 793*e15472e8SRoger Quadros update_rd_ptr = bd_rd_ptr; 794*e15472e8SRoger Quadros ret = icssm_emac_rx_packet(emac, &update_rd_ptr, 795*e15472e8SRoger Quadros &pkt_info, rxqueue); 796*e15472e8SRoger Quadros if (ret) 797*e15472e8SRoger Quadros return used; 798*e15472e8SRoger Quadros used++; 799*e15472e8SRoger Quadros } 800*e15472e8SRoger Quadros 801*e15472e8SRoger Quadros /* after reading the buffer descriptor we clear it 802*e15472e8SRoger Quadros * to prevent improperly moved read pointer errors 803*e15472e8SRoger Quadros * from simply looking like old packets. 804*e15472e8SRoger Quadros */ 805*e15472e8SRoger Quadros writel(0, shared_ram + bd_rd_ptr); 806*e15472e8SRoger Quadros 807*e15472e8SRoger Quadros /* update read pointer in queue descriptor */ 808*e15472e8SRoger Quadros writew(update_rd_ptr, &queue_desc->rd_ptr); 809*e15472e8SRoger Quadros bd_rd_ptr = update_rd_ptr; 810*e15472e8SRoger Quadros 811*e15472e8SRoger Quadros /* all we have room for? */ 812*e15472e8SRoger Quadros if (used >= budget) 813*e15472e8SRoger Quadros return used; 814*e15472e8SRoger Quadros } 815*e15472e8SRoger Quadros } 816*e15472e8SRoger Quadros 817*e15472e8SRoger Quadros return used; 818*e15472e8SRoger Quadros } 819*e15472e8SRoger Quadros 820*e15472e8SRoger Quadros static int icssm_emac_napi_poll(struct napi_struct *napi, int budget) 821*e15472e8SRoger Quadros { 822*e15472e8SRoger Quadros struct prueth_emac *emac = container_of(napi, struct prueth_emac, napi); 823*e15472e8SRoger Quadros int num_rx; 824*e15472e8SRoger Quadros 825*e15472e8SRoger Quadros num_rx = icssm_emac_rx_packets(emac, budget); 826*e15472e8SRoger Quadros 827*e15472e8SRoger Quadros if (num_rx < budget && napi_complete_done(napi, num_rx)) 828*e15472e8SRoger Quadros enable_irq(emac->rx_irq); 829*e15472e8SRoger Quadros 830*e15472e8SRoger Quadros return num_rx; 831*e15472e8SRoger Quadros } 832*e15472e8SRoger Quadros 833511f6c1aSRoger Quadros static int icssm_emac_set_boot_pru(struct prueth_emac *emac, 834511f6c1aSRoger Quadros struct net_device *ndev) 835511f6c1aSRoger Quadros { 836511f6c1aSRoger Quadros const struct prueth_firmware *pru_firmwares; 837511f6c1aSRoger Quadros struct prueth *prueth = emac->prueth; 838511f6c1aSRoger Quadros const char *fw_name; 839511f6c1aSRoger Quadros int ret; 840511f6c1aSRoger Quadros 841511f6c1aSRoger Quadros pru_firmwares = &prueth->fw_data->fw_pru[emac->port_id - 1]; 842511f6c1aSRoger Quadros fw_name = pru_firmwares->fw_name[prueth->eth_type]; 843511f6c1aSRoger Quadros if (!fw_name) { 844511f6c1aSRoger Quadros netdev_err(ndev, "eth_type %d not supported\n", 845511f6c1aSRoger Quadros prueth->eth_type); 846511f6c1aSRoger Quadros return -ENODEV; 847511f6c1aSRoger Quadros } 848511f6c1aSRoger Quadros 849511f6c1aSRoger Quadros ret = rproc_set_firmware(emac->pru, fw_name); 850511f6c1aSRoger Quadros if (ret) { 851511f6c1aSRoger Quadros netdev_err(ndev, "failed to set %s firmware: %d\n", 852511f6c1aSRoger Quadros fw_name, ret); 853511f6c1aSRoger Quadros return ret; 854511f6c1aSRoger Quadros } 855511f6c1aSRoger Quadros 856511f6c1aSRoger Quadros ret = rproc_boot(emac->pru); 857511f6c1aSRoger Quadros if (ret) { 858511f6c1aSRoger Quadros netdev_err(ndev, "failed to boot %s firmware: %d\n", 859511f6c1aSRoger Quadros fw_name, ret); 860511f6c1aSRoger Quadros return ret; 861511f6c1aSRoger Quadros } 862*e15472e8SRoger Quadros return ret; 863*e15472e8SRoger Quadros } 864*e15472e8SRoger Quadros 865*e15472e8SRoger Quadros static int icssm_emac_request_irqs(struct prueth_emac *emac) 866*e15472e8SRoger Quadros { 867*e15472e8SRoger Quadros struct net_device *ndev = emac->ndev; 868*e15472e8SRoger Quadros int ret; 869*e15472e8SRoger Quadros 870*e15472e8SRoger Quadros ret = request_irq(emac->rx_irq, icssm_emac_rx_irq, 871*e15472e8SRoger Quadros IRQF_TRIGGER_HIGH, 872*e15472e8SRoger Quadros ndev->name, ndev); 873*e15472e8SRoger Quadros if (ret) { 874*e15472e8SRoger Quadros netdev_err(ndev, "unable to request RX IRQ\n"); 875*e15472e8SRoger Quadros return ret; 876*e15472e8SRoger Quadros } 877511f6c1aSRoger Quadros 878511f6c1aSRoger Quadros return ret; 879511f6c1aSRoger Quadros } 880511f6c1aSRoger Quadros 881511f6c1aSRoger Quadros /** 882511f6c1aSRoger Quadros * icssm_emac_ndo_open - EMAC device open 883511f6c1aSRoger Quadros * @ndev: network adapter device 884511f6c1aSRoger Quadros * 885511f6c1aSRoger Quadros * Called when system wants to start the interface. 886511f6c1aSRoger Quadros * 887511f6c1aSRoger Quadros * Return: 0 for a successful open, or appropriate error code 888511f6c1aSRoger Quadros */ 889511f6c1aSRoger Quadros static int icssm_emac_ndo_open(struct net_device *ndev) 890511f6c1aSRoger Quadros { 891511f6c1aSRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 892a99b5657SRoger Quadros struct prueth *prueth = emac->prueth; 893511f6c1aSRoger Quadros int ret; 894511f6c1aSRoger Quadros 895a99b5657SRoger Quadros /* set h/w MAC as user might have re-configured */ 896a99b5657SRoger Quadros ether_addr_copy(emac->mac_addr, ndev->dev_addr); 897a99b5657SRoger Quadros 898a99b5657SRoger Quadros if (!prueth->emac_configured) 899a99b5657SRoger Quadros icssm_prueth_init_ethernet_mode(prueth); 900a99b5657SRoger Quadros 901a99b5657SRoger Quadros icssm_prueth_emac_config(emac); 902a99b5657SRoger Quadros 903511f6c1aSRoger Quadros ret = icssm_emac_set_boot_pru(emac, ndev); 904511f6c1aSRoger Quadros if (ret) 905511f6c1aSRoger Quadros return ret; 906511f6c1aSRoger Quadros 907*e15472e8SRoger Quadros ret = icssm_emac_request_irqs(emac); 908*e15472e8SRoger Quadros if (ret) 909*e15472e8SRoger Quadros goto rproc_shutdown; 910*e15472e8SRoger Quadros 911*e15472e8SRoger Quadros napi_enable(&emac->napi); 912*e15472e8SRoger Quadros 913511f6c1aSRoger Quadros /* start PHY */ 914511f6c1aSRoger Quadros phy_start(emac->phydev); 915*e15472e8SRoger Quadros 916*e15472e8SRoger Quadros /* enable the port and vlan */ 917*e15472e8SRoger Quadros icssm_prueth_port_enable(emac, true); 918*e15472e8SRoger Quadros 919a99b5657SRoger Quadros prueth->emac_configured |= BIT(emac->port_id); 920*e15472e8SRoger Quadros 921*e15472e8SRoger Quadros if (netif_msg_drv(emac)) 922*e15472e8SRoger Quadros dev_notice(&ndev->dev, "started\n"); 923*e15472e8SRoger Quadros 924511f6c1aSRoger Quadros return 0; 925*e15472e8SRoger Quadros 926*e15472e8SRoger Quadros rproc_shutdown: 927*e15472e8SRoger Quadros rproc_shutdown(emac->pru); 928*e15472e8SRoger Quadros 929*e15472e8SRoger Quadros return ret; 930511f6c1aSRoger Quadros } 931511f6c1aSRoger Quadros 932511f6c1aSRoger Quadros /** 933511f6c1aSRoger Quadros * icssm_emac_ndo_stop - EMAC device stop 934511f6c1aSRoger Quadros * @ndev: network adapter device 935511f6c1aSRoger Quadros * 936511f6c1aSRoger Quadros * Called when system wants to stop or down the interface. 937511f6c1aSRoger Quadros * 938511f6c1aSRoger Quadros * Return: Always 0 (Success) 939511f6c1aSRoger Quadros */ 940511f6c1aSRoger Quadros static int icssm_emac_ndo_stop(struct net_device *ndev) 941511f6c1aSRoger Quadros { 942511f6c1aSRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 943*e15472e8SRoger Quadros struct prueth *prueth = emac->prueth; 944*e15472e8SRoger Quadros 945*e15472e8SRoger Quadros prueth->emac_configured &= ~BIT(emac->port_id); 946*e15472e8SRoger Quadros 947*e15472e8SRoger Quadros /* disable the mac port */ 948*e15472e8SRoger Quadros icssm_prueth_port_enable(emac, false); 949511f6c1aSRoger Quadros 950511f6c1aSRoger Quadros /* stop PHY */ 951511f6c1aSRoger Quadros phy_stop(emac->phydev); 952511f6c1aSRoger Quadros 953*e15472e8SRoger Quadros napi_disable(&emac->napi); 954*e15472e8SRoger Quadros hrtimer_cancel(&emac->tx_hrtimer); 955*e15472e8SRoger Quadros 956*e15472e8SRoger Quadros /* stop the PRU */ 957511f6c1aSRoger Quadros rproc_shutdown(emac->pru); 958511f6c1aSRoger Quadros 959*e15472e8SRoger Quadros /* free rx interrupts */ 960*e15472e8SRoger Quadros free_irq(emac->rx_irq, ndev); 961*e15472e8SRoger Quadros 962*e15472e8SRoger Quadros if (netif_msg_drv(emac)) 963*e15472e8SRoger Quadros dev_notice(&ndev->dev, "stopped\n"); 964*e15472e8SRoger Quadros 965511f6c1aSRoger Quadros return 0; 966511f6c1aSRoger Quadros } 967511f6c1aSRoger Quadros 968*e15472e8SRoger Quadros /* VLAN-tag PCP to priority queue map for EMAC/Switch/HSR/PRP used by driver 969*e15472e8SRoger Quadros * Index is PCP val / 2. 970*e15472e8SRoger Quadros * low - pcp 0..3 maps to Q4 for Host 971*e15472e8SRoger Quadros * high - pcp 4..7 maps to Q3 for Host 972*e15472e8SRoger Quadros * low - pcp 0..3 maps to Q2 (FWD Queue) for PRU-x 973*e15472e8SRoger Quadros * where x = 1 for PRUETH_PORT_MII0 974*e15472e8SRoger Quadros * 0 for PRUETH_PORT_MII1 975*e15472e8SRoger Quadros * high - pcp 4..7 maps to Q1 (FWD Queue) for PRU-x 976*e15472e8SRoger Quadros */ 977*e15472e8SRoger Quadros static const unsigned short emac_pcp_tx_priority_queue_map[] = { 978*e15472e8SRoger Quadros PRUETH_QUEUE4, PRUETH_QUEUE4, 979*e15472e8SRoger Quadros PRUETH_QUEUE3, PRUETH_QUEUE3, 980*e15472e8SRoger Quadros PRUETH_QUEUE2, PRUETH_QUEUE2, 981*e15472e8SRoger Quadros PRUETH_QUEUE1, PRUETH_QUEUE1, 982*e15472e8SRoger Quadros }; 983*e15472e8SRoger Quadros 984*e15472e8SRoger Quadros static u16 icssm_prueth_get_tx_queue_id(struct prueth *prueth, 985*e15472e8SRoger Quadros struct sk_buff *skb) 986*e15472e8SRoger Quadros { 987*e15472e8SRoger Quadros u16 vlan_tci, pcp; 988*e15472e8SRoger Quadros int err; 989*e15472e8SRoger Quadros 990*e15472e8SRoger Quadros err = vlan_get_tag(skb, &vlan_tci); 991*e15472e8SRoger Quadros if (likely(err)) 992*e15472e8SRoger Quadros pcp = 0; 993*e15472e8SRoger Quadros else 994*e15472e8SRoger Quadros pcp = (vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; 995*e15472e8SRoger Quadros 996*e15472e8SRoger Quadros /* Below code (pcp >>= 1) is made common for all 997*e15472e8SRoger Quadros * protocols (i.e., EMAC, RSTP, HSR and PRP)* 998*e15472e8SRoger Quadros * pcp value 0,1 will be updated to 0 mapped to QUEUE4 999*e15472e8SRoger Quadros * pcp value 2,3 will be updated to 1 mapped to QUEUE4 1000*e15472e8SRoger Quadros * pcp value 4,5 will be updated to 2 mapped to QUEUE3 1001*e15472e8SRoger Quadros * pcp value 6,7 will be updated to 3 mapped to QUEUE3 1002*e15472e8SRoger Quadros */ 1003*e15472e8SRoger Quadros pcp >>= 1; 1004*e15472e8SRoger Quadros 1005*e15472e8SRoger Quadros return emac_pcp_tx_priority_queue_map[pcp]; 1006*e15472e8SRoger Quadros } 1007*e15472e8SRoger Quadros 1008*e15472e8SRoger Quadros /** 1009*e15472e8SRoger Quadros * icssm_emac_ndo_start_xmit - EMAC Transmit function 1010*e15472e8SRoger Quadros * @skb: SKB pointer 1011*e15472e8SRoger Quadros * @ndev: EMAC network adapter 1012*e15472e8SRoger Quadros * 1013*e15472e8SRoger Quadros * Called by the system to transmit a packet - we queue the packet in 1014*e15472e8SRoger Quadros * EMAC hardware transmit queue 1015*e15472e8SRoger Quadros * 1016*e15472e8SRoger Quadros * Return: enum netdev_tx 1017*e15472e8SRoger Quadros */ 1018*e15472e8SRoger Quadros static enum netdev_tx icssm_emac_ndo_start_xmit(struct sk_buff *skb, 1019*e15472e8SRoger Quadros struct net_device *ndev) 1020*e15472e8SRoger Quadros { 1021*e15472e8SRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 1022*e15472e8SRoger Quadros int ret; 1023*e15472e8SRoger Quadros u16 qid; 1024*e15472e8SRoger Quadros 1025*e15472e8SRoger Quadros qid = icssm_prueth_get_tx_queue_id(emac->prueth, skb); 1026*e15472e8SRoger Quadros ret = icssm_prueth_tx_enqueue(emac, skb, qid); 1027*e15472e8SRoger Quadros if (ret) { 1028*e15472e8SRoger Quadros if (ret != -ENOBUFS && netif_msg_tx_err(emac) && 1029*e15472e8SRoger Quadros net_ratelimit()) 1030*e15472e8SRoger Quadros netdev_err(ndev, "packet queue failed: %d\n", ret); 1031*e15472e8SRoger Quadros goto fail_tx; 1032*e15472e8SRoger Quadros } 1033*e15472e8SRoger Quadros 1034*e15472e8SRoger Quadros emac->stats.tx_packets++; 1035*e15472e8SRoger Quadros emac->stats.tx_bytes += skb->len; 1036*e15472e8SRoger Quadros dev_kfree_skb_any(skb); 1037*e15472e8SRoger Quadros 1038*e15472e8SRoger Quadros return NETDEV_TX_OK; 1039*e15472e8SRoger Quadros 1040*e15472e8SRoger Quadros fail_tx: 1041*e15472e8SRoger Quadros if (ret == -ENOBUFS) { 1042*e15472e8SRoger Quadros netif_stop_queue(ndev); 1043*e15472e8SRoger Quadros hrtimer_start(&emac->tx_hrtimer, 1044*e15472e8SRoger Quadros us_to_ktime(HR_TIMER_TX_DELAY_US), 1045*e15472e8SRoger Quadros HRTIMER_MODE_REL_PINNED); 1046*e15472e8SRoger Quadros ret = NETDEV_TX_BUSY; 1047*e15472e8SRoger Quadros } else { 1048*e15472e8SRoger Quadros /* error */ 1049*e15472e8SRoger Quadros emac->stats.tx_dropped++; 1050*e15472e8SRoger Quadros ret = NET_XMIT_DROP; 1051*e15472e8SRoger Quadros } 1052*e15472e8SRoger Quadros 1053*e15472e8SRoger Quadros return ret; 1054*e15472e8SRoger Quadros } 1055*e15472e8SRoger Quadros 1056*e15472e8SRoger Quadros /** 1057*e15472e8SRoger Quadros * icssm_emac_ndo_get_stats64 - EMAC get statistics function 1058*e15472e8SRoger Quadros * @ndev: The EMAC network adapter 1059*e15472e8SRoger Quadros * @stats: rtnl_link_stats structure 1060*e15472e8SRoger Quadros * 1061*e15472e8SRoger Quadros * Called when system wants to get statistics from the device. 1062*e15472e8SRoger Quadros * 1063*e15472e8SRoger Quadros */ 1064*e15472e8SRoger Quadros static void icssm_emac_ndo_get_stats64(struct net_device *ndev, 1065*e15472e8SRoger Quadros struct rtnl_link_stats64 *stats) 1066*e15472e8SRoger Quadros { 1067*e15472e8SRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 1068*e15472e8SRoger Quadros 1069*e15472e8SRoger Quadros stats->rx_packets = emac->stats.rx_packets; 1070*e15472e8SRoger Quadros stats->rx_bytes = emac->stats.rx_bytes; 1071*e15472e8SRoger Quadros stats->tx_packets = emac->stats.tx_packets; 1072*e15472e8SRoger Quadros stats->tx_bytes = emac->stats.tx_bytes; 1073*e15472e8SRoger Quadros stats->tx_dropped = emac->stats.tx_dropped; 1074*e15472e8SRoger Quadros stats->rx_over_errors = emac->stats.rx_over_errors; 1075*e15472e8SRoger Quadros stats->rx_length_errors = emac->stats.rx_length_errors; 1076*e15472e8SRoger Quadros } 1077*e15472e8SRoger Quadros 1078511f6c1aSRoger Quadros static const struct net_device_ops emac_netdev_ops = { 1079511f6c1aSRoger Quadros .ndo_open = icssm_emac_ndo_open, 1080511f6c1aSRoger Quadros .ndo_stop = icssm_emac_ndo_stop, 1081*e15472e8SRoger Quadros .ndo_start_xmit = icssm_emac_ndo_start_xmit, 1082*e15472e8SRoger Quadros .ndo_get_stats64 = icssm_emac_ndo_get_stats64, 1083511f6c1aSRoger Quadros }; 1084511f6c1aSRoger Quadros 1085511f6c1aSRoger Quadros /* get emac_port corresponding to eth_node name */ 1086511f6c1aSRoger Quadros static int icssm_prueth_node_port(struct device_node *eth_node) 1087511f6c1aSRoger Quadros { 1088511f6c1aSRoger Quadros u32 port_id; 1089511f6c1aSRoger Quadros int ret; 1090511f6c1aSRoger Quadros 1091511f6c1aSRoger Quadros ret = of_property_read_u32(eth_node, "reg", &port_id); 1092511f6c1aSRoger Quadros if (ret) 1093511f6c1aSRoger Quadros return ret; 1094511f6c1aSRoger Quadros 1095511f6c1aSRoger Quadros if (port_id == 0) 1096511f6c1aSRoger Quadros return PRUETH_PORT_MII0; 1097511f6c1aSRoger Quadros else if (port_id == 1) 1098511f6c1aSRoger Quadros return PRUETH_PORT_MII1; 1099511f6c1aSRoger Quadros else 1100511f6c1aSRoger Quadros return PRUETH_PORT_INVALID; 1101511f6c1aSRoger Quadros } 1102511f6c1aSRoger Quadros 1103511f6c1aSRoger Quadros /* get MAC instance corresponding to eth_node name */ 1104511f6c1aSRoger Quadros static int icssm_prueth_node_mac(struct device_node *eth_node) 1105511f6c1aSRoger Quadros { 1106511f6c1aSRoger Quadros u32 port_id; 1107511f6c1aSRoger Quadros int ret; 1108511f6c1aSRoger Quadros 1109511f6c1aSRoger Quadros ret = of_property_read_u32(eth_node, "reg", &port_id); 1110511f6c1aSRoger Quadros if (ret) 1111511f6c1aSRoger Quadros return ret; 1112511f6c1aSRoger Quadros 1113511f6c1aSRoger Quadros if (port_id == 0) 1114511f6c1aSRoger Quadros return PRUETH_MAC0; 1115511f6c1aSRoger Quadros else if (port_id == 1) 1116511f6c1aSRoger Quadros return PRUETH_MAC1; 1117511f6c1aSRoger Quadros else 1118511f6c1aSRoger Quadros return PRUETH_MAC_INVALID; 1119511f6c1aSRoger Quadros } 1120511f6c1aSRoger Quadros 1121*e15472e8SRoger Quadros static enum hrtimer_restart icssm_emac_tx_timer_callback(struct hrtimer *timer) 1122*e15472e8SRoger Quadros { 1123*e15472e8SRoger Quadros struct prueth_emac *emac = 1124*e15472e8SRoger Quadros container_of(timer, struct prueth_emac, tx_hrtimer); 1125*e15472e8SRoger Quadros 1126*e15472e8SRoger Quadros if (netif_queue_stopped(emac->ndev)) 1127*e15472e8SRoger Quadros netif_wake_queue(emac->ndev); 1128*e15472e8SRoger Quadros 1129*e15472e8SRoger Quadros return HRTIMER_NORESTART; 1130*e15472e8SRoger Quadros } 1131*e15472e8SRoger Quadros 1132511f6c1aSRoger Quadros static int icssm_prueth_netdev_init(struct prueth *prueth, 1133511f6c1aSRoger Quadros struct device_node *eth_node) 1134511f6c1aSRoger Quadros { 1135511f6c1aSRoger Quadros struct prueth_emac *emac; 1136511f6c1aSRoger Quadros struct net_device *ndev; 1137511f6c1aSRoger Quadros enum prueth_port port; 1138511f6c1aSRoger Quadros enum prueth_mac mac; 1139511f6c1aSRoger Quadros int ret; 1140511f6c1aSRoger Quadros 1141511f6c1aSRoger Quadros port = icssm_prueth_node_port(eth_node); 1142511f6c1aSRoger Quadros if (port == PRUETH_PORT_INVALID) 1143511f6c1aSRoger Quadros return -EINVAL; 1144511f6c1aSRoger Quadros 1145511f6c1aSRoger Quadros mac = icssm_prueth_node_mac(eth_node); 1146511f6c1aSRoger Quadros if (mac == PRUETH_MAC_INVALID) 1147511f6c1aSRoger Quadros return -EINVAL; 1148511f6c1aSRoger Quadros 1149511f6c1aSRoger Quadros ndev = devm_alloc_etherdev(prueth->dev, sizeof(*emac)); 1150511f6c1aSRoger Quadros if (!ndev) 1151511f6c1aSRoger Quadros return -ENOMEM; 1152511f6c1aSRoger Quadros 1153511f6c1aSRoger Quadros SET_NETDEV_DEV(ndev, prueth->dev); 1154511f6c1aSRoger Quadros emac = netdev_priv(ndev); 1155511f6c1aSRoger Quadros prueth->emac[mac] = emac; 1156511f6c1aSRoger Quadros emac->prueth = prueth; 1157511f6c1aSRoger Quadros emac->ndev = ndev; 1158511f6c1aSRoger Quadros emac->port_id = port; 1159511f6c1aSRoger Quadros 1160511f6c1aSRoger Quadros /* by default eth_type is EMAC */ 1161511f6c1aSRoger Quadros switch (port) { 1162511f6c1aSRoger Quadros case PRUETH_PORT_MII0: 1163*e15472e8SRoger Quadros emac->tx_port_queue = PRUETH_PORT_QUEUE_MII0; 1164*e15472e8SRoger Quadros 1165*e15472e8SRoger Quadros /* packets from MII0 are on queues 1 through 2 */ 1166*e15472e8SRoger Quadros emac->rx_queue_start = PRUETH_QUEUE1; 1167*e15472e8SRoger Quadros emac->rx_queue_end = PRUETH_QUEUE2; 1168*e15472e8SRoger Quadros 1169a99b5657SRoger Quadros emac->dram = PRUETH_MEM_DRAM0; 1170511f6c1aSRoger Quadros emac->pru = prueth->pru0; 1171511f6c1aSRoger Quadros break; 1172511f6c1aSRoger Quadros case PRUETH_PORT_MII1: 1173*e15472e8SRoger Quadros emac->tx_port_queue = PRUETH_PORT_QUEUE_MII1; 1174*e15472e8SRoger Quadros 1175*e15472e8SRoger Quadros /* packets from MII1 are on queues 3 through 4 */ 1176*e15472e8SRoger Quadros emac->rx_queue_start = PRUETH_QUEUE3; 1177*e15472e8SRoger Quadros emac->rx_queue_end = PRUETH_QUEUE4; 1178*e15472e8SRoger Quadros 1179a99b5657SRoger Quadros emac->dram = PRUETH_MEM_DRAM1; 1180511f6c1aSRoger Quadros emac->pru = prueth->pru1; 1181511f6c1aSRoger Quadros break; 1182511f6c1aSRoger Quadros default: 1183511f6c1aSRoger Quadros return -EINVAL; 1184511f6c1aSRoger Quadros } 1185*e15472e8SRoger Quadros 1186*e15472e8SRoger Quadros emac->rx_irq = of_irq_get_byname(eth_node, "rx"); 1187*e15472e8SRoger Quadros if (emac->rx_irq < 0) { 1188*e15472e8SRoger Quadros ret = emac->rx_irq; 1189*e15472e8SRoger Quadros if (ret != -EPROBE_DEFER) 1190*e15472e8SRoger Quadros dev_err(prueth->dev, "could not get rx irq\n"); 1191*e15472e8SRoger Quadros goto free; 1192*e15472e8SRoger Quadros } 1193*e15472e8SRoger Quadros 1194511f6c1aSRoger Quadros /* get mac address from DT and set private and netdev addr */ 1195511f6c1aSRoger Quadros ret = of_get_ethdev_address(eth_node, ndev); 1196511f6c1aSRoger Quadros if (!is_valid_ether_addr(ndev->dev_addr)) { 1197511f6c1aSRoger Quadros eth_hw_addr_random(ndev); 1198511f6c1aSRoger Quadros dev_warn(prueth->dev, "port %d: using random MAC addr: %pM\n", 1199511f6c1aSRoger Quadros port, ndev->dev_addr); 1200511f6c1aSRoger Quadros } 1201511f6c1aSRoger Quadros ether_addr_copy(emac->mac_addr, ndev->dev_addr); 1202511f6c1aSRoger Quadros 1203511f6c1aSRoger Quadros /* connect PHY */ 1204511f6c1aSRoger Quadros emac->phydev = of_phy_get_and_connect(ndev, eth_node, 1205511f6c1aSRoger Quadros icssm_emac_adjust_link); 1206511f6c1aSRoger Quadros if (!emac->phydev) { 1207511f6c1aSRoger Quadros dev_dbg(prueth->dev, "PHY connection failed\n"); 1208511f6c1aSRoger Quadros ret = -ENODEV; 1209511f6c1aSRoger Quadros goto free; 1210511f6c1aSRoger Quadros } 1211511f6c1aSRoger Quadros 1212511f6c1aSRoger Quadros /* remove unsupported modes */ 1213511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Full_BIT); 1214511f6c1aSRoger Quadros 1215511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT); 1216511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT); 1217511f6c1aSRoger Quadros 1218511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Pause_BIT); 1219511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT); 1220511f6c1aSRoger Quadros 1221511f6c1aSRoger Quadros ndev->dev.of_node = eth_node; 1222511f6c1aSRoger Quadros ndev->netdev_ops = &emac_netdev_ops; 1223511f6c1aSRoger Quadros 1224*e15472e8SRoger Quadros netif_napi_add(ndev, &emac->napi, icssm_emac_napi_poll); 1225*e15472e8SRoger Quadros 1226*e15472e8SRoger Quadros hrtimer_setup(&emac->tx_hrtimer, &icssm_emac_tx_timer_callback, 1227*e15472e8SRoger Quadros CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); 1228*e15472e8SRoger Quadros 1229511f6c1aSRoger Quadros return 0; 1230511f6c1aSRoger Quadros free: 1231511f6c1aSRoger Quadros emac->ndev = NULL; 1232511f6c1aSRoger Quadros prueth->emac[mac] = NULL; 1233511f6c1aSRoger Quadros 1234511f6c1aSRoger Quadros return ret; 1235511f6c1aSRoger Quadros } 1236511f6c1aSRoger Quadros 1237511f6c1aSRoger Quadros static void icssm_prueth_netdev_exit(struct prueth *prueth, 1238511f6c1aSRoger Quadros struct device_node *eth_node) 1239511f6c1aSRoger Quadros { 1240511f6c1aSRoger Quadros struct prueth_emac *emac; 1241511f6c1aSRoger Quadros enum prueth_mac mac; 1242511f6c1aSRoger Quadros 1243511f6c1aSRoger Quadros mac = icssm_prueth_node_mac(eth_node); 1244511f6c1aSRoger Quadros if (mac == PRUETH_MAC_INVALID) 1245511f6c1aSRoger Quadros return; 1246511f6c1aSRoger Quadros 1247511f6c1aSRoger Quadros emac = prueth->emac[mac]; 1248511f6c1aSRoger Quadros if (!emac) 1249511f6c1aSRoger Quadros return; 1250511f6c1aSRoger Quadros 1251511f6c1aSRoger Quadros phy_disconnect(emac->phydev); 1252511f6c1aSRoger Quadros 1253*e15472e8SRoger Quadros netif_napi_del(&emac->napi); 1254511f6c1aSRoger Quadros prueth->emac[mac] = NULL; 1255511f6c1aSRoger Quadros } 1256511f6c1aSRoger Quadros 1257511f6c1aSRoger Quadros static int icssm_prueth_probe(struct platform_device *pdev) 1258511f6c1aSRoger Quadros { 1259511f6c1aSRoger Quadros struct device_node *eth0_node = NULL, *eth1_node = NULL; 1260511f6c1aSRoger Quadros struct device_node *eth_node, *eth_ports_node; 1261511f6c1aSRoger Quadros enum pruss_pru_id pruss_id0, pruss_id1; 1262511f6c1aSRoger Quadros struct device *dev = &pdev->dev; 1263511f6c1aSRoger Quadros struct device_node *np; 1264511f6c1aSRoger Quadros struct prueth *prueth; 1265a99b5657SRoger Quadros struct pruss *pruss; 1266511f6c1aSRoger Quadros int i, ret; 1267511f6c1aSRoger Quadros 1268511f6c1aSRoger Quadros np = dev->of_node; 1269511f6c1aSRoger Quadros if (!np) 1270511f6c1aSRoger Quadros return -ENODEV; /* we don't support non DT */ 1271511f6c1aSRoger Quadros 1272511f6c1aSRoger Quadros prueth = devm_kzalloc(dev, sizeof(*prueth), GFP_KERNEL); 1273511f6c1aSRoger Quadros if (!prueth) 1274511f6c1aSRoger Quadros return -ENOMEM; 1275511f6c1aSRoger Quadros 1276511f6c1aSRoger Quadros platform_set_drvdata(pdev, prueth); 1277511f6c1aSRoger Quadros prueth->dev = dev; 1278511f6c1aSRoger Quadros prueth->fw_data = device_get_match_data(dev); 1279511f6c1aSRoger Quadros 1280511f6c1aSRoger Quadros eth_ports_node = of_get_child_by_name(np, "ethernet-ports"); 1281511f6c1aSRoger Quadros if (!eth_ports_node) 1282511f6c1aSRoger Quadros return -ENOENT; 1283511f6c1aSRoger Quadros 1284511f6c1aSRoger Quadros for_each_child_of_node(eth_ports_node, eth_node) { 1285511f6c1aSRoger Quadros u32 reg; 1286511f6c1aSRoger Quadros 1287511f6c1aSRoger Quadros if (strcmp(eth_node->name, "ethernet-port")) 1288511f6c1aSRoger Quadros continue; 1289511f6c1aSRoger Quadros ret = of_property_read_u32(eth_node, "reg", ®); 1290511f6c1aSRoger Quadros if (ret < 0) { 1291511f6c1aSRoger Quadros dev_err(dev, "%pOF error reading port_id %d\n", 1292511f6c1aSRoger Quadros eth_node, ret); 1293511f6c1aSRoger Quadros of_node_put(eth_node); 1294511f6c1aSRoger Quadros return ret; 1295511f6c1aSRoger Quadros } 1296511f6c1aSRoger Quadros 1297511f6c1aSRoger Quadros of_node_get(eth_node); 1298511f6c1aSRoger Quadros 1299511f6c1aSRoger Quadros if (reg == 0 && !eth0_node) { 1300511f6c1aSRoger Quadros eth0_node = eth_node; 1301511f6c1aSRoger Quadros if (!of_device_is_available(eth0_node)) { 1302511f6c1aSRoger Quadros of_node_put(eth0_node); 1303511f6c1aSRoger Quadros eth0_node = NULL; 1304511f6c1aSRoger Quadros } 1305511f6c1aSRoger Quadros } else if (reg == 1 && !eth1_node) { 1306511f6c1aSRoger Quadros eth1_node = eth_node; 1307511f6c1aSRoger Quadros if (!of_device_is_available(eth1_node)) { 1308511f6c1aSRoger Quadros of_node_put(eth1_node); 1309511f6c1aSRoger Quadros eth1_node = NULL; 1310511f6c1aSRoger Quadros } 1311511f6c1aSRoger Quadros } else { 1312511f6c1aSRoger Quadros if (reg == 0 || reg == 1) 1313511f6c1aSRoger Quadros dev_err(dev, "duplicate port reg value: %d\n", 1314511f6c1aSRoger Quadros reg); 1315511f6c1aSRoger Quadros else 1316511f6c1aSRoger Quadros dev_err(dev, "invalid port reg value: %d\n", 1317511f6c1aSRoger Quadros reg); 1318511f6c1aSRoger Quadros 1319511f6c1aSRoger Quadros of_node_put(eth_node); 1320511f6c1aSRoger Quadros } 1321511f6c1aSRoger Quadros } 1322511f6c1aSRoger Quadros 1323511f6c1aSRoger Quadros of_node_put(eth_ports_node); 1324511f6c1aSRoger Quadros 1325511f6c1aSRoger Quadros /* At least one node must be present and available else we fail */ 1326511f6c1aSRoger Quadros if (!eth0_node && !eth1_node) { 1327511f6c1aSRoger Quadros dev_err(dev, "neither port0 nor port1 node available\n"); 1328511f6c1aSRoger Quadros return -ENODEV; 1329511f6c1aSRoger Quadros } 1330511f6c1aSRoger Quadros 1331511f6c1aSRoger Quadros prueth->eth_node[PRUETH_MAC0] = eth0_node; 1332511f6c1aSRoger Quadros prueth->eth_node[PRUETH_MAC1] = eth1_node; 1333511f6c1aSRoger Quadros 1334a99b5657SRoger Quadros prueth->mii_rt = syscon_regmap_lookup_by_phandle(np, "ti,mii-rt"); 1335a99b5657SRoger Quadros if (IS_ERR(prueth->mii_rt)) { 1336a99b5657SRoger Quadros dev_err(dev, "couldn't get mii-rt syscon regmap\n"); 1337a99b5657SRoger Quadros return -ENODEV; 1338a99b5657SRoger Quadros } 1339a99b5657SRoger Quadros 1340511f6c1aSRoger Quadros if (eth0_node) { 1341511f6c1aSRoger Quadros prueth->pru0 = pru_rproc_get(np, 0, &pruss_id0); 1342511f6c1aSRoger Quadros if (IS_ERR(prueth->pru0)) { 1343511f6c1aSRoger Quadros ret = PTR_ERR(prueth->pru0); 1344511f6c1aSRoger Quadros dev_err_probe(dev, ret, "unable to get PRU0"); 1345511f6c1aSRoger Quadros goto put_pru; 1346511f6c1aSRoger Quadros } 1347511f6c1aSRoger Quadros } 1348511f6c1aSRoger Quadros 1349511f6c1aSRoger Quadros if (eth1_node) { 1350511f6c1aSRoger Quadros prueth->pru1 = pru_rproc_get(np, 1, &pruss_id1); 1351511f6c1aSRoger Quadros if (IS_ERR(prueth->pru1)) { 1352511f6c1aSRoger Quadros ret = PTR_ERR(prueth->pru1); 1353511f6c1aSRoger Quadros dev_err_probe(dev, ret, "unable to get PRU1"); 1354511f6c1aSRoger Quadros goto put_pru; 1355511f6c1aSRoger Quadros } 1356511f6c1aSRoger Quadros } 1357511f6c1aSRoger Quadros 1358a99b5657SRoger Quadros pruss = pruss_get(prueth->pru0 ? prueth->pru0 : prueth->pru1); 1359a99b5657SRoger Quadros if (IS_ERR(pruss)) { 1360a99b5657SRoger Quadros ret = PTR_ERR(pruss); 1361a99b5657SRoger Quadros dev_err(dev, "unable to get pruss handle\n"); 1362a99b5657SRoger Quadros goto put_pru; 1363a99b5657SRoger Quadros } 1364a99b5657SRoger Quadros prueth->pruss = pruss; 1365a99b5657SRoger Quadros 1366a99b5657SRoger Quadros /* Configure PRUSS */ 1367a99b5657SRoger Quadros if (eth0_node) 1368a99b5657SRoger Quadros pruss_cfg_gpimode(pruss, pruss_id0, PRUSS_GPI_MODE_MII); 1369a99b5657SRoger Quadros if (eth1_node) 1370a99b5657SRoger Quadros pruss_cfg_gpimode(pruss, pruss_id1, PRUSS_GPI_MODE_MII); 1371a99b5657SRoger Quadros pruss_cfg_miirt_enable(pruss, true); 1372a99b5657SRoger Quadros pruss_cfg_xfr_enable(pruss, PRU_TYPE_PRU, true); 1373a99b5657SRoger Quadros 1374a99b5657SRoger Quadros /* Get PRUSS mem resources */ 1375a99b5657SRoger Quadros /* OCMC is system resource which we get separately */ 1376a99b5657SRoger Quadros for (i = 0; i < ARRAY_SIZE(pruss_mem_ids); i++) { 1377a99b5657SRoger Quadros /* skip appropriate DRAM if not required */ 1378a99b5657SRoger Quadros if (!eth0_node && i == PRUETH_MEM_DRAM0) 1379a99b5657SRoger Quadros continue; 1380a99b5657SRoger Quadros 1381a99b5657SRoger Quadros if (!eth1_node && i == PRUETH_MEM_DRAM1) 1382a99b5657SRoger Quadros continue; 1383a99b5657SRoger Quadros 1384a99b5657SRoger Quadros ret = pruss_request_mem_region(pruss, pruss_mem_ids[i], 1385a99b5657SRoger Quadros &prueth->mem[i]); 1386a99b5657SRoger Quadros if (ret) { 1387a99b5657SRoger Quadros dev_err(dev, "unable to get PRUSS resource %d: %d\n", 1388a99b5657SRoger Quadros i, ret); 1389a99b5657SRoger Quadros goto put_mem; 1390a99b5657SRoger Quadros } 1391a99b5657SRoger Quadros } 1392a99b5657SRoger Quadros 1393a99b5657SRoger Quadros prueth->sram_pool = of_gen_pool_get(np, "sram", 0); 1394a99b5657SRoger Quadros if (!prueth->sram_pool) { 1395a99b5657SRoger Quadros dev_err(dev, "unable to get SRAM pool\n"); 1396a99b5657SRoger Quadros ret = -ENODEV; 1397a99b5657SRoger Quadros goto put_mem; 1398a99b5657SRoger Quadros } 1399a99b5657SRoger Quadros 1400a99b5657SRoger Quadros prueth->ocmc_ram_size = OCMC_RAM_SIZE; 1401a99b5657SRoger Quadros /* Decreased by 8KB to address the reserved region for AM33x */ 1402a99b5657SRoger Quadros if (prueth->fw_data->driver_data == PRUSS_AM33XX) 1403a99b5657SRoger Quadros prueth->ocmc_ram_size = (SZ_64K - SZ_8K); 1404a99b5657SRoger Quadros 1405a99b5657SRoger Quadros prueth->mem[PRUETH_MEM_OCMC].va = 1406a99b5657SRoger Quadros (void __iomem *)gen_pool_alloc(prueth->sram_pool, 1407a99b5657SRoger Quadros prueth->ocmc_ram_size); 1408a99b5657SRoger Quadros if (!prueth->mem[PRUETH_MEM_OCMC].va) { 1409a99b5657SRoger Quadros dev_err(dev, "unable to allocate OCMC resource\n"); 1410a99b5657SRoger Quadros ret = -ENOMEM; 1411a99b5657SRoger Quadros goto put_mem; 1412a99b5657SRoger Quadros } 1413a99b5657SRoger Quadros prueth->mem[PRUETH_MEM_OCMC].pa = gen_pool_virt_to_phys 1414a99b5657SRoger Quadros (prueth->sram_pool, (unsigned long) 1415a99b5657SRoger Quadros prueth->mem[PRUETH_MEM_OCMC].va); 1416a99b5657SRoger Quadros prueth->mem[PRUETH_MEM_OCMC].size = prueth->ocmc_ram_size; 1417a99b5657SRoger Quadros dev_dbg(dev, "ocmc: pa %pa va %p size %#zx\n", 1418a99b5657SRoger Quadros &prueth->mem[PRUETH_MEM_OCMC].pa, 1419a99b5657SRoger Quadros prueth->mem[PRUETH_MEM_OCMC].va, 1420a99b5657SRoger Quadros prueth->mem[PRUETH_MEM_OCMC].size); 1421a99b5657SRoger Quadros 1422511f6c1aSRoger Quadros /* setup netdev interfaces */ 1423511f6c1aSRoger Quadros if (eth0_node) { 1424511f6c1aSRoger Quadros ret = icssm_prueth_netdev_init(prueth, eth0_node); 1425511f6c1aSRoger Quadros if (ret) { 1426511f6c1aSRoger Quadros if (ret != -EPROBE_DEFER) { 1427511f6c1aSRoger Quadros dev_err(dev, "netdev init %s failed: %d\n", 1428511f6c1aSRoger Quadros eth0_node->name, ret); 1429511f6c1aSRoger Quadros } 1430a99b5657SRoger Quadros goto free_pool; 1431511f6c1aSRoger Quadros } 1432511f6c1aSRoger Quadros } 1433511f6c1aSRoger Quadros 1434511f6c1aSRoger Quadros if (eth1_node) { 1435511f6c1aSRoger Quadros ret = icssm_prueth_netdev_init(prueth, eth1_node); 1436511f6c1aSRoger Quadros if (ret) { 1437511f6c1aSRoger Quadros if (ret != -EPROBE_DEFER) { 1438511f6c1aSRoger Quadros dev_err(dev, "netdev init %s failed: %d\n", 1439511f6c1aSRoger Quadros eth1_node->name, ret); 1440511f6c1aSRoger Quadros } 1441511f6c1aSRoger Quadros goto netdev_exit; 1442511f6c1aSRoger Quadros } 1443511f6c1aSRoger Quadros } 1444511f6c1aSRoger Quadros 1445511f6c1aSRoger Quadros /* register the network devices */ 1446511f6c1aSRoger Quadros if (eth0_node) { 1447511f6c1aSRoger Quadros ret = register_netdev(prueth->emac[PRUETH_MAC0]->ndev); 1448511f6c1aSRoger Quadros if (ret) { 1449511f6c1aSRoger Quadros dev_err(dev, "can't register netdev for port MII0"); 1450511f6c1aSRoger Quadros goto netdev_exit; 1451511f6c1aSRoger Quadros } 1452511f6c1aSRoger Quadros 1453511f6c1aSRoger Quadros prueth->registered_netdevs[PRUETH_MAC0] = 1454511f6c1aSRoger Quadros prueth->emac[PRUETH_MAC0]->ndev; 1455511f6c1aSRoger Quadros } 1456511f6c1aSRoger Quadros 1457511f6c1aSRoger Quadros if (eth1_node) { 1458511f6c1aSRoger Quadros ret = register_netdev(prueth->emac[PRUETH_MAC1]->ndev); 1459511f6c1aSRoger Quadros if (ret) { 1460511f6c1aSRoger Quadros dev_err(dev, "can't register netdev for port MII1"); 1461511f6c1aSRoger Quadros goto netdev_unregister; 1462511f6c1aSRoger Quadros } 1463511f6c1aSRoger Quadros 1464511f6c1aSRoger Quadros prueth->registered_netdevs[PRUETH_MAC1] = 1465511f6c1aSRoger Quadros prueth->emac[PRUETH_MAC1]->ndev; 1466511f6c1aSRoger Quadros } 1467511f6c1aSRoger Quadros 1468a99b5657SRoger Quadros dev_info(dev, "TI PRU ethernet driver initialized: %s EMAC mode\n", 1469a99b5657SRoger Quadros (!eth0_node || !eth1_node) ? "single" : "dual"); 1470a99b5657SRoger Quadros 1471511f6c1aSRoger Quadros if (eth1_node) 1472511f6c1aSRoger Quadros of_node_put(eth1_node); 1473511f6c1aSRoger Quadros if (eth0_node) 1474511f6c1aSRoger Quadros of_node_put(eth0_node); 1475511f6c1aSRoger Quadros return 0; 1476511f6c1aSRoger Quadros 1477511f6c1aSRoger Quadros netdev_unregister: 1478511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 1479511f6c1aSRoger Quadros if (!prueth->registered_netdevs[i]) 1480511f6c1aSRoger Quadros continue; 1481511f6c1aSRoger Quadros unregister_netdev(prueth->registered_netdevs[i]); 1482511f6c1aSRoger Quadros } 1483511f6c1aSRoger Quadros 1484511f6c1aSRoger Quadros netdev_exit: 1485511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 1486511f6c1aSRoger Quadros eth_node = prueth->eth_node[i]; 1487511f6c1aSRoger Quadros if (!eth_node) 1488511f6c1aSRoger Quadros continue; 1489511f6c1aSRoger Quadros 1490511f6c1aSRoger Quadros icssm_prueth_netdev_exit(prueth, eth_node); 1491511f6c1aSRoger Quadros } 1492511f6c1aSRoger Quadros 1493a99b5657SRoger Quadros free_pool: 1494a99b5657SRoger Quadros gen_pool_free(prueth->sram_pool, 1495a99b5657SRoger Quadros (unsigned long)prueth->mem[PRUETH_MEM_OCMC].va, 1496a99b5657SRoger Quadros prueth->ocmc_ram_size); 1497a99b5657SRoger Quadros 1498a99b5657SRoger Quadros put_mem: 1499a99b5657SRoger Quadros for (i = PRUETH_MEM_DRAM0; i < PRUETH_MEM_OCMC; i++) { 1500a99b5657SRoger Quadros if (prueth->mem[i].va) 1501a99b5657SRoger Quadros pruss_release_mem_region(pruss, &prueth->mem[i]); 1502a99b5657SRoger Quadros } 1503a99b5657SRoger Quadros pruss_put(prueth->pruss); 1504a99b5657SRoger Quadros 1505511f6c1aSRoger Quadros put_pru: 1506511f6c1aSRoger Quadros if (eth1_node) { 1507511f6c1aSRoger Quadros if (prueth->pru1) 1508511f6c1aSRoger Quadros pru_rproc_put(prueth->pru1); 1509511f6c1aSRoger Quadros of_node_put(eth1_node); 1510511f6c1aSRoger Quadros } 1511511f6c1aSRoger Quadros 1512511f6c1aSRoger Quadros if (eth0_node) { 1513511f6c1aSRoger Quadros if (prueth->pru0) 1514511f6c1aSRoger Quadros pru_rproc_put(prueth->pru0); 1515511f6c1aSRoger Quadros of_node_put(eth0_node); 1516511f6c1aSRoger Quadros } 1517511f6c1aSRoger Quadros 1518511f6c1aSRoger Quadros return ret; 1519511f6c1aSRoger Quadros } 1520511f6c1aSRoger Quadros 1521511f6c1aSRoger Quadros static void icssm_prueth_remove(struct platform_device *pdev) 1522511f6c1aSRoger Quadros { 1523511f6c1aSRoger Quadros struct prueth *prueth = platform_get_drvdata(pdev); 1524511f6c1aSRoger Quadros struct device_node *eth_node; 1525511f6c1aSRoger Quadros int i; 1526511f6c1aSRoger Quadros 1527511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 1528511f6c1aSRoger Quadros if (!prueth->registered_netdevs[i]) 1529511f6c1aSRoger Quadros continue; 1530511f6c1aSRoger Quadros unregister_netdev(prueth->registered_netdevs[i]); 1531511f6c1aSRoger Quadros } 1532511f6c1aSRoger Quadros 1533511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 1534511f6c1aSRoger Quadros eth_node = prueth->eth_node[i]; 1535511f6c1aSRoger Quadros if (!eth_node) 1536511f6c1aSRoger Quadros continue; 1537511f6c1aSRoger Quadros 1538511f6c1aSRoger Quadros icssm_prueth_netdev_exit(prueth, eth_node); 1539511f6c1aSRoger Quadros of_node_put(eth_node); 1540511f6c1aSRoger Quadros } 1541511f6c1aSRoger Quadros 1542a99b5657SRoger Quadros gen_pool_free(prueth->sram_pool, 1543a99b5657SRoger Quadros (unsigned long)prueth->mem[PRUETH_MEM_OCMC].va, 1544a99b5657SRoger Quadros prueth->ocmc_ram_size); 1545a99b5657SRoger Quadros 1546a99b5657SRoger Quadros for (i = PRUETH_MEM_DRAM0; i < PRUETH_MEM_OCMC; i++) { 1547a99b5657SRoger Quadros if (prueth->mem[i].va) 1548a99b5657SRoger Quadros pruss_release_mem_region(prueth->pruss, 1549a99b5657SRoger Quadros &prueth->mem[i]); 1550a99b5657SRoger Quadros } 1551a99b5657SRoger Quadros 1552511f6c1aSRoger Quadros pruss_put(prueth->pruss); 1553511f6c1aSRoger Quadros 1554511f6c1aSRoger Quadros if (prueth->eth_node[PRUETH_MAC0]) 1555511f6c1aSRoger Quadros pru_rproc_put(prueth->pru0); 1556511f6c1aSRoger Quadros if (prueth->eth_node[PRUETH_MAC1]) 1557511f6c1aSRoger Quadros pru_rproc_put(prueth->pru1); 1558511f6c1aSRoger Quadros } 1559511f6c1aSRoger Quadros 1560511f6c1aSRoger Quadros #ifdef CONFIG_PM_SLEEP 1561511f6c1aSRoger Quadros static int icssm_prueth_suspend(struct device *dev) 1562511f6c1aSRoger Quadros { 1563511f6c1aSRoger Quadros struct prueth *prueth = dev_get_drvdata(dev); 1564511f6c1aSRoger Quadros struct net_device *ndev; 1565511f6c1aSRoger Quadros int i, ret; 1566511f6c1aSRoger Quadros 1567511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 1568511f6c1aSRoger Quadros ndev = prueth->registered_netdevs[i]; 1569511f6c1aSRoger Quadros 1570511f6c1aSRoger Quadros if (!ndev) 1571511f6c1aSRoger Quadros continue; 1572511f6c1aSRoger Quadros 1573511f6c1aSRoger Quadros if (netif_running(ndev)) { 1574511f6c1aSRoger Quadros netif_device_detach(ndev); 1575511f6c1aSRoger Quadros ret = icssm_emac_ndo_stop(ndev); 1576511f6c1aSRoger Quadros if (ret < 0) { 1577511f6c1aSRoger Quadros netdev_err(ndev, "failed to stop: %d", ret); 1578511f6c1aSRoger Quadros return ret; 1579511f6c1aSRoger Quadros } 1580511f6c1aSRoger Quadros } 1581511f6c1aSRoger Quadros } 1582511f6c1aSRoger Quadros 1583511f6c1aSRoger Quadros return 0; 1584511f6c1aSRoger Quadros } 1585511f6c1aSRoger Quadros 1586511f6c1aSRoger Quadros static int icssm_prueth_resume(struct device *dev) 1587511f6c1aSRoger Quadros { 1588511f6c1aSRoger Quadros struct prueth *prueth = dev_get_drvdata(dev); 1589511f6c1aSRoger Quadros struct net_device *ndev; 1590511f6c1aSRoger Quadros int i, ret; 1591511f6c1aSRoger Quadros 1592511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 1593511f6c1aSRoger Quadros ndev = prueth->registered_netdevs[i]; 1594511f6c1aSRoger Quadros 1595511f6c1aSRoger Quadros if (!ndev) 1596511f6c1aSRoger Quadros continue; 1597511f6c1aSRoger Quadros 1598511f6c1aSRoger Quadros if (netif_running(ndev)) { 1599511f6c1aSRoger Quadros ret = icssm_emac_ndo_open(ndev); 1600511f6c1aSRoger Quadros if (ret < 0) { 1601511f6c1aSRoger Quadros netdev_err(ndev, "failed to start: %d", ret); 1602511f6c1aSRoger Quadros return ret; 1603511f6c1aSRoger Quadros } 1604511f6c1aSRoger Quadros netif_device_attach(ndev); 1605511f6c1aSRoger Quadros } 1606511f6c1aSRoger Quadros } 1607511f6c1aSRoger Quadros 1608511f6c1aSRoger Quadros return 0; 1609511f6c1aSRoger Quadros } 1610511f6c1aSRoger Quadros 1611511f6c1aSRoger Quadros #endif /* CONFIG_PM_SLEEP */ 1612511f6c1aSRoger Quadros 1613511f6c1aSRoger Quadros static const struct dev_pm_ops prueth_dev_pm_ops = { 1614511f6c1aSRoger Quadros SET_SYSTEM_SLEEP_PM_OPS(icssm_prueth_suspend, icssm_prueth_resume) 1615511f6c1aSRoger Quadros }; 1616511f6c1aSRoger Quadros 1617511f6c1aSRoger Quadros /* AM335x SoC-specific firmware data */ 1618511f6c1aSRoger Quadros static struct prueth_private_data am335x_prueth_pdata = { 1619a99b5657SRoger Quadros .driver_data = PRUSS_AM33XX, 1620511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU0] = { 1621511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 1622511f6c1aSRoger Quadros "ti-pruss/am335x-pru0-prueth-fw.elf", 1623511f6c1aSRoger Quadros }, 1624511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU1] = { 1625511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 1626511f6c1aSRoger Quadros "ti-pruss/am335x-pru1-prueth-fw.elf", 1627511f6c1aSRoger Quadros }, 1628511f6c1aSRoger Quadros }; 1629511f6c1aSRoger Quadros 1630511f6c1aSRoger Quadros /* AM437x SoC-specific firmware data */ 1631511f6c1aSRoger Quadros static struct prueth_private_data am437x_prueth_pdata = { 1632a99b5657SRoger Quadros .driver_data = PRUSS_AM43XX, 1633511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU0] = { 1634511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 1635511f6c1aSRoger Quadros "ti-pruss/am437x-pru0-prueth-fw.elf", 1636511f6c1aSRoger Quadros }, 1637511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU1] = { 1638511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 1639511f6c1aSRoger Quadros "ti-pruss/am437x-pru1-prueth-fw.elf", 1640511f6c1aSRoger Quadros }, 1641511f6c1aSRoger Quadros }; 1642511f6c1aSRoger Quadros 1643511f6c1aSRoger Quadros /* AM57xx SoC-specific firmware data */ 1644511f6c1aSRoger Quadros static struct prueth_private_data am57xx_prueth_pdata = { 1645a99b5657SRoger Quadros .driver_data = PRUSS_AM57XX, 1646511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU0] = { 1647511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 1648511f6c1aSRoger Quadros "ti-pruss/am57xx-pru0-prueth-fw.elf", 1649511f6c1aSRoger Quadros }, 1650511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU1] = { 1651511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 1652511f6c1aSRoger Quadros "ti-pruss/am57xx-pru1-prueth-fw.elf", 1653511f6c1aSRoger Quadros }, 1654511f6c1aSRoger Quadros }; 1655511f6c1aSRoger Quadros 1656511f6c1aSRoger Quadros static const struct of_device_id prueth_dt_match[] = { 1657511f6c1aSRoger Quadros { .compatible = "ti,am57-prueth", .data = &am57xx_prueth_pdata, }, 1658511f6c1aSRoger Quadros { .compatible = "ti,am4376-prueth", .data = &am437x_prueth_pdata, }, 1659511f6c1aSRoger Quadros { .compatible = "ti,am3359-prueth", .data = &am335x_prueth_pdata, }, 1660511f6c1aSRoger Quadros { /* sentinel */ } 1661511f6c1aSRoger Quadros }; 1662511f6c1aSRoger Quadros MODULE_DEVICE_TABLE(of, prueth_dt_match); 1663511f6c1aSRoger Quadros 1664511f6c1aSRoger Quadros static struct platform_driver prueth_driver = { 1665511f6c1aSRoger Quadros .probe = icssm_prueth_probe, 1666511f6c1aSRoger Quadros .remove = icssm_prueth_remove, 1667511f6c1aSRoger Quadros .driver = { 1668511f6c1aSRoger Quadros .name = "prueth", 1669511f6c1aSRoger Quadros .of_match_table = prueth_dt_match, 1670511f6c1aSRoger Quadros .pm = &prueth_dev_pm_ops, 1671511f6c1aSRoger Quadros }, 1672511f6c1aSRoger Quadros }; 1673511f6c1aSRoger Quadros module_platform_driver(prueth_driver); 1674511f6c1aSRoger Quadros 1675511f6c1aSRoger Quadros MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>"); 1676511f6c1aSRoger Quadros MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 1677511f6c1aSRoger Quadros MODULE_DESCRIPTION("PRUSS ICSSM Ethernet Driver"); 1678511f6c1aSRoger Quadros MODULE_LICENSE("GPL"); 1679