Initial commit
Some checks failed
Build Kernel / Build all affected Kernels (push) Has been cancelled
Build all core packages / Build all core packages for selected target (push) Has been cancelled
Build and Push prebuilt tools container / Build and Push all prebuilt containers (push) Has been cancelled
Build Toolchains / Build Toolchains for each target (push) Has been cancelled
Build host tools / Build host tools for linux and macos based systems (push) Has been cancelled
Coverity scan build / Coverity x86/64 build (push) Has been cancelled
Some checks failed
Build Kernel / Build all affected Kernels (push) Has been cancelled
Build all core packages / Build all core packages for selected target (push) Has been cancelled
Build and Push prebuilt tools container / Build and Push all prebuilt containers (push) Has been cancelled
Build Toolchains / Build Toolchains for each target (push) Has been cancelled
Build host tools / Build host tools for linux and macos based systems (push) Has been cancelled
Coverity scan build / Coverity x86/64 build (push) Has been cancelled
This commit is contained in:
@@ -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
|
||||
@@ -15903,6 +15903,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
|
||||
@@ -954,6 +954,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
|
||||
@@ -171,6 +171,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");
|
||||
@@ -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;
|
||||
@@ -355,6 +357,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)
|
||||
{
|
||||
@@ -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 */
|
||||
@@ -250,6 +286,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;
|
||||
@@ -277,10 +314,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;
|
||||
@@ -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)
|
||||
@@ -247,14 +255,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;
|
||||
@@ -266,6 +321,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[] = {
|
||||
@@ -290,8 +346,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)
|
||||
@@ -352,6 +410,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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
From deaf1cecdeb052cdb5e92fd642016198724b44a4 Mon Sep 17 00:00:00 2001
|
||||
From: Sander Vanheule <sander@svanheule.net>
|
||||
Date: Sat, 9 Apr 2022 21:55:49 +0200
|
||||
Subject: [PATCH 4/6] gpio: realtek-otto: Add RTL930x support
|
||||
|
||||
The RTL930x SoC series has support for 24 GPIOs, with the port order
|
||||
reversed compared to RTL838x and RTL839x. The RTL930x series also has
|
||||
two CPUs (VPEs) and can distribute individual GPIO interrupts between
|
||||
them.
|
||||
|
||||
Signed-off-by: Sander Vanheule <sander@svanheule.net>
|
||||
Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
|
||||
---
|
||||
drivers/gpio/gpio-realtek-otto.c | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
--- a/drivers/gpio/gpio-realtek-otto.c
|
||||
+++ b/drivers/gpio/gpio-realtek-otto.c
|
||||
@@ -335,6 +335,10 @@ static const struct of_device_id realtek
|
||||
{
|
||||
.compatible = "realtek,rtl8390-gpio",
|
||||
},
|
||||
+ {
|
||||
+ .compatible = "realtek,rtl9300-gpio",
|
||||
+ .data = (void *)(GPIO_PORTS_REVERSED | GPIO_INTERRUPTS_PER_CPU)
|
||||
+ },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, realtek_gpio_of_match);
|
||||
@@ -0,0 +1,30 @@
|
||||
From d3bf3dc4bbbf6109bd9b4bd60089d36205ec4a37 Mon Sep 17 00:00:00 2001
|
||||
From: Sander Vanheule <sander@svanheule.net>
|
||||
Date: Sat, 9 Apr 2022 21:55:51 +0200
|
||||
Subject: [PATCH 6/6] gpio: realtek-otto: Add RTL931x support
|
||||
|
||||
The RTL931x SoC series has support for 32 GPIOs, although not all lines
|
||||
may be broken out to a physical pad.
|
||||
|
||||
The GPIO bank's parent interrupt can be routed to either or both of the
|
||||
SoC's CPU cores by the GIC. Line-by-line IRQ balancing is not possible
|
||||
on these SoCs.
|
||||
|
||||
Signed-off-by: Sander Vanheule <sander@svanheule.net>
|
||||
Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
|
||||
---
|
||||
drivers/gpio/gpio-realtek-otto.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/drivers/gpio/gpio-realtek-otto.c
|
||||
+++ b/drivers/gpio/gpio-realtek-otto.c
|
||||
@@ -339,6 +339,9 @@ static const struct of_device_id realtek
|
||||
.compatible = "realtek,rtl9300-gpio",
|
||||
.data = (void *)(GPIO_PORTS_REVERSED | GPIO_INTERRUPTS_PER_CPU)
|
||||
},
|
||||
+ {
|
||||
+ .compatible = "realtek,rtl9310-gpio",
|
||||
+ },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, realtek_gpio_of_match);
|
||||
@@ -0,0 +1,86 @@
|
||||
From fce11f68491b46b93df69de0630cd9edb90bc772 Mon Sep 17 00:00:00 2001
|
||||
From: Birger Koblitz <git@birger-koblitz.de>
|
||||
Date: Wed, 29 Dec 2021 21:54:21 +0100
|
||||
Subject: [PATCH] realtek: Create 4 different Realtek Platforms
|
||||
|
||||
Creates RTL83XX as a basic kernel config parameter for the
|
||||
RTL838X, RTL839x, RTL930X and RTL931X platforms with respective
|
||||
configurations for the SoCs, which are introduced in addition.
|
||||
|
||||
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
|
||||
---
|
||||
arch/mips/Kbuild.platforms | 1 +
|
||||
arch/mips/Kconfig | 57 ++++++++++++++
|
||||
2 files changed, 58 insertions(+)
|
||||
|
||||
--- a/arch/mips/Kbuild.platforms
|
||||
+++ b/arch/mips/Kbuild.platforms
|
||||
@@ -23,6 +23,7 @@ platform-$(CONFIG_NLM_COMMON) += netlog
|
||||
platform-$(CONFIG_PIC32MZDA) += pic32/
|
||||
platform-$(CONFIG_RALINK) += ralink/
|
||||
platform-$(CONFIG_MIKROTIK_RB532) += rb532/
|
||||
+platform-$(CONFIG_RTL83XX) += rtl838x/
|
||||
platform-$(CONFIG_SGI_IP22) += sgi-ip22/
|
||||
platform-$(CONFIG_SGI_IP27) += sgi-ip27/
|
||||
platform-$(CONFIG_SGI_IP28) += sgi-ip22/
|
||||
--- a/arch/mips/Kconfig
|
||||
+++ b/arch/mips/Kconfig
|
||||
@@ -1056,8 +1056,58 @@ config NLM_XLP_BOARD
|
||||
This board is based on Netlogic XLP Processor.
|
||||
Say Y here if you have a XLP based board.
|
||||
|
||||
+config RTL83XX
|
||||
+ bool "Realtek based platforms"
|
||||
+ select DMA_NONCOHERENT
|
||||
+ select IRQ_MIPS_CPU
|
||||
+ select NO_EXCEPT_FILL
|
||||
+ select SYS_HAS_CPU_MIPS32_R1
|
||||
+ select SYS_HAS_CPU_MIPS32_R2
|
||||
+ select SYS_SUPPORTS_BIG_ENDIAN
|
||||
+ select SYS_SUPPORTS_HIGHMEM
|
||||
+ select SYS_SUPPORTS_32BIT_KERNEL
|
||||
+ select SYS_SUPPORTS_MIPS16
|
||||
+ select SYS_HAS_EARLY_PRINTK
|
||||
+ select SYS_HAS_EARLY_PRINTK_8250
|
||||
+ select USE_GENERIC_EARLY_PRINTK_8250
|
||||
+ select BOOT_RAW
|
||||
+ select PINCTRL
|
||||
+ select ARCH_HAS_RESET_CONTROLLER
|
||||
+ select RESET_CONTROLLER
|
||||
+ select USE_OF
|
||||
+
|
||||
endchoice
|
||||
|
||||
+config RTL838X
|
||||
+ bool "Realtek RTL838X based platforms"
|
||||
+ depends on RTL83XX
|
||||
+ select CPU_SUPPORTS_CPUFREQ
|
||||
+ select MIPS_EXTERNAL_TIMER
|
||||
+
|
||||
+config RTL839X
|
||||
+ bool "Realtek RTL839X based platforms"
|
||||
+ depends on RTL83XX
|
||||
+ select CPU_SUPPORTS_CPUFREQ
|
||||
+ select MIPS_EXTERNAL_TIMER
|
||||
+ select SYS_SUPPORTS_MULTITHREADING
|
||||
+
|
||||
+config RTL930X
|
||||
+ bool "Realtek RTL930X based platforms"
|
||||
+ depends on RTL83XX
|
||||
+ select MIPS_CPU_SCACHE
|
||||
+ select MIPS_EXTERNAL_TIMER
|
||||
+ select SYS_SUPPORTS_MULTITHREADING
|
||||
+
|
||||
+config RTL931X
|
||||
+ bool "Realtek RTL931X based platforms"
|
||||
+ depends on RTL930X
|
||||
+ select MIPS_GIC
|
||||
+ select COMMON_CLK
|
||||
+ select CLKSRC_MIPS_GIC
|
||||
+ select SYS_SUPPORTS_VPE_LOADER
|
||||
+ select SYS_SUPPORTS_SMP
|
||||
+ select SYS_SUPPORTS_MIPS_CPS
|
||||
+
|
||||
source "arch/mips/alchemy/Kconfig"
|
||||
source "arch/mips/ath25/Kconfig"
|
||||
source "arch/mips/ath79/Kconfig"
|
||||
@@ -0,0 +1,50 @@
|
||||
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <john@phrozen.org>
|
||||
Date: Thu, 26 Nov 2020 12:02:21 +0100
|
||||
Subject: [PATCH] realtek: update the tree to the latest refactored version
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
* rename the target to realtek
|
||||
* add refactored DSA driver
|
||||
* add latest gpio driver
|
||||
* lots of arch cleanups
|
||||
* new irq driver
|
||||
* additional boards
|
||||
|
||||
Submitted-by: Bert Vermeulen <bert@biot.com>
|
||||
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
|
||||
Submitted-by: Sander Vanheule <sander@svanheule.net>
|
||||
Submitted-by: Bjørn Mork <bjorn@mork.no>
|
||||
Submitted-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
drivers/gpio/Kconfig | 6 ++++++
|
||||
drivers/gpio/Makefile | 1 +
|
||||
2 files changed, 7 insertions(+)
|
||||
|
||||
--- a/drivers/gpio/Kconfig
|
||||
+++ b/drivers/gpio/Kconfig
|
||||
@@ -529,6 +529,12 @@ config GPIO_ROCKCHIP
|
||||
help
|
||||
Say yes here to support GPIO on Rockchip SoCs.
|
||||
|
||||
+config GPIO_RTL8231
|
||||
+ tristate "RTL8231 GPIO"
|
||||
+ depends on RTL83XX
|
||||
+ help
|
||||
+ Say yes here to support Realtek RTL8231 GPIO expansion chips.
|
||||
+
|
||||
config GPIO_SAMA5D2_PIOBU
|
||||
tristate "SAMA5D2 PIOBU GPIO support"
|
||||
depends on MFD_SYSCON
|
||||
--- a/drivers/gpio/Makefile
|
||||
+++ b/drivers/gpio/Makefile
|
||||
@@ -129,6 +129,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc3
|
||||
obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o
|
||||
obj-$(CONFIG_GPIO_REG) += gpio-reg.o
|
||||
obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o
|
||||
+obj-$(CONFIG_GPIO_RTL8231) += gpio-rtl8231.o
|
||||
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
|
||||
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
|
||||
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
|
||||
@@ -0,0 +1,93 @@
|
||||
From 3cc8011171186d906c547bc6f0c1f8e350edc7cf Mon Sep 17 00:00:00 2001
|
||||
From: Markus Stockhausen <markus.stockhausen@gmx.de>
|
||||
Date: Mon, 3 Oct 2022 14:45:21 +0200
|
||||
Subject: [PATCH] realtek: resurrect timer driver
|
||||
|
||||
Now that we provide a clock driver for the Reltek SOCs the CPU frequency might
|
||||
change on demand. This has direct visible effects during operation
|
||||
|
||||
- the CEVT 4K timer is no longer a stable clocksource
|
||||
- after CPU frequencies changes time calculation works wrong
|
||||
- sched_clock falls back to kernel default interval (100 Hz)
|
||||
- timestamps in dmesg have only 2 digits left
|
||||
|
||||
[ 0.000000] sched_clock: 32 bits at 100 Hz, resolution 10000000ns, wraps ...
|
||||
[ 0.060000] pid_max: default: 32768 minimum: 301
|
||||
[ 0.070000] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
|
||||
[ 0.070000] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
|
||||
[ 0.080000] dyndbg: Ignore empty _ddebug table in a CONFIG_DYNAMIC_DEBUG_CORE build
|
||||
[ 0.090000] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, ...
|
||||
|
||||
Looking around where we can start the CEVT timer for RTL930X is a good basis.
|
||||
Initially it was developed as a clocksource driver for the broken timer in that
|
||||
specific SOC series. Afterwards it was shifted around to the CEVT location,
|
||||
got SMP enablement and lost its clocksource feature. So we at least have
|
||||
something to copy from. As the timers on these devices are well understood
|
||||
the implementation follows this way:
|
||||
|
||||
- leave the RTL930X implementation as is
|
||||
- provide a new driver for RTL83XX devices only
|
||||
- swap RTL930X driver at a later time
|
||||
|
||||
Like the clock driver this patch contains a self contained module that is SOC
|
||||
independet and already provides full support for the RTL838X, RTL839X and
|
||||
RTL930X devices. Some of the new (or reestablished) features are:
|
||||
|
||||
- simplified initialization routines
|
||||
- SMP setup with CPU hotplug framework
|
||||
- derived from LXB clock speed
|
||||
- supplied clocksource
|
||||
- dedicated register functions for better readability
|
||||
- documentation about some caveats
|
||||
|
||||
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
|
||||
[remove unused header includes, remove old CONFIG_MIPS dependency, add
|
||||
REALTEK_ prefix to driver symbol]
|
||||
Signed-off-by: Sander Vanheule <sander@svanheule.net>
|
||||
|
||||
---
|
||||
drivers/clocksource/Kconfig | 12 +++
|
||||
drivers/clocksource/Makefile | 1 +
|
||||
include/linux/cpuhotplug.h | 1 +
|
||||
3 files changed, 14 insertions(+)
|
||||
|
||||
--- a/drivers/clocksource/Kconfig
|
||||
+++ b/drivers/clocksource/Kconfig
|
||||
@@ -127,6 +127,17 @@ config RDA_TIMER
|
||||
help
|
||||
Enables the support for the RDA Micro timer driver.
|
||||
|
||||
+config REALTEK_OTTO_TIMER
|
||||
+ bool "Clocksource/timer for the Realtek Otto platform"
|
||||
+ select COMMON_CLK
|
||||
+ select TIMER_OF
|
||||
+ help
|
||||
+ This driver adds support for the timers found in the Realtek RTL83xx
|
||||
+ and RTL93xx SoCs series. This includes chips such as RTL8380, RTL8381
|
||||
+ and RTL832, as well as chips from the RTL839x series, such as RTL8390
|
||||
+ RT8391, RTL8392, RTL8393 and RTL8396 and chips of the RTL930x series
|
||||
+ such as RTL9301, RTL9302 or RTL9303.
|
||||
+
|
||||
config SUN4I_TIMER
|
||||
bool "Sun4i timer driver" if COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
--- a/drivers/clocksource/Makefile
|
||||
+++ b/drivers/clocksource/Makefile
|
||||
@@ -58,6 +58,7 @@ obj-$(CONFIG_MILBEAUT_TIMER) += timer-mi
|
||||
obj-$(CONFIG_SPRD_TIMER) += timer-sprd.o
|
||||
obj-$(CONFIG_NPCM7XX_TIMER) += timer-npcm7xx.o
|
||||
obj-$(CONFIG_RDA_TIMER) += timer-rda.o
|
||||
+obj-$(CONFIG_REALTEK_OTTO_TIMER) += timer-rtl-otto.o
|
||||
|
||||
obj-$(CONFIG_ARC_TIMERS) += arc_timer.o
|
||||
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
|
||||
--- a/include/linux/cpuhotplug.h
|
||||
+++ b/include/linux/cpuhotplug.h
|
||||
@@ -176,6 +176,7 @@ enum cpuhp_state {
|
||||
CPUHP_AP_MARCO_TIMER_STARTING,
|
||||
CPUHP_AP_MIPS_GIC_TIMER_STARTING,
|
||||
CPUHP_AP_ARC_TIMER_STARTING,
|
||||
+ CPUHP_AP_REALTEK_TIMER_STARTING,
|
||||
CPUHP_AP_RISCV_TIMER_STARTING,
|
||||
CPUHP_AP_CLINT_TIMER_STARTING,
|
||||
CPUHP_AP_CSKY_TIMER_STARTING,
|
||||
@@ -0,0 +1,26 @@
|
||||
From 9bac1c20b8f39f2e0e342b087add5093b94feaed Mon Sep 17 00:00:00 2001
|
||||
From: INAGAKI Hiroshi <musashino.open@gmail.com>
|
||||
Date: Wed, 5 May 2021 22:05:39 +0900
|
||||
Subject: realtek: backport gpio-realtek-otto driver from 5.13 to 5.10
|
||||
|
||||
This patch backports "gpio-realtek-otto" driver to Kernel 5.10.
|
||||
"MACH_REALTEK_RTL" is used as a platform name in upstream, but "RTL838X"
|
||||
is used in OpenWrt, so update the dependency by the additional patch.
|
||||
|
||||
Submitted-by: INAGAKI Hiroshi <musashino.open@gmail.com>
|
||||
---
|
||||
drivers/gpio/Kconfig | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
--- a/drivers/gpio/Kconfig
|
||||
+++ b/drivers/gpio/Kconfig
|
||||
@@ -503,8 +503,8 @@ config GPIO_RDA
|
||||
|
||||
config GPIO_REALTEK_OTTO
|
||||
tristate "Realtek Otto GPIO support"
|
||||
- depends on MACH_REALTEK_RTL
|
||||
- default MACH_REALTEK_RTL
|
||||
+ depends on RTL83XX
|
||||
+ default RTL838X
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
@@ -0,0 +1,25 @@
|
||||
From 0b000cbfe0aa0323bffa855ef8449c0687a4c071 Mon Sep 17 00:00:00 2001
|
||||
From: INAGAKI Hiroshi <musashino.open@gmail.com>
|
||||
Date: Thu, 6 May 2021 19:30:58 +0900
|
||||
Subject: realtek: backport spi-realtek-rtl driver from 5.12 to 5.10
|
||||
|
||||
This patch backports "spi-realtek-rtl" driver to Kernel 5.10 from 5.12.
|
||||
"MACH_REALTEK_RTL" is used as a platform name in upstream, but "RTL838X"
|
||||
is used in OpenWrt, so update the dependency by the additional patch.
|
||||
|
||||
Submitted-by: INAGAKI Hiroshi <musashino.open@gmail.com>
|
||||
---
|
||||
drivers/spi/Makefile | 2 +-
|
||||
1 files changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/spi/Makefile
|
||||
+++ b/drivers/spi/Makefile
|
||||
@@ -97,7 +97,7 @@ obj-$(CONFIG_SPI_QUP) += spi-qup.o
|
||||
obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
|
||||
obj-$(CONFIG_SPI_ROCKCHIP_SFC) += spi-rockchip-sfc.o
|
||||
obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
|
||||
-obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o
|
||||
+obj-$(CONFIG_RTL83XX) += 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
|
||||
@@ -0,0 +1,25 @@
|
||||
From 2cd00b51470a30198b048a5fca48a04db77e29cc Mon Sep 17 00:00:00 2001
|
||||
From: INAGAKI Hiroshi <musashino.open@gmail.com>
|
||||
Date: Fri, 21 May 2021 23:16:37 +0900
|
||||
Subject: [PATCH] realtek: backport irq-realtek-rtl driver from 5.12 to 5.10
|
||||
|
||||
This patch backports "irq-realtek-rtl" driver to Kernel 5.10 from 5.12.
|
||||
"MACH_REALTEK_RTL" is used as a platform name in upstream, but "RTL838X"
|
||||
is used in OpenWrt, so update the dependency by the additional patch.
|
||||
|
||||
Submitted-by: INAGAKI Hiroshi <musashino.open@gmail.com>
|
||||
---
|
||||
drivers/irqchip/Makefile | 2 +-
|
||||
1 files changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/irqchip/Makefile
|
||||
+++ b/drivers/irqchip/Makefile
|
||||
@@ -112,7 +112,7 @@ 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
|
||||
+obj-$(CONFIG_RTL83XX) += irq-realtek-rtl.o
|
||||
obj-$(CONFIG_WPCM450_AIC) += irq-wpcm450-aic.o
|
||||
obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o
|
||||
obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o
|
||||
@@ -0,0 +1,32 @@
|
||||
From b8fc5eecdc5d33cf261986436597b5482ab856da Mon Sep 17 00:00:00 2001
|
||||
From: Sander Vanheule <sander@svanheule.net>
|
||||
Date: Sun, 14 Nov 2021 19:45:32 +0100
|
||||
Subject: [PATCH] realtek: Backport Realtek Otto WDT driver
|
||||
|
||||
Add patch submitted upstream to linux-watchdog and replace the MIPS
|
||||
architecture symbols. Requires one extra patch for the DIV_ROUND_*
|
||||
macros, which have moved to a different header since 5.10.
|
||||
|
||||
Submitted-by: Sander Vanheule <sander@svanheule.net>
|
||||
Tested-by: Stijn Segers <foss@volatilesystems.org>
|
||||
Tested-by: Paul Fertser <fercerpav@gmail.com>
|
||||
Tested-by: Stijn Tintel <stijn@linux-ipv6.be>
|
||||
---
|
||||
drivers/watchdog/Kconfig | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/watchdog/Kconfig
|
||||
+++ b/drivers/watchdog/Kconfig
|
||||
@@ -956,10 +956,10 @@ config RTD119X_WATCHDOG
|
||||
|
||||
config REALTEK_OTTO_WDT
|
||||
tristate "Realtek Otto MIPS watchdog support"
|
||||
- depends on MACH_REALTEK_RTL || COMPILE_TEST
|
||||
+ depends on RTL83XX
|
||||
depends on COMMON_CLK
|
||||
select WATCHDOG_CORE
|
||||
- default MACH_REALTEK_RTL
|
||||
+ default RTL83XX
|
||||
help
|
||||
Say Y here to include support for the watchdog timer on Realtek
|
||||
RTL838x, RTL839x, RTL930x SoCs. This watchdog has pretimeout
|
||||
@@ -0,0 +1,28 @@
|
||||
From b8fc5eecdc5d33cf261986436597b5482ab856da Mon Sep 17 00:00:00 2001
|
||||
From: Sander Vanheule <sander@svanheule.net>
|
||||
Date: Sun, 14 Nov 2021 19:45:32 +0100
|
||||
Subject: [PATCH] realtek: Backport Realtek Otto WDT driver
|
||||
|
||||
Add patch submitted upstream to linux-watchdog and replace the MIPS
|
||||
architecture symbols. Requires one extra patch for the DIV_ROUND_*
|
||||
macros, which have moved to a different header since 5.10.
|
||||
|
||||
Submitted-by: Sander Vanheule <sander@svanheule.net>
|
||||
Tested-by: Stijn Segers <foss@volatilesystems.org>
|
||||
Tested-by: Paul Fertser <fercerpav@gmail.com>
|
||||
Tested-by: Stijn Tintel <stijn@linux-ipv6.be>
|
||||
---
|
||||
drivers/watchdog/realtek_otto_wdt.c | 2 +-
|
||||
1 files changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/watchdog/realtek_otto_wdt.c
|
||||
+++ b/drivers/watchdog/realtek_otto_wdt.c
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
-#include <linux/math.h>
|
||||
+#include <linux/kernel.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
@@ -0,0 +1,46 @@
|
||||
From 63a0a4d85bc900464c5b046b13808a582345f8c8 Mon Sep 17 00:00:00 2001
|
||||
From: Birger Koblitz <git@birger-koblitz.de>
|
||||
Date: Sat, 11 Dec 2021 20:14:47 +0100
|
||||
Subject: [PATCH] realtek: Add support for RTL9300/RTL9310 I2C controller
|
||||
|
||||
This adds support for the RTL9300 and RTL9310 I2C controller.
|
||||
The controller implements the SMBus protocol for SMBus transfers
|
||||
over an I2C bus. The driver supports selecting one of the 2 possible
|
||||
SCL pins and any of the 8 possible SDA pins. Bus speeds of
|
||||
100kHz (standard speed) and 400kHz (high speed I2C) are supported.
|
||||
|
||||
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
|
||||
---
|
||||
drivers/i2c/busses/Kconfig | 10 +++++++++
|
||||
drivers/i2c/busses/Makefile | 1 +
|
||||
2 files changed, 11 insertions(+)
|
||||
|
||||
--- a/drivers/i2c/busses/Kconfig
|
||||
+++ b/drivers/i2c/busses/Kconfig
|
||||
@@ -949,6 +949,16 @@ config I2C_RK3X
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called i2c-rk3x.
|
||||
|
||||
+config I2C_RTL9300
|
||||
+ tristate "Realtek RTL9300 I2C adapter"
|
||||
+ depends on OF
|
||||
+ help
|
||||
+ Say Y here to include support for the I2C adapter in Realtek RTL9300
|
||||
+ and RTL9310 SoCs.
|
||||
+
|
||||
+ This driver can also be built as a module. If so, the module will
|
||||
+ be called i2c-rtl9300.
|
||||
+
|
||||
config HAVE_S3C2410_I2C
|
||||
bool
|
||||
help
|
||||
--- a/drivers/i2c/busses/Makefile
|
||||
+++ b/drivers/i2c/busses/Makefile
|
||||
@@ -94,6 +94,7 @@ obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom-
|
||||
obj-$(CONFIG_I2C_QUP) += i2c-qup.o
|
||||
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
|
||||
obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o
|
||||
+obj-$(CONFIG_I2C_RTL9300) += i2c-rtl9300.o
|
||||
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
|
||||
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
|
||||
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
|
||||
@@ -0,0 +1,46 @@
|
||||
From f4bdb7fdccdfe3fa382abe77f72a16c2f2e6add0 Mon Sep 17 00:00:00 2001
|
||||
From: Birger Koblitz <git@birger-koblitz.de>
|
||||
Date: Sat, 11 Dec 2021 20:25:37 +0100
|
||||
Subject: [PATCH] realtek: Add support for RTL9300/RTL9310 I2C multiplexing
|
||||
|
||||
The RTL9300/RTL9310 I2C controllers have support for 2 independent I2C
|
||||
masters, each with a fixed SCL pin, that cannot be changed. Each of these
|
||||
masters can use 8 (RTL9300) or 16 (RTL9310) different pins for SDA.
|
||||
This multiplexer directly controls the two masters and their shared
|
||||
IO configuration registers to allow multiplexing between any of these
|
||||
busses. The two masters cannot be used in parallel as the multiplex
|
||||
is protected by a standard multiplex lock.
|
||||
|
||||
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
|
||||
---
|
||||
drivers/i2c/muxes/Kconfig | 9 +++++++
|
||||
drivers/i2c/muxes/Makefile | 1 +
|
||||
2 files changed, 10 insertions(+)
|
||||
|
||||
--- a/drivers/i2c/muxes/Kconfig
|
||||
+++ b/drivers/i2c/muxes/Kconfig
|
||||
@@ -99,6 +99,15 @@ config I2C_MUX_REG
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-mux-reg.
|
||||
|
||||
+config I2C_MUX_RTL9300
|
||||
+ tristate "RTL9300 based I2C multiplexer"
|
||||
+ help
|
||||
+ If you say yes to this option, support will be included for a
|
||||
+ RTL9300 based I2C multiplexer.
|
||||
+
|
||||
+ This driver can also be built as a module. If so, the module
|
||||
+ will be called i2c-mux-reg.
|
||||
+
|
||||
config I2C_DEMUX_PINCTRL
|
||||
tristate "pinctrl-based I2C demultiplexer"
|
||||
depends on PINCTRL && OF
|
||||
--- a/drivers/i2c/muxes/Makefile
|
||||
+++ b/drivers/i2c/muxes/Makefile
|
||||
@@ -14,5 +14,6 @@ obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux
|
||||
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
|
||||
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
|
||||
obj-$(CONFIG_I2C_MUX_REG) += i2c-mux-reg.o
|
||||
+obj-$(CONFIG_I2C_MUX_RTL9300) += i2c-mux-rtl9300.o
|
||||
|
||||
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
|
||||
@@ -0,0 +1,402 @@
|
||||
From 6c18e9c491959ac0674ebe36b09f9ddc3f2c9bce Mon Sep 17 00:00:00 2001
|
||||
From: Birger Koblitz <git@birger-koblitz.de>
|
||||
Date: Fri, 31 Dec 2021 11:56:49 +0100
|
||||
Subject: [PATCH] realtek: Add VPE support for the IRQ driver
|
||||
|
||||
In order to support VSMP, enable support for both VPEs
|
||||
of the RTL839X and RTL930X SoCs in the irq-realtek-rtl
|
||||
driver. Add support for IRQ affinity setting.
|
||||
|
||||
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
|
||||
---
|
||||
drivers/irqchip/irq-realtek-rtl.c | 152 +++++++++++++++---
|
||||
1 file changed, 73 insertions(+), 76 deletions(-)
|
||||
|
||||
--- a/drivers/irqchip/irq-realtek-rtl.c
|
||||
+++ b/drivers/irqchip/irq-realtek-rtl.c
|
||||
@@ -21,21 +21,63 @@
|
||||
#define RTL_ICTL_IRR2 0x10
|
||||
#define RTL_ICTL_IRR3 0x14
|
||||
|
||||
-#define REG(x) (realtek_ictl_base + x)
|
||||
+#define RTL_ICTL_NUM_INPUTS 32
|
||||
+#define RTL_ICTL_NUM_OUTPUTS 15
|
||||
|
||||
static DEFINE_RAW_SPINLOCK(irq_lock);
|
||||
-static void __iomem *realtek_ictl_base;
|
||||
+
|
||||
+#define REG(offset, cpu) (realtek_ictl_base[cpu] + offset)
|
||||
+
|
||||
+static void __iomem *realtek_ictl_base[NR_CPUS];
|
||||
+static cpumask_t realtek_ictl_cpu_configurable;
|
||||
+
|
||||
+struct realtek_ictl_output {
|
||||
+ /* IRQ controller data */
|
||||
+ struct fwnode_handle *fwnode;
|
||||
+ /* Output specific data */
|
||||
+ unsigned int output_index;
|
||||
+ struct irq_domain *domain;
|
||||
+ u32 child_mask;
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering,
|
||||
+ * placing IRQ 31 in the first four bits. A routing value of '0' means the
|
||||
+ * interrupt is left disconnected. Routing values {1..15} connect to output
|
||||
+ * lines {0..14}.
|
||||
+ */
|
||||
+#define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32))
|
||||
+#define IRR_SHIFT(idx) ((idx * 4) % 32)
|
||||
+
|
||||
+static inline u32 read_irr(void __iomem *irr0, int idx)
|
||||
+{
|
||||
+ return (readl(irr0 + IRR_OFFSET(idx)) >> IRR_SHIFT(idx)) & 0xf;
|
||||
+}
|
||||
+
|
||||
+static inline void write_irr(void __iomem *irr0, int idx, u32 value)
|
||||
+{
|
||||
+ unsigned int offset = IRR_OFFSET(idx);
|
||||
+ unsigned int shift = IRR_SHIFT(idx);
|
||||
+ u32 irr;
|
||||
+
|
||||
+ irr = readl(irr0 + offset) & ~(0xf << shift);
|
||||
+ irr |= (value & 0xf) << shift;
|
||||
+ writel(irr, irr0 + offset);
|
||||
+}
|
||||
|
||||
static void realtek_ictl_unmask_irq(struct irq_data *i)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
+ int cpu;
|
||||
|
||||
raw_spin_lock_irqsave(&irq_lock, flags);
|
||||
|
||||
- value = readl(REG(RTL_ICTL_GIMR));
|
||||
- value |= BIT(i->hwirq);
|
||||
- writel(value, REG(RTL_ICTL_GIMR));
|
||||
+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) {
|
||||
+ value = readl(REG(RTL_ICTL_GIMR, cpu));
|
||||
+ value |= BIT(i->hwirq);
|
||||
+ writel(value, REG(RTL_ICTL_GIMR, cpu));
|
||||
+ }
|
||||
|
||||
raw_spin_unlock_irqrestore(&irq_lock, flags);
|
||||
}
|
||||
@@ -44,52 +86,137 @@ static void realtek_ictl_mask_irq(struct
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
+ int cpu;
|
||||
|
||||
raw_spin_lock_irqsave(&irq_lock, flags);
|
||||
|
||||
- value = readl(REG(RTL_ICTL_GIMR));
|
||||
- value &= ~BIT(i->hwirq);
|
||||
- writel(value, REG(RTL_ICTL_GIMR));
|
||||
+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) {
|
||||
+ value = readl(REG(RTL_ICTL_GIMR, cpu));
|
||||
+ value &= ~BIT(i->hwirq);
|
||||
+ writel(value, REG(RTL_ICTL_GIMR, cpu));
|
||||
+ }
|
||||
|
||||
raw_spin_unlock_irqrestore(&irq_lock, flags);
|
||||
}
|
||||
|
||||
+static int __maybe_unused realtek_ictl_irq_affinity(struct irq_data *i,
|
||||
+ const struct cpumask *dest, bool force)
|
||||
+{
|
||||
+ struct realtek_ictl_output *output = i->domain->host_data;
|
||||
+ cpumask_t cpu_configure;
|
||||
+ cpumask_t cpu_disable;
|
||||
+ cpumask_t cpu_enable;
|
||||
+ unsigned long flags;
|
||||
+ int cpu;
|
||||
+
|
||||
+ raw_spin_lock_irqsave(&irq_lock, flags);
|
||||
+
|
||||
+ cpumask_and(&cpu_configure, cpu_present_mask, &realtek_ictl_cpu_configurable);
|
||||
+
|
||||
+ cpumask_and(&cpu_enable, &cpu_configure, dest);
|
||||
+ cpumask_andnot(&cpu_disable, &cpu_configure, dest);
|
||||
+
|
||||
+ for_each_cpu(cpu, &cpu_disable)
|
||||
+ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, 0);
|
||||
+
|
||||
+ for_each_cpu(cpu, &cpu_enable)
|
||||
+ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, output->output_index + 1);
|
||||
+
|
||||
+ irq_data_update_effective_affinity(i, &cpu_enable);
|
||||
+
|
||||
+ raw_spin_unlock_irqrestore(&irq_lock, flags);
|
||||
+
|
||||
+ return IRQ_SET_MASK_OK;
|
||||
+}
|
||||
+
|
||||
static struct irq_chip realtek_ictl_irq = {
|
||||
.name = "realtek-rtl-intc",
|
||||
.irq_mask = realtek_ictl_mask_irq,
|
||||
.irq_unmask = realtek_ictl_unmask_irq,
|
||||
+#ifdef CONFIG_SMP
|
||||
+ .irq_set_affinity = realtek_ictl_irq_affinity,
|
||||
+#endif
|
||||
};
|
||||
|
||||
static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
|
||||
{
|
||||
+ struct realtek_ictl_output *output = d->host_data;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq);
|
||||
|
||||
+ raw_spin_lock_irqsave(&irq_lock, flags);
|
||||
+
|
||||
+ output->child_mask |= BIT(hw);
|
||||
+ write_irr(REG(RTL_ICTL_IRR0, 0), hw, output->output_index + 1);
|
||||
+
|
||||
+ raw_spin_unlock_irqrestore(&irq_lock, flags);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int intc_select(struct irq_domain *d, struct irq_fwspec *fwspec,
|
||||
+ enum irq_domain_bus_token bus_token)
|
||||
+{
|
||||
+ struct realtek_ictl_output *output = d->host_data;
|
||||
+ bool routed_elsewhere;
|
||||
+ unsigned long flags;
|
||||
+ u32 routing_old;
|
||||
+ int cpu;
|
||||
+
|
||||
+ if (fwspec->fwnode != output->fwnode)
|
||||
+ return false;
|
||||
+
|
||||
+ /* Original specifiers had only one parameter */
|
||||
+ if (fwspec->param_count < 2)
|
||||
+ return true;
|
||||
+
|
||||
+ raw_spin_lock_irqsave(&irq_lock, flags);
|
||||
+
|
||||
+ /*
|
||||
+ * Inputs can only be routed to one output, so they shouldn't be
|
||||
+ * allowed to end up in multiple domains.
|
||||
+ */
|
||||
+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) {
|
||||
+ routing_old = read_irr(REG(RTL_ICTL_IRR0, cpu), fwspec->param[0]);
|
||||
+ routed_elsewhere = routing_old && fwspec->param[1] != routing_old - 1;
|
||||
+ if (routed_elsewhere) {
|
||||
+ pr_warn("soc int %d already routed to output %d\n",
|
||||
+ fwspec->param[0], routing_old - 1);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ raw_spin_unlock_irqrestore(&irq_lock, flags);
|
||||
+
|
||||
+ return !routed_elsewhere && fwspec->param[1] == output->output_index;
|
||||
+}
|
||||
+
|
||||
static const struct irq_domain_ops irq_domain_ops = {
|
||||
.map = intc_map,
|
||||
+ .select = intc_select,
|
||||
.xlate = irq_domain_xlate_onecell,
|
||||
};
|
||||
|
||||
static void realtek_irq_dispatch(struct irq_desc *desc)
|
||||
{
|
||||
+ struct realtek_ictl_output *output = irq_desc_get_handler_data(desc);
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
- struct irq_domain *domain;
|
||||
+ int cpu = smp_processor_id();
|
||||
unsigned long pending;
|
||||
unsigned int soc_int;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
- pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR));
|
||||
+ pending = readl(REG(RTL_ICTL_GIMR, cpu)) & readl(REG(RTL_ICTL_GISR, cpu))
|
||||
+ & output->child_mask;
|
||||
|
||||
if (unlikely(!pending)) {
|
||||
spurious_interrupt();
|
||||
goto out;
|
||||
}
|
||||
|
||||
- domain = irq_desc_get_handler_data(desc);
|
||||
- for_each_set_bit(soc_int, &pending, 32)
|
||||
- generic_handle_domain_irq(domain, soc_int);
|
||||
+ for_each_set_bit(soc_int, &pending, RTL_ICTL_NUM_INPUTS)
|
||||
+ generic_handle_domain_irq(output->domain, soc_int);
|
||||
|
||||
out:
|
||||
chained_irq_exit(chip, desc);
|
||||
@@ -102,85 +229,110 @@ out:
|
||||
* thus go into 4 IRRs. A routing value of '0' means the interrupt is left
|
||||
* disconnected. Routing values {1..15} connect to output lines {0..14}.
|
||||
*/
|
||||
-static int __init map_interrupts(struct device_node *node, struct irq_domain *domain)
|
||||
+static int __init setup_parent_interrupts(struct device_node *node, int *parents,
|
||||
+ unsigned int num_parents)
|
||||
{
|
||||
- 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;
|
||||
+ struct realtek_ictl_output *outputs;
|
||||
+ struct realtek_ictl_output *output;
|
||||
+ struct irq_domain *domain;
|
||||
+ unsigned int p;
|
||||
|
||||
- ret = of_property_read_u32(node, "#address-cells", &tmp);
|
||||
- if (ret || tmp)
|
||||
- return -EINVAL;
|
||||
+ outputs = kcalloc(num_parents, sizeof(*outputs), GFP_KERNEL);
|
||||
+ if (!outputs)
|
||||
+ return -ENOMEM;
|
||||
|
||||
- imap = of_get_property(node, "interrupt-map", &imaplen);
|
||||
- if (!imap || imaplen % 3)
|
||||
- return -EINVAL;
|
||||
+ for (p = 0; p < num_parents; p++) {
|
||||
+ output = outputs + p;
|
||||
|
||||
- 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);
|
||||
- of_node_put(cpu_ictl);
|
||||
- if (ret || tmp != 1)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- cpu_int = be32_to_cpup(imap + 2);
|
||||
- if (cpu_int > 7 || cpu_int < 2)
|
||||
- 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);
|
||||
- }
|
||||
+ domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, output);
|
||||
+ if (!domain)
|
||||
+ goto domain_err;
|
||||
|
||||
- /* Use routing values (1..6) for CPU interrupts (2..7) */
|
||||
- regs[(soc_int * 4) / 32] |= (cpu_int - 1) << (soc_int * 4) % 32;
|
||||
- imap += 3;
|
||||
- }
|
||||
+ output->fwnode = of_node_to_fwnode(node);
|
||||
+ output->output_index = p;
|
||||
+ output->domain = domain;
|
||||
|
||||
- for (i = 0; i < 4; i++)
|
||||
- writel(regs[i], REG(irr_regs[i]));
|
||||
+ irq_set_chained_handler_and_data(parents[p], realtek_irq_dispatch, output);
|
||||
+ }
|
||||
|
||||
return 0;
|
||||
+
|
||||
+domain_err:
|
||||
+ while (p--) {
|
||||
+ irq_set_chained_handler_and_data(parents[p], NULL, NULL);
|
||||
+ irq_domain_remove(outputs[p].domain);
|
||||
+ }
|
||||
+
|
||||
+ kfree(outputs);
|
||||
+
|
||||
+ return -ENOMEM;
|
||||
}
|
||||
|
||||
static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent)
|
||||
{
|
||||
- struct irq_domain *domain;
|
||||
- int ret;
|
||||
+ int parent_irqs[RTL_ICTL_NUM_OUTPUTS];
|
||||
+ struct of_phandle_args oirq;
|
||||
+ unsigned int num_parents;
|
||||
+ unsigned int soc_irq;
|
||||
+ unsigned int p;
|
||||
+ int cpu;
|
||||
+
|
||||
+ cpumask_clear(&realtek_ictl_cpu_configurable);
|
||||
+
|
||||
+ for (cpu = 0; cpu < NR_CPUS; cpu++) {
|
||||
+ realtek_ictl_base[cpu] = of_iomap(node, cpu);
|
||||
+ if (realtek_ictl_base[cpu]) {
|
||||
+ cpumask_set_cpu(cpu, &realtek_ictl_cpu_configurable);
|
||||
+
|
||||
+ /* Disable all cascaded interrupts and clear routing */
|
||||
+ writel(0, REG(RTL_ICTL_GIMR, cpu));
|
||||
+ for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++)
|
||||
+ write_irr(REG(RTL_ICTL_IRR0, cpu), soc_irq, 0);
|
||||
+ }
|
||||
+ }
|
||||
|
||||
- realtek_ictl_base = of_iomap(node, 0);
|
||||
- if (!realtek_ictl_base)
|
||||
+ if (cpumask_empty(&realtek_ictl_cpu_configurable))
|
||||
return -ENXIO;
|
||||
|
||||
- /* Disable all cascaded interrupts */
|
||||
- writel(0, REG(RTL_ICTL_GIMR));
|
||||
+ num_parents = of_irq_count(node);
|
||||
+ if (num_parents > RTL_ICTL_NUM_OUTPUTS) {
|
||||
+ pr_err("too many parent interrupts\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
|
||||
- domain = irq_domain_add_simple(node, 32, 0,
|
||||
- &irq_domain_ops, NULL);
|
||||
+ for (p = 0; p < num_parents; p++)
|
||||
+ parent_irqs[p] = of_irq_get(node, p);
|
||||
|
||||
- ret = map_interrupts(node, domain);
|
||||
- if (ret) {
|
||||
- pr_err("invalid interrupt map\n");
|
||||
- return ret;
|
||||
+ if (WARN_ON(!num_parents)) {
|
||||
+ /*
|
||||
+ * If DT contains no parent interrupts, assume MIPS CPU IRQ 2
|
||||
+ * (HW0) is connected to the first output. This is the case for
|
||||
+ * all known hardware anyway. "interrupt-map" is deprecated, so
|
||||
+ * don't bother trying to parse that.
|
||||
+ * Since this is to account for old devicetrees with one-cell
|
||||
+ * interrupt specifiers, only one output domain is needed.
|
||||
+ */
|
||||
+ oirq.np = of_find_compatible_node(NULL, NULL, "mti,cpu-interrupt-controller");
|
||||
+ if (oirq.np) {
|
||||
+ oirq.args_count = 1;
|
||||
+ oirq.args[0] = 2;
|
||||
+
|
||||
+ parent_irqs[0] = irq_create_of_mapping(&oirq);
|
||||
+ num_parents = 1;
|
||||
+ }
|
||||
+
|
||||
+ of_node_put(oirq.np);
|
||||
}
|
||||
|
||||
- return 0;
|
||||
+ /* Ensure we haven't collected any errors before proceeding */
|
||||
+ for (p = 0; p < num_parents; p++) {
|
||||
+ if (parent_irqs[p] < 0)
|
||||
+ return parent_irqs[p];
|
||||
+ if (!parent_irqs[p])
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ return setup_parent_interrupts(node, &parent_irqs[0], num_parents);
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc", realtek_rtl_of_init);
|
||||
@@ -0,0 +1,51 @@
|
||||
From bde6311569ef25a00c3beaeabfd6b78b19651872 Mon Sep 17 00:00:00 2001
|
||||
From: Sander Vanheule <sander@svanheule.net>
|
||||
Date: Sun, 29 May 2022 19:38:09 +0200
|
||||
Subject: [PATCH] realtek: don't unmask non-maskable GPIO IRQs
|
||||
|
||||
On uniprocessor builds, for_each_cpu(cpu, mask) will assume 'mask'
|
||||
always contains exactly one CPU, and ignore the actual mask contents.
|
||||
This causes the loop to run, even when it shouldn't on an empty mask,
|
||||
and tries to access an uninitialised pointer.
|
||||
|
||||
Fix this by wrapping the loop in a cpumask_empty() check, to ensure it
|
||||
will not run on uniprocessor builds if the CPU mask is empty.
|
||||
|
||||
Fixes: af6cd37f42f3 ("realtek: replace RTL93xx GPIO patches")
|
||||
Reported-by: INAGAKI Hiroshi <musashino.open@gmail.com>
|
||||
Reported-by: Robert Marko <robimarko@gmail.com>
|
||||
Tested-by: Robert Marko <robimarko@gmail.com>
|
||||
Submitted-by: Sander Vanheule <sander@svanheule.net>
|
||||
---
|
||||
drivers/gpio/gpio-realtek-otto.c | 9 +++++++++++--
|
||||
1 file changed, 11 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/gpio/gpio-realtek-otto.c
|
||||
+++ b/drivers/gpio/gpio-realtek-otto.c
|
||||
@@ -301,6 +301,7 @@ static int realtek_gpio_irq_set_affinity
|
||||
static int realtek_gpio_irq_init(struct gpio_chip *gc)
|
||||
{
|
||||
struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);
|
||||
+ void __iomem *irq_cpu_mask;
|
||||
unsigned int port;
|
||||
int cpu;
|
||||
|
||||
@@ -308,8 +309,16 @@ static int realtek_gpio_irq_init(struct
|
||||
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));
|
||||
+ /*
|
||||
+ * Uniprocessor builds assume a mask always contains one CPU,
|
||||
+ * so only start the loop if we have at least one maskable CPU.
|
||||
+ */
|
||||
+ if(!cpumask_empty(&ctrl->cpu_irq_maskable)) {
|
||||
+ for_each_cpu(cpu, &ctrl->cpu_irq_maskable) {
|
||||
+ irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, port, cpu);
|
||||
+ iowrite8(GENMASK(7, 0), irq_cpu_mask);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -0,0 +1,368 @@
|
||||
From ee0175b3b44288c74d5292c2a9c2c154f6c0317e Mon Sep 17 00:00:00 2001
|
||||
From: Sander Vanheule <sander@svanheule.net>
|
||||
Date: Sun, 7 Aug 2022 21:21:15 +0200
|
||||
Subject: [PATCH] gpio: realtek-otto: switch to 32-bit I/O
|
||||
|
||||
By using 16-bit I/O on the GPIO peripheral, which is apparently not safe
|
||||
on MIPS, the IMR can end up containing garbage. This then results in
|
||||
interrupt triggers for lines that don't have an interrupt handler
|
||||
associated. The irq_desc lookup fails, and the ISR will not be cleared,
|
||||
keeping the CPU busy until reboot, or until another IMR operation
|
||||
restores the correct value. This situation appears to happen very
|
||||
rarely, for < 0.5% of IMR writes.
|
||||
|
||||
Instead of using 8-bit or 16-bit I/O operations on the 32-bit memory
|
||||
mapped peripheral registers, switch to using 32-bit I/O only, operating
|
||||
on the entire bank for all single bit line settings. For 2-bit line
|
||||
settings, with 16-bit port values, stick to manual (un)packing.
|
||||
|
||||
This issue has been seen on RTL8382M (HPE 1920-16G), RTL8391M (Netgear
|
||||
GS728TP v2), and RTL8393M (D-Link DGS-1210-52 F3, Zyxel GS1900-48).
|
||||
|
||||
Reported-by: Luiz Angelo Daros de Luca <luizluca@gmail.com> # DGS-1210-52
|
||||
Reported-by: Birger Koblitz <mail@birger-koblitz.de> # GS728TP
|
||||
Reported-by: Jan Hoffmann <jan@3e8.eu> # 1920-16G
|
||||
Fixes: 0d82fb1127fb ("gpio: Add Realtek Otto GPIO support")
|
||||
Signed-off-by: Sander Vanheule <sander@svanheule.net>
|
||||
Cc: Paul Cercueil <paul@crapouillou.net>
|
||||
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
|
||||
Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
|
||||
|
||||
Update patch for missing upstream changes:
|
||||
- commit a01a40e33499 ("gpio: realtek-otto: Make the irqchip immutable")
|
||||
Signed-off-by: Sander Vanheule <sander@svanheule.net>
|
||||
|
||||
---
|
||||
drivers/gpio/gpio-realtek-otto.c | 166 ++++++++++++++++---------------
|
||||
1 file changed, 85 insertions(+), 81 deletions(-)
|
||||
|
||||
--- a/drivers/gpio/gpio-realtek-otto.c
|
||||
+++ b/drivers/gpio/gpio-realtek-otto.c
|
||||
@@ -46,10 +46,20 @@
|
||||
* @lock: Lock for accessing the IRQ registers and values
|
||||
* @intr_mask: Mask for interrupts lines
|
||||
* @intr_type: Interrupt type selection
|
||||
+ * @bank_read: Read a bank setting as a single 32-bit value
|
||||
+ * @bank_write: Write a bank setting as a single 32-bit value
|
||||
+ * @imr_line_pos: Bit shift of an IRQ line's IMR value.
|
||||
+ *
|
||||
+ * The DIR, DATA, and ISR registers consist of four 8-bit port values, packed
|
||||
+ * into a single 32-bit register. Use @bank_read (@bank_write) to get (assign)
|
||||
+ * a value from (to) these registers. The IMR register consists of four 16-bit
|
||||
+ * port values, packed into two 32-bit registers. Use @imr_line_pos to get the
|
||||
+ * bit shift of the 2-bit field for a line's IMR settings. Shifts larger than
|
||||
+ * 32 overflow into the second register.
|
||||
*
|
||||
* 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
|
||||
+ * mask/unmask the interrupts for a GPIO line, and @intr_type is used to store
|
||||
* the selected interrupt types. The logical AND of these values is written to
|
||||
* IMR on changes.
|
||||
*/
|
||||
@@ -59,10 +69,11 @@ struct realtek_gpio_ctrl {
|
||||
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];
|
||||
- unsigned int (*port_offset_u8)(unsigned int port);
|
||||
- unsigned int (*port_offset_u16)(unsigned int port);
|
||||
+ u8 intr_mask[REALTEK_GPIO_MAX];
|
||||
+ u8 intr_type[REALTEK_GPIO_MAX];
|
||||
+ u32 (*bank_read)(void __iomem *reg);
|
||||
+ void (*bank_write)(void __iomem *reg, u32 value);
|
||||
+ unsigned int (*line_imr_pos)(unsigned int line);
|
||||
};
|
||||
|
||||
/* Expand with more flags as devices with other quirks are added */
|
||||
@@ -101,14 +112,22 @@ 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)
|
||||
+static u32 realtek_gpio_bank_read_swapped(void __iomem *reg)
|
||||
+{
|
||||
+ return ioread32be(reg);
|
||||
+}
|
||||
+
|
||||
+static void realtek_gpio_bank_write_swapped(void __iomem *reg, u32 value)
|
||||
{
|
||||
- return port;
|
||||
+ iowrite32be(value, reg);
|
||||
}
|
||||
|
||||
-static unsigned int realtek_gpio_port_offset_u16(unsigned int port)
|
||||
+static unsigned int realtek_gpio_line_imr_pos_swapped(unsigned int line)
|
||||
{
|
||||
- return 2 * port;
|
||||
+ unsigned int port_pin = line % 8;
|
||||
+ unsigned int port = line / 8;
|
||||
+
|
||||
+ return 2 * (8 * (port ^ 1) + port_pin);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -119,64 +138,65 @@ static unsigned int realtek_gpio_port_of
|
||||
* 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)
|
||||
+static u32 realtek_gpio_bank_read(void __iomem *reg)
|
||||
{
|
||||
- return 3 - port;
|
||||
+ return ioread32(reg);
|
||||
}
|
||||
|
||||
-static unsigned int realtek_gpio_port_offset_u16_rev(unsigned int port)
|
||||
+static void realtek_gpio_bank_write(void __iomem *reg, u32 value)
|
||||
{
|
||||
- return 2 * (port ^ 1);
|
||||
+ iowrite32(value, reg);
|
||||
}
|
||||
|
||||
-static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl,
|
||||
- unsigned int port, u16 irq_type, u16 irq_mask)
|
||||
+static unsigned int realtek_gpio_line_imr_pos(unsigned int line)
|
||||
{
|
||||
- iowrite16(irq_type & irq_mask,
|
||||
- ctrl->base + REALTEK_GPIO_REG_IMR + ctrl->port_offset_u16(port));
|
||||
+ return 2 * line;
|
||||
}
|
||||
|
||||
-static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl,
|
||||
- unsigned int port, u8 mask)
|
||||
+static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl, u32 mask)
|
||||
{
|
||||
- iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port));
|
||||
+ ctrl->bank_write(ctrl->base + REALTEK_GPIO_REG_ISR, mask);
|
||||
}
|
||||
|
||||
-static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port)
|
||||
+static u32 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl)
|
||||
{
|
||||
- return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port));
|
||||
+ return ctrl->bank_read(ctrl->base + REALTEK_GPIO_REG_ISR);
|
||||
}
|
||||
|
||||
-/* Set the rising and falling edge mask bits for a GPIO port pin */
|
||||
-static u16 realtek_gpio_imr_bits(unsigned int pin, u16 value)
|
||||
+/* Set the rising and falling edge mask bits for a GPIO pin */
|
||||
+static void realtek_gpio_update_line_imr(struct realtek_gpio_ctrl *ctrl, unsigned int line)
|
||||
{
|
||||
- return (value & REALTEK_GPIO_IMR_LINE_MASK) << 2 * pin;
|
||||
+ void __iomem *reg = ctrl->base + REALTEK_GPIO_REG_IMR;
|
||||
+ unsigned int line_shift = ctrl->line_imr_pos(line);
|
||||
+ unsigned int shift = line_shift % 32;
|
||||
+ u32 irq_type = ctrl->intr_type[line];
|
||||
+ u32 irq_mask = ctrl->intr_mask[line];
|
||||
+ u32 reg_val;
|
||||
+
|
||||
+ reg += 4 * (line_shift / 32);
|
||||
+ reg_val = ioread32(reg);
|
||||
+ reg_val &= ~(REALTEK_GPIO_IMR_LINE_MASK << shift);
|
||||
+ reg_val |= (irq_type & irq_mask & REALTEK_GPIO_IMR_LINE_MASK) << shift;
|
||||
+ iowrite32(reg_val, reg);
|
||||
}
|
||||
|
||||
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));
|
||||
+ realtek_gpio_clear_isr(ctrl, BIT(line));
|
||||
}
|
||||
|
||||
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);
|
||||
+ ctrl->intr_mask[line] = REALTEK_GPIO_IMR_LINE_MASK;
|
||||
+ realtek_gpio_update_line_imr(ctrl, line);
|
||||
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
||||
}
|
||||
|
||||
@@ -184,16 +204,11 @@ static void realtek_gpio_irq_mask(struct
|
||||
{
|
||||
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);
|
||||
+ ctrl->intr_mask[line] = 0;
|
||||
+ realtek_gpio_update_line_imr(ctrl, line);
|
||||
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
||||
}
|
||||
|
||||
@@ -201,10 +216,8 @@ static int realtek_gpio_irq_set_type(str
|
||||
{
|
||||
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;
|
||||
+ u8 type;
|
||||
|
||||
switch (flow_type & IRQ_TYPE_SENSE_MASK) {
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
@@ -223,11 +236,8 @@ static int realtek_gpio_irq_set_type(str
|
||||
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]);
|
||||
+ ctrl->intr_type[line] = type;
|
||||
+ realtek_gpio_update_line_imr(ctrl, line);
|
||||
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
||||
|
||||
return 0;
|
||||
@@ -238,28 +248,21 @@ static void realtek_gpio_irq_handler(str
|
||||
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 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)
|
||||
- generic_handle_domain_irq(gc->irq.domain, offset + lines_done);
|
||||
- }
|
||||
+ status = realtek_gpio_read_isr(ctrl);
|
||||
+ for_each_set_bit(offset, &status, gc->ngpio)
|
||||
+ generic_handle_domain_irq(gc->irq.domain, offset);
|
||||
|
||||
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)
|
||||
+static inline void __iomem *realtek_gpio_irq_cpu_mask(struct realtek_gpio_ctrl *ctrl, int cpu)
|
||||
{
|
||||
- return ctrl->cpumask_base + ctrl->port_offset_u8(port) +
|
||||
- REALTEK_GPIO_PORTS_PER_BANK * cpu;
|
||||
+ return ctrl->cpumask_base + REALTEK_GPIO_PORTS_PER_BANK * cpu;
|
||||
}
|
||||
|
||||
static int realtek_gpio_irq_set_affinity(struct irq_data *data,
|
||||
@@ -267,12 +270,10 @@ static int realtek_gpio_irq_set_affinity
|
||||
{
|
||||
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;
|
||||
+ u32 v;
|
||||
|
||||
if (!ctrl->cpumask_base)
|
||||
return -ENXIO;
|
||||
@@ -280,15 +281,15 @@ static int realtek_gpio_irq_set_affinity
|
||||
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);
|
||||
+ irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, cpu);
|
||||
+ v = ctrl->bank_read(irq_cpu_mask);
|
||||
|
||||
if (cpumask_test_cpu(cpu, dest))
|
||||
- v |= BIT(port_pin);
|
||||
+ v |= BIT(line);
|
||||
else
|
||||
- v &= ~BIT(port_pin);
|
||||
+ v &= ~BIT(line);
|
||||
|
||||
- iowrite8(v, irq_cpu_mask);
|
||||
+ ctrl->bank_write(irq_cpu_mask, v);
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
||||
@@ -302,22 +303,23 @@ static int realtek_gpio_irq_init(struct
|
||||
{
|
||||
struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc);
|
||||
void __iomem *irq_cpu_mask;
|
||||
- unsigned int port;
|
||||
+ u32 mask_all = GENMASK(gc->ngpio - 1, 0);
|
||||
+ unsigned int line;
|
||||
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));
|
||||
-
|
||||
- /*
|
||||
- * Uniprocessor builds assume a mask always contains one CPU,
|
||||
- * so only start the loop if we have at least one maskable CPU.
|
||||
- */
|
||||
- if(!cpumask_empty(&ctrl->cpu_irq_maskable)) {
|
||||
- for_each_cpu(cpu, &ctrl->cpu_irq_maskable) {
|
||||
- irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, port, cpu);
|
||||
- iowrite8(GENMASK(7, 0), irq_cpu_mask);
|
||||
- }
|
||||
+ for (line = 0; line < gc->ngpio; line++)
|
||||
+ realtek_gpio_update_line_imr(ctrl, line);
|
||||
+
|
||||
+ realtek_gpio_clear_isr(ctrl, mask_all);
|
||||
+
|
||||
+ /*
|
||||
+ * Uniprocessor builds assume a mask always contains one CPU,
|
||||
+ * so only start the loop if we have at least one maskable CPU.
|
||||
+ */
|
||||
+ if(!cpumask_empty(&ctrl->cpu_irq_maskable)) {
|
||||
+ for_each_cpu(cpu, &ctrl->cpu_irq_maskable) {
|
||||
+ irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, cpu);
|
||||
+ ctrl->bank_write(irq_cpu_mask, mask_all);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,12 +392,14 @@ static int realtek_gpio_probe(struct pla
|
||||
|
||||
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;
|
||||
+ ctrl->bank_read = realtek_gpio_bank_read;
|
||||
+ ctrl->bank_write = realtek_gpio_bank_write;
|
||||
+ ctrl->line_imr_pos = realtek_gpio_line_imr_pos;
|
||||
} 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;
|
||||
+ ctrl->bank_read = realtek_gpio_bank_read_swapped;
|
||||
+ ctrl->bank_write = realtek_gpio_bank_write_swapped;
|
||||
+ ctrl->line_imr_pos = realtek_gpio_line_imr_pos_swapped;
|
||||
}
|
||||
|
||||
err = bgpio_init(&ctrl->gc, dev, 4,
|
||||
@@ -0,0 +1,33 @@
|
||||
From 800d5fb3c6a16661932c932bacd660e38d06b727 Mon Sep 17 00:00:00 2001
|
||||
From: Markus Stockhausen <markus.stockhausen@gmx.de>
|
||||
Date: Thu, 25 Aug 2022 08:22:36 +0200
|
||||
Subject: [PATCH] realtek: add patch to enable new clock driver in kernel
|
||||
|
||||
Allow building the clock driver with kernel config options.
|
||||
|
||||
Submitted-by: Markus Stockhausen <markus.stockhausen@gmx.de>
|
||||
---
|
||||
drivers/clk/Kconfig | 1 +
|
||||
drivers/clk/Makefile | 1 +
|
||||
2 files changed, 2 insertions(+)
|
||||
|
||||
--- a/drivers/clk/Kconfig
|
||||
+++ b/drivers/clk/Kconfig
|
||||
@@ -406,6 +406,7 @@ source "drivers/clk/mstar/Kconfig"
|
||||
source "drivers/clk/mvebu/Kconfig"
|
||||
source "drivers/clk/pistachio/Kconfig"
|
||||
source "drivers/clk/qcom/Kconfig"
|
||||
+source "drivers/clk/realtek/Kconfig"
|
||||
source "drivers/clk/ralink/Kconfig"
|
||||
source "drivers/clk/renesas/Kconfig"
|
||||
source "drivers/clk/rockchip/Kconfig"
|
||||
--- a/drivers/clk/Makefile
|
||||
+++ b/drivers/clk/Makefile
|
||||
@@ -101,6 +101,7 @@ obj-$(CONFIG_COMMON_CLK_PISTACHIO) += pi
|
||||
obj-$(CONFIG_COMMON_CLK_PXA) += pxa/
|
||||
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
|
||||
obj-y += ralink/
|
||||
+obj-$(CONFIG_COMMON_CLK_REALTEK) += realtek/
|
||||
obj-y += renesas/
|
||||
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
|
||||
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
|
||||
@@ -0,0 +1,159 @@
|
||||
From 2cd00b51470a30198b048a5fca48a04db77e29cc Mon Sep 17 00:00:00 2001
|
||||
From: INAGAKI Hiroshi <musashino.open@gmail.com>
|
||||
Date: Fri, 21 May 2021 23:16:37 +0900
|
||||
Subject: [PATCH] realtek: backport irq-realtek-rtl driver from 5.12 to 5.10
|
||||
|
||||
This patch backports "irq-realtek-rtl" driver to Kernel 5.10 from 5.12.
|
||||
"MACH_REALTEK_RTL" is used as a platform name in upstream, but "RTL838X"
|
||||
is used in OpenWrt, so update the dependency by the additional patch.
|
||||
|
||||
Submitted-by: INAGAKI Hiroshi <musashino.open@gmail.com>
|
||||
---
|
||||
drivers/irqchip/irq-realtek-rtl.c | 38 +++++++++++------
|
||||
1 files changed, 58 insertions(+), 20 deletions(-)
|
||||
|
||||
--- a/drivers/irqchip/irq-realtek-rtl.c
|
||||
+++ b/drivers/irqchip/irq-realtek-rtl.c
|
||||
@@ -28,6 +28,7 @@ static DEFINE_RAW_SPINLOCK(irq_lock);
|
||||
|
||||
#define REG(offset, cpu) (realtek_ictl_base[cpu] + offset)
|
||||
|
||||
+static u32 realtek_ictl_unmask[NR_CPUS];
|
||||
static void __iomem *realtek_ictl_base[NR_CPUS];
|
||||
static cpumask_t realtek_ictl_cpu_configurable;
|
||||
|
||||
@@ -41,11 +42,29 @@ struct realtek_ictl_output {
|
||||
};
|
||||
|
||||
/*
|
||||
- * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering,
|
||||
- * placing IRQ 31 in the first four bits. A routing value of '0' means the
|
||||
- * interrupt is left disconnected. Routing values {1..15} connect to output
|
||||
- * lines {0..14}.
|
||||
+ * Per CPU we have a set of 5 registers that determine interrupt handling for
|
||||
+ * 32 external interrupts. GIMR (enable/disable interrupt) plus IRR0-IRR3 that
|
||||
+ * contain "routing" or "priority" values. GIMR uses one bit for each interrupt
|
||||
+ * and IRRx store 4 bits per interrupt. Realtek uses inverted numbering,
|
||||
+ * placing IRQ 31 in the first four bits. The register combinations give the
|
||||
+ * following results for a single interrupt in the wild:
|
||||
+ *
|
||||
+ * a) GIMR = 0 / IRRx > 0 -> no interrupts
|
||||
+ * b) GIMR = 0 / IRRx = 0 -> no interrupts
|
||||
+ * c) GIMR = 1 / IRRx > 0 -> interrupts
|
||||
+ * d) GIMR = 1 / IRRx = 0 -> rare interrupts in SMP environment
|
||||
+ *
|
||||
+ * Combination d) seems to trigger interrupts only on a VPE if the other VPE
|
||||
+ * has GIMR = 0 and IRRx > 0. E.g. busy without interrupts allowed. To provide
|
||||
+ * IRQ balancing features in SMP this driver will handle the registers as
|
||||
+ * follows:
|
||||
+ *
|
||||
+ * 1) set IRRx > 0 for VPE where the interrupt is desired
|
||||
+ * 2) set IRRx = 0 for VPE where the interrupt is not desired
|
||||
+ * 3) set both GIMR = 0 to mask (disabled) interrupt
|
||||
+ * 4) set GIMR = 1 to unmask (enable) interrupt but only for VPE where IRRx > 0
|
||||
*/
|
||||
+
|
||||
#define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32))
|
||||
#define IRR_SHIFT(idx) ((idx * 4) % 32)
|
||||
|
||||
@@ -65,19 +84,33 @@ static inline void write_irr(void __iome
|
||||
writel(irr, irr0 + offset);
|
||||
}
|
||||
|
||||
+static inline void enable_gimr(int hwirq, int cpu)
|
||||
+{
|
||||
+ u32 value;
|
||||
+
|
||||
+ value = readl(REG(RTL_ICTL_GIMR, cpu));
|
||||
+ value |= (BIT(hwirq) & realtek_ictl_unmask[cpu]);
|
||||
+ writel(value, REG(RTL_ICTL_GIMR, cpu));
|
||||
+}
|
||||
+
|
||||
+static inline void disable_gimr(int hwirq, int cpu)
|
||||
+{
|
||||
+ u32 value;
|
||||
+
|
||||
+ value = readl(REG(RTL_ICTL_GIMR, cpu));
|
||||
+ value &= ~BIT(hwirq);
|
||||
+ writel(value, REG(RTL_ICTL_GIMR, cpu));
|
||||
+}
|
||||
+
|
||||
static void realtek_ictl_unmask_irq(struct irq_data *i)
|
||||
{
|
||||
unsigned long flags;
|
||||
- u32 value;
|
||||
int cpu;
|
||||
|
||||
raw_spin_lock_irqsave(&irq_lock, flags);
|
||||
|
||||
- for_each_cpu(cpu, &realtek_ictl_cpu_configurable) {
|
||||
- value = readl(REG(RTL_ICTL_GIMR, cpu));
|
||||
- value |= BIT(i->hwirq);
|
||||
- writel(value, REG(RTL_ICTL_GIMR, cpu));
|
||||
- }
|
||||
+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable)
|
||||
+ enable_gimr(i->hwirq, cpu);
|
||||
|
||||
raw_spin_unlock_irqrestore(&irq_lock, flags);
|
||||
}
|
||||
@@ -85,16 +118,12 @@ static void realtek_ictl_unmask_irq(stru
|
||||
static void realtek_ictl_mask_irq(struct irq_data *i)
|
||||
{
|
||||
unsigned long flags;
|
||||
- u32 value;
|
||||
int cpu;
|
||||
|
||||
raw_spin_lock_irqsave(&irq_lock, flags);
|
||||
|
||||
- for_each_cpu(cpu, &realtek_ictl_cpu_configurable) {
|
||||
- value = readl(REG(RTL_ICTL_GIMR, cpu));
|
||||
- value &= ~BIT(i->hwirq);
|
||||
- writel(value, REG(RTL_ICTL_GIMR, cpu));
|
||||
- }
|
||||
+ for_each_cpu(cpu, &realtek_ictl_cpu_configurable)
|
||||
+ disable_gimr(i->hwirq, cpu);
|
||||
|
||||
raw_spin_unlock_irqrestore(&irq_lock, flags);
|
||||
}
|
||||
@@ -116,11 +145,17 @@ static int __maybe_unused realtek_ictl_i
|
||||
cpumask_and(&cpu_enable, &cpu_configure, dest);
|
||||
cpumask_andnot(&cpu_disable, &cpu_configure, dest);
|
||||
|
||||
- for_each_cpu(cpu, &cpu_disable)
|
||||
+ for_each_cpu(cpu, &cpu_disable) {
|
||||
write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, 0);
|
||||
+ realtek_ictl_unmask[cpu] &= ~BIT(i->hwirq);
|
||||
+ disable_gimr(i->hwirq, cpu);
|
||||
+ }
|
||||
|
||||
- for_each_cpu(cpu, &cpu_enable)
|
||||
+ for_each_cpu(cpu, &cpu_enable) {
|
||||
write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, output->output_index + 1);
|
||||
+ realtek_ictl_unmask[cpu] |= BIT(i->hwirq);
|
||||
+ enable_gimr(i->hwirq, cpu);
|
||||
+ }
|
||||
|
||||
irq_data_update_effective_affinity(i, &cpu_enable);
|
||||
|
||||
@@ -149,6 +184,7 @@ static int intc_map(struct irq_domain *d
|
||||
|
||||
output->child_mask |= BIT(hw);
|
||||
write_irr(REG(RTL_ICTL_IRR0, 0), hw, output->output_index + 1);
|
||||
+ realtek_ictl_unmask[0] |= BIT(hw);
|
||||
|
||||
raw_spin_unlock_irqrestore(&irq_lock, flags);
|
||||
|
||||
@@ -285,9 +321,11 @@ static int __init realtek_rtl_of_init(st
|
||||
cpumask_set_cpu(cpu, &realtek_ictl_cpu_configurable);
|
||||
|
||||
/* Disable all cascaded interrupts and clear routing */
|
||||
- writel(0, REG(RTL_ICTL_GIMR, cpu));
|
||||
- for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++)
|
||||
+ for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) {
|
||||
write_irr(REG(RTL_ICTL_IRR0, cpu), soc_irq, 0);
|
||||
+ realtek_ictl_unmask[cpu] &= ~BIT(soc_irq);
|
||||
+ disable_gimr(soc_irq, cpu);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <john@phrozen.org>
|
||||
Date: Thu, 26 Nov 2020 12:02:21 +0100
|
||||
Subject: net: dsa: Add support for rtl838x switch
|
||||
|
||||
* rename the target to realtek
|
||||
* add refactored DSA driver
|
||||
* add latest gpio driver
|
||||
* lots of arch cleanups
|
||||
* new irq driver
|
||||
* additional boards
|
||||
|
||||
Submitted-by: Bert Vermeulen <bert@biot.com>
|
||||
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
|
||||
Submitted-by: Sander Vanheule <sander@svanheule.net>
|
||||
Submitted-by: Bjørn Mork <bjorn@mork.no>
|
||||
Submitted-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
drivers/net/dsa/rtl83xx/Kconfig | 2 ++
|
||||
drivers/net/dsa/rtl83xx/Makefile | 1 +
|
||||
2 files changed, 3 insertions(+)
|
||||
|
||||
--- a/drivers/net/dsa/Kconfig
|
||||
+++ b/drivers/net/dsa/Kconfig
|
||||
@@ -85,6 +85,8 @@ source "drivers/net/dsa/sja1105/Kconfig"
|
||||
|
||||
source "drivers/net/dsa/xrs700x/Kconfig"
|
||||
|
||||
+source "drivers/net/dsa/rtl83xx/Kconfig"
|
||||
+
|
||||
config NET_DSA_REALTEK_SMI
|
||||
tristate "Realtek SMI Ethernet switch family support"
|
||||
select NET_DSA_TAG_RTL4_A
|
||||
--- a/drivers/net/dsa/Makefile
|
||||
+++ b/drivers/net/dsa/Makefile
|
||||
@@ -24,5 +24,6 @@ obj-y += microchip/
|
||||
obj-y += mv88e6xxx/
|
||||
obj-y += ocelot/
|
||||
obj-y += qca/
|
||||
+obj-y += rtl83xx/
|
||||
obj-y += sja1105/
|
||||
obj-y += xrs700x/
|
||||
@@ -0,0 +1,61 @@
|
||||
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <john@phrozen.org>
|
||||
Date: Thu, 26 Nov 2020 12:02:21 +0100
|
||||
Subject: net: dsa: Add rtl838x support for tag trailer
|
||||
|
||||
* rename the target to realtek
|
||||
* add refactored DSA driver
|
||||
* add latest gpio driver
|
||||
* lots of arch cleanups
|
||||
* new irq driver
|
||||
* additional boards
|
||||
|
||||
Submitted-by: Bert Vermeulen <bert@biot.com>
|
||||
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
|
||||
Submitted-by: Sander Vanheule <sander@svanheule.net>
|
||||
Submitted-by: Bjørn Mork <bjorn@mork.no>
|
||||
Submitted-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
net/dsa/tag_trailer.c | 16 +++++++++++++-
|
||||
1 file changed, 17 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/net/dsa/tag_trailer.c
|
||||
+++ b/net/dsa/tag_trailer.c
|
||||
@@ -17,7 +17,12 @@ static struct sk_buff *trailer_xmit(stru
|
||||
|
||||
trailer = skb_put(skb, 4);
|
||||
trailer[0] = 0x80;
|
||||
+
|
||||
+#ifdef CONFIG_NET_DSA_RTL83XX
|
||||
+ trailer[1] = dp->index;
|
||||
+#else
|
||||
trailer[1] = 1 << dp->index;
|
||||
+#endif /* CONFIG_NET_DSA_RTL838X */
|
||||
trailer[2] = 0x10;
|
||||
trailer[3] = 0x00;
|
||||
|
||||
@@ -33,12 +38,23 @@ static struct sk_buff *trailer_rcv(struc
|
||||
return NULL;
|
||||
|
||||
trailer = skb_tail_pointer(skb) - 4;
|
||||
+
|
||||
+#ifdef CONFIG_NET_DSA_RTL83XX
|
||||
+ if (trailer[0] != 0x80 || (trailer[1] & 0x80) != 0x00 ||
|
||||
+ (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00)
|
||||
+ return NULL;
|
||||
+
|
||||
+ if (trailer[1] & 0x40)
|
||||
+ skb->offload_fwd_mark = 1;
|
||||
+
|
||||
+ source_port = trailer[1] & 0x3f;
|
||||
+#else
|
||||
if (trailer[0] != 0x80 || (trailer[1] & 0xf8) != 0x00 ||
|
||||
(trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00)
|
||||
return NULL;
|
||||
|
||||
source_port = trailer[1] & 7;
|
||||
-
|
||||
+#endif
|
||||
skb->dev = dsa_master_find_slave(dev, 0, source_port);
|
||||
if (!skb->dev)
|
||||
return NULL;
|
||||
@@ -0,0 +1,32 @@
|
||||
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <john@phrozen.org>
|
||||
Date: Thu, 26 Nov 2020 12:02:21 +0100
|
||||
Subject: net: dsa: Increase max ports for rtl838x
|
||||
|
||||
* rename the target to realtek
|
||||
* add refactored DSA driver
|
||||
* add latest gpio driver
|
||||
* lots of arch cleanups
|
||||
* new irq driver
|
||||
* additional boards
|
||||
|
||||
Submitted-by: Bert Vermeulen <bert@biot.com>
|
||||
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
|
||||
Submitted-by: Sander Vanheule <sander@svanheule.net>
|
||||
Submitted-by: Bjørn Mork <bjorn@mork.no>
|
||||
Submitted-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
include/linux/platform_data/dsa.h | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/include/linux/platform_data/dsa.h
|
||||
+++ b/include/linux/platform_data/dsa.h
|
||||
@@ -6,7 +6,7 @@ struct device;
|
||||
struct net_device;
|
||||
|
||||
#define DSA_MAX_SWITCHES 4
|
||||
-#define DSA_MAX_PORTS 12
|
||||
+#define DSA_MAX_PORTS 54
|
||||
#define DSA_RTABLE_NONE -1
|
||||
|
||||
struct dsa_chip_data {
|
||||
@@ -0,0 +1,48 @@
|
||||
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <john@phrozen.org>
|
||||
Date: Thu, 26 Nov 2020 12:02:21 +0100
|
||||
Subject: net: ethernet: Add support for RTL838x ethernet
|
||||
|
||||
* rename the target to realtek
|
||||
* add refactored DSA driver
|
||||
* add latest gpio driver
|
||||
* lots of arch cleanups
|
||||
* new irq driver
|
||||
* additional boards
|
||||
|
||||
Submitted-by: Bert Vermeulen <bert@biot.com>
|
||||
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
|
||||
Submitted-by: Sander Vanheule <sander@svanheule.net>
|
||||
Submitted-by: Bjørn Mork <bjorn@mork.no>
|
||||
Submitted-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
drivers/net/ethernet/Kconfig | 7 +-
|
||||
drivers/net/ethernet/Makefile | 1 +
|
||||
2 files changed, 8 insertions(+)
|
||||
|
||||
--- a/drivers/net/ethernet/Kconfig
|
||||
+++ b/drivers/net/ethernet/Kconfig
|
||||
@@ -166,6 +166,13 @@ source "drivers/net/ethernet/rdc/Kconfig
|
||||
source "drivers/net/ethernet/realtek/Kconfig"
|
||||
source "drivers/net/ethernet/renesas/Kconfig"
|
||||
source "drivers/net/ethernet/rocker/Kconfig"
|
||||
+
|
||||
+config NET_RTL838X
|
||||
+ tristate "Realtek rtl838x Ethernet MAC support"
|
||||
+ depends on RTL83XX
|
||||
+ help
|
||||
+ Say Y here if you want to use the Realtek rtl838x Gbps Ethernet MAC.
|
||||
+
|
||||
source "drivers/net/ethernet/samsung/Kconfig"
|
||||
source "drivers/net/ethernet/seeq/Kconfig"
|
||||
source "drivers/net/ethernet/sgi/Kconfig"
|
||||
--- a/drivers/net/ethernet/Makefile
|
||||
+++ b/drivers/net/ethernet/Makefile
|
||||
@@ -77,6 +77,7 @@ obj-$(CONFIG_NET_VENDOR_REALTEK) += real
|
||||
obj-$(CONFIG_NET_VENDOR_RENESAS) += renesas/
|
||||
obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
|
||||
obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/
|
||||
+obj-$(CONFIG_NET_RTL838X) += rtl838x_eth.o
|
||||
obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/
|
||||
obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
|
||||
obj-$(CONFIG_NET_VENDOR_SILAN) += silan/
|
||||
@@ -0,0 +1,34 @@
|
||||
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <john@phrozen.org>
|
||||
Date: Thu, 26 Nov 2020 12:02:21 +0100
|
||||
Subject: phy: Add PHY ops for rtl838x EEE
|
||||
|
||||
* rename the target to realtek
|
||||
* add refactored DSA driver
|
||||
* add latest gpio driver
|
||||
* lots of arch cleanups
|
||||
* new irq driver
|
||||
* additional boards
|
||||
|
||||
Submitted-by: Bert Vermeulen <bert@biot.com>
|
||||
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
|
||||
Submitted-by: Sander Vanheule <sander@svanheule.net>
|
||||
Submitted-by: Bjørn Mork <bjorn@mork.no>
|
||||
Submitted-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
include/linux/phy.h | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -1009,6 +1009,10 @@ struct phy_driver {
|
||||
int (*led_blink_set)(struct phy_device *dev, u8 index,
|
||||
unsigned long *delay_on,
|
||||
unsigned long *delay_off);
|
||||
+ int (*get_port)(struct phy_device *dev);
|
||||
+ int (*set_port)(struct phy_device *dev, int port);
|
||||
+ int (*get_eee)(struct phy_device *dev, struct ethtool_eee *e);
|
||||
+ int (*set_eee)(struct phy_device *dev, struct ethtool_eee *e);
|
||||
};
|
||||
#define to_phy_driver(d) container_of(to_mdio_common_driver(d), \
|
||||
struct phy_driver, mdiodrv)
|
||||
@@ -0,0 +1,61 @@
|
||||
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <john@phrozen.org>
|
||||
Date: Thu, 26 Nov 2020 12:02:21 +0100
|
||||
Subject: net: phy: EEE support for rtl838x
|
||||
|
||||
* rename the target to realtek
|
||||
* add refactored DSA driver
|
||||
* add latest gpio driver
|
||||
* lots of arch cleanups
|
||||
* new irq driver
|
||||
* additional boards
|
||||
|
||||
Submitted-by: Bert Vermeulen <bert@biot.com>
|
||||
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
|
||||
Submitted-by: Sander Vanheule <sander@svanheule.net>
|
||||
Submitted-by: Bjørn Mork <bjorn@mork.no>
|
||||
Submitted-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
drivers/net/phy/phylink. | 14 +++++++++++--
|
||||
1 file changed, 12 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -1994,6 +1994,11 @@ int phylink_ethtool_ksettings_set(struct
|
||||
* the presence of a PHY, this should not be changed as that
|
||||
* should be determined from the media side advertisement.
|
||||
*/
|
||||
+ if (pl->phydev->drv->get_port && pl->phydev->drv->set_port) {
|
||||
+ if(pl->phydev->drv->get_port(pl->phydev) != kset->base.port) {
|
||||
+ pl->phydev->drv->set_port(pl->phydev, kset->base.port);
|
||||
+ }
|
||||
+ }
|
||||
return phy_ethtool_ksettings_set(pl->phydev, kset);
|
||||
}
|
||||
|
||||
@@ -2297,8 +2302,11 @@ int phylink_ethtool_get_eee(struct phyli
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
- if (pl->phydev)
|
||||
+ if (pl->phydev) {
|
||||
+ if (pl->phydev->drv->get_eee)
|
||||
+ return pl->phydev->drv->get_eee(pl->phydev, eee);
|
||||
ret = phy_ethtool_get_eee(pl->phydev, eee);
|
||||
+ }
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -2315,8 +2323,11 @@ int phylink_ethtool_set_eee(struct phyli
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
- if (pl->phydev)
|
||||
+ if (pl->phydev) {
|
||||
+ if (pl->phydev->drv->set_eee)
|
||||
+ return pl->phydev->drv->set_eee(pl->phydev, eee);
|
||||
ret = phy_ethtool_set_eee(pl->phydev, eee);
|
||||
+ }
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
From 9d9bf16aa8d966834ac1280f96c37d22552c33d1 Mon Sep 17 00:00:00 2001
|
||||
From: Birger Koblitz <git@birger-koblitz.de>
|
||||
Date: Wed, 8 Sep 2021 16:13:18 +0200
|
||||
Subject: phy: Add PHY hsgmii mode
|
||||
|
||||
This adds RTL93xx-specific MAC configuration routines that allow also configuration
|
||||
of 10GBit links for phylink. There is support for the Realtek-specific HISGMI
|
||||
protocol.
|
||||
|
||||
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
|
||||
---
|
||||
drivers/net/phy/phylink.c | 2 ++
|
||||
include/linux/phy.h | 3 +++
|
||||
2 file changed, 5 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/phy-core.c
|
||||
+++ b/drivers/net/phy/phy-core.c
|
||||
@@ -124,6 +124,7 @@ int phy_interface_num_ports(phy_interfac
|
||||
case PHY_INTERFACE_MODE_MOCA:
|
||||
case PHY_INTERFACE_MODE_TRGMII:
|
||||
case PHY_INTERFACE_MODE_USXGMII:
|
||||
+ case PHY_INTERFACE_MODE_HSGMII:
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_SMII:
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -410,6 +410,7 @@ void phylink_get_linkmodes(unsigned long
|
||||
|
||||
case PHY_INTERFACE_MODE_XGMII:
|
||||
case PHY_INTERFACE_MODE_RXAUI:
|
||||
+ case PHY_INTERFACE_MODE_HSGMII:
|
||||
case PHY_INTERFACE_MODE_XAUI:
|
||||
case PHY_INTERFACE_MODE_10GBASER:
|
||||
case PHY_INTERFACE_MODE_10GKR:
|
||||
@@ -665,6 +666,7 @@ static int phylink_parse_mode(struct phy
|
||||
fallthrough;
|
||||
case PHY_INTERFACE_MODE_USXGMII:
|
||||
case PHY_INTERFACE_MODE_10GKR:
|
||||
+ case PHY_INTERFACE_MODE_HSGMII:
|
||||
case PHY_INTERFACE_MODE_10GBASER:
|
||||
phylink_set(pl->supported, 10baseT_Half);
|
||||
phylink_set(pl->supported, 10baseT_Full);
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -141,6 +141,7 @@ typedef enum {
|
||||
PHY_INTERFACE_MODE_XGMII,
|
||||
PHY_INTERFACE_MODE_XLGMII,
|
||||
PHY_INTERFACE_MODE_MOCA,
|
||||
+ PHY_INTERFACE_MODE_HSGMII,
|
||||
PHY_INTERFACE_MODE_QSGMII,
|
||||
PHY_INTERFACE_MODE_TRGMII,
|
||||
PHY_INTERFACE_MODE_100BASEX,
|
||||
@@ -248,6 +249,8 @@ static inline const char *phy_modes(phy_
|
||||
return "xlgmii";
|
||||
case PHY_INTERFACE_MODE_MOCA:
|
||||
return "moca";
|
||||
+ case PHY_INTERFACE_MODE_HSGMII:
|
||||
+ return "hsgmii";
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
return "qsgmii";
|
||||
case PHY_INTERFACE_MODE_TRGMII:
|
||||
39
target/linux/realtek/patches-5.15/705-add-rtl-phy.patch
Normal file
39
target/linux/realtek/patches-5.15/705-add-rtl-phy.patch
Normal file
@@ -0,0 +1,39 @@
|
||||
From 89f71ebb355c624320c2b0ace8ae9488ff53cbeb Mon Sep 17 00:00:00 2001
|
||||
From: Birger Koblitz <mail@birger-koblitz.de>
|
||||
Date: Tue, 5 Jan 2021 20:40:52 +0100
|
||||
Subject: PHY: Add realtek PHY
|
||||
|
||||
This fixes the build problems for the REALTEK target by adding a proper
|
||||
configuration option for the phy module.
|
||||
|
||||
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
|
||||
---
|
||||
drivers/net/phy/Kconfig | 6 ++++++
|
||||
drivers/net/phy/Makefile | 1 +
|
||||
2 files changed, 7 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/Kconfig
|
||||
+++ b/drivers/net/phy/Kconfig
|
||||
@@ -354,6 +354,12 @@ config REALTEK_PHY
|
||||
help
|
||||
Supports the Realtek 821x PHY.
|
||||
|
||||
+config REALTEK_SOC_PHY
|
||||
+ tristate "Realtek SoC PHYs"
|
||||
+ depends on RTL83XX
|
||||
+ help
|
||||
+ Supports the PHYs found in combination with Realtek Switch SoCs
|
||||
+
|
||||
config RENESAS_PHY
|
||||
tristate "Renesas PHYs"
|
||||
help
|
||||
--- a/drivers/net/phy/Makefile
|
||||
+++ b/drivers/net/phy/Makefile
|
||||
@@ -89,6 +89,7 @@ obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp
|
||||
obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
|
||||
obj-$(CONFIG_QSEMI_PHY) += qsemi.o
|
||||
obj-$(CONFIG_REALTEK_PHY) += realtek.o
|
||||
+obj-$(CONFIG_REALTEK_SOC_PHY) += rtl83xx-phy.o
|
||||
obj-$(CONFIG_RENESAS_PHY) += uPD60620.o
|
||||
obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o
|
||||
obj-$(CONFIG_SMSC_PHY) += smsc.o
|
||||
@@ -0,0 +1,32 @@
|
||||
From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <john@phrozen.org>
|
||||
Date: Thu, 26 Nov 2020 12:02:21 +0100
|
||||
Subject: PHY: Increase max PHY adddress number
|
||||
|
||||
* rename the target to realtek
|
||||
* add refactored DSA driver
|
||||
* add latest gpio driver
|
||||
* lots of arch cleanups
|
||||
* new irq driver
|
||||
* additional boards
|
||||
|
||||
Submitted-by: Bert Vermeulen <bert@biot.com>
|
||||
Submitted-by: Birger Koblitz <mail@birger-koblitz.de>
|
||||
Submitted-by: Sander Vanheule <sander@svanheule.net>
|
||||
Submitted-by: Bjørn Mork <bjorn@mork.no>
|
||||
Submitted-by: John Crispin <john@phrozen.org>
|
||||
---
|
||||
include/linux/phy.h | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -287,7 +287,7 @@ static inline const char *phy_modes(phy_
|
||||
#define PHY_INIT_TIMEOUT 100000
|
||||
#define PHY_FORCE_TIMEOUT 10
|
||||
|
||||
-#define PHY_MAX_ADDR 32
|
||||
+#define PHY_MAX_ADDR 64
|
||||
|
||||
/* Used when trying to connect to a specific phy (mii bus id:phy device id) */
|
||||
#define PHY_ID_FMT "%s:%02x"
|
||||
@@ -0,0 +1,26 @@
|
||||
From a381ac0aa281fdb0b41a39d8a2bc08fd88f6db92 Mon Sep 17 00:00:00 2001
|
||||
From: Antoine Tenart <antoine.tenart@bootlin.com>
|
||||
Date: Tue, 25 Feb 2020 16:32:37 +0100
|
||||
Subject: [PATCH 1/3] net: phy: sfp: re-probe modules on DEV_UP event
|
||||
|
||||
Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
|
||||
---
|
||||
drivers/net/phy/sfp.c | 7 +++++++
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/sfp.c
|
||||
+++ b/drivers/net/phy/sfp.c
|
||||
@@ -2160,6 +2160,13 @@ static void sfp_sm_module(struct sfp *sf
|
||||
return;
|
||||
}
|
||||
|
||||
+ /* Re-probe the SFP modules when an interface is brought up, as the MAC
|
||||
+ * do not report its link status (This means Phylink wouldn't be
|
||||
+ * triggered if the PHY had a link before a MAC is brought up).
|
||||
+ */
|
||||
+ if (event == SFP_E_DEV_UP && sfp->sm_mod_state == SFP_MOD_PRESENT)
|
||||
+ sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_SERIAL);
|
||||
+
|
||||
switch (sfp->sm_mod_state) {
|
||||
default:
|
||||
if (event == SFP_E_INSERT) {
|
||||
@@ -0,0 +1,148 @@
|
||||
From d585c55b9f70cf9e8c66820d7efe7130c683f19e Mon Sep 17 00:00:00 2001
|
||||
From: Antoine Tenart <antoine.tenart@bootlin.com>
|
||||
Date: Fri, 21 Feb 2020 11:51:27 +0100
|
||||
Subject: [PATCH 2/3] net: phy: add an MDIO SMBus library
|
||||
|
||||
Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
|
||||
---
|
||||
drivers/net/mdio/Kconfig | 11 +++++++
|
||||
drivers/net/mdio/Makefile | 1 +
|
||||
drivers/net/mdio/mdio-smbus.c | 62 +++++++++++++++++++++++++++++++++++
|
||||
drivers/net/phy/Kconfig | 1 +
|
||||
include/linux/mdio/mdio-i2c.h | 16 +++++++++
|
||||
5 files changed, 91 insertions(+)
|
||||
create mode 100644 drivers/net/mdio/mdio-smbus.c
|
||||
|
||||
--- a/drivers/net/mdio/Kconfig
|
||||
+++ b/drivers/net/mdio/Kconfig
|
||||
@@ -54,6 +54,17 @@ config MDIO_SUN4I
|
||||
interface units of the Allwinner SoC that have an EMAC (A10,
|
||||
A12, A10s, etc.)
|
||||
|
||||
+config MDIO_SMBUS
|
||||
+ tristate
|
||||
+ depends on I2C_SMBUS
|
||||
+ help
|
||||
+ Support SMBus based PHYs. This provides a MDIO bus bridged
|
||||
+ to SMBus to allow PHYs connected in SMBus mode to be accessed
|
||||
+ using the existing infrastructure.
|
||||
+
|
||||
+ This is library mode.
|
||||
+
|
||||
+
|
||||
config MDIO_XGENE
|
||||
tristate "APM X-Gene SoC MDIO bus controller"
|
||||
depends on ARCH_XGENE || COMPILE_TEST
|
||||
--- a/drivers/net/mdio/Makefile
|
||||
+++ b/drivers/net/mdio/Makefile
|
||||
@@ -19,6 +19,7 @@ obj-$(CONFIG_MDIO_MOXART) += mdio-moxar
|
||||
obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-mscc-miim.o
|
||||
obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o
|
||||
obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o
|
||||
+obj-$(CONFIG_MDIO_SMBUS) += mdio-smbus.o
|
||||
obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
|
||||
obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o
|
||||
obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/mdio/mdio-smbus.c
|
||||
@@ -0,0 +1,62 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+/*
|
||||
+ * MDIO SMBus bridge
|
||||
+ *
|
||||
+ * Copyright (C) 2020 Antoine Tenart
|
||||
+ *
|
||||
+ * Network PHYs can appear on SMBus when they are part of SFP modules.
|
||||
+ */
|
||||
+#include <linux/i2c.h>
|
||||
+#include <linux/phy.h>
|
||||
+#include <linux/mdio/mdio-i2c.h>
|
||||
+
|
||||
+static int smbus_mii_read(struct mii_bus *mii, int phy_id, int reg)
|
||||
+{
|
||||
+ struct i2c_adapter *i2c = mii->priv;
|
||||
+ union i2c_smbus_data data;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = i2c_smbus_xfer(i2c, i2c_mii_phy_addr(phy_id), 0, I2C_SMBUS_READ,
|
||||
+ reg, I2C_SMBUS_BYTE_DATA, &data);
|
||||
+ if (ret < 0)
|
||||
+ return 0xff;
|
||||
+
|
||||
+ return data.byte;
|
||||
+}
|
||||
+
|
||||
+static int smbus_mii_write(struct mii_bus *mii, int phy_id, int reg, u16 val)
|
||||
+{
|
||||
+ struct i2c_adapter *i2c = mii->priv;
|
||||
+ union i2c_smbus_data data;
|
||||
+ int ret;
|
||||
+
|
||||
+ data.byte = val;
|
||||
+
|
||||
+ ret = i2c_smbus_xfer(i2c, i2c_mii_phy_addr(phy_id), 0, I2C_SMBUS_WRITE,
|
||||
+ reg, I2C_SMBUS_BYTE_DATA, &data);
|
||||
+ return ret < 0 ? ret : 0;
|
||||
+}
|
||||
+
|
||||
+struct mii_bus *mdio_smbus_alloc(struct device *parent, struct i2c_adapter *i2c)
|
||||
+{
|
||||
+ struct mii_bus *mii;
|
||||
+
|
||||
+ if (!i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+
|
||||
+ mii = mdiobus_alloc();
|
||||
+ if (!mii)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ snprintf(mii->id, MII_BUS_ID_SIZE, "smbus:%s", dev_name(parent));
|
||||
+ mii->parent = parent;
|
||||
+ mii->read = smbus_mii_read;
|
||||
+ mii->write = smbus_mii_write;
|
||||
+ mii->priv = i2c;
|
||||
+
|
||||
+ return mii;
|
||||
+}
|
||||
+
|
||||
+MODULE_AUTHOR("Antoine Tenart");
|
||||
+MODULE_DESCRIPTION("MDIO SMBus bridge library");
|
||||
+MODULE_LICENSE("GPL");
|
||||
--- a/drivers/net/phy/Kconfig
|
||||
+++ b/drivers/net/phy/Kconfig
|
||||
@@ -61,6 +61,7 @@ config SFP
|
||||
depends on I2C && PHYLINK
|
||||
depends on HWMON || HWMON=n
|
||||
select MDIO_I2C
|
||||
+ select MDIO_SMBUS
|
||||
|
||||
comment "Switch configuration API + drivers"
|
||||
|
||||
--- a/include/linux/mdio/mdio-i2c.h
|
||||
+++ b/include/linux/mdio/mdio-i2c.h
|
||||
@@ -12,5 +12,8 @@ struct i2c_adapter;
|
||||
struct mii_bus;
|
||||
|
||||
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c);
|
||||
+struct mii_bus *mdio_smbus_alloc(struct device *parent, struct i2c_adapter *i2c);
|
||||
+bool i2c_mii_valid_phy_id(int phy_id);
|
||||
+unsigned int i2c_mii_phy_addr(int phy_id);
|
||||
|
||||
#endif
|
||||
--- a/drivers/net/mdio/mdio-i2c.c
|
||||
+++ b/drivers/net/mdio/mdio-i2c.c
|
||||
@@ -18,12 +18,12 @@
|
||||
* specified to be present in SFP modules. These correspond with PHY
|
||||
* addresses 16 and 17. Disallow access to these "phy" addresses.
|
||||
*/
|
||||
-static bool i2c_mii_valid_phy_id(int phy_id)
|
||||
+bool i2c_mii_valid_phy_id(int phy_id)
|
||||
{
|
||||
return phy_id != 0x10 && phy_id != 0x11;
|
||||
}
|
||||
|
||||
-static unsigned int i2c_mii_phy_addr(int phy_id)
|
||||
+unsigned int i2c_mii_phy_addr(int phy_id)
|
||||
{
|
||||
return phy_id + 0x40;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
From 3cb0bde365d913c484d20224367a54a0eac780a7 Mon Sep 17 00:00:00 2001
|
||||
From: Antoine Tenart <antoine.tenart@bootlin.com>
|
||||
Date: Fri, 21 Feb 2020 11:55:29 +0100
|
||||
Subject: [PATCH 3/3] net: phy: sfp: add support for SMBus
|
||||
|
||||
Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
|
||||
---
|
||||
drivers/net/phy/sfp.c | 68 ++++++++++++++++++++++++++++++++++---------
|
||||
1 file changed, 54 insertions(+), 14 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/sfp.c
|
||||
+++ b/drivers/net/phy/sfp.c
|
||||
@@ -556,32 +556,72 @@ static int sfp_i2c_write(struct sfp *sfp
|
||||
return ret == ARRAY_SIZE(msgs) ? len : 0;
|
||||
}
|
||||
|
||||
+static int sfp_smbus_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
|
||||
+ size_t len)
|
||||
+{
|
||||
+ u8 bus_addr = a2 ? 0x51 : 0x50, *val = buf;
|
||||
+
|
||||
+ bus_addr -= 0x40;
|
||||
+
|
||||
+ while (len > 0) {
|
||||
+ *val = sfp->i2c_mii->read(sfp->i2c_mii, bus_addr, dev_addr);
|
||||
+
|
||||
+ val++;
|
||||
+ dev_addr++;
|
||||
+ len--;
|
||||
+ }
|
||||
+
|
||||
+ return val - (u8 *)buf;
|
||||
+}
|
||||
+
|
||||
+static int sfp_smbus_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
|
||||
+ size_t len)
|
||||
+{
|
||||
+ u8 bus_addr = a2 ? 0x51 : 0x50;
|
||||
+ u16 val;
|
||||
+
|
||||
+ memcpy(&val, buf, len);
|
||||
+
|
||||
+ return sfp->i2c_mii->write(sfp->i2c_mii, bus_addr, dev_addr, val);
|
||||
+}
|
||||
+
|
||||
static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
|
||||
{
|
||||
- struct mii_bus *i2c_mii;
|
||||
+ struct mii_bus *mii;
|
||||
int ret;
|
||||
|
||||
- if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
|
||||
- return -EINVAL;
|
||||
-
|
||||
sfp->i2c = i2c;
|
||||
- sfp->read = sfp_i2c_read;
|
||||
- sfp->write = sfp_i2c_write;
|
||||
|
||||
- i2c_mii = mdio_i2c_alloc(sfp->dev, i2c);
|
||||
- if (IS_ERR(i2c_mii))
|
||||
- return PTR_ERR(i2c_mii);
|
||||
+ if (i2c_check_functionality(i2c, I2C_FUNC_I2C)) {
|
||||
+ sfp->read = sfp_i2c_read;
|
||||
+ sfp->write = sfp_i2c_write;
|
||||
+
|
||||
+ mii = mdio_i2c_alloc(sfp->dev, i2c);
|
||||
+ if (IS_ERR(mii))
|
||||
+ return PTR_ERR(mii);
|
||||
+
|
||||
+ mii->name = "SFP I2C Bus";
|
||||
+ } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
+ sfp->read = sfp_smbus_read;
|
||||
+ sfp->write = sfp_smbus_write;
|
||||
+
|
||||
+ mii = mdio_smbus_alloc(sfp->dev, i2c);
|
||||
+ if (IS_ERR(mii))
|
||||
+ return PTR_ERR(mii);
|
||||
|
||||
- i2c_mii->name = "SFP I2C Bus";
|
||||
- i2c_mii->phy_mask = ~0;
|
||||
+ mii->name = "SFP SMBus";
|
||||
+ } else {
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
|
||||
- ret = mdiobus_register(i2c_mii);
|
||||
+ mii->phy_mask = ~0;
|
||||
+ ret = mdiobus_register(mii);
|
||||
if (ret < 0) {
|
||||
- mdiobus_free(i2c_mii);
|
||||
+ mdiobus_free(mii);
|
||||
return ret;
|
||||
}
|
||||
|
||||
- sfp->i2c_mii = i2c_mii;
|
||||
+ sfp->i2c_mii = mii;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,840 @@
|
||||
From 5d84f16b0036b33487b94abef15ad3c224c81ee9 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Thu, 3 Feb 2022 16:38:50 +0000
|
||||
Subject: [PATCH] net: mdio: support hardware-assisted indirect access
|
||||
|
||||
MDIO controllers found in Switch-SoCs can offload some MDIO operations
|
||||
to the hardware:
|
||||
* MMD register access via Clause-22
|
||||
Instead of using multiple operations to access MMD registers via
|
||||
MII register MII_MMD_CTRL and MII_MMD_DATA some controllers
|
||||
allow transparent access to MMD PHY registers.
|
||||
|
||||
* paged MII register access
|
||||
Some PHYs (namely RealTek and Vitesse) use vendor-defined MII
|
||||
register 0x1f for paged access. Some MDIO host controllers support
|
||||
transparent paged access when used with such PHYs.
|
||||
|
||||
* add convenience accessors to fully support paged access also on
|
||||
multi-PHY packages (like the embedded PHYs in RTL83xx):
|
||||
phy_package_read_paged and phy_package_write_paged
|
||||
phy_package_port_read and phy_package_port_write
|
||||
phy_package_port_read_paged and phy_package_port_write_paged
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
drivers/net/phy/mdio_bus.c | 335 ++++++++++++++++++++++++++++++++++++-
|
||||
drivers/net/phy/phy-core.c | 66 +++++++-
|
||||
include/linux/mdio.h | 59 +++++++
|
||||
include/linux/phy.h | 129 ++++++++++++++
|
||||
include/uapi/linux/mii.h | 1 +
|
||||
5 files changed, 580 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/mdio_bus.c
|
||||
+++ b/drivers/net/phy/mdio_bus.c
|
||||
@@ -742,6 +742,32 @@ out:
|
||||
}
|
||||
|
||||
/**
|
||||
+ * __mdiobus_select_page - Unlocked version of the mdiobus_select_page function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @page: register page to select
|
||||
+ *
|
||||
+ * Selects a MDIO bus register page. Caller must hold the mdio bus lock.
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context.
|
||||
+ */
|
||||
+int __mdiobus_select_page(struct mii_bus *bus, int addr, u16 page)
|
||||
+{
|
||||
+ lockdep_assert_held_once(&bus->mdio_lock);
|
||||
+
|
||||
+ if (bus->selected_page[addr] == page)
|
||||
+ return 0;
|
||||
+
|
||||
+ bus->selected_page[addr] = page;
|
||||
+ if (bus->read_paged)
|
||||
+ return 0;
|
||||
+
|
||||
+ return bus->write(bus, addr, MII_MAINPAGE, page);
|
||||
+
|
||||
+}
|
||||
+EXPORT_SYMBOL(__mdiobus_select_page);
|
||||
+
|
||||
+/**
|
||||
* __mdiobus_read - Unlocked version of the mdiobus_read function
|
||||
* @bus: the mii_bus struct
|
||||
* @addr: the phy address
|
||||
@@ -757,7 +783,10 @@ int __mdiobus_read(struct mii_bus *bus,
|
||||
|
||||
lockdep_assert_held_once(&bus->mdio_lock);
|
||||
|
||||
- retval = bus->read(bus, addr, regnum);
|
||||
+ if (bus->read_paged)
|
||||
+ retval = bus->read_paged(bus, addr, bus->selected_page[addr], regnum);
|
||||
+ else
|
||||
+ retval = bus->read(bus, addr, regnum);
|
||||
|
||||
trace_mdio_access(bus, 1, addr, regnum, retval, retval);
|
||||
mdiobus_stats_acct(&bus->stats[addr], true, retval);
|
||||
@@ -767,6 +796,40 @@ int __mdiobus_read(struct mii_bus *bus,
|
||||
EXPORT_SYMBOL(__mdiobus_read);
|
||||
|
||||
/**
|
||||
+ * __mdiobus_read_paged - Unlocked version of the mdiobus_read_paged function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @page: the register page to access
|
||||
+ * @regnum: register number to read
|
||||
+ *
|
||||
+ * Read a MDIO bus register. Caller must hold the mdio bus lock.
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context.
|
||||
+ */
|
||||
+int __mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum)
|
||||
+{
|
||||
+ int retval;
|
||||
+ int oldpage;
|
||||
+
|
||||
+ lockdep_assert_held_once(&bus->mdio_lock);
|
||||
+
|
||||
+ if (bus->read_paged) {
|
||||
+ retval = bus->read_paged(bus, addr, page, regnum);
|
||||
+ } else {
|
||||
+ oldpage = bus->selected_page[addr];
|
||||
+ __mdiobus_select_page(bus, addr, page);
|
||||
+ retval = bus->read(bus, addr, regnum);
|
||||
+ __mdiobus_select_page(bus, addr, oldpage);
|
||||
+ }
|
||||
+
|
||||
+ trace_mdio_access(bus, 1, addr, regnum, retval, retval);
|
||||
+ mdiobus_stats_acct(&bus->stats[addr], true, retval);
|
||||
+
|
||||
+ return retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(__mdiobus_read_paged);
|
||||
+
|
||||
+/**
|
||||
* __mdiobus_write - Unlocked version of the mdiobus_write function
|
||||
* @bus: the mii_bus struct
|
||||
* @addr: the phy address
|
||||
@@ -783,7 +846,10 @@ int __mdiobus_write(struct mii_bus *bus,
|
||||
|
||||
lockdep_assert_held_once(&bus->mdio_lock);
|
||||
|
||||
- err = bus->write(bus, addr, regnum, val);
|
||||
+ if (bus->write_paged)
|
||||
+ err = bus->write_paged(bus, addr, bus->selected_page[addr], regnum, val);
|
||||
+ else
|
||||
+ err = bus->write(bus, addr, regnum, val);
|
||||
|
||||
trace_mdio_access(bus, 0, addr, regnum, val, err);
|
||||
mdiobus_stats_acct(&bus->stats[addr], false, err);
|
||||
@@ -793,6 +859,39 @@ int __mdiobus_write(struct mii_bus *bus,
|
||||
EXPORT_SYMBOL(__mdiobus_write);
|
||||
|
||||
/**
|
||||
+ * __mdiobus_write_paged - Unlocked version of the mdiobus_write_paged function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @page: the register page to access
|
||||
+ * @regnum: register number to write
|
||||
+ * @val: value to write to @regnum
|
||||
+ *
|
||||
+ * Write a MDIO bus register. Caller must hold the mdio bus lock.
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context.
|
||||
+ */
|
||||
+int __mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val)
|
||||
+{
|
||||
+ int err, oldpage;
|
||||
+
|
||||
+ lockdep_assert_held_once(&bus->mdio_lock);
|
||||
+
|
||||
+ if (bus->write_paged) {
|
||||
+ err = bus->write_paged(bus, addr, page, regnum, val);
|
||||
+ } else {
|
||||
+ oldpage = bus->selected_page[addr];
|
||||
+ __mdiobus_select_page(bus, addr, page);
|
||||
+ err = bus->write(bus, addr, regnum, val);
|
||||
+ __mdiobus_select_page(bus, addr, oldpage);
|
||||
+ }
|
||||
+ trace_mdio_access(bus, 0, addr, regnum, val, err);
|
||||
+ mdiobus_stats_acct(&bus->stats[addr], false, err);
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL(__mdiobus_write_paged);
|
||||
+
|
||||
+
|
||||
+/**
|
||||
* __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function
|
||||
* @bus: the mii_bus struct
|
||||
* @addr: the phy address
|
||||
@@ -825,6 +924,43 @@ int __mdiobus_modify_changed(struct mii_
|
||||
EXPORT_SYMBOL_GPL(__mdiobus_modify_changed);
|
||||
|
||||
/**
|
||||
+ * __mdiobus_modify_changed_paged - Unlocked version of the mdiobus_modify_paged function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @regnum: register number to modify
|
||||
+ * @mask: bit mask of bits to clear
|
||||
+ * @set: bit mask of bits to set
|
||||
+ *
|
||||
+ * Read, modify, and if any change, write the register value back to the
|
||||
+ * device. Any error returns a negative number.
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context.
|
||||
+ */
|
||||
+int __mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u32 regnum, u16 page,
|
||||
+ u16 mask, u16 set)
|
||||
+{
|
||||
+ int new, ret, oldpage;
|
||||
+
|
||||
+ oldpage = bus->selected_page[addr];
|
||||
+ __mdiobus_select_page(bus, addr, page);
|
||||
+
|
||||
+ ret = __mdiobus_read_paged(bus, addr, page, regnum);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ new = (ret & ~mask) | set;
|
||||
+ if (new == ret)
|
||||
+ return 0;
|
||||
+
|
||||
+ ret = __mdiobus_write_paged(bus, addr, page, regnum, new);
|
||||
+
|
||||
+ __mdiobus_select_page(bus, addr, oldpage);
|
||||
+
|
||||
+ return ret < 0 ? ret : 1;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(__mdiobus_modify_changed_paged);
|
||||
+
|
||||
+/**
|
||||
* mdiobus_read_nested - Nested version of the mdiobus_read function
|
||||
* @bus: the mii_bus struct
|
||||
* @addr: the phy address
|
||||
@@ -850,6 +986,79 @@ int mdiobus_read_nested(struct mii_bus *
|
||||
EXPORT_SYMBOL(mdiobus_read_nested);
|
||||
|
||||
/**
|
||||
+ * mdiobus_select_page_nested - Nested version of the mdiobus_select_page function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @page: register page to access
|
||||
+ *
|
||||
+ * In case of nested MDIO bus access avoid lockdep false positives by
|
||||
+ * using mutex_lock_nested().
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context,
|
||||
+ * because the bus read/write functions may wait for an interrupt
|
||||
+ * to conclude the operation.
|
||||
+ */
|
||||
+int mdiobus_select_page_nested(struct mii_bus *bus, int addr, u16 page)
|
||||
+{
|
||||
+ int retval;
|
||||
+
|
||||
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
|
||||
+ retval = __mdiobus_select_page(bus, addr, page);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(mdiobus_select_page_nested);
|
||||
+
|
||||
+/**
|
||||
+ * mdiobus_read_paged_nested - Nested version of the mdiobus_read_paged function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @page: register page to access
|
||||
+ * @regnum: register number to read
|
||||
+ *
|
||||
+ * In case of nested MDIO bus access avoid lockdep false positives by
|
||||
+ * using mutex_lock_nested().
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context,
|
||||
+ * because the bus read/write functions may wait for an interrupt
|
||||
+ * to conclude the operation.
|
||||
+ */
|
||||
+int mdiobus_read_paged_nested(struct mii_bus *bus, int addr, u16 page, u32 regnum)
|
||||
+{
|
||||
+ int retval;
|
||||
+
|
||||
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
|
||||
+ retval = __mdiobus_read_paged(bus, addr, page, regnum);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(mdiobus_read_paged_nested);
|
||||
+
|
||||
+/**
|
||||
+ * mdiobus_select_page - Convenience function for setting the MII register page
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @page: the register page to set
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context,
|
||||
+ * because the bus read/write functions may wait for an interrupt
|
||||
+ * to conclude the operation.
|
||||
+ */
|
||||
+int mdiobus_select_page(struct mii_bus *bus, int addr, u16 page)
|
||||
+{
|
||||
+ int retval;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ retval = __mdiobus_select_page(bus, addr, page);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(mdiobus_select_page);
|
||||
+
|
||||
+/**
|
||||
* mdiobus_read - Convenience function for reading a given MII mgmt register
|
||||
* @bus: the mii_bus struct
|
||||
* @addr: the phy address
|
||||
@@ -872,6 +1081,29 @@ int mdiobus_read(struct mii_bus *bus, in
|
||||
EXPORT_SYMBOL(mdiobus_read);
|
||||
|
||||
/**
|
||||
+ * mdiobus_read_paged - Convenience function for reading a given paged MII mgmt register
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @page: register page to access
|
||||
+ * @regnum: register number to read
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context,
|
||||
+ * because the bus read/write functions may wait for an interrupt
|
||||
+ * to conclude the operation.
|
||||
+ */
|
||||
+int mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum)
|
||||
+{
|
||||
+ int retval;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ retval = __mdiobus_read_paged(bus, addr, page, regnum);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(mdiobus_read_paged);
|
||||
+
|
||||
+/**
|
||||
* mdiobus_write_nested - Nested version of the mdiobus_write function
|
||||
* @bus: the mii_bus struct
|
||||
* @addr: the phy address
|
||||
@@ -898,6 +1130,33 @@ int mdiobus_write_nested(struct mii_bus
|
||||
EXPORT_SYMBOL(mdiobus_write_nested);
|
||||
|
||||
/**
|
||||
+ * mdiobus_write_paged_nested - Nested version of the mdiobus_write_aged function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @page: the register page to access
|
||||
+ * @regnum: register number to write
|
||||
+ * @val: value to write to @regnum
|
||||
+ *
|
||||
+ * In case of nested MDIO bus access avoid lockdep false positives by
|
||||
+ * using mutex_lock_nested().
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context,
|
||||
+ * because the bus read/write functions may wait for an interrupt
|
||||
+ * to conclude the operation.
|
||||
+ */
|
||||
+int mdiobus_write_paged_nested(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
|
||||
+ err = __mdiobus_write_paged(bus, addr, page, regnum, val);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL(mdiobus_write_paged_nested);
|
||||
+
|
||||
+/**
|
||||
* mdiobus_write - Convenience function for writing a given MII mgmt register
|
||||
* @bus: the mii_bus struct
|
||||
* @addr: the phy address
|
||||
@@ -921,6 +1180,30 @@ int mdiobus_write(struct mii_bus *bus, i
|
||||
EXPORT_SYMBOL(mdiobus_write);
|
||||
|
||||
/**
|
||||
+ * mdiobus_write_paged - Convenience function for writing a given paged MII mgmt register
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @page: the register page to access
|
||||
+ * @regnum: register number to write
|
||||
+ * @val: value to write to @regnum
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context,
|
||||
+ * because the bus read/write functions may wait for an interrupt
|
||||
+ * to conclude the operation.
|
||||
+ */
|
||||
+int mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ err = __mdiobus_write_paged(bus, addr, page, regnum, val);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL(mdiobus_write_paged);
|
||||
+
|
||||
+/**
|
||||
* mdiobus_modify - Convenience function for modifying a given mdio device
|
||||
* register
|
||||
* @bus: the mii_bus struct
|
||||
@@ -942,6 +1225,51 @@ int mdiobus_modify(struct mii_bus *bus,
|
||||
EXPORT_SYMBOL_GPL(mdiobus_modify);
|
||||
|
||||
/**
|
||||
+ * mdiobus_modify_paged - Convenience function for modifying a given mdio device
|
||||
+ * register
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @page: the register page to access
|
||||
+ * @regnum: register number to write
|
||||
+ * @mask: bit mask of bits to clear
|
||||
+ * @set: bit mask of bits to set
|
||||
+ */
|
||||
+int mdiobus_modify_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 mask, u16 set)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ err = __mdiobus_modify_changed_paged(bus, addr, page, regnum, mask, set);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return err < 0 ? err : 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(mdiobus_modify_paged);
|
||||
+
|
||||
+/**
|
||||
+ * mdiobus_modify_changed_paged - Convenience function for modifying a given paged
|
||||
+ * mdio device register and returning if it changed
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @page: the register page to access
|
||||
+ * @regnum: register number to write
|
||||
+ * @mask: bit mask of bits to clear
|
||||
+ * @set: bit mask of bits to set
|
||||
+ */
|
||||
+int mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum,
|
||||
+ u16 mask, u16 set)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ err = __mdiobus_modify_changed_paged(bus, addr, page, regnum, mask, set);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(mdiobus_modify_changed_paged);
|
||||
+
|
||||
+/**
|
||||
* mdio_bus_match - determine if given MDIO driver supports the given
|
||||
* MDIO device
|
||||
* @dev: target MDIO device
|
||||
--- a/drivers/net/phy/phy-core.c
|
||||
+++ b/drivers/net/phy/phy-core.c
|
||||
@@ -557,10 +557,16 @@ int __phy_read_mmd(struct phy_device *ph
|
||||
struct mii_bus *bus = phydev->mdio.bus;
|
||||
int phy_addr = phydev->mdio.addr;
|
||||
|
||||
- mmd_phy_indirect(bus, phy_addr, devad, regnum);
|
||||
-
|
||||
- /* Read the content of the MMD's selected register */
|
||||
- val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
|
||||
+ if (bus->access_capabilities & MDIOBUS_ACCESS_C22_MMD) {
|
||||
+ val = __mdiobus_c22_mmd_read(phydev->mdio.bus,
|
||||
+ phydev->mdio.addr,
|
||||
+ devad, regnum);
|
||||
+ } else {
|
||||
+ mmd_phy_indirect(bus, phy_addr, devad, regnum);
|
||||
+
|
||||
+ /* Read the content of the MMD's selected register */
|
||||
+ val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
|
||||
+ }
|
||||
}
|
||||
return val;
|
||||
}
|
||||
@@ -613,12 +619,18 @@ int __phy_write_mmd(struct phy_device *p
|
||||
struct mii_bus *bus = phydev->mdio.bus;
|
||||
int phy_addr = phydev->mdio.addr;
|
||||
|
||||
- mmd_phy_indirect(bus, phy_addr, devad, regnum);
|
||||
+ if (bus->access_capabilities & MDIOBUS_ACCESS_C22_MMD) {
|
||||
+ ret = __mdiobus_c22_mmd_write(phydev->mdio.bus,
|
||||
+ phydev->mdio.addr,
|
||||
+ devad, regnum, val);
|
||||
+ } else {
|
||||
+ mmd_phy_indirect(bus, phy_addr, devad, regnum);
|
||||
|
||||
- /* Write the data into MMD's selected register */
|
||||
- __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
|
||||
+ /* Write the data into MMD's selected register */
|
||||
+ __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
|
||||
|
||||
- ret = 0;
|
||||
+ ret = 0;
|
||||
+ }
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -824,6 +836,13 @@ EXPORT_SYMBOL_GPL(phy_modify_mmd);
|
||||
|
||||
static int __phy_read_page(struct phy_device *phydev)
|
||||
{
|
||||
+ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) {
|
||||
+ struct mii_bus *bus = phydev->mdio.bus;
|
||||
+ int phy_addr = phydev->mdio.addr;
|
||||
+
|
||||
+ return bus->selected_page[phy_addr];
|
||||
+ }
|
||||
+
|
||||
if (WARN_ONCE(!phydev->drv->read_page, "read_page callback not available, PHY driver not loaded?\n"))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@@ -832,6 +851,13 @@ static int __phy_read_page(struct phy_de
|
||||
|
||||
static int __phy_write_page(struct phy_device *phydev, int page)
|
||||
{
|
||||
+ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) {
|
||||
+ struct mii_bus *bus = phydev->mdio.bus;
|
||||
+ int phy_addr = phydev->mdio.addr;
|
||||
+
|
||||
+ return __mdiobus_select_page(bus, phy_addr, page);
|
||||
+ }
|
||||
+
|
||||
if (WARN_ONCE(!phydev->drv->write_page, "write_page callback not available, PHY driver not loaded?\n"))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@@ -933,6 +959,18 @@ int phy_read_paged(struct phy_device *ph
|
||||
{
|
||||
int ret = 0, oldpage;
|
||||
|
||||
+ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) {
|
||||
+ struct mii_bus *bus = phydev->mdio.bus;
|
||||
+ int phy_addr = phydev->mdio.addr;
|
||||
+
|
||||
+ if (bus->read_paged) {
|
||||
+ phy_lock_mdio_bus(phydev);
|
||||
+ ret = bus->read_paged(bus, phy_addr, page, regnum);
|
||||
+ phy_unlock_mdio_bus(phydev);
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
oldpage = phy_select_page(phydev, page);
|
||||
if (oldpage >= 0)
|
||||
ret = __phy_read(phydev, regnum);
|
||||
@@ -954,6 +992,18 @@ int phy_write_paged(struct phy_device *p
|
||||
{
|
||||
int ret = 0, oldpage;
|
||||
|
||||
+ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) {
|
||||
+ struct mii_bus *bus = phydev->mdio.bus;
|
||||
+ int phy_addr = phydev->mdio.addr;
|
||||
+
|
||||
+ if (bus->write_paged) {
|
||||
+ phy_lock_mdio_bus(phydev);
|
||||
+ ret = bus->write_paged(bus, phy_addr, page, regnum, val);
|
||||
+ phy_unlock_mdio_bus(phydev);
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
oldpage = phy_select_page(phydev, page);
|
||||
if (oldpage >= 0)
|
||||
ret = __phy_write(phydev, regnum, val);
|
||||
--- a/include/linux/mdio.h
|
||||
+++ b/include/linux/mdio.h
|
||||
@@ -14,6 +14,7 @@
|
||||
* IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips.
|
||||
*/
|
||||
#define MII_ADDR_C45 (1<<30)
|
||||
+#define MII_ADDR_C22_MMD (1<<29)
|
||||
#define MII_DEVADDR_C45_SHIFT 16
|
||||
#define MII_DEVADDR_C45_MASK GENMASK(20, 16)
|
||||
#define MII_REGADDR_C45_MASK GENMASK(15, 0)
|
||||
@@ -340,11 +341,19 @@ static inline void mii_10gbt_stat_mod_li
|
||||
advertising, lpa & MDIO_AN_10GBT_STAT_LP10G);
|
||||
}
|
||||
|
||||
+int __mdiobus_select_page(struct mii_bus *bus, int addr, u16 page);
|
||||
int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
|
||||
int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
|
||||
int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
|
||||
u16 mask, u16 set);
|
||||
|
||||
+int __mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum);
|
||||
+int __mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val);
|
||||
+int __mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u32 regnum, u16 page,
|
||||
+ u16 mask, u16 set);
|
||||
+
|
||||
+int mdiobus_select_page(struct mii_bus *bus, int addr, u16 page);
|
||||
+int mdiobus_select_page_nested(struct mii_bus *bus, int addr, u16 page);
|
||||
int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
|
||||
int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum);
|
||||
int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
|
||||
@@ -352,11 +361,51 @@ int mdiobus_write_nested(struct mii_bus
|
||||
int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask,
|
||||
u16 set);
|
||||
|
||||
+int mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum);
|
||||
+int mdiobus_read_nested_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum);
|
||||
+int mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val);
|
||||
+int mdiobus_write_nested_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val);
|
||||
+int mdiobus_modify_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 mask,
|
||||
+ u16 set);
|
||||
+int mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum,
|
||||
+ u16 mask, u16 set);
|
||||
+
|
||||
+static inline int mdiodev_read_paged(struct mdio_device *mdiodev, u16 page,
|
||||
+ u32 regnum)
|
||||
+{
|
||||
+ return mdiobus_read_paged(mdiodev->bus, mdiodev->addr, page, regnum);
|
||||
+}
|
||||
+
|
||||
+static inline int mdiodev_write_paged(struct mdio_device *mdiodev, u16 page,
|
||||
+ u32 regnum, u16 val)
|
||||
+{
|
||||
+ return mdiobus_write_paged(mdiodev->bus, mdiodev->addr, page, regnum, val);
|
||||
+}
|
||||
+
|
||||
+static inline int mdiodev_modify_paged(struct mdio_device *mdiodev, u16 page,
|
||||
+ u32 regnum, u16 mask, u16 set)
|
||||
+{
|
||||
+ return mdiobus_modify_paged(mdiodev->bus, mdiodev->addr, page, regnum,
|
||||
+ mask, set);
|
||||
+}
|
||||
+
|
||||
+static inline int mdiodev_modify_changed_paged(struct mdio_device *mdiodev, u16 page,
|
||||
+ u32 regnum, u16 mask, u16 set)
|
||||
+{
|
||||
+ return mdiobus_modify_changed_paged(mdiodev->bus, mdiodev->addr, page, regnum,
|
||||
+ mask, set);
|
||||
+}
|
||||
+
|
||||
static inline u32 mdiobus_c45_addr(int devad, u16 regnum)
|
||||
{
|
||||
return MII_ADDR_C45 | devad << MII_DEVADDR_C45_SHIFT | regnum;
|
||||
}
|
||||
|
||||
+static inline u32 mdiobus_c22_mmd_addr(int devad, u16 regnum)
|
||||
+{
|
||||
+ return MII_ADDR_C22_MMD | devad << MII_DEVADDR_C45_SHIFT | regnum;
|
||||
+}
|
||||
+
|
||||
static inline u16 mdiobus_c45_regad(u32 regnum)
|
||||
{
|
||||
return FIELD_GET(MII_REGADDR_C45_MASK, regnum);
|
||||
@@ -380,6 +429,19 @@ static inline int __mdiobus_c45_write(st
|
||||
val);
|
||||
}
|
||||
|
||||
+static inline int __mdiobus_c22_mmd_read(struct mii_bus *bus, int prtad,
|
||||
+ int devad, u16 regnum)
|
||||
+{
|
||||
+ return __mdiobus_read(bus, prtad, mdiobus_c22_mmd_addr(devad, regnum));
|
||||
+}
|
||||
+
|
||||
+static inline int __mdiobus_c22_mmd_write(struct mii_bus *bus, int prtad,
|
||||
+ int devad, u16 regnum, u16 val)
|
||||
+{
|
||||
+ return __mdiobus_write(bus, prtad, mdiobus_c22_mmd_addr(devad, regnum),
|
||||
+ val);
|
||||
+}
|
||||
+
|
||||
static inline int mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
|
||||
u16 regnum)
|
||||
{
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -81,6 +81,7 @@ extern const int phy_10gbit_features_arr
|
||||
#define PHY_IS_INTERNAL 0x00000001
|
||||
#define PHY_RST_AFTER_CLK_EN 0x00000002
|
||||
#define PHY_POLL_CABLE_TEST 0x00000004
|
||||
+#define PHY_HAS_REALTEK_PAGES 0x00000010
|
||||
#define MDIO_DEVICE_IS_PHY 0x80000000
|
||||
|
||||
/**
|
||||
@@ -428,6 +429,22 @@ struct mii_bus {
|
||||
|
||||
/** @shared: shared state across different PHYs */
|
||||
struct phy_package_shared *shared[PHY_MAX_ADDR];
|
||||
+
|
||||
+ /** @access_capabilities: hardware-assisted access capabilties */
|
||||
+ enum {
|
||||
+ MDIOBUS_ACCESS_SOFTWARE_ONLY = 0,
|
||||
+ MDIOBUS_ACCESS_C22_MMD = 0x1,
|
||||
+ } access_capabilities;
|
||||
+
|
||||
+ /** @read: Perform a read transfer on the bus, offloading page access */
|
||||
+ int (*read_paged)(struct mii_bus *bus, int addr, u16 page, int regnum);
|
||||
+ /** @write: Perform a write transfer on the bus, offloading page access */
|
||||
+ int (*write_paged)(struct mii_bus *bus, int addr, u16 page, int regnum, u16 val);
|
||||
+ /** currently selected page when page access is offloaded
|
||||
+ * array should be PHY_MAX_ADDR+1size, but current design of the MDIO driver
|
||||
+ * uses port addresses as phy addresses and they are up to 6 bit.
|
||||
+ */
|
||||
+ u16 selected_page[64];
|
||||
};
|
||||
#define to_mii_bus(d) container_of(d, struct mii_bus, dev)
|
||||
|
||||
@@ -1825,6 +1842,66 @@ static inline int __phy_package_read(str
|
||||
return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
|
||||
}
|
||||
|
||||
+static inline int phy_package_read_port(struct phy_device *phydev, u16 port, u32 regnum)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+
|
||||
+ if (!shared)
|
||||
+ return -EIO;
|
||||
+
|
||||
+ return mdiobus_read(phydev->mdio.bus, shared->addr + port, regnum);
|
||||
+}
|
||||
+
|
||||
+static inline int __phy_package_read_port(struct phy_device *phydev, u16 port, u32 regnum)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+
|
||||
+ if (!shared)
|
||||
+ return -EIO;
|
||||
+
|
||||
+ return __mdiobus_read(phydev->mdio.bus, shared->addr + port, regnum);
|
||||
+}
|
||||
+
|
||||
+static inline int phy_package_read_paged(struct phy_device *phydev, u16 page, u32 regnum)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+
|
||||
+ if (!shared)
|
||||
+ return -EIO;
|
||||
+
|
||||
+ return mdiobus_read_paged(phydev->mdio.bus, shared->addr, page, regnum);
|
||||
+}
|
||||
+
|
||||
+static inline int __phy_package_read_paged(struct phy_device *phydev, u16 page, u32 regnum)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+
|
||||
+ if (!shared)
|
||||
+ return -EIO;
|
||||
+
|
||||
+ return __mdiobus_read_paged(phydev->mdio.bus, shared->addr, page, regnum);
|
||||
+}
|
||||
+
|
||||
+static inline int phy_package_port_read_paged(struct phy_device *phydev, u16 port, u16 page, u32 regnum)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+
|
||||
+ if (!shared)
|
||||
+ return -EIO;
|
||||
+
|
||||
+ return mdiobus_read_paged(phydev->mdio.bus, shared->addr + port, page, regnum);
|
||||
+}
|
||||
+
|
||||
+static inline int __phy_package_port_read_paged(struct phy_device *phydev, u16 port, u16 page, u32 regnum)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+
|
||||
+ if (!shared)
|
||||
+ return -EIO;
|
||||
+
|
||||
+ return __mdiobus_read_paged(phydev->mdio.bus, shared->addr + port, page, regnum);
|
||||
+}
|
||||
+
|
||||
static inline int phy_package_write(struct phy_device *phydev,
|
||||
u32 regnum, u16 val)
|
||||
{
|
||||
@@ -1847,6 +1924,72 @@ static inline int __phy_package_write(st
|
||||
return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
|
||||
}
|
||||
|
||||
+static inline int phy_package_port_write(struct phy_device *phydev,
|
||||
+ u16 port, u32 regnum, u16 val)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+
|
||||
+ if (!shared)
|
||||
+ return -EIO;
|
||||
+
|
||||
+ return mdiobus_write(phydev->mdio.bus, shared->addr + port, regnum, val);
|
||||
+}
|
||||
+
|
||||
+static inline int __phy_package_port_write(struct phy_device *phydev,
|
||||
+ u16 port, u32 regnum, u16 val)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+
|
||||
+ if (!shared)
|
||||
+ return -EIO;
|
||||
+
|
||||
+ return __mdiobus_write(phydev->mdio.bus, shared->addr + port, regnum, val);
|
||||
+}
|
||||
+
|
||||
+static inline int phy_package_port_write_paged(struct phy_device *phydev,
|
||||
+ u16 port, u16 page, u32 regnum, u16 val)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+
|
||||
+ if (!shared)
|
||||
+ return -EIO;
|
||||
+
|
||||
+ return mdiobus_write_paged(phydev->mdio.bus, shared->addr + port, page, regnum, val);
|
||||
+}
|
||||
+
|
||||
+static inline int __phy_package_port_write_paged(struct phy_device *phydev,
|
||||
+ u16 port, u16 page, u32 regnum, u16 val)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+
|
||||
+ if (!shared)
|
||||
+ return -EIO;
|
||||
+
|
||||
+ return __mdiobus_write_paged(phydev->mdio.bus, shared->addr + port, page, regnum, val);
|
||||
+}
|
||||
+
|
||||
+static inline int phy_package_write_paged(struct phy_device *phydev,
|
||||
+ u16 page, u32 regnum, u16 val)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+
|
||||
+ if (!shared)
|
||||
+ return -EIO;
|
||||
+
|
||||
+ return mdiobus_write_paged(phydev->mdio.bus, shared->addr, page, regnum, val);
|
||||
+}
|
||||
+
|
||||
+static inline int __phy_package_write_paged(struct phy_device *phydev,
|
||||
+ u16 page, u32 regnum, u16 val)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+
|
||||
+ if (!shared)
|
||||
+ return -EIO;
|
||||
+
|
||||
+ return __mdiobus_write_paged(phydev->mdio.bus, shared->addr, page, regnum, val);
|
||||
+}
|
||||
+
|
||||
static inline bool __phy_package_set_once(struct phy_device *phydev,
|
||||
unsigned int b)
|
||||
{
|
||||
--- a/include/uapi/linux/mii.h
|
||||
+++ b/include/uapi/linux/mii.h
|
||||
@@ -36,6 +36,7 @@
|
||||
#define MII_RESV2 0x1a /* Reserved... */
|
||||
#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
|
||||
#define MII_NCONFIG 0x1c /* Network interface config */
|
||||
+#define MII_MAINPAGE 0x1f /* Page register */
|
||||
|
||||
/* Basic mode control register. */
|
||||
#define BMCR_RESV 0x003f /* Unused... */
|
||||
Reference in New Issue
Block a user