12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
211ce2ba3SJiri Pirko /*
311ce2ba3SJiri Pirko  * drivers/net/ethernet/rocker/rocker.c - Rocker switch device driver
411ce2ba3SJiri Pirko  * Copyright (c) 2014-2016 Jiri Pirko <jiri@mellanox.com>
511ce2ba3SJiri Pirko  * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
611ce2ba3SJiri Pirko  */
711ce2ba3SJiri Pirko 
811ce2ba3SJiri Pirko #include <linux/kernel.h>
911ce2ba3SJiri Pirko #include <linux/module.h>
1011ce2ba3SJiri Pirko #include <linux/pci.h>
1111ce2ba3SJiri Pirko #include <linux/interrupt.h>
1211ce2ba3SJiri Pirko #include <linux/sched.h>
1311ce2ba3SJiri Pirko #include <linux/wait.h>
1411ce2ba3SJiri Pirko #include <linux/spinlock.h>
1511ce2ba3SJiri Pirko #include <linux/sort.h>
1611ce2ba3SJiri Pirko #include <linux/random.h>
1711ce2ba3SJiri Pirko #include <linux/netdevice.h>
1811ce2ba3SJiri Pirko #include <linux/skbuff.h>
1911ce2ba3SJiri Pirko #include <linux/socket.h>
2011ce2ba3SJiri Pirko #include <linux/etherdevice.h>
2111ce2ba3SJiri Pirko #include <linux/ethtool.h>
2211ce2ba3SJiri Pirko #include <linux/if_ether.h>
2311ce2ba3SJiri Pirko #include <linux/if_vlan.h>
2411ce2ba3SJiri Pirko #include <linux/if_bridge.h>
2511ce2ba3SJiri Pirko #include <linux/bitops.h>
2611ce2ba3SJiri Pirko #include <linux/ctype.h>
27c1bb279cSIdo Schimmel #include <linux/workqueue.h>
2811ce2ba3SJiri Pirko #include <net/switchdev.h>
2911ce2ba3SJiri Pirko #include <net/rtnetlink.h>
3011ce2ba3SJiri Pirko #include <net/netevent.h>
3111ce2ba3SJiri Pirko #include <net/arp.h>
325d7bfd14SIdo Schimmel #include <net/fib_rules.h>
3304b1d4e5SIdo Schimmel #include <net/fib_notifier.h>
3411ce2ba3SJiri Pirko #include <linux/io-64-nonatomic-lo-hi.h>
3511ce2ba3SJiri Pirko #include <generated/utsrelease.h>
3611ce2ba3SJiri Pirko 
3711ce2ba3SJiri Pirko #include "rocker_hw.h"
38de152192SJiri Pirko #include "rocker.h"
39de152192SJiri Pirko #include "rocker_tlv.h"
4011ce2ba3SJiri Pirko 
4111ce2ba3SJiri Pirko static const char rocker_driver_name[] = "rocker";
4211ce2ba3SJiri Pirko 
4311ce2ba3SJiri Pirko static const struct pci_device_id rocker_pci_id_table[] = {
4411ce2ba3SJiri Pirko 	{PCI_VDEVICE(REDHAT, PCI_DEVICE_ID_REDHAT_ROCKER), 0},
4511ce2ba3SJiri Pirko 	{0, }
4611ce2ba3SJiri Pirko };
4711ce2ba3SJiri Pirko 
4811ce2ba3SJiri Pirko struct rocker_wait {
4911ce2ba3SJiri Pirko 	wait_queue_head_t wait;
5011ce2ba3SJiri Pirko 	bool done;
5111ce2ba3SJiri Pirko 	bool nowait;
5211ce2ba3SJiri Pirko };
5311ce2ba3SJiri Pirko 
rocker_wait_reset(struct rocker_wait * wait)5411ce2ba3SJiri Pirko static void rocker_wait_reset(struct rocker_wait *wait)
5511ce2ba3SJiri Pirko {
5611ce2ba3SJiri Pirko 	wait->done = false;
5711ce2ba3SJiri Pirko 	wait->nowait = false;
5811ce2ba3SJiri Pirko }
5911ce2ba3SJiri Pirko 
rocker_wait_init(struct rocker_wait * wait)6011ce2ba3SJiri Pirko static void rocker_wait_init(struct rocker_wait *wait)
6111ce2ba3SJiri Pirko {
6211ce2ba3SJiri Pirko 	init_waitqueue_head(&wait->wait);
6311ce2ba3SJiri Pirko 	rocker_wait_reset(wait);
6411ce2ba3SJiri Pirko }
6511ce2ba3SJiri Pirko 
rocker_wait_create(void)66ca0a5f2aSJiri Pirko static struct rocker_wait *rocker_wait_create(void)
6711ce2ba3SJiri Pirko {
6811ce2ba3SJiri Pirko 	struct rocker_wait *wait;
6911ce2ba3SJiri Pirko 
70ca0a5f2aSJiri Pirko 	wait = kzalloc(sizeof(*wait), GFP_KERNEL);
7111ce2ba3SJiri Pirko 	if (!wait)
7211ce2ba3SJiri Pirko 		return NULL;
7311ce2ba3SJiri Pirko 	return wait;
7411ce2ba3SJiri Pirko }
7511ce2ba3SJiri Pirko 
rocker_wait_destroy(struct rocker_wait * wait)76ca0a5f2aSJiri Pirko static void rocker_wait_destroy(struct rocker_wait *wait)
7711ce2ba3SJiri Pirko {
78ca0a5f2aSJiri Pirko 	kfree(wait);
7911ce2ba3SJiri Pirko }
8011ce2ba3SJiri Pirko 
rocker_wait_event_timeout(struct rocker_wait * wait,unsigned long timeout)8111ce2ba3SJiri Pirko static bool rocker_wait_event_timeout(struct rocker_wait *wait,
8211ce2ba3SJiri Pirko 				      unsigned long timeout)
8311ce2ba3SJiri Pirko {
8411ce2ba3SJiri Pirko 	wait_event_timeout(wait->wait, wait->done, HZ / 10);
8511ce2ba3SJiri Pirko 	if (!wait->done)
8611ce2ba3SJiri Pirko 		return false;
8711ce2ba3SJiri Pirko 	return true;
8811ce2ba3SJiri Pirko }
8911ce2ba3SJiri Pirko 
rocker_wait_wake_up(struct rocker_wait * wait)9011ce2ba3SJiri Pirko static void rocker_wait_wake_up(struct rocker_wait *wait)
9111ce2ba3SJiri Pirko {
9211ce2ba3SJiri Pirko 	wait->done = true;
9311ce2ba3SJiri Pirko 	wake_up(&wait->wait);
9411ce2ba3SJiri Pirko }
9511ce2ba3SJiri Pirko 
rocker_msix_vector(const struct rocker * rocker,unsigned int vector)9611ce2ba3SJiri Pirko static u32 rocker_msix_vector(const struct rocker *rocker, unsigned int vector)
9711ce2ba3SJiri Pirko {
9811ce2ba3SJiri Pirko 	return rocker->msix_entries[vector].vector;
9911ce2ba3SJiri Pirko }
10011ce2ba3SJiri Pirko 
rocker_msix_tx_vector(const struct rocker_port * rocker_port)10111ce2ba3SJiri Pirko static u32 rocker_msix_tx_vector(const struct rocker_port *rocker_port)
10211ce2ba3SJiri Pirko {
10311ce2ba3SJiri Pirko 	return rocker_msix_vector(rocker_port->rocker,
10411ce2ba3SJiri Pirko 				  ROCKER_MSIX_VEC_TX(rocker_port->port_number));
10511ce2ba3SJiri Pirko }
10611ce2ba3SJiri Pirko 
rocker_msix_rx_vector(const struct rocker_port * rocker_port)10711ce2ba3SJiri Pirko static u32 rocker_msix_rx_vector(const struct rocker_port *rocker_port)
10811ce2ba3SJiri Pirko {
10911ce2ba3SJiri Pirko 	return rocker_msix_vector(rocker_port->rocker,
11011ce2ba3SJiri Pirko 				  ROCKER_MSIX_VEC_RX(rocker_port->port_number));
11111ce2ba3SJiri Pirko }
11211ce2ba3SJiri Pirko 
11311ce2ba3SJiri Pirko #define rocker_write32(rocker, reg, val)	\
11411ce2ba3SJiri Pirko 	writel((val), (rocker)->hw_addr + (ROCKER_ ## reg))
11511ce2ba3SJiri Pirko #define rocker_read32(rocker, reg)	\
11611ce2ba3SJiri Pirko 	readl((rocker)->hw_addr + (ROCKER_ ## reg))
11711ce2ba3SJiri Pirko #define rocker_write64(rocker, reg, val)	\
11811ce2ba3SJiri Pirko 	writeq((val), (rocker)->hw_addr + (ROCKER_ ## reg))
11911ce2ba3SJiri Pirko #define rocker_read64(rocker, reg)	\
12011ce2ba3SJiri Pirko 	readq((rocker)->hw_addr + (ROCKER_ ## reg))
12111ce2ba3SJiri Pirko 
12211ce2ba3SJiri Pirko /*****************************
12311ce2ba3SJiri Pirko  * HW basic testing functions
12411ce2ba3SJiri Pirko  *****************************/
12511ce2ba3SJiri Pirko 
rocker_reg_test(const struct rocker * rocker)12611ce2ba3SJiri Pirko static int rocker_reg_test(const struct rocker *rocker)
12711ce2ba3SJiri Pirko {
12811ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
12911ce2ba3SJiri Pirko 	u64 test_reg;
13011ce2ba3SJiri Pirko 	u64 rnd;
13111ce2ba3SJiri Pirko 
132a251c17aSJason A. Donenfeld 	rnd = get_random_u32();
13311ce2ba3SJiri Pirko 	rnd >>= 1;
13411ce2ba3SJiri Pirko 	rocker_write32(rocker, TEST_REG, rnd);
13511ce2ba3SJiri Pirko 	test_reg = rocker_read32(rocker, TEST_REG);
13611ce2ba3SJiri Pirko 	if (test_reg != rnd * 2) {
13711ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "unexpected 32bit register value %08llx, expected %08llx\n",
13811ce2ba3SJiri Pirko 			test_reg, rnd * 2);
13911ce2ba3SJiri Pirko 		return -EIO;
14011ce2ba3SJiri Pirko 	}
14111ce2ba3SJiri Pirko 
142a251c17aSJason A. Donenfeld 	rnd = get_random_u32();
14311ce2ba3SJiri Pirko 	rnd <<= 31;
144a251c17aSJason A. Donenfeld 	rnd |= get_random_u32();
14511ce2ba3SJiri Pirko 	rocker_write64(rocker, TEST_REG64, rnd);
14611ce2ba3SJiri Pirko 	test_reg = rocker_read64(rocker, TEST_REG64);
14711ce2ba3SJiri Pirko 	if (test_reg != rnd * 2) {
14811ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "unexpected 64bit register value %16llx, expected %16llx\n",
14911ce2ba3SJiri Pirko 			test_reg, rnd * 2);
15011ce2ba3SJiri Pirko 		return -EIO;
15111ce2ba3SJiri Pirko 	}
15211ce2ba3SJiri Pirko 
15311ce2ba3SJiri Pirko 	return 0;
15411ce2ba3SJiri Pirko }
15511ce2ba3SJiri Pirko 
rocker_dma_test_one(const struct rocker * rocker,struct rocker_wait * wait,u32 test_type,dma_addr_t dma_handle,const unsigned char * buf,const unsigned char * expect,size_t size)15611ce2ba3SJiri Pirko static int rocker_dma_test_one(const struct rocker *rocker,
15711ce2ba3SJiri Pirko 			       struct rocker_wait *wait, u32 test_type,
15811ce2ba3SJiri Pirko 			       dma_addr_t dma_handle, const unsigned char *buf,
15911ce2ba3SJiri Pirko 			       const unsigned char *expect, size_t size)
16011ce2ba3SJiri Pirko {
16111ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
16211ce2ba3SJiri Pirko 	int i;
16311ce2ba3SJiri Pirko 
16411ce2ba3SJiri Pirko 	rocker_wait_reset(wait);
16511ce2ba3SJiri Pirko 	rocker_write32(rocker, TEST_DMA_CTRL, test_type);
16611ce2ba3SJiri Pirko 
16711ce2ba3SJiri Pirko 	if (!rocker_wait_event_timeout(wait, HZ / 10)) {
16811ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "no interrupt received within a timeout\n");
16911ce2ba3SJiri Pirko 		return -EIO;
17011ce2ba3SJiri Pirko 	}
17111ce2ba3SJiri Pirko 
17211ce2ba3SJiri Pirko 	for (i = 0; i < size; i++) {
17311ce2ba3SJiri Pirko 		if (buf[i] != expect[i]) {
17411ce2ba3SJiri Pirko 			dev_err(&pdev->dev, "unexpected memory content %02x at byte %x\n, %02x expected",
17511ce2ba3SJiri Pirko 				buf[i], i, expect[i]);
17611ce2ba3SJiri Pirko 			return -EIO;
17711ce2ba3SJiri Pirko 		}
17811ce2ba3SJiri Pirko 	}
17911ce2ba3SJiri Pirko 	return 0;
18011ce2ba3SJiri Pirko }
18111ce2ba3SJiri Pirko 
18211ce2ba3SJiri Pirko #define ROCKER_TEST_DMA_BUF_SIZE (PAGE_SIZE * 4)
18311ce2ba3SJiri Pirko #define ROCKER_TEST_DMA_FILL_PATTERN 0x96
18411ce2ba3SJiri Pirko 
rocker_dma_test_offset(const struct rocker * rocker,struct rocker_wait * wait,int offset)18511ce2ba3SJiri Pirko static int rocker_dma_test_offset(const struct rocker *rocker,
18611ce2ba3SJiri Pirko 				  struct rocker_wait *wait, int offset)
18711ce2ba3SJiri Pirko {
18811ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
18911ce2ba3SJiri Pirko 	unsigned char *alloc;
19011ce2ba3SJiri Pirko 	unsigned char *buf;
19111ce2ba3SJiri Pirko 	unsigned char *expect;
19211ce2ba3SJiri Pirko 	dma_addr_t dma_handle;
19311ce2ba3SJiri Pirko 	int i;
19411ce2ba3SJiri Pirko 	int err;
19511ce2ba3SJiri Pirko 
19611ce2ba3SJiri Pirko 	alloc = kzalloc(ROCKER_TEST_DMA_BUF_SIZE * 2 + offset,
19711ce2ba3SJiri Pirko 			GFP_KERNEL | GFP_DMA);
19811ce2ba3SJiri Pirko 	if (!alloc)
19911ce2ba3SJiri Pirko 		return -ENOMEM;
20011ce2ba3SJiri Pirko 	buf = alloc + offset;
20111ce2ba3SJiri Pirko 	expect = buf + ROCKER_TEST_DMA_BUF_SIZE;
20211ce2ba3SJiri Pirko 
203c68d0cebSChristophe JAILLET 	dma_handle = dma_map_single(&pdev->dev, buf, ROCKER_TEST_DMA_BUF_SIZE,
204c68d0cebSChristophe JAILLET 				    DMA_BIDIRECTIONAL);
205c68d0cebSChristophe JAILLET 	if (dma_mapping_error(&pdev->dev, dma_handle)) {
20611ce2ba3SJiri Pirko 		err = -EIO;
20711ce2ba3SJiri Pirko 		goto free_alloc;
20811ce2ba3SJiri Pirko 	}
20911ce2ba3SJiri Pirko 
21011ce2ba3SJiri Pirko 	rocker_write64(rocker, TEST_DMA_ADDR, dma_handle);
21111ce2ba3SJiri Pirko 	rocker_write32(rocker, TEST_DMA_SIZE, ROCKER_TEST_DMA_BUF_SIZE);
21211ce2ba3SJiri Pirko 
21311ce2ba3SJiri Pirko 	memset(expect, ROCKER_TEST_DMA_FILL_PATTERN, ROCKER_TEST_DMA_BUF_SIZE);
21411ce2ba3SJiri Pirko 	err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_FILL,
21511ce2ba3SJiri Pirko 				  dma_handle, buf, expect,
21611ce2ba3SJiri Pirko 				  ROCKER_TEST_DMA_BUF_SIZE);
21711ce2ba3SJiri Pirko 	if (err)
21811ce2ba3SJiri Pirko 		goto unmap;
21911ce2ba3SJiri Pirko 
22011ce2ba3SJiri Pirko 	memset(expect, 0, ROCKER_TEST_DMA_BUF_SIZE);
22111ce2ba3SJiri Pirko 	err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_CLEAR,
22211ce2ba3SJiri Pirko 				  dma_handle, buf, expect,
22311ce2ba3SJiri Pirko 				  ROCKER_TEST_DMA_BUF_SIZE);
22411ce2ba3SJiri Pirko 	if (err)
22511ce2ba3SJiri Pirko 		goto unmap;
22611ce2ba3SJiri Pirko 
227*197173dbSJason A. Donenfeld 	get_random_bytes(buf, ROCKER_TEST_DMA_BUF_SIZE);
22811ce2ba3SJiri Pirko 	for (i = 0; i < ROCKER_TEST_DMA_BUF_SIZE; i++)
22911ce2ba3SJiri Pirko 		expect[i] = ~buf[i];
23011ce2ba3SJiri Pirko 	err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_INVERT,
23111ce2ba3SJiri Pirko 				  dma_handle, buf, expect,
23211ce2ba3SJiri Pirko 				  ROCKER_TEST_DMA_BUF_SIZE);
23311ce2ba3SJiri Pirko 	if (err)
23411ce2ba3SJiri Pirko 		goto unmap;
23511ce2ba3SJiri Pirko 
23611ce2ba3SJiri Pirko unmap:
237c68d0cebSChristophe JAILLET 	dma_unmap_single(&pdev->dev, dma_handle, ROCKER_TEST_DMA_BUF_SIZE,
238c68d0cebSChristophe JAILLET 			 DMA_BIDIRECTIONAL);
23911ce2ba3SJiri Pirko free_alloc:
24011ce2ba3SJiri Pirko 	kfree(alloc);
24111ce2ba3SJiri Pirko 
24211ce2ba3SJiri Pirko 	return err;
24311ce2ba3SJiri Pirko }
24411ce2ba3SJiri Pirko 
rocker_dma_test(const struct rocker * rocker,struct rocker_wait * wait)24511ce2ba3SJiri Pirko static int rocker_dma_test(const struct rocker *rocker,
24611ce2ba3SJiri Pirko 			   struct rocker_wait *wait)
24711ce2ba3SJiri Pirko {
24811ce2ba3SJiri Pirko 	int i;
24911ce2ba3SJiri Pirko 	int err;
25011ce2ba3SJiri Pirko 
25111ce2ba3SJiri Pirko 	for (i = 0; i < 8; i++) {
25211ce2ba3SJiri Pirko 		err = rocker_dma_test_offset(rocker, wait, i);
25311ce2ba3SJiri Pirko 		if (err)
25411ce2ba3SJiri Pirko 			return err;
25511ce2ba3SJiri Pirko 	}
25611ce2ba3SJiri Pirko 	return 0;
25711ce2ba3SJiri Pirko }
25811ce2ba3SJiri Pirko 
rocker_test_irq_handler(int irq,void * dev_id)25911ce2ba3SJiri Pirko static irqreturn_t rocker_test_irq_handler(int irq, void *dev_id)
26011ce2ba3SJiri Pirko {
26111ce2ba3SJiri Pirko 	struct rocker_wait *wait = dev_id;
26211ce2ba3SJiri Pirko 
26311ce2ba3SJiri Pirko 	rocker_wait_wake_up(wait);
26411ce2ba3SJiri Pirko 
26511ce2ba3SJiri Pirko 	return IRQ_HANDLED;
26611ce2ba3SJiri Pirko }
26711ce2ba3SJiri Pirko 
rocker_basic_hw_test(const struct rocker * rocker)26811ce2ba3SJiri Pirko static int rocker_basic_hw_test(const struct rocker *rocker)
26911ce2ba3SJiri Pirko {
27011ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
27111ce2ba3SJiri Pirko 	struct rocker_wait wait;
27211ce2ba3SJiri Pirko 	int err;
27311ce2ba3SJiri Pirko 
27411ce2ba3SJiri Pirko 	err = rocker_reg_test(rocker);
27511ce2ba3SJiri Pirko 	if (err) {
27611ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "reg test failed\n");
27711ce2ba3SJiri Pirko 		return err;
27811ce2ba3SJiri Pirko 	}
27911ce2ba3SJiri Pirko 
28011ce2ba3SJiri Pirko 	err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_TEST),
28111ce2ba3SJiri Pirko 			  rocker_test_irq_handler, 0,
28211ce2ba3SJiri Pirko 			  rocker_driver_name, &wait);
28311ce2ba3SJiri Pirko 	if (err) {
28411ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "cannot assign test irq\n");
28511ce2ba3SJiri Pirko 		return err;
28611ce2ba3SJiri Pirko 	}
28711ce2ba3SJiri Pirko 
28811ce2ba3SJiri Pirko 	rocker_wait_init(&wait);
28911ce2ba3SJiri Pirko 	rocker_write32(rocker, TEST_IRQ, ROCKER_MSIX_VEC_TEST);
29011ce2ba3SJiri Pirko 
29111ce2ba3SJiri Pirko 	if (!rocker_wait_event_timeout(&wait, HZ / 10)) {
29211ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "no interrupt received within a timeout\n");
29311ce2ba3SJiri Pirko 		err = -EIO;
29411ce2ba3SJiri Pirko 		goto free_irq;
29511ce2ba3SJiri Pirko 	}
29611ce2ba3SJiri Pirko 
29711ce2ba3SJiri Pirko 	err = rocker_dma_test(rocker, &wait);
29811ce2ba3SJiri Pirko 	if (err)
29911ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "dma test failed\n");
30011ce2ba3SJiri Pirko 
30111ce2ba3SJiri Pirko free_irq:
30211ce2ba3SJiri Pirko 	free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_TEST), &wait);
30311ce2ba3SJiri Pirko 	return err;
30411ce2ba3SJiri Pirko }
30511ce2ba3SJiri Pirko 
30611ce2ba3SJiri Pirko /******************************************
30711ce2ba3SJiri Pirko  * DMA rings and descriptors manipulations
30811ce2ba3SJiri Pirko  ******************************************/
30911ce2ba3SJiri Pirko 
__pos_inc(u32 pos,size_t limit)31011ce2ba3SJiri Pirko static u32 __pos_inc(u32 pos, size_t limit)
31111ce2ba3SJiri Pirko {
31211ce2ba3SJiri Pirko 	return ++pos == limit ? 0 : pos;
31311ce2ba3SJiri Pirko }
31411ce2ba3SJiri Pirko 
rocker_desc_err(const struct rocker_desc_info * desc_info)31511ce2ba3SJiri Pirko static int rocker_desc_err(const struct rocker_desc_info *desc_info)
31611ce2ba3SJiri Pirko {
31711ce2ba3SJiri Pirko 	int err = desc_info->desc->comp_err & ~ROCKER_DMA_DESC_COMP_ERR_GEN;
31811ce2ba3SJiri Pirko 
31911ce2ba3SJiri Pirko 	switch (err) {
32011ce2ba3SJiri Pirko 	case ROCKER_OK:
32111ce2ba3SJiri Pirko 		return 0;
32211ce2ba3SJiri Pirko 	case -ROCKER_ENOENT:
32311ce2ba3SJiri Pirko 		return -ENOENT;
32411ce2ba3SJiri Pirko 	case -ROCKER_ENXIO:
32511ce2ba3SJiri Pirko 		return -ENXIO;
32611ce2ba3SJiri Pirko 	case -ROCKER_ENOMEM:
32711ce2ba3SJiri Pirko 		return -ENOMEM;
32811ce2ba3SJiri Pirko 	case -ROCKER_EEXIST:
32911ce2ba3SJiri Pirko 		return -EEXIST;
33011ce2ba3SJiri Pirko 	case -ROCKER_EINVAL:
33111ce2ba3SJiri Pirko 		return -EINVAL;
33211ce2ba3SJiri Pirko 	case -ROCKER_EMSGSIZE:
33311ce2ba3SJiri Pirko 		return -EMSGSIZE;
33411ce2ba3SJiri Pirko 	case -ROCKER_ENOTSUP:
33511ce2ba3SJiri Pirko 		return -EOPNOTSUPP;
33611ce2ba3SJiri Pirko 	case -ROCKER_ENOBUFS:
33711ce2ba3SJiri Pirko 		return -ENOBUFS;
33811ce2ba3SJiri Pirko 	}
33911ce2ba3SJiri Pirko 
34011ce2ba3SJiri Pirko 	return -EINVAL;
34111ce2ba3SJiri Pirko }
34211ce2ba3SJiri Pirko 
rocker_desc_gen_clear(const struct rocker_desc_info * desc_info)34311ce2ba3SJiri Pirko static void rocker_desc_gen_clear(const struct rocker_desc_info *desc_info)
34411ce2ba3SJiri Pirko {
34511ce2ba3SJiri Pirko 	desc_info->desc->comp_err &= ~ROCKER_DMA_DESC_COMP_ERR_GEN;
34611ce2ba3SJiri Pirko }
34711ce2ba3SJiri Pirko 
rocker_desc_gen(const struct rocker_desc_info * desc_info)34811ce2ba3SJiri Pirko static bool rocker_desc_gen(const struct rocker_desc_info *desc_info)
34911ce2ba3SJiri Pirko {
35011ce2ba3SJiri Pirko 	u32 comp_err = desc_info->desc->comp_err;
35111ce2ba3SJiri Pirko 
35211ce2ba3SJiri Pirko 	return comp_err & ROCKER_DMA_DESC_COMP_ERR_GEN ? true : false;
35311ce2ba3SJiri Pirko }
35411ce2ba3SJiri Pirko 
35511ce2ba3SJiri Pirko static void *
rocker_desc_cookie_ptr_get(const struct rocker_desc_info * desc_info)35611ce2ba3SJiri Pirko rocker_desc_cookie_ptr_get(const struct rocker_desc_info *desc_info)
35711ce2ba3SJiri Pirko {
35811ce2ba3SJiri Pirko 	return (void *)(uintptr_t)desc_info->desc->cookie;
35911ce2ba3SJiri Pirko }
36011ce2ba3SJiri Pirko 
rocker_desc_cookie_ptr_set(const struct rocker_desc_info * desc_info,void * ptr)36111ce2ba3SJiri Pirko static void rocker_desc_cookie_ptr_set(const struct rocker_desc_info *desc_info,
36211ce2ba3SJiri Pirko 				       void *ptr)
36311ce2ba3SJiri Pirko {
36411ce2ba3SJiri Pirko 	desc_info->desc->cookie = (uintptr_t) ptr;
36511ce2ba3SJiri Pirko }
36611ce2ba3SJiri Pirko 
36711ce2ba3SJiri Pirko static struct rocker_desc_info *
rocker_desc_head_get(const struct rocker_dma_ring_info * info)36811ce2ba3SJiri Pirko rocker_desc_head_get(const struct rocker_dma_ring_info *info)
36911ce2ba3SJiri Pirko {
3709333f207SYueHaibing 	struct rocker_desc_info *desc_info;
37111ce2ba3SJiri Pirko 	u32 head = __pos_inc(info->head, info->size);
37211ce2ba3SJiri Pirko 
37311ce2ba3SJiri Pirko 	desc_info = &info->desc_info[info->head];
37411ce2ba3SJiri Pirko 	if (head == info->tail)
37511ce2ba3SJiri Pirko 		return NULL; /* ring full */
37611ce2ba3SJiri Pirko 	desc_info->tlv_size = 0;
37711ce2ba3SJiri Pirko 	return desc_info;
37811ce2ba3SJiri Pirko }
37911ce2ba3SJiri Pirko 
rocker_desc_commit(const struct rocker_desc_info * desc_info)38011ce2ba3SJiri Pirko static void rocker_desc_commit(const struct rocker_desc_info *desc_info)
38111ce2ba3SJiri Pirko {
38211ce2ba3SJiri Pirko 	desc_info->desc->buf_size = desc_info->data_size;
38311ce2ba3SJiri Pirko 	desc_info->desc->tlv_size = desc_info->tlv_size;
38411ce2ba3SJiri Pirko }
38511ce2ba3SJiri Pirko 
rocker_desc_head_set(const struct rocker * rocker,struct rocker_dma_ring_info * info,const struct rocker_desc_info * desc_info)38611ce2ba3SJiri Pirko static void rocker_desc_head_set(const struct rocker *rocker,
38711ce2ba3SJiri Pirko 				 struct rocker_dma_ring_info *info,
38811ce2ba3SJiri Pirko 				 const struct rocker_desc_info *desc_info)
38911ce2ba3SJiri Pirko {
39011ce2ba3SJiri Pirko 	u32 head = __pos_inc(info->head, info->size);
39111ce2ba3SJiri Pirko 
39211ce2ba3SJiri Pirko 	BUG_ON(head == info->tail);
39311ce2ba3SJiri Pirko 	rocker_desc_commit(desc_info);
39411ce2ba3SJiri Pirko 	info->head = head;
39511ce2ba3SJiri Pirko 	rocker_write32(rocker, DMA_DESC_HEAD(info->type), head);
39611ce2ba3SJiri Pirko }
39711ce2ba3SJiri Pirko 
39811ce2ba3SJiri Pirko static struct rocker_desc_info *
rocker_desc_tail_get(struct rocker_dma_ring_info * info)39911ce2ba3SJiri Pirko rocker_desc_tail_get(struct rocker_dma_ring_info *info)
40011ce2ba3SJiri Pirko {
4019333f207SYueHaibing 	struct rocker_desc_info *desc_info;
40211ce2ba3SJiri Pirko 
40311ce2ba3SJiri Pirko 	if (info->tail == info->head)
40411ce2ba3SJiri Pirko 		return NULL; /* nothing to be done between head and tail */
40511ce2ba3SJiri Pirko 	desc_info = &info->desc_info[info->tail];
40611ce2ba3SJiri Pirko 	if (!rocker_desc_gen(desc_info))
40711ce2ba3SJiri Pirko 		return NULL; /* gen bit not set, desc is not ready yet */
40811ce2ba3SJiri Pirko 	info->tail = __pos_inc(info->tail, info->size);
40911ce2ba3SJiri Pirko 	desc_info->tlv_size = desc_info->desc->tlv_size;
41011ce2ba3SJiri Pirko 	return desc_info;
41111ce2ba3SJiri Pirko }
41211ce2ba3SJiri Pirko 
rocker_dma_ring_credits_set(const struct rocker * rocker,const struct rocker_dma_ring_info * info,u32 credits)41311ce2ba3SJiri Pirko static void rocker_dma_ring_credits_set(const struct rocker *rocker,
41411ce2ba3SJiri Pirko 					const struct rocker_dma_ring_info *info,
41511ce2ba3SJiri Pirko 					u32 credits)
41611ce2ba3SJiri Pirko {
41711ce2ba3SJiri Pirko 	if (credits)
41811ce2ba3SJiri Pirko 		rocker_write32(rocker, DMA_DESC_CREDITS(info->type), credits);
41911ce2ba3SJiri Pirko }
42011ce2ba3SJiri Pirko 
rocker_dma_ring_size_fix(size_t size)42111ce2ba3SJiri Pirko static unsigned long rocker_dma_ring_size_fix(size_t size)
42211ce2ba3SJiri Pirko {
42311ce2ba3SJiri Pirko 	return max(ROCKER_DMA_SIZE_MIN,
42411ce2ba3SJiri Pirko 		   min(roundup_pow_of_two(size), ROCKER_DMA_SIZE_MAX));
42511ce2ba3SJiri Pirko }
42611ce2ba3SJiri Pirko 
rocker_dma_ring_create(const struct rocker * rocker,unsigned int type,size_t size,struct rocker_dma_ring_info * info)42711ce2ba3SJiri Pirko static int rocker_dma_ring_create(const struct rocker *rocker,
42811ce2ba3SJiri Pirko 				  unsigned int type,
42911ce2ba3SJiri Pirko 				  size_t size,
43011ce2ba3SJiri Pirko 				  struct rocker_dma_ring_info *info)
43111ce2ba3SJiri Pirko {
43211ce2ba3SJiri Pirko 	int i;
43311ce2ba3SJiri Pirko 
43411ce2ba3SJiri Pirko 	BUG_ON(size != rocker_dma_ring_size_fix(size));
43511ce2ba3SJiri Pirko 	info->size = size;
43611ce2ba3SJiri Pirko 	info->type = type;
43711ce2ba3SJiri Pirko 	info->head = 0;
43811ce2ba3SJiri Pirko 	info->tail = 0;
43911ce2ba3SJiri Pirko 	info->desc_info = kcalloc(info->size, sizeof(*info->desc_info),
44011ce2ba3SJiri Pirko 				  GFP_KERNEL);
44111ce2ba3SJiri Pirko 	if (!info->desc_info)
44211ce2ba3SJiri Pirko 		return -ENOMEM;
44311ce2ba3SJiri Pirko 
444c68d0cebSChristophe JAILLET 	info->desc = dma_alloc_coherent(&rocker->pdev->dev,
44511ce2ba3SJiri Pirko 					info->size * sizeof(*info->desc),
446c68d0cebSChristophe JAILLET 					&info->mapaddr, GFP_KERNEL);
44711ce2ba3SJiri Pirko 	if (!info->desc) {
44811ce2ba3SJiri Pirko 		kfree(info->desc_info);
44911ce2ba3SJiri Pirko 		return -ENOMEM;
45011ce2ba3SJiri Pirko 	}
45111ce2ba3SJiri Pirko 
45211ce2ba3SJiri Pirko 	for (i = 0; i < info->size; i++)
45311ce2ba3SJiri Pirko 		info->desc_info[i].desc = &info->desc[i];
45411ce2ba3SJiri Pirko 
45511ce2ba3SJiri Pirko 	rocker_write32(rocker, DMA_DESC_CTRL(info->type),
45611ce2ba3SJiri Pirko 		       ROCKER_DMA_DESC_CTRL_RESET);
45711ce2ba3SJiri Pirko 	rocker_write64(rocker, DMA_DESC_ADDR(info->type), info->mapaddr);
45811ce2ba3SJiri Pirko 	rocker_write32(rocker, DMA_DESC_SIZE(info->type), info->size);
45911ce2ba3SJiri Pirko 
46011ce2ba3SJiri Pirko 	return 0;
46111ce2ba3SJiri Pirko }
46211ce2ba3SJiri Pirko 
rocker_dma_ring_destroy(const struct rocker * rocker,const struct rocker_dma_ring_info * info)46311ce2ba3SJiri Pirko static void rocker_dma_ring_destroy(const struct rocker *rocker,
46411ce2ba3SJiri Pirko 				    const struct rocker_dma_ring_info *info)
46511ce2ba3SJiri Pirko {
46611ce2ba3SJiri Pirko 	rocker_write64(rocker, DMA_DESC_ADDR(info->type), 0);
46711ce2ba3SJiri Pirko 
468c68d0cebSChristophe JAILLET 	dma_free_coherent(&rocker->pdev->dev,
469c68d0cebSChristophe JAILLET 			  info->size * sizeof(struct rocker_desc), info->desc,
470c68d0cebSChristophe JAILLET 			  info->mapaddr);
47111ce2ba3SJiri Pirko 	kfree(info->desc_info);
47211ce2ba3SJiri Pirko }
47311ce2ba3SJiri Pirko 
rocker_dma_ring_pass_to_producer(const struct rocker * rocker,struct rocker_dma_ring_info * info)47411ce2ba3SJiri Pirko static void rocker_dma_ring_pass_to_producer(const struct rocker *rocker,
47511ce2ba3SJiri Pirko 					     struct rocker_dma_ring_info *info)
47611ce2ba3SJiri Pirko {
47711ce2ba3SJiri Pirko 	int i;
47811ce2ba3SJiri Pirko 
47911ce2ba3SJiri Pirko 	BUG_ON(info->head || info->tail);
48011ce2ba3SJiri Pirko 
48111ce2ba3SJiri Pirko 	/* When ring is consumer, we need to advance head for each desc.
48211ce2ba3SJiri Pirko 	 * That tells hw that the desc is ready to be used by it.
48311ce2ba3SJiri Pirko 	 */
48411ce2ba3SJiri Pirko 	for (i = 0; i < info->size - 1; i++)
48511ce2ba3SJiri Pirko 		rocker_desc_head_set(rocker, info, &info->desc_info[i]);
48611ce2ba3SJiri Pirko 	rocker_desc_commit(&info->desc_info[i]);
48711ce2ba3SJiri Pirko }
48811ce2ba3SJiri Pirko 
rocker_dma_ring_bufs_alloc(const struct rocker * rocker,const struct rocker_dma_ring_info * info,int direction,size_t buf_size)48911ce2ba3SJiri Pirko static int rocker_dma_ring_bufs_alloc(const struct rocker *rocker,
49011ce2ba3SJiri Pirko 				      const struct rocker_dma_ring_info *info,
49111ce2ba3SJiri Pirko 				      int direction, size_t buf_size)
49211ce2ba3SJiri Pirko {
49311ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
49411ce2ba3SJiri Pirko 	int i;
49511ce2ba3SJiri Pirko 	int err;
49611ce2ba3SJiri Pirko 
49711ce2ba3SJiri Pirko 	for (i = 0; i < info->size; i++) {
49811ce2ba3SJiri Pirko 		struct rocker_desc_info *desc_info = &info->desc_info[i];
49911ce2ba3SJiri Pirko 		struct rocker_desc *desc = &info->desc[i];
50011ce2ba3SJiri Pirko 		dma_addr_t dma_handle;
50111ce2ba3SJiri Pirko 		char *buf;
50211ce2ba3SJiri Pirko 
50311ce2ba3SJiri Pirko 		buf = kzalloc(buf_size, GFP_KERNEL | GFP_DMA);
50411ce2ba3SJiri Pirko 		if (!buf) {
50511ce2ba3SJiri Pirko 			err = -ENOMEM;
50611ce2ba3SJiri Pirko 			goto rollback;
50711ce2ba3SJiri Pirko 		}
50811ce2ba3SJiri Pirko 
509c68d0cebSChristophe JAILLET 		dma_handle = dma_map_single(&pdev->dev, buf, buf_size,
510c68d0cebSChristophe JAILLET 					    direction);
511c68d0cebSChristophe JAILLET 		if (dma_mapping_error(&pdev->dev, dma_handle)) {
51211ce2ba3SJiri Pirko 			kfree(buf);
51311ce2ba3SJiri Pirko 			err = -EIO;
51411ce2ba3SJiri Pirko 			goto rollback;
51511ce2ba3SJiri Pirko 		}
51611ce2ba3SJiri Pirko 
51711ce2ba3SJiri Pirko 		desc_info->data = buf;
51811ce2ba3SJiri Pirko 		desc_info->data_size = buf_size;
51911ce2ba3SJiri Pirko 		dma_unmap_addr_set(desc_info, mapaddr, dma_handle);
52011ce2ba3SJiri Pirko 
52111ce2ba3SJiri Pirko 		desc->buf_addr = dma_handle;
52211ce2ba3SJiri Pirko 		desc->buf_size = buf_size;
52311ce2ba3SJiri Pirko 	}
52411ce2ba3SJiri Pirko 	return 0;
52511ce2ba3SJiri Pirko 
52611ce2ba3SJiri Pirko rollback:
52711ce2ba3SJiri Pirko 	for (i--; i >= 0; i--) {
52811ce2ba3SJiri Pirko 		const struct rocker_desc_info *desc_info = &info->desc_info[i];
52911ce2ba3SJiri Pirko 
530c68d0cebSChristophe JAILLET 		dma_unmap_single(&pdev->dev,
531c68d0cebSChristophe JAILLET 				 dma_unmap_addr(desc_info, mapaddr),
53211ce2ba3SJiri Pirko 				 desc_info->data_size, direction);
53311ce2ba3SJiri Pirko 		kfree(desc_info->data);
53411ce2ba3SJiri Pirko 	}
53511ce2ba3SJiri Pirko 	return err;
53611ce2ba3SJiri Pirko }
53711ce2ba3SJiri Pirko 
rocker_dma_ring_bufs_free(const struct rocker * rocker,const struct rocker_dma_ring_info * info,int direction)53811ce2ba3SJiri Pirko static void rocker_dma_ring_bufs_free(const struct rocker *rocker,
53911ce2ba3SJiri Pirko 				      const struct rocker_dma_ring_info *info,
54011ce2ba3SJiri Pirko 				      int direction)
54111ce2ba3SJiri Pirko {
54211ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
54311ce2ba3SJiri Pirko 	int i;
54411ce2ba3SJiri Pirko 
54511ce2ba3SJiri Pirko 	for (i = 0; i < info->size; i++) {
54611ce2ba3SJiri Pirko 		const struct rocker_desc_info *desc_info = &info->desc_info[i];
54711ce2ba3SJiri Pirko 		struct rocker_desc *desc = &info->desc[i];
54811ce2ba3SJiri Pirko 
54911ce2ba3SJiri Pirko 		desc->buf_addr = 0;
55011ce2ba3SJiri Pirko 		desc->buf_size = 0;
551c68d0cebSChristophe JAILLET 		dma_unmap_single(&pdev->dev,
552c68d0cebSChristophe JAILLET 				 dma_unmap_addr(desc_info, mapaddr),
55311ce2ba3SJiri Pirko 				 desc_info->data_size, direction);
55411ce2ba3SJiri Pirko 		kfree(desc_info->data);
55511ce2ba3SJiri Pirko 	}
55611ce2ba3SJiri Pirko }
55711ce2ba3SJiri Pirko 
rocker_dma_cmd_ring_wait_alloc(struct rocker_desc_info * desc_info)558ca0a5f2aSJiri Pirko static int rocker_dma_cmd_ring_wait_alloc(struct rocker_desc_info *desc_info)
559ca0a5f2aSJiri Pirko {
560ca0a5f2aSJiri Pirko 	struct rocker_wait *wait;
561ca0a5f2aSJiri Pirko 
562ca0a5f2aSJiri Pirko 	wait = rocker_wait_create();
563ca0a5f2aSJiri Pirko 	if (!wait)
564ca0a5f2aSJiri Pirko 		return -ENOMEM;
565ca0a5f2aSJiri Pirko 	rocker_desc_cookie_ptr_set(desc_info, wait);
566ca0a5f2aSJiri Pirko 	return 0;
567ca0a5f2aSJiri Pirko }
568ca0a5f2aSJiri Pirko 
569ca0a5f2aSJiri Pirko static void
rocker_dma_cmd_ring_wait_free(const struct rocker_desc_info * desc_info)570ca0a5f2aSJiri Pirko rocker_dma_cmd_ring_wait_free(const struct rocker_desc_info *desc_info)
571ca0a5f2aSJiri Pirko {
572ca0a5f2aSJiri Pirko 	struct rocker_wait *wait = rocker_desc_cookie_ptr_get(desc_info);
573ca0a5f2aSJiri Pirko 
574ca0a5f2aSJiri Pirko 	rocker_wait_destroy(wait);
575ca0a5f2aSJiri Pirko }
576ca0a5f2aSJiri Pirko 
rocker_dma_cmd_ring_waits_alloc(const struct rocker * rocker)577ca0a5f2aSJiri Pirko static int rocker_dma_cmd_ring_waits_alloc(const struct rocker *rocker)
578ca0a5f2aSJiri Pirko {
579ca0a5f2aSJiri Pirko 	const struct rocker_dma_ring_info *cmd_ring = &rocker->cmd_ring;
580ca0a5f2aSJiri Pirko 	int i;
581ca0a5f2aSJiri Pirko 	int err;
582ca0a5f2aSJiri Pirko 
583ca0a5f2aSJiri Pirko 	for (i = 0; i < cmd_ring->size; i++) {
584ca0a5f2aSJiri Pirko 		err = rocker_dma_cmd_ring_wait_alloc(&cmd_ring->desc_info[i]);
585ca0a5f2aSJiri Pirko 		if (err)
586ca0a5f2aSJiri Pirko 			goto rollback;
587ca0a5f2aSJiri Pirko 	}
588ca0a5f2aSJiri Pirko 	return 0;
589ca0a5f2aSJiri Pirko 
590ca0a5f2aSJiri Pirko rollback:
591ca0a5f2aSJiri Pirko 	for (i--; i >= 0; i--)
592ca0a5f2aSJiri Pirko 		rocker_dma_cmd_ring_wait_free(&cmd_ring->desc_info[i]);
593ca0a5f2aSJiri Pirko 	return err;
594ca0a5f2aSJiri Pirko }
595ca0a5f2aSJiri Pirko 
rocker_dma_cmd_ring_waits_free(const struct rocker * rocker)596ca0a5f2aSJiri Pirko static void rocker_dma_cmd_ring_waits_free(const struct rocker *rocker)
597ca0a5f2aSJiri Pirko {
598ca0a5f2aSJiri Pirko 	const struct rocker_dma_ring_info *cmd_ring = &rocker->cmd_ring;
599ca0a5f2aSJiri Pirko 	int i;
600ca0a5f2aSJiri Pirko 
601ca0a5f2aSJiri Pirko 	for (i = 0; i < cmd_ring->size; i++)
602ca0a5f2aSJiri Pirko 		rocker_dma_cmd_ring_wait_free(&cmd_ring->desc_info[i]);
603ca0a5f2aSJiri Pirko }
604ca0a5f2aSJiri Pirko 
rocker_dma_rings_init(struct rocker * rocker)60511ce2ba3SJiri Pirko static int rocker_dma_rings_init(struct rocker *rocker)
60611ce2ba3SJiri Pirko {
60711ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
60811ce2ba3SJiri Pirko 	int err;
60911ce2ba3SJiri Pirko 
61011ce2ba3SJiri Pirko 	err = rocker_dma_ring_create(rocker, ROCKER_DMA_CMD,
61111ce2ba3SJiri Pirko 				     ROCKER_DMA_CMD_DEFAULT_SIZE,
61211ce2ba3SJiri Pirko 				     &rocker->cmd_ring);
61311ce2ba3SJiri Pirko 	if (err) {
61411ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "failed to create command dma ring\n");
61511ce2ba3SJiri Pirko 		return err;
61611ce2ba3SJiri Pirko 	}
61711ce2ba3SJiri Pirko 
61811ce2ba3SJiri Pirko 	spin_lock_init(&rocker->cmd_ring_lock);
61911ce2ba3SJiri Pirko 
62011ce2ba3SJiri Pirko 	err = rocker_dma_ring_bufs_alloc(rocker, &rocker->cmd_ring,
621c68d0cebSChristophe JAILLET 					 DMA_BIDIRECTIONAL, PAGE_SIZE);
62211ce2ba3SJiri Pirko 	if (err) {
62311ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "failed to alloc command dma ring buffers\n");
62411ce2ba3SJiri Pirko 		goto err_dma_cmd_ring_bufs_alloc;
62511ce2ba3SJiri Pirko 	}
62611ce2ba3SJiri Pirko 
627ca0a5f2aSJiri Pirko 	err = rocker_dma_cmd_ring_waits_alloc(rocker);
628ca0a5f2aSJiri Pirko 	if (err) {
629ca0a5f2aSJiri Pirko 		dev_err(&pdev->dev, "failed to alloc command dma ring waits\n");
630ca0a5f2aSJiri Pirko 		goto err_dma_cmd_ring_waits_alloc;
631ca0a5f2aSJiri Pirko 	}
632ca0a5f2aSJiri Pirko 
63311ce2ba3SJiri Pirko 	err = rocker_dma_ring_create(rocker, ROCKER_DMA_EVENT,
63411ce2ba3SJiri Pirko 				     ROCKER_DMA_EVENT_DEFAULT_SIZE,
63511ce2ba3SJiri Pirko 				     &rocker->event_ring);
63611ce2ba3SJiri Pirko 	if (err) {
63711ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "failed to create event dma ring\n");
63811ce2ba3SJiri Pirko 		goto err_dma_event_ring_create;
63911ce2ba3SJiri Pirko 	}
64011ce2ba3SJiri Pirko 
64111ce2ba3SJiri Pirko 	err = rocker_dma_ring_bufs_alloc(rocker, &rocker->event_ring,
642c68d0cebSChristophe JAILLET 					 DMA_FROM_DEVICE, PAGE_SIZE);
64311ce2ba3SJiri Pirko 	if (err) {
64411ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "failed to alloc event dma ring buffers\n");
64511ce2ba3SJiri Pirko 		goto err_dma_event_ring_bufs_alloc;
64611ce2ba3SJiri Pirko 	}
64711ce2ba3SJiri Pirko 	rocker_dma_ring_pass_to_producer(rocker, &rocker->event_ring);
64811ce2ba3SJiri Pirko 	return 0;
64911ce2ba3SJiri Pirko 
65011ce2ba3SJiri Pirko err_dma_event_ring_bufs_alloc:
65111ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker->event_ring);
65211ce2ba3SJiri Pirko err_dma_event_ring_create:
65358d0c864SAditya Pakki 	rocker_dma_cmd_ring_waits_free(rocker);
65458d0c864SAditya Pakki err_dma_cmd_ring_waits_alloc:
65511ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker->cmd_ring,
656c68d0cebSChristophe JAILLET 				  DMA_BIDIRECTIONAL);
65711ce2ba3SJiri Pirko err_dma_cmd_ring_bufs_alloc:
65811ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker->cmd_ring);
65911ce2ba3SJiri Pirko 	return err;
66011ce2ba3SJiri Pirko }
66111ce2ba3SJiri Pirko 
rocker_dma_rings_fini(struct rocker * rocker)66211ce2ba3SJiri Pirko static void rocker_dma_rings_fini(struct rocker *rocker)
66311ce2ba3SJiri Pirko {
66411ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker->event_ring,
665c68d0cebSChristophe JAILLET 				  DMA_BIDIRECTIONAL);
66611ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker->event_ring);
667ca0a5f2aSJiri Pirko 	rocker_dma_cmd_ring_waits_free(rocker);
66811ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker->cmd_ring,
669c68d0cebSChristophe JAILLET 				  DMA_BIDIRECTIONAL);
67011ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker->cmd_ring);
67111ce2ba3SJiri Pirko }
67211ce2ba3SJiri Pirko 
rocker_dma_rx_ring_skb_map(const struct rocker_port * rocker_port,struct rocker_desc_info * desc_info,struct sk_buff * skb,size_t buf_len)67311ce2ba3SJiri Pirko static int rocker_dma_rx_ring_skb_map(const struct rocker_port *rocker_port,
67411ce2ba3SJiri Pirko 				      struct rocker_desc_info *desc_info,
67511ce2ba3SJiri Pirko 				      struct sk_buff *skb, size_t buf_len)
67611ce2ba3SJiri Pirko {
67711ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
67811ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
67911ce2ba3SJiri Pirko 	dma_addr_t dma_handle;
68011ce2ba3SJiri Pirko 
681c68d0cebSChristophe JAILLET 	dma_handle = dma_map_single(&pdev->dev, skb->data, buf_len,
682c68d0cebSChristophe JAILLET 				    DMA_FROM_DEVICE);
683c68d0cebSChristophe JAILLET 	if (dma_mapping_error(&pdev->dev, dma_handle))
68411ce2ba3SJiri Pirko 		return -EIO;
68511ce2ba3SJiri Pirko 	if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_RX_FRAG_ADDR, dma_handle))
68611ce2ba3SJiri Pirko 		goto tlv_put_failure;
68711ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_RX_FRAG_MAX_LEN, buf_len))
68811ce2ba3SJiri Pirko 		goto tlv_put_failure;
68911ce2ba3SJiri Pirko 	return 0;
69011ce2ba3SJiri Pirko 
69111ce2ba3SJiri Pirko tlv_put_failure:
692c68d0cebSChristophe JAILLET 	dma_unmap_single(&pdev->dev, dma_handle, buf_len, DMA_FROM_DEVICE);
69311ce2ba3SJiri Pirko 	desc_info->tlv_size = 0;
69411ce2ba3SJiri Pirko 	return -EMSGSIZE;
69511ce2ba3SJiri Pirko }
69611ce2ba3SJiri Pirko 
rocker_port_rx_buf_len(const struct rocker_port * rocker_port)69711ce2ba3SJiri Pirko static size_t rocker_port_rx_buf_len(const struct rocker_port *rocker_port)
69811ce2ba3SJiri Pirko {
69911ce2ba3SJiri Pirko 	return rocker_port->dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
70011ce2ba3SJiri Pirko }
70111ce2ba3SJiri Pirko 
rocker_dma_rx_ring_skb_alloc(const struct rocker_port * rocker_port,struct rocker_desc_info * desc_info)70211ce2ba3SJiri Pirko static int rocker_dma_rx_ring_skb_alloc(const struct rocker_port *rocker_port,
70311ce2ba3SJiri Pirko 					struct rocker_desc_info *desc_info)
70411ce2ba3SJiri Pirko {
70511ce2ba3SJiri Pirko 	struct net_device *dev = rocker_port->dev;
70611ce2ba3SJiri Pirko 	struct sk_buff *skb;
70711ce2ba3SJiri Pirko 	size_t buf_len = rocker_port_rx_buf_len(rocker_port);
70811ce2ba3SJiri Pirko 	int err;
70911ce2ba3SJiri Pirko 
71011ce2ba3SJiri Pirko 	/* Ensure that hw will see tlv_size zero in case of an error.
71111ce2ba3SJiri Pirko 	 * That tells hw to use another descriptor.
71211ce2ba3SJiri Pirko 	 */
71311ce2ba3SJiri Pirko 	rocker_desc_cookie_ptr_set(desc_info, NULL);
71411ce2ba3SJiri Pirko 	desc_info->tlv_size = 0;
71511ce2ba3SJiri Pirko 
71611ce2ba3SJiri Pirko 	skb = netdev_alloc_skb_ip_align(dev, buf_len);
71711ce2ba3SJiri Pirko 	if (!skb)
71811ce2ba3SJiri Pirko 		return -ENOMEM;
71911ce2ba3SJiri Pirko 	err = rocker_dma_rx_ring_skb_map(rocker_port, desc_info, skb, buf_len);
72011ce2ba3SJiri Pirko 	if (err) {
72111ce2ba3SJiri Pirko 		dev_kfree_skb_any(skb);
72211ce2ba3SJiri Pirko 		return err;
72311ce2ba3SJiri Pirko 	}
72411ce2ba3SJiri Pirko 	rocker_desc_cookie_ptr_set(desc_info, skb);
72511ce2ba3SJiri Pirko 	return 0;
72611ce2ba3SJiri Pirko }
72711ce2ba3SJiri Pirko 
rocker_dma_rx_ring_skb_unmap(const struct rocker * rocker,const struct rocker_tlv ** attrs)72811ce2ba3SJiri Pirko static void rocker_dma_rx_ring_skb_unmap(const struct rocker *rocker,
72911ce2ba3SJiri Pirko 					 const struct rocker_tlv **attrs)
73011ce2ba3SJiri Pirko {
73111ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
73211ce2ba3SJiri Pirko 	dma_addr_t dma_handle;
73311ce2ba3SJiri Pirko 	size_t len;
73411ce2ba3SJiri Pirko 
73511ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_RX_FRAG_ADDR] ||
73611ce2ba3SJiri Pirko 	    !attrs[ROCKER_TLV_RX_FRAG_MAX_LEN])
73711ce2ba3SJiri Pirko 		return;
73811ce2ba3SJiri Pirko 	dma_handle = rocker_tlv_get_u64(attrs[ROCKER_TLV_RX_FRAG_ADDR]);
73911ce2ba3SJiri Pirko 	len = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FRAG_MAX_LEN]);
740c68d0cebSChristophe JAILLET 	dma_unmap_single(&pdev->dev, dma_handle, len, DMA_FROM_DEVICE);
74111ce2ba3SJiri Pirko }
74211ce2ba3SJiri Pirko 
rocker_dma_rx_ring_skb_free(const struct rocker * rocker,const struct rocker_desc_info * desc_info)74311ce2ba3SJiri Pirko static void rocker_dma_rx_ring_skb_free(const struct rocker *rocker,
74411ce2ba3SJiri Pirko 					const struct rocker_desc_info *desc_info)
74511ce2ba3SJiri Pirko {
74611ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_RX_MAX + 1];
74711ce2ba3SJiri Pirko 	struct sk_buff *skb = rocker_desc_cookie_ptr_get(desc_info);
74811ce2ba3SJiri Pirko 
74911ce2ba3SJiri Pirko 	if (!skb)
75011ce2ba3SJiri Pirko 		return;
75111ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_RX_MAX, desc_info);
75211ce2ba3SJiri Pirko 	rocker_dma_rx_ring_skb_unmap(rocker, attrs);
75311ce2ba3SJiri Pirko 	dev_kfree_skb_any(skb);
75411ce2ba3SJiri Pirko }
75511ce2ba3SJiri Pirko 
rocker_dma_rx_ring_skbs_alloc(const struct rocker_port * rocker_port)75611ce2ba3SJiri Pirko static int rocker_dma_rx_ring_skbs_alloc(const struct rocker_port *rocker_port)
75711ce2ba3SJiri Pirko {
75811ce2ba3SJiri Pirko 	const struct rocker_dma_ring_info *rx_ring = &rocker_port->rx_ring;
75911ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
76011ce2ba3SJiri Pirko 	int i;
76111ce2ba3SJiri Pirko 	int err;
76211ce2ba3SJiri Pirko 
76311ce2ba3SJiri Pirko 	for (i = 0; i < rx_ring->size; i++) {
76411ce2ba3SJiri Pirko 		err = rocker_dma_rx_ring_skb_alloc(rocker_port,
76511ce2ba3SJiri Pirko 						   &rx_ring->desc_info[i]);
76611ce2ba3SJiri Pirko 		if (err)
76711ce2ba3SJiri Pirko 			goto rollback;
76811ce2ba3SJiri Pirko 	}
76911ce2ba3SJiri Pirko 	return 0;
77011ce2ba3SJiri Pirko 
77111ce2ba3SJiri Pirko rollback:
77211ce2ba3SJiri Pirko 	for (i--; i >= 0; i--)
77311ce2ba3SJiri Pirko 		rocker_dma_rx_ring_skb_free(rocker, &rx_ring->desc_info[i]);
77411ce2ba3SJiri Pirko 	return err;
77511ce2ba3SJiri Pirko }
77611ce2ba3SJiri Pirko 
rocker_dma_rx_ring_skbs_free(const struct rocker_port * rocker_port)77711ce2ba3SJiri Pirko static void rocker_dma_rx_ring_skbs_free(const struct rocker_port *rocker_port)
77811ce2ba3SJiri Pirko {
77911ce2ba3SJiri Pirko 	const struct rocker_dma_ring_info *rx_ring = &rocker_port->rx_ring;
78011ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
78111ce2ba3SJiri Pirko 	int i;
78211ce2ba3SJiri Pirko 
78311ce2ba3SJiri Pirko 	for (i = 0; i < rx_ring->size; i++)
78411ce2ba3SJiri Pirko 		rocker_dma_rx_ring_skb_free(rocker, &rx_ring->desc_info[i]);
78511ce2ba3SJiri Pirko }
78611ce2ba3SJiri Pirko 
rocker_port_dma_rings_init(struct rocker_port * rocker_port)78711ce2ba3SJiri Pirko static int rocker_port_dma_rings_init(struct rocker_port *rocker_port)
78811ce2ba3SJiri Pirko {
78911ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
79011ce2ba3SJiri Pirko 	int err;
79111ce2ba3SJiri Pirko 
79211ce2ba3SJiri Pirko 	err = rocker_dma_ring_create(rocker,
79311ce2ba3SJiri Pirko 				     ROCKER_DMA_TX(rocker_port->port_number),
79411ce2ba3SJiri Pirko 				     ROCKER_DMA_TX_DEFAULT_SIZE,
79511ce2ba3SJiri Pirko 				     &rocker_port->tx_ring);
79611ce2ba3SJiri Pirko 	if (err) {
79711ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "failed to create tx dma ring\n");
79811ce2ba3SJiri Pirko 		return err;
79911ce2ba3SJiri Pirko 	}
80011ce2ba3SJiri Pirko 
80111ce2ba3SJiri Pirko 	err = rocker_dma_ring_bufs_alloc(rocker, &rocker_port->tx_ring,
802c68d0cebSChristophe JAILLET 					 DMA_TO_DEVICE,
80311ce2ba3SJiri Pirko 					 ROCKER_DMA_TX_DESC_SIZE);
80411ce2ba3SJiri Pirko 	if (err) {
80511ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "failed to alloc tx dma ring buffers\n");
80611ce2ba3SJiri Pirko 		goto err_dma_tx_ring_bufs_alloc;
80711ce2ba3SJiri Pirko 	}
80811ce2ba3SJiri Pirko 
80911ce2ba3SJiri Pirko 	err = rocker_dma_ring_create(rocker,
81011ce2ba3SJiri Pirko 				     ROCKER_DMA_RX(rocker_port->port_number),
81111ce2ba3SJiri Pirko 				     ROCKER_DMA_RX_DEFAULT_SIZE,
81211ce2ba3SJiri Pirko 				     &rocker_port->rx_ring);
81311ce2ba3SJiri Pirko 	if (err) {
81411ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "failed to create rx dma ring\n");
81511ce2ba3SJiri Pirko 		goto err_dma_rx_ring_create;
81611ce2ba3SJiri Pirko 	}
81711ce2ba3SJiri Pirko 
81811ce2ba3SJiri Pirko 	err = rocker_dma_ring_bufs_alloc(rocker, &rocker_port->rx_ring,
819c68d0cebSChristophe JAILLET 					 DMA_BIDIRECTIONAL,
82011ce2ba3SJiri Pirko 					 ROCKER_DMA_RX_DESC_SIZE);
82111ce2ba3SJiri Pirko 	if (err) {
82211ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "failed to alloc rx dma ring buffers\n");
82311ce2ba3SJiri Pirko 		goto err_dma_rx_ring_bufs_alloc;
82411ce2ba3SJiri Pirko 	}
82511ce2ba3SJiri Pirko 
82611ce2ba3SJiri Pirko 	err = rocker_dma_rx_ring_skbs_alloc(rocker_port);
82711ce2ba3SJiri Pirko 	if (err) {
82811ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "failed to alloc rx dma ring skbs\n");
82911ce2ba3SJiri Pirko 		goto err_dma_rx_ring_skbs_alloc;
83011ce2ba3SJiri Pirko 	}
83111ce2ba3SJiri Pirko 	rocker_dma_ring_pass_to_producer(rocker, &rocker_port->rx_ring);
83211ce2ba3SJiri Pirko 
83311ce2ba3SJiri Pirko 	return 0;
83411ce2ba3SJiri Pirko 
83511ce2ba3SJiri Pirko err_dma_rx_ring_skbs_alloc:
83611ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker_port->rx_ring,
837c68d0cebSChristophe JAILLET 				  DMA_BIDIRECTIONAL);
83811ce2ba3SJiri Pirko err_dma_rx_ring_bufs_alloc:
83911ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker_port->rx_ring);
84011ce2ba3SJiri Pirko err_dma_rx_ring_create:
84111ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker_port->tx_ring,
842c68d0cebSChristophe JAILLET 				  DMA_TO_DEVICE);
84311ce2ba3SJiri Pirko err_dma_tx_ring_bufs_alloc:
84411ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker_port->tx_ring);
84511ce2ba3SJiri Pirko 	return err;
84611ce2ba3SJiri Pirko }
84711ce2ba3SJiri Pirko 
rocker_port_dma_rings_fini(struct rocker_port * rocker_port)84811ce2ba3SJiri Pirko static void rocker_port_dma_rings_fini(struct rocker_port *rocker_port)
84911ce2ba3SJiri Pirko {
85011ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
85111ce2ba3SJiri Pirko 
85211ce2ba3SJiri Pirko 	rocker_dma_rx_ring_skbs_free(rocker_port);
85311ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker_port->rx_ring,
854c68d0cebSChristophe JAILLET 				  DMA_BIDIRECTIONAL);
85511ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker_port->rx_ring);
85611ce2ba3SJiri Pirko 	rocker_dma_ring_bufs_free(rocker, &rocker_port->tx_ring,
857c68d0cebSChristophe JAILLET 				  DMA_TO_DEVICE);
85811ce2ba3SJiri Pirko 	rocker_dma_ring_destroy(rocker, &rocker_port->tx_ring);
85911ce2ba3SJiri Pirko }
86011ce2ba3SJiri Pirko 
rocker_port_set_enable(const struct rocker_port * rocker_port,bool enable)86111ce2ba3SJiri Pirko static void rocker_port_set_enable(const struct rocker_port *rocker_port,
86211ce2ba3SJiri Pirko 				   bool enable)
86311ce2ba3SJiri Pirko {
86411ce2ba3SJiri Pirko 	u64 val = rocker_read64(rocker_port->rocker, PORT_PHYS_ENABLE);
86511ce2ba3SJiri Pirko 
86611ce2ba3SJiri Pirko 	if (enable)
86711ce2ba3SJiri Pirko 		val |= 1ULL << rocker_port->pport;
86811ce2ba3SJiri Pirko 	else
86911ce2ba3SJiri Pirko 		val &= ~(1ULL << rocker_port->pport);
87011ce2ba3SJiri Pirko 	rocker_write64(rocker_port->rocker, PORT_PHYS_ENABLE, val);
87111ce2ba3SJiri Pirko }
87211ce2ba3SJiri Pirko 
87311ce2ba3SJiri Pirko /********************************
87411ce2ba3SJiri Pirko  * Interrupt handler and helpers
87511ce2ba3SJiri Pirko  ********************************/
87611ce2ba3SJiri Pirko 
rocker_cmd_irq_handler(int irq,void * dev_id)87711ce2ba3SJiri Pirko static irqreturn_t rocker_cmd_irq_handler(int irq, void *dev_id)
87811ce2ba3SJiri Pirko {
87911ce2ba3SJiri Pirko 	struct rocker *rocker = dev_id;
88011ce2ba3SJiri Pirko 	const struct rocker_desc_info *desc_info;
88111ce2ba3SJiri Pirko 	struct rocker_wait *wait;
88211ce2ba3SJiri Pirko 	u32 credits = 0;
88311ce2ba3SJiri Pirko 
88411ce2ba3SJiri Pirko 	spin_lock(&rocker->cmd_ring_lock);
88511ce2ba3SJiri Pirko 	while ((desc_info = rocker_desc_tail_get(&rocker->cmd_ring))) {
88611ce2ba3SJiri Pirko 		wait = rocker_desc_cookie_ptr_get(desc_info);
88711ce2ba3SJiri Pirko 		if (wait->nowait) {
88811ce2ba3SJiri Pirko 			rocker_desc_gen_clear(desc_info);
88911ce2ba3SJiri Pirko 		} else {
89011ce2ba3SJiri Pirko 			rocker_wait_wake_up(wait);
89111ce2ba3SJiri Pirko 		}
89211ce2ba3SJiri Pirko 		credits++;
89311ce2ba3SJiri Pirko 	}
89411ce2ba3SJiri Pirko 	spin_unlock(&rocker->cmd_ring_lock);
89511ce2ba3SJiri Pirko 	rocker_dma_ring_credits_set(rocker, &rocker->cmd_ring, credits);
89611ce2ba3SJiri Pirko 
89711ce2ba3SJiri Pirko 	return IRQ_HANDLED;
89811ce2ba3SJiri Pirko }
89911ce2ba3SJiri Pirko 
rocker_port_link_up(const struct rocker_port * rocker_port)90011ce2ba3SJiri Pirko static void rocker_port_link_up(const struct rocker_port *rocker_port)
90111ce2ba3SJiri Pirko {
90211ce2ba3SJiri Pirko 	netif_carrier_on(rocker_port->dev);
90311ce2ba3SJiri Pirko 	netdev_info(rocker_port->dev, "Link is up\n");
90411ce2ba3SJiri Pirko }
90511ce2ba3SJiri Pirko 
rocker_port_link_down(const struct rocker_port * rocker_port)90611ce2ba3SJiri Pirko static void rocker_port_link_down(const struct rocker_port *rocker_port)
90711ce2ba3SJiri Pirko {
90811ce2ba3SJiri Pirko 	netif_carrier_off(rocker_port->dev);
90911ce2ba3SJiri Pirko 	netdev_info(rocker_port->dev, "Link is down\n");
91011ce2ba3SJiri Pirko }
91111ce2ba3SJiri Pirko 
rocker_event_link_change(const struct rocker * rocker,const struct rocker_tlv * info)91211ce2ba3SJiri Pirko static int rocker_event_link_change(const struct rocker *rocker,
91311ce2ba3SJiri Pirko 				    const struct rocker_tlv *info)
91411ce2ba3SJiri Pirko {
91511ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_EVENT_LINK_CHANGED_MAX + 1];
91611ce2ba3SJiri Pirko 	unsigned int port_number;
91711ce2ba3SJiri Pirko 	bool link_up;
91811ce2ba3SJiri Pirko 	struct rocker_port *rocker_port;
91911ce2ba3SJiri Pirko 
92011ce2ba3SJiri Pirko 	rocker_tlv_parse_nested(attrs, ROCKER_TLV_EVENT_LINK_CHANGED_MAX, info);
92111ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_EVENT_LINK_CHANGED_PPORT] ||
92211ce2ba3SJiri Pirko 	    !attrs[ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP])
92311ce2ba3SJiri Pirko 		return -EIO;
92411ce2ba3SJiri Pirko 	port_number =
92511ce2ba3SJiri Pirko 		rocker_tlv_get_u32(attrs[ROCKER_TLV_EVENT_LINK_CHANGED_PPORT]) - 1;
92611ce2ba3SJiri Pirko 	link_up = rocker_tlv_get_u8(attrs[ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP]);
92711ce2ba3SJiri Pirko 
92811ce2ba3SJiri Pirko 	if (port_number >= rocker->port_count)
92911ce2ba3SJiri Pirko 		return -EINVAL;
93011ce2ba3SJiri Pirko 
93111ce2ba3SJiri Pirko 	rocker_port = rocker->ports[port_number];
93211ce2ba3SJiri Pirko 	if (netif_carrier_ok(rocker_port->dev) != link_up) {
93311ce2ba3SJiri Pirko 		if (link_up)
93411ce2ba3SJiri Pirko 			rocker_port_link_up(rocker_port);
93511ce2ba3SJiri Pirko 		else
93611ce2ba3SJiri Pirko 			rocker_port_link_down(rocker_port);
93711ce2ba3SJiri Pirko 	}
93811ce2ba3SJiri Pirko 
93911ce2ba3SJiri Pirko 	return 0;
94011ce2ba3SJiri Pirko }
94111ce2ba3SJiri Pirko 
942e420114eSJiri Pirko static int rocker_world_port_ev_mac_vlan_seen(struct rocker_port *rocker_port,
943e420114eSJiri Pirko 					      const unsigned char *addr,
944e420114eSJiri Pirko 					      __be16 vlan_id);
94511ce2ba3SJiri Pirko 
rocker_event_mac_vlan_seen(const struct rocker * rocker,const struct rocker_tlv * info)94611ce2ba3SJiri Pirko static int rocker_event_mac_vlan_seen(const struct rocker *rocker,
94711ce2ba3SJiri Pirko 				      const struct rocker_tlv *info)
94811ce2ba3SJiri Pirko {
94911ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAX + 1];
95011ce2ba3SJiri Pirko 	unsigned int port_number;
95111ce2ba3SJiri Pirko 	struct rocker_port *rocker_port;
95211ce2ba3SJiri Pirko 	const unsigned char *addr;
95311ce2ba3SJiri Pirko 	__be16 vlan_id;
95411ce2ba3SJiri Pirko 
95511ce2ba3SJiri Pirko 	rocker_tlv_parse_nested(attrs, ROCKER_TLV_EVENT_MAC_VLAN_MAX, info);
95611ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_EVENT_MAC_VLAN_PPORT] ||
95711ce2ba3SJiri Pirko 	    !attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAC] ||
95811ce2ba3SJiri Pirko 	    !attrs[ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID])
95911ce2ba3SJiri Pirko 		return -EIO;
96011ce2ba3SJiri Pirko 	port_number =
96111ce2ba3SJiri Pirko 		rocker_tlv_get_u32(attrs[ROCKER_TLV_EVENT_MAC_VLAN_PPORT]) - 1;
96211ce2ba3SJiri Pirko 	addr = rocker_tlv_data(attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAC]);
96311ce2ba3SJiri Pirko 	vlan_id = rocker_tlv_get_be16(attrs[ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID]);
96411ce2ba3SJiri Pirko 
96511ce2ba3SJiri Pirko 	if (port_number >= rocker->port_count)
96611ce2ba3SJiri Pirko 		return -EINVAL;
96711ce2ba3SJiri Pirko 
96811ce2ba3SJiri Pirko 	rocker_port = rocker->ports[port_number];
9693fbcdbf3SJiri Pirko 	return rocker_world_port_ev_mac_vlan_seen(rocker_port, addr, vlan_id);
97011ce2ba3SJiri Pirko }
97111ce2ba3SJiri Pirko 
rocker_event_process(const struct rocker * rocker,const struct rocker_desc_info * desc_info)97211ce2ba3SJiri Pirko static int rocker_event_process(const struct rocker *rocker,
97311ce2ba3SJiri Pirko 				const struct rocker_desc_info *desc_info)
97411ce2ba3SJiri Pirko {
97511ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_EVENT_MAX + 1];
97611ce2ba3SJiri Pirko 	const struct rocker_tlv *info;
97711ce2ba3SJiri Pirko 	u16 type;
97811ce2ba3SJiri Pirko 
97911ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_EVENT_MAX, desc_info);
98011ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_EVENT_TYPE] ||
98111ce2ba3SJiri Pirko 	    !attrs[ROCKER_TLV_EVENT_INFO])
98211ce2ba3SJiri Pirko 		return -EIO;
98311ce2ba3SJiri Pirko 
98411ce2ba3SJiri Pirko 	type = rocker_tlv_get_u16(attrs[ROCKER_TLV_EVENT_TYPE]);
98511ce2ba3SJiri Pirko 	info = attrs[ROCKER_TLV_EVENT_INFO];
98611ce2ba3SJiri Pirko 
98711ce2ba3SJiri Pirko 	switch (type) {
98811ce2ba3SJiri Pirko 	case ROCKER_TLV_EVENT_TYPE_LINK_CHANGED:
98911ce2ba3SJiri Pirko 		return rocker_event_link_change(rocker, info);
99011ce2ba3SJiri Pirko 	case ROCKER_TLV_EVENT_TYPE_MAC_VLAN_SEEN:
99111ce2ba3SJiri Pirko 		return rocker_event_mac_vlan_seen(rocker, info);
99211ce2ba3SJiri Pirko 	}
99311ce2ba3SJiri Pirko 
99411ce2ba3SJiri Pirko 	return -EOPNOTSUPP;
99511ce2ba3SJiri Pirko }
99611ce2ba3SJiri Pirko 
rocker_event_irq_handler(int irq,void * dev_id)99711ce2ba3SJiri Pirko static irqreturn_t rocker_event_irq_handler(int irq, void *dev_id)
99811ce2ba3SJiri Pirko {
99911ce2ba3SJiri Pirko 	struct rocker *rocker = dev_id;
100011ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
100111ce2ba3SJiri Pirko 	const struct rocker_desc_info *desc_info;
100211ce2ba3SJiri Pirko 	u32 credits = 0;
100311ce2ba3SJiri Pirko 	int err;
100411ce2ba3SJiri Pirko 
100511ce2ba3SJiri Pirko 	while ((desc_info = rocker_desc_tail_get(&rocker->event_ring))) {
100611ce2ba3SJiri Pirko 		err = rocker_desc_err(desc_info);
100711ce2ba3SJiri Pirko 		if (err) {
100811ce2ba3SJiri Pirko 			dev_err(&pdev->dev, "event desc received with err %d\n",
100911ce2ba3SJiri Pirko 				err);
101011ce2ba3SJiri Pirko 		} else {
101111ce2ba3SJiri Pirko 			err = rocker_event_process(rocker, desc_info);
101211ce2ba3SJiri Pirko 			if (err)
101311ce2ba3SJiri Pirko 				dev_err(&pdev->dev, "event processing failed with err %d\n",
101411ce2ba3SJiri Pirko 					err);
101511ce2ba3SJiri Pirko 		}
101611ce2ba3SJiri Pirko 		rocker_desc_gen_clear(desc_info);
101711ce2ba3SJiri Pirko 		rocker_desc_head_set(rocker, &rocker->event_ring, desc_info);
101811ce2ba3SJiri Pirko 		credits++;
101911ce2ba3SJiri Pirko 	}
102011ce2ba3SJiri Pirko 	rocker_dma_ring_credits_set(rocker, &rocker->event_ring, credits);
102111ce2ba3SJiri Pirko 
102211ce2ba3SJiri Pirko 	return IRQ_HANDLED;
102311ce2ba3SJiri Pirko }
102411ce2ba3SJiri Pirko 
rocker_tx_irq_handler(int irq,void * dev_id)102511ce2ba3SJiri Pirko static irqreturn_t rocker_tx_irq_handler(int irq, void *dev_id)
102611ce2ba3SJiri Pirko {
102711ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = dev_id;
102811ce2ba3SJiri Pirko 
102911ce2ba3SJiri Pirko 	napi_schedule(&rocker_port->napi_tx);
103011ce2ba3SJiri Pirko 	return IRQ_HANDLED;
103111ce2ba3SJiri Pirko }
103211ce2ba3SJiri Pirko 
rocker_rx_irq_handler(int irq,void * dev_id)103311ce2ba3SJiri Pirko static irqreturn_t rocker_rx_irq_handler(int irq, void *dev_id)
103411ce2ba3SJiri Pirko {
103511ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = dev_id;
103611ce2ba3SJiri Pirko 
103711ce2ba3SJiri Pirko 	napi_schedule(&rocker_port->napi_rx);
103811ce2ba3SJiri Pirko 	return IRQ_HANDLED;
103911ce2ba3SJiri Pirko }
104011ce2ba3SJiri Pirko 
104111ce2ba3SJiri Pirko /********************
104211ce2ba3SJiri Pirko  * Command interface
104311ce2ba3SJiri Pirko  ********************/
104411ce2ba3SJiri Pirko 
rocker_cmd_exec(struct rocker_port * rocker_port,bool nowait,rocker_cmd_prep_cb_t prepare,void * prepare_priv,rocker_cmd_proc_cb_t process,void * process_priv)10453fbcdbf3SJiri Pirko int rocker_cmd_exec(struct rocker_port *rocker_port, bool nowait,
104611ce2ba3SJiri Pirko 		    rocker_cmd_prep_cb_t prepare, void *prepare_priv,
104711ce2ba3SJiri Pirko 		    rocker_cmd_proc_cb_t process, void *process_priv)
104811ce2ba3SJiri Pirko {
104911ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
105011ce2ba3SJiri Pirko 	struct rocker_desc_info *desc_info;
105111ce2ba3SJiri Pirko 	struct rocker_wait *wait;
105211ce2ba3SJiri Pirko 	unsigned long lock_flags;
105311ce2ba3SJiri Pirko 	int err;
105411ce2ba3SJiri Pirko 
105511ce2ba3SJiri Pirko 	spin_lock_irqsave(&rocker->cmd_ring_lock, lock_flags);
105611ce2ba3SJiri Pirko 
105711ce2ba3SJiri Pirko 	desc_info = rocker_desc_head_get(&rocker->cmd_ring);
105811ce2ba3SJiri Pirko 	if (!desc_info) {
105911ce2ba3SJiri Pirko 		spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags);
1060ca0a5f2aSJiri Pirko 		return -EAGAIN;
106111ce2ba3SJiri Pirko 	}
106211ce2ba3SJiri Pirko 
1063ca0a5f2aSJiri Pirko 	wait = rocker_desc_cookie_ptr_get(desc_info);
1064ca0a5f2aSJiri Pirko 	rocker_wait_init(wait);
1065ca0a5f2aSJiri Pirko 	wait->nowait = nowait;
1066ca0a5f2aSJiri Pirko 
106711ce2ba3SJiri Pirko 	err = prepare(rocker_port, desc_info, prepare_priv);
106811ce2ba3SJiri Pirko 	if (err) {
106911ce2ba3SJiri Pirko 		spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags);
1070ca0a5f2aSJiri Pirko 		return err;
107111ce2ba3SJiri Pirko 	}
107211ce2ba3SJiri Pirko 
107311ce2ba3SJiri Pirko 	rocker_desc_head_set(rocker, &rocker->cmd_ring, desc_info);
107411ce2ba3SJiri Pirko 
107511ce2ba3SJiri Pirko 	spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags);
107611ce2ba3SJiri Pirko 
107711ce2ba3SJiri Pirko 	if (nowait)
107811ce2ba3SJiri Pirko 		return 0;
107911ce2ba3SJiri Pirko 
108011ce2ba3SJiri Pirko 	if (!rocker_wait_event_timeout(wait, HZ / 10))
108111ce2ba3SJiri Pirko 		return -EIO;
108211ce2ba3SJiri Pirko 
108311ce2ba3SJiri Pirko 	err = rocker_desc_err(desc_info);
108411ce2ba3SJiri Pirko 	if (err)
108511ce2ba3SJiri Pirko 		return err;
108611ce2ba3SJiri Pirko 
108711ce2ba3SJiri Pirko 	if (process)
108811ce2ba3SJiri Pirko 		err = process(rocker_port, desc_info, process_priv);
108911ce2ba3SJiri Pirko 
109011ce2ba3SJiri Pirko 	rocker_desc_gen_clear(desc_info);
109111ce2ba3SJiri Pirko 	return err;
109211ce2ba3SJiri Pirko }
109311ce2ba3SJiri Pirko 
109411ce2ba3SJiri Pirko static int
rocker_cmd_get_port_settings_prep(const struct rocker_port * rocker_port,struct rocker_desc_info * desc_info,void * priv)109511ce2ba3SJiri Pirko rocker_cmd_get_port_settings_prep(const struct rocker_port *rocker_port,
109611ce2ba3SJiri Pirko 				  struct rocker_desc_info *desc_info,
109711ce2ba3SJiri Pirko 				  void *priv)
109811ce2ba3SJiri Pirko {
109911ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
110011ce2ba3SJiri Pirko 
110111ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
110211ce2ba3SJiri Pirko 			       ROCKER_TLV_CMD_TYPE_GET_PORT_SETTINGS))
110311ce2ba3SJiri Pirko 		return -EMSGSIZE;
110411ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
110511ce2ba3SJiri Pirko 	if (!cmd_info)
110611ce2ba3SJiri Pirko 		return -EMSGSIZE;
110711ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT,
110811ce2ba3SJiri Pirko 			       rocker_port->pport))
110911ce2ba3SJiri Pirko 		return -EMSGSIZE;
111011ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
111111ce2ba3SJiri Pirko 	return 0;
111211ce2ba3SJiri Pirko }
111311ce2ba3SJiri Pirko 
111411ce2ba3SJiri Pirko static int
rocker_cmd_get_port_settings_ethtool_proc(const struct rocker_port * rocker_port,const struct rocker_desc_info * desc_info,void * priv)111511ce2ba3SJiri Pirko rocker_cmd_get_port_settings_ethtool_proc(const struct rocker_port *rocker_port,
111611ce2ba3SJiri Pirko 					  const struct rocker_desc_info *desc_info,
111711ce2ba3SJiri Pirko 					  void *priv)
111811ce2ba3SJiri Pirko {
1119de480150SPhilippe Reynes 	struct ethtool_link_ksettings *ecmd = priv;
112011ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
112111ce2ba3SJiri Pirko 	const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
112211ce2ba3SJiri Pirko 	u32 speed;
112311ce2ba3SJiri Pirko 	u8 duplex;
112411ce2ba3SJiri Pirko 	u8 autoneg;
112511ce2ba3SJiri Pirko 
112611ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
112711ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_CMD_INFO])
112811ce2ba3SJiri Pirko 		return -EIO;
112911ce2ba3SJiri Pirko 
113011ce2ba3SJiri Pirko 	rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
113111ce2ba3SJiri Pirko 				attrs[ROCKER_TLV_CMD_INFO]);
113211ce2ba3SJiri Pirko 	if (!info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED] ||
113311ce2ba3SJiri Pirko 	    !info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX] ||
113411ce2ba3SJiri Pirko 	    !info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG])
113511ce2ba3SJiri Pirko 		return -EIO;
113611ce2ba3SJiri Pirko 
113711ce2ba3SJiri Pirko 	speed = rocker_tlv_get_u32(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED]);
113811ce2ba3SJiri Pirko 	duplex = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX]);
113911ce2ba3SJiri Pirko 	autoneg = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]);
114011ce2ba3SJiri Pirko 
1141de480150SPhilippe Reynes 	ethtool_link_ksettings_zero_link_mode(ecmd, supported);
1142de480150SPhilippe Reynes 	ethtool_link_ksettings_add_link_mode(ecmd, supported, TP);
1143de480150SPhilippe Reynes 
1144de480150SPhilippe Reynes 	ecmd->base.phy_address = 0xff;
1145de480150SPhilippe Reynes 	ecmd->base.port = PORT_TP;
1146de480150SPhilippe Reynes 	ecmd->base.speed = speed;
1147de480150SPhilippe Reynes 	ecmd->base.duplex = duplex ? DUPLEX_FULL : DUPLEX_HALF;
1148de480150SPhilippe Reynes 	ecmd->base.autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
114911ce2ba3SJiri Pirko 
115011ce2ba3SJiri Pirko 	return 0;
115111ce2ba3SJiri Pirko }
115211ce2ba3SJiri Pirko 
115311ce2ba3SJiri Pirko static int
rocker_cmd_get_port_settings_macaddr_proc(const struct rocker_port * rocker_port,const struct rocker_desc_info * desc_info,void * priv)115411ce2ba3SJiri Pirko rocker_cmd_get_port_settings_macaddr_proc(const struct rocker_port *rocker_port,
115511ce2ba3SJiri Pirko 					  const struct rocker_desc_info *desc_info,
115611ce2ba3SJiri Pirko 					  void *priv)
115711ce2ba3SJiri Pirko {
115811ce2ba3SJiri Pirko 	unsigned char *macaddr = priv;
115911ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
116011ce2ba3SJiri Pirko 	const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
116111ce2ba3SJiri Pirko 	const struct rocker_tlv *attr;
116211ce2ba3SJiri Pirko 
116311ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
116411ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_CMD_INFO])
116511ce2ba3SJiri Pirko 		return -EIO;
116611ce2ba3SJiri Pirko 
116711ce2ba3SJiri Pirko 	rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
116811ce2ba3SJiri Pirko 				attrs[ROCKER_TLV_CMD_INFO]);
116911ce2ba3SJiri Pirko 	attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR];
117011ce2ba3SJiri Pirko 	if (!attr)
117111ce2ba3SJiri Pirko 		return -EIO;
117211ce2ba3SJiri Pirko 
117311ce2ba3SJiri Pirko 	if (rocker_tlv_len(attr) != ETH_ALEN)
117411ce2ba3SJiri Pirko 		return -EINVAL;
117511ce2ba3SJiri Pirko 
117611ce2ba3SJiri Pirko 	ether_addr_copy(macaddr, rocker_tlv_data(attr));
117711ce2ba3SJiri Pirko 	return 0;
117811ce2ba3SJiri Pirko }
117911ce2ba3SJiri Pirko 
1180e1ba3deeSJiri Pirko static int
rocker_cmd_get_port_settings_mode_proc(const struct rocker_port * rocker_port,const struct rocker_desc_info * desc_info,void * priv)1181e1ba3deeSJiri Pirko rocker_cmd_get_port_settings_mode_proc(const struct rocker_port *rocker_port,
1182e1ba3deeSJiri Pirko 				       const struct rocker_desc_info *desc_info,
1183e1ba3deeSJiri Pirko 				       void *priv)
1184e1ba3deeSJiri Pirko {
1185e1ba3deeSJiri Pirko 	u8 *p_mode = priv;
1186e1ba3deeSJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
1187e1ba3deeSJiri Pirko 	const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
1188e1ba3deeSJiri Pirko 	const struct rocker_tlv *attr;
1189e1ba3deeSJiri Pirko 
1190e1ba3deeSJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
1191e1ba3deeSJiri Pirko 	if (!attrs[ROCKER_TLV_CMD_INFO])
1192e1ba3deeSJiri Pirko 		return -EIO;
1193e1ba3deeSJiri Pirko 
1194e1ba3deeSJiri Pirko 	rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
1195e1ba3deeSJiri Pirko 				attrs[ROCKER_TLV_CMD_INFO]);
1196e1ba3deeSJiri Pirko 	attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE];
1197e1ba3deeSJiri Pirko 	if (!attr)
1198e1ba3deeSJiri Pirko 		return -EIO;
1199e1ba3deeSJiri Pirko 
1200e1ba3deeSJiri Pirko 	*p_mode = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]);
1201e1ba3deeSJiri Pirko 	return 0;
1202e1ba3deeSJiri Pirko }
1203e1ba3deeSJiri Pirko 
120411ce2ba3SJiri Pirko struct port_name {
120511ce2ba3SJiri Pirko 	char *buf;
120611ce2ba3SJiri Pirko 	size_t len;
120711ce2ba3SJiri Pirko };
120811ce2ba3SJiri Pirko 
120911ce2ba3SJiri Pirko static int
rocker_cmd_get_port_settings_phys_name_proc(const struct rocker_port * rocker_port,const struct rocker_desc_info * desc_info,void * priv)121011ce2ba3SJiri Pirko rocker_cmd_get_port_settings_phys_name_proc(const struct rocker_port *rocker_port,
121111ce2ba3SJiri Pirko 					    const struct rocker_desc_info *desc_info,
121211ce2ba3SJiri Pirko 					    void *priv)
121311ce2ba3SJiri Pirko {
121411ce2ba3SJiri Pirko 	const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
121511ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
121611ce2ba3SJiri Pirko 	struct port_name *name = priv;
121711ce2ba3SJiri Pirko 	const struct rocker_tlv *attr;
121811ce2ba3SJiri Pirko 	size_t i, j, len;
121911ce2ba3SJiri Pirko 	const char *str;
122011ce2ba3SJiri Pirko 
122111ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
122211ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_CMD_INFO])
122311ce2ba3SJiri Pirko 		return -EIO;
122411ce2ba3SJiri Pirko 
122511ce2ba3SJiri Pirko 	rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
122611ce2ba3SJiri Pirko 				attrs[ROCKER_TLV_CMD_INFO]);
122711ce2ba3SJiri Pirko 	attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME];
122811ce2ba3SJiri Pirko 	if (!attr)
122911ce2ba3SJiri Pirko 		return -EIO;
123011ce2ba3SJiri Pirko 
123111ce2ba3SJiri Pirko 	len = min_t(size_t, rocker_tlv_len(attr), name->len);
123211ce2ba3SJiri Pirko 	str = rocker_tlv_data(attr);
123311ce2ba3SJiri Pirko 
123411ce2ba3SJiri Pirko 	/* make sure name only contains alphanumeric characters */
123511ce2ba3SJiri Pirko 	for (i = j = 0; i < len; ++i) {
123611ce2ba3SJiri Pirko 		if (isalnum(str[i])) {
123711ce2ba3SJiri Pirko 			name->buf[j] = str[i];
123811ce2ba3SJiri Pirko 			j++;
123911ce2ba3SJiri Pirko 		}
124011ce2ba3SJiri Pirko 	}
124111ce2ba3SJiri Pirko 
124211ce2ba3SJiri Pirko 	if (j == 0)
124311ce2ba3SJiri Pirko 		return -EIO;
124411ce2ba3SJiri Pirko 
124511ce2ba3SJiri Pirko 	name->buf[j] = '\0';
124611ce2ba3SJiri Pirko 
124711ce2ba3SJiri Pirko 	return 0;
124811ce2ba3SJiri Pirko }
124911ce2ba3SJiri Pirko 
125011ce2ba3SJiri Pirko static int
rocker_cmd_set_port_settings_ethtool_prep(const struct rocker_port * rocker_port,struct rocker_desc_info * desc_info,void * priv)125111ce2ba3SJiri Pirko rocker_cmd_set_port_settings_ethtool_prep(const struct rocker_port *rocker_port,
125211ce2ba3SJiri Pirko 					  struct rocker_desc_info *desc_info,
125311ce2ba3SJiri Pirko 					  void *priv)
125411ce2ba3SJiri Pirko {
1255de480150SPhilippe Reynes 	struct ethtool_link_ksettings *ecmd = priv;
125611ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
125711ce2ba3SJiri Pirko 
125811ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
125911ce2ba3SJiri Pirko 			       ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS))
126011ce2ba3SJiri Pirko 		return -EMSGSIZE;
126111ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
126211ce2ba3SJiri Pirko 	if (!cmd_info)
126311ce2ba3SJiri Pirko 		return -EMSGSIZE;
126411ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT,
126511ce2ba3SJiri Pirko 			       rocker_port->pport))
126611ce2ba3SJiri Pirko 		return -EMSGSIZE;
126711ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_SPEED,
1268de480150SPhilippe Reynes 			       ecmd->base.speed))
126911ce2ba3SJiri Pirko 		return -EMSGSIZE;
127011ce2ba3SJiri Pirko 	if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX,
1271de480150SPhilippe Reynes 			      ecmd->base.duplex))
127211ce2ba3SJiri Pirko 		return -EMSGSIZE;
127311ce2ba3SJiri Pirko 	if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG,
1274de480150SPhilippe Reynes 			      ecmd->base.autoneg))
127511ce2ba3SJiri Pirko 		return -EMSGSIZE;
127611ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
127711ce2ba3SJiri Pirko 	return 0;
127811ce2ba3SJiri Pirko }
127911ce2ba3SJiri Pirko 
128011ce2ba3SJiri Pirko static int
rocker_cmd_set_port_settings_macaddr_prep(const struct rocker_port * rocker_port,struct rocker_desc_info * desc_info,void * priv)128111ce2ba3SJiri Pirko rocker_cmd_set_port_settings_macaddr_prep(const struct rocker_port *rocker_port,
128211ce2ba3SJiri Pirko 					  struct rocker_desc_info *desc_info,
128311ce2ba3SJiri Pirko 					  void *priv)
128411ce2ba3SJiri Pirko {
128511ce2ba3SJiri Pirko 	const unsigned char *macaddr = priv;
128611ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
128711ce2ba3SJiri Pirko 
128811ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
128911ce2ba3SJiri Pirko 			       ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS))
129011ce2ba3SJiri Pirko 		return -EMSGSIZE;
129111ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
129211ce2ba3SJiri Pirko 	if (!cmd_info)
129311ce2ba3SJiri Pirko 		return -EMSGSIZE;
129411ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT,
129511ce2ba3SJiri Pirko 			       rocker_port->pport))
129611ce2ba3SJiri Pirko 		return -EMSGSIZE;
129711ce2ba3SJiri Pirko 	if (rocker_tlv_put(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR,
129811ce2ba3SJiri Pirko 			   ETH_ALEN, macaddr))
129911ce2ba3SJiri Pirko 		return -EMSGSIZE;
130011ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
130111ce2ba3SJiri Pirko 	return 0;
130211ce2ba3SJiri Pirko }
130311ce2ba3SJiri Pirko 
130411ce2ba3SJiri Pirko static int
rocker_cmd_set_port_settings_mtu_prep(const struct rocker_port * rocker_port,struct rocker_desc_info * desc_info,void * priv)130511ce2ba3SJiri Pirko rocker_cmd_set_port_settings_mtu_prep(const struct rocker_port *rocker_port,
130611ce2ba3SJiri Pirko 				      struct rocker_desc_info *desc_info,
130711ce2ba3SJiri Pirko 				      void *priv)
130811ce2ba3SJiri Pirko {
130911ce2ba3SJiri Pirko 	int mtu = *(int *)priv;
131011ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
131111ce2ba3SJiri Pirko 
131211ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
131311ce2ba3SJiri Pirko 			       ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS))
131411ce2ba3SJiri Pirko 		return -EMSGSIZE;
131511ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
131611ce2ba3SJiri Pirko 	if (!cmd_info)
131711ce2ba3SJiri Pirko 		return -EMSGSIZE;
131811ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT,
131911ce2ba3SJiri Pirko 			       rocker_port->pport))
132011ce2ba3SJiri Pirko 		return -EMSGSIZE;
132111ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_MTU,
132211ce2ba3SJiri Pirko 			       mtu))
132311ce2ba3SJiri Pirko 		return -EMSGSIZE;
132411ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
132511ce2ba3SJiri Pirko 	return 0;
132611ce2ba3SJiri Pirko }
132711ce2ba3SJiri Pirko 
132811ce2ba3SJiri Pirko static int
rocker_cmd_set_port_learning_prep(const struct rocker_port * rocker_port,struct rocker_desc_info * desc_info,void * priv)132911ce2ba3SJiri Pirko rocker_cmd_set_port_learning_prep(const struct rocker_port *rocker_port,
133011ce2ba3SJiri Pirko 				  struct rocker_desc_info *desc_info,
133111ce2ba3SJiri Pirko 				  void *priv)
133211ce2ba3SJiri Pirko {
1333c1fe922eSJiri Pirko 	bool learning = *(bool *)priv;
133411ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_info;
133511ce2ba3SJiri Pirko 
133611ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
133711ce2ba3SJiri Pirko 			       ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS))
133811ce2ba3SJiri Pirko 		return -EMSGSIZE;
133911ce2ba3SJiri Pirko 	cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
134011ce2ba3SJiri Pirko 	if (!cmd_info)
134111ce2ba3SJiri Pirko 		return -EMSGSIZE;
134211ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT,
134311ce2ba3SJiri Pirko 			       rocker_port->pport))
134411ce2ba3SJiri Pirko 		return -EMSGSIZE;
134511ce2ba3SJiri Pirko 	if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING,
1346c1fe922eSJiri Pirko 			      learning))
134711ce2ba3SJiri Pirko 		return -EMSGSIZE;
134811ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_info);
134911ce2ba3SJiri Pirko 	return 0;
135011ce2ba3SJiri Pirko }
135111ce2ba3SJiri Pirko 
1352de480150SPhilippe Reynes static int
rocker_cmd_get_port_settings_ethtool(struct rocker_port * rocker_port,struct ethtool_link_ksettings * ecmd)1353de480150SPhilippe Reynes rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port,
1354de480150SPhilippe Reynes 				     struct ethtool_link_ksettings *ecmd)
135511ce2ba3SJiri Pirko {
135653901cc0SJiri Pirko 	return rocker_cmd_exec(rocker_port, false,
135711ce2ba3SJiri Pirko 			       rocker_cmd_get_port_settings_prep, NULL,
135811ce2ba3SJiri Pirko 			       rocker_cmd_get_port_settings_ethtool_proc,
135911ce2ba3SJiri Pirko 			       ecmd);
136011ce2ba3SJiri Pirko }
136111ce2ba3SJiri Pirko 
rocker_cmd_get_port_settings_macaddr(struct rocker_port * rocker_port,unsigned char * macaddr)136211ce2ba3SJiri Pirko static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port,
136311ce2ba3SJiri Pirko 						unsigned char *macaddr)
136411ce2ba3SJiri Pirko {
136553901cc0SJiri Pirko 	return rocker_cmd_exec(rocker_port, false,
136611ce2ba3SJiri Pirko 			       rocker_cmd_get_port_settings_prep, NULL,
136711ce2ba3SJiri Pirko 			       rocker_cmd_get_port_settings_macaddr_proc,
136811ce2ba3SJiri Pirko 			       macaddr);
136911ce2ba3SJiri Pirko }
137011ce2ba3SJiri Pirko 
rocker_cmd_get_port_settings_mode(struct rocker_port * rocker_port,u8 * p_mode)1371e1ba3deeSJiri Pirko static int rocker_cmd_get_port_settings_mode(struct rocker_port *rocker_port,
1372e1ba3deeSJiri Pirko 					     u8 *p_mode)
1373e1ba3deeSJiri Pirko {
137453901cc0SJiri Pirko 	return rocker_cmd_exec(rocker_port, false,
1375e1ba3deeSJiri Pirko 			       rocker_cmd_get_port_settings_prep, NULL,
1376e1ba3deeSJiri Pirko 			       rocker_cmd_get_port_settings_mode_proc, p_mode);
1377e1ba3deeSJiri Pirko }
1378e1ba3deeSJiri Pirko 
1379de480150SPhilippe Reynes static int
rocker_cmd_set_port_settings_ethtool(struct rocker_port * rocker_port,const struct ethtool_link_ksettings * ecmd)1380de480150SPhilippe Reynes rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port,
1381de480150SPhilippe Reynes 				     const struct ethtool_link_ksettings *ecmd)
138211ce2ba3SJiri Pirko {
1383de480150SPhilippe Reynes 	struct ethtool_link_ksettings copy_ecmd;
1384de480150SPhilippe Reynes 
1385de480150SPhilippe Reynes 	memcpy(&copy_ecmd, ecmd, sizeof(copy_ecmd));
1386de480150SPhilippe Reynes 
138753901cc0SJiri Pirko 	return rocker_cmd_exec(rocker_port, false,
138811ce2ba3SJiri Pirko 			       rocker_cmd_set_port_settings_ethtool_prep,
1389de480150SPhilippe Reynes 			       &copy_ecmd, NULL, NULL);
139011ce2ba3SJiri Pirko }
139111ce2ba3SJiri Pirko 
rocker_cmd_set_port_settings_macaddr(struct rocker_port * rocker_port,unsigned char * macaddr)139211ce2ba3SJiri Pirko static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port,
139311ce2ba3SJiri Pirko 						unsigned char *macaddr)
139411ce2ba3SJiri Pirko {
139553901cc0SJiri Pirko 	return rocker_cmd_exec(rocker_port, false,
139611ce2ba3SJiri Pirko 			       rocker_cmd_set_port_settings_macaddr_prep,
139711ce2ba3SJiri Pirko 			       macaddr, NULL, NULL);
139811ce2ba3SJiri Pirko }
139911ce2ba3SJiri Pirko 
rocker_cmd_set_port_settings_mtu(struct rocker_port * rocker_port,int mtu)140011ce2ba3SJiri Pirko static int rocker_cmd_set_port_settings_mtu(struct rocker_port *rocker_port,
140111ce2ba3SJiri Pirko 					    int mtu)
140211ce2ba3SJiri Pirko {
140353901cc0SJiri Pirko 	return rocker_cmd_exec(rocker_port, false,
140411ce2ba3SJiri Pirko 			       rocker_cmd_set_port_settings_mtu_prep,
140511ce2ba3SJiri Pirko 			       &mtu, NULL, NULL);
140611ce2ba3SJiri Pirko }
140711ce2ba3SJiri Pirko 
rocker_port_set_learning(struct rocker_port * rocker_port,bool learning)14083fbcdbf3SJiri Pirko int rocker_port_set_learning(struct rocker_port *rocker_port,
1409c1fe922eSJiri Pirko 			     bool learning)
141011ce2ba3SJiri Pirko {
141153901cc0SJiri Pirko 	return rocker_cmd_exec(rocker_port, false,
141211ce2ba3SJiri Pirko 			       rocker_cmd_set_port_learning_prep,
1413c1fe922eSJiri Pirko 			       &learning, NULL, NULL);
141411ce2ba3SJiri Pirko }
141511ce2ba3SJiri Pirko 
1416e420114eSJiri Pirko /**********************
1417e420114eSJiri Pirko  * Worlds manipulation
1418e420114eSJiri Pirko  **********************/
1419e420114eSJiri Pirko 
1420e420114eSJiri Pirko static struct rocker_world_ops *rocker_world_ops[] = {
1421e420114eSJiri Pirko 	&rocker_ofdpa_ops,
1422e420114eSJiri Pirko };
1423e420114eSJiri Pirko 
1424e420114eSJiri Pirko #define ROCKER_WORLD_OPS_LEN ARRAY_SIZE(rocker_world_ops)
1425e420114eSJiri Pirko 
rocker_world_ops_find(u8 mode)1426e420114eSJiri Pirko static struct rocker_world_ops *rocker_world_ops_find(u8 mode)
1427e420114eSJiri Pirko {
1428e420114eSJiri Pirko 	int i;
1429e420114eSJiri Pirko 
1430e420114eSJiri Pirko 	for (i = 0; i < ROCKER_WORLD_OPS_LEN; i++)
1431e420114eSJiri Pirko 		if (rocker_world_ops[i]->mode == mode)
1432e420114eSJiri Pirko 			return rocker_world_ops[i];
1433e420114eSJiri Pirko 	return NULL;
1434e420114eSJiri Pirko }
1435e420114eSJiri Pirko 
rocker_world_init(struct rocker * rocker,u8 mode)1436e420114eSJiri Pirko static int rocker_world_init(struct rocker *rocker, u8 mode)
1437e420114eSJiri Pirko {
1438e420114eSJiri Pirko 	struct rocker_world_ops *wops;
1439e420114eSJiri Pirko 	int err;
1440e420114eSJiri Pirko 
1441e420114eSJiri Pirko 	wops = rocker_world_ops_find(mode);
1442e420114eSJiri Pirko 	if (!wops) {
1443e420114eSJiri Pirko 		dev_err(&rocker->pdev->dev, "port mode \"%d\" is not supported\n",
1444e420114eSJiri Pirko 			mode);
1445e420114eSJiri Pirko 		return -EINVAL;
1446e420114eSJiri Pirko 	}
1447e420114eSJiri Pirko 	rocker->wops = wops;
1448e420114eSJiri Pirko 	rocker->wpriv = kzalloc(wops->priv_size, GFP_KERNEL);
1449e420114eSJiri Pirko 	if (!rocker->wpriv)
1450e420114eSJiri Pirko 		return -ENOMEM;
1451e420114eSJiri Pirko 	if (!wops->init)
1452e420114eSJiri Pirko 		return 0;
1453e420114eSJiri Pirko 	err = wops->init(rocker);
1454e420114eSJiri Pirko 	if (err)
1455e420114eSJiri Pirko 		kfree(rocker->wpriv);
1456e420114eSJiri Pirko 	return err;
1457e420114eSJiri Pirko }
1458e420114eSJiri Pirko 
rocker_world_fini(struct rocker * rocker)1459e420114eSJiri Pirko static void rocker_world_fini(struct rocker *rocker)
1460e420114eSJiri Pirko {
1461e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker->wops;
1462e420114eSJiri Pirko 
1463e420114eSJiri Pirko 	if (!wops || !wops->fini)
1464e420114eSJiri Pirko 		return;
1465e420114eSJiri Pirko 	wops->fini(rocker);
1466e420114eSJiri Pirko 	kfree(rocker->wpriv);
1467e420114eSJiri Pirko }
1468e420114eSJiri Pirko 
rocker_world_check_init(struct rocker_port * rocker_port)1469e420114eSJiri Pirko static int rocker_world_check_init(struct rocker_port *rocker_port)
1470e420114eSJiri Pirko {
1471e420114eSJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
1472e420114eSJiri Pirko 	u8 mode;
1473e420114eSJiri Pirko 	int err;
1474e420114eSJiri Pirko 
1475e420114eSJiri Pirko 	err = rocker_cmd_get_port_settings_mode(rocker_port, &mode);
1476e420114eSJiri Pirko 	if (err) {
1477e420114eSJiri Pirko 		dev_err(&rocker->pdev->dev, "failed to get port mode\n");
1478e420114eSJiri Pirko 		return err;
1479e420114eSJiri Pirko 	}
1480e420114eSJiri Pirko 	if (rocker->wops) {
1481e420114eSJiri Pirko 		if (rocker->wops->mode != mode) {
1482e420114eSJiri Pirko 			dev_err(&rocker->pdev->dev, "hardware has ports in different worlds, which is not supported\n");
148392d230ddSWei Yongjun 			return -EINVAL;
1484e420114eSJiri Pirko 		}
1485e420114eSJiri Pirko 		return 0;
1486e420114eSJiri Pirko 	}
1487e420114eSJiri Pirko 	return rocker_world_init(rocker, mode);
1488e420114eSJiri Pirko }
1489e420114eSJiri Pirko 
rocker_world_port_pre_init(struct rocker_port * rocker_port)1490e420114eSJiri Pirko static int rocker_world_port_pre_init(struct rocker_port *rocker_port)
1491e420114eSJiri Pirko {
1492e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1493e420114eSJiri Pirko 	int err;
1494e420114eSJiri Pirko 
1495e420114eSJiri Pirko 	rocker_port->wpriv = kzalloc(wops->port_priv_size, GFP_KERNEL);
1496e420114eSJiri Pirko 	if (!rocker_port->wpriv)
1497e420114eSJiri Pirko 		return -ENOMEM;
1498e420114eSJiri Pirko 	if (!wops->port_pre_init)
1499e420114eSJiri Pirko 		return 0;
1500e420114eSJiri Pirko 	err = wops->port_pre_init(rocker_port);
1501e420114eSJiri Pirko 	if (err)
1502e420114eSJiri Pirko 		kfree(rocker_port->wpriv);
1503e420114eSJiri Pirko 	return 0;
1504e420114eSJiri Pirko }
1505e420114eSJiri Pirko 
rocker_world_port_init(struct rocker_port * rocker_port)1506e420114eSJiri Pirko static int rocker_world_port_init(struct rocker_port *rocker_port)
1507e420114eSJiri Pirko {
1508e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1509e420114eSJiri Pirko 
1510e420114eSJiri Pirko 	if (!wops->port_init)
1511e420114eSJiri Pirko 		return 0;
1512e420114eSJiri Pirko 	return wops->port_init(rocker_port);
1513e420114eSJiri Pirko }
1514e420114eSJiri Pirko 
rocker_world_port_fini(struct rocker_port * rocker_port)1515e420114eSJiri Pirko static void rocker_world_port_fini(struct rocker_port *rocker_port)
1516e420114eSJiri Pirko {
1517e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1518e420114eSJiri Pirko 
1519e420114eSJiri Pirko 	if (!wops->port_fini)
1520e420114eSJiri Pirko 		return;
1521e420114eSJiri Pirko 	wops->port_fini(rocker_port);
1522e420114eSJiri Pirko }
1523e420114eSJiri Pirko 
rocker_world_port_post_fini(struct rocker_port * rocker_port)1524e420114eSJiri Pirko static void rocker_world_port_post_fini(struct rocker_port *rocker_port)
1525e420114eSJiri Pirko {
1526e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1527e420114eSJiri Pirko 
1528e420114eSJiri Pirko 	if (!wops->port_post_fini)
1529e420114eSJiri Pirko 		return;
1530e420114eSJiri Pirko 	wops->port_post_fini(rocker_port);
1531e420114eSJiri Pirko 	kfree(rocker_port->wpriv);
1532e420114eSJiri Pirko }
1533e420114eSJiri Pirko 
rocker_world_port_open(struct rocker_port * rocker_port)1534e420114eSJiri Pirko static int rocker_world_port_open(struct rocker_port *rocker_port)
1535e420114eSJiri Pirko {
1536e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1537e420114eSJiri Pirko 
1538e420114eSJiri Pirko 	if (!wops->port_open)
1539e420114eSJiri Pirko 		return 0;
1540e420114eSJiri Pirko 	return wops->port_open(rocker_port);
1541e420114eSJiri Pirko }
1542e420114eSJiri Pirko 
rocker_world_port_stop(struct rocker_port * rocker_port)1543e420114eSJiri Pirko static void rocker_world_port_stop(struct rocker_port *rocker_port)
1544e420114eSJiri Pirko {
1545e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1546e420114eSJiri Pirko 
1547e420114eSJiri Pirko 	if (!wops->port_stop)
1548e420114eSJiri Pirko 		return;
1549e420114eSJiri Pirko 	wops->port_stop(rocker_port);
1550e420114eSJiri Pirko }
1551e420114eSJiri Pirko 
rocker_world_port_attr_stp_state_set(struct rocker_port * rocker_port,u8 state)1552e420114eSJiri Pirko static int rocker_world_port_attr_stp_state_set(struct rocker_port *rocker_port,
1553bae33f2bSVladimir Oltean 						u8 state)
1554e420114eSJiri Pirko {
1555e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1556e420114eSJiri Pirko 
1557e420114eSJiri Pirko 	if (!wops->port_attr_stp_state_set)
1558fccd84d4SJiri Pirko 		return -EOPNOTSUPP;
155900fc0c51SArkadi Sharshevsky 
156000fc0c51SArkadi Sharshevsky 	return wops->port_attr_stp_state_set(rocker_port, state);
1561e420114eSJiri Pirko }
1562e420114eSJiri Pirko 
1563e420114eSJiri Pirko static int
rocker_world_port_attr_bridge_flags_support_get(const struct rocker_port * rocker_port,unsigned long * p_brport_flags_support)156493700458SFlorian Fainelli rocker_world_port_attr_bridge_flags_support_get(const struct rocker_port *
156593700458SFlorian Fainelli 						rocker_port,
156693700458SFlorian Fainelli 						unsigned long *
156793700458SFlorian Fainelli 						p_brport_flags_support)
156893700458SFlorian Fainelli {
156993700458SFlorian Fainelli 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
157093700458SFlorian Fainelli 
157193700458SFlorian Fainelli 	if (!wops->port_attr_bridge_flags_support_get)
157293700458SFlorian Fainelli 		return -EOPNOTSUPP;
157393700458SFlorian Fainelli 	return wops->port_attr_bridge_flags_support_get(rocker_port,
157493700458SFlorian Fainelli 							p_brport_flags_support);
157593700458SFlorian Fainelli }
157693700458SFlorian Fainelli 
157793700458SFlorian Fainelli static int
rocker_world_port_attr_pre_bridge_flags_set(struct rocker_port * rocker_port,struct switchdev_brport_flags flags)157893700458SFlorian Fainelli rocker_world_port_attr_pre_bridge_flags_set(struct rocker_port *rocker_port,
1579e18f4c18SVladimir Oltean 					    struct switchdev_brport_flags flags)
158093700458SFlorian Fainelli {
158193700458SFlorian Fainelli 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
158293700458SFlorian Fainelli 	unsigned long brport_flags_s;
158393700458SFlorian Fainelli 	int err;
158493700458SFlorian Fainelli 
158593700458SFlorian Fainelli 	if (!wops->port_attr_bridge_flags_set)
158693700458SFlorian Fainelli 		return -EOPNOTSUPP;
158793700458SFlorian Fainelli 
158893700458SFlorian Fainelli 	err = rocker_world_port_attr_bridge_flags_support_get(rocker_port,
158993700458SFlorian Fainelli 							      &brport_flags_s);
159093700458SFlorian Fainelli 	if (err)
159193700458SFlorian Fainelli 		return err;
159293700458SFlorian Fainelli 
1593e18f4c18SVladimir Oltean 	if (flags.mask & ~brport_flags_s)
159493700458SFlorian Fainelli 		return -EINVAL;
159593700458SFlorian Fainelli 
159693700458SFlorian Fainelli 	return 0;
159793700458SFlorian Fainelli }
159893700458SFlorian Fainelli 
159993700458SFlorian Fainelli static int
rocker_world_port_attr_bridge_flags_set(struct rocker_port * rocker_port,struct switchdev_brport_flags flags)1600e420114eSJiri Pirko rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port,
1601e18f4c18SVladimir Oltean 					struct switchdev_brport_flags flags)
1602e420114eSJiri Pirko {
1603e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1604e420114eSJiri Pirko 
1605e420114eSJiri Pirko 	if (!wops->port_attr_bridge_flags_set)
1606fccd84d4SJiri Pirko 		return -EOPNOTSUPP;
160700fc0c51SArkadi Sharshevsky 
1608e18f4c18SVladimir Oltean 	return wops->port_attr_bridge_flags_set(rocker_port, flags.val);
1609e420114eSJiri Pirko }
1610e420114eSJiri Pirko 
1611e420114eSJiri Pirko static int
rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port * rocker_port,u32 ageing_time)1612e420114eSJiri Pirko rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port,
1613bae33f2bSVladimir Oltean 					      u32 ageing_time)
1614e420114eSJiri Pirko {
1615e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1616e420114eSJiri Pirko 
1617e420114eSJiri Pirko 	if (!wops->port_attr_bridge_ageing_time_set)
1618fccd84d4SJiri Pirko 		return -EOPNOTSUPP;
161900fc0c51SArkadi Sharshevsky 
1620bae33f2bSVladimir Oltean 	return wops->port_attr_bridge_ageing_time_set(rocker_port, ageing_time);
1621e420114eSJiri Pirko }
1622e420114eSJiri Pirko 
1623e420114eSJiri Pirko static int
rocker_world_port_obj_vlan_add(struct rocker_port * rocker_port,const struct switchdev_obj_port_vlan * vlan)1624e420114eSJiri Pirko rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port,
1625ffb68fc5SVladimir Oltean 			       const struct switchdev_obj_port_vlan *vlan)
1626e420114eSJiri Pirko {
1627e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1628e420114eSJiri Pirko 
1629e420114eSJiri Pirko 	if (!wops->port_obj_vlan_add)
1630fccd84d4SJiri Pirko 		return -EOPNOTSUPP;
163100fc0c51SArkadi Sharshevsky 
163200fc0c51SArkadi Sharshevsky 	return wops->port_obj_vlan_add(rocker_port, vlan);
1633e420114eSJiri Pirko }
1634e420114eSJiri Pirko 
1635e420114eSJiri Pirko static int
rocker_world_port_obj_vlan_del(struct rocker_port * rocker_port,const struct switchdev_obj_port_vlan * vlan)1636e420114eSJiri Pirko rocker_world_port_obj_vlan_del(struct rocker_port *rocker_port,
1637e420114eSJiri Pirko 			       const struct switchdev_obj_port_vlan *vlan)
1638e420114eSJiri Pirko {
1639e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1640e420114eSJiri Pirko 
16412855118fSPetr Machata 	if (netif_is_bridge_master(vlan->obj.orig_dev))
16422855118fSPetr Machata 		return -EOPNOTSUPP;
16432855118fSPetr Machata 
1644e420114eSJiri Pirko 	if (!wops->port_obj_vlan_del)
1645fccd84d4SJiri Pirko 		return -EOPNOTSUPP;
1646e420114eSJiri Pirko 	return wops->port_obj_vlan_del(rocker_port, vlan);
1647e420114eSJiri Pirko }
1648e420114eSJiri Pirko 
1649e420114eSJiri Pirko static int
rocker_world_port_fdb_add(struct rocker_port * rocker_port,struct switchdev_notifier_fdb_info * info)1650726fd42fSArkadi Sharshevsky rocker_world_port_fdb_add(struct rocker_port *rocker_port,
1651726fd42fSArkadi Sharshevsky 			  struct switchdev_notifier_fdb_info *info)
1652726fd42fSArkadi Sharshevsky {
1653726fd42fSArkadi Sharshevsky 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1654726fd42fSArkadi Sharshevsky 
1655726fd42fSArkadi Sharshevsky 	if (!wops->port_obj_fdb_add)
1656726fd42fSArkadi Sharshevsky 		return -EOPNOTSUPP;
1657726fd42fSArkadi Sharshevsky 
1658726fd42fSArkadi Sharshevsky 	return wops->port_obj_fdb_add(rocker_port, info->vid, info->addr);
1659726fd42fSArkadi Sharshevsky }
1660726fd42fSArkadi Sharshevsky 
1661726fd42fSArkadi Sharshevsky static int
rocker_world_port_fdb_del(struct rocker_port * rocker_port,struct switchdev_notifier_fdb_info * info)1662726fd42fSArkadi Sharshevsky rocker_world_port_fdb_del(struct rocker_port *rocker_port,
1663726fd42fSArkadi Sharshevsky 			  struct switchdev_notifier_fdb_info *info)
1664726fd42fSArkadi Sharshevsky {
1665726fd42fSArkadi Sharshevsky 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1666726fd42fSArkadi Sharshevsky 
1667726fd42fSArkadi Sharshevsky 	if (!wops->port_obj_fdb_del)
1668726fd42fSArkadi Sharshevsky 		return -EOPNOTSUPP;
1669726fd42fSArkadi Sharshevsky 	return wops->port_obj_fdb_del(rocker_port, info->vid, info->addr);
1670726fd42fSArkadi Sharshevsky }
1671726fd42fSArkadi Sharshevsky 
rocker_world_port_master_linked(struct rocker_port * rocker_port,struct net_device * master,struct netlink_ext_ack * extack)1672e420114eSJiri Pirko static int rocker_world_port_master_linked(struct rocker_port *rocker_port,
16732f5dc00fSVladimir Oltean 					   struct net_device *master,
16742f5dc00fSVladimir Oltean 					   struct netlink_ext_ack *extack)
1675e420114eSJiri Pirko {
1676e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1677e420114eSJiri Pirko 
1678e420114eSJiri Pirko 	if (!wops->port_master_linked)
1679fccd84d4SJiri Pirko 		return -EOPNOTSUPP;
16802f5dc00fSVladimir Oltean 	return wops->port_master_linked(rocker_port, master, extack);
1681e420114eSJiri Pirko }
1682e420114eSJiri Pirko 
rocker_world_port_master_unlinked(struct rocker_port * rocker_port,struct net_device * master)1683e420114eSJiri Pirko static int rocker_world_port_master_unlinked(struct rocker_port *rocker_port,
1684e420114eSJiri Pirko 					     struct net_device *master)
1685e420114eSJiri Pirko {
1686e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1687e420114eSJiri Pirko 
1688e420114eSJiri Pirko 	if (!wops->port_master_unlinked)
1689fccd84d4SJiri Pirko 		return -EOPNOTSUPP;
1690e420114eSJiri Pirko 	return wops->port_master_unlinked(rocker_port, master);
1691e420114eSJiri Pirko }
1692e420114eSJiri Pirko 
rocker_world_port_neigh_update(struct rocker_port * rocker_port,struct neighbour * n)1693e420114eSJiri Pirko static int rocker_world_port_neigh_update(struct rocker_port *rocker_port,
1694e420114eSJiri Pirko 					  struct neighbour *n)
1695e420114eSJiri Pirko {
1696e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1697e420114eSJiri Pirko 
1698e420114eSJiri Pirko 	if (!wops->port_neigh_update)
1699fccd84d4SJiri Pirko 		return -EOPNOTSUPP;
1700e420114eSJiri Pirko 	return wops->port_neigh_update(rocker_port, n);
1701e420114eSJiri Pirko }
1702e420114eSJiri Pirko 
rocker_world_port_neigh_destroy(struct rocker_port * rocker_port,struct neighbour * n)1703e420114eSJiri Pirko static int rocker_world_port_neigh_destroy(struct rocker_port *rocker_port,
1704e420114eSJiri Pirko 					   struct neighbour *n)
1705e420114eSJiri Pirko {
1706e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1707e420114eSJiri Pirko 
1708e420114eSJiri Pirko 	if (!wops->port_neigh_destroy)
1709fccd84d4SJiri Pirko 		return -EOPNOTSUPP;
1710e420114eSJiri Pirko 	return wops->port_neigh_destroy(rocker_port, n);
1711e420114eSJiri Pirko }
1712e420114eSJiri Pirko 
rocker_world_port_ev_mac_vlan_seen(struct rocker_port * rocker_port,const unsigned char * addr,__be16 vlan_id)1713e420114eSJiri Pirko static int rocker_world_port_ev_mac_vlan_seen(struct rocker_port *rocker_port,
1714e420114eSJiri Pirko 					      const unsigned char *addr,
1715e420114eSJiri Pirko 					      __be16 vlan_id)
1716e420114eSJiri Pirko {
1717e420114eSJiri Pirko 	struct rocker_world_ops *wops = rocker_port->rocker->wops;
1718e420114eSJiri Pirko 
1719e420114eSJiri Pirko 	if (!wops->port_ev_mac_vlan_seen)
1720fccd84d4SJiri Pirko 		return -EOPNOTSUPP;
1721e420114eSJiri Pirko 	return wops->port_ev_mac_vlan_seen(rocker_port, addr, vlan_id);
1722e420114eSJiri Pirko }
1723e420114eSJiri Pirko 
rocker_world_fib4_add(struct rocker * rocker,const struct fib_entry_notifier_info * fen_info)1724936bd486SJiri Pirko static int rocker_world_fib4_add(struct rocker *rocker,
1725936bd486SJiri Pirko 				 const struct fib_entry_notifier_info *fen_info)
1726936bd486SJiri Pirko {
1727936bd486SJiri Pirko 	struct rocker_world_ops *wops = rocker->wops;
1728936bd486SJiri Pirko 
1729936bd486SJiri Pirko 	if (!wops->fib4_add)
1730936bd486SJiri Pirko 		return 0;
1731936bd486SJiri Pirko 	return wops->fib4_add(rocker, fen_info);
1732936bd486SJiri Pirko }
1733936bd486SJiri Pirko 
rocker_world_fib4_del(struct rocker * rocker,const struct fib_entry_notifier_info * fen_info)1734936bd486SJiri Pirko static int rocker_world_fib4_del(struct rocker *rocker,
1735936bd486SJiri Pirko 				 const struct fib_entry_notifier_info *fen_info)
1736936bd486SJiri Pirko {
1737936bd486SJiri Pirko 	struct rocker_world_ops *wops = rocker->wops;
1738936bd486SJiri Pirko 
1739936bd486SJiri Pirko 	if (!wops->fib4_del)
1740936bd486SJiri Pirko 		return 0;
1741936bd486SJiri Pirko 	return wops->fib4_del(rocker, fen_info);
1742936bd486SJiri Pirko }
1743936bd486SJiri Pirko 
rocker_world_fib4_abort(struct rocker * rocker)1744936bd486SJiri Pirko static void rocker_world_fib4_abort(struct rocker *rocker)
1745936bd486SJiri Pirko {
1746936bd486SJiri Pirko 	struct rocker_world_ops *wops = rocker->wops;
1747936bd486SJiri Pirko 
1748936bd486SJiri Pirko 	if (wops->fib4_abort)
1749936bd486SJiri Pirko 		wops->fib4_abort(rocker);
1750936bd486SJiri Pirko }
1751936bd486SJiri Pirko 
175211ce2ba3SJiri Pirko /*****************
175311ce2ba3SJiri Pirko  * Net device ops
175411ce2ba3SJiri Pirko  *****************/
175511ce2ba3SJiri Pirko 
rocker_port_open(struct net_device * dev)175611ce2ba3SJiri Pirko static int rocker_port_open(struct net_device *dev)
175711ce2ba3SJiri Pirko {
175811ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
175911ce2ba3SJiri Pirko 	int err;
176011ce2ba3SJiri Pirko 
176111ce2ba3SJiri Pirko 	err = rocker_port_dma_rings_init(rocker_port);
176211ce2ba3SJiri Pirko 	if (err)
176311ce2ba3SJiri Pirko 		return err;
176411ce2ba3SJiri Pirko 
176511ce2ba3SJiri Pirko 	err = request_irq(rocker_msix_tx_vector(rocker_port),
176611ce2ba3SJiri Pirko 			  rocker_tx_irq_handler, 0,
176711ce2ba3SJiri Pirko 			  rocker_driver_name, rocker_port);
176811ce2ba3SJiri Pirko 	if (err) {
176911ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "cannot assign tx irq\n");
177011ce2ba3SJiri Pirko 		goto err_request_tx_irq;
177111ce2ba3SJiri Pirko 	}
177211ce2ba3SJiri Pirko 
177311ce2ba3SJiri Pirko 	err = request_irq(rocker_msix_rx_vector(rocker_port),
177411ce2ba3SJiri Pirko 			  rocker_rx_irq_handler, 0,
177511ce2ba3SJiri Pirko 			  rocker_driver_name, rocker_port);
177611ce2ba3SJiri Pirko 	if (err) {
177711ce2ba3SJiri Pirko 		netdev_err(rocker_port->dev, "cannot assign rx irq\n");
177811ce2ba3SJiri Pirko 		goto err_request_rx_irq;
177911ce2ba3SJiri Pirko 	}
178011ce2ba3SJiri Pirko 
1781e420114eSJiri Pirko 	err = rocker_world_port_open(rocker_port);
1782e420114eSJiri Pirko 	if (err) {
1783e420114eSJiri Pirko 		netdev_err(rocker_port->dev, "cannot open port in world\n");
1784e420114eSJiri Pirko 		goto err_world_port_open;
1785e420114eSJiri Pirko 	}
1786e420114eSJiri Pirko 
178711ce2ba3SJiri Pirko 	napi_enable(&rocker_port->napi_tx);
178811ce2ba3SJiri Pirko 	napi_enable(&rocker_port->napi_rx);
178911ce2ba3SJiri Pirko 	if (!dev->proto_down)
179011ce2ba3SJiri Pirko 		rocker_port_set_enable(rocker_port, true);
179111ce2ba3SJiri Pirko 	netif_start_queue(dev);
179211ce2ba3SJiri Pirko 	return 0;
179311ce2ba3SJiri Pirko 
1794e420114eSJiri Pirko err_world_port_open:
179511ce2ba3SJiri Pirko 	free_irq(rocker_msix_rx_vector(rocker_port), rocker_port);
179611ce2ba3SJiri Pirko err_request_rx_irq:
179711ce2ba3SJiri Pirko 	free_irq(rocker_msix_tx_vector(rocker_port), rocker_port);
179811ce2ba3SJiri Pirko err_request_tx_irq:
179911ce2ba3SJiri Pirko 	rocker_port_dma_rings_fini(rocker_port);
180011ce2ba3SJiri Pirko 	return err;
180111ce2ba3SJiri Pirko }
180211ce2ba3SJiri Pirko 
rocker_port_stop(struct net_device * dev)180311ce2ba3SJiri Pirko static int rocker_port_stop(struct net_device *dev)
180411ce2ba3SJiri Pirko {
180511ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
180611ce2ba3SJiri Pirko 
180711ce2ba3SJiri Pirko 	netif_stop_queue(dev);
180811ce2ba3SJiri Pirko 	rocker_port_set_enable(rocker_port, false);
180911ce2ba3SJiri Pirko 	napi_disable(&rocker_port->napi_rx);
181011ce2ba3SJiri Pirko 	napi_disable(&rocker_port->napi_tx);
1811e420114eSJiri Pirko 	rocker_world_port_stop(rocker_port);
181211ce2ba3SJiri Pirko 	free_irq(rocker_msix_rx_vector(rocker_port), rocker_port);
181311ce2ba3SJiri Pirko 	free_irq(rocker_msix_tx_vector(rocker_port), rocker_port);
181411ce2ba3SJiri Pirko 	rocker_port_dma_rings_fini(rocker_port);
181511ce2ba3SJiri Pirko 
181611ce2ba3SJiri Pirko 	return 0;
181711ce2ba3SJiri Pirko }
181811ce2ba3SJiri Pirko 
rocker_tx_desc_frags_unmap(const struct rocker_port * rocker_port,const struct rocker_desc_info * desc_info)181911ce2ba3SJiri Pirko static void rocker_tx_desc_frags_unmap(const struct rocker_port *rocker_port,
182011ce2ba3SJiri Pirko 				       const struct rocker_desc_info *desc_info)
182111ce2ba3SJiri Pirko {
182211ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
182311ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
182411ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_TX_MAX + 1];
182511ce2ba3SJiri Pirko 	struct rocker_tlv *attr;
182611ce2ba3SJiri Pirko 	int rem;
182711ce2ba3SJiri Pirko 
182811ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_TX_MAX, desc_info);
182911ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_TX_FRAGS])
183011ce2ba3SJiri Pirko 		return;
183111ce2ba3SJiri Pirko 	rocker_tlv_for_each_nested(attr, attrs[ROCKER_TLV_TX_FRAGS], rem) {
183211ce2ba3SJiri Pirko 		const struct rocker_tlv *frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_MAX + 1];
183311ce2ba3SJiri Pirko 		dma_addr_t dma_handle;
183411ce2ba3SJiri Pirko 		size_t len;
183511ce2ba3SJiri Pirko 
183611ce2ba3SJiri Pirko 		if (rocker_tlv_type(attr) != ROCKER_TLV_TX_FRAG)
183711ce2ba3SJiri Pirko 			continue;
183811ce2ba3SJiri Pirko 		rocker_tlv_parse_nested(frag_attrs, ROCKER_TLV_TX_FRAG_ATTR_MAX,
183911ce2ba3SJiri Pirko 					attr);
184011ce2ba3SJiri Pirko 		if (!frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_ADDR] ||
184111ce2ba3SJiri Pirko 		    !frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_LEN])
184211ce2ba3SJiri Pirko 			continue;
184311ce2ba3SJiri Pirko 		dma_handle = rocker_tlv_get_u64(frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]);
184411ce2ba3SJiri Pirko 		len = rocker_tlv_get_u16(frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_LEN]);
1845c68d0cebSChristophe JAILLET 		dma_unmap_single(&pdev->dev, dma_handle, len, DMA_TO_DEVICE);
184611ce2ba3SJiri Pirko 	}
184711ce2ba3SJiri Pirko }
184811ce2ba3SJiri Pirko 
rocker_tx_desc_frag_map_put(const struct rocker_port * rocker_port,struct rocker_desc_info * desc_info,char * buf,size_t buf_len)184911ce2ba3SJiri Pirko static int rocker_tx_desc_frag_map_put(const struct rocker_port *rocker_port,
185011ce2ba3SJiri Pirko 				       struct rocker_desc_info *desc_info,
185111ce2ba3SJiri Pirko 				       char *buf, size_t buf_len)
185211ce2ba3SJiri Pirko {
185311ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
185411ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
185511ce2ba3SJiri Pirko 	dma_addr_t dma_handle;
185611ce2ba3SJiri Pirko 	struct rocker_tlv *frag;
185711ce2ba3SJiri Pirko 
1858c68d0cebSChristophe JAILLET 	dma_handle = dma_map_single(&pdev->dev, buf, buf_len, DMA_TO_DEVICE);
1859c68d0cebSChristophe JAILLET 	if (unlikely(dma_mapping_error(&pdev->dev, dma_handle))) {
186011ce2ba3SJiri Pirko 		if (net_ratelimit())
186111ce2ba3SJiri Pirko 			netdev_err(rocker_port->dev, "failed to dma map tx frag\n");
186211ce2ba3SJiri Pirko 		return -EIO;
186311ce2ba3SJiri Pirko 	}
186411ce2ba3SJiri Pirko 	frag = rocker_tlv_nest_start(desc_info, ROCKER_TLV_TX_FRAG);
186511ce2ba3SJiri Pirko 	if (!frag)
186611ce2ba3SJiri Pirko 		goto unmap_frag;
186711ce2ba3SJiri Pirko 	if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_TX_FRAG_ATTR_ADDR,
186811ce2ba3SJiri Pirko 			       dma_handle))
186911ce2ba3SJiri Pirko 		goto nest_cancel;
187011ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_TX_FRAG_ATTR_LEN,
187111ce2ba3SJiri Pirko 			       buf_len))
187211ce2ba3SJiri Pirko 		goto nest_cancel;
187311ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, frag);
187411ce2ba3SJiri Pirko 	return 0;
187511ce2ba3SJiri Pirko 
187611ce2ba3SJiri Pirko nest_cancel:
187711ce2ba3SJiri Pirko 	rocker_tlv_nest_cancel(desc_info, frag);
187811ce2ba3SJiri Pirko unmap_frag:
1879c68d0cebSChristophe JAILLET 	dma_unmap_single(&pdev->dev, dma_handle, buf_len, DMA_TO_DEVICE);
188011ce2ba3SJiri Pirko 	return -EMSGSIZE;
188111ce2ba3SJiri Pirko }
188211ce2ba3SJiri Pirko 
rocker_port_xmit(struct sk_buff * skb,struct net_device * dev)188311ce2ba3SJiri Pirko static netdev_tx_t rocker_port_xmit(struct sk_buff *skb, struct net_device *dev)
188411ce2ba3SJiri Pirko {
188511ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
188611ce2ba3SJiri Pirko 	struct rocker *rocker = rocker_port->rocker;
188711ce2ba3SJiri Pirko 	struct rocker_desc_info *desc_info;
188811ce2ba3SJiri Pirko 	struct rocker_tlv *frags;
188911ce2ba3SJiri Pirko 	int i;
189011ce2ba3SJiri Pirko 	int err;
189111ce2ba3SJiri Pirko 
189211ce2ba3SJiri Pirko 	desc_info = rocker_desc_head_get(&rocker_port->tx_ring);
189311ce2ba3SJiri Pirko 	if (unlikely(!desc_info)) {
189411ce2ba3SJiri Pirko 		if (net_ratelimit())
189511ce2ba3SJiri Pirko 			netdev_err(dev, "tx ring full when queue awake\n");
189611ce2ba3SJiri Pirko 		return NETDEV_TX_BUSY;
189711ce2ba3SJiri Pirko 	}
189811ce2ba3SJiri Pirko 
189911ce2ba3SJiri Pirko 	rocker_desc_cookie_ptr_set(desc_info, skb);
190011ce2ba3SJiri Pirko 
190111ce2ba3SJiri Pirko 	frags = rocker_tlv_nest_start(desc_info, ROCKER_TLV_TX_FRAGS);
190211ce2ba3SJiri Pirko 	if (!frags)
190311ce2ba3SJiri Pirko 		goto out;
190411ce2ba3SJiri Pirko 	err = rocker_tx_desc_frag_map_put(rocker_port, desc_info,
190511ce2ba3SJiri Pirko 					  skb->data, skb_headlen(skb));
190611ce2ba3SJiri Pirko 	if (err)
190711ce2ba3SJiri Pirko 		goto nest_cancel;
190811ce2ba3SJiri Pirko 	if (skb_shinfo(skb)->nr_frags > ROCKER_TX_FRAGS_MAX) {
190911ce2ba3SJiri Pirko 		err = skb_linearize(skb);
191011ce2ba3SJiri Pirko 		if (err)
191111ce2ba3SJiri Pirko 			goto unmap_frags;
191211ce2ba3SJiri Pirko 	}
191311ce2ba3SJiri Pirko 
191411ce2ba3SJiri Pirko 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
191511ce2ba3SJiri Pirko 		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
191611ce2ba3SJiri Pirko 
191711ce2ba3SJiri Pirko 		err = rocker_tx_desc_frag_map_put(rocker_port, desc_info,
191811ce2ba3SJiri Pirko 						  skb_frag_address(frag),
191911ce2ba3SJiri Pirko 						  skb_frag_size(frag));
192011ce2ba3SJiri Pirko 		if (err)
192111ce2ba3SJiri Pirko 			goto unmap_frags;
192211ce2ba3SJiri Pirko 	}
192311ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, frags);
192411ce2ba3SJiri Pirko 
192511ce2ba3SJiri Pirko 	rocker_desc_gen_clear(desc_info);
192611ce2ba3SJiri Pirko 	rocker_desc_head_set(rocker, &rocker_port->tx_ring, desc_info);
192711ce2ba3SJiri Pirko 
192811ce2ba3SJiri Pirko 	desc_info = rocker_desc_head_get(&rocker_port->tx_ring);
192911ce2ba3SJiri Pirko 	if (!desc_info)
193011ce2ba3SJiri Pirko 		netif_stop_queue(dev);
193111ce2ba3SJiri Pirko 
193211ce2ba3SJiri Pirko 	return NETDEV_TX_OK;
193311ce2ba3SJiri Pirko 
193411ce2ba3SJiri Pirko unmap_frags:
193511ce2ba3SJiri Pirko 	rocker_tx_desc_frags_unmap(rocker_port, desc_info);
193611ce2ba3SJiri Pirko nest_cancel:
193711ce2ba3SJiri Pirko 	rocker_tlv_nest_cancel(desc_info, frags);
193811ce2ba3SJiri Pirko out:
193911ce2ba3SJiri Pirko 	dev_kfree_skb(skb);
194011ce2ba3SJiri Pirko 	dev->stats.tx_dropped++;
194111ce2ba3SJiri Pirko 
194211ce2ba3SJiri Pirko 	return NETDEV_TX_OK;
194311ce2ba3SJiri Pirko }
194411ce2ba3SJiri Pirko 
rocker_port_set_mac_address(struct net_device * dev,void * p)194511ce2ba3SJiri Pirko static int rocker_port_set_mac_address(struct net_device *dev, void *p)
194611ce2ba3SJiri Pirko {
194711ce2ba3SJiri Pirko 	struct sockaddr *addr = p;
194811ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
194911ce2ba3SJiri Pirko 	int err;
195011ce2ba3SJiri Pirko 
195111ce2ba3SJiri Pirko 	if (!is_valid_ether_addr(addr->sa_data))
195211ce2ba3SJiri Pirko 		return -EADDRNOTAVAIL;
195311ce2ba3SJiri Pirko 
195411ce2ba3SJiri Pirko 	err = rocker_cmd_set_port_settings_macaddr(rocker_port, addr->sa_data);
195511ce2ba3SJiri Pirko 	if (err)
195611ce2ba3SJiri Pirko 		return err;
1957a05e4c0aSJakub Kicinski 	eth_hw_addr_set(dev, addr->sa_data);
195811ce2ba3SJiri Pirko 	return 0;
195911ce2ba3SJiri Pirko }
196011ce2ba3SJiri Pirko 
rocker_port_change_mtu(struct net_device * dev,int new_mtu)196111ce2ba3SJiri Pirko static int rocker_port_change_mtu(struct net_device *dev, int new_mtu)
196211ce2ba3SJiri Pirko {
196311ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
196411ce2ba3SJiri Pirko 	int running = netif_running(dev);
196511ce2ba3SJiri Pirko 	int err;
196611ce2ba3SJiri Pirko 
196711ce2ba3SJiri Pirko 	if (running)
196811ce2ba3SJiri Pirko 		rocker_port_stop(dev);
196911ce2ba3SJiri Pirko 
197011ce2ba3SJiri Pirko 	netdev_info(dev, "MTU change from %d to %d\n", dev->mtu, new_mtu);
197111ce2ba3SJiri Pirko 	dev->mtu = new_mtu;
197211ce2ba3SJiri Pirko 
197311ce2ba3SJiri Pirko 	err = rocker_cmd_set_port_settings_mtu(rocker_port, new_mtu);
197411ce2ba3SJiri Pirko 	if (err)
197511ce2ba3SJiri Pirko 		return err;
197611ce2ba3SJiri Pirko 
197711ce2ba3SJiri Pirko 	if (running)
197811ce2ba3SJiri Pirko 		err = rocker_port_open(dev);
197911ce2ba3SJiri Pirko 
198011ce2ba3SJiri Pirko 	return err;
198111ce2ba3SJiri Pirko }
198211ce2ba3SJiri Pirko 
rocker_port_get_phys_port_name(struct net_device * dev,char * buf,size_t len)198311ce2ba3SJiri Pirko static int rocker_port_get_phys_port_name(struct net_device *dev,
198411ce2ba3SJiri Pirko 					  char *buf, size_t len)
198511ce2ba3SJiri Pirko {
198611ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
198711ce2ba3SJiri Pirko 	struct port_name name = { .buf = buf, .len = len };
198811ce2ba3SJiri Pirko 	int err;
198911ce2ba3SJiri Pirko 
199053901cc0SJiri Pirko 	err = rocker_cmd_exec(rocker_port, false,
199111ce2ba3SJiri Pirko 			      rocker_cmd_get_port_settings_prep, NULL,
199211ce2ba3SJiri Pirko 			      rocker_cmd_get_port_settings_phys_name_proc,
199311ce2ba3SJiri Pirko 			      &name);
199411ce2ba3SJiri Pirko 
199511ce2ba3SJiri Pirko 	return err ? -EOPNOTSUPP : 0;
199611ce2ba3SJiri Pirko }
199711ce2ba3SJiri Pirko 
rocker_port_neigh_destroy(struct net_device * dev,struct neighbour * n)1998503eebc2SJiri Pirko static void rocker_port_neigh_destroy(struct net_device *dev,
1999503eebc2SJiri Pirko 				      struct neighbour *n)
200011ce2ba3SJiri Pirko {
200111ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(n->dev);
2002e420114eSJiri Pirko 	int err;
200311ce2ba3SJiri Pirko 
2004e420114eSJiri Pirko 	err = rocker_world_port_neigh_destroy(rocker_port, n);
2005e420114eSJiri Pirko 	if (err)
2006e420114eSJiri Pirko 		netdev_warn(rocker_port->dev, "failed to handle neigh destroy (err %d)\n",
2007e420114eSJiri Pirko 			    err);
200811ce2ba3SJiri Pirko }
200911ce2ba3SJiri Pirko 
rocker_port_get_port_parent_id(struct net_device * dev,struct netdev_phys_item_id * ppid)20107026b8a6SFlorian Fainelli static int rocker_port_get_port_parent_id(struct net_device *dev,
20117026b8a6SFlorian Fainelli 					  struct netdev_phys_item_id *ppid)
20127026b8a6SFlorian Fainelli {
20137026b8a6SFlorian Fainelli 	const struct rocker_port *rocker_port = netdev_priv(dev);
20147026b8a6SFlorian Fainelli 	const struct rocker *rocker = rocker_port->rocker;
20157026b8a6SFlorian Fainelli 
20167026b8a6SFlorian Fainelli 	ppid->id_len = sizeof(rocker->hw.id);
20177026b8a6SFlorian Fainelli 	memcpy(&ppid->id, &rocker->hw.id, ppid->id_len);
20187026b8a6SFlorian Fainelli 
20197026b8a6SFlorian Fainelli 	return 0;
20207026b8a6SFlorian Fainelli }
20217026b8a6SFlorian Fainelli 
202211ce2ba3SJiri Pirko static const struct net_device_ops rocker_port_netdev_ops = {
202311ce2ba3SJiri Pirko 	.ndo_open			= rocker_port_open,
202411ce2ba3SJiri Pirko 	.ndo_stop			= rocker_port_stop,
202511ce2ba3SJiri Pirko 	.ndo_start_xmit			= rocker_port_xmit,
202611ce2ba3SJiri Pirko 	.ndo_set_mac_address		= rocker_port_set_mac_address,
202711ce2ba3SJiri Pirko 	.ndo_change_mtu			= rocker_port_change_mtu,
202811ce2ba3SJiri Pirko 	.ndo_get_phys_port_name		= rocker_port_get_phys_port_name,
202911ce2ba3SJiri Pirko 	.ndo_neigh_destroy		= rocker_port_neigh_destroy,
20307026b8a6SFlorian Fainelli 	.ndo_get_port_parent_id		= rocker_port_get_port_parent_id,
203111ce2ba3SJiri Pirko };
203211ce2ba3SJiri Pirko 
203311ce2ba3SJiri Pirko /********************
203411ce2ba3SJiri Pirko  * swdev interface
203511ce2ba3SJiri Pirko  ********************/
203611ce2ba3SJiri Pirko 
rocker_port_attr_set(struct net_device * dev,const struct switchdev_attr * attr)203711ce2ba3SJiri Pirko static int rocker_port_attr_set(struct net_device *dev,
2038bae33f2bSVladimir Oltean 				const struct switchdev_attr *attr)
203911ce2ba3SJiri Pirko {
204011ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
204111ce2ba3SJiri Pirko 	int err = 0;
204211ce2ba3SJiri Pirko 
204311ce2ba3SJiri Pirko 	switch (attr->id) {
204411ce2ba3SJiri Pirko 	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
2045e420114eSJiri Pirko 		err = rocker_world_port_attr_stp_state_set(rocker_port,
2046bae33f2bSVladimir Oltean 							   attr->u.stp_state);
204711ce2ba3SJiri Pirko 		break;
204893700458SFlorian Fainelli 	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
204993700458SFlorian Fainelli 		err = rocker_world_port_attr_pre_bridge_flags_set(rocker_port,
2050bae33f2bSVladimir Oltean 								  attr->u.brport_flags);
20517a25c6c0SFlorian Fainelli 		break;
205211ce2ba3SJiri Pirko 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
2053e420114eSJiri Pirko 		err = rocker_world_port_attr_bridge_flags_set(rocker_port,
2054bae33f2bSVladimir Oltean 							      attr->u.brport_flags);
205511ce2ba3SJiri Pirko 		break;
205611ce2ba3SJiri Pirko 	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
2057e420114eSJiri Pirko 		err = rocker_world_port_attr_bridge_ageing_time_set(rocker_port,
2058bae33f2bSVladimir Oltean 								    attr->u.ageing_time);
205911ce2ba3SJiri Pirko 		break;
206011ce2ba3SJiri Pirko 	default:
206111ce2ba3SJiri Pirko 		err = -EOPNOTSUPP;
206211ce2ba3SJiri Pirko 		break;
206311ce2ba3SJiri Pirko 	}
206411ce2ba3SJiri Pirko 
206511ce2ba3SJiri Pirko 	return err;
206611ce2ba3SJiri Pirko }
206711ce2ba3SJiri Pirko 
rocker_port_obj_add(struct net_device * dev,const struct switchdev_obj * obj)206811ce2ba3SJiri Pirko static int rocker_port_obj_add(struct net_device *dev,
2069ffb68fc5SVladimir Oltean 			       const struct switchdev_obj *obj)
207011ce2ba3SJiri Pirko {
207111ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
207211ce2ba3SJiri Pirko 	int err = 0;
207311ce2ba3SJiri Pirko 
207411ce2ba3SJiri Pirko 	switch (obj->id) {
207511ce2ba3SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
2076e420114eSJiri Pirko 		err = rocker_world_port_obj_vlan_add(rocker_port,
2077ffb68fc5SVladimir Oltean 						     SWITCHDEV_OBJ_PORT_VLAN(obj));
207811ce2ba3SJiri Pirko 		break;
207911ce2ba3SJiri Pirko 	default:
208011ce2ba3SJiri Pirko 		err = -EOPNOTSUPP;
208111ce2ba3SJiri Pirko 		break;
208211ce2ba3SJiri Pirko 	}
208311ce2ba3SJiri Pirko 
208411ce2ba3SJiri Pirko 	return err;
208511ce2ba3SJiri Pirko }
208611ce2ba3SJiri Pirko 
rocker_port_obj_del(struct net_device * dev,const struct switchdev_obj * obj)208711ce2ba3SJiri Pirko static int rocker_port_obj_del(struct net_device *dev,
208811ce2ba3SJiri Pirko 			       const struct switchdev_obj *obj)
208911ce2ba3SJiri Pirko {
209011ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
209111ce2ba3SJiri Pirko 	int err = 0;
209211ce2ba3SJiri Pirko 
209311ce2ba3SJiri Pirko 	switch (obj->id) {
209411ce2ba3SJiri Pirko 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
2095e420114eSJiri Pirko 		err = rocker_world_port_obj_vlan_del(rocker_port,
2096e420114eSJiri Pirko 						     SWITCHDEV_OBJ_PORT_VLAN(obj));
209711ce2ba3SJiri Pirko 		break;
209811ce2ba3SJiri Pirko 	default:
209911ce2ba3SJiri Pirko 		err = -EOPNOTSUPP;
210011ce2ba3SJiri Pirko 		break;
210111ce2ba3SJiri Pirko 	}
210211ce2ba3SJiri Pirko 
210311ce2ba3SJiri Pirko 	return err;
210411ce2ba3SJiri Pirko }
210511ce2ba3SJiri Pirko 
2106db701955SIdo Schimmel struct rocker_fib_event_work {
2107db701955SIdo Schimmel 	struct work_struct work;
21085d7bfd14SIdo Schimmel 	union {
2109db701955SIdo Schimmel 		struct fib_entry_notifier_info fen_info;
21105d7bfd14SIdo Schimmel 		struct fib_rule_notifier_info fr_info;
21115d7bfd14SIdo Schimmel 	};
2112db701955SIdo Schimmel 	struct rocker *rocker;
2113db701955SIdo Schimmel 	unsigned long event;
2114db701955SIdo Schimmel };
2115db701955SIdo Schimmel 
rocker_router_fib_event_work(struct work_struct * work)2116db701955SIdo Schimmel static void rocker_router_fib_event_work(struct work_struct *work)
2117936bd486SJiri Pirko {
2118db701955SIdo Schimmel 	struct rocker_fib_event_work *fib_work =
2119db701955SIdo Schimmel 		container_of(work, struct rocker_fib_event_work, work);
2120db701955SIdo Schimmel 	struct rocker *rocker = fib_work->rocker;
21215d7bfd14SIdo Schimmel 	struct fib_rule *rule;
2122936bd486SJiri Pirko 	int err;
2123936bd486SJiri Pirko 
2124db701955SIdo Schimmel 	/* Protect internal structures from changes */
2125db701955SIdo Schimmel 	rtnl_lock();
2126db701955SIdo Schimmel 	switch (fib_work->event) {
2127446f7391SIdo Schimmel 	case FIB_EVENT_ENTRY_REPLACE:
2128db701955SIdo Schimmel 		err = rocker_world_fib4_add(rocker, &fib_work->fen_info);
2129936bd486SJiri Pirko 		if (err)
2130936bd486SJiri Pirko 			rocker_world_fib4_abort(rocker);
2131db701955SIdo Schimmel 		fib_info_put(fib_work->fen_info.fi);
2132936bd486SJiri Pirko 		break;
2133936bd486SJiri Pirko 	case FIB_EVENT_ENTRY_DEL:
2134db701955SIdo Schimmel 		rocker_world_fib4_del(rocker, &fib_work->fen_info);
2135db701955SIdo Schimmel 		fib_info_put(fib_work->fen_info.fi);
2136936bd486SJiri Pirko 		break;
2137df561f66SGustavo A. R. Silva 	case FIB_EVENT_RULE_ADD:
2138936bd486SJiri Pirko 	case FIB_EVENT_RULE_DEL:
21395d7bfd14SIdo Schimmel 		rule = fib_work->fr_info.rule;
21405d7bfd14SIdo Schimmel 		if (!fib4_rule_default(rule))
2141936bd486SJiri Pirko 			rocker_world_fib4_abort(rocker);
21425d7bfd14SIdo Schimmel 		fib_rule_put(rule);
2143936bd486SJiri Pirko 		break;
2144936bd486SJiri Pirko 	}
2145db701955SIdo Schimmel 	rtnl_unlock();
2146db701955SIdo Schimmel 	kfree(fib_work);
2147db701955SIdo Schimmel }
2148db701955SIdo Schimmel 
2149db701955SIdo Schimmel /* Called with rcu_read_lock() */
rocker_router_fib_event(struct notifier_block * nb,unsigned long event,void * ptr)2150db701955SIdo Schimmel static int rocker_router_fib_event(struct notifier_block *nb,
2151db701955SIdo Schimmel 				   unsigned long event, void *ptr)
2152db701955SIdo Schimmel {
2153db701955SIdo Schimmel 	struct rocker *rocker = container_of(nb, struct rocker, fib_nb);
2154db701955SIdo Schimmel 	struct rocker_fib_event_work *fib_work;
2155d371ac1eSIdo Schimmel 	struct fib_notifier_info *info = ptr;
2156d371ac1eSIdo Schimmel 
2157d371ac1eSIdo Schimmel 	if (info->family != AF_INET)
2158d371ac1eSIdo Schimmel 		return NOTIFY_DONE;
2159db701955SIdo Schimmel 
2160db701955SIdo Schimmel 	fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
2161db701955SIdo Schimmel 	if (WARN_ON(!fib_work))
2162db701955SIdo Schimmel 		return NOTIFY_BAD;
2163db701955SIdo Schimmel 
2164db701955SIdo Schimmel 	INIT_WORK(&fib_work->work, rocker_router_fib_event_work);
2165db701955SIdo Schimmel 	fib_work->rocker = rocker;
2166db701955SIdo Schimmel 	fib_work->event = event;
2167db701955SIdo Schimmel 
2168db701955SIdo Schimmel 	switch (event) {
2169df561f66SGustavo A. R. Silva 	case FIB_EVENT_ENTRY_REPLACE:
2170db701955SIdo Schimmel 	case FIB_EVENT_ENTRY_DEL:
217119a9d136SDavid Ahern 		if (info->family == AF_INET) {
217219a9d136SDavid Ahern 			struct fib_entry_notifier_info *fen_info = ptr;
217319a9d136SDavid Ahern 
217419a9d136SDavid Ahern 			if (fen_info->fi->fib_nh_is_v6) {
217519a9d136SDavid Ahern 				NL_SET_ERR_MSG_MOD(info->extack, "IPv6 gateway with IPv4 route is not supported");
2176011f1754SColin Ian King 				kfree(fib_work);
217719a9d136SDavid Ahern 				return notifier_from_errno(-EINVAL);
217819a9d136SDavid Ahern 			}
2179dbcc4fa7SDavid Ahern 			if (fen_info->fi->nh) {
2180dbcc4fa7SDavid Ahern 				NL_SET_ERR_MSG_MOD(info->extack, "IPv4 route with nexthop objects is not supported");
2181011f1754SColin Ian King 				kfree(fib_work);
2182dbcc4fa7SDavid Ahern 				return notifier_from_errno(-EINVAL);
2183dbcc4fa7SDavid Ahern 			}
218419a9d136SDavid Ahern 		}
218519a9d136SDavid Ahern 
2186db701955SIdo Schimmel 		memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info));
2187db701955SIdo Schimmel 		/* Take referece on fib_info to prevent it from being
2188db701955SIdo Schimmel 		 * freed while work is queued. Release it afterwards.
2189db701955SIdo Schimmel 		 */
2190db701955SIdo Schimmel 		fib_info_hold(fib_work->fen_info.fi);
2191db701955SIdo Schimmel 		break;
2192df561f66SGustavo A. R. Silva 	case FIB_EVENT_RULE_ADD:
21935d7bfd14SIdo Schimmel 	case FIB_EVENT_RULE_DEL:
21945d7bfd14SIdo Schimmel 		memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info));
21955d7bfd14SIdo Schimmel 		fib_rule_get(fib_work->fr_info.rule);
21965d7bfd14SIdo Schimmel 		break;
2197db701955SIdo Schimmel 	}
2198db701955SIdo Schimmel 
2199db701955SIdo Schimmel 	queue_work(rocker->rocker_owq, &fib_work->work);
2200db701955SIdo Schimmel 
2201936bd486SJiri Pirko 	return NOTIFY_DONE;
2202936bd486SJiri Pirko }
2203936bd486SJiri Pirko 
220411ce2ba3SJiri Pirko /********************
220511ce2ba3SJiri Pirko  * ethtool interface
220611ce2ba3SJiri Pirko  ********************/
220711ce2ba3SJiri Pirko 
2208de480150SPhilippe Reynes static int
rocker_port_get_link_ksettings(struct net_device * dev,struct ethtool_link_ksettings * ecmd)2209de480150SPhilippe Reynes rocker_port_get_link_ksettings(struct net_device *dev,
2210de480150SPhilippe Reynes 			       struct ethtool_link_ksettings *ecmd)
221111ce2ba3SJiri Pirko {
221211ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
221311ce2ba3SJiri Pirko 
221411ce2ba3SJiri Pirko 	return rocker_cmd_get_port_settings_ethtool(rocker_port, ecmd);
221511ce2ba3SJiri Pirko }
221611ce2ba3SJiri Pirko 
2217de480150SPhilippe Reynes static int
rocker_port_set_link_ksettings(struct net_device * dev,const struct ethtool_link_ksettings * ecmd)2218de480150SPhilippe Reynes rocker_port_set_link_ksettings(struct net_device *dev,
2219de480150SPhilippe Reynes 			       const struct ethtool_link_ksettings *ecmd)
222011ce2ba3SJiri Pirko {
222111ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
222211ce2ba3SJiri Pirko 
222311ce2ba3SJiri Pirko 	return rocker_cmd_set_port_settings_ethtool(rocker_port, ecmd);
222411ce2ba3SJiri Pirko }
222511ce2ba3SJiri Pirko 
rocker_port_get_drvinfo(struct net_device * dev,struct ethtool_drvinfo * drvinfo)222611ce2ba3SJiri Pirko static void rocker_port_get_drvinfo(struct net_device *dev,
222711ce2ba3SJiri Pirko 				    struct ethtool_drvinfo *drvinfo)
222811ce2ba3SJiri Pirko {
2229f029c781SWolfram Sang 	strscpy(drvinfo->driver, rocker_driver_name, sizeof(drvinfo->driver));
2230f029c781SWolfram Sang 	strscpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version));
223111ce2ba3SJiri Pirko }
223211ce2ba3SJiri Pirko 
223311ce2ba3SJiri Pirko static struct rocker_port_stats {
223411ce2ba3SJiri Pirko 	char str[ETH_GSTRING_LEN];
223511ce2ba3SJiri Pirko 	int type;
223611ce2ba3SJiri Pirko } rocker_port_stats[] = {
223711ce2ba3SJiri Pirko 	{ "rx_packets", ROCKER_TLV_CMD_PORT_STATS_RX_PKTS,    },
223811ce2ba3SJiri Pirko 	{ "rx_bytes",   ROCKER_TLV_CMD_PORT_STATS_RX_BYTES,   },
223911ce2ba3SJiri Pirko 	{ "rx_dropped", ROCKER_TLV_CMD_PORT_STATS_RX_DROPPED, },
224011ce2ba3SJiri Pirko 	{ "rx_errors",  ROCKER_TLV_CMD_PORT_STATS_RX_ERRORS,  },
224111ce2ba3SJiri Pirko 
224211ce2ba3SJiri Pirko 	{ "tx_packets", ROCKER_TLV_CMD_PORT_STATS_TX_PKTS,    },
224311ce2ba3SJiri Pirko 	{ "tx_bytes",   ROCKER_TLV_CMD_PORT_STATS_TX_BYTES,   },
224411ce2ba3SJiri Pirko 	{ "tx_dropped", ROCKER_TLV_CMD_PORT_STATS_TX_DROPPED, },
224511ce2ba3SJiri Pirko 	{ "tx_errors",  ROCKER_TLV_CMD_PORT_STATS_TX_ERRORS,  },
224611ce2ba3SJiri Pirko };
224711ce2ba3SJiri Pirko 
224811ce2ba3SJiri Pirko #define ROCKER_PORT_STATS_LEN  ARRAY_SIZE(rocker_port_stats)
224911ce2ba3SJiri Pirko 
rocker_port_get_strings(struct net_device * netdev,u32 stringset,u8 * data)225011ce2ba3SJiri Pirko static void rocker_port_get_strings(struct net_device *netdev, u32 stringset,
225111ce2ba3SJiri Pirko 				    u8 *data)
225211ce2ba3SJiri Pirko {
225311ce2ba3SJiri Pirko 	u8 *p = data;
225411ce2ba3SJiri Pirko 	int i;
225511ce2ba3SJiri Pirko 
225611ce2ba3SJiri Pirko 	switch (stringset) {
225711ce2ba3SJiri Pirko 	case ETH_SS_STATS:
225811ce2ba3SJiri Pirko 		for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) {
225911ce2ba3SJiri Pirko 			memcpy(p, rocker_port_stats[i].str, ETH_GSTRING_LEN);
226011ce2ba3SJiri Pirko 			p += ETH_GSTRING_LEN;
226111ce2ba3SJiri Pirko 		}
226211ce2ba3SJiri Pirko 		break;
226311ce2ba3SJiri Pirko 	}
226411ce2ba3SJiri Pirko }
226511ce2ba3SJiri Pirko 
226611ce2ba3SJiri Pirko static int
rocker_cmd_get_port_stats_prep(const struct rocker_port * rocker_port,struct rocker_desc_info * desc_info,void * priv)226711ce2ba3SJiri Pirko rocker_cmd_get_port_stats_prep(const struct rocker_port *rocker_port,
226811ce2ba3SJiri Pirko 			       struct rocker_desc_info *desc_info,
226911ce2ba3SJiri Pirko 			       void *priv)
227011ce2ba3SJiri Pirko {
227111ce2ba3SJiri Pirko 	struct rocker_tlv *cmd_stats;
227211ce2ba3SJiri Pirko 
227311ce2ba3SJiri Pirko 	if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
227411ce2ba3SJiri Pirko 			       ROCKER_TLV_CMD_TYPE_GET_PORT_STATS))
227511ce2ba3SJiri Pirko 		return -EMSGSIZE;
227611ce2ba3SJiri Pirko 
227711ce2ba3SJiri Pirko 	cmd_stats = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
227811ce2ba3SJiri Pirko 	if (!cmd_stats)
227911ce2ba3SJiri Pirko 		return -EMSGSIZE;
228011ce2ba3SJiri Pirko 
228111ce2ba3SJiri Pirko 	if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_STATS_PPORT,
228211ce2ba3SJiri Pirko 			       rocker_port->pport))
228311ce2ba3SJiri Pirko 		return -EMSGSIZE;
228411ce2ba3SJiri Pirko 
228511ce2ba3SJiri Pirko 	rocker_tlv_nest_end(desc_info, cmd_stats);
228611ce2ba3SJiri Pirko 
228711ce2ba3SJiri Pirko 	return 0;
228811ce2ba3SJiri Pirko }
228911ce2ba3SJiri Pirko 
229011ce2ba3SJiri Pirko static int
rocker_cmd_get_port_stats_ethtool_proc(const struct rocker_port * rocker_port,const struct rocker_desc_info * desc_info,void * priv)229111ce2ba3SJiri Pirko rocker_cmd_get_port_stats_ethtool_proc(const struct rocker_port *rocker_port,
229211ce2ba3SJiri Pirko 				       const struct rocker_desc_info *desc_info,
229311ce2ba3SJiri Pirko 				       void *priv)
229411ce2ba3SJiri Pirko {
229511ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
229611ce2ba3SJiri Pirko 	const struct rocker_tlv *stats_attrs[ROCKER_TLV_CMD_PORT_STATS_MAX + 1];
229711ce2ba3SJiri Pirko 	const struct rocker_tlv *pattr;
229811ce2ba3SJiri Pirko 	u32 pport;
229911ce2ba3SJiri Pirko 	u64 *data = priv;
230011ce2ba3SJiri Pirko 	int i;
230111ce2ba3SJiri Pirko 
230211ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
230311ce2ba3SJiri Pirko 
230411ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_CMD_INFO])
230511ce2ba3SJiri Pirko 		return -EIO;
230611ce2ba3SJiri Pirko 
230711ce2ba3SJiri Pirko 	rocker_tlv_parse_nested(stats_attrs, ROCKER_TLV_CMD_PORT_STATS_MAX,
230811ce2ba3SJiri Pirko 				attrs[ROCKER_TLV_CMD_INFO]);
230911ce2ba3SJiri Pirko 
231011ce2ba3SJiri Pirko 	if (!stats_attrs[ROCKER_TLV_CMD_PORT_STATS_PPORT])
231111ce2ba3SJiri Pirko 		return -EIO;
231211ce2ba3SJiri Pirko 
231311ce2ba3SJiri Pirko 	pport = rocker_tlv_get_u32(stats_attrs[ROCKER_TLV_CMD_PORT_STATS_PPORT]);
231411ce2ba3SJiri Pirko 	if (pport != rocker_port->pport)
231511ce2ba3SJiri Pirko 		return -EIO;
231611ce2ba3SJiri Pirko 
231711ce2ba3SJiri Pirko 	for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) {
231811ce2ba3SJiri Pirko 		pattr = stats_attrs[rocker_port_stats[i].type];
231911ce2ba3SJiri Pirko 		if (!pattr)
232011ce2ba3SJiri Pirko 			continue;
232111ce2ba3SJiri Pirko 
232211ce2ba3SJiri Pirko 		data[i] = rocker_tlv_get_u64(pattr);
232311ce2ba3SJiri Pirko 	}
232411ce2ba3SJiri Pirko 
232511ce2ba3SJiri Pirko 	return 0;
232611ce2ba3SJiri Pirko }
232711ce2ba3SJiri Pirko 
rocker_cmd_get_port_stats_ethtool(struct rocker_port * rocker_port,void * priv)232811ce2ba3SJiri Pirko static int rocker_cmd_get_port_stats_ethtool(struct rocker_port *rocker_port,
232911ce2ba3SJiri Pirko 					     void *priv)
233011ce2ba3SJiri Pirko {
233153901cc0SJiri Pirko 	return rocker_cmd_exec(rocker_port, false,
233211ce2ba3SJiri Pirko 			       rocker_cmd_get_port_stats_prep, NULL,
233311ce2ba3SJiri Pirko 			       rocker_cmd_get_port_stats_ethtool_proc,
233411ce2ba3SJiri Pirko 			       priv);
233511ce2ba3SJiri Pirko }
233611ce2ba3SJiri Pirko 
rocker_port_get_stats(struct net_device * dev,struct ethtool_stats * stats,u64 * data)233711ce2ba3SJiri Pirko static void rocker_port_get_stats(struct net_device *dev,
233811ce2ba3SJiri Pirko 				  struct ethtool_stats *stats, u64 *data)
233911ce2ba3SJiri Pirko {
234011ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = netdev_priv(dev);
234111ce2ba3SJiri Pirko 
234211ce2ba3SJiri Pirko 	if (rocker_cmd_get_port_stats_ethtool(rocker_port, data) != 0) {
234311ce2ba3SJiri Pirko 		int i;
234411ce2ba3SJiri Pirko 
234511ce2ba3SJiri Pirko 		for (i = 0; i < ARRAY_SIZE(rocker_port_stats); ++i)
234611ce2ba3SJiri Pirko 			data[i] = 0;
234711ce2ba3SJiri Pirko 	}
234811ce2ba3SJiri Pirko }
234911ce2ba3SJiri Pirko 
rocker_port_get_sset_count(struct net_device * netdev,int sset)235011ce2ba3SJiri Pirko static int rocker_port_get_sset_count(struct net_device *netdev, int sset)
235111ce2ba3SJiri Pirko {
235211ce2ba3SJiri Pirko 	switch (sset) {
235311ce2ba3SJiri Pirko 	case ETH_SS_STATS:
235411ce2ba3SJiri Pirko 		return ROCKER_PORT_STATS_LEN;
235511ce2ba3SJiri Pirko 	default:
235611ce2ba3SJiri Pirko 		return -EOPNOTSUPP;
235711ce2ba3SJiri Pirko 	}
235811ce2ba3SJiri Pirko }
235911ce2ba3SJiri Pirko 
236011ce2ba3SJiri Pirko static const struct ethtool_ops rocker_port_ethtool_ops = {
236111ce2ba3SJiri Pirko 	.get_drvinfo		= rocker_port_get_drvinfo,
236211ce2ba3SJiri Pirko 	.get_link		= ethtool_op_get_link,
236311ce2ba3SJiri Pirko 	.get_strings		= rocker_port_get_strings,
236411ce2ba3SJiri Pirko 	.get_ethtool_stats	= rocker_port_get_stats,
236511ce2ba3SJiri Pirko 	.get_sset_count		= rocker_port_get_sset_count,
2366de480150SPhilippe Reynes 	.get_link_ksettings	= rocker_port_get_link_ksettings,
2367de480150SPhilippe Reynes 	.set_link_ksettings	= rocker_port_set_link_ksettings,
236811ce2ba3SJiri Pirko };
236911ce2ba3SJiri Pirko 
237011ce2ba3SJiri Pirko /*****************
237111ce2ba3SJiri Pirko  * NAPI interface
237211ce2ba3SJiri Pirko  *****************/
237311ce2ba3SJiri Pirko 
rocker_port_napi_tx_get(struct napi_struct * napi)237411ce2ba3SJiri Pirko static struct rocker_port *rocker_port_napi_tx_get(struct napi_struct *napi)
237511ce2ba3SJiri Pirko {
237611ce2ba3SJiri Pirko 	return container_of(napi, struct rocker_port, napi_tx);
237711ce2ba3SJiri Pirko }
237811ce2ba3SJiri Pirko 
rocker_port_poll_tx(struct napi_struct * napi,int budget)237911ce2ba3SJiri Pirko static int rocker_port_poll_tx(struct napi_struct *napi, int budget)
238011ce2ba3SJiri Pirko {
238111ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = rocker_port_napi_tx_get(napi);
238211ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
238311ce2ba3SJiri Pirko 	const struct rocker_desc_info *desc_info;
238411ce2ba3SJiri Pirko 	u32 credits = 0;
238511ce2ba3SJiri Pirko 	int err;
238611ce2ba3SJiri Pirko 
238711ce2ba3SJiri Pirko 	/* Cleanup tx descriptors */
238811ce2ba3SJiri Pirko 	while ((desc_info = rocker_desc_tail_get(&rocker_port->tx_ring))) {
238911ce2ba3SJiri Pirko 		struct sk_buff *skb;
239011ce2ba3SJiri Pirko 
239111ce2ba3SJiri Pirko 		err = rocker_desc_err(desc_info);
239211ce2ba3SJiri Pirko 		if (err && net_ratelimit())
239311ce2ba3SJiri Pirko 			netdev_err(rocker_port->dev, "tx desc received with err %d\n",
239411ce2ba3SJiri Pirko 				   err);
239511ce2ba3SJiri Pirko 		rocker_tx_desc_frags_unmap(rocker_port, desc_info);
239611ce2ba3SJiri Pirko 
239711ce2ba3SJiri Pirko 		skb = rocker_desc_cookie_ptr_get(desc_info);
239811ce2ba3SJiri Pirko 		if (err == 0) {
239911ce2ba3SJiri Pirko 			rocker_port->dev->stats.tx_packets++;
240011ce2ba3SJiri Pirko 			rocker_port->dev->stats.tx_bytes += skb->len;
240111ce2ba3SJiri Pirko 		} else {
240211ce2ba3SJiri Pirko 			rocker_port->dev->stats.tx_errors++;
240311ce2ba3SJiri Pirko 		}
240411ce2ba3SJiri Pirko 
240511ce2ba3SJiri Pirko 		dev_kfree_skb_any(skb);
240611ce2ba3SJiri Pirko 		credits++;
240711ce2ba3SJiri Pirko 	}
240811ce2ba3SJiri Pirko 
240911ce2ba3SJiri Pirko 	if (credits && netif_queue_stopped(rocker_port->dev))
241011ce2ba3SJiri Pirko 		netif_wake_queue(rocker_port->dev);
241111ce2ba3SJiri Pirko 
241211ce2ba3SJiri Pirko 	napi_complete(napi);
241311ce2ba3SJiri Pirko 	rocker_dma_ring_credits_set(rocker, &rocker_port->tx_ring, credits);
241411ce2ba3SJiri Pirko 
241511ce2ba3SJiri Pirko 	return 0;
241611ce2ba3SJiri Pirko }
241711ce2ba3SJiri Pirko 
rocker_port_rx_proc(const struct rocker * rocker,const struct rocker_port * rocker_port,struct rocker_desc_info * desc_info)241811ce2ba3SJiri Pirko static int rocker_port_rx_proc(const struct rocker *rocker,
241911ce2ba3SJiri Pirko 			       const struct rocker_port *rocker_port,
242011ce2ba3SJiri Pirko 			       struct rocker_desc_info *desc_info)
242111ce2ba3SJiri Pirko {
242211ce2ba3SJiri Pirko 	const struct rocker_tlv *attrs[ROCKER_TLV_RX_MAX + 1];
242311ce2ba3SJiri Pirko 	struct sk_buff *skb = rocker_desc_cookie_ptr_get(desc_info);
242411ce2ba3SJiri Pirko 	size_t rx_len;
242511ce2ba3SJiri Pirko 	u16 rx_flags = 0;
242611ce2ba3SJiri Pirko 
242711ce2ba3SJiri Pirko 	if (!skb)
242811ce2ba3SJiri Pirko 		return -ENOENT;
242911ce2ba3SJiri Pirko 
243011ce2ba3SJiri Pirko 	rocker_tlv_parse_desc(attrs, ROCKER_TLV_RX_MAX, desc_info);
243111ce2ba3SJiri Pirko 	if (!attrs[ROCKER_TLV_RX_FRAG_LEN])
243211ce2ba3SJiri Pirko 		return -EINVAL;
243311ce2ba3SJiri Pirko 	if (attrs[ROCKER_TLV_RX_FLAGS])
243411ce2ba3SJiri Pirko 		rx_flags = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FLAGS]);
243511ce2ba3SJiri Pirko 
243611ce2ba3SJiri Pirko 	rocker_dma_rx_ring_skb_unmap(rocker, attrs);
243711ce2ba3SJiri Pirko 
243811ce2ba3SJiri Pirko 	rx_len = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FRAG_LEN]);
243911ce2ba3SJiri Pirko 	skb_put(skb, rx_len);
244011ce2ba3SJiri Pirko 	skb->protocol = eth_type_trans(skb, rocker_port->dev);
244111ce2ba3SJiri Pirko 
244211ce2ba3SJiri Pirko 	if (rx_flags & ROCKER_RX_FLAGS_FWD_OFFLOAD)
24436bc506b4SIdo Schimmel 		skb->offload_fwd_mark = 1;
244411ce2ba3SJiri Pirko 
244511ce2ba3SJiri Pirko 	rocker_port->dev->stats.rx_packets++;
244611ce2ba3SJiri Pirko 	rocker_port->dev->stats.rx_bytes += skb->len;
244711ce2ba3SJiri Pirko 
244811ce2ba3SJiri Pirko 	netif_receive_skb(skb);
244911ce2ba3SJiri Pirko 
245011ce2ba3SJiri Pirko 	return rocker_dma_rx_ring_skb_alloc(rocker_port, desc_info);
245111ce2ba3SJiri Pirko }
245211ce2ba3SJiri Pirko 
rocker_port_napi_rx_get(struct napi_struct * napi)245311ce2ba3SJiri Pirko static struct rocker_port *rocker_port_napi_rx_get(struct napi_struct *napi)
245411ce2ba3SJiri Pirko {
245511ce2ba3SJiri Pirko 	return container_of(napi, struct rocker_port, napi_rx);
245611ce2ba3SJiri Pirko }
245711ce2ba3SJiri Pirko 
rocker_port_poll_rx(struct napi_struct * napi,int budget)245811ce2ba3SJiri Pirko static int rocker_port_poll_rx(struct napi_struct *napi, int budget)
245911ce2ba3SJiri Pirko {
246011ce2ba3SJiri Pirko 	struct rocker_port *rocker_port = rocker_port_napi_rx_get(napi);
246111ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
246211ce2ba3SJiri Pirko 	struct rocker_desc_info *desc_info;
246311ce2ba3SJiri Pirko 	u32 credits = 0;
246411ce2ba3SJiri Pirko 	int err;
246511ce2ba3SJiri Pirko 
246611ce2ba3SJiri Pirko 	/* Process rx descriptors */
246711ce2ba3SJiri Pirko 	while (credits < budget &&
246811ce2ba3SJiri Pirko 	       (desc_info = rocker_desc_tail_get(&rocker_port->rx_ring))) {
246911ce2ba3SJiri Pirko 		err = rocker_desc_err(desc_info);
247011ce2ba3SJiri Pirko 		if (err) {
247111ce2ba3SJiri Pirko 			if (net_ratelimit())
247211ce2ba3SJiri Pirko 				netdev_err(rocker_port->dev, "rx desc received with err %d\n",
247311ce2ba3SJiri Pirko 					   err);
247411ce2ba3SJiri Pirko 		} else {
247511ce2ba3SJiri Pirko 			err = rocker_port_rx_proc(rocker, rocker_port,
247611ce2ba3SJiri Pirko 						  desc_info);
247711ce2ba3SJiri Pirko 			if (err && net_ratelimit())
247811ce2ba3SJiri Pirko 				netdev_err(rocker_port->dev, "rx processing failed with err %d\n",
247911ce2ba3SJiri Pirko 					   err);
248011ce2ba3SJiri Pirko 		}
248111ce2ba3SJiri Pirko 		if (err)
248211ce2ba3SJiri Pirko 			rocker_port->dev->stats.rx_errors++;
248311ce2ba3SJiri Pirko 
248411ce2ba3SJiri Pirko 		rocker_desc_gen_clear(desc_info);
248511ce2ba3SJiri Pirko 		rocker_desc_head_set(rocker, &rocker_port->rx_ring, desc_info);
248611ce2ba3SJiri Pirko 		credits++;
248711ce2ba3SJiri Pirko 	}
248811ce2ba3SJiri Pirko 
248911ce2ba3SJiri Pirko 	if (credits < budget)
24906ad20165SEric Dumazet 		napi_complete_done(napi, credits);
249111ce2ba3SJiri Pirko 
249211ce2ba3SJiri Pirko 	rocker_dma_ring_credits_set(rocker, &rocker_port->rx_ring, credits);
249311ce2ba3SJiri Pirko 
249411ce2ba3SJiri Pirko 	return credits;
249511ce2ba3SJiri Pirko }
249611ce2ba3SJiri Pirko 
249711ce2ba3SJiri Pirko /*****************
249811ce2ba3SJiri Pirko  * PCI driver ops
249911ce2ba3SJiri Pirko  *****************/
250011ce2ba3SJiri Pirko 
rocker_carrier_init(const struct rocker_port * rocker_port)250111ce2ba3SJiri Pirko static void rocker_carrier_init(const struct rocker_port *rocker_port)
250211ce2ba3SJiri Pirko {
250311ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
250411ce2ba3SJiri Pirko 	u64 link_status = rocker_read64(rocker, PORT_PHYS_LINK_STATUS);
250511ce2ba3SJiri Pirko 	bool link_up;
250611ce2ba3SJiri Pirko 
250711ce2ba3SJiri Pirko 	link_up = link_status & (1 << rocker_port->pport);
250811ce2ba3SJiri Pirko 	if (link_up)
250911ce2ba3SJiri Pirko 		netif_carrier_on(rocker_port->dev);
251011ce2ba3SJiri Pirko 	else
251111ce2ba3SJiri Pirko 		netif_carrier_off(rocker_port->dev);
251211ce2ba3SJiri Pirko }
251311ce2ba3SJiri Pirko 
rocker_remove_ports(struct rocker * rocker)2514e420114eSJiri Pirko static void rocker_remove_ports(struct rocker *rocker)
251511ce2ba3SJiri Pirko {
251611ce2ba3SJiri Pirko 	struct rocker_port *rocker_port;
251711ce2ba3SJiri Pirko 	int i;
251811ce2ba3SJiri Pirko 
251911ce2ba3SJiri Pirko 	for (i = 0; i < rocker->port_count; i++) {
252011ce2ba3SJiri Pirko 		rocker_port = rocker->ports[i];
252111ce2ba3SJiri Pirko 		if (!rocker_port)
252211ce2ba3SJiri Pirko 			continue;
2523e420114eSJiri Pirko 		rocker_world_port_fini(rocker_port);
252411ce2ba3SJiri Pirko 		unregister_netdev(rocker_port->dev);
2525e420114eSJiri Pirko 		rocker_world_port_post_fini(rocker_port);
252611ce2ba3SJiri Pirko 		free_netdev(rocker_port->dev);
252711ce2ba3SJiri Pirko 	}
2528e420114eSJiri Pirko 	rocker_world_fini(rocker);
252911ce2ba3SJiri Pirko 	kfree(rocker->ports);
253011ce2ba3SJiri Pirko }
253111ce2ba3SJiri Pirko 
rocker_port_dev_addr_init(struct rocker_port * rocker_port)253211ce2ba3SJiri Pirko static void rocker_port_dev_addr_init(struct rocker_port *rocker_port)
253311ce2ba3SJiri Pirko {
253411ce2ba3SJiri Pirko 	const struct rocker *rocker = rocker_port->rocker;
253511ce2ba3SJiri Pirko 	const struct pci_dev *pdev = rocker->pdev;
2536298b0e0cSJakub Kicinski 	u8 addr[ETH_ALEN];
253711ce2ba3SJiri Pirko 	int err;
253811ce2ba3SJiri Pirko 
2539298b0e0cSJakub Kicinski 	err = rocker_cmd_get_port_settings_macaddr(rocker_port, addr);
2540298b0e0cSJakub Kicinski 	if (!err) {
2541298b0e0cSJakub Kicinski 		eth_hw_addr_set(rocker_port->dev, addr);
2542298b0e0cSJakub Kicinski 	} else {
254311ce2ba3SJiri Pirko 		dev_warn(&pdev->dev, "failed to get mac address, using random\n");
254411ce2ba3SJiri Pirko 		eth_hw_addr_random(rocker_port->dev);
254511ce2ba3SJiri Pirko 	}
254611ce2ba3SJiri Pirko }
254711ce2ba3SJiri Pirko 
254844770e11SJarod Wilson #define ROCKER_PORT_MIN_MTU	ETH_MIN_MTU
254944770e11SJarod Wilson #define ROCKER_PORT_MAX_MTU	9000
rocker_probe_port(struct rocker * rocker,unsigned int port_number)255011ce2ba3SJiri Pirko static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
255111ce2ba3SJiri Pirko {
255297699056SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
255311ce2ba3SJiri Pirko 	struct rocker_port *rocker_port;
255411ce2ba3SJiri Pirko 	struct net_device *dev;
255511ce2ba3SJiri Pirko 	int err;
255611ce2ba3SJiri Pirko 
255711ce2ba3SJiri Pirko 	dev = alloc_etherdev(sizeof(struct rocker_port));
255811ce2ba3SJiri Pirko 	if (!dev)
255911ce2ba3SJiri Pirko 		return -ENOMEM;
256097699056SJiri Pirko 	SET_NETDEV_DEV(dev, &pdev->dev);
256111ce2ba3SJiri Pirko 	rocker_port = netdev_priv(dev);
256211ce2ba3SJiri Pirko 	rocker_port->dev = dev;
256311ce2ba3SJiri Pirko 	rocker_port->rocker = rocker;
256411ce2ba3SJiri Pirko 	rocker_port->port_number = port_number;
256511ce2ba3SJiri Pirko 	rocker_port->pport = port_number + 1;
256611ce2ba3SJiri Pirko 
2567e420114eSJiri Pirko 	err = rocker_world_check_init(rocker_port);
2568e420114eSJiri Pirko 	if (err) {
2569e420114eSJiri Pirko 		dev_err(&pdev->dev, "world init failed\n");
2570e420114eSJiri Pirko 		goto err_world_check_init;
2571e420114eSJiri Pirko 	}
2572e420114eSJiri Pirko 
257311ce2ba3SJiri Pirko 	rocker_port_dev_addr_init(rocker_port);
257411ce2ba3SJiri Pirko 	dev->netdev_ops = &rocker_port_netdev_ops;
257511ce2ba3SJiri Pirko 	dev->ethtool_ops = &rocker_port_ethtool_ops;
257616d083e2SJakub Kicinski 	netif_napi_add_tx(dev, &rocker_port->napi_tx, rocker_port_poll_tx);
2577b48b89f9SJakub Kicinski 	netif_napi_add(dev, &rocker_port->napi_rx, rocker_port_poll_rx);
257811ce2ba3SJiri Pirko 	rocker_carrier_init(rocker_port);
257911ce2ba3SJiri Pirko 
258011ce2ba3SJiri Pirko 	dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_SG;
258111ce2ba3SJiri Pirko 
258244770e11SJarod Wilson 	/* MTU range: 68 - 9000 */
258344770e11SJarod Wilson 	dev->min_mtu = ROCKER_PORT_MIN_MTU;
258444770e11SJarod Wilson 	dev->max_mtu = ROCKER_PORT_MAX_MTU;
258544770e11SJarod Wilson 
2586e420114eSJiri Pirko 	err = rocker_world_port_pre_init(rocker_port);
2587e420114eSJiri Pirko 	if (err) {
2588e420114eSJiri Pirko 		dev_err(&pdev->dev, "port world pre-init failed\n");
2589e420114eSJiri Pirko 		goto err_world_port_pre_init;
2590e420114eSJiri Pirko 	}
259111ce2ba3SJiri Pirko 	err = register_netdev(dev);
259211ce2ba3SJiri Pirko 	if (err) {
259311ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "register_netdev failed\n");
259411ce2ba3SJiri Pirko 		goto err_register_netdev;
259511ce2ba3SJiri Pirko 	}
259611ce2ba3SJiri Pirko 	rocker->ports[port_number] = rocker_port;
259711ce2ba3SJiri Pirko 
2598e420114eSJiri Pirko 	err = rocker_world_port_init(rocker_port);
2599e420114eSJiri Pirko 	if (err) {
2600e420114eSJiri Pirko 		dev_err(&pdev->dev, "port world init failed\n");
2601e420114eSJiri Pirko 		goto err_world_port_init;
2602e420114eSJiri Pirko 	}
2603e420114eSJiri Pirko 
260411ce2ba3SJiri Pirko 	return 0;
260511ce2ba3SJiri Pirko 
2606e420114eSJiri Pirko err_world_port_init:
260711ce2ba3SJiri Pirko 	rocker->ports[port_number] = NULL;
260811ce2ba3SJiri Pirko 	unregister_netdev(dev);
260911ce2ba3SJiri Pirko err_register_netdev:
2610e420114eSJiri Pirko 	rocker_world_port_post_fini(rocker_port);
2611e420114eSJiri Pirko err_world_port_pre_init:
2612e420114eSJiri Pirko err_world_check_init:
261311ce2ba3SJiri Pirko 	free_netdev(dev);
261411ce2ba3SJiri Pirko 	return err;
261511ce2ba3SJiri Pirko }
261611ce2ba3SJiri Pirko 
rocker_probe_ports(struct rocker * rocker)261711ce2ba3SJiri Pirko static int rocker_probe_ports(struct rocker *rocker)
261811ce2ba3SJiri Pirko {
261911ce2ba3SJiri Pirko 	int i;
262011ce2ba3SJiri Pirko 	size_t alloc_size;
262111ce2ba3SJiri Pirko 	int err;
262211ce2ba3SJiri Pirko 
262311ce2ba3SJiri Pirko 	alloc_size = sizeof(struct rocker_port *) * rocker->port_count;
262411ce2ba3SJiri Pirko 	rocker->ports = kzalloc(alloc_size, GFP_KERNEL);
262511ce2ba3SJiri Pirko 	if (!rocker->ports)
262611ce2ba3SJiri Pirko 		return -ENOMEM;
262711ce2ba3SJiri Pirko 	for (i = 0; i < rocker->port_count; i++) {
262811ce2ba3SJiri Pirko 		err = rocker_probe_port(rocker, i);
262911ce2ba3SJiri Pirko 		if (err)
263011ce2ba3SJiri Pirko 			goto remove_ports;
263111ce2ba3SJiri Pirko 	}
263211ce2ba3SJiri Pirko 	return 0;
263311ce2ba3SJiri Pirko 
263411ce2ba3SJiri Pirko remove_ports:
263511ce2ba3SJiri Pirko 	rocker_remove_ports(rocker);
263611ce2ba3SJiri Pirko 	return err;
263711ce2ba3SJiri Pirko }
263811ce2ba3SJiri Pirko 
rocker_msix_init(struct rocker * rocker)263911ce2ba3SJiri Pirko static int rocker_msix_init(struct rocker *rocker)
264011ce2ba3SJiri Pirko {
264111ce2ba3SJiri Pirko 	struct pci_dev *pdev = rocker->pdev;
264211ce2ba3SJiri Pirko 	int msix_entries;
264311ce2ba3SJiri Pirko 	int i;
264411ce2ba3SJiri Pirko 	int err;
264511ce2ba3SJiri Pirko 
264611ce2ba3SJiri Pirko 	msix_entries = pci_msix_vec_count(pdev);
264711ce2ba3SJiri Pirko 	if (msix_entries < 0)
264811ce2ba3SJiri Pirko 		return msix_entries;
264911ce2ba3SJiri Pirko 
265011ce2ba3SJiri Pirko 	if (msix_entries != ROCKER_MSIX_VEC_COUNT(rocker->port_count))
265111ce2ba3SJiri Pirko 		return -EINVAL;
265211ce2ba3SJiri Pirko 
265311ce2ba3SJiri Pirko 	rocker->msix_entries = kmalloc_array(msix_entries,
265411ce2ba3SJiri Pirko 					     sizeof(struct msix_entry),
265511ce2ba3SJiri Pirko 					     GFP_KERNEL);
265611ce2ba3SJiri Pirko 	if (!rocker->msix_entries)
265711ce2ba3SJiri Pirko 		return -ENOMEM;
265811ce2ba3SJiri Pirko 
265911ce2ba3SJiri Pirko 	for (i = 0; i < msix_entries; i++)
266011ce2ba3SJiri Pirko 		rocker->msix_entries[i].entry = i;
266111ce2ba3SJiri Pirko 
266211ce2ba3SJiri Pirko 	err = pci_enable_msix_exact(pdev, rocker->msix_entries, msix_entries);
266311ce2ba3SJiri Pirko 	if (err < 0)
266411ce2ba3SJiri Pirko 		goto err_enable_msix;
266511ce2ba3SJiri Pirko 
266611ce2ba3SJiri Pirko 	return 0;
266711ce2ba3SJiri Pirko 
266811ce2ba3SJiri Pirko err_enable_msix:
266911ce2ba3SJiri Pirko 	kfree(rocker->msix_entries);
267011ce2ba3SJiri Pirko 	return err;
267111ce2ba3SJiri Pirko }
267211ce2ba3SJiri Pirko 
rocker_msix_fini(const struct rocker * rocker)267311ce2ba3SJiri Pirko static void rocker_msix_fini(const struct rocker *rocker)
267411ce2ba3SJiri Pirko {
267511ce2ba3SJiri Pirko 	pci_disable_msix(rocker->pdev);
267611ce2ba3SJiri Pirko 	kfree(rocker->msix_entries);
267711ce2ba3SJiri Pirko }
267811ce2ba3SJiri Pirko 
rocker_port_dev_check(const struct net_device * dev)2679726fd42fSArkadi Sharshevsky static bool rocker_port_dev_check(const struct net_device *dev)
2680726fd42fSArkadi Sharshevsky {
2681726fd42fSArkadi Sharshevsky 	return dev->netdev_ops == &rocker_port_netdev_ops;
2682726fd42fSArkadi Sharshevsky }
2683726fd42fSArkadi Sharshevsky 
26844f705486SFlorian Fainelli static int
rocker_switchdev_port_attr_set_event(struct net_device * netdev,struct switchdev_notifier_port_attr_info * port_attr_info)26854f705486SFlorian Fainelli rocker_switchdev_port_attr_set_event(struct net_device *netdev,
26864f705486SFlorian Fainelli 		struct switchdev_notifier_port_attr_info *port_attr_info)
26874f705486SFlorian Fainelli {
26884f705486SFlorian Fainelli 	int err;
26894f705486SFlorian Fainelli 
2690bae33f2bSVladimir Oltean 	err = rocker_port_attr_set(netdev, port_attr_info->attr);
26914f705486SFlorian Fainelli 
26924f705486SFlorian Fainelli 	port_attr_info->handled = true;
26934f705486SFlorian Fainelli 	return notifier_from_errno(err);
26944f705486SFlorian Fainelli }
26954f705486SFlorian Fainelli 
2696726fd42fSArkadi Sharshevsky struct rocker_switchdev_event_work {
2697726fd42fSArkadi Sharshevsky 	struct work_struct work;
2698726fd42fSArkadi Sharshevsky 	struct switchdev_notifier_fdb_info fdb_info;
2699726fd42fSArkadi Sharshevsky 	struct rocker_port *rocker_port;
2700726fd42fSArkadi Sharshevsky 	unsigned long event;
2701726fd42fSArkadi Sharshevsky };
2702726fd42fSArkadi Sharshevsky 
2703726fd42fSArkadi Sharshevsky static void
rocker_fdb_offload_notify(struct rocker_port * rocker_port,struct switchdev_notifier_fdb_info * recv_info)2704726fd42fSArkadi Sharshevsky rocker_fdb_offload_notify(struct rocker_port *rocker_port,
2705726fd42fSArkadi Sharshevsky 			  struct switchdev_notifier_fdb_info *recv_info)
2706726fd42fSArkadi Sharshevsky {
2707c35b57ceSVladimir Oltean 	struct switchdev_notifier_fdb_info info = {};
2708726fd42fSArkadi Sharshevsky 
2709726fd42fSArkadi Sharshevsky 	info.addr = recv_info->addr;
2710726fd42fSArkadi Sharshevsky 	info.vid = recv_info->vid;
2711e9ba0fbcSIdo Schimmel 	info.offloaded = true;
2712726fd42fSArkadi Sharshevsky 	call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
27136685987cSPetr Machata 				 rocker_port->dev, &info.info, NULL);
2714726fd42fSArkadi Sharshevsky }
2715726fd42fSArkadi Sharshevsky 
rocker_switchdev_event_work(struct work_struct * work)2716726fd42fSArkadi Sharshevsky static void rocker_switchdev_event_work(struct work_struct *work)
2717726fd42fSArkadi Sharshevsky {
2718726fd42fSArkadi Sharshevsky 	struct rocker_switchdev_event_work *switchdev_work =
2719726fd42fSArkadi Sharshevsky 		container_of(work, struct rocker_switchdev_event_work, work);
2720726fd42fSArkadi Sharshevsky 	struct rocker_port *rocker_port = switchdev_work->rocker_port;
2721726fd42fSArkadi Sharshevsky 	struct switchdev_notifier_fdb_info *fdb_info;
2722726fd42fSArkadi Sharshevsky 	int err;
2723726fd42fSArkadi Sharshevsky 
2724726fd42fSArkadi Sharshevsky 	rtnl_lock();
2725726fd42fSArkadi Sharshevsky 	switch (switchdev_work->event) {
2726726fd42fSArkadi Sharshevsky 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
2727726fd42fSArkadi Sharshevsky 		fdb_info = &switchdev_work->fdb_info;
27282c4eca3eSVladimir Oltean 		if (!fdb_info->added_by_user || fdb_info->is_local)
2729ec9efb52SPetr Machata 			break;
2730726fd42fSArkadi Sharshevsky 		err = rocker_world_port_fdb_add(rocker_port, fdb_info);
2731726fd42fSArkadi Sharshevsky 		if (err) {
2732726fd42fSArkadi Sharshevsky 			netdev_dbg(rocker_port->dev, "fdb add failed err=%d\n", err);
2733726fd42fSArkadi Sharshevsky 			break;
2734726fd42fSArkadi Sharshevsky 		}
2735726fd42fSArkadi Sharshevsky 		rocker_fdb_offload_notify(rocker_port, fdb_info);
2736726fd42fSArkadi Sharshevsky 		break;
2737726fd42fSArkadi Sharshevsky 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
2738726fd42fSArkadi Sharshevsky 		fdb_info = &switchdev_work->fdb_info;
27392c4eca3eSVladimir Oltean 		if (!fdb_info->added_by_user || fdb_info->is_local)
2740ec9efb52SPetr Machata 			break;
2741726fd42fSArkadi Sharshevsky 		err = rocker_world_port_fdb_del(rocker_port, fdb_info);
2742726fd42fSArkadi Sharshevsky 		if (err)
2743726fd42fSArkadi Sharshevsky 			netdev_dbg(rocker_port->dev, "fdb add failed err=%d\n", err);
2744726fd42fSArkadi Sharshevsky 		break;
2745726fd42fSArkadi Sharshevsky 	}
2746726fd42fSArkadi Sharshevsky 	rtnl_unlock();
2747726fd42fSArkadi Sharshevsky 
2748726fd42fSArkadi Sharshevsky 	kfree(switchdev_work->fdb_info.addr);
2749726fd42fSArkadi Sharshevsky 	kfree(switchdev_work);
2750726fd42fSArkadi Sharshevsky 	dev_put(rocker_port->dev);
2751726fd42fSArkadi Sharshevsky }
2752726fd42fSArkadi Sharshevsky 
2753726fd42fSArkadi Sharshevsky /* called under rcu_read_lock() */
rocker_switchdev_event(struct notifier_block * unused,unsigned long event,void * ptr)2754726fd42fSArkadi Sharshevsky static int rocker_switchdev_event(struct notifier_block *unused,
2755726fd42fSArkadi Sharshevsky 				  unsigned long event, void *ptr)
2756726fd42fSArkadi Sharshevsky {
2757726fd42fSArkadi Sharshevsky 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
2758726fd42fSArkadi Sharshevsky 	struct rocker_switchdev_event_work *switchdev_work;
2759726fd42fSArkadi Sharshevsky 	struct switchdev_notifier_fdb_info *fdb_info = ptr;
2760726fd42fSArkadi Sharshevsky 	struct rocker_port *rocker_port;
2761726fd42fSArkadi Sharshevsky 
2762726fd42fSArkadi Sharshevsky 	if (!rocker_port_dev_check(dev))
2763726fd42fSArkadi Sharshevsky 		return NOTIFY_DONE;
2764726fd42fSArkadi Sharshevsky 
27654f705486SFlorian Fainelli 	if (event == SWITCHDEV_PORT_ATTR_SET)
27664f705486SFlorian Fainelli 		return rocker_switchdev_port_attr_set_event(dev, ptr);
27674f705486SFlorian Fainelli 
2768726fd42fSArkadi Sharshevsky 	rocker_port = netdev_priv(dev);
2769726fd42fSArkadi Sharshevsky 	switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
2770726fd42fSArkadi Sharshevsky 	if (WARN_ON(!switchdev_work))
2771726fd42fSArkadi Sharshevsky 		return NOTIFY_BAD;
2772726fd42fSArkadi Sharshevsky 
2773726fd42fSArkadi Sharshevsky 	INIT_WORK(&switchdev_work->work, rocker_switchdev_event_work);
2774726fd42fSArkadi Sharshevsky 	switchdev_work->rocker_port = rocker_port;
2775726fd42fSArkadi Sharshevsky 	switchdev_work->event = event;
2776726fd42fSArkadi Sharshevsky 
2777726fd42fSArkadi Sharshevsky 	switch (event) {
2778df561f66SGustavo A. R. Silva 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
2779726fd42fSArkadi Sharshevsky 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
2780726fd42fSArkadi Sharshevsky 		memcpy(&switchdev_work->fdb_info, ptr,
2781726fd42fSArkadi Sharshevsky 		       sizeof(switchdev_work->fdb_info));
2782726fd42fSArkadi Sharshevsky 		switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
27835c149314SKangjie Lu 		if (unlikely(!switchdev_work->fdb_info.addr)) {
27845c149314SKangjie Lu 			kfree(switchdev_work);
27855c149314SKangjie Lu 			return NOTIFY_BAD;
27865c149314SKangjie Lu 		}
27875c149314SKangjie Lu 
2788726fd42fSArkadi Sharshevsky 		ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
2789726fd42fSArkadi Sharshevsky 				fdb_info->addr);
2790726fd42fSArkadi Sharshevsky 		/* Take a reference on the rocker device */
2791726fd42fSArkadi Sharshevsky 		dev_hold(dev);
2792726fd42fSArkadi Sharshevsky 		break;
2793726fd42fSArkadi Sharshevsky 	default:
2794726fd42fSArkadi Sharshevsky 		kfree(switchdev_work);
2795726fd42fSArkadi Sharshevsky 		return NOTIFY_DONE;
2796726fd42fSArkadi Sharshevsky 	}
2797726fd42fSArkadi Sharshevsky 
2798726fd42fSArkadi Sharshevsky 	queue_work(rocker_port->rocker->rocker_owq,
2799726fd42fSArkadi Sharshevsky 		   &switchdev_work->work);
2800726fd42fSArkadi Sharshevsky 	return NOTIFY_DONE;
2801726fd42fSArkadi Sharshevsky }
2802726fd42fSArkadi Sharshevsky 
2803c6fa35b2SPetr Machata static int
rocker_switchdev_port_obj_event(unsigned long event,struct net_device * netdev,struct switchdev_notifier_port_obj_info * port_obj_info)2804c6fa35b2SPetr Machata rocker_switchdev_port_obj_event(unsigned long event, struct net_device *netdev,
2805c6fa35b2SPetr Machata 			struct switchdev_notifier_port_obj_info *port_obj_info)
2806c6fa35b2SPetr Machata {
2807c6fa35b2SPetr Machata 	int err = -EOPNOTSUPP;
2808c6fa35b2SPetr Machata 
2809c6fa35b2SPetr Machata 	switch (event) {
2810c6fa35b2SPetr Machata 	case SWITCHDEV_PORT_OBJ_ADD:
2811ffb68fc5SVladimir Oltean 		err = rocker_port_obj_add(netdev, port_obj_info->obj);
2812c6fa35b2SPetr Machata 		break;
2813c6fa35b2SPetr Machata 	case SWITCHDEV_PORT_OBJ_DEL:
2814c6fa35b2SPetr Machata 		err = rocker_port_obj_del(netdev, port_obj_info->obj);
2815c6fa35b2SPetr Machata 		break;
2816c6fa35b2SPetr Machata 	}
2817c6fa35b2SPetr Machata 
2818c6fa35b2SPetr Machata 	port_obj_info->handled = true;
2819c6fa35b2SPetr Machata 	return notifier_from_errno(err);
2820c6fa35b2SPetr Machata }
2821c6fa35b2SPetr Machata 
rocker_switchdev_blocking_event(struct notifier_block * unused,unsigned long event,void * ptr)2822c6fa35b2SPetr Machata static int rocker_switchdev_blocking_event(struct notifier_block *unused,
2823c6fa35b2SPetr Machata 					   unsigned long event, void *ptr)
2824c6fa35b2SPetr Machata {
2825c6fa35b2SPetr Machata 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
2826c6fa35b2SPetr Machata 
2827c6fa35b2SPetr Machata 	if (!rocker_port_dev_check(dev))
2828c6fa35b2SPetr Machata 		return NOTIFY_DONE;
2829c6fa35b2SPetr Machata 
2830c6fa35b2SPetr Machata 	switch (event) {
2831c6fa35b2SPetr Machata 	case SWITCHDEV_PORT_OBJ_ADD:
2832c6fa35b2SPetr Machata 	case SWITCHDEV_PORT_OBJ_DEL:
2833c6fa35b2SPetr Machata 		return rocker_switchdev_port_obj_event(event, dev, ptr);
28344f705486SFlorian Fainelli 	case SWITCHDEV_PORT_ATTR_SET:
28354f705486SFlorian Fainelli 		return rocker_switchdev_port_attr_set_event(dev, ptr);
2836c6fa35b2SPetr Machata 	}
2837c6fa35b2SPetr Machata 
2838c6fa35b2SPetr Machata 	return NOTIFY_DONE;
2839c6fa35b2SPetr Machata }
2840c6fa35b2SPetr Machata 
2841726fd42fSArkadi Sharshevsky static struct notifier_block rocker_switchdev_notifier = {
2842726fd42fSArkadi Sharshevsky 	.notifier_call = rocker_switchdev_event,
2843726fd42fSArkadi Sharshevsky };
2844726fd42fSArkadi Sharshevsky 
2845c6fa35b2SPetr Machata static struct notifier_block rocker_switchdev_blocking_notifier = {
2846c6fa35b2SPetr Machata 	.notifier_call = rocker_switchdev_blocking_event,
2847c6fa35b2SPetr Machata };
2848c6fa35b2SPetr Machata 
rocker_probe(struct pci_dev * pdev,const struct pci_device_id * id)284911ce2ba3SJiri Pirko static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
285011ce2ba3SJiri Pirko {
2851c6fa35b2SPetr Machata 	struct notifier_block *nb;
285211ce2ba3SJiri Pirko 	struct rocker *rocker;
285311ce2ba3SJiri Pirko 	int err;
285411ce2ba3SJiri Pirko 
285511ce2ba3SJiri Pirko 	rocker = kzalloc(sizeof(*rocker), GFP_KERNEL);
285611ce2ba3SJiri Pirko 	if (!rocker)
285711ce2ba3SJiri Pirko 		return -ENOMEM;
285811ce2ba3SJiri Pirko 
285911ce2ba3SJiri Pirko 	err = pci_enable_device(pdev);
286011ce2ba3SJiri Pirko 	if (err) {
286111ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "pci_enable_device failed\n");
286211ce2ba3SJiri Pirko 		goto err_pci_enable_device;
286311ce2ba3SJiri Pirko 	}
286411ce2ba3SJiri Pirko 
286511ce2ba3SJiri Pirko 	err = pci_request_regions(pdev, rocker_driver_name);
286611ce2ba3SJiri Pirko 	if (err) {
286711ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "pci_request_regions failed\n");
286811ce2ba3SJiri Pirko 		goto err_pci_request_regions;
286911ce2ba3SJiri Pirko 	}
287011ce2ba3SJiri Pirko 
28717ac2d77cSChristophe JAILLET 	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
287211ce2ba3SJiri Pirko 	if (err) {
2873c68d0cebSChristophe JAILLET 		dev_err(&pdev->dev, "dma_set_mask failed\n");
287411ce2ba3SJiri Pirko 		goto err_pci_set_dma_mask;
287511ce2ba3SJiri Pirko 	}
287611ce2ba3SJiri Pirko 
287711ce2ba3SJiri Pirko 	if (pci_resource_len(pdev, 0) < ROCKER_PCI_BAR0_SIZE) {
287811ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "invalid PCI region size\n");
287911ce2ba3SJiri Pirko 		err = -EINVAL;
288011ce2ba3SJiri Pirko 		goto err_pci_resource_len_check;
288111ce2ba3SJiri Pirko 	}
288211ce2ba3SJiri Pirko 
288311ce2ba3SJiri Pirko 	rocker->hw_addr = ioremap(pci_resource_start(pdev, 0),
288411ce2ba3SJiri Pirko 				  pci_resource_len(pdev, 0));
288511ce2ba3SJiri Pirko 	if (!rocker->hw_addr) {
288611ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "ioremap failed\n");
288711ce2ba3SJiri Pirko 		err = -EIO;
288811ce2ba3SJiri Pirko 		goto err_ioremap;
288911ce2ba3SJiri Pirko 	}
289011ce2ba3SJiri Pirko 	pci_set_master(pdev);
289111ce2ba3SJiri Pirko 
289211ce2ba3SJiri Pirko 	rocker->pdev = pdev;
289311ce2ba3SJiri Pirko 	pci_set_drvdata(pdev, rocker);
289411ce2ba3SJiri Pirko 
289511ce2ba3SJiri Pirko 	rocker->port_count = rocker_read32(rocker, PORT_PHYS_COUNT);
289611ce2ba3SJiri Pirko 
289711ce2ba3SJiri Pirko 	err = rocker_msix_init(rocker);
289811ce2ba3SJiri Pirko 	if (err) {
289911ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "MSI-X init failed\n");
290011ce2ba3SJiri Pirko 		goto err_msix_init;
290111ce2ba3SJiri Pirko 	}
290211ce2ba3SJiri Pirko 
290311ce2ba3SJiri Pirko 	err = rocker_basic_hw_test(rocker);
290411ce2ba3SJiri Pirko 	if (err) {
290511ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "basic hw test failed\n");
290611ce2ba3SJiri Pirko 		goto err_basic_hw_test;
290711ce2ba3SJiri Pirko 	}
290811ce2ba3SJiri Pirko 
290911ce2ba3SJiri Pirko 	rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET);
291011ce2ba3SJiri Pirko 
291111ce2ba3SJiri Pirko 	err = rocker_dma_rings_init(rocker);
291211ce2ba3SJiri Pirko 	if (err)
291311ce2ba3SJiri Pirko 		goto err_dma_rings_init;
291411ce2ba3SJiri Pirko 
291511ce2ba3SJiri Pirko 	err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD),
291611ce2ba3SJiri Pirko 			  rocker_cmd_irq_handler, 0,
291711ce2ba3SJiri Pirko 			  rocker_driver_name, rocker);
291811ce2ba3SJiri Pirko 	if (err) {
291911ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "cannot assign cmd irq\n");
292011ce2ba3SJiri Pirko 		goto err_request_cmd_irq;
292111ce2ba3SJiri Pirko 	}
292211ce2ba3SJiri Pirko 
292311ce2ba3SJiri Pirko 	err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT),
292411ce2ba3SJiri Pirko 			  rocker_event_irq_handler, 0,
292511ce2ba3SJiri Pirko 			  rocker_driver_name, rocker);
292611ce2ba3SJiri Pirko 	if (err) {
292711ce2ba3SJiri Pirko 		dev_err(&pdev->dev, "cannot assign event irq\n");
292811ce2ba3SJiri Pirko 		goto err_request_event_irq;
292911ce2ba3SJiri Pirko 	}
293011ce2ba3SJiri Pirko 
2931c1bb279cSIdo Schimmel 	rocker->rocker_owq = alloc_ordered_workqueue(rocker_driver_name,
2932c1bb279cSIdo Schimmel 						     WQ_MEM_RECLAIM);
2933c1bb279cSIdo Schimmel 	if (!rocker->rocker_owq) {
2934c1bb279cSIdo Schimmel 		err = -ENOMEM;
2935c1bb279cSIdo Schimmel 		goto err_alloc_ordered_workqueue;
2936c1bb279cSIdo Schimmel 	}
2937c1bb279cSIdo Schimmel 
2938a83165f0SJiri Pirko 	err = rocker_probe_ports(rocker);
2939a83165f0SJiri Pirko 	if (err) {
2940a83165f0SJiri Pirko 		dev_err(&pdev->dev, "failed to probe ports\n");
2941a83165f0SJiri Pirko 		goto err_probe_ports;
2942a83165f0SJiri Pirko 	}
2943a83165f0SJiri Pirko 
2944c3852ef7SIdo Schimmel 	/* Only FIBs pointing to our own netdevs are programmed into
2945c3852ef7SIdo Schimmel 	 * the device, so no need to pass a callback.
2946c3852ef7SIdo Schimmel 	 */
294717f8be7dSIdo Schimmel 	rocker->fib_nb.notifier_call = rocker_router_fib_event;
2948b7a59557SJiri Pirko 	err = register_fib_notifier(&init_net, &rocker->fib_nb, NULL, NULL);
2949c3852ef7SIdo Schimmel 	if (err)
2950c3852ef7SIdo Schimmel 		goto err_register_fib_notifier;
295117f8be7dSIdo Schimmel 
2952726fd42fSArkadi Sharshevsky 	err = register_switchdev_notifier(&rocker_switchdev_notifier);
2953726fd42fSArkadi Sharshevsky 	if (err) {
2954726fd42fSArkadi Sharshevsky 		dev_err(&pdev->dev, "Failed to register switchdev notifier\n");
2955726fd42fSArkadi Sharshevsky 		goto err_register_switchdev_notifier;
2956726fd42fSArkadi Sharshevsky 	}
2957726fd42fSArkadi Sharshevsky 
2958c6fa35b2SPetr Machata 	nb = &rocker_switchdev_blocking_notifier;
2959c6fa35b2SPetr Machata 	err = register_switchdev_blocking_notifier(nb);
2960c6fa35b2SPetr Machata 	if (err) {
2961c6fa35b2SPetr Machata 		dev_err(&pdev->dev, "Failed to register switchdev blocking notifier\n");
2962c6fa35b2SPetr Machata 		goto err_register_switchdev_blocking_notifier;
2963c6fa35b2SPetr Machata 	}
2964c6fa35b2SPetr Machata 
296511ce2ba3SJiri Pirko 	rocker->hw.id = rocker_read64(rocker, SWITCH_ID);
296611ce2ba3SJiri Pirko 
296711ce2ba3SJiri Pirko 	dev_info(&pdev->dev, "Rocker switch with id %*phN\n",
296811ce2ba3SJiri Pirko 		 (int)sizeof(rocker->hw.id), &rocker->hw.id);
296911ce2ba3SJiri Pirko 
297011ce2ba3SJiri Pirko 	return 0;
297111ce2ba3SJiri Pirko 
2972c6fa35b2SPetr Machata err_register_switchdev_blocking_notifier:
2973c6fa35b2SPetr Machata 	unregister_switchdev_notifier(&rocker_switchdev_notifier);
2974726fd42fSArkadi Sharshevsky err_register_switchdev_notifier:
29757c550dafSJiri Pirko 	unregister_fib_notifier(&init_net, &rocker->fib_nb);
2976c3852ef7SIdo Schimmel err_register_fib_notifier:
2977a83165f0SJiri Pirko 	rocker_remove_ports(rocker);
2978a83165f0SJiri Pirko err_probe_ports:
2979c1bb279cSIdo Schimmel 	destroy_workqueue(rocker->rocker_owq);
2980c1bb279cSIdo Schimmel err_alloc_ordered_workqueue:
298111ce2ba3SJiri Pirko 	free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker);
298211ce2ba3SJiri Pirko err_request_event_irq:
298311ce2ba3SJiri Pirko 	free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker);
298411ce2ba3SJiri Pirko err_request_cmd_irq:
298511ce2ba3SJiri Pirko 	rocker_dma_rings_fini(rocker);
298611ce2ba3SJiri Pirko err_dma_rings_init:
298711ce2ba3SJiri Pirko err_basic_hw_test:
298811ce2ba3SJiri Pirko 	rocker_msix_fini(rocker);
298911ce2ba3SJiri Pirko err_msix_init:
299011ce2ba3SJiri Pirko 	iounmap(rocker->hw_addr);
299111ce2ba3SJiri Pirko err_ioremap:
299211ce2ba3SJiri Pirko err_pci_resource_len_check:
299311ce2ba3SJiri Pirko err_pci_set_dma_mask:
299411ce2ba3SJiri Pirko 	pci_release_regions(pdev);
299511ce2ba3SJiri Pirko err_pci_request_regions:
299611ce2ba3SJiri Pirko 	pci_disable_device(pdev);
299711ce2ba3SJiri Pirko err_pci_enable_device:
299811ce2ba3SJiri Pirko 	kfree(rocker);
299911ce2ba3SJiri Pirko 	return err;
300011ce2ba3SJiri Pirko }
300111ce2ba3SJiri Pirko 
rocker_remove(struct pci_dev * pdev)300211ce2ba3SJiri Pirko static void rocker_remove(struct pci_dev *pdev)
300311ce2ba3SJiri Pirko {
300411ce2ba3SJiri Pirko 	struct rocker *rocker = pci_get_drvdata(pdev);
3005c6fa35b2SPetr Machata 	struct notifier_block *nb;
3006c6fa35b2SPetr Machata 
3007c6fa35b2SPetr Machata 	nb = &rocker_switchdev_blocking_notifier;
3008c6fa35b2SPetr Machata 	unregister_switchdev_blocking_notifier(nb);
300911ce2ba3SJiri Pirko 
3010726fd42fSArkadi Sharshevsky 	unregister_switchdev_notifier(&rocker_switchdev_notifier);
30117c550dafSJiri Pirko 	unregister_fib_notifier(&init_net, &rocker->fib_nb);
3012a83165f0SJiri Pirko 	rocker_remove_ports(rocker);
301311ce2ba3SJiri Pirko 	rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET);
3014c1bb279cSIdo Schimmel 	destroy_workqueue(rocker->rocker_owq);
301511ce2ba3SJiri Pirko 	free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker);
301611ce2ba3SJiri Pirko 	free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker);
301711ce2ba3SJiri Pirko 	rocker_dma_rings_fini(rocker);
301811ce2ba3SJiri Pirko 	rocker_msix_fini(rocker);
301911ce2ba3SJiri Pirko 	iounmap(rocker->hw_addr);
302011ce2ba3SJiri Pirko 	pci_release_regions(rocker->pdev);
302111ce2ba3SJiri Pirko 	pci_disable_device(rocker->pdev);
302211ce2ba3SJiri Pirko 	kfree(rocker);
302311ce2ba3SJiri Pirko }
302411ce2ba3SJiri Pirko 
302511ce2ba3SJiri Pirko static struct pci_driver rocker_pci_driver = {
302611ce2ba3SJiri Pirko 	.name		= rocker_driver_name,
302711ce2ba3SJiri Pirko 	.id_table	= rocker_pci_id_table,
302811ce2ba3SJiri Pirko 	.probe		= rocker_probe,
302911ce2ba3SJiri Pirko 	.remove		= rocker_remove,
303011ce2ba3SJiri Pirko };
303111ce2ba3SJiri Pirko 
303211ce2ba3SJiri Pirko /************************************
303311ce2ba3SJiri Pirko  * Net device notifier event handler
303411ce2ba3SJiri Pirko  ************************************/
303511ce2ba3SJiri Pirko 
rocker_port_dev_check_under(const struct net_device * dev,struct rocker * rocker)3036936bd486SJiri Pirko static bool rocker_port_dev_check_under(const struct net_device *dev,
3037936bd486SJiri Pirko 					struct rocker *rocker)
3038936bd486SJiri Pirko {
3039936bd486SJiri Pirko 	struct rocker_port *rocker_port;
3040936bd486SJiri Pirko 
3041936bd486SJiri Pirko 	if (!rocker_port_dev_check(dev))
3042936bd486SJiri Pirko 		return false;
3043936bd486SJiri Pirko 
3044936bd486SJiri Pirko 	rocker_port = netdev_priv(dev);
3045936bd486SJiri Pirko 	if (rocker_port->rocker != rocker)
3046936bd486SJiri Pirko 		return false;
3047936bd486SJiri Pirko 
3048936bd486SJiri Pirko 	return true;
3049936bd486SJiri Pirko }
3050936bd486SJiri Pirko 
3051cf2d6740SDavid Ahern struct rocker_walk_data {
3052cf2d6740SDavid Ahern 	struct rocker *rocker;
3053cf2d6740SDavid Ahern 	struct rocker_port *port;
3054cf2d6740SDavid Ahern };
3055cf2d6740SDavid Ahern 
rocker_lower_dev_walk(struct net_device * lower_dev,struct netdev_nested_priv * priv)3056eff74233STaehee Yoo static int rocker_lower_dev_walk(struct net_device *lower_dev,
3057eff74233STaehee Yoo 				 struct netdev_nested_priv *priv)
3058cf2d6740SDavid Ahern {
3059eff74233STaehee Yoo 	struct rocker_walk_data *data = (struct rocker_walk_data *)priv->data;
3060cf2d6740SDavid Ahern 	int ret = 0;
3061cf2d6740SDavid Ahern 
3062cf2d6740SDavid Ahern 	if (rocker_port_dev_check_under(lower_dev, data->rocker)) {
3063cf2d6740SDavid Ahern 		data->port = netdev_priv(lower_dev);
3064cf2d6740SDavid Ahern 		ret = 1;
3065cf2d6740SDavid Ahern 	}
3066cf2d6740SDavid Ahern 
3067cf2d6740SDavid Ahern 	return ret;
3068cf2d6740SDavid Ahern }
3069cf2d6740SDavid Ahern 
rocker_port_dev_lower_find(struct net_device * dev,struct rocker * rocker)3070936bd486SJiri Pirko struct rocker_port *rocker_port_dev_lower_find(struct net_device *dev,
3071936bd486SJiri Pirko 					       struct rocker *rocker)
3072936bd486SJiri Pirko {
3073eff74233STaehee Yoo 	struct netdev_nested_priv priv;
3074cf2d6740SDavid Ahern 	struct rocker_walk_data data;
3075936bd486SJiri Pirko 
3076936bd486SJiri Pirko 	if (rocker_port_dev_check_under(dev, rocker))
3077936bd486SJiri Pirko 		return netdev_priv(dev);
3078936bd486SJiri Pirko 
3079cf2d6740SDavid Ahern 	data.rocker = rocker;
3080cf2d6740SDavid Ahern 	data.port = NULL;
3081eff74233STaehee Yoo 	priv.data = (void *)&data;
3082eff74233STaehee Yoo 	netdev_walk_all_lower_dev(dev, rocker_lower_dev_walk, &priv);
3083cf2d6740SDavid Ahern 
3084cf2d6740SDavid Ahern 	return data.port;
3085936bd486SJiri Pirko }
3086936bd486SJiri Pirko 
rocker_netdevice_event(struct notifier_block * unused,unsigned long event,void * ptr)308711ce2ba3SJiri Pirko static int rocker_netdevice_event(struct notifier_block *unused,
308811ce2ba3SJiri Pirko 				  unsigned long event, void *ptr)
308911ce2ba3SJiri Pirko {
30902f5dc00fSVladimir Oltean 	struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
309111ce2ba3SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
309211ce2ba3SJiri Pirko 	struct netdev_notifier_changeupper_info *info;
309311ce2ba3SJiri Pirko 	struct rocker_port *rocker_port;
309411ce2ba3SJiri Pirko 	int err;
309511ce2ba3SJiri Pirko 
309611ce2ba3SJiri Pirko 	if (!rocker_port_dev_check(dev))
309711ce2ba3SJiri Pirko 		return NOTIFY_DONE;
309811ce2ba3SJiri Pirko 
309911ce2ba3SJiri Pirko 	switch (event) {
310011ce2ba3SJiri Pirko 	case NETDEV_CHANGEUPPER:
310111ce2ba3SJiri Pirko 		info = ptr;
310211ce2ba3SJiri Pirko 		if (!info->master)
310311ce2ba3SJiri Pirko 			goto out;
310411ce2ba3SJiri Pirko 		rocker_port = netdev_priv(dev);
310511ce2ba3SJiri Pirko 		if (info->linking) {
3106e420114eSJiri Pirko 			err = rocker_world_port_master_linked(rocker_port,
31072f5dc00fSVladimir Oltean 							      info->upper_dev,
31082f5dc00fSVladimir Oltean 							      extack);
3109e420114eSJiri Pirko 			if (err)
3110e420114eSJiri Pirko 				netdev_warn(dev, "failed to reflect master linked (err %d)\n",
3111e420114eSJiri Pirko 					    err);
311211ce2ba3SJiri Pirko 		} else {
3113e420114eSJiri Pirko 			err = rocker_world_port_master_unlinked(rocker_port,
3114e420114eSJiri Pirko 								info->upper_dev);
3115e420114eSJiri Pirko 			if (err)
3116e420114eSJiri Pirko 				netdev_warn(dev, "failed to reflect master unlinked (err %d)\n",
3117e420114eSJiri Pirko 					    err);
311811ce2ba3SJiri Pirko 		}
311911ce2ba3SJiri Pirko 	}
312011ce2ba3SJiri Pirko out:
312111ce2ba3SJiri Pirko 	return NOTIFY_DONE;
312211ce2ba3SJiri Pirko }
312311ce2ba3SJiri Pirko 
312411ce2ba3SJiri Pirko static struct notifier_block rocker_netdevice_nb __read_mostly = {
312511ce2ba3SJiri Pirko 	.notifier_call = rocker_netdevice_event,
312611ce2ba3SJiri Pirko };
312711ce2ba3SJiri Pirko 
312811ce2ba3SJiri Pirko /************************************
312911ce2ba3SJiri Pirko  * Net event notifier event handler
313011ce2ba3SJiri Pirko  ************************************/
313111ce2ba3SJiri Pirko 
rocker_netevent_event(struct notifier_block * unused,unsigned long event,void * ptr)313211ce2ba3SJiri Pirko static int rocker_netevent_event(struct notifier_block *unused,
313311ce2ba3SJiri Pirko 				 unsigned long event, void *ptr)
313411ce2ba3SJiri Pirko {
3135e420114eSJiri Pirko 	struct rocker_port *rocker_port;
313611ce2ba3SJiri Pirko 	struct net_device *dev;
313711ce2ba3SJiri Pirko 	struct neighbour *n = ptr;
313811ce2ba3SJiri Pirko 	int err;
313911ce2ba3SJiri Pirko 
314011ce2ba3SJiri Pirko 	switch (event) {
314111ce2ba3SJiri Pirko 	case NETEVENT_NEIGH_UPDATE:
314211ce2ba3SJiri Pirko 		if (n->tbl != &arp_tbl)
314311ce2ba3SJiri Pirko 			return NOTIFY_DONE;
314411ce2ba3SJiri Pirko 		dev = n->dev;
314511ce2ba3SJiri Pirko 		if (!rocker_port_dev_check(dev))
314611ce2ba3SJiri Pirko 			return NOTIFY_DONE;
3147e420114eSJiri Pirko 		rocker_port = netdev_priv(dev);
3148e420114eSJiri Pirko 		err = rocker_world_port_neigh_update(rocker_port, n);
3149e420114eSJiri Pirko 		if (err)
3150e420114eSJiri Pirko 			netdev_warn(dev, "failed to handle neigh update (err %d)\n",
3151e420114eSJiri Pirko 				    err);
315211ce2ba3SJiri Pirko 		break;
315311ce2ba3SJiri Pirko 	}
315411ce2ba3SJiri Pirko 
315511ce2ba3SJiri Pirko 	return NOTIFY_DONE;
315611ce2ba3SJiri Pirko }
315711ce2ba3SJiri Pirko 
315811ce2ba3SJiri Pirko static struct notifier_block rocker_netevent_nb __read_mostly = {
315911ce2ba3SJiri Pirko 	.notifier_call = rocker_netevent_event,
316011ce2ba3SJiri Pirko };
316111ce2ba3SJiri Pirko 
316211ce2ba3SJiri Pirko /***********************
316311ce2ba3SJiri Pirko  * Module init and exit
316411ce2ba3SJiri Pirko  ***********************/
316511ce2ba3SJiri Pirko 
rocker_module_init(void)316611ce2ba3SJiri Pirko static int __init rocker_module_init(void)
316711ce2ba3SJiri Pirko {
316811ce2ba3SJiri Pirko 	int err;
316911ce2ba3SJiri Pirko 
317011ce2ba3SJiri Pirko 	register_netdevice_notifier(&rocker_netdevice_nb);
317111ce2ba3SJiri Pirko 	register_netevent_notifier(&rocker_netevent_nb);
317211ce2ba3SJiri Pirko 	err = pci_register_driver(&rocker_pci_driver);
317311ce2ba3SJiri Pirko 	if (err)
317411ce2ba3SJiri Pirko 		goto err_pci_register_driver;
317511ce2ba3SJiri Pirko 	return 0;
317611ce2ba3SJiri Pirko 
317711ce2ba3SJiri Pirko err_pci_register_driver:
317811ce2ba3SJiri Pirko 	unregister_netevent_notifier(&rocker_netevent_nb);
317911ce2ba3SJiri Pirko 	unregister_netdevice_notifier(&rocker_netdevice_nb);
318011ce2ba3SJiri Pirko 	return err;
318111ce2ba3SJiri Pirko }
318211ce2ba3SJiri Pirko 
rocker_module_exit(void)318311ce2ba3SJiri Pirko static void __exit rocker_module_exit(void)
318411ce2ba3SJiri Pirko {
318511ce2ba3SJiri Pirko 	unregister_netevent_notifier(&rocker_netevent_nb);
318611ce2ba3SJiri Pirko 	unregister_netdevice_notifier(&rocker_netdevice_nb);
318711ce2ba3SJiri Pirko 	pci_unregister_driver(&rocker_pci_driver);
318811ce2ba3SJiri Pirko }
318911ce2ba3SJiri Pirko 
319011ce2ba3SJiri Pirko module_init(rocker_module_init);
319111ce2ba3SJiri Pirko module_exit(rocker_module_exit);
319211ce2ba3SJiri Pirko 
319311ce2ba3SJiri Pirko MODULE_LICENSE("GPL v2");
319411ce2ba3SJiri Pirko MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>");
319511ce2ba3SJiri Pirko MODULE_AUTHOR("Scott Feldman <sfeldma@gmail.com>");
319611ce2ba3SJiri Pirko MODULE_DESCRIPTION("Rocker switch device driver");
319711ce2ba3SJiri Pirko MODULE_DEVICE_TABLE(pci, rocker_pci_id_table);
3198