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" 331853367bSParvathi Pudi #include "../icssg/icss_iep.h" 34a99b5657SRoger Quadros 35a99b5657SRoger Quadros #define OCMC_RAM_SIZE (SZ_64K) 36a99b5657SRoger Quadros 37a99b5657SRoger Quadros #define TX_START_DELAY 0x40 38a99b5657SRoger Quadros #define TX_CLK_DELAY_100M 0x6 39e15472e8SRoger Quadros #define HR_TIMER_TX_DELAY_US 100 40e15472e8SRoger Quadros 41e15472e8SRoger Quadros static void icssm_prueth_write_reg(struct prueth *prueth, 42e15472e8SRoger Quadros enum prueth_mem region, 43e15472e8SRoger Quadros unsigned int reg, u32 val) 44e15472e8SRoger Quadros { 45e15472e8SRoger Quadros writel_relaxed(val, prueth->mem[region].va + reg); 46e15472e8SRoger Quadros } 47a99b5657SRoger Quadros 48a99b5657SRoger Quadros /* Below macro is for 1528 Byte Frame support, to Allow even with 49a99b5657SRoger Quadros * Redundancy tag 50a99b5657SRoger Quadros */ 51a99b5657SRoger Quadros #define PRUSS_MII_RT_RX_FRMS_MAX_SUPPORT_EMAC (VLAN_ETH_FRAME_LEN + \ 52a99b5657SRoger Quadros ETH_FCS_LEN + \ 53a99b5657SRoger Quadros ICSSM_LRE_TAG_SIZE) 54a99b5657SRoger Quadros 55a99b5657SRoger Quadros /* ensure that order of PRUSS mem regions is same as enum prueth_mem */ 56a99b5657SRoger Quadros static enum pruss_mem pruss_mem_ids[] = { PRUSS_MEM_DRAM0, PRUSS_MEM_DRAM1, 57a99b5657SRoger Quadros PRUSS_MEM_SHRD_RAM2 }; 58a99b5657SRoger Quadros 59a99b5657SRoger Quadros static const struct prueth_queue_info queue_infos[][NUM_QUEUES] = { 60a99b5657SRoger Quadros [PRUETH_PORT_QUEUE_HOST] = { 61a99b5657SRoger Quadros [PRUETH_QUEUE1] = { 62a99b5657SRoger Quadros P0_Q1_BUFFER_OFFSET, 63a99b5657SRoger Quadros HOST_QUEUE_DESC_OFFSET, 64a99b5657SRoger Quadros P0_Q1_BD_OFFSET, 65a99b5657SRoger Quadros P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE), 66a99b5657SRoger Quadros }, 67a99b5657SRoger Quadros [PRUETH_QUEUE2] = { 68a99b5657SRoger Quadros P0_Q2_BUFFER_OFFSET, 69a99b5657SRoger Quadros HOST_QUEUE_DESC_OFFSET + 8, 70a99b5657SRoger Quadros P0_Q2_BD_OFFSET, 71a99b5657SRoger Quadros P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE), 72a99b5657SRoger Quadros }, 73a99b5657SRoger Quadros [PRUETH_QUEUE3] = { 74a99b5657SRoger Quadros P0_Q3_BUFFER_OFFSET, 75a99b5657SRoger Quadros HOST_QUEUE_DESC_OFFSET + 16, 76a99b5657SRoger Quadros P0_Q3_BD_OFFSET, 77a99b5657SRoger Quadros P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE), 78a99b5657SRoger Quadros }, 79a99b5657SRoger Quadros [PRUETH_QUEUE4] = { 80a99b5657SRoger Quadros P0_Q4_BUFFER_OFFSET, 81a99b5657SRoger Quadros HOST_QUEUE_DESC_OFFSET + 24, 82a99b5657SRoger Quadros P0_Q4_BD_OFFSET, 83a99b5657SRoger Quadros P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE), 84a99b5657SRoger Quadros }, 85a99b5657SRoger Quadros }, 86a99b5657SRoger Quadros [PRUETH_PORT_QUEUE_MII0] = { 87a99b5657SRoger Quadros [PRUETH_QUEUE1] = { 88a99b5657SRoger Quadros P1_Q1_BUFFER_OFFSET, 89a99b5657SRoger Quadros P1_Q1_BUFFER_OFFSET + ((QUEUE_1_SIZE - 1) * 90a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 91a99b5657SRoger Quadros P1_Q1_BD_OFFSET, 92a99b5657SRoger Quadros P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), 93a99b5657SRoger Quadros }, 94a99b5657SRoger Quadros [PRUETH_QUEUE2] = { 95a99b5657SRoger Quadros P1_Q2_BUFFER_OFFSET, 96a99b5657SRoger Quadros P1_Q2_BUFFER_OFFSET + ((QUEUE_2_SIZE - 1) * 97a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 98a99b5657SRoger Quadros P1_Q2_BD_OFFSET, 99a99b5657SRoger Quadros P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), 100a99b5657SRoger Quadros }, 101a99b5657SRoger Quadros [PRUETH_QUEUE3] = { 102a99b5657SRoger Quadros P1_Q3_BUFFER_OFFSET, 103a99b5657SRoger Quadros P1_Q3_BUFFER_OFFSET + ((QUEUE_3_SIZE - 1) * 104a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 105a99b5657SRoger Quadros P1_Q3_BD_OFFSET, 106a99b5657SRoger Quadros P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), 107a99b5657SRoger Quadros }, 108a99b5657SRoger Quadros [PRUETH_QUEUE4] = { 109a99b5657SRoger Quadros P1_Q4_BUFFER_OFFSET, 110a99b5657SRoger Quadros P1_Q4_BUFFER_OFFSET + ((QUEUE_4_SIZE - 1) * 111a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 112a99b5657SRoger Quadros P1_Q4_BD_OFFSET, 113a99b5657SRoger Quadros P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), 114a99b5657SRoger Quadros }, 115a99b5657SRoger Quadros }, 116a99b5657SRoger Quadros [PRUETH_PORT_QUEUE_MII1] = { 117a99b5657SRoger Quadros [PRUETH_QUEUE1] = { 118a99b5657SRoger Quadros P2_Q1_BUFFER_OFFSET, 119a99b5657SRoger Quadros P2_Q1_BUFFER_OFFSET + ((QUEUE_1_SIZE - 1) * 120a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 121a99b5657SRoger Quadros P2_Q1_BD_OFFSET, 122a99b5657SRoger Quadros P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), 123a99b5657SRoger Quadros }, 124a99b5657SRoger Quadros [PRUETH_QUEUE2] = { 125a99b5657SRoger Quadros P2_Q2_BUFFER_OFFSET, 126a99b5657SRoger Quadros P2_Q2_BUFFER_OFFSET + ((QUEUE_2_SIZE - 1) * 127a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 128a99b5657SRoger Quadros P2_Q2_BD_OFFSET, 129a99b5657SRoger Quadros P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), 130a99b5657SRoger Quadros }, 131a99b5657SRoger Quadros [PRUETH_QUEUE3] = { 132a99b5657SRoger Quadros P2_Q3_BUFFER_OFFSET, 133a99b5657SRoger Quadros P2_Q3_BUFFER_OFFSET + ((QUEUE_3_SIZE - 1) * 134a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 135a99b5657SRoger Quadros P2_Q3_BD_OFFSET, 136a99b5657SRoger Quadros P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), 137a99b5657SRoger Quadros }, 138a99b5657SRoger Quadros [PRUETH_QUEUE4] = { 139a99b5657SRoger Quadros P2_Q4_BUFFER_OFFSET, 140a99b5657SRoger Quadros P2_Q4_BUFFER_OFFSET + ((QUEUE_4_SIZE - 1) * 141a99b5657SRoger Quadros ICSS_BLOCK_SIZE), 142a99b5657SRoger Quadros P2_Q4_BD_OFFSET, 143a99b5657SRoger Quadros P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), 144a99b5657SRoger Quadros }, 145a99b5657SRoger Quadros }, 146a99b5657SRoger Quadros }; 147a99b5657SRoger Quadros 148a99b5657SRoger Quadros static const struct prueth_queue_desc queue_descs[][NUM_QUEUES] = { 149a99b5657SRoger Quadros [PRUETH_PORT_QUEUE_HOST] = { 150a99b5657SRoger Quadros { .rd_ptr = P0_Q1_BD_OFFSET, .wr_ptr = P0_Q1_BD_OFFSET, }, 151a99b5657SRoger Quadros { .rd_ptr = P0_Q2_BD_OFFSET, .wr_ptr = P0_Q2_BD_OFFSET, }, 152a99b5657SRoger Quadros { .rd_ptr = P0_Q3_BD_OFFSET, .wr_ptr = P0_Q3_BD_OFFSET, }, 153a99b5657SRoger Quadros { .rd_ptr = P0_Q4_BD_OFFSET, .wr_ptr = P0_Q4_BD_OFFSET, }, 154a99b5657SRoger Quadros }, 155a99b5657SRoger Quadros [PRUETH_PORT_QUEUE_MII0] = { 156a99b5657SRoger Quadros { .rd_ptr = P1_Q1_BD_OFFSET, .wr_ptr = P1_Q1_BD_OFFSET, }, 157a99b5657SRoger Quadros { .rd_ptr = P1_Q2_BD_OFFSET, .wr_ptr = P1_Q2_BD_OFFSET, }, 158a99b5657SRoger Quadros { .rd_ptr = P1_Q3_BD_OFFSET, .wr_ptr = P1_Q3_BD_OFFSET, }, 159a99b5657SRoger Quadros { .rd_ptr = P1_Q4_BD_OFFSET, .wr_ptr = P1_Q4_BD_OFFSET, }, 160a99b5657SRoger Quadros }, 161a99b5657SRoger Quadros [PRUETH_PORT_QUEUE_MII1] = { 162a99b5657SRoger Quadros { .rd_ptr = P2_Q1_BD_OFFSET, .wr_ptr = P2_Q1_BD_OFFSET, }, 163a99b5657SRoger Quadros { .rd_ptr = P2_Q2_BD_OFFSET, .wr_ptr = P2_Q2_BD_OFFSET, }, 164a99b5657SRoger Quadros { .rd_ptr = P2_Q3_BD_OFFSET, .wr_ptr = P2_Q3_BD_OFFSET, }, 165a99b5657SRoger Quadros { .rd_ptr = P2_Q4_BD_OFFSET, .wr_ptr = P2_Q4_BD_OFFSET, }, 166a99b5657SRoger Quadros } 167a99b5657SRoger Quadros }; 168a99b5657SRoger Quadros 169a99b5657SRoger Quadros static void icssm_prueth_hostconfig(struct prueth *prueth) 170a99b5657SRoger Quadros { 171a99b5657SRoger Quadros void __iomem *sram_base = prueth->mem[PRUETH_MEM_SHARED_RAM].va; 172a99b5657SRoger Quadros void __iomem *sram; 173a99b5657SRoger Quadros 174a99b5657SRoger Quadros /* queue size lookup table */ 175a99b5657SRoger Quadros sram = sram_base + HOST_QUEUE_SIZE_ADDR; 176a99b5657SRoger Quadros writew(HOST_QUEUE_1_SIZE, sram); 177a99b5657SRoger Quadros writew(HOST_QUEUE_2_SIZE, sram + 2); 178a99b5657SRoger Quadros writew(HOST_QUEUE_3_SIZE, sram + 4); 179a99b5657SRoger Quadros writew(HOST_QUEUE_4_SIZE, sram + 6); 180a99b5657SRoger Quadros 181a99b5657SRoger Quadros /* queue information table */ 182a99b5657SRoger Quadros sram = sram_base + HOST_Q1_RX_CONTEXT_OFFSET; 183a99b5657SRoger Quadros memcpy_toio(sram, queue_infos[PRUETH_PORT_QUEUE_HOST], 184a99b5657SRoger Quadros sizeof(queue_infos[PRUETH_PORT_QUEUE_HOST])); 185a99b5657SRoger Quadros 186a99b5657SRoger Quadros /* buffer offset table */ 187a99b5657SRoger Quadros sram = sram_base + HOST_QUEUE_OFFSET_ADDR; 188a99b5657SRoger Quadros writew(P0_Q1_BUFFER_OFFSET, sram); 189a99b5657SRoger Quadros writew(P0_Q2_BUFFER_OFFSET, sram + 2); 190a99b5657SRoger Quadros writew(P0_Q3_BUFFER_OFFSET, sram + 4); 191a99b5657SRoger Quadros writew(P0_Q4_BUFFER_OFFSET, sram + 6); 192a99b5657SRoger Quadros 193a99b5657SRoger Quadros /* buffer descriptor offset table*/ 194a99b5657SRoger Quadros sram = sram_base + HOST_QUEUE_DESCRIPTOR_OFFSET_ADDR; 195a99b5657SRoger Quadros writew(P0_Q1_BD_OFFSET, sram); 196a99b5657SRoger Quadros writew(P0_Q2_BD_OFFSET, sram + 2); 197a99b5657SRoger Quadros writew(P0_Q3_BD_OFFSET, sram + 4); 198a99b5657SRoger Quadros writew(P0_Q4_BD_OFFSET, sram + 6); 199a99b5657SRoger Quadros 200a99b5657SRoger Quadros /* queue table */ 201a99b5657SRoger Quadros sram = sram_base + HOST_QUEUE_DESC_OFFSET; 202a99b5657SRoger Quadros memcpy_toio(sram, queue_descs[PRUETH_PORT_QUEUE_HOST], 203a99b5657SRoger Quadros sizeof(queue_descs[PRUETH_PORT_QUEUE_HOST])); 204a99b5657SRoger Quadros } 205a99b5657SRoger Quadros 206a99b5657SRoger Quadros static void icssm_prueth_mii_init(struct prueth *prueth) 207a99b5657SRoger Quadros { 208a99b5657SRoger Quadros struct regmap *mii_rt; 209a99b5657SRoger Quadros u32 rxcfg_reg, rxcfg; 210a99b5657SRoger Quadros u32 txcfg_reg, txcfg; 211a99b5657SRoger Quadros 212a99b5657SRoger Quadros mii_rt = prueth->mii_rt; 213a99b5657SRoger Quadros 214a99b5657SRoger Quadros rxcfg = PRUSS_MII_RT_RXCFG_RX_ENABLE | 215a99b5657SRoger Quadros PRUSS_MII_RT_RXCFG_RX_DATA_RDY_MODE_DIS | 216a99b5657SRoger Quadros PRUSS_MII_RT_RXCFG_RX_L2_EN | 217a99b5657SRoger Quadros PRUSS_MII_RT_RXCFG_RX_CUT_PREAMBLE | 218a99b5657SRoger Quadros PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS; 219a99b5657SRoger Quadros 220a99b5657SRoger Quadros /* Configuration of Port 0 Rx */ 221a99b5657SRoger Quadros rxcfg_reg = PRUSS_MII_RT_RXCFG0; 222a99b5657SRoger Quadros 223a99b5657SRoger Quadros regmap_write(mii_rt, rxcfg_reg, rxcfg); 224a99b5657SRoger Quadros 225a99b5657SRoger Quadros /* Configuration of Port 1 Rx */ 226a99b5657SRoger Quadros rxcfg_reg = PRUSS_MII_RT_RXCFG1; 227a99b5657SRoger Quadros 228a99b5657SRoger Quadros rxcfg |= PRUSS_MII_RT_RXCFG_RX_MUX_SEL; 229a99b5657SRoger Quadros 230a99b5657SRoger Quadros regmap_write(mii_rt, rxcfg_reg, rxcfg); 231a99b5657SRoger Quadros 232a99b5657SRoger Quadros txcfg = PRUSS_MII_RT_TXCFG_TX_ENABLE | 233a99b5657SRoger Quadros PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE | 234a99b5657SRoger Quadros PRUSS_MII_RT_TXCFG_TX_32_MODE_EN | 235a99b5657SRoger Quadros (TX_START_DELAY << PRUSS_MII_RT_TXCFG_TX_START_DELAY_SHIFT) | 236a99b5657SRoger Quadros (TX_CLK_DELAY_100M << PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT); 237a99b5657SRoger Quadros 238a99b5657SRoger Quadros /* Configuration of Port 0 Tx */ 239a99b5657SRoger Quadros txcfg_reg = PRUSS_MII_RT_TXCFG0; 240a99b5657SRoger Quadros 241a99b5657SRoger Quadros regmap_write(mii_rt, txcfg_reg, txcfg); 242a99b5657SRoger Quadros 243a99b5657SRoger Quadros txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; 244a99b5657SRoger Quadros 245a99b5657SRoger Quadros /* Configuration of Port 1 Tx */ 246a99b5657SRoger Quadros txcfg_reg = PRUSS_MII_RT_TXCFG1; 247a99b5657SRoger Quadros 248a99b5657SRoger Quadros regmap_write(mii_rt, txcfg_reg, txcfg); 249a99b5657SRoger Quadros 250a99b5657SRoger Quadros txcfg_reg = PRUSS_MII_RT_RX_FRMS0; 251a99b5657SRoger Quadros 252a99b5657SRoger Quadros /* Min frame length should be set to 64 to allow receive of standard 253a99b5657SRoger Quadros * Ethernet frames such as PTP, LLDP that will not have the tag/rct. 254a99b5657SRoger Quadros * Actual size written to register is size - 1 per TRM. This also 255a99b5657SRoger Quadros * includes CRC/FCS. 256a99b5657SRoger Quadros */ 257a99b5657SRoger Quadros txcfg = FIELD_PREP(PRUSS_MII_RT_RX_FRMS_MIN_FRM_MASK, 258a99b5657SRoger Quadros (PRUSS_MII_RT_RX_FRMS_MIN_FRM - 1)); 259a99b5657SRoger Quadros 260a99b5657SRoger Quadros /* For EMAC, set Max frame size to 1528 i.e size with VLAN. 261a99b5657SRoger Quadros * Actual size written to register is size - 1 as per TRM. 262a99b5657SRoger Quadros * Since driver support run time change of protocol, driver 263a99b5657SRoger Quadros * must overwrite the values based on Ethernet type. 264a99b5657SRoger Quadros */ 265a99b5657SRoger Quadros txcfg |= FIELD_PREP(PRUSS_MII_RT_RX_FRMS_MAX_FRM_MASK, 266a99b5657SRoger Quadros (PRUSS_MII_RT_RX_FRMS_MAX_SUPPORT_EMAC - 1)); 267a99b5657SRoger Quadros 268a99b5657SRoger Quadros regmap_write(mii_rt, txcfg_reg, txcfg); 269a99b5657SRoger Quadros 270a99b5657SRoger Quadros txcfg_reg = PRUSS_MII_RT_RX_FRMS1; 271a99b5657SRoger Quadros 272a99b5657SRoger Quadros regmap_write(mii_rt, txcfg_reg, txcfg); 273a99b5657SRoger Quadros } 274a99b5657SRoger Quadros 275a99b5657SRoger Quadros static void icssm_prueth_clearmem(struct prueth *prueth, enum prueth_mem region) 276a99b5657SRoger Quadros { 277a99b5657SRoger Quadros memset_io(prueth->mem[region].va, 0, prueth->mem[region].size); 278a99b5657SRoger Quadros } 279a99b5657SRoger Quadros 280a99b5657SRoger Quadros static void icssm_prueth_hostinit(struct prueth *prueth) 281a99b5657SRoger Quadros { 282a99b5657SRoger Quadros /* Clear shared RAM */ 283a99b5657SRoger Quadros icssm_prueth_clearmem(prueth, PRUETH_MEM_SHARED_RAM); 284a99b5657SRoger Quadros 285a99b5657SRoger Quadros /* Clear OCMC RAM */ 286a99b5657SRoger Quadros icssm_prueth_clearmem(prueth, PRUETH_MEM_OCMC); 287a99b5657SRoger Quadros 288a99b5657SRoger Quadros /* Clear data RAMs */ 289a99b5657SRoger Quadros if (prueth->eth_node[PRUETH_MAC0]) 290a99b5657SRoger Quadros icssm_prueth_clearmem(prueth, PRUETH_MEM_DRAM0); 291a99b5657SRoger Quadros if (prueth->eth_node[PRUETH_MAC1]) 292a99b5657SRoger Quadros icssm_prueth_clearmem(prueth, PRUETH_MEM_DRAM1); 293a99b5657SRoger Quadros 294a99b5657SRoger Quadros /* Initialize host queues in shared RAM */ 295a99b5657SRoger Quadros icssm_prueth_hostconfig(prueth); 296a99b5657SRoger Quadros 297a99b5657SRoger Quadros /* Configure MII_RT */ 298a99b5657SRoger Quadros icssm_prueth_mii_init(prueth); 299a99b5657SRoger Quadros } 300a99b5657SRoger Quadros 301e15472e8SRoger Quadros /* This function initialize the driver in EMAC mode 302a99b5657SRoger Quadros * based on eth_type 303a99b5657SRoger Quadros */ 304a99b5657SRoger Quadros static void icssm_prueth_init_ethernet_mode(struct prueth *prueth) 305a99b5657SRoger Quadros { 306a99b5657SRoger Quadros icssm_prueth_hostinit(prueth); 307a99b5657SRoger Quadros } 308a99b5657SRoger Quadros 309e15472e8SRoger Quadros static void icssm_prueth_port_enable(struct prueth_emac *emac, bool enable) 310e15472e8SRoger Quadros { 311e15472e8SRoger Quadros struct prueth *prueth = emac->prueth; 312e15472e8SRoger Quadros void __iomem *port_ctrl; 313e15472e8SRoger Quadros void __iomem *ram; 314e15472e8SRoger Quadros 315e15472e8SRoger Quadros ram = prueth->mem[emac->dram].va; 316e15472e8SRoger Quadros port_ctrl = ram + PORT_CONTROL_ADDR; 317e15472e8SRoger Quadros writeb(!!enable, port_ctrl); 318e15472e8SRoger Quadros } 319e15472e8SRoger Quadros 320a99b5657SRoger Quadros static int icssm_prueth_emac_config(struct prueth_emac *emac) 321a99b5657SRoger Quadros { 322a99b5657SRoger Quadros struct prueth *prueth = emac->prueth; 323a99b5657SRoger Quadros u32 sharedramaddr, ocmcaddr; 324a99b5657SRoger Quadros void __iomem *dram_base; 325a99b5657SRoger Quadros void __iomem *mac_addr; 326a99b5657SRoger Quadros void __iomem *dram; 327e15472e8SRoger Quadros void __iomem *sram; 328a99b5657SRoger Quadros 329a99b5657SRoger Quadros /* PRU needs local shared RAM address for C28 */ 330a99b5657SRoger Quadros sharedramaddr = ICSS_LOCAL_SHARED_RAM; 331a99b5657SRoger Quadros /* PRU needs real global OCMC address for C30*/ 332a99b5657SRoger Quadros ocmcaddr = (u32)prueth->mem[PRUETH_MEM_OCMC].pa; 333e15472e8SRoger Quadros sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; 334a99b5657SRoger Quadros 335a99b5657SRoger Quadros /* Clear data RAM */ 336a99b5657SRoger Quadros icssm_prueth_clearmem(prueth, emac->dram); 337a99b5657SRoger Quadros 338a99b5657SRoger Quadros dram_base = prueth->mem[emac->dram].va; 339a99b5657SRoger Quadros 340a99b5657SRoger Quadros /* setup mac address */ 341a99b5657SRoger Quadros mac_addr = dram_base + PORT_MAC_ADDR; 342a99b5657SRoger Quadros memcpy_toio(mac_addr, emac->mac_addr, 6); 343a99b5657SRoger Quadros 344a99b5657SRoger Quadros /* queue information table */ 345a99b5657SRoger Quadros dram = dram_base + TX_CONTEXT_Q1_OFFSET_ADDR; 346a99b5657SRoger Quadros memcpy_toio(dram, queue_infos[emac->port_id], 347a99b5657SRoger Quadros sizeof(queue_infos[emac->port_id])); 348a99b5657SRoger Quadros 349a99b5657SRoger Quadros /* queue table */ 350a99b5657SRoger Quadros dram = dram_base + PORT_QUEUE_DESC_OFFSET; 351a99b5657SRoger Quadros memcpy_toio(dram, queue_descs[emac->port_id], 352a99b5657SRoger Quadros sizeof(queue_descs[emac->port_id])); 353a99b5657SRoger Quadros 354e15472e8SRoger Quadros emac->rx_queue_descs = sram + HOST_QUEUE_DESC_OFFSET; 355e15472e8SRoger Quadros emac->tx_queue_descs = dram; 356e15472e8SRoger Quadros 357a99b5657SRoger Quadros /* Set in constant table C28 of PRU0 to ICSS Shared memory */ 358a99b5657SRoger Quadros pru_rproc_set_ctable(emac->pru, PRU_C28, sharedramaddr); 359a99b5657SRoger Quadros 360a99b5657SRoger Quadros /* Set in constant table C30 of PRU0 to OCMC memory */ 361a99b5657SRoger Quadros pru_rproc_set_ctable(emac->pru, PRU_C30, ocmcaddr); 362a99b5657SRoger Quadros 363a99b5657SRoger Quadros return 0; 364a99b5657SRoger Quadros } 365511f6c1aSRoger Quadros 366511f6c1aSRoger Quadros /* called back by PHY layer if there is change in link state of hw port*/ 367511f6c1aSRoger Quadros static void icssm_emac_adjust_link(struct net_device *ndev) 368511f6c1aSRoger Quadros { 369511f6c1aSRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 370511f6c1aSRoger Quadros struct phy_device *phydev = emac->phydev; 371e15472e8SRoger Quadros struct prueth *prueth = emac->prueth; 372511f6c1aSRoger Quadros bool new_state = false; 373e15472e8SRoger Quadros enum prueth_mem region; 374511f6c1aSRoger Quadros unsigned long flags; 375e15472e8SRoger Quadros u32 port_status = 0; 376e15472e8SRoger Quadros u32 txcfg, mask; 377e15472e8SRoger Quadros u32 delay; 378511f6c1aSRoger Quadros 379511f6c1aSRoger Quadros spin_lock_irqsave(&emac->lock, flags); 380511f6c1aSRoger Quadros 381511f6c1aSRoger Quadros if (phydev->link) { 382511f6c1aSRoger Quadros /* check the mode of operation */ 383511f6c1aSRoger Quadros if (phydev->duplex != emac->duplex) { 384511f6c1aSRoger Quadros new_state = true; 385511f6c1aSRoger Quadros emac->duplex = phydev->duplex; 386511f6c1aSRoger Quadros } 387511f6c1aSRoger Quadros if (phydev->speed != emac->speed) { 388511f6c1aSRoger Quadros new_state = true; 389511f6c1aSRoger Quadros emac->speed = phydev->speed; 390511f6c1aSRoger Quadros } 391511f6c1aSRoger Quadros if (!emac->link) { 392511f6c1aSRoger Quadros new_state = true; 393511f6c1aSRoger Quadros emac->link = 1; 394511f6c1aSRoger Quadros } 395511f6c1aSRoger Quadros } else if (emac->link) { 396511f6c1aSRoger Quadros new_state = true; 397511f6c1aSRoger Quadros emac->link = 0; 398511f6c1aSRoger Quadros } 399511f6c1aSRoger Quadros 400e15472e8SRoger Quadros if (new_state) { 401511f6c1aSRoger Quadros phy_print_status(phydev); 402e15472e8SRoger Quadros region = emac->dram; 403e15472e8SRoger Quadros 404e15472e8SRoger Quadros /* update phy/port status information based on PHY values*/ 405e15472e8SRoger Quadros if (emac->link) { 406e15472e8SRoger Quadros port_status |= PORT_LINK_MASK; 407e15472e8SRoger Quadros 408e15472e8SRoger Quadros icssm_prueth_write_reg(prueth, region, PHY_SPEED_OFFSET, 409e15472e8SRoger Quadros emac->speed); 410e15472e8SRoger Quadros 411e15472e8SRoger Quadros delay = TX_CLK_DELAY_100M; 412e15472e8SRoger Quadros delay = delay << PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT; 413e15472e8SRoger Quadros mask = PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_MASK; 414e15472e8SRoger Quadros 415e15472e8SRoger Quadros if (emac->port_id) 416e15472e8SRoger Quadros txcfg = PRUSS_MII_RT_TXCFG1; 417e15472e8SRoger Quadros else 418e15472e8SRoger Quadros txcfg = PRUSS_MII_RT_TXCFG0; 419e15472e8SRoger Quadros 420e15472e8SRoger Quadros regmap_update_bits(prueth->mii_rt, txcfg, mask, delay); 421e15472e8SRoger Quadros } 422e15472e8SRoger Quadros 423e15472e8SRoger Quadros writeb(port_status, prueth->mem[region].va + 424e15472e8SRoger Quadros PORT_STATUS_OFFSET); 425e15472e8SRoger Quadros } 426511f6c1aSRoger Quadros 427511f6c1aSRoger Quadros if (emac->link) { 428511f6c1aSRoger Quadros /* reactivate the transmit queue if it is stopped */ 429511f6c1aSRoger Quadros if (netif_running(ndev) && netif_queue_stopped(ndev)) 430511f6c1aSRoger Quadros netif_wake_queue(ndev); 431511f6c1aSRoger Quadros } else { 432511f6c1aSRoger Quadros if (!netif_queue_stopped(ndev)) 433511f6c1aSRoger Quadros netif_stop_queue(ndev); 434511f6c1aSRoger Quadros } 435511f6c1aSRoger Quadros 436511f6c1aSRoger Quadros spin_unlock_irqrestore(&emac->lock, flags); 437511f6c1aSRoger Quadros } 438511f6c1aSRoger Quadros 439e15472e8SRoger Quadros static unsigned int 440e15472e8SRoger Quadros icssm_get_buff_desc_count(const struct prueth_queue_info *queue) 441e15472e8SRoger Quadros { 442e15472e8SRoger Quadros unsigned int buffer_desc_count; 443e15472e8SRoger Quadros 444e15472e8SRoger Quadros buffer_desc_count = queue->buffer_desc_end - 445e15472e8SRoger Quadros queue->buffer_desc_offset; 446e15472e8SRoger Quadros buffer_desc_count /= BD_SIZE; 447e15472e8SRoger Quadros buffer_desc_count++; 448e15472e8SRoger Quadros 449e15472e8SRoger Quadros return buffer_desc_count; 450e15472e8SRoger Quadros } 451e15472e8SRoger Quadros 452e15472e8SRoger Quadros static void icssm_get_block(struct prueth_queue_desc __iomem *queue_desc, 453e15472e8SRoger Quadros const struct prueth_queue_info *queue, 454e15472e8SRoger Quadros int *write_block, int *read_block) 455e15472e8SRoger Quadros { 456e15472e8SRoger Quadros *write_block = (readw(&queue_desc->wr_ptr) - 457e15472e8SRoger Quadros queue->buffer_desc_offset) / BD_SIZE; 458e15472e8SRoger Quadros *read_block = (readw(&queue_desc->rd_ptr) - 459e15472e8SRoger Quadros queue->buffer_desc_offset) / BD_SIZE; 460e15472e8SRoger Quadros } 461e15472e8SRoger Quadros 462e15472e8SRoger Quadros /** 463e15472e8SRoger Quadros * icssm_emac_rx_irq - EMAC Rx interrupt handler 464e15472e8SRoger Quadros * @irq: interrupt number 465e15472e8SRoger Quadros * @dev_id: pointer to net_device 466e15472e8SRoger Quadros * 467e15472e8SRoger Quadros * EMAC Interrupt handler - we only schedule NAPI and not process any packets 468e15472e8SRoger Quadros * here. 469e15472e8SRoger Quadros * 470e15472e8SRoger Quadros * Return: IRQ_HANDLED if the interrupt handled 471e15472e8SRoger Quadros */ 472e15472e8SRoger Quadros static irqreturn_t icssm_emac_rx_irq(int irq, void *dev_id) 473e15472e8SRoger Quadros { 474e15472e8SRoger Quadros struct net_device *ndev = (struct net_device *)dev_id; 475e15472e8SRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 476e15472e8SRoger Quadros 477e15472e8SRoger Quadros if (likely(netif_running(ndev))) { 478e15472e8SRoger Quadros /* disable Rx system event */ 479e15472e8SRoger Quadros disable_irq_nosync(emac->rx_irq); 480e15472e8SRoger Quadros napi_schedule(&emac->napi); 481e15472e8SRoger Quadros } 482e15472e8SRoger Quadros 483e15472e8SRoger Quadros return IRQ_HANDLED; 484e15472e8SRoger Quadros } 485e15472e8SRoger Quadros 486e15472e8SRoger Quadros /** 487e15472e8SRoger Quadros * icssm_prueth_tx_enqueue - queue a packet to firmware for transmission 488e15472e8SRoger Quadros * 489e15472e8SRoger Quadros * @emac: EMAC data structure 490e15472e8SRoger Quadros * @skb: packet data buffer 491e15472e8SRoger Quadros * @queue_id: priority queue id 492e15472e8SRoger Quadros * 493e15472e8SRoger Quadros * Return: 0 (Success) 494e15472e8SRoger Quadros */ 495e15472e8SRoger Quadros static int icssm_prueth_tx_enqueue(struct prueth_emac *emac, 496e15472e8SRoger Quadros struct sk_buff *skb, 497e15472e8SRoger Quadros enum prueth_queue_id queue_id) 498e15472e8SRoger Quadros { 499e15472e8SRoger Quadros struct prueth_queue_desc __iomem *queue_desc; 500e15472e8SRoger Quadros const struct prueth_queue_info *txqueue; 501e15472e8SRoger Quadros struct net_device *ndev = emac->ndev; 502e15472e8SRoger Quadros unsigned int buffer_desc_count; 503e15472e8SRoger Quadros int free_blocks, update_block; 504e15472e8SRoger Quadros bool buffer_wrapped = false; 505e15472e8SRoger Quadros int write_block, read_block; 506e15472e8SRoger Quadros void *src_addr, *dst_addr; 507e15472e8SRoger Quadros int pkt_block_size; 508e15472e8SRoger Quadros void __iomem *dram; 509e15472e8SRoger Quadros int txport, pktlen; 510e15472e8SRoger Quadros u16 update_wr_ptr; 511e15472e8SRoger Quadros u32 wr_buf_desc; 512e15472e8SRoger Quadros void *ocmc_ram; 513e15472e8SRoger Quadros 514e15472e8SRoger Quadros dram = emac->prueth->mem[emac->dram].va; 515e15472e8SRoger Quadros if (eth_skb_pad(skb)) { 516e15472e8SRoger Quadros if (netif_msg_tx_err(emac) && net_ratelimit()) 517e15472e8SRoger Quadros netdev_err(ndev, "packet pad failed\n"); 518e15472e8SRoger Quadros return -ENOMEM; 519e15472e8SRoger Quadros } 520e15472e8SRoger Quadros 521e15472e8SRoger Quadros /* which port to tx: MII0 or MII1 */ 522e15472e8SRoger Quadros txport = emac->tx_port_queue; 523e15472e8SRoger Quadros src_addr = skb->data; 524e15472e8SRoger Quadros pktlen = skb->len; 525e15472e8SRoger Quadros /* Get the tx queue */ 526e15472e8SRoger Quadros queue_desc = emac->tx_queue_descs + queue_id; 527e15472e8SRoger Quadros txqueue = &queue_infos[txport][queue_id]; 528e15472e8SRoger Quadros 529e15472e8SRoger Quadros buffer_desc_count = icssm_get_buff_desc_count(txqueue); 530e15472e8SRoger Quadros 531e15472e8SRoger Quadros /* the PRU firmware deals mostly in pointers already 532e15472e8SRoger Quadros * offset into ram, we would like to deal in indexes 533e15472e8SRoger Quadros * within the queue we are working with for code 534e15472e8SRoger Quadros * simplicity, calculate this here 535e15472e8SRoger Quadros */ 536e15472e8SRoger Quadros icssm_get_block(queue_desc, txqueue, &write_block, &read_block); 537e15472e8SRoger Quadros 538e15472e8SRoger Quadros if (write_block > read_block) { 539e15472e8SRoger Quadros free_blocks = buffer_desc_count - write_block; 540e15472e8SRoger Quadros free_blocks += read_block; 541e15472e8SRoger Quadros } else if (write_block < read_block) { 542e15472e8SRoger Quadros free_blocks = read_block - write_block; 543e15472e8SRoger Quadros } else { /* they are all free */ 544e15472e8SRoger Quadros free_blocks = buffer_desc_count; 545e15472e8SRoger Quadros } 546e15472e8SRoger Quadros 547e15472e8SRoger Quadros pkt_block_size = DIV_ROUND_UP(pktlen, ICSS_BLOCK_SIZE); 548e15472e8SRoger Quadros if (pkt_block_size > free_blocks) /* out of queue space */ 549e15472e8SRoger Quadros return -ENOBUFS; 550e15472e8SRoger Quadros 551e15472e8SRoger Quadros /* calculate end BD address post write */ 552e15472e8SRoger Quadros update_block = write_block + pkt_block_size; 553e15472e8SRoger Quadros 554e15472e8SRoger Quadros /* Check for wrap around */ 555e15472e8SRoger Quadros if (update_block >= buffer_desc_count) { 556e15472e8SRoger Quadros update_block %= buffer_desc_count; 557e15472e8SRoger Quadros buffer_wrapped = true; 558e15472e8SRoger Quadros } 559e15472e8SRoger Quadros 560e15472e8SRoger Quadros /* OCMC RAM is not cached and write order is not important */ 561e15472e8SRoger Quadros ocmc_ram = (__force void *)emac->prueth->mem[PRUETH_MEM_OCMC].va; 562e15472e8SRoger Quadros dst_addr = ocmc_ram + txqueue->buffer_offset + 563e15472e8SRoger Quadros (write_block * ICSS_BLOCK_SIZE); 564e15472e8SRoger Quadros 565e15472e8SRoger Quadros /* Copy the data from socket buffer(DRAM) to PRU buffers(OCMC) */ 566e15472e8SRoger Quadros if (buffer_wrapped) { /* wrapped around buffer */ 567e15472e8SRoger Quadros int bytes = (buffer_desc_count - write_block) * ICSS_BLOCK_SIZE; 568e15472e8SRoger Quadros int remaining; 569e15472e8SRoger Quadros 570e15472e8SRoger Quadros /* bytes is integral multiple of ICSS_BLOCK_SIZE but 571e15472e8SRoger Quadros * entire packet may have fit within the last BD 572e15472e8SRoger Quadros * if pkt_info.length is not integral multiple of 573e15472e8SRoger Quadros * ICSS_BLOCK_SIZE 574e15472e8SRoger Quadros */ 575e15472e8SRoger Quadros if (pktlen < bytes) 576e15472e8SRoger Quadros bytes = pktlen; 577e15472e8SRoger Quadros 578e15472e8SRoger Quadros /* copy non-wrapped part */ 579e15472e8SRoger Quadros memcpy(dst_addr, src_addr, bytes); 580e15472e8SRoger Quadros 581e15472e8SRoger Quadros /* copy wrapped part */ 582e15472e8SRoger Quadros src_addr += bytes; 583e15472e8SRoger Quadros remaining = pktlen - bytes; 584e15472e8SRoger Quadros dst_addr = ocmc_ram + txqueue->buffer_offset; 585e15472e8SRoger Quadros memcpy(dst_addr, src_addr, remaining); 586e15472e8SRoger Quadros } else { 587e15472e8SRoger Quadros memcpy(dst_addr, src_addr, pktlen); 588e15472e8SRoger Quadros } 589e15472e8SRoger Quadros 590e15472e8SRoger Quadros /* update first buffer descriptor */ 591e15472e8SRoger Quadros wr_buf_desc = (pktlen << PRUETH_BD_LENGTH_SHIFT) & 592e15472e8SRoger Quadros PRUETH_BD_LENGTH_MASK; 593e15472e8SRoger Quadros writel(wr_buf_desc, dram + readw(&queue_desc->wr_ptr)); 594e15472e8SRoger Quadros 595e15472e8SRoger Quadros /* update the write pointer in this queue descriptor, the firmware 596e15472e8SRoger Quadros * polls for this change so this will signal the start of transmission 597e15472e8SRoger Quadros */ 598e15472e8SRoger Quadros update_wr_ptr = txqueue->buffer_desc_offset + (update_block * BD_SIZE); 599e15472e8SRoger Quadros writew(update_wr_ptr, &queue_desc->wr_ptr); 600e15472e8SRoger Quadros 601e15472e8SRoger Quadros return 0; 602e15472e8SRoger Quadros } 603e15472e8SRoger Quadros 604e15472e8SRoger Quadros void icssm_parse_packet_info(struct prueth *prueth, u32 buffer_descriptor, 605e15472e8SRoger Quadros struct prueth_packet_info *pkt_info) 606e15472e8SRoger Quadros { 607e15472e8SRoger Quadros pkt_info->shadow = !!(buffer_descriptor & PRUETH_BD_SHADOW_MASK); 608e15472e8SRoger Quadros pkt_info->port = (buffer_descriptor & PRUETH_BD_PORT_MASK) >> 609e15472e8SRoger Quadros PRUETH_BD_PORT_SHIFT; 610e15472e8SRoger Quadros pkt_info->length = (buffer_descriptor & PRUETH_BD_LENGTH_MASK) >> 611e15472e8SRoger Quadros PRUETH_BD_LENGTH_SHIFT; 612e15472e8SRoger Quadros pkt_info->broadcast = !!(buffer_descriptor & PRUETH_BD_BROADCAST_MASK); 613e15472e8SRoger Quadros pkt_info->error = !!(buffer_descriptor & PRUETH_BD_ERROR_MASK); 614e15472e8SRoger Quadros pkt_info->lookup_success = !!(buffer_descriptor & 615e15472e8SRoger Quadros PRUETH_BD_LOOKUP_SUCCESS_MASK); 616e15472e8SRoger Quadros pkt_info->flood = !!(buffer_descriptor & PRUETH_BD_SW_FLOOD_MASK); 617e15472e8SRoger Quadros pkt_info->timestamp = !!(buffer_descriptor & PRUETH_BD_TIMESTAMP_MASK); 618e15472e8SRoger Quadros } 619e15472e8SRoger Quadros 620e15472e8SRoger Quadros /** 621e15472e8SRoger Quadros * icssm_emac_rx_packet - EMAC Receive function 622e15472e8SRoger Quadros * 623e15472e8SRoger Quadros * @emac: EMAC data structure 624e15472e8SRoger Quadros * @bd_rd_ptr: Buffer descriptor read pointer 625e15472e8SRoger Quadros * @pkt_info: packet information structure 626e15472e8SRoger Quadros * @rxqueue: Receive queue information structure 627e15472e8SRoger Quadros * 628e15472e8SRoger Quadros * Get a packet from receive queue 629e15472e8SRoger Quadros * 630e15472e8SRoger Quadros * Return: 0 (Success) 631e15472e8SRoger Quadros */ 632e15472e8SRoger Quadros int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, 633e15472e8SRoger Quadros struct prueth_packet_info *pkt_info, 634e15472e8SRoger Quadros const struct prueth_queue_info *rxqueue) 635e15472e8SRoger Quadros { 636e15472e8SRoger Quadros struct net_device *ndev = emac->ndev; 637e15472e8SRoger Quadros unsigned int buffer_desc_count; 638e15472e8SRoger Quadros int read_block, update_block; 639e15472e8SRoger Quadros unsigned int actual_pkt_len; 640e15472e8SRoger Quadros bool buffer_wrapped = false; 641e15472e8SRoger Quadros void *src_addr, *dst_addr; 642e15472e8SRoger Quadros struct sk_buff *skb; 643e15472e8SRoger Quadros int pkt_block_size; 644e15472e8SRoger Quadros void *ocmc_ram; 645e15472e8SRoger Quadros 646e15472e8SRoger Quadros /* the PRU firmware deals mostly in pointers already 647e15472e8SRoger Quadros * offset into ram, we would like to deal in indexes 648e15472e8SRoger Quadros * within the queue we are working with for code 649e15472e8SRoger Quadros * simplicity, calculate this here 650e15472e8SRoger Quadros */ 651e15472e8SRoger Quadros buffer_desc_count = icssm_get_buff_desc_count(rxqueue); 652e15472e8SRoger Quadros read_block = (*bd_rd_ptr - rxqueue->buffer_desc_offset) / BD_SIZE; 653e15472e8SRoger Quadros pkt_block_size = DIV_ROUND_UP(pkt_info->length, ICSS_BLOCK_SIZE); 654e15472e8SRoger Quadros 655e15472e8SRoger Quadros /* calculate end BD address post read */ 656e15472e8SRoger Quadros update_block = read_block + pkt_block_size; 657e15472e8SRoger Quadros 658e15472e8SRoger Quadros /* Check for wrap around */ 659e15472e8SRoger Quadros if (update_block >= buffer_desc_count) { 660e15472e8SRoger Quadros update_block %= buffer_desc_count; 661e15472e8SRoger Quadros if (update_block) 662e15472e8SRoger Quadros buffer_wrapped = true; 663e15472e8SRoger Quadros } 664e15472e8SRoger Quadros 665e15472e8SRoger Quadros /* calculate new pointer in ram */ 666e15472e8SRoger Quadros *bd_rd_ptr = rxqueue->buffer_desc_offset + (update_block * BD_SIZE); 667e15472e8SRoger Quadros 668e15472e8SRoger Quadros actual_pkt_len = pkt_info->length; 669e15472e8SRoger Quadros 670e15472e8SRoger Quadros /* Allocate a socket buffer for this packet */ 671e15472e8SRoger Quadros skb = netdev_alloc_skb_ip_align(ndev, actual_pkt_len); 672e15472e8SRoger Quadros if (!skb) { 673e15472e8SRoger Quadros if (netif_msg_rx_err(emac) && net_ratelimit()) 674e15472e8SRoger Quadros netdev_err(ndev, "failed rx buffer alloc\n"); 675e15472e8SRoger Quadros return -ENOMEM; 676e15472e8SRoger Quadros } 677e15472e8SRoger Quadros 678e15472e8SRoger Quadros dst_addr = skb->data; 679e15472e8SRoger Quadros 680e15472e8SRoger Quadros /* OCMC RAM is not cached and read order is not important */ 681e15472e8SRoger Quadros ocmc_ram = (__force void *)emac->prueth->mem[PRUETH_MEM_OCMC].va; 682e15472e8SRoger Quadros 683e15472e8SRoger Quadros /* Get the start address of the first buffer from 684e15472e8SRoger Quadros * the read buffer description 685e15472e8SRoger Quadros */ 686e15472e8SRoger Quadros src_addr = ocmc_ram + rxqueue->buffer_offset + 687e15472e8SRoger Quadros (read_block * ICSS_BLOCK_SIZE); 688e15472e8SRoger Quadros 689e15472e8SRoger Quadros /* Copy the data from PRU buffers(OCMC) to socket buffer(DRAM) */ 690e15472e8SRoger Quadros if (buffer_wrapped) { /* wrapped around buffer */ 691e15472e8SRoger Quadros int bytes = (buffer_desc_count - read_block) * ICSS_BLOCK_SIZE; 692e15472e8SRoger Quadros int remaining; 693e15472e8SRoger Quadros /* bytes is integral multiple of ICSS_BLOCK_SIZE but 694e15472e8SRoger Quadros * entire packet may have fit within the last BD 695e15472e8SRoger Quadros * if pkt_info.length is not integral multiple of 696e15472e8SRoger Quadros * ICSS_BLOCK_SIZE 697e15472e8SRoger Quadros */ 698e15472e8SRoger Quadros if (pkt_info->length < bytes) 699e15472e8SRoger Quadros bytes = pkt_info->length; 700e15472e8SRoger Quadros 701e15472e8SRoger Quadros /* copy non-wrapped part */ 702e15472e8SRoger Quadros memcpy(dst_addr, src_addr, bytes); 703e15472e8SRoger Quadros 704e15472e8SRoger Quadros /* copy wrapped part */ 705e15472e8SRoger Quadros dst_addr += bytes; 706e15472e8SRoger Quadros remaining = actual_pkt_len - bytes; 707e15472e8SRoger Quadros 708e15472e8SRoger Quadros src_addr = ocmc_ram + rxqueue->buffer_offset; 709e15472e8SRoger Quadros memcpy(dst_addr, src_addr, remaining); 710e15472e8SRoger Quadros src_addr += remaining; 711e15472e8SRoger Quadros } else { 712e15472e8SRoger Quadros memcpy(dst_addr, src_addr, actual_pkt_len); 713e15472e8SRoger Quadros src_addr += actual_pkt_len; 714e15472e8SRoger Quadros } 715e15472e8SRoger Quadros 716e15472e8SRoger Quadros skb_put(skb, actual_pkt_len); 717e15472e8SRoger Quadros 718e15472e8SRoger Quadros /* send packet up the stack */ 719e15472e8SRoger Quadros skb->protocol = eth_type_trans(skb, ndev); 720e15472e8SRoger Quadros netif_receive_skb(skb); 721e15472e8SRoger Quadros 722e15472e8SRoger Quadros /* update stats */ 723e15472e8SRoger Quadros emac->stats.rx_bytes += actual_pkt_len; 724e15472e8SRoger Quadros emac->stats.rx_packets++; 725e15472e8SRoger Quadros 726e15472e8SRoger Quadros return 0; 727e15472e8SRoger Quadros } 728e15472e8SRoger Quadros 729e15472e8SRoger Quadros static int icssm_emac_rx_packets(struct prueth_emac *emac, int budget) 730e15472e8SRoger Quadros { 731e15472e8SRoger Quadros struct prueth_queue_desc __iomem *queue_desc; 732e15472e8SRoger Quadros const struct prueth_queue_info *rxqueue; 733e15472e8SRoger Quadros struct prueth *prueth = emac->prueth; 734e15472e8SRoger Quadros struct prueth_packet_info pkt_info; 735e15472e8SRoger Quadros int start_queue, end_queue; 736e15472e8SRoger Quadros void __iomem *shared_ram; 737e15472e8SRoger Quadros u16 bd_rd_ptr, bd_wr_ptr; 738e15472e8SRoger Quadros u16 update_rd_ptr; 739e15472e8SRoger Quadros u8 overflow_cnt; 740e15472e8SRoger Quadros u32 rd_buf_desc; 741e15472e8SRoger Quadros int used = 0; 742e15472e8SRoger Quadros int i, ret; 743e15472e8SRoger Quadros 744e15472e8SRoger Quadros shared_ram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; 745e15472e8SRoger Quadros 746e15472e8SRoger Quadros start_queue = emac->rx_queue_start; 747e15472e8SRoger Quadros end_queue = emac->rx_queue_end; 748e15472e8SRoger Quadros 749e15472e8SRoger Quadros /* skip Rx if budget is 0 */ 750e15472e8SRoger Quadros if (!budget) 751e15472e8SRoger Quadros return 0; 752e15472e8SRoger Quadros 753e15472e8SRoger Quadros /* search host queues for packets */ 754e15472e8SRoger Quadros for (i = start_queue; i <= end_queue; i++) { 755e15472e8SRoger Quadros queue_desc = emac->rx_queue_descs + i; 756e15472e8SRoger Quadros rxqueue = &queue_infos[PRUETH_PORT_HOST][i]; 757e15472e8SRoger Quadros 758e15472e8SRoger Quadros overflow_cnt = readb(&queue_desc->overflow_cnt); 759e15472e8SRoger Quadros if (overflow_cnt > 0) { 760e15472e8SRoger Quadros emac->stats.rx_over_errors += overflow_cnt; 761e15472e8SRoger Quadros /* reset to zero */ 762e15472e8SRoger Quadros writeb(0, &queue_desc->overflow_cnt); 763e15472e8SRoger Quadros } 764e15472e8SRoger Quadros 765e15472e8SRoger Quadros bd_rd_ptr = readw(&queue_desc->rd_ptr); 766e15472e8SRoger Quadros bd_wr_ptr = readw(&queue_desc->wr_ptr); 767e15472e8SRoger Quadros 768e15472e8SRoger Quadros /* while packets are available in this queue */ 769e15472e8SRoger Quadros while (bd_rd_ptr != bd_wr_ptr) { 770e15472e8SRoger Quadros /* get packet info from the read buffer descriptor */ 771e15472e8SRoger Quadros rd_buf_desc = readl(shared_ram + bd_rd_ptr); 772e15472e8SRoger Quadros icssm_parse_packet_info(prueth, rd_buf_desc, &pkt_info); 773e15472e8SRoger Quadros 774e15472e8SRoger Quadros if (pkt_info.length <= 0) { 775e15472e8SRoger Quadros /* a packet length of zero will cause us to 776e15472e8SRoger Quadros * never move the read pointer ahead, locking 777e15472e8SRoger Quadros * the driver, so we manually have to move it 778e15472e8SRoger Quadros * to the write pointer, discarding all 779e15472e8SRoger Quadros * remaining packets in this queue. This should 780e15472e8SRoger Quadros * never happen. 781e15472e8SRoger Quadros */ 782e15472e8SRoger Quadros update_rd_ptr = bd_wr_ptr; 783e15472e8SRoger Quadros emac->stats.rx_length_errors++; 784e15472e8SRoger Quadros } else if (pkt_info.length > EMAC_MAX_FRM_SUPPORT) { 785e15472e8SRoger Quadros /* if the packet is too large we skip it but we 786e15472e8SRoger Quadros * still need to move the read pointer ahead 787e15472e8SRoger Quadros * and assume something is wrong with the read 788e15472e8SRoger Quadros * pointer as the firmware should be filtering 789e15472e8SRoger Quadros * these packets 790e15472e8SRoger Quadros */ 791e15472e8SRoger Quadros update_rd_ptr = bd_wr_ptr; 792e15472e8SRoger Quadros emac->stats.rx_length_errors++; 793e15472e8SRoger Quadros } else { 794e15472e8SRoger Quadros update_rd_ptr = bd_rd_ptr; 795e15472e8SRoger Quadros ret = icssm_emac_rx_packet(emac, &update_rd_ptr, 796e15472e8SRoger Quadros &pkt_info, rxqueue); 797e15472e8SRoger Quadros if (ret) 798e15472e8SRoger Quadros return used; 799e15472e8SRoger Quadros used++; 800e15472e8SRoger Quadros } 801e15472e8SRoger Quadros 802e15472e8SRoger Quadros /* after reading the buffer descriptor we clear it 803e15472e8SRoger Quadros * to prevent improperly moved read pointer errors 804e15472e8SRoger Quadros * from simply looking like old packets. 805e15472e8SRoger Quadros */ 806e15472e8SRoger Quadros writel(0, shared_ram + bd_rd_ptr); 807e15472e8SRoger Quadros 808e15472e8SRoger Quadros /* update read pointer in queue descriptor */ 809e15472e8SRoger Quadros writew(update_rd_ptr, &queue_desc->rd_ptr); 810e15472e8SRoger Quadros bd_rd_ptr = update_rd_ptr; 811e15472e8SRoger Quadros 812e15472e8SRoger Quadros /* all we have room for? */ 813e15472e8SRoger Quadros if (used >= budget) 814e15472e8SRoger Quadros return used; 815e15472e8SRoger Quadros } 816e15472e8SRoger Quadros } 817e15472e8SRoger Quadros 818e15472e8SRoger Quadros return used; 819e15472e8SRoger Quadros } 820e15472e8SRoger Quadros 821e15472e8SRoger Quadros static int icssm_emac_napi_poll(struct napi_struct *napi, int budget) 822e15472e8SRoger Quadros { 823e15472e8SRoger Quadros struct prueth_emac *emac = container_of(napi, struct prueth_emac, napi); 824e15472e8SRoger Quadros int num_rx; 825e15472e8SRoger Quadros 826e15472e8SRoger Quadros num_rx = icssm_emac_rx_packets(emac, budget); 827e15472e8SRoger Quadros 828e15472e8SRoger Quadros if (num_rx < budget && napi_complete_done(napi, num_rx)) 829e15472e8SRoger Quadros enable_irq(emac->rx_irq); 830e15472e8SRoger Quadros 831e15472e8SRoger Quadros return num_rx; 832e15472e8SRoger Quadros } 833e15472e8SRoger Quadros 834511f6c1aSRoger Quadros static int icssm_emac_set_boot_pru(struct prueth_emac *emac, 835511f6c1aSRoger Quadros struct net_device *ndev) 836511f6c1aSRoger Quadros { 837511f6c1aSRoger Quadros const struct prueth_firmware *pru_firmwares; 838511f6c1aSRoger Quadros struct prueth *prueth = emac->prueth; 839511f6c1aSRoger Quadros const char *fw_name; 840511f6c1aSRoger Quadros int ret; 841511f6c1aSRoger Quadros 842511f6c1aSRoger Quadros pru_firmwares = &prueth->fw_data->fw_pru[emac->port_id - 1]; 843511f6c1aSRoger Quadros fw_name = pru_firmwares->fw_name[prueth->eth_type]; 844511f6c1aSRoger Quadros if (!fw_name) { 845511f6c1aSRoger Quadros netdev_err(ndev, "eth_type %d not supported\n", 846511f6c1aSRoger Quadros prueth->eth_type); 847511f6c1aSRoger Quadros return -ENODEV; 848511f6c1aSRoger Quadros } 849511f6c1aSRoger Quadros 850511f6c1aSRoger Quadros ret = rproc_set_firmware(emac->pru, fw_name); 851511f6c1aSRoger Quadros if (ret) { 852511f6c1aSRoger Quadros netdev_err(ndev, "failed to set %s firmware: %d\n", 853511f6c1aSRoger Quadros fw_name, ret); 854511f6c1aSRoger Quadros return ret; 855511f6c1aSRoger Quadros } 856511f6c1aSRoger Quadros 857511f6c1aSRoger Quadros ret = rproc_boot(emac->pru); 858511f6c1aSRoger Quadros if (ret) { 859511f6c1aSRoger Quadros netdev_err(ndev, "failed to boot %s firmware: %d\n", 860511f6c1aSRoger Quadros fw_name, ret); 861511f6c1aSRoger Quadros return ret; 862511f6c1aSRoger Quadros } 863e15472e8SRoger Quadros return ret; 864e15472e8SRoger Quadros } 865e15472e8SRoger Quadros 866e15472e8SRoger Quadros static int icssm_emac_request_irqs(struct prueth_emac *emac) 867e15472e8SRoger Quadros { 868e15472e8SRoger Quadros struct net_device *ndev = emac->ndev; 869e15472e8SRoger Quadros int ret; 870e15472e8SRoger Quadros 871e15472e8SRoger Quadros ret = request_irq(emac->rx_irq, icssm_emac_rx_irq, 872e15472e8SRoger Quadros IRQF_TRIGGER_HIGH, 873e15472e8SRoger Quadros ndev->name, ndev); 874e15472e8SRoger Quadros if (ret) { 875e15472e8SRoger Quadros netdev_err(ndev, "unable to request RX IRQ\n"); 876e15472e8SRoger Quadros return ret; 877e15472e8SRoger Quadros } 878511f6c1aSRoger Quadros 879511f6c1aSRoger Quadros return ret; 880511f6c1aSRoger Quadros } 881511f6c1aSRoger Quadros 8821853367bSParvathi Pudi static void icssm_ptp_dram_init(struct prueth_emac *emac) 8831853367bSParvathi Pudi { 8841853367bSParvathi Pudi void __iomem *sram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; 8851853367bSParvathi Pudi u64 temp64; 8861853367bSParvathi Pudi 8871853367bSParvathi Pudi writew(0, sram + MII_RX_CORRECTION_OFFSET); 8881853367bSParvathi Pudi writew(0, sram + MII_TX_CORRECTION_OFFSET); 8891853367bSParvathi Pudi 8901853367bSParvathi Pudi /* Initialize RCF to 1 (Linux N/A) */ 8911853367bSParvathi Pudi writel(1 * 1024, sram + TIMESYNC_TC_RCF_OFFSET); 8921853367bSParvathi Pudi 8931853367bSParvathi Pudi /* This flag will be set and cleared by firmware */ 8941853367bSParvathi Pudi /* Write Sync0 period for sync signal generation in PTP 8951853367bSParvathi Pudi * memory in shared RAM 8961853367bSParvathi Pudi */ 8971853367bSParvathi Pudi writel(200000000 / 50, sram + TIMESYNC_SYNC0_WIDTH_OFFSET); 8981853367bSParvathi Pudi 8991853367bSParvathi Pudi /* Write CMP1 period for sync signal generation in PTP 9001853367bSParvathi Pudi * memory in shared RAM 9011853367bSParvathi Pudi */ 9021853367bSParvathi Pudi temp64 = 1000000; 9031853367bSParvathi Pudi memcpy_toio(sram + TIMESYNC_CMP1_CMP_OFFSET, &temp64, sizeof(temp64)); 9041853367bSParvathi Pudi 9051853367bSParvathi Pudi /* Write Sync0 period for sync signal generation in PTP 9061853367bSParvathi Pudi * memory in shared RAM 9071853367bSParvathi Pudi */ 9081853367bSParvathi Pudi writel(1000000, sram + TIMESYNC_CMP1_PERIOD_OFFSET); 9091853367bSParvathi Pudi 9101853367bSParvathi Pudi /* Configures domainNumber list. Firmware supports 2 domains */ 9111853367bSParvathi Pudi writeb(0, sram + TIMESYNC_DOMAIN_NUMBER_LIST); 9121853367bSParvathi Pudi writeb(0, sram + TIMESYNC_DOMAIN_NUMBER_LIST + 1); 9131853367bSParvathi Pudi 9141853367bSParvathi Pudi /* Configure 1-step/2-step */ 9151853367bSParvathi Pudi writeb(1, sram + DISABLE_SWITCH_SYNC_RELAY_OFFSET); 9161853367bSParvathi Pudi 9171853367bSParvathi Pudi /* Configures the setting to Link local frame without HSR tag */ 9181853367bSParvathi Pudi writeb(0, sram + LINK_LOCAL_FRAME_HAS_HSR_TAG); 9191853367bSParvathi Pudi 9201853367bSParvathi Pudi /* Enable E2E/UDP PTP message timestamping */ 9211853367bSParvathi Pudi writeb(1, sram + PTP_IPV4_UDP_E2E_ENABLE); 9221853367bSParvathi Pudi } 9231853367bSParvathi Pudi 924511f6c1aSRoger Quadros /** 925511f6c1aSRoger Quadros * icssm_emac_ndo_open - EMAC device open 926511f6c1aSRoger Quadros * @ndev: network adapter device 927511f6c1aSRoger Quadros * 928511f6c1aSRoger Quadros * Called when system wants to start the interface. 929511f6c1aSRoger Quadros * 930511f6c1aSRoger Quadros * Return: 0 for a successful open, or appropriate error code 931511f6c1aSRoger Quadros */ 932511f6c1aSRoger Quadros static int icssm_emac_ndo_open(struct net_device *ndev) 933511f6c1aSRoger Quadros { 934511f6c1aSRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 935a99b5657SRoger Quadros struct prueth *prueth = emac->prueth; 936511f6c1aSRoger Quadros int ret; 937511f6c1aSRoger Quadros 938a99b5657SRoger Quadros /* set h/w MAC as user might have re-configured */ 939a99b5657SRoger Quadros ether_addr_copy(emac->mac_addr, ndev->dev_addr); 940a99b5657SRoger Quadros 941a99b5657SRoger Quadros if (!prueth->emac_configured) 942a99b5657SRoger Quadros icssm_prueth_init_ethernet_mode(prueth); 943a99b5657SRoger Quadros 944a99b5657SRoger Quadros icssm_prueth_emac_config(emac); 945a99b5657SRoger Quadros 9461853367bSParvathi Pudi if (!prueth->emac_configured) { 9471853367bSParvathi Pudi icssm_ptp_dram_init(emac); 9481853367bSParvathi Pudi ret = icss_iep_init(prueth->iep, NULL, NULL, 0); 9491853367bSParvathi Pudi if (ret) { 9501853367bSParvathi Pudi netdev_err(ndev, "Failed to initialize iep: %d\n", ret); 9511853367bSParvathi Pudi goto iep_exit; 9521853367bSParvathi Pudi } 9531853367bSParvathi Pudi } 9541853367bSParvathi Pudi 955511f6c1aSRoger Quadros ret = icssm_emac_set_boot_pru(emac, ndev); 956511f6c1aSRoger Quadros if (ret) 9571853367bSParvathi Pudi goto iep_exit; 958511f6c1aSRoger Quadros 959e15472e8SRoger Quadros ret = icssm_emac_request_irqs(emac); 960e15472e8SRoger Quadros if (ret) 961e15472e8SRoger Quadros goto rproc_shutdown; 962e15472e8SRoger Quadros 963e15472e8SRoger Quadros napi_enable(&emac->napi); 964e15472e8SRoger Quadros 965511f6c1aSRoger Quadros /* start PHY */ 966511f6c1aSRoger Quadros phy_start(emac->phydev); 967e15472e8SRoger Quadros 968e15472e8SRoger Quadros /* enable the port and vlan */ 969e15472e8SRoger Quadros icssm_prueth_port_enable(emac, true); 970e15472e8SRoger Quadros 971a99b5657SRoger Quadros prueth->emac_configured |= BIT(emac->port_id); 972e15472e8SRoger Quadros 973e15472e8SRoger Quadros if (netif_msg_drv(emac)) 974e15472e8SRoger Quadros dev_notice(&ndev->dev, "started\n"); 975e15472e8SRoger Quadros 976511f6c1aSRoger Quadros return 0; 977e15472e8SRoger Quadros 978e15472e8SRoger Quadros rproc_shutdown: 979e15472e8SRoger Quadros rproc_shutdown(emac->pru); 980e15472e8SRoger Quadros 9811853367bSParvathi Pudi iep_exit: 9821853367bSParvathi Pudi if (!prueth->emac_configured) 9831853367bSParvathi Pudi icss_iep_exit(prueth->iep); 9841853367bSParvathi Pudi 985e15472e8SRoger Quadros return ret; 986511f6c1aSRoger Quadros } 987511f6c1aSRoger Quadros 988511f6c1aSRoger Quadros /** 989511f6c1aSRoger Quadros * icssm_emac_ndo_stop - EMAC device stop 990511f6c1aSRoger Quadros * @ndev: network adapter device 991511f6c1aSRoger Quadros * 992511f6c1aSRoger Quadros * Called when system wants to stop or down the interface. 993511f6c1aSRoger Quadros * 994511f6c1aSRoger Quadros * Return: Always 0 (Success) 995511f6c1aSRoger Quadros */ 996511f6c1aSRoger Quadros static int icssm_emac_ndo_stop(struct net_device *ndev) 997511f6c1aSRoger Quadros { 998511f6c1aSRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 999e15472e8SRoger Quadros struct prueth *prueth = emac->prueth; 1000e15472e8SRoger Quadros 1001e15472e8SRoger Quadros prueth->emac_configured &= ~BIT(emac->port_id); 1002e15472e8SRoger Quadros 1003e15472e8SRoger Quadros /* disable the mac port */ 1004e15472e8SRoger Quadros icssm_prueth_port_enable(emac, false); 1005511f6c1aSRoger Quadros 1006511f6c1aSRoger Quadros /* stop PHY */ 1007511f6c1aSRoger Quadros phy_stop(emac->phydev); 1008511f6c1aSRoger Quadros 1009e15472e8SRoger Quadros napi_disable(&emac->napi); 1010e15472e8SRoger Quadros hrtimer_cancel(&emac->tx_hrtimer); 1011e15472e8SRoger Quadros 1012e15472e8SRoger Quadros /* stop the PRU */ 1013511f6c1aSRoger Quadros rproc_shutdown(emac->pru); 1014511f6c1aSRoger Quadros 1015e15472e8SRoger Quadros /* free rx interrupts */ 1016e15472e8SRoger Quadros free_irq(emac->rx_irq, ndev); 1017e15472e8SRoger Quadros 1018e15472e8SRoger Quadros if (netif_msg_drv(emac)) 1019e15472e8SRoger Quadros dev_notice(&ndev->dev, "stopped\n"); 1020e15472e8SRoger Quadros 1021511f6c1aSRoger Quadros return 0; 1022511f6c1aSRoger Quadros } 1023511f6c1aSRoger Quadros 1024e15472e8SRoger Quadros /* VLAN-tag PCP to priority queue map for EMAC/Switch/HSR/PRP used by driver 1025e15472e8SRoger Quadros * Index is PCP val / 2. 1026e15472e8SRoger Quadros * low - pcp 0..3 maps to Q4 for Host 1027e15472e8SRoger Quadros * high - pcp 4..7 maps to Q3 for Host 1028e15472e8SRoger Quadros * low - pcp 0..3 maps to Q2 (FWD Queue) for PRU-x 1029e15472e8SRoger Quadros * where x = 1 for PRUETH_PORT_MII0 1030e15472e8SRoger Quadros * 0 for PRUETH_PORT_MII1 1031e15472e8SRoger Quadros * high - pcp 4..7 maps to Q1 (FWD Queue) for PRU-x 1032e15472e8SRoger Quadros */ 1033e15472e8SRoger Quadros static const unsigned short emac_pcp_tx_priority_queue_map[] = { 1034e15472e8SRoger Quadros PRUETH_QUEUE4, PRUETH_QUEUE4, 1035e15472e8SRoger Quadros PRUETH_QUEUE3, PRUETH_QUEUE3, 1036e15472e8SRoger Quadros PRUETH_QUEUE2, PRUETH_QUEUE2, 1037e15472e8SRoger Quadros PRUETH_QUEUE1, PRUETH_QUEUE1, 1038e15472e8SRoger Quadros }; 1039e15472e8SRoger Quadros 1040e15472e8SRoger Quadros static u16 icssm_prueth_get_tx_queue_id(struct prueth *prueth, 1041e15472e8SRoger Quadros struct sk_buff *skb) 1042e15472e8SRoger Quadros { 1043e15472e8SRoger Quadros u16 vlan_tci, pcp; 1044e15472e8SRoger Quadros int err; 1045e15472e8SRoger Quadros 1046e15472e8SRoger Quadros err = vlan_get_tag(skb, &vlan_tci); 1047e15472e8SRoger Quadros if (likely(err)) 1048e15472e8SRoger Quadros pcp = 0; 1049e15472e8SRoger Quadros else 1050e15472e8SRoger Quadros pcp = (vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; 1051e15472e8SRoger Quadros 1052e15472e8SRoger Quadros /* Below code (pcp >>= 1) is made common for all 1053e15472e8SRoger Quadros * protocols (i.e., EMAC, RSTP, HSR and PRP)* 1054e15472e8SRoger Quadros * pcp value 0,1 will be updated to 0 mapped to QUEUE4 1055e15472e8SRoger Quadros * pcp value 2,3 will be updated to 1 mapped to QUEUE4 1056e15472e8SRoger Quadros * pcp value 4,5 will be updated to 2 mapped to QUEUE3 1057e15472e8SRoger Quadros * pcp value 6,7 will be updated to 3 mapped to QUEUE3 1058e15472e8SRoger Quadros */ 1059e15472e8SRoger Quadros pcp >>= 1; 1060e15472e8SRoger Quadros 1061e15472e8SRoger Quadros return emac_pcp_tx_priority_queue_map[pcp]; 1062e15472e8SRoger Quadros } 1063e15472e8SRoger Quadros 1064e15472e8SRoger Quadros /** 1065e15472e8SRoger Quadros * icssm_emac_ndo_start_xmit - EMAC Transmit function 1066e15472e8SRoger Quadros * @skb: SKB pointer 1067e15472e8SRoger Quadros * @ndev: EMAC network adapter 1068e15472e8SRoger Quadros * 1069e15472e8SRoger Quadros * Called by the system to transmit a packet - we queue the packet in 1070e15472e8SRoger Quadros * EMAC hardware transmit queue 1071e15472e8SRoger Quadros * 1072e15472e8SRoger Quadros * Return: enum netdev_tx 1073e15472e8SRoger Quadros */ 1074e15472e8SRoger Quadros static enum netdev_tx icssm_emac_ndo_start_xmit(struct sk_buff *skb, 1075e15472e8SRoger Quadros struct net_device *ndev) 1076e15472e8SRoger Quadros { 1077e15472e8SRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 1078e15472e8SRoger Quadros int ret; 1079e15472e8SRoger Quadros u16 qid; 1080e15472e8SRoger Quadros 1081e15472e8SRoger Quadros qid = icssm_prueth_get_tx_queue_id(emac->prueth, skb); 1082e15472e8SRoger Quadros ret = icssm_prueth_tx_enqueue(emac, skb, qid); 1083e15472e8SRoger Quadros if (ret) { 1084e15472e8SRoger Quadros if (ret != -ENOBUFS && netif_msg_tx_err(emac) && 1085e15472e8SRoger Quadros net_ratelimit()) 1086e15472e8SRoger Quadros netdev_err(ndev, "packet queue failed: %d\n", ret); 1087e15472e8SRoger Quadros goto fail_tx; 1088e15472e8SRoger Quadros } 1089e15472e8SRoger Quadros 1090e15472e8SRoger Quadros emac->stats.tx_packets++; 1091e15472e8SRoger Quadros emac->stats.tx_bytes += skb->len; 1092e15472e8SRoger Quadros dev_kfree_skb_any(skb); 1093e15472e8SRoger Quadros 1094e15472e8SRoger Quadros return NETDEV_TX_OK; 1095e15472e8SRoger Quadros 1096e15472e8SRoger Quadros fail_tx: 1097e15472e8SRoger Quadros if (ret == -ENOBUFS) { 1098e15472e8SRoger Quadros netif_stop_queue(ndev); 1099e15472e8SRoger Quadros hrtimer_start(&emac->tx_hrtimer, 1100e15472e8SRoger Quadros us_to_ktime(HR_TIMER_TX_DELAY_US), 1101e15472e8SRoger Quadros HRTIMER_MODE_REL_PINNED); 1102e15472e8SRoger Quadros ret = NETDEV_TX_BUSY; 1103e15472e8SRoger Quadros } else { 1104e15472e8SRoger Quadros /* error */ 1105e15472e8SRoger Quadros emac->stats.tx_dropped++; 1106e15472e8SRoger Quadros ret = NET_XMIT_DROP; 1107e15472e8SRoger Quadros } 1108e15472e8SRoger Quadros 1109e15472e8SRoger Quadros return ret; 1110e15472e8SRoger Quadros } 1111e15472e8SRoger Quadros 1112e15472e8SRoger Quadros /** 1113e15472e8SRoger Quadros * icssm_emac_ndo_get_stats64 - EMAC get statistics function 1114e15472e8SRoger Quadros * @ndev: The EMAC network adapter 1115e15472e8SRoger Quadros * @stats: rtnl_link_stats structure 1116e15472e8SRoger Quadros * 1117e15472e8SRoger Quadros * Called when system wants to get statistics from the device. 1118e15472e8SRoger Quadros * 1119e15472e8SRoger Quadros */ 1120e15472e8SRoger Quadros static void icssm_emac_ndo_get_stats64(struct net_device *ndev, 1121e15472e8SRoger Quadros struct rtnl_link_stats64 *stats) 1122e15472e8SRoger Quadros { 1123e15472e8SRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 1124e15472e8SRoger Quadros 1125e15472e8SRoger Quadros stats->rx_packets = emac->stats.rx_packets; 1126e15472e8SRoger Quadros stats->rx_bytes = emac->stats.rx_bytes; 1127e15472e8SRoger Quadros stats->tx_packets = emac->stats.tx_packets; 1128e15472e8SRoger Quadros stats->tx_bytes = emac->stats.tx_bytes; 1129e15472e8SRoger Quadros stats->tx_dropped = emac->stats.tx_dropped; 1130e15472e8SRoger Quadros stats->rx_over_errors = emac->stats.rx_over_errors; 1131e15472e8SRoger Quadros stats->rx_length_errors = emac->stats.rx_length_errors; 1132e15472e8SRoger Quadros } 1133e15472e8SRoger Quadros 1134511f6c1aSRoger Quadros static const struct net_device_ops emac_netdev_ops = { 1135511f6c1aSRoger Quadros .ndo_open = icssm_emac_ndo_open, 1136511f6c1aSRoger Quadros .ndo_stop = icssm_emac_ndo_stop, 1137e15472e8SRoger Quadros .ndo_start_xmit = icssm_emac_ndo_start_xmit, 1138e15472e8SRoger Quadros .ndo_get_stats64 = icssm_emac_ndo_get_stats64, 1139511f6c1aSRoger Quadros }; 1140511f6c1aSRoger Quadros 1141511f6c1aSRoger Quadros /* get emac_port corresponding to eth_node name */ 1142511f6c1aSRoger Quadros static int icssm_prueth_node_port(struct device_node *eth_node) 1143511f6c1aSRoger Quadros { 1144511f6c1aSRoger Quadros u32 port_id; 1145511f6c1aSRoger Quadros int ret; 1146511f6c1aSRoger Quadros 1147511f6c1aSRoger Quadros ret = of_property_read_u32(eth_node, "reg", &port_id); 1148511f6c1aSRoger Quadros if (ret) 1149511f6c1aSRoger Quadros return ret; 1150511f6c1aSRoger Quadros 1151511f6c1aSRoger Quadros if (port_id == 0) 1152511f6c1aSRoger Quadros return PRUETH_PORT_MII0; 1153511f6c1aSRoger Quadros else if (port_id == 1) 1154511f6c1aSRoger Quadros return PRUETH_PORT_MII1; 1155511f6c1aSRoger Quadros else 1156511f6c1aSRoger Quadros return PRUETH_PORT_INVALID; 1157511f6c1aSRoger Quadros } 1158511f6c1aSRoger Quadros 1159511f6c1aSRoger Quadros /* get MAC instance corresponding to eth_node name */ 1160511f6c1aSRoger Quadros static int icssm_prueth_node_mac(struct device_node *eth_node) 1161511f6c1aSRoger Quadros { 1162511f6c1aSRoger Quadros u32 port_id; 1163511f6c1aSRoger Quadros int ret; 1164511f6c1aSRoger Quadros 1165511f6c1aSRoger Quadros ret = of_property_read_u32(eth_node, "reg", &port_id); 1166511f6c1aSRoger Quadros if (ret) 1167511f6c1aSRoger Quadros return ret; 1168511f6c1aSRoger Quadros 1169511f6c1aSRoger Quadros if (port_id == 0) 1170511f6c1aSRoger Quadros return PRUETH_MAC0; 1171511f6c1aSRoger Quadros else if (port_id == 1) 1172511f6c1aSRoger Quadros return PRUETH_MAC1; 1173511f6c1aSRoger Quadros else 1174511f6c1aSRoger Quadros return PRUETH_MAC_INVALID; 1175511f6c1aSRoger Quadros } 1176511f6c1aSRoger Quadros 1177e15472e8SRoger Quadros static enum hrtimer_restart icssm_emac_tx_timer_callback(struct hrtimer *timer) 1178e15472e8SRoger Quadros { 1179e15472e8SRoger Quadros struct prueth_emac *emac = 1180e15472e8SRoger Quadros container_of(timer, struct prueth_emac, tx_hrtimer); 1181e15472e8SRoger Quadros 1182e15472e8SRoger Quadros if (netif_queue_stopped(emac->ndev)) 1183e15472e8SRoger Quadros netif_wake_queue(emac->ndev); 1184e15472e8SRoger Quadros 1185e15472e8SRoger Quadros return HRTIMER_NORESTART; 1186e15472e8SRoger Quadros } 1187e15472e8SRoger Quadros 1188511f6c1aSRoger Quadros static int icssm_prueth_netdev_init(struct prueth *prueth, 1189511f6c1aSRoger Quadros struct device_node *eth_node) 1190511f6c1aSRoger Quadros { 1191511f6c1aSRoger Quadros struct prueth_emac *emac; 1192511f6c1aSRoger Quadros struct net_device *ndev; 1193511f6c1aSRoger Quadros enum prueth_port port; 1194511f6c1aSRoger Quadros enum prueth_mac mac; 1195511f6c1aSRoger Quadros int ret; 1196511f6c1aSRoger Quadros 1197511f6c1aSRoger Quadros port = icssm_prueth_node_port(eth_node); 1198511f6c1aSRoger Quadros if (port == PRUETH_PORT_INVALID) 1199511f6c1aSRoger Quadros return -EINVAL; 1200511f6c1aSRoger Quadros 1201511f6c1aSRoger Quadros mac = icssm_prueth_node_mac(eth_node); 1202511f6c1aSRoger Quadros if (mac == PRUETH_MAC_INVALID) 1203511f6c1aSRoger Quadros return -EINVAL; 1204511f6c1aSRoger Quadros 1205511f6c1aSRoger Quadros ndev = devm_alloc_etherdev(prueth->dev, sizeof(*emac)); 1206511f6c1aSRoger Quadros if (!ndev) 1207511f6c1aSRoger Quadros return -ENOMEM; 1208511f6c1aSRoger Quadros 1209511f6c1aSRoger Quadros SET_NETDEV_DEV(ndev, prueth->dev); 1210511f6c1aSRoger Quadros emac = netdev_priv(ndev); 1211511f6c1aSRoger Quadros prueth->emac[mac] = emac; 1212511f6c1aSRoger Quadros emac->prueth = prueth; 1213511f6c1aSRoger Quadros emac->ndev = ndev; 1214511f6c1aSRoger Quadros emac->port_id = port; 1215511f6c1aSRoger Quadros 1216511f6c1aSRoger Quadros /* by default eth_type is EMAC */ 1217511f6c1aSRoger Quadros switch (port) { 1218511f6c1aSRoger Quadros case PRUETH_PORT_MII0: 1219e15472e8SRoger Quadros emac->tx_port_queue = PRUETH_PORT_QUEUE_MII0; 1220e15472e8SRoger Quadros 1221e15472e8SRoger Quadros /* packets from MII0 are on queues 1 through 2 */ 1222e15472e8SRoger Quadros emac->rx_queue_start = PRUETH_QUEUE1; 1223e15472e8SRoger Quadros emac->rx_queue_end = PRUETH_QUEUE2; 1224e15472e8SRoger Quadros 1225a99b5657SRoger Quadros emac->dram = PRUETH_MEM_DRAM0; 1226511f6c1aSRoger Quadros emac->pru = prueth->pru0; 1227511f6c1aSRoger Quadros break; 1228511f6c1aSRoger Quadros case PRUETH_PORT_MII1: 1229e15472e8SRoger Quadros emac->tx_port_queue = PRUETH_PORT_QUEUE_MII1; 1230e15472e8SRoger Quadros 1231e15472e8SRoger Quadros /* packets from MII1 are on queues 3 through 4 */ 1232e15472e8SRoger Quadros emac->rx_queue_start = PRUETH_QUEUE3; 1233e15472e8SRoger Quadros emac->rx_queue_end = PRUETH_QUEUE4; 1234e15472e8SRoger Quadros 1235a99b5657SRoger Quadros emac->dram = PRUETH_MEM_DRAM1; 1236511f6c1aSRoger Quadros emac->pru = prueth->pru1; 1237511f6c1aSRoger Quadros break; 1238511f6c1aSRoger Quadros default: 1239511f6c1aSRoger Quadros return -EINVAL; 1240511f6c1aSRoger Quadros } 1241e15472e8SRoger Quadros 1242e15472e8SRoger Quadros emac->rx_irq = of_irq_get_byname(eth_node, "rx"); 1243e15472e8SRoger Quadros if (emac->rx_irq < 0) { 1244e15472e8SRoger Quadros ret = emac->rx_irq; 1245e15472e8SRoger Quadros if (ret != -EPROBE_DEFER) 1246e15472e8SRoger Quadros dev_err(prueth->dev, "could not get rx irq\n"); 1247e15472e8SRoger Quadros goto free; 1248e15472e8SRoger Quadros } 1249e15472e8SRoger Quadros 1250511f6c1aSRoger Quadros /* get mac address from DT and set private and netdev addr */ 1251511f6c1aSRoger Quadros ret = of_get_ethdev_address(eth_node, ndev); 1252511f6c1aSRoger Quadros if (!is_valid_ether_addr(ndev->dev_addr)) { 1253511f6c1aSRoger Quadros eth_hw_addr_random(ndev); 1254511f6c1aSRoger Quadros dev_warn(prueth->dev, "port %d: using random MAC addr: %pM\n", 1255511f6c1aSRoger Quadros port, ndev->dev_addr); 1256511f6c1aSRoger Quadros } 1257511f6c1aSRoger Quadros ether_addr_copy(emac->mac_addr, ndev->dev_addr); 1258511f6c1aSRoger Quadros 1259511f6c1aSRoger Quadros /* connect PHY */ 1260511f6c1aSRoger Quadros emac->phydev = of_phy_get_and_connect(ndev, eth_node, 1261511f6c1aSRoger Quadros icssm_emac_adjust_link); 1262511f6c1aSRoger Quadros if (!emac->phydev) { 1263511f6c1aSRoger Quadros dev_dbg(prueth->dev, "PHY connection failed\n"); 1264511f6c1aSRoger Quadros ret = -ENODEV; 1265511f6c1aSRoger Quadros goto free; 1266511f6c1aSRoger Quadros } 1267511f6c1aSRoger Quadros 1268511f6c1aSRoger Quadros /* remove unsupported modes */ 1269511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Full_BIT); 1270511f6c1aSRoger Quadros 1271511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT); 1272511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT); 1273511f6c1aSRoger Quadros 1274511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Pause_BIT); 1275511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT); 1276511f6c1aSRoger Quadros 1277511f6c1aSRoger Quadros ndev->dev.of_node = eth_node; 1278511f6c1aSRoger Quadros ndev->netdev_ops = &emac_netdev_ops; 1279511f6c1aSRoger Quadros 1280e15472e8SRoger Quadros netif_napi_add(ndev, &emac->napi, icssm_emac_napi_poll); 1281e15472e8SRoger Quadros 1282e15472e8SRoger Quadros hrtimer_setup(&emac->tx_hrtimer, &icssm_emac_tx_timer_callback, 1283e15472e8SRoger Quadros CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); 1284e15472e8SRoger Quadros 1285511f6c1aSRoger Quadros return 0; 1286511f6c1aSRoger Quadros free: 1287511f6c1aSRoger Quadros emac->ndev = NULL; 1288511f6c1aSRoger Quadros prueth->emac[mac] = NULL; 1289511f6c1aSRoger Quadros 1290511f6c1aSRoger Quadros return ret; 1291511f6c1aSRoger Quadros } 1292511f6c1aSRoger Quadros 1293511f6c1aSRoger Quadros static void icssm_prueth_netdev_exit(struct prueth *prueth, 1294511f6c1aSRoger Quadros struct device_node *eth_node) 1295511f6c1aSRoger Quadros { 1296511f6c1aSRoger Quadros struct prueth_emac *emac; 1297511f6c1aSRoger Quadros enum prueth_mac mac; 1298511f6c1aSRoger Quadros 1299511f6c1aSRoger Quadros mac = icssm_prueth_node_mac(eth_node); 1300511f6c1aSRoger Quadros if (mac == PRUETH_MAC_INVALID) 1301511f6c1aSRoger Quadros return; 1302511f6c1aSRoger Quadros 1303511f6c1aSRoger Quadros emac = prueth->emac[mac]; 1304511f6c1aSRoger Quadros if (!emac) 1305511f6c1aSRoger Quadros return; 1306511f6c1aSRoger Quadros 1307511f6c1aSRoger Quadros phy_disconnect(emac->phydev); 1308511f6c1aSRoger Quadros 1309e15472e8SRoger Quadros netif_napi_del(&emac->napi); 1310511f6c1aSRoger Quadros prueth->emac[mac] = NULL; 1311511f6c1aSRoger Quadros } 1312511f6c1aSRoger Quadros 1313511f6c1aSRoger Quadros static int icssm_prueth_probe(struct platform_device *pdev) 1314511f6c1aSRoger Quadros { 1315511f6c1aSRoger Quadros struct device_node *eth0_node = NULL, *eth1_node = NULL; 1316511f6c1aSRoger Quadros struct device_node *eth_node, *eth_ports_node; 1317511f6c1aSRoger Quadros enum pruss_pru_id pruss_id0, pruss_id1; 1318511f6c1aSRoger Quadros struct device *dev = &pdev->dev; 1319511f6c1aSRoger Quadros struct device_node *np; 1320511f6c1aSRoger Quadros struct prueth *prueth; 1321a99b5657SRoger Quadros struct pruss *pruss; 1322511f6c1aSRoger Quadros int i, ret; 1323511f6c1aSRoger Quadros 1324511f6c1aSRoger Quadros np = dev->of_node; 1325511f6c1aSRoger Quadros if (!np) 1326511f6c1aSRoger Quadros return -ENODEV; /* we don't support non DT */ 1327511f6c1aSRoger Quadros 1328511f6c1aSRoger Quadros prueth = devm_kzalloc(dev, sizeof(*prueth), GFP_KERNEL); 1329511f6c1aSRoger Quadros if (!prueth) 1330511f6c1aSRoger Quadros return -ENOMEM; 1331511f6c1aSRoger Quadros 1332511f6c1aSRoger Quadros platform_set_drvdata(pdev, prueth); 1333511f6c1aSRoger Quadros prueth->dev = dev; 1334511f6c1aSRoger Quadros prueth->fw_data = device_get_match_data(dev); 1335511f6c1aSRoger Quadros 1336511f6c1aSRoger Quadros eth_ports_node = of_get_child_by_name(np, "ethernet-ports"); 1337511f6c1aSRoger Quadros if (!eth_ports_node) 1338511f6c1aSRoger Quadros return -ENOENT; 1339511f6c1aSRoger Quadros 1340511f6c1aSRoger Quadros for_each_child_of_node(eth_ports_node, eth_node) { 1341511f6c1aSRoger Quadros u32 reg; 1342511f6c1aSRoger Quadros 1343511f6c1aSRoger Quadros if (strcmp(eth_node->name, "ethernet-port")) 1344511f6c1aSRoger Quadros continue; 1345511f6c1aSRoger Quadros ret = of_property_read_u32(eth_node, "reg", ®); 1346511f6c1aSRoger Quadros if (ret < 0) { 1347511f6c1aSRoger Quadros dev_err(dev, "%pOF error reading port_id %d\n", 1348511f6c1aSRoger Quadros eth_node, ret); 1349511f6c1aSRoger Quadros of_node_put(eth_node); 1350511f6c1aSRoger Quadros return ret; 1351511f6c1aSRoger Quadros } 1352511f6c1aSRoger Quadros 1353511f6c1aSRoger Quadros of_node_get(eth_node); 1354511f6c1aSRoger Quadros 1355511f6c1aSRoger Quadros if (reg == 0 && !eth0_node) { 1356511f6c1aSRoger Quadros eth0_node = eth_node; 1357511f6c1aSRoger Quadros if (!of_device_is_available(eth0_node)) { 1358511f6c1aSRoger Quadros of_node_put(eth0_node); 1359511f6c1aSRoger Quadros eth0_node = NULL; 1360511f6c1aSRoger Quadros } 1361511f6c1aSRoger Quadros } else if (reg == 1 && !eth1_node) { 1362511f6c1aSRoger Quadros eth1_node = eth_node; 1363511f6c1aSRoger Quadros if (!of_device_is_available(eth1_node)) { 1364511f6c1aSRoger Quadros of_node_put(eth1_node); 1365511f6c1aSRoger Quadros eth1_node = NULL; 1366511f6c1aSRoger Quadros } 1367511f6c1aSRoger Quadros } else { 1368511f6c1aSRoger Quadros if (reg == 0 || reg == 1) 1369511f6c1aSRoger Quadros dev_err(dev, "duplicate port reg value: %d\n", 1370511f6c1aSRoger Quadros reg); 1371511f6c1aSRoger Quadros else 1372511f6c1aSRoger Quadros dev_err(dev, "invalid port reg value: %d\n", 1373511f6c1aSRoger Quadros reg); 1374511f6c1aSRoger Quadros 1375511f6c1aSRoger Quadros of_node_put(eth_node); 1376511f6c1aSRoger Quadros } 1377511f6c1aSRoger Quadros } 1378511f6c1aSRoger Quadros 1379511f6c1aSRoger Quadros of_node_put(eth_ports_node); 1380511f6c1aSRoger Quadros 1381511f6c1aSRoger Quadros /* At least one node must be present and available else we fail */ 1382511f6c1aSRoger Quadros if (!eth0_node && !eth1_node) { 1383511f6c1aSRoger Quadros dev_err(dev, "neither port0 nor port1 node available\n"); 1384511f6c1aSRoger Quadros return -ENODEV; 1385511f6c1aSRoger Quadros } 1386511f6c1aSRoger Quadros 1387511f6c1aSRoger Quadros prueth->eth_node[PRUETH_MAC0] = eth0_node; 1388511f6c1aSRoger Quadros prueth->eth_node[PRUETH_MAC1] = eth1_node; 1389511f6c1aSRoger Quadros 1390a99b5657SRoger Quadros prueth->mii_rt = syscon_regmap_lookup_by_phandle(np, "ti,mii-rt"); 1391a99b5657SRoger Quadros if (IS_ERR(prueth->mii_rt)) { 1392a99b5657SRoger Quadros dev_err(dev, "couldn't get mii-rt syscon regmap\n"); 1393*5fc7fa74SDan Carpenter ret = PTR_ERR(prueth->mii_rt); 1394*5fc7fa74SDan Carpenter goto put_eth; 1395a99b5657SRoger Quadros } 1396a99b5657SRoger Quadros 1397511f6c1aSRoger Quadros if (eth0_node) { 1398511f6c1aSRoger Quadros prueth->pru0 = pru_rproc_get(np, 0, &pruss_id0); 1399511f6c1aSRoger Quadros if (IS_ERR(prueth->pru0)) { 1400511f6c1aSRoger Quadros ret = PTR_ERR(prueth->pru0); 1401511f6c1aSRoger Quadros dev_err_probe(dev, ret, "unable to get PRU0"); 1402*5fc7fa74SDan Carpenter goto put_eth; 1403511f6c1aSRoger Quadros } 1404511f6c1aSRoger Quadros } 1405511f6c1aSRoger Quadros 1406511f6c1aSRoger Quadros if (eth1_node) { 1407511f6c1aSRoger Quadros prueth->pru1 = pru_rproc_get(np, 1, &pruss_id1); 1408511f6c1aSRoger Quadros if (IS_ERR(prueth->pru1)) { 1409511f6c1aSRoger Quadros ret = PTR_ERR(prueth->pru1); 1410511f6c1aSRoger Quadros dev_err_probe(dev, ret, "unable to get PRU1"); 1411*5fc7fa74SDan Carpenter goto put_pru0; 1412511f6c1aSRoger Quadros } 1413511f6c1aSRoger Quadros } 1414511f6c1aSRoger Quadros 1415a99b5657SRoger Quadros pruss = pruss_get(prueth->pru0 ? prueth->pru0 : prueth->pru1); 1416a99b5657SRoger Quadros if (IS_ERR(pruss)) { 1417a99b5657SRoger Quadros ret = PTR_ERR(pruss); 1418a99b5657SRoger Quadros dev_err(dev, "unable to get pruss handle\n"); 1419*5fc7fa74SDan Carpenter goto put_pru1; 1420a99b5657SRoger Quadros } 1421a99b5657SRoger Quadros prueth->pruss = pruss; 1422a99b5657SRoger Quadros 1423a99b5657SRoger Quadros /* Configure PRUSS */ 1424a99b5657SRoger Quadros if (eth0_node) 1425a99b5657SRoger Quadros pruss_cfg_gpimode(pruss, pruss_id0, PRUSS_GPI_MODE_MII); 1426a99b5657SRoger Quadros if (eth1_node) 1427a99b5657SRoger Quadros pruss_cfg_gpimode(pruss, pruss_id1, PRUSS_GPI_MODE_MII); 1428a99b5657SRoger Quadros pruss_cfg_miirt_enable(pruss, true); 1429a99b5657SRoger Quadros pruss_cfg_xfr_enable(pruss, PRU_TYPE_PRU, true); 1430a99b5657SRoger Quadros 1431a99b5657SRoger Quadros /* Get PRUSS mem resources */ 1432a99b5657SRoger Quadros /* OCMC is system resource which we get separately */ 1433a99b5657SRoger Quadros for (i = 0; i < ARRAY_SIZE(pruss_mem_ids); i++) { 1434a99b5657SRoger Quadros /* skip appropriate DRAM if not required */ 1435a99b5657SRoger Quadros if (!eth0_node && i == PRUETH_MEM_DRAM0) 1436a99b5657SRoger Quadros continue; 1437a99b5657SRoger Quadros 1438a99b5657SRoger Quadros if (!eth1_node && i == PRUETH_MEM_DRAM1) 1439a99b5657SRoger Quadros continue; 1440a99b5657SRoger Quadros 1441a99b5657SRoger Quadros ret = pruss_request_mem_region(pruss, pruss_mem_ids[i], 1442a99b5657SRoger Quadros &prueth->mem[i]); 1443a99b5657SRoger Quadros if (ret) { 1444a99b5657SRoger Quadros dev_err(dev, "unable to get PRUSS resource %d: %d\n", 1445a99b5657SRoger Quadros i, ret); 1446a99b5657SRoger Quadros goto put_mem; 1447a99b5657SRoger Quadros } 1448a99b5657SRoger Quadros } 1449a99b5657SRoger Quadros 1450a99b5657SRoger Quadros prueth->sram_pool = of_gen_pool_get(np, "sram", 0); 1451a99b5657SRoger Quadros if (!prueth->sram_pool) { 1452a99b5657SRoger Quadros dev_err(dev, "unable to get SRAM pool\n"); 1453a99b5657SRoger Quadros ret = -ENODEV; 1454a99b5657SRoger Quadros goto put_mem; 1455a99b5657SRoger Quadros } 1456a99b5657SRoger Quadros 1457a99b5657SRoger Quadros prueth->ocmc_ram_size = OCMC_RAM_SIZE; 1458a99b5657SRoger Quadros /* Decreased by 8KB to address the reserved region for AM33x */ 1459a99b5657SRoger Quadros if (prueth->fw_data->driver_data == PRUSS_AM33XX) 1460a99b5657SRoger Quadros prueth->ocmc_ram_size = (SZ_64K - SZ_8K); 1461a99b5657SRoger Quadros 1462a99b5657SRoger Quadros prueth->mem[PRUETH_MEM_OCMC].va = 1463a99b5657SRoger Quadros (void __iomem *)gen_pool_alloc(prueth->sram_pool, 1464a99b5657SRoger Quadros prueth->ocmc_ram_size); 1465a99b5657SRoger Quadros if (!prueth->mem[PRUETH_MEM_OCMC].va) { 1466a99b5657SRoger Quadros dev_err(dev, "unable to allocate OCMC resource\n"); 1467a99b5657SRoger Quadros ret = -ENOMEM; 1468a99b5657SRoger Quadros goto put_mem; 1469a99b5657SRoger Quadros } 1470a99b5657SRoger Quadros prueth->mem[PRUETH_MEM_OCMC].pa = gen_pool_virt_to_phys 1471a99b5657SRoger Quadros (prueth->sram_pool, (unsigned long) 1472a99b5657SRoger Quadros prueth->mem[PRUETH_MEM_OCMC].va); 1473a99b5657SRoger Quadros prueth->mem[PRUETH_MEM_OCMC].size = prueth->ocmc_ram_size; 1474a99b5657SRoger Quadros dev_dbg(dev, "ocmc: pa %pa va %p size %#zx\n", 1475a99b5657SRoger Quadros &prueth->mem[PRUETH_MEM_OCMC].pa, 1476a99b5657SRoger Quadros prueth->mem[PRUETH_MEM_OCMC].va, 1477a99b5657SRoger Quadros prueth->mem[PRUETH_MEM_OCMC].size); 1478a99b5657SRoger Quadros 1479511f6c1aSRoger Quadros /* setup netdev interfaces */ 1480511f6c1aSRoger Quadros if (eth0_node) { 1481511f6c1aSRoger Quadros ret = icssm_prueth_netdev_init(prueth, eth0_node); 1482511f6c1aSRoger Quadros if (ret) { 1483511f6c1aSRoger Quadros if (ret != -EPROBE_DEFER) { 1484511f6c1aSRoger Quadros dev_err(dev, "netdev init %s failed: %d\n", 1485511f6c1aSRoger Quadros eth0_node->name, ret); 1486511f6c1aSRoger Quadros } 1487a99b5657SRoger Quadros goto free_pool; 1488511f6c1aSRoger Quadros } 1489511f6c1aSRoger Quadros } 1490511f6c1aSRoger Quadros 1491511f6c1aSRoger Quadros if (eth1_node) { 1492511f6c1aSRoger Quadros ret = icssm_prueth_netdev_init(prueth, eth1_node); 1493511f6c1aSRoger Quadros if (ret) { 1494511f6c1aSRoger Quadros if (ret != -EPROBE_DEFER) { 1495511f6c1aSRoger Quadros dev_err(dev, "netdev init %s failed: %d\n", 1496511f6c1aSRoger Quadros eth1_node->name, ret); 1497511f6c1aSRoger Quadros } 1498511f6c1aSRoger Quadros goto netdev_exit; 1499511f6c1aSRoger Quadros } 1500511f6c1aSRoger Quadros } 1501511f6c1aSRoger Quadros 15021853367bSParvathi Pudi prueth->iep = icss_iep_get(np); 15031853367bSParvathi Pudi if (IS_ERR(prueth->iep)) { 15041853367bSParvathi Pudi ret = PTR_ERR(prueth->iep); 15051853367bSParvathi Pudi dev_err(dev, "unable to get IEP\n"); 15061853367bSParvathi Pudi goto netdev_exit; 15071853367bSParvathi Pudi } 15081853367bSParvathi Pudi 1509511f6c1aSRoger Quadros /* register the network devices */ 1510511f6c1aSRoger Quadros if (eth0_node) { 1511511f6c1aSRoger Quadros ret = register_netdev(prueth->emac[PRUETH_MAC0]->ndev); 1512511f6c1aSRoger Quadros if (ret) { 1513511f6c1aSRoger Quadros dev_err(dev, "can't register netdev for port MII0"); 15141853367bSParvathi Pudi goto iep_put; 1515511f6c1aSRoger Quadros } 1516511f6c1aSRoger Quadros 1517511f6c1aSRoger Quadros prueth->registered_netdevs[PRUETH_MAC0] = 1518511f6c1aSRoger Quadros prueth->emac[PRUETH_MAC0]->ndev; 1519511f6c1aSRoger Quadros } 1520511f6c1aSRoger Quadros 1521511f6c1aSRoger Quadros if (eth1_node) { 1522511f6c1aSRoger Quadros ret = register_netdev(prueth->emac[PRUETH_MAC1]->ndev); 1523511f6c1aSRoger Quadros if (ret) { 1524511f6c1aSRoger Quadros dev_err(dev, "can't register netdev for port MII1"); 1525511f6c1aSRoger Quadros goto netdev_unregister; 1526511f6c1aSRoger Quadros } 1527511f6c1aSRoger Quadros 1528511f6c1aSRoger Quadros prueth->registered_netdevs[PRUETH_MAC1] = 1529511f6c1aSRoger Quadros prueth->emac[PRUETH_MAC1]->ndev; 1530511f6c1aSRoger Quadros } 1531511f6c1aSRoger Quadros 1532a99b5657SRoger Quadros dev_info(dev, "TI PRU ethernet driver initialized: %s EMAC mode\n", 1533a99b5657SRoger Quadros (!eth0_node || !eth1_node) ? "single" : "dual"); 1534a99b5657SRoger Quadros 1535511f6c1aSRoger Quadros if (eth1_node) 1536511f6c1aSRoger Quadros of_node_put(eth1_node); 1537511f6c1aSRoger Quadros if (eth0_node) 1538511f6c1aSRoger Quadros of_node_put(eth0_node); 1539511f6c1aSRoger Quadros return 0; 1540511f6c1aSRoger Quadros 1541511f6c1aSRoger Quadros netdev_unregister: 1542511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 1543511f6c1aSRoger Quadros if (!prueth->registered_netdevs[i]) 1544511f6c1aSRoger Quadros continue; 1545511f6c1aSRoger Quadros unregister_netdev(prueth->registered_netdevs[i]); 1546511f6c1aSRoger Quadros } 1547511f6c1aSRoger Quadros 15481853367bSParvathi Pudi iep_put: 15491853367bSParvathi Pudi icss_iep_put(prueth->iep); 15501853367bSParvathi Pudi prueth->iep = NULL; 15511853367bSParvathi Pudi 1552511f6c1aSRoger Quadros netdev_exit: 1553511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 1554511f6c1aSRoger Quadros eth_node = prueth->eth_node[i]; 1555511f6c1aSRoger Quadros if (!eth_node) 1556511f6c1aSRoger Quadros continue; 1557511f6c1aSRoger Quadros 1558511f6c1aSRoger Quadros icssm_prueth_netdev_exit(prueth, eth_node); 1559511f6c1aSRoger Quadros } 1560511f6c1aSRoger Quadros 1561a99b5657SRoger Quadros free_pool: 1562a99b5657SRoger Quadros gen_pool_free(prueth->sram_pool, 1563a99b5657SRoger Quadros (unsigned long)prueth->mem[PRUETH_MEM_OCMC].va, 1564a99b5657SRoger Quadros prueth->ocmc_ram_size); 1565a99b5657SRoger Quadros 1566a99b5657SRoger Quadros put_mem: 1567a99b5657SRoger Quadros for (i = PRUETH_MEM_DRAM0; i < PRUETH_MEM_OCMC; i++) { 1568a99b5657SRoger Quadros if (prueth->mem[i].va) 1569a99b5657SRoger Quadros pruss_release_mem_region(pruss, &prueth->mem[i]); 1570a99b5657SRoger Quadros } 1571a99b5657SRoger Quadros pruss_put(prueth->pruss); 1572a99b5657SRoger Quadros 1573*5fc7fa74SDan Carpenter put_pru1: 1574*5fc7fa74SDan Carpenter if (eth1_node) 1575511f6c1aSRoger Quadros pru_rproc_put(prueth->pru1); 1576*5fc7fa74SDan Carpenter put_pru0: 1577*5fc7fa74SDan Carpenter if (eth0_node) 1578511f6c1aSRoger Quadros pru_rproc_put(prueth->pru0); 1579*5fc7fa74SDan Carpenter put_eth: 1580*5fc7fa74SDan Carpenter of_node_put(eth1_node); 1581511f6c1aSRoger Quadros of_node_put(eth0_node); 1582511f6c1aSRoger Quadros 1583511f6c1aSRoger Quadros return ret; 1584511f6c1aSRoger Quadros } 1585511f6c1aSRoger Quadros 1586511f6c1aSRoger Quadros static void icssm_prueth_remove(struct platform_device *pdev) 1587511f6c1aSRoger Quadros { 1588511f6c1aSRoger Quadros struct prueth *prueth = platform_get_drvdata(pdev); 1589511f6c1aSRoger Quadros struct device_node *eth_node; 1590511f6c1aSRoger Quadros int i; 1591511f6c1aSRoger Quadros 1592511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 1593511f6c1aSRoger Quadros if (!prueth->registered_netdevs[i]) 1594511f6c1aSRoger Quadros continue; 1595511f6c1aSRoger Quadros unregister_netdev(prueth->registered_netdevs[i]); 1596511f6c1aSRoger Quadros } 1597511f6c1aSRoger Quadros 1598511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 1599511f6c1aSRoger Quadros eth_node = prueth->eth_node[i]; 1600511f6c1aSRoger Quadros if (!eth_node) 1601511f6c1aSRoger Quadros continue; 1602511f6c1aSRoger Quadros 1603511f6c1aSRoger Quadros icssm_prueth_netdev_exit(prueth, eth_node); 1604511f6c1aSRoger Quadros of_node_put(eth_node); 1605511f6c1aSRoger Quadros } 1606511f6c1aSRoger Quadros 1607a99b5657SRoger Quadros gen_pool_free(prueth->sram_pool, 1608a99b5657SRoger Quadros (unsigned long)prueth->mem[PRUETH_MEM_OCMC].va, 1609a99b5657SRoger Quadros prueth->ocmc_ram_size); 1610a99b5657SRoger Quadros 1611a99b5657SRoger Quadros for (i = PRUETH_MEM_DRAM0; i < PRUETH_MEM_OCMC; i++) { 1612a99b5657SRoger Quadros if (prueth->mem[i].va) 1613a99b5657SRoger Quadros pruss_release_mem_region(prueth->pruss, 1614a99b5657SRoger Quadros &prueth->mem[i]); 1615a99b5657SRoger Quadros } 1616a99b5657SRoger Quadros 16171853367bSParvathi Pudi icss_iep_put(prueth->iep); 16181853367bSParvathi Pudi prueth->iep = NULL; 16191853367bSParvathi Pudi 1620511f6c1aSRoger Quadros pruss_put(prueth->pruss); 1621511f6c1aSRoger Quadros 1622511f6c1aSRoger Quadros if (prueth->eth_node[PRUETH_MAC0]) 1623511f6c1aSRoger Quadros pru_rproc_put(prueth->pru0); 1624511f6c1aSRoger Quadros if (prueth->eth_node[PRUETH_MAC1]) 1625511f6c1aSRoger Quadros pru_rproc_put(prueth->pru1); 1626511f6c1aSRoger Quadros } 1627511f6c1aSRoger Quadros 1628511f6c1aSRoger Quadros #ifdef CONFIG_PM_SLEEP 1629511f6c1aSRoger Quadros static int icssm_prueth_suspend(struct device *dev) 1630511f6c1aSRoger Quadros { 1631511f6c1aSRoger Quadros struct prueth *prueth = dev_get_drvdata(dev); 1632511f6c1aSRoger Quadros struct net_device *ndev; 1633511f6c1aSRoger Quadros int i, ret; 1634511f6c1aSRoger Quadros 1635511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 1636511f6c1aSRoger Quadros ndev = prueth->registered_netdevs[i]; 1637511f6c1aSRoger Quadros 1638511f6c1aSRoger Quadros if (!ndev) 1639511f6c1aSRoger Quadros continue; 1640511f6c1aSRoger Quadros 1641511f6c1aSRoger Quadros if (netif_running(ndev)) { 1642511f6c1aSRoger Quadros netif_device_detach(ndev); 1643511f6c1aSRoger Quadros ret = icssm_emac_ndo_stop(ndev); 1644511f6c1aSRoger Quadros if (ret < 0) { 1645511f6c1aSRoger Quadros netdev_err(ndev, "failed to stop: %d", ret); 1646511f6c1aSRoger Quadros return ret; 1647511f6c1aSRoger Quadros } 1648511f6c1aSRoger Quadros } 1649511f6c1aSRoger Quadros } 1650511f6c1aSRoger Quadros 1651511f6c1aSRoger Quadros return 0; 1652511f6c1aSRoger Quadros } 1653511f6c1aSRoger Quadros 1654511f6c1aSRoger Quadros static int icssm_prueth_resume(struct device *dev) 1655511f6c1aSRoger Quadros { 1656511f6c1aSRoger Quadros struct prueth *prueth = dev_get_drvdata(dev); 1657511f6c1aSRoger Quadros struct net_device *ndev; 1658511f6c1aSRoger Quadros int i, ret; 1659511f6c1aSRoger Quadros 1660511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 1661511f6c1aSRoger Quadros ndev = prueth->registered_netdevs[i]; 1662511f6c1aSRoger Quadros 1663511f6c1aSRoger Quadros if (!ndev) 1664511f6c1aSRoger Quadros continue; 1665511f6c1aSRoger Quadros 1666511f6c1aSRoger Quadros if (netif_running(ndev)) { 1667511f6c1aSRoger Quadros ret = icssm_emac_ndo_open(ndev); 1668511f6c1aSRoger Quadros if (ret < 0) { 1669511f6c1aSRoger Quadros netdev_err(ndev, "failed to start: %d", ret); 1670511f6c1aSRoger Quadros return ret; 1671511f6c1aSRoger Quadros } 1672511f6c1aSRoger Quadros netif_device_attach(ndev); 1673511f6c1aSRoger Quadros } 1674511f6c1aSRoger Quadros } 1675511f6c1aSRoger Quadros 1676511f6c1aSRoger Quadros return 0; 1677511f6c1aSRoger Quadros } 1678511f6c1aSRoger Quadros 1679511f6c1aSRoger Quadros #endif /* CONFIG_PM_SLEEP */ 1680511f6c1aSRoger Quadros 1681511f6c1aSRoger Quadros static const struct dev_pm_ops prueth_dev_pm_ops = { 1682511f6c1aSRoger Quadros SET_SYSTEM_SLEEP_PM_OPS(icssm_prueth_suspend, icssm_prueth_resume) 1683511f6c1aSRoger Quadros }; 1684511f6c1aSRoger Quadros 1685511f6c1aSRoger Quadros /* AM335x SoC-specific firmware data */ 1686511f6c1aSRoger Quadros static struct prueth_private_data am335x_prueth_pdata = { 1687a99b5657SRoger Quadros .driver_data = PRUSS_AM33XX, 1688511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU0] = { 1689511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 1690511f6c1aSRoger Quadros "ti-pruss/am335x-pru0-prueth-fw.elf", 1691511f6c1aSRoger Quadros }, 1692511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU1] = { 1693511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 1694511f6c1aSRoger Quadros "ti-pruss/am335x-pru1-prueth-fw.elf", 1695511f6c1aSRoger Quadros }, 1696511f6c1aSRoger Quadros }; 1697511f6c1aSRoger Quadros 1698511f6c1aSRoger Quadros /* AM437x SoC-specific firmware data */ 1699511f6c1aSRoger Quadros static struct prueth_private_data am437x_prueth_pdata = { 1700a99b5657SRoger Quadros .driver_data = PRUSS_AM43XX, 1701511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU0] = { 1702511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 1703511f6c1aSRoger Quadros "ti-pruss/am437x-pru0-prueth-fw.elf", 1704511f6c1aSRoger Quadros }, 1705511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU1] = { 1706511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 1707511f6c1aSRoger Quadros "ti-pruss/am437x-pru1-prueth-fw.elf", 1708511f6c1aSRoger Quadros }, 1709511f6c1aSRoger Quadros }; 1710511f6c1aSRoger Quadros 1711511f6c1aSRoger Quadros /* AM57xx SoC-specific firmware data */ 1712511f6c1aSRoger Quadros static struct prueth_private_data am57xx_prueth_pdata = { 1713a99b5657SRoger Quadros .driver_data = PRUSS_AM57XX, 1714511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU0] = { 1715511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 1716511f6c1aSRoger Quadros "ti-pruss/am57xx-pru0-prueth-fw.elf", 1717511f6c1aSRoger Quadros }, 1718511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU1] = { 1719511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 1720511f6c1aSRoger Quadros "ti-pruss/am57xx-pru1-prueth-fw.elf", 1721511f6c1aSRoger Quadros }, 1722511f6c1aSRoger Quadros }; 1723511f6c1aSRoger Quadros 1724511f6c1aSRoger Quadros static const struct of_device_id prueth_dt_match[] = { 1725511f6c1aSRoger Quadros { .compatible = "ti,am57-prueth", .data = &am57xx_prueth_pdata, }, 1726511f6c1aSRoger Quadros { .compatible = "ti,am4376-prueth", .data = &am437x_prueth_pdata, }, 1727511f6c1aSRoger Quadros { .compatible = "ti,am3359-prueth", .data = &am335x_prueth_pdata, }, 1728511f6c1aSRoger Quadros { /* sentinel */ } 1729511f6c1aSRoger Quadros }; 1730511f6c1aSRoger Quadros MODULE_DEVICE_TABLE(of, prueth_dt_match); 1731511f6c1aSRoger Quadros 1732511f6c1aSRoger Quadros static struct platform_driver prueth_driver = { 1733511f6c1aSRoger Quadros .probe = icssm_prueth_probe, 1734511f6c1aSRoger Quadros .remove = icssm_prueth_remove, 1735511f6c1aSRoger Quadros .driver = { 1736511f6c1aSRoger Quadros .name = "prueth", 1737511f6c1aSRoger Quadros .of_match_table = prueth_dt_match, 1738511f6c1aSRoger Quadros .pm = &prueth_dev_pm_ops, 1739511f6c1aSRoger Quadros }, 1740511f6c1aSRoger Quadros }; 1741511f6c1aSRoger Quadros module_platform_driver(prueth_driver); 1742511f6c1aSRoger Quadros 1743511f6c1aSRoger Quadros MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>"); 1744511f6c1aSRoger Quadros MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 1745511f6c1aSRoger Quadros MODULE_DESCRIPTION("PRUSS ICSSM Ethernet Driver"); 1746511f6c1aSRoger Quadros MODULE_LICENSE("GPL"); 1747