Merge branch 'net-pcs-rzn1-miic-support-configurable-phy_link-polarity'

Lad Prabhakar says:

====================
net: pcs: rzn1-miic: Support configurable PHY_LINK polarity

This series adds support for configuring the active level of MIIC
PHY_LINK status signals on Renesas RZ/N1 and RZ/T2H/N2H platforms.

The MIIC block provides dedicated hardware PHY_LINK signals that indicate
EtherPHY link-up and link-down status independently of whether the MAC
(GMAC) or Ethernet switch (ETHSW) is used. While GMAC-based systems
typically obtain link state via MDIO and handle it in software, the
ETHSW relies on these PHY_LINK pins for both CPU-assisted operation and
switch-only forwarding paths that do not involve the host processor.

These hardware PHY_LINK signals are particularly important for use cases
requiring fast reaction to link-down events, such as redundancy protocols
including Device Level Ring (DLR). In such scenarios, relying solely on
software-based link detection introduces latency that can negatively
impact recovery time. The ETHSW therefore exposes PHY_LINK signals to
enable immediate hardware-level detection of cable or port failures.

Some systems require the PHY_LINK signal polarity to be configured as
active low rather than the default active high. This series introduces a
new DT property to describe the required polarity and adds corresponding
driver support to program the MIIC PHY_LINK register accordingly. The
configuration is accumulated during DT parsing and applied once hardware
initialization is complete, taking into account SoC-specific differences
between RZ/N1 and RZ/T2H/N2H.
====================

Link: https://patch.msgid.link/20260112173555.1166714-1-prabhakar.mahadev-lad.rj@bp.renesas.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-01-17 15:17:27 -08:00
commit b790404680
2 changed files with 109 additions and 3 deletions

View File

@ -86,6 +86,13 @@ patternProperties:
and include/dt-bindings/net/renesas,r9a09g077-pcs-miic.h for RZ/N2H, RZ/T2H SoCs.
$ref: /schemas/types.yaml#/definitions/uint32
renesas,miic-phy-link-active-low:
type: boolean
description: Indicates that the PHY-link signal provided by the Ethernet switch,
EtherCAT, or SERCOS3 interface is active low. When present, this property
sets the corresponding signal polarity to active low. When omitted, the signal
defaults to active high.
required:
- reg
- renesas,miic-input

View File

