From: Carlos Munoz <cmu...@cavium.com>

Add the BGX nexus architeture for Octeon III BGX Ethernet.

Signed-off-by: Carlos Munoz <cmu...@cavium.com>
Signed-off-by: Steven J. Hill <steven.h...@cavium.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. <supp...@caviumnetworks.com>");
+MODULE_DESCRIPTION("Cavium, Inc. BGX MAC Nexus driver.");
-- 
2.1.4

Reply via email to