Discussion:
[RFC 08/18] gpio: add driver for Atheros AR5312 SoC GPIO controller
Sergey Ryazanov
2014-09-14 19:33:23 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
---
arch/mips/ar231x/Kconfig | 1 +
arch/mips/ar231x/ar5312.c | 19 ++++
arch/mips/include/asm/mach-ar231x/ar5312_regs.h | 2 +
drivers/gpio/Kconfig | 7 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-ar5312.c | 121 ++++++++++++++++++++++++
6 files changed, 151 insertions(+)
create mode 100644 drivers/gpio/gpio-ar5312.c

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

config SOC_AR2315
diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
index 56cb0d7..8683eb6 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -16,6 +16,7 @@

#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <asm/bootinfo.h>
#include <asm/reboot.h>
@@ -118,6 +119,22 @@ void __init ar5312_arch_init_irq(void)
irq_set_chained_handler(AR5312_IRQ_MISC, ar5312_misc_irq_handler);
}

+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;
@@ -157,6 +174,8 @@ void __init ar5312_init_devices(void)

/* Locate board/radio config data */
ar231x_find_config(ar5312_flash_limit);
+
+ platform_device_register(&ar5312_gpio);
}

static void ar5312_restart(char *command)
diff --git a/arch/mips/include/asm/mach-ar231x/ar5312_regs.h b/arch/mips/include/asm/mach-ar231x/ar5312_regs.h
index 0b4cee8..104c558 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 */
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.1.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
Sergey Ryazanov
2014-09-14 19:33:24 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
---
arch/mips/ar231x/Kconfig | 1 +
arch/mips/ar231x/ar2315.c | 49 ++++-
arch/mips/include/asm/mach-ar231x/ar2315_regs.h | 5 +
arch/mips/include/asm/mach-ar231x/ar231x.h | 1 +
drivers/gpio/Kconfig | 7 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-ar2315.c | 233 ++++++++++++++++++++++++
7 files changed, 294 insertions(+), 3 deletions(-)
create mode 100644 drivers/gpio/gpio-ar2315.c

diff --git a/arch/mips/ar231x/Kconfig b/arch/mips/ar231x/Kconfig
index 378a6e1..88ca061 100644
--- a/arch/mips/ar231x/Kconfig
+++ b/arch/mips/ar231x/Kconfig
@@ -7,4 +7,5 @@ config SOC_AR5312
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 a766b0d..06074cb 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>
@@ -53,7 +56,10 @@ static void ar2315_misc_irq_handler(unsigned irq, struct irq_desc *desc)
generic_handle_irq(AR2315_MISC_IRQ_TIMER);
else if (pending & AR2315_ISR_AHB)
generic_handle_irq(AR2315_MISC_IRQ_AHB);
- else if (pending & AR2315_ISR_UART0)
+ else if (pending & AR2315_ISR_GPIO) {
+ ar231x_write_reg(AR2315_ISR, AR2315_ISR_GPIO);
+ generic_handle_irq(AR2315_MISC_IRQ_GPIO);
+ } else if (pending & AR2315_ISR_UART0)
generic_handle_irq(AR2315_MISC_IRQ_UART0);
else
spurious_interrupt();
@@ -119,6 +125,34 @@ void __init ar2315_arch_init_irq(void)
irq_set_chained_handler(AR2315_IRQ_MISC, ar2315_misc_irq_handler);
}

+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,
+ .start = AR2315_MISC_IRQ_GPIO,
+ .end = AR2315_MISC_IRQ_GPIO,
+ },
+ {
+ .name = "ar2315-gpio-irq-base",
+ .flags = IORESOURCE_IRQ,
+ .start = AR231X_GPIO_IRQ_BASE,
+ .end = AR231X_GPIO_IRQ_BASE,
+ }
+};
+
+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
@@ -134,6 +168,8 @@ void __init ar2315_init_devices(void)

/* Find board configuration */
ar231x_find_config(ar2315_flash_limit);
+
+ platform_device_register(&ar2315_gpio);
}

static void ar2315_restart(char *command)
@@ -145,8 +181,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/include/asm/mach-ar231x/ar2315_regs.h b/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
index 27f5490..dd7df42 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/ar231x.h b/arch/mips/include/asm/mach-ar231x/ar231x.h
index 69702b2..259001d 100644
--- a/arch/mips/include/asm/mach-ar231x/ar231x.h
+++ b/arch/mips/include/asm/mach-ar231x/ar231x.h
@@ -4,6 +4,7 @@
#include <linux/io.h>

#define AR231X_MISC_IRQ_BASE 0x20
+#define AR231X_GPIO_IRQ_BASE 0x30

