From cc0c042aafec40d93b06be3b6d7e94f38c5ac69f Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Mon, 25 May 2015 10:58:22 -0500 Subject: update kernel to 3.14.43 --- .../3.14.40/0001-mtd-add-rb4xx-nand-driver.patch | 351 -- ...htool-ioctl-support-used-by-ag71xx-driver.patch | 80 - .../3.14.40/0003-net-add-ag71xx-mac-driver.patch | 4245 -------------------- ...ivers-link-SPI-drivers-before-MTD-drivers.patch | 27 - ...ious-flags-to-spi_transfer-and-spi_messag.patch | 34 - .../3.14.40/0006-spi-add-rb4xx-SPI-driver.patch | 557 --- .../3.14.40/0007-spi-add-rb4xx-cpld-driver.patch | 548 --- .../3.14.40/0008-gpio-add-GPIO-latch-driver.patch | 290 -- ...0009-spi-export-spi_bitbang_bufs-function.patch | 45 - ...spi-add-type-field-to-spi_transfer-struct.patch | 37 - .../0011-mtd-m25p80-set-SPI-transfer-type.patch | 29 - ...mips-ath79-swizzle-PCI-address-for-ar71xx.patch | 130 - .../3.14.40/0013-net-add-swconfig-support.patch | 1859 --------- ...-add-detach-callback-to-struct-phy_driver.patch | 46 - .../3.14.40/0015-phy-add-ar8216-PHY-support.patch | 3671 ----------------- .../0016-phy-mdio-bitbang-ignore-TA-value.patch | 44 - .../0017-MIPS-ath79-fix-maximum-timeout.patch | 37 - ...PHY-drivers-to-insert-packet-mangle-hooks.patch | 211 - ...9-MIPS-ath79-process-board-cmdline-option.patch | 26 - ...020-spi-ath79-add-fast-flash-read-support.patch | 202 - .../3.14.40/0021-phy-add-mdio-boardinfo.patch | 227 -- ...0022-mips-ath79-add-ath79-ethernet-driver.patch | 1429 ------- ...S-ath79-add-Mikrotik-rb4xx-device-support.patch | 536 --- .../3.14.40/0024-various-fixups-for-Werror.patch | 105 - .../0025-rb4xx_nand-add-partition-for-cfgfs.patch | 28 - ...us-fixups-for-ath5k-fixing-system-freezes.patch | 108 - .../3.14.43/0001-mtd-add-rb4xx-nand-driver.patch | 351 ++ ...htool-ioctl-support-used-by-ag71xx-driver.patch | 80 + .../3.14.43/0003-net-add-ag71xx-mac-driver.patch | 4245 ++++++++++++++++++++ ...ivers-link-SPI-drivers-before-MTD-drivers.patch | 27 + ...ious-flags-to-spi_transfer-and-spi_messag.patch | 34 + .../3.14.43/0006-spi-add-rb4xx-SPI-driver.patch | 557 +++ .../3.14.43/0007-spi-add-rb4xx-cpld-driver.patch | 548 +++ .../3.14.43/0008-gpio-add-GPIO-latch-driver.patch | 290 ++ ...0009-spi-export-spi_bitbang_bufs-function.patch | 45 + ...spi-add-type-field-to-spi_transfer-struct.patch | 37 + .../0011-mtd-m25p80-set-SPI-transfer-type.patch | 29 + ...mips-ath79-swizzle-PCI-address-for-ar71xx.patch | 130 + .../3.14.43/0013-net-add-swconfig-support.patch | 1859 +++++++++ ...-add-detach-callback-to-struct-phy_driver.patch | 46 + .../3.14.43/0015-phy-add-ar8216-PHY-support.patch | 3671 +++++++++++++++++ .../0016-phy-mdio-bitbang-ignore-TA-value.patch | 44 + .../0017-MIPS-ath79-fix-maximum-timeout.patch | 37 + ...PHY-drivers-to-insert-packet-mangle-hooks.patch | 211 + ...9-MIPS-ath79-process-board-cmdline-option.patch | 26 + ...020-spi-ath79-add-fast-flash-read-support.patch | 202 + .../3.14.43/0021-phy-add-mdio-boardinfo.patch | 227 ++ ...0022-mips-ath79-add-ath79-ethernet-driver.patch | 1429 +++++++ ...S-ath79-add-Mikrotik-rb4xx-device-support.patch | 536 +++ .../3.14.43/0024-various-fixups-for-Werror.patch | 105 + .../0025-rb4xx_nand-add-partition-for-cfgfs.patch | 28 + ...us-fixups-for-ath5k-fixing-system-freezes.patch | 108 + 52 files changed, 14902 insertions(+), 14902 deletions(-) delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0001-mtd-add-rb4xx-nand-driver.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0002-phy-add-ethtool-ioctl-support-used-by-ag71xx-driver.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0003-net-add-ag71xx-mac-driver.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0004-drivers-link-SPI-drivers-before-MTD-drivers.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0005-spi-add-various-flags-to-spi_transfer-and-spi_messag.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0006-spi-add-rb4xx-SPI-driver.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0007-spi-add-rb4xx-cpld-driver.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0008-gpio-add-GPIO-latch-driver.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0009-spi-export-spi_bitbang_bufs-function.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0010-spi-add-type-field-to-spi_transfer-struct.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0011-mtd-m25p80-set-SPI-transfer-type.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0012-mips-ath79-swizzle-PCI-address-for-ar71xx.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0013-net-add-swconfig-support.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0014-phy-add-detach-callback-to-struct-phy_driver.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0015-phy-add-ar8216-PHY-support.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0016-phy-mdio-bitbang-ignore-TA-value.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0017-MIPS-ath79-fix-maximum-timeout.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0018-net-allow-PHY-drivers-to-insert-packet-mangle-hooks.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0019-MIPS-ath79-process-board-cmdline-option.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0020-spi-ath79-add-fast-flash-read-support.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0021-phy-add-mdio-boardinfo.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0022-mips-ath79-add-ath79-ethernet-driver.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0023-MIPS-ath79-add-Mikrotik-rb4xx-device-support.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0024-various-fixups-for-Werror.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0025-rb4xx_nand-add-partition-for-cfgfs.patch delete mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.40/0026-various-fixups-for-ath5k-fixing-system-freezes.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0001-mtd-add-rb4xx-nand-driver.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0002-phy-add-ethtool-ioctl-support-used-by-ag71xx-driver.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0003-net-add-ag71xx-mac-driver.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0004-drivers-link-SPI-drivers-before-MTD-drivers.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0005-spi-add-various-flags-to-spi_transfer-and-spi_messag.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0006-spi-add-rb4xx-SPI-driver.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0007-spi-add-rb4xx-cpld-driver.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0008-gpio-add-GPIO-latch-driver.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0009-spi-export-spi_bitbang_bufs-function.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0010-spi-add-type-field-to-spi_transfer-struct.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0011-mtd-m25p80-set-SPI-transfer-type.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0012-mips-ath79-swizzle-PCI-address-for-ar71xx.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0013-net-add-swconfig-support.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0014-phy-add-detach-callback-to-struct-phy_driver.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0015-phy-add-ar8216-PHY-support.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0016-phy-mdio-bitbang-ignore-TA-value.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0017-MIPS-ath79-fix-maximum-timeout.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0018-net-allow-PHY-drivers-to-insert-packet-mangle-hooks.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0019-MIPS-ath79-process-board-cmdline-option.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0020-spi-ath79-add-fast-flash-read-support.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0021-phy-add-mdio-boardinfo.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0022-mips-ath79-add-ath79-ethernet-driver.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0023-MIPS-ath79-add-Mikrotik-rb4xx-device-support.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0024-various-fixups-for-Werror.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0025-rb4xx_nand-add-partition-for-cfgfs.patch create mode 100644 target/mips/mikrotik-rb4xx/patches/3.14.43/0026-various-fixups-for-ath5k-fixing-system-freezes.patch (limited to 'target/mips/mikrotik-rb4xx/patches') diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.40/0001-mtd-add-rb4xx-nand-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0001-mtd-add-rb4xx-nand-driver.patch deleted file mode 100644 index 8199de991..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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 -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#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 "); -+MODULE_AUTHOR("Imre Kaloz "); -+MODULE_LICENSE("GPL v2"); --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.40/0002-phy-add-ethtool-ioctl-support-used-by-ag71xx-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/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.40/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 -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.40/0003-net-add-ag71xx-mac-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0003-net-add-ag71xx-mac-driver.patch deleted file mode 100644 index 1915c184c..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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 -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * 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 -+#include -+#include -+#include -+ -+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 -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+#include -+ -+#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 -+ * Copyright (c) 2010 Felix Fietkau -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#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 -+ * -+ * 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 -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * 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 -+ -+#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 -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * 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 -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * 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 "); -+MODULE_AUTHOR("Imre Kaloz "); -+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 -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * 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 -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * 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.40/0004-drivers-link-SPI-drivers-before-MTD-drivers.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0004-drivers-link-SPI-drivers-before-MTD-drivers.patch deleted file mode 100644 index 824d6ebec..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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.40/0005-spi-add-various-flags-to-spi_transfer-and-spi_messag.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/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.40/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 -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.40/0006-spi-add-rb4xx-SPI-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0006-spi-add-rb4xx-SPI-driver.patch deleted file mode 100644 index 757dc775b..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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 -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#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 "); -+MODULE_LICENSE("GPL v2"); --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.40/0007-spi-add-rb4xx-cpld-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0007-spi-add-rb4xx-cpld-driver.patch deleted file mode 100644 index 452f2e761..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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 -+ * -+ * 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 -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#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 "); -+MODULE_LICENSE("GPL v2"); --- -1.8.5.3 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.40/0008-gpio-add-GPIO-latch-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0008-gpio-add-GPIO-latch-driver.patch deleted file mode 100644 index 188cec3b2..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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 -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+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 "); -+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.40/0009-spi-export-spi_bitbang_bufs-function.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0009-spi-export-spi_bitbang_bufs-function.patch deleted file mode 100644 index dc6af0a9d..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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.40/0010-spi-add-type-field-to-spi_transfer-struct.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/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.40/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 -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.40/0011-mtd-m25p80-set-SPI-transfer-type.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0011-mtd-m25p80-set-SPI-transfer-type.patch deleted file mode 100644 index e2dfad6e0..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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.40/0012-mips-ath79-swizzle-PCI-address-for-ar71xx.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0012-mips-ath79-swizzle-PCI-address-for-ar71xx.patch deleted file mode 100644 index c63489112..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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 -+#include - #include - #include - #include -@@ -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 -+ * -+ * 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.40/0013-net-add-swconfig-support.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0013-net-add-swconfig-support.patch deleted file mode 100644 index 57c112842..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/0013-net-add-swconfig-support.patch +++ /dev/null @@ -1,1859 +0,0 @@ -From fe40f6aba9ba59000ffa681a23ad59e9347346af Mon Sep 17 00:00:00 2001 -From: Phil Sutter -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 -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define SWCONFIG_DEVNAME "switch%d" -+ -+#include "swconfig_leds.c" -+ -+MODULE_AUTHOR("Felix Fietkau "); -+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 -+ * -+ * 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 -+#include -+#include -+#include -+ -+#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 -+ * -+ * 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 -+#include -+ -+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 -+ * -+ * 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 -+#include -+#include -+#include -+#ifndef __KERNEL__ -+#include -+#include -+#include -+#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.40/0014-phy-add-detach-callback-to-struct-phy_driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/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.40/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 -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.40/0015-phy-add-ar8216-PHY-support.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0015-phy-add-ar8216-PHY-support.patch deleted file mode 100644 index 46b2ba467..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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 -+ * Copyright (C) 2011-2012 Gabor Juhos -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 -+ * -+ * 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 -+ * -+ * 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.40/0016-phy-mdio-bitbang-ignore-TA-value.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0016-phy-mdio-bitbang-ignore-TA-value.patch deleted file mode 100644 index fe23f4912..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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.40/0017-MIPS-ath79-fix-maximum-timeout.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0017-MIPS-ath79-fix-maximum-timeout.patch deleted file mode 100644 index 3ca02783d..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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 ---- - 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.40/0018-net-allow-PHY-drivers-to-insert-packet-mangle-hooks.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/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.40/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 -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 - #include - #include -+#include - - #include - #include -@@ -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.40/0019-MIPS-ath79-process-board-cmdline-option.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0019-MIPS-ath79-process-board-cmdline-option.patch deleted file mode 100644 index 13eae3b8c..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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.40/0020-spi-ath79-add-fast-flash-read-support.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0020-spi-ath79-add-fast-flash-read-support.patch deleted file mode 100644 index 8fd174448..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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.40/0021-phy-add-mdio-boardinfo.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0021-phy-add-mdio-boardinfo.patch deleted file mode 100644 index 3ec15e171..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/0021-phy-add-mdio-boardinfo.patch +++ /dev/null @@ -1,227 +0,0 @@ -From b8d5957374dc0e6ec8687c6e1b154ea066d27a5b Mon Sep 17 00:00:00 2001 -From: Phil Sutter -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 -+#include -+#include -+#include -+#include -+#include -+ -+#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 -+ -+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 - -+#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.40/0022-mips-ath79-add-ath79-ethernet-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0022-mips-ath79-add-ath79-ethernet-driver.patch deleted file mode 100644 index a7eff47b1..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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 -+ * Copyright (C) 2008-2012 Gabor Juhos -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#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 -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * 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 -+ -+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 - - #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.40/0023-MIPS-ath79-add-Mikrotik-rb4xx-device-support.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0023-MIPS-ath79-add-Mikrotik-rb4xx-device-support.patch deleted file mode 100644 index 67d390432..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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 -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#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.40/0024-various-fixups-for-Werror.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0024-various-fixups-for-Werror.patch deleted file mode 100644 index 77883846d..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/0024-various-fixups-for-Werror.patch +++ /dev/null @@ -1,105 +0,0 @@ -From 45bdbeaf12f96a95bda6016a2aa943ae2dfceb96 Mon Sep 17 00:00:00 2001 -From: Phil Sutter -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.40/0025-rb4xx_nand-add-partition-for-cfgfs.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0025-rb4xx_nand-add-partition-for-cfgfs.patch deleted file mode 100644 index 7d9d85f62..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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.40/0026-various-fixups-for-ath5k-fixing-system-freezes.patch b/target/mips/mikrotik-rb4xx/patches/3.14.40/0026-various-fixups-for-ath5k-fixing-system-freezes.patch deleted file mode 100644 index 4b17700d8..000000000 --- a/target/mips/mikrotik-rb4xx/patches/3.14.40/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 -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 - diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.43/0001-mtd-add-rb4xx-nand-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0001-mtd-add-rb4xx-nand-driver.patch new file mode 100644 index 000000000..8199de991 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0001-mtd-add-rb4xx-nand-driver.patch @@ -0,0 +1,351 @@ +From 1e692cc0c53202b932eedabd0315107910c5b093 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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 "); ++MODULE_AUTHOR("Imre Kaloz "); ++MODULE_LICENSE("GPL v2"); +-- +1.8.5.3 + diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.43/0002-phy-add-ethtool-ioctl-support-used-by-ag71xx-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0002-phy-add-ethtool-ioctl-support-used-by-ag71xx-driver.patch new file mode 100644 index 000000000..ba7fbfad8 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0002-phy-add-ethtool-ioctl-support-used-by-ag71xx-driver.patch @@ -0,0 +1,80 @@ +From 7b864612a6e3b139a5a607abd0048a19078fe42f Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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.43/0003-net-add-ag71xx-mac-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0003-net-add-ag71xx-mac-driver.patch new file mode 100644 index 000000000..1915c184c --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0003-net-add-ag71xx-mac-driver.patch @@ -0,0 +1,4245 @@ +From c5eb03f91f9185f4813431692f36db3862716a35 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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 ++#include ++#include ++#include ++ ++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 ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++#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 ++ * Copyright (c) 2010 Felix Fietkau ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++ * ++ * 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 ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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 ++ ++#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 ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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 ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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 "); ++MODULE_AUTHOR("Imre Kaloz "); ++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 ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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 ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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.43/0004-drivers-link-SPI-drivers-before-MTD-drivers.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0004-drivers-link-SPI-drivers-before-MTD-drivers.patch new file mode 100644 index 000000000..824d6ebec --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0004-drivers-link-SPI-drivers-before-MTD-drivers.patch @@ -0,0 +1,27 @@ +From 3c367cc533d07353f60110340c110f6d622094b8 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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.43/0005-spi-add-various-flags-to-spi_transfer-and-spi_messag.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0005-spi-add-various-flags-to-spi_transfer-and-spi_messag.patch new file mode 100644 index 000000000..505562fe0 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0005-spi-add-various-flags-to-spi_transfer-and-spi_messag.patch @@ -0,0 +1,34 @@ +From 34dcc540e28cc2253fd3bdaacdd77faf1d42d759 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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.43/0006-spi-add-rb4xx-SPI-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0006-spi-add-rb4xx-SPI-driver.patch new file mode 100644 index 000000000..757dc775b --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0006-spi-add-rb4xx-SPI-driver.patch @@ -0,0 +1,557 @@ +From 97ffc04a7528abe1d84c069d99afb19704d50224 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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 "); ++MODULE_LICENSE("GPL v2"); +-- +1.8.5.3 + diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.43/0007-spi-add-rb4xx-cpld-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0007-spi-add-rb4xx-cpld-driver.patch new file mode 100644 index 000000000..452f2e761 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0007-spi-add-rb4xx-cpld-driver.patch @@ -0,0 +1,548 @@ +From bb8d2a4ebf63bc2f04f15a28c92652700416ff83 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 ++ * ++ * 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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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 "); ++MODULE_LICENSE("GPL v2"); +-- +1.8.5.3 + diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.43/0008-gpio-add-GPIO-latch-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0008-gpio-add-GPIO-latch-driver.patch new file mode 100644 index 000000000..188cec3b2 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0008-gpio-add-GPIO-latch-driver.patch @@ -0,0 +1,290 @@ +From dd93d7e5b6530f1574860776fe6f960c4fd2661d Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++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 "); ++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.43/0009-spi-export-spi_bitbang_bufs-function.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0009-spi-export-spi_bitbang_bufs-function.patch new file mode 100644 index 000000000..dc6af0a9d --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0009-spi-export-spi_bitbang_bufs-function.patch @@ -0,0 +1,45 @@ +From ff81dc67568d5393c30352c6075b43afc9de2329 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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.43/0010-spi-add-type-field-to-spi_transfer-struct.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0010-spi-add-type-field-to-spi_transfer-struct.patch new file mode 100644 index 000000000..2721d3c4e --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0010-spi-add-type-field-to-spi_transfer-struct.patch @@ -0,0 +1,37 @@ +From eaf82ac5fc9272545d4d4fb4582eab69d37e389a Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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.43/0011-mtd-m25p80-set-SPI-transfer-type.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0011-mtd-m25p80-set-SPI-transfer-type.patch new file mode 100644 index 000000000..e2dfad6e0 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0011-mtd-m25p80-set-SPI-transfer-type.patch @@ -0,0 +1,29 @@ +From 531989d989855f673af76ef85300769a8a167405 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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.43/0012-mips-ath79-swizzle-PCI-address-for-ar71xx.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0012-mips-ath79-swizzle-PCI-address-for-ar71xx.patch new file mode 100644 index 000000000..c63489112 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0012-mips-ath79-swizzle-PCI-address-for-ar71xx.patch @@ -0,0 +1,130 @@ +From 0c139cb15774f3c41a0cf6620727e676c874834a Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 ++#include + #include + #include + #include +@@ -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 ++ * ++ * 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.43/0013-net-add-swconfig-support.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0013-net-add-swconfig-support.patch new file mode 100644 index 000000000..57c112842 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0013-net-add-swconfig-support.patch @@ -0,0 +1,1859 @@ +From fe40f6aba9ba59000ffa681a23ad59e9347346af Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SWCONFIG_DEVNAME "switch%d" ++ ++#include "swconfig_leds.c" ++ ++MODULE_AUTHOR("Felix Fietkau "); ++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 ++ * ++ * 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 ++#include ++#include ++#include ++ ++#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 ++ * ++ * 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 ++#include ++ ++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 ++ * ++ * 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 ++#include ++#include ++#include ++#ifndef __KERNEL__ ++#include ++#include ++#include ++#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.43/0014-phy-add-detach-callback-to-struct-phy_driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0014-phy-add-detach-callback-to-struct-phy_driver.patch new file mode 100644 index 000000000..43ca7ce9b --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0014-phy-add-detach-callback-to-struct-phy_driver.patch @@ -0,0 +1,46 @@ +From 76e9965a69ff97c3ca973ca54f145a96023bdeca Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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.43/0015-phy-add-ar8216-PHY-support.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0015-phy-add-ar8216-PHY-support.patch new file mode 100644 index 000000000..46b2ba467 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0015-phy-add-ar8216-PHY-support.patch @@ -0,0 +1,3671 @@ +From 6137bedf972f576765c6e5d4373a488951371609 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ * ++ * 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 ++ * ++ * 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.43/0016-phy-mdio-bitbang-ignore-TA-value.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0016-phy-mdio-bitbang-ignore-TA-value.patch new file mode 100644 index 000000000..fe23f4912 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0016-phy-mdio-bitbang-ignore-TA-value.patch @@ -0,0 +1,44 @@ +From e73f7d9a658c7fc693a9b9c45a1f65c014dd6e40 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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.43/0017-MIPS-ath79-fix-maximum-timeout.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0017-MIPS-ath79-fix-maximum-timeout.patch new file mode 100644 index 000000000..3ca02783d --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0017-MIPS-ath79-fix-maximum-timeout.patch @@ -0,0 +1,37 @@ +From 54d01581baa903adb8515625d98652ed43efba36 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 +--- + 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.43/0018-net-allow-PHY-drivers-to-insert-packet-mangle-hooks.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0018-net-allow-PHY-drivers-to-insert-packet-mangle-hooks.patch new file mode 100644 index 000000000..6a372c8f1 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0018-net-allow-PHY-drivers-to-insert-packet-mangle-hooks.patch @@ -0,0 +1,211 @@ +From ebca842041d737b7441748a17ffd535aab851fce Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 + #include + #include ++#include + + #include + #include +@@ -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.43/0019-MIPS-ath79-process-board-cmdline-option.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0019-MIPS-ath79-process-board-cmdline-option.patch new file mode 100644 index 000000000..13eae3b8c --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0019-MIPS-ath79-process-board-cmdline-option.patch @@ -0,0 +1,26 @@ +From 4c84b317734842765cb1c52624fc569efd9222dc Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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.43/0020-spi-ath79-add-fast-flash-read-support.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0020-spi-ath79-add-fast-flash-read-support.patch new file mode 100644 index 000000000..8fd174448 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0020-spi-ath79-add-fast-flash-read-support.patch @@ -0,0 +1,202 @@ +From c4388a57860440e23c9654f4de2f515433e685a1 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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.43/0021-phy-add-mdio-boardinfo.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0021-phy-add-mdio-boardinfo.patch new file mode 100644 index 000000000..3ec15e171 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0021-phy-add-mdio-boardinfo.patch @@ -0,0 +1,227 @@ +From b8d5957374dc0e6ec8687c6e1b154ea066d27a5b Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++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 + ++#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.43/0022-mips-ath79-add-ath79-ethernet-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0022-mips-ath79-add-ath79-ethernet-driver.patch new file mode 100644 index 000000000..a7eff47b1 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0022-mips-ath79-add-ath79-ethernet-driver.patch @@ -0,0 +1,1429 @@ +From 0c6bdad5f210f5f2fe28dc197ab77a36402bb36e Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 ++ * Copyright (C) 2008-2012 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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 ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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 ++ ++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 + + #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.43/0023-MIPS-ath79-add-Mikrotik-rb4xx-device-support.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0023-MIPS-ath79-add-Mikrotik-rb4xx-device-support.patch new file mode 100644 index 000000000..67d390432 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0023-MIPS-ath79-add-Mikrotik-rb4xx-device-support.patch @@ -0,0 +1,536 @@ +From 7f5193750c4fb525ab7bd0610d05631b1dfbd8bb Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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.43/0024-various-fixups-for-Werror.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0024-various-fixups-for-Werror.patch new file mode 100644 index 000000000..77883846d --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0024-various-fixups-for-Werror.patch @@ -0,0 +1,105 @@ +From 45bdbeaf12f96a95bda6016a2aa943ae2dfceb96 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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.43/0025-rb4xx_nand-add-partition-for-cfgfs.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0025-rb4xx_nand-add-partition-for-cfgfs.patch new file mode 100644 index 000000000..7d9d85f62 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0025-rb4xx_nand-add-partition-for-cfgfs.patch @@ -0,0 +1,28 @@ +From 8cbc2ee92ec6dbed4a806cedffc6919b6b90275b Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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.43/0026-various-fixups-for-ath5k-fixing-system-freezes.patch b/target/mips/mikrotik-rb4xx/patches/3.14.43/0026-various-fixups-for-ath5k-fixing-system-freezes.patch new file mode 100644 index 000000000..4b17700d8 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.43/0026-various-fixups-for-ath5k-fixing-system-freezes.patch @@ -0,0 +1,108 @@ +From 95945fe79069ee6b7ccce2b14fb9f8b93db33918 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +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 + -- cgit v1.2.3