get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/811180/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 811180,
    "url": "http://patchwork.ozlabs.org/api/patches/811180/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/netdev/patch/93AF473E2DA327428DE3D46B72B1E9FD41121A87@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": "<93AF473E2DA327428DE3D46B72B1E9FD41121A87@CHN-SV-EXMX02.mchp-main.com>",
    "list_archive_url": null,
    "date": "2017-09-07T21:17:16",
    "name": "[RFC,3/5] Add KSZ8795 switch driver",
    "commit_ref": null,
    "pull_url": null,
    "state": "rfc",
    "archived": true,
    "hash": "f3c6f72fb977ba120a425f403f10b35aa2dba801",
    "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/93AF473E2DA327428DE3D46B72B1E9FD41121A87@CHN-SV-EXMX02.mchp-main.com/mbox/",
    "series": [
        {
            "id": 2066,
            "url": "http://patchwork.ozlabs.org/api/series/2066/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/netdev/list/?series=2066",
            "date": "2017-09-07T21:17:33",
            "name": "[RFC,1/5] Add KSZ8795 switch driver support in Kconfig",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/2066/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/811180/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/811180/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 3xpCwj4xVYz9s3T\n\tfor <patchwork-incoming@ozlabs.org>;\n\tFri,  8 Sep 2017 07:18:13 +1000 (AEST)",
            "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1756111AbdIGVRh convert rfc822-to-8bit (ORCPT\n\t<rfc822;patchwork-incoming@ozlabs.org>);\n\tThu, 7 Sep 2017 17:17:37 -0400",
            "from esa4.microchip.iphmx.com ([68.232.154.123]:8554 \"EHLO\n\tesa4.microchip.iphmx.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1756103AbdIGVRe (ORCPT\n\t<rfc822;netdev@vger.kernel.org>); Thu, 7 Sep 2017 17:17:34 -0400",
            "from exsmtp02.microchip.com (HELO email.microchip.com)\n\t([198.175.253.38])\n\tby esa4.microchip.iphmx.com with ESMTP/TLS/DHE-RSA-AES256-SHA;\n\t07 Sep 2017 14:17:17 -0700",
            "from CHN-SV-EXMX02.mchp-main.com ([fe80::7dfe:3761:863e:3963]) by\n\tCHN-SV-EXCH02.mchp-main.com ([::1]) with mapi id 14.03.0352.000;\n\tThu, 7 Sep 2017 14:17:17 -0700"
        ],
        "X-IronPort-AV": "E=Sophos;i=\"5.42,360,1500966000\"; d=\"scan'208\";a=\"6632199\"",
        "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 3/5] Add KSZ8795 switch driver",
        "Thread-Topic": "[PATCH RFC 3/5] Add KSZ8795 switch driver",
        "Thread-Index": "AdMoGw7chwKpH5nHQDyZLhGSoYClOwAA0svA",
        "Date": "Thu, 7 Sep 2017 21:17:16 +0000",
        "Message-ID": "<93AF473E2DA327428DE3D46B72B1E9FD41121A87@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\nAdd KSZ8795 switch support with function code.\n\nSigned-off-by: Tristram Ha <Tristram.Ha@microchip.com>\n---",
    "diff": "diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c\nnew file mode 100644\nindex 0000000..e4d4e6a\n--- /dev/null\n+++ b/drivers/net/dsa/microchip/ksz8795.c\n@@ -0,0 +1,2066 @@\n+/*\n+ * Microchip KSZ8795 switch driver\n+ *\n+ * Copyright (C) 2017 Microchip Technology Inc.\n+ *\tTristram Ha <Tristram.Ha@microchip.com>\n+ *\n+ * Permission to use, copy, modify, and/or distribute this software for any\n+ * purpose with or without fee is hereby granted, provided that the 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 WARRANTIES\n+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 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 \"ksz8795.h\"\n+\n+/**\n+ * Some counters do not need to be read too often because they are less likely\n+ * to increase much.\n+ */\n+static const struct {\n+\tint interval;\n+\tchar string[ETH_GSTRING_LEN];\n+} mib_names[TOTAL_SWITCH_COUNTER_NUM] = {\n+\t{ 1, \"rx_hi\" },\n+\t{ 4, \"rx_undersize\" },\n+\t{ 4, \"rx_fragments\" },\n+\t{ 4, \"rx_oversize\" },\n+\t{ 4, \"rx_jabbers\" },\n+\t{ 4, \"rx_symbol_err\" },\n+\t{ 4, \"rx_crc_err\" },\n+\t{ 4, \"rx_align_err\" },\n+\t{ 4, \"rx_mac_ctrl\" },\n+\t{ 1, \"rx_pause\" },\n+\t{ 1, \"rx_bcast\" },\n+\t{ 1, \"rx_mcast\" },\n+\t{ 1, \"rx_ucast\" },\n+\t{ 2, \"rx_64_or_less\" },\n+\t{ 2, \"rx_65_127\" },\n+\t{ 2, \"rx_128_255\" },\n+\t{ 2, \"rx_256_511\" },\n+\t{ 2, \"rx_512_1023\" },\n+\t{ 2, \"rx_1024_1522\" },\n+\t{ 2, \"rx_1523_2000\" },\n+\t{ 2, \"rx_2001\" },\n+\t{ 1, \"tx_hi\" },\n+\t{ 4, \"tx_late_col\" },\n+\t{ 1, \"tx_pause\" },\n+\t{ 1, \"tx_bcast\" },\n+\t{ 1, \"tx_mcast\" },\n+\t{ 1, \"tx_ucast\" },\n+\t{ 4, \"tx_deferred\" },\n+\t{ 4, \"tx_total_col\" },\n+\t{ 4, \"tx_exc_col\" },\n+\t{ 4, \"tx_single_col\" },\n+\t{ 4, \"tx_mult_col\" },\n+\t{ 1, \"rx_total\" },\n+\t{ 1, \"tx_total\" },\n+\t{ 2, \"rx_discards\" },\n+\t{ 2, \"tx_discards\" },\n+};\n+\n+static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)\n+{\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_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 int chk_last_port(struct ksz_device *dev, int p)\n+{\n+\tif (dev->last_port && p == dev->last_port)\n+\t\tp = dev->cpu_port;\n+\treturn p;\n+}\n+\n+static int ksz_reset_switch(struct ksz_device *dev)\n+{\n+\t/* reset switch */\n+\tksz_write8(dev, REG_POWER_MANAGEMENT_1,\n+\t\t   SW_SOFTWARE_POWER_DOWN << SW_POWER_MANAGEMENT_MODE_S);\n+\tksz_write8(dev, REG_POWER_MANAGEMENT_1, 0);\n+\n+\treturn 0;\n+}\n+\n+static void port_set_prio_queue(struct ksz_device *dev, int port, int queue)\n+{\n+\tu8 hi;\n+\tu8 lo;\n+\n+\tswitch (queue) {\n+\tcase 4:\n+\tcase 3:\n+\t\tqueue = PORT_QUEUE_SPLIT_4;\n+\t\tbreak;\n+\tcase 2:\n+\t\tqueue = PORT_QUEUE_SPLIT_2;\n+\t\tbreak;\n+\tdefault:\n+\t\tqueue = PORT_QUEUE_SPLIT_1;\n+\t}\n+\tksz_pread8(dev, port, REG_PORT_CTRL_0, &lo);\n+\tksz_pread8(dev, port, P_DROP_TAG_CTRL, &hi);\n+\tlo &= ~PORT_QUEUE_SPLIT_L;\n+\tif (queue & 1)\n+\t\tlo |= PORT_QUEUE_SPLIT_L;\n+\thi &= ~PORT_QUEUE_SPLIT_H;\n+\tif (queue & 2)\n+\t\thi |= PORT_QUEUE_SPLIT_H;\n+\tksz_pwrite8(dev, port, REG_PORT_CTRL_0, lo);\n+\tksz_pwrite8(dev, port, P_DROP_TAG_CTRL, hi);\n+\n+\t/* Default is port based for egress rate limit. */\n+\tif (queue)\n+\t\tksz_cfg(dev, REG_SW_CTRL_19, SW_OUT_RATE_LIMIT_QUEUE_BASED,\n+\t\t\ttrue);\n+}\n+\n+static void port_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt)\n+{\n+\tu32 data;\n+\tu16 ctrl_addr;\n+\tu8 check;\n+\tint timeout;\n+\n+\tctrl_addr = addr + SWITCH_COUNTER_NUM * port;\n+\n+\tctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ);\n+\tmutex_lock(&dev->stats_mutex);\n+\tksz_write16(dev, REG_IND_CTRL_0, ctrl_addr);\n+\n+\tfor (timeout = 1; timeout > 0; timeout--) {\n+\t\tksz_read8(dev, REG_IND_MIB_CHECK, &check);\n+\n+\t\tif (check & MIB_COUNTER_VALID) {\n+\t\t\tksz_read32(dev, REG_IND_DATA_LO, &data);\n+\t\t\tif (check & MIB_COUNTER_OVERFLOW)\n+\t\t\t\t*cnt += MIB_COUNTER_VALUE + 1;\n+\t\t\t*cnt += data & MIB_COUNTER_VALUE;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tmutex_unlock(&dev->stats_mutex);\n+}\n+\n+static void port_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, u64 *cnt)\n+{\n+\tu32 data;\n+\tu16 ctrl_addr;\n+\tu8 check;\n+\tint timeout;\n+\n+\taddr -= SWITCH_COUNTER_NUM;\n+\tctrl_addr = (KS_MIB_TOTAL_RX_1 - KS_MIB_TOTAL_RX_0) * port;\n+\tctrl_addr += addr + KS_MIB_TOTAL_RX_0;\n+\n+\tctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ);\n+\tmutex_lock(&dev->stats_mutex);\n+\tksz_write16(dev, REG_IND_CTRL_0, ctrl_addr);\n+\n+\tfor (timeout = 1; timeout > 0; timeout--) {\n+\t\tksz_read8(dev, REG_IND_MIB_CHECK, &check);\n+\n+\t\tif (check & MIB_COUNTER_VALID) {\n+\t\t\tksz_read32(dev, REG_IND_DATA_LO, &data);\n+\t\t\tif (addr < 2) {\n+\t\t\t\tu64 total;\n+\n+\t\t\t\ttotal = check & MIB_TOTAL_BYTES_H;\n+\t\t\t\ttotal <<= 32;\n+\t\t\t\t*cnt += total;\n+\t\t\t\t*cnt += data;\n+\t\t\t\tif (check & MIB_COUNTER_OVERFLOW) {\n+\t\t\t\t\ttotal = MIB_TOTAL_BYTES_H + 1;\n+\t\t\t\t\ttotal <<= 32;\n+\t\t\t\t\t*cnt += total;\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\tif (check & MIB_COUNTER_OVERFLOW)\n+\t\t\t\t\t*cnt += MIB_PACKET_DROPPED + 1;\n+\t\t\t\t*cnt += data & MIB_PACKET_DROPPED;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tmutex_unlock(&dev->stats_mutex);\n+}\n+\n+static int port_r_cnt(struct ksz_device *dev, int port)\n+{\n+\tstruct ksz_port_mib *mib = &dev->ports[port].mib;\n+\tstruct ksz_port_mib_info *info;\n+\n+\t/* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */\n+\twhile (mib->cnt_ptr < SWITCH_COUNTER_NUM) {\n+\t\tinfo = &mib->info[mib->cnt_ptr];\n+\t\tspin_lock(&dev->mib_read_lock);\n+\t\t++info->read_cnt;\n+\t\tif (info->read_cnt >= info->read_max) {\n+\t\t\tinfo->read_cnt = 0;\n+\t\t\tport_r_mib_cnt(dev, port, mib->cnt_ptr,\n+\t\t\t\t       &info->counter);\n+\t\t}\n+\t\tspin_unlock(&dev->mib_read_lock);\n+\t\t++mib->cnt_ptr;\n+\t}\n+\n+\t/* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */\n+\twhile (mib->cnt_ptr < TOTAL_SWITCH_COUNTER_NUM) {\n+\t\tinfo = &mib->info[mib->cnt_ptr];\n+\t\tspin_lock(&dev->mib_read_lock);\n+\t\t++info->read_cnt;\n+\t\tif (info->read_cnt >= info->read_max) {\n+\t\t\tinfo->read_cnt = 0;\n+\t\t\tport_r_mib_pkt(dev, port, mib->cnt_ptr,\n+\t\t\t\t       &info->counter);\n+\t\t}\n+\t\tspin_unlock(&dev->mib_read_lock);\n+\t\t++mib->cnt_ptr;\n+\t}\n+\tmib->cnt_ptr = 0;\n+\treturn 0;\n+}\n+\n+static void port_init_cnt(struct ksz_device *dev, int port)\n+{\n+\tstruct ksz_port_mib *mib = &dev->ports[port].mib;\n+\tstruct ksz_port_mib_info *info;\n+\n+\tmib->cnt_ptr = 0;\n+\n+\t/* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */\n+\twhile (mib->cnt_ptr < SWITCH_COUNTER_NUM) {\n+\t\tinfo = &mib->info[mib->cnt_ptr];\n+\t\tinfo->read_cnt = 0;\n+\t\tinfo->read_max = mib_names[mib->cnt_ptr].interval;\n+\t\tport_r_mib_cnt(dev, port, mib->cnt_ptr,\n+\t\t\t       &info->counter);\n+\t\tinfo->counter = 0;\n+\t\t++mib->cnt_ptr;\n+\t}\n+\n+\t/* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */\n+\twhile (mib->cnt_ptr < TOTAL_SWITCH_COUNTER_NUM) {\n+\t\tinfo->read_cnt = 0;\n+\t\tinfo->read_max = mib_names[mib->cnt_ptr].interval;\n+\t\tport_r_mib_pkt(dev, port, mib->cnt_ptr,\n+\t\t\t       &info->counter);\n+\t\tinfo->counter = 0;\n+\t\t++mib->cnt_ptr;\n+\t}\n+\tmib->cnt_ptr = 0;\n+\tmib->time = jiffies;\n+}\n+\n+static void ksz_r_table_64(struct ksz_device *dev, int table, u16 addr,\n+\t\t\t   u64 *data)\n+{\n+\tu16 ctrl_addr;\n+\n+\tctrl_addr = IND_ACC_TABLE(table | TABLE_READ) | addr;\n+\n+\tksz_write16(dev, REG_IND_CTRL_0, ctrl_addr);\n+\tdev->ops->get(dev, REG_IND_DATA_HI, data, sizeof(u64));\n+\t*data = be64_to_cpu(*data);\n+}\n+\n+static void ksz_w_table_64(struct ksz_device *dev, int table, u16 addr,\n+\t\t\t   u64 data)\n+{\n+\tu16 ctrl_addr;\n+\n+\tctrl_addr = IND_ACC_TABLE(table) | addr;\n+\tdata = cpu_to_be64(data);\n+\n+\tdev->ops->set(dev, REG_IND_DATA_HI, &data, sizeof(u64));\n+\tksz_write16(dev, REG_IND_CTRL_0, ctrl_addr);\n+}\n+\n+static int valid_dyn_entry(struct ksz_device *dev, u8 *data)\n+{\n+\tint timeout = 100;\n+\n+\tdo {\n+\t\tksz_read8(dev, REG_IND_DATA_CHECK, data);\n+\t\ttimeout--;\n+\t} while ((*data & DYNAMIC_MAC_TABLE_NOT_READY) && timeout);\n+\n+\t/* Entry is not ready for accessing. */\n+\tif (*data & DYNAMIC_MAC_TABLE_NOT_READY) {\n+\t\treturn 1;\n+\t/* Entry is ready for accessing. */\n+\t} else {\n+\t\tksz_read8(dev, REG_IND_DATA_8, data);\n+\n+\t\t/* There is no valid entry in the table. */\n+\t\tif (*data & DYNAMIC_MAC_TABLE_MAC_EMPTY)\n+\t\t\treturn 2;\n+\t}\n+\treturn 0;\n+}\n+\n+static int ksz_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr,\n+\t\t\t       u8 *fid, u8 *src_port, u8 *timestamp,\n+\t\t\t       u16 *entries)\n+{\n+\tu32 data_hi;\n+\tu32 data_lo;\n+\tu16 ctrl_addr;\n+\tint rc;\n+\tu8 data;\n+\n+\tctrl_addr = IND_ACC_TABLE(TABLE_DYNAMIC_MAC | TABLE_READ) | addr;\n+\n+\tksz_write16(dev, REG_IND_CTRL_0, ctrl_addr);\n+\n+\trc = valid_dyn_entry(dev, &data);\n+\tif (rc == 1) {\n+\t\tif (addr == 0)\n+\t\t\t*entries = 0;\n+\t} else if (rc == 2) {\n+\t\t*entries = 0;\n+\t/* At least one valid entry in the table. */\n+\t} else {\n+\t\tu64 buf;\n+\n+\t\tdev->ops->get(dev, REG_IND_DATA_HI, &buf, sizeof(buf));\n+\t\tbuf = be64_to_cpu(buf);\n+\t\tdata_hi = (u32)(buf >> 32);\n+\t\tdata_lo = (u32)buf;\n+\n+\t\t/* Check out how many valid entry in the table. */\n+\t\t*entries = (u16)(((((u16)\n+\t\t\tdata & DYNAMIC_MAC_TABLE_ENTRIES_H) <<\n+\t\t\tDYNAMIC_MAC_ENTRIES_H_S) |\n+\t\t\t(((data_hi & DYNAMIC_MAC_TABLE_ENTRIES) >>\n+\t\t\tDYNAMIC_MAC_ENTRIES_S))) + 1);\n+\n+\t\t*fid = (u8)((data_hi & DYNAMIC_MAC_TABLE_FID) >>\n+\t\t\tDYNAMIC_MAC_FID_S);\n+\t\t*src_port = (u8)((data_hi & DYNAMIC_MAC_TABLE_SRC_PORT) >>\n+\t\t\tDYNAMIC_MAC_SRC_PORT_S);\n+\t\t*timestamp = (u8)((\n+\t\t\tdata_hi & DYNAMIC_MAC_TABLE_TIMESTAMP) >>\n+\t\t\tDYNAMIC_MAC_TIMESTAMP_S);\n+\n+\t\tmac_addr[5] = (u8)data_lo;\n+\t\tmac_addr[4] = (u8)(data_lo >> 8);\n+\t\tmac_addr[3] = (u8)(data_lo >> 16);\n+\t\tmac_addr[2] = (u8)(data_lo >> 24);\n+\n+\t\tmac_addr[1] = (u8)data_hi;\n+\t\tmac_addr[0] = (u8)(data_hi >> 8);\n+\t\trc = 0;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+struct alu_struct {\n+\tu8\tis_static:1;\n+\tu8\tprio_age:3;\n+\tu8\tis_override:1;\n+\tu8\tis_use_fid:1;\n+\tu8\tport_forward:5;\n+\tu8\tfid:7;\n+\tu8\tmac[ETH_ALEN];\n+};\n+\n+static int ksz_r_sta_mac_table(struct ksz_device *dev, u16 addr,\n+\t\t\t       struct alu_struct *alu)\n+{\n+\tu64 data;\n+\tu32 data_hi;\n+\tu32 data_lo;\n+\n+\tksz_r_table_64(dev, TABLE_STATIC_MAC, addr, &data);\n+\tdata_hi = data >> 32;\n+\tdata_lo = (u32)data;\n+\tif (data_hi & (STATIC_MAC_TABLE_VALID | STATIC_MAC_TABLE_OVERRIDE)) {\n+\t\talu->mac[5] = (u8)data_lo;\n+\t\talu->mac[4] = (u8)(data_lo >> 8);\n+\t\talu->mac[3] = (u8)(data_lo >> 16);\n+\t\talu->mac[2] = (u8)(data_lo >> 24);\n+\t\talu->mac[1] = (u8)data_hi;\n+\t\talu->mac[0] = (u8)(data_hi >> 8);\n+\t\talu->port_forward =\n+\t\t\t(u8)((data_hi & STATIC_MAC_TABLE_FWD_PORTS) >>\n+\t\t\tSTATIC_MAC_FWD_PORTS_S);\n+\t\talu->is_override =\n+\t\t\t(data_hi & STATIC_MAC_TABLE_OVERRIDE) ? 1 : 0;\n+\t\tdata_hi >>= 1;\n+\t\talu->is_use_fid = (data_hi & STATIC_MAC_TABLE_USE_FID) ? 1 : 0;\n+\t\talu->fid = (u8)((data_hi & STATIC_MAC_TABLE_FID) >>\n+\t\t\tSTATIC_MAC_FID_S);\n+\t\treturn 0;\n+\t}\n+\treturn -1;\n+}\n+\n+static void ksz_w_sta_mac_table(struct ksz_device *dev, u16 addr,\n+\t\t\t\tstruct alu_struct *alu)\n+{\n+\tu64 data;\n+\tu32 data_hi;\n+\tu32 data_lo;\n+\n+\tdata_lo = ((u32)alu->mac[2] << 24) |\n+\t\t((u32)alu->mac[3] << 16) |\n+\t\t((u32)alu->mac[4] << 8) | alu->mac[5];\n+\tdata_hi = ((u32)alu->mac[0] << 8) | alu->mac[1];\n+\tdata_hi |= (u32)alu->port_forward << STATIC_MAC_FWD_PORTS_S;\n+\n+\tif (alu->is_override)\n+\t\tdata_hi |= STATIC_MAC_TABLE_OVERRIDE;\n+\tif (alu->is_use_fid) {\n+\t\tdata_hi |= STATIC_MAC_TABLE_USE_FID;\n+\t\tdata_hi |= (u32)alu->fid << STATIC_MAC_FID_S;\n+\t}\n+\tif (alu->is_static)\n+\t\tdata_hi |= STATIC_MAC_TABLE_VALID;\n+\telse\n+\t\tdata_hi &= ~STATIC_MAC_TABLE_OVERRIDE;\n+\n+\tdata = (u64)data_hi << 32 | data_lo;\n+\tksz_w_table_64(dev, TABLE_STATIC_MAC, addr, data);\n+}\n+\n+static inline void from_vlan(u16 vlan, u8 *fid, u8 *member, u8 *valid)\n+{\n+\t*fid = (u8)(vlan & VLAN_TABLE_FID);\n+\t*member = (u8)(vlan & VLAN_TABLE_MEMBERSHIP) >> VLAN_TABLE_MEMBERSHIP_S;\n+\t*valid = !!(vlan & VLAN_TABLE_VALID);\n+}\n+\n+static inline void to_vlan(u8 fid, u8 member, u8 valid, u16 *vlan)\n+{\n+\t*vlan = fid;\n+\t*vlan |= (u16)member << VLAN_TABLE_MEMBERSHIP_S;\n+\tif (valid)\n+\t\t*vlan |= VLAN_TABLE_VALID;\n+}\n+\n+static void ksz_r_vlan_entries(struct ksz_device *dev, u16 addr)\n+{\n+\tu64 data;\n+\tint i;\n+\n+\tksz_r_table_64(dev, TABLE_VLAN, addr, &data);\n+\taddr *= 4;\n+\tfor (i = 0; i < 4; i++) {\n+\t\tdev->vlan_cache[addr + i].table[0] = (u16)data;\n+\t\tdata >>= VLAN_TABLE_S;\n+\t}\n+}\n+\n+static void ksz_r_vlan_table(struct ksz_device *dev, u16 vid,\n+\t\t\t     u16 *vlan)\n+{\n+\tu64 buf;\n+\tu16 *data = (u16 *)&buf;\n+\tu16 addr;\n+\tint index;\n+\n+\taddr = vid / 4;\n+\tindex = vid & 3;\n+\tksz_r_table_64(dev, TABLE_VLAN, addr, &buf);\n+\t*vlan = data[index];\n+}\n+\n+static void ksz_w_vlan_table(struct ksz_device *dev, u16 vid,\n+\t\t\t     u16 vlan)\n+{\n+\tu64 buf;\n+\tu16 *data = (u16 *)&buf;\n+\tu16 addr;\n+\tint index;\n+\n+\taddr = vid / 4;\n+\tindex = vid & 3;\n+\tksz_r_table_64(dev, TABLE_VLAN, addr, &buf);\n+\tdata[index] = vlan;\n+\tdev->vlan_cache[vid].table[0] = vlan;\n+\tksz_w_table_64(dev, TABLE_VLAN, addr, buf);\n+}\n+\n+static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds)\n+{\n+\treturn DSA_TAG_PROTO_KSZ;\n+}\n+\n+static void ksz_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val)\n+{\n+\tstruct ksz_port *port;\n+\tu8 ctrl;\n+\tu8 restart;\n+\tu8 link;\n+\tu8 speed;\n+\tu8 force;\n+\tu8 p = phy;\n+\tu16 data = 0;\n+\tint processed = true;\n+\n+\tport = &dev->ports[p];\n+\tswitch (reg) {\n+\tcase PHY_REG_CTRL:\n+\t\tksz_pread8(dev, p, P_LOCAL_CTRL, &ctrl);\n+\t\tksz_pread8(dev, p, P_NEG_RESTART_CTRL, &restart);\n+\t\tksz_pread8(dev, p, P_SPEED_STATUS, &speed);\n+\t\tksz_pread8(dev, p, P_FORCE_CTRL, &force);\n+\t\tif (restart & PORT_PHY_LOOPBACK)\n+\t\t\tdata |= PHY_LOOPBACK;\n+\t\tif (force & PORT_FORCE_100_MBIT)\n+\t\t\tdata |= PHY_SPEED_100MBIT;\n+\t\tif (!(force & PORT_AUTO_NEG_DISABLE))\n+\t\t\tdata |= PHY_AUTO_NEG_ENABLE;\n+\t\tif (restart & PORT_POWER_DOWN)\n+\t\t\tdata |= PHY_POWER_DOWN;\n+\t\tif (restart & PORT_AUTO_NEG_RESTART)\n+\t\t\tdata |= PHY_AUTO_NEG_RESTART;\n+\t\tif (force & PORT_FORCE_FULL_DUPLEX)\n+\t\t\tdata |= PHY_FULL_DUPLEX;\n+\t\tif (speed & PORT_HP_MDIX)\n+\t\t\tdata |= PHY_HP_MDIX;\n+\t\tif (restart & PORT_FORCE_MDIX)\n+\t\t\tdata |= PHY_FORCE_MDIX;\n+\t\tif (restart & PORT_AUTO_MDIX_DISABLE)\n+\t\t\tdata |= PHY_AUTO_MDIX_DISABLE;\n+\t\tif (restart & PORT_TX_DISABLE)\n+\t\t\tdata |= PHY_TRANSMIT_DISABLE;\n+\t\tif (restart & PORT_LED_OFF)\n+\t\t\tdata |= PHY_LED_DISABLE;\n+\t\tbreak;\n+\tcase PHY_REG_STATUS:\n+\t\tksz_pread8(dev, p, P_LINK_STATUS, &link);\n+\t\tksz_pread8(dev, p, P_SPEED_STATUS, &speed);\n+\t\tdata = PHY_100BTX_FD_CAPABLE |\n+\t\t\tPHY_100BTX_CAPABLE |\n+\t\t\tPHY_10BT_FD_CAPABLE |\n+\t\t\tPHY_10BT_CAPABLE |\n+\t\t\tPHY_AUTO_NEG_CAPABLE;\n+\t\tif (link & PORT_AUTO_NEG_COMPLETE)\n+\t\t\tdata |= PHY_AUTO_NEG_ACKNOWLEDGE;\n+\t\tif (link & PORT_STAT_LINK_GOOD) {\n+\t\t\tdata |= PHY_LINK_STATUS;\n+\t\t\tif (!port->link_up) {\n+\t\t\t\tport->link_up = 1;\n+\t\t\t\tdev->live_ports |= (1 << phy) & dev->on_ports;\n+\t\t\t}\n+\t\t} else if (port->link_up) {\n+\t\t\tport->link_down = 1;\n+\t\t\tport->link_up = 0;\n+\t\t\tdev->live_ports &= ~(1 << phy);\n+\t\t}\n+\t\tbreak;\n+\tcase PHY_REG_ID_1:\n+\t\tdata = KSZ8795_ID_HI;\n+\t\tbreak;\n+\tcase PHY_REG_ID_2:\n+\t\tdata = KSZ8795_ID_LO;\n+\t\tbreak;\n+\tcase PHY_REG_AUTO_NEGOTIATION:\n+\t\tksz_pread8(dev, p, P_LOCAL_CTRL, &ctrl);\n+\t\tdata = PHY_AUTO_NEG_802_3;\n+\t\tif (ctrl & PORT_AUTO_NEG_SYM_PAUSE)\n+\t\t\tdata |= PHY_AUTO_NEG_SYM_PAUSE;\n+\t\tif (ctrl & PORT_AUTO_NEG_100BTX_FD)\n+\t\t\tdata |= PHY_AUTO_NEG_100BTX_FD;\n+\t\tif (ctrl & PORT_AUTO_NEG_100BTX)\n+\t\t\tdata |= PHY_AUTO_NEG_100BTX;\n+\t\tif (ctrl & PORT_AUTO_NEG_10BT_FD)\n+\t\t\tdata |= PHY_AUTO_NEG_10BT_FD;\n+\t\tif (ctrl & PORT_AUTO_NEG_10BT)\n+\t\t\tdata |= PHY_AUTO_NEG_10BT;\n+\t\tbreak;\n+\tcase PHY_REG_REMOTE_CAPABILITY:\n+\t\tksz_pread8(dev, p, P_REMOTE_STATUS, &link);\n+\t\tdata = PHY_AUTO_NEG_802_3;\n+\t\tif (link & PORT_REMOTE_SYM_PAUSE)\n+\t\t\tdata |= PHY_AUTO_NEG_SYM_PAUSE;\n+\t\tif (link & PORT_REMOTE_100BTX_FD)\n+\t\t\tdata |= PHY_AUTO_NEG_100BTX_FD;\n+\t\tif (link & PORT_REMOTE_100BTX)\n+\t\t\tdata |= PHY_AUTO_NEG_100BTX;\n+\t\tif (link & PORT_REMOTE_10BT_FD)\n+\t\t\tdata |= PHY_AUTO_NEG_10BT_FD;\n+\t\tif (link & PORT_REMOTE_10BT)\n+\t\t\tdata |= PHY_AUTO_NEG_10BT;\n+\t\tbreak;\n+\tdefault:\n+\t\tprocessed = false;\n+\t\tbreak;\n+\t}\n+\tif (processed)\n+\t\t*val = data;\n+}\n+\n+static void ksz_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)\n+{\n+\tu8 ctrl;\n+\tu8 restart;\n+\tu8 speed;\n+\tu8 data;\n+\tu8 p = phy;\n+\n+\tswitch (reg) {\n+\tcase PHY_REG_CTRL:\n+\n+\t\t/* Do not support PHY reset function. */\n+\t\tif (val & PHY_RESET)\n+\t\t\tbreak;\n+\t\tksz_pread8(dev, p, P_SPEED_STATUS, &speed);\n+\t\tdata = speed;\n+\t\tif (val & PHY_HP_MDIX)\n+\t\t\tdata |= PORT_HP_MDIX;\n+\t\telse\n+\t\t\tdata &= ~PORT_HP_MDIX;\n+\t\tif (data != speed)\n+\t\t\tksz_pwrite8(dev, p, P_SPEED_STATUS, data);\n+\t\tksz_pread8(dev, p, P_FORCE_CTRL, &ctrl);\n+\t\tdata = ctrl;\n+\t\tif (!(val & PHY_AUTO_NEG_ENABLE))\n+\t\t\tdata |= PORT_AUTO_NEG_DISABLE;\n+\t\telse\n+\t\t\tdata &= ~PORT_AUTO_NEG_DISABLE;\n+\n+\t\t/* Fiber port does not support auto-negotiation. */\n+\t\tif (dev->ports[p].fiber)\n+\t\t\tdata |= PORT_AUTO_NEG_DISABLE;\n+\t\tif (val & PHY_SPEED_100MBIT)\n+\t\t\tdata |= PORT_FORCE_100_MBIT;\n+\t\telse\n+\t\t\tdata &= ~PORT_FORCE_100_MBIT;\n+\t\tif (val & PHY_FULL_DUPLEX)\n+\t\t\tdata |= PORT_FORCE_FULL_DUPLEX;\n+\t\telse\n+\t\t\tdata &= ~PORT_FORCE_FULL_DUPLEX;\n+\t\tif (data != ctrl)\n+\t\t\tksz_pwrite8(dev, p, P_FORCE_CTRL, data);\n+\t\tksz_pread8(dev, p, P_NEG_RESTART_CTRL, &restart);\n+\t\tdata = restart;\n+\t\tif (val & PHY_LED_DISABLE)\n+\t\t\tdata |= PORT_LED_OFF;\n+\t\telse\n+\t\t\tdata &= ~PORT_LED_OFF;\n+\t\tif (val & PHY_TRANSMIT_DISABLE)\n+\t\t\tdata |= PORT_TX_DISABLE;\n+\t\telse\n+\t\t\tdata &= ~PORT_TX_DISABLE;\n+\t\tif (val & PHY_AUTO_NEG_RESTART)\n+\t\t\tdata |= PORT_AUTO_NEG_RESTART;\n+\t\telse\n+\t\t\tdata &= ~(PORT_AUTO_NEG_RESTART);\n+\t\tif (val & PHY_POWER_DOWN)\n+\t\t\tdata |= PORT_POWER_DOWN;\n+\t\telse\n+\t\t\tdata &= ~PORT_POWER_DOWN;\n+\t\tif (val & PHY_AUTO_MDIX_DISABLE)\n+\t\t\tdata |= PORT_AUTO_MDIX_DISABLE;\n+\t\telse\n+\t\t\tdata &= ~PORT_AUTO_MDIX_DISABLE;\n+\t\tif (val & PHY_FORCE_MDIX)\n+\t\t\tdata |= PORT_FORCE_MDIX;\n+\t\telse\n+\t\t\tdata &= ~PORT_FORCE_MDIX;\n+\t\tif (val & PHY_LOOPBACK)\n+\t\t\tdata |= PORT_PHY_LOOPBACK;\n+\t\telse\n+\t\t\tdata &= ~PORT_PHY_LOOPBACK;\n+\t\tif (data != restart)\n+\t\t\tksz_pwrite8(dev, p, P_NEG_RESTART_CTRL, data);\n+\t\tbreak;\n+\tcase PHY_REG_AUTO_NEGOTIATION:\n+\t\tksz_pread8(dev, p, P_LOCAL_CTRL, &ctrl);\n+\t\tdata = ctrl;\n+\t\tdata &= ~(PORT_AUTO_NEG_SYM_PAUSE |\n+\t\t\tPORT_AUTO_NEG_100BTX_FD |\n+\t\t\tPORT_AUTO_NEG_100BTX |\n+\t\t\tPORT_AUTO_NEG_10BT_FD |\n+\t\t\tPORT_AUTO_NEG_10BT);\n+\t\tif (val & PHY_AUTO_NEG_SYM_PAUSE)\n+\t\t\tdata |= PORT_AUTO_NEG_SYM_PAUSE;\n+\t\tif (val & PHY_AUTO_NEG_100BTX_FD)\n+\t\t\tdata |= PORT_AUTO_NEG_100BTX_FD;\n+\t\tif (val & PHY_AUTO_NEG_100BTX)\n+\t\t\tdata |= PORT_AUTO_NEG_100BTX;\n+\t\tif (val & PHY_AUTO_NEG_10BT_FD)\n+\t\t\tdata |= PORT_AUTO_NEG_10BT_FD;\n+\t\tif (val & PHY_AUTO_NEG_10BT)\n+\t\t\tdata |= PORT_AUTO_NEG_10BT;\n+\t\tif (data != ctrl)\n+\t\t\tksz_pwrite8(dev, p, P_LOCAL_CTRL, data);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+}\n+\n+static int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\tu16 val = 0xffff;\n+\n+\tksz_r_phy(dev, addr, reg, &val);\n+\n+\treturn val;\n+}\n+\n+static int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\tksz_w_phy(dev, addr, reg, val);\n+\n+\treturn 0;\n+}\n+\n+static int ksz_sset_count(struct dsa_switch *ds)\n+{\n+\treturn TOTAL_SWITCH_COUNTER_NUM;\n+}\n+\n+static void ksz_get_strings(struct dsa_switch *ds, int port, uint8_t *buf)\n+{\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+\tstruct ksz_port_mib *mib;\n+\tstruct ksz_port_mib_info *info;\n+\tint i;\n+\n+\tmib = &dev->ports[port].mib;\n+\n+\tspin_lock(&dev->mib_read_lock);\n+\tfor (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {\n+\t\tinfo = &mib->info[i];\n+\t\tbuf[i] = info->counter;\n+\t\tinfo->read_cnt += info->read_max;\n+\t}\n+\tspin_unlock(&dev->mib_read_lock);\n+\n+\tmib->ctrl = 1;\n+\tschedule_work(&dev->mib_read);\n+}\n+\n+static u8 STP_MULTICAST_ADDR[] = {\n+\t0x01, 0x80, 0xC2, 0x00, 0x00, 0x00\n+};\n+\n+static void ksz_cfg_port_member(struct ksz_device *dev, int port, u8 member)\n+{\n+\tu8 data;\n+\n+\tksz_pread8(dev, port, P_MIRROR_CTRL, &data);\n+\tdata &= ~PORT_VLAN_MEMBERSHIP;\n+\tdata |= (member & dev->PORT_MASK);\n+\tksz_pwrite8(dev, port, P_MIRROR_CTRL, data);\n+\tdev->ports[port].member = member;\n+}\n+\n+static void ksz_update_port_member(struct ksz_device *dev, int port)\n+{\n+\tstruct ksz_port *p;\n+\tint i;\n+\n+\tfor (i = 0; i < dev->port_cnt; i++) {\n+\t\tif (i == port)\n+\t\t\tcontinue;\n+\t\tp = &dev->ports[i];\n+\t\tif (!(dev->member & (1 << i)))\n+\t\t\tcontinue;\n+\n+\t\t/* Port is a member of the bridge and is forwarding. */\n+\t\tif (p->stp_state == BR_STATE_FORWARDING)\n+\t\t\tksz_cfg_port_member(dev, i, dev->member);\n+\t}\n+}\n+\n+static int ksz_port_bridge_join(struct dsa_switch *ds, int port,\n+\t\t\t\tstruct net_device *br)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\tdev->br_member |= (1 << port);\n+\n+\t/**\n+\t * port_stp_state_set() will be called after to put the port in\n+\t * appropriate state so there is no need to do anything.\n+\t */\n+\n+\treturn 0;\n+}\n+\n+static void ksz_port_bridge_leave(struct dsa_switch *ds, int port,\n+\t\t\t\t  struct net_device *br)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\tdev->br_member &= ~(1 << port);\n+\tdev->member &= ~(1 << port);\n+\n+\t/**\n+\t * port_stp_state_set() will be called after to put the port in\n+\t * forwarding state so there is no need to do anything.\n+\t */\n+}\n+\n+static void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\tstruct ksz_port *p = &dev->ports[port];\n+\tu8 data;\n+\tu16 forward = dev->member;\n+\tint member = -1;\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\tif (port < SWITCH_PORT_NUM)\n+\t\t\tmember = 0;\n+\t\tbreak;\n+\tcase BR_STATE_LISTENING:\n+\t\tdata |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE);\n+\t\tif (port < SWITCH_PORT_NUM &&\n+\t\t    p->stp_state == BR_STATE_DISABLED)\n+\t\t\tmember = dev->HOST_MASK | p->vid_member;\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+\n+\t\t/* This function is also used internally. */\n+\t\tif (port == dev->cpu_port)\n+\t\t\tbreak;\n+\n+\t\t/* Port is a member of a bridge. */\n+\t\tif (dev->br_member & (1 << port)) {\n+\t\t\tdev->member |= (1 << port);\n+\t\t\tmember = dev->member;\n+\t\t} else {\n+\t\t\tmember = dev->HOST_MASK | p->vid_member;\n+\t\t}\n+\t\tbreak;\n+\tcase BR_STATE_BLOCKING:\n+\t\tdata |= PORT_LEARN_DISABLE;\n+\t\tif (port < SWITCH_PORT_NUM &&\n+\t\t    p->stp_state == BR_STATE_DISABLED)\n+\t\t\tmember = dev->HOST_MASK | p->vid_member;\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+\tp->stp_state = state;\n+\tif (data & PORT_RX_ENABLE)\n+\t\tdev->rx_ports |= (1 << port);\n+\telse\n+\t\tdev->rx_ports &= ~(1 << port);\n+\tif (data & PORT_TX_ENABLE)\n+\t\tdev->tx_ports |= (1 << port);\n+\telse\n+\t\tdev->tx_ports &= ~(1 << port);\n+\n+\t/* Port membership may share register with STP state. */\n+\tif (member >= 0 && member != p->member)\n+\t\tksz_cfg_port_member(dev, port, (u8)member);\n+\n+\t/* Check if forwarding needs to be updated. */\n+\tif (state != BR_STATE_FORWARDING) {\n+\t\tif (dev->br_member & (1 << port))\n+\t\t\tdev->member &= ~(1 << port);\n+\t}\n+\n+\t/* Topology is changed. */\n+\tif (forward != dev->member)\n+\t\tksz_update_port_member(dev, port);\n+}\n+\n+static void ksz_flush_dyn_mac_table(struct ksz_device *dev, int port)\n+{\n+\tint cnt;\n+\tint first;\n+\tint index;\n+\tint last;\n+\tu8 learn[TOTAL_PORT_NUM];\n+\n+\tif ((uint)port < TOTAL_PORT_NUM) {\n+\t\tfirst = port;\n+\t\tcnt = port + 1;\n+\t} else {\n+\t\t/* Flush all ports. */\n+\t\tfirst = 0;\n+\t\tcnt = dev->mib_port_cnt;\n+\t}\n+\tfor (index = first; index < cnt; index++) {\n+\t\tlast = chk_last_port(dev, index);\n+\t\tif (last != index)\n+\t\t\tcontinue;\n+\t\tksz_pread8(dev, index, P_STP_CTRL, &learn[index]);\n+\t\tif (!(learn[index] & PORT_LEARN_DISABLE))\n+\t\t\tksz_pwrite8(dev, index, P_STP_CTRL,\n+\t\t\t\t    learn[index] | PORT_LEARN_DISABLE);\n+\t}\n+\tksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_DYN_MAC_TABLE, true);\n+\tfor (index = first; index < cnt; index++) {\n+\t\tlast = chk_last_port(dev, index);\n+\t\tif (last != index)\n+\t\t\tcontinue;\n+\t\tksz_pwrite8(dev, index, P_STP_CTRL, learn[index]);\n+\t}\n+}\n+\n+static void ksz_port_fast_age(struct dsa_switch *ds, int port)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\tksz_flush_dyn_mac_table(dev, port);\n+}\n+\n+static int ksz_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\tksz_cfg(dev, S_MIRROR_CTRL, SW_VLAN_ENABLE, flag);\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+{\n+\tstruct ksz_device *dev = ds->priv;\n+\tbool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;\n+\tu16 data;\n+\tu16 vid;\n+\tu8 fid;\n+\tu8 member;\n+\tu8 valid;\n+\tu16 new_pvid = 0;\n+\n+\tksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged);\n+\n+\tfor (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {\n+\t\tksz_r_vlan_table(dev, vid, &data);\n+\t\tfrom_vlan(data, &fid, &member, &valid);\n+\n+\t\t/* First time to setup the VLAN entry. */\n+\t\tif (!valid) {\n+\t\t\t/* Need to find a way to map VID to FID. */\n+\t\t\tfid = 1;\n+\t\t\tvalid = 1;\n+\t\t}\n+\t\tmember |= BIT(port);\n+\n+\t\tto_vlan(fid, member, valid, &data);\n+\t\tksz_w_vlan_table(dev, vid, data);\n+\n+\t\t/* change PVID */\n+\t\tif (vlan->flags & BRIDGE_VLAN_INFO_PVID)\n+\t\t\tnew_pvid = vid;\n+\t}\n+\n+\tif (new_pvid) {\n+\t\tksz_pread16(dev, port, REG_PORT_CTRL_VID, &vid);\n+\t\tvid &= 0xfff;\n+\t\tvid |= new_pvid;\n+\t\tksz_pwrite16(dev, port, REG_PORT_CTRL_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+{\n+\tstruct ksz_device *dev = ds->priv;\n+\tbool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;\n+\tu16 data;\n+\tu16 vid;\n+\tu16 pvid;\n+\tu8 fid;\n+\tu8 member;\n+\tu8 valid;\n+\tu16 new_pvid = 0;\n+\n+\tksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid);\n+\tpvid = pvid & 0xFFF;\n+\n+\tksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged);\n+\n+\tfor (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {\n+\t\tksz_r_vlan_table(dev, vid, &data);\n+\t\tfrom_vlan(data, &fid, &member, &valid);\n+\n+\t\tmember &= ~BIT(port);\n+\n+\t\t/* Invalidate the entry if no more member. */\n+\t\tif (!member) {\n+\t\t\tfid = 0;\n+\t\t\tvalid = 0;\n+\t\t}\n+\n+\t\tif (pvid == vid)\n+\t\t\tnew_pvid = 1;\n+\n+\t\tto_vlan(fid, member, valid, &data);\n+\t\tksz_w_vlan_table(dev, vid, data);\n+\t}\n+\n+\tif (new_pvid != pvid)\n+\t\tksz_pwrite16(dev, port, REG_PORT_CTRL_VID, pvid);\n+\n+\treturn 0;\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+{\n+\tstruct ksz_device *dev = ds->priv;\n+\tint ret = 0;\n+\tu16 i = 0;\n+\tu16 entries = 0;\n+\tu8 timestamp = 0;\n+\tu8 fid;\n+\tu8 member;\n+\tstruct alu_struct alu;\n+\n+\tmutex_lock(&dev->alu_mutex);\n+\tdo {\n+\t\talu.is_static = false;\n+\t\tret = ksz_r_dyn_mac_table(dev, i, alu.mac, &fid, &member,\n+\t\t\t\t\t  &timestamp, &entries);\n+\t\tif (!ret && (member & BIT(port))) {\n+\t\t\tret = cb(alu.mac, alu.fid, alu.is_static, data);\n+\t\t\tif (ret)\n+\t\t\t\tbreak;\n+\t\t}\n+\t\ti++;\n+\t} while (i < entries);\n+\tmutex_unlock(&dev->alu_mutex);\n+\tif (i >= entries)\n+\t\tret = 0;\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+\tstruct alu_struct alu;\n+\tint index;\n+\tint empty = 0;\n+\n+\tmutex_lock(&dev->alu_mutex);\n+\n+\tfor (index = 0; index < dev->num_statics; index++) {\n+\t\tif (!ksz_r_sta_mac_table(dev, index, &alu)) {\n+\t\t\t/* Found one already in static MAC table. */\n+\t\t\tif (!memcmp(alu.mac, mdb->addr, ETH_ALEN) &&\n+\t\t\t    alu.fid == mdb->vid)\n+\t\t\t\tbreak;\n+\t\t/* Remember the first empty entry. */\n+\t\t} else if (!empty) {\n+\t\t\tempty = index + 1;\n+\t\t}\n+\t}\n+\n+\t/* no available entry */\n+\tif (index == dev->num_statics && !empty)\n+\t\tgoto exit;\n+\n+\t/* add entry */\n+\tif (index == dev->num_statics) {\n+\t\tindex = empty - 1;\n+\t\tmemset(&alu, 0, sizeof(alu));\n+\t\tmemcpy(alu.mac, mdb->addr, ETH_ALEN);\n+\t\talu.is_static = true;\n+\t}\n+\talu.port_forward |= BIT(port);\n+\tif (mdb->vid) {\n+\t\talu.is_use_fid = true;\n+\n+\t\t/* Need a way to map VID to FID. */\n+\t\talu.fid = mdb->vid;\n+\t}\n+\tksz_w_sta_mac_table(dev, index, &alu);\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+{\n+\tstruct ksz_device *dev = ds->priv;\n+\tstruct alu_struct alu;\n+\tint index;\n+\tint ret = 0;\n+\n+\tmutex_lock(&dev->alu_mutex);\n+\n+\tfor (index = 0; index < dev->num_statics; index++) {\n+\t\tif (!ksz_r_sta_mac_table(dev, index, &alu)) {\n+\t\t\t/* Found one already in static MAC table. */\n+\t\t\tif (!memcmp(alu.mac, mdb->addr, ETH_ALEN) &&\n+\t\t\t    alu.fid == mdb->vid)\n+\t\t\t\tbreak;\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+\talu.port_forward &= ~BIT(port);\n+\tif (!alu.port_forward)\n+\t\talu.is_static = false;\n+\tksz_w_sta_mac_table(dev, index, &alu);\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+\t\tdev->mirror_rx |= (1 << port);\n+\t} else {\n+\t\tksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);\n+\t\tdev->mirror_tx |= (1 << port);\n+\t}\n+\n+\tksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);\n+\n+\t/* configure mirror port */\n+\tif (dev->mirror_rx || dev->mirror_tx)\n+\t\tksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,\n+\t\t\t     PORT_MIRROR_SNIFFER, true);\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+{\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+\t\tdev->mirror_rx &= ~(1 << port);\n+\t} else {\n+\t\tksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);\n+\t\tdev->mirror_tx &= ~(1 << port);\n+\t}\n+\n+\tksz_pread8(dev, port, P_MIRROR_CTRL, &data);\n+\n+\tif (!dev->mirror_rx && !dev->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 u8 ksz_advertised_flow_ctrl(u8 flow_ctrl, u8 ctrl)\n+{\n+\tctrl &= ~PORT_AUTO_NEG_SYM_PAUSE;\n+\tswitch (flow_ctrl) {\n+\tcase PHY_FLOW_CTRL:\n+\t\tctrl |= PORT_AUTO_NEG_SYM_PAUSE;\n+\t\tbreak;\n+\n+\t/* Not supported. */\n+\tcase PHY_TX_ONLY:\n+\tcase PHY_RX_ONLY:\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\treturn ctrl;\n+}\n+\n+static u8 ksz_determine_flow_ctrl(u8 local, u8 remote, int force)\n+{\n+\tint rx;\n+\tint tx;\n+\tu8 flow = 0;\n+\n+\trx = 0;\n+\ttx = 0;\n+\tif (force) {\n+\t\trx = 1;\n+\t\ttx = 1;\n+\t}\n+\tif (remote & PORT_REMOTE_SYM_PAUSE) {\n+\t\tif (local & PORT_AUTO_NEG_SYM_PAUSE) {\n+\t\t\trx = 1;\n+\t\t\ttx = 1;\n+\t\t}\n+\t}\n+\tif (rx)\n+\t\tflow |= 0x01;\n+\tif (tx)\n+\t\tflow |= 0x02;\n+\treturn flow;\n+}\n+\n+static void port_get_link_speed(struct ksz_device *dev, int port)\n+{\n+\tstruct ksz_port *p = &dev->ports[port];\n+\tu8 data;\n+\tu8 link;\n+\tu8 local;\n+\tu8 remote;\n+\tu8 status;\n+\n+\tksz_pread8(dev, port, P_LOCAL_CTRL, &local);\n+\tksz_pread8(dev, port, P_REMOTE_STATUS, &remote);\n+\tksz_pread8(dev, port, P_SPEED_STATUS, &status);\n+\tksz_pread8(dev, port, P_LINK_STATUS, &data);\n+\n+\t/**\n+\t * The partner capability register is updated but the\n+\t * auto-negotiation is not completed yet.\n+\t */\n+\tlink = data & (PORT_AUTO_NEG_COMPLETE | PORT_STAT_LINK_GOOD);\n+\tlink |= status & (PORT_STAT_SPEED_100MBIT | PORT_STAT_FULL_DUPLEX);\n+\n+\tif (local == p->advertised && link == p->link)\n+\t\treturn;\n+\n+\tif (data & PORT_STAT_LINK_GOOD) {\n+\t\tp->speed = SPEED_10;\n+\t\tif (status & PORT_STAT_SPEED_100MBIT)\n+\t\t\tp->speed = SPEED_100;\n+\t\tp->duplex = 1;\n+\t\tif (status & PORT_STAT_FULL_DUPLEX)\n+\t\t\tp->duplex = 2;\n+\t\tksz_port_cfg(dev, port, P_STP_CTRL, PORT_BACK_PRESSURE,\n+\t\t\t     p->duplex == 1);\n+\t\tp->flow_ctrl = ksz_determine_flow_ctrl(local, remote,\n+\t\t\t\t\t\t       p->force);\n+\t\tif (status & PORT_RX_FLOW_CTRL)\n+\t\t\tp->flow_ctrl |= 0x10;\n+\t\tif (status & PORT_TX_FLOW_CTRL)\n+\t\t\tp->flow_ctrl |= 0x20;\n+\t\tp->link_up = 1;\n+\t\tdev->live_ports |= (1 << port) & dev->on_ports;\n+\t} else if (p->link_up) {\n+\t\tp->link_up = 0;\n+\t\tp->link_down = 1;\n+\t\tdev->live_ports &= ~(1 << port);\n+\t}\n+\tp->advertised = local;\n+\tp->partner = remote;\n+\tp->link = link;\n+}\n+\n+static void port_set_link_speed(struct ksz_device *dev, int port, int speed,\n+\t\t\t\tint duplex)\n+{\n+\tstruct ksz_port *p = &dev->ports[port];\n+\tu8 adv;\n+\tu8 cfg;\n+\tu8 data;\n+\tu8 local;\n+\tu8 status;\n+\n+\tif (p->fiber)\n+\t\treturn;\n+\n+\tksz_pread8(dev, port, P_LOCAL_CTRL, &local);\n+\tksz_pread8(dev, port, P_LINK_STATUS, &status);\n+\tksz_pread8(dev, port, P_FORCE_CTRL, &data);\n+\n+\tadv = 0;\n+\tcfg = 0;\n+\tif (status & PORT_STAT_LINK_GOOD) {\n+\t\tcfg = data;\n+\t\tadv = local;\n+\t}\n+\n+\tdata &= ~PORT_AUTO_NEG_DISABLE;\n+\tlocal = ksz_advertised_flow_ctrl(PHY_FLOW_CTRL, local);\n+\n+\tlocal |= PORT_AUTO_NEG_100BTX_FD | PORT_AUTO_NEG_100BTX |\n+\t\t PORT_AUTO_NEG_10BT_FD | PORT_AUTO_NEG_10BT;\n+\n+\tif (speed || duplex) {\n+\t\tif (speed == SPEED_10)\n+\t\t\tlocal &= ~(PORT_AUTO_NEG_100BTX_FD |\n+\t\t\t\t   PORT_AUTO_NEG_100BTX);\n+\t\telse if (speed == SPEED_100)\n+\t\t\tlocal &= ~(PORT_AUTO_NEG_10BT_FD |\n+\t\t\t\t   PORT_AUTO_NEG_10BT);\n+\t\tif (duplex == 1)\n+\t\t\tlocal &= ~(PORT_AUTO_NEG_100BTX_FD |\n+\t\t\t\t   PORT_AUTO_NEG_10BT_FD);\n+\t\telse if (duplex == 2)\n+\t\t\tlocal &= ~(PORT_AUTO_NEG_100BTX |\n+\t\t\t\t   PORT_AUTO_NEG_10BT);\n+\t}\n+\tif (data != cfg || local != adv) {\n+\t\tksz_pwrite8(dev, port, P_FORCE_CTRL, data);\n+\t\tksz_pwrite8(dev, port, P_LOCAL_CTRL, local);\n+\t\tksz_pread8(dev, port, P_NEG_RESTART_CTRL, &data);\n+\t\tdata |= PORT_AUTO_NEG_RESTART;\n+\t\tksz_pwrite8(dev, port, P_NEG_RESTART_CTRL, data);\n+\n+\t\t/* Link is going down. */\n+\t\tif (p->link_up)\n+\t\t\tp->link_down = 1;\n+\t\tp->link_up = 0;\n+\t}\n+\tp->force = 0;\n+}\n+\n+static void port_force_link_speed(struct ksz_device *dev, int port, int speed,\n+\t\t\t\t  int duplex)\n+{\n+\tstruct ksz_port *p = &dev->ports[port];\n+\tu8 data;\n+\n+\tksz_pread8(dev, port, P_FORCE_CTRL, &data);\n+\tdata |= PORT_AUTO_NEG_DISABLE;\n+\tif (speed == SPEED_10)\n+\t\tdata &= ~PORT_FORCE_100_MBIT;\n+\telse\n+\t\tdata |= PORT_FORCE_100_MBIT;\n+\tif (duplex == 1)\n+\t\tdata &= ~PORT_FORCE_FULL_DUPLEX;\n+\telse\n+\t\tdata |= PORT_FORCE_FULL_DUPLEX;\n+\tksz_pwrite8(dev, port, P_FORCE_CTRL, data);\n+\tp->force = 1;\n+}\n+\n+static void ksz_mib_read_work(struct work_struct *work)\n+{\n+\tstruct ksz_device *dev =\n+\t\tcontainer_of(work, struct ksz_device, mib_read);\n+\tstruct ksz_port *p;\n+\tstruct ksz_port_mib *mib;\n+\tstruct ksz_port_mib_info *info;\n+\tint i;\n+\tunsigned long next_jiffies;\n+\n+\t/**\n+\t * Want to read MIB counters from all ports every second and spread\n+\t * the register reading around so that this very low priority\n+\t * operation does not affect critical register access like PTP or\n+\t * interrupt.  In fact the MIB counter reading may stop when an\n+\t * interrupt is triggered.\n+\t */\n+\tnext_jiffies = jiffies + dev->MIB_READ_INTERVAL * dev->mib_port_cnt;\n+\tfor (i = 0; i < dev->mib_port_cnt; i++) {\n+\t\tp = &dev->ports[i];\n+\t\tmib = &p->mib;\n+\t\tif (mib->cnt_ptr || 1 == mib->ctrl) {\n+\t\t\tif (port_r_cnt(dev, i))\n+\t\t\t\tbreak;\n+\t\t\tmib->ctrl = 0;\n+\n+\t\t\tif (mib->cnt_ptr == 0)\n+\t\t\t\tmib->ctrl = 2;\n+\t\t} else if (time_is_before_eq_jiffies(mib->time) &&\n+\t\t\t   (dev->live_ports & (1 << i))) {\n+\t\t\tmib->ctrl = 1;\n+\t\t\tmib->time = next_jiffies;\n+\t\t\tnext_jiffies += dev->MIB_READ_INTERVAL;\n+\t\t} else if (p->link_down) {\n+\t\t\tint j;\n+\n+\t\t\tspin_lock(&dev->mib_read_lock);\n+\t\t\tfor (j = 0; j < TOTAL_SWITCH_COUNTER_NUM; j++) {\n+\t\t\t\tinfo = &mib->info[j];\n+\t\t\t\tinfo->read_cnt += info->read_max;\n+\t\t\t}\n+\t\t\tspin_unlock(&dev->mib_read_lock);\n+\t\t\tp->link_down = 0;\n+\n+\t\t\t/* Read counters one last time after link is lost. */\n+\t\t\tmib->ctrl = 1;\n+\t\t}\n+\t}\n+\tdo {\n+\t\tu8 intr_status;\n+\n+\t\tksz_read8(dev, REG_INT_STATUS, &intr_status);\n+\t\tintr_status &= ~INT_PME;\n+\t\tif (!intr_status)\n+\t\t\tbreak;\n+\t\tksz_write8(dev, REG_INT_STATUS, intr_status);\n+\t\tfor (i = 0; i < dev->port_cnt; i++)\n+\t\t\tport_get_link_speed(dev, i);\n+\t} while (0);\n+}\n+\n+static void mib_monitor(unsigned long ptr)\n+{\n+\tstruct ksz_device *dev = (struct ksz_device *)ptr;\n+\n+\tmod_timer(&dev->mib_read_timer, jiffies + dev->MIB_READ_INTERVAL);\n+\tschedule_work(&dev->mib_read);\n+}\n+\n+static void port_setup(struct ksz_device *dev, int port, bool cpu_port)\n+{\n+\tu8 data8;\n+\tu8 member;\n+\tstruct ksz_port *p = &dev->ports[port];\n+\n+\t/* enable broadcast storm limit */\n+\tksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);\n+\n+\tport_set_prio_queue(dev, port, 4);\n+\n+\t/* disable DiffServ priority */\n+\tksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_ENABLE, false);\n+\n+\t/* replace priority */\n+\tksz_port_cfg(dev, port, P_802_1P_CTRL, PORT_802_1P_REMAPPING, false);\n+\n+\t/* enable 802.1p priority */\n+\tksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_ENABLE, true);\n+\n+\tif (cpu_port) {\n+\t\t/* Configure MII interface for proper network communication. */\n+\t\tksz_read8(dev, REG_PORT_5_CTRL_6, &data8);\n+\t\tdata8 &= ~PORT_INTERFACE_TYPE;\n+\t\tdata8 &= ~PORT_GMII_1GPS_MODE;\n+\t\tswitch (dev->interface) {\n+\t\tcase PHY_INTERFACE_MODE_MII:\n+\t\t\tbreak;\n+\t\tcase PHY_INTERFACE_MODE_RMII:\n+\t\t\tdata8 |= PORT_INTERFACE_RMII;\n+\t\t\tbreak;\n+\t\tcase PHY_INTERFACE_MODE_GMII:\n+\t\t\tdata8 |= PORT_GMII_1GPS_MODE;\n+\t\t\tdata8 |= PORT_INTERFACE_GMII;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tdata8 &= ~PORT_RGMII_ID_IN_ENABLE;\n+\t\t\tdata8 &= ~PORT_RGMII_ID_OUT_ENABLE;\n+\t\t\tif (dev->interface == PHY_INTERFACE_MODE_RGMII_ID ||\n+\t\t\t    dev->interface == PHY_INTERFACE_MODE_RGMII_RXID)\n+\t\t\t\tdata8 |= PORT_RGMII_ID_IN_ENABLE;\n+\t\t\tif (dev->interface == PHY_INTERFACE_MODE_RGMII_ID ||\n+\t\t\t    dev->interface == PHY_INTERFACE_MODE_RGMII_TXID)\n+\t\t\t\tdata8 |= PORT_RGMII_ID_OUT_ENABLE;\n+\t\t\tdata8 |= PORT_GMII_1GPS_MODE;\n+\t\t\tdata8 |= PORT_INTERFACE_RGMII;\n+\t\t\tbreak;\n+\t\t}\n+\t\tksz_write8(dev, REG_PORT_5_CTRL_6, data8);\n+\n+\t\tmember = dev->PORT_MASK;\n+\t\tdev->on_ports = dev->HOST_MASK;\n+\t\tdev->live_ports = dev->HOST_MASK;\n+\t} else {\n+\t\tmember = dev->HOST_MASK | p->vid_member;\n+\t\tdev->on_ports |= (1 << port);\n+\n+\t\t/* Link was detected before port is enabled. */\n+\t\tif (p->link_up)\n+\t\t\tdev->live_ports |= (1 << port);\n+\t}\n+\tksz_cfg_port_member(dev, port, member);\n+}\n+\n+static void ksz_config_cpu_port(struct dsa_switch *ds)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\tstruct ksz_port *p;\n+\tint i;\n+\tu8 remote;\n+\n+\tds->num_ports = dev->port_cnt + 1;\n+\n+\t/* Switch marks the maximum frame with extra byte as oversize. */\n+\tksz_cfg(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, true);\n+\tksz_cfg(dev, S_TAIL_TAG_CTRL, SW_TAIL_TAG_ENABLE, true);\n+\n+\tp = &dev->ports[dev->cpu_port];\n+\tp->vid_member = dev->PORT_MASK;\n+\tp->on = 1;\n+\n+\tport_setup(dev, dev->cpu_port, true);\n+\tdev->member = dev->HOST_MASK;\n+\n+\tfor (i = 0; i < SWITCH_PORT_NUM; i++) {\n+\t\tp = &dev->ports[i];\n+\n+\t\t/**\n+\t\t * Initialize to non-zero so that ksz_cfg_port_member() will\n+\t\t * be called.\n+\t\t */\n+\t\tp->vid_member = (1 << i);\n+\t\tp->member = dev->PORT_MASK;\n+\t\tksz_port_stp_state_set(ds, i, BR_STATE_DISABLED);\n+\t\tif (i == dev->port_cnt)\n+\t\t\tbreak;\n+\t\tp->on = 1;\n+\t}\n+\tfor (i = 0; i < dev->port_cnt; i++) {\n+\t\tp = &dev->ports[i];\n+\t\tif (!p->on)\n+\t\t\tcontinue;\n+\t\tksz_pread8(dev, i, P_REMOTE_STATUS, &remote);\n+\t\tif (remote & PORT_FIBER_MODE)\n+\t\t\tp->fiber = 1;\n+\t\tport_get_link_speed(dev, i);\n+\t\tif (p->fiber) {\n+\t\t\tksz_port_cfg(dev, i, P_STP_CTRL, PORT_FORCE_FLOW_CTRL,\n+\t\t\t\t     true);\n+\t\t\tport_force_link_speed(dev, i, 100, 2);\n+\t\t} else {\n+\t\t\tu8 data;\n+\n+\t\t\tksz_port_cfg(dev, i, P_STP_CTRL, PORT_FORCE_FLOW_CTRL,\n+\t\t\t\t     false);\n+\t\t\tksz_pread8(dev, i, P_FORCE_CTRL, &data);\n+\t\t\tdata &= ~PORT_FORCE_FULL_DUPLEX;\n+\t\t\tksz_pwrite8(dev, i, P_FORCE_CTRL, data);\n+\t\t\tport_set_link_speed(dev, i, 0, 0);\n+\t\t}\n+\t}\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+\t/**\n+\t * port_stp_state_set() will be called after to enable the port so\n+\t * there is no need to do anything.\n+\t */\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+\tdev->on_ports &= ~(1 << port);\n+\tdev->live_ports &= ~(1 << port);\n+\n+\t/**\n+\t * port_stp_state_set() will be called after to disable the port so\n+\t * there is no need to do anything.\n+\t */\n+}\n+\n+static int ksz_set_addr(struct dsa_switch *ds, u8 *addr)\n+{\n+\tstruct ksz_device *dev = ds->priv;\n+\n+\tmutex_lock(&dev->reg_mutex);\n+\tdev->ops->set(dev, REG_SW_MAC_ADDR_0, addr, ETH_ALEN);\n+\tmutex_unlock(&dev->reg_mutex);\n+\n+\treturn 0;\n+}\n+\n+static int ksz_setup(struct dsa_switch *ds)\n+{\n+\tu8 data8;\n+\tu16 data16;\n+\tu32 value;\n+\tint i;\n+\tstruct alu_struct alu;\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+\tspin_lock_init(&dev->mib_read_lock);\n+\tINIT_WORK(&dev->mib_read, ksz_mib_read_work);\n+\tsetup_timer(&dev->mib_read_timer, mib_monitor, (unsigned long)dev);\n+\n+\tksz_cfg(dev, S_REPLACE_VID_CTRL, SW_FLOW_CTRL, true);\n+\n+\t/* Enable automatic fast aging when link changed detected. */\n+\tksz_cfg(dev, S_LINK_AGING_CTRL, SW_LINK_AUTO_AGING, true);\n+\n+\tksz_read8(dev, REG_SW_CTRL_1, &data8);\n+\n+\t/* Enable aggressive back off algorithm in half duplex mode. */\n+\tdata8 |= SW_AGGR_BACKOFF;\n+\tksz_write8(dev, REG_SW_CTRL_1, data8);\n+\n+\tksz_read8(dev, REG_SW_CTRL_2, &data8);\n+\n+\t/* Make sure unicast VLAN boundary is set as default. */\n+\tdata8 |= UNICAST_VLAN_BOUNDARY;\n+\n+\t/* Enable no excessive collision drop. */\n+\tdata8 |= NO_EXC_COLLISION_DROP;\n+\tksz_write8(dev, REG_SW_CTRL_2, data8);\n+\n+\tksz_config_cpu_port(ds);\n+\n+\tksz_cfg(dev, REG_SW_CTRL_2, MULTICAST_STORM_DISABLE, true);\n+\n+\tksz_cfg(dev, S_REPLACE_VID_CTRL, SW_REPLACE_VID, false);\n+\n+\tksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);\n+\n+\t/* set broadcast storm protection 10% rate */\n+\tdata8 = BROADCAST_STORM_PROT_RATE;\n+\tvalue = ((u32)BROADCAST_STORM_VALUE * data8) / 100;\n+\tif (value > BROADCAST_STORM_RATE)\n+\t\tvalue = BROADCAST_STORM_RATE;\n+\tksz_read16(dev, S_REPLACE_VID_CTRL, &data16);\n+\tdata16 &= ~BROADCAST_STORM_RATE;\n+\tdata16 |= value;\n+\tksz_write16(dev, S_REPLACE_VID_CTRL, data16);\n+\n+\tfor (i = 0; i < VLAN_TABLE_ENTRIES; i++)\n+\t\tksz_r_vlan_entries(dev, i);\n+\n+\tfor (i = 0; i < dev->mib_port_cnt; i++)\n+\t\tport_init_cnt(dev, i);\n+\n+\t/* Setup STP address for STP operation. */\n+\tmemset(&alu, 0, sizeof(alu));\n+\tmemcpy(alu.mac, STP_MULTICAST_ADDR, ETH_ALEN);\n+\talu.is_static = true;\n+\talu.is_override = true;\n+\talu.port_forward = dev->HOST_MASK;\n+\n+\tmutex_lock(&dev->alu_mutex);\n+\tksz_w_sta_mac_table(dev, 0, &alu);\n+\tmutex_unlock(&dev->alu_mutex);\n+\n+\t/* Start the timer 2 seconds later. */\n+\tdev->mib_read_timer.expires = jiffies + msecs_to_jiffies(2000);\n+\tadd_timer(&dev->mib_read_timer);\n+\n+\treturn 0;\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.set_addr\t\t= ksz_set_addr,\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_bridge_join\t= ksz_port_bridge_join,\n+\t.port_bridge_leave\t= ksz_port_bridge_leave,\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_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+/* Register access function can be disabled if desired. */\n+#if 1\n+#define KSZ_REGS_SIZE\t\t0x100\n+#endif\n+\n+#ifdef KSZ_REGS_SIZE\n+static ssize_t ksz_registers_read(struct file *filp, struct kobject *kobj,\n+\t\t\t\t  struct bin_attribute *bin_attr, char *buf,\n+\t\t\t\t  loff_t off, size_t count)\n+{\n+\tsize_t i;\n+\tu8 reg;\n+\tstruct device *dev;\n+\tstruct ksz_device *swdev;\n+\n+\tif (unlikely(off >= KSZ_REGS_SIZE))\n+\t\treturn 0;\n+\n+\tif ((off + count) >= KSZ_REGS_SIZE)\n+\t\tcount = KSZ_REGS_SIZE - off;\n+\n+\tif (unlikely(!count))\n+\t\treturn count;\n+\n+\tdev = container_of(kobj, struct device, kobj);\n+\tswdev = dev_get_drvdata(dev);\n+\n+\treg = off;\n+\tmutex_lock(&swdev->reg_mutex);\n+\ti = swdev->ops->get(swdev, reg, buf, count);\n+\tmutex_unlock(&swdev->reg_mutex);\n+\treturn i;\n+}\n+\n+static ssize_t ksz_registers_write(struct file *filp, struct kobject *kobj,\n+\t\t\t\t   struct bin_attribute *bin_attr, char *buf,\n+\t\t\t\t   loff_t off, size_t count)\n+{\n+\tsize_t i;\n+\tu8 reg;\n+\tstruct device *dev;\n+\tstruct ksz_device *swdev;\n+\n+\tif (unlikely(off >= KSZ_REGS_SIZE))\n+\t\treturn -EFBIG;\n+\n+\tif ((off + count) >= KSZ_REGS_SIZE)\n+\t\tcount = KSZ_REGS_SIZE - off;\n+\n+\tif (unlikely(!count))\n+\t\treturn count;\n+\n+\tdev = container_of(kobj, struct device, kobj);\n+\tswdev = dev_get_drvdata(dev);\n+\n+\treg = off;\n+\tmutex_lock(&swdev->reg_mutex);\n+\ti = swdev->ops->set(swdev, reg, buf, count);\n+\tmutex_unlock(&swdev->reg_mutex);\n+\treturn i;\n+}\n+\n+static struct bin_attribute ksz_registers_attr = {\n+\t.attr = {\n+\t\t.name\t= \"registers\",\n+\t\t.mode\t= 00600,\n+\t},\n+\t.size\t= KSZ_REGS_SIZE,\n+\t.read\t= ksz_registers_read,\n+\t.write\t= ksz_registers_write,\n+};\n+#endif\n+\n+static u32 ksz_get_port_addr(int port, int offset)\n+{\n+\treturn PORT_CTRL_ADDR(port, offset);\n+}\n+\n+/**\n+ * This information needs to be communicated to the MAC driver so that it can\n+ * receive the frames correctly from the switch as some MAC drivers adhere to\n+ * receive exactly 1518 bytes.\n+ */\n+static int ksz_get_rx_len(struct ksz_device *dev)\n+{\n+\treturn 1;\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+{\n+\treturn 1;\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+\tu8 *trailer;\n+\tint len = 1;\n+\n+\tport = 1 << port;\n+\n+\t/* Need override as the port may be transmit disabled. */\n+\tif (!memcmp(skb->data, STP_MULTICAST_ADDR, ETH_ALEN))\n+\t\tport |= TAIL_TAG_OVERRIDE;\n+\ttrailer = skb_put(skb, len);\n+\ttrailer[0] = (u8)port;\n+}\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 = 1;\n+\n+\ttrailer = skb_tail_pointer(skb) - len;\n+\t*port = *trailer;\n+\tif (*port >= 0 && *port < SWITCH_PORT_NUM) {\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+{\n+\tu16 id16;\n+\tu8 id1;\n+\tu8 id2;\n+\tint ret;\n+\n+\t/* read chip id */\n+\tret = ksz_read16(dev, REG_CHIP_ID0, &id16);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tid1 = id16 >> 8;\n+\tid2 = id16 & SW_CHIP_ID_M;\n+\tif (id1 != FAMILY_ID ||\n+\t    (id2 != CHIP_ID_94 && id2 != CHIP_ID_95))\n+\t\treturn -ENODEV;\n+\n+\tdev->mib_port_cnt = TOTAL_PORT_NUM;\n+\tdev->port_cnt = SWITCH_PORT_NUM;\n+\n+\tif (id2 == CHIP_ID_95) {\n+\t\tu8 val;\n+\n+\t\tid2 = 0x95;\n+\t\tksz_read8(dev, REG_PORT_1_STATUS_0, &val);\n+\t\tif (val & PORT_FIBER_MODE)\n+\t\t\tid2 = 0x65;\n+\t} else if (id2 == CHIP_ID_94) {\n+\t\tdev->port_cnt--;\n+\t\tdev->last_port = dev->port_cnt;\n+\t\tdev->features |= USE_FEWER_PORTS;\n+\t\tid2 = 0x94;\n+\t}\n+\tid16 &= ~0xff;\n+\tid16 |= id2;\n+\tdev->chip_id = id16;\n+\n+\tdev->cpu_port = dev->mib_port_cnt - 1;\n+\tdev->HOST_MASK = (1 << dev->cpu_port);\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+\tu16 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 = 0x8795,\n+\t\t.dev_name = \"KSZ8795\",\n+\t\t.num_vlans = 4096,\n+\t\t.num_alus = 0,\n+\t\t.num_statics = 8,\n+\t\t.cpu_ports = 0x10,\t/* can be configured as cpu port */\n+\t\t.port_cnt = 4,\t\t/* total physical port count */\n+\t},\n+\t{\n+\t\t.chip_id = 0x8765,\n+\t\t.dev_name = \"KSZ8765\",\n+\t\t.num_vlans = 4096,\n+\t\t.num_alus = 0,\n+\t\t.num_statics = 8,\n+\t\t.cpu_ports = 0x10,\t/* can be configured as cpu port */\n+\t\t.port_cnt = 4,\t\t/* total physical port count */\n+\t},\n+};\n+\n+static int ksz_switch_init(struct ksz_device *dev)\n+{\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+#ifdef KSZ_REGS_SIZE\n+\ti = sysfs_create_bin_file(&dev->dev->kobj,\n+\t\t\t\t  &ksz_registers_attr);\n+#endif\n+\n+\treturn 0;\n+}\n+\n+static void ksz_switch_exit(struct ksz_device *dev)\n+{\n+\tif (dev->mib_read_timer.expires)\n+\t\tdel_timer_sync(&dev->mib_read_timer);\n+\n+#ifdef KSZ_REGS_SIZE\n+\tsysfs_remove_bin_file(&dev->dev->kobj, &ksz_registers_attr);\n+#endif\n+}\n+\n+static const struct ksz_dev_ops ksz8795_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 ksz8795_switch_register(struct ksz_device *dev)\n+{\n+\treturn ksz_switch_register(dev, &ksz8795_dev_ops);\n+}\n+EXPORT_SYMBOL(ksz8795_switch_register);\n+\n+MODULE_AUTHOR(\"Tristram Ha <Tristram.Ha@microchip.com>\");\n+MODULE_DESCRIPTION(\"Microchip KSZ8795 Series Switch DSA Driver\");\n+MODULE_LICENSE(\"GPL\");\n",
    "prefixes": [
        "RFC",
        "3/5"
    ]
}