1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 24b8ac966SJiri Pirko /* 34b8ac966SJiri Pirko * drivers/net/ethernet/rocker/rocker.c - Rocker switch device driver 411ce2ba3SJiri Pirko * Copyright (c) 2014-2016 Jiri Pirko <jiri@mellanox.com> 54b8ac966SJiri Pirko * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com> 64b8ac966SJiri Pirko */ 74b8ac966SJiri Pirko 84b8ac966SJiri Pirko #include <linux/kernel.h> 94b8ac966SJiri Pirko #include <linux/module.h> 104b8ac966SJiri Pirko #include <linux/pci.h> 114b8ac966SJiri Pirko #include <linux/interrupt.h> 124b8ac966SJiri Pirko #include <linux/sched.h> 134b8ac966SJiri Pirko #include <linux/wait.h> 144b8ac966SJiri Pirko #include <linux/spinlock.h> 154b8ac966SJiri Pirko #include <linux/sort.h> 164b8ac966SJiri Pirko #include <linux/random.h> 174b8ac966SJiri Pirko #include <linux/netdevice.h> 184b8ac966SJiri Pirko #include <linux/skbuff.h> 194b8ac966SJiri Pirko #include <linux/socket.h> 204b8ac966SJiri Pirko #include <linux/etherdevice.h> 214b8ac966SJiri Pirko #include <linux/ethtool.h> 224b8ac966SJiri Pirko #include <linux/if_ether.h> 234b8ac966SJiri Pirko #include <linux/if_vlan.h> 246c707945SScott Feldman #include <linux/if_bridge.h> 259f6bbf7cSScott Feldman #include <linux/bitops.h> 26db19170bSDavid Ahern #include <linux/ctype.h> 27c1bb279cSIdo Schimmel #include <linux/workqueue.h> 284b8ac966SJiri Pirko #include <net/switchdev.h> 294b8ac966SJiri Pirko #include <net/rtnetlink.h> 30c1beeef7SScott Feldman #include <net/netevent.h> 31c1beeef7SScott Feldman #include <net/arp.h> 325d7bfd14SIdo Schimmel #include <net/fib_rules.h> 3304b1d4e5SIdo Schimmel #include <net/fib_notifier.h> 342f8e2c87SChristoph Hellwig #include <linux/io-64-nonatomic-lo-hi.h> 354b8ac966SJiri Pirko #include <generated/utsrelease.h> 364b8ac966SJiri Pirko 370fe685f6SJiri Pirko #include "rocker_hw.h" 38de152192SJiri Pirko #include "rocker.h" 39de152192SJiri Pirko #include "rocker_tlv.h" 404b8ac966SJiri Pirko 414b8ac966SJiri Pirko static const char rocker_driver_name[] = "rocker"; 424b8ac966SJiri Pirko 434b8ac966SJiri Pirko static const struct pci_device_id rocker_pci_id_table[] = { 444b8ac966SJiri Pirko {PCI_VDEVICE(REDHAT, PCI_DEVICE_ID_REDHAT_ROCKER), 0}, 454b8ac966SJiri Pirko {0, } 464b8ac966SJiri Pirko }; 474b8ac966SJiri Pirko 484b8ac966SJiri Pirko struct rocker_wait { 494b8ac966SJiri Pirko wait_queue_head_t wait; 504b8ac966SJiri Pirko bool done; 51179f9a25SScott Feldman bool nowait; 524b8ac966SJiri Pirko }; 534b8ac966SJiri Pirko 544b8ac966SJiri Pirko static void rocker_wait_reset(struct rocker_wait *wait) 554b8ac966SJiri Pirko { 564b8ac966SJiri Pirko wait->done = false; 57179f9a25SScott Feldman wait->nowait = false; 584b8ac966SJiri Pirko } 594b8ac966SJiri Pirko 604b8ac966SJiri Pirko static void rocker_wait_init(struct rocker_wait *wait) 614b8ac966SJiri Pirko { 624b8ac966SJiri Pirko init_waitqueue_head(&wait->wait); 634b8ac966SJiri Pirko rocker_wait_reset(wait); 644b8ac966SJiri Pirko } 654b8ac966SJiri Pirko 66ca0a5f2aSJiri Pirko static struct rocker_wait *rocker_wait_create(void) 674b8ac966SJiri Pirko { 684b8ac966SJiri Pirko struct rocker_wait *wait; 694b8ac966SJiri Pirko 70ca0a5f2aSJiri Pirko wait = kzalloc(sizeof(*wait), GFP_KERNEL); 714b8ac966SJiri Pirko if (!wait) 724b8ac966SJiri Pirko return NULL; 734b8ac966SJiri Pirko return wait; 744b8ac966SJiri Pirko } 754b8ac966SJiri Pirko 76ca0a5f2aSJiri Pirko static void rocker_wait_destroy(struct rocker_wait *wait) 774b8ac966SJiri Pirko { 78ca0a5f2aSJiri Pirko kfree(wait); 794b8ac966SJiri Pirko } 804b8ac966SJiri Pirko 814b8ac966SJiri Pirko static bool rocker_wait_event_timeout(struct rocker_wait *wait, 824b8ac966SJiri Pirko unsigned long timeout) 834b8ac966SJiri Pirko { 844b8ac966SJiri Pirko wait_event_timeout(wait->wait, wait->done, HZ / 10); 854b8ac966SJiri Pirko if (!wait->done) 864b8ac966SJiri Pirko return false; 874b8ac966SJiri Pirko return true; 884b8ac966SJiri Pirko } 894b8ac966SJiri Pirko 904b8ac966SJiri Pirko static void rocker_wait_wake_up(struct rocker_wait *wait) 914b8ac966SJiri Pirko { 924b8ac966SJiri Pirko wait->done = true; 934b8ac966SJiri Pirko wake_up(&wait->wait); 944b8ac966SJiri Pirko } 954b8ac966SJiri Pirko 96e5054643SSimon Horman static u32 rocker_msix_vector(const struct rocker *rocker, unsigned int vector) 974b8ac966SJiri Pirko { 984b8ac966SJiri Pirko return rocker->msix_entries[vector].vector; 994b8ac966SJiri Pirko } 1004b8ac966SJiri Pirko 101e5054643SSimon Horman static u32 rocker_msix_tx_vector(const struct rocker_port *rocker_port) 1024b8ac966SJiri Pirko { 1034b8ac966SJiri Pirko return rocker_msix_vector(rocker_port->rocker, 1044b8ac966SJiri Pirko ROCKER_MSIX_VEC_TX(rocker_port->port_number)); 1054b8ac966SJiri Pirko } 1064b8ac966SJiri Pirko 107e5054643SSimon Horman static u32 rocker_msix_rx_vector(const struct rocker_port *rocker_port) 1084b8ac966SJiri Pirko { 1094b8ac966SJiri Pirko return rocker_msix_vector(rocker_port->rocker, 1104b8ac966SJiri Pirko ROCKER_MSIX_VEC_RX(rocker_port->port_number)); 1114b8ac966SJiri Pirko } 1124b8ac966SJiri Pirko 1134b8ac966SJiri Pirko #define rocker_write32(rocker, reg, val) \ 1144b8ac966SJiri Pirko writel((val), (rocker)->hw_addr + (ROCKER_ ## reg)) 1154b8ac966SJiri Pirko #define rocker_read32(rocker, reg) \ 1164b8ac966SJiri Pirko readl((rocker)->hw_addr + (ROCKER_ ## reg)) 1174b8ac966SJiri Pirko #define rocker_write64(rocker, reg, val) \ 1184b8ac966SJiri Pirko writeq((val), (rocker)->hw_addr + (ROCKER_ ## reg)) 1194b8ac966SJiri Pirko #define rocker_read64(rocker, reg) \ 1204b8ac966SJiri Pirko readq((rocker)->hw_addr + (ROCKER_ ## reg)) 1214b8ac966SJiri Pirko 1224b8ac966SJiri Pirko /***************************** 1234b8ac966SJiri Pirko * HW basic testing functions 1244b8ac966SJiri Pirko *****************************/ 1254b8ac966SJiri Pirko 126e5054643SSimon Horman static int rocker_reg_test(const struct rocker *rocker) 1274b8ac966SJiri Pirko { 128e5054643SSimon Horman const struct pci_dev *pdev = rocker->pdev; 1294b8ac966SJiri Pirko u64 test_reg; 1304b8ac966SJiri Pirko u64 rnd; 1314b8ac966SJiri Pirko 1324b8ac966SJiri Pirko rnd = prandom_u32(); 1334b8ac966SJiri Pirko rnd >>= 1; 1344b8ac966SJiri Pirko rocker_write32(rocker, TEST_REG, rnd); 1354b8ac966SJiri Pirko test_reg = rocker_read32(rocker, TEST_REG); 1364b8ac966SJiri Pirko if (test_reg != rnd * 2) { 1374b8ac966SJiri Pirko dev_err(&pdev->dev, "unexpected 32bit register value %08llx, expected %08llx\n", 1384b8ac966SJiri Pirko test_reg, rnd * 2); 1394b8ac966SJiri Pirko return -EIO; 1404b8ac966SJiri Pirko } 1414b8ac966SJiri Pirko 1424b8ac966SJiri Pirko rnd = prandom_u32(); 1434b8ac966SJiri Pirko rnd <<= 31; 1444b8ac966SJiri Pirko rnd |= prandom_u32(); 1454b8ac966SJiri Pirko rocker_write64(rocker, TEST_REG64, rnd); 1464b8ac966SJiri Pirko test_reg = rocker_read64(rocker, TEST_REG64); 1474b8ac966SJiri Pirko if (test_reg != rnd * 2) { 1484b8ac966SJiri Pirko dev_err(&pdev->dev, "unexpected 64bit register value %16llx, expected %16llx\n", 1494b8ac966SJiri Pirko test_reg, rnd * 2); 1504b8ac966SJiri Pirko return -EIO; 1514b8ac966SJiri Pirko } 1524b8ac966SJiri Pirko 1534b8ac966SJiri Pirko return 0; 1544b8ac966SJiri Pirko } 1554b8ac966SJiri Pirko 156e5054643SSimon Horman static int rocker_dma_test_one(const struct rocker *rocker, 157e5054643SSimon Horman struct rocker_wait *wait, u32 test_type, 158e5054643SSimon Horman dma_addr_t dma_handle, const unsigned char *buf, 159e5054643SSimon Horman const unsigned char *expect, size_t size) 1604b8ac966SJiri Pirko { 161e5054643SSimon Horman const struct pci_dev *pdev = rocker->pdev; 1624b8ac966SJiri Pirko int i; 1634b8ac966SJiri Pirko 1644b8ac966SJiri Pirko rocker_wait_reset(wait); 1654b8ac966SJiri Pirko rocker_write32(rocker, TEST_DMA_CTRL, test_type); 1664b8ac966SJiri Pirko 1674b8ac966SJiri Pirko if (!rocker_wait_event_timeout(wait, HZ / 10)) { 1684b8ac966SJiri Pirko dev_err(&pdev->dev, "no interrupt received within a timeout\n"); 1694b8ac966SJiri Pirko return -EIO; 1704b8ac966SJiri Pirko } 1714b8ac966SJiri Pirko 1724b8ac966SJiri Pirko for (i = 0; i < size; i++) { 1734b8ac966SJiri Pirko if (buf[i] != expect[i]) { 1744b8ac966SJiri Pirko dev_err(&pdev->dev, "unexpected memory content %02x at byte %x\n, %02x expected", 1754b8ac966SJiri Pirko buf[i], i, expect[i]); 1764b8ac966SJiri Pirko return -EIO; 1774b8ac966SJiri Pirko } 1784b8ac966SJiri Pirko } 1794b8ac966SJiri Pirko return 0; 1804b8ac966SJiri Pirko } 1814b8ac966SJiri Pirko 1824b8ac966SJiri Pirko #define ROCKER_TEST_DMA_BUF_SIZE (PAGE_SIZE * 4) 1834b8ac966SJiri Pirko #define ROCKER_TEST_DMA_FILL_PATTERN 0x96 1844b8ac966SJiri Pirko 185e5054643SSimon Horman static int rocker_dma_test_offset(const struct rocker *rocker, 1864b8ac966SJiri Pirko struct rocker_wait *wait, int offset) 1874b8ac966SJiri Pirko { 1884b8ac966SJiri Pirko struct pci_dev *pdev = rocker->pdev; 1894b8ac966SJiri Pirko unsigned char *alloc; 1904b8ac966SJiri Pirko unsigned char *buf; 1914b8ac966SJiri Pirko unsigned char *expect; 1924b8ac966SJiri Pirko dma_addr_t dma_handle; 1934b8ac966SJiri Pirko int i; 1944b8ac966SJiri Pirko int err; 1954b8ac966SJiri Pirko 1964b8ac966SJiri Pirko alloc = kzalloc(ROCKER_TEST_DMA_BUF_SIZE * 2 + offset, 1974b8ac966SJiri Pirko GFP_KERNEL | GFP_DMA); 1984b8ac966SJiri Pirko if (!alloc) 1994b8ac966SJiri Pirko return -ENOMEM; 2004b8ac966SJiri Pirko buf = alloc + offset; 2014b8ac966SJiri Pirko expect = buf + ROCKER_TEST_DMA_BUF_SIZE; 2024b8ac966SJiri Pirko 2034b8ac966SJiri Pirko dma_handle = pci_map_single(pdev, buf, ROCKER_TEST_DMA_BUF_SIZE, 2044b8ac966SJiri Pirko PCI_DMA_BIDIRECTIONAL); 2054b8ac966SJiri Pirko if (pci_dma_mapping_error(pdev, dma_handle)) { 2064b8ac966SJiri Pirko err = -EIO; 2074b8ac966SJiri Pirko goto free_alloc; 2084b8ac966SJiri Pirko } 2094b8ac966SJiri Pirko 2104b8ac966SJiri Pirko rocker_write64(rocker, TEST_DMA_ADDR, dma_handle); 2114b8ac966SJiri Pirko rocker_write32(rocker, TEST_DMA_SIZE, ROCKER_TEST_DMA_BUF_SIZE); 2124b8ac966SJiri Pirko 2134b8ac966SJiri Pirko memset(expect, ROCKER_TEST_DMA_FILL_PATTERN, ROCKER_TEST_DMA_BUF_SIZE); 2144b8ac966SJiri Pirko err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_FILL, 2154b8ac966SJiri Pirko dma_handle, buf, expect, 2164b8ac966SJiri Pirko ROCKER_TEST_DMA_BUF_SIZE); 2174b8ac966SJiri Pirko if (err) 2184b8ac966SJiri Pirko goto unmap; 2194b8ac966SJiri Pirko 2204b8ac966SJiri Pirko memset(expect, 0, ROCKER_TEST_DMA_BUF_SIZE); 2214b8ac966SJiri Pirko err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_CLEAR, 2224b8ac966SJiri Pirko dma_handle, buf, expect, 2234b8ac966SJiri Pirko ROCKER_TEST_DMA_BUF_SIZE); 2244b8ac966SJiri Pirko if (err) 2254b8ac966SJiri Pirko goto unmap; 2264b8ac966SJiri Pirko 2274b8ac966SJiri Pirko prandom_bytes(buf, ROCKER_TEST_DMA_BUF_SIZE); 2284b8ac966SJiri Pirko for (i = 0; i < ROCKER_TEST_DMA_BUF_SIZE; i++) 2294b8ac966SJiri Pirko expect[i] = ~buf[i]; 2304b8ac966SJiri Pirko err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_INVERT, 2314b8ac966SJiri Pirko dma_handle, buf, expect, 2324b8ac966SJiri Pirko ROCKER_TEST_DMA_BUF_SIZE); 2334b8ac966SJiri Pirko if (err) 2344b8ac966SJiri Pirko goto unmap; 2354b8ac966SJiri Pirko 2364b8ac966SJiri Pirko unmap: 2374b8ac966SJiri Pirko pci_unmap_single(pdev, dma_handle, ROCKER_TEST_DMA_BUF_SIZE, 2384b8ac966SJiri Pirko PCI_DMA_BIDIRECTIONAL); 2394b8ac966SJiri Pirko free_alloc: 2404b8ac966SJiri Pirko kfree(alloc); 2414b8ac966SJiri Pirko 2424b8ac966SJiri Pirko return err; 2434b8ac966SJiri Pirko } 2444b8ac966SJiri Pirko 245e5054643SSimon Horman static int rocker_dma_test(const struct rocker *rocker, 246e5054643SSimon Horman struct rocker_wait *wait) 2474b8ac966SJiri Pirko { 2484b8ac966SJiri Pirko int i; 2494b8ac966SJiri Pirko int err; 2504b8ac966SJiri Pirko 2514b8ac966SJiri Pirko for (i = 0; i < 8; i++) { 2524b8ac966SJiri Pirko err = rocker_dma_test_offset(rocker, wait, i); 2534b8ac966SJiri Pirko if (err) 2544b8ac966SJiri Pirko return err; 2554b8ac966SJiri Pirko } 2564b8ac966SJiri Pirko return 0; 2574b8ac966SJiri Pirko } 2584b8ac966SJiri Pirko 2594b8ac966SJiri Pirko static irqreturn_t rocker_test_irq_handler(int irq, void *dev_id) 2604b8ac966SJiri Pirko { 2614b8ac966SJiri Pirko struct rocker_wait *wait = dev_id; 2624b8ac966SJiri Pirko 2634b8ac966SJiri Pirko rocker_wait_wake_up(wait); 2644b8ac966SJiri Pirko 2654b8ac966SJiri Pirko return IRQ_HANDLED; 2664b8ac966SJiri Pirko } 2674b8ac966SJiri Pirko 268e5054643SSimon Horman static int rocker_basic_hw_test(const struct rocker *rocker) 2694b8ac966SJiri Pirko { 270e5054643SSimon Horman const struct pci_dev *pdev = rocker->pdev; 2714b8ac966SJiri Pirko struct rocker_wait wait; 2724b8ac966SJiri Pirko int err; 2734b8ac966SJiri Pirko 2744b8ac966SJiri Pirko err = rocker_reg_test(rocker); 2754b8ac966SJiri Pirko if (err) { 2764b8ac966SJiri Pirko dev_err(&pdev->dev, "reg test failed\n"); 2774b8ac966SJiri Pirko return err; 2784b8ac966SJiri Pirko } 2794b8ac966SJiri Pirko 2804b8ac966SJiri Pirko err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_TEST), 2814b8ac966SJiri Pirko rocker_test_irq_handler, 0, 2824b8ac966SJiri Pirko rocker_driver_name, &wait); 2834b8ac966SJiri Pirko if (err) { 2844b8ac966SJiri Pirko dev_err(&pdev->dev, "cannot assign test irq\n"); 2854b8ac966SJiri Pirko return err; 2864b8ac966SJiri Pirko } 2874b8ac966SJiri Pirko 2884b8ac966SJiri Pirko rocker_wait_init(&wait); 2894b8ac966SJiri Pirko rocker_write32(rocker, TEST_IRQ, ROCKER_MSIX_VEC_TEST); 2904b8ac966SJiri Pirko 2914b8ac966SJiri Pirko if (!rocker_wait_event_timeout(&wait, HZ / 10)) { 2924b8ac966SJiri Pirko dev_err(&pdev->dev, "no interrupt received within a timeout\n"); 2934b8ac966SJiri Pirko err = -EIO; 2944b8ac966SJiri Pirko goto free_irq; 2954b8ac966SJiri Pirko } 2964b8ac966SJiri Pirko 2974b8ac966SJiri Pirko err = rocker_dma_test(rocker, &wait); 2984b8ac966SJiri Pirko if (err) 2994b8ac966SJiri Pirko dev_err(&pdev->dev, "dma test failed\n"); 3004b8ac966SJiri Pirko 3014b8ac966SJiri Pirko free_irq: 3024b8ac966SJiri Pirko free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_TEST), &wait); 3034b8ac966SJiri Pirko return err; 3044b8ac966SJiri Pirko } 3054b8ac966SJiri Pirko 3064b8ac966SJiri Pirko /****************************************** 3074b8ac966SJiri Pirko * DMA rings and descriptors manipulations 3084b8ac966SJiri Pirko ******************************************/ 3094b8ac966SJiri Pirko 3104b8ac966SJiri Pirko static u32 __pos_inc(u32 pos, size_t limit) 3114b8ac966SJiri Pirko { 3124b8ac966SJiri Pirko return ++pos == limit ? 0 : pos; 3134b8ac966SJiri Pirko } 3144b8ac966SJiri Pirko 315e5054643SSimon Horman static int rocker_desc_err(const struct rocker_desc_info *desc_info) 3164b8ac966SJiri Pirko { 3177eb344f8SScott Feldman int err = desc_info->desc->comp_err & ~ROCKER_DMA_DESC_COMP_ERR_GEN; 3187eb344f8SScott Feldman 3197eb344f8SScott Feldman switch (err) { 3207eb344f8SScott Feldman case ROCKER_OK: 3217eb344f8SScott Feldman return 0; 3227eb344f8SScott Feldman case -ROCKER_ENOENT: 3237eb344f8SScott Feldman return -ENOENT; 3247eb344f8SScott Feldman case -ROCKER_ENXIO: 3257eb344f8SScott Feldman return -ENXIO; 3267eb344f8SScott Feldman case -ROCKER_ENOMEM: 3277eb344f8SScott Feldman return -ENOMEM; 3287eb344f8SScott Feldman case -ROCKER_EEXIST: 3297eb344f8SScott Feldman return -EEXIST; 3307eb344f8SScott Feldman case -ROCKER_EINVAL: 3317eb344f8SScott Feldman return -EINVAL; 3327eb344f8SScott Feldman case -ROCKER_EMSGSIZE: 3337eb344f8SScott Feldman return -EMSGSIZE; 3347eb344f8SScott Feldman case -ROCKER_ENOTSUP: 3357eb344f8SScott Feldman return -EOPNOTSUPP; 3367eb344f8SScott Feldman case -ROCKER_ENOBUFS: 3377eb344f8SScott Feldman return -ENOBUFS; 3387eb344f8SScott Feldman } 3397eb344f8SScott Feldman 3407eb344f8SScott Feldman return -EINVAL; 3414b8ac966SJiri Pirko } 3424b8ac966SJiri Pirko 343e5054643SSimon Horman static void rocker_desc_gen_clear(const struct rocker_desc_info *desc_info) 3444b8ac966SJiri Pirko { 3454b8ac966SJiri Pirko desc_info->desc->comp_err &= ~ROCKER_DMA_DESC_COMP_ERR_GEN; 3464b8ac966SJiri Pirko } 3474b8ac966SJiri Pirko 348e5054643SSimon Horman static bool rocker_desc_gen(const struct rocker_desc_info *desc_info) 3494b8ac966SJiri Pirko { 3504b8ac966SJiri Pirko u32 comp_err = desc_info->desc->comp_err; 3514b8ac966SJiri Pirko 3524b8ac966SJiri Pirko return comp_err & ROCKER_DMA_DESC_COMP_ERR_GEN ? true : false; 3534b8ac966SJiri Pirko } 3544b8ac966SJiri Pirko 35511ce2ba3SJiri Pirko static void * 35611ce2ba3SJiri Pirko rocker_desc_cookie_ptr_get(const struct rocker_desc_info *desc_info) 3574b8ac966SJiri Pirko { 358adedf37bSArnd Bergmann return (void *)(uintptr_t)desc_info->desc->cookie; 3594b8ac966SJiri Pirko } 3604b8ac966SJiri Pirko 361e5054643SSimon Horman static void rocker_desc_cookie_ptr_set(const struct rocker_desc_info *desc_info, 3624b8ac966SJiri Pirko void *ptr) 3634b8ac966SJiri Pirko { 364adedf37bSArnd Bergmann desc_info->desc->cookie = (uintptr_t) ptr; 3654b8ac966SJiri Pirko } 3664b8ac966SJiri Pirko 3674b8ac966SJiri Pirko static struct rocker_desc_info * 368e5054643SSimon Horman rocker_desc_head_get(const struct rocker_dma_ring_info *info) 3694b8ac966SJiri Pirko { 3709333f207SYueHaibing struct rocker_desc_info *desc_info; 3714b8ac966SJiri Pirko u32 head = __pos_inc(info->head, info->size); 3724b8ac966SJiri Pirko 3734b8ac966SJiri Pirko desc_info = &info->desc_info[info->head]; 3744b8ac966SJiri Pirko if (head == info->tail) 3754b8ac966SJiri Pirko return NULL; /* ring full */ 3764b8ac966SJiri Pirko desc_info->tlv_size = 0; 3774b8ac966SJiri Pirko return desc_info; 3784b8ac966SJiri Pirko } 3794b8ac966SJiri Pirko 380e5054643SSimon Horman static void rocker_desc_commit(const struct rocker_desc_info *desc_info) 3814b8ac966SJiri Pirko { 3824b8ac966SJiri Pirko desc_info->desc->buf_size = desc_info->data_size; 3834b8ac966SJiri Pirko desc_info->desc->tlv_size = desc_info->tlv_size; 3844b8ac966SJiri Pirko } 3854b8ac966SJiri Pirko 386e5054643SSimon Horman static void rocker_desc_head_set(const struct rocker *rocker, 3874b8ac966SJiri Pirko struct rocker_dma_ring_info *info, 388e5054643SSimon Horman const struct rocker_desc_info *desc_info) 3894b8ac966SJiri Pirko { 3904b8ac966SJiri Pirko u32 head = __pos_inc(info->head, info->size); 3914b8ac966SJiri Pirko 3924b8ac966SJiri Pirko BUG_ON(head == info->tail); 3934b8ac966SJiri Pirko rocker_desc_commit(desc_info); 3944b8ac966SJiri Pirko info->head = head; 3954b8ac966SJiri Pirko rocker_write32(rocker, DMA_DESC_HEAD(info->type), head); 3964b8ac966SJiri Pirko } 3974b8ac966SJiri Pirko 3984b8ac966SJiri Pirko static struct rocker_desc_info * 3994b8ac966SJiri Pirko rocker_desc_tail_get(struct rocker_dma_ring_info *info) 4004b8ac966SJiri Pirko { 4019333f207SYueHaibing struct rocker_desc_info *desc_info; 4024b8ac966SJiri Pirko 4034b8ac966SJiri Pirko if (info->tail == info->head) 4044b8ac966SJiri Pirko return NULL; /* nothing to be done between head and tail */ 4054b8ac966SJiri Pirko desc_info = &info->desc_info[info->tail]; 4064b8ac966SJiri Pirko if (!rocker_desc_gen(desc_info)) 4074b8ac966SJiri Pirko return NULL; /* gen bit not set, desc is not ready yet */ 4084b8ac966SJiri Pirko info->tail = __pos_inc(info->tail, info->size); 4094b8ac966SJiri Pirko desc_info->tlv_size = desc_info->desc->tlv_size; 4104b8ac966SJiri Pirko return desc_info; 4114b8ac966SJiri Pirko } 4124b8ac966SJiri Pirko 413e5054643SSimon Horman static void rocker_dma_ring_credits_set(const struct rocker *rocker, 414e5054643SSimon Horman const struct rocker_dma_ring_info *info, 4154b8ac966SJiri Pirko u32 credits) 4164b8ac966SJiri Pirko { 4174b8ac966SJiri Pirko if (credits) 4184b8ac966SJiri Pirko rocker_write32(rocker, DMA_DESC_CREDITS(info->type), credits); 4194b8ac966SJiri Pirko } 4204b8ac966SJiri Pirko 4214b8ac966SJiri Pirko static unsigned long rocker_dma_ring_size_fix(size_t size) 4224b8ac966SJiri Pirko { 4234b8ac966SJiri Pirko return max(ROCKER_DMA_SIZE_MIN, 4244b8ac966SJiri Pirko min(roundup_pow_of_two(size), ROCKER_DMA_SIZE_MAX)); 4254b8ac966SJiri Pirko } 4264b8ac966SJiri Pirko 427e5054643SSimon Horman static int rocker_dma_ring_create(const struct rocker *rocker, 4284b8ac966SJiri Pirko unsigned int type, 4294b8ac966SJiri Pirko size_t size, 4304b8ac966SJiri Pirko struct rocker_dma_ring_info *info) 4314b8ac966SJiri Pirko { 4324b8ac966SJiri Pirko int i; 4334b8ac966SJiri Pirko 4344b8ac966SJiri Pirko BUG_ON(size != rocker_dma_ring_size_fix(size)); 4354b8ac966SJiri Pirko info->size = size; 4364b8ac966SJiri Pirko info->type = type; 4374b8ac966SJiri Pirko info->head = 0; 4384b8ac966SJiri Pirko info->tail = 0; 4394b8ac966SJiri Pirko info->desc_info = kcalloc(info->size, sizeof(*info->desc_info), 4404b8ac966SJiri Pirko GFP_KERNEL); 4414b8ac966SJiri Pirko if (!info->desc_info) 4424b8ac966SJiri Pirko return -ENOMEM; 4434b8ac966SJiri Pirko 4444b8ac966SJiri Pirko info->desc = pci_alloc_consistent(rocker->pdev, 4454b8ac966SJiri Pirko info->size * sizeof(*info->desc), 4464b8ac966SJiri Pirko &info->mapaddr); 4474b8ac966SJiri Pirko if (!info->desc) { 4484b8ac966SJiri Pirko kfree(info->desc_info); 4494b8ac966SJiri Pirko return -ENOMEM; 4504b8ac966SJiri Pirko } 4514b8ac966SJiri Pirko 4524b8ac966SJiri Pirko for (i = 0; i < info->size; i++) 4534b8ac966SJiri Pirko info->desc_info[i].desc = &info->desc[i]; 4544b8ac966SJiri Pirko 4554b8ac966SJiri Pirko rocker_write32(rocker, DMA_DESC_CTRL(info->type), 4564b8ac966SJiri Pirko ROCKER_DMA_DESC_CTRL_RESET); 4574b8ac966SJiri Pirko rocker_write64(rocker, DMA_DESC_ADDR(info->type), info->mapaddr); 4584b8ac966SJiri Pirko rocker_write32(rocker, DMA_DESC_SIZE(info->type), info->size); 4594b8ac966SJiri Pirko 4604b8ac966SJiri Pirko return 0; 4614b8ac966SJiri Pirko } 4624b8ac966SJiri Pirko 463e5054643SSimon Horman static void rocker_dma_ring_destroy(const struct rocker *rocker, 464e5054643SSimon Horman const struct rocker_dma_ring_info *info) 4654b8ac966SJiri Pirko { 4664b8ac966SJiri Pirko rocker_write64(rocker, DMA_DESC_ADDR(info->type), 0); 4674b8ac966SJiri Pirko 4684b8ac966SJiri Pirko pci_free_consistent(rocker->pdev, 4694b8ac966SJiri Pirko info->size * sizeof(struct rocker_desc), 4704b8ac966SJiri Pirko info->desc, info->mapaddr); 4714b8ac966SJiri Pirko kfree(info->desc_info); 4724b8ac966SJiri Pirko } 4734b8ac966SJiri Pirko 474e5054643SSimon Horman static void rocker_dma_ring_pass_to_producer(const struct rocker *rocker, 4754b8ac966SJiri Pirko struct rocker_dma_ring_info *info) 4764b8ac966SJiri Pirko { 4774b8ac966SJiri Pirko int i; 4784b8ac966SJiri Pirko 4794b8ac966SJiri Pirko BUG_ON(info->head || info->tail); 4804b8ac966SJiri Pirko 4814b8ac966SJiri Pirko /* When ring is consumer, we need to advance head for each desc. 4824b8ac966SJiri Pirko * That tells hw that the desc is ready to be used by it. 4834b8ac966SJiri Pirko */ 4844b8ac966SJiri Pirko for (i = 0; i < info->size - 1; i++) 4854b8ac966SJiri Pirko rocker_desc_head_set(rocker, info, &info->desc_info[i]); 4864b8ac966SJiri Pirko rocker_desc_commit(&info->desc_info[i]); 4874b8ac966SJiri Pirko } 4884b8ac966SJiri Pirko 489e5054643SSimon Horman static int rocker_dma_ring_bufs_alloc(const struct rocker *rocker, 490e5054643SSimon Horman const struct rocker_dma_ring_info *info, 4914b8ac966SJiri Pirko int direction, size_t buf_size) 4924b8ac966SJiri Pirko { 4934b8ac966SJiri Pirko struct pci_dev *pdev = rocker->pdev; 4944b8ac966SJiri Pirko int i; 4954b8ac966SJiri Pirko int err; 4964b8ac966SJiri Pirko 4974b8ac966SJiri Pirko for (i = 0; i < info->size; i++) { 4984b8ac966SJiri Pirko struct rocker_desc_info *desc_info = &info->desc_info[i]; 4994b8ac966SJiri Pirko struct rocker_desc *desc = &info->desc[i]; 5004b8ac966SJiri Pirko dma_addr_t dma_handle; 5014b8ac966SJiri Pirko char *buf; 5024b8ac966SJiri Pirko 5034b8ac966SJiri Pirko buf = kzalloc(buf_size, GFP_KERNEL | GFP_DMA); 5044b8ac966SJiri Pirko if (!buf) { 5054b8ac966SJiri Pirko err = -ENOMEM; 5064b8ac966SJiri Pirko goto rollback; 5074b8ac966SJiri Pirko } 5084b8ac966SJiri Pirko 5094b8ac966SJiri Pirko dma_handle = pci_map_single(pdev, buf, buf_size, direction); 5104b8ac966SJiri Pirko if (pci_dma_mapping_error(pdev, dma_handle)) { 5114b8ac966SJiri Pirko kfree(buf); 5124b8ac966SJiri Pirko err = -EIO; 5134b8ac966SJiri Pirko goto rollback; 5144b8ac966SJiri Pirko } 5154b8ac966SJiri Pirko 5164b8ac966SJiri Pirko desc_info->data = buf; 5174b8ac966SJiri Pirko desc_info->data_size = buf_size; 5184b8ac966SJiri Pirko dma_unmap_addr_set(desc_info, mapaddr, dma_handle); 5194b8ac966SJiri Pirko 5204b8ac966SJiri Pirko desc->buf_addr = dma_handle; 5214b8ac966SJiri Pirko desc->buf_size = buf_size; 5224b8ac966SJiri Pirko } 5234b8ac966SJiri Pirko return 0; 5244b8ac966SJiri Pirko 5254b8ac966SJiri Pirko rollback: 5264b8ac966SJiri Pirko for (i--; i >= 0; i--) { 527e5054643SSimon Horman const struct rocker_desc_info *desc_info = &info->desc_info[i]; 5284b8ac966SJiri Pirko 5294b8ac966SJiri Pirko pci_unmap_single(pdev, dma_unmap_addr(desc_info, mapaddr), 5304b8ac966SJiri Pirko desc_info->data_size, direction); 5314b8ac966SJiri Pirko kfree(desc_info->data); 5324b8ac966SJiri Pirko } 5334b8ac966SJiri Pirko return err; 5344b8ac966SJiri Pirko } 5354b8ac966SJiri Pirko 536e5054643SSimon Horman static void rocker_dma_ring_bufs_free(const struct rocker *rocker, 537e5054643SSimon Horman const struct rocker_dma_ring_info *info, 5384b8ac966SJiri Pirko int direction) 5394b8ac966SJiri Pirko { 5404b8ac966SJiri Pirko struct pci_dev *pdev = rocker->pdev; 5414b8ac966SJiri Pirko int i; 5424b8ac966SJiri Pirko 5434b8ac966SJiri Pirko for (i = 0; i < info->size; i++) { 544e5054643SSimon Horman const struct rocker_desc_info *desc_info = &info->desc_info[i]; 5454b8ac966SJiri Pirko struct rocker_desc *desc = &info->desc[i]; 5464b8ac966SJiri Pirko 5474b8ac966SJiri Pirko desc->buf_addr = 0; 5484b8ac966SJiri Pirko desc->buf_size = 0; 5494b8ac966SJiri Pirko pci_unmap_single(pdev, dma_unmap_addr(desc_info, mapaddr), 5504b8ac966SJiri Pirko desc_info->data_size, direction); 5514b8ac966SJiri Pirko kfree(desc_info->data); 5524b8ac966SJiri Pirko } 5534b8ac966SJiri Pirko } 5544b8ac966SJiri Pirko 555ca0a5f2aSJiri Pirko static int rocker_dma_cmd_ring_wait_alloc(struct rocker_desc_info *desc_info) 556ca0a5f2aSJiri Pirko { 557ca0a5f2aSJiri Pirko struct rocker_wait *wait; 558ca0a5f2aSJiri Pirko 559ca0a5f2aSJiri Pirko wait = rocker_wait_create(); 560ca0a5f2aSJiri Pirko if (!wait) 561ca0a5f2aSJiri Pirko return -ENOMEM; 562ca0a5f2aSJiri Pirko rocker_desc_cookie_ptr_set(desc_info, wait); 563ca0a5f2aSJiri Pirko return 0; 564ca0a5f2aSJiri Pirko } 565ca0a5f2aSJiri Pirko 566ca0a5f2aSJiri Pirko static void 567ca0a5f2aSJiri Pirko rocker_dma_cmd_ring_wait_free(const struct rocker_desc_info *desc_info) 568ca0a5f2aSJiri Pirko { 569ca0a5f2aSJiri Pirko struct rocker_wait *wait = rocker_desc_cookie_ptr_get(desc_info); 570ca0a5f2aSJiri Pirko 571ca0a5f2aSJiri Pirko rocker_wait_destroy(wait); 572ca0a5f2aSJiri Pirko } 573ca0a5f2aSJiri Pirko 574ca0a5f2aSJiri Pirko static int rocker_dma_cmd_ring_waits_alloc(const struct rocker *rocker) 575ca0a5f2aSJiri Pirko { 576ca0a5f2aSJiri Pirko const struct rocker_dma_ring_info *cmd_ring = &rocker->cmd_ring; 577ca0a5f2aSJiri Pirko int i; 578ca0a5f2aSJiri Pirko int err; 579ca0a5f2aSJiri Pirko 580ca0a5f2aSJiri Pirko for (i = 0; i < cmd_ring->size; i++) { 581ca0a5f2aSJiri Pirko err = rocker_dma_cmd_ring_wait_alloc(&cmd_ring->desc_info[i]); 582ca0a5f2aSJiri Pirko if (err) 583ca0a5f2aSJiri Pirko goto rollback; 584ca0a5f2aSJiri Pirko } 585ca0a5f2aSJiri Pirko return 0; 586ca0a5f2aSJiri Pirko 587ca0a5f2aSJiri Pirko rollback: 588ca0a5f2aSJiri Pirko for (i--; i >= 0; i--) 589ca0a5f2aSJiri Pirko rocker_dma_cmd_ring_wait_free(&cmd_ring->desc_info[i]); 590ca0a5f2aSJiri Pirko return err; 591ca0a5f2aSJiri Pirko } 592ca0a5f2aSJiri Pirko 593ca0a5f2aSJiri Pirko static void rocker_dma_cmd_ring_waits_free(const struct rocker *rocker) 594ca0a5f2aSJiri Pirko { 595ca0a5f2aSJiri Pirko const struct rocker_dma_ring_info *cmd_ring = &rocker->cmd_ring; 596ca0a5f2aSJiri Pirko int i; 597ca0a5f2aSJiri Pirko 598ca0a5f2aSJiri Pirko for (i = 0; i < cmd_ring->size; i++) 599ca0a5f2aSJiri Pirko rocker_dma_cmd_ring_wait_free(&cmd_ring->desc_info[i]); 600ca0a5f2aSJiri Pirko } 601ca0a5f2aSJiri Pirko 6024b8ac966SJiri Pirko static int rocker_dma_rings_init(struct rocker *rocker) 6034b8ac966SJiri Pirko { 604e5054643SSimon Horman const struct pci_dev *pdev = rocker->pdev; 6054b8ac966SJiri Pirko int err; 6064b8ac966SJiri Pirko 6074b8ac966SJiri Pirko err = rocker_dma_ring_create(rocker, ROCKER_DMA_CMD, 6084b8ac966SJiri Pirko ROCKER_DMA_CMD_DEFAULT_SIZE, 6094b8ac966SJiri Pirko &rocker->cmd_ring); 6104b8ac966SJiri Pirko if (err) { 6114b8ac966SJiri Pirko dev_err(&pdev->dev, "failed to create command dma ring\n"); 6124b8ac966SJiri Pirko return err; 6134b8ac966SJiri Pirko } 6144b8ac966SJiri Pirko 6154b8ac966SJiri Pirko spin_lock_init(&rocker->cmd_ring_lock); 6164b8ac966SJiri Pirko 6174b8ac966SJiri Pirko err = rocker_dma_ring_bufs_alloc(rocker, &rocker->cmd_ring, 6184b8ac966SJiri Pirko PCI_DMA_BIDIRECTIONAL, PAGE_SIZE); 6194b8ac966SJiri Pirko if (err) { 6204b8ac966SJiri Pirko dev_err(&pdev->dev, "failed to alloc command dma ring buffers\n"); 6214b8ac966SJiri Pirko goto err_dma_cmd_ring_bufs_alloc; 6224b8ac966SJiri Pirko } 6234b8ac966SJiri Pirko 624ca0a5f2aSJiri Pirko err = rocker_dma_cmd_ring_waits_alloc(rocker); 625ca0a5f2aSJiri Pirko if (err) { 626ca0a5f2aSJiri Pirko dev_err(&pdev->dev, "failed to alloc command dma ring waits\n"); 627ca0a5f2aSJiri Pirko goto err_dma_cmd_ring_waits_alloc; 628ca0a5f2aSJiri Pirko } 629ca0a5f2aSJiri Pirko 6304b8ac966SJiri Pirko err = rocker_dma_ring_create(rocker, ROCKER_DMA_EVENT, 6314b8ac966SJiri Pirko ROCKER_DMA_EVENT_DEFAULT_SIZE, 6324b8ac966SJiri Pirko &rocker->event_ring); 6334b8ac966SJiri Pirko if (err) { 6344b8ac966SJiri Pirko dev_err(&pdev->dev, "failed to create event dma ring\n"); 6354b8ac966SJiri Pirko goto err_dma_event_ring_create; 6364b8ac966SJiri Pirko } 6374b8ac966SJiri Pirko 6384b8ac966SJiri Pirko err = rocker_dma_ring_bufs_alloc(rocker, &rocker->event_ring, 6394b8ac966SJiri Pirko PCI_DMA_FROMDEVICE, PAGE_SIZE); 6404b8ac966SJiri Pirko if (err) { 6414b8ac966SJiri Pirko dev_err(&pdev->dev, "failed to alloc event dma ring buffers\n"); 6424b8ac966SJiri Pirko goto err_dma_event_ring_bufs_alloc; 6434b8ac966SJiri Pirko } 6444b8ac966SJiri Pirko rocker_dma_ring_pass_to_producer(rocker, &rocker->event_ring); 6454b8ac966SJiri Pirko return 0; 6464b8ac966SJiri Pirko 6474b8ac966SJiri Pirko err_dma_event_ring_bufs_alloc: 6484b8ac966SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker->event_ring); 6494b8ac966SJiri Pirko err_dma_event_ring_create: 6504b8ac966SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker->cmd_ring, 6514b8ac966SJiri Pirko PCI_DMA_BIDIRECTIONAL); 652ca0a5f2aSJiri Pirko err_dma_cmd_ring_waits_alloc: 653ca0a5f2aSJiri Pirko rocker_dma_cmd_ring_waits_free(rocker); 6544b8ac966SJiri Pirko err_dma_cmd_ring_bufs_alloc: 6554b8ac966SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker->cmd_ring); 6564b8ac966SJiri Pirko return err; 6574b8ac966SJiri Pirko } 6584b8ac966SJiri Pirko 6594b8ac966SJiri Pirko static void rocker_dma_rings_fini(struct rocker *rocker) 6604b8ac966SJiri Pirko { 6614b8ac966SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker->event_ring, 6624b8ac966SJiri Pirko PCI_DMA_BIDIRECTIONAL); 6634b8ac966SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker->event_ring); 664ca0a5f2aSJiri Pirko rocker_dma_cmd_ring_waits_free(rocker); 6654b8ac966SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker->cmd_ring, 6664b8ac966SJiri Pirko PCI_DMA_BIDIRECTIONAL); 6674b8ac966SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker->cmd_ring); 6684b8ac966SJiri Pirko } 6694b8ac966SJiri Pirko 670534ba6a8SSimon Horman static int rocker_dma_rx_ring_skb_map(const struct rocker_port *rocker_port, 6714b8ac966SJiri Pirko struct rocker_desc_info *desc_info, 6724b8ac966SJiri Pirko struct sk_buff *skb, size_t buf_len) 6734b8ac966SJiri Pirko { 674534ba6a8SSimon Horman const struct rocker *rocker = rocker_port->rocker; 6754b8ac966SJiri Pirko struct pci_dev *pdev = rocker->pdev; 6764b8ac966SJiri Pirko dma_addr_t dma_handle; 6774b8ac966SJiri Pirko 6784b8ac966SJiri Pirko dma_handle = pci_map_single(pdev, skb->data, buf_len, 6794b8ac966SJiri Pirko PCI_DMA_FROMDEVICE); 6804b8ac966SJiri Pirko if (pci_dma_mapping_error(pdev, dma_handle)) 6814b8ac966SJiri Pirko return -EIO; 6824b8ac966SJiri Pirko if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_RX_FRAG_ADDR, dma_handle)) 6834b8ac966SJiri Pirko goto tlv_put_failure; 6844b8ac966SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_RX_FRAG_MAX_LEN, buf_len)) 6854b8ac966SJiri Pirko goto tlv_put_failure; 6864b8ac966SJiri Pirko return 0; 6874b8ac966SJiri Pirko 6884b8ac966SJiri Pirko tlv_put_failure: 6894b8ac966SJiri Pirko pci_unmap_single(pdev, dma_handle, buf_len, PCI_DMA_FROMDEVICE); 6904b8ac966SJiri Pirko desc_info->tlv_size = 0; 6914b8ac966SJiri Pirko return -EMSGSIZE; 6924b8ac966SJiri Pirko } 6934b8ac966SJiri Pirko 694e5054643SSimon Horman static size_t rocker_port_rx_buf_len(const struct rocker_port *rocker_port) 6954b8ac966SJiri Pirko { 6964b8ac966SJiri Pirko return rocker_port->dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; 6974b8ac966SJiri Pirko } 6984b8ac966SJiri Pirko 699534ba6a8SSimon Horman static int rocker_dma_rx_ring_skb_alloc(const struct rocker_port *rocker_port, 7004b8ac966SJiri Pirko struct rocker_desc_info *desc_info) 7014b8ac966SJiri Pirko { 7024b8ac966SJiri Pirko struct net_device *dev = rocker_port->dev; 7034b8ac966SJiri Pirko struct sk_buff *skb; 7044b8ac966SJiri Pirko size_t buf_len = rocker_port_rx_buf_len(rocker_port); 7054b8ac966SJiri Pirko int err; 7064b8ac966SJiri Pirko 7074b8ac966SJiri Pirko /* Ensure that hw will see tlv_size zero in case of an error. 7084b8ac966SJiri Pirko * That tells hw to use another descriptor. 7094b8ac966SJiri Pirko */ 7104b8ac966SJiri Pirko rocker_desc_cookie_ptr_set(desc_info, NULL); 7114b8ac966SJiri Pirko desc_info->tlv_size = 0; 7124b8ac966SJiri Pirko 7134b8ac966SJiri Pirko skb = netdev_alloc_skb_ip_align(dev, buf_len); 7144b8ac966SJiri Pirko if (!skb) 7154b8ac966SJiri Pirko return -ENOMEM; 716534ba6a8SSimon Horman err = rocker_dma_rx_ring_skb_map(rocker_port, desc_info, skb, buf_len); 7174b8ac966SJiri Pirko if (err) { 7184b8ac966SJiri Pirko dev_kfree_skb_any(skb); 7194b8ac966SJiri Pirko return err; 7204b8ac966SJiri Pirko } 7214b8ac966SJiri Pirko rocker_desc_cookie_ptr_set(desc_info, skb); 7224b8ac966SJiri Pirko return 0; 7234b8ac966SJiri Pirko } 7244b8ac966SJiri Pirko 725e5054643SSimon Horman static void rocker_dma_rx_ring_skb_unmap(const struct rocker *rocker, 726e5054643SSimon Horman const struct rocker_tlv **attrs) 7274b8ac966SJiri Pirko { 7284b8ac966SJiri Pirko struct pci_dev *pdev = rocker->pdev; 7294b8ac966SJiri Pirko dma_addr_t dma_handle; 7304b8ac966SJiri Pirko size_t len; 7314b8ac966SJiri Pirko 7324b8ac966SJiri Pirko if (!attrs[ROCKER_TLV_RX_FRAG_ADDR] || 7334b8ac966SJiri Pirko !attrs[ROCKER_TLV_RX_FRAG_MAX_LEN]) 7344b8ac966SJiri Pirko return; 7354b8ac966SJiri Pirko dma_handle = rocker_tlv_get_u64(attrs[ROCKER_TLV_RX_FRAG_ADDR]); 7364b8ac966SJiri Pirko len = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FRAG_MAX_LEN]); 7374b8ac966SJiri Pirko pci_unmap_single(pdev, dma_handle, len, PCI_DMA_FROMDEVICE); 7384b8ac966SJiri Pirko } 7394b8ac966SJiri Pirko 740e5054643SSimon Horman static void rocker_dma_rx_ring_skb_free(const struct rocker *rocker, 741e5054643SSimon Horman const struct rocker_desc_info *desc_info) 7424b8ac966SJiri Pirko { 743e5054643SSimon Horman const struct rocker_tlv *attrs[ROCKER_TLV_RX_MAX + 1]; 7444b8ac966SJiri Pirko struct sk_buff *skb = rocker_desc_cookie_ptr_get(desc_info); 7454b8ac966SJiri Pirko 7464b8ac966SJiri Pirko if (!skb) 7474b8ac966SJiri Pirko return; 7484b8ac966SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_RX_MAX, desc_info); 7494b8ac966SJiri Pirko rocker_dma_rx_ring_skb_unmap(rocker, attrs); 7504b8ac966SJiri Pirko dev_kfree_skb_any(skb); 7514b8ac966SJiri Pirko } 7524b8ac966SJiri Pirko 753534ba6a8SSimon Horman static int rocker_dma_rx_ring_skbs_alloc(const struct rocker_port *rocker_port) 7544b8ac966SJiri Pirko { 755e5054643SSimon Horman const struct rocker_dma_ring_info *rx_ring = &rocker_port->rx_ring; 756534ba6a8SSimon Horman const struct rocker *rocker = rocker_port->rocker; 7574b8ac966SJiri Pirko int i; 7584b8ac966SJiri Pirko int err; 7594b8ac966SJiri Pirko 7604b8ac966SJiri Pirko for (i = 0; i < rx_ring->size; i++) { 761534ba6a8SSimon Horman err = rocker_dma_rx_ring_skb_alloc(rocker_port, 7624b8ac966SJiri Pirko &rx_ring->desc_info[i]); 7634b8ac966SJiri Pirko if (err) 7644b8ac966SJiri Pirko goto rollback; 7654b8ac966SJiri Pirko } 7664b8ac966SJiri Pirko return 0; 7674b8ac966SJiri Pirko 7684b8ac966SJiri Pirko rollback: 7694b8ac966SJiri Pirko for (i--; i >= 0; i--) 7704b8ac966SJiri Pirko rocker_dma_rx_ring_skb_free(rocker, &rx_ring->desc_info[i]); 7714b8ac966SJiri Pirko return err; 7724b8ac966SJiri Pirko } 7734b8ac966SJiri Pirko 774534ba6a8SSimon Horman static void rocker_dma_rx_ring_skbs_free(const struct rocker_port *rocker_port) 7754b8ac966SJiri Pirko { 776e5054643SSimon Horman const struct rocker_dma_ring_info *rx_ring = &rocker_port->rx_ring; 777534ba6a8SSimon Horman const struct rocker *rocker = rocker_port->rocker; 7784b8ac966SJiri Pirko int i; 7794b8ac966SJiri Pirko 7804b8ac966SJiri Pirko for (i = 0; i < rx_ring->size; i++) 7814b8ac966SJiri Pirko rocker_dma_rx_ring_skb_free(rocker, &rx_ring->desc_info[i]); 7824b8ac966SJiri Pirko } 7834b8ac966SJiri Pirko 7844b8ac966SJiri Pirko static int rocker_port_dma_rings_init(struct rocker_port *rocker_port) 7854b8ac966SJiri Pirko { 7864b8ac966SJiri Pirko struct rocker *rocker = rocker_port->rocker; 7874b8ac966SJiri Pirko int err; 7884b8ac966SJiri Pirko 7894b8ac966SJiri Pirko err = rocker_dma_ring_create(rocker, 7904b8ac966SJiri Pirko ROCKER_DMA_TX(rocker_port->port_number), 7914b8ac966SJiri Pirko ROCKER_DMA_TX_DEFAULT_SIZE, 7924b8ac966SJiri Pirko &rocker_port->tx_ring); 7934b8ac966SJiri Pirko if (err) { 7944b8ac966SJiri Pirko netdev_err(rocker_port->dev, "failed to create tx dma ring\n"); 7954b8ac966SJiri Pirko return err; 7964b8ac966SJiri Pirko } 7974b8ac966SJiri Pirko 7984b8ac966SJiri Pirko err = rocker_dma_ring_bufs_alloc(rocker, &rocker_port->tx_ring, 7994b8ac966SJiri Pirko PCI_DMA_TODEVICE, 8004b8ac966SJiri Pirko ROCKER_DMA_TX_DESC_SIZE); 8014b8ac966SJiri Pirko if (err) { 8024b8ac966SJiri Pirko netdev_err(rocker_port->dev, "failed to alloc tx dma ring buffers\n"); 8034b8ac966SJiri Pirko goto err_dma_tx_ring_bufs_alloc; 8044b8ac966SJiri Pirko } 8054b8ac966SJiri Pirko 8064b8ac966SJiri Pirko err = rocker_dma_ring_create(rocker, 8074b8ac966SJiri Pirko ROCKER_DMA_RX(rocker_port->port_number), 8084b8ac966SJiri Pirko ROCKER_DMA_RX_DEFAULT_SIZE, 8094b8ac966SJiri Pirko &rocker_port->rx_ring); 8104b8ac966SJiri Pirko if (err) { 8114b8ac966SJiri Pirko netdev_err(rocker_port->dev, "failed to create rx dma ring\n"); 8124b8ac966SJiri Pirko goto err_dma_rx_ring_create; 8134b8ac966SJiri Pirko } 8144b8ac966SJiri Pirko 8154b8ac966SJiri Pirko err = rocker_dma_ring_bufs_alloc(rocker, &rocker_port->rx_ring, 8164b8ac966SJiri Pirko PCI_DMA_BIDIRECTIONAL, 8174b8ac966SJiri Pirko ROCKER_DMA_RX_DESC_SIZE); 8184b8ac966SJiri Pirko if (err) { 8194b8ac966SJiri Pirko netdev_err(rocker_port->dev, "failed to alloc rx dma ring buffers\n"); 8204b8ac966SJiri Pirko goto err_dma_rx_ring_bufs_alloc; 8214b8ac966SJiri Pirko } 8224b8ac966SJiri Pirko 823534ba6a8SSimon Horman err = rocker_dma_rx_ring_skbs_alloc(rocker_port); 8244b8ac966SJiri Pirko if (err) { 8254b8ac966SJiri Pirko netdev_err(rocker_port->dev, "failed to alloc rx dma ring skbs\n"); 8264b8ac966SJiri Pirko goto err_dma_rx_ring_skbs_alloc; 8274b8ac966SJiri Pirko } 8284b8ac966SJiri Pirko rocker_dma_ring_pass_to_producer(rocker, &rocker_port->rx_ring); 8294b8ac966SJiri Pirko 8304b8ac966SJiri Pirko return 0; 8314b8ac966SJiri Pirko 8324b8ac966SJiri Pirko err_dma_rx_ring_skbs_alloc: 8334b8ac966SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker_port->rx_ring, 8344b8ac966SJiri Pirko PCI_DMA_BIDIRECTIONAL); 8354b8ac966SJiri Pirko err_dma_rx_ring_bufs_alloc: 8364b8ac966SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker_port->rx_ring); 8374b8ac966SJiri Pirko err_dma_rx_ring_create: 8384b8ac966SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker_port->tx_ring, 8394b8ac966SJiri Pirko PCI_DMA_TODEVICE); 8404b8ac966SJiri Pirko err_dma_tx_ring_bufs_alloc: 8414b8ac966SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker_port->tx_ring); 8424b8ac966SJiri Pirko return err; 8434b8ac966SJiri Pirko } 8444b8ac966SJiri Pirko 8454b8ac966SJiri Pirko static void rocker_port_dma_rings_fini(struct rocker_port *rocker_port) 8464b8ac966SJiri Pirko { 8474b8ac966SJiri Pirko struct rocker *rocker = rocker_port->rocker; 8484b8ac966SJiri Pirko 849534ba6a8SSimon Horman rocker_dma_rx_ring_skbs_free(rocker_port); 8504b8ac966SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker_port->rx_ring, 8514b8ac966SJiri Pirko PCI_DMA_BIDIRECTIONAL); 8524b8ac966SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker_port->rx_ring); 8534b8ac966SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker_port->tx_ring, 8544b8ac966SJiri Pirko PCI_DMA_TODEVICE); 8554b8ac966SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker_port->tx_ring); 8564b8ac966SJiri Pirko } 8574b8ac966SJiri Pirko 858e5054643SSimon Horman static void rocker_port_set_enable(const struct rocker_port *rocker_port, 859e5054643SSimon Horman bool enable) 8604b8ac966SJiri Pirko { 8614b8ac966SJiri Pirko u64 val = rocker_read64(rocker_port->rocker, PORT_PHYS_ENABLE); 8624b8ac966SJiri Pirko 8634b8ac966SJiri Pirko if (enable) 86471a83a6dSDavid S. Miller val |= 1ULL << rocker_port->pport; 8654b8ac966SJiri Pirko else 86671a83a6dSDavid S. Miller val &= ~(1ULL << rocker_port->pport); 8674b8ac966SJiri Pirko rocker_write64(rocker_port->rocker, PORT_PHYS_ENABLE, val); 8684b8ac966SJiri Pirko } 8694b8ac966SJiri Pirko 8704b8ac966SJiri Pirko /******************************** 8714b8ac966SJiri Pirko * Interrupt handler and helpers 8724b8ac966SJiri Pirko ********************************/ 8734b8ac966SJiri Pirko 8744b8ac966SJiri Pirko static irqreturn_t rocker_cmd_irq_handler(int irq, void *dev_id) 8754b8ac966SJiri Pirko { 8764b8ac966SJiri Pirko struct rocker *rocker = dev_id; 877e5054643SSimon Horman const struct rocker_desc_info *desc_info; 8784b8ac966SJiri Pirko struct rocker_wait *wait; 8794b8ac966SJiri Pirko u32 credits = 0; 8804b8ac966SJiri Pirko 8814b8ac966SJiri Pirko spin_lock(&rocker->cmd_ring_lock); 8824b8ac966SJiri Pirko while ((desc_info = rocker_desc_tail_get(&rocker->cmd_ring))) { 8834b8ac966SJiri Pirko wait = rocker_desc_cookie_ptr_get(desc_info); 884179f9a25SScott Feldman if (wait->nowait) { 885179f9a25SScott Feldman rocker_desc_gen_clear(desc_info); 886179f9a25SScott Feldman } else { 8874b8ac966SJiri Pirko rocker_wait_wake_up(wait); 888179f9a25SScott Feldman } 8894b8ac966SJiri Pirko credits++; 8904b8ac966SJiri Pirko } 8914b8ac966SJiri Pirko spin_unlock(&rocker->cmd_ring_lock); 8924b8ac966SJiri Pirko rocker_dma_ring_credits_set(rocker, &rocker->cmd_ring, credits); 8934b8ac966SJiri Pirko 8944b8ac966SJiri Pirko return IRQ_HANDLED; 8954b8ac966SJiri Pirko } 8964b8ac966SJiri Pirko 897e5054643SSimon Horman static void rocker_port_link_up(const struct rocker_port *rocker_port) 8984b8ac966SJiri Pirko { 8994b8ac966SJiri Pirko netif_carrier_on(rocker_port->dev); 9004b8ac966SJiri Pirko netdev_info(rocker_port->dev, "Link is up\n"); 9014b8ac966SJiri Pirko } 9024b8ac966SJiri Pirko 903e5054643SSimon Horman static void rocker_port_link_down(const struct rocker_port *rocker_port) 9044b8ac966SJiri Pirko { 9054b8ac966SJiri Pirko netif_carrier_off(rocker_port->dev); 9064b8ac966SJiri Pirko netdev_info(rocker_port->dev, "Link is down\n"); 9074b8ac966SJiri Pirko } 9084b8ac966SJiri Pirko 909e5054643SSimon Horman static int rocker_event_link_change(const struct rocker *rocker, 9104b8ac966SJiri Pirko const struct rocker_tlv *info) 9114b8ac966SJiri Pirko { 912e5054643SSimon Horman const struct rocker_tlv *attrs[ROCKER_TLV_EVENT_LINK_CHANGED_MAX + 1]; 9134b8ac966SJiri Pirko unsigned int port_number; 9144b8ac966SJiri Pirko bool link_up; 9154b8ac966SJiri Pirko struct rocker_port *rocker_port; 9164b8ac966SJiri Pirko 9174b8ac966SJiri Pirko rocker_tlv_parse_nested(attrs, ROCKER_TLV_EVENT_LINK_CHANGED_MAX, info); 9184a6bb6d3SScott Feldman if (!attrs[ROCKER_TLV_EVENT_LINK_CHANGED_PPORT] || 9194b8ac966SJiri Pirko !attrs[ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP]) 9204b8ac966SJiri Pirko return -EIO; 9214b8ac966SJiri Pirko port_number = 9224a6bb6d3SScott Feldman rocker_tlv_get_u32(attrs[ROCKER_TLV_EVENT_LINK_CHANGED_PPORT]) - 1; 9234b8ac966SJiri Pirko link_up = rocker_tlv_get_u8(attrs[ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP]); 9244b8ac966SJiri Pirko 9254b8ac966SJiri Pirko if (port_number >= rocker->port_count) 9264b8ac966SJiri Pirko return -EINVAL; 9274b8ac966SJiri Pirko 9284b8ac966SJiri Pirko rocker_port = rocker->ports[port_number]; 9294b8ac966SJiri Pirko if (netif_carrier_ok(rocker_port->dev) != link_up) { 9304b8ac966SJiri Pirko if (link_up) 9314b8ac966SJiri Pirko rocker_port_link_up(rocker_port); 9324b8ac966SJiri Pirko else 9334b8ac966SJiri Pirko rocker_port_link_down(rocker_port); 9344b8ac966SJiri Pirko } 9354b8ac966SJiri Pirko 9364b8ac966SJiri Pirko return 0; 9374b8ac966SJiri Pirko } 9384b8ac966SJiri Pirko 939e420114eSJiri Pirko static int rocker_world_port_ev_mac_vlan_seen(struct rocker_port *rocker_port, 940e420114eSJiri Pirko const unsigned char *addr, 941e420114eSJiri Pirko __be16 vlan_id); 9426c707945SScott Feldman 943e5054643SSimon Horman static int rocker_event_mac_vlan_seen(const struct rocker *rocker, 9446c707945SScott Feldman const struct rocker_tlv *info) 9456c707945SScott Feldman { 946e5054643SSimon Horman const struct rocker_tlv *attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAX + 1]; 9476c707945SScott Feldman unsigned int port_number; 9486c707945SScott Feldman struct rocker_port *rocker_port; 949e5054643SSimon Horman const unsigned char *addr; 9506c707945SScott Feldman __be16 vlan_id; 9516c707945SScott Feldman 9526c707945SScott Feldman rocker_tlv_parse_nested(attrs, ROCKER_TLV_EVENT_MAC_VLAN_MAX, info); 9534a6bb6d3SScott Feldman if (!attrs[ROCKER_TLV_EVENT_MAC_VLAN_PPORT] || 9546c707945SScott Feldman !attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAC] || 9556c707945SScott Feldman !attrs[ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID]) 9566c707945SScott Feldman return -EIO; 9576c707945SScott Feldman port_number = 9584a6bb6d3SScott Feldman rocker_tlv_get_u32(attrs[ROCKER_TLV_EVENT_MAC_VLAN_PPORT]) - 1; 9596c707945SScott Feldman addr = rocker_tlv_data(attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAC]); 9609b03c71fSJiri Pirko vlan_id = rocker_tlv_get_be16(attrs[ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID]); 9616c707945SScott Feldman 9626c707945SScott Feldman if (port_number >= rocker->port_count) 9636c707945SScott Feldman return -EINVAL; 9646c707945SScott Feldman 9656c707945SScott Feldman rocker_port = rocker->ports[port_number]; 9663fbcdbf3SJiri Pirko return rocker_world_port_ev_mac_vlan_seen(rocker_port, addr, vlan_id); 9676c707945SScott Feldman } 9689f6bbf7cSScott Feldman 969e5054643SSimon Horman static int rocker_event_process(const struct rocker *rocker, 970e5054643SSimon Horman const struct rocker_desc_info *desc_info) 9714b8ac966SJiri Pirko { 972e5054643SSimon Horman const struct rocker_tlv *attrs[ROCKER_TLV_EVENT_MAX + 1]; 973e5054643SSimon Horman const struct rocker_tlv *info; 9744b8ac966SJiri Pirko u16 type; 9754b8ac966SJiri Pirko 9764b8ac966SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_EVENT_MAX, desc_info); 9774b8ac966SJiri Pirko if (!attrs[ROCKER_TLV_EVENT_TYPE] || 9784b8ac966SJiri Pirko !attrs[ROCKER_TLV_EVENT_INFO]) 9794b8ac966SJiri Pirko return -EIO; 9804b8ac966SJiri Pirko 9814b8ac966SJiri Pirko type = rocker_tlv_get_u16(attrs[ROCKER_TLV_EVENT_TYPE]); 9824b8ac966SJiri Pirko info = attrs[ROCKER_TLV_EVENT_INFO]; 9834b8ac966SJiri Pirko 9844b8ac966SJiri Pirko switch (type) { 9854b8ac966SJiri Pirko case ROCKER_TLV_EVENT_TYPE_LINK_CHANGED: 9864b8ac966SJiri Pirko return rocker_event_link_change(rocker, info); 9876c707945SScott Feldman case ROCKER_TLV_EVENT_TYPE_MAC_VLAN_SEEN: 9886c707945SScott Feldman return rocker_event_mac_vlan_seen(rocker, info); 9894b8ac966SJiri Pirko } 9904b8ac966SJiri Pirko 9914b8ac966SJiri Pirko return -EOPNOTSUPP; 9924b8ac966SJiri Pirko } 9934b8ac966SJiri Pirko 9944b8ac966SJiri Pirko static irqreturn_t rocker_event_irq_handler(int irq, void *dev_id) 9954b8ac966SJiri Pirko { 9964b8ac966SJiri Pirko struct rocker *rocker = dev_id; 997e5054643SSimon Horman const struct pci_dev *pdev = rocker->pdev; 998e5054643SSimon Horman const struct rocker_desc_info *desc_info; 9994b8ac966SJiri Pirko u32 credits = 0; 10004b8ac966SJiri Pirko int err; 10014b8ac966SJiri Pirko 10024b8ac966SJiri Pirko while ((desc_info = rocker_desc_tail_get(&rocker->event_ring))) { 10034b8ac966SJiri Pirko err = rocker_desc_err(desc_info); 10044b8ac966SJiri Pirko if (err) { 10054b8ac966SJiri Pirko dev_err(&pdev->dev, "event desc received with err %d\n", 10064b8ac966SJiri Pirko err); 10074b8ac966SJiri Pirko } else { 10084b8ac966SJiri Pirko err = rocker_event_process(rocker, desc_info); 10094b8ac966SJiri Pirko if (err) 10104b8ac966SJiri Pirko dev_err(&pdev->dev, "event processing failed with err %d\n", 10114b8ac966SJiri Pirko err); 10124b8ac966SJiri Pirko } 10134b8ac966SJiri Pirko rocker_desc_gen_clear(desc_info); 10144b8ac966SJiri Pirko rocker_desc_head_set(rocker, &rocker->event_ring, desc_info); 10154b8ac966SJiri Pirko credits++; 10164b8ac966SJiri Pirko } 10174b8ac966SJiri Pirko rocker_dma_ring_credits_set(rocker, &rocker->event_ring, credits); 10184b8ac966SJiri Pirko 10194b8ac966SJiri Pirko return IRQ_HANDLED; 10204b8ac966SJiri Pirko } 10214b8ac966SJiri Pirko 10224b8ac966SJiri Pirko static irqreturn_t rocker_tx_irq_handler(int irq, void *dev_id) 10234b8ac966SJiri Pirko { 10244b8ac966SJiri Pirko struct rocker_port *rocker_port = dev_id; 10254b8ac966SJiri Pirko 10264b8ac966SJiri Pirko napi_schedule(&rocker_port->napi_tx); 10274b8ac966SJiri Pirko return IRQ_HANDLED; 10284b8ac966SJiri Pirko } 10294b8ac966SJiri Pirko 10304b8ac966SJiri Pirko static irqreturn_t rocker_rx_irq_handler(int irq, void *dev_id) 10314b8ac966SJiri Pirko { 10324b8ac966SJiri Pirko struct rocker_port *rocker_port = dev_id; 10334b8ac966SJiri Pirko 10344b8ac966SJiri Pirko napi_schedule(&rocker_port->napi_rx); 10354b8ac966SJiri Pirko return IRQ_HANDLED; 10364b8ac966SJiri Pirko } 10374b8ac966SJiri Pirko 10384b8ac966SJiri Pirko /******************** 10394b8ac966SJiri Pirko * Command interface 10404b8ac966SJiri Pirko ********************/ 10414b8ac966SJiri Pirko 10423fbcdbf3SJiri Pirko int rocker_cmd_exec(struct rocker_port *rocker_port, bool nowait, 1043e5054643SSimon Horman rocker_cmd_prep_cb_t prepare, void *prepare_priv, 1044e5054643SSimon Horman rocker_cmd_proc_cb_t process, void *process_priv) 10454b8ac966SJiri Pirko { 1046534ba6a8SSimon Horman struct rocker *rocker = rocker_port->rocker; 10474b8ac966SJiri Pirko struct rocker_desc_info *desc_info; 10484b8ac966SJiri Pirko struct rocker_wait *wait; 1049179f9a25SScott Feldman unsigned long lock_flags; 10504b8ac966SJiri Pirko int err; 10514b8ac966SJiri Pirko 1052179f9a25SScott Feldman spin_lock_irqsave(&rocker->cmd_ring_lock, lock_flags); 1053c4f20321SScott Feldman 10544b8ac966SJiri Pirko desc_info = rocker_desc_head_get(&rocker->cmd_ring); 10554b8ac966SJiri Pirko if (!desc_info) { 1056179f9a25SScott Feldman spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags); 1057ca0a5f2aSJiri Pirko return -EAGAIN; 10584b8ac966SJiri Pirko } 1059c4f20321SScott Feldman 1060ca0a5f2aSJiri Pirko wait = rocker_desc_cookie_ptr_get(desc_info); 1061ca0a5f2aSJiri Pirko rocker_wait_init(wait); 1062ca0a5f2aSJiri Pirko wait->nowait = nowait; 1063ca0a5f2aSJiri Pirko 1064534ba6a8SSimon Horman err = prepare(rocker_port, desc_info, prepare_priv); 10654b8ac966SJiri Pirko if (err) { 1066179f9a25SScott Feldman spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags); 1067ca0a5f2aSJiri Pirko return err; 10684b8ac966SJiri Pirko } 1069c4f20321SScott Feldman 10704b8ac966SJiri Pirko rocker_desc_head_set(rocker, &rocker->cmd_ring, desc_info); 1071c4f20321SScott Feldman 1072179f9a25SScott Feldman spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags); 1073179f9a25SScott Feldman 1074179f9a25SScott Feldman if (nowait) 1075179f9a25SScott Feldman return 0; 10764b8ac966SJiri Pirko 10774b8ac966SJiri Pirko if (!rocker_wait_event_timeout(wait, HZ / 10)) 10784b8ac966SJiri Pirko return -EIO; 10794b8ac966SJiri Pirko 10804b8ac966SJiri Pirko err = rocker_desc_err(desc_info); 10814b8ac966SJiri Pirko if (err) 10824b8ac966SJiri Pirko return err; 10834b8ac966SJiri Pirko 10844b8ac966SJiri Pirko if (process) 1085534ba6a8SSimon Horman err = process(rocker_port, desc_info, process_priv); 10864b8ac966SJiri Pirko 10874b8ac966SJiri Pirko rocker_desc_gen_clear(desc_info); 10884b8ac966SJiri Pirko return err; 10894b8ac966SJiri Pirko } 10904b8ac966SJiri Pirko 10914b8ac966SJiri Pirko static int 1092534ba6a8SSimon Horman rocker_cmd_get_port_settings_prep(const struct rocker_port *rocker_port, 10934b8ac966SJiri Pirko struct rocker_desc_info *desc_info, 10944b8ac966SJiri Pirko void *priv) 10954b8ac966SJiri Pirko { 10964b8ac966SJiri Pirko struct rocker_tlv *cmd_info; 10974b8ac966SJiri Pirko 10984b8ac966SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, 10994b8ac966SJiri Pirko ROCKER_TLV_CMD_TYPE_GET_PORT_SETTINGS)) 11004b8ac966SJiri Pirko return -EMSGSIZE; 11014b8ac966SJiri Pirko cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 11024b8ac966SJiri Pirko if (!cmd_info) 11034b8ac966SJiri Pirko return -EMSGSIZE; 11044a6bb6d3SScott Feldman if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, 11054a6bb6d3SScott Feldman rocker_port->pport)) 11064b8ac966SJiri Pirko return -EMSGSIZE; 11074b8ac966SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_info); 11084b8ac966SJiri Pirko return 0; 11094b8ac966SJiri Pirko } 11104b8ac966SJiri Pirko 11114b8ac966SJiri Pirko static int 1112534ba6a8SSimon Horman rocker_cmd_get_port_settings_ethtool_proc(const struct rocker_port *rocker_port, 1113e5054643SSimon Horman const struct rocker_desc_info *desc_info, 11144b8ac966SJiri Pirko void *priv) 11154b8ac966SJiri Pirko { 1116de480150SPhilippe Reynes struct ethtool_link_ksettings *ecmd = priv; 1117e5054643SSimon Horman const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1]; 1118e5054643SSimon Horman const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1]; 11194b8ac966SJiri Pirko u32 speed; 11204b8ac966SJiri Pirko u8 duplex; 11214b8ac966SJiri Pirko u8 autoneg; 11224b8ac966SJiri Pirko 11234b8ac966SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info); 11244b8ac966SJiri Pirko if (!attrs[ROCKER_TLV_CMD_INFO]) 11254b8ac966SJiri Pirko return -EIO; 11264b8ac966SJiri Pirko 11274b8ac966SJiri Pirko rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX, 11284b8ac966SJiri Pirko attrs[ROCKER_TLV_CMD_INFO]); 11294b8ac966SJiri Pirko if (!info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED] || 11304b8ac966SJiri Pirko !info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX] || 11314b8ac966SJiri Pirko !info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]) 11324b8ac966SJiri Pirko return -EIO; 11334b8ac966SJiri Pirko 11344b8ac966SJiri Pirko speed = rocker_tlv_get_u32(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED]); 11354b8ac966SJiri Pirko duplex = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX]); 11364b8ac966SJiri Pirko autoneg = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]); 11374b8ac966SJiri Pirko 1138de480150SPhilippe Reynes ethtool_link_ksettings_zero_link_mode(ecmd, supported); 1139de480150SPhilippe Reynes ethtool_link_ksettings_add_link_mode(ecmd, supported, TP); 1140de480150SPhilippe Reynes 1141de480150SPhilippe Reynes ecmd->base.phy_address = 0xff; 1142de480150SPhilippe Reynes ecmd->base.port = PORT_TP; 1143de480150SPhilippe Reynes ecmd->base.speed = speed; 1144de480150SPhilippe Reynes ecmd->base.duplex = duplex ? DUPLEX_FULL : DUPLEX_HALF; 1145de480150SPhilippe Reynes ecmd->base.autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; 11464b8ac966SJiri Pirko 11474b8ac966SJiri Pirko return 0; 11484b8ac966SJiri Pirko } 11494b8ac966SJiri Pirko 11504b8ac966SJiri Pirko static int 1151534ba6a8SSimon Horman rocker_cmd_get_port_settings_macaddr_proc(const struct rocker_port *rocker_port, 1152e5054643SSimon Horman const struct rocker_desc_info *desc_info, 11534b8ac966SJiri Pirko void *priv) 11544b8ac966SJiri Pirko { 11554b8ac966SJiri Pirko unsigned char *macaddr = priv; 1156e5054643SSimon Horman const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1]; 1157e5054643SSimon Horman const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1]; 1158e5054643SSimon Horman const struct rocker_tlv *attr; 11594b8ac966SJiri Pirko 11604b8ac966SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info); 11614b8ac966SJiri Pirko if (!attrs[ROCKER_TLV_CMD_INFO]) 11624b8ac966SJiri Pirko return -EIO; 11634b8ac966SJiri Pirko 11644b8ac966SJiri Pirko rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX, 11654b8ac966SJiri Pirko attrs[ROCKER_TLV_CMD_INFO]); 11664b8ac966SJiri Pirko attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]; 11674b8ac966SJiri Pirko if (!attr) 11684b8ac966SJiri Pirko return -EIO; 11694b8ac966SJiri Pirko 11704b8ac966SJiri Pirko if (rocker_tlv_len(attr) != ETH_ALEN) 11714b8ac966SJiri Pirko return -EINVAL; 11724b8ac966SJiri Pirko 11734b8ac966SJiri Pirko ether_addr_copy(macaddr, rocker_tlv_data(attr)); 11744b8ac966SJiri Pirko return 0; 11754b8ac966SJiri Pirko } 11764b8ac966SJiri Pirko 1177e1ba3deeSJiri Pirko static int 1178e1ba3deeSJiri Pirko rocker_cmd_get_port_settings_mode_proc(const struct rocker_port *rocker_port, 1179e1ba3deeSJiri Pirko const struct rocker_desc_info *desc_info, 1180e1ba3deeSJiri Pirko void *priv) 1181e1ba3deeSJiri Pirko { 1182e1ba3deeSJiri Pirko u8 *p_mode = priv; 1183e1ba3deeSJiri Pirko const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1]; 1184e1ba3deeSJiri Pirko const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1]; 1185e1ba3deeSJiri Pirko const struct rocker_tlv *attr; 1186e1ba3deeSJiri Pirko 1187e1ba3deeSJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info); 1188e1ba3deeSJiri Pirko if (!attrs[ROCKER_TLV_CMD_INFO]) 1189e1ba3deeSJiri Pirko return -EIO; 1190e1ba3deeSJiri Pirko 1191e1ba3deeSJiri Pirko rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX, 1192e1ba3deeSJiri Pirko attrs[ROCKER_TLV_CMD_INFO]); 1193e1ba3deeSJiri Pirko attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]; 1194e1ba3deeSJiri Pirko if (!attr) 1195e1ba3deeSJiri Pirko return -EIO; 1196e1ba3deeSJiri Pirko 1197e1ba3deeSJiri Pirko *p_mode = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]); 1198e1ba3deeSJiri Pirko return 0; 1199e1ba3deeSJiri Pirko } 1200e1ba3deeSJiri Pirko 1201db19170bSDavid Ahern struct port_name { 1202db19170bSDavid Ahern char *buf; 1203db19170bSDavid Ahern size_t len; 1204db19170bSDavid Ahern }; 1205db19170bSDavid Ahern 1206db19170bSDavid Ahern static int 1207534ba6a8SSimon Horman rocker_cmd_get_port_settings_phys_name_proc(const struct rocker_port *rocker_port, 1208e5054643SSimon Horman const struct rocker_desc_info *desc_info, 1209db19170bSDavid Ahern void *priv) 1210db19170bSDavid Ahern { 1211e5054643SSimon Horman const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1]; 1212e5054643SSimon Horman const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1]; 1213db19170bSDavid Ahern struct port_name *name = priv; 1214e5054643SSimon Horman const struct rocker_tlv *attr; 1215db19170bSDavid Ahern size_t i, j, len; 1216e5054643SSimon Horman const char *str; 1217db19170bSDavid Ahern 1218db19170bSDavid Ahern rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info); 1219db19170bSDavid Ahern if (!attrs[ROCKER_TLV_CMD_INFO]) 1220db19170bSDavid Ahern return -EIO; 1221db19170bSDavid Ahern 1222db19170bSDavid Ahern rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX, 1223db19170bSDavid Ahern attrs[ROCKER_TLV_CMD_INFO]); 1224db19170bSDavid Ahern attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME]; 1225db19170bSDavid Ahern if (!attr) 1226db19170bSDavid Ahern return -EIO; 1227db19170bSDavid Ahern 1228db19170bSDavid Ahern len = min_t(size_t, rocker_tlv_len(attr), name->len); 1229db19170bSDavid Ahern str = rocker_tlv_data(attr); 1230db19170bSDavid Ahern 1231db19170bSDavid Ahern /* make sure name only contains alphanumeric characters */ 1232db19170bSDavid Ahern for (i = j = 0; i < len; ++i) { 1233db19170bSDavid Ahern if (isalnum(str[i])) { 1234db19170bSDavid Ahern name->buf[j] = str[i]; 1235db19170bSDavid Ahern j++; 1236db19170bSDavid Ahern } 1237db19170bSDavid Ahern } 1238db19170bSDavid Ahern 1239db19170bSDavid Ahern if (j == 0) 1240db19170bSDavid Ahern return -EIO; 1241db19170bSDavid Ahern 1242db19170bSDavid Ahern name->buf[j] = '\0'; 1243db19170bSDavid Ahern 1244db19170bSDavid Ahern return 0; 1245db19170bSDavid Ahern } 1246db19170bSDavid Ahern 12474b8ac966SJiri Pirko static int 1248534ba6a8SSimon Horman rocker_cmd_set_port_settings_ethtool_prep(const struct rocker_port *rocker_port, 12494b8ac966SJiri Pirko struct rocker_desc_info *desc_info, 12504b8ac966SJiri Pirko void *priv) 12514b8ac966SJiri Pirko { 1252de480150SPhilippe Reynes struct ethtool_link_ksettings *ecmd = priv; 12534b8ac966SJiri Pirko struct rocker_tlv *cmd_info; 12544b8ac966SJiri Pirko 12554b8ac966SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, 12564b8ac966SJiri Pirko ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS)) 12574b8ac966SJiri Pirko return -EMSGSIZE; 12584b8ac966SJiri Pirko cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 12594b8ac966SJiri Pirko if (!cmd_info) 12604b8ac966SJiri Pirko return -EMSGSIZE; 12614a6bb6d3SScott Feldman if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, 12624a6bb6d3SScott Feldman rocker_port->pport)) 12634b8ac966SJiri Pirko return -EMSGSIZE; 12644b8ac966SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_SPEED, 1265de480150SPhilippe Reynes ecmd->base.speed)) 12664b8ac966SJiri Pirko return -EMSGSIZE; 12674b8ac966SJiri Pirko if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX, 1268de480150SPhilippe Reynes ecmd->base.duplex)) 12694b8ac966SJiri Pirko return -EMSGSIZE; 12704b8ac966SJiri Pirko if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG, 1271de480150SPhilippe Reynes ecmd->base.autoneg)) 12724b8ac966SJiri Pirko return -EMSGSIZE; 12734b8ac966SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_info); 12744b8ac966SJiri Pirko return 0; 12754b8ac966SJiri Pirko } 12764b8ac966SJiri Pirko 12774b8ac966SJiri Pirko static int 1278534ba6a8SSimon Horman rocker_cmd_set_port_settings_macaddr_prep(const struct rocker_port *rocker_port, 12794b8ac966SJiri Pirko struct rocker_desc_info *desc_info, 12804b8ac966SJiri Pirko void *priv) 12814b8ac966SJiri Pirko { 1282e5054643SSimon Horman const unsigned char *macaddr = priv; 12834b8ac966SJiri Pirko struct rocker_tlv *cmd_info; 12844b8ac966SJiri Pirko 12854b8ac966SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, 12864b8ac966SJiri Pirko ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS)) 12874b8ac966SJiri Pirko return -EMSGSIZE; 12884b8ac966SJiri Pirko cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 12894b8ac966SJiri Pirko if (!cmd_info) 12904b8ac966SJiri Pirko return -EMSGSIZE; 12914a6bb6d3SScott Feldman if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, 12924a6bb6d3SScott Feldman rocker_port->pport)) 12934b8ac966SJiri Pirko return -EMSGSIZE; 12944b8ac966SJiri Pirko if (rocker_tlv_put(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR, 12954b8ac966SJiri Pirko ETH_ALEN, macaddr)) 12964b8ac966SJiri Pirko return -EMSGSIZE; 12974b8ac966SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_info); 12984b8ac966SJiri Pirko return 0; 12994b8ac966SJiri Pirko } 13004b8ac966SJiri Pirko 13015111f80cSScott Feldman static int 130277a58c74SScott Feldman rocker_cmd_set_port_settings_mtu_prep(const struct rocker_port *rocker_port, 130377a58c74SScott Feldman struct rocker_desc_info *desc_info, 130477a58c74SScott Feldman void *priv) 130577a58c74SScott Feldman { 130677a58c74SScott Feldman int mtu = *(int *)priv; 130777a58c74SScott Feldman struct rocker_tlv *cmd_info; 130877a58c74SScott Feldman 130977a58c74SScott Feldman if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, 131077a58c74SScott Feldman ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS)) 131177a58c74SScott Feldman return -EMSGSIZE; 131277a58c74SScott Feldman cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 131377a58c74SScott Feldman if (!cmd_info) 131477a58c74SScott Feldman return -EMSGSIZE; 131577a58c74SScott Feldman if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, 131677a58c74SScott Feldman rocker_port->pport)) 131777a58c74SScott Feldman return -EMSGSIZE; 131877a58c74SScott Feldman if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_MTU, 131977a58c74SScott Feldman mtu)) 132077a58c74SScott Feldman return -EMSGSIZE; 132177a58c74SScott Feldman rocker_tlv_nest_end(desc_info, cmd_info); 132277a58c74SScott Feldman return 0; 132377a58c74SScott Feldman } 132477a58c74SScott Feldman 132577a58c74SScott Feldman static int 1326534ba6a8SSimon Horman rocker_cmd_set_port_learning_prep(const struct rocker_port *rocker_port, 13275111f80cSScott Feldman struct rocker_desc_info *desc_info, 13285111f80cSScott Feldman void *priv) 13295111f80cSScott Feldman { 1330c1fe922eSJiri Pirko bool learning = *(bool *)priv; 13315111f80cSScott Feldman struct rocker_tlv *cmd_info; 13325111f80cSScott Feldman 13335111f80cSScott Feldman if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, 13345111f80cSScott Feldman ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS)) 13355111f80cSScott Feldman return -EMSGSIZE; 13365111f80cSScott Feldman cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 13375111f80cSScott Feldman if (!cmd_info) 13385111f80cSScott Feldman return -EMSGSIZE; 13394a6bb6d3SScott Feldman if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, 13404a6bb6d3SScott Feldman rocker_port->pport)) 13415111f80cSScott Feldman return -EMSGSIZE; 13425111f80cSScott Feldman if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING, 1343c1fe922eSJiri Pirko learning)) 13445111f80cSScott Feldman return -EMSGSIZE; 13455111f80cSScott Feldman rocker_tlv_nest_end(desc_info, cmd_info); 13465111f80cSScott Feldman return 0; 13475111f80cSScott Feldman } 13485111f80cSScott Feldman 1349de480150SPhilippe Reynes static int 1350de480150SPhilippe Reynes rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port, 1351de480150SPhilippe Reynes struct ethtool_link_ksettings *ecmd) 13524b8ac966SJiri Pirko { 135353901cc0SJiri Pirko return rocker_cmd_exec(rocker_port, false, 13544b8ac966SJiri Pirko rocker_cmd_get_port_settings_prep, NULL, 13554b8ac966SJiri Pirko rocker_cmd_get_port_settings_ethtool_proc, 1356c4f20321SScott Feldman ecmd); 13574b8ac966SJiri Pirko } 13584b8ac966SJiri Pirko 13594b8ac966SJiri Pirko static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port, 13604b8ac966SJiri Pirko unsigned char *macaddr) 13614b8ac966SJiri Pirko { 136253901cc0SJiri Pirko return rocker_cmd_exec(rocker_port, false, 13634b8ac966SJiri Pirko rocker_cmd_get_port_settings_prep, NULL, 13644b8ac966SJiri Pirko rocker_cmd_get_port_settings_macaddr_proc, 1365c4f20321SScott Feldman macaddr); 13664b8ac966SJiri Pirko } 13674b8ac966SJiri Pirko 1368e1ba3deeSJiri Pirko static int rocker_cmd_get_port_settings_mode(struct rocker_port *rocker_port, 1369e1ba3deeSJiri Pirko u8 *p_mode) 1370e1ba3deeSJiri Pirko { 137153901cc0SJiri Pirko return rocker_cmd_exec(rocker_port, false, 1372e1ba3deeSJiri Pirko rocker_cmd_get_port_settings_prep, NULL, 1373e1ba3deeSJiri Pirko rocker_cmd_get_port_settings_mode_proc, p_mode); 1374e1ba3deeSJiri Pirko } 1375e1ba3deeSJiri Pirko 1376de480150SPhilippe Reynes static int 1377de480150SPhilippe Reynes rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port, 1378de480150SPhilippe Reynes const struct ethtool_link_ksettings *ecmd) 13794b8ac966SJiri Pirko { 1380de480150SPhilippe Reynes struct ethtool_link_ksettings copy_ecmd; 1381de480150SPhilippe Reynes 1382de480150SPhilippe Reynes memcpy(©_ecmd, ecmd, sizeof(copy_ecmd)); 1383de480150SPhilippe Reynes 138453901cc0SJiri Pirko return rocker_cmd_exec(rocker_port, false, 13854b8ac966SJiri Pirko rocker_cmd_set_port_settings_ethtool_prep, 1386de480150SPhilippe Reynes ©_ecmd, NULL, NULL); 13874b8ac966SJiri Pirko } 13884b8ac966SJiri Pirko 13894b8ac966SJiri Pirko static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port, 13904b8ac966SJiri Pirko unsigned char *macaddr) 13914b8ac966SJiri Pirko { 139253901cc0SJiri Pirko return rocker_cmd_exec(rocker_port, false, 13934b8ac966SJiri Pirko rocker_cmd_set_port_settings_macaddr_prep, 1394c4f20321SScott Feldman macaddr, NULL, NULL); 13954b8ac966SJiri Pirko } 13964b8ac966SJiri Pirko 139777a58c74SScott Feldman static int rocker_cmd_set_port_settings_mtu(struct rocker_port *rocker_port, 139877a58c74SScott Feldman int mtu) 139977a58c74SScott Feldman { 140053901cc0SJiri Pirko return rocker_cmd_exec(rocker_port, false, 140177a58c74SScott Feldman rocker_cmd_set_port_settings_mtu_prep, 140277a58c74SScott Feldman &mtu, NULL, NULL); 140377a58c74SScott Feldman } 140477a58c74SScott Feldman 14053fbcdbf3SJiri Pirko int rocker_port_set_learning(struct rocker_port *rocker_port, 1406c1fe922eSJiri Pirko bool learning) 14075111f80cSScott Feldman { 140853901cc0SJiri Pirko return rocker_cmd_exec(rocker_port, false, 14095111f80cSScott Feldman rocker_cmd_set_port_learning_prep, 1410c1fe922eSJiri Pirko &learning, NULL, NULL); 14115111f80cSScott Feldman } 14125111f80cSScott Feldman 1413e420114eSJiri Pirko /********************** 1414e420114eSJiri Pirko * Worlds manipulation 1415e420114eSJiri Pirko **********************/ 1416e420114eSJiri Pirko 1417e420114eSJiri Pirko static struct rocker_world_ops *rocker_world_ops[] = { 1418e420114eSJiri Pirko &rocker_ofdpa_ops, 1419e420114eSJiri Pirko }; 1420e420114eSJiri Pirko 1421e420114eSJiri Pirko #define ROCKER_WORLD_OPS_LEN ARRAY_SIZE(rocker_world_ops) 1422e420114eSJiri Pirko 1423e420114eSJiri Pirko static struct rocker_world_ops *rocker_world_ops_find(u8 mode) 1424e420114eSJiri Pirko { 1425e420114eSJiri Pirko int i; 1426e420114eSJiri Pirko 1427e420114eSJiri Pirko for (i = 0; i < ROCKER_WORLD_OPS_LEN; i++) 1428e420114eSJiri Pirko if (rocker_world_ops[i]->mode == mode) 1429e420114eSJiri Pirko return rocker_world_ops[i]; 1430e420114eSJiri Pirko return NULL; 1431e420114eSJiri Pirko } 1432e420114eSJiri Pirko 1433e420114eSJiri Pirko static int rocker_world_init(struct rocker *rocker, u8 mode) 1434e420114eSJiri Pirko { 1435e420114eSJiri Pirko struct rocker_world_ops *wops; 1436e420114eSJiri Pirko int err; 1437e420114eSJiri Pirko 1438e420114eSJiri Pirko wops = rocker_world_ops_find(mode); 1439e420114eSJiri Pirko if (!wops) { 1440e420114eSJiri Pirko dev_err(&rocker->pdev->dev, "port mode \"%d\" is not supported\n", 1441e420114eSJiri Pirko mode); 1442e420114eSJiri Pirko return -EINVAL; 1443e420114eSJiri Pirko } 1444e420114eSJiri Pirko rocker->wops = wops; 1445e420114eSJiri Pirko rocker->wpriv = kzalloc(wops->priv_size, GFP_KERNEL); 1446e420114eSJiri Pirko if (!rocker->wpriv) 1447e420114eSJiri Pirko return -ENOMEM; 1448e420114eSJiri Pirko if (!wops->init) 1449e420114eSJiri Pirko return 0; 1450e420114eSJiri Pirko err = wops->init(rocker); 1451e420114eSJiri Pirko if (err) 1452e420114eSJiri Pirko kfree(rocker->wpriv); 1453e420114eSJiri Pirko return err; 1454e420114eSJiri Pirko } 1455e420114eSJiri Pirko 1456e420114eSJiri Pirko static void rocker_world_fini(struct rocker *rocker) 1457e420114eSJiri Pirko { 1458e420114eSJiri Pirko struct rocker_world_ops *wops = rocker->wops; 1459e420114eSJiri Pirko 1460e420114eSJiri Pirko if (!wops || !wops->fini) 1461e420114eSJiri Pirko return; 1462e420114eSJiri Pirko wops->fini(rocker); 1463e420114eSJiri Pirko kfree(rocker->wpriv); 1464e420114eSJiri Pirko } 1465e420114eSJiri Pirko 1466e420114eSJiri Pirko static int rocker_world_check_init(struct rocker_port *rocker_port) 1467e420114eSJiri Pirko { 1468e420114eSJiri Pirko struct rocker *rocker = rocker_port->rocker; 1469e420114eSJiri Pirko u8 mode; 1470e420114eSJiri Pirko int err; 1471e420114eSJiri Pirko 1472e420114eSJiri Pirko err = rocker_cmd_get_port_settings_mode(rocker_port, &mode); 1473e420114eSJiri Pirko if (err) { 1474e420114eSJiri Pirko dev_err(&rocker->pdev->dev, "failed to get port mode\n"); 1475e420114eSJiri Pirko return err; 1476e420114eSJiri Pirko } 1477e420114eSJiri Pirko if (rocker->wops) { 1478e420114eSJiri Pirko if (rocker->wops->mode != mode) { 1479e420114eSJiri Pirko dev_err(&rocker->pdev->dev, "hardware has ports in different worlds, which is not supported\n"); 148092d230ddSWei Yongjun return -EINVAL; 1481e420114eSJiri Pirko } 1482e420114eSJiri Pirko return 0; 1483e420114eSJiri Pirko } 1484e420114eSJiri Pirko return rocker_world_init(rocker, mode); 1485e420114eSJiri Pirko } 1486e420114eSJiri Pirko 1487e420114eSJiri Pirko static int rocker_world_port_pre_init(struct rocker_port *rocker_port) 1488e420114eSJiri Pirko { 1489e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1490e420114eSJiri Pirko int err; 1491e420114eSJiri Pirko 1492e420114eSJiri Pirko rocker_port->wpriv = kzalloc(wops->port_priv_size, GFP_KERNEL); 1493e420114eSJiri Pirko if (!rocker_port->wpriv) 1494e420114eSJiri Pirko return -ENOMEM; 1495e420114eSJiri Pirko if (!wops->port_pre_init) 1496e420114eSJiri Pirko return 0; 1497e420114eSJiri Pirko err = wops->port_pre_init(rocker_port); 1498e420114eSJiri Pirko if (err) 1499e420114eSJiri Pirko kfree(rocker_port->wpriv); 1500e420114eSJiri Pirko return 0; 1501e420114eSJiri Pirko } 1502e420114eSJiri Pirko 1503e420114eSJiri Pirko static int rocker_world_port_init(struct rocker_port *rocker_port) 1504e420114eSJiri Pirko { 1505e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1506e420114eSJiri Pirko 1507e420114eSJiri Pirko if (!wops->port_init) 1508e420114eSJiri Pirko return 0; 1509e420114eSJiri Pirko return wops->port_init(rocker_port); 1510e420114eSJiri Pirko } 1511e420114eSJiri Pirko 1512e420114eSJiri Pirko static void rocker_world_port_fini(struct rocker_port *rocker_port) 1513e420114eSJiri Pirko { 1514e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1515e420114eSJiri Pirko 1516e420114eSJiri Pirko if (!wops->port_fini) 1517e420114eSJiri Pirko return; 1518e420114eSJiri Pirko wops->port_fini(rocker_port); 1519e420114eSJiri Pirko } 1520e420114eSJiri Pirko 1521e420114eSJiri Pirko static void rocker_world_port_post_fini(struct rocker_port *rocker_port) 1522e420114eSJiri Pirko { 1523e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1524e420114eSJiri Pirko 1525e420114eSJiri Pirko if (!wops->port_post_fini) 1526e420114eSJiri Pirko return; 1527e420114eSJiri Pirko wops->port_post_fini(rocker_port); 1528e420114eSJiri Pirko kfree(rocker_port->wpriv); 1529e420114eSJiri Pirko } 1530e420114eSJiri Pirko 1531e420114eSJiri Pirko static int rocker_world_port_open(struct rocker_port *rocker_port) 1532e420114eSJiri Pirko { 1533e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1534e420114eSJiri Pirko 1535e420114eSJiri Pirko if (!wops->port_open) 1536e420114eSJiri Pirko return 0; 1537e420114eSJiri Pirko return wops->port_open(rocker_port); 1538e420114eSJiri Pirko } 1539e420114eSJiri Pirko 1540e420114eSJiri Pirko static void rocker_world_port_stop(struct rocker_port *rocker_port) 1541e420114eSJiri Pirko { 1542e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1543e420114eSJiri Pirko 1544e420114eSJiri Pirko if (!wops->port_stop) 1545e420114eSJiri Pirko return; 1546e420114eSJiri Pirko wops->port_stop(rocker_port); 1547e420114eSJiri Pirko } 1548e420114eSJiri Pirko 1549e420114eSJiri Pirko static int rocker_world_port_attr_stp_state_set(struct rocker_port *rocker_port, 1550e420114eSJiri Pirko u8 state, 1551e420114eSJiri Pirko struct switchdev_trans *trans) 1552e420114eSJiri Pirko { 1553e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1554e420114eSJiri Pirko 1555e420114eSJiri Pirko if (!wops->port_attr_stp_state_set) 1556fccd84d4SJiri Pirko return -EOPNOTSUPP; 155700fc0c51SArkadi Sharshevsky 155800fc0c51SArkadi Sharshevsky if (switchdev_trans_ph_prepare(trans)) 155900fc0c51SArkadi Sharshevsky return 0; 156000fc0c51SArkadi Sharshevsky 156100fc0c51SArkadi Sharshevsky return wops->port_attr_stp_state_set(rocker_port, state); 1562e420114eSJiri Pirko } 1563e420114eSJiri Pirko 1564e420114eSJiri Pirko static int 156593700458SFlorian Fainelli rocker_world_port_attr_bridge_flags_support_get(const struct rocker_port * 156693700458SFlorian Fainelli rocker_port, 156793700458SFlorian Fainelli unsigned long * 156893700458SFlorian Fainelli p_brport_flags_support) 156993700458SFlorian Fainelli { 157093700458SFlorian Fainelli struct rocker_world_ops *wops = rocker_port->rocker->wops; 157193700458SFlorian Fainelli 157293700458SFlorian Fainelli if (!wops->port_attr_bridge_flags_support_get) 157393700458SFlorian Fainelli return -EOPNOTSUPP; 157493700458SFlorian Fainelli return wops->port_attr_bridge_flags_support_get(rocker_port, 157593700458SFlorian Fainelli p_brport_flags_support); 157693700458SFlorian Fainelli } 157793700458SFlorian Fainelli 157893700458SFlorian Fainelli static int 157993700458SFlorian Fainelli rocker_world_port_attr_pre_bridge_flags_set(struct rocker_port *rocker_port, 158093700458SFlorian Fainelli unsigned long brport_flags, 158193700458SFlorian Fainelli struct switchdev_trans *trans) 158293700458SFlorian Fainelli { 158393700458SFlorian Fainelli struct rocker_world_ops *wops = rocker_port->rocker->wops; 158493700458SFlorian Fainelli unsigned long brport_flags_s; 158593700458SFlorian Fainelli int err; 158693700458SFlorian Fainelli 158793700458SFlorian Fainelli if (!wops->port_attr_bridge_flags_set) 158893700458SFlorian Fainelli return -EOPNOTSUPP; 158993700458SFlorian Fainelli 159093700458SFlorian Fainelli err = rocker_world_port_attr_bridge_flags_support_get(rocker_port, 159193700458SFlorian Fainelli &brport_flags_s); 159293700458SFlorian Fainelli if (err) 159393700458SFlorian Fainelli return err; 159493700458SFlorian Fainelli 159593700458SFlorian Fainelli if (brport_flags & ~brport_flags_s) 159693700458SFlorian Fainelli return -EINVAL; 159793700458SFlorian Fainelli 159893700458SFlorian Fainelli return 0; 159993700458SFlorian Fainelli } 160093700458SFlorian Fainelli 160193700458SFlorian Fainelli static int 1602e420114eSJiri Pirko rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port, 1603e420114eSJiri Pirko unsigned long brport_flags, 1604e420114eSJiri Pirko struct switchdev_trans *trans) 1605e420114eSJiri Pirko { 1606e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1607e420114eSJiri Pirko 1608e420114eSJiri Pirko if (!wops->port_attr_bridge_flags_set) 1609fccd84d4SJiri Pirko return -EOPNOTSUPP; 161000fc0c51SArkadi Sharshevsky 161100fc0c51SArkadi Sharshevsky if (switchdev_trans_ph_prepare(trans)) 161200fc0c51SArkadi Sharshevsky return 0; 161300fc0c51SArkadi Sharshevsky 1614e420114eSJiri Pirko return wops->port_attr_bridge_flags_set(rocker_port, brport_flags, 1615e420114eSJiri Pirko trans); 1616e420114eSJiri Pirko } 1617e420114eSJiri Pirko 1618e420114eSJiri Pirko static int 1619e420114eSJiri Pirko rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port, 1620e420114eSJiri Pirko u32 ageing_time, 1621e420114eSJiri Pirko struct switchdev_trans *trans) 1622e420114eSJiri Pirko 1623e420114eSJiri Pirko { 1624e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1625e420114eSJiri Pirko 1626e420114eSJiri Pirko if (!wops->port_attr_bridge_ageing_time_set) 1627fccd84d4SJiri Pirko return -EOPNOTSUPP; 162800fc0c51SArkadi Sharshevsky 162900fc0c51SArkadi Sharshevsky if (switchdev_trans_ph_prepare(trans)) 163000fc0c51SArkadi Sharshevsky return 0; 163100fc0c51SArkadi Sharshevsky 1632e420114eSJiri Pirko return wops->port_attr_bridge_ageing_time_set(rocker_port, ageing_time, 1633e420114eSJiri Pirko trans); 1634e420114eSJiri Pirko } 1635e420114eSJiri Pirko 1636e420114eSJiri Pirko static int 1637e420114eSJiri Pirko rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port, 1638e420114eSJiri Pirko const struct switchdev_obj_port_vlan *vlan, 1639e420114eSJiri Pirko struct switchdev_trans *trans) 1640e420114eSJiri Pirko { 1641e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1642e420114eSJiri Pirko 1643e420114eSJiri Pirko if (!wops->port_obj_vlan_add) 1644fccd84d4SJiri Pirko return -EOPNOTSUPP; 164500fc0c51SArkadi Sharshevsky 164600fc0c51SArkadi Sharshevsky if (switchdev_trans_ph_prepare(trans)) 164700fc0c51SArkadi Sharshevsky return 0; 164800fc0c51SArkadi Sharshevsky 164900fc0c51SArkadi Sharshevsky return wops->port_obj_vlan_add(rocker_port, vlan); 1650e420114eSJiri Pirko } 1651e420114eSJiri Pirko 1652e420114eSJiri Pirko static int 1653e420114eSJiri Pirko rocker_world_port_obj_vlan_del(struct rocker_port *rocker_port, 1654e420114eSJiri Pirko const struct switchdev_obj_port_vlan *vlan) 1655e420114eSJiri Pirko { 1656e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1657e420114eSJiri Pirko 16582855118fSPetr Machata if (netif_is_bridge_master(vlan->obj.orig_dev)) 16592855118fSPetr Machata return -EOPNOTSUPP; 16602855118fSPetr Machata 1661e420114eSJiri Pirko if (!wops->port_obj_vlan_del) 1662fccd84d4SJiri Pirko return -EOPNOTSUPP; 1663e420114eSJiri Pirko return wops->port_obj_vlan_del(rocker_port, vlan); 1664e420114eSJiri Pirko } 1665e420114eSJiri Pirko 1666e420114eSJiri Pirko static int 1667726fd42fSArkadi Sharshevsky rocker_world_port_fdb_add(struct rocker_port *rocker_port, 1668726fd42fSArkadi Sharshevsky struct switchdev_notifier_fdb_info *info) 1669726fd42fSArkadi Sharshevsky { 1670726fd42fSArkadi Sharshevsky struct rocker_world_ops *wops = rocker_port->rocker->wops; 1671726fd42fSArkadi Sharshevsky 1672726fd42fSArkadi Sharshevsky if (!wops->port_obj_fdb_add) 1673726fd42fSArkadi Sharshevsky return -EOPNOTSUPP; 1674726fd42fSArkadi Sharshevsky 1675726fd42fSArkadi Sharshevsky return wops->port_obj_fdb_add(rocker_port, info->vid, info->addr); 1676726fd42fSArkadi Sharshevsky } 1677726fd42fSArkadi Sharshevsky 1678726fd42fSArkadi Sharshevsky static int 1679726fd42fSArkadi Sharshevsky rocker_world_port_fdb_del(struct rocker_port *rocker_port, 1680726fd42fSArkadi Sharshevsky struct switchdev_notifier_fdb_info *info) 1681726fd42fSArkadi Sharshevsky { 1682726fd42fSArkadi Sharshevsky struct rocker_world_ops *wops = rocker_port->rocker->wops; 1683726fd42fSArkadi Sharshevsky 1684726fd42fSArkadi Sharshevsky if (!wops->port_obj_fdb_del) 1685726fd42fSArkadi Sharshevsky return -EOPNOTSUPP; 1686726fd42fSArkadi Sharshevsky return wops->port_obj_fdb_del(rocker_port, info->vid, info->addr); 1687726fd42fSArkadi Sharshevsky } 1688726fd42fSArkadi Sharshevsky 1689e420114eSJiri Pirko static int rocker_world_port_master_linked(struct rocker_port *rocker_port, 1690e420114eSJiri Pirko struct net_device *master) 1691e420114eSJiri Pirko { 1692e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1693e420114eSJiri Pirko 1694e420114eSJiri Pirko if (!wops->port_master_linked) 1695fccd84d4SJiri Pirko return -EOPNOTSUPP; 1696e420114eSJiri Pirko return wops->port_master_linked(rocker_port, master); 1697e420114eSJiri Pirko } 1698e420114eSJiri Pirko 1699e420114eSJiri Pirko static int rocker_world_port_master_unlinked(struct rocker_port *rocker_port, 1700e420114eSJiri Pirko struct net_device *master) 1701e420114eSJiri Pirko { 1702e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1703e420114eSJiri Pirko 1704e420114eSJiri Pirko if (!wops->port_master_unlinked) 1705fccd84d4SJiri Pirko return -EOPNOTSUPP; 1706e420114eSJiri Pirko return wops->port_master_unlinked(rocker_port, master); 1707e420114eSJiri Pirko } 1708e420114eSJiri Pirko 1709e420114eSJiri Pirko static int rocker_world_port_neigh_update(struct rocker_port *rocker_port, 1710e420114eSJiri Pirko struct neighbour *n) 1711e420114eSJiri Pirko { 1712e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1713e420114eSJiri Pirko 1714e420114eSJiri Pirko if (!wops->port_neigh_update) 1715fccd84d4SJiri Pirko return -EOPNOTSUPP; 1716e420114eSJiri Pirko return wops->port_neigh_update(rocker_port, n); 1717e420114eSJiri Pirko } 1718e420114eSJiri Pirko 1719e420114eSJiri Pirko static int rocker_world_port_neigh_destroy(struct rocker_port *rocker_port, 1720e420114eSJiri Pirko struct neighbour *n) 1721e420114eSJiri Pirko { 1722e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1723e420114eSJiri Pirko 1724e420114eSJiri Pirko if (!wops->port_neigh_destroy) 1725fccd84d4SJiri Pirko return -EOPNOTSUPP; 1726e420114eSJiri Pirko return wops->port_neigh_destroy(rocker_port, n); 1727e420114eSJiri Pirko } 1728e420114eSJiri Pirko 1729e420114eSJiri Pirko static int rocker_world_port_ev_mac_vlan_seen(struct rocker_port *rocker_port, 1730e420114eSJiri Pirko const unsigned char *addr, 1731e420114eSJiri Pirko __be16 vlan_id) 1732e420114eSJiri Pirko { 1733e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1734e420114eSJiri Pirko 1735e420114eSJiri Pirko if (!wops->port_ev_mac_vlan_seen) 1736fccd84d4SJiri Pirko return -EOPNOTSUPP; 1737e420114eSJiri Pirko return wops->port_ev_mac_vlan_seen(rocker_port, addr, vlan_id); 1738e420114eSJiri Pirko } 1739e420114eSJiri Pirko 1740936bd486SJiri Pirko static int rocker_world_fib4_add(struct rocker *rocker, 1741936bd486SJiri Pirko const struct fib_entry_notifier_info *fen_info) 1742936bd486SJiri Pirko { 1743936bd486SJiri Pirko struct rocker_world_ops *wops = rocker->wops; 1744936bd486SJiri Pirko 1745936bd486SJiri Pirko if (!wops->fib4_add) 1746936bd486SJiri Pirko return 0; 1747936bd486SJiri Pirko return wops->fib4_add(rocker, fen_info); 1748936bd486SJiri Pirko } 1749936bd486SJiri Pirko 1750936bd486SJiri Pirko static int rocker_world_fib4_del(struct rocker *rocker, 1751936bd486SJiri Pirko const struct fib_entry_notifier_info *fen_info) 1752936bd486SJiri Pirko { 1753936bd486SJiri Pirko struct rocker_world_ops *wops = rocker->wops; 1754936bd486SJiri Pirko 1755936bd486SJiri Pirko if (!wops->fib4_del) 1756936bd486SJiri Pirko return 0; 1757936bd486SJiri Pirko return wops->fib4_del(rocker, fen_info); 1758936bd486SJiri Pirko } 1759936bd486SJiri Pirko 1760936bd486SJiri Pirko static void rocker_world_fib4_abort(struct rocker *rocker) 1761936bd486SJiri Pirko { 1762936bd486SJiri Pirko struct rocker_world_ops *wops = rocker->wops; 1763936bd486SJiri Pirko 1764936bd486SJiri Pirko if (wops->fib4_abort) 1765936bd486SJiri Pirko wops->fib4_abort(rocker); 1766936bd486SJiri Pirko } 1767936bd486SJiri Pirko 17684b8ac966SJiri Pirko /***************** 17694b8ac966SJiri Pirko * Net device ops 17704b8ac966SJiri Pirko *****************/ 17714b8ac966SJiri Pirko 17724b8ac966SJiri Pirko static int rocker_port_open(struct net_device *dev) 17734b8ac966SJiri Pirko { 17744b8ac966SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 17754b8ac966SJiri Pirko int err; 17764b8ac966SJiri Pirko 17774b8ac966SJiri Pirko err = rocker_port_dma_rings_init(rocker_port); 17784b8ac966SJiri Pirko if (err) 17794b8ac966SJiri Pirko return err; 17804b8ac966SJiri Pirko 17814b8ac966SJiri Pirko err = request_irq(rocker_msix_tx_vector(rocker_port), 17824b8ac966SJiri Pirko rocker_tx_irq_handler, 0, 17834b8ac966SJiri Pirko rocker_driver_name, rocker_port); 17844b8ac966SJiri Pirko if (err) { 17854b8ac966SJiri Pirko netdev_err(rocker_port->dev, "cannot assign tx irq\n"); 17864b8ac966SJiri Pirko goto err_request_tx_irq; 17874b8ac966SJiri Pirko } 17884b8ac966SJiri Pirko 17894b8ac966SJiri Pirko err = request_irq(rocker_msix_rx_vector(rocker_port), 17904b8ac966SJiri Pirko rocker_rx_irq_handler, 0, 17914b8ac966SJiri Pirko rocker_driver_name, rocker_port); 17924b8ac966SJiri Pirko if (err) { 17934b8ac966SJiri Pirko netdev_err(rocker_port->dev, "cannot assign rx irq\n"); 17944b8ac966SJiri Pirko goto err_request_rx_irq; 17954b8ac966SJiri Pirko } 17964b8ac966SJiri Pirko 1797e420114eSJiri Pirko err = rocker_world_port_open(rocker_port); 1798e420114eSJiri Pirko if (err) { 1799e420114eSJiri Pirko netdev_err(rocker_port->dev, "cannot open port in world\n"); 1800e420114eSJiri Pirko goto err_world_port_open; 1801e420114eSJiri Pirko } 1802e420114eSJiri Pirko 18034b8ac966SJiri Pirko napi_enable(&rocker_port->napi_tx); 18044b8ac966SJiri Pirko napi_enable(&rocker_port->napi_rx); 1805c3055246SAnuradha Karuppiah if (!dev->proto_down) 18064b8ac966SJiri Pirko rocker_port_set_enable(rocker_port, true); 18074b8ac966SJiri Pirko netif_start_queue(dev); 18084b8ac966SJiri Pirko return 0; 18094b8ac966SJiri Pirko 1810e420114eSJiri Pirko err_world_port_open: 18116c707945SScott Feldman free_irq(rocker_msix_rx_vector(rocker_port), rocker_port); 18124b8ac966SJiri Pirko err_request_rx_irq: 18134b8ac966SJiri Pirko free_irq(rocker_msix_tx_vector(rocker_port), rocker_port); 18144b8ac966SJiri Pirko err_request_tx_irq: 18154b8ac966SJiri Pirko rocker_port_dma_rings_fini(rocker_port); 18164b8ac966SJiri Pirko return err; 18174b8ac966SJiri Pirko } 18184b8ac966SJiri Pirko 18194b8ac966SJiri Pirko static int rocker_port_stop(struct net_device *dev) 18204b8ac966SJiri Pirko { 18214b8ac966SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 18224b8ac966SJiri Pirko 18234b8ac966SJiri Pirko netif_stop_queue(dev); 18244b8ac966SJiri Pirko rocker_port_set_enable(rocker_port, false); 18254b8ac966SJiri Pirko napi_disable(&rocker_port->napi_rx); 18264b8ac966SJiri Pirko napi_disable(&rocker_port->napi_tx); 1827e420114eSJiri Pirko rocker_world_port_stop(rocker_port); 18284b8ac966SJiri Pirko free_irq(rocker_msix_rx_vector(rocker_port), rocker_port); 18294b8ac966SJiri Pirko free_irq(rocker_msix_tx_vector(rocker_port), rocker_port); 18304b8ac966SJiri Pirko rocker_port_dma_rings_fini(rocker_port); 18314b8ac966SJiri Pirko 18324b8ac966SJiri Pirko return 0; 18334b8ac966SJiri Pirko } 18344b8ac966SJiri Pirko 1835e5054643SSimon Horman static void rocker_tx_desc_frags_unmap(const struct rocker_port *rocker_port, 1836e5054643SSimon Horman const struct rocker_desc_info *desc_info) 18374b8ac966SJiri Pirko { 1838e5054643SSimon Horman const struct rocker *rocker = rocker_port->rocker; 18394b8ac966SJiri Pirko struct pci_dev *pdev = rocker->pdev; 1840e5054643SSimon Horman const struct rocker_tlv *attrs[ROCKER_TLV_TX_MAX + 1]; 18414b8ac966SJiri Pirko struct rocker_tlv *attr; 18424b8ac966SJiri Pirko int rem; 18434b8ac966SJiri Pirko 18444b8ac966SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_TX_MAX, desc_info); 18454b8ac966SJiri Pirko if (!attrs[ROCKER_TLV_TX_FRAGS]) 18464b8ac966SJiri Pirko return; 18474b8ac966SJiri Pirko rocker_tlv_for_each_nested(attr, attrs[ROCKER_TLV_TX_FRAGS], rem) { 1848e5054643SSimon Horman const struct rocker_tlv *frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_MAX + 1]; 18494b8ac966SJiri Pirko dma_addr_t dma_handle; 18504b8ac966SJiri Pirko size_t len; 18514b8ac966SJiri Pirko 18524b8ac966SJiri Pirko if (rocker_tlv_type(attr) != ROCKER_TLV_TX_FRAG) 18534b8ac966SJiri Pirko continue; 18544b8ac966SJiri Pirko rocker_tlv_parse_nested(frag_attrs, ROCKER_TLV_TX_FRAG_ATTR_MAX, 18554b8ac966SJiri Pirko attr); 18564b8ac966SJiri Pirko if (!frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_ADDR] || 18574b8ac966SJiri Pirko !frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_LEN]) 18584b8ac966SJiri Pirko continue; 18594b8ac966SJiri Pirko dma_handle = rocker_tlv_get_u64(frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]); 18604b8ac966SJiri Pirko len = rocker_tlv_get_u16(frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_LEN]); 18614b8ac966SJiri Pirko pci_unmap_single(pdev, dma_handle, len, DMA_TO_DEVICE); 18624b8ac966SJiri Pirko } 18634b8ac966SJiri Pirko } 18644b8ac966SJiri Pirko 1865e5054643SSimon Horman static int rocker_tx_desc_frag_map_put(const struct rocker_port *rocker_port, 18664b8ac966SJiri Pirko struct rocker_desc_info *desc_info, 18674b8ac966SJiri Pirko char *buf, size_t buf_len) 18684b8ac966SJiri Pirko { 1869e5054643SSimon Horman const struct rocker *rocker = rocker_port->rocker; 18704b8ac966SJiri Pirko struct pci_dev *pdev = rocker->pdev; 18714b8ac966SJiri Pirko dma_addr_t dma_handle; 18724b8ac966SJiri Pirko struct rocker_tlv *frag; 18734b8ac966SJiri Pirko 18744b8ac966SJiri Pirko dma_handle = pci_map_single(pdev, buf, buf_len, DMA_TO_DEVICE); 18754b8ac966SJiri Pirko if (unlikely(pci_dma_mapping_error(pdev, dma_handle))) { 18764b8ac966SJiri Pirko if (net_ratelimit()) 18774b8ac966SJiri Pirko netdev_err(rocker_port->dev, "failed to dma map tx frag\n"); 18784b8ac966SJiri Pirko return -EIO; 18794b8ac966SJiri Pirko } 18804b8ac966SJiri Pirko frag = rocker_tlv_nest_start(desc_info, ROCKER_TLV_TX_FRAG); 18814b8ac966SJiri Pirko if (!frag) 18824b8ac966SJiri Pirko goto unmap_frag; 18834b8ac966SJiri Pirko if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_TX_FRAG_ATTR_ADDR, 18844b8ac966SJiri Pirko dma_handle)) 18854b8ac966SJiri Pirko goto nest_cancel; 18864b8ac966SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_TX_FRAG_ATTR_LEN, 18874b8ac966SJiri Pirko buf_len)) 18884b8ac966SJiri Pirko goto nest_cancel; 18894b8ac966SJiri Pirko rocker_tlv_nest_end(desc_info, frag); 18904b8ac966SJiri Pirko return 0; 18914b8ac966SJiri Pirko 18924b8ac966SJiri Pirko nest_cancel: 18934b8ac966SJiri Pirko rocker_tlv_nest_cancel(desc_info, frag); 18944b8ac966SJiri Pirko unmap_frag: 18954b8ac966SJiri Pirko pci_unmap_single(pdev, dma_handle, buf_len, DMA_TO_DEVICE); 18964b8ac966SJiri Pirko return -EMSGSIZE; 18974b8ac966SJiri Pirko } 18984b8ac966SJiri Pirko 18994b8ac966SJiri Pirko static netdev_tx_t rocker_port_xmit(struct sk_buff *skb, struct net_device *dev) 19004b8ac966SJiri Pirko { 19014b8ac966SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 19024b8ac966SJiri Pirko struct rocker *rocker = rocker_port->rocker; 19034b8ac966SJiri Pirko struct rocker_desc_info *desc_info; 19044b8ac966SJiri Pirko struct rocker_tlv *frags; 19054b8ac966SJiri Pirko int i; 19064b8ac966SJiri Pirko int err; 19074b8ac966SJiri Pirko 19084b8ac966SJiri Pirko desc_info = rocker_desc_head_get(&rocker_port->tx_ring); 19094b8ac966SJiri Pirko if (unlikely(!desc_info)) { 19104b8ac966SJiri Pirko if (net_ratelimit()) 19114b8ac966SJiri Pirko netdev_err(dev, "tx ring full when queue awake\n"); 19124b8ac966SJiri Pirko return NETDEV_TX_BUSY; 19134b8ac966SJiri Pirko } 19144b8ac966SJiri Pirko 19154b8ac966SJiri Pirko rocker_desc_cookie_ptr_set(desc_info, skb); 19164b8ac966SJiri Pirko 19174b8ac966SJiri Pirko frags = rocker_tlv_nest_start(desc_info, ROCKER_TLV_TX_FRAGS); 19184b8ac966SJiri Pirko if (!frags) 19194b8ac966SJiri Pirko goto out; 19204b8ac966SJiri Pirko err = rocker_tx_desc_frag_map_put(rocker_port, desc_info, 19214b8ac966SJiri Pirko skb->data, skb_headlen(skb)); 19224b8ac966SJiri Pirko if (err) 19234b8ac966SJiri Pirko goto nest_cancel; 192495b9be64SJiri Pirko if (skb_shinfo(skb)->nr_frags > ROCKER_TX_FRAGS_MAX) { 192595b9be64SJiri Pirko err = skb_linearize(skb); 192695b9be64SJiri Pirko if (err) 192795b9be64SJiri Pirko goto unmap_frags; 192895b9be64SJiri Pirko } 19294b8ac966SJiri Pirko 19304b8ac966SJiri Pirko for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 19314b8ac966SJiri Pirko const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 19324b8ac966SJiri Pirko 19334b8ac966SJiri Pirko err = rocker_tx_desc_frag_map_put(rocker_port, desc_info, 19344b8ac966SJiri Pirko skb_frag_address(frag), 19354b8ac966SJiri Pirko skb_frag_size(frag)); 19364b8ac966SJiri Pirko if (err) 19374b8ac966SJiri Pirko goto unmap_frags; 19384b8ac966SJiri Pirko } 19394b8ac966SJiri Pirko rocker_tlv_nest_end(desc_info, frags); 19404b8ac966SJiri Pirko 19414b8ac966SJiri Pirko rocker_desc_gen_clear(desc_info); 19424b8ac966SJiri Pirko rocker_desc_head_set(rocker, &rocker_port->tx_ring, desc_info); 19434b8ac966SJiri Pirko 19444b8ac966SJiri Pirko desc_info = rocker_desc_head_get(&rocker_port->tx_ring); 19454b8ac966SJiri Pirko if (!desc_info) 19464b8ac966SJiri Pirko netif_stop_queue(dev); 19474b8ac966SJiri Pirko 19484b8ac966SJiri Pirko return NETDEV_TX_OK; 19494b8ac966SJiri Pirko 19504b8ac966SJiri Pirko unmap_frags: 19514b8ac966SJiri Pirko rocker_tx_desc_frags_unmap(rocker_port, desc_info); 19524b8ac966SJiri Pirko nest_cancel: 19534b8ac966SJiri Pirko rocker_tlv_nest_cancel(desc_info, frags); 19544b8ac966SJiri Pirko out: 19554b8ac966SJiri Pirko dev_kfree_skb(skb); 1956f2bbca51SDavid Ahern dev->stats.tx_dropped++; 1957f2bbca51SDavid Ahern 19584b8ac966SJiri Pirko return NETDEV_TX_OK; 19594b8ac966SJiri Pirko } 19604b8ac966SJiri Pirko 19614b8ac966SJiri Pirko static int rocker_port_set_mac_address(struct net_device *dev, void *p) 19624b8ac966SJiri Pirko { 19634b8ac966SJiri Pirko struct sockaddr *addr = p; 19644b8ac966SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 19654b8ac966SJiri Pirko int err; 19664b8ac966SJiri Pirko 19674b8ac966SJiri Pirko if (!is_valid_ether_addr(addr->sa_data)) 19684b8ac966SJiri Pirko return -EADDRNOTAVAIL; 19694b8ac966SJiri Pirko 19704b8ac966SJiri Pirko err = rocker_cmd_set_port_settings_macaddr(rocker_port, addr->sa_data); 19714b8ac966SJiri Pirko if (err) 19724b8ac966SJiri Pirko return err; 19734b8ac966SJiri Pirko memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); 19744b8ac966SJiri Pirko return 0; 19754b8ac966SJiri Pirko } 19764b8ac966SJiri Pirko 197777a58c74SScott Feldman static int rocker_port_change_mtu(struct net_device *dev, int new_mtu) 197877a58c74SScott Feldman { 197977a58c74SScott Feldman struct rocker_port *rocker_port = netdev_priv(dev); 198077a58c74SScott Feldman int running = netif_running(dev); 198177a58c74SScott Feldman int err; 198277a58c74SScott Feldman 198377a58c74SScott Feldman if (running) 198477a58c74SScott Feldman rocker_port_stop(dev); 198577a58c74SScott Feldman 198677a58c74SScott Feldman netdev_info(dev, "MTU change from %d to %d\n", dev->mtu, new_mtu); 198777a58c74SScott Feldman dev->mtu = new_mtu; 198877a58c74SScott Feldman 198977a58c74SScott Feldman err = rocker_cmd_set_port_settings_mtu(rocker_port, new_mtu); 199077a58c74SScott Feldman if (err) 199177a58c74SScott Feldman return err; 199277a58c74SScott Feldman 199377a58c74SScott Feldman if (running) 199477a58c74SScott Feldman err = rocker_port_open(dev); 199577a58c74SScott Feldman 199677a58c74SScott Feldman return err; 199777a58c74SScott Feldman } 199877a58c74SScott Feldman 1999db19170bSDavid Ahern static int rocker_port_get_phys_port_name(struct net_device *dev, 2000db19170bSDavid Ahern char *buf, size_t len) 2001db19170bSDavid Ahern { 2002db19170bSDavid Ahern struct rocker_port *rocker_port = netdev_priv(dev); 2003db19170bSDavid Ahern struct port_name name = { .buf = buf, .len = len }; 2004db19170bSDavid Ahern int err; 2005db19170bSDavid Ahern 200653901cc0SJiri Pirko err = rocker_cmd_exec(rocker_port, false, 2007db19170bSDavid Ahern rocker_cmd_get_port_settings_prep, NULL, 2008db19170bSDavid Ahern rocker_cmd_get_port_settings_phys_name_proc, 2009c4f20321SScott Feldman &name); 2010db19170bSDavid Ahern 2011db19170bSDavid Ahern return err ? -EOPNOTSUPP : 0; 2012db19170bSDavid Ahern } 2013db19170bSDavid Ahern 2014c3055246SAnuradha Karuppiah static int rocker_port_change_proto_down(struct net_device *dev, 2015c3055246SAnuradha Karuppiah bool proto_down) 2016c3055246SAnuradha Karuppiah { 2017c3055246SAnuradha Karuppiah struct rocker_port *rocker_port = netdev_priv(dev); 2018c3055246SAnuradha Karuppiah 2019c3055246SAnuradha Karuppiah if (rocker_port->dev->flags & IFF_UP) 2020c3055246SAnuradha Karuppiah rocker_port_set_enable(rocker_port, !proto_down); 2021c3055246SAnuradha Karuppiah rocker_port->dev->proto_down = proto_down; 2022c3055246SAnuradha Karuppiah return 0; 2023c3055246SAnuradha Karuppiah } 2024c3055246SAnuradha Karuppiah 2025503eebc2SJiri Pirko static void rocker_port_neigh_destroy(struct net_device *dev, 2026503eebc2SJiri Pirko struct neighbour *n) 2027dd19f83dSScott Feldman { 2028dd19f83dSScott Feldman struct rocker_port *rocker_port = netdev_priv(n->dev); 2029e420114eSJiri Pirko int err; 2030dd19f83dSScott Feldman 2031e420114eSJiri Pirko err = rocker_world_port_neigh_destroy(rocker_port, n); 2032e420114eSJiri Pirko if (err) 2033e420114eSJiri Pirko netdev_warn(rocker_port->dev, "failed to handle neigh destroy (err %d)\n", 2034e420114eSJiri Pirko err); 2035dd19f83dSScott Feldman } 2036dd19f83dSScott Feldman 20377026b8a6SFlorian Fainelli static int rocker_port_get_port_parent_id(struct net_device *dev, 20387026b8a6SFlorian Fainelli struct netdev_phys_item_id *ppid) 20397026b8a6SFlorian Fainelli { 20407026b8a6SFlorian Fainelli const struct rocker_port *rocker_port = netdev_priv(dev); 20417026b8a6SFlorian Fainelli const struct rocker *rocker = rocker_port->rocker; 20427026b8a6SFlorian Fainelli 20437026b8a6SFlorian Fainelli ppid->id_len = sizeof(rocker->hw.id); 20447026b8a6SFlorian Fainelli memcpy(&ppid->id, &rocker->hw.id, ppid->id_len); 20457026b8a6SFlorian Fainelli 20467026b8a6SFlorian Fainelli return 0; 20477026b8a6SFlorian Fainelli } 20487026b8a6SFlorian Fainelli 20494b8ac966SJiri Pirko static const struct net_device_ops rocker_port_netdev_ops = { 20504b8ac966SJiri Pirko .ndo_open = rocker_port_open, 20514b8ac966SJiri Pirko .ndo_stop = rocker_port_stop, 20524b8ac966SJiri Pirko .ndo_start_xmit = rocker_port_xmit, 20534b8ac966SJiri Pirko .ndo_set_mac_address = rocker_port_set_mac_address, 205477a58c74SScott Feldman .ndo_change_mtu = rocker_port_change_mtu, 2055db19170bSDavid Ahern .ndo_get_phys_port_name = rocker_port_get_phys_port_name, 2056c3055246SAnuradha Karuppiah .ndo_change_proto_down = rocker_port_change_proto_down, 2057dd19f83dSScott Feldman .ndo_neigh_destroy = rocker_port_neigh_destroy, 20587026b8a6SFlorian Fainelli .ndo_get_port_parent_id = rocker_port_get_port_parent_id, 205998237d43SScott Feldman }; 206098237d43SScott Feldman 206198237d43SScott Feldman /******************** 206298237d43SScott Feldman * swdev interface 206398237d43SScott Feldman ********************/ 206498237d43SScott Feldman 2065c4f20321SScott Feldman static int rocker_port_attr_set(struct net_device *dev, 2066f7fadf30SJiri Pirko const struct switchdev_attr *attr, 20677ea6eb3fSJiri Pirko struct switchdev_trans *trans) 2068c4f20321SScott Feldman { 2069c4f20321SScott Feldman struct rocker_port *rocker_port = netdev_priv(dev); 2070c4f20321SScott Feldman int err = 0; 2071c4f20321SScott Feldman 2072c4f20321SScott Feldman switch (attr->id) { 20731f868398SJiri Pirko case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 2074e420114eSJiri Pirko err = rocker_world_port_attr_stp_state_set(rocker_port, 2075e420114eSJiri Pirko attr->u.stp_state, 2076e420114eSJiri Pirko trans); 207735636062SScott Feldman break; 207893700458SFlorian Fainelli case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: 207993700458SFlorian Fainelli err = rocker_world_port_attr_pre_bridge_flags_set(rocker_port, 208093700458SFlorian Fainelli attr->u.brport_flags, 208193700458SFlorian Fainelli trans); 20827a25c6c0SFlorian Fainelli break; 20831f868398SJiri Pirko case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: 2084e420114eSJiri Pirko err = rocker_world_port_attr_bridge_flags_set(rocker_port, 2085e420114eSJiri Pirko attr->u.brport_flags, 2086e420114eSJiri Pirko trans); 20876004c867SScott Feldman break; 2088d0cf57f9SScott Feldman case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: 2089e420114eSJiri Pirko err = rocker_world_port_attr_bridge_ageing_time_set(rocker_port, 2090e420114eSJiri Pirko attr->u.ageing_time, 2091e420114eSJiri Pirko trans); 2092d0cf57f9SScott Feldman break; 2093c4f20321SScott Feldman default: 2094c4f20321SScott Feldman err = -EOPNOTSUPP; 2095c4f20321SScott Feldman break; 2096c4f20321SScott Feldman } 2097c4f20321SScott Feldman 2098c4f20321SScott Feldman return err; 209998237d43SScott Feldman } 210098237d43SScott Feldman 21019228ad26SScott Feldman static int rocker_port_obj_add(struct net_device *dev, 2102648b4a99SJiri Pirko const struct switchdev_obj *obj, 21037ea6eb3fSJiri Pirko struct switchdev_trans *trans) 21049228ad26SScott Feldman { 21059228ad26SScott Feldman struct rocker_port *rocker_port = netdev_priv(dev); 21069228ad26SScott Feldman int err = 0; 21079228ad26SScott Feldman 21089e8f4a54SJiri Pirko switch (obj->id) { 210957d80838SJiri Pirko case SWITCHDEV_OBJ_ID_PORT_VLAN: 2110e420114eSJiri Pirko err = rocker_world_port_obj_vlan_add(rocker_port, 2111e420114eSJiri Pirko SWITCHDEV_OBJ_PORT_VLAN(obj), 2112e420114eSJiri Pirko trans); 21139228ad26SScott Feldman break; 21149228ad26SScott Feldman default: 21159228ad26SScott Feldman err = -EOPNOTSUPP; 21169228ad26SScott Feldman break; 21179228ad26SScott Feldman } 21189228ad26SScott Feldman 21199228ad26SScott Feldman return err; 21209228ad26SScott Feldman } 21219228ad26SScott Feldman 21229228ad26SScott Feldman static int rocker_port_obj_del(struct net_device *dev, 2123648b4a99SJiri Pirko const struct switchdev_obj *obj) 21249228ad26SScott Feldman { 21259228ad26SScott Feldman struct rocker_port *rocker_port = netdev_priv(dev); 21269228ad26SScott Feldman int err = 0; 21279228ad26SScott Feldman 21289e8f4a54SJiri Pirko switch (obj->id) { 212957d80838SJiri Pirko case SWITCHDEV_OBJ_ID_PORT_VLAN: 2130e420114eSJiri Pirko err = rocker_world_port_obj_vlan_del(rocker_port, 2131e420114eSJiri Pirko SWITCHDEV_OBJ_PORT_VLAN(obj)); 21329228ad26SScott Feldman break; 21339228ad26SScott Feldman default: 21349228ad26SScott Feldman err = -EOPNOTSUPP; 21359228ad26SScott Feldman break; 21369228ad26SScott Feldman } 21379228ad26SScott Feldman 21389228ad26SScott Feldman return err; 21399228ad26SScott Feldman } 21409228ad26SScott Feldman 2141db701955SIdo Schimmel struct rocker_fib_event_work { 2142db701955SIdo Schimmel struct work_struct work; 21435d7bfd14SIdo Schimmel union { 2144db701955SIdo Schimmel struct fib_entry_notifier_info fen_info; 21455d7bfd14SIdo Schimmel struct fib_rule_notifier_info fr_info; 21465d7bfd14SIdo Schimmel }; 2147db701955SIdo Schimmel struct rocker *rocker; 2148db701955SIdo Schimmel unsigned long event; 2149db701955SIdo Schimmel }; 2150db701955SIdo Schimmel 2151db701955SIdo Schimmel static void rocker_router_fib_event_work(struct work_struct *work) 2152936bd486SJiri Pirko { 2153db701955SIdo Schimmel struct rocker_fib_event_work *fib_work = 2154db701955SIdo Schimmel container_of(work, struct rocker_fib_event_work, work); 2155db701955SIdo Schimmel struct rocker *rocker = fib_work->rocker; 21565d7bfd14SIdo Schimmel struct fib_rule *rule; 2157936bd486SJiri Pirko int err; 2158936bd486SJiri Pirko 2159db701955SIdo Schimmel /* Protect internal structures from changes */ 2160db701955SIdo Schimmel rtnl_lock(); 2161db701955SIdo Schimmel switch (fib_work->event) { 2162936bd486SJiri Pirko case FIB_EVENT_ENTRY_ADD: 2163db701955SIdo Schimmel err = rocker_world_fib4_add(rocker, &fib_work->fen_info); 2164936bd486SJiri Pirko if (err) 2165936bd486SJiri Pirko rocker_world_fib4_abort(rocker); 2166db701955SIdo Schimmel fib_info_put(fib_work->fen_info.fi); 2167936bd486SJiri Pirko break; 2168936bd486SJiri Pirko case FIB_EVENT_ENTRY_DEL: 2169db701955SIdo Schimmel rocker_world_fib4_del(rocker, &fib_work->fen_info); 2170db701955SIdo Schimmel fib_info_put(fib_work->fen_info.fi); 2171936bd486SJiri Pirko break; 2172936bd486SJiri Pirko case FIB_EVENT_RULE_ADD: /* fall through */ 2173936bd486SJiri Pirko case FIB_EVENT_RULE_DEL: 21745d7bfd14SIdo Schimmel rule = fib_work->fr_info.rule; 21755d7bfd14SIdo Schimmel if (!fib4_rule_default(rule)) 2176936bd486SJiri Pirko rocker_world_fib4_abort(rocker); 21775d7bfd14SIdo Schimmel fib_rule_put(rule); 2178936bd486SJiri Pirko break; 2179936bd486SJiri Pirko } 2180db701955SIdo Schimmel rtnl_unlock(); 2181db701955SIdo Schimmel kfree(fib_work); 2182db701955SIdo Schimmel } 2183db701955SIdo Schimmel 2184db701955SIdo Schimmel /* Called with rcu_read_lock() */ 2185db701955SIdo Schimmel static int rocker_router_fib_event(struct notifier_block *nb, 2186db701955SIdo Schimmel unsigned long event, void *ptr) 2187db701955SIdo Schimmel { 2188db701955SIdo Schimmel struct rocker *rocker = container_of(nb, struct rocker, fib_nb); 2189db701955SIdo Schimmel struct rocker_fib_event_work *fib_work; 2190d371ac1eSIdo Schimmel struct fib_notifier_info *info = ptr; 2191d371ac1eSIdo Schimmel 2192d371ac1eSIdo Schimmel if (info->family != AF_INET) 2193d371ac1eSIdo Schimmel return NOTIFY_DONE; 2194db701955SIdo Schimmel 2195db701955SIdo Schimmel fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); 2196db701955SIdo Schimmel if (WARN_ON(!fib_work)) 2197db701955SIdo Schimmel return NOTIFY_BAD; 2198db701955SIdo Schimmel 2199db701955SIdo Schimmel INIT_WORK(&fib_work->work, rocker_router_fib_event_work); 2200db701955SIdo Schimmel fib_work->rocker = rocker; 2201db701955SIdo Schimmel fib_work->event = event; 2202db701955SIdo Schimmel 2203db701955SIdo Schimmel switch (event) { 2204db701955SIdo Schimmel case FIB_EVENT_ENTRY_ADD: /* fall through */ 2205db701955SIdo Schimmel case FIB_EVENT_ENTRY_DEL: 220619a9d136SDavid Ahern if (info->family == AF_INET) { 220719a9d136SDavid Ahern struct fib_entry_notifier_info *fen_info = ptr; 220819a9d136SDavid Ahern 220919a9d136SDavid Ahern if (fen_info->fi->fib_nh_is_v6) { 221019a9d136SDavid Ahern NL_SET_ERR_MSG_MOD(info->extack, "IPv6 gateway with IPv4 route is not supported"); 221119a9d136SDavid Ahern return notifier_from_errno(-EINVAL); 221219a9d136SDavid Ahern } 221319a9d136SDavid Ahern } 221419a9d136SDavid Ahern 2215db701955SIdo Schimmel memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info)); 2216db701955SIdo Schimmel /* Take referece on fib_info to prevent it from being 2217db701955SIdo Schimmel * freed while work is queued. Release it afterwards. 2218db701955SIdo Schimmel */ 2219db701955SIdo Schimmel fib_info_hold(fib_work->fen_info.fi); 2220db701955SIdo Schimmel break; 22215d7bfd14SIdo Schimmel case FIB_EVENT_RULE_ADD: /* fall through */ 22225d7bfd14SIdo Schimmel case FIB_EVENT_RULE_DEL: 22235d7bfd14SIdo Schimmel memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info)); 22245d7bfd14SIdo Schimmel fib_rule_get(fib_work->fr_info.rule); 22255d7bfd14SIdo Schimmel break; 2226db701955SIdo Schimmel } 2227db701955SIdo Schimmel 2228db701955SIdo Schimmel queue_work(rocker->rocker_owq, &fib_work->work); 2229db701955SIdo Schimmel 2230936bd486SJiri Pirko return NOTIFY_DONE; 2231936bd486SJiri Pirko } 2232936bd486SJiri Pirko 22334b8ac966SJiri Pirko /******************** 22344b8ac966SJiri Pirko * ethtool interface 22354b8ac966SJiri Pirko ********************/ 22364b8ac966SJiri Pirko 2237de480150SPhilippe Reynes static int 2238de480150SPhilippe Reynes rocker_port_get_link_ksettings(struct net_device *dev, 2239de480150SPhilippe Reynes struct ethtool_link_ksettings *ecmd) 22404b8ac966SJiri Pirko { 22414b8ac966SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 22424b8ac966SJiri Pirko 22434b8ac966SJiri Pirko return rocker_cmd_get_port_settings_ethtool(rocker_port, ecmd); 22444b8ac966SJiri Pirko } 22454b8ac966SJiri Pirko 2246de480150SPhilippe Reynes static int 2247de480150SPhilippe Reynes rocker_port_set_link_ksettings(struct net_device *dev, 2248de480150SPhilippe Reynes const struct ethtool_link_ksettings *ecmd) 22494b8ac966SJiri Pirko { 22504b8ac966SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 22514b8ac966SJiri Pirko 22524b8ac966SJiri Pirko return rocker_cmd_set_port_settings_ethtool(rocker_port, ecmd); 22534b8ac966SJiri Pirko } 22544b8ac966SJiri Pirko 22554b8ac966SJiri Pirko static void rocker_port_get_drvinfo(struct net_device *dev, 22564b8ac966SJiri Pirko struct ethtool_drvinfo *drvinfo) 22574b8ac966SJiri Pirko { 22584b8ac966SJiri Pirko strlcpy(drvinfo->driver, rocker_driver_name, sizeof(drvinfo->driver)); 22594b8ac966SJiri Pirko strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version)); 22604b8ac966SJiri Pirko } 22614b8ac966SJiri Pirko 22629766e97aSDavid Ahern static struct rocker_port_stats { 22639766e97aSDavid Ahern char str[ETH_GSTRING_LEN]; 22649766e97aSDavid Ahern int type; 22659766e97aSDavid Ahern } rocker_port_stats[] = { 22669766e97aSDavid Ahern { "rx_packets", ROCKER_TLV_CMD_PORT_STATS_RX_PKTS, }, 22679766e97aSDavid Ahern { "rx_bytes", ROCKER_TLV_CMD_PORT_STATS_RX_BYTES, }, 22689766e97aSDavid Ahern { "rx_dropped", ROCKER_TLV_CMD_PORT_STATS_RX_DROPPED, }, 22699766e97aSDavid Ahern { "rx_errors", ROCKER_TLV_CMD_PORT_STATS_RX_ERRORS, }, 22709766e97aSDavid Ahern 22719766e97aSDavid Ahern { "tx_packets", ROCKER_TLV_CMD_PORT_STATS_TX_PKTS, }, 22729766e97aSDavid Ahern { "tx_bytes", ROCKER_TLV_CMD_PORT_STATS_TX_BYTES, }, 22739766e97aSDavid Ahern { "tx_dropped", ROCKER_TLV_CMD_PORT_STATS_TX_DROPPED, }, 22749766e97aSDavid Ahern { "tx_errors", ROCKER_TLV_CMD_PORT_STATS_TX_ERRORS, }, 22759766e97aSDavid Ahern }; 22769766e97aSDavid Ahern 22779766e97aSDavid Ahern #define ROCKER_PORT_STATS_LEN ARRAY_SIZE(rocker_port_stats) 22789766e97aSDavid Ahern 22799766e97aSDavid Ahern static void rocker_port_get_strings(struct net_device *netdev, u32 stringset, 22809766e97aSDavid Ahern u8 *data) 22819766e97aSDavid Ahern { 22829766e97aSDavid Ahern u8 *p = data; 22839766e97aSDavid Ahern int i; 22849766e97aSDavid Ahern 22859766e97aSDavid Ahern switch (stringset) { 22869766e97aSDavid Ahern case ETH_SS_STATS: 22879766e97aSDavid Ahern for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) { 22889766e97aSDavid Ahern memcpy(p, rocker_port_stats[i].str, ETH_GSTRING_LEN); 22899766e97aSDavid Ahern p += ETH_GSTRING_LEN; 22909766e97aSDavid Ahern } 22919766e97aSDavid Ahern break; 22929766e97aSDavid Ahern } 22939766e97aSDavid Ahern } 22949766e97aSDavid Ahern 22959766e97aSDavid Ahern static int 2296534ba6a8SSimon Horman rocker_cmd_get_port_stats_prep(const struct rocker_port *rocker_port, 22979766e97aSDavid Ahern struct rocker_desc_info *desc_info, 22989766e97aSDavid Ahern void *priv) 22999766e97aSDavid Ahern { 23009766e97aSDavid Ahern struct rocker_tlv *cmd_stats; 23019766e97aSDavid Ahern 23029766e97aSDavid Ahern if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, 23039766e97aSDavid Ahern ROCKER_TLV_CMD_TYPE_GET_PORT_STATS)) 23049766e97aSDavid Ahern return -EMSGSIZE; 23059766e97aSDavid Ahern 23069766e97aSDavid Ahern cmd_stats = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 23079766e97aSDavid Ahern if (!cmd_stats) 23089766e97aSDavid Ahern return -EMSGSIZE; 23099766e97aSDavid Ahern 23104a6bb6d3SScott Feldman if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_STATS_PPORT, 23114a6bb6d3SScott Feldman rocker_port->pport)) 23129766e97aSDavid Ahern return -EMSGSIZE; 23139766e97aSDavid Ahern 23149766e97aSDavid Ahern rocker_tlv_nest_end(desc_info, cmd_stats); 23159766e97aSDavid Ahern 23169766e97aSDavid Ahern return 0; 23179766e97aSDavid Ahern } 23189766e97aSDavid Ahern 23199766e97aSDavid Ahern static int 2320534ba6a8SSimon Horman rocker_cmd_get_port_stats_ethtool_proc(const struct rocker_port *rocker_port, 2321e5054643SSimon Horman const struct rocker_desc_info *desc_info, 23229766e97aSDavid Ahern void *priv) 23239766e97aSDavid Ahern { 2324e5054643SSimon Horman const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1]; 2325e5054643SSimon Horman const struct rocker_tlv *stats_attrs[ROCKER_TLV_CMD_PORT_STATS_MAX + 1]; 2326e5054643SSimon Horman const struct rocker_tlv *pattr; 23274a6bb6d3SScott Feldman u32 pport; 23289766e97aSDavid Ahern u64 *data = priv; 23299766e97aSDavid Ahern int i; 23309766e97aSDavid Ahern 23319766e97aSDavid Ahern rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info); 23329766e97aSDavid Ahern 23339766e97aSDavid Ahern if (!attrs[ROCKER_TLV_CMD_INFO]) 23349766e97aSDavid Ahern return -EIO; 23359766e97aSDavid Ahern 23369766e97aSDavid Ahern rocker_tlv_parse_nested(stats_attrs, ROCKER_TLV_CMD_PORT_STATS_MAX, 23379766e97aSDavid Ahern attrs[ROCKER_TLV_CMD_INFO]); 23389766e97aSDavid Ahern 23394a6bb6d3SScott Feldman if (!stats_attrs[ROCKER_TLV_CMD_PORT_STATS_PPORT]) 23409766e97aSDavid Ahern return -EIO; 23419766e97aSDavid Ahern 23424a6bb6d3SScott Feldman pport = rocker_tlv_get_u32(stats_attrs[ROCKER_TLV_CMD_PORT_STATS_PPORT]); 23434a6bb6d3SScott Feldman if (pport != rocker_port->pport) 23449766e97aSDavid Ahern return -EIO; 23459766e97aSDavid Ahern 23469766e97aSDavid Ahern for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) { 23479766e97aSDavid Ahern pattr = stats_attrs[rocker_port_stats[i].type]; 23489766e97aSDavid Ahern if (!pattr) 23499766e97aSDavid Ahern continue; 23509766e97aSDavid Ahern 23519766e97aSDavid Ahern data[i] = rocker_tlv_get_u64(pattr); 23529766e97aSDavid Ahern } 23539766e97aSDavid Ahern 23549766e97aSDavid Ahern return 0; 23559766e97aSDavid Ahern } 23569766e97aSDavid Ahern 23579766e97aSDavid Ahern static int rocker_cmd_get_port_stats_ethtool(struct rocker_port *rocker_port, 23589766e97aSDavid Ahern void *priv) 23599766e97aSDavid Ahern { 236053901cc0SJiri Pirko return rocker_cmd_exec(rocker_port, false, 23619766e97aSDavid Ahern rocker_cmd_get_port_stats_prep, NULL, 23629766e97aSDavid Ahern rocker_cmd_get_port_stats_ethtool_proc, 2363c4f20321SScott Feldman priv); 23649766e97aSDavid Ahern } 23659766e97aSDavid Ahern 23669766e97aSDavid Ahern static void rocker_port_get_stats(struct net_device *dev, 23679766e97aSDavid Ahern struct ethtool_stats *stats, u64 *data) 23689766e97aSDavid Ahern { 23699766e97aSDavid Ahern struct rocker_port *rocker_port = netdev_priv(dev); 23709766e97aSDavid Ahern 23719766e97aSDavid Ahern if (rocker_cmd_get_port_stats_ethtool(rocker_port, data) != 0) { 23729766e97aSDavid Ahern int i; 23739766e97aSDavid Ahern 23749766e97aSDavid Ahern for (i = 0; i < ARRAY_SIZE(rocker_port_stats); ++i) 23759766e97aSDavid Ahern data[i] = 0; 23769766e97aSDavid Ahern } 23779766e97aSDavid Ahern } 23789766e97aSDavid Ahern 23799766e97aSDavid Ahern static int rocker_port_get_sset_count(struct net_device *netdev, int sset) 23809766e97aSDavid Ahern { 23819766e97aSDavid Ahern switch (sset) { 23829766e97aSDavid Ahern case ETH_SS_STATS: 23839766e97aSDavid Ahern return ROCKER_PORT_STATS_LEN; 23849766e97aSDavid Ahern default: 23859766e97aSDavid Ahern return -EOPNOTSUPP; 23869766e97aSDavid Ahern } 23879766e97aSDavid Ahern } 23889766e97aSDavid Ahern 23894b8ac966SJiri Pirko static const struct ethtool_ops rocker_port_ethtool_ops = { 23904b8ac966SJiri Pirko .get_drvinfo = rocker_port_get_drvinfo, 23914b8ac966SJiri Pirko .get_link = ethtool_op_get_link, 23929766e97aSDavid Ahern .get_strings = rocker_port_get_strings, 23939766e97aSDavid Ahern .get_ethtool_stats = rocker_port_get_stats, 23949766e97aSDavid Ahern .get_sset_count = rocker_port_get_sset_count, 2395de480150SPhilippe Reynes .get_link_ksettings = rocker_port_get_link_ksettings, 2396de480150SPhilippe Reynes .set_link_ksettings = rocker_port_set_link_ksettings, 23974b8ac966SJiri Pirko }; 23984b8ac966SJiri Pirko 23994b8ac966SJiri Pirko /***************** 24004b8ac966SJiri Pirko * NAPI interface 24014b8ac966SJiri Pirko *****************/ 24024b8ac966SJiri Pirko 24034b8ac966SJiri Pirko static struct rocker_port *rocker_port_napi_tx_get(struct napi_struct *napi) 24044b8ac966SJiri Pirko { 24054b8ac966SJiri Pirko return container_of(napi, struct rocker_port, napi_tx); 24064b8ac966SJiri Pirko } 24074b8ac966SJiri Pirko 24084b8ac966SJiri Pirko static int rocker_port_poll_tx(struct napi_struct *napi, int budget) 24094b8ac966SJiri Pirko { 24104b8ac966SJiri Pirko struct rocker_port *rocker_port = rocker_port_napi_tx_get(napi); 2411e5054643SSimon Horman const struct rocker *rocker = rocker_port->rocker; 2412e5054643SSimon Horman const struct rocker_desc_info *desc_info; 24134b8ac966SJiri Pirko u32 credits = 0; 24144b8ac966SJiri Pirko int err; 24154b8ac966SJiri Pirko 24164b8ac966SJiri Pirko /* Cleanup tx descriptors */ 24174b8ac966SJiri Pirko while ((desc_info = rocker_desc_tail_get(&rocker_port->tx_ring))) { 2418f2bbca51SDavid Ahern struct sk_buff *skb; 2419f2bbca51SDavid Ahern 24204b8ac966SJiri Pirko err = rocker_desc_err(desc_info); 24214b8ac966SJiri Pirko if (err && net_ratelimit()) 24224b8ac966SJiri Pirko netdev_err(rocker_port->dev, "tx desc received with err %d\n", 24234b8ac966SJiri Pirko err); 24244b8ac966SJiri Pirko rocker_tx_desc_frags_unmap(rocker_port, desc_info); 2425f2bbca51SDavid Ahern 2426f2bbca51SDavid Ahern skb = rocker_desc_cookie_ptr_get(desc_info); 2427f2bbca51SDavid Ahern if (err == 0) { 2428f2bbca51SDavid Ahern rocker_port->dev->stats.tx_packets++; 2429f2bbca51SDavid Ahern rocker_port->dev->stats.tx_bytes += skb->len; 24304725ceb9SScott Feldman } else { 2431f2bbca51SDavid Ahern rocker_port->dev->stats.tx_errors++; 24324725ceb9SScott Feldman } 2433f2bbca51SDavid Ahern 2434f2bbca51SDavid Ahern dev_kfree_skb_any(skb); 24354b8ac966SJiri Pirko credits++; 24364b8ac966SJiri Pirko } 24374b8ac966SJiri Pirko 24384b8ac966SJiri Pirko if (credits && netif_queue_stopped(rocker_port->dev)) 24394b8ac966SJiri Pirko netif_wake_queue(rocker_port->dev); 24404b8ac966SJiri Pirko 24414b8ac966SJiri Pirko napi_complete(napi); 24424b8ac966SJiri Pirko rocker_dma_ring_credits_set(rocker, &rocker_port->tx_ring, credits); 24434b8ac966SJiri Pirko 24444b8ac966SJiri Pirko return 0; 24454b8ac966SJiri Pirko } 24464b8ac966SJiri Pirko 2447e5054643SSimon Horman static int rocker_port_rx_proc(const struct rocker *rocker, 2448e5054643SSimon Horman const struct rocker_port *rocker_port, 24494b8ac966SJiri Pirko struct rocker_desc_info *desc_info) 24504b8ac966SJiri Pirko { 2451e5054643SSimon Horman const struct rocker_tlv *attrs[ROCKER_TLV_RX_MAX + 1]; 24524b8ac966SJiri Pirko struct sk_buff *skb = rocker_desc_cookie_ptr_get(desc_info); 24534b8ac966SJiri Pirko size_t rx_len; 24543f98a8e6SScott Feldman u16 rx_flags = 0; 24554b8ac966SJiri Pirko 24564b8ac966SJiri Pirko if (!skb) 24574b8ac966SJiri Pirko return -ENOENT; 24584b8ac966SJiri Pirko 24594b8ac966SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_RX_MAX, desc_info); 24604b8ac966SJiri Pirko if (!attrs[ROCKER_TLV_RX_FRAG_LEN]) 24614b8ac966SJiri Pirko return -EINVAL; 24623f98a8e6SScott Feldman if (attrs[ROCKER_TLV_RX_FLAGS]) 24633f98a8e6SScott Feldman rx_flags = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FLAGS]); 24644b8ac966SJiri Pirko 24654b8ac966SJiri Pirko rocker_dma_rx_ring_skb_unmap(rocker, attrs); 24664b8ac966SJiri Pirko 24674b8ac966SJiri Pirko rx_len = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FRAG_LEN]); 24684b8ac966SJiri Pirko skb_put(skb, rx_len); 24694b8ac966SJiri Pirko skb->protocol = eth_type_trans(skb, rocker_port->dev); 2470f2bbca51SDavid Ahern 24713f98a8e6SScott Feldman if (rx_flags & ROCKER_RX_FLAGS_FWD_OFFLOAD) 24726bc506b4SIdo Schimmel skb->offload_fwd_mark = 1; 24733f98a8e6SScott Feldman 2474f2bbca51SDavid Ahern rocker_port->dev->stats.rx_packets++; 2475f2bbca51SDavid Ahern rocker_port->dev->stats.rx_bytes += skb->len; 2476f2bbca51SDavid Ahern 24774b8ac966SJiri Pirko netif_receive_skb(skb); 24784b8ac966SJiri Pirko 2479534ba6a8SSimon Horman return rocker_dma_rx_ring_skb_alloc(rocker_port, desc_info); 24804b8ac966SJiri Pirko } 24814b8ac966SJiri Pirko 24824b8ac966SJiri Pirko static struct rocker_port *rocker_port_napi_rx_get(struct napi_struct *napi) 24834b8ac966SJiri Pirko { 24844b8ac966SJiri Pirko return container_of(napi, struct rocker_port, napi_rx); 24854b8ac966SJiri Pirko } 24864b8ac966SJiri Pirko 24874b8ac966SJiri Pirko static int rocker_port_poll_rx(struct napi_struct *napi, int budget) 24884b8ac966SJiri Pirko { 24894b8ac966SJiri Pirko struct rocker_port *rocker_port = rocker_port_napi_rx_get(napi); 2490e5054643SSimon Horman const struct rocker *rocker = rocker_port->rocker; 24914b8ac966SJiri Pirko struct rocker_desc_info *desc_info; 24924b8ac966SJiri Pirko u32 credits = 0; 24934b8ac966SJiri Pirko int err; 24944b8ac966SJiri Pirko 24954b8ac966SJiri Pirko /* Process rx descriptors */ 24964b8ac966SJiri Pirko while (credits < budget && 24974b8ac966SJiri Pirko (desc_info = rocker_desc_tail_get(&rocker_port->rx_ring))) { 24984b8ac966SJiri Pirko err = rocker_desc_err(desc_info); 24994b8ac966SJiri Pirko if (err) { 25004b8ac966SJiri Pirko if (net_ratelimit()) 25014b8ac966SJiri Pirko netdev_err(rocker_port->dev, "rx desc received with err %d\n", 25024b8ac966SJiri Pirko err); 25034b8ac966SJiri Pirko } else { 25044b8ac966SJiri Pirko err = rocker_port_rx_proc(rocker, rocker_port, 25054b8ac966SJiri Pirko desc_info); 25064b8ac966SJiri Pirko if (err && net_ratelimit()) 25074b8ac966SJiri Pirko netdev_err(rocker_port->dev, "rx processing failed with err %d\n", 25084b8ac966SJiri Pirko err); 25094b8ac966SJiri Pirko } 2510f2bbca51SDavid Ahern if (err) 2511f2bbca51SDavid Ahern rocker_port->dev->stats.rx_errors++; 2512f2bbca51SDavid Ahern 25134b8ac966SJiri Pirko rocker_desc_gen_clear(desc_info); 25144b8ac966SJiri Pirko rocker_desc_head_set(rocker, &rocker_port->rx_ring, desc_info); 25154b8ac966SJiri Pirko credits++; 25164b8ac966SJiri Pirko } 25174b8ac966SJiri Pirko 25184b8ac966SJiri Pirko if (credits < budget) 25196ad20165SEric Dumazet napi_complete_done(napi, credits); 25204b8ac966SJiri Pirko 25214b8ac966SJiri Pirko rocker_dma_ring_credits_set(rocker, &rocker_port->rx_ring, credits); 25224b8ac966SJiri Pirko 25234b8ac966SJiri Pirko return credits; 25244b8ac966SJiri Pirko } 25254b8ac966SJiri Pirko 25264b8ac966SJiri Pirko /***************** 25274b8ac966SJiri Pirko * PCI driver ops 25284b8ac966SJiri Pirko *****************/ 25294b8ac966SJiri Pirko 2530e5054643SSimon Horman static void rocker_carrier_init(const struct rocker_port *rocker_port) 25314b8ac966SJiri Pirko { 2532e5054643SSimon Horman const struct rocker *rocker = rocker_port->rocker; 25334b8ac966SJiri Pirko u64 link_status = rocker_read64(rocker, PORT_PHYS_LINK_STATUS); 25344b8ac966SJiri Pirko bool link_up; 25354b8ac966SJiri Pirko 25364a6bb6d3SScott Feldman link_up = link_status & (1 << rocker_port->pport); 25374b8ac966SJiri Pirko if (link_up) 25384b8ac966SJiri Pirko netif_carrier_on(rocker_port->dev); 25394b8ac966SJiri Pirko else 25404b8ac966SJiri Pirko netif_carrier_off(rocker_port->dev); 25414b8ac966SJiri Pirko } 25424b8ac966SJiri Pirko 2543e420114eSJiri Pirko static void rocker_remove_ports(struct rocker *rocker) 25444b8ac966SJiri Pirko { 25459f6bbf7cSScott Feldman struct rocker_port *rocker_port; 25464b8ac966SJiri Pirko int i; 25474b8ac966SJiri Pirko 25489f6bbf7cSScott Feldman for (i = 0; i < rocker->port_count; i++) { 25499f6bbf7cSScott Feldman rocker_port = rocker->ports[i]; 2550a0720310SScott Feldman if (!rocker_port) 2551a0720310SScott Feldman continue; 2552e420114eSJiri Pirko rocker_world_port_fini(rocker_port); 25539f6bbf7cSScott Feldman unregister_netdev(rocker_port->dev); 2554e420114eSJiri Pirko rocker_world_port_post_fini(rocker_port); 25551ebd47efSIdo Schimmel free_netdev(rocker_port->dev); 25569f6bbf7cSScott Feldman } 2557e420114eSJiri Pirko rocker_world_fini(rocker); 25584b8ac966SJiri Pirko kfree(rocker->ports); 25594b8ac966SJiri Pirko } 25604b8ac966SJiri Pirko 2561534ba6a8SSimon Horman static void rocker_port_dev_addr_init(struct rocker_port *rocker_port) 25624b8ac966SJiri Pirko { 2563534ba6a8SSimon Horman const struct rocker *rocker = rocker_port->rocker; 2564e5054643SSimon Horman const struct pci_dev *pdev = rocker->pdev; 25654b8ac966SJiri Pirko int err; 25664b8ac966SJiri Pirko 25674b8ac966SJiri Pirko err = rocker_cmd_get_port_settings_macaddr(rocker_port, 25684b8ac966SJiri Pirko rocker_port->dev->dev_addr); 25694b8ac966SJiri Pirko if (err) { 25704b8ac966SJiri Pirko dev_warn(&pdev->dev, "failed to get mac address, using random\n"); 25714b8ac966SJiri Pirko eth_hw_addr_random(rocker_port->dev); 25724b8ac966SJiri Pirko } 25734b8ac966SJiri Pirko } 25744b8ac966SJiri Pirko 257544770e11SJarod Wilson #define ROCKER_PORT_MIN_MTU ETH_MIN_MTU 257644770e11SJarod Wilson #define ROCKER_PORT_MAX_MTU 9000 25774b8ac966SJiri Pirko static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) 25784b8ac966SJiri Pirko { 257997699056SJiri Pirko struct pci_dev *pdev = rocker->pdev; 25804b8ac966SJiri Pirko struct rocker_port *rocker_port; 25814b8ac966SJiri Pirko struct net_device *dev; 25824b8ac966SJiri Pirko int err; 25834b8ac966SJiri Pirko 25844b8ac966SJiri Pirko dev = alloc_etherdev(sizeof(struct rocker_port)); 25854b8ac966SJiri Pirko if (!dev) 25864b8ac966SJiri Pirko return -ENOMEM; 258797699056SJiri Pirko SET_NETDEV_DEV(dev, &pdev->dev); 25884b8ac966SJiri Pirko rocker_port = netdev_priv(dev); 25894b8ac966SJiri Pirko rocker_port->dev = dev; 25904b8ac966SJiri Pirko rocker_port->rocker = rocker; 25914b8ac966SJiri Pirko rocker_port->port_number = port_number; 25924a6bb6d3SScott Feldman rocker_port->pport = port_number + 1; 25934b8ac966SJiri Pirko 2594e420114eSJiri Pirko err = rocker_world_check_init(rocker_port); 2595e420114eSJiri Pirko if (err) { 2596e420114eSJiri Pirko dev_err(&pdev->dev, "world init failed\n"); 2597e420114eSJiri Pirko goto err_world_check_init; 2598e420114eSJiri Pirko } 2599e420114eSJiri Pirko 2600534ba6a8SSimon Horman rocker_port_dev_addr_init(rocker_port); 26014b8ac966SJiri Pirko dev->netdev_ops = &rocker_port_netdev_ops; 26024b8ac966SJiri Pirko dev->ethtool_ops = &rocker_port_ethtool_ops; 2603d64b5e85SEric Dumazet netif_tx_napi_add(dev, &rocker_port->napi_tx, rocker_port_poll_tx, 26044b8ac966SJiri Pirko NAPI_POLL_WEIGHT); 26054b8ac966SJiri Pirko netif_napi_add(dev, &rocker_port->napi_rx, rocker_port_poll_rx, 26064b8ac966SJiri Pirko NAPI_POLL_WEIGHT); 26074b8ac966SJiri Pirko rocker_carrier_init(rocker_port); 26084b8ac966SJiri Pirko 260921518a6eSIdo Schimmel dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_SG; 26104b8ac966SJiri Pirko 261144770e11SJarod Wilson /* MTU range: 68 - 9000 */ 261244770e11SJarod Wilson dev->min_mtu = ROCKER_PORT_MIN_MTU; 261344770e11SJarod Wilson dev->max_mtu = ROCKER_PORT_MAX_MTU; 261444770e11SJarod Wilson 2615e420114eSJiri Pirko err = rocker_world_port_pre_init(rocker_port); 2616e420114eSJiri Pirko if (err) { 2617e420114eSJiri Pirko dev_err(&pdev->dev, "port world pre-init failed\n"); 2618e420114eSJiri Pirko goto err_world_port_pre_init; 2619e420114eSJiri Pirko } 26204b8ac966SJiri Pirko err = register_netdev(dev); 26214b8ac966SJiri Pirko if (err) { 26224b8ac966SJiri Pirko dev_err(&pdev->dev, "register_netdev failed\n"); 26234b8ac966SJiri Pirko goto err_register_netdev; 26244b8ac966SJiri Pirko } 26254b8ac966SJiri Pirko rocker->ports[port_number] = rocker_port; 26264b8ac966SJiri Pirko 2627e420114eSJiri Pirko err = rocker_world_port_init(rocker_port); 2628e420114eSJiri Pirko if (err) { 2629e420114eSJiri Pirko dev_err(&pdev->dev, "port world init failed\n"); 2630e420114eSJiri Pirko goto err_world_port_init; 2631e420114eSJiri Pirko } 2632e420114eSJiri Pirko 26334b8ac966SJiri Pirko return 0; 26344b8ac966SJiri Pirko 2635e420114eSJiri Pirko err_world_port_init: 26366c4f7780SScott Feldman rocker->ports[port_number] = NULL; 26379f6bbf7cSScott Feldman unregister_netdev(dev); 26384b8ac966SJiri Pirko err_register_netdev: 2639e420114eSJiri Pirko rocker_world_port_post_fini(rocker_port); 2640e420114eSJiri Pirko err_world_port_pre_init: 2641e420114eSJiri Pirko err_world_check_init: 26424b8ac966SJiri Pirko free_netdev(dev); 26434b8ac966SJiri Pirko return err; 26444b8ac966SJiri Pirko } 26454b8ac966SJiri Pirko 26464b8ac966SJiri Pirko static int rocker_probe_ports(struct rocker *rocker) 26474b8ac966SJiri Pirko { 26484b8ac966SJiri Pirko int i; 26494b8ac966SJiri Pirko size_t alloc_size; 26504b8ac966SJiri Pirko int err; 26514b8ac966SJiri Pirko 26524b8ac966SJiri Pirko alloc_size = sizeof(struct rocker_port *) * rocker->port_count; 265327b808cbSScott Feldman rocker->ports = kzalloc(alloc_size, GFP_KERNEL); 2654e65ad3beSDan Carpenter if (!rocker->ports) 2655e65ad3beSDan Carpenter return -ENOMEM; 26564b8ac966SJiri Pirko for (i = 0; i < rocker->port_count; i++) { 26574b8ac966SJiri Pirko err = rocker_probe_port(rocker, i); 26584b8ac966SJiri Pirko if (err) 26594b8ac966SJiri Pirko goto remove_ports; 26604b8ac966SJiri Pirko } 26614b8ac966SJiri Pirko return 0; 26624b8ac966SJiri Pirko 26634b8ac966SJiri Pirko remove_ports: 26644b8ac966SJiri Pirko rocker_remove_ports(rocker); 26654b8ac966SJiri Pirko return err; 26664b8ac966SJiri Pirko } 26674b8ac966SJiri Pirko 26684b8ac966SJiri Pirko static int rocker_msix_init(struct rocker *rocker) 26694b8ac966SJiri Pirko { 26704b8ac966SJiri Pirko struct pci_dev *pdev = rocker->pdev; 26714b8ac966SJiri Pirko int msix_entries; 26724b8ac966SJiri Pirko int i; 26734b8ac966SJiri Pirko int err; 26744b8ac966SJiri Pirko 26754b8ac966SJiri Pirko msix_entries = pci_msix_vec_count(pdev); 26764b8ac966SJiri Pirko if (msix_entries < 0) 26774b8ac966SJiri Pirko return msix_entries; 26784b8ac966SJiri Pirko 26794b8ac966SJiri Pirko if (msix_entries != ROCKER_MSIX_VEC_COUNT(rocker->port_count)) 26804b8ac966SJiri Pirko return -EINVAL; 26814b8ac966SJiri Pirko 26824b8ac966SJiri Pirko rocker->msix_entries = kmalloc_array(msix_entries, 26834b8ac966SJiri Pirko sizeof(struct msix_entry), 26844b8ac966SJiri Pirko GFP_KERNEL); 26854b8ac966SJiri Pirko if (!rocker->msix_entries) 26864b8ac966SJiri Pirko return -ENOMEM; 26874b8ac966SJiri Pirko 26884b8ac966SJiri Pirko for (i = 0; i < msix_entries; i++) 26894b8ac966SJiri Pirko rocker->msix_entries[i].entry = i; 26904b8ac966SJiri Pirko 26914b8ac966SJiri Pirko err = pci_enable_msix_exact(pdev, rocker->msix_entries, msix_entries); 26924b8ac966SJiri Pirko if (err < 0) 26934b8ac966SJiri Pirko goto err_enable_msix; 26944b8ac966SJiri Pirko 26954b8ac966SJiri Pirko return 0; 26964b8ac966SJiri Pirko 26974b8ac966SJiri Pirko err_enable_msix: 26984b8ac966SJiri Pirko kfree(rocker->msix_entries); 26994b8ac966SJiri Pirko return err; 27004b8ac966SJiri Pirko } 27014b8ac966SJiri Pirko 2702e5054643SSimon Horman static void rocker_msix_fini(const struct rocker *rocker) 27034b8ac966SJiri Pirko { 27044b8ac966SJiri Pirko pci_disable_msix(rocker->pdev); 27054b8ac966SJiri Pirko kfree(rocker->msix_entries); 27064b8ac966SJiri Pirko } 27074b8ac966SJiri Pirko 2708726fd42fSArkadi Sharshevsky static bool rocker_port_dev_check(const struct net_device *dev) 2709726fd42fSArkadi Sharshevsky { 2710726fd42fSArkadi Sharshevsky return dev->netdev_ops == &rocker_port_netdev_ops; 2711726fd42fSArkadi Sharshevsky } 2712726fd42fSArkadi Sharshevsky 27134f705486SFlorian Fainelli static int 27144f705486SFlorian Fainelli rocker_switchdev_port_attr_set_event(struct net_device *netdev, 27154f705486SFlorian Fainelli struct switchdev_notifier_port_attr_info *port_attr_info) 27164f705486SFlorian Fainelli { 27174f705486SFlorian Fainelli int err; 27184f705486SFlorian Fainelli 27194f705486SFlorian Fainelli err = rocker_port_attr_set(netdev, port_attr_info->attr, 27204f705486SFlorian Fainelli port_attr_info->trans); 27214f705486SFlorian Fainelli 27224f705486SFlorian Fainelli port_attr_info->handled = true; 27234f705486SFlorian Fainelli return notifier_from_errno(err); 27244f705486SFlorian Fainelli } 27254f705486SFlorian Fainelli 2726726fd42fSArkadi Sharshevsky struct rocker_switchdev_event_work { 2727726fd42fSArkadi Sharshevsky struct work_struct work; 2728726fd42fSArkadi Sharshevsky struct switchdev_notifier_fdb_info fdb_info; 2729726fd42fSArkadi Sharshevsky struct rocker_port *rocker_port; 2730726fd42fSArkadi Sharshevsky unsigned long event; 2731726fd42fSArkadi Sharshevsky }; 2732726fd42fSArkadi Sharshevsky 2733726fd42fSArkadi Sharshevsky static void 2734726fd42fSArkadi Sharshevsky rocker_fdb_offload_notify(struct rocker_port *rocker_port, 2735726fd42fSArkadi Sharshevsky struct switchdev_notifier_fdb_info *recv_info) 2736726fd42fSArkadi Sharshevsky { 2737726fd42fSArkadi Sharshevsky struct switchdev_notifier_fdb_info info; 2738726fd42fSArkadi Sharshevsky 2739726fd42fSArkadi Sharshevsky info.addr = recv_info->addr; 2740726fd42fSArkadi Sharshevsky info.vid = recv_info->vid; 2741e9ba0fbcSIdo Schimmel info.offloaded = true; 2742726fd42fSArkadi Sharshevsky call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, 27436685987cSPetr Machata rocker_port->dev, &info.info, NULL); 2744726fd42fSArkadi Sharshevsky } 2745726fd42fSArkadi Sharshevsky 2746726fd42fSArkadi Sharshevsky static void rocker_switchdev_event_work(struct work_struct *work) 2747726fd42fSArkadi Sharshevsky { 2748726fd42fSArkadi Sharshevsky struct rocker_switchdev_event_work *switchdev_work = 2749726fd42fSArkadi Sharshevsky container_of(work, struct rocker_switchdev_event_work, work); 2750726fd42fSArkadi Sharshevsky struct rocker_port *rocker_port = switchdev_work->rocker_port; 2751726fd42fSArkadi Sharshevsky struct switchdev_notifier_fdb_info *fdb_info; 2752726fd42fSArkadi Sharshevsky int err; 2753726fd42fSArkadi Sharshevsky 2754726fd42fSArkadi Sharshevsky rtnl_lock(); 2755726fd42fSArkadi Sharshevsky switch (switchdev_work->event) { 2756726fd42fSArkadi Sharshevsky case SWITCHDEV_FDB_ADD_TO_DEVICE: 2757726fd42fSArkadi Sharshevsky fdb_info = &switchdev_work->fdb_info; 2758ec9efb52SPetr Machata if (!fdb_info->added_by_user) 2759ec9efb52SPetr Machata break; 2760726fd42fSArkadi Sharshevsky err = rocker_world_port_fdb_add(rocker_port, fdb_info); 2761726fd42fSArkadi Sharshevsky if (err) { 2762726fd42fSArkadi Sharshevsky netdev_dbg(rocker_port->dev, "fdb add failed err=%d\n", err); 2763726fd42fSArkadi Sharshevsky break; 2764726fd42fSArkadi Sharshevsky } 2765726fd42fSArkadi Sharshevsky rocker_fdb_offload_notify(rocker_port, fdb_info); 2766726fd42fSArkadi Sharshevsky break; 2767726fd42fSArkadi Sharshevsky case SWITCHDEV_FDB_DEL_TO_DEVICE: 2768726fd42fSArkadi Sharshevsky fdb_info = &switchdev_work->fdb_info; 2769ec9efb52SPetr Machata if (!fdb_info->added_by_user) 2770ec9efb52SPetr Machata break; 2771726fd42fSArkadi Sharshevsky err = rocker_world_port_fdb_del(rocker_port, fdb_info); 2772726fd42fSArkadi Sharshevsky if (err) 2773726fd42fSArkadi Sharshevsky netdev_dbg(rocker_port->dev, "fdb add failed err=%d\n", err); 2774726fd42fSArkadi Sharshevsky break; 2775726fd42fSArkadi Sharshevsky } 2776726fd42fSArkadi Sharshevsky rtnl_unlock(); 2777726fd42fSArkadi Sharshevsky 2778726fd42fSArkadi Sharshevsky kfree(switchdev_work->fdb_info.addr); 2779726fd42fSArkadi Sharshevsky kfree(switchdev_work); 2780726fd42fSArkadi Sharshevsky dev_put(rocker_port->dev); 2781726fd42fSArkadi Sharshevsky } 2782726fd42fSArkadi Sharshevsky 2783726fd42fSArkadi Sharshevsky /* called under rcu_read_lock() */ 2784726fd42fSArkadi Sharshevsky static int rocker_switchdev_event(struct notifier_block *unused, 2785726fd42fSArkadi Sharshevsky unsigned long event, void *ptr) 2786726fd42fSArkadi Sharshevsky { 2787726fd42fSArkadi Sharshevsky struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 2788726fd42fSArkadi Sharshevsky struct rocker_switchdev_event_work *switchdev_work; 2789726fd42fSArkadi Sharshevsky struct switchdev_notifier_fdb_info *fdb_info = ptr; 2790726fd42fSArkadi Sharshevsky struct rocker_port *rocker_port; 2791726fd42fSArkadi Sharshevsky 2792726fd42fSArkadi Sharshevsky if (!rocker_port_dev_check(dev)) 2793726fd42fSArkadi Sharshevsky return NOTIFY_DONE; 2794726fd42fSArkadi Sharshevsky 27954f705486SFlorian Fainelli if (event == SWITCHDEV_PORT_ATTR_SET) 27964f705486SFlorian Fainelli return rocker_switchdev_port_attr_set_event(dev, ptr); 27974f705486SFlorian Fainelli 2798726fd42fSArkadi Sharshevsky rocker_port = netdev_priv(dev); 2799726fd42fSArkadi Sharshevsky switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); 2800726fd42fSArkadi Sharshevsky if (WARN_ON(!switchdev_work)) 2801726fd42fSArkadi Sharshevsky return NOTIFY_BAD; 2802726fd42fSArkadi Sharshevsky 2803726fd42fSArkadi Sharshevsky INIT_WORK(&switchdev_work->work, rocker_switchdev_event_work); 2804726fd42fSArkadi Sharshevsky switchdev_work->rocker_port = rocker_port; 2805726fd42fSArkadi Sharshevsky switchdev_work->event = event; 2806726fd42fSArkadi Sharshevsky 2807726fd42fSArkadi Sharshevsky switch (event) { 2808726fd42fSArkadi Sharshevsky case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */ 2809726fd42fSArkadi Sharshevsky case SWITCHDEV_FDB_DEL_TO_DEVICE: 2810726fd42fSArkadi Sharshevsky memcpy(&switchdev_work->fdb_info, ptr, 2811726fd42fSArkadi Sharshevsky sizeof(switchdev_work->fdb_info)); 2812726fd42fSArkadi Sharshevsky switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); 28135c149314SKangjie Lu if (unlikely(!switchdev_work->fdb_info.addr)) { 28145c149314SKangjie Lu kfree(switchdev_work); 28155c149314SKangjie Lu return NOTIFY_BAD; 28165c149314SKangjie Lu } 28175c149314SKangjie Lu 2818726fd42fSArkadi Sharshevsky ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, 2819726fd42fSArkadi Sharshevsky fdb_info->addr); 2820726fd42fSArkadi Sharshevsky /* Take a reference on the rocker device */ 2821726fd42fSArkadi Sharshevsky dev_hold(dev); 2822726fd42fSArkadi Sharshevsky break; 2823726fd42fSArkadi Sharshevsky default: 2824726fd42fSArkadi Sharshevsky kfree(switchdev_work); 2825726fd42fSArkadi Sharshevsky return NOTIFY_DONE; 2826726fd42fSArkadi Sharshevsky } 2827726fd42fSArkadi Sharshevsky 2828726fd42fSArkadi Sharshevsky queue_work(rocker_port->rocker->rocker_owq, 2829726fd42fSArkadi Sharshevsky &switchdev_work->work); 2830726fd42fSArkadi Sharshevsky return NOTIFY_DONE; 2831726fd42fSArkadi Sharshevsky } 2832726fd42fSArkadi Sharshevsky 2833c6fa35b2SPetr Machata static int 2834c6fa35b2SPetr Machata rocker_switchdev_port_obj_event(unsigned long event, struct net_device *netdev, 2835c6fa35b2SPetr Machata struct switchdev_notifier_port_obj_info *port_obj_info) 2836c6fa35b2SPetr Machata { 2837c6fa35b2SPetr Machata int err = -EOPNOTSUPP; 2838c6fa35b2SPetr Machata 2839c6fa35b2SPetr Machata switch (event) { 2840c6fa35b2SPetr Machata case SWITCHDEV_PORT_OBJ_ADD: 2841c6fa35b2SPetr Machata err = rocker_port_obj_add(netdev, port_obj_info->obj, 2842c6fa35b2SPetr Machata port_obj_info->trans); 2843c6fa35b2SPetr Machata break; 2844c6fa35b2SPetr Machata case SWITCHDEV_PORT_OBJ_DEL: 2845c6fa35b2SPetr Machata err = rocker_port_obj_del(netdev, port_obj_info->obj); 2846c6fa35b2SPetr Machata break; 2847c6fa35b2SPetr Machata } 2848c6fa35b2SPetr Machata 2849c6fa35b2SPetr Machata port_obj_info->handled = true; 2850c6fa35b2SPetr Machata return notifier_from_errno(err); 2851c6fa35b2SPetr Machata } 2852c6fa35b2SPetr Machata 2853c6fa35b2SPetr Machata static int rocker_switchdev_blocking_event(struct notifier_block *unused, 2854c6fa35b2SPetr Machata unsigned long event, void *ptr) 2855c6fa35b2SPetr Machata { 2856c6fa35b2SPetr Machata struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 2857c6fa35b2SPetr Machata 2858c6fa35b2SPetr Machata if (!rocker_port_dev_check(dev)) 2859c6fa35b2SPetr Machata return NOTIFY_DONE; 2860c6fa35b2SPetr Machata 2861c6fa35b2SPetr Machata switch (event) { 2862c6fa35b2SPetr Machata case SWITCHDEV_PORT_OBJ_ADD: 2863c6fa35b2SPetr Machata case SWITCHDEV_PORT_OBJ_DEL: 2864c6fa35b2SPetr Machata return rocker_switchdev_port_obj_event(event, dev, ptr); 28654f705486SFlorian Fainelli case SWITCHDEV_PORT_ATTR_SET: 28664f705486SFlorian Fainelli return rocker_switchdev_port_attr_set_event(dev, ptr); 2867c6fa35b2SPetr Machata } 2868c6fa35b2SPetr Machata 2869c6fa35b2SPetr Machata return NOTIFY_DONE; 2870c6fa35b2SPetr Machata } 2871c6fa35b2SPetr Machata 2872726fd42fSArkadi Sharshevsky static struct notifier_block rocker_switchdev_notifier = { 2873726fd42fSArkadi Sharshevsky .notifier_call = rocker_switchdev_event, 2874726fd42fSArkadi Sharshevsky }; 2875726fd42fSArkadi Sharshevsky 2876c6fa35b2SPetr Machata static struct notifier_block rocker_switchdev_blocking_notifier = { 2877c6fa35b2SPetr Machata .notifier_call = rocker_switchdev_blocking_event, 2878c6fa35b2SPetr Machata }; 2879c6fa35b2SPetr Machata 28804b8ac966SJiri Pirko static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) 28814b8ac966SJiri Pirko { 2882c6fa35b2SPetr Machata struct notifier_block *nb; 28834b8ac966SJiri Pirko struct rocker *rocker; 28844b8ac966SJiri Pirko int err; 28854b8ac966SJiri Pirko 28864b8ac966SJiri Pirko rocker = kzalloc(sizeof(*rocker), GFP_KERNEL); 28874b8ac966SJiri Pirko if (!rocker) 28884b8ac966SJiri Pirko return -ENOMEM; 28894b8ac966SJiri Pirko 28904b8ac966SJiri Pirko err = pci_enable_device(pdev); 28914b8ac966SJiri Pirko if (err) { 28924b8ac966SJiri Pirko dev_err(&pdev->dev, "pci_enable_device failed\n"); 28934b8ac966SJiri Pirko goto err_pci_enable_device; 28944b8ac966SJiri Pirko } 28954b8ac966SJiri Pirko 28964b8ac966SJiri Pirko err = pci_request_regions(pdev, rocker_driver_name); 28974b8ac966SJiri Pirko if (err) { 28984b8ac966SJiri Pirko dev_err(&pdev->dev, "pci_request_regions failed\n"); 28994b8ac966SJiri Pirko goto err_pci_request_regions; 29004b8ac966SJiri Pirko } 29014b8ac966SJiri Pirko 29024b8ac966SJiri Pirko err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); 29034b8ac966SJiri Pirko if (!err) { 29044b8ac966SJiri Pirko err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); 29054b8ac966SJiri Pirko if (err) { 29064b8ac966SJiri Pirko dev_err(&pdev->dev, "pci_set_consistent_dma_mask failed\n"); 29074b8ac966SJiri Pirko goto err_pci_set_dma_mask; 29084b8ac966SJiri Pirko } 29094b8ac966SJiri Pirko } else { 29104b8ac966SJiri Pirko err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); 29114b8ac966SJiri Pirko if (err) { 29124b8ac966SJiri Pirko dev_err(&pdev->dev, "pci_set_dma_mask failed\n"); 29134b8ac966SJiri Pirko goto err_pci_set_dma_mask; 29144b8ac966SJiri Pirko } 29154b8ac966SJiri Pirko } 29164b8ac966SJiri Pirko 29174b8ac966SJiri Pirko if (pci_resource_len(pdev, 0) < ROCKER_PCI_BAR0_SIZE) { 29184b8ac966SJiri Pirko dev_err(&pdev->dev, "invalid PCI region size\n"); 29193122a92eSWei Yongjun err = -EINVAL; 29204b8ac966SJiri Pirko goto err_pci_resource_len_check; 29214b8ac966SJiri Pirko } 29224b8ac966SJiri Pirko 29234b8ac966SJiri Pirko rocker->hw_addr = ioremap(pci_resource_start(pdev, 0), 29244b8ac966SJiri Pirko pci_resource_len(pdev, 0)); 29254b8ac966SJiri Pirko if (!rocker->hw_addr) { 29264b8ac966SJiri Pirko dev_err(&pdev->dev, "ioremap failed\n"); 29274b8ac966SJiri Pirko err = -EIO; 29284b8ac966SJiri Pirko goto err_ioremap; 29294b8ac966SJiri Pirko } 29304b8ac966SJiri Pirko pci_set_master(pdev); 29314b8ac966SJiri Pirko 29324b8ac966SJiri Pirko rocker->pdev = pdev; 29334b8ac966SJiri Pirko pci_set_drvdata(pdev, rocker); 29344b8ac966SJiri Pirko 29354b8ac966SJiri Pirko rocker->port_count = rocker_read32(rocker, PORT_PHYS_COUNT); 29364b8ac966SJiri Pirko 29374b8ac966SJiri Pirko err = rocker_msix_init(rocker); 29384b8ac966SJiri Pirko if (err) { 29394b8ac966SJiri Pirko dev_err(&pdev->dev, "MSI-X init failed\n"); 29404b8ac966SJiri Pirko goto err_msix_init; 29414b8ac966SJiri Pirko } 29424b8ac966SJiri Pirko 29434b8ac966SJiri Pirko err = rocker_basic_hw_test(rocker); 29444b8ac966SJiri Pirko if (err) { 29454b8ac966SJiri Pirko dev_err(&pdev->dev, "basic hw test failed\n"); 29464b8ac966SJiri Pirko goto err_basic_hw_test; 29474b8ac966SJiri Pirko } 29484b8ac966SJiri Pirko 29494b8ac966SJiri Pirko rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET); 29504b8ac966SJiri Pirko 29514b8ac966SJiri Pirko err = rocker_dma_rings_init(rocker); 29524b8ac966SJiri Pirko if (err) 29534b8ac966SJiri Pirko goto err_dma_rings_init; 29544b8ac966SJiri Pirko 29554b8ac966SJiri Pirko err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), 29564b8ac966SJiri Pirko rocker_cmd_irq_handler, 0, 29574b8ac966SJiri Pirko rocker_driver_name, rocker); 29584b8ac966SJiri Pirko if (err) { 29594b8ac966SJiri Pirko dev_err(&pdev->dev, "cannot assign cmd irq\n"); 29604b8ac966SJiri Pirko goto err_request_cmd_irq; 29614b8ac966SJiri Pirko } 29624b8ac966SJiri Pirko 29634b8ac966SJiri Pirko err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), 29644b8ac966SJiri Pirko rocker_event_irq_handler, 0, 29654b8ac966SJiri Pirko rocker_driver_name, rocker); 29664b8ac966SJiri Pirko if (err) { 29674b8ac966SJiri Pirko dev_err(&pdev->dev, "cannot assign event irq\n"); 29684b8ac966SJiri Pirko goto err_request_event_irq; 29694b8ac966SJiri Pirko } 29704b8ac966SJiri Pirko 2971c1bb279cSIdo Schimmel rocker->rocker_owq = alloc_ordered_workqueue(rocker_driver_name, 2972c1bb279cSIdo Schimmel WQ_MEM_RECLAIM); 2973c1bb279cSIdo Schimmel if (!rocker->rocker_owq) { 2974c1bb279cSIdo Schimmel err = -ENOMEM; 2975c1bb279cSIdo Schimmel goto err_alloc_ordered_workqueue; 2976c1bb279cSIdo Schimmel } 2977c1bb279cSIdo Schimmel 2978a83165f0SJiri Pirko err = rocker_probe_ports(rocker); 2979a83165f0SJiri Pirko if (err) { 2980a83165f0SJiri Pirko dev_err(&pdev->dev, "failed to probe ports\n"); 2981a83165f0SJiri Pirko goto err_probe_ports; 2982a83165f0SJiri Pirko } 2983a83165f0SJiri Pirko 2984c3852ef7SIdo Schimmel /* Only FIBs pointing to our own netdevs are programmed into 2985c3852ef7SIdo Schimmel * the device, so no need to pass a callback. 2986c3852ef7SIdo Schimmel */ 298717f8be7dSIdo Schimmel rocker->fib_nb.notifier_call = rocker_router_fib_event; 2988c3852ef7SIdo Schimmel err = register_fib_notifier(&rocker->fib_nb, NULL); 2989c3852ef7SIdo Schimmel if (err) 2990c3852ef7SIdo Schimmel goto err_register_fib_notifier; 299117f8be7dSIdo Schimmel 2992726fd42fSArkadi Sharshevsky err = register_switchdev_notifier(&rocker_switchdev_notifier); 2993726fd42fSArkadi Sharshevsky if (err) { 2994726fd42fSArkadi Sharshevsky dev_err(&pdev->dev, "Failed to register switchdev notifier\n"); 2995726fd42fSArkadi Sharshevsky goto err_register_switchdev_notifier; 2996726fd42fSArkadi Sharshevsky } 2997726fd42fSArkadi Sharshevsky 2998c6fa35b2SPetr Machata nb = &rocker_switchdev_blocking_notifier; 2999c6fa35b2SPetr Machata err = register_switchdev_blocking_notifier(nb); 3000c6fa35b2SPetr Machata if (err) { 3001c6fa35b2SPetr Machata dev_err(&pdev->dev, "Failed to register switchdev blocking notifier\n"); 3002c6fa35b2SPetr Machata goto err_register_switchdev_blocking_notifier; 3003c6fa35b2SPetr Machata } 3004c6fa35b2SPetr Machata 30054b8ac966SJiri Pirko rocker->hw.id = rocker_read64(rocker, SWITCH_ID); 30064b8ac966SJiri Pirko 3007c8beb5b2SScott Feldman dev_info(&pdev->dev, "Rocker switch with id %*phN\n", 3008c8beb5b2SScott Feldman (int)sizeof(rocker->hw.id), &rocker->hw.id); 30094b8ac966SJiri Pirko 30104b8ac966SJiri Pirko return 0; 30114b8ac966SJiri Pirko 3012c6fa35b2SPetr Machata err_register_switchdev_blocking_notifier: 3013c6fa35b2SPetr Machata unregister_switchdev_notifier(&rocker_switchdev_notifier); 3014726fd42fSArkadi Sharshevsky err_register_switchdev_notifier: 301517f8be7dSIdo Schimmel unregister_fib_notifier(&rocker->fib_nb); 3016c3852ef7SIdo Schimmel err_register_fib_notifier: 3017a83165f0SJiri Pirko rocker_remove_ports(rocker); 3018a83165f0SJiri Pirko err_probe_ports: 3019c1bb279cSIdo Schimmel destroy_workqueue(rocker->rocker_owq); 3020c1bb279cSIdo Schimmel err_alloc_ordered_workqueue: 30214b8ac966SJiri Pirko free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker); 30224b8ac966SJiri Pirko err_request_event_irq: 30234b8ac966SJiri Pirko free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker); 30244b8ac966SJiri Pirko err_request_cmd_irq: 30254b8ac966SJiri Pirko rocker_dma_rings_fini(rocker); 30264b8ac966SJiri Pirko err_dma_rings_init: 30274b8ac966SJiri Pirko err_basic_hw_test: 30284b8ac966SJiri Pirko rocker_msix_fini(rocker); 30294b8ac966SJiri Pirko err_msix_init: 30304b8ac966SJiri Pirko iounmap(rocker->hw_addr); 30314b8ac966SJiri Pirko err_ioremap: 30324b8ac966SJiri Pirko err_pci_resource_len_check: 30334b8ac966SJiri Pirko err_pci_set_dma_mask: 30344b8ac966SJiri Pirko pci_release_regions(pdev); 30354b8ac966SJiri Pirko err_pci_request_regions: 30364b8ac966SJiri Pirko pci_disable_device(pdev); 30374b8ac966SJiri Pirko err_pci_enable_device: 30384b8ac966SJiri Pirko kfree(rocker); 30394b8ac966SJiri Pirko return err; 30404b8ac966SJiri Pirko } 30414b8ac966SJiri Pirko 30424b8ac966SJiri Pirko static void rocker_remove(struct pci_dev *pdev) 30434b8ac966SJiri Pirko { 30444b8ac966SJiri Pirko struct rocker *rocker = pci_get_drvdata(pdev); 3045c6fa35b2SPetr Machata struct notifier_block *nb; 3046c6fa35b2SPetr Machata 3047c6fa35b2SPetr Machata nb = &rocker_switchdev_blocking_notifier; 3048c6fa35b2SPetr Machata unregister_switchdev_blocking_notifier(nb); 30494b8ac966SJiri Pirko 3050726fd42fSArkadi Sharshevsky unregister_switchdev_notifier(&rocker_switchdev_notifier); 3051936bd486SJiri Pirko unregister_fib_notifier(&rocker->fib_nb); 3052a83165f0SJiri Pirko rocker_remove_ports(rocker); 30534b8ac966SJiri Pirko rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET); 3054c1bb279cSIdo Schimmel destroy_workqueue(rocker->rocker_owq); 30554b8ac966SJiri Pirko free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker); 30564b8ac966SJiri Pirko free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker); 30574b8ac966SJiri Pirko rocker_dma_rings_fini(rocker); 30584b8ac966SJiri Pirko rocker_msix_fini(rocker); 30594b8ac966SJiri Pirko iounmap(rocker->hw_addr); 30604b8ac966SJiri Pirko pci_release_regions(rocker->pdev); 30614b8ac966SJiri Pirko pci_disable_device(rocker->pdev); 30624b8ac966SJiri Pirko kfree(rocker); 30634b8ac966SJiri Pirko } 30644b8ac966SJiri Pirko 30654b8ac966SJiri Pirko static struct pci_driver rocker_pci_driver = { 30664b8ac966SJiri Pirko .name = rocker_driver_name, 30674b8ac966SJiri Pirko .id_table = rocker_pci_id_table, 30684b8ac966SJiri Pirko .probe = rocker_probe, 30694b8ac966SJiri Pirko .remove = rocker_remove, 30704b8ac966SJiri Pirko }; 30714b8ac966SJiri Pirko 30726c707945SScott Feldman /************************************ 30736c707945SScott Feldman * Net device notifier event handler 30746c707945SScott Feldman ************************************/ 30756c707945SScott Feldman 3076936bd486SJiri Pirko static bool rocker_port_dev_check_under(const struct net_device *dev, 3077936bd486SJiri Pirko struct rocker *rocker) 3078936bd486SJiri Pirko { 3079936bd486SJiri Pirko struct rocker_port *rocker_port; 3080936bd486SJiri Pirko 3081936bd486SJiri Pirko if (!rocker_port_dev_check(dev)) 3082936bd486SJiri Pirko return false; 3083936bd486SJiri Pirko 3084936bd486SJiri Pirko rocker_port = netdev_priv(dev); 3085936bd486SJiri Pirko if (rocker_port->rocker != rocker) 3086936bd486SJiri Pirko return false; 3087936bd486SJiri Pirko 3088936bd486SJiri Pirko return true; 3089936bd486SJiri Pirko } 3090936bd486SJiri Pirko 3091cf2d6740SDavid Ahern struct rocker_walk_data { 3092cf2d6740SDavid Ahern struct rocker *rocker; 3093cf2d6740SDavid Ahern struct rocker_port *port; 3094cf2d6740SDavid Ahern }; 3095cf2d6740SDavid Ahern 3096cf2d6740SDavid Ahern static int rocker_lower_dev_walk(struct net_device *lower_dev, void *_data) 3097cf2d6740SDavid Ahern { 3098cf2d6740SDavid Ahern struct rocker_walk_data *data = _data; 3099cf2d6740SDavid Ahern int ret = 0; 3100cf2d6740SDavid Ahern 3101cf2d6740SDavid Ahern if (rocker_port_dev_check_under(lower_dev, data->rocker)) { 3102cf2d6740SDavid Ahern data->port = netdev_priv(lower_dev); 3103cf2d6740SDavid Ahern ret = 1; 3104cf2d6740SDavid Ahern } 3105cf2d6740SDavid Ahern 3106cf2d6740SDavid Ahern return ret; 3107cf2d6740SDavid Ahern } 3108cf2d6740SDavid Ahern 3109936bd486SJiri Pirko struct rocker_port *rocker_port_dev_lower_find(struct net_device *dev, 3110936bd486SJiri Pirko struct rocker *rocker) 3111936bd486SJiri Pirko { 3112cf2d6740SDavid Ahern struct rocker_walk_data data; 3113936bd486SJiri Pirko 3114936bd486SJiri Pirko if (rocker_port_dev_check_under(dev, rocker)) 3115936bd486SJiri Pirko return netdev_priv(dev); 3116936bd486SJiri Pirko 3117cf2d6740SDavid Ahern data.rocker = rocker; 3118cf2d6740SDavid Ahern data.port = NULL; 3119cf2d6740SDavid Ahern netdev_walk_all_lower_dev(dev, rocker_lower_dev_walk, &data); 3120cf2d6740SDavid Ahern 3121cf2d6740SDavid Ahern return data.port; 3122936bd486SJiri Pirko } 3123936bd486SJiri Pirko 31246c707945SScott Feldman static int rocker_netdevice_event(struct notifier_block *unused, 31256c707945SScott Feldman unsigned long event, void *ptr) 31266c707945SScott Feldman { 3127686ed304SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 3128686ed304SJiri Pirko struct netdev_notifier_changeupper_info *info; 3129686ed304SJiri Pirko struct rocker_port *rocker_port; 31306c707945SScott Feldman int err; 31316c707945SScott Feldman 3132686ed304SJiri Pirko if (!rocker_port_dev_check(dev)) 3133686ed304SJiri Pirko return NOTIFY_DONE; 3134686ed304SJiri Pirko 31356c707945SScott Feldman switch (event) { 31366c707945SScott Feldman case NETDEV_CHANGEUPPER: 3137686ed304SJiri Pirko info = ptr; 3138686ed304SJiri Pirko if (!info->master) 3139686ed304SJiri Pirko goto out; 3140686ed304SJiri Pirko rocker_port = netdev_priv(dev); 3141686ed304SJiri Pirko if (info->linking) { 3142e420114eSJiri Pirko err = rocker_world_port_master_linked(rocker_port, 3143e420114eSJiri Pirko info->upper_dev); 3144e420114eSJiri Pirko if (err) 3145e420114eSJiri Pirko netdev_warn(dev, "failed to reflect master linked (err %d)\n", 3146e420114eSJiri Pirko err); 3147686ed304SJiri Pirko } else { 3148e420114eSJiri Pirko err = rocker_world_port_master_unlinked(rocker_port, 3149e420114eSJiri Pirko info->upper_dev); 3150e420114eSJiri Pirko if (err) 3151e420114eSJiri Pirko netdev_warn(dev, "failed to reflect master unlinked (err %d)\n", 3152e420114eSJiri Pirko err); 3153686ed304SJiri Pirko } 31546c707945SScott Feldman } 3155686ed304SJiri Pirko out: 31566c707945SScott Feldman return NOTIFY_DONE; 31576c707945SScott Feldman } 31586c707945SScott Feldman 31596c707945SScott Feldman static struct notifier_block rocker_netdevice_nb __read_mostly = { 31606c707945SScott Feldman .notifier_call = rocker_netdevice_event, 31616c707945SScott Feldman }; 31626c707945SScott Feldman 3163c1beeef7SScott Feldman /************************************ 3164c1beeef7SScott Feldman * Net event notifier event handler 3165c1beeef7SScott Feldman ************************************/ 3166c1beeef7SScott Feldman 3167c1beeef7SScott Feldman static int rocker_netevent_event(struct notifier_block *unused, 3168c1beeef7SScott Feldman unsigned long event, void *ptr) 3169c1beeef7SScott Feldman { 3170e420114eSJiri Pirko struct rocker_port *rocker_port; 3171c1beeef7SScott Feldman struct net_device *dev; 3172c1beeef7SScott Feldman struct neighbour *n = ptr; 3173c1beeef7SScott Feldman int err; 3174c1beeef7SScott Feldman 3175c1beeef7SScott Feldman switch (event) { 3176c1beeef7SScott Feldman case NETEVENT_NEIGH_UPDATE: 3177c1beeef7SScott Feldman if (n->tbl != &arp_tbl) 3178c1beeef7SScott Feldman return NOTIFY_DONE; 3179c1beeef7SScott Feldman dev = n->dev; 3180c1beeef7SScott Feldman if (!rocker_port_dev_check(dev)) 3181c1beeef7SScott Feldman return NOTIFY_DONE; 3182e420114eSJiri Pirko rocker_port = netdev_priv(dev); 3183e420114eSJiri Pirko err = rocker_world_port_neigh_update(rocker_port, n); 3184e420114eSJiri Pirko if (err) 3185e420114eSJiri Pirko netdev_warn(dev, "failed to handle neigh update (err %d)\n", 3186e420114eSJiri Pirko err); 3187c1beeef7SScott Feldman break; 3188c1beeef7SScott Feldman } 3189c1beeef7SScott Feldman 3190c1beeef7SScott Feldman return NOTIFY_DONE; 3191c1beeef7SScott Feldman } 3192c1beeef7SScott Feldman 3193c1beeef7SScott Feldman static struct notifier_block rocker_netevent_nb __read_mostly = { 3194c1beeef7SScott Feldman .notifier_call = rocker_netevent_event, 3195c1beeef7SScott Feldman }; 3196c1beeef7SScott Feldman 31974b8ac966SJiri Pirko /*********************** 31984b8ac966SJiri Pirko * Module init and exit 31994b8ac966SJiri Pirko ***********************/ 32004b8ac966SJiri Pirko 32014b8ac966SJiri Pirko static int __init rocker_module_init(void) 32024b8ac966SJiri Pirko { 32036c707945SScott Feldman int err; 32046c707945SScott Feldman 32056c707945SScott Feldman register_netdevice_notifier(&rocker_netdevice_nb); 3206c1beeef7SScott Feldman register_netevent_notifier(&rocker_netevent_nb); 32076c707945SScott Feldman err = pci_register_driver(&rocker_pci_driver); 32086c707945SScott Feldman if (err) 32096c707945SScott Feldman goto err_pci_register_driver; 32106c707945SScott Feldman return 0; 32116c707945SScott Feldman 32126c707945SScott Feldman err_pci_register_driver: 3213a076e6bfSGilad Ben-Yossef unregister_netevent_notifier(&rocker_netevent_nb); 32146c707945SScott Feldman unregister_netdevice_notifier(&rocker_netdevice_nb); 32156c707945SScott Feldman return err; 32164b8ac966SJiri Pirko } 32174b8ac966SJiri Pirko 32184b8ac966SJiri Pirko static void __exit rocker_module_exit(void) 32194b8ac966SJiri Pirko { 3220c1beeef7SScott Feldman unregister_netevent_notifier(&rocker_netevent_nb); 32216c707945SScott Feldman unregister_netdevice_notifier(&rocker_netdevice_nb); 32224b8ac966SJiri Pirko pci_unregister_driver(&rocker_pci_driver); 32234b8ac966SJiri Pirko } 32244b8ac966SJiri Pirko 32254b8ac966SJiri Pirko module_init(rocker_module_init); 32264b8ac966SJiri Pirko module_exit(rocker_module_exit); 32274b8ac966SJiri Pirko 32284b8ac966SJiri Pirko MODULE_LICENSE("GPL v2"); 32294b8ac966SJiri Pirko MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>"); 32304b8ac966SJiri Pirko MODULE_AUTHOR("Scott Feldman <sfeldma@gmail.com>"); 32314b8ac966SJiri Pirko MODULE_DESCRIPTION("Rocker switch device driver"); 32324b8ac966SJiri Pirko MODULE_DEVICE_TABLE(pci, rocker_pci_id_table); 3233