dgn3500 support with eeprom loading from sysfs

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
owl
John Crispin 2012-05-27 16:02:22 +00:00
parent 6e8fe84472
commit 81ddc886e1
2 changed files with 203 additions and 33 deletions

View File

@ -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

View 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>
@ -15,13 +16,51 @@
#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[] =
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 = {
{
.desc = "wps",
.type = EV_KEY,
.code = BTN_0,
.debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL,
.gpio = 54,
.active_low = 1,
},
{
.desc = "reset",
.type = EV_KEY,
.code = BTN_1,
.debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL,
.gpio = 36,
.active_low = 1,
},
};
#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_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
};
static u8 ltq_ethaddr[6] = { 0 };
static int __init setup_ethaddr(char *str)
{
{
.name = "uboot",
.offset = 0x0,
.size = 0x40000,
},
{
.name = "uboot_env",
.offset = 0x40000,
.size = 0x40000, /* 2 sectors for redundant env. */
},
{
.name = "linux",
.offset = 0x80000,
.size = 0xF80000, /* map only 16 MiB */
},
};
if (!mac_pton(str, ltq_ethaddr))
memset(ltq_ethaddr, 0, 6);
return 0;
}
__setup("ethaddr=", setup_ethaddr);
static struct flash_platform_data easy98000_spi_flash_platform_data = {
.name = "sflash",
.parts = easy98000_nor_partitions,
.nr_parts = ARRAY_SIZE(easy98000_nor_partitions)
};
static u16 dgn3500_eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS] = {0};
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 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;
struct ltq_spi_platform_data ltq_spi_data = {
.num_chipselect = 4,
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);
}