diff options
Diffstat (limited to 'arch/arm/plat-s3c64xx')
34 files changed, 3704 insertions, 0 deletions
diff --git a/arch/arm/plat-s3c64xx/Kconfig b/arch/arm/plat-s3c64xx/Kconfig new file mode 100644 index 00000000000..54375a00a7d --- /dev/null +++ b/arch/arm/plat-s3c64xx/Kconfig @@ -0,0 +1,62 @@ +# arch/arm/plat-s3c64xx/Kconfig +# +# Copyright 2008 Openmoko, Inc. +# Copyright 2008 Simtec Electronics +# Ben Dooks <ben@simtec.co.uk> +# +# Licensed under GPLv2 + +config PLAT_S3C64XX + bool + depends on ARCH_S3C64XX + default y + select CPU_V6 + select PLAT_S3C + select ARM_VIC + select NO_IOPORT + select ARCH_REQUIRE_GPIOLIB + select S3C_GPIO_TRACK + select S3C_GPIO_PULL_UPDOWN + select S3C_GPIO_CFG_S3C24XX + select S3C_GPIO_CFG_S3C64XX + help + Base platform code for any Samsung S3C64XX device + +if PLAT_S3C64XX + +# Configuration options shared by all S3C64XX implementations + +config CPU_S3C6400_INIT + bool + help + Common initialisation code for the S3C6400 that is shared + by other CPUs in the series, such as the S3C6410. + +config CPU_S3C6400_CLOCK + bool + help + Common clock support code for the S3C6400 that is shared + by other CPUs in the series, such as the S3C6410. + +# platform specific device setup + +config S3C64XX_SETUP_I2C0 + bool + default y + help + Common setup code for i2c bus 0. + + Note, currently since i2c0 is always compiled, this setup helper + is always compiled with it. + +config S3C64XX_SETUP_I2C1 + bool + help + Common setup code for i2c bus 1. + +config S3C64XX_SETUP_FB_24BPP + bool + help + Common setup code for S3C64XX with an 24bpp RGB display helper. + +endif diff --git a/arch/arm/plat-s3c64xx/Makefile b/arch/arm/plat-s3c64xx/Makefile new file mode 100644 index 00000000000..2e6d79bf8f3 --- /dev/null +++ b/arch/arm/plat-s3c64xx/Makefile @@ -0,0 +1,31 @@ +# arch/arm/plat-s3c64xx/Makefile +# +# Copyright 2008 Openmoko, Inc. +# Copyright 2008 Simtec Electronics +# +# Licensed under GPLv2 + +obj-y := +obj-m := +obj-n := dummy.o +obj- := + +# Core files + +obj-y += dev-uart.o +obj-y += cpu.o +obj-y += irq.o +obj-y += irq-eint.o +obj-y += clock.o +obj-y += gpiolib.o + +# CPU support + +obj-$(CONFIG_CPU_S3C6400_INIT) += s3c6400-init.o +obj-$(CONFIG_CPU_S3C6400_CLOCK) += s3c6400-clock.o + +# Device setup + +obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o +obj-$(CONFIG_S3C64XX_SETUP_I2C1) += setup-i2c1.o +obj-$(CONFIG_S3C64XX_SETUP_FB_24BPP) += setup-fb-24bpp.o diff --git a/arch/arm/plat-s3c64xx/clock.c b/arch/arm/plat-s3c64xx/clock.c new file mode 100644 index 00000000000..136c982c68e --- /dev/null +++ b/arch/arm/plat-s3c64xx/clock.c @@ -0,0 +1,281 @@ +/* linux/arch/arm/plat-s3c64xx/clock.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C64XX Base clock support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/io.h> + +#include <mach/hardware.h> +#include <mach/map.h> + +#include <plat/regs-sys.h> +#include <plat/regs-clock.h> +#include <plat/cpu.h> +#include <plat/devs.h> +#include <plat/clock.h> + +struct clk clk_27m = { + .name = "clk_27m", + .id = -1, + .rate = 27000000, +}; + +static int clk_48m_ctrl(struct clk *clk, int enable) +{ + unsigned long flags; + u32 val; + + /* can't rely on clock lock, this register has other usages */ + local_irq_save(flags); + + val = __raw_readl(S3C64XX_OTHERS); + if (enable) + val |= S3C64XX_OTHERS_USBMASK; + else + val &= ~S3C64XX_OTHERS_USBMASK; + + __raw_writel(val, S3C64XX_OTHERS); + local_irq_restore(flags); + + return 0; +} + +struct clk clk_48m = { + .name = "clk_48m", + .id = -1, + .rate = 48000000, + .enable = clk_48m_ctrl, +}; + +static int inline s3c64xx_gate(void __iomem *reg, + struct clk *clk, + int enable) +{ + unsigned int ctrlbit = clk->ctrlbit; + u32 con; + + con = __raw_readl(reg); + + if (enable) + con |= ctrlbit; + else + con &= ~ctrlbit; + + __raw_writel(con, reg); + return 0; +} + +static int s3c64xx_pclk_ctrl(struct clk *clk, int enable) +{ + return s3c64xx_gate(S3C_PCLK_GATE, clk, enable); +} + +static int s3c64xx_hclk_ctrl(struct clk *clk, int enable) +{ + return s3c64xx_gate(S3C_HCLK_GATE, clk, enable); +} + +int s3c64xx_sclk_ctrl(struct clk *clk, int enable) +{ + return s3c64xx_gate(S3C_SCLK_GATE, clk, enable); +} + +static struct clk init_clocks_disable[] = { + { + .name = "nand", + .id = -1, + .parent = &clk_h, + }, { + .name = "adc", + .id = -1, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_TSADC, + }, { + .name = "i2c", + .id = -1, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_IIC, + }, { + .name = "iis", + .id = 0, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_IIS0, + }, { + .name = "iis", + .id = 1, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_IIS1, + }, { + .name = "spi", + .id = 0, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_SPI0, + }, { + .name = "spi", + .id = 1, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_SPI1, + }, { + .name = "48m", + .id = 0, + .parent = &clk_48m, + .enable = s3c64xx_sclk_ctrl, + .ctrlbit = S3C_CLKCON_SCLK_MMC0_48, + }, { + .name = "48m", + .id = 1, + .parent = &clk_48m, + .enable = s3c64xx_sclk_ctrl, + .ctrlbit = S3C_CLKCON_SCLK_MMC1_48, + }, { + .name = "48m", + .id = 2, + .parent = &clk_48m, + .enable = s3c64xx_sclk_ctrl, + .ctrlbit = S3C_CLKCON_SCLK_MMC2_48, + }, +}; + +static struct clk init_clocks[] = { + { + .name = "lcd", + .id = -1, + .parent = &clk_h, + .enable = s3c64xx_hclk_ctrl, + .ctrlbit = S3C_CLKCON_HCLK_LCD, + }, { + .name = "gpio", + .id = -1, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_GPIO, + }, { + .name = "usb-host", + .id = -1, + .parent = &clk_h, + .enable = s3c64xx_hclk_ctrl, + .ctrlbit = S3C_CLKCON_SCLK_UHOST, + }, { + .name = "hsmmc", + .id = 0, + .parent = &clk_h, + .enable = s3c64xx_hclk_ctrl, + .ctrlbit = S3C_CLKCON_HCLK_HSMMC0, + }, { + .name = "hsmmc", + .id = 1, + .parent = &clk_h, + .enable = s3c64xx_hclk_ctrl, + .ctrlbit = S3C_CLKCON_HCLK_HSMMC1, + }, { + .name = "hsmmc", + .id = 2, + .parent = &clk_h, + .enable = s3c64xx_hclk_ctrl, + .ctrlbit = S3C_CLKCON_HCLK_HSMMC2, + }, { + .name = "timers", + .id = -1, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_PWM, + }, { + .name = "uart", + .id = 0, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_UART0, + }, { + .name = "uart", + .id = 1, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_UART1, + }, { + .name = "uart", + .id = 2, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_UART2, + }, { + .name = "uart", + .id = 3, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_UART3, + }, { + .name = "rtc", + .id = -1, + .parent = &clk_p, + .enable = s3c64xx_pclk_ctrl, + .ctrlbit = S3C_CLKCON_PCLK_RTC, + }, { + .name = "watchdog", + .id = -1, + .parent = &clk_p, + .ctrlbit = S3C_CLKCON_PCLK_WDT, + }, { + .name = "ac97", + .id = -1, + .parent = &clk_p, + .ctrlbit = S3C_CLKCON_PCLK_AC97, + } +}; + +static struct clk *clks[] __initdata = { + &clk_ext, + &clk_epll, + &clk_27m, + &clk_48m, +}; + +void s3c64xx_register_clocks(void) +{ + struct clk *clkp; + int ret; + int ptr; + + s3c24xx_register_clocks(clks, ARRAY_SIZE(clks)); + + clkp = init_clocks; + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } + + clkp = init_clocks_disable; + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) { + + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + + (clkp->enable)(clkp, 0); + } + + s3c_pwmclk_init(); +} diff --git a/arch/arm/plat-s3c64xx/cpu.c b/arch/arm/plat-s3c64xx/cpu.c new file mode 100644 index 00000000000..fbde183a456 --- /dev/null +++ b/arch/arm/plat-s3c64xx/cpu.c @@ -0,0 +1,114 @@ +/* linux/arch/arm/plat-s3c64xx/cpu.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C64XX CPU Support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/serial_core.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +#include <mach/hardware.h> +#include <mach/map.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include <plat/regs-serial.h> + +#include <plat/cpu.h> +#include <plat/devs.h> +#include <plat/clock.h> + +#include <plat/s3c6400.h> +#include <plat/s3c6410.h> + +/* table of supported CPUs */ + +static const char name_s3c6400[] = "S3C6400"; +static const char name_s3c6410[] = "S3C6410"; + +static struct cpu_table cpu_ids[] __initdata = { + { + .idcode = 0x36400000, + .idmask = 0xfffff000, + .map_io = s3c6400_map_io, + .init_clocks = s3c6400_init_clocks, + .init_uarts = s3c6400_init_uarts, + .init = s3c6400_init, + .name = name_s3c6400, + }, { + .idcode = 0x36410100, + .idmask = 0xffffff00, + .map_io = s3c6410_map_io, + .init_clocks = s3c6410_init_clocks, + .init_uarts = s3c6410_init_uarts, + .init = s3c6410_init, + .name = name_s3c6410, + }, +}; + +/* minimal IO mapping */ + +/* see notes on uart map in arch/arm/mach-s3c6400/include/mach/debug-macro.S */ +#define UART_OFFS (S3C_PA_UART & 0xfffff) + +static struct map_desc s3c_iodesc[] __initdata = { + { + .virtual = (unsigned long)S3C_VA_SYS, + .pfn = __phys_to_pfn(S3C64XX_PA_SYSCON), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)(S3C_VA_UART + UART_OFFS), + .pfn = __phys_to_pfn(S3C_PA_UART), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C_VA_VIC0, + .pfn = __phys_to_pfn(S3C64XX_PA_VIC0), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C_VA_VIC1, + .pfn = __phys_to_pfn(S3C64XX_PA_VIC1), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C_VA_TIMER, + .pfn = __phys_to_pfn(S3C_PA_TIMER), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C64XX_VA_GPIO, + .pfn = __phys_to_pfn(S3C64XX_PA_GPIO), + .length = SZ_4K, + .type = MT_DEVICE, + }, +}; + +/* read cpu identification code */ + +void __init s3c64xx_init_io(struct map_desc *mach_desc, int size) +{ + unsigned long idcode; + + /* initialise the io descriptors we need for initialisation */ + iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); + iotable_init(mach_desc, size); + + idcode = __raw_readl(S3C_VA_SYS + 0x118); + s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids)); +} diff --git a/arch/arm/plat-s3c64xx/dev-uart.c b/arch/arm/plat-s3c64xx/dev-uart.c new file mode 100644 index 00000000000..62c11a6fc7b --- /dev/null +++ b/arch/arm/plat-s3c64xx/dev-uart.c @@ -0,0 +1,176 @@ +/* linux/arch/arm/plat-s3c64xx/dev-uart.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * Base S3C64XX UART resource and device definitions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/platform_device.h> + +#include <asm/mach/arch.h> +#include <asm/mach/irq.h> +#include <mach/hardware.h> +#include <mach/map.h> + +#include <plat/devs.h> + +/* Serial port registrations */ + +/* 64xx uarts are closer together */ + +static struct resource s3c64xx_uart0_resource[] = { + [0] = { + .start = S3C_PA_UART0, + .end = S3C_PA_UART0 + 0x100, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX0, + .end = IRQ_S3CUART_RX0, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_S3CUART_TX0, + .end = IRQ_S3CUART_TX0, + .flags = IORESOURCE_IRQ, + + }, + [3] = { + .start = IRQ_S3CUART_ERR0, + .end = IRQ_S3CUART_ERR0, + .flags = IORESOURCE_IRQ, + } +}; + +static struct resource s3c64xx_uart1_resource[] = { + [0] = { + .start = S3C_PA_UART1, + .end = S3C_PA_UART1 + 0x100, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX1, + .end = IRQ_S3CUART_RX1, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_S3CUART_TX1, + .end = IRQ_S3CUART_TX1, + .flags = IORESOURCE_IRQ, + + }, + [3] = { + .start = IRQ_S3CUART_ERR1, + .end = IRQ_S3CUART_ERR1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource s3c6xx_uart2_resource[] = { + [0] = { + .start = S3C_PA_UART2, + .end = S3C_PA_UART2 + 0x100, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX2, + .end = IRQ_S3CUART_RX2, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_S3CUART_TX2, + .end = IRQ_S3CUART_TX2, + .flags = IORESOURCE_IRQ, + + }, + [3] = { + .start = IRQ_S3CUART_ERR2, + .end = IRQ_S3CUART_ERR2, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource s3c64xx_uart3_resource[] = { + [0] = { + .start = S3C_PA_UART3, + .end = S3C_PA_UART3 + 0x100, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX3, + .end = IRQ_S3CUART_RX3, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_S3CUART_TX3, + .end = IRQ_S3CUART_TX3, + .flags = IORESOURCE_IRQ, + + }, + [3] = { + .start = IRQ_S3CUART_ERR3, + .end = IRQ_S3CUART_ERR3, + .flags = IORESOURCE_IRQ, + }, +}; + + +struct s3c24xx_uart_resources s3c64xx_uart_resources[] __initdata = { + [0] = { + .resources = s3c64xx_uart0_resource, + .nr_resources = ARRAY_SIZE(s3c64xx_uart0_resource), + }, + [1] = { + .resources = s3c64xx_uart1_resource, + .nr_resources = ARRAY_SIZE(s3c64xx_uart1_resource), + }, + [2] = { + .resources = s3c6xx_uart2_resource, + .nr_resources = ARRAY_SIZE(s3c6xx_uart2_resource), + }, + [3] = { + .resources = s3c64xx_uart3_resource, + .nr_resources = ARRAY_SIZE(s3c64xx_uart3_resource), + }, +}; + +/* uart devices */ + +static struct platform_device s3c24xx_uart_device0 = { + .id = 0, +}; + +static struct platform_device s3c24xx_uart_device1 = { + .id = 1, +}; + +static struct platform_device s3c24xx_uart_device2 = { + .id = 2, +}; + +static struct platform_device s3c24xx_uart_device3 = { + .id = 3, +}; + +struct platform_device *s3c24xx_uart_src[4] = { + &s3c24xx_uart_device0, + &s3c24xx_uart_device1, + &s3c24xx_uart_device2, + &s3c24xx_uart_device3, +}; + +struct platform_device *s3c24xx_uart_devs[4] = { +}; + diff --git a/arch/arm/plat-s3c64xx/gpiolib.c b/arch/arm/plat-s3c64xx/gpiolib.c new file mode 100644 index 00000000000..cc62941d7b5 --- /dev/null +++ b/arch/arm/plat-s3c64xx/gpiolib.c @@ -0,0 +1,420 @@ +/* arch/arm/plat-s3c64xx/gpiolib.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C64XX - GPIOlib support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/irq.h> +#include <linux/io.h> + +#include <mach/map.h> +#include <mach/gpio.h> +#include <mach/gpio-core.h> + +#include <plat/gpio-cfg.h> +#include <plat/gpio-cfg-helpers.h> +#include <plat/regs-gpio.h> + +/* GPIO bank summary: + * + * Bank GPIOs Style SlpCon ExtInt Group + * A 8 4Bit Yes 1 + * B 7 4Bit Yes 1 + * C 8 4Bit Yes 2 + * D 5 4Bit Yes 3 + * E 5 4Bit Yes None + * F 16 2Bit Yes 4 [1] + * G 7 4Bit Yes 5 + * H 10 4Bit[2] Yes 6 + * I 16 2Bit Yes None + * J 12 2Bit Yes None + * K 16 4Bit[2] No None + * L 15 4Bit[2] No None + * M 6 4Bit No IRQ_EINT + * N 16 2Bit No IRQ_EINT + * O 16 2Bit Yes 7 + * P 15 2Bit Yes 8 + * Q 9 2Bit Yes 9 + * + * [1] BANKF pins 14,15 do not form part of the external interrupt sources + * [2] BANK has two control registers, GPxCON0 and GPxCON1 + */ + +#define OFF_GPCON (0x00) +#define OFF_GPDAT (0x04) + +#define con_4bit_shift(__off) ((__off) * 4) + +#if 1 +#define gpio_dbg(x...) do { } while(0) +#else +#define gpio_dbg(x...) printk(KERN_DEBUG ## x) +#endif + +/* The s3c64xx_gpiolib_4bit routines are to control the gpio banks where + * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the + * following example: + * + * base + 0x00: Control register, 4 bits per gpio + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x04: Data register, 1 bit per gpio + * bit n: data bit n + * + * Note, since the data register is one bit per gpio and is at base + 0x4 + * we can use s3c_gpiolib_get and s3c_gpiolib_set to change the state of + * the output. +*/ + +static int s3c64xx_gpiolib_4bit_input(struct gpio_chip *chip, unsigned offset) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long con; + + con = __raw_readl(base + OFF_GPCON); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, base + OFF_GPCON); + + gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con); + + return 0; +} + +static int s3c64xx_gpiolib_4bit_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long con; + unsigned long dat; + + con = __raw_readl(base + OFF_GPCON); + con &= ~(0xf << con_4bit_shift(offset)); + con |= 0x1 << con_4bit_shift(offset); + + dat = __raw_readl(base + OFF_GPDAT); + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(dat, base + OFF_GPDAT); + __raw_writel(con, base + OFF_GPCON); + __raw_writel(dat, base + OFF_GPDAT); + + gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); + + return 0; +} + +/* The next set of routines are for the case where the GPIO configuration + * registers are 4 bits per GPIO but there is more than one register (the + * bank has more than 8 GPIOs. + * + * This case is the similar to the 4 bit case, but the registers are as + * follows: + * + * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs) + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs) + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x08: Data register, 1 bit per gpio + * bit n: data bit n + * + * To allow us to use the s3c_gpiolib_get and s3c_gpiolib_set routines we + * store the 'base + 0x4' address so that these routines see the data + * register at ourchip->base + 0x04. +*/ + +static int s3c64xx_gpiolib_4bit2_input(struct gpio_chip *chip, unsigned offset) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + + if (offset > 7) + offset -= 8; + else + regcon -= 4; + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, regcon); + + gpio_dbg("%s: %p: CON %08lx\n", __func__, base, con); + + return 0; + +} + +static int s3c64xx_gpiolib_4bit2_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + unsigned long dat; + + if (offset > 7) + offset -= 8; + else + regcon -= 4; + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(offset)); + con |= 0x1 << con_4bit_shift(offset); + + dat = __raw_readl(base + OFF_GPDAT); + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(dat, base + OFF_GPDAT); + __raw_writel(con, regcon); + __raw_writel(dat, base + OFF_GPDAT); + + gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); + + return 0; +} + +static struct s3c_gpio_cfg gpio_4bit_cfg_noint = { + .set_config = s3c_gpio_setcfg_s3c64xx_4bit, + .set_pull = s3c_gpio_setpull_updown, + .get_pull = s3c_gpio_getpull_updown, +}; + +static struct s3c_gpio_cfg gpio_4bit_cfg_eint0111 = { + .cfg_eint = 7, + .set_config = s3c_gpio_setcfg_s3c64xx_4bit, + .set_pull = s3c_gpio_setpull_updown, + .get_pull = s3c_gpio_getpull_updown, +}; + +static struct s3c_gpio_cfg gpio_4bit_cfg_eint0011 = { + .cfg_eint = 3, + .set_config = s3c_gpio_setcfg_s3c64xx_4bit, + .set_pull = s3c_gpio_setpull_updown, + .get_pull = s3c_gpio_getpull_updown, +}; + +static struct s3c_gpio_chip gpio_4bit[] = { + { + .base = S3C64XX_GPA_BASE, + .config = &gpio_4bit_cfg_eint0111, + .chip = { + .base = S3C64XX_GPA(0), + .ngpio = S3C64XX_GPIO_A_NR, + .label = "GPA", + }, + }, { + .base = S3C64XX_GPB_BASE, + .config = &gpio_4bit_cfg_eint0111, + .chip = { + .base = S3C64XX_GPB(0), + .ngpio = S3C64XX_GPIO_B_NR, + .label = "GPB", + }, + }, { + .base = S3C64XX_GPC_BASE, + .config = &gpio_4bit_cfg_eint0111, + .chip = { + .base = S3C64XX_GPC(0), + .ngpio = S3C64XX_GPIO_C_NR, + .label = "GPC", + }, + }, { + .base = S3C64XX_GPD_BASE, + .config = &gpio_4bit_cfg_eint0111, + .chip = { + .base = S3C64XX_GPD(0), + .ngpio = S3C64XX_GPIO_D_NR, + .label = "GPD", + }, + }, { + .base = S3C64XX_GPE_BASE, + .config = &gpio_4bit_cfg_noint, + .chip = { + .base = S3C64XX_GPE(0), + .ngpio = S3C64XX_GPIO_E_NR, + .label = "GPE", + }, + }, { + .base = S3C64XX_GPG_BASE, + .config = &gpio_4bit_cfg_eint0111, + .chip = { + .base = S3C64XX_GPG(0), + .ngpio = S3C64XX_GPIO_G_NR, + .label = "GPG", + }, + }, { + .base = S3C64XX_GPM_BASE, + .config = &gpio_4bit_cfg_eint0011, + .chip = { + .base = S3C64XX_GPM(0), + .ngpio = S3C64XX_GPIO_M_NR, + .label = "GPM", + }, + }, +}; + +static struct s3c_gpio_chip gpio_4bit2[] = { + { + .base = S3C64XX_GPH_BASE + 0x4, + .config = &gpio_4bit_cfg_eint0111, + .chip = { + .base = S3C64XX_GPH(0), + .ngpio = S3C64XX_GPIO_H_NR, + .label = "GPH", + }, + }, { + .base = S3C64XX_GPK_BASE + 0x4, + .config = &gpio_4bit_cfg_noint, + .chip = { + .base = S3C64XX_GPK(0), + .ngpio = S3C64XX_GPIO_K_NR, + .label = "GPK", + }, + }, { + .base = S3C64XX_GPL_BASE + 0x4, + .config = &gpio_4bit_cfg_eint0011, + .chip = { |