@ -28,6 +28,8 @@
#define MIIC_MODCTRL 0x8
#define MIIC_PHY_LINK 0x14
#define MIIC_CONVCTRL(port) (0x100 + (port) * 4)
#define MIIC_CONVCTRL_CONV_SPEED GENMASK(1, 0)
@ -177,6 +179,16 @@ static const char * const rzt2h_reset_ids[] = {
"crst",
};
/**
* struct miic_phy_link_cfg - MIIC PHY_LINK configuration
* @mask: Mask of phy_link bits
* @val: Value of phy_link bits
*/
struct miic_phy_link_cfg {
u32 mask;
u32 val;
};
/**
* struct miic - MII converter structure
* @base: base address of the MII converter
@ -184,6 +196,7 @@ static const char * const rzt2h_reset_ids[] = {
* @lock: Lock used for read-modify-write access
* @rsts: Reset controls for the MII converter
* @of_data: Pointer to OF data
* @link_cfg: MIIC PHY_LINK configuration
*/
struct miic {
void __iomem *base;
@ -191,6 +204,12 @@ struct miic {
spinlock_t lock;
struct reset_control_bulk_data rsts[MIIC_MAX_NUM_RSTS];
const struct miic_of_data *of_data;
struct miic_phy_link_cfg link_cfg;
};
enum miic_type {
MIIC_TYPE_RZN1,
MIIC_TYPE_RZT2H,
};
/**
@ -210,6 +229,7 @@ struct miic {
* @init_unlock_lock_regs: Flag to indicate if registers need to be unlocked
* before access.
* @miic_write: Function pointer to write a value to a MIIC register
* @type: Type of MIIC
*/
struct miic_of_data {
struct modctrl_match *match_table;
@ -226,6 +246,7 @@ struct miic_of_data {
u8 reset_count;
bool init_unlock_lock_regs;
void (*miic_write)(struct miic *miic, int offset, u32 value);
enum miic_type type;
};
/**
@ -581,10 +602,79 @@ static int miic_match_dt_conf(struct miic *miic, s8 *dt_val, u32 *mode_cfg)
return -EINVAL;
}
static void miic_configure_phy_link(struct miic *miic, u32 conf,
u32 port, bool active_low)
{
bool polarity_active_high;
u32 mask, shift;
/* determine shift and polarity for this conf */
if (miic->of_data->type == MIIC_TYPE_RZN1) {
switch (conf) {
/* switch ports => bits [3:0] (shift 0), active when low */
case MIIC_SWITCH_PORTA:
case MIIC_SWITCH_PORTB:
case MIIC_SWITCH_PORTC:
case MIIC_SWITCH_PORTD:
shift = 0;
polarity_active_high = false;
break;
/* EtherCAT ports => bits [7:4] (shift 4), active when high */
case MIIC_ETHERCAT_PORTA:
case MIIC_ETHERCAT_PORTB:
case MIIC_ETHERCAT_PORTC:
shift = 4;
polarity_active_high = true;
break;
/* Sercos ports => bits [11:8] (shift 8), active when high */
case MIIC_SERCOS_PORTA:
case MIIC_SERCOS_PORTB:
shift = 8;
polarity_active_high = true;
break;
default:
return;
}
} else {
switch (conf) {
/* ETHSW ports => bits [3:0] (shift 0), active when low */
case ETHSS_ETHSW_PORT0:
case ETHSS_ETHSW_PORT1:
case ETHSS_ETHSW_PORT2:
shift = 0;
polarity_active_high = false;
break;
/* ESC ports => bits [7:4] (shift 4), active when high */
case ETHSS_ESC_PORT0:
case ETHSS_ESC_PORT1:
case ETHSS_ESC_PORT2:
shift = 4;
polarity_active_high = true;
break;
default:
return;
}
}
mask = BIT(port + shift);
miic->link_cfg.mask |= mask;
if (polarity_active_high != active_low)
miic->link_cfg.val |= mask;
else
miic->link_cfg.val &= ~mask;
}
static int miic_parse_dt(struct miic *miic, u32 *mode_cfg)
{
struct device_node *np = miic->dev->of_node;
struct device_node *conv;
bool active_low;
int port, ret;
s8 *dt_val;
u32 conf;
@ -603,10 +693,15 @@ static int miic_parse_dt(struct miic *miic, u32 *mode_cfg)
if (of_property_read_u32(conv, "reg", &port))
continue;
if (of_property_read_u32(conv, "renesas,miic-input", &conf))
continue;
/* Adjust for 0 based index */
port += !miic->of_data->miic_port_start;
if (of_property_read_u32(conv, "renesas,miic-input", &conf) == 0)
dt_val[port] = conf;
dt_val[port + !miic->of_data->miic_port_start] = conf;
active_low = of_property_read_bool(conv, "renesas,miic-phy-link-active-low");
miic_configure_phy_link(miic, conf, port, active_low);
}
ret = miic_match_dt_conf(miic, dt_val, mode_cfg);
@ -696,6 +791,8 @@ static int miic_probe(struct platform_device *pdev)
if (ret)
goto disable_runtime_pm;
miic_reg_rmw(miic, MIIC_PHY_LINK, miic->link_cfg.mask, miic->link_cfg.val);
/* miic_create() relies on that fact that data are attached to the
* platform device to determine if the driver is ready so this needs to
* be the last thing to be done after everything is initialized
@ -729,6 +826,7 @@ static struct miic_of_data rzn1_miic_of_data = {
.sw_mode_mask = GENMASK(4, 0),
.init_unlock_lock_regs = true,
.miic_write = miic_reg_writel_unlocked,
.type = MIIC_TYPE_RZN1,
};
static struct miic_of_data rzt2h_miic_of_data = {
@ -745,6 +843,7 @@ static struct miic_of_data rzt2h_miic_of_data = {
.reset_ids = rzt2h_reset_ids,
.reset_count = ARRAY_SIZE(rzt2h_reset_ids),
.miic_write = miic_reg_writel_locked,
.type = MIIC_TYPE_RZT2H,
};
static const struct of_device_id miic_of_mtable[] = {