Commit cf51016b authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'net-dsa-b53-mmap-add-bcm63xx-ephy-power-control'

Kyle Hendry says:

====================
net: dsa: b53: mmap: Add bcm63xx EPHY power control

The gpio controller on some bcm63xx SoCs has a register for
controlling functionality of the internal fast ethernet phys.
These patches allow the b53 driver to enable/disable phy
power.

The register also contains reset bits which will be set by
a reset driver in another patch series:
https://lore.kernel.org/all/20250715234605.36216-1-kylehendrydev@gmail.com/

v1: https://lore.kernel.org/20250716002922.230807-1-kylehendrydev@gmail.com
====================

Link: https://patch.msgid.link/20250724035300.20497-1-kylehendrydev@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 33360f25 5ac00023
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -66,6 +66,12 @@ properties:
              - brcm,bcm63268-switch
          - const: brcm,bcm63xx-switch

  brcm,gpio-ctrl:
    description:
      A phandle to the syscon node of the bcm63xx gpio controller
      which contains phy control registers
    $ref: /schemas/types.yaml#/definitions/phandle

required:
  - compatible
  - reg
+12 −15
Original line number Diff line number Diff line
@@ -689,6 +689,9 @@ int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy)

	cpu_port = dsa_to_port(ds, port)->cpu_dp->index;

	if (dev->ops->phy_enable)
		dev->ops->phy_enable(dev, port);

	if (dev->ops->irq_enable)
		ret = dev->ops->irq_enable(dev, port);
	if (ret)
@@ -727,6 +730,9 @@ void b53_disable_port(struct dsa_switch *ds, int port)
	reg |= PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE;
	b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), reg);

	if (dev->ops->phy_disable)
		dev->ops->phy_disable(dev, port);

	if (dev->ops->irq_disable)
		dev->ops->irq_disable(dev, port);
}
@@ -1404,7 +1410,7 @@ static void b53_adjust_63xx_rgmii(struct dsa_switch *ds, int port,
	b53_read8(dev, B53_CTRL_PAGE, B53_RGMII_CTRL_P(port), &rgmii_ctrl);
	rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC);

	if (is63268(dev))
	if (is6318_268(dev))
		rgmii_ctrl |= RGMII_CTRL_MII_OVERRIDE;

	rgmii_ctrl |= RGMII_CTRL_ENABLE_GMII;
