realtek: copy dts/files/patches/configs for 5.15

Copy dts/files/patches/configs from 5.10 to 5.15.

Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
[refresh with updated DGS-1210 dts files]
Signed-off-by: Sander Vanheule <sander@svanheule.net>
This commit is contained in:
INAGAKI Hiroshi 2022-09-09 22:08:16 +09:00 committed by Sander Vanheule
parent fef55d5ffd
commit 8fb15ea52a
139 changed files with 39485 additions and 0 deletions

View file

@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include "rtl83xx_d-link_dgs-1210_common.dtsi"
#include "rtl83xx_d-link_dgs-1210_gpio.dtsi"
/ {
compatible = "d-link,dgs-1210-10mp-f", "realtek,rtl8382-soc", "realtek,rtl838x-soc";
model = "D-Link DGS-1210-10MP F";
};
&leds {
link_act {
label = "green:link_act";
gpios = <&gpio1 28 GPIO_ACTIVE_LOW>;
};
poe {
label = "green:poe";
gpios = <&gpio1 29 GPIO_ACTIVE_LOW>;
};
poe_max {
label = "yellow:poe_max";
gpios = <&gpio1 27 GPIO_ACTIVE_LOW>;
};
};
&keys {
mode {
label = "mode";
gpios = <&gpio1 30 GPIO_ACTIVE_LOW>;
linux,code = <KEY_LIGHTS_TOGGLE>;
};
};
&uart1 {
status = "okay";
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
INTERNAL_PHY(24)
INTERNAL_PHY(26)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(8, 1, internal)
SWITCH_PORT(9, 2, internal)
SWITCH_PORT(10, 3, internal)
SWITCH_PORT(11, 4, internal)
SWITCH_PORT(12, 5, internal)
SWITCH_PORT(13, 6, internal)
SWITCH_PORT(14, 7, internal)
SWITCH_PORT(15, 8, internal)
SWITCH_SFP_PORT(24, 9, rgmii-id)
SWITCH_SFP_PORT(26, 10, rgmii-id)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,229 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl838x.dtsi"
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
/ {
compatible = "engenius,ews2910p", "realtek,rtl838x-soc";
model = "EnGenius EWS2910P";
aliases {
led-boot = &led_power;
led-failsafe = &led_fault;
led-running = &led_power;
led-upgrade = &led_power;
};
memory@0 {
device_type = "memory";
reg = <0x0 0x10000000>;
};
keys {
compatible = "gpio-keys";
reset {
label = "reset";
gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
led_mode {
label = "led-mode";
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
linux,code = <BTN_0>;
};
};
gpio1: rtl8231-gpio {
compatible = "realtek,rtl8231-gpio";
#gpio-cells = <2>;
gpio-controller;
indirect-access-bus-id = <0>;
poe_enable {
gpio-hog;
gpios = <1 GPIO_ACTIVE_HIGH>;
output-high;
line-name = "poe-enable";
};
sff_p9_gpios {
gpio-hog;
gpios = < 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>,
< 11 GPIO_ACTIVE_HIGH>, /* los-gpio */
< 12 GPIO_ACTIVE_LOW>; /* mod-def0-gpio */
input;
line-name = "sff-p9-gpios";
};
};
gpio-export {
compatible = "gpio-export";
sff-p9-tx-disable {
gpio-export,name = "sff-p9-tx-disable";
gpio-export,output = <1>;
gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
};
};
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpio1 0 GPIO_ACTIVE_LOW>;
};
leds {
compatible = "gpio-leds";
led_power: led-0 {
label = "green:power";
gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
};
led_lan_mode: led-1 {
label = "green:lan-mode";
gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
};
led_fault: led-2 {
label = "amber:fault";
gpios = <&gpio1 2 GPIO_ACTIVE_LOW>;
};
led_poe_max: led-3 {
label = "amber:poe-max";
gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
};
};
i2c1: i2c-gpio-1 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp1: sfp-p10 {
compatible = "sff,sfp";
i2c-bus = <&i2c1>;
tx-disable-gpio = <&gpio1 13 GPIO_ACTIVE_HIGH>;
los-gpio = <&gpio1 14 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 21 GPIO_ACTIVE_LOW>;
};
};
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x80000>;
read-only;
};
partition@80000 {
label = "u-boot-env";
reg = <0x80000 0x10000>;
read-only;
};
partition@90000 {
label = "u-boot-env2";
reg = <0x90000 0x10000>;
};
partition@a0000 {
label = "rootfs_data";
reg = <0xa0000 0xd60000>;
};
partition@e00000 {
label = "jffs2-log";
reg = <0xe00000 0x200000>;
};
partition@1000000 {
compatible = "openwrt,uimage";
label = "firmware";
reg = <0x1000000 0x800000>;
openwrt,ih-magic = <0x03802910>;
};
partition@1800000 {
label = "firmware2";
reg = <0x1800000 0x800000>;
};
};
};
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
INTERNAL_PHY(24)
INTERNAL_PHY(26)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(8, 1, internal)
SWITCH_PORT(9, 2, internal)
SWITCH_PORT(10, 3, internal)
SWITCH_PORT(11, 4, internal)
SWITCH_PORT(12, 5, internal)
SWITCH_PORT(13, 6, internal)
SWITCH_PORT(14, 7, internal)
SWITCH_PORT(15, 8, internal)
SWITCH_SFP_PORT(24, 9, 1000base-x)
port@26 {
reg = <26>;
label = "lan10";
phy-mode = "1000base-x";
phy-handle = <&phy26>;
managed = "in-band-status";
sfp = <&sfp1>;
};
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};
&uart1 {
status = "okay";
};

View file

@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include "rtl838x_hpe_1920.dtsi"
/ {
compatible = "hpe,1920-8g", "realtek,rtl838x-soc";
model = "HPE 1920-8G (JG920A)";
gpio1: rtl8231-gpio {
compatible = "realtek,rtl8231-gpio";
#gpio-cells = <2>;
gpio-controller;
indirect-access-bus-id = <0>;
};
i2c0: i2c-gpio-0 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 23 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp0: sfp-0 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
los-gpio = <&gpio1 26 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 25 GPIO_ACTIVE_LOW>;
// tx-fault and tx-disable unconnected
};
i2c1: i2c-gpio-1 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp1: sfp-1 {
compatible = "sff,sfp";
i2c-bus = <&i2c1>;
los-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 21 GPIO_ACTIVE_LOW>;
// tx-fault and tx-disable unconnected
};
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
INTERNAL_PHY(24)
INTERNAL_PHY(26)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(8, 1, internal)
SWITCH_PORT(9, 2, internal)
SWITCH_PORT(10, 3, internal)
SWITCH_PORT(11, 4, internal)
SWITCH_PORT(12, 5, internal)
SWITCH_PORT(13, 6, internal)
SWITCH_PORT(14, 7, internal)
SWITCH_PORT(15, 8, internal)
port@24 {
reg = <24>;
label = "lan9";
phy-mode = "1000base-x";
managed = "in-band-status";
sfp = <&sfp0>;
};
port@26 {
reg = <26>;
label = "lan10";
phy-mode = "1000base-x";
managed = "in-band-status";
sfp = <&sfp1>;
};
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,85 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl838x.dtsi"
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
/ {
compatible = "realtek,rtl838x-soc";
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;
};
keys {
pinctrl-names = "default";
pinctrl-0 = <&pinmux_disable_sys_led>;
compatible = "gpio-keys";
mode {
label = "reset";
gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
};
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
open-source;
};
gpio1: rtl8231-gpio {
compatible = "realtek,rtl8231-gpio";
#gpio-cells = <2>;
gpio-controller;
indirect-access-bus-id = <31>;
};
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(8, 1, internal)
SWITCH_PORT(9, 2, internal)
SWITCH_PORT(10, 3, internal)
SWITCH_PORT(11, 4, internal)
SWITCH_PORT(12, 5, internal)
SWITCH_PORT(13, 6, internal)
SWITCH_PORT(14, 7, internal)
SWITCH_PORT(15, 8, internal)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,60 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_netgear_gigabit.dtsi"
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <50000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0000000 0x00e0000>;
read-only;
};
partition@e0000 {
label = "u-boot-env";
reg = <0x00e0000 0x0010000>;
};
partition@f0000 {
label = "u-boot-env2";
reg = <0x00f0000 0x0010000>;
};
partition@100000 {
label = "jffs";
reg = <0x0100000 0x0100000>;
read-only;
};
partition@200000 {
label = "jffs2";
reg = <0x0200000 0x0100000>;
read-only;
};
partition@300000 {
label = "firmware";
compatible = "openwrt,uimage", "denx,uimage";
openwrt,ih-magic = <0x4e474520>;
reg = <0x0300000 0x0e80000>;
};
partition@1180000 {
label = "runtime2";
reg = <0x1180000 0x0e80000>;
read-only;
};
};
};
};

View file

@ -0,0 +1,60 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_netgear_gigabit.dtsi"
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <50000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0000000 0x00e0000>;
read-only;
};
partition@e0000 {
label = "u-boot-env";
reg = <0x00e0000 0x0010000>;
};
partition@f0000 {
label = "u-boot-env2";
reg = <0x00f0000 0x0010000>;
};
partition@100000 {
label = "jffs";
reg = <0x0100000 0x0100000>;
read-only;
};
partition@200000 {
label = "jffs2";
reg = <0x0200000 0x0100000>;
read-only;
};
partition@300000 {
label = "firmware";
compatible = "openwrt,uimage", "denx,uimage";
openwrt,ih-magic = <0x4e474335>;
reg = <0x0300000 0x0e80000>;
};
partition@1180000 {
label = "runtime2";
reg = <0x1180000 0x0e80000>;
read-only;
};
};
};
};

View file

@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_netgear_gigabit_1xx.dtsi"
#include <dt-bindings/leds/common.h>
/ {
compatible = "netgear,gs108t-v3", "realtek,rtl838x-soc";
model = "Netgear GS108T v3";
aliases {
led-boot = &led_power_green;
led-failsafe = &led_power_amber;
led-running = &led_power_green;
led-upgrade = &led_power_amber;
};
leds {
compatible = "gpio-leds";
led_power_amber: led-0 {
label = "amber:power";
color = <LED_COLOR_ID_AMBER>;
function = LED_FUNCTION_POWER;
gpios = <&gpio1 32 GPIO_ACTIVE_LOW>;
};
led_power_green: led-1 {
label = "green:power";
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_POWER;
gpios = <&gpio1 31 GPIO_ACTIVE_LOW>;
};
};
};

View file

@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_netgear_gigabit_1xx.dtsi"
#include <dt-bindings/leds/common.h>
/ {
compatible = "netgear,gs110tpp-v1", "realtek,rtl838x-soc";
model = "Netgear GS110TPP v1";
aliases {
led-boot = &led_status_green;
led-failsafe = &led_status_red;
led-running = &led_status_green;
led-upgrade = &led_status_blue;
};
leds {
compatible = "gpio-leds";
led_status_red: led-0 {
label = "red:status";
color = <LED_COLOR_ID_RED>;
function = LED_FUNCTION_STATUS;
gpios = <&gpio1 31 GPIO_ACTIVE_LOW>;
};
led_status_green: led-1 {
label = "green:status";
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_STATUS;
gpios = <&gpio1 32 GPIO_ACTIVE_LOW>;
};
led_status_blue: led-2 {
label = "blue:status";
color = <LED_COLOR_ID_BLUE>;
function = LED_FUNCTION_STATUS;
gpios = <&gpio1 34 GPIO_ACTIVE_LOW>;
};
};
};
&uart1 {
status = "okay";
};
&mdio {
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
};
&switch0 {
ports {
SWITCH_PORT(16, 9, qsgmii)
SWITCH_PORT(17, 10, qsgmii)
};
};

View file

@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_netgear_gigabit_3xx.dtsi"
#include <dt-bindings/leds/common.h>
/ {
compatible = "netgear,gs308t-v1", "realtek,rtl838x-soc";
model = "Netgear GS308T v1";
aliases {
led-boot = &led_power_green;
led-failsafe = &led_power_amber;
led-running = &led_power_green;
led-upgrade = &led_power_amber;
};
leds {
compatible = "gpio-leds";
led_power_amber: led-0 {
label = "amber:power";
color = <LED_COLOR_ID_AMBER>;
function = LED_FUNCTION_POWER;
gpios = <&gpio1 32 GPIO_ACTIVE_LOW>;
};
led_power_green: led-1 {
label = "green:power";
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_POWER;
gpios = <&gpio1 31 GPIO_ACTIVE_LOW>;
};
};
};

View file

@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_netgear_gigabit_3xx.dtsi"
/ {
compatible = "netgear,gs310tp-v1", "realtek,rtl838x-soc";
model = "Netgear GS310TP v1";
};
&uart1 {
status = "okay";
};
&mdio {
INTERNAL_PHY(24)
INTERNAL_PHY(26)
};
&switch0 {
ports {
SWITCH_SFP_PORT(24, 9, rgmii-id)
SWITCH_SFP_PORT(26, 10, rgmii-id)
};
};

View file

@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include "rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi"
#include <dt-bindings/interrupt-controller/irq.h>
/ {
compatible = "panasonic,m8eg-pn28080k", "realtek,rtl8380-soc";
model = "Panasonic Switch-M8eG PN28080K";
aliases {
led-boot = &led_status_eco_green;
led-failsafe = &led_status_eco_amber;
led-running = &led_status_eco_green;
led-upgrade = &led_status_eco_green;
};
sfp0: sfp-p9 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
tx-fault-gpio = <&gpio1 0 GPIO_ACTIVE_HIGH>;
tx-disable-gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 2 GPIO_ACTIVE_LOW>;
los-gpio = <&gpio1 3 GPIO_ACTIVE_HIGH>;
};
};
&leds {
led_status_eco_amber: led-5 {
label = "amber:status_eco";
gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_AMBER>;
function = LED_FUNCTION_STATUS;
function-enumerator = <1>;
};
led_status_eco_green: led-6 {
label = "green:status_eco";
gpios = <&gpio2 2 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_STATUS;
function-enumerator = <2>;
};
};
&i2c_gpio_0 {
scl-gpios = <&gpio0 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
sda-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
};
&i2c_gpio_1 {
scl-gpios = <&gpio0 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
sda-gpios = <&gpio0 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
};
&gpio1 {
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gpio0>;
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
};
&gpio2 {
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gpio0>;
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
};
&i2c_switch {
i2c0: i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
};
};
&ethernet0 {
mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
INTERNAL_PHY(24)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(8, 1, internal)
SWITCH_PORT(9, 2, internal)
SWITCH_PORT(10, 3, internal)
SWITCH_PORT(11, 4, internal)
SWITCH_PORT(12, 5, internal)
SWITCH_PORT(13, 6, internal)
SWITCH_PORT(14, 7, internal)
SWITCH_PORT(15, 8, internal)
port@24 {
reg = <24>;
label = "lan9";
phy-mode = "1000base-x";
phy-handle = <&phy24>;
managed = "in-band-status";
sfp = <&sfp0>;
};
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_tplink_sg2xxx.dtsi"
/ {
compatible = "tplink,sg2008p-v1", "realtek,rtl838x-soc";
model = "TP-Link SG2008P v1";
};
&tps23861_20 {
status = "disabled";
};
&phy24 {
status = "disabled";
};
&phy26 {
status = "disabled";
};
&port24 {
status = "disabled";
};
&port26 {
status = "disabled";
};

View file

@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_tplink_sg2xxx.dtsi"
/ {
compatible = "tplink,sg2210p-v3", "realtek,rtl838x-soc";
model = "TP-Link SG2210P v3";
};
&port24 {
label = "lan-sfp2";
};
&port26 {
label = "lan-sfp1";
};

View file

@ -0,0 +1,183 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl838x.dtsi"
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
/ {
aliases {
led-boot = &led_power;
led-failsafe = &led_power;
led-running = &led_power;
led-upgrade = &led_power;
label-mac-device = &ethernet0;
};
memory@0 {
device_type = "memory";
reg = <0x0 0x10000000>;
};
keys {
compatible = "gpio-keys";
reset {
label = "reset";
gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
};
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
};
leds {
compatible = "gpio-leds";
led_power: led-0 {
label = "green:power";
gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
};
};
i2c1: i2c-gpio-1 {
compatible = "i2c-gpio";
scl-gpios = <&gpio0 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
sda-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
tps23861_20: tps23861@20 {
compatible = "ti,tps23861";
reg = <0x20>;
shunt-resistor-micro-ohms = <255000>;
};
tps23861_28: tps23861@28 {
compatible = "ti,tps23861";
reg = <0x28>;
shunt-resistor-micro-ohms = <255000>;
};
};
watchdog {
compatible = "linux,wdt-gpio";
gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
hw_algo = "toggle";
/* SGM706 specs: typical 1.6s, but minimum 1.0s. */
hw_margin_ms = <1000>;
};
};
&gpio0 {
watchdog-enable {
gpio-hog;
gpios = <14 GPIO_ACTIVE_LOW>;
output-low;
line-name = "watchdog-enable";
};
};
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0xe0000>;
read-only;
};
partition@e0000 {
label = "u-boot-env";
reg = <0xe0000 0x20000>;
};
partition@100000 {
compatible = "denx,uimage";
label = "firmware";
reg = <0x100000 0x1a00000>;
};
partition@1b00000 {
label = "usrappfs";
reg = <0x1b00000 0x400000>;
};
partition@1f00000 {
compatible = "nvmem-cells";
label = "para";
reg = <0x1f00000 0x100000>;
#address-cells = <1>;
#size-cells = <1>;
read-only;
factory_macaddr: macaddr@fdff4 {
reg = <0xfdff4 0x6>;
};
};
};
};
};
&ethernet0 {
nvmem-cells = <&factory_macaddr>;
nvmem-cell-names = "mac-address";
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
#address-cells = <1>;
#size-cells = <0>;
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
INTERNAL_PHY(24)
INTERNAL_PHY(26)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(15, 1, internal)
SWITCH_PORT(14, 2, internal)
SWITCH_PORT(13, 3, internal)
SWITCH_PORT(12, 4, internal)
SWITCH_PORT(11, 5, internal)
SWITCH_PORT(10, 6, internal)
SWITCH_PORT(9, 7, internal)
SWITCH_PORT(8, 8, internal)
SWITCH_SFP_PORT(24, 9, 1000base-x)
SWITCH_SFP_PORT(26, 10, 1000base-x)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,75 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_zyxel_gs1900.dtsi"
/ {
compatible = "zyxel,gs1900-10hp", "realtek,rtl838x-soc";
model = "ZyXEL GS1900-10HP Switch";
/* i2c of the left SFP cage: port 9 */
i2c0: i2c-gpio-0 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp0: sfp-p9 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
los-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>;
tx-fault-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 26 GPIO_ACTIVE_LOW>;
tx-disable-gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>;
};
/* i2c of the right SFP cage: port 10 */
i2c1: i2c-gpio-1 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp1: sfp-p10 {
compatible = "sff,sfp";
i2c-bus = <&i2c1>;
los-gpio = <&gpio1 33 GPIO_ACTIVE_HIGH>;
tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 32 GPIO_ACTIVE_LOW>;
tx-disable-gpio = <&gpio1 29 GPIO_ACTIVE_HIGH>;
};
};
&uart1 {
status = "okay";
};
&mdio {
INTERNAL_PHY(24)
INTERNAL_PHY(26)
};
&switch0 {
ports {
port@24 {
reg = <24>;
label = "lan9";
phy-mode = "1000base-x";
managed = "in-band-status";
sfp = <&sfp0>;
};
port@26 {
reg = <26>;
label = "lan10";
phy-mode = "1000base-x";
managed = "in-band-status";
sfp = <&sfp1>;
};
};
};

View file

@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_zyxel_gs1900.dtsi"
/ {
compatible = "zyxel,gs1900-8", "realtek,rtl838x-soc";
model = "ZyXEL GS1900-8 Switch";
};
&gpio1 {
/delete-node/ poe_enable;
};

View file

@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_zyxel_gs1900.dtsi"
/ {
compatible = "zyxel,gs1900-8hp-v1", "realtek,rtl838x-soc";
model = "ZyXEL GS1900-8HP v1 Switch";
};
&uart1 {
status = "okay";
};

View file

@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_zyxel_gs1900.dtsi"
/ {
compatible = "zyxel,gs1900-8hp-v2", "realtek,rtl838x-soc";
model = "ZyXEL GS1900-8HP v2 Switch";
};
&uart1 {
status = "okay";
};

View file

@ -0,0 +1,149 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl838x.dtsi"
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
/ {
aliases {
led-boot = &led_sys;
led-failsafe = &led_sys;
led-running = &led_sys;
led-upgrade = &led_sys;
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;
};
keys {
compatible = "gpio-keys-polled";
poll-interval = <20>;
reset {
label = "reset";
gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
};
leds {
pinctrl-names = "default";
pinctrl-0 = <&pinmux_disable_sys_led>;
compatible = "gpio-leds";
led_sys: sys {
label = "green:sys";
gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
};
};
gpio1: rtl8231-gpio {
compatible = "realtek,rtl8231-gpio";
#gpio-cells = <2>;
gpio-controller;
indirect-access-bus-id = <0>;
poe_enable {
gpio-hog;
gpios = <13 GPIO_ACTIVE_HIGH>;
output-high;
};
};
};
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x40000>;
read-only;
};
partition@40000 {
label = "u-boot-env";
reg = <0x40000 0x10000>;
read-only;
};
partition@50000 {
label = "u-boot-env2";
reg = <0x50000 0x10000>;
};
partition@60000 {
label = "jffs";
reg = <0x60000 0x100000>;
};
partition@160000 {
label = "jffs2";
reg = <0x160000 0x100000>;
};
partition@b260000 {
label = "firmware";
reg = <0x260000 0x6d0000>;
compatible = "openwrt,uimage", "denx,uimage";
openwrt,ih-magic = <0x83800000>;
};
partition@930000 {
label = "runtime2";
reg = <0x930000 0x6d0000>;
};
};
};
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(8, 1, internal)
SWITCH_PORT(9, 2, internal)
SWITCH_PORT(10, 3, internal)
SWITCH_PORT(11, 4, internal)
SWITCH_PORT(12, 5, internal)
SWITCH_PORT(13, 6, internal)
SWITCH_PORT(14, 7, internal)
SWITCH_PORT(15, 8, internal)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl838x.dtsi"
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
/ {
compatible = "allnet,all-sg8208m", "realtek,rtl838x-soc";
model = "ALLNET ALL-SG8208M";
aliases {
led-boot = &led_sys;
led-failsafe = &led_sys;
led-running = &led_sys;
led-upgrade = &led_sys;
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;
};
keys {
compatible = "gpio-keys-polled";
poll-interval = <20>;
/* is this pin 3 on the external RTL8231 (&gpio1)? */
/*reset {
label = "reset";
gpios = <&gpio0 67 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};*/
};
leds {
pinctrl-names = "default";
pinctrl-0 = <&pinmux_disable_sys_led>;
compatible = "gpio-leds";
led_sys: sys {
label = "green:sys";
gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
};
// GPIO 25: power on/off all port leds
};
};
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x80000>;
read-only;
};
partition@80000 {
label = "u-boot-env";
reg = <0x80000 0x10000>;
read-only;
};
partition@90000 {
label = "u-boot-env2";
reg = <0x90000 0x10000>;
};
partition@a0000 {
label = "jffs";
reg = <0xa0000 0x100000>;
};
partition@1a0000 {
label = "jffs2";
reg = <0x1a0000 0x100000>;
};
partition@2a0000 {
label = "firmware";
reg = <0x2a0000 0xd60000>;
compatible = "openwrt,uimage", "denx,uimage";
openwrt,ih-magic = <0x00000006>;
};
};
};
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(8, 1, internal)
SWITCH_PORT(9, 2, internal)
SWITCH_PORT(10, 3, internal)
SWITCH_PORT(11, 4, internal)
SWITCH_PORT(12, 5, internal)
SWITCH_PORT(13, 6, internal)
SWITCH_PORT(14, 7, internal)
SWITCH_PORT(15, 8, internal)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include "rtl83xx_d-link_dgs-1210_common.dtsi"
/ {
compatible = "d-link,dgs-1210-10p", "realtek,rtl838x-soc";
model = "D-Link DGS-1210-10P";
keys {
compatible = "gpio-keys-polled";
poll-interval = <20>;
mode {
label = "mode";
gpios = <&gpio1 30 GPIO_ACTIVE_LOW>;
linux,code = <KEY_LIGHTS_TOGGLE>;
};
reset {
label = "reset";
gpios = <&gpio1 33 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
};
leds {
link_act {
label = "green:link_act";
gpios = <&gpio1 28 GPIO_ACTIVE_LOW>;
};
poe {
label = "green:poe";
gpios = <&gpio1 29 GPIO_ACTIVE_LOW>;
};
poe_max {
label = "yellow:poe_max";
gpios = <&gpio1 27 GPIO_ACTIVE_LOW>;
};
};
gpio1: rtl8231-gpio {
compatible = "realtek,rtl8231-gpio";
#gpio-cells = <2>;
gpio-controller;
indirect-access-bus-id = <0>;
};
};
&uart1 {
status = "okay";
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
INTERNAL_PHY(24)
INTERNAL_PHY(26)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(8, 1, internal)
SWITCH_PORT(9, 2, internal)
SWITCH_PORT(10, 3, internal)
SWITCH_PORT(11, 4, internal)
SWITCH_PORT(12, 5, internal)
SWITCH_PORT(13, 6, internal)
SWITCH_PORT(14, 7, internal)
SWITCH_PORT(15, 8, internal)
SWITCH_SFP_PORT(24, 9, rgmii-id)
SWITCH_SFP_PORT(26, 10, rgmii-id)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,81 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include "rtl83xx_d-link_dgs-1210_common.dtsi"
/ {
compatible = "d-link,dgs-1210-16", "realtek,rtl838x-soc";
model = "D-Link DGS-1210-16";
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
EXTERNAL_SFP_PHY(24)
EXTERNAL_SFP_PHY(25)
EXTERNAL_SFP_PHY(26)
EXTERNAL_SFP_PHY(27)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(0, 1, qsgmii)
SWITCH_PORT(1, 2, qsgmii)
SWITCH_PORT(2, 3, qsgmii)
SWITCH_PORT(3, 4, qsgmii)
SWITCH_PORT(4, 5, qsgmii)
SWITCH_PORT(5, 6, qsgmii)
SWITCH_PORT(6, 7, qsgmii)
SWITCH_PORT(7, 8, qsgmii)
SWITCH_PORT(8, 9, internal)
SWITCH_PORT(9, 10, internal)
SWITCH_PORT(10, 11, internal)
SWITCH_PORT(11, 12, internal)
SWITCH_PORT(12, 13, internal)
SWITCH_PORT(13, 14, internal)
SWITCH_PORT(14, 15, internal)
SWITCH_PORT(15, 16, internal)
SWITCH_PORT(24, 17, qsgmii)
SWITCH_PORT(25, 18, qsgmii)
SWITCH_PORT(26, 19, qsgmii)
SWITCH_PORT(27, 20, qsgmii)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include "rtl83xx_d-link_dgs-1210_common.dtsi"
#include "rtl83xx_d-link_dgs-1210_gpio.dtsi"
/ {
compatible = "d-link,dgs-1210-20", "realtek,rtl838x-soc";
model = "D-Link DGS-1210-20";
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
EXTERNAL_SFP_PHY(24)
EXTERNAL_SFP_PHY(25)
EXTERNAL_SFP_PHY(26)
EXTERNAL_SFP_PHY(27)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(0, 1, qsgmii)
SWITCH_PORT(1, 2, qsgmii)
SWITCH_PORT(2, 3, qsgmii)
SWITCH_PORT(3, 4, qsgmii)
SWITCH_PORT(4, 5, qsgmii)
SWITCH_PORT(5, 6, qsgmii)
SWITCH_PORT(6, 7, qsgmii)
SWITCH_PORT(7, 8, qsgmii)
SWITCH_PORT(8, 9, internal)
SWITCH_PORT(9, 10, internal)
SWITCH_PORT(10, 11, internal)
SWITCH_PORT(11, 12, internal)
SWITCH_PORT(12, 13, internal)
SWITCH_PORT(13, 14, internal)
SWITCH_PORT(14, 15, internal)
SWITCH_PORT(15, 16, internal)
SWITCH_PORT(24, 17, qsgmii)
SWITCH_PORT(25, 18, qsgmii)
SWITCH_PORT(26, 19, qsgmii)
SWITCH_PORT(27, 20, qsgmii)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include "rtl83xx_d-link_dgs-1210_common.dtsi"
#include "rtl83xx_d-link_dgs-1210_gpio.dtsi"
#include "rtl8382_d-link_dgs-1210-28_common.dtsi"
/ {
compatible = "d-link,dgs-1210-28", "realtek,rtl838x-soc";
model = "D-Link DGS-1210-28";
};

View file

@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
EXTERNAL_SFP_PHY(24)
EXTERNAL_SFP_PHY(25)
EXTERNAL_SFP_PHY(26)
EXTERNAL_SFP_PHY(27)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(0, 1, qsgmii)
SWITCH_PORT(1, 2, qsgmii)
SWITCH_PORT(2, 3, qsgmii)
SWITCH_PORT(3, 4, qsgmii)
SWITCH_PORT(4, 5, qsgmii)
SWITCH_PORT(5, 6, qsgmii)
SWITCH_PORT(6, 7, qsgmii)
SWITCH_PORT(7, 8, qsgmii)
SWITCH_PORT(8, 9, internal)
SWITCH_PORT(9, 10, internal)
SWITCH_PORT(10, 11, internal)
SWITCH_PORT(11, 12, internal)
SWITCH_PORT(12, 13, internal)
SWITCH_PORT(13, 14, internal)
SWITCH_PORT(14, 15, internal)
SWITCH_PORT(15, 16, internal)
SWITCH_PORT(16, 17, qsgmii)
SWITCH_PORT(17, 18, qsgmii)
SWITCH_PORT(18, 19, qsgmii)
SWITCH_PORT(19, 20, qsgmii)
SWITCH_PORT(20, 21, qsgmii)
SWITCH_PORT(21, 22, qsgmii)
SWITCH_PORT(22, 23, qsgmii)
SWITCH_PORT(23, 24, qsgmii)
SWITCH_PORT(24, 25, qsgmii)
SWITCH_PORT(25, 26, qsgmii)
SWITCH_PORT(26, 27, qsgmii)
SWITCH_PORT(27, 28, qsgmii)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,40 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include "rtl83xx_d-link_dgs-1210_common.dtsi"
#include "rtl83xx_d-link_dgs-1210_gpio.dtsi"
#include "rtl8382_d-link_dgs-1210-28_common.dtsi"
/ {
compatible = "d-link,dgs-1210-28mp-f", "realtek,rtl8382-soc", "realtek,rtl838x-soc";
model = "D-Link DGS-1210-28MP F";
};
&leds {
link_act {
label = "green:link_act";
gpios = <&gpio1 28 GPIO_ACTIVE_LOW>;
};
poe {
label = "green:poe";
gpios = <&gpio1 29 GPIO_ACTIVE_LOW>;
};
poe_max {
label = "yellow:poe_max";
gpios = <&gpio1 27 GPIO_ACTIVE_LOW>;
};
};
&keys {
mode {
label = "mode";
gpios = <&gpio1 30 GPIO_ACTIVE_LOW>;
linux,code = <BTN_0>;
};
};
&uart1 {
status = "okay";
};

View file

@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl8382_hpe_1920.dtsi"
/ {
compatible = "hpe,1920-16g", "realtek,rtl838x-soc";
model = "HPE 1920-16G (JG923A)";
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(8, 1, internal)
SWITCH_PORT(9, 2, internal)
SWITCH_PORT(10, 3, internal)
SWITCH_PORT(11, 4, internal)
SWITCH_PORT(12, 5, internal)
SWITCH_PORT(13, 6, internal)
SWITCH_PORT(14, 7, internal)
SWITCH_PORT(15, 8, internal)
SWITCH_PORT(16, 9, qsgmii)
SWITCH_PORT(17, 10, qsgmii)
SWITCH_PORT(18, 11, qsgmii)
SWITCH_PORT(19, 12, qsgmii)
SWITCH_PORT(20, 13, qsgmii)
SWITCH_PORT(21, 14, qsgmii)
SWITCH_PORT(22, 15, qsgmii)
SWITCH_PORT(23, 16, qsgmii)
SWITCH_PORT(24, 17, qsgmii)
SWITCH_PORT(25, 18, qsgmii)
SWITCH_PORT(26, 19, qsgmii)
SWITCH_PORT(27, 20, qsgmii)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl8382_hpe_1920.dtsi"
/ {
compatible = "hpe,1920-24g", "realtek,rtl838x-soc";
model = "HPE 1920-24G (JG924A)";
};
&mdio {
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(0, 1, qsgmii)
SWITCH_PORT(1, 2, qsgmii)
SWITCH_PORT(2, 3, qsgmii)
SWITCH_PORT(3, 4, qsgmii)
SWITCH_PORT(4, 5, qsgmii)
SWITCH_PORT(5, 6, qsgmii)
SWITCH_PORT(6, 7, qsgmii)
SWITCH_PORT(7, 8, qsgmii)
SWITCH_PORT(8, 9, internal)
SWITCH_PORT(9, 10, internal)
SWITCH_PORT(10, 11, internal)
SWITCH_PORT(11, 12, internal)
SWITCH_PORT(12, 13, internal)
SWITCH_PORT(13, 14, internal)
SWITCH_PORT(14, 15, internal)
SWITCH_PORT(15, 16, internal)
SWITCH_PORT(16, 17, qsgmii)
SWITCH_PORT(17, 18, qsgmii)
SWITCH_PORT(18, 19, qsgmii)
SWITCH_PORT(19, 20, qsgmii)
SWITCH_PORT(20, 21, qsgmii)
SWITCH_PORT(21, 22, qsgmii)
SWITCH_PORT(22, 23, qsgmii)
SWITCH_PORT(23, 24, qsgmii)
SWITCH_PORT(24, 25, qsgmii)
SWITCH_PORT(25, 26, qsgmii)
SWITCH_PORT(26, 27, qsgmii)
SWITCH_PORT(27, 28, qsgmii)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include "rtl838x_hpe_1920.dtsi"
/ {
gpio1: rtl8231-gpio {
compatible = "realtek,rtl8231-gpio";
#gpio-cells = <2>;
gpio-controller;
indirect-access-bus-id = <0>;
};
i2c0: i2c-gpio-0 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp0: sfp-0 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
los-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 21 GPIO_ACTIVE_LOW>;
// tx-fault unconnected
// tx-disable connected to RTL8214FC
};
i2c1: i2c-gpio-1 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 23 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp1: sfp-1 {
compatible = "sff,sfp";
i2c-bus = <&i2c1>;
los-gpio = <&gpio1 26 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 25 GPIO_ACTIVE_LOW>;
// tx-fault unconnected
// tx-disable connected to RTL8214FC
};
i2c2: i2c-gpio-2 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 27 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 28 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp2: sfp-2 {
compatible = "sff,sfp";
i2c-bus = <&i2c2>;
los-gpio = <&gpio1 30 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 29 GPIO_ACTIVE_LOW>;
// tx-fault unconnected
// tx-disable connected to RTL8214FC
};
i2c3: i2c-gpio-3 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 32 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp3: sfp-3 {
compatible = "sff,sfp";
i2c-bus = <&i2c3>;
los-gpio = <&gpio1 34 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 33 GPIO_ACTIVE_LOW>;
// tx-fault unconnected
// tx-disable connected to RTL8214FC
};
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
EXTERNAL_SFP_PHY_FULL(24, 0)
EXTERNAL_SFP_PHY_FULL(25, 1)
EXTERNAL_SFP_PHY_FULL(26, 2)
EXTERNAL_SFP_PHY_FULL(27, 3)
};
};

View file

@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
/ {
compatible = "inaba,aml2-17gp", "realtek,rtl838x-soc";
model = "INABA Abaniact AML2-17GP";
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;
};
keys {
pinctrl-names = "default";
pinctrl-0 = <&pinmux_disable_sys_led>;
compatible = "gpio-keys";
reset {
label = "reset";
gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
};
};
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x80000>;
read-only;
};
partition@80000 {
label = "u-boot-env";
reg = <0x80000 0x10000>;
read-only;
};
partition@90000 {
label = "u-boot-env2";
reg = <0x90000 0x10000>;
};
partition@a0000 {
label = "jffs2_cfg";
reg = <0xa0000 0x400000>;
read-only;
};
partition@4a0000 {
label = "jffs2_log";
reg = <0x4a0000 0x100000>;
read-only;
};
partition@5a0000 {
compatible = "openwrt,uimage", "denx,uimage";
label = "firmware";
reg = <0x5a0000 0xd30000>;
openwrt,ih-magic = <0x83800000>;
};
partition@12d0000 {
label = "runtime2";
reg = <0x12d0000 0xd30000>;
};
};
};
};
&ethernet0 {
mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
EXTERNAL_PHY(24)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(8, 1, internal)
SWITCH_PORT(9, 2, internal)
SWITCH_PORT(10, 3, internal)
SWITCH_PORT(11, 4, internal)
SWITCH_PORT(12, 5, internal)
SWITCH_PORT(13, 6, internal)
SWITCH_PORT(14, 7, internal)
SWITCH_PORT(15, 8, internal)
SWITCH_PORT(16, 9, qsgmii)
SWITCH_PORT(17, 10, qsgmii)
SWITCH_PORT(18, 11, qsgmii)
SWITCH_PORT(19, 12, qsgmii)
SWITCH_PORT(20, 13, qsgmii)
SWITCH_PORT(21, 14, qsgmii)
SWITCH_PORT(22, 15, qsgmii)
SWITCH_PORT(23, 16, qsgmii)
port@24 {
reg = <24>;
label = "wan";
phy-handle = <&phy24>;
phy-mode = "qsgmii";
};
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,197 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/leds/common.h>
/ {
compatible = "iodata,bsh-g24mb", "realtek,rtl838x-soc";
model = "I-O DATA BSH-G24MB";
aliases {
led-boot = &led_sys_loop;
led-failsafe = &led_sys_loop;
led-upgrade = &led_sys_loop;
};
chosen {
bootargs = "console=ttyS0,115200";
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;
};
leds {
pinctrl-names = "default";
pinctrl-0 = <&pinmux_disable_sys_led>;
compatible = "gpio-leds";
led_sys_loop: led {
label = "red:sys_loop";
gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_RED>;
function = LED_FUNCTION_STATUS;
};
};
keys {
compatible = "gpio-keys-polled";
poll-interval = <20>;
reset {
label = "reset";
gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
};
gpio1: rtl8231-gpio {
compatible = "realtek,rtl8231-gpio";
#gpio-cells = <2>;
gpio-controller;
indirect-access-bus-id = <0>;
};
};
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x80000>;
read-only;
};
partition@80000 {
label = "u-boot-env";
reg = <0x80000 0x10000>;
read-only;
};
partition@90000 {
label = "u-boot-env2";
reg = <0x90000 0x10000>;
};
partition@a0000 {
label = "jffs2_cfg";
reg = <0xa0000 0x100000>;
read-only;
};
partition@1a0000 {
label = "jffs2_log";
reg = <0x1a0000 0x100000>;
read-only;
};
/*
* use 2x OS partitions in OpenWrt
*
* 0x2A0000-0x94FFFF: RUNTIME
* 0x950000-0xFFFFFF: RUNTIME2 (not used in stock)
*/
partition@2a0000 {
compatible = "openwrt,uimage", "denx,uimage";
label = "firmware";
reg = <0x2a0000 0xd60000>;
openwrt,ih-magic = <0x83800013>;
};
};
};
};
&ethernet0 {
mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(0, 1, qsgmii)
SWITCH_PORT(1, 2, qsgmii)
SWITCH_PORT(2, 3, qsgmii)
SWITCH_PORT(3, 4, qsgmii)
SWITCH_PORT(4, 5, qsgmii)
SWITCH_PORT(5, 6, qsgmii)
SWITCH_PORT(6, 7, qsgmii)
SWITCH_PORT(7, 8, qsgmii)
SWITCH_PORT(8, 9, internal)
SWITCH_PORT(9, 10, internal)
SWITCH_PORT(10, 11, internal)
SWITCH_PORT(11, 12, internal)
SWITCH_PORT(12, 13, internal)
SWITCH_PORT(13, 14, internal)
SWITCH_PORT(14, 15, internal)
SWITCH_PORT(15, 16, internal)
SWITCH_PORT(16, 17, qsgmii)
SWITCH_PORT(17, 18, qsgmii)
SWITCH_PORT(18, 19, qsgmii)
SWITCH_PORT(19, 20, qsgmii)
SWITCH_PORT(20, 21, qsgmii)
SWITCH_PORT(21, 22, qsgmii)
SWITCH_PORT(22, 23, qsgmii)
SWITCH_PORT(23, 24, qsgmii)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include "rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi"
#include <dt-bindings/interrupt-controller/irq.h>
/ {
compatible = "panasonic,m16eg-pn28160k", "realtek,rtl8382-soc";
model = "Panasonic Switch-M16eG PN28160K";
aliases {
led-boot = &led_status_eco_green;
led-failsafe = &led_status_eco_amber;
led-running = &led_status_eco_green;
led-upgrade = &led_status_eco_green;
};
/*
* sfp0/1 are "combo" port with each TP port (23/24), and they are
* connected to the RTL8218FB. Currently, there is no support for
* the chip and only TP ports work by the RTL8218D support.
*/
sfp0: sfp-p23 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
tx-fault-gpio = <&gpio1 0 GPIO_ACTIVE_HIGH>;
tx-disable-gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 2 GPIO_ACTIVE_LOW>;
los-gpio = <&gpio1 3 GPIO_ACTIVE_HIGH>;
};
sfp1: sfp-p24 {
compatible = "sff,sfp";
i2c-bus = <&i2c1>;
tx-fault-gpio = <&gpio1 4 GPIO_ACTIVE_HIGH>;
tx-disable-gpio = <&gpio1 5 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 6 GPIO_ACTIVE_LOW>;
los-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;
};
};
&leds {
led_status_eco_amber: led-5 {
label = "amber:status_eco";
gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_AMBER>;
function = LED_FUNCTION_STATUS;
function-enumerator = <1>;
};
led_status_eco_green: led-6 {
label = "green:status_eco";
gpios = <&gpio2 2 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_STATUS;
function-enumerator = <2>;
};
};
&i2c_gpio_0 {
scl-gpios = <&gpio0 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
sda-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
};
&i2c_gpio_1 {
scl-gpios = <&gpio0 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
sda-gpios = <&gpio0 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
};
&gpio2 {
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gpio0>;
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
/*
* GPIO12 (IO1_4): RTL8218FB
*
* This GPIO pin should be specified as "reset-gpio" in mdio node, but
* RTL8218FB phy won't be configured on RTL8218D support in the current
* phy driver. So, ethernet ports on the phy will be broken after hard-
* resetting.
* (RTL8218FB phy will be detected as RTL8218D by the phy driver)
* At the moment, configure this GPIO pin as gpio-hog to avoid breaking
* by resetting.
*/
ext_switch_reset {
gpio-hog;
gpios = <12 GPIO_ACTIVE_HIGH>;
output-high;
line-name = "ext-switch-reset";
};
};
&i2c_switch {
i2c0: i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
};
i2c1: i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
};
};
&ethernet0 {
mdio-bus {
compatible = "realtek,rtl838x-mdio";
#address-cells = <1>;
#size-cells = <0>;
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
/* RTL8218FB */
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(8, 1, internal)
SWITCH_PORT(9, 2, internal)
SWITCH_PORT(10, 3, internal)
SWITCH_PORT(11, 4, internal)
SWITCH_PORT(12, 5, internal)
SWITCH_PORT(13, 6, internal)
SWITCH_PORT(14, 7, internal)
SWITCH_PORT(15, 8, internal)
SWITCH_PORT(16, 9, qsgmii)
SWITCH_PORT(17, 10, qsgmii)
SWITCH_PORT(18, 11, qsgmii)
SWITCH_PORT(19, 12, qsgmii)
SWITCH_PORT(20, 13, qsgmii)
SWITCH_PORT(21, 14, qsgmii)
SWITCH_PORT(22, 15, qsgmii)
SWITCH_PORT(23, 16, qsgmii)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,190 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl838x.dtsi"
#include "rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi"
#include <dt-bindings/interrupt-controller/irq.h>
/ {
compatible = "panasonic,m24eg-pn28240k", "realtek,rtl8382-soc";
model = "Panasonic Switch-M24eG PN28240K";
aliases {
led-boot = &led_status_eco_green;
led-failsafe = &led_status_eco_amber;
led-running = &led_status_eco_green;
led-upgrade = &led_status_eco_green;
};
/*
* sfp0/1 are "combo" port with each TP port (23/24), and they are
* connected to the RTL8218FB. Currently, there is no support for
* the chip and only TP ports work by the RTL8218D support.
*/
sfp0: sfp-p23 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
tx-fault-gpio = <&gpio1 0 GPIO_ACTIVE_HIGH>;
tx-disable-gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 2 GPIO_ACTIVE_LOW>;
los-gpio = <&gpio1 3 GPIO_ACTIVE_HIGH>;
};
sfp1: sfp-p24 {
compatible = "sff,sfp";
i2c-bus = <&i2c1>;
tx-fault-gpio = <&gpio1 4 GPIO_ACTIVE_HIGH>;
tx-disable-gpio = <&gpio1 5 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 6 GPIO_ACTIVE_LOW>;
los-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;
};
};
&leds {
led_status_eco_amber: led-5 {
label = "amber:status_eco";
gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_AMBER>;
function = LED_FUNCTION_STATUS;
function-enumerator = <1>;
};
led_status_eco_green: led-6 {
label = "green:status_eco";
gpios = <&gpio2 2 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_STATUS;
function-enumerator = <2>;
};
};
&i2c_gpio_0 {
scl-gpios = <&gpio0 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
sda-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
};
&i2c_gpio_1 {
scl-gpios = <&gpio0 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
sda-gpios = <&gpio0 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
};
&gpio2 {
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gpio0>;
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
/*
* GPIO12 (IO1_4): RTL8218B + RTL8218FB
*
* This GPIO pin should be specified as "reset-gpio" in mdio node,
* but the current configuration of RTL8218B phy in the phy driver
* seems to be incomplete and RTL8218FB phy won't be configured on
* RTL8218D support. So, ethernet ports on these phys will be broken
* after hard-resetting.
* (RTL8218FB phy will be detected as RTL8218D by the phy driver)
* At the moment, configure this GPIO pin as gpio-hog to avoid breaking
* by resetting.
*/
ext_switch_reset {
gpio-hog;
gpios = <12 GPIO_ACTIVE_HIGH>;
output-high;
line-name = "ext-switch-reset";
};
};
&i2c_switch {
i2c0: i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
};
i2c1: i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
};
};
&ethernet0 {
mdio-bus {
compatible = "realtek,rtl838x-mdio";
#address-cells = <1>;
#size-cells = <0>;
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
INTERNAL_PHY(8)
INTERNAL_PHY(9)
INTERNAL_PHY(10)
INTERNAL_PHY(11)
INTERNAL_PHY(12)
INTERNAL_PHY(13)
INTERNAL_PHY(14)
INTERNAL_PHY(15)
/* RTL8218FB */
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(0, 1, qsgmii)
SWITCH_PORT(1, 2, qsgmii)
SWITCH_PORT(2, 3, qsgmii)
SWITCH_PORT(3, 4, qsgmii)
SWITCH_PORT(4, 5, qsgmii)
SWITCH_PORT(5, 6, qsgmii)
SWITCH_PORT(6, 7, qsgmii)
SWITCH_PORT(7, 8, qsgmii)
SWITCH_PORT(8, 9, internal)
SWITCH_PORT(9, 10, internal)
SWITCH_PORT(10, 11, internal)
SWITCH_PORT(11, 12, internal)
SWITCH_PORT(12, 13, internal)
SWITCH_PORT(13, 14, internal)
SWITCH_PORT(14, 15, internal)
SWITCH_PORT(15, 16, internal)
SWITCH_PORT(16, 17, qsgmii)
SWITCH_PORT(17, 18, qsgmii)
SWITCH_PORT(18, 19, qsgmii)
SWITCH_PORT(19, 20, qsgmii)
SWITCH_PORT(20, 21, qsgmii)
SWITCH_PORT(21, 22, qsgmii)
SWITCH_PORT(22, 23, qsgmii)
SWITCH_PORT(23, 24, qsgmii)
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_zyxel_gs1900.dtsi"
/ {
compatible = "zyxel,gs1900-16", "realtek,rtl838x-soc";
model = "ZyXEL GS1900-16";
};
&mdio {
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
};
&switch0 {
ports {
SWITCH_PORT(16, 9, qsgmii)
SWITCH_PORT(17, 10, qsgmii)
SWITCH_PORT(18, 11, qsgmii)
SWITCH_PORT(19, 12, qsgmii)
SWITCH_PORT(20, 13, qsgmii)
SWITCH_PORT(21, 14, qsgmii)
SWITCH_PORT(22, 15, qsgmii)
SWITCH_PORT(23, 16, qsgmii)
};
};
&gpio1 {
/delete-node/ poe_enable;
};

View file

@ -0,0 +1,128 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_zyxel_gs1900.dtsi"
/ {
compatible = "zyxel,gs1900-24-v1", "realtek,rtl838x-soc";
model = "ZyXEL GS1900-24 v1";
memory@0 {
reg = <0x0 0x4000000>;
};
/* i2c of the left SFP cage: port 25 */
i2c0: i2c-gpio-0 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp0: sfp-p25 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
los-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>;
tx-fault-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 26 GPIO_ACTIVE_LOW>;
tx-disable-gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>;
};
/* i2c of the right SFP cage: port 26 */
i2c1: i2c-gpio-1 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp1: sfp-p26 {
compatible = "sff,sfp";
i2c-bus = <&i2c1>;
los-gpio = <&gpio1 33 GPIO_ACTIVE_HIGH>;
tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 32 GPIO_ACTIVE_LOW>;
tx-disable-gpio = <&gpio1 29 GPIO_ACTIVE_HIGH>;
};
};
&uart1 {
status = "okay";
};
&mdio {
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
INTERNAL_PHY(24)
INTERNAL_PHY(26)
};
&switch0 {
ports {
SWITCH_PORT(0, 1, qsgmii)
SWITCH_PORT(1, 2, qsgmii)
SWITCH_PORT(2, 3, qsgmii)
SWITCH_PORT(3, 4, qsgmii)
SWITCH_PORT(4, 5, qsgmii)
SWITCH_PORT(5, 6, qsgmii)
SWITCH_PORT(6, 7, qsgmii)
SWITCH_PORT(7, 8, qsgmii)
SWITCH_PORT(8, 9, internal)
SWITCH_PORT(9, 10, internal)
SWITCH_PORT(10, 11, internal)
SWITCH_PORT(11, 12, internal)
SWITCH_PORT(12, 13, internal)
SWITCH_PORT(13, 14, internal)
SWITCH_PORT(14, 15, internal)
SWITCH_PORT(15, 16, internal)
SWITCH_PORT(16, 17, qsgmii)
SWITCH_PORT(17, 18, qsgmii)
SWITCH_PORT(18, 19, qsgmii)
SWITCH_PORT(19, 20, qsgmii)
SWITCH_PORT(20, 21, qsgmii)
SWITCH_PORT(21, 22, qsgmii)
SWITCH_PORT(22, 23, qsgmii)
SWITCH_PORT(23, 24, qsgmii)
port@24 {
reg = <24>;
label = "lan25";
phy-mode = "1000base-x";
managed = "in-band-status";
sfp = <&sfp0>;
};
port@26 {
reg = <26>;
label = "lan26";
phy-mode = "1000base-x";
managed = "in-band-status";
sfp = <&sfp1>;
};
};
};
&gpio1 {
/delete-node/ poe_enable;
};

View file

@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_zyxel_gs1900.dtsi"
/ {
compatible = "zyxel,gs1900-24e", "realtek,rtl838x-soc";
model = "ZyXEL GS1900-24E";
};
&mdio {
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
};
&switch0 {
ports {
SWITCH_PORT(1, 1, qsgmii)
SWITCH_PORT(0, 2, qsgmii)
SWITCH_PORT(3, 3, qsgmii)
SWITCH_PORT(2, 4, qsgmii)
SWITCH_PORT(5, 5, qsgmii)
SWITCH_PORT(4, 6, qsgmii)
SWITCH_PORT(7, 7, qsgmii)
SWITCH_PORT(6, 8, qsgmii)
SWITCH_PORT(9, 9, internal)
SWITCH_PORT(8, 10, internal)
SWITCH_PORT(11, 11, internal)
SWITCH_PORT(10, 12, internal)
SWITCH_PORT(13, 13, internal)
SWITCH_PORT(12, 14, internal)
SWITCH_PORT(15, 15, internal)
SWITCH_PORT(14, 16, internal)
SWITCH_PORT(17, 17, qsgmii)
SWITCH_PORT(16, 18, qsgmii)
SWITCH_PORT(19, 19, qsgmii)
SWITCH_PORT(18, 20, qsgmii)
SWITCH_PORT(21, 21, qsgmii)
SWITCH_PORT(20, 22, qsgmii)
SWITCH_PORT(23, 23, qsgmii)
SWITCH_PORT(22, 24, qsgmii)
};
};
&gpio1 {
/delete-node/ poe_enable;
};

View file

@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_zyxel_gs1900.dtsi"
/ {
compatible = "zyxel,gs1900-24hp-v1", "realtek,rtl838x-soc";
model = "ZyXEL GS1900-24HP v1";
memory@0 {
reg = <0x0 0x4000000>;
};
/* i2c of the left SFP cage: port 25 */
i2c0: i2c-gpio-0 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp0: sfp-p25 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
los-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>;
tx-fault-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 26 GPIO_ACTIVE_LOW>;
tx-disable-gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>;
};
/* i2c of the right SFP cage: port 26 */
i2c1: i2c-gpio-1 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp1: sfp-p26 {
compatible = "sff,sfp";
i2c-bus = <&i2c1>;
los-gpio = <&gpio1 33 GPIO_ACTIVE_HIGH>;
tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 32 GPIO_ACTIVE_LOW>;
tx-disable-gpio = <&gpio1 29 GPIO_ACTIVE_HIGH>;
};
};
&uart1 {
status = "okay";
};
&mdio {
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
INTERNAL_PHY(24)
INTERNAL_PHY(26)
};
&switch0 {
ports {
SWITCH_PORT(0, 1, qsgmii)
SWITCH_PORT(1, 2, qsgmii)
SWITCH_PORT(2, 3, qsgmii)
SWITCH_PORT(3, 4, qsgmii)
SWITCH_PORT(4, 5, qsgmii)
SWITCH_PORT(5, 6, qsgmii)
SWITCH_PORT(6, 7, qsgmii)
SWITCH_PORT(7, 8, qsgmii)
SWITCH_PORT(8, 9, internal)
SWITCH_PORT(9, 10, internal)
SWITCH_PORT(10, 11, internal)
SWITCH_PORT(11, 12, internal)
SWITCH_PORT(12, 13, internal)
SWITCH_PORT(13, 14, internal)
SWITCH_PORT(14, 15, internal)
SWITCH_PORT(15, 16, internal)
SWITCH_PORT(16, 17, qsgmii)
SWITCH_PORT(17, 18, qsgmii)
SWITCH_PORT(18, 19, qsgmii)
SWITCH_PORT(19, 20, qsgmii)
SWITCH_PORT(20, 21, qsgmii)
SWITCH_PORT(21, 22, qsgmii)
SWITCH_PORT(22, 23, qsgmii)
SWITCH_PORT(23, 24, qsgmii)
port@24 {
reg = <24>;
label = "lan25";
phy-mode = "1000base-x";
managed = "in-band-status";
sfp = <&sfp0>;
};
port@26 {
reg = <26>;
label = "lan26";
phy-mode = "1000base-x";
managed = "in-band-status";
sfp = <&sfp1>;
};
};
};

View file

@ -0,0 +1,121 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "rtl8380_zyxel_gs1900.dtsi"
/ {
compatible = "zyxel,gs1900-24hp-v2", "realtek,rtl838x-soc";
model = "ZyXEL GS1900-24HP v2 Switch";
/* i2c of the left SFP cage: port 25 */
i2c0: i2c-gpio-0 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp0: sfp-p25 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
los-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>;
tx-fault-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 26 GPIO_ACTIVE_LOW>;
tx-disable-gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>;
};
/* i2c of the right SFP cage: port 26 */
i2c1: i2c-gpio-1 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp1: sfp-p26 {
compatible = "sff,sfp";
i2c-bus = <&i2c1>;
los-gpio = <&gpio1 33 GPIO_ACTIVE_HIGH>;
tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 32 GPIO_ACTIVE_LOW>;
tx-disable-gpio = <&gpio1 29 GPIO_ACTIVE_HIGH>;
};
};
&uart1 {
status = "okay";
};
&mdio {
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
INTERNAL_PHY(24)
INTERNAL_PHY(26)
};
&switch0 {
ports {
SWITCH_PORT(0, 1, qsgmii)
SWITCH_PORT(1, 2, qsgmii)
SWITCH_PORT(2, 3, qsgmii)
SWITCH_PORT(3, 4, qsgmii)
SWITCH_PORT(4, 5, qsgmii)
SWITCH_PORT(5, 6, qsgmii)
SWITCH_PORT(6, 7, qsgmii)
SWITCH_PORT(7, 8, qsgmii)
SWITCH_PORT(8, 9, internal)
SWITCH_PORT(9, 10, internal)
SWITCH_PORT(10, 11, internal)
SWITCH_PORT(11, 12, internal)
SWITCH_PORT(12, 13, internal)
SWITCH_PORT(13, 14, internal)
SWITCH_PORT(14, 15, internal)
SWITCH_PORT(15, 16, internal)
SWITCH_PORT(16, 17, qsgmii)
SWITCH_PORT(17, 18, qsgmii)
SWITCH_PORT(18, 19, qsgmii)
SWITCH_PORT(19, 20, qsgmii)
SWITCH_PORT(20, 21, qsgmii)
SWITCH_PORT(21, 22, qsgmii)
SWITCH_PORT(22, 23, qsgmii)
SWITCH_PORT(23, 24, qsgmii)
port@24 {
reg = <24>;
label = "lan25";
phy-mode = "1000base-x";
managed = "in-band-status";
sfp = <&sfp0>;
};
port@26 {
reg = <26>;
label = "lan26";
phy-mode = "1000base-x";
managed = "in-band-status";
sfp = <&sfp1>;
};
};
};

View file

@ -0,0 +1,287 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include <dt-bindings/clock/rtl83xx-clk.h>
/dts-v1/;
#define STRINGIZE(s) #s
#define LAN_LABEL(p, s) STRINGIZE(p ## s)
#define SWITCH_PORT_LABEL(n) LAN_LABEL(lan, n)
#define INTERNAL_PHY(n) \
phy##n: ethernet-phy@##n { \
reg = <##n>; \
compatible = "ethernet-phy-ieee802.3-c22"; \
phy-is-integrated; \
};
#define EXTERNAL_PHY(n) \
phy##n: ethernet-phy@##n { \
reg = <##n>; \
compatible = "ethernet-phy-ieee802.3-c22"; \
};
#define EXTERNAL_SFP_PHY(n) \
phy##n: ethernet-phy@##n { \
compatible = "ethernet-phy-ieee802.3-c22"; \
sfp; \
media = "fibre"; \
reg = <##n>; \
};
#define EXTERNAL_SFP_PHY_FULL(n, s) \
phy##n: ethernet-phy@##n { \
compatible = "ethernet-phy-ieee802.3-c22"; \
sfp = <&sfp##s>; \
reg = <##n>; \
};
#define SWITCH_PORT(n, s, m) \
port##n: port@##n { \
reg = <##n>; \
label = SWITCH_PORT_LABEL(s) ; \
phy-handle = <&phy##n>; \
phy-mode = #m ; \
};
#define SWITCH_SFP_PORT(n, s, m) \
port##n: port@##n { \
reg = <##n>; \
label = SWITCH_PORT_LABEL(s) ; \
phy-handle = <&phy##n>; \
phy-mode = #m ; \
fixed-link { \
speed = <1000>; \
full-duplex; \
}; \
};
/ {
#address-cells = <1>;
#size-cells = <1>;
compatible = "realtek,rtl838x-soc";
osc: oscillator {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <25000000>;
};
ccu: clock-controller {
compatible = "realtek,rtl8380-clock";
#clock-cells = <1>;
clocks = <&osc>;
clock-names = "ref_clk";
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "mips,mips4KEc";
reg = <0>;
clocks = <&ccu CLK_CPU>;
operating-points-v2 = <&cpu_opp_table>;
};
};
cpu_opp_table: opp-table-0 {
compatible = "operating-points-v2";
opp-shared;
opp00 {
opp-hz = /bits/ 64 <325000000>;
};
opp01 {
opp-hz = /bits/ 64 <350000000>;
};
opp02 {
opp-hz = /bits/ 64 <375000000>;
};
opp03 {
opp-hz = /bits/ 64 <400000000>;
};
opp04 {
opp-hz = /bits/ 64 <425000000>;
};
opp05 {
opp-hz = /bits/ 64 <450000000>;
};
opp06 {
opp-hz = /bits/ 64 <475000000>;
};
opp07 {
opp-hz = /bits/ 64 <500000000>;
};
};
chosen {
bootargs = "console=ttyS0,115200";
};
cpuintc: cpuintc {
compatible = "mti,cpu-interrupt-controller";
#address-cells = <0>;
#interrupt-cells = <1>;
interrupt-controller;
};
soc: soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0x18000000 0x10000>;
intc: interrupt-controller@3000 {
compatible = "realtek,rtl8380-intc", "realtek,rtl-intc";
reg = <0x3000 0x18>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&cpuintc>;
interrupts = <2>, <3>, <4>, <5>, <6>;
};
spi0: spi@1200 {
compatible = "realtek,rtl8380-spi";
reg = <0x1200 0x100>;
#address-cells = <1>;
#size-cells = <0>;
};
timer0: timer@3100 {
compatible = "realtek,rtl8380-timer", "realtek,otto-timer";
reg = <0x3100 0x10>, <0x3110 0x10>, <0x3120 0x10>,
<0x3130 0x10>, <0x3140 0x10>;
interrupt-parent = <&intc>;
interrupts = <29 4>, <28 4>, <17 4>, <16 4>, <15 4>;
clocks = <&ccu CLK_LXB>;
};
uart0: uart@2000 {
compatible = "ns16550a";
reg = <0x2000 0x100>;
clocks = <&ccu CLK_LXB>;
interrupt-parent = <&intc>;
interrupts = <31 1>;
reg-io-width = <1>;
reg-shift = <2>;
fifo-size = <1>;
no-loopback-test;
};
uart1: uart@2100 {
pinctrl-names = "default";
pinctrl-0 = <&enable_uart1>;
compatible = "ns16550a";
reg = <0x2100 0x100>;
clocks = <&ccu CLK_LXB>;
interrupt-parent = <&intc>;
interrupts = <30 0>;
reg-io-width = <1>;
reg-shift = <2>;
fifo-size = <1>;
no-loopback-test;
status = "disabled";
};
watchdog0: watchdog@3150 {
compatible = "realtek,rtl8380-wdt";
reg = <0x3150 0xc>;
realtek,reset-mode = "soc";
clocks = <&ccu CLK_LXB>;
timeout-sec = <30>;
interrupt-parent = <&intc>;
interrupt-names = "phase1", "phase2";
interrupts = <19 3>, <18 4>;
};
gpio0: gpio-controller@3500 {
compatible = "realtek,rtl8380-gpio", "realtek,otto-gpio";
reg = <0x3500 0x20>;
gpio-controller;
#gpio-cells = <2>;
ngpios = <24>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&intc>;
interrupts = <23 3>;
};
};
pinmux: pinmux@1b001000 {
compatible = "pinctrl-single";
reg = <0x1b001000 0x4>;
pinctrl-single,bit-per-mux;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <0x1>;
#pinctrl-cells = <2>;
enable_uart1: pinmux_enable_uart1 {
pinctrl-single,bits = <0x0 0x10 0x10>;
};
};
/* LED_GLB_CTRL */
pinmux_led: pinmux@1b00a000 {
compatible = "pinctrl-single";
reg = <0x1b00a000 0x4>;
pinctrl-single,bit-per-mux;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <0x1>;
#pinctrl-cells = <2>;
/* enable GPIO 0 */
pinmux_disable_sys_led: disable_sys_led {
pinctrl-single,bits = <0x0 0x0 0x8000>;
};
};
ethernet0: ethernet@1b00a300 {
compatible = "realtek,rtl838x-eth";
reg = <0x1b00a300 0x100>;
interrupt-parent = <&intc>;
interrupts = <24 3>;
#interrupt-cells = <1>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
sram0: sram@9f000000 {
compatible = "mmio-sram";
reg = <0x9f000000 0x10000>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x9f000000 0x10000>;
};
switch0: switch@1b000000 {
compatible = "realtek,rtl83xx-switch";
interrupt-parent = <&intc>;
interrupts = <20 2>;
};
};

View file

@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
/ {
chosen {
bootargs = "console=ttyS0,38400";
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;
};
watchdog1: watchdog {
// PT7A7514
compatible = "linux,wdt-gpio";
gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
hw_algo = "toggle";
hw_margin_ms = <1000>;
always-running;
pinctrl-names = "default";
pinctrl-0 = <&pinmux_disable_sys_led>;
};
};
&watchdog0 {
status = "disabled";
};
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <50000000>;
m25p,fast-read;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "bootware_basic";
reg = <0x0 0x50000>;
read-only;
};
partition@0x60000 {
label = "bootware_data";
reg = <0x60000 0x30000>;
read-only;
};
partition@0x90000 {
label = "bootware_extend";
reg = <0x90000 0x40000>;
read-only;
};
partition@0x100000 {
label = "bootware_basic_backup";
reg = <0x100000 0x50000>;
read-only;
};
partition@0x160000 {
label = "bootware_data_backup";
reg = <0x160000 0x30000>;
read-only;
};
partition@0x190000 {
label = "bootware_extend_backup";
reg = <0x190000 0x40000>;
read-only;
};
partition@0x300000 {
label = "firmware";
compatible = "h3c,vfs-firmware";
reg = <0x300000 0x1cf0000>;
};
partition@0x1ff0000 {
label = "factory";
reg = <0x1ff0000 0x10000>;
read-only;
};
};
};
};

View file

@ -0,0 +1,163 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl839x.dtsi"
#include "rtl83xx_d-link_dgs-1210_common.dtsi"
#include "rtl83xx_d-link_dgs-1210_gpio.dtsi"
#include "rtl839x_d-link_dgs-1210_gpio.dtsi"
/ {
compatible = "d-link,dgs-1210-52", "realtek,rtl8393-soc";
model = "D-Link DGS-1210-52";
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
/* External phy RTL8218B #1 */
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
/* External phy RTL8218B #2 */
EXTERNAL_PHY(8)
EXTERNAL_PHY(9)
EXTERNAL_PHY(10)
EXTERNAL_PHY(11)
EXTERNAL_PHY(12)
EXTERNAL_PHY(13)
EXTERNAL_PHY(14)
EXTERNAL_PHY(15)
/* External phy RTL8218B #3 */
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
/* External phy RTL8218B #4 */
EXTERNAL_PHY(24)
EXTERNAL_PHY(25)
EXTERNAL_PHY(26)
EXTERNAL_PHY(27)
EXTERNAL_PHY(28)
EXTERNAL_PHY(29)
EXTERNAL_PHY(30)
EXTERNAL_PHY(31)
/* External phy RTL8218B #5 */
EXTERNAL_PHY(32)
EXTERNAL_PHY(33)
EXTERNAL_PHY(34)
EXTERNAL_PHY(35)
EXTERNAL_PHY(36)
EXTERNAL_PHY(37)
EXTERNAL_PHY(38)
EXTERNAL_PHY(39)
/* External phy RTL8218B #6 */
EXTERNAL_PHY(40)
EXTERNAL_PHY(41)
EXTERNAL_PHY(42)
EXTERNAL_PHY(43)
EXTERNAL_PHY(44)
EXTERNAL_PHY(45)
EXTERNAL_PHY(46)
EXTERNAL_PHY(47)
/* External phy RTL8214FC */
EXTERNAL_SFP_PHY_FULL(48, 0)
EXTERNAL_SFP_PHY_FULL(49, 1)
EXTERNAL_SFP_PHY_FULL(50, 2)
EXTERNAL_SFP_PHY_FULL(51, 3)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(0, 1, qsgmii)
SWITCH_PORT(1, 2, qsgmii)
SWITCH_PORT(2, 3, qsgmii)
SWITCH_PORT(3, 4, qsgmii)
SWITCH_PORT(4, 5, qsgmii)
SWITCH_PORT(5, 6, qsgmii)
SWITCH_PORT(6, 7, qsgmii)
SWITCH_PORT(7, 8, qsgmii)
SWITCH_PORT(8, 9, qsgmii)
SWITCH_PORT(9, 10, qsgmii)
SWITCH_PORT(10, 11, qsgmii)
SWITCH_PORT(11, 12, qsgmii)
SWITCH_PORT(12, 13, qsgmii)
SWITCH_PORT(13, 14, qsgmii)
SWITCH_PORT(14, 15, qsgmii)
SWITCH_PORT(15, 16, qsgmii)
SWITCH_PORT(16, 17, qsgmii)
SWITCH_PORT(17, 18, qsgmii)
SWITCH_PORT(18, 19, qsgmii)
SWITCH_PORT(19, 20, qsgmii)
SWITCH_PORT(20, 21, qsgmii)
SWITCH_PORT(21, 22, qsgmii)
SWITCH_PORT(22, 23, qsgmii)
SWITCH_PORT(23, 24, qsgmii)
SWITCH_PORT(24, 25, qsgmii)
SWITCH_PORT(25, 26, qsgmii)
SWITCH_PORT(26, 27, qsgmii)
SWITCH_PORT(27, 28, qsgmii)
SWITCH_PORT(28, 29, qsgmii)
SWITCH_PORT(29, 30, qsgmii)
SWITCH_PORT(30, 31, qsgmii)
SWITCH_PORT(31, 32, qsgmii)
SWITCH_PORT(32, 33, qsgmii)
SWITCH_PORT(33, 34, qsgmii)
SWITCH_PORT(34, 35, qsgmii)
SWITCH_PORT(35, 36, qsgmii)
SWITCH_PORT(36, 37, qsgmii)
SWITCH_PORT(37, 38, qsgmii)
SWITCH_PORT(38, 39, qsgmii)
SWITCH_PORT(39, 40, qsgmii)
SWITCH_PORT(40, 41, qsgmii)
SWITCH_PORT(41, 42, qsgmii)
SWITCH_PORT(42, 43, qsgmii)
SWITCH_PORT(43, 44, qsgmii)
SWITCH_PORT(44, 45, qsgmii)
SWITCH_PORT(45, 46, qsgmii)
SWITCH_PORT(46, 47, qsgmii)
SWITCH_PORT(47, 48, qsgmii)
SWITCH_PORT(48, 49, qsgmii)
SWITCH_PORT(49, 50, qsgmii)
SWITCH_PORT(50, 51, qsgmii)
SWITCH_PORT(51, 52, qsgmii)
/* CPU-Port */
port@52 {
ethernet = <&ethernet0>;
reg = <52>;
phy-mode = "qsgmii";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,380 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl839x.dtsi"
#include "rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi"
#include <dt-bindings/interrupt-controller/irq.h>
/ {
compatible = "panasonic,m48eg-pn28480k", "realtek,rtl8393-soc";
model = "Panasonic Switch-M48eG PN28480K";
aliases {
led-boot = &led_status_eco_green;
led-failsafe = &led_status_eco_amber;
led-running = &led_status_eco_green;
led-upgrade = &led_status_eco_green;
};
fan: gpio-fan {
compatible = "gpio-fan";
gpios = <&gpio0 19 GPIO_ACTIVE_HIGH>;
/* the actual speeds (rpm) are unknown, just use dummy values */
gpio-fan,speed-map = <1 0>, <2 1>;
#cooling-cells = <2>;
};
/*
* sfp0/1/2/3 are "combo" port with each TP port (45/46/47/48),
* and they are connected to the RTL8218FB. Currently, there is
* no support for the chip and only TP ports work by the RTL8218B
* support.
*/
sfp0: sfp-p45 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
tx-fault-gpio = <&gpio1 0 GPIO_ACTIVE_HIGH>;
tx-disable-gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 2 GPIO_ACTIVE_LOW>;
los-gpio = <&gpio1 3 GPIO_ACTIVE_HIGH>;
};
sfp1: sfp-p46 {
compatible = "sff,sfp";
i2c-bus = <&i2c1>;
tx-fault-gpio = <&gpio1 4 GPIO_ACTIVE_HIGH>;
tx-disable-gpio = <&gpio1 5 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 6 GPIO_ACTIVE_LOW>;
los-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>;
};
sfp2: sfp-p47 {
compatible = "sff,sfp";
i2c-bus = <&i2c2>;
tx-fault-gpio = <&gpio1 8 GPIO_ACTIVE_HIGH>;
tx-disable-gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 10 GPIO_ACTIVE_LOW>;
los-gpio = <&gpio1 11 GPIO_ACTIVE_HIGH>;
};
sfp3: sfp-p48 {
compatible = "sff,sfp";
i2c-bus = <&i2c3>;
tx-fault-gpio = <&gpio1 12 GPIO_ACTIVE_HIGH>;
tx-disable-gpio = <&gpio1 13 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 14 GPIO_ACTIVE_LOW>;
los-gpio = <&gpio1 15 GPIO_ACTIVE_HIGH>;
};
thermal-zones {
/*
* Zone for SoC temperature
*
* Fan speed:
*
* - 0-44 celsius: Low
* - 45-54 celsius: High
*/
cpu-thermal {
polling-delay-passive = <1000>;
polling-delay = <2000>;
thermal-sensors = <&tsens_soc>;
trips {
cpu_alert: trip-point {
temperature = <45000>;
hysteresis = <4000>;
type = "active";
};
cpu_crit {
temperature = <55000>;
hysteresis = <1000>;
type = "critical";
};
};
cooling-maps {
map {
trip = <&cpu_alert>;
cooling-device = <&fan 0 1>;
};
};
};
/*
* Zone for system temperature
*
* Fan speed:
*
* - 0-39 celsius: Low
* - 40-49 celsius: High
*
* Note: official recommended ranges of temperature on each
* fan speed setting:
*
* - Low speed : 0-40 celsius
* - High speed: 0-50 celsius
*
* (stock firmware doesn't support auto-selection of
* speed and need to be selected manually by user)
*/
sys-thermal {
polling-delay-passive = <1000>;
polling-delay = <2000>;
thermal-sensors = <&tsens_sys>;
trips {
sys_alert: trip-point {
temperature = <40000>;
hysteresis = <4000>;
type = "active";
};
sys_crit {
temperature = <50000>;
hysteresis = <2000>;
type = "critical";
};
};
cooling-maps {
map {
trip = <&sys_alert>;
cooling-device = <&fan 0 1>;
};
};
};
};
};
&leds {
led_status_eco_amber: led-5 {
label = "amber:status_eco";
gpios = <&gpio2 1 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_AMBER>;
function = LED_FUNCTION_STATUS;
function-enumerator = <1>;
};
led_status_eco_green: led-6 {
label = "green:status_eco";
gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_STATUS;
function-enumerator = <2>;
};
};
&i2c_gpio_0 {
scl-gpios = <&gpio0 16 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
sda-gpios = <&gpio0 17 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
/* Microchip TCN75A (for SoC) */
tsens_soc: sensor@48 {
compatible = "microchip,tcn75";
reg = <0x48>;
#thermal-sensor-cells = <0>;
};
/* Microchip TCN75A (for System) */
tsens_sys: sensor@49 {
compatible = "microchip,tcn75";
reg = <0x49>;
#thermal-sensor-cells = <0>;
};
};
&i2c_gpio_1 {
scl-gpios = <&gpio0 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
sda-gpios = <&gpio0 15 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
};
&gpio2 {
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gpio0>;
interrupts = <18 IRQ_TYPE_EDGE_FALLING>;
/*
* GPIO12 (IO1_4): 5x RTL8218B + RTL8218FB
*
* This GPIO pin should be specified as "reset-gpio" in mdio node,
* but the current configuration of RTL8218B phy in the phy driver
* seems to be incomplete and RTL8218FB phy won't be configured on
* RTL8218D support. So, ethernet ports on these phys will be broken
* after hard-resetting.
* (RTL8218FB phy will be detected as RTL8218D by the phy driver)
* At the moment, configure this GPIO pin as gpio-hog to avoid breaking
* by resetting.
*/
ext_switch_reset {
gpio-hog;
gpios = <12 GPIO_ACTIVE_HIGH>;
output-high;
line-name = "ext-switch-reset";
};
};
&i2c_switch {
i2c0: i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
};
i2c1: i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
};
i2c2: i2c@2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <2>;
};
i2c3: i2c@3 {
#address-cells = <1>;
#size-cells = <0>;
reg = <3>;
};
};
&ethernet0 {
mdio-bus {
compatible = "realtek,rtl838x-mdio";
#address-cells = <1>;
#size-cells = <0>;
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
EXTERNAL_PHY(8)
EXTERNAL_PHY(9)
EXTERNAL_PHY(10)
EXTERNAL_PHY(11)
EXTERNAL_PHY(12)
EXTERNAL_PHY(13)
EXTERNAL_PHY(14)
EXTERNAL_PHY(15)
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
EXTERNAL_PHY(24)
EXTERNAL_PHY(25)
EXTERNAL_PHY(26)
EXTERNAL_PHY(27)
EXTERNAL_PHY(28)
EXTERNAL_PHY(29)
EXTERNAL_PHY(30)
EXTERNAL_PHY(31)
EXTERNAL_PHY(32)
EXTERNAL_PHY(33)
EXTERNAL_PHY(34)
EXTERNAL_PHY(35)
EXTERNAL_PHY(36)
EXTERNAL_PHY(37)
EXTERNAL_PHY(38)
EXTERNAL_PHY(39)
/* RTL8218FB */
EXTERNAL_PHY(40)
EXTERNAL_PHY(41)
EXTERNAL_PHY(42)
EXTERNAL_PHY(43)
EXTERNAL_PHY(44)
EXTERNAL_PHY(45)
EXTERNAL_PHY(46)
EXTERNAL_PHY(47)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(0, 1, qsgmii)
SWITCH_PORT(1, 2, qsgmii)
SWITCH_PORT(2, 3, qsgmii)
SWITCH_PORT(3, 4, qsgmii)
SWITCH_PORT(4, 5, qsgmii)
SWITCH_PORT(5, 6, qsgmii)
SWITCH_PORT(6, 7, qsgmii)
SWITCH_PORT(7, 8, qsgmii)
SWITCH_PORT(8, 9, qsgmii)
SWITCH_PORT(9, 10, qsgmii)
SWITCH_PORT(10, 11, qsgmii)
SWITCH_PORT(11, 12, qsgmii)
SWITCH_PORT(12, 13, qsgmii)
SWITCH_PORT(13, 14, qsgmii)
SWITCH_PORT(14, 15, qsgmii)
SWITCH_PORT(15, 16, qsgmii)
SWITCH_PORT(16, 17, qsgmii)
SWITCH_PORT(17, 18, qsgmii)
SWITCH_PORT(18, 19, qsgmii)
SWITCH_PORT(19, 20, qsgmii)
SWITCH_PORT(20, 21, qsgmii)
SWITCH_PORT(21, 22, qsgmii)
SWITCH_PORT(22, 23, qsgmii)
SWITCH_PORT(23, 24, qsgmii)
SWITCH_PORT(24, 25, qsgmii)
SWITCH_PORT(25, 26, qsgmii)
SWITCH_PORT(26, 27, qsgmii)
SWITCH_PORT(27, 28, qsgmii)
SWITCH_PORT(28, 29, qsgmii)
SWITCH_PORT(29, 30, qsgmii)
SWITCH_PORT(30, 31, qsgmii)
SWITCH_PORT(31, 32, qsgmii)
SWITCH_PORT(32, 33, qsgmii)
SWITCH_PORT(33, 34, qsgmii)
SWITCH_PORT(34, 35, qsgmii)
SWITCH_PORT(35, 36, qsgmii)
SWITCH_PORT(36, 37, qsgmii)
SWITCH_PORT(37, 38, qsgmii)
SWITCH_PORT(38, 39, qsgmii)
SWITCH_PORT(39, 40, qsgmii)
SWITCH_PORT(40, 41, qsgmii)
SWITCH_PORT(41, 42, qsgmii)
SWITCH_PORT(42, 43, qsgmii)
SWITCH_PORT(43, 44, qsgmii)
SWITCH_PORT(44, 45, qsgmii)
SWITCH_PORT(45, 46, qsgmii)
SWITCH_PORT(46, 47, qsgmii)
SWITCH_PORT(47, 48, qsgmii)
port@52 {
ethernet = <&ethernet0>;
reg = <52>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,422 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "rtl839x.dtsi"
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/leds/common.h>
/ {
compatible = "tplink,sg2452p-v4", "realtek,rtl8393-soc";
model = "TP-Link SG2452P v4";
memory@0 {
device_type = "memory";
reg = <0x0 0x10000000>;
};
aliases {
led-boot = &led_sys;
led-failsafe = &led_sys;
led-running = &led_sys;
led-upgrade = &led_sys;
label-mac-device = &ethernet0;
};
chosen {
bootargs = "console=ttyS0,38400";
};
keys {
compatible = "gpio-keys";
reset {
label = "reset";
gpios = <&gpio0 20 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
speed {
label = "speed";
gpios = <&gpio0 19 GPIO_ACTIVE_LOW>;
linux,code = <BTN_0>;
};
};
gpio_fan_sys {
compatible = "gpio-fan";
alarm-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
};
gpio_fan_psu_1 {
pinctrl-names = "default";
pinctrl-0 = <&disable_jtag>;
compatible = "gpio-fan";
alarm-gpios = <&gpio0 7 GPIO_ACTIVE_LOW>;
gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
/* the actual speeds (rpm) are unknown, just use dummy values */
gpio-fan,speed-map = <1 0>, <2 1>;
#cooling-cells = <2>;
};
gpio_fan_psu_2 {
/* This fan runs in parallel to PSU1 fan, but has a separate
* alarm GPIO. This is not (yet) supported by the gpio-fan driver,
* so a separate instance is added
*/
compatible = "gpio-fan";
alarm-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
};
leds {
pinctrl-names = "default";
compatible = "gpio-leds";
led-0 {
label = "green:speed";
gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_INDICATOR;
};
led-1 {
label = "green:poe";
gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_INDICATOR;
};
led_sys: led-2 {
label = "green:sys";
gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_STATUS;
};
led-3 {
label = "green:fan";
gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_STATUS;
};
led-4 {
label = "amber:fan";
gpios = <&gpio0 16 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_AMBER>;
function = "fault-fan";
};
led-5 {
label = "green:poe-max";
gpios = <&gpio0 18 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_GREEN>;
function = "alarm-poe";
};
};
i2c-gpio-0 {
compatible = "i2c-gpio";
sda-gpios = <&gpio0 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
/* LAN9 - LAN12 */
tps23861@5 {
compatible = "ti,tps23861";
reg = <0x05>;
};
/* LAN17 - LAN20 */
tps23861@6 {
compatible = "ti,tps23861";
reg = <0x06>;
};
/* LAN45 - LAN48 */
tps23861@9 {
compatible = "ti,tps23861";
reg = <0x09>;
};
/* LAN37 - LAN40 */
tps23861@a {
compatible = "ti,tps23861";
reg = <0x0a>;
};
/* LAN1 - LAN4 */
tps23861@14 {
compatible = "ti,tps23861";
reg = <0x14>;
};
/* LAN25 - LAN28 */
tps23861@24 {
compatible = "ti,tps23861";
reg = <0x24>;
};
/* LAN33 - LAN 36 */
tps23861@25 {
compatible = "ti,tps23861";
reg = <0x25>;
};
/* LAN41 - LAN44 */
tps23861@26 {
compatible = "ti,tps23861";
reg = <0x26>;
};
/* LAN13 - LAN16 */
tps23861@29 {
compatible = "ti,tps23861";
reg = <0x29>;
};
/* LAN29 - LAN32 */
tps23861@2c {
compatible = "ti,tps23861";
reg = <0x2c>;
};
/* LAN5 - LAN8 */
tps23861@48 {
compatible = "ti,tps23861";
reg = <0x48>;
};
/* LAN21 - LAN24 */
tps23861@49 {
compatible = "ti,tps23861";
reg = <0x49>;
};
};
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpio0 17 GPIO_ACTIVE_LOW>;
};
};
&gpio0 {
poe-enable {
gpio-hog;
gpios = <23 GPIO_ACTIVE_HIGH>;
output-high;
line-name = "poe-enable";
};
};
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0xe0000>;
read-only;
};
partition@e0000 {
label = "u-boot-env";
reg = <0xe0000 0x20000>;
};
/* We use the "sys", "usrimg1" and "usrimg2" partitions
* as firmware since the kernel needs to be in "sys", but the
* partition is too small to hold the "rootfs" as well.
* The original partition map contains:
*
* partition@100000 {
* label = "sys";
* reg = <0x100000 0x600000>;
* };
* partition@700000 {
* label = "usrimg1";
* reg = <0x700000 0xa00000>;
* };
* partition@1100000 {
* label = "usrimg2";
* reg = <0x1100000 0xa00000>;
* };
*/
partition@100000 {
label = "firmware";
reg = <0x100000 0x1a00000>;
};
partition@1b00000 {
label = "usrappfs";
reg = <0x1b00000 0x400000>;
};
partition@1f00000 {
compatible = "nvmem-cells";
label = "para";
reg = <0x1f00000 0x100000>;
#address-cells = <1>;
#size-cells = <1>;
read-only;
factory_macaddr: macaddr@fdff4 {
reg = <0xfdff4 0x6>;
};
};
};
};
};
&ethernet0 {
nvmem-cells = <&factory_macaddr>;
nvmem-cell-names = "mac-address";
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
#address-cells = <1>;
#size-cells = <0>;
/* External phy RTL8218B #1 */
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
/* External phy RTL8218B #2 */
EXTERNAL_PHY(8)
EXTERNAL_PHY(9)
EXTERNAL_PHY(10)
EXTERNAL_PHY(11)
EXTERNAL_PHY(12)
EXTERNAL_PHY(13)
EXTERNAL_PHY(14)
EXTERNAL_PHY(15)
/* External phy RTL8218B #3 */
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
/* External phy RTL8218B #4 */
EXTERNAL_PHY(24)
EXTERNAL_PHY(25)
EXTERNAL_PHY(26)
EXTERNAL_PHY(27)
EXTERNAL_PHY(28)
EXTERNAL_PHY(29)
EXTERNAL_PHY(30)
EXTERNAL_PHY(31)
/* External phy RTL8218B #5 */
EXTERNAL_PHY(32)
EXTERNAL_PHY(33)
EXTERNAL_PHY(34)
EXTERNAL_PHY(35)
EXTERNAL_PHY(36)
EXTERNAL_PHY(37)
EXTERNAL_PHY(38)
EXTERNAL_PHY(39)
/* External phy RTL8218B #6 */
EXTERNAL_PHY(40)
EXTERNAL_PHY(41)
EXTERNAL_PHY(42)
EXTERNAL_PHY(43)
EXTERNAL_PHY(44)
EXTERNAL_PHY(45)
EXTERNAL_PHY(46)
EXTERNAL_PHY(47)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(0, 01, qsgmii)
SWITCH_PORT(1, 02, qsgmii)
SWITCH_PORT(2, 03, qsgmii)
SWITCH_PORT(3, 04, qsgmii)
SWITCH_PORT(4, 05, qsgmii)
SWITCH_PORT(5, 06, qsgmii)
SWITCH_PORT(6, 07, qsgmii)
SWITCH_PORT(7, 08, qsgmii)
SWITCH_PORT(8, 09, qsgmii)
SWITCH_PORT(9, 10, qsgmii)
SWITCH_PORT(10, 11, qsgmii)
SWITCH_PORT(11, 12, qsgmii)
SWITCH_PORT(12, 13, qsgmii)
SWITCH_PORT(13, 14, qsgmii)
SWITCH_PORT(14, 15, qsgmii)
SWITCH_PORT(15, 16, qsgmii)
SWITCH_PORT(16, 17, qsgmii)
SWITCH_PORT(17, 18, qsgmii)
SWITCH_PORT(18, 19, qsgmii)
SWITCH_PORT(19, 20, qsgmii)
SWITCH_PORT(20, 21, qsgmii)
SWITCH_PORT(21, 22, qsgmii)
SWITCH_PORT(22, 23, qsgmii)
SWITCH_PORT(23, 24, qsgmii)
SWITCH_PORT(24, 25, qsgmii)
SWITCH_PORT(25, 26, qsgmii)
SWITCH_PORT(26, 27, qsgmii)
SWITCH_PORT(27, 28, qsgmii)
SWITCH_PORT(28, 29, qsgmii)
SWITCH_PORT(29, 30, qsgmii)
SWITCH_PORT(30, 31, qsgmii)
SWITCH_PORT(31, 32, qsgmii)
SWITCH_PORT(32, 33, qsgmii)
SWITCH_PORT(33, 34, qsgmii)
SWITCH_PORT(34, 35, qsgmii)
SWITCH_PORT(35, 36, qsgmii)
SWITCH_PORT(36, 37, qsgmii)
SWITCH_PORT(37, 38, qsgmii)
SWITCH_PORT(38, 39, qsgmii)
SWITCH_PORT(39, 40, qsgmii)
SWITCH_PORT(40, 41, qsgmii)
SWITCH_PORT(41, 42, qsgmii)
SWITCH_PORT(42, 43, qsgmii)
SWITCH_PORT(43, 44, qsgmii)
SWITCH_PORT(44, 45, qsgmii)
SWITCH_PORT(45, 46, qsgmii)
SWITCH_PORT(46, 47, qsgmii)
SWITCH_PORT(47, 48, qsgmii)
/* CPU-Port */
port@52 {
ethernet = <&ethernet0>;
reg = <52>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,320 @@
/dts-v1/;
#include "rtl839x.dtsi"
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
/ {
compatible = "zyxel,gs1900-48", "realtek,rtl8393-soc";
model = "Zyxel GS1900-48";
aliases {
led-boot = &led_sys;
led-failsafe = &led_sys;
led-running = &led_sys;
led-upgrade = &led_sys;
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;
};
leds {
pinctrl-names = "default";
pinctrl-0 = <&pinmux_disable_sys_led>;
compatible = "gpio-leds";
led_sys: sys {
label = "green:sys";
gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
};
};
gpio1: rtl8231-gpio {
compatible = "realtek,rtl8231-gpio";
#gpio-cells = <2>;
indirect-access-bus-id = <3>;
gpio-controller;
};
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
};
keys {
compatible = "gpio-keys-polled";
poll-interval = <20>;
mode {
label = "reset";
gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
};
/* i2c of the left SFP cage: port 49 */
i2c0: i2c-gpio-0 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp0: sfp-p9 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
los-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>;
tx-fault-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 26 GPIO_ACTIVE_LOW>;
tx-disable-gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>;
};
/* i2c of the right SFP cage: port 50 */
i2c1: i2c-gpio-1 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp1: sfp-p10 {
compatible = "sff,sfp";
i2c-bus = <&i2c1>;
los-gpio = <&gpio1 33 GPIO_ACTIVE_HIGH>;
tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 32 GPIO_ACTIVE_LOW>;
tx-disable-gpio = <&gpio1 29 GPIO_ACTIVE_HIGH>;
};
};
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x40000>;
read-only;
};
partition@40000 {
label = "u-boot-env";
reg = <0x40000 0x10000>;
read-only;
};
partition@50000 {
label = "u-boot-env2";
reg = <0x50000 0x10000>;
read-only;
};
partition@60000 {
label = "jffs";
reg = <0x60000 0x100000>;
};
partition@160000 {
label = "jffs2";
reg = <0x160000 0x100000>;
};
partition@b260000 {
label = "firmware";
reg = <0x260000 0xda0000>;
compatible = "openwrt,uimage", "denx,uimage";
openwrt,ih-magic = <0x83800000>;
};
partition@930000 {
label = "runtime2";
reg = <0x930000 0x6d0000>;
};
};
};
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
/* External phy RTL8218B #1 */
EXTERNAL_PHY(0)
EXTERNAL_PHY(1)
EXTERNAL_PHY(2)
EXTERNAL_PHY(3)
EXTERNAL_PHY(4)
EXTERNAL_PHY(5)
EXTERNAL_PHY(6)
EXTERNAL_PHY(7)
/* External phy RTL8218B #2 */
EXTERNAL_PHY(8)
EXTERNAL_PHY(9)
EXTERNAL_PHY(10)
EXTERNAL_PHY(11)
EXTERNAL_PHY(12)
EXTERNAL_PHY(13)
EXTERNAL_PHY(14)
EXTERNAL_PHY(15)
/* External phy RTL8218B #3 */
EXTERNAL_PHY(16)
EXTERNAL_PHY(17)
EXTERNAL_PHY(18)
EXTERNAL_PHY(19)
EXTERNAL_PHY(20)
EXTERNAL_PHY(21)
EXTERNAL_PHY(22)
EXTERNAL_PHY(23)
/* External phy RTL8218B #4 */
EXTERNAL_PHY(24)
EXTERNAL_PHY(25)
EXTERNAL_PHY(26)
EXTERNAL_PHY(27)
EXTERNAL_PHY(28)
EXTERNAL_PHY(29)
EXTERNAL_PHY(30)
EXTERNAL_PHY(31)
/* External phy RTL8218B #5 */
EXTERNAL_PHY(32)
EXTERNAL_PHY(33)
EXTERNAL_PHY(34)
EXTERNAL_PHY(35)
EXTERNAL_PHY(36)
EXTERNAL_PHY(37)
EXTERNAL_PHY(38)
EXTERNAL_PHY(39)
/* External phy RTL8218B #6 */
EXTERNAL_PHY(40)
EXTERNAL_PHY(41)
EXTERNAL_PHY(42)
EXTERNAL_PHY(43)
EXTERNAL_PHY(44)
EXTERNAL_PHY(45)
EXTERNAL_PHY(46)
EXTERNAL_PHY(47)
/* RTL8393 Internal SerDes */
INTERNAL_PHY(48)
INTERNAL_PHY(49)
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
SWITCH_PORT(0, 01, qsgmii)
SWITCH_PORT(1, 02, qsgmii)
SWITCH_PORT(2, 03, qsgmii)
SWITCH_PORT(3, 04, qsgmii)
SWITCH_PORT(4, 05, qsgmii)
SWITCH_PORT(5, 06, qsgmii)
SWITCH_PORT(6, 07, qsgmii)
SWITCH_PORT(7, 08, qsgmii)
SWITCH_PORT(8, 09, qsgmii)
SWITCH_PORT(9, 10, qsgmii)
SWITCH_PORT(10, 11, qsgmii)
SWITCH_PORT(11, 12, qsgmii)
SWITCH_PORT(12, 13, qsgmii)
SWITCH_PORT(13, 14, qsgmii)
SWITCH_PORT(14, 15, qsgmii)
SWITCH_PORT(15, 16, qsgmii)
SWITCH_PORT(16, 17, qsgmii)
SWITCH_PORT(17, 18, qsgmii)
SWITCH_PORT(18, 19, qsgmii)
SWITCH_PORT(19, 20, qsgmii)
SWITCH_PORT(20, 21, qsgmii)
SWITCH_PORT(21, 22, qsgmii)
SWITCH_PORT(22, 23, qsgmii)
SWITCH_PORT(23, 24, qsgmii)
SWITCH_PORT(24, 25, qsgmii)
SWITCH_PORT(25, 26, qsgmii)
SWITCH_PORT(26, 27, qsgmii)
SWITCH_PORT(27, 28, qsgmii)
SWITCH_PORT(28, 29, qsgmii)
SWITCH_PORT(29, 30, qsgmii)
SWITCH_PORT(30, 31, qsgmii)
SWITCH_PORT(31, 32, qsgmii)
SWITCH_PORT(32, 33, qsgmii)
SWITCH_PORT(33, 34, qsgmii)
SWITCH_PORT(34, 35, qsgmii)
SWITCH_PORT(35, 36, qsgmii)
SWITCH_PORT(36, 37, qsgmii)
SWITCH_PORT(37, 38, qsgmii)
SWITCH_PORT(38, 39, qsgmii)
SWITCH_PORT(39, 40, qsgmii)
SWITCH_PORT(40, 41, qsgmii)
SWITCH_PORT(41, 42, qsgmii)
SWITCH_PORT(42, 43, qsgmii)
SWITCH_PORT(43, 44, qsgmii)
SWITCH_PORT(44, 45, qsgmii)
SWITCH_PORT(45, 46, qsgmii)
SWITCH_PORT(46, 47, qsgmii)
SWITCH_PORT(47, 48, qsgmii)
/* SFP cages */
port@48 {
reg = <48>;
label = "lan49";
phy-mode = "sgmii";
phy-handle = <&phy48>;
sfp = <&sfp0>;
fixed-link {
speed = <1000>;
full-duplex;
pause;
};
};
port@49 {
reg = <49>;
label = "lan50";
phy-mode = "sgmii";
phy-handle = <&phy49>;
sfp = <&sfp1>;
fixed-link {
speed = <1000>;
full-duplex;
pause;
};
};
/* CPU-Port */
port@52 {
ethernet = <&ethernet0>;
reg = <52>;
phy-mode = "qsgmii";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,319 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include <dt-bindings/clock/rtl83xx-clk.h>
/dts-v1/;
#define STRINGIZE(s) #s
#define LAN_LABEL(p, s) STRINGIZE(p ## s)
#define SWITCH_PORT_LABEL(n) LAN_LABEL(lan, n)
#define INTERNAL_PHY(n) \
phy##n: ethernet-phy@##n { \
reg = <##n>; \
compatible = "ethernet-phy-ieee802.3-c22"; \
phy-is-integrated; \
};
#define EXTERNAL_PHY(n) \
phy##n: ethernet-phy@##n { \
reg = <##n>; \
compatible = "ethernet-phy-ieee802.3-c22"; \
};
#define EXTERNAL_SFP_PHY(n) \
phy##n: ethernet-phy@##n { \
compatible = "ethernet-phy-ieee802.3-c22"; \
sfp; \
media = "fibre"; \
reg = <##n>; \
};
#define EXTERNAL_SFP_PHY_FULL(n, s) \
phy##n: ethernet-phy@##n { \
compatible = "ethernet-phy-ieee802.3-c22"; \
sfp = <&sfp##s>; \
reg = <##n>; \
};
#define SWITCH_PORT(n, s, m) \
port@##n { \
reg = <##n>; \
label = SWITCH_PORT_LABEL(s) ; \
phy-handle = <&phy##n>; \
phy-mode = #m ; \
};
#define SWITCH_SFP_PORT(n, s, m) \
port@##n { \
reg = <##n>; \
label = SWITCH_PORT_LABEL(s) ; \
phy-handle = <&phy##n>; \
phy-mode = #m ; \
fixed-link { \
speed = <1000>; \
full-duplex; \
}; \
};
/ {
#address-cells = <1>;
#size-cells = <1>;
compatible = "realtek,rtl839x-soc";
osc: oscillator {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <25000000>;
};
ccu: clock-controller {
compatible = "realtek,rtl8390-clock";
#clock-cells = <1>;
clocks = <&osc>;
clock-names = "ref_clk";
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "mips,mips34Kc";
reg = <0>;
clocks = <&ccu CLK_CPU>;
operating-points-v2 = <&cpu_opp_table>;
};
cpu@1 {
compatible = "mips,mips34Kc";
reg = <1>;
clocks = <&ccu CLK_CPU>;
operating-points-v2 = <&cpu_opp_table>;
};
};
cpu_opp_table: opp-table-0 {
compatible = "operating-points-v2";
opp-shared;
opp00 {
opp-hz = /bits/ 64 <425000000>;
};
opp01 {
opp-hz = /bits/ 64 <450000000>;
};
opp02 {
opp-hz = /bits/ 64 <475000000>;
};
opp03 {
opp-hz = /bits/ 64 <500000000>;
};
opp04 {
opp-hz = /bits/ 64 <525000000>;
};
opp05 {
opp-hz = /bits/ 64 <550000000>;
};
opp06 {
opp-hz = /bits/ 64 <575000000>;
};
opp07 {
opp-hz = /bits/ 64 <600000000>;
};
opp08 {
opp-hz = /bits/ 64 <625000000>;
};
opp09 {
opp-hz = /bits/ 64 <650000000>;
};
opp10 {
opp-hz = /bits/ 64 <675000000>;
};
opp11 {
opp-hz = /bits/ 64 <700000000>;
};
opp12 {
opp-hz = /bits/ 64 <725000000>;
};
opp13 {
opp-hz = /bits/ 64 <750000000>;
};
};
chosen {
bootargs = "console=ttyS0,115200";
};
cpuintc: cpuintc {
compatible = "mti,cpu-interrupt-controller";
#address-cells = <0>;
#interrupt-cells = <1>;
interrupt-controller;
};
soc: soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0x18000000 0x10000>;
intc: interrupt-controller@3000 {
compatible = "realtek,rtl8390-intc", "realtek,rtl-intc";
reg = <0x3000 0x18>, <0x3018 0x18>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&cpuintc>;
interrupts = <2>, <3>, <4>, <5>, <6>;
};
spi0: spi@1200 {
compatible = "realtek,rtl8380-spi";
reg = <0x1200 0x100>;
#address-cells = <1>;
#size-cells = <0>;
};
timer0: timer@3100 {
compatible = "realtek,rtl8390-timer", "realtek,otto-timer";
reg = <0x3100 0x10>, <0x3110 0x10>, <0x3120 0x10>,
<0x3130 0x10>, <0x3140 0x10>;
interrupt-parent = <&intc>;
interrupts = <29 4>, <28 4>, <17 4>, <16 4>, <15 4>;
clocks = <&ccu CLK_LXB>;
};
uart0: uart@2000 {
compatible = "ns16550a";
reg = <0x2000 0x100>;
clocks = <&ccu CLK_LXB>;
interrupt-parent = <&intc>;
interrupts = <31 1>;
reg-io-width = <1>;
reg-shift = <2>;
fifo-size = <1>;
no-loopback-test;
};
uart1: uart@2100 {
pinctrl-names = "default";
pinctrl-0 = <&enable_uart1>;
compatible = "ns16550a";
reg = <0x2100 0x100>;
clocks = <&ccu CLK_LXB>;
interrupt-parent = <&intc>;
interrupts = <30 2>;
reg-io-width = <1>;
reg-shift = <2>;
fifo-size = <1>;
no-loopback-test;
status = "disabled";
};
gpio0: gpio-controller@3500 {
compatible = "realtek,rtl8390-gpio", "realtek,otto-gpio";
reg = <0x3500 0x20>;
gpio-controller;
#gpio-cells = <2>;
ngpios = <24>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&intc>;
interrupts = <23 2>;
};
watchdog0: watchdog@3150 {
compatible = "realtek,rtl8390-wdt";
reg = <0x3150 0xc>;
realtek,reset-mode = "soc";
clocks = <&ccu CLK_LXB>;
timeout-sec = <30>;
interrupt-parent = <&intc>;
interrupt-names = "phase1", "phase2";
interrupts = <19 4>, <18 4>;
};
};
pinmux@1b000004 {
compatible = "pinctrl-single";
reg = <0x1b000004 0x4>;
pinctrl-single,bit-per-mux;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <0x1>;
#pinctrl-cells = <2>;
enable_uart1: pinmux_enable_uart1 {
pinctrl-single,bits = <0x0 0x1 0x3>;
};
disable_jtag: pinmux_disable_jtag {
pinctrl-single,bits = <0x0 0x2 0x3>;
};
};
/* LED_GLB_CTRL */
pinmux@1b0000e4 {
compatible = "pinctrl-single";
reg = <0x1b0000e4 0x4>;
pinctrl-single,bit-per-mux;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <0x1>;
#pinctrl-cells = <2>;
/* enable GPIO 0 */
pinmux_disable_sys_led: disable_sys_led {
pinctrl-single,bits = <0x0 0x0 0x4000>;
};
};
ethernet0: ethernet@1b00a300 {
compatible = "realtek,rtl838x-eth";
reg = <0x1b00a300 0x100>;
interrupt-parent = <&intc>;
interrupts = <24 3>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
sram0: sram@9f000000 {
compatible = "mmio-sram";
reg = <0x9f000000 0x18000>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x9f000000 0x18000>;
};
switch0: switch@1b000000 {
status = "okay";
compatible = "realtek,rtl83xx-switch";
interrupt-parent = <&intc>;
interrupts = <20 2>;
};
};

View file

@ -0,0 +1,75 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
/ {
/* Lan 49 */
i2c0: i2c-gpio-0 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp0: sfp-p49 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
los-gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 8 GPIO_ACTIVE_LOW>;
/* tx-disable-gpio handled by RTL8214FC based on media setting */
};
/* Lan 50 */
i2c1: i2c-gpio-1 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp1: sfp-p50 {
compatible = "sff,sfp";
i2c-bus = <&i2c1>;
los-gpio = <&gpio1 4 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
/* tx-disable-gpio handled by RTL8214FC based on media setting */
};
/* Lan 51 */
i2c2: i2c-gpio-2 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 22 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 23 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp2: sfp-p51 {
compatible = "sff,sfp";
i2c-bus = <&i2c2>;
los-gpio = <&gpio1 25 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 24 GPIO_ACTIVE_LOW>;
/* tx-disable-gpio handled by RTL8214FC based on media setting */
};
/* Lan 52 */
i2c3: i2c-gpio-3 {
compatible = "i2c-gpio";
sda-gpios = <&gpio1 11 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
scl-gpios = <&gpio1 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
sfp3: sfp-p52 {
compatible = "sff,sfp";
i2c-bus = <&i2c3>;
los-gpio = <&gpio1 14 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio1 13 GPIO_ACTIVE_LOW>;
/* tx-disable-gpio handled by RTL8214FC based on media setting */
};
};

View file

@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
/ {
aliases {
led-boot = &led_power;
led-failsafe = &led_power;
led-running = &led_power;
led-upgrade = &led_power;
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;
};
leds: leds {
pinctrl-names = "default";
pinctrl-0 = <&pinmux_disable_sys_led>;
compatible = "gpio-leds";
led_power: power {
label = "green:power";
gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
};
};
};
&gpio0 {
indirect-access-bus-id = <0>;
};
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x00000000 0x80000>;
read-only;
};
partition@80000 {
label = "u-boot-env";
reg = <0x00080000 0x40000>;
};
partition@c0000 {
label = "board-name";
reg = <0x000c0000 0x40000>;
};
partition@280000 {
label = "firmware";
compatible = "denx,uimage";
reg = <0x00100000 0xd80000>;
};
partition@be80000 {
label = "kernel2";
reg = <0x00e80000 0x180000>;
};
partition@1000000 {
label = "sysinfo";
reg = <0x01000000 0x40000>;
};
partition@1040000 {
label = "rootfs2";
reg = <0x01040000 0xc00000>;
};
partition@1c40000 {
label = "jffs2";
reg = <0x01c40000 0x3c0000>;
};
};
};
};

View file

@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
/ {
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpio1 34 GPIO_ACTIVE_LOW>;
open-source;
};
keys: keys {
compatible = "gpio-keys-polled";
poll-interval = <20>;
reset {
label = "reset";
gpios = <&gpio1 33 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
};
gpio1: rtl8231-gpio {
compatible = "realtek,rtl8231-gpio";
#gpio-cells = <2>;
gpio-controller;
indirect-access-bus-id = <0>;
};
};

View file

@ -0,0 +1,196 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/leds/common.h>
/ {
chosen {
bootargs = "console=ttyS0,9600";
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;
};
leds: leds {
compatible = "gpio-leds";
led-0 {
label = "amber:any_col";
gpios = <&gpio2 0 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_AMBER>;
function = LED_FUNCTION_FAULT;
};
led-1 {
label = "green:giga";
gpios = <&gpio2 8 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_INDICATOR;
function-enumerator = <1>;
};
led-2 {
label = "green:100m";
gpios = <&gpio2 9 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_INDICATOR;
function-enumerator = <2>;
};
led-3 {
label = "green:full";
gpios = <&gpio2 10 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_INDICATOR;
function-enumerator = <3>;
};
led-4 {
label = "green:loop_history";
gpios = <&gpio2 11 GPIO_ACTIVE_LOW>;
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_INDICATOR;
function-enumerator = <4>;
};
};
keys {
compatible = "gpio-keys";
led_mode {
label = "led-mode";
gpios = <&gpio2 15 GPIO_ACTIVE_LOW>;
linux,code = <BTN_0>;
};
};
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
};
i2c_gpio_0: i2c-gpio-0 {
compatible = "i2c-gpio";
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
gpio1: gpio@20 {
compatible = "nxp,pca9555";
reg = <0x20>;
gpio-controller;
#gpio-cells = <2>;
};
gpio2: gpio@75 {
compatible = "nxp,pca9539";
reg = <0x75>;
gpio-controller;
#gpio-cells = <2>;
/*
* GPIO14 (IO1_6): Shift Register RESET (port LED)
* - Switch-M8eG PN28080K: 3x 74HC164
* - Switch-M16eG PN28160K: 4x 74HC164
* - Switch-M24eG PN28240K: 6x 74HC164
* - Switch-M48eG PN28480K: 12x 74HC164
*/
portled_sregister_reset {
gpio-hog;
gpios = <14 GPIO_ACTIVE_HIGH>;
output-high;
line-name = "portled-sregister-reset";
};
};
};
i2c_gpio_1: i2c-gpio-1 {
compatible = "i2c-gpio";
i2c-gpio,delay-us = <2>;
#address-cells = <1>;
#size-cells = <0>;
i2c_switch: i2c-switch@70 {
compatible = "nxp,pca9545";
reset-gpios = <&gpio2 13 GPIO_ACTIVE_LOW>;
reg = <0x70>;
#address-cells = <1>;
#size-cells = <0>;
};
};
};
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x80000>;
read-only;
};
partition@80000 {
label = "u-boot-env";
reg = <0x80000 0x10000>;
};
partition@90000 {
label = "u-boot-env2";
reg = <0x90000 0x10000>;
};
partition@a0000 {
label = "sysinfo";
reg = <0xa0000 0x60000>;
read-only;
};
/*
* Filesystem area in stock firmware
* (0x100000-0x1DFFFFF)
*
* stock firmware images are required to pass
* the checking by the U-Boot, also for OpenWrt
*
* in OpenWrt:
* - 0x100000-0xDFFFFF (13M): stock images
* - 0xE00000-0x1DFFFFF(16M): OpenWrt image
*/
partition@100000 {
label = "fs_reserved";
reg = <0x100000 0xd00000>;
};
partition@e00000 {
compatible = "denx,uimage";
label = "firmware";
reg = <0xe00000 0x1000000>;
};
partition@1e00000 {
label = "vlog_data";
reg = <0x1e00000 0x100000>;
read-only;
};
partition@1f00000 {
label = "elog_data";
reg = <0x1f00000 0x100000>;
read-only;
};
};
};
};

View file

@ -0,0 +1,324 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/dts-v1/;
#include "rtl930x.dtsi"
#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/leds/common.h>
/ {
compatible = "zyxel,xgs1250-12", "realtek,rtl838x-soc";
model = "Zyxel XGS1250-12 Switch";
aliases {
led-boot = &led_pwr_sys;
led-failsafe = &led_pwr_sys;
led-running = &led_pwr_sys;
led-upgrade = &led_pwr_sys;
};
keys {
compatible = "gpio-keys";
mode {
label = "reset";
gpios = <&gpio0 22 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
};
/* i2c of the SFP cage: port 12 */
i2c0: i2c-rtl9300 {
compatible = "realtek,rtl9300-i2c";
reg = <0x1b00036c 0x3c>;
#address-cells = <1>;
#size-cells = <0>;
sda-pin = <10>;
scl-pin = <8>;
clock-frequency = <100000>;
};
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&pinmux_disable_sys_led>;
led_pwr_sys: led-0 {
label = "green:power";
color = <LED_COLOR_ID_GREEN>;
function = LED_FUNCTION_POWER;
gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
};
};
sfp0: sfp-p12 {
compatible = "sff,sfp";
i2c-bus = <&i2c0>;
los-gpio = <&gpio0 17 GPIO_ACTIVE_HIGH>;
tx-fault-gpio = <&gpio0 20 GPIO_ACTIVE_HIGH>;
mod-def0-gpio = <&gpio0 16 GPIO_ACTIVE_LOW>;
tx-disable-gpio = <&gpio0 15 GPIO_ACTIVE_HIGH>;
};
led_set: led_set@0 {
compatible = "realtek,rtl9300-leds";
led_set0 = <0x0000 0xffff 0x0a20 0x0b80>; // LED set 0: 1000Mbps, 10/100Mbps
led_set1 = <0x0a0b 0x0a28 0x0a82 0x0a0b>; // LED set 1: (10G, 5G, 2.5G) (2.5G, 1G)
// (5G, 10/100) (10G, 5G, 2.5G)
led_set2 = <0x0000 0xffff 0x0a20 0x0a01>; // LED set 2: 1000MBit, 10GBit
};
};
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0xe0000>;
read-only;
};
partition@e0000 {
label = "u-boot-env";
reg = <0xe0000 0x10000>;
};
partition@f0000 {
label = "u-boot-env2";
reg = <0xf0000 0x10000>;
read-only;
};
partition@100000 {
label = "jffs";
reg = <0x100000 0x100000>;
};
partition@200000 {
label = "jffs2";
reg = <0x200000 0x100000>;
};
partition@b300000 {
label = "firmware";
reg = <0x300000 0xce0000>;
compatible = "openwrt,uimage", "denx,uimage";
openwrt,ih-magic = <0x93001250>;
};
partition@fe0000 {
label = "log";
reg = <0xfe0000 0x20000>;
};
};
};
};
&ethernet0 {
mdio: mdio-bus {
compatible = "realtek,rtl838x-mdio";
regmap = <&ethernet0>;
#address-cells = <1>;
#size-cells = <0>;
/* External RTL8218D PHY */
phy0: ethernet-phy@0 {
reg = <0>;
compatible = "ethernet-phy-ieee802.3-c22";
rtl9300,smi-address = <0 0>;
sds = < 2 >;
// Disabled because we do not know how to bring up again
// reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
};
phy1: ethernet-phy@1 {
reg = <1>;
compatible = "ethernet-phy-ieee802.3-c22";
rtl9300,smi-address = <0 1>;
};
phy2: ethernet-phy@2 {
reg = <2>;
compatible = "ethernet-phy-ieee802.3-c22";
rtl9300,smi-address = <0 2>;
};
phy3: ethernet-phy@3 {
reg = <3>;
compatible = "ethernet-phy-ieee802.3-c22";
rtl9300,smi-address = <0 3>;
};
phy4: ethernet-phy@4 {
reg = <4>;
compatible = "ethernet-phy-ieee802.3-c22";
rtl9300,smi-address = <0 4>;
};
phy5: ethernet-phy@5 {
reg = <5>;
compatible = "ethernet-phy-ieee802.3-c22";
rtl9300,smi-address = <0 5>;
};
phy6: ethernet-phy@6 {
reg = <6>;
compatible = "ethernet-phy-ieee802.3-c22";
rtl9300,smi-address = <0 6>;
};
phy7: ethernet-phy@7 {
reg = <7>;
compatible = "ethernet-phy-ieee802.3-c22";
rtl9300,smi-address = <0 7>;
};
/* External Aquantia 113C PHYs */
phy24: ethernet-phy@24 {
reg = <24>;
compatible = "ethernet-phy-ieee802.3-c45";
rtl9300,smi-address = <1 8>;
sds = < 6 >;
// Disabled because we do not know how to bring up again
// reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
};
phy25: ethernet-phy@25 {
reg = <25>;
compatible = "ethernet-phy-ieee802.3-c45";
rtl9300,smi-address = <2 8>;
sds = < 7 >;
// Disabled because we do not know how to bring up again
// reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
};
phy26: ethernet-phy@26 {
reg = <26>;
compatible = "ethernet-phy-ieee802.3-c45";
rtl9300,smi-address = <3 8>;
sds = < 8 >;
// Disabled because we do not know how to bring up again
// reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
};
/* SFP Ports */
phy27: ethernet-phy@27 {
compatible = "ethernet-phy-ieee802.3-c22";
phy-is-integrated;
reg = <27>;
rtl9300,smi-address = <4 0>;
sds = < 9 >;
};
};
};
&switch0 {
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
label = "lan1";
phy-handle = <&phy0>;
phy-mode = "xgmii";
led-set = <0>;
};
port@1 {
reg = <1>;
label = "lan2";
phy-handle = <&phy1>;
phy-mode = "xgmii";
led-set = <0>;
};
port@2 {
reg = <2>;
label = "lan3";
phy-handle = <&phy2>;
phy-mode = "xgmii";
led-set = <0>;
};
port@3 {
reg = <3>;
label = "lan4";
phy-handle = <&phy3>;
phy-mode = "xgmii";
led-set = <0>;
};
port@4 {
reg = <4>;
label = "lan5";
phy-handle = <&phy4>;
phy-mode = "xgmii";
led-set = <0>;
};
port@5 {
reg = <5>;
label = "lan6";
phy-handle = <&phy5>;
phy-mode = "xgmii";
led-set = <0>;
};
port@6 {
reg = <6>;
label = "lan7";
phy-handle = <&phy6>;
phy-mode = "xgmii";
led-set = <0>;
};
port@7 {
reg = <7>;
label = "lan8";
phy-handle = <&phy7>;
phy-mode = "xgmii";
led-set = <0>;
};
port@24 {
reg = <24>;
label = "lan9";
phy-mode = "usxgmii";
phy-handle = <&phy24>;
led-set = <1>;
};
port@25 {
reg = <25>;
label = "lan10";
phy-mode = "usxgmii";
phy-handle = <&phy25>;
led-set = <1>;
};
port@26 {
reg = <26>;
label = "lan11";
phy-mode = "usxgmii";
phy-handle = <&phy26>;
led-set = <1>;
};
port@27 {
reg = <27>;
label = "lan12";
phy-mode = "10gbase-r";
phy-handle = <&phy27>;
sfp = <&sfp0>;
led-set = <2>;
fixed-link {
speed = <10000>;
full-duplex;
pause;
};
};
port@28 {
ethernet = <&ethernet0>;
reg = <28>;
phy-mode = "internal";
fixed-link {
speed = <10000>;
full-duplex;
};
};
};
};

View file

@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
compatible = "realtek,rtl838x-soc";
cpus {
#address-cells = <1>;
#size-cells = <0>;
frequency = <800000000>;
cpu@0 {
compatible = "mips,mips34Kc";
reg = <0>;
};
};
memory@0 {
device_type = "memory";
reg = <0x0 0x8000000>;
};
chosen {
bootargs = "console=ttyS0,115200";
};
cpuintc: cpuintc {
compatible = "mti,cpu-interrupt-controller";
#address-cells = <0>;
#interrupt-cells = <1>;
interrupt-controller;
};
lx_clk: lx_clk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <175000000>;
};
soc: soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0x18000000 0x10000>;
intc: interrupt-controller@3000 {
compatible = "realtek,rtl9300-intc", "realtek,rtl-intc";
reg = <0x3000 0x18>, <0x3018 0x18>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&cpuintc>;
interrupts = <2>, <3>, <4>, <5>, <6>, <7>;
};
rtl9300clock: rtl9300clock@3200 {
compatible = "realtek,rtl9300clock";
reg = <0x3200 0x10>, <0x3210 0x10>;
interrupt-parent = <&intc>;
interrupts = <7 5>, <8 5>;
};
spi0: spi@1200 {
compatible = "realtek,rtl8380-spi";
reg = <0x1200 0x100>;
#address-cells = <1>;
#size-cells = <0>;
};
uart0: uart@2000 {
compatible = "ns16550a";
reg = <0x2000 0x100>;
clocks = <&lx_clk>;
interrupt-parent = <&intc>;
interrupts = <30 1>;
reg-io-width = <1>;
reg-shift = <2>;
fifo-size = <1>;
no-loopback-test;
};
uart1: uart@2100 {
compatible = "ns16550a";
reg = <0x2100 0x100>;
clocks = <&lx_clk>;
interrupt-parent = <&intc>;
interrupts = <31 0>;
reg-io-width = <1>;
reg-shift = <2>;
fifo-size = <1>;
no-loopback-test;
status = "disabled";
};
watchdog0: watchdog@3260 {
compatible = "realtek,rtl9300-wdt";
reg = <0x3260 0xc>;
realtek,reset-mode = "soc";
clocks = <&lx_clk>;
timeout-sec = <30>;
interrupt-parent = <&intc>;
interrupt-names = "phase1", "phase2";
interrupts = <5 4>, <6 4>;
};
gpio0: gpio-controller@3300 {
compatible = "realtek,rtl9300-gpio", "realtek,otto-gpio";
reg = <0x3300 0x1c>, <0x3338 0x8>;
gpio-controller;
#gpio-cells = <2>;
ngpios = <24>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&intc>;
interrupts = <13 1>;
};
};
pinmux_led: pinmux@1b00cc00 {
compatible = "pinctrl-single";
reg = <0x1b00cc00 0x4>;
pinctrl-single,bit-per-mux;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <0x1>;
#pinctrl-cells = <2>;
/* enable GPIO 0 */
pinmux_disable_sys_led: disable_sys_led {
pinctrl-single,bits = <0x0 0x0 0x1000>;
};
};
ethernet0: ethernet@1b00a300 {
compatible = "realtek,rtl838x-eth";
reg = <0x1b00a300 0x100>;
interrupt-parent = <&intc>;
interrupts = <24 3>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
switch0: switch@1b000000 {
compatible = "realtek,rtl83xx-switch";
status = "okay";
interrupt-parent = <&intc>;
interrupts = <23 2>;
};
};

View file

@ -0,0 +1,200 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include <dt-bindings/interrupt-controller/mips-gic.h>
/ {
#address-cells = <1>;
#size-cells = <1>;
compatible = "realtek,rtl838x-soc";
cpus {
#address-cells = <1>;
#size-cells = <0>;
frequency = <1000000000>;
cpu@0 {
compatible = "mti,interaptive";
reg = <0>;
};
cpu@1 {
compatible = "mti,interaptive";
reg = <1>;
};
};
memory@0 {
device_type = "memory";
reg = <0x0 0x10000000>;
};
chosen {
bootargs = "console=ttyS0,115200";
};
lx_clk: lx_clk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <200000000>;
};
cpuclock: cpuclock@0 {
#clock-cells = <0>;
compatible = "fixed-clock";
/* FIXME: there should be way to detect this */
clock-frequency = <1000000000>;
};
cpuintc: cpuintc {
compatible = "mti,cpu-interrupt-controller";
#address-cells = <0>;
#interrupt-cells = <1>;
interrupt-controller;
};
gic: interrupt-controller@1ddc0000 {
compatible = "mti,gic";
reg = <0x1ddc0000 0x20000>;
interrupt-controller;
#interrupt-cells = <3>;
/*
* Declare the interrupt-parent even though the mti,gic
* binding doesn't require it, such that the kernel can
* figure out that cpu_intc is the root interrupt
* controller & should be probed first.
*/
interrupt-parent = <&cpuintc>;
timer {
compatible = "mti,gic-timer";
interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
clocks = <&cpuclock>;
};
};
soc: soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0x18000000 0x10000>;
spi0: spi@1200 {
status = "okay";
compatible = "realtek,rtl8380-spi";
reg = <0x1200 0x100>;
#address-cells = <1>;
#size-cells = <0>;
};
watchdog0: watchdog@3260 {
compatible = "realtek,rtl9310-wdt";
reg = <0x3260 0xc>;
realtek,reset-mode = "soc";
clocks = <&lx_clk>;
timeout-sec = <30>;
interrupt-parent = <&gic>;
interrupt-names = "phase1", "phase2";
interrupts = <GIC_SHARED 8 IRQ_TYPE_LEVEL_HIGH>, <GIC_SHARED 9 IRQ_TYPE_LEVEL_HIGH>;
};
gpio0: gpio-controller@3300 {
compatible = "realtek,rtl9310-gpio", "realtek,otto-gpio";
reg = <0x3300 0x1c>;
gpio-controller;
#gpio-cells = <2>;
ngpios = <32>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&gic>;
interrupts = <GIC_SHARED 20 IRQ_TYPE_LEVEL_HIGH>;
};
uart0: uart@2000 {
compatible = "ns16550a";
reg = <0x2000 0x100>;
clock-frequency = <200000000>;
interrupt-parent = <&gic>;
#interrupt-cells = <3>;
interrupts = <GIC_SHARED 22 IRQ_TYPE_LEVEL_HIGH>;
reg-io-width = <1>;
reg-shift = <2>;
fifo-size = <1>;
no-loopback-test;
};
uart1: uart@2100 {
compatible = "ns16550a";
reg = <0x2100 0x100>;
clock-frequency = <200000000>;
interrupt-parent = <&gic>;
#interrupt-cells = <3>;
interrupts = <GIC_SHARED 23 IRQ_TYPE_LEVEL_HIGH>;
reg-io-width = <1>;
reg-shift = <2>;
fifo-size = <1>;
no-loopback-test;
status = "disabled";
};
};
pinmux: pinmux@1b001358 {
compatible = "pinctrl-single";
reg = <0x1b001358 0x4>;
pinctrl-single,bit-per-mux;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <0x1>;
#pinctrl-cells = <2>;
/* Enable GPIO6 and GPIO7, possibly unknown others */
pinmux_disable_jtag: disable_jtag {
pinctrl-single,bits = <0x0 0x0 0x8000>;
};
/* Controls GPIO0 */
pinmux_disable_sys_led: disable_sys_led {
pinctrl-single,bits = <0x0 0x0 0x100>;
};
};
ethernet0: ethernet@1b00a300 {
status = "okay";
compatible = "realtek,rtl838x-eth";
reg = <0x1b00a300 0x100>;
interrupt-parent = <&gic>;
#interrupt-cells = <3>;
interrupts = <GIC_SHARED 16 IRQ_TYPE_LEVEL_HIGH>;
phy-mode = "internal";
fixed-link {
speed = <1000>;
full-duplex;
};
};
switch0: switch@1b000000 {
compatible = "realtek,rtl83xx-switch";
status = "okay";
interrupt-parent = <&gic>;
#interrupt-cells = <3>;
interrupts = <GIC_SHARED 15 IRQ_TYPE_LEVEL_HIGH>;
};
};

View file

@ -0,0 +1,85 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/timer/realtek,rtl8300-timer.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Realtek Timer Device Tree Bindings
maintainers:
- Markus Stockhausen <markus.stockhausen@gmx.de>
description: |
The Realtek SOCs of the RTL83XX and RTL93XX series have at least 5 known
timers with corresponding interrupt lines . Their speed is derived from the
Lexra Bus (LXB) by dividers. Each timer has a block of 4 control registers in
the address range 0xb800xxxx with following start offsets.
RTL83XX: 0x3100, 0x3110, 0x3120, 0x3130, 0x3140
RTL93XX: 0x3200, 0x3210, 0x3220, 0x3230, 0x3240
properties:
compatible:
items:
- enum:
- realtek,rtl8380-timer
- realtek,rtl8390-timer
- realtek,rtl9300-timer
- const: realtek,otto-timer
reg:
minItems: 5
maxItems: 5
description:
List of timer register addresses.
interrupts:
minItems: 5
maxItems: 5
description:
List of timer interrupts.
clocks:
maxItems: 1
required:
- compatible
- reg
- interrupts
- clocks
additionalProperties: false
examples:
- |
timer0: timer@3100 {
compatible = "realtek,rtl8380-timer", "realtek,otto-timer";
reg = <0x3100 0x10>, <0x3110 0x10>, <0x3120 0x10>,
<0x3130 0x10>, <0x3140 0x10>;
interrupt-parent = <&intc>;
interrupts = <29 4>, <28 4>, <17 4>, <16 4>, <15 4>;
clocks = <&ccu CLK_LXB>;
};
- |
timer0: timer@3100 {
compatible = "realtek,rtl8390-timer", "realtek,otto-timer";
reg = <0x3100 0x10>, <0x3110 0x10>, <0x3120 0x10>,
<0x3130 0x10>, <0x3140 0x10>;
interrupt-parent = <&intc>;
interrupts = <29 4>, <28 4>, <17 4>, <16 4>, <15 4>;
clocks = <&ccu CLK_LXB>;
};
- |
timer0: timer@3200 {
compatible = "realtek,rtl9300-timer", "realtek,otto-timer";
reg = <0x3200 0x10>, <0x3210 0x10>, <0x3220 0x10>,
<0x3230 0x10>, <0x3240 0x10>;
interrupt-parent = <&intc>;
interrupts = <7 4>, <8 4>, <9 4>, <10 4>, <11 4>;
clocks = <&ccu CLK_LXB>;
};
...

View file

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef RTL838X_IOREMAP_H_
#define RTL838X_IOREMAP_H_
static inline int is_rtl838x_internal_registers(phys_addr_t offset)
{
/* IO-Block */
if (offset >= 0xb8000000 && offset < 0xb9000000)
return 1;
/* Switch block */
if (offset >= 0xbb000000 && offset < 0xbc000000)
return 1;
return 0;
}
static inline void __iomem *plat_ioremap(phys_addr_t offset, unsigned long size,
unsigned long flags)
{
if (is_rtl838x_internal_registers(offset))
return (void __iomem *)offset;
return NULL;
}
static inline int plat_iounmap(const volatile void __iomem *addr)
{
return is_rtl838x_internal_registers((unsigned long)addr);
}
#endif

View file

@ -0,0 +1,416 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com)
* Copyright (C) 2020 B. Koblitz
*/
#ifndef _MACH_RTL838X_H_
#define _MACH_RTL838X_H_
#include <asm/types.h>
/*
* Register access macros
*/
#define RTL838X_SW_BASE ((volatile void *) 0xBB000000)
#define rtl83xx_r32(reg) readl(reg)
#define rtl83xx_w32(val, reg) writel(val, reg)
#define rtl83xx_w32_mask(clear, set, reg) rtl83xx_w32((rtl83xx_r32(reg) & ~(clear)) | (set), reg)
#define rtl83xx_r8(reg) readb(reg)
#define rtl83xx_w8(val, reg) writeb(val, reg)
#define sw_r32(reg) readl(RTL838X_SW_BASE + reg)
#define sw_w32(val, reg) writel(val, RTL838X_SW_BASE + reg)
#define sw_w32_mask(clear, set, reg) \
sw_w32((sw_r32(reg) & ~(clear)) | (set), reg)
#define sw_r64(reg) ((((u64)readl(RTL838X_SW_BASE + reg)) << 32) | \
readl(RTL838X_SW_BASE + reg + 4))
#define sw_w64(val, reg) do { \
writel((u32)((val) >> 32), RTL838X_SW_BASE + reg); \
writel((u32)((val) & 0xffffffff), \
RTL838X_SW_BASE + reg + 4); \
} while (0)
/*
* SPRAM
*/
#define RTL838X_ISPRAM_BASE 0x0
#define RTL838X_DSPRAM_BASE 0x0
/*
* IRQ Controller
*/
#define RTL838X_IRQ_CPU_BASE 0
#define RTL838X_IRQ_CPU_NUM 8
#define RTL838X_IRQ_ICTL_BASE (RTL838X_IRQ_CPU_BASE + RTL838X_IRQ_CPU_NUM)
#define RTL838X_IRQ_ICTL_NUM 32
#define RTL83XX_IRQ_UART0 31
#define RTL83XX_IRQ_UART1 30
#define RTL83XX_IRQ_TC0 29
#define RTL83XX_IRQ_TC1 28
#define RTL83XX_IRQ_OCPTO 27
#define RTL83XX_IRQ_HLXTO 26
#define RTL83XX_IRQ_SLXTO 25
#define RTL83XX_IRQ_NIC 24
#define RTL83XX_IRQ_GPIO_ABCD 23
#define RTL83XX_IRQ_GPIO_EFGH 22
#define RTL83XX_IRQ_RTC 21
#define RTL83XX_IRQ_SWCORE 20
#define RTL83XX_IRQ_WDT_IP1 19
#define RTL83XX_IRQ_WDT_IP2 18
#define RTL9300_UART1_IRQ 31
#define RTL9300_UART0_IRQ 30
#define RTL9300_USB_H2_IRQ 28
#define RTL9300_NIC_IRQ 24
#define RTL9300_SWCORE_IRQ 23
#define RTL9300_GPIO_ABC_IRQ 13
#define RTL9300_TC4_IRQ 11
#define RTL9300_TC3_IRQ 10
#define RTL9300_TC2_IRQ 9
#define RTL9300_TC1_IRQ 8
#define RTL9300_TC0_IRQ 7
/*
* MIPS32R2 counter
*/
#define RTL838X_COMPARE_IRQ (RTL838X_IRQ_CPU_BASE + 7)
/*
* ICTL
* Base address 0xb8003000UL
*/
#define RTL838X_ICTL1_IRQ (RTL838X_IRQ_CPU_BASE + 2)
#define RTL838X_ICTL2_IRQ (RTL838X_IRQ_CPU_BASE + 3)
#define RTL838X_ICTL3_IRQ (RTL838X_IRQ_CPU_BASE + 4)
#define RTL838X_ICTL4_IRQ (RTL838X_IRQ_CPU_BASE + 5)
#define RTL838X_ICTL5_IRQ (RTL838X_IRQ_CPU_BASE + 6)
#define GIMR (0x00)
#define UART0_IE (1 << 31)
#define UART1_IE (1 << 30)
#define TC0_IE (1 << 29)
#define TC1_IE (1 << 28)
#define OCPTO_IE (1 << 27)
#define HLXTO_IE (1 << 26)
#define SLXTO_IE (1 << 25)
#define NIC_IE (1 << 24)
#define GPIO_ABCD_IE (1 << 23)
#define GPIO_EFGH_IE (1 << 22)
#define RTC_IE (1 << 21)
#define WDT_IP1_IE (1 << 19)
#define WDT_IP2_IE (1 << 18)
#define GISR (0x04)
#define UART0_IP (1 << 31)
#define UART1_IP (1 << 30)
#define TC0_IP (1 << 29)
#define TC1_IP (1 << 28)
#define OCPTO_IP (1 << 27)
#define HLXTO_IP (1 << 26)
#define SLXTO_IP (1 << 25)
#define NIC_IP (1 << 24)
#define GPIO_ABCD_IP (1 << 23)
#define GPIO_EFGH_IP (1 << 22)
#define RTC_IP (1 << 21)
#define WDT_IP1_IP (1 << 19)
#define WDT_IP2_IP (1 << 18)
/* Interrupt Routing Selection */
#define UART0_RS 2
#define UART1_RS 1
#define TC0_RS 5
#define TC1_RS 1
#define OCPTO_RS 1
#define HLXTO_RS 1
#define SLXTO_RS 1
#define NIC_RS 4
#define GPIO_ABCD_RS 4
#define GPIO_EFGH_RS 4
#define RTC_RS 4
#define SWCORE_RS 3
#define WDT_IP1_RS 4
#define WDT_IP2_RS 5
/* Interrupt IRQ Assignments */
#define UART0_IRQ 31
#define UART1_IRQ 30
#define TC0_IRQ 29
#define TC1_IRQ 28
#define OCPTO_IRQ 27
#define HLXTO_IRQ 26
#define SLXTO_IRQ 25
#define NIC_IRQ 24
#define GPIO_ABCD_IRQ 23
#define GPIO_EFGH_IRQ 22
#define RTC_IRQ 21
#define SWCORE_IRQ 20
#define WDT_IP1_IRQ 19
#define WDT_IP2_IRQ 18
#define SYSTEM_FREQ 200000000
#define RTL838X_UART0_BASE ((volatile void *)(0xb8002000UL))
#define RTL838X_UART0_BAUD 38400 /* ex. 19200 or 38400 or 57600 or 115200 */
#define RTL838X_UART0_FREQ (SYSTEM_FREQ - RTL838X_UART0_BAUD * 24)
#define RTL838X_UART0_MAPBASE 0x18002000UL
#define RTL838X_UART0_MAPSIZE 0x100
#define RTL838X_UART0_IRQ UART0_IRQ
#define RTL838X_UART1_BASE ((volatile void *)(0xb8002100UL))
#define RTL838X_UART1_BAUD 38400 /* ex. 19200 or 38400 or 57600 or 115200 */
#define RTL838X_UART1_FREQ (SYSTEM_FREQ - RTL838X_UART1_BAUD * 24)
#define RTL838X_UART1_MAPBASE 0x18002100UL
#define RTL838X_UART1_MAPSIZE 0x100
#define RTL838X_UART1_IRQ UART1_IRQ
#define UART0_RBR (RTL838X_UART0_BASE + 0x000)
#define UART0_THR (RTL838X_UART0_BASE + 0x000)
#define UART0_DLL (RTL838X_UART0_BASE + 0x000)
#define UART0_IER (RTL838X_UART0_BASE + 0x004)
#define UART0_DLM (RTL838X_UART0_BASE + 0x004)
#define UART0_IIR (RTL838X_UART0_BASE + 0x008)
#define UART0_FCR (RTL838X_UART0_BASE + 0x008)
#define UART0_LCR (RTL838X_UART0_BASE + 0x00C)
#define UART0_MCR (RTL838X_UART0_BASE + 0x010)
#define UART0_LSR (RTL838X_UART0_BASE + 0x014)
#define UART1_RBR (RTL838X_UART1_BASE + 0x000)
#define UART1_THR (RTL838X_UART1_BASE + 0x000)
#define UART1_DLL (RTL838X_UART1_BASE + 0x000)
#define UART1_IER (RTL838X_UART1_BASE + 0x004)
#define UART1_DLM (RTL838X_UART1_BASE + 0x004)
#define UART1_IIR (RTL838X_UART1_BASE + 0x008)
#define UART1_FCR (RTL838X_UART1_BASE + 0x008)
#define UART1_LCR (RTL838X_UART1_BASE + 0x00C)
#define UART1_MCR (RTL838X_UART1_BASE + 0x010)
#define UART1_LSR (RTL838X_UART1_BASE + 0x014)
/*
* Memory Controller
*/
#define MC_MCR 0xB8001000
#define MC_MCR_VAL 0x00000000
#define MC_DCR 0xB8001004
#define MC_DCR0_VAL 0x54480000
#define MC_DTCR 0xB8001008
#define MC_DTCR_VAL 0xFFFF05C0
/*
* GPIO
*/
#define GPIO_CTRL_REG_BASE ((volatile void *) 0xb8003500)
#define RTL838X_GPIO_PABC_CNR (GPIO_CTRL_REG_BASE + 0x0)
#define RTL838X_GPIO_PABC_TYPE (GPIO_CTRL_REG_BASE + 0x04)
#define RTL838X_GPIO_PABC_DIR (GPIO_CTRL_REG_BASE + 0x8)
#define RTL838X_GPIO_PABC_DATA (GPIO_CTRL_REG_BASE + 0xc)
#define RTL838X_GPIO_PABC_ISR (GPIO_CTRL_REG_BASE + 0x10)
#define RTL838X_GPIO_PAB_IMR (GPIO_CTRL_REG_BASE + 0x14)
#define RTL838X_GPIO_PC_IMR (GPIO_CTRL_REG_BASE + 0x18)
#define RTL930X_GPIO_CTRL_REG_BASE ((volatile void *) 0xb8003300)
#define RTL930X_GPIO_PABCD_DIR (RTL930X_GPIO_CTRL_REG_BASE + 0x8)
#define RTL930X_GPIO_PABCD_DAT (RTL930X_GPIO_CTRL_REG_BASE + 0xc)
#define RTL930X_GPIO_PABCD_ISR (RTL930X_GPIO_CTRL_REG_BASE + 0x10)
#define RTL930X_GPIO_PAB_IMR (RTL930X_GPIO_CTRL_REG_BASE + 0x14)
#define RTL930X_GPIO_PCD_IMR (RTL930X_GPIO_CTRL_REG_BASE + 0x18)
#define RTL838X_MODEL_NAME_INFO (0x00D4)
#define RTL839X_MODEL_NAME_INFO (0x0FF0)
#define RTL93XX_MODEL_NAME_INFO (0x0004)
#define RTL931X_CHIP_INFO_ADDR (0x0008)
#define RTL838X_LED_GLB_CTRL (0xA000)
#define RTL839X_LED_GLB_CTRL (0x00E4)
#define RTL9302_LED_GLB_CTRL (0xcc00)
#define RTL930X_LED_GLB_CTRL (0xCC00)
#define RTL931X_LED_GLB_CTRL (0x0600)
#define RTL838X_EXT_GPIO_DIR (0xA08C)
#define RTL839X_EXT_GPIO_DIR (0x0214)
#define RTL838X_EXT_GPIO_DATA (0xA094)
#define RTL839X_EXT_GPIO_DATA (0x021c)
#define RTL838X_EXT_GPIO_INDRT_ACCESS (0xA09C)
#define RTL839X_EXT_GPIO_INDRT_ACCESS (0x0224)
#define RTL838X_EXTRA_GPIO_CTRL (0xA0E0)
#define RTL838X_DMY_REG5 (0x0144)
#define RTL838X_EXTRA_GPIO_CTRL (0xA0E0)
#define RTL838X_GMII_INTF_SEL (0x1000)
#define RTL838X_IO_DRIVING_ABILITY_CTRL (0x1010)
#define RTL838X_GPIO_A7 31
#define RTL838X_GPIO_A6 30
#define RTL838X_GPIO_A5 29
#define RTL838X_GPIO_A4 28
#define RTL838X_GPIO_A3 27
#define RTL838X_GPIO_A2 26
#define RTL838X_GPIO_A1 25
#define RTL838X_GPIO_A0 24
#define RTL838X_GPIO_B7 23
#define RTL838X_GPIO_B6 22
#define RTL838X_GPIO_B5 21
#define RTL838X_GPIO_B4 20
#define RTL838X_GPIO_B3 19
#define RTL838X_GPIO_B2 18
#define RTL838X_GPIO_B1 17
#define RTL838X_GPIO_B0 16
#define RTL838X_GPIO_C7 15
#define RTL838X_GPIO_C6 14
#define RTL838X_GPIO_C5 13
#define RTL838X_GPIO_C4 12
#define RTL838X_GPIO_C3 11
#define RTL838X_GPIO_C2 10
#define RTL838X_GPIO_C1 9
#define RTL838X_GPIO_C0 8
#define RTL838X_INT_RW_CTRL (0x0058)
#define RTL838X_EXT_VERSION (0x00D0)
#define RTL838X_PLL_CML_CTRL (0x0FF8)
#define RTL838X_STRAP_DBG (0x100C)
/*
* Reset
*/
#define RGCR (0x1E70)
#define RTL838X_RST_GLB_CTRL_0 (0x003c)
#define RTL838X_RST_GLB_CTRL_1 (0x0040)
#define RTL839X_RST_GLB_CTRL (0x0014)
#define RTL930X_RST_GLB_CTRL_0 (0x000c)
#define RTL931X_RST_GLB_CTRL (0x0400)
/* LED control by switch */
#define RTL838X_LED_MODE_SEL (0x1004)
#define RTL838X_LED_MODE_CTRL (0xA004)
#define RTL838X_LED_P_EN_CTRL (0xA008)
/* LED control by software */
#define RTL838X_LED_SW_CTRL (0x0128)
#define RTL839X_LED_SW_CTRL (0xA00C)
#define RTL838X_LED_SW_P_EN_CTRL (0xA010)
#define RTL839X_LED_SW_P_EN_CTRL (0x012C)
#define RTL838X_LED0_SW_P_EN_CTRL (0xA010)
#define RTL839X_LED0_SW_P_EN_CTRL (0x012C)
#define RTL838X_LED1_SW_P_EN_CTRL (0xA014)
#define RTL839X_LED1_SW_P_EN_CTRL (0x0130)
#define RTL838X_LED2_SW_P_EN_CTRL (0xA018)
#define RTL839X_LED2_SW_P_EN_CTRL (0x0134)
#define RTL838X_LED_SW_P_CTRL (0xA01C)
#define RTL839X_LED_SW_P_CTRL (0x0144)
#define RTL839X_MAC_EFUSE_CTRL (0x02ac)
/*
* MDIO via Realtek's SMI interface
*/
#define RTL838X_SMI_GLB_CTRL (0xa100)
#define RTL838X_SMI_ACCESS_PHY_CTRL_0 (0xa1b8)
#define RTL838X_SMI_ACCESS_PHY_CTRL_1 (0xa1bc)
#define RTL838X_SMI_ACCESS_PHY_CTRL_2 (0xa1c0)
#define RTL838X_SMI_ACCESS_PHY_CTRL_3 (0xa1c4)
#define RTL838X_SMI_PORT0_5_ADDR_CTRL (0xa1c8)
#define RTL838X_SMI_POLL_CTRL (0xa17c)
#define RTL839X_SMI_GLB_CTRL (0x03f8)
#define RTL839X_SMI_PORT_POLLING_CTRL (0x03fc)
#define RTL839X_PHYREG_ACCESS_CTRL (0x03DC)
#define RTL839X_PHYREG_CTRL (0x03E0)
#define RTL839X_PHYREG_PORT_CTRL (0x03E4)
#define RTL839X_PHYREG_DATA_CTRL (0x03F0)
#define RTL839X_PHYREG_MMD_CTRL (0x3F4)
#define RTL930X_SMI_GLB_CTRL (0xCA00)
#define RTL930X_SMI_POLL_CTRL (0xca90)
#define RTL930X_SMI_PORT0_15_POLLING_SEL (0xCA08)
#define RTL930X_SMI_PORT16_27_POLLING_SEL (0xCA0C)
#define RTL930X_SMI_PORT0_5_ADDR (0xCB80)
#define RTL930X_SMI_ACCESS_PHY_CTRL_0 (0xCB70)
#define RTL930X_SMI_ACCESS_PHY_CTRL_1 (0xCB74)
#define RTL930X_SMI_ACCESS_PHY_CTRL_2 (0xCB78)
#define RTL930X_SMI_ACCESS_PHY_CTRL_3 (0xCB7C)
#define RTL931X_SMI_GLB_CTRL1 (0x0CBC)
#define RTL931X_SMI_GLB_CTRL0 (0x0CC0)
#define RTL931X_SMI_PORT_POLLING_CTRL (0x0CCC)
#define RTL931X_SMI_PORT_ADDR (0x0C74)
#define RTL931X_SMI_PORT_POLLING_SEL (0x0C9C)
#define RTL9310_SMI_PORT_POLLING_CTRL (0x0CCC)
#define RTL931X_SMI_INDRT_ACCESS_CTRL_0 (0x0C00)
#define RTL931X_SMI_INDRT_ACCESS_CTRL_1 (0x0C04)
#define RTL931X_SMI_INDRT_ACCESS_CTRL_2 (0x0C08)
#define RTL931X_SMI_INDRT_ACCESS_CTRL_3 (0x0C10)
#define RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL (0x0C14)
#define RTL931X_SMI_INDRT_ACCESS_MMD_CTRL (0xC18)
#define RTL931X_MAC_L2_GLOBAL_CTRL2 (0x1358)
#define RTL931X_MAC_L2_GLOBAL_CTRL1 (0x5548)
/*
* Switch interrupts
*/
#define RTL838X_IMR_GLB (0x1100)
#define RTL838X_IMR_PORT_LINK_STS_CHG (0x1104)
#define RTL838X_ISR_GLB_SRC (0x1148)
#define RTL838X_ISR_PORT_LINK_STS_CHG (0x114C)
#define RTL839X_IMR_GLB (0x0064)
#define RTL839X_IMR_PORT_LINK_STS_CHG (0x0068)
#define RTL839X_ISR_GLB_SRC (0x009c)
#define RTL839X_ISR_PORT_LINK_STS_CHG (0x00a0)
#define RTL930X_IMR_GLB (0xC628)
#define RTL930X_IMR_PORT_LINK_STS_CHG (0xC62C)
#define RTL930X_ISR_GLB (0xC658)
#define RTL930X_ISR_PORT_LINK_STS_CHG (0xC660)
// IMR_GLB does not exit on RTL931X
#define RTL931X_IMR_PORT_LINK_STS_CHG (0x126C)
#define RTL931X_ISR_GLB_SRC (0x12B4)
#define RTL931X_ISR_PORT_LINK_STS_CHG (0x12B8)
/* Definition of family IDs */
#define RTL8389_FAMILY_ID (0x8389)
#define RTL8328_FAMILY_ID (0x8328)
#define RTL8390_FAMILY_ID (0x8390)
#define RTL8350_FAMILY_ID (0x8350)
#define RTL8380_FAMILY_ID (0x8380)
#define RTL8330_FAMILY_ID (0x8330)
#define RTL9300_FAMILY_ID (0x9300)
#define RTL9310_FAMILY_ID (0x9310)
/* SPI Support */
#define RTL931X_SPI_CTRL0 (0x103C)
/* Basic SoC Features */
#define RTL838X_CPU_PORT 28
#define RTL839X_CPU_PORT 52
#define RTL930X_CPU_PORT 28
#define RTL931X_CPU_PORT 56
struct rtl83xx_soc_info {
unsigned char *name;
unsigned int id;
unsigned int family;
unsigned char *compatible;
volatile void *sw_base;
volatile void *icu_base;
int cpu_port;
};
/* rtl83xx-related functions used across subsystems */
int rtl838x_smi_wait_op(int timeout);
int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val);
int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val);
int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val);
int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val);
#endif /* _MACH_RTL838X_H_ */

View file

@ -0,0 +1,200 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/clockchips.h>
#include <linux/init.h>
#include <asm/time.h>
#include <asm/idle.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#include <mach-rtl83xx.h>
/*
* Timer registers
* the RTL9300/9310 SoCs have 6 timers, each register block 0x10 apart
*/
#define RTL9300_TC_DATA 0x0
#define RTL9300_TC_CNT 0x4
#define RTL9300_TC_CTRL 0x8
#define RTL9300_TC_CTRL_MODE BIT(24)
#define RTL9300_TC_CTRL_EN BIT(28)
#define RTL9300_TC_INT 0xc
#define RTL9300_TC_INT_IP BIT(16)
#define RTL9300_TC_INT_IE BIT(20)
// Timer modes
#define TIMER_MODE_REPEAT 1
#define TIMER_MODE_ONCE 0
// Minimum divider is 2
#define DIVISOR_RTL9300 2
#define N_BITS 28
#define RTL9300_CLOCK_RATE 87500000
struct rtl9300_clk_dev {
struct clock_event_device clkdev;
void __iomem *base;
};
static void __iomem *rtl9300_tc_base(struct clock_event_device *clk)
{
struct rtl9300_clk_dev *rtl_clk = container_of(clk, struct rtl9300_clk_dev, clkdev);
return rtl_clk->base;
}
static irqreturn_t rtl9300_timer_interrupt(int irq, void *dev_id)
{
struct rtl9300_clk_dev *rtl_clk = dev_id;
struct clock_event_device *clk = &rtl_clk->clkdev;
u32 v = readl(rtl_clk->base + RTL9300_TC_INT);
// Acknowledge the IRQ
v |= RTL9300_TC_INT_IP;
writel(v, rtl_clk->base + RTL9300_TC_INT);
clk->event_handler(clk);
return IRQ_HANDLED;
}
static void rtl9300_clock_stop(void __iomem *base)
{
u32 v;
writel(0, base + RTL9300_TC_CTRL);
// Acknowledge possibly pending IRQ
v = readl(base + RTL9300_TC_INT);
writel(v | RTL9300_TC_INT_IP, base + RTL9300_TC_INT);
}
static void rtl9300_timer_start(void __iomem *base, bool periodic)
{
u32 v = (periodic ? RTL9300_TC_CTRL_MODE : 0) | RTL9300_TC_CTRL_EN | DIVISOR_RTL9300;
writel(0, base + RTL9300_TC_CNT);
pr_debug("------------- starting timer base %08x\n", (u32)base);
writel(v, base + RTL9300_TC_CTRL);
}
static int rtl9300_next_event(unsigned long delta, struct clock_event_device *clk)
{
void __iomem *base = rtl9300_tc_base(clk);
rtl9300_clock_stop(base);
writel(delta, base + RTL9300_TC_DATA);
rtl9300_timer_start(base, TIMER_MODE_ONCE);
return 0;
}
static int rtl9300_state_periodic(struct clock_event_device *clk)
{
void __iomem *base = rtl9300_tc_base(clk);
pr_debug("------------- rtl9300_state_periodic %08x\n", (u32)base);
rtl9300_clock_stop(base);
writel(RTL9300_CLOCK_RATE / HZ, base + RTL9300_TC_DATA);
rtl9300_timer_start(base, TIMER_MODE_REPEAT);
return 0;
}
static int rtl9300_state_oneshot(struct clock_event_device *clk)
{
void __iomem *base = rtl9300_tc_base(clk);
pr_debug("------------- rtl9300_state_oneshot %08x\n", (u32)base);
rtl9300_clock_stop(base);
writel(RTL9300_CLOCK_RATE / HZ, base + RTL9300_TC_DATA);
rtl9300_timer_start(base, TIMER_MODE_ONCE);
return 0;
}
static int rtl9300_shutdown(struct clock_event_device *clk)
{
void __iomem *base = rtl9300_tc_base(clk);
pr_debug("------------- rtl9300_shutdown %08x\n", (u32)base);
rtl9300_clock_stop(base);
return 0;
}
static void rtl9300_clock_setup(void __iomem *base)
{
u32 v;
// Disable timer
writel(0, base + RTL9300_TC_CTRL);
// Acknowledge possibly pending IRQ
v = readl(base + RTL9300_TC_INT);
writel(v | RTL9300_TC_INT_IP, base + RTL9300_TC_INT);
// Setup maximum period (for use as clock-source)
writel(0x0fffffff, base + RTL9300_TC_DATA);
}
static DEFINE_PER_CPU(struct rtl9300_clk_dev, rtl9300_clockevent);
static DEFINE_PER_CPU(char [18], rtl9300_clock_name);
void rtl9300_clockevent_init(void)
{
int cpu = smp_processor_id();
int irq;
struct rtl9300_clk_dev *rtl_clk = &per_cpu(rtl9300_clockevent, cpu);
struct clock_event_device *cd = &rtl_clk->clkdev;
unsigned char *name = per_cpu(rtl9300_clock_name, cpu);
unsigned long flags = IRQF_PERCPU | IRQF_TIMER;
struct device_node *node;
pr_info("%s called for cpu%d\n", __func__, cpu);
BUG_ON(cpu > 3); /* Only have 4 general purpose timers */
node = of_find_compatible_node(NULL, NULL, "realtek,rtl9300clock");
if (!node) {
pr_err("No DT entry found for realtek,rtl9300clock\n");
return;
}
irq = irq_of_parse_and_map(node, cpu);
pr_info("%s using IRQ %d\n", __func__, irq);
rtl_clk->base = of_iomap(node, cpu);
if (!rtl_clk->base) {
pr_err("cannot map timer for cpu %d", cpu);
return;
}
rtl9300_clock_setup(rtl_clk->base);
sprintf(name, "rtl9300-counter-%d", cpu);
cd->name = name;
cd->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
clockevent_set_clock(cd, RTL9300_CLOCK_RATE);
cd->max_delta_ns = clockevent_delta2ns(0x0fffffff, cd);
cd->max_delta_ticks = 0x0fffffff;
cd->min_delta_ns = clockevent_delta2ns(0x20, cd);
cd->min_delta_ticks = 0x20;
cd->rating = 300;
cd->irq = irq;
cd->cpumask = cpumask_of(cpu);
cd->set_next_event = rtl9300_next_event;
cd->set_state_shutdown = rtl9300_shutdown;
cd->set_state_periodic = rtl9300_state_periodic;
cd->set_state_oneshot = rtl9300_state_oneshot;
clockevents_register_device(cd);
irq_set_affinity(irq, cd->cpumask);
if (request_irq(irq, rtl9300_timer_interrupt, flags, name, rtl_clk))
pr_err("Failed to request irq %d (%s)\n", irq, name);
writel(RTL9300_TC_INT_IE, rtl_clk->base + RTL9300_TC_INT);
}

View file

@ -0,0 +1,5 @@
#
# Makefile for the rtl838x specific parts of the kernel
#
obj-y := setup.o prom.o

View file

@ -0,0 +1,5 @@
#
# Realtek RTL838x SoCs
#
cflags-$(CONFIG_RTL83XX) += -I$(srctree)/arch/mips/include/asm/mach-rtl838x/
load-$(CONFIG_RTL83XX) += 0xffffffff80000000

View file

@ -0,0 +1,216 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* prom.c
* Early intialization code for the Realtek RTL838X SoC
*
* based on the original BSP by
* Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com)
* Copyright (C) 2020 B. Koblitz
*
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/of_fdt.h>
#include <linux/libfdt.h>
#include <asm/bootinfo.h>
#include <asm/addrspace.h>
#include <asm/page.h>
#include <asm/cpu.h>
#include <asm/fw/fw.h>
#include <asm/smp-ops.h>
#include <asm/mips-cps.h>
#include <mach-rtl83xx.h>
extern char arcs_cmdline[];
extern const char __appended_dtb;
struct rtl83xx_soc_info soc_info;
const void *fdt;
#ifdef CONFIG_MIPS_MT_SMP
extern const struct plat_smp_ops vsmp_smp_ops;
static struct plat_smp_ops rtl_smp_ops;
static void rtl_init_secondary(void)
{
#ifndef CONFIG_CEVT_R4K
/*
* These devices are low on resources. There might be the chance that CEVT_R4K
* is not enabled in kernel build. Nevertheless the timer and interrupt 7 might
* be active by default after startup of secondary VPE. With no registered
* handler that leads to continuous unhandeled interrupts. In this case disable
* counting (DC) in the core and confirm a pending interrupt.
*/
write_c0_cause(read_c0_cause() | CAUSEF_DC);
write_c0_compare(0);
#endif /* CONFIG_CEVT_R4K */
/*
* Enable all CPU interrupts, as everything is managed by the external
* controller. TODO: Standard vsmp_init_secondary() has special treatment for
* Malta if external GIC is available. Maybe we need this too.
*/
if (mips_gic_present())
pr_warn("%s: GIC present. Maybe interrupt enabling required.\n", __func__);
else
set_c0_status(ST0_IM);
}
#endif /* CONFIG_MIPS_MT_SMP */
const char *get_system_type(void)
{
return soc_info.name;
}
void __init prom_free_prom_memory(void)
{
}
void __init device_tree_init(void)
{
if (!fdt_check_header(&__appended_dtb)) {
fdt = &__appended_dtb;
pr_info("Using appended Device Tree.\n");
}
initial_boot_params = (void *)fdt;
unflatten_and_copy_device_tree();
}
void __init identify_rtl9302(void)
{
switch (sw_r32(RTL93XX_MODEL_NAME_INFO) & 0xfffffff0) {
case 0x93020810:
soc_info.name = "RTL9302A 12x2.5G";
break;
case 0x93021010:
soc_info.name = "RTL9302B 8x2.5G";
break;
case 0x93021810:
soc_info.name = "RTL9302C 16x2.5G";
break;
case 0x93022010:
soc_info.name = "RTL9302D 24x2.5G";
break;
case 0x93020800:
soc_info.name = "RTL9302A";
break;
case 0x93021000:
soc_info.name = "RTL9302B";
break;
case 0x93021800:
soc_info.name = "RTL9302C";
break;
case 0x93022000:
soc_info.name = "RTL9302D";
break;
case 0x93023001:
soc_info.name = "RTL9302F";
break;
default:
soc_info.name = "RTL9302";
}
}
void __init prom_init(void)
{
uint32_t model;
/* uart0 */
setup_8250_early_printk_port(0xb8002000, 2, 0);
model = sw_r32(RTL838X_MODEL_NAME_INFO);
pr_info("RTL838X model is %x\n", model);
model = model >> 16 & 0xFFFF;
if ((model != 0x8328) && (model != 0x8330) && (model != 0x8332)
&& (model != 0x8380) && (model != 0x8382)) {
model = sw_r32(RTL839X_MODEL_NAME_INFO);
pr_info("RTL839X model is %x\n", model);
model = model >> 16 & 0xFFFF;
}
if ((model & 0x8390) != 0x8380 && (model & 0x8390) != 0x8390) {
model = sw_r32(RTL93XX_MODEL_NAME_INFO);
pr_info("RTL93XX model is %x\n", model);
model = model >> 16 & 0xFFFF;
}
soc_info.id = model;
switch (model) {
case 0x8328:
soc_info.name = "RTL8328";
soc_info.family = RTL8328_FAMILY_ID;
break;
case 0x8332:
soc_info.name = "RTL8332";
soc_info.family = RTL8380_FAMILY_ID;
break;
case 0x8380:
soc_info.name = "RTL8380";
soc_info.family = RTL8380_FAMILY_ID;
break;
case 0x8382:
soc_info.name = "RTL8382";
soc_info.family = RTL8380_FAMILY_ID;
break;
case 0x8390:
soc_info.name = "RTL8390";
soc_info.family = RTL8390_FAMILY_ID;
break;
case 0x8391:
soc_info.name = "RTL8391";
soc_info.family = RTL8390_FAMILY_ID;
break;
case 0x8392:
soc_info.name = "RTL8392";
soc_info.family = RTL8390_FAMILY_ID;
break;
case 0x8393:
soc_info.name = "RTL8393";
soc_info.family = RTL8390_FAMILY_ID;
break;
case 0x9301:
soc_info.name = "RTL9301";
soc_info.family = RTL9300_FAMILY_ID;
break;
case 0x9302:
identify_rtl9302();
soc_info.family = RTL9300_FAMILY_ID;
break;
case 0x9303:
soc_info.name = "RTL9303";
soc_info.family = RTL9300_FAMILY_ID;
break;
case 0x9313:
soc_info.name = "RTL9313";
soc_info.family = RTL9310_FAMILY_ID;
break;
default:
soc_info.name = "DEFAULT";
soc_info.family = 0;
}
pr_info("SoC Type: %s\n", get_system_type());
fw_init_cmdline();
mips_cpc_probe();
if (!register_cps_smp_ops())
return;
#ifdef CONFIG_MIPS_MT_SMP
if (cpu_has_mipsmt) {
rtl_smp_ops = vsmp_smp_ops;
rtl_smp_ops.init_secondary = rtl_init_secondary;
register_smp_ops(&rtl_smp_ops);
return;
}
#endif
register_up_smp_ops();
}

View file

@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Setup for the Realtek RTL838X SoC:
* Memory, Timer and Serial
*
* Copyright (C) 2020 B. Koblitz
* based on the original BSP by
* Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com)
*
*/
#include <linux/console.h>
#include <linux/init.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/of_fdt.h>
#include <linux/irqchip.h>
#include <asm/addrspace.h>
#include <asm/io.h>
#include <asm/bootinfo.h>
#include <asm/time.h>
#include <asm/prom.h>
#include <asm/smp-ops.h>
#include "mach-rtl83xx.h"
extern struct rtl83xx_soc_info soc_info;
void __init plat_mem_setup(void)
{
void *dtb;
set_io_port_base(KSEG1);
if (fw_passed_dtb) /* UHI interface */
dtb = (void *)fw_passed_dtb;
else if (&__dtb_start[0] != &__dtb_end[0])
dtb = (void *)__dtb_start;
else
panic("no dtb found");
/*
* Load the devicetree. This causes the chosen node to be
* parsed resulting in our memory appearing
*/
__dt_setup_arch(dtb);
}
void plat_time_init_fallback(void)
{
struct device_node *np;
u32 freq = 500000000;
np = of_find_node_by_name(NULL, "cpus");
if (!np) {
pr_err("Missing 'cpus' DT node, using default frequency.");
} else {
if (of_property_read_u32(np, "frequency", &freq) < 0)
pr_err("No 'frequency' property in DT, using default.");
else
pr_info("CPU frequency from device tree: %dMHz", freq / 1000000);
of_node_put(np);
}
mips_hpt_frequency = freq / 2;
}
void __init plat_time_init(void)
{
/*
* Initialization routine resembles generic MIPS plat_time_init() with
* lazy error handling. The final fallback is only needed until we have
* converted all device trees to new clock syntax.
*/
struct device_node *np;
struct clk *clk;
of_clk_init(NULL);
mips_hpt_frequency = 0;
np = of_get_cpu_node(0, NULL);
if (!np) {
pr_err("Failed to get CPU node\n");
} else {
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("Failed to get CPU clock: %ld\n", PTR_ERR(clk));
} else {
mips_hpt_frequency = clk_get_rate(clk) / 2;
clk_put(clk);
}
}
if (!mips_hpt_frequency)
plat_time_init_fallback();
timer_probe();
}
void __init arch_init_irq(void)
{
irqchip_init();
}

View file

@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig COMMON_CLK_REALTEK
bool "Support for Realtek's clock controllers"
depends on RTL83XX
if COMMON_CLK_REALTEK
config COMMON_CLK_RTL83XX
bool "Clock driver for Realtek RTL83XX"
depends on RTL83XX
select SRAM
help
This driver adds support for the Realtek RTL83xx series basic clocks.
This includes chips in the RTL838x series, such as RTL8380, RTL8381,
RTL832, as well as chips from the RTL839x series, such as RTL8390,
RT8391, RTL8392, RTL8393 and RTL8396.
endif

View file

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_COMMON_CLK_RTL83XX) += clk-rtl83xx.o clk-rtl838x-sram.o clk-rtl839x-sram.o

View file

@ -0,0 +1,150 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Realtek RTL838X SRAM clock setters
* Copyright (C) 2022 Markus Stockhausen <markus.stockhausen@gmx.de>
*/
#include <dt-bindings/clock/rtl83xx-clk.h>
#include "clk-rtl83xx.h"
#define rGLB $t0
#define rCTR $t1
#define rMSK $t2
#define rSLP $t3
#define rTMP $t4
.set noreorder
.globl rtcl_838x_dram_start
rtcl_838x_dram_start:
/*
* Functions start here and should avoid access to normal memory. REMARK! Do not forget about
* stack pointer and dirty caches that might interfere.
*/
.globl rtcl_838x_dram_set_rate
.ent rtcl_838x_dram_set_rate
rtcl_838x_dram_set_rate:
#ifdef CONFIG_RTL838X
li rCTR, RTL_SW_CORE_BASE
addiu rGLB, rCTR, RTL838X_PLL_GLB_CTRL
ori rTMP, $0, CLK_CPU
beq $a0, rTMP, pre_cpu
ori rTMP, $0, CLK_MEM
beq $a0, rTMP, pre_mem
nop
pre_lxb:
ori rSLP, $0, RTL838X_GLB_CTRL_LXB_PLL_READY_MASK
addiu rCTR, rCTR, RTL838X_PLL_LXB_CTRL0
b main_set
ori rMSK, $0, RTL838X_GLB_CTRL_EN_LXB_PLL_MASK
pre_mem:
/* simple 64K data cache flush to avoid unexpected memory access */
li rMSK, RTL_SRAM_BASE
li rTMP, 2048
pre_flush:
lw $0, 0(rMSK)
addiu rMSK, rMSK, 32
addiu rTMP, rTMP, -1
bne rTMP, $0, pre_flush
lw $0, -4(rMSK)
ori rSLP, $0, RTL838X_GLB_CTRL_MEM_PLL_READY_MASK
addiu rCTR, rCTR, RTL838X_PLL_MEM_CTRL0
b main_set
ori rMSK, $0, RTL838X_GLB_CTRL_EN_MEM_PLL_MASK
pre_cpu:
/* switch CPU to LXB clock */
ori rMSK, $0, RTL838X_GLB_CTRL_CPU_PLL_SC_MUX_MASK
nor rMSK, rMSK, $0
sync
lw rTMP, 0(rGLB)
and rTMP, rTMP, rMSK
sw rTMP, 0(rGLB)
sync
ori rSLP, $0, RTL838X_GLB_CTRL_CPU_PLL_READY_MASK
addiu rCTR, rCTR, RTL838X_PLL_CPU_CTRL0
ori rMSK, $0, RTL838X_GLB_CTRL_EN_CPU_PLL_MASK
main_set:
/* disable PLL */
nor rMSK, rMSK, 0
sync
lw rTMP, 0(rGLB)
sync
and rTMP, rTMP, rMSK
sync
sw rTMP, 0(rGLB)
/* set new PLL values */
sync
sw $a1, 0(rCTR)
sw $a2, 4(rCTR)
sync
/* enable PLL (will reset it and clear ready status) */
nor rMSK, rMSK, 0
sync
lw rTMP, 0(rGLB)
sync
or rTMP, rTMP, rMSK
sync
sw rTMP, 0(rGLB)
/* wait for PLL to become ready */
wait_ready:
lw rTMP, 0(rGLB)
and rTMP, rTMP, rSLP
bne rTMP, $0, wait_ready
sync
/* branch to post processing */
ori rTMP, $0, CLK_CPU
beq $a0, rTMP, post_cpu
ori rTMP, $0, CLK_MEM
beq $a0, rTMP, post_mem
nop
post_lxb:
jr $ra
nop
post_mem:
jr $ra
nop
post_cpu:
/* stabilize clock to avoid crash, empirically determined */
ori rSLP, $0, 0x3000
wait_cpu:
bnez rSLP, wait_cpu
addiu rSLP, rSLP, -1
/* switch CPU to PLL clock */
ori rMSK, $0, RTL838X_GLB_CTRL_CPU_PLL_SC_MUX_MASK
sync
lw rTMP, 0(rGLB)
or rTMP, rTMP, rMSK
sw rTMP, 0(rGLB)
sync
jr $ra
nop
#else /* !CONFIG_RTL838X */
jr $ra
nop
#endif
.end rtcl_838x_dram_set_rate
/*
* End marker. Do not delete.
*/
.word RTL_SRAM_MARKER
.globl rtcl_838x_dram_size
rtcl_838x_dram_size:
.word .-rtcl_838x_dram_start

View file

@ -0,0 +1,142 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Realtek RTL839X SRAM clock setters
* Copyright (C) 2022 Markus Stockhausen <markus.stockhausen@gmx.de>
*/
#include <asm/mipsregs.h>
#include <dt-bindings/clock/rtl83xx-clk.h>
#include "clk-rtl83xx.h"
#define rGLB $t0
#define rCTR $t1
#define rMSK $t2
#define rSLP1 $t3
#define rSLP2 $t4
#define rSLP3 $t5
#define rTMP $t6
#define rCP0 $t7
.set noreorder
.globl rtcl_839x_dram_start
rtcl_839x_dram_start:
/*
* Functions start here and should avoid access to normal memory. REMARK! Do not forget about
* stack pointer and dirty caches that might interfere.
*/
.globl rtcl_839x_dram_set_rate
.ent rtcl_839x_dram_set_rate
rtcl_839x_dram_set_rate:
#ifdef CONFIG_RTL839X
/* disable MIPS 34K branch and return prediction */
mfc0 rCP0, CP0_CONFIG, 7
ori rTMP, rCP0, 0xc
mtc0 rTMP, CP0_CONFIG, 7
li rCTR, RTL_SW_CORE_BASE
addiu rGLB, rCTR, RTL839X_PLL_GLB_CTRL
ori rTMP, $0, CLK_CPU
beq $a0, rTMP, pre_cpu
ori rTMP, $0, CLK_MEM
beq $a0, rTMP, pre_mem
nop
pre_lxb:
li rSLP1, 0x400000
li rSLP2, 0x400000
li rSLP3, 0x400000
addiu rCTR, rCTR, RTL839X_PLL_LXB_CTRL0
b main_set
ori rMSK, $0, RTL839X_GLB_CTRL_LXB_CLKSEL_MASK
pre_mem:
/* try to avoid memory access with simple 64K data cache flush */
li rMSK, RTL_SRAM_BASE
li rTMP, 2048
pre_flush:
lw $0, 0(rMSK)
addiu rMSK, rMSK, 32
addiu rTMP, rTMP, -1
bne rTMP, $0, pre_flush
lw $0, -4(rMSK)
li rSLP1, 0x10000
li rSLP2, 0x10000
li rSLP3, 0x10000
addiu rCTR, rCTR, RTL839X_PLL_MEM_CTRL0
b main_set
ori rMSK, $0, RTL839X_GLB_CTRL_MEM_CLKSEL_MASK
pre_cpu:
li rSLP1, 0x1000
li rSLP2, 0x1000
li rSLP3, 0x200
addiu rCTR, rCTR, RTL839X_PLL_CPU_CTRL0
ori rMSK, $0, RTL839X_GLB_CTRL_CPU_CLKSEL_MASK
main_set:
/* switch to fixed clock */
sync
lw rTMP, 0(rGLB)
sync
or rTMP, rTMP, rMSK
sync
sw rTMP, 0(rGLB)
/* wait until fixed clock in use */
or rTMP, rSLP1, $0
wait_fixclock:
bnez rTMP, wait_fixclock
addiu rTMP, rTMP, -1
/* set new PLL values */
sync
sw $a1, 0(rCTR)
sw $a2, 4(rCTR)
sync
/* wait for value takeover */
or rTMP, rSLP2, $0
wait_pll:
bnez rTMP, wait_pll
addiu rTMP, rTMP, -1
/* switch back to PLL clock*/
nor rMSK, rMSK, $0
sync
lw rTMP, 0(rGLB)
sync
and rTMP, rTMP, rMSK
sync
sw rTMP, 0(rGLB)
/* wait until PLL clock in use */
or rTMP, rSLP3, $0
wait_pllclock:
bnez rTMP, wait_pllclock
addiu rTMP, rTMP, -1
/* restore branch prediction */
mtc0 rCP0, CP0_CONFIG, 7
jr $ra
nop
#else /* !CONFIG_RTL839X */
jr $ra
nop
#endif
.end rtcl_839x_dram_set_rate
/*
* End marker. Do not delete.
*/
.word RTL_SRAM_MARKER
.globl rtcl_839x_dram_size
rtcl_839x_dram_size:
.word .-rtcl_839x_dram_start

View file

@ -0,0 +1,766 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Realtek RTL83XX clock driver
* Copyright (C) 2022 Markus Stockhausen <markus.stockhausen@gmx.de>
*
* This driver provides basic clock support for the central core clock unit (CCU) and its PLLs
* inside the RTL838X and RTL8389X SOC. Currently CPU, memory and LXB clock information can be
* accessed. To make use of the driver add the following devices and configurations at the
* appropriate locations to the DT.
*
* #include <dt-bindings/clock/rtl83xx-clk.h>
*
* sram0: sram@9f000000 {
* compatible = "mmio-sram";
* reg = <0x9f000000 0x18000>;
* #address-cells = <1>;
* #size-cells = <1>;
* ranges = <0 0x9f000000 0x18000>;
* };
*
* osc: oscillator {
* compatible = "fixed-clock";
* #clock-cells = <0>;
* clock-frequency = <25000000>;
* };
*
* ccu: clock-controller {
* compatible = "realtek,rtl8380-clock";
* #clock-cells = <1>;
* clocks = <&osc>;
* clock-names = "ref_clk";
* };
*
*
* The SRAM part is needed to be able to set clocks. When changing clocks the code must not run
* from DRAM. Otherwise system might freeze. Take care to adjust CCU compatibility, SRAM address
* and size to the target SOC device. Afterwards one can access/identify the clocks in the other
* DT devices with <&ccu CLK_CPU>, <&ccu CLK_MEM> or <&ccu CLK_LXB>. Additionally the clocks can
* be used inside the kernel with
*
* cpu_clk = clk_get(NULL, "cpu_clk");
* mem_clk = clk_get(NULL, "mem_clk");
* lxb_clk = clk_get(NULL, "lxb_clk");
*
* This driver can be directly used by the DT based cpufreq driver (CONFIG_CPUFREQ_DT) if CPU
* references the right clock and sane operating points (OPP) are provided. E.g.
*
* cpu@0 {
* compatible = "mips,mips4KEc";
* reg = <0>;
* clocks = <&ccu CLK_CPU>;
* operating-points-v2 = <&cpu_opp_table>;
* };
*
* cpu_opp_table: opp-table-0 {
* compatible = "operating-points-v2";
* opp-shared;
* opp00 {
* opp-hz = /bits/ 64 <425000000>;
* };
* ...
* }
*/
#include <asm/cacheflush.h>
#include <asm/mipsmtregs.h>
#include <dt-bindings/clock/rtl83xx-clk.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "clk-rtl83xx.h"
#define read_sw(reg) ioread32(((void *)RTL_SW_CORE_BASE) + reg)
#define read_soc(reg) ioread32(((void *)RTL_SOC_BASE) + reg)
#define write_sw(val, reg) iowrite32(val, ((void *)RTL_SW_CORE_BASE) + reg)
#define write_soc(val, reg) iowrite32(val, ((void *)RTL_SOC_BASE) + reg)
/*
* some hardware specific definitions
*/
#define SOC_RTL838X 0
#define SOC_RTL839X 1
#define SOC_COUNT 2
#define MEM_DDR1 1
#define MEM_DDR2 2
#define MEM_DDR3 3
#define REG_CTRL0 0
#define REG_CTRL1 1
#define REG_COUNT 2
#define OSC_RATE 25000000
static const int rtcl_regs[SOC_COUNT][REG_COUNT][CLK_COUNT] = {
{
{ RTL838X_PLL_CPU_CTRL0, RTL838X_PLL_MEM_CTRL0, RTL838X_PLL_LXB_CTRL0 },
{ RTL838X_PLL_CPU_CTRL1, RTL838X_PLL_MEM_CTRL1, RTL838X_PLL_LXB_CTRL1 },
}, {
{ RTL839X_PLL_CPU_CTRL0, RTL839X_PLL_MEM_CTRL0, RTL839X_PLL_LXB_CTRL0 },
{ RTL839X_PLL_CPU_CTRL1, RTL839X_PLL_MEM_CTRL1, RTL839X_PLL_LXB_CTRL1 },
}
};
#define RTCL_REG_SET(_rate, _ctrl0, _ctrl1) \
{ \
.rate = _rate, \
.ctrl0 = _ctrl0, \
.ctrl1 = _ctrl1, \
}
struct rtcl_reg_set {
unsigned int rate;
unsigned int ctrl0;
unsigned int ctrl1;
};
/*
* The following configuration tables are valid operation points for their corresponding PLLs.
* The magic numbers are precalculated mulitpliers and dividers to keep the driver simple. They
* also provide rates outside the allowed physical specifications. E.g. DDR3 memory has a lower
* limit of 303 MHz or the CPU might get unstable if set to anything above its startup frequency.
* Additionally the Realtek SOCs tend to expect CPU speed > MEM speed > LXB speed. The caller or
* DT configuration must take care that only valid operating points are selected.
*/
static const struct rtcl_reg_set rtcl_838x_cpu_reg_set[] = {
RTCL_REG_SET(300000000, 0x045c8, 0x1414530e),
RTCL_REG_SET(325000000, 0x04648, 0x1414530e),
RTCL_REG_SET(350000000, 0x046c8, 0x1414530e),
RTCL_REG_SET(375000000, 0x04748, 0x1414530e),
RTCL_REG_SET(400000000, 0x045c8, 0x0c14530e),
RTCL_REG_SET(425000000, 0x04628, 0x0c14530e),
RTCL_REG_SET(450000000, 0x04688, 0x0c14530e),
RTCL_REG_SET(475000000, 0x046e8, 0x0c14530e),
RTCL_REG_SET(500000000, 0x04748, 0x0c14530e),
RTCL_REG_SET(525000000, 0x047a8, 0x0c14530e),
RTCL_REG_SET(550000000, 0x04808, 0x0c14530e),
RTCL_REG_SET(575000000, 0x04868, 0x0c14530e),
RTCL_REG_SET(600000000, 0x048c8, 0x0c14530e),
RTCL_REG_SET(625000000, 0x04928, 0x0c14530e)
};
static const struct rtcl_reg_set rtcl_838x_mem_reg_set[] = {
RTCL_REG_SET(200000000, 0x041bc, 0x14018C80),
RTCL_REG_SET(225000000, 0x0417c, 0x0c018C80),
RTCL_REG_SET(250000000, 0x041ac, 0x0c018C80),
RTCL_REG_SET(275000000, 0x0412c, 0x04018C80),
RTCL_REG_SET(300000000, 0x0414c, 0x04018c80),
RTCL_REG_SET(325000000, 0x0416c, 0x04018c80),
RTCL_REG_SET(350000000, 0x0418c, 0x04018c80),
RTCL_REG_SET(375000000, 0x041ac, 0x04018c80)
};
static const struct rtcl_reg_set rtcl_838x_lxb_reg_set[] = {
RTCL_REG_SET(100000000, 0x043c8, 0x001ad30e),
RTCL_REG_SET(125000000, 0x043c8, 0x001ad30e),
RTCL_REG_SET(150000000, 0x04508, 0x1c1ad30e),
RTCL_REG_SET(175000000, 0x04508, 0x1c1ad30e),
RTCL_REG_SET(200000000, 0x047c8, 0x001ad30e)
};
static const struct rtcl_reg_set rtcl_839x_cpu_reg_set[] = {
RTCL_REG_SET(400000000, 0x0414c, 0x00000005),
RTCL_REG_SET(425000000, 0x041ec, 0x00000006),
RTCL_REG_SET(450000000, 0x0417c, 0x00000005),
RTCL_REG_SET(475000000, 0x0422c, 0x00000006),
RTCL_REG_SET(500000000, 0x041ac, 0x00000005),
RTCL_REG_SET(525000000, 0x0426c, 0x00000006),
RTCL_REG_SET(550000000, 0x0412c, 0x00000004),
RTCL_REG_SET(575000000, 0x042ac, 0x00000006),
RTCL_REG_SET(600000000, 0x0414c, 0x00000004),
RTCL_REG_SET(625000000, 0x042ec, 0x00000006),
RTCL_REG_SET(650000000, 0x0416c, 0x00000004),
RTCL_REG_SET(675000000, 0x04324, 0x00000006),
RTCL_REG_SET(700000000, 0x0418c, 0x00000004),
RTCL_REG_SET(725000000, 0x0436c, 0x00000006),
RTCL_REG_SET(750000000, 0x0438c, 0x00000006),
RTCL_REG_SET(775000000, 0x043ac, 0x00000006),
RTCL_REG_SET(800000000, 0x043cc, 0x00000006),
RTCL_REG_SET(825000000, 0x043ec, 0x00000006),
RTCL_REG_SET(850000000, 0x0440c, 0x00000006)
};
static const struct rtcl_reg_set rtcl_839x_mem_reg_set[] = {
RTCL_REG_SET(100000000, 0x041cc, 0x00000000),
RTCL_REG_SET(125000000, 0x041ac, 0x00000007),
RTCL_REG_SET(150000000, 0x0414c, 0x00000006),
RTCL_REG_SET(175000000, 0x0418c, 0x00000006),
RTCL_REG_SET(200000000, 0x041cc, 0x00000006),
RTCL_REG_SET(225000000, 0x0417c, 0x00000005),
RTCL_REG_SET(250000000, 0x041ac, 0x00000005),
RTCL_REG_SET(275000000, 0x0412c, 0x00000004),
RTCL_REG_SET(300000000, 0x0414c, 0x00000004),
RTCL_REG_SET(325000000, 0x0416c, 0x00000004),
RTCL_REG_SET(350000000, 0x0418c, 0x00000004),
RTCL_REG_SET(375000000, 0x041ac, 0x00000004),
RTCL_REG_SET(400000000, 0x041cc, 0x00000004)
};
static const struct rtcl_reg_set rtcl_839x_lxb_reg_set[] = {
RTCL_REG_SET(50000000, 0x1414c, 0x00000003),
RTCL_REG_SET(100000000, 0x0814c, 0x00000003),
RTCL_REG_SET(150000000, 0x0414c, 0x00000003),
RTCL_REG_SET(200000000, 0x0414c, 0x00000007)
};
struct rtcl_rtab_set {
int count;
const struct rtcl_reg_set *rset;
};
#define RTCL_RTAB_SET(_rset) \
{ \
.count = ARRAY_SIZE(_rset), \
.rset = _rset, \
}
static const struct rtcl_rtab_set rtcl_rtab_set[SOC_COUNT][CLK_COUNT] = {
{
RTCL_RTAB_SET(rtcl_838x_cpu_reg_set),
RTCL_RTAB_SET(rtcl_838x_mem_reg_set),
RTCL_RTAB_SET(rtcl_838x_lxb_reg_set)
}, {
RTCL_RTAB_SET(rtcl_839x_cpu_reg_set),
RTCL_RTAB_SET(rtcl_839x_mem_reg_set),
RTCL_RTAB_SET(rtcl_839x_lxb_reg_set)
}
};
#define RTCL_ROUND_SET(_min, _max, _step) \
{ \
.min = _min, \
.max = _max, \
.step = _step, \
}
struct rtcl_round_set {
unsigned long min;
unsigned long max;
unsigned long step;
};
static const struct rtcl_round_set rtcl_round_set[SOC_COUNT][CLK_COUNT] = {
{
RTCL_ROUND_SET(300000000, 625000000, 25000000),
RTCL_ROUND_SET(200000000, 375000000, 25000000),
RTCL_ROUND_SET(100000000, 200000000, 25000000)
}, {
RTCL_ROUND_SET(400000000, 850000000, 25000000),
RTCL_ROUND_SET(100000000, 400000000, 25000000),
RTCL_ROUND_SET(50000000, 200000000, 50000000)
}
};
static const int rtcl_divn3[] = { 2, 3, 4, 6 };
static const int rtcl_xdiv[] = { 2, 4, 2 };
/*
* module data structures
*/
#define RTCL_CLK_INFO(_idx, _name, _pname, _dname) \
{ \
.idx = _idx, \
.name = _name, \
.parent_name = _pname, \
.display_name = _dname, \
}
struct rtcl_clk_info {
unsigned int idx;
const char *name;
const char *parent_name;
const char *display_name;
};
struct rtcl_clk {
struct clk_hw hw;
unsigned int idx;
unsigned long min;
unsigned long max;
unsigned long rate;
unsigned long startup;
};
static const struct rtcl_clk_info rtcl_clk_info[CLK_COUNT] = {
RTCL_CLK_INFO(CLK_CPU, "cpu_clk", "ref_clk", "CPU"),
RTCL_CLK_INFO(CLK_MEM, "mem_clk", "ref_clk", "MEM"),
RTCL_CLK_INFO(CLK_LXB, "lxb_clk", "ref_clk", "LXB")
};
struct rtcl_dram {
int type;
int buswidth;
};
struct rtcl_sram {
int *pmark;
unsigned long vbase;
};
struct rtcl_ccu {
spinlock_t lock;
unsigned int soc;
struct rtcl_sram sram;
struct rtcl_dram dram;
struct device_node *np;
struct platform_device *pdev;
struct rtcl_clk clks[CLK_COUNT];
};
struct rtcl_ccu *rtcl_ccu;
#define rtcl_hw_to_clk(_hw) container_of(_hw, struct rtcl_clk, hw)
/*
* SRAM relocatable assembler functions. The dram() parts point to normal kernel memory while
* the sram() parts are the same functions but relocated to SRAM.
*/
extern void rtcl_838x_dram_start(void);
extern int rtcl_838x_dram_size;
extern void (*rtcl_838x_dram_set_rate)(int clk_idx, int ctrl0, int ctrl1);
static void (*rtcl_838x_sram_set_rate)(int clk_idx, int ctrl0, int ctrl1);
extern void rtcl_839x_dram_start(void);
extern int rtcl_839x_dram_size;
extern void (*rtcl_839x_dram_set_rate)(int clk_idx, int ctrl0, int ctrl1);
static void (*rtcl_839x_sram_set_rate)(int clk_idx, int ctrl0, int ctrl1);
/*
* clock setter/getter functions
*/
static unsigned long rtcl_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct rtcl_clk *clk = rtcl_hw_to_clk(hw);
unsigned int ctrl0, ctrl1, div1, div2, cmu_ncode_in;
unsigned int cmu_sel_prediv, cmu_sel_div4, cmu_divn2, cmu_divn2_selb, cmu_divn3_sel;
if ((clk->idx >= CLK_COUNT) || (!rtcl_ccu) || (rtcl_ccu->soc >= SOC_COUNT))
return 0;
ctrl0 = read_sw(rtcl_regs[rtcl_ccu->soc][REG_CTRL0][clk->idx]);
ctrl1 = read_sw(rtcl_regs[rtcl_ccu->soc][REG_CTRL1][clk->idx]);
cmu_sel_prediv = 1 << RTL_PLL_CTRL0_CMU_SEL_PREDIV(ctrl0);
cmu_sel_div4 = RTL_PLL_CTRL0_CMU_SEL_DIV4(ctrl0) ? 4 : 1;
cmu_ncode_in = RTL_PLL_CTRL0_CMU_NCODE_IN(ctrl0) + 4;
cmu_divn2 = RTL_PLL_CTRL0_CMU_DIVN2(ctrl0) + 4;
switch (rtcl_ccu->soc) {
case SOC_RTL838X:
if ((ctrl0 == 0) && (ctrl1 == 0) && (clk->idx == CLK_LXB))
return 200000000;
cmu_divn2_selb = RTL838X_PLL_CTRL1_CMU_DIVN2_SELB(ctrl1);
cmu_divn3_sel = rtcl_divn3[RTL838X_PLL_CTRL1_CMU_DIVN3_SEL(ctrl1)];
break;
case SOC_RTL839X:
cmu_divn2_selb = RTL839X_PLL_CTRL1_CMU_DIVN2_SELB(ctrl1);
cmu_divn3_sel = rtcl_divn3[RTL839X_PLL_CTRL1_CMU_DIVN3_SEL(ctrl1)];
break;
}
div1 = cmu_divn2_selb ? cmu_divn3_sel : cmu_divn2;
div2 = rtcl_xdiv[clk->idx];
return (((parent_rate / 16) * cmu_ncode_in) / (div1 * div2)) *
cmu_sel_prediv * cmu_sel_div4 * 16;
}
static int rtcl_838x_set_rate(int clk_idx, const struct rtcl_reg_set *reg)
{
unsigned long irqflags;
/*
* Runtime of this function (including locking)
* CPU: up to 14000 cycles / up to 56 us at 250 MHz (half default speed)
*/
spin_lock_irqsave(&rtcl_ccu->lock, irqflags);
rtcl_838x_sram_set_rate(clk_idx, reg->ctrl0, reg->ctrl1);
spin_unlock_irqrestore(&rtcl_ccu->lock, irqflags);
return 0;
}
static int rtcl_839x_set_rate(int clk_idx, const struct rtcl_reg_set *reg)
{
unsigned long vpflags;
unsigned long irqflags;
/*
* Runtime of this function (including locking)
* CPU: up to 31000 cycles / up to 89 us at 350 MHz (half default speed)
*/
spin_lock_irqsave(&rtcl_ccu->lock, irqflags);
vpflags = dvpe();
rtcl_839x_sram_set_rate(clk_idx, reg->ctrl0, reg->ctrl1);
evpe(vpflags);
spin_unlock_irqrestore(&rtcl_ccu->lock, irqflags);
return 0;
}
static int rtcl_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
{
int tab_idx;
struct rtcl_clk *clk = rtcl_hw_to_clk(hw);
const struct rtcl_rtab_set *rtab = &rtcl_rtab_set[rtcl_ccu->soc][clk->idx];
const struct rtcl_round_set *round = &rtcl_round_set[rtcl_ccu->soc][clk->idx];
if ((parent_rate != OSC_RATE) || (!rtcl_ccu->sram.vbase))
return -EINVAL;
/*
* Currently we do not know if SRAM is stable on these devices. Maybe someone changes memory in
* this region and does not care about proper allocation. So check if something might go wrong.
*/
if (unlikely(*rtcl_ccu->sram.pmark != RTL_SRAM_MARKER)) {
dev_err(&rtcl_ccu->pdev->dev, "SRAM code lost\n");
return -EINVAL;
}
tab_idx = (rate - round->min) / round->step;
if ((tab_idx < 0) || (tab_idx >= rtab->count) || (rtab->rset[tab_idx].rate != rate))
return -EINVAL;
rtcl_ccu->clks[clk->idx].rate = rate;
switch (rtcl_ccu->soc) {
case SOC_RTL838X:
return rtcl_838x_set_rate(clk->idx, &rtab->rset[tab_idx]);
case SOC_RTL839X:
return rtcl_839x_set_rate(clk->idx, &rtab->rset[tab_idx]);
}
return -ENXIO;
}
static long rtcl_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate)
{
struct rtcl_clk *clk = rtcl_hw_to_clk(hw);
unsigned long rrate = max(clk->min, min(clk->max, rate));
const struct rtcl_round_set *round = &rtcl_round_set[rtcl_ccu->soc][clk->idx];
rrate = ((rrate + (round->step >> 1)) / round->step) * round->step;
rrate -= (rrate > clk->max) ? round->step : 0;
rrate += (rrate < clk->min) ? round->step : 0;
return rrate;
}
/*
* Initialization functions to register the CCU and its clocks
*/
#define RTCL_SRAM_FUNC(SOC, PBASE, FN) ({ \
rtcl_##SOC##_sram_##FN = ((void *)&rtcl_##SOC##_dram_##FN \
- (void *)&rtcl_##SOC##_dram_start) \
+ (void *)PBASE; })
static const struct clk_ops rtcl_clk_ops = {
.set_rate = rtcl_set_rate,
.round_rate = rtcl_round_rate,
.recalc_rate = rtcl_recalc_rate,
};
static int rtcl_ccu_create(struct device_node *np)
{
int soc;
if (of_device_is_compatible(np, "realtek,rtl8380-clock"))
soc = SOC_RTL838X;
else if (of_device_is_compatible(np, "realtek,rtl8390-clock"))
soc = SOC_RTL839X;
else
return -ENXIO;
rtcl_ccu = kzalloc(sizeof(*rtcl_ccu), GFP_KERNEL);
if (IS_ERR(rtcl_ccu))
return -ENOMEM;
rtcl_ccu->np = np;
rtcl_ccu->soc = soc;
rtcl_ccu->dram.type = RTL_MC_MCR_DRAMTYPE(read_soc(RTL_MC_MCR));
rtcl_ccu->dram.buswidth = RTL_MC_DCR_BUSWIDTH(read_soc(RTL_MC_DCR));
spin_lock_init(&rtcl_ccu->lock);
return 0;
}
int rtcl_register_clkhw(int clk_idx)
{
int ret;
struct clk *clk;
struct clk_init_data hw_init = { };
struct rtcl_clk *rclk = &rtcl_ccu->clks[clk_idx];
struct clk_parent_data parent_data = { .fw_name = rtcl_clk_info[clk_idx].parent_name };
rclk->idx = clk_idx;
rclk->hw.init = &hw_init;
hw_init.num_parents = 1;
hw_init.ops = &rtcl_clk_ops;
hw_init.parent_data = &parent_data;
hw_init.name = rtcl_clk_info[clk_idx].name;
ret = of_clk_hw_register(rtcl_ccu->np, &rclk->hw);
if (ret)
return ret;
clk_hw_register_clkdev(&rclk->hw, rtcl_clk_info[clk_idx].name, NULL);
clk = clk_get(NULL, rtcl_clk_info[clk_idx].name);
rclk->startup = clk_get_rate(clk);
clk_put(clk);
switch (clk_idx) {
case CLK_CPU:
rclk->min = rtcl_round_set[rtcl_ccu->soc][clk_idx].min;
rclk->max = rtcl_round_set[rtcl_ccu->soc][clk_idx].max;
break;
default:
/*
* TODO: This driver supports PLL reclocking and nothing else. Additional required steps for non
* CPU PLLs are missing. E.g. if we want to change memory clocks the right way we must adapt a lot
* of other settings like MCR and DTRx timing registers (0xb80001000, 0xb8001008, ...) and initiate
* a DLL reset so that hardware operates in the allowed limits. This is far too complex without
* official support. Avoid this for now.
*/
rclk->min = rclk->max = rclk->startup;
break;
}
return 0;
}
static struct clk_hw *rtcl_get_clkhw(struct of_phandle_args *clkspec, void *prv)
{
unsigned int idx = clkspec->args[0];
if (idx >= CLK_COUNT) {
pr_err("%s: Invalid index %u\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
return &rtcl_ccu->clks[idx].hw;
}
static int rtcl_ccu_register_clocks(void)
{
int clk_idx, ret;
for (clk_idx = 0; clk_idx < CLK_COUNT; clk_idx++) {
ret = rtcl_register_clkhw(clk_idx);
if (ret) {
pr_err("%s: Couldn't register %s clock\n",
__func__, rtcl_clk_info[clk_idx].display_name);
goto err_hw_unregister;
}
}
ret = of_clk_add_hw_provider(rtcl_ccu->np, rtcl_get_clkhw, rtcl_ccu);
if (ret) {
pr_err("%s: Couldn't register clock provider of %s\n",
__func__, of_node_full_name(rtcl_ccu->np));
goto err_hw_unregister;
}
return 0;
err_hw_unregister:
for (--clk_idx; clk_idx >= 0; --clk_idx)
clk_hw_unregister(&rtcl_ccu->clks[clk_idx].hw);
return ret;
}
int rtcl_init_sram(void)
{
struct gen_pool *sram_pool;
phys_addr_t sram_pbase;
unsigned long sram_vbase;
struct device_node *node;
struct platform_device *pdev = NULL;
void *dram_start;
int dram_size;
const char *wrn = ", rate setting disabled.\n";
switch (rtcl_ccu->soc) {
case SOC_RTL838X:
dram_start = &rtcl_838x_dram_start;
dram_size = rtcl_838x_dram_size;
break;
case SOC_RTL839X:
dram_start = &rtcl_839x_dram_start;
dram_size = rtcl_839x_dram_size;
break;
default:
return -ENXIO;
}
for_each_compatible_node(node, NULL, "mmio-sram") {
pdev = of_find_device_by_node(node);
if (pdev) {
of_node_put(node);
break;
}
}
if (!pdev) {
dev_warn(&rtcl_ccu->pdev->dev, "no SRAM device found%s", wrn);
return -ENXIO;
}
sram_pool = gen_pool_get(&pdev->dev, NULL);
if (!sram_pool) {
dev_warn(&rtcl_ccu->pdev->dev, "SRAM pool unavailable%s", wrn);
goto err_put_device;
}
sram_vbase = gen_pool_alloc(sram_pool, dram_size);
if (!sram_vbase) {
dev_warn(&rtcl_ccu->pdev->dev, "can not allocate SRAM%s", wrn);
goto err_put_device;
}
sram_pbase = gen_pool_virt_to_phys(sram_pool, sram_vbase);
memcpy((void *)sram_pbase, dram_start, dram_size);
flush_icache_range((unsigned long)sram_pbase, (unsigned long)(sram_pbase + dram_size));
switch (rtcl_ccu->soc) {
case SOC_RTL838X:
RTCL_SRAM_FUNC(838x, sram_pbase, set_rate);
break;
case SOC_RTL839X:
RTCL_SRAM_FUNC(839x, sram_pbase, set_rate);
break;
}
rtcl_ccu->sram.pmark = (int *)((void *)sram_pbase + (dram_size - 4));
rtcl_ccu->sram.vbase = sram_vbase;
return 0;
err_put_device:
put_device(&pdev->dev);
return -ENXIO;
}
void rtcl_ccu_log_early(void)
{
int clk_idx;
char meminfo[80], clkinfo[255], msg[255] = "rtl83xx-clk: initialized";
sprintf(meminfo, " (%d Bit DDR%d)", rtcl_ccu->dram.buswidth, rtcl_ccu->dram.type);
for (clk_idx = 0; clk_idx < CLK_COUNT; clk_idx++) {
sprintf(clkinfo, ", %s %lu MHz", rtcl_clk_info[clk_idx].display_name,
rtcl_ccu->clks[clk_idx].startup / 1000000);
if (clk_idx == CLK_MEM)
strcat(clkinfo, meminfo);
strcat(msg, clkinfo);
}
pr_info("%s\n", msg);
}
void rtcl_ccu_log_late(void)
{
int clk_idx;
struct rtcl_clk *rclk;
bool overclock = false;
char clkinfo[80], msg[255] = "rate setting enabled";
for (clk_idx = 0; clk_idx < CLK_COUNT; clk_idx++) {
rclk = &rtcl_ccu->clks[clk_idx];
overclock |= rclk->max > rclk->startup;
sprintf(clkinfo, ", %s %lu-%lu MHz", rtcl_clk_info[clk_idx].display_name,
rclk->min / 1000000, rclk->max / 1000000);
strcat(msg, clkinfo);
}
if (overclock)
strcat(msg, ", OVERCLOCK AT OWN RISK");
dev_info(&rtcl_ccu->pdev->dev, "%s\n", msg);
}
/*
* Early registration: This module provides core startup clocks that are needed for generic SOC
* init and for further builtin devices (e.g. UART). Register asap via clock framework.
*/
static void __init rtcl_probe_early(struct device_node *np)
{
if (rtcl_ccu_create(np))
return;
if (rtcl_ccu_register_clocks())
kfree(rtcl_ccu);
else
rtcl_ccu_log_early();
}
CLK_OF_DECLARE_DRIVER(rtl838x_clk, "realtek,rtl8380-clock", rtcl_probe_early);
CLK_OF_DECLARE_DRIVER(rtl839x_clk, "realtek,rtl8390-clock", rtcl_probe_early);
/*
* Late registration: Finally register as normal platform driver. At this point we can make use
* of other modules like SRAM.
*/
static const struct of_device_id rtcl_dt_ids[] = {
{ .compatible = "realtek,rtl8380-clock" },
{ .compatible = "realtek,rtl8390-clock" },
{}
};
static int rtcl_probe_late(struct platform_device *pdev)
{
int ret;
if (!rtcl_ccu) {
dev_err(&pdev->dev, "early initialization not run");
return -ENXIO;
}
rtcl_ccu->pdev = pdev;
ret = rtcl_init_sram();
if (ret)
return ret;
rtcl_ccu_log_late();
return 0;
}
static struct platform_driver rtcl_platform_driver = {
.driver = {
.name = "rtl83xx-clk",
.of_match_table = rtcl_dt_ids,
},
.probe = rtcl_probe_late,
};
static int __init rtcl_init_subsys(void)
{
return platform_driver_register(&rtcl_platform_driver);
}
/*
* The driver does not know when SRAM module has finally loaded. With an arch_initcall() we might
* overtake SRAM initialization. Be polite and give the system a little more time.
*/
subsys_initcall(rtcl_init_subsys);

View file

@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Realtek RTL83XX clock headers
* Copyright (C) 2022 Markus Stockhausen <markus.stockhausen@gmx.de>
*/
/*
* Switch registers (e.g. PLL)
*/
#define RTL_SW_CORE_BASE (0xbb000000)
#define RTL838X_PLL_GLB_CTRL (0x0fc0)
#define RTL838X_PLL_CPU_CTRL0 (0x0fc4)
#define RTL838X_PLL_CPU_CTRL1 (0x0fc8)
#define RTL838X_PLL_LXB_CTRL0 (0x0fd0)
#define RTL838X_PLL_LXB_CTRL1 (0x0fd4)
#define RTL838X_PLL_MEM_CTRL0 (0x0fdc)
#define RTL838X_PLL_MEM_CTRL1 (0x0fe0)
#define RTL839X_PLL_GLB_CTRL (0x0024)
#define RTL839X_PLL_CPU_CTRL0 (0x0028)
#define RTL839X_PLL_CPU_CTRL1 (0x002c)
#define RTL839X_PLL_LXB_CTRL0 (0x0038)
#define RTL839X_PLL_LXB_CTRL1 (0x003c)
#define RTL839X_PLL_MEM_CTRL0 (0x0048)
#define RTL839X_PLL_MEM_CTRL1 (0x004c)
#define RTL_PLL_CTRL0_CMU_SEL_PREDIV(v) (((v) >> 0) & 0x3)
#define RTL_PLL_CTRL0_CMU_SEL_DIV4(v) (((v) >> 2) & 0x1)
#define RTL_PLL_CTRL0_CMU_NCODE_IN(v) (((v) >> 4) & 0xff)
#define RTL_PLL_CTRL0_CMU_DIVN2(v) (((v) >> 12) & 0xff)
#define RTL838X_GLB_CTRL_EN_CPU_PLL_MASK (1 << 0)
#define RTL838X_GLB_CTRL_EN_LXB_PLL_MASK (1 << 1)
#define RTL838X_GLB_CTRL_EN_MEM_PLL_MASK (1 << 2)
#define RTL838X_GLB_CTRL_CPU_PLL_READY_MASK (1 << 8)
#define RTL838X_GLB_CTRL_LXB_PLL_READY_MASK (1 << 9)
#define RTL838X_GLB_CTRL_MEM_PLL_READY_MASK (1 << 10)
#define RTL838X_GLB_CTRL_CPU_PLL_SC_MUX_MASK (1 << 12)
#define RTL838X_PLL_CTRL1_CMU_DIVN2_SELB(v) (((v) >> 26) & 0x1)
#define RTL838X_PLL_CTRL1_CMU_DIVN3_SEL(v) (((v) >> 27) & 0x3)
#define RTL839X_GLB_CTRL_CPU_CLKSEL_MASK (1 << 11)
#define RTL839X_GLB_CTRL_MEM_CLKSEL_MASK (1 << 12)
#define RTL839X_GLB_CTRL_LXB_CLKSEL_MASK (1 << 13)
#define RTL839X_PLL_CTRL1_CMU_DIVN2_SELB(v) (((v) >> 2) & 0x1)
#define RTL839X_PLL_CTRL1_CMU_DIVN3_SEL(v) (((v) >> 0) & 0x3)
/*
* Core registers (e.g. memory controller)
*/
#define RTL_SOC_BASE (0xB8000000)
#define RTL_MC_MCR (0x1000)
#define RTL_MC_DCR (0x1004)
#define RTL_MC_DTR0 (0x1008)
#define RTL_MC_DTR1 (0x100c)
#define RTL_MC_DTR2 (0x1010)
#define RTL_MC_DMCR (0x101c)
#define RTL_MC_DACCR (0x1500)
#define RTL_MC_DCDR (0x1060)
#define RTL_MC_MCR_DRAMTYPE(v) ((((v) >> 28) & 0xf) + 1)
#define RTL_MC_DCR_BUSWIDTH(v) (8 << (((v) >> 24) & 0xf))
/*
* Other stuff
*/
#define RTL_SRAM_MARKER (0x5eaf00d5)
#define RTL_SRAM_BASE (0x9f000000)

View file

@ -0,0 +1,299 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/cpu.h>
#include <linux/cpuhotplug.h>
#include <linux/interrupt.h>
#include <linux/sched_clock.h>
#include "timer-of.h"
#define RTTM_DATA 0x0
#define RTTM_CNT 0x4
#define RTTM_CTRL 0x8
#define RTTM_INT 0xc
#define RTTM_CTRL_ENABLE BIT(28)
#define RTTM_INT_PENDING BIT(16)
#define RTTM_INT_ENABLE BIT(20)
/*
* The Otto platform provides multiple 28 bit timers/counters with the following
* operating logic. If enabled the timer counts up. Per timer one can set a
* maximum counter value as an end marker. If end marker is reached the timer
* fires an interrupt. If the timer "overflows" by reaching the end marker or
* by adding 1 to 0x0fffffff the counter is reset to 0. When this happens and
* the timer is in operating mode COUNTER it stops. In mode TIMER it will
* continue to count up.
*/
#define RTTM_CTRL_COUNTER 0
#define RTTM_CTRL_TIMER BIT(24)
#define RTTM_BIT_COUNT 28
#define RTTM_MIN_DELTA 8
#define RTTM_MAX_DELTA CLOCKSOURCE_MASK(28)
/*
* Timers are derived from the LXB clock frequency. Usually this is a fixed
* multiple of the 25 MHz oscillator. The 930X SOC is an exception from that.
* Its LXB clock has only dividers and uses the switch PLL of 2.45 GHz as its
* base. The only meaningful frequencies we can achieve from that are 175.000
* MHz and 153.125 MHz. The greatest common divisor of all explained possible
* speeds is 3125000. Pin the timers to this 3.125 MHz reference frequency.
*/
#define RTTM_TICKS_PER_SEC 3125000
struct rttm_cs {
struct timer_of to;
struct clocksource cs;
};
/*
* Simple internal register functions
*/
static inline void rttm_set_counter(void __iomem *base, unsigned int counter)
{
iowrite32(counter, base + RTTM_CNT);
}
static inline unsigned int rttm_get_counter(void __iomem *base)
{
return ioread32(base + RTTM_CNT);
}
static inline void rttm_set_period(void __iomem *base, unsigned int period)
{
iowrite32(period, base + RTTM_DATA);
}
static inline void rttm_disable_timer(void __iomem *base)
{
iowrite32(0, base + RTTM_CTRL);
}
static inline void rttm_enable_timer(void __iomem *base, u32 mode, u32 divisor)
{
iowrite32(RTTM_CTRL_ENABLE | mode | divisor, base + RTTM_CTRL);
}
static inline void rttm_ack_irq(void __iomem *base)
{
iowrite32(ioread32(base + RTTM_INT) | RTTM_INT_PENDING, base + RTTM_INT);
}
static inline void rttm_enable_irq(void __iomem *base)
{
iowrite32(RTTM_INT_ENABLE, base + RTTM_INT);
}
static inline void rttm_disable_irq(void __iomem *base)
{
iowrite32(0, base + RTTM_INT);
}
/*
* Aggregated control functions for kernel clock framework
*/
#define RTTM_DEBUG(base) \
pr_debug("------------- %s %d %08x\n", __func__, \
smp_processor_id(), (u32)base)
static irqreturn_t rttm_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *clkevt = dev_id;
struct timer_of *to = to_timer_of(clkevt);
rttm_ack_irq(to->of_base.base);
RTTM_DEBUG(to->of_base.base);
clkevt->event_handler(clkevt);
return IRQ_HANDLED;
}
static void rttm_stop_timer(void __iomem *base)
{
rttm_disable_timer(base);
rttm_ack_irq(base);
}
static void rttm_start_timer(struct timer_of *to, u32 mode)
{
rttm_set_counter(to->of_base.base, 0);
rttm_enable_timer(to->of_base.base, mode, to->of_clk.rate / RTTM_TICKS_PER_SEC);
}
static int rttm_next_event(unsigned long delta, struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
RTTM_DEBUG(to->of_base.base);
rttm_stop_timer(to->of_base.base);
rttm_set_period(to->of_base.base, delta);
rttm_start_timer(to, RTTM_CTRL_COUNTER);
return 0;
}
static int rttm_state_oneshot(struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
RTTM_DEBUG(to->of_base.base);
rttm_stop_timer(to->of_base.base);
rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ);
rttm_start_timer(to, RTTM_CTRL_COUNTER);
return 0;
}
static int rttm_state_periodic(struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
RTTM_DEBUG(to->of_base.base);
rttm_stop_timer(to->of_base.base);
rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ);
rttm_start_timer(to, RTTM_CTRL_TIMER);
return 0;
}
static int rttm_state_shutdown(struct clock_event_device *clkevt)
{
struct timer_of *to = to_timer_of(clkevt);
RTTM_DEBUG(to->of_base.base);
rttm_stop_timer(to->of_base.base);
return 0;
}
static void rttm_setup_timer(void __iomem *base)
{
RTTM_DEBUG(base);
rttm_stop_timer(base);
rttm_set_period(base, 0);
}
static u64 rttm_read_clocksource(struct clocksource *cs)
{
struct rttm_cs *rcs = container_of(cs, struct rttm_cs, cs);
return (u64)rttm_get_counter(rcs->to.of_base.base);
}
/*
* Module initialization part.
*/
static DEFINE_PER_CPU(struct timer_of, rttm_to) = {
.flags = TIMER_OF_BASE | TIMER_OF_CLOCK | TIMER_OF_IRQ,
.of_irq = {
.flags = IRQF_PERCPU | IRQF_TIMER,
.handler = rttm_timer_interrupt,
},
.clkevt = {
.rating = 400,
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_state_periodic = rttm_state_periodic,
.set_state_shutdown = rttm_state_shutdown,
.set_state_oneshot = rttm_state_oneshot,
.set_next_event = rttm_next_event
},
};
static int rttm_enable_clocksource(struct clocksource *cs)
{
struct rttm_cs *rcs = container_of(cs, struct rttm_cs, cs);
rttm_disable_irq(rcs->to.of_base.base);
rttm_setup_timer(rcs->to.of_base.base);
rttm_enable_timer(rcs->to.of_base.base, RTTM_CTRL_TIMER,
rcs->to.of_clk.rate / RTTM_TICKS_PER_SEC);
return 0;
}
struct rttm_cs rttm_cs = {
.to = {
.flags = TIMER_OF_BASE | TIMER_OF_CLOCK,
},
.cs = {
.name = "realtek_otto_timer",
.rating = 400,
.mask = CLOCKSOURCE_MASK(RTTM_BIT_COUNT),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.read = rttm_read_clocksource,
.enable = rttm_enable_clocksource
}
};
static u64 notrace rttm_read_clock(void)
{
return (u64)rttm_get_counter(rttm_cs.to.of_base.base);
}
static int rttm_cpu_starting(unsigned int cpu)
{
struct timer_of *to = per_cpu_ptr(&rttm_to, cpu);
RTTM_DEBUG(to->of_base.base);
to->clkevt.cpumask = cpumask_of(cpu);
irq_set_affinity(to->of_irq.irq, to->clkevt.cpumask);
clockevents_config_and_register(&to->clkevt, RTTM_TICKS_PER_SEC,
RTTM_MIN_DELTA, RTTM_MAX_DELTA);
rttm_enable_irq(to->of_base.base);
return 0;
}
static int __init rttm_probe(struct device_node *np)
{
int cpu, cpu_rollback;
struct timer_of *to;
int clkidx = num_possible_cpus();
/*
* Use the first n timers as per CPU clock event generators
*/
for_each_possible_cpu(cpu) {
to = per_cpu_ptr(&rttm_to, cpu);
to->of_irq.index = to->of_base.index = cpu;
if (timer_of_init(np, to)) {
pr_err("%s: setup of timer %d failed\n", __func__, cpu);
goto rollback;
}
rttm_setup_timer(to->of_base.base);
}
/*
* Activate the n'th+1 timer as a stable CPU clocksource.
*/
to = &rttm_cs.to;
to->of_base.index = clkidx;
timer_of_init(np, to);
if (rttm_cs.to.of_base.base && rttm_cs.to.of_clk.rate) {
clocksource_register_hz(&rttm_cs.cs, RTTM_TICKS_PER_SEC);
sched_clock_register(rttm_read_clock, RTTM_BIT_COUNT, RTTM_TICKS_PER_SEC);
} else
pr_err("%s: setup of timer %d as clocksoure failed", __func__, clkidx);
return cpuhp_setup_state(CPUHP_AP_REALTEK_TIMER_STARTING,
"timer/realtek:online",
rttm_cpu_starting, NULL);
rollback:
pr_err("%s: timer registration failed\n", __func__);
for_each_possible_cpu(cpu_rollback) {
if (cpu_rollback == cpu)
break;
to = per_cpu_ptr(&rttm_to, cpu_rollback);
timer_of_cleanup(to);
}
return -EINVAL;
}
TIMER_OF_DECLARE(otto_timer, "realtek,otto-timer", rttm_probe);

View file

@ -0,0 +1,355 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <asm/mach-rtl838x/mach-rtl83xx.h>
/* RTL8231 registers for LED control */
#define RTL8231_LED_FUNC0 0x0000
#define RTL8231_LED_FUNC1 0x0001
#define RTL8231_READY_MASK 0x03f0
#define RTL8231_READY_VALUE 0x0370
#define RTL8231_GPIO_PIN_SEL(gpio) ((0x0002) + ((gpio) >> 4))
#define RTL8231_GPIO_DIR(gpio) ((0x0005) + ((gpio) >> 4))
#define RTL8231_GPIO_DATA(gpio) ((0x001C) + ((gpio) >> 4))
#define USEC_TIMEOUT 5000
#define RTL8231_SMI_BUS_ID_MAX 0x1F
struct rtl8231_gpios {
struct gpio_chip gc;
struct device *dev;
u32 id;
u32 smi_bus_id;
u16 reg_shadow[0x20];
u32 reg_cached;
int ext_gpio_indrt_access;
};
extern struct rtl83xx_soc_info soc_info;
DEFINE_MUTEX(miim_lock);
static u32 rtl8231_read(struct rtl8231_gpios *gpios, u32 reg)
{
u32 t = 0, n = 0;
reg &= 0x1f;
/* Calculate read register address */
t = (gpios->smi_bus_id << 2) | (reg << 7);
/* Set execution bit: cleared when operation completed */
t |= 1;
// Start execution
sw_w32(t, gpios->ext_gpio_indrt_access);
do {
udelay(1);
t = sw_r32(gpios->ext_gpio_indrt_access);
n++;
} while ((t & 1) && (n < USEC_TIMEOUT));
if (n >= USEC_TIMEOUT)
return 0x80000000;
pr_debug("%s: %x, %x, %x\n", __func__, gpios->smi_bus_id,
reg, (t & 0xffff0000) >> 16);
return (t & 0xffff0000) >> 16;
}
static int rtl8231_write(struct rtl8231_gpios *gpios, u32 reg, u32 data)
{
u32 t = 0, n = 0;
pr_debug("%s: %x, %x, %x\n", __func__, gpios->smi_bus_id, reg, data);
reg &= 0x1f;
t = (gpios->smi_bus_id << 2) | (reg << 7) | (data << 16);
/* Set write bit */
t |= 2;
/* Set execution bit: cleared when operation completed */
t |= 1;
// Start execution
sw_w32(t, gpios->ext_gpio_indrt_access);
do {
udelay(1);
t = sw_r32(gpios->ext_gpio_indrt_access);
} while ((t & 1) && (n < USEC_TIMEOUT));
if (n >= USEC_TIMEOUT)
return -1;
return 0;
}
static u32 rtl8231_read_cached(struct rtl8231_gpios *gpios, u32 reg)
{
if (reg > 0x1f)
return 0;
if (gpios->reg_cached & (1 << reg))
return gpios->reg_shadow[reg];
return rtl8231_read(gpios, reg);
}
/* Set Direction of the RTL8231 pin:
* dir 1: input
* dir 0: output
*/
static int rtl8231_pin_dir(struct rtl8231_gpios *gpios, u32 gpio, u32 dir)
{
u32 v;
int pin_sel_addr = RTL8231_GPIO_PIN_SEL(gpio);
int pin_dir_addr = RTL8231_GPIO_DIR(gpio);
int dpin = gpio % 16;
if (gpio > 31) {
pr_debug("WARNING: HIGH pin\n");
dpin += 5;
pin_dir_addr = pin_sel_addr;
}
v = rtl8231_read_cached(gpios, pin_dir_addr);
if (v & 0x80000000) {
pr_err("Error reading RTL8231\n");
return -1;
}
v = (v & ~(1 << dpin)) | (dir << dpin);
rtl8231_write(gpios, pin_dir_addr, v);
gpios->reg_shadow[pin_dir_addr] = v;
gpios->reg_cached |= 1 << pin_dir_addr;
return 0;
}
static int rtl8231_pin_dir_get(struct rtl8231_gpios *gpios, u32 gpio, u32 *dir)
{
/* dir 1: input
* dir 0: output
*/
u32 v;
int pin_dir_addr = RTL8231_GPIO_DIR(gpio);
int pin = gpio % 16;
if (gpio > 31) {
pin_dir_addr = RTL8231_GPIO_PIN_SEL(gpio);
pin += 5;
}
v = rtl8231_read(gpios, pin_dir_addr);
if (v & (1 << pin))
*dir = 1;
else
*dir = 0;
return 0;
}
static int rtl8231_pin_set(struct rtl8231_gpios *gpios, u32 gpio, u32 data)
{
u32 v = rtl8231_read(gpios, RTL8231_GPIO_DATA(gpio));
pr_debug("%s: %d to %d\n", __func__, gpio, data);
if (v & 0x80000000) {
pr_err("Error reading RTL8231\n");
return -1;
}
v = (v & ~(1 << (gpio % 16))) | (data << (gpio % 16));
rtl8231_write(gpios, RTL8231_GPIO_DATA(gpio), v);
gpios->reg_shadow[RTL8231_GPIO_DATA(gpio)] = v;
gpios->reg_cached |= 1 << RTL8231_GPIO_DATA(gpio);
return 0;
}
static int rtl8231_pin_get(struct rtl8231_gpios *gpios, u32 gpio, u16 *state)
{
u32 v = rtl8231_read(gpios, RTL8231_GPIO_DATA(gpio));
if (v & 0x80000000) {
pr_err("Error reading RTL8231\n");
return -1;
}
*state = v & 0xffff;
return 0;
}
static int rtl8231_direction_input(struct gpio_chip *gc, unsigned int offset)
{
int err;
struct rtl8231_gpios *gpios = gpiochip_get_data(gc);
pr_debug("%s: %d\n", __func__, offset);
mutex_lock(&miim_lock);
err = rtl8231_pin_dir(gpios, offset, 1);
mutex_unlock(&miim_lock);
return err;
}
static int rtl8231_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
{
int err;
struct rtl8231_gpios *gpios = gpiochip_get_data(gc);
pr_debug("%s: %d\n", __func__, offset);
mutex_lock(&miim_lock);
err = rtl8231_pin_dir(gpios, offset, 0);
mutex_unlock(&miim_lock);
if (!err)
err = rtl8231_pin_set(gpios, offset, value);
return err;
}
static int rtl8231_get_direction(struct gpio_chip *gc, unsigned int offset)
{
u32 v = 0;
struct rtl8231_gpios *gpios = gpiochip_get_data(gc);
pr_debug("%s: %d\n", __func__, offset);
mutex_lock(&miim_lock);
rtl8231_pin_dir_get(gpios, offset, &v);
mutex_unlock(&miim_lock);
return v;
}
static int rtl8231_gpio_get(struct gpio_chip *gc, unsigned int offset)
{
u16 state = 0;
struct rtl8231_gpios *gpios = gpiochip_get_data(gc);
mutex_lock(&miim_lock);
rtl8231_pin_get(gpios, offset, &state);
mutex_unlock(&miim_lock);
if (state & (1 << (offset % 16)))
return 1;
return 0;
}
void rtl8231_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
struct rtl8231_gpios *gpios = gpiochip_get_data(gc);
rtl8231_pin_set(gpios, offset, value);
}
int rtl8231_init(struct rtl8231_gpios *gpios)
{
u32 ret;
pr_info("%s called, MDIO bus ID: %d\n", __func__, gpios->smi_bus_id);
gpios->reg_cached = 0;
if (soc_info.family == RTL8390_FAMILY_ID) {
// RTL8390: Enable external gpio in global led control register
sw_w32_mask(0x7 << 18, 0x4 << 18, RTL839X_LED_GLB_CTRL);
} else if (soc_info.family == RTL8380_FAMILY_ID) {
// RTL8380: Enable RTL8231 indirect access mode
sw_w32_mask(0, 1, RTL838X_EXTRA_GPIO_CTRL);
sw_w32_mask(3, 1, RTL838X_DMY_REG5);
}
ret = rtl8231_read(gpios, RTL8231_LED_FUNC1);
if ((ret & 0x80000000) || ((ret & RTL8231_READY_MASK) != RTL8231_READY_VALUE))
return -ENXIO;
/* Select GPIO functionality and force input direction for pins 0-36 */
rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(0), 0xffff);
rtl8231_write(gpios, RTL8231_GPIO_DIR(0), 0xffff);
rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(16), 0xffff);
rtl8231_write(gpios, RTL8231_GPIO_DIR(16), 0xffff);
rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(32), 0x03ff);
/* Set LED_Start to enable drivers for output mode */
rtl8231_write(gpios, RTL8231_LED_FUNC0, 1 << 1);
return 0;
}
static const struct of_device_id rtl8231_gpio_of_match[] = {
{ .compatible = "realtek,rtl8231-gpio" },
{},
};
MODULE_DEVICE_TABLE(of, rtl8231_gpio_of_match);
static int rtl8231_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct rtl8231_gpios *gpios;
int err;
pr_info("Probing RTL8231 GPIOs\n");
if (!np) {
dev_err(&pdev->dev, "No DT found\n");
return -EINVAL;
}
gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL);
if (!gpios)
return -ENOMEM;
gpios->id = soc_info.id;
if (soc_info.family == RTL8380_FAMILY_ID) {
gpios->ext_gpio_indrt_access = RTL838X_EXT_GPIO_INDRT_ACCESS;
}
if (soc_info.family == RTL8390_FAMILY_ID) {
gpios->ext_gpio_indrt_access = RTL839X_EXT_GPIO_INDRT_ACCESS;
}
err = of_property_read_u32(np, "indirect-access-bus-id", &gpios->smi_bus_id);
if (!err && gpios->smi_bus_id > RTL8231_SMI_BUS_ID_MAX)
err = -EINVAL;
if (err) {
dev_err(dev, "invalid or missing indirect-access-bus-id\n");
return err;
}
err = rtl8231_init(gpios);
if (err) {
dev_err(dev, "no device found at bus address %d\n", gpios->smi_bus_id);
return err;
}
gpios->dev = dev;
gpios->gc.base = -1;
gpios->gc.ngpio = 37;
gpios->gc.label = "rtl8231";
gpios->gc.parent = dev;
gpios->gc.owner = THIS_MODULE;
gpios->gc.can_sleep = true;
gpios->gc.direction_input = rtl8231_direction_input;
gpios->gc.direction_output = rtl8231_direction_output;
gpios->gc.set = rtl8231_gpio_set;
gpios->gc.get = rtl8231_gpio_get;
gpios->gc.get_direction = rtl8231_get_direction;
err = devm_gpiochip_add_data(dev, &gpios->gc, gpios);
return err;
}
static struct platform_driver rtl8231_gpio_driver = {
.driver = {
.name = "rtl8231-gpio",
.of_match_table = rtl8231_gpio_of_match,
},
.probe = rtl8231_gpio_probe,
};
module_platform_driver(rtl8231_gpio_driver);
MODULE_DESCRIPTION("Realtek RTL8231 GPIO expansion chip support");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,488 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <linux/of_platform.h>
#include "i2c-rtl9300.h"
#define REG(i, x) (i->base + x + (i->scl_num ? i->mst2_offset : 0))
#define REG_MASK(i, clear, set, reg) \
writel((readl(REG(i, reg)) & ~(clear)) | (set), REG(i, reg))
struct i2c_drv_data {
int scl0_pin;
int scl1_pin;
int sda0_pin;
struct i2c_algorithm *algo;
int (*read)(struct rtl9300_i2c *i2c, u8 *buf, int len);
int (*write)(struct rtl9300_i2c *i2c, u8 *buf, int len);
void (*reg_addr_set)(struct rtl9300_i2c *i2c, u32 reg, u16 len);
int (*config_xfer)(struct rtl9300_i2c *i2c, u16 addr, u16 len);
int (*execute_xfer)(struct rtl9300_i2c *i2c, char read_write, int size,
union i2c_smbus_data * data, int len);
void (*writel)(struct rtl9300_i2c *i2c, u32 data);
void (*config_io)(struct rtl9300_i2c *i2c, int scl_num, int sda_num);
u32 mst2_offset;
};
DEFINE_MUTEX(i2c_lock);
static void rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
{
// Set register address width
REG_MASK(i2c, 0x3 << RTL9300_I2C_CTRL2_MADDR_WIDTH, len << RTL9300_I2C_CTRL2_MADDR_WIDTH,
RTL9300_I2C_CTRL2);
// Set register address
REG_MASK(i2c, 0xffffff << RTL9300_I2C_CTRL1_MEM_ADDR, reg << RTL9300_I2C_CTRL1_MEM_ADDR,
RTL9300_I2C_CTRL1);
}
static void rtl9310_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
{
// Set register address width
REG_MASK(i2c, 0x3 << RTL9310_I2C_CTRL_MADDR_WIDTH, len << RTL9310_I2C_CTRL_MADDR_WIDTH,
RTL9310_I2C_CTRL);
// Set register address
writel(reg, REG(i2c, RTL9310_I2C_MEMADDR));
}
static void rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, int scl_num, int sda_num)
{
u32 v;
// Set SCL pin
REG_MASK(i2c, 0, BIT(RTL9300_I2C_CTRL1_GPIO8_SCL_SEL), RTL9300_I2C_CTRL1);
// Set SDA pin
REG_MASK(i2c, 0x7 << RTL9300_I2C_CTRL1_SDA_OUT_SEL,
i2c->sda_num << RTL9300_I2C_CTRL1_SDA_OUT_SEL, RTL9300_I2C_CTRL1);
// Set SDA pin to I2C functionality
v = readl(i2c->base + RTL9300_I2C_MST_GLB_CTRL);
v |= BIT(i2c->sda_num);
writel(v, i2c->base + RTL9300_I2C_MST_GLB_CTRL);
}
static void rtl9310_i2c_config_io(struct rtl9300_i2c *i2c, int scl_num, int sda_num)
{
u32 v;
// Set SCL pin
REG_MASK(i2c, 0, BIT(RTL9310_I2C_MST_IF_SEL_GPIO_SCL_SEL + scl_num), RTL9310_I2C_MST_IF_SEL);
// Set SDA pin
REG_MASK(i2c, 0x7 << RTL9310_I2C_CTRL_SDA_OUT_SEL,
i2c->sda_num << RTL9310_I2C_CTRL_SDA_OUT_SEL, RTL9310_I2C_CTRL);
// Set SDA pin to I2C functionality
v = readl(i2c->base + RTL9310_I2C_MST_IF_SEL);
v |= BIT(i2c->sda_num);
writel(v, i2c->base + RTL9310_I2C_MST_IF_SEL);
}
static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, u16 addr, u16 len)
{
// Set bus frequency
REG_MASK(i2c, 0x3 << RTL9300_I2C_CTRL2_SCL_FREQ,
i2c->bus_freq << RTL9300_I2C_CTRL2_SCL_FREQ, RTL9300_I2C_CTRL2);
// Set slave device address
REG_MASK(i2c, 0x7f << RTL9300_I2C_CTRL2_DEV_ADDR,
addr << RTL9300_I2C_CTRL2_DEV_ADDR, RTL9300_I2C_CTRL2);
// Set data length
REG_MASK(i2c, 0xf << RTL9300_I2C_CTRL2_DATA_WIDTH,
((len - 1) & 0xf) << RTL9300_I2C_CTRL2_DATA_WIDTH, RTL9300_I2C_CTRL2);
// Set read mode to random
REG_MASK(i2c, 0x1 << RTL9300_I2C_CTRL2_READ_MODE, 0, RTL9300_I2C_CTRL2);
return 0;
}
static int rtl9310_i2c_config_xfer(struct rtl9300_i2c *i2c, u16 addr, u16 len)
{
// Set bus frequency
REG_MASK(i2c, 0x3 << RTL9310_I2C_CTRL_SCL_FREQ,
i2c->bus_freq << RTL9310_I2C_CTRL_SCL_FREQ, RTL9310_I2C_CTRL);
// Set slave device address
REG_MASK(i2c, 0x7f << RTL9310_I2C_CTRL_DEV_ADDR,
addr << RTL9310_I2C_CTRL_DEV_ADDR, RTL9310_I2C_CTRL);
// Set data length
REG_MASK(i2c, 0xf << RTL9310_I2C_CTRL_DATA_WIDTH,
((len - 1) & 0xf) << RTL9310_I2C_CTRL_DATA_WIDTH, RTL9310_I2C_CTRL);
// Set read mode to random
REG_MASK(i2c, 0x1 << RTL9310_I2C_CTRL_READ_MODE, 0, RTL9310_I2C_CTRL);
return 0;
}
static int i2c_read(void __iomem *r0, u8 *buf, int len)
{
int i;
u32 v;
if (len > 16)
return -EIO;
for (i = 0; i < len; i++) {
if (i % 4 == 0)
v = readl(r0 + i);
buf[i] = v;
v >>= 8;
}
return len;
}
static int i2c_write(void __iomem *r0, u8 *buf, int len)
{
u32 v;
int i;
if (len > 16)
return -EIO;
for (i = 0; i < len; i++) {
if (! (i % 4))
v = 0;
v <<= 8;
v |= buf[i];
if (i % 4 == 3 || i == len - 1)
writel(v, r0 + (i / 4) * 4);
}
return len;
}
static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
{
return i2c_read(REG(i2c, RTL9300_I2C_DATA_WORD0), buf, len);
}
static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
{
return i2c_write(REG(i2c, RTL9300_I2C_DATA_WORD0), buf, len);
}
static int rtl9310_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
{
return i2c_read(REG(i2c, RTL9310_I2C_DATA), buf, len);
}
static int rtl9310_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
{
return i2c_write(REG(i2c, RTL9310_I2C_DATA), buf, len);
}
static void rtl9300_writel(struct rtl9300_i2c *i2c, u32 data)
{
writel(data, REG(i2c, RTL9300_I2C_DATA_WORD0));
}
static void rtl9310_writel(struct rtl9300_i2c *i2c, u32 data)
{
writel(data, REG(i2c, RTL9310_I2C_DATA));
}
static int rtl9300_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
int size, union i2c_smbus_data * data, int len)
{
u32 v;
if (read_write == I2C_SMBUS_READ)
REG_MASK(i2c, BIT(RTL9300_I2C_CTRL1_RWOP), 0, RTL9300_I2C_CTRL1);
else
REG_MASK(i2c, 0, BIT(RTL9300_I2C_CTRL1_RWOP), RTL9300_I2C_CTRL1);
REG_MASK(i2c, 0, BIT(RTL9300_I2C_CTRL1_I2C_TRIG), RTL9300_I2C_CTRL1);
do {
v = readl(REG(i2c, RTL9300_I2C_CTRL1));
} while (v & BIT(RTL9300_I2C_CTRL1_I2C_TRIG));
if (v & BIT(RTL9300_I2C_CTRL1_I2C_FAIL))
return -EIO;
if (read_write == I2C_SMBUS_READ) {
if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA){
data->byte = readl(REG(i2c, RTL9300_I2C_DATA_WORD0));
} else if (size == I2C_SMBUS_WORD_DATA) {
data->word = readl(REG(i2c, RTL9300_I2C_DATA_WORD0));
} else if (len > 0) {
rtl9300_i2c_read(i2c, &data->block[0], len);
}
}
return 0;
}
static int rtl9310_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
int size, union i2c_smbus_data * data, int len)
{
u32 v;
if (read_write == I2C_SMBUS_READ)
REG_MASK(i2c, BIT(RTL9310_I2C_CTRL_RWOP), 0, RTL9310_I2C_CTRL);
else
REG_MASK(i2c, 0, BIT(RTL9310_I2C_CTRL_RWOP), RTL9310_I2C_CTRL);
REG_MASK(i2c, 0, BIT(RTL9310_I2C_CTRL_I2C_TRIG), RTL9310_I2C_CTRL);
do {
v = readl(REG(i2c, RTL9310_I2C_CTRL));
} while (v & BIT(RTL9310_I2C_CTRL_I2C_TRIG));
if (v & BIT(RTL9310_I2C_CTRL_I2C_FAIL))
return -EIO;
if (read_write == I2C_SMBUS_READ) {
if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA){
data->byte = readl(REG(i2c, RTL9310_I2C_DATA));
} else if (size == I2C_SMBUS_WORD_DATA) {
data->word = readl(REG(i2c, RTL9310_I2C_DATA));
} else if (len > 0) {
rtl9310_i2c_read(i2c, &data->block[0], len);
}
}
return 0;
}
static int rtl9300_i2c_smbus_xfer(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
struct rtl9300_i2c *i2c = i2c_get_adapdata(adap);
struct i2c_drv_data *drv_data = (struct i2c_drv_data *)device_get_match_data(i2c->dev);
int len = 0, ret;
mutex_lock(&i2c_lock);
switch (size) {
case I2C_SMBUS_QUICK:
drv_data->config_xfer(i2c, addr, 0);
drv_data->reg_addr_set(i2c, 0, 0);
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_WRITE) {
drv_data->config_xfer(i2c, addr, 0);
drv_data->reg_addr_set(i2c, command, 1);
} else {
drv_data->config_xfer(i2c, addr, 1);
drv_data->reg_addr_set(i2c, 0, 0);
}
break;
case I2C_SMBUS_BYTE_DATA:
pr_debug("I2C_SMBUS_BYTE_DATA %02x, read %d cmd %02x\n", addr, read_write, command);
drv_data->reg_addr_set(i2c, command, 1);
drv_data->config_xfer(i2c, addr, 1);
if (read_write == I2C_SMBUS_WRITE) {
pr_debug("--> data %02x\n", data->byte);
drv_data->writel(i2c, data->byte);
}
break;
case I2C_SMBUS_WORD_DATA:
pr_debug("I2C_SMBUS_WORD %02x, read %d\n", addr, read_write);
drv_data->reg_addr_set(i2c, command, 1);
drv_data->config_xfer(i2c, addr, 2);
if (read_write == I2C_SMBUS_WRITE)
drv_data->writel(i2c, data->word);
break;
case I2C_SMBUS_BLOCK_DATA:
pr_debug("I2C_SMBUS_BLOCK_DATA %02x, read %d, len %d\n",
addr, read_write, data->block[0]);
drv_data->reg_addr_set(i2c, command, 1);
drv_data->config_xfer(i2c, addr, data->block[0]);
if (read_write == I2C_SMBUS_WRITE)
drv_data->write(i2c, &data->block[1], data->block[0]);
len = data->block[0];
break;
default:
dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
return -EOPNOTSUPP;
}
ret = drv_data->execute_xfer(i2c, read_write, size, data, len);
mutex_unlock(&i2c_lock);
return ret;
}
static u32 rtl9300_i2c_func(struct i2c_adapter *a)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
static const struct i2c_algorithm rtl9300_i2c_algo = {
.smbus_xfer = rtl9300_i2c_smbus_xfer,
.functionality = rtl9300_i2c_func,
};
struct i2c_adapter_quirks rtl9300_i2c_quirks = {
.flags = I2C_AQ_NO_CLK_STRETCH,
.max_read_len = 16,
.max_write_len = 16,
};
static int rtl9300_i2c_probe(struct platform_device *pdev)
{
struct resource *res;
struct rtl9300_i2c *i2c;
struct i2c_adapter *adap;
struct i2c_drv_data *drv_data;
struct device_node *node = pdev->dev.of_node;
u32 clock_freq, pin;
int ret = 0;
pr_info("%s probing I2C adapter\n", __func__);
if (!node) {
dev_err(i2c->dev, "No DT found\n");
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
drv_data = (struct i2c_drv_data *) device_get_match_data(&pdev->dev);
i2c = devm_kzalloc(&pdev->dev, sizeof(struct rtl9300_i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
i2c->base = devm_ioremap_resource(&pdev->dev, res);
i2c->mst2_offset = drv_data->mst2_offset;
if (IS_ERR(i2c->base))
return PTR_ERR(i2c->base);
pr_debug("%s base memory %08x\n", __func__, (u32)i2c->base);
i2c->dev = &pdev->dev;
if (of_property_read_u32(node, "clock-frequency", &clock_freq)) {
clock_freq = I2C_MAX_STANDARD_MODE_FREQ;
}
switch(clock_freq) {
case I2C_MAX_STANDARD_MODE_FREQ:
i2c->bus_freq = RTL9300_I2C_STD_FREQ;
break;
case I2C_MAX_FAST_MODE_FREQ:
i2c->bus_freq = RTL9300_I2C_FAST_FREQ;
break;
default:
dev_warn(i2c->dev, "clock-frequency %d not supported\n", clock_freq);
return -EINVAL;
}
dev_info(&pdev->dev, "SCL speed %d, mode is %d\n", clock_freq, i2c->bus_freq);
if (of_property_read_u32(node, "scl-pin", &pin)) {
dev_warn(i2c->dev, "SCL pin not found in DT, using default\n");
pin = drv_data->scl0_pin;
}
if (!(pin == drv_data->scl0_pin || pin == drv_data->scl1_pin)) {
dev_warn(i2c->dev, "SCL pin %d not supported\n", pin);
return -EINVAL;
}
i2c->scl_num = pin == drv_data->scl0_pin ? 0 : 1;
pr_info("%s scl_num %d\n", __func__, i2c->scl_num);
if (of_property_read_u32(node, "sda-pin", &pin)) {
dev_warn(i2c->dev, "SDA pin not found in DT, using default \n");
pin = drv_data->sda0_pin;
}
i2c->sda_num = pin - drv_data->sda0_pin;
if (i2c->sda_num < 0 || i2c->sda_num > 7) {
dev_warn(i2c->dev, "SDA pin %d not supported\n", pin);
return -EINVAL;
}
pr_info("%s sda_num %d\n", __func__, i2c->sda_num);
adap = &i2c->adap;
adap->owner = THIS_MODULE;
adap->algo = &rtl9300_i2c_algo;
adap->retries = 3;
adap->dev.parent = &pdev->dev;
i2c_set_adapdata(adap, i2c);
adap->dev.of_node = node;
strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
platform_set_drvdata(pdev, i2c);
drv_data->config_io(i2c, i2c->scl_num, i2c->sda_num);
ret = i2c_add_adapter(adap);
return ret;
}
static int rtl9300_i2c_remove(struct platform_device *pdev)
{
struct rtl9300_i2c *i2c = platform_get_drvdata(pdev);
i2c_del_adapter(&i2c->adap);
return 0;
}
struct i2c_drv_data rtl9300_i2c_drv_data = {
.scl0_pin = 8,
.scl1_pin = 17,
.sda0_pin = 9,
.read = rtl9300_i2c_read,
.read = rtl9300_i2c_write,
.reg_addr_set = rtl9300_i2c_reg_addr_set,
.config_xfer = rtl9300_i2c_config_xfer,
.execute_xfer = rtl9300_execute_xfer,
.writel = rtl9300_writel,
.config_io = rtl9300_i2c_config_io,
.mst2_offset = 0x1c,
};
struct i2c_drv_data rtl9310_i2c_drv_data = {
.scl0_pin = 13,
.scl1_pin = 14,
.sda0_pin = 0,
.read = rtl9310_i2c_read,
.read = rtl9310_i2c_write,
.reg_addr_set = rtl9310_i2c_reg_addr_set,
.config_xfer = rtl9310_i2c_config_xfer,
.execute_xfer = rtl9310_execute_xfer,
.writel = rtl9310_writel,
.config_io = rtl9310_i2c_config_io,
.mst2_offset = 0x18,
};
static const struct of_device_id i2c_rtl9300_dt_ids[] = {
{ .compatible = "realtek,rtl9300-i2c", .data = (void *) &rtl9300_i2c_drv_data },
{ .compatible = "realtek,rtl9310-i2c", .data = (void *) &rtl9310_i2c_drv_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rtl838x_eth_of_ids);
static struct platform_driver rtl9300_i2c_driver = {
.probe = rtl9300_i2c_probe,
.remove = rtl9300_i2c_remove,
.driver = {
.name = "i2c-rtl9300",
.pm = NULL,
.of_match_table = i2c_rtl9300_dt_ids,
},
};
module_platform_driver(rtl9300_i2c_driver);
MODULE_AUTHOR("Birger Koblitz");
MODULE_DESCRIPTION("RTL9300 I2C host driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,62 @@
#ifndef I2C_RTL9300_H
#define I2C_RTL9300_H
#include <linux/i2c.h>
#define RTL9300_I2C_CTRL1 0x00
#define RTL9300_I2C_CTRL1_MEM_ADDR 8
#define RTL9300_I2C_CTRL1_SDA_OUT_SEL 4
#define RTL9300_I2C_CTRL1_GPIO8_SCL_SEL 3
#define RTL9300_I2C_CTRL1_RWOP 2
#define RTL9300_I2C_CTRL1_I2C_FAIL 1
#define RTL9300_I2C_CTRL1_I2C_TRIG 0
#define RTL9300_I2C_CTRL2 0x04
#define RTL9300_I2C_CTRL2_DRIVE_ACK_DELAY 20
#define RTL9300_I2C_CTRL2_CHECK_ACK_DELAY 16
#define RTL9300_I2C_CTRL2_READ_MODE 15
#define RTL9300_I2C_CTRL2_DEV_ADDR 8
#define RTL9300_I2C_CTRL2_DATA_WIDTH 4
#define RTL9300_I2C_CTRL2_MADDR_WIDTH 2
#define RTL9300_I2C_CTRL2_SCL_FREQ 0
#define RTL9300_I2C_DATA_WORD0 0x08
#define RTL9300_I2C_MST_GLB_CTRL 0x18
#define RTL9310_I2C_MST_IF_CTRL 0x00
#define RTL9310_I2C_MST_IF_SEL 0x04
#define RTL9310_I2C_MST_IF_SEL_GPIO_SCL_SEL 12
#define RTL9310_I2C_CTRL 0x08
#define RTL9310_I2C_CTRL_SCL_FREQ 30
#define RTL9310_I2C_CTRL_CHECK_ACK_DELAY 26
#define RTL9310_I2C_CTRL_DRIVE_ACK_DELAY 22
#define RTL9310_I2C_CTRL_SDA_OUT_SEL 18
#define RTL9310_I2C_CTRL_DEV_ADDR 11
#define RTL9310_I2C_CTRL_MADDR_WIDTH 9
#define RTL9310_I2C_CTRL_DATA_WIDTH 5
#define RTL9310_I2C_CTRL_READ_MODE 4
#define RTL9310_I2C_CTRL_RWOP 2
#define RTL9310_I2C_CTRL_I2C_FAIL 1
#define RTL9310_I2C_CTRL_I2C_TRIG 0
#define RTL9310_I2C_MEMADDR 0x0c
#define RTL9310_I2C_DATA 0x10
#define RTL9300_I2C_STD_FREQ 0
#define RTL9300_I2C_FAST_FREQ 1
struct rtl9300_i2c {
void __iomem *base;
u32 mst2_offset;
struct device *dev;
struct i2c_adapter adap;
u8 bus_freq;
u8 sda_num; // SDA channel number
u8 scl_num; // SCL channel, mapping to master 1 or 2
};
#endif

View file

@ -0,0 +1,293 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C multiplexer for the 2 I2C Masters of the RTL9300
* with up to 8 channels each, but which are not entirely
* independent of each other
*/
#include <linux/i2c-mux.h>
#include <linux/module.h>
#include <linux/mux/consumer.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include "../busses/i2c-rtl9300.h"
#define NUM_MASTERS 2
#define NUM_BUSSES 8
#define REG(mst, x) (mux->base + x + (mst ? mux->i2c->mst2_offset : 0))
#define REG_MASK(mst, clear, set, reg) \
writel((readl(REG((mst),(reg))) & ~(clear)) | (set), REG((mst),(reg)))
struct channel {
u8 sda_num;
u8 scl_num;
};
static struct channel channels[NUM_MASTERS * NUM_BUSSES];
struct rtl9300_mux {
void __iomem *base;
struct device *dev;
struct i2c_adapter *parent;
struct rtl9300_i2c * i2c;
};
struct i2c_mux_data {
int scl0_pin;
int scl1_pin;
int sda0_pin;
int sda_pins;
int (*i2c_mux_select)(struct i2c_mux_core *muxc, u32 chan);
int (*i2c_mux_deselect)(struct i2c_mux_core *muxc, u32 chan);
void (*sda_sel)(struct i2c_mux_core *muxc, int pin);
};
static int rtl9300_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
{
struct rtl9300_mux *mux = i2c_mux_priv(muxc);
// Set SCL pin
REG_MASK(channels[chan].scl_num, 0,
BIT(RTL9300_I2C_CTRL1_GPIO8_SCL_SEL), RTL9300_I2C_CTRL1);
// Set SDA pin
REG_MASK(channels[chan].scl_num, 0x7 << RTL9300_I2C_CTRL1_SDA_OUT_SEL,
channels[chan].sda_num << RTL9300_I2C_CTRL1_SDA_OUT_SEL, RTL9300_I2C_CTRL1);
mux->i2c->sda_num = channels[chan].sda_num;
mux->i2c->scl_num = channels[chan].scl_num;
return 0;
}
static int rtl9310_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
{
struct rtl9300_mux *mux = i2c_mux_priv(muxc);
// Set SCL pin
REG_MASK(0, 0, BIT(RTL9310_I2C_MST_IF_SEL_GPIO_SCL_SEL + channels[chan].scl_num),
RTL9310_I2C_MST_IF_SEL);
// Set SDA pin
REG_MASK(channels[chan].scl_num, 0xf << RTL9310_I2C_CTRL_SDA_OUT_SEL,
channels[chan].sda_num << RTL9310_I2C_CTRL_SDA_OUT_SEL, RTL9310_I2C_CTRL);
mux->i2c->sda_num = channels[chan].sda_num;
mux->i2c->scl_num = channels[chan].scl_num;
return 0;
}
static int rtl9300_i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
{
return 0;
}
static void rtl9300_sda_sel(struct i2c_mux_core *muxc, int pin)
{
struct rtl9300_mux *mux = i2c_mux_priv(muxc);
u32 v;
// Set SDA pin to I2C functionality
v = readl(REG(0, RTL9300_I2C_MST_GLB_CTRL));
v |= BIT(pin);
writel(v, REG(0, RTL9300_I2C_MST_GLB_CTRL));
}
static void rtl9310_sda_sel(struct i2c_mux_core *muxc, int pin)
{
struct rtl9300_mux *mux = i2c_mux_priv(muxc);
u32 v;
// Set SDA pin to I2C functionality
v = readl(REG(0, RTL9310_I2C_MST_IF_SEL));
v |= BIT(pin);
writel(v, REG(0, RTL9310_I2C_MST_IF_SEL));
}
static struct device_node *mux_parent_adapter(struct device *dev, struct rtl9300_mux *mux)
{
struct device_node *node = dev->of_node;
struct device_node *parent_np;
struct i2c_adapter *parent;
parent_np = of_parse_phandle(node, "i2c-parent", 0);
if (!parent_np) {
dev_err(dev, "Cannot parse i2c-parent\n");
return ERR_PTR(-ENODEV);
}
parent = of_find_i2c_adapter_by_node(parent_np);
of_node_put(parent_np);
if (!parent)
return ERR_PTR(-EPROBE_DEFER);
if (!(of_device_is_compatible(parent_np, "realtek,rtl9300-i2c")
|| of_device_is_compatible(parent_np, "realtek,rtl9310-i2c"))){
dev_err(dev, "I2C parent not an RTL9300 I2C controller\n");
return ERR_PTR(-ENODEV);
}
mux->parent = parent;
mux->i2c = (struct rtl9300_i2c *)i2c_get_adapdata(parent);
mux->base = mux->i2c->base;
return parent_np;
}
struct i2c_mux_data rtl9300_i2c_mux_data = {
.scl0_pin = 8,
.scl1_pin = 17,
.sda0_pin = 9,
.sda_pins = 8,
.i2c_mux_select = rtl9300_i2c_mux_select,
.i2c_mux_deselect = rtl9300_i2c_mux_deselect,
.sda_sel = rtl9300_sda_sel,
};
struct i2c_mux_data rtl9310_i2c_mux_data = {
.scl0_pin = 13,
.scl1_pin = 14,
.sda0_pin = 0,
.sda_pins = 16,
.i2c_mux_select = rtl9310_i2c_mux_select,
.i2c_mux_deselect = rtl9300_i2c_mux_deselect,
.sda_sel = rtl9310_sda_sel,
};
static const struct of_device_id rtl9300_i2c_mux_of_match[] = {
{ .compatible = "realtek,i2c-mux-rtl9300", .data = (void *) &rtl9300_i2c_mux_data},
{ .compatible = "realtek,i2c-mux-rtl9310", .data = (void *) &rtl9310_i2c_mux_data},
{},
};
MODULE_DEVICE_TABLE(of, rtl9300_i2c_mux_of_match);
static int rtl9300_i2c_mux_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct device_node *parent_np;
struct device_node *child;
struct i2c_mux_core *muxc;
struct rtl9300_mux *mux;
struct i2c_mux_data *mux_data;
int children;
int ret;
pr_info("%s probing I2C adapter\n", __func__);
if (!node) {
dev_err(dev, "No DT found\n");
return -EINVAL;
}
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return -ENOMEM;
mux->dev = dev;
mux_data = (struct i2c_mux_data *) device_get_match_data(dev);
parent_np = mux_parent_adapter(dev, mux);
if (IS_ERR(parent_np))
return dev_err_probe(dev, PTR_ERR(parent_np), "i2c-parent adapter not found\n");
pr_info("%s base memory %08x\n", __func__, (u32)mux->base);
children = of_get_child_count(node);
muxc = i2c_mux_alloc(mux->parent, dev, children, 0, 0,
mux_data->i2c_mux_select, mux_data->i2c_mux_deselect);
if (!muxc) {
ret = -ENOMEM;
goto err_parent;
}
muxc->priv = mux;
platform_set_drvdata(pdev, muxc);
for_each_child_of_node(node, child) {
u32 chan;
u32 pin;
ret = of_property_read_u32(child, "reg", &chan);
if (ret < 0) {
dev_err(dev, "no reg property for node '%pOFn'\n",
child);
goto err_children;
}
if (chan >= NUM_MASTERS * NUM_BUSSES) {
dev_err(dev, "invalid reg %u\n", chan);
ret = -EINVAL;
goto err_children;
}
if (of_property_read_u32(child, "scl-pin", &pin)) {
dev_warn(dev, "SCL pin not found in DT, using default\n");
pin = mux_data->scl0_pin;
}
if (!(pin == mux_data->scl0_pin || pin == mux_data->scl1_pin)) {
dev_warn(dev, "SCL pin %d not supported\n", pin);
ret = -EINVAL;
goto err_children;
}
channels[chan].scl_num = pin == mux_data->scl0_pin ? 0 : 1;
pr_info("%s channel %d scl_num %d\n", __func__, chan, channels[chan].scl_num);
if (of_property_read_u32(child, "sda-pin", &pin)) {
dev_warn(dev, "SDA pin not found in DT, using default \n");
pin = mux_data->sda0_pin;
}
channels[chan].sda_num = pin - mux_data->sda0_pin;
if (channels[chan].sda_num < 0 || channels[chan].sda_num >= mux_data->sda_pins) {
dev_warn(dev, "SDA pin %d not supported\n", pin);
return -EINVAL;
}
pr_info("%s channel %d sda_num %d\n", __func__, chan, channels[chan].sda_num);
mux_data->sda_sel(muxc, channels[chan].sda_num);
ret = i2c_mux_add_adapter(muxc, 0, chan, 0);
if (ret)
goto err_children;
}
dev_info(dev, "%d-port mux on %s adapter\n", children, mux->parent->name);
return 0;
err_children:
i2c_mux_del_adapters(muxc);
err_parent:
i2c_put_adapter(mux->parent);
return ret;
}
static int rtl9300_i2c_mux_remove(struct platform_device *pdev)
{
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
i2c_mux_del_adapters(muxc);
i2c_put_adapter(muxc->parent);
return 0;
}
static struct platform_driver i2c_mux_driver = {
.probe = rtl9300_i2c_mux_probe,
.remove = rtl9300_i2c_mux_remove,
.driver = {
.name = "i2c-mux-rtl9300",
.of_match_table = rtl9300_i2c_mux_of_match,
},
};
module_platform_driver(i2c_mux_driver);
MODULE_DESCRIPTION("RTL9300 I2C multiplexer driver");
MODULE_AUTHOR("Birger Koblitz");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
config NET_DSA_RTL83XX
tristate "Realtek RTL838x/RTL839x switch support"
depends on RTL83XX
select NET_DSA_TAG_TRAILER
help
This driver adds support for Realtek RTL83xx series switching.

View file

@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_NET_DSA_RTL83XX) += common.o dsa.o \
rtl838x.o rtl839x.o rtl930x.o rtl931x.o debugfs.o qos.o tc.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,727 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <asm/mach-rtl838x/mach-rtl83xx.h>
#include "rtl83xx.h"
#define RTL838X_DRIVER_NAME "rtl838x"
#define RTL8380_LED_GLB_CTRL (0xA000)
#define RTL8380_LED_MODE_SEL (0x1004)
#define RTL8380_LED_MODE_CTRL (0xA004)
#define RTL8380_LED_P_EN_CTRL (0xA008)
#define RTL8380_LED_SW_CTRL (0xA00C)
#define RTL8380_LED0_SW_P_EN_CTRL (0xA010)
#define RTL8380_LED1_SW_P_EN_CTRL (0xA014)
#define RTL8380_LED2_SW_P_EN_CTRL (0xA018)
#define RTL8380_LED_SW_P_CTRL(p) (0xA01C + (((p) << 2)))
#define RTL8390_LED_GLB_CTRL (0x00E4)
#define RTL8390_LED_SET_2_3_CTRL (0x00E8)
#define RTL8390_LED_SET_0_1_CTRL (0x00EC)
#define RTL8390_LED_COPR_SET_SEL_CTRL(p) (0x00F0 + (((p >> 4) << 2)))
#define RTL8390_LED_FIB_SET_SEL_CTRL(p) (0x0100 + (((p >> 4) << 2)))
#define RTL8390_LED_COPR_PMASK_CTRL(p) (0x0110 + (((p >> 5) << 2)))
#define RTL8390_LED_FIB_PMASK_CTRL(p) (0x00118 + (((p >> 5) << 2)))
#define RTL8390_LED_COMBO_CTRL(p) (0x0120 + (((p >> 5) << 2)))
#define RTL8390_LED_SW_CTRL (0x0128)
#define RTL8390_LED_SW_P_EN_CTRL(p) (0x012C + (((p / 10) << 2)))
#define RTL8390_LED_SW_P_CTRL(p) (0x0144 + (((p) << 2)))
#define RTL838X_MIR_QID_CTRL(grp) (0xAD44 + (((grp) << 2)))
#define RTL838X_MIR_RSPAN_VLAN_CTRL(grp) (0xA340 + (((grp) << 2)))
#define RTL838X_MIR_RSPAN_VLAN_CTRL_MAC(grp) (0xAA70 + (((grp) << 2)))
#define RTL838X_MIR_RSPAN_TX_CTRL (0xA350)
#define RTL838X_MIR_RSPAN_TX_TAG_RM_CTRL (0xAA80)
#define RTL838X_MIR_RSPAN_TX_TAG_EN_CTRL (0xAA84)
#define RTL839X_MIR_RSPAN_VLAN_CTRL(grp) (0xA340 + (((grp) << 2)))
#define RTL839X_MIR_RSPAN_TX_CTRL (0x69b0)
#define RTL839X_MIR_RSPAN_TX_TAG_RM_CTRL (0x2550)
#define RTL839X_MIR_RSPAN_TX_TAG_EN_CTRL (0x2554)
#define RTL839X_MIR_SAMPLE_RATE_CTRL (0x2558)
#define RTL838X_STAT_PRVTE_DROP_COUNTERS (0x6A00)
#define RTL839X_STAT_PRVTE_DROP_COUNTERS (0x3E00)
#define RTL930X_STAT_PRVTE_DROP_COUNTERS (0xB5B8)
#define RTL931X_STAT_PRVTE_DROP_COUNTERS (0xd800)
int rtl83xx_port_get_stp_state(struct rtl838x_switch_priv *priv, int port);
void rtl83xx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
void rtl83xx_fast_age(struct dsa_switch *ds, int port);
u32 rtl838x_get_egress_rate(struct rtl838x_switch_priv *priv, int port);
u32 rtl839x_get_egress_rate(struct rtl838x_switch_priv *priv, int port);
int rtl838x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate);
int rtl839x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate);
const char *rtl838x_drop_cntr[] = {
"ALE_TX_GOOD_PKTS", "MAC_RX_DROP", "ACL_FWD_DROP", "HW_ATTACK_PREVENTION_DROP",
"RMA_DROP", "VLAN_IGR_FLTR_DROP", "INNER_OUTER_CFI_EQUAL_1_DROP", "PORT_MOVE_DROP",
"NEW_SA_DROP", "MAC_LIMIT_SYS_DROP", "MAC_LIMIT_VLAN_DROP", "MAC_LIMIT_PORT_DROP",
"SWITCH_MAC_DROP", "ROUTING_EXCEPTION_DROP", "DA_LKMISS_DROP", "RSPAN_DROP",
"ACL_LKMISS_DROP", "ACL_DROP", "INBW_DROP", "IGR_METER_DROP",
"ACCEPT_FRAME_TYPE_DROP", "STP_IGR_DROP", "INVALID_SA_DROP", "SA_BLOCKING_DROP",
"DA_BLOCKING_DROP", "L2_INVALID_DPM_DROP", "MCST_INVALID_DPM_DROP", "RX_FLOW_CONTROL_DROP",
"STORM_SPPRS_DROP", "LALS_DROP", "VLAN_EGR_FILTER_DROP", "STP_EGR_DROP",
"SRC_PORT_FILTER_DROP", "PORT_ISOLATION_DROP", "ACL_FLTR_DROP", "MIRROR_FLTR_DROP",
"TX_MAX_DROP", "LINK_DOWN_DROP", "FLOW_CONTROL_DROP", "BRIDGE .1d discards"
};
const char *rtl839x_drop_cntr[] = {
"ALE_TX_GOOD_PKTS", "ERROR_PKTS", "EGR_ACL_DROP", "EGR_METER_DROP",
"OAM", "CFM" "VLAN_IGR_FLTR", "VLAN_ERR",
"INNER_OUTER_CFI_EQUAL_1", "VLAN_TAG_FORMAT", "SRC_PORT_SPENDING_TREE", "INBW",
"RMA", "HW_ATTACK_PREVENTION", "PROTO_STORM", "MCAST_SA",
"IGR_ACL_DROP", "IGR_METER_DROP", "DFLT_ACTION_FOR_MISS_ACL_AND_C2SC", "NEW_SA",
"PORT_MOVE", "SA_BLOCKING", "ROUTING_EXCEPTION", "SRC_PORT_SPENDING_TREE_NON_FWDING",
"MAC_LIMIT", "UNKNOW_STORM", "MISS_DROP", "CPU_MAC_DROP",
"DA_BLOCKING", "SRC_PORT_FILTER_BEFORE_EGR_ACL", "VLAN_EGR_FILTER", "SPANNING_TRE",
"PORT_ISOLATION", "OAM_EGRESS_DROP", "MIRROR_ISOLATION", "MAX_LEN_BEFORE_EGR_ACL",
"SRC_PORT_FILTER_BEFORE_MIRROR", "MAX_LEN_BEFORE_MIRROR", "SPECIAL_CONGEST_BEFORE_MIRROR",
"LINK_STATUS_BEFORE_MIRROR",
"WRED_BEFORE_MIRROR", "MAX_LEN_AFTER_MIRROR", "SPECIAL_CONGEST_AFTER_MIRROR",
"LINK_STATUS_AFTER_MIRROR",
"WRED_AFTER_MIRROR"
};
const char *rtl930x_drop_cntr[] = {
"OAM_PARSER", "UC_RPF", "DEI_CFI", "MAC_IP_SUBNET_BASED_VLAN", "VLAN_IGR_FILTER",
"L2_UC_MC", "IPV_IP6_MC_BRIDGE", "PTP", "USER_DEF_0_3", "RESERVED",
"RESERVED1", "RESERVED2", "BPDU_RMA", "LACP", "LLDP",
"EAPOL", "XX_RMA", "L3_IPUC_NON_IP", "IP4_IP6_HEADER_ERROR", "L3_BAD_IP",
"L3_DIP_DMAC_MISMATCH", "IP4_IP_OPTION", "IP_UC_MC_ROUTING_LOOK_UP_MISS", "L3_DST_NULL_INTF",
"L3_PBR_NULL_INTF",
"HOST_NULL_INTF", "ROUTE_NULL_INTF", "BRIDGING_ACTION", "ROUTING_ACTION", "IPMC_RPF",
"L2_NEXTHOP_AGE_OUT", "L3_UC_TTL_FAIL", "L3_MC_TTL_FAIL", "L3_UC_MTU_FAIL", "L3_MC_MTU_FAIL",
"L3_UC_ICMP_REDIR", "IP6_MLD_OTHER_ACT", "ND", "IP_MC_RESERVED", "IP6_HBH",
"INVALID_SA", "L2_HASH_FULL", "NEW_SA", "PORT_MOVE_FORBID", "STATIC_PORT_MOVING",
"DYNMIC_PORT_MOVING", "L3_CRC", "MAC_LIMIT", "ATTACK_PREVENT", "ACL_FWD_ACTION",
"OAMPDU", "OAM_MUX", "TRUNK_FILTER", "ACL_DROP", "IGR_BW",
"ACL_METER", "VLAN_ACCEPT_FRAME_TYPE", "MSTP_SRC_DROP_DISABLED_BLOCKING", "SA_BLOCK", "DA_BLOCK",
"STORM_CONTROL", "VLAN_EGR_FILTER", "MSTP_DESTINATION_DROP", "SRC_PORT_FILTER", "PORT_ISOLATION",
"TX_MAX_FRAME_SIZE", "EGR_LINK_STATUS", "MAC_TX_DISABLE", "MAC_PAUSE_FRAME", "MAC_RX_DROP",
"MIRROR_ISOLATE", "RX_FC", "EGR_QUEUE", "HSM_RUNOUT", "ROUTING_DISABLE", "INVALID_L2_NEXTHOP_ENTRY",
"L3_MC_SRC_FLT", "CPUTAG_FLT", "FWD_PMSK_NULL", "IPUC_ROUTING_LOOKUP_MISS", "MY_DEV_DROP",
"STACK_NONUC_BLOCKING_PMSK", "STACK_PORT_NOT_FOUND", "ACL_LOOPBACK_DROP", "IP6_ROUTING_EXT_HEADER"
};
const char *rtl931x_drop_cntr[] = {
"ALE_RX_GOOD_PKTS", "RX_MAX_FRAME_SIZE", "MAC_RX_DROP", "OPENFLOW_IP_MPLS_TTL", "OPENFLOW_TBL_MISS",
"IGR_BW", "SPECIAL_CONGEST", "EGR_QUEUE", "RESERVED", "EGR_LINK_STATUS", "STACK_UCAST_NONUCAST_TTL", // 10
"STACK_NONUC_BLOCKING_PMSK", "L2_CRC", "SRC_PORT_FILTER", "PARSER_PACKET_TOO_LONG", "PARSER_MALFORM_PACKET",
"MPLS_OVER_2_LBL", "EACL_METER", "IACL_METER", "PROTO_STORM", "INVALID_CAPWAP_HEADER", // 20
"MAC_IP_SUBNET_BASED_VLAN", "OAM_PARSER", "UC_MC_RPF", "IP_MAC_BINDING_MATCH_MISMATCH", "SA_BLOCK",
"TUNNEL_IP_ADDRESS_CHECK", "EACL_DROP", "IACL_DROP", "ATTACK_PREVENT", "SYSTEM_PORT_LIMIT_LEARN", // 30,
"OAMPDU", "CCM_RX", "CFM_UNKNOWN_TYPE", "LBM_LBR_LTM_LTR", "Y_1731", "VLAN_LIMIT_LEARN",
"VLAN_ACCEPT_FRAME_TYPE", "CFI_1", "STATIC_DYNAMIC_PORT_MOVING", "PORT_MOVE_FORBID", // 40
"L3_CRC", "BPDU_PTP_LLDP_EAPOL_RMA", "MSTP_SRC_DROP_DISABLED_BLOCKING", "INVALID_SA", "NEW_SA",
"VLAN_IGR_FILTER", "IGR_VLAN_CONVERT", "GRATUITOUS_ARP", "MSTP_SRC_DROP", "L2_HASH_FULL", // 50
"MPLS_UNKNOWN_LBL", "L3_IPUC_NON_IP", "TTL", "MTU", "ICMP_REDIRECT", "STORM_CONTROL", "L3_DIP_DMAC_MISMATCH",
"IP4_IP_OPTION", "IP6_HBH_EXT_HEADER", "IP4_IP6_HEADER_ERROR", // 60
"ROUTING_IP_ADDR_CHECK", "ROUTING_EXCEPTION", "DA_BLOCK", "OAM_MUX", "PORT_ISOLATION", "VLAN_EGR_FILTER",
"MIRROR_ISOLATE", "MSTP_DESTINATION_DROP", "L2_MC_BRIDGE", "IP_UC_MC_ROUTING_LOOK_UP_MISS", // 70
"L2_UC", "L2_MC", "IP4_MC", "IP6_MC", "L3_UC_MC_ROUTE", "UNKNOWN_L2_UC_FLPM", "BC_FLPM",
"VLAN_PRO_UNKNOWN_L2_MC_FLPM", "VLAN_PRO_UNKNOWN_IP4_MC_FLPM", "VLAN_PROFILE_UNKNOWN_IP6_MC_FLPM" // 80,
};
static ssize_t rtl838x_common_read(char __user *buffer, size_t count,
loff_t *ppos, unsigned int value)
{
char *buf;
ssize_t len;
if (*ppos != 0)
return 0;
buf = kasprintf(GFP_KERNEL, "0x%08x\n", value);
if (!buf)
return -ENOMEM;
if (count < strlen(buf)) {
kfree(buf);
return -ENOSPC;
}
len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
kfree(buf);
return len;
}
static ssize_t rtl838x_common_write(const char __user *buffer, size_t count,
loff_t *ppos, unsigned int *value)
{
char b[32];
ssize_t len;
int ret;
if (*ppos != 0)
return -EINVAL;
if (count >= sizeof(b))
return -ENOSPC;
len = simple_write_to_buffer(b, sizeof(b) - 1, ppos,
buffer, count);
if (len < 0)
return len;
b[len] = '\0';
ret = kstrtouint(b, 16, value);
if (ret)
return -EIO;
return len;
}
static ssize_t stp_state_read(struct file *filp, char __user *buffer, size_t count,
loff_t *ppos)
{
struct rtl838x_port *p = filp->private_data;
struct dsa_switch *ds = p->dp->ds;
int value = rtl83xx_port_get_stp_state(ds->priv, p->dp->index);
if (value < 0)
return -EINVAL;
return rtl838x_common_read(buffer, count, ppos, (u32)value);
}
static ssize_t stp_state_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct rtl838x_port *p = filp->private_data;
u32 value;
size_t res = rtl838x_common_write(buffer, count, ppos, &value);
if (res < 0)
return res;
rtl83xx_port_stp_state_set(p->dp->ds, p->dp->index, (u8)value);
return res;
}
static const struct file_operations stp_state_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = stp_state_read,
.write = stp_state_write,
};
static ssize_t drop_counter_read(struct file *filp, char __user *buffer, size_t count,
loff_t *ppos)
{
struct rtl838x_switch_priv *priv = filp->private_data;
int i;
const char **d;
u32 v;
char *buf;
int n = 0, len, offset;
int num;
switch (priv->family_id) {
case RTL8380_FAMILY_ID:
d = rtl838x_drop_cntr;
offset = RTL838X_STAT_PRVTE_DROP_COUNTERS;
num = 40;
break;
case RTL8390_FAMILY_ID:
d = rtl839x_drop_cntr;
offset = RTL839X_STAT_PRVTE_DROP_COUNTERS;
num = 45;
break;
case RTL9300_FAMILY_ID:
d = rtl930x_drop_cntr;
offset = RTL930X_STAT_PRVTE_DROP_COUNTERS;
num = 85;
break;
case RTL9310_FAMILY_ID:
d = rtl931x_drop_cntr;
offset = RTL931X_STAT_PRVTE_DROP_COUNTERS;
num = 81;
break;
}
buf = kmalloc(30 * num, GFP_KERNEL);
if (!buf)
return -ENOMEM;
for (i = 0; i < num; i++) {
v = sw_r32(offset + (i << 2)) & 0xffff;
n += sprintf(buf + n, "%s: %d\n", d[i], v);
}
if (count < strlen(buf)) {
kfree(buf);
return -ENOSPC;
}
len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
kfree(buf);
return len;
}
static const struct file_operations drop_counter_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = drop_counter_read,
};
static void l2_table_print_entry(struct seq_file *m, struct rtl838x_switch_priv *priv,
struct rtl838x_l2_entry *e)
{
u64 portmask;
int i;
if (e->type == L2_UNICAST) {
seq_puts(m, "L2_UNICAST\n");
seq_printf(m, " mac %02x:%02x:%02x:%02x:%02x:%02x vid %u rvid %u\n",
e->mac[0], e->mac[1], e->mac[2], e->mac[3], e->mac[4], e->mac[5],
e->vid, e->rvid);
seq_printf(m, " port %d age %d", e->port, e->age);
if (e->is_static)
seq_puts(m, " static");
if (e->block_da)
seq_puts(m, " block_da");
if (e->block_sa)
seq_puts(m, " block_sa");
if (e->suspended)
seq_puts(m, " suspended");
if (e->next_hop)
seq_printf(m, " next_hop route_id %u", e->nh_route_id);
seq_puts(m, "\n");
} else {
if (e->type == L2_MULTICAST) {
seq_puts(m, "L2_MULTICAST\n");
seq_printf(m, " mac %02x:%02x:%02x:%02x:%02x:%02x vid %u rvid %u\n",
e->mac[0], e->mac[1], e->mac[2], e->mac[3], e->mac[4], e->mac[5],
e->vid, e->rvid);
}
if (e->type == IP4_MULTICAST || e->type == IP6_MULTICAST) {
seq_puts(m, (e->type == IP4_MULTICAST) ?
"IP4_MULTICAST\n" : "IP6_MULTICAST\n");
seq_printf(m, " gip %08x sip %08x vid %u rvid %u\n",
e->mc_gip, e->mc_sip, e->vid, e->rvid);
}
portmask = priv->r->read_mcast_pmask(e->mc_portmask_index);
seq_printf(m, " index %u ports", e->mc_portmask_index);
for (i = 0; i < 64; i++) {
if (portmask & BIT_ULL(i))
seq_printf(m, " %d", i);
}
seq_puts(m, "\n");
}
seq_puts(m, "\n");
}
static int l2_table_show(struct seq_file *m, void *v)
{
struct rtl838x_switch_priv *priv = m->private;
struct rtl838x_l2_entry e;
int i, bucket, index;
mutex_lock(&priv->reg_mutex);
for (i = 0; i < priv->fib_entries; i++) {
bucket = i >> 2;
index = i & 0x3;
priv->r->read_l2_entry_using_hash(bucket, index, &e);
if (!e.valid)
continue;
seq_printf(m, "Hash table bucket %d index %d ", bucket, index);
l2_table_print_entry(m, priv, &e);
}
for (i = 0; i < 64; i++) {
priv->r->read_cam(i, &e);
if (!e.valid)
continue;
seq_printf(m, "CAM index %d ", i);
l2_table_print_entry(m, priv, &e);
}
mutex_unlock(&priv->reg_mutex);
return 0;
}
static int l2_table_open(struct inode *inode, struct file *filp)
{
return single_open(filp, l2_table_show, inode->i_private);
}
static const struct file_operations l2_table_fops = {
.owner = THIS_MODULE,
.open = l2_table_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static ssize_t age_out_read(struct file *filp, char __user *buffer, size_t count,
loff_t *ppos)
{
struct rtl838x_port *p = filp->private_data;
struct dsa_switch *ds = p->dp->ds;
struct rtl838x_switch_priv *priv = ds->priv;
int value = sw_r32(priv->r->l2_port_aging_out);
if (value < 0)
return -EINVAL;
return rtl838x_common_read(buffer, count, ppos, (u32)value);
}
static ssize_t age_out_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct rtl838x_port *p = filp->private_data;
u32 value;
size_t res = rtl838x_common_write(buffer, count, ppos, &value);
if (res < 0)
return res;
rtl83xx_fast_age(p->dp->ds, p->dp->index);
return res;
}
static const struct file_operations age_out_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = age_out_read,
.write = age_out_write,
};
static ssize_t port_egress_rate_read(struct file *filp, char __user *buffer, size_t count,
loff_t *ppos)
{
struct rtl838x_port *p = filp->private_data;
struct dsa_switch *ds = p->dp->ds;
struct rtl838x_switch_priv *priv = ds->priv;
int value;
if (priv->family_id == RTL8380_FAMILY_ID)
value = rtl838x_get_egress_rate(priv, p->dp->index);
else
value = rtl839x_get_egress_rate(priv, p->dp->index);
if (value < 0)
return -EINVAL;
return rtl838x_common_read(buffer, count, ppos, (u32)value);
}
static ssize_t port_egress_rate_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct rtl838x_port *p = filp->private_data;
struct dsa_switch *ds = p->dp->ds;
struct rtl838x_switch_priv *priv = ds->priv;
u32 value;
size_t res = rtl838x_common_write(buffer, count, ppos, &value);
if (res < 0)
return res;
if (priv->family_id == RTL8380_FAMILY_ID)
rtl838x_set_egress_rate(priv, p->dp->index, value);
else
rtl839x_set_egress_rate(priv, p->dp->index, value);
return res;
}
static const struct file_operations port_egress_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = port_egress_rate_read,
.write = port_egress_rate_write,
};
static const struct debugfs_reg32 port_ctrl_regs[] = {
{ .name = "port_isolation", .offset = RTL838X_PORT_ISO_CTRL(0), },
{ .name = "mac_force_mode", .offset = RTL838X_MAC_FORCE_MODE_CTRL, },
};
void rtl838x_dbgfs_cleanup(struct rtl838x_switch_priv *priv)
{
debugfs_remove_recursive(priv->dbgfs_dir);
// kfree(priv->dbgfs_entries);
}
static int rtl838x_dbgfs_port_init(struct dentry *parent, struct rtl838x_switch_priv *priv,
int port)
{
struct dentry *port_dir;
struct debugfs_regset32 *port_ctrl_regset;
port_dir = debugfs_create_dir(priv->ports[port].dp->name, parent);
if (priv->family_id == RTL8380_FAMILY_ID) {
debugfs_create_x32("storm_rate_uc", 0644, port_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_UC(port)));
debugfs_create_x32("storm_rate_mc", 0644, port_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_MC(port)));
debugfs_create_x32("storm_rate_bc", 0644, port_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_BC(port)));
} else {
debugfs_create_x32("storm_rate_uc", 0644, port_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_UC_0(port)));
debugfs_create_x32("storm_rate_mc", 0644, port_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_MC_0(port)));
debugfs_create_x32("storm_rate_bc", 0644, port_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_BC_0(port)));
}
debugfs_create_u32("id", 0444, port_dir, (u32 *)&priv->ports[port].dp->index);
port_ctrl_regset = devm_kzalloc(priv->dev, sizeof(*port_ctrl_regset), GFP_KERNEL);
if (!port_ctrl_regset)
return -ENOMEM;
port_ctrl_regset->regs = port_ctrl_regs;
port_ctrl_regset->nregs = ARRAY_SIZE(port_ctrl_regs);
port_ctrl_regset->base = (void *)(RTL838X_SW_BASE + (port << 2));
debugfs_create_regset32("port_ctrl", 0400, port_dir, port_ctrl_regset);
debugfs_create_file("stp_state", 0600, port_dir, &priv->ports[port], &stp_state_fops);
debugfs_create_file("age_out", 0600, port_dir, &priv->ports[port], &age_out_fops);
debugfs_create_file("port_egress_rate", 0600, port_dir, &priv->ports[port],
&port_egress_fops);
return 0;
}
static int rtl838x_dbgfs_leds(struct dentry *parent, struct rtl838x_switch_priv *priv)
{
struct dentry *led_dir;
int p;
char led_sw_p_ctrl_name[20];
char port_led_name[20];
led_dir = debugfs_create_dir("led", parent);
if (priv->family_id == RTL8380_FAMILY_ID) {
debugfs_create_x32("led_glb_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8380_LED_GLB_CTRL));
debugfs_create_x32("led_mode_sel", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8380_LED_MODE_SEL));
debugfs_create_x32("led_mode_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8380_LED_MODE_CTRL));
debugfs_create_x32("led_p_en_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8380_LED_P_EN_CTRL));
debugfs_create_x32("led_sw_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8380_LED_SW_CTRL));
debugfs_create_x32("led0_sw_p_en_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8380_LED0_SW_P_EN_CTRL));
debugfs_create_x32("led1_sw_p_en_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8380_LED1_SW_P_EN_CTRL));
debugfs_create_x32("led2_sw_p_en_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8380_LED2_SW_P_EN_CTRL));
for (p = 0; p < 28; p++) {
snprintf(led_sw_p_ctrl_name, sizeof(led_sw_p_ctrl_name),
"led_sw_p_ctrl.%02d", p);
debugfs_create_x32(led_sw_p_ctrl_name, 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8380_LED_SW_P_CTRL(p)));
}
} else if (priv->family_id == RTL8390_FAMILY_ID) {
debugfs_create_x32("led_glb_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_GLB_CTRL));
debugfs_create_x32("led_set_2_3", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SET_2_3_CTRL));
debugfs_create_x32("led_set_0_1", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SET_0_1_CTRL));
for (p = 0; p < 4; p++) {
snprintf(port_led_name, sizeof(port_led_name), "led_copr_set_sel.%1d", p);
debugfs_create_x32(port_led_name, 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_SET_SEL_CTRL(p << 4)));
snprintf(port_led_name, sizeof(port_led_name), "led_fib_set_sel.%1d", p);
debugfs_create_x32(port_led_name, 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_SET_SEL_CTRL(p << 4)));
}
debugfs_create_x32("led_copr_pmask_ctrl_0", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_PMASK_CTRL(0)));
debugfs_create_x32("led_copr_pmask_ctrl_1", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_PMASK_CTRL(32)));
debugfs_create_x32("led_fib_pmask_ctrl_0", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_PMASK_CTRL(0)));
debugfs_create_x32("led_fib_pmask_ctrl_1", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_PMASK_CTRL(32)));
debugfs_create_x32("led_combo_ctrl_0", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COMBO_CTRL(0)));
debugfs_create_x32("led_combo_ctrl_1", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COMBO_CTRL(32)));
debugfs_create_x32("led_sw_ctrl", 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_CTRL));
for (p = 0; p < 5; p++) {
snprintf(port_led_name, sizeof(port_led_name), "led_sw_p_en_ctrl.%1d", p);
debugfs_create_x32(port_led_name, 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_P_EN_CTRL(p * 10)));
}
for (p = 0; p < 28; p++) {
snprintf(port_led_name, sizeof(port_led_name), "led_sw_p_ctrl.%02d", p);
debugfs_create_x32(port_led_name, 0644, led_dir,
(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_P_CTRL(p)));
}
}
return 0;
}
void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv)
{
struct dentry *rtl838x_dir;
struct dentry *port_dir;
struct dentry *mirror_dir;
struct debugfs_regset32 *port_ctrl_regset;
int ret, i;
char lag_name[10];
char mirror_name[10];
pr_info("%s called\n", __func__);
rtl838x_dir = debugfs_lookup(RTL838X_DRIVER_NAME, NULL);
if (!rtl838x_dir)
rtl838x_dir = debugfs_create_dir(RTL838X_DRIVER_NAME, NULL);
priv->dbgfs_dir = rtl838x_dir;
debugfs_create_u32("soc", 0444, rtl838x_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MODEL_NAME_INFO));
/* Create one directory per port */
for (i = 0; i < priv->cpu_port; i++) {
if (priv->ports[i].phy) {
ret = rtl838x_dbgfs_port_init(rtl838x_dir, priv, i);
if (ret)
goto err;
}
}
/* Create directory for CPU-port */
port_dir = debugfs_create_dir("cpu_port", rtl838x_dir);
port_ctrl_regset = devm_kzalloc(priv->dev, sizeof(*port_ctrl_regset), GFP_KERNEL);
if (!port_ctrl_regset) {
ret = -ENOMEM;
goto err;
}
port_ctrl_regset->regs = port_ctrl_regs;
port_ctrl_regset->nregs = ARRAY_SIZE(port_ctrl_regs);
port_ctrl_regset->base = (void *)(RTL838X_SW_BASE + (priv->cpu_port << 2));
debugfs_create_regset32("port_ctrl", 0400, port_dir, port_ctrl_regset);
debugfs_create_u8("id", 0444, port_dir, &priv->cpu_port);
/* Create entries for LAGs */
for (i = 0; i < priv->n_lags; i++) {
snprintf(lag_name, sizeof(lag_name), "lag.%02d", i);
if (priv->family_id == RTL8380_FAMILY_ID)
debugfs_create_x32(lag_name, 0644, rtl838x_dir,
(u32 *)(RTL838X_SW_BASE + priv->r->trk_mbr_ctr(i)));
else
debugfs_create_x64(lag_name, 0644, rtl838x_dir,
(u64 *)(RTL838X_SW_BASE + priv->r->trk_mbr_ctr(i)));
}
/* Create directories for mirror groups */
for (i = 0; i < 4; i++) {
snprintf(mirror_name, sizeof(mirror_name), "mirror.%1d", i);
mirror_dir = debugfs_create_dir(mirror_name, rtl838x_dir);
if (priv->family_id == RTL8380_FAMILY_ID) {
debugfs_create_x32("ctrl", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_CTRL + i * 4));
debugfs_create_x32("ingress_pm", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + priv->r->mir_spm + i * 4));
debugfs_create_x32("egress_pm", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + priv->r->mir_dpm + i * 4));
debugfs_create_x32("qid", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_QID_CTRL(i)));
debugfs_create_x32("rspan_vlan", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_VLAN_CTRL(i)));
debugfs_create_x32("rspan_vlan_mac", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_VLAN_CTRL_MAC(i)));
debugfs_create_x32("rspan_tx", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_CTRL));
debugfs_create_x32("rspan_tx_tag_rm", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_TAG_RM_CTRL));
debugfs_create_x32("rspan_tx_tag_en", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_TAG_EN_CTRL));
} else {
debugfs_create_x32("ctrl", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_CTRL + i * 4));
debugfs_create_x64("ingress_pm", 0644, mirror_dir,
(u64 *)(RTL838X_SW_BASE + priv->r->mir_spm + i * 8));
debugfs_create_x64("egress_pm", 0644, mirror_dir,
(u64 *)(RTL838X_SW_BASE + priv->r->mir_dpm + i * 8));
debugfs_create_x32("rspan_vlan", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_VLAN_CTRL(i)));
debugfs_create_x32("rspan_tx", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_CTRL));
debugfs_create_x32("rspan_tx_tag_rm", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_TAG_RM_CTRL));
debugfs_create_x32("rspan_tx_tag_en", 0644, mirror_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_TAG_EN_CTRL));
debugfs_create_x64("sample_rate", 0644, mirror_dir,
(u64 *)(RTL838X_SW_BASE + RTL839X_MIR_SAMPLE_RATE_CTRL));
}
}
if (priv->family_id == RTL8380_FAMILY_ID)
debugfs_create_x32("bpdu_flood_mask", 0644, rtl838x_dir,
(u32 *)(RTL838X_SW_BASE + priv->r->rma_bpdu_fld_pmask));
else
debugfs_create_x64("bpdu_flood_mask", 0644, rtl838x_dir,
(u64 *)(RTL838X_SW_BASE + priv->r->rma_bpdu_fld_pmask));
if (priv->family_id == RTL8380_FAMILY_ID)
debugfs_create_x32("vlan_ctrl", 0644, rtl838x_dir,
(u32 *)(RTL838X_SW_BASE + RTL838X_VLAN_CTRL));
else
debugfs_create_x32("vlan_ctrl", 0644, rtl838x_dir,
(u32 *)(RTL838X_SW_BASE + RTL839X_VLAN_CTRL));
ret = rtl838x_dbgfs_leds(rtl838x_dir, priv);
if (ret)
goto err;
debugfs_create_file("drop_counters", 0400, rtl838x_dir, priv, &drop_counter_fops);
debugfs_create_file("l2_table", 0400, rtl838x_dir, priv, &l2_table_fops);
return;
err:
rtl838x_dbgfs_cleanup(priv);
}
void rtl930x_dbgfs_init(struct rtl838x_switch_priv *priv)
{
struct dentry *dbg_dir;
pr_info("%s called\n", __func__);
dbg_dir = debugfs_lookup(RTL838X_DRIVER_NAME, NULL);
if (!dbg_dir)
dbg_dir = debugfs_create_dir(RTL838X_DRIVER_NAME, NULL);
priv->dbgfs_dir = dbg_dir;
debugfs_create_file("drop_counters", 0400, dbg_dir, priv, &drop_counter_fops);
debugfs_create_file("l2_table", 0400, dbg_dir, priv, &l2_table_fops);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,576 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <net/dsa.h>
#include <linux/delay.h>
#include <asm/mach-rtl838x/mach-rtl83xx.h>
#include "rtl83xx.h"
static struct rtl838x_switch_priv *switch_priv;
extern struct rtl83xx_soc_info soc_info;
enum scheduler_type {
WEIGHTED_FAIR_QUEUE = 0,
WEIGHTED_ROUND_ROBIN,
};
int max_available_queue[] = {0, 1, 2, 3, 4, 5, 6, 7};
int default_queue_weights[] = {1, 1, 1, 1, 1, 1, 1, 1};
int dot1p_priority_remapping[] = {0, 1, 2, 3, 4, 5, 6, 7};
static void rtl839x_read_scheduling_table(int port)
{
u32 cmd = 1 << 9 /* Execute cmd */
| 0 << 8 /* Read */
| 0 << 6 /* Table type 0b00 */
| (port & 0x3f);
rtl839x_exec_tbl2_cmd(cmd);
}
static void rtl839x_write_scheduling_table(int port)
{
u32 cmd = 1 << 9 /* Execute cmd */
| 1 << 8 /* Write */
| 0 << 6 /* Table type 0b00 */
| (port & 0x3f);
rtl839x_exec_tbl2_cmd(cmd);
}
static void rtl839x_read_out_q_table(int port)
{
u32 cmd = 1 << 9 /* Execute cmd */
| 0 << 8 /* Read */
| 2 << 6 /* Table type 0b10 */
| (port & 0x3f);
rtl839x_exec_tbl2_cmd(cmd);
}
static void rtl838x_storm_enable(struct rtl838x_switch_priv *priv, int port, bool enable)
{
// Enable Storm control for that port for UC, MC, and BC
if (enable)
sw_w32(0x7, RTL838X_STORM_CTRL_LB_CTRL(port));
else
sw_w32(0x0, RTL838X_STORM_CTRL_LB_CTRL(port));
}
u32 rtl838x_get_egress_rate(struct rtl838x_switch_priv *priv, int port)
{
u32 rate;
if (port > priv->cpu_port)
return 0;
rate = sw_r32(RTL838X_SCHED_P_EGR_RATE_CTRL(port)) & 0x3fff;
return rate;
}
/* Sets the rate limit, 10MBit/s is equal to a rate value of 625 */
int rtl838x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate)
{
u32 old_rate;
if (port > priv->cpu_port)
return -1;
old_rate = sw_r32(RTL838X_SCHED_P_EGR_RATE_CTRL(port));
sw_w32(rate, RTL838X_SCHED_P_EGR_RATE_CTRL(port));
return old_rate;
}
/* Set the rate limit for a particular queue in Bits/s
* units of the rate is 16Kbps
*/
void rtl838x_egress_rate_queue_limit(struct rtl838x_switch_priv *priv, int port,
int queue, u32 rate)
{
if (port > priv->cpu_port)
return;
if (queue > 7)
return;
sw_w32(rate, RTL838X_SCHED_Q_EGR_RATE_CTRL(port, queue));
}
static void rtl838x_rate_control_init(struct rtl838x_switch_priv *priv)
{
int i;
pr_info("Enabling Storm control\n");
// TICK_PERIOD_PPS
if (priv->id == 0x8380)
sw_w32_mask(0x3ff << 20, 434 << 20, RTL838X_SCHED_LB_TICK_TKN_CTRL_0);
// Set burst rate
sw_w32(0x00008000, RTL838X_STORM_CTRL_BURST_0); // UC
sw_w32(0x80008000, RTL838X_STORM_CTRL_BURST_1); // MC and BC
// Set burst Packets per Second to 32
sw_w32(0x00000020, RTL838X_STORM_CTRL_BURST_PPS_0); // UC
sw_w32(0x00200020, RTL838X_STORM_CTRL_BURST_PPS_1); // MC and BC
// Include IFG in storm control, rate based on bytes/s (0 = packets)
sw_w32_mask(0, 1 << 6 | 1 << 5, RTL838X_STORM_CTRL);
// Bandwidth control includes preamble and IFG (10 Bytes)
sw_w32_mask(0, 1, RTL838X_SCHED_CTRL);
// On SoCs except RTL8382M, set burst size of port egress
if (priv->id != 0x8382)
sw_w32_mask(0xffff, 0x800, RTL838X_SCHED_LB_THR);
/* Enable storm control on all ports with a PHY and limit rates,
* for UC and MC for both known and unknown addresses */
for (i = 0; i < priv->cpu_port; i++) {
if (priv->ports[i].phy) {
sw_w32((1 << 18) | 0x8000, RTL838X_STORM_CTRL_PORT_UC(i));
sw_w32((1 << 18) | 0x8000, RTL838X_STORM_CTRL_PORT_MC(i));
sw_w32(0x8000, RTL838X_STORM_CTRL_PORT_BC(i));
rtl838x_storm_enable(priv, i, true);
}
}
// Attack prevention, enable all attack prevention measures
//sw_w32(0x1ffff, RTL838X_ATK_PRVNT_CTRL);
/* Attack prevention, drop (bit = 0) problematic packets on all ports.
* Setting bit = 1 means: trap to CPU
*/
//sw_w32(0, RTL838X_ATK_PRVNT_ACT);
// Enable attack prevention on all ports
//sw_w32(0x0fffffff, RTL838X_ATK_PRVNT_PORT_EN);
}
/* Sets the rate limit, 10MBit/s is equal to a rate value of 625 */
u32 rtl839x_get_egress_rate(struct rtl838x_switch_priv *priv, int port)
{
u32 rate;
pr_debug("%s: Getting egress rate on port %d to %d\n", __func__, port, rate);
if (port >= priv->cpu_port)
return 0;
mutex_lock(&priv->reg_mutex);
rtl839x_read_scheduling_table(port);
rate = sw_r32(RTL839X_TBL_ACCESS_DATA_2(7));
rate <<= 12;
rate |= sw_r32(RTL839X_TBL_ACCESS_DATA_2(8)) >> 20;
mutex_unlock(&priv->reg_mutex);
return rate;
}
/* Sets the rate limit, 10MBit/s is equal to a rate value of 625, returns previous rate */
int rtl839x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate)
{
u32 old_rate;
pr_debug("%s: Setting egress rate on port %d to %d\n", __func__, port, rate);
if (port >= priv->cpu_port)
return -1;
mutex_lock(&priv->reg_mutex);
rtl839x_read_scheduling_table(port);
old_rate = sw_r32(RTL839X_TBL_ACCESS_DATA_2(7)) & 0xff;
old_rate <<= 12;
old_rate |= sw_r32(RTL839X_TBL_ACCESS_DATA_2(8)) >> 20;
sw_w32_mask(0xff, (rate >> 12) & 0xff, RTL839X_TBL_ACCESS_DATA_2(7));
sw_w32_mask(0xfff << 20, rate << 20, RTL839X_TBL_ACCESS_DATA_2(8));
rtl839x_write_scheduling_table(port);
mutex_unlock(&priv->reg_mutex);
return old_rate;
}
/* Set the rate limit for a particular queue in Bits/s
* units of the rate is 16Kbps
*/
void rtl839x_egress_rate_queue_limit(struct rtl838x_switch_priv *priv, int port,
int queue, u32 rate)
{
int lsb = 128 + queue * 20;
int low_byte = 8 - (lsb >> 5);
int start_bit = lsb - (low_byte << 5);
u32 high_mask = 0xfffff >> (32 - start_bit);
pr_debug("%s: Setting egress rate on port %d, queue %d to %d\n",
__func__, port, queue, rate);
if (port >= priv->cpu_port)
return;
if (queue > 7)
return;
mutex_lock(&priv->reg_mutex);
rtl839x_read_scheduling_table(port);
sw_w32_mask(0xfffff << start_bit, (rate & 0xfffff) << start_bit,
RTL839X_TBL_ACCESS_DATA_2(low_byte));
if (high_mask)
sw_w32_mask(high_mask, (rate & 0xfffff) >> (32- start_bit),
RTL839X_TBL_ACCESS_DATA_2(low_byte - 1));
rtl839x_write_scheduling_table(port);
mutex_unlock(&priv->reg_mutex);
}
static void rtl839x_rate_control_init(struct rtl838x_switch_priv *priv)
{
int p, q;
pr_info("%s: enabling rate control\n", __func__);
/* Tick length and token size settings for SoC with 250MHz,
* RTL8350 family would use 50MHz
*/
// Set the special tick period
sw_w32(976563, RTL839X_STORM_CTRL_SPCL_LB_TICK_TKN_CTRL);
// Ingress tick period and token length 10G
sw_w32(18 << 11 | 151, RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_0);
// Ingress tick period and token length 1G
sw_w32(245 << 11 | 129, RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_1);
// Egress tick period 10G, bytes/token 10G and tick period 1G, bytes/token 1G
sw_w32(18 << 24 | 151 << 16 | 185 << 8 | 97, RTL839X_SCHED_LB_TICK_TKN_CTRL);
// Set the tick period of the CPU and the Token Len
sw_w32(3815 << 8 | 1, RTL839X_SCHED_LB_TICK_TKN_PPS_CTRL);
// Set the Weighted Fair Queueing burst size
sw_w32_mask(0xffff, 4500, RTL839X_SCHED_LB_THR);
// Storm-rate calculation is based on bytes/sec (bit 5), include IFG (bit 6)
sw_w32_mask(0, 1 << 5 | 1 << 6, RTL839X_STORM_CTRL);
/* Based on the rate control mode being bytes/s
* set tick period and token length for 10G
*/
sw_w32(18 << 10 | 151, RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_0);
/* and for 1G ports */
sw_w32(246 << 10 | 129, RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_1);
/* Set default burst rates on all ports (the same for 1G / 10G) with a PHY
* for UC, MC and BC
* For 1G port, the minimum burst rate is 1700, maximum 65535,
* For 10G ports it is 2650 and 1048575 respectively */
for (p = 0; p < priv->cpu_port; p++) {
if (priv->ports[p].phy && !priv->ports[p].is10G) {
sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_UC_1(p));
sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_MC_1(p));
sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_BC_1(p));
}
}
/* Setup ingress/egress per-port rate control */
for (p = 0; p < priv->cpu_port; p++) {
if (!priv->ports[p].phy)
continue;
if (priv->ports[p].is10G)
rtl839x_set_egress_rate(priv, p, 625000); // 10GB/s
else
rtl839x_set_egress_rate(priv, p, 62500); // 1GB/s
// Setup queues: all RTL83XX SoCs have 8 queues, maximum rate
for (q = 0; q < 8; q++)
rtl839x_egress_rate_queue_limit(priv, p, q, 0xfffff);
if (priv->ports[p].is10G) {
// Set high threshold to maximum
sw_w32_mask(0xffff, 0xffff, RTL839X_IGR_BWCTRL_PORT_CTRL_10G_0(p));
} else {
// Set high threshold to maximum
sw_w32_mask(0xffff, 0xffff, RTL839X_IGR_BWCTRL_PORT_CTRL_1(p));
}
}
// Set global ingress low watermark rate
sw_w32(65532, RTL839X_IGR_BWCTRL_CTRL_LB_THR);
}
void rtl838x_setup_prio2queue_matrix(int *min_queues)
{
int i;
u32 v;
pr_info("Current Intprio2queue setting: %08x\n", sw_r32(RTL838X_QM_INTPRI2QID_CTRL));
for (i = 0; i < MAX_PRIOS; i++)
v |= i << (min_queues[i] * 3);
sw_w32(v, RTL838X_QM_INTPRI2QID_CTRL);
}
void rtl839x_setup_prio2queue_matrix(int *min_queues)
{
int i, q;
pr_info("Current Intprio2queue setting: %08x\n", sw_r32(RTL839X_QM_INTPRI2QID_CTRL(0)));
for (i = 0; i < MAX_PRIOS; i++) {
q = min_queues[i];
sw_w32(i << (q * 3), RTL839X_QM_INTPRI2QID_CTRL(q));
}
}
/* Sets the CPU queue depending on the internal priority of a packet */
void rtl83xx_setup_prio2queue_cpu_matrix(int *max_queues)
{
int reg = soc_info.family == RTL8380_FAMILY_ID ? RTL838X_QM_PKT2CPU_INTPRI_MAP
: RTL839X_QM_PKT2CPU_INTPRI_MAP;
int i;
u32 v;
pr_info("QM_PKT2CPU_INTPRI_MAP: %08x\n", sw_r32(reg));
for (i = 0; i < MAX_PRIOS; i++)
v |= max_queues[i] << (i * 3);
sw_w32(v, reg);
}
void rtl83xx_setup_default_prio2queue(void)
{
if (soc_info.family == RTL8380_FAMILY_ID) {
rtl838x_setup_prio2queue_matrix(max_available_queue);
} else {
rtl839x_setup_prio2queue_matrix(max_available_queue);
}
rtl83xx_setup_prio2queue_cpu_matrix(max_available_queue);
}
/* Sets the output queue assigned to a port, the port can be the CPU-port */
void rtl839x_set_egress_queue(int port, int queue)
{
sw_w32(queue << ((port % 10) *3), RTL839X_QM_PORT_QNUM(port));
}
/* Sets the priority assigned of an ingress port, the port can be the CPU-port */
void rtl83xx_set_ingress_priority(int port, int priority)
{
if (soc_info.family == RTL8380_FAMILY_ID)
sw_w32(priority << ((port % 10) *3), RTL838X_PRI_SEL_PORT_PRI(port));
else
sw_w32(priority << ((port % 10) *3), RTL839X_PRI_SEL_PORT_PRI(port));
}
int rtl839x_get_scheduling_algorithm(struct rtl838x_switch_priv *priv, int port)
{
u32 v;
mutex_lock(&priv->reg_mutex);
rtl839x_read_scheduling_table(port);
v = sw_r32(RTL839X_TBL_ACCESS_DATA_2(8));
mutex_unlock(&priv->reg_mutex);
if (v & BIT(19))
return WEIGHTED_ROUND_ROBIN;
return WEIGHTED_FAIR_QUEUE;
}
void rtl839x_set_scheduling_algorithm(struct rtl838x_switch_priv *priv, int port,
enum scheduler_type sched)
{
enum scheduler_type t = rtl839x_get_scheduling_algorithm(priv, port);
u32 v, oam_state, oam_port_state;
u32 count;
int i, egress_rate;
mutex_lock(&priv->reg_mutex);
/* Check whether we need to empty the egress queue of that port due to Errata E0014503 */
if (sched == WEIGHTED_FAIR_QUEUE && t == WEIGHTED_ROUND_ROBIN && port != priv->cpu_port) {
// Read Operations, Adminstatrion and Management control register
oam_state = sw_r32(RTL839X_OAM_CTRL);
// Get current OAM state
oam_port_state = sw_r32(RTL839X_OAM_PORT_ACT_CTRL(port));
// Disable OAM to block traffice
v = sw_r32(RTL839X_OAM_CTRL);
sw_w32_mask(0, 1, RTL839X_OAM_CTRL);
v = sw_r32(RTL839X_OAM_CTRL);
// Set to trap action OAM forward (bits 1, 2) and OAM Mux Action Drop (bit 0)
sw_w32(0x2, RTL839X_OAM_PORT_ACT_CTRL(port));
// Set port egress rate to unlimited
egress_rate = rtl839x_set_egress_rate(priv, port, 0xFFFFF);
// Wait until the egress used page count of that port is 0
i = 0;
do {
usleep_range(100, 200);
rtl839x_read_out_q_table(port);
count = sw_r32(RTL839X_TBL_ACCESS_DATA_2(6));
count >>= 20;
i++;
} while (i < 3500 && count > 0);
}
// Actually set the scheduling algorithm
rtl839x_read_scheduling_table(port);
sw_w32_mask(BIT(19), sched ? BIT(19) : 0, RTL839X_TBL_ACCESS_DATA_2(8));
rtl839x_write_scheduling_table(port);
if (sched == WEIGHTED_FAIR_QUEUE && t == WEIGHTED_ROUND_ROBIN && port != priv->cpu_port) {
// Restore OAM state to control register
sw_w32(oam_state, RTL839X_OAM_CTRL);
// Restore trap action state
sw_w32(oam_port_state, RTL839X_OAM_PORT_ACT_CTRL(port));
// Restore port egress rate
rtl839x_set_egress_rate(priv, port, egress_rate);
}
mutex_unlock(&priv->reg_mutex);
}
void rtl839x_set_scheduling_queue_weights(struct rtl838x_switch_priv *priv, int port,
int *queue_weights)
{
int i, lsb, low_byte, start_bit, high_mask;
mutex_lock(&priv->reg_mutex);
rtl839x_read_scheduling_table(port);
for (i = 0; i < 8; i++) {
lsb = 48 + i * 8;
low_byte = 8 - (lsb >> 5);
start_bit = lsb - (low_byte << 5);
high_mask = 0x3ff >> (32 - start_bit);
sw_w32_mask(0x3ff << start_bit, (queue_weights[i] & 0x3ff) << start_bit,
RTL839X_TBL_ACCESS_DATA_2(low_byte));
if (high_mask)
sw_w32_mask(high_mask, (queue_weights[i] & 0x3ff) >> (32- start_bit),
RTL839X_TBL_ACCESS_DATA_2(low_byte - 1));
}
rtl839x_write_scheduling_table(port);
mutex_unlock(&priv->reg_mutex);
}
void rtl838x_config_qos(void)
{
int i, p;
u32 v;
pr_info("Setting up RTL838X QoS\n");
pr_info("RTL838X_PRI_SEL_TBL_CTRL(i): %08x\n", sw_r32(RTL838X_PRI_SEL_TBL_CTRL(0)));
rtl83xx_setup_default_prio2queue();
// Enable inner (bit 12) and outer (bit 13) priority remapping from DSCP
sw_w32_mask(0, BIT(12) | BIT(13), RTL838X_PRI_DSCP_INVLD_CTRL0);
/* Set default weight for calculating internal priority, in prio selection group 0
* Port based (prio 3), Port outer-tag (4), DSCP (5), Inner Tag (6), Outer Tag (7)
*/
v = 3 | (4 << 3) | (5 << 6) | (6 << 9) | (7 << 12);
sw_w32(v, RTL838X_PRI_SEL_TBL_CTRL(0));
// Set the inner and outer priority one-to-one to re-marked outer dot1p priority
v = 0;
for (p = 0; p < 8; p++)
v |= p << (3 * p);
sw_w32(v, RTL838X_RMK_OPRI_CTRL);
sw_w32(v, RTL838X_RMK_IPRI_CTRL);
v = 0;
for (p = 0; p < 8; p++)
v |= (dot1p_priority_remapping[p] & 0x7) << (p * 3);
sw_w32(v, RTL838X_PRI_SEL_IPRI_REMAP);
// On all ports set scheduler type to WFQ
for (i = 0; i <= soc_info.cpu_port; i++)
sw_w32(0, RTL838X_SCHED_P_TYPE_CTRL(i));
// Enable egress scheduler for CPU-Port
sw_w32_mask(0, BIT(8), RTL838X_SCHED_LB_CTRL(soc_info.cpu_port));
// Enable egress drop allways on
sw_w32_mask(0, BIT(11), RTL838X_FC_P_EGR_DROP_CTRL(soc_info.cpu_port));
// Give special trap frames priority 7 (BPDUs) and routing exceptions:
sw_w32_mask(0, 7 << 3 | 7, RTL838X_QM_PKT2CPU_INTPRI_2);
// Give RMA frames priority 7:
sw_w32_mask(0, 7, RTL838X_QM_PKT2CPU_INTPRI_1);
}
void rtl839x_config_qos(void)
{
int port, p, q;
u32 v;
struct rtl838x_switch_priv *priv = switch_priv;
pr_info("Setting up RTL839X QoS\n");
pr_info("RTL839X_PRI_SEL_TBL_CTRL(i): %08x\n", sw_r32(RTL839X_PRI_SEL_TBL_CTRL(0)));
rtl83xx_setup_default_prio2queue();
for (port = 0; port < soc_info.cpu_port; port++)
sw_w32(7, RTL839X_QM_PORT_QNUM(port));
// CPU-port gets queue number 7
sw_w32(7, RTL839X_QM_PORT_QNUM(soc_info.cpu_port));
for (port = 0; port <= soc_info.cpu_port; port++) {
rtl83xx_set_ingress_priority(port, 0);
rtl839x_set_scheduling_algorithm(priv, port, WEIGHTED_FAIR_QUEUE);
rtl839x_set_scheduling_queue_weights(priv, port, default_queue_weights);
// Do re-marking based on outer tag
sw_w32_mask(0, BIT(port % 32), RTL839X_RMK_PORT_DEI_TAG_CTRL(port));
}
// Remap dot1p priorities to internal priority, for this the outer tag needs be re-marked
v = 0;
for (p = 0; p < 8; p++)
v |= (dot1p_priority_remapping[p] & 0x7) << (p * 3);
sw_w32(v, RTL839X_PRI_SEL_IPRI_REMAP);
/* Configure Drop Precedence for Drop Eligible Indicator (DEI)
* Index 0: 0
* Index 1: 2
* Each indicator is 2 bits long
*/
sw_w32(2 << 2, RTL839X_PRI_SEL_DEI2DP_REMAP);
// Re-mark DEI: 4 bit-fields of 2 bits each, field 0 is bits 0-1, ...
sw_w32((0x1 << 2) | (0x1 << 4), RTL839X_RMK_DEI_CTRL);
/* Set Congestion avoidance drop probability to 0 for drop precedences 0-2 (bits 24-31)
* low threshold (bits 0-11) to 4095 and high threshold (bits 12-23) to 4095
* Weighted Random Early Detection (WRED) is used
*/
sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(0));
sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(1));
sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(2));
/* Set queue-based congestion avoidance properties, register fields are as
* for forward RTL839X_WRED_PORT_THR_CTRL
*/
for (q = 0; q < 8; q++) {
sw_w32(255 << 24 | 78 << 12 | 68, RTL839X_WRED_QUEUE_THR_CTRL(q, 0));
sw_w32(255 << 24 | 74 << 12 | 64, RTL839X_WRED_QUEUE_THR_CTRL(q, 0));
sw_w32(255 << 24 | 70 << 12 | 60, RTL839X_WRED_QUEUE_THR_CTRL(q, 0));
}
}
void __init rtl83xx_setup_qos(struct rtl838x_switch_priv *priv)
{
switch_priv = priv;
pr_info("In %s\n", __func__);
if (priv->family_id == RTL8380_FAMILY_ID)
return rtl838x_config_qos();
else if (priv->family_id == RTL8390_FAMILY_ID)
return rtl839x_config_qos();
if (priv->family_id == RTL8380_FAMILY_ID)
rtl838x_rate_control_init(priv);
else if (priv->family_id == RTL8390_FAMILY_ID)
rtl839x_rate_control_init(priv);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,137 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _NET_DSA_RTL83XX_H
#define _NET_DSA_RTL83XX_H
#include <net/dsa.h>
#include "rtl838x.h"
#define RTL8380_VERSION_A 'A'
#define RTL8390_VERSION_A 'A'
#define RTL8380_VERSION_B 'B'
struct fdb_update_work {
struct work_struct work;
struct net_device *ndev;
u64 macs[];
};
#define MIB_DESC(_size, _offset, _name) {.size = _size, .offset = _offset, .name = _name}
struct rtl83xx_mib_desc {
unsigned int size;
unsigned int offset;
const char *name;
};
/* API for switch table access */
struct table_reg {
u16 addr;
u16 data;
u8 max_data;
u8 c_bit;
u8 t_bit;
u8 rmode;
u8 tbl;
struct mutex lock;
};
#define TBL_DESC(_addr, _data, _max_data, _c_bit, _t_bit, _rmode) \
{ .addr = _addr, .data = _data, .max_data = _max_data, .c_bit = _c_bit, \
.t_bit = _t_bit, .rmode = _rmode \
}
typedef enum {
RTL8380_TBL_L2 = 0,
RTL8380_TBL_0,
RTL8380_TBL_1,
RTL8390_TBL_L2,
RTL8390_TBL_0,
RTL8390_TBL_1,
RTL8390_TBL_2,
RTL9300_TBL_L2,
RTL9300_TBL_0,
RTL9300_TBL_1,
RTL9300_TBL_2,
RTL9300_TBL_HSB,
RTL9300_TBL_HSA,
RTL9310_TBL_0,
RTL9310_TBL_1,
RTL9310_TBL_2,
RTL9310_TBL_3,
RTL9310_TBL_4,
RTL9310_TBL_5,
RTL_TBL_END
} rtl838x_tbl_reg_t;
void rtl_table_init(void);
struct table_reg *rtl_table_get(rtl838x_tbl_reg_t r, int t);
void rtl_table_release(struct table_reg *r);
void rtl_table_read(struct table_reg *r, int idx);
void rtl_table_write(struct table_reg *r, int idx);
inline u16 rtl_table_data(struct table_reg *r, int i);
inline u32 rtl_table_data_r(struct table_reg *r, int i);
inline void rtl_table_data_w(struct table_reg *r, u32 v, int i);
void __init rtl83xx_setup_qos(struct rtl838x_switch_priv *priv);
int rtl83xx_packet_cntr_alloc(struct rtl838x_switch_priv *priv);
int rtl83xx_port_is_under(const struct net_device * dev, struct rtl838x_switch_priv *priv);
int read_phy(u32 port, u32 page, u32 reg, u32 *val);
int write_phy(u32 port, u32 page, u32 reg, u32 val);
/* Port register accessor functions for the RTL839x and RTL931X SoCs */
void rtl839x_mask_port_reg_be(u64 clear, u64 set, int reg);
u64 rtl839x_get_port_reg_be(int reg);
void rtl839x_set_port_reg_be(u64 set, int reg);
void rtl839x_mask_port_reg_le(u64 clear, u64 set, int reg);
void rtl839x_set_port_reg_le(u64 set, int reg);
u64 rtl839x_get_port_reg_le(int reg);
/* Port register accessor functions for the RTL838x and RTL930X SoCs */
void rtl838x_mask_port_reg(u64 clear, u64 set, int reg);
void rtl838x_set_port_reg(u64 set, int reg);
u64 rtl838x_get_port_reg(int reg);
/* RTL838x-specific */
u32 rtl838x_hash(struct rtl838x_switch_priv *priv, u64 seed);
irqreturn_t rtl838x_switch_irq(int irq, void *dev_id);
void rtl8380_get_version(struct rtl838x_switch_priv *priv);
void rtl838x_vlan_profile_dump(int index);
int rtl83xx_dsa_phy_read(struct dsa_switch *ds, int phy_addr, int phy_reg);
void rtl8380_sds_rst(int mac);
int rtl8380_sds_power(int mac, int val);
void rtl838x_print_matrix(void);
/* RTL839x-specific */
u32 rtl839x_hash(struct rtl838x_switch_priv *priv, u64 seed);
irqreturn_t rtl839x_switch_irq(int irq, void *dev_id);
void rtl8390_get_version(struct rtl838x_switch_priv *priv);
void rtl839x_vlan_profile_dump(int index);
int rtl83xx_dsa_phy_write(struct dsa_switch *ds, int phy_addr, int phy_reg, u16 val);
void rtl839x_exec_tbl2_cmd(u32 cmd);
void rtl839x_print_matrix(void);
/* RTL930x-specific */
u32 rtl930x_hash(struct rtl838x_switch_priv *priv, u64 seed);
irqreturn_t rtl930x_switch_irq(int irq, void *dev_id);
irqreturn_t rtl839x_switch_irq(int irq, void *dev_id);
void rtl930x_vlan_profile_dump(int index);
int rtl9300_sds_power(int mac, int val);
void rtl9300_sds_rst(int sds_num, u32 mode);
int rtl9300_serdes_setup(int sds_num, phy_interface_t phy_mode);
void rtl930x_print_matrix(void);
/* RTL931x-specific */
irqreturn_t rtl931x_switch_irq(int irq, void *dev_id);
int rtl931x_sds_cmu_band_get(int sds, phy_interface_t mode);
int rtl931x_sds_cmu_band_set(int sds, bool enable, u32 band, phy_interface_t mode);
void rtl931x_sds_init(u32 sds, phy_interface_t mode);
int rtl83xx_lag_add(struct dsa_switch *ds, int group, int port, struct netdev_lag_upper_info *info);
int rtl83xx_lag_del(struct dsa_switch *ds, int group, int port);
#endif /* _NET_DSA_RTL83XX_H */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,409 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <net/dsa.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <net/flow_offload.h>
#include <linux/rhashtable.h>
#include <asm/mach-rtl838x/mach-rtl83xx.h>
#include "rtl83xx.h"
#include "rtl838x.h"
/*
* Parse the flow rule for the matching conditions
*/
static int rtl83xx_parse_flow_rule(struct rtl838x_switch_priv *priv,
struct flow_rule *rule, struct rtl83xx_flow *flow)
{
struct flow_dissector *dissector = rule->match.dissector;
pr_debug("In %s\n", __func__);
/* KEY_CONTROL and KEY_BASIC are needed for forming a meaningful key */
if ((dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL)) == 0 ||
(dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_BASIC)) == 0) {
pr_err("Cannot form TC key: used_keys = 0x%x\n", dissector->used_keys);
return -EOPNOTSUPP;
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
struct flow_match_basic match;
pr_debug("%s: BASIC\n", __func__);
flow_rule_match_basic(rule, &match);
if (match.key->n_proto == htons(ETH_P_ARP))
flow->rule.frame_type = 0;
if (match.key->n_proto == htons(ETH_P_IP))
flow->rule.frame_type = 2;
if (match.key->n_proto == htons(ETH_P_IPV6))
flow->rule.frame_type = 3;
if ((match.key->n_proto == htons(ETH_P_ARP)) || flow->rule.frame_type)
flow->rule.frame_type_m = 3;
if (flow->rule.frame_type >= 2) {
if (match.key->ip_proto == IPPROTO_UDP)
flow->rule.frame_type_l4 = 0;
if (match.key->ip_proto == IPPROTO_TCP)
flow->rule.frame_type_l4 = 1;
if (match.key->ip_proto == IPPROTO_ICMP
|| match.key->ip_proto ==IPPROTO_ICMPV6)
flow->rule.frame_type_l4 = 2;
if (match.key->ip_proto == IPPROTO_TCP)
flow->rule.frame_type_l4 = 3;
if ((match.key->ip_proto == IPPROTO_UDP) || flow->rule.frame_type_l4)
flow->rule.frame_type_l4_m = 7;
}
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
struct flow_match_eth_addrs match;
pr_debug("%s: ETH_ADDR\n", __func__);
flow_rule_match_eth_addrs(rule, &match);
ether_addr_copy(flow->rule.dmac, match.key->dst);
ether_addr_copy(flow->rule.dmac_m, match.mask->dst);
ether_addr_copy(flow->rule.smac, match.key->src);
ether_addr_copy(flow->rule.smac_m, match.mask->src);
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
struct flow_match_vlan match;
pr_debug("%s: VLAN\n", __func__);
flow_rule_match_vlan(rule, &match);
flow->rule.itag = match.key->vlan_id;
flow->rule.itag_m = match.mask->vlan_id;
// TODO: What about match.key->vlan_priority ?
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
struct flow_match_ipv4_addrs match;
pr_debug("%s: IPV4\n", __func__);
flow_rule_match_ipv4_addrs(rule, &match);
flow->rule.is_ipv6 = false;
flow->rule.dip = match.key->dst;
flow->rule.dip_m = match.mask->dst;
flow->rule.sip = match.key->src;
flow->rule.sip_m = match.mask->src;
} else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
struct flow_match_ipv6_addrs match;
pr_debug("%s: IPV6\n", __func__);
flow->rule.is_ipv6 = true;
flow_rule_match_ipv6_addrs(rule, &match);
flow->rule.dip6 = match.key->dst;
flow->rule.dip6_m = match.mask->dst;
flow->rule.sip6 = match.key->src;
flow->rule.sip6_m = match.mask->src;
}
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
struct flow_match_ports match;
pr_debug("%s: PORTS\n", __func__);
flow_rule_match_ports(rule, &match);
flow->rule.dport = match.key->dst;
flow->rule.dport_m = match.mask->dst;
flow->rule.sport = match.key->src;
flow->rule.sport_m = match.mask->src;
}
// TODO: ICMP
return 0;
}
static void rtl83xx_flow_bypass_all(struct rtl83xx_flow *flow)
{
flow->rule.bypass_sel = true;
flow->rule.bypass_all = true;
flow->rule.bypass_igr_stp = true;
flow->rule.bypass_ibc_sc = true;
}
static int rtl83xx_parse_fwd(struct rtl838x_switch_priv *priv,
const struct flow_action_entry *act, struct rtl83xx_flow *flow)
{
struct net_device *dev = act->dev;
int port;
port = rtl83xx_port_is_under(dev, priv);
if (port < 0) {
netdev_info(dev, "%s: not a DSA device.\n", __func__);
return -EINVAL;
}
flow->rule.fwd_sel = true;
flow->rule.fwd_data = port;
pr_debug("Using port index: %d\n", port);
rtl83xx_flow_bypass_all(flow);
return 0;
}
static int rtl83xx_add_flow(struct rtl838x_switch_priv *priv, struct flow_cls_offload *f,
struct rtl83xx_flow *flow)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(f);
const struct flow_action_entry *act;
int i, err;
pr_debug("%s\n", __func__);
rtl83xx_parse_flow_rule(priv, rule, flow);
flow_action_for_each(i, act, &rule->action) {
switch (act->id) {
case FLOW_ACTION_DROP:
pr_debug("%s: DROP\n", __func__);
flow->rule.drop = true;
rtl83xx_flow_bypass_all(flow);
return 0;
case FLOW_ACTION_TRAP:
pr_debug("%s: TRAP\n", __func__);
flow->rule.fwd_data = priv->cpu_port;
flow->rule.fwd_act = PIE_ACT_REDIRECT_TO_PORT;
rtl83xx_flow_bypass_all(flow);
break;
case FLOW_ACTION_MANGLE:
pr_err("%s: FLOW_ACTION_MANGLE not supported\n", __func__);
return -EOPNOTSUPP;
case FLOW_ACTION_ADD:
pr_err("%s: FLOW_ACTION_ADD not supported\n", __func__);
return -EOPNOTSUPP;
case FLOW_ACTION_VLAN_PUSH:
pr_debug("%s: VLAN_PUSH\n", __func__);
// TODO: act->vlan.proto
flow->rule.ivid_act = PIE_ACT_VID_ASSIGN;
flow->rule.ivid_sel = true;
flow->rule.ivid_data = htons(act->vlan.vid);
flow->rule.ovid_act = PIE_ACT_VID_ASSIGN;
flow->rule.ovid_sel = true;
flow->rule.ovid_data = htons(act->vlan.vid);
flow->rule.fwd_mod_to_cpu = true;
break;
case FLOW_ACTION_VLAN_POP:
pr_debug("%s: VLAN_POP\n", __func__);
flow->rule.ivid_act = PIE_ACT_VID_ASSIGN;
flow->rule.ivid_data = 0;
flow->rule.ivid_sel = true;
flow->rule.ovid_act = PIE_ACT_VID_ASSIGN;
flow->rule.ovid_data = 0;
flow->rule.ovid_sel = true;
flow->rule.fwd_mod_to_cpu = true;
break;
case FLOW_ACTION_CSUM:
pr_err("%s: FLOW_ACTION_CSUM not supported\n", __func__);
return -EOPNOTSUPP;
case FLOW_ACTION_REDIRECT:
pr_debug("%s: REDIRECT\n", __func__);
err = rtl83xx_parse_fwd(priv, act, flow);
if (err)
return err;
flow->rule.fwd_act = PIE_ACT_REDIRECT_TO_PORT;
break;
case FLOW_ACTION_MIRRED:
pr_debug("%s: MIRRED\n", __func__);
err = rtl83xx_parse_fwd(priv, act, flow);
if (err)
return err;
flow->rule.fwd_act = PIE_ACT_COPY_TO_PORT;
break;
default:
pr_err("%s: Flow action not supported: %d\n", __func__, act->id);
return -EOPNOTSUPP;
}
}
return 0;
}
static const struct rhashtable_params tc_ht_params = {
.head_offset = offsetof(struct rtl83xx_flow, node),
.key_offset = offsetof(struct rtl83xx_flow, cookie),
.key_len = sizeof(((struct rtl83xx_flow *)0)->cookie),
.automatic_shrinking = true,
};
static int rtl83xx_configure_flower(struct rtl838x_switch_priv *priv,
struct flow_cls_offload *f)
{
struct rtl83xx_flow *flow;
int err = 0;
pr_debug("In %s\n", __func__);
rcu_read_lock();
pr_debug("Cookie %08lx\n", f->cookie);
flow = rhashtable_lookup(&priv->tc_ht, &f->cookie, tc_ht_params);
if (flow) {
pr_info("%s: Got flow\n", __func__);
err = -EEXIST;
goto rcu_unlock;
}
rcu_unlock:
rcu_read_unlock();
if (flow)
goto out;
pr_debug("%s: New flow\n", __func__);
flow = kzalloc(sizeof(*flow), GFP_KERNEL);
if (!flow) {
err = -ENOMEM;
goto out;
}
flow->cookie = f->cookie;
flow->priv = priv;
err = rhashtable_insert_fast(&priv->tc_ht, &flow->node, tc_ht_params);
if (err) {
pr_err("Could not insert add new rule\n");
goto out_free;
}
rtl83xx_add_flow(priv, f, flow); // TODO: check error
// Add log action to flow
flow->rule.packet_cntr = rtl83xx_packet_cntr_alloc(priv);
if (flow->rule.packet_cntr >= 0) {
pr_debug("Using packet counter %d\n", flow->rule.packet_cntr);
flow->rule.log_sel = true;
flow->rule.log_data = flow->rule.packet_cntr;
}
err = priv->r->pie_rule_add(priv, &flow->rule);
return err;
out_free:
kfree(flow);
out:
pr_err("%s: error %d\n", __func__, err);
return err;
}
static int rtl83xx_delete_flower(struct rtl838x_switch_priv *priv,
struct flow_cls_offload * cls_flower)
{
struct rtl83xx_flow *flow;
pr_debug("In %s\n", __func__);
rcu_read_lock();
flow = rhashtable_lookup_fast(&priv->tc_ht, &cls_flower->cookie, tc_ht_params);
if (!flow) {
rcu_read_unlock();
return -EINVAL;
}
priv->r->pie_rule_rm(priv, &flow->rule);
rhashtable_remove_fast(&priv->tc_ht, &flow->node, tc_ht_params);
kfree_rcu(flow, rcu_head);
rcu_read_unlock();
return 0;
}
static int rtl83xx_stats_flower(struct rtl838x_switch_priv *priv,
struct flow_cls_offload * cls_flower)
{
struct rtl83xx_flow *flow;
unsigned long lastused = 0;
int total_packets, new_packets;
pr_debug("%s: \n", __func__);
flow = rhashtable_lookup_fast(&priv->tc_ht, &cls_flower->cookie, tc_ht_params);
if (!flow)
return -1;
if (flow->rule.packet_cntr >= 0) {
total_packets = priv->r->packet_cntr_read(flow->rule.packet_cntr);
pr_debug("Total packets: %d\n", total_packets);
new_packets = total_packets - flow->rule.last_packet_cnt;
flow->rule.last_packet_cnt = total_packets;
}
// TODO: We need a second PIE rule to count the bytes
flow_stats_update(&cls_flower->stats, 100 * new_packets, new_packets, 0, lastused,
FLOW_ACTION_HW_STATS_IMMEDIATE);
return 0;
}
static int rtl83xx_setup_tc_cls_flower(struct rtl838x_switch_priv *priv,
struct flow_cls_offload *cls_flower)
{
pr_debug("%s: %d\n", __func__, cls_flower->command);
switch (cls_flower->command) {
case FLOW_CLS_REPLACE:
return rtl83xx_configure_flower(priv, cls_flower);
case FLOW_CLS_DESTROY:
return rtl83xx_delete_flower(priv, cls_flower);
case FLOW_CLS_STATS:
return rtl83xx_stats_flower(priv, cls_flower);
default:
return -EOPNOTSUPP;
}
}
static int rtl83xx_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
void *cb_priv)
{
struct rtl838x_switch_priv *priv = cb_priv;
switch (type) {
case TC_SETUP_CLSFLOWER:
pr_debug("%s: TC_SETUP_CLSFLOWER\n", __func__);
return rtl83xx_setup_tc_cls_flower(priv, type_data);
default:
return -EOPNOTSUPP;
}
}
static LIST_HEAD(rtl83xx_block_cb_list);
int rtl83xx_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data)
{
struct rtl838x_switch_priv *priv;
struct flow_block_offload *f = type_data;
static bool first_time = true;
int err;
pr_debug("%s: %d\n", __func__, type);
if(!netdev_uses_dsa(dev)) {
pr_err("%s: no DSA\n", __func__);
return 0;
}
priv = dev->dsa_ptr->ds->priv;
switch (type) {
case TC_SETUP_BLOCK:
if (first_time) {
first_time = false;
err = rhashtable_init(&priv->tc_ht, &tc_ht_params);
if (err)
pr_err("%s: Could not initialize hash table\n", __func__);
}
f->unlocked_driver_cb = true;
return flow_block_cb_setup_simple(type_data,
&rtl83xx_block_cb_list,
rtl83xx_setup_tc_block_cb,
priv, priv, true);
default:
return -EOPNOTSUPP;
}
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,457 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _RTL838X_ETH_H
#define _RTL838X_ETH_H
/*
* Register definition
*/
/* Per port MAC control */
#define RTL838X_MAC_PORT_CTRL (0xd560)
#define RTL839X_MAC_PORT_CTRL (0x8004)
#define RTL930X_MAC_L2_PORT_CTRL (0x3268)
#define RTL930X_MAC_PORT_CTRL (0x3260)
#define RTL931X_MAC_L2_PORT_CTRL (0x6000)
#define RTL931X_MAC_PORT_CTRL (0x6004)
/* DMA interrupt control and status registers */
#define RTL838X_DMA_IF_CTRL (0x9f58)
#define RTL838X_DMA_IF_INTR_STS (0x9f54)
#define RTL838X_DMA_IF_INTR_MSK (0x9f50)
#define RTL839X_DMA_IF_CTRL (0x786c)
#define RTL839X_DMA_IF_INTR_STS (0x7868)
#define RTL839X_DMA_IF_INTR_MSK (0x7864)
#define RTL930X_DMA_IF_CTRL (0xe028)
#define RTL930X_DMA_IF_INTR_RX_RUNOUT_STS (0xe01C)
#define RTL930X_DMA_IF_INTR_RX_DONE_STS (0xe020)
#define RTL930X_DMA_IF_INTR_TX_DONE_STS (0xe024)
#define RTL930X_DMA_IF_INTR_RX_RUNOUT_MSK (0xe010)
#define RTL930X_DMA_IF_INTR_RX_DONE_MSK (0xe014)
#define RTL930X_DMA_IF_INTR_TX_DONE_MSK (0xe018)
#define RTL930X_L2_NTFY_IF_INTR_MSK (0xe04C)
#define RTL930X_L2_NTFY_IF_INTR_STS (0xe050)
/* TODO: RTL931X_DMA_IF_CTRL has different bits meanings */
#define RTL931X_DMA_IF_CTRL (0x0928)
#define RTL931X_DMA_IF_INTR_RX_RUNOUT_STS (0x091c)
#define RTL931X_DMA_IF_INTR_RX_DONE_STS (0x0920)
#define RTL931X_DMA_IF_INTR_TX_DONE_STS (0x0924)
#define RTL931X_DMA_IF_INTR_RX_RUNOUT_MSK (0x0910)
#define RTL931X_DMA_IF_INTR_RX_DONE_MSK (0x0914)
#define RTL931X_DMA_IF_INTR_TX_DONE_MSK (0x0918)
#define RTL931X_L2_NTFY_IF_INTR_MSK (0x09E4)
#define RTL931X_L2_NTFY_IF_INTR_STS (0x09E8)
#define RTL838X_MAC_FORCE_MODE_CTRL (0xa104)
#define RTL839X_MAC_FORCE_MODE_CTRL (0x02bc)
#define RTL930X_MAC_FORCE_MODE_CTRL (0xCA1C)
#define RTL931X_MAC_FORCE_MODE_CTRL (0x0ddc)
/* MAC address settings */
#define RTL838X_MAC (0xa9ec)
#define RTL839X_MAC (0x02b4)
#define RTL838X_MAC_ALE (0x6b04)
#define RTL838X_MAC2 (0xa320)
#define RTL930X_MAC_L2_ADDR_CTRL (0xC714)
#define RTL931X_MAC_L2_ADDR_CTRL (0x135c)
/* Ringbuffer setup */
#define RTL838X_DMA_RX_BASE (0x9f00)
#define RTL839X_DMA_RX_BASE (0x780c)
#define RTL930X_DMA_RX_BASE (0xdf00)
#define RTL931X_DMA_RX_BASE (0x0800)
#define RTL838X_DMA_TX_BASE (0x9f40)
#define RTL839X_DMA_TX_BASE (0x784c)
#define RTL930X_DMA_TX_BASE (0xe000)
#define RTL931X_DMA_TX_BASE (0x0900)
#define RTL838X_DMA_IF_RX_RING_SIZE (0xB7E4)
#define RTL839X_DMA_IF_RX_RING_SIZE (0x6038)
#define RTL930X_DMA_IF_RX_RING_SIZE (0x7C60)
#define RTL931X_DMA_IF_RX_RING_SIZE (0x2080)
#define RTL838X_DMA_IF_RX_RING_CNTR (0xB7E8)
#define RTL839X_DMA_IF_RX_RING_CNTR (0x603c)
#define RTL930X_DMA_IF_RX_RING_CNTR (0x7C8C)
#define RTL931X_DMA_IF_RX_RING_CNTR (0x20AC)
#define RTL838X_DMA_IF_RX_CUR (0x9F20)
#define RTL839X_DMA_IF_RX_CUR (0x782c)
#define RTL930X_DMA_IF_RX_CUR (0xdf80)
#define RTL931X_DMA_IF_RX_CUR (0x0880)
#define RTL838X_DMA_IF_TX_CUR_DESC_ADDR_CTRL (0x9F48)
#define RTL930X_DMA_IF_TX_CUR_DESC_ADDR_CTRL (0xE008)
#define RTL838X_DMY_REG31 (0x3b28)
#define RTL838X_SDS_MODE_SEL (0x0028)
#define RTL838X_SDS_CFG_REG (0x0034)
#define RTL838X_INT_MODE_CTRL (0x005c)
#define RTL838X_CHIP_INFO (0x00d8)
#define RTL838X_SDS4_REG28 (0xef80)
#define RTL838X_SDS4_DUMMY0 (0xef8c)
#define RTL838X_SDS5_EXT_REG6 (0xf18c)
/* L2 features */
#define RTL839X_TBL_ACCESS_L2_CTRL (0x1180)
#define RTL839X_TBL_ACCESS_L2_DATA(idx) (0x1184 + ((idx) << 2))
#define RTL838X_TBL_ACCESS_CTRL_0 (0x6914)
#define RTL838X_TBL_ACCESS_DATA_0(idx) (0x6918 + ((idx) << 2))
/* MAC-side link state handling */
#define RTL838X_MAC_LINK_STS (0xa188)
#define RTL839X_MAC_LINK_STS (0x0390)
#define RTL930X_MAC_LINK_STS (0xCB10)
#define RTL931X_MAC_LINK_STS (0x0ec0)
#define RTL838X_MAC_LINK_SPD_STS (0xa190)
#define RTL839X_MAC_LINK_SPD_STS (0x03a0)
#define RTL930X_MAC_LINK_SPD_STS (0xCB18)
#define RTL931X_MAC_LINK_SPD_STS (0x0ed0)
#define RTL838X_MAC_LINK_DUP_STS (0xa19c)
#define RTL839X_MAC_LINK_DUP_STS (0x03b0)
#define RTL930X_MAC_LINK_DUP_STS (0xCB28)
#define RTL931X_MAC_LINK_DUP_STS (0x0ef0)
// TODO: RTL8390_MAC_LINK_MEDIA_STS_ADDR ???
#define RTL838X_MAC_TX_PAUSE_STS (0xa1a0)
#define RTL839X_MAC_TX_PAUSE_STS (0x03b8)
#define RTL930X_MAC_TX_PAUSE_STS (0xCB2C)
#define RTL931X_MAC_TX_PAUSE_STS (0x0ef8)
#define RTL838X_MAC_RX_PAUSE_STS (0xa1a4)
#define RTL839X_MAC_RX_PAUSE_STS (0xCB30)
#define RTL930X_MAC_RX_PAUSE_STS (0xC2F8)
#define RTL931X_MAC_RX_PAUSE_STS (0x0f00)
#define RTL838X_EEE_TX_TIMER_GIGA_CTRL (0xaa04)
#define RTL838X_EEE_TX_TIMER_GELITE_CTRL (0xaa08)
#define RTL930X_L2_UNKN_UC_FLD_PMSK (0x9064)
#define RTL931X_L2_UNKN_UC_FLD_PMSK (0xC8F4)
#define RTL839X_MAC_GLB_CTRL (0x02a8)
#define RTL839X_SCHED_LB_TICK_TKN_CTRL (0x60f8)
#define RTL838X_L2_TBL_FLUSH_CTRL (0x3370)
#define RTL839X_L2_TBL_FLUSH_CTRL (0x3ba0)
#define RTL930X_L2_TBL_FLUSH_CTRL (0x9404)
#define RTL931X_L2_TBL_FLUSH_CTRL (0xCD9C)
#define RTL930X_L2_PORT_SABLK_CTRL (0x905c)
#define RTL930X_L2_PORT_DABLK_CTRL (0x9060)
/* MAC link state bits */
#define FORCE_EN (1 << 0)
#define FORCE_LINK_EN (1 << 1)
#define NWAY_EN (1 << 2)
#define DUPLX_MODE (1 << 3)
#define TX_PAUSE_EN (1 << 6)
#define RX_PAUSE_EN (1 << 7)
/* L2 Notification DMA interface */
#define RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL (0x785C)
#define RTL839X_L2_NOTIFICATION_CTRL (0x7808)
#define RTL931X_L2_NTFY_RING_BASE_ADDR (0x09DC)
#define RTL931X_L2_NTFY_RING_CUR_ADDR (0x09E0)
#define RTL839X_L2_NOTIFICATION_CTRL (0x7808)
#define RTL931X_L2_NTFY_CTRL (0xCDC8)
#define RTL838X_L2_CTRL_0 (0x3200)
#define RTL839X_L2_CTRL_0 (0x3800)
#define RTL930X_L2_CTRL (0x8FD8)
#define RTL931X_L2_CTRL (0xC800)
/* TRAPPING to CPU-PORT */
#define RTL838X_SPCL_TRAP_IGMP_CTRL (0x6984)
#define RTL838X_RMA_CTRL_0 (0x4300)
#define RTL838X_RMA_CTRL_1 (0x4304)
#define RTL839X_RMA_CTRL_0 (0x1200)
#define RTL839X_SPCL_TRAP_IGMP_CTRL (0x1058)
#define RTL839X_RMA_CTRL_1 (0x1204)
#define RTL839X_RMA_CTRL_2 (0x1208)
#define RTL839X_RMA_CTRL_3 (0x120C)
#define RTL930X_VLAN_APP_PKT_CTRL (0xA23C)
#define RTL930X_RMA_CTRL_0 (0x9E60)
#define RTL930X_RMA_CTRL_1 (0x9E64)
#define RTL930X_RMA_CTRL_2 (0x9E68)
#define RTL931X_VLAN_APP_PKT_CTRL (0x96b0)
#define RTL931X_RMA_CTRL_0 (0x8800)
#define RTL931X_RMA_CTRL_1 (0x8804)
#define RTL931X_RMA_CTRL_2 (0x8808)
/* Advanced SMI control for clause 45 PHYs */
#define RTL930X_SMI_MAC_TYPE_CTRL (0xCA04)
#define RTL930X_SMI_PORT24_27_ADDR_CTRL (0xCB90)
#define RTL930X_SMI_PORT0_15_POLLING_SEL (0xCA08)
#define RTL930X_SMI_PORT16_27_POLLING_SEL (0xCA0C)
#define RTL930X_SMI_10GPHY_POLLING_REG0_CFG (0xCBB4)
#define RTL930X_SMI_10GPHY_POLLING_REG9_CFG (0xCBB8)
#define RTL930X_SMI_10GPHY_POLLING_REG10_CFG (0xCBBC)
#define RTL930X_SMI_PRVTE_POLLING_CTRL (0xCA10)
/* Registers of the internal Serdes of the 8390 */
#define RTL839X_SDS12_13_XSG0 (0xB800)
/* Chip configuration registers of the RTL9310 */
#define RTL931X_MEM_ENCAP_INIT (0x4854)
#define RTL931X_MEM_MIB_INIT (0x7E18)
#define RTL931X_MEM_ACL_INIT (0x40BC)
#define RTL931X_MEM_ALE_INIT_0 (0x83F0)
#define RTL931X_MEM_ALE_INIT_1 (0x83F4)
#define RTL931X_MEM_ALE_INIT_2 (0x82E4)
#define RTL931X_MDX_CTRL_RSVD (0x0fcc)
#define RTL931X_PS_SOC_CTRL (0x13f8)
#define RTL931X_SMI_10GPHY_POLLING_SEL2 (0xCF8)
#define RTL931X_SMI_10GPHY_POLLING_SEL3 (0xCFC)
#define RTL931X_SMI_10GPHY_POLLING_SEL4 (0xD00)
/* Registers of the internal Serdes of the 8380 */
#define RTL838X_SDS4_FIB_REG0 (0xF800)
inline int rtl838x_mac_port_ctrl(int p)
{
return RTL838X_MAC_PORT_CTRL + (p << 7);
}
inline int rtl839x_mac_port_ctrl(int p)
{
return RTL839X_MAC_PORT_CTRL + (p << 7);
}
/* On the RTL931XX, the functionality of the MAC port control register is split up
* into RTL931X_MAC_L2_PORT_CTRL and RTL931X_MAC_PORT_CTRL the functionality used
* by the Ethernet driver is in the same bits now in RTL931X_MAC_L2_PORT_CTRL
*/
inline int rtl930x_mac_port_ctrl(int p)
{
return RTL930X_MAC_L2_PORT_CTRL + (p << 6);
}
inline int rtl931x_mac_port_ctrl(int p)
{
return RTL931X_MAC_L2_PORT_CTRL + (p << 7);
}
inline int rtl838x_dma_if_rx_ring_size(int i)
{
return RTL838X_DMA_IF_RX_RING_SIZE + ((i >> 3) << 2);
}
inline int rtl839x_dma_if_rx_ring_size(int i)
{
return RTL839X_DMA_IF_RX_RING_SIZE + ((i >> 3) << 2);
}
inline int rtl930x_dma_if_rx_ring_size(int i)
{
return RTL930X_DMA_IF_RX_RING_SIZE + ((i / 3) << 2);
}
inline int rtl931x_dma_if_rx_ring_size(int i)
{
return RTL931X_DMA_IF_RX_RING_SIZE + ((i / 3) << 2);
}
inline int rtl838x_dma_if_rx_ring_cntr(int i)
{
return RTL838X_DMA_IF_RX_RING_CNTR + ((i >> 3) << 2);
}
inline int rtl839x_dma_if_rx_ring_cntr(int i)
{
return RTL839X_DMA_IF_RX_RING_CNTR + ((i >> 3) << 2);
}
inline int rtl930x_dma_if_rx_ring_cntr(int i)
{
return RTL930X_DMA_IF_RX_RING_CNTR + ((i / 3) << 2);
}
inline int rtl931x_dma_if_rx_ring_cntr(int i)
{
return RTL931X_DMA_IF_RX_RING_CNTR + ((i / 3) << 2);
}
inline u32 rtl838x_get_mac_link_sts(int port)
{
return (sw_r32(RTL838X_MAC_LINK_STS) & BIT(port));
}
inline u32 rtl839x_get_mac_link_sts(int p)
{
return (sw_r32(RTL839X_MAC_LINK_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl930x_get_mac_link_sts(int port)
{
u32 link = sw_r32(RTL930X_MAC_LINK_STS);
link = sw_r32(RTL930X_MAC_LINK_STS);
pr_info("%s link state is %08x\n", __func__, link);
return link & BIT(port);
}
inline u32 rtl931x_get_mac_link_sts(int p)
{
return (sw_r32(RTL931X_MAC_LINK_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl838x_get_mac_link_dup_sts(int port)
{
return (sw_r32(RTL838X_MAC_LINK_DUP_STS) & BIT(port));
}
inline u32 rtl839x_get_mac_link_dup_sts(int p)
{
return (sw_r32(RTL839X_MAC_LINK_DUP_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl930x_get_mac_link_dup_sts(int port)
{
return (sw_r32(RTL930X_MAC_LINK_DUP_STS) & BIT(port));
}
inline u32 rtl931x_get_mac_link_dup_sts(int p)
{
return (sw_r32(RTL931X_MAC_LINK_DUP_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl838x_get_mac_link_spd_sts(int port)
{
int r = RTL838X_MAC_LINK_SPD_STS + ((port >> 4) << 2);
u32 speed = sw_r32(r);
speed >>= (port % 16) << 1;
return (speed & 0x3);
}
inline u32 rtl839x_get_mac_link_spd_sts(int port)
{
int r = RTL839X_MAC_LINK_SPD_STS + ((port >> 4) << 2);
u32 speed = sw_r32(r);
speed >>= (port % 16) << 1;
return (speed & 0x3);
}
inline u32 rtl930x_get_mac_link_spd_sts(int port)
{
int r = RTL930X_MAC_LINK_SPD_STS + ((port >> 3) << 2);
u32 speed = sw_r32(r);
speed >>= (port % 8) << 2;
return (speed & 0xf);
}
inline u32 rtl931x_get_mac_link_spd_sts(int port)
{
int r = RTL931X_MAC_LINK_SPD_STS + ((port >> 3) << 2);
u32 speed = sw_r32(r);
speed >>= (port % 8) << 2;
return (speed & 0xf);
}
inline u32 rtl838x_get_mac_rx_pause_sts(int port)
{
return (sw_r32(RTL838X_MAC_RX_PAUSE_STS) & (1 << port));
}
inline u32 rtl839x_get_mac_rx_pause_sts(int p)
{
return (sw_r32(RTL839X_MAC_RX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl930x_get_mac_rx_pause_sts(int port)
{
return (sw_r32(RTL930X_MAC_RX_PAUSE_STS) & (1 << port));
}
inline u32 rtl931x_get_mac_rx_pause_sts(int p)
{
return (sw_r32(RTL931X_MAC_RX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl838x_get_mac_tx_pause_sts(int port)
{
return (sw_r32(RTL838X_MAC_TX_PAUSE_STS) & (1 << port));
}
inline u32 rtl839x_get_mac_tx_pause_sts(int p)
{
return (sw_r32(RTL839X_MAC_TX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
inline u32 rtl930x_get_mac_tx_pause_sts(int port)
{
return (sw_r32(RTL930X_MAC_TX_PAUSE_STS) & (1 << port));
}
inline u32 rtl931x_get_mac_tx_pause_sts(int p)
{
return (sw_r32(RTL931X_MAC_TX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32));
}
struct p_hdr;
struct dsa_tag;
struct rtl838x_eth_reg {
irqreturn_t (*net_irq)(int irq, void *dev_id);
int (*mac_port_ctrl)(int port);
int dma_if_intr_sts;
int dma_if_intr_msk;
int dma_if_intr_rx_runout_sts;
int dma_if_intr_rx_done_sts;
int dma_if_intr_tx_done_sts;
int dma_if_intr_rx_runout_msk;
int dma_if_intr_rx_done_msk;
int dma_if_intr_tx_done_msk;
int l2_ntfy_if_intr_sts;
int l2_ntfy_if_intr_msk;
int dma_if_ctrl;
int mac_force_mode_ctrl;
int dma_rx_base;
int dma_tx_base;
int (*dma_if_rx_ring_size)(int ring);
int (*dma_if_rx_ring_cntr)(int ring);
int dma_if_rx_cur;
int rst_glb_ctrl;
u32 (*get_mac_link_sts)(int port);
u32 (*get_mac_link_dup_sts)(int port);
u32 (*get_mac_link_spd_sts)(int port);
u32 (*get_mac_rx_pause_sts)(int port);
u32 (*get_mac_tx_pause_sts)(int port);
int mac;
int l2_tbl_flush_ctrl;
void (*update_cntr)(int r, int work_done);
void (*create_tx_header)(struct p_hdr *h, unsigned int dest_port, int prio);
bool (*decode_tag)(struct p_hdr *h, struct dsa_tag *tag);
};
int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val);
int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val);
int rtl838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val);
int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val);
int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val);
int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val);
int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val);
int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val);
int rtl83xx_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data);
#endif /* _RTL838X_ETH_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0-only
struct rtl83xx_shared_private {
char *name;
};
struct __attribute__ ((__packed__)) part {
uint16_t start;
uint8_t wordsize;
uint8_t words;
};
struct __attribute__ ((__packed__)) fw_header {
uint32_t magic;
uint32_t phy;
uint32_t checksum;
uint32_t version;
struct part parts[10];
};
// TODO: fixed path?
#define FIRMWARE_838X_8380_1 "rtl838x_phy/rtl838x_8380.fw"
#define FIRMWARE_838X_8214FC_1 "rtl838x_phy/rtl838x_8214fc.fw"
#define FIRMWARE_838X_8218b_1 "rtl838x_phy/rtl838x_8218b.fw"
/* External RTL8218B and RTL8214FC IDs are identical */
#define PHY_ID_RTL8214C 0x001cc942
#define PHY_ID_RTL8214FC 0x001cc981
#define PHY_ID_RTL8218B_E 0x001cc981
#define PHY_ID_RTL8218D 0x001cc983
#define PHY_ID_RTL8218B_I 0x001cca40
#define PHY_ID_RTL8221B 0x001cc849
#define PHY_ID_RTL8226 0x001cc838
#define PHY_ID_RTL8390_GENERIC 0x001ccab0
#define PHY_ID_RTL8393_I 0x001c8393
#define PHY_ID_RTL9300_I 0x70d03106
// PHY MMD devices
#define MMD_AN 7
#define MMD_VEND2 31
/* Registers of the internal Serdes of the 8380 */
#define RTL838X_SDS_MODE_SEL (0x0028)
#define RTL838X_SDS_CFG_REG (0x0034)
#define RTL838X_INT_MODE_CTRL (0x005c)
#define RTL838X_DMY_REG31 (0x3b28)
#define RTL8380_SDS4_FIB_REG0 (0xF800)
#define RTL838X_SDS4_REG28 (0xef80)
#define RTL838X_SDS4_DUMMY0 (0xef8c)
#define RTL838X_SDS5_EXT_REG6 (0xf18c)
#define RTL838X_SDS4_FIB_REG0 (RTL838X_SDS4_REG28 + 0x880)
#define RTL838X_SDS5_FIB_REG0 (RTL838X_SDS4_REG28 + 0x980)
/* Registers of the internal SerDes of the RTL8390 */
#define RTL839X_SDS12_13_XSG0 (0xB800)
/* Registers of the internal Serdes of the 9300 */
#define RTL930X_SDS_INDACS_CMD (0x03B0)
#define RTL930X_SDS_INDACS_DATA (0x03B4)
#define RTL930X_MAC_FORCE_MODE_CTRL (0xCA1C)
/*Registers of the internal SerDes of the 9310 */
#define RTL931X_SERDES_INDRT_ACCESS_CTRL (0x5638)
#define RTL931X_SERDES_INDRT_DATA_CTRL (0x563C)
#define RTL931X_SERDES_MODE_CTRL (0x13cc)
#define RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR (0x13F4)
#define RTL931X_MAC_SERDES_MODE_CTRL(sds) (0x136C + (((sds) << 2)))

View file

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2022 Markus Stockhausen
*
* RTL83XX clock indices
*/
#ifndef __DT_BINDINGS_CLOCK_RTL83XX_H
#define __DT_BINDINGS_CLOCK_RTL83XX_H
#define CLK_CPU 0
#define CLK_MEM 1
#define CLK_LXB 2
#define CLK_COUNT 3
#endif /* __DT_BINDINGS_CLOCK_RTL83XX_H */

View file

@ -0,0 +1,103 @@
From a362c0ce64866939c3daa17c76943cfed555b065 Mon Sep 17 00:00:00 2001
From: Sander Vanheule <sander@svanheule.net>
Date: Tue, 30 Mar 2021 19:48:42 +0200
Subject: dt-bindings: gpio: Binding for Realtek Otto GPIO
Add a binding description for Realtek's GPIO controller found on several
of their MIPS-based SoCs (codenamed Otto), such as the RTL838x and
RTL839x series of switch SoCs.
A fallback binding 'realtek,otto-gpio' is provided for cases where the
actual port ordering is not known yet, and enabling the interrupt
controller may result in uncaught interrupts.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../bindings/gpio/realtek,otto-gpio.yaml | 78 ++++++++++++++++++++++
1 file changed, 78 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/realtek,otto-gpio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek Otto GPIO controller
+
+maintainers:
+ - Sander Vanheule <sander@svanheule.net>
+ - Bert Vermeulen <bert@biot.com>
+
+description: |
+ Realtek's GPIO controller on their MIPS switch SoCs (Otto platform) consists
+ of two banks of 32 GPIOs. These GPIOs can generate edge-triggered interrupts.
+ Each bank's interrupts are cascased into one interrupt line on the parent
+ interrupt controller, if provided.
+ This binding allows defining a single bank in the devicetree. The interrupt
+ controller is not supported on the fallback compatible name, which only
+ allows for GPIO port use.
+
+properties:
+ $nodename:
+ pattern: "^gpio@[0-9a-f]+$"
+
+ compatible:
+ items:
+ - enum:
+ - realtek,rtl8380-gpio
+ - realtek,rtl8390-gpio
+ - const: realtek,otto-gpio
+
+ reg:
+ maxItems: 1
+
+ "#gpio-cells":
+ const: 2
+
+ gpio-controller: true
+
+ ngpios:
+ minimum: 1
+ maximum: 32
+
+ interrupt-controller: true
+
+ "#interrupt-cells":
+ const: 2
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - "#gpio-cells"
+ - gpio-controller
+
+additionalProperties: false
+
+dependencies:
+ interrupt-controller: [ interrupts ]
+
+examples:
+ - |
+ gpio@3500 {
+ compatible = "realtek,rtl8380-gpio", "realtek,otto-gpio";
+ reg = <0x3500 0x1c>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ ngpios = <24>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupt-parent = <&rtlintc>;
+ interrupts = <23>;
+ };
+
+...

View file

@ -0,0 +1,394 @@
From f0f7d662e8514169c90d3d84cd6df773b2983088 Mon Sep 17 00:00:00 2001
From: Sander Vanheule <sander@svanheule.net>
Date: Tue, 30 Mar 2021 19:48:43 +0200
Subject: gpio: Add Realtek Otto GPIO support
Realtek MIPS SoCs (platform name Otto) have GPIO controllers with up to
64 GPIOs, divided over two banks. Each bank has a set of registers for
32 GPIOs, with support for edge-triggered interrupts.
Each GPIO bank consists of four 8-bit GPIO ports (ABCD and EFGH). Most
registers pack one bit per GPIO, except for the IMR register, which
packs two bits per GPIO (AB-CD).
Although the byte order is currently assumed to have port A..D at offset
0x0..0x3, this has been observed to be reversed on other, Lexra-based,
SoCs (e.g. RTL8196E/97D/97F).
Interrupt support is disabled for the fallback devicetree-compatible
'realtek,otto-gpio'. This allows for quick support of GPIO banks in
which the byte order would be unknown. In this case, the port ordering
in the IMR registers may not match the reversed order in the other
registers (DCBA, and BA-DC or DC-BA).
Signed-off-by: Sander Vanheule <sander@svanheule.net>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/gpio/Kconfig | 13 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-realtek-otto.c | 325 +++++++++++++++++++++++++++++++++++++++
3 files changed, 339 insertions(+)
create mode 100644 drivers/gpio/gpio-realtek-otto.c
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -489,6 +489,19 @@ config GPIO_RDA
help
Say Y here to support RDA Micro GPIO controller.
+config GPIO_REALTEK_OTTO
+ tristate "Realtek Otto GPIO support"
+ depends on MACH_REALTEK_RTL
+ default MACH_REALTEK_RTL
+ select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
+ help
+ The GPIO controller on the Otto MIPS platform supports up to two
+ banks of 32 GPIOs, with edge triggered interrupts. The 32 GPIOs
+ are grouped in four 8-bit wide ports.
+
+ When built as a module, the module will be called realtek_otto_gpio.
+
config GPIO_REG
bool
help
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -125,6 +125,7 @@ obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t
obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
obj-$(CONFIG_GPIO_RDA) += gpio-rda.o
obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
+obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o
obj-$(CONFIG_GPIO_REG) += gpio-reg.o
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
--- /dev/null
+++ b/drivers/gpio/gpio-realtek-otto.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/gpio/driver.h>
+#include <linux/irq.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+
+/*
+ * Total register block size is 0x1C for one bank of four ports (A, B, C, D).
+ * An optional second bank, with ports E, F, G, and H, may be present, starting
+ * at register offset 0x1C.
+ */
+
+/*
+ * Pin select: (0) "normal", (1) "dedicate peripheral"
+ * Not used on RTL8380/RTL8390, peripheral selection is managed by control bits
+ * in the peripheral registers.
+ */
+#define REALTEK_GPIO_REG_CNR 0x00
+/* Clear bit (0) for input, set bit (1) for output */
+#define REALTEK_GPIO_REG_DIR 0x08
+#define REALTEK_GPIO_REG_DATA 0x0C
+/* Read bit for IRQ status, write 1 to clear IRQ */
+#define REALTEK_GPIO_REG_ISR 0x10
+/* Two bits per GPIO in IMR registers */
+#define REALTEK_GPIO_REG_IMR 0x14
+#define REALTEK_GPIO_REG_IMR_AB 0x14
+#define REALTEK_GPIO_REG_IMR_CD 0x18
+#define REALTEK_GPIO_IMR_LINE_MASK GENMASK(1, 0)
+#define REALTEK_GPIO_IRQ_EDGE_FALLING 1
+#define REALTEK_GPIO_IRQ_EDGE_RISING 2
+#define REALTEK_GPIO_IRQ_EDGE_BOTH 3
+
+#define REALTEK_GPIO_MAX 32
+#define REALTEK_GPIO_PORTS_PER_BANK 4
+
+/**
+ * realtek_gpio_ctrl - Realtek Otto GPIO driver data
+ *
+ * @gc: Associated gpio_chip instance
+ * @base: Base address of the register block for a GPIO bank
+ * @lock: Lock for accessing the IRQ registers and values
+ * @intr_mask: Mask for interrupts lines
+ * @intr_type: Interrupt type selection
+ *
+ * Because the interrupt mask register (IMR) combines the function of IRQ type
+ * selection and masking, two extra values are stored. @intr_mask is used to
+ * mask/unmask the interrupts for a GPIO port, and @intr_type is used to store
+ * the selected interrupt types. The logical AND of these values is written to
+ * IMR on changes.
+ */
+struct realtek_gpio_ctrl {
+ struct gpio_chip gc;
+ void __iomem *base;
+ raw_spinlock_t lock;
+ u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK];
+ u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK];
+};
+
+/* Expand with more flags as devices with other quirks are added */
+enum realtek_gpio_flags {
+ /*
+ * Allow disabling interrupts, for cases where the port order is
+ * unknown. This may result in a port mismatch between ISR and IMR.
+ * An interrupt would appear to come from a different line than the
+ * line the IRQ handler was assigned to, causing uncaught interrupts.
+ */
+ GPIO_INTERRUPTS_DISABLED = BIT(0),
+};
+
+static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+
+ return container_of(gc, struct realtek_gpio_ctrl, gc);
+}
+
+/*
+ * Normal port order register access
+ *
+ * Port information is stored with the first port at offset 0, followed by the
+ * second, etc. Most registers store one bit per GPIO and use a u8 value per
+ * port. The two interrupt mask registers store two bits per GPIO, so use u16
+ * values.
+ */
+static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl,
+ unsigned int port, u16 irq_type, u16 irq_mask)
+{
+ iowrite16(irq_type & irq_mask, ctrl->base + REALTEK_GPIO_REG_IMR + 2 * port);
+}
+
+static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl,
+ unsigned int port, u8 mask)
+{
+ iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + port);
+}
+
+static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port)
+{
+ return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + port);
+}
+
+/* Set the rising and falling edge mask bits for a GPIO port pin */
+static u16 realtek_gpio_imr_bits(unsigned int pin, u16 value)
+{
+ return (value & REALTEK_GPIO_IMR_LINE_MASK) << 2 * pin;
+}
+
+static void realtek_gpio_irq_ack(struct irq_data *data)
+{
+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
+ irq_hw_number_t line = irqd_to_hwirq(data);
+ unsigned int port = line / 8;
+ unsigned int port_pin = line % 8;
+
+ realtek_gpio_clear_isr(ctrl, port, BIT(port_pin));
+}
+
+static void realtek_gpio_irq_unmask(struct irq_data *data)
+{
+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
+ unsigned int line = irqd_to_hwirq(data);
+ unsigned int port = line / 8;
+ unsigned int port_pin = line % 8;
+ unsigned long flags;
+ u16 m;
+
+ raw_spin_lock_irqsave(&ctrl->lock, flags);
+ m = ctrl->intr_mask[port];
+ m |= realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK);
+ ctrl->intr_mask[port] = m;
+ realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m);
+ raw_spin_unlock_irqrestore(&ctrl->lock, flags);
+}
+
+static void realtek_gpio_irq_mask(struct irq_data *data)
+{
+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
+ unsigned int line = irqd_to_hwirq(data);
+ unsigned int port = line / 8;
+ unsigned int port_pin = line % 8;
+ unsigned long flags;
+ u16 m;
+
+ raw_spin_lock_irqsave(&ctrl->lock, flags);
+ m = ctrl->intr_mask[port];
+ m &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK);
+ ctrl->intr_mask[port] = m;
+ realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m);
+ raw_spin_unlock_irqrestore(&ctrl->lock, flags);
+}
+
+static int realtek_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
+{
+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
+ unsigned int line = irqd_to_hwirq(data);
+ unsigned int port = line / 8;
+ unsigned int port_pin = line % 8;
+ unsigned long flags;
+ u16 type, t;
+
+ switch (flow_type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_FALLING:
+ type = REALTEK_GPIO_IRQ_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ type = REALTEK_GPIO_IRQ_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ type = REALTEK_GPIO_IRQ_EDGE_BOTH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ irq_set_handler_locked(data, handle_edge_irq);
+
+ raw_spin_lock_irqsave(&ctrl->lock, flags);
+ t = ctrl->intr_type[port];
+ t &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK);
+ t |= realtek_gpio_imr_bits(port_pin, type);
+ ctrl->intr_type[port] = t;
+ realtek_gpio_write_imr(ctrl, port, t, ctrl->intr_mask[port]);
+ raw_spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ return 0;
+}
+
+static void realtek_gpio_irq_handler(struct irq_desc *desc)
+{
+ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+ struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);
+ struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+ unsigned int lines_done;
+ unsigned int port_pin_count;
+ unsigned int irq;
+ unsigned long status;
+ int offset;
+
+ chained_irq_enter(irq_chip, desc);
+
+ for (lines_done = 0; lines_done < gc->ngpio; lines_done += 8) {
+ status = realtek_gpio_read_isr(ctrl, lines_done / 8);
+ port_pin_count = min(gc->ngpio - lines_done, 8U);
+ for_each_set_bit(offset, &status, port_pin_count) {
+ irq = irq_find_mapping(gc->irq.domain, offset);
+ generic_handle_irq(irq);
+ }
+ }
+
+ chained_irq_exit(irq_chip, desc);
+}
+
+static int realtek_gpio_irq_init(struct gpio_chip *gc)
+{
+ struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);
+ unsigned int port;
+
+ for (port = 0; (port * 8) < gc->ngpio; port++) {
+ realtek_gpio_write_imr(ctrl, port, 0, 0);
+ realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0));
+ }
+
+ return 0;
+}
+
+static struct irq_chip realtek_gpio_irq_chip = {
+ .name = "realtek-otto-gpio",
+ .irq_ack = realtek_gpio_irq_ack,
+ .irq_mask = realtek_gpio_irq_mask,
+ .irq_unmask = realtek_gpio_irq_unmask,
+ .irq_set_type = realtek_gpio_irq_set_type,
+};
+
+static const struct of_device_id realtek_gpio_of_match[] = {
+ {
+ .compatible = "realtek,otto-gpio",
+ .data = (void *)GPIO_INTERRUPTS_DISABLED,
+ },
+ {
+ .compatible = "realtek,rtl8380-gpio",
+ },
+ {
+ .compatible = "realtek,rtl8390-gpio",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, realtek_gpio_of_match);
+
+static int realtek_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ unsigned int dev_flags;
+ struct gpio_irq_chip *girq;
+ struct realtek_gpio_ctrl *ctrl;
+ u32 ngpios;
+ int err, irq;
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ dev_flags = (unsigned int) device_get_match_data(dev);
+
+ ngpios = REALTEK_GPIO_MAX;
+ device_property_read_u32(dev, "ngpios", &ngpios);
+
+ if (ngpios > REALTEK_GPIO_MAX) {
+ dev_err(&pdev->dev, "invalid ngpios (max. %d)\n",
+ REALTEK_GPIO_MAX);
+ return -EINVAL;
+ }
+
+ ctrl->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ctrl->base))
+ return PTR_ERR(ctrl->base);
+
+ raw_spin_lock_init(&ctrl->lock);
+
+ err = bgpio_init(&ctrl->gc, dev, 4,
+ ctrl->base + REALTEK_GPIO_REG_DATA, NULL, NULL,
+ ctrl->base + REALTEK_GPIO_REG_DIR, NULL,
+ BGPIOF_BIG_ENDIAN_BYTE_ORDER);
+ if (err) {
+ dev_err(dev, "unable to init generic GPIO");
+ return err;
+ }
+
+ ctrl->gc.ngpio = ngpios;
+ ctrl->gc.owner = THIS_MODULE;
+
+ irq = platform_get_irq_optional(pdev, 0);
+ if (!(dev_flags & GPIO_INTERRUPTS_DISABLED) && irq > 0) {
+ girq = &ctrl->gc.irq;
+ girq->chip = &realtek_gpio_irq_chip;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->handler = handle_bad_irq;
+ girq->parent_handler = realtek_gpio_irq_handler;
+ girq->num_parents = 1;
+ girq->parents = devm_kcalloc(dev, girq->num_parents,
+ sizeof(*girq->parents), GFP_KERNEL);
+ if (!girq->parents)
+ return -ENOMEM;
+ girq->parents[0] = irq;
+ girq->init_hw = realtek_gpio_irq_init;
+ }
+
+ return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
+}
+
+static struct platform_driver realtek_gpio_driver = {
+ .driver = {
+ .name = "realtek-otto-gpio",
+ .of_match_table = realtek_gpio_of_match,
+ },
+ .probe = realtek_gpio_probe,
+};
+module_platform_driver(realtek_gpio_driver);
+
+MODULE_DESCRIPTION("Realtek Otto GPIO support");
+MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
+MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,57 @@
From 6acbd614c2c8d3b8de5fb7605d6e24b9b3a8a17b Mon Sep 17 00:00:00 2001
From: Bert Vermeulen <bert@biot.com>
Date: Wed, 20 Jan 2021 14:59:27 +0100
Subject: spi: Realtek RTL838x/RTL839x SPI controller
Signed-off-by: Bert Vermeulen <bert@biot.com>
Link: https://lore.kernel.org/r/20210120135928.246054-2-bert@biot.com
Signed-off-by: Mark Brown <broonie@kernel.org>
---
.../devicetree/bindings/spi/realtek,rtl-spi.yaml | 41 ++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/realtek,rtl-spi.yaml
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/realtek,rtl-spi.yaml
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/realtek,rtl-spi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek RTL838x/RTL839x SPI controller
+
+maintainers:
+ - Bert Vermeulen <bert@biot.com>
+ - Birger Koblitz <mail@birger-koblitz.de>
+
+allOf:
+ - $ref: "spi-controller.yaml#"
+
+properties:
+ compatible:
+ oneOf:
+ - const: realtek,rtl8380-spi
+ - const: realtek,rtl8382-spi
+ - const: realtek,rtl8391-spi
+ - const: realtek,rtl8392-spi
+ - const: realtek,rtl8393-spi
+
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ spi: spi@1200 {
+ compatible = "realtek,rtl8382-spi";
+ reg = <0x1200 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };

View file

@ -0,0 +1,248 @@
From a8af5cc2ff1e804694629a8ef320935629dd15ba Mon Sep 17 00:00:00 2001
From: Bert Vermeulen <bert@biot.com>
Date: Wed, 20 Jan 2021 14:59:28 +0100
Subject: spi: realtek-rtl: Add support for Realtek RTL838x/RTL839x SPI
controllers
This driver likely also supports earlier (RTL8196) and later (RTL93xx)
SoCs.
The SPI hardware in these SoCs is specifically intended for connecting NOR
bootflash chips, and only used for that in dozens of examined devices.
However boiled down to basics, it's really just a half-duplex SPI
controller.
The hardware appears to have a vestigial second chip-select control, but
it hasn't been seen in the wild and is thus not supported.
Signed-off-by: Bert Vermeulen <bert@biot.com>
Link: https://lore.kernel.org/r/20210120135928.246054-3-bert@biot.com
Signed-off-by: Mark Brown <broonie@kernel.org>
---
drivers/spi/Makefile | 1 +
drivers/spi/spi-realtek-rtl.c | 209 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 210 insertions(+)
create mode 100644 drivers/spi/spi-realtek-rtl.c
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -94,6 +94,7 @@ obj-$(CONFIG_SPI_QCOM_QSPI) += spi-qcom
obj-$(CONFIG_SPI_QUP) += spi-qup.o
obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
+obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o
obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
--- /dev/null
+++ b/drivers/spi/spi-realtek-rtl.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/spi/spi.h>
+
+struct rtspi {
+ void __iomem *base;
+};
+
+/* SPI Flash Configuration Register */
+#define RTL_SPI_SFCR 0x00
+#define RTL_SPI_SFCR_RBO BIT(28)
+#define RTL_SPI_SFCR_WBO BIT(27)
+
+/* SPI Flash Control and Status Register */
+#define RTL_SPI_SFCSR 0x08
+#define RTL_SPI_SFCSR_CSB0 BIT(31)
+#define RTL_SPI_SFCSR_CSB1 BIT(30)
+#define RTL_SPI_SFCSR_RDY BIT(27)
+#define RTL_SPI_SFCSR_CS BIT(24)
+#define RTL_SPI_SFCSR_LEN_MASK ~(0x03 << 28)
+#define RTL_SPI_SFCSR_LEN1 (0x00 << 28)
+#define RTL_SPI_SFCSR_LEN4 (0x03 << 28)
+
+/* SPI Flash Data Register */
+#define RTL_SPI_SFDR 0x0c
+
+#define REG(x) (rtspi->base + x)
+
+
+static void rt_set_cs(struct spi_device *spi, bool active)
+{
+ struct rtspi *rtspi = spi_controller_get_devdata(spi->controller);
+ u32 value;
+
+ /* CS0 bit is active low */
+ value = readl(REG(RTL_SPI_SFCSR));
+ if (active)
+ value |= RTL_SPI_SFCSR_CSB0;
+ else
+ value &= ~RTL_SPI_SFCSR_CSB0;
+ writel(value, REG(RTL_SPI_SFCSR));
+}
+
+static void set_size(struct rtspi *rtspi, int size)
+{
+ u32 value;
+
+ value = readl(REG(RTL_SPI_SFCSR));
+ value &= RTL_SPI_SFCSR_LEN_MASK;
+ if (size == 4)
+ value |= RTL_SPI_SFCSR_LEN4;
+ else if (size == 1)
+ value |= RTL_SPI_SFCSR_LEN1;
+ writel(value, REG(RTL_SPI_SFCSR));
+}
+
+static inline void wait_ready(struct rtspi *rtspi)
+{
+ while (!(readl(REG(RTL_SPI_SFCSR)) & RTL_SPI_SFCSR_RDY))
+ cpu_relax();
+}
+static void send4(struct rtspi *rtspi, const u32 *buf)
+{
+ wait_ready(rtspi);
+ set_size(rtspi, 4);
+ writel(*buf, REG(RTL_SPI_SFDR));
+}
+
+static void send1(struct rtspi *rtspi, const u8 *buf)
+{
+ wait_ready(rtspi);
+ set_size(rtspi, 1);
+ writel(buf[0] << 24, REG(RTL_SPI_SFDR));
+}
+
+static void rcv4(struct rtspi *rtspi, u32 *buf)
+{
+ wait_ready(rtspi);
+ set_size(rtspi, 4);
+ *buf = readl(REG(RTL_SPI_SFDR));
+}
+
+static void rcv1(struct rtspi *rtspi, u8 *buf)
+{
+ wait_ready(rtspi);
+ set_size(rtspi, 1);
+ *buf = readl(REG(RTL_SPI_SFDR)) >> 24;
+}
+
+static int transfer_one(struct spi_controller *ctrl, struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct rtspi *rtspi = spi_controller_get_devdata(ctrl);
+ void *rx_buf;
+ const void *tx_buf;
+ int cnt;
+
+ tx_buf = xfer->tx_buf;
+ rx_buf = xfer->rx_buf;
+ cnt = xfer->len;
+ if (tx_buf) {
+ while (cnt >= 4) {
+ send4(rtspi, tx_buf);
+ tx_buf += 4;
+ cnt -= 4;
+ }
+ while (cnt) {
+ send1(rtspi, tx_buf);
+ tx_buf++;
+ cnt--;
+ }
+ } else if (rx_buf) {
+ while (cnt >= 4) {
+ rcv4(rtspi, rx_buf);
+ rx_buf += 4;
+ cnt -= 4;
+ }
+ while (cnt) {
+ rcv1(rtspi, rx_buf);
+ rx_buf++;
+ cnt--;
+ }
+ }
+
+ spi_finalize_current_transfer(ctrl);
+
+ return 0;
+}
+
+static void init_hw(struct rtspi *rtspi)
+{
+ u32 value;
+
+ /* Turn on big-endian byte ordering */
+ value = readl(REG(RTL_SPI_SFCR));
+ value |= RTL_SPI_SFCR_RBO | RTL_SPI_SFCR_WBO;
+ writel(value, REG(RTL_SPI_SFCR));
+
+ value = readl(REG(RTL_SPI_SFCSR));
+ /* Permanently disable CS1, since it's never used */
+ value |= RTL_SPI_SFCSR_CSB1;
+ /* Select CS0 for use */
+ value &= RTL_SPI_SFCSR_CS;
+ writel(value, REG(RTL_SPI_SFCSR));
+}
+
+static int realtek_rtl_spi_probe(struct platform_device *pdev)
+{
+ struct spi_controller *ctrl;
+ struct rtspi *rtspi;
+ int err;
+
+ ctrl = devm_spi_alloc_master(&pdev->dev, sizeof(*rtspi));
+ if (!ctrl) {
+ dev_err(&pdev->dev, "Error allocating SPI controller\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, ctrl);
+ rtspi = spi_controller_get_devdata(ctrl);
+
+ rtspi->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(rtspi->base)) {
+ dev_err(&pdev->dev, "Could not map SPI register address");
+ return -ENOMEM;
+ }
+
+ init_hw(rtspi);
+
+ ctrl->dev.of_node = pdev->dev.of_node;
+ ctrl->flags = SPI_CONTROLLER_HALF_DUPLEX;
+ ctrl->set_cs = rt_set_cs;
+ ctrl->transfer_one = transfer_one;
+
+ err = devm_spi_register_controller(&pdev->dev, ctrl);
+ if (err) {
+ dev_err(&pdev->dev, "Could not register SPI controller\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+static const struct of_device_id realtek_rtl_spi_of_ids[] = {
+ { .compatible = "realtek,rtl8380-spi" },
+ { .compatible = "realtek,rtl8382-spi" },
+ { .compatible = "realtek,rtl8391-spi" },
+ { .compatible = "realtek,rtl8392-spi" },
+ { .compatible = "realtek,rtl8393-spi" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, realtek_rtl_spi_of_ids);
+
+static struct platform_driver realtek_rtl_spi_driver = {
+ .probe = realtek_rtl_spi_probe,
+ .driver = {
+ .name = "realtek-rtl-spi",
+ .of_match_table = realtek_rtl_spi_of_ids,
+ },
+};
+
+module_platform_driver(realtek_rtl_spi_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Bert Vermeulen <bert@biot.com>");
+MODULE_DESCRIPTION("Realtek RTL SPI driver");

View file

@ -0,0 +1,78 @@
From 4a2b92a5d3519fc2c1edda4d4aa0e05bff41e8de Mon Sep 17 00:00:00 2001
From: Bert Vermeulen <bert@biot.com>
Date: Fri, 22 Jan 2021 21:42:23 +0100
Subject: dt-bindings: interrupt-controller: Add Realtek RTL838x/RTL839x
support
Document the binding for the Realtek RTL838x/RTL839x interrupt controller.
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Bert Vermeulen <bert@biot.com>
[maz: Add a commit message, as the author couldn't be bothered...]
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20210122204224.509124-2-bert@biot.com
---
.../interrupt-controller/realtek,rtl-intc.yaml | 57 ++++++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/realtek,rtl-intc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek RTL SoC interrupt controller devicetree bindings
+
+maintainers:
+ - Birger Koblitz <mail@birger-koblitz.de>
+ - Bert Vermeulen <bert@biot.com>
+ - John Crispin <john@phrozen.org>
+
+properties:
+ compatible:
+ const: realtek,rtl-intc
+
+ "#interrupt-cells":
+ const: 1
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ interrupt-controller: true
+
+ "#address-cells":
+ const: 0
+
+ interrupt-map:
+ description: Describes mapping from SoC interrupts to CPU interrupts
+
+required:
+ - compatible
+ - reg
+ - "#interrupt-cells"
+ - interrupt-controller
+ - "#address-cells"
+ - interrupt-map
+
+additionalProperties: false
+
+examples:
+ - |
+ intc: interrupt-controller@3000 {
+ compatible = "realtek,rtl-intc";
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ reg = <0x3000 0x20>;
+ #address-cells = <0>;
+ interrupt-map =
+ <31 &cpuintc 2>,
+ <30 &cpuintc 1>,
+ <29 &cpuintc 5>;
+ };

View file

@ -0,0 +1,211 @@
From 9f3a0f34b84ad1b9a8f2bdae44b66f16685b2143 Mon Sep 17 00:00:00 2001
From: Bert Vermeulen <bert@biot.com>
Date: Fri, 22 Jan 2021 21:42:24 +0100
Subject: irqchip: Add support for Realtek RTL838x/RTL839x interrupt controller
This is a standard IRQ driver with only status and mask registers.
The mapping from SoC interrupts (18-31) to MIPS core interrupts is
done via an interrupt-map in device tree.
Signed-off-by: Bert Vermeulen <bert@biot.com>
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Acked-by: John Crispin <john@phrozen.org>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20210122204224.509124-3-bert@biot.com
---
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-realtek-rtl.c | 180 ++++++++++++++++++++++++++++++++++++++
2 files changed, 181 insertions(+)
create mode 100644 drivers/irqchip/irq-realtek-rtl.c
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -114,3 +114,4 @@ obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-l
obj-$(CONFIG_LOONGSON_PCH_MSI) += irq-loongson-pch-msi.o
obj-$(CONFIG_MST_IRQ) += irq-mst-intc.o
obj-$(CONFIG_SL28CPLD_INTC) += irq-sl28cpld.o
+obj-$(CONFIG_MACH_REALTEK_RTL) += irq-realtek-rtl.o
--- /dev/null
+++ b/drivers/irqchip/irq-realtek-rtl.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 Birger Koblitz <mail@birger-koblitz.de>
+ * Copyright (C) 2020 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2020 John Crispin <john@phrozen.org>
+ */
+
+#include <linux/of_irq.h>
+#include <linux/irqchip.h>
+#include <linux/spinlock.h>
+#include <linux/of_address.h>
+#include <linux/irqchip/chained_irq.h>
+
+/* Global Interrupt Mask Register */
+#define RTL_ICTL_GIMR 0x00
+/* Global Interrupt Status Register */
+#define RTL_ICTL_GISR 0x04
+/* Interrupt Routing Registers */
+#define RTL_ICTL_IRR0 0x08
+#define RTL_ICTL_IRR1 0x0c
+#define RTL_ICTL_IRR2 0x10
+#define RTL_ICTL_IRR3 0x14
+
+#define REG(x) (realtek_ictl_base + x)
+
+static DEFINE_RAW_SPINLOCK(irq_lock);
+static void __iomem *realtek_ictl_base;
+
+static void realtek_ictl_unmask_irq(struct irq_data *i)
+{
+ unsigned long flags;
+ u32 value;
+
+ raw_spin_lock_irqsave(&irq_lock, flags);
+
+ value = readl(REG(RTL_ICTL_GIMR));
+ value |= BIT(i->hwirq);
+ writel(value, REG(RTL_ICTL_GIMR));
+
+ raw_spin_unlock_irqrestore(&irq_lock, flags);
+}
+
+static void realtek_ictl_mask_irq(struct irq_data *i)
+{
+ unsigned long flags;
+ u32 value;
+
+ raw_spin_lock_irqsave(&irq_lock, flags);
+
+ value = readl(REG(RTL_ICTL_GIMR));
+ value &= ~BIT(i->hwirq);
+ writel(value, REG(RTL_ICTL_GIMR));
+
+ raw_spin_unlock_irqrestore(&irq_lock, flags);
+}
+
+static struct irq_chip realtek_ictl_irq = {
+ .name = "realtek-rtl-intc",
+ .irq_mask = realtek_ictl_mask_irq,
+ .irq_unmask = realtek_ictl_unmask_irq,
+};
+
+static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(hw, &realtek_ictl_irq, handle_level_irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops irq_domain_ops = {
+ .map = intc_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static void realtek_irq_dispatch(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_domain *domain;
+ unsigned int pending;
+
+ chained_irq_enter(chip, desc);
+ pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR));
+ if (unlikely(!pending)) {
+ spurious_interrupt();
+ goto out;
+ }
+ domain = irq_desc_get_handler_data(desc);
+ generic_handle_irq(irq_find_mapping(domain, __ffs(pending)));
+
+out:
+ chained_irq_exit(chip, desc);
+}
+
+/*
+ * SoC interrupts are cascaded to MIPS CPU interrupts according to the
+ * interrupt-map in the device tree. Each SoC interrupt gets 4 bits for
+ * the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts
+ * thus go into 4 IRRs.
+ */
+static int __init map_interrupts(struct device_node *node, struct irq_domain *domain)
+{
+ struct device_node *cpu_ictl;
+ const __be32 *imap;
+ u32 imaplen, soc_int, cpu_int, tmp, regs[4];
+ int ret, i, irr_regs[] = {
+ RTL_ICTL_IRR3,
+ RTL_ICTL_IRR2,
+ RTL_ICTL_IRR1,
+ RTL_ICTL_IRR0,
+ };
+ u8 mips_irqs_set;
+
+ ret = of_property_read_u32(node, "#address-cells", &tmp);
+ if (ret || tmp)
+ return -EINVAL;
+
+ imap = of_get_property(node, "interrupt-map", &imaplen);
+ if (!imap || imaplen % 3)
+ return -EINVAL;
+
+ mips_irqs_set = 0;
+ memset(regs, 0, sizeof(regs));
+ for (i = 0; i < imaplen; i += 3 * sizeof(u32)) {
+ soc_int = be32_to_cpup(imap);
+ if (soc_int > 31)
+ return -EINVAL;
+
+ cpu_ictl = of_find_node_by_phandle(be32_to_cpup(imap + 1));
+ if (!cpu_ictl)
+ return -EINVAL;
+ ret = of_property_read_u32(cpu_ictl, "#interrupt-cells", &tmp);
+ if (ret || tmp != 1)
+ return -EINVAL;
+ of_node_put(cpu_ictl);
+
+ cpu_int = be32_to_cpup(imap + 2);
+ if (cpu_int > 7)
+ return -EINVAL;
+
+ if (!(mips_irqs_set & BIT(cpu_int))) {
+ irq_set_chained_handler_and_data(cpu_int, realtek_irq_dispatch,
+ domain);
+ mips_irqs_set |= BIT(cpu_int);
+ }
+
+ regs[(soc_int * 4) / 32] |= cpu_int << (soc_int * 4) % 32;
+ imap += 3;
+ }
+
+ for (i = 0; i < 4; i++)
+ writel(regs[i], REG(irr_regs[i]));
+
+ return 0;
+}
+
+static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent)
+{
+ struct irq_domain *domain;
+ int ret;
+
+ realtek_ictl_base = of_iomap(node, 0);
+ if (!realtek_ictl_base)
+ return -ENXIO;
+
+ /* Disable all cascaded interrupts */
+ writel(0, REG(RTL_ICTL_GIMR));
+
+ domain = irq_domain_add_simple(node, 32, 0,
+ &irq_domain_ops, NULL);
+
+ ret = map_interrupts(node, domain);
+ if (ret) {
+ pr_err("invalid interrupt map\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc", realtek_rtl_of_init);

View file

@ -0,0 +1,22 @@
From: Sander Vanheule <sander@svanheule.net>
Subject: gpio: realtek-otto: fix GPIO line IRQ offset
The irqchip uses one domain for all GPIO lines, so th line offset should be
determined w.r.t. the first line of the first port, not the first line of the
triggered port.
Fixes: 0d82fb1127fb ("gpio: Add Realtek Otto GPIO support")
Signed-off-by: Sander Vanheule <sander@svanheule.net>
Link: https://lore.kernel.org/linux-gpio/20211028085243.34360-1-sander@svanheule.net/
--- a/drivers/gpio/gpio-realtek-otto.c
+++ b/drivers/gpio/gpio-realtek-otto.c
@@ -206,7 +206,7 @@ static void realtek_gpio_irq_handler(str
status = realtek_gpio_read_isr(ctrl, lines_done / 8);
port_pin_count = min(gc->ngpio - lines_done, 8U);
for_each_set_bit(offset, &status, port_pin_count) {
- irq = irq_find_mapping(gc->irq.domain, offset);
+ irq = irq_find_mapping(gc->irq.domain, offset + lines_done);
generic_handle_irq(irq);
}
}

View file

@ -0,0 +1,467 @@
From 293903b9dfe43520f01374dc1661be11d6838c49 Mon Sep 17 00:00:00 2001
From: Sander Vanheule <sander@svanheule.net>
Date: Thu, 18 Nov 2021 17:29:52 +0100
Subject: watchdog: Add Realtek Otto watchdog timer
Realtek MIPS SoCs (platform name Otto) have a watchdog timer with
pretimeout notifitication support. The WDT can (partially) hard reset,
or soft reset the SoC.
This driver implements all features as described in the devicetree
binding, except the phase2 interrupt, and also functions as a restart
handler. The cpu reset mode is considered to be a "warm" restart, since
this mode does not reset all peripherals. Being an embedded system
though, the "cpu" and "software" modes will still cause the bootloader
to run on restart.
It is not known how a forced system reset can be disabled on the
supported platforms. This means that the phase2 interrupt will only fire
at the same time as reset, so implementing phase2 is of little use.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/6d060bccbdcc709cfa79203485db85aad3c3beb5.1637252610.git.sander@svanheule.net
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
MAINTAINERS | 7 +
drivers/watchdog/Kconfig | 13 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/realtek_otto_wdt.c | 384 ++++++++++++++++++++++++++++++++++++
4 files changed, 405 insertions(+)
create mode 100644 drivers/watchdog/realtek_otto_wdt.c
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14816,6 +14816,13 @@ S: Maintained
F: include/sound/rt*.h
F: sound/soc/codecs/rt*
+REALTEK OTTO WATCHDOG
+M: Sander Vanheule <sander@svanheule.net>
+L: linux-watchdog@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
+F: driver/watchdog/realtek_otto_wdt.c
+
REALTEK RTL83xx SMI DSA ROUTER CHIPS
M: Linus Walleij <linus.walleij@linaro.org>
S: Maintained
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -995,6 +995,19 @@ config RTD119X_WATCHDOG
Say Y here to include support for the watchdog timer in
Realtek RTD1295 SoCs.
+config REALTEK_OTTO_WDT
+ tristate "Realtek Otto MIPS watchdog support"
+ depends on MACH_REALTEK_RTL || COMPILE_TEST
+ depends on COMMON_CLK
+ select WATCHDOG_CORE
+ default MACH_REALTEK_RTL
+ help
+ Say Y here to include support for the watchdog timer on Realtek
+ RTL838x, RTL839x, RTL930x SoCs. This watchdog has pretimeout
+ notifications and system reset on timeout.
+
+ When built as a module this will be called realtek_otto_wdt.
+
config SPRD_WATCHDOG
tristate "Spreadtrum watchdog support"
depends on ARCH_SPRD || COMPILE_TEST
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -174,6 +174,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o
obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o
obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o
obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o
+obj-$(CONFIG_REALTEK_OTTO_WDT) += realtek_otto_wdt.o
# PARISC Architecture
--- /dev/null
+++ b/drivers/watchdog/realtek_otto_wdt.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Realtek Otto MIPS platform watchdog
+ *
+ * Watchdog timer that will reset the system after timeout, using the selected
+ * reset mode.
+ *
+ * Counter scaling and timeouts:
+ * - Base prescale of (2 << 25), providing tick duration T_0: 168ms @ 200MHz
+ * - PRESCALE: logarithmic prescaler adding a factor of {1, 2, 4, 8}
+ * - Phase 1: Times out after (PHASE1 + 1) × PRESCALE × T_0
+ * Generates an interrupt, WDT cannot be stopped after phase 1
+ * - Phase 2: starts after phase 1, times out after (PHASE2 + 1) × PRESCALE × T_0
+ * Resets the system according to RST_MODE
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+
+#define OTTO_WDT_REG_CNTR 0x0
+#define OTTO_WDT_CNTR_PING BIT(31)
+
+#define OTTO_WDT_REG_INTR 0x4
+#define OTTO_WDT_INTR_PHASE_1 BIT(31)
+#define OTTO_WDT_INTR_PHASE_2 BIT(30)
+
+#define OTTO_WDT_REG_CTRL 0x8
+#define OTTO_WDT_CTRL_ENABLE BIT(31)
+#define OTTO_WDT_CTRL_PRESCALE GENMASK(30, 29)
+#define OTTO_WDT_CTRL_PHASE1 GENMASK(26, 22)
+#define OTTO_WDT_CTRL_PHASE2 GENMASK(19, 15)
+#define OTTO_WDT_CTRL_RST_MODE GENMASK(1, 0)
+#define OTTO_WDT_MODE_SOC 0
+#define OTTO_WDT_MODE_CPU 1
+#define OTTO_WDT_MODE_SOFTWARE 2
+#define OTTO_WDT_CTRL_DEFAULT OTTO_WDT_MODE_CPU
+
+#define OTTO_WDT_PRESCALE_MAX 3
+
+/*
+ * One higher than the max values contained in PHASE{1,2}, since a value of 0
+ * corresponds to one tick.
+ */
+#define OTTO_WDT_PHASE_TICKS_MAX 32
+
+/*
+ * The maximum reset delay is actually 2×32 ticks, but that would require large
+ * pretimeout values for timeouts longer than 32 ticks. Limit the maximum timeout
+ * to 32 + 1 to ensure small pretimeout values can be configured as expected.
+ */
+#define OTTO_WDT_TIMEOUT_TICKS_MAX (OTTO_WDT_PHASE_TICKS_MAX + 1)
+
+struct otto_wdt_ctrl {
+ struct watchdog_device wdev;
+ struct device *dev;
+ void __iomem *base;
+ unsigned int clk_rate_khz;
+ int irq_phase1;
+};
+
+static int otto_wdt_start(struct watchdog_device *wdev)
+{
+ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+ u32 v;
+
+ v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+ v |= OTTO_WDT_CTRL_ENABLE;
+ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+ return 0;
+}
+
+static int otto_wdt_stop(struct watchdog_device *wdev)
+{
+ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+ u32 v;
+
+ v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+ v &= ~OTTO_WDT_CTRL_ENABLE;
+ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+ return 0;
+}
+
+static int otto_wdt_ping(struct watchdog_device *wdev)
+{
+ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+
+ iowrite32(OTTO_WDT_CNTR_PING, ctrl->base + OTTO_WDT_REG_CNTR);
+
+ return 0;
+}
+
+static int otto_wdt_tick_ms(struct otto_wdt_ctrl *ctrl, int prescale)
+{
+ return DIV_ROUND_CLOSEST(1 << (25 + prescale), ctrl->clk_rate_khz);
+}
+
+/*
+ * The timer asserts the PHASE1/PHASE2 IRQs when the number of ticks exceeds
+ * the value stored in those fields. This means each phase will run for at least
+ * one tick, so small values need to be clamped to correctly reflect the timeout.
+ */
+static inline unsigned int div_round_ticks(unsigned int val, unsigned int tick_duration,
+ unsigned int min_ticks)
+{
+ return max(min_ticks, DIV_ROUND_UP(val, tick_duration));
+}
+
+static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned int timeout,
+ unsigned int pretimeout)
+{
+ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+ unsigned int pretimeout_ms = pretimeout * 1000;
+ unsigned int timeout_ms = timeout * 1000;
+ unsigned int prescale_next = 0;
+ unsigned int phase1_ticks;
+ unsigned int phase2_ticks;
+ unsigned int total_ticks;
+ unsigned int prescale;
+ unsigned int tick_ms;
+ u32 v;
+
+ do {
+ prescale = prescale_next;
+ if (prescale > OTTO_WDT_PRESCALE_MAX)
+ return -EINVAL;
+
+ tick_ms = otto_wdt_tick_ms(ctrl, prescale);
+ total_ticks = div_round_ticks(timeout_ms, tick_ms, 2);
+ phase1_ticks = div_round_ticks(timeout_ms - pretimeout_ms, tick_ms, 1);
+ phase2_ticks = total_ticks - phase1_ticks;
+
+ prescale_next++;
+ } while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX
+ || phase2_ticks > OTTO_WDT_PHASE_TICKS_MAX);
+
+ v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+
+ v &= ~(OTTO_WDT_CTRL_PRESCALE | OTTO_WDT_CTRL_PHASE1 | OTTO_WDT_CTRL_PHASE2);
+ v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE1, phase1_ticks - 1);
+ v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE2, phase2_ticks - 1);
+ v |= FIELD_PREP(OTTO_WDT_CTRL_PRESCALE, prescale);
+
+ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+ timeout_ms = total_ticks * tick_ms;
+ ctrl->wdev.timeout = timeout_ms / 1000;
+
+ pretimeout_ms = phase2_ticks * tick_ms;
+ ctrl->wdev.pretimeout = pretimeout_ms / 1000;
+
+ return 0;
+}
+
+static int otto_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
+{
+ return otto_wdt_determine_timeouts(wdev, val, min(wdev->pretimeout, val - 1));
+}
+
+static int otto_wdt_set_pretimeout(struct watchdog_device *wdev, unsigned int val)
+{
+ return otto_wdt_determine_timeouts(wdev, wdev->timeout, val);
+}
+
+static int otto_wdt_restart(struct watchdog_device *wdev, unsigned long reboot_mode,
+ void *data)
+{
+ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+ u32 reset_mode;
+ u32 v;
+
+ disable_irq(ctrl->irq_phase1);
+
+ switch (reboot_mode) {
+ case REBOOT_SOFT:
+ reset_mode = OTTO_WDT_MODE_SOFTWARE;
+ break;
+ case REBOOT_WARM:
+ reset_mode = OTTO_WDT_MODE_CPU;
+ break;
+ default:
+ reset_mode = OTTO_WDT_MODE_SOC;
+ break;
+ }
+
+ /* Configure for shortest timeout and wait for reset to occur */
+ v = FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, reset_mode) | OTTO_WDT_CTRL_ENABLE;
+ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+ mdelay(3 * otto_wdt_tick_ms(ctrl, 0));
+
+ return 0;
+}
+
+static irqreturn_t otto_wdt_phase1_isr(int irq, void *dev_id)
+{
+ struct otto_wdt_ctrl *ctrl = dev_id;
+
+ iowrite32(OTTO_WDT_INTR_PHASE_1, ctrl->base + OTTO_WDT_REG_INTR);
+ dev_crit(ctrl->dev, "phase 1 timeout\n");
+ watchdog_notify_pretimeout(&ctrl->wdev);
+
+ return IRQ_HANDLED;
+}
+
+static const struct watchdog_ops otto_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = otto_wdt_start,
+ .stop = otto_wdt_stop,
+ .ping = otto_wdt_ping,
+ .set_timeout = otto_wdt_set_timeout,
+ .set_pretimeout = otto_wdt_set_pretimeout,
+ .restart = otto_wdt_restart,
+};
+
+static const struct watchdog_info otto_wdt_info = {
+ .identity = "Realtek Otto watchdog timer",
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE |
+ WDIOF_SETTIMEOUT |
+ WDIOF_PRETIMEOUT,
+};
+
+static void otto_wdt_clock_action(void *data)
+{
+ clk_disable_unprepare(data);
+}
+
+static int otto_wdt_probe_clk(struct otto_wdt_ctrl *ctrl)
+{
+ struct clk *clk = devm_clk_get(ctrl->dev, NULL);
+ int ret;
+
+ if (IS_ERR(clk))
+ return dev_err_probe(ctrl->dev, PTR_ERR(clk), "Failed to get clock\n");
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return dev_err_probe(ctrl->dev, ret, "Failed to enable clock\n");
+
+ ret = devm_add_action_or_reset(ctrl->dev, otto_wdt_clock_action, clk);
+ if (ret)
+ return ret;
+
+ ctrl->clk_rate_khz = clk_get_rate(clk) / 1000;
+ if (ctrl->clk_rate_khz == 0)
+ return dev_err_probe(ctrl->dev, -ENXIO, "Failed to get clock rate\n");
+
+ return 0;
+}
+
+static int otto_wdt_probe_reset_mode(struct otto_wdt_ctrl *ctrl)
+{
+ static const char *mode_property = "realtek,reset-mode";
+ const struct fwnode_handle *node = ctrl->dev->fwnode;
+ int mode_count;
+ u32 mode;
+ u32 v;
+
+ if (!node)
+ return -ENXIO;
+
+ mode_count = fwnode_property_string_array_count(node, mode_property);
+ if (mode_count < 0)
+ return mode_count;
+ else if (mode_count == 0)
+ return 0;
+ else if (mode_count != 1)
+ return -EINVAL;
+
+ if (fwnode_property_match_string(node, mode_property, "soc") == 0)
+ mode = OTTO_WDT_MODE_SOC;
+ else if (fwnode_property_match_string(node, mode_property, "cpu") == 0)
+ mode = OTTO_WDT_MODE_CPU;
+ else if (fwnode_property_match_string(node, mode_property, "software") == 0)
+ mode = OTTO_WDT_MODE_SOFTWARE;
+ else
+ return -EINVAL;
+
+ v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+ v &= ~OTTO_WDT_CTRL_RST_MODE;
+ v |= FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, mode);
+ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+ return 0;
+}
+
+static int otto_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct otto_wdt_ctrl *ctrl;
+ unsigned int max_tick_ms;
+ int ret;
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ ctrl->dev = dev;
+ ctrl->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ctrl->base))
+ return PTR_ERR(ctrl->base);
+
+ /* Clear any old interrupts and reset initial state */
+ iowrite32(OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2,
+ ctrl->base + OTTO_WDT_REG_INTR);
+ iowrite32(OTTO_WDT_CTRL_DEFAULT, ctrl->base + OTTO_WDT_REG_CTRL);
+
+ ret = otto_wdt_probe_clk(ctrl);
+ if (ret)
+ return ret;
+
+ ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1");
+ if (ctrl->irq_phase1 < 0)
+ return ctrl->irq_phase1;
+
+ ret = devm_request_irq(dev, ctrl->irq_phase1, otto_wdt_phase1_isr, 0,
+ "realtek-otto-wdt", ctrl);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get IRQ for phase1\n");
+
+ ret = otto_wdt_probe_reset_mode(ctrl);
+ if (ret)
+ return dev_err_probe(dev, ret, "Invalid reset mode specified\n");
+
+ ctrl->wdev.parent = dev;
+ ctrl->wdev.info = &otto_wdt_info;
+ ctrl->wdev.ops = &otto_wdt_ops;
+
+ /*
+ * Since pretimeout cannot be disabled, min. timeout is twice the
+ * subsystem resolution. Max. timeout is ca. 43s at a bus clock of 200MHz.
+ */
+ ctrl->wdev.min_timeout = 2;
+ max_tick_ms = otto_wdt_tick_ms(ctrl, OTTO_WDT_PRESCALE_MAX);
+ ctrl->wdev.max_hw_heartbeat_ms = max_tick_ms * OTTO_WDT_TIMEOUT_TICKS_MAX;
+ ctrl->wdev.timeout = min(30U, ctrl->wdev.max_hw_heartbeat_ms / 1000);
+
+ watchdog_set_drvdata(&ctrl->wdev, ctrl);
+ watchdog_init_timeout(&ctrl->wdev, 0, dev);
+ watchdog_stop_on_reboot(&ctrl->wdev);
+ watchdog_set_restart_priority(&ctrl->wdev, 128);
+
+ ret = otto_wdt_determine_timeouts(&ctrl->wdev, ctrl->wdev.timeout, 1);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set timeout\n");
+
+ return devm_watchdog_register_device(dev, &ctrl->wdev);
+}
+
+static const struct of_device_id otto_wdt_ids[] = {
+ { .compatible = "realtek,rtl8380-wdt" },
+ { .compatible = "realtek,rtl8390-wdt" },
+ { .compatible = "realtek,rtl9300-wdt" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, otto_wdt_ids);
+
+static struct platform_driver otto_wdt_driver = {
+ .probe = otto_wdt_probe,
+ .driver = {
+ .name = "realtek-otto-watchdog",
+ .of_match_table = otto_wdt_ids,
+ },
+};
+module_platform_driver(otto_wdt_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
+MODULE_DESCRIPTION("Realtek Otto watchdog timer driver");

View file

@ -0,0 +1,53 @@
From c6af53f038aa32cec12e8a305ba07c7ef168f1b0 Mon Sep 17 00:00:00 2001
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
Date: Tue, 4 Jan 2022 12:07:00 +0000
Subject: [PATCH 2/3] net: mdio: add helpers to extract clause 45 regad and
devad fields
Add a couple of helpers and definitions to extract the clause 45 regad
and devad fields from the regnum passed into MDIO drivers.
Tested-by: Daniel Golle <daniel@makrotopia.org>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
---
include/linux/mdio.h | 12 ++++++++++++
1 file changed, 12 insertions(+)
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -7,6 +7,7 @@
#define __LINUX_MDIO_H__
#include <uapi/linux/mdio.h>
+#include <linux/bitfield.h>
#include <linux/mod_devicetable.h>
/* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit
@@ -14,6 +15,7 @@
*/
#define MII_ADDR_C45 (1<<30)
#define MII_DEVADDR_C45_SHIFT 16
+#define MII_DEVADDR_C45_MASK GENMASK(20, 16)
#define MII_REGADDR_C45_MASK GENMASK(15, 0)
struct gpio_desc;
@@ -342,6 +344,16 @@ static inline u32 mdiobus_c45_addr(int d
return MII_ADDR_C45 | devad << MII_DEVADDR_C45_SHIFT | regnum;
}
+static inline u16 mdiobus_c45_regad(u32 regnum)
+{
+ return FIELD_GET(MII_REGADDR_C45_MASK, regnum);
+}
+
+static inline u16 mdiobus_c45_devad(u32 regnum)
+{
+ return FIELD_GET(MII_DEVADDR_C45_MASK, regnum);
+}
+
static inline int __mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
u16 regnum)
{

View file

@ -0,0 +1,123 @@
From 512c5be35223d9baa2629efa1084cf5210eaee80 Mon Sep 17 00:00:00 2001
From: Sander Vanheule <sander@svanheule.net>
Date: Sat, 9 Apr 2022 21:55:47 +0200
Subject: [PATCH 2/6] gpio: realtek-otto: Support reversed port layouts
The GPIO port layout on the RTL930x SoC series is reversed compared to
the RTL838x and RTL839x SoC series. Add new port offset calculator
functions to ensure the correct order is used when reading port IRQ
data, and ensure bgpio uses the right byte ordering.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
---
drivers/gpio/gpio-realtek-otto.c | 55 +++++++++++++++++++++++++++++---
1 file changed, 51 insertions(+), 4 deletions(-)
--- a/drivers/gpio/gpio-realtek-otto.c
+++ b/drivers/gpio/gpio-realtek-otto.c
@@ -58,6 +58,8 @@ struct realtek_gpio_ctrl {
raw_spinlock_t lock;
u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK];
u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK];
+ unsigned int (*port_offset_u8)(unsigned int port);
+ unsigned int (*port_offset_u16)(unsigned int port);
};
/* Expand with more flags as devices with other quirks are added */
@@ -69,6 +71,11 @@ enum realtek_gpio_flags {
* line the IRQ handler was assigned to, causing uncaught interrupts.
*/
GPIO_INTERRUPTS_DISABLED = BIT(0),
+ /*
+ * Port order is reversed, meaning DCBA register layout for 1-bit
+ * fields, and [BA, DC] for 2-bit fields.
+ */
+ GPIO_PORTS_REVERSED = BIT(1),
};
static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data)
@@ -86,21 +93,50 @@ static struct realtek_gpio_ctrl *irq_dat
* port. The two interrupt mask registers store two bits per GPIO, so use u16
* values.
*/
+static unsigned int realtek_gpio_port_offset_u8(unsigned int port)
+{
+ return port;
+}
+
+static unsigned int realtek_gpio_port_offset_u16(unsigned int port)
+{
+ return 2 * port;
+}
+
+/*
+ * Reversed port order register access
+ *
+ * For registers with one bit per GPIO, all ports are stored as u8-s in one
+ * register in reversed order. The two interrupt mask registers store two bits
+ * per GPIO, so use u16 values. The first register contains ports 1 and 0, the
+ * second ports 3 and 2.
+ */
+static unsigned int realtek_gpio_port_offset_u8_rev(unsigned int port)
+{
+ return 3 - port;
+}
+
+static unsigned int realtek_gpio_port_offset_u16_rev(unsigned int port)
+{
+ return 2 * (port ^ 1);
+}
+
static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl,
unsigned int port, u16 irq_type, u16 irq_mask)
{
- iowrite16(irq_type & irq_mask, ctrl->base + REALTEK_GPIO_REG_IMR + 2 * port);
+ iowrite16(irq_type & irq_mask,
+ ctrl->base + REALTEK_GPIO_REG_IMR + ctrl->port_offset_u16(port));
}
static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl,
unsigned int port, u8 mask)
{
- iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + port);
+ iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port));
}
static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port)
{
- return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + port);
+ return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port));
}
/* Set the rising and falling edge mask bits for a GPIO port pin */
@@ -253,6 +289,7 @@ MODULE_DEVICE_TABLE(of, realtek_gpio_of_
static int realtek_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ unsigned long bgpio_flags;
unsigned int dev_flags;
struct gpio_irq_chip *girq;
struct realtek_gpio_ctrl *ctrl;
@@ -280,10 +317,20 @@ static int realtek_gpio_probe(struct pla
raw_spin_lock_init(&ctrl->lock);
+ if (dev_flags & GPIO_PORTS_REVERSED) {
+ bgpio_flags = 0;
+ ctrl->port_offset_u8 = realtek_gpio_port_offset_u8_rev;
+ ctrl->port_offset_u16 = realtek_gpio_port_offset_u16_rev;
+ } else {
+ bgpio_flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER;
+ ctrl->port_offset_u8 = realtek_gpio_port_offset_u8;
+ ctrl->port_offset_u16 = realtek_gpio_port_offset_u16;
+ }
+
err = bgpio_init(&ctrl->gc, dev, 4,
ctrl->base + REALTEK_GPIO_REG_DATA, NULL, NULL,
ctrl->base + REALTEK_GPIO_REG_DIR, NULL,
- BGPIOF_BIG_ENDIAN_BYTE_ORDER);
+ bgpio_flags);
if (err) {
dev_err(dev, "unable to init generic GPIO");
return err;

View file

@ -0,0 +1,153 @@
From 95fa6dbe58f286a8f87cb37b7516232eb678de2d Mon Sep 17 00:00:00 2001
From: Sander Vanheule <sander@svanheule.net>
Date: Sat, 9 Apr 2022 21:55:48 +0200
Subject: [PATCH 3/6] gpio: realtek-otto: Support per-cpu interrupts
On SoCs with multiple cores, it is possible that the GPIO interrupt
controller supports assigning specific pins to one or more cores.
IRQ balancing can be performed on a line-by-line basis if the parent
interrupt is routed to all available cores, which is the default upon
initialisation.
Signed-off-by: Sander Vanheule <sander@svanheule.net>
Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
---
drivers/gpio/gpio-realtek-otto.c | 75 +++++++++++++++++++++++++++++++-
1 file changed, 74 insertions(+), 1 deletion(-)
--- a/drivers/gpio/gpio-realtek-otto.c
+++ b/drivers/gpio/gpio-realtek-otto.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/gpio/driver.h>
+#include <linux/cpumask.h>
#include <linux/irq.h>
#include <linux/minmax.h>
#include <linux/mod_devicetable.h>
@@ -55,6 +56,8 @@
struct realtek_gpio_ctrl {
struct gpio_chip gc;
void __iomem *base;
+ void __iomem *cpumask_base;
+ struct cpumask cpu_irq_maskable;
raw_spinlock_t lock;
u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK];
u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK];
@@ -76,6 +79,11 @@ enum realtek_gpio_flags {
* fields, and [BA, DC] for 2-bit fields.
*/
GPIO_PORTS_REVERSED = BIT(1),
+ /*
+ * Interrupts can be enabled per cpu. This requires a secondary IO
+ * range, where the per-cpu enable masks are located.
+ */
+ GPIO_INTERRUPTS_PER_CPU = BIT(2),
};
static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data)
@@ -250,14 +258,61 @@ static void realtek_gpio_irq_handler(str
chained_irq_exit(irq_chip, desc);
}
+static inline void __iomem *realtek_gpio_irq_cpu_mask(struct realtek_gpio_ctrl *ctrl,
+ unsigned int port, int cpu)
+{
+ return ctrl->cpumask_base + ctrl->port_offset_u8(port) +
+ REALTEK_GPIO_PORTS_PER_BANK * cpu;
+}
+
+static int realtek_gpio_irq_set_affinity(struct irq_data *data,
+ const struct cpumask *dest, bool force)
+{
+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data);
+ unsigned int line = irqd_to_hwirq(data);
+ unsigned int port = line / 8;
+ unsigned int port_pin = line % 8;
+ void __iomem *irq_cpu_mask;
+ unsigned long flags;
+ int cpu;
+ u8 v;
+
+ if (!ctrl->cpumask_base)
+ return -ENXIO;
+
+ raw_spin_lock_irqsave(&ctrl->lock, flags);
+
+ for_each_cpu(cpu, &ctrl->cpu_irq_maskable) {
+ irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, port, cpu);
+ v = ioread8(irq_cpu_mask);
+
+ if (cpumask_test_cpu(cpu, dest))
+ v |= BIT(port_pin);
+ else
+ v &= ~BIT(port_pin);
+
+ iowrite8(v, irq_cpu_mask);
+ }
+
+ raw_spin_unlock_irqrestore(&ctrl->lock, flags);
+
+ irq_data_update_effective_affinity(data, dest);
+
+ return 0;
+}
+
static int realtek_gpio_irq_init(struct gpio_chip *gc)
{
struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);
unsigned int port;
+ int cpu;
for (port = 0; (port * 8) < gc->ngpio; port++) {
realtek_gpio_write_imr(ctrl, port, 0, 0);
realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0));
+
+ for_each_cpu(cpu, &ctrl->cpu_irq_maskable)
+ iowrite8(GENMASK(7, 0), realtek_gpio_irq_cpu_mask(ctrl, port, cpu));
}
return 0;
@@ -269,6 +324,7 @@ static struct irq_chip realtek_gpio_irq_
.irq_mask = realtek_gpio_irq_mask,
.irq_unmask = realtek_gpio_irq_unmask,
.irq_set_type = realtek_gpio_irq_set_type,
+ .irq_set_affinity = realtek_gpio_irq_set_affinity,
};
static const struct of_device_id realtek_gpio_of_match[] = {
@@ -293,8 +349,10 @@ static int realtek_gpio_probe(struct pla
unsigned int dev_flags;
struct gpio_irq_chip *girq;
struct realtek_gpio_ctrl *ctrl;
+ struct resource *res;
u32 ngpios;
- int err, irq;
+ unsigned int nr_cpus;
+ int cpu, err, irq;
ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
@@ -355,6 +413,21 @@ static int realtek_gpio_probe(struct pla
girq->init_hw = realtek_gpio_irq_init;
}
+ cpumask_clear(&ctrl->cpu_irq_maskable);
+
+ if ((dev_flags & GPIO_INTERRUPTS_PER_CPU) && irq > 0) {
+ ctrl->cpumask_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
+ if (IS_ERR(ctrl->cpumask_base))
+ return dev_err_probe(dev, PTR_ERR(ctrl->cpumask_base),
+ "missing CPU IRQ mask registers");
+
+ nr_cpus = resource_size(res) / REALTEK_GPIO_PORTS_PER_BANK;
+ nr_cpus = min(nr_cpus, num_present_cpus());
+
+ for (cpu = 0; cpu < nr_cpus; cpu++)
+ cpumask_set_cpu(cpu, &ctrl->cpu_irq_maskable);
+ }
+
return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
}

Some files were not shown because too many files have changed in this diff Show more