xref: /linux/drivers/net/ethernet/rocker/rocker_main.c (revision 2f4c53349961c8ca480193e47da4d44fdb8335a8)
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(&copy_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 			       &copy_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