#define AR231X_IRQ_CPU_CLOCK (MIPS_CPU_IRQ_BASE + 7) /* C0_CAUSE: 0x8000 */

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..00e3101
--- /dev/null
+++ b/drivers/gpio/gpio-ar2315.c
@@ -0,0 +1,233 @@
+/*
+ * 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,
+ "ar2315-gpio-irq-base");
+ if (!res) {
+ dev_err(dev, "not found GPIO IRQ base\n");
+ return -ENXIO;
+ }
+ ar2315_gpio_irq_base = res->start;
+
+ 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;
+ }
+
+ 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.1.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-09-29 09:18:04 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.
(...)
Post by Sergey Ryazanov
- /* 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);
Please try to use the new GPIO descriptor API.
See Documentation/gpio/consumer.txt

(...)
Post by Sergey Ryazanov
+++ b/drivers/gpio/gpio-ar2315.c
+static u32 ar2315_gpio_intmask;
+static u32 ar2315_gpio_intval;
+static unsigned ar2315_gpio_irq_base;
+static void __iomem *ar2315_mem;
Get rid of these local variables and put them into an allocated
state container, see
Documentation/driver-model/design-patterns.txt

Get rid of _gpio_irq_base altogether. (See comments below.)

(...)
Post by Sergey Ryazanov
+static inline u32 ar2315_gpio_reg_read(unsigned reg)
+{
+ return __raw_readl(ar2315_mem + reg);
+}
Use readl_relaxed()
Post by Sergey Ryazanov
+static inline void ar2315_gpio_reg_write(unsigned reg, u32 val)
+{
+ __raw_writel(val, ar2315_mem + reg);
+}
Use writel_relaxed()
Post by Sergey Ryazanov
+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);
Do this:

#include <linux/bitops.h>

pend &= ~BIT(bit);
Post by Sergey Ryazanov
+ ar2315_gpio_intmask |= (1 << gpio);
|= BIT(gpio);
Post by Sergey Ryazanov
+ 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;
Wait, no. No keeping irq bases around like that please.

unsigned offset = d->hwirq;

And call it offset rather than "gpio" everywhere please, since
it is local to this GPIO controller, not the global GPIO numberspace.
Post by Sergey Ryazanov
+ /* Disable interrupt */
+ ar2315_gpio_intmask &= ~(1 << gpio);
&= ~BIT(gpio);
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:
return !!(ar2315_gpio_reg_read(AR2315_GPIO_DI) & BIT(gpio));
Post by Sergey Ryazanov
+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);
Convoluted, I would use an if() else construct rather than the ? operator.
Post by Sergey Ryazanov
+static int ar2315_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+ return ar2315_gpio_irq_base + gpio;
+}
Don't implement this at all. You're using the gpiolib irqchip helpers!
It will just overrun this implementation.

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
Jonas Gorski
2014-09-29 09:24:18 UTC
Permalink
On Mon, Sep 29, 2014 at 11:18 AM, Linus Walleij
Post by Linus Walleij
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.
(...)
Post by Sergey Ryazanov
- /* 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);
Please try to use the new GPIO descriptor API.
See Documentation/gpio/consumer.txt
(...)
Post by Sergey Ryazanov
+++ b/drivers/gpio/gpio-ar2315.c
+static u32 ar2315_gpio_intmask;
+static u32 ar2315_gpio_intval;
+static unsigned ar2315_gpio_irq_base;
+static void __iomem *ar2315_mem;
Get rid of these local variables and put them into an allocated
state container, see
Documentation/driver-model/design-patterns.txt
Get rid of _gpio_irq_base altogether. (See comments below.)
(...)
Post by Sergey Ryazanov
+static inline u32 ar2315_gpio_reg_read(unsigned reg)
+{
+ return __raw_readl(ar2315_mem + reg);
+}
Use readl_relaxed()
ar2315/ar5312 is big endian, so readl_relaxed would mess up the byte
order. There does not seem to be a big endian equivalent for _relaxed.


Regards
Jonas
--
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-29 20:43:21 UTC
Permalink
Post by Linus Walleij
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.
(...)
Post by Sergey Ryazanov
- /* 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);
Please try to use the new GPIO descriptor API.
See Documentation/gpio/consumer.txt
I will investigate this possibility.
Post by Linus Walleij
(...)
Post by Sergey Ryazanov
+++ b/drivers/gpio/gpio-ar2315.c
+static u32 ar2315_gpio_intmask;
+static u32 ar2315_gpio_intval;
+static unsigned ar2315_gpio_irq_base;
+static void __iomem *ar2315_mem;
Get rid of these local variables and put them into an allocated
state container, see
Documentation/driver-model/design-patterns.txt
AR2315 SoC contains only one GPIO unit, so there are no reasons to
increase driver complexity. But if you insist, I will add state
container.
Post by Linus Walleij
Get rid of _gpio_irq_base altogether. (See comments below.)
(...)
Post by Sergey Ryazanov
+static inline u32 ar2315_gpio_reg_read(unsigned reg)
+{
+ return __raw_readl(ar2315_mem + reg);
+}
Use readl_relaxed()
Post by Sergey Ryazanov
+static inline void ar2315_gpio_reg_write(unsigned reg, u32 val)
+{
+ __raw_writel(val, ar2315_mem + reg);
+}
Use writel_relaxed()
Post by Sergey Ryazanov
+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);
#include <linux/bitops.h>
pend &= ~BIT(bit);
Post by Sergey Ryazanov
+ ar2315_gpio_intmask |= (1 << gpio);
|= BIT(gpio);
Post by Sergey Ryazanov
+ 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;
Wait, no. No keeping irq bases around like that please.
unsigned offset = d->hwirq;
And call it offset rather than "gpio" everywhere please, since
it is local to this GPIO controller, not the global GPIO numberspace.
Sure.
Post by Linus Walleij
Post by Sergey Ryazanov
+ /* Disable interrupt */
+ ar2315_gpio_intmask &= ~(1 << gpio);
&= ~BIT(gpio);
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(gpio));
Ok.
Post by Linus Walleij
Post by Sergey Ryazanov
+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);
Convoluted, I would use an if() else construct rather than the ? operator.
Convoluted, but 3 lines shorter :) And checkpatch has no objections.
Post by Linus Walleij
Post by Sergey Ryazanov
+static int ar2315_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+ return ar2315_gpio_irq_base + gpio;
+}
Don't implement this at all. You're using the gpiolib irqchip helpers!
It will just overrun this implementation.
Seems that I missed some new gpiolib features. I will try to rewrite this.

