+ return container_of(led_cdev, struct ar8327_led, cdev);
+}
+
+static int
+ar8327_led_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+
+ if (*delay_on == 0 && *delay_off == 0) {
+ *delay_on = 125;
+ *delay_off = 125;
+ }
+
+ if (*delay_on != 125 || *delay_off != 125) {
+ /*
+ * The hardware only supports blinking at 4Hz. Fall back
+ * to software implementation in other cases.
+ */
+ return -EINVAL;
+ }
+
+ spin_lock(&aled->lock);
+
+ aled->enable_hw_mode = false;
+ ar8327_led_schedule_change(aled, AR8327_LED_PATTERN_BLINK);
+
+ spin_unlock(&aled->lock);
+
+ return 0;
+}
+
+static void
+ar8327_led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+ u8 pattern;
+ bool active;
+
+ active = (brightness != LED_OFF);
+ active ^= aled->active_low;
+
+ pattern = (active) ? AR8327_LED_PATTERN_ON :
+ AR8327_LED_PATTERN_OFF;
+
+ spin_lock(&aled->lock);
+
+ aled->enable_hw_mode = false;
+ ar8327_led_schedule_change(aled, pattern);
+
+ spin_unlock(&aled->lock);
+}
+
+static ssize_t
+ar8327_led_enable_hw_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+ ssize_t ret = 0;
+
+ spin_lock(&aled->lock);
+ ret += sprintf(buf, "%d\n", aled->enable_hw_mode);
+ spin_unlock(&aled->lock);
+
+ return ret;
+}
+
+static ssize_t
+ar8327_led_enable_hw_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+ u8 pattern;
+ u8 value;
+ int ret;
+
+ ret = kstrtou8(buf, 10, &value);
+ if (ret < 0)
+ return -EINVAL;
+
+ spin_lock(&aled->lock);
+
+ aled->enable_hw_mode = !!value;
+ if (aled->enable_hw_mode)
+ pattern = AR8327_LED_PATTERN_RULE;
+ else
+ pattern = AR8327_LED_PATTERN_OFF;
+
+ ar8327_led_schedule_change(aled, pattern);
+
+ spin_unlock(&aled->lock);
+
+ return size;
+}
+
+static DEVICE_ATTR(enable_hw_mode, S_IRUGO | S_IWUSR,
+ ar8327_led_enable_hw_mode_show,
+ ar8327_led_enable_hw_mode_store);
+
+static int
+ar8327_led_register(struct ar8xxx_priv *priv, struct ar8327_led *aled)
+{
+ int ret;
+
+ ret = led_classdev_register(NULL, &aled->cdev);
+ if (ret < 0)
+ return ret;
+
+ if (aled->mode == AR8327_LED_MODE_HW) {
+ ret = device_create_file(aled->cdev.dev,
+ &dev_attr_enable_hw_mode);
+ if (ret)
+ goto err_unregister;
+ }
+
+ return 0;
+
+err_unregister:
+ led_classdev_unregister(&aled->cdev);
+ return ret;
+}
+
+static void
+ar8327_led_unregister(struct ar8327_led *aled)
+{
+ if (aled->mode == AR8327_LED_MODE_HW)
+ device_remove_file(aled->cdev.dev, &dev_attr_enable_hw_mode);
+
+ led_classdev_unregister(&aled->cdev);
+ cancel_work_sync(&aled->led_work);
+}
+
+static int
+ar8327_led_create(struct ar8xxx_priv *priv,
+ const struct ar8327_led_info *led_info)
+{
+ struct ar8327_data *data = &priv->chip_data.ar8327;
+ struct ar8327_led *aled;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
+ return 0;
+
+ if (!led_info->name)
+ return -EINVAL;
+
+ if (led_info->led_num >= AR8327_NUM_LEDS)
+ return -EINVAL;
+
+ aled = kzalloc(sizeof(*aled) + strlen(led_info->name) + 1,
+ GFP_KERNEL);
+ if (!aled)
+ return -ENOMEM;
+
+ aled->sw_priv = priv;
+ aled->led_num = led_info->led_num;
+ aled->active_low = led_info->active_low;
+ aled->mode = led_info->mode;
+
+ if (aled->mode == AR8327_LED_MODE_HW)
+ aled->enable_hw_mode = true;
+
+ aled->name = (char *)(aled + 1);
+ strcpy(aled->name, led_info->name);
+
+ aled->cdev.name = aled->name;
+ aled->cdev.brightness_set = ar8327_led_set_brightness;
+ aled->cdev.blink_set = ar8327_led_blink_set;
+ aled->cdev.default_trigger = led_info->default_trigger;
+
+ spin_lock_init(&aled->lock);
+ mutex_init(&aled->mutex);
+ INIT_WORK(&aled->led_work, ar8327_led_work_func);
+
+ ret = ar8327_led_register(priv, aled);
+ if (ret)
+ goto err_free;
+
+ data->leds[data->num_leds++] = aled;
+
+ return 0;
+
+err_free:
+ kfree(aled);
+ return ret;
+}
+
+static void
+ar8327_led_destroy(struct ar8327_led *aled)
+{
+ ar8327_led_unregister(aled);
+ kfree(aled);
+}
+
+static void
+ar8327_leds_init(struct ar8xxx_priv *priv)
+{
+ struct ar8327_data *data;
+ unsigned i;
+
+ if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
+ return;
+
+ data = &priv->chip_data.ar8327;
+
+ for (i = 0; i < data->num_leds; i++) {
+ struct ar8327_led *aled;
+
+ aled = data->leds[i];
+
+ if (aled->enable_hw_mode)
+ aled->pattern = AR8327_LED_PATTERN_RULE;
+ else
+ aled->pattern = AR8327_LED_PATTERN_OFF;
+
+ ar8327_set_led_pattern(priv, aled->led_num, aled->pattern);
+ }
+}
+
+static void
+ar8327_leds_cleanup(struct ar8xxx_priv *priv)
+{
+ struct ar8327_data *data = &priv->chip_data.ar8327;
+ unsigned i;
+
+ if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
+ return;
+
+ for (i = 0; i < data->num_leds; i++) {
+ struct ar8327_led *aled;
+
+ aled = data->leds[i];
+ ar8327_led_destroy(aled);
+ }
+
+ kfree(data->leds);
+}
+
+static int
+ar8327_hw_config_pdata(struct ar8xxx_priv *priv,
+ struct ar8327_platform_data *pdata)
+{
+ struct ar8327_led_cfg *led_cfg;
+ struct ar8327_data *data;
+ u32 pos, new_pos;
+ u32 t;
+
+ if (!pdata)
+ return -EINVAL;
+
+ priv->get_port_link = pdata->get_port_link;
+
+ data = &priv->chip_data.ar8327;
+
+ data->port0_status = ar8327_get_port_init_status(&pdata->port0_cfg);
+ data->port6_status = ar8327_get_port_init_status(&pdata->port6_cfg);
+
+ t = ar8327_get_pad_cfg(pdata->pad0_cfg);
+ if (chip_is_ar8337(priv))
+ t |= AR8337_PAD_MAC06_EXCHANGE_EN;
+
+ priv->write(priv, AR8327_REG_PAD0_MODE, t);
+ t = ar8327_get_pad_cfg(pdata->pad5_cfg);
+ priv->write(priv, AR8327_REG_PAD5_MODE, t);
+ t = ar8327_get_pad_cfg(pdata->pad6_cfg);
+ priv->write(priv, AR8327_REG_PAD6_MODE, t);
+
+ pos = priv->read(priv, AR8327_REG_POWER_ON_STRIP);
+ new_pos = pos;
+
+ led_cfg = pdata->led_cfg;
+ if (led_cfg) {
+ if (led_cfg->open_drain)
+ new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN;
+ else
+ new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN;
+
+ priv->write(priv, AR8327_REG_LED_CTRL0, led_cfg->led_ctrl0);
+ priv->write(priv, AR8327_REG_LED_CTRL1, led_cfg->led_ctrl1);
+ priv->write(priv, AR8327_REG_LED_CTRL2, led_cfg->led_ctrl2);
+ priv->write(priv, AR8327_REG_LED_CTRL3, led_cfg->led_ctrl3);
+
+ if (new_pos != pos)
+ new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL;
+ }
+
+ if (pdata->sgmii_cfg) {
+ t = pdata->sgmii_cfg->sgmii_ctrl;
+ if (priv->chip_rev == 1)
+ t |= AR8327_SGMII_CTRL_EN_PLL |
+ AR8327_SGMII_CTRL_EN_RX |
+ AR8327_SGMII_CTRL_EN_TX;
+ else
+ t &= ~(AR8327_SGMII_CTRL_EN_PLL |
+ AR8327_SGMII_CTRL_EN_RX |
+ AR8327_SGMII_CTRL_EN_TX);
+
+ priv->write(priv, AR8327_REG_SGMII_CTRL, t);
+
+ if (pdata->sgmii_cfg->serdes_aen)
+ new_pos &= ~AR8327_POWER_ON_STRIP_SERDES_AEN;
+ else
+ new_pos |= AR8327_POWER_ON_STRIP_SERDES_AEN;
+ }
+
+ priv->write(priv, AR8327_REG_POWER_ON_STRIP, new_pos);
+
+ if (pdata->leds && pdata->num_leds) {
+ int i;
+
+ data->leds = kzalloc(pdata->num_leds * sizeof(void *),
+ GFP_KERNEL);
+ if (!data->leds)
+ return -ENOMEM;
+
+ for (i = 0; i < pdata->num_leds; i++)
+ ar8327_led_create(priv, &pdata->leds[i]);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static int
+ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
+{
+ const __be32 *paddr;
+ int len;
+ int i;
+
+ paddr = of_get_property(np, "qca,ar8327-initvals", &len);
+ if (!paddr || len < (2 * sizeof(*paddr)))
+ return -EINVAL;
+
+ len /= sizeof(*paddr);
+
+ for (i = 0; i < len - 1; i += 2) {
+ u32 reg;
+ u32 val;
+
+ reg = be32_to_cpup(paddr + i);
+ val = be32_to_cpup(paddr + i + 1);
+
+ switch (reg) {
+ case AR8327_REG_PORT_STATUS(0):
+ priv->chip_data.ar8327.port0_status = val;
+ break;
+ case AR8327_REG_PORT_STATUS(6):
+ priv->chip_data.ar8327.port6_status = val;
+ break;
+ default:
+ priv->write(priv, reg, val);
+ break;
+ }
+ }
+
+ return 0;
+}
+#else
+static inline int
+ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
+{
+ return -EINVAL;
+}
+#endif
+
+static int
+ar8327_hw_init(struct ar8xxx_priv *priv)
+{
+ struct mii_bus *bus;
+ int ret;
+ int i;
+
+ if (priv->phy->dev.of_node)
+ ret = ar8327_hw_config_of(priv, priv->phy->dev.of_node);
+ else
+ ret = ar8327_hw_config_pdata(priv,
+ priv->phy->dev.platform_data);
+
+ if (ret)
+ return ret;
+
+ ar8327_leds_init(priv);
+
+ bus = priv->mii_bus;
+ for (i = 0; i < AR8327_NUM_PHYS; i++) {
+ ar8327_phy_fixup(priv, i);
+
+ /* start aneg on the PHY */
+ mdiobus_write(bus, i, MII_ADVERTISE, ADVERTISE_ALL |
+ ADVERTISE_PAUSE_CAP |
+ ADVERTISE_PAUSE_ASYM);
+ mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL);
+ mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
+ }
+
+ msleep(1000);
+
+ return 0;
+}
+
+static void
+ar8327_cleanup(struct ar8xxx_priv *priv)
+{
+ ar8327_leds_cleanup(priv);
+}
+
+static void
+ar8327_init_globals(struct ar8xxx_priv *priv)
+{
+ u32 t;
+
+ /* enable CPU port and disable mirror port */
+ t = AR8327_FWD_CTRL0_CPU_PORT_EN |
+ AR8327_FWD_CTRL0_MIRROR_PORT;
+ priv->write(priv, AR8327_REG_FWD_CTRL0, t);
+
+ /* forward multicast and broadcast frames to CPU */
+ t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) |
+ (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) |
+ (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S);
+ priv->write(priv, AR8327_REG_FWD_CTRL1, t);
+
+ /* enable jumbo frames */
+ ar8xxx_rmw(priv, AR8327_REG_MAX_FRAME_SIZE,
+ AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2);
+
+ /* Enable MIB counters */
+ ar8xxx_reg_set(priv, AR8327_REG_MODULE_EN,
+ AR8327_MODULE_EN_MIB);
+
+ /* Disable EEE on all ports due to stability issues */
+ t = priv->read(priv, AR8327_REG_EEE_CTRL);
+ t |= AR8327_EEE_CTRL_DISABLE_PHY(0) |
+ AR8327_EEE_CTRL_DISABLE_PHY(1) |
+ AR8327_EEE_CTRL_DISABLE_PHY(2) |
+ AR8327_EEE_CTRL_DISABLE_PHY(3) |
+ AR8327_EEE_CTRL_DISABLE_PHY(4);
+ priv->write(priv, AR8327_REG_EEE_CTRL, t);
+}
+
+static void
+ar8327_init_port(struct ar8xxx_priv *priv, int port)
+{
+ u32 t;
+
+ if (port == AR8216_PORT_CPU)
+ t = priv->chip_data.ar8327.port0_status;
+ else if (port == 6)
+ t = priv->chip_data.ar8327.port6_status;
+ else
+ t = AR8216_PORT_STATUS_LINK_AUTO;
+
+ priv->write(priv, AR8327_REG_PORT_STATUS(port), t);
+ priv->write(priv, AR8327_REG_PORT_HEADER(port), 0);
+
+ t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S;
+ t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S;
+ priv->write(priv, AR8327_REG_PORT_VLAN0(port), t);
+
+ t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S;
+ priv->write(priv, AR8327_REG_PORT_VLAN1(port), t);
+
+ t = AR8327_PORT_LOOKUP_LEARN;
+ t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
+ priv->write(priv, AR8327_REG_PORT_LOOKUP(port), t);
+}
+
+static u32
+ar8327_read_port_status(struct ar8xxx_priv *priv, int port)
+{
+ return priv->read(priv, AR8327_REG_PORT_STATUS(port));
+}
+
+static int
+ar8327_atu_flush(struct ar8xxx_priv *priv)
+{
+ int ret;
+
+ ret = ar8216_wait_bit(priv, AR8327_REG_ATU_FUNC,
+ AR8327_ATU_FUNC_BUSY, 0);
+ if (!ret)
+ priv->write(priv, AR8327_REG_ATU_FUNC,
+ AR8327_ATU_FUNC_OP_FLUSH);
+
+ return ret;