From patchwork Tue Dec 26 11:34:34 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Konstantin Kuzov X-Patchwork-Id: 852960 X-Patchwork-Delegate: blogic@openwrt.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=65.50.211.133; helo=bombadil.infradead.org; envelope-from=lede-dev-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="AscS18B8"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="UPntC2qF"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3z5Yp44HSNz9s9Y for ; Tue, 26 Dec 2017 22:35:56 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:Subject:Message-Id: Date:To:From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=LAZQ4KH3tZWkw2kNu7uSqf/3LOpwgdy1enL03usEs6k=; b=AscS18B8AHVJ9i MXzPZF8jautxfli0U3xe26R3bDP3zX0eO3KvOTv6kqzh7ZGnh+9HP5j4mbyTNmt34smOce0N/rfPe boCmtoa0h3xGvAC0JHdmXzz1qZVwQ5kuryUiSrD+7gjYtUXfesWo1dCKRzYHM+mcV1PvqquxzxC0Z Eufr0BJg419sV1H2V19GO93DP2ctUlR7jUexzIOKZ3PhCist7pMXgLHcWhJhTy+tYqYonOUOejQ8S /bTj/d4cNUR9HcTPggLh93LdKJSCqBV0yNGpqD7PlesMRAk9GFcDm5VZJ3iq3VNqKY60M10Ia7ez1 3nE3vBajjxfs5Zc9XvDQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1eTnWB-0008NV-Gu; Tue, 26 Dec 2017 11:35:35 +0000 Received: from mail-lf0-x243.google.com ([2a00:1450:4010:c07::243]) by bombadil.infradead.org with esmtps (Exim 4.89 #1 (Red Hat Linux)) id 1eTnVv-0007bp-T0 for lede-dev@lists.infradead.org; Tue, 26 Dec 2017 11:35:23 +0000 Received: by mail-lf0-x243.google.com with SMTP id y78so33727471lfd.1 for ; Tue, 26 Dec 2017 03:35:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=7oSYMy+ZewolBuGCPrFEyKw0drhNdV9n/Nyj/7ZAJws=; b=UPntC2qFtKdUmJO1ulML55wU3kbrWpHY8V2F1vsXKqUjjKag3O2Q5maquEjsjAbFYW tOJF2Wd74bGsFcBwlJiq2Iyt5hZOxymUln9/Ku/TLS2OA16millV4JLj6wgZrTGFiRgQ AAm4GVS7Xi59CNKaB26A2kLTB8/vjoxcJT6qm2Vta6BGuUUMyMtLIjMDBm2wNPdfRXiL 3IEi+f7LmYqjeA4sFk5+RRJO/i3lIgmAo4zOmP0d8M66EB8M1mEM90Ms1FAtWngPetPU rYVu3a6wijSPHo809ptg1sup/PNz1XybABME4klACJlitaQGJ1ka0NgW1IIMqcDHAKQR XTog== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=7oSYMy+ZewolBuGCPrFEyKw0drhNdV9n/Nyj/7ZAJws=; b=K+fj3BglNWZ2k+SZ1MvpwGlf/EkBpljIRORhM/3KumP1JVj+NSRhaRaGSVFj9OPaov r4KoWtGxF2XDPGwwpXAvs9HA3UZUIYlJISgDQHOo99HLLYJhe/vc5rn0ev5pf7J+FUrU YMtjNyTf/35L6ePDAHRTSPErSDWnvdCYtZjUdSQMn+DxABSh+/xKAJlrIYEV94VAdZpY nkTrwDGEQk3yTkgrntFwr+q4ySPvPD4aBG34iGJxKAqvL6gzhI4+Tufq7lJakNDwEZrY 6WpWcs9KrOYK0ya8LlEU8/fsAKYDNOmxWEOfUE7dvYpL0ua1V5OypEO+J9vkPNV5yPbz +vzQ== X-Gm-Message-State: AKGB3mJqD9Hgk5pfTXX2ogsSPaIeRcxvo+srl7YU6ErJbv6n2ty7Zf5z vnH1996CJioPZmhzGdEHZecf0A== X-Google-Smtp-Source: ACJfBouJWfTlhJ/OgPDzQKI8/NrfwEMl1HXjQFVliMSTxtFkQq2+VkV4iGg1mXBcUe1gSIhscdpabw== X-Received: by 10.46.83.79 with SMTP id t15mr15058051ljd.135.1514288106731; Tue, 26 Dec 2017 03:35:06 -0800 (PST) Received: from work.g0x.ru ([37.44.44.156]) by smtp.gmail.com with ESMTPSA id 72sm5975624ljz.31.2017.12.26.03.35.05 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 26 Dec 2017 03:35:06 -0800 (PST) From: Konstantin Kuzov To: lede-dev@lists.infradead.org Date: Tue, 26 Dec 2017 14:34:34 +0300 Message-Id: <20171226113435.19263-1-master.nosferatu@gmail.com> X-Mailer: git-send-email 2.14.1 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20171226_033520_047402_325921A1 X-CRM114-Status: GOOD ( 15.81 ) X-Spam-Score: -2.0 (--) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-2.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2a00:1450:4010:c07:0:0:0:243 listed in] [list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (master.nosferatu[at]gmail.com) -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature Subject: [LEDE-DEV] [PATCH 1/2] net: add realtek 8309g switch driver X-BeenThere: lede-dev@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Konstantin Kuzov MIME-Version: 1.0 Sender: "Lede-dev" Errors-To: lede-dev-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org I didn't find a proper way to probe chip id. So we are currently reading switch mac address for identification. Also there are a room for optimization of reg read/write count. But apart from that switch driver is fully functional. Signed-off-by: Konstantin Kuzov --- target/linux/ar71xx/config-4.4 | 1 + target/linux/ar71xx/config-4.9 | 1 + target/linux/generic/config-4.14 | 1 + target/linux/generic/config-4.4 | 1 + target/linux/generic/config-4.9 | 1 + .../linux/generic/files/drivers/net/phy/rtl8309g.c | 1047 ++++++++++++++++++++ .../hack-4.14/730-phy_rtl8309g-switch-driver.patch | 38 + .../hack-4.9/730-phy_rtl8309g-switch-driver.patch | 38 + 8 files changed, 1128 insertions(+) create mode 100644 target/linux/generic/files/drivers/net/phy/rtl8309g.c create mode 100644 target/linux/generic/hack-4.14/730-phy_rtl8309g-switch-driver.patch create mode 100644 target/linux/generic/hack-4.9/730-phy_rtl8309g-switch-driver.patch diff --git a/target/linux/ar71xx/config-4.4 b/target/linux/ar71xx/config-4.4 index 3ba3853720..d8684dcc97 100644 --- a/target/linux/ar71xx/config-4.4 +++ b/target/linux/ar71xx/config-4.4 @@ -439,6 +439,7 @@ CONFIG_PHYLIB=y CONFIG_RATIONAL=y # CONFIG_RCU_STALL_COMMON is not set CONFIG_RTL8306_PHY=y +CONFIG_RTL8309G_PHY=y CONFIG_RTL8366RB_PHY=y CONFIG_RTL8366S_PHY=y CONFIG_RTL8366_SMI=y diff --git a/target/linux/ar71xx/config-4.9 b/target/linux/ar71xx/config-4.9 index 1d47246bf3..c4939d4312 100644 --- a/target/linux/ar71xx/config-4.9 +++ b/target/linux/ar71xx/config-4.9 @@ -444,6 +444,7 @@ CONFIG_PHYLIB=y CONFIG_RATIONAL=y # CONFIG_RCU_STALL_COMMON is not set CONFIG_RTL8306_PHY=y +CONFIG_RTL8309G_PHY=y CONFIG_RTL8366RB_PHY=y CONFIG_RTL8366S_PHY=y CONFIG_RTL8366_SMI=y diff --git a/target/linux/generic/config-4.14 b/target/linux/generic/config-4.14 index 546d689f99..d300e1e92b 100644 --- a/target/linux/generic/config-4.14 +++ b/target/linux/generic/config-4.14 @@ -3812,6 +3812,7 @@ CONFIG_RTC_SYSTOHC_DEVICE="rtc0" # CONFIG_RTL8192E is not set # CONFIG_RTL8192U is not set # CONFIG_RTL8306_PHY is not set +# CONFIG_RTL8309G_PHY is not set # CONFIG_RTL8366RB_PHY is not set # CONFIG_RTL8366S_PHY is not set # CONFIG_RTL8366_SMI is not set diff --git a/target/linux/generic/config-4.4 b/target/linux/generic/config-4.4 index 23ed22d4c3..8cf1a93587 100644 --- a/target/linux/generic/config-4.4 +++ b/target/linux/generic/config-4.4 @@ -3282,6 +3282,7 @@ CONFIG_RTC_SYSTOHC_DEVICE="rtc0" # CONFIG_RTL8192E is not set # CONFIG_RTL8192U is not set # CONFIG_RTL8306_PHY is not set +# CONFIG_RTL8309G_PHY is not set # CONFIG_RTL8366RB_PHY is not set # CONFIG_RTL8366S_PHY is not set # CONFIG_RTL8366_SMI is not set diff --git a/target/linux/generic/config-4.9 b/target/linux/generic/config-4.9 index 998d60297d..92f59bac45 100644 --- a/target/linux/generic/config-4.9 +++ b/target/linux/generic/config-4.9 @@ -3619,6 +3619,7 @@ CONFIG_RTC_SYSTOHC_DEVICE="rtc0" # CONFIG_RTL8192E is not set # CONFIG_RTL8192U is not set # CONFIG_RTL8306_PHY is not set +# CONFIG_RTL8309G_PHY is not set # CONFIG_RTL8366RB_PHY is not set # CONFIG_RTL8366S_PHY is not set # CONFIG_RTL8366_SMI is not set diff --git a/target/linux/generic/files/drivers/net/phy/rtl8309g.c b/target/linux/generic/files/drivers/net/phy/rtl8309g.c new file mode 100644 index 0000000000..079d00ad86 --- /dev/null +++ b/target/linux/generic/files/drivers/net/phy/rtl8309g.c @@ -0,0 +1,1047 @@ +/* + * rtl8309g.c: RTL8309G switch driver + * + * Copyright (C) 2017 Konstantin Kuzov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTL8309G_MAGIC 0x8309 +#define RTL8309G_NAME "RTL8309G" +#define RTL8309G_NUM_VLANS 9 +#define RTL8309G_NUM_PORTS 9 +#define RTL8309G_PORT_CPU 8 + +struct rtl8309g_reg { + uint8_t phy; + uint8_t reg; + uint8_t bits; + uint8_t shift; +}; + +#define RTL8309G_PHY_REGOFS(name) \ + (RTL8309G_PHY_1_PORT_##name - RTL8309G_PHY_0_PORT_##name) + +#define RTL8309G_PHY_REG(id, reg) \ + (RTL8309G_PHY_0_PORT_##reg + (id * RTL8309G_PHY_REGOFS(reg))) + +enum rtl8309g_regidx { +#define RTL8309G_PORT_ENUM(id) \ + RTL8309G_PHY_##id##_PORT_CTRL_RESET, \ + RTL8309G_PHY_##id##_PORT_CTRL_LOOPBACK, \ + RTL8309G_PHY_##id##_PORT_CTRL_SPEED, \ + RTL8309G_PHY_##id##_PORT_CTRL_ANEG, \ + RTL8309G_PHY_##id##_PORT_CTRL_POWER_DOWN, \ + RTL8309G_PHY_##id##_PORT_CTRL_ISOLATE, \ + RTL8309G_PHY_##id##_PORT_CTRL_ANEG_RESTART, \ + RTL8309G_PHY_##id##_PORT_CTRL_DUPLEX, \ + RTL8309G_PHY_##id##_PORT_STATUS_100BASE_TX_FD, \ + RTL8309G_PHY_##id##_PORT_STATUS_100BASE_TX_HD, \ + RTL8309G_PHY_##id##_PORT_STATUS_10BASE_T_FD, \ + RTL8309G_PHY_##id##_PORT_STATUS_10BASE_T_HD, \ + RTL8309G_PHY_##id##_PORT_STATUS_MF_PREAM_SUPR, \ + RTL8309G_PHY_##id##_PORT_STATUS_ANEG_COMPLETE, \ + RTL8309G_PHY_##id##_PORT_STATUS_REM_FAULT, \ + RTL8309G_PHY_##id##_PORT_STATUS_ANEG_ABILITY, \ + RTL8309G_PHY_##id##_PORT_STATUS_LINK_STATUS, \ + RTL8309G_PHY_##id##_PORT_STATUS_JABBER_DETECT, \ + RTL8309G_PHY_##id##_PORT_ANEG_ADV_REM_FAULT, \ + RTL8309G_PHY_##id##_PORT_ANEG_ADV_FLOW_CTRL, \ + RTL8309G_PHY_##id##_PORT_ANEG_ADV_100BASE_TX_FD, \ + RTL8309G_PHY_##id##_PORT_ANEG_ADV_100BASE_TX_HD, \ + RTL8309G_PHY_##id##_PORT_ANEG_ADV_10BASE_T_FD, \ + RTL8309G_PHY_##id##_PORT_ANEG_ADV_10BASE_T_HD, \ + RTL8309G_PHY_##id##_PORT_CTRL0_NULL_VID_REPLACE, \ + RTL8309G_PHY_##id##_PORT_CTRL0_DISCARD_NON_PVID_PKT, \ + RTL8309G_PHY_##id##_PORT_CTRL0_DISABLE_PRIORITY, \ + RTL8309G_PHY_##id##_PORT_CTRL0_DISABLE_DIFF_PRIORITY, \ + RTL8309G_PHY_##id##_PORT_CTRL0_DISABLE_PORT_BASE_PRIORITY, \ + RTL8309G_PHY_##id##_PORT_CTRL0_VLAN_TAG, \ + RTL8309G_PHY_##id##_PORT_CTRL1_ENABLE_TRANSMISSION, \ + RTL8309G_PHY_##id##_PORT_CTRL1_ENABLE_RECEPTION, \ + RTL8309G_PHY_##id##_PORT_CTRL1_ENABLE_LEARNING, \ + RTL8309G_PHY_##id##_PORT_CTRL2_VLAN_INDEX, \ + RTL8309G_PHY_##id##_PORT_CTRL2_VLAN_MEMBERSHIP, \ + RTL8309G_PHY_##id##_PORT_VLAN_ID + RTL8309G_PORT_ENUM(0), + RTL8309G_PORT_ENUM(1), + RTL8309G_PORT_ENUM(2), + RTL8309G_PORT_ENUM(3), + RTL8309G_PORT_ENUM(4), + RTL8309G_PORT_ENUM(5), + RTL8309G_PORT_ENUM(6), + RTL8309G_PORT_ENUM(7), + RTL8309G_PORT_ENUM(8), + +#define RTL8309G_PORT_ENUM_NC(id) \ + RTL8309G_PHY_##id##_PORT_ANEG_LINK_REM_FAULT, \ + RTL8309G_PHY_##id##_PORT_ANEG_LINK_FLOW_CTRL, \ + RTL8309G_PHY_##id##_PORT_ANEG_LINK_100BASE_TX_FD, \ + RTL8309G_PHY_##id##_PORT_ANEG_LINK_100BASE_TX_HD, \ + RTL8309G_PHY_##id##_PORT_ANEG_LINK_10BASE_T_FD, \ + RTL8309G_PHY_##id##_PORT_ANEG_LINK_10BASE_T_HD, \ + RTL8309G_PHY_##id##_PORT_CTRL0_LOOPBACK, \ + RTL8309G_PHY_##id##_PORT_CTRL1_LOOP_STATUS, \ + RTL8309G_PHY_##id##_PORT_CTRL1_LINK_QUALITY + RTL8309G_PORT_ENUM_NC(0), + RTL8309G_PORT_ENUM_NC(1), + RTL8309G_PORT_ENUM_NC(2), + RTL8309G_PORT_ENUM_NC(3), + RTL8309G_PORT_ENUM_NC(4), + RTL8309G_PORT_ENUM_NC(5), + RTL8309G_PORT_ENUM_NC(6), + RTL8309G_PORT_ENUM_NC(7), + + RTL8309G_GCTRL0_LED_MODE, + RTL8309G_GCTRL0_RESET, + RTL8309G_GCTRL0_DISABLE_VLAN, + RTL8309G_GCTRL0_DISABLE_VLAN_TAG_AWARE, + RTL8309G_GCTRL0_DISABLE_VLAN_INGRESS_FILTER, + RTL8309G_GCTRL0_DISABLE_VLAN_TAG_CONTROL, + RTL8309G_GCTRL0_EEPROM_EXIST, + RTL8309G_GCTRL0_DISABLE_ERROR_FILTER, + RTL8309G_GCTRL0_ENABLE_FLOW_CTRL_TRANSMIT, + RTL8309G_GCTRL0_ENABLE_FLOW_CTRL_RECEIVE, + RTL8309G_GCTRL0_ENABLE_BROADCAST, + RTL8309G_GCTRL0_ENABLE_AGING, + RTL8309G_GCTRL0_ENABLE_FAST_AGING, + RTL8309G_GCTRL0_ENABLE_ISP_MAC_TRANSLATION, + + RTL8309G_GCTRL1_PRIORITY, + RTL8309G_GCTRL1_TRUNKING_PORT, + RTL8309G_GCTRL1_QUEUE_WEIGHT, + RTL8309G_GCTRL1_DISABLE_IP_PRIORITY_A, + RTL8309G_GCTRL1_DISABLE_IP_PRIORITY_B, + + RTL8309G_GCTRL2_ENABLE_DIFF_SCP_A, + RTL8309G_GCTRL2_DIFF_SCP_A, + RTL8309G_GCTRL2_ENABLE_DIFF_SCP_B, + RTL8309G_GCTRL2_DIFF_SCP_B, + + RTL8309G_GCTRL3_ENABLE_DROP_SRAM, + RTL8309G_GCTRL3_TX_IPG_COMPENSATION, + RTL8309G_GCTRL3_DISABLE_LOOP_DETECT, + RTL8309G_GCTRL3_ENABLE_LOOKUP_TBL, + + RTL8309G_IPPRIO_IP_ADDR_A_1, + RTL8309G_IPPRIO_IP_ADDR_A_2, + + RTL8309G_IPPRIO_IP_ADDR_B_1, + RTL8309G_IPPRIO_IP_ADDR_B_2, + + RTL8309G_IPPRIO_IP_MASK_A_1, + RTL8309G_IPPRIO_IP_MASK_A_2, + + RTL8309G_IPPRIO_IP_MASK_B_1, + RTL8309G_IPPRIO_IP_MASK_B_2, + + RTL8309G_SW_MAC_ADDR_1, + RTL8309G_SW_MAC_ADDR_2, + RTL8309G_SW_MAC_ADDR_3, + + RTL8309G_ISP_MAC_ADDR_1, + RTL8309G_ISP_MAC_ADDR_2, + RTL8309G_ISP_MAC_ADDR_3, + + RTL8309G_MII_ENABLE_TRANSMISSION, + RTL8309G_MII_ENABLE_RECEPTION, + RTL8309G_MII_ENABLE_LEARNING, + RTL8309G_MII_DISABLE_PRIORITY, + RTL8309G_MII_DISABLE_DIFF_PRIORITY, + RTL8309G_MII_DISABLE_PORT_PRIORITY, + RTL8309G_MII_VLAN_TAG, + + RTL8309G_WAN_PORT, + RTL8309G_CPU_PORT, + + RTL8309G_IACTRL_COMMAND_EXECUTION, + RTL8309G_IACTRL_OPERATION_CYCLE, + RTL8309G_IACTRL_DATA_1, + RTL8309G_IACTRL_DATA_2, + RTL8309G_IACTRL_DATA_3, + RTL8309G_IACTRL_DATA_4 +}; + +static const struct rtl8309g_reg rtl8309g_regs[] = { +#define RTL8309G_PORT_REG(id) \ + [RTL8309G_PHY_##id##_PORT_CTRL_RESET] = { id, 0, 1, 15 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL_LOOPBACK] = { id, 0, 1, 14 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL_SPEED] = { id, 0, 1, 13 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL_ANEG] = { id, 0, 1, 12 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL_POWER_DOWN] = { id, 0, 1, 11 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL_ISOLATE] = { id, 0, 1, 10 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL_ANEG_RESTART] = { id, 0, 1, 9 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL_DUPLEX] = { id, 0, 1, 8 }, \ + [RTL8309G_PHY_##id##_PORT_STATUS_100BASE_TX_FD] = { id, 1, 1, 14 }, \ + [RTL8309G_PHY_##id##_PORT_STATUS_100BASE_TX_HD] = { id, 1, 1, 13 }, \ + [RTL8309G_PHY_##id##_PORT_STATUS_10BASE_T_FD] = { id, 1, 1, 12 }, \ + [RTL8309G_PHY_##id##_PORT_STATUS_10BASE_T_HD] = { id, 1, 1, 11 }, \ + [RTL8309G_PHY_##id##_PORT_STATUS_MF_PREAM_SUPR] = { id, 1, 1, 6 }, \ + [RTL8309G_PHY_##id##_PORT_STATUS_ANEG_COMPLETE] = { id, 1, 1, 5 }, \ + [RTL8309G_PHY_##id##_PORT_STATUS_REM_FAULT] = { id, 1, 1, 4 }, \ + [RTL8309G_PHY_##id##_PORT_STATUS_ANEG_ABILITY] = { id, 1, 1, 3 }, \ + [RTL8309G_PHY_##id##_PORT_STATUS_LINK_STATUS] = { id, 1, 1, 2 }, \ + [RTL8309G_PHY_##id##_PORT_STATUS_JABBER_DETECT] = { id, 1, 1, 1 }, \ + [RTL8309G_PHY_##id##_PORT_ANEG_ADV_REM_FAULT] = { id, 4, 1, 13 }, \ + [RTL8309G_PHY_##id##_PORT_ANEG_ADV_FLOW_CTRL] = { id, 4, 1, 10 }, \ + [RTL8309G_PHY_##id##_PORT_ANEG_ADV_100BASE_TX_FD] = { id, 4, 1, 8 }, \ + [RTL8309G_PHY_##id##_PORT_ANEG_ADV_100BASE_TX_HD] = { id, 4, 1, 7 }, \ + [RTL8309G_PHY_##id##_PORT_ANEG_ADV_10BASE_T_FD] = { id, 4, 1, 6 }, \ + [RTL8309G_PHY_##id##_PORT_ANEG_ADV_10BASE_T_HD] = { id, 4, 1, 5 } + +#define RTL8309G_PORT_REG_D(id) \ + [RTL8309G_PHY_##id##_PORT_CTRL0_NULL_VID_REPLACE] = { id, 22, 1, 12 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL0_DISCARD_NON_PVID_PKT] = { id, 22, 1, 11 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL0_DISABLE_PRIORITY] = { id, 22, 1, 10 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL0_DISABLE_DIFF_PRIORITY] = { id, 22, 1, 9 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL0_DISABLE_PORT_BASE_PRIORITY] = { id, 22, 1, 8 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL0_VLAN_TAG] = { id, 22, 2, 0 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL1_ENABLE_TRANSMISSION] = { id, 23, 1, 11 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL1_ENABLE_RECEPTION] = { id, 23, 1, 10 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL1_ENABLE_LEARNING] = { id, 23, 1, 9 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL2_VLAN_INDEX] = { id, 24, 4, 12 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL2_VLAN_MEMBERSHIP] = { id, 24, 9, 0 }, \ + [RTL8309G_PHY_##id##_PORT_VLAN_ID] = { id, 25, 12, 0 } + + RTL8309G_PORT_REG(0), + RTL8309G_PORT_REG_D(0), + RTL8309G_PORT_REG(1), + RTL8309G_PORT_REG_D(1), + RTL8309G_PORT_REG(2), + RTL8309G_PORT_REG_D(2), + RTL8309G_PORT_REG(3), + RTL8309G_PORT_REG_D(3), + RTL8309G_PORT_REG(4), + RTL8309G_PORT_REG_D(4), + RTL8309G_PORT_REG(5), + RTL8309G_PORT_REG_D(5), + RTL8309G_PORT_REG(6), + RTL8309G_PORT_REG_D(6), + RTL8309G_PORT_REG(7), + RTL8309G_PORT_REG_D(7), + RTL8309G_PORT_REG(8), + [RTL8309G_PHY_8_PORT_CTRL0_NULL_VID_REPLACE] = { 5, 17, 1, 15 }, + [RTL8309G_PHY_8_PORT_CTRL0_DISCARD_NON_PVID_PKT] = { 5, 17, 1, 14 }, + [RTL8309G_PHY_8_PORT_CTRL0_DISABLE_PRIORITY] = { 5, 16, 1, 11 }, + [RTL8309G_PHY_8_PORT_CTRL0_DISABLE_DIFF_PRIORITY] = { 5, 16, 1, 10 }, + [RTL8309G_PHY_8_PORT_CTRL0_DISABLE_PORT_BASE_PRIORITY] = { 5, 16, 1, 9 }, + [RTL8309G_PHY_8_PORT_CTRL0_VLAN_TAG] = { 5, 16, 2, 0 }, + [RTL8309G_PHY_8_PORT_CTRL1_ENABLE_TRANSMISSION] = { 5, 16, 1, 15 }, + [RTL8309G_PHY_8_PORT_CTRL1_ENABLE_RECEPTION] = { 5, 16, 1, 14 }, + [RTL8309G_PHY_8_PORT_CTRL1_ENABLE_LEARNING] = { 5, 16, 1, 13 }, + [RTL8309G_PHY_8_PORT_CTRL2_VLAN_INDEX] = { 5, 17, 4, 9 }, + [RTL8309G_PHY_8_PORT_CTRL2_VLAN_MEMBERSHIP] = { 5, 17, 9, 0 }, + [RTL8309G_PHY_8_PORT_VLAN_ID] = { 5, 18, 12, 0 }, + +#define RTL8309G_PORT_REG_C(id) \ + [RTL8309G_PHY_##id##_PORT_ANEG_LINK_REM_FAULT] = { id, 5, 1, 13 }, \ + [RTL8309G_PHY_##id##_PORT_ANEG_LINK_FLOW_CTRL] = { id, 5, 1, 10 }, \ + [RTL8309G_PHY_##id##_PORT_ANEG_LINK_100BASE_TX_FD] = { id, 5, 1, 8 }, \ + [RTL8309G_PHY_##id##_PORT_ANEG_LINK_100BASE_TX_HD] = { id, 5, 1, 7 }, \ + [RTL8309G_PHY_##id##_PORT_ANEG_LINK_10BASE_T_FD] = { id, 5, 1, 6 }, \ + [RTL8309G_PHY_##id##_PORT_ANEG_LINK_10BASE_T_HD] = { id, 5, 1, 5 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL0_LOOPBACK] = { id, 22, 1, 13 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL1_LOOP_STATUS] = { id, 23, 1, 8 }, \ + [RTL8309G_PHY_##id##_PORT_CTRL1_LINK_QUALITY] = { id, 23, 4, 4 } + RTL8309G_PORT_REG_C(0), + RTL8309G_PORT_REG_C(1), + RTL8309G_PORT_REG_C(2), + RTL8309G_PORT_REG_C(3), + RTL8309G_PORT_REG_C(4), + RTL8309G_PORT_REG_C(5), + RTL8309G_PORT_REG_C(6), + RTL8309G_PORT_REG_C(7), + + [RTL8309G_GCTRL0_LED_MODE] = { 0, 16, 3, 13 }, + [RTL8309G_GCTRL0_RESET] = { 0, 16, 1, 12 }, + [RTL8309G_GCTRL0_DISABLE_VLAN] = { 0, 16, 1, 11 }, + [RTL8309G_GCTRL0_DISABLE_VLAN_TAG_AWARE] = { 0, 16, 1, 10 }, + [RTL8309G_GCTRL0_DISABLE_VLAN_INGRESS_FILTER] = { 0, 16, 1, 9 }, + [RTL8309G_GCTRL0_DISABLE_VLAN_TAG_CONTROL] = { 0, 16, 1, 8 }, + [RTL8309G_GCTRL0_EEPROM_EXIST] = { 0, 16, 1, 7 }, + [RTL8309G_GCTRL0_DISABLE_ERROR_FILTER] = { 0, 16, 1, 6 }, + [RTL8309G_GCTRL0_ENABLE_FLOW_CTRL_TRANSMIT] = { 0, 16, 1, 5 }, + [RTL8309G_GCTRL0_ENABLE_FLOW_CTRL_RECEIVE] = { 0, 16, 1, 4 }, + [RTL8309G_GCTRL0_ENABLE_BROADCAST] = { 0, 16, 1, 3 }, + [RTL8309G_GCTRL0_ENABLE_AGING] = { 0, 16, 1, 2 }, + [RTL8309G_GCTRL0_ENABLE_FAST_AGING] = { 0, 16, 1, 1 }, + [RTL8309G_GCTRL0_ENABLE_ISP_MAC_TRANSLATION] = { 0, 16, 1, 0 }, + + [RTL8309G_GCTRL1_PRIORITY] = { 0, 17, 3, 13 }, + [RTL8309G_GCTRL1_TRUNKING_PORT] = { 0, 17, 1, 12 }, + [RTL8309G_GCTRL1_QUEUE_WEIGHT] = { 0, 17, 2, 10 }, + [RTL8309G_GCTRL1_DISABLE_IP_PRIORITY_A] = { 0, 17, 1, 9 }, + [RTL8309G_GCTRL1_DISABLE_IP_PRIORITY_B] = { 0, 17, 1, 8 }, + + [RTL8309G_GCTRL2_ENABLE_DIFF_SCP_A] = { 0, 18, 1, 15 }, + [RTL8309G_GCTRL2_DIFF_SCP_A] = { 0, 18, 6, 8 }, + [RTL8309G_GCTRL2_ENABLE_DIFF_SCP_B] = { 0, 18, 1, 7 }, + [RTL8309G_GCTRL2_DIFF_SCP_B] = { 0, 18, 6, 0 }, + + [RTL8309G_GCTRL3_ENABLE_DROP_SRAM] = { 0, 19, 1, 15 }, + [RTL8309G_GCTRL3_TX_IPG_COMPENSATION] = { 0, 19, 1, 13 }, + [RTL8309G_GCTRL3_DISABLE_LOOP_DETECT] = { 0, 19, 1, 12 }, + [RTL8309G_GCTRL3_ENABLE_LOOKUP_TBL] = { 0, 19, 1, 11 }, + + [RTL8309G_IPPRIO_IP_ADDR_A_1] = { 1, 16, 16, 0 }, + [RTL8309G_IPPRIO_IP_ADDR_A_2] = { 1, 17, 16, 0 }, + + [RTL8309G_IPPRIO_IP_ADDR_B_1] = { 1, 18, 16, 0 }, + [RTL8309G_IPPRIO_IP_ADDR_B_2] = { 1, 19, 16, 0 }, + + [RTL8309G_IPPRIO_IP_MASK_A_1] = { 2, 16, 16, 0 }, + [RTL8309G_IPPRIO_IP_MASK_A_2] = { 2, 17, 16, 0 }, + + [RTL8309G_IPPRIO_IP_MASK_B_1] = { 2, 18, 16, 0 }, + [RTL8309G_IPPRIO_IP_MASK_B_2] = { 2, 19, 16, 0 }, + + [RTL8309G_SW_MAC_ADDR_1] = { 3, 16, 16, 0 }, + [RTL8309G_SW_MAC_ADDR_2] = { 3, 17, 16, 0 }, + [RTL8309G_SW_MAC_ADDR_3] = { 3, 18, 16, 0 }, + + [RTL8309G_ISP_MAC_ADDR_1] = { 4, 16, 16, 0 }, + [RTL8309G_ISP_MAC_ADDR_2] = { 4, 17, 16, 0 }, + [RTL8309G_ISP_MAC_ADDR_3] = { 4, 18, 16, 0 }, + + [RTL8309G_MII_ENABLE_TRANSMISSION] = { 5, 16, 1, 15 }, + [RTL8309G_MII_ENABLE_RECEPTION] = { 5, 16, 1, 14 }, + [RTL8309G_MII_ENABLE_LEARNING] = { 5, 16, 1, 13 }, + [RTL8309G_MII_DISABLE_PRIORITY] = { 5, 16, 1, 11 }, + [RTL8309G_MII_DISABLE_DIFF_PRIORITY] = { 5, 16, 1, 10 }, + [RTL8309G_MII_DISABLE_PORT_PRIORITY] = { 5, 16, 1, 9 }, + [RTL8309G_MII_VLAN_TAG] = { 5, 16, 2, 0 }, + + [RTL8309G_WAN_PORT] = { 5, 19, 4, 4 }, + [RTL8309G_CPU_PORT] = { 5, 19, 4, 0 }, + + [RTL8309G_IACTRL_COMMAND_EXECUTION] = { 7, 16, 1, 1 }, + [RTL8309G_IACTRL_OPERATION_CYCLE] = { 7, 16, 1, 0 }, + [RTL8309G_IACTRL_DATA_1] = { 7, 17, 16, 0 }, + [RTL8309G_IACTRL_DATA_2] = { 7, 18, 16, 0 }, + [RTL8309G_IACTRL_DATA_3] = { 7, 19, 16, 0 }, + [RTL8309G_IACTRL_DATA_4] = { 7, 20, 16, 0 } +}; + +#define to_rtl(_dev) container_of(_dev, struct rtl_priv, dev) + +struct rtl_priv { + struct list_head list; + struct switch_dev dev; + int type; + struct mii_bus *bus; + char hwname[16]; + bool fixup; +}; + +static LIST_HEAD(phydevs); + +static uint16_t +poslen_mask(uint8_t pos, uint8_t len) +{ + int i; + uint16_t mask = 0; + + for (i = 0; i < len; i++) + mask+= 1 << (pos + i); + + return mask; +} + +static uint16_t +rtl_read(struct mii_bus *bus, uint8_t phy, uint8_t reg) +{ + return (bus->read(bus, phy, reg)); +} + +static void +rtl_write(struct mii_bus *bus, uint8_t phy, uint8_t reg, uint16_t val) +{ + bus->write(bus, phy, reg, val); +} + +static uint16_t +rtl_read_reg(struct mii_bus *bus, enum rtl8309g_regidx s) +{ + const struct rtl8309g_reg *r; + + BUG_ON(s >= ARRAY_SIZE(rtl8309g_regs)); + + r = &rtl8309g_regs[s]; + + return ((rtl_read(bus, r->phy, r->reg) & poslen_mask(r->shift, r->bits)) >> r->shift); +} + +static void +rtl_write_reg(struct mii_bus *bus, enum rtl8309g_regidx s, uint16_t val) +{ + const struct rtl8309g_reg *r; + uint16_t reg; + uint16_t mask; + + BUG_ON(s >= ARRAY_SIZE(rtl8309g_regs)); + + r = &rtl8309g_regs[s]; + mask = poslen_mask(r->shift, r->bits); + + reg = rtl_read(bus, r->phy, r->reg); + rtl_write(bus, r->phy, r->reg, (reg & ~(mask)) | ((val << r->shift) & mask)); +} + +static int +rtl8309g_probe(struct phy_device *pdev) +{ + struct rtl_priv *priv; + + list_for_each_entry(priv, &phydevs, list) { + /* + * share one rtl_priv instance between virtual phy + * devices on the same bus + */ + if (priv->bus == pdev->mdio.bus) + goto found; + } + + priv = kzalloc(sizeof(struct rtl_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->bus = pdev->mdio.bus; + +found: + pdev->priv = priv; + + return 0; +} + +static int +rtl_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + val->value.i = rtl_read_reg(bus, RTL8309G_GCTRL0_DISABLE_VLAN) == 0 ? 1 : 0; + + return 0; +} + +static int +rtl_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + int en = val->value.i ? 0 : 1; + + rtl_write_reg(bus, RTL8309G_GCTRL0_DISABLE_VLAN_INGRESS_FILTER, en); + rtl_write_reg(bus, RTL8309G_GCTRL0_DISABLE_VLAN_TAG_AWARE, en); + rtl_write_reg(bus, RTL8309G_GCTRL0_DISABLE_VLAN, en); + + return 0; +} + +static int +rtl_get_led_mode(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + val->value.i = rtl_read_reg(bus, RTL8309G_GCTRL0_LED_MODE); + + return 0; +} + +static int +rtl_set_led_mode(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + int en = val->value.i; + + rtl_write_reg(bus, RTL8309G_GCTRL0_LED_MODE, en); + + return 0; +} + +static int +rtl_get_aging(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + int en = 0; + + if (rtl_read_reg(bus, RTL8309G_GCTRL0_ENABLE_AGING)) { + en++; + if (rtl_read_reg(bus, RTL8309G_GCTRL0_ENABLE_FAST_AGING)) + en++; + } + + val->value.i = en; + + return 0; +} + +static int +rtl_set_aging(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + rtl_write_reg(bus, RTL8309G_GCTRL0_ENABLE_AGING, val->value.i > 0 ? 1 : 0); + rtl_write_reg(bus, RTL8309G_GCTRL0_ENABLE_FAST_AGING, val->value.i == 2 ? 1 : 0); + + return 0; +} + +static int +rtl_get_vlan_vid(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + if (val->port_vlan >= RTL8309G_NUM_PORTS) + return -EINVAL; + + val->value.i = rtl_read_reg(bus, RTL8309G_PHY_REG(val->port_vlan, VLAN_ID)); + + return 0; +} + +static int +rtl_set_vlan_vid(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + int en = val->value.i; + + if (val->port_vlan >= RTL8309G_NUM_PORTS) + return -EINVAL; + + rtl_write_reg(bus, RTL8309G_PHY_REG(val->port_vlan, VLAN_ID), en); + + return 0; +} + +static int +rtl_get_traffic(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + if (val->port_vlan >= RTL8309G_NUM_PORTS) + return -EINVAL; + + val->value.i = rtl_read_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL1_ENABLE_RECEPTION)) & + rtl_read_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL1_ENABLE_TRANSMISSION)) + ? 1 : 0; + + return 0; +} + +static int +rtl_set_traffic(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + if (val->port_vlan >= RTL8309G_NUM_PORTS) + return -EINVAL; + + rtl_write_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL1_ENABLE_RECEPTION), val->value.i); + rtl_write_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL1_ENABLE_TRANSMISSION), val->value.i); + + return 0; +} + +static int +rtl_get_learning(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + if (val->port_vlan >= RTL8309G_NUM_PORTS) + return -EINVAL; + + val->value.i = rtl_read_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL1_ENABLE_LEARNING)); + + return 0; +} + +static int +rtl_set_learning(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + if (val->port_vlan >= RTL8309G_NUM_PORTS) + return -EINVAL; + + rtl_write_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL1_ENABLE_LEARNING), val->value.i); + + return 0; +} + +static int +rtl_get_loop_status(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + if (val->port_vlan >= RTL8309G_NUM_PORTS - 1) + return -EINVAL; + + val->value.i = rtl_read_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL1_LOOP_STATUS)); + + return 0; +} + +static int +rtl_get_link_quality(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + if (val->port_vlan >= RTL8309G_NUM_PORTS - 1) + return -EINVAL; + + val->value.i = rtl_read_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL1_LINK_QUALITY)); + + return 0; +} + +static int +rtl_get_port_pvid(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + if (val->port_vlan >= RTL8309G_NUM_PORTS) + return -EINVAL; + + val->value.i = rtl_read_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL2_VLAN_INDEX)); + + return 0; +} + +static int +rtl_set_port_pvid(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + if (val->port_vlan >= RTL8309G_NUM_PORTS) + return -EINVAL; + + rtl_write_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL2_VLAN_INDEX), val->value.i); + + return 0; +} + +static int +rtl_get_vlan_ports(struct switch_dev *dev, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + uint16_t pmask; + int i; + + if (val->port_vlan >= RTL8309G_NUM_PORTS) + return -EINVAL; + + pmask = rtl_read_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL2_VLAN_MEMBERSHIP)); + for (i = 0; i < RTL8309G_NUM_PORTS; i++) { + struct switch_port *port; + + if (!(pmask & (1 << i))) + continue; + + port = &val->value.ports[val->len]; + port->id = i; + + if (rtl_read_reg(bus, RTL8309G_PHY_REG(i, CTRL0_VLAN_TAG)) == 2) + port->flags = (1 << SWITCH_PORT_FLAG_TAGGED); + + val->len++; + } + + return 0; +} + +static int +rtl_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + uint16_t pmask = 0, omask, dmask; + int i; + + if (val->port_vlan >= RTL8309G_NUM_PORTS) + return -EINVAL; + + for (i = 0; i < val->len; i++) { + struct switch_port *port = &val->value.ports[i]; + bool tagged = false; + + pmask|= 1 << port->id; + + if (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) + tagged = true; + + if (!tagged) + rtl_write_reg(bus, RTL8309G_PHY_REG(port->id, CTRL2_VLAN_INDEX), val->port_vlan); + + rtl_write_reg(bus, RTL8309G_PHY_REG(port->id, CTRL0_DISCARD_NON_PVID_PKT), (tagged ? 0 : 1)); + rtl_write_reg(bus, RTL8309G_PHY_REG(port->id, CTRL0_NULL_VID_REPLACE), (tagged ? 1 : 0)); + rtl_write_reg(bus, RTL8309G_PHY_REG(port->id, CTRL0_VLAN_TAG), (tagged ? 2 : 1)); + } + + omask = rtl_read_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL2_VLAN_MEMBERSHIP)); + rtl_write_reg(bus, RTL8309G_PHY_REG(val->port_vlan, CTRL2_VLAN_MEMBERSHIP), pmask); + + if (val->port_vlan == 0) + return 0; + + dmask = rtl_read_reg(bus, RTL8309G_PHY_REG(0, CTRL2_VLAN_MEMBERSHIP)); + dmask&= ~(pmask & ~(1 << dev->cpu_port)); + rtl_write_reg(bus, RTL8309G_PHY_REG(0, CTRL2_VLAN_MEMBERSHIP), dmask); + + omask &= ~pmask; + for (i = 0; i < RTL8309G_NUM_PORTS; i++) { + if (!(omask & (1 << i))) + continue; + + if (i == dev->cpu_port) + continue; + + if (rtl_read_reg(bus, RTL8309G_PHY_REG(i, CTRL2_VLAN_INDEX)) == val->port_vlan) + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL2_VLAN_INDEX), 0); + } + + return 0; +} + +static int +rtl_get_port_link(struct switch_dev *dev, int port, struct switch_port_link *link) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + + if (port >= RTL8309G_NUM_PORTS) + return -EINVAL; + + // TODO: optimize reg reads + link->aneg = rtl_read_reg(bus, RTL8309G_PHY_REG(port, STATUS_ANEG_COMPLETE)); + link->link = rtl_read_reg(bus, RTL8309G_PHY_REG(port, STATUS_LINK_STATUS)); + link->speed = rtl_read_reg(bus, RTL8309G_PHY_REG(port, CTRL_SPEED)) ? SWITCH_PORT_SPEED_100 : SWITCH_PORT_SPEED_10; + link->duplex = rtl_read_reg(bus, RTL8309G_PHY_REG(port, CTRL_DUPLEX)) ? DUPLEX_FULL : DUPLEX_HALF; + if (port != dev->cpu_port && rtl_read_reg(bus, RTL8309G_GCTRL0_ENABLE_FLOW_CTRL_TRANSMIT)) + link->tx_flow = rtl_read_reg(bus, RTL8309G_PHY_REG(port, ANEG_ADV_FLOW_CTRL)); + else + link->tx_flow = 0; + if (port != dev->cpu_port && rtl_read_reg(bus, RTL8309G_GCTRL0_ENABLE_FLOW_CTRL_RECEIVE)) + link->rx_flow = rtl_read_reg(bus, RTL8309G_PHY_REG(port, ANEG_ADV_FLOW_CTRL)); + else + link->rx_flow = 0; + + return 0; +} + +static int +rtl_hw_apply(struct switch_dev *dev) +{ + return 0; +} + +static int +rtl_reset(struct switch_dev *dev) +{ + struct rtl_priv *priv = to_rtl(dev); + struct mii_bus *bus = priv->bus; + int i; + + /* Disable rx/tx on PHYs */ + for (i = 0; i < RTL8309G_NUM_PORTS - 1; i++) { + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL1_ENABLE_TRANSMISSION), 0); + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL1_ENABLE_RECEPTION), 0); + } + + rtl_write_reg(bus, RTL8309G_GCTRL0_DISABLE_VLAN, 1); + rtl_write_reg(bus, RTL8309G_GCTRL0_DISABLE_VLAN_INGRESS_FILTER, 1); + rtl_write_reg(bus, RTL8309G_CPU_PORT, dev->cpu_port); + + /* Reset switch */ + rtl_write_reg(bus, RTL8309G_GCTRL0_RESET, 1); + for (i = 0; i < 10; i++) { + if (rtl_read_reg(bus, RTL8309G_GCTRL0_RESET) == 0) + break; + + msleep(1); + } + + /* Reset PHYs */ + for (i = 1; i < 9; i++) + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL_RESET), 1); + + /* All ports to VLAN 0 with PVID 0 */ + for (i = 0; i < RTL8309G_NUM_PORTS; i++) { + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL2_VLAN_INDEX), 0); + if (i == 0) + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL2_VLAN_MEMBERSHIP), 0x1FF); + else + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL2_VLAN_MEMBERSHIP), 0); + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL0_NULL_VID_REPLACE), 0); + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL0_DISCARD_NON_PVID_PKT), 0); + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL0_VLAN_TAG), 3); + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL_ANEG), 1); + } + + /* Enable rx/tx on PHYs */ + for (i = 0; i < RTL8309G_NUM_PORTS - 1; i++) { + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL1_ENABLE_TRANSMISSION), 1); + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL1_ENABLE_RECEPTION), 1); + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL_ANEG_RESTART), 1); + } + + return 0; +} + +static struct switch_attr rtl_globals[] = { + { + .type = SWITCH_TYPE_INT, + .name = "enable_vlan", + .description = "Enable VLAN mode", + .set = rtl_set_vlan, + .get = rtl_get_vlan, + .max = 1, + }, { + .type = SWITCH_TYPE_INT, + .name = "enable_aging", + .description = "Enable aging and fast aging", + .set = rtl_set_aging, + .get = rtl_get_aging, + .max = 2, + }, { + .type = SWITCH_TYPE_INT, + .name = "led_mode", + .description = "Get/Set led mode (0 - 7)", + .set = rtl_set_led_mode, + .get = rtl_get_led_mode, + .max = 7, + } +}; + +static struct switch_attr rtl_port[] = { + { + .type = SWITCH_TYPE_INT, + .name = "pvid", + .description = "Port PVID", + .set = rtl_set_port_pvid, + .get = rtl_get_port_pvid, + .max = RTL8309G_NUM_VLANS - 1, + }, { + .type = SWITCH_TYPE_INT, + .name = "enable_traffic", + .description = "Enable packet transmission", + .set = rtl_set_traffic, + .get = rtl_get_traffic, + .max = 1, + }, { + .type = SWITCH_TYPE_INT, + .name = "enable_learning", + .description = "Enable learning", + .set = rtl_set_learning, + .get = rtl_get_learning, + .max = 1, + }, { + .type = SWITCH_TYPE_INT, + .name = "loop_status", + .description = "Loop status", + .get = rtl_get_loop_status, + .max = 1, + }, { + .type = SWITCH_TYPE_INT, + .name = "link_quality", + .description = "Link quality (0 - highest, 15 - lowest)", + .get = rtl_get_link_quality, + .max = 15, + } +}; + +static struct switch_attr rtl_vlan[] = { + { + .type = SWITCH_TYPE_INT, + .name = "vid", + .description = "VLAN ID (1-4095)", + .max = 4095, + .set = rtl_set_vlan_vid, + .get = rtl_get_vlan_vid, + } +}; + +static const struct switch_dev_ops rtl8309g_ops = { + .attr_global = { + .attr = rtl_globals, + .n_attr = ARRAY_SIZE(rtl_globals), + }, + .attr_port = { + .attr = rtl_port, + .n_attr = ARRAY_SIZE(rtl_port), + }, + .attr_vlan = { + .attr = rtl_vlan, + .n_attr = ARRAY_SIZE(rtl_vlan), + }, + .get_vlan_ports = rtl_get_vlan_ports, + .set_vlan_ports = rtl_set_vlan_ports, + .apply_config = rtl_hw_apply, + .reset_switch = rtl_reset, + .get_port_link = rtl_get_port_link, +}; + +static int +rtl8309g_config_init(struct phy_device *pdev) +{ + struct net_device *netdev = pdev->attached_dev; + struct rtl_priv *priv = pdev->priv; + struct switch_dev *dev = &priv->dev; + struct switch_val val; + int err; + + val.value.i = 1; + priv->dev.cpu_port = RTL8309G_PORT_CPU; + priv->dev.ports = RTL8309G_NUM_PORTS; + priv->dev.vlans = RTL8309G_NUM_VLANS; + priv->dev.ops = &rtl8309g_ops; + priv->bus = pdev->mdio.bus; + + strncpy(priv->hwname, RTL8309G_NAME, sizeof(priv->hwname)); + + dev->name = priv->hwname; + + err = register_switch(dev, netdev); + if (err < 0) { + kfree(priv); + return err; + } + + printk(KERN_INFO "Registered Realtek %s switch\n", priv->hwname); + + return 0; +} + +static int +rtl8309g_fixup(struct phy_device *pdev) +{ + struct rtl_priv priv; + struct mii_bus *bus = pdev->mdio.bus; + u16 mac1, mac2, mac3; + + memset(&priv, 0, sizeof(priv)); + priv.fixup = true; + priv.bus = bus; + + mac1 = rtl_read_reg(bus, RTL8309G_SW_MAC_ADDR_1); + mac2 = rtl_read_reg(bus, RTL8309G_SW_MAC_ADDR_2); + mac3 = rtl_read_reg(bus, RTL8309G_SW_MAC_ADDR_3); + + if (mac1 == 0x5452 && mac2 == 0x834C && mac3 == 0xB009) + pdev->phy_id = RTL8309G_MAGIC; + + return 0; +} + +static void +rtl8309g_remove(struct phy_device *pdev) +{ + struct rtl_priv *priv = pdev->priv; + + unregister_switch(&priv->dev); + printk(KERN_INFO "Unregistered Realtek %s switch\n", priv->hwname); + + kfree(priv); +} + +static int +rtl8309g_config_aneg(struct phy_device *pdev) +{ + struct rtl_priv *priv = pdev->priv; + struct mii_bus *bus = priv->bus; + int i; + + /* Restart autonegotiation */ + for (i = 0; i < 9; i++) { + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL_ANEG), 1); + rtl_write_reg(bus, RTL8309G_PHY_REG(i, CTRL_ANEG_RESTART), 1); + } + + return 0; +} + +static int +rtl8309g_read_status(struct phy_device *pdev) +{ + pdev->speed = SPEED_100; + pdev->duplex = DUPLEX_FULL; + pdev->link = 1; + + if (pdev->link) { + pdev->state = PHY_RUNNING; + netif_carrier_on(pdev->attached_dev); + pdev->adjust_link(pdev->attached_dev); + } else { + pdev->state = PHY_NOLINK; + netif_carrier_off(pdev->attached_dev); + pdev->adjust_link(pdev->attached_dev); + } + + return 0; +} + +static struct phy_driver rtl8309g_driver = { + .name = "Realtek RTL8309G", + .flags = PHY_HAS_MAGICANEG, + .phy_id = RTL8309G_MAGIC, + .phy_id_mask = 0xffffffff, + .features = PHY_BASIC_FEATURES, + .probe = &rtl8309g_probe, + .remove = &rtl8309g_remove, + .config_init = &rtl8309g_config_init, + .config_aneg = &rtl8309g_config_aneg, + .read_status = &rtl8309g_read_status, +}; + +static int __init +rtl_init(void) +{ + phy_register_fixup_for_id(PHY_ANY_ID, rtl8309g_fixup); + return phy_driver_register(&rtl8309g_driver, THIS_MODULE); +} + +static void __exit +rtl_exit(void) +{ + phy_driver_unregister(&rtl8309g_driver); +} + +module_init(rtl_init); +module_exit(rtl_exit); +MODULE_LICENSE("GPL"); + diff --git a/target/linux/generic/hack-4.14/730-phy_rtl8309g-switch-driver.patch b/target/linux/generic/hack-4.14/730-phy_rtl8309g-switch-driver.patch new file mode 100644 index 0000000000..061b809205 --- /dev/null +++ b/target/linux/generic/hack-4.14/730-phy_rtl8309g-switch-driver.patch @@ -0,0 +1,38 @@ +From ee63da0d4b47c6decce7280213899567875786eb Mon Sep 17 00:00:00 2001 +From: Konstantin +Date: Mon, 25 Dec 2017 17:17:02 +0300 +Subject: net: Realtek 8309G switch driver + +Signed-off-by: Konstantin +--- + drivers/net/phy/Kconfig | 4 ++++ + drivers/net/phy/Makefile | 1 + + 2 files changed, 5 insertions(+) + +diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig +index 49f5454f95e3..125f2a4903ad 100644 +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -252,6 +252,10 @@ config RTL8306_PHY + tristate "Driver for Realtek RTL8306S switches" + select SWCONFIG + ++config RTL8309G_PHY ++ tristate "Driver for Realtek RTL8309G switches" ++ select SWCONFIG ++ + config RTL8366_SMI + tristate "Driver for the RTL8366 SMI interface" + depends on GPIOLIB +diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile +index cd411e0df531..56fdaac771c5 100644 +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -31,6 +31,7 @@ obj-$(CONFIG_MVSWITCH_PHY) += mvswitch.o + obj-$(CONFIG_MVSW61XX_PHY) += mvsw61xx.o + obj-$(CONFIG_PSB6970_PHY) += psb6970.o + obj-$(CONFIG_RTL8306_PHY) += rtl8306.o ++obj-$(CONFIG_RTL8309G_PHY) += rtl8309g.o + obj-$(CONFIG_RTL8366_SMI) += rtl8366_smi.o + obj-$(CONFIG_RTL8366S_PHY) += rtl8366s.o + obj-$(CONFIG_RTL8366RB_PHY) += rtl8366rb.o diff --git a/target/linux/generic/hack-4.9/730-phy_rtl8309g-switch-driver.patch b/target/linux/generic/hack-4.9/730-phy_rtl8309g-switch-driver.patch new file mode 100644 index 0000000000..061b809205 --- /dev/null +++ b/target/linux/generic/hack-4.9/730-phy_rtl8309g-switch-driver.patch @@ -0,0 +1,38 @@ +From ee63da0d4b47c6decce7280213899567875786eb Mon Sep 17 00:00:00 2001 +From: Konstantin +Date: Mon, 25 Dec 2017 17:17:02 +0300 +Subject: net: Realtek 8309G switch driver + +Signed-off-by: Konstantin +--- + drivers/net/phy/Kconfig | 4 ++++ + drivers/net/phy/Makefile | 1 + + 2 files changed, 5 insertions(+) + +diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig +index 49f5454f95e3..125f2a4903ad 100644 +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -252,6 +252,10 @@ config RTL8306_PHY + tristate "Driver for Realtek RTL8306S switches" + select SWCONFIG + ++config RTL8309G_PHY ++ tristate "Driver for Realtek RTL8309G switches" ++ select SWCONFIG ++ + config RTL8366_SMI + tristate "Driver for the RTL8366 SMI interface" + depends on GPIOLIB +diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile +index cd411e0df531..56fdaac771c5 100644 +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -31,6 +31,7 @@ obj-$(CONFIG_MVSWITCH_PHY) += mvswitch.o + obj-$(CONFIG_MVSW61XX_PHY) += mvsw61xx.o + obj-$(CONFIG_PSB6970_PHY) += psb6970.o + obj-$(CONFIG_RTL8306_PHY) += rtl8306.o ++obj-$(CONFIG_RTL8309G_PHY) += rtl8309g.o + obj-$(CONFIG_RTL8366_SMI) += rtl8366_smi.o + obj-$(CONFIG_RTL8366S_PHY) += rtl8366s.o + obj-$(CONFIG_RTL8366RB_PHY) += rtl8366rb.o