Thank you for detailed review!
--
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
Linus Walleij
2014-10-21 08:29:27 UTC
Permalink
On Mon, Sep 29, 2014 at 10:43 PM, Sergey Ryazanov
Post by Sergey Ryazanov
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;
Get rid of these local variables and put them into an allocated
state container, see
Documentation/driver-model/design-patterns.txt
AR2315 SoC contains only one GPIO unit, so there are no reasons to
increase driver complexity. But if you insist, I will add state
container.
I insist. It makes the driver easier to maintain if it looks like
most other drivers instead of using static locals.
Post by Sergey Ryazanov
Post by Linus Walleij
Convoluted, I would use an if() else construct rather than the ? operator.
Convoluted, but 3 lines shorter :) And checkpatch has no objections.
True but it's me who is going to be maintaining this, not checkpatch.

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-21 08:59:15 UTC
Permalink
Post by Linus Walleij
On Mon, Sep 29, 2014 at 10:43 PM, Sergey Ryazanov
Post by Sergey Ryazanov
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;
Get rid of these local variables and put them into an allocated
state container, see
Documentation/driver-model/design-patterns.txt
AR2315 SoC contains only one GPIO unit, so there are no reasons to
increase driver complexity. But if you insist, I will add state
container.
I insist. It makes the driver easier to maintain if it looks like
most other drivers instead of using static locals.
No problem. I rewrite the driver using the state container.
Post by Linus Walleij
Post by Sergey Ryazanov
Post by Linus Walleij
Convoluted, I would use an if() else construct rather than the ? operator.
Convoluted, but 3 lines shorter :) And checkpatch has no objections.
True but it's me who is going to be maintaining this, not checkpatch.
I missed that. Added to my todo list.

