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(©_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 ©_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