Discussion:
[PATCH 00/16] MIPS: support for the Atheros AR231X SoCs
Sergey Ryazanov
2014-09-28 18:32:59 UTC
Permalink
This patch set contains initial support for the following Atheros SoCs: AR5312,
AR2312, AR2313, AR2315, AR2316, AR2317, AR2318.

- Patches 1 through 14 and patch 16 add support for different parts of AR231x
SoCs.
- Patch 11 provided only for reference, since it should be rewritten to use
spi-nor framework.
- Patch 15 updates ath5k dependecies

The code was successfully tested with AR2313, AR2315 and AR2317 SoCs.

This code has been written by OpenWRT developers and it resided in OpenWRT's
tree for a long time. My work was to cleanup the code and its rebase on the
latest linux-mips tree.

Changes since RFC:
- use dynamic IRQ numbers allocation
- group ath5 related changes in one patch
- group devices registration in separate patch

Sergey Ryazanov (16):
MIPS: ar231x: add common parts
MIPS: ar231x: add basic AR5312 SoC support
MIPS: ar231x: add basic AR2315 SoC support
MIPS: ar231x: add interrupts handling routines
MIPS: ar231x: add early printk support
MIPS: ar231x: add UART support
MIPS: ar231x: add board configuration detection
MIPS: ar231x: add SoC type detection
gpio: add driver for Atheros AR5312 SoC GPIO controller
gpio: add driver for Atheros AR2315 SoC GPIO controller
mtd: add Atheros AR2315 SPI Flash driver
watchdog: add Atheros AR2315 watchdog driver
MIPS: ar231x: register various chip devices
MIPS: ar231x: add AR2315 PCI host controller driver
ath5k: update dependencies
MIPS: ar231x: add Wireless device support

arch/mips/Kbuild.platforms | 1 +
arch/mips/Kconfig | 15 +
arch/mips/ar231x/Kconfig | 18 +
arch/mips/ar231x/Makefile | 16 +
arch/mips/ar231x/Platform | 6 +
arch/mips/ar231x/ar2315.c | 385 ++++++++++++++
arch/mips/ar231x/ar2315.h | 24 +
arch/mips/ar231x/ar5312.c | 392 ++++++++++++++
arch/mips/ar231x/ar5312.h | 24 +
arch/mips/ar231x/board.c | 222 ++++++++
arch/mips/ar231x/devices.c | 120 +++++
arch/mips/ar231x/devices.h | 39 ++
arch/mips/ar231x/early_printk.c | 45 ++
arch/mips/ar231x/prom.c | 31 ++
arch/mips/include/asm/mach-ar231x/ar2315_regs.h | 580 +++++++++++++++++++++
arch/mips/include/asm/mach-ar231x/ar231x.h | 31 ++
.../mips/include/asm/mach-ar231x/ar231x_platform.h | 73 +++
arch/mips/include/asm/mach-ar231x/ar5312_regs.h | 215 ++++++++
.../asm/mach-ar231x/cpu-feature-overrides.h | 84 +++
arch/mips/include/asm/mach-ar231x/dma-coherence.h | 76 +++
arch/mips/include/asm/mach-ar231x/gpio.h | 16 +
arch/mips/include/asm/mach-ar231x/war.h | 25 +
arch/mips/pci/Makefile | 1 +
arch/mips/pci/pci-ar2315.c | 353 +++++++++++++
drivers/gpio/Kconfig | 14 +
drivers/gpio/Makefile | 2 +
drivers/gpio/gpio-ar2315.c | 232 +++++++++
drivers/gpio/gpio-ar5312.c | 121 +++++
drivers/mtd/devices/Kconfig | 5 +
drivers/mtd/devices/Makefile | 1 +
drivers/mtd/devices/ar2315.c | 459 ++++++++++++++++
drivers/mtd/devices/ar2315_spiflash.h | 106 ++++
drivers/net/wireless/ath/ath5k/Kconfig | 10 +-
drivers/net/wireless/ath/ath5k/ath5k.h | 2 +-
drivers/net/wireless/ath/ath5k/base.c | 4 +-
drivers/net/wireless/ath/ath5k/led.c | 4 +-
drivers/watchdog/Kconfig | 8 +
drivers/watchdog/Makefile | 1 +
drivers/watchdog/ar2315-wtd.c | 167 ++++++
39 files changed, 3918 insertions(+), 10 deletions(-)
create mode 100644 arch/mips/ar231x/Kconfig
create mode 100644 arch/mips/ar231x/Makefile
create mode 100644 arch/mips/ar231x/Platform
create mode 100644 arch/mips/ar231x/ar2315.c
create mode 100644 arch/mips/ar231x/ar2315.h
create mode 100644 arch/mips/ar231x/ar5312.c
create mode 100644 arch/mips/ar231x/ar5312.h
create mode 100644 arch/mips/ar231x/board.c
create mode 100644 arch/mips/ar231x/devices.c
create mode 100644 arch/mips/ar231x/devices.h
create mode 100644 arch/mips/ar231x/early_printk.c
create mode 100644 arch/mips/ar231x/prom.c
create mode 100644 arch/mips/include/asm/mach-ar231x/ar2315_regs.h
create mode 100644 arch/mips/include/asm/mach-ar231x/ar231x.h
create mode 100644 arch/mips/include/asm/mach-ar231x/ar231x_platform.h
create mode 100644 arch/mips/include/asm/mach-ar231x/ar5312_regs.h
create mode 100644 arch/mips/include/asm/mach-ar231x/cpu-feature-overrides.h
create mode 100644 arch/mips/include/asm/mach-ar231x/dma-coherence.h
create mode 100644 arch/mips/include/asm/mach-ar231x/gpio.h
create mode 100644 arch/mips/include/asm/mach-ar231x/war.h
create mode 100644 arch/mips/pci/pci-ar2315.c
create mode 100644 drivers/gpio/gpio-ar2315.c
create mode 100644 drivers/gpio/gpio-ar5312.c
create mode 100644 drivers/mtd/devices/ar2315.c
create mode 100644 drivers/mtd/devices/ar2315_spiflash.h
create mode 100644 drivers/watchdog/ar2315-wtd.c
--
1.8.5.5
Sergey Ryazanov
2014-09-28 18:33:08 UTC
Permalink
Atheros AR5312 SoC have a builtin GPIO controller, which could be accessed
via memory mapped registers. This patch adds new driver for them.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
Cc: Linus Walleij <***@linaro.org>
Cc: Alexandre Courbot <***@gmail.com>
Cc: linux-***@vger.kernel.org
---

Changes since RFC:
- move device registration to separate patch

drivers/gpio/Kconfig | 7 +++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-ar5312.c | 121 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 129 insertions(+)
create mode 100644 drivers/gpio/gpio-ar5312.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 9de1515..7ce411b 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -112,6 +112,13 @@ config GPIO_MAX730X

comment "Memory mapped GPIO drivers:"

+config GPIO_AR5312
+ bool "AR5312 SoC GPIO support"
+ default y if SOC_AR5312
+ depends on SOC_AR5312
+ help
+ Say yes here to enable GPIO support for Atheros AR5312/AR2312+ SoCs.
+
config GPIO_CLPS711X
tristate "CLPS711X GPIO support"
depends on ARCH_CLPS711X || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 5d024e3..fae00f4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
+obj-$(CONFIG_GPIO_AR5312) += gpio-ar5312.o
obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
diff --git a/drivers/gpio/gpio-ar5312.c b/drivers/gpio/gpio-ar5312.c
new file mode 100644
index 0000000..27adb61
--- /dev/null
+++ b/drivers/gpio/gpio-ar5312.c
@@ -0,0 +1,121 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003 Atheros Communications, Inc., All Rights Reserved.
+ * Copyright (C) 2006 FON Technology, SL.
+ * Copyright (C) 2006 Imre Kaloz <***@openwrt.org>
+ * Copyright (C) 2006-2009 Felix Fietkau <***@openwrt.org>
+ * Copyright (C) 2012 Alexandros C. Couloumbis <***@ozo.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#define DRIVER_NAME "ar5312-gpio"
+
+#define AR5312_GPIO_DO 0x00 /* output register */
+#define AR5312_GPIO_DI 0x04 /* intput register */
+#define AR5312_GPIO_CR 0x08 /* control register */
+
+#define AR5312_GPIO_CR_M(x) (1 << (x)) /* mask for i/o */
+#define AR5312_GPIO_CR_O(x) (0 << (x)) /* mask for output */
+#define AR5312_GPIO_CR_I(x) (1 << (x)) /* mask for input */
+#define AR5312_GPIO_CR_INT(x) (1 << ((x)+8)) /* mask for interrupt */
+#define AR5312_GPIO_CR_UART(x) (1 << ((x)+16)) /* uart multiplex */
+
+#define AR5312_GPIO_NUM 8
+
+static void __iomem *ar5312_mem;
+
+static inline u32 ar5312_gpio_reg_read(unsigned reg)
+{
+ return __raw_readl(ar5312_mem + reg);
+}
+
+static inline void ar5312_gpio_reg_write(unsigned reg, u32 val)
+{
+ __raw_writel(val, ar5312_mem + reg);
+}
+
+static inline void ar5312_gpio_reg_mask(unsigned reg, u32 mask, u32 val)
+{
+ ar5312_gpio_reg_write(reg, (ar5312_gpio_reg_read(reg) & ~mask) | val);
+}
+
+static int ar5312_gpio_get_val(struct gpio_chip *chip, unsigned gpio)
+{
+ return (ar5312_gpio_reg_read(AR5312_GPIO_DI) >> gpio) & 1;
+}
+
+static void ar5312_gpio_set_val(struct gpio_chip *chip, unsigned gpio, int val)
+{
+ u32 reg = ar5312_gpio_reg_read(AR5312_GPIO_DO);
+
+ reg = val ? reg | (1 << gpio) : reg & ~(1 << gpio);
+ ar5312_gpio_reg_write(AR5312_GPIO_DO, reg);
+}
+
+static int ar5312_gpio_dir_in(struct gpio_chip *chip, unsigned gpio)
+{
+ ar5312_gpio_reg_mask(AR5312_GPIO_CR, 0, 1 << gpio);
+ return 0;
+}
+
+static int ar5312_gpio_dir_out(struct gpio_chip *chip, unsigned gpio, int val)
+{
+ ar5312_gpio_reg_mask(AR5312_GPIO_CR, 1 << gpio, 0);
+ ar5312_gpio_set_val(chip, gpio, val);
+ return 0;
+}
+
+static struct gpio_chip ar5312_gpio_chip = {
+ .label = DRIVER_NAME,
+ .direction_input = ar5312_gpio_dir_in,
+ .direction_output = ar5312_gpio_dir_out,
+ .set = ar5312_gpio_set_val,
+ .get = ar5312_gpio_get_val,
+ .base = 0,
+ .ngpio = AR5312_GPIO_NUM,
+};
+
+static int ar5312_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int ret;
+
+ if (ar5312_mem)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ar5312_mem = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ar5312_mem))
+ return PTR_ERR(ar5312_mem);
+
+ ar5312_gpio_chip.dev = dev;
+ ret = gpiochip_add(&ar5312_gpio_chip);
+ if (ret) {
+ dev_err(dev, "failed to add gpiochip\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver ar5312_gpio_driver = {
+ .probe = ar5312_gpio_probe,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init ar5312_gpio_init(void)
+{
+ return platform_driver_register(&ar5312_gpio_driver);
+}
+subsys_initcall(ar5312_gpio_init);
--
1.8.5.5

--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Linus Walleij
2014-10-15 07:33:01 UTC
Permalink
Post by Sergey Ryazanov
Atheros AR5312 SoC have a builtin GPIO controller, which could be accessed
via memory mapped registers. This patch adds new driver for them.
This driver is extremely simple. You should be able to use
drivers/gpio/gpio-generic.c for this.

NAK for the time being.
Post by Sergey Ryazanov
+#define AR5312_GPIO_CR_INT(x) (1 << ((x)+8)) /* mask for interrupt */
Seems to be unused.
For a MMIO gpiochip using interrupts it's still possible
to use gpio-generic.c as a library-
Post by Sergey Ryazanov
+#define AR5312_GPIO_CR_UART(x) (1 << ((x)+16)) /* uart multiplex */
That sounds like pin control business.

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Sergey Ryazanov
2014-09-28 18:33:09 UTC
Permalink
Atheros AR2315 SoC have a builtin GPIO controller, which could be
accessed via memory mapped registers. This patch adds new driver
for them.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
Cc: Linus Walleij <***@linaro.org>
Cc: Alexandre Courbot <***@gmail.com>
Cc: linux-***@vger.kernel.org
---

Changes since RFC:
- fix chip name, this patch adds AR2315 GPIO controller driver
- use dynamic IRQ numbers allocation
- move device registration to separate patch

drivers/gpio/Kconfig | 7 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-ar2315.c | 232 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 240 insertions(+)
create mode 100644 drivers/gpio/gpio-ar2315.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7ce411b..0ceb4ba 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -112,6 +112,13 @@ config GPIO_MAX730X

comment "Memory mapped GPIO drivers:"

+config GPIO_AR2315
+ bool "AR2315 SoC GPIO support"
+ default y if SOC_AR2315
+ depends on SOC_AR2315
+ help
+ Say yes here to enable GPIO support for Atheros AR2315+ SoCs.
+
config GPIO_AR5312
bool "AR5312 SoC GPIO support"
default y if SOC_AR5312
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index fae00f4..9a3a136 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
+obj-$(CONFIG_GPIO_AR2315) += gpio-ar2315.o
obj-$(CONFIG_GPIO_AR5312) += gpio-ar5312.o
obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
diff --git a/drivers/gpio/gpio-ar2315.c b/drivers/gpio/gpio-ar2315.c
new file mode 100644
index 0000000..2a6caaf
--- /dev/null
+++ b/drivers/gpio/gpio-ar2315.c
@@ -0,0 +1,232 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003 Atheros Communications, Inc., All Rights Reserved.
+ * Copyright (C) 2006 FON Technology, SL.
+ * Copyright (C) 2006 Imre Kaloz <***@openwrt.org>
+ * Copyright (C) 2006 Felix Fietkau <***@openwrt.org>
+ * Copyright (C) 2012 Alexandros C. Couloumbis <***@ozo.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+
+#define DRIVER_NAME "ar2315-gpio"
+
+#define AR2315_GPIO_DI 0x0000
+#define AR2315_GPIO_DO 0x0008
+#define AR2315_GPIO_DIR 0x0010
+#define AR2315_GPIO_INT 0x0018
+
+#define AR2315_GPIO_DIR_M(x) (1 << (x)) /* mask for i/o */
+#define AR2315_GPIO_DIR_O(x) (1 << (x)) /* output */
+#define AR2315_GPIO_DIR_I(x) (0) /* input */
+
+#define AR2315_GPIO_INT_NUM_M 0x3F /* mask for GPIO num */
+#define AR2315_GPIO_INT_TRIG(x) ((x) << 6) /* interrupt trigger */
+#define AR2315_GPIO_INT_TRIG_M (0x3 << 6) /* mask for int trig */
+
+#define AR2315_GPIO_INT_TRIG_OFF 0 /* Triggerring off */
+#define AR2315_GPIO_INT_TRIG_LOW 1 /* Low Level Triggered */
+#define AR2315_GPIO_INT_TRIG_HIGH 2 /* High Level Triggered */
+#define AR2315_GPIO_INT_TRIG_EDGE 3 /* Edge Triggered */
+
+#define AR2315_GPIO_NUM 22
+
+static u32 ar2315_gpio_intmask;
+static u32 ar2315_gpio_intval;
+static unsigned ar2315_gpio_irq_base;
+static void __iomem *ar2315_mem;
+
+static inline u32 ar2315_gpio_reg_read(unsigned reg)
+{
+ return __raw_readl(ar2315_mem + reg);
+}
+
+static inline void ar2315_gpio_reg_write(unsigned reg, u32 val)
+{
+ __raw_writel(val, ar2315_mem + reg);
+}
+
+static inline void ar2315_gpio_reg_mask(unsigned reg, u32 mask, u32 val)
+{
+ ar2315_gpio_reg_write(reg, (ar2315_gpio_reg_read(reg) & ~mask) | val);
+}
+
+static void ar2315_gpio_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ u32 pend;
+ int bit = -1;
+
+ /* only do one gpio interrupt at a time */
+ pend = ar2315_gpio_reg_read(AR2315_GPIO_DI);
+ pend ^= ar2315_gpio_intval;
+ pend &= ar2315_gpio_intmask;
+
+ if (pend) {
+ bit = fls(pend) - 1;
+ pend &= ~(1 << bit);
+ ar2315_gpio_intval ^= (1 << bit);
+ }
+
+ /* Enable interrupt with edge detection */
+ if ((ar2315_gpio_reg_read(AR2315_GPIO_DIR) & AR2315_GPIO_DIR_M(bit)) !=
+ AR2315_GPIO_DIR_I(bit))
+ return;
+
+ if (bit >= 0)
+ generic_handle_irq(ar2315_gpio_irq_base + bit);
+}
+
+static void ar2315_gpio_int_setup(unsigned gpio, int trig)
+{
+ u32 reg = ar2315_gpio_reg_read(AR2315_GPIO_INT);
+
+ reg &= ~(AR2315_GPIO_INT_NUM_M | AR2315_GPIO_INT_TRIG_M);
+ reg |= gpio | AR2315_GPIO_INT_TRIG(trig);
+ ar2315_gpio_reg_write(AR2315_GPIO_INT, reg);
+}
+
+static void ar2315_gpio_irq_unmask(struct irq_data *d)
+{
+ unsigned gpio = d->irq - ar2315_gpio_irq_base;
+ u32 dir = ar2315_gpio_reg_read(AR2315_GPIO_DIR);
+
+ /* Enable interrupt with edge detection */
+ if ((dir & AR2315_GPIO_DIR_M(gpio)) != AR2315_GPIO_DIR_I(gpio))
+ return;
+
+ ar2315_gpio_intmask |= (1 << gpio);
+ ar2315_gpio_int_setup(gpio, AR2315_GPIO_INT_TRIG_EDGE);
+}
+
+static void ar2315_gpio_irq_mask(struct irq_data *d)
+{
+ unsigned gpio = d->irq - ar2315_gpio_irq_base;
+
+ /* Disable interrupt */
+ ar2315_gpio_intmask &= ~(1 << gpio);
+ ar2315_gpio_int_setup(gpio, AR2315_GPIO_INT_TRIG_OFF);
+}
+
+static struct irq_chip ar2315_gpio_irq_chip = {
+ .name = DRIVER_NAME,
+ .irq_unmask = ar2315_gpio_irq_unmask,
+ .irq_mask = ar2315_gpio_irq_mask,
+};
+
+static void ar2315_gpio_irq_init(unsigned irq)
+{
+ unsigned i;
+
+ ar2315_gpio_intval = ar2315_gpio_reg_read(AR2315_GPIO_DI);
+ for (i = 0; i < AR2315_GPIO_NUM; i++) {
+ unsigned _irq = ar2315_gpio_irq_base + i;
+
+ irq_set_chip_and_handler(_irq, &ar2315_gpio_irq_chip,
+ handle_level_irq);
+ }
+ irq_set_chained_handler(irq, ar2315_gpio_irq_handler);
+}
+
+static int ar2315_gpio_get_val(struct gpio_chip *chip, unsigned gpio)
+{
+ return (ar2315_gpio_reg_read(AR2315_GPIO_DI) >> gpio) & 1;
+}
+
+static void ar2315_gpio_set_val(struct gpio_chip *chip, unsigned gpio, int val)
+{
+ u32 reg = ar2315_gpio_reg_read(AR2315_GPIO_DO);
+
+ reg = val ? reg | (1 << gpio) : reg & ~(1 << gpio);
+ ar2315_gpio_reg_write(AR2315_GPIO_DO, reg);
+}
+
+static int ar2315_gpio_dir_in(struct gpio_chip *chip, unsigned gpio)
+{
+ ar2315_gpio_reg_mask(AR2315_GPIO_DIR, 1 << gpio, 0);
+ return 0;
+}
+
+static int ar2315_gpio_dir_out(struct gpio_chip *chip, unsigned gpio, int val)
+{
+ ar2315_gpio_reg_mask(AR2315_GPIO_DIR, 0, 1 << gpio);
+ ar2315_gpio_set_val(chip, gpio, val);
+ return 0;
+}
+
+static int ar2315_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+ return ar2315_gpio_irq_base + gpio;
+}
+
+static struct gpio_chip ar2315_gpio_chip = {
+ .label = DRIVER_NAME,
+ .direction_input = ar2315_gpio_dir_in,
+ .direction_output = ar2315_gpio_dir_out,
+ .set = ar2315_gpio_set_val,
+ .get = ar2315_gpio_get_val,
+ .to_irq = ar2315_gpio_to_irq,
+ .base = 0,
+ .ngpio = AR2315_GPIO_NUM,
+};
+
+static int ar2315_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ unsigned irq;
+ int ret;
+
+ if (ar2315_mem)
+ return -EBUSY;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, DRIVER_NAME);
+ if (!res) {
+ dev_err(dev, "not found IRQ number\n");
+ return -ENXIO;
+ }
+ irq = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, DRIVER_NAME);
+ ar2315_mem = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ar2315_mem))
+ return PTR_ERR(ar2315_mem);
+
+ ar2315_gpio_chip.dev = dev;
+ ret = gpiochip_add(&ar2315_gpio_chip);
+ if (ret) {
+ dev_err(dev, "failed to add gpiochip\n");
+ return ret;
+ }
+
+ ret = irq_alloc_descs(-1, 0, AR2315_GPIO_NUM, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to allocate IRQ numbers\n");
+ return ret;
+ }
+ ar2315_gpio_irq_base = ret;
+
+ ar2315_gpio_irq_init(irq);
+
+ return 0;
+}
+
+static struct platform_driver ar2315_gpio_driver = {
+ .probe = ar2315_gpio_probe,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init ar2315_gpio_init(void)
+{
+ return platform_driver_register(&ar2315_gpio_driver);
+}
+subsys_initcall(ar2315_gpio_init);
--
1.8.5.5

--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Linus Walleij
2014-10-15 08:58:37 UTC
Permalink
Post by Sergey Ryazanov
Atheros AR2315 SoC have a builtin GPIO controller, which could be
accessed via memory mapped registers. This patch adds new driver
for them.
+config GPIO_AR2315
+ bool "AR2315 SoC GPIO support"
+ default y if SOC_AR2315
+ depends on SOC_AR2315
+ help
+ Say yes here to enable GPIO support for Atheros AR2315+ SoCs.
select GPIOLIB_IRQCHIP

As far as I can see this driver should use the gpiolib irqchip helpers
and create a cascading irqchip. The code uses some copy/pasting
from earlier drivers which is not a good idea. Look at one of the drivers
using GPIOLIB_IRQCHIP and be inspired, check e.g. gpio-pl061.c
or gpio-zynq.c
Post by Sergey Ryazanov
+static u32 ar2315_gpio_intmask;
+static u32 ar2315_gpio_intval;
+static unsigned ar2315_gpio_irq_base;
+static void __iomem *ar2315_mem;
No static locals. Allocate and use a state container, see
Documentation/driver-model/design-patterns.txt
Post by Sergey Ryazanov
+static inline u32 ar2315_gpio_reg_read(unsigned reg)
+{
+ return __raw_readl(ar2315_mem + reg);
+}
Use readl_relaxed() instead.
Post by Sergey Ryazanov
+static inline void ar2315_gpio_reg_write(unsigned reg, u32 val)
+{
+ __raw_writel(val, ar2315_mem + reg);
+}
Use writel_relaxed() instead.

When you use the state container, you need to do a
state dereference like that:

mystate->base + reg

So I don't think these inlines buy you anything. Just use
readl/writel_relaxed directly in the code.
Post by Sergey Ryazanov
+static inline void ar2315_gpio_reg_mask(unsigned reg, u32 mask, u32 val)
+{
+ ar2315_gpio_reg_write(reg, (ar2315_gpio_reg_read(reg) & ~mask) | val);
+}
Too simple helper IMO, if you wanna do this in some organized fashion,
use regmap.
Post by Sergey Ryazanov
+static void ar2315_gpio_int_setup(unsigned gpio, int trig)
+{
+ u32 reg = ar2315_gpio_reg_read(AR2315_GPIO_INT);
+
+ reg &= ~(AR2315_GPIO_INT_NUM_M | AR2315_GPIO_INT_TRIG_M);
+ reg |= gpio | AR2315_GPIO_INT_TRIG(trig);
+ ar2315_gpio_reg_write(AR2315_GPIO_INT, reg);
+}
Trig? Isn't this supposed to be done in the .set_type()
callback?
Post by Sergey Ryazanov
+static void ar2315_gpio_irq_unmask(struct irq_data *d)
+{
+ unsigned gpio = d->irq - ar2315_gpio_irq_base;
This kind of weird calculations go away with irqdomain and that
is in turn used by GPIOLIB_IRQCHIP.

After that you will just use d->hwirq. And name the variable
"offset" while you're at it.
Post by Sergey Ryazanov
+ u32 dir = ar2315_gpio_reg_read(AR2315_GPIO_DIR);
+
+ /* Enable interrupt with edge detection */
+ if ((dir & AR2315_GPIO_DIR_M(gpio)) != AR2315_GPIO_DIR_I(gpio))
+ return;
+
+ ar2315_gpio_intmask |= (1 << gpio);
#include <linux/bitops.h>

ar2315_gpio_intmask |= BIT(gpio);
Post by Sergey Ryazanov
+ ar2315_gpio_int_setup(gpio, AR2315_GPIO_INT_TRIG_EDGE);
+}
+static struct irq_chip ar2315_gpio_irq_chip = {
+ .name = DRIVER_NAME,
+ .irq_unmask = ar2315_gpio_irq_unmask,
+ .irq_mask = ar2315_gpio_irq_mask,
+};
So why is .set_type() not implemented and instead hard-coded into
the unmask function? Please fix this. It will be called by the
core eventually no matter what.
Post by Sergey Ryazanov
+static void ar2315_gpio_irq_init(unsigned irq)
+{
+ unsigned i;
+
+ ar2315_gpio_intval = ar2315_gpio_reg_read(AR2315_GPIO_DI);
+ for (i = 0; i < AR2315_GPIO_NUM; i++) {
+ unsigned _irq = ar2315_gpio_irq_base + i;
+
+ irq_set_chip_and_handler(_irq, &ar2315_gpio_irq_chip,
+ handle_level_irq);
+ }
+ irq_set_chained_handler(irq, ar2315_gpio_irq_handler);
+}
No, use the gpiolib irqchip helpers.
Post by Sergey Ryazanov
+static int ar2315_gpio_get_val(struct gpio_chip *chip, unsigned gpio)
+{
+ return (ar2315_gpio_reg_read(AR2315_GPIO_DI) >> gpio) & 1;
+}
Do this instead:

return !!(ar2315_gpio_reg_read(AR2315_GPIO_DI) & BIT(offset));
Post by Sergey Ryazanov
+static int ar2315_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+ return ar2315_gpio_irq_base + gpio;
+}
You do not implement this at all when using the gpiolib irqchip helpers.
Post by Sergey Ryazanov
+static int ar2315_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ unsigned irq;
+ int ret;
+
+ if (ar2315_mem)
+ return -EBUSY;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, DRIVER_NAME);
+ if (!res) {
+ dev_err(dev, "not found IRQ number\n");
+ return -ENXIO;
+ }
+ irq = res->start;
Use

irq = platform_get_irq_byname(pdev, DRIVER_NAME);
if (irq < 0)...
Post by Sergey Ryazanov
+ ret = irq_alloc_descs(-1, 0, AR2315_GPIO_NUM, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to allocate IRQ numbers\n");
+ return ret;
+ }
+ ar2315_gpio_irq_base = ret;
+
+ ar2315_gpio_irq_init(irq);
No, let GPIOLIB_IRQCHIP handle this.
Post by Sergey Ryazanov
+static int __init ar2315_gpio_init(void)
+{
+ return platform_driver_register(&ar2315_gpio_driver);
+}
+subsys_initcall(ar2315_gpio_init);
Why are you using subsys_initcall()?

This should not be necessary.

Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Sergey Ryazanov
2014-10-15 11:12:52 UTC
Permalink
Hi Linus,

thank you for so detailed review!

I have one more generic question: could you merge driver without
GPIOLIB_IRQCHIP usage? Currently no one driver for the AR231x SoCs
uses irq_domain and I do not like to enable IRQ_DOMAIN just for one
driver. I plan to convert drivers to make them irq_domain aware a bit
later.

Please, find my comments below.
Post by Linus Walleij
Post by Sergey Ryazanov
Atheros AR2315 SoC have a builtin GPIO controller, which could be
accessed via memory mapped registers. This patch adds new driver
for them.
+config GPIO_AR2315
+ bool "AR2315 SoC GPIO support"
+ default y if SOC_AR2315
+ depends on SOC_AR2315
+ help
+ Say yes here to enable GPIO support for Atheros AR2315+ SoCs.
select GPIOLIB_IRQCHIP
As far as I can see this driver should use the gpiolib irqchip helpers
and create a cascading irqchip. The code uses some copy/pasting
from earlier drivers which is not a good idea. Look at one of the drivers
using GPIOLIB_IRQCHIP and be inspired, check e.g. gpio-pl061.c
or gpio-zynq.c
Yes, this driver is pretty old, I updated it with newer API except
IRQ_DOMAIN, which I left for stage 2. Thank you for your hint about
reference realization.
Post by Linus Walleij
Post by Sergey Ryazanov
+static u32 ar2315_gpio_intmask;
+static u32 ar2315_gpio_intval;
+static unsigned ar2315_gpio_irq_base;
+static void __iomem *ar2315_mem;
No static locals. Allocate and use a state container, see
Documentation/driver-model/design-patterns.txt
Is that rule mandatory for drivers, which serve only one device?
Post by Linus Walleij
Post by Sergey Ryazanov
+static inline u32 ar2315_gpio_reg_read(unsigned reg)
+{
+ return __raw_readl(ar2315_mem + reg);
+}
Use readl_relaxed() instead.
readl_relaxed() converts the bit ordering and seems inapplicable in this case.
Post by Linus Walleij
Post by Sergey Ryazanov
+static inline void ar2315_gpio_reg_write(unsigned reg, u32 val)
+{
+ __raw_writel(val, ar2315_mem + reg);
+}
Use writel_relaxed() instead.
When you use the state container, you need to do a
mystate->base + reg
So I don't think these inlines buy you anything. Just use
readl/writel_relaxed directly in the code.
These helpers make code shorter and clearer. I can use macros if you
do preferred.
Post by Linus Walleij
Post by Sergey Ryazanov
+static inline void ar2315_gpio_reg_mask(unsigned reg, u32 mask, u32 val)
+{
+ ar2315_gpio_reg_write(reg, (ar2315_gpio_reg_read(reg) & ~mask) | val);
+}
Too simple helper IMO, if you wanna do this in some organized fashion,
use regmap.
Post by Sergey Ryazanov
+static void ar2315_gpio_int_setup(unsigned gpio, int trig)
+{
+ u32 reg = ar2315_gpio_reg_read(AR2315_GPIO_INT);
+
+ reg &= ~(AR2315_GPIO_INT_NUM_M | AR2315_GPIO_INT_TRIG_M);
+ reg |= gpio | AR2315_GPIO_INT_TRIG(trig);
+ ar2315_gpio_reg_write(AR2315_GPIO_INT, reg);
+}
Trig? Isn't this supposed to be done in the .set_type()
callback?
Yep. I tried to cheat kernel and made as if driver does not supports
any other trigger types :)
Post by Linus Walleij
Post by Sergey Ryazanov
+static void ar2315_gpio_irq_unmask(struct irq_data *d)
+{
+ unsigned gpio = d->irq - ar2315_gpio_irq_base;
This kind of weird calculations go away with irqdomain and that
is in turn used by GPIOLIB_IRQCHIP.
After that you will just use d->hwirq. And name the variable
"offset" while you're at it.
Post by Sergey Ryazanov
+ u32 dir = ar2315_gpio_reg_read(AR2315_GPIO_DIR);
+
+ /* Enable interrupt with edge detection */
+ if ((dir & AR2315_GPIO_DIR_M(gpio)) != AR2315_GPIO_DIR_I(gpio))
+ return;
+
+ ar2315_gpio_intmask |= (1 << gpio);
#include <linux/bitops.h>
ar2315_gpio_intmask |= BIT(gpio);
Post by Sergey Ryazanov
+ ar2315_gpio_int_setup(gpio, AR2315_GPIO_INT_TRIG_EDGE);
+}
+static struct irq_chip ar2315_gpio_irq_chip = {
+ .name = DRIVER_NAME,
+ .irq_unmask = ar2315_gpio_irq_unmask,
+ .irq_mask = ar2315_gpio_irq_mask,
+};
So why is .set_type() not implemented and instead hard-coded into
the unmask function? Please fix this. It will be called by the
core eventually no matter what.
The interrupt configuration is a bit complex. This controller could be
configured to generate interrupts only for two lines at once. Or in
other words: user could select any two lines to generate interrupt.
Post by Linus Walleij
Post by Sergey Ryazanov
+static void ar2315_gpio_irq_init(unsigned irq)
+{
+ unsigned i;
+
+ ar2315_gpio_intval = ar2315_gpio_reg_read(AR2315_GPIO_DI);
+ for (i = 0; i < AR2315_GPIO_NUM; i++) {
+ unsigned _irq = ar2315_gpio_irq_base + i;
+
+ irq_set_chip_and_handler(_irq, &ar2315_gpio_irq_chip,
+ handle_level_irq);
+ }
+ irq_set_chained_handler(irq, ar2315_gpio_irq_handler);
+}
No, use the gpiolib irqchip helpers.
Post by Sergey Ryazanov
+static int ar2315_gpio_get_val(struct gpio_chip *chip, unsigned gpio)
+{
+ return (ar2315_gpio_reg_read(AR2315_GPIO_DI) >> gpio) & 1;
+}
return !!(ar2315_gpio_reg_read(AR2315_GPIO_DI) & BIT(offset));
Post by Sergey Ryazanov
+static int ar2315_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+ return ar2315_gpio_irq_base + gpio;
+}
You do not implement this at all when using the gpiolib irqchip helpers.
Post by Sergey Ryazanov
+static int ar2315_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ unsigned irq;
+ int ret;
+
+ if (ar2315_mem)
+ return -EBUSY;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, DRIVER_NAME);
+ if (!res) {
+ dev_err(dev, "not found IRQ number\n");
+ return -ENXIO;
+ }
+ irq = res->start;
Use
irq = platform_get_irq_byname(pdev, DRIVER_NAME);
if (irq < 0)...
Post by Sergey Ryazanov
+ ret = irq_alloc_descs(-1, 0, AR2315_GPIO_NUM, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to allocate IRQ numbers\n");
+ return ret;
+ }
+ ar2315_gpio_irq_base = ret;
+
+ ar2315_gpio_irq_init(irq);
No, let GPIOLIB_IRQCHIP handle this.
Post by Sergey Ryazanov
+static int __init ar2315_gpio_init(void)
+{
+ return platform_driver_register(&ar2315_gpio_driver);
+}
+subsys_initcall(ar2315_gpio_init);
Why are you using subsys_initcall()?
This should not be necessary.
I have users of GPIO in arch code, what called earlier than the
devices initcall.
Post by Linus Walleij
Yours,
Linus Walleij
--
BR,
Sergey
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to ***@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Sergey Ryazanov
2014-09-28 18:33:00 UTC
Permalink
Add common code for Atheros AR5312 and Atheros AR2315 SoCs families.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
---
arch/mips/Kbuild.platforms | 1 +
arch/mips/Kconfig | 13 ++++
arch/mips/ar231x/Makefile | 11 ++++
arch/mips/ar231x/Platform | 6 ++
arch/mips/ar231x/board.c | 53 +++++++++++++++
arch/mips/ar231x/devices.c | 20 ++++++
arch/mips/ar231x/devices.h | 22 +++++++
arch/mips/ar231x/prom.c | 26 ++++++++
arch/mips/include/asm/mach-ar231x/ar231x.h | 29 +++++++++
.../asm/mach-ar231x/cpu-feature-overrides.h | 76 ++++++++++++++++++++++
arch/mips/include/asm/mach-ar231x/dma-coherence.h | 64 ++++++++++++++++++
arch/mips/include/asm/mach-ar231x/gpio.h | 16 +++++
arch/mips/include/asm/mach-ar231x/war.h | 25 +++++++
13 files changed, 362 insertions(+)
create mode 100644 arch/mips/ar231x/Makefile
create mode 100644 arch/mips/ar231x/Platform
create mode 100644 arch/mips/ar231x/board.c
create mode 100644 arch/mips/ar231x/devices.c
create mode 100644 arch/mips/ar231x/devices.h
create mode 100644 arch/mips/ar231x/prom.c
create mode 100644 arch/mips/include/asm/mach-ar231x/ar231x.h
create mode 100644 arch/mips/include/asm/mach-ar231x/cpu-feature-overrides.h
create mode 100644 arch/mips/include/asm/mach-ar231x/dma-coherence.h
create mode 100644 arch/mips/include/asm/mach-ar231x/gpio.h
create mode 100644 arch/mips/include/asm/mach-ar231x/war.h

diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms
index f5e18bf..ee1940a 100644
--- a/arch/mips/Kbuild.platforms
+++ b/arch/mips/Kbuild.platforms
@@ -1,6 +1,7 @@
# All platforms listed in alphabetic order

platforms += alchemy
+platforms += ar231x
platforms += ar7
platforms += ath79
platforms += bcm47xx
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 01c0389..6adae4c 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -73,6 +73,19 @@ config MIPS_ALCHEMY
select SYS_SUPPORTS_ZBOOT
select COMMON_CLK

+config AR231X
+ bool "Atheros AR231x/AR531x SoC support"
+ select CEVT_R4K
+ select CSRC_R4K
+ select DMA_NONCOHERENT
+ select IRQ_CPU
+ select SYS_HAS_CPU_MIPS32_R1
+ select SYS_SUPPORTS_BIG_ENDIAN
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select ARCH_REQUIRE_GPIOLIB
+ help
+ Support for Atheros AR231x and Atheros AR531x based boards
+
config AR7
bool "Texas Instruments AR7"
select BOOT_ELF32
diff --git a/arch/mips/ar231x/Makefile b/arch/mips/ar231x/Makefile
new file mode 100644
index 0000000..9199fa1
--- /dev/null
+++ b/arch/mips/ar231x/Makefile
@@ -0,0 +1,11 @@
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 2006 FON Technology, SL.
+# Copyright (C) 2006 Imre Kaloz <***@openwrt.org>
+# Copyright (C) 2006-2009 Felix Fietkau <***@openwrt.org>
+#
+
+obj-y += board.o prom.o devices.o
diff --git a/arch/mips/ar231x/Platform b/arch/mips/ar231x/Platform
new file mode 100644
index 0000000..c924fd1
--- /dev/null
+++ b/arch/mips/ar231x/Platform
@@ -0,0 +1,6 @@
+#
+# Atheros AR531X/AR231X WiSoC
+#
+platform-$(CONFIG_AR231X) += ar231x/
+cflags-$(CONFIG_AR231X) += -I$(srctree)/arch/mips/include/asm/mach-ar231x
+load-$(CONFIG_AR231X) += 0xffffffff80041000
diff --git a/arch/mips/ar231x/board.c b/arch/mips/ar231x/board.c
new file mode 100644
index 0000000..9cde045
--- /dev/null
+++ b/arch/mips/ar231x/board.c
@@ -0,0 +1,53 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003 Atheros Communications, Inc., All Rights Reserved.
+ * Copyright (C) 2006 FON Technology, SL.
+ * Copyright (C) 2006 Imre Kaloz <***@openwrt.org>
+ * Copyright (C) 2006-2009 Felix Fietkau <***@openwrt.org>
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/irq_cpu.h>
+#include <asm/reboot.h>
+#include <asm/bootinfo.h>
+#include <asm/time.h>
+
+static void ar231x_halt(void)
+{
+ local_irq_disable();
+ while (1)
+ ;
+}
+
+void __init plat_mem_setup(void)
+{
+ _machine_halt = ar231x_halt;
+ pm_power_off = ar231x_halt;
+
+ /* Disable data watchpoints */
+ write_c0_watchlo0(0);
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+}
+
+void __init plat_time_init(void)
+{
+}
+
+unsigned int __cpuinit get_c0_compare_int(void)
+{
+ return CP0_LEGACY_COMPARE_IRQ;
+}
+
+void __init arch_init_irq(void)
+{
+ clear_c0_status(ST0_IM);
+ mips_cpu_irq_init();
+}
+
diff --git a/arch/mips/ar231x/devices.c b/arch/mips/ar231x/devices.c
new file mode 100644
index 0000000..f71a643
--- /dev/null
+++ b/arch/mips/ar231x/devices.c
@@ -0,0 +1,20 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/bootinfo.h>
+
+#include "devices.h"
+
+int ar231x_devtype = DEV_TYPE_UNKNOWN;
+
+static const char * const devtype_strings[] = {
+ [DEV_TYPE_UNKNOWN] = "Atheros (unknown)",
+};
+
+const char *get_system_type(void)
+{
+ if ((ar231x_devtype >= ARRAY_SIZE(devtype_strings)) ||
+ !devtype_strings[ar231x_devtype])
+ return devtype_strings[DEV_TYPE_UNKNOWN];
+ return devtype_strings[ar231x_devtype];
+}
+
diff --git a/arch/mips/ar231x/devices.h b/arch/mips/ar231x/devices.h
new file mode 100644
index 0000000..1590577
--- /dev/null
+++ b/arch/mips/ar231x/devices.h
@@ -0,0 +1,22 @@
+#ifndef __AR231X_DEVICES_H
+#define __AR231X_DEVICES_H
+
+#include <linux/cpu.h>
+
+enum {
+ DEV_TYPE_UNKNOWN
+};
+
+extern int ar231x_devtype;
+
+static inline bool is_2315(void)
+{
+ return (current_cpu_data.cputype == CPU_4KEC);
+}
+
+static inline bool is_5312(void)
+{
+ return !is_2315();
+}
+
+#endif
diff --git a/arch/mips/ar231x/prom.c b/arch/mips/ar231x/prom.c
new file mode 100644
index 0000000..522357f
--- /dev/null
+++ b/arch/mips/ar231x/prom.c
@@ -0,0 +1,26 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright MontaVista Software Inc
+ * Copyright (C) 2003 Atheros Communications, Inc., All Rights Reserved.
+ * Copyright (C) 2006 FON Technology, SL.
+ * Copyright (C) 2006 Imre Kaloz <***@openwrt.org>
+ * Copyright (C) 2006 Felix Fietkau <***@openwrt.org>
+ */
+
+/*
+ * Prom setup file for ar231x
+ */
+
+#include <linux/init.h>
+#include <asm/bootinfo.h>
+
+void __init prom_init(void)
+{
+}
+
+void __init prom_free_prom_memory(void)
+{
+}
diff --git a/arch/mips/include/asm/mach-ar231x/ar231x.h b/arch/mips/include/asm/mach-ar231x/ar231x.h
new file mode 100644
index 0000000..b830723
--- /dev/null
+++ b/arch/mips/include/asm/mach-ar231x/ar231x.h
@@ -0,0 +1,29 @@
+#ifndef __ASM_MACH_AR231X_H
+#define __ASM_MACH_AR231X_H
+
+#include <linux/io.h>
+
+#define AR231X_REG_MS(_val, _field) (((_val) & _field##_M) >> _field##_S)
+
+static inline u32 ar231x_read_reg(u32 reg)
+{
+ return __raw_readl((void __iomem *)KSEG1ADDR(reg));
+}
+
+static inline void ar231x_write_reg(u32 reg, u32 val)
+{
+ __raw_writel(val, (void __iomem *)KSEG1ADDR(reg));
+}
+
+static inline u32 ar231x_mask_reg(u32 reg, u32 mask, u32 val)
+{
+ u32 ret = ar231x_read_reg(reg);
+
+ ret &= ~mask;
+ ret |= val;
+ ar231x_write_reg(reg, ret);
+
+ return ret;
+}
+
+#endif /* __ASM_MACH_AR231X_H */
diff --git a/arch/mips/include/asm/mach-ar231x/cpu-feature-overrides.h b/arch/mips/include/asm/mach-ar231x/cpu-feature-overrides.h
new file mode 100644
index 0000000..337fe3e
--- /dev/null
+++ b/arch/mips/include/asm/mach-ar231x/cpu-feature-overrides.h
@@ -0,0 +1,76 @@
+/*
+ * Atheros AR231x/AR531x SoC specific CPU feature overrides
+ *
+ * Copyright (C) 2008 Gabor Juhos <***@openwrt.org>
+ *
+ * This file was derived from: include/asm-mips/cpu-features.h
+ * Copyright (C) 2003, 2004 Ralf Baechle
+ * Copyright (C) 2004 Maciej W. Rozycki
+ *
+ * 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_AR231X_CPU_FEATURE_OVERRIDES_H
+#define __ASM_MACH_AR231X_CPU_FEATURE_OVERRIDES_H
+
+/*
+ * The Atheros AR531x/AR231x SoCs have MIPS 4Kc/4KEc core.
+ */
+#define cpu_has_tlb 1
+#define cpu_has_4kex 1
+#define cpu_has_3k_cache 0
+#define cpu_has_4k_cache 1
+#define cpu_has_tx39_cache 0
+#define cpu_has_sb1_cache 0
+#define cpu_has_fpu 0
+#define cpu_has_32fpr 0
+#define cpu_has_counter 1
+/* #define cpu_has_watch ? */
+/* #define cpu_has_divec ? */
+/* #define cpu_has_vce ? */
+/* #define cpu_has_cache_cdex_p ? */
+/* #define cpu_has_cache_cdex_s ? */
+/* #define cpu_has_prefetch ? */
+/* #define cpu_has_mcheck ? */
+#define cpu_has_ejtag 1
+
+/*
+ * The MIPS 4Kc V0.9 core in the AR5312/AR2312 have problems with the
+ * ll/sc instructions.
+ */
+#define cpu_has_llsc 0
+
+#define cpu_has_mips16 0
+#define cpu_has_mdmx 0
+#define cpu_has_mips3d 0
+#define cpu_has_smartmips 0
+
+/* #define cpu_has_vtag_icache ? */
+/* #define cpu_has_dc_aliases ? */
+/* #define cpu_has_ic_fills_f_dc ? */
+/* #define cpu_has_pindexed_dcache ? */
+
+/* #define cpu_icache_snoops_remote_store ? */
+
+#define cpu_has_mips32r1 1
+
+#define cpu_has_mips64r1 0
+#define cpu_has_mips64r2 0
+
+#define cpu_has_dsp 0
+#define cpu_has_mipsmt 0
+
+/* #define cpu_has_nofpuex ? */
+#define cpu_has_64bits 0
+#define cpu_has_64bit_zero_reg 0
+#define cpu_has_64bit_gp_regs 0
+#define cpu_has_64bit_addresses 0
+
+/* #define cpu_has_inclusive_pcaches ? */
+
+/* #define cpu_dcache_line_size() ? */
+/* #define cpu_icache_line_size() ? */
+
+#endif /* __ASM_MACH_AR231X_CPU_FEATURE_OVERRIDES_H */
diff --git a/arch/mips/include/asm/mach-ar231x/dma-coherence.h b/arch/mips/include/asm/mach-ar231x/dma-coherence.h
new file mode 100644
index 0000000..ed32240
--- /dev/null
+++ b/arch/mips/include/asm/mach-ar231x/dma-coherence.h
@@ -0,0 +1,64 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2006 Ralf Baechle <***@linux-mips.org>
+ * Copyright (C) 2007 Felix Fietkau <***@openwrt.org>
+ *
+ */
+#ifndef __ASM_MACH_AR231X_DMA_COHERENCE_H
+#define __ASM_MACH_AR231X_DMA_COHERENCE_H
+
+#include <linux/device.h>
+
+static inline dma_addr_t
+plat_map_dma_mem(struct device *dev, void *addr, size_t size)
+{
+ return virt_to_phys(addr);
+}
+
+static inline dma_addr_t
+plat_map_dma_mem_page(struct device *dev, struct page *page)
+{
+ return page_to_phys(page);
+}
+
+static inline unsigned long
+plat_dma_addr_to_phys(struct device *dev, dma_addr_t dma_addr)
+{
+ return dma_addr;
+}
+
+static inline void
+plat_unmap_dma_mem(struct device *dev, dma_addr_t dma_addr, size_t size,
+ enum dma_data_direction direction)
+{
+}
+
+static inline int plat_dma_supported(struct device *dev, u64 mask)
+{
+ return 1;
+}
+
+static inline void plat_extra_sync_for_device(struct device *dev)
+{
+}
+
+static inline int plat_dma_mapping_error(struct device *dev,
+ dma_addr_t dma_addr)
+{
+ return 0;
+}
+
+static inline int plat_device_is_coherent(struct device *dev)
+{
+#ifdef CONFIG_DMA_COHERENT
+ return 1;
+#endif
+#ifdef CONFIG_DMA_NONCOHERENT
+ return 0;
+#endif
+}
+
+#endif /* __ASM_MACH_AR231X_DMA_COHERENCE_H */
diff --git a/arch/mips/include/asm/mach-ar231x/gpio.h b/arch/mips/include/asm/mach-ar231x/gpio.h
new file mode 100644
index 0000000..89d29f1
--- /dev/null
+++ b/arch/mips/include/asm/mach-ar231x/gpio.h
@@ -0,0 +1,16 @@
+#ifndef __ASM_MACH_AR231X_GPIO_H
+#define __ASM_MACH_AR231X_GPIO_H
+
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
+#define gpio_to_irq __gpio_to_irq
+
+static inline int irq_to_gpio(unsigned irq)
+{
+ return -EINVAL;
+}
+
+#endif /* __ASM_MACH_AR231X_GPIO_H */
diff --git a/arch/mips/include/asm/mach-ar231x/war.h b/arch/mips/include/asm/mach-ar231x/war.h
new file mode 100644
index 0000000..5faf9da
--- /dev/null
+++ b/arch/mips/include/asm/mach-ar231x/war.h
@@ -0,0 +1,25 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2008 Felix Fietkau <***@openwrt.org>
+ */
+#ifndef __ASM_MACH_AR231X_WAR_H
+#define __ASM_MACH_AR231X_WAR_H
+
+#define R4600_V1_INDEX_ICACHEOP_WAR 0
+#define R4600_V1_HIT_CACHEOP_WAR 0
+#define R4600_V2_HIT_CACHEOP_WAR 0
+#define R5432_CP0_INTERRUPT_WAR 0
+#define BCM1250_M3_WAR 0
+#define SIBYTE_1956_WAR 0
+#define MIPS4K_ICACHE_REFILL_WAR 0
+#define MIPS_CACHE_SYNC_WAR 0
+#define TX49XX_ICACHE_INDEX_INV_WAR 0
+#define RM9000_CDEX_SMP_WAR 0
+#define ICACHE_REFILLS_WORKAROUND_WAR 0
+#define R10000_LLSC_WAR 0
+#define MIPS34K_MISSED_ITLB_WAR 0
+
+#endif /* __ASM_MACH_AR231X_WAR_H */
--
1.8.5.5
Jonas Gorski
2014-09-29 09:30:31 UTC
Permalink
Post by Sergey Ryazanov
Add common code for Atheros AR5312 and Atheros AR2315 SoCs families.
---
arch/mips/Kbuild.platforms | 1 +
arch/mips/Kconfig | 13 ++++
arch/mips/ar231x/Makefile | 11 ++++
arch/mips/ar231x/Platform | 6 ++
arch/mips/ar231x/board.c | 53 +++++++++++++++
arch/mips/ar231x/devices.c | 20 ++++++
arch/mips/ar231x/devices.h | 22 +++++++
arch/mips/ar231x/prom.c | 26 ++++++++
arch/mips/include/asm/mach-ar231x/ar231x.h | 29 +++++++++
.../asm/mach-ar231x/cpu-feature-overrides.h | 76 ++++++++++++++++++++++
arch/mips/include/asm/mach-ar231x/dma-coherence.h | 64 ++++++++++++++++++
arch/mips/include/asm/mach-ar231x/gpio.h | 16 +++++
arch/mips/include/asm/mach-ar231x/war.h | 25 +++++++
13 files changed, 362 insertions(+)
create mode 100644 arch/mips/ar231x/Makefile
create mode 100644 arch/mips/ar231x/Platform
create mode 100644 arch/mips/ar231x/board.c
create mode 100644 arch/mips/ar231x/devices.c
create mode 100644 arch/mips/ar231x/devices.h
create mode 100644 arch/mips/ar231x/prom.c
create mode 100644 arch/mips/include/asm/mach-ar231x/ar231x.h
create mode 100644 arch/mips/include/asm/mach-ar231x/cpu-feature-overrides.h
create mode 100644 arch/mips/include/asm/mach-ar231x/dma-coherence.h
create mode 100644 arch/mips/include/asm/mach-ar231x/gpio.h
create mode 100644 arch/mips/include/asm/mach-ar231x/war.h
diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms
index f5e18bf..ee1940a 100644
--- a/arch/mips/Kbuild.platforms
+++ b/arch/mips/Kbuild.platforms
@@ -1,6 +1,7 @@
# All platforms listed in alphabetic order
platforms += alchemy
+platforms += ar231x
platforms += ar7
platforms += ath79
platforms += bcm47xx
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 01c0389..6adae4c 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -73,6 +73,19 @@ config MIPS_ALCHEMY
select SYS_SUPPORTS_ZBOOT
select COMMON_CLK
+config AR231X
I would suggest naming it ATH25, to match the other atheros target (ATH79).
Post by Sergey Ryazanov
+ bool "Atheros AR231x/AR531x SoC support"
+ select CEVT_R4K
+ select CSRC_R4K
+ select DMA_NONCOHERENT
+ select IRQ_CPU
+ select SYS_HAS_CPU_MIPS32_R1
+ select SYS_SUPPORTS_BIG_ENDIAN
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select ARCH_REQUIRE_GPIOLIB
+ help
+ Support for Atheros AR231x and Atheros AR531x based boards
+
config AR7
bool "Texas Instruments AR7"
select BOOT_ELF32
diff --git a/arch/mips/ar231x/Makefile b/arch/mips/ar231x/Makefile
new file mode 100644
index 0000000..9199fa1
--- /dev/null
+++ b/arch/mips/ar231x/Makefile
@@ -0,0 +1,11 @@
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 2006 FON Technology, SL.
+#
+
+obj-y += board.o prom.o devices.o
diff --git a/arch/mips/ar231x/Platform b/arch/mips/ar231x/Platform
new file mode 100644
index 0000000..c924fd1
--- /dev/null
+++ b/arch/mips/ar231x/Platform
@@ -0,0 +1,6 @@
+#
+# Atheros AR531X/AR231X WiSoC
+#
+platform-$(CONFIG_AR231X) += ar231x/
+cflags-$(CONFIG_AR231X) += -I$(srctree)/arch/mips/include/asm/mach-ar231x
+load-$(CONFIG_AR231X) += 0xffffffff80041000
diff --git a/arch/mips/ar231x/board.c b/arch/mips/ar231x/board.c
new file mode 100644
index 0000000..9cde045
--- /dev/null
+++ b/arch/mips/ar231x/board.c
@@ -0,0 +1,53 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003 Atheros Communications, Inc., All Rights Reserved.
+ * Copyright (C) 2006 FON Technology, SL.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/irq_cpu.h>
+#include <asm/reboot.h>
+#include <asm/bootinfo.h>
+#include <asm/time.h>
+
+static void ar231x_halt(void)
+{
+ local_irq_disable();
+ while (1)
+ ;
+}
+
+void __init plat_mem_setup(void)
+{
+ _machine_halt = ar231x_halt;
+ pm_power_off = ar231x_halt;
+
+ /* Disable data watchpoints */
+ write_c0_watchlo0(0);
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+}
+
+void __init plat_time_init(void)
+{
+}
+
+unsigned int __cpuinit get_c0_compare_int(void)
+{
+ return CP0_LEGACY_COMPARE_IRQ;
+}
+
+void __init arch_init_irq(void)
+{
+ clear_c0_status(ST0_IM);
+ mips_cpu_irq_init();
+}
+
diff --git a/arch/mips/ar231x/devices.c b/arch/mips/ar231x/devices.c
new file mode 100644
index 0000000..f71a643
--- /dev/null
+++ b/arch/mips/ar231x/devices.c
@@ -0,0 +1,20 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/bootinfo.h>
+
+#include "devices.h"
+
+int ar231x_devtype = DEV_TYPE_UNKNOWN;
+
+static const char * const devtype_strings[] = {
+ [DEV_TYPE_UNKNOWN] = "Atheros (unknown)",
+};
+
+const char *get_system_type(void)
+{
+ if ((ar231x_devtype >= ARRAY_SIZE(devtype_strings)) ||
+ !devtype_strings[ar231x_devtype])
+ return devtype_strings[DEV_TYPE_UNKNOWN];
+ return devtype_strings[ar231x_devtype];
+}
+
diff --git a/arch/mips/ar231x/devices.h b/arch/mips/ar231x/devices.h
new file mode 100644
index 0000000..1590577
--- /dev/null
+++ b/arch/mips/ar231x/devices.h
@@ -0,0 +1,22 @@
+#ifndef __AR231X_DEVICES_H
+#define __AR231X_DEVICES_H
+
+#include <linux/cpu.h>
+
+enum {
+ DEV_TYPE_UNKNOWN
+};
+
+extern int ar231x_devtype;
+
+static inline bool is_2315(void)
Since this is matching the whole family of ar231x SoCs, I would
suggest to give it a prefix and call it ath25_is_231x.
Post by Sergey Ryazanov
+{
+ return (current_cpu_data.cputype == CPU_4KEC);
Unnecessary ().
Post by Sergey Ryazanov
+}
+
+static inline bool is_5312(void)
Same comment here for 5312 -> 531x.
Post by Sergey Ryazanov
+{
+ return !is_2315();
+}
+
+#endif
Regards
Jonas
Sergey Ryazanov
2014-09-29 20:57:02 UTC
Permalink
Post by Jonas Gorski
Post by Sergey Ryazanov
Add common code for Atheros AR5312 and Atheros AR2315 SoCs families.
---
arch/mips/Kbuild.platforms | 1 +
arch/mips/Kconfig | 13 ++++
arch/mips/ar231x/Makefile | 11 ++++
arch/mips/ar231x/Platform | 6 ++
arch/mips/ar231x/board.c | 53 +++++++++++++++
arch/mips/ar231x/devices.c | 20 ++++++
arch/mips/ar231x/devices.h | 22 +++++++
arch/mips/ar231x/prom.c | 26 ++++++++
arch/mips/include/asm/mach-ar231x/ar231x.h | 29 +++++++++
.../asm/mach-ar231x/cpu-feature-overrides.h | 76 ++++++++++++++++++++++
arch/mips/include/asm/mach-ar231x/dma-coherence.h | 64 ++++++++++++++++++
arch/mips/include/asm/mach-ar231x/gpio.h | 16 +++++
arch/mips/include/asm/mach-ar231x/war.h | 25 +++++++
13 files changed, 362 insertions(+)
create mode 100644 arch/mips/ar231x/Makefile
create mode 100644 arch/mips/ar231x/Platform
create mode 100644 arch/mips/ar231x/board.c
create mode 100644 arch/mips/ar231x/devices.c
create mode 100644 arch/mips/ar231x/devices.h
create mode 100644 arch/mips/ar231x/prom.c
create mode 100644 arch/mips/include/asm/mach-ar231x/ar231x.h
create mode 100644 arch/mips/include/asm/mach-ar231x/cpu-feature-overrides.h
create mode 100644 arch/mips/include/asm/mach-ar231x/dma-coherence.h
create mode 100644 arch/mips/include/asm/mach-ar231x/gpio.h
create mode 100644 arch/mips/include/asm/mach-ar231x/war.h
diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms
index f5e18bf..ee1940a 100644
--- a/arch/mips/Kbuild.platforms
+++ b/arch/mips/Kbuild.platforms
@@ -1,6 +1,7 @@
# All platforms listed in alphabetic order
platforms += alchemy
+platforms += ar231x
platforms += ar7
platforms += ath79
platforms += bcm47xx
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 01c0389..6adae4c 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -73,6 +73,19 @@ config MIPS_ALCHEMY
select SYS_SUPPORTS_ZBOOT
select COMMON_CLK
+config AR231X
I would suggest naming it ATH25, to match the other atheros target (ATH79).
I have been thinking about such name. But decided to keep code closer
to owrt realization. May be maintainers could suggest smth. Ralf, what
do you think?
Post by Jonas Gorski
Post by Sergey Ryazanov
+ bool "Atheros AR231x/AR531x SoC support"
+ select CEVT_R4K
+ select CSRC_R4K
+ select DMA_NONCOHERENT
+ select IRQ_CPU
+ select SYS_HAS_CPU_MIPS32_R1
+ select SYS_SUPPORTS_BIG_ENDIAN
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select ARCH_REQUIRE_GPIOLIB
+ help
+ Support for Atheros AR231x and Atheros AR531x based boards
+
config AR7
bool "Texas Instruments AR7"
select BOOT_ELF32
diff --git a/arch/mips/ar231x/Makefile b/arch/mips/ar231x/Makefile
new file mode 100644
index 0000000..9199fa1
--- /dev/null
+++ b/arch/mips/ar231x/Makefile
@@ -0,0 +1,11 @@
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 2006 FON Technology, SL.
+#
+
+obj-y += board.o prom.o devices.o
diff --git a/arch/mips/ar231x/Platform b/arch/mips/ar231x/Platform
new file mode 100644
index 0000000..c924fd1
--- /dev/null
+++ b/arch/mips/ar231x/Platform
@@ -0,0 +1,6 @@
+#
+# Atheros AR531X/AR231X WiSoC
+#
+platform-$(CONFIG_AR231X) += ar231x/
+cflags-$(CONFIG_AR231X) += -I$(srctree)/arch/mips/include/asm/mach-ar231x
+load-$(CONFIG_AR231X) += 0xffffffff80041000
diff --git a/arch/mips/ar231x/board.c b/arch/mips/ar231x/board.c
new file mode 100644
index 0000000..9cde045
--- /dev/null
+++ b/arch/mips/ar231x/board.c
@@ -0,0 +1,53 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003 Atheros Communications, Inc., All Rights Reserved.
+ * Copyright (C) 2006 FON Technology, SL.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/irq_cpu.h>
+#include <asm/reboot.h>
+#include <asm/bootinfo.h>
+#include <asm/time.h>
+
+static void ar231x_halt(void)
+{
+ local_irq_disable();
+ while (1)
+ ;
+}
+
+void __init plat_mem_setup(void)
+{
+ _machine_halt = ar231x_halt;
+ pm_power_off = ar231x_halt;
+
+ /* Disable data watchpoints */
+ write_c0_watchlo0(0);
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+}
+
+void __init plat_time_init(void)
+{
+}
+
+unsigned int __cpuinit get_c0_compare_int(void)
+{
+ return CP0_LEGACY_COMPARE_IRQ;
+}
+
+void __init arch_init_irq(void)
+{
+ clear_c0_status(ST0_IM);
+ mips_cpu_irq_init();
+}
+
diff --git a/arch/mips/ar231x/devices.c b/arch/mips/ar231x/devices.c
new file mode 100644
index 0000000..f71a643
--- /dev/null
+++ b/arch/mips/ar231x/devices.c
@@ -0,0 +1,20 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/bootinfo.h>
+
+#include "devices.h"
+
+int ar231x_devtype = DEV_TYPE_UNKNOWN;
+
+static const char * const devtype_strings[] = {
+ [DEV_TYPE_UNKNOWN] = "Atheros (unknown)",
+};
+
+const char *get_system_type(void)
+{
+ if ((ar231x_devtype >= ARRAY_SIZE(devtype_strings)) ||
+ !devtype_strings[ar231x_devtype])
+ return devtype_strings[DEV_TYPE_UNKNOWN];
+ return devtype_strings[ar231x_devtype];
+}
+
diff --git a/arch/mips/ar231x/devices.h b/arch/mips/ar231x/devices.h
new file mode 100644
index 0000000..1590577
--- /dev/null
+++ b/arch/mips/ar231x/devices.h
@@ -0,0 +1,22 @@
+#ifndef __AR231X_DEVICES_H
+#define __AR231X_DEVICES_H
+
+#include <linux/cpu.h>
+
+enum {
+ DEV_TYPE_UNKNOWN
+};
+
+extern int ar231x_devtype;
+
+static inline bool is_2315(void)
Since this is matching the whole family of ar231x SoCs, I would
suggest to give it a prefix and call it ath25_is_231x.
Only AR2315 and later chips have the 4KEc core.
Post by Jonas Gorski
Post by Sergey Ryazanov
+{
+ return (current_cpu_data.cputype == CPU_4KEC);
Unnecessary ().
Thank you, I missed this braces during cleanup.
Post by Jonas Gorski
Post by Sergey Ryazanov
+}
+
+static inline bool is_5312(void)
Same comment here for 5312 -> 531x.
This test is true for AR5312, AR2312 and AR2313 (see SoC type
detection patch), also we support only AR5312 SoC from whole AR531x
series.
Post by Jonas Gorski
Post by Sergey Ryazanov
+{
+ return !is_2315();
+}
+
+#endif
Regards
Jonas
--
BR,
Sergey
Sergey Ryazanov
2014-09-28 18:33:14 UTC
Permalink
- Use config symbol defined in the driver instead of arch specific one for
conditional compilation.
- Rename the ATHEROS_AR231X config symbol to AR231X.
- Some of AR231x SoCs (e.g. AR2315) have PCI bus support, so remove !PCI
dependency, which block AHB support build.

Signed-off-by: Sergey Ryazanov <ryazanov.s.a-***@public.gmane.org>
Cc: Jiri Slaby <jirislaby-***@public.gmane.org>
Cc: Nick Kossifidis <mickflemm-***@public.gmane.org>
Cc: "Luis R. Rodriguez" <mcgrof-***@public.gmane.org>
Cc: linux-wireless-***@public.gmane.org
Cc: ath5k-devel-***@public.gmane.org
---

Changes since RFC:
- merge together patches that update ath5k dependencies

drivers/net/wireless/ath/ath5k/Kconfig | 10 +++++-----
drivers/net/wireless/ath/ath5k/ath5k.h | 2 +-
drivers/net/wireless/ath/ath5k/base.c | 4 ++--
drivers/net/wireless/ath/ath5k/led.c | 4 ++--
4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig
index c9f81a3..2b2a399 100644
--- a/drivers/net/wireless/ath/ath5k/Kconfig
+++ b/drivers/net/wireless/ath/ath5k/Kconfig
@@ -1,13 +1,13 @@
config ATH5K
tristate "Atheros 5xxx wireless cards support"
- depends on (PCI || ATHEROS_AR231X) && MAC80211
+ depends on (PCI || AR231X) && MAC80211
select ATH_COMMON
select MAC80211_LEDS
select LEDS_CLASS
select NEW_LEDS
select AVERAGE
- select ATH5K_AHB if (ATHEROS_AR231X && !PCI)
- select ATH5K_PCI if (!ATHEROS_AR231X && PCI)
+ select ATH5K_AHB if AR231X
+ select ATH5K_PCI if !AR231X
---help---
This module adds support for wireless adapters based on
Atheros 5xxx chipset.
@@ -54,14 +54,14 @@ config ATH5K_TRACER

config ATH5K_AHB
bool "Atheros 5xxx AHB bus support"
- depends on (ATHEROS_AR231X && !PCI)
+ depends on AR231X
---help---
This adds support for WiSoC type chipsets of the 5xxx Atheros
family.

config ATH5K_PCI
bool "Atheros 5xxx PCI bus support"
- depends on (!ATHEROS_AR231X && PCI)
+ depends on (!AR231X && PCI)
---help---
This adds support for PCI type chipsets of the 5xxx Atheros
family.
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index 85316bb..1ed7a88 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -1647,7 +1647,7 @@ static inline struct ath_regulatory *ath5k_hw_regulatory(struct ath5k_hw *ah)
return &(ath5k_hw_common(ah)->regulatory);
}

-#ifdef CONFIG_ATHEROS_AR231X
+#ifdef CONFIG_ATH5K_AHB
#define AR5K_AR2315_PCI_BASE ((void __iomem *)0xb0100000)

static inline void __iomem *ath5k_ahb_reg(struct ath5k_hw *ah, u16 reg)
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 8ad2550..dd42487 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -99,7 +99,7 @@ static int ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,

