diff options
Diffstat (limited to 'target/mips/mikrotik-rb4xx/patches/3.14.8')
26 files changed, 0 insertions, 14902 deletions
diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0001-mtd-add-rb4xx-nand-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0001-mtd-add-rb4xx-nand-driver.patch deleted file mode 100644 index 8199de991..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0001-mtd-add-rb4xx-nand-driver.patch +++ /dev/null @@ -1,351 +0,0 @@ -From 1e692cc0c53202b932eedabd0315107910c5b093 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:08:54 +0200 -Subject: [PATCH] mtd: add rb4xx nand driver - ---- - drivers/mtd/nand/Kconfig | 4 + - drivers/mtd/nand/Makefile | 1 + - drivers/mtd/nand/rb4xx_nand.c | 305 ++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 310 insertions(+) - create mode 100644 drivers/mtd/nand/rb4xx_nand.c - -diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig -index 90ff447..bb01309 100644 ---- a/drivers/mtd/nand/Kconfig -+++ b/drivers/mtd/nand/Kconfig -@@ -510,4 +510,8 @@ config MTD_NAND_XWAY - Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached - to the External Bus Unit (EBU). - -+config MTD_NAND_RB4XX -+ tristate "NAND flash driver for RouterBoard 4xx series" -+ depends on MTD_NAND && ATH79_MACH_RB4XX -+ - endif # MTD_NAND -diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile -index 542b568..e2b5e1c 100644 ---- a/drivers/mtd/nand/Makefile -+++ b/drivers/mtd/nand/Makefile -@@ -31,6 +31,7 @@ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o - obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o - obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o - obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o -+obj-$(CONFIG_MTD_NAND_RB4XX) += rb4xx_nand.o - obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o - obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o - obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o -diff --git a/drivers/mtd/nand/rb4xx_nand.c b/drivers/mtd/nand/rb4xx_nand.c -new file mode 100644 -index 0000000..5b9841b ---- /dev/null -+++ b/drivers/mtd/nand/rb4xx_nand.c -@@ -0,0 +1,305 @@ -+/* -+ * NAND flash driver for the MikroTik RouterBoard 4xx series -+ * -+ * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> -+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> -+ * -+ * This file was based on the driver for Linux 2.6.22 published by -+ * MikroTik for their RouterBoard 4xx series devices. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/mtd/nand.h> -+#include <linux/mtd/mtd.h> -+#include <linux/mtd/partitions.h> -+#include <linux/platform_device.h> -+#include <linux/delay.h> -+#include <linux/io.h> -+#include <linux/gpio.h> -+#include <linux/slab.h> -+ -+#include <asm/mach-ath79/ath79.h> -+#include <asm/mach-ath79/rb4xx_cpld.h> -+ -+#define DRV_NAME "rb4xx-nand" -+#define DRV_VERSION "0.2.0" -+#define DRV_DESC "NAND flash driver for RouterBoard 4xx series" -+ -+#define RB4XX_NAND_GPIO_READY 5 -+#define RB4XX_NAND_GPIO_ALE 37 -+#define RB4XX_NAND_GPIO_CLE 38 -+#define RB4XX_NAND_GPIO_NCE 39 -+ -+struct rb4xx_nand_info { -+ struct nand_chip chip; -+ struct mtd_info mtd; -+}; -+ -+/* -+ * We need to use the OLD Yaffs-1 OOB layout, otherwise the RB bootloader -+ * will not be able to find the kernel that we load. -+ */ -+static struct nand_ecclayout rb4xx_nand_ecclayout = { -+ .eccbytes = 6, -+ .eccpos = { 8, 9, 10, 13, 14, 15 }, -+ .oobavail = 9, -+ .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } } -+}; -+ -+static struct mtd_partition rb4xx_nand_partitions[] = { -+ { -+ .name = "booter", -+ .offset = 0, -+ .size = (256 * 1024), -+ .mask_flags = MTD_WRITEABLE, -+ }, -+ { -+ .name = "kernel", -+ .offset = (256 * 1024), -+ .size = (4 * 1024 * 1024) - (256 * 1024), -+ }, -+ { -+ .name = "rootfs", -+ .offset = MTDPART_OFS_NXTBLK, -+ .size = MTDPART_SIZ_FULL, -+ }, -+}; -+ -+static int rb4xx_nand_dev_ready(struct mtd_info *mtd) -+{ -+ return gpio_get_value_cansleep(RB4XX_NAND_GPIO_READY); -+} -+ -+static void rb4xx_nand_write_cmd(unsigned char cmd) -+{ -+ unsigned char data = cmd; -+ int err; -+ -+ err = rb4xx_cpld_write(&data, 1); -+ if (err) -+ pr_err("rb4xx_nand: write cmd failed, err=%d\n", err); -+} -+ -+static void rb4xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, -+ unsigned int ctrl) -+{ -+ if (ctrl & NAND_CTRL_CHANGE) { -+ gpio_set_value_cansleep(RB4XX_NAND_GPIO_CLE, -+ (ctrl & NAND_CLE) ? 1 : 0); -+ gpio_set_value_cansleep(RB4XX_NAND_GPIO_ALE, -+ (ctrl & NAND_ALE) ? 1 : 0); -+ gpio_set_value_cansleep(RB4XX_NAND_GPIO_NCE, -+ (ctrl & NAND_NCE) ? 0 : 1); -+ } -+ -+ if (cmd != NAND_CMD_NONE) -+ rb4xx_nand_write_cmd(cmd); -+} -+ -+static unsigned char rb4xx_nand_read_byte(struct mtd_info *mtd) -+{ -+ unsigned char data = 0; -+ int err; -+ -+ err = rb4xx_cpld_read(&data, NULL, 1); -+ if (err) { -+ pr_err("rb4xx_nand: read data failed, err=%d\n", err); -+ data = 0xff; -+ } -+ -+ return data; -+} -+ -+static void rb4xx_nand_write_buf(struct mtd_info *mtd, const unsigned char *buf, -+ int len) -+{ -+ int err; -+ -+ err = rb4xx_cpld_write(buf, len); -+ if (err) -+ pr_err("rb4xx_nand: write buf failed, err=%d\n", err); -+} -+ -+static void rb4xx_nand_read_buf(struct mtd_info *mtd, unsigned char *buf, -+ int len) -+{ -+ int err; -+ -+ err = rb4xx_cpld_read(buf, NULL, len); -+ if (err) -+ pr_err("rb4xx_nand: read buf failed, err=%d\n", err); -+} -+ -+static int rb4xx_nand_probe(struct platform_device *pdev) -+{ -+ struct rb4xx_nand_info *info; -+ int ret; -+ -+ printk(KERN_INFO DRV_DESC " version " DRV_VERSION "\n"); -+ -+ ret = gpio_request(RB4XX_NAND_GPIO_READY, "NAND RDY"); -+ if (ret) { -+ dev_err(&pdev->dev, "unable to request gpio %d\n", -+ RB4XX_NAND_GPIO_READY); -+ goto err; -+ } -+ -+ ret = gpio_direction_input(RB4XX_NAND_GPIO_READY); -+ if (ret) { -+ dev_err(&pdev->dev, "unable to set input mode on gpio %d\n", -+ RB4XX_NAND_GPIO_READY); -+ goto err_free_gpio_ready; -+ } -+ -+ ret = gpio_request(RB4XX_NAND_GPIO_ALE, "NAND ALE"); -+ if (ret) { -+ dev_err(&pdev->dev, "unable to request gpio %d\n", -+ RB4XX_NAND_GPIO_ALE); -+ goto err_free_gpio_ready; -+ } -+ -+ ret = gpio_direction_output(RB4XX_NAND_GPIO_ALE, 0); -+ if (ret) { -+ dev_err(&pdev->dev, "unable to set output mode on gpio %d\n", -+ RB4XX_NAND_GPIO_ALE); -+ goto err_free_gpio_ale; -+ } -+ -+ ret = gpio_request(RB4XX_NAND_GPIO_CLE, "NAND CLE"); -+ if (ret) { -+ dev_err(&pdev->dev, "unable to request gpio %d\n", -+ RB4XX_NAND_GPIO_CLE); -+ goto err_free_gpio_ale; -+ } -+ -+ ret = gpio_direction_output(RB4XX_NAND_GPIO_CLE, 0); -+ if (ret) { -+ dev_err(&pdev->dev, "unable to set output mode on gpio %d\n", -+ RB4XX_NAND_GPIO_CLE); -+ goto err_free_gpio_cle; -+ } -+ -+ ret = gpio_request(RB4XX_NAND_GPIO_NCE, "NAND NCE"); -+ if (ret) { -+ dev_err(&pdev->dev, "unable to request gpio %d\n", -+ RB4XX_NAND_GPIO_NCE); -+ goto err_free_gpio_cle; -+ } -+ -+ ret = gpio_direction_output(RB4XX_NAND_GPIO_NCE, 1); -+ if (ret) { -+ dev_err(&pdev->dev, "unable to set output mode on gpio %d\n", -+ RB4XX_NAND_GPIO_ALE); -+ goto err_free_gpio_nce; -+ } -+ -+ info = kzalloc(sizeof(*info), GFP_KERNEL); -+ if (!info) { -+ dev_err(&pdev->dev, "rb4xx-nand: no memory for private data\n"); -+ ret = -ENOMEM; -+ goto err_free_gpio_nce; -+ } -+ -+ info->chip.priv = &info; -+ info->mtd.priv = &info->chip; -+ info->mtd.owner = THIS_MODULE; -+ -+ info->chip.cmd_ctrl = rb4xx_nand_cmd_ctrl; -+ info->chip.dev_ready = rb4xx_nand_dev_ready; -+ info->chip.read_byte = rb4xx_nand_read_byte; -+ info->chip.write_buf = rb4xx_nand_write_buf; -+ info->chip.read_buf = rb4xx_nand_read_buf; -+ -+ info->chip.chip_delay = 25; -+ info->chip.ecc.mode = NAND_ECC_SOFT; -+ -+ platform_set_drvdata(pdev, info); -+ -+ ret = nand_scan_ident(&info->mtd, 1, NULL); -+ if (ret) { -+ ret = -ENXIO; -+ goto err_free_info; -+ } -+ -+ if (info->mtd.writesize == 512) -+ info->chip.ecc.layout = &rb4xx_nand_ecclayout; -+ -+ ret = nand_scan_tail(&info->mtd); -+ if (ret) { -+ return -ENXIO; -+ goto err_set_drvdata; -+ } -+ -+ mtd_device_register(&info->mtd, rb4xx_nand_partitions, -+ ARRAY_SIZE(rb4xx_nand_partitions)); -+ if (ret) -+ goto err_release_nand; -+ -+ return 0; -+ -+err_release_nand: -+ nand_release(&info->mtd); -+err_set_drvdata: -+ platform_set_drvdata(pdev, NULL); -+err_free_info: -+ kfree(info); -+err_free_gpio_nce: -+ gpio_free(RB4XX_NAND_GPIO_NCE); -+err_free_gpio_cle: -+ gpio_free(RB4XX_NAND_GPIO_CLE); -+err_free_gpio_ale: -+ gpio_free(RB4XX_NAND_GPIO_ALE); -+err_free_gpio_ready: -+ gpio_free(RB4XX_NAND_GPIO_READY); -+err: -+ return ret; -+} -+ -+static int rb4xx_nand_remove(struct platform_device *pdev) -+{ -+ struct rb4xx_nand_info *info = platform_get_drvdata(pdev); -+ -+ nand_release(&info->mtd); -+ platform_set_drvdata(pdev, NULL); -+ kfree(info); -+ gpio_free(RB4XX_NAND_GPIO_NCE); -+ gpio_free(RB4XX_NAND_GPIO_CLE); -+ gpio_free(RB4XX_NAND_GPIO_ALE); -+ gpio_free(RB4XX_NAND_GPIO_READY); -+ -+ return 0; -+} -+ -+static struct platform_driver rb4xx_nand_driver = { -+ .probe = rb4xx_nand_probe, -+ .remove = rb4xx_nand_remove, -+ .driver = { -+ .name = DRV_NAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init rb4xx_nand_init(void) -+{ -+ return platform_driver_register(&rb4xx_nand_driver); -+} -+ -+static void __exit rb4xx_nand_exit(void) -+{ -+ platform_driver_unregister(&rb4xx_nand_driver); -+} -+ -+module_init(rb4xx_nand_init); -+module_exit(rb4xx_nand_exit); -+ -+MODULE_DESCRIPTION(DRV_DESC); -+MODULE_VERSION(DRV_VERSION); -+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); -+MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>"); -+MODULE_LICENSE("GPL v2"); --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0002-phy-add-ethtool-ioctl-support-used-by-ag71xx-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0002-phy-add-ethtool-ioctl-support-used-by-ag71xx-driver.patch deleted file mode 100644 index ba7fbfad8..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0002-phy-add-ethtool-ioctl-support-used-by-ag71xx-driver.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 7b864612a6e3b139a5a607abd0048a19078fe42f Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Wed, 14 May 2014 02:55:06 +0200 -Subject: [PATCH] phy: add ethtool ioctl support, used by ag71xx driver - ---- - drivers/net/phy/phy.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ - include/linux/phy.h | 1 + - 2 files changed, 45 insertions(+) - -diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c -index 76d96b9..9439ef3 100644 ---- a/drivers/net/phy/phy.c -+++ b/drivers/net/phy/phy.c -@@ -293,6 +293,50 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd) - } - EXPORT_SYMBOL(phy_ethtool_gset); - -+int phy_ethtool_ioctl(struct phy_device *phydev, void *useraddr) -+{ -+ u32 cmd; -+ int tmp; -+ struct ethtool_cmd ecmd = { ETHTOOL_GSET }; -+ struct ethtool_value edata = { ETHTOOL_GLINK }; -+ -+ if (get_user(cmd, (u32 *) useraddr)) -+ return -EFAULT; -+ -+ switch (cmd) { -+ case ETHTOOL_GSET: -+ phy_ethtool_gset(phydev, &ecmd); -+ if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) -+ return -EFAULT; -+ return 0; -+ -+ case ETHTOOL_SSET: -+ if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) -+ return -EFAULT; -+ return phy_ethtool_sset(phydev, &ecmd); -+ -+ case ETHTOOL_NWAY_RST: -+ /* if autoneg is off, it's an error */ -+ tmp = phy_read(phydev, MII_BMCR); -+ if (tmp & BMCR_ANENABLE) { -+ tmp |= (BMCR_ANRESTART); -+ phy_write(phydev, MII_BMCR, tmp); -+ return 0; -+ } -+ return -EINVAL; -+ -+ case ETHTOOL_GLINK: -+ edata.data = (phy_read(phydev, -+ MII_BMSR) & BMSR_LSTATUS) ? 1 : 0; -+ if (copy_to_user(useraddr, &edata, sizeof(edata))) -+ return -EFAULT; -+ return 0; -+ } -+ -+ return -EOPNOTSUPP; -+} -+EXPORT_SYMBOL(phy_ethtool_ioctl); -+ - /** - * phy_mii_ioctl - generic PHY MII ioctl interface - * @phydev: the phy_device struct -diff --git a/include/linux/phy.h b/include/linux/phy.h -index 565188c..9ab0d79 100644 ---- a/include/linux/phy.h -+++ b/include/linux/phy.h -@@ -628,6 +628,7 @@ void phy_stop_machine(struct phy_device *phydev); - int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd); - int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd); - int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd); -+int phy_ethtool_ioctl(struct phy_device *phydev, void *useraddr); - int phy_start_interrupts(struct phy_device *phydev); - void phy_print_status(struct phy_device *phydev); - void phy_device_free(struct phy_device *phydev); --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0003-net-add-ag71xx-mac-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0003-net-add-ag71xx-mac-driver.patch deleted file mode 100644 index 1915c184c..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0003-net-add-ag71xx-mac-driver.patch +++ /dev/null @@ -1,4245 +0,0 @@ -From c5eb03f91f9185f4813431692f36db3862716a35 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:12:37 +0200 -Subject: [PATCH] net: add ag71xx mac driver - ---- - arch/mips/include/asm/mach-ath79/ag71xx_platform.h | 65 + - drivers/net/ethernet/atheros/Kconfig | 2 + - drivers/net/ethernet/atheros/Makefile | 1 + - drivers/net/ethernet/atheros/ag71xx/Kconfig | 33 + - drivers/net/ethernet/atheros/ag71xx/Makefile | 15 + - drivers/net/ethernet/atheros/ag71xx/ag71xx.h | 476 +++++++ - .../net/ethernet/atheros/ag71xx/ag71xx_ar7240.c | 1202 ++++++++++++++++++ - .../net/ethernet/atheros/ag71xx/ag71xx_ar8216.c | 44 + - .../net/ethernet/atheros/ag71xx/ag71xx_debugfs.c | 284 +++++ - .../net/ethernet/atheros/ag71xx/ag71xx_ethtool.c | 124 ++ - drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c | 1325 ++++++++++++++++++++ - drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c | 318 +++++ - drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c | 235 ++++ - 13 files changed, 4124 insertions(+) - create mode 100644 arch/mips/include/asm/mach-ath79/ag71xx_platform.h - create mode 100644 drivers/net/ethernet/atheros/ag71xx/Kconfig - create mode 100644 drivers/net/ethernet/atheros/ag71xx/Makefile - create mode 100644 drivers/net/ethernet/atheros/ag71xx/ag71xx.h - create mode 100644 drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c - create mode 100644 drivers/net/ethernet/atheros/ag71xx/ag71xx_ar8216.c - create mode 100644 drivers/net/ethernet/atheros/ag71xx/ag71xx_debugfs.c - create mode 100644 drivers/net/ethernet/atheros/ag71xx/ag71xx_ethtool.c - create mode 100644 drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c - create mode 100644 drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c - create mode 100644 drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c - -diff --git a/arch/mips/include/asm/mach-ath79/ag71xx_platform.h b/arch/mips/include/asm/mach-ath79/ag71xx_platform.h -new file mode 100644 -index 0000000..d46dc4e ---- /dev/null -+++ b/arch/mips/include/asm/mach-ath79/ag71xx_platform.h -@@ -0,0 +1,65 @@ -+/* -+ * Atheros AR71xx SoC specific platform data definitions -+ * -+ * Copyright (C) 2008-2012 Gabor Juhos <juhosg@openwrt.org> -+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#ifndef __ASM_MACH_ATH79_PLATFORM_H -+#define __ASM_MACH_ATH79_PLATFORM_H -+ -+#include <linux/if_ether.h> -+#include <linux/skbuff.h> -+#include <linux/phy.h> -+#include <linux/spi/spi.h> -+ -+struct ag71xx_switch_platform_data { -+ u8 phy4_mii_en:1; -+ u8 phy_poll_mask; -+}; -+ -+struct ag71xx_platform_data { -+ phy_interface_t phy_if_mode; -+ u32 phy_mask; -+ int speed; -+ int duplex; -+ u32 reset_bit; -+ u8 mac_addr[ETH_ALEN]; -+ struct device *mii_bus_dev; -+ -+ u8 has_gbit:1; -+ u8 is_ar91xx:1; -+ u8 is_ar7240:1; -+ u8 is_ar724x:1; -+ u8 has_ar8216:1; -+ -+ struct ag71xx_switch_platform_data *switch_data; -+ -+ void (*ddr_flush)(void); -+ void (*set_speed)(int speed); -+ -+ u32 fifo_cfg1; -+ u32 fifo_cfg2; -+ u32 fifo_cfg3; -+ -+ unsigned int max_frame_len; -+ unsigned int desc_pktlen_mask; -+}; -+ -+struct ag71xx_mdio_platform_data { -+ u32 phy_mask; -+ u8 builtin_switch:1; -+ u8 is_ar7240:1; -+ u8 is_ar9330:1; -+ u8 is_ar934x:1; -+ unsigned long mdio_clock; -+ unsigned long ref_clock; -+ -+ void (*reset)(struct mii_bus *bus); -+}; -+ -+#endif /* __ASM_MACH_ATH79_PLATFORM_H */ -diff --git a/drivers/net/ethernet/atheros/Kconfig b/drivers/net/ethernet/atheros/Kconfig -index 58ad37c..1fae572 100644 ---- a/drivers/net/ethernet/atheros/Kconfig -+++ b/drivers/net/ethernet/atheros/Kconfig -@@ -80,4 +80,6 @@ config ALX - To compile this driver as a module, choose M here. The module - will be called alx. - -+source drivers/net/ethernet/atheros/ag71xx/Kconfig -+ - endif # NET_VENDOR_ATHEROS -diff --git a/drivers/net/ethernet/atheros/Makefile b/drivers/net/ethernet/atheros/Makefile -index 5cf1c65..d1c5a49 100644 ---- a/drivers/net/ethernet/atheros/Makefile -+++ b/drivers/net/ethernet/atheros/Makefile -@@ -2,6 +2,7 @@ - # Makefile for the Atheros network device drivers. - # - -+obj-$(CONFIG_AG71XX) += ag71xx/ - obj-$(CONFIG_ATL1) += atlx/ - obj-$(CONFIG_ATL2) += atlx/ - obj-$(CONFIG_ATL1E) += atl1e/ -diff --git a/drivers/net/ethernet/atheros/ag71xx/Kconfig b/drivers/net/ethernet/atheros/ag71xx/Kconfig -new file mode 100644 -index 0000000..42d544f ---- /dev/null -+++ b/drivers/net/ethernet/atheros/ag71xx/Kconfig -@@ -0,0 +1,33 @@ -+config AG71XX -+ tristate "Atheros AR7XXX/AR9XXX built-in ethernet mac support" -+ depends on ATH79 -+ select PHYLIB -+ help -+ If you wish to compile a kernel for AR7XXX/91XXX and enable -+ ethernet support, then you should always answer Y to this. -+ -+if AG71XX -+ -+config AG71XX_DEBUG -+ bool "Atheros AR71xx built-in ethernet driver debugging" -+ default n -+ help -+ Atheros AR71xx built-in ethernet driver debugging messages. -+ -+config AG71XX_DEBUG_FS -+ bool "Atheros AR71xx built-in ethernet driver debugfs support" -+ depends on DEBUG_FS -+ default n -+ help -+ Say Y, if you need access to various statistics provided by -+ the ag71xx driver. -+ -+config AG71XX_AR8216_SUPPORT -+ bool "special support for the Atheros AR8216 switch" -+ default n -+ default y if ATH79_MACH_WNR2000 || ATH79_MACH_MZK_W04NU -+ help -+ Say 'y' here if you want to enable special support for the -+ Atheros AR8216 switch found on some boards. -+ -+endif -diff --git a/drivers/net/ethernet/atheros/ag71xx/Makefile b/drivers/net/ethernet/atheros/ag71xx/Makefile -new file mode 100644 -index 0000000..b3ec408 ---- /dev/null -+++ b/drivers/net/ethernet/atheros/ag71xx/Makefile -@@ -0,0 +1,15 @@ -+# -+# Makefile for the Atheros AR71xx built-in ethernet macs -+# -+ -+ag71xx-y += ag71xx_main.o -+ag71xx-y += ag71xx_ethtool.o -+ag71xx-y += ag71xx_phy.o -+ag71xx-y += ag71xx_mdio.o -+ag71xx-y += ag71xx_ar7240.o -+ -+ag71xx-$(CONFIG_AG71XX_DEBUG_FS) += ag71xx_debugfs.o -+ag71xx-$(CONFIG_AG71XX_AR8216_SUPPORT) += ag71xx_ar8216.o -+ -+obj-$(CONFIG_AG71XX) += ag71xx.o -+ -diff --git a/drivers/net/ethernet/atheros/ag71xx/ag71xx.h b/drivers/net/ethernet/atheros/ag71xx/ag71xx.h -new file mode 100644 -index 0000000..f6d85b9 ---- /dev/null -+++ b/drivers/net/ethernet/atheros/ag71xx/ag71xx.h -@@ -0,0 +1,476 @@ -+/* -+ * Atheros AR71xx built-in ethernet mac driver -+ * -+ * Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org> -+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> -+ * -+ * Based on Atheros' AG7100 driver -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#ifndef __AG71XX_H -+#define __AG71XX_H -+ -+#include <linux/kernel.h> -+#include <linux/version.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/types.h> -+#include <linux/random.h> -+#include <linux/spinlock.h> -+#include <linux/interrupt.h> -+#include <linux/platform_device.h> -+#include <linux/ethtool.h> -+#include <linux/etherdevice.h> -+#include <linux/if_vlan.h> -+#include <linux/phy.h> -+#include <linux/skbuff.h> -+#include <linux/dma-mapping.h> -+#include <linux/workqueue.h> -+ -+#include <linux/bitops.h> -+ -+#include <asm/mach-ath79/ar71xx_regs.h> -+#include <asm/mach-ath79/ath79.h> -+#include <asm/mach-ath79/ag71xx_platform.h> -+ -+#define AG71XX_DRV_NAME "ag71xx" -+#define AG71XX_DRV_VERSION "0.5.35" -+ -+#define AG71XX_NAPI_WEIGHT 64 -+#define AG71XX_OOM_REFILL (1 + HZ/10) -+ -+#define AG71XX_INT_ERR (AG71XX_INT_RX_BE | AG71XX_INT_TX_BE) -+#define AG71XX_INT_TX (AG71XX_INT_TX_PS) -+#define AG71XX_INT_RX (AG71XX_INT_RX_PR | AG71XX_INT_RX_OF) -+ -+#define AG71XX_INT_POLL (AG71XX_INT_RX | AG71XX_INT_TX) -+#define AG71XX_INT_INIT (AG71XX_INT_ERR | AG71XX_INT_POLL) -+ -+#define AG71XX_TX_MTU_LEN 1540 -+ -+#define AG71XX_TX_RING_SIZE_DEFAULT 32 -+#define AG71XX_RX_RING_SIZE_DEFAULT 128 -+ -+#define AG71XX_TX_RING_SIZE_MAX 32 -+#define AG71XX_RX_RING_SIZE_MAX 128 -+ -+#ifdef CONFIG_AG71XX_DEBUG -+#define DBG(fmt, args...) pr_debug(fmt, ## args) -+#else -+#define DBG(fmt, args...) do {} while (0) -+#endif -+ -+#define ag71xx_assert(_cond) \ -+do { \ -+ if (_cond) \ -+ break; \ -+ printk("%s,%d: assertion failed\n", __FILE__, __LINE__); \ -+ BUG(); \ -+} while (0) -+ -+struct ag71xx_desc { -+ u32 data; -+ u32 ctrl; -+#define DESC_EMPTY BIT(31) -+#define DESC_MORE BIT(24) -+#define DESC_PKTLEN_M 0xfff -+ u32 next; -+ u32 pad; -+} __attribute__((aligned(4))); -+ -+struct ag71xx_buf { -+ union { -+ struct sk_buff *skb; -+ void *rx_buf; -+ }; -+ struct ag71xx_desc *desc; -+ union { -+ dma_addr_t dma_addr; -+ unsigned long timestamp; -+ }; -+ unsigned int len; -+}; -+ -+struct ag71xx_ring { -+ struct ag71xx_buf *buf; -+ u8 *descs_cpu; -+ dma_addr_t descs_dma; -+ unsigned int desc_size; -+ unsigned int curr; -+ unsigned int dirty; -+ unsigned int size; -+}; -+ -+struct ag71xx_mdio { -+ struct mii_bus *mii_bus; -+ int mii_irq[PHY_MAX_ADDR]; -+ void __iomem *mdio_base; -+ struct ag71xx_mdio_platform_data *pdata; -+}; -+ -+struct ag71xx_int_stats { -+ unsigned long rx_pr; -+ unsigned long rx_be; -+ unsigned long rx_of; -+ unsigned long tx_ps; -+ unsigned long tx_be; -+ unsigned long tx_ur; -+ unsigned long total; -+}; -+ -+struct ag71xx_napi_stats { -+ unsigned long napi_calls; -+ unsigned long rx_count; -+ unsigned long rx_packets; -+ unsigned long rx_packets_max; -+ unsigned long tx_count; -+ unsigned long tx_packets; -+ unsigned long tx_packets_max; -+ -+ unsigned long rx[AG71XX_NAPI_WEIGHT + 1]; -+ unsigned long tx[AG71XX_NAPI_WEIGHT + 1]; -+}; -+ -+struct ag71xx_debug { -+ struct dentry *debugfs_dir; -+ -+ struct ag71xx_int_stats int_stats; -+ struct ag71xx_napi_stats napi_stats; -+}; -+ -+struct ag71xx { -+ void __iomem *mac_base; -+ -+ spinlock_t lock; -+ struct platform_device *pdev; -+ struct net_device *dev; -+ struct napi_struct napi; -+ u32 msg_enable; -+ -+ struct ag71xx_desc *stop_desc; -+ dma_addr_t stop_desc_dma; -+ -+ struct ag71xx_ring rx_ring; -+ struct ag71xx_ring tx_ring; -+ -+ struct mii_bus *mii_bus; -+ struct phy_device *phy_dev; -+ void *phy_priv; -+ -+ unsigned int link; -+ unsigned int speed; -+ int duplex; -+ -+ unsigned int max_frame_len; -+ unsigned int desc_pktlen_mask; -+ unsigned int rx_buf_size; -+ -+ struct work_struct restart_work; -+ struct delayed_work link_work; -+ struct timer_list oom_timer; -+ -+#ifdef CONFIG_AG71XX_DEBUG_FS -+ struct ag71xx_debug debug; -+#endif -+}; -+ -+extern struct ethtool_ops ag71xx_ethtool_ops; -+void ag71xx_link_adjust(struct ag71xx *ag); -+ -+int ag71xx_mdio_driver_init(void) __init; -+void ag71xx_mdio_driver_exit(void); -+ -+int ag71xx_phy_connect(struct ag71xx *ag); -+void ag71xx_phy_disconnect(struct ag71xx *ag); -+void ag71xx_phy_start(struct ag71xx *ag); -+void ag71xx_phy_stop(struct ag71xx *ag); -+ -+static inline struct ag71xx_platform_data *ag71xx_get_pdata(struct ag71xx *ag) -+{ -+ return ag->pdev->dev.platform_data; -+} -+ -+static inline int ag71xx_desc_empty(struct ag71xx_desc *desc) -+{ -+ return (desc->ctrl & DESC_EMPTY) != 0; -+} -+ -+/* Register offsets */ -+#define AG71XX_REG_MAC_CFG1 0x0000 -+#define AG71XX_REG_MAC_CFG2 0x0004 -+#define AG71XX_REG_MAC_IPG 0x0008 -+#define AG71XX_REG_MAC_HDX 0x000c -+#define AG71XX_REG_MAC_MFL 0x0010 -+#define AG71XX_REG_MII_CFG 0x0020 -+#define AG71XX_REG_MII_CMD 0x0024 -+#define AG71XX_REG_MII_ADDR 0x0028 -+#define AG71XX_REG_MII_CTRL 0x002c -+#define AG71XX_REG_MII_STATUS 0x0030 -+#define AG71XX_REG_MII_IND 0x0034 -+#define AG71XX_REG_MAC_IFCTL 0x0038 -+#define AG71XX_REG_MAC_ADDR1 0x0040 -+#define AG71XX_REG_MAC_ADDR2 0x0044 -+#define AG71XX_REG_FIFO_CFG0 0x0048 -+#define AG71XX_REG_FIFO_CFG1 0x004c -+#define AG71XX_REG_FIFO_CFG2 0x0050 -+#define AG71XX_REG_FIFO_CFG3 0x0054 -+#define AG71XX_REG_FIFO_CFG4 0x0058 -+#define AG71XX_REG_FIFO_CFG5 0x005c -+#define AG71XX_REG_FIFO_RAM0 0x0060 -+#define AG71XX_REG_FIFO_RAM1 0x0064 -+#define AG71XX_REG_FIFO_RAM2 0x0068 -+#define AG71XX_REG_FIFO_RAM3 0x006c -+#define AG71XX_REG_FIFO_RAM4 0x0070 -+#define AG71XX_REG_FIFO_RAM5 0x0074 -+#define AG71XX_REG_FIFO_RAM6 0x0078 -+#define AG71XX_REG_FIFO_RAM7 0x007c -+ -+#define AG71XX_REG_TX_CTRL 0x0180 -+#define AG71XX_REG_TX_DESC 0x0184 -+#define AG71XX_REG_TX_STATUS 0x0188 -+#define AG71XX_REG_RX_CTRL 0x018c -+#define AG71XX_REG_RX_DESC 0x0190 -+#define AG71XX_REG_RX_STATUS 0x0194 -+#define AG71XX_REG_INT_ENABLE 0x0198 -+#define AG71XX_REG_INT_STATUS 0x019c -+ -+#define AG71XX_REG_FIFO_DEPTH 0x01a8 -+#define AG71XX_REG_RX_SM 0x01b0 -+#define AG71XX_REG_TX_SM 0x01b4 -+ -+#define MAC_CFG1_TXE BIT(0) /* Tx Enable */ -+#define MAC_CFG1_STX BIT(1) /* Synchronize Tx Enable */ -+#define MAC_CFG1_RXE BIT(2) /* Rx Enable */ -+#define MAC_CFG1_SRX BIT(3) /* Synchronize Rx Enable */ -+#define MAC_CFG1_TFC BIT(4) /* Tx Flow Control Enable */ -+#define MAC_CFG1_RFC BIT(5) /* Rx Flow Control Enable */ -+#define MAC_CFG1_LB BIT(8) /* Loopback mode */ -+#define MAC_CFG1_SR BIT(31) /* Soft Reset */ -+ -+#define MAC_CFG2_FDX BIT(0) -+#define MAC_CFG2_CRC_EN BIT(1) -+#define MAC_CFG2_PAD_CRC_EN BIT(2) -+#define MAC_CFG2_LEN_CHECK BIT(4) -+#define MAC_CFG2_HUGE_FRAME_EN BIT(5) -+#define MAC_CFG2_IF_1000 BIT(9) -+#define MAC_CFG2_IF_10_100 BIT(8) -+ -+#define FIFO_CFG0_WTM BIT(0) /* Watermark Module */ -+#define FIFO_CFG0_RXS BIT(1) /* Rx System Module */ -+#define FIFO_CFG0_RXF BIT(2) /* Rx Fabric Module */ -+#define FIFO_CFG0_TXS BIT(3) /* Tx System Module */ -+#define FIFO_CFG0_TXF BIT(4) /* Tx Fabric Module */ -+#define FIFO_CFG0_ALL (FIFO_CFG0_WTM | FIFO_CFG0_RXS | FIFO_CFG0_RXF \ -+ | FIFO_CFG0_TXS | FIFO_CFG0_TXF) -+ -+#define FIFO_CFG0_ENABLE_SHIFT 8 -+ -+#define FIFO_CFG4_DE BIT(0) /* Drop Event */ -+#define FIFO_CFG4_DV BIT(1) /* RX_DV Event */ -+#define FIFO_CFG4_FC BIT(2) /* False Carrier */ -+#define FIFO_CFG4_CE BIT(3) /* Code Error */ -+#define FIFO_CFG4_CR BIT(4) /* CRC error */ -+#define FIFO_CFG4_LM BIT(5) /* Length Mismatch */ -+#define FIFO_CFG4_LO BIT(6) /* Length out of range */ -+#define FIFO_CFG4_OK BIT(7) /* Packet is OK */ -+#define FIFO_CFG4_MC BIT(8) /* Multicast Packet */ -+#define FIFO_CFG4_BC BIT(9) /* Broadcast Packet */ -+#define FIFO_CFG4_DR BIT(10) /* Dribble */ -+#define FIFO_CFG4_LE BIT(11) /* Long Event */ -+#define FIFO_CFG4_CF BIT(12) /* Control Frame */ -+#define FIFO_CFG4_PF BIT(13) /* Pause Frame */ -+#define FIFO_CFG4_UO BIT(14) /* Unsupported Opcode */ -+#define FIFO_CFG4_VT BIT(15) /* VLAN tag detected */ -+#define FIFO_CFG4_FT BIT(16) /* Frame Truncated */ -+#define FIFO_CFG4_UC BIT(17) /* Unicast Packet */ -+ -+#define FIFO_CFG5_DE BIT(0) /* Drop Event */ -+#define FIFO_CFG5_DV BIT(1) /* RX_DV Event */ -+#define FIFO_CFG5_FC BIT(2) /* False Carrier */ -+#define FIFO_CFG5_CE BIT(3) /* Code Error */ -+#define FIFO_CFG5_LM BIT(4) /* Length Mismatch */ -+#define FIFO_CFG5_LO BIT(5) /* Length Out of Range */ -+#define FIFO_CFG5_OK BIT(6) /* Packet is OK */ -+#define FIFO_CFG5_MC BIT(7) /* Multicast Packet */ -+#define FIFO_CFG5_BC BIT(8) /* Broadcast Packet */ -+#define FIFO_CFG5_DR BIT(9) /* Dribble */ -+#define FIFO_CFG5_CF BIT(10) /* Control Frame */ -+#define FIFO_CFG5_PF BIT(11) /* Pause Frame */ -+#define FIFO_CFG5_UO BIT(12) /* Unsupported Opcode */ -+#define FIFO_CFG5_VT BIT(13) /* VLAN tag detected */ -+#define FIFO_CFG5_LE BIT(14) /* Long Event */ -+#define FIFO_CFG5_FT BIT(15) /* Frame Truncated */ -+#define FIFO_CFG5_16 BIT(16) /* unknown */ -+#define FIFO_CFG5_17 BIT(17) /* unknown */ -+#define FIFO_CFG5_SF BIT(18) /* Short Frame */ -+#define FIFO_CFG5_BM BIT(19) /* Byte Mode */ -+ -+#define AG71XX_INT_TX_PS BIT(0) -+#define AG71XX_INT_TX_UR BIT(1) -+#define AG71XX_INT_TX_BE BIT(3) -+#define AG71XX_INT_RX_PR BIT(4) -+#define AG71XX_INT_RX_OF BIT(6) -+#define AG71XX_INT_RX_BE BIT(7) -+ -+#define MAC_IFCTL_SPEED BIT(16) -+ -+#define MII_CFG_CLK_DIV_4 0 -+#define MII_CFG_CLK_DIV_6 2 -+#define MII_CFG_CLK_DIV_8 3 -+#define MII_CFG_CLK_DIV_10 4 -+#define MII_CFG_CLK_DIV_14 5 -+#define MII_CFG_CLK_DIV_20 6 -+#define MII_CFG_CLK_DIV_28 7 -+#define MII_CFG_CLK_DIV_34 8 -+#define MII_CFG_CLK_DIV_42 9 -+#define MII_CFG_CLK_DIV_50 10 -+#define MII_CFG_CLK_DIV_58 11 -+#define MII_CFG_CLK_DIV_66 12 -+#define MII_CFG_CLK_DIV_74 13 -+#define MII_CFG_CLK_DIV_82 14 -+#define MII_CFG_CLK_DIV_98 15 -+#define MII_CFG_RESET BIT(31) -+ -+#define MII_CMD_WRITE 0x0 -+#define MII_CMD_READ 0x1 -+#define MII_ADDR_SHIFT 8 -+#define MII_IND_BUSY BIT(0) -+#define MII_IND_INVALID BIT(2) -+ -+#define TX_CTRL_TXE BIT(0) /* Tx Enable */ -+ -+#define TX_STATUS_PS BIT(0) /* Packet Sent */ -+#define TX_STATUS_UR BIT(1) /* Tx Underrun */ -+#define TX_STATUS_BE BIT(3) /* Bus Error */ -+ -+#define RX_CTRL_RXE BIT(0) /* Rx Enable */ -+ -+#define RX_STATUS_PR BIT(0) /* Packet Received */ -+#define RX_STATUS_OF BIT(2) /* Rx Overflow */ -+#define RX_STATUS_BE BIT(3) /* Bus Error */ -+ -+static inline void ag71xx_check_reg_offset(struct ag71xx *ag, unsigned reg) -+{ -+ switch (reg) { -+ case AG71XX_REG_MAC_CFG1 ... AG71XX_REG_MAC_MFL: -+ case AG71XX_REG_MAC_IFCTL ... AG71XX_REG_TX_SM: -+ case AG71XX_REG_MII_CFG: -+ break; -+ -+ default: -+ BUG(); -+ } -+} -+ -+static inline void ag71xx_wr(struct ag71xx *ag, unsigned reg, u32 value) -+{ -+ ag71xx_check_reg_offset(ag, reg); -+ -+ __raw_writel(value, ag->mac_base + reg); -+ /* flush write */ -+ (void) __raw_readl(ag->mac_base + reg); -+} -+ -+static inline u32 ag71xx_rr(struct ag71xx *ag, unsigned reg) -+{ -+ ag71xx_check_reg_offset(ag, reg); -+ -+ return __raw_readl(ag->mac_base + reg); -+} -+ -+static inline void ag71xx_sb(struct ag71xx *ag, unsigned reg, u32 mask) -+{ -+ void __iomem *r; -+ -+ ag71xx_check_reg_offset(ag, reg); -+ -+ r = ag->mac_base + reg; -+ __raw_writel(__raw_readl(r) | mask, r); -+ /* flush write */ -+ (void)__raw_readl(r); -+} -+ -+static inline void ag71xx_cb(struct ag71xx *ag, unsigned reg, u32 mask) -+{ -+ void __iomem *r; -+ -+ ag71xx_check_reg_offset(ag, reg); -+ -+ r = ag->mac_base + reg; -+ __raw_writel(__raw_readl(r) & ~mask, r); -+ /* flush write */ -+ (void) __raw_readl(r); -+} -+ -+static inline void ag71xx_int_enable(struct ag71xx *ag, u32 ints) -+{ -+ ag71xx_sb(ag, AG71XX_REG_INT_ENABLE, ints); -+} -+ -+static inline void ag71xx_int_disable(struct ag71xx *ag, u32 ints) -+{ -+ ag71xx_cb(ag, AG71XX_REG_INT_ENABLE, ints); -+} -+ -+#ifdef CONFIG_AG71XX_AR8216_SUPPORT -+void ag71xx_add_ar8216_header(struct ag71xx *ag, struct sk_buff *skb); -+int ag71xx_remove_ar8216_header(struct ag71xx *ag, struct sk_buff *skb, -+ int pktlen); -+static inline int ag71xx_has_ar8216(struct ag71xx *ag) -+{ -+ return ag71xx_get_pdata(ag)->has_ar8216; -+} -+#else -+static inline void ag71xx_add_ar8216_header(struct ag71xx *ag, -+ struct sk_buff *skb) -+{ -+} -+ -+static inline int ag71xx_remove_ar8216_header(struct ag71xx *ag, -+ struct sk_buff *skb, -+ int pktlen) -+{ -+ return 0; -+} -+static inline int ag71xx_has_ar8216(struct ag71xx *ag) -+{ -+ return 0; -+} -+#endif -+ -+#ifdef CONFIG_AG71XX_DEBUG_FS -+int ag71xx_debugfs_root_init(void); -+void ag71xx_debugfs_root_exit(void); -+int ag71xx_debugfs_init(struct ag71xx *ag); -+void ag71xx_debugfs_exit(struct ag71xx *ag); -+void ag71xx_debugfs_update_int_stats(struct ag71xx *ag, u32 status); -+void ag71xx_debugfs_update_napi_stats(struct ag71xx *ag, int rx, int tx); -+#else -+static inline int ag71xx_debugfs_root_init(void) { return 0; } -+static inline void ag71xx_debugfs_root_exit(void) {} -+static inline int ag71xx_debugfs_init(struct ag71xx *ag) { return 0; } -+static inline void ag71xx_debugfs_exit(struct ag71xx *ag) {} -+static inline void ag71xx_debugfs_update_int_stats(struct ag71xx *ag, -+ u32 status) {} -+static inline void ag71xx_debugfs_update_napi_stats(struct ag71xx *ag, -+ int rx, int tx) {} -+#endif /* CONFIG_AG71XX_DEBUG_FS */ -+ -+void ag71xx_ar7240_start(struct ag71xx *ag); -+void ag71xx_ar7240_stop(struct ag71xx *ag); -+int ag71xx_ar7240_init(struct ag71xx *ag); -+void ag71xx_ar7240_cleanup(struct ag71xx *ag); -+ -+int ag71xx_mdio_mii_read(struct ag71xx_mdio *am, int addr, int reg); -+void ag71xx_mdio_mii_write(struct ag71xx_mdio *am, int addr, int reg, u16 val); -+ -+u16 ar7240sw_phy_read(struct mii_bus *mii, unsigned phy_addr, -+ unsigned reg_addr); -+int ar7240sw_phy_write(struct mii_bus *mii, unsigned phy_addr, -+ unsigned reg_addr, u16 reg_val); -+ -+#endif /* _AG71XX_H */ -diff --git a/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c b/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c -new file mode 100644 -index 0000000..d4ccc02 ---- /dev/null -+++ b/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c -@@ -0,0 +1,1202 @@ -+/* -+ * Driver for the built-in ethernet switch of the Atheros AR7240 SoC -+ * Copyright (c) 2010 Gabor Juhos <juhosg@openwrt.org> -+ * Copyright (c) 2010 Felix Fietkau <nbd@openwrt.org> -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ * -+ */ -+ -+#include <linux/etherdevice.h> -+#include <linux/list.h> -+#include <linux/netdevice.h> -+#include <linux/phy.h> -+#include <linux/mii.h> -+#include <linux/bitops.h> -+#include <linux/switch.h> -+#include "ag71xx.h" -+ -+#define BITM(_count) (BIT(_count) - 1) -+#define BITS(_shift, _count) (BITM(_count) << _shift) -+ -+#define AR7240_REG_MASK_CTRL 0x00 -+#define AR7240_MASK_CTRL_REVISION_M BITM(8) -+#define AR7240_MASK_CTRL_VERSION_M BITM(8) -+#define AR7240_MASK_CTRL_VERSION_S 8 -+#define AR7240_MASK_CTRL_VERSION_AR7240 0x01 -+#define AR7240_MASK_CTRL_VERSION_AR934X 0x02 -+#define AR7240_MASK_CTRL_SOFT_RESET BIT(31) -+ -+#define AR7240_REG_MAC_ADDR0 0x20 -+#define AR7240_REG_MAC_ADDR1 0x24 -+ -+#define AR7240_REG_FLOOD_MASK 0x2c -+#define AR7240_FLOOD_MASK_BROAD_TO_CPU BIT(26) -+ -+#define AR7240_REG_GLOBAL_CTRL 0x30 -+#define AR7240_GLOBAL_CTRL_MTU_M BITM(11) -+#define AR9340_GLOBAL_CTRL_MTU_M BITM(14) -+ -+#define AR7240_REG_VTU 0x0040 -+#define AR7240_VTU_OP BITM(3) -+#define AR7240_VTU_OP_NOOP 0x0 -+#define AR7240_VTU_OP_FLUSH 0x1 -+#define AR7240_VTU_OP_LOAD 0x2 -+#define AR7240_VTU_OP_PURGE 0x3 -+#define AR7240_VTU_OP_REMOVE_PORT 0x4 -+#define AR7240_VTU_ACTIVE BIT(3) -+#define AR7240_VTU_FULL BIT(4) -+#define AR7240_VTU_PORT BITS(8, 4) -+#define AR7240_VTU_PORT_S 8 -+#define AR7240_VTU_VID BITS(16, 12) -+#define AR7240_VTU_VID_S 16 -+#define AR7240_VTU_PRIO BITS(28, 3) -+#define AR7240_VTU_PRIO_S 28 -+#define AR7240_VTU_PRIO_EN BIT(31) -+ -+#define AR7240_REG_VTU_DATA 0x0044 -+#define AR7240_VTUDATA_MEMBER BITS(0, 10) -+#define AR7240_VTUDATA_VALID BIT(11) -+ -+#define AR7240_REG_ATU 0x50 -+#define AR7240_ATU_FLUSH_ALL 0x1 -+ -+#define AR7240_REG_AT_CTRL 0x5c -+#define AR7240_AT_CTRL_AGE_TIME BITS(0, 15) -+#define AR7240_AT_CTRL_AGE_EN BIT(17) -+#define AR7240_AT_CTRL_LEARN_CHANGE BIT(18) -+#define AR7240_AT_CTRL_RESERVED BIT(19) -+#define AR7240_AT_CTRL_ARP_EN BIT(20) -+ -+#define AR7240_REG_TAG_PRIORITY 0x70 -+ -+#define AR7240_REG_SERVICE_TAG 0x74 -+#define AR7240_SERVICE_TAG_M BITM(16) -+ -+#define AR7240_REG_CPU_PORT 0x78 -+#define AR7240_MIRROR_PORT_S 4 -+#define AR7240_CPU_PORT_EN BIT(8) -+ -+#define AR7240_REG_MIB_FUNCTION0 0x80 -+#define AR7240_MIB_TIMER_M BITM(16) -+#define AR7240_MIB_AT_HALF_EN BIT(16) -+#define AR7240_MIB_BUSY BIT(17) -+#define AR7240_MIB_FUNC_S 24 -+#define AR7240_MIB_FUNC_M BITM(3) -+#define AR7240_MIB_FUNC_NO_OP 0x0 -+#define AR7240_MIB_FUNC_FLUSH 0x1 -+#define AR7240_MIB_FUNC_CAPTURE 0x3 -+ -+#define AR7240_REG_MDIO_CTRL 0x98 -+#define AR7240_MDIO_CTRL_DATA_M BITM(16) -+#define AR7240_MDIO_CTRL_REG_ADDR_S 16 -+#define AR7240_MDIO_CTRL_PHY_ADDR_S 21 -+#define AR7240_MDIO_CTRL_CMD_WRITE 0 -+#define AR7240_MDIO_CTRL_CMD_READ BIT(27) -+#define AR7240_MDIO_CTRL_MASTER_EN BIT(30) -+#define AR7240_MDIO_CTRL_BUSY BIT(31) -+ -+#define AR7240_REG_PORT_BASE(_port) (0x100 + (_port) * 0x100) -+ -+#define AR7240_REG_PORT_STATUS(_port) (AR7240_REG_PORT_BASE((_port)) + 0x00) -+#define AR7240_PORT_STATUS_SPEED_S 0 -+#define AR7240_PORT_STATUS_SPEED_M BITM(2) -+#define AR7240_PORT_STATUS_SPEED_10 0 -+#define AR7240_PORT_STATUS_SPEED_100 1 -+#define AR7240_PORT_STATUS_SPEED_1000 2 -+#define AR7240_PORT_STATUS_TXMAC BIT(2) -+#define AR7240_PORT_STATUS_RXMAC BIT(3) -+#define AR7240_PORT_STATUS_TXFLOW BIT(4) -+#define AR7240_PORT_STATUS_RXFLOW BIT(5) -+#define AR7240_PORT_STATUS_DUPLEX BIT(6) -+#define AR7240_PORT_STATUS_LINK_UP BIT(8) -+#define AR7240_PORT_STATUS_LINK_AUTO BIT(9) -+#define AR7240_PORT_STATUS_LINK_PAUSE BIT(10) -+ -+#define AR7240_REG_PORT_CTRL(_port) (AR7240_REG_PORT_BASE((_port)) + 0x04) -+#define AR7240_PORT_CTRL_STATE_M BITM(3) -+#define AR7240_PORT_CTRL_STATE_DISABLED 0 -+#define AR7240_PORT_CTRL_STATE_BLOCK 1 -+#define AR7240_PORT_CTRL_STATE_LISTEN 2 -+#define AR7240_PORT_CTRL_STATE_LEARN 3 -+#define AR7240_PORT_CTRL_STATE_FORWARD 4 -+#define AR7240_PORT_CTRL_LEARN_LOCK BIT(7) -+#define AR7240_PORT_CTRL_VLAN_MODE_S 8 -+#define AR7240_PORT_CTRL_VLAN_MODE_KEEP 0 -+#define AR7240_PORT_CTRL_VLAN_MODE_STRIP 1 -+#define AR7240_PORT_CTRL_VLAN_MODE_ADD 2 -+#define AR7240_PORT_CTRL_VLAN_MODE_DOUBLE_TAG 3 -+#define AR7240_PORT_CTRL_IGMP_SNOOP BIT(10) -+#define AR7240_PORT_CTRL_HEADER BIT(11) -+#define AR7240_PORT_CTRL_MAC_LOOP BIT(12) -+#define AR7240_PORT_CTRL_SINGLE_VLAN BIT(13) -+#define AR7240_PORT_CTRL_LEARN BIT(14) -+#define AR7240_PORT_CTRL_DOUBLE_TAG BIT(15) -+#define AR7240_PORT_CTRL_MIRROR_TX BIT(16) -+#define AR7240_PORT_CTRL_MIRROR_RX BIT(17) -+ -+#define AR7240_REG_PORT_VLAN(_port) (AR7240_REG_PORT_BASE((_port)) + 0x08) -+ -+#define AR7240_PORT_VLAN_DEFAULT_ID_S 0 -+#define AR7240_PORT_VLAN_DEST_PORTS_S 16 -+#define AR7240_PORT_VLAN_MODE_S 30 -+#define AR7240_PORT_VLAN_MODE_PORT_ONLY 0 -+#define AR7240_PORT_VLAN_MODE_PORT_FALLBACK 1 -+#define AR7240_PORT_VLAN_MODE_VLAN_ONLY 2 -+#define AR7240_PORT_VLAN_MODE_SECURE 3 -+ -+ -+#define AR7240_REG_STATS_BASE(_port) (0x20000 + (_port) * 0x100) -+ -+#define AR7240_STATS_RXBROAD 0x00 -+#define AR7240_STATS_RXPAUSE 0x04 -+#define AR7240_STATS_RXMULTI 0x08 -+#define AR7240_STATS_RXFCSERR 0x0c -+#define AR7240_STATS_RXALIGNERR 0x10 -+#define AR7240_STATS_RXRUNT 0x14 -+#define AR7240_STATS_RXFRAGMENT 0x18 -+#define AR7240_STATS_RX64BYTE 0x1c -+#define AR7240_STATS_RX128BYTE 0x20 -+#define AR7240_STATS_RX256BYTE 0x24 -+#define AR7240_STATS_RX512BYTE 0x28 -+#define AR7240_STATS_RX1024BYTE 0x2c -+#define AR7240_STATS_RX1518BYTE 0x30 -+#define AR7240_STATS_RXMAXBYTE 0x34 -+#define AR7240_STATS_RXTOOLONG 0x38 -+#define AR7240_STATS_RXGOODBYTE 0x3c -+#define AR7240_STATS_RXBADBYTE 0x44 -+#define AR7240_STATS_RXOVERFLOW 0x4c -+#define AR7240_STATS_FILTERED 0x50 -+#define AR7240_STATS_TXBROAD 0x54 -+#define AR7240_STATS_TXPAUSE 0x58 -+#define AR7240_STATS_TXMULTI 0x5c -+#define AR7240_STATS_TXUNDERRUN 0x60 -+#define AR7240_STATS_TX64BYTE 0x64 -+#define AR7240_STATS_TX128BYTE 0x68 -+#define AR7240_STATS_TX256BYTE 0x6c -+#define AR7240_STATS_TX512BYTE 0x70 -+#define AR7240_STATS_TX1024BYTE 0x74 -+#define AR7240_STATS_TX1518BYTE 0x78 -+#define AR7240_STATS_TXMAXBYTE 0x7c -+#define AR7240_STATS_TXOVERSIZE 0x80 -+#define AR7240_STATS_TXBYTE 0x84 -+#define AR7240_STATS_TXCOLLISION 0x8c -+#define AR7240_STATS_TXABORTCOL 0x90 -+#define AR7240_STATS_TXMULTICOL 0x94 -+#define AR7240_STATS_TXSINGLECOL 0x98 -+#define AR7240_STATS_TXEXCDEFER 0x9c -+#define AR7240_STATS_TXDEFER 0xa0 -+#define AR7240_STATS_TXLATECOL 0xa4 -+ -+#define AR7240_PORT_CPU 0 -+#define AR7240_NUM_PORTS 6 -+#define AR7240_NUM_PHYS 5 -+ -+#define AR7240_PHY_ID1 0x004d -+#define AR7240_PHY_ID2 0xd041 -+ -+#define AR934X_PHY_ID1 0x004d -+#define AR934X_PHY_ID2 0xd042 -+ -+#define AR7240_MAX_VLANS 16 -+ -+#define AR934X_REG_OPER_MODE0 0x04 -+#define AR934X_OPER_MODE0_MAC_GMII_EN BIT(6) -+#define AR934X_OPER_MODE0_PHY_MII_EN BIT(10) -+ -+#define AR934X_REG_OPER_MODE1 0x08 -+#define AR934X_REG_OPER_MODE1_PHY4_MII_EN BIT(28) -+ -+#define AR934X_REG_FLOOD_MASK 0x2c -+#define AR934X_FLOOD_MASK_MC_DP(_p) BIT(16 + (_p)) -+#define AR934X_FLOOD_MASK_BC_DP(_p) BIT(25 + (_p)) -+ -+#define AR934X_REG_QM_CTRL 0x3c -+#define AR934X_QM_CTRL_ARP_EN BIT(15) -+ -+#define AR934X_REG_AT_CTRL 0x5c -+#define AR934X_AT_CTRL_AGE_TIME BITS(0, 15) -+#define AR934X_AT_CTRL_AGE_EN BIT(17) -+#define AR934X_AT_CTRL_LEARN_CHANGE BIT(18) -+ -+#define AR934X_MIB_ENABLE BIT(30) -+ -+#define AR934X_REG_PORT_BASE(_port) (0x100 + (_port) * 0x100) -+ -+#define AR934X_REG_PORT_VLAN1(_port) (AR934X_REG_PORT_BASE((_port)) + 0x08) -+#define AR934X_PORT_VLAN1_DEFAULT_SVID_S 0 -+#define AR934X_PORT_VLAN1_FORCE_DEFAULT_VID_EN BIT(12) -+#define AR934X_PORT_VLAN1_PORT_TLS_MODE BIT(13) -+#define AR934X_PORT_VLAN1_PORT_VLAN_PROP_EN BIT(14) -+#define AR934X_PORT_VLAN1_PORT_CLONE_EN BIT(15) -+#define AR934X_PORT_VLAN1_DEFAULT_CVID_S 16 -+#define AR934X_PORT_VLAN1_FORCE_PORT_VLAN_EN BIT(28) -+#define AR934X_PORT_VLAN1_ING_PORT_PRI_S 29 -+ -+#define AR934X_REG_PORT_VLAN2(_port) (AR934X_REG_PORT_BASE((_port)) + 0x0c) -+#define AR934X_PORT_VLAN2_PORT_VID_MEM_S 16 -+#define AR934X_PORT_VLAN2_8021Q_MODE_S 30 -+#define AR934X_PORT_VLAN2_8021Q_MODE_PORT_ONLY 0 -+#define AR934X_PORT_VLAN2_8021Q_MODE_PORT_FALLBACK 1 -+#define AR934X_PORT_VLAN2_8021Q_MODE_VLAN_ONLY 2 -+#define AR934X_PORT_VLAN2_8021Q_MODE_SECURE 3 -+ -+#define sw_to_ar7240(_dev) container_of(_dev, struct ar7240sw, swdev) -+ -+struct ar7240sw_port_stat { -+ unsigned long rx_broadcast; -+ unsigned long rx_pause; -+ unsigned long rx_multicast; -+ unsigned long rx_fcs_error; -+ unsigned long rx_align_error; -+ unsigned long rx_runt; -+ unsigned long rx_fragments; -+ unsigned long rx_64byte; -+ unsigned long rx_128byte; -+ unsigned long rx_256byte; -+ unsigned long rx_512byte; -+ unsigned long rx_1024byte; -+ unsigned long rx_1518byte; -+ unsigned long rx_maxbyte; -+ unsigned long rx_toolong; -+ unsigned long rx_good_byte; -+ unsigned long rx_bad_byte; -+ unsigned long rx_overflow; -+ unsigned long filtered; -+ -+ unsigned long tx_broadcast; -+ unsigned long tx_pause; -+ unsigned long tx_multicast; -+ unsigned long tx_underrun; -+ unsigned long tx_64byte; -+ unsigned long tx_128byte; -+ unsigned long tx_256byte; -+ unsigned long tx_512byte; -+ unsigned long tx_1024byte; -+ unsigned long tx_1518byte; -+ unsigned long tx_maxbyte; -+ unsigned long tx_oversize; -+ unsigned long tx_byte; -+ unsigned long tx_collision; -+ unsigned long tx_abortcol; -+ unsigned long tx_multicol; -+ unsigned long tx_singlecol; -+ unsigned long tx_excdefer; -+ unsigned long tx_defer; -+ unsigned long tx_xlatecol; -+}; -+ -+struct ar7240sw { -+ struct mii_bus *mii_bus; -+ struct ag71xx_switch_platform_data *swdata; -+ struct switch_dev swdev; -+ int num_ports; -+ u8 ver; -+ bool vlan; -+ u16 vlan_id[AR7240_MAX_VLANS]; -+ u8 vlan_table[AR7240_MAX_VLANS]; -+ u8 vlan_tagged; -+ u16 pvid[AR7240_NUM_PORTS]; -+ char buf[80]; -+ -+ rwlock_t stats_lock; -+ struct ar7240sw_port_stat port_stats[AR7240_NUM_PORTS]; -+}; -+ -+struct ar7240sw_hw_stat { -+ char string[ETH_GSTRING_LEN]; -+ int sizeof_stat; -+ int reg; -+}; -+ -+static DEFINE_MUTEX(reg_mutex); -+ -+static inline int sw_is_ar7240(struct ar7240sw *as) -+{ -+ return as->ver == AR7240_MASK_CTRL_VERSION_AR7240; -+} -+ -+static inline int sw_is_ar934x(struct ar7240sw *as) -+{ -+ return as->ver == AR7240_MASK_CTRL_VERSION_AR934X; -+} -+ -+static inline u32 ar7240sw_port_mask(struct ar7240sw *as, int port) -+{ -+ return BIT(port); -+} -+ -+static inline u32 ar7240sw_port_mask_all(struct ar7240sw *as) -+{ -+ return BIT(as->swdev.ports) - 1; -+} -+ -+static inline u32 ar7240sw_port_mask_but(struct ar7240sw *as, int port) -+{ -+ return ar7240sw_port_mask_all(as) & ~BIT(port); -+} -+ -+static inline u16 mk_phy_addr(u32 reg) -+{ -+ return 0x17 & ((reg >> 4) | 0x10); -+} -+ -+static inline u16 mk_phy_reg(u32 reg) -+{ -+ return (reg << 1) & 0x1e; -+} -+ -+static inline u16 mk_high_addr(u32 reg) -+{ -+ return (reg >> 7) & 0x1ff; -+} -+ -+static u32 __ar7240sw_reg_read(struct mii_bus *mii, u32 reg) -+{ -+ unsigned long flags; -+ u16 phy_addr; -+ u16 phy_reg; -+ u32 hi, lo; -+ -+ reg = (reg & 0xfffffffc) >> 2; -+ phy_addr = mk_phy_addr(reg); -+ phy_reg = mk_phy_reg(reg); -+ -+ local_irq_save(flags); -+ ag71xx_mdio_mii_write(mii->priv, 0x1f, 0x10, mk_high_addr(reg)); -+ lo = (u32) ag71xx_mdio_mii_read(mii->priv, phy_addr, phy_reg); -+ hi = (u32) ag71xx_mdio_mii_read(mii->priv, phy_addr, phy_reg + 1); -+ local_irq_restore(flags); -+ -+ return (hi << 16) | lo; -+} -+ -+static void __ar7240sw_reg_write(struct mii_bus *mii, u32 reg, u32 val) -+{ -+ unsigned long flags; -+ u16 phy_addr; -+ u16 phy_reg; -+ -+ reg = (reg & 0xfffffffc) >> 2; -+ phy_addr = mk_phy_addr(reg); -+ phy_reg = mk_phy_reg(reg); -+ -+ local_irq_save(flags); -+ ag71xx_mdio_mii_write(mii->priv, 0x1f, 0x10, mk_high_addr(reg)); -+ ag71xx_mdio_mii_write(mii->priv, phy_addr, phy_reg + 1, (val >> 16)); -+ ag71xx_mdio_mii_write(mii->priv, phy_addr, phy_reg, (val & 0xffff)); -+ local_irq_restore(flags); -+} -+ -+static u32 ar7240sw_reg_read(struct mii_bus *mii, u32 reg_addr) -+{ -+ u32 ret; -+ -+ mutex_lock(®_mutex); -+ ret = __ar7240sw_reg_read(mii, reg_addr); -+ mutex_unlock(®_mutex); -+ -+ return ret; -+} -+ -+static void ar7240sw_reg_write(struct mii_bus *mii, u32 reg_addr, u32 reg_val) -+{ -+ mutex_lock(®_mutex); -+ __ar7240sw_reg_write(mii, reg_addr, reg_val); -+ mutex_unlock(®_mutex); -+} -+ -+static u32 ar7240sw_reg_rmw(struct mii_bus *mii, u32 reg, u32 mask, u32 val) -+{ -+ u32 t; -+ -+ mutex_lock(®_mutex); -+ t = __ar7240sw_reg_read(mii, reg); -+ t &= ~mask; -+ t |= val; -+ __ar7240sw_reg_write(mii, reg, t); -+ mutex_unlock(®_mutex); -+ -+ return t; -+} -+ -+static void ar7240sw_reg_set(struct mii_bus *mii, u32 reg, u32 val) -+{ -+ u32 t; -+ -+ mutex_lock(®_mutex); -+ t = __ar7240sw_reg_read(mii, reg); -+ t |= val; -+ __ar7240sw_reg_write(mii, reg, t); -+ mutex_unlock(®_mutex); -+} -+ -+static int __ar7240sw_reg_wait(struct mii_bus *mii, u32 reg, u32 mask, u32 val, -+ unsigned timeout) -+{ -+ int i; -+ -+ for (i = 0; i < timeout; i++) { -+ u32 t; -+ -+ t = __ar7240sw_reg_read(mii, reg); -+ if ((t & mask) == val) -+ return 0; -+ -+ msleep(1); -+ } -+ -+ return -ETIMEDOUT; -+} -+ -+static int ar7240sw_reg_wait(struct mii_bus *mii, u32 reg, u32 mask, u32 val, -+ unsigned timeout) -+{ -+ int ret; -+ -+ mutex_lock(®_mutex); -+ ret = __ar7240sw_reg_wait(mii, reg, mask, val, timeout); -+ mutex_unlock(®_mutex); -+ return ret; -+} -+ -+u16 ar7240sw_phy_read(struct mii_bus *mii, unsigned phy_addr, -+ unsigned reg_addr) -+{ -+ u32 t, val = 0xffff; -+ int err; -+ -+ if (phy_addr >= AR7240_NUM_PHYS) -+ return 0xffff; -+ -+ mutex_lock(®_mutex); -+ t = (reg_addr << AR7240_MDIO_CTRL_REG_ADDR_S) | -+ (phy_addr << AR7240_MDIO_CTRL_PHY_ADDR_S) | -+ AR7240_MDIO_CTRL_MASTER_EN | -+ AR7240_MDIO_CTRL_BUSY | -+ AR7240_MDIO_CTRL_CMD_READ; -+ -+ __ar7240sw_reg_write(mii, AR7240_REG_MDIO_CTRL, t); -+ err = __ar7240sw_reg_wait(mii, AR7240_REG_MDIO_CTRL, -+ AR7240_MDIO_CTRL_BUSY, 0, 5); -+ if (!err) -+ val = __ar7240sw_reg_read(mii, AR7240_REG_MDIO_CTRL); -+ mutex_unlock(®_mutex); -+ -+ return val & AR7240_MDIO_CTRL_DATA_M; -+} -+ -+int ar7240sw_phy_write(struct mii_bus *mii, unsigned phy_addr, -+ unsigned reg_addr, u16 reg_val) -+{ -+ u32 t; -+ int ret; -+ -+ if (phy_addr >= AR7240_NUM_PHYS) -+ return -EINVAL; -+ -+ mutex_lock(®_mutex); -+ t = (phy_addr << AR7240_MDIO_CTRL_PHY_ADDR_S) | -+ (reg_addr << AR7240_MDIO_CTRL_REG_ADDR_S) | -+ AR7240_MDIO_CTRL_MASTER_EN | -+ AR7240_MDIO_CTRL_BUSY | -+ AR7240_MDIO_CTRL_CMD_WRITE | -+ reg_val; -+ -+ __ar7240sw_reg_write(mii, AR7240_REG_MDIO_CTRL, t); -+ ret = __ar7240sw_reg_wait(mii, AR7240_REG_MDIO_CTRL, -+ AR7240_MDIO_CTRL_BUSY, 0, 5); -+ mutex_unlock(®_mutex); -+ -+ return ret; -+} -+ -+static int ar7240sw_capture_stats(struct ar7240sw *as) -+{ -+ struct mii_bus *mii = as->mii_bus; -+ int port; -+ int ret; -+ -+ write_lock(&as->stats_lock); -+ -+ /* Capture the hardware statistics for all ports */ -+ ar7240sw_reg_rmw(mii, AR7240_REG_MIB_FUNCTION0, -+ (AR7240_MIB_FUNC_M << AR7240_MIB_FUNC_S), -+ (AR7240_MIB_FUNC_CAPTURE << AR7240_MIB_FUNC_S)); -+ -+ /* Wait for the capturing to complete. */ -+ ret = ar7240sw_reg_wait(mii, AR7240_REG_MIB_FUNCTION0, -+ AR7240_MIB_BUSY, 0, 10); -+ -+ if (ret) -+ goto unlock; -+ -+ for (port = 0; port < AR7240_NUM_PORTS; port++) { -+ unsigned int base; -+ struct ar7240sw_port_stat *stats; -+ -+ base = AR7240_REG_STATS_BASE(port); -+ stats = &as->port_stats[port]; -+ -+#define READ_STAT(_r) ar7240sw_reg_read(mii, base + AR7240_STATS_ ## _r) -+ -+ stats->rx_good_byte += READ_STAT(RXGOODBYTE); -+ stats->tx_byte += READ_STAT(TXBYTE); -+ -+#undef READ_STAT -+ } -+ -+ ret = 0; -+ -+unlock: -+ write_unlock(&as->stats_lock); -+ return ret; -+} -+ -+static void ar7240sw_disable_port(struct ar7240sw *as, unsigned port) -+{ -+ ar7240sw_reg_write(as->mii_bus, AR7240_REG_PORT_CTRL(port), -+ AR7240_PORT_CTRL_STATE_DISABLED); -+} -+ -+static void ar7240sw_setup(struct ar7240sw *as) -+{ -+ struct mii_bus *mii = as->mii_bus; -+ -+ /* Enable CPU port, and disable mirror port */ -+ ar7240sw_reg_write(mii, AR7240_REG_CPU_PORT, -+ AR7240_CPU_PORT_EN | -+ (15 << AR7240_MIRROR_PORT_S)); -+ -+ /* Setup TAG priority mapping */ -+ ar7240sw_reg_write(mii, AR7240_REG_TAG_PRIORITY, 0xfa50); -+ -+ if (sw_is_ar934x(as)) { -+ /* Enable aging, MAC replacing */ -+ ar7240sw_reg_write(mii, AR934X_REG_AT_CTRL, -+ 0x2b /* 5 min age time */ | -+ AR934X_AT_CTRL_AGE_EN | -+ AR934X_AT_CTRL_LEARN_CHANGE); -+ /* Enable ARP frame acknowledge */ -+ ar7240sw_reg_set(mii, AR934X_REG_QM_CTRL, -+ AR934X_QM_CTRL_ARP_EN); -+ /* Enable Broadcast/Multicast frames transmitted to the CPU */ -+ ar7240sw_reg_set(mii, AR934X_REG_FLOOD_MASK, -+ AR934X_FLOOD_MASK_BC_DP(0) | -+ AR934X_FLOOD_MASK_MC_DP(0)); -+ -+ /* setup MTU */ -+ ar7240sw_reg_rmw(mii, AR7240_REG_GLOBAL_CTRL, -+ AR9340_GLOBAL_CTRL_MTU_M, -+ AR9340_GLOBAL_CTRL_MTU_M); -+ -+ /* Enable MIB counters */ -+ ar7240sw_reg_set(mii, AR7240_REG_MIB_FUNCTION0, -+ AR934X_MIB_ENABLE); -+ -+ } else { -+ /* Enable ARP frame acknowledge, aging, MAC replacing */ -+ ar7240sw_reg_write(mii, AR7240_REG_AT_CTRL, -+ AR7240_AT_CTRL_RESERVED | -+ 0x2b /* 5 min age time */ | -+ AR7240_AT_CTRL_AGE_EN | -+ AR7240_AT_CTRL_ARP_EN | -+ AR7240_AT_CTRL_LEARN_CHANGE); -+ /* Enable Broadcast frames transmitted to the CPU */ -+ ar7240sw_reg_set(mii, AR7240_REG_FLOOD_MASK, -+ AR7240_FLOOD_MASK_BROAD_TO_CPU); -+ -+ /* setup MTU */ -+ ar7240sw_reg_rmw(mii, AR7240_REG_GLOBAL_CTRL, -+ AR7240_GLOBAL_CTRL_MTU_M, -+ AR7240_GLOBAL_CTRL_MTU_M); -+ } -+ -+ /* setup Service TAG */ -+ ar7240sw_reg_rmw(mii, AR7240_REG_SERVICE_TAG, AR7240_SERVICE_TAG_M, 0); -+} -+ -+static int ar7240sw_reset(struct ar7240sw *as) -+{ -+ struct mii_bus *mii = as->mii_bus; -+ int ret; -+ int i; -+ -+ /* Set all ports to disabled state. */ -+ for (i = 0; i < AR7240_NUM_PORTS; i++) -+ ar7240sw_disable_port(as, i); -+ -+ /* Wait for transmit queues to drain. */ -+ msleep(2); -+ -+ /* Reset the switch. */ -+ ar7240sw_reg_write(mii, AR7240_REG_MASK_CTRL, -+ AR7240_MASK_CTRL_SOFT_RESET); -+ -+ ret = ar7240sw_reg_wait(mii, AR7240_REG_MASK_CTRL, -+ AR7240_MASK_CTRL_SOFT_RESET, 0, 1000); -+ -+ /* setup PHYs */ -+ for (i = 0; i < AR7240_NUM_PHYS; i++) { -+ ar7240sw_phy_write(mii, i, MII_ADVERTISE, -+ ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | -+ ADVERTISE_PAUSE_ASYM); -+ ar7240sw_phy_write(mii, i, MII_BMCR, -+ BMCR_RESET | BMCR_ANENABLE); -+ } -+ msleep(1000); -+ -+ ar7240sw_setup(as); -+ return ret; -+} -+ -+static void ar7240sw_setup_port(struct ar7240sw *as, unsigned port, u8 portmask) -+{ -+ struct mii_bus *mii = as->mii_bus; -+ u32 ctrl; -+ u32 vid, mode; -+ -+ ctrl = AR7240_PORT_CTRL_STATE_FORWARD | AR7240_PORT_CTRL_LEARN | -+ AR7240_PORT_CTRL_SINGLE_VLAN; -+ -+ if (port == AR7240_PORT_CPU) { -+ ar7240sw_reg_write(mii, AR7240_REG_PORT_STATUS(port), -+ AR7240_PORT_STATUS_SPEED_1000 | -+ AR7240_PORT_STATUS_TXFLOW | -+ AR7240_PORT_STATUS_RXFLOW | -+ AR7240_PORT_STATUS_TXMAC | -+ AR7240_PORT_STATUS_RXMAC | -+ AR7240_PORT_STATUS_DUPLEX); -+ } else { -+ ar7240sw_reg_write(mii, AR7240_REG_PORT_STATUS(port), -+ AR7240_PORT_STATUS_LINK_AUTO); -+ } -+ -+ /* Set the default VID for this port */ -+ if (as->vlan) { -+ vid = as->vlan_id[as->pvid[port]]; -+ mode = AR7240_PORT_VLAN_MODE_SECURE; -+ } else { -+ vid = port; -+ mode = AR7240_PORT_VLAN_MODE_PORT_ONLY; -+ } -+ -+ if (as->vlan) { -+ if (as->vlan_tagged & BIT(port)) -+ ctrl |= AR7240_PORT_CTRL_VLAN_MODE_ADD << -+ AR7240_PORT_CTRL_VLAN_MODE_S; -+ else -+ ctrl |= AR7240_PORT_CTRL_VLAN_MODE_STRIP << -+ AR7240_PORT_CTRL_VLAN_MODE_S; -+ } else { -+ ctrl |= AR7240_PORT_CTRL_VLAN_MODE_KEEP << -+ AR7240_PORT_CTRL_VLAN_MODE_S; -+ } -+ -+ if (!portmask) { -+ if (port == AR7240_PORT_CPU) -+ portmask = ar7240sw_port_mask_but(as, AR7240_PORT_CPU); -+ else -+ portmask = ar7240sw_port_mask(as, AR7240_PORT_CPU); -+ } -+ -+ /* allow the port to talk to all other ports, but exclude its -+ * own ID to prevent frames from being reflected back to the -+ * port that they came from */ -+ portmask &= ar7240sw_port_mask_but(as, port); -+ -+ ar7240sw_reg_write(mii, AR7240_REG_PORT_CTRL(port), ctrl); -+ if (sw_is_ar934x(as)) { -+ u32 vlan1, vlan2; -+ -+ vlan1 = (vid << AR934X_PORT_VLAN1_DEFAULT_CVID_S); -+ vlan2 = (portmask << AR934X_PORT_VLAN2_PORT_VID_MEM_S) | -+ (mode << AR934X_PORT_VLAN2_8021Q_MODE_S); -+ ar7240sw_reg_write(mii, AR934X_REG_PORT_VLAN1(port), vlan1); -+ ar7240sw_reg_write(mii, AR934X_REG_PORT_VLAN2(port), vlan2); -+ } else { -+ u32 vlan; -+ -+ vlan = vid | (mode << AR7240_PORT_VLAN_MODE_S) | -+ (portmask << AR7240_PORT_VLAN_DEST_PORTS_S); -+ -+ ar7240sw_reg_write(mii, AR7240_REG_PORT_VLAN(port), vlan); -+ } -+} -+ -+static int ar7240_set_addr(struct ar7240sw *as, u8 *addr) -+{ -+ struct mii_bus *mii = as->mii_bus; -+ u32 t; -+ -+ t = (addr[4] << 8) | addr[5]; -+ ar7240sw_reg_write(mii, AR7240_REG_MAC_ADDR0, t); -+ -+ t = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; -+ ar7240sw_reg_write(mii, AR7240_REG_MAC_ADDR1, t); -+ -+ return 0; -+} -+ -+static int -+ar7240_set_vid(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar7240sw *as = sw_to_ar7240(dev); -+ as->vlan_id[val->port_vlan] = val->value.i; -+ return 0; -+} -+ -+static int -+ar7240_get_vid(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar7240sw *as = sw_to_ar7240(dev); -+ val->value.i = as->vlan_id[val->port_vlan]; -+ return 0; -+} -+ -+static int -+ar7240_set_pvid(struct switch_dev *dev, int port, int vlan) -+{ -+ struct ar7240sw *as = sw_to_ar7240(dev); -+ -+ /* make sure no invalid PVIDs get set */ -+ -+ if (vlan >= dev->vlans) -+ return -EINVAL; -+ -+ as->pvid[port] = vlan; -+ return 0; -+} -+ -+static int -+ar7240_get_pvid(struct switch_dev *dev, int port, int *vlan) -+{ -+ struct ar7240sw *as = sw_to_ar7240(dev); -+ *vlan = as->pvid[port]; -+ return 0; -+} -+ -+static int -+ar7240_get_ports(struct switch_dev *dev, struct switch_val *val) -+{ -+ struct ar7240sw *as = sw_to_ar7240(dev); -+ u8 ports = as->vlan_table[val->port_vlan]; -+ int i; -+ -+ val->len = 0; -+ for (i = 0; i < as->swdev.ports; i++) { -+ struct switch_port *p; -+ -+ if (!(ports & (1 << i))) -+ continue; -+ -+ p = &val->value.ports[val->len++]; -+ p->id = i; -+ if (as->vlan_tagged & (1 << i)) -+ p->flags = (1 << SWITCH_PORT_FLAG_TAGGED); -+ else -+ p->flags = 0; -+ } -+ return 0; -+} -+ -+static int -+ar7240_set_ports(struct switch_dev *dev, struct switch_val *val) -+{ -+ struct ar7240sw *as = sw_to_ar7240(dev); -+ u8 *vt = &as->vlan_table[val->port_vlan]; -+ int i, j; -+ -+ *vt = 0; -+ for (i = 0; i < val->len; i++) { -+ struct switch_port *p = &val->value.ports[i]; -+ -+ if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) -+ as->vlan_tagged |= (1 << p->id); -+ else { -+ as->vlan_tagged &= ~(1 << p->id); -+ as->pvid[p->id] = val->port_vlan; -+ -+ /* make sure that an untagged port does not -+ * appear in other vlans */ -+ for (j = 0; j < AR7240_MAX_VLANS; j++) { -+ if (j == val->port_vlan) -+ continue; -+ as->vlan_table[j] &= ~(1 << p->id); -+ } -+ } -+ -+ *vt |= 1 << p->id; -+ } -+ return 0; -+} -+ -+static int -+ar7240_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar7240sw *as = sw_to_ar7240(dev); -+ as->vlan = !!val->value.i; -+ return 0; -+} -+ -+static int -+ar7240_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar7240sw *as = sw_to_ar7240(dev); -+ val->value.i = as->vlan; -+ return 0; -+} -+ -+static void -+ar7240_vtu_op(struct ar7240sw *as, u32 op, u32 val) -+{ -+ struct mii_bus *mii = as->mii_bus; -+ -+ if (ar7240sw_reg_wait(mii, AR7240_REG_VTU, AR7240_VTU_ACTIVE, 0, 5)) -+ return; -+ -+ if ((op & AR7240_VTU_OP) == AR7240_VTU_OP_LOAD) { -+ val &= AR7240_VTUDATA_MEMBER; -+ val |= AR7240_VTUDATA_VALID; -+ ar7240sw_reg_write(mii, AR7240_REG_VTU_DATA, val); -+ } -+ op |= AR7240_VTU_ACTIVE; -+ ar7240sw_reg_write(mii, AR7240_REG_VTU, op); -+} -+ -+static int -+ar7240_hw_apply(struct switch_dev *dev) -+{ -+ struct ar7240sw *as = sw_to_ar7240(dev); -+ u8 portmask[AR7240_NUM_PORTS]; -+ int i, j; -+ -+ /* flush all vlan translation unit entries */ -+ ar7240_vtu_op(as, AR7240_VTU_OP_FLUSH, 0); -+ -+ memset(portmask, 0, sizeof(portmask)); -+ if (as->vlan) { -+ /* calculate the port destination masks and load vlans -+ * into the vlan translation unit */ -+ for (j = 0; j < AR7240_MAX_VLANS; j++) { -+ u8 vp = as->vlan_table[j]; -+ -+ if (!vp) -+ continue; -+ -+ for (i = 0; i < as->swdev.ports; i++) { -+ u8 mask = (1 << i); -+ if (vp & mask) -+ portmask[i] |= vp & ~mask; -+ } -+ -+ ar7240_vtu_op(as, -+ AR7240_VTU_OP_LOAD | -+ (as->vlan_id[j] << AR7240_VTU_VID_S), -+ as->vlan_table[j]); -+ } -+ } else { -+ /* vlan disabled: -+ * isolate all ports, but connect them to the cpu port */ -+ for (i = 0; i < as->swdev.ports; i++) { -+ if (i == AR7240_PORT_CPU) -+ continue; -+ -+ portmask[i] = 1 << AR7240_PORT_CPU; -+ portmask[AR7240_PORT_CPU] |= (1 << i); -+ } -+ } -+ -+ /* update the port destination mask registers and tag settings */ -+ for (i = 0; i < as->swdev.ports; i++) -+ ar7240sw_setup_port(as, i, portmask[i]); -+ -+ return 0; -+} -+ -+static int -+ar7240_reset_switch(struct switch_dev *dev) -+{ -+ struct ar7240sw *as = sw_to_ar7240(dev); -+ ar7240sw_reset(as); -+ return 0; -+} -+ -+static int -+ar7240_get_port_link(struct switch_dev *dev, int port, -+ struct switch_port_link *link) -+{ -+ struct ar7240sw *as = sw_to_ar7240(dev); -+ struct mii_bus *mii = as->mii_bus; -+ u32 status; -+ -+ if (port > AR7240_NUM_PORTS) -+ return -EINVAL; -+ -+ status = ar7240sw_reg_read(mii, AR7240_REG_PORT_STATUS(port)); -+ link->aneg = !!(status & AR7240_PORT_STATUS_LINK_AUTO); -+ if (link->aneg) { -+ link->link = !!(status & AR7240_PORT_STATUS_LINK_UP); -+ if (!link->link) -+ return 0; -+ } else { -+ link->link = true; -+ } -+ -+ link->duplex = !!(status & AR7240_PORT_STATUS_DUPLEX); -+ link->tx_flow = !!(status & AR7240_PORT_STATUS_TXFLOW); -+ link->rx_flow = !!(status & AR7240_PORT_STATUS_RXFLOW); -+ switch (status & AR7240_PORT_STATUS_SPEED_M) { -+ case AR7240_PORT_STATUS_SPEED_10: -+ link->speed = SWITCH_PORT_SPEED_10; -+ break; -+ case AR7240_PORT_STATUS_SPEED_100: -+ link->speed = SWITCH_PORT_SPEED_100; -+ break; -+ case AR7240_PORT_STATUS_SPEED_1000: -+ link->speed = SWITCH_PORT_SPEED_1000; -+ break; -+ } -+ -+ return 0; -+} -+ -+static int -+ar7240_get_port_stats(struct switch_dev *dev, int port, -+ struct switch_port_stats *stats) -+{ -+ struct ar7240sw *as = sw_to_ar7240(dev); -+ -+ if (port > AR7240_NUM_PORTS) -+ return -EINVAL; -+ -+ ar7240sw_capture_stats(as); -+ -+ read_lock(&as->stats_lock); -+ stats->rx_bytes = as->port_stats[port].rx_good_byte; -+ stats->tx_bytes = as->port_stats[port].tx_byte; -+ read_unlock(&as->stats_lock); -+ -+ return 0; -+} -+ -+static struct switch_attr ar7240_globals[] = { -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_vlan", -+ .description = "Enable VLAN mode", -+ .set = ar7240_set_vlan, -+ .get = ar7240_get_vlan, -+ .max = 1 -+ }, -+}; -+ -+static struct switch_attr ar7240_port[] = { -+}; -+ -+static struct switch_attr ar7240_vlan[] = { -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "vid", -+ .description = "VLAN ID", -+ .set = ar7240_set_vid, -+ .get = ar7240_get_vid, -+ .max = 4094, -+ }, -+}; -+ -+static const struct switch_dev_ops ar7240_ops = { -+ .attr_global = { -+ .attr = ar7240_globals, -+ .n_attr = ARRAY_SIZE(ar7240_globals), -+ }, -+ .attr_port = { -+ .attr = ar7240_port, -+ .n_attr = ARRAY_SIZE(ar7240_port), -+ }, -+ .attr_vlan = { -+ .attr = ar7240_vlan, -+ .n_attr = ARRAY_SIZE(ar7240_vlan), -+ }, -+ .get_port_pvid = ar7240_get_pvid, -+ .set_port_pvid = ar7240_set_pvid, -+ .get_vlan_ports = ar7240_get_ports, -+ .set_vlan_ports = ar7240_set_ports, -+ .apply_config = ar7240_hw_apply, -+ .reset_switch = ar7240_reset_switch, -+ .get_port_link = ar7240_get_port_link, -+ .get_port_stats = ar7240_get_port_stats, -+}; -+ -+static struct ar7240sw *ar7240_probe(struct ag71xx *ag) -+{ -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ struct mii_bus *mii = ag->mii_bus; -+ struct ar7240sw *as; -+ struct switch_dev *swdev; -+ u32 ctrl; -+ u16 phy_id1; -+ u16 phy_id2; -+ int i; -+ -+ phy_id1 = ar7240sw_phy_read(mii, 0, MII_PHYSID1); -+ phy_id2 = ar7240sw_phy_read(mii, 0, MII_PHYSID2); -+ if ((phy_id1 != AR7240_PHY_ID1 || phy_id2 != AR7240_PHY_ID2) && -+ (phy_id1 != AR934X_PHY_ID1 || phy_id2 != AR934X_PHY_ID2)) { -+ pr_err("%s: unknown phy id '%04x:%04x'\n", -+ dev_name(&mii->dev), phy_id1, phy_id2); -+ return NULL; -+ } -+ -+ as = kzalloc(sizeof(*as), GFP_KERNEL); -+ if (!as) -+ return NULL; -+ -+ as->mii_bus = mii; -+ as->swdata = pdata->switch_data; -+ -+ swdev = &as->swdev; -+ -+ ctrl = ar7240sw_reg_read(mii, AR7240_REG_MASK_CTRL); -+ as->ver = (ctrl >> AR7240_MASK_CTRL_VERSION_S) & -+ AR7240_MASK_CTRL_VERSION_M; -+ -+ if (sw_is_ar7240(as)) { -+ swdev->name = "AR7240/AR9330 built-in switch"; -+ swdev->ports = AR7240_NUM_PORTS - 1; -+ } else if (sw_is_ar934x(as)) { -+ swdev->name = "AR934X built-in switch"; -+ -+ if (pdata->phy_if_mode == PHY_INTERFACE_MODE_GMII) { -+ ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE0, -+ AR934X_OPER_MODE0_MAC_GMII_EN); -+ } else if (pdata->phy_if_mode == PHY_INTERFACE_MODE_MII) { -+ ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE0, -+ AR934X_OPER_MODE0_PHY_MII_EN); -+ } else { -+ pr_err("%s: invalid PHY interface mode\n", -+ dev_name(&mii->dev)); -+ goto err_free; -+ } -+ -+ if (as->swdata->phy4_mii_en) { -+ ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE1, -+ AR934X_REG_OPER_MODE1_PHY4_MII_EN); -+ swdev->ports = AR7240_NUM_PORTS - 1; -+ } else { -+ swdev->ports = AR7240_NUM_PORTS; -+ } -+ } else { -+ pr_err("%s: unsupported chip, ctrl=%08x\n", -+ dev_name(&mii->dev), ctrl); -+ goto err_free; -+ } -+ -+ swdev->cpu_port = AR7240_PORT_CPU; -+ swdev->vlans = AR7240_MAX_VLANS; -+ swdev->ops = &ar7240_ops; -+ -+ if (register_switch(&as->swdev, ag->dev) < 0) -+ goto err_free; -+ -+ pr_info("%s: Found an %s\n", dev_name(&mii->dev), swdev->name); -+ -+ /* initialize defaults */ -+ for (i = 0; i < AR7240_MAX_VLANS; i++) -+ as->vlan_id[i] = i; -+ -+ as->vlan_table[0] = ar7240sw_port_mask_all(as); -+ -+ return as; -+ -+err_free: -+ kfree(as); -+ return NULL; -+} -+ -+static void link_function(struct work_struct *work) { -+ struct ag71xx *ag = container_of(work, struct ag71xx, link_work.work); -+ struct ar7240sw *as = ag->phy_priv; -+ unsigned long flags; -+ u8 mask; -+ int i; -+ int status = 0; -+ -+ mask = ~as->swdata->phy_poll_mask; -+ for (i = 0; i < AR7240_NUM_PHYS; i++) { -+ int link; -+ -+ if (!(mask & BIT(i))) -+ continue; -+ -+ link = ar7240sw_phy_read(ag->mii_bus, i, MII_BMSR); -+ if (link & BMSR_LSTATUS) { -+ status = 1; -+ break; -+ } -+ } -+ -+ spin_lock_irqsave(&ag->lock, flags); -+ if (status != ag->link) { -+ ag->link = status; -+ ag71xx_link_adjust(ag); -+ } -+ spin_unlock_irqrestore(&ag->lock, flags); -+ -+ schedule_delayed_work(&ag->link_work, HZ / 2); -+} -+ -+void ag71xx_ar7240_start(struct ag71xx *ag) -+{ -+ struct ar7240sw *as = ag->phy_priv; -+ -+ ar7240sw_reset(as); -+ -+ ag->speed = SPEED_1000; -+ ag->duplex = 1; -+ -+ ar7240_set_addr(as, ag->dev->dev_addr); -+ ar7240_hw_apply(&as->swdev); -+ -+ schedule_delayed_work(&ag->link_work, HZ / 10); -+} -+ -+void ag71xx_ar7240_stop(struct ag71xx *ag) -+{ -+ cancel_delayed_work_sync(&ag->link_work); -+} -+ -+int ag71xx_ar7240_init(struct ag71xx *ag) -+{ -+ struct ar7240sw *as; -+ -+ as = ar7240_probe(ag); -+ if (!as) -+ return -ENODEV; -+ -+ ag->phy_priv = as; -+ ar7240sw_reset(as); -+ -+ rwlock_init(&as->stats_lock); -+ INIT_DELAYED_WORK(&ag->link_work, link_function); -+ -+ return 0; -+} -+ -+void ag71xx_ar7240_cleanup(struct ag71xx *ag) -+{ -+ struct ar7240sw *as = ag->phy_priv; -+ -+ if (!as) -+ return; -+ -+ unregister_switch(&as->swdev); -+ kfree(as); -+ ag->phy_priv = NULL; -+} -diff --git a/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar8216.c b/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar8216.c -new file mode 100644 -index 0000000..7ec43b7 ---- /dev/null -+++ b/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar8216.c -@@ -0,0 +1,44 @@ -+/* -+ * Atheros AR71xx built-in ethernet mac driver -+ * Special support for the Atheros ar8216 switch chip -+ * -+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> -+ * -+ * Based on Atheros' AG7100 driver -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include "ag71xx.h" -+ -+#define AR8216_PACKET_TYPE_MASK 0xf -+#define AR8216_PACKET_TYPE_NORMAL 0 -+ -+#define AR8216_HEADER_LEN 2 -+ -+void ag71xx_add_ar8216_header(struct ag71xx *ag, struct sk_buff *skb) -+{ -+ skb_push(skb, AR8216_HEADER_LEN); -+ skb->data[0] = 0x10; -+ skb->data[1] = 0x80; -+} -+ -+int ag71xx_remove_ar8216_header(struct ag71xx *ag, struct sk_buff *skb, -+ int pktlen) -+{ -+ u8 type; -+ -+ type = skb->data[1] & AR8216_PACKET_TYPE_MASK; -+ switch (type) { -+ case AR8216_PACKET_TYPE_NORMAL: -+ break; -+ -+ default: -+ return -EINVAL; -+ } -+ -+ skb_pull(skb, AR8216_HEADER_LEN); -+ return 0; -+} -diff --git a/drivers/net/ethernet/atheros/ag71xx/ag71xx_debugfs.c b/drivers/net/ethernet/atheros/ag71xx/ag71xx_debugfs.c -new file mode 100644 -index 0000000..757a572 ---- /dev/null -+++ b/drivers/net/ethernet/atheros/ag71xx/ag71xx_debugfs.c -@@ -0,0 +1,284 @@ -+/* -+ * Atheros AR71xx built-in ethernet mac driver -+ * -+ * Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org> -+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> -+ * -+ * Based on Atheros' AG7100 driver -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include <linux/debugfs.h> -+ -+#include "ag71xx.h" -+ -+static struct dentry *ag71xx_debugfs_root; -+ -+static int ag71xx_debugfs_generic_open(struct inode *inode, struct file *file) -+{ -+ file->private_data = inode->i_private; -+ return 0; -+} -+ -+void ag71xx_debugfs_update_int_stats(struct ag71xx *ag, u32 status) -+{ -+ if (status) -+ ag->debug.int_stats.total++; -+ if (status & AG71XX_INT_TX_PS) -+ ag->debug.int_stats.tx_ps++; -+ if (status & AG71XX_INT_TX_UR) -+ ag->debug.int_stats.tx_ur++; -+ if (status & AG71XX_INT_TX_BE) -+ ag->debug.int_stats.tx_be++; -+ if (status & AG71XX_INT_RX_PR) -+ ag->debug.int_stats.rx_pr++; -+ if (status & AG71XX_INT_RX_OF) -+ ag->debug.int_stats.rx_of++; -+ if (status & AG71XX_INT_RX_BE) -+ ag->debug.int_stats.rx_be++; -+} -+ -+static ssize_t read_file_int_stats(struct file *file, char __user *user_buf, -+ size_t count, loff_t *ppos) -+{ -+#define PR_INT_STAT(_label, _field) \ -+ len += snprintf(buf + len, sizeof(buf) - len, \ -+ "%20s: %10lu\n", _label, ag->debug.int_stats._field); -+ -+ struct ag71xx *ag = file->private_data; -+ char buf[256]; -+ unsigned int len = 0; -+ -+ PR_INT_STAT("TX Packet Sent", tx_ps); -+ PR_INT_STAT("TX Underrun", tx_ur); -+ PR_INT_STAT("TX Bus Error", tx_be); -+ PR_INT_STAT("RX Packet Received", rx_pr); -+ PR_INT_STAT("RX Overflow", rx_of); -+ PR_INT_STAT("RX Bus Error", rx_be); -+ len += snprintf(buf + len, sizeof(buf) - len, "\n"); -+ PR_INT_STAT("Total", total); -+ -+ return simple_read_from_buffer(user_buf, count, ppos, buf, len); -+#undef PR_INT_STAT -+} -+ -+static const struct file_operations ag71xx_fops_int_stats = { -+ .open = ag71xx_debugfs_generic_open, -+ .read = read_file_int_stats, -+ .owner = THIS_MODULE -+}; -+ -+void ag71xx_debugfs_update_napi_stats(struct ag71xx *ag, int rx, int tx) -+{ -+ struct ag71xx_napi_stats *stats = &ag->debug.napi_stats; -+ -+ if (rx) { -+ stats->rx_count++; -+ stats->rx_packets += rx; -+ if (rx <= AG71XX_NAPI_WEIGHT) -+ stats->rx[rx]++; -+ if (rx > stats->rx_packets_max) -+ stats->rx_packets_max = rx; -+ } -+ -+ if (tx) { -+ stats->tx_count++; -+ stats->tx_packets += tx; -+ if (tx <= AG71XX_NAPI_WEIGHT) -+ stats->tx[tx]++; -+ if (tx > stats->tx_packets_max) -+ stats->tx_packets_max = tx; -+ } -+} -+ -+static ssize_t read_file_napi_stats(struct file *file, char __user *user_buf, -+ size_t count, loff_t *ppos) -+{ -+ struct ag71xx *ag = file->private_data; -+ struct ag71xx_napi_stats *stats = &ag->debug.napi_stats; -+ char *buf; -+ unsigned int buflen; -+ unsigned int len = 0; -+ unsigned long rx_avg = 0; -+ unsigned long tx_avg = 0; -+ int ret; -+ int i; -+ -+ buflen = 2048; -+ buf = kmalloc(buflen, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ if (stats->rx_count) -+ rx_avg = stats->rx_packets / stats->rx_count; -+ -+ if (stats->tx_count) -+ tx_avg = stats->tx_packets / stats->tx_count; -+ -+ len += snprintf(buf + len, buflen - len, "%3s %10s %10s\n", -+ "len", "rx", "tx"); -+ -+ for (i = 1; i <= AG71XX_NAPI_WEIGHT; i++) -+ len += snprintf(buf + len, buflen - len, -+ "%3d: %10lu %10lu\n", -+ i, stats->rx[i], stats->tx[i]); -+ -+ len += snprintf(buf + len, buflen - len, "\n"); -+ -+ len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", -+ "sum", stats->rx_count, stats->tx_count); -+ len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", -+ "avg", rx_avg, tx_avg); -+ len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", -+ "max", stats->rx_packets_max, stats->tx_packets_max); -+ len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", -+ "pkt", stats->rx_packets, stats->tx_packets); -+ -+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); -+ kfree(buf); -+ -+ return ret; -+} -+ -+static const struct file_operations ag71xx_fops_napi_stats = { -+ .open = ag71xx_debugfs_generic_open, -+ .read = read_file_napi_stats, -+ .owner = THIS_MODULE -+}; -+ -+#define DESC_PRINT_LEN 64 -+ -+static ssize_t read_file_ring(struct file *file, char __user *user_buf, -+ size_t count, loff_t *ppos, -+ struct ag71xx *ag, -+ struct ag71xx_ring *ring, -+ unsigned desc_reg) -+{ -+ char *buf; -+ unsigned int buflen; -+ unsigned int len = 0; -+ unsigned long flags; -+ ssize_t ret; -+ int curr; -+ int dirty; -+ u32 desc_hw; -+ int i; -+ -+ buflen = (ring->size * DESC_PRINT_LEN); -+ buf = kmalloc(buflen, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ len += snprintf(buf + len, buflen - len, -+ "Idx ... %-8s %-8s %-8s %-8s . %-10s\n", -+ "desc", "next", "data", "ctrl", "timestamp"); -+ -+ spin_lock_irqsave(&ag->lock, flags); -+ -+ curr = (ring->curr % ring->size); -+ dirty = (ring->dirty % ring->size); -+ desc_hw = ag71xx_rr(ag, desc_reg); -+ for (i = 0; i < ring->size; i++) { -+ struct ag71xx_buf *ab = &ring->buf[i]; -+ u32 desc_dma = ((u32) ring->descs_dma) + i * ring->desc_size; -+ -+ len += snprintf(buf + len, buflen - len, -+ "%3d %c%c%c %08x %08x %08x %08x %c %10lu\n", -+ i, -+ (i == curr) ? 'C' : ' ', -+ (i == dirty) ? 'D' : ' ', -+ (desc_hw == desc_dma) ? 'H' : ' ', -+ desc_dma, -+ ab->desc->next, -+ ab->desc->data, -+ ab->desc->ctrl, -+ (ab->desc->ctrl & DESC_EMPTY) ? 'E' : '*', -+ ab->timestamp); -+ } -+ -+ spin_unlock_irqrestore(&ag->lock, flags); -+ -+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); -+ kfree(buf); -+ -+ return ret; -+} -+ -+static ssize_t read_file_tx_ring(struct file *file, char __user *user_buf, -+ size_t count, loff_t *ppos) -+{ -+ struct ag71xx *ag = file->private_data; -+ -+ return read_file_ring(file, user_buf, count, ppos, ag, &ag->tx_ring, -+ AG71XX_REG_TX_DESC); -+} -+ -+static const struct file_operations ag71xx_fops_tx_ring = { -+ .open = ag71xx_debugfs_generic_open, -+ .read = read_file_tx_ring, -+ .owner = THIS_MODULE -+}; -+ -+static ssize_t read_file_rx_ring(struct file *file, char __user *user_buf, -+ size_t count, loff_t *ppos) -+{ -+ struct ag71xx *ag = file->private_data; -+ -+ return read_file_ring(file, user_buf, count, ppos, ag, &ag->rx_ring, -+ AG71XX_REG_RX_DESC); -+} -+ -+static const struct file_operations ag71xx_fops_rx_ring = { -+ .open = ag71xx_debugfs_generic_open, -+ .read = read_file_rx_ring, -+ .owner = THIS_MODULE -+}; -+ -+void ag71xx_debugfs_exit(struct ag71xx *ag) -+{ -+ debugfs_remove_recursive(ag->debug.debugfs_dir); -+} -+ -+int ag71xx_debugfs_init(struct ag71xx *ag) -+{ -+ struct device *dev = &ag->pdev->dev; -+ -+ ag->debug.debugfs_dir = debugfs_create_dir(dev_name(dev), -+ ag71xx_debugfs_root); -+ if (!ag->debug.debugfs_dir) { -+ dev_err(dev, "unable to create debugfs directory\n"); -+ return -ENOENT; -+ } -+ -+ debugfs_create_file("int_stats", S_IRUGO, ag->debug.debugfs_dir, -+ ag, &ag71xx_fops_int_stats); -+ debugfs_create_file("napi_stats", S_IRUGO, ag->debug.debugfs_dir, -+ ag, &ag71xx_fops_napi_stats); -+ debugfs_create_file("tx_ring", S_IRUGO, ag->debug.debugfs_dir, -+ ag, &ag71xx_fops_tx_ring); -+ debugfs_create_file("rx_ring", S_IRUGO, ag->debug.debugfs_dir, -+ ag, &ag71xx_fops_rx_ring); -+ -+ return 0; -+} -+ -+int ag71xx_debugfs_root_init(void) -+{ -+ if (ag71xx_debugfs_root) -+ return -EBUSY; -+ -+ ag71xx_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); -+ if (!ag71xx_debugfs_root) -+ return -ENOENT; -+ -+ return 0; -+} -+ -+void ag71xx_debugfs_root_exit(void) -+{ -+ debugfs_remove(ag71xx_debugfs_root); -+ ag71xx_debugfs_root = NULL; -+} -diff --git a/drivers/net/ethernet/atheros/ag71xx/ag71xx_ethtool.c b/drivers/net/ethernet/atheros/ag71xx/ag71xx_ethtool.c -new file mode 100644 -index 0000000..498fbed ---- /dev/null -+++ b/drivers/net/ethernet/atheros/ag71xx/ag71xx_ethtool.c -@@ -0,0 +1,124 @@ -+/* -+ * Atheros AR71xx built-in ethernet mac driver -+ * -+ * Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org> -+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> -+ * -+ * Based on Atheros' AG7100 driver -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include "ag71xx.h" -+ -+static int ag71xx_ethtool_get_settings(struct net_device *dev, -+ struct ethtool_cmd *cmd) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ struct phy_device *phydev = ag->phy_dev; -+ -+ if (!phydev) -+ return -ENODEV; -+ -+ return phy_ethtool_gset(phydev, cmd); -+} -+ -+static int ag71xx_ethtool_set_settings(struct net_device *dev, -+ struct ethtool_cmd *cmd) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ struct phy_device *phydev = ag->phy_dev; -+ -+ if (!phydev) -+ return -ENODEV; -+ -+ return phy_ethtool_sset(phydev, cmd); -+} -+ -+static void ag71xx_ethtool_get_drvinfo(struct net_device *dev, -+ struct ethtool_drvinfo *info) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ -+ strcpy(info->driver, ag->pdev->dev.driver->name); -+ strcpy(info->version, AG71XX_DRV_VERSION); -+ strcpy(info->bus_info, dev_name(&ag->pdev->dev)); -+} -+ -+static u32 ag71xx_ethtool_get_msglevel(struct net_device *dev) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ -+ return ag->msg_enable; -+} -+ -+static void ag71xx_ethtool_set_msglevel(struct net_device *dev, u32 msg_level) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ -+ ag->msg_enable = msg_level; -+} -+ -+static void ag71xx_ethtool_get_ringparam(struct net_device *dev, -+ struct ethtool_ringparam *er) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ -+ er->tx_max_pending = AG71XX_TX_RING_SIZE_MAX; -+ er->rx_max_pending = AG71XX_RX_RING_SIZE_MAX; -+ er->rx_mini_max_pending = 0; -+ er->rx_jumbo_max_pending = 0; -+ -+ er->tx_pending = ag->tx_ring.size; -+ er->rx_pending = ag->rx_ring.size; -+ er->rx_mini_pending = 0; -+ er->rx_jumbo_pending = 0; -+} -+ -+static int ag71xx_ethtool_set_ringparam(struct net_device *dev, -+ struct ethtool_ringparam *er) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ unsigned tx_size; -+ unsigned rx_size; -+ int err; -+ -+ if (er->rx_mini_pending != 0|| -+ er->rx_jumbo_pending != 0 || -+ er->rx_pending == 0 || -+ er->tx_pending == 0) -+ return -EINVAL; -+ -+ tx_size = er->tx_pending < AG71XX_TX_RING_SIZE_MAX ? -+ er->tx_pending : AG71XX_TX_RING_SIZE_MAX; -+ -+ rx_size = er->rx_pending < AG71XX_RX_RING_SIZE_MAX ? -+ er->rx_pending : AG71XX_RX_RING_SIZE_MAX; -+ -+ if (netif_running(dev)) { -+ err = dev->netdev_ops->ndo_stop(dev); -+ if (err) -+ return err; -+ } -+ -+ ag->tx_ring.size = tx_size; -+ ag->rx_ring.size = rx_size; -+ -+ if (netif_running(dev)) -+ err = dev->netdev_ops->ndo_open(dev); -+ -+ return err; -+} -+ -+struct ethtool_ops ag71xx_ethtool_ops = { -+ .set_settings = ag71xx_ethtool_set_settings, -+ .get_settings = ag71xx_ethtool_get_settings, -+ .get_drvinfo = ag71xx_ethtool_get_drvinfo, -+ .get_msglevel = ag71xx_ethtool_get_msglevel, -+ .set_msglevel = ag71xx_ethtool_set_msglevel, -+ .get_ringparam = ag71xx_ethtool_get_ringparam, -+ .set_ringparam = ag71xx_ethtool_set_ringparam, -+ .get_link = ethtool_op_get_link, -+}; -diff --git a/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c b/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c -new file mode 100644 -index 0000000..d010373 ---- /dev/null -+++ b/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c -@@ -0,0 +1,1325 @@ -+/* -+ * Atheros AR71xx built-in ethernet mac driver -+ * -+ * Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org> -+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> -+ * -+ * Based on Atheros' AG7100 driver -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include "ag71xx.h" -+ -+#define AG71XX_DEFAULT_MSG_ENABLE \ -+ (NETIF_MSG_DRV \ -+ | NETIF_MSG_PROBE \ -+ | NETIF_MSG_LINK \ -+ | NETIF_MSG_TIMER \ -+ | NETIF_MSG_IFDOWN \ -+ | NETIF_MSG_IFUP \ -+ | NETIF_MSG_RX_ERR \ -+ | NETIF_MSG_TX_ERR) -+ -+static int ag71xx_msg_level = -1; -+ -+module_param_named(msg_level, ag71xx_msg_level, int, 0); -+MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)"); -+ -+#define ETH_SWITCH_HEADER_LEN 2 -+ -+static inline unsigned int ag71xx_max_frame_len(unsigned int mtu) -+{ -+ return ETH_SWITCH_HEADER_LEN + ETH_HLEN + VLAN_HLEN + mtu + ETH_FCS_LEN; -+} -+ -+static void ag71xx_dump_dma_regs(struct ag71xx *ag) -+{ -+ DBG("%s: dma_tx_ctrl=%08x, dma_tx_desc=%08x, dma_tx_status=%08x\n", -+ ag->dev->name, -+ ag71xx_rr(ag, AG71XX_REG_TX_CTRL), -+ ag71xx_rr(ag, AG71XX_REG_TX_DESC), -+ ag71xx_rr(ag, AG71XX_REG_TX_STATUS)); -+ -+ DBG("%s: dma_rx_ctrl=%08x, dma_rx_desc=%08x, dma_rx_status=%08x\n", -+ ag->dev->name, -+ ag71xx_rr(ag, AG71XX_REG_RX_CTRL), -+ ag71xx_rr(ag, AG71XX_REG_RX_DESC), -+ ag71xx_rr(ag, AG71XX_REG_RX_STATUS)); -+} -+ -+static void ag71xx_dump_regs(struct ag71xx *ag) -+{ -+ DBG("%s: mac_cfg1=%08x, mac_cfg2=%08x, ipg=%08x, hdx=%08x, mfl=%08x\n", -+ ag->dev->name, -+ ag71xx_rr(ag, AG71XX_REG_MAC_CFG1), -+ ag71xx_rr(ag, AG71XX_REG_MAC_CFG2), -+ ag71xx_rr(ag, AG71XX_REG_MAC_IPG), -+ ag71xx_rr(ag, AG71XX_REG_MAC_HDX), -+ ag71xx_rr(ag, AG71XX_REG_MAC_MFL)); -+ DBG("%s: mac_ifctl=%08x, mac_addr1=%08x, mac_addr2=%08x\n", -+ ag->dev->name, -+ ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL), -+ ag71xx_rr(ag, AG71XX_REG_MAC_ADDR1), -+ ag71xx_rr(ag, AG71XX_REG_MAC_ADDR2)); -+ DBG("%s: fifo_cfg0=%08x, fifo_cfg1=%08x, fifo_cfg2=%08x\n", -+ ag->dev->name, -+ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0), -+ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1), -+ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2)); -+ DBG("%s: fifo_cfg3=%08x, fifo_cfg4=%08x, fifo_cfg5=%08x\n", -+ ag->dev->name, -+ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3), -+ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4), -+ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5)); -+} -+ -+static inline void ag71xx_dump_intr(struct ag71xx *ag, char *label, u32 intr) -+{ -+ DBG("%s: %s intr=%08x %s%s%s%s%s%s\n", -+ ag->dev->name, label, intr, -+ (intr & AG71XX_INT_TX_PS) ? "TXPS " : "", -+ (intr & AG71XX_INT_TX_UR) ? "TXUR " : "", -+ (intr & AG71XX_INT_TX_BE) ? "TXBE " : "", -+ (intr & AG71XX_INT_RX_PR) ? "RXPR " : "", -+ (intr & AG71XX_INT_RX_OF) ? "RXOF " : "", -+ (intr & AG71XX_INT_RX_BE) ? "RXBE " : ""); -+} -+ -+static void ag71xx_ring_free(struct ag71xx_ring *ring) -+{ -+ kfree(ring->buf); -+ -+ if (ring->descs_cpu) -+ dma_free_coherent(NULL, ring->size * ring->desc_size, -+ ring->descs_cpu, ring->descs_dma); -+} -+ -+static int ag71xx_ring_alloc(struct ag71xx_ring *ring) -+{ -+ int err; -+ int i; -+ -+ ring->desc_size = sizeof(struct ag71xx_desc); -+ if (ring->desc_size % cache_line_size()) { -+ DBG("ag71xx: ring %p, desc size %u rounded to %u\n", -+ ring, ring->desc_size, -+ roundup(ring->desc_size, cache_line_size())); -+ ring->desc_size = roundup(ring->desc_size, cache_line_size()); -+ } -+ -+ ring->descs_cpu = dma_alloc_coherent(NULL, ring->size * ring->desc_size, -+ &ring->descs_dma, GFP_ATOMIC); -+ if (!ring->descs_cpu) { -+ err = -ENOMEM; -+ goto err; -+ } -+ -+ -+ ring->buf = kzalloc(ring->size * sizeof(*ring->buf), GFP_KERNEL); -+ if (!ring->buf) { -+ err = -ENOMEM; -+ goto err; -+ } -+ -+ for (i = 0; i < ring->size; i++) { -+ int idx = i * ring->desc_size; -+ ring->buf[i].desc = (struct ag71xx_desc *)&ring->descs_cpu[idx]; -+ DBG("ag71xx: ring %p, desc %d at %p\n", -+ ring, i, ring->buf[i].desc); -+ } -+ -+ return 0; -+ -+err: -+ return err; -+} -+ -+static void ag71xx_ring_tx_clean(struct ag71xx *ag) -+{ -+ struct ag71xx_ring *ring = &ag->tx_ring; -+ struct net_device *dev = ag->dev; -+ u32 bytes_compl = 0, pkts_compl = 0; -+ -+ while (ring->curr != ring->dirty) { -+ u32 i = ring->dirty % ring->size; -+ -+ if (!ag71xx_desc_empty(ring->buf[i].desc)) { -+ ring->buf[i].desc->ctrl = 0; -+ dev->stats.tx_errors++; -+ } -+ -+ if (ring->buf[i].skb) { -+ bytes_compl += ring->buf[i].len; -+ pkts_compl++; -+ dev_kfree_skb_any(ring->buf[i].skb); -+ } -+ ring->buf[i].skb = NULL; -+ ring->dirty++; -+ } -+ -+ /* flush descriptors */ -+ wmb(); -+ -+ netdev_completed_queue(dev, pkts_compl, bytes_compl); -+} -+ -+static void ag71xx_ring_tx_init(struct ag71xx *ag) -+{ -+ struct ag71xx_ring *ring = &ag->tx_ring; -+ int i; -+ -+ for (i = 0; i < ring->size; i++) { -+ ring->buf[i].desc->next = (u32) (ring->descs_dma + -+ ring->desc_size * ((i + 1) % ring->size)); -+ -+ ring->buf[i].desc->ctrl = DESC_EMPTY; -+ ring->buf[i].skb = NULL; -+ } -+ -+ /* flush descriptors */ -+ wmb(); -+ -+ ring->curr = 0; -+ ring->dirty = 0; -+ netdev_reset_queue(ag->dev); -+} -+ -+static void ag71xx_ring_rx_clean(struct ag71xx *ag) -+{ -+ struct ag71xx_ring *ring = &ag->rx_ring; -+ int i; -+ -+ if (!ring->buf) -+ return; -+ -+ for (i = 0; i < ring->size; i++) -+ if (ring->buf[i].rx_buf) { -+ dma_unmap_single(&ag->dev->dev, ring->buf[i].dma_addr, -+ ag->rx_buf_size, DMA_FROM_DEVICE); -+ kfree(ring->buf[i].rx_buf); -+ } -+} -+ -+static int ag71xx_buffer_offset(struct ag71xx *ag) -+{ -+ int offset = NET_SKB_PAD; -+ -+ /* -+ * On AR71xx/AR91xx packets must be 4-byte aligned. -+ * -+ * When using builtin AR8216 support, hardware adds a 2-byte header, -+ * so we don't need any extra alignment in that case. -+ */ -+ if (!ag71xx_get_pdata(ag)->is_ar724x || ag71xx_has_ar8216(ag)) -+ return offset; -+ -+ return offset + NET_IP_ALIGN; -+} -+ -+static bool ag71xx_fill_rx_buf(struct ag71xx *ag, struct ag71xx_buf *buf, -+ int offset) -+{ -+ void *data; -+ -+ data = kmalloc(ag->rx_buf_size + -+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), -+ GFP_ATOMIC); -+ if (!data) -+ return false; -+ -+ buf->rx_buf = data; -+ buf->dma_addr = dma_map_single(&ag->dev->dev, data, ag->rx_buf_size, -+ DMA_FROM_DEVICE); -+ buf->desc->data = (u32) buf->dma_addr + offset; -+ return true; -+} -+ -+static int ag71xx_ring_rx_init(struct ag71xx *ag) -+{ -+ struct ag71xx_ring *ring = &ag->rx_ring; -+ unsigned int i; -+ int ret; -+ int offset = ag71xx_buffer_offset(ag); -+ -+ ret = 0; -+ for (i = 0; i < ring->size; i++) { -+ ring->buf[i].desc->next = (u32) (ring->descs_dma + -+ ring->desc_size * ((i + 1) % ring->size)); -+ -+ DBG("ag71xx: RX desc at %p, next is %08x\n", -+ ring->buf[i].desc, -+ ring->buf[i].desc->next); -+ } -+ -+ for (i = 0; i < ring->size; i++) { -+ if (!ag71xx_fill_rx_buf(ag, &ring->buf[i], offset)) { -+ ret = -ENOMEM; -+ break; -+ } -+ -+ ring->buf[i].desc->ctrl = DESC_EMPTY; -+ } -+ -+ /* flush descriptors */ -+ wmb(); -+ -+ ring->curr = 0; -+ ring->dirty = 0; -+ -+ return ret; -+} -+ -+static int ag71xx_ring_rx_refill(struct ag71xx *ag) -+{ -+ struct ag71xx_ring *ring = &ag->rx_ring; -+ unsigned int count; -+ int offset = ag71xx_buffer_offset(ag); -+ -+ count = 0; -+ for (; ring->curr - ring->dirty > 0; ring->dirty++) { -+ unsigned int i; -+ -+ i = ring->dirty % ring->size; -+ -+ if (!ring->buf[i].rx_buf && -+ !ag71xx_fill_rx_buf(ag, &ring->buf[i], offset)) -+ break; -+ -+ ring->buf[i].desc->ctrl = DESC_EMPTY; -+ count++; -+ } -+ -+ /* flush descriptors */ -+ wmb(); -+ -+ DBG("%s: %u rx descriptors refilled\n", ag->dev->name, count); -+ -+ return count; -+} -+ -+static int ag71xx_rings_init(struct ag71xx *ag) -+{ -+ int ret; -+ -+ ret = ag71xx_ring_alloc(&ag->tx_ring); -+ if (ret) -+ return ret; -+ -+ ag71xx_ring_tx_init(ag); -+ -+ ret = ag71xx_ring_alloc(&ag->rx_ring); -+ if (ret) -+ return ret; -+ -+ ret = ag71xx_ring_rx_init(ag); -+ return ret; -+} -+ -+static void ag71xx_rings_cleanup(struct ag71xx *ag) -+{ -+ ag71xx_ring_rx_clean(ag); -+ ag71xx_ring_free(&ag->rx_ring); -+ -+ ag71xx_ring_tx_clean(ag); -+ netdev_reset_queue(ag->dev); -+ ag71xx_ring_free(&ag->tx_ring); -+} -+ -+static unsigned char *ag71xx_speed_str(struct ag71xx *ag) -+{ -+ switch (ag->speed) { -+ case SPEED_1000: -+ return "1000"; -+ case SPEED_100: -+ return "100"; -+ case SPEED_10: -+ return "10"; -+ } -+ -+ return "?"; -+} -+ -+static void ag71xx_hw_set_macaddr(struct ag71xx *ag, unsigned char *mac) -+{ -+ u32 t; -+ -+ t = (((u32) mac[5]) << 24) | (((u32) mac[4]) << 16) -+ | (((u32) mac[3]) << 8) | ((u32) mac[2]); -+ -+ ag71xx_wr(ag, AG71XX_REG_MAC_ADDR1, t); -+ -+ t = (((u32) mac[1]) << 24) | (((u32) mac[0]) << 16); -+ ag71xx_wr(ag, AG71XX_REG_MAC_ADDR2, t); -+} -+ -+static void ag71xx_dma_reset(struct ag71xx *ag) -+{ -+ u32 val; -+ int i; -+ -+ ag71xx_dump_dma_regs(ag); -+ -+ /* stop RX and TX */ -+ ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0); -+ ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0); -+ -+ /* -+ * give the hardware some time to really stop all rx/tx activity -+ * clearing the descriptors too early causes random memory corruption -+ */ -+ mdelay(1); -+ -+ /* clear descriptor addresses */ -+ ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->stop_desc_dma); -+ ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->stop_desc_dma); -+ -+ /* clear pending RX/TX interrupts */ -+ for (i = 0; i < 256; i++) { -+ ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR); -+ ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS); -+ } -+ -+ /* clear pending errors */ -+ ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE | RX_STATUS_OF); -+ ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE | TX_STATUS_UR); -+ -+ val = ag71xx_rr(ag, AG71XX_REG_RX_STATUS); -+ if (val) -+ pr_alert("%s: unable to clear DMA Rx status: %08x\n", -+ ag->dev->name, val); -+ -+ val = ag71xx_rr(ag, AG71XX_REG_TX_STATUS); -+ -+ /* mask out reserved bits */ -+ val &= ~0xff000000; -+ -+ if (val) -+ pr_alert("%s: unable to clear DMA Tx status: %08x\n", -+ ag->dev->name, val); -+ -+ ag71xx_dump_dma_regs(ag); -+} -+ -+#define MAC_CFG1_INIT (MAC_CFG1_RXE | MAC_CFG1_TXE | \ -+ MAC_CFG1_SRX | MAC_CFG1_STX) -+ -+#define FIFO_CFG0_INIT (FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT) -+ -+#define FIFO_CFG4_INIT (FIFO_CFG4_DE | FIFO_CFG4_DV | FIFO_CFG4_FC | \ -+ FIFO_CFG4_CE | FIFO_CFG4_CR | FIFO_CFG4_LM | \ -+ FIFO_CFG4_LO | FIFO_CFG4_OK | FIFO_CFG4_MC | \ -+ FIFO_CFG4_BC | FIFO_CFG4_DR | FIFO_CFG4_LE | \ -+ FIFO_CFG4_CF | FIFO_CFG4_PF | FIFO_CFG4_UO | \ -+ FIFO_CFG4_VT) -+ -+#define FIFO_CFG5_INIT (FIFO_CFG5_DE | FIFO_CFG5_DV | FIFO_CFG5_FC | \ -+ FIFO_CFG5_CE | FIFO_CFG5_LO | FIFO_CFG5_OK | \ -+ FIFO_CFG5_MC | FIFO_CFG5_BC | FIFO_CFG5_DR | \ -+ FIFO_CFG5_CF | FIFO_CFG5_PF | FIFO_CFG5_VT | \ -+ FIFO_CFG5_LE | FIFO_CFG5_FT | FIFO_CFG5_16 | \ -+ FIFO_CFG5_17 | FIFO_CFG5_SF) -+ -+static void ag71xx_hw_stop(struct ag71xx *ag) -+{ -+ /* disable all interrupts and stop the rx/tx engine */ -+ ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, 0); -+ ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0); -+ ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0); -+} -+ -+static void ag71xx_hw_setup(struct ag71xx *ag) -+{ -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ -+ /* setup MAC configuration registers */ -+ ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_INIT); -+ -+ ag71xx_sb(ag, AG71XX_REG_MAC_CFG2, -+ MAC_CFG2_PAD_CRC_EN | MAC_CFG2_LEN_CHECK); -+ -+ /* setup max frame length to zero */ -+ ag71xx_wr(ag, AG71XX_REG_MAC_MFL, 0); -+ -+ /* setup FIFO configuration registers */ -+ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG0, FIFO_CFG0_INIT); -+ if (pdata->is_ar724x) { -+ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, pdata->fifo_cfg1); -+ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, pdata->fifo_cfg2); -+ } else { -+ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, 0x0fff0000); -+ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, 0x00001fff); -+ } -+ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG4, FIFO_CFG4_INIT); -+ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, FIFO_CFG5_INIT); -+} -+ -+static void ag71xx_hw_init(struct ag71xx *ag) -+{ -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ u32 reset_mask = pdata->reset_bit; -+ -+ ag71xx_hw_stop(ag); -+ -+ if (pdata->is_ar724x) { -+ u32 reset_phy = reset_mask; -+ -+ reset_phy &= AR71XX_RESET_GE0_PHY | AR71XX_RESET_GE1_PHY; -+ reset_mask &= ~(AR71XX_RESET_GE0_PHY | AR71XX_RESET_GE1_PHY); -+ -+ ath79_device_reset_set(reset_phy); -+ mdelay(50); -+ ath79_device_reset_clear(reset_phy); -+ mdelay(200); -+ } -+ -+ ag71xx_sb(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_SR); -+ udelay(20); -+ -+ ath79_device_reset_set(reset_mask); -+ mdelay(100); -+ ath79_device_reset_clear(reset_mask); -+ mdelay(200); -+ -+ ag71xx_hw_setup(ag); -+ -+ ag71xx_dma_reset(ag); -+} -+ -+static void ag71xx_fast_reset(struct ag71xx *ag) -+{ -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ struct net_device *dev = ag->dev; -+ u32 reset_mask = pdata->reset_bit; -+ u32 rx_ds, tx_ds; -+ u32 mii_reg; -+ -+ reset_mask &= AR71XX_RESET_GE0_MAC | AR71XX_RESET_GE1_MAC; -+ -+ mii_reg = ag71xx_rr(ag, AG71XX_REG_MII_CFG); -+ rx_ds = ag71xx_rr(ag, AG71XX_REG_RX_DESC); -+ tx_ds = ag71xx_rr(ag, AG71XX_REG_TX_DESC); -+ -+ ath79_device_reset_set(reset_mask); -+ udelay(10); -+ ath79_device_reset_clear(reset_mask); -+ udelay(10); -+ -+ ag71xx_dma_reset(ag); -+ ag71xx_hw_setup(ag); -+ -+ /* setup max frame length */ -+ ag71xx_wr(ag, AG71XX_REG_MAC_MFL, -+ ag71xx_max_frame_len(ag->dev->mtu)); -+ -+ ag71xx_wr(ag, AG71XX_REG_RX_DESC, rx_ds); -+ ag71xx_wr(ag, AG71XX_REG_TX_DESC, tx_ds); -+ ag71xx_wr(ag, AG71XX_REG_MII_CFG, mii_reg); -+ -+ ag71xx_hw_set_macaddr(ag, dev->dev_addr); -+} -+ -+static void ag71xx_hw_start(struct ag71xx *ag) -+{ -+ /* start RX engine */ -+ ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); -+ -+ /* enable interrupts */ -+ ag71xx_wr(ag, AG71XX_REG_INT_ENABLE, AG71XX_INT_INIT); -+} -+ -+void ag71xx_link_adjust(struct ag71xx *ag) -+{ -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ u32 cfg2; -+ u32 ifctl; -+ u32 fifo5; -+ -+ if (!ag->link) { -+ ag71xx_hw_stop(ag); -+ netif_carrier_off(ag->dev); -+ if (netif_msg_link(ag)) -+ pr_info("%s: link down\n", ag->dev->name); -+ return; -+ } -+ -+ if (pdata->is_ar724x) -+ ag71xx_fast_reset(ag); -+ -+ cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2); -+ cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX); -+ cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0; -+ -+ ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL); -+ ifctl &= ~(MAC_IFCTL_SPEED); -+ -+ fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5); -+ fifo5 &= ~FIFO_CFG5_BM; -+ -+ switch (ag->speed) { -+ case SPEED_1000: -+ cfg2 |= MAC_CFG2_IF_1000; -+ fifo5 |= FIFO_CFG5_BM; -+ break; -+ case SPEED_100: -+ cfg2 |= MAC_CFG2_IF_10_100; -+ ifctl |= MAC_IFCTL_SPEED; -+ break; -+ case SPEED_10: -+ cfg2 |= MAC_CFG2_IF_10_100; -+ break; -+ default: -+ BUG(); -+ return; -+ } -+ -+ if (pdata->is_ar91xx) -+ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x00780fff); -+ else if (pdata->is_ar724x) -+ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, pdata->fifo_cfg3); -+ else -+ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x008001ff); -+ -+ if (pdata->set_speed) -+ pdata->set_speed(ag->speed); -+ -+ ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2); -+ ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5); -+ ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl); -+ ag71xx_hw_start(ag); -+ -+ netif_carrier_on(ag->dev); -+ if (netif_msg_link(ag)) -+ pr_info("%s: link up (%sMbps/%s duplex)\n", -+ ag->dev->name, -+ ag71xx_speed_str(ag), -+ (DUPLEX_FULL == ag->duplex) ? "Full" : "Half"); -+ -+ DBG("%s: fifo_cfg0=%#x, fifo_cfg1=%#x, fifo_cfg2=%#x\n", -+ ag->dev->name, -+ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0), -+ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1), -+ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2)); -+ -+ DBG("%s: fifo_cfg3=%#x, fifo_cfg4=%#x, fifo_cfg5=%#x\n", -+ ag->dev->name, -+ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3), -+ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4), -+ ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5)); -+ -+ DBG("%s: mac_cfg2=%#x, mac_ifctl=%#x\n", -+ ag->dev->name, -+ ag71xx_rr(ag, AG71XX_REG_MAC_CFG2), -+ ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL)); -+} -+ -+static int ag71xx_open(struct net_device *dev) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ unsigned int max_frame_len; -+ int ret; -+ -+ max_frame_len = ag71xx_max_frame_len(dev->mtu); -+ ag->rx_buf_size = max_frame_len + NET_SKB_PAD + NET_IP_ALIGN; -+ -+ /* setup max frame length */ -+ ag71xx_wr(ag, AG71XX_REG_MAC_MFL, max_frame_len); -+ -+ ret = ag71xx_rings_init(ag); -+ if (ret) -+ goto err; -+ -+ napi_enable(&ag->napi); -+ -+ netif_carrier_off(dev); -+ ag71xx_phy_start(ag); -+ -+ ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma); -+ ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->rx_ring.descs_dma); -+ -+ ag71xx_hw_set_macaddr(ag, dev->dev_addr); -+ -+ netif_start_queue(dev); -+ -+ return 0; -+ -+err: -+ ag71xx_rings_cleanup(ag); -+ return ret; -+} -+ -+static int ag71xx_stop(struct net_device *dev) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ unsigned long flags; -+ -+ netif_carrier_off(dev); -+ ag71xx_phy_stop(ag); -+ -+ spin_lock_irqsave(&ag->lock, flags); -+ -+ netif_stop_queue(dev); -+ -+ ag71xx_hw_stop(ag); -+ ag71xx_dma_reset(ag); -+ -+ napi_disable(&ag->napi); -+ del_timer_sync(&ag->oom_timer); -+ -+ spin_unlock_irqrestore(&ag->lock, flags); -+ -+ ag71xx_rings_cleanup(ag); -+ -+ return 0; -+} -+ -+static netdev_tx_t ag71xx_hard_start_xmit(struct sk_buff *skb, -+ struct net_device *dev) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ struct ag71xx_ring *ring = &ag->tx_ring; -+ struct ag71xx_desc *desc; -+ dma_addr_t dma_addr; -+ int i; -+ -+ i = ring->curr % ring->size; -+ desc = ring->buf[i].desc; -+ -+ if (!ag71xx_desc_empty(desc)) -+ goto err_drop; -+ -+ if (ag71xx_has_ar8216(ag)) -+ ag71xx_add_ar8216_header(ag, skb); -+ -+ if (skb->len <= 0) { -+ DBG("%s: packet len is too small\n", ag->dev->name); -+ goto err_drop; -+ } -+ -+ dma_addr = dma_map_single(&dev->dev, skb->data, skb->len, -+ DMA_TO_DEVICE); -+ -+ netdev_sent_queue(dev, skb->len); -+ ring->buf[i].len = skb->len; -+ ring->buf[i].skb = skb; -+ ring->buf[i].timestamp = jiffies; -+ -+ /* setup descriptor fields */ -+ desc->data = (u32) dma_addr; -+ desc->ctrl = skb->len & ag->desc_pktlen_mask; -+ -+ /* flush descriptor */ -+ wmb(); -+ -+ ring->curr++; -+ if (ring->curr == (ring->dirty + ring->size)) { -+ DBG("%s: tx queue full\n", ag->dev->name); -+ netif_stop_queue(dev); -+ } -+ -+ DBG("%s: packet injected into TX queue\n", ag->dev->name); -+ -+ /* enable TX engine */ -+ ag71xx_wr(ag, AG71XX_REG_TX_CTRL, TX_CTRL_TXE); -+ -+ return NETDEV_TX_OK; -+ -+err_drop: -+ dev->stats.tx_dropped++; -+ -+ dev_kfree_skb(skb); -+ return NETDEV_TX_OK; -+} -+ -+static int ag71xx_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ int ret; -+ -+ switch (cmd) { -+ case SIOCETHTOOL: -+ if (ag->phy_dev == NULL) -+ break; -+ -+ spin_lock_irq(&ag->lock); -+ ret = phy_ethtool_ioctl(ag->phy_dev, (void *) ifr->ifr_data); -+ spin_unlock_irq(&ag->lock); -+ return ret; -+ -+ case SIOCSIFHWADDR: -+ if (copy_from_user -+ (dev->dev_addr, ifr->ifr_data, sizeof(dev->dev_addr))) -+ return -EFAULT; -+ return 0; -+ -+ case SIOCGIFHWADDR: -+ if (copy_to_user -+ (ifr->ifr_data, dev->dev_addr, sizeof(dev->dev_addr))) -+ return -EFAULT; -+ return 0; -+ -+ case SIOCGMIIPHY: -+ case SIOCGMIIREG: -+ case SIOCSMIIREG: -+ if (ag->phy_dev == NULL) -+ break; -+ -+ return phy_mii_ioctl(ag->phy_dev, ifr, cmd); -+ -+ default: -+ break; -+ } -+ -+ return -EOPNOTSUPP; -+} -+ -+static void ag71xx_oom_timer_handler(unsigned long data) -+{ -+ struct net_device *dev = (struct net_device *) data; -+ struct ag71xx *ag = netdev_priv(dev); -+ -+ napi_schedule(&ag->napi); -+} -+ -+static void ag71xx_tx_timeout(struct net_device *dev) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ -+ if (netif_msg_tx_err(ag)) -+ pr_info("%s: tx timeout\n", ag->dev->name); -+ -+ schedule_work(&ag->restart_work); -+} -+ -+static void ag71xx_restart_work_func(struct work_struct *work) -+{ -+ struct ag71xx *ag = container_of(work, struct ag71xx, restart_work); -+ -+ if (ag71xx_get_pdata(ag)->is_ar724x) { -+ ag->link = 0; -+ ag71xx_link_adjust(ag); -+ return; -+ } -+ -+ ag71xx_stop(ag->dev); -+ ag71xx_open(ag->dev); -+} -+ -+static bool ag71xx_check_dma_stuck(struct ag71xx *ag, unsigned long timestamp) -+{ -+ u32 rx_sm, tx_sm, rx_fd; -+ -+ if (likely(time_before(jiffies, timestamp + HZ/10))) -+ return false; -+ -+ if (!netif_carrier_ok(ag->dev)) -+ return false; -+ -+ rx_sm = ag71xx_rr(ag, AG71XX_REG_RX_SM); -+ if ((rx_sm & 0x7) == 0x3 && ((rx_sm >> 4) & 0x7) == 0x6) -+ return true; -+ -+ tx_sm = ag71xx_rr(ag, AG71XX_REG_TX_SM); -+ rx_fd = ag71xx_rr(ag, AG71XX_REG_FIFO_DEPTH); -+ if (((tx_sm >> 4) & 0x7) == 0 && ((rx_sm & 0x7) == 0) && -+ ((rx_sm >> 4) & 0x7) == 0 && rx_fd == 0) -+ return true; -+ -+ return false; -+} -+ -+static int ag71xx_tx_packets(struct ag71xx *ag) -+{ -+ struct ag71xx_ring *ring = &ag->tx_ring; -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ int sent = 0; -+ int bytes_compl = 0; -+ -+ DBG("%s: processing TX ring\n", ag->dev->name); -+ -+ while (ring->dirty != ring->curr) { -+ unsigned int i = ring->dirty % ring->size; -+ struct ag71xx_desc *desc = ring->buf[i].desc; -+ struct sk_buff *skb = ring->buf[i].skb; -+ int len = ring->buf[i].len; -+ -+ if (!ag71xx_desc_empty(desc)) { -+ if (pdata->is_ar7240 && -+ ag71xx_check_dma_stuck(ag, ring->buf[i].timestamp)) -+ schedule_work(&ag->restart_work); -+ break; -+ } -+ -+ ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS); -+ -+ bytes_compl += len; -+ ag->dev->stats.tx_bytes += len; -+ ag->dev->stats.tx_packets++; -+ -+ dev_kfree_skb_any(skb); -+ ring->buf[i].skb = NULL; -+ -+ ring->dirty++; -+ sent++; -+ } -+ -+ DBG("%s: %d packets sent out\n", ag->dev->name, sent); -+ -+ if (!sent) -+ return 0; -+ -+ netdev_completed_queue(ag->dev, sent, bytes_compl); -+ if ((ring->curr - ring->dirty) < (ring->size * 3) / 4) -+ netif_wake_queue(ag->dev); -+ -+ return sent; -+} -+ -+static int ag71xx_rx_packets(struct ag71xx *ag, int limit) -+{ -+ struct net_device *dev = ag->dev; -+ struct ag71xx_ring *ring = &ag->rx_ring; -+ int offset = ag71xx_buffer_offset(ag); -+ unsigned int pktlen_mask = ag->desc_pktlen_mask; -+ int done = 0; -+ -+ DBG("%s: rx packets, limit=%d, curr=%u, dirty=%u\n", -+ dev->name, limit, ring->curr, ring->dirty); -+ -+ while (done < limit) { -+ unsigned int i = ring->curr % ring->size; -+ struct ag71xx_desc *desc = ring->buf[i].desc; -+ struct sk_buff *skb; -+ int pktlen; -+ int err = 0; -+ -+ if (ag71xx_desc_empty(desc)) -+ break; -+ -+ if ((ring->dirty + ring->size) == ring->curr) { -+ ag71xx_assert(0); -+ break; -+ } -+ -+ ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR); -+ -+ pktlen = desc->ctrl & pktlen_mask; -+ pktlen -= ETH_FCS_LEN; -+ -+ dma_unmap_single(&dev->dev, ring->buf[i].dma_addr, -+ ag->rx_buf_size, DMA_FROM_DEVICE); -+ -+ dev->stats.rx_packets++; -+ dev->stats.rx_bytes += pktlen; -+ -+ skb = build_skb(ring->buf[i].rx_buf, 0); -+ if (!skb) { -+ kfree(ring->buf[i].rx_buf); -+ goto next; -+ } -+ -+ skb_reserve(skb, offset); -+ skb_put(skb, pktlen); -+ -+ if (ag71xx_has_ar8216(ag)) -+ err = ag71xx_remove_ar8216_header(ag, skb, pktlen); -+ -+ if (err) { -+ dev->stats.rx_dropped++; -+ kfree_skb(skb); -+ } else { -+ skb->dev = dev; -+ skb->ip_summed = CHECKSUM_NONE; -+ skb->protocol = eth_type_trans(skb, dev); -+ netif_receive_skb(skb); -+ } -+ -+next: -+ ring->buf[i].rx_buf = NULL; -+ done++; -+ -+ ring->curr++; -+ } -+ -+ ag71xx_ring_rx_refill(ag); -+ -+ DBG("%s: rx finish, curr=%u, dirty=%u, done=%d\n", -+ dev->name, ring->curr, ring->dirty, done); -+ -+ return done; -+} -+ -+static int ag71xx_poll(struct napi_struct *napi, int limit) -+{ -+ struct ag71xx *ag = container_of(napi, struct ag71xx, napi); -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ struct net_device *dev = ag->dev; -+ struct ag71xx_ring *rx_ring; -+ unsigned long flags; -+ u32 status; -+ int tx_done; -+ int rx_done; -+ -+ pdata->ddr_flush(); -+ tx_done = ag71xx_tx_packets(ag); -+ -+ DBG("%s: processing RX ring\n", dev->name); -+ rx_done = ag71xx_rx_packets(ag, limit); -+ -+ ag71xx_debugfs_update_napi_stats(ag, rx_done, tx_done); -+ -+ rx_ring = &ag->rx_ring; -+ if (rx_ring->buf[rx_ring->dirty % rx_ring->size].rx_buf == NULL) -+ goto oom; -+ -+ status = ag71xx_rr(ag, AG71XX_REG_RX_STATUS); -+ if (unlikely(status & RX_STATUS_OF)) { -+ ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_OF); -+ dev->stats.rx_fifo_errors++; -+ -+ /* restart RX */ -+ ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); -+ } -+ -+ if (rx_done < limit) { -+ if (status & RX_STATUS_PR) -+ goto more; -+ -+ status = ag71xx_rr(ag, AG71XX_REG_TX_STATUS); -+ if (status & TX_STATUS_PS) -+ goto more; -+ -+ DBG("%s: disable polling mode, rx=%d, tx=%d,limit=%d\n", -+ dev->name, rx_done, tx_done, limit); -+ -+ napi_complete(napi); -+ -+ /* enable interrupts */ -+ spin_lock_irqsave(&ag->lock, flags); -+ ag71xx_int_enable(ag, AG71XX_INT_POLL); -+ spin_unlock_irqrestore(&ag->lock, flags); -+ return rx_done; -+ } -+ -+more: -+ DBG("%s: stay in polling mode, rx=%d, tx=%d, limit=%d\n", -+ dev->name, rx_done, tx_done, limit); -+ return rx_done; -+ -+oom: -+ if (netif_msg_rx_err(ag)) -+ pr_info("%s: out of memory\n", dev->name); -+ -+ mod_timer(&ag->oom_timer, jiffies + AG71XX_OOM_REFILL); -+ napi_complete(napi); -+ return 0; -+} -+ -+static irqreturn_t ag71xx_interrupt(int irq, void *dev_id) -+{ -+ struct net_device *dev = dev_id; -+ struct ag71xx *ag = netdev_priv(dev); -+ u32 status; -+ -+ status = ag71xx_rr(ag, AG71XX_REG_INT_STATUS); -+ ag71xx_dump_intr(ag, "raw", status); -+ -+ if (unlikely(!status)) -+ return IRQ_NONE; -+ -+ if (unlikely(status & AG71XX_INT_ERR)) { -+ if (status & AG71XX_INT_TX_BE) { -+ ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE); -+ dev_err(&dev->dev, "TX BUS error\n"); -+ } -+ if (status & AG71XX_INT_RX_BE) { -+ ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE); -+ dev_err(&dev->dev, "RX BUS error\n"); -+ } -+ } -+ -+ if (likely(status & AG71XX_INT_POLL)) { -+ ag71xx_int_disable(ag, AG71XX_INT_POLL); -+ DBG("%s: enable polling mode\n", dev->name); -+ napi_schedule(&ag->napi); -+ } -+ -+ ag71xx_debugfs_update_int_stats(ag, status); -+ -+ return IRQ_HANDLED; -+} -+ -+#ifdef CONFIG_NET_POLL_CONTROLLER -+/* -+ * Polling 'interrupt' - used by things like netconsole to send skbs -+ * without having to re-enable interrupts. It's not called while -+ * the interrupt routine is executing. -+ */ -+static void ag71xx_netpoll(struct net_device *dev) -+{ -+ disable_irq(dev->irq); -+ ag71xx_interrupt(dev->irq, dev); -+ enable_irq(dev->irq); -+} -+#endif -+ -+static int ag71xx_change_mtu(struct net_device *dev, int new_mtu) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ unsigned int max_frame_len; -+ -+ max_frame_len = ag71xx_max_frame_len(new_mtu); -+ if (new_mtu < 68 || max_frame_len > ag->max_frame_len) -+ return -EINVAL; -+ -+ if (netif_running(dev)) -+ return -EBUSY; -+ -+ dev->mtu = new_mtu; -+ return 0; -+} -+ -+static const struct net_device_ops ag71xx_netdev_ops = { -+ .ndo_open = ag71xx_open, -+ .ndo_stop = ag71xx_stop, -+ .ndo_start_xmit = ag71xx_hard_start_xmit, -+ .ndo_do_ioctl = ag71xx_do_ioctl, -+ .ndo_tx_timeout = ag71xx_tx_timeout, -+ .ndo_change_mtu = ag71xx_change_mtu, -+ .ndo_set_mac_address = eth_mac_addr, -+ .ndo_validate_addr = eth_validate_addr, -+#ifdef CONFIG_NET_POLL_CONTROLLER -+ .ndo_poll_controller = ag71xx_netpoll, -+#endif -+}; -+ -+static const char *ag71xx_get_phy_if_mode_name(phy_interface_t mode) -+{ -+ switch (mode) { -+ case PHY_INTERFACE_MODE_MII: -+ return "MII"; -+ case PHY_INTERFACE_MODE_GMII: -+ return "GMII"; -+ case PHY_INTERFACE_MODE_RMII: -+ return "RMII"; -+ case PHY_INTERFACE_MODE_RGMII: -+ return "RGMII"; -+ case PHY_INTERFACE_MODE_SGMII: -+ return "SGMII"; -+ default: -+ break; -+ } -+ -+ return "unknown"; -+} -+ -+ -+static int ag71xx_probe(struct platform_device *pdev) -+{ -+ struct net_device *dev; -+ struct resource *res; -+ struct ag71xx *ag; -+ struct ag71xx_platform_data *pdata; -+ int err; -+ -+ pdata = pdev->dev.platform_data; -+ if (!pdata) { -+ dev_err(&pdev->dev, "no platform data specified\n"); -+ err = -ENXIO; -+ goto err_out; -+ } -+ -+ if (pdata->mii_bus_dev == NULL && pdata->phy_mask) { -+ dev_err(&pdev->dev, "no MII bus device specified\n"); -+ err = -EINVAL; -+ goto err_out; -+ } -+ -+ dev = alloc_etherdev(sizeof(*ag)); -+ if (!dev) { -+ dev_err(&pdev->dev, "alloc_etherdev failed\n"); -+ err = -ENOMEM; -+ goto err_out; -+ } -+ -+ if (!pdata->max_frame_len || !pdata->desc_pktlen_mask) -+ return -EINVAL; -+ -+ SET_NETDEV_DEV(dev, &pdev->dev); -+ -+ ag = netdev_priv(dev); -+ ag->pdev = pdev; -+ ag->dev = dev; -+ ag->msg_enable = netif_msg_init(ag71xx_msg_level, -+ AG71XX_DEFAULT_MSG_ENABLE); -+ spin_lock_init(&ag->lock); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac_base"); -+ if (!res) { -+ dev_err(&pdev->dev, "no mac_base resource found\n"); -+ err = -ENXIO; -+ goto err_out; -+ } -+ -+ ag->mac_base = ioremap_nocache(res->start, res->end - res->start + 1); -+ if (!ag->mac_base) { -+ dev_err(&pdev->dev, "unable to ioremap mac_base\n"); -+ err = -ENOMEM; -+ goto err_free_dev; -+ } -+ -+ dev->irq = platform_get_irq(pdev, 0); -+ err = request_irq(dev->irq, ag71xx_interrupt, -+ IRQF_DISABLED, -+ dev->name, dev); -+ if (err) { -+ dev_err(&pdev->dev, "unable to request IRQ %d\n", dev->irq); -+ goto err_unmap_base; -+ } -+ -+ dev->base_addr = (unsigned long)ag->mac_base; -+ dev->netdev_ops = &ag71xx_netdev_ops; -+ dev->ethtool_ops = &ag71xx_ethtool_ops; -+ -+ INIT_WORK(&ag->restart_work, ag71xx_restart_work_func); -+ -+ init_timer(&ag->oom_timer); -+ ag->oom_timer.data = (unsigned long) dev; -+ ag->oom_timer.function = ag71xx_oom_timer_handler; -+ -+ ag->tx_ring.size = AG71XX_TX_RING_SIZE_DEFAULT; -+ ag->rx_ring.size = AG71XX_RX_RING_SIZE_DEFAULT; -+ -+ ag->max_frame_len = pdata->max_frame_len; -+ ag->desc_pktlen_mask = pdata->desc_pktlen_mask; -+ -+ ag->stop_desc = dma_alloc_coherent(NULL, -+ sizeof(struct ag71xx_desc), &ag->stop_desc_dma, GFP_KERNEL); -+ -+ if (!ag->stop_desc) -+ goto err_free_irq; -+ -+ ag->stop_desc->data = 0; -+ ag->stop_desc->ctrl = 0; -+ ag->stop_desc->next = (u32) ag->stop_desc_dma; -+ -+ memcpy(dev->dev_addr, pdata->mac_addr, ETH_ALEN); -+ -+ netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT); -+ -+ ag71xx_dump_regs(ag); -+ -+ ag71xx_hw_init(ag); -+ -+ ag71xx_dump_regs(ag); -+ -+ err = ag71xx_phy_connect(ag); -+ if (err) -+ goto err_free_desc; -+ -+ err = ag71xx_debugfs_init(ag); -+ if (err) -+ goto err_phy_disconnect; -+ -+ platform_set_drvdata(pdev, dev); -+ -+ err = register_netdev(dev); -+ if (err) { -+ dev_err(&pdev->dev, "unable to register net device\n"); -+ goto err_debugfs_exit; -+ } -+ -+ pr_info("%s: Atheros AG71xx at 0x%08lx, irq %d, mode:%s\n", -+ dev->name, dev->base_addr, dev->irq, -+ ag71xx_get_phy_if_mode_name(pdata->phy_if_mode)); -+ -+ return 0; -+ -+err_debugfs_exit: -+ ag71xx_debugfs_exit(ag); -+err_phy_disconnect: -+ ag71xx_phy_disconnect(ag); -+err_free_desc: -+ dma_free_coherent(NULL, sizeof(struct ag71xx_desc), ag->stop_desc, -+ ag->stop_desc_dma); -+err_free_irq: -+ free_irq(dev->irq, dev); -+err_unmap_base: -+ iounmap(ag->mac_base); -+err_free_dev: -+ kfree(dev); -+err_out: -+ platform_set_drvdata(pdev, NULL); -+ return err; -+} -+ -+static int ag71xx_remove(struct platform_device *pdev) -+{ -+ struct net_device *dev = platform_get_drvdata(pdev); -+ -+ if (dev) { -+ struct ag71xx *ag = netdev_priv(dev); -+ -+ ag71xx_debugfs_exit(ag); -+ ag71xx_phy_disconnect(ag); -+ unregister_netdev(dev); -+ free_irq(dev->irq, dev); -+ iounmap(ag->mac_base); -+ kfree(dev); -+ platform_set_drvdata(pdev, NULL); -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver ag71xx_driver = { -+ .probe = ag71xx_probe, -+ .remove = ag71xx_remove, -+ .driver = { -+ .name = AG71XX_DRV_NAME, -+ } -+}; -+ -+static int __init ag71xx_module_init(void) -+{ -+ int ret; -+ -+ ret = ag71xx_debugfs_root_init(); -+ if (ret) -+ goto err_out; -+ -+ ret = ag71xx_mdio_driver_init(); -+ if (ret) -+ goto err_debugfs_exit; -+ -+ ret = platform_driver_register(&ag71xx_driver); -+ if (ret) -+ goto err_mdio_exit; -+ -+ return 0; -+ -+err_mdio_exit: -+ ag71xx_mdio_driver_exit(); -+err_debugfs_exit: -+ ag71xx_debugfs_root_exit(); -+err_out: -+ return ret; -+} -+ -+static void __exit ag71xx_module_exit(void) -+{ -+ platform_driver_unregister(&ag71xx_driver); -+ ag71xx_mdio_driver_exit(); -+ ag71xx_debugfs_root_exit(); -+} -+ -+module_init(ag71xx_module_init); -+module_exit(ag71xx_module_exit); -+ -+MODULE_VERSION(AG71XX_DRV_VERSION); -+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); -+MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org>"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:" AG71XX_DRV_NAME); -diff --git a/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c b/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c -new file mode 100644 -index 0000000..71ae825 ---- /dev/null -+++ b/drivers/net/ethernet/atheros/ag71xx/ag71xx_mdio.c -@@ -0,0 +1,318 @@ -+/* -+ * Atheros AR71xx built-in ethernet mac driver -+ * -+ * Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org> -+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> -+ * -+ * Based on Atheros' AG7100 driver -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include "ag71xx.h" -+ -+#define AG71XX_MDIO_RETRY 1000 -+#define AG71XX_MDIO_DELAY 5 -+ -+static inline void ag71xx_mdio_wr(struct ag71xx_mdio *am, unsigned reg, -+ u32 value) -+{ -+ void __iomem *r; -+ -+ r = am->mdio_base + reg; -+ __raw_writel(value, r); -+ -+ /* flush write */ -+ (void) __raw_readl(r); -+} -+ -+static inline u32 ag71xx_mdio_rr(struct ag71xx_mdio *am, unsigned reg) -+{ -+ return __raw_readl(am->mdio_base + reg); -+} -+ -+static void ag71xx_mdio_dump_regs(struct ag71xx_mdio *am) -+{ -+ DBG("%s: mii_cfg=%08x, mii_cmd=%08x, mii_addr=%08x\n", -+ am->mii_bus->name, -+ ag71xx_mdio_rr(am, AG71XX_REG_MII_CFG), -+ ag71xx_mdio_rr(am, AG71XX_REG_MII_CMD), -+ ag71xx_mdio_rr(am, AG71XX_REG_MII_ADDR)); -+ DBG("%s: mii_ctrl=%08x, mii_status=%08x, mii_ind=%08x\n", -+ am->mii_bus->name, -+ ag71xx_mdio_rr(am, AG71XX_REG_MII_CTRL), -+ ag71xx_mdio_rr(am, AG71XX_REG_MII_STATUS), -+ ag71xx_mdio_rr(am, AG71XX_REG_MII_IND)); -+} -+ -+static int ag71xx_mdio_wait_busy(struct ag71xx_mdio *am) -+{ -+ int i; -+ -+ for (i = 0; i < AG71XX_MDIO_RETRY; i++) { -+ u32 busy; -+ -+ udelay(AG71XX_MDIO_DELAY); -+ -+ busy = ag71xx_mdio_rr(am, AG71XX_REG_MII_IND); -+ if (!busy) -+ return 0; -+ -+ udelay(AG71XX_MDIO_DELAY); -+ } -+ -+ pr_err("%s: MDIO operation timed out\n", am->mii_bus->name); -+ -+ return -ETIMEDOUT; -+} -+ -+int ag71xx_mdio_mii_read(struct ag71xx_mdio *am, int addr, int reg) -+{ -+ int err; -+ int ret; -+ -+ err = ag71xx_mdio_wait_busy(am); -+ if (err) -+ return 0xffff; -+ -+ ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_WRITE); -+ ag71xx_mdio_wr(am, AG71XX_REG_MII_ADDR, -+ ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff)); -+ ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_READ); -+ -+ err = ag71xx_mdio_wait_busy(am); -+ if (err) -+ return 0xffff; -+ -+ ret = ag71xx_mdio_rr(am, AG71XX_REG_MII_STATUS) & 0xffff; -+ ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_WRITE); -+ -+ DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret); -+ -+ return ret; -+} -+ -+void ag71xx_mdio_mii_write(struct ag71xx_mdio *am, int addr, int reg, u16 val) -+{ -+ DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val); -+ -+ ag71xx_mdio_wr(am, AG71XX_REG_MII_ADDR, -+ ((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff)); -+ ag71xx_mdio_wr(am, AG71XX_REG_MII_CTRL, val); -+ -+ ag71xx_mdio_wait_busy(am); -+} -+ -+static const u32 ar71xx_mdio_div_table[] = { -+ 4, 4, 6, 8, 10, 14, 20, 28, -+}; -+ -+static const u32 ar7240_mdio_div_table[] = { -+ 2, 2, 4, 6, 8, 12, 18, 26, 32, 40, 48, 56, 62, 70, 78, 96, -+}; -+ -+static const u32 ar933x_mdio_div_table[] = { -+ 4, 4, 6, 8, 10, 14, 20, 28, 34, 42, 50, 58, 66, 74, 82, 98, -+}; -+ -+static int ag71xx_mdio_get_divider(struct ag71xx_mdio *am, u32 *div) -+{ -+ unsigned long ref_clock, mdio_clock; -+ const u32 *table; -+ int ndivs; -+ int i; -+ -+ ref_clock = am->pdata->ref_clock; -+ mdio_clock = am->pdata->mdio_clock; -+ -+ if (!ref_clock || !mdio_clock) -+ return -EINVAL; -+ -+ if (am->pdata->is_ar9330 || am->pdata->is_ar934x) { -+ table = ar933x_mdio_div_table; -+ ndivs = ARRAY_SIZE(ar933x_mdio_div_table); -+ } else if (am->pdata->is_ar7240) { -+ table = ar7240_mdio_div_table; -+ ndivs = ARRAY_SIZE(ar7240_mdio_div_table); -+ } else { -+ table = ar71xx_mdio_div_table; -+ ndivs = ARRAY_SIZE(ar71xx_mdio_div_table); -+ } -+ -+ for (i = 0; i < ndivs; i++) { -+ unsigned long t; -+ -+ t = ref_clock / table[i]; -+ if (t <= mdio_clock) { -+ *div = i; -+ return 0; -+ } -+ } -+ -+ dev_err(&am->mii_bus->dev, "no divider found for %lu/%lu\n", -+ ref_clock, mdio_clock); -+ return -ENOENT; -+} -+ -+static int ag71xx_mdio_reset(struct mii_bus *bus) -+{ -+ struct ag71xx_mdio *am = bus->priv; -+ u32 t; -+ int err; -+ -+ err = ag71xx_mdio_get_divider(am, &t); -+ if (err) { -+ /* fallback */ -+ if (am->pdata->is_ar7240) -+ t = MII_CFG_CLK_DIV_6; -+ else if (am->pdata->builtin_switch && !am->pdata->is_ar934x) -+ t = MII_CFG_CLK_DIV_10; -+ else if (!am->pdata->builtin_switch && am->pdata->is_ar934x) -+ t = MII_CFG_CLK_DIV_58; -+ else -+ t = MII_CFG_CLK_DIV_28; -+ } -+ -+ ag71xx_mdio_wr(am, AG71XX_REG_MII_CFG, t | MII_CFG_RESET); -+ udelay(100); -+ -+ ag71xx_mdio_wr(am, AG71XX_REG_MII_CFG, t); -+ udelay(100); -+ -+ if (am->pdata->reset) -+ am->pdata->reset(bus); -+ -+ return 0; -+} -+ -+static int ag71xx_mdio_read(struct mii_bus *bus, int addr, int reg) -+{ -+ struct ag71xx_mdio *am = bus->priv; -+ -+ if (am->pdata->builtin_switch) -+ return ar7240sw_phy_read(bus, addr, reg); -+ else -+ return ag71xx_mdio_mii_read(am, addr, reg); -+} -+ -+static int ag71xx_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val) -+{ -+ struct ag71xx_mdio *am = bus->priv; -+ -+ if (am->pdata->builtin_switch) -+ ar7240sw_phy_write(bus, addr, reg, val); -+ else -+ ag71xx_mdio_mii_write(am, addr, reg, val); -+ return 0; -+} -+ -+static int ag71xx_mdio_probe(struct platform_device *pdev) -+{ -+ struct ag71xx_mdio_platform_data *pdata; -+ struct ag71xx_mdio *am; -+ struct resource *res; -+ int i; -+ int err; -+ -+ pdata = pdev->dev.platform_data; -+ if (!pdata) { -+ dev_err(&pdev->dev, "no platform data specified\n"); -+ return -EINVAL; -+ } -+ -+ am = kzalloc(sizeof(*am), GFP_KERNEL); -+ if (!am) { -+ err = -ENOMEM; -+ goto err_out; -+ } -+ -+ am->pdata = pdata; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(&pdev->dev, "no iomem resource found\n"); -+ err = -ENXIO; -+ goto err_out; -+ } -+ -+ am->mdio_base = ioremap_nocache(res->start, res->end - res->start + 1); -+ if (!am->mdio_base) { -+ dev_err(&pdev->dev, "unable to ioremap registers\n"); -+ err = -ENOMEM; -+ goto err_free_mdio; -+ } -+ -+ am->mii_bus = mdiobus_alloc(); -+ if (am->mii_bus == NULL) { -+ err = -ENOMEM; -+ goto err_iounmap; -+ } -+ -+ am->mii_bus->name = "ag71xx_mdio"; -+ am->mii_bus->read = ag71xx_mdio_read; -+ am->mii_bus->write = ag71xx_mdio_write; -+ am->mii_bus->reset = ag71xx_mdio_reset; -+ am->mii_bus->irq = am->mii_irq; -+ am->mii_bus->priv = am; -+ am->mii_bus->parent = &pdev->dev; -+ snprintf(am->mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(&pdev->dev)); -+ am->mii_bus->phy_mask = pdata->phy_mask; -+ -+ for (i = 0; i < PHY_MAX_ADDR; i++) -+ am->mii_irq[i] = PHY_POLL; -+ -+ ag71xx_mdio_wr(am, AG71XX_REG_MAC_CFG1, 0); -+ -+ err = mdiobus_register(am->mii_bus); -+ if (err) -+ goto err_free_bus; -+ -+ ag71xx_mdio_dump_regs(am); -+ -+ platform_set_drvdata(pdev, am); -+ return 0; -+ -+err_free_bus: -+ mdiobus_free(am->mii_bus); -+err_iounmap: -+ iounmap(am->mdio_base); -+err_free_mdio: -+ kfree(am); -+err_out: -+ return err; -+} -+ -+static int ag71xx_mdio_remove(struct platform_device *pdev) -+{ -+ struct ag71xx_mdio *am = platform_get_drvdata(pdev); -+ -+ if (am) { -+ mdiobus_unregister(am->mii_bus); -+ mdiobus_free(am->mii_bus); -+ iounmap(am->mdio_base); -+ kfree(am); -+ platform_set_drvdata(pdev, NULL); -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver ag71xx_mdio_driver = { -+ .probe = ag71xx_mdio_probe, -+ .remove = ag71xx_mdio_remove, -+ .driver = { -+ .name = "ag71xx-mdio", -+ } -+}; -+ -+int __init ag71xx_mdio_driver_init(void) -+{ -+ return platform_driver_register(&ag71xx_mdio_driver); -+} -+ -+void ag71xx_mdio_driver_exit(void) -+{ -+ platform_driver_unregister(&ag71xx_mdio_driver); -+} -diff --git a/drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c b/drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c -new file mode 100644 -index 0000000..9de77e9 ---- /dev/null -+++ b/drivers/net/ethernet/atheros/ag71xx/ag71xx_phy.c -@@ -0,0 +1,235 @@ -+/* -+ * Atheros AR71xx built-in ethernet mac driver -+ * -+ * Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org> -+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> -+ * -+ * Based on Atheros' AG7100 driver -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include "ag71xx.h" -+ -+static void ag71xx_phy_link_adjust(struct net_device *dev) -+{ -+ struct ag71xx *ag = netdev_priv(dev); -+ struct phy_device *phydev = ag->phy_dev; -+ unsigned long flags; -+ int status_change = 0; -+ -+ spin_lock_irqsave(&ag->lock, flags); -+ -+ if (phydev->link) { -+ if (ag->duplex != phydev->duplex -+ || ag->speed != phydev->speed) { -+ status_change = 1; -+ } -+ } -+ -+ if (phydev->link != ag->link) -+ status_change = 1; -+ -+ ag->link = phydev->link; -+ ag->duplex = phydev->duplex; -+ ag->speed = phydev->speed; -+ -+ if (status_change) -+ ag71xx_link_adjust(ag); -+ -+ spin_unlock_irqrestore(&ag->lock, flags); -+} -+ -+void ag71xx_phy_start(struct ag71xx *ag) -+{ -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ -+ if (ag->phy_dev) { -+ phy_start(ag->phy_dev); -+ } else if (pdata->mii_bus_dev && pdata->switch_data) { -+ ag71xx_ar7240_start(ag); -+ } else { -+ ag->link = 1; -+ ag71xx_link_adjust(ag); -+ } -+} -+ -+void ag71xx_phy_stop(struct ag71xx *ag) -+{ -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ unsigned long flags; -+ -+ if (ag->phy_dev) -+ phy_stop(ag->phy_dev); -+ else if (pdata->mii_bus_dev && pdata->switch_data) -+ ag71xx_ar7240_stop(ag); -+ -+ spin_lock_irqsave(&ag->lock, flags); -+ if (ag->link) { -+ ag->link = 0; -+ ag71xx_link_adjust(ag); -+ } -+ spin_unlock_irqrestore(&ag->lock, flags); -+} -+ -+static int ag71xx_phy_connect_fixed(struct ag71xx *ag) -+{ -+ struct device *dev = &ag->pdev->dev; -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ int ret = 0; -+ -+ /* use fixed settings */ -+ switch (pdata->speed) { -+ case SPEED_10: -+ case SPEED_100: -+ case SPEED_1000: -+ break; -+ default: -+ dev_err(dev, "invalid speed specified\n"); -+ ret = -EINVAL; -+ break; -+ } -+ -+ dev_dbg(dev, "using fixed link parameters\n"); -+ -+ ag->duplex = pdata->duplex; -+ ag->speed = pdata->speed; -+ -+ return ret; -+} -+ -+static int ag71xx_phy_connect_multi(struct ag71xx *ag) -+{ -+ struct device *dev = &ag->pdev->dev; -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ struct phy_device *phydev = NULL; -+ int phy_addr; -+ int ret = 0; -+ -+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { -+ if (!(pdata->phy_mask & (1 << phy_addr))) -+ continue; -+ -+ if (ag->mii_bus->phy_map[phy_addr] == NULL) -+ continue; -+ -+ DBG("%s: PHY found at %s, uid=%08x\n", -+ dev_name(dev), -+ dev_name(&ag->mii_bus->phy_map[phy_addr]->dev), -+ ag->mii_bus->phy_map[phy_addr]->phy_id); -+ -+ if (phydev == NULL) -+ phydev = ag->mii_bus->phy_map[phy_addr]; -+ } -+ -+ if (!phydev) { -+ dev_err(dev, "no PHY found with phy_mask=%08x\n", -+ pdata->phy_mask); -+ return -ENODEV; -+ } -+ -+ ag->phy_dev = phy_connect(ag->dev, dev_name(&phydev->dev), -+ &ag71xx_phy_link_adjust, -+ pdata->phy_if_mode); -+ -+ if (IS_ERR(ag->phy_dev)) { -+ dev_err(dev, "could not connect to PHY at %s\n", -+ dev_name(&phydev->dev)); -+ return PTR_ERR(ag->phy_dev); -+ } -+ -+ /* mask with MAC supported features */ -+ if (pdata->has_gbit) -+ phydev->supported &= PHY_GBIT_FEATURES; -+ else -+ phydev->supported &= PHY_BASIC_FEATURES; -+ -+ phydev->advertising = phydev->supported; -+ -+ dev_info(dev, "connected to PHY at %s [uid=%08x, driver=%s]\n", -+ dev_name(&phydev->dev), phydev->phy_id, phydev->drv->name); -+ -+ ag->link = 0; -+ ag->speed = 0; -+ ag->duplex = -1; -+ -+ return ret; -+} -+ -+static int dev_is_class(struct device *dev, void *class) -+{ -+ if (dev->class != NULL && !strcmp(dev->class->name, class)) -+ return 1; -+ -+ return 0; -+} -+ -+static struct device *dev_find_class(struct device *parent, char *class) -+{ -+ if (dev_is_class(parent, class)) { -+ get_device(parent); -+ return parent; -+ } -+ -+ return device_find_child(parent, class, dev_is_class); -+} -+ -+static struct mii_bus *dev_to_mii_bus(struct device *dev) -+{ -+ struct device *d; -+ -+ d = dev_find_class(dev, "mdio_bus"); -+ if (d != NULL) { -+ struct mii_bus *bus; -+ -+ bus = to_mii_bus(d); -+ put_device(d); -+ -+ return bus; -+ } -+ -+ return NULL; -+} -+ -+int ag71xx_phy_connect(struct ag71xx *ag) -+{ -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ -+ if (pdata->mii_bus_dev == NULL || -+ pdata->mii_bus_dev->bus == NULL ) -+ return ag71xx_phy_connect_fixed(ag); -+ -+ ag->mii_bus = dev_to_mii_bus(pdata->mii_bus_dev); -+ if (ag->mii_bus == NULL) { -+ dev_err(&ag->pdev->dev, "unable to find MII bus on device '%s'\n", -+ dev_name(pdata->mii_bus_dev)); -+ return -ENODEV; -+ } -+ -+ /* Reset the mdio bus explicitly */ -+ if (ag->mii_bus->reset) { -+ mutex_lock(&ag->mii_bus->mdio_lock); -+ ag->mii_bus->reset(ag->mii_bus); -+ mutex_unlock(&ag->mii_bus->mdio_lock); -+ } -+ -+ if (pdata->switch_data) -+ return ag71xx_ar7240_init(ag); -+ -+ if (pdata->phy_mask) -+ return ag71xx_phy_connect_multi(ag); -+ -+ return ag71xx_phy_connect_fixed(ag); -+} -+ -+void ag71xx_phy_disconnect(struct ag71xx *ag) -+{ -+ struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); -+ -+ if (pdata->switch_data) -+ ag71xx_ar7240_cleanup(ag); -+ else if (ag->phy_dev) -+ phy_disconnect(ag->phy_dev); -+} --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0004-drivers-link-SPI-drivers-before-MTD-drivers.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0004-drivers-link-SPI-drivers-before-MTD-drivers.patch deleted file mode 100644 index 824d6ebec..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0004-drivers-link-SPI-drivers-before-MTD-drivers.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 3c367cc533d07353f60110340c110f6d622094b8 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:14:15 +0200 -Subject: [PATCH] drivers: link SPI drivers before MTD drivers - -This prevents probe deferral in SPI-driven MTD drivers. ---- - drivers/Makefile | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/Makefile b/drivers/Makefile -index 8e3b8b0..61bbeb2 100644 ---- a/drivers/Makefile -+++ b/drivers/Makefile -@@ -64,8 +64,8 @@ obj-$(CONFIG_IDE) += ide/ - obj-$(CONFIG_SCSI) += scsi/ - obj-$(CONFIG_ATA) += ata/ - obj-$(CONFIG_TARGET_CORE) += target/ --obj-$(CONFIG_MTD) += mtd/ - obj-$(CONFIG_SPI) += spi/ -+obj-$(CONFIG_MTD) += mtd/ - obj-y += hsi/ - obj-y += net/ - obj-$(CONFIG_ATM) += atm/ --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0005-spi-add-various-flags-to-spi_transfer-and-spi_messag.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0005-spi-add-various-flags-to-spi_transfer-and-spi_messag.patch deleted file mode 100644 index 505562fe0..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0005-spi-add-various-flags-to-spi_transfer-and-spi_messag.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 34dcc540e28cc2253fd3bdaacdd77faf1d42d759 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:17:06 +0200 -Subject: [PATCH] spi: add various flags to spi_transfer and spi_message - structs - ---- - include/linux/spi/spi.h | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h -index 4203c66..4ee1a02 100644 ---- a/include/linux/spi/spi.h -+++ b/include/linux/spi/spi.h -@@ -581,6 +581,8 @@ struct spi_transfer { - dma_addr_t rx_dma; - - unsigned cs_change:1; -+ unsigned verify:1; -+ unsigned fast_write:1; - unsigned tx_nbits:3; - unsigned rx_nbits:3; - #define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */ -@@ -627,6 +629,7 @@ struct spi_message { - struct spi_device *spi; - - unsigned is_dma_mapped:1; -+ unsigned fast_read:1; - - /* REVISIT: we might want a flag affecting the behavior of the - * last transfer ... allowing things like "read 16 bit length L" --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0006-spi-add-rb4xx-SPI-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0006-spi-add-rb4xx-SPI-driver.patch deleted file mode 100644 index 757dc775b..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0006-spi-add-rb4xx-SPI-driver.patch +++ /dev/null @@ -1,557 +0,0 @@ -From 97ffc04a7528abe1d84c069d99afb19704d50224 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:18:58 +0200 -Subject: [PATCH] spi: add rb4xx SPI driver - ---- - drivers/spi/Kconfig | 6 + - drivers/spi/Makefile | 1 + - drivers/spi/spi-rb4xx.c | 507 ++++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 514 insertions(+) - create mode 100644 drivers/spi/spi-rb4xx.c - -diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig -index 581ee2a..721f3a7 100644 ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -381,6 +381,12 @@ config SPI_RSPI - help - SPI driver for Renesas RSPI and QSPI blocks. - -+config SPI_RB4XX -+ tristate "Mikrotik RB4XX SPI master" -+ depends on SPI_MASTER && ATH79_MACH_RB4XX -+ help -+ SPI controller driver for the Mikrotik RB4xx series boards. -+ - config SPI_S3C24XX - tristate "Samsung S3C24XX series SPI" - depends on ARCH_S3C24XX -diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile -index 95af48d..e738c7a 100644 ---- a/drivers/spi/Makefile -+++ b/drivers/spi/Makefile -@@ -59,6 +59,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o - spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o - obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o - obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o -+obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o - obj-$(CONFIG_SPI_RSPI) += spi-rspi.o - obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o - spi-s3c24xx-hw-y := spi-s3c24xx.o -diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c -new file mode 100644 -index 0000000..56260ff ---- /dev/null -+++ b/drivers/spi/spi-rb4xx.c -@@ -0,0 +1,507 @@ -+/* -+ * SPI controller driver for the Mikrotik RB4xx boards -+ * -+ * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org> -+ * -+ * This file was based on the patches for Linux 2.6.27.39 published by -+ * MikroTik for their RouterBoard 4xx series devices. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ */ -+ -+#include <linux/clk.h> -+#include <linux/err.h> -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/delay.h> -+#include <linux/spinlock.h> -+#include <linux/workqueue.h> -+#include <linux/platform_device.h> -+#include <linux/spi/spi.h> -+ -+#include <asm/mach-ath79/ar71xx_regs.h> -+#include <asm/mach-ath79/ath79.h> -+ -+#define DRV_NAME "rb4xx-spi" -+#define DRV_DESC "Mikrotik RB4xx SPI controller driver" -+#define DRV_VERSION "0.1.0" -+ -+#define SPI_CTRL_FASTEST 0x40 -+#define SPI_FLASH_HZ 33333334 -+#define SPI_CPLD_HZ 33333334 -+ -+#define CPLD_CMD_READ_FAST 0x0b -+ -+#undef RB4XX_SPI_DEBUG -+ -+struct rb4xx_spi { -+ void __iomem *base; -+ struct spi_master *master; -+ -+ unsigned spi_ctrl_flash; -+ unsigned spi_ctrl_fread; -+ -+ struct clk *ahb_clk; -+ unsigned long ahb_freq; -+ -+ spinlock_t lock; -+ struct list_head queue; -+ int busy:1; -+ int cs_wait; -+}; -+ -+static unsigned spi_clk_low = AR71XX_SPI_IOC_CS1; -+ -+#ifdef RB4XX_SPI_DEBUG -+static inline void do_spi_delay(void) -+{ -+ ndelay(20000); -+} -+#else -+static inline void do_spi_delay(void) { } -+#endif -+ -+static inline void do_spi_init(struct spi_device *spi) -+{ -+ unsigned cs = AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1; -+ -+ if (!(spi->mode & SPI_CS_HIGH)) -+ cs ^= (spi->chip_select == 2) ? AR71XX_SPI_IOC_CS1 : -+ AR71XX_SPI_IOC_CS0; -+ -+ spi_clk_low = cs; -+} -+ -+static inline void do_spi_finish(void __iomem *base) -+{ -+ do_spi_delay(); -+ __raw_writel(AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1, -+ base + AR71XX_SPI_REG_IOC); -+} -+ -+static inline void do_spi_clk(void __iomem *base, int bit) -+{ -+ unsigned bval = spi_clk_low | ((bit & 1) ? AR71XX_SPI_IOC_DO : 0); -+ -+ do_spi_delay(); -+ __raw_writel(bval, base + AR71XX_SPI_REG_IOC); -+ do_spi_delay(); -+ __raw_writel(bval | AR71XX_SPI_IOC_CLK, base + AR71XX_SPI_REG_IOC); -+} -+ -+static void do_spi_byte(void __iomem *base, unsigned char byte) -+{ -+ do_spi_clk(base, byte >> 7); -+ do_spi_clk(base, byte >> 6); -+ do_spi_clk(base, byte >> 5); -+ do_spi_clk(base, byte >> 4); -+ do_spi_clk(base, byte >> 3); -+ do_spi_clk(base, byte >> 2); -+ do_spi_clk(base, byte >> 1); -+ do_spi_clk(base, byte); -+ -+ pr_debug("spi_byte sent 0x%02x got 0x%02x\n", -+ (unsigned)byte, -+ (unsigned char)__raw_readl(base + AR71XX_SPI_REG_RDS)); -+} -+ -+static inline void do_spi_clk_fast(void __iomem *base, unsigned bit1, -+ unsigned bit2) -+{ -+ unsigned bval = (spi_clk_low | -+ ((bit1 & 1) ? AR71XX_SPI_IOC_DO : 0) | -+ ((bit2 & 1) ? AR71XX_SPI_IOC_CS2 : 0)); -+ do_spi_delay(); -+ __raw_writel(bval, base + AR71XX_SPI_REG_IOC); -+ do_spi_delay(); -+ __raw_writel(bval | AR71XX_SPI_IOC_CLK, base + AR71XX_SPI_REG_IOC); -+} -+ -+static void do_spi_byte_fast(void __iomem *base, unsigned char byte) -+{ -+ do_spi_clk_fast(base, byte >> 7, byte >> 6); -+ do_spi_clk_fast(base, byte >> 5, byte >> 4); -+ do_spi_clk_fast(base, byte >> 3, byte >> 2); -+ do_spi_clk_fast(base, byte >> 1, byte >> 0); -+ -+ pr_debug("spi_byte_fast sent 0x%02x got 0x%02x\n", -+ (unsigned)byte, -+ (unsigned char) __raw_readl(base + AR71XX_SPI_REG_RDS)); -+} -+ -+static int rb4xx_spi_txrx(void __iomem *base, struct spi_transfer *t) -+{ -+ const unsigned char *rxv_ptr = NULL; -+ const unsigned char *tx_ptr = t->tx_buf; -+ unsigned char *rx_ptr = t->rx_buf; -+ unsigned i; -+ -+ pr_debug("spi_txrx len %u tx %u rx %u\n", -+ t->len, -+ (t->tx_buf ? 1 : 0), -+ (t->rx_buf ? 1 : 0)); -+ -+ if (t->verify) { -+ rxv_ptr = tx_ptr; -+ tx_ptr = NULL; -+ } -+ -+ for (i = 0; i < t->len; ++i) { -+ unsigned char sdata = tx_ptr ? tx_ptr[i] : 0; -+ -+ if (t->fast_write) -+ do_spi_byte_fast(base, sdata); -+ else -+ do_spi_byte(base, sdata); -+ -+ if (rx_ptr) { -+ rx_ptr[i] = __raw_readl(base + AR71XX_SPI_REG_RDS) & 0xff; -+ } else if (rxv_ptr) { -+ unsigned char c = __raw_readl(base + AR71XX_SPI_REG_RDS); -+ if (rxv_ptr[i] != c) -+ return i; -+ } -+ } -+ -+ return i; -+} -+ -+static int rb4xx_spi_read_fast(struct rb4xx_spi *rbspi, -+ struct spi_message *m) -+{ -+ struct spi_transfer *t; -+ const unsigned char *tx_ptr; -+ unsigned addr; -+ void __iomem *base = rbspi->base; -+ -+ /* check for exactly two transfers */ -+ if (list_empty(&m->transfers) || -+ list_is_last(m->transfers.next, &m->transfers) || -+ !list_is_last(m->transfers.next->next, &m->transfers)) { -+ return -1; -+ } -+ -+ /* first transfer contains command and address */ -+ t = list_entry(m->transfers.next, -+ struct spi_transfer, transfer_list); -+ -+ if (t->len != 5 || t->tx_buf == NULL) -+ return -1; -+ -+ tx_ptr = t->tx_buf; -+ if (tx_ptr[0] != CPLD_CMD_READ_FAST) -+ return -1; -+ -+ addr = tx_ptr[1]; -+ addr = tx_ptr[2] | (addr << 8); -+ addr = tx_ptr[3] | (addr << 8); -+ addr += (unsigned) base; -+ -+ m->actual_length += t->len; -+ -+ /* second transfer contains data itself */ -+ t = list_entry(m->transfers.next->next, -+ struct spi_transfer, transfer_list); -+ -+ if (t->tx_buf && !t->verify) -+ return -1; -+ -+ __raw_writel(AR71XX_SPI_FS_GPIO, base + AR71XX_SPI_REG_FS); -+ __raw_writel(rbspi->spi_ctrl_fread, base + AR71XX_SPI_REG_CTRL); -+ __raw_writel(0, base + AR71XX_SPI_REG_FS); -+ -+ if (t->rx_buf) { -+ memcpy(t->rx_buf, (const void *)addr, t->len); -+ } else if (t->tx_buf) { -+ unsigned char buf[t->len]; -+ memcpy(buf, (const void *)addr, t->len); -+ if (memcmp(t->tx_buf, buf, t->len) != 0) -+ m->status = -EMSGSIZE; -+ } -+ m->actual_length += t->len; -+ -+ if (rbspi->spi_ctrl_flash != rbspi->spi_ctrl_fread) { -+ __raw_writel(AR71XX_SPI_FS_GPIO, base + AR71XX_SPI_REG_FS); -+ __raw_writel(rbspi->spi_ctrl_flash, base + AR71XX_SPI_REG_CTRL); -+ __raw_writel(0, base + AR71XX_SPI_REG_FS); -+ } -+ -+ return 0; -+} -+ -+static int rb4xx_spi_msg(struct rb4xx_spi *rbspi, struct spi_message *m) -+{ -+ struct spi_transfer *t = NULL; -+ void __iomem *base = rbspi->base; -+ -+ m->status = 0; -+ if (list_empty(&m->transfers)) -+ return -1; -+ -+ if (m->fast_read) -+ if (rb4xx_spi_read_fast(rbspi, m) == 0) -+ return -1; -+ -+ __raw_writel(AR71XX_SPI_FS_GPIO, base + AR71XX_SPI_REG_FS); -+ __raw_writel(SPI_CTRL_FASTEST, base + AR71XX_SPI_REG_CTRL); -+ do_spi_init(m->spi); -+ -+ list_for_each_entry(t, &m->transfers, transfer_list) { -+ int len; -+ -+ len = rb4xx_spi_txrx(base, t); -+ if (len != t->len) { -+ m->status = -EMSGSIZE; -+ break; -+ } -+ m->actual_length += len; -+ -+ if (t->cs_change) { -+ if (list_is_last(&t->transfer_list, &m->transfers)) { -+ /* wait for continuation */ -+ return m->spi->chip_select; -+ } -+ do_spi_finish(base); -+ ndelay(100); -+ } -+ } -+ -+ do_spi_finish(base); -+ __raw_writel(rbspi->spi_ctrl_flash, base + AR71XX_SPI_REG_CTRL); -+ __raw_writel(0, base + AR71XX_SPI_REG_FS); -+ return -1; -+} -+ -+static void rb4xx_spi_process_queue_locked(struct rb4xx_spi *rbspi, -+ unsigned long *flags) -+{ -+ int cs = rbspi->cs_wait; -+ -+ rbspi->busy = 1; -+ while (!list_empty(&rbspi->queue)) { -+ struct spi_message *m; -+ -+ list_for_each_entry(m, &rbspi->queue, queue) -+ if (cs < 0 || cs == m->spi->chip_select) -+ break; -+ -+ if (&m->queue == &rbspi->queue) -+ break; -+ -+ list_del_init(&m->queue); -+ spin_unlock_irqrestore(&rbspi->lock, *flags); -+ -+ cs = rb4xx_spi_msg(rbspi, m); -+ m->complete(m->context); -+ -+ spin_lock_irqsave(&rbspi->lock, *flags); -+ } -+ -+ rbspi->cs_wait = cs; -+ rbspi->busy = 0; -+ -+ if (cs >= 0) { -+ /* TODO: add timer to unlock cs after 1s inactivity */ -+ } -+} -+ -+static int rb4xx_spi_transfer(struct spi_device *spi, -+ struct spi_message *m) -+{ -+ struct rb4xx_spi *rbspi = spi_master_get_devdata(spi->master); -+ unsigned long flags; -+ -+ m->actual_length = 0; -+ m->status = -EINPROGRESS; -+ -+ spin_lock_irqsave(&rbspi->lock, flags); -+ list_add_tail(&m->queue, &rbspi->queue); -+ if (rbspi->busy || -+ (rbspi->cs_wait >= 0 && rbspi->cs_wait != m->spi->chip_select)) { -+ /* job will be done later */ -+ spin_unlock_irqrestore(&rbspi->lock, flags); -+ return 0; -+ } -+ -+ /* process job in current context */ -+ rb4xx_spi_process_queue_locked(rbspi, &flags); -+ spin_unlock_irqrestore(&rbspi->lock, flags); -+ -+ return 0; -+} -+ -+static int rb4xx_spi_setup(struct spi_device *spi) -+{ -+ struct rb4xx_spi *rbspi = spi_master_get_devdata(spi->master); -+ unsigned long flags; -+ -+ if (spi->mode & ~(SPI_CS_HIGH)) { -+ dev_err(&spi->dev, "mode %x not supported\n", -+ (unsigned) spi->mode); -+ return -EINVAL; -+ } -+ -+ if (spi->bits_per_word != 8 && spi->bits_per_word != 0) { -+ dev_err(&spi->dev, "bits_per_word %u not supported\n", -+ (unsigned) spi->bits_per_word); -+ return -EINVAL; -+ } -+ -+ spin_lock_irqsave(&rbspi->lock, flags); -+ if (rbspi->cs_wait == spi->chip_select && !rbspi->busy) { -+ rbspi->cs_wait = -1; -+ rb4xx_spi_process_queue_locked(rbspi, &flags); -+ } -+ spin_unlock_irqrestore(&rbspi->lock, flags); -+ -+ return 0; -+} -+ -+static unsigned get_spi_ctrl(struct rb4xx_spi *rbspi, unsigned hz_max, -+ const char *name) -+{ -+ unsigned div; -+ -+ div = (rbspi->ahb_freq - 1) / (2 * hz_max); -+ -+ /* -+ * CPU has a bug at (div == 0) - first bit read is random -+ */ -+ if (div == 0) -+ ++div; -+ -+ if (name) { -+ unsigned ahb_khz = (rbspi->ahb_freq + 500) / 1000; -+ unsigned div_real = 2 * (div + 1); -+ pr_debug("rb4xx: %s SPI clock %u kHz (AHB %u kHz / %u)\n", -+ name, -+ ahb_khz / div_real, -+ ahb_khz, div_real); -+ } -+ -+ return SPI_CTRL_FASTEST + div; -+} -+ -+static int rb4xx_spi_probe(struct platform_device *pdev) -+{ -+ struct spi_master *master; -+ struct rb4xx_spi *rbspi; -+ struct resource *r; -+ int err = 0; -+ -+ master = spi_alloc_master(&pdev->dev, sizeof(*rbspi)); -+ if (master == NULL) { -+ dev_err(&pdev->dev, "no memory for spi_master\n"); -+ err = -ENOMEM; -+ goto err_out; -+ } -+ -+ master->bus_num = 0; -+ master->num_chipselect = 3; -+ master->setup = rb4xx_spi_setup; -+ master->transfer = rb4xx_spi_transfer; -+ -+ rbspi = spi_master_get_devdata(master); -+ -+ rbspi->ahb_clk = clk_get(&pdev->dev, "ahb"); -+ if (IS_ERR(rbspi->ahb_clk)) { -+ err = PTR_ERR(rbspi->ahb_clk); -+ goto err_put_master; -+ } -+ -+ err = clk_enable(rbspi->ahb_clk); -+ if (err) -+ goto err_clk_put; -+ -+ rbspi->ahb_freq = clk_get_rate(rbspi->ahb_clk); -+ if (!rbspi->ahb_freq) { -+ err = -EINVAL; -+ goto err_clk_disable; -+ } -+ -+ platform_set_drvdata(pdev, rbspi); -+ -+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (r == NULL) { -+ err = -ENOENT; -+ goto err_clk_disable; -+ } -+ -+ rbspi->base = ioremap(r->start, r->end - r->start + 1); -+ if (!rbspi->base) { -+ err = -ENXIO; -+ goto err_clk_disable; -+ } -+ -+ rbspi->master = master; -+ rbspi->spi_ctrl_flash = get_spi_ctrl(rbspi, SPI_FLASH_HZ, "FLASH"); -+ rbspi->spi_ctrl_fread = get_spi_ctrl(rbspi, SPI_CPLD_HZ, "CPLD"); -+ rbspi->cs_wait = -1; -+ -+ spin_lock_init(&rbspi->lock); -+ INIT_LIST_HEAD(&rbspi->queue); -+ -+ err = spi_register_master(master); -+ if (err) { -+ dev_err(&pdev->dev, "failed to register SPI master\n"); -+ goto err_iounmap; -+ } -+ -+ return 0; -+ -+err_iounmap: -+ iounmap(rbspi->base); -+err_clk_disable: -+ clk_disable(rbspi->ahb_clk); -+err_clk_put: -+ clk_put(rbspi->ahb_clk); -+err_put_master: -+ platform_set_drvdata(pdev, NULL); -+ spi_master_put(master); -+err_out: -+ return err; -+} -+ -+static int rb4xx_spi_remove(struct platform_device *pdev) -+{ -+ struct rb4xx_spi *rbspi = platform_get_drvdata(pdev); -+ -+ iounmap(rbspi->base); -+ clk_disable(rbspi->ahb_clk); -+ clk_put(rbspi->ahb_clk); -+ platform_set_drvdata(pdev, NULL); -+ spi_master_put(rbspi->master); -+ -+ return 0; -+} -+ -+static struct platform_driver rb4xx_spi_drv = { -+ .probe = rb4xx_spi_probe, -+ .remove = rb4xx_spi_remove, -+ .driver = { -+ .name = DRV_NAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init rb4xx_spi_init(void) -+{ -+ return platform_driver_register(&rb4xx_spi_drv); -+} -+subsys_initcall(rb4xx_spi_init); -+ -+static void __exit rb4xx_spi_exit(void) -+{ -+ platform_driver_unregister(&rb4xx_spi_drv); -+} -+ -+module_exit(rb4xx_spi_exit); -+ -+MODULE_DESCRIPTION(DRV_DESC); -+MODULE_VERSION(DRV_VERSION); -+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); -+MODULE_LICENSE("GPL v2"); --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0007-spi-add-rb4xx-cpld-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0007-spi-add-rb4xx-cpld-driver.patch deleted file mode 100644 index 452f2e761..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0007-spi-add-rb4xx-cpld-driver.patch +++ /dev/null @@ -1,548 +0,0 @@ -From bb8d2a4ebf63bc2f04f15a28c92652700416ff83 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:20:04 +0200 -Subject: [PATCH] spi: add rb4xx cpld driver - ---- - arch/mips/include/asm/mach-ath79/rb4xx_cpld.h | 48 +++ - drivers/spi/Kconfig | 7 + - drivers/spi/Makefile | 1 + - drivers/spi/spi-rb4xx-cpld.c | 441 ++++++++++++++++++++++++++ - 4 files changed, 497 insertions(+) - create mode 100644 arch/mips/include/asm/mach-ath79/rb4xx_cpld.h - create mode 100644 drivers/spi/spi-rb4xx-cpld.c - -diff --git a/arch/mips/include/asm/mach-ath79/rb4xx_cpld.h b/arch/mips/include/asm/mach-ath79/rb4xx_cpld.h -new file mode 100644 -index 0000000..5b17e94 ---- /dev/null -+++ b/arch/mips/include/asm/mach-ath79/rb4xx_cpld.h -@@ -0,0 +1,48 @@ -+/* -+ * SPI driver definitions for the CPLD chip on the Mikrotik RB4xx boards -+ * -+ * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org> -+ * -+ * This file was based on the patches for Linux 2.6.27.39 published by -+ * MikroTik for their RouterBoard 4xx series devices. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#define CPLD_GPIO_nLED1 0 -+#define CPLD_GPIO_nLED2 1 -+#define CPLD_GPIO_nLED3 2 -+#define CPLD_GPIO_nLED4 3 -+#define CPLD_GPIO_FAN 4 -+#define CPLD_GPIO_ALE 5 -+#define CPLD_GPIO_CLE 6 -+#define CPLD_GPIO_nCE 7 -+#define CPLD_GPIO_nLED5 8 -+ -+#define CPLD_NUM_GPIOS 9 -+ -+#define CPLD_CFG_nLED1 BIT(CPLD_GPIO_nLED1) -+#define CPLD_CFG_nLED2 BIT(CPLD_GPIO_nLED2) -+#define CPLD_CFG_nLED3 BIT(CPLD_GPIO_nLED3) -+#define CPLD_CFG_nLED4 BIT(CPLD_GPIO_nLED4) -+#define CPLD_CFG_FAN BIT(CPLD_GPIO_FAN) -+#define CPLD_CFG_ALE BIT(CPLD_GPIO_ALE) -+#define CPLD_CFG_CLE BIT(CPLD_GPIO_CLE) -+#define CPLD_CFG_nCE BIT(CPLD_GPIO_nCE) -+#define CPLD_CFG_nLED5 BIT(CPLD_GPIO_nLED5) -+ -+struct rb4xx_cpld_platform_data { -+ unsigned gpio_base; -+}; -+ -+extern int rb4xx_cpld_change_cfg(unsigned mask, unsigned value); -+extern int rb4xx_cpld_read(unsigned char *rx_buf, -+ const unsigned char *verify_buf, -+ unsigned cnt); -+extern int rb4xx_cpld_read_from(unsigned addr, -+ unsigned char *rx_buf, -+ const unsigned char *verify_buf, -+ unsigned cnt); -+extern int rb4xx_cpld_write(const unsigned char *buf, unsigned count); -diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig -index 721f3a7..dbd7e98 100644 ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -577,6 +577,13 @@ config SPI_TLE62X0 - sysfs interface, with each line presented as a kind of GPIO - exposing both switch control and diagnostic feedback. - -+config SPI_RB4XX_CPLD -+ tristate "MikroTik RB4XX CPLD driver" -+ depends on ATH79_MACH_RB4XX -+ help -+ SPI driver for the Xilinx CPLD chip present on the -+ MikroTik RB4xx boards. -+ - # - # Add new SPI protocol masters in alphabetical order above this line - # -diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile -index e738c7a..50913ae 100644 ---- a/drivers/spi/Makefile -+++ b/drivers/spi/Makefile -@@ -60,6 +60,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o - obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o - obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o - obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o -+obj-$(CONFIG_SPI_RB4XX_CPLD) += spi-rb4xx-cpld.o - obj-$(CONFIG_SPI_RSPI) += spi-rspi.o - obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o - spi-s3c24xx-hw-y := spi-s3c24xx.o -diff --git a/drivers/spi/spi-rb4xx-cpld.c b/drivers/spi/spi-rb4xx-cpld.c -new file mode 100644 -index 0000000..a8d5282 ---- /dev/null -+++ b/drivers/spi/spi-rb4xx-cpld.c -@@ -0,0 +1,441 @@ -+/* -+ * SPI driver for the CPLD chip on the Mikrotik RB4xx boards -+ * -+ * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org> -+ * -+ * This file was based on the patches for Linux 2.6.27.39 published by -+ * MikroTik for their RouterBoard 4xx series devices. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include <linux/types.h> -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/module.h> -+#include <linux/device.h> -+#include <linux/bitops.h> -+#include <linux/spi/spi.h> -+#include <linux/gpio.h> -+#include <linux/slab.h> -+ -+#include <asm/mach-ath79/rb4xx_cpld.h> -+ -+#define DRV_NAME "spi-rb4xx-cpld" -+#define DRV_DESC "RB4xx CPLD driver" -+#define DRV_VERSION "0.1.0" -+ -+#define CPLD_CMD_WRITE_NAND 0x08 /* send cmd, n x send data, send indle */ -+#define CPLD_CMD_WRITE_CFG 0x09 /* send cmd, n x send cfg */ -+#define CPLD_CMD_READ_NAND 0x0a /* send cmd, send idle, n x read data */ -+#define CPLD_CMD_READ_FAST 0x0b /* send cmd, 4 x idle, n x read data */ -+#define CPLD_CMD_LED5_ON 0x0c /* send cmd */ -+#define CPLD_CMD_LED5_OFF 0x0d /* send cmd */ -+ -+struct rb4xx_cpld { -+ struct spi_device *spi; -+ struct mutex lock; -+ struct gpio_chip chip; -+ unsigned int config; -+}; -+ -+static struct rb4xx_cpld *rb4xx_cpld; -+ -+static inline struct rb4xx_cpld *gpio_to_cpld(struct gpio_chip *chip) -+{ -+ return container_of(chip, struct rb4xx_cpld, chip); -+} -+ -+static int rb4xx_cpld_write_cmd(struct rb4xx_cpld *cpld, unsigned char cmd) -+{ -+ struct spi_transfer t[1]; -+ struct spi_message m; -+ unsigned char tx_buf[1]; -+ int err; -+ -+ spi_message_init(&m); -+ memset(&t, 0, sizeof(t)); -+ -+ t[0].tx_buf = tx_buf; -+ t[0].len = sizeof(tx_buf); -+ spi_message_add_tail(&t[0], &m); -+ -+ tx_buf[0] = cmd; -+ -+ err = spi_sync(cpld->spi, &m); -+ return err; -+} -+ -+static int rb4xx_cpld_write_cfg(struct rb4xx_cpld *cpld, unsigned char config) -+{ -+ struct spi_transfer t[1]; -+ struct spi_message m; -+ unsigned char cmd[2]; -+ int err; -+ -+ spi_message_init(&m); -+ memset(&t, 0, sizeof(t)); -+ -+ t[0].tx_buf = cmd; -+ t[0].len = sizeof(cmd); -+ spi_message_add_tail(&t[0], &m); -+ -+ cmd[0] = CPLD_CMD_WRITE_CFG; -+ cmd[1] = config; -+ -+ err = spi_sync(cpld->spi, &m); -+ return err; -+} -+ -+static int __rb4xx_cpld_change_cfg(struct rb4xx_cpld *cpld, unsigned mask, -+ unsigned value) -+{ -+ unsigned int config; -+ int err; -+ -+ config = cpld->config & ~mask; -+ config |= value; -+ -+ if ((cpld->config ^ config) & 0xff) { -+ err = rb4xx_cpld_write_cfg(cpld, config); -+ if (err) -+ return err; -+ } -+ -+ if ((cpld->config ^ config) & CPLD_CFG_nLED5) { -+ err = rb4xx_cpld_write_cmd(cpld, (value) ? CPLD_CMD_LED5_ON : -+ CPLD_CMD_LED5_OFF); -+ if (err) -+ return err; -+ } -+ -+ cpld->config = config; -+ return 0; -+} -+ -+int rb4xx_cpld_change_cfg(unsigned mask, unsigned value) -+{ -+ int ret; -+ -+ if (rb4xx_cpld == NULL) -+ return -ENODEV; -+ -+ mutex_lock(&rb4xx_cpld->lock); -+ ret = __rb4xx_cpld_change_cfg(rb4xx_cpld, mask, value); -+ mutex_unlock(&rb4xx_cpld->lock); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(rb4xx_cpld_change_cfg); -+ -+int rb4xx_cpld_read_from(unsigned addr, unsigned char *rx_buf, -+ const unsigned char *verify_buf, unsigned count) -+{ -+ const unsigned char cmd[5] = { -+ CPLD_CMD_READ_FAST, -+ (addr >> 16) & 0xff, -+ (addr >> 8) & 0xff, -+ addr & 0xff, -+ 0 -+ }; -+ struct spi_transfer t[2] = { -+ { -+ .tx_buf = &cmd, -+ .len = 5, -+ }, -+ { -+ .tx_buf = verify_buf, -+ .rx_buf = rx_buf, -+ .len = count, -+ .verify = (verify_buf != NULL), -+ }, -+ }; -+ struct spi_message m; -+ -+ if (rb4xx_cpld == NULL) -+ return -ENODEV; -+ -+ spi_message_init(&m); -+ m.fast_read = 1; -+ spi_message_add_tail(&t[0], &m); -+ spi_message_add_tail(&t[1], &m); -+ return spi_sync(rb4xx_cpld->spi, &m); -+} -+EXPORT_SYMBOL_GPL(rb4xx_cpld_read_from); -+ -+#if 0 -+int rb4xx_cpld_read(unsigned char *buf, unsigned char *verify_buf, -+ unsigned count) -+{ -+ struct spi_transfer t[2]; -+ struct spi_message m; -+ unsigned char cmd[2]; -+ -+ if (rb4xx_cpld == NULL) -+ return -ENODEV; -+ -+ spi_message_init(&m); -+ memset(&t, 0, sizeof(t)); -+ -+ /* send command */ -+ t[0].tx_buf = cmd; -+ t[0].len = sizeof(cmd); -+ spi_message_add_tail(&t[0], &m); -+ -+ cmd[0] = CPLD_CMD_READ_NAND; -+ cmd[1] = 0; -+ -+ /* read data */ -+ t[1].rx_buf = buf; -+ t[1].len = count; -+ spi_message_add_tail(&t[1], &m); -+ -+ return spi_sync(rb4xx_cpld->spi, &m); -+} -+#else -+int rb4xx_cpld_read(unsigned char *rx_buf, const unsigned char *verify_buf, -+ unsigned count) -+{ -+ static const unsigned char cmd[2] = { CPLD_CMD_READ_NAND, 0 }; -+ struct spi_transfer t[2] = { -+ { -+ .tx_buf = &cmd, -+ .len = 2, -+ }, { -+ .tx_buf = verify_buf, -+ .rx_buf = rx_buf, -+ .len = count, -+ .verify = (verify_buf != NULL), -+ }, -+ }; -+ struct spi_message m; -+ -+ if (rb4xx_cpld == NULL) -+ return -ENODEV; -+ -+ spi_message_init(&m); -+ spi_message_add_tail(&t[0], &m); -+ spi_message_add_tail(&t[1], &m); -+ return spi_sync(rb4xx_cpld->spi, &m); -+} -+#endif -+EXPORT_SYMBOL_GPL(rb4xx_cpld_read); -+ -+int rb4xx_cpld_write(const unsigned char *buf, unsigned count) -+{ -+#if 0 -+ struct spi_transfer t[3]; -+ struct spi_message m; -+ unsigned char cmd[1]; -+ -+ if (rb4xx_cpld == NULL) -+ return -ENODEV; -+ -+ memset(&t, 0, sizeof(t)); -+ spi_message_init(&m); -+ -+ /* send command */ -+ t[0].tx_buf = cmd; -+ t[0].len = sizeof(cmd); -+ spi_message_add_tail(&t[0], &m); -+ -+ cmd[0] = CPLD_CMD_WRITE_NAND; -+ -+ /* write data */ -+ t[1].tx_buf = buf; -+ t[1].len = count; -+ spi_message_add_tail(&t[1], &m); -+ -+ /* send idle */ -+ t[2].len = 1; -+ spi_message_add_tail(&t[2], &m); -+ -+ return spi_sync(rb4xx_cpld->spi, &m); -+#else -+ static const unsigned char cmd = CPLD_CMD_WRITE_NAND; -+ struct spi_transfer t[3] = { -+ { -+ .tx_buf = &cmd, -+ .len = 1, -+ }, { -+ .tx_buf = buf, -+ .len = count, -+ .fast_write = 1, -+ }, { -+ .len = 1, -+ .fast_write = 1, -+ }, -+ }; -+ struct spi_message m; -+ -+ if (rb4xx_cpld == NULL) -+ return -ENODEV; -+ -+ spi_message_init(&m); -+ spi_message_add_tail(&t[0], &m); -+ spi_message_add_tail(&t[1], &m); -+ spi_message_add_tail(&t[2], &m); -+ return spi_sync(rb4xx_cpld->spi, &m); -+#endif -+} -+EXPORT_SYMBOL_GPL(rb4xx_cpld_write); -+ -+static int rb4xx_cpld_gpio_get(struct gpio_chip *chip, unsigned offset) -+{ -+ struct rb4xx_cpld *cpld = gpio_to_cpld(chip); -+ int ret; -+ -+ mutex_lock(&cpld->lock); -+ ret = (cpld->config >> offset) & 1; -+ mutex_unlock(&cpld->lock); -+ -+ return ret; -+} -+ -+static void rb4xx_cpld_gpio_set(struct gpio_chip *chip, unsigned offset, -+ int value) -+{ -+ struct rb4xx_cpld *cpld = gpio_to_cpld(chip); -+ -+ mutex_lock(&cpld->lock); -+ __rb4xx_cpld_change_cfg(cpld, (1 << offset), !!value << offset); -+ mutex_unlock(&cpld->lock); -+} -+ -+static int rb4xx_cpld_gpio_direction_input(struct gpio_chip *chip, -+ unsigned offset) -+{ -+ return -EOPNOTSUPP; -+} -+ -+static int rb4xx_cpld_gpio_direction_output(struct gpio_chip *chip, -+ unsigned offset, -+ int value) -+{ -+ struct rb4xx_cpld *cpld = gpio_to_cpld(chip); -+ int ret; -+ -+ mutex_lock(&cpld->lock); -+ ret = __rb4xx_cpld_change_cfg(cpld, (1 << offset), !!value << offset); -+ mutex_unlock(&cpld->lock); -+ -+ return ret; -+} -+ -+static int rb4xx_cpld_gpio_init(struct rb4xx_cpld *cpld, unsigned int base) -+{ -+ int err; -+ -+ /* init config */ -+ cpld->config = CPLD_CFG_nLED1 | CPLD_CFG_nLED2 | CPLD_CFG_nLED3 | -+ CPLD_CFG_nLED4 | CPLD_CFG_nCE; -+ rb4xx_cpld_write_cfg(cpld, cpld->config); -+ -+ /* setup GPIO chip */ -+ cpld->chip.label = DRV_NAME; -+ -+ cpld->chip.get = rb4xx_cpld_gpio_get; -+ cpld->chip.set = rb4xx_cpld_gpio_set; -+ cpld->chip.direction_input = rb4xx_cpld_gpio_direction_input; -+ cpld->chip.direction_output = rb4xx_cpld_gpio_direction_output; -+ -+ cpld->chip.base = base; -+ cpld->chip.ngpio = CPLD_NUM_GPIOS; -+ cpld->chip.can_sleep = 1; -+ cpld->chip.dev = &cpld->spi->dev; -+ cpld->chip.owner = THIS_MODULE; -+ -+ err = gpiochip_add(&cpld->chip); -+ if (err) -+ dev_err(&cpld->spi->dev, "adding GPIO chip failed, err=%d\n", -+ err); -+ -+ return err; -+} -+ -+static int rb4xx_cpld_probe(struct spi_device *spi) -+{ -+ struct rb4xx_cpld *cpld; -+ struct rb4xx_cpld_platform_data *pdata; -+ int err; -+ -+ pdata = spi->dev.platform_data; -+ if (!pdata) { -+ dev_dbg(&spi->dev, "no platform data\n"); -+ return -EINVAL; -+ } -+ -+ cpld = kzalloc(sizeof(*cpld), GFP_KERNEL); -+ if (!cpld) { -+ dev_err(&spi->dev, "no memory for private data\n"); -+ return -ENOMEM; -+ } -+ -+ mutex_init(&cpld->lock); -+ cpld->spi = spi_dev_get(spi); -+ dev_set_drvdata(&spi->dev, cpld); -+ -+ spi->mode = SPI_MODE_0; -+ spi->bits_per_word = 8; -+ err = spi_setup(spi); -+ if (err) { -+ dev_err(&spi->dev, "spi_setup failed, err=%d\n", err); -+ goto err_drvdata; -+ } -+ -+ err = rb4xx_cpld_gpio_init(cpld, pdata->gpio_base); -+ if (err) -+ goto err_drvdata; -+ -+ rb4xx_cpld = cpld; -+ -+ return 0; -+ -+err_drvdata: -+ dev_set_drvdata(&spi->dev, NULL); -+ kfree(cpld); -+ -+ return err; -+} -+ -+static int rb4xx_cpld_remove(struct spi_device *spi) -+{ -+ struct rb4xx_cpld *cpld; -+ -+ rb4xx_cpld = NULL; -+ cpld = dev_get_drvdata(&spi->dev); -+ dev_set_drvdata(&spi->dev, NULL); -+ kfree(cpld); -+ -+ return 0; -+} -+ -+static struct spi_driver rb4xx_cpld_driver = { -+ .driver = { -+ .name = DRV_NAME, -+ .bus = &spi_bus_type, -+ .owner = THIS_MODULE, -+ }, -+ .probe = rb4xx_cpld_probe, -+ .remove = rb4xx_cpld_remove, -+}; -+ -+static int __init rb4xx_cpld_init(void) -+{ -+ return spi_register_driver(&rb4xx_cpld_driver); -+} -+module_init(rb4xx_cpld_init); -+ -+static void __exit rb4xx_cpld_exit(void) -+{ -+ spi_unregister_driver(&rb4xx_cpld_driver); -+} -+module_exit(rb4xx_cpld_exit); -+ -+MODULE_DESCRIPTION(DRV_DESC); -+MODULE_VERSION(DRV_VERSION); -+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); -+MODULE_LICENSE("GPL v2"); --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0008-gpio-add-GPIO-latch-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0008-gpio-add-GPIO-latch-driver.patch deleted file mode 100644 index 188cec3b2..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0008-gpio-add-GPIO-latch-driver.patch +++ /dev/null @@ -1,290 +0,0 @@ -From dd93d7e5b6530f1574860776fe6f960c4fd2661d Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:21:54 +0200 -Subject: [PATCH] gpio: add GPIO latch driver - ---- - drivers/gpio/Kconfig | 7 + - drivers/gpio/Makefile | 1 + - drivers/gpio/gpio-latch.c | 219 +++++++++++++++++++++++++++++++ - include/linux/platform_data/gpio-latch.h | 14 ++ - 4 files changed, 241 insertions(+) - create mode 100644 drivers/gpio/gpio-latch.c - create mode 100644 include/linux/platform_data/gpio-latch.h - -diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig -index 903f24d..905730b 100644 ---- a/drivers/gpio/Kconfig -+++ b/drivers/gpio/Kconfig -@@ -834,4 +834,11 @@ config GPIO_VIPERBOARD - River Tech's viperboard.h for detailed meaning - of the module parameters. - -+comment "Other GPIO expanders" -+ -+config GPIO_LATCH -+ tristate "GPIO latch driver" -+ help -+ Say yes here to enable a GPIO latch driver. -+ - endif -diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile -index 5d50179..7d03524 100644 ---- a/drivers/gpio/Makefile -+++ b/drivers/gpio/Makefile -@@ -36,6 +36,7 @@ obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o - obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o - obj-$(CONFIG_GPIO_INTEL_MID) += gpio-intel-mid.o - obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o -+obj-$(CONFIG_GPIO_LATCH) += gpio-latch.o - obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o - obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o - obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o -diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c -new file mode 100644 -index 0000000..1efa1a1 ---- /dev/null -+++ b/drivers/gpio/gpio-latch.c -@@ -0,0 +1,219 @@ -+/* -+ * GPIO latch driver -+ * -+ * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org> -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/gpio.h> -+#include <linux/slab.h> -+#include <linux/platform_device.h> -+ -+#include <linux/platform_data/gpio-latch.h> -+ -+struct gpio_latch_chip { -+ struct gpio_chip gc; -+ -+ struct mutex mutex; -+ struct mutex latch_mutex; -+ bool latch_enabled; -+ int le_gpio; -+ bool le_active_low; -+ int *gpios; -+}; -+ -+static inline struct gpio_latch_chip *to_gpio_latch_chip(struct gpio_chip *gc) -+{ -+ return container_of(gc, struct gpio_latch_chip, gc); -+} -+ -+static void gpio_latch_lock(struct gpio_latch_chip *glc, bool enable) -+{ -+ mutex_lock(&glc->mutex); -+ -+ if (enable) -+ glc->latch_enabled = true; -+ -+ if (glc->latch_enabled) -+ mutex_lock(&glc->latch_mutex); -+} -+ -+static void gpio_latch_unlock(struct gpio_latch_chip *glc, bool disable) -+{ -+ if (glc->latch_enabled) -+ mutex_unlock(&glc->latch_mutex); -+ -+ if (disable) -+ glc->latch_enabled = true; -+ -+ mutex_unlock(&glc->mutex); -+} -+ -+static int -+gpio_latch_get(struct gpio_chip *gc, unsigned offset) -+{ -+ struct gpio_latch_chip *glc = to_gpio_latch_chip(gc); -+ int ret; -+ -+ gpio_latch_lock(glc, false); -+ ret = gpio_get_value(glc->gpios[offset]); -+ gpio_latch_unlock(glc, false); -+ -+ return ret; -+} -+ -+static void -+gpio_latch_set(struct gpio_chip *gc, unsigned offset, int value) -+{ -+ struct gpio_latch_chip *glc = to_gpio_latch_chip(gc); -+ bool enable_latch = false; -+ bool disable_latch = false; -+ int gpio; -+ -+ gpio = glc->gpios[offset]; -+ -+ if (gpio == glc->le_gpio) { -+ enable_latch = value ^ glc->le_active_low; -+ disable_latch = !enable_latch; -+ } -+ -+ gpio_latch_lock(glc, enable_latch); -+ gpio_set_value(gpio, value); -+ gpio_latch_unlock(glc, disable_latch); -+} -+ -+static int -+gpio_latch_direction_input(struct gpio_chip *gc, unsigned offset) -+{ -+ struct gpio_latch_chip *glc = to_gpio_latch_chip(gc); -+ int ret; -+ -+ gpio_latch_lock(glc, false); -+ ret = gpio_direction_input(glc->gpios[offset]); -+ gpio_latch_unlock(glc, false); -+ -+ return ret; -+} -+ -+static int -+gpio_latch_direction_output(struct gpio_chip *gc, unsigned offset, int value) -+{ -+ struct gpio_latch_chip *glc = to_gpio_latch_chip(gc); -+ bool enable_latch = false; -+ bool disable_latch = false; -+ int gpio; -+ int ret; -+ -+ gpio = glc->gpios[offset]; -+ -+ if (gpio == glc->le_gpio) { -+ enable_latch = value ^ glc->le_active_low; -+ disable_latch = !enable_latch; -+ } -+ -+ gpio_latch_lock(glc, enable_latch); -+ ret = gpio_direction_output(gpio, value); -+ gpio_latch_unlock(glc, disable_latch); -+ -+ return ret; -+} -+ -+static int gpio_latch_probe(struct platform_device *pdev) -+{ -+ struct gpio_latch_chip *glc; -+ struct gpio_latch_platform_data *pdata; -+ struct gpio_chip *gc; -+ int size; -+ int ret; -+ int i; -+ -+ pdata = dev_get_platdata(&pdev->dev); -+ if (!pdata) -+ return -EINVAL; -+ -+ if (pdata->le_gpio_index >= pdata->num_gpios || -+ !pdata->num_gpios || -+ !pdata->gpios) -+ return -EINVAL; -+ -+ for (i = 0; i < pdata->num_gpios; i++) { -+ int gpio = pdata->gpios[i]; -+ -+ ret = devm_gpio_request(&pdev->dev, gpio, -+ GPIO_LATCH_DRIVER_NAME); -+ if (ret) -+ return ret; -+ } -+ -+ glc = devm_kzalloc(&pdev->dev, sizeof(*glc), GFP_KERNEL); -+ if (!glc) -+ return -ENOMEM; -+ -+ mutex_init(&glc->mutex); -+ mutex_init(&glc->latch_mutex); -+ -+ size = pdata->num_gpios * sizeof(glc->gpios[0]); -+ glc->gpios = devm_kzalloc(&pdev->dev, size , GFP_KERNEL); -+ if (!glc->gpios) -+ return -ENOMEM; -+ -+ memcpy(glc->gpios, pdata->gpios, size); -+ -+ glc->le_gpio = glc->gpios[pdata->le_gpio_index]; -+ glc->le_active_low = pdata->le_active_low; -+ -+ gc = &glc->gc; -+ -+ gc->label = GPIO_LATCH_DRIVER_NAME; -+ gc->base = pdata->base; -+ gc->can_sleep = true; -+ gc->ngpio = pdata->num_gpios; -+ gc->get = gpio_latch_get; -+ gc->set = gpio_latch_set; -+ gc->direction_input = gpio_latch_direction_input, -+ gc->direction_output = gpio_latch_direction_output; -+ -+ platform_set_drvdata(pdev, glc); -+ -+ ret = gpiochip_add(&glc->gc); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int gpio_latch_remove(struct platform_device *pdev) -+{ -+ struct gpio_latch_chip *glc = platform_get_drvdata(pdev); -+ -+ return gpiochip_remove(&glc->gc);; -+} -+ -+ -+static struct platform_driver gpio_latch_driver = { -+ .probe = gpio_latch_probe, -+ .remove = gpio_latch_remove, -+ .driver = { -+ .name = GPIO_LATCH_DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init gpio_latch_init(void) -+{ -+ return platform_driver_register(&gpio_latch_driver); -+} -+ -+postcore_initcall(gpio_latch_init); -+ -+MODULE_DESCRIPTION("GPIO latch driver"); -+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:" GPIO_LATCH_DRIVER_NAME); -diff --git a/include/linux/platform_data/gpio-latch.h b/include/linux/platform_data/gpio-latch.h -new file mode 100644 -index 0000000..0450e67 ---- /dev/null -+++ b/include/linux/platform_data/gpio-latch.h -@@ -0,0 +1,14 @@ -+#ifndef _GPIO_LATCH_H_ -+#define _GPIO_LATCH_H_ -+ -+#define GPIO_LATCH_DRIVER_NAME "gpio-latch" -+ -+struct gpio_latch_platform_data { -+ int base; -+ int num_gpios; -+ int *gpios; -+ int le_gpio_index; -+ bool le_active_low; -+}; -+ -+#endif /* _GPIO_LATCH_H_ */ --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0009-spi-export-spi_bitbang_bufs-function.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0009-spi-export-spi_bitbang_bufs-function.patch deleted file mode 100644 index dc6af0a9d..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0009-spi-export-spi_bitbang_bufs-function.patch +++ /dev/null @@ -1,45 +0,0 @@ -From ff81dc67568d5393c30352c6075b43afc9de2329 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:22:55 +0200 -Subject: [PATCH] spi: export spi_bitbang_bufs function - ---- - drivers/spi/spi-bitbang.c | 3 ++- - include/linux/spi/spi_bitbang.h | 1 + - 2 files changed, 3 insertions(+), 1 deletion(-) - -diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c -index bd222f6..2145d77 100644 ---- a/drivers/spi/spi-bitbang.c -+++ b/drivers/spi/spi-bitbang.c -@@ -234,13 +234,14 @@ void spi_bitbang_cleanup(struct spi_device *spi) - } - EXPORT_SYMBOL_GPL(spi_bitbang_cleanup); - --static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) -+int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) - { - struct spi_bitbang_cs *cs = spi->controller_state; - unsigned nsecs = cs->nsecs; - - return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t); - } -+EXPORT_SYMBOL_GPL(spi_bitbang_bufs); - - /*----------------------------------------------------------------------*/ - -diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h -index daebaba..1631d7a 100644 ---- a/include/linux/spi/spi_bitbang.h -+++ b/include/linux/spi/spi_bitbang.h -@@ -39,6 +39,7 @@ extern int spi_bitbang_setup(struct spi_device *spi); - extern void spi_bitbang_cleanup(struct spi_device *spi); - extern int spi_bitbang_setup_transfer(struct spi_device *spi, - struct spi_transfer *t); -+extern int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t); - - /* start or stop queue processing */ - extern int spi_bitbang_start(struct spi_bitbang *spi); --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0010-spi-add-type-field-to-spi_transfer-struct.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0010-spi-add-type-field-to-spi_transfer-struct.patch deleted file mode 100644 index 2721d3c4e..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0010-spi-add-type-field-to-spi_transfer-struct.patch +++ /dev/null @@ -1,37 +0,0 @@ -From eaf82ac5fc9272545d4d4fb4582eab69d37e389a Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:23:56 +0200 -Subject: [PATCH] spi: add type field to spi_transfer struct - ---- - include/linux/spi/spi.h | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h -index 4ee1a02..a77d6c6 100644 ---- a/include/linux/spi/spi.h -+++ b/include/linux/spi/spi.h -@@ -475,6 +475,12 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum); - - /*---------------------------------------------------------------------------*/ - -+enum spi_transfer_type { -+ SPI_TRANSFER_GENERIC = 0, -+ SPI_TRANSFER_FLASH_READ_CMD, -+ SPI_TRANSFER_FLASH_READ_DATA, -+}; -+ - /* - * I/O INTERFACE between SPI controller and protocol drivers - * -@@ -591,6 +597,7 @@ struct spi_transfer { - u8 bits_per_word; - u16 delay_usecs; - u32 speed_hz; -+ enum spi_transfer_type type; - - struct list_head transfer_list; - }; --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0011-mtd-m25p80-set-SPI-transfer-type.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0011-mtd-m25p80-set-SPI-transfer-type.patch deleted file mode 100644 index e2dfad6e0..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0011-mtd-m25p80-set-SPI-transfer-type.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 531989d989855f673af76ef85300769a8a167405 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:25:59 +0200 -Subject: [PATCH] mtd: m25p80: set SPI transfer type - ---- - drivers/mtd/devices/m25p80.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c -index ad19139..cdabcc0 100644 ---- a/drivers/mtd/devices/m25p80.c -+++ b/drivers/mtd/devices/m25p80.c -@@ -524,10 +524,12 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, - return -EINVAL; - } - -+ t[0].type = SPI_TRANSFER_FLASH_READ_CMD; - t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash) + dummy; - spi_message_add_tail(&t[0], &m); - -+ t[1].type = SPI_TRANSFER_FLASH_READ_DATA; - t[1].rx_buf = buf; - t[1].rx_nbits = m25p80_rx_nbits(flash); - t[1].len = len; --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0012-mips-ath79-swizzle-PCI-address-for-ar71xx.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0012-mips-ath79-swizzle-PCI-address-for-ar71xx.patch deleted file mode 100644 index c63489112..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0012-mips-ath79-swizzle-PCI-address-for-ar71xx.patch +++ /dev/null @@ -1,130 +0,0 @@ -From 0c139cb15774f3c41a0cf6620727e676c874834a Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:28:24 +0200 -Subject: [PATCH] mips: ath79: swizzle PCI address for ar71xx - ---- - arch/mips/ath79/pci.c | 42 ++++++++++++++++++++++++++ - arch/mips/include/asm/mach-ath79/mangle-port.h | 37 +++++++++++++++++++++++ - 2 files changed, 79 insertions(+) - create mode 100644 arch/mips/include/asm/mach-ath79/mangle-port.h - -diff --git a/arch/mips/ath79/pci.c b/arch/mips/ath79/pci.c -index 730c0b0..47be58c 100644 ---- a/arch/mips/ath79/pci.c -+++ b/arch/mips/ath79/pci.c -@@ -13,6 +13,7 @@ - */ - - #include <linux/init.h> -+#include <linux/export.h> - #include <linux/pci.h> - #include <linux/resource.h> - #include <linux/platform_device.h> -@@ -25,6 +26,9 @@ static int (*ath79_pci_plat_dev_init)(struct pci_dev *dev); - static const struct ath79_pci_irq *ath79_pci_irq_map __initdata; - static unsigned ath79_pci_nr_irqs __initdata; - -+static unsigned long (*__ath79_pci_swizzle_b)(unsigned long port); -+static unsigned long (*__ath79_pci_swizzle_w)(unsigned long port); -+ - static const struct ath79_pci_irq ar71xx_pci_irq_map[] __initconst = { - { - .slot = 17, -@@ -212,12 +216,50 @@ ath79_register_pci_ar724x(int id, - return pdev; - } - -+static inline bool ar71xx_is_pci_addr(unsigned long port) -+{ -+ unsigned long phys = CPHYSADDR(port); -+ -+ return (phys >= AR71XX_PCI_MEM_BASE && -+ phys < AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE); -+} -+ -+static unsigned long ar71xx_pci_swizzle_b(unsigned long port) -+{ -+ return ar71xx_is_pci_addr(port) ? port ^ 3 : port; -+} -+ -+static unsigned long ar71xx_pci_swizzle_w(unsigned long port) -+{ -+ return ar71xx_is_pci_addr(port) ? port ^ 2 : port; -+} -+ -+unsigned long ath79_pci_swizzle_b(unsigned long port) -+{ -+ if (__ath79_pci_swizzle_b) -+ return __ath79_pci_swizzle_b(port); -+ -+ return port; -+} -+EXPORT_SYMBOL(ath79_pci_swizzle_b); -+ -+unsigned long ath79_pci_swizzle_w(unsigned long port) -+{ -+ if (__ath79_pci_swizzle_w) -+ return __ath79_pci_swizzle_w(port); -+ -+ return port; -+} -+EXPORT_SYMBOL(ath79_pci_swizzle_w); -+ - int __init ath79_register_pci(void) - { - struct platform_device *pdev = NULL; - - if (soc_is_ar71xx()) { - pdev = ath79_register_pci_ar71xx(); -+ __ath79_pci_swizzle_b = ar71xx_pci_swizzle_b; -+ __ath79_pci_swizzle_w = ar71xx_pci_swizzle_w; - } else if (soc_is_ar724x()) { - pdev = ath79_register_pci_ar724x(-1, - AR724X_PCI_CFG_BASE, -diff --git a/arch/mips/include/asm/mach-ath79/mangle-port.h b/arch/mips/include/asm/mach-ath79/mangle-port.h -new file mode 100644 -index 0000000..ffd4e20 ---- /dev/null -+++ b/arch/mips/include/asm/mach-ath79/mangle-port.h -@@ -0,0 +1,37 @@ -+/* -+ * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org> -+ * -+ * This file was derived from: inlude/asm-mips/mach-generic/mangle-port.h -+ * Copyright (C) 2003, 2004 Ralf Baechle -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#ifndef __ASM_MACH_ATH79_MANGLE_PORT_H -+#define __ASM_MACH_ATH79_MANGLE_PORT_H -+ -+#ifdef CONFIG_PCI -+extern unsigned long (ath79_pci_swizzle_b)(unsigned long port); -+extern unsigned long (ath79_pci_swizzle_w)(unsigned long port); -+#else -+#define ath79_pci_swizzle_b(port) (port) -+#define ath79_pci_swizzle_w(port) (port) -+#endif -+ -+#define __swizzle_addr_b(port) ath79_pci_swizzle_b(port) -+#define __swizzle_addr_w(port) ath79_pci_swizzle_w(port) -+#define __swizzle_addr_l(port) (port) -+#define __swizzle_addr_q(port) (port) -+ -+# define ioswabb(a, x) (x) -+# define __mem_ioswabb(a, x) (x) -+# define ioswabw(a, x) (x) -+# define __mem_ioswabw(a, x) cpu_to_le16(x) -+# define ioswabl(a, x) (x) -+# define __mem_ioswabl(a, x) cpu_to_le32(x) -+# define ioswabq(a, x) (x) -+# define __mem_ioswabq(a, x) cpu_to_le64(x) -+ -+#endif /* __ASM_MACH_ATH79_MANGLE_PORT_H */ --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0013-net-add-swconfig-support.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0013-net-add-swconfig-support.patch deleted file mode 100644 index 57c112842..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0013-net-add-swconfig-support.patch +++ /dev/null @@ -1,1859 +0,0 @@ -From fe40f6aba9ba59000ffa681a23ad59e9347346af Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:36:13 +0200 -Subject: [PATCH] net: add swconfig support - ---- - drivers/net/phy/Kconfig | 10 + - drivers/net/phy/Makefile | 1 + - drivers/net/phy/swconfig.c | 1144 +++++++++++++++++++++++++++++++++++++++ - drivers/net/phy/swconfig_leds.c | 354 ++++++++++++ - include/linux/switch.h | 167 ++++++ - include/uapi/linux/Kbuild | 1 + - include/uapi/linux/switch.h | 103 ++++ - 7 files changed, 1780 insertions(+) - create mode 100644 drivers/net/phy/swconfig.c - create mode 100644 drivers/net/phy/swconfig_leds.c - create mode 100644 include/linux/switch.h - create mode 100644 include/uapi/linux/switch.h - -diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig -index 9b5d46c..36a13fc 100644 ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -12,6 +12,16 @@ menuconfig PHYLIB - - if PHYLIB - -+config SWCONFIG -+ tristate "Switch configuration API" -+ ---help--- -+ Switch configuration API using netlink. This allows -+ you to configure the VLAN features of certain switches. -+ -+config SWCONFIG_LEDS -+ bool "Switch LED trigger support" -+ depends on (SWCONFIG && LEDS_TRIGGERS) -+ - comment "MII PHY device drivers" - - config AT803X_PHY -diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile -index 9013dfa..b510bd6 100644 ---- a/drivers/net/phy/Makefile -+++ b/drivers/net/phy/Makefile -@@ -3,6 +3,7 @@ - libphy-objs := phy.o phy_device.o mdio_bus.o - - obj-$(CONFIG_PHYLIB) += libphy.o -+obj-$(CONFIG_SWCONFIG) += swconfig.o - obj-$(CONFIG_MARVELL_PHY) += marvell.o - obj-$(CONFIG_DAVICOM_PHY) += davicom.o - obj-$(CONFIG_CICADA_PHY) += cicada.o -diff --git a/drivers/net/phy/swconfig.c b/drivers/net/phy/swconfig.c -new file mode 100644 -index 0000000..c043ee4 ---- /dev/null -+++ b/drivers/net/phy/swconfig.c -@@ -0,0 +1,1144 @@ -+/* -+ * swconfig.c: Switch configuration API -+ * -+ * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2 -+ * of the License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include <linux/types.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/list.h> -+#include <linux/if.h> -+#include <linux/if_ether.h> -+#include <linux/capability.h> -+#include <linux/skbuff.h> -+#include <linux/switch.h> -+#include <linux/of.h> -+#include <linux/version.h> -+ -+#define SWCONFIG_DEVNAME "switch%d" -+ -+#include "swconfig_leds.c" -+ -+MODULE_AUTHOR("Felix Fietkau <nbd@openwrt.org>"); -+MODULE_LICENSE("GPL"); -+ -+static int swdev_id; -+static struct list_head swdevs; -+static DEFINE_SPINLOCK(swdevs_lock); -+struct swconfig_callback; -+ -+struct swconfig_callback { -+ struct sk_buff *msg; -+ struct genlmsghdr *hdr; -+ struct genl_info *info; -+ int cmd; -+ -+ /* callback for filling in the message data */ -+ int (*fill)(struct swconfig_callback *cb, void *arg); -+ -+ /* callback for closing the message before sending it */ -+ int (*close)(struct swconfig_callback *cb, void *arg); -+ -+ struct nlattr *nest[4]; -+ int args[4]; -+}; -+ -+/* defaults */ -+ -+static int -+swconfig_get_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ int ret; -+ if (val->port_vlan >= dev->vlans) -+ return -EINVAL; -+ -+ if (!dev->ops->get_vlan_ports) -+ return -EOPNOTSUPP; -+ -+ ret = dev->ops->get_vlan_ports(dev, val); -+ return ret; -+} -+ -+static int -+swconfig_set_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct switch_port *ports = val->value.ports; -+ const struct switch_dev_ops *ops = dev->ops; -+ int i; -+ -+ if (val->port_vlan >= dev->vlans) -+ return -EINVAL; -+ -+ /* validate ports */ -+ if (val->len > dev->ports) -+ return -EINVAL; -+ -+ if (!ops->set_vlan_ports) -+ return -EOPNOTSUPP; -+ -+ for (i = 0; i < val->len; i++) { -+ if (ports[i].id >= dev->ports) -+ return -EINVAL; -+ -+ if (ops->set_port_pvid && -+ !(ports[i].flags & (1 << SWITCH_PORT_FLAG_TAGGED))) -+ ops->set_port_pvid(dev, ports[i].id, val->port_vlan); -+ } -+ -+ return ops->set_vlan_ports(dev, val); -+} -+ -+static int -+swconfig_set_pvid(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ if (val->port_vlan >= dev->ports) -+ return -EINVAL; -+ -+ if (!dev->ops->set_port_pvid) -+ return -EOPNOTSUPP; -+ -+ return dev->ops->set_port_pvid(dev, val->port_vlan, val->value.i); -+} -+ -+static int -+swconfig_get_pvid(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ if (val->port_vlan >= dev->ports) -+ return -EINVAL; -+ -+ if (!dev->ops->get_port_pvid) -+ return -EOPNOTSUPP; -+ -+ return dev->ops->get_port_pvid(dev, val->port_vlan, &val->value.i); -+} -+ -+static const char * -+swconfig_speed_str(enum switch_port_speed speed) -+{ -+ switch (speed) { -+ case SWITCH_PORT_SPEED_10: -+ return "10baseT"; -+ case SWITCH_PORT_SPEED_100: -+ return "100baseT"; -+ case SWITCH_PORT_SPEED_1000: -+ return "1000baseT"; -+ default: -+ break; -+ } -+ -+ return "unknown"; -+} -+ -+static int -+swconfig_get_link(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct switch_port_link link; -+ int len; -+ int ret; -+ -+ if (val->port_vlan >= dev->ports) -+ return -EINVAL; -+ -+ if (!dev->ops->get_port_link) -+ return -EOPNOTSUPP; -+ -+ memset(&link, 0, sizeof(link)); -+ ret = dev->ops->get_port_link(dev, val->port_vlan, &link); -+ if (ret) -+ return ret; -+ -+ memset(dev->buf, 0, sizeof(dev->buf)); -+ -+ if (link.link) -+ len = snprintf(dev->buf, sizeof(dev->buf), -+ "port:%d link:up speed:%s %s-duplex %s%s%s", -+ val->port_vlan, -+ swconfig_speed_str(link.speed), -+ link.duplex ? "full" : "half", -+ link.tx_flow ? "txflow " : "", -+ link.rx_flow ? "rxflow " : "", -+ link.aneg ? "auto" : ""); -+ else -+ len = snprintf(dev->buf, sizeof(dev->buf), "port:%d link:down", -+ val->port_vlan); -+ -+ val->value.s = dev->buf; -+ val->len = len; -+ -+ return 0; -+} -+ -+static int -+swconfig_apply_config(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ /* don't complain if not supported by the switch driver */ -+ if (!dev->ops->apply_config) -+ return 0; -+ -+ return dev->ops->apply_config(dev); -+} -+ -+static int -+swconfig_reset_switch(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ /* don't complain if not supported by the switch driver */ -+ if (!dev->ops->reset_switch) -+ return 0; -+ -+ return dev->ops->reset_switch(dev); -+} -+ -+enum global_defaults { -+ GLOBAL_APPLY, -+ GLOBAL_RESET, -+}; -+ -+enum vlan_defaults { -+ VLAN_PORTS, -+}; -+ -+enum port_defaults { -+ PORT_PVID, -+ PORT_LINK, -+}; -+ -+static struct switch_attr default_global[] = { -+ [GLOBAL_APPLY] = { -+ .type = SWITCH_TYPE_NOVAL, -+ .name = "apply", -+ .description = "Activate changes in the hardware", -+ .set = swconfig_apply_config, -+ }, -+ [GLOBAL_RESET] = { -+ .type = SWITCH_TYPE_NOVAL, -+ .name = "reset", -+ .description = "Reset the switch", -+ .set = swconfig_reset_switch, -+ } -+}; -+ -+static struct switch_attr default_port[] = { -+ [PORT_PVID] = { -+ .type = SWITCH_TYPE_INT, -+ .name = "pvid", -+ .description = "Primary VLAN ID", -+ .set = swconfig_set_pvid, -+ .get = swconfig_get_pvid, -+ }, -+ [PORT_LINK] = { -+ .type = SWITCH_TYPE_STRING, -+ .name = "link", -+ .description = "Get port link information", -+ .set = NULL, -+ .get = swconfig_get_link, -+ } -+}; -+ -+static struct switch_attr default_vlan[] = { -+ [VLAN_PORTS] = { -+ .type = SWITCH_TYPE_PORTS, -+ .name = "ports", -+ .description = "VLAN port mapping", -+ .set = swconfig_set_vlan_ports, -+ .get = swconfig_get_vlan_ports, -+ }, -+}; -+ -+static const struct switch_attr * -+swconfig_find_attr_by_name(const struct switch_attrlist *alist, -+ const char *name) -+{ -+ int i; -+ -+ for (i = 0; i < alist->n_attr; i++) -+ if (strcmp(name, alist->attr[i].name) == 0) -+ return &alist->attr[i]; -+ -+ return NULL; -+} -+ -+static void swconfig_defaults_init(struct switch_dev *dev) -+{ -+ const struct switch_dev_ops *ops = dev->ops; -+ -+ dev->def_global = 0; -+ dev->def_vlan = 0; -+ dev->def_port = 0; -+ -+ if (ops->get_vlan_ports || ops->set_vlan_ports) -+ set_bit(VLAN_PORTS, &dev->def_vlan); -+ -+ if (ops->get_port_pvid || ops->set_port_pvid) -+ set_bit(PORT_PVID, &dev->def_port); -+ -+ if (ops->get_port_link && -+ !swconfig_find_attr_by_name(&ops->attr_port, "link")) -+ set_bit(PORT_LINK, &dev->def_port); -+ -+ /* always present, can be no-op */ -+ set_bit(GLOBAL_APPLY, &dev->def_global); -+ set_bit(GLOBAL_RESET, &dev->def_global); -+} -+ -+ -+static struct genl_family switch_fam = { -+ .id = GENL_ID_GENERATE, -+ .name = "switch", -+ .hdrsize = 0, -+ .version = 1, -+ .maxattr = SWITCH_ATTR_MAX, -+}; -+ -+static const struct nla_policy switch_policy[SWITCH_ATTR_MAX+1] = { -+ [SWITCH_ATTR_ID] = { .type = NLA_U32 }, -+ [SWITCH_ATTR_OP_ID] = { .type = NLA_U32 }, -+ [SWITCH_ATTR_OP_PORT] = { .type = NLA_U32 }, -+ [SWITCH_ATTR_OP_VLAN] = { .type = NLA_U32 }, -+ [SWITCH_ATTR_OP_VALUE_INT] = { .type = NLA_U32 }, -+ [SWITCH_ATTR_OP_VALUE_STR] = { .type = NLA_NUL_STRING }, -+ [SWITCH_ATTR_OP_VALUE_PORTS] = { .type = NLA_NESTED }, -+ [SWITCH_ATTR_TYPE] = { .type = NLA_U32 }, -+}; -+ -+static const struct nla_policy port_policy[SWITCH_PORT_ATTR_MAX+1] = { -+ [SWITCH_PORT_ID] = { .type = NLA_U32 }, -+ [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG }, -+}; -+ -+static inline void -+swconfig_lock(void) -+{ -+ spin_lock(&swdevs_lock); -+} -+ -+static inline void -+swconfig_unlock(void) -+{ -+ spin_unlock(&swdevs_lock); -+} -+ -+static struct switch_dev * -+swconfig_get_dev(struct genl_info *info) -+{ -+ struct switch_dev *dev = NULL; -+ struct switch_dev *p; -+ int id; -+ -+ if (!info->attrs[SWITCH_ATTR_ID]) -+ goto done; -+ -+ id = nla_get_u32(info->attrs[SWITCH_ATTR_ID]); -+ swconfig_lock(); -+ list_for_each_entry(p, &swdevs, dev_list) { -+ if (id != p->id) -+ continue; -+ -+ dev = p; -+ break; -+ } -+ if (dev) -+ mutex_lock(&dev->sw_mutex); -+ else -+ pr_debug("device %d not found\n", id); -+ swconfig_unlock(); -+done: -+ return dev; -+} -+ -+static inline void -+swconfig_put_dev(struct switch_dev *dev) -+{ -+ mutex_unlock(&dev->sw_mutex); -+} -+ -+static int -+swconfig_dump_attr(struct swconfig_callback *cb, void *arg) -+{ -+ struct switch_attr *op = arg; -+ struct genl_info *info = cb->info; -+ struct sk_buff *msg = cb->msg; -+ int id = cb->args[0]; -+ void *hdr; -+ -+ hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam, -+ NLM_F_MULTI, SWITCH_CMD_NEW_ATTR); -+ if (IS_ERR(hdr)) -+ return -1; -+ -+ if (nla_put_u32(msg, SWITCH_ATTR_OP_ID, id)) -+ goto nla_put_failure; -+ if (nla_put_u32(msg, SWITCH_ATTR_OP_TYPE, op->type)) -+ goto nla_put_failure; -+ if (nla_put_string(msg, SWITCH_ATTR_OP_NAME, op->name)) -+ goto nla_put_failure; -+ if (op->description) -+ if (nla_put_string(msg, SWITCH_ATTR_OP_DESCRIPTION, -+ op->description)) -+ goto nla_put_failure; -+ -+ return genlmsg_end(msg, hdr); -+nla_put_failure: -+ genlmsg_cancel(msg, hdr); -+ return -EMSGSIZE; -+} -+ -+/* spread multipart messages across multiple message buffers */ -+static int -+swconfig_send_multipart(struct swconfig_callback *cb, void *arg) -+{ -+ struct genl_info *info = cb->info; -+ int restart = 0; -+ int err; -+ -+ do { -+ if (!cb->msg) { -+ cb->msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); -+ if (cb->msg == NULL) -+ goto error; -+ } -+ -+ if (!(cb->fill(cb, arg) < 0)) -+ break; -+ -+ /* fill failed, check if this was already the second attempt */ -+ if (restart) -+ goto error; -+ -+ /* try again in a new message, send the current one */ -+ restart = 1; -+ if (cb->close) { -+ if (cb->close(cb, arg) < 0) -+ goto error; -+ } -+ err = genlmsg_reply(cb->msg, info); -+ cb->msg = NULL; -+ if (err < 0) -+ goto error; -+ -+ } while (restart); -+ -+ return 0; -+ -+error: -+ if (cb->msg) -+ nlmsg_free(cb->msg); -+ return -1; -+} -+ -+static int -+swconfig_list_attrs(struct sk_buff *skb, struct genl_info *info) -+{ -+ struct genlmsghdr *hdr = nlmsg_data(info->nlhdr); -+ const struct switch_attrlist *alist; -+ struct switch_dev *dev; -+ struct swconfig_callback cb; -+ int err = -EINVAL; -+ int i; -+ -+ /* defaults */ -+ struct switch_attr *def_list; -+ unsigned long *def_active; -+ int n_def; -+ -+ dev = swconfig_get_dev(info); -+ if (!dev) -+ return -EINVAL; -+ -+ switch (hdr->cmd) { -+ case SWITCH_CMD_LIST_GLOBAL: -+ alist = &dev->ops->attr_global; -+ def_list = default_global; -+ def_active = &dev->def_global; -+ n_def = ARRAY_SIZE(default_global); -+ break; -+ case SWITCH_CMD_LIST_VLAN: -+ alist = &dev->ops->attr_vlan; -+ def_list = default_vlan; -+ def_active = &dev->def_vlan; -+ n_def = ARRAY_SIZE(default_vlan); -+ break; -+ case SWITCH_CMD_LIST_PORT: -+ alist = &dev->ops->attr_port; -+ def_list = default_port; -+ def_active = &dev->def_port; -+ n_def = ARRAY_SIZE(default_port); -+ break; -+ default: -+ WARN_ON(1); -+ goto out; -+ } -+ -+ memset(&cb, 0, sizeof(cb)); -+ cb.info = info; -+ cb.fill = swconfig_dump_attr; -+ for (i = 0; i < alist->n_attr; i++) { -+ if (alist->attr[i].disabled) -+ continue; -+ cb.args[0] = i; -+ err = swconfig_send_multipart(&cb, (void *) &alist->attr[i]); -+ if (err < 0) -+ goto error; -+ } -+ -+ /* defaults */ -+ for (i = 0; i < n_def; i++) { -+ if (!test_bit(i, def_active)) -+ continue; -+ cb.args[0] = SWITCH_ATTR_DEFAULTS_OFFSET + i; -+ err = swconfig_send_multipart(&cb, (void *) &def_list[i]); -+ if (err < 0) -+ goto error; -+ } -+ swconfig_put_dev(dev); -+ -+ if (!cb.msg) -+ return 0; -+ -+ return genlmsg_reply(cb.msg, info); -+ -+error: -+ if (cb.msg) -+ nlmsg_free(cb.msg); -+out: -+ swconfig_put_dev(dev); -+ return err; -+} -+ -+static const struct switch_attr * -+swconfig_lookup_attr(struct switch_dev *dev, struct genl_info *info, -+ struct switch_val *val) -+{ -+ struct genlmsghdr *hdr = nlmsg_data(info->nlhdr); -+ const struct switch_attrlist *alist; -+ const struct switch_attr *attr = NULL; -+ int attr_id; -+ -+ /* defaults */ -+ struct switch_attr *def_list; -+ unsigned long *def_active; -+ int n_def; -+ -+ if (!info->attrs[SWITCH_ATTR_OP_ID]) -+ goto done; -+ -+ switch (hdr->cmd) { -+ case SWITCH_CMD_SET_GLOBAL: -+ case SWITCH_CMD_GET_GLOBAL: -+ alist = &dev->ops->attr_global; -+ def_list = default_global; -+ def_active = &dev->def_global; -+ n_def = ARRAY_SIZE(default_global); -+ break; -+ case SWITCH_CMD_SET_VLAN: -+ case SWITCH_CMD_GET_VLAN: -+ alist = &dev->ops->attr_vlan; -+ def_list = default_vlan; -+ def_active = &dev->def_vlan; -+ n_def = ARRAY_SIZE(default_vlan); -+ if (!info->attrs[SWITCH_ATTR_OP_VLAN]) -+ goto done; -+ val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_VLAN]); -+ if (val->port_vlan >= dev->vlans) -+ goto done; -+ break; -+ case SWITCH_CMD_SET_PORT: -+ case SWITCH_CMD_GET_PORT: -+ alist = &dev->ops->attr_port; -+ def_list = default_port; -+ def_active = &dev->def_port; -+ n_def = ARRAY_SIZE(default_port); -+ if (!info->attrs[SWITCH_ATTR_OP_PORT]) -+ goto done; -+ val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_PORT]); -+ if (val->port_vlan >= dev->ports) -+ goto done; -+ break; -+ default: -+ WARN_ON(1); -+ goto done; -+ } -+ -+ if (!alist) -+ goto done; -+ -+ attr_id = nla_get_u32(info->attrs[SWITCH_ATTR_OP_ID]); -+ if (attr_id >= SWITCH_ATTR_DEFAULTS_OFFSET) { -+ attr_id -= SWITCH_ATTR_DEFAULTS_OFFSET; -+ if (attr_id >= n_def) -+ goto done; -+ if (!test_bit(attr_id, def_active)) -+ goto done; -+ attr = &def_list[attr_id]; -+ } else { -+ if (attr_id >= alist->n_attr) -+ goto done; -+ attr = &alist->attr[attr_id]; -+ } -+ -+ if (attr->disabled) -+ attr = NULL; -+ -+done: -+ if (!attr) -+ pr_debug("attribute lookup failed\n"); -+ val->attr = attr; -+ return attr; -+} -+ -+static int -+swconfig_parse_ports(struct sk_buff *msg, struct nlattr *head, -+ struct switch_val *val, int max) -+{ -+ struct nlattr *nla; -+ int rem; -+ -+ val->len = 0; -+ nla_for_each_nested(nla, head, rem) { -+ struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1]; -+ struct switch_port *port = &val->value.ports[val->len]; -+ -+ if (val->len >= max) -+ return -EINVAL; -+ -+ if (nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, nla, -+ port_policy)) -+ return -EINVAL; -+ -+ if (!tb[SWITCH_PORT_ID]) -+ return -EINVAL; -+ -+ port->id = nla_get_u32(tb[SWITCH_PORT_ID]); -+ if (tb[SWITCH_PORT_FLAG_TAGGED]) -+ port->flags |= (1 << SWITCH_PORT_FLAG_TAGGED); -+ val->len++; -+ } -+ -+ return 0; -+} -+ -+static int -+swconfig_set_attr(struct sk_buff *skb, struct genl_info *info) -+{ -+ const struct switch_attr *attr; -+ struct switch_dev *dev; -+ struct switch_val val; -+ int err = -EINVAL; -+ -+ dev = swconfig_get_dev(info); -+ if (!dev) -+ return -EINVAL; -+ -+ memset(&val, 0, sizeof(val)); -+ attr = swconfig_lookup_attr(dev, info, &val); -+ if (!attr || !attr->set) -+ goto error; -+ -+ val.attr = attr; -+ switch (attr->type) { -+ case SWITCH_TYPE_NOVAL: -+ break; -+ case SWITCH_TYPE_INT: -+ if (!info->attrs[SWITCH_ATTR_OP_VALUE_INT]) -+ goto error; -+ val.value.i = -+ nla_get_u32(info->attrs[SWITCH_ATTR_OP_VALUE_INT]); -+ break; -+ case SWITCH_TYPE_STRING: -+ if (!info->attrs[SWITCH_ATTR_OP_VALUE_STR]) -+ goto error; -+ val.value.s = -+ nla_data(info->attrs[SWITCH_ATTR_OP_VALUE_STR]); -+ break; -+ case SWITCH_TYPE_PORTS: -+ val.value.ports = dev->portbuf; -+ memset(dev->portbuf, 0, -+ sizeof(struct switch_port) * dev->ports); -+ -+ /* TODO: implement multipart? */ -+ if (info->attrs[SWITCH_ATTR_OP_VALUE_PORTS]) { -+ err = swconfig_parse_ports(skb, -+ info->attrs[SWITCH_ATTR_OP_VALUE_PORTS], -+ &val, dev->ports); -+ if (err < 0) -+ goto error; -+ } else { -+ val.len = 0; -+ err = 0; -+ } -+ break; -+ default: -+ goto error; -+ } -+ -+ err = attr->set(dev, attr, &val); -+error: -+ swconfig_put_dev(dev); -+ return err; -+} -+ -+static int -+swconfig_close_portlist(struct swconfig_callback *cb, void *arg) -+{ -+ if (cb->nest[0]) -+ nla_nest_end(cb->msg, cb->nest[0]); -+ return 0; -+} -+ -+static int -+swconfig_send_port(struct swconfig_callback *cb, void *arg) -+{ -+ const struct switch_port *port = arg; -+ struct nlattr *p = NULL; -+ -+ if (!cb->nest[0]) { -+ cb->nest[0] = nla_nest_start(cb->msg, cb->cmd); -+ if (!cb->nest[0]) -+ return -1; -+ } -+ -+ p = nla_nest_start(cb->msg, SWITCH_ATTR_PORT); -+ if (!p) -+ goto error; -+ -+ if (nla_put_u32(cb->msg, SWITCH_PORT_ID, port->id)) -+ goto nla_put_failure; -+ if (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) { -+ if (nla_put_flag(cb->msg, SWITCH_PORT_FLAG_TAGGED)) -+ goto nla_put_failure; -+ } -+ -+ nla_nest_end(cb->msg, p); -+ return 0; -+ -+nla_put_failure: -+ nla_nest_cancel(cb->msg, p); -+error: -+ nla_nest_cancel(cb->msg, cb->nest[0]); -+ return -1; -+} -+ -+static int -+swconfig_send_ports(struct sk_buff **msg, struct genl_info *info, int attr, -+ const struct switch_val *val) -+{ -+ struct swconfig_callback cb; -+ int err = 0; -+ int i; -+ -+ if (!val->value.ports) -+ return -EINVAL; -+ -+ memset(&cb, 0, sizeof(cb)); -+ cb.cmd = attr; -+ cb.msg = *msg; -+ cb.info = info; -+ cb.fill = swconfig_send_port; -+ cb.close = swconfig_close_portlist; -+ -+ cb.nest[0] = nla_nest_start(cb.msg, cb.cmd); -+ for (i = 0; i < val->len; i++) { -+ err = swconfig_send_multipart(&cb, &val->value.ports[i]); -+ if (err) -+ goto done; -+ } -+ err = val->len; -+ swconfig_close_portlist(&cb, NULL); -+ *msg = cb.msg; -+ -+done: -+ return err; -+} -+ -+static int -+swconfig_get_attr(struct sk_buff *skb, struct genl_info *info) -+{ -+ struct genlmsghdr *hdr = nlmsg_data(info->nlhdr); -+ const struct switch_attr *attr; -+ struct switch_dev *dev; -+ struct sk_buff *msg = NULL; -+ struct switch_val val; -+ int err = -EINVAL; -+ int cmd = hdr->cmd; -+ -+ dev = swconfig_get_dev(info); -+ if (!dev) -+ return -EINVAL; -+ -+ memset(&val, 0, sizeof(val)); -+ attr = swconfig_lookup_attr(dev, info, &val); -+ if (!attr || !attr->get) -+ goto error; -+ -+ if (attr->type == SWITCH_TYPE_PORTS) { -+ val.value.ports = dev->portbuf; -+ memset(dev->portbuf, 0, -+ sizeof(struct switch_port) * dev->ports); -+ } -+ -+ err = attr->get(dev, attr, &val); -+ if (err) -+ goto error; -+ -+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); -+ if (!msg) -+ goto error; -+ -+ hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam, -+ 0, cmd); -+ if (IS_ERR(hdr)) -+ goto nla_put_failure; -+ -+ switch (attr->type) { -+ case SWITCH_TYPE_INT: -+ if (nla_put_u32(msg, SWITCH_ATTR_OP_VALUE_INT, val.value.i)) -+ goto nla_put_failure; -+ break; -+ case SWITCH_TYPE_STRING: -+ if (nla_put_string(msg, SWITCH_ATTR_OP_VALUE_STR, val.value.s)) -+ goto nla_put_failure; -+ break; -+ case SWITCH_TYPE_PORTS: -+ err = swconfig_send_ports(&msg, info, -+ SWITCH_ATTR_OP_VALUE_PORTS, &val); -+ if (err < 0) -+ goto nla_put_failure; -+ break; -+ default: -+ pr_debug("invalid type in attribute\n"); -+ err = -EINVAL; -+ goto error; -+ } -+ err = genlmsg_end(msg, hdr); -+ if (err < 0) -+ goto nla_put_failure; -+ -+ swconfig_put_dev(dev); -+ return genlmsg_reply(msg, info); -+ -+nla_put_failure: -+ if (msg) -+ nlmsg_free(msg); -+error: -+ swconfig_put_dev(dev); -+ if (!err) -+ err = -ENOMEM; -+ return err; -+} -+ -+static int -+swconfig_send_switch(struct sk_buff *msg, u32 pid, u32 seq, int flags, -+ const struct switch_dev *dev) -+{ -+ struct nlattr *p = NULL, *m = NULL; -+ void *hdr; -+ int i; -+ -+ hdr = genlmsg_put(msg, pid, seq, &switch_fam, flags, -+ SWITCH_CMD_NEW_ATTR); -+ if (IS_ERR(hdr)) -+ return -1; -+ -+ if (nla_put_u32(msg, SWITCH_ATTR_ID, dev->id)) -+ goto nla_put_failure; -+ if (nla_put_string(msg, SWITCH_ATTR_DEV_NAME, dev->devname)) -+ goto nla_put_failure; -+ if (nla_put_string(msg, SWITCH_ATTR_ALIAS, dev->alias)) -+ goto nla_put_failure; -+ if (nla_put_string(msg, SWITCH_ATTR_NAME, dev->name)) -+ goto nla_put_failure; -+ if (nla_put_u32(msg, SWITCH_ATTR_VLANS, dev->vlans)) -+ goto nla_put_failure; -+ if (nla_put_u32(msg, SWITCH_ATTR_PORTS, dev->ports)) -+ goto nla_put_failure; -+ if (nla_put_u32(msg, SWITCH_ATTR_CPU_PORT, dev->cpu_port)) -+ goto nla_put_failure; -+ -+ m = nla_nest_start(msg, SWITCH_ATTR_PORTMAP); -+ if (!m) -+ goto nla_put_failure; -+ for (i = 0; i < dev->ports; i++) { -+ p = nla_nest_start(msg, SWITCH_ATTR_PORTS); -+ if (!p) -+ continue; -+ if (dev->portmap[i].s) { -+ if (nla_put_string(msg, SWITCH_PORTMAP_SEGMENT, -+ dev->portmap[i].s)) -+ goto nla_put_failure; -+ if (nla_put_u32(msg, SWITCH_PORTMAP_VIRT, -+ dev->portmap[i].virt)) -+ goto nla_put_failure; -+ } -+ nla_nest_end(msg, p); -+ } -+ nla_nest_end(msg, m); -+ return genlmsg_end(msg, hdr); -+nla_put_failure: -+ genlmsg_cancel(msg, hdr); -+ return -EMSGSIZE; -+} -+ -+static int swconfig_dump_switches(struct sk_buff *skb, -+ struct netlink_callback *cb) -+{ -+ struct switch_dev *dev; -+ int start = cb->args[0]; -+ int idx = 0; -+ -+ swconfig_lock(); -+ list_for_each_entry(dev, &swdevs, dev_list) { -+ if (++idx <= start) -+ continue; -+ if (swconfig_send_switch(skb, NETLINK_CB(cb->skb).portid, -+ cb->nlh->nlmsg_seq, NLM_F_MULTI, -+ dev) < 0) -+ break; -+ } -+ swconfig_unlock(); -+ cb->args[0] = idx; -+ -+ return skb->len; -+} -+ -+static int -+swconfig_done(struct netlink_callback *cb) -+{ -+ return 0; -+} -+ -+static struct genl_ops swconfig_ops[] = { -+ { -+ .cmd = SWITCH_CMD_LIST_GLOBAL, -+ .doit = swconfig_list_attrs, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_LIST_VLAN, -+ .doit = swconfig_list_attrs, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_LIST_PORT, -+ .doit = swconfig_list_attrs, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_GET_GLOBAL, -+ .doit = swconfig_get_attr, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_GET_VLAN, -+ .doit = swconfig_get_attr, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_GET_PORT, -+ .doit = swconfig_get_attr, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_SET_GLOBAL, -+ .doit = swconfig_set_attr, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_SET_VLAN, -+ .doit = swconfig_set_attr, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_SET_PORT, -+ .doit = swconfig_set_attr, -+ .policy = switch_policy, -+ }, -+ { -+ .cmd = SWITCH_CMD_GET_SWITCH, -+ .dumpit = swconfig_dump_switches, -+ .policy = switch_policy, -+ .done = swconfig_done, -+ } -+}; -+ -+#ifdef CONFIG_OF -+void -+of_switch_load_portmap(struct switch_dev *dev) -+{ -+ struct device_node *port; -+ -+ if (!dev->of_node) -+ return; -+ -+ for_each_child_of_node(dev->of_node, port) { -+ const __be32 *prop; -+ const char *segment; -+ int size, phys; -+ -+ if (!of_device_is_compatible(port, "swconfig,port")) -+ continue; -+ -+ if (of_property_read_string(port, "swconfig,segment", &segment)) -+ continue; -+ -+ prop = of_get_property(port, "swconfig,portmap", &size); -+ if (!prop) -+ continue; -+ -+ if (size != (2 * sizeof(*prop))) { -+ pr_err("%s: failed to parse port mapping\n", -+ port->name); -+ continue; -+ } -+ -+ phys = be32_to_cpup(prop++); -+ if ((phys < 0) | (phys >= dev->ports)) { -+ pr_err("%s: physical port index out of range\n", -+ port->name); -+ continue; -+ } -+ -+ dev->portmap[phys].s = kstrdup(segment, GFP_KERNEL); -+ dev->portmap[phys].virt = be32_to_cpup(prop); -+ pr_debug("Found port: %s, physical: %d, virtual: %d\n", -+ segment, phys, dev->portmap[phys].virt); -+ } -+} -+#endif -+ -+int -+register_switch(struct switch_dev *dev, struct net_device *netdev) -+{ -+ struct switch_dev *sdev; -+ const int max_switches = 8 * sizeof(unsigned long); -+ unsigned long in_use = 0; -+ int err; -+ int i; -+ -+ INIT_LIST_HEAD(&dev->dev_list); -+ if (netdev) { -+ dev->netdev = netdev; -+ if (!dev->alias) -+ dev->alias = netdev->name; -+ } -+ BUG_ON(!dev->alias); -+ -+ if (dev->ports > 0) { -+ dev->portbuf = kzalloc(sizeof(struct switch_port) * -+ dev->ports, GFP_KERNEL); -+ if (!dev->portbuf) -+ return -ENOMEM; -+ dev->portmap = kzalloc(sizeof(struct switch_portmap) * -+ dev->ports, GFP_KERNEL); -+ if (!dev->portmap) { -+ kfree(dev->portbuf); -+ return -ENOMEM; -+ } -+ } -+ swconfig_defaults_init(dev); -+ mutex_init(&dev->sw_mutex); -+ swconfig_lock(); -+ dev->id = ++swdev_id; -+ -+ list_for_each_entry(sdev, &swdevs, dev_list) { -+ if (!sscanf(sdev->devname, SWCONFIG_DEVNAME, &i)) -+ continue; -+ if (i < 0 || i > max_switches) -+ continue; -+ -+ set_bit(i, &in_use); -+ } -+ i = find_first_zero_bit(&in_use, max_switches); -+ -+ if (i == max_switches) { -+ swconfig_unlock(); -+ return -ENFILE; -+ } -+ -+#ifdef CONFIG_OF -+ if (dev->ports) -+ of_switch_load_portmap(dev); -+#endif -+ -+ /* fill device name */ -+ snprintf(dev->devname, IFNAMSIZ, SWCONFIG_DEVNAME, i); -+ -+ list_add(&dev->dev_list, &swdevs); -+ swconfig_unlock(); -+ -+ err = swconfig_create_led_trigger(dev); -+ if (err) -+ return err; -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(register_switch); -+ -+void -+unregister_switch(struct switch_dev *dev) -+{ -+ swconfig_destroy_led_trigger(dev); -+ kfree(dev->portbuf); -+ mutex_lock(&dev->sw_mutex); -+ swconfig_lock(); -+ list_del(&dev->dev_list); -+ swconfig_unlock(); -+ mutex_unlock(&dev->sw_mutex); -+} -+EXPORT_SYMBOL_GPL(unregister_switch); -+ -+ -+static int __init -+swconfig_init(void) -+{ -+ int i, err; -+ -+ INIT_LIST_HEAD(&swdevs); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)) -+ err = genl_register_family(&switch_fam); -+ if (err) -+ return err; -+ -+ for (i = 0; i < ARRAY_SIZE(swconfig_ops); i++) { -+ err = genl_register_ops(&switch_fam, &swconfig_ops[i]); -+ if (err) -+ goto unregister; -+ } -+#else -+ err = genl_register_family_with_ops(&switch_fam, swconfig_ops); -+ if (err) -+ return err; -+#endif -+ return 0; -+ -+unregister: -+ genl_unregister_family(&switch_fam); -+ return err; -+} -+ -+static void __exit -+swconfig_exit(void) -+{ -+ genl_unregister_family(&switch_fam); -+} -+ -+module_init(swconfig_init); -+module_exit(swconfig_exit); -+ -diff --git a/drivers/net/phy/swconfig_leds.c b/drivers/net/phy/swconfig_leds.c -new file mode 100644 -index 0000000..abd7bed ---- /dev/null -+++ b/drivers/net/phy/swconfig_leds.c -@@ -0,0 +1,354 @@ -+/* -+ * swconfig_led.c: LED trigger support for the switch configuration API -+ * -+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2 -+ * of the License, or (at your option) any later version. -+ * -+ */ -+ -+#ifdef CONFIG_SWCONFIG_LEDS -+ -+#include <linux/leds.h> -+#include <linux/ctype.h> -+#include <linux/device.h> -+#include <linux/workqueue.h> -+ -+#define SWCONFIG_LED_TIMER_INTERVAL (HZ / 10) -+#define SWCONFIG_LED_NUM_PORTS 32 -+ -+struct switch_led_trigger { -+ struct led_trigger trig; -+ struct switch_dev *swdev; -+ -+ struct delayed_work sw_led_work; -+ u32 port_mask; -+ u32 port_link; -+ unsigned long port_traffic[SWCONFIG_LED_NUM_PORTS]; -+}; -+ -+struct swconfig_trig_data { -+ struct led_classdev *led_cdev; -+ struct switch_dev *swdev; -+ -+ rwlock_t lock; -+ u32 port_mask; -+ -+ bool prev_link; -+ unsigned long prev_traffic; -+ enum led_brightness prev_brightness; -+}; -+ -+static void -+swconfig_trig_set_brightness(struct swconfig_trig_data *trig_data, -+ enum led_brightness brightness) -+{ -+ led_set_brightness(trig_data->led_cdev, brightness); -+ trig_data->prev_brightness = brightness; -+} -+ -+static void -+swconfig_trig_update_port_mask(struct led_trigger *trigger) -+{ -+ struct list_head *entry; -+ struct switch_led_trigger *sw_trig; -+ u32 port_mask; -+ -+ if (!trigger) -+ return; -+ -+ sw_trig = (void *) trigger; -+ -+ port_mask = 0; -+ read_lock(&trigger->leddev_list_lock); -+ list_for_each(entry, &trigger->led_cdevs) { -+ struct led_classdev *led_cdev; -+ struct swconfig_trig_data *trig_data; -+ -+ led_cdev = list_entry(entry, struct led_classdev, trig_list); -+ trig_data = led_cdev->trigger_data; -+ if (trig_data) { -+ read_lock(&trig_data->lock); -+ port_mask |= trig_data->port_mask; -+ read_unlock(&trig_data->lock); -+ } -+ } -+ read_unlock(&trigger->leddev_list_lock); -+ -+ sw_trig->port_mask = port_mask; -+ -+ if (port_mask) -+ schedule_delayed_work(&sw_trig->sw_led_work, -+ SWCONFIG_LED_TIMER_INTERVAL); -+ else -+ cancel_delayed_work_sync(&sw_trig->sw_led_work); -+} -+ -+static ssize_t -+swconfig_trig_port_mask_store(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t size) -+{ -+ struct led_classdev *led_cdev = dev_get_drvdata(dev); -+ struct swconfig_trig_data *trig_data = led_cdev->trigger_data; -+ unsigned long port_mask; -+ ssize_t ret = -EINVAL; -+ char *after; -+ size_t count; -+ -+ port_mask = simple_strtoul(buf, &after, 16); -+ count = after - buf; -+ -+ if (*after && isspace(*after)) -+ count++; -+ -+ if (count == size) { -+ bool changed; -+ -+ write_lock(&trig_data->lock); -+ -+ changed = (trig_data->port_mask != port_mask); -+ if (changed) { -+ trig_data->port_mask = port_mask; -+ if (port_mask == 0) -+ swconfig_trig_set_brightness(trig_data, LED_OFF); -+ } -+ -+ write_unlock(&trig_data->lock); -+ -+ if (changed) -+ swconfig_trig_update_port_mask(led_cdev->trigger); -+ -+ ret = count; -+ } -+ -+ return ret; -+} -+ -+static ssize_t -+swconfig_trig_port_mask_show(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct led_classdev *led_cdev = dev_get_drvdata(dev); -+ struct swconfig_trig_data *trig_data = led_cdev->trigger_data; -+ -+ read_lock(&trig_data->lock); -+ sprintf(buf, "%#x\n", trig_data->port_mask); -+ read_unlock(&trig_data->lock); -+ -+ return strlen(buf) + 1; -+} -+ -+static DEVICE_ATTR(port_mask, 0644, swconfig_trig_port_mask_show, -+ swconfig_trig_port_mask_store); -+ -+static void -+swconfig_trig_activate(struct led_classdev *led_cdev) -+{ -+ struct switch_led_trigger *sw_trig; -+ struct swconfig_trig_data *trig_data; -+ int err; -+ -+ if (led_cdev->trigger->activate != swconfig_trig_activate) -+ return; -+ -+ trig_data = kzalloc(sizeof(struct swconfig_trig_data), GFP_KERNEL); -+ if (!trig_data) -+ return; -+ -+ sw_trig = (void *) led_cdev->trigger; -+ -+ rwlock_init(&trig_data->lock); -+ trig_data->led_cdev = led_cdev; -+ trig_data->swdev = sw_trig->swdev; -+ led_cdev->trigger_data = trig_data; -+ -+ err = device_create_file(led_cdev->dev, &dev_attr_port_mask); -+ if (err) -+ goto err_free; -+ -+ return; -+ -+err_free: -+ led_cdev->trigger_data = NULL; -+ kfree(trig_data); -+} -+ -+static void -+swconfig_trig_deactivate(struct led_classdev *led_cdev) -+{ -+ struct swconfig_trig_data *trig_data; -+ -+ swconfig_trig_update_port_mask(led_cdev->trigger); -+ -+ trig_data = (void *) led_cdev->trigger_data; -+ if (trig_data) { -+ device_remove_file(led_cdev->dev, &dev_attr_port_mask); -+ kfree(trig_data); -+ } -+} -+ -+static void -+swconfig_trig_led_event(struct switch_led_trigger *sw_trig, -+ struct led_classdev *led_cdev) -+{ -+ struct swconfig_trig_data *trig_data; -+ u32 port_mask; -+ bool link; -+ -+ trig_data = led_cdev->trigger_data; -+ if (!trig_data) -+ return; -+ -+ read_lock(&trig_data->lock); -+ port_mask = trig_data->port_mask; -+ read_unlock(&trig_data->lock); -+ -+ link = !!(sw_trig->port_link & port_mask); -+ if (!link) { -+ if (link != trig_data->prev_link) -+ swconfig_trig_set_brightness(trig_data, LED_OFF); -+ } else { -+ unsigned long traffic; -+ int i; -+ -+ traffic = 0; -+ for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) { -+ if (port_mask & (1 << i)) -+ traffic += sw_trig->port_traffic[i]; -+ } -+ -+ if (trig_data->prev_brightness != LED_FULL) -+ swconfig_trig_set_brightness(trig_data, LED_FULL); -+ else if (traffic != trig_data->prev_traffic) -+ swconfig_trig_set_brightness(trig_data, LED_OFF); -+ -+ trig_data->prev_traffic = traffic; -+ } -+ -+ trig_data->prev_link = link; -+} -+ -+static void -+swconfig_trig_update_leds(struct switch_led_trigger *sw_trig) -+{ -+ struct list_head *entry; -+ struct led_trigger *trigger; -+ -+ trigger = &sw_trig->trig; -+ read_lock(&trigger->leddev_list_lock); -+ list_for_each(entry, &trigger->led_cdevs) { -+ struct led_classdev *led_cdev; -+ -+ led_cdev = list_entry(entry, struct led_classdev, trig_list); -+ swconfig_trig_led_event(sw_trig, led_cdev); -+ } -+ read_unlock(&trigger->leddev_list_lock); -+} -+ -+static void -+swconfig_led_work_func(struct work_struct *work) -+{ -+ struct switch_led_trigger *sw_trig; -+ struct switch_dev *swdev; -+ u32 port_mask; -+ u32 link; -+ int i; -+ -+ sw_trig = container_of(work, struct switch_led_trigger, -+ sw_led_work.work); -+ -+ port_mask = sw_trig->port_mask; -+ swdev = sw_trig->swdev; -+ -+ link = 0; -+ for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) { -+ u32 port_bit; -+ -+ port_bit = BIT(i); -+ if ((port_mask & port_bit) == 0) -+ continue; -+ -+ if (swdev->ops->get_port_link) { -+ struct switch_port_link port_link; -+ -+ memset(&port_link, '\0', sizeof(port_link)); -+ swdev->ops->get_port_link(swdev, i, &port_link); -+ -+ if (port_link.link) -+ link |= port_bit; -+ } -+ -+ if (swdev->ops->get_port_stats) { -+ struct switch_port_stats port_stats; -+ -+ memset(&port_stats, '\0', sizeof(port_stats)); -+ swdev->ops->get_port_stats(swdev, i, &port_stats); -+ sw_trig->port_traffic[i] = port_stats.tx_bytes + -+ port_stats.rx_bytes; -+ } -+ } -+ -+ sw_trig->port_link = link; -+ -+ swconfig_trig_update_leds(sw_trig); -+ -+ schedule_delayed_work(&sw_trig->sw_led_work, -+ SWCONFIG_LED_TIMER_INTERVAL); -+} -+ -+static int -+swconfig_create_led_trigger(struct switch_dev *swdev) -+{ -+ struct switch_led_trigger *sw_trig; -+ int err; -+ -+ if (!swdev->ops->get_port_link) -+ return 0; -+ -+ sw_trig = kzalloc(sizeof(struct switch_led_trigger), GFP_KERNEL); -+ if (!sw_trig) -+ return -ENOMEM; -+ -+ sw_trig->swdev = swdev; -+ sw_trig->trig.name = swdev->devname; -+ sw_trig->trig.activate = swconfig_trig_activate; -+ sw_trig->trig.deactivate = swconfig_trig_deactivate; -+ -+ INIT_DELAYED_WORK(&sw_trig->sw_led_work, swconfig_led_work_func); -+ -+ err = led_trigger_register(&sw_trig->trig); -+ if (err) -+ goto err_free; -+ -+ swdev->led_trigger = sw_trig; -+ -+ return 0; -+ -+err_free: -+ kfree(sw_trig); -+ return err; -+} -+ -+static void -+swconfig_destroy_led_trigger(struct switch_dev *swdev) -+{ -+ struct switch_led_trigger *sw_trig; -+ -+ sw_trig = swdev->led_trigger; -+ if (sw_trig) { -+ cancel_delayed_work_sync(&sw_trig->sw_led_work); -+ led_trigger_unregister(&sw_trig->trig); -+ kfree(sw_trig); -+ } -+} -+ -+#else /* SWCONFIG_LEDS */ -+static inline int -+swconfig_create_led_trigger(struct switch_dev *swdev) { return 0; } -+ -+static inline void -+swconfig_destroy_led_trigger(struct switch_dev *swdev) { } -+#endif /* CONFIG_SWCONFIG_LEDS */ -diff --git a/include/linux/switch.h b/include/linux/switch.h -new file mode 100644 -index 0000000..b53431e ---- /dev/null -+++ b/include/linux/switch.h -@@ -0,0 +1,167 @@ -+/* -+ * switch.h: Switch configuration API -+ * -+ * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2 -+ * of the License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+#ifndef _LINUX_SWITCH_H -+#define _LINUX_SWITCH_H -+ -+#include <net/genetlink.h> -+#include <uapi/linux/switch.h> -+ -+struct switch_dev; -+struct switch_op; -+struct switch_val; -+struct switch_attr; -+struct switch_attrlist; -+struct switch_led_trigger; -+ -+int register_switch(struct switch_dev *dev, struct net_device *netdev); -+void unregister_switch(struct switch_dev *dev); -+ -+/** -+ * struct switch_attrlist - attribute list -+ * -+ * @n_attr: number of attributes -+ * @attr: pointer to the attributes array -+ */ -+struct switch_attrlist { -+ int n_attr; -+ const struct switch_attr *attr; -+}; -+ -+enum switch_port_speed { -+ SWITCH_PORT_SPEED_UNKNOWN = 0, -+ SWITCH_PORT_SPEED_10 = 10, -+ SWITCH_PORT_SPEED_100 = 100, -+ SWITCH_PORT_SPEED_1000 = 1000, -+}; -+ -+struct switch_port_link { -+ bool link; -+ bool duplex; -+ bool aneg; -+ bool tx_flow; -+ bool rx_flow; -+ enum switch_port_speed speed; -+}; -+ -+struct switch_port_stats { -+ unsigned long tx_bytes; -+ unsigned long rx_bytes; -+}; -+ -+/** -+ * struct switch_dev_ops - switch driver operations -+ * -+ * @attr_global: global switch attribute list -+ * @attr_port: port attribute list -+ * @attr_vlan: vlan attribute list -+ * -+ * Callbacks: -+ * -+ * @get_vlan_ports: read the port list of a VLAN -+ * @set_vlan_ports: set the port list of a VLAN -+ * -+ * @get_port_pvid: get the primary VLAN ID of a port -+ * @set_port_pvid: set the primary VLAN ID of a port -+ * -+ * @apply_config: apply all changed settings to the switch -+ * @reset_switch: resetting the switch -+ */ -+struct switch_dev_ops { -+ struct switch_attrlist attr_global, attr_port, attr_vlan; -+ -+ int (*get_vlan_ports)(struct switch_dev *dev, struct switch_val *val); -+ int (*set_vlan_ports)(struct switch_dev *dev, struct switch_val *val); -+ -+ int (*get_port_pvid)(struct switch_dev *dev, int port, int *val); -+ int (*set_port_pvid)(struct switch_dev *dev, int port, int val); -+ -+ int (*apply_config)(struct switch_dev *dev); -+ int (*reset_switch)(struct switch_dev *dev); -+ -+ int (*get_port_link)(struct switch_dev *dev, int port, -+ struct switch_port_link *link); -+ int (*get_port_stats)(struct switch_dev *dev, int port, -+ struct switch_port_stats *stats); -+}; -+ -+struct switch_dev { -+ struct device_node *of_node; -+ const struct switch_dev_ops *ops; -+ /* will be automatically filled */ -+ char devname[IFNAMSIZ]; -+ -+ const char *name; -+ /* NB: either alias or netdev must be set */ -+ const char *alias; -+ struct net_device *netdev; -+ -+ int ports; -+ int vlans; -+ int cpu_port; -+ -+ /* the following fields are internal for swconfig */ -+ int id; -+ struct list_head dev_list; -+ unsigned long def_global, def_port, def_vlan; -+ -+ struct mutex sw_mutex; -+ struct switch_port *portbuf; -+ struct switch_portmap *portmap; -+ -+ char buf[128]; -+ -+#ifdef CONFIG_SWCONFIG_LEDS -+ struct switch_led_trigger *led_trigger; -+#endif -+}; -+ -+struct switch_port { -+ u32 id; -+ u32 flags; -+}; -+ -+struct switch_portmap { -+ u32 virt; -+ const char *s; -+}; -+ -+struct switch_val { -+ const struct switch_attr *attr; -+ int port_vlan; -+ int len; -+ union { -+ const char *s; -+ u32 i; -+ struct switch_port *ports; -+ } value; -+}; -+ -+struct switch_attr { -+ int disabled; -+ int type; -+ const char *name; -+ const char *description; -+ -+ int (*set)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val); -+ int (*get)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val); -+ -+ /* for driver internal use */ -+ int id; -+ int ofs; -+ int max; -+}; -+ -+#endif /* _LINUX_SWITCH_H */ -diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild -index 3ce25b5..b9565df 100644 ---- a/include/uapi/linux/Kbuild -+++ b/include/uapi/linux/Kbuild -@@ -365,6 +365,7 @@ header-y += stddef.h - header-y += string.h - header-y += suspend_ioctls.h - header-y += swab.h -+header-y += switch.h - header-y += synclink.h - header-y += sysctl.h - header-y += sysinfo.h -diff --git a/include/uapi/linux/switch.h b/include/uapi/linux/switch.h -new file mode 100644 -index 0000000..a59b239 ---- /dev/null -+++ b/include/uapi/linux/switch.h -@@ -0,0 +1,103 @@ -+/* -+ * switch.h: Switch configuration API -+ * -+ * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2 -+ * of the License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef _UAPI_LINUX_SWITCH_H -+#define _UAPI_LINUX_SWITCH_H -+ -+#include <linux/types.h> -+#include <linux/netdevice.h> -+#include <linux/netlink.h> -+#include <linux/genetlink.h> -+#ifndef __KERNEL__ -+#include <netlink/netlink.h> -+#include <netlink/genl/genl.h> -+#include <netlink/genl/ctrl.h> -+#endif -+ -+/* main attributes */ -+enum { -+ SWITCH_ATTR_UNSPEC, -+ /* global */ -+ SWITCH_ATTR_TYPE, -+ /* device */ -+ SWITCH_ATTR_ID, -+ SWITCH_ATTR_DEV_NAME, -+ SWITCH_ATTR_ALIAS, -+ SWITCH_ATTR_NAME, -+ SWITCH_ATTR_VLANS, -+ SWITCH_ATTR_PORTS, -+ SWITCH_ATTR_PORTMAP, -+ SWITCH_ATTR_CPU_PORT, -+ /* attributes */ -+ SWITCH_ATTR_OP_ID, -+ SWITCH_ATTR_OP_TYPE, -+ SWITCH_ATTR_OP_NAME, -+ SWITCH_ATTR_OP_PORT, -+ SWITCH_ATTR_OP_VLAN, -+ SWITCH_ATTR_OP_VALUE_INT, -+ SWITCH_ATTR_OP_VALUE_STR, -+ SWITCH_ATTR_OP_VALUE_PORTS, -+ SWITCH_ATTR_OP_DESCRIPTION, -+ /* port lists */ -+ SWITCH_ATTR_PORT, -+ SWITCH_ATTR_MAX -+}; -+ -+enum { -+ /* port map */ -+ SWITCH_PORTMAP_PORTS, -+ SWITCH_PORTMAP_SEGMENT, -+ SWITCH_PORTMAP_VIRT, -+ SWITCH_PORTMAP_MAX -+}; -+ -+/* commands */ -+enum { -+ SWITCH_CMD_UNSPEC, -+ SWITCH_CMD_GET_SWITCH, -+ SWITCH_CMD_NEW_ATTR, -+ SWITCH_CMD_LIST_GLOBAL, -+ SWITCH_CMD_GET_GLOBAL, -+ SWITCH_CMD_SET_GLOBAL, -+ SWITCH_CMD_LIST_PORT, -+ SWITCH_CMD_GET_PORT, -+ SWITCH_CMD_SET_PORT, -+ SWITCH_CMD_LIST_VLAN, -+ SWITCH_CMD_GET_VLAN, -+ SWITCH_CMD_SET_VLAN -+}; -+ -+/* data types */ -+enum switch_val_type { -+ SWITCH_TYPE_UNSPEC, -+ SWITCH_TYPE_INT, -+ SWITCH_TYPE_STRING, -+ SWITCH_TYPE_PORTS, -+ SWITCH_TYPE_NOVAL, -+}; -+ -+/* port nested attributes */ -+enum { -+ SWITCH_PORT_UNSPEC, -+ SWITCH_PORT_ID, -+ SWITCH_PORT_FLAG_TAGGED, -+ SWITCH_PORT_ATTR_MAX -+}; -+ -+#define SWITCH_ATTR_DEFAULTS_OFFSET 0x1000 -+ -+ -+#endif /* _UAPI_LINUX_SWITCH_H */ --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0014-phy-add-detach-callback-to-struct-phy_driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0014-phy-add-detach-callback-to-struct-phy_driver.patch deleted file mode 100644 index 43ca7ce9b..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0014-phy-add-detach-callback-to-struct-phy_driver.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 76e9965a69ff97c3ca973ca54f145a96023bdeca Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Wed, 14 May 2014 03:24:00 +0200 -Subject: [PATCH] phy: add detach callback to struct phy_driver - -This is used by ar8216 driver. ---- - drivers/net/phy/phy_device.c | 4 ++++ - include/linux/phy.h | 6 ++++++ - 2 files changed, 10 insertions(+) - -diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c -index 3653754..b35ece2 100644 ---- a/drivers/net/phy/phy_device.c -+++ b/drivers/net/phy/phy_device.c -@@ -662,6 +662,10 @@ EXPORT_SYMBOL(phy_attach); - void phy_detach(struct phy_device *phydev) - { - int i; -+ -+ if (phydev->drv && phydev->drv->detach) -+ phydev->drv->detach(phydev); -+ - phydev->attached_dev->phydev = NULL; - phydev->attached_dev = NULL; - phy_suspend(phydev); -diff --git a/include/linux/phy.h b/include/linux/phy.h -index 9ab0d79..f1441b4 100644 ---- a/include/linux/phy.h -+++ b/include/linux/phy.h -@@ -432,6 +432,12 @@ struct phy_driver { - */ - int (*did_interrupt)(struct phy_device *phydev); - -+ /* -+ * Called before an ethernet device is detached -+ * from the PHY. -+ */ -+ void (*detach)(struct phy_device *phydev); -+ - /* Clears up any memory if needed */ - void (*remove)(struct phy_device *phydev); - --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0015-phy-add-ar8216-PHY-support.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0015-phy-add-ar8216-PHY-support.patch deleted file mode 100644 index 46b2ba467..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0015-phy-add-ar8216-PHY-support.patch +++ /dev/null @@ -1,3671 +0,0 @@ -From 6137bedf972f576765c6e5d4373a488951371609 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 00:37:30 +0200 -Subject: [PATCH] phy: add ar8216 PHY support - ---- - drivers/net/phy/Kconfig | 9 + - drivers/net/phy/Makefile | 1 + - drivers/net/phy/ar8216.c | 2978 +++++++++++++++++++++++++++++++++++++++ - drivers/net/phy/ar8216.h | 492 +++++++ - include/linux/ar8216_platform.h | 131 ++ - 5 files changed, 3611 insertions(+) - create mode 100644 drivers/net/phy/ar8216.c - create mode 100644 drivers/net/phy/ar8216.h - create mode 100644 include/linux/ar8216_platform.h - -diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig -index 36a13fc..0414889 100644 ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -116,6 +116,15 @@ config MICREL_PHY - ---help--- - Supports the KSZ9021, VSC8201, KS8001 PHYs. - -+config AR8216_PHY -+ tristate "Driver for Atheros AR8216 switches" -+ select ETHERNET_PACKET_MANGLE -+ select SWCONFIG -+ -+config AR8216_PHY_LEDS -+ bool "Atheros AR8216 switch LED support" -+ depends on (AR8216_PHY && LEDS_CLASS) -+ - config FIXED_PHY - bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" - depends on PHYLIB=y -diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile -index b510bd6..3c76ff8 100644 ---- a/drivers/net/phy/Makefile -+++ b/drivers/net/phy/Makefile -@@ -16,6 +16,7 @@ obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o - obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o - obj-$(CONFIG_ICPLUS_PHY) += icplus.o - obj-$(CONFIG_REALTEK_PHY) += realtek.o -+obj-$(CONFIG_AR8216_PHY) += ar8216.o - obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o - obj-$(CONFIG_FIXED_PHY) += fixed.o - obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o -diff --git a/drivers/net/phy/ar8216.c b/drivers/net/phy/ar8216.c -new file mode 100644 -index 0000000..3f60878 ---- /dev/null -+++ b/drivers/net/phy/ar8216.c -@@ -0,0 +1,2978 @@ -+/* -+ * ar8216.c: AR8216 switch driver -+ * -+ * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> -+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2 -+ * of the License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include <linux/if.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/list.h> -+#include <linux/if_ether.h> -+#include <linux/skbuff.h> -+#include <linux/netdevice.h> -+#include <linux/netlink.h> -+#include <linux/bitops.h> -+#include <net/genetlink.h> -+#include <linux/switch.h> -+#include <linux/delay.h> -+#include <linux/phy.h> -+#include <linux/netdevice.h> -+#include <linux/etherdevice.h> -+#include <linux/lockdep.h> -+#include <linux/ar8216_platform.h> -+#include <linux/workqueue.h> -+#include <linux/of_device.h> -+#include <linux/leds.h> -+#include <linux/gpio.h> -+ -+#include "ar8216.h" -+ -+/* size of the vlan table */ -+#define AR8X16_MAX_VLANS 128 -+#define AR8X16_PROBE_RETRIES 10 -+#define AR8X16_MAX_PORTS 8 -+ -+#define AR8XXX_MIB_WORK_DELAY 2000 /* msecs */ -+ -+struct ar8xxx_priv; -+ -+#define AR8XXX_CAP_GIGE BIT(0) -+#define AR8XXX_CAP_MIB_COUNTERS BIT(1) -+ -+enum { -+ AR8XXX_VER_AR8216 = 0x01, -+ AR8XXX_VER_AR8236 = 0x03, -+ AR8XXX_VER_AR8316 = 0x10, -+ AR8XXX_VER_AR8327 = 0x12, -+ AR8XXX_VER_AR8337 = 0x13, -+}; -+ -+struct ar8xxx_mib_desc { -+ unsigned int size; -+ unsigned int offset; -+ const char *name; -+}; -+ -+struct ar8xxx_chip { -+ unsigned long caps; -+ -+ int (*hw_init)(struct ar8xxx_priv *priv); -+ void (*cleanup)(struct ar8xxx_priv *priv); -+ -+ void (*init_globals)(struct ar8xxx_priv *priv); -+ void (*init_port)(struct ar8xxx_priv *priv, int port); -+ void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 egress, -+ u32 ingress, u32 members, u32 pvid); -+ u32 (*read_port_status)(struct ar8xxx_priv *priv, int port); -+ int (*atu_flush)(struct ar8xxx_priv *priv); -+ void (*vtu_flush)(struct ar8xxx_priv *priv); -+ void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask); -+ -+ const struct ar8xxx_mib_desc *mib_decs; -+ unsigned num_mibs; -+}; -+ -+enum ar8327_led_pattern { -+ AR8327_LED_PATTERN_OFF = 0, -+ AR8327_LED_PATTERN_BLINK, -+ AR8327_LED_PATTERN_ON, -+ AR8327_LED_PATTERN_RULE, -+}; -+ -+struct ar8327_led_entry { -+ unsigned reg; -+ unsigned shift; -+}; -+ -+struct ar8327_led { -+ struct led_classdev cdev; -+ struct ar8xxx_priv *sw_priv; -+ -+ char *name; -+ bool active_low; -+ u8 led_num; -+ enum ar8327_led_mode mode; -+ -+ struct mutex mutex; -+ spinlock_t lock; -+ struct work_struct led_work; -+ bool enable_hw_mode; -+ enum ar8327_led_pattern pattern; -+}; -+ -+struct ar8327_data { -+ u32 port0_status; -+ u32 port6_status; -+ -+ struct ar8327_led **leds; -+ unsigned int num_leds; -+}; -+ -+struct ar8xxx_priv { -+ struct switch_dev dev; -+ struct mii_bus *mii_bus; -+ struct phy_device *phy; -+ -+ u32 (*read)(struct ar8xxx_priv *priv, int reg); -+ void (*write)(struct ar8xxx_priv *priv, int reg, u32 val); -+ u32 (*rmw)(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val); -+ -+ int (*get_port_link)(unsigned port); -+ -+ const struct net_device_ops *ndo_old; -+ struct net_device_ops ndo; -+ struct mutex reg_mutex; -+ u8 chip_ver; -+ u8 chip_rev; -+ const struct ar8xxx_chip *chip; -+ union { -+ struct ar8327_data ar8327; -+ } chip_data; -+ bool initialized; -+ bool port4_phy; -+ char buf[2048]; -+ -+ bool init; -+ bool mii_lo_first; -+ -+ struct mutex mib_lock; -+ struct delayed_work mib_work; -+ int mib_next_port; -+ u64 *mib_stats; -+ -+ struct list_head list; -+ unsigned int use_count; -+ -+ /* all fields below are cleared on reset */ -+ bool vlan; -+ u16 vlan_id[AR8X16_MAX_VLANS]; -+ u8 vlan_table[AR8X16_MAX_VLANS]; -+ u8 vlan_tagged; -+ u16 pvid[AR8X16_MAX_PORTS]; -+ -+ /* mirroring */ -+ bool mirror_rx; -+ bool mirror_tx; -+ int source_port; -+ int monitor_port; -+}; -+ -+#define MIB_DESC(_s , _o, _n) \ -+ { \ -+ .size = (_s), \ -+ .offset = (_o), \ -+ .name = (_n), \ -+ } -+ -+static const struct ar8xxx_mib_desc ar8216_mibs[] = { -+ MIB_DESC(1, AR8216_STATS_RXBROAD, "RxBroad"), -+ MIB_DESC(1, AR8216_STATS_RXPAUSE, "RxPause"), -+ MIB_DESC(1, AR8216_STATS_RXMULTI, "RxMulti"), -+ MIB_DESC(1, AR8216_STATS_RXFCSERR, "RxFcsErr"), -+ MIB_DESC(1, AR8216_STATS_RXALIGNERR, "RxAlignErr"), -+ MIB_DESC(1, AR8216_STATS_RXRUNT, "RxRunt"), -+ MIB_DESC(1, AR8216_STATS_RXFRAGMENT, "RxFragment"), -+ MIB_DESC(1, AR8216_STATS_RX64BYTE, "Rx64Byte"), -+ MIB_DESC(1, AR8216_STATS_RX128BYTE, "Rx128Byte"), -+ MIB_DESC(1, AR8216_STATS_RX256BYTE, "Rx256Byte"), -+ MIB_DESC(1, AR8216_STATS_RX512BYTE, "Rx512Byte"), -+ MIB_DESC(1, AR8216_STATS_RX1024BYTE, "Rx1024Byte"), -+ MIB_DESC(1, AR8216_STATS_RXMAXBYTE, "RxMaxByte"), -+ MIB_DESC(1, AR8216_STATS_RXTOOLONG, "RxTooLong"), -+ MIB_DESC(2, AR8216_STATS_RXGOODBYTE, "RxGoodByte"), -+ MIB_DESC(2, AR8216_STATS_RXBADBYTE, "RxBadByte"), -+ MIB_DESC(1, AR8216_STATS_RXOVERFLOW, "RxOverFlow"), -+ MIB_DESC(1, AR8216_STATS_FILTERED, "Filtered"), -+ MIB_DESC(1, AR8216_STATS_TXBROAD, "TxBroad"), -+ MIB_DESC(1, AR8216_STATS_TXPAUSE, "TxPause"), -+ MIB_DESC(1, AR8216_STATS_TXMULTI, "TxMulti"), -+ MIB_DESC(1, AR8216_STATS_TXUNDERRUN, "TxUnderRun"), -+ MIB_DESC(1, AR8216_STATS_TX64BYTE, "Tx64Byte"), -+ MIB_DESC(1, AR8216_STATS_TX128BYTE, "Tx128Byte"), -+ MIB_DESC(1, AR8216_STATS_TX256BYTE, "Tx256Byte"), -+ MIB_DESC(1, AR8216_STATS_TX512BYTE, "Tx512Byte"), -+ MIB_DESC(1, AR8216_STATS_TX1024BYTE, "Tx1024Byte"), -+ MIB_DESC(1, AR8216_STATS_TXMAXBYTE, "TxMaxByte"), -+ MIB_DESC(1, AR8216_STATS_TXOVERSIZE, "TxOverSize"), -+ MIB_DESC(2, AR8216_STATS_TXBYTE, "TxByte"), -+ MIB_DESC(1, AR8216_STATS_TXCOLLISION, "TxCollision"), -+ MIB_DESC(1, AR8216_STATS_TXABORTCOL, "TxAbortCol"), -+ MIB_DESC(1, AR8216_STATS_TXMULTICOL, "TxMultiCol"), -+ MIB_DESC(1, AR8216_STATS_TXSINGLECOL, "TxSingleCol"), -+ MIB_DESC(1, AR8216_STATS_TXEXCDEFER, "TxExcDefer"), -+ MIB_DESC(1, AR8216_STATS_TXDEFER, "TxDefer"), -+ MIB_DESC(1, AR8216_STATS_TXLATECOL, "TxLateCol"), -+}; -+ -+static const struct ar8xxx_mib_desc ar8236_mibs[] = { -+ MIB_DESC(1, AR8236_STATS_RXBROAD, "RxBroad"), -+ MIB_DESC(1, AR8236_STATS_RXPAUSE, "RxPause"), -+ MIB_DESC(1, AR8236_STATS_RXMULTI, "RxMulti"), -+ MIB_DESC(1, AR8236_STATS_RXFCSERR, "RxFcsErr"), -+ MIB_DESC(1, AR8236_STATS_RXALIGNERR, "RxAlignErr"), -+ MIB_DESC(1, AR8236_STATS_RXRUNT, "RxRunt"), -+ MIB_DESC(1, AR8236_STATS_RXFRAGMENT, "RxFragment"), -+ MIB_DESC(1, AR8236_STATS_RX64BYTE, "Rx64Byte"), -+ MIB_DESC(1, AR8236_STATS_RX128BYTE, "Rx128Byte"), -+ MIB_DESC(1, AR8236_STATS_RX256BYTE, "Rx256Byte"), -+ MIB_DESC(1, AR8236_STATS_RX512BYTE, "Rx512Byte"), -+ MIB_DESC(1, AR8236_STATS_RX1024BYTE, "Rx1024Byte"), -+ MIB_DESC(1, AR8236_STATS_RX1518BYTE, "Rx1518Byte"), -+ MIB_DESC(1, AR8236_STATS_RXMAXBYTE, "RxMaxByte"), -+ MIB_DESC(1, AR8236_STATS_RXTOOLONG, "RxTooLong"), -+ MIB_DESC(2, AR8236_STATS_RXGOODBYTE, "RxGoodByte"), -+ MIB_DESC(2, AR8236_STATS_RXBADBYTE, "RxBadByte"), -+ MIB_DESC(1, AR8236_STATS_RXOVERFLOW, "RxOverFlow"), -+ MIB_DESC(1, AR8236_STATS_FILTERED, "Filtered"), -+ MIB_DESC(1, AR8236_STATS_TXBROAD, "TxBroad"), -+ MIB_DESC(1, AR8236_STATS_TXPAUSE, "TxPause"), -+ MIB_DESC(1, AR8236_STATS_TXMULTI, "TxMulti"), -+ MIB_DESC(1, AR8236_STATS_TXUNDERRUN, "TxUnderRun"), -+ MIB_DESC(1, AR8236_STATS_TX64BYTE, "Tx64Byte"), -+ MIB_DESC(1, AR8236_STATS_TX128BYTE, "Tx128Byte"), -+ MIB_DESC(1, AR8236_STATS_TX256BYTE, "Tx256Byte"), -+ MIB_DESC(1, AR8236_STATS_TX512BYTE, "Tx512Byte"), -+ MIB_DESC(1, AR8236_STATS_TX1024BYTE, "Tx1024Byte"), -+ MIB_DESC(1, AR8236_STATS_TX1518BYTE, "Tx1518Byte"), -+ MIB_DESC(1, AR8236_STATS_TXMAXBYTE, "TxMaxByte"), -+ MIB_DESC(1, AR8236_STATS_TXOVERSIZE, "TxOverSize"), -+ MIB_DESC(2, AR8236_STATS_TXBYTE, "TxByte"), -+ MIB_DESC(1, AR8236_STATS_TXCOLLISION, "TxCollision"), -+ MIB_DESC(1, AR8236_STATS_TXABORTCOL, "TxAbortCol"), -+ MIB_DESC(1, AR8236_STATS_TXMULTICOL, "TxMultiCol"), -+ MIB_DESC(1, AR8236_STATS_TXSINGLECOL, "TxSingleCol"), -+ MIB_DESC(1, AR8236_STATS_TXEXCDEFER, "TxExcDefer"), -+ MIB_DESC(1, AR8236_STATS_TXDEFER, "TxDefer"), -+ MIB_DESC(1, AR8236_STATS_TXLATECOL, "TxLateCol"), -+}; -+ -+static DEFINE_MUTEX(ar8xxx_dev_list_lock); -+static LIST_HEAD(ar8xxx_dev_list); -+ -+static inline struct ar8xxx_priv * -+swdev_to_ar8xxx(struct switch_dev *swdev) -+{ -+ return container_of(swdev, struct ar8xxx_priv, dev); -+} -+ -+static inline bool ar8xxx_has_gige(struct ar8xxx_priv *priv) -+{ -+ return priv->chip->caps & AR8XXX_CAP_GIGE; -+} -+ -+static inline bool ar8xxx_has_mib_counters(struct ar8xxx_priv *priv) -+{ -+ return priv->chip->caps & AR8XXX_CAP_MIB_COUNTERS; -+} -+ -+static inline bool chip_is_ar8216(struct ar8xxx_priv *priv) -+{ -+ return priv->chip_ver == AR8XXX_VER_AR8216; -+} -+ -+static inline bool chip_is_ar8236(struct ar8xxx_priv *priv) -+{ -+ return priv->chip_ver == AR8XXX_VER_AR8236; -+} -+ -+static inline bool chip_is_ar8316(struct ar8xxx_priv *priv) -+{ -+ return priv->chip_ver == AR8XXX_VER_AR8316; -+} -+ -+static inline bool chip_is_ar8327(struct ar8xxx_priv *priv) -+{ -+ return priv->chip_ver == AR8XXX_VER_AR8327; -+} -+ -+static inline bool chip_is_ar8337(struct ar8xxx_priv *priv) -+{ -+ return priv->chip_ver == AR8XXX_VER_AR8337; -+} -+ -+static inline void -+split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) -+{ -+ regaddr >>= 1; -+ *r1 = regaddr & 0x1e; -+ -+ regaddr >>= 5; -+ *r2 = regaddr & 0x7; -+ -+ regaddr >>= 3; -+ *page = regaddr & 0x1ff; -+} -+ -+static u32 -+ar8xxx_mii_read(struct ar8xxx_priv *priv, int reg) -+{ -+ struct mii_bus *bus = priv->mii_bus; -+ u16 r1, r2, page; -+ u16 lo, hi; -+ -+ split_addr((u32) reg, &r1, &r2, &page); -+ -+ mutex_lock(&bus->mdio_lock); -+ -+ bus->write(bus, 0x18, 0, page); -+ usleep_range(1000, 2000); /* wait for the page switch to propagate */ -+ lo = bus->read(bus, 0x10 | r2, r1); -+ hi = bus->read(bus, 0x10 | r2, r1 + 1); -+ -+ mutex_unlock(&bus->mdio_lock); -+ -+ return (hi << 16) | lo; -+} -+ -+static void -+ar8xxx_mii_write(struct ar8xxx_priv *priv, int reg, u32 val) -+{ -+ struct mii_bus *bus = priv->mii_bus; -+ u16 r1, r2, r3; -+ u16 lo, hi; -+ -+ split_addr((u32) reg, &r1, &r2, &r3); -+ lo = val & 0xffff; -+ hi = (u16) (val >> 16); -+ -+ mutex_lock(&bus->mdio_lock); -+ -+ bus->write(bus, 0x18, 0, r3); -+ usleep_range(1000, 2000); /* wait for the page switch to propagate */ -+ if (priv->mii_lo_first) { -+ bus->write(bus, 0x10 | r2, r1, lo); -+ bus->write(bus, 0x10 | r2, r1 + 1, hi); -+ } else { -+ bus->write(bus, 0x10 | r2, r1 + 1, hi); -+ bus->write(bus, 0x10 | r2, r1, lo); -+ } -+ -+ mutex_unlock(&bus->mdio_lock); -+} -+ -+static u32 -+ar8xxx_mii_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) -+{ -+ struct mii_bus *bus = priv->mii_bus; -+ u16 r1, r2, page; -+ u16 lo, hi; -+ u32 ret; -+ -+ split_addr((u32) reg, &r1, &r2, &page); -+ -+ mutex_lock(&bus->mdio_lock); -+ -+ bus->write(bus, 0x18, 0, page); -+ usleep_range(1000, 2000); /* wait for the page switch to propagate */ -+ -+ lo = bus->read(bus, 0x10 | r2, r1); -+ hi = bus->read(bus, 0x10 | r2, r1 + 1); -+ -+ ret = hi << 16 | lo; -+ ret &= ~mask; -+ ret |= val; -+ -+ lo = ret & 0xffff; -+ hi = (u16) (ret >> 16); -+ -+ if (priv->mii_lo_first) { -+ bus->write(bus, 0x10 | r2, r1, lo); -+ bus->write(bus, 0x10 | r2, r1 + 1, hi); -+ } else { -+ bus->write(bus, 0x10 | r2, r1 + 1, hi); -+ bus->write(bus, 0x10 | r2, r1, lo); -+ } -+ -+ mutex_unlock(&bus->mdio_lock); -+ -+ return ret; -+} -+ -+ -+static void -+ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr, -+ u16 dbg_addr, u16 dbg_data) -+{ -+ struct mii_bus *bus = priv->mii_bus; -+ -+ mutex_lock(&bus->mdio_lock); -+ bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr); -+ bus->write(bus, phy_addr, MII_ATH_DBG_DATA, dbg_data); -+ mutex_unlock(&bus->mdio_lock); -+} -+ -+static void -+ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 data) -+{ -+ struct mii_bus *bus = priv->mii_bus; -+ -+ mutex_lock(&bus->mdio_lock); -+ bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr); -+ bus->write(bus, phy_addr, MII_ATH_MMD_DATA, data); -+ mutex_unlock(&bus->mdio_lock); -+} -+ -+static inline u32 -+ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) -+{ -+ return priv->rmw(priv, reg, mask, val); -+} -+ -+static inline void -+ar8xxx_reg_set(struct ar8xxx_priv *priv, int reg, u32 val) -+{ -+ priv->rmw(priv, reg, 0, val); -+} -+ -+static int -+ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val, -+ unsigned timeout) -+{ -+ int i; -+ -+ for (i = 0; i < timeout; i++) { -+ u32 t; -+ -+ t = priv->read(priv, reg); -+ if ((t & mask) == val) -+ return 0; -+ -+ usleep_range(1000, 2000); -+ } -+ -+ return -ETIMEDOUT; -+} -+ -+static int -+ar8xxx_mib_op(struct ar8xxx_priv *priv, u32 op) -+{ -+ unsigned mib_func; -+ int ret; -+ -+ lockdep_assert_held(&priv->mib_lock); -+ -+ if (chip_is_ar8327(priv) || chip_is_ar8337(priv)) -+ mib_func = AR8327_REG_MIB_FUNC; -+ else -+ mib_func = AR8216_REG_MIB_FUNC; -+ -+ /* Capture the hardware statistics for all ports */ -+ ar8xxx_rmw(priv, mib_func, AR8216_MIB_FUNC, (op << AR8216_MIB_FUNC_S)); -+ -+ /* Wait for the capturing to complete. */ -+ ret = ar8xxx_reg_wait(priv, mib_func, AR8216_MIB_BUSY, 0, 10); -+ if (ret) -+ goto out; -+ -+ ret = 0; -+ -+out: -+ return ret; -+} -+ -+static int -+ar8xxx_mib_capture(struct ar8xxx_priv *priv) -+{ -+ return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_CAPTURE); -+} -+ -+static int -+ar8xxx_mib_flush(struct ar8xxx_priv *priv) -+{ -+ return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_FLUSH); -+} -+ -+static void -+ar8xxx_mib_fetch_port_stat(struct ar8xxx_priv *priv, int port, bool flush) -+{ -+ unsigned int base; -+ u64 *mib_stats; -+ int i; -+ -+ WARN_ON(port >= priv->dev.ports); -+ -+ lockdep_assert_held(&priv->mib_lock); -+ -+ if (chip_is_ar8327(priv) || chip_is_ar8337(priv)) -+ base = AR8327_REG_PORT_STATS_BASE(port); -+ else if (chip_is_ar8236(priv) || -+ chip_is_ar8316(priv)) -+ base = AR8236_REG_PORT_STATS_BASE(port); -+ else -+ base = AR8216_REG_PORT_STATS_BASE(port); -+ -+ mib_stats = &priv->mib_stats[port * priv->chip->num_mibs]; -+ for (i = 0; i < priv->chip->num_mibs; i++) { -+ const struct ar8xxx_mib_desc *mib; -+ u64 t; -+ -+ mib = &priv->chip->mib_decs[i]; -+ t = priv->read(priv, base + mib->offset); -+ if (mib->size == 2) { -+ u64 hi; -+ -+ hi = priv->read(priv, base + mib->offset + 4); -+ t |= hi << 32; -+ } -+ -+ if (flush) -+ mib_stats[i] = 0; -+ else -+ mib_stats[i] += t; -+ } -+} -+ -+static void -+ar8216_read_port_link(struct ar8xxx_priv *priv, int port, -+ struct switch_port_link *link) -+{ -+ u32 status; -+ u32 speed; -+ -+ memset(link, '\0', sizeof(*link)); -+ -+ status = priv->chip->read_port_status(priv, port); -+ -+ link->aneg = !!(status & AR8216_PORT_STATUS_LINK_AUTO); -+ if (link->aneg) { -+ link->link = !!(status & AR8216_PORT_STATUS_LINK_UP); -+ } else { -+ link->link = true; -+ -+ if (priv->get_port_link) { -+ int err; -+ -+ err = priv->get_port_link(port); -+ if (err >= 0) -+ link->link = !!err; -+ } -+ } -+ -+ if (!link->link) -+ return; -+ -+ link->duplex = !!(status & AR8216_PORT_STATUS_DUPLEX); -+ link->tx_flow = !!(status & AR8216_PORT_STATUS_TXFLOW); -+ link->rx_flow = !!(status & AR8216_PORT_STATUS_RXFLOW); -+ -+ speed = (status & AR8216_PORT_STATUS_SPEED) >> -+ AR8216_PORT_STATUS_SPEED_S; -+ -+ switch (speed) { -+ case AR8216_PORT_SPEED_10M: -+ link->speed = SWITCH_PORT_SPEED_10; -+ break; -+ case AR8216_PORT_SPEED_100M: -+ link->speed = SWITCH_PORT_SPEED_100; -+ break; -+ case AR8216_PORT_SPEED_1000M: -+ link->speed = SWITCH_PORT_SPEED_1000; -+ break; -+ default: -+ link->speed = SWITCH_PORT_SPEED_UNKNOWN; -+ break; -+ } -+} -+ -+static struct sk_buff * -+ar8216_mangle_tx(struct net_device *dev, struct sk_buff *skb) -+{ -+ struct ar8xxx_priv *priv = dev->phy_ptr; -+ unsigned char *buf; -+ -+ if (unlikely(!priv)) -+ goto error; -+ -+ if (!priv->vlan) -+ goto send; -+ -+ if (unlikely(skb_headroom(skb) < 2)) { -+ if (pskb_expand_head(skb, 2, 0, GFP_ATOMIC) < 0) -+ goto error; -+ } -+ -+ buf = skb_push(skb, 2); -+ buf[0] = 0x10; -+ buf[1] = 0x80; -+ -+send: -+ return skb; -+ -+error: -+ dev_kfree_skb_any(skb); -+ return NULL; -+} -+ -+static void -+ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb) -+{ -+ struct ar8xxx_priv *priv; -+ unsigned char *buf; -+ int port, vlan; -+ -+ priv = dev->phy_ptr; -+ if (!priv) -+ return; -+ -+ /* don't strip the header if vlan mode is disabled */ -+ if (!priv->vlan) -+ return; -+ -+ /* strip header, get vlan id */ -+ buf = skb->data; -+ skb_pull(skb, 2); -+ -+ /* check for vlan header presence */ -+ if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00)) -+ return; -+ -+ port = buf[0] & 0xf; -+ -+ /* no need to fix up packets coming from a tagged source */ -+ if (priv->vlan_tagged & (1 << port)) -+ return; -+ -+ /* lookup port vid from local table, the switch passes an invalid vlan id */ -+ vlan = priv->vlan_id[priv->pvid[port]]; -+ -+ buf[14 + 2] &= 0xf0; -+ buf[14 + 2] |= vlan >> 8; -+ buf[15 + 2] = vlan & 0xff; -+} -+ -+static int -+ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) -+{ -+ int timeout = 20; -+ u32 t = 0; -+ -+ while (1) { -+ t = priv->read(priv, reg); -+ if ((t & mask) == val) -+ return 0; -+ -+ if (timeout-- <= 0) -+ break; -+ -+ udelay(10); -+ } -+ -+ pr_err("ar8216: timeout on reg %08x: %08x & %08x != %08x\n", -+ (unsigned int) reg, t, mask, val); -+ return -ETIMEDOUT; -+} -+ -+static void -+ar8216_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val) -+{ -+ if (ar8216_wait_bit(priv, AR8216_REG_VTU, AR8216_VTU_ACTIVE, 0)) -+ return; -+ if ((op & AR8216_VTU_OP) == AR8216_VTU_OP_LOAD) { -+ val &= AR8216_VTUDATA_MEMBER; -+ val |= AR8216_VTUDATA_VALID; -+ priv->write(priv, AR8216_REG_VTU_DATA, val); -+ } -+ op |= AR8216_VTU_ACTIVE; -+ priv->write(priv, AR8216_REG_VTU, op); -+} -+ -+static void -+ar8216_vtu_flush(struct ar8xxx_priv *priv) -+{ -+ ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0); -+} -+ -+static void -+ar8216_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask) -+{ -+ u32 op; -+ -+ op = AR8216_VTU_OP_LOAD | (vid << AR8216_VTU_VID_S); -+ ar8216_vtu_op(priv, op, port_mask); -+} -+ -+static int -+ar8216_atu_flush(struct ar8xxx_priv *priv) -+{ -+ int ret; -+ -+ ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0); -+ if (!ret) -+ priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); -+ -+ return ret; -+} -+ -+static u32 -+ar8216_read_port_status(struct ar8xxx_priv *priv, int port) -+{ -+ return priv->read(priv, AR8216_REG_PORT_STATUS(port)); -+} -+ -+static void -+ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, -+ u32 members, u32 pvid) -+{ -+ u32 header; -+ -+ if (chip_is_ar8216(priv) && priv->vlan && port == AR8216_PORT_CPU) -+ header = AR8216_PORT_CTRL_HEADER; -+ else -+ header = 0; -+ -+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), -+ AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE | -+ AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE | -+ AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK, -+ AR8216_PORT_CTRL_LEARN | header | -+ (egress << AR8216_PORT_CTRL_VLAN_MODE_S) | -+ (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S)); -+ -+ ar8xxx_rmw(priv, AR8216_REG_PORT_VLAN(port), -+ AR8216_PORT_VLAN_DEST_PORTS | AR8216_PORT_VLAN_MODE | -+ AR8216_PORT_VLAN_DEFAULT_ID, -+ (members << AR8216_PORT_VLAN_DEST_PORTS_S) | -+ (ingress << AR8216_PORT_VLAN_MODE_S) | -+ (pvid << AR8216_PORT_VLAN_DEFAULT_ID_S)); -+} -+ -+static int -+ar8216_hw_init(struct ar8xxx_priv *priv) -+{ -+ return 0; -+} -+ -+static void -+ar8216_init_globals(struct ar8xxx_priv *priv) -+{ -+ /* standard atheros magic */ -+ priv->write(priv, 0x38, 0xc000050e); -+ -+ ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, -+ AR8216_GCTRL_MTU, 1518 + 8 + 2); -+} -+ -+static void -+ar8216_init_port(struct ar8xxx_priv *priv, int port) -+{ -+ /* Enable port learning and tx */ -+ priv->write(priv, AR8216_REG_PORT_CTRL(port), -+ AR8216_PORT_CTRL_LEARN | -+ (4 << AR8216_PORT_CTRL_STATE_S)); -+ -+ priv->write(priv, AR8216_REG_PORT_VLAN(port), 0); -+ -+ if (port == AR8216_PORT_CPU) { -+ priv->write(priv, AR8216_REG_PORT_STATUS(port), -+ AR8216_PORT_STATUS_LINK_UP | -+ (ar8xxx_has_gige(priv) ? -+ AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) | -+ AR8216_PORT_STATUS_TXMAC | -+ AR8216_PORT_STATUS_RXMAC | -+ (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_RXFLOW : 0) | -+ (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_TXFLOW : 0) | -+ AR8216_PORT_STATUS_DUPLEX); -+ } else { -+ priv->write(priv, AR8216_REG_PORT_STATUS(port), -+ AR8216_PORT_STATUS_LINK_AUTO); -+ } -+} -+ -+static const struct ar8xxx_chip ar8216_chip = { -+ .caps = AR8XXX_CAP_MIB_COUNTERS, -+ -+ .hw_init = ar8216_hw_init, -+ .init_globals = ar8216_init_globals, -+ .init_port = ar8216_init_port, -+ .setup_port = ar8216_setup_port, -+ .read_port_status = ar8216_read_port_status, -+ .atu_flush = ar8216_atu_flush, -+ .vtu_flush = ar8216_vtu_flush, -+ .vtu_load_vlan = ar8216_vtu_load_vlan, -+ -+ .num_mibs = ARRAY_SIZE(ar8216_mibs), -+ .mib_decs = ar8216_mibs, -+}; -+ -+static void -+ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, -+ u32 members, u32 pvid) -+{ -+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), -+ AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE | -+ AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE | -+ AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK, -+ AR8216_PORT_CTRL_LEARN | -+ (egress << AR8216_PORT_CTRL_VLAN_MODE_S) | -+ (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S)); -+ -+ ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN(port), -+ AR8236_PORT_VLAN_DEFAULT_ID, -+ (pvid << AR8236_PORT_VLAN_DEFAULT_ID_S)); -+ -+ ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN2(port), -+ AR8236_PORT_VLAN2_VLAN_MODE | -+ AR8236_PORT_VLAN2_MEMBER, -+ (ingress << AR8236_PORT_VLAN2_VLAN_MODE_S) | -+ (members << AR8236_PORT_VLAN2_MEMBER_S)); -+} -+ -+static int -+ar8236_hw_init(struct ar8xxx_priv *priv) -+{ -+ int i; -+ struct mii_bus *bus; -+ -+ if (priv->initialized) -+ return 0; -+ -+ /* Initialize the PHYs */ -+ bus = priv->mii_bus; -+ for (i = 0; i < 5; i++) { -+ mdiobus_write(bus, i, MII_ADVERTISE, -+ ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | -+ ADVERTISE_PAUSE_ASYM); -+ mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); -+ } -+ msleep(1000); -+ -+ priv->initialized = true; -+ return 0; -+} -+ -+static void -+ar8236_init_globals(struct ar8xxx_priv *priv) -+{ -+ /* enable jumbo frames */ -+ ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, -+ AR8316_GCTRL_MTU, 9018 + 8 + 2); -+ -+ /* Enable MIB counters */ -+ ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN, -+ (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) | -+ AR8236_MIB_EN); -+} -+ -+static const struct ar8xxx_chip ar8236_chip = { -+ .caps = AR8XXX_CAP_MIB_COUNTERS, -+ .hw_init = ar8236_hw_init, -+ .init_globals = ar8236_init_globals, -+ .init_port = ar8216_init_port, -+ .setup_port = ar8236_setup_port, -+ .read_port_status = ar8216_read_port_status, -+ .atu_flush = ar8216_atu_flush, -+ .vtu_flush = ar8216_vtu_flush, -+ .vtu_load_vlan = ar8216_vtu_load_vlan, -+ -+ .num_mibs = ARRAY_SIZE(ar8236_mibs), -+ .mib_decs = ar8236_mibs, -+}; -+ -+static int -+ar8316_hw_init(struct ar8xxx_priv *priv) -+{ -+ int i; -+ u32 val, newval; -+ struct mii_bus *bus; -+ -+ val = priv->read(priv, AR8316_REG_POSTRIP); -+ -+ if (priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { -+ if (priv->port4_phy) { -+ /* value taken from Ubiquiti RouterStation Pro */ -+ newval = 0x81461bea; -+ pr_info("ar8316: Using port 4 as PHY\n"); -+ } else { -+ newval = 0x01261be2; -+ pr_info("ar8316: Using port 4 as switch port\n"); -+ } -+ } else if (priv->phy->interface == PHY_INTERFACE_MODE_GMII) { -+ /* value taken from AVM Fritz!Box 7390 sources */ -+ newval = 0x010e5b71; -+ } else { -+ /* no known value for phy interface */ -+ pr_err("ar8316: unsupported mii mode: %d.\n", -+ priv->phy->interface); -+ return -EINVAL; -+ } -+ -+ if (val == newval) -+ goto out; -+ -+ priv->write(priv, AR8316_REG_POSTRIP, newval); -+ -+ if (priv->port4_phy && -+ priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { -+ /* work around for phy4 rgmii mode */ -+ ar8xxx_phy_dbg_write(priv, 4, 0x12, 0x480c); -+ /* rx delay */ -+ ar8xxx_phy_dbg_write(priv, 4, 0x0, 0x824e); -+ /* tx delay */ -+ ar8xxx_phy_dbg_write(priv, 4, 0x5, 0x3d47); -+ msleep(1000); -+ } -+ -+ /* Initialize the ports */ -+ bus = priv->mii_bus; -+ for (i = 0; i < 5; i++) { -+ /* initialize the port itself */ -+ 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); -+ -+out: -+ priv->initialized = true; -+ return 0; -+} -+ -+static void -+ar8316_init_globals(struct ar8xxx_priv *priv) -+{ -+ /* standard atheros magic */ -+ priv->write(priv, 0x38, 0xc000050e); -+ -+ /* enable cpu port to receive multicast and broadcast frames */ -+ priv->write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f); -+ -+ /* enable jumbo frames */ -+ ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, -+ AR8316_GCTRL_MTU, 9018 + 8 + 2); -+ -+ /* Enable MIB counters */ -+ ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN, -+ (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) | -+ AR8236_MIB_EN); -+} -+ -+static const struct ar8xxx_chip ar8316_chip = { -+ .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS, -+ .hw_init = ar8316_hw_init, -+ .init_globals = ar8316_init_globals, -+ .init_port = ar8216_init_port, -+ .setup_port = ar8216_setup_port, -+ .read_port_status = ar8216_read_port_status, -+ .atu_flush = ar8216_atu_flush, -+ .vtu_flush = ar8216_vtu_flush, -+ .vtu_load_vlan = ar8216_vtu_load_vlan, -+ -+ .num_mibs = ARRAY_SIZE(ar8236_mibs), -+ .mib_decs = ar8236_mibs, -+}; -+ -+static u32 -+ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg) -+{ -+ u32 t; -+ -+ if (!cfg) -+ return 0; -+ -+ t = 0; -+ switch (cfg->mode) { -+ case AR8327_PAD_NC: -+ break; -+ -+ case AR8327_PAD_MAC2MAC_MII: -+ t = AR8327_PAD_MAC_MII_EN; -+ if (cfg->rxclk_sel) -+ t |= AR8327_PAD_MAC_MII_RXCLK_SEL; -+ if (cfg->txclk_sel) -+ t |= AR8327_PAD_MAC_MII_TXCLK_SEL; -+ break; -+ -+ case AR8327_PAD_MAC2MAC_GMII: -+ t = AR8327_PAD_MAC_GMII_EN; -+ if (cfg->rxclk_sel) -+ t |= AR8327_PAD_MAC_GMII_RXCLK_SEL; -+ if (cfg->txclk_sel) -+ t |= AR8327_PAD_MAC_GMII_TXCLK_SEL; -+ break; -+ -+ case AR8327_PAD_MAC_SGMII: -+ t = AR8327_PAD_SGMII_EN; -+ -+ /* -+ * WAR for the QUalcomm Atheros AP136 board. -+ * It seems that RGMII TX/RX delay settings needs to be -+ * applied for SGMII mode as well, The ethernet is not -+ * reliable without this. -+ */ -+ t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; -+ t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; -+ if (cfg->rxclk_delay_en) -+ t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; -+ if (cfg->txclk_delay_en) -+ t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; -+ -+ if (cfg->sgmii_delay_en) -+ t |= AR8327_PAD_SGMII_DELAY_EN; -+ -+ break; -+ -+ case AR8327_PAD_MAC2PHY_MII: -+ t = AR8327_PAD_PHY_MII_EN; -+ if (cfg->rxclk_sel) -+ t |= AR8327_PAD_PHY_MII_RXCLK_SEL; -+ if (cfg->txclk_sel) -+ t |= AR8327_PAD_PHY_MII_TXCLK_SEL; -+ break; -+ -+ case AR8327_PAD_MAC2PHY_GMII: -+ t = AR8327_PAD_PHY_GMII_EN; -+ if (cfg->pipe_rxclk_sel) -+ t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL; -+ if (cfg->rxclk_sel) -+ t |= AR8327_PAD_PHY_GMII_RXCLK_SEL; -+ if (cfg->txclk_sel) -+ t |= AR8327_PAD_PHY_GMII_TXCLK_SEL; -+ break; -+ -+ case AR8327_PAD_MAC_RGMII: -+ t = AR8327_PAD_RGMII_EN; -+ t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; -+ t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; -+ if (cfg->rxclk_delay_en) -+ t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; -+ if (cfg->txclk_delay_en) -+ t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; -+ break; -+ -+ case AR8327_PAD_PHY_GMII: -+ t = AR8327_PAD_PHYX_GMII_EN; -+ break; -+ -+ case AR8327_PAD_PHY_RGMII: -+ t = AR8327_PAD_PHYX_RGMII_EN; -+ break; -+ -+ case AR8327_PAD_PHY_MII: -+ t = AR8327_PAD_PHYX_MII_EN; -+ break; -+ } -+ -+ return t; -+} -+ -+static void -+ar8327_phy_fixup(struct ar8xxx_priv *priv, int phy) -+{ -+ switch (priv->chip_rev) { -+ case 1: -+ /* For 100M waveform */ -+ ar8xxx_phy_dbg_write(priv, phy, 0, 0x02ea); -+ /* Turn on Gigabit clock */ -+ ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x68a0); -+ break; -+ -+ case 2: -+ ar8xxx_phy_mmd_write(priv, phy, 0x7, 0x3c); -+ ar8xxx_phy_mmd_write(priv, phy, 0x4007, 0x0); -+ /* fallthrough */ -+ case 4: -+ ar8xxx_phy_mmd_write(priv, phy, 0x3, 0x800d); -+ ar8xxx_phy_mmd_write(priv, phy, 0x4003, 0x803f); -+ -+ ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x6860); -+ ar8xxx_phy_dbg_write(priv, phy, 0x5, 0x2c46); -+ ar8xxx_phy_dbg_write(priv, phy, 0x3c, 0x6000); -+ break; -+ } -+} -+ -+static u32 -+ar8327_get_port_init_status(struct ar8327_port_cfg *cfg) -+{ -+ u32 t; -+ -+ if (!cfg->force_link) -+ return AR8216_PORT_STATUS_LINK_AUTO; -+ -+ t = AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC; -+ t |= cfg->duplex ? AR8216_PORT_STATUS_DUPLEX : 0; -+ t |= cfg->rxpause ? AR8216_PORT_STATUS_RXFLOW : 0; -+ t |= cfg->txpause ? AR8216_PORT_STATUS_TXFLOW : 0; -+ -+ switch (cfg->speed) { -+ case AR8327_PORT_SPEED_10: -+ t |= AR8216_PORT_SPEED_10M; -+ break; -+ case AR8327_PORT_SPEED_100: -+ t |= AR8216_PORT_SPEED_100M; -+ break; -+ case AR8327_PORT_SPEED_1000: -+ t |= AR8216_PORT_SPEED_1000M; -+ break; -+ } -+ -+ return t; -+} -+ -+#define AR8327_LED_ENTRY(_num, _reg, _shift) \ -+ [_num] = { .reg = (_reg), .shift = (_shift) } -+ -+static const struct ar8327_led_entry -+ar8327_led_map[AR8327_NUM_LEDS] = { -+ AR8327_LED_ENTRY(AR8327_LED_PHY0_0, 0, 14), -+ AR8327_LED_ENTRY(AR8327_LED_PHY0_1, 1, 14), -+ AR8327_LED_ENTRY(AR8327_LED_PHY0_2, 2, 14), -+ -+ AR8327_LED_ENTRY(AR8327_LED_PHY1_0, 3, 8), -+ AR8327_LED_ENTRY(AR8327_LED_PHY1_1, 3, 10), -+ AR8327_LED_ENTRY(AR8327_LED_PHY1_2, 3, 12), -+ -+ AR8327_LED_ENTRY(AR8327_LED_PHY2_0, 3, 14), -+ AR8327_LED_ENTRY(AR8327_LED_PHY2_1, 3, 16), -+ AR8327_LED_ENTRY(AR8327_LED_PHY2_2, 3, 18), -+ -+ AR8327_LED_ENTRY(AR8327_LED_PHY3_0, 3, 20), -+ AR8327_LED_ENTRY(AR8327_LED_PHY3_1, 3, 22), -+ AR8327_LED_ENTRY(AR8327_LED_PHY3_2, 3, 24), -+ -+ AR8327_LED_ENTRY(AR8327_LED_PHY4_0, 0, 30), -+ AR8327_LED_ENTRY(AR8327_LED_PHY4_1, 1, 30), -+ AR8327_LED_ENTRY(AR8327_LED_PHY4_2, 2, 30), -+}; -+ -+static void -+ar8327_set_led_pattern(struct ar8xxx_priv *priv, unsigned int led_num, -+ enum ar8327_led_pattern pattern) -+{ -+ const struct ar8327_led_entry *entry; -+ -+ entry = &ar8327_led_map[led_num]; -+ ar8xxx_rmw(priv, AR8327_REG_LED_CTRL(entry->reg), -+ (3 << entry->shift), pattern << entry->shift); -+} -+ -+static void -+ar8327_led_work_func(struct work_struct *work) -+{ -+ struct ar8327_led *aled; -+ u8 pattern; -+ -+ aled = container_of(work, struct ar8327_led, led_work); -+ -+ spin_lock(&aled->lock); -+ pattern = aled->pattern; -+ spin_unlock(&aled->lock); -+ -+ ar8327_set_led_pattern(aled->sw_priv, aled->led_num, -+ pattern); -+} -+ -+static void -+ar8327_led_schedule_change(struct ar8327_led *aled, u8 pattern) -+{ -+ if (aled->pattern == pattern) -+ return; -+ -+ aled->pattern = pattern; -+ schedule_work(&aled->led_work); -+} -+ -+static inline struct ar8327_led * -+led_cdev_to_ar8327_led(struct led_classdev *led_cdev) -+{ -+ 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); -+} -+ -+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; -+} -+ -+static void -+ar8327_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val) -+{ -+ if (ar8216_wait_bit(priv, AR8327_REG_VTU_FUNC1, -+ AR8327_VTU_FUNC1_BUSY, 0)) -+ return; -+ -+ if ((op & AR8327_VTU_FUNC1_OP) == AR8327_VTU_FUNC1_OP_LOAD) -+ priv->write(priv, AR8327_REG_VTU_FUNC0, val); -+ -+ op |= AR8327_VTU_FUNC1_BUSY; -+ priv->write(priv, AR8327_REG_VTU_FUNC1, op); -+} -+ -+static void -+ar8327_vtu_flush(struct ar8xxx_priv *priv) -+{ -+ ar8327_vtu_op(priv, AR8327_VTU_FUNC1_OP_FLUSH, 0); -+} -+ -+static void -+ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask) -+{ -+ u32 op; -+ u32 val; -+ int i; -+ -+ op = AR8327_VTU_FUNC1_OP_LOAD | (vid << AR8327_VTU_FUNC1_VID_S); -+ val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL; -+ for (i = 0; i < AR8327_NUM_PORTS; i++) { -+ u32 mode; -+ -+ if ((port_mask & BIT(i)) == 0) -+ mode = AR8327_VTU_FUNC0_EG_MODE_NOT; -+ else if (priv->vlan == 0) -+ mode = AR8327_VTU_FUNC0_EG_MODE_KEEP; -+ else if (priv->vlan_tagged & BIT(i)) -+ mode = AR8327_VTU_FUNC0_EG_MODE_TAG; -+ else -+ mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG; -+ -+ val |= mode << AR8327_VTU_FUNC0_EG_MODE_S(i); -+ } -+ ar8327_vtu_op(priv, op, val); -+} -+ -+static void -+ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, -+ u32 members, u32 pvid) -+{ -+ u32 t; -+ u32 mode; -+ -+ t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S; -+ t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S; -+ priv->write(priv, AR8327_REG_PORT_VLAN0(port), t); -+ -+ mode = AR8327_PORT_VLAN1_OUT_MODE_UNMOD; -+ switch (egress) { -+ case AR8216_OUT_KEEP: -+ mode = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; -+ break; -+ case AR8216_OUT_STRIP_VLAN: -+ mode = AR8327_PORT_VLAN1_OUT_MODE_UNTAG; -+ break; -+ case AR8216_OUT_ADD_VLAN: -+ mode = AR8327_PORT_VLAN1_OUT_MODE_TAG; -+ break; -+ } -+ -+ t = AR8327_PORT_VLAN1_PORT_VLAN_PROP; -+ t |= mode << AR8327_PORT_VLAN1_OUT_MODE_S; -+ priv->write(priv, AR8327_REG_PORT_VLAN1(port), t); -+ -+ t = members; -+ t |= AR8327_PORT_LOOKUP_LEARN; -+ t |= ingress << AR8327_PORT_LOOKUP_IN_MODE_S; -+ t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; -+ priv->write(priv, AR8327_REG_PORT_LOOKUP(port), t); -+} -+ -+static const struct ar8xxx_chip ar8327_chip = { -+ .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS, -+ .hw_init = ar8327_hw_init, -+ .cleanup = ar8327_cleanup, -+ .init_globals = ar8327_init_globals, -+ .init_port = ar8327_init_port, -+ .setup_port = ar8327_setup_port, -+ .read_port_status = ar8327_read_port_status, -+ .atu_flush = ar8327_atu_flush, -+ .vtu_flush = ar8327_vtu_flush, -+ .vtu_load_vlan = ar8327_vtu_load_vlan, -+ -+ .num_mibs = ARRAY_SIZE(ar8236_mibs), -+ .mib_decs = ar8236_mibs, -+}; -+ -+static int -+ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ priv->vlan = !!val->value.i; -+ return 0; -+} -+ -+static int -+ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ val->value.i = priv->vlan; -+ return 0; -+} -+ -+ -+static int -+ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ -+ /* make sure no invalid PVIDs get set */ -+ -+ if (vlan >= dev->vlans) -+ return -EINVAL; -+ -+ priv->pvid[port] = vlan; -+ return 0; -+} -+ -+static int -+ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ *vlan = priv->pvid[port]; -+ return 0; -+} -+ -+static int -+ar8xxx_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ priv->vlan_id[val->port_vlan] = val->value.i; -+ return 0; -+} -+ -+static int -+ar8xxx_sw_get_vid(struct switch_dev *dev, const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ val->value.i = priv->vlan_id[val->port_vlan]; -+ return 0; -+} -+ -+static int -+ar8xxx_sw_get_port_link(struct switch_dev *dev, int port, -+ struct switch_port_link *link) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ -+ ar8216_read_port_link(priv, port, link); -+ return 0; -+} -+ -+static int -+ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ u8 ports = priv->vlan_table[val->port_vlan]; -+ int i; -+ -+ val->len = 0; -+ for (i = 0; i < dev->ports; i++) { -+ struct switch_port *p; -+ -+ if (!(ports & (1 << i))) -+ continue; -+ -+ p = &val->value.ports[val->len++]; -+ p->id = i; -+ if (priv->vlan_tagged & (1 << i)) -+ p->flags = (1 << SWITCH_PORT_FLAG_TAGGED); -+ else -+ p->flags = 0; -+ } -+ return 0; -+} -+ -+static int -+ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ u8 *vt = &priv->vlan_table[val->port_vlan]; -+ int i, j; -+ -+ *vt = 0; -+ for (i = 0; i < val->len; i++) { -+ struct switch_port *p = &val->value.ports[i]; -+ -+ if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) { -+ priv->vlan_tagged |= (1 << p->id); -+ } else { -+ priv->vlan_tagged &= ~(1 << p->id); -+ priv->pvid[p->id] = val->port_vlan; -+ -+ /* make sure that an untagged port does not -+ * appear in other vlans */ -+ for (j = 0; j < AR8X16_MAX_VLANS; j++) { -+ if (j == val->port_vlan) -+ continue; -+ priv->vlan_table[j] &= ~(1 << p->id); -+ } -+ } -+ -+ *vt |= 1 << p->id; -+ } -+ return 0; -+} -+ -+static void -+ar8327_set_mirror_regs(struct ar8xxx_priv *priv) -+{ -+ int port; -+ -+ /* reset all mirror registers */ -+ ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0, -+ AR8327_FWD_CTRL0_MIRROR_PORT, -+ (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S)); -+ for (port = 0; port < AR8327_NUM_PORTS; port++) { -+ ar8xxx_rmw(priv, AR8327_REG_PORT_LOOKUP(port), -+ AR8327_PORT_LOOKUP_ING_MIRROR_EN, -+ 0); -+ -+ ar8xxx_rmw(priv, AR8327_REG_PORT_HOL_CTRL1(port), -+ AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN, -+ 0); -+ } -+ -+ /* now enable mirroring if necessary */ -+ if (priv->source_port >= AR8327_NUM_PORTS || -+ priv->monitor_port >= AR8327_NUM_PORTS || -+ priv->source_port == priv->monitor_port) { -+ return; -+ } -+ -+ ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0, -+ AR8327_FWD_CTRL0_MIRROR_PORT, -+ (priv->monitor_port << AR8327_FWD_CTRL0_MIRROR_PORT_S)); -+ -+ if (priv->mirror_rx) -+ ar8xxx_rmw(priv, AR8327_REG_PORT_LOOKUP(priv->source_port), -+ AR8327_PORT_LOOKUP_ING_MIRROR_EN, -+ AR8327_PORT_LOOKUP_ING_MIRROR_EN); -+ -+ if (priv->mirror_tx) -+ ar8xxx_rmw(priv, AR8327_REG_PORT_HOL_CTRL1(priv->source_port), -+ AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN, -+ AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN); -+} -+ -+static void -+ar8216_set_mirror_regs(struct ar8xxx_priv *priv) -+{ -+ int port; -+ -+ /* reset all mirror registers */ -+ ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT, -+ AR8216_GLOBAL_CPUPORT_MIRROR_PORT, -+ (0xF << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S)); -+ for (port = 0; port < AR8216_NUM_PORTS; port++) { -+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), -+ AR8216_PORT_CTRL_MIRROR_RX, -+ 0); -+ -+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), -+ AR8216_PORT_CTRL_MIRROR_TX, -+ 0); -+ } -+ -+ /* now enable mirroring if necessary */ -+ if (priv->source_port >= AR8216_NUM_PORTS || -+ priv->monitor_port >= AR8216_NUM_PORTS || -+ priv->source_port == priv->monitor_port) { -+ return; -+ } -+ -+ ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT, -+ AR8216_GLOBAL_CPUPORT_MIRROR_PORT, -+ (priv->monitor_port << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S)); -+ -+ if (priv->mirror_rx) -+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port), -+ AR8216_PORT_CTRL_MIRROR_RX, -+ AR8216_PORT_CTRL_MIRROR_RX); -+ -+ if (priv->mirror_tx) -+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port), -+ AR8216_PORT_CTRL_MIRROR_TX, -+ AR8216_PORT_CTRL_MIRROR_TX); -+} -+ -+static void -+ar8xxx_set_mirror_regs(struct ar8xxx_priv *priv) -+{ -+ if (chip_is_ar8327(priv) || chip_is_ar8337(priv)) { -+ ar8327_set_mirror_regs(priv); -+ } else { -+ ar8216_set_mirror_regs(priv); -+ } -+} -+ -+static int -+ar8xxx_sw_hw_apply(struct switch_dev *dev) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ u8 portmask[AR8X16_MAX_PORTS]; -+ int i, j; -+ -+ mutex_lock(&priv->reg_mutex); -+ /* flush all vlan translation unit entries */ -+ priv->chip->vtu_flush(priv); -+ -+ memset(portmask, 0, sizeof(portmask)); -+ if (!priv->init) { -+ /* calculate the port destination masks and load vlans -+ * into the vlan translation unit */ -+ for (j = 0; j < AR8X16_MAX_VLANS; j++) { -+ u8 vp = priv->vlan_table[j]; -+ -+ if (!vp) -+ continue; -+ -+ for (i = 0; i < dev->ports; i++) { -+ u8 mask = (1 << i); -+ if (vp & mask) -+ portmask[i] |= vp & ~mask; -+ } -+ -+ priv->chip->vtu_load_vlan(priv, priv->vlan_id[j], -+ priv->vlan_table[j]); -+ } -+ } else { -+ /* vlan disabled: -+ * isolate all ports, but connect them to the cpu port */ -+ for (i = 0; i < dev->ports; i++) { -+ if (i == AR8216_PORT_CPU) -+ continue; -+ -+ portmask[i] = 1 << AR8216_PORT_CPU; -+ portmask[AR8216_PORT_CPU] |= (1 << i); -+ } -+ } -+ -+ /* update the port destination mask registers and tag settings */ -+ for (i = 0; i < dev->ports; i++) { -+ int egress, ingress; -+ int pvid; -+ -+ if (priv->vlan) { -+ pvid = priv->vlan_id[priv->pvid[i]]; -+ if (priv->vlan_tagged & (1 << i)) -+ egress = AR8216_OUT_ADD_VLAN; -+ else -+ egress = AR8216_OUT_STRIP_VLAN; -+ ingress = AR8216_IN_SECURE; -+ } else { -+ pvid = i; -+ egress = AR8216_OUT_KEEP; -+ ingress = AR8216_IN_PORT_ONLY; -+ } -+ -+ priv->chip->setup_port(priv, i, egress, ingress, portmask[i], -+ pvid); -+ } -+ -+ ar8xxx_set_mirror_regs(priv); -+ -+ mutex_unlock(&priv->reg_mutex); -+ return 0; -+} -+ -+static int -+ar8xxx_sw_reset_switch(struct switch_dev *dev) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ int i; -+ -+ mutex_lock(&priv->reg_mutex); -+ memset(&priv->vlan, 0, sizeof(struct ar8xxx_priv) - -+ offsetof(struct ar8xxx_priv, vlan)); -+ -+ for (i = 0; i < AR8X16_MAX_VLANS; i++) -+ priv->vlan_id[i] = i; -+ -+ /* Configure all ports */ -+ for (i = 0; i < dev->ports; i++) -+ priv->chip->init_port(priv, i); -+ -+ priv->mirror_rx = false; -+ priv->mirror_tx = false; -+ priv->source_port = 0; -+ priv->monitor_port = 0; -+ -+ priv->chip->init_globals(priv); -+ -+ mutex_unlock(&priv->reg_mutex); -+ -+ return ar8xxx_sw_hw_apply(dev); -+} -+ -+static int -+ar8xxx_sw_set_reset_mibs(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ unsigned int len; -+ int ret; -+ -+ if (!ar8xxx_has_mib_counters(priv)) -+ return -EOPNOTSUPP; -+ -+ mutex_lock(&priv->mib_lock); -+ -+ len = priv->dev.ports * priv->chip->num_mibs * -+ sizeof(*priv->mib_stats); -+ memset(priv->mib_stats, '\0', len); -+ ret = ar8xxx_mib_flush(priv); -+ if (ret) -+ goto unlock; -+ -+ ret = 0; -+ -+unlock: -+ mutex_unlock(&priv->mib_lock); -+ return ret; -+} -+ -+static int -+ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ -+ mutex_lock(&priv->reg_mutex); -+ priv->mirror_rx = !!val->value.i; -+ ar8xxx_set_mirror_regs(priv); -+ mutex_unlock(&priv->reg_mutex); -+ -+ return 0; -+} -+ -+static int -+ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ val->value.i = priv->mirror_rx; -+ return 0; -+} -+ -+static int -+ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ -+ mutex_lock(&priv->reg_mutex); -+ priv->mirror_tx = !!val->value.i; -+ ar8xxx_set_mirror_regs(priv); -+ mutex_unlock(&priv->reg_mutex); -+ -+ return 0; -+} -+ -+static int -+ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ val->value.i = priv->mirror_tx; -+ return 0; -+} -+ -+static int -+ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ -+ mutex_lock(&priv->reg_mutex); -+ priv->monitor_port = val->value.i; -+ ar8xxx_set_mirror_regs(priv); -+ mutex_unlock(&priv->reg_mutex); -+ -+ return 0; -+} -+ -+static int -+ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ val->value.i = priv->monitor_port; -+ return 0; -+} -+ -+static int -+ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ -+ mutex_lock(&priv->reg_mutex); -+ priv->source_port = val->value.i; -+ ar8xxx_set_mirror_regs(priv); -+ mutex_unlock(&priv->reg_mutex); -+ -+ return 0; -+} -+ -+static int -+ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ val->value.i = priv->source_port; -+ return 0; -+} -+ -+static int -+ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ int port; -+ int ret; -+ -+ if (!ar8xxx_has_mib_counters(priv)) -+ return -EOPNOTSUPP; -+ -+ port = val->port_vlan; -+ if (port >= dev->ports) -+ return -EINVAL; -+ -+ mutex_lock(&priv->mib_lock); -+ ret = ar8xxx_mib_capture(priv); -+ if (ret) -+ goto unlock; -+ -+ ar8xxx_mib_fetch_port_stat(priv, port, true); -+ -+ ret = 0; -+ -+unlock: -+ mutex_unlock(&priv->mib_lock); -+ return ret; -+} -+ -+static int -+ar8xxx_sw_get_port_mib(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); -+ const struct ar8xxx_chip *chip = priv->chip; -+ u64 *mib_stats; -+ int port; -+ int ret; -+ char *buf = priv->buf; -+ int i, len = 0; -+ -+ if (!ar8xxx_has_mib_counters(priv)) -+ return -EOPNOTSUPP; -+ -+ port = val->port_vlan; -+ if (port >= dev->ports) -+ return -EINVAL; -+ -+ mutex_lock(&priv->mib_lock); -+ ret = ar8xxx_mib_capture(priv); -+ if (ret) -+ goto unlock; -+ -+ ar8xxx_mib_fetch_port_stat(priv, port, false); -+ -+ len += snprintf(buf + len, sizeof(priv->buf) - len, -+ "Port %d MIB counters\n", -+ port); -+ -+ mib_stats = &priv->mib_stats[port * chip->num_mibs]; -+ for (i = 0; i < chip->num_mibs; i++) -+ len += snprintf(buf + len, sizeof(priv->buf) - len, -+ "%-12s: %llu\n", -+ chip->mib_decs[i].name, -+ mib_stats[i]); -+ -+ val->value.s = buf; -+ val->len = len; -+ -+ ret = 0; -+ -+unlock: -+ mutex_unlock(&priv->mib_lock); -+ return ret; -+} -+ -+static struct switch_attr ar8xxx_sw_attr_globals[] = { -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_vlan", -+ .description = "Enable VLAN mode", -+ .set = ar8xxx_sw_set_vlan, -+ .get = ar8xxx_sw_get_vlan, -+ .max = 1 -+ }, -+ { -+ .type = SWITCH_TYPE_NOVAL, -+ .name = "reset_mibs", -+ .description = "Reset all MIB counters", -+ .set = ar8xxx_sw_set_reset_mibs, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_mirror_rx", -+ .description = "Enable mirroring of RX packets", -+ .set = ar8xxx_sw_set_mirror_rx_enable, -+ .get = ar8xxx_sw_get_mirror_rx_enable, -+ .max = 1 -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_mirror_tx", -+ .description = "Enable mirroring of TX packets", -+ .set = ar8xxx_sw_set_mirror_tx_enable, -+ .get = ar8xxx_sw_get_mirror_tx_enable, -+ .max = 1 -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "mirror_monitor_port", -+ .description = "Mirror monitor port", -+ .set = ar8xxx_sw_set_mirror_monitor_port, -+ .get = ar8xxx_sw_get_mirror_monitor_port, -+ .max = AR8216_NUM_PORTS - 1 -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "mirror_source_port", -+ .description = "Mirror source port", -+ .set = ar8xxx_sw_set_mirror_source_port, -+ .get = ar8xxx_sw_get_mirror_source_port, -+ .max = AR8216_NUM_PORTS - 1 -+ }, -+}; -+ -+static struct switch_attr ar8327_sw_attr_globals[] = { -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_vlan", -+ .description = "Enable VLAN mode", -+ .set = ar8xxx_sw_set_vlan, -+ .get = ar8xxx_sw_get_vlan, -+ .max = 1 -+ }, -+ { -+ .type = SWITCH_TYPE_NOVAL, -+ .name = "reset_mibs", -+ .description = "Reset all MIB counters", -+ .set = ar8xxx_sw_set_reset_mibs, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_mirror_rx", -+ .description = "Enable mirroring of RX packets", -+ .set = ar8xxx_sw_set_mirror_rx_enable, -+ .get = ar8xxx_sw_get_mirror_rx_enable, -+ .max = 1 -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_mirror_tx", -+ .description = "Enable mirroring of TX packets", -+ .set = ar8xxx_sw_set_mirror_tx_enable, -+ .get = ar8xxx_sw_get_mirror_tx_enable, -+ .max = 1 -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "mirror_monitor_port", -+ .description = "Mirror monitor port", -+ .set = ar8xxx_sw_set_mirror_monitor_port, -+ .get = ar8xxx_sw_get_mirror_monitor_port, -+ .max = AR8327_NUM_PORTS - 1 -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "mirror_source_port", -+ .description = "Mirror source port", -+ .set = ar8xxx_sw_set_mirror_source_port, -+ .get = ar8xxx_sw_get_mirror_source_port, -+ .max = AR8327_NUM_PORTS - 1 -+ }, -+}; -+ -+static struct switch_attr ar8xxx_sw_attr_port[] = { -+ { -+ .type = SWITCH_TYPE_NOVAL, -+ .name = "reset_mib", -+ .description = "Reset single port MIB counters", -+ .set = ar8xxx_sw_set_port_reset_mib, -+ }, -+ { -+ .type = SWITCH_TYPE_STRING, -+ .name = "mib", -+ .description = "Get port's MIB counters", -+ .set = NULL, -+ .get = ar8xxx_sw_get_port_mib, -+ }, -+}; -+ -+static struct switch_attr ar8xxx_sw_attr_vlan[] = { -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "vid", -+ .description = "VLAN ID (0-4094)", -+ .set = ar8xxx_sw_set_vid, -+ .get = ar8xxx_sw_get_vid, -+ .max = 4094, -+ }, -+}; -+ -+static const struct switch_dev_ops ar8xxx_sw_ops = { -+ .attr_global = { -+ .attr = ar8xxx_sw_attr_globals, -+ .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_globals), -+ }, -+ .attr_port = { -+ .attr = ar8xxx_sw_attr_port, -+ .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port), -+ }, -+ .attr_vlan = { -+ .attr = ar8xxx_sw_attr_vlan, -+ .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan), -+ }, -+ .get_port_pvid = ar8xxx_sw_get_pvid, -+ .set_port_pvid = ar8xxx_sw_set_pvid, -+ .get_vlan_ports = ar8xxx_sw_get_ports, -+ .set_vlan_ports = ar8xxx_sw_set_ports, -+ .apply_config = ar8xxx_sw_hw_apply, -+ .reset_switch = ar8xxx_sw_reset_switch, -+ .get_port_link = ar8xxx_sw_get_port_link, -+}; -+ -+static const struct switch_dev_ops ar8327_sw_ops = { -+ .attr_global = { -+ .attr = ar8327_sw_attr_globals, -+ .n_attr = ARRAY_SIZE(ar8327_sw_attr_globals), -+ }, -+ .attr_port = { -+ .attr = ar8xxx_sw_attr_port, -+ .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port), -+ }, -+ .attr_vlan = { -+ .attr = ar8xxx_sw_attr_vlan, -+ .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan), -+ }, -+ .get_port_pvid = ar8xxx_sw_get_pvid, -+ .set_port_pvid = ar8xxx_sw_set_pvid, -+ .get_vlan_ports = ar8xxx_sw_get_ports, -+ .set_vlan_ports = ar8xxx_sw_set_ports, -+ .apply_config = ar8xxx_sw_hw_apply, -+ .reset_switch = ar8xxx_sw_reset_switch, -+ .get_port_link = ar8xxx_sw_get_port_link, -+}; -+ -+static int -+ar8xxx_id_chip(struct ar8xxx_priv *priv) -+{ -+ u32 val; -+ u16 id; -+ int i; -+ -+ val = priv->read(priv, AR8216_REG_CTRL); -+ if (val == ~0) -+ return -ENODEV; -+ -+ id = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION); -+ for (i = 0; i < AR8X16_PROBE_RETRIES; i++) { -+ u16 t; -+ -+ val = priv->read(priv, AR8216_REG_CTRL); -+ if (val == ~0) -+ return -ENODEV; -+ -+ t = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION); -+ if (t != id) -+ return -ENODEV; -+ } -+ -+ priv->chip_ver = (id & AR8216_CTRL_VERSION) >> AR8216_CTRL_VERSION_S; -+ priv->chip_rev = (id & AR8216_CTRL_REVISION); -+ -+ switch (priv->chip_ver) { -+ case AR8XXX_VER_AR8216: -+ priv->chip = &ar8216_chip; -+ break; -+ case AR8XXX_VER_AR8236: -+ priv->chip = &ar8236_chip; -+ break; -+ case AR8XXX_VER_AR8316: -+ priv->chip = &ar8316_chip; -+ break; -+ case AR8XXX_VER_AR8327: -+ priv->mii_lo_first = true; -+ priv->chip = &ar8327_chip; -+ break; -+ case AR8XXX_VER_AR8337: -+ priv->mii_lo_first = true; -+ priv->chip = &ar8327_chip; -+ break; -+ default: -+ pr_err("ar8216: Unknown Atheros device [ver=%d, rev=%d]\n", -+ priv->chip_ver, priv->chip_rev); -+ -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+static void -+ar8xxx_mib_work_func(struct work_struct *work) -+{ -+ struct ar8xxx_priv *priv; -+ int err; -+ -+ priv = container_of(work, struct ar8xxx_priv, mib_work.work); -+ -+ mutex_lock(&priv->mib_lock); -+ -+ err = ar8xxx_mib_capture(priv); -+ if (err) -+ goto next_port; -+ -+ ar8xxx_mib_fetch_port_stat(priv, priv->mib_next_port, false); -+ -+next_port: -+ priv->mib_next_port++; -+ if (priv->mib_next_port >= priv->dev.ports) -+ priv->mib_next_port = 0; -+ -+ mutex_unlock(&priv->mib_lock); -+ schedule_delayed_work(&priv->mib_work, -+ msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY)); -+} -+ -+static int -+ar8xxx_mib_init(struct ar8xxx_priv *priv) -+{ -+ unsigned int len; -+ -+ if (!ar8xxx_has_mib_counters(priv)) -+ return 0; -+ -+ BUG_ON(!priv->chip->mib_decs || !priv->chip->num_mibs); -+ -+ len = priv->dev.ports * priv->chip->num_mibs * -+ sizeof(*priv->mib_stats); -+ priv->mib_stats = kzalloc(len, GFP_KERNEL); -+ -+ if (!priv->mib_stats) -+ return -ENOMEM; -+ -+ return 0; -+} -+ -+static void -+ar8xxx_mib_start(struct ar8xxx_priv *priv) -+{ -+ if (!ar8xxx_has_mib_counters(priv)) -+ return; -+ -+ schedule_delayed_work(&priv->mib_work, -+ msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY)); -+} -+ -+static void -+ar8xxx_mib_stop(struct ar8xxx_priv *priv) -+{ -+ if (!ar8xxx_has_mib_counters(priv)) -+ return; -+ -+ cancel_delayed_work(&priv->mib_work); -+} -+ -+static struct ar8xxx_priv * -+ar8xxx_create(void) -+{ -+ struct ar8xxx_priv *priv; -+ -+ priv = kzalloc(sizeof(struct ar8xxx_priv), GFP_KERNEL); -+ if (priv == NULL) -+ return NULL; -+ -+ mutex_init(&priv->reg_mutex); -+ mutex_init(&priv->mib_lock); -+ INIT_DELAYED_WORK(&priv->mib_work, ar8xxx_mib_work_func); -+ -+ return priv; -+} -+ -+static void -+ar8xxx_free(struct ar8xxx_priv *priv) -+{ -+ if (priv->chip && priv->chip->cleanup) -+ priv->chip->cleanup(priv); -+ -+ kfree(priv->mib_stats); -+ kfree(priv); -+} -+ -+static struct ar8xxx_priv * -+ar8xxx_create_mii(struct mii_bus *bus) -+{ -+ struct ar8xxx_priv *priv; -+ -+ priv = ar8xxx_create(); -+ if (priv) { -+ priv->mii_bus = bus; -+ priv->read = ar8xxx_mii_read; -+ priv->write = ar8xxx_mii_write; -+ priv->rmw = ar8xxx_mii_rmw; -+ } -+ -+ return priv; -+} -+ -+static int -+ar8xxx_probe_switch(struct ar8xxx_priv *priv) -+{ -+ struct switch_dev *swdev; -+ int ret; -+ -+ ret = ar8xxx_id_chip(priv); -+ if (ret) -+ return ret; -+ -+ swdev = &priv->dev; -+ swdev->cpu_port = AR8216_PORT_CPU; -+ swdev->ops = &ar8xxx_sw_ops; -+ -+ if (chip_is_ar8316(priv)) { -+ swdev->name = "Atheros AR8316"; -+ swdev->vlans = AR8X16_MAX_VLANS; -+ swdev->ports = AR8216_NUM_PORTS; -+ } else if (chip_is_ar8236(priv)) { -+ swdev->name = "Atheros AR8236"; -+ swdev->vlans = AR8216_NUM_VLANS; -+ swdev->ports = AR8216_NUM_PORTS; -+ } else if (chip_is_ar8327(priv)) { -+ swdev->name = "Atheros AR8327"; -+ swdev->vlans = AR8X16_MAX_VLANS; -+ swdev->ports = AR8327_NUM_PORTS; -+ swdev->ops = &ar8327_sw_ops; -+ } else if (chip_is_ar8337(priv)) { -+ swdev->name = "Atheros AR8337"; -+ swdev->vlans = AR8X16_MAX_VLANS; -+ swdev->ports = AR8327_NUM_PORTS; -+ swdev->ops = &ar8327_sw_ops; -+ } else { -+ swdev->name = "Atheros AR8216"; -+ swdev->vlans = AR8216_NUM_VLANS; -+ swdev->ports = AR8216_NUM_PORTS; -+ } -+ -+ ret = ar8xxx_mib_init(priv); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int -+ar8xxx_start(struct ar8xxx_priv *priv) -+{ -+ int ret; -+ -+ priv->init = true; -+ -+ ret = priv->chip->hw_init(priv); -+ if (ret) -+ return ret; -+ -+ ret = ar8xxx_sw_reset_switch(&priv->dev); -+ if (ret) -+ return ret; -+ -+ priv->init = false; -+ -+ ar8xxx_mib_start(priv); -+ -+ return 0; -+} -+ -+static int -+ar8xxx_phy_config_init(struct phy_device *phydev) -+{ -+ struct ar8xxx_priv *priv = phydev->priv; -+ struct net_device *dev = phydev->attached_dev; -+ int ret; -+ -+ if (WARN_ON(!priv)) -+ return -ENODEV; -+ -+ if (chip_is_ar8327(priv) || chip_is_ar8337(priv)) -+ return 0; -+ -+ priv->phy = phydev; -+ -+ if (phydev->addr != 0) { -+ if (chip_is_ar8316(priv)) { -+ /* switch device has been initialized, reinit */ -+ priv->dev.ports = (AR8216_NUM_PORTS - 1); -+ priv->initialized = false; -+ priv->port4_phy = true; -+ ar8316_hw_init(priv); -+ return 0; -+ } -+ -+ return 0; -+ } -+ -+ ret = ar8xxx_start(priv); -+ if (ret) -+ return ret; -+ -+ /* VID fixup only needed on ar8216 */ -+ if (chip_is_ar8216(priv)) { -+ dev->phy_ptr = priv; -+ dev->priv_flags |= IFF_NO_IP_ALIGN; -+ dev->eth_mangle_rx = ar8216_mangle_rx; -+ dev->eth_mangle_tx = ar8216_mangle_tx; -+ } -+ -+ return 0; -+} -+ -+static int -+ar8xxx_phy_read_status(struct phy_device *phydev) -+{ -+ struct ar8xxx_priv *priv = phydev->priv; -+ struct switch_port_link link; -+ int ret; -+ -+ if (phydev->addr != 0) -+ return genphy_read_status(phydev); -+ -+ ar8216_read_port_link(priv, phydev->addr, &link); -+ phydev->link = !!link.link; -+ if (!phydev->link) -+ return 0; -+ -+ switch (link.speed) { -+ case SWITCH_PORT_SPEED_10: -+ phydev->speed = SPEED_10; -+ break; -+ case SWITCH_PORT_SPEED_100: -+ phydev->speed = SPEED_100; -+ break; -+ case SWITCH_PORT_SPEED_1000: -+ phydev->speed = SPEED_1000; -+ break; -+ default: -+ phydev->speed = 0; -+ } -+ phydev->duplex = link.duplex ? DUPLEX_FULL : DUPLEX_HALF; -+ -+ /* flush the address translation unit */ -+ mutex_lock(&priv->reg_mutex); -+ ret = priv->chip->atu_flush(priv); -+ mutex_unlock(&priv->reg_mutex); -+ -+ phydev->state = PHY_RUNNING; -+ netif_carrier_on(phydev->attached_dev); -+ phydev->adjust_link(phydev->attached_dev); -+ -+ return ret; -+} -+ -+static int -+ar8xxx_phy_config_aneg(struct phy_device *phydev) -+{ -+ if (phydev->addr == 0) -+ return 0; -+ -+ return genphy_config_aneg(phydev); -+} -+ -+static const u32 ar8xxx_phy_ids[] = { -+ 0x004dd033, -+ 0x004dd034, /* AR8327 */ -+ 0x004dd036, /* AR8337 */ -+ 0x004dd041, -+ 0x004dd042, -+}; -+ -+static bool -+ar8xxx_phy_match(u32 phy_id) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(ar8xxx_phy_ids); i++) -+ if (phy_id == ar8xxx_phy_ids[i]) -+ return true; -+ -+ return false; -+} -+ -+static bool -+ar8xxx_is_possible(struct mii_bus *bus) -+{ -+ unsigned i; -+ -+ for (i = 0; i < 4; i++) { -+ u32 phy_id; -+ -+ phy_id = mdiobus_read(bus, i, MII_PHYSID1) << 16; -+ phy_id |= mdiobus_read(bus, i, MII_PHYSID2); -+ if (!ar8xxx_phy_match(phy_id)) { -+ pr_debug("ar8xxx: unknown PHY at %s:%02x id:%08x\n", -+ dev_name(&bus->dev), i, phy_id); -+ return false; -+ } -+ } -+ -+ return true; -+} -+ -+static int -+ar8xxx_phy_probe(struct phy_device *phydev) -+{ -+ struct ar8xxx_priv *priv; -+ struct switch_dev *swdev; -+ int ret; -+ -+ /* skip PHYs at unused adresses */ -+ if (phydev->addr != 0 && phydev->addr != 4) -+ return -ENODEV; -+ -+ if (!ar8xxx_is_possible(phydev->bus)) -+ return -ENODEV; -+ -+ mutex_lock(&ar8xxx_dev_list_lock); -+ list_for_each_entry(priv, &ar8xxx_dev_list, list) -+ if (priv->mii_bus == phydev->bus) -+ goto found; -+ -+ priv = ar8xxx_create_mii(phydev->bus); -+ if (priv == NULL) { -+ ret = -ENOMEM; -+ goto unlock; -+ } -+ -+ ret = ar8xxx_probe_switch(priv); -+ if (ret) -+ goto free_priv; -+ -+ swdev = &priv->dev; -+ swdev->alias = dev_name(&priv->mii_bus->dev); -+ ret = register_switch(swdev, NULL); -+ if (ret) -+ goto free_priv; -+ -+ pr_info("%s: %s rev. %u switch registered on %s\n", -+ swdev->devname, swdev->name, priv->chip_rev, -+ dev_name(&priv->mii_bus->dev)); -+ -+found: -+ priv->use_count++; -+ -+ if (phydev->addr == 0) { -+ if (ar8xxx_has_gige(priv)) { -+ phydev->supported = SUPPORTED_1000baseT_Full; -+ phydev->advertising = ADVERTISED_1000baseT_Full; -+ } else { -+ phydev->supported = SUPPORTED_100baseT_Full; -+ phydev->advertising = ADVERTISED_100baseT_Full; -+ } -+ -+ if (chip_is_ar8327(priv) || chip_is_ar8337(priv)) { -+ priv->phy = phydev; -+ -+ ret = ar8xxx_start(priv); -+ if (ret) -+ goto err_unregister_switch; -+ } -+ } else { -+ if (ar8xxx_has_gige(priv)) { -+ phydev->supported |= SUPPORTED_1000baseT_Full; -+ phydev->advertising |= ADVERTISED_1000baseT_Full; -+ } -+ } -+ -+ phydev->priv = priv; -+ -+ list_add(&priv->list, &ar8xxx_dev_list); -+ -+ mutex_unlock(&ar8xxx_dev_list_lock); -+ -+ return 0; -+ -+err_unregister_switch: -+ if (--priv->use_count) -+ goto unlock; -+ -+ unregister_switch(&priv->dev); -+ -+free_priv: -+ ar8xxx_free(priv); -+unlock: -+ mutex_unlock(&ar8xxx_dev_list_lock); -+ return ret; -+} -+ -+static void -+ar8xxx_phy_detach(struct phy_device *phydev) -+{ -+ struct net_device *dev = phydev->attached_dev; -+ -+ if (!dev) -+ return; -+ -+ dev->phy_ptr = NULL; -+ dev->priv_flags &= ~IFF_NO_IP_ALIGN; -+ dev->eth_mangle_rx = NULL; -+ dev->eth_mangle_tx = NULL; -+} -+ -+static void -+ar8xxx_phy_remove(struct phy_device *phydev) -+{ -+ struct ar8xxx_priv *priv = phydev->priv; -+ -+ if (WARN_ON(!priv)) -+ return; -+ -+ phydev->priv = NULL; -+ if (--priv->use_count > 0) -+ return; -+ -+ mutex_lock(&ar8xxx_dev_list_lock); -+ list_del(&priv->list); -+ mutex_unlock(&ar8xxx_dev_list_lock); -+ -+ unregister_switch(&priv->dev); -+ ar8xxx_mib_stop(priv); -+ ar8xxx_free(priv); -+} -+ -+static struct phy_driver ar8xxx_phy_driver = { -+ .phy_id = 0x004d0000, -+ .name = "Atheros AR8216/AR8236/AR8316", -+ .phy_id_mask = 0xffff0000, -+ .features = PHY_BASIC_FEATURES, -+ .probe = ar8xxx_phy_probe, -+ .remove = ar8xxx_phy_remove, -+ .detach = ar8xxx_phy_detach, -+ .config_init = ar8xxx_phy_config_init, -+ .config_aneg = ar8xxx_phy_config_aneg, -+ .read_status = ar8xxx_phy_read_status, -+ .driver = { .owner = THIS_MODULE }, -+}; -+ -+int __init -+ar8xxx_init(void) -+{ -+ return phy_driver_register(&ar8xxx_phy_driver); -+} -+ -+void __exit -+ar8xxx_exit(void) -+{ -+ phy_driver_unregister(&ar8xxx_phy_driver); -+} -+ -+module_init(ar8xxx_init); -+module_exit(ar8xxx_exit); -+MODULE_LICENSE("GPL"); -+ -diff --git a/drivers/net/phy/ar8216.h b/drivers/net/phy/ar8216.h -new file mode 100644 -index 0000000..00d6d7f ---- /dev/null -+++ b/drivers/net/phy/ar8216.h -@@ -0,0 +1,492 @@ -+/* -+ * ar8216.h: AR8216 switch driver -+ * -+ * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2 -+ * of the License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef __AR8216_H -+#define __AR8216_H -+ -+#define BITS(_s, _n) (((1UL << (_n)) - 1) << _s) -+ -+#define AR8216_PORT_CPU 0 -+#define AR8216_NUM_PORTS 6 -+#define AR8216_NUM_VLANS 16 -+#define AR8316_NUM_VLANS 4096 -+ -+/* Atheros specific MII registers */ -+#define MII_ATH_MMD_ADDR 0x0d -+#define MII_ATH_MMD_DATA 0x0e -+#define MII_ATH_DBG_ADDR 0x1d -+#define MII_ATH_DBG_DATA 0x1e -+ -+#define AR8216_REG_CTRL 0x0000 -+#define AR8216_CTRL_REVISION BITS(0, 8) -+#define AR8216_CTRL_REVISION_S 0 -+#define AR8216_CTRL_VERSION BITS(8, 8) -+#define AR8216_CTRL_VERSION_S 8 -+#define AR8216_CTRL_RESET BIT(31) -+ -+#define AR8216_REG_FLOOD_MASK 0x002C -+#define AR8216_FM_UNI_DEST_PORTS BITS(0, 6) -+#define AR8216_FM_MULTI_DEST_PORTS BITS(16, 6) -+ -+#define AR8216_REG_GLOBAL_CTRL 0x0030 -+#define AR8216_GCTRL_MTU BITS(0, 11) -+#define AR8236_GCTRL_MTU BITS(0, 14) -+#define AR8316_GCTRL_MTU BITS(0, 14) -+ -+#define AR8216_REG_VTU 0x0040 -+#define AR8216_VTU_OP BITS(0, 3) -+#define AR8216_VTU_OP_NOOP 0x0 -+#define AR8216_VTU_OP_FLUSH 0x1 -+#define AR8216_VTU_OP_LOAD 0x2 -+#define AR8216_VTU_OP_PURGE 0x3 -+#define AR8216_VTU_OP_REMOVE_PORT 0x4 -+#define AR8216_VTU_ACTIVE BIT(3) -+#define AR8216_VTU_FULL BIT(4) -+#define AR8216_VTU_PORT BITS(8, 4) -+#define AR8216_VTU_PORT_S 8 -+#define AR8216_VTU_VID BITS(16, 12) -+#define AR8216_VTU_VID_S 16 -+#define AR8216_VTU_PRIO BITS(28, 3) -+#define AR8216_VTU_PRIO_S 28 -+#define AR8216_VTU_PRIO_EN BIT(31) -+ -+#define AR8216_REG_VTU_DATA 0x0044 -+#define AR8216_VTUDATA_MEMBER BITS(0, 10) -+#define AR8236_VTUDATA_MEMBER BITS(0, 7) -+#define AR8216_VTUDATA_VALID BIT(11) -+ -+#define AR8216_REG_ATU 0x0050 -+#define AR8216_ATU_OP BITS(0, 3) -+#define AR8216_ATU_OP_NOOP 0x0 -+#define AR8216_ATU_OP_FLUSH 0x1 -+#define AR8216_ATU_OP_LOAD 0x2 -+#define AR8216_ATU_OP_PURGE 0x3 -+#define AR8216_ATU_OP_FLUSH_LOCKED 0x4 -+#define AR8216_ATU_OP_FLUSH_UNICAST 0x5 -+#define AR8216_ATU_OP_GET_NEXT 0x6 -+#define AR8216_ATU_ACTIVE BIT(3) -+#define AR8216_ATU_PORT_NUM BITS(8, 4) -+#define AR8216_ATU_FULL_VIO BIT(12) -+#define AR8216_ATU_ADDR4 BITS(16, 8) -+#define AR8216_ATU_ADDR5 BITS(24, 8) -+ -+#define AR8216_REG_ATU_DATA 0x0054 -+#define AR8216_ATU_ADDR3 BITS(0, 8) -+#define AR8216_ATU_ADDR2 BITS(8, 8) -+#define AR8216_ATU_ADDR1 BITS(16, 8) -+#define AR8216_ATU_ADDR0 BITS(24, 8) -+ -+#define AR8216_REG_ATU_CTRL 0x005C -+#define AR8216_ATU_CTRL_AGE_EN BIT(17) -+#define AR8216_ATU_CTRL_AGE_TIME BITS(0, 16) -+#define AR8216_ATU_CTRL_AGE_TIME_S 0 -+ -+#define AR8216_REG_MIB_FUNC 0x0080 -+#define AR8216_MIB_TIMER BITS(0, 16) -+#define AR8216_MIB_AT_HALF_EN BIT(16) -+#define AR8216_MIB_BUSY BIT(17) -+#define AR8216_MIB_FUNC BITS(24, 3) -+#define AR8216_MIB_FUNC_S 24 -+#define AR8216_MIB_FUNC_NO_OP 0x0 -+#define AR8216_MIB_FUNC_FLUSH 0x1 -+#define AR8216_MIB_FUNC_CAPTURE 0x3 -+#define AR8236_MIB_EN BIT(30) -+ -+#define AR8216_REG_GLOBAL_CPUPORT 0x0078 -+#define AR8216_GLOBAL_CPUPORT_MIRROR_PORT BITS(4, 4) -+#define AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S 4 -+ -+#define AR8216_PORT_OFFSET(_i) (0x0100 * (_i + 1)) -+#define AR8216_REG_PORT_STATUS(_i) (AR8216_PORT_OFFSET(_i) + 0x0000) -+#define AR8216_PORT_STATUS_SPEED BITS(0,2) -+#define AR8216_PORT_STATUS_SPEED_S 0 -+#define AR8216_PORT_STATUS_TXMAC BIT(2) -+#define AR8216_PORT_STATUS_RXMAC BIT(3) -+#define AR8216_PORT_STATUS_TXFLOW BIT(4) -+#define AR8216_PORT_STATUS_RXFLOW BIT(5) -+#define AR8216_PORT_STATUS_DUPLEX BIT(6) -+#define AR8216_PORT_STATUS_LINK_UP BIT(8) -+#define AR8216_PORT_STATUS_LINK_AUTO BIT(9) -+#define AR8216_PORT_STATUS_LINK_PAUSE BIT(10) -+ -+#define AR8216_REG_PORT_CTRL(_i) (AR8216_PORT_OFFSET(_i) + 0x0004) -+ -+/* port forwarding state */ -+#define AR8216_PORT_CTRL_STATE BITS(0, 3) -+#define AR8216_PORT_CTRL_STATE_S 0 -+ -+#define AR8216_PORT_CTRL_LEARN_LOCK BIT(7) -+ -+/* egress 802.1q mode */ -+#define AR8216_PORT_CTRL_VLAN_MODE BITS(8, 2) -+#define AR8216_PORT_CTRL_VLAN_MODE_S 8 -+ -+#define AR8216_PORT_CTRL_IGMP_SNOOP BIT(10) -+#define AR8216_PORT_CTRL_HEADER BIT(11) -+#define AR8216_PORT_CTRL_MAC_LOOP BIT(12) -+#define AR8216_PORT_CTRL_SINGLE_VLAN BIT(13) -+#define AR8216_PORT_CTRL_LEARN BIT(14) -+#define AR8216_PORT_CTRL_MIRROR_TX BIT(16) -+#define AR8216_PORT_CTRL_MIRROR_RX BIT(17) -+ -+#define AR8216_REG_PORT_VLAN(_i) (AR8216_PORT_OFFSET(_i) + 0x0008) -+ -+#define AR8216_PORT_VLAN_DEFAULT_ID BITS(0, 12) -+#define AR8216_PORT_VLAN_DEFAULT_ID_S 0 -+ -+#define AR8216_PORT_VLAN_DEST_PORTS BITS(16, 9) -+#define AR8216_PORT_VLAN_DEST_PORTS_S 16 -+ -+/* bit0 added to the priority field of egress frames */ -+#define AR8216_PORT_VLAN_TX_PRIO BIT(27) -+ -+/* port default priority */ -+#define AR8216_PORT_VLAN_PRIORITY BITS(28, 2) -+#define AR8216_PORT_VLAN_PRIORITY_S 28 -+ -+/* ingress 802.1q mode */ -+#define AR8216_PORT_VLAN_MODE BITS(30, 2) -+#define AR8216_PORT_VLAN_MODE_S 30 -+ -+#define AR8216_REG_PORT_RATE(_i) (AR8216_PORT_OFFSET(_i) + 0x000c) -+#define AR8216_REG_PORT_PRIO(_i) (AR8216_PORT_OFFSET(_i) + 0x0010) -+ -+#define AR8216_REG_PORT_STATS_BASE(_i) (0x19000 + (_i) * 0xa0) -+ -+#define AR8216_STATS_RXBROAD 0x00 -+#define AR8216_STATS_RXPAUSE 0x04 -+#define AR8216_STATS_RXMULTI 0x08 -+#define AR8216_STATS_RXFCSERR 0x0c -+#define AR8216_STATS_RXALIGNERR 0x10 -+#define AR8216_STATS_RXRUNT 0x14 -+#define AR8216_STATS_RXFRAGMENT 0x18 -+#define AR8216_STATS_RX64BYTE 0x1c -+#define AR8216_STATS_RX128BYTE 0x20 -+#define AR8216_STATS_RX256BYTE 0x24 -+#define AR8216_STATS_RX512BYTE 0x28 -+#define AR8216_STATS_RX1024BYTE 0x2c -+#define AR8216_STATS_RXMAXBYTE 0x30 -+#define AR8216_STATS_RXTOOLONG 0x34 -+#define AR8216_STATS_RXGOODBYTE 0x38 -+#define AR8216_STATS_RXBADBYTE 0x40 -+#define AR8216_STATS_RXOVERFLOW 0x48 -+#define AR8216_STATS_FILTERED 0x4c -+#define AR8216_STATS_TXBROAD 0x50 -+#define AR8216_STATS_TXPAUSE 0x54 -+#define AR8216_STATS_TXMULTI 0x58 -+#define AR8216_STATS_TXUNDERRUN 0x5c -+#define AR8216_STATS_TX64BYTE 0x60 -+#define AR8216_STATS_TX128BYTE 0x64 -+#define AR8216_STATS_TX256BYTE 0x68 -+#define AR8216_STATS_TX512BYTE 0x6c -+#define AR8216_STATS_TX1024BYTE 0x70 -+#define AR8216_STATS_TXMAXBYTE 0x74 -+#define AR8216_STATS_TXOVERSIZE 0x78 -+#define AR8216_STATS_TXBYTE 0x7c -+#define AR8216_STATS_TXCOLLISION 0x84 -+#define AR8216_STATS_TXABORTCOL 0x88 -+#define AR8216_STATS_TXMULTICOL 0x8c -+#define AR8216_STATS_TXSINGLECOL 0x90 -+#define AR8216_STATS_TXEXCDEFER 0x94 -+#define AR8216_STATS_TXDEFER 0x98 -+#define AR8216_STATS_TXLATECOL 0x9c -+ -+#define AR8236_REG_PORT_VLAN(_i) (AR8216_PORT_OFFSET((_i)) + 0x0008) -+#define AR8236_PORT_VLAN_DEFAULT_ID BITS(16, 12) -+#define AR8236_PORT_VLAN_DEFAULT_ID_S 16 -+#define AR8236_PORT_VLAN_PRIORITY BITS(29, 3) -+#define AR8236_PORT_VLAN_PRIORITY_S 28 -+ -+#define AR8236_REG_PORT_VLAN2(_i) (AR8216_PORT_OFFSET((_i)) + 0x000c) -+#define AR8236_PORT_VLAN2_MEMBER BITS(16, 7) -+#define AR8236_PORT_VLAN2_MEMBER_S 16 -+#define AR8236_PORT_VLAN2_TX_PRIO BIT(23) -+#define AR8236_PORT_VLAN2_VLAN_MODE BITS(30, 2) -+#define AR8236_PORT_VLAN2_VLAN_MODE_S 30 -+ -+#define AR8236_REG_PORT_STATS_BASE(_i) (0x20000 + (_i) * 0x100) -+ -+#define AR8236_STATS_RXBROAD 0x00 -+#define AR8236_STATS_RXPAUSE 0x04 -+#define AR8236_STATS_RXMULTI 0x08 -+#define AR8236_STATS_RXFCSERR 0x0c -+#define AR8236_STATS_RXALIGNERR 0x10 -+#define AR8236_STATS_RXRUNT 0x14 -+#define AR8236_STATS_RXFRAGMENT 0x18 -+#define AR8236_STATS_RX64BYTE 0x1c -+#define AR8236_STATS_RX128BYTE 0x20 -+#define AR8236_STATS_RX256BYTE 0x24 -+#define AR8236_STATS_RX512BYTE 0x28 -+#define AR8236_STATS_RX1024BYTE 0x2c -+#define AR8236_STATS_RX1518BYTE 0x30 -+#define AR8236_STATS_RXMAXBYTE 0x34 -+#define AR8236_STATS_RXTOOLONG 0x38 -+#define AR8236_STATS_RXGOODBYTE 0x3c -+#define AR8236_STATS_RXBADBYTE 0x44 -+#define AR8236_STATS_RXOVERFLOW 0x4c -+#define AR8236_STATS_FILTERED 0x50 -+#define AR8236_STATS_TXBROAD 0x54 -+#define AR8236_STATS_TXPAUSE 0x58 -+#define AR8236_STATS_TXMULTI 0x5c -+#define AR8236_STATS_TXUNDERRUN 0x60 -+#define AR8236_STATS_TX64BYTE 0x64 -+#define AR8236_STATS_TX128BYTE 0x68 -+#define AR8236_STATS_TX256BYTE 0x6c -+#define AR8236_STATS_TX512BYTE 0x70 -+#define AR8236_STATS_TX1024BYTE 0x74 -+#define AR8236_STATS_TX1518BYTE 0x78 -+#define AR8236_STATS_TXMAXBYTE 0x7c -+#define AR8236_STATS_TXOVERSIZE 0x80 -+#define AR8236_STATS_TXBYTE 0x84 -+#define AR8236_STATS_TXCOLLISION 0x8c -+#define AR8236_STATS_TXABORTCOL 0x90 -+#define AR8236_STATS_TXMULTICOL 0x94 -+#define AR8236_STATS_TXSINGLECOL 0x98 -+#define AR8236_STATS_TXEXCDEFER 0x9c -+#define AR8236_STATS_TXDEFER 0xa0 -+#define AR8236_STATS_TXLATECOL 0xa4 -+ -+#define AR8316_REG_POSTRIP 0x0008 -+#define AR8316_POSTRIP_MAC0_GMII_EN BIT(0) -+#define AR8316_POSTRIP_MAC0_RGMII_EN BIT(1) -+#define AR8316_POSTRIP_PHY4_GMII_EN BIT(2) -+#define AR8316_POSTRIP_PHY4_RGMII_EN BIT(3) -+#define AR8316_POSTRIP_MAC0_MAC_MODE BIT(4) -+#define AR8316_POSTRIP_RTL_MODE BIT(5) -+#define AR8316_POSTRIP_RGMII_RXCLK_DELAY_EN BIT(6) -+#define AR8316_POSTRIP_RGMII_TXCLK_DELAY_EN BIT(7) -+#define AR8316_POSTRIP_SERDES_EN BIT(8) -+#define AR8316_POSTRIP_SEL_ANA_RST BIT(9) -+#define AR8316_POSTRIP_GATE_25M_EN BIT(10) -+#define AR8316_POSTRIP_SEL_CLK25M BIT(11) -+#define AR8316_POSTRIP_HIB_PULSE_HW BIT(12) -+#define AR8316_POSTRIP_DBG_MODE_I BIT(13) -+#define AR8316_POSTRIP_MAC5_MAC_MODE BIT(14) -+#define AR8316_POSTRIP_MAC5_PHY_MODE BIT(15) -+#define AR8316_POSTRIP_POWER_DOWN_HW BIT(16) -+#define AR8316_POSTRIP_LPW_STATE_EN BIT(17) -+#define AR8316_POSTRIP_MAN_EN BIT(18) -+#define AR8316_POSTRIP_PHY_PLL_ON BIT(19) -+#define AR8316_POSTRIP_LPW_EXIT BIT(20) -+#define AR8316_POSTRIP_TXDELAY_S0 BIT(21) -+#define AR8316_POSTRIP_TXDELAY_S1 BIT(22) -+#define AR8316_POSTRIP_RXDELAY_S0 BIT(23) -+#define AR8316_POSTRIP_LED_OPEN_EN BIT(24) -+#define AR8316_POSTRIP_SPI_EN BIT(25) -+#define AR8316_POSTRIP_RXDELAY_S1 BIT(26) -+#define AR8316_POSTRIP_POWER_ON_SEL BIT(31) -+ -+#define AR8327_NUM_PORTS 7 -+#define AR8327_NUM_LEDS 15 -+#define AR8327_NUM_PHYS 5 -+#define AR8327_PORTS_ALL 0x7f -+#define AR8327_NUM_LED_CTRL_REGS 4 -+ -+#define AR8327_REG_MASK 0x000 -+ -+#define AR8327_REG_PAD0_MODE 0x004 -+#define AR8327_REG_PAD5_MODE 0x008 -+#define AR8327_REG_PAD6_MODE 0x00c -+#define AR8327_PAD_MAC_MII_RXCLK_SEL BIT(0) -+#define AR8327_PAD_MAC_MII_TXCLK_SEL BIT(1) -+#define AR8327_PAD_MAC_MII_EN BIT(2) -+#define AR8327_PAD_MAC_GMII_RXCLK_SEL BIT(4) -+#define AR8327_PAD_MAC_GMII_TXCLK_SEL BIT(5) -+#define AR8327_PAD_MAC_GMII_EN BIT(6) -+#define AR8327_PAD_SGMII_EN BIT(7) -+#define AR8327_PAD_PHY_MII_RXCLK_SEL BIT(8) -+#define AR8327_PAD_PHY_MII_TXCLK_SEL BIT(9) -+#define AR8327_PAD_PHY_MII_EN BIT(10) -+#define AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL BIT(11) -+#define AR8327_PAD_PHY_GMII_RXCLK_SEL BIT(12) -+#define AR8327_PAD_PHY_GMII_TXCLK_SEL BIT(13) -+#define AR8327_PAD_PHY_GMII_EN BIT(14) -+#define AR8327_PAD_PHYX_GMII_EN BIT(16) -+#define AR8327_PAD_PHYX_RGMII_EN BIT(17) -+#define AR8327_PAD_PHYX_MII_EN BIT(18) -+#define AR8327_PAD_SGMII_DELAY_EN BIT(19) -+#define AR8327_PAD_RGMII_RXCLK_DELAY_SEL BITS(20, 2) -+#define AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S 20 -+#define AR8327_PAD_RGMII_TXCLK_DELAY_SEL BITS(22, 2) -+#define AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S 22 -+#define AR8327_PAD_RGMII_RXCLK_DELAY_EN BIT(24) -+#define AR8327_PAD_RGMII_TXCLK_DELAY_EN BIT(25) -+#define AR8327_PAD_RGMII_EN BIT(26) -+ -+#define AR8327_REG_POWER_ON_STRIP 0x010 -+#define AR8327_POWER_ON_STRIP_POWER_ON_SEL BIT(31) -+#define AR8327_POWER_ON_STRIP_LED_OPEN_EN BIT(24) -+#define AR8327_POWER_ON_STRIP_SERDES_AEN BIT(7) -+ -+#define AR8327_REG_INT_STATUS0 0x020 -+#define AR8327_INT0_VT_DONE BIT(20) -+ -+#define AR8327_REG_INT_STATUS1 0x024 -+#define AR8327_REG_INT_MASK0 0x028 -+#define AR8327_REG_INT_MASK1 0x02c -+ -+#define AR8327_REG_MODULE_EN 0x030 -+#define AR8327_MODULE_EN_MIB BIT(0) -+ -+#define AR8327_REG_MIB_FUNC 0x034 -+#define AR8327_MIB_CPU_KEEP BIT(20) -+ -+#define AR8327_REG_SERVICE_TAG 0x048 -+#define AR8327_REG_LED_CTRL(_i) (0x050 + (_i) * 4) -+#define AR8327_REG_LED_CTRL0 0x050 -+#define AR8327_REG_LED_CTRL1 0x054 -+#define AR8327_REG_LED_CTRL2 0x058 -+#define AR8327_REG_LED_CTRL3 0x05c -+#define AR8327_REG_MAC_ADDR0 0x060 -+#define AR8327_REG_MAC_ADDR1 0x064 -+ -+#define AR8327_REG_MAX_FRAME_SIZE 0x078 -+#define AR8327_MAX_FRAME_SIZE_MTU BITS(0, 14) -+ -+#define AR8327_REG_PORT_STATUS(_i) (0x07c + (_i) * 4) -+ -+#define AR8327_REG_HEADER_CTRL 0x098 -+#define AR8327_REG_PORT_HEADER(_i) (0x09c + (_i) * 4) -+ -+#define AR8327_REG_SGMII_CTRL 0x0e0 -+#define AR8327_SGMII_CTRL_EN_PLL BIT(1) -+#define AR8327_SGMII_CTRL_EN_RX BIT(2) -+#define AR8327_SGMII_CTRL_EN_TX BIT(3) -+ -+#define AR8327_REG_PORT_VLAN0(_i) (0x420 + (_i) * 0x8) -+#define AR8327_PORT_VLAN0_DEF_SVID BITS(0, 12) -+#define AR8327_PORT_VLAN0_DEF_SVID_S 0 -+#define AR8327_PORT_VLAN0_DEF_CVID BITS(16, 12) -+#define AR8327_PORT_VLAN0_DEF_CVID_S 16 -+ -+#define AR8327_REG_PORT_VLAN1(_i) (0x424 + (_i) * 0x8) -+#define AR8327_PORT_VLAN1_PORT_VLAN_PROP BIT(6) -+#define AR8327_PORT_VLAN1_OUT_MODE BITS(12, 2) -+#define AR8327_PORT_VLAN1_OUT_MODE_S 12 -+#define AR8327_PORT_VLAN1_OUT_MODE_UNMOD 0 -+#define AR8327_PORT_VLAN1_OUT_MODE_UNTAG 1 -+#define AR8327_PORT_VLAN1_OUT_MODE_TAG 2 -+#define AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH 3 -+ -+#define AR8327_REG_ATU_DATA0 0x600 -+#define AR8327_REG_ATU_DATA1 0x604 -+#define AR8327_REG_ATU_DATA2 0x608 -+ -+#define AR8327_REG_ATU_FUNC 0x60c -+#define AR8327_ATU_FUNC_OP BITS(0, 4) -+#define AR8327_ATU_FUNC_OP_NOOP 0x0 -+#define AR8327_ATU_FUNC_OP_FLUSH 0x1 -+#define AR8327_ATU_FUNC_OP_LOAD 0x2 -+#define AR8327_ATU_FUNC_OP_PURGE 0x3 -+#define AR8327_ATU_FUNC_OP_FLUSH_LOCKED 0x4 -+#define AR8327_ATU_FUNC_OP_FLUSH_UNICAST 0x5 -+#define AR8327_ATU_FUNC_OP_GET_NEXT 0x6 -+#define AR8327_ATU_FUNC_OP_SEARCH_MAC 0x7 -+#define AR8327_ATU_FUNC_OP_CHANGE_TRUNK 0x8 -+#define AR8327_ATU_FUNC_BUSY BIT(31) -+ -+#define AR8327_REG_VTU_FUNC0 0x0610 -+#define AR8327_VTU_FUNC0_EG_MODE BITS(4, 14) -+#define AR8327_VTU_FUNC0_EG_MODE_S(_i) (4 + (_i) * 2) -+#define AR8327_VTU_FUNC0_EG_MODE_KEEP 0 -+#define AR8327_VTU_FUNC0_EG_MODE_UNTAG 1 -+#define AR8327_VTU_FUNC0_EG_MODE_TAG 2 -+#define AR8327_VTU_FUNC0_EG_MODE_NOT 3 -+#define AR8327_VTU_FUNC0_IVL BIT(19) -+#define AR8327_VTU_FUNC0_VALID BIT(20) -+ -+#define AR8327_REG_VTU_FUNC1 0x0614 -+#define AR8327_VTU_FUNC1_OP BITS(0, 3) -+#define AR8327_VTU_FUNC1_OP_NOOP 0 -+#define AR8327_VTU_FUNC1_OP_FLUSH 1 -+#define AR8327_VTU_FUNC1_OP_LOAD 2 -+#define AR8327_VTU_FUNC1_OP_PURGE 3 -+#define AR8327_VTU_FUNC1_OP_REMOVE_PORT 4 -+#define AR8327_VTU_FUNC1_OP_GET_NEXT 5 -+#define AR8327_VTU_FUNC1_OP_GET_ONE 6 -+#define AR8327_VTU_FUNC1_FULL BIT(4) -+#define AR8327_VTU_FUNC1_PORT BIT(8, 4) -+#define AR8327_VTU_FUNC1_PORT_S 8 -+#define AR8327_VTU_FUNC1_VID BIT(16, 12) -+#define AR8327_VTU_FUNC1_VID_S 16 -+#define AR8327_VTU_FUNC1_BUSY BIT(31) -+ -+#define AR8327_REG_FWD_CTRL0 0x620 -+#define AR8327_FWD_CTRL0_CPU_PORT_EN BIT(10) -+#define AR8327_FWD_CTRL0_MIRROR_PORT BITS(4, 4) -+#define AR8327_FWD_CTRL0_MIRROR_PORT_S 4 -+ -+#define AR8327_REG_FWD_CTRL1 0x624 -+#define AR8327_FWD_CTRL1_UC_FLOOD BITS(0, 7) -+#define AR8327_FWD_CTRL1_UC_FLOOD_S 0 -+#define AR8327_FWD_CTRL1_MC_FLOOD BITS(8, 7) -+#define AR8327_FWD_CTRL1_MC_FLOOD_S 8 -+#define AR8327_FWD_CTRL1_BC_FLOOD BITS(16, 7) -+#define AR8327_FWD_CTRL1_BC_FLOOD_S 16 -+#define AR8327_FWD_CTRL1_IGMP BITS(24, 7) -+#define AR8327_FWD_CTRL1_IGMP_S 24 -+ -+#define AR8327_REG_PORT_LOOKUP(_i) (0x660 + (_i) * 0xc) -+#define AR8327_PORT_LOOKUP_MEMBER BITS(0, 7) -+#define AR8327_PORT_LOOKUP_IN_MODE BITS(8, 2) -+#define AR8327_PORT_LOOKUP_IN_MODE_S 8 -+#define AR8327_PORT_LOOKUP_STATE BITS(16, 3) -+#define AR8327_PORT_LOOKUP_STATE_S 16 -+#define AR8327_PORT_LOOKUP_LEARN BIT(20) -+#define AR8327_PORT_LOOKUP_ING_MIRROR_EN BIT(25) -+ -+#define AR8327_REG_PORT_PRIO(_i) (0x664 + (_i) * 0xc) -+ -+#define AR8327_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8) -+#define AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16) -+ -+#define AR8327_REG_PORT_STATS_BASE(_i) (0x1000 + (_i) * 0x100) -+ -+#define AR8337_PAD_MAC06_EXCHANGE_EN BIT(31) -+ -+/* port speed */ -+enum { -+ AR8216_PORT_SPEED_10M = 0, -+ AR8216_PORT_SPEED_100M = 1, -+ AR8216_PORT_SPEED_1000M = 2, -+ AR8216_PORT_SPEED_ERR = 3, -+}; -+ -+/* ingress 802.1q mode */ -+enum { -+ AR8216_IN_PORT_ONLY = 0, -+ AR8216_IN_PORT_FALLBACK = 1, -+ AR8216_IN_VLAN_ONLY = 2, -+ AR8216_IN_SECURE = 3 -+}; -+ -+/* egress 802.1q mode */ -+enum { -+ AR8216_OUT_KEEP = 0, -+ AR8216_OUT_STRIP_VLAN = 1, -+ AR8216_OUT_ADD_VLAN = 2 -+}; -+ -+/* port forwarding state */ -+enum { -+ AR8216_PORT_STATE_DISABLED = 0, -+ AR8216_PORT_STATE_BLOCK = 1, -+ AR8216_PORT_STATE_LISTEN = 2, -+ AR8216_PORT_STATE_LEARN = 3, -+ AR8216_PORT_STATE_FORWARD = 4 -+}; -+ -+#endif -diff --git a/include/linux/ar8216_platform.h b/include/linux/ar8216_platform.h -new file mode 100644 -index 0000000..4935ad3 ---- /dev/null -+++ b/include/linux/ar8216_platform.h -@@ -0,0 +1,131 @@ -+/* -+ * AR8216 switch driver platform data -+ * -+ * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2 -+ * of the License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#ifndef AR8216_PLATFORM_H -+#define AR8216_PLATFORM_H -+ -+enum ar8327_pad_mode { -+ AR8327_PAD_NC = 0, -+ AR8327_PAD_MAC2MAC_MII, -+ AR8327_PAD_MAC2MAC_GMII, -+ AR8327_PAD_MAC_SGMII, -+ AR8327_PAD_MAC2PHY_MII, -+ AR8327_PAD_MAC2PHY_GMII, -+ AR8327_PAD_MAC_RGMII, -+ AR8327_PAD_PHY_GMII, -+ AR8327_PAD_PHY_RGMII, -+ AR8327_PAD_PHY_MII, -+}; -+ -+enum ar8327_clk_delay_sel { -+ AR8327_CLK_DELAY_SEL0 = 0, -+ AR8327_CLK_DELAY_SEL1, -+ AR8327_CLK_DELAY_SEL2, -+ AR8327_CLK_DELAY_SEL3, -+}; -+ -+struct ar8327_pad_cfg { -+ enum ar8327_pad_mode mode; -+ bool rxclk_sel; -+ bool txclk_sel; -+ bool pipe_rxclk_sel; -+ bool txclk_delay_en; -+ bool rxclk_delay_en; -+ bool sgmii_delay_en; -+ enum ar8327_clk_delay_sel txclk_delay_sel; -+ enum ar8327_clk_delay_sel rxclk_delay_sel; -+}; -+ -+enum ar8327_port_speed { -+ AR8327_PORT_SPEED_10 = 0, -+ AR8327_PORT_SPEED_100, -+ AR8327_PORT_SPEED_1000, -+}; -+ -+struct ar8327_port_cfg { -+ int force_link:1; -+ enum ar8327_port_speed speed; -+ int txpause:1; -+ int rxpause:1; -+ int duplex:1; -+}; -+ -+struct ar8327_sgmii_cfg { -+ u32 sgmii_ctrl; -+ bool serdes_aen; -+}; -+ -+struct ar8327_led_cfg { -+ u32 led_ctrl0; -+ u32 led_ctrl1; -+ u32 led_ctrl2; -+ u32 led_ctrl3; -+ bool open_drain; -+}; -+ -+enum ar8327_led_num { -+ AR8327_LED_PHY0_0 = 0, -+ AR8327_LED_PHY0_1, -+ AR8327_LED_PHY0_2, -+ AR8327_LED_PHY1_0, -+ AR8327_LED_PHY1_1, -+ AR8327_LED_PHY1_2, -+ AR8327_LED_PHY2_0, -+ AR8327_LED_PHY2_1, -+ AR8327_LED_PHY2_2, -+ AR8327_LED_PHY3_0, -+ AR8327_LED_PHY3_1, -+ AR8327_LED_PHY3_2, -+ AR8327_LED_PHY4_0, -+ AR8327_LED_PHY4_1, -+ AR8327_LED_PHY4_2, -+}; -+ -+enum ar8327_led_mode { -+ AR8327_LED_MODE_HW = 0, -+ AR8327_LED_MODE_SW, -+}; -+ -+struct ar8327_led_info { -+ const char *name; -+ const char *default_trigger; -+ bool active_low; -+ enum ar8327_led_num led_num; -+ enum ar8327_led_mode mode; -+}; -+ -+#define AR8327_LED_INFO(_led, _mode, _name) { \ -+ .name = (_name), \ -+ .led_num = AR8327_LED_ ## _led, \ -+ .mode = AR8327_LED_MODE_ ## _mode \ -+} -+ -+struct ar8327_platform_data { -+ struct ar8327_pad_cfg *pad0_cfg; -+ struct ar8327_pad_cfg *pad5_cfg; -+ struct ar8327_pad_cfg *pad6_cfg; -+ struct ar8327_sgmii_cfg *sgmii_cfg; -+ struct ar8327_port_cfg port0_cfg; -+ struct ar8327_port_cfg port6_cfg; -+ struct ar8327_led_cfg *led_cfg; -+ -+ int (*get_port_link)(unsigned port); -+ -+ unsigned num_leds; -+ const struct ar8327_led_info *leds; -+}; -+ -+#endif /* AR8216_PLATFORM_H */ -\ No newline at end of file --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0016-phy-mdio-bitbang-ignore-TA-value.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0016-phy-mdio-bitbang-ignore-TA-value.patch deleted file mode 100644 index fe23f4912..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0016-phy-mdio-bitbang-ignore-TA-value.patch +++ /dev/null @@ -1,44 +0,0 @@ -From e73f7d9a658c7fc693a9b9c45a1f65c014dd6e40 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 01:17:38 +0200 -Subject: [PATCH] phy: mdio-bitbang: ignore TA value - -This is necessary on rb493g to make the kernel detect the second switch. ---- - drivers/net/phy/mdio-bitbang.c | 13 ++----------- - 1 file changed, 2 insertions(+), 11 deletions(-) - -diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c -index daec9b0..4fa2be0 100644 ---- a/drivers/net/phy/mdio-bitbang.c -+++ b/drivers/net/phy/mdio-bitbang.c -@@ -155,7 +155,7 @@ static int mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, u32 addr) - static int mdiobb_read(struct mii_bus *bus, int phy, int reg) - { - struct mdiobb_ctrl *ctrl = bus->priv; -- int ret, i; -+ int ret; - - if (reg & MII_ADDR_C45) { - reg = mdiobb_cmd_addr(ctrl, phy, reg); -@@ -165,16 +165,7 @@ static int mdiobb_read(struct mii_bus *bus, int phy, int reg) - - ctrl->ops->set_mdio_dir(ctrl, 0); - -- /* check the turnaround bit: the PHY should be driving it to zero */ -- if (mdiobb_get_bit(ctrl) != 0) { -- /* PHY didn't drive TA low -- flush any bits it -- * may be trying to send. -- */ -- for (i = 0; i < 32; i++) -- mdiobb_get_bit(ctrl); -- -- return 0xffff; -- } -+ mdiobb_get_bit(ctrl); - - ret = mdiobb_get_num(ctrl, 16); - mdiobb_get_bit(ctrl); --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0017-MIPS-ath79-fix-maximum-timeout.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0017-MIPS-ath79-fix-maximum-timeout.patch deleted file mode 100644 index 3ca02783d..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0017-MIPS-ath79-fix-maximum-timeout.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 54d01581baa903adb8515625d98652ed43efba36 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 01:21:59 +0200 -Subject: [PATCH] MIPS: ath79: fix maximum timeout - -If the userland tries to set a timeout higher than the max_timeout, then -we should fallback to max_timeout. - -Signed-off-by: John Crispin <blogic@openwrt.org> ---- - drivers/watchdog/ath79_wdt.c | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c -index 9fa1f69..bf26baf 100644 ---- a/drivers/watchdog/ath79_wdt.c -+++ b/drivers/watchdog/ath79_wdt.c -@@ -105,10 +105,14 @@ static inline void ath79_wdt_disable(void) - - static int ath79_wdt_set_timeout(int val) - { -- if (val < 1 || val > max_timeout) -+ if (val < 1) - return -EINVAL; - -- timeout = val; -+ if (val > max_timeout) -+ timeout = max_timeout; -+ else -+ timeout = val; -+ - ath79_wdt_keepalive(); - - return 0; --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0018-net-allow-PHY-drivers-to-insert-packet-mangle-hooks.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0018-net-allow-PHY-drivers-to-insert-packet-mangle-hooks.patch deleted file mode 100644 index 6a372c8f1..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0018-net-allow-PHY-drivers-to-insert-packet-mangle-hooks.patch +++ /dev/null @@ -1,211 +0,0 @@ -From ebca842041d737b7441748a17ffd535aab851fce Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 01:32:11 +0200 -Subject: [PATCH] net: allow PHY drivers to insert packet mangle hooks - ---- - include/linux/netdevice.h | 8 ++++++++ - include/linux/skbuff.h | 14 ++++---------- - include/uapi/linux/if.h | 1 + - net/Kconfig | 6 ++++++ - net/core/dev.c | 36 ++++++++++++++++++++++++++++-------- - net/core/skbuff.c | 17 +++++++++++++++++ - net/ethernet/eth.c | 6 ++++++ - 7 files changed, 70 insertions(+), 18 deletions(-) - -diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h -index 911718f..8e8dd46 100644 ---- a/include/linux/netdevice.h -+++ b/include/linux/netdevice.h -@@ -1245,6 +1245,11 @@ struct net_device { - const struct ethtool_ops *ethtool_ops; - const struct forwarding_accel_ops *fwd_ops; - -+#ifdef CONFIG_ETHERNET_PACKET_MANGLE -+ void (*eth_mangle_rx)(struct net_device *dev, struct sk_buff *skb); -+ struct sk_buff *(*eth_mangle_tx)(struct net_device *dev, struct sk_buff *skb); -+#endif -+ - /* Hardware header description */ - const struct header_ops *header_ops; - -@@ -1313,6 +1318,9 @@ struct net_device { - void *ax25_ptr; /* AX.25 specific data */ - struct wireless_dev *ieee80211_ptr; /* IEEE 802.11 specific data, - assign before registering */ -+#ifdef CONFIG_ETHERNET_PACKET_MANGLE -+ void *phy_ptr; /* PHY device specific data */ -+#endif - - /* - * Cache lines mostly used on receive path (including eth_type_trans()) -diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h -index 15ede6a..5530766 100644 ---- a/include/linux/skbuff.h -+++ b/include/linux/skbuff.h -@@ -1858,6 +1858,10 @@ static inline int pskb_trim(struct sk_buff *skb, unsigned int len) - return (len < skb->len) ? __pskb_trim(skb, len) : 0; - } - -+extern struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, -+ unsigned int length, gfp_t gfp); -+ -+ - /** - * pskb_trim_unique - remove end from a paged unique (not cloned) buffer - * @skb: buffer to alter -@@ -1966,16 +1970,6 @@ static inline struct sk_buff *dev_alloc_skb(unsigned int length) - } - - --static inline struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, -- unsigned int length, gfp_t gfp) --{ -- struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp); -- -- if (NET_IP_ALIGN && skb) -- skb_reserve(skb, NET_IP_ALIGN); -- return skb; --} -- - static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, - unsigned int length) - { -diff --git a/include/uapi/linux/if.h b/include/uapi/linux/if.h -index d758163..7ffa548 100644 ---- a/include/uapi/linux/if.h -+++ b/include/uapi/linux/if.h -@@ -84,6 +84,7 @@ - #define IFF_LIVE_ADDR_CHANGE 0x100000 /* device supports hardware address - * change when it's running */ - #define IFF_MACVLAN 0x200000 /* Macvlan device */ -+#define IFF_NO_IP_ALIGN 0x400000 /* do not ip-align allocated rx pkts */ - - - #define IF_GET_IFACE 0x0001 /* for querying only */ -diff --git a/net/Kconfig b/net/Kconfig -index e411046..970c52a 100644 ---- a/net/Kconfig -+++ b/net/Kconfig -@@ -24,6 +24,12 @@ menuconfig NET - - if NET - -+config ETHERNET_PACKET_MANGLE -+ bool -+ help -+ This option can be selected by phy drivers that need to mangle -+ packets going in or out of an ethernet device. -+ - config WANT_COMPAT_NETLINK_MESSAGES - bool - help -diff --git a/net/core/dev.c b/net/core/dev.c -index fccc195..2e0ba23 100644 ---- a/net/core/dev.c -+++ b/net/core/dev.c -@@ -2607,10 +2607,20 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, - if (!list_empty(&ptype_all)) - dev_queue_xmit_nit(skb, dev); - -- skb_len = skb->len; -- trace_net_dev_start_xmit(skb, dev); -- rc = ops->ndo_start_xmit(skb, dev); -- trace_net_dev_xmit(skb, rc, dev, skb_len); -+#ifdef CONFIG_ETHERNET_PACKET_MANGLE -+ if (!dev->eth_mangle_tx || -+ (skb = dev->eth_mangle_tx(dev, skb)) != NULL) -+#else -+ if (1) -+#endif -+ { -+ skb_len = skb->len; -+ trace_net_dev_start_xmit(skb, dev); -+ rc = ops->ndo_start_xmit(skb, dev); -+ trace_net_dev_xmit(skb, rc, dev, skb_len); -+ } else { -+ rc = NETDEV_TX_OK; -+ } - if (rc == NETDEV_TX_OK) - txq_trans_update(txq); - return rc; -@@ -2626,10 +2636,20 @@ gso: - if (!list_empty(&ptype_all)) - dev_queue_xmit_nit(nskb, dev); - -- skb_len = nskb->len; -- trace_net_dev_start_xmit(nskb, dev); -- rc = ops->ndo_start_xmit(nskb, dev); -- trace_net_dev_xmit(nskb, rc, dev, skb_len); -+#ifdef CONFIG_ETHERNET_PACKET_MANGLE -+ if (!dev->eth_mangle_tx || -+ (nskb = dev->eth_mangle_tx(dev, nskb)) != NULL) -+#else -+ if (1) -+#endif -+ { -+ skb_len = nskb->len; -+ trace_net_dev_start_xmit(nskb, dev); -+ rc = ops->ndo_start_xmit(nskb, dev); -+ trace_net_dev_xmit(nskb, rc, dev, skb_len); -+ } else { -+ rc = NETDEV_TX_OK; -+ } - if (unlikely(rc != NETDEV_TX_OK)) { - if (rc & ~NETDEV_TX_MASK) - goto out_kfree_gso_skb; -diff --git a/net/core/skbuff.c b/net/core/skbuff.c -index e5ae776e..400ff2a 100644 ---- a/net/core/skbuff.c -+++ b/net/core/skbuff.c -@@ -62,6 +62,7 @@ - #include <linux/scatterlist.h> - #include <linux/errqueue.h> - #include <linux/prefetch.h> -+#include <uapi/linux/if.h> - - #include <net/protocol.h> - #include <net/dst.h> -@@ -439,6 +440,22 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, - } - EXPORT_SYMBOL(__netdev_alloc_skb); - -+struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, -+ unsigned int length, gfp_t gfp) -+{ -+ struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp); -+ -+#ifdef CONFIG_ETHERNET_PACKET_MANGLE -+ if (dev && (dev->priv_flags & IFF_NO_IP_ALIGN)) -+ return skb; -+#endif -+ -+ if (NET_IP_ALIGN && skb) -+ skb_reserve(skb, NET_IP_ALIGN); -+ return skb; -+} -+EXPORT_SYMBOL(__netdev_alloc_skb_ip_align); -+ - void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, - int size, unsigned int truesize) - { -diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c -index 5dc638c..f4fd124 100644 ---- a/net/ethernet/eth.c -+++ b/net/ethernet/eth.c -@@ -161,6 +161,12 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev) - const struct ethhdr *eth; - - skb->dev = dev; -+ -+#ifdef CONFIG_ETHERNET_PACKET_MANGLE -+ if (dev->eth_mangle_rx) -+ dev->eth_mangle_rx(dev, skb); -+#endif -+ - skb_reset_mac_header(skb); - skb_pull_inline(skb, ETH_HLEN); - eth = eth_hdr(skb); --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0019-MIPS-ath79-process-board-cmdline-option.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0019-MIPS-ath79-process-board-cmdline-option.patch deleted file mode 100644 index 13eae3b8c..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0019-MIPS-ath79-process-board-cmdline-option.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 4c84b317734842765cb1c52624fc569efd9222dc Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 02:11:59 +0200 -Subject: [PATCH] MIPS: ath79: process board cmdline option - -This is necessary to correctly identify the running machine. ---- - arch/mips/ath79/setup.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/arch/mips/ath79/setup.c b/arch/mips/ath79/setup.c -index 64807a4..0c95758 100644 ---- a/arch/mips/ath79/setup.c -+++ b/arch/mips/ath79/setup.c -@@ -229,6 +229,8 @@ void __init plat_time_init(void) - mips_hpt_frequency = cpu_clk_rate / 2; - } - -+__setup("board=", mips_machtype_setup); -+ - static int __init ath79_setup(void) - { - ath79_gpio_init(); --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0020-spi-ath79-add-fast-flash-read-support.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0020-spi-ath79-add-fast-flash-read-support.patch deleted file mode 100644 index 8fd174448..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0020-spi-ath79-add-fast-flash-read-support.patch +++ /dev/null @@ -1,202 +0,0 @@ -From c4388a57860440e23c9654f4de2f515433e685a1 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 13 May 2014 04:12:35 +0200 -Subject: [PATCH] spi-ath79: add fast flash read support - ---- - .../include/asm/mach-ath79/ath79_spi_platform.h | 1 + - drivers/spi/spi-ath79.c | 124 ++++++++++++++++++++- - 2 files changed, 120 insertions(+), 5 deletions(-) - -diff --git a/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h b/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h -index aa2283e..65369fe 100644 ---- a/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h -+++ b/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h -@@ -18,6 +18,7 @@ struct ath79_spi_platform_data { - - struct ath79_spi_controller_data { - unsigned gpio; -+ bool is_flash; - }; - - #endif /* _ATH79_SPI_PLATFORM_H */ -diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c -index c3b2fb9..a26a6a4 100644 ---- a/drivers/spi/spi-ath79.c -+++ b/drivers/spi/spi-ath79.c -@@ -35,6 +35,11 @@ - #define ATH79_SPI_RRW_DELAY_FACTOR 12000 - #define MHZ (1000 * 1000) - -+enum ath79_spi_state { -+ ATH79_SPI_STATE_WAIT_CMD = 0, -+ ATH79_SPI_STATE_WAIT_READ, -+}; -+ - struct ath79_spi { - struct spi_bitbang bitbang; - u32 ioc_base; -@@ -42,6 +47,11 @@ struct ath79_spi { - void __iomem *base; - struct clk *clk; - unsigned rrw_delay; -+ -+ enum ath79_spi_state state; -+ u32 clk_div; -+ unsigned long read_addr; -+ unsigned long ahb_rate; - }; - - static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned reg) -@@ -104,9 +114,6 @@ static void ath79_spi_enable(struct ath79_spi *sp) - /* save CTRL register */ - sp->reg_ctrl = ath79_spi_rr(sp, AR71XX_SPI_REG_CTRL); - sp->ioc_base = ath79_spi_rr(sp, AR71XX_SPI_REG_IOC); -- -- /* TODO: setup speed? */ -- ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, 0x43); - } - - static void ath79_spi_disable(struct ath79_spi *sp) -@@ -203,6 +210,110 @@ static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned nsecs, - return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS); - } - -+static int ath79_spi_do_read_flash_data(struct spi_device *spi, -+ struct spi_transfer *t) -+{ -+ struct ath79_spi *sp = ath79_spidev_to_sp(spi); -+ -+ /* disable GPIO mode */ -+ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0); -+ -+ memcpy_fromio(t->rx_buf, sp->base + sp->read_addr, t->len); -+ -+ /* enable GPIO mode */ -+ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO); -+ -+ /* restore IOC register */ -+ ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); -+ -+ return t->len; -+} -+ -+static int ath79_spi_do_read_flash_cmd(struct spi_device *spi, -+ struct spi_transfer *t) -+{ -+ struct ath79_spi *sp = ath79_spidev_to_sp(spi); -+ int len; -+ const u8 *p; -+ -+ sp->read_addr = 0; -+ -+ len = t->len - 1; -+ p = t->tx_buf; -+ -+ while (len--) { -+ p++; -+ sp->read_addr <<= 8; -+ sp->read_addr |= *p; -+ } -+ -+ return t->len; -+} -+ -+static bool ath79_spi_is_read_cmd(struct spi_device *spi, -+ struct spi_transfer *t) -+{ -+ return t->type == SPI_TRANSFER_FLASH_READ_CMD; -+} -+ -+static bool ath79_spi_is_data_read(struct spi_device *spi, -+ struct spi_transfer *t) -+{ -+ return t->type == SPI_TRANSFER_FLASH_READ_DATA; -+} -+ -+static int ath79_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) -+{ -+ struct ath79_spi *sp = ath79_spidev_to_sp(spi); -+ int ret; -+ -+ switch (sp->state) { -+ case ATH79_SPI_STATE_WAIT_CMD: -+ if (ath79_spi_is_read_cmd(spi, t)) { -+ ret = ath79_spi_do_read_flash_cmd(spi, t); -+ sp->state = ATH79_SPI_STATE_WAIT_READ; -+ } else { -+ ret = spi_bitbang_bufs(spi, t); -+ } -+ break; -+ -+ case ATH79_SPI_STATE_WAIT_READ: -+ if (ath79_spi_is_data_read(spi, t)) { -+ ret = ath79_spi_do_read_flash_data(spi, t); -+ } else { -+ dev_warn(&spi->dev, "flash data read expected\n"); -+ ret = -EIO; -+ } -+ sp->state = ATH79_SPI_STATE_WAIT_CMD; -+ break; -+ -+ default: -+ BUG(); -+ } -+ -+ return ret; -+} -+ -+static int ath79_spi_setup_transfer(struct spi_device *spi, -+ struct spi_transfer *t) -+{ -+ struct ath79_spi *sp = ath79_spidev_to_sp(spi); -+ struct ath79_spi_controller_data *cdata; -+ int ret; -+ -+ ret = spi_bitbang_setup_transfer(spi, t); -+ if (ret) -+ return ret; -+ -+ cdata = spi->controller_data; -+ if (cdata->is_flash) -+ sp->bitbang.txrx_bufs = ath79_spi_txrx_bufs; -+ else -+ sp->bitbang.txrx_bufs = spi_bitbang_bufs; -+ -+ return ret; -+} -+ - static int ath79_spi_probe(struct platform_device *pdev) - { - struct spi_master *master; -@@ -223,6 +334,8 @@ static int ath79_spi_probe(struct platform_device *pdev) - - pdata = dev_get_platdata(&pdev->dev); - -+ sp->state = ATH79_SPI_STATE_WAIT_CMD; -+ - master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); - master->setup = ath79_spi_setup; - master->cleanup = ath79_spi_cleanup; -@@ -234,7 +347,7 @@ static int ath79_spi_probe(struct platform_device *pdev) - sp->bitbang.master = master; - sp->bitbang.chipselect = ath79_spi_chipselect; - sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0; -- sp->bitbang.setup_transfer = spi_bitbang_setup_transfer; -+ sp->bitbang.setup_transfer = ath79_spi_setup_transfer; - sp->bitbang.flags = SPI_CS_HIGH; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); -@@ -259,7 +372,8 @@ static int ath79_spi_probe(struct platform_device *pdev) - if (ret) - goto err_put_master; - -- rate = DIV_ROUND_UP(clk_get_rate(sp->clk), MHZ); -+ sp->ahb_rate = clk_get_rate(sp->clk); -+ rate = DIV_ROUND_UP(sp->ahb_rate, MHZ); - if (!rate) { - ret = -EINVAL; - goto err_clk_disable; --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0021-phy-add-mdio-boardinfo.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0021-phy-add-mdio-boardinfo.patch deleted file mode 100644 index 3ec15e171..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0021-phy-add-mdio-boardinfo.patch +++ /dev/null @@ -1,227 +0,0 @@ -From b8d5957374dc0e6ec8687c6e1b154ea066d27a5b Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Wed, 14 May 2014 02:44:43 +0200 -Subject: [PATCH] phy: add mdio boardinfo - ---- - drivers/net/Makefile | 2 +- - drivers/net/phy/Kconfig | 4 +++ - drivers/net/phy/Makefile | 2 ++ - drivers/net/phy/mdio-boardinfo.c | 58 ++++++++++++++++++++++++++++++++++++++++ - drivers/net/phy/mdio-boardinfo.h | 22 +++++++++++++++ - drivers/net/phy/mdio_bus.c | 20 ++++++++++++++ - include/linux/phy.h | 18 +++++++++++++ - 7 files changed, 125 insertions(+), 1 deletion(-) - create mode 100644 drivers/net/phy/mdio-boardinfo.c - create mode 100644 drivers/net/phy/mdio-boardinfo.h - -diff --git a/drivers/net/Makefile b/drivers/net/Makefile -index 3fef8a8..70b736b 100644 ---- a/drivers/net/Makefile -+++ b/drivers/net/Makefile -@@ -15,7 +15,7 @@ obj-$(CONFIG_MII) += mii.o - obj-$(CONFIG_MDIO) += mdio.o - obj-$(CONFIG_NET) += Space.o loopback.o - obj-$(CONFIG_NETCONSOLE) += netconsole.o --obj-$(CONFIG_PHYLIB) += phy/ -+obj-y += phy/ - obj-$(CONFIG_RIONET) += rionet.o - obj-$(CONFIG_NET_TEAM) += team/ - obj-$(CONFIG_TUN) += tun.o -diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig -index 0414889..97ca8ec 100644 ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -12,6 +12,10 @@ menuconfig PHYLIB - - if PHYLIB - -+config MDIO_BOARDINFO -+ bool -+ default y -+ - config SWCONFIG - tristate "Switch configuration API" - ---help--- -diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile -index 3c76ff8..0c990a4 100644 ---- a/drivers/net/phy/Makefile -+++ b/drivers/net/phy/Makefile -@@ -2,6 +2,8 @@ - - libphy-objs := phy.o phy_device.o mdio_bus.o - -+obj-$(CONFIG_MDIO_BOARDINFO) += mdio-boardinfo.o -+ - obj-$(CONFIG_PHYLIB) += libphy.o - obj-$(CONFIG_SWCONFIG) += swconfig.o - obj-$(CONFIG_MARVELL_PHY) += marvell.o -diff --git a/drivers/net/phy/mdio-boardinfo.c b/drivers/net/phy/mdio-boardinfo.c -new file mode 100644 -index 0000000..9b8aaed ---- /dev/null -+++ b/drivers/net/phy/mdio-boardinfo.c -@@ -0,0 +1,58 @@ -+/* -+ * mdio-boardinfo.c - collect pre-declarations of PHY devices -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation; either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/phy.h> -+#include <linux/slab.h> -+#include <linux/export.h> -+#include <linux/mutex.h> -+#include <linux/phy.h> -+ -+#include "mdio-boardinfo.h" -+ -+/* -+ * These symbols are exported ONLY FOR the mdio_bus component. -+ * No other users will be supported. -+ */ -+ -+LIST_HEAD(__mdio_board_list); -+EXPORT_SYMBOL_GPL(__mdio_board_list); -+ -+DEFINE_MUTEX(__mdio_board_lock); -+EXPORT_SYMBOL_GPL(__mdio_board_lock); -+ -+/** -+ * mdio_register_board_info - register PHY devices for a given board -+ * @info: array of chip descriptors -+ * @n: how many descriptors are provided -+ * Context: can sleep -+ * -+ * The board info passed can safely be __initdata ... but be careful of -+ * any embedded pointers (platform_data, etc), they're copied as-is. -+ */ -+int __init -+mdiobus_register_board_info(struct mdio_board_info const *info, unsigned n) -+{ -+ struct mdio_board_entry *be; -+ int i; -+ -+ be = kzalloc(n * sizeof(*be), GFP_KERNEL); -+ if (!be) -+ return -ENOMEM; -+ -+ for (i = 0; i < n; i++, be++, info++) { -+ memcpy(&be->board_info, info, sizeof(*info)); -+ mutex_lock(&__mdio_board_lock); -+ list_add_tail(&be->list, &__mdio_board_list); -+ mutex_unlock(&__mdio_board_lock); -+ } -+ -+ return 0; -+} -diff --git a/drivers/net/phy/mdio-boardinfo.h b/drivers/net/phy/mdio-boardinfo.h -new file mode 100644 -index 0000000..28fbc0d ---- /dev/null -+++ b/drivers/net/phy/mdio-boardinfo.h -@@ -0,0 +1,22 @@ -+/* -+ * mdio-boardinfo.h - boardinfo interface internal to the mdio_bus component -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation; either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ */ -+ -+#include <linux/mutex.h> -+ -+struct mdio_board_entry { -+ struct list_head list; -+ struct mdio_board_info board_info; -+}; -+ -+/* __mdio_board_lock protects __mdio_board_list -+ * only mdio_bus components are allowed to use these symbols. -+ */ -+extern struct mutex __mdio_board_lock; -+extern struct list_head __mdio_board_list; -diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c -index 71e4900..50fbe35 100644 ---- a/drivers/net/phy/mdio_bus.c -+++ b/drivers/net/phy/mdio_bus.c -@@ -38,6 +38,8 @@ - - #include <asm/irq.h> - -+#include "mdio-boardinfo.h" -+ - /** - * mdiobus_alloc_size - allocate a mii_bus structure - * @size: extra amount of memory to allocate for private storage. -@@ -224,15 +226,33 @@ void mdiobus_free(struct mii_bus *bus) - } - EXPORT_SYMBOL(mdiobus_free); - -+static void mdiobus_setup_phydev_from_boardinfo(struct mii_bus *bus, -+ struct phy_device *phydev, -+ struct mdio_board_info *bi) -+{ -+ if (strcmp(bus->id, bi->bus_id) || -+ bi->phy_addr != phydev->addr) -+ return; -+ -+ phydev->dev.platform_data = (void *) bi->platform_data; -+} -+ - struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) - { - struct phy_device *phydev; -+ struct mdio_board_entry *be; - int err; - - phydev = get_phy_device(bus, addr, false); - if (IS_ERR(phydev) || phydev == NULL) - return phydev; - -+ mutex_lock(&__mdio_board_lock); -+ list_for_each_entry(be, &__mdio_board_list, list) -+ mdiobus_setup_phydev_from_boardinfo(bus, phydev, -+ &be->board_info); -+ mutex_unlock(&__mdio_board_lock); -+ - err = phy_device_register(phydev); - if (err) { - phy_device_free(phydev); -diff --git a/include/linux/phy.h b/include/linux/phy.h -index f1441b4..9dca415 100644 ---- a/include/linux/phy.h -+++ b/include/linux/phy.h -@@ -658,4 +658,22 @@ int __init mdio_bus_init(void); - void mdio_bus_exit(void); - - extern struct bus_type mdio_bus_type; -+ -+struct mdio_board_info { -+ const char *bus_id; -+ int phy_addr; -+ -+ const void *platform_data; -+}; -+ -+#ifdef CONFIG_MDIO_BOARDINFO -+int mdiobus_register_board_info(const struct mdio_board_info *info, unsigned n); -+#else -+static inline int -+mdiobus_register_board_info(const struct mdio_board_info *info, unsigned n) -+{ -+ return 0; -+} -+#endif -+ - #endif /* __PHY_H */ --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0022-mips-ath79-add-ath79-ethernet-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0022-mips-ath79-add-ath79-ethernet-driver.patch deleted file mode 100644 index a7eff47b1..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0022-mips-ath79-add-ath79-ethernet-driver.patch +++ /dev/null @@ -1,1429 +0,0 @@ -From 0c6bdad5f210f5f2fe28dc197ab77a36402bb36e Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Wed, 14 May 2014 03:08:37 +0200 -Subject: [PATCH] mips: ath79: add ath79 ethernet driver - ---- - arch/mips/ath79/Kconfig | 3 + - arch/mips/ath79/Makefile | 1 + - arch/mips/ath79/dev-eth.c | 1151 ++++++++++++++++++++++++ - arch/mips/ath79/dev-eth.h | 51 ++ - arch/mips/include/asm/mach-ath79/ar71xx_regs.h | 81 ++ - 5 files changed, 1287 insertions(+) - create mode 100644 arch/mips/ath79/dev-eth.c - create mode 100644 arch/mips/ath79/dev-eth.h - -diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig -index 3995e31..52cefd7 100644 ---- a/arch/mips/ath79/Kconfig -+++ b/arch/mips/ath79/Kconfig -@@ -109,6 +109,9 @@ config SOC_QCA955X - config PCI_AR724X - def_bool n - -+config ATH79_DEV_ETH -+ def_bool n -+ - config ATH79_DEV_GPIO_BUTTONS - def_bool n - -diff --git a/arch/mips/ath79/Makefile b/arch/mips/ath79/Makefile -index 5c9ff69..05485da 100644 ---- a/arch/mips/ath79/Makefile -+++ b/arch/mips/ath79/Makefile -@@ -17,6 +17,7 @@ obj-$(CONFIG_PCI) += pci.o - # Devices - # - obj-y += dev-common.o -+obj-$(CONFIG_ATH79_DEV_ETH) += dev-eth.o - obj-$(CONFIG_ATH79_DEV_GPIO_BUTTONS) += dev-gpio-buttons.o - obj-$(CONFIG_ATH79_DEV_LEDS_GPIO) += dev-leds-gpio.o - obj-$(CONFIG_ATH79_DEV_SPI) += dev-spi.o -diff --git a/arch/mips/ath79/dev-eth.c b/arch/mips/ath79/dev-eth.c -new file mode 100644 -index 0000000..21feeb9 ---- /dev/null -+++ b/arch/mips/ath79/dev-eth.c -@@ -0,0 +1,1151 @@ -+/* -+ * Atheros AR71xx SoC platform devices -+ * -+ * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> -+ * Copyright (C) 2008-2012 Gabor Juhos <juhosg@openwrt.org> -+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> -+ * -+ * Parts of this file are based on Atheros 2.6.15 BSP -+ * Parts of this file are based on Atheros 2.6.31 BSP -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/delay.h> -+#include <linux/etherdevice.h> -+#include <linux/platform_device.h> -+#include <linux/serial_8250.h> -+#include <linux/clk.h> -+#include <linux/sizes.h> -+ -+#include <asm/mach-ath79/ath79.h> -+#include <asm/mach-ath79/ar71xx_regs.h> -+#include <asm/mach-ath79/irq.h> -+ -+#include "common.h" -+#include "dev-eth.h" -+ -+unsigned char ath79_mac_base[ETH_ALEN] __initdata; -+ -+static struct resource ath79_mdio0_resources[] = { -+ { -+ .name = "mdio_base", -+ .flags = IORESOURCE_MEM, -+ .start = AR71XX_GE0_BASE, -+ .end = AR71XX_GE0_BASE + 0x200 - 1, -+ } -+}; -+ -+struct ag71xx_mdio_platform_data ath79_mdio0_data; -+ -+struct platform_device ath79_mdio0_device = { -+ .name = "ag71xx-mdio", -+ .id = 0, -+ .resource = ath79_mdio0_resources, -+ .num_resources = ARRAY_SIZE(ath79_mdio0_resources), -+ .dev = { -+ .platform_data = &ath79_mdio0_data, -+ }, -+}; -+ -+static struct resource ath79_mdio1_resources[] = { -+ { -+ .name = "mdio_base", -+ .flags = IORESOURCE_MEM, -+ .start = AR71XX_GE1_BASE, -+ .end = AR71XX_GE1_BASE + 0x200 - 1, -+ } -+}; -+ -+struct ag71xx_mdio_platform_data ath79_mdio1_data; -+ -+struct platform_device ath79_mdio1_device = { -+ .name = "ag71xx-mdio", -+ .id = 1, -+ .resource = ath79_mdio1_resources, -+ .num_resources = ARRAY_SIZE(ath79_mdio1_resources), -+ .dev = { -+ .platform_data = &ath79_mdio1_data, -+ }, -+}; -+ -+static void ath79_set_pll(u32 cfg_reg, u32 pll_reg, u32 pll_val, u32 shift) -+{ -+ void __iomem *base; -+ u32 t; -+ -+ base = ioremap_nocache(AR71XX_PLL_BASE, AR71XX_PLL_SIZE); -+ -+ t = __raw_readl(base + cfg_reg); -+ t &= ~(3 << shift); -+ t |= (2 << shift); -+ __raw_writel(t, base + cfg_reg); -+ udelay(100); -+ -+ __raw_writel(pll_val, base + pll_reg); -+ -+ t |= (3 << shift); -+ __raw_writel(t, base + cfg_reg); -+ udelay(100); -+ -+ t &= ~(3 << shift); -+ __raw_writel(t, base + cfg_reg); -+ udelay(100); -+ -+ printk(KERN_DEBUG "ar71xx: pll_reg %#x: %#x\n", -+ (unsigned int)(base + pll_reg), __raw_readl(base + pll_reg)); -+ -+ iounmap(base); -+} -+ -+static void __init ath79_mii_ctrl_set_if(unsigned int reg, -+ unsigned int mii_if) -+{ -+ void __iomem *base; -+ u32 t; -+ -+ base = ioremap(AR71XX_MII_BASE, AR71XX_MII_SIZE); -+ -+ t = __raw_readl(base + reg); -+ t &= ~(AR71XX_MII_CTRL_IF_MASK); -+ t |= (mii_if & AR71XX_MII_CTRL_IF_MASK); -+ __raw_writel(t, base + reg); -+ -+ iounmap(base); -+} -+ -+static void ath79_mii_ctrl_set_speed(unsigned int reg, unsigned int speed) -+{ -+ void __iomem *base; -+ unsigned int mii_speed; -+ u32 t; -+ -+ switch (speed) { -+ case SPEED_10: -+ mii_speed = AR71XX_MII_CTRL_SPEED_10; -+ break; -+ case SPEED_100: -+ mii_speed = AR71XX_MII_CTRL_SPEED_100; -+ break; -+ case SPEED_1000: -+ mii_speed = AR71XX_MII_CTRL_SPEED_1000; -+ break; -+ default: -+ BUG(); -+ } -+ -+ base = ioremap(AR71XX_MII_BASE, AR71XX_MII_SIZE); -+ -+ t = __raw_readl(base + reg); -+ t &= ~(AR71XX_MII_CTRL_SPEED_MASK << AR71XX_MII_CTRL_SPEED_SHIFT); -+ t |= mii_speed << AR71XX_MII_CTRL_SPEED_SHIFT; -+ __raw_writel(t, base + reg); -+ -+ iounmap(base); -+} -+ -+static unsigned long ar934x_get_mdio_ref_clock(void) -+{ -+ void __iomem *base; -+ unsigned long ret; -+ u32 t; -+ -+ base = ioremap(AR71XX_PLL_BASE, AR71XX_PLL_SIZE); -+ -+ ret = 0; -+ t = __raw_readl(base + AR934X_PLL_SWITCH_CLOCK_CONTROL_REG); -+ if (t & AR934X_PLL_SWITCH_CLOCK_CONTROL_MDIO_CLK_SEL) { -+ ret = 100 * 1000 * 1000; -+ } else { -+ struct clk *clk; -+ -+ clk = clk_get(NULL, "ref"); -+ if (!IS_ERR(clk)) -+ ret = clk_get_rate(clk); -+ } -+ -+ iounmap(base); -+ -+ return ret; -+} -+ -+void __init ath79_register_mdio(unsigned int id, u32 phy_mask) -+{ -+ struct platform_device *mdio_dev; -+ struct ag71xx_mdio_platform_data *mdio_data; -+ unsigned int max_id; -+ -+ if (ath79_soc == ATH79_SOC_AR9341 || -+ ath79_soc == ATH79_SOC_AR9342 || -+ ath79_soc == ATH79_SOC_AR9344 || -+ ath79_soc == ATH79_SOC_QCA9556 || -+ ath79_soc == ATH79_SOC_QCA9558) -+ max_id = 1; -+ else -+ max_id = 0; -+ -+ if (id > max_id) { -+ printk(KERN_ERR "ar71xx: invalid MDIO id %u\n", id); -+ return; -+ } -+ -+ switch (ath79_soc) { -+ case ATH79_SOC_AR7241: -+ case ATH79_SOC_AR9330: -+ case ATH79_SOC_AR9331: -+ mdio_dev = &ath79_mdio1_device; -+ mdio_data = &ath79_mdio1_data; -+ break; -+ -+ case ATH79_SOC_AR9341: -+ case ATH79_SOC_AR9342: -+ case ATH79_SOC_AR9344: -+ case ATH79_SOC_QCA9556: -+ case ATH79_SOC_QCA9558: -+ if (id == 0) { -+ mdio_dev = &ath79_mdio0_device; -+ mdio_data = &ath79_mdio0_data; -+ } else { -+ mdio_dev = &ath79_mdio1_device; -+ mdio_data = &ath79_mdio1_data; -+ } -+ break; -+ -+ case ATH79_SOC_AR7242: -+ ath79_set_pll(AR71XX_PLL_REG_SEC_CONFIG, -+ AR7242_PLL_REG_ETH0_INT_CLOCK, 0x62000000, -+ AR71XX_ETH0_PLL_SHIFT); -+ /* fall through */ -+ default: -+ mdio_dev = &ath79_mdio0_device; -+ mdio_data = &ath79_mdio0_data; -+ break; -+ } -+ -+ mdio_data->phy_mask = phy_mask; -+ -+ switch (ath79_soc) { -+ case ATH79_SOC_AR7240: -+ mdio_data->is_ar7240 = 1; -+ /* fall through */ -+ case ATH79_SOC_AR7241: -+ mdio_data->builtin_switch = 1; -+ break; -+ -+ case ATH79_SOC_AR9330: -+ mdio_data->is_ar9330 = 1; -+ /* fall through */ -+ case ATH79_SOC_AR9331: -+ mdio_data->builtin_switch = 1; -+ break; -+ -+ case ATH79_SOC_AR9341: -+ case ATH79_SOC_AR9342: -+ case ATH79_SOC_AR9344: -+ if (id == 1) { -+ mdio_data->builtin_switch = 1; -+ mdio_data->ref_clock = ar934x_get_mdio_ref_clock(); -+ mdio_data->mdio_clock = 6250000; -+ } -+ mdio_data->is_ar934x = 1; -+ break; -+ -+ case ATH79_SOC_QCA9556: -+ case ATH79_SOC_QCA9558: -+ mdio_data->is_ar934x = 1; -+ break; -+ -+ default: -+ break; -+ } -+ -+ platform_device_register(mdio_dev); -+} -+ -+struct ath79_eth_pll_data ath79_eth0_pll_data; -+struct ath79_eth_pll_data ath79_eth1_pll_data; -+ -+static u32 ath79_get_eth_pll(unsigned int mac, int speed) -+{ -+ struct ath79_eth_pll_data *pll_data; -+ u32 pll_val; -+ -+ switch (mac) { -+ case 0: -+ pll_data = &ath79_eth0_pll_data; -+ break; -+ case 1: -+ pll_data = &ath79_eth1_pll_data; -+ break; -+ default: -+ BUG(); -+ } -+ -+ switch (speed) { -+ case SPEED_10: -+ pll_val = pll_data->pll_10; -+ break; -+ case SPEED_100: -+ pll_val = pll_data->pll_100; -+ break; -+ case SPEED_1000: -+ pll_val = pll_data->pll_1000; -+ break; -+ default: -+ BUG(); -+ } -+ -+ return pll_val; -+} -+ -+static void ath79_set_speed_ge0(int speed) -+{ -+ u32 val = ath79_get_eth_pll(0, speed); -+ -+ ath79_set_pll(AR71XX_PLL_REG_SEC_CONFIG, AR71XX_PLL_REG_ETH0_INT_CLOCK, -+ val, AR71XX_ETH0_PLL_SHIFT); -+ ath79_mii_ctrl_set_speed(AR71XX_MII_REG_MII0_CTRL, speed); -+} -+ -+static void ath79_set_speed_ge1(int speed) -+{ -+ u32 val = ath79_get_eth_pll(1, speed); -+ -+ ath79_set_pll(AR71XX_PLL_REG_SEC_CONFIG, AR71XX_PLL_REG_ETH1_INT_CLOCK, -+ val, AR71XX_ETH1_PLL_SHIFT); -+ ath79_mii_ctrl_set_speed(AR71XX_MII_REG_MII1_CTRL, speed); -+} -+ -+static void ar7242_set_speed_ge0(int speed) -+{ -+ u32 val = ath79_get_eth_pll(0, speed); -+ void __iomem *base; -+ -+ base = ioremap_nocache(AR71XX_PLL_BASE, AR71XX_PLL_SIZE); -+ __raw_writel(val, base + AR7242_PLL_REG_ETH0_INT_CLOCK); -+ iounmap(base); -+} -+ -+static void ar91xx_set_speed_ge0(int speed) -+{ -+ u32 val = ath79_get_eth_pll(0, speed); -+ -+ ath79_set_pll(AR913X_PLL_REG_ETH_CONFIG, AR913X_PLL_REG_ETH0_INT_CLOCK, -+ val, AR913X_ETH0_PLL_SHIFT); -+ ath79_mii_ctrl_set_speed(AR71XX_MII_REG_MII0_CTRL, speed); -+} -+ -+static void ar91xx_set_speed_ge1(int speed) -+{ -+ u32 val = ath79_get_eth_pll(1, speed); -+ -+ ath79_set_pll(AR913X_PLL_REG_ETH_CONFIG, AR913X_PLL_REG_ETH1_INT_CLOCK, -+ val, AR913X_ETH1_PLL_SHIFT); -+ ath79_mii_ctrl_set_speed(AR71XX_MII_REG_MII1_CTRL, speed); -+} -+ -+static void ar934x_set_speed_ge0(int speed) -+{ -+ void __iomem *base; -+ u32 val = ath79_get_eth_pll(0, speed); -+ -+ base = ioremap_nocache(AR71XX_PLL_BASE, AR71XX_PLL_SIZE); -+ __raw_writel(val, base + AR934X_PLL_ETH_XMII_CONTROL_REG); -+ iounmap(base); -+} -+ -+static void qca955x_set_speed_xmii(int speed) -+{ -+ void __iomem *base; -+ u32 val = ath79_get_eth_pll(0, speed); -+ -+ base = ioremap_nocache(AR71XX_PLL_BASE, AR71XX_PLL_SIZE); -+ __raw_writel(val, base + QCA955X_PLL_ETH_XMII_CONTROL_REG); -+ iounmap(base); -+} -+ -+static void qca955x_set_speed_sgmii(int speed) -+{ -+ void __iomem *base; -+ u32 val = ath79_get_eth_pll(1, speed); -+ -+ base = ioremap_nocache(AR71XX_PLL_BASE, AR71XX_PLL_SIZE); -+ __raw_writel(val, base + QCA955X_PLL_ETH_SGMII_CONTROL_REG); -+ iounmap(base); -+} -+ -+static void ath79_set_speed_dummy(int speed) -+{ -+} -+ -+static void ath79_ddr_no_flush(void) -+{ -+} -+ -+static void ath79_ddr_flush_ge0(void) -+{ -+ ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_GE0); -+} -+ -+static void ath79_ddr_flush_ge1(void) -+{ -+ ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_GE1); -+} -+ -+static void ar724x_ddr_flush_ge0(void) -+{ -+ ath79_ddr_wb_flush(AR724X_DDR_REG_FLUSH_GE0); -+} -+ -+static void ar724x_ddr_flush_ge1(void) -+{ -+ ath79_ddr_wb_flush(AR724X_DDR_REG_FLUSH_GE1); -+} -+ -+static void ar91xx_ddr_flush_ge0(void) -+{ -+ ath79_ddr_wb_flush(AR913X_DDR_REG_FLUSH_GE0); -+} -+ -+static void ar91xx_ddr_flush_ge1(void) -+{ -+ ath79_ddr_wb_flush(AR913X_DDR_REG_FLUSH_GE1); -+} -+ -+static void ar933x_ddr_flush_ge0(void) -+{ -+ ath79_ddr_wb_flush(AR933X_DDR_REG_FLUSH_GE0); -+} -+ -+static void ar933x_ddr_flush_ge1(void) -+{ -+ ath79_ddr_wb_flush(AR933X_DDR_REG_FLUSH_GE1); -+} -+ -+static struct resource ath79_eth0_resources[] = { -+ { -+ .name = "mac_base", -+ .flags = IORESOURCE_MEM, -+ .start = AR71XX_GE0_BASE, -+ .end = AR71XX_GE0_BASE + 0x200 - 1, -+ }, { -+ .name = "mac_irq", -+ .flags = IORESOURCE_IRQ, -+ .start = ATH79_CPU_IRQ(4), -+ .end = ATH79_CPU_IRQ(4), -+ }, -+}; -+ -+struct ag71xx_platform_data ath79_eth0_data = { -+ .reset_bit = AR71XX_RESET_GE0_MAC, -+}; -+ -+struct platform_device ath79_eth0_device = { -+ .name = "ag71xx", -+ .id = 0, -+ .resource = ath79_eth0_resources, -+ .num_resources = ARRAY_SIZE(ath79_eth0_resources), -+ .dev = { -+ .platform_data = &ath79_eth0_data, -+ }, -+}; -+ -+static struct resource ath79_eth1_resources[] = { -+ { -+ .name = "mac_base", -+ .flags = IORESOURCE_MEM, -+ .start = AR71XX_GE1_BASE, -+ .end = AR71XX_GE1_BASE + 0x200 - 1, -+ }, { -+ .name = "mac_irq", -+ .flags = IORESOURCE_IRQ, -+ .start = ATH79_CPU_IRQ(5), -+ .end = ATH79_CPU_IRQ(5), -+ }, -+}; -+ -+struct ag71xx_platform_data ath79_eth1_data = { -+ .reset_bit = AR71XX_RESET_GE1_MAC, -+}; -+ -+struct platform_device ath79_eth1_device = { -+ .name = "ag71xx", -+ .id = 1, -+ .resource = ath79_eth1_resources, -+ .num_resources = ARRAY_SIZE(ath79_eth1_resources), -+ .dev = { -+ .platform_data = &ath79_eth1_data, -+ }, -+}; -+ -+struct ag71xx_switch_platform_data ath79_switch_data; -+ -+#define AR71XX_PLL_VAL_1000 0x00110000 -+#define AR71XX_PLL_VAL_100 0x00001099 -+#define AR71XX_PLL_VAL_10 0x00991099 -+ -+#define AR724X_PLL_VAL_1000 0x00110000 -+#define AR724X_PLL_VAL_100 0x00001099 -+#define AR724X_PLL_VAL_10 0x00991099 -+ -+#define AR7242_PLL_VAL_1000 0x16000000 -+#define AR7242_PLL_VAL_100 0x00000101 -+#define AR7242_PLL_VAL_10 0x00001616 -+ -+#define AR913X_PLL_VAL_1000 0x1a000000 -+#define AR913X_PLL_VAL_100 0x13000a44 -+#define AR913X_PLL_VAL_10 0x00441099 -+ -+#define AR933X_PLL_VAL_1000 0x00110000 -+#define AR933X_PLL_VAL_100 0x00001099 -+#define AR933X_PLL_VAL_10 0x00991099 -+ -+#define AR934X_PLL_VAL_1000 0x16000000 -+#define AR934X_PLL_VAL_100 0x00000101 -+#define AR934X_PLL_VAL_10 0x00001616 -+ -+static void __init ath79_init_eth_pll_data(unsigned int id) -+{ -+ struct ath79_eth_pll_data *pll_data; -+ u32 pll_10, pll_100, pll_1000; -+ -+ switch (id) { -+ case 0: -+ pll_data = &ath79_eth0_pll_data; -+ break; -+ case 1: -+ pll_data = &ath79_eth1_pll_data; -+ break; -+ default: -+ BUG(); -+ } -+ -+ switch (ath79_soc) { -+ case ATH79_SOC_AR7130: -+ case ATH79_SOC_AR7141: -+ case ATH79_SOC_AR7161: -+ pll_10 = AR71XX_PLL_VAL_10; -+ pll_100 = AR71XX_PLL_VAL_100; -+ pll_1000 = AR71XX_PLL_VAL_1000; -+ break; -+ -+ case ATH79_SOC_AR7240: -+ case ATH79_SOC_AR7241: -+ pll_10 = AR724X_PLL_VAL_10; -+ pll_100 = AR724X_PLL_VAL_100; -+ pll_1000 = AR724X_PLL_VAL_1000; -+ break; -+ -+ case ATH79_SOC_AR7242: -+ pll_10 = AR7242_PLL_VAL_10; -+ pll_100 = AR7242_PLL_VAL_100; -+ pll_1000 = AR7242_PLL_VAL_1000; -+ break; -+ -+ case ATH79_SOC_AR9130: -+ case ATH79_SOC_AR9132: -+ pll_10 = AR913X_PLL_VAL_10; -+ pll_100 = AR913X_PLL_VAL_100; -+ pll_1000 = AR913X_PLL_VAL_1000; -+ break; -+ -+ case ATH79_SOC_AR9330: -+ case ATH79_SOC_AR9331: -+ pll_10 = AR933X_PLL_VAL_10; -+ pll_100 = AR933X_PLL_VAL_100; -+ pll_1000 = AR933X_PLL_VAL_1000; -+ break; -+ -+ case ATH79_SOC_AR9341: -+ case ATH79_SOC_AR9342: -+ case ATH79_SOC_AR9344: -+ case ATH79_SOC_QCA9556: -+ case ATH79_SOC_QCA9558: -+ pll_10 = AR934X_PLL_VAL_10; -+ pll_100 = AR934X_PLL_VAL_100; -+ pll_1000 = AR934X_PLL_VAL_1000; -+ break; -+ -+ default: -+ BUG(); -+ } -+ -+ if (!pll_data->pll_10) -+ pll_data->pll_10 = pll_10; -+ -+ if (!pll_data->pll_100) -+ pll_data->pll_100 = pll_100; -+ -+ if (!pll_data->pll_1000) -+ pll_data->pll_1000 = pll_1000; -+} -+ -+static int __init ath79_setup_phy_if_mode(unsigned int id, -+ struct ag71xx_platform_data *pdata) -+{ -+ unsigned int mii_if; -+ -+ switch (id) { -+ case 0: -+ switch (ath79_soc) { -+ case ATH79_SOC_AR7130: -+ case ATH79_SOC_AR7141: -+ case ATH79_SOC_AR7161: -+ case ATH79_SOC_AR9130: -+ case ATH79_SOC_AR9132: -+ switch (pdata->phy_if_mode) { -+ case PHY_INTERFACE_MODE_MII: -+ mii_if = AR71XX_MII0_CTRL_IF_MII; -+ break; -+ case PHY_INTERFACE_MODE_GMII: -+ mii_if = AR71XX_MII0_CTRL_IF_GMII; -+ break; -+ case PHY_INTERFACE_MODE_RGMII: -+ mii_if = AR71XX_MII0_CTRL_IF_RGMII; -+ break; -+ case PHY_INTERFACE_MODE_RMII: -+ mii_if = AR71XX_MII0_CTRL_IF_RMII; -+ break; -+ default: -+ return -EINVAL; -+ } -+ ath79_mii_ctrl_set_if(AR71XX_MII_REG_MII0_CTRL, mii_if); -+ break; -+ -+ case ATH79_SOC_AR7240: -+ case ATH79_SOC_AR7241: -+ case ATH79_SOC_AR9330: -+ case ATH79_SOC_AR9331: -+ pdata->phy_if_mode = PHY_INTERFACE_MODE_MII; -+ break; -+ -+ case ATH79_SOC_AR7242: -+ /* FIXME */ -+ -+ case ATH79_SOC_AR9341: -+ case ATH79_SOC_AR9342: -+ case ATH79_SOC_AR9344: -+ switch (pdata->phy_if_mode) { -+ case PHY_INTERFACE_MODE_MII: -+ case PHY_INTERFACE_MODE_GMII: -+ case PHY_INTERFACE_MODE_RGMII: -+ case PHY_INTERFACE_MODE_RMII: -+ break; -+ default: -+ return -EINVAL; -+ } -+ break; -+ -+ case ATH79_SOC_QCA9556: -+ case ATH79_SOC_QCA9558: -+ switch (pdata->phy_if_mode) { -+ case PHY_INTERFACE_MODE_MII: -+ case PHY_INTERFACE_MODE_RGMII: -+ case PHY_INTERFACE_MODE_SGMII: -+ break; -+ default: -+ return -EINVAL; -+ } -+ break; -+ -+ default: -+ BUG(); -+ } -+ break; -+ case 1: -+ switch (ath79_soc) { -+ case ATH79_SOC_AR7130: -+ case ATH79_SOC_AR7141: -+ case ATH79_SOC_AR7161: -+ case ATH79_SOC_AR9130: -+ case ATH79_SOC_AR9132: -+ switch (pdata->phy_if_mode) { -+ case PHY_INTERFACE_MODE_RMII: -+ mii_if = AR71XX_MII1_CTRL_IF_RMII; -+ break; -+ case PHY_INTERFACE_MODE_RGMII: -+ mii_if = AR71XX_MII1_CTRL_IF_RGMII; -+ break; -+ default: -+ return -EINVAL; -+ } -+ ath79_mii_ctrl_set_if(AR71XX_MII_REG_MII1_CTRL, mii_if); -+ break; -+ -+ case ATH79_SOC_AR7240: -+ case ATH79_SOC_AR7241: -+ case ATH79_SOC_AR9330: -+ case ATH79_SOC_AR9331: -+ pdata->phy_if_mode = PHY_INTERFACE_MODE_GMII; -+ break; -+ -+ case ATH79_SOC_AR7242: -+ /* FIXME */ -+ -+ case ATH79_SOC_AR9341: -+ case ATH79_SOC_AR9342: -+ case ATH79_SOC_AR9344: -+ switch (pdata->phy_if_mode) { -+ case PHY_INTERFACE_MODE_MII: -+ case PHY_INTERFACE_MODE_GMII: -+ break; -+ default: -+ return -EINVAL; -+ } -+ break; -+ -+ case ATH79_SOC_QCA9556: -+ case ATH79_SOC_QCA9558: -+ switch (pdata->phy_if_mode) { -+ case PHY_INTERFACE_MODE_MII: -+ case PHY_INTERFACE_MODE_RGMII: -+ case PHY_INTERFACE_MODE_SGMII: -+ break; -+ default: -+ return -EINVAL; -+ } -+ break; -+ -+ default: -+ BUG(); -+ } -+ break; -+ } -+ -+ return 0; -+} -+ -+void __init ath79_setup_ar933x_phy4_switch(bool mac, bool mdio) -+{ -+ void __iomem *base; -+ u32 t; -+ -+ base = ioremap(AR933X_GMAC_BASE, AR933X_GMAC_SIZE); -+ -+ t = __raw_readl(base + AR933X_GMAC_REG_ETH_CFG); -+ t &= ~(AR933X_ETH_CFG_SW_PHY_SWAP | AR933X_ETH_CFG_SW_PHY_ADDR_SWAP); -+ if (mac) -+ t |= AR933X_ETH_CFG_SW_PHY_SWAP; -+ if (mdio) -+ t |= AR933X_ETH_CFG_SW_PHY_ADDR_SWAP; -+ __raw_writel(t, base + AR933X_GMAC_REG_ETH_CFG); -+ -+ iounmap(base); -+} -+ -+void __init ath79_setup_ar934x_eth_cfg(u32 mask) -+{ -+ void __iomem *base; -+ u32 t; -+ -+ base = ioremap(AR934X_GMAC_BASE, AR934X_GMAC_SIZE); -+ -+ t = __raw_readl(base + AR934X_GMAC_REG_ETH_CFG); -+ -+ t &= ~(AR934X_ETH_CFG_RGMII_GMAC0 | -+ AR934X_ETH_CFG_MII_GMAC0 | -+ AR934X_ETH_CFG_GMII_GMAC0 | -+ AR934X_ETH_CFG_SW_ONLY_MODE | -+ AR934X_ETH_CFG_SW_PHY_SWAP); -+ -+ t |= mask; -+ -+ __raw_writel(t, base + AR934X_GMAC_REG_ETH_CFG); -+ /* flush write */ -+ __raw_readl(base + AR934X_GMAC_REG_ETH_CFG); -+ -+ iounmap(base); -+} -+ -+static int ath79_eth_instance __initdata; -+void __init ath79_register_eth(unsigned int id) -+{ -+ struct platform_device *pdev; -+ struct ag71xx_platform_data *pdata; -+ int err; -+ -+ if (id > 1) { -+ printk(KERN_ERR "ar71xx: invalid ethernet id %d\n", id); -+ return; -+ } -+ -+ ath79_init_eth_pll_data(id); -+ -+ if (id == 0) -+ pdev = &ath79_eth0_device; -+ else -+ pdev = &ath79_eth1_device; -+ -+ pdata = pdev->dev.platform_data; -+ -+ pdata->max_frame_len = 1540; -+ pdata->desc_pktlen_mask = 0xfff; -+ -+ err = ath79_setup_phy_if_mode(id, pdata); -+ if (err) { -+ printk(KERN_ERR -+ "ar71xx: invalid PHY interface mode for GE%u\n", id); -+ return; -+ } -+ -+ switch (ath79_soc) { -+ case ATH79_SOC_AR7130: -+ if (id == 0) { -+ pdata->ddr_flush = ath79_ddr_flush_ge0; -+ pdata->set_speed = ath79_set_speed_ge0; -+ } else { -+ pdata->ddr_flush = ath79_ddr_flush_ge1; -+ pdata->set_speed = ath79_set_speed_ge1; -+ } -+ break; -+ -+ case ATH79_SOC_AR7141: -+ case ATH79_SOC_AR7161: -+ if (id == 0) { -+ pdata->ddr_flush = ath79_ddr_flush_ge0; -+ pdata->set_speed = ath79_set_speed_ge0; -+ } else { -+ pdata->ddr_flush = ath79_ddr_flush_ge1; -+ pdata->set_speed = ath79_set_speed_ge1; -+ } -+ pdata->has_gbit = 1; -+ break; -+ -+ case ATH79_SOC_AR7242: -+ if (id == 0) { -+ pdata->reset_bit |= AR724X_RESET_GE0_MDIO | -+ AR71XX_RESET_GE0_PHY; -+ pdata->ddr_flush = ar724x_ddr_flush_ge0; -+ pdata->set_speed = ar7242_set_speed_ge0; -+ } else { -+ pdata->reset_bit |= AR724X_RESET_GE1_MDIO | -+ AR71XX_RESET_GE1_PHY; -+ pdata->ddr_flush = ar724x_ddr_flush_ge1; -+ pdata->set_speed = ath79_set_speed_dummy; -+ } -+ pdata->has_gbit = 1; -+ pdata->is_ar724x = 1; -+ -+ if (!pdata->fifo_cfg1) -+ pdata->fifo_cfg1 = 0x0010ffff; -+ if (!pdata->fifo_cfg2) -+ pdata->fifo_cfg2 = 0x015500aa; -+ if (!pdata->fifo_cfg3) -+ pdata->fifo_cfg3 = 0x01f00140; -+ break; -+ -+ case ATH79_SOC_AR7241: -+ if (id == 0) -+ pdata->reset_bit |= AR724X_RESET_GE0_MDIO; -+ else -+ pdata->reset_bit |= AR724X_RESET_GE1_MDIO; -+ /* fall through */ -+ case ATH79_SOC_AR7240: -+ if (id == 0) { -+ pdata->reset_bit |= AR71XX_RESET_GE0_PHY; -+ pdata->ddr_flush = ar724x_ddr_flush_ge0; -+ pdata->set_speed = ath79_set_speed_dummy; -+ -+ pdata->phy_mask = BIT(4); -+ } else { -+ pdata->reset_bit |= AR71XX_RESET_GE1_PHY; -+ pdata->ddr_flush = ar724x_ddr_flush_ge1; -+ pdata->set_speed = ath79_set_speed_dummy; -+ -+ pdata->speed = SPEED_1000; -+ pdata->duplex = DUPLEX_FULL; -+ pdata->switch_data = &ath79_switch_data; -+ -+ ath79_switch_data.phy_poll_mask |= BIT(4); -+ } -+ pdata->has_gbit = 1; -+ pdata->is_ar724x = 1; -+ if (ath79_soc == ATH79_SOC_AR7240) -+ pdata->is_ar7240 = 1; -+ -+ if (!pdata->fifo_cfg1) -+ pdata->fifo_cfg1 = 0x0010ffff; -+ if (!pdata->fifo_cfg2) -+ pdata->fifo_cfg2 = 0x015500aa; -+ if (!pdata->fifo_cfg3) -+ pdata->fifo_cfg3 = 0x01f00140; -+ break; -+ -+ case ATH79_SOC_AR9130: -+ if (id == 0) { -+ pdata->ddr_flush = ar91xx_ddr_flush_ge0; -+ pdata->set_speed = ar91xx_set_speed_ge0; -+ } else { -+ pdata->ddr_flush = ar91xx_ddr_flush_ge1; -+ pdata->set_speed = ar91xx_set_speed_ge1; -+ } -+ pdata->is_ar91xx = 1; -+ break; -+ -+ case ATH79_SOC_AR9132: -+ if (id == 0) { -+ pdata->ddr_flush = ar91xx_ddr_flush_ge0; -+ pdata->set_speed = ar91xx_set_speed_ge0; -+ } else { -+ pdata->ddr_flush = ar91xx_ddr_flush_ge1; -+ pdata->set_speed = ar91xx_set_speed_ge1; -+ } -+ pdata->is_ar91xx = 1; -+ pdata->has_gbit = 1; -+ break; -+ -+ case ATH79_SOC_AR9330: -+ case ATH79_SOC_AR9331: -+ if (id == 0) { -+ pdata->reset_bit = AR933X_RESET_GE0_MAC | -+ AR933X_RESET_GE0_MDIO; -+ pdata->ddr_flush = ar933x_ddr_flush_ge0; -+ pdata->set_speed = ath79_set_speed_dummy; -+ -+ pdata->phy_mask = BIT(4); -+ } else { -+ pdata->reset_bit = AR933X_RESET_GE1_MAC | -+ AR933X_RESET_GE1_MDIO; -+ pdata->ddr_flush = ar933x_ddr_flush_ge1; -+ pdata->set_speed = ath79_set_speed_dummy; -+ -+ pdata->speed = SPEED_1000; -+ pdata->duplex = DUPLEX_FULL; -+ pdata->switch_data = &ath79_switch_data; -+ -+ ath79_switch_data.phy_poll_mask |= BIT(4); -+ } -+ -+ pdata->has_gbit = 1; -+ pdata->is_ar724x = 1; -+ -+ if (!pdata->fifo_cfg1) -+ pdata->fifo_cfg1 = 0x0010ffff; -+ if (!pdata->fifo_cfg2) -+ pdata->fifo_cfg2 = 0x015500aa; -+ if (!pdata->fifo_cfg3) -+ pdata->fifo_cfg3 = 0x01f00140; -+ break; -+ -+ case ATH79_SOC_AR9341: -+ case ATH79_SOC_AR9342: -+ case ATH79_SOC_AR9344: -+ if (id == 0) { -+ pdata->reset_bit = AR934X_RESET_GE0_MAC | -+ AR934X_RESET_GE0_MDIO; -+ pdata->set_speed = ar934x_set_speed_ge0; -+ } else { -+ pdata->reset_bit = AR934X_RESET_GE1_MAC | -+ AR934X_RESET_GE1_MDIO; -+ pdata->set_speed = ath79_set_speed_dummy; -+ -+ pdata->switch_data = &ath79_switch_data; -+ -+ /* reset the built-in switch */ -+ ath79_device_reset_set(AR934X_RESET_ETH_SWITCH); -+ ath79_device_reset_clear(AR934X_RESET_ETH_SWITCH); -+ } -+ -+ pdata->ddr_flush = ath79_ddr_no_flush; -+ pdata->has_gbit = 1; -+ pdata->is_ar724x = 1; -+ -+ pdata->max_frame_len = SZ_16K - 1; -+ pdata->desc_pktlen_mask = SZ_16K - 1; -+ -+ if (!pdata->fifo_cfg1) -+ pdata->fifo_cfg1 = 0x0010ffff; -+ if (!pdata->fifo_cfg2) -+ pdata->fifo_cfg2 = 0x015500aa; -+ if (!pdata->fifo_cfg3) -+ pdata->fifo_cfg3 = 0x01f00140; -+ break; -+ -+ case ATH79_SOC_QCA9556: -+ case ATH79_SOC_QCA9558: -+ if (id == 0) { -+ pdata->reset_bit = QCA955X_RESET_GE0_MAC | -+ QCA955X_RESET_GE0_MDIO; -+ pdata->set_speed = qca955x_set_speed_xmii; -+ } else { -+ pdata->reset_bit = QCA955X_RESET_GE1_MAC | -+ QCA955X_RESET_GE1_MDIO; -+ pdata->set_speed = qca955x_set_speed_sgmii; -+ } -+ -+ pdata->ddr_flush = ath79_ddr_no_flush; -+ pdata->has_gbit = 1; -+ pdata->is_ar724x = 1; -+ -+ /* -+ * Limit the maximum frame length to 4095 bytes. -+ * Although the documentation says that the hardware -+ * limit is 16383 bytes but that does not work in -+ * practice. It seems that the hardware only updates -+ * the lowest 12 bits of the packet length field -+ * in the RX descriptor. -+ */ -+ pdata->max_frame_len = SZ_4K - 1; -+ pdata->desc_pktlen_mask = SZ_16K - 1; -+ -+ if (!pdata->fifo_cfg1) -+ pdata->fifo_cfg1 = 0x0010ffff; -+ if (!pdata->fifo_cfg2) -+ pdata->fifo_cfg2 = 0x015500aa; -+ if (!pdata->fifo_cfg3) -+ pdata->fifo_cfg3 = 0x01f00140; -+ break; -+ -+ default: -+ BUG(); -+ } -+ -+ switch (pdata->phy_if_mode) { -+ case PHY_INTERFACE_MODE_GMII: -+ case PHY_INTERFACE_MODE_RGMII: -+ case PHY_INTERFACE_MODE_SGMII: -+ if (!pdata->has_gbit) { -+ printk(KERN_ERR "ar71xx: no gbit available on eth%d\n", -+ id); -+ return; -+ } -+ /* fallthrough */ -+ default: -+ break; -+ } -+ -+ if (!is_valid_ether_addr(pdata->mac_addr)) { -+ random_ether_addr(pdata->mac_addr); -+ printk(KERN_DEBUG -+ "ar71xx: using random MAC address for eth%d\n", -+ ath79_eth_instance); -+ } -+ -+ if (pdata->mii_bus_dev == NULL) { -+ switch (ath79_soc) { -+ case ATH79_SOC_AR9341: -+ case ATH79_SOC_AR9342: -+ case ATH79_SOC_AR9344: -+ if (id == 0) -+ pdata->mii_bus_dev = &ath79_mdio0_device.dev; -+ else -+ pdata->mii_bus_dev = &ath79_mdio1_device.dev; -+ break; -+ -+ case ATH79_SOC_AR7241: -+ case ATH79_SOC_AR9330: -+ case ATH79_SOC_AR9331: -+ pdata->mii_bus_dev = &ath79_mdio1_device.dev; -+ break; -+ -+ case ATH79_SOC_QCA9556: -+ case ATH79_SOC_QCA9558: -+ /* don't assign any MDIO device by default */ -+ break; -+ -+ default: -+ pdata->mii_bus_dev = &ath79_mdio0_device.dev; -+ break; -+ } -+ } -+ -+ /* Reset the device */ -+ ath79_device_reset_set(pdata->reset_bit); -+ mdelay(100); -+ -+ ath79_device_reset_clear(pdata->reset_bit); -+ mdelay(100); -+ -+ platform_device_register(pdev); -+ ath79_eth_instance++; -+} -+ -+void __init ath79_set_mac_base(unsigned char *mac) -+{ -+ memcpy(ath79_mac_base, mac, ETH_ALEN); -+} -+ -+void __init ath79_parse_ascii_mac(char *mac_str, u8 *mac) -+{ -+ int t; -+ -+ t = sscanf(mac_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", -+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); -+ -+ if (t != ETH_ALEN) -+ t = sscanf(mac_str, "%02hhx.%02hhx.%02hhx.%02hhx.%02hhx.%02hhx", -+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); -+ -+ if (t != ETH_ALEN || !is_valid_ether_addr(mac)) { -+ memset(mac, 0, ETH_ALEN); -+ printk(KERN_DEBUG "ar71xx: invalid mac address \"%s\"\n", -+ mac_str); -+ } -+} -+ -+static void __init ath79_set_mac_base_ascii(char *str) -+{ -+ u8 mac[ETH_ALEN]; -+ -+ ath79_parse_ascii_mac(str, mac); -+ ath79_set_mac_base(mac); -+} -+ -+static int __init ath79_ethaddr_setup(char *str) -+{ -+ ath79_set_mac_base_ascii(str); -+ return 1; -+} -+__setup("ethaddr=", ath79_ethaddr_setup); -+ -+static int __init ath79_kmac_setup(char *str) -+{ -+ ath79_set_mac_base_ascii(str); -+ return 1; -+} -+__setup("kmac=", ath79_kmac_setup); -+ -+void __init ath79_init_mac(unsigned char *dst, const unsigned char *src, -+ int offset) -+{ -+ int t; -+ -+ if (!dst) -+ return; -+ -+ if (!src || !is_valid_ether_addr(src)) { -+ memset(dst, '\0', ETH_ALEN); -+ return; -+ } -+ -+ t = (((u32) src[3]) << 16) + (((u32) src[4]) << 8) + ((u32) src[5]); -+ t += offset; -+ -+ dst[0] = src[0]; -+ dst[1] = src[1]; -+ dst[2] = src[2]; -+ dst[3] = (t >> 16) & 0xff; -+ dst[4] = (t >> 8) & 0xff; -+ dst[5] = t & 0xff; -+} -+ -+void __init ath79_init_local_mac(unsigned char *dst, const unsigned char *src) -+{ -+ int i; -+ -+ if (!dst) -+ return; -+ -+ if (!src || !is_valid_ether_addr(src)) { -+ memset(dst, '\0', ETH_ALEN); -+ return; -+ } -+ -+ for (i = 0; i < ETH_ALEN; i++) -+ dst[i] = src[i]; -+ dst[0] |= 0x02; -+} -diff --git a/arch/mips/ath79/dev-eth.h b/arch/mips/ath79/dev-eth.h -new file mode 100644 -index 0000000..ff26ec4 ---- /dev/null -+++ b/arch/mips/ath79/dev-eth.h -@@ -0,0 +1,51 @@ -+/* -+ * Atheros AR71xx SoC device definitions -+ * -+ * Copyright (C) 2008-2012 Gabor Juhos <juhosg@openwrt.org> -+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#ifndef _ATH79_DEV_ETH_H -+#define _ATH79_DEV_ETH_H -+ -+#include <asm/mach-ath79/ag71xx_platform.h> -+ -+struct platform_device; -+ -+extern unsigned char ath79_mac_base[] __initdata; -+void ath79_parse_ascii_mac(char *mac_str, u8 *mac); -+void ath79_init_mac(unsigned char *dst, const unsigned char *src, -+ int offset); -+void ath79_init_local_mac(unsigned char *dst, const unsigned char *src); -+ -+struct ath79_eth_pll_data { -+ u32 pll_10; -+ u32 pll_100; -+ u32 pll_1000; -+}; -+ -+extern struct ath79_eth_pll_data ath79_eth0_pll_data; -+extern struct ath79_eth_pll_data ath79_eth1_pll_data; -+ -+extern struct ag71xx_platform_data ath79_eth0_data; -+extern struct ag71xx_platform_data ath79_eth1_data; -+extern struct platform_device ath79_eth0_device; -+extern struct platform_device ath79_eth1_device; -+void ath79_register_eth(unsigned int id); -+ -+extern struct ag71xx_switch_platform_data ath79_switch_data; -+ -+extern struct ag71xx_mdio_platform_data ath79_mdio0_data; -+extern struct ag71xx_mdio_platform_data ath79_mdio1_data; -+extern struct platform_device ath79_mdio0_device; -+extern struct platform_device ath79_mdio1_device; -+void ath79_register_mdio(unsigned int id, u32 phy_mask); -+ -+void ath79_setup_ar933x_phy4_switch(bool mac, bool mdio); -+void ath79_setup_ar934x_eth_cfg(u32 mask); -+ -+#endif /* _ATH79_DEV_ETH_H */ -diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h -index cd41e93..3e6b2ed 100644 ---- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h -+++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h -@@ -20,6 +20,10 @@ - #include <linux/bitops.h> - - #define AR71XX_APB_BASE 0x18000000 -+#define AR71XX_GE0_BASE 0x19000000 -+#define AR71XX_GE0_SIZE 0x10000 -+#define AR71XX_GE1_BASE 0x1a000000 -+#define AR71XX_GE1_SIZE 0x10000 - #define AR71XX_EHCI_BASE 0x1b000000 - #define AR71XX_EHCI_SIZE 0x1000 - #define AR71XX_OHCI_BASE 0x1c000000 -@@ -39,6 +43,8 @@ - #define AR71XX_PLL_SIZE 0x100 - #define AR71XX_RESET_BASE (AR71XX_APB_BASE + 0x00060000) - #define AR71XX_RESET_SIZE 0x100 -+#define AR71XX_MII_BASE (AR71XX_APB_BASE + 0x00070000) -+#define AR71XX_MII_SIZE 0x100 - - #define AR71XX_PCI_MEM_BASE 0x10000000 - #define AR71XX_PCI_MEM_SIZE 0x07000000 -@@ -81,11 +87,15 @@ - - #define AR933X_UART_BASE (AR71XX_APB_BASE + 0x00020000) - #define AR933X_UART_SIZE 0x14 -+#define AR933X_GMAC_BASE (AR71XX_APB_BASE + 0x00070000) -+#define AR933X_GMAC_SIZE 0x04 - #define AR933X_WMAC_BASE (AR71XX_APB_BASE + 0x00100000) - #define AR933X_WMAC_SIZE 0x20000 - #define AR933X_EHCI_BASE 0x1b000000 - #define AR933X_EHCI_SIZE 0x1000 - -+#define AR934X_GMAC_BASE (AR71XX_APB_BASE + 0x00070000) -+#define AR934X_GMAC_SIZE 0x14 - #define AR934X_WMAC_BASE (AR71XX_APB_BASE + 0x00100000) - #define AR934X_WMAC_SIZE 0x20000 - #define AR934X_EHCI_BASE 0x1b000000 -@@ -166,6 +176,9 @@ - #define AR71XX_AHB_DIV_SHIFT 20 - #define AR71XX_AHB_DIV_MASK 0x7 - -+#define AR71XX_ETH0_PLL_SHIFT 17 -+#define AR71XX_ETH1_PLL_SHIFT 19 -+ - #define AR724X_PLL_REG_CPU_CONFIG 0x00 - #define AR724X_PLL_REG_PCIE_CONFIG 0x18 - -@@ -178,6 +191,8 @@ - #define AR724X_DDR_DIV_SHIFT 22 - #define AR724X_DDR_DIV_MASK 0x3 - -+#define AR7242_PLL_REG_ETH0_INT_CLOCK 0x2c -+ - #define AR913X_PLL_REG_CPU_CONFIG 0x00 - #define AR913X_PLL_REG_ETH_CONFIG 0x04 - #define AR913X_PLL_REG_ETH0_INT_CLOCK 0x14 -@@ -190,6 +205,9 @@ - #define AR913X_AHB_DIV_SHIFT 19 - #define AR913X_AHB_DIV_MASK 0x1 - -+#define AR913X_ETH0_PLL_SHIFT 20 -+#define AR913X_ETH1_PLL_SHIFT 22 -+ - #define AR933X_PLL_CPU_CONFIG_REG 0x00 - #define AR933X_PLL_CLOCK_CTRL_REG 0x08 - -@@ -211,6 +229,8 @@ - #define AR934X_PLL_CPU_CONFIG_REG 0x00 - #define AR934X_PLL_DDR_CONFIG_REG 0x04 - #define AR934X_PLL_CPU_DDR_CLK_CTRL_REG 0x08 -+#define AR934X_PLL_SWITCH_CLOCK_CONTROL_REG 0x24 -+#define AR934X_PLL_ETH_XMII_CONTROL_REG 0x2c - - #define AR934X_PLL_CPU_CONFIG_NFRAC_SHIFT 0 - #define AR934X_PLL_CPU_CONFIG_NFRAC_MASK 0x3f -@@ -243,9 +263,13 @@ - #define AR934X_PLL_CPU_DDR_CLK_CTRL_DDRCLK_FROM_DDRPLL BIT(21) - #define AR934X_PLL_CPU_DDR_CLK_CTRL_AHBCLK_FROM_DDRPLL BIT(24) - -+#define AR934X_PLL_SWITCH_CLOCK_CONTROL_MDIO_CLK_SEL BIT(6) -+ - #define QCA955X_PLL_CPU_CONFIG_REG 0x00 - #define QCA955X_PLL_DDR_CONFIG_REG 0x04 - #define QCA955X_PLL_CLK_CTRL_REG 0x08 -+#define QCA955X_PLL_ETH_XMII_CONTROL_REG 0x28 -+#define QCA955X_PLL_ETH_SGMII_CONTROL_REG 0x48 - - #define QCA955X_PLL_CPU_CONFIG_NFRAC_SHIFT 0 - #define QCA955X_PLL_CPU_CONFIG_NFRAC_MASK 0x3f -@@ -370,16 +394,30 @@ - #define AR913X_RESET_USB_HOST BIT(5) - #define AR913X_RESET_USB_PHY BIT(4) - -+#define AR933X_RESET_GE1_MDIO BIT(23) -+#define AR933X_RESET_GE0_MDIO BIT(22) -+#define AR933X_RESET_GE1_MAC BIT(13) - #define AR933X_RESET_WMAC BIT(11) -+#define AR933X_RESET_GE0_MAC BIT(9) - #define AR933X_RESET_USB_HOST BIT(5) - #define AR933X_RESET_USB_PHY BIT(4) - #define AR933X_RESET_USBSUS_OVERRIDE BIT(3) - -+#define AR934X_RESET_GE1_MDIO BIT(23) -+#define AR934X_RESET_GE0_MDIO BIT(22) -+#define AR934X_RESET_GE1_MAC BIT(13) - #define AR934X_RESET_USB_PHY_ANALOG BIT(11) -+#define AR934X_RESET_GE0_MAC BIT(9) -+#define AR934X_RESET_ETH_SWITCH BIT(8) - #define AR934X_RESET_USB_HOST BIT(5) - #define AR934X_RESET_USB_PHY BIT(4) - #define AR934X_RESET_USBSUS_OVERRIDE BIT(3) - -+#define QCA955X_RESET_GE1_MDIO BIT(23) -+#define QCA955X_RESET_GE0_MDIO BIT(22) -+#define QCA955X_RESET_GE1_MAC BIT(13) -+#define QCA955X_RESET_GE0_MAC BIT(9) -+ - #define AR933X_BOOTSTRAP_REF_CLK_40 BIT(0) - - #define AR934X_BOOTSTRAP_SW_OPTION8 BIT(23) -@@ -552,4 +590,47 @@ - #define AR934X_SRIF_DPLL2_OUTDIV_SHIFT 13 - #define AR934X_SRIF_DPLL2_OUTDIV_MASK 0x7 - -+#define AR71XX_GPIO_FUNC_SPI_CS2_EN BIT(13) -+#define AR71XX_GPIO_FUNC_SPI_CS1_EN BIT(12) -+ -+/* -+ * MII_CTRL block -+ */ -+#define AR71XX_MII_REG_MII0_CTRL 0x00 -+#define AR71XX_MII_REG_MII1_CTRL 0x04 -+ -+#define AR71XX_MII_CTRL_IF_MASK 3 -+#define AR71XX_MII_CTRL_SPEED_SHIFT 4 -+#define AR71XX_MII_CTRL_SPEED_MASK 3 -+#define AR71XX_MII_CTRL_SPEED_10 0 -+#define AR71XX_MII_CTRL_SPEED_100 1 -+#define AR71XX_MII_CTRL_SPEED_1000 2 -+ -+#define AR71XX_MII0_CTRL_IF_GMII 0 -+#define AR71XX_MII0_CTRL_IF_MII 1 -+#define AR71XX_MII0_CTRL_IF_RGMII 2 -+#define AR71XX_MII0_CTRL_IF_RMII 3 -+ -+#define AR71XX_MII1_CTRL_IF_RGMII 0 -+#define AR71XX_MII1_CTRL_IF_RMII 1 -+ -+/* -+ * AR933X GMAC interface -+ */ -+#define AR933X_GMAC_REG_ETH_CFG 0x00 -+ -+#define AR933X_ETH_CFG_SW_PHY_SWAP BIT(7) -+#define AR933X_ETH_CFG_SW_PHY_ADDR_SWAP BIT(8) -+ -+/* -+ * AR934X GMAC Interface -+ */ -+#define AR934X_GMAC_REG_ETH_CFG 0x00 -+ -+#define AR934X_ETH_CFG_RGMII_GMAC0 BIT(0) -+#define AR934X_ETH_CFG_MII_GMAC0 BIT(1) -+#define AR934X_ETH_CFG_GMII_GMAC0 BIT(2) -+#define AR934X_ETH_CFG_SW_ONLY_MODE BIT(6) -+#define AR934X_ETH_CFG_SW_PHY_SWAP BIT(7) -+ - #endif /* __ASM_MACH_AR71XX_REGS_H */ --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0023-MIPS-ath79-add-Mikrotik-rb4xx-device-support.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0023-MIPS-ath79-add-Mikrotik-rb4xx-device-support.patch deleted file mode 100644 index 67d390432..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0023-MIPS-ath79-add-Mikrotik-rb4xx-device-support.patch +++ /dev/null @@ -1,536 +0,0 @@ -From 7f5193750c4fb525ab7bd0610d05631b1dfbd8bb Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Wed, 14 May 2014 03:10:28 +0200 -Subject: [PATCH] MIPS: ath79: add Mikrotik rb4xx device support - ---- - arch/mips/ath79/Kconfig | 8 + - arch/mips/ath79/Makefile | 1 + - arch/mips/ath79/mach-rb4xx.c | 465 +++++++++++++++++++++++++++++++++++++++++++ - arch/mips/ath79/machtypes.h | 9 + - 4 files changed, 483 insertions(+) - create mode 100644 arch/mips/ath79/mach-rb4xx.c - -diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig -index 52cefd7..7863079 100644 ---- a/arch/mips/ath79/Kconfig -+++ b/arch/mips/ath79/Kconfig -@@ -61,6 +61,14 @@ config ATH79_MACH_PB44 - Say 'Y' here if you want your kernel to support the - Atheros PB44 reference board. - -+config ATH79_MACH_RB4XX -+ bool "MikroTik RouterBOARD 4xx series support" -+ select SOC_AR71XX -+ select ATH79_DEV_ETH -+ select ATH79_DEV_GPIO_BUTTONS -+ select ATH79_DEV_LEDS_GPIO -+ select ATH79_DEV_USB -+ - config ATH79_MACH_UBNT_XM - bool "Ubiquiti Networks XM (rev 1.0) board" - select SOC_AR724X -diff --git a/arch/mips/ath79/Makefile b/arch/mips/ath79/Makefile -index 05485da..2b0e01b 100644 ---- a/arch/mips/ath79/Makefile -+++ b/arch/mips/ath79/Makefile -@@ -32,4 +32,5 @@ obj-$(CONFIG_ATH79_MACH_AP136) += mach-ap136.o - obj-$(CONFIG_ATH79_MACH_AP81) += mach-ap81.o - obj-$(CONFIG_ATH79_MACH_DB120) += mach-db120.o - obj-$(CONFIG_ATH79_MACH_PB44) += mach-pb44.o -+obj-$(CONFIG_ATH79_MACH_RB4XX) += mach-rb4xx.o - obj-$(CONFIG_ATH79_MACH_UBNT_XM) += mach-ubnt-xm.o -diff --git a/arch/mips/ath79/mach-rb4xx.c b/arch/mips/ath79/mach-rb4xx.c -new file mode 100644 -index 0000000..1a61b45 ---- /dev/null -+++ b/arch/mips/ath79/mach-rb4xx.c -@@ -0,0 +1,465 @@ -+/* -+ * MikroTik RouterBOARD 4xx series support -+ * -+ * Copyright (C) 2008-2012 Gabor Juhos <juhosg@openwrt.org> -+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include <linux/platform_device.h> -+#include <linux/irq.h> -+#include <linux/mdio-gpio.h> -+#include <linux/mmc/host.h> -+#include <linux/spi/spi.h> -+#include <linux/spi/flash.h> -+#include <linux/spi/mmc_spi.h> -+#include <linux/mtd/mtd.h> -+#include <linux/mtd/partitions.h> -+ -+#include <asm/mach-ath79/ar71xx_regs.h> -+#include <asm/mach-ath79/ath79.h> -+#include <asm/mach-ath79/rb4xx_cpld.h> -+ -+#include "common.h" -+#include "dev-eth.h" -+#include "dev-gpio-buttons.h" -+#include "dev-leds-gpio.h" -+#include "dev-usb.h" -+#include "machtypes.h" -+#include "pci.h" -+ -+#define RB4XX_GPIO_USER_LED 4 -+#define RB4XX_GPIO_RESET_SWITCH 7 -+ -+#define RB4XX_GPIO_CPLD_BASE 32 -+#define RB4XX_GPIO_CPLD_LED1 (RB4XX_GPIO_CPLD_BASE + CPLD_GPIO_nLED1) -+#define RB4XX_GPIO_CPLD_LED2 (RB4XX_GPIO_CPLD_BASE + CPLD_GPIO_nLED2) -+#define RB4XX_GPIO_CPLD_LED3 (RB4XX_GPIO_CPLD_BASE + CPLD_GPIO_nLED3) -+#define RB4XX_GPIO_CPLD_LED4 (RB4XX_GPIO_CPLD_BASE + CPLD_GPIO_nLED4) -+#define RB4XX_GPIO_CPLD_LED5 (RB4XX_GPIO_CPLD_BASE + CPLD_GPIO_nLED5) -+ -+#define RB4XX_KEYS_POLL_INTERVAL 20 /* msecs */ -+#define RB4XX_KEYS_DEBOUNCE_INTERVAL (3 * RB4XX_KEYS_POLL_INTERVAL) -+ -+static struct gpio_led rb4xx_leds_gpio[] __initdata = { -+ { -+ .name = "rb4xx:yellow:user", -+ .gpio = RB4XX_GPIO_USER_LED, -+ .active_low = 0, -+ }, { -+ .name = "rb4xx:green:led1", -+ .gpio = RB4XX_GPIO_CPLD_LED1, -+ .active_low = 1, -+ }, { -+ .name = "rb4xx:green:led2", -+ .gpio = RB4XX_GPIO_CPLD_LED2, -+ .active_low = 1, -+ }, { -+ .name = "rb4xx:green:led3", -+ .gpio = RB4XX_GPIO_CPLD_LED3, -+ .active_low = 1, -+ }, { -+ .name = "rb4xx:green:led4", -+ .gpio = RB4XX_GPIO_CPLD_LED4, -+ .active_low = 1, -+ }, { -+ .name = "rb4xx:green:led5", -+ .gpio = RB4XX_GPIO_CPLD_LED5, -+ .active_low = 0, -+ }, -+}; -+ -+static struct gpio_keys_button rb4xx_gpio_keys[] __initdata = { -+ { -+ .desc = "reset_switch", -+ .type = EV_KEY, -+ .code = KEY_RESTART, -+ .debounce_interval = RB4XX_KEYS_DEBOUNCE_INTERVAL, -+ .gpio = RB4XX_GPIO_RESET_SWITCH, -+ .active_low = 1, -+ } -+}; -+ -+static struct platform_device rb4xx_nand_device = { -+ .name = "rb4xx-nand", -+ .id = -1, -+}; -+ -+static struct ath79_pci_irq rb4xx_pci_irqs[] __initdata = { -+ { -+ .slot = 17, -+ .pin = 1, -+ .irq = ATH79_PCI_IRQ(2), -+ }, { -+ .slot = 18, -+ .pin = 1, -+ .irq = ATH79_PCI_IRQ(0), -+ }, { -+ .slot = 18, -+ .pin = 2, -+ .irq = ATH79_PCI_IRQ(1), -+ }, { -+ .slot = 19, -+ .pin = 1, -+ .irq = ATH79_PCI_IRQ(1), -+ }, { -+ .slot = 19, -+ .pin = 2, -+ .irq = ATH79_PCI_IRQ(2), -+ }, { -+ .slot = 20, -+ .pin = 1, -+ .irq = ATH79_PCI_IRQ(2), -+ }, { -+ .slot = 20, -+ .pin = 2, -+ .irq = ATH79_PCI_IRQ(0), -+ }, { -+ .slot = 21, -+ .pin = 1, -+ .irq = ATH79_PCI_IRQ(0), -+ }, { -+ .slot = 22, -+ .pin = 1, -+ .irq = ATH79_PCI_IRQ(1), -+ }, { -+ .slot = 22, -+ .pin = 2, -+ .irq = ATH79_PCI_IRQ(2), -+ }, { -+ .slot = 23, -+ .pin = 1, -+ .irq = ATH79_PCI_IRQ(2), -+ }, { -+ .slot = 23, -+ .pin = 2, -+ .irq = ATH79_PCI_IRQ(0), -+ } -+}; -+ -+static struct mtd_partition rb4xx_partitions[] = { -+ { -+ .name = "routerboot", -+ .offset = 0, -+ .size = 0x0b000, -+ .mask_flags = MTD_WRITEABLE, -+ }, { -+ .name = "hard_config", -+ .offset = 0x0b000, -+ .size = 0x01000, -+ .mask_flags = MTD_WRITEABLE, -+ }, { -+ .name = "bios", -+ .offset = 0x0d000, -+ .size = 0x02000, -+ .mask_flags = MTD_WRITEABLE, -+ }, { -+ .name = "soft_config", -+ .offset = 0x0f000, -+ .size = 0x01000, -+ } -+}; -+ -+static struct flash_platform_data rb4xx_flash_data = { -+ .type = "pm25lv512", -+ .parts = rb4xx_partitions, -+ .nr_parts = ARRAY_SIZE(rb4xx_partitions), -+}; -+ -+static struct rb4xx_cpld_platform_data rb4xx_cpld_data = { -+ .gpio_base = RB4XX_GPIO_CPLD_BASE, -+}; -+ -+static struct mmc_spi_platform_data rb4xx_mmc_data = { -+ .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, -+}; -+ -+static struct spi_board_info rb4xx_spi_info[] = { -+ { -+ .bus_num = 0, -+ .chip_select = 0, -+ .max_speed_hz = 25000000, -+ .modalias = "m25p80", -+ .platform_data = &rb4xx_flash_data, -+ }, { -+ .bus_num = 0, -+ .chip_select = 1, -+ .max_speed_hz = 25000000, -+ .modalias = "spi-rb4xx-cpld", -+ .platform_data = &rb4xx_cpld_data, -+ } -+}; -+ -+static struct spi_board_info rb4xx_microsd_info[] = { -+ { -+ .bus_num = 0, -+ .chip_select = 2, -+ .max_speed_hz = 25000000, -+ .modalias = "mmc_spi", -+ .platform_data = &rb4xx_mmc_data, -+ } -+}; -+ -+ -+static struct resource rb4xx_spi_resources[] = { -+ { -+ .start = AR71XX_SPI_BASE, -+ .end = AR71XX_SPI_BASE + AR71XX_SPI_SIZE - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+}; -+ -+static struct platform_device rb4xx_spi_device = { -+ .name = "rb4xx-spi", -+ .id = -1, -+ .resource = rb4xx_spi_resources, -+ .num_resources = ARRAY_SIZE(rb4xx_spi_resources), -+}; -+ -+static void __init rb4xx_generic_setup(void) -+{ -+ ath79_gpio_function_enable(AR71XX_GPIO_FUNC_SPI_CS1_EN | -+ AR71XX_GPIO_FUNC_SPI_CS2_EN); -+ -+ ath79_register_leds_gpio(-1, ARRAY_SIZE(rb4xx_leds_gpio), -+ rb4xx_leds_gpio); -+ -+ ath79_register_gpio_keys_polled(-1, RB4XX_KEYS_POLL_INTERVAL, -+ ARRAY_SIZE(rb4xx_gpio_keys), -+ rb4xx_gpio_keys); -+ -+ spi_register_board_info(rb4xx_spi_info, ARRAY_SIZE(rb4xx_spi_info)); -+ platform_device_register(&rb4xx_spi_device); -+ platform_device_register(&rb4xx_nand_device); -+} -+ -+static void __init rb411_setup(void) -+{ -+ rb4xx_generic_setup(); -+ spi_register_board_info(rb4xx_microsd_info, -+ ARRAY_SIZE(rb4xx_microsd_info)); -+ -+ ath79_register_mdio(0, 0xfffffffc); -+ -+ ath79_init_mac(ath79_eth0_data.mac_addr, ath79_mac_base, 0); -+ ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII; -+ ath79_eth0_data.phy_mask = 0x00000003; -+ -+ ath79_register_eth(0); -+ -+ ath79_pci_set_irq_map(ARRAY_SIZE(rb4xx_pci_irqs), rb4xx_pci_irqs); -+ ath79_register_pci(); -+} -+ -+MIPS_MACHINE(ATH79_MACH_RB_411, "411", "MikroTik RouterBOARD 411/A/AH", -+ rb411_setup); -+ -+static void __init rb411u_setup(void) -+{ -+ rb411_setup(); -+ ath79_register_usb(); -+} -+ -+MIPS_MACHINE(ATH79_MACH_RB_411U, "411U", "MikroTik RouterBOARD 411U", -+ rb411u_setup); -+ -+#define RB433_LAN_PHYMASK BIT(0) -+#define RB433_WAN_PHYMASK BIT(4) -+#define RB433_MDIO_PHYMASK (RB433_LAN_PHYMASK | RB433_WAN_PHYMASK) -+ -+static void __init rb433_setup(void) -+{ -+ rb4xx_generic_setup(); -+ spi_register_board_info(rb4xx_microsd_info, -+ ARRAY_SIZE(rb4xx_microsd_info)); -+ -+ ath79_register_mdio(0, ~RB433_MDIO_PHYMASK); -+ -+ ath79_init_mac(ath79_eth0_data.mac_addr, ath79_mac_base, 1); -+ ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII; -+ ath79_eth0_data.phy_mask = RB433_LAN_PHYMASK; -+ -+ ath79_init_mac(ath79_eth1_data.mac_addr, ath79_mac_base, 0); -+ ath79_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; -+ ath79_eth1_data.phy_mask = RB433_WAN_PHYMASK; -+ -+ ath79_register_eth(1); -+ ath79_register_eth(0); -+ -+ ath79_pci_set_irq_map(ARRAY_SIZE(rb4xx_pci_irqs), rb4xx_pci_irqs); -+ ath79_register_pci(); -+} -+ -+MIPS_MACHINE(ATH79_MACH_RB_433, "433", "MikroTik RouterBOARD 433/AH", -+ rb433_setup); -+ -+static void __init rb433u_setup(void) -+{ -+ rb433_setup(); -+ ath79_register_usb(); -+} -+ -+MIPS_MACHINE(ATH79_MACH_RB_433U, "433U", "MikroTik RouterBOARD 433UAH", -+ rb433u_setup); -+ -+static void __init rb435g_setup(void) -+{ -+ rb4xx_generic_setup(); -+ -+ spi_register_board_info(rb4xx_microsd_info, -+ ARRAY_SIZE(rb4xx_microsd_info)); -+ -+ ath79_register_mdio(0, ~RB433_MDIO_PHYMASK); -+ -+ ath79_init_mac(ath79_eth0_data.mac_addr, ath79_mac_base, 1); -+ ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII; -+ ath79_eth0_data.phy_mask = RB433_LAN_PHYMASK; -+ -+ ath79_init_mac(ath79_eth1_data.mac_addr, ath79_mac_base, 0); -+ ath79_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII; -+ ath79_eth1_data.phy_mask = RB433_WAN_PHYMASK; -+ -+ ath79_register_eth(1); -+ ath79_register_eth(0); -+ -+ ath79_pci_set_irq_map(ARRAY_SIZE(rb4xx_pci_irqs), rb4xx_pci_irqs); -+ ath79_register_pci(); -+ -+ ath79_register_usb(); -+} -+ -+MIPS_MACHINE(ATH79_MACH_RB_435G, "435G", "MikroTik RouterBOARD 435G", -+ rb435g_setup); -+ -+#define RB450_LAN_PHYMASK BIT(0) -+#define RB450_WAN_PHYMASK BIT(4) -+#define RB450_MDIO_PHYMASK (RB450_LAN_PHYMASK | RB450_WAN_PHYMASK) -+ -+static void __init rb450_generic_setup(int gige) -+{ -+ rb4xx_generic_setup(); -+ ath79_register_mdio(0, ~RB450_MDIO_PHYMASK); -+ -+ ath79_init_mac(ath79_eth0_data.mac_addr, ath79_mac_base, 1); -+ ath79_eth0_data.phy_if_mode = (gige) ? -+ PHY_INTERFACE_MODE_RGMII : PHY_INTERFACE_MODE_MII; -+ ath79_eth0_data.phy_mask = RB450_LAN_PHYMASK; -+ -+ ath79_init_mac(ath79_eth1_data.mac_addr, ath79_mac_base, 0); -+ ath79_eth1_data.phy_if_mode = (gige) ? -+ PHY_INTERFACE_MODE_RGMII : PHY_INTERFACE_MODE_RMII; -+ ath79_eth1_data.phy_mask = RB450_WAN_PHYMASK; -+ -+ ath79_register_eth(1); -+ ath79_register_eth(0); -+} -+ -+static void __init rb450_setup(void) -+{ -+ rb450_generic_setup(0); -+} -+ -+MIPS_MACHINE(ATH79_MACH_RB_450, "450", "MikroTik RouterBOARD 450", -+ rb450_setup); -+ -+static void __init rb450g_setup(void) -+{ -+ rb450_generic_setup(1); -+ spi_register_board_info(rb4xx_microsd_info, -+ ARRAY_SIZE(rb4xx_microsd_info)); -+} -+ -+MIPS_MACHINE(ATH79_MACH_RB_450G, "450G", "MikroTik RouterBOARD 450G", -+ rb450g_setup); -+ -+static void __init rb493_setup(void) -+{ -+ rb4xx_generic_setup(); -+ -+ ath79_register_mdio(0, 0x3fffff00); -+ -+ ath79_init_mac(ath79_eth0_data.mac_addr, ath79_mac_base, 0); -+ ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII; -+ ath79_eth0_data.speed = SPEED_100; -+ ath79_eth0_data.duplex = DUPLEX_FULL; -+ -+ ath79_init_mac(ath79_eth1_data.mac_addr, ath79_mac_base, 1); -+ ath79_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; -+ ath79_eth1_data.phy_mask = 0x00000001; -+ -+ ath79_register_eth(0); -+ ath79_register_eth(1); -+ -+ ath79_pci_set_irq_map(ARRAY_SIZE(rb4xx_pci_irqs), rb4xx_pci_irqs); -+ ath79_register_pci(); -+} -+ -+MIPS_MACHINE(ATH79_MACH_RB_493, "493", "MikroTik RouterBOARD 493/AH", -+ rb493_setup); -+ -+#define RB493G_GPIO_MDIO_MDC 7 -+#define RB493G_GPIO_MDIO_DATA 8 -+ -+#define RB493G_MDIO_PHYMASK BIT(0) -+ -+static struct mdio_gpio_platform_data rb493g_mdio_data = { -+ .mdc = RB493G_GPIO_MDIO_MDC, -+ .mdio = RB493G_GPIO_MDIO_DATA, -+ -+ .phy_mask = ~RB493G_MDIO_PHYMASK, -+}; -+ -+static struct platform_device rb493g_mdio_device = { -+ .name = "mdio-gpio", -+ .id = -1, -+ .dev = { -+ .platform_data = &rb493g_mdio_data, -+ }, -+}; -+ -+static void __init rb493g_setup(void) -+{ -+ ath79_gpio_function_enable(AR71XX_GPIO_FUNC_SPI_CS1_EN | -+ AR71XX_GPIO_FUNC_SPI_CS2_EN); -+ -+ ath79_register_leds_gpio(-1, ARRAY_SIZE(rb4xx_leds_gpio), -+ rb4xx_leds_gpio); -+ -+ spi_register_board_info(rb4xx_spi_info, ARRAY_SIZE(rb4xx_spi_info)); -+ spi_register_board_info(rb4xx_microsd_info, -+ ARRAY_SIZE(rb4xx_microsd_info)); -+ -+ platform_device_register(&rb4xx_spi_device); -+ platform_device_register(&rb4xx_nand_device); -+ -+ ath79_register_mdio(0, ~RB493G_MDIO_PHYMASK); -+ -+ ath79_init_mac(ath79_eth0_data.mac_addr, ath79_mac_base, 0); -+ ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII; -+ ath79_eth0_data.phy_mask = RB493G_MDIO_PHYMASK; -+ ath79_eth0_data.speed = SPEED_1000; -+ ath79_eth0_data.duplex = DUPLEX_FULL; -+ -+ ath79_init_mac(ath79_eth1_data.mac_addr, ath79_mac_base, 1); -+ ath79_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII; -+ ath79_eth1_data.mii_bus_dev = &rb493g_mdio_device.dev; -+ ath79_eth1_data.phy_mask = RB493G_MDIO_PHYMASK; -+ ath79_eth1_data.speed = SPEED_1000; -+ ath79_eth1_data.duplex = DUPLEX_FULL; -+ -+ platform_device_register(&rb493g_mdio_device); -+ -+ ath79_register_eth(1); -+ ath79_register_eth(0); -+ -+ ath79_register_usb(); -+ -+ ath79_pci_set_irq_map(ARRAY_SIZE(rb4xx_pci_irqs), rb4xx_pci_irqs); -+ ath79_register_pci(); -+} -+ -+MIPS_MACHINE(ATH79_MACH_RB_493G, "493G", "MikroTik RouterBOARD 493G", -+ rb493g_setup); -diff --git a/arch/mips/ath79/machtypes.h b/arch/mips/ath79/machtypes.h -index 2625405..7630954 100644 ---- a/arch/mips/ath79/machtypes.h -+++ b/arch/mips/ath79/machtypes.h -@@ -21,6 +21,15 @@ enum ath79_mach_type { - ATH79_MACH_AP81, /* Atheros AP81 reference board */ - ATH79_MACH_DB120, /* Atheros DB120 reference board */ - ATH79_MACH_PB44, /* Atheros PB44 reference board */ -+ ATH79_MACH_RB_411, /* MikroTik RouterBOARD 411/411A/411AH */ -+ ATH79_MACH_RB_411U, /* MikroTik RouterBOARD 411U */ -+ ATH79_MACH_RB_433, /* MikroTik RouterBOARD 433/433AH */ -+ ATH79_MACH_RB_433U, /* MikroTik RouterBOARD 433UAH */ -+ ATH79_MACH_RB_435G, /* MikroTik RouterBOARD 435G */ -+ ATH79_MACH_RB_450G, /* MikroTik RouterBOARD 450G */ -+ ATH79_MACH_RB_450, /* MikroTik RouterBOARD 450 */ -+ ATH79_MACH_RB_493, /* Mikrotik RouterBOARD 493/493AH */ -+ ATH79_MACH_RB_493G, /* Mikrotik RouterBOARD 493G */ - ATH79_MACH_UBNT_XM, /* Ubiquiti Networks XM board rev 1.0 */ - }; - --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0024-various-fixups-for-Werror.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0024-various-fixups-for-Werror.patch deleted file mode 100644 index 77883846d..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0024-various-fixups-for-Werror.patch +++ /dev/null @@ -1,105 +0,0 @@ -From 45bdbeaf12f96a95bda6016a2aa943ae2dfceb96 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Fri, 16 May 2014 04:37:17 +0200 -Subject: [PATCH] various fixups for -Werror - ---- - arch/mips/ath79/common.c | 4 ++-- - arch/mips/ath79/dev-eth.c | 8 ++++---- - drivers/net/phy/swconfig.c | 18 +----------------- - 3 files changed, 7 insertions(+), 23 deletions(-) - -diff --git a/arch/mips/ath79/common.c b/arch/mips/ath79/common.c -index eb3966c..def54c2 100644 ---- a/arch/mips/ath79/common.c -+++ b/arch/mips/ath79/common.c -@@ -59,7 +59,7 @@ EXPORT_SYMBOL_GPL(ath79_ddr_wb_flush); - void ath79_device_reset_set(u32 mask) - { - unsigned long flags; -- u32 reg; -+ u32 reg = 0; - u32 t; - - if (soc_is_ar71xx()) -@@ -87,7 +87,7 @@ EXPORT_SYMBOL_GPL(ath79_device_reset_set); - void ath79_device_reset_clear(u32 mask) - { - unsigned long flags; -- u32 reg; -+ u32 reg = 0; - u32 t; - - if (soc_is_ar71xx()) -diff --git a/arch/mips/ath79/dev-eth.c b/arch/mips/ath79/dev-eth.c -index 21feeb9..879f1cd 100644 ---- a/arch/mips/ath79/dev-eth.c -+++ b/arch/mips/ath79/dev-eth.c -@@ -121,7 +121,7 @@ static void __init ath79_mii_ctrl_set_if(unsigned int reg, - static void ath79_mii_ctrl_set_speed(unsigned int reg, unsigned int speed) - { - void __iomem *base; -- unsigned int mii_speed; -+ unsigned int mii_speed = 0; - u32 t; - - switch (speed) { -@@ -271,8 +271,8 @@ struct ath79_eth_pll_data ath79_eth1_pll_data; - - static u32 ath79_get_eth_pll(unsigned int mac, int speed) - { -- struct ath79_eth_pll_data *pll_data; -- u32 pll_val; -+ struct ath79_eth_pll_data *pll_data = NULL; -+ u32 pll_val = 0; - - switch (mac) { - case 0: -@@ -511,7 +511,7 @@ struct ag71xx_switch_platform_data ath79_switch_data; - static void __init ath79_init_eth_pll_data(unsigned int id) - { - struct ath79_eth_pll_data *pll_data; -- u32 pll_10, pll_100, pll_1000; -+ u32 pll_10 = 0, pll_100 = 0, pll_1000 = 0; - - switch (id) { - case 0: -diff --git a/drivers/net/phy/swconfig.c b/drivers/net/phy/swconfig.c -index c043ee4..c4d7689 100644 ---- a/drivers/net/phy/swconfig.c -+++ b/drivers/net/phy/swconfig.c -@@ -1107,30 +1107,14 @@ EXPORT_SYMBOL_GPL(unregister_switch); - static int __init - swconfig_init(void) - { -- int i, err; -+ int err; - - INIT_LIST_HEAD(&swdevs); -- --#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)) -- err = genl_register_family(&switch_fam); -- if (err) -- return err; - -- for (i = 0; i < ARRAY_SIZE(swconfig_ops); i++) { -- err = genl_register_ops(&switch_fam, &swconfig_ops[i]); -- if (err) -- goto unregister; -- } --#else - err = genl_register_family_with_ops(&switch_fam, swconfig_ops); - if (err) - return err; --#endif - return 0; -- --unregister: -- genl_unregister_family(&switch_fam); -- return err; - } - - static void __exit --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0025-rb4xx_nand-add-partition-for-cfgfs.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0025-rb4xx_nand-add-partition-for-cfgfs.patch deleted file mode 100644 index 7d9d85f62..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0025-rb4xx_nand-add-partition-for-cfgfs.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 8cbc2ee92ec6dbed4a806cedffc6919b6b90275b Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Tue, 3 Jun 2014 00:32:22 +0200 -Subject: [PATCH] rb4xx_nand: add partition for cfgfs - ---- - drivers/mtd/nand/rb4xx_nand.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/drivers/mtd/nand/rb4xx_nand.c b/drivers/mtd/nand/rb4xx_nand.c -index 5b9841b..603d001 100644 ---- a/drivers/mtd/nand/rb4xx_nand.c -+++ b/drivers/mtd/nand/rb4xx_nand.c -@@ -65,6 +65,11 @@ static struct mtd_partition rb4xx_nand_partitions[] = { - .size = (4 * 1024 * 1024) - (256 * 1024), - }, - { -+ .name = "cfgfs", -+ .offset = MTDPART_OFS_NXTBLK, -+ .size = 0x400000, -+ }, -+ { - .name = "rootfs", - .offset = MTDPART_OFS_NXTBLK, - .size = MTDPART_SIZ_FULL, --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.8/0026-various-fixups-for-ath5k-fixing-system-freezes.patch b/target/mips/mikrotik-rb4xx/patches/3.14.8/0026-various-fixups-for-ath5k-fixing-system-freezes.patch deleted file mode 100644 index 4b17700d8..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.8/0026-various-fixups-for-ath5k-fixing-system-freezes.patch +++ /dev/null @@ -1,108 +0,0 @@ -From 95945fe79069ee6b7ccce2b14fb9f8b93db33918 Mon Sep 17 00:00:00 2001 -From: Phil Sutter <phil@nwl.cc> -Date: Sun, 15 Jun 2014 18:29:27 +0200 -Subject: [PATCH] various fixups for ath5k, fixing system freezes - ---- - drivers/net/wireless/ath/ath5k/base.c | 3 +++ - drivers/net/wireless/ath/ath5k/dma.c | 9 +++++++++ - drivers/net/wireless/ath/ath5k/initvals.c | 6 ++++++ - drivers/net/wireless/ath/ath5k/phy.c | 4 ++-- - drivers/net/wireless/ath/ath5k/reset.c | 2 ++ - 5 files changed, 22 insertions(+), 2 deletions(-) - -diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c -index ef35da8..4b18434 100644 ---- a/drivers/net/wireless/ath/ath5k/base.c -+++ b/drivers/net/wireless/ath/ath5k/base.c -@@ -751,6 +751,9 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, - bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len, - DMA_TO_DEVICE); - -+ if (dma_mapping_error(ah->dev, bf->skbaddr)) -+ return -ENOSPC; -+ - ieee80211_get_tx_rates(info->control.vif, (control) ? control->sta : NULL, skb, bf->rates, - ARRAY_SIZE(bf->rates)); - -diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c -index e6c52f7..72bf600 100644 ---- a/drivers/net/wireless/ath/ath5k/dma.c -+++ b/drivers/net/wireless/ath/ath5k/dma.c -@@ -869,10 +869,19 @@ ath5k_hw_dma_init(struct ath5k_hw *ah) - * guess we can tweak it and see how it goes ;-) - */ - if (ah->ah_version != AR5K_AR5210) { -+#if !defined(CONFIG_ATHEROS_AR71XX) && !defined(CONFIG_ATH79) - AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, - AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B); - AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG, - AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B); -+#else -+ /* WAR for AR71xx PCI bug */ -+ AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, -+ AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B); -+ AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG, -+ AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_4B); -+#endif -+ - } - - /* Pre-enable interrupts on 5211/5212*/ -diff --git a/drivers/net/wireless/ath/ath5k/initvals.c b/drivers/net/wireless/ath/ath5k/initvals.c -index ee1c2fa..ba84ab5 100644 ---- a/drivers/net/wireless/ath/ath5k/initvals.c -+++ b/drivers/net/wireless/ath/ath5k/initvals.c -@@ -62,8 +62,14 @@ static const struct ath5k_ini ar5210_ini[] = { - { AR5K_IMR, 0 }, - { AR5K_IER, AR5K_IER_DISABLE }, - { AR5K_BSR, 0, AR5K_INI_READ }, -+#if !defined(CONFIG_ATHEROS_AR71XX) && !defined(CONFIG_ATH79) - { AR5K_TXCFG, AR5K_DMASIZE_128B }, - { AR5K_RXCFG, AR5K_DMASIZE_128B }, -+#else -+ /* WAR for AR71xx PCI bug */ -+ { AR5K_TXCFG, AR5K_DMASIZE_128B }, -+ { AR5K_RXCFG, AR5K_DMASIZE_4B }, -+#endif - { AR5K_CFG, AR5K_INIT_CFG }, - { AR5K_TOPS, 8 }, - { AR5K_RXNOFRM, 8 }, -diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c -index 1a2973b..0fce1c7 100644 ---- a/drivers/net/wireless/ath/ath5k/phy.c -+++ b/drivers/net/wireless/ath/ath5k/phy.c -@@ -3709,8 +3709,8 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, - AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP), - AR5K_TPC); - } else { -- ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX | -- AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); -+ ath5k_hw_reg_write(ah, AR5K_TUNE_MAX_TXPOWER, -+ AR5K_PHY_TXPOWER_RATE_MAX); - } - - return 0; -diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c -index a3399c4..66d0ecc 100644 ---- a/drivers/net/wireless/ath/ath5k/reset.c -+++ b/drivers/net/wireless/ath/ath5k/reset.c -@@ -1154,6 +1154,7 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, - tsf_lo = 0; - mode = 0; - -+#if 0 - /* - * Sanity check for fast flag - * Fast channel change only available -@@ -1161,6 +1162,7 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, - */ - if (fast && (ah->ah_radio != AR5K_RF2413) && - (ah->ah_radio != AR5K_RF5413)) -+#endif - fast = false; - - /* Disable sleep clock operation --- -1.8.5.3 - |