[lantiq] dgn3500 support with eeprom loading from sysfs
authorJohn Crispin <john@openwrt.org>
Sun, 27 May 2012 16:02:22 +0000 (16:02 +0000)
committerJohn Crispin <john@openwrt.org>
Sun, 27 May 2012 16:02:22 +0000 (16:02 +0000)
WIFI eeprom:
As discussed, it is impossible for nand and spi flash platforms to have the eeprom data available from mtd. I suggested to load the eeprom from user-space. I've looked into regular firmware loading but this is only possible when using modules.  I've created a sysfs entry that allows reading and writing the eeprom data to the platform data. After loading the eeprom data I rely on pci-hotplug support to disable the bogus pci device and rescan the bus (with fixups and all). Because hotplug is not available, an init script is created that performs the copy from mtd to platform data. I think it is best to eventually move the sysfs functions to dev_wifi_athxk.c file, this would get rid of the external to the ath9k platform data.

SPI flash:
It seems that the spi-xway driver is not really working. It causes my kernel to crash in all sorts of ways. I added to bitbang SPI to be able to the calibration data mention above.
I've kept the original mtd partitioning that Netgear uses.

Buttons/LED:
Both buttons are working properly. Two leds are not enabled: The red internet led is connected to the pci_gnt1 pin... I can't disable it because then DMA stops working.
The green wifi led is connected to an unknown atheros gpio.

Signed-off-by: Pieter Voorthuijsen <p.voorthuijsen at gmail.com>
SVN-Revision: 31910

target/linux/lantiq/base-files/lib/preinit/42_athfix [new file with mode: 0644]
target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-netgear.c

diff --git a/target/linux/lantiq/base-files/lib/preinit/42_athfix b/target/linux/lantiq/base-files/lib/preinit/42_athfix
new file mode 100644 (file)
index 0000000..114aaff
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+. /lib/lantiq.sh
+
+init_atheeprom() {
+       local board=$(lantiq_board_name)
+       case $board in
+       "Netgear DGN3500B")
+               echo "- loading eeprom -"
+               dd if=/dev/mtd2 of=/sys/firmware/ath_eeprom bs=1k skip=60 count=4
+               echo 0 > /sys/bus/pci/slots/0000\:00\:0e.0/power
+               sleep 1
+               echo 1 > /sys/bus/pci/rescan
+               ;;
+       esac
+}
+
+boot_hook_add preinit_essential init_atheeprom
+
index f30478c..29b0728 100644 (file)
@@ -4,6 +4,7 @@
  *  by the Free Software Foundation.
  *
  *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ *  Copyright (C) 2012 Pieter Voorthuijsen <p.voorthuijsen@gmail.com>
  */
 
 #include <linux/init.h>
 #include <linux/phy.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
+#include <linux/spi/spi_gpio.h>
+#include <linux/ath9k_platform.h>
+#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
 
 #include <lantiq_soc.h>
 #include <irq.h>
+#include <dev-gpio-leds.h>
+#include <dev-gpio-buttons.h>
+#include "dev-wifi-athxk.h"
 
 #include "../machtypes.h"
 #include "devices.h"
 #include "dev-dwc_otg.h"
