162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* Realtek Simple Management Interface (SMI) driver 362306a36Sopenharmony_ci * It can be discussed how "simple" this interface is. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * The SMI protocol piggy-backs the MDIO MDC and MDIO signals levels 662306a36Sopenharmony_ci * but the protocol is not MDIO at all. Instead it is a Realtek 762306a36Sopenharmony_ci * pecularity that need to bit-bang the lines in a special way to 862306a36Sopenharmony_ci * communicate with the switch. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * ASICs we intend to support with this driver: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * RTL8366 - The original version, apparently 1362306a36Sopenharmony_ci * RTL8369 - Similar enough to have the same datsheet as RTL8366 1462306a36Sopenharmony_ci * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite 1562306a36Sopenharmony_ci * different register layout from the other two 1662306a36Sopenharmony_ci * RTL8366S - Is this "RTL8366 super"? 1762306a36Sopenharmony_ci * RTL8367 - Has an OpenWRT driver as well 1862306a36Sopenharmony_ci * RTL8368S - Seems to be an alternative name for RTL8366RB 1962306a36Sopenharmony_ci * RTL8370 - Also uses SMI 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> 2262306a36Sopenharmony_ci * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com> 2362306a36Sopenharmony_ci * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv> 2462306a36Sopenharmony_ci * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com> 2562306a36Sopenharmony_ci * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/kernel.h> 2962306a36Sopenharmony_ci#include <linux/module.h> 3062306a36Sopenharmony_ci#include <linux/device.h> 3162306a36Sopenharmony_ci#include <linux/spinlock.h> 3262306a36Sopenharmony_ci#include <linux/skbuff.h> 3362306a36Sopenharmony_ci#include <linux/of.h> 3462306a36Sopenharmony_ci#include <linux/of_mdio.h> 3562306a36Sopenharmony_ci#include <linux/delay.h> 3662306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 3762306a36Sopenharmony_ci#include <linux/platform_device.h> 3862306a36Sopenharmony_ci#include <linux/regmap.h> 3962306a36Sopenharmony_ci#include <linux/bitops.h> 4062306a36Sopenharmony_ci#include <linux/if_bridge.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include "realtek.h" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define REALTEK_SMI_ACK_RETRY_COUNT 5 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic inline void realtek_smi_clk_delay(struct realtek_priv *priv) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci ndelay(priv->clk_delay); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void realtek_smi_start(struct realtek_priv *priv) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci /* Set GPIO pins to output mode, with initial state: 5462306a36Sopenharmony_ci * SCK = 0, SDA = 1 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci gpiod_direction_output(priv->mdc, 0); 5762306a36Sopenharmony_ci gpiod_direction_output(priv->mdio, 1); 5862306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* CLK 1: 0 -> 1, 1 -> 0 */ 6162306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 1); 6262306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 6362306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 0); 6462306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* CLK 2: */ 6762306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 1); 6862306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 6962306a36Sopenharmony_ci gpiod_set_value(priv->mdio, 0); 7062306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 7162306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 0); 7262306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 7362306a36Sopenharmony_ci gpiod_set_value(priv->mdio, 1); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void realtek_smi_stop(struct realtek_priv *priv) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 7962306a36Sopenharmony_ci gpiod_set_value(priv->mdio, 0); 8062306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 1); 8162306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 8262306a36Sopenharmony_ci gpiod_set_value(priv->mdio, 1); 8362306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 8462306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 1); 8562306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 8662306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 0); 8762306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 8862306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 1); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Add a click */ 9162306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 9262306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 0); 9362306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 9462306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 1); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* Set GPIO pins to input mode */ 9762306a36Sopenharmony_ci gpiod_direction_input(priv->mdio); 9862306a36Sopenharmony_ci gpiod_direction_input(priv->mdc); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic void realtek_smi_write_bits(struct realtek_priv *priv, u32 data, u32 len) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci for (; len > 0; len--) { 10462306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* Prepare data */ 10762306a36Sopenharmony_ci gpiod_set_value(priv->mdio, !!(data & (1 << (len - 1)))); 10862306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* Clocking */ 11162306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 1); 11262306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 11362306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 0); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void realtek_smi_read_bits(struct realtek_priv *priv, u32 len, u32 *data) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci gpiod_direction_input(priv->mdio); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci for (*data = 0; len > 0; len--) { 12262306a36Sopenharmony_ci u32 u; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Clocking */ 12762306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 1); 12862306a36Sopenharmony_ci realtek_smi_clk_delay(priv); 12962306a36Sopenharmony_ci u = !!gpiod_get_value(priv->mdio); 13062306a36Sopenharmony_ci gpiod_set_value(priv->mdc, 0); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci *data |= (u << (len - 1)); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci gpiod_direction_output(priv->mdio, 0); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int realtek_smi_wait_for_ack(struct realtek_priv *priv) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci int retry_cnt; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci retry_cnt = 0; 14362306a36Sopenharmony_ci do { 14462306a36Sopenharmony_ci u32 ack; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci realtek_smi_read_bits(priv, 1, &ack); 14762306a36Sopenharmony_ci if (ack == 0) 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (++retry_cnt > REALTEK_SMI_ACK_RETRY_COUNT) { 15162306a36Sopenharmony_ci dev_err(priv->dev, "ACK timeout\n"); 15262306a36Sopenharmony_ci return -ETIMEDOUT; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci } while (1); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int realtek_smi_write_byte(struct realtek_priv *priv, u8 data) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci realtek_smi_write_bits(priv, data, 8); 16262306a36Sopenharmony_ci return realtek_smi_wait_for_ack(priv); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int realtek_smi_write_byte_noack(struct realtek_priv *priv, u8 data) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci realtek_smi_write_bits(priv, data, 8); 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int realtek_smi_read_byte0(struct realtek_priv *priv, u8 *data) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci u32 t; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Read data */ 17662306a36Sopenharmony_ci realtek_smi_read_bits(priv, 8, &t); 17762306a36Sopenharmony_ci *data = (t & 0xff); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Send an ACK */ 18062306a36Sopenharmony_ci realtek_smi_write_bits(priv, 0x00, 1); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int realtek_smi_read_byte1(struct realtek_priv *priv, u8 *data) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci u32 t; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Read data */ 19062306a36Sopenharmony_ci realtek_smi_read_bits(priv, 8, &t); 19162306a36Sopenharmony_ci *data = (t & 0xff); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Send an ACK */ 19462306a36Sopenharmony_ci realtek_smi_write_bits(priv, 0x01, 1); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int realtek_smi_read_reg(struct realtek_priv *priv, u32 addr, u32 *data) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci unsigned long flags; 20262306a36Sopenharmony_ci u8 lo = 0; 20362306a36Sopenharmony_ci u8 hi = 0; 20462306a36Sopenharmony_ci int ret; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci realtek_smi_start(priv); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Send READ command */ 21162306a36Sopenharmony_ci ret = realtek_smi_write_byte(priv, priv->cmd_read); 21262306a36Sopenharmony_ci if (ret) 21362306a36Sopenharmony_ci goto out; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Set ADDR[7:0] */ 21662306a36Sopenharmony_ci ret = realtek_smi_write_byte(priv, addr & 0xff); 21762306a36Sopenharmony_ci if (ret) 21862306a36Sopenharmony_ci goto out; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Set ADDR[15:8] */ 22162306a36Sopenharmony_ci ret = realtek_smi_write_byte(priv, addr >> 8); 22262306a36Sopenharmony_ci if (ret) 22362306a36Sopenharmony_ci goto out; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* Read DATA[7:0] */ 22662306a36Sopenharmony_ci realtek_smi_read_byte0(priv, &lo); 22762306a36Sopenharmony_ci /* Read DATA[15:8] */ 22862306a36Sopenharmony_ci realtek_smi_read_byte1(priv, &hi); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci *data = ((u32)lo) | (((u32)hi) << 8); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ret = 0; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci out: 23562306a36Sopenharmony_ci realtek_smi_stop(priv); 23662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return ret; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int realtek_smi_write_reg(struct realtek_priv *priv, 24262306a36Sopenharmony_ci u32 addr, u32 data, bool ack) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci unsigned long flags; 24562306a36Sopenharmony_ci int ret; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci realtek_smi_start(priv); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Send WRITE command */ 25262306a36Sopenharmony_ci ret = realtek_smi_write_byte(priv, priv->cmd_write); 25362306a36Sopenharmony_ci if (ret) 25462306a36Sopenharmony_ci goto out; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* Set ADDR[7:0] */ 25762306a36Sopenharmony_ci ret = realtek_smi_write_byte(priv, addr & 0xff); 25862306a36Sopenharmony_ci if (ret) 25962306a36Sopenharmony_ci goto out; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* Set ADDR[15:8] */ 26262306a36Sopenharmony_ci ret = realtek_smi_write_byte(priv, addr >> 8); 26362306a36Sopenharmony_ci if (ret) 26462306a36Sopenharmony_ci goto out; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Write DATA[7:0] */ 26762306a36Sopenharmony_ci ret = realtek_smi_write_byte(priv, data & 0xff); 26862306a36Sopenharmony_ci if (ret) 26962306a36Sopenharmony_ci goto out; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Write DATA[15:8] */ 27262306a36Sopenharmony_ci if (ack) 27362306a36Sopenharmony_ci ret = realtek_smi_write_byte(priv, data >> 8); 27462306a36Sopenharmony_ci else 27562306a36Sopenharmony_ci ret = realtek_smi_write_byte_noack(priv, data >> 8); 27662306a36Sopenharmony_ci if (ret) 27762306a36Sopenharmony_ci goto out; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ret = 0; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci out: 28262306a36Sopenharmony_ci realtek_smi_stop(priv); 28362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return ret; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* There is one single case when we need to use this accessor and that 28962306a36Sopenharmony_ci * is when issueing soft reset. Since the device reset as soon as we write 29062306a36Sopenharmony_ci * that bit, no ACK will come back for natural reasons. 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_cistatic int realtek_smi_write_reg_noack(void *ctx, u32 reg, u32 val) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci return realtek_smi_write_reg(ctx, reg, val, false); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* Regmap accessors */ 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int realtek_smi_write(void *ctx, u32 reg, u32 val) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct realtek_priv *priv = ctx; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return realtek_smi_write_reg(priv, reg, val, true); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int realtek_smi_read(void *ctx, u32 reg, u32 *val) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct realtek_priv *priv = ctx; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci return realtek_smi_read_reg(priv, reg, val); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic void realtek_smi_lock(void *ctx) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct realtek_priv *priv = ctx; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci mutex_lock(&priv->map_lock); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void realtek_smi_unlock(void *ctx) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct realtek_priv *priv = ctx; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci mutex_unlock(&priv->map_lock); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic const struct regmap_config realtek_smi_regmap_config = { 32862306a36Sopenharmony_ci .reg_bits = 10, /* A4..A0 R4..R0 */ 32962306a36Sopenharmony_ci .val_bits = 16, 33062306a36Sopenharmony_ci .reg_stride = 1, 33162306a36Sopenharmony_ci /* PHY regs are at 0x8000 */ 33262306a36Sopenharmony_ci .max_register = 0xffff, 33362306a36Sopenharmony_ci .reg_format_endian = REGMAP_ENDIAN_BIG, 33462306a36Sopenharmony_ci .reg_read = realtek_smi_read, 33562306a36Sopenharmony_ci .reg_write = realtek_smi_write, 33662306a36Sopenharmony_ci .cache_type = REGCACHE_NONE, 33762306a36Sopenharmony_ci .lock = realtek_smi_lock, 33862306a36Sopenharmony_ci .unlock = realtek_smi_unlock, 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic const struct regmap_config realtek_smi_nolock_regmap_config = { 34262306a36Sopenharmony_ci .reg_bits = 10, /* A4..A0 R4..R0 */ 34362306a36Sopenharmony_ci .val_bits = 16, 34462306a36Sopenharmony_ci .reg_stride = 1, 34562306a36Sopenharmony_ci /* PHY regs are at 0x8000 */ 34662306a36Sopenharmony_ci .max_register = 0xffff, 34762306a36Sopenharmony_ci .reg_format_endian = REGMAP_ENDIAN_BIG, 34862306a36Sopenharmony_ci .reg_read = realtek_smi_read, 34962306a36Sopenharmony_ci .reg_write = realtek_smi_write, 35062306a36Sopenharmony_ci .cache_type = REGCACHE_NONE, 35162306a36Sopenharmony_ci .disable_locking = true, 35262306a36Sopenharmony_ci}; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct realtek_priv *priv = bus->priv; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return priv->ops->phy_read(priv, addr, regnum); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum, 36262306a36Sopenharmony_ci u16 val) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct realtek_priv *priv = bus->priv; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return priv->ops->phy_write(priv, addr, regnum, val); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int realtek_smi_setup_mdio(struct dsa_switch *ds) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct realtek_priv *priv = ds->priv; 37262306a36Sopenharmony_ci struct device_node *mdio_np; 37362306a36Sopenharmony_ci int ret; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci mdio_np = of_get_compatible_child(priv->dev->of_node, "realtek,smi-mdio"); 37662306a36Sopenharmony_ci if (!mdio_np) { 37762306a36Sopenharmony_ci dev_err(priv->dev, "no MDIO bus node\n"); 37862306a36Sopenharmony_ci return -ENODEV; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci priv->slave_mii_bus = devm_mdiobus_alloc(priv->dev); 38262306a36Sopenharmony_ci if (!priv->slave_mii_bus) { 38362306a36Sopenharmony_ci ret = -ENOMEM; 38462306a36Sopenharmony_ci goto err_put_node; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci priv->slave_mii_bus->priv = priv; 38762306a36Sopenharmony_ci priv->slave_mii_bus->name = "SMI slave MII"; 38862306a36Sopenharmony_ci priv->slave_mii_bus->read = realtek_smi_mdio_read; 38962306a36Sopenharmony_ci priv->slave_mii_bus->write = realtek_smi_mdio_write; 39062306a36Sopenharmony_ci snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d", 39162306a36Sopenharmony_ci ds->index); 39262306a36Sopenharmony_ci priv->slave_mii_bus->dev.of_node = mdio_np; 39362306a36Sopenharmony_ci priv->slave_mii_bus->parent = priv->dev; 39462306a36Sopenharmony_ci ds->slave_mii_bus = priv->slave_mii_bus; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ret = devm_of_mdiobus_register(priv->dev, priv->slave_mii_bus, mdio_np); 39762306a36Sopenharmony_ci if (ret) { 39862306a36Sopenharmony_ci dev_err(priv->dev, "unable to register MDIO bus %s\n", 39962306a36Sopenharmony_ci priv->slave_mii_bus->id); 40062306a36Sopenharmony_ci goto err_put_node; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cierr_put_node: 40662306a36Sopenharmony_ci of_node_put(mdio_np); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return ret; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int realtek_smi_probe(struct platform_device *pdev) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci const struct realtek_variant *var; 41462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 41562306a36Sopenharmony_ci struct realtek_priv *priv; 41662306a36Sopenharmony_ci struct regmap_config rc; 41762306a36Sopenharmony_ci struct device_node *np; 41862306a36Sopenharmony_ci int ret; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci var = of_device_get_match_data(dev); 42162306a36Sopenharmony_ci np = dev->of_node; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv) + var->chip_data_sz, GFP_KERNEL); 42462306a36Sopenharmony_ci if (!priv) 42562306a36Sopenharmony_ci return -ENOMEM; 42662306a36Sopenharmony_ci priv->chip_data = (void *)priv + sizeof(*priv); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci mutex_init(&priv->map_lock); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci rc = realtek_smi_regmap_config; 43162306a36Sopenharmony_ci rc.lock_arg = priv; 43262306a36Sopenharmony_ci priv->map = devm_regmap_init(dev, NULL, priv, &rc); 43362306a36Sopenharmony_ci if (IS_ERR(priv->map)) { 43462306a36Sopenharmony_ci ret = PTR_ERR(priv->map); 43562306a36Sopenharmony_ci dev_err(dev, "regmap init failed: %d\n", ret); 43662306a36Sopenharmony_ci return ret; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci rc = realtek_smi_nolock_regmap_config; 44062306a36Sopenharmony_ci priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); 44162306a36Sopenharmony_ci if (IS_ERR(priv->map_nolock)) { 44262306a36Sopenharmony_ci ret = PTR_ERR(priv->map_nolock); 44362306a36Sopenharmony_ci dev_err(dev, "regmap init failed: %d\n", ret); 44462306a36Sopenharmony_ci return ret; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* Link forward and backward */ 44862306a36Sopenharmony_ci priv->dev = dev; 44962306a36Sopenharmony_ci priv->clk_delay = var->clk_delay; 45062306a36Sopenharmony_ci priv->cmd_read = var->cmd_read; 45162306a36Sopenharmony_ci priv->cmd_write = var->cmd_write; 45262306a36Sopenharmony_ci priv->ops = var->ops; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci priv->setup_interface = realtek_smi_setup_mdio; 45562306a36Sopenharmony_ci priv->write_reg_noack = realtek_smi_write_reg_noack; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci dev_set_drvdata(dev, priv); 45862306a36Sopenharmony_ci spin_lock_init(&priv->lock); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* TODO: if power is software controlled, set up any regulators here */ 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 46362306a36Sopenharmony_ci if (IS_ERR(priv->reset)) { 46462306a36Sopenharmony_ci dev_err(dev, "failed to get RESET GPIO\n"); 46562306a36Sopenharmony_ci return PTR_ERR(priv->reset); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci if (priv->reset) { 46862306a36Sopenharmony_ci gpiod_set_value(priv->reset, 1); 46962306a36Sopenharmony_ci dev_dbg(dev, "asserted RESET\n"); 47062306a36Sopenharmony_ci msleep(REALTEK_HW_STOP_DELAY); 47162306a36Sopenharmony_ci gpiod_set_value(priv->reset, 0); 47262306a36Sopenharmony_ci msleep(REALTEK_HW_START_DELAY); 47362306a36Sopenharmony_ci dev_dbg(dev, "deasserted RESET\n"); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* Fetch MDIO pins */ 47762306a36Sopenharmony_ci priv->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW); 47862306a36Sopenharmony_ci if (IS_ERR(priv->mdc)) 47962306a36Sopenharmony_ci return PTR_ERR(priv->mdc); 48062306a36Sopenharmony_ci priv->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW); 48162306a36Sopenharmony_ci if (IS_ERR(priv->mdio)) 48262306a36Sopenharmony_ci return PTR_ERR(priv->mdio); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci ret = priv->ops->detect(priv); 48762306a36Sopenharmony_ci if (ret) { 48862306a36Sopenharmony_ci dev_err(dev, "unable to detect switch\n"); 48962306a36Sopenharmony_ci return ret; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); 49362306a36Sopenharmony_ci if (!priv->ds) 49462306a36Sopenharmony_ci return -ENOMEM; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci priv->ds->dev = dev; 49762306a36Sopenharmony_ci priv->ds->num_ports = priv->num_ports; 49862306a36Sopenharmony_ci priv->ds->priv = priv; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci priv->ds->ops = var->ds_ops_smi; 50162306a36Sopenharmony_ci ret = dsa_register_switch(priv->ds); 50262306a36Sopenharmony_ci if (ret) { 50362306a36Sopenharmony_ci dev_err_probe(dev, ret, "unable to register switch\n"); 50462306a36Sopenharmony_ci return ret; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int realtek_smi_remove(struct platform_device *pdev) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct realtek_priv *priv = platform_get_drvdata(pdev); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (!priv) 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci dsa_unregister_switch(priv->ds); 51762306a36Sopenharmony_ci if (priv->slave_mii_bus) 51862306a36Sopenharmony_ci of_node_put(priv->slave_mii_bus->dev.of_node); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* leave the device reset asserted */ 52162306a36Sopenharmony_ci if (priv->reset) 52262306a36Sopenharmony_ci gpiod_set_value(priv->reset, 1); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic void realtek_smi_shutdown(struct platform_device *pdev) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct realtek_priv *priv = platform_get_drvdata(pdev); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (!priv) 53262306a36Sopenharmony_ci return; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci dsa_switch_shutdown(priv->ds); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci platform_set_drvdata(pdev, NULL); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic const struct of_device_id realtek_smi_of_match[] = { 54062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) 54162306a36Sopenharmony_ci { 54262306a36Sopenharmony_ci .compatible = "realtek,rtl8366rb", 54362306a36Sopenharmony_ci .data = &rtl8366rb_variant, 54462306a36Sopenharmony_ci }, 54562306a36Sopenharmony_ci#endif 54662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) 54762306a36Sopenharmony_ci { 54862306a36Sopenharmony_ci .compatible = "realtek,rtl8365mb", 54962306a36Sopenharmony_ci .data = &rtl8365mb_variant, 55062306a36Sopenharmony_ci }, 55162306a36Sopenharmony_ci#endif 55262306a36Sopenharmony_ci { /* sentinel */ }, 55362306a36Sopenharmony_ci}; 55462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, realtek_smi_of_match); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic struct platform_driver realtek_smi_driver = { 55762306a36Sopenharmony_ci .driver = { 55862306a36Sopenharmony_ci .name = "realtek-smi", 55962306a36Sopenharmony_ci .of_match_table = realtek_smi_of_match, 56062306a36Sopenharmony_ci }, 56162306a36Sopenharmony_ci .probe = realtek_smi_probe, 56262306a36Sopenharmony_ci .remove = realtek_smi_remove, 56362306a36Sopenharmony_ci .shutdown = realtek_smi_shutdown, 56462306a36Sopenharmony_ci}; 56562306a36Sopenharmony_cimodule_platform_driver(realtek_smi_driver); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 56862306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); 56962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 570