2023-03-24 09:37:32

by Maxime Chevallier

[permalink] [raw]
Subject: [RFC 0/7] Introduce a generic regmap-based MDIO driver

Hello everyone,

When the Altera TSE PCS driver was initially introduced, there were
comments by Russell that the register layout looked very familiar to the
existing Lynx PCS driver, the only difference being that the TSE PCS
driver is memory-mapped whereas the Lynx PCS driver sits on an MDIO bus.

Since then, I've sent a followup to create a wrapper around Lynx, that
would create a virtual MDIO bus driver that would translate the mdio
operations to mmio operations [1].

Discussion ensued, and we agreed that we could make this more generic,
the idea being that this PCS isn't the only example of such a device,
that memory-maps an mdio-like register layout

Vladimir mentionned that some ocelot devices have the same kind of
problematic, but this time the devices sits on a SPI bus, exposing MDIO
registers.

This series therefore introduces a new "virtual" mdio driver, that would
translate MDIO accesses to an underlying regmap. That way, we can use
the whole phylink and phylib logic to configure the device (as phylink
interacts with mdiodevice's).

One issue encountered is that the MDIO registers have a stride of 1,
and we need to translate that to a higher stride for memory-mapped
devices. This is what the first 3 patches of this series do.

Then, patch 4 addresses a small inconsistency found in the only user of
regmap's reg_downshift.

Patch 5 introduces the new MDIO driver, while patch 6 makes use of it
by porting altera_tse to the Lynx PCS.

Finally patch 7 drops the TSE PCS driver, as it is no longer needed.

This series is a RFC as is still has a few shortcomings, but due to
various subsystems being involved, I'm sending it as a whole so that
reviewers can get a clear view of the big picture, the end-goal and the
various problems faces.

The existing shortcomings are :
- The virtual MDIO bus driver can only have 1 mdio-device on it. If we
have multiple ones that are memory-mapped onto the same range (with
an offset, for example), we can't address them and we would have to
create one such virtual bus driver per device. I don't know as of
today if the problem will show-up for some users

- With this series, we can also convert dwmac_socfpga to Lynx, but I've
left this out for now

- The renaming of regmap.format.reg_downshift to regmap.format.reg_shift
can be confusing, as regmap.reg_shift also exists and has another
meaning. I'm very bad at naming, so any suggestion is welcome.

Thanks,

Maxime

[1] : https://lore.kernel.org/all/[email protected]/

Maxime Chevallier (7):
regmap: add a helper to translate the register address
regmap: check for alignment on translated register addresses
regmap: allow upshifting register addresses before performing
operations
mfd: ocelot-spi: Change the regmap stride to reflect the real one
net: mdio: Introduce a regmap-based mdio driver
net: ethernet: altera-tse: Convert to mdio-regmap and use PCS Lynx
net: pcs: Drop the TSE PCS driver

MAINTAINERS | 14 +-
drivers/base/regmap/internal.h | 2 +-
drivers/base/regmap/regmap.c | 55 +++---
drivers/mfd/ocelot-spi.c | 4 +-
drivers/net/ethernet/altera/altera_tse.h | 1 +
drivers/net/ethernet/altera/altera_tse_main.c | 54 +++++-
drivers/net/mdio/Kconfig | 11 ++
drivers/net/mdio/Makefile | 1 +
drivers/net/mdio/mdio-regmap.c | 85 ++++++++++
drivers/net/pcs/Kconfig | 6 -
drivers/net/pcs/Makefile | 1 -
drivers/net/pcs/pcs-altera-tse.c | 160 ------------------
include/linux/mdio/mdio-regmap.h | 25 +++
include/linux/pcs-altera-tse.h | 17 --
include/linux/regmap.h | 15 +-
15 files changed, 223 insertions(+), 228 deletions(-)
create mode 100644 drivers/net/mdio/mdio-regmap.c
delete mode 100644 drivers/net/pcs/pcs-altera-tse.c
create mode 100644 include/linux/mdio/mdio-regmap.h
delete mode 100644 include/linux/pcs-altera-tse.h

--
2.39.2


2023-03-24 09:37:53

by Maxime Chevallier

[permalink] [raw]
Subject: [RFC 1/7] regmap: add a helper to translate the register address

Register addresses passed to regmap operations can be offset with
regmap.reg_base and downshifted with regmap.reg_downshift.

Add a helper to apply both these operations and return the translated
address, that we can then use to perform the actual register operation
ont the underlying bus.

Signed-off-by: Maxime Chevallier <[email protected]>
---
drivers/base/regmap/regmap.c | 27 +++++++++++++--------------
1 file changed, 13 insertions(+), 14 deletions(-)

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index d2a54eb0efd9..a4e4367648bf 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -1676,6 +1676,12 @@ static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes,
buf[i] |= (mask >> (8 * i)) & 0xff;
}

+static unsigned int regmap_reg_addr(struct regmap *map, unsigned int reg)
+{
+ reg += map->reg_base;
+ return reg >> map->format.reg_downshift;
+}
+
static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
const void *val, size_t val_len, bool noinc)
{
@@ -1753,8 +1759,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
return ret;
}

- reg += map->reg_base;
- reg >>= map->format.reg_downshift;
+ reg = regmap_reg_addr(map, reg);
map->format.format_reg(map->work_buf, reg, map->reg_shift);
regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
map->write_flag_mask);
@@ -1924,8 +1929,7 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg,
return ret;
}

- reg += map->reg_base;
- reg >>= map->format.reg_downshift;
+ reg = regmap_reg_addr(map, reg);
map->format.format_write(map, reg, val);

trace_regmap_hw_write_start(map, reg, 1);
@@ -1942,8 +1946,7 @@ static int _regmap_bus_reg_write(void *context, unsigned int reg,
{
struct regmap *map = context;

- reg += map->reg_base;
- reg >>= map->format.reg_downshift;
+ reg = regmap_reg_addr(map, reg);
return map->bus->reg_write(map->bus_context, reg, val);
}

@@ -2494,8 +2497,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map,
unsigned int reg = regs[i].reg;
unsigned int val = regs[i].def;
trace_regmap_hw_write_start(map, reg, 1);
- reg += map->reg_base;
- reg >>= map->format.reg_downshift;
+ reg = regmap_reg_addr(map, reg);
map->format.format_reg(u8, reg, map->reg_shift);
u8 += reg_bytes + pad_bytes;
map->format.format_val(u8, val, 0);
@@ -2821,8 +2823,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
return ret;
}

