[RFC PATCH 2/7] realtek: add switch core MFD driver
Sander Vanheule
sander at svanheule.net
Sat Jul 16 12:09:54 PDT 2022
Realtek managed switch SoCs such as the RTL8380 consist of a MIPS CPU
with a number of basic peripherals, and an ethernet switch peripheral.
Besides performing ethernet related tasks, this switch core also
provides SoC management features. These switch core features are badly
separated, and cannot be divided into distinct IO ranges.
This MFD core driver is intended to manage the switch core regmap, and
to expose some limited features that don't warrant their own driver.
These include SoC identification and system LED management.
Signed-off-by: Sander Vanheule <sander at svanheule.net>
---
.../drivers/mfd/realtek-switchcore.c | 244 ++++++++++++++++++
...0-mfd-add-Realtek-switch-core-driver.patch | 50 ++++
target/linux/realtek/rtl838x/config-5.10 | 2 +
3 files changed, 296 insertions(+)
create mode 100644 target/linux/realtek/files-5.10/drivers/mfd/realtek-switchcore.c
create mode 100644 target/linux/realtek/patches-5.10/200-mfd-add-Realtek-switch-core-driver.patch
diff --git a/target/linux/realtek/files-5.10/drivers/mfd/realtek-switchcore.c b/target/linux/realtek/files-5.10/drivers/mfd/realtek-switchcore.c
new file mode 100644
index 000000000000..5b3f6eee6fe2
--- /dev/null
+++ b/target/linux/realtek/files-5.10/drivers/mfd/realtek-switchcore.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/bitfield.h>
+#include <linux/leds.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct realtek_switchcore_ctrl;
+
+struct realtek_switchcore_data {
+ struct reg_field sys_led_field;
+ const struct mfd_cell *mfd_devices;
+ unsigned int mfd_device_count;
+ void (*probe_model_name)(const struct realtek_switchcore_ctrl *ctrl);
+};
+
+struct realtek_switchcore_ctrl {
+ struct device *dev;
+ struct regmap *map;
+ const struct realtek_switchcore_data *data;
+ struct led_classdev sys_led;
+ struct regmap_field *sys_led_field;
+ bool active_low;
+};
+
+/*
+ * Model name probe
+ *
+ * Reads the family-specific MODEL_NAME_INFO register
+ * to identify the SoC model and revision
+ */
+#define RTL8380_REG_MODEL_NAME_INFO 0x00d4
+#define RTL8380_REG_CHIP_INFO 0x00d8
+#define MODEL_NAME_CHAR_XLATE(val) ((val) ? 'A' + (val) - 1 : '\0')
+#define MODEL_NAME_CHAR(reg, msb, lsb) (MODEL_NAME_CHAR_XLATE(FIELD_GET(GENMASK((msb), (lsb)), (val))))
+
+#define RTL8380_REG_INT_RW_CTRL 0x0058
+
+static void rtl8380_probe_model_name(const struct realtek_switchcore_ctrl *ctrl)
+{
+ char model_char[4] = {0, 0, 0, 0};
+ char chip_rev;
+ u32 model_id;
+ u32 val = 0;
+
+ regmap_read(ctrl->map, RTL8380_REG_MODEL_NAME_INFO, &val);
+ model_id = FIELD_GET(GENMASK(31, 16), val);
+ model_char[0] = MODEL_NAME_CHAR(val, 15, 11);
+ model_char[1] = MODEL_NAME_CHAR(val, 10, 6);
+ model_char[2] = MODEL_NAME_CHAR(val, 5, 1);
+
+ /* CHIP_INFO register must be unlocked by writing 0xa to the top bits */
+ regmap_write(ctrl->map, RTL8380_REG_CHIP_INFO, FIELD_PREP(GENMASK(31, 28), 0xa));
+ regmap_read(ctrl->map, RTL8380_REG_CHIP_INFO, &val);
+ chip_rev = MODEL_NAME_CHAR(val, 20, 16) ?: '0';
+
+ dev_info(ctrl->dev, "found RTL%04x%s rev. %c\n", model_id, model_char, chip_rev);
+}
+
+/*
+ * Realtek hardware system LED
+ *
+ * The switch SoC supports one hardware managed direct LED output
+ * to manage a system LED, with two supported blinking rates.
+ */
+enum {
+ SWITCHCORE_SYS_LED_OFF = 0,
+ SWITCHCORE_SYS_LED_BLINK_64MS,
+ SWITCHCORE_SYS_LED_BLINK_1024MS,
+ SWITCHCORE_SYS_LED_ON
+};
+
+static void switchcore_sys_led_set_rate(const struct realtek_switchcore_ctrl *ctrl,
+ unsigned int mode)
+{
+ regmap_field_write(ctrl->sys_led_field, mode);
+}
+
+static void switchcore_sys_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct realtek_switchcore_ctrl *ctrl =
+ container_of(led_cdev, struct realtek_switchcore_ctrl, sys_led);
+
+ if ((!ctrl->active_low && brightness == LED_OFF) ||
+ (ctrl->active_low && brightness != LED_OFF))
+ switchcore_sys_led_set_rate(ctrl, SWITCHCORE_SYS_LED_OFF);
+ else
+ switchcore_sys_led_set_rate(ctrl, SWITCHCORE_SYS_LED_ON);
+}
+
+static enum led_brightness switchcore_sys_led_brightness_get(
+ struct led_classdev *led_cdev)
+{
+ struct realtek_switchcore_ctrl *ctrl =
+ container_of(led_cdev, struct realtek_switchcore_ctrl, sys_led);
+ u32 val;
+
+ regmap_field_read(ctrl->sys_led_field, &val);
+
+ if ((!ctrl->active_low && val == SWITCHCORE_SYS_LED_OFF) ||
+ (ctrl->active_low && val == SWITCHCORE_SYS_LED_ON))
+ return LED_OFF;
+ else
+ return LED_ON;
+}
+
+static int switchcore_sys_led_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on, unsigned long *delay_off)
+{
+ struct realtek_switchcore_ctrl *ctrl =
+ container_of(led_cdev, struct realtek_switchcore_ctrl, sys_led);
+ u32 blink_interval = *delay_on + *delay_off;
+
+ /* Split range at geometric mean of 64 and 1024 */
+ if (blink_interval == 0 || blink_interval > 2 * 256) {
+ *delay_on = 1024;
+ *delay_off = 1024;
+ switchcore_sys_led_set_rate(ctrl, SWITCHCORE_SYS_LED_BLINK_1024MS);
+ } else {
+ *delay_on = 64;
+ *delay_off = 64;
+ switchcore_sys_led_set_rate(ctrl, SWITCHCORE_SYS_LED_BLINK_64MS);
+ }
+
+ return 0;
+}
+
+static int switchcore_sys_led_probe(struct realtek_switchcore_ctrl *ctrl,
+ struct device_node *np)
+{
+ struct led_classdev *sys_led = &ctrl->sys_led;
+ struct led_init_data init_data = {};
+
+ init_data.fwnode = of_fwnode_handle(np);
+
+ ctrl->sys_led_field =
+ devm_regmap_field_alloc(ctrl->dev, ctrl->map, ctrl->data->sys_led_field);
+ if (IS_ERR(ctrl->sys_led_field))
+ return PTR_ERR(ctrl->sys_led_field);
+
+ ctrl->active_low = of_property_read_bool(np, "active-low");
+
+ sys_led->max_brightness = 1;
+ sys_led->brightness_set = switchcore_sys_led_brightness_set;
+ sys_led->brightness_get = switchcore_sys_led_brightness_get;
+ sys_led->blink_set = switchcore_sys_led_blink_set;
+
+ return devm_led_classdev_register_ext(ctrl->dev, sys_led, &init_data);
+}
+
+static const struct mfd_cell rtl8380_mfd_devices[] = {
+ OF_MFD_CELL("realtek-switchcore-port-leds",
+ NULL, NULL, 0, 0, "realtek,rtl8380-port-led"),
+ OF_MFD_CELL("realtek-switchcore-aux-mdio",
+ NULL, NULL, 0, 0, "realtek,rtl8380-aux-mdio"),
+ OF_MFD_CELL("realtek-switchcore-pinctrl",
+ NULL, NULL, 0, 0, "realtek,rtl8380-pinctrl"),
+};
+
+static const struct realtek_switchcore_data rtl8380_switchcore_data = {
+ .sys_led_field = REG_FIELD(0xa000, 16, 17),
+ .mfd_devices = rtl8380_mfd_devices,
+ .mfd_device_count = ARRAY_SIZE(rtl8380_mfd_devices),
+ .probe_model_name = rtl8380_probe_model_name,
+};
+
+static const struct of_device_id of_realtek_switchcore_match[] = {
+ {
+ .compatible = "realtek,rtl8380-switchcore",
+ .data = &rtl8380_switchcore_data,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_realtek_switchcore_match);
+
+static int realtek_switchcore_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *np_sys_led;
+ const struct of_device_id *match;
+ struct realtek_switchcore_ctrl *ctrl;
+ int err;
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ match = of_match_device(of_realtek_switchcore_match, dev);
+ if (match)
+ ctrl->data = (struct realtek_switchcore_data *) match->data;
+ else
+ return dev_err_probe(dev, -EINVAL, "no device match\n");
+
+ ctrl->dev = dev;
+
+ if (!np)
+ return dev_err_probe(dev, -ENODEV, "no DT node found\n");
+
+ ctrl->map = device_node_to_regmap(np);
+ if (!ctrl->map)
+ return dev_err_probe(dev, -ENXIO, "failed to get regmap\n");
+
+ if (ctrl->data->probe_model_name)
+ ctrl->data->probe_model_name(ctrl);
+
+ /* Parse optional led-sys child */
+ np_sys_led = of_get_child_by_name(np, "led-sys");
+ if (IS_ERR(np_sys_led))
+ return PTR_ERR(np_sys_led);
+
+ if (np_sys_led && of_device_is_available(np_sys_led)) {
+ err = switchcore_sys_led_probe(ctrl, np_sys_led);
+ if (err)
+ dev_warn(dev, "probing of system led failed %d\n", err);
+ }
+
+ /* Find sub-devices */
+ if (ctrl->data->mfd_devices)
+ mfd_add_devices(dev, 0, ctrl->data->mfd_devices,
+ ctrl->data->mfd_device_count, NULL, 0, NULL);
+
+ return 0;
+}
+
+static struct platform_driver realtek_switchcore_driver = {
+ .probe = realtek_switchcore_probe,
+ .driver = {
+ .name = "realtek-switchcore",
+ .of_match_table = of_realtek_switchcore_match
+ }
+};
+module_platform_driver(realtek_switchcore_driver);
+
+MODULE_AUTHOR("Sander Vanheule <sander at svanheule.net>");
+MODULE_DESCRIPTION("Realtek SoC switch core driver");
+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/realtek/patches-5.10/200-mfd-add-Realtek-switch-core-driver.patch b/target/linux/realtek/patches-5.10/200-mfd-add-Realtek-switch-core-driver.patch
new file mode 100644
index 000000000000..e0f4678f28f3
--- /dev/null
+++ b/target/linux/realtek/patches-5.10/200-mfd-add-Realtek-switch-core-driver.patch
@@ -0,0 +1,50 @@
+From 2b4df5433baaa10eeb23a58012329d4ec47216ba Mon Sep 17 00:00:00 2001
+From: Sander Vanheule <sander at svanheule.net>
+Date: Mon, 11 Jul 2022 09:24:18 +0200
+Subject: [PATCH 01/14] mfd: add Realtek switch core driver
+
+Realtek managed switch SoCs such as the RTL8380 consist of a MIPS CPU
+with a number of basic peripherals, and an ethernet switch peripheral.
+Besides performing ethernet related tasks, this switch core also
+provides SoC management features. These switch core features are badly
+separated, and cannot be divided into distinct IO ranges.
+
+This MFD core driver is intended to manage the switch core regmap, and
+to expose some limited features that don't warrant their own driver.
+These include SoC identification and system LED management.
+
+Signed-off-by: Sander Vanheule <sander at svanheule.net>
+---
+ drivers/mfd/Kconfig | 11 ++
+ drivers/mfd/Makefile | 2 +
+ drivers/mfd/realtek-switchcore.c | 242 +++++++++++++++++++++++++++++++
+ 3 files changed, 255 insertions(+)
+ create mode 100644 drivers/mfd/realtek-switchcore.c
+
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -1000,6 +1000,16 @@ config MFD_RETU
+ Retu and Tahvo are a multi-function devices found on Nokia
+ Internet Tablets (770, N800 and N810).
+
++config MFD_REALTEK_SWITCHCORE
++ tristate "Realtek switch core driver"
++ select MFD_CORE
++ select REGMAP_MMIO
++ help
++ Say yes here if you want to support the switch core found in RTL838x,
++ RTL839x, RTL930x, and RTL931x SoCs. The switch core provides ethernet
++ functionality, and management features for the SoC like pin control.
++ The mfd cell drivers have to be selected separately.
++
+ config MFD_PCF50633
+ tristate "NXP PCF50633"
+ depends on I2C
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -267,3 +267,5 @@ obj-$(CONFIG_MFD_KHADAS_MCU) += khadas-
+ obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o
+ obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o
+ obj-$(CONFIG_MFD_INTEL_M10_BMC) += intel-m10-bmc.o
++
++obj-$(CONFIG_MFD_REALTEK_SWITCHCORE) += realtek-switchcore.o
diff --git a/target/linux/realtek/rtl838x/config-5.10 b/target/linux/realtek/rtl838x/config-5.10
index c117fc489a81..065948532057 100644
--- a/target/linux/realtek/rtl838x/config-5.10
+++ b/target/linux/realtek/rtl838x/config-5.10
@@ -109,6 +109,8 @@ CONFIG_MDIO_DEVICE=y
CONFIG_MDIO_DEVRES=y
CONFIG_MDIO_I2C=y
CONFIG_MEMFD_CREATE=y
+CONFIG_MFD_CORE=y
+CONFIG_MFD_REALTEK_SWITCHCORE=y
CONFIG_MFD_SYSCON=y
CONFIG_MIGRATION=y
CONFIG_MIPS=y
--
2.36.1
More information about the openwrt-devel
mailing list