diff options
Diffstat (limited to 'arch/arm/mach-ixp4xx/goramo_mlr.c')
| -rw-r--r-- | arch/arm/mach-ixp4xx/goramo_mlr.c | 507 | 
1 files changed, 507 insertions, 0 deletions
diff --git a/arch/arm/mach-ixp4xx/goramo_mlr.c b/arch/arm/mach-ixp4xx/goramo_mlr.c new file mode 100644 index 00000000000..a733b8ff3ce --- /dev/null +++ b/arch/arm/mach-ixp4xx/goramo_mlr.c @@ -0,0 +1,507 @@ +/* + * Goramo MultiLink router platform code + * Copyright (C) 2006-2009 Krzysztof Halasa <khc@pm.waw.pl> + */ + +#include <linux/delay.h> +#include <linux/hdlc.h> +#include <linux/i2c-gpio.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/serial_8250.h> +#include <asm/mach-types.h> +#include <asm/system.h> +#include <asm/mach/arch.h> +#include <asm/mach/flash.h> +#include <asm/mach/pci.h> + +#define xgpio_irq(n)		(IRQ_IXP4XX_GPIO ## n) +#define gpio_irq(n)		xgpio_irq(n) + +#define SLOT_ETHA		0x0B	/* IDSEL = AD21 */ +#define SLOT_ETHB		0x0C	/* IDSEL = AD20 */ +#define SLOT_MPCI		0x0D	/* IDSEL = AD19 */ +#define SLOT_NEC		0x0E	/* IDSEL = AD18 */ + +#define IRQ_ETHA		IRQ_IXP4XX_GPIO4 +#define IRQ_ETHB		IRQ_IXP4XX_GPIO5 +#define IRQ_NEC			IRQ_IXP4XX_GPIO3 +#define IRQ_MPCI		IRQ_IXP4XX_GPIO12 + +/* GPIO lines */ +#define GPIO_SCL		0 +#define GPIO_SDA		1 +#define GPIO_STR		2 +#define GPIO_HSS0_DCD_N		6 +#define GPIO_HSS1_DCD_N		7 +#define GPIO_HSS0_CTS_N		10 +#define GPIO_HSS1_CTS_N		11 +#define GPIO_HSS1_RTS_N		13 +#define GPIO_HSS0_RTS_N		14 + +/* Control outputs from 74HC4094 */ +#define CONTROL_HSS0_CLK_INT	0 +#define CONTROL_HSS1_CLK_INT	1 +#define CONTROL_HSS0_DTR_N	2 +#define CONTROL_HSS1_DTR_N	3 +#define CONTROL_EXT		4 +#define CONTROL_AUTO_RESET	5 +#define CONTROL_PCI_RESET_N	6 +#define CONTROL_EEPROM_WC_N	7 + +/* offsets from start of flash ROM = 0x50000000 */ +#define CFG_ETH0_ADDRESS	0x40 /* 6 bytes */ +#define CFG_ETH1_ADDRESS	0x46 /* 6 bytes */ +#define CFG_REV			0x4C /* u32 */ +#define CFG_SDRAM_SIZE		0x50 /* u32 */ +#define CFG_SDRAM_CONF		0x54 /* u32 */ +#define CFG_SDRAM_MODE		0x58 /* u32 */ +#define CFG_SDRAM_REFRESH	0x5C /* u32 */ + +#define CFG_HW_BITS		0x60 /* u32 */ +#define  CFG_HW_USB_PORTS	0x00000007 /* 0 = no NEC chip, 1-5 = ports # */ +#define  CFG_HW_HAS_PCI_SLOT	0x00000008 +#define  CFG_HW_HAS_ETH0	0x00000010 +#define  CFG_HW_HAS_ETH1	0x00000020 +#define  CFG_HW_HAS_HSS0	0x00000040 +#define  CFG_HW_HAS_HSS1	0x00000080 +#define  CFG_HW_HAS_UART0	0x00000100 +#define  CFG_HW_HAS_UART1	0x00000200 +#define  CFG_HW_HAS_EEPROM	0x00000400 + +#define FLASH_CMD_READ_ARRAY	0xFF +#define FLASH_CMD_READ_ID	0x90 +#define FLASH_SER_OFF		0x102 /* 0x81 in 16-bit mode */ + +static u32 hw_bits = 0xFFFFFFFD;    /* assume all hardware present */; +static u8 control_value; + +static void set_scl(u8 value) +{ +	gpio_line_set(GPIO_SCL, !!value); +	udelay(3); +} + +static void set_sda(u8 value) +{ +	gpio_line_set(GPIO_SDA, !!value); +	udelay(3); +} + +static void set_str(u8 value) +{ +	gpio_line_set(GPIO_STR, !!value); +	udelay(3); +} + +static inline void set_control(int line, int value) +{ +	if (value) +		control_value |=  (1 << line); +	else +		control_value &= ~(1 << line); +} + + +static void output_control(void) +{ +	int i; + +	gpio_line_config(GPIO_SCL, IXP4XX_GPIO_OUT); +	gpio_line_config(GPIO_SDA, IXP4XX_GPIO_OUT); + +	for (i = 0; i < 8; i++) { +		set_scl(0); +		set_sda(control_value & (0x80 >> i)); /* MSB first */ +		set_scl(1);	/* active edge */ +	} + +	set_str(1); +	set_str(0); + +	set_scl(0); +	set_sda(1);		/* Be ready for START */ +	set_scl(1); +} + + +static void (*set_carrier_cb_tab[2])(void *pdev, int carrier); + +static int hss_set_clock(int port, unsigned int clock_type) +{ +	int ctrl_int = port ? CONTROL_HSS1_CLK_INT : CONTROL_HSS0_CLK_INT; + +	switch (clock_type) { +	case CLOCK_DEFAULT: +	case CLOCK_EXT: +		set_control(ctrl_int, 0); +		output_control(); +		return CLOCK_EXT; + +	case CLOCK_INT: +		set_control(ctrl_int, 1); +		output_control(); +		return CLOCK_INT; + +	default: +		return -EINVAL; +	} +} + +static irqreturn_t hss_dcd_irq(int irq, void *pdev) +{ +	int i, port = (irq == gpio_irq(GPIO_HSS1_DCD_N)); +	gpio_line_get(port ? GPIO_HSS1_DCD_N : GPIO_HSS0_DCD_N, &i); +	set_carrier_cb_tab[port](pdev, !i); +	return IRQ_HANDLED; +} + + +static int hss_open(int port, void *pdev, +		    void (*set_carrier_cb)(void *pdev, int carrier)) +{ +	int i, irq; + +	if (!port) +		irq = gpio_irq(GPIO_HSS0_DCD_N); +	else +		irq = gpio_irq(GPIO_HSS1_DCD_N); + +	gpio_line_get(port ? GPIO_HSS1_DCD_N : GPIO_HSS0_DCD_N, &i); +	set_carrier_cb(pdev, !i); + +	set_carrier_cb_tab[!!port] = set_carrier_cb; + +	if ((i = request_irq(irq, hss_dcd_irq, 0, "IXP4xx HSS", pdev)) != 0) { +		printk(KERN_ERR "ixp4xx_hss: failed to request IRQ%i (%i)\n", +		       irq, i); +		return i; +	} + +	set_control(port ? CONTROL_HSS1_DTR_N : CONTROL_HSS0_DTR_N, 0); +	output_control(); +	gpio_line_set(port ? GPIO_HSS1_RTS_N : GPIO_HSS0_RTS_N, 0); +	return 0; +} + +static void hss_close(int port, void *pdev) +{ +	free_irq(port ? gpio_irq(GPIO_HSS1_DCD_N) : gpio_irq(GPIO_HSS0_DCD_N), +		 pdev); +	set_carrier_cb_tab[!!port] = NULL; /* catch bugs */ + +	set_control(port ? CONTROL_HSS1_DTR_N : CONTROL_HSS0_DTR_N, 1); +	output_control(); +	gpio_line_set(port ? GPIO_HSS1_RTS_N : GPIO_HSS0_RTS_N, 1); +} + + +/* Flash memory */ +static struct flash_platform_data flash_data = { +	.map_name	= "cfi_probe", +	.width		= 2, +}; + +static struct resource flash_resource = { +	.flags		= IORESOURCE_MEM, +}; + +static struct platform_device device_flash = { +	.name		= "IXP4XX-Flash", +	.id		= 0, +	.dev		= { .platform_data = &flash_data }, +	.num_resources	= 1, +	.resource	= &flash_resource, +}; + + +/* I^2C interface */ +static struct i2c_gpio_platform_data i2c_data = { +	.sda_pin	= GPIO_SDA, +	.scl_pin	= GPIO_SCL, +}; + +static struct platform_device device_i2c = { +	.name		= "i2c-gpio", +	.id		= 0, +	.dev		= { .platform_data = &i2c_data }, +}; + + +/* IXP425 2 UART ports */ +static struct resource uart_resources[] = { +	{ +		.start		= IXP4XX_UART1_BASE_PHYS, +		.end		= IXP4XX_UART1_BASE_PHYS + 0x0fff, +		.flags		= IORESOURCE_MEM, +	}, +	{ +		.start		= IXP4XX_UART2_BASE_PHYS, +		.end		= IXP4XX_UART2_BASE_PHYS + 0x0fff, +		.flags		= IORESOURCE_MEM, +	} +}; + +static struct plat_serial8250_port uart_data[] = { +	{ +		.mapbase	= IXP4XX_UART1_BASE_PHYS, +		.membase	= (char __iomem *)IXP4XX_UART1_BASE_VIRT + +			REG_OFFSET, +		.irq		= IRQ_IXP4XX_UART1, +		.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, +		.iotype		= UPIO_MEM, +		.regshift	= 2, +		.uartclk	= IXP4XX_UART_XTAL, +	}, +	{ +		.mapbase	= IXP4XX_UART2_BASE_PHYS, +		.membase	= (char __iomem *)IXP4XX_UART2_BASE_VIRT + +			REG_OFFSET, +		.irq		= IRQ_IXP4XX_UART2, +		.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, +		.iotype		= UPIO_MEM, +		.regshift	= 2, +		.uartclk	= IXP4XX_UART_XTAL, +	}, +	{ }, +}; + +static struct platform_device device_uarts = { +	.name			= "serial8250", +	.id			= PLAT8250_DEV_PLATFORM, +	.dev.platform_data	= uart_data, +	.num_resources		= 2, +	.resource		= uart_resources, +}; + + +/* Built-in 10/100 Ethernet MAC interfaces */ +static struct eth_plat_info eth_plat[] = { +	{ +		.phy		= 0, +		.rxq		= 3, +		.txreadyq	= 32, +	}, { +		.phy		= 1, +		.rxq		= 4, +		.txreadyq	= 33, +	} +}; + +static struct platform_device device_eth_tab[] = { +	{ +		.name			= "ixp4xx_eth", +		.id			= IXP4XX_ETH_NPEB, +		.dev.platform_data	= eth_plat, +	}, { +		.name			= "ixp4xx_eth", +		.id			= IXP4XX_ETH_NPEC, +		.dev.platform_data	= eth_plat + 1, +	} +}; + + +/* IXP425 2 synchronous serial ports */ +static struct hss_plat_info hss_plat[] = { +	{ +		.set_clock	= hss_set_clock, +		.open		= hss_open, +		.close		= hss_close, +		.txreadyq	= 34, +	}, { +		.set_clock	= hss_set_clock, +		.open		= hss_open, +		.close		= hss_close, +		.txreadyq	= 35, +	} +}; + +static struct platform_device device_hss_tab[] = { +	{ +		.name			= "ixp4xx_hss", +		.id			= 0, +		.dev.platform_data	= hss_plat, +	}, { +		.name			= "ixp4xx_hss", +		.id			= 1, +		.dev.platform_data	= hss_plat + 1, +	} +}; + + +static struct platform_device *device_tab[6] __initdata = { +	&device_flash,		/* index 0 */ +}; + +static inline u8 __init flash_readb(u8 __iomem *flash, u32 addr) +{ +#ifdef __ARMEB__ +	return __raw_readb(flash + addr); +#else +	return __raw_readb(flash + (addr ^ 3)); +#endif +} + +static inline u16 __init flash_readw(u8 __iomem *flash, u32 addr) +{ +#ifdef __ARMEB__ +	return __raw_readw(flash + addr); +#else +	return __raw_readw(flash + (addr ^ 2)); +#endif +} + +static void __init gmlr_init(void) +{ +	u8 __iomem *flash; +	int i, devices = 1; /* flash */ + +	ixp4xx_sys_init(); + +	if ((flash = ioremap(IXP4XX_EXP_BUS_BASE_PHYS, 0x80)) == NULL) +		printk(KERN_ERR "goramo-mlr: unable to access system" +		       " configuration data\n"); +	else { +		system_rev = __raw_readl(flash + CFG_REV); +		hw_bits = __raw_readl(flash + CFG_HW_BITS); + +		for (i = 0; i < ETH_ALEN; i++) { +			eth_plat[0].hwaddr[i] = +				flash_readb(flash, CFG_ETH0_ADDRESS + i); +			eth_plat[1].hwaddr[i] = +				flash_readb(flash, CFG_ETH1_ADDRESS + i); +		} + +		__raw_writew(FLASH_CMD_READ_ID, flash); +		system_serial_high = flash_readw(flash, FLASH_SER_OFF); +		system_serial_high <<= 16; +		system_serial_high |= flash_readw(flash, FLASH_SER_OFF + 2); +		system_serial_low = flash_readw(flash, FLASH_SER_OFF + 4); +		system_serial_low <<= 16; +		system_serial_low |= flash_readw(flash, FLASH_SER_OFF + 6); +		__raw_writew(FLASH_CMD_READ_ARRAY, flash); + +		iounmap(flash); +	} + +	switch (hw_bits & (CFG_HW_HAS_UART0 | CFG_HW_HAS_UART1)) { +	case CFG_HW_HAS_UART0: +		memset(&uart_data[1], 0, sizeof(uart_data[1])); +		device_uarts.num_resources = 1; +		break; + +	case CFG_HW_HAS_UART1: +		device_uarts.dev.platform_data = &uart_data[1]; +		device_uarts.resource = &uart_resources[1]; +		device_uarts.num_resources = 1; +		break; +	} +	if (hw_bits & (CFG_HW_HAS_UART0 | CFG_HW_HAS_UART1)) +		device_tab[devices++] = &device_uarts; /* max index 1 */ + +	if (hw_bits & CFG_HW_HAS_ETH0) +		device_tab[devices++] = &device_eth_tab[0]; /* max index 2 */ +	if (hw_bits & CFG_HW_HAS_ETH1) +		device_tab[devices++] = &device_eth_tab[1]; /* max index 3 */ + +	if (hw_bits & CFG_HW_HAS_HSS0) +		device_tab[devices++] = &device_hss_tab[0]; /* max index 4 */ +	if (hw_bits & CFG_HW_HAS_HSS1) +		device_tab[devices++] = &device_hss_tab[1]; /* max index 5 */ + +	if (hw_bits & CFG_HW_HAS_EEPROM) +		device_tab[devices++] = &device_i2c; /* max index 6 */ + +	gpio_line_config(GPIO_SCL, IXP4XX_GPIO_OUT); +	gpio_line_config(GPIO_SDA, IXP4XX_GPIO_OUT); +	gpio_line_config(GPIO_STR, IXP4XX_GPIO_OUT); +	gpio_line_config(GPIO_HSS0_RTS_N, IXP4XX_GPIO_OUT); +	gpio_line_config(GPIO_HSS1_RTS_N, IXP4XX_GPIO_OUT); +	gpio_line_config(GPIO_HSS0_DCD_N, IXP4XX_GPIO_IN); +	gpio_line_config(GPIO_HSS1_DCD_N, IXP4XX_GPIO_IN); +	set_irq_type(gpio_irq(GPIO_HSS0_DCD_N), IRQ_TYPE_EDGE_BOTH); +	set_irq_type(gpio_irq(GPIO_HSS1_DCD_N), IRQ_TYPE_EDGE_BOTH); + +	set_control(CONTROL_HSS0_DTR_N, 1); +	set_control(CONTROL_HSS1_DTR_N, 1); +	set_control(CONTROL_EEPROM_WC_N, 1); +	set_control(CONTROL_PCI_RESET_N, 1); +	output_control(); + +	msleep(1);	      /* Wait for PCI devices to initialize */ + +	flash_resource.start = IXP4XX_EXP_BUS_BASE(0); +	flash_resource.end = IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; + +	platform_add_devices(device_tab, devices); +} + + +#ifdef CONFIG_PCI +static void __init gmlr_pci_preinit(void) +{ +	set_irq_type(IRQ_ETHA, IRQ_TYPE_LEVEL_LOW); +	set_irq_type(IRQ_ETHB, IRQ_TYPE_LEVEL_LOW); +	set_irq_type(IRQ_NEC, IRQ_TYPE_LEVEL_LOW); +	set_irq_type(IRQ_MPCI, IRQ_TYPE_LEVEL_LOW); +	ixp4xx_pci_preinit(); +} + +static void __init gmlr_pci_postinit(void) +{ +	if ((hw_bits & CFG_HW_USB_PORTS) >= 2 && +	    (hw_bits & CFG_HW_USB_PORTS) < 5) { +		/* need to adjust number of USB ports on NEC chip */ +		u32 value, addr = BIT(32 - SLOT_NEC) | 0xE0; +		if (!ixp4xx_pci_read(addr, NP_CMD_CONFIGREAD, &value)) { +			value &= ~7; +			value |= (hw_bits & CFG_HW_USB_PORTS); +			ixp4xx_pci_write(addr, NP_CMD_CONFIGWRITE, value); +		} +	} +} + +static int __init gmlr_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ +	switch(slot) { +	case SLOT_ETHA:	return IRQ_ETHA; +	case SLOT_ETHB:	return IRQ_ETHB; +	case SLOT_NEC:	return IRQ_NEC; +	default:	return IRQ_MPCI; +	} +} + +static struct hw_pci gmlr_hw_pci __initdata = { +	.nr_controllers = 1, +	.preinit	= gmlr_pci_preinit, +	.postinit	= gmlr_pci_postinit, +	.swizzle	= pci_std_swizzle, +	.setup		= ixp4xx_setup, +	.scan		= ixp4xx_scan_bus, +	.map_irq	= gmlr_map_irq, +}; + +static int __init gmlr_pci_init(void) +{ +	if (machine_is_goramo_mlr() && +	    (hw_bits & (CFG_HW_USB_PORTS | CFG_HW_HAS_PCI_SLOT))) +		pci_common_init(&gmlr_hw_pci); +	return 0; +} + +subsys_initcall(gmlr_pci_init); +#endif /* CONFIG_PCI */ + + +MACHINE_START(GORAMO_MLR, "MultiLink") +	/* Maintainer: Krzysztof Halasa */ +	.phys_io	= IXP4XX_PERIPHERAL_BASE_PHYS, +	.io_pg_offst	= ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xFFFC, +	.map_io		= ixp4xx_map_io, +	.init_irq	= ixp4xx_init_irq, +	.timer		= &ixp4xx_timer, +	.boot_params	= 0x0100, +	.init_machine	= gmlr_init, +MACHINE_END  |