Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/811170/?format=api
{ "id": 811170, "url": "http://patchwork.ozlabs.org/api/patches/811170/?format=api", "web_url": "http://patchwork.ozlabs.org/project/netdev/patch/93AF473E2DA327428DE3D46B72B1E9FD41121A06@CHN-SV-EXMX02.mchp-main.com/", "project": { "id": 7, "url": "http://patchwork.ozlabs.org/api/projects/7/?format=api", "name": "Linux network development", "link_name": "netdev", "list_id": "netdev.vger.kernel.org", "list_email": "netdev@vger.kernel.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<93AF473E2DA327428DE3D46B72B1E9FD41121A06@CHN-SV-EXMX02.mchp-main.com>", "list_archive_url": null, "date": "2017-09-07T21:09:04", "name": "[RFC,2/6] Create new file ksz9477.c from KSZ9477 code in ksz_common.c", "commit_ref": null, "pull_url": null, "state": "rfc", "archived": true, "hash": "2a487c0f0d700b4f7ca81478233343b5bb8173f8", "submitter": { "id": 72262, "url": "http://patchwork.ozlabs.org/api/people/72262/?format=api", "name": "", "email": "Tristram.Ha@microchip.com" }, "delegate": { "id": 34, "url": "http://patchwork.ozlabs.org/api/users/34/?format=api", "username": "davem", "first_name": "David", "last_name": "Miller", "email": "davem@davemloft.net" }, "mbox": "http://patchwork.ozlabs.org/project/netdev/patch/93AF473E2DA327428DE3D46B72B1E9FD41121A06@CHN-SV-EXMX02.mchp-main.com/mbox/", "series": [ { "id": 2062, "url": "http://patchwork.ozlabs.org/api/series/2062/?format=api", "web_url": "http://patchwork.ozlabs.org/project/netdev/list/?series=2062", "date": "2017-09-07T21:09:04", "name": "[RFC,1/6] The file ksz_common.c will be used by other KSZ switch drivers.", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/2062/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/811170/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/811170/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<netdev-owner@vger.kernel.org>", "X-Original-To": "patchwork-incoming@ozlabs.org", "Delivered-To": "patchwork-incoming@ozlabs.org", "Authentication-Results": "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)", "Received": [ "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xpCkj1Yzrz9t2M\n\tfor <patchwork-incoming@ozlabs.org>;\n\tFri, 8 Sep 2017 07:09:33 +1000 (AEST)", "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S932351AbdIGVJU convert rfc822-to-8bit (ORCPT\n\t<rfc822;patchwork-incoming@ozlabs.org>);\n\tThu, 7 Sep 2017 17:09:20 -0400", "from esa2.microchip.iphmx.com ([68.232.149.84]:57372 \"EHLO\n\tesa2.microchip.iphmx.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1755895AbdIGVJR (ORCPT\n\t<rfc822;netdev@vger.kernel.org>); Thu, 7 Sep 2017 17:09:17 -0400", "from smtpout.microchip.com (HELO email.microchip.com)\n\t([198.175.253.82])\n\tby esa2.microchip.iphmx.com with ESMTP/TLS/DHE-RSA-AES256-SHA;\n\t07 Sep 2017 14:09:04 -0700", "from CHN-SV-EXMX02.mchp-main.com ([fe80::7dfe:3761:863e:3963]) by\n\tCHN-SV-EXCH05.mchp-main.com ([fe80::c1bf:7679:c1f8:4560%15]) with\n\tmapi id 14.03.0352.000; Thu, 7 Sep 2017 14:09:04 -0700" ], "X-IronPort-AV": "E=Sophos;i=\"5.42,360,1500966000\"; d=\"scan'208\";a=\"6818501\"", "From": "<Tristram.Ha@microchip.com>", "To": "<andrew@lunn.ch>, <muvarov@gmail.com>, <pavel@ucw.cz>,\n\t<nathan.leigh.conrad@gmail.com>,\n\t<vivien.didelot@savoirfairelinux.com>, <f.fainelli@gmail.com>,\n\t<netdev@vger.kernel.org>, <linux-kernel@vger.kernel.org>,\n\t<Woojung.Huh@microchip.com>", "Subject": "[PATCH RFC 2/6] Create new file ksz9477.c from KSZ9477 code in\n\tksz_common.c", "Thread-Topic": "[PATCH RFC 2/6] Create new file ksz9477.c from KSZ9477 code in\n\tksz_common.c", "Thread-Index": "AdMoGj41K/xPwoG/RLOqTZHbh44b4QAAoZiA", "Date": "Thu, 7 Sep 2017 21:09:04 +0000", "Message-ID": "<93AF473E2DA327428DE3D46B72B1E9FD41121A06@CHN-SV-EXMX02.mchp-main.com>", "References": "<93AF473E2DA327428DE3D46B72B1E9FD411218CF@CHN-SV-EXMX02.mchp-main.com>", "In-Reply-To": "<93AF473E2DA327428DE3D46B72B1E9FD411218CF@CHN-SV-EXMX02.mchp-main.com>", "Accept-Language": "en-US", "Content-Language": "en-US", "X-MS-Has-Attach": "", "X-MS-TNEF-Correlator": "", "x-originating-ip": "[10.10.76.4]", "Content-Type": "text/plain; charset=\"us-ascii\"", "Content-Transfer-Encoding": "8BIT", "MIME-Version": "1.0", "Sender": "netdev-owner@vger.kernel.org", "Precedence": "bulk", "List-ID": "<netdev.vger.kernel.org>", "X-Mailing-List": "netdev@vger.kernel.org" }, "content": "From: Tristram Ha <Tristram.Ha@microchip.com>\n\nCreate new ksz9477.c file from original ksz_common.c.\n\nSigned-off-by: Tristram Ha <Tristram.Ha@microchip.com>\n---", "diff": "diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c\nnew file mode 100644\nindex 0000000..bc722b4\n--- /dev/null\n+++ b/drivers/net/dsa/microchip/ksz9477.c\n@@ -0,0 +1,1317 @@\n+/*\n+ * Microchip switch driver main logic\n+ *\n+ * Copyright (C) 2017\n+ *\n+ * Permission to use, copy, modify, and/or distribute this software for \n+any\n+ * purpose with or without fee is hereby granted, provided that the \n+above\n+ * copyright notice and this permission notice appear in all copies.\n+ *\n+ * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL \n+WARRANTIES\n+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE \n+FOR\n+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY \n+DAMAGES\n+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN \n+AN\n+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT \n+OF\n+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n+ */\n+\n+#include <linux/delay.h>\n+#include <linux/export.h>\n+#include <linux/gpio.h>\n+#include <linux/kernel.h>\n+#include <linux/module.h>\n+#include <linux/platform_data/microchip-ksz.h>\n+#include <linux/phy.h>\n+#include <linux/etherdevice.h>\n+#include <linux/if_bridge.h>\n+#include <net/dsa.h>\n+#include <net/switchdev.h>\n+\n+#include \"ksz_priv.h\"\n+#include \"ksz_9477_reg.h\"\n+\n+static const struct {\n+\tint index;\n+\tchar string[ETH_GSTRING_LEN];\n+} mib_names[TOTAL_SWITCH_COUNTER_NUM] = {\n+\t{ 0x00, \"rx_hi\" },\n+\t{ 0x01, \"rx_undersize\" },\n+\t{ 0x02, \"rx_fragments\" },\n+\t{ 0x03, \"rx_oversize\" },\n+\t{ 0x04, \"rx_jabbers\" },\n+\t{ 0x05, \"rx_symbol_err\" },\n+\t{ 0x06, \"rx_crc_err\" },\n+\t{ 0x07, \"rx_align_err\" },\n+\t{ 0x08, \"rx_mac_ctrl\" },\n+\t{ 0x09, \"rx_pause\" },\n+\t{ 0x0A, \"rx_bcast\" },\n+\t{ 0x0B, \"rx_mcast\" },\n+\t{ 0x0C, \"rx_ucast\" },\n+\t{ 0x0D, \"rx_64_or_less\" },\n+\t{ 0x0E, \"rx_65_127\" },\n+\t{ 0x0F, \"rx_128_255\" },\n+\t{ 0x10, \"rx_256_511\" },\n+\t{ 0x11, \"rx_512_1023\" },\n+\t{ 0x12, \"rx_1024_1522\" },\n+\t{ 0x13, \"rx_1523_2000\" },\n+\t{ 0x14, \"rx_2001\" },\n+\t{ 0x15, \"tx_hi\" },\n+\t{ 0x16, \"tx_late_col\" },\n+\t{ 0x17, \"tx_pause\" },\n+\t{ 0x18, \"tx_bcast\" },\n+\t{ 0x19, \"tx_mcast\" },\n+\t{ 0x1A, \"tx_ucast\" },\n+\t{ 0x1B, \"tx_deferred\" },\n+\t{ 0x1C, \"tx_total_col\" },\n+\t{ 0x1D, \"tx_exc_col\" },\n+\t{ 0x1E, \"tx_single_col\" },\n+\t{ 0x1F, \"tx_mult_col\" },\n+\t{ 0x80, \"rx_total\" },\n+\t{ 0x81, \"tx_total\" },\n+\t{ 0x82, \"rx_discards\" },\n+\t{ 0x83, \"tx_discards\" },\n+};\n+\n+static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool \n+set) {\n+\tu8 data;\n+\n+\tksz_read8(dev, addr, &data);\n+\tif (set)\n+\t\tdata |= bits;\n+\telse\n+\t\tdata &= ~bits;\n+\tksz_write8(dev, addr, data);\n+}\n+\n+static void ksz_cfg32(struct ksz_device *dev, u32 addr, u32 bits, bool \n+set) {\n+\tu32 data;\n+\n+\tksz_read32(dev, addr, &data);\n+\tif (set)\n+\t\tdata |= bits;\n+\telse\n+\t\tdata &= ~bits;\n+\tksz_write32(dev, addr, data);\n+}\n+\n+static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,\n+\t\t\t bool set)\n+{\n+\tu32 addr;\n+\tu8 data;\n+\n+\taddr = PORT_CTRL_ADDR(port, offset);\n+\tksz_read8(dev, addr, &data);\n+\n+\tif (set)\n+\t\tdata |= bits;\n+\telse\n+\t\tdata &= ~bits;\n+\n+\tksz_write8(dev, addr, data);\n+}\n+\n+static void ksz_port_cfg32(struct ksz_device *dev, int port, int offset,\n+\t\t\t u32 bits, bool set)\n+{\n+\tu32 addr;\n+\tu32 data;\n+\n+\taddr = PORT_CTRL_ADDR(port, offset);\n+\tksz_read32(dev, addr, &data);\n+\n+\tif (set)\n+\t\tdata |= bits;\n+\telse\n+\t\tdata &= ~bits;\n+\n+\tksz_write32(dev, addr, data);\n+}\n+\n+static int wait_vlan_ctrl_ready(struct ksz_device *dev, u32 waiton, int \n+timeout) {\n+\tu8 data;\n+\n+\tdo {\n+\t\tksz_read8(dev, REG_SW_VLAN_CTRL, &data);\n+\t\tif (!(data & waiton))\n+\t\t\tbreak;\n+\t\tusleep_range(1, 10);\n+\t} while (timeout-- > 0);\n+\n+\tif (timeout <= 0)\n+\t\treturn -ETIMEDOUT;\n+\n+\treturn 0;\n+}\n+\n+static int get_vlan_table(struct dsa_switch *ds, u16 vid, u32 \n+*vlan_table) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tint ret;\n+\n+\tmutex_lock(&dev->vlan_mutex);\n+\n+\tksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);\n+\tksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_READ | VLAN_START);\n+\n+\t/* wait to be cleared */\n+\tret = wait_vlan_ctrl_ready(dev, VLAN_START, 1000);\n+\tif (ret < 0) {\n+\t\tdev_dbg(dev->dev, \"Failed to read vlan table\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\tksz_read32(dev, REG_SW_VLAN_ENTRY__4, &vlan_table[0]);\n+\tksz_read32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, &vlan_table[1]);\n+\tksz_read32(dev, REG_SW_VLAN_ENTRY_PORTS__4, &vlan_table[2]);\n+\n+\tksz_write8(dev, REG_SW_VLAN_CTRL, 0);\n+\n+exit:\n+\tmutex_unlock(&dev->vlan_mutex);\n+\n+\treturn ret;\n+}\n+\n+static int set_vlan_table(struct dsa_switch *ds, u16 vid, u32 \n+*vlan_table) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tint ret;\n+\n+\tmutex_lock(&dev->vlan_mutex);\n+\n+\tksz_write32(dev, REG_SW_VLAN_ENTRY__4, vlan_table[0]);\n+\tksz_write32(dev, REG_SW_VLAN_ENTRY_UNTAG__4, vlan_table[1]);\n+\tksz_write32(dev, REG_SW_VLAN_ENTRY_PORTS__4, vlan_table[2]);\n+\n+\tksz_write16(dev, REG_SW_VLAN_ENTRY_INDEX__2, vid & VLAN_INDEX_M);\n+\tksz_write8(dev, REG_SW_VLAN_CTRL, VLAN_START | VLAN_WRITE);\n+\n+\t/* wait to be cleared */\n+\tret = wait_vlan_ctrl_ready(dev, VLAN_START, 1000);\n+\tif (ret < 0) {\n+\t\tdev_dbg(dev->dev, \"Failed to write vlan table\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\tksz_write8(dev, REG_SW_VLAN_CTRL, 0);\n+\n+\t/* update vlan cache table */\n+\tdev->vlan_cache[vid].table[0] = vlan_table[0];\n+\tdev->vlan_cache[vid].table[1] = vlan_table[1];\n+\tdev->vlan_cache[vid].table[2] = vlan_table[2];\n+\n+exit:\n+\tmutex_unlock(&dev->vlan_mutex);\n+\n+\treturn ret;\n+}\n+\n+static void read_table(struct dsa_switch *ds, u32 *table) {\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\tksz_read32(dev, REG_SW_ALU_VAL_A, &table[0]);\n+\tksz_read32(dev, REG_SW_ALU_VAL_B, &table[1]);\n+\tksz_read32(dev, REG_SW_ALU_VAL_C, &table[2]);\n+\tksz_read32(dev, REG_SW_ALU_VAL_D, &table[3]); }\n+\n+static void write_table(struct dsa_switch *ds, u32 *table) {\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\tksz_write32(dev, REG_SW_ALU_VAL_A, table[0]);\n+\tksz_write32(dev, REG_SW_ALU_VAL_B, table[1]);\n+\tksz_write32(dev, REG_SW_ALU_VAL_C, table[2]);\n+\tksz_write32(dev, REG_SW_ALU_VAL_D, table[3]); }\n+\n+static int wait_alu_ready(struct ksz_device *dev, u32 waiton, int \n+timeout) {\n+\tu32 data;\n+\n+\tdo {\n+\t\tksz_read32(dev, REG_SW_ALU_CTRL__4, &data);\n+\t\tif (!(data & waiton))\n+\t\t\tbreak;\n+\t\tusleep_range(1, 10);\n+\t} while (timeout-- > 0);\n+\n+\tif (timeout <= 0)\n+\t\treturn -ETIMEDOUT;\n+\n+\treturn 0;\n+}\n+\n+static int wait_alu_sta_ready(struct ksz_device *dev, u32 waiton, int \n+timeout) {\n+\tu32 data;\n+\n+\tdo {\n+\t\tksz_read32(dev, REG_SW_ALU_STAT_CTRL__4, &data);\n+\t\tif (!(data & waiton))\n+\t\t\tbreak;\n+\t\tusleep_range(1, 10);\n+\t} while (timeout-- > 0);\n+\n+\tif (timeout <= 0)\n+\t\treturn -ETIMEDOUT;\n+\n+\treturn 0;\n+}\n+\n+static int ksz_reset_switch(struct ksz_device *dev) {\n+\tu8 data8;\n+\tu16 data16;\n+\tu32 data32;\n+\n+\t/* reset switch */\n+\tksz_cfg(dev, REG_SW_OPERATION, SW_RESET, true);\n+\n+\t/* turn off SPI DO Edge select */\n+\tksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8);\n+\tdata8 &= ~SPI_AUTO_EDGE_DETECTION;\n+\tksz_write8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, data8);\n+\n+\t/* default configuration */\n+\tksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);\n+\tdata8 = SW_AGING_ENABLE | SW_LINK_AUTO_AGING |\n+\t SW_SRC_ADDR_FILTER | SW_FLUSH_STP_TABLE | SW_FLUSH_MSTP_TABLE;\n+\tksz_write8(dev, REG_SW_LUE_CTRL_1, data8);\n+\n+\t/* disable interrupts */\n+\tksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK);\n+\tksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0x7F);\n+\tksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32);\n+\n+\t/* set broadcast storm protection 10% rate */\n+\tksz_read16(dev, REG_SW_MAC_CTRL_2, &data16);\n+\tdata16 &= ~BROADCAST_STORM_RATE;\n+\tdata16 |= (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;\n+\tksz_write16(dev, REG_SW_MAC_CTRL_2, data16);\n+\n+\treturn 0;\n+}\n+\n+static void port_setup(struct ksz_device *dev, int port, bool cpu_port) \n+{\n+\tu8 data8;\n+\tu16 data16;\n+\n+\t/* enable tag tail for host port */\n+\tif (cpu_port)\n+\t\tksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_TAIL_TAG_ENABLE,\n+\t\t\t true);\n+\n+\tksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, false);\n+\n+\t/* set back pressure */\n+\tksz_port_cfg(dev, port, REG_PORT_MAC_CTRL_1, PORT_BACK_PRESSURE, \n+true);\n+\n+\t/* set flow control */\n+\tksz_port_cfg(dev, port, REG_PORT_CTRL_0,\n+\t\t PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL, true);\n+\n+\t/* enable broadcast storm limit */\n+\tksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, \n+true);\n+\n+\t/* disable DiffServ priority */\n+\tksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_PRIO_ENABLE, \n+false);\n+\n+\t/* replace priority */\n+\tksz_port_cfg(dev, port, REG_PORT_MRI_MAC_CTRL, PORT_USER_PRIO_CEILING,\n+\t\t false);\n+\tksz_port_cfg32(dev, port, REG_PORT_MTI_QUEUE_CTRL_0__4,\n+\t\t MTI_PVID_REPLACE, false);\n+\n+\t/* enable 802.1p priority */\n+\tksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true);\n+\n+\t/* configure MAC to 1G & RGMII mode */\n+\tksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8);\n+\tdata8 |= PORT_RGMII_ID_EG_ENABLE;\n+\tdata8 &= ~PORT_MII_NOT_1GBIT;\n+\tdata8 &= ~PORT_MII_SEL_M;\n+\tdata8 |= PORT_RGMII_SEL;\n+\tksz_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8);\n+\n+\t/* clear pending interrupts */\n+\tksz_pread16(dev, port, REG_PORT_PHY_INT_ENABLE, &data16); }\n+\n+static void ksz_config_cpu_port(struct dsa_switch *ds) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tint i;\n+\n+\tds->num_ports = dev->port_cnt;\n+\n+\tfor (i = 0; i < ds->num_ports; i++) {\n+\t\tif (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) {\n+\t\t\tdev->cpu_port = i;\n+\n+\t\t\t/* enable cpu port */\n+\t\t\tport_setup(dev, i, true);\n+\t\t\tdev->HOST_MASK = (1 << dev->cpu_port);\n+\t\t\tdev->PORT_MASK |= dev->HOST_MASK;\n+\t\t}\n+\t}\n+}\n+\n+static int ksz_setup(struct dsa_switch *ds) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tint ret = 0;\n+\n+\tdev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),\n+\t\t\t\t dev->num_vlans, GFP_KERNEL);\n+\tif (!dev->vlan_cache)\n+\t\treturn -ENOMEM;\n+\n+\tret = ksz_reset_switch(dev);\n+\tif (ret) {\n+\t\tdev_err(ds->dev, \"failed to reset switch\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\t/* accept packet up to 2000bytes */\n+\tksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_LEGAL_PACKET_DISABLE, true);\n+\n+\tksz_config_cpu_port(ds);\n+\n+\tksz_cfg(dev, REG_SW_MAC_CTRL_1, MULTICAST_STORM_DISABLE, true);\n+\n+\t/* queue based egress rate limit */\n+\tksz_cfg(dev, REG_SW_MAC_CTRL_5, SW_OUT_RATE_LIMIT_QUEUE_BASED, true);\n+\n+\t/* start switch */\n+\tksz_cfg(dev, REG_SW_OPERATION, SW_START, true);\n+\n+\treturn 0;\n+}\n+\n+static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch \n+*ds) {\n+\treturn DSA_TAG_PROTO_KSZ;\n+}\n+\n+static int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tu16 val = 0;\n+\n+\tksz_pread16(dev, addr, 0x100 + (reg << 1), &val);\n+\n+\treturn val;\n+}\n+\n+static int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, \n+u16 val) {\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\tksz_pwrite16(dev, addr, 0x100 + (reg << 1), val);\n+\n+\treturn 0;\n+}\n+\n+static int ksz_enable_port(struct dsa_switch *ds, int port,\n+\t\t\t struct phy_device *phy)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\t/* setup slave port */\n+\tport_setup(dev, port, false);\n+\n+\treturn 0;\n+}\n+\n+static void ksz_disable_port(struct dsa_switch *ds, int port,\n+\t\t\t struct phy_device *phy)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\t/* there is no port disable */\n+\tksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_MAC_LOOPBACK, true); }\n+\n+static int ksz_sset_count(struct dsa_switch *ds) {\n+\treturn TOTAL_SWITCH_COUNTER_NUM;\n+}\n+\n+static void ksz_get_strings(struct dsa_switch *ds, int port, uint8_t \n+*buf) {\n+\tint i;\n+\n+\tfor (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {\n+\t\tmemcpy(buf + i * ETH_GSTRING_LEN, mib_names[i].string,\n+\t\t ETH_GSTRING_LEN);\n+\t}\n+}\n+\n+static void ksz_get_ethtool_stats(struct dsa_switch *ds, int port,\n+\t\t\t\t uint64_t *buf)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\tint i;\n+\tu32 data;\n+\tint timeout;\n+\n+\tmutex_lock(&dev->stats_mutex);\n+\n+\tfor (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {\n+\t\tdata = MIB_COUNTER_READ;\n+\t\tdata |= ((mib_names[i].index & 0xFF) << MIB_COUNTER_INDEX_S);\n+\t\tksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, data);\n+\n+\t\ttimeout = 1000;\n+\t\tdo {\n+\t\t\tksz_pread32(dev, port, REG_PORT_MIB_CTRL_STAT__4,\n+\t\t\t\t &data);\n+\t\t\tusleep_range(1, 10);\n+\t\t\tif (!(data & MIB_COUNTER_READ))\n+\t\t\t\tbreak;\n+\t\t} while (timeout-- > 0);\n+\n+\t\t/* failed to read MIB. get out of loop */\n+\t\tif (!timeout) {\n+\t\t\tdev_dbg(dev->dev, \"Failed to get MIB\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* count resets upon read */\n+\t\tksz_pread32(dev, port, REG_PORT_MIB_DATA, &data);\n+\n+\t\tdev->ports[port].mib.info[i].counter += (uint64_t)data;\n+\t\tbuf[i] = dev->ports[port].mib.info[i].counter;\n+\t}\n+\n+\tmutex_unlock(&dev->stats_mutex);\n+}\n+\n+static void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 \n+state) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tu8 data;\n+\n+\tksz_pread8(dev, port, P_STP_CTRL, &data);\n+\tdata &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE);\n+\n+\tswitch (state) {\n+\tcase BR_STATE_DISABLED:\n+\t\tdata |= PORT_LEARN_DISABLE;\n+\t\tbreak;\n+\tcase BR_STATE_LISTENING:\n+\t\tdata |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);\n+\t\tbreak;\n+\tcase BR_STATE_LEARNING:\n+\t\tdata |= PORT_RX_ENABLE;\n+\t\tbreak;\n+\tcase BR_STATE_FORWARDING:\n+\t\tdata |= (PORT_TX_ENABLE | PORT_RX_ENABLE);\n+\t\tbreak;\n+\tcase BR_STATE_BLOCKING:\n+\t\tdata |= PORT_LEARN_DISABLE;\n+\t\tbreak;\n+\tdefault:\n+\t\tdev_err(ds->dev, \"invalid STP state: %d\\n\", state);\n+\t\treturn;\n+\t}\n+\n+\tksz_pwrite8(dev, port, P_STP_CTRL, data); }\n+\n+static void ksz_port_fast_age(struct dsa_switch *ds, int port) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tu8 data8;\n+\n+\tksz_read8(dev, REG_SW_LUE_CTRL_1, &data8);\n+\tdata8 |= SW_FAST_AGING;\n+\tksz_write8(dev, REG_SW_LUE_CTRL_1, data8);\n+\n+\tdata8 &= ~SW_FAST_AGING;\n+\tksz_write8(dev, REG_SW_LUE_CTRL_1, data8); }\n+\n+static int ksz_port_vlan_filtering(struct dsa_switch *ds, int port, \n+bool flag) {\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\tif (flag) {\n+\t\tksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,\n+\t\t\t PORT_VLAN_LOOKUP_VID_0, true);\n+\t\tksz_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, true);\n+\t\tksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, true);\n+\t} else {\n+\t\tksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, false);\n+\t\tksz_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, false);\n+\t\tksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,\n+\t\t\t PORT_VLAN_LOOKUP_VID_0, false);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int ksz_port_vlan_prepare(struct dsa_switch *ds, int port,\n+\t\t\t\t const struct switchdev_obj_port_vlan *vlan,\n+\t\t\t\t struct switchdev_trans *trans)\n+{\n+\t/* nothing needed */\n+\n+\treturn 0;\n+}\n+\n+static void ksz_port_vlan_add(struct dsa_switch *ds, int port,\n+\t\t\t const struct switchdev_obj_port_vlan *vlan,\n+\t\t\t struct switchdev_trans *trans) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tu32 vlan_table[3];\n+\tu16 vid;\n+\tbool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;\n+\n+\tfor (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {\n+\t\tif (get_vlan_table(ds, vid, vlan_table)) {\n+\t\t\tdev_dbg(dev->dev, \"Failed to get vlan table\\n\");\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tvlan_table[0] = VLAN_VALID | (vid & VLAN_FID_M);\n+\t\tif (untagged)\n+\t\t\tvlan_table[1] |= BIT(port);\n+\t\telse\n+\t\t\tvlan_table[1] &= ~BIT(port);\n+\t\tvlan_table[1] &= ~(BIT(dev->cpu_port));\n+\n+\t\tvlan_table[2] |= BIT(port) | BIT(dev->cpu_port);\n+\n+\t\tif (set_vlan_table(ds, vid, vlan_table)) {\n+\t\t\tdev_dbg(dev->dev, \"Failed to set vlan table\\n\");\n+\t\t\treturn;\n+\t\t}\n+\n+\t\t/* change PVID */\n+\t\tif (vlan->flags & BRIDGE_VLAN_INFO_PVID)\n+\t\t\tksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vid);\n+\t}\n+}\n+\n+static int ksz_port_vlan_del(struct dsa_switch *ds, int port,\n+\t\t\t const struct switchdev_obj_port_vlan *vlan) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tbool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;\n+\tu32 vlan_table[3];\n+\tu16 vid;\n+\tu16 pvid;\n+\n+\tksz_pread16(dev, port, REG_PORT_DEFAULT_VID, &pvid);\n+\tpvid = pvid & 0xFFF;\n+\n+\tfor (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {\n+\t\tif (get_vlan_table(ds, vid, vlan_table)) {\n+\t\t\tdev_dbg(dev->dev, \"Failed to get vlan table\\n\");\n+\t\t\treturn -ETIMEDOUT;\n+\t\t}\n+\n+\t\tvlan_table[2] &= ~BIT(port);\n+\n+\t\tif (pvid == vid)\n+\t\t\tpvid = 1;\n+\n+\t\tif (untagged)\n+\t\t\tvlan_table[1] &= ~BIT(port);\n+\n+\t\tif (set_vlan_table(ds, vid, vlan_table)) {\n+\t\t\tdev_dbg(dev->dev, \"Failed to set vlan table\\n\");\n+\t\t\treturn -ETIMEDOUT;\n+\t\t}\n+\t}\n+\n+\tksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, pvid);\n+\n+\treturn 0;\n+}\n+\n+struct alu_struct {\n+\t/* entry 1 */\n+\tu8\tis_static:1;\n+\tu8\tis_src_filter:1;\n+\tu8\tis_dst_filter:1;\n+\tu8\tprio_age:3;\n+\tu32\t_reserv_0_1:23;\n+\tu8\tmstp:3;\n+\t/* entry 2 */\n+\tu8\tis_override:1;\n+\tu8\tis_use_fid:1;\n+\tu32\t_reserv_1_1:23;\n+\tu8\tport_forward:7;\n+\t/* entry 3 & 4*/\n+\tu32\t_reserv_2_1:9;\n+\tu8\tfid:7;\n+\tu8\tmac[ETH_ALEN];\n+};\n+\n+static int ksz_port_fdb_add(struct dsa_switch *ds, int port,\n+\t\t\t const unsigned char *addr, u16 vid) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tu32 alu_table[4];\n+\tu32 data;\n+\tint ret = 0;\n+\n+\tmutex_lock(&dev->alu_mutex);\n+\n+\t/* find any entry with mac & vid */\n+\tdata = vid << ALU_FID_INDEX_S;\n+\tdata |= ((addr[0] << 8) | addr[1]);\n+\tksz_write32(dev, REG_SW_ALU_INDEX_0, data);\n+\n+\tdata = ((addr[2] << 24) | (addr[3] << 16));\n+\tdata |= ((addr[4] << 8) | addr[5]);\n+\tksz_write32(dev, REG_SW_ALU_INDEX_1, data);\n+\n+\t/* start read operation */\n+\tksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);\n+\n+\t/* wait to be finished */\n+\tret = wait_alu_ready(dev, ALU_START, 1000);\n+\tif (ret < 0) {\n+\t\tdev_dbg(dev->dev, \"Failed to read ALU\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\t/* read ALU entry */\n+\tread_table(ds, alu_table);\n+\n+\t/* update ALU entry */\n+\talu_table[0] = ALU_V_STATIC_VALID;\n+\talu_table[1] |= BIT(port);\n+\tif (vid)\n+\t\talu_table[1] |= ALU_V_USE_FID;\n+\talu_table[2] = (vid << ALU_V_FID_S);\n+\talu_table[2] |= ((addr[0] << 8) | addr[1]);\n+\talu_table[3] = ((addr[2] << 24) | (addr[3] << 16));\n+\talu_table[3] |= ((addr[4] << 8) | addr[5]);\n+\n+\twrite_table(ds, alu_table);\n+\n+\tksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);\n+\n+\t/* wait to be finished */\n+\tret = wait_alu_ready(dev, ALU_START, 1000);\n+\tif (ret < 0)\n+\t\tdev_dbg(dev->dev, \"Failed to write ALU\\n\");\n+\n+exit:\n+\tmutex_unlock(&dev->alu_mutex);\n+\n+\treturn ret;\n+}\n+\n+static int ksz_port_fdb_del(struct dsa_switch *ds, int port,\n+\t\t\t const unsigned char *addr, u16 vid) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tu32 alu_table[4];\n+\tu32 data;\n+\tint ret = 0;\n+\n+\tmutex_lock(&dev->alu_mutex);\n+\n+\t/* read any entry with mac & vid */\n+\tdata = vid << ALU_FID_INDEX_S;\n+\tdata |= ((addr[0] << 8) | addr[1]);\n+\tksz_write32(dev, REG_SW_ALU_INDEX_0, data);\n+\n+\tdata = ((addr[2] << 24) | (addr[3] << 16));\n+\tdata |= ((addr[4] << 8) | addr[5]);\n+\tksz_write32(dev, REG_SW_ALU_INDEX_1, data);\n+\n+\t/* start read operation */\n+\tksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START);\n+\n+\t/* wait to be finished */\n+\tret = wait_alu_ready(dev, ALU_START, 1000);\n+\tif (ret < 0) {\n+\t\tdev_dbg(dev->dev, \"Failed to read ALU\\n\");\n+\t\tgoto exit;\n+\t}\n+\n+\tksz_read32(dev, REG_SW_ALU_VAL_A, &alu_table[0]);\n+\tif (alu_table[0] & ALU_V_STATIC_VALID) {\n+\t\tksz_read32(dev, REG_SW_ALU_VAL_B, &alu_table[1]);\n+\t\tksz_read32(dev, REG_SW_ALU_VAL_C, &alu_table[2]);\n+\t\tksz_read32(dev, REG_SW_ALU_VAL_D, &alu_table[3]);\n+\n+\t\t/* clear forwarding port */\n+\t\talu_table[2] &= ~BIT(port);\n+\n+\t\t/* if there is no port to forward, clear table */\n+\t\tif ((alu_table[2] & ALU_V_PORT_MAP) == 0) {\n+\t\t\talu_table[0] = 0;\n+\t\t\talu_table[1] = 0;\n+\t\t\talu_table[2] = 0;\n+\t\t\talu_table[3] = 0;\n+\t\t}\n+\t} else {\n+\t\talu_table[0] = 0;\n+\t\talu_table[1] = 0;\n+\t\talu_table[2] = 0;\n+\t\talu_table[3] = 0;\n+\t}\n+\n+\twrite_table(ds, alu_table);\n+\n+\tksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START);\n+\n+\t/* wait to be finished */\n+\tret = wait_alu_ready(dev, ALU_START, 1000);\n+\tif (ret < 0)\n+\t\tdev_dbg(dev->dev, \"Failed to write ALU\\n\");\n+\n+exit:\n+\tmutex_unlock(&dev->alu_mutex);\n+\n+\treturn ret;\n+}\n+\n+static void convert_alu(struct alu_struct *alu, u32 *alu_table) {\n+\talu->is_static = !!(alu_table[0] & ALU_V_STATIC_VALID);\n+\talu->is_src_filter = !!(alu_table[0] & ALU_V_SRC_FILTER);\n+\talu->is_dst_filter = !!(alu_table[0] & ALU_V_DST_FILTER);\n+\talu->prio_age = (alu_table[0] >> ALU_V_PRIO_AGE_CNT_S) &\n+\t\t\tALU_V_PRIO_AGE_CNT_M;\n+\talu->mstp = alu_table[0] & ALU_V_MSTP_M;\n+\n+\talu->is_override = !!(alu_table[1] & ALU_V_OVERRIDE);\n+\talu->is_use_fid = !!(alu_table[1] & ALU_V_USE_FID);\n+\talu->port_forward = alu_table[1] & ALU_V_PORT_MAP;\n+\n+\talu->fid = (alu_table[2] >> ALU_V_FID_S) & ALU_V_FID_M;\n+\n+\talu->mac[0] = (alu_table[2] >> 8) & 0xFF;\n+\talu->mac[1] = alu_table[2] & 0xFF;\n+\talu->mac[2] = (alu_table[3] >> 24) & 0xFF;\n+\talu->mac[3] = (alu_table[3] >> 16) & 0xFF;\n+\talu->mac[4] = (alu_table[3] >> 8) & 0xFF;\n+\talu->mac[5] = alu_table[3] & 0xFF;\n+}\n+\n+static int ksz_port_fdb_dump(struct dsa_switch *ds, int port,\n+\t\t\t dsa_fdb_dump_cb_t *cb, void *data) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tint ret = 0;\n+\tu32 ksz_data;\n+\tu32 alu_table[4];\n+\tstruct alu_struct alu;\n+\tint timeout;\n+\n+\tmutex_lock(&dev->alu_mutex);\n+\n+\t/* start ALU search */\n+\tksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_START | ALU_SEARCH);\n+\n+\tdo {\n+\t\ttimeout = 1000;\n+\t\tdo {\n+\t\t\tksz_read32(dev, REG_SW_ALU_CTRL__4, &ksz_data);\n+\t\t\tif ((ksz_data & ALU_VALID) || !(ksz_data & ALU_START))\n+\t\t\t\tbreak;\n+\t\t\tusleep_range(1, 10);\n+\t\t} while (timeout-- > 0);\n+\n+\t\tif (!timeout) {\n+\t\t\tdev_dbg(dev->dev, \"Failed to search ALU\\n\");\n+\t\t\tret = -ETIMEDOUT;\n+\t\t\tgoto exit;\n+\t\t}\n+\n+\t\t/* read ALU table */\n+\t\tread_table(ds, alu_table);\n+\n+\t\tconvert_alu(&alu, alu_table);\n+\n+\t\tif (alu.port_forward & BIT(port)) {\n+\t\t\tret = cb(alu.mac, alu.fid, alu.is_static, data);\n+\t\t\tif (ret)\n+\t\t\t\tgoto exit;\n+\t\t}\n+\t} while (ksz_data & ALU_START);\n+\n+exit:\n+\n+\t/* stop ALU search */\n+\tksz_write32(dev, REG_SW_ALU_CTRL__4, 0);\n+\n+\tmutex_unlock(&dev->alu_mutex);\n+\n+\treturn ret;\n+}\n+\n+static int ksz_port_mdb_prepare(struct dsa_switch *ds, int port,\n+\t\t\t\tconst struct switchdev_obj_port_mdb *mdb,\n+\t\t\t\tstruct switchdev_trans *trans)\n+{\n+\t/* nothing to do */\n+\treturn 0;\n+}\n+\n+static void ksz_port_mdb_add(struct dsa_switch *ds, int port,\n+\t\t\t const struct switchdev_obj_port_mdb *mdb,\n+\t\t\t struct switchdev_trans *trans)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\tu32 static_table[4];\n+\tu32 data;\n+\tint index;\n+\tu32 mac_hi, mac_lo;\n+\n+\tmac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);\n+\tmac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));\n+\tmac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);\n+\n+\tmutex_lock(&dev->alu_mutex);\n+\n+\tfor (index = 0; index < dev->num_statics; index++) {\n+\t\t/* find empty slot first */\n+\t\tdata = (index << ALU_STAT_INDEX_S) |\n+\t\t\tALU_STAT_READ | ALU_STAT_START;\n+\t\tksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);\n+\n+\t\t/* wait to be finished */\n+\t\tif (wait_alu_sta_ready(dev, ALU_STAT_START, 1000) < 0) {\n+\t\t\tdev_dbg(dev->dev, \"Failed to read ALU STATIC\\n\");\n+\t\t\tgoto exit;\n+\t\t}\n+\n+\t\t/* read ALU static table */\n+\t\tread_table(ds, static_table);\n+\n+\t\tif (static_table[0] & ALU_V_STATIC_VALID) {\n+\t\t\t/* check this has same vid & mac address */\n+\t\t\tif (((static_table[2] >> ALU_V_FID_S) == mdb->vid) &&\n+\t\t\t ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&\n+\t\t\t static_table[3] == mac_lo) {\n+\t\t\t\t/* found matching one */\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t} else {\n+\t\t\t/* found empty one */\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\t/* no available entry */\n+\tif (index == dev->num_statics)\n+\t\tgoto exit;\n+\n+\t/* add entry */\n+\tstatic_table[0] = ALU_V_STATIC_VALID;\n+\tstatic_table[1] |= BIT(port);\n+\tif (mdb->vid)\n+\t\tstatic_table[1] |= ALU_V_USE_FID;\n+\tstatic_table[2] = (mdb->vid << ALU_V_FID_S);\n+\tstatic_table[2] |= mac_hi;\n+\tstatic_table[3] = mac_lo;\n+\n+\twrite_table(ds, static_table);\n+\n+\tdata = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;\n+\tksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);\n+\n+\t/* wait to be finished */\n+\tif (wait_alu_sta_ready(dev, ALU_STAT_START, 1000) < 0)\n+\t\tdev_dbg(dev->dev, \"Failed to read ALU STATIC\\n\");\n+\n+exit:\n+\tmutex_unlock(&dev->alu_mutex);\n+}\n+\n+static int ksz_port_mdb_del(struct dsa_switch *ds, int port,\n+\t\t\t const struct switchdev_obj_port_mdb *mdb) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tu32 static_table[4];\n+\tu32 data;\n+\tint index;\n+\tint ret = 0;\n+\tu32 mac_hi, mac_lo;\n+\n+\tmac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);\n+\tmac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));\n+\tmac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]);\n+\n+\tmutex_lock(&dev->alu_mutex);\n+\n+\tfor (index = 0; index < dev->num_statics; index++) {\n+\t\t/* find empty slot first */\n+\t\tdata = (index << ALU_STAT_INDEX_S) |\n+\t\t\tALU_STAT_READ | ALU_STAT_START;\n+\t\tksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);\n+\n+\t\t/* wait to be finished */\n+\t\tret = wait_alu_sta_ready(dev, ALU_STAT_START, 1000);\n+\t\tif (ret < 0) {\n+\t\t\tdev_dbg(dev->dev, \"Failed to read ALU STATIC\\n\");\n+\t\t\tgoto exit;\n+\t\t}\n+\n+\t\t/* read ALU static table */\n+\t\tread_table(ds, static_table);\n+\n+\t\tif (static_table[0] & ALU_V_STATIC_VALID) {\n+\t\t\t/* check this has same vid & mac address */\n+\n+\t\t\tif (((static_table[2] >> ALU_V_FID_S) == mdb->vid) &&\n+\t\t\t ((static_table[2] & ALU_V_MAC_ADDR_HI) == mac_hi) &&\n+\t\t\t static_table[3] == mac_lo) {\n+\t\t\t\t/* found matching one */\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/* no available entry */\n+\tif (index == dev->num_statics) {\n+\t\tret = -EINVAL;\n+\t\tgoto exit;\n+\t}\n+\n+\t/* clear port */\n+\tstatic_table[1] &= ~BIT(port);\n+\n+\tif ((static_table[1] & ALU_V_PORT_MAP) == 0) {\n+\t\t/* delete entry */\n+\t\tstatic_table[0] = 0;\n+\t\tstatic_table[1] = 0;\n+\t\tstatic_table[2] = 0;\n+\t\tstatic_table[3] = 0;\n+\t}\n+\n+\twrite_table(ds, static_table);\n+\n+\tdata = (index << ALU_STAT_INDEX_S) | ALU_STAT_START;\n+\tksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);\n+\n+\t/* wait to be finished */\n+\tret = wait_alu_sta_ready(dev, ALU_STAT_START, 1000);\n+\tif (ret < 0)\n+\t\tdev_dbg(dev->dev, \"Failed to read ALU STATIC\\n\");\n+\n+exit:\n+\tmutex_unlock(&dev->alu_mutex);\n+\n+\treturn ret;\n+}\n+\n+static int ksz_port_mirror_add(struct dsa_switch *ds, int port,\n+\t\t\t struct dsa_mall_mirror_tc_entry *mirror,\n+\t\t\t bool ingress)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\tif (ingress)\n+\t\tksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true);\n+\telse\n+\t\tksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);\n+\n+\tksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);\n+\n+\t/* configure mirror port */\n+\tksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,\n+\t\t PORT_MIRROR_SNIFFER, true);\n+\n+\tksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);\n+\n+\treturn 0;\n+}\n+\n+static void ksz_port_mirror_del(struct dsa_switch *ds, int port,\n+\t\t\t\tstruct dsa_mall_mirror_tc_entry *mirror) {\n+\tstruct ksz_device *dev = ds->priv;\n+\tu8 data;\n+\n+\tif (mirror->ingress)\n+\t\tksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false);\n+\telse\n+\t\tksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);\n+\n+\tksz_pread8(dev, port, P_MIRROR_CTRL, &data);\n+\n+\tif (!(data & (PORT_MIRROR_RX | PORT_MIRROR_TX)))\n+\t\tksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,\n+\t\t\t PORT_MIRROR_SNIFFER, false);\n+}\n+\n+static const struct dsa_switch_ops ksz_switch_ops = {\n+\t.get_tag_protocol\t= ksz_get_tag_protocol,\n+\t.setup\t\t\t= ksz_setup,\n+\t.phy_read\t\t= ksz_phy_read16,\n+\t.phy_write\t\t= ksz_phy_write16,\n+\t.port_enable\t\t= ksz_enable_port,\n+\t.port_disable\t\t= ksz_disable_port,\n+\t.get_strings\t\t= ksz_get_strings,\n+\t.get_ethtool_stats\t= ksz_get_ethtool_stats,\n+\t.get_sset_count\t\t= ksz_sset_count,\n+\t.port_stp_state_set\t= ksz_port_stp_state_set,\n+\t.port_fast_age\t\t= ksz_port_fast_age,\n+\t.port_vlan_filtering\t= ksz_port_vlan_filtering,\n+\t.port_vlan_prepare\t= ksz_port_vlan_prepare,\n+\t.port_vlan_add\t\t= ksz_port_vlan_add,\n+\t.port_vlan_del\t\t= ksz_port_vlan_del,\n+\t.port_fdb_dump\t\t= ksz_port_fdb_dump,\n+\t.port_fdb_add\t\t= ksz_port_fdb_add,\n+\t.port_fdb_del\t\t= ksz_port_fdb_del,\n+\t.port_mdb_prepare = ksz_port_mdb_prepare,\n+\t.port_mdb_add = ksz_port_mdb_add,\n+\t.port_mdb_del = ksz_port_mdb_del,\n+\t.port_mirror_add\t= ksz_port_mirror_add,\n+\t.port_mirror_del\t= ksz_port_mirror_del,\n+};\n+\n+static u32 ksz_get_port_addr(int port, int offset) {\n+\treturn PORT_CTRL_ADDR(port, offset);\n+}\n+\n+/* For Ingress (Host -> KSZ), 2 bytes are added before FCS.\n+ * \n+-----------------------------------------------------------------------\n+----\n+ * \n+DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|tag1(1byte)|FCS(4by\n+tes)\n+ * \n+-----------------------------------------------------------------------\n+----\n+ * tag0 : Prioritization (not used now)\n+ * tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, \n+0x10=port5)\n+ *\n+ * For Egress (KSZ -> Host), 1 byte is added before FCS.\n+ * \n+-----------------------------------------------------------------------\n+----\n+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)\n+ * \n+-----------------------------------------------------------------------\n+----\n+ * tag0 : zero-based value represents port\n+ *\t (eg, 0x00=port1, 0x02=port3, 0x06=port7)\n+ */\n+\n+#define\tKSZ_INGRESS_TAG_LEN\t2\n+#define\tKSZ_EGRESS_TAG_LEN\t1\n+#define KSZ_PTP_TAG_LEN\t\t4\n+#define KSZ_PTP_TAG_INDICATION\t0x80\n+\n+/**\n+ * This information needs to be communicated to the MAC driver so that \n+it can\n+ * receive the frames correctly from the switch as some MAC drivers \n+adhere to\n+ * receive exactly 1518 bytes.\n+ */\n+static int ksz_get_rx_len(struct ksz_device *dev) {\n+\tint len = KSZ_EGRESS_TAG_LEN;\n+\n+\t/* PTP function is turned on. */\n+\tif (dev->overrides & PTP_TAG)\n+\t\tlen += KSZ_PTP_TAG_LEN;\n+\treturn len;\n+}\n+\n+/**\n+ * This information tells how many more bytes need to be allocated to\n+ * accommodate the tail tag.\n+ */\n+static int ksz_get_tx_len(struct ksz_device *dev) {\n+\tint len = KSZ_INGRESS_TAG_LEN;\n+\n+\t/* PTP function is turned on. */\n+\tif (dev->overrides & PTP_TAG)\n+\t\tlen += KSZ_PTP_TAG_LEN;\n+\treturn len;\n+}\n+\n+static void ksz_add_tail_tag(struct ksz_device *dev, struct sk_buff *skb,\n+\t\t\t int port)\n+{\n+\tstruct {\n+\t\tu32 timestamp;\n+\t\tu16 ports;\n+\t} tx_tag;\n+\tu8 *trailer;\n+\tu8 *tag;\n+\tint len = KSZ_INGRESS_TAG_LEN;\n+\tint ptp_len = 0;\n+\n+\tport = 1 << port;\n+\n+\t/* PTP function is turned on. */\n+\tif (dev->overrides & PTP_TAG)\n+\t\tptp_len = KSZ_PTP_TAG_LEN;\n+\tlen += ptp_len;\n+\ttrailer = skb_put(skb, len);\n+\tmemset(&tx_tag, 0, sizeof(tx_tag));\n+\ttx_tag.ports = port & dev->PORT_MASK;\n+\ttx_tag.ports = cpu_to_be16(tx_tag.ports);\n+\ttag = (u8 *)&tx_tag;\n+\tmemcpy(trailer, &tag[4 - ptp_len], ptp_len + KSZ_INGRESS_TAG_LEN); }\n+\n+static int ksz_get_tail_tag(struct ksz_device *dev, struct sk_buff *skb,\n+\t\t\t int *port)\n+{\n+\tu8 *trailer;\n+\tint len = KSZ_EGRESS_TAG_LEN;\n+\n+\ttrailer = skb_tail_pointer(skb) - len;\n+\tif (*trailer & KSZ_PTP_TAG_INDICATION)\n+\t\tlen += KSZ_PTP_TAG_LEN;\n+\t*trailer &= ~KSZ_PTP_TAG_INDICATION;\n+\t*port = *trailer;\n+\tif (*port >= 0 && *port < dev->mib_port_cnt) {\n+\t\tstruct ksz_port *p = &dev->ports[*port];\n+\n+\t\t/**\n+\t\t * Switch is forwarding when port membership includes other\n+\t\t * ports.\n+\t\t */\n+\t\tif (p->member & ~((1 << *port) | dev->HOST_MASK))\n+\t\t\tskb->offload_fwd_mark = 1;\n+\t}\n+\treturn len;\n+}\n+\n+static int ksz_switch_detect(struct ksz_device *dev) {\n+\tu8 data8;\n+\tu32 id32;\n+\tint ret;\n+\n+\t/* turn off SPI DO Edge select */\n+\tret = ksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tdata8 &= ~SPI_AUTO_EDGE_DETECTION;\n+\tret = ksz_write8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, data8);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* read chip id */\n+\tret = ksz_read32(dev, REG_CHIP_ID0__1, &id32);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* Number of ports can be reduced depending on chip. */\n+\tdev->mib_port_cnt = TOTAL_PORT_NUM;\n+\n+\tdev->chip_id = id32;\n+\n+\tdev->mib_cnt = TOTAL_SWITCH_COUNTER_NUM;\n+\n+\t/* Try to read MIB counters in all ports every second. */\n+\tdev->MIB_READ_INTERVAL = msecs_to_jiffies(1000 / dev->mib_port_cnt);\n+\n+\treturn 0;\n+}\n+\n+struct ksz_chip_data {\n+\tu32 chip_id;\n+\tconst char *dev_name;\n+\tint num_vlans;\n+\tint num_alus;\n+\tint num_statics;\n+\tint cpu_ports;\n+\tint port_cnt;\n+};\n+\n+static const struct ksz_chip_data ksz_switch_chips[] = {\n+\t{\n+\t\t.chip_id = 0x00947700,\n+\t\t.dev_name = \"KSZ9477\",\n+\t\t.num_vlans = 4096,\n+\t\t.num_alus = 4096,\n+\t\t.num_statics = 16,\n+\t\t.cpu_ports = 0x7F,\t/* can be configured as cpu port */\n+\t\t.port_cnt = 7,\t\t/* total physical port count */\n+\t},\n+};\n+\n+static int ksz_switch_init(struct ksz_device *dev) {\n+\tint i;\n+\n+\tmutex_init(&dev->reg_mutex);\n+\tmutex_init(&dev->stats_mutex);\n+\tmutex_init(&dev->alu_mutex);\n+\tmutex_init(&dev->vlan_mutex);\n+\n+\tdev->ds->ops = &ksz_switch_ops;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(ksz_switch_chips); i++) {\n+\t\tconst struct ksz_chip_data *chip = &ksz_switch_chips[i];\n+\n+\t\tif (dev->chip_id == chip->chip_id) {\n+\t\t\tdev->name = chip->dev_name;\n+\t\t\tdev->num_vlans = chip->num_vlans;\n+\t\t\tdev->num_alus = chip->num_alus;\n+\t\t\tdev->num_statics = chip->num_statics;\n+\t\t\tdev->port_cnt = chip->port_cnt;\n+\t\t\tdev->cpu_ports = chip->cpu_ports;\n+\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\t/* no switch found */\n+\tif (!dev->port_cnt)\n+\t\treturn -ENODEV;\n+\n+\tdev->PORT_MASK = (1 << dev->port_cnt) - 1;\n+\tdev->PORT_MASK |= dev->HOST_MASK;\n+\n+\ti = dev->mib_port_cnt;\n+\tdev->ports = devm_kzalloc(dev->dev, sizeof(struct ksz_port) * i,\n+\t\t\t\t GFP_KERNEL);\n+\tif (!dev->ports)\n+\t\treturn -ENOMEM;\n+\tfor (i = 0; i < dev->mib_port_cnt; i++) {\n+\t\tdev->ports[i].mib.info =\n+\t\t\tdevm_kzalloc(dev->dev,\n+\t\t\t\t sizeof(struct ksz_port_mib_info) *\n+\t\t\t\t TOTAL_SWITCH_COUNTER_NUM, GFP_KERNEL);\n+\t\tif (!dev->ports[i].mib.info)\n+\t\t\treturn -ENOMEM;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void ksz_switch_exit(struct ksz_device *dev) { }\n+\n+static const struct ksz_dev_ops ksz9477_dev_ops = {\n+\t.get_port_addr = ksz_get_port_addr,\n+\t.reset = ksz_reset_switch,\n+\t.get_rx_len = ksz_get_rx_len,\n+\t.get_tx_len = ksz_get_tx_len,\n+\t.add_tail_tag = ksz_add_tail_tag,\n+\t.get_tail_tag = ksz_get_tail_tag,\n+\t.detect = ksz_switch_detect,\n+\t.init = ksz_switch_init,\n+\t.exit = ksz_switch_exit,\n+};\n+\n+int ksz9477_switch_register(struct ksz_device *dev) {\n+\treturn ksz_switch_register(dev, &ksz9477_dev_ops); } \n+EXPORT_SYMBOL(ksz9477_switch_register);\n+\n+MODULE_AUTHOR(\"Woojung Huh <Woojung.Huh@microchip.com>\"); \n+MODULE_DESCRIPTION(\"Microchip KSZ Series Switch DSA Driver\"); \n+MODULE_LICENSE(\"GPL\");\n\n", "prefixes": [ "RFC", "2/6" ] }