@@ -2768,19 +2774,6 @@ static const struct b53_chip_data b53_switch_chips[] = {
		.jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
		.jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,
	},
	{
		.chip_id = BCM63268_DEVICE_ID,
		.dev_name = "BCM63268",
		.vlans = 4096,
		.enabled_ports = 0, /* pdata must provide them */
		.arl_bins = 4,
		.arl_buckets = 1024,
		.imp_port = 8,
		.vta_regs = B53_VTA_REGS_63XX,
		.duplex_reg = B53_DUPLEX_STAT_63XX,
		.jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
		.jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,
	},
	{
		.chip_id = BCM53010_DEVICE_ID,
		.dev_name = "BCM53010",
@@ -2930,13 +2923,17 @@ static const struct b53_chip_data b53_switch_chips[] = {

static int b53_switch_init(struct b53_device *dev)
{
	u32 chip_id = dev->chip_id;
	unsigned int i;
	int ret;

	if (is63xx(dev))
		chip_id = BCM63XX_DEVICE_ID;

	for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) {
		const struct b53_chip_data *chip = &b53_switch_chips[i];

		if (chip->chip_id == dev->chip_id) {
		if (chip->chip_id == chip_id) {
			if (!dev->enabled_ports)
				dev->enabled_ports = chip->enabled_ports;
			dev->name = chip->dev_name;
+103 −4
Original line number Diff line number Diff line
@@ -21,13 +21,54 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
#include <linux/platform_data/b53.h>
#include <linux/regmap.h>

#include "b53_priv.h"

#define BCM63XX_EPHY_REG 0x3C

struct b53_phy_info {
	u32 ephy_enable_mask;
	u32 ephy_port_mask;
	u32 ephy_bias_bit;
	const u32 *ephy_offset;
};

struct b53_mmap_priv {
	void __iomem *regs;
	struct regmap *gpio_ctrl;
	const struct b53_phy_info *phy_info;
	u32 phys_enabled;
};

static const u32 bcm6318_ephy_offsets[] = {4, 5, 6, 7};

static const struct b53_phy_info bcm6318_ephy_info = {
	.ephy_enable_mask = BIT(0) | BIT(4) | BIT(8) | BIT(12) | BIT(16),
	.ephy_port_mask = GENMASK((ARRAY_SIZE(bcm6318_ephy_offsets) - 1), 0),
	.ephy_bias_bit = 24,
	.ephy_offset = bcm6318_ephy_offsets,
};

static const u32 bcm6368_ephy_offsets[] = {2, 3, 4, 5};

static const struct b53_phy_info bcm6368_ephy_info = {
	.ephy_enable_mask = BIT(0),
	.ephy_port_mask = GENMASK((ARRAY_SIZE(bcm6368_ephy_offsets) - 1), 0),
	.ephy_bias_bit = 0,
	.ephy_offset = bcm6368_ephy_offsets,
};

static const u32 bcm63268_ephy_offsets[] = {4, 9, 14};

static const struct b53_phy_info bcm63268_ephy_info = {
	.ephy_enable_mask = GENMASK(4, 0),
	.ephy_port_mask = GENMASK((ARRAY_SIZE(bcm63268_ephy_offsets) - 1), 0),
	.ephy_bias_bit = 24,
	.ephy_offset = bcm63268_ephy_offsets,
};

static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
@@ -229,6 +270,50 @@ static int b53_mmap_phy_write16(struct b53_device *dev, int addr, int reg,
	return -EIO;
}

static int bcm63xx_ephy_set(struct b53_device *dev, int port, bool enable)
{
	struct b53_mmap_priv *priv = dev->priv;
	const struct b53_phy_info *info = priv->phy_info;
	struct regmap *gpio_ctrl = priv->gpio_ctrl;
	u32 mask, val;

	if (enable) {
		mask = (info->ephy_enable_mask << info->ephy_offset[port])
				| BIT(info->ephy_bias_bit);
		val = 0;
	} else {
		mask = (info->ephy_enable_mask << info->ephy_offset[port]);
		if (!((priv->phys_enabled & ~BIT(port)) & info->ephy_port_mask))
			mask |= BIT(info->ephy_bias_bit);
		val = mask;
	}
	return regmap_update_bits(gpio_ctrl, BCM63XX_EPHY_REG, mask, val);
}

static void b53_mmap_phy_enable(struct b53_device *dev, int port)
{
	struct b53_mmap_priv *priv = dev->priv;
	int ret = 0;

	if (priv->phy_info && (BIT(port) & priv->phy_info->ephy_port_mask))
		ret = bcm63xx_ephy_set(dev, port, true);

	if (!ret)
		priv->phys_enabled |= BIT(port);
}

static void b53_mmap_phy_disable(struct b53_device *dev, int port)
{
	struct b53_mmap_priv *priv = dev->priv;
	int ret = 0;

	if (priv->phy_info && (BIT(port) & priv->phy_info->ephy_port_mask))
		ret = bcm63xx_ephy_set(dev, port, false);

	if (!ret)
		priv->phys_enabled &= ~BIT(port);
}

static const struct b53_io_ops b53_mmap_ops = {
	.read8 = b53_mmap_read8,
	.read16 = b53_mmap_read16,
@@ -242,6 +327,8 @@ static const struct b53_io_ops b53_mmap_ops = {
	.write64 = b53_mmap_write64,
	.phy_read16 = b53_mmap_phy_read16,
	.phy_write16 = b53_mmap_phy_write16,
	.phy_enable = b53_mmap_phy_enable,
	.phy_disable = b53_mmap_phy_disable,
};

static int b53_mmap_probe_of(struct platform_device *pdev,
@@ -313,6 +400,18 @@ static int b53_mmap_probe(struct platform_device *pdev)

	priv->regs = pdata->regs;

	priv->gpio_ctrl = syscon_regmap_lookup_by_phandle(np, "brcm,gpio-ctrl");
	if (!IS_ERR(priv->gpio_ctrl)) {
		if (pdata->chip_id == BCM6318_DEVICE_ID ||
		    pdata->chip_id == BCM6328_DEVICE_ID ||
		    pdata->chip_id == BCM6362_DEVICE_ID)
			priv->phy_info = &bcm6318_ephy_info;
		else if (pdata->chip_id == BCM6368_DEVICE_ID)
			priv->phy_info = &bcm6368_ephy_info;
		else if (pdata->chip_id == BCM63268_DEVICE_ID)
			priv->phy_info = &bcm63268_ephy_info;
	}

	dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, priv);
	if (!dev)
		return -ENOMEM;
@@ -348,16 +447,16 @@ static const struct of_device_id b53_mmap_of_table[] = {
		.data = (void *)BCM63XX_DEVICE_ID,
	}, {
		.compatible = "brcm,bcm6318-switch",
		.data = (void *)BCM63268_DEVICE_ID,
		.data = (void *)BCM6318_DEVICE_ID,
	}, {
		.compatible = "brcm,bcm6328-switch",
		.data = (void *)BCM63XX_DEVICE_ID,
		.data = (void *)BCM6328_DEVICE_ID,
	}, {
		.compatible = "brcm,bcm6362-switch",
		.data = (void *)BCM63XX_DEVICE_ID,
		.data = (void *)BCM6362_DEVICE_ID,
	}, {
		.compatible = "brcm,bcm6368-switch",
		.data = (void *)BCM63XX_DEVICE_ID,
		.data = (void *)BCM6368_DEVICE_ID,
	}, {
		.compatible = "brcm,bcm63268-switch",
		.data = (void *)BCM63268_DEVICE_ID,
+13 −2
Original line number Diff line number Diff line
@@ -45,6 +45,8 @@ struct b53_io_ops {
	int (*phy_write16)(struct b53_device *dev, int addr, int reg, u16 value);
	int (*irq_enable)(struct b53_device *dev, int port);
	void (*irq_disable)(struct b53_device *dev, int port);
	void (*phy_enable)(struct b53_device *dev, int port);
	void (*phy_disable)(struct b53_device *dev, int port);
	void (*phylink_get_caps)(struct b53_device *dev, int port,
				 struct phylink_config *config);
	struct phylink_pcs *(*phylink_mac_select_pcs)(struct b53_device *dev,
@@ -71,6 +73,10 @@ enum {
	BCM53125_DEVICE_ID = 0x53125,
	BCM53128_DEVICE_ID = 0x53128,
	BCM63XX_DEVICE_ID = 0x6300,
	BCM6318_DEVICE_ID = 0x6318,
	BCM6328_DEVICE_ID = 0x6328,
	BCM6362_DEVICE_ID = 0x6362,
	BCM6368_DEVICE_ID = 0x6368,
	BCM63268_DEVICE_ID = 0x63268,
	BCM53010_DEVICE_ID = 0x53010,
	BCM53011_DEVICE_ID = 0x53011,
@@ -218,12 +224,17 @@ static inline int is531x5(struct b53_device *dev)
static inline int is63xx(struct b53_device *dev)
{
	return dev->chip_id == BCM63XX_DEVICE_ID ||
		dev->chip_id == BCM6318_DEVICE_ID ||
		dev->chip_id == BCM6328_DEVICE_ID ||
		dev->chip_id == BCM6362_DEVICE_ID ||
		dev->chip_id == BCM6368_DEVICE_ID ||
		dev->chip_id == BCM63268_DEVICE_ID;
}

static inline int is63268(struct b53_device *dev)
static inline int is6318_268(struct b53_device *dev)
{
	return dev->chip_id == BCM63268_DEVICE_ID;
	return dev->chip_id == BCM6318_DEVICE_ID ||
		dev->chip_id == BCM63268_DEVICE_ID;
}

static inline int is5301x(struct b53_device *dev)