[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1528149617-8964-4-git-send-email-steven.hill@cavium.com>
Date: Mon, 4 Jun 2018 17:00:10 -0500
From: "Steven J. Hill" <steven.hill@...ium.com>
To: netdev@...r.kernel.org
Cc: Carlos Munoz <cmunoz@...ium.com>,
"Steven J. Hill" <Steven.Hill@...ium.com>
Subject: [PATCH net-next v11 03/10] netdev: cavium: octeon: Add Octeon III BGX Ethernet Nexus
From: Carlos Munoz <cmunoz@...ium.com>
Add the BGX nexus architeture for Octeon III BGX Ethernet.
Signed-off-by: Carlos Munoz <cmunoz@...ium.com>
Signed-off-by: Steven J. Hill <Steven.Hill@...ium.com>
---
.../net/ethernet/cavium/octeon/octeon3-bgx-nexus.c | 673 +++++++++++++++++++++
1 file changed, 673 insertions(+)
create mode 100644 drivers/net/ethernet/cavium/octeon/octeon3-bgx-nexus.c
diff --git a/drivers/net/ethernet/cavium/octeon/octeon3-bgx-nexus.c b/drivers/net/ethernet/cavium/octeon/octeon3-bgx-nexus.c
new file mode 100644
index 0000000..f9c45d7
--- /dev/null
+++ b/drivers/net/ethernet/cavium/octeon/octeon3-bgx-nexus.c
@@ -0,0 +1,673 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Octeon III BGX Nexus Ethernet driver
+ *
+ * Copyright (C) 2018 Cavium, Inc.
+ */
+
+#include <linux/ctype.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+#include "octeon3.h"
+
+static atomic_t request_mgmt_once;
+static atomic_t load_driver_once;
+static atomic_t pki_id;
+
+static char *mix_port;
+module_param(mix_port, charp, 0444);
+MODULE_PARM_DESC(mix_port, "Specifies which ports connect to MIX interfaces.");
+
+static char *pki_port;
+module_param(pki_port, charp, 0444);
+MODULE_PARM_DESC(pki_port, "Specifies which ports connect to the PKI.");
+
+#define MAX_MIX_PER_NODE 2
+#define MAX_MIX (MAX_NODES * MAX_MIX_PER_NODE)
+
+/* struct mix_port_lmac - Describes a lmac that connects to a mix port. The lmac
+ * must be on the same node as the mix.
+ * @node: Node of the lmac.
+ * @bgx: Bgx of the lmac.
+ * @lmac: Lmac index.
+ */
+struct mix_port_lmac {
+ int node;
+ int bgx;
+ int lmac;
+};
+
+/* mix_ports_lmacs contains all the lmacs connected to mix ports */
+static struct mix_port_lmac mix_port_lmacs[MAX_MIX];
+
+/* pki_ports keeps track of the lmacs connected to the pki */
+static bool pki_ports[MAX_NODES][MAX_BGX_PER_NODE][MAX_LMAC_PER_BGX];
+
+/* Created platform devices get added to this list */
+static struct list_head pdev_list;
+static struct mutex pdev_list_lock;
+
+/* Created platform device use this structure to add themselves to the list */
+struct pdev_list_item {
+ struct list_head list;
+ struct platform_device *pdev;
+};
+
+/* is_lmac_to_mix - Search the list of lmacs connected to mix'es for a match.
+ * @node: Numa node of lmac to search for.
+ * @bgx: Bgx of lmac to search for.
+ * @lmac: Lmac index to search for.
+ *
+ * Returns true if the lmac is connected to a mix.
+ * Returns false if the lmac is not connected to a mix.
+ */
+static bool is_lmac_to_mix(int node, int bgx, int lmac)
+{
+ int i;
+
+ for (i = 0; i < MAX_MIX; i++) {
+ if (mix_port_lmacs[i].node == node &&
+ mix_port_lmacs[i].bgx == bgx &&
+ mix_port_lmacs[i].lmac == lmac)
+ return true;
+ }
+
+ return false;
+}
+
+/* is_lmac_to_pki - Search the list of lmacs connected to the pki for a match.
+ * @node: Numa node of lmac to search for.
+ * @bgx: Bgx of lmac to search for.
+ * @lmac: Lmac index to search for.
+ *
+ * Returns true if the lmac is connected to the pki.
+ * Returns false if the lmac is not connected to the pki.
+ */
+static bool is_lmac_to_pki(int node, int bgx, int lmac)
+{
+ return pki_ports[node][bgx][lmac];
+}
+
+/* is_lmac_to_xcv - Check if this lmac is connected to the xcv block (rgmii).
+ * @of_node: Device node to check.
+ *
+ * Returns true if the lmac is connected to the xcv port.
+ * Returns false if the lmac is not connected to the xcv port.
+ */
+static bool is_lmac_to_xcv(struct device_node *of_node)
+{
+ return of_device_is_compatible(of_node, "cavium,octeon-7360-xcv");
+}
+
+static int bgx_probe(struct platform_device *pdev)
+{
+ struct platform_device *new_dev, *pki_dev;
+ struct mac_platform_data platform_data;
+ int i, interface, numa_node, r = 0;
+ struct device_node *child;
+ const __be32 *reg;
+ u64 addr, data;
+ char id[64];
+ u32 port;
+
+ reg = of_get_property(pdev->dev.of_node, "reg", NULL);
+ addr = of_translate_address(pdev->dev.of_node, reg);
+ interface = (addr >> 24) & 0xf;
+ numa_node = (addr >> 36) & 0x7;
+
+ /* Assign 8 CAM entries per LMAC */
+ for (i = 0; i < 32; i++) {
+ data = i >> 3;
+ oct_csr_write(data,
+ BGX_CMR_RX_ADRX_CAM(numa_node, interface, i));
+ }
+
+ for_each_available_child_of_node(pdev->dev.of_node, child) {
+ struct pdev_list_item *pdev_item;
+ bool is_mix = false;
+ bool is_pki = false;
+ bool is_xcv = false;
+
+ if (!of_device_is_compatible(child, "cavium,octeon-7890-bgx-port") &&
+ !of_device_is_compatible(child, "cavium,octeon-7360-xcv"))
+ continue;
+ r = of_property_read_u32(child, "reg", &port);
+ if (r)
+ return -ENODEV;
+
+ is_mix = is_lmac_to_mix(numa_node, interface, port);
+ is_pki = is_lmac_to_pki(numa_node, interface, port);
+ is_xcv = is_lmac_to_xcv(child);
+
+ /* Check if this port should be configured */
+ if (!is_mix && !is_pki)
+ continue;
+
+ /* Connect to PKI/PKO */
+ data = oct_csr_read(BGX_CMR_CONFIG(numa_node, interface, port));
+ if (is_mix)
+ data |= BIT(11);
+ else
+ data &= ~BIT(11);
+ oct_csr_write(data, BGX_CMR_CONFIG(numa_node, interface, port));
+
+ /* Unreset the mix bgx interface or it will interfare with the
+ * other ports.
+ */
+ if (is_mix) {
+ data = oct_csr_read(BGX_CMR_GLOBAL_CONFIG(numa_node, interface));
+ if (!port)
+ data &= ~BIT(3);
+ else if (port == 1)
+ data &= ~BIT(4);
+ oct_csr_write(data, BGX_CMR_GLOBAL_CONFIG(numa_node, interface));
+ }
+
+ snprintf(id, sizeof(id), "%llx.%u.ethernet-mac",
+ (unsigned long long)addr, port);
+ new_dev = of_platform_device_create(child, id, &pdev->dev);
+ if (!new_dev) {
+ dev_err(&pdev->dev, "Error creating %s\n", id);
+ continue;
+ }
+ platform_data.mac_type = BGX_MAC;
+ platform_data.numa_node = numa_node;
+ platform_data.interface = interface;
+ platform_data.port = port;
+ if (is_xcv)
+ platform_data.src_type = XCV;
+ else
+ platform_data.src_type = QLM;
+
+ /* Add device to the list of created devices so we can remove it
+ * on exit.
+ */
+ pdev_item = kmalloc(sizeof(*pdev_item), GFP_KERNEL);
+ pdev_item->pdev = new_dev;
+ mutex_lock(&pdev_list_lock);
+ list_add(&pdev_item->list, &pdev_list);
+ mutex_unlock(&pdev_list_lock);
+
+ atomic_inc(&pki_id);
+ pki_dev = platform_device_register_data(&new_dev->dev,
+ is_mix ? "octeon_mgmt" : "ethernet-mac-pki",
+ (int)atomic_read(&pki_id),
+ &platform_data, sizeof(platform_data));
+ dev_info(&pdev->dev, "Created %s %u: %p\n",
+ is_mix ? "MIX" : "PKI", pki_dev->id, pki_dev);
+
+ /* Add device to the list of created devices so we can remove it
+ * on exit.
+ */
+ pdev_item = kmalloc(sizeof(*pdev_item), GFP_KERNEL);
+ pdev_item->pdev = pki_dev;
+ mutex_lock(&pdev_list_lock);
+ list_add(&pdev_item->list, &pdev_list);
+ mutex_unlock(&pdev_list_lock);
+
+#ifdef CONFIG_NUMA
+ new_dev->dev.numa_node = pdev->dev.numa_node;
+ pki_dev->dev.numa_node = pdev->dev.numa_node;
+#endif
+ /* One time request driver module */
+ if (is_mix) {
+ if (atomic_cmpxchg(&request_mgmt_once, 0, 1) == 0)
+ request_module_nowait("octeon_mgmt");
+ }
+ if (is_pki) {
+ if (atomic_cmpxchg(&load_driver_once, 0, 1) == 0)
+ request_module_nowait("octeon3-ethernet");
+ }
+ }
+
+ dev_info(&pdev->dev, "Probed\n");
+ return 0;
+}
+
+/* bgx_mix_init_from_fdt - Initialize the list of lmacs that connect to mix
+ * ports from information in the device tree.
+ *
+ * Returns 0 if successful.
+ * Returns <0 for error codes.
+ */
+static int bgx_mix_init_from_fdt(void)
+{
+ struct device_node *node, *parent = NULL;
+ int mix = 0;
+
+ for_each_compatible_node(node, NULL, "cavium,octeon-7890-mix") {
+ struct device_node *lmac_fdt_node;
+ const __be32 *reg;
+ u64 addr;
+
+ /* Get the fdt node of the lmac connected to this mix */
+ lmac_fdt_node = of_parse_phandle(node, "cavium,mac-handle", 0);
+ if (!lmac_fdt_node)
+ goto err;
+
+ /* Get the numa node and bgx of the lmac */
+ parent = of_get_parent(lmac_fdt_node);
+ if (!parent)
+ goto err;
+ reg = of_get_property(parent, "reg", NULL);
+ if (!reg)
+ goto err;
+ addr = of_translate_address(parent, reg);
+ of_node_put(parent);
+ parent = NULL;
+
+ mix_port_lmacs[mix].node = (addr >> 36) & 0x7;
+ mix_port_lmacs[mix].bgx = (addr >> 24) & 0xf;
+
+ /* Get the lmac index */
+ reg = of_get_property(lmac_fdt_node, "reg", NULL);
+ if (!reg)
+ goto err;
+
+ mix_port_lmacs[mix].lmac = *reg;
+
+ mix++;
+ if (mix >= MAX_MIX)
+ break;
+ }
+
+ return 0;
+err:
+ pr_warn("Invalid device tree mix port information\n");
+ for (mix = 0; mix < MAX_MIX; mix++) {
+ mix_port_lmacs[mix].node = -1;
+ mix_port_lmacs[mix].bgx = -1;
+ mix_port_lmacs[mix].lmac = -1;
+ }
+ if (parent)
+ of_node_put(parent);
+
+ return -EINVAL;
+}
+
+/* bgx_mix_init_from_param - Initialize the list of lmacs that connect to mix
+ * ports from information in the "mix_port" parameter.
+ * The mix_port parameter format is as follows:
+ * mix_port=nbl
+ * where:
+ * n = node
+ * b = bgx
+ * l = lmac
+ * There can be up to 4 lmacs defined separated by
+ * commas. For example to select node0, bgx0, lmac0
+ * and node0, bgx4, lamc0, the mix_port parameter
+ * would be: mix_port=000,040
+ *
+ * Returns 0 if successful.
+ * Returns <0 for error codes.
+ */
+static int bgx_mix_init_from_param(void)
+{
+ char *p = mix_port;
+ int i, mix = 0;
+
+ while (*p) {
+ int node = -1;
+ int bgx = -1;
+ int lmac = -1;
+
+ if (strlen(p) < 3)
+ goto err;
+
+ /* Get the numa node */
+ if (!isdigit(*p))
+ goto err;
+ node = *p - '0';
+ if (node >= MAX_NODES)
+ goto err;
+
+ /* Get the bgx */
+ p++;
+ if (!isdigit(*p))
+ goto err;
+ bgx = *p - '0';
+ if (bgx >= MAX_BGX_PER_NODE)
+ goto err;
+
+ /* Get the lmac index */
+ p++;
+ if (!isdigit(*p))
+ goto err;
+ lmac = *p - '0';
+ if (lmac >= 2)
+ goto err;
+
+ /* Only one lmac0 and one lmac1 per node is supported */
+ for (i = 0; i < MAX_MIX; i++) {
+ if (mix_port_lmacs[i].node == node &&
+ mix_port_lmacs[i].lmac == lmac)
+ goto err;
+ }
+
+ mix_port_lmacs[mix].node = node;
+ mix_port_lmacs[mix].bgx = bgx;
+ mix_port_lmacs[mix].lmac = lmac;
+
+ p++;
+ if (*p == ',')
+ p++;
+
+ mix++;
+ if (mix >= MAX_MIX)
+ break;
+ }
+
+ return 0;
+err:
+ pr_warn("Invalid parameter mix_port=%s\n", mix_port);
+ for (mix = 0; mix < MAX_MIX; mix++) {
+ mix_port_lmacs[mix].node = -1;
+ mix_port_lmacs[mix].bgx = -1;
+ mix_port_lmacs[mix].lmac = -1;
+ }
+ return -EINVAL;
+}
+
+/* bgx_mix_port_lmacs_init - Initialize the mix_port_lmacs variable with the
+ * lmacs that connect to mic ports.
+ *
+ * Returns 0 if successful.
+ * Returns <0 for error codes.
+ */
+static int bgx_mix_port_lmacs_init(void)
+{
+ int mix;
+
+ /* Start with no mix ports configured */
+ for (mix = 0; mix < MAX_MIX; mix++) {
+ mix_port_lmacs[mix].node = -1;
+ mix_port_lmacs[mix].bgx = -1;
+ mix_port_lmacs[mix].lmac = -1;
+ }
+
+ /* Check if no mix port should be configured */
+ if (mix_port && !strcmp(mix_port, "none"))
+ return 0;
+
+ /* Configure the mix ports using information from the device tree if no
+ * parameter was passed. Otherwise, use the information in the module
+ * parameter.
+ */
+ if (!mix_port)
+ bgx_mix_init_from_fdt();
+ else
+ bgx_mix_init_from_param();
+
+ return 0;
+}
+
+/* bgx_parse_pki_elem - Parse a single element (node, bgx, or lmac) out a pki
+ * lmac string and set its bitmap accordingly.
+ * @str: Pki lmac string to parse.
+ * @bitmap: Updated with the bits selected by str.
+ * @size: Maximum size of the bitmap.
+ *
+ * Returns number of characters processed from str.
+ * Returns <0 for error codes.
+ */
+static int bgx_parse_pki_elem(const char *str, unsigned long *bitmap, int size)
+{
+ const char *p = str;
+ int bit, len = -1;
+
+ if (*p == 0) {
+ /* If identifier is missing, the whole subset is allowed */
+ bitmap_set(bitmap, 0, size);
+ len = 0;
+ } else if (*p == '*') {
+ /* If identifier is an asterisk, the whole subset is allowed */
+ bitmap_set(bitmap, 0, size);
+ len = 1;
+ } else if (isdigit(*p)) {
+ /* If identifier is a digit, only the bit corresponding to the
+ * digit is set.
+ */
+ bit = *p - '0';
+ if (bit < size) {
+ bitmap_set(bitmap, bit, 1);
+ len = 1;
+ }
+ } else if (*p == '[') {
+ /* If identifier is a bracket, all the bits corresponding to
+ * the digits inside the bracket are set.
+ */
+ p++;
+ len = 1;
+ do {
+ if (isdigit(*p)) {
+ bit = *p - '0';
+ if (bit < size)
+ bitmap_set(bitmap, bit, 1);
+ else
+ return -1;
+ } else {
+ return -1;
+ }
+ p++;
+ len++;
+ } while (*p != ']');
+ len++;
+ } else {
+ len = -1;
+ }
+
+ return len;
+}
+
+/* bgx_pki_bitmap_set - Set the bitmap bits for all elements (node, bgx, and
+ * lmac) selected by a pki lmac string.
+ * @str: Pki lmac string to process.
+ * @node: Updated with the nodes specified in the pki lmac string.
+ * @bgx: Updated with the bgx's specified in the pki lmac string.
+ * @lmac: Updated with the lmacs specified in the pki lmac string.
+ *
+ * Returns 0 if successful.
+ * Returns <0 for error codes.
+ */
+static unsigned long bgx_pki_bitmap_set(const char *str, unsigned long *node,
+ unsigned long *bgx, unsigned long *lmac)
+{
+ const char *p = str;
+ int len;
+
+ /* Parse the node */
+ len = bgx_parse_pki_elem(p, node, MAX_NODES);
+ if (len < 0)
+ goto err;
+
+ /* Parse the bgx */
+ p += len;
+ len = bgx_parse_pki_elem(p, bgx, MAX_BGX_PER_NODE);
+ if (len < 0)
+ goto err;
+
+ /* Parse the lmac */
+ p += len;
+ len = bgx_parse_pki_elem(p, lmac, MAX_LMAC_PER_BGX);
+ if (len < 0)
+ goto err;
+
+ return 0;
+err:
+ bitmap_zero(node, MAX_NODES);
+ bitmap_zero(bgx, MAX_BGX_PER_NODE);
+ bitmap_zero(lmac, MAX_LMAC_PER_BGX);
+ return len;
+}
+
+/* bgx_pki_init_from_param - Initialize the list of lmacs that connect to the
+ * pki from information in the "pki_port" parameter.
+ *
+ * The pki_port parameter format is as follows:
+ * pki_port=nbl
+ * where:
+ * n = node
+ * b = bgx
+ * l = lmac
+ *
+ * Commas must be used to separate multiple lmacs:
+ * pki_port=000,100,110
+ *
+ * Asterisks (*) specify all possible characters in
+ * the subset:
+ * pki_port=00* (all lmacs of node0 bgx0).
+ *
+ * Missing lmacs identifiers default to all
+ * possible characters in the subset:
+ * pki_port=00 (all lmacs on node0 bgx0)
+ *
+ * Brackets ('[' and ']') specify the valid
+ * characters in the subset:
+ * pki_port=00[01] (lmac0 and lmac1 of node0 bgx0).
+ *
+ * Returns 0 if successful.
+ * Returns <0 for error codes.
+ */
+static int bgx_pki_init_from_param(void)
+{
+ DECLARE_BITMAP(lmac_bitmap, MAX_LMAC_PER_BGX);
+ DECLARE_BITMAP(bgx_bitmap, MAX_BGX_PER_NODE);
+ DECLARE_BITMAP(node_bitmap, MAX_NODES);
+ char *cur, *next;
+
+ /* Parse each comma separated lmac specifier */
+ cur = pki_port;
+ while (cur) {
+ unsigned long bgx, lmac, node;
+
+ bitmap_zero(node_bitmap, BITS_PER_LONG);
+ bitmap_zero(bgx_bitmap, BITS_PER_LONG);
+ bitmap_zero(lmac_bitmap, BITS_PER_LONG);
+
+ next = strchr(cur, ',');
+ if (next)
+ *next++ = '\0';
+
+ /* Convert the specifier into a bitmap */
+ bgx_pki_bitmap_set(cur, node_bitmap, bgx_bitmap, lmac_bitmap);
+
+ /* Mark the lmacs to be connected to the pki */
+ for_each_set_bit(node, node_bitmap, MAX_NODES) {
+ for_each_set_bit(bgx, bgx_bitmap, MAX_BGX_PER_NODE) {
+ for_each_set_bit(lmac, lmac_bitmap,
+ MAX_LMAC_PER_BGX)
+ pki_ports[node][bgx][lmac] = true;
+ }
+ }
+
+ cur = next;
+ }
+
+ return 0;
+}
+
+/* bgx_pki_ports_init - Initialize the pki_ports variable with the lmacs that
+ * connect to the pki.
+ *
+ * Returns 0 if successful.
+ * Returns <0 for error codes.
+ */
+static int bgx_pki_ports_init(void)
+{
+ bool def_val;
+ int i, j, k;
+
+ /* Whether all ports default to connect to the pki or not depend on the
+ * passed module parameter (if any).
+ */
+ if (pki_port)
+ def_val = false;
+ else
+ def_val = true;
+
+ for (i = 0; i < MAX_NODES; i++) {
+ for (j = 0; j < MAX_BGX_PER_NODE; j++) {
+ for (k = 0; k < MAX_LMAC_PER_BGX; k++)
+ pki_ports[i][j][k] = def_val;
+ }
+ }
+
+ /* Check if ports have to be individually configured */
+ if (pki_port && strcmp(pki_port, "none"))
+ bgx_pki_init_from_param();
+
+ return 0;
+}
+
+static int bgx_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static void bgx_shutdown(struct platform_device *pdev)
+{
+}
+
+static const struct of_device_id bgx_match[] = {
+ {
+ .compatible = "cavium,octeon-7890-bgx",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bgx_match);
+
+static struct platform_driver bgx_driver = {
+ .probe = bgx_probe,
+ .remove = bgx_remove,
+ .shutdown = bgx_shutdown,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .of_match_table = bgx_match,
+ },
+};
+
+/* Allow bgx_port driver to force this driver to load */
+void bgx_nexus_load(void)
+{
+}
+EXPORT_SYMBOL(bgx_nexus_load);
+
+static int __init bgx_driver_init(void)
+{
+ int r;
+
+ INIT_LIST_HEAD(&pdev_list);
+ mutex_init(&pdev_list_lock);
+
+ bgx_mix_port_lmacs_init();
+ bgx_pki_ports_init();
+
+ r = platform_driver_register(&bgx_driver);
+
+ return r;
+}
+
+static void __exit bgx_driver_exit(void)
+{
+ struct pdev_list_item *pdev_item;
+
+ mutex_lock(&pdev_list_lock);
+ while (!list_empty(&pdev_list)) {
+ pdev_item = list_first_entry(&pdev_list,
+ struct pdev_list_item, list);
+ list_del(&pdev_item->list);
+ platform_device_unregister(pdev_item->pdev);
+ kfree(pdev_item);
+ }
+ mutex_unlock(&pdev_list_lock);
+
+ platform_driver_unregister(&bgx_driver);
+}
+
+module_init(bgx_driver_init);
+module_exit(bgx_driver_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cavium, Inc. <support@...iumnetworks.com>");
+MODULE_DESCRIPTION("Cavium, Inc. BGX MAC Nexus driver.");
--
2.1.4
Powered by blists - more mailing lists