/* Known SREVs */
static const struct ath5k_srev_name srev_names[] = {
-#ifdef CONFIG_ATHEROS_AR231X
+#ifdef CONFIG_ATH5K_AHB
{ "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R2 },
{ "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R7 },
{ "2313", AR5K_VERSION_MAC, AR5K_SREV_AR2313_R8 },
@@ -142,7 +142,7 @@ static const struct ath5k_srev_name srev_names[] = {
{ "5413", AR5K_VERSION_RAD, AR5K_SREV_RAD_5413 },
{ "5424", AR5K_VERSION_RAD, AR5K_SREV_RAD_5424 },
{ "5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133 },
-#ifdef CONFIG_ATHEROS_AR231X
+#ifdef CONFIG_ATH5K_AHB
{ "2316", AR5K_VERSION_RAD, AR5K_SREV_RAD_2316 },
{ "2317", AR5K_VERSION_RAD, AR5K_SREV_RAD_2317 },
#endif
diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c
index 48a6a69b..c730677 100644
--- a/drivers/net/wireless/ath/ath5k/led.c
+++ b/drivers/net/wireless/ath/ath5k/led.c
@@ -162,7 +162,7 @@ int ath5k_init_leds(struct ath5k_hw *ah)
{
int ret = 0;
struct ieee80211_hw *hw = ah->hw;
-#ifndef CONFIG_ATHEROS_AR231X
+#ifndef CONFIG_ATH5K_AHB
struct pci_dev *pdev = ah->pdev;
#endif
char name[ATH5K_LED_MAX_NAME_LEN + 1];
@@ -171,7 +171,7 @@ int ath5k_init_leds(struct ath5k_hw *ah)
if (!ah->pdev)
return 0;

-#ifdef CONFIG_ATHEROS_AR231X
+#ifdef CONFIG_ATH5K_AHB
match = NULL;
#else
match = pci_match_id(&ath5k_led_devices[0], pdev);
--
1.8.5.5

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
John W. Linville
2014-09-30 17:20:45 UTC
Permalink
This patch does not seem to apply to wireless-next. What tree is it
based upon?

John
Post by Sergey Ryazanov
- Use config symbol defined in the driver instead of arch specific one for
conditional compilation.
- Rename the ATHEROS_AR231X config symbol to AR231X.
- Some of AR231x SoCs (e.g. AR2315) have PCI bus support, so remove !PCI
dependency, which block AHB support build.
---
- merge together patches that update ath5k dependencies
drivers/net/wireless/ath/ath5k/Kconfig | 10 +++++-----
drivers/net/wireless/ath/ath5k/ath5k.h | 2 +-
drivers/net/wireless/ath/ath5k/base.c | 4 ++--
drivers/net/wireless/ath/ath5k/led.c | 4 ++--
4 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig
index c9f81a3..2b2a399 100644
--- a/drivers/net/wireless/ath/ath5k/Kconfig
+++ b/drivers/net/wireless/ath/ath5k/Kconfig
@@ -1,13 +1,13 @@
config ATH5K
tristate "Atheros 5xxx wireless cards support"
- depends on (PCI || ATHEROS_AR231X) && MAC80211
+ depends on (PCI || AR231X) && MAC80211
select ATH_COMMON
select MAC80211_LEDS
select LEDS_CLASS
select NEW_LEDS
select AVERAGE
- select ATH5K_AHB if (ATHEROS_AR231X && !PCI)
- select ATH5K_PCI if (!ATHEROS_AR231X && PCI)
+ select ATH5K_AHB if AR231X
+ select ATH5K_PCI if !AR231X
---help---
This module adds support for wireless adapters based on
Atheros 5xxx chipset.
@@ -54,14 +54,14 @@ config ATH5K_TRACER
config ATH5K_AHB
bool "Atheros 5xxx AHB bus support"
- depends on (ATHEROS_AR231X && !PCI)
+ depends on AR231X
---help---
This adds support for WiSoC type chipsets of the 5xxx Atheros
family.
config ATH5K_PCI
bool "Atheros 5xxx PCI bus support"
- depends on (!ATHEROS_AR231X && PCI)
+ depends on (!AR231X && PCI)
---help---
This adds support for PCI type chipsets of the 5xxx Atheros
family.
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index 85316bb..1ed7a88 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -1647,7 +1647,7 @@ static inline struct ath_regulatory *ath5k_hw_regulatory(struct ath5k_hw *ah)
return &(ath5k_hw_common(ah)->regulatory);
}
-#ifdef CONFIG_ATHEROS_AR231X
+#ifdef CONFIG_ATH5K_AHB
#define AR5K_AR2315_PCI_BASE ((void __iomem *)0xb0100000)
static inline void __iomem *ath5k_ahb_reg(struct ath5k_hw *ah, u16 reg)
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 8ad2550..dd42487 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -99,7 +99,7 @@ static int ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
/* Known SREVs */
static const struct ath5k_srev_name srev_names[] = {
-#ifdef CONFIG_ATHEROS_AR231X
+#ifdef CONFIG_ATH5K_AHB
{ "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R2 },
{ "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R7 },
{ "2313", AR5K_VERSION_MAC, AR5K_SREV_AR2313_R8 },
@@ -142,7 +142,7 @@ static const struct ath5k_srev_name srev_names[] = {
{ "5413", AR5K_VERSION_RAD, AR5K_SREV_RAD_5413 },
{ "5424", AR5K_VERSION_RAD, AR5K_SREV_RAD_5424 },
{ "5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133 },
-#ifdef CONFIG_ATHEROS_AR231X
+#ifdef CONFIG_ATH5K_AHB
{ "2316", AR5K_VERSION_RAD, AR5K_SREV_RAD_2316 },
{ "2317", AR5K_VERSION_RAD, AR5K_SREV_RAD_2317 },
#endif
diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c
index 48a6a69b..c730677 100644
--- a/drivers/net/wireless/ath/ath5k/led.c
+++ b/drivers/net/wireless/ath/ath5k/led.c
@@ -162,7 +162,7 @@ int ath5k_init_leds(struct ath5k_hw *ah)
{
int ret = 0;
struct ieee80211_hw *hw = ah->hw;
-#ifndef CONFIG_ATHEROS_AR231X
+#ifndef CONFIG_ATH5K_AHB
struct pci_dev *pdev = ah->pdev;
#endif
char name[ATH5K_LED_MAX_NAME_LEN + 1];
@@ -171,7 +171,7 @@ int ath5k_init_leds(struct ath5k_hw *ah)
if (!ah->pdev)
return 0;
-#ifdef CONFIG_ATHEROS_AR231X
+#ifdef CONFIG_ATH5K_AHB
match = NULL;
#else
match = pci_match_id(&ath5k_led_devices[0], pdev);
--
1.8.5.5
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
More majordomo info at http://vger.kernel.org/majordomo-info.html
--
John W. Linville Someday the world will need a hero, and you
linville-***@public.gmane.org might be all we have. Be ready.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Sergey Ryazanov
2014-10-01 14:41:34 UTC
Permalink
Post by John W. Linville
This patch does not seem to apply to wireless-next. What tree is it
based upon?
John
Its based on linux-mips. I thought that ath5k was not changed in
recent time and did not rebase patch on top of wireless-next.

John, could you delay patch merging? There is an idea to rename ar231x
in ath25, to be consistent with ath79 for AR71xx/AR9xxx.
Post by John W. Linville
- Use config symbol defined in the driver instead of arch specific o=
ne for
Post by John W. Linville
conditional compilation.
- Rename the ATHEROS_AR231X config symbol to AR231X.
- Some of AR231x SoCs (e.g. AR2315) have PCI bus support, so remove =
!PCI
Post by John W. Linville
dependency, which block AHB support build.
---
- merge together patches that update ath5k dependencies
drivers/net/wireless/ath/ath5k/Kconfig | 10 +++++-----
drivers/net/wireless/ath/ath5k/ath5k.h | 2 +-
drivers/net/wireless/ath/ath5k/base.c | 4 ++--
drivers/net/wireless/ath/ath5k/led.c | 4 ++--
4 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wi=
reless/ath/ath5k/Kconfig
Post by John W. Linville
index c9f81a3..2b2a399 100644
--- a/drivers/net/wireless/ath/ath5k/Kconfig
+++ b/drivers/net/wireless/ath/ath5k/Kconfig
@@ -1,13 +1,13 @@
config ATH5K
tristate "Atheros 5xxx wireless cards support"
- depends on (PCI || ATHEROS_AR231X) && MAC80211
+ depends on (PCI || AR231X) && MAC80211
select ATH_COMMON
select MAC80211_LEDS
select LEDS_CLASS
select NEW_LEDS
select AVERAGE
- select ATH5K_AHB if (ATHEROS_AR231X && !PCI)
- select ATH5K_PCI if (!ATHEROS_AR231X && PCI)
+ select ATH5K_AHB if AR231X
+ select ATH5K_PCI if !AR231X
---help---
This module adds support for wireless adapters based on
Atheros 5xxx chipset.
@@ -54,14 +54,14 @@ config ATH5K_TRACER
config ATH5K_AHB
bool "Atheros 5xxx AHB bus support"
- depends on (ATHEROS_AR231X && !PCI)
+ depends on AR231X
---help---
This adds support for WiSoC type chipsets of the 5xxx Athero=
s
Post by John W. Linville
family.
config ATH5K_PCI
bool "Atheros 5xxx PCI bus support"
- depends on (!ATHEROS_AR231X && PCI)
+ depends on (!AR231X && PCI)
---help---
This adds support for PCI type chipsets of the 5xxx Atheros
family.
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wi=
reless/ath/ath5k/ath5k.h
Post by John W. Linville
index 85316bb..1ed7a88 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -1647,7 +1647,7 @@ static inline struct ath_regulatory *ath5k_hw_=
regulatory(struct ath5k_hw *ah)
Post by John W. Linville
return &(ath5k_hw_common(ah)->regulatory);
}
-#ifdef CONFIG_ATHEROS_AR231X
+#ifdef CONFIG_ATH5K_AHB
#define AR5K_AR2315_PCI_BASE ((void __iomem *)0xb0100000)
static inline void __iomem *ath5k_ahb_reg(struct ath5k_hw *ah, u16 =
reg)
Post by John W. Linville
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wir=
eless/ath/ath5k/base.c
Post by John W. Linville
index 8ad2550..dd42487 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -99,7 +99,7 @@ static int ath5k_reset(struct ath5k_hw *ah, struct=
ieee80211_channel *chan,
Post by John W. Linville
/* Known SREVs */
static const struct ath5k_srev_name srev_names[] =3D {
-#ifdef CONFIG_ATHEROS_AR231X
+#ifdef CONFIG_ATH5K_AHB
{ "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R2 },
{ "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R7 },
{ "2313", AR5K_VERSION_MAC, AR5K_SREV_AR2313_R8 },
@@ -142,7 +142,7 @@ static const struct ath5k_srev_name srev_names[]=
=3D {
Post by John W. Linville
{ "5413", AR5K_VERSION_RAD, AR5K_SREV_RAD_5413 },
{ "5424", AR5K_VERSION_RAD, AR5K_SREV_RAD_5424 },
{ "5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133 },
-#ifdef CONFIG_ATHEROS_AR231X
+#ifdef CONFIG_ATH5K_AHB
{ "2316", AR5K_VERSION_RAD, AR5K_SREV_RAD_2316 },
{ "2317", AR5K_VERSION_RAD, AR5K_SREV_RAD_2317 },
#endif
diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wire=
less/ath/ath5k/led.c
Post by John W. Linville
index 48a6a69b..c730677 100644
--- a/drivers/net/wireless/ath/ath5k/led.c
+++ b/drivers/net/wireless/ath/ath5k/led.c
@@ -162,7 +162,7 @@ int ath5k_init_leds(struct ath5k_hw *ah)
{
int ret =3D 0;
struct ieee80211_hw *hw =3D ah->hw;
-#ifndef CONFIG_ATHEROS_AR231X
+#ifndef CONFIG_ATH5K_AHB
struct pci_dev *pdev =3D ah->pdev;
#endif
char name[ATH5K_LED_MAX_NAME_LEN + 1];
@@ -171,7 +171,7 @@ int ath5k_init_leds(struct ath5k_hw *ah)
if (!ah->pdev)
return 0;
-#ifdef CONFIG_ATHEROS_AR231X
+#ifdef CONFIG_ATH5K_AHB
match =3D NULL;
#else
match =3D pci_match_id(&ath5k_led_devices[0], pdev);
--
1.8.5.5
--=20
BR,
Sergey

=F3 =CE=C1=C9=CC=D5=DE=DB=C9=CD=C9 =D0=CF=D6=C5=CC=C1=CE=C9=D1=CD=C9
=F2=D1=DA=C1=CE=CF=D7 =F3=C5=D2=C7=C5=CA
--
To unsubscribe from this list: send the line "unsubscribe linux-wireles=
s" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
John W. Linville
2014-10-02 17:37:46 UTC
Permalink
Post by Sergey Ryazanov
Post by John W. Linville
This patch does not seem to apply to wireless-next. What tree is it
based upon?
John
Its based on linux-mips. I thought that ath5k was not changed in
recent time and did not rebase patch on top of wireless-next.
John, could you delay patch merging? There is an idea to rename ar231x
in ath25, to be consistent with ath79 for AR71xx/AR9xxx.
OK
--
John W. Linville Someday the world will need a hero, and you
linville-***@public.gmane.org might be all we have. Be ready.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Sergey Ryazanov
2014-09-28 18:33:01 UTC
Permalink
Add basic support for Atheros AR5312/AR2312 SoCs: registers definition
file and initial setup code.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
---
arch/mips/Kconfig | 1 +
arch/mips/ar231x/Kconfig | 4 +
arch/mips/ar231x/Makefile | 2 +
arch/mips/ar231x/ar5312.c | 143 ++++++++++++++++++
arch/mips/ar231x/ar5312.h | 18 +++
arch/mips/ar231x/board.c | 5 +
arch/mips/ar231x/prom.c | 3 +
arch/mips/include/asm/mach-ar231x/ar5312_regs.h | 190 ++++++++++++++++++++++++
8 files changed, 366 insertions(+)
create mode 100644 arch/mips/ar231x/Kconfig
create mode 100644 arch/mips/ar231x/ar5312.c
create mode 100644 arch/mips/ar231x/ar5312.h
create mode 100644 arch/mips/include/asm/mach-ar231x/ar5312_regs.h

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 6adae4c..bd81f7a 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -845,6 +845,7 @@ config MIPS_PARAVIRT
endchoice

source "arch/mips/alchemy/Kconfig"
+source "arch/mips/ar231x/Kconfig"
source "arch/mips/ath79/Kconfig"
source "arch/mips/bcm47xx/Kconfig"
source "arch/mips/bcm63xx/Kconfig"
diff --git a/arch/mips/ar231x/Kconfig b/arch/mips/ar231x/Kconfig
new file mode 100644
index 0000000..7634a64
--- /dev/null
+++ b/arch/mips/ar231x/Kconfig
@@ -0,0 +1,4 @@
+config SOC_AR5312
+ bool "Atheros AR5312/AR2312+ SoC support"
+ depends on AR231X
+ default y
diff --git a/arch/mips/ar231x/Makefile b/arch/mips/ar231x/Makefile
index 9199fa1..3361619 100644
--- a/arch/mips/ar231x/Makefile
+++ b/arch/mips/ar231x/Makefile
@@ -9,3 +9,5 @@
#

obj-y += board.o prom.o devices.o
+
+obj-$(CONFIG_SOC_AR5312) += ar5312.o
diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
new file mode 100644
index 0000000..909bee0
--- /dev/null
+++ b/arch/mips/ar231x/ar5312.c
@@ -0,0 +1,143 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003 Atheros Communications, Inc., All Rights Reserved.
+ * Copyright (C) 2006 FON Technology, SL.
+ * Copyright (C) 2006 Imre Kaloz <***@openwrt.org>
+ * Copyright (C) 2006-2009 Felix Fietkau <***@openwrt.org>
+ * Copyright (C) 2012 Alexandros C. Couloumbis <***@ozo.com>
+ */
+
+/*
+ * Platform devices for Atheros AR5312 SoCs
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/reboot.h>
+#include <asm/bootinfo.h>
+#include <asm/reboot.h>
+#include <asm/time.h>
+
+#include <ar5312_regs.h>
+#include <ar231x.h>
+
+#include "devices.h"
+#include "ar5312.h"
+
+static void ar5312_restart(char *command)
+{
+ /* reset the system */
+ local_irq_disable();
+ while (1)
+ ar231x_write_reg(AR5312_RESET, AR5312_RESET_SYSTEM);
+}
+
+/*
+ * This table is indexed by bits 5..4 of the CLOCKCTL1 register
+ * to determine the predevisor value.
+ */
+static unsigned clockctl1_predivide_table[4] __initdata = { 1, 2, 4, 5 };
+
+static unsigned __init ar5312_cpu_frequency(void)
+{
+ u32 scratch, devid, clock_ctl1;
+ u32 predivide_mask, multiplier_mask, doubler_mask;
+ unsigned predivide_shift, multiplier_shift;
+ unsigned predivide_select, predivisor, multiplier;
+
+ /* Trust the bootrom's idea of cpu frequency. */
+ scratch = ar231x_read_reg(AR5312_SCRATCH);
+ if (scratch)
+ return scratch;
+
+ devid = ar231x_read_reg(AR5312_REV);
+ devid = (devid & AR5312_REV_MAJ) >> AR5312_REV_MAJ_S;
+ if (devid == AR5312_REV_MAJ_AR2313) {
+ predivide_mask = AR2313_CLOCKCTL1_PREDIVIDE_MASK;
+ predivide_shift = AR2313_CLOCKCTL1_PREDIVIDE_SHIFT;
+ multiplier_mask = AR2313_CLOCKCTL1_MULTIPLIER_MASK;
+ multiplier_shift = AR2313_CLOCKCTL1_MULTIPLIER_SHIFT;
+ doubler_mask = AR2313_CLOCKCTL1_DOUBLER_MASK;
+ } else { /* AR5312 and AR2312 */
+ predivide_mask = AR5312_CLOCKCTL1_PREDIVIDE_MASK;
+ predivide_shift = AR5312_CLOCKCTL1_PREDIVIDE_SHIFT;
+ multiplier_mask = AR5312_CLOCKCTL1_MULTIPLIER_MASK;
+ multiplier_shift = AR5312_CLOCKCTL1_MULTIPLIER_SHIFT;
+ doubler_mask = AR5312_CLOCKCTL1_DOUBLER_MASK;
+ }
+
+ /*
+ * Clocking is derived from a fixed 40MHz input clock.
+ *
+ * cpu_freq = input_clock * MULT (where MULT is PLL multiplier)
+ * sys_freq = cpu_freq / 4 (used for APB clock, serial,
+ * flash, Timer, Watchdog Timer)
+ *
+ * cnt_freq = cpu_freq / 2 (use for CPU count/compare)
+ *
+ * So, for example, with a PLL multiplier of 5, we have
+ *
+ * cpu_freq = 200MHz
+ * sys_freq = 50MHz
+ * cnt_freq = 100MHz
+ *
+ * We compute the CPU frequency, based on PLL settings.
+ */
+
+ clock_ctl1 = ar231x_read_reg(AR5312_CLOCKCTL1);
+ predivide_select = (clock_ctl1 & predivide_mask) >> predivide_shift;
+ predivisor = clockctl1_predivide_table[predivide_select];
+ multiplier = (clock_ctl1 & multiplier_mask) >> multiplier_shift;
+
+ if (clock_ctl1 & doubler_mask)
+ multiplier <<= 1;
+
+ return (40000000 / predivisor) * multiplier;
+}
+
+static inline unsigned ar5312_sys_frequency(void)
+{
+ return ar5312_cpu_frequency() / 4;
+}
+
+void __init ar5312_plat_time_init(void)
+{
+ if (!is_5312())
+ return;
+
+ mips_hpt_frequency = ar5312_cpu_frequency() / 2;
+}
+
+void __init ar5312_plat_mem_setup(void)
+{
+ if (!is_5312())
+ return;
+
+ /* Clear any lingering AHB errors */
+ ar231x_read_reg(AR5312_PROCADDR);
+ ar231x_read_reg(AR5312_DMAADDR);
+ ar231x_write_reg(AR5312_WD_CTRL, AR5312_WD_CTRL_IGNORE_EXPIRATION);
+
+ _machine_restart = ar5312_restart;
+}
+
+void __init ar5312_prom_init(void)
+{
+ u32 memsize, memcfg, bank0_ac, bank1_ac;
+
+ if (!is_5312())
+ return;
+
+ /* Detect memory size */
+ memcfg = ar231x_read_reg(AR5312_MEM_CFG1);
+ bank0_ac = AR231X_REG_MS(memcfg, AR5312_MEM_CFG1_AC0);
+ bank1_ac = AR231X_REG_MS(memcfg, AR5312_MEM_CFG1_AC1);
+ memsize = (bank0_ac ? (1 << (bank0_ac + 1)) : 0) +
+ (bank1_ac ? (1 << (bank1_ac + 1)) : 0);
+ memsize <<= 20;
+ add_memory_region(0, memsize, BOOT_MEM_RAM);
+}
+
diff --git a/arch/mips/ar231x/ar5312.h b/arch/mips/ar231x/ar5312.h
new file mode 100644
index 0000000..339b28e
--- /dev/null
+++ b/arch/mips/ar231x/ar5312.h
@@ -0,0 +1,18 @@
+#ifndef __AR5312_H
+#define __AR5312_H
+
+#ifdef CONFIG_SOC_AR5312
+
+void ar5312_plat_time_init(void);
+void ar5312_plat_mem_setup(void);
+void ar5312_prom_init(void);
+
+#else
+
+static inline void ar5312_plat_time_init(void) {}
+static inline void ar5312_plat_mem_setup(void) {}
+static inline void ar5312_prom_init(void) {}
+
+#endif
+
+#endif /* __AR5312_H */
diff --git a/arch/mips/ar231x/board.c b/arch/mips/ar231x/board.c
index 9cde045..0014967 100644
--- a/arch/mips/ar231x/board.c
+++ b/arch/mips/ar231x/board.c
@@ -16,6 +16,8 @@
#include <asm/bootinfo.h>
#include <asm/time.h>

+#include "ar5312.h"
+
static void ar231x_halt(void)
{
local_irq_disable();
@@ -28,6 +30,8 @@ void __init plat_mem_setup(void)
_machine_halt = ar231x_halt;
pm_power_off = ar231x_halt;

+ ar5312_plat_mem_setup();
+
/* Disable data watchpoints */
write_c0_watchlo0(0);
}
@@ -38,6 +42,7 @@ asmlinkage void plat_irq_dispatch(void)

void __init plat_time_init(void)
{
+ ar5312_plat_time_init();
}

unsigned int __cpuinit get_c0_compare_int(void)
diff --git a/arch/mips/ar231x/prom.c b/arch/mips/ar231x/prom.c
index 522357f..d3efdd8 100644
--- a/arch/mips/ar231x/prom.c
+++ b/arch/mips/ar231x/prom.c
@@ -17,8 +17,11 @@
#include <linux/init.h>
#include <asm/bootinfo.h>

+#include "ar5312.h"
+
void __init prom_init(void)
{
+ ar5312_prom_init();
}

void __init prom_free_prom_memory(void)
diff --git a/arch/mips/include/asm/mach-ar231x/ar5312_regs.h b/arch/mips/include/asm/mach-ar231x/ar5312_regs.h
new file mode 100644
index 0000000..5eb22fd
--- /dev/null
+++ b/arch/mips/include/asm/mach-ar231x/ar5312_regs.h
@@ -0,0 +1,190 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003 Atheros Communications, Inc., All Rights Reserved.
+ * Copyright (C) 2006 Imre Kaloz <***@openwrt.org>
+ * Copyright (C) 2006 Felix Fietkau <***@openwrt.org>
+ */
+
+#ifndef __ASM_MACH_AR231X_AR5312_REGS_H
+#define __ASM_MACH_AR231X_AR5312_REGS_H
+
+/*
+ * Address Map
+ *
+ * The AR5312 supports 2 enet MACS, even though many reference boards only
+ * actually use 1 of them (i.e. Only MAC 0 is actually connected to an enet
+ * PHY or PHY switch. The AR2312 supports 1 enet MAC.
+ */
+#define AR5312_WLAN0 0x18000000
+#define AR5312_WLAN1 0x18500000
+#define AR5312_ENET0 0x18100000
+#define AR5312_ENET1 0x18200000
+#define AR5312_SDRAMCTL 0x18300000
+#define AR5312_FLASHCTL 0x18400000
+#define AR5312_APBBASE 0x1c000000
+#define AR5312_UART0 0x1c000000 /* UART MMR */
+#define AR5312_FLASH 0x1e000000
+
+/*
+ * Need these defines to determine true number of ethernet MACs
+ */
+#define AR5312_AR5312_REV2 0x0052 /* AR5312 WMAC (AP31) */
+#define AR5312_AR5312_REV7 0x0057 /* AR5312 WMAC (AP30-040) */
+#define AR5312_AR2313_REV8 0x0058 /* AR2313 WMAC (AP43-030) */
+
+/* Reset/Timer Block Address Map */
+#define AR5312_RESETTMR (AR5312_APBBASE + 0x3000)
+#define AR5312_TIMER (AR5312_RESETTMR + 0x0000) /* countdown timer */
+#define AR5312_WD_CTRL (AR5312_RESETTMR + 0x0008) /* watchdog cntrl */
+#define AR5312_WD_TIMER (AR5312_RESETTMR + 0x000c) /* watchdog timer */
+#define AR5312_ISR (AR5312_RESETTMR + 0x0010) /* Intr Status Reg */
+#define AR5312_IMR (AR5312_RESETTMR + 0x0014) /* Intr Mask Reg */
+#define AR5312_RESET (AR5312_RESETTMR + 0x0020)
+#define AR5312_CLOCKCTL1 (AR5312_RESETTMR + 0x0064)
+#define AR5312_SCRATCH (AR5312_RESETTMR + 0x006c)
+#define AR5312_PROCADDR (AR5312_RESETTMR + 0x0070)
+#define AR5312_PROC1 (AR5312_RESETTMR + 0x0074)
+#define AR5312_DMAADDR (AR5312_RESETTMR + 0x0078)
+#define AR5312_DMA1 (AR5312_RESETTMR + 0x007c)
+#define AR5312_ENABLE (AR5312_RESETTMR + 0x0080) /* interface enb */
+#define AR5312_REV (AR5312_RESETTMR + 0x0090) /* revision */
+
+/* AR5312_WD_CTRL register bit field definitions */
+#define AR5312_WD_CTRL_IGNORE_EXPIRATION 0x0000
+#define AR5312_WD_CTRL_NMI 0x0001
+#define AR5312_WD_CTRL_RESET 0x0002
+
+/* AR5312_ISR register bit field definitions */
+#define AR5312_ISR_TIMER 0x0001
+#define AR5312_ISR_AHBPROC 0x0002
+#define AR5312_ISR_AHBDMA 0x0004
+#define AR5312_ISR_GPIO 0x0008
+#define AR5312_ISR_UART0 0x0010
+#define AR5312_ISR_UART0DMA 0x0020
+#define AR5312_ISR_WD 0x0040
+#define AR5312_ISR_LOCAL 0x0080
+
+/* AR5312_RESET register bit field definitions */
+#define AR5312_RESET_SYSTEM 0x00000001 /* cold reset full system */
+#define AR5312_RESET_PROC 0x00000002 /* cold reset MIPS core */
+#define AR5312_RESET_WLAN0 0x00000004 /* cold reset WLAN MAC/BB */
+#define AR5312_RESET_EPHY0 0x00000008 /* cold reset ENET0 phy */
+#define AR5312_RESET_EPHY1 0x00000010 /* cold reset ENET1 phy */
+#define AR5312_RESET_ENET0 0x00000020 /* cold reset ENET0 MAC */
+#define AR5312_RESET_ENET1 0x00000040 /* cold reset ENET1 MAC */
+#define AR5312_RESET_UART0 0x00000100 /* cold reset UART0 */
+#define AR5312_RESET_WLAN1 0x00000200 /* cold reset WLAN MAC/BB */
+#define AR5312_RESET_APB 0x00000400 /* cold reset APB ar5312 */
+#define AR5312_RESET_WARM_PROC 0x00001000 /* warm reset MIPS core */
+#define AR5312_RESET_WARM_WLAN0_MAC 0x00002000 /* warm reset WLAN0 MAC */
+#define AR5312_RESET_WARM_WLAN0_BB 0x00004000 /* warm reset WLAN0 BB */
+#define AR5312_RESET_NMI 0x00010000 /* send an NMI to the CPU */
+#define AR5312_RESET_WARM_WLAN1_MAC 0x00020000 /* warm reset WLAN1 MAC */
+#define AR5312_RESET_WARM_WLAN1_BB 0x00040000 /* warm reset WLAN1 BB */
+#define AR5312_RESET_LOCAL_BUS 0x00080000 /* reset local bus */
+#define AR5312_RESET_WDOG 0x00100000 /* last reset was a wdt */
+
+#define AR5312_RESET_WMAC0_BITS (AR5312_RESET_WLAN0 |\
+ AR5312_RESET_WARM_WLAN0_MAC |\
+ AR5312_RESET_WARM_WLAN0_BB)
+
+#define AR5312_RESET_WMAC1_BITS (AR5312_RESET_WLAN1 |\
+ AR5312_RESET_WARM_WLAN1_MAC |\
+ AR5312_RESET_WARM_WLAN1_BB)
+
+/* AR5312_CLOCKCTL1 register bit field definitions */
+#define AR5312_CLOCKCTL1_PREDIVIDE_MASK 0x00000030
+#define AR5312_CLOCKCTL1_PREDIVIDE_SHIFT 4
+#define AR5312_CLOCKCTL1_MULTIPLIER_MASK 0x00001f00
+#define AR5312_CLOCKCTL1_MULTIPLIER_SHIFT 8
+#define AR5312_CLOCKCTL1_DOUBLER_MASK 0x00010000
+
+/* Valid for AR5312 and AR2312 */
+#define AR5312_CLOCKCTL1_PREDIVIDE_MASK 0x00000030
+#define AR5312_CLOCKCTL1_PREDIVIDE_SHIFT 4
+#define AR5312_CLOCKCTL1_MULTIPLIER_MASK 0x00001f00
+#define AR5312_CLOCKCTL1_MULTIPLIER_SHIFT 8
+#define AR5312_CLOCKCTL1_DOUBLER_MASK 0x00010000
+
+/* Valid for AR2313 */
+#define AR2313_CLOCKCTL1_PREDIVIDE_MASK 0x00003000
+#define AR2313_CLOCKCTL1_PREDIVIDE_SHIFT 12
+#define AR2313_CLOCKCTL1_MULTIPLIER_MASK 0x001f0000
+#define AR2313_CLOCKCTL1_MULTIPLIER_SHIFT 16
+#define AR2313_CLOCKCTL1_DOUBLER_MASK 0x00000000
+
+/* AR5312_ENABLE register bit field definitions */
+#define AR5312_ENABLE_WLAN0 0x0001
+#define AR5312_ENABLE_ENET0 0x0002
+#define AR5312_ENABLE_ENET1 0x0004
+#define AR5312_ENABLE_UART_AND_WLAN1_PIO 0x0008 /* UART & WLAN1 PIOs */
+#define AR5312_ENABLE_WLAN1_DMA 0x0010 /* WLAN1 DMAs */
+#define AR5312_ENABLE_WLAN1 (AR5312_ENABLE_UART_AND_WLAN1_PIO |\
+ AR5312_ENABLE_WLAN1_DMA)
+
+/* AR5312_REV register bit field definitions */
+#define AR5312_REV_WMAC_MAJ 0xf000
+#define AR5312_REV_WMAC_MAJ_S 12
+#define AR5312_REV_WMAC_MIN 0x0f00
+#define AR5312_REV_WMAC_MIN_S 8
+#define AR5312_REV_MAJ 0x00f0
+#define AR5312_REV_MAJ_S 4
+#define AR5312_REV_MIN 0x000f
+#define AR5312_REV_MIN_S 0
+#define AR5312_REV_CHIP (AR5312_REV_MAJ|AR5312_REV_MIN)
+
+/* Major revision numbers, bits 7..4 of Revision ID register */
+#define AR5312_REV_MAJ_AR5312 0x4
+#define AR5312_REV_MAJ_AR2313 0x5
+
+/* Minor revision numbers, bits 3..0 of Revision ID register */
+#define AR5312_REV_MIN_DUAL 0x0 /* Dual WLAN version */
+#define AR5312_REV_MIN_SINGLE 0x1 /* Single WLAN version */
+
+/* AR5312_FLASHCTL register bit field definitions */
+#define AR5312_FLASHCTL_IDCY 0x0000000f /* Idle cycle turnaround time */
+#define AR5312_FLASHCTL_IDCY_S 0
+#define AR5312_FLASHCTL_WST1 0x000003e0 /* Wait state 1 */
+#define AR5312_FLASHCTL_WST1_S 5
+#define AR5312_FLASHCTL_RBLE 0x00000400 /* Read byte lane enable */
+#define AR5312_FLASHCTL_WST2 0x0000f800 /* Wait state 2 */
+#define AR5312_FLASHCTL_WST2_S 11
+#define AR5312_FLASHCTL_AC 0x00070000 /* Flash addr check (added) */
+#define AR5312_FLASHCTL_AC_S 16
+#define AR5312_FLASHCTL_AC_128K 0x00000000
+#define AR5312_FLASHCTL_AC_256K 0x00010000
+#define AR5312_FLASHCTL_AC_512K 0x00020000
+#define AR5312_FLASHCTL_AC_1M 0x00030000
+#define AR5312_FLASHCTL_AC_2M 0x00040000
+#define AR5312_FLASHCTL_AC_4M 0x00050000
+#define AR5312_FLASHCTL_AC_8M 0x00060000
+#define AR5312_FLASHCTL_AC_RES 0x00070000 /* 16MB is not supported */
+#define AR5312_FLASHCTL_E 0x00080000 /* Flash bank enable (added) */
+#define AR5312_FLASHCTL_BUSERR 0x01000000 /* Bus transfer error flag */
+#define AR5312_FLASHCTL_WPERR 0x02000000 /* Write protect error flag */
+#define AR5312_FLASHCTL_WP 0x04000000 /* Write protect */
+#define AR5312_FLASHCTL_BM 0x08000000 /* Burst mode */
+#define AR5312_FLASHCTL_MW 0x30000000 /* Mem width */
+#define AR5312_FLASHCTL_MW8 0x00000000 /* Mem width x8 */
+#define AR5312_FLASHCTL_MW16 0x10000000 /* Mem width x16 */
+#define AR5312_FLASHCTL_MW32 0x20000000 /* Mem width x32 (not supp) */
+#define AR5312_FLASHCTL_ATNR 0x00000000 /* Access == no retry */
+#define AR5312_FLASHCTL_ATR 0x80000000 /* Access == retry every */
+#define AR5312_FLASHCTL_ATR4 0xc0000000 /* Access == retry every 4 */
+
+/* ARM Flash Controller -- 3 flash banks with either x8 or x16 devices. */
+#define AR5312_FLASHCTL0 (AR5312_FLASHCTL + 0x00)
+#define AR5312_FLASHCTL1 (AR5312_FLASHCTL + 0x04)
+#define AR5312_FLASHCTL2 (AR5312_FLASHCTL + 0x08)
+
+/* ARM SDRAM Controller -- just enough to determine memory size */
+#define AR5312_MEM_CFG1 (AR5312_SDRAMCTL + 0x04)
+#define AR5312_MEM_CFG1_AC0_M 0x00000700 /* bank 0: SDRAM addr check */
+#define AR5312_MEM_CFG1_AC0_S 8
+#define AR5312_MEM_CFG1_AC1_M 0x00007000 /* bank 1: SDRAM addr check */
+#define AR5312_MEM_CFG1_AC1_S 12
+
+#endif /* __ASM_MACH_AR231X_AR5312_REGS_H */
--
1.8.5.5
Jonas Gorski
2014-09-29 09:35:41 UTC
Permalink
Post by Sergey Ryazanov
Add basic support for Atheros AR5312/AR2312 SoCs: registers definition
file and initial setup code.
For the whole file: please use the style

do_foo()
{
if (is_ar5312())
ar5312_foo();
}

instead of

do_foo()
{
ar5312_foo();
}

ar5312_foo()
{
if (!is_ar5312())
return;
}

also same comments regarding naming (ar531x instead of ar5312).

Regards
Jonas
Sergey Ryazanov
2014-09-29 21:05:16 UTC
Permalink
Post by Jonas Gorski
Post by Sergey Ryazanov
Add basic support for Atheros AR5312/AR2312 SoCs: registers definition
file and initial setup code.
For the whole file: please use the style
do_foo()
{
if (is_ar5312())
ar5312_foo();
}
instead of
do_foo()
{
ar5312_foo();
}
ar5312_foo()
{
if (!is_ar5312())
return;
}
Ok.
Post by Jonas Gorski
also same comments regarding naming (ar531x instead of ar5312).
Seems there are no reasons to change naming approach (see details in
previous patch discussion).
Post by Jonas Gorski
Regards
Jonas
--
BR,
Sergey
Sergey Ryazanov
2014-09-28 18:33:02 UTC
Permalink
Add basic support for Atheros AR2315+ SoCs: registers definition file
and initial setup code.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
---
arch/mips/ar231x/Kconfig | 5 +
arch/mips/ar231x/Makefile | 1 +
arch/mips/ar231x/ar2315.c | 136 ++++++
arch/mips/ar231x/ar2315.h | 18 +
arch/mips/ar231x/board.c | 3 +
arch/mips/ar231x/prom.c | 2 +
arch/mips/include/asm/mach-ar231x/ar2315_regs.h | 529 +++++++++++++++++++++
.../asm/mach-ar231x/cpu-feature-overrides.h | 10 +-
8 files changed, 703 insertions(+), 1 deletion(-)
create mode 100644 arch/mips/ar231x/ar2315.c
create mode 100644 arch/mips/ar231x/ar2315.h
create mode 100644 arch/mips/include/asm/mach-ar231x/ar2315_regs.h

diff --git a/arch/mips/ar231x/Kconfig b/arch/mips/ar231x/Kconfig
index 7634a64..aa0fceb 100644
--- a/arch/mips/ar231x/Kconfig
+++ b/arch/mips/ar231x/Kconfig
@@ -2,3 +2,8 @@ config SOC_AR5312
bool "Atheros AR5312/AR2312+ SoC support"
depends on AR231X
default y
+
+config SOC_AR2315
+ bool "Atheros AR2315+ SoC support"
+ depends on AR231X
+ default y
diff --git a/arch/mips/ar231x/Makefile b/arch/mips/ar231x/Makefile
index 3361619..201b7d4 100644
--- a/arch/mips/ar231x/Makefile
+++ b/arch/mips/ar231x/Makefile
@@ -11,3 +11,4 @@
obj-y += board.o prom.o devices.o

obj-$(CONFIG_SOC_AR5312) += ar5312.o
+obj-$(CONFIG_SOC_AR2315) += ar2315.o
diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
new file mode 100644
index 0000000..5f8b7c4
--- /dev/null
+++ b/arch/mips/ar231x/ar2315.c
@@ -0,0 +1,136 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003 Atheros Communications, Inc., All Rights Reserved.
+ * Copyright (C) 2006 FON Technology, SL.
+ * Copyright (C) 2006 Imre Kaloz <***@openwrt.org>
+ * Copyright (C) 2006 Felix Fietkau <***@openwrt.org>
+ * Copyright (C) 2012 Alexandros C. Couloumbis <***@ozo.com>
+ */
+
+/*
+ * Platform devices for Atheros AR2315 SoCs
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/reboot.h>
+#include <asm/bootinfo.h>
+#include <asm/reboot.h>
+#include <asm/time.h>
+
+#include <ar2315_regs.h>
+#include <ar231x.h>
+
+#include "devices.h"
+#include "ar2315.h"
+
+static void ar2315_restart(char *command)
+{
+ void (*mips_reset_vec)(void) = (void *)0xbfc00000;
+
+ local_irq_disable();
+
+ /* try reset the system via reset control */
+ ar231x_write_reg(AR2315_COLD_RESET, AR2317_RESET_SYSTEM);
+
+ /* Attempt to jump to the mips reset location - the boot loader
+ * itself might be able to recover the system */
+ mips_reset_vec();
+}
+
+/*
+ * This table is indexed by bits 5..4 of the CLOCKCTL1 register
+ * to determine the predevisor value.
+ */
+static int clockctl1_predivide_table[4] __initdata = { 1, 2, 4, 5 };
+static int pllc_divide_table[5] __initdata = { 2, 3, 4, 6, 3 };
+
+static unsigned __init ar2315_sys_clk(u32 clock_ctl)
+{
+ unsigned int pllc_ctrl, cpu_div;
+ unsigned int pllc_out, refdiv, fdiv, divby2;
+ unsigned int clk_div;
+
+ pllc_ctrl = ar231x_read_reg(AR2315_PLLC_CTL);
+ refdiv = AR231X_REG_MS(pllc_ctrl, AR2315_PLLC_REF_DIV);
+ refdiv = clockctl1_predivide_table[refdiv];
+ fdiv = AR231X_REG_MS(pllc_ctrl, AR2315_PLLC_FDBACK_DIV);
+ divby2 = AR231X_REG_MS(pllc_ctrl, AR2315_PLLC_ADD_FDBACK_DIV) + 1;
+ pllc_out = (40000000 / refdiv) * (2 * divby2) * fdiv;
+
+ /* clkm input selected */
+ switch (clock_ctl & AR2315_CPUCLK_CLK_SEL_M) {
+ case 0:
+ case 1:
+ clk_div = AR231X_REG_MS(pllc_ctrl, AR2315_PLLC_CLKM_DIV);
+ clk_div = pllc_divide_table[clk_div];
+ break;
+ case 2:
+ clk_div = AR231X_REG_MS(pllc_ctrl, AR2315_PLLC_CLKC_DIV);
+ clk_div = pllc_divide_table[clk_div];
+ break;
+ default:
+ pllc_out = 40000000;
+ clk_div = 1;
+ break;
+ }
+
+ cpu_div = AR231X_REG_MS(clock_ctl, AR2315_CPUCLK_CLK_DIV);
+ cpu_div = cpu_div * 2 ?: 1;
+
+ return pllc_out / (clk_div * cpu_div);
+}
+
+static inline unsigned ar2315_cpu_frequency(void)
+{
+ return ar2315_sys_clk(ar231x_read_reg(AR2315_CPUCLK));
+}
+
+static inline unsigned ar2315_apb_frequency(void)
+{
+ return ar2315_sys_clk(ar231x_read_reg(AR2315_AMBACLK));
+}
+
+void __init ar2315_plat_time_init(void)
+{
+ if (!is_2315())
+ return;
+
+ mips_hpt_frequency = ar2315_cpu_frequency() / 2;
+}
+
+void __init ar2315_plat_mem_setup(void)
+{
+ u32 config;
+
+ if (!is_2315())
+ return;
+
+ /* Clear any lingering AHB errors */
+ config = read_c0_config();
+ write_c0_config(config & ~0x3);
+ ar231x_write_reg(AR2315_AHB_ERR0, AR2315_AHB_ERROR_DET);
+ ar231x_read_reg(AR2315_AHB_ERR1);
+ ar231x_write_reg(AR2315_WDC, AR2315_WDC_IGNORE_EXPIRATION);
+
+ _machine_restart = ar2315_restart;
+}
+
+void __init ar2315_prom_init(void)
+{
+ u32 memsize, memcfg;
+
+ if (!is_2315())
+ return;
+
+ memcfg = ar231x_read_reg(AR2315_MEM_CFG);
+ memsize = 1 + AR231X_REG_MS(memcfg, AR2315_MEM_CFG_DATA_WIDTH);
+ memsize <<= 1 + AR231X_REG_MS(memcfg, AR2315_MEM_CFG_COL_WIDTH);
+ memsize <<= 1 + AR231X_REG_MS(memcfg, AR2315_MEM_CFG_ROW_WIDTH);
+ memsize <<= 3;
+ add_memory_region(0, memsize, BOOT_MEM_RAM);
+}
+
diff --git a/arch/mips/ar231x/ar2315.h b/arch/mips/ar231x/ar2315.h
new file mode 100644
index 0000000..98d32b2
--- /dev/null
+++ b/arch/mips/ar231x/ar2315.h
@@ -0,0 +1,18 @@
+#ifndef __AR2315_H
+#define __AR2315_H
+
+#ifdef CONFIG_SOC_AR2315
+
+void ar2315_plat_time_init(void);
+void ar2315_plat_mem_setup(void);
+void ar2315_prom_init(void);
+
+#else
+
+static inline void ar2315_plat_time_init(void) {}
+static inline void ar2315_plat_mem_setup(void) {}
+static inline void ar2315_prom_init(void) {}
+
+#endif
+
+#endif /* __AR2315_H */
diff --git a/arch/mips/ar231x/board.c b/arch/mips/ar231x/board.c
index 0014967..f50a7cf 100644
--- a/arch/mips/ar231x/board.c
+++ b/arch/mips/ar231x/board.c
@@ -17,6 +17,7 @@
#include <asm/time.h>

#include "ar5312.h"
+#include "ar2315.h"

static void ar231x_halt(void)
{
@@ -31,6 +32,7 @@ void __init plat_mem_setup(void)
pm_power_off = ar231x_halt;

ar5312_plat_mem_setup();
+ ar2315_plat_mem_setup();

/* Disable data watchpoints */
write_c0_watchlo0(0);
@@ -43,6 +45,7 @@ asmlinkage void plat_irq_dispatch(void)
void __init plat_time_init(void)
{
ar5312_plat_time_init();
+ ar2315_plat_time_init();
}

unsigned int __cpuinit get_c0_compare_int(void)
diff --git a/arch/mips/ar231x/prom.c b/arch/mips/ar231x/prom.c
index d3efdd8..d8a2c23 100644
--- a/arch/mips/ar231x/prom.c
+++ b/arch/mips/ar231x/prom.c
@@ -18,10 +18,12 @@
#include <asm/bootinfo.h>

#include "ar5312.h"
+#include "ar2315.h"

void __init prom_init(void)
{
ar5312_prom_init();
+ ar2315_prom_init();
}

void __init prom_free_prom_memory(void)
diff --git a/arch/mips/include/asm/mach-ar231x/ar2315_regs.h b/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
new file mode 100644
index 0000000..91197b6
--- /dev/null
+++ b/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
@@ -0,0 +1,529 @@
+/*
+ * Register definitions for AR2315+
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003 Atheros Communications, Inc., All Rights Reserved.
+ * Copyright (C) 2006 FON Technology, SL.
+ * Copyright (C) 2006 Imre Kaloz <***@openwrt.org>
+ * Copyright (C) 2006-2008 Felix Fietkau <***@openwrt.org>
+ */
+
+#ifndef __ASM_MACH_AR231X_AR2315_REGS_H
+#define __ASM_MACH_AR231X_AR2315_REGS_H
+
+/*
+ * Address map
+ */
+#define AR2315_SPI_READ 0x08000000 /* SPI flash */
+#define AR2315_WLAN0 0x10000000 /* Wireless MMR */
+#define AR2315_PCI 0x10100000 /* PCI MMR */
+#define AR2315_SDRAMCTL 0x10300000 /* SDRAM MMR */
+#define AR2315_LOCAL 0x10400000 /* Local bus MMR */
+#define AR2315_ENET0 0x10500000 /* Ethernet MMR */
+#define AR2315_DSLBASE 0x11000000 /* Reset control MMR */
+#define AR2315_UART0 0x11100000 /* UART MMR */
+#define AR2315_SPI_MMR 0x11300000 /* SPI flash MMR */
+#define AR2315_PCIEXT 0x80000000 /* PCI external */
+#define AR2315_PCIEXT_SZ 0x40000000
+
+/*
+ * Cold reset register
+ */
+#define AR2315_COLD_RESET (AR2315_DSLBASE + 0x0000)
+
+#define AR2315_RESET_COLD_AHB 0x00000001
+#define AR2315_RESET_COLD_APB 0x00000002
+#define AR2315_RESET_COLD_CPU 0x00000004
+#define AR2315_RESET_COLD_CPUWARM 0x00000008
+#define AR2315_RESET_SYSTEM (RESET_COLD_CPU |\
+ RESET_COLD_APB |\
+ RESET_COLD_AHB) /* full system */
+#define AR2317_RESET_SYSTEM 0x00000010
+
+/*
+ * Reset register
+ */
+#define AR2315_RESET (AR2315_DSLBASE + 0x0004)
+
+#define AR2315_RESET_WARM_WLAN0_MAC 0x00000001 /* warm reset WLAN0 MAC */
+#define AR2315_RESET_WARM_WLAN0_BB 0x00000002 /* warm reset WLAN0 BB */
+#define AR2315_RESET_MPEGTS_RSVD 0x00000004 /* warm reset MPEG-TS */
+#define AR2315_RESET_PCIDMA 0x00000008 /* warm reset PCI ahb/dma */
+#define AR2315_RESET_MEMCTL 0x00000010 /* warm reset mem control */
+#define AR2315_RESET_LOCAL 0x00000020 /* warm reset local bus */
+#define AR2315_RESET_I2C_RSVD 0x00000040 /* warm reset I2C bus */
+#define AR2315_RESET_SPI 0x00000080 /* warm reset SPI iface */
+#define AR2315_RESET_UART0 0x00000100 /* warm reset UART0 */
+#define AR2315_RESET_IR_RSVD 0x00000200 /* warm reset IR iface */
+#define AR2315_RESET_EPHY0 0x00000400 /* cold reset ENET0 phy */
+#define AR2315_RESET_ENET0 0x00000800 /* cold reset ENET0 MAC */
+
+/*
+ * AHB master arbitration control
+ */
+#define AR2315_AHB_ARB_CTL (AR2315_DSLBASE + 0x0008)
+
+#define AR2315_ARB_CPU 0x00000001 /* CPU, default */
+#define AR2315_ARB_WLAN 0x00000002 /* WLAN */
+#define AR2315_ARB_MPEGTS_RSVD 0x00000004 /* MPEG-TS */
+#define AR2315_ARB_LOCAL 0x00000008 /* Local bus */
+#define AR2315_ARB_PCI 0x00000010 /* PCI bus */
+#define AR2315_ARB_ETHERNET 0x00000020 /* Ethernet */
+#define AR2315_ARB_RETRY 0x00000100 /* Retry policy (debug) */
+
+/*
+ * Config Register
+ */
+#define AR2315_ENDIAN_CTL (AR2315_DSLBASE + 0x000c)
+
+#define AR2315_CONFIG_AHB 0x00000001 /* EC-AHB bridge endian */
+#define AR2315_CONFIG_WLAN 0x00000002 /* WLAN byteswap */
+#define AR2315_CONFIG_MPEGTS_RSVD 0x00000004 /* MPEG-TS byteswap */
+#define AR2315_CONFIG_PCI 0x00000008 /* PCI byteswap */
+#define AR2315_CONFIG_MEMCTL 0x00000010 /* Mem controller endian */
+#define AR2315_CONFIG_LOCAL 0x00000020 /* Local bus byteswap */
+#define AR2315_CONFIG_ETHERNET 0x00000040 /* Ethernet byteswap */
+#define AR2315_CONFIG_MERGE 0x00000200 /* CPU write buffer merge */
+#define AR2315_CONFIG_CPU 0x00000400 /* CPU big endian */
+#define AR2315_CONFIG_BIG 0x00000400
+#define AR2315_CONFIG_PCIAHB 0x00000800
+#define AR2315_CONFIG_PCIAHB_BRIDGE 0x00001000
+#define AR2315_CONFIG_SPI 0x00008000 /* SPI byteswap */
+#define AR2315_CONFIG_CPU_DRAM 0x00010000
+#define AR2315_CONFIG_CPU_PCI 0x00020000
+#define AR2315_CONFIG_CPU_MMR 0x00040000
+
+/*
+ * NMI control
+ */
+#define AR2315_NMI_CTL (AR2315_DSLBASE + 0x0010)
+
+#define AR2315_NMI_EN 1
+
+/*
+ * Revision Register - Initial value is 0x3010 (WMAC 3.0, AR231X 1.0).
+ */
+#define AR2315_SREV (AR2315_DSLBASE + 0x0014)
+
+#define AR2315_REV_MAJ 0x00f0
+#define AR2315_REV_MAJ_S 4
+#define AR2315_REV_MIN 0x000f
+#define AR2315_REV_MIN_S 0
+#define AR2315_REV_CHIP (AR2315_REV_MAJ | AR2315_REV_MIN)
+
+/*
+ * Interface Enable
+ */
+#define AR2315_IF_CTL (AR2315_DSLBASE + 0x0018)
+
+#define AR2315_IF_MASK 0x00000007
+#define AR2315_IF_DISABLED 0 /* Disable all */
+#define AR2315_IF_PCI 1 /* PCI */
+#define AR2315_IF_TS_LOCAL 2 /* Local bus */
+#define AR2315_IF_ALL 3 /* Emulation only */
+#define AR2315_IF_LOCAL_HOST 0x00000008
+#define AR2315_IF_PCI_HOST 0x00000010
+#define AR2315_IF_PCI_INTR 0x00000020
+#define AR2315_IF_PCI_CLK_MASK 0x00030000
+#define AR2315_IF_PCI_CLK_INPUT 0
+#define AR2315_IF_PCI_CLK_OUTPUT_LOW 1
+#define AR2315_IF_PCI_CLK_OUTPUT_CLK 2
+#define AR2315_IF_PCI_CLK_OUTPUT_HIGH 3
+#define AR2315_IF_PCI_CLK_SHIFT 16
+
+/*
+ * APB Interrupt control
+ */
+
+#define AR2315_ISR (AR2315_DSLBASE + 0x0020)
+#define AR2315_IMR (AR2315_DSLBASE + 0x0024)
+#define AR2315_GISR (AR2315_DSLBASE + 0x0028)
+
+#define AR2315_ISR_UART0 0x0001 /* high speed UART */
+#define AR2315_ISR_I2C_RSVD 0x0002 /* I2C bus */
+#define AR2315_ISR_SPI 0x0004 /* SPI bus */
+#define AR2315_ISR_AHB 0x0008 /* AHB error */
+#define AR2315_ISR_APB 0x0010 /* APB error */
+#define AR2315_ISR_TIMER 0x0020 /* Timer */
+#define AR2315_ISR_GPIO 0x0040 /* GPIO */
+#define AR2315_ISR_WD 0x0080 /* Watchdog */
+#define AR2315_ISR_IR_RSVD 0x0100 /* IR */
+
+#define AR2315_GISR_MISC 0x0001 /* Misc */
+#define AR2315_GISR_WLAN0 0x0002 /* WLAN0 */
+#define AR2315_GISR_MPEGTS_RSVD 0x0004 /* MPEG-TS */
+#define AR2315_GISR_LOCALPCI 0x0008 /* Local/PCI bus */
+#define AR2315_GISR_WMACPOLL 0x0010
+#define AR2315_GISR_TIMER 0x0020
+#define AR2315_GISR_ETHERNET 0x0040 /* Ethernet */
+
+/*
+ * Timers
+ */
+#define AR2315_TIMER (AR2315_DSLBASE + 0x0030)
+#define AR2315_RELOAD (AR2315_DSLBASE + 0x0034)
+#define AR2315_WD (AR2315_DSLBASE + 0x0038)
+#define AR2315_WDC (AR2315_DSLBASE + 0x003c)
+
+#define AR2315_WDC_IGNORE_EXPIRATION 0x00000000
+#define AR2315_WDC_NMI 0x00000001 /* NMI on watchdog */
+#define AR2315_WDC_RESET 0x00000002 /* reset on watchdog */
+
+/*
+ * CPU Performance Counters
+ */
+#define AR2315_PERFCNT0 (AR2315_DSLBASE + 0x0048)
+#define AR2315_PERFCNT1 (AR2315_DSLBASE + 0x004c)
+
+#define AR2315_PERF0_DATAHIT 0x0001 /* Count Data Cache Hits */
+#define AR2315_PERF0_DATAMISS 0x0002 /* Count Data Cache Misses */
+#define AR2315_PERF0_INSTHIT 0x0004 /* Count Instruction Cache Hits */
+#define AR2315_PERF0_INSTMISS 0x0008 /* Count Instruction Cache Misses */
+#define AR2315_PERF0_ACTIVE 0x0010 /* Count Active Processor Cycles */
+#define AR2315_PERF0_WBHIT 0x0020 /* Count CPU Write Buffer Hits */
+#define AR2315_PERF0_WBMISS 0x0040 /* Count CPU Write Buffer Misses */
+
+#define AR2315_PERF1_EB_ARDY 0x0001 /* Count EB_ARdy signal */
+#define AR2315_PERF1_EB_AVALID 0x0002 /* Count EB_AValid signal */
+#define AR2315_PERF1_EB_WDRDY 0x0004 /* Count EB_WDRdy signal */
+#define AR2315_PERF1_EB_RDVAL 0x0008 /* Count EB_RdVal signal */
+#define AR2315_PERF1_VRADDR 0x0010 /* Count valid read address cycles */
+#define AR2315_PERF1_VWADDR 0x0020 /* Count valid write address cycles */
+#define AR2315_PERF1_VWDATA 0x0040 /* Count valid write data cycles */
+
+/*
+ * AHB Error Reporting.
+ */
+#define AR2315_AHB_ERR0 (AR2315_DSLBASE + 0x0050) /* error */
+#define AR2315_AHB_ERR1 (AR2315_DSLBASE + 0x0054) /* haddr */
+#define AR2315_AHB_ERR2 (AR2315_DSLBASE + 0x0058) /* hwdata */
+#define AR2315_AHB_ERR3 (AR2315_DSLBASE + 0x005c) /* hrdata */
+#define AR2315_AHB_ERR4 (AR2315_DSLBASE + 0x0060) /* status */
+
+#define AR2315_AHB_ERROR_DET 1 /* AHB Error has been detected, */
+ /* write 1 to clear all bits in ERR0 */
+#define AR2315_AHB_ERROR_OVR 2 /* AHB Error overflow has been detected */
+#define AR2315_AHB_ERROR_WDT 4 /* AHB Error due to wdt instead of hresp */
+
+#define AR2315_PROCERR_HMAST 0x0000000f
+#define AR2315_PROCERR_HMAST_DFLT 0
+#define AR2315_PROCERR_HMAST_WMAC 1
+#define AR2315_PROCERR_HMAST_ENET 2
+#define AR2315_PROCERR_HMAST_PCIENDPT 3
+#define AR2315_PROCERR_HMAST_LOCAL 4
+#define AR2315_PROCERR_HMAST_CPU 5
+#define AR2315_PROCERR_HMAST_PCITGT 6
+#define AR2315_PROCERR_HMAST_S 0
+#define AR2315_PROCERR_HWRITE 0x00000010
+#define AR2315_PROCERR_HSIZE 0x00000060
+#define AR2315_PROCERR_HSIZE_S 5
+#define AR2315_PROCERR_HTRANS 0x00000180
+#define AR2315_PROCERR_HTRANS_S 7
+#define AR2315_PROCERR_HBURST 0x00000e00
+#define AR2315_PROCERR_HBURST_S 9
+
+/*
+ * Clock Control
+ */
+#define AR2315_PLLC_CTL (AR2315_DSLBASE + 0x0064)
+#define AR2315_PLLV_CTL (AR2315_DSLBASE + 0x0068)
+#define AR2315_CPUCLK (AR2315_DSLBASE + 0x006c)
+#define AR2315_AMBACLK (AR2315_DSLBASE + 0x0070)
+#define AR2315_SYNCCLK (AR2315_DSLBASE + 0x0074)
+#define AR2315_DSL_SLEEP_CTL (AR2315_DSLBASE + 0x0080)
+#define AR2315_DSL_SLEEP_DUR (AR2315_DSLBASE + 0x0084)
+
+/* PLLc Control fields */
+#define AR2315_PLLC_REF_DIV_M 0x00000003
+#define AR2315_PLLC_REF_DIV_S 0
+#define AR2315_PLLC_FDBACK_DIV_M 0x0000007C
+#define AR2315_PLLC_FDBACK_DIV_S 2
+#define AR2315_PLLC_ADD_FDBACK_DIV_M 0x00000080
+#define AR2315_PLLC_ADD_FDBACK_DIV_S 7
+#define AR2315_PLLC_CLKC_DIV_M 0x0001c000
+#define AR2315_PLLC_CLKC_DIV_S 14
+#define AR2315_PLLC_CLKM_DIV_M 0x00700000
+#define AR2315_PLLC_CLKM_DIV_S 20
+
+/* CPU CLK Control fields */
+#define AR2315_CPUCLK_CLK_SEL_M 0x00000003
+#define AR2315_CPUCLK_CLK_SEL_S 0
+#define AR2315_CPUCLK_CLK_DIV_M 0x0000000c
+#define AR2315_CPUCLK_CLK_DIV_S 2
+
+/* AMBA CLK Control fields */
+#define AR2315_AMBACLK_CLK_SEL_M 0x00000003
+#define AR2315_AMBACLK_CLK_SEL_S 0
+#define AR2315_AMBACLK_CLK_DIV_M 0x0000000c
+#define AR2315_AMBACLK_CLK_DIV_S 2
+
+/*
+ * PCI Clock Control
+ */
+#define AR2315_PCICLK (AR2315_DSLBASE + 0x00a4)
+
+#define AR2315_PCICLK_INPUT_M 0x3
+#define AR2315_PCICLK_INPUT_S 0
+#define AR2315_PCICLK_PLLC_CLKM 0
+#define AR2315_PCICLK_PLLC_CLKM1 1
+#define AR2315_PCICLK_PLLC_CLKC 2
+#define AR2315_PCICLK_REF_CLK 3
+#define AR2315_PCICLK_DIV_M 0xc
+#define AR2315_PCICLK_DIV_S 2
+#define AR2315_PCICLK_IN_FREQ 0
+#define AR2315_PCICLK_IN_FREQ_DIV_6 1
+#define AR2315_PCICLK_IN_FREQ_DIV_8 2
+#define AR2315_PCICLK_IN_FREQ_DIV_10 3
+
+/*
+ * Observation Control Register
+ */
+#define AR2315_OCR (AR2315_DSLBASE + 0x00b0)
+
+#define AR2315_OCR_GPIO0_IRIN 0x0040
+#define AR2315_OCR_GPIO1_IROUT 0x0080
+#define AR2315_OCR_GPIO3_RXCLR 0x0200
+
+/*
+ * General Clock Control
+ */
+
+#define AR2315_MISCCLK (AR2315_DSLBASE + 0x00b4)
+
+#define AR2315_MISCCLK_PLLBYPASS_EN 0x00000001
+#define AR2315_MISCCLK_PROCREFCLK 0x00000002
+
+/*
+ * SDRAM Controller
+ * - No read or write buffers are included.
+ */
+#define AR2315_MEM_CFG (AR2315_SDRAMCTL + 0x00)
+#define AR2315_MEM_CTRL (AR2315_SDRAMCTL + 0x0c)
+#define AR2315_MEM_REF (AR2315_SDRAMCTL + 0x10)
+
+#define AR2315_MEM_CFG_DATA_WIDTH_M 0x00006000
+#define AR2315_MEM_CFG_DATA_WIDTH_S 13
+#define AR2315_MEM_CFG_COL_WIDTH_M 0x00001E00
+#define AR2315_MEM_CFG_COL_WIDTH_S 9
+#define AR2315_MEM_CFG_ROW_WIDTH_M 0x000001E0
+#define AR2315_MEM_CFG_ROW_WIDTH_S 5
+#define AR2315_MEM_CFG_BANKADDR_BITS_M 0x00000018
+#define AR2315_MEM_CFG_BANKADDR_BITS_S 3
+
+/*
+ * PCI Bus Interface Registers
+ */
+#define AR2315_PCI_1MS_REG (AR2315_PCI + 0x0008)
+
+#define AR2315_PCI_1MS_MASK 0x3FFFF /* # of AHB clk cycles in 1ms */
+
+#define AR2315_PCI_MISC_CONFIG (AR2315_PCI + 0x000c)
+
+#define AR2315_PCIMISC_TXD_EN 0x00000001 /* Enable TXD for fragments */
+#define AR2315_PCIMISC_CFG_SEL 0x00000002 /* Mem or Config cycles */
+#define AR2315_PCIMISC_GIG_MASK 0x0000000C /* bits 31-30 for pci req */
+#define AR2315_PCIMISC_RST_MODE 0x00000030
+#define AR2315_PCIRST_INPUT 0x00000000 /* 4:5=0 rst is input */
+#define AR2315_PCIRST_LOW 0x00000010 /* 4:5=1 rst to GND */
+#define AR2315_PCIRST_HIGH 0x00000020 /* 4:5=2 rst to VDD */
+#define AR2315_PCIGRANT_EN 0x00000000 /* 6:7=0 early grant en */
+#define AR2315_PCIGRANT_FRAME 0x00000040 /* 6:7=1 grant waits 4 frame */
+#define AR2315_PCIGRANT_IDLE 0x00000080 /* 6:7=2 grant waits 4 idle */
+#define AR2315_PCIGRANT_GAP 0x00000000 /* 6:7=2 grant waits 4 idle */
+#define AR2315_PCICACHE_DIS 0x00001000 /* PCI external access cache
+ * disable */
+
+#define AR2315_PCI_OUT_TSTAMP (AR2315_PCI + 0x0010)
+
+#define AR2315_PCI_UNCACHE_CFG (AR2315_PCI + 0x0014)
+
+#define AR2315_PCI_IN_EN (AR2315_PCI + 0x0100)
+
+#define AR2315_PCI_IN_EN0 0x01 /* Enable chain 0 */
+#define AR2315_PCI_IN_EN1 0x02 /* Enable chain 1 */
+#define AR2315_PCI_IN_EN2 0x04 /* Enable chain 2 */
+#define AR2315_PCI_IN_EN3 0x08 /* Enable chain 3 */
+
+#define AR2315_PCI_IN_DIS (AR2315_PCI + 0x0104)
+
+#define AR2315_PCI_IN_DIS0 0x01 /* Disable chain 0 */
+#define AR2315_PCI_IN_DIS1 0x02 /* Disable chain 1 */
+#define AR2315_PCI_IN_DIS2 0x04 /* Disable chain 2 */
+#define AR2315_PCI_IN_DIS3 0x08 /* Disable chain 3 */
+
+#define AR2315_PCI_IN_PTR (AR2315_PCI + 0x0200)
+
+#define AR2315_PCI_OUT_EN (AR2315_PCI + 0x0400)
+
+#define AR2315_PCI_OUT_EN0 0x01 /* Enable chain 0 */
+
+#define AR2315_PCI_OUT_DIS (AR2315_PCI + 0x0404)
+
+#define AR2315_PCI_OUT_DIS0 0x01 /* Disable chain 0 */
+
+#define AR2315_PCI_OUT_PTR (AR2315_PCI + 0x0408)
+
+/*
+ * PCI interrupt status
+ * Write one to clear
+ */
+#define AR2315_PCI_ISR (AR2315_PCI + 0x0500)
+
+#define AR2315_PCI_INT_TX 0x00000001 /* Desc In Completed */
+#define AR2315_PCI_INT_TXOK 0x00000002 /* Desc In OK */
+#define AR2315_PCI_INT_TXERR 0x00000004 /* Desc In ERR */
+#define AR2315_PCI_INT_TXEOL 0x00000008 /* Desc In End-of-List */
+#define AR2315_PCI_INT_RX 0x00000010 /* Desc Out Completed */
+#define AR2315_PCI_INT_RXOK 0x00000020 /* Desc Out OK */
+#define AR2315_PCI_INT_RXERR 0x00000040 /* Desc Out ERR */
+#define AR2315_PCI_INT_RXEOL 0x00000080 /* Desc Out EOL */
+#define AR2315_PCI_INT_TXOOD 0x00000200 /* Desc In Out-of-Desc */
+#define AR2315_PCI_INT_DESCMASK 0x0000FFFF /* Desc Mask */
+#define AR2315_PCI_INT_EXT 0x02000000 /* Extern PCI INTA */
+#define AR2315_PCI_INT_ABORT 0x04000000 /* PCI bus abort event */
+
+/* PCI interrupt mask */
+#define AR2315_PCI_IMR (AR2315_PCI + 0x0504)
+
+/* Global PCI interrupt enable */
+#define AR2315_PCI_IER (AR2315_PCI + 0x0508)
+
+#define AR2315_PCI_IER_DISABLE 0x00 /* disable pci interrupts */
+#define AR2315_PCI_IER_ENABLE 0x01 /* enable pci interrupts */
+
+#define AR2315_PCI_HOST_IN_EN (AR2315_PCI + 0x0800)
+#define AR2315_PCI_HOST_IN_DIS (AR2315_PCI + 0x0804)
+#define AR2315_PCI_HOST_IN_PTR (AR2315_PCI + 0x0810)
+#define AR2315_PCI_HOST_OUT_EN (AR2315_PCI + 0x0900)
+#define AR2315_PCI_HOST_OUT_DIS (AR2315_PCI + 0x0904)
+#define AR2315_PCI_HOST_OUT_PTR (AR2315_PCI + 0x0908)
+
+/*
+ * Local Bus Interface Registers
+ */
+#define AR2315_LB_CONFIG (AR2315_LOCAL + 0x0000)
+
+#define AR2315_LBCONF_OE 0x00000001 /* =1 OE is low-true */
+#define AR2315_LBCONF_CS0 0x00000002 /* =1 first CS is low-true */
+#define AR2315_LBCONF_CS1 0x00000004 /* =1 2nd CS is low-true */
+#define AR2315_LBCONF_RDY 0x00000008 /* =1 RDY is low-true */
+#define AR2315_LBCONF_WE 0x00000010 /* =1 Write En is low-true */
+#define AR2315_LBCONF_WAIT 0x00000020 /* =1 WAIT is low-true */
+#define AR2315_LBCONF_ADS 0x00000040 /* =1 Adr Strobe is low-true */
+#define AR2315_LBCONF_MOT 0x00000080 /* =0 Intel, =1 Motorola */
+#define AR2315_LBCONF_8CS 0x00000100 /* =1 8 bits CS, 0= 16bits */
+#define AR2315_LBCONF_8DS 0x00000200 /* =1 8 bits Data S, 0=16bits */
+#define AR2315_LBCONF_ADS_EN 0x00000400 /* =1 Enable ADS */
+#define AR2315_LBCONF_ADR_OE 0x00000800 /* =1 Adr cap on OE, WE or DS */
+#define AR2315_LBCONF_ADDT_MUX 0x00001000 /* =1 Adr and Data share bus */
+#define AR2315_LBCONF_DATA_OE 0x00002000 /* =1 Data cap on OE, WE, DS */
+#define AR2315_LBCONF_16DATA 0x00004000 /* =1 Data is 16 bits wide */
+#define AR2315_LBCONF_SWAPDT 0x00008000 /* =1 Byte swap data */
+#define AR2315_LBCONF_SYNC 0x00010000 /* =1 Bus synchronous to clk */
+#define AR2315_LBCONF_INT 0x00020000 /* =1 Intr is low true */
+#define AR2315_LBCONF_INT_CTR0 0x00000000 /* GND high-Z, Vdd is high-Z */
+#define AR2315_LBCONF_INT_CTR1 0x00040000 /* GND drive, Vdd is high-Z */
+#define AR2315_LBCONF_INT_CTR2 0x00080000 /* GND high-Z, Vdd drive */
+#define AR2315_LBCONF_INT_CTR3 0x000C0000 /* GND drive, Vdd drive */
+#define AR2315_LBCONF_RDY_WAIT 0x00100000 /* =1 RDY is negative of WAIT */
+#define AR2315_LBCONF_INT_PULSE 0x00200000 /* =1 Interrupt is a pulse */
+#define AR2315_LBCONF_ENABLE 0x00400000 /* =1 Falcon respond to LB */
+
+#define AR2315_LB_CLKSEL (AR2315_LOCAL + 0x0004)
+
+#define AR2315_LBCLK_EXT 0x0001 /* use external clk for lb */
+
+#define AR2315_LB_1MS (AR2315_LOCAL + 0x0008)
+
+#define AR2315_LB1MS_MASK 0x3FFFF /* # of AHB clk cycles in 1ms */
+
+#define AR2315_LB_MISCCFG (AR2315_LOCAL + 0x000C)
+
+#define AR2315_LBM_TXD_EN 0x00000001 /* Enable TXD for fragments */
+#define AR2315_LBM_RX_INTEN 0x00000002 /* Enable LB ints on RX ready */
+#define AR2315_LBM_MBOXWR_INTEN 0x00000004 /* Enable LB ints on mbox wr */
+#define AR2315_LBM_MBOXRD_INTEN 0x00000008 /* Enable LB ints on mbox rd */
+#define AR2315_LMB_DESCSWAP_EN 0x00000010 /* Byte swap desc enable */
+#define AR2315_LBM_TIMEOUT_M 0x00FFFF80
+#define AR2315_LBM_TIMEOUT_S 7
+#define AR2315_LBM_PORTMUX 0x07000000
+
+#define AR2315_LB_RXTSOFF (AR2315_LOCAL + 0x0010)
+
+#define AR2315_LB_TX_CHAIN_EN (AR2315_LOCAL + 0x0100)
+
+#define AR2315_LB_TXEN_0 0x01
+#define AR2315_LB_TXEN_1 0x02
+#define AR2315_LB_TXEN_2 0x04
+#define AR2315_LB_TXEN_3 0x08
+
+#define AR2315_LB_TX_CHAIN_DIS (AR2315_LOCAL + 0x0104)
+#define AR2315_LB_TX_DESC_PTR (AR2315_LOCAL + 0x0200)
+
+#define AR2315_LB_RX_CHAIN_EN (AR2315_LOCAL + 0x0400)
+
+#define AR2315_LB_RXEN 0x01
+
+#define AR2315_LB_RX_CHAIN_DIS (AR2315_LOCAL + 0x0404)
+#define AR2315_LB_RX_DESC_PTR (AR2315_LOCAL + 0x0408)
+
+#define AR2315_LB_INT_STATUS (AR2315_LOCAL + 0x0500)
+
+#define AR2315_INT_TX_DESC 0x0001
+#define AR2315_INT_TX_OK 0x0002
+#define AR2315_INT_TX_ERR 0x0004
+#define AR2315_INT_TX_EOF 0x0008
+#define AR2315_INT_RX_DESC 0x0010
+#define AR2315_INT_RX_OK 0x0020
+#define AR2315_INT_RX_ERR 0x0040
+#define AR2315_INT_RX_EOF 0x0080
+#define AR2315_INT_TX_TRUNC 0x0100
+#define AR2315_INT_TX_STARVE 0x0200
+#define AR2315_INT_LB_TIMEOUT 0x0400
+#define AR2315_INT_LB_ERR 0x0800
+#define AR2315_INT_MBOX_WR 0x1000
+#define AR2315_INT_MBOX_RD 0x2000
+
+/* Bit definitions for INT MASK are the same as INT_STATUS */
+#define AR2315_LB_INT_MASK (AR2315_LOCAL + 0x0504)
+
+#define AR2315_LB_INT_EN (AR2315_LOCAL + 0x0508)
+#define AR2315_LB_MBOX (AR2315_LOCAL + 0x0600)
+
+/*
+ * IR Interface Registers
+ */
+#define AR2315_IR_PKTDATA (AR2315_IR + 0x0000)
+
+#define AR2315_IR_PKTLEN (AR2315_IR + 0x07fc) /* 0 - 63 */
+
+#define AR2315_IR_CONTROL (AR2315_IR + 0x0800)
+
+#define AR2315_IRCTL_TX 0x00000000 /* use as tranmitter */
+#define AR2315_IRCTL_RX 0x00000001 /* use as receiver */
+#define AR2315_IRCTL_SAMPLECLK_M 0x00003ffe /* Sample clk divisor */
+#define AR2315_IRCTL_SAMPLECLK_S 1
+#define AR2315_IRCTL_OUTPUTCLK_M 0x03ffc000 /* Output clk div */
+#define AR2315_IRCTL_OUTPUTCLK_S 14
+
+#define AR2315_IR_STATUS (AR2315_IR + 0x0804)
+
+#define AR2315_IRSTS_RX 0x00000001 /* receive in progress */
+#define AR2315_IRSTS_TX 0x00000002 /* transmit in progress */
+
+#define AR2315_IR_CONFIG (AR2315_IR + 0x0808)
+
+#define AR2315_IRCFG_INVIN 0x00000001 /* invert in polarity */
+#define AR2315_IRCFG_INVOUT 0x00000002 /* invert out polarity */
+#define AR2315_IRCFG_SEQ_START_WIN_SEL 0x00000004 /* 1 => 28, 0 => 7 */
+#define AR2315_IRCFG_SEQ_START_THRESH 0x000000f0
+#define AR2315_IRCFG_SEQ_END_UNIT_SEL 0x00000100
+#define AR2315_IRCFG_SEQ_END_UNIT_THRESH 0x00007e00
+#define AR2315_IRCFG_SEQ_END_WIN_SEL 0x00008000
+#define AR2315_IRCFG_SEQ_END_WIN_THRESH 0x001f0000
+#define AR2315_IRCFG_NUM_BACKOFF_WORDS 0x01e00000
+
+#endif /* __ASM_MACH_AR231X_AR2315_REGS_H */
diff --git a/arch/mips/include/asm/mach-ar231x/cpu-feature-overrides.h b/arch/mips/include/asm/mach-ar231x/cpu-feature-overrides.h
index 337fe3e..5b7cee6 100644
--- a/arch/mips/include/asm/mach-ar231x/cpu-feature-overrides.h
+++ b/arch/mips/include/asm/mach-ar231x/cpu-feature-overrides.h
@@ -36,11 +36,15 @@
/* #define cpu_has_mcheck ? */
#define cpu_has_ejtag 1

+#if !defined(CONFIG_SOC_AR5312)
+# define cpu_has_llsc 1
+#else
/*
* The MIPS 4Kc V0.9 core in the AR5312/AR2312 have problems with the
* ll/sc instructions.
*/
-#define cpu_has_llsc 0
+# define cpu_has_llsc 0
+#endif

#define cpu_has_mips16 0
#define cpu_has_mdmx 0
@@ -56,6 +60,10 @@

#define cpu_has_mips32r1 1

+#if !defined(CONFIG_SOC_AR5312)
+# define cpu_has_mips32r2 1
+#endif
+
#define cpu_has_mips64r1 0
#define cpu_has_mips64r2 0
--
1.8.5.5
Jonas Gorski
2014-09-29 09:50:10 UTC
Permalink
Post by Sergey Ryazanov
Add basic support for Atheros AR2315+ SoCs: registers definition file
and initial setup code.
Same comment regarding the style of checking the SoC family as for patch 2.


Regards
Jonas
Sergey Ryazanov
2014-09-28 18:33:03 UTC
Permalink
Add interrupts initialization and handling routines, also add AHB bus
error interrupt handlers for both SoCs families.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
---

Changes since RFC:
- add all interrupts
- use dynamic IRQ numbers allocation

arch/mips/ar231x/ar2315.c | 111 ++++++++++++++++++++++++
arch/mips/ar231x/ar2315.h | 2 +
arch/mips/ar231x/ar5312.c | 106 ++++++++++++++++++++++
arch/mips/ar231x/ar5312.h | 2 +
arch/mips/ar231x/board.c | 8 ++
arch/mips/ar231x/devices.h | 1 +
arch/mips/include/asm/mach-ar231x/ar2315_regs.h | 23 +++++
arch/mips/include/asm/mach-ar231x/ar231x.h | 2 +
arch/mips/include/asm/mach-ar231x/ar5312_regs.h | 23 +++++
9 files changed, 278 insertions(+)

diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
index 5f8b7c4..320893a 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -27,6 +27,117 @@
#include "devices.h"
#include "ar2315.h"

+static unsigned ar2315_misc_irq_base;
+
+static irqreturn_t ar2315_ahb_err_handler(int cpl, void *dev_id)
+{
+ ar231x_write_reg(AR2315_AHB_ERR0, AR2315_AHB_ERROR_DET);
+ ar231x_read_reg(AR2315_AHB_ERR1);
+
+ pr_emerg("AHB fatal error\n");
+ machine_restart("AHB error"); /* Catastrophic failure */
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction ar2315_ahb_err_interrupt = {
+ .handler = ar2315_ahb_err_handler,
+ .name = "ar2315-ahb-error",
+};
+
+static void ar2315_misc_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ u32 pending = ar231x_read_reg(AR2315_ISR) & ar231x_read_reg(AR2315_IMR);
+ unsigned base = ar2315_misc_irq_base;
+
+ if (pending & AR2315_ISR_SPI)
+ generic_handle_irq(base + AR2315_MISC_IRQ_SPI);
+ else if (pending & AR2315_ISR_TIMER)
+ generic_handle_irq(base + AR2315_MISC_IRQ_TIMER);
+ else if (pending & AR2315_ISR_AHB)
+ generic_handle_irq(base + AR2315_MISC_IRQ_AHB);
+ else if (pending & AR2315_ISR_GPIO) {
+ ar231x_write_reg(AR2315_ISR, AR2315_ISR_GPIO);
+ generic_handle_irq(base + AR2315_MISC_IRQ_GPIO);
+ } else if (pending & AR2315_ISR_UART0)
+ generic_handle_irq(base + AR2315_MISC_IRQ_UART0);
+ else if (pending & AR2315_ISR_WD) {
+ ar231x_write_reg(AR2315_ISR, AR2315_ISR_WD);
+ generic_handle_irq(base + AR2315_MISC_IRQ_WATCHDOG);
+ } else
+ spurious_interrupt();
+}
+
+static void ar2315_misc_irq_unmask(struct irq_data *d)
+{
+ u32 imr = ar231x_read_reg(AR2315_IMR);
+
+ imr |= 1 << (d->irq - ar2315_misc_irq_base);
+ ar231x_write_reg(AR2315_IMR, imr);
+}
+
+static void ar2315_misc_irq_mask(struct irq_data *d)
+{
+ u32 imr = ar231x_read_reg(AR2315_IMR);
+
+ imr &= ~(1 << (d->irq - ar2315_misc_irq_base));
+ ar231x_write_reg(AR2315_IMR, imr);
+}
+
+static struct irq_chip ar2315_misc_irq_chip = {
+ .name = "ar2315-misc",
+ .irq_unmask = ar2315_misc_irq_unmask,
+ .irq_mask = ar2315_misc_irq_mask,
+};
+
+/*
+ * Called when an interrupt is received, this function
+ * determines exactly which interrupt it was, and it
+ * invokes the appropriate handler.
+ *
+ * Implicitly, we also define interrupt priority by
+ * choosing which to dispatch first.
+ */
+static void ar2315_irq_dispatch(void)
+{
+ u32 pending = read_c0_status() & read_c0_cause();
+
+ if (pending & CAUSEF_IP3)
+ do_IRQ(AR2315_IRQ_WLAN0);
+ else if (pending & CAUSEF_IP2)
+ do_IRQ(AR2315_IRQ_MISC);
+ else if (pending & CAUSEF_IP7)
+ do_IRQ(AR231X_IRQ_CPU_CLOCK);
+ else
+ spurious_interrupt();
+}
+
+void __init ar2315_arch_init_irq(void)
+{
+ unsigned i;
+ int res;
+
+ if (!is_2315())
+ return;
+
+ ar231x_irq_dispatch = ar2315_irq_dispatch;
+
+ res = irq_alloc_descs(-1, 0, AR2315_MISC_IRQ_COUNT, 0);
+ if (res < 0)
+ pr_emerg("Failed to allocate misc IRQ numbers\n");
+ ar2315_misc_irq_base = res;
+
+ for (i = 0; i < AR2315_MISC_IRQ_COUNT; i++) {
+ unsigned irq = ar2315_misc_irq_base + i;
+
+ irq_set_chip_and_handler(irq, &ar2315_misc_irq_chip,
+ handle_level_irq);
+ }
+ setup_irq(ar2315_misc_irq_base + AR2315_MISC_IRQ_AHB,
+ &ar2315_ahb_err_interrupt);
+ irq_set_chained_handler(AR2315_IRQ_MISC, ar2315_misc_irq_handler);
+}
+
static void ar2315_restart(char *command)
{
void (*mips_reset_vec)(void) = (void *)0xbfc00000;
diff --git a/arch/mips/ar231x/ar2315.h b/arch/mips/ar231x/ar2315.h
index 98d32b2..2a57858 100644
--- a/arch/mips/ar231x/ar2315.h
+++ b/arch/mips/ar231x/ar2315.h
@@ -3,12 +3,14 @@

#ifdef CONFIG_SOC_AR2315

+void ar2315_arch_init_irq(void);
void ar2315_plat_time_init(void);
void ar2315_plat_mem_setup(void);
void ar2315_prom_init(void);

#else

+static inline void ar2315_arch_init_irq(void) {}
static inline void ar2315_plat_time_init(void) {}
static inline void ar2315_plat_mem_setup(void) {}
static inline void ar2315_prom_init(void) {}
diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
index 909bee0..3f81d33 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -27,6 +27,112 @@
#include "devices.h"
#include "ar5312.h"

+static unsigned ar5312_misc_irq_base;
+
+static irqreturn_t ar5312_ahb_err_handler(int cpl, void *dev_id)
+{
+ u32 proc1 = ar231x_read_reg(AR5312_PROC1);
+ u32 proc_addr = ar231x_read_reg(AR5312_PROCADDR); /* clears error */
+ u32 dma1 = ar231x_read_reg(AR5312_DMA1);
+ u32 dma_addr = ar231x_read_reg(AR5312_DMAADDR); /* clears error */
+
+ pr_emerg("AHB interrupt: PROCADDR=0x%8.8x PROC1=0x%8.8x DMAADDR=0x%8.8x DMA1=0x%8.8x\n",
+ proc_addr, proc1, dma_addr, dma1);
+
+ machine_restart("AHB error"); /* Catastrophic failure */
+ return IRQ_HANDLED;
+}
+
+static struct irqaction ar5312_ahb_err_interrupt = {
+ .handler = ar5312_ahb_err_handler,
+ .name = "ar5312-ahb-error",
+};
+
+static void ar5312_misc_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ u32 pending = ar231x_read_reg(AR5312_ISR) & ar231x_read_reg(AR5312_IMR);
+ unsigned base = ar5312_misc_irq_base;
+
+ if (pending & AR5312_ISR_TIMER) {
+ generic_handle_irq(base + AR5312_MISC_IRQ_TIMER);
+ (void)ar231x_read_reg(AR5312_TIMER);
+ } else if (pending & AR5312_ISR_AHBPROC)
+ generic_handle_irq(base + AR5312_MISC_IRQ_AHB_PROC);
+ else if (pending & AR5312_ISR_UART0)
+ generic_handle_irq(base + AR5312_MISC_IRQ_UART0);
+ else if (pending & AR5312_ISR_WD)
+ generic_handle_irq(base + AR5312_MISC_IRQ_WATCHDOG);
+ else
+ spurious_interrupt();
+}
+
+/* Enable the specified AR5312_MISC_IRQ interrupt */
+static void ar5312_misc_irq_unmask(struct irq_data *d)
+{
+ u32 imr = ar231x_read_reg(AR5312_IMR);
+
+ imr |= 1 << (d->irq - ar5312_misc_irq_base);
+ ar231x_write_reg(AR5312_IMR, imr);
+}
+
+/* Disable the specified AR5312_MISC_IRQ interrupt */
+static void ar5312_misc_irq_mask(struct irq_data *d)
+{
+ u32 imr = ar231x_read_reg(AR5312_IMR);
+
+ imr &= ~(1 << (d->irq - ar5312_misc_irq_base));
+ ar231x_write_reg(AR5312_IMR, imr);
+ ar231x_read_reg(AR5312_IMR); /* flush write buffer */
+}
+
+static struct irq_chip ar5312_misc_irq_chip = {
+ .name = "ar5312-misc",
+ .irq_unmask = ar5312_misc_irq_unmask,
+ .irq_mask = ar5312_misc_irq_mask,
+};
+
+static void ar5312_irq_dispatch(void)
+{
+ u32 pending = read_c0_status() & read_c0_cause();
+
+ if (pending & CAUSEF_IP2)
+ do_IRQ(AR5312_IRQ_WLAN0);
+ else if (pending & CAUSEF_IP5)
+ do_IRQ(AR5312_IRQ_WLAN1);
+ else if (pending & CAUSEF_IP6)
+ do_IRQ(AR5312_IRQ_MISC);
+ else if (pending & CAUSEF_IP7)
+ do_IRQ(AR231X_IRQ_CPU_CLOCK);
+ else
+ spurious_interrupt();
+}
+
+void __init ar5312_arch_init_irq(void)
+{
+ unsigned i;
+ int res;
+
+ if (!is_5312())
+ return;
+
+ ar231x_irq_dispatch = ar5312_irq_dispatch;
+
+ res = irq_alloc_descs(-1, 0, AR5312_MISC_IRQ_COUNT, 0);
+ if (res < 0)
+ pr_emerg("Failed to allocate misc IRQ numbers\n");
+ ar5312_misc_irq_base = res;
+
+ for (i = 0; i < AR5312_MISC_IRQ_COUNT; i++) {
+ unsigned irq = ar5312_misc_irq_base + i;
+
+ irq_set_chip_and_handler(irq, &ar5312_misc_irq_chip,
+ handle_level_irq);
+ }
+ setup_irq(ar5312_misc_irq_base + AR5312_MISC_IRQ_AHB_PROC,
+ &ar5312_ahb_err_interrupt);
+ irq_set_chained_handler(AR5312_IRQ_MISC, ar5312_misc_irq_handler);
+}
+
static void ar5312_restart(char *command)
{
/* reset the system */
diff --git a/arch/mips/ar231x/ar5312.h b/arch/mips/ar231x/ar5312.h
index 339b28e..b60ad38 100644
--- a/arch/mips/ar231x/ar5312.h
+++ b/arch/mips/ar231x/ar5312.h
@@ -3,12 +3,14 @@

#ifdef CONFIG_SOC_AR5312

+void ar5312_arch_init_irq(void);
void ar5312_plat_time_init(void);
void ar5312_plat_mem_setup(void);
void ar5312_prom_init(void);

#else

+static inline void ar5312_arch_init_irq(void) {}
static inline void ar5312_plat_time_init(void) {}
static inline void ar5312_plat_mem_setup(void) {}
static inline void ar5312_prom_init(void) {}
diff --git a/arch/mips/ar231x/board.c b/arch/mips/ar231x/board.c
index f50a7cf..24a00b4 100644
--- a/arch/mips/ar231x/board.c
+++ b/arch/mips/ar231x/board.c
@@ -16,9 +16,12 @@
#include <asm/bootinfo.h>
#include <asm/time.h>

+#include "devices.h"
#include "ar5312.h"
#include "ar2315.h"

+void (*ar231x_irq_dispatch)(void);
+
static void ar231x_halt(void)
{
local_irq_disable();
@@ -40,6 +43,7 @@ void __init plat_mem_setup(void)

asmlinkage void plat_irq_dispatch(void)
{
+ ar231x_irq_dispatch();
}

void __init plat_time_init(void)
@@ -57,5 +61,9 @@ void __init arch_init_irq(void)
{
clear_c0_status(ST0_IM);
mips_cpu_irq_init();
+
+ /* Initialize interrupt controllers */
+ ar5312_arch_init_irq();
+ ar2315_arch_init_irq();
}

diff --git a/arch/mips/ar231x/devices.h b/arch/mips/ar231x/devices.h
index 1590577..82fa6fb 100644
--- a/arch/mips/ar231x/devices.h
+++ b/arch/mips/ar231x/devices.h
@@ -8,6 +8,7 @@ enum {
};

extern int ar231x_devtype;
+extern void (*ar231x_irq_dispatch)(void);

static inline bool is_2315(void)
{
diff --git a/arch/mips/include/asm/mach-ar231x/ar2315_regs.h b/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
index 91197b6..a65d578 100644
--- a/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
+++ b/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
@@ -15,6 +15,29 @@
#define __ASM_MACH_AR231X_AR2315_REGS_H

/*
+ * IRQs
+ */
+#define AR2315_IRQ_MISC (MIPS_CPU_IRQ_BASE + 2) /* C0_CAUSE: 0x0400 */
+#define AR2315_IRQ_WLAN0 (MIPS_CPU_IRQ_BASE + 3) /* C0_CAUSE: 0x0800 */
+#define AR2315_IRQ_ENET0 (MIPS_CPU_IRQ_BASE + 4) /* C0_CAUSE: 0x1000 */
+#define AR2315_IRQ_LCBUS_PCI (MIPS_CPU_IRQ_BASE + 5) /* C0_CAUSE: 0x2000 */
+#define AR2315_IRQ_WLAN0_POLL (MIPS_CPU_IRQ_BASE + 6) /* C0_CAUSE: 0x4000 */
+
+/*
+ * Miscellaneous interrupts, which share IP2.
+ */
+#define AR2315_MISC_IRQ_UART0 0
+#define AR2315_MISC_IRQ_I2C_RSVD 1
+#define AR2315_MISC_IRQ_SPI 2
+#define AR2315_MISC_IRQ_AHB 3
+#define AR2315_MISC_IRQ_APB 4
+#define AR2315_MISC_IRQ_TIMER 5
+#define AR2315_MISC_IRQ_GPIO 6
+#define AR2315_MISC_IRQ_WATCHDOG 7
+#define AR2315_MISC_IRQ_IR_RSVD 8
+#define AR2315_MISC_IRQ_COUNT 9
+
+/*
* Address map
*/
#define AR2315_SPI_READ 0x08000000 /* SPI flash */
diff --git a/arch/mips/include/asm/mach-ar231x/ar231x.h b/arch/mips/include/asm/mach-ar231x/ar231x.h
index b830723..a9f05aa 100644
--- a/arch/mips/include/asm/mach-ar231x/ar231x.h
+++ b/arch/mips/include/asm/mach-ar231x/ar231x.h
@@ -3,6 +3,8 @@

#include <linux/io.h>

+#define AR231X_IRQ_CPU_CLOCK (MIPS_CPU_IRQ_BASE + 7) /* C0_CAUSE: 0x8000 */
+
#define AR231X_REG_MS(_val, _field) (((_val) & _field##_M) >> _field##_S)

static inline u32 ar231x_read_reg(u32 reg)
diff --git a/arch/mips/include/asm/mach-ar231x/ar5312_regs.h b/arch/mips/include/asm/mach-ar231x/ar5312_regs.h
index 5eb22fd..c7055e32 100644
--- a/arch/mips/include/asm/mach-ar231x/ar5312_regs.h
+++ b/arch/mips/include/asm/mach-ar231x/ar5312_regs.h
@@ -12,6 +12,29 @@
#define __ASM_MACH_AR231X_AR5312_REGS_H

/*
+ * IRQs
+ */
+#define AR5312_IRQ_WLAN0 (MIPS_CPU_IRQ_BASE + 2) /* C0_CAUSE: 0x0400 */
+#define AR5312_IRQ_ENET0 (MIPS_CPU_IRQ_BASE + 3) /* C0_CAUSE: 0x0800 */
+#define AR5312_IRQ_ENET1 (MIPS_CPU_IRQ_BASE + 4) /* C0_CAUSE: 0x1000 */
+#define AR5312_IRQ_WLAN1 (MIPS_CPU_IRQ_BASE + 5) /* C0_CAUSE: 0x2000 */
+#define AR5312_IRQ_MISC (MIPS_CPU_IRQ_BASE + 6) /* C0_CAUSE: 0x4000 */
+
+/*
+ * Miscellaneous interrupts, which share IP6.
+ */
+#define AR5312_MISC_IRQ_TIMER 0
+#define AR5312_MISC_IRQ_AHB_PROC 1
+#define AR5312_MISC_IRQ_AHB_DMA 2
+#define AR5312_MISC_IRQ_GPIO 3
+#define AR5312_MISC_IRQ_UART0 4
+#define AR5312_MISC_IRQ_UART0_DMA 5
+#define AR5312_MISC_IRQ_WATCHDOG 6
+#define AR5312_MISC_IRQ_LOCAL 7
+#define AR5312_MISC_IRQ_SPI 8
+#define AR5312_MISC_IRQ_COUNT 9
+
+/*
* Address Map
*
* The AR5312 supports 2 enet MACS, even though many reference boards only
--
1.8.5.5
Sergey Ryazanov
2014-09-28 18:33:04 UTC
Permalink
Signed-off-by: Sergey Ryazanov <***@gmail.com>
---
arch/mips/Kconfig | 1 +
arch/mips/ar231x/Makefile | 2 ++
arch/mips/ar231x/early_printk.c | 45 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 48 insertions(+)
create mode 100644 arch/mips/ar231x/early_printk.c

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index bd81f7a..b89bfdf 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -83,6 +83,7 @@ config AR231X
select SYS_SUPPORTS_BIG_ENDIAN
select SYS_SUPPORTS_32BIT_KERNEL
select ARCH_REQUIRE_GPIOLIB
+ select SYS_HAS_EARLY_PRINTK
help
Support for Atheros AR231x and Atheros AR531x based boards

diff --git a/arch/mips/ar231x/Makefile b/arch/mips/ar231x/Makefile
index 201b7d4..eabad7d 100644
--- a/arch/mips/ar231x/Makefile
+++ b/arch/mips/ar231x/Makefile
@@ -10,5 +10,7 @@

obj-y += board.o prom.o devices.o

+obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+
obj-$(CONFIG_SOC_AR5312) += ar5312.o
obj-$(CONFIG_SOC_AR2315) += ar2315.o
diff --git a/arch/mips/ar231x/early_printk.c b/arch/mips/ar231x/early_printk.c
new file mode 100644
index 0000000..393c5ab
--- /dev/null
+++ b/arch/mips/ar231x/early_printk.c
@@ -0,0 +1,45 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2010 Gabor Juhos <***@openwrt.org>
+ */
+
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/serial_reg.h>
+
+#include <ar2315_regs.h>
+#include <ar5312_regs.h>
+#include "devices.h"
+
+static inline void prom_uart_wr(void __iomem *base, unsigned reg,
+ unsigned char ch)
+{
+ __raw_writel(ch, base + 4 * reg);
+}
+
+static inline unsigned char prom_uart_rr(void __iomem *base, unsigned reg)
+{
+ return __raw_readl(base + 4 * reg);
+}
+
+void prom_putchar(unsigned char ch)
+{
+ static void __iomem *base;
+
+ if (unlikely(base == NULL)) {
+ if (is_2315())
+ base = (void __iomem *)(KSEG1ADDR(AR2315_UART0));
+ else
+ base = (void __iomem *)(KSEG1ADDR(AR5312_UART0));
+ }
+
+ while ((prom_uart_rr(base, UART_LSR) & UART_LSR_THRE) == 0)
+ ;
+ prom_uart_wr(base, UART_TX, ch);
+ while ((prom_uart_rr(base, UART_LSR) & UART_LSR_THRE) == 0)
+ ;
+}
+
--
1.8.5.5
Jonas Gorski
2014-09-29 12:47:22 UTC
Permalink
Post by Sergey Ryazanov
---
arch/mips/Kconfig | 1 +
arch/mips/ar231x/Makefile | 2 ++
arch/mips/ar231x/early_printk.c | 45 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 48 insertions(+)
create mode 100644 arch/mips/ar231x/early_printk.c
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index bd81f7a..b89bfdf 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -83,6 +83,7 @@ config AR231X
select SYS_SUPPORTS_BIG_ENDIAN
select SYS_SUPPORTS_32BIT_KERNEL
select ARCH_REQUIRE_GPIOLIB
+ select SYS_HAS_EARLY_PRINTK
help
Support for Atheros AR231x and Atheros AR531x based boards
diff --git a/arch/mips/ar231x/Makefile b/arch/mips/ar231x/Makefile
index 201b7d4..eabad7d 100644
--- a/arch/mips/ar231x/Makefile
+++ b/arch/mips/ar231x/Makefile
@@ -10,5 +10,7 @@
obj-y += board.o prom.o devices.o
+obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+
obj-$(CONFIG_SOC_AR5312) += ar5312.o
obj-$(CONFIG_SOC_AR2315) += ar2315.o
diff --git a/arch/mips/ar231x/early_printk.c b/arch/mips/ar231x/early_printk.c
new file mode 100644
index 0000000..393c5ab
--- /dev/null
+++ b/arch/mips/ar231x/early_printk.c
@@ -0,0 +1,45 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/serial_reg.h>
+
+#include <ar2315_regs.h>
+#include <ar5312_regs.h>
+#include "devices.h"
+
+static inline void prom_uart_wr(void __iomem *base, unsigned reg,
+ unsigned char ch)
+{
+ __raw_writel(ch, base + 4 * reg);
+}
+
+static inline unsigned char prom_uart_rr(void __iomem *base, unsigned reg)
+{
+ return __raw_readl(base + 4 * reg);
+}
+
+void prom_putchar(unsigned char ch)
+{
+ static void __iomem *base;
+
+ if (unlikely(base == NULL)) {
+ if (is_2315())
+ base = (void __iomem *)(KSEG1ADDR(AR2315_UART0));
+ else
+ base = (void __iomem *)(KSEG1ADDR(AR5312_UART0));
+ }
+
+ while ((prom_uart_rr(base, UART_LSR) & UART_LSR_THRE) == 0)
+ ;
+ prom_uart_wr(base, UART_TX, ch);
+ while ((prom_uart_rr(base, UART_LSR) & UART_LSR_THRE) == 0)
+ ;
+}
Have you tried using EARLY_PRINTK_8250 instead? Since this is a 8250 anyway.


Jonas
Sergey Ryazanov
2014-09-29 19:36:51 UTC
Permalink
Post by Jonas Gorski
Have you tried using EARLY_PRINTK_8250 instead? Since this is a 8250 anyway.
I plan to do that a bit later.
--
BR,
Sergey
Sergey Ryazanov
2014-09-28 18:33:05 UTC
Permalink
Signed-off-by: Sergey Ryazanov <***@gmail.com>
---

Changes since RFC:
- register UART during arch initialization

arch/mips/ar231x/ar2315.c | 9 +++++++++
arch/mips/ar231x/ar2315.h | 2 ++
arch/mips/ar231x/ar5312.c | 9 +++++++++
arch/mips/ar231x/ar5312.h | 2 ++
arch/mips/ar231x/devices.c | 25 +++++++++++++++++++++++++
arch/mips/ar231x/devices.h | 2 ++
6 files changed, 49 insertions(+)

diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
index 320893a..3d21a25a 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -245,3 +245,12 @@ void __init ar2315_prom_init(void)
add_memory_region(0, memsize, BOOT_MEM_RAM);
}

+void __init ar2315_arch_init(void)
+{
+ if (!is_2315())
+ return;
+
+ ar231x_serial_setup(AR2315_UART0, ar2315_misc_irq_base +
+ AR2315_MISC_IRQ_UART0, ar2315_apb_frequency());
+}
+
diff --git a/arch/mips/ar231x/ar2315.h b/arch/mips/ar231x/ar2315.h
index 2a57858..9af22db 100644
--- a/arch/mips/ar231x/ar2315.h
+++ b/arch/mips/ar231x/ar2315.h
@@ -7,6 +7,7 @@ void ar2315_arch_init_irq(void);
void ar2315_plat_time_init(void);
void ar2315_plat_mem_setup(void);
void ar2315_prom_init(void);
+void ar2315_arch_init(void);

#else

@@ -14,6 +15,7 @@ static inline void ar2315_arch_init_irq(void) {}
static inline void ar2315_plat_time_init(void) {}
static inline void ar2315_plat_mem_setup(void) {}
static inline void ar2315_prom_init(void) {}
+static inline void ar2315_arch_init(void) {}

#endif

diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
index 3f81d33..138d7e1 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -247,3 +247,12 @@ void __init ar5312_prom_init(void)
add_memory_region(0, memsize, BOOT_MEM_RAM);
}

+void __init ar5312_arch_init(void)
+{
+ if (!is_5312())
+ return;
+
+ ar231x_serial_setup(AR5312_UART0, ar5312_misc_irq_base +
+ AR5312_MISC_IRQ_UART0, ar5312_sys_frequency());
+}
+
diff --git a/arch/mips/ar231x/ar5312.h b/arch/mips/ar231x/ar5312.h
index b60ad38..d04a33d 100644
--- a/arch/mips/ar231x/ar5312.h
+++ b/arch/mips/ar231x/ar5312.h
@@ -7,6 +7,7 @@ void ar5312_arch_init_irq(void);
void ar5312_plat_time_init(void);
void ar5312_plat_mem_setup(void);
void ar5312_prom_init(void);
+void ar5312_arch_init(void);

#else

@@ -14,6 +15,7 @@ static inline void ar5312_arch_init_irq(void) {}
static inline void ar5312_plat_time_init(void) {}
static inline void ar5312_plat_mem_setup(void) {}
static inline void ar5312_prom_init(void) {}
+static inline void ar5312_arch_init(void) {}

#endif

diff --git a/arch/mips/ar231x/devices.c b/arch/mips/ar231x/devices.c
index f71a643..85caae4 100644
--- a/arch/mips/ar231x/devices.c
+++ b/arch/mips/ar231x/devices.c
@@ -1,5 +1,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/serial_8250.h>
#include <asm/bootinfo.h>

#include "devices.h"
@@ -18,3 +19,27 @@ const char *get_system_type(void)
return devtype_strings[ar231x_devtype];
}

+void __init ar231x_serial_setup(u32 mapbase, int irq, unsigned int uartclk)
+{
+ struct uart_port s;
+
+ memset(&s, 0, sizeof(s));
+
+ s.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP;
+ s.iotype = UPIO_MEM32;
+ s.irq = irq;
+ s.regshift = 2;
+ s.mapbase = mapbase;
+ s.uartclk = uartclk;
+
+ early_serial_setup(&s);
+}
+
+static int __init ar231x_arch_init(void)
+{
+ ar5312_arch_init();
+ ar2315_arch_init();
+ return 0;
+}
+
+arch_initcall(ar231x_arch_init);
diff --git a/arch/mips/ar231x/devices.h b/arch/mips/ar231x/devices.h
index 82fa6fb..9f83150 100644
--- a/arch/mips/ar231x/devices.h
+++ b/arch/mips/ar231x/devices.h
@@ -10,6 +10,8 @@ enum {
extern int ar231x_devtype;
extern void (*ar231x_irq_dispatch)(void);

+void ar231x_serial_setup(u32 mapbase, int irq, unsigned int uartclk);
+
static inline bool is_2315(void)
{
return (current_cpu_data.cputype == CPU_4KEC);
--
1.8.5.5
Sergey Ryazanov
2014-09-28 18:33:06 UTC
Permalink
All boards based on AR5312/AR2315 SoC have a special structure located
at the end of flash. This structure contains board-specific data such as
Ethernet and Wireless MAC addresses. The flash is mapped to the memmory
at predefined location.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
---
arch/mips/ar231x/ar2315.c | 17 +++
arch/mips/ar231x/ar2315.h | 2 +
arch/mips/ar231x/ar5312.c | 41 ++++++
arch/mips/ar231x/ar5312.h | 2 +
arch/mips/ar231x/board.c | 153 +++++++++++++++++++++
arch/mips/ar231x/devices.c | 14 ++
arch/mips/ar231x/devices.h | 2 +
.../mips/include/asm/mach-ar231x/ar231x_platform.h | 73 ++++++++++
8 files changed, 304 insertions(+)
create mode 100644 arch/mips/include/asm/mach-ar231x/ar231x_platform.h

diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
index 3d21a25a..0b973eb 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -138,6 +138,23 @@ void __init ar2315_arch_init_irq(void)
irq_set_chained_handler(AR2315_IRQ_MISC, ar2315_misc_irq_handler);
}

+/*
+ * NB: We use mapping size that is larger than the actual flash size,
+ * but this shouldn't be a problem here, because the flash will simply
+ * be mapped multiple times.
+ */
+static const u8 * const __initconst
+ar2315_flash_limit = (u8 *)KSEG1ADDR(AR2315_SPI_READ + 0x1000000);
+
+void __init ar2315_init_devices(void)
+{
+ if (!is_2315())
+ return;
+
+ /* Find board configuration */
+ ar231x_find_config(ar2315_flash_limit);
+}
+
static void ar2315_restart(char *command)
{
void (*mips_reset_vec)(void) = (void *)0xbfc00000;
diff --git a/arch/mips/ar231x/ar2315.h b/arch/mips/ar231x/ar2315.h
index 9af22db..308379a 100644
--- a/arch/mips/ar231x/ar2315.h
+++ b/arch/mips/ar231x/ar2315.h
@@ -4,6 +4,7 @@
#ifdef CONFIG_SOC_AR2315

void ar2315_arch_init_irq(void);
+void ar2315_init_devices(void);
void ar2315_plat_time_init(void);
void ar2315_plat_mem_setup(void);
void ar2315_prom_init(void);
@@ -12,6 +13,7 @@ void ar2315_arch_init(void);
#else

static inline void ar2315_arch_init_irq(void) {}
+static inline void ar2315_init_devices(void) {}
static inline void ar2315_plat_time_init(void) {}
static inline void ar2315_plat_mem_setup(void) {}
static inline void ar2315_prom_init(void) {}
diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
index 138d7e1..576c790 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -133,6 +133,47 @@ void __init ar5312_arch_init_irq(void)
irq_set_chained_handler(AR5312_IRQ_MISC, ar5312_misc_irq_handler);
}

+static void __init ar5312_flash_init(void)
+{
+ u32 ctl;
+
+ /*
+ * Configure flash bank 0.
+ * Assume 8M window size. Flash will be aliased if it's smaller
+ */
+ ctl = ar231x_read_reg(AR5312_FLASHCTL0) & AR5312_FLASHCTL_MW;
+ ctl |= AR5312_FLASHCTL_E | AR5312_FLASHCTL_AC_8M | AR5312_FLASHCTL_RBLE;
+ ctl |= 0x01 << AR5312_FLASHCTL_IDCY_S;
+ ctl |= 0x07 << AR5312_FLASHCTL_WST1_S;
+ ctl |= 0x07 << AR5312_FLASHCTL_WST2_S;
+ ar231x_write_reg(AR5312_FLASHCTL0, ctl);
+
+ /* Disable other flash banks */
+ ar231x_mask_reg(AR5312_FLASHCTL1, AR5312_FLASHCTL_E |
+ AR5312_FLASHCTL_AC, 0);
+ ar231x_mask_reg(AR5312_FLASHCTL2, AR5312_FLASHCTL_E |
+ AR5312_FLASHCTL_AC, 0);
+}
+
+/*
+ * NB: This mapping size is larger than the actual flash size,
+ * but this shouldn't be a problem here, because the flash
+ * will simply be mapped multiple times.
+ */
+static const u8 * const __initconst
+ar5312_flash_limit = (u8 *)KSEG1ADDR(AR5312_FLASH + 0x800000);
+
+void __init ar5312_init_devices(void)
+{
+ if (!is_5312())
+ return;
+
+ ar5312_flash_init();
+
+ /* Locate board/radio config data */
+ ar231x_find_config(ar5312_flash_limit);
+}
+
static void ar5312_restart(char *command)
{
/* reset the system */
diff --git a/arch/mips/ar231x/ar5312.h b/arch/mips/ar231x/ar5312.h
index d04a33d..211992f 100644
--- a/arch/mips/ar231x/ar5312.h
+++ b/arch/mips/ar231x/ar5312.h
@@ -4,6 +4,7 @@
#ifdef CONFIG_SOC_AR5312

void ar5312_arch_init_irq(void);
+void ar5312_init_devices(void);
void ar5312_plat_time_init(void);
void ar5312_plat_mem_setup(void);
void ar5312_prom_init(void);
@@ -12,6 +13,7 @@ void ar5312_arch_init(void);
#else

static inline void ar5312_arch_init_irq(void) {}
+static inline void ar5312_init_devices(void) {}
static inline void ar5312_plat_time_init(void) {}
static inline void ar5312_plat_mem_setup(void) {}
static inline void ar5312_prom_init(void) {}
diff --git a/arch/mips/ar231x/board.c b/arch/mips/ar231x/board.c
index 24a00b4..f1e5d8f 100644
--- a/arch/mips/ar231x/board.c
+++ b/arch/mips/ar231x/board.c
@@ -16,12 +16,165 @@
#include <asm/bootinfo.h>
#include <asm/time.h>

+#include <ar231x_platform.h>
#include "devices.h"
#include "ar5312.h"
#include "ar2315.h"

void (*ar231x_irq_dispatch)(void);

+static inline bool check_radio_magic(const u8 *addr)
+{
+ addr += 0x7a; /* offset for flash magic */
+ return (addr[0] == 0x5a) && (addr[1] == 0xa5);
+}
+
+static inline bool check_notempty(const u8 *addr)
+{
+ return *(u32 *)addr != 0xffffffff;
+}
+
+static inline bool check_board_data(const u8 *flash_limit, const u8 *addr,
+ bool broken)
+{
+ /* config magic found */
+ if (*((u32 *)addr) == AR231X_BD_MAGIC)
+ return true;
+
+ if (!broken)
+ return false;
+
+ if (check_radio_magic(addr + 0xf8))
+ ar231x_board.radio = addr + 0xf8;
+ if ((addr < flash_limit + 0x10000) &&
+ check_radio_magic(addr + 0x10000))
+ ar231x_board.radio = addr + 0x10000;
+
+ if (ar231x_board.radio) {
+ /* broken board data detected, use radio data to find the
+ * offset, user will fix this */
+ return true;
+ }
+
+ return false;
+}
+
+static const u8 * __init find_board_config(const u8 *flash_limit, bool broken)
+{
+ const u8 *addr;
+ const u8 *begin = flash_limit - 0x1000;
+ const u8 *end = flash_limit - 0x30000;
+
+ for (addr = begin; addr >= end; addr -= 0x1000)
+ if (check_board_data(flash_limit, addr, broken))
+ return addr;
+
+ return NULL;
+}
+
+static const u8 * __init find_radio_config(const u8 *flash_limit,
+ const u8 *bcfg)
+{
+ const u8 *rcfg, *begin, *end;
+
+ /*
+ * Now find the start of Radio Configuration data, using heuristics:
+ * Search forward from Board Configuration data by 0x1000 bytes
+ * at a time until we find non-0xffffffff.
+ */
+ begin = bcfg + 0x1000;
+ end = flash_limit;
+ for (rcfg = begin; rcfg < end; rcfg += 0x1000)
+ if (check_notempty(rcfg) && check_radio_magic(rcfg))
+ return rcfg;
+
+ /* AR2316 relocates radio config to new location */
+ begin = bcfg + 0xf8;
+ end = flash_limit - 0x1000 + 0xf8;
+ for (rcfg = begin; rcfg < end; rcfg += 0x1000)
+ if (check_notempty(rcfg) && check_radio_magic(rcfg))
+ return rcfg;
+
+ pr_warn("WARNING: Could not find Radio Configuration data\n");
+
+ return NULL;
+}
+
+int __init ar231x_find_config(const u8 *flash_limit)
+{
+ struct ar231x_boarddata *config;
+ unsigned int rcfg_size;
+ int broken_boarddata = 0;
+ const u8 *bcfg, *rcfg;
+ u8 *board_data;
+ u8 *radio_data;
+ u8 *mac_addr;
+ u32 offset;
+
+ ar231x_board.config = NULL;
+ ar231x_board.radio = NULL;
+ /* Copy the board and radio data to RAM, because accessing the mapped
+ * memory of the flash directly after booting is not safe */
+
+ /* Try to find valid board and radio data */
+ bcfg = find_board_config(flash_limit, false);
+
+ /* If that fails, try to at least find valid radio data */
+ if (!bcfg) {
+ bcfg = find_board_config(flash_limit, true);
+ broken_boarddata = 1;
+ }
+
+ if (!bcfg) {
+ pr_warn("WARNING: No board configuration data found!\n");
+ return -ENODEV;
+ }
+
+ board_data = kzalloc(BOARD_CONFIG_BUFSZ, GFP_KERNEL);
+ ar231x_board.config = (struct ar231x_boarddata *)board_data;
+ memcpy(board_data, bcfg, 0x100);
+ if (broken_boarddata) {
+ pr_warn("WARNING: broken board data detected\n");
+ config = ar231x_board.config;
+ if (is_zero_ether_addr(config->enet0_mac)) {
+ pr_info("Fixing up empty mac addresses\n");
+ config->reset_config_gpio = 0xffff;
+ config->sys_led_gpio = 0xffff;
+ random_ether_addr(config->wlan0_mac);
+ config->wlan0_mac[0] &= ~0x06;
+ random_ether_addr(config->enet0_mac);
+ random_ether_addr(config->enet1_mac);
+ }
+ }
+
+ /* Radio config starts 0x100 bytes after board config, regardless
+ * of what the physical layout on the flash chip looks like */
+
+ if (ar231x_board.radio)
+ rcfg = (u8 *)ar231x_board.radio;
+ else
+ rcfg = find_radio_config(flash_limit, bcfg);
+
+ if (!rcfg)
+ return -ENODEV;
+
+ radio_data = board_data + 0x100 + ((rcfg - bcfg) & 0xfff);
+ ar231x_board.radio = radio_data;
+ offset = radio_data - board_data;
+ pr_info("Radio config found at offset 0x%x (0x%x)\n", rcfg - bcfg,
+ offset);
+ rcfg_size = BOARD_CONFIG_BUFSZ - offset;
+ memcpy(radio_data, rcfg, rcfg_size);
+
+ mac_addr = &radio_data[0x1d * 2];
+ if (is_broadcast_ether_addr(mac_addr)) {
+ pr_info("Radio MAC is blank; using board-data\n");
+ ether_addr_copy(mac_addr, ar231x_board.config->wlan0_mac);
+ }
+
+ return 0;
+}
+
static void ar231x_halt(void)
{
local_irq_disable();
diff --git a/arch/mips/ar231x/devices.c b/arch/mips/ar231x/devices.c
index 85caae4..0b7d42b 100644
--- a/arch/mips/ar231x/devices.c
+++ b/arch/mips/ar231x/devices.c
@@ -3,8 +3,12 @@
#include <linux/serial_8250.h>
#include <asm/bootinfo.h>

+#include <ar231x_platform.h>
#include "devices.h"
+#include "ar5312.h"
+#include "ar2315.h"

+struct ar231x_board_config ar231x_board;
int ar231x_devtype = DEV_TYPE_UNKNOWN;

static const char * const devtype_strings[] = {
@@ -35,6 +39,16 @@ void __init ar231x_serial_setup(u32 mapbase, int irq, unsigned int uartclk)
early_serial_setup(&s);
}

+static int __init ar231x_register_devices(void)
+{
+ ar5312_init_devices();
+ ar2315_init_devices();
+
+ return 0;
+}
+
+device_initcall(ar231x_register_devices);
+
static int __init ar231x_arch_init(void)
{
ar5312_arch_init();
diff --git a/arch/mips/ar231x/devices.h b/arch/mips/ar231x/devices.h
index 9f83150..ef50bd0 100644
--- a/arch/mips/ar231x/devices.h
+++ b/arch/mips/ar231x/devices.h
@@ -8,8 +8,10 @@ enum {
};

extern int ar231x_devtype;
+extern struct ar231x_board_config ar231x_board;
extern void (*ar231x_irq_dispatch)(void);

+int ar231x_find_config(const u8 *flash_limit);
void ar231x_serial_setup(u32 mapbase, int irq, unsigned int uartclk);

static inline bool is_2315(void)
diff --git a/arch/mips/include/asm/mach-ar231x/ar231x_platform.h b/arch/mips/include/asm/mach-ar231x/ar231x_platform.h
new file mode 100644
index 0000000..a726a81
--- /dev/null
+++ b/arch/mips/include/asm/mach-ar231x/ar231x_platform.h
@@ -0,0 +1,73 @@
+#ifndef __ASM_MACH_AR231X_PLATFORM_H
+#define __ASM_MACH_AR231X_PLATFORM_H
+
+#include <linux/etherdevice.h>
+
+/*
+ * This is board-specific data that is stored in a "fixed" location in flash.
+ * It is shared across operating systems, so it should not be changed lightly.
+ * The main reason we need it is in order to extract the ethernet MAC
+ * address(es).
+ */
+struct ar231x_boarddata {
+ u32 magic; /* board data is valid */
+#define AR231X_BD_MAGIC 0x35333131 /* "5311", for all 531x/231x platforms */
+ u16 cksum; /* checksum (starting with BD_REV 2) */
+ u16 rev; /* revision of this struct */
+#define BD_REV 4
+ char board_name[64]; /* Name of board */
+ u16 major; /* Board major number */
+ u16 minor; /* Board minor number */
+ u32 flags; /* Board configuration */
+#define BD_ENET0 0x00000001 /* ENET0 is stuffed */
+#define BD_ENET1 0x00000002 /* ENET1 is stuffed */
+#define BD_UART1 0x00000004 /* UART1 is stuffed */
+#define BD_UART0 0x00000008 /* UART0 is stuffed (dma) */
+#define BD_RSTFACTORY 0x00000010 /* Reset factory defaults stuffed */
+#define BD_SYSLED 0x00000020 /* System LED stuffed */
+#define BD_EXTUARTCLK 0x00000040 /* External UART clock */
+#define BD_CPUFREQ 0x00000080 /* cpu freq is valid in nvram */
+#define BD_SYSFREQ 0x00000100 /* sys freq is set in nvram */
+#define BD_WLAN0 0x00000200 /* Enable WLAN0 */
+#define BD_MEMCAP 0x00000400 /* CAP SDRAM @ mem_cap for testing */
+#define BD_DISWATCHDOG 0x00000800 /* disable system watchdog */
+#define BD_WLAN1 0x00001000 /* Enable WLAN1 (ar5212) */
+#define BD_ISCASPER 0x00002000 /* FLAG for AR2312 */
+#define BD_WLAN0_2G_EN 0x00004000 /* FLAG for radio0_2G */
+#define BD_WLAN0_5G_EN 0x00008000 /* FLAG for radio0_2G */
+#define BD_WLAN1_2G_EN 0x00020000 /* FLAG for radio0_2G */
+#define BD_WLAN1_5G_EN 0x00040000 /* FLAG for radio0_2G */
+ u16 reset_config_gpio; /* Reset factory GPIO pin */
+ u16 sys_led_gpio; /* System LED GPIO pin */
+
+ u32 cpu_freq; /* CPU core frequency in Hz */
+ u32 sys_freq; /* System frequency in Hz */
+ u32 cnt_freq; /* Calculated C0_COUNT frequency */
+
+ u8 wlan0_mac[ETH_ALEN];
+ u8 enet0_mac[ETH_ALEN];
+ u8 enet1_mac[ETH_ALEN];
+
+ u16 pci_id; /* Pseudo PCIID for common code */
+ u16 mem_cap; /* cap bank1 in MB */
+
+ /* version 3 */
+ u8 wlan1_mac[ETH_ALEN]; /* (ar5212) */
+};
+
+#define BOARD_CONFIG_BUFSZ 0x1000
+
+/*
+ * Platform device information for the Wireless MAC
+ */
+struct ar231x_board_config {
+ u16 devid;
+
+ /* board config data */
+ struct ar231x_boarddata *config;
+
+ /* radio calibration data */
+ const char *radio;
+};
+
+#endif /* __ASM_MACH_AR231X_PLATFORM_H */
--
1.8.5.5
Sergey Ryazanov
2014-09-28 18:33:07 UTC
Permalink
Detect SoC type based on device ID and board configuration data.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
---
arch/mips/ar231x/ar2315.c | 22 +++++++++++++++++++++-
arch/mips/ar231x/ar5312.c | 22 ++++++++++++++++++++++
arch/mips/ar231x/devices.c | 7 +++++++
arch/mips/ar231x/devices.h | 11 +++++++++++
4 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
index 0b973eb..7791637 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -21,6 +21,7 @@
#include <asm/reboot.h>
#include <asm/time.h>

+#include <ar231x_platform.h>
#include <ar2315_regs.h>
#include <ar231x.h>

@@ -249,7 +250,7 @@ void __init ar2315_plat_mem_setup(void)

void __init ar2315_prom_init(void)
{
- u32 memsize, memcfg;
+ u32 memsize, memcfg, devid;

if (!is_2315())
return;
@@ -260,6 +261,25 @@ void __init ar2315_prom_init(void)
memsize <<= 1 + AR231X_REG_MS(memcfg, AR2315_MEM_CFG_ROW_WIDTH);
memsize <<= 3;
add_memory_region(0, memsize, BOOT_MEM_RAM);
+
+ /* Detect the hardware based on the device ID */
+ devid = ar231x_read_reg(AR2315_SREV) & AR2315_REV_CHIP;
+ switch (devid) {
+ case 0x91: /* Need to check */
+ ar231x_devtype = DEV_TYPE_AR2318;
+ break;
+ case 0x90:
+ ar231x_devtype = DEV_TYPE_AR2317;
+ break;
+ case 0x87:
+ ar231x_devtype = DEV_TYPE_AR2316;
+ break;
+ case 0x86:
+ default:
+ ar231x_devtype = DEV_TYPE_AR2315;
+ break;
+ }
+ ar231x_board.devid = devid;
}

void __init ar2315_arch_init(void)
diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
index 576c790..f207d14 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -21,6 +21,7 @@
#include <asm/reboot.h>
#include <asm/time.h>

+#include <ar231x_platform.h>
#include <ar5312_regs.h>
#include <ar231x.h>

@@ -165,6 +166,8 @@ ar5312_flash_limit = (u8 *)KSEG1ADDR(AR5312_FLASH + 0x800000);

void __init ar5312_init_devices(void)
{
+ struct ar231x_boarddata *config;
+
if (!is_5312())
return;

@@ -172,6 +175,19 @@ void __init ar5312_init_devices(void)

/* Locate board/radio config data */
ar231x_find_config(ar5312_flash_limit);
+ config = ar231x_board.config;
+
+ /* AR2313 has CPU minor rev. 10 */
+ if ((current_cpu_data.processor_id & 0xff) == 0x0a)
+ ar231x_devtype = DEV_TYPE_AR2313;
+
+ /* AR2312 shares the same Silicon ID as AR5312 */
+ else if (config->flags & BD_ISCASPER)
+ ar231x_devtype = DEV_TYPE_AR2312;
+
+ /* Everything else is probably AR5312 or compatible */
+ else
+ ar231x_devtype = DEV_TYPE_AR5312;
}

static void ar5312_restart(char *command)
@@ -274,6 +290,7 @@ void __init ar5312_plat_mem_setup(void)
void __init ar5312_prom_init(void)
{
u32 memsize, memcfg, bank0_ac, bank1_ac;
+ u32 devid;

if (!is_5312())
return;
@@ -286,6 +303,11 @@ void __init ar5312_prom_init(void)
(bank1_ac ? (1 << (bank1_ac + 1)) : 0);
memsize <<= 20;
add_memory_region(0, memsize, BOOT_MEM_RAM);
+
+ devid = ar231x_read_reg(AR5312_REV);
+ devid >>= AR5312_REV_WMAC_MIN_S;
+ devid &= AR5312_REV_CHIP;
+ ar231x_board.devid = (u16)devid;
}

void __init ar5312_arch_init(void)
diff --git a/arch/mips/ar231x/devices.c b/arch/mips/ar231x/devices.c
index 0b7d42b..21f90f2 100644
--- a/arch/mips/ar231x/devices.c
+++ b/arch/mips/ar231x/devices.c
@@ -12,6 +12,13 @@ struct ar231x_board_config ar231x_board;
int ar231x_devtype = DEV_TYPE_UNKNOWN;

static const char * const devtype_strings[] = {
+ [DEV_TYPE_AR5312] = "Atheros AR5312",
+ [DEV_TYPE_AR2312] = "Atheros AR2312",
+ [DEV_TYPE_AR2313] = "Atheros AR2313",
+ [DEV_TYPE_AR2315] = "Atheros AR2315",
+ [DEV_TYPE_AR2316] = "Atheros AR2316",
+ [DEV_TYPE_AR2317] = "Atheros AR2317",
+ [DEV_TYPE_AR2318] = "Atheros AR2318",
[DEV_TYPE_UNKNOWN] = "Atheros (unknown)",
};

diff --git a/arch/mips/ar231x/devices.h b/arch/mips/ar231x/devices.h
index ef50bd0..5ffa091 100644
--- a/arch/mips/ar231x/devices.h
+++ b/arch/mips/ar231x/devices.h
@@ -4,6 +4,17 @@
#include <linux/cpu.h>

enum {
+ /* handled by ar5312.c */
+ DEV_TYPE_AR2312,
+ DEV_TYPE_AR2313,
+ DEV_TYPE_AR5312,
+
+ /* handled by ar2315.c */
+ DEV_TYPE_AR2315,
+ DEV_TYPE_AR2316,
+ DEV_TYPE_AR2317,
+ DEV_TYPE_AR2318,
+
DEV_TYPE_UNKNOWN
};
--
1.8.5.5
Sergey Ryazanov
2014-09-28 18:33:10 UTC
Permalink
Atheros AR2315 SoC have a SPI Flash unit with hybrid flash access: flash
read is performed via memory mapping, on the other hand flash write is
performed by explicitly issued SPI command.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
Cc: David Woodhouse <***@infradead.org>
Cc: Brian Norris <***@gmail.com>
Cc: linux-***@lists.infradead.org
---
This driver is not ready for merging since it should be rewrited using
spi-nor framework.

Changes since RFC:
- move device registration to separate patch

drivers/mtd/devices/Kconfig | 5 +
drivers/mtd/devices/Makefile | 1 +
drivers/mtd/devices/ar2315.c | 459 ++++++++++++++++++++++++++++++++++
drivers/mtd/devices/ar2315_spiflash.h | 106 ++++++++
4 files changed, 571 insertions(+)
create mode 100644 drivers/mtd/devices/ar2315.c
create mode 100644 drivers/mtd/devices/ar2315_spiflash.h

diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index c49d0b1..9533867 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -112,6 +112,11 @@ config MTD_SST25L
Set up your spi devices with the right board-specific platform data,
if you want to specify device partitioning.

+config MTD_AR2315
+ tristate "Atheros AR2315+ SPI Flash support"
+ depends on SOC_AR2315
+ default y
+
config MTD_BCM47XXSFLASH
tristate "R/O support for serial flash on BCMA bus"
depends on BCMA_SFLASH
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index c68868f..eaec8fb 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_MTD_M25P80) += m25p80.o
obj-$(CONFIG_MTD_NAND_OMAP_BCH) += elm.o
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o
+obj-$(CONFIG_MTD_AR2315) += ar2315.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o

diff --git a/drivers/mtd/devices/ar2315.c b/drivers/mtd/devices/ar2315.c
new file mode 100644
index 0000000..f2a5e28
--- /dev/null
+++ b/drivers/mtd/devices/ar2315.c
@@ -0,0 +1,459 @@
+
+/*
+ * MTD driver for the SPI Flash Memory support on Atheros AR2315
+ *
+ * Copyright (c) 2005-2006 Atheros Communications Inc.
+ * Copyright (C) 2006-2007 FON Technology, SL.
+ * Copyright (C) 2006-2007 Imre Kaloz <***@openwrt.org>
+ * Copyright (C) 2006-2009 Felix Fietkau <***@openwrt.org>
+ * Copyright (C) 2012 Alexandros C. Couloumbis <***@ozo.com>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+
+#include "ar2315_spiflash.h"
+
+#define DRIVER_NAME "ar2315-spiflash"
+
+#define busy_wait(_priv, _condition, _wait) do { \
+ while (_condition) { \
+ if (_wait > 1) \
+ msleep(_wait); \
+ else if ((_wait == 1) && need_resched()) \
+ schedule(); \
+ else \
+ udelay(1); \
+ } \
+} while (0)
+
+enum {
+ FLASH_NONE,
+ FLASH_1MB,
+ FLASH_2MB,
+ FLASH_4MB,
+ FLASH_8MB,
+ FLASH_16MB,
+};
+
+/* Flash configuration table */
+struct flashconfig {
+ u32 byte_cnt;
+ u32 sector_cnt;
+ u32 sector_size;
+};
+
+static const struct flashconfig flashconfig_tbl[] = {
+ [FLASH_NONE] = { 0, 0, 0},
+ [FLASH_1MB] = { STM_1MB_BYTE_COUNT, STM_1MB_SECTOR_COUNT,
+ STM_1MB_SECTOR_SIZE},
+ [FLASH_2MB] = { STM_2MB_BYTE_COUNT, STM_2MB_SECTOR_COUNT,
+ STM_2MB_SECTOR_SIZE},
+ [FLASH_4MB] = { STM_4MB_BYTE_COUNT, STM_4MB_SECTOR_COUNT,
+ STM_4MB_SECTOR_SIZE},
+ [FLASH_8MB] = { STM_8MB_BYTE_COUNT, STM_8MB_SECTOR_COUNT,
+ STM_8MB_SECTOR_SIZE},
+ [FLASH_16MB] = { STM_16MB_BYTE_COUNT, STM_16MB_SECTOR_COUNT,
+ STM_16MB_SECTOR_SIZE}
+};
+
+/* Mapping of generic opcodes to STM serial flash opcodes */
+enum {
+ SPI_WRITE_ENABLE,
+ SPI_WRITE_DISABLE,
+ SPI_RD_STATUS,
+ SPI_WR_STATUS,
+ SPI_RD_DATA,
+ SPI_FAST_RD_DATA,
+ SPI_PAGE_PROGRAM,
+ SPI_SECTOR_ERASE,
+ SPI_BULK_ERASE,
+ SPI_DEEP_PWRDOWN,
+ SPI_RD_SIG,
+};
+
+struct opcodes {
+ __u16 code;
+ __s8 tx_cnt;
+ __s8 rx_cnt;
+};
+
+static const struct opcodes stm_opcodes[] = {
+ [SPI_WRITE_ENABLE] = {STM_OP_WR_ENABLE, 1, 0},
+ [SPI_WRITE_DISABLE] = {STM_OP_WR_DISABLE, 1, 0},
+ [SPI_RD_STATUS] = {STM_OP_RD_STATUS, 1, 1},
+ [SPI_WR_STATUS] = {STM_OP_WR_STATUS, 1, 0},
+ [SPI_RD_DATA] = {STM_OP_RD_DATA, 4, 4},
+ [SPI_FAST_RD_DATA] = {STM_OP_FAST_RD_DATA, 5, 0},
+ [SPI_PAGE_PROGRAM] = {STM_OP_PAGE_PGRM, 8, 0},
+ [SPI_SECTOR_ERASE] = {STM_OP_SECTOR_ERASE, 4, 0},
+ [SPI_BULK_ERASE] = {STM_OP_BULK_ERASE, 1, 0},
+ [SPI_DEEP_PWRDOWN] = {STM_OP_DEEP_PWRDOWN, 1, 0},
+ [SPI_RD_SIG] = {STM_OP_RD_SIG, 4, 1},
+};
+
+/* Driver private data structure */
+struct spiflash_priv {
+ struct mtd_info mtd;
+ void __iomem *readaddr; /* memory mapped data for read */
+ void __iomem *mmraddr; /* memory mapped register space */
+ struct mutex lock; /* serialize registers access */
+};
+
+#define to_spiflash(_mtd) container_of(_mtd, struct spiflash_priv, mtd)
+
+enum {
+ FL_READY,
+ FL_READING,
+ FL_ERASING,
+ FL_WRITING
+};
+
+/*****************************************************************************/
+
+static u32
+spiflash_read_reg(struct spiflash_priv *priv, int reg)
+{
+ return ioread32(priv->mmraddr + reg);
+}
+
+static void
+spiflash_write_reg(struct spiflash_priv *priv, int reg, u32 data)
+{
+ iowrite32(data, priv->mmraddr + reg);
+}
+
+static u32
+spiflash_wait_busy(struct spiflash_priv *priv)
+{
+ u32 reg;
+
+ busy_wait(priv, (reg = spiflash_read_reg(priv, SPI_FLASH_CTL)) &
+ SPI_CTL_BUSY, 0);
+ return reg;
+}
+
+static u32
+spiflash_sendcmd(struct spiflash_priv *priv, int opcode, u32 addr)
+{
+ const struct opcodes *op;
+ u32 reg, mask;
+
+ op = &stm_opcodes[opcode];
+ reg = spiflash_wait_busy(priv);
+ spiflash_write_reg(priv, SPI_FLASH_OPCODE,
+ ((u32)op->code) | (addr << 8));
+
+ reg &= ~SPI_CTL_TX_RX_CNT_MASK;
+ reg |= SPI_CTL_START | op->tx_cnt | (op->rx_cnt << 4);
+
+ spiflash_write_reg(priv, SPI_FLASH_CTL, reg);
+ spiflash_wait_busy(priv);
+
+ if (!op->rx_cnt)
+ return 0;
+
+ reg = spiflash_read_reg(priv, SPI_FLASH_DATA);
+
+ switch (op->rx_cnt) {
+ case 1:
+ mask = 0x000000ff;
+ break;
+ case 2:
+ mask = 0x0000ffff;
+ break;
+ case 3:
+ mask = 0x00ffffff;
+ break;
+ default:
+ mask = 0xffffffff;
+ break;
+ }
+ reg &= mask;
+
+ return reg;
+}
+
+/*
+ * Probe SPI flash device
+ * Function returns 0 for failure.
+ * and flashconfig_tbl array index for success.
+ */
+static int
+spiflash_probe_chip(struct platform_device *pdev, struct spiflash_priv *priv)
+{
+ u32 sig = spiflash_sendcmd(priv, SPI_RD_SIG, 0);
+ int flash_size;
+
+ switch (sig) {
+ case STM_8MBIT_SIGNATURE:
+ flash_size = FLASH_1MB;
+ break;
+ case STM_16MBIT_SIGNATURE:
+ flash_size = FLASH_2MB;
+ break;
+ case STM_32MBIT_SIGNATURE:
+ flash_size = FLASH_4MB;
+ break;
+ case STM_64MBIT_SIGNATURE:
+ flash_size = FLASH_8MB;
+ break;
+ case STM_128MBIT_SIGNATURE:
+ flash_size = FLASH_16MB;
+ break;
+ default:
+ dev_warn(&pdev->dev, "read of flash device signature failed!\n");
+ return 0;
+ }
+
+ return flash_size;
+}
+
+static void
+spiflash_wait_complete(struct spiflash_priv *priv, unsigned int timeout)
+{
+ busy_wait(priv, spiflash_sendcmd(priv, SPI_RD_STATUS, 0) &
+ SPI_STATUS_WIP, timeout);
+}
+
+static int
+spiflash_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct spiflash_priv *priv = to_spiflash(mtd);
+ const struct opcodes *op;
+ u32 temp, reg;
+
+ if (instr->addr + instr->len > mtd->size)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ spiflash_sendcmd(priv, SPI_WRITE_ENABLE, 0);
+ reg = spiflash_wait_busy(priv);
+
+ op = &stm_opcodes[SPI_SECTOR_ERASE];
+ temp = ((u32)instr->addr << 8) | (u32)(op->code);
+ spiflash_write_reg(priv, SPI_FLASH_OPCODE, temp);
+
+ reg &= ~SPI_CTL_TX_RX_CNT_MASK;
+ reg |= op->tx_cnt | SPI_CTL_START;
+ spiflash_write_reg(priv, SPI_FLASH_CTL, reg);
+
+ spiflash_wait_complete(priv, 20);
+
+ mutex_unlock(&priv->lock);
+
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+
+ return 0;
+}
+
+static int
+spiflash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+ u_char *buf)
+{
+ struct spiflash_priv *priv = to_spiflash(mtd);
+
+ if (!len)
+ return 0;
+
+ if (from + len > mtd->size)
+ return -EINVAL;
+
+ *retlen = len;
+
+ mutex_lock(&priv->lock);
+
+ memcpy_fromio(buf, priv->readaddr + from, len);
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int
+spiflash_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+ const u8 *buf)
+{
+ struct spiflash_priv *priv = to_spiflash(mtd);
+ u32 opcode, bytes_left;
+
+ *retlen = 0;
+
+ if (!len)
+ return 0;
+
+ if (to + len > mtd->size)
+ return -EINVAL;
+
+ bytes_left = len;
+
+ do {
+ u32 read_len, reg, page_offset, spi_data = 0;
+
+ read_len = min(bytes_left, sizeof(u32));
+
+ /* 32-bit writes cannot span across a page boundary
+ * (256 bytes). This types of writes require two page
+ * program operations to handle it correctly. The STM part
+ * will write the overflow data to the beginning of the
+ * current page as opposed to the subsequent page.
+ */
+ page_offset = (to & (STM_PAGE_SIZE - 1)) + read_len;
+
+ if (page_offset > STM_PAGE_SIZE)
+ read_len -= (page_offset - STM_PAGE_SIZE);
+
+ mutex_lock(&priv->lock);
+
+ spiflash_sendcmd(priv, SPI_WRITE_ENABLE, 0);
+ spi_data = 0;
+ switch (read_len) {
+ case 4:
+ spi_data |= buf[3] << 24;
+ /* fall through */
+ case 3:
+ spi_data |= buf[2] << 16;
+ /* fall through */
+ case 2:
+ spi_data |= buf[1] << 8;
+ /* fall through */
+ case 1:
+ spi_data |= buf[0] & 0xff;
+ break;
+ default:
+ break;
+ }
+
+ spiflash_write_reg(priv, SPI_FLASH_DATA, spi_data);
+ opcode = stm_opcodes[SPI_PAGE_PROGRAM].code |
+ (to & 0x00ffffff) << 8;
+ spiflash_write_reg(priv, SPI_FLASH_OPCODE, opcode);
+
+ reg = spiflash_read_reg(priv, SPI_FLASH_CTL);
+ reg &= ~SPI_CTL_TX_RX_CNT_MASK;
+ reg |= (read_len + 4) | SPI_CTL_START;
+ spiflash_write_reg(priv, SPI_FLASH_CTL, reg);
+
+ spiflash_wait_complete(priv, 1);
+
+ mutex_unlock(&priv->lock);
+
+ bytes_left -= read_len;
+ to += read_len;
+ buf += read_len;
+
+ *retlen += read_len;
+ } while (bytes_left != 0);
+
+ return 0;
+}
+
+#if defined CONFIG_MTD_REDBOOT_PARTS || CONFIG_MTD_MYLOADER_PARTS
+static const char * const part_probe_types[] = {
+ "cmdlinepart", "RedBoot", "MyLoader", NULL
+};
+#endif
+
+static int
+spiflash_probe(struct platform_device *pdev)
+{
+ struct spiflash_priv *priv;
+ struct mtd_info *mtd;
+ struct resource *res;
+ int index;
+ int result = 0;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->lock);
+ mtd = &priv->mtd;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ priv->mmraddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->mmraddr)) {
+ dev_warn(&pdev->dev, "failed to map flash MMR\n");
+ return PTR_ERR(priv->mmraddr);
+ }
+
+ index = spiflash_probe_chip(pdev, priv);
+ if (!index) {
+ dev_warn(&pdev->dev, "found no flash device\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->readaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->readaddr)) {
+ dev_warn(&pdev->dev, "failed to map flash read mem\n");
+ return PTR_ERR(priv->readaddr);
+ }
+
+ platform_set_drvdata(pdev, priv);
+ mtd->name = "spiflash";
+ mtd->type = MTD_NORFLASH;
+ mtd->flags = (MTD_CAP_NORFLASH|MTD_WRITEABLE);
+ mtd->size = flashconfig_tbl[index].byte_cnt;
+ mtd->erasesize = flashconfig_tbl[index].sector_size;
+ mtd->writesize = 1;
+ mtd->numeraseregions = 0;
+ mtd->eraseregions = NULL;
+ mtd->_erase = spiflash_erase;
+ mtd->_read = spiflash_read;
+ mtd->_write = spiflash_write;
+ mtd->owner = THIS_MODULE;
+
+ dev_info(&pdev->dev, "%lld Kbytes flash detected\n", mtd->size >> 10);
+
+#if defined CONFIG_MTD_REDBOOT_PARTS || CONFIG_MTD_MYLOADER_PARTS
+ /* parse redboot partitions */
+
+ result = mtd_device_parse_register(mtd, part_probe_types,
+ NULL, NULL, 0);
+#endif
+
+ return result;
+}
+
+static int
+spiflash_remove(struct platform_device *pdev)
+{
+ struct spiflash_priv *priv = platform_get_drvdata(pdev);
+
+ mtd_device_unregister(&priv->mtd);
+
+ return 0;
+}
+
+static struct platform_driver spiflash_driver = {
+ .driver.name = DRIVER_NAME,
+ .probe = spiflash_probe,
+ .remove = spiflash_remove,
+};
+
+module_platform_driver(spiflash_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("OpenWrt.org");
+MODULE_AUTHOR("Atheros Communications Inc");
+MODULE_DESCRIPTION("MTD driver for SPI Flash on Atheros AR2315+ SOC");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+
diff --git a/drivers/mtd/devices/ar2315_spiflash.h b/drivers/mtd/devices/ar2315_spiflash.h
new file mode 100644
index 0000000..17b0903
--- /dev/null
+++ b/drivers/mtd/devices/ar2315_spiflash.h
@@ -0,0 +1,106 @@
+/*
+ * Atheros AR2315 SPI Flash Memory support header file.
+ *
+ * Copyright (c) 2005, Atheros Communications Inc.
+ * Copyright (C) 2006 FON Technology, SL.
+ * Copyright (C) 2006 Imre Kaloz <***@openwrt.org>
+ * Copyright (C) 2006-2009 Felix Fietkau <***@openwrt.org>
+ *
+ * This code 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 __AR2315_SPIFLASH_H
+#define __AR2315_SPIFLASH_H
+
+#define STM_PAGE_SIZE 256
+
+#define SFI_WRITE_BUFFER_SIZE 4
+#define SFI_FLASH_ADDR_MASK 0x00ffffff
+
+#define STM_8MBIT_SIGNATURE 0x13
+#define STM_M25P80_BYTE_COUNT 1048576
+#define STM_M25P80_SECTOR_COUNT 16
+#define STM_M25P80_SECTOR_SIZE 0x10000
+
+#define STM_16MBIT_SIGNATURE 0x14
+#define STM_M25P16_BYTE_COUNT 2097152
+#define STM_M25P16_SECTOR_COUNT 32
+#define STM_M25P16_SECTOR_SIZE 0x10000
+
+#define STM_32MBIT_SIGNATURE 0x15
+#define STM_M25P32_BYTE_COUNT 4194304
+#define STM_M25P32_SECTOR_COUNT 64
+#define STM_M25P32_SECTOR_SIZE 0x10000
+
+#define STM_64MBIT_SIGNATURE 0x16
+#define STM_M25P64_BYTE_COUNT 8388608
+#define STM_M25P64_SECTOR_COUNT 128
+#define STM_M25P64_SECTOR_SIZE 0x10000
+
+#define STM_128MBIT_SIGNATURE 0x17
+#define STM_M25P128_BYTE_COUNT 16777216
+#define STM_M25P128_SECTOR_COUNT 256
+#define STM_M25P128_SECTOR_SIZE 0x10000
+
+#define STM_1MB_BYTE_COUNT STM_M25P80_BYTE_COUNT
+#define STM_1MB_SECTOR_COUNT STM_M25P80_SECTOR_COUNT
+#define STM_1MB_SECTOR_SIZE STM_M25P80_SECTOR_SIZE
+#define STM_2MB_BYTE_COUNT STM_M25P16_BYTE_COUNT
+#define STM_2MB_SECTOR_COUNT STM_M25P16_SECTOR_COUNT
+#define STM_2MB_SECTOR_SIZE STM_M25P16_SECTOR_SIZE
+#define STM_4MB_BYTE_COUNT STM_M25P32_BYTE_COUNT
+#define STM_4MB_SECTOR_COUNT STM_M25P32_SECTOR_COUNT
+#define STM_4MB_SECTOR_SIZE STM_M25P32_SECTOR_SIZE
+#define STM_8MB_BYTE_COUNT STM_M25P64_BYTE_COUNT
+#define STM_8MB_SECTOR_COUNT STM_M25P64_SECTOR_COUNT
+#define STM_8MB_SECTOR_SIZE STM_M25P64_SECTOR_SIZE
+#define STM_16MB_BYTE_COUNT STM_M25P128_BYTE_COUNT
+#define STM_16MB_SECTOR_COUNT STM_M25P128_SECTOR_COUNT
+#define STM_16MB_SECTOR_SIZE STM_M25P128_SECTOR_SIZE
+
+/*
+ * ST Microelectronics Opcodes for Serial Flash
+ */
+
+#define STM_OP_WR_ENABLE 0x06 /* Write Enable */
+#define STM_OP_WR_DISABLE 0x04 /* Write Disable */
+#define STM_OP_RD_STATUS 0x05 /* Read Status */
+#define STM_OP_WR_STATUS 0x01 /* Write Status */
+#define STM_OP_RD_DATA 0x03 /* Read Data */
+#define STM_OP_FAST_RD_DATA 0x0b /* Fast Read Data */
+#define STM_OP_PAGE_PGRM 0x02 /* Page Program */
+#define STM_OP_SECTOR_ERASE 0xd8 /* Sector Erase */
+#define STM_OP_BULK_ERASE 0xc7 /* Bulk Erase */
+#define STM_OP_DEEP_PWRDOWN 0xb9 /* Deep Power-Down Mode */
+#define STM_OP_RD_SIG 0xab /* Read Electronic Signature */
+
+#define STM_STATUS_WIP 0x01 /* Write-In-Progress */
+#define STM_STATUS_WEL 0x02 /* Write Enable Latch */
+#define STM_STATUS_BP0 0x04 /* Block Protect 0 */
+#define STM_STATUS_BP1 0x08 /* Block Protect 1 */
+#define STM_STATUS_BP2 0x10 /* Block Protect 2 */
+#define STM_STATUS_SRWD 0x80 /* Status Register Write Disable */
+
+/*
+ * SPI Flash Interface Registers
+ */
+
+#define SPI_FLASH_CTL 0x00
+#define SPI_FLASH_OPCODE 0x04
+#define SPI_FLASH_DATA 0x08
+
+#define SPI_CTL_START 0x00000100
+#define SPI_CTL_BUSY 0x00010000
+#define SPI_CTL_TXCNT_MASK 0x0000000f
+#define SPI_CTL_RXCNT_MASK 0x000000f0
+#define SPI_CTL_TX_RX_CNT_MASK 0x000000ff
+#define SPI_CTL_SIZE_MASK 0x00060000
+
+#define SPI_CTL_CLK_SEL_MASK 0x03000000
+#define SPI_OPCODE_MASK 0x000000ff
+
+#define SPI_STATUS_WIP STM_STATUS_WIP
+
+#endif
--
1.8.5.5
Sergey Ryazanov
2014-09-28 18:33:11 UTC
Permalink
Signed-off-by: Sergey Ryazanov <***@gmail.com>
Cc: Wim Van Sebroeck <***@iguana.be>
Cc: linux-***@vger.kernel.org
---

Changes since RFC:
- use watchdog infrastructure
- remove deprecated IRQF_DISABLED flag
- move device registration to separate patch

drivers/watchdog/Kconfig | 8 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/ar2315-wtd.c | 167 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 176 insertions(+)
create mode 100644 drivers/watchdog/ar2315-wtd.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f57312f..dbace99 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1186,6 +1186,14 @@ config RALINK_WDT
help
Hardware driver for the Ralink SoC Watchdog Timer.

+config AR2315_WDT
+ tristate "Atheros AR2315+ WiSoCs Watchdog Timer"
+ select WATCHDOG_CORE
+ depends on SOC_AR2315
+ help
+ Hardware driver for the built-in watchdog timer on the Atheros
+ AR2315/AR2316 WiSoCs.
+
# PARISC Architecture

# POWERPC Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 468c320..ef7f83b 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -133,6 +133,7 @@ obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o
obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
+obj-$(CONFIG_AR2315_WDT) += ar2315-wtd.o
obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
diff --git a/drivers/watchdog/ar2315-wtd.c b/drivers/watchdog/ar2315-wtd.c
new file mode 100644
index 0000000..4fd34d2
--- /dev/null
+++ b/drivers/watchdog/ar2315-wtd.c
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2008 John Crispin <***@openwrt.org>
+ * Based on EP93xx and ifxmips wdt driver
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define DRIVER_NAME "ar2315-wdt"
+
+#define CLOCK_RATE 40000000
+
+#define WDT_REG_TIMER 0x00
+#define WDT_REG_CTRL 0x04
+
+#define WDT_CTRL_ACT_NONE 0x00000000 /* No action */
+#define WDT_CTRL_ACT_NMI 0x00000001 /* NMI on watchdog */
+#define WDT_CTRL_ACT_RESET 0x00000002 /* reset on watchdog */
+
+static int started;
+static void __iomem *wdt_base;
+
+static inline void ar2315_wdt_wr(unsigned reg, u32 val)
+{
+ iowrite32(val, wdt_base + reg);
+}
+
+static void ar2315_wdt_enable(struct watchdog_device *wdd)
+{
+ ar2315_wdt_wr(WDT_REG_TIMER, wdd->timeout * CLOCK_RATE);
+}
+
+static int ar2315_wdt_start(struct watchdog_device *wdd)
+{
+ ar2315_wdt_enable(wdd);
+ started = 1;
+ return 0;
+}
+
+static int ar2315_wdt_stop(struct watchdog_device *wdd)
+{
+ return 0;
+}
+
+static int ar2315_wdt_ping(struct watchdog_device *wdd)
+{
+ ar2315_wdt_enable(wdd);
+ return 0;
+}
+
+static int ar2315_wdt_set_timeout(struct watchdog_device *wdd, unsigned val)
+{
+ wdd->timeout = val;
+ return 0;
+}
+
+static irqreturn_t ar2315_wdt_interrupt(int irq, void *dev)
+{
+ struct platform_device *pdev = (struct platform_device *)dev;
+
+ if (started) {
+ dev_crit(&pdev->dev, "watchdog expired, rebooting system\n");
+ emergency_restart();
+ } else {
+ ar2315_wdt_wr(WDT_REG_CTRL, 0);
+ ar2315_wdt_wr(WDT_REG_TIMER, 0);
+ }
+ return IRQ_HANDLED;
+}
+
+static const struct watchdog_info ar2315_wdt_info = {
+ .identity = "ar2315 Watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+static const struct watchdog_ops ar2315_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = ar2315_wdt_start,
+ .stop = ar2315_wdt_stop,
+ .ping = ar2315_wdt_ping,
+ .set_timeout = ar2315_wdt_set_timeout,
+};
+
+static struct watchdog_device ar2315_wdt_dev = {
+ .info = &ar2315_wdt_info,
+ .ops = &ar2315_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = 90,
+ .timeout = 20,
+};
+
+static int ar2315_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int ret = 0;
+
+ if (wdt_base)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ wdt_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(wdt_base))
+ return PTR_ERR(wdt_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "no IRQ resource\n");
+ return -ENOENT;
+ }
+
+ ret = devm_request_irq(dev, res->start, ar2315_wdt_interrupt, 0,
+ DRIVER_NAME, pdev);
+ if (ret) {
+ dev_err(dev, "failed to register inetrrupt\n");
+ return ret;
+ }
+
+ ar2315_wdt_dev.parent = dev;
+ ret = watchdog_register_device(&ar2315_wdt_dev);
+ if (ret) {
+ dev_err(dev, "failed to register watchdog device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ar2315_wdt_remove(struct platform_device *pdev)
+{
+ watchdog_unregister_device(&ar2315_wdt_dev);
+ return 0;
+}
+
+static struct platform_driver ar2315_wdt_driver = {
+ .probe = ar2315_wdt_probe,
+ .remove = ar2315_wdt_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(ar2315_wdt_driver);
+
+MODULE_DESCRIPTION("Atheros AR2315 hardware watchdog driver");
+MODULE_AUTHOR("John Crispin <***@openwrt.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
--
1.8.5.5
Guenter Roeck
2014-09-28 21:35:15 UTC
Permalink
Post by Sergey Ryazanov
---
- use watchdog infrastructure
- remove deprecated IRQF_DISABLED flag
- move device registration to separate patch
drivers/watchdog/Kconfig | 8 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/ar2315-wtd.c | 167 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 176 insertions(+)
create mode 100644 drivers/watchdog/ar2315-wtd.c
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f57312f..dbace99 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1186,6 +1186,14 @@ config RALINK_WDT
help
Hardware driver for the Ralink SoC Watchdog Timer.
+config AR2315_WDT
+ tristate "Atheros AR2315+ WiSoCs Watchdog Timer"
+ select WATCHDOG_CORE
+ depends on SOC_AR2315
+ help
+ Hardware driver for the built-in watchdog timer on the Atheros
+ AR2315/AR2316 WiSoCs.
+
# PARISC Architecture
# POWERPC Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 468c320..ef7f83b 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -133,6 +133,7 @@ obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o
obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
+obj-$(CONFIG_AR2315_WDT) += ar2315-wtd.o
obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
diff --git a/drivers/watchdog/ar2315-wtd.c b/drivers/watchdog/ar2315-wtd.c
new file mode 100644
index 0000000..4fd34d2
--- /dev/null
+++ b/drivers/watchdog/ar2315-wtd.c
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Based on EP93xx and ifxmips wdt driver
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define DRIVER_NAME "ar2315-wdt"
+
+#define CLOCK_RATE 40000000
+
+#define WDT_REG_TIMER 0x00
+#define WDT_REG_CTRL 0x04
+
+#define WDT_CTRL_ACT_NONE 0x00000000 /* No action */
+#define WDT_CTRL_ACT_NMI 0x00000001 /* NMI on watchdog */
+#define WDT_CTRL_ACT_RESET 0x00000002 /* reset on watchdog */
+
What are those defines for ? They don't seem to be used.

If the watchdog can result in an immediate restart, as
this define suggests, why don't you use it but rely on
the interrupt handler instead ?

This means the watchdog won't really fire if it times out, but depend
on the interrupt handler to work. Which it won't if there is a real
problem and interrupts are disabled (or if the system hangs entirely).
Post by Sergey Ryazanov
+static int started;
+static void __iomem *wdt_base;
+
+static inline void ar2315_wdt_wr(unsigned reg, u32 val)
+{
+ iowrite32(val, wdt_base + reg);
+}
+
+static void ar2315_wdt_enable(struct watchdog_device *wdd)
+{
+ ar2315_wdt_wr(WDT_REG_TIMER, wdd->timeout * CLOCK_RATE);
+}
+
+static int ar2315_wdt_start(struct watchdog_device *wdd)
+{
+ ar2315_wdt_enable(wdd);
+ started = 1;
I don't really see why you would need this variable.
Post by Sergey Ryazanov
+ return 0;
+}
+
+static int ar2315_wdt_stop(struct watchdog_device *wdd)
+{
+ return 0;
+}
+
+static int ar2315_wdt_ping(struct watchdog_device *wdd)
+{
+ ar2315_wdt_enable(wdd);
+ return 0;
+}
+
+static int ar2315_wdt_set_timeout(struct watchdog_device *wdd, unsigned val)
+{
+ wdd->timeout = val;
+ return 0;
+}
+
+static irqreturn_t ar2315_wdt_interrupt(int irq, void *dev)
+{
+ struct platform_device *pdev = (struct platform_device *)dev;
+
+ if (started) {
+ dev_crit(&pdev->dev, "watchdog expired, rebooting system\n");
+ emergency_restart();
+ } else {
+ ar2315_wdt_wr(WDT_REG_CTRL, 0);
+ ar2315_wdt_wr(WDT_REG_TIMER, 0);
+ }
This is quite unusual.
Why not stop the watchdog in the stop function ? Quite apparently
it can be stopped, or at least this is what it looks like.

When do you expect this function to be called in the first place
with started == 1 ?

If the idea is to stop the watchdog if it was already enabled
when probing the driver, why don't you stop it there ?
Post by Sergey Ryazanov
+ return IRQ_HANDLED;
+}
+
+static const struct watchdog_info ar2315_wdt_info = {
+ .identity = "ar2315 Watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+static const struct watchdog_ops ar2315_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = ar2315_wdt_start,
+ .stop = ar2315_wdt_stop,
+ .ping = ar2315_wdt_ping,
+ .set_timeout = ar2315_wdt_set_timeout,
+};
+
+static struct watchdog_device ar2315_wdt_dev = {
+ .info = &ar2315_wdt_info,
+ .ops = &ar2315_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = 90,
+ .timeout = 20,
+};
+
+static int ar2315_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int ret = 0;
+
+ if (wdt_base)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ wdt_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(wdt_base))
+ return PTR_ERR(wdt_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "no IRQ resource\n");
+ return -ENOENT;
+ }
+
+ ret = devm_request_irq(dev, res->start, ar2315_wdt_interrupt, 0,
+ DRIVER_NAME, pdev);
+ if (ret) {
+ dev_err(dev, "failed to register inetrrupt\n");
+ return ret;
+ }
+
+ ar2315_wdt_dev.parent = dev;
+ ret = watchdog_register_device(&ar2315_wdt_dev);
+ if (ret) {
+ dev_err(dev, "failed to register watchdog device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ar2315_wdt_remove(struct platform_device *pdev)
+{
+ watchdog_unregister_device(&ar2315_wdt_dev);
Why don't you stop the watchdog on remove ?
Post by Sergey Ryazanov
+ return 0;
+}
+
+static struct platform_driver ar2315_wdt_driver = {
+ .probe = ar2315_wdt_probe,
+ .remove = ar2315_wdt_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(ar2315_wdt_driver);
+
+MODULE_DESCRIPTION("Atheros AR2315 hardware watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
Sergey Ryazanov
2014-09-29 20:14:58 UTC
Permalink
Post by Guenter Roeck
Post by Sergey Ryazanov
---
- use watchdog infrastructure
- remove deprecated IRQF_DISABLED flag
- move device registration to separate patch
drivers/watchdog/Kconfig | 8 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/ar2315-wtd.c | 167
++++++++++++++++++++++++++++++++++++++++++
3 files changed, 176 insertions(+)
create mode 100644 drivers/watchdog/ar2315-wtd.c
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f57312f..dbace99 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1186,6 +1186,14 @@ config RALINK_WDT
help
Hardware driver for the Ralink SoC Watchdog Timer.
+config AR2315_WDT
+ tristate "Atheros AR2315+ WiSoCs Watchdog Timer"
+ select WATCHDOG_CORE
+ depends on SOC_AR2315
+ help
+ Hardware driver for the built-in watchdog timer on the Atheros
+ AR2315/AR2316 WiSoCs.
+
# PARISC Architecture
# POWERPC Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 468c320..ef7f83b 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -133,6 +133,7 @@ obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o
obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
+obj-$(CONFIG_AR2315_WDT) += ar2315-wtd.o
obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
diff --git a/drivers/watchdog/ar2315-wtd.c b/drivers/watchdog/ar2315-wtd.c
new file mode 100644
index 0000000..4fd34d2
--- /dev/null
+++ b/drivers/watchdog/ar2315-wtd.c
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Based on EP93xx and ifxmips wdt driver
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define DRIVER_NAME "ar2315-wdt"
+
+#define CLOCK_RATE 40000000
+
+#define WDT_REG_TIMER 0x00
+#define WDT_REG_CTRL 0x04
+
+#define WDT_CTRL_ACT_NONE 0x00000000 /* No action */
+#define WDT_CTRL_ACT_NMI 0x00000001 /* NMI on watchdog */
+#define WDT_CTRL_ACT_RESET 0x00000002 /* reset on watchdog */
+
What are those defines for ? They don't seem to be used.
This defines for reference. There no documentation for this chips, so
I left this defines as some kind of documentation.
Post by Guenter Roeck
If the watchdog can result in an immediate restart, as
this define suggests, why don't you use it but rely on
the interrupt handler instead ?
AFAIK some of chips have a HW bug in restarting unit, so chip specific
restart routine (in arch code) use a lot of hacks to reset chip. So we
use interrupt to call reset function, which should reliably reset
chip.
Post by Guenter Roeck
This means the watchdog won't really fire if it times out, but depend
on the interrupt handler to work. Which it won't if there is a real
problem and interrupts are disabled (or if the system hangs entirely).
Sure. But without reset function call from the interrupt handler we
can not reliable reset chip (see above).
Post by Guenter Roeck
Post by Sergey Ryazanov
+static int started;
+static void __iomem *wdt_base;
+
+static inline void ar2315_wdt_wr(unsigned reg, u32 val)
+{
+ iowrite32(val, wdt_base + reg);
+}
+
+static void ar2315_wdt_enable(struct watchdog_device *wdd)
+{
+ ar2315_wdt_wr(WDT_REG_TIMER, wdd->timeout * CLOCK_RATE);
+}
+
+static int ar2315_wdt_start(struct watchdog_device *wdd)
+{
+ ar2315_wdt_enable(wdd);
+ started = 1;
I don't really see why you would need this variable.
To protect against spurious interrupts, since the watchdog timer could
be started by bootloader.
Post by Guenter Roeck
Post by Sergey Ryazanov
+ return 0;
+}
+
+static int ar2315_wdt_stop(struct watchdog_device *wdd)
+{
+ return 0;
+}
+
+static int ar2315_wdt_ping(struct watchdog_device *wdd)
+{
+ ar2315_wdt_enable(wdd);
+ return 0;
+}
+
+static int ar2315_wdt_set_timeout(struct watchdog_device *wdd, unsigned val)
+{
+ wdd->timeout = val;
+ return 0;
+}
+
+static irqreturn_t ar2315_wdt_interrupt(int irq, void *dev)
+{
+ struct platform_device *pdev = (struct platform_device *)dev;
+
+ if (started) {
+ dev_crit(&pdev->dev, "watchdog expired, rebooting
system\n");
+ emergency_restart();
+ } else {
+ ar2315_wdt_wr(WDT_REG_CTRL, 0);
+ ar2315_wdt_wr(WDT_REG_TIMER, 0);
+ }
This is quite unusual.
Why not stop the watchdog in the stop function ? Quite apparently
it can be stopped, or at least this is what it looks like.
The started variable is set to true inside the watchdog start routine,
but it never reset to false. This code only disable the watchdog when
it was started by bootloader.
Post by Guenter Roeck
When do you expect this function to be called in the first place
with started == 1 ?
If the idea is to stop the watchdog if it was already enabled
when probing the driver, why don't you stop it there ?
Sure, I will try to do that.
Post by Guenter Roeck
Post by Sergey Ryazanov
+ return IRQ_HANDLED;
+}
+
+static const struct watchdog_info ar2315_wdt_info = {
+ .identity = "ar2315 Watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+static const struct watchdog_ops ar2315_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = ar2315_wdt_start,
+ .stop = ar2315_wdt_stop,
+ .ping = ar2315_wdt_ping,
+ .set_timeout = ar2315_wdt_set_timeout,
+};
+
+static struct watchdog_device ar2315_wdt_dev = {
+ .info = &ar2315_wdt_info,
+ .ops = &ar2315_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = 90,
+ .timeout = 20,
+};
+
+static int ar2315_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int ret = 0;
+
+ if (wdt_base)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ wdt_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(wdt_base))
+ return PTR_ERR(wdt_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "no IRQ resource\n");
+ return -ENOENT;
+ }
+
+ ret = devm_request_irq(dev, res->start, ar2315_wdt_interrupt, 0,
+ DRIVER_NAME, pdev);
+ if (ret) {
+ dev_err(dev, "failed to register inetrrupt\n");
+ return ret;
+ }
+
+ ar2315_wdt_dev.parent = dev;
+ ret = watchdog_register_device(&ar2315_wdt_dev);
+ if (ret) {
+ dev_err(dev, "failed to register watchdog device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ar2315_wdt_remove(struct platform_device *pdev)
+{
+ watchdog_unregister_device(&ar2315_wdt_dev);
Why don't you stop the watchdog on remove ?
While the watchdog is running, the watchdog core prevents the module
unloading, so this routine could not be called while the watchdog is
running. Isn't it?
Post by Guenter Roeck
Post by Sergey Ryazanov
+ return 0;
+}
+
+static struct platform_driver ar2315_wdt_driver = {
+ .probe = ar2315_wdt_probe,
+ .remove = ar2315_wdt_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(ar2315_wdt_driver);
+
+MODULE_DESCRIPTION("Atheros AR2315 hardware watchdog driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
--
BR,
Sergey
Guenter Roeck
2014-09-29 20:46:19 UTC
Permalink
Post by Sergey Ryazanov
Post by Guenter Roeck
Post by Sergey Ryazanov
---
- use watchdog infrastructure
- remove deprecated IRQF_DISABLED flag
- move device registration to separate patch
drivers/watchdog/Kconfig | 8 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/ar2315-wtd.c | 167
++++++++++++++++++++++++++++++++++++++++++
3 files changed, 176 insertions(+)
create mode 100644 drivers/watchdog/ar2315-wtd.c
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f57312f..dbace99 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1186,6 +1186,14 @@ config RALINK_WDT
help
Hardware driver for the Ralink SoC Watchdog Timer.
+config AR2315_WDT
+ tristate "Atheros AR2315+ WiSoCs Watchdog Timer"
+ select WATCHDOG_CORE
+ depends on SOC_AR2315
+ help
+ Hardware driver for the built-in watchdog timer on the Atheros
+ AR2315/AR2316 WiSoCs.
+
# PARISC Architecture
# POWERPC Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 468c320..ef7f83b 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -133,6 +133,7 @@ obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o
obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
+obj-$(CONFIG_AR2315_WDT) += ar2315-wtd.o
obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
diff --git a/drivers/watchdog/ar2315-wtd.c b/drivers/watchdog/ar2315-wtd.c
new file mode 100644
index 0000000..4fd34d2
--- /dev/null
+++ b/drivers/watchdog/ar2315-wtd.c
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Based on EP93xx and ifxmips wdt driver
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define DRIVER_NAME "ar2315-wdt"
+
+#define CLOCK_RATE 40000000
+
+#define WDT_REG_TIMER 0x00
+#define WDT_REG_CTRL 0x04
+
+#define WDT_CTRL_ACT_NONE 0x00000000 /* No action */
+#define WDT_CTRL_ACT_NMI 0x00000001 /* NMI on watchdog */
+#define WDT_CTRL_ACT_RESET 0x00000002 /* reset on watchdog */
+
What are those defines for ? They don't seem to be used.
This defines for reference. There no documentation for this chips, so
I left this defines as some kind of documentation.
If they are not used, please drop those defines. They only create unnecessary
confusion if unused.
Post by Sergey Ryazanov
Post by Guenter Roeck
If the watchdog can result in an immediate restart, as
this define suggests, why don't you use it but rely on
the interrupt handler instead ?
AFAIK some of chips have a HW bug in restarting unit, so chip specific
restart routine (in arch code) use a lot of hacks to reset chip. So we
use interrupt to call reset function, which should reliably reset
chip.
Post by Guenter Roeck
This means the watchdog won't really fire if it times out, but depend
on the interrupt handler to work. Which it won't if there is a real
problem and interrupts are disabled (or if the system hangs entirely).
Sure. But without reset function call from the interrupt handler we
can not reliable reset chip (see above).
Post by Guenter Roeck
Post by Sergey Ryazanov
+static int started;
+static void __iomem *wdt_base;
+
+static inline void ar2315_wdt_wr(unsigned reg, u32 val)
+{
+ iowrite32(val, wdt_base + reg);
+}
+
+static void ar2315_wdt_enable(struct watchdog_device *wdd)
+{
+ ar2315_wdt_wr(WDT_REG_TIMER, wdd->timeout * CLOCK_RATE);
+}
+
+static int ar2315_wdt_start(struct watchdog_device *wdd)
+{
+ ar2315_wdt_enable(wdd);
+ started = 1;
I don't really see why you would need this variable.
To protect against spurious interrupts, since the watchdog timer could
be started by bootloader.
Then it would be appropriate to stop it in the probe function.
Post by Sergey Ryazanov
Post by Guenter Roeck
Post by Sergey Ryazanov
+ return 0;
+}
+
+static int ar2315_wdt_stop(struct watchdog_device *wdd)
+{
+ return 0;
+}
+
+static int ar2315_wdt_ping(struct watchdog_device *wdd)
+{
+ ar2315_wdt_enable(wdd);
+ return 0;
+}
+
+static int ar2315_wdt_set_timeout(struct watchdog_device *wdd, unsigned val)
+{
+ wdd->timeout = val;
+ return 0;
+}
+
+static irqreturn_t ar2315_wdt_interrupt(int irq, void *dev)
+{
+ struct platform_device *pdev = (struct platform_device *)dev;
+
+ if (started) {
+ dev_crit(&pdev->dev, "watchdog expired, rebooting
system\n");
+ emergency_restart();
+ } else {
+ ar2315_wdt_wr(WDT_REG_CTRL, 0);
+ ar2315_wdt_wr(WDT_REG_TIMER, 0);
+ }
This is quite unusual.
Why not stop the watchdog in the stop function ? Quite apparently
it can be stopped, or at least this is what it looks like.
The started variable is set to true inside the watchdog start routine,
but it never reset to false. This code only disable the watchdog when
it was started by bootloader.
Post by Guenter Roeck
When do you expect this function to be called in the first place
with started == 1 ?
If the idea is to stop the watchdog if it was already enabled
when probing the driver, why don't you stop it there ?
Sure, I will try to do that.
Post by Guenter Roeck
Post by Sergey Ryazanov
+ return IRQ_HANDLED;
+}
+
+static const struct watchdog_info ar2315_wdt_info = {
+ .identity = "ar2315 Watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+static const struct watchdog_ops ar2315_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = ar2315_wdt_start,
+ .stop = ar2315_wdt_stop,
+ .ping = ar2315_wdt_ping,
+ .set_timeout = ar2315_wdt_set_timeout,
+};
+
+static struct watchdog_device ar2315_wdt_dev = {
+ .info = &ar2315_wdt_info,
+ .ops = &ar2315_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = 90,
+ .timeout = 20,
+};
+
+static int ar2315_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int ret = 0;
+
+ if (wdt_base)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ wdt_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(wdt_base))
+ return PTR_ERR(wdt_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "no IRQ resource\n");
+ return -ENOENT;
+ }
+
+ ret = devm_request_irq(dev, res->start, ar2315_wdt_interrupt, 0,
+ DRIVER_NAME, pdev);
+ if (ret) {
+ dev_err(dev, "failed to register inetrrupt\n");
+ return ret;
+ }
+
+ ar2315_wdt_dev.parent = dev;
+ ret = watchdog_register_device(&ar2315_wdt_dev);
+ if (ret) {
+ dev_err(dev, "failed to register watchdog device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ar2315_wdt_remove(struct platform_device *pdev)
+{
+ watchdog_unregister_device(&ar2315_wdt_dev);
Why don't you stop the watchdog on remove ?
While the watchdog is running, the watchdog core prevents the module
unloading, so this routine could not be called while the watchdog is
running. Isn't it?
Only you never stop the watchdog nor disable the chip interrupt,
even on close (the stop function above does nothing).

Guenter
Sergey Ryazanov
2014-09-29 21:01:05 UTC
Permalink
Post by Guenter Roeck
Post by Sergey Ryazanov
Post by Guenter Roeck
Post by Sergey Ryazanov
---
- use watchdog infrastructure
- remove deprecated IRQF_DISABLED flag
- move device registration to separate patch
drivers/watchdog/Kconfig | 8 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/ar2315-wtd.c | 167
++++++++++++++++++++++++++++++++++++++++++
3 files changed, 176 insertions(+)
create mode 100644 drivers/watchdog/ar2315-wtd.c
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f57312f..dbace99 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1186,6 +1186,14 @@ config RALINK_WDT
help
Hardware driver for the Ralink SoC Watchdog Timer.
+config AR2315_WDT
+ tristate "Atheros AR2315+ WiSoCs Watchdog Timer"
+ select WATCHDOG_CORE
+ depends on SOC_AR2315
+ help
+ Hardware driver for the built-in watchdog timer on the Atheros
+ AR2315/AR2316 WiSoCs.
+
# PARISC Architecture
# POWERPC Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 468c320..ef7f83b 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -133,6 +133,7 @@ obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o
obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
+obj-$(CONFIG_AR2315_WDT) += ar2315-wtd.o
obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
diff --git a/drivers/watchdog/ar2315-wtd.c b/drivers/watchdog/ar2315-wtd.c
new file mode 100644
index 0000000..4fd34d2
--- /dev/null
+++ b/drivers/watchdog/ar2315-wtd.c
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Based on EP93xx and ifxmips wdt driver
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define DRIVER_NAME "ar2315-wdt"
+
+#define CLOCK_RATE 40000000
+
+#define WDT_REG_TIMER 0x00
+#define WDT_REG_CTRL 0x04
+
+#define WDT_CTRL_ACT_NONE 0x00000000 /* No action */
+#define WDT_CTRL_ACT_NMI 0x00000001 /* NMI on watchdog */
+#define WDT_CTRL_ACT_RESET 0x00000002 /* reset on watchdog */
+
What are those defines for ? They don't seem to be used.
This defines for reference. There no documentation for this chips, so
I left this defines as some kind of documentation.
If they are not used, please drop those defines. They only create unnecessary
confusion if unused.
Ok.
Post by Guenter Roeck
Post by Sergey Ryazanov
Post by Guenter Roeck
If the watchdog can result in an immediate restart, as
this define suggests, why don't you use it but rely on
the interrupt handler instead ?
AFAIK some of chips have a HW bug in restarting unit, so chip specific
restart routine (in arch code) use a lot of hacks to reset chip. So we
use interrupt to call reset function, which should reliably reset
chip.
Post by Guenter Roeck
This means the watchdog won't really fire if it times out, but depend
on the interrupt handler to work. Which it won't if there is a real
problem and interrupts are disabled (or if the system hangs entirely).
Sure. But without reset function call from the interrupt handler we
can not reliable reset chip (see above).
Post by Guenter Roeck
Post by Sergey Ryazanov
+static int started;
+static void __iomem *wdt_base;
+
+static inline void ar2315_wdt_wr(unsigned reg, u32 val)
+{
+ iowrite32(val, wdt_base + reg);
+}
+
+static void ar2315_wdt_enable(struct watchdog_device *wdd)
+{
+ ar2315_wdt_wr(WDT_REG_TIMER, wdd->timeout * CLOCK_RATE);
+}
+
+static int ar2315_wdt_start(struct watchdog_device *wdd)
+{
+ ar2315_wdt_enable(wdd);
+ started = 1;
I don't really see why you would need this variable.
To protect against spurious interrupts, since the watchdog timer could
be started by bootloader.
Then it would be appropriate to stop it in the probe function.
Yes sure.
Post by Guenter Roeck
Post by Sergey Ryazanov
Post by Guenter Roeck
Post by Sergey Ryazanov
+ return 0;
+}
+
+static int ar2315_wdt_stop(struct watchdog_device *wdd)
+{
+ return 0;
+}
+
+static int ar2315_wdt_ping(struct watchdog_device *wdd)
+{
+ ar2315_wdt_enable(wdd);
+ return 0;
+}
+
+static int ar2315_wdt_set_timeout(struct watchdog_device *wdd, unsigned val)
+{
+ wdd->timeout = val;
+ return 0;
+}
+
+static irqreturn_t ar2315_wdt_interrupt(int irq, void *dev)
+{
+ struct platform_device *pdev = (struct platform_device *)dev;
+
+ if (started) {
+ dev_crit(&pdev->dev, "watchdog expired, rebooting
system\n");
+ emergency_restart();
+ } else {
+ ar2315_wdt_wr(WDT_REG_CTRL, 0);
+ ar2315_wdt_wr(WDT_REG_TIMER, 0);
+ }
This is quite unusual.
Why not stop the watchdog in the stop function ? Quite apparently
it can be stopped, or at least this is what it looks like.
The started variable is set to true inside the watchdog start routine,
but it never reset to false. This code only disable the watchdog when
it was started by bootloader.
Post by Guenter Roeck
When do you expect this function to be called in the first place
with started == 1 ?
If the idea is to stop the watchdog if it was already enabled
when probing the driver, why don't you stop it there ?
Sure, I will try to do that.
Post by Guenter Roeck
Post by Sergey Ryazanov
+ return IRQ_HANDLED;
+}
+
+static const struct watchdog_info ar2315_wdt_info = {
+ .identity = "ar2315 Watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+static const struct watchdog_ops ar2315_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = ar2315_wdt_start,
+ .stop = ar2315_wdt_stop,
+ .ping = ar2315_wdt_ping,
+ .set_timeout = ar2315_wdt_set_timeout,
+};
+
+static struct watchdog_device ar2315_wdt_dev = {
+ .info = &ar2315_wdt_info,
+ .ops = &ar2315_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = 90,
+ .timeout = 20,
+};
+
+static int ar2315_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int ret = 0;
+
+ if (wdt_base)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ wdt_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(wdt_base))
+ return PTR_ERR(wdt_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "no IRQ resource\n");
+ return -ENOENT;
+ }
+
+ ret = devm_request_irq(dev, res->start, ar2315_wdt_interrupt, 0,
+ DRIVER_NAME, pdev);
+ if (ret) {
+ dev_err(dev, "failed to register inetrrupt\n");
+ return ret;
+ }
+
+ ar2315_wdt_dev.parent = dev;
+ ret = watchdog_register_device(&ar2315_wdt_dev);
+ if (ret) {
+ dev_err(dev, "failed to register watchdog device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ar2315_wdt_remove(struct platform_device *pdev)
+{
+ watchdog_unregister_device(&ar2315_wdt_dev);
Why don't you stop the watchdog on remove ?
While the watchdog is running, the watchdog core prevents the module
unloading, so this routine could not be called while the watchdog is
running. Isn't it?
Only you never stop the watchdog nor disable the chip interrupt,
even on close (the stop function above does nothing).
Oops. I really forget to disable interrupt. Thank you!
--
BR,
Sergey
Sergey Ryazanov
2014-09-28 18:33:12 UTC
Permalink
Register GPIO, watchdog and flash devices.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
---
arch/mips/ar231x/Kconfig | 2 +
arch/mips/ar231x/ar2315.c | 86 ++++++++++++++++++++++++-
arch/mips/ar231x/ar5312.c | 53 ++++++++++++++-
arch/mips/include/asm/mach-ar231x/ar2315_regs.h | 5 ++
arch/mips/include/asm/mach-ar231x/ar5312_regs.h | 2 +
5 files changed, 144 insertions(+), 4 deletions(-)

diff --git a/arch/mips/ar231x/Kconfig b/arch/mips/ar231x/Kconfig
index aa0fceb..88ca061 100644
--- a/arch/mips/ar231x/Kconfig
+++ b/arch/mips/ar231x/Kconfig
@@ -1,9 +1,11 @@
config SOC_AR5312
bool "Atheros AR5312/AR2312+ SoC support"
depends on AR231X
+ select GPIO_AR5312
default y

config SOC_AR2315
bool "Atheros AR2315+ SoC support"
depends on AR231X
+ select GPIO_AR2315
default y
diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
index 7791637..dab9992 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -16,7 +16,10 @@

#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/platform_device.h>
#include <linux/reboot.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
#include <asm/bootinfo.h>
#include <asm/reboot.h>
#include <asm/time.h>
@@ -139,6 +142,66 @@ void __init ar2315_arch_init_irq(void)
irq_set_chained_handler(AR2315_IRQ_MISC, ar2315_misc_irq_handler);
}

+static struct resource ar2315_spiflash_res[] = {
+ {
+ .name = "spiflash_read",
+ .flags = IORESOURCE_MEM,
+ .start = AR2315_SPI_READ,
+ .end = AR2315_SPI_READ + 0x1000000 - 1,
+ },
+ {
+ .name = "spiflash_mmr",
+ .flags = IORESOURCE_MEM,
+ .start = AR2315_SPI_MMR,
+ .end = AR2315_SPI_MMR + 12 - 1,
+ },
+};
+
+static struct platform_device ar2315_spiflash = {
+ .id = -1,
+ .name = "ar2315-spiflash",
+ .resource = ar2315_spiflash_res,
+ .num_resources = ARRAY_SIZE(ar2315_spiflash_res)
+};
+
+static struct resource ar2315_wdt_res[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ .start = AR2315_WD,
+ .end = AR2315_WD + 8 - 1,
+ },
+ {
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device ar2315_wdt = {
+ .id = -1,
+ .name = "ar2315-wdt",
+ .resource = ar2315_wdt_res,
+ .num_resources = ARRAY_SIZE(ar2315_wdt_res)
+};
+
+static struct resource ar2315_gpio_res[] = {
+ {
+ .name = "ar2315-gpio",
+ .flags = IORESOURCE_MEM,
+ .start = AR2315_GPIO,
+ .end = AR2315_GPIO + 0x10 - 1,
+ },
+ {
+ .name = "ar2315-gpio",
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device ar2315_gpio = {
+ .id = -1,
+ .name = "ar2315-gpio",
+ .resource = ar2315_gpio_res,
+ .num_resources = ARRAY_SIZE(ar2315_gpio_res)
+};
+
/*
* NB: We use mapping size that is larger than the actual flash size,
* but this shouldn't be a problem here, because the flash will simply
@@ -149,11 +212,23 @@ ar2315_flash_limit = (u8 *)KSEG1ADDR(AR2315_SPI_READ + 0x1000000);

void __init ar2315_init_devices(void)
{
+ struct resource *res;
+
if (!is_2315())
return;

/* Find board configuration */
ar231x_find_config(ar2315_flash_limit);
+
+ res = &ar2315_gpio_res[1];
+ res->start = ar2315_misc_irq_base + AR2315_MISC_IRQ_GPIO;
+ res->end = res->start;
+ platform_device_register(&ar2315_gpio);
+ res = &ar2315_wdt_res[1];
+ res->start = ar2315_misc_irq_base + AR2315_MISC_IRQ_WATCHDOG;
+ res->end = res->start;
+ platform_device_register(&ar2315_wdt);
+ platform_device_register(&ar2315_spiflash);
}

static void ar2315_restart(char *command)
@@ -165,8 +240,15 @@ static void ar2315_restart(char *command)
/* try reset the system via reset control */
ar231x_write_reg(AR2315_COLD_RESET, AR2317_RESET_SYSTEM);

- /* Attempt to jump to the mips reset location - the boot loader
- * itself might be able to recover the system */
+ /* Cold reset does not work on the AR2315/6, use the GPIO reset bits
+ * a workaround. Give it some time to attempt a gpio based hardware
+ * reset (atheros reference design workaround) */
+ gpio_request_one(AR2315_RESET_GPIO, GPIOF_OUT_INIT_LOW, "Reset");
+ mdelay(100);
+
+ /* Some boards (e.g. Senao EOC-2610) don't implement the reset logic
+ * workaround. Attempt to jump to the mips reset location -
+ * the boot loader itself might be able to recover the system */
mips_reset_vec();
}

diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
index f207d14..5efbe00 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -16,6 +16,8 @@

#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/physmap.h>
#include <linux/reboot.h>
#include <asm/bootinfo.h>
#include <asm/reboot.h>
@@ -134,15 +136,59 @@ void __init ar5312_arch_init_irq(void)
irq_set_chained_handler(AR5312_IRQ_MISC, ar5312_misc_irq_handler);
}

+static struct physmap_flash_data ar5312_flash_data = {
+ .width = 2,
+};
+
+static struct resource ar5312_flash_resource = {
+ .start = AR5312_FLASH,
+ .end = AR5312_FLASH + 0x800000 - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device ar5312_physmap_flash = {
+ .name = "physmap-flash",
+ .id = 0,
+ .dev.platform_data = &ar5312_flash_data,
+ .resource = &ar5312_flash_resource,
+ .num_resources = 1,
+};
+
+static struct resource ar5312_gpio_res[] = {
+ {
+ .name = "ar5312-gpio",
+ .flags = IORESOURCE_MEM,
+ .start = AR5312_GPIO,
+ .end = AR5312_GPIO + 0x0c - 1,
+ },
+};
+
+static struct platform_device ar5312_gpio = {
+ .name = "ar5312-gpio",
+ .id = -1,
+ .resource = ar5312_gpio_res,
+ .num_resources = ARRAY_SIZE(ar5312_gpio_res),
+};
+
static void __init ar5312_flash_init(void)
{
- u32 ctl;
+ u32 ctl = ar231x_read_reg(AR5312_FLASHCTL0) & AR5312_FLASHCTL_MW;
+
+ /* fixup flash width */
+ switch (ctl) {
+ case AR5312_FLASHCTL_MW16:
+ ar5312_flash_data.width = 2;
+ break;
+ case AR5312_FLASHCTL_MW8:
+ default:
+ ar5312_flash_data.width = 1;
+ break;
+ }

/*
* Configure flash bank 0.
* Assume 8M window size. Flash will be aliased if it's smaller
*/
- ctl = ar231x_read_reg(AR5312_FLASHCTL0) & AR5312_FLASHCTL_MW;
ctl |= AR5312_FLASHCTL_E | AR5312_FLASHCTL_AC_8M | AR5312_FLASHCTL_RBLE;
ctl |= 0x01 << AR5312_FLASHCTL_IDCY_S;
ctl |= 0x07 << AR5312_FLASHCTL_WST1_S;
@@ -188,6 +234,9 @@ void __init ar5312_init_devices(void)
/* Everything else is probably AR5312 or compatible */
else
ar231x_devtype = DEV_TYPE_AR5312;
+
+ platform_device_register(&ar5312_physmap_flash);
+ platform_device_register(&ar5312_gpio);
}

static void ar5312_restart(char *command)
diff --git a/arch/mips/include/asm/mach-ar231x/ar2315_regs.h b/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
index a65d578..ab64560 100644
--- a/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
+++ b/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
@@ -283,6 +283,11 @@
#define AR2315_AMBACLK_CLK_DIV_M 0x0000000c
#define AR2315_AMBACLK_CLK_DIV_S 2

+/* GPIO MMR base address */
+#define AR2315_GPIO (AR2315_DSLBASE + 0x0088)
+
+#define AR2315_RESET_GPIO 5
+
/*
* PCI Clock Control
*/
diff --git a/arch/mips/include/asm/mach-ar231x/ar5312_regs.h b/arch/mips/include/asm/mach-ar231x/ar5312_regs.h
index c7055e32..fccdb4d 100644
--- a/arch/mips/include/asm/mach-ar231x/ar5312_regs.h
+++ b/arch/mips/include/asm/mach-ar231x/ar5312_regs.h
@@ -210,4 +210,6 @@
#define AR5312_MEM_CFG1_AC1_M 0x00007000 /* bank 1: SDRAM addr check */
#define AR5312_MEM_CFG1_AC1_S 12

+#define AR5312_GPIO (AR5312_APBBASE + 0x2000)
+
#endif /* __ASM_MACH_AR231X_AR5312_REGS_H */
--
1.8.5.5
Sergey Ryazanov
2014-09-28 18:33:13 UTC
Permalink
Add PCI host controller driver and DMA address calculation hook.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
---

Changes since RFC:
- use dynamic IRQ numbers allocation

arch/mips/ar231x/Kconfig | 7 +
arch/mips/ar231x/ar2315.c | 9 +
arch/mips/include/asm/mach-ar231x/ar2315_regs.h | 23 ++
arch/mips/include/asm/mach-ar231x/dma-coherence.h | 18 +-
arch/mips/pci/Makefile | 1 +
arch/mips/pci/pci-ar2315.c | 353 ++++++++++++++++++++++
6 files changed, 408 insertions(+), 3 deletions(-)
create mode 100644 arch/mips/pci/pci-ar2315.c

diff --git a/arch/mips/ar231x/Kconfig b/arch/mips/ar231x/Kconfig
index 88ca061..f4bf354 100644
--- a/arch/mips/ar231x/Kconfig
+++ b/arch/mips/ar231x/Kconfig
@@ -9,3 +9,10 @@ config SOC_AR2315
depends on AR231X
select GPIO_AR2315
default y
+
+config PCI_AR2315
+ bool "Atheros AR2315 PCI controller support"
+ depends on SOC_AR2315
+ select HW_HAS_PCI
+ select PCI
+ default y
diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
index dab9992..0d50718 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -108,6 +108,10 @@ static void ar2315_irq_dispatch(void)

if (pending & CAUSEF_IP3)
do_IRQ(AR2315_IRQ_WLAN0);
+#ifdef CONFIG_PCI_AR2315
+ else if (pending & CAUSEF_IP5)
+ do_IRQ(AR2315_IRQ_LCBUS_PCI);
+#endif
else if (pending & CAUSEF_IP2)
do_IRQ(AR2315_IRQ_MISC);
else if (pending & CAUSEF_IP7)
@@ -371,5 +375,10 @@ void __init ar2315_arch_init(void)

ar231x_serial_setup(AR2315_UART0, ar2315_misc_irq_base +
AR2315_MISC_IRQ_UART0, ar2315_apb_frequency());
+
+#ifdef CONFIG_PCI_AR2315
+ if (ar231x_devtype == DEV_TYPE_AR2315)
+ platform_device_register_simple("ar2315-pci", -1, NULL, 0);
+#endif
}

diff --git a/arch/mips/include/asm/mach-ar231x/ar2315_regs.h b/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
index ab64560..b620ec4 100644
--- a/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
+++ b/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
@@ -38,6 +38,15 @@
#define AR2315_MISC_IRQ_COUNT 9

/*
+ * PCI interrupts, which share IP5
+ * Keep ordered according to AR2315_PCI_INT_XXX bits
+ */
+#define AR2315_PCI_IRQ_EXT 0
+#define AR2315_PCI_IRQ_ABORT 1
+#define AR2315_PCI_IRQ_COUNT 2
+#define AR2315_PCI_IRQ_SHIFT 25 /* in AR2315_PCI_INT_STATUS */
+
+/*
* Address map
*/
#define AR2315_SPI_READ 0x08000000 /* SPI flash */
@@ -554,4 +563,18 @@
#define AR2315_IRCFG_SEQ_END_WIN_THRESH 0x001f0000
#define AR2315_IRCFG_NUM_BACKOFF_WORDS 0x01e00000

+/*
+ * We need some arbitrary non-zero value to be programmed to the BAR1 register
+ * of PCI host controller to enable DMA. The same value should be used as the
+ * offset to calculate the physical address of DMA buffer for PCI devices.
+ */
+#define AR2315_PCI_HOST_SDRAM_BASEADDR 0x20000000
+
+/* ??? access BAR */
+#define AR2315_PCI_HOST_MBAR0 0x10000000
+/* RAM access BAR */
+#define AR2315_PCI_HOST_MBAR1 AR2315_PCI_HOST_SDRAM_BASEADDR
+/* ??? access BAR */
+#define AR2315_PCI_HOST_MBAR2 0x30000000
+
#endif /* __ASM_MACH_AR231X_AR2315_REGS_H */
diff --git a/arch/mips/include/asm/mach-ar231x/dma-coherence.h b/arch/mips/include/asm/mach-ar231x/dma-coherence.h
index ed32240..90065c1 100644
--- a/arch/mips/include/asm/mach-ar231x/dma-coherence.h
+++ b/arch/mips/include/asm/mach-ar231x/dma-coherence.h
@@ -11,23 +11,35 @@
#define __ASM_MACH_AR231X_DMA_COHERENCE_H

#include <linux/device.h>
+#include <ar2315_regs.h>
+
+static inline dma_addr_t ar231x_dev_offset(struct device *dev)
+{
+#ifdef CONFIG_PCI
+ extern struct bus_type pci_bus_type;
+
+ if (dev && dev->bus == &pci_bus_type)
+ return AR2315_PCI_HOST_SDRAM_BASEADDR;
+#endif
+ return 0;
+}

static inline dma_addr_t
plat_map_dma_mem(struct device *dev, void *addr, size_t size)
{
- return virt_to_phys(addr);
+ return virt_to_phys(addr) + ar231x_dev_offset(dev);
}

static inline dma_addr_t
plat_map_dma_mem_page(struct device *dev, struct page *page)
{
- return page_to_phys(page);
+ return page_to_phys(page) + ar231x_dev_offset(dev);
}

static inline unsigned long
plat_dma_addr_to_phys(struct device *dev, dma_addr_t dma_addr)
{
- return dma_addr;
+ return dma_addr - ar231x_dev_offset(dev);
}

static inline void
diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile
index 6523d55..ab6c857 100644
--- a/arch/mips/pci/Makefile
+++ b/arch/mips/pci/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_BCM47XX) += pci-bcm47xx.o
obj-$(CONFIG_BCM63XX) += pci-bcm63xx.o fixup-bcm63xx.o \
ops-bcm63xx.o
obj-$(CONFIG_MIPS_ALCHEMY) += pci-alchemy.o
+obj-$(CONFIG_PCI_AR2315) += pci-ar2315.o
obj-$(CONFIG_SOC_AR71XX) += pci-ar71xx.o
obj-$(CONFIG_PCI_AR724X) += pci-ar724x.o
obj-$(CONFIG_MIPS_PCI_VIRTIO) += pci-virtio-guest.o
diff --git a/arch/mips/pci/pci-ar2315.c b/arch/mips/pci/pci-ar2315.c
new file mode 100644
index 0000000..cfbb7a1
--- /dev/null
+++ b/arch/mips/pci/pci-ar2315.c
@@ -0,0 +1,353 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Both AR2315 and AR2316 chips have PCI interface unit, which supports DMA
+ * and interrupt. PCI interface supports MMIO access method, but does not
+ * seem to support I/O ports.
+ *
+ * Read/write operation in the region 0x80000000-0xBFFFFFFF causes
+ * a memory read/write command on the PCI bus. 30 LSBs of address on
+ * the bus are taken from memory read/write request and 2 MSBs are
+ * determined by PCI unit configuration.
+ *
+ * To work with the configuration space instead of memory is necessary set
+ * the CFG_SEL bit in the PCI_MISC_CONFIG register.
+ *
+ * Devices on the bus can perform DMA requests via chip BAR1. PCI host
+ * controller BARs are programmend as if an external device is programmed.
+ * Which means that during configuration, IDSEL pin of the chip should be
+ * asserted.
+ *
+ * We know (and support) only one board that uses the PCI interface -
+ * Fonera 2.0g (FON2202). It has a USB EHCI controller connected to the
+ * AR2315 PCI bus. IDSEL pin of USB controller is connected to AD[13] line
+ * and IDSEL pin of AR125 is connected to AD[16] line.
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <asm/paccess.h>
+#include <ar231x_platform.h>
+#include <ar231x.h>
+#include <ar2315_regs.h>
+
+/* Arbitrary size of memory region to access the configuration space */
+#define AR2315_PCI_CFG_SIZE 0x00100000
+
+#define AR2315_PCI_HOST_SLOT 3
+#define AR2315_PCI_HOST_DEVID ((0xff18 << 16) | PCI_VENDOR_ID_ATHEROS)
+
+static void __iomem *ar2315_pci_cfg_mem;
+static unsigned ar2315_pci_irq_base;
+
+static int ar2315_pci_cfg_access(int devfn, int where, int size, u32 *ptr,
+ bool write)
+{
+ int func = PCI_FUNC(devfn);
+ int dev = PCI_SLOT(devfn);
+ u32 addr = (1 << (13 + dev)) | (func << 8) | (where & ~3);
+ u32 mask = 0xffffffff >> 8 * (4 - size);
+ u32 sh = (where & 3) * 8;
+ u32 value, isr;
+
+ /* Prevent access past the remapped area */
+ if (addr >= AR2315_PCI_CFG_SIZE || dev > 18)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ /* Clear pending errors */
+ ar231x_write_reg(AR2315_PCI_ISR, AR2315_PCI_INT_ABORT);
+ /* Select Configuration access */
+ ar231x_mask_reg(AR2315_PCI_MISC_CONFIG, 0, AR2315_PCIMISC_CFG_SEL);
+
+ mb(); /* PCI must see space change before we begin */
+
+ value = __raw_readl(ar2315_pci_cfg_mem + addr);
+
+ isr = ar231x_read_reg(AR2315_PCI_ISR);
+ if (isr & AR2315_PCI_INT_ABORT)
+ goto exit_err;
+
+ if (write) {
+ value = (value & ~(mask << sh)) | *ptr << sh;
+ __raw_writel(value, ar2315_pci_cfg_mem + addr);
+ isr = ar231x_read_reg(AR2315_PCI_ISR);
+ if (isr & AR2315_PCI_INT_ABORT)
+ goto exit_err;
+ } else {
+ *ptr = (value >> sh) & mask;
+ }
+
+ goto exit;
+
+exit_err:
+ ar231x_write_reg(AR2315_PCI_ISR, AR2315_PCI_INT_ABORT);
+ if (!write)
+ *ptr = 0xffffffff;
+
+exit:
+ /* Select Memory access */
+ ar231x_mask_reg(AR2315_PCI_MISC_CONFIG, AR2315_PCIMISC_CFG_SEL, 0);
+
+ return isr & AR2315_PCI_INT_ABORT ? PCIBIOS_DEVICE_NOT_FOUND :
+ PCIBIOS_SUCCESSFUL;
+}
+
+static inline int ar2315_pci_local_cfg_rd(unsigned devfn, int where, u32 *val)
+{
+ return ar2315_pci_cfg_access(devfn, where, sizeof(u32), val, false);
+}
+
+static inline int ar2315_pci_local_cfg_wr(unsigned devfn, int where, u32 val)
+{
+ return ar2315_pci_cfg_access(devfn, where, sizeof(u32), &val, true);
+}
+
+static int ar2315_pci_cfg_read(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *value)
+{
+ if (PCI_SLOT(devfn) == AR2315_PCI_HOST_SLOT)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ return ar2315_pci_cfg_access(devfn, where, size, value, 0);
+}
+
+static int ar2315_pci_cfg_write(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 value)
+{
+ if (PCI_SLOT(devfn) == AR2315_PCI_HOST_SLOT)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ return ar2315_pci_cfg_access(devfn, where, size, &value, 1);
+}
+
+static struct pci_ops ar2315_pci_ops = {
+ .read = ar2315_pci_cfg_read,
+ .write = ar2315_pci_cfg_write,
+};
+
+static struct resource ar2315_mem_resource = {
+ .name = "ar2315-pci-mem",
+ .start = AR2315_PCIEXT,
+ .end = AR2315_PCIEXT + AR2315_PCIEXT_SZ - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+/* PCI controller does not support I/O ports */
+static struct resource ar2315_io_resource = {
+ .name = "ar2315-pci-io",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IO,
+};
+
+static struct pci_controller ar2315_pci_controller = {
+ .pci_ops = &ar2315_pci_ops,
+ .mem_resource = &ar2315_mem_resource,
+ .io_resource = &ar2315_io_resource,
+ .mem_offset = 0x00000000UL,
+ .io_offset = 0x00000000UL,
+};
+
+static int ar2315_pci_host_setup(void)
+{
+ unsigned devfn = PCI_DEVFN(AR2315_PCI_HOST_SLOT, 0);
+ int res;
+ u32 id;
+
+ res = ar2315_pci_local_cfg_rd(devfn, PCI_VENDOR_ID, &id);
+ if (res != PCIBIOS_SUCCESSFUL || id != AR2315_PCI_HOST_DEVID)
+ return -ENODEV;
+
+ /* Program MBARs */
+ ar2315_pci_local_cfg_wr(devfn, PCI_BASE_ADDRESS_0,
+ AR2315_PCI_HOST_MBAR0);
+ ar2315_pci_local_cfg_wr(devfn, PCI_BASE_ADDRESS_1,
+ AR2315_PCI_HOST_MBAR1);
+ ar2315_pci_local_cfg_wr(devfn, PCI_BASE_ADDRESS_2,
+ AR2315_PCI_HOST_MBAR2);
+
+ /* Run */
+ ar2315_pci_local_cfg_wr(devfn, PCI_COMMAND, PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_SPECIAL |
+ PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY |
+ PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK);
+
+ return 0;
+}
+
+static void ar2315_pci_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ u32 pending = ar231x_read_reg(AR2315_PCI_ISR) &
+ ar231x_read_reg(AR2315_PCI_IMR);
+
+ if (pending & AR2315_PCI_INT_EXT)
+ generic_handle_irq(ar2315_pci_irq_base + AR2315_PCI_IRQ_EXT);
+ else if (pending & AR2315_PCI_INT_ABORT)
+ generic_handle_irq(ar2315_pci_irq_base + AR2315_PCI_IRQ_ABORT);
+ else
+ spurious_interrupt();
+}
+
+static void ar2315_pci_irq_mask(struct irq_data *d)
+{
+ u32 m = 1 << (d->irq - ar2315_pci_irq_base + AR2315_PCI_IRQ_SHIFT);
+
+ ar231x_mask_reg(AR2315_PCI_IMR, m, 0);
+}
+
+static void ar2315_pci_irq_mask_ack(struct irq_data *d)
+{
+ u32 m = 1 << (d->irq - ar2315_pci_irq_base + AR2315_PCI_IRQ_SHIFT);
+
+ ar231x_mask_reg(AR2315_PCI_IMR, m, 0);
+ ar231x_write_reg(AR2315_PCI_ISR, m);
+}
+
+static void ar2315_pci_irq_unmask(struct irq_data *d)
+{
+ u32 m = 1 << (d->irq - ar2315_pci_irq_base + AR2315_PCI_IRQ_SHIFT);
+
+ ar231x_mask_reg(AR2315_PCI_IMR, 0, m);
+}
+
+static struct irq_chip ar2315_pci_irq_chip = {
+ .name = "AR2315-PCI",
+ .irq_mask = ar2315_pci_irq_mask,
+ .irq_mask_ack = ar2315_pci_irq_mask_ack,
+ .irq_unmask = ar2315_pci_irq_unmask,
+};
+
+static void ar2315_pci_irq_init(void)
+{
+ unsigned i;
+
+ ar231x_mask_reg(AR2315_PCI_IER, AR2315_PCI_IER_ENABLE, 0);
+ ar231x_mask_reg(AR2315_PCI_IMR, (AR2315_PCI_INT_ABORT |
+ AR2315_PCI_INT_EXT), 0);
+
+ for (i = 0; i < AR2315_PCI_IRQ_COUNT; ++i) {
+ unsigned irq = ar2315_pci_irq_base + i;
+
+ irq_set_chip_and_handler(irq, &ar2315_pci_irq_chip,
+ handle_level_irq);
+ }
+
+ irq_set_chained_handler(AR2315_IRQ_LCBUS_PCI, ar2315_pci_irq_handler);
+
+ /* Clear any pending Abort or external Interrupts
+ * and enable interrupt processing */
+ ar231x_write_reg(AR2315_PCI_ISR, (AR2315_PCI_INT_ABORT |
+ AR2315_PCI_INT_EXT));
+ ar231x_mask_reg(AR2315_PCI_IER, 0, AR2315_PCI_IER_ENABLE);
+}
+
+static int ar2315_pci_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ u32 reg;
+ int res;
+
+ /* Remap PCI config space */
+ ar2315_pci_cfg_mem = devm_ioremap_nocache(dev, AR2315_PCIEXT,
+ AR2315_PCI_CFG_SIZE);
+ if (!ar2315_pci_cfg_mem) {
+ dev_err(dev, "failed to remap PCI config space\n");
+ return -ENOMEM;
+ }
+
+ /* Reset PCI DMA logic */
+ reg = ar231x_mask_reg(AR2315_RESET, 0, AR2315_RESET_PCIDMA);
+ msleep(20);
+ reg &= ~AR2315_RESET_PCIDMA;
+ ar231x_write_reg(AR2315_RESET, reg);
+ msleep(20);
+
+ ar231x_mask_reg(AR2315_ENDIAN_CTL, 0,
+ AR2315_CONFIG_PCIAHB | AR2315_CONFIG_PCIAHB_BRIDGE);
+
+ ar231x_write_reg(AR2315_PCICLK, AR2315_PCICLK_PLLC_CLKM |
+ (AR2315_PCICLK_IN_FREQ_DIV_6 << AR2315_PCICLK_DIV_S));
+ ar231x_mask_reg(AR2315_AHB_ARB_CTL, 0, AR2315_ARB_PCI);
+ ar231x_mask_reg(AR2315_IF_CTL, AR2315_IF_PCI_CLK_MASK | AR2315_IF_MASK,
+ AR2315_IF_PCI | AR2315_IF_PCI_HOST |
+ AR2315_IF_PCI_INTR | (AR2315_IF_PCI_CLK_OUTPUT_CLK <<
+ AR2315_IF_PCI_CLK_SHIFT));
+
+ /* Reset the PCI bus by setting bits 5-4 in PCI_MCFG */
+ ar231x_mask_reg(AR2315_PCI_MISC_CONFIG, AR2315_PCIMISC_RST_MODE,
+ AR2315_PCIRST_LOW);
+ msleep(100);
+
+ /* Bring the PCI out of reset */
+ ar231x_mask_reg(AR2315_PCI_MISC_CONFIG, AR2315_PCIMISC_RST_MODE,
+ AR2315_PCIRST_HIGH | AR2315_PCICACHE_DIS | 0x8);
+
+ ar231x_write_reg(AR2315_PCI_UNCACHE_CFG,
+ 0x1E | /* 1GB uncached */
+ (1 << 5) | /* Enable uncached */
+ (0x2 << 30) /* Base: 0x80000000 */);
+ ar231x_read_reg(AR2315_PCI_UNCACHE_CFG);
+
+ msleep(500);
+
+ res = ar2315_pci_host_setup();
+ if (res)
+ return res;
+
+ res = irq_alloc_descs(-1, 0, AR2315_PCI_IRQ_COUNT, 0);
+ if (res < 0) {
+ dev_err(dev, "failed to allocate PCI IRQ numbers\n");
+ return res;
+ }
+ ar2315_pci_irq_base = res;
+
+ ar2315_pci_irq_init();
+
+ register_pci_controller(&ar2315_pci_controller);
+
+ return 0;
+}
+
+static struct platform_driver ar2315_pci_driver = {
+ .probe = ar2315_pci_probe,
+ .driver = {
+ .name = "ar2315-pci",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ar2315_pci_init(void)
+{
+ return platform_driver_register(&ar2315_pci_driver);
+}
+arch_initcall(ar2315_pci_init);
+
+int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ return ar2315_pci_irq_base + AR2315_PCI_IRQ_EXT;
+}
+
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+ return 0;
+}
--
1.8.5.5
Sergey Ryazanov
2014-09-28 18:33:15 UTC
Permalink
Atheros AR5312 and AR2315 both have a builtin wireless device, this
patch add helper code and register platform device for all supported
WiSoCs.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
---
arch/mips/ar231x/ar2315.c | 1 +
arch/mips/ar231x/ar5312.c | 22 +++++++++++++++++++
arch/mips/ar231x/devices.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++
arch/mips/ar231x/devices.h | 1 +
4 files changed, 78 insertions(+)

diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
index 0d50718..837fcad 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -233,6 +233,7 @@ void __init ar2315_init_devices(void)
res->end = res->start;
platform_device_register(&ar2315_wdt);
platform_device_register(&ar2315_spiflash);
+ ar231x_add_wmac(0, AR2315_WLAN0, AR2315_IRQ_WLAN0);
}

static void ar2315_restart(char *command)
diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
index 5efbe00..33b22e2 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -237,6 +237,28 @@ void __init ar5312_init_devices(void)

platform_device_register(&ar5312_physmap_flash);
platform_device_register(&ar5312_gpio);
+
+ switch (ar231x_devtype) {
+ case DEV_TYPE_AR5312:
+ if (!ar231x_board.radio)
+ return;
+
+ if (!(config->flags & BD_WLAN0))
+ break;
+
+ ar231x_add_wmac(0, AR5312_WLAN0, AR5312_IRQ_WLAN0);
+ break;
+ case DEV_TYPE_AR2312:
+ case DEV_TYPE_AR2313:
+ if (!ar231x_board.radio)
+ return;
+ break;
+ default:
+ break;
+ }
+
+ if (config->flags & BD_WLAN1)
+ ar231x_add_wmac(1, AR5312_WLAN1, AR5312_IRQ_WLAN1);
}

static void ar5312_restart(char *command)
diff --git a/arch/mips/ar231x/devices.c b/arch/mips/ar231x/devices.c
index 21f90f2..65d1026 100644
--- a/arch/mips/ar231x/devices.c
+++ b/arch/mips/ar231x/devices.c
@@ -1,6 +1,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/serial_8250.h>
+#include <linux/platform_device.h>
#include <asm/bootinfo.h>

#include <ar231x_platform.h>
@@ -11,6 +12,45 @@
struct ar231x_board_config ar231x_board;
int ar231x_devtype = DEV_TYPE_UNKNOWN;

+static struct resource ar231x_wmac0_res[] = {
+ {
+ .name = "wmac0_membase",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "wmac0_irq",
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct resource ar231x_wmac1_res[] = {
+ {
+ .name = "wmac1_membase",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "wmac1_irq",
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device ar231x_wmac[] = {
+ {
+ .id = 0,
+ .name = "ar231x-wmac",
+ .resource = ar231x_wmac0_res,
+ .num_resources = ARRAY_SIZE(ar231x_wmac0_res),
+ .dev.platform_data = &ar231x_board,
+ },
+ {
+ .id = 1,
+ .name = "ar231x-wmac",
+ .resource = ar231x_wmac1_res,
+ .num_resources = ARRAY_SIZE(ar231x_wmac1_res),
+ .dev.platform_data = &ar231x_board,
+ },
+};
+
static const char * const devtype_strings[] = {
[DEV_TYPE_AR5312] = "Atheros AR5312",
[DEV_TYPE_AR2312] = "Atheros AR2312",
@@ -46,6 +86,20 @@ void __init ar231x_serial_setup(u32 mapbase, int irq, unsigned int uartclk)
early_serial_setup(&s);
}

+int __init ar231x_add_wmac(int nr, u32 base, int irq)
+{
+ struct resource *res;
+
+ ar231x_wmac[nr].dev.platform_data = &ar231x_board;
+ res = &ar231x_wmac[nr].resource[0];
+ res->start = base;
+ res->end = base + 0x10000 - 1;
+ res++;
+ res->start = irq;
+ res->end = irq;
+ return platform_device_register(&ar231x_wmac[nr]);
+}
+
static int __init ar231x_register_devices(void)
{
ar5312_init_devices();
diff --git a/arch/mips/ar231x/devices.h b/arch/mips/ar231x/devices.h
index 5ffa091..6e3d242 100644
--- a/arch/mips/ar231x/devices.h
+++ b/arch/mips/ar231x/devices.h
@@ -24,6 +24,7 @@ extern void (*ar231x_irq_dispatch)(void);

int ar231x_find_config(const u8 *flash_limit);
void ar231x_serial_setup(u32 mapbase, int irq, unsigned int uartclk);
+int ar231x_add_wmac(int nr, u32 base, int irq);

static inline bool is_2315(void)
{
--
1.8.5.5
Loading...