- reg += map->reg_base;
- reg >>= map->format.reg_downshift;
+ reg = regmap_reg_addr(map, reg);
map->format.format_reg(map->work_buf, reg, map->reg_shift);
regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
map->read_flag_mask);
@@ -2842,8 +2843,7 @@ static int _regmap_bus_reg_read(void *context, unsigned int reg,
{
struct regmap *map = context;

- reg += map->reg_base;
- reg >>= map->format.reg_downshift;
+ reg = regmap_reg_addr(map, reg);
return map->bus->reg_read(map->bus_context, reg, val);
}

@@ -3235,8 +3235,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
*change = false;

if (regmap_volatile(map, reg) && map->reg_update_bits) {
- reg += map->reg_base;
- reg >>= map->format.reg_downshift;
+ reg = regmap_reg_addr(map, reg);
ret = map->reg_update_bits(map->bus_context, reg, mask, val);
if (ret == 0 && change)
*change = true;
--
2.39.2

2023-03-24 09:38:07

by Maxime Chevallier

[permalink] [raw]
Subject: [RFC 3/7] regmap: allow upshifting register addresses before performing operations

Similar to the existing reg_downshift mechanism, that is used to
translate register addresses on busses that have a smaller address
stride, it's also possible to want to upshift register addresses.

Such a case was encountered when network PHYs and PCS that usually sit
on a MDIO bus (16-bits register with a stride of 1) are integrated
directly as memory-mapped devices. Here, the same register layout
defined in 802.3 is used, but the register now have a larger stride.

Introduce a mechanism to also allow upshifting register addresses.
Re-purpose reg_downshift into a more generic, signed reg_shift, whose
sign indicates the direction of the shift. To avoid confusion, also
introduce macros to explicitly indicate if we want to downshift or
upshift.

For bisectability, change any use of reg_downshift to use reg_shift.

Signed-off-by: Maxime Chevallier <[email protected]>
---
drivers/base/regmap/internal.h | 2 +-
drivers/base/regmap/regmap.c | 10 ++++++++--
drivers/mfd/ocelot-spi.c | 2 +-
include/linux/regmap.h | 15 ++++++++++++---
4 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index da8996e7a1f1..2ef53936652b 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -31,7 +31,7 @@ struct regmap_format {
size_t buf_size;
size_t reg_bytes;
size_t pad_bytes;
- size_t reg_downshift;
+ int reg_shift;
size_t val_bytes;
void (*format_write)(struct regmap *map,
unsigned int reg, unsigned int val);
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 726f59612fd6..c4cde4f45b05 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -814,7 +814,7 @@ struct regmap *__regmap_init(struct device *dev,

map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
map->format.pad_bytes = config->pad_bits / 8;
- map->format.reg_downshift = config->reg_downshift;
+ map->format.reg_shift = config->reg_shift;
map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
config->val_bits + config->pad_bits, 8);
@@ -1679,7 +1679,13 @@ static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes,
static unsigned int regmap_reg_addr(struct regmap *map, unsigned int reg)
{
reg += map->reg_base;
- return reg >> map->format.reg_downshift;
+
+ if (map->format.reg_shift > 0)
+ reg >>= map->format.reg_shift;
+ else if (map->format.reg_shift < 0)
+ reg <<= -(map->format.reg_shift);
+
+ return reg;
}

static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
diff --git a/drivers/mfd/ocelot-spi.c b/drivers/mfd/ocelot-spi.c
index 2ecd271de2fb..2d1349a10ca9 100644
--- a/drivers/mfd/ocelot-spi.c
+++ b/drivers/mfd/ocelot-spi.c
@@ -125,7 +125,7 @@ static int ocelot_spi_initialize(struct device *dev)
static const struct regmap_config ocelot_spi_regmap_config = {
.reg_bits = 24,
.reg_stride = 4,
- .reg_downshift = 2,
+ .reg_shift = REGMAP_DOWNSHIFT(2),
.val_bits = 32,

.write_flag_mask = 0x80,
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 4d10790adeb0..f02c3857b023 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -46,6 +46,14 @@ struct sdw_slave;
#define REGMAP_MDIO_C45_DEVAD_MASK GENMASK(20, 16)
#define REGMAP_MDIO_C45_REGNUM_MASK GENMASK(15, 0)

+/*
+ * regmap.reg_shift indicates by how much we must shift registers prior to
+ * performing any operation. It's a signed value, positive numbers means
+ * downshifting the register's address, while negative numbers means upshifting.
+ */
+#define REGMAP_UPSHIFT(s) (-(s))
+#define REGMAP_DOWNSHIFT(s) (s)
+
/* An enum of all the supported cache types */
enum regcache_type {
REGCACHE_NONE,
@@ -246,8 +254,9 @@ typedef void (*regmap_unlock)(void *);
* @reg_stride: The register address stride. Valid register addresses are a
* multiple of this value. If set to 0, a value of 1 will be
* used.
- * @reg_downshift: The number of bits to downshift the register before
- * performing any operations.
+ * @reg_shift: The number of bits to shift the register before performing any
+ * operations. Any positive number will be downshifted, and negative
+ * values will be upshifted
* @reg_base: Value to be added to every register address before performing any
* operation.
* @pad_bits: Number of bits of padding between register and value.
@@ -381,7 +390,7 @@ struct regmap_config {

int reg_bits;
int reg_stride;
- int reg_downshift;
+ int reg_shift;
unsigned int reg_base;
int pad_bits;
int val_bits;
--
2.39.2

2023-03-24 09:38:41

by Maxime Chevallier

[permalink] [raw]
Subject: [RFC 4/7] mfd: ocelot-spi: Change the regmap stride to reflect the real one

When used over SPI, the register addresses needs to be translated,
compared to when used over MMIO. The translation consists in applying an
offset with reg_base, then downshifting the registers by 2. This
actually changes the register stride from 4 to 1.

Signed-off-by: Maxime Chevallier <[email protected]>
---
drivers/mfd/ocelot-spi.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mfd/ocelot-spi.c b/drivers/mfd/ocelot-spi.c
index 2d1349a10ca9..107cda0544aa 100644
--- a/drivers/mfd/ocelot-spi.c
+++ b/drivers/mfd/ocelot-spi.c
@@ -124,7 +124,7 @@ static int ocelot_spi_initialize(struct device *dev)

static const struct regmap_config ocelot_spi_regmap_config = {
.reg_bits = 24,
- .reg_stride = 4,
+ .reg_stride = 1,
.reg_shift = REGMAP_DOWNSHIFT(2),
.val_bits = 32,

--
2.39.2

2023-03-24 09:38:43

by Maxime Chevallier

[permalink] [raw]
Subject: [RFC 5/7] net: mdio: Introduce a regmap-based mdio driver

There exists several examples today of devices that embed an ethernet
PHY or PCS directly inside an SoC. In this situation, either the device
is controlled through a vendor-specific register set, or sometimes
exposes the standard 802.3 registers that are typically accessed over
MDIO.

As phylib and phylink are designed to use mdiodevices, this driver
allows creating a virtual MDIO bus, that translates mdiodev register
accesses to regmap accesses.

The reason we use regmap is because there are at least 3 such devices
known today, 2 of them are Altera TSE PCS's, memory-mapped, exposed
with a 4-byte stride in stmmac's dwmac-socfpga variant, and a 2-byte
stride in altera-tse. The other one (nxp,sja1110-base-tx-mdio) is
exposed over SPI.

Signed-off-by: Maxime Chevallier <[email protected]>
---
MAINTAINERS | 7 +++
drivers/net/mdio/Kconfig | 11 +++++
drivers/net/mdio/Makefile | 1 +
drivers/net/mdio/mdio-regmap.c | 85 ++++++++++++++++++++++++++++++++
include/linux/mdio/mdio-regmap.h | 25 ++++++++++
5 files changed, 129 insertions(+)
create mode 100644 drivers/net/mdio/mdio-regmap.c
create mode 100644 include/linux/mdio/mdio-regmap.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 8d5bc223f305..10b3a1800e0d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12751,6 +12751,13 @@ F: Documentation/devicetree/bindings/net/ieee802154/mcr20a.txt
F: drivers/net/ieee802154/mcr20a.c
F: drivers/net/ieee802154/mcr20a.h

+MDIO REGMAP DRIVER
+M: Maxime Chevallier <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/net/mdio/mdio-regmap.c
+F: include/linux/mdio/mdio-regmap.h
+
MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
M: William Breathitt Gray <[email protected]>
L: [email protected]
diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig
index 90309980686e..671e4bb82e3e 100644
--- a/drivers/net/mdio/Kconfig
+++ b/drivers/net/mdio/Kconfig
@@ -182,6 +182,17 @@ config MDIO_IPQ8064
This driver supports the MDIO interface found in the network
interface units of the IPQ8064 SoC

+config MDIO_REGMAP
+ tristate "Regmap-based virtual MDIO bus driver"
+ depends on REGMAP
+ help
+ This driver allows using MDIO devices that are not sitting on a
+ regular MDIO bus, but still exposes the standard 802.3 register
+ layout. It's regmap-based so that it can be used on integrated,
+ memory-mapped PHYs, SPI PHYs and so on. A new virtual MDIO bus is
+ created, and its read/write operations are mapped to the underlying
+ regmap.
+
config MDIO_THUNDER
tristate "ThunderX SOCs MDIO buses"
depends on 64BIT
diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile
index 7d4cb4c11e4e..1015f0db4531 100644
--- a/drivers/net/mdio/Makefile
+++ b/drivers/net/mdio/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
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_REGMAP) += mdio-regmap.o
obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o
obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o
diff --git a/drivers/net/mdio/mdio-regmap.c b/drivers/net/mdio/mdio-regmap.c
new file mode 100644
index 000000000000..c85d62c2f55c
--- /dev/null
+++ b/drivers/net/mdio/mdio-regmap.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Driver for MMIO-Mapped MDIO devices. Some IPs expose internal PHYs or PCS
+ * within the MMIO-mapped area
+ *
+ * Copyright (C) 2023 Maxime Chevallier <[email protected]>
+ */
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mdio/mdio-regmap.h>
+
+#define DRV_NAME "mdio-regmap"
+
+static int mdio_regmap_read_c22(struct mii_bus *bus, int addr, int regnum)
+{
+ struct mdio_regmap_config *ctx = bus->priv;
+ unsigned int val;
+ int ret;
+
+ if (!(ctx->valid_addr & BIT(addr)))
+ return -ENODEV;
+
+ ret = regmap_read(ctx->regmap, regnum, &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+static int mdio_regmap_write_c22(struct mii_bus *bus, int addr, int regnum,
+ u16 val)
+{
+ struct mdio_regmap_config *ctx = bus->priv;
+
+ if (!(ctx->valid_addr & BIT(addr)))
+ return -ENODEV;
+
+ return regmap_write(ctx->regmap, regnum, val);
+}
+
+struct mii_bus *devm_mdio_regmap_register(struct device *dev,
+ const struct mdio_regmap_config *config)
+{
+ struct mdio_regmap_config *mrc;
+ struct mii_bus *mii;
+ int rc;
+
+ if (!config->parent)
+ return ERR_PTR(-EINVAL);
+
+ if (!config->valid_addr)
+ return ERR_PTR(-EINVAL);
+
+ mii = devm_mdiobus_alloc_size(config->parent, sizeof(*mrc));
+ if (!mii)
+ return ERR_PTR(-ENOMEM);
+
+ mrc = mii->priv;
+ memcpy(mrc, config, sizeof(*mrc));
+
+ mrc->regmap = config->regmap;
+ mrc->parent = config->parent;
+ mrc->valid_addr = config->valid_addr;
+
+ mii->name = DRV_NAME;
+ strncpy(mii->id, config->name, MII_BUS_ID_SIZE);
+ mii->parent = config->parent;
+ mii->read = mdio_regmap_read_c22;
+ mii->write = mdio_regmap_write_c22;
+
+ rc = devm_mdiobus_register(dev, mii);
+ if (rc) {
+ dev_err(config->parent, "Cannot register MDIO bus![%s] (%d)\n", mii->id, rc);
+ return ERR_PTR(rc);
+ }
+
+ return mii;
+}
+
diff --git a/include/linux/mdio/mdio-regmap.h b/include/linux/mdio/mdio-regmap.h
new file mode 100644
index 000000000000..ea428e5a2913
--- /dev/null
+++ b/include/linux/mdio/mdio-regmap.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Driver for MMIO-Mapped MDIO devices. Some IPs expose internal PHYs or PCS
+ * within the MMIO-mapped area
+ *
+ * Copyright (C) 2023 Maxime Chevallier <[email protected]>
+ */
+#ifndef MDIO_REGMAP_H
+#define MDIO_REGMAP_H
+
+#define MDIO_REGMAP_NAME 63
+
+struct device;
+struct regmap;
+
+struct mdio_regmap_config {
+ struct device *parent;
+ struct regmap *regmap;
+ char name[MDIO_REGMAP_NAME];
+ u32 valid_addr;
+};
+
+struct mii_bus *devm_mdio_regmap_register(struct device *dev,
+ const struct mdio_regmap_config *config);
+
+#endif
--
2.39.2

2023-03-24 09:39:09

by Maxime Chevallier

[permalink] [raw]
Subject: [RFC 2/7] regmap: check for alignment on translated register addresses

With regmap->reg_base and regmap->reg_downshift, the actual register
address that is going to be used for the next operation might not be the
same as the one we have as an input. Addresses can be offset with
reg_base and shifted, which will affect alignment.

Check for alignment on the real register address.

Signed-off-by: Maxime Chevallier <[email protected]>
---
drivers/base/regmap/regmap.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index a4e4367648bf..726f59612fd6 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -2016,7 +2016,7 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;

- if (!IS_ALIGNED(reg, map->reg_stride))
+ if (!IS_ALIGNED(regmap_reg_addr(map, reg), map->reg_stride))
return -EINVAL;

map->lock(map->lock_arg);
@@ -2043,7 +2043,7 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;

- if (!IS_ALIGNED(reg, map->reg_stride))
+ if (!IS_ALIGNED(regmap_reg_addr(map, reg), map->reg_stride))
return -EINVAL;

map->lock(map->lock_arg);
@@ -2258,7 +2258,7 @@ int regmap_noinc_write(struct regmap *map, unsigned int reg,
return -EINVAL;
if (val_len % map->format.val_bytes)
return -EINVAL;
- if (!IS_ALIGNED(reg, map->reg_stride))
+ if (!IS_ALIGNED(regmap_reg_addr(map, reg), map->reg_stride))
return -EINVAL;
if (val_len == 0)
return -EINVAL;
@@ -2399,7 +2399,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
int ret = 0, i;
size_t val_bytes = map->format.val_bytes;

- if (!IS_ALIGNED(reg, map->reg_stride))
+ if (!IS_ALIGNED(regmap_reg_addr(map, reg), map->reg_stride))
return -EINVAL;

/*
@@ -2638,7 +2638,7 @@ static int _regmap_multi_reg_write(struct regmap *map,
int reg = regs[i].reg;
if (!map->writeable_reg(map->dev, reg))
return -EINVAL;
- if (!IS_ALIGNED(reg, map->reg_stride))
+ if (!IS_ALIGNED(regmap_reg_addr(map, reg), map->reg_stride))
return -EINVAL;
}

@@ -2789,7 +2789,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,

if (val_len % map->format.val_bytes)
return -EINVAL;
- if (!IS_ALIGNED(reg, map->reg_stride))
+ if (!IS_ALIGNED(regmap_reg_addr(map, reg), map->reg_stride))
return -EINVAL;

map->lock(map->lock_arg);
@@ -2911,7 +2911,7 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
{
int ret;

- if (!IS_ALIGNED(reg, map->reg_stride))
+ if (!IS_ALIGNED(regmap_reg_addr(map, reg), map->reg_stride))
return -EINVAL;

map->lock(map->lock_arg);
@@ -2945,7 +2945,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,

if (val_len % map->format.val_bytes)
return -EINVAL;
- if (!IS_ALIGNED(reg, map->reg_stride))
+ if (!IS_ALIGNED(regmap_reg_addr(map, reg), map->reg_stride))
return -EINVAL;
if (val_count == 0)
return -EINVAL;
@@ -3040,7 +3040,7 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,

if (val_len % map->format.val_bytes)
return -EINVAL;
- if (!IS_ALIGNED(reg, map->reg_stride))
+ if (!IS_ALIGNED(regmap_reg_addr(map, reg), map->reg_stride))
return -EINVAL;
if (val_len == 0)
return -EINVAL;
@@ -3162,7 +3162,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
size_t val_bytes = map->format.val_bytes;
bool vol = regmap_volatile_range(map, reg, val_count);

- if (!IS_ALIGNED(reg, map->reg_stride))
+ if (!IS_ALIGNED(regmap_reg_addr(map, reg), map->reg_stride))
return -EINVAL;
if (val_count == 0)
return -EINVAL;
--
2.39.2

2023-03-24 09:39:12

by Maxime Chevallier

[permalink] [raw]
Subject: [RFC 7/7] net: pcs: Drop the TSE PCS driver

Now that we can easily create a mdio-device that represents a
memory-mapped device that exposes an MDIO-like register layout, we don't
need the Altera TSE PCS anymore, since we can use the Lynx PCS instead.

Signed-off-by: Maxime Chevallier <[email protected]>
---
MAINTAINERS | 7 --
drivers/net/pcs/Kconfig | 6 --
drivers/net/pcs/Makefile | 1 -
drivers/net/pcs/pcs-altera-tse.c | 160 -------------------------------
include/linux/pcs-altera-tse.h | 17 ----
5 files changed, 191 deletions(-)
delete mode 100644 drivers/net/pcs/pcs-altera-tse.c
delete mode 100644 include/linux/pcs-altera-tse.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 10b3a1800e0d..e2e648a46e3f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -909,13 +909,6 @@ L: [email protected]
S: Maintained
F: drivers/net/ethernet/altera/

-ALTERA TSE PCS
-M: Maxime Chevallier <[email protected]>
-L: [email protected]
-S: Supported
-F: drivers/net/pcs/pcs-altera-tse.c
-F: include/linux/pcs-altera-tse.h
-
ALTERA UART/JTAG UART SERIAL DRIVERS
M: Tobias Klauser <[email protected]>
L: [email protected]
diff --git a/drivers/net/pcs/Kconfig b/drivers/net/pcs/Kconfig
index 6e7e6c346a3e..6289b7c765f1 100644
--- a/drivers/net/pcs/Kconfig
+++ b/drivers/net/pcs/Kconfig
@@ -26,10 +26,4 @@ config PCS_RZN1_MIIC
on RZ/N1 SoCs. This PCS converts MII to RMII/RGMII or can be set in
pass-through mode for MII.

-config PCS_ALTERA_TSE
- tristate
- help
- This module provides helper functions for the Altera Triple Speed
- Ethernet SGMII PCS, that can be found on the Intel Socfpga family.
-
endmenu
diff --git a/drivers/net/pcs/Makefile b/drivers/net/pcs/Makefile
index 4c780d8f2e98..0ff5388fcdea 100644
--- a/drivers/net/pcs/Makefile
+++ b/drivers/net/pcs/Makefile
@@ -6,4 +6,3 @@ pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o
obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o
-obj-$(CONFIG_PCS_ALTERA_TSE) += pcs-altera-tse.o
diff --git a/drivers/net/pcs/pcs-altera-tse.c b/drivers/net/pcs/pcs-altera-tse.c
deleted file mode 100644
index d616749761f4..000000000000
--- a/drivers/net/pcs/pcs-altera-tse.c
+++ /dev/null
@@ -1,160 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2022 Bootlin
- *
- * Maxime Chevallier <[email protected]>
- */
-
-#include <linux/netdevice.h>
-#include <linux/phy.h>
-#include <linux/phylink.h>
-#include <linux/pcs-altera-tse.h>
-
-/* SGMII PCS register addresses
- */
-#define SGMII_PCS_LINK_TIMER_0 0x12
-#define SGMII_PCS_LINK_TIMER_1 0x13
-#define SGMII_PCS_IF_MODE 0x14
-#define PCS_IF_MODE_SGMII_ENA BIT(0)
-#define PCS_IF_MODE_USE_SGMII_AN BIT(1)
-#define PCS_IF_MODE_SGMI_HALF_DUPLEX BIT(4)
-#define PCS_IF_MODE_SGMI_PHY_AN BIT(5)
-#define SGMII_PCS_SW_RESET_TIMEOUT 100 /* usecs */
-
-struct altera_tse_pcs {
- struct phylink_pcs pcs;
- void __iomem *base;
- int reg_width;
-};
-
-static struct altera_tse_pcs *phylink_pcs_to_tse_pcs(struct phylink_pcs *pcs)
-{
- return container_of(pcs, struct altera_tse_pcs, pcs);
-}
-
-static u16 tse_pcs_read(struct altera_tse_pcs *tse_pcs, int regnum)
-{
- if (tse_pcs->reg_width == 4)
- return readl(tse_pcs->base + regnum * 4);
- else
- return readw(tse_pcs->base + regnum * 2);
-}
-
-static void tse_pcs_write(struct altera_tse_pcs *tse_pcs, int regnum,
- u16 value)
-{
- if (tse_pcs->reg_width == 4)
- writel(value, tse_pcs->base + regnum * 4);
- else
- writew(value, tse_pcs->base + regnum * 2);
-}
-
-static int tse_pcs_reset(struct altera_tse_pcs *tse_pcs)
-{
- u16 bmcr;
-
- /* Reset PCS block */
- bmcr = tse_pcs_read(tse_pcs, MII_BMCR);
- bmcr |= BMCR_RESET;
- tse_pcs_write(tse_pcs, MII_BMCR, bmcr);
-
- return read_poll_timeout(tse_pcs_read, bmcr, (bmcr & BMCR_RESET),
- 10, SGMII_PCS_SW_RESET_TIMEOUT, 1,
- tse_pcs, MII_BMCR);
-}
-
-static int alt_tse_pcs_validate(struct phylink_pcs *pcs,
- unsigned long *supported,
- const struct phylink_link_state *state)
-{
- if (state->interface == PHY_INTERFACE_MODE_SGMII ||
- state->interface == PHY_INTERFACE_MODE_1000BASEX)
- return 1;
-
- return -EINVAL;
-}
-
-static int alt_tse_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
- phy_interface_t interface,
- const unsigned long *advertising,
- bool permit_pause_to_mac)
-{
- struct altera_tse_pcs *tse_pcs = phylink_pcs_to_tse_pcs(pcs);
- u32 ctrl, if_mode;
-
- ctrl = tse_pcs_read(tse_pcs, MII_BMCR);
- if_mode = tse_pcs_read(tse_pcs, SGMII_PCS_IF_MODE);
-
- /* Set link timer to 1.6ms, as per the MegaCore Function User Guide */
- tse_pcs_write(tse_pcs, SGMII_PCS_LINK_TIMER_0, 0x0D40);
- tse_pcs_write(tse_pcs, SGMII_PCS_LINK_TIMER_1, 0x03);
-
- if (interface == PHY_INTERFACE_MODE_SGMII) {
- if_mode |= PCS_IF_MODE_USE_SGMII_AN | PCS_IF_MODE_SGMII_ENA;
- } else if (interface == PHY_INTERFACE_MODE_1000BASEX) {
- if_mode &= ~(PCS_IF_MODE_USE_SGMII_AN | PCS_IF_MODE_SGMII_ENA);
- }
-
- ctrl |= (BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE);
-
- tse_pcs_write(tse_pcs, MII_BMCR, ctrl);
- tse_pcs_write(tse_pcs, SGMII_PCS_IF_MODE, if_mode);
-
- return tse_pcs_reset(tse_pcs);
-}
-
-static void alt_tse_pcs_get_state(struct phylink_pcs *pcs,
- struct phylink_link_state *state)
-{
- struct altera_tse_pcs *tse_pcs = phylink_pcs_to_tse_pcs(pcs);
- u16 bmsr, lpa;
-
- bmsr = tse_pcs_read(tse_pcs, MII_BMSR);
- lpa = tse_pcs_read(tse_pcs, MII_LPA);
-
- phylink_mii_c22_pcs_decode_state(state, bmsr, lpa);
-}
-
-static void alt_tse_pcs_an_restart(struct phylink_pcs *pcs)
-{
- struct altera_tse_pcs *tse_pcs = phylink_pcs_to_tse_pcs(pcs);
- u16 bmcr;
-
- bmcr = tse_pcs_read(tse_pcs, MII_BMCR);
- bmcr |= BMCR_ANRESTART;
- tse_pcs_write(tse_pcs, MII_BMCR, bmcr);
-
- /* This PCS seems to require a soft reset to re-sync the AN logic */
- tse_pcs_reset(tse_pcs);
-}
-
-static const struct phylink_pcs_ops alt_tse_pcs_ops = {
- .pcs_validate = alt_tse_pcs_validate,
- .pcs_get_state = alt_tse_pcs_get_state,
- .pcs_config = alt_tse_pcs_config,
- .pcs_an_restart = alt_tse_pcs_an_restart,
-};
-
-struct phylink_pcs *alt_tse_pcs_create(struct net_device *ndev,
- void __iomem *pcs_base, int reg_width)
-{
- struct altera_tse_pcs *tse_pcs;
-
- if (reg_width != 4 && reg_width != 2)
- return ERR_PTR(-EINVAL);
-
- tse_pcs = devm_kzalloc(&ndev->dev, sizeof(*tse_pcs), GFP_KERNEL);
- if (!tse_pcs)
- return ERR_PTR(-ENOMEM);
-
- tse_pcs->pcs.ops = &alt_tse_pcs_ops;
- tse_pcs->base = pcs_base;
- tse_pcs->reg_width = reg_width;
-
- return &tse_pcs->pcs;
-}
-EXPORT_SYMBOL_GPL(alt_tse_pcs_create);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Altera TSE PCS driver");
-MODULE_AUTHOR("Maxime Chevallier <[email protected]>");
diff --git a/include/linux/pcs-altera-tse.h b/include/linux/pcs-altera-tse.h
deleted file mode 100644
index 92ab9f08e835..000000000000
--- a/include/linux/pcs-altera-tse.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (C) 2022 Bootlin
- *
- * Maxime Chevallier <[email protected]>
- */
-
-#ifndef __LINUX_PCS_ALTERA_TSE_H
-#define __LINUX_PCS_ALTERA_TSE_H
-
-struct phylink_pcs;
-struct net_device;
-
-struct phylink_pcs *alt_tse_pcs_create(struct net_device *ndev,
- void __iomem *pcs_base, int reg_width);
-
-#endif /* __LINUX_PCS_ALTERA_TSE_H */
--
2.39.2

2023-03-24 09:40:17

by Maxime Chevallier

[permalink] [raw]
Subject: [RFC 6/7] net: ethernet: altera-tse: Convert to mdio-regmap and use PCS Lynx

The newly introduced regmap-based MDIO driver allows for an easy mapping
of an mdiodevice onto the memory-mapped TSE PCS, which is actually a
Lynx PCS.

Convert Altera TSE to use this PCS instead of the pcs-altera-tse, which
is nothing more than a memory-mapped Lynx PCS.

Signed-off-by: Maxime Chevallier <[email protected]>
---
drivers/net/ethernet/altera/altera_tse.h | 1 +
drivers/net/ethernet/altera/altera_tse_main.c | 54 ++++++++++++++++---
2 files changed, 49 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h
index db5eed06e92d..d50cf440d01b 100644
--- a/drivers/net/ethernet/altera/altera_tse.h
+++ b/drivers/net/ethernet/altera/altera_tse.h
@@ -477,6 +477,7 @@ struct altera_tse_private {
struct phylink *phylink;
struct phylink_config phylink_config;
struct phylink_pcs *pcs;
+ struct mdio_device *pcs_mdiodev;
};

/* Function prototypes
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index 66e3af73ec41..c5f4b5e24376 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -27,14 +27,16 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mii.h>
+#include <linux/mdio/mdio-regmap.h>
#include <linux/netdevice.h>
#include <linux/of_device.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/of_platform.h>
-#include <linux/pcs-altera-tse.h>
+#include <linux/pcs-lynx.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/skbuff.h>
#include <asm/cacheflush.h>

@@ -1139,13 +1141,21 @@ static int altera_tse_probe(struct platform_device *pdev)
const struct of_device_id *of_id = NULL;
struct altera_tse_private *priv;
struct resource *control_port;
+ struct regmap *pcs_regmap;
struct resource *dma_res;
struct resource *pcs_res;
+ struct mii_bus *pcs_bus;
struct net_device *ndev;
void __iomem *descmap;
- int pcs_reg_width = 2;
int ret = -ENODEV;

+ struct regmap_config pcs_regmap_cfg;
+
+ struct mdio_regmap_config mrc = {
+ .parent = &pdev->dev,
+ .valid_addr = 0x1,
+ };
+
ndev = alloc_etherdev(sizeof(struct altera_tse_private));
if (!ndev) {
dev_err(&pdev->dev, "Could not allocate network device\n");
@@ -1263,9 +1273,30 @@ static int altera_tse_probe(struct platform_device *pdev)
ret = request_and_map(pdev, "pcs", &pcs_res,
&priv->pcs_base);
if (ret) {
+ /* If we can't find a dedicated resource for the PCS, fallback
+ * to the internal PCS, that has a different address stride
+ */
priv->pcs_base = priv->mac_dev + tse_csroffs(mdio_phy0);
- pcs_reg_width = 4;
+ pcs_regmap_cfg.reg_bits = 32;
+ /* Values are MDIO-like values, on 16 bits */
+ pcs_regmap_cfg.val_bits = 16;
+ pcs_regmap_cfg.reg_stride = 4;
+ pcs_regmap_cfg.reg_shift = REGMAP_UPSHIFT(2);
+ } else {
+ pcs_regmap_cfg.reg_bits = 16;
+ pcs_regmap_cfg.val_bits = 16;
+ pcs_regmap_cfg.reg_stride = 2;
+ pcs_regmap_cfg.reg_shift = REGMAP_UPSHIFT(1);
+ }
+
+ /* Create a regmap for the PCS so that it can be used by the PCS driver */
+ pcs_regmap = devm_regmap_init_mmio(&pdev->dev, priv->pcs_base,
+ &pcs_regmap_cfg);
+ if (IS_ERR(pcs_regmap)) {
+ ret = PTR_ERR(pcs_regmap);
+ goto err_free_netdev;
}
+ mrc.regmap = pcs_regmap;

/* Rx IRQ */
priv->rx_irq = platform_get_irq_byname(pdev, "rx_irq");
@@ -1389,7 +1420,15 @@ static int altera_tse_probe(struct platform_device *pdev)
(unsigned long) control_port->start, priv->rx_irq,
priv->tx_irq);

- priv->pcs = alt_tse_pcs_create(ndev, priv->pcs_base, pcs_reg_width);
+ snprintf(mrc.name, MII_BUS_ID_SIZE, "%s-pcs-mii", ndev->name);
+ pcs_bus = devm_mdio_regmap_register(&pdev->dev, &mrc);
+ priv->pcs_mdiodev = mdio_device_create(pcs_bus, 0);
+
+ priv->pcs = lynx_pcs_create(priv->pcs_mdiodev);
+ if (!priv->pcs) {
+ ret = -ENODEV;
+ goto err_init_phy;
+ }

priv->phylink_config.dev = &ndev->dev;
priv->phylink_config.type = PHYLINK_NETDEV;
@@ -1412,11 +1451,12 @@ static int altera_tse_probe(struct platform_device *pdev)
if (IS_ERR(priv->phylink)) {
dev_err(&pdev->dev, "failed to create phylink\n");
ret = PTR_ERR(priv->phylink);
- goto err_init_phy;
+ goto err_pcs;
}

return 0;
-
+err_pcs:
+ mdio_device_free(priv->pcs_mdiodev);
err_init_phy:
unregister_netdev(ndev);
err_register_netdev:
@@ -1438,6 +1478,8 @@ static int altera_tse_remove(struct platform_device *pdev)
altera_tse_mdio_destroy(ndev);
unregister_netdev(ndev);
phylink_destroy(priv->phylink);
+ mdio_device_free(priv->pcs_mdiodev);
+
free_netdev(ndev);

return 0;
--
2.39.2

2023-03-24 12:18:45

by Andrew Lunn

[permalink] [raw]
Subject: Re: [RFC 3/7] regmap: allow upshifting register addresses before performing operations

> index da8996e7a1f1..2ef53936652b 100644
> --- a/drivers/base/regmap/internal.h
> +++ b/drivers/base/regmap/internal.h
> @@ -31,7 +31,7 @@ struct regmap_format {
> size_t buf_size;
> size_t reg_bytes;
> size_t pad_bytes;
> - size_t reg_downshift;
> + int reg_shift;

Maybe ssize_t to keep with the pattern of using size_t. However,
ssize_t is somewhat over sized for a bit shift. So maybe just s8?

> size_t val_bytes;
> void (*format_write)(struct regmap *map,
> unsigned int reg, unsigned int val);
> diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
> index 726f59612fd6..c4cde4f45b05 100644
> --- a/drivers/base/regmap/regmap.c
> +++ b/drivers/base/regmap/regmap.c
> @@ -814,7 +814,7 @@ struct regmap *__regmap_init(struct device *dev,
>
> map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
> map->format.pad_bytes = config->pad_bits / 8;
> - map->format.reg_downshift = config->reg_downshift;
> + map->format.reg_shift = config->reg_shift;
> map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
> map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
> config->val_bits + config->pad_bits, 8);
> @@ -1679,7 +1679,13 @@ static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes,
> static unsigned int regmap_reg_addr(struct regmap *map, unsigned int reg)
> {
> reg += map->reg_base;
> - return reg >> map->format.reg_downshift;
> +
> + if (map->format.reg_shift > 0)
> + reg >>= map->format.reg_shift;
> + else if (map->format.reg_shift < 0)
> + reg <<= -(map->format.reg_shift);

I was wondering about negative shifts. It is apparently undefined
behaviour. So this construct is required.

Andrew

2023-03-24 12:18:56

by Andrew Lunn

[permalink] [raw]
Subject: Re: [RFC 4/7] mfd: ocelot-spi: Change the regmap stride to reflect the real one

On Fri, Mar 24, 2023 at 10:36:41AM +0100, Maxime Chevallier wrote:
> When used over SPI, the register addresses needs to be translated,
> compared to when used over MMIO. The translation consists in applying an
> offset with reg_base, then downshifting the registers by 2. This
> actually changes the register stride from 4 to 1.
>
> Signed-off-by: Maxime Chevallier <[email protected]>
> ---
> drivers/mfd/ocelot-spi.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/mfd/ocelot-spi.c b/drivers/mfd/ocelot-spi.c
> index 2d1349a10ca9..107cda0544aa 100644
> --- a/drivers/mfd/ocelot-spi.c
> +++ b/drivers/mfd/ocelot-spi.c
> @@ -124,7 +124,7 @@ static int ocelot_spi_initialize(struct device *dev)
>
> static const struct regmap_config ocelot_spi_regmap_config = {
> .reg_bits = 24,
> - .reg_stride = 4,
> + .reg_stride = 1,
> .reg_shift = REGMAP_DOWNSHIFT(2),
> .val_bits = 32,

This does not look like a bisectable change? Or did it never work
before?

Andrew

2023-03-24 13:11:02

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [RFC 4/7] mfd: ocelot-spi: Change the regmap stride to reflect the real one

Hello Andrew,

On Fri, 24 Mar 2023 13:11:07 +0100
Andrew Lunn <[email protected]> wrote:

> On Fri, Mar 24, 2023 at 10:36:41AM +0100, Maxime Chevallier wrote:
> > When used over SPI, the register addresses needs to be translated,
> > compared to when used over MMIO. The translation consists in
> > applying an offset with reg_base, then downshifting the registers
> > by 2. This actually changes the register stride from 4 to 1.
> >
> > Signed-off-by: Maxime Chevallier <[email protected]>
> > ---
> > drivers/mfd/ocelot-spi.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/mfd/ocelot-spi.c b/drivers/mfd/ocelot-spi.c
> > index 2d1349a10ca9..107cda0544aa 100644
> > --- a/drivers/mfd/ocelot-spi.c
> > +++ b/drivers/mfd/ocelot-spi.c
> > @@ -124,7 +124,7 @@ static int ocelot_spi_initialize(struct device
> > *dev)
> > static const struct regmap_config ocelot_spi_regmap_config = {
> > .reg_bits = 24,
> > - .reg_stride = 4,
> > + .reg_stride = 1,
> > .reg_shift = REGMAP_DOWNSHIFT(2),
> > .val_bits = 32,
>
> This does not look like a bisectable change? Or did it never work
> before?

Actually this works in all cases because of "regmap: check for alignment
on translated register addresses" in this series. Before this series,
I think using a stride of 1 would have worked too, as any 4-byte-aligned
accesses are also 1-byte aligned.

But that's also why I need review on this, my understanding is that
reg_stride is used just as a check for alignment, and I couldn't test
this ocelot-related patch on the real HW, so please take it with a
grain of salt :(

Thanks,

Maxime

> Andrew

2023-03-24 15:49:27

by Colin Foster

[permalink] [raw]
Subject: Re: [RFC 4/7] mfd: ocelot-spi: Change the regmap stride to reflect the real one

Hi Maxime,

On Fri, Mar 24, 2023 at 01:48:17PM +0100, Maxime Chevallier wrote:
> Hello Andrew,
>
> On Fri, 24 Mar 2023 13:11:07 +0100
> Andrew Lunn <[email protected]> wrote:
>
> > On Fri, Mar 24, 2023 at 10:36:41AM +0100, Maxime Chevallier wrote:
> > > When used over SPI, the register addresses needs to be translated,
> > > compared to when used over MMIO. The translation consists in
> > > applying an offset with reg_base, then downshifting the registers
> > > by 2. This actually changes the register stride from 4 to 1.
> > >
> > > Signed-off-by: Maxime Chevallier <[email protected]>
> > > ---
> > > drivers/mfd/ocelot-spi.c | 2 +-
> > > 1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/mfd/ocelot-spi.c b/drivers/mfd/ocelot-spi.c
> > > index 2d1349a10ca9..107cda0544aa 100644
> > > --- a/drivers/mfd/ocelot-spi.c
> > > +++ b/drivers/mfd/ocelot-spi.c
> > > @@ -124,7 +124,7 @@ static int ocelot_spi_initialize(struct device
> > > *dev)
> > > static const struct regmap_config ocelot_spi_regmap_config = {
> > > .reg_bits = 24,
> > > - .reg_stride = 4,
> > > + .reg_stride = 1,
> > > .reg_shift = REGMAP_DOWNSHIFT(2),
> > > .val_bits = 32,
> >
> > This does not look like a bisectable change? Or did it never work
> > before?
>
> Actually this works in all cases because of "regmap: check for alignment
> on translated register addresses" in this series. Before this series,
> I think using a stride of 1 would have worked too, as any 4-byte-aligned
> accesses are also 1-byte aligned.
>
> But that's also why I need review on this, my understanding is that
> reg_stride is used just as a check for alignment, and I couldn't test
> this ocelot-related patch on the real HW, so please take it with a
> grain of salt :(

You're exactly right. reg_stride wasn't used anywhere in the
ocelot-spi path before this patch series. When I build against patch 3
("regmap: allow upshifting register addresses before performing
operations") ocelot-spi breaks.

[ 3.207711] ocelot-soc spi0.0: error -EINVAL: Error initializing SPI bus

When I build against the whole series, or even just up to patch 4 ("mfd:
ocelot-spi: Change the regmap stride to reflect the real one")
functionality returns.

If you keep patch 4 and apply it before patch 2, everything should
work.

Sorry for the bug. Thanks for the fix. And I'm glad I'm not the only one
taking advantage of the "reg_shift" regmap operation! I thought I'd be
the only one.


Let me know if you want me to take any action on this fix.


Colin

2023-03-24 18:00:29

by Colin Foster

[permalink] [raw]
Subject: Re: [RFC 4/7] mfd: ocelot-spi: Change the regmap stride to reflect the real one

On Fri, Mar 24, 2023 at 08:48:18AM -0700, Colin Foster wrote:
> Hi Maxime,
>
> On Fri, Mar 24, 2023 at 01:48:17PM +0100, Maxime Chevallier wrote:
> > Hello Andrew,
> >
> > On Fri, 24 Mar 2023 13:11:07 +0100
> > Andrew Lunn <[email protected]> wrote:
> >
> > > > .reg_bits = 24,
> > > > - .reg_stride = 4,
> > > > + .reg_stride = 1,
> > > > .reg_shift = REGMAP_DOWNSHIFT(2),
> > > > .val_bits = 32,
> > >
> > > This does not look like a bisectable change? Or did it never work
> > > before?
> >
> > Actually this works in all cases because of "regmap: check for alignment
> > on translated register addresses" in this series. Before this series,
> > I think using a stride of 1 would have worked too, as any 4-byte-aligned
> > accesses are also 1-byte aligned.
> >
> > But that's also why I need review on this, my understanding is that
> > reg_stride is used just as a check for alignment, and I couldn't test
> > this ocelot-related patch on the real HW, so please take it with a
> > grain of salt :(
>
> You're exactly right. reg_stride wasn't used anywhere in the
> ocelot-spi path before this patch series. When I build against patch 3
> ("regmap: allow upshifting register addresses before performing
> operations") ocelot-spi breaks.
>
> [ 3.207711] ocelot-soc spi0.0: error -EINVAL: Error initializing SPI bus
>
> When I build against the whole series, or even just up to patch 4 ("mfd:
> ocelot-spi: Change the regmap stride to reflect the real one")
> functionality returns.
>
> If you keep patch 4 and apply it before patch 2, everything should
> work.

I replied too soon, before looking more into patch 2.

Some context from that patch:

--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -2016,7 +2016,7 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;

- if (!IS_ALIGNED(reg, map->reg_stride))
+ if (!IS_ALIGNED(regmap_reg_addr(map, reg), map->reg_stride))
return -EINVAL;

map->lock(map->lock_arg);


I don't know whether checking IS_ALIGNED before or after the shift is
the right thing to do. My initial intention was to perform the shift at
the last possible moment before calling into the read / write routines.
That way it wouldn't interfere with any underlying regcache mechanisms
(which aren't used by ocelot-spi)

But to me it seems like patch 2 changes this expected behavior, so the
two patches should be squashed.


... Thinking more about it ...


In ocelot-spi, at the driver layer, we're accessing two registers.
They'd be at address 0x71070000 and 0x71070004. The driver uses those
addresses, so there's a stride of 4. I can't access 0x71070001.

The fact that the translation from "address" to "bits that go out the
SPI bus" shifts out the last two bits and hacks off a couple of the MSBs
doesn't seem like it should affect the 'reg_stride'.


So maybe patches 2 and 4 should be dropped, and your patch 6
alterra_tse_main should use a reg_stride of 1? That has a subtle benefit
of not needing an additional operation or two from regmap_reg_addr().

Would that cause any issues? Hopefully there isn't something I'm
missing.


(Aside: I'm now curious how the compiler will optimize
regmap_reg_addr())


Colin

2023-03-24 18:54:52

by Mark Brown

[permalink] [raw]
Subject: Re: [RFC 2/7] regmap: check for alignment on translated register addresses

On Fri, Mar 24, 2023 at 10:36:39AM +0100, Maxime Chevallier wrote:
> With regmap->reg_base and regmap->reg_downshift, the actual register
> address that is going to be used for the next operation might not be the
> same as the one we have as an input. Addresses can be offset with
> reg_base and shifted, which will affect alignment.
>
> Check for alignment on the real register address.

It is not at all clear to me that the combination of stride and
downshift particularly makes sense, and especially not that the
stride should be applied after downshifting rather than to what
the user is passing in.


Attachments:
(No filename) (618.00 B)
signature.asc (499.00 B)
Download all attachments

2023-03-24 20:39:08

by Mark Brown

[permalink] [raw]
Subject: Re: (subset) [RFC 0/7] Introduce a generic regmap-based MDIO driver

On Fri, 24 Mar 2023 10:36:37 +0100, Maxime Chevallier wrote:
> When the Altera TSE PCS driver was initially introduced, there were
> comments by Russell that the register layout looked very familiar to the
> existing Lynx PCS driver, the only difference being that the TSE PCS
> driver is memory-mapped whereas the Lynx PCS driver sits on an MDIO bus.
>
> Since then, I've sent a followup to create a wrapper around Lynx, that
> would create a virtual MDIO bus driver that would translate the mdio
> operations to mmio operations [1].
>
> [...]

Applied to

broonie/regmap.git for-next

Thanks!

[1/7] regmap: add a helper to translate the register address
commit: 3f58f6dc4d92ed6fae4a4da0d5b091e00ec10fa8

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

2023-03-27 00:23:13

by Andrew Lunn

[permalink] [raw]
Subject: Re: [RFC 4/7] mfd: ocelot-spi: Change the regmap stride to reflect the real one

> > > static const struct regmap_config ocelot_spi_regmap_config = {
> > > .reg_bits = 24,
> > > - .reg_stride = 4,
> > > + .reg_stride = 1,
> > > .reg_shift = REGMAP_DOWNSHIFT(2),
> > > .val_bits = 32,
> >
> > This does not look like a bisectable change? Or did it never work
> > before?
>
> Actually this works in all cases because of "regmap: check for alignment
> on translated register addresses" in this series. Before this series,
> I think using a stride of 1 would have worked too, as any 4-byte-aligned
> accesses are also 1-byte aligned.

This is the sort of think which is good to explain in the commit
message. That is the place to answer questions reviewers are likely to
ask for things which are not obvious from just the patch.

Andrew

2023-03-30 09:48:31

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [RFC 2/7] regmap: check for alignment on translated register addresses

Hello Mark,

On Fri, 24 Mar 2023 18:51:19 +0000
Mark Brown <[email protected]> wrote:

> On Fri, Mar 24, 2023 at 10:36:39AM +0100, Maxime Chevallier wrote:
> > With regmap->reg_base and regmap->reg_downshift, the actual register
> > address that is going to be used for the next operation might not
> > be the same as the one we have as an input. Addresses can be offset
> > with reg_base and shifted, which will affect alignment.
> >
> > Check for alignment on the real register address.
>
> It is not at all clear to me that the combination of stride and
> downshift particularly makes sense, and especially not that the
> stride should be applied after downshifting rather than to what
> the user is passing in.

I agree on the part where the ordering of "adding and offset, then
down/upshifting" isn't natural. This is the order in which operations
are done today, and from what I could gather, only the ocelot-spi MFD
driver uses both of these operations.

It would indeed make sense to first shift the register to have the
proper spacing between register addresses, then adding the offset.

So maybe we should address that in ocelot-spi in the next iteration,
Colin, would you agree ?

Thanks,

Maxime

2023-03-30 09:49:19

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [RFC 4/7] mfd: ocelot-spi: Change the regmap stride to reflect the real one

Hello Andrew,

On Mon, 27 Mar 2023 02:02:55 +0200
Andrew Lunn <[email protected]> wrote:

> > > > static const struct regmap_config ocelot_spi_regmap_config = {
> > > > .reg_bits = 24,
> > > > - .reg_stride = 4,
> > > > + .reg_stride = 1,
> > > > .reg_shift = REGMAP_DOWNSHIFT(2),
> > > > .val_bits = 32,
> > >
> > > This does not look like a bisectable change? Or did it never work
> > > before?
> >
> > Actually this works in all cases because of "regmap: check for
> > alignment on translated register addresses" in this series. Before
> > this series, I think using a stride of 1 would have worked too, as
> > any 4-byte-aligned accesses are also 1-byte aligned.
>
> This is the sort of think which is good to explain in the commit
> message. That is the place to answer questions reviewers are likely to
> ask for things which are not obvious from just the patch.

That's right, I will include this explanation in the next iteration.
Thanks for the review,

Maxime

> Andrew

2023-03-30 09:58:20

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [RFC 4/7] mfd: ocelot-spi: Change the regmap stride to reflect the real one

On Fri, 24 Mar 2023 10:56:05 -0700
Colin Foster <[email protected]> wrote:

> On Fri, Mar 24, 2023 at 08:48:18AM -0700, Colin Foster wrote:
> > Hi Maxime,
> >
> > On Fri, Mar 24, 2023 at 01:48:17PM +0100, Maxime Chevallier wrote:
> > > Hello Andrew,
> > >
> > > On Fri, 24 Mar 2023 13:11:07 +0100
> > > Andrew Lunn <[email protected]> wrote:
> > >
> > > > > .reg_bits = 24,
> > > > > - .reg_stride = 4,
> > > > > + .reg_stride = 1,
> > > > > .reg_shift = REGMAP_DOWNSHIFT(2),
> > > > > .val_bits = 32,
> > > >
> > > > This does not look like a bisectable change? Or did it never
> > > > work before?
> > >
> > > Actually this works in all cases because of "regmap: check for
> > > alignment on translated register addresses" in this series.
> > > Before this series, I think using a stride of 1 would have worked
> > > too, as any 4-byte-aligned accesses are also 1-byte aligned.
> > >
> > > But that's also why I need review on this, my understanding is
> > > that reg_stride is used just as a check for alignment, and I
> > > couldn't test this ocelot-related patch on the real HW, so please
> > > take it with a grain of salt :(
> >
> > You're exactly right. reg_stride wasn't used anywhere in the
> > ocelot-spi path before this patch series. When I build against
> > patch 3 ("regmap: allow upshifting register addresses before
> > performing operations") ocelot-spi breaks.
> >
> > [ 3.207711] ocelot-soc spi0.0: error -EINVAL: Error initializing
> > SPI bus
> >
> > When I build against the whole series, or even just up to patch 4
> > ("mfd: ocelot-spi: Change the regmap stride to reflect the real
> > one") functionality returns.
> >
> > If you keep patch 4 and apply it before patch 2, everything should
> > work.
>
> I replied too soon, before looking more into patch 2.
>
> Some context from that patch:
>
> --- a/drivers/base/regmap/regmap.c
> +++ b/drivers/base/regmap/regmap.c
> @@ -2016,7 +2016,7 @@ int regmap_write(struct regmap *map, unsigned
> int reg, unsigned int val) {
> int ret;
>
> - if (!IS_ALIGNED(reg, map->reg_stride))
> + if (!IS_ALIGNED(regmap_reg_addr(map, reg), map->reg_stride))
> return -EINVAL;
>
> map->lock(map->lock_arg);
>
>
> I don't know whether checking IS_ALIGNED before or after the shift is
> the right thing to do. My initial intention was to perform the shift
> at the last possible moment before calling into the read / write
> routines. That way it wouldn't interfere with any underlying regcache
> mechanisms (which aren't used by ocelot-spi)
>
> But to me it seems like patch 2 changes this expected behavior, so the
> two patches should be squashed.
>
>
> ... Thinking more about it ...
>
>
> In ocelot-spi, at the driver layer, we're accessing two registers.
> They'd be at address 0x71070000 and 0x71070004. The driver uses those
> addresses, so there's a stride of 4. I can't access 0x71070001.
>
> The fact that the translation from "address" to "bits that go out the
> SPI bus" shifts out the last two bits and hacks off a couple of the
> MSBs doesn't seem like it should affect the 'reg_stride'.
>
>
> So maybe patches 2 and 4 should be dropped, and your patch 6
> alterra_tse_main should use a reg_stride of 1? That has a subtle
> benefit of not needing an additional operation or two from
> regmap_reg_addr().
>
> Would that cause any issues? Hopefully there isn't something I'm
> missing.

Well here I guess it's also about the semantic of reg_stride. Should it
represent the alignment constraints of the register address we feed as
an input to a regmap_read/regmap_write operation, or the alignment
constraints of the underlying bus ? This is kind of a new concern, as
we are now translating register addresses.

I asked myself the same question, so I'm very open for discussion, but
my gut feeling is that the reg_stride is there to make sure we don't
perform an access whose alignment won't work with the bus we are using,
so using a stride of 1 on a memory-mapped device with 2 or 4 byte
register alignment is a bit counter-intuitive.

Thanks a lot for the review, suggestions and tests !

Best regards,

Maxime

>
> (Aside: I'm now curious how the compiler will optimize
> regmap_reg_addr())
>
>
> Colin

2023-03-30 14:15:06

by Mark Brown

[permalink] [raw]
Subject: Re: [RFC 2/7] regmap: check for alignment on translated register addresses

On Thu, Mar 30, 2023 at 11:45:46AM +0200, Maxime Chevallier wrote:
> Mark Brown <[email protected]> wrote:

> > It is not at all clear to me that the combination of stride and
> > downshift particularly makes sense, and especially not that the
> > stride should be applied after downshifting rather than to what
> > the user is passing in.

> I agree on the part where the ordering of "adding and offset, then
> down/upshifting" isn't natural. This is the order in which operations
> are done today, and from what I could gather, only the ocelot-spi MFD
> driver uses both of these operations.

Right. I don't think the ordering is particularly intentional,
like I say it's not clear that combinding the two makes sense so
I think it just wasn't considered at all.


Attachments:
(No filename) (783.00 B)
signature.asc (499.00 B)
Download all attachments

2023-03-30 16:40:35

by Colin Foster

[permalink] [raw]
Subject: Re: [RFC 2/7] regmap: check for alignment on translated register addresses

Hi Maxime,

On Thu, Mar 30, 2023 at 11:45:46AM +0200, Maxime Chevallier wrote:
> Hello Mark,
>
> On Fri, 24 Mar 2023 18:51:19 +0000
> Mark Brown <[email protected]> wrote:
>
> > On Fri, Mar 24, 2023 at 10:36:39AM +0100, Maxime Chevallier wrote:
> > > With regmap->reg_base and regmap->reg_downshift, the actual register
> > > address that is going to be used for the next operation might not
> > > be the same as the one we have as an input. Addresses can be offset
> > > with reg_base and shifted, which will affect alignment.
> > >
> > > Check for alignment on the real register address.
> >
> > It is not at all clear to me that the combination of stride and
> > downshift particularly makes sense, and especially not that the
> > stride should be applied after downshifting rather than to what
> > the user is passing in.
>
> I agree on the part where the ordering of "adding and offset, then
> down/upshifting" isn't natural. This is the order in which operations
> are done today, and from what I could gather, only the ocelot-spi MFD
> driver uses both of these operations.
>
> It would indeed make sense to first shift the register to have the
> proper spacing between register addresses, then adding the offset.
>
> So maybe we should address that in ocelot-spi in the next iteration,
> Colin, would you agree ?

I'm curious what you mean by "proper spacing". The proper spacing of the
VSC7512 (ocelot-spi) is 4, and that's what the reg_stride is. All
registers can be accessed in many ways, as I described.

Consider the case I brought up, where we're trying to access a register
at 0x71070004. If you're on the processor internal to ocelot, this would
be

uint32 val = *(uint32 *)0x71070004;

Accesses to 0x71070001, 0x71070002, and 0x71070003 are not possible.
(Well maybe they are _possible_ on some architectures... you get the
point though)


Translate this to accessing the same register via SPI, where everything
is shifted down two bits. We're still accessing 0x71070004, but the way
this gets sent on the bus is 0x71070004 >> 2 = 0x1c41c001.

This patch set would have allowed accesses to 0x71070001 in the
ocelot-spi scenario, but not in the MMIO scenario. That wouldn't be
desired.


The result of all this is a consistent driver interface. "Give me
register 0x4 from a base address of 0x71070000" works in both MMIO and
SPI. Everything in /sys/kernel/debug/regmap should be the same in both
scenarios as well. All of these scenarios were the motivation behind
reg_shift.


Colin Foster

2023-04-01 14:06:19

by Vladimir Oltean

[permalink] [raw]
Subject: Re: [RFC 5/7] net: mdio: Introduce a regmap-based mdio driver

Hi Maxime,

On Fri, Mar 24, 2023 at 10:36:42AM +0100, Maxime Chevallier wrote:
> There exists several examples today of devices that embed an ethernet
> PHY or PCS directly inside an SoC. In this situation, either the device
> is controlled through a vendor-specific register set, or sometimes
> exposes the standard 802.3 registers that are typically accessed over
> MDIO.
>
> As phylib and phylink are designed to use mdiodevices, this driver
> allows creating a virtual MDIO bus, that translates mdiodev register
> accesses to regmap accesses.
>
> The reason we use regmap is because there are at least 3 such devices
> known today, 2 of them are Altera TSE PCS's, memory-mapped, exposed
> with a 4-byte stride in stmmac's dwmac-socfpga variant, and a 2-byte
> stride in altera-tse. The other one (nxp,sja1110-base-tx-mdio) is
> exposed over SPI.
>
> Signed-off-by: Maxime Chevallier <[email protected]>
> ---
> MAINTAINERS | 7 +++
> drivers/net/mdio/Kconfig | 11 +++++
> drivers/net/mdio/Makefile | 1 +
> drivers/net/mdio/mdio-regmap.c | 85 ++++++++++++++++++++++++++++++++
> include/linux/mdio/mdio-regmap.h | 25 ++++++++++
> 5 files changed, 129 insertions(+)
> create mode 100644 drivers/net/mdio/mdio-regmap.c
> create mode 100644 include/linux/mdio/mdio-regmap.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8d5bc223f305..10b3a1800e0d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12751,6 +12751,13 @@ F: Documentation/devicetree/bindings/net/ieee802154/mcr20a.txt
> F: drivers/net/ieee802154/mcr20a.c
> F: drivers/net/ieee802154/mcr20a.h
>
> +MDIO REGMAP DRIVER
> +M: Maxime Chevallier <[email protected]>
> +L: [email protected]
> +S: Maintained
> +F: drivers/net/mdio/mdio-regmap.c
> +F: include/linux/mdio/mdio-regmap.h
> +
> MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
> M: William Breathitt Gray <[email protected]>
> L: [email protected]
> diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig
> index 90309980686e..671e4bb82e3e 100644
> --- a/drivers/net/mdio/Kconfig
> +++ b/drivers/net/mdio/Kconfig
> @@ -182,6 +182,17 @@ config MDIO_IPQ8064
> This driver supports the MDIO interface found in the network
> interface units of the IPQ8064 SoC
>
> +config MDIO_REGMAP
> + tristate "Regmap-based virtual MDIO bus driver"

IMO this shouldn't have the text visible to the Kconfig user (just
"tristate"); it should only be selectable by the driver which makes use
of the exported symbol.

AFAIK, Kconfig options which are selectable should not depend on
anything. The dependency should transfer to the options which selects
it (here REGMAP). Optionally there can be a comment specifying that the

> + depends on REGMAP
> + help
> + This driver allows using MDIO devices that are not sitting on a
> + regular MDIO bus, but still exposes the standard 802.3 register
> + layout. It's regmap-based so that it can be used on integrated,
> + memory-mapped PHYs, SPI PHYs and so on. A new virtual MDIO bus is
> + created, and its read/write operations are mapped to the underlying
> + regmap.
> +
> config MDIO_THUNDER
> tristate "ThunderX SOCs MDIO buses"
> depends on 64BIT
> diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile
> index 7d4cb4c11e4e..1015f0db4531 100644
> --- a/drivers/net/mdio/Makefile
> +++ b/drivers/net/mdio/Makefile
> @@ -19,6 +19,7 @@ obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
> 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_REGMAP) += mdio-regmap.o
> obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
> obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o
> obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o
> diff --git a/drivers/net/mdio/mdio-regmap.c b/drivers/net/mdio/mdio-regmap.c
> new file mode 100644
> index 000000000000..c85d62c2f55c
> --- /dev/null
> +++ b/drivers/net/mdio/mdio-regmap.c
> @@ -0,0 +1,85 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/* Driver for MMIO-Mapped MDIO devices. Some IPs expose internal PHYs or PCS
> + * within the MMIO-mapped area
> + *
> + * Copyright (C) 2023 Maxime Chevallier <[email protected]>
> + */
> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/mdio.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_mdio.h>
> +#include <linux/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/mdio/mdio-regmap.h>
> +
> +#define DRV_NAME "mdio-regmap"
> +
> +static int mdio_regmap_read_c22(struct mii_bus *bus, int addr, int regnum)
> +{
> + struct mdio_regmap_config *ctx = bus->priv;
> + unsigned int val;
> + int ret;
> +
> + if (!(ctx->valid_addr & BIT(addr)))
> + return -ENODEV;
> +
> + ret = regmap_read(ctx->regmap, regnum, &val);
> + if (ret < 0)
> + return ret;
> +
> + return val;
> +}
> +
> +static int mdio_regmap_write_c22(struct mii_bus *bus, int addr, int regnum,
> + u16 val)
> +{
> + struct mdio_regmap_config *ctx = bus->priv;
> +
> + if (!(ctx->valid_addr & BIT(addr)))
> + return -ENODEV;
> +
> + return regmap_write(ctx->regmap, regnum, val);
> +}
> +
> +struct mii_bus *devm_mdio_regmap_register(struct device *dev,
> + const struct mdio_regmap_config *config)
> +{
> + struct mdio_regmap_config *mrc;
> + struct mii_bus *mii;
> + int rc;
> +
> + if (!config->parent)
> + return ERR_PTR(-EINVAL);
> +
> + if (!config->valid_addr)
> + return ERR_PTR(-EINVAL);
> +
> + mii = devm_mdiobus_alloc_size(config->parent, sizeof(*mrc));
> + if (!mii)
> + return ERR_PTR(-ENOMEM);
> +
> + mrc = mii->priv;
> + memcpy(mrc, config, sizeof(*mrc));
> +
> + mrc->regmap = config->regmap;
> + mrc->parent = config->parent;

mrc->parent doesn't seem to be used

> + mrc->valid_addr = config->valid_addr;
> +
> + mii->name = DRV_NAME;
> + strncpy(mii->id, config->name, MII_BUS_ID_SIZE);
> + mii->parent = config->parent;
> + mii->read = mdio_regmap_read_c22;
> + mii->write = mdio_regmap_write_c22;
> +
> + rc = devm_mdiobus_register(dev, mii);
> + if (rc) {
> + dev_err(config->parent, "Cannot register MDIO bus![%s] (%d)\n", mii->id, rc);
> + return ERR_PTR(rc);
> + }
> +
> + return mii;
> +}
> +
> diff --git a/include/linux/mdio/mdio-regmap.h b/include/linux/mdio/mdio-regmap.h
> new file mode 100644
> index 000000000000..ea428e5a2913
> --- /dev/null
> +++ b/include/linux/mdio/mdio-regmap.h
> @@ -0,0 +1,25 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Driver for MMIO-Mapped MDIO devices. Some IPs expose internal PHYs or PCS
> + * within the MMIO-mapped area
> + *
> + * Copyright (C) 2023 Maxime Chevallier <[email protected]>
> + */
> +#ifndef MDIO_REGMAP_H
> +#define MDIO_REGMAP_H
> +
> +#define MDIO_REGMAP_NAME 63

hmm.. NAME_LEN would be less confusing for a name. Although... why not
MII_BUS_ID_SIZE directly, seeing that this is how it is actually used?

> +
> +struct device;
> +struct regmap;
> +
> +struct mdio_regmap_config {
> + struct device *parent;
> + struct regmap *regmap;
> + char name[MDIO_REGMAP_NAME];
> + u32 valid_addr;

Would it be better to name this valid_addr_mask, since that is how it is
used? Or is the "ctx->valid_addr & BIT(addr)" check wrong (should have
been "ctx->valid_addr == addr")? How is the driver supposed to work with
multiple valid addresses?

> +};
> +
> +struct mii_bus *devm_mdio_regmap_register(struct device *dev,
> + const struct mdio_regmap_config *config);
> +
> +#endif
> --
> 2.39.2
>

2023-05-15 12:10:26

by Vladimir Oltean

[permalink] [raw]
Subject: Re: [RFC 7/7] net: pcs: Drop the TSE PCS driver

Hi Maxime,

On Fri, Mar 24, 2023 at 10:36:44AM +0100, Maxime Chevallier wrote:
> Now that we can easily create a mdio-device that represents a
> memory-mapped device that exposes an MDIO-like register layout, we don't
> need the Altera TSE PCS anymore, since we can use the Lynx PCS instead.
>
> Signed-off-by: Maxime Chevallier <[email protected]>
> ---
> -static int tse_pcs_reset(struct altera_tse_pcs *tse_pcs)
> -{
> - u16 bmcr;
> -
> - /* Reset PCS block */
> - bmcr = tse_pcs_read(tse_pcs, MII_BMCR);
> - bmcr |= BMCR_RESET;
> - tse_pcs_write(tse_pcs, MII_BMCR, bmcr);
> -
> - return read_poll_timeout(tse_pcs_read, bmcr, (bmcr & BMCR_RESET),
> - 10, SGMII_PCS_SW_RESET_TIMEOUT, 1,
> - tse_pcs, MII_BMCR);
> -}

I just noticed this difference between the Lynx PCS and Altera TSE PCS
drivers. The Lynx driver doesn't reset the PCS, and if it did, it would
wait until the opposite condition from yours would be true: BMCR_RESET
should clear: "!(bmcr & BMCR_RESET)".

Is your reset procedure correct, I wonder?

2023-05-22 06:35:59

by Maxime Chevallier

[permalink] [raw]
Subject: Re: [RFC 7/7] net: pcs: Drop the TSE PCS driver

Hello Vlad,

On Mon, 15 May 2023 14:48:09 +0300
Vladimir Oltean <[email protected]> wrote:

> Hi Maxime,
>
> On Fri, Mar 24, 2023 at 10:36:44AM +0100, Maxime Chevallier wrote:
> > Now that we can easily create a mdio-device that represents a
> > memory-mapped device that exposes an MDIO-like register layout, we
> > don't need the Altera TSE PCS anymore, since we can use the Lynx
> > PCS instead.
> >
> > Signed-off-by: Maxime Chevallier <[email protected]>
> > ---
> > -static int tse_pcs_reset(struct altera_tse_pcs *tse_pcs)
> > -{
> > - u16 bmcr;
> > -
> > - /* Reset PCS block */
> > - bmcr = tse_pcs_read(tse_pcs, MII_BMCR);
> > - bmcr |= BMCR_RESET;
> > - tse_pcs_write(tse_pcs, MII_BMCR, bmcr);
> > -
> > - return read_poll_timeout(tse_pcs_read, bmcr, (bmcr &
> > BMCR_RESET),
> > - 10, SGMII_PCS_SW_RESET_TIMEOUT, 1,
> > - tse_pcs, MII_BMCR);
> > -}
>
> I just noticed this difference between the Lynx PCS and Altera TSE PCS
> drivers. The Lynx driver doesn't reset the PCS, and if it did, it
> would wait until the opposite condition from yours would be true:
> BMCR_RESET should clear: "!(bmcr & BMCR_RESET)".
>
> Is your reset procedure correct, I wonder?

I also wonder... My understanding from the documentation I had is that
the PCS needed to be reset every time a major configuration was
changed, as the reset would put the internal state machines back to
their init condition, but without resetting the whole IP.
When performing my initial tests, this was indeed necessary. However
when porting to Lynx and running tests, it pretty much worked out of the
box, and I'm quite happy with it being that way. So I didn't dig much
deeper on why the reset was needed with the TSE PCS in the first place.

On a side note, the final dependencies for the final removal of TSE
were merged into the regmap tree, I'll therefore send a new version of
that series shortly.

Thanks for the review Vlad,

Best Regards,

Maxime