summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Ryazanov <ryazanov.s.a@gmail.com>2014-10-29 02:18:44 +0300
committerRalf Baechle <ralf@linux-mips.org>2014-11-24 09:45:27 +0300
commita7473717483ef3bb78563611bf1b3b82c5515b2e (patch)
treed5e3431409c4aee784c7c8401cf19614aed91d7c
parent1ac91b1f686e9d819b16525baf2e8db3c282edba (diff)
downloadlinux-a7473717483ef3bb78563611bf1b3b82c5515b2e.tar.xz
MIPS: ath25: add board configuration detection
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 <ryazanov.s.a@gmail.com> Cc: Linux MIPS <linux-mips@linux-mips.org> Patchwork: https://patchwork.linux-mips.org/patch/8243/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r--arch/mips/ath25/ar2315.c6
-rw-r--r--arch/mips/ath25/ar2315.h2
-rw-r--r--arch/mips/ath25/ar5312.c39
-rw-r--r--arch/mips/ath25/ar5312.h2
-rw-r--r--arch/mips/ath25/board.c161
-rw-r--r--arch/mips/ath25/devices.c15
-rw-r--r--arch/mips/ath25/devices.h2
-rw-r--r--arch/mips/include/asm/mach-ath25/ath25_platform.h73
8 files changed, 300 insertions, 0 deletions
diff --git a/arch/mips/ath25/ar2315.c b/arch/mips/ath25/ar2315.c
index d10eac4cd828..3ba8e757add6 100644
--- a/arch/mips/ath25/ar2315.c
+++ b/arch/mips/ath25/ar2315.c
@@ -160,6 +160,12 @@ void __init ar2315_arch_init_irq(void)
ar2315_misc_irq_domain = domain;
}
+void __init ar2315_init_devices(void)
+{
+ /* Find board configuration */
+ ath25_find_config(AR2315_SPI_READ_BASE, AR2315_SPI_READ_SIZE);
+}
+
static void ar2315_restart(char *command)
{
void (*mips_reset_vec)(void) = (void *)0xbfc00000;
diff --git a/arch/mips/ath25/ar2315.h b/arch/mips/ath25/ar2315.h
index 4af5f4c75f44..877afe63eed5 100644
--- a/arch/mips/ath25/ar2315.h
+++ b/arch/mips/ath25/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_arch_init(void);
@@ -11,6 +12,7 @@ void ar2315_arch_init(void);
#else
static inline void ar2315_arch_init_irq(void) {}
+static inline void ar2315_init_devices(void) {}
static inline void ar2315_plat_time_init(void) {}
static inline void ar2315_plat_mem_setup(void) {}
static inline void ar2315_arch_init(void) {}
diff --git a/arch/mips/ath25/ar5312.c b/arch/mips/ath25/ar5312.c
index 398d4fd4dd2d..41bd56d6ab23 100644
--- a/arch/mips/ath25/ar5312.c
+++ b/arch/mips/ath25/ar5312.c
@@ -158,6 +158,45 @@ void __init ar5312_arch_init_irq(void)
ar5312_misc_irq_domain = domain;
}
+static void __init ar5312_flash_init(void)
+{
+ void __iomem *flashctl_base;
+ u32 ctl;
+
+ flashctl_base = ioremap_nocache(AR5312_FLASHCTL_BASE,
+ AR5312_FLASHCTL_SIZE);
+
+ /*
+ * Configure flash bank 0.
+ * Assume 8M window size. Flash will be aliased if it's smaller
+ */
+ ctl = __raw_readl(flashctl_base + AR5312_FLASHCTL0);
+ ctl &= 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;
+ __raw_writel(ctl, flashctl_base + AR5312_FLASHCTL0);
+
+ /* Disable other flash banks */
+ ctl = __raw_readl(flashctl_base + AR5312_FLASHCTL1);
+ ctl &= ~(AR5312_FLASHCTL_E | AR5312_FLASHCTL_AC);
+ __raw_writel(ctl, flashctl_base + AR5312_FLASHCTL1);
+ ctl = __raw_readl(flashctl_base + AR5312_FLASHCTL2);
+ ctl &= ~(AR5312_FLASHCTL_E | AR5312_FLASHCTL_AC);
+ __raw_writel(ctl, flashctl_base + AR5312_FLASHCTL2);
+
+ iounmap(flashctl_base);
+}
+
+void __init ar5312_init_devices(void)
+{
+ ar5312_flash_init();
+
+ /* Locate board/radio config data */
+ ath25_find_config(AR5312_FLASH_BASE, AR5312_FLASH_SIZE);
+}
+
static void ar5312_restart(char *command)
{
/* reset the system */
diff --git a/arch/mips/ath25/ar5312.h b/arch/mips/ath25/ar5312.h
index 86dfc6d04a6d..470abb0052bd 100644
--- a/arch/mips/ath25/ar5312.h
+++ b/arch/mips/ath25/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_arch_init(void);
@@ -11,6 +12,7 @@ void ar5312_arch_init(void);
#else
static inline void ar5312_arch_init_irq(void) {}
+static inline void ar5312_init_devices(void) {}
static inline void ar5312_plat_time_init(void) {}
static inline void ar5312_plat_mem_setup(void) {}
static inline void ar5312_arch_init(void) {}
diff --git a/arch/mips/ath25/board.c b/arch/mips/ath25/board.c
index d4675e04a634..b8bb78282d6a 100644
--- a/arch/mips/ath25/board.c
+++ b/arch/mips/ath25/board.c
@@ -16,12 +16,173 @@
#include <asm/bootinfo.h>
#include <asm/time.h>
+#include <ath25_platform.h>
#include "devices.h"
#include "ar5312.h"
#include "ar2315.h"
void (*ath25_irq_dispatch)(void);
+static inline bool check_radio_magic(const void __iomem *addr)
+{
+ addr += 0x7a; /* offset for flash magic */
+ return (__raw_readb(addr) == 0x5a) && (__raw_readb(addr + 1) == 0xa5);
+}
+
+static inline bool check_notempty(const void __iomem *addr)
+{
+ return __raw_readl(addr) != 0xffffffff;
+}
+
+static inline bool check_board_data(const void __iomem *addr, bool broken)
+{
+ /* config magic found */
+ if (__raw_readl(addr) == ATH25_BD_MAGIC)
+ return true;
+
+ if (!broken)
+ return false;
+
+ /* broken board data detected, use radio data to find the
+ * offset, user will fix this */
+
+ if (check_radio_magic(addr + 0x1000))
+ return true;
+ if (check_radio_magic(addr + 0xf8))
+ return true;
+
+ return false;
+}
+
+static const void __iomem * __init find_board_config(const void __iomem *limit,
+ const bool broken)
+{
+ const void __iomem *addr;
+ const void __iomem *begin = limit - 0x1000;
+ const void __iomem *end = limit - 0x30000;
+
+ for (addr = begin; addr >= end; addr -= 0x1000)
+ if (check_board_data(addr, broken))
+ return addr;
+
+ return NULL;
+}
+
+static const void __iomem * __init find_radio_config(const void __iomem *limit,
+ const void __iomem *bcfg)
+{
+ const void __iomem *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 = 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 = limit - 0x1000 + 0xf8;
+ for (rcfg = begin; rcfg < end; rcfg += 0x1000)
+ if (check_notempty(rcfg) && check_radio_magic(rcfg))
+ return rcfg;
+
+ return NULL;
+}
+
+/*
+ * NB: Search region size could be larger than the actual flash size,
+ * but this shouldn't be a problem here, because the flash
+ * will simply be mapped multiple times.
+ */
+int __init ath25_find_config(phys_addr_t base, unsigned long size)
+{
+ const void __iomem *flash_base, *flash_limit;
+ struct ath25_boarddata *config;
+ unsigned int rcfg_size;
+ int broken_boarddata = 0;
+ const void __iomem *bcfg, *rcfg;
+ u8 *board_data;
+ u8 *radio_data;
+ u8 *mac_addr;
+ u32 offset;
+
+ flash_base = ioremap_nocache(base, size);
+ flash_limit = flash_base + size;
+
+ ath25_board.config = NULL;
+ ath25_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");
+ goto error;
+ }
+
+ board_data = kzalloc(BOARD_CONFIG_BUFSZ, GFP_KERNEL);
+ ath25_board.config = (struct ath25_boarddata *)board_data;
+ memcpy_fromio(board_data, bcfg, 0x100);
+ if (broken_boarddata) {
+ pr_warn("WARNING: broken board data detected\n");
+ config = ath25_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 */
+
+ rcfg = find_radio_config(flash_limit, bcfg);
+ if (!rcfg) {
+ pr_warn("WARNING: Could not find Radio Configuration data\n");
+ goto error;
+ }
+
+ radio_data = board_data + 0x100 + ((rcfg - bcfg) & 0xfff);
+ ath25_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_fromio(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, ath25_board.config->wlan0_mac);
+ }
+
+ iounmap(flash_base);
+
+ return 0;
+
+error:
+ iounmap(flash_base);
+ return -ENODEV;
+}
+
static void ath25_halt(void)
{
local_irq_disable();
diff --git a/arch/mips/ath25/devices.c b/arch/mips/ath25/devices.c
index 400419d8e7d9..d24dbb1ef8ea 100644
--- a/arch/mips/ath25/devices.c
+++ b/arch/mips/ath25/devices.c
@@ -3,10 +3,13 @@
#include <linux/serial_8250.h>
#include <asm/bootinfo.h>
+#include <ath25_platform.h>
#include "devices.h"
#include "ar5312.h"
#include "ar2315.h"
+struct ar231x_board_config ath25_board;
+
const char *get_system_type(void)
{
return "Atheros (unknown)";
@@ -28,6 +31,18 @@ void __init ath25_serial_setup(u32 mapbase, int irq, unsigned int uartclk)
early_serial_setup(&s);
}
+static int __init ath25_register_devices(void)
+{
+ if (is_ar5312())
+ ar5312_init_devices();
+ else
+ ar2315_init_devices();
+
+ return 0;
+}
+
+device_initcall(ath25_register_devices);
+
static int __init ath25_arch_init(void)
{
if (is_ar5312())
diff --git a/arch/mips/ath25/devices.h b/arch/mips/ath25/devices.h
index 23b53cb71c72..65e86cc46e49 100644
--- a/arch/mips/ath25/devices.h
+++ b/arch/mips/ath25/devices.h
@@ -7,8 +7,10 @@
#define ATH25_IRQ_CPU_CLOCK (MIPS_CPU_IRQ_BASE + 7) /* C0_CAUSE: 0x8000 */
+extern struct ar231x_board_config ath25_board;
extern void (*ath25_irq_dispatch)(void);
+int ath25_find_config(phys_addr_t offset, unsigned long size);
void ath25_serial_setup(u32 mapbase, int irq, unsigned int uartclk);
static inline bool is_ar2315(void)
diff --git a/arch/mips/include/asm/mach-ath25/ath25_platform.h b/arch/mips/include/asm/mach-ath25/ath25_platform.h
new file mode 100644
index 000000000000..4f4ee4f9e5ec
--- /dev/null
+++ b/arch/mips/include/asm/mach-ath25/ath25_platform.h
@@ -0,0 +1,73 @@
+#ifndef __ASM_MACH_ATH25_PLATFORM_H
+#define __ASM_MACH_ATH25_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 ath25_boarddata {
+ u32 magic; /* board data is valid */
+#define ATH25_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 ath25_boarddata *config;
+
+ /* radio calibration data */
+ const char *radio;
+};
+
+#endif /* __ASM_MACH_ATH25_PLATFORM_H */