+#include "pci-ath-fixup.h"
+#include <mtd/mtd-abi.h>
+#include <asm-generic/sizes.h>
+
+static struct mtd_partition dgn3500_partitions[] = {
+       {
+               .name = "u-boot",
+               .offset = 0,
+               .size = 0x10000,
+               .mask_flags = MTD_WRITEABLE,
+       },
+       {
+               .name = "environment",
+               .offset = 0x10000,
+               .size = 0x10000,
+               .mask_flags = MTD_WRITEABLE,
+       },
+       {
+               .name = "calibration",
+               .offset = 0x20000,
+               .size = 0x10000,
+               .mask_flags = MTD_WRITEABLE,
+       },
+       {
+               .name = "linux",
+               .offset = 0x50000,
+               .size = 0xfa0000,
+       },
+};
 
 static struct ltq_pci_data ltq_pci_data = {
        .clock  = PCI_CLOCK_INT,
@@ -35,49 +74,161 @@ static struct ltq_eth_data ltq_eth_data = {
        .mii_mode = PHY_INTERFACE_MODE_MII,
 };
 
-static struct mtd_partition easy98000_nor_partitions[] =
-{
-       {
-               .name   = "uboot",
-               .offset = 0x0,
-               .size   = 0x40000,
-       },
+static struct gpio_led
+dgn3500_gpio_leds[] __initdata = {
+       { .name = "soc:green:power", .gpio = 34, .active_low = 1, },
+       { .name = "soc:red:power", .gpio = 39, .active_low = 1, },
+       { .name = "soc:orange:wlan", .gpio = 51, .active_low = 1, },
+       { .name = "soc:green:wps", .gpio = 52, .active_low = 1, },
+       { .name = "soc:green:usb", .gpio = 22, .active_low = 1, },
+       { .name = "soc:green:dsl", .gpio = 4, .active_low = 1, },
+       { .name = "soc:green:internet", .gpio = 2, .active_low = 1, },
+};
+
+static struct gpio_keys_button
+dgn3500_gpio_keys[] __initdata = {
        {
-               .name   = "uboot_env",
-               .offset = 0x40000,
-               .size   = 0x40000,      /* 2 sectors for redundant env. */
+               .desc = "wps",
+               .type = EV_KEY,
+               .code = BTN_0,
+               .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL,
+               .gpio = 54,
+               .active_low = 1,
        },
        {
-               .name   = "linux",
-               .offset = 0x80000,
-               .size   = 0xF80000,     /* map only 16 MiB */
+               .desc = "reset",
+               .type = EV_KEY,
+               .code = BTN_1,
+               .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL,
+               .gpio = 36,
+               .active_low = 1,
        },
 };
 
-static struct flash_platform_data easy98000_spi_flash_platform_data = {
-       .name = "sflash",
-       .parts = easy98000_nor_partitions,
-       .nr_parts = ARRAY_SIZE(easy98000_nor_partitions)
+#define SPI_GPIO_MRST   16
+#define SPI_GPIO_MTSR   17
+#define SPI_GPIO_CLK    18
+#define SPI_GPIO_CS0    10
+
+static struct spi_gpio_platform_data spi_gpio_data = {
+       .sck            = SPI_GPIO_CLK,
+       .mosi           = SPI_GPIO_MTSR,
+       .miso           = SPI_GPIO_MRST,
+       .num_chipselect = 2,
+};
+
+static struct platform_device spi_gpio_device = {
+       .name                   = "spi_gpio",
+       .dev.platform_data      = &spi_gpio_data,
+};
+
+static struct flash_platform_data spi_flash_data = {
+       .name           = "sflash",
+       .parts          = dgn3500_partitions,
+       .nr_parts       = ARRAY_SIZE(dgn3500_partitions),
 };
 
-static struct spi_board_info spi_info __initdata = {
-       .modalias               = "m25p80",
-       .bus_num                = 0,
-       .chip_select            = 3,
-       .max_speed_hz           = 10 * 1000 * 1000,
-       .mode                   = SPI_MODE_3,
-       .platform_data          = &easy98000_spi_flash_platform_data
+static struct spi_board_info spi_flash __initdata = {
+       .modalias               = "m25p80",
+       .bus_num                = 0,
+       .chip_select            = 0,
+       .max_speed_hz           = 10 * 1000 * 1000,
+       .mode                   = SPI_MODE_3,
+       .chip_select            = 0,
+       .controller_data        = (void *) SPI_GPIO_CS0,
+       .platform_data          = &spi_flash_data
 };
 
-struct ltq_spi_platform_data ltq_spi_data = {
-       .num_chipselect = 4,
+static u8 ltq_ethaddr[6] = { 0 };
+
+static int __init setup_ethaddr(char *str)
+{
+       if (!mac_pton(str, ltq_ethaddr))
+               memset(ltq_ethaddr, 0, 6);
+       return 0;
+}
+__setup("ethaddr=", setup_ethaddr);
+
+static u16 dgn3500_eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS] = {0};
+
+static ssize_t ath_eeprom_read(struct file *filp, struct kobject *kobj,
+               struct bin_attribute *attr, char *buf,
+               loff_t offset, size_t count)
+{
+       if (unlikely(offset >= sizeof(dgn3500_eeprom_data)))
+               return 0;
+       if ((offset + count) > sizeof(dgn3500_eeprom_data))
+               count = sizeof(dgn3500_eeprom_data) - offset;
+       if (unlikely(!count))
+               return count;
+
+       memcpy(buf, (char *)(dgn3500_eeprom_data) + offset, count);
+
+       return count;
+}
+
+extern struct ath9k_platform_data ath9k_pdata;
+
+static ssize_t ath_eeprom_write(struct file *filp, struct kobject *kobj,
+               struct bin_attribute *attr, char *buf,
+               loff_t offset, size_t count)
+{
+       int i;
+       char *eeprom_bytes = (char *)dgn3500_eeprom_data;
+
+       if (unlikely(offset >= sizeof(dgn3500_eeprom_data)))
+               return -EFBIG;
+       if ((offset + count) > sizeof(dgn3500_eeprom_data))
+               count = sizeof(dgn3500_eeprom_data) - offset;
+       if (unlikely(!count))
+               return count;
+       if (count % 2)
+               return 0;
+
+       /* The PCI fixup routine requires an endian swap of the calibartion data
+        * stored in flash */
+       for (i = 0; i < count; i += 2) {
+               eeprom_bytes[offset + i + 1] = buf[i];
+               eeprom_bytes[offset + i] = buf[i+1];
+       }
+
+       /* The original data does not contain a checksum. Set the country and
+        * calculate new checksum when all data is received */
+       if ((count + offset) == sizeof(dgn3500_eeprom_data))
+               memcpy(ath9k_pdata.eeprom_data, dgn3500_eeprom_data,
+                               sizeof(ath9k_pdata.eeprom_data));
+
+       return count;
+}
+
+static struct bin_attribute dev_attr_ath_eeprom = {
+       .attr = {
+               .name = "ath_eeprom",
+               .mode = S_IRUGO|S_IWUSR,
+       },
+       .read = ath_eeprom_read,
+       .write = ath_eeprom_write,
 };
 
 static void __init dgn3500_init(void)
 {
+       if (sysfs_create_bin_file(firmware_kobj, &dev_attr_ath_eeprom))
+               printk(KERN_INFO "Failed to create ath eeprom sysfs entry\n");
+       ltq_add_device_gpio_leds(-1, ARRAY_SIZE(dgn3500_gpio_leds),
+                       dgn3500_gpio_leds);
+       ltq_register_gpio_keys_polled(-1, LTQ_KEYS_POLL_INTERVAL,
+                       ARRAY_SIZE(dgn3500_gpio_keys), dgn3500_gpio_keys);
+       platform_device_register(&spi_gpio_device);
        ltq_register_pci(&ltq_pci_data);
+       spi_register_board_info(&spi_flash, 1);
+       if (!is_valid_ether_addr(ltq_ethaddr)) {
+               printk(KERN_INFO "MAC invalid using random\n");
+               random_ether_addr(ltq_ethaddr);
+       }
+       memcpy(&ltq_eth_data.mac.sa_data, ltq_ethaddr, 6);
        ltq_register_etop(&ltq_eth_data);
-       ltq_register_spi(&ltq_spi_data, &spi_info, 1);
+       ltq_register_ath9k(dgn3500_eeprom_data, ltq_ethaddr);
+       ltq_pci_ath_fixup(14, dgn3500_eeprom_data);
        /* The usb power is always enabled, protected by a fuse */
        xway_register_dwc(-1);
 }