diff mbox series

[v2,01/16] realtek: add switch core MFD driver

Message ID bac42579cb264b44ad89c08640db21cad8ed71a9.1664830160.git.sander@svanheule.net
State Superseded
Delegated to: Sander Vanheule
Headers show
Series realtek: pinctrl and switch LED drivers | expand

Commit Message

Sander Vanheule Oct. 3, 2022, 8:52 p.m. UTC
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,
such as SoC identification.

Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
 .../drivers/mfd/realtek-switchcore.c          | 217 ++++++++++++++++++
 ...0-mfd-add-Realtek-switch-core-driver.patch |  46 ++++
 2 files changed, 263 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 mbox series

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..be9d96b8cb0a
--- /dev/null
+++ b/target/linux/realtek/files-5.10/drivers/mfd/realtek-switchcore.c
@@ -0,0 +1,217 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+struct realtek_switchcore_ctrl;
+
+struct realtek_switchcore_data {
+	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;
+};
+
+/*
+ * Model name probe
+ *
+ * Reads the family-specific MODEL_NAME_INFO register
+ * to identify the SoC model and revision
+ */
+#define MODEL_NAME_CHAR_XLATE(val)	((val) ? 'A' + (val) - 1 : '\0')
+
+#define RTL83XX_MODEL_NAME_ID		GENMASK(31, 16)
+#define RTL83XX_MODEL_NAME_CHAR		GENMASK(15, 11)
+
+#define RTL83XX_CHIP_INFO_UNLOCK	GENMASK(31, 28)
+#define RTL83XX_CHIP_INFO_UNLOCK_CODE	0xa
+#define RTL83XX_CHIP_INFO_CHIP_REV	GENMASK(20, 16)
+#define RTL83XX_CHIP_INFO_RLID		GENMASK(15, 0)
+
+/* Maple registers */
+#define RTL838X_REG_MODEL_NAME_INFO	0x00d4
+#define RTL838X_REG_CHIP_INFO		0x00d8
+
+#define RTL838X_REG_INT_RW_CTRL		0x0058
+#define RTL838X_REG_MODE_DEFINE_CTL	0x1024
+
+/* Cypress registers */
+#define RTL839X_REG_MODEL_NAME_INFO	0x0ff0
+#define RTL839X_REG_CHIP_INFO		0x0ff4
+
+static void rtl83xx_read_chip_name(struct regmap *map, unsigned int reg,
+				   unsigned int *model_id, char *model_first_char)
+{
+	u32 val = 0;
+
+	regmap_read(map, reg, &val);
+	*model_id = FIELD_GET(RTL83XX_MODEL_NAME_ID, val);
+	*model_first_char = MODEL_NAME_CHAR_XLATE(FIELD_GET(RTL83XX_MODEL_NAME_CHAR, val));
+}
+
+static void rtl83xx_read_chip_info(struct regmap *map, unsigned int reg,
+				   unsigned int *chip_rev, unsigned int *rl_id)
+{
+	u32 val = 0;
+
+	val = FIELD_PREP(RTL83XX_CHIP_INFO_UNLOCK, RTL83XX_CHIP_INFO_UNLOCK_CODE);
+	regmap_write(map, reg, val);
+
+	regmap_read(map, reg, &val);
+	*chip_rev = FIELD_GET(RTL83XX_CHIP_INFO_CHIP_REV, val);
+	*rl_id = FIELD_GET(RTL83XX_CHIP_INFO_RLID, val);
+}
+
+static void rtl_swcore_chip_print(struct device *dev, unsigned int model_id, char model_suffix,
+				  unsigned int chip_rev, unsigned int rl_id)
+{
+	dev_info(dev, "found RTL%04x%c rev. %c, RL:%04x\n",
+		 model_id, model_suffix, 'A' + (char) chip_rev, rl_id);
+}
+
+static void rtl838x_probe_model_name(const struct realtek_switchcore_ctrl *ctrl)
+{
+	unsigned int model_id = 0;
+	unsigned int chip_rev = 0;
+	char model_suffix = ' ';
+	unsigned int rl_id = 0;
+	u32 val = 0;
+
+	/*
+	 * CHIP_INFO register requires global register lock to be disabled.
+	 * Leave the lock disabled as a side effect, to allow other switch core R/W operations.
+	 */
+	regmap_write(ctrl->map, RTL838X_REG_INT_RW_CTRL, 0x3);
+
+	rtl83xx_read_chip_name(ctrl->map, RTL838X_REG_MODEL_NAME_INFO, &model_id, &model_suffix);
+
+	if (model_id == 0x8380) {
+		regmap_read(ctrl->map, RTL838X_REG_MODE_DEFINE_CTL, &val);
+		/*
+		 * Undocumented bit which is only set on RTL8380M.
+		 * Possibly related to the presence of QSGMII ports for external phy.
+		 */
+		if (!(val & BIT(23)))
+			model_id = 0x8381;
+	}
+
+	rtl83xx_read_chip_info(ctrl->map, RTL838X_REG_CHIP_INFO, &chip_rev, &rl_id);
+	/* 'A' cut is denoted by revision 1, etc. Non-838x revisions start at 0. */
+	if (chip_rev > 0)
+		chip_rev--;
+
+	rtl_swcore_chip_print(ctrl->dev, model_id, model_suffix, chip_rev, rl_id);
+}
+
+static void rtl839x_probe_model_name(const struct realtek_switchcore_ctrl *ctrl)
+{
+	unsigned int model_id = 0;
+	unsigned int chip_rev = 0;
+	char model_suffix = ' ';
+	unsigned int rl_id = 0;
+
+	rtl83xx_read_chip_name(ctrl->map, RTL839X_REG_MODEL_NAME_INFO, &model_id, &model_suffix);
+	rtl83xx_read_chip_info(ctrl->map, RTL839X_REG_CHIP_INFO, &chip_rev, &rl_id);
+	rtl_swcore_chip_print(ctrl->dev, model_id, model_suffix, chip_rev, rl_id);
+}
+
+static const struct mfd_cell rtl838x_mfd_devices[] = {
+	{
+		.name = "realtek-switchcore-sys-led",
+		.of_compatible = "realtek,maple-sys-led",
+	},
+	{
+		.name = "realtek-switchcore-port-leds",
+		.of_compatible = "realtek,maple-port-led",
+	},
+	{
+		.name = "realtek-switchcore-pinctrl",
+		.of_compatible = "realtek,maple-pinctrl",
+	},
+};
+
+static const struct realtek_switchcore_data rtl838x_switchcore_data = {
+	.mfd_devices = rtl838x_mfd_devices,
+	.mfd_device_count = ARRAY_SIZE(rtl838x_mfd_devices),
+	.probe_model_name = rtl838x_probe_model_name,
+};
+
+static const struct mfd_cell rtl839x_mfd_devices[] = {
+	{
+		.name = "realtek-switchcore-sys-led",
+		.of_compatible = "realtek,cypress-sys-led",
+	},
+	{
+		.name = "realtek-switchcore-port-leds",
+		.of_compatible = "realtek,cypress-port-led",
+	},
+	{
+		.name = "realtek-switchcore-pinctrl",
+		.of_compatible = "realtek,cypress-pinctrl",
+	},
+};
+
+static const struct realtek_switchcore_data rtl839x_switchcore_data = {
+	.mfd_devices = rtl839x_mfd_devices,
+	.mfd_device_count = ARRAY_SIZE(rtl839x_mfd_devices),
+	.probe_model_name = rtl839x_probe_model_name,
+};
+
+static const struct of_device_id of_realtek_switchcore_match[] = {
+	{
+		.compatible = "realtek,maple-switchcore",
+		.data = &rtl838x_switchcore_data,
+	},
+	{
+		.compatible = "realtek,cypress-switchcore",
+		.data = &rtl839x_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 realtek_switchcore_ctrl *ctrl;
+
+	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	ctrl->dev = dev;
+	ctrl->data = device_get_match_data(dev);
+	ctrl->map = syscon_node_to_regmap(dev->of_node);
+	if (!ctrl->map)
+		return dev_err_probe(dev, -ENXIO, "failed to get regmap\n");
+
+	ctrl->data->probe_model_name(ctrl);
+
+	return mfd_add_devices(dev, 0, ctrl->data->mfd_devices,
+			       ctrl->data->mfd_device_count, NULL, 0, NULL);
+}
+
+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@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..17bb79cf2d1b
--- /dev/null
+++ b/target/linux/realtek/patches-5.10/200-mfd-add-Realtek-switch-core-driver.patch
@@ -0,0 +1,46 @@ 
+From 2b4df5433baaa10eeb23a58012329d4ec47216ba Mon Sep 17 00:00:00 2001
+From: Sander Vanheule <sander@svanheule.net>
+Date: Mon, 11 Jul 2022 09:24:18 +0200
+Subject: 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,
+such as SoC identification.
+
+Signed-off-by: Sander Vanheule <sander@svanheule.net>
+---
+
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -1000,6 +1000,17 @@ config MFD_RETU
+ 	  Retu and Tahvo are a multi-function devices found on Nokia
+ 	  Internet Tablets (770, N800 and N810).
+ 
++config MFD_REALTEK_SWITCHCORE
++	bool "Realtek switch core driver"
++	select MFD_CORE
++	select REGMAP_MMIO
++	default RTL83XX
++	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