// SPDX-License-Identifier: GPL-2.0 /* Realtek SMI subdriver for the Realtek RTL8365MB-VC ethernet switch. * * Copyright (C) 2021 Alvin Šipraga <alsi@bang-olufsen.dk> * Copyright (C) 2021 Michael Rasmussen <mir@bang-olufsen.dk> * * The RTL8365MB-VC is a 4+1 port 10/100/1000M switch controller. It includes 4 * integrated PHYs for the user facing ports, and an extension interface which * can be connected to the CPU - or another PHY - via either MII, RMII, or * RGMII. The switch is configured via the Realtek Simple Management Interface * (SMI), which uses the MDIO/MDC lines. * * Below is a simplified block diagram of the chip and its relevant interfaces. * * .-----------------------------------. * | | * UTP <---------------> Giga PHY <-> PCS <-> P0 GMAC | * UTP <---------------> Giga PHY <-> PCS <-> P1 GMAC | * UTP <---------------> Giga PHY <-> PCS <-> P2 GMAC | * UTP <---------------> Giga PHY <-> PCS <-> P3 GMAC | * | | * CPU/PHY <-MII/RMII/RGMII---> Extension <---> Extension | * | interface 1 GMAC 1 | * | | * SMI driver/ <-MDC/SCL---> Management ~~~~~~~~~~~~~~ | * EEPROM <-MDIO/SDA--> interface ~REALTEK ~~~~~ | * | ~RTL8365MB ~~~ | * | ~GXXXC TAIWAN~ | * GPIO <--------------> Reset ~~~~~~~~~~~~~~ | * | | * Interrupt <----------> Link UP/DOWN events | * controller | | * '-----------------------------------' * * The driver uses DSA to integrate the 4 user and 1 extension ports into the * kernel. Netdevices are created for the user ports, as are PHY devices for * their integrated PHYs. The device tree firmware should also specify the link * partner of the extension port - either via a fixed-link or other phy-handle. * See the device tree bindings for more detailed information. Note that the * driver has only been tested with a fixed-link, but in principle it should not * matter. * * NOTE: Currently, only the RGMII interface is implemented in this driver. * * The interrupt line is asserted on link UP/DOWN events. The driver creates a * custom irqchip to handle this interrupt and demultiplex the events by reading * the status registers via SMI. Interrupts are then propagated to the relevant * PHY device. * * The EEPROM contains initial register values which the chip will read over I2C * upon hardware reset. It is also possible to omit the EEPROM. In both cases, * the driver will manually reprogram some registers using jam tables to reach * an initial state defined by the vendor driver. * * This Linux driver is written based on an OS-agnostic vendor driver from * Realtek. The reference GPL-licensed sources can be found in the OpenWrt * source tree under the name rtl8367c. The vendor driver claims to support a * number of similar switch controllers from Realtek, but the only hardware we * have is the RTL8365MB-VC. Moreover, there does not seem to be any chip under * the name RTL8367C. Although one wishes that the 'C' stood for some kind of * common hardware revision, there exist examples of chips with the suffix -VC * which are explicitly not supported by the rtl8367c driver and which instead * require the rtl8367d vendor driver. With all this uncertainty, the driver has * been modestly named rtl8365mb. Future implementors may wish to rename things * accordingly. * * In the same family of chips, some carry up to 8 user ports and up to 2 * extension ports. Where possible this driver tries to make things generic, but * more work must be done to support these configurations. According to * documentation from Realtek, the family should include the following chips: * * - RTL8363NB * - RTL8363NB-VB * - RTL8363SC * - RTL8363SC-VB * - RTL8364NB * - RTL8364NB-VB * - RTL8365MB-VC * - RTL8366SC * - RTL8367RB-VB * - RTL8367SB * - RTL8367S * - RTL8370MB * - RTL8310SR * * Some of the register logic for these additional chips has been skipped over * while implementing this driver. It is therefore not possible to assume that * things will work out-of-the-box for other chips, and a careful review of the * vendor driver may be needed to expand support. The RTL8365MB-VC seems to be * one of the simpler chips.
*/
/* 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 * to block. On the other hand, accessing MIB counters absolutely requires us to * block. The solution is thus to schedule work which polls the MIB counters * asynchronously and updates some private data, which the callback can then * fetch atomically. Three seconds should be a good enough polling interval.
*/ #define RTL8365MB_STATS_INTERVAL_JIFFIES (3 * HZ)
/** * struct rtl8365mb_extint - external interface info * @port: the port with an external interface * @id: the external interface ID, which is either 0, 1, or 2 * @supported_interfaces: a bitmask of supported PHY interface modes * * Represents a mapping: port -> { id, supported_interfaces }. To be embedded * in &struct rtl8365mb_chip_info for every port with an external interface.
*/ struct rtl8365mb_extint { int port; int id; unsignedint supported_interfaces;
};
/** * struct rtl8365mb_chip_info - static chip-specific info * @name: human-readable chip name * @chip_id: chip identifier * @chip_ver: chip silicon revision * @extints: available external interfaces * @jam_table: chip-specific initialization jam table * @jam_size: size of the chip's jam table * * These data are specific to a given chip in the family of switches supported * by this driver. When adding support for another chip in the family, a new * chip info should be added to the rtl8365mb_chip_infos array.
*/ struct rtl8365mb_chip_info { constchar *name;
u32 chip_id;
u32 chip_ver; conststruct rtl8365mb_extint extints[RTL8365MB_MAX_NUM_EXTINTS]; conststruct rtl8365mb_jam_tbl_entry *jam_table;
size_t jam_size;
};
/** * struct rtl8365mb_cpu - CPU port configuration * @enable: enable/disable hardware insertion of CPU tag in switch->CPU frames * @mask: port mask of ports that parse should parse CPU tags * @trap_port: forward trapped frames to this port * @insert: CPU tag insertion mode in switch->CPU frames * @position: position of CPU tag in frame * @rx_length: minimum CPU RX length * @format: CPU tag format * * Represents the CPU tagging and CPU port configuration of the switch. These * settings are configurable at runtime.
*/ struct rtl8365mb_cpu { bool enable;
u32 mask;
u32 trap_port; enum rtl8365mb_cpu_insert insert; enum rtl8365mb_cpu_position position; enum rtl8365mb_cpu_rxlen rx_length; enum rtl8365mb_cpu_format format;
};
/** * struct rtl8365mb_port - private per-port data * @priv: pointer to parent realtek_priv data * @index: DSA port index, same as dsa_port::index * @stats: link statistics populated by rtl8365mb_stats_poll, ready for atomic * access via rtl8365mb_get_stats64 * @stats_lock: protect the stats structure during read/update * @mib_work: delayed work for polling MIB counters
*/ struct rtl8365mb_port { struct realtek_priv *priv; unsignedint index; struct rtnl_link_stats64 stats;
spinlock_t stats_lock; struct delayed_work mib_work;
};
/** * struct rtl8365mb - driver private data * @priv: pointer to parent realtek_priv data * @irq: registered IRQ or zero * @chip_info: chip-specific info about the attached switch * @cpu: CPU tagging and CPU port configuration for this chip * @mib_lock: prevent concurrent reads of MIB counters * @ports: per-port data * * Private data for this driver.
*/ struct rtl8365mb { struct realtek_priv *priv; int irq; conststruct rtl8365mb_chip_info *chip_info; struct rtl8365mb_cpu cpu; struct mutex mib_lock; struct rtl8365mb_port ports[RTL8365MB_MAX_NUM_PORTS];
};
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;
return 0;
}
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);
return 0;
}
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 (cpu->position == RTL8365MB_CPU_POS_BEFORE_CRC) return DSA_TAG_PROTO_RTL8_4T;
return DSA_TAG_PROTO_RTL8_4;
}
staticint rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port,
phy_interface_t interface)
{ conststruct rtl8365mb_extint *extint =
rtl8365mb_get_port_extint(priv, port); struct dsa_switch *ds = &priv->ds; struct device_node *dn; struct dsa_port *dp; int tx_delay = 0; int rx_delay = 0;
u32 val; int ret;
if (!extint) return -ENODEV;
dp = dsa_to_port(ds, port);
dn = dp->dn;
/* Set the RGMII TX/RX delay * * The Realtek vendor driver indicates the following possible * configuration settings: * * TX delay: * 0 = no delay, 1 = 2 ns delay * RX delay: * 0 = no delay, 7 = maximum delay * Each step is approximately 0.3 ns, so the maximum delay is about * 2.1 ns. * * The vendor driver also states that this must be configured *before* * forcing the external interface into a particular mode, which is done * in the rtl8365mb_phylink_mac_link_{up,down} functions. * * Only configure an RGMII TX (resp. RX) delay if the * tx-internal-delay-ps (resp. rx-internal-delay-ps) OF property is * specified. We ignore the detail of the RGMII interface mode * (RGMII_{RXID, TXID, etc.}), as this is considered to be a PHY-only * property.
*/ if (!of_property_read_u32(dn, "tx-internal-delay-ps", &val)) {
val = val / 1000; /* convert to ns */
if (val == 0 || val == 2)
tx_delay = val / 2; else
dev_warn(priv->dev, "RGMII TX delay must be 0 or 2 ns\n");
}
if (!of_property_read_u32(dn, "rx-internal-delay-ps", &val)) {
val = DIV_ROUND_CLOSEST(val, 300); /* convert to 0.3 ns step */
if (val <= 7)
rx_delay = val; else
dev_warn(priv->dev, "RGMII RX delay must be 0 to 2.1 ns\n");
}
ret = regmap_update_bits(
priv->map, RTL8365MB_EXT_RGMXF_REG(extint->id),
RTL8365MB_EXT_RGMXF_TXDELAY_MASK |
RTL8365MB_EXT_RGMXF_RXDELAY_MASK,
FIELD_PREP(RTL8365MB_EXT_RGMXF_TXDELAY_MASK, tx_delay) |
FIELD_PREP(RTL8365MB_EXT_RGMXF_RXDELAY_MASK, rx_delay)); if (ret) return ret;
ret = regmap_update_bits(
priv->map, RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(extint->id),
RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(extint->id),
RTL8365MB_EXT_PORT_MODE_RGMII
<< RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(
extint->id)); if (ret) return ret;
return 0;
}
staticint rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port, bool link, int speed, int duplex, bool tx_pause, bool rx_pause)
{ conststruct rtl8365mb_extint *extint =
rtl8365mb_get_port_extint(priv, port);
u32 r_tx_pause;
u32 r_rx_pause;
u32 r_duplex;
u32 r_speed;
u32 r_link; int val; int ret;
if (!extint) return -ENODEV;
if (link) { /* Force the link up with the desired configuration */
r_link = 1;
r_rx_pause = rx_pause ? 1 : 0;
r_tx_pause = tx_pause ? 1 : 0;
if (!extint) {
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces);
/* GMII is the default interface mode for phylib, so * we have to support it for ports with integrated PHY.
*/
__set_bit(PHY_INTERFACE_MODE_GMII,
config->supported_interfaces); return;
}
/* Populate according to the modes supported by _this driver_, * not necessarily the modes supported by the hardware, some of * which remain unimplemented.
*/
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 * largest MTU of the user ports. Because the switch only has a global * RX length register, only allowing CPU port here is enough.
*/ if (!dsa_is_cpu_port(ds, port)) return 0;
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 * port can learn. Realtek documentation states that a limit of zero * disables learning. When enabling learning, set it to the chip'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 * and then poll the control register before reading the value from some * counter registers.
*/
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 * MIB counter. Depending on the offset, we should read from the upper * two or lower two registers. In case the MIB counter is 4 words, we * read from all four registers.
*/ 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;
return 0;
}
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 * IEEE 802.3 Managed Objects. This is not always completely faithful, * but we try out best. See RFC 3635 for a detailed treatment of the * subject.
*/
/* Per-chip global mutex to protect MIB counter access, since doing * so requires accessing a series of registers in a particular order.
*/
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 * the same 0x04 8-bit version and probably 8-bit port source/dest. * There is no public doc about it. Not supported yet and it will probably * never be.
*/ 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;
}
/* Realtek documentation says the chip needs 1 second to reset. Sleep * for 100 ms before accessing any registers to prevent ACK timeouts.
*/
msleep(100); return regmap_read_poll_timeout(priv->map, RTL8365MB_CHIP_RESET_REG, val,
!(val & RTL8365MB_CHIP_RESET_HW_MASK),
20000, 1e6);
}
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 * ports will still forward frames to the CPU despite being * administratively down by default.
*/
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 * register whenever accessing the chip ID/version registers.
*/
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;
return 0;
}
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];
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.