From a7473717483ef3bb78563611bf1b3b82c5515b2e Mon Sep 17 00:00:00 2001 From: Sergey Ryazanov Date: Wed, 29 Oct 2014 03:18:44 +0400 Subject: 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 Cc: Linux MIPS Patchwork: https://patchwork.linux-mips.org/patch/8243/ Signed-off-by: Ralf Baechle --- arch/mips/ath25/ar2315.c | 6 ++ arch/mips/ath25/ar2315.h | 2 + arch/mips/ath25/ar5312.c | 39 +++++++++++ arch/mips/ath25/ar5312.h | 2 + arch/mips/ath25/board.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++ arch/mips/ath25/devices.c | 15 +++++ arch/mips/ath25/devices.h | 2 + 7 files changed, 227 insertions(+) (limited to 'arch/mips/ath25') 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 #include +#include #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 #include +#include #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) -- cgit