BTW, the use of the irq_domain framework is required or I could
opencode some stuff?
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-14 19:33:32 UTC
Permalink
Rename the ATHEROS_AR231X config symbol to AR231X.

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
---
drivers/net/wireless/ath/ath5k/Kconfig | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig
index c9f81a3..027c11c 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 && !PCI)
+ select ATH5K_PCI if (!AR231X && PCI)
---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 && !PCI)
---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.
--
1.8.1.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
Sergey Ryazanov
2014-09-14 19:33:31 UTC
Permalink
This code parts should depend on a config symbol defined in the driver
instead of arch specific one.

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
---
drivers/net/wireless/ath/ath5k/ath5k.h | 2 +-
drivers/net/wireless/ath/ath5k/base.c | 4 ++--
drivers/net/wireless/ath/ath5k/led.c | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)

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.1.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
Sergey Ryazanov
2014-09-14 19:33:33 UTC
Permalink
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
---
drivers/net/wireless/ath/ath5k/Kconfig | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig
index 027c11c..2b2a399 100644
--- a/drivers/net/wireless/ath/ath5k/Kconfig
+++ b/drivers/net/wireless/ath/ath5k/Kconfig
@@ -6,8 +6,8 @@ config ATH5K
select LEDS_CLASS
select NEW_LEDS
select AVERAGE
- select ATH5K_AHB if (AR231X && !PCI)
- select ATH5K_PCI if (!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,7 +54,7 @@ config ATH5K_TRACER

config ATH5K_AHB
bool "Atheros 5xxx AHB bus support"
- depends on (AR231X && !PCI)
+ depends on AR231X
---help---
This adds support for WiSoC type chipsets of the 5xxx Atheros
family.
--
1.8.1.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
Sergey Ryazanov
2014-09-14 19:33:16 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.1.5
Sergey Ryazanov
2014-09-14 19:33:17 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.1.5
Sergey Ryazanov
2014-09-14 19:33:18 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.1.5
Sergey Ryazanov
2014-09-14 19:33:19 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>
---
arch/mips/ar231x/ar2315.c | 90 +++++++++++++++++++++++++
arch/mips/ar231x/ar2315.h | 2 +
arch/mips/ar231x/ar5312.c | 89 ++++++++++++++++++++++++
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 | 4 ++
arch/mips/include/asm/mach-ar231x/ar5312_regs.h | 23 +++++++
9 files changed, 242 insertions(+)

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

+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);
+
+ if (pending & AR2315_ISR_SPI)
+ generic_handle_irq(AR2315_MISC_IRQ_SPI);
+ else if (pending & AR2315_ISR_TIMER)
+ generic_handle_irq(AR2315_MISC_IRQ_TIMER);
+ else if (pending & AR2315_ISR_AHB)
+ generic_handle_irq(AR2315_MISC_IRQ_AHB);
+ else
+ spurious_interrupt();
+}
+
+static void ar2315_misc_irq_unmask(struct irq_data *d)
+{
+ u32 imr = ar231x_read_reg(AR2315_IMR);
+
+ imr |= 1 << (d->irq - AR231X_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 - AR231X_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_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;
+
+ if (!is_2315())
+ return;
+
+ ar231x_irq_dispatch = ar2315_irq_dispatch;
+ for (i = 0; i < AR2315_MISC_IRQ_COUNT; i++) {
+ unsigned irq = AR231X_MISC_IRQ_BASE + i;
+
+ irq_set_chip_and_handler(irq, &ar2315_misc_irq_chip,
+ handle_level_irq);
+ }
+ setup_irq(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..c3abb13 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -27,6 +27,95 @@
#include "devices.h"
#include "ar5312.h"

+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);
+
+ if (pending & AR5312_ISR_TIMER) {
+ generic_handle_irq(AR5312_MISC_IRQ_TIMER);
+ (void)ar231x_read_reg(AR5312_TIMER);
+ } else if (pending & AR5312_ISR_AHBPROC)
+ generic_handle_irq(AR5312_MISC_IRQ_AHB_PROC);
+ else if (pending & AR5312_ISR_WD)
+ generic_handle_irq(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 - AR231X_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 - AR231X_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_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;
+
+ if (!is_5312())
+ return;
+
+ ar231x_irq_dispatch = ar5312_irq_dispatch;
+ for (i = 0; i < AR5312_MISC_IRQ_COUNT; i++) {
+ unsigned irq = AR231X_MISC_IRQ_BASE + i;
+
+ irq_set_chip_and_handler(irq, &ar5312_misc_irq_chip,
+ handle_level_irq);
+ }
+ setup_irq(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..27f5490 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 (AR231X_MISC_IRQ_BASE+0)
+#define AR2315_MISC_IRQ_I2C_RSVD (AR231X_MISC_IRQ_BASE+1)
+#define AR2315_MISC_IRQ_SPI (AR231X_MISC_IRQ_BASE+2)
+#define AR2315_MISC_IRQ_AHB (AR231X_MISC_IRQ_BASE+3)
+#define AR2315_MISC_IRQ_APB (AR231X_MISC_IRQ_BASE+4)
+#define AR2315_MISC_IRQ_TIMER (AR231X_MISC_IRQ_BASE+5)
+#define AR2315_MISC_IRQ_GPIO (AR231X_MISC_IRQ_BASE+6)
+#define AR2315_MISC_IRQ_WATCHDOG (AR231X_MISC_IRQ_BASE+7)
+#define AR2315_MISC_IRQ_IR_RSVD (AR231X_MISC_IRQ_BASE+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..69702b2 100644
--- a/arch/mips/include/asm/mach-ar231x/ar231x.h
+++ b/arch/mips/include/asm/mach-ar231x/ar231x.h
@@ -3,6 +3,10 @@

#include <linux/io.h>

+#define AR231X_MISC_IRQ_BASE 0x20
+
+#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..0b4cee8 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 (AR231X_MISC_IRQ_BASE+0)
+#define AR5312_MISC_IRQ_AHB_PROC (AR231X_MISC_IRQ_BASE+1)
+#define AR5312_MISC_IRQ_AHB_DMA (AR231X_MISC_IRQ_BASE+2)
+#define AR5312_MISC_IRQ_GPIO (AR231X_MISC_IRQ_BASE+3)
+#define AR5312_MISC_IRQ_UART0 (AR231X_MISC_IRQ_BASE+4)
+#define AR5312_MISC_IRQ_UART0_DMA (AR231X_MISC_IRQ_BASE+5)
+#define AR5312_MISC_IRQ_WATCHDOG (AR231X_MISC_IRQ_BASE+6)
+#define AR5312_MISC_IRQ_LOCAL (AR231X_MISC_IRQ_BASE+7)
+#define AR5312_MISC_IRQ_SPI (AR231X_MISC_IRQ_BASE+8)
+#define AR5312_MISC_IRQ_COUNT 9
+
+/*
* Address Map
*
* The AR5312 supports 2 enet MACS, even though many reference boards only
--
1.8.1.5
Sergey Ryazanov
2014-09-14 19:33:20 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.1.5
Sergey Ryazanov
2014-09-14 19:33:21 UTC
Permalink
Signed-off-by: Sergey Ryazanov <***@gmail.com>
---
arch/mips/ar231x/ar2315.c | 4 ++++
arch/mips/ar231x/ar5312.c | 4 ++++
arch/mips/ar231x/devices.c | 17 +++++++++++++++++
arch/mips/ar231x/devices.h | 2 ++
4 files changed, 27 insertions(+)

diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
index e64aa16..94bab76 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -53,6 +53,8 @@ static void ar2315_misc_irq_handler(unsigned irq, struct irq_desc *desc)
generic_handle_irq(AR2315_MISC_IRQ_TIMER);
else if (pending & AR2315_ISR_AHB)
generic_handle_irq(AR2315_MISC_IRQ_AHB);
+ else if (pending & AR2315_ISR_UART0)
+ generic_handle_irq(AR2315_MISC_IRQ_UART0);
else
spurious_interrupt();
}
@@ -207,6 +209,8 @@ void __init ar2315_plat_mem_setup(void)
ar231x_write_reg(AR2315_WDC, AR2315_WDC_IGNORE_EXPIRATION);

_machine_restart = ar2315_restart;
+ ar231x_serial_setup(AR2315_UART0, AR2315_MISC_IRQ_UART0,
+ ar2315_apb_frequency());
}

void __init ar2315_prom_init(void)
diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
index c3abb13..35f7c71 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -55,6 +55,8 @@ static void ar5312_misc_irq_handler(unsigned irq, struct irq_desc *desc)
(void)ar231x_read_reg(AR5312_TIMER);
} else if (pending & AR5312_ISR_AHBPROC)
generic_handle_irq(AR5312_MISC_IRQ_AHB_PROC);
+ else if ((pending & AR5312_ISR_UART0))
+ generic_handle_irq(AR5312_MISC_IRQ_UART0);
else if (pending & AR5312_ISR_WD)
generic_handle_irq(AR5312_MISC_IRQ_WATCHDOG);
else
@@ -211,6 +213,8 @@ void __init ar5312_plat_mem_setup(void)
ar231x_write_reg(AR5312_WD_CTRL, AR5312_WD_CTRL_IGNORE_EXPIRATION);

_machine_restart = ar5312_restart;
+ ar231x_serial_setup(AR5312_UART0, AR5312_MISC_IRQ_UART0,
+ ar5312_sys_frequency());
}

void __init ar5312_prom_init(void)
diff --git a/arch/mips/ar231x/devices.c b/arch/mips/ar231x/devices.c
index f71a643..3d650bb 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,19 @@ 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);
+}
+
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.1.5
Sergey Ryazanov
2014-09-14 19:33:27 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.
---
arch/mips/ar231x/ar2315.c | 23 ++
drivers/mtd/devices/Kconfig | 5 +
drivers/mtd/devices/Makefile | 1 +
drivers/mtd/devices/ar2315.c | 459 ++++++++++++++++++++++++++++++++++
drivers/mtd/devices/ar2315_spiflash.h | 106 ++++++++
5 files changed, 594 insertions(+)
create mode 100644 drivers/mtd/devices/ar2315.c
create mode 100644 drivers/mtd/devices/ar2315_spiflash.h

diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
index ab38ad4..cab3b76 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -126,6 +126,28 @@ 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_gpio_res[] = {
{
.name = "ar2315-gpio",
@@ -171,6 +193,7 @@ void __init ar2315_init_devices(void)
ar231x_find_config(ar2315_flash_limit);

platform_device_register(&ar2315_gpio);
+ platform_device_register(&ar2315_spiflash);
}

static void ar2315_restart(char *command)
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.1.5
Sergey Ryazanov
2014-09-14 19:33:22 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 | 13 ++
arch/mips/ar231x/devices.h | 2 +
.../mips/include/asm/mach-ar231x/ar231x_platform.h | 73 ++++++++++
8 files changed, 303 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 94bab76..a766b0d 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -119,6 +119,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 2a57858..3ef7a28 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);
@@ -11,6 +12,7 @@ void ar2315_prom_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 35f7c71..56cb0d7 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -118,6 +118,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 b60ad38..a023350 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);
@@ -11,6 +12,7 @@ void ar5312_prom_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 3d650bb..66cd151 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,3 +39,12 @@ 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);
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.1.5
Sergey Ryazanov
2014-09-14 19:33:25 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 06074cb..ab38ad4 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -24,6 +24,7 @@
#include <asm/reboot.h>
#include <asm/time.h>

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

@@ -275,7 +276,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;
@@ -286,5 +287,24 @@ 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;
}

diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
index 8683eb6..6a429d2 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -22,6 +22,7 @@
#include <asm/reboot.h>
#include <asm/time.h>

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

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

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

@@ -174,6 +177,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;

platform_device_register(&ar5312_gpio);
}
@@ -280,6 +296,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;
@@ -292,5 +309,10 @@ 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;
}

diff --git a/arch/mips/ar231x/devices.c b/arch/mips/ar231x/devices.c
index 66cd151..95083b0 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.1.5
Sergey Ryazanov
2014-09-14 19:33:26 UTC
Permalink
Signed-off-by: Sergey Ryazanov <***@gmail.com>
---
arch/mips/ar231x/ar5312.c | 34 ++++++++++++++++++++++++++++++++--
1 file changed, 32 insertions(+), 2 deletions(-)

diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
index 6a429d2..944cd16 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -17,6 +17,7 @@
#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>
@@ -120,6 +121,24 @@ 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",
@@ -138,13 +157,23 @@ static struct platform_device ar5312_gpio = {

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;
@@ -191,6 +220,7 @@ void __init ar5312_init_devices(void)
else
ar231x_devtype = DEV_TYPE_AR5312;

+ platform_device_register(&ar5312_physmap_flash);
platform_device_register(&ar5312_gpio);
}
--
1.8.1.5
Sergey Ryazanov
2014-09-14 19:33:28 UTC
Permalink
Signed-off-by: Sergey Ryazanov <***@gmail.com>
Cc: Wim Van Sebroeck <***@iguana.be>
Cc: linux-***@vger.kernel.org
---
arch/mips/ar231x/ar2315.c | 26 +++++-
drivers/watchdog/Kconfig | 7 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/ar2315-wtd.c | 202 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 235 insertions(+), 1 deletion(-)
create mode 100644 drivers/watchdog/ar2315-wtd.c

diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
index cab3b76..62cf548 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -62,7 +62,10 @@ static void ar2315_misc_irq_handler(unsigned irq, struct irq_desc *desc)
generic_handle_irq(AR2315_MISC_IRQ_GPIO);
} else if (pending & AR2315_ISR_UART0)
generic_handle_irq(AR2315_MISC_IRQ_UART0);
- else
+ else if (pending & AR2315_ISR_WD) {
+ ar231x_write_reg(AR2315_ISR, AR2315_ISR_WD);
+ generic_handle_irq(AR2315_MISC_IRQ_WATCHDOG);
+ } else
spurious_interrupt();
}

@@ -148,6 +151,26 @@ static struct platform_device ar2315_spiflash = {
.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,
+ .start = AR2315_MISC_IRQ_WATCHDOG,
+ .end = AR2315_MISC_IRQ_WATCHDOG,
+ }
+};
+
+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",
@@ -193,6 +216,7 @@ void __init ar2315_init_devices(void)
ar231x_find_config(ar2315_flash_limit);

platform_device_register(&ar2315_gpio);
+ platform_device_register(&ar2315_wdt);
platform_device_register(&ar2315_spiflash);
}

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

+config AR2315_WDT
+ tristate "Atheros AR2315+ WiSoCs Watchdog Timer"
+ 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..8e1687a
--- /dev/null
+++ b/drivers/watchdog/ar2315-wtd.c
@@ -0,0 +1,202 @@
+/*
+ * 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/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#define DRIVER_NAME "ar2315-wdt"
+
+#define CLOCK_RATE 40000000
+#define HEARTBEAT(x) (x < 1 || x > 90 ? 20 : x)
+
+#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 wdt_timeout = 20;
+static int started;
+static int in_use;
+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(void)
+{
+ ar2315_wdt_wr(WDT_REG_TIMER, wdt_timeout * CLOCK_RATE);
+}
+
+static ssize_t ar2315_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len)
+ ar2315_wdt_enable();
+ return len;
+}
+
+static int ar2315_wdt_open(struct inode *inode, struct file *file)
+{
+ if (in_use)
+ return -EBUSY;
+ ar2315_wdt_enable();
+ in_use = 1;
+ started = 1;
+ return nonseekable_open(inode, file);
+}
+
+static int ar2315_wdt_release(struct inode *inode, struct file *file)
+{
+ in_use = 0;
+ 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 struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "ar2315 Watchdog",
+};
+
+static long ar2315_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_wdt_timeout;
+ int ret = -ENOIOCTLCMD;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((void __user *)arg, &ident, sizeof(ident)) ?
+ -EFAULT : 0;
+ break;
+ case WDIOC_KEEPALIVE:
+ ar2315_wdt_enable();
+ ret = 0;
+ break;
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(new_wdt_timeout, (int __user *)arg);
+ if (ret)
+ break;
+ wdt_timeout = HEARTBEAT(new_wdt_timeout);
+ ar2315_wdt_enable();
+ break;
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(wdt_timeout, (int __user *)arg);
+ break;
+ }
+ return ret;
+}
+
+static const struct file_operations ar2315_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = ar2315_wdt_write,
+ .unlocked_ioctl = ar2315_wdt_ioctl,
+ .open = ar2315_wdt_open,
+ .release = ar2315_wdt_release,
+};
+
+static struct miscdevice ar2315_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ar2315_wdt_fops,
+};
+
+static int ar2315_wdt_probe(struct platform_device *dev)
+{
+ struct resource *mem_res, *irq_res;
+ int ret = 0;
+
+ if (wdt_base)
+ return -EBUSY;
+
+ irq_res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+ if (!irq_res) {
+ dev_err(&dev->dev, "no IRQ resource\n");
+ return -ENOENT;
+ }
+
+ mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ wdt_base = devm_ioremap_resource(&dev->dev, mem_res);
+ if (IS_ERR(wdt_base))
+ return PTR_ERR(wdt_base);
+
+ ret = devm_request_irq(&dev->dev, irq_res->start, ar2315_wdt_interrupt,
+ IRQF_DISABLED, DRIVER_NAME, dev);
+ if (ret) {
+ dev_err(&dev->dev, "failed to register inetrrupt\n");
+ goto out;
+ }
+
+ ret = misc_register(&ar2315_wdt_miscdev);
+ if (ret)
+ dev_err(&dev->dev, "failed to register miscdev\n");
+
+out:
+ return ret;
+}
+
+static int ar2315_wdt_remove(struct platform_device *dev)
+{
+ misc_deregister(&ar2315_wdt_miscdev);
+ 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.1.5
Guenter Roeck
2014-09-15 03:01:55 UTC
Permalink
Post by Sergey Ryazanov
---
arch/mips/ar231x/ar2315.c | 26 +++++-
drivers/watchdog/Kconfig | 7 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/ar2315-wtd.c | 202 ++++++++++++++++++++++++++++++++++++++++++
This should be two patches: One to instantiate the watchdog,
the second the watchdog driver itself. The weatchdog driver
should use the watchdog infrastructure.

Guenter
Post by Sergey Ryazanov
4 files changed, 235 insertions(+), 1 deletion(-)
create mode 100644 drivers/watchdog/ar2315-wtd.c
diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
index cab3b76..62cf548 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -62,7 +62,10 @@ static void ar2315_misc_irq_handler(unsigned irq, struct irq_desc *desc)
generic_handle_irq(AR2315_MISC_IRQ_GPIO);
} else if (pending & AR2315_ISR_UART0)
generic_handle_irq(AR2315_MISC_IRQ_UART0);
- else
+ else if (pending & AR2315_ISR_WD) {
+ ar231x_write_reg(AR2315_ISR, AR2315_ISR_WD);
+ generic_handle_irq(AR2315_MISC_IRQ_WATCHDOG);
+ } else
spurious_interrupt();
}
@@ -148,6 +151,26 @@ static struct platform_device ar2315_spiflash = {
.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,
+ .start = AR2315_MISC_IRQ_WATCHDOG,
+ .end = AR2315_MISC_IRQ_WATCHDOG,
+ }
+};
+
+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",
@@ -193,6 +216,7 @@ void __init ar2315_init_devices(void)
ar231x_find_config(ar2315_flash_limit);
platform_device_register(&ar2315_gpio);
+ platform_device_register(&ar2315_wdt);
platform_device_register(&ar2315_spiflash);
}
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f57312f..0e84f3a 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1186,6 +1186,13 @@ config RALINK_WDT
help
Hardware driver for the Ralink SoC Watchdog Timer.
+config AR2315_WDT
+ tristate "Atheros AR2315+ WiSoCs Watchdog Timer"
+ 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..8e1687a
--- /dev/null
+++ b/drivers/watchdog/ar2315-wtd.c
@@ -0,0 +1,202 @@
+/*
+ * 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
2008 ?
Post by Sergey Ryazanov
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#define DRIVER_NAME "ar2315-wdt"
+
+#define CLOCK_RATE 40000000
+#define HEARTBEAT(x) (x < 1 || x > 90 ? 20 : x)
+
Whatever the logic is here, it does not make much sense to me.
Post by Sergey Ryazanov
+#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 wdt_timeout = 20;
+static int started;
+static int in_use;
+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(void)
+{
+ ar2315_wdt_wr(WDT_REG_TIMER, wdt_timeout * CLOCK_RATE);
+}
+
+static ssize_t ar2315_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len)
+ ar2315_wdt_enable();
+ return len;
+}
+
+static int ar2315_wdt_open(struct inode *inode, struct file *file)
+{
+ if (in_use)
+ return -EBUSY;
+ ar2315_wdt_enable();
+ in_use = 1;
+ started = 1;
+ return nonseekable_open(inode, file);
+}
+
+static int ar2315_wdt_release(struct inode *inode, struct file *file)
+{
+ in_use = 0;
+ 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 struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "ar2315 Watchdog",
+};
+
+static long ar2315_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_wdt_timeout;
+ int ret = -ENOIOCTLCMD;
+
+ switch (cmd) {
+ ret = copy_to_user((void __user *)arg, &ident, sizeof(ident)) ?
+ -EFAULT : 0;
+ break;
+ ar2315_wdt_enable();
+ ret = 0;
+ break;
+ ret = get_user(new_wdt_timeout, (int __user *)arg);
+ if (ret)
+ break;
+ wdt_timeout = HEARTBEAT(new_wdt_timeout);
+ ar2315_wdt_enable();
+ break;
+ ret = put_user(wdt_timeout, (int __user *)arg);
+ break;
+ }
+ return ret;
+}
+
+static const struct file_operations ar2315_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = ar2315_wdt_write,
+ .unlocked_ioctl = ar2315_wdt_ioctl,
+ .open = ar2315_wdt_open,
+ .release = ar2315_wdt_release,
+};
+
+static struct miscdevice ar2315_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ar2315_wdt_fops,
+};
+
+static int ar2315_wdt_probe(struct platform_device *dev)
+{
+ struct resource *mem_res, *irq_res;
+ int ret = 0;
+
+ if (wdt_base)
+ return -EBUSY;
+
+ irq_res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+ if (!irq_res) {
+ dev_err(&dev->dev, "no IRQ resource\n");
+ return -ENOENT;
+ }
+
+ mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ wdt_base = devm_ioremap_resource(&dev->dev, mem_res);
+ if (IS_ERR(wdt_base))
+ return PTR_ERR(wdt_base);
+
+ ret = devm_request_irq(&dev->dev, irq_res->start, ar2315_wdt_interrupt,
+ IRQF_DISABLED, DRIVER_NAME, dev);
+ if (ret) {
+ dev_err(&dev->dev, "failed to register inetrrupt\n");
+ goto out;
+ }
+
+ ret = misc_register(&ar2315_wdt_miscdev);
+ if (ret)
+ dev_err(&dev->dev, "failed to register miscdev\n");
+
+ return ret;
+}
+
+static int ar2315_wdt_remove(struct platform_device *dev)
+{
+ misc_deregister(&ar2315_wdt_miscdev);
+ 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-15 09:42:36 UTC
Permalink
Post by Guenter Roeck
Post by Sergey Ryazanov
---
arch/mips/ar231x/ar2315.c | 26 +++++-
drivers/watchdog/Kconfig | 7 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/ar2315-wtd.c | 202
++++++++++++++++++++++++++++++++++++++++++
This should be two patches: One to instantiate the watchdog,
the second the watchdog driver itself. The weatchdog driver
should use the watchdog infrastructure.
Ok. Will do in v2.

[skipped]
Post by Guenter Roeck
Post by Sergey Ryazanov
+ *
+ * Based on EP93xx and ifxmips wdt driver
2008 ?
Yes. This driver is pretty old.

[skipped]
Post by Guenter Roeck
Post by Sergey Ryazanov
+
+#define CLOCK_RATE 40000000
+#define HEARTBEAT(x) (x < 1 || x > 90 ? 20 : x)
+
Whatever the logic is here, it does not make much sense to me.
90 second is maximal value which we could write to register, and value
below 1 second is senseless. So this macros always return a value
which make sense: specified by user or default.
--
BR,
Sergey
Guenter Roeck
2014-09-15 13:22:40 UTC
Permalink
Post by Sergey Ryazanov
Post by Guenter Roeck
Post by Sergey Ryazanov
---
arch/mips/ar231x/ar2315.c | 26 +++++-
drivers/watchdog/Kconfig | 7 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/ar2315-wtd.c | 202
++++++++++++++++++++++++++++++++++++++++++
This should be two patches: One to instantiate the watchdog,
the second the watchdog driver itself. The weatchdog driver
should use the watchdog infrastructure.
Ok. Will do in v2.
[skipped]
Post by Guenter Roeck
Post by Sergey Ryazanov
+ *
+ * Based on EP93xx and ifxmips wdt driver
2008 ?
Yes. This driver is pretty old.
[skipped]
Post by Guenter Roeck
Post by Sergey Ryazanov
+
+#define CLOCK_RATE 40000000
+#define HEARTBEAT(x) (x < 1 || x > 90 ? 20 : x)
+
Whatever the logic is here, it does not make much sense to me.
90 second is maximal value which we could write to register, and value
below 1 second is senseless. So this macros always return a value
which make sense: specified by user or default.
I would agree that the value generates would be clamped, for example
by using the clamp_val macro. Bit your logic is such that the user
gets a timeout of 90 seconds when specifying 90 seconds, and 20
seconds when specifying 91 seconds.

Doesn't really matter much if you use the watchdog framework,
as it takes care of range checking.

Guenter
Sergey Ryazanov
2014-09-14 19:33:29 UTC
Permalink
Add PCI host controller driver and DMA address calculation hook.

Signed-off-by: Sergey Ryazanov <***@gmail.com>
---
arch/mips/ar231x/Kconfig | 7 +
arch/mips/ar231x/ar2315.c | 19 ++
arch/mips/include/asm/mach-ar231x/ar2315_regs.h | 24 ++
arch/mips/include/asm/mach-ar231x/dma-coherence.h | 18 +-
arch/mips/pci/Makefile | 1 +
arch/mips/pci/pci-ar2315.c | 345 ++++++++++++++++++++++
6 files changed, 411 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 62cf548..a9fbe4b 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -103,6 +103,11 @@ static void ar2315_irq_dispatch(void)
{
u32 pending = read_c0_status() & read_c0_cause();

+#ifdef CONFIG_PCI_AR2315
+ if (pending & CAUSEF_IP5)
+ do_IRQ(AR2315_IRQ_LCBUS_PCI);
+ else
+#endif
if (pending & CAUSEF_IP2)
do_IRQ(AR2315_IRQ_MISC);
else if (pending & CAUSEF_IP7)
@@ -355,3 +360,17 @@ void __init ar2315_prom_init(void)
ar231x_board.devid = devid;
}

+#ifdef CONFIG_PCI_AR2315
+static int __init ar2315_pci_init(void)
+{
+ struct platform_device *pdev;
+
+ if (!is_2315() || ar231x_devtype != DEV_TYPE_AR2315)
+ return -ENODEV;
+
+ pdev = platform_device_register_simple("ar2315-pci", -1, NULL, 0);
+
+ return pdev ? 0 : -ENODEV;
+}
+arch_initcall(ar2315_pci_init);
+#endif
diff --git a/arch/mips/include/asm/mach-ar231x/ar2315_regs.h b/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
index dd7df42..5dcc1dc 100644
--- a/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
+++ b/arch/mips/include/asm/mach-ar231x/ar2315_regs.h
@@ -38,6 +38,16 @@
#define AR2315_MISC_IRQ_COUNT 9

/*
+ * PCI interrupts, which share IP5
+ * Keep ordered according to AR2315_PCI_INT_XXX bits
+ */
+#define AR2315_PCI_IRQ_BASE 0x50
+#define AR2315_PCI_IRQ_EXT (AR2315_PCI_IRQ_BASE+0)
+#define AR2315_PCI_IRQ_ABORT (AR2315_PCI_IRQ_BASE+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 +564,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..f53d2e8
--- /dev/null
+++ b/arch/mips/pci/pci-ar2315.c
@@ -0,0 +1,345 @@
+/*
+ * 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 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_EXT);
+ else if (pending & AR2315_PCI_INT_ABORT)
+ generic_handle_irq(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)
+{
+ int 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) {
+ int 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;
+
+ 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_EXT;
+}
+
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+ return 0;
+}
--
1.8.1.5
Sergey Ryazanov
2014-09-14 19:33:30 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 | 8 ++++---
arch/mips/ar231x/ar5312.c | 28 +++++++++++++++++++++++-
arch/mips/ar231x/devices.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++
arch/mips/ar231x/devices.h | 1 +
4 files changed, 87 insertions(+), 4 deletions(-)

diff --git a/arch/mips/ar231x/ar2315.c b/arch/mips/ar231x/ar2315.c
index a9fbe4b..ce4be47 100644
--- a/arch/mips/ar231x/ar2315.c
+++ b/arch/mips/ar231x/ar2315.c
@@ -103,12 +103,13 @@ static void ar2315_irq_dispatch(void)
{
u32 pending = read_c0_status() & read_c0_cause();

+ if (pending & CAUSEF_IP3)
+ do_IRQ(AR2315_IRQ_WLAN0);
#ifdef CONFIG_PCI_AR2315
- if (pending & CAUSEF_IP5)
+ else if (pending & CAUSEF_IP5)
do_IRQ(AR2315_IRQ_LCBUS_PCI);
- else
#endif
- if (pending & CAUSEF_IP2)
+ else if (pending & CAUSEF_IP2)
do_IRQ(AR2315_IRQ_MISC);
else if (pending & CAUSEF_IP7)
do_IRQ(AR231X_IRQ_CPU_CLOCK);
@@ -223,6 +224,7 @@ void __init ar2315_init_devices(void)
platform_device_register(&ar2315_gpio);
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 944cd16..975f580 100644
--- a/arch/mips/ar231x/ar5312.c
+++ b/arch/mips/ar231x/ar5312.c
@@ -95,7 +95,11 @@ static void ar5312_irq_dispatch(void)
{
u32 pending = read_c0_status() & read_c0_cause();

- if (pending & CAUSEF_IP6)
+ 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);
@@ -222,6 +226,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 95083b0..951e7d7 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.1.5
Linus Walleij
2014-09-29 09:03:20 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.
(...)
Post by Sergey Ryazanov
diff --git a/arch/mips/ar231x/Kconfig b/arch/mips/ar231x/Kconfig
diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
diff --git a/arch/mips/include/asm/mach-ar231x/ar5312_regs.h b/arch/mips/include/asm/mach-ar231x/ar5312_regs.h
Please put these MIPS-related changes into a separate patch
to be handled through the MIPS git tree.

This driver seems surplus, it's just MMIO. Use
drivers/gpio/gpio-generic.c instead.

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
Linus Walleij
2014-09-29 09:04:35 UTC
Permalink
On Mon, Sep 29, 2014 at 11:03 AM, Linus Walleij
Post by Linus Walleij
This driver seems surplus, it's just MMIO. Use
drivers/gpio/gpio-generic.c instead.
Bah, I'm talking crap. It does IRQs so it needs to be a custom
driver for sure. I'll take a second look.

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-29 20:18:11 UTC
Permalink
Post by Linus Walleij
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.
(...)
Post by Sergey Ryazanov
diff --git a/arch/mips/ar231x/Kconfig b/arch/mips/ar231x/Kconfig
diff --git a/arch/mips/ar231x/ar5312.c b/arch/mips/ar231x/ar5312.c
diff --git a/arch/mips/include/asm/mach-ar231x/ar5312_regs.h b/arch/mips/include/asm/mach-ar231x/ar5312_regs.h
Please put these MIPS-related changes into a separate patch
to be handled through the MIPS git tree.
Already do that in non RFC patch.
--
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
Loading...