/* CPU port mask register - controls which ports are treated as CPU ports */ #define RTL8365MB_CPU_PORT_MASK_REG 0x1219 #define RTL8365MB_CPU_PORT_MASK_MASK 0x07FF
/* The DSA callback .get_stats64 runs in atomic context, so we are not allowed *toblock.Ontheotherhand,accessingMIBcountersabsolutelyrequiresusto *block.ThesolutionisthustoscheduleworkwhichpollstheMIBcounters *asynchronouslyandupdatessomeprivatedata,whichthecallbackcanthen *fetchatomically.Threesecondsshouldbeagoodenoughpollinginterval.
*/ #define RTL8365MB_STATS_INTERVAL_JIFFIES (3 * HZ)
staticint rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy,
u32 ocp_addr)
{
u32 val; int ret;
/* Set OCP prefix */
val = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK, ocp_addr);
ret = regmap_update_bits(
priv->map_nolock, RTL8365MB_GPHY_OCP_MSB_0_REG,
RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK,
FIELD_PREP(RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, val)); if (ret) return ret;
/* Set PHY register address */
val = RTL8365MB_PHY_BASE;
val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK, phy);
val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK,
ocp_addr >> 1);
val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK,
ocp_addr >> 6);
ret = regmap_write(priv->map_nolock,
RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, val); if (ret) return ret;
return0;
}
staticint rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy,
u32 ocp_addr, u16 *data)
{
u32 val; int ret;
rtl83xx_lock(priv);
ret = rtl8365mb_phy_poll_busy(priv); if (ret) goto out;
ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); if (ret) goto out;
/* Execute read operation */
val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK,
RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) |
FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK,
RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ);
ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG,
val); if (ret) goto out;
ret = rtl8365mb_phy_poll_busy(priv); if (ret) goto out;
/* Get PHY register data */
ret = regmap_read(priv->map_nolock,
RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, &val); if (ret) goto out;
*data = val & 0xFFFF;
out:
rtl83xx_unlock(priv);
return ret;
}
staticint rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy,
u32 ocp_addr, u16 data)
{
u32 val; int ret;
rtl83xx_lock(priv);
ret = rtl8365mb_phy_poll_busy(priv); if (ret) goto out;
ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); if (ret) goto out;
/* Set PHY register data */
ret = regmap_write(priv->map_nolock,
RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, data); if (ret) goto out;
/* Execute write operation */
val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK,
RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) |
FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK,
RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE);
ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG,
val); if (ret) goto out;
ret = rtl8365mb_phy_poll_busy(priv); if (ret) goto out;
out:
rtl83xx_unlock(priv);
return0;
}
staticint rtl8365mb_phy_read(struct realtek_priv *priv, int phy, int regnum)
{
u32 ocp_addr;
u16 val; int ret;
if (phy > RTL8365MB_PHYADDRMAX) return -EINVAL;
if (regnum > RTL8365MB_PHYREGMAX) return -EINVAL;
ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2;
ret = rtl8365mb_phy_ocp_read(priv, phy, ocp_addr, &val); if (ret) {
dev_err(priv->dev, "failed to read PHY%d reg %02x @ %04x, ret %d\n", phy,
regnum, ocp_addr, ret); return ret;
}
if (!extint) {
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
/* GMII is the default interface mode for phylib, so *wehavetosupportitforportswithintegratedPHY.
*/
__set_bit(PHY_INTERFACE_MODE_GMII,
config->supported_interfaces); return;
}
/* Populate according to the modes supported by _this driver_, *notnecessarilythemodessupportedbythehardware,someof *whichremainunimplemented.
*/
if (extint->supported_interfaces & RTL8365MB_PHY_INTERFACE_MODE_RGMII)
phy_interface_set_rgmii(config->supported_interfaces);
}
if (mode != MLO_AN_PHY && mode != MLO_AN_FIXED) {
dev_err(priv->dev, "port %d supports only conventional PHY or fixed-link\n",
port); return;
}
if (phy_interface_mode_is_rgmii(state->interface)) {
ret = rtl8365mb_ext_config_rgmii(priv, port, state->interface); if (ret)
dev_err(priv->dev, "failed to configure RGMII mode on port %d: %d\n",
port, ret); return;
}
/* TODO: Implement MII and RMII modes, which the RTL8365MB-VC also *supports
*/
}
mb = priv->chip_data;
p = &mb->ports[port];
cancel_delayed_work_sync(&p->mib_work);
if (phy_interface_mode_is_rgmii(interface)) {
ret = rtl8365mb_ext_config_forcemode(priv, port, false, 0, 0, false, false); if (ret)
dev_err(priv->dev, "failed to reset forced mode on port %d: %d\n",
port, ret);
mb = priv->chip_data;
p = &mb->ports[port];
schedule_delayed_work(&p->mib_work, 0);
if (phy_interface_mode_is_rgmii(interface)) {
ret = rtl8365mb_ext_config_forcemode(priv, port, true, speed,
duplex, tx_pause,
rx_pause); if (ret)
dev_err(priv->dev, "failed to force mode on port %d: %d\n", port,
ret);
return;
}
}
staticint rtl8365mb_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
{ struct realtek_priv *priv = ds->priv; int frame_size;
/* When a new MTU is set, DSA always sets the CPU port's MTU to the *largestMTUoftheuserports.Becausetheswitchonlyhasaglobal *RXlengthregister,onlyallowingCPUporthereisenough.
*/ if (!dsa_is_cpu_port(ds, port)) return0;
switch (state) { case BR_STATE_DISABLED:
val = RTL8365MB_STP_STATE_DISABLED; break; case BR_STATE_BLOCKING: case BR_STATE_LISTENING:
val = RTL8365MB_STP_STATE_BLOCKING; break; case BR_STATE_LEARNING:
val = RTL8365MB_STP_STATE_LEARNING; break; case BR_STATE_FORWARDING:
val = RTL8365MB_STP_STATE_FORWARDING; break; default:
dev_err(priv->dev, "invalid STP state: %u\n", state); return;
}
regmap_update_bits(priv->map, RTL8365MB_MSTI_CTRL_REG(msti, port),
RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(port),
val << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(port));
}
staticint rtl8365mb_port_set_learning(struct realtek_priv *priv, int port, bool enable)
{ /* Enable/disable learning by limiting the number of L2 addresses the *portcanlearn.Realtekdocumentationstatesthatalimitofzero *disableslearning.Whenenablinglearning,setittothechip's *maximum.
*/ return regmap_write(priv->map, RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(port),
enable ? RTL8365MB_LEARN_LIMIT_MAX : 0);
}
staticint rtl8365mb_mib_counter_read(struct realtek_priv *priv, int port,
u32 offset, u32 length, u64 *mibvalue)
{
u64 tmpvalue = 0;
u32 val; int ret; int i;
/* The MIB address is an SRAM address. We request a particular address *andthenpollthecontrolregisterbeforereadingthevaluefromsome *counterregisters.
*/
ret = regmap_write(priv->map, RTL8365MB_MIB_ADDRESS_REG,
RTL8365MB_MIB_ADDRESS(port, offset)); if (ret) return ret;
/* Poll for completion */
ret = regmap_read_poll_timeout(priv->map, RTL8365MB_MIB_CTRL0_REG, val,
!(val & RTL8365MB_MIB_CTRL0_BUSY_MASK), 10, 100); if (ret) return ret;
/* Presumably this indicates a MIB counter read failure */ if (val & RTL8365MB_MIB_CTRL0_RESET_MASK) return -EIO;
/* There are four MIB counter registers each holding a 16 bit word of a *MIBcounter.Dependingontheoffset,weshouldreadfromtheupper *twoorlowertworegisters.IncasetheMIBcounteris4words,we *readfromallfourregisters.
*/ if (length == 4)
offset = 3; else
offset = (offset + 1) % 4;
/* Read the MIB counter 16 bits at a time */ for (i = 0; i < length; i++) {
ret = regmap_read(priv->map,
RTL8365MB_MIB_COUNTER_REG(offset - i), &val); if (ret) return ret;
tmpvalue = ((tmpvalue) << 16) | (val & 0xFFFF);
}
/* Only commit the result if no error occurred */
*mibvalue = tmpvalue;
return0;
}
staticvoid rtl8365mb_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data)
{ struct realtek_priv *priv = ds->priv; struct rtl8365mb *mb; int ret; int i;
mb = priv->chip_data;
mutex_lock(&mb->mib_lock); for (i = 0; i < RTL8365MB_MIB_END; i++) { struct rtl8365mb_mib_counter *mib = &rtl8365mb_mib_counters[i];
ret = rtl8365mb_mib_counter_read(priv, port, mib->offset,
mib->length, &data[i]); if (ret) {
dev_err(priv->dev, "failed to read port %d counters: %d\n", port,
ret); break;
}
}
mutex_unlock(&mb->mib_lock);
}
staticvoid rtl8365mb_get_strings(struct dsa_switch *ds, int port, u32 stringset, u8 *data)
{ int i;
if (stringset != ETH_SS_STATS) return;
for (i = 0; i < RTL8365MB_MIB_END; i++) { struct rtl8365mb_mib_counter *mib = &rtl8365mb_mib_counters[i];
ethtool_puts(&data, mib->name);
}
}
staticint rtl8365mb_get_sset_count(struct dsa_switch *ds, int port, int sset)
{ if (sset != ETH_SS_STATS) return -EOPNOTSUPP;
}; struct realtek_priv *priv = ds->priv; struct rtl8365mb *mb; int ret; int i;
mb = priv->chip_data;
mutex_lock(&mb->mib_lock); for (i = 0; i < RTL8365MB_MIB_END; i++) { struct rtl8365mb_mib_counter *mib = &rtl8365mb_mib_counters[i];
/* Only fetch required MIB counters (marked = 1 above) */ if (!cnt[i]) continue;
ret = rtl8365mb_mib_counter_read(priv, port, mib->offset,
mib->length, &cnt[i]); if (ret) break;
}
mutex_unlock(&mb->mib_lock);
/* The RTL8365MB-VC exposes MIB objects, which we have to translate into *IEEE802.3ManagedObjects.Thisisnotalwayscompletelyfaithful, *butwetryoutbest.SeeRFC3635foradetailedtreatmentofthe *subject.
*/
/* Per-chip global mutex to protect MIB counter access, since doing *sorequiresaccessingaseriesofregistersinaparticularorder.
*/
mutex_init(&mb->mib_lock);
for (i = 0; i < priv->num_ports; i++) { struct rtl8365mb_port *p = &mb->ports[i];
if (dsa_is_unused_port(ds, i)) continue;
/* Per-port spinlock to protect the stats64 data */
spin_lock_init(&p->stats_lock);
/* This work polls the MIB counters and keeps the stats64 data *up-to-date.
*/
INIT_DELAYED_WORK(&p->mib_work, rtl8365mb_stats_poll);
}
}
/* rtl8365mb IRQs cascade off this one */
irq = of_irq_get(intc, 0); if (irq <= 0) { if (irq != -EPROBE_DEFER)
dev_err(priv->dev, "failed to get parent irq: %d\n",
irq);
ret = irq ? irq : -EINVAL; goto out_put_node;
}
priv->irqdomain = irq_domain_create_linear(of_fwnode_handle(intc), priv->num_ports,
&rtl8365mb_irqdomain_ops, priv); if (!priv->irqdomain) {
dev_err(priv->dev, "failed to add irq domain\n");
ret = -ENOMEM; goto out_put_node;
}
for (i = 0; i < priv->num_ports; i++) {
virq = irq_create_mapping(priv->irqdomain, i); if (!virq) {
dev_err(priv->dev, "failed to create irq domain mapping\n");
ret = -EINVAL; goto out_remove_irqdomain;
}
irq_set_parent(virq, irq);
}
/* Configure chip interrupt signal polarity */
irq_trig = irq_get_trigger_type(irq); switch (irq_trig) { case IRQF_TRIGGER_RISING: case IRQF_TRIGGER_HIGH:
val = RTL8365MB_INTR_POLARITY_HIGH; break; case IRQF_TRIGGER_FALLING: case IRQF_TRIGGER_LOW:
val = RTL8365MB_INTR_POLARITY_LOW; break; default:
dev_err(priv->dev, "unsupported irq trigger type %u\n",
irq_trig);
ret = -EINVAL; goto out_remove_irqdomain;
}
ret = regmap_update_bits(priv->map, RTL8365MB_INTR_POLARITY_REG,
RTL8365MB_INTR_POLARITY_MASK,
FIELD_PREP(RTL8365MB_INTR_POLARITY_MASK, val)); if (ret) goto out_remove_irqdomain;
/* Disable the interrupt in case the chip has it enabled on reset */
ret = rtl8365mb_irq_disable(priv); if (ret) goto out_remove_irqdomain;
/* Clear the interrupt status register */
ret = regmap_write(priv->map, RTL8365MB_INTR_STATUS_REG,
RTL8365MB_INTR_ALL_MASK); if (ret) goto out_remove_irqdomain;
ret = request_threaded_irq(irq, NULL, rtl8365mb_irq, IRQF_ONESHOT, "rtl8365mb", priv); if (ret) {
dev_err(priv->dev, "failed to request irq: %d\n", ret); goto out_remove_irqdomain;
}
/* Store the irq so that we know to free it during teardown */
mb->irq = irq;
ret = rtl8365mb_irq_enable(priv); if (ret) goto out_free_irq;
switch (proto) { case DSA_TAG_PROTO_RTL8_4:
cpu->format = RTL8365MB_CPU_FORMAT_8BYTES;
cpu->position = RTL8365MB_CPU_POS_AFTER_SA; break; case DSA_TAG_PROTO_RTL8_4T:
cpu->format = RTL8365MB_CPU_FORMAT_8BYTES;
cpu->position = RTL8365MB_CPU_POS_BEFORE_CRC; break; /* The switch also supports a 4-byte format, similar to rtl4a but with *thesame0x048-bitversionandprobably8-bitportsource/dest. *Thereisnopublicdocaboutit.Notsupportedyetanditwillprobably *neverbe.
*/ default: return -EPROTONOSUPPORT;
}
return rtl8365mb_cpu_config(priv);
}
staticint rtl8365mb_switch_init(struct realtek_priv *priv)
{ struct rtl8365mb *mb = priv->chip_data; conststruct rtl8365mb_chip_info *ci; int ret; int i;
ci = mb->chip_info;
/* Do any chip-specific init jam before getting to the common stuff */ if (ci->jam_table) { for (i = 0; i < ci->jam_size; i++) {
ret = regmap_write(priv->map, ci->jam_table[i].reg,
ci->jam_table[i].val); if (ret) return ret;
}
}
/* Common init jam */ for (i = 0; i < ARRAY_SIZE(rtl8365mb_init_jam_common); i++) {
ret = regmap_write(priv->map, rtl8365mb_init_jam_common[i].reg,
rtl8365mb_init_jam_common[i].val); if (ret) return ret;
}
ret = rtl8365mb_reset_chip(priv); if (ret) {
dev_err(priv->dev, "failed to reset chip: %d\n", ret); goto out_error;
}
/* Configure switch to vendor-defined initial state */
ret = rtl8365mb_switch_init(priv); if (ret) {
dev_err(priv->dev, "failed to initialize switch: %d\n", ret); goto out_error;
}
/* Set up cascading IRQs */
ret = rtl8365mb_irq_setup(priv); if (ret == -EPROBE_DEFER) return ret; elseif (ret)
dev_info(priv->dev, "no interrupt support\n");
if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS)
cpu->trap_port = cpu_dp->index;
}
cpu->enable = cpu->mask > 0;
ret = rtl8365mb_cpu_config(priv); if (ret) goto out_teardown_irq;
/* Configure ports */ for (i = 0; i < priv->num_ports; i++) { struct rtl8365mb_port *p = &mb->ports[i];
if (dsa_is_unused_port(ds, i)) continue;
/* Forward only to the CPU */
ret = rtl8365mb_port_set_isolation(priv, i, cpu->mask); if (ret) goto out_teardown_irq;
/* Disable learning */
ret = rtl8365mb_port_set_learning(priv, i, false); if (ret) goto out_teardown_irq;
/* Set the initial STP state of all ports to DISABLED, otherwise *portswillstillforwardframestotheCPUdespitebeing *administrativelydownbydefault.
*/
rtl8365mb_port_stp_state_set(ds, i, BR_STATE_DISABLED);
/* Set up per-port private data */
p->priv = priv;
p->index = i;
}
ret = rtl8365mb_port_change_mtu(ds, cpu->trap_port, ETH_DATA_LEN); if (ret) goto out_teardown_irq;
ret = rtl83xx_setup_user_mdio(ds); if (ret) {
dev_err(priv->dev, "could not set up MDIO bus\n"); goto out_teardown_irq;
}
/* For some reason we have to write a magic value to an arbitrary *registerwheneveraccessingthechipID/versionregisters.
*/
ret = regmap_write(map, RTL8365MB_MAGIC_REG, RTL8365MB_MAGIC_VALUE); if (ret) return ret;
ret = regmap_read(map, RTL8365MB_CHIP_ID_REG, id); if (ret) return ret;
ret = regmap_read(map, RTL8365MB_CHIP_VER_REG, ver); if (ret) return ret;
/* Reset magic register */
ret = regmap_write(map, RTL8365MB_MAGIC_REG, 0); if (ret) return ret;
return0;
}
staticint rtl8365mb_detect(struct realtek_priv *priv)
{ struct rtl8365mb *mb = priv->chip_data;
u32 chip_id;
u32 chip_ver; int ret; int i;
ret = rtl8365mb_get_chip_id_and_ver(priv->map, &chip_id, &chip_ver); if (ret) {
dev_err(priv->dev, "failed to read chip id and version: %d\n",
ret); return ret;
}
for (i = 0; i < ARRAY_SIZE(rtl8365mb_chip_infos); i++) { conststruct rtl8365mb_chip_info *ci = &rtl8365mb_chip_infos[i];
MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>");
MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("REALTEK_DSA");
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.62Bemerkung:
(vorverarbeitet am 2026-10-11)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.