From: Rasmus Villemoes <ravi@prevas.dk>
To: Colin Foster <colin.foster@in-advantage.com>, Lee Jones <lee@kernel.org>
Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
Felix Blix Everberg <felix.blix@prevas.dk>,
Rasmus Villemoes <ravi@prevas.dk>
Subject: [PATCH 7/8] mfd: ocelot: enable support for mdio management
Date: Wed, 19 Mar 2025 13:30:57 +0100 [thread overview]
Message-ID: <20250319123058.452202-8-ravi@prevas.dk> (raw)
In-Reply-To: <20250319123058.452202-1-ravi@prevas.dk>
The implementation is rather straight-forward, following section
3.5.3 (MIIM interface in slave mode) in the data sheet for the
vsc7514.
Since each register access requires multiple MDIO accesses, keep the
parent mii_bus locked for the whole read/write in order that accesses
by different sub-devices do not end up corrupting each other. Since
the MFD among other things exposes an mdio bus to the switch's
internal PHYs, use MDIO_MUTEX_NESTED.
Looking through the data sheets of all of VSC7511, VSC7512, VSC7513,
VSC7514, I haven't seen any indication that they can be controlled
over I2C, so drop that mention from the Kconfig help text and add MDIO
in its place.
Signed-off-by: Rasmus Villemoes <ravi@prevas.dk>
---
drivers/mfd/Kconfig | 3 +-
drivers/mfd/Makefile | 2 +-
drivers/mfd/ocelot-mdio.c | 161 ++++++++++++++++++++++++++++++++++++++
3 files changed, 164 insertions(+), 2 deletions(-)
create mode 100644 drivers/mfd/ocelot-mdio.c
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 4dc894061b62e..c062563794d9e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1048,6 +1048,7 @@ config MFD_MENF21BMC
config MFD_OCELOT
tristate "Microsemi Ocelot External Control Support"
depends on SPI_MASTER
+ select PHYLIB
select MFD_CORE
select REGMAP
help
@@ -1056,7 +1057,7 @@ config MFD_OCELOT
other functions, including pinctrl, MDIO, and communication with
external chips. While some chips have an internal processor capable of
running an OS, others don't. All chips can be controlled externally
- through different interfaces, including SPI, I2C, and PCIe.
+ through different interfaces, including SPI, MDIO, and PCIe.
Say yes here to add support for Ocelot chips (VSC7511, VSC7512,
VSC7513, VSC7514) controlled externally.
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9220eaf7cf125..fc675ddd59f17 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -123,7 +123,7 @@ obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
-ocelot-soc-objs := ocelot-core.o ocelot-spi.o
+ocelot-soc-objs := ocelot-core.o ocelot-spi.o ocelot-mdio.o
obj-$(CONFIG_MFD_OCELOT) += ocelot-soc.o
obj-$(CONFIG_EZX_PCAP) += ezx-pcap.o
diff --git a/drivers/mfd/ocelot-mdio.c b/drivers/mfd/ocelot-mdio.c
new file mode 100644
index 0000000000000..7ac232a1ad6ad
--- /dev/null
+++ b/drivers/mfd/ocelot-mdio.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * MIIM core driver for the Ocelot chip family.
+ */
+
+/*
+ * Each register access requires multiple MDIO accesses.
+ */
+
+#include <linux/device.h>
+#include <linux/mdio.h>
+#include <linux/phy.h>
+#include <linux/regmap.h>
+
+#include "ocelot.h"
+
+#define ADDR_REG0 0
+#define ADDR_REG1 1
+#define DATA_REG0 2
+#define DATA_REG1 3
+
+static int
+ocelot_mdio_write_addr(struct mdio_device *mdiodev, unsigned int addr)
+{
+ int ret;
+
+ addr &= 0x00ffffff;
+ addr >>= 2;
+
+ ret = __mdiodev_write(mdiodev, ADDR_REG0, addr & 0xffff);
+ if (ret)
+ return ret;
+
+ return __mdiodev_write(mdiodev, ADDR_REG1, addr >> 16);
+}
+
+static int
+__ocelot_mdio_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct mdio_device *mdiodev = context;
+ int ret;
+
+ ret = ocelot_mdio_write_addr(mdiodev, reg);
+ if (ret)
+ return ret;
+
+ ret = __mdiodev_write(mdiodev, DATA_REG0, val & 0xffff);
+ if (ret)
+ return ret;
+
+ return __mdiodev_write(mdiodev, DATA_REG1, val >> 16);
+}
+
+static int
+__ocelot_mdio_read(struct mdio_device *mdiodev, unsigned int reg, unsigned int *val)
+{
+ int ret, lo, hi, i;
+
+ ret = ocelot_mdio_write_addr(mdiodev, reg);
+ if (ret)
+ return ret;
+
+ /*
+ * The data registers must be read twice. Only after the first
+ * read is the value of the register whose address was written
+ * into the address registers latched into the data registers.
+ */
+ for (i = 0; i < 2; ++i) {
+ lo = __mdiodev_read(mdiodev, DATA_REG0);
+ if (lo < 0)
+ return lo;
+ hi = __mdiodev_read(mdiodev, DATA_REG1);
+ if (hi < 0)
+ return hi;
+ }
+
+ *val = (hi << 16) | (lo & 0xffff);
+
+ return 0;
+}
+
+static int
+ocelot_mdio_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct mdio_device *mdiodev = context;
+ int ret;
+
+ mutex_lock_nested(&mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
+ ret = __ocelot_mdio_write(mdiodev, reg, val);
+ mutex_unlock(&mdiodev->bus->mdio_lock);
+
+ return ret;
+}
+
+static int
+ocelot_mdio_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct mdio_device *mdiodev = context;
+ int ret;
+
+ mutex_lock_nested(&mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
+ ret = __ocelot_mdio_read(mdiodev, reg, val);
+ mutex_unlock(&mdiodev->bus->mdio_lock);
+
+ return ret;
+}
+
+static const struct regmap_bus ocelot_mdio_regmap_bus = {
+ .reg_write = ocelot_mdio_write,
+ .reg_read = ocelot_mdio_read,
+};
+
+static struct regmap *ocelot_mdio_init_regmap(struct device *dev, const struct resource *res)
+{
+ struct mdio_device *mdiodev = to_mdio_device(dev);
+ struct regmap_config regmap_config = {};
+
+ regmap_config.reg_bits = 32;
+ regmap_config.reg_stride = 4;
+ regmap_config.val_bits = 32;
+ regmap_config.name = res->name;
+ regmap_config.max_register = resource_size(res) - 1;
+ regmap_config.reg_base = res->start;
+
+ return devm_regmap_init(dev, &ocelot_mdio_regmap_bus, mdiodev, ®map_config);
+}
+
+static int ocelot_mdio_probe(struct mdio_device *mdiodev)
+{
+ struct device *dev = &mdiodev->dev;
+ struct ocelot_ddata *ddata;
+
+ ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, ddata);
+ ddata->init_regmap = ocelot_mdio_init_regmap;
+
+ return ocelot_core_init(dev);
+}
+
+static const struct of_device_id ocelot_mdio_of_match[] = {
+ { .compatible = "mscc,vsc7512" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ocelot_mdio_of_match);
+
+static struct mdio_driver ocelot_mdio_driver = {
+ .probe = ocelot_mdio_probe,
+ .mdiodrv.driver = {
+ .name = "ocelot-soc",
+ .of_match_table = ocelot_mdio_of_match,
+ },
+};
+mdio_module_driver(ocelot_mdio_driver);
+
+MODULE_DESCRIPTION("MDIO Controlled Ocelot Chip Driver");
+MODULE_AUTHOR("Rasmus Villemoes <ravi@prevas.dk>");
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_IMPORT_NS("MFD_OCELOT");
--
2.49.0
next prev parent reply other threads:[~2025-03-19 12:31 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-19 12:30 [PATCH 0/8] mfd: ocelot: add support for MDIO managed switch Rasmus Villemoes
2025-03-19 12:30 ` [PATCH 1/8] mfd: ocelot: refactor bus-specific regmap initialization Rasmus Villemoes
2025-03-19 20:08 ` Colin Foster
2025-03-21 11:41 ` Lee Jones
2025-03-21 12:39 ` Rasmus Villemoes
2025-03-28 8:37 ` Lee Jones
2025-03-19 12:30 ` [PATCH 2/8] mfd: ocelot: move SPI specific macros to ocelot-spi.c Rasmus Villemoes
2025-03-19 20:11 ` Colin Foster
2025-03-19 12:30 ` [PATCH 3/8] mfd: ocelot: rework SPI (re-)initialization after chip reset Rasmus Villemoes
2025-03-19 22:08 ` Colin Foster
2025-03-20 11:17 ` Rasmus Villemoes
2025-03-22 13:36 ` Colin Foster
2025-03-25 15:35 ` Rasmus Villemoes
2025-03-19 12:30 ` [PATCH 4/8] mfd: ocelot: lift chip reset logic to ocelot-core.c Rasmus Villemoes
2025-03-19 22:44 ` Colin Foster
2025-03-19 12:30 ` [PATCH 5/8] mfd: ocelot: make ocelot_chip_init() static Rasmus Villemoes
2025-03-19 22:45 ` Colin Foster
2025-03-19 12:30 ` [PATCH 6/8] mfd: ocelot: correct Kconfig dependency Rasmus Villemoes
2025-03-19 22:48 ` Colin Foster
2025-03-19 12:30 ` Rasmus Villemoes [this message]
2025-03-19 12:30 ` [PATCH 8/8] dt-bindings: mfd: ocelot: mention MDIO management and add example Rasmus Villemoes
2025-03-19 13:24 ` Rob Herring (Arm)
2025-03-20 14:25 ` Rasmus Villemoes
2025-03-19 19:55 ` [PATCH 0/8] mfd: ocelot: add support for MDIO managed switch Colin Foster
2025-06-30 15:14 ` Colin Foster
2025-07-01 10:52 ` Rasmus Villemoes
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250319123058.452202-8-ravi@prevas.dk \
--to=ravi@prevas.dk \
--cc=colin.foster@in-advantage.com \
--cc=devicetree@vger.kernel.org \
--cc=felix.blix@prevas.dk \
--cc=lee@kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®