diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 7ac134e15..61283221b 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -39,7 +39,11 @@ config ARCH_BCM2835 config ARCH_CLPS711X bool "Cirrus Logic EP711x/EP721x/EP731x" select CLKDEV_LOOKUP + select CLOCKSOURCE_CLPS711X + select COMMON_CLK select CPU_32v4T + select GPIOLIB + select MFD_SYSCON config ARCH_EP93XX bool "Cirrus Logic EP93xx" diff --git a/arch/arm/boards/clep7212/env/config b/arch/arm/boards/clep7212/env/config deleted file mode 100644 index e8f2c3ab1..000000000 --- a/arch/arm/boards/clep7212/env/config +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -global.hostname=clps711x - -# set to false if you do not want to have colors -global.allow_color=true - -# user (used for network filenames) -global.user=anonymous - -# timeout in seconds before the default boot entry is started -global.autoboot_timeout=2 - -# default boot entry (one of /env/boot/*) -if [ -e /dev/nor0 ]; then - global.boot.default=nor -fi - -# default bootargs -global.linux.bootargs.base="earlyprintk console=ttyCL0,57600n8" diff --git a/arch/arm/boards/clep7212/env/config-board b/arch/arm/boards/clep7212/env/config-board new file mode 100644 index 000000000..3cf699aab --- /dev/null +++ b/arch/arm/boards/clep7212/env/config-board @@ -0,0 +1,14 @@ +#!/bin/sh + +global.hostname=clps711x + +# Timeout in seconds before the default boot entry is started +global.autoboot_timeout=2 + +# Default boot entry (one of /env/boot/*) +if [ -e /dev/nor0 ]; then + global.boot.default=nor +fi + +# Board bootargs +global.linux.bootargs.base="earlyprintk console=ttyCL0,57600n8" diff --git a/arch/arm/boards/clep7212/lowlevel.c b/arch/arm/boards/clep7212/lowlevel.c index fcf828500..92fbb9b53 100644 --- a/arch/arm/boards/clep7212/lowlevel.c +++ b/arch/arm/boards/clep7212/lowlevel.c @@ -14,13 +14,15 @@ #include -#if (CONFIG_CLPS711X_CPU_PLL_MULT < 20) || (CONFIG_CLPS711X_CPU_PLL_MULT > 50) -# error "CPU PLL multiplier out of range" +#ifdef CONFIG_CLPS711X_RAISE_CPUFREQ +# define CLPS711X_CPU_PLL_MULT 50 +#else +# define CLPS711X_CPU_PLL_MULT 40 #endif void __naked __bare_init barebox_arm_reset_vector(void) { arm_cpu_lowlevel_init(); - clps711x_barebox_entry(CONFIG_CLPS711X_CPU_PLL_MULT); + clps711x_barebox_entry(CLPS711X_CPU_PLL_MULT); } diff --git a/arch/arm/mach-clps711x/Kconfig b/arch/arm/mach-clps711x/Kconfig index d2873b4c0..b774c540b 100644 --- a/arch/arm/mach-clps711x/Kconfig +++ b/arch/arm/mach-clps711x/Kconfig @@ -12,14 +12,13 @@ endchoice menu "CLPS711X specific settings" -config CLPS711X_CPU_PLL_MULT - int "CPU PLL multiplier (20-50)" - range 20 50 - default "40" +config CLPS711X_RAISE_CPUFREQ + bool "Raise CPU frequency to 90 MHz" + depends on MACH_CLEP7212 help - Define CPU PLL multiplier. PLL is calculated by formula: - PLL Frequency = (PLL Multiplier / 2) * 3686400 Hz - Default value is 40, for achieve 73 MHz. + Raise CPU frequency to 90 MHz. This operation can be performed + only for devices which allow to operate at 90 MHz. + If option is not selected, CPU frequency will set to default 73 MHz. endmenu diff --git a/arch/arm/mach-clps711x/clock.c b/arch/arm/mach-clps711x/clock.c index 09cbaf961..7658c9aa7 100644 --- a/arch/arm/mach-clps711x/clock.c +++ b/arch/arm/mach-clps711x/clock.c @@ -9,103 +9,119 @@ #include #include -#include +#include #include +#include #include #include -static struct clk { - unsigned long rate; -} uart_clk, bus_clk; +#define CLPS711X_OSC_FREQ 3686400 +#define CLPS711X_EXT_FREQ 13000000 -static uint64_t clocksource_read(void) -{ - return ~readw(TC2D); -} - -static struct clocksource cs = { - .read = clocksource_read, - .mask = CLOCKSOURCE_MASK(16), +enum clps711x_clks { + dummy, cpu, bus, uart, timer_hf, timer_lf, tc1, tc2, clk_max }; -unsigned long clk_get_rate(struct clk *clk) -{ - return clk->rate; -} -EXPORT_SYMBOL(clk_get_rate); +static struct { + const char *name; + struct clk *clk; +} clks[clk_max] = { + { "dummy", }, + { "cpu", }, + { "bus", }, + { "uart", }, + { "timer_hf", }, + { "timer_lf", }, + { "tc1", }, + { "tc2", }, +}; -int clk_enable(struct clk *clk) -{ - /* Do nothing */ - return 0; -} -EXPORT_SYMBOL(clk_enable); +static const char *tc_sel_clks[] = { + "timer_lf", + "timer_hf", +}; -void clk_disable(struct clk *clk) +static __init void clps711x_clk_register(enum clps711x_clks id) { - /* Do nothing */ + clk_register_clkdev(clks[id].clk, clks[id].name, NULL); } -EXPORT_SYMBOL(clk_disable); -static int clocks_init(void) +static __init int clps711x_clk_init(void) { - int osc, ext, pll, cpu, timer; + unsigned int f_cpu, f_bus, f_uart, f_timer_hf, f_timer_lf, pll; u32 tmp; - osc = 3686400; - ext = 13000000; - tmp = readl(PLLR) >> 24; if (tmp) - pll = (osc * tmp) / 2; + pll = (CLPS711X_OSC_FREQ * tmp) / 2; else pll = 73728000; /* Default value for old CPUs */ tmp = readl(SYSFLG2); if (tmp & SYSFLG2_CKMODE) { - cpu = ext; - bus_clk.rate = cpu; + f_cpu = CLPS711X_EXT_FREQ; + f_bus = CLPS711X_EXT_FREQ; } else { - cpu = pll; - if (cpu >= 36864000) - bus_clk.rate = cpu / 2; + f_cpu = pll; + if (f_cpu >= 36864000) + f_bus = f_cpu / 2; else - bus_clk.rate = 36864000 / 2; + f_bus = 36864000 / 2; } - uart_clk.rate = bus_clk.rate / 10; + f_uart = f_bus / 10; if (tmp & SYSFLG2_CKMODE) { tmp = readw(SYSCON2); if (tmp & SYSCON2_OSTB) - timer = ext / 26; + f_timer_hf = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 26); else - timer = 541440; + f_timer_hf = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 24); } else - timer = cpu / 144; + f_timer_hf = DIV_ROUND_CLOSEST(f_cpu, 144); + f_timer_lf = DIV_ROUND_CLOSEST(f_timer_hf, 256); + + /* Turn timers in free running mode */ tmp = readl(SYSCON1); - tmp &= ~SYSCON1_TC2M; /* Free running mode */ - tmp |= SYSCON1_TC2S; /* High frequency source */ + tmp &= ~(SYSCON1_TC1M | SYSCON1_TC2M); writel(tmp, SYSCON1); - clocks_calc_mult_shift(&cs.mult, &cs.shift, timer, NSEC_PER_SEC, 10); + clks[dummy].clk = clk_fixed(clks[dummy].name, 0); + clks[cpu].clk = clk_fixed(clks[cpu].name, f_cpu); + clks[bus].clk = clk_fixed(clks[bus].name, f_bus); + clks[uart].clk = clk_fixed(clks[uart].name, f_uart); + clks[timer_hf].clk = clk_fixed(clks[timer_hf].name, f_timer_hf); + clks[timer_lf].clk = clk_fixed(clks[timer_lf].name, f_timer_lf); + clks[tc1].clk = clk_mux(clks[tc1].name, IOMEM(SYSCON1), 5, 1, + tc_sel_clks, ARRAY_SIZE(tc_sel_clks)); + clks[tc2].clk = clk_mux(clks[tc2].name, IOMEM(SYSCON1), 7, 1, + tc_sel_clks, ARRAY_SIZE(tc_sel_clks)); - return init_clock(&cs); -} -core_initcall(clocks_init); - -static struct clk_lookup clocks_lookups[] = { - CLKDEV_CON_ID("bus", &bus_clk), - CLKDEV_DEV_ID("clps711x_serial0", &uart_clk), - CLKDEV_DEV_ID("clps711x_serial1", &uart_clk), -}; - -static int clkdev_init(void) -{ - clkdev_add_table(clocks_lookups, ARRAY_SIZE(clocks_lookups)); + clps711x_clk_register(dummy); + clps711x_clk_register(cpu); + clps711x_clk_register(bus); + clps711x_clk_register(uart); + clps711x_clk_register(timer_hf); + clps711x_clk_register(timer_lf); + clps711x_clk_register(tc1); + clps711x_clk_register(tc2); return 0; } -postcore_initcall(clkdev_init); +postcore_initcall(clps711x_clk_init); + +static const char *clps711x_clocksrc_name = "clps711x-cs"; + +static __init int clps711x_core_init(void) +{ + /* Using TC2 in low frequency mode as clocksource */ + clk_set_parent(clks[tc2].clk, clks[timer_lf].clk); + clk_add_alias(NULL, clps711x_clocksrc_name, "tc2", NULL); + add_generic_device(clps711x_clocksrc_name, DEVICE_ID_SINGLE, NULL, + TC2D, SZ_2, IORESOURCE_MEM, NULL); + + return 0; +} +coredevice_initcall(clps711x_core_init); diff --git a/arch/arm/mach-clps711x/devices.c b/arch/arm/mach-clps711x/devices.c index 6c760db94..9eeff5c52 100644 --- a/arch/arm/mach-clps711x/devices.c +++ b/arch/arm/mach-clps711x/devices.c @@ -14,6 +14,8 @@ #include #include +#include + #include static int clps711x_mem_init(void) @@ -42,68 +44,92 @@ void clps711x_setup_memcfg(int bank, u32 val) case 0 ... 3: _clps711x_setup_memcfg(bank, MEMCFG1, val); break; - case 4 ... 7: + case 4 ... 5: _clps711x_setup_memcfg(bank - 4, MEMCFG2, val); break; } } static struct resource uart0_resources[] = { - { - .start = UBRLCR1, - .end = UBRLCR1, - .flags = IORESOURCE_MEM, - }, - { - .start = SYSCON1, - .end = SYSCON1, - .flags = IORESOURCE_MEM, - }, - { - .start = SYSFLG1, - .end = SYSFLG1, - .flags = IORESOURCE_MEM, - }, - { - .start = UARTDR1, - .end = UARTDR1, - .flags = IORESOURCE_MEM, - }, + DEFINE_RES_MEM(UBRLCR1, SZ_4), + DEFINE_RES_MEM(UARTDR1, SZ_4), }; static struct resource uart1_resources[] = { - { - .start = UBRLCR2, - .end = UBRLCR2, - .flags = IORESOURCE_MEM, - }, - { - .start = SYSCON2, - .end = SYSCON2, - .flags = IORESOURCE_MEM, - }, - { - .start = SYSFLG2, - .end = SYSFLG2, - .flags = IORESOURCE_MEM, - }, - { - .start = UARTDR2, - .end = UARTDR2, - .flags = IORESOURCE_MEM, - }, + DEFINE_RES_MEM(UBRLCR2, SZ_4), + DEFINE_RES_MEM(UARTDR2, SZ_4), }; void clps711x_add_uart(unsigned int id) { switch (id) { case 0: + clk_add_alias(NULL, "clps711x_serial0", "uart", NULL); add_generic_device_res("clps711x_serial", 0, uart0_resources, ARRAY_SIZE(uart0_resources), NULL); break; case 1: + clk_add_alias(NULL, "clps711x_serial1", "uart", NULL); add_generic_device_res("clps711x_serial", 1, uart1_resources, ARRAY_SIZE(uart1_resources), NULL); break; } } + +static struct resource gpio0_resources[] = { + DEFINE_RES_MEM(PADR, SZ_1), + DEFINE_RES_MEM(PADDR, SZ_1), +}; + +static struct resource gpio1_resources[] = { + DEFINE_RES_MEM(PBDR, SZ_1), + DEFINE_RES_MEM(PBDDR, SZ_1), +}; + +static struct resource gpio2_resources[] = { + DEFINE_RES_MEM(PCDR, SZ_1), + DEFINE_RES_MEM(PCDDR, SZ_1), +}; + +static struct resource gpio3_resources[] = { + DEFINE_RES_MEM(PDDR, SZ_1), + DEFINE_RES_MEM(PDDDR, SZ_1), +}; + +static struct resource gpio4_resources[] = { + DEFINE_RES_MEM(PEDR, SZ_1), + DEFINE_RES_MEM(PEDDR, SZ_1), +}; + +static __init int clps711x_gpio_init(void) +{ + add_generic_device_res("clps711x-gpio", 0, gpio0_resources, + ARRAY_SIZE(gpio0_resources), NULL); + add_generic_device_res("clps711x-gpio", 1, gpio1_resources, + ARRAY_SIZE(gpio1_resources), NULL); + add_generic_device_res("clps711x-gpio", 2, gpio2_resources, + ARRAY_SIZE(gpio2_resources), NULL); + add_generic_device_res("clps711x-gpio", 3, gpio3_resources, + ARRAY_SIZE(gpio3_resources), NULL); + add_generic_device_res("clps711x-gpio", 4, gpio4_resources, + ARRAY_SIZE(gpio4_resources), NULL); + + return 0; +} +coredevice_initcall(clps711x_gpio_init); + +static __init int clps711x_syscon_init(void) +{ + /* SYSCON1, SYSFLG1 */ + add_generic_device("clps711x-syscon", 1, NULL, SYSCON1, SZ_128, + IORESOURCE_MEM, NULL); + /* SYSCON2, SYSFLG2 */ + add_generic_device("clps711x-syscon", 2, NULL, SYSCON2, SZ_128, + IORESOURCE_MEM, NULL); + /* SYSCON3 */ + add_generic_device("clps711x-syscon", 3, NULL, SYSCON3, SZ_64, + IORESOURCE_MEM, NULL); + + return 0; +} +postcore_initcall(clps711x_syscon_init); diff --git a/arch/arm/mach-clps711x/include/mach/gpio.h b/arch/arm/mach-clps711x/include/mach/gpio.h new file mode 100644 index 000000000..3428fe54b --- /dev/null +++ b/arch/arm/mach-clps711x/include/mach/gpio.h @@ -0,0 +1,3 @@ +#include + +#define CLPS711X_GPIO(prt,bit) ((prt) * 8 + (bit)) diff --git a/arch/arm/mach-clps711x/lowlevel.c b/arch/arm/mach-clps711x/lowlevel.c index 193f61aa6..58306f29f 100644 --- a/arch/arm/mach-clps711x/lowlevel.c +++ b/arch/arm/mach-clps711x/lowlevel.c @@ -21,12 +21,12 @@ void __naked __bare_init clps711x_barebox_entry(u32 pllmult) { u32 cpu, bus; - /* Setup base clocking, Enable SDQM pins */ - writel(SYSCON3_CLKCTL0 | SYSCON3_CLKCTL1, SYSCON3); - asm("nop"); - /* Check if we running from external 13 MHz clock */ if (!(readl(SYSFLG2) & SYSFLG2_CKMODE)) { + /* Setup bus wait state scaling factor to 2 */ + writel(SYSCON3_CLKCTL0 | SYSCON3_CLKCTL1, SYSCON3); + asm("nop"); + /* Check valid multiplier, default to 74 MHz */ if ((pllmult < 20) || (pllmult > 50)) pllmult = 40; @@ -42,11 +42,15 @@ void __naked __bare_init clps711x_barebox_entry(u32 pllmult) cpu = pllmult * 3686400; if (cpu >= 36864000) - bus = cpu /2; + bus = cpu / 2; else bus = 36864000 / 2; - } else + } else { bus = 13000000; + /* Setup bus wait state scaling factor to 1 */ + writel(0, SYSCON3); + asm("nop"); + } /* CLKEN select, SDRAM width=32 */ writel(SYSCON2_CLKENSL, SYSCON2); diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 3f27cf243..9f3558b6e 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -10,6 +10,10 @@ config CLOCKSOURCE_BCM2835 bool depends on ARCH_BCM2835 +config CLOCKSOURCE_CLPS711X + bool + depends on ARCH_CLPS711X + config CLOCKSOURCE_NOMADIK bool depends on ARM diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index b0bc8bd7d..d919881fb 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_AMBA_SP804) += amba-sp804.o obj-$(CONFIG_ARM_SMP_TWD) += arm_smp_twd.o obj-$(CONFIG_CLOCKSOURCE_BCM2835) += bcm2835.o +obj-$(CONFIG_CLOCKSOURCE_CLPS711X) += clps711x.o obj-$(CONFIG_CLOCKSOURCE_NOMADIK) += nomadik.o diff --git a/drivers/clocksource/clps711x.c b/drivers/clocksource/clps711x.c new file mode 100644 index 000000000..8c379d39e --- /dev/null +++ b/drivers/clocksource/clps711x.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Alexander Shiyan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include + +static __iomem void *clps711x_timer_base; + +static uint64_t clps711x_cs_read(void) +{ + return ~readw(clps711x_timer_base); +} + +static struct clocksource clps711x_cs = { + .read = clps711x_cs_read, + .mask = CLOCKSOURCE_MASK(16), +}; + +static int clps711x_cs_probe(struct device_d *dev) +{ + u32 rate; + struct clk *timer_clk; + + timer_clk = clk_get(dev, NULL); + if (IS_ERR(timer_clk)) + return PTR_ERR(timer_clk); + + rate = clk_get_rate(timer_clk); + clps711x_timer_base = dev_request_mem_region(dev, 0); + if (!clps711x_timer_base) { + clk_put(timer_clk); + return -ENOENT; + } + + clocks_calc_mult_shift(&clps711x_cs.mult, &clps711x_cs.shift, rate, + NSEC_PER_SEC, 10); + + return init_clock(&clps711x_cs); +} + +static struct driver_d clps711x_cs_driver = { + .name = "clps711x-cs", + .probe = clps711x_cs_probe, +}; + +static __init int clps711x_cs_init(void) +{ + return platform_driver_register(&clps711x_cs_driver); +} +coredevice_initcall(clps711x_cs_init); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5500ad112..ea07028db 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -6,10 +6,27 @@ if GPIOLIB menu "GPIO" +config GPIO_GENERIC + bool + config GPIO_BCM2835 bool "GPIO support for BCM2835" depends on ARCH_BCM2835 +config GPIO_CLPS711X + bool "GPIO support for CLPS711X" + depends on ARCH_CLPS711X + select GPIO_GENERIC + help + Say yes here to enable the GPIO driver for the CLPS711X CPUs + +config GPIO_GENERIC_PLATFORM + bool "Generic memory-mapped GPIO controller support" + select GPIO_GENERIC + help + Say yes here to support basic platform memory-mapped + GPIO controllers + config GPIO_PL061 bool "PrimeCell PL061 GPIO support" depends on ARM_AMBA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 993ab8959..00acb68b3 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -1,4 +1,6 @@ -obj-$(CONFIG_GPIOLIB) += gpio.o +obj-$(CONFIG_GPIOLIB) += gpio.o obj-$(CONFIG_GPIO_BCM2835) += gpio-bcm2835.o +obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o +obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o -obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o +obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c new file mode 100644 index 000000000..feead5152 --- /dev/null +++ b/drivers/gpio/gpio-clps711x.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013 Alexander Shiyan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include +#include +#include + +#include + +static int clps711x_gpio_probe(struct device_d *dev) +{ + int err; + void __iomem *dat, *dir = NULL, *dir_inv = NULL; + struct bgpio_chip *bgc; + + if ((dev->id < 0) || (dev->id > 4)) + return -ENODEV; + + dat = dev_request_mem_region(dev, 0); + switch (dev->id) { + case 3: + dir_inv = dev_request_mem_region(dev, 1); + break; + default: + dir = dev_request_mem_region(dev, 1); + break; + } + + if (!dat || (!dir && !dir_inv)) + return -EINVAL; + + bgc = xzalloc(sizeof(struct bgpio_chip)); + if (!bgc) + return -ENOMEM; + + err = bgpio_init(bgc, dev, 1, dat, NULL, NULL, dir, dir_inv, 0); + if (err) { + free(bgc); + return err; + } + + bgc->gc.base = dev->id * 8; + switch (dev->id) { + case 4: + bgc->gc.ngpio = 3; + break; + default: + bgc->gc.ngpio = 8; + break; + } + + return gpiochip_add(&bgc->gc); +} + +static struct driver_d clps711x_gpio_driver = { + .name = "clps711x-gpio", + .probe = clps711x_gpio_probe, +}; + +static __init int clps711x_gpio_register(void) +{ + return platform_driver_register(&clps711x_gpio_driver); +} +coredevice_initcall(clps711x_gpio_register); diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c new file mode 100644 index 000000000..a2fc40043 --- /dev/null +++ b/drivers/gpio/gpio-generic.c @@ -0,0 +1,428 @@ +/* + * Generic driver for memory-mapped GPIO controllers. + * + * Based on linux driver by: + * Copyright 2008 MontaVista Software, Inc. + * Copyright 2008,2010 Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +static void bgpio_write8(void __iomem *reg, unsigned int data) +{ + writeb(data, reg); +} + +static unsigned int bgpio_read8(void __iomem *reg) +{ + return readb(reg); +} + +static void bgpio_write16(void __iomem *reg, unsigned int data) +{ + writew(data, reg); +} + +static unsigned int bgpio_read16(void __iomem *reg) +{ + return readw(reg); +} + +static void bgpio_write32(void __iomem *reg, unsigned int data) +{ + writel(data, reg); +} + +static unsigned int bgpio_read32(void __iomem *reg) +{ + return readl(reg); +} + +static unsigned int bgpio_pin2mask(struct bgpio_chip *bgc, unsigned int pin) +{ + return 1 << pin; +} + +static unsigned int bgpio_pin2mask_be(struct bgpio_chip *bgc, unsigned int pin) +{ + return 1 << (bgc->bits - 1 - pin); +} + +static int bgpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + + return bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio); +} + +static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned int mask = bgc->pin2mask(bgc, gpio); + + if (val) + bgc->data |= mask; + else + bgc->data &= ~mask; + + bgc->write_reg(bgc->reg_dat, bgc->data); +} + +static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, + int val) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned int mask = bgc->pin2mask(bgc, gpio); + + if (val) + bgc->write_reg(bgc->reg_set, mask); + else + bgc->write_reg(bgc->reg_clr, mask); +} + +static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned int mask = bgc->pin2mask(bgc, gpio); + + if (val) + bgc->data |= mask; + else + bgc->data &= ~mask; + + bgc->write_reg(bgc->reg_set, bgc->data); +} + +static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + return 0; +} + +static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, + int val) +{ + gc->ops->set(gc, gpio, val); + + return 0; +} + +static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + + bgc->dir &= ~bgc->pin2mask(bgc, gpio); + bgc->write_reg(bgc->reg_dir, bgc->dir); + + return 0; +} + +static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + + gc->ops->set(gc, gpio, val); + + bgc->dir |= bgc->pin2mask(bgc, gpio); + bgc->write_reg(bgc->reg_dir, bgc->dir); + + return 0; +} + +static int bgpio_dir_in_inv(struct gpio_chip *gc, unsigned int gpio) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + + bgc->dir |= bgc->pin2mask(bgc, gpio); + bgc->write_reg(bgc->reg_dir, bgc->dir); + + return 0; +} + +static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + + gc->ops->set(gc, gpio, val); + + bgc->dir &= ~bgc->pin2mask(bgc, gpio); + bgc->write_reg(bgc->reg_dir, bgc->dir); + + return 0; +} + +static int bgpio_setup_accessors(struct device_d *dev, struct bgpio_chip *bgc, + bool be) +{ + switch (bgc->bits) { + case 8: + bgc->read_reg = bgpio_read8; + bgc->write_reg = bgpio_write8; + break; + case 16: + bgc->read_reg = bgpio_read16; + bgc->write_reg = bgpio_write16; + break; + case 32: + bgc->read_reg = bgpio_read32; + bgc->write_reg = bgpio_write32; + break; + default: + dev_err(dev, "Unsupported data width %u bits\n", bgc->bits); + return -EINVAL; + } + + bgc->pin2mask = be ? bgpio_pin2mask_be : bgpio_pin2mask; + + return 0; +} + +/* + * Create the device and allocate the resources. For setting GPIO's there are + * three supported configurations: + * + * - single input/output register resource (named "dat"). + * - set/clear pair (named "set" and "clr"). + * - single output register resource and single input resource ("set" and + * dat"). + * + * For the single output register, this drives a 1 by setting a bit and a zero + * by clearing a bit. For the set clr pair, this drives a 1 by setting a bit + * in the set register and clears it by setting a bit in the clear register. + * The configuration is detected by which resources are present. + * + * For setting the GPIO direction, there are three supported configurations: + * + * - simple bidirection GPIO that requires no configuration. + * - an output direction register (named "dirout") where a 1 bit + * indicates the GPIO is an output. + * - an input direction register (named "dirin") where a 1 bit indicates + * the GPIO is an input. + */ +static int bgpio_setup_io(struct bgpio_chip *bgc, + void __iomem *dat, + void __iomem *set, + void __iomem *clr) +{ + if (!dat) + return -EINVAL; + + bgc->reg_dat = dat; + + if (set && clr) { + bgc->reg_set = set; + bgc->reg_clr = clr; + bgc->gc.ops->set = bgpio_set_with_clear; + } else if (set && !clr) { + bgc->reg_set = set; + bgc->gc.ops->set = bgpio_set_set; + } else + bgc->gc.ops->set = bgpio_set; + + bgc->gc.ops->get = bgpio_get; + + return 0; +} + +static int bgpio_setup_direction(struct bgpio_chip *bgc, + void __iomem *dirout, + void __iomem *dirin) +{ + if (dirout && dirin) + return -EINVAL; + + if (dirout) { + bgc->reg_dir = dirout; + bgc->gc.ops->direction_output = bgpio_dir_out; + bgc->gc.ops->direction_input = bgpio_dir_in; + } else if (dirin) { + bgc->reg_dir = dirin; + bgc->gc.ops->direction_output = bgpio_dir_out_inv; + bgc->gc.ops->direction_input = bgpio_dir_in_inv; + } else { + bgc->gc.ops->direction_output = bgpio_simple_dir_out; + bgc->gc.ops->direction_input = bgpio_simple_dir_in; + } + + return 0; +} + +int bgpio_init(struct bgpio_chip *bgc, struct device_d *dev, + unsigned int sz, void __iomem *dat, void __iomem *set, + void __iomem *clr, void __iomem *dirout, void __iomem *dirin, + unsigned long flags) +{ + int ret; + + if ((sz > 4) || !is_power_of_2(sz)) + return -EINVAL; + + bgc->bits = sz * 8; + bgc->gc.ngpio = bgc->bits; + bgc->gc.base = -1; + bgc->gc.dev = dev; + bgc->gc.ops = &bgc->ops; + + ret = bgpio_setup_io(bgc, dat, set, clr); + if (ret) + return ret; + + ret = bgpio_setup_accessors(dev, bgc, flags & BGPIOF_BIG_ENDIAN); + if (ret) + return ret; + + ret = bgpio_setup_direction(bgc, dirout, dirin); + if (ret) + return ret; + + bgc->data = bgc->read_reg(bgc->reg_dat); + + if (bgc->gc.ops->set == bgpio_set_set && !(flags & + BGPIOF_UNREADABLE_REG_SET)) + bgc->data = bgc->read_reg(bgc->reg_set); + + if (bgc->reg_dir && !(flags & BGPIOF_UNREADABLE_REG_DIR)) + bgc->dir = bgc->read_reg(bgc->reg_dir); + + return ret; +} + +void bgpio_remove(struct bgpio_chip *bgc) +{ + gpiochip_remove(&bgc->gc); + free(bgc); +} + +#ifdef CONFIG_GPIO_GENERIC_PLATFORM + +static void __iomem *bgpio_map(struct device_d *dev, const char *name, + resource_size_t sane_sz, int *err) +{ + struct resource *r; + void __iomem *ret; + + *err = 0; + + r = dev_get_resource_by_name(dev, name); + if (!r) + return NULL; + + if (resource_size(r) != sane_sz) { + *err = -EINVAL; + return NULL; + } + + ret = request_iomem_region(dev_name(dev), r->start, r->end); + if (!ret) { + *err = -ENOMEM; + return NULL; + } + + return ret; +} + +static int bgpio_dev_probe(struct device_d *dev) +{ + struct resource *r; + void __iomem *dat; + void __iomem *set; + void __iomem *clr; + void __iomem *dirout; + void __iomem *dirin; + unsigned int sz; + unsigned long flags = 0; + int err; + struct bgpio_chip *bgc; + struct bgpio_pdata *pdata = dev->platform_data; + + r = dev_get_resource_by_name(dev, "dat"); + if (!r) + return -EINVAL; + + sz = resource_size(r); + + dat = bgpio_map(dev, "dat", sz, &err); + if (!dat) + return err ? err : -EINVAL; + + set = bgpio_map(dev, "set", sz, &err); + if (err) + return err; + + clr = bgpio_map(dev, "clr", sz, &err); + if (err) + return err; + + dirout = bgpio_map(dev, "dirout", sz, &err); + if (err) + return err; + + dirin = bgpio_map(dev, "dirin", sz, &err); + if (err) + return err; + + dev_get_drvdata(dev, &flags); + + bgc = xzalloc(sizeof(struct bgpio_chip)); + if (!bgc) + return -ENOMEM; + + err = bgpio_init(bgc, dev, sz, dat, set, clr, dirout, dirin, flags); + if (err) + return err; + + if (pdata) { + bgc->gc.base = pdata->base; + if (pdata->ngpio > 0) + bgc->gc.ngpio = pdata->ngpio; + } + + dev->priv = bgc; + + return gpiochip_add(&bgc->gc); +} + +static void bgpio_dev_remove(struct device_d *dev) +{ + struct bgpio_chip *bgc = dev->priv; + + bgpio_remove(bgc); +} + +static struct platform_device_id bgpio_id_table[] = { + { + .name = "basic-mmio-gpio", + .driver_data = 0, + }, + { + .name = "basic-mmio-gpio-be", + .driver_data = BGPIOF_BIG_ENDIAN, + }, + { } +}; + +static struct driver_d bgpio_driver = { + .name = "basic-mmio-gpio", + .id_table = bgpio_id_table, + .probe = bgpio_dev_probe, + .remove = bgpio_dev_remove, +}; + +static int bgpio_register(void) +{ + return platform_driver_register(&bgpio_driver); +} +coredevice_initcall(bgpio_register); + +#endif + +MODULE_DESCRIPTION("Driver for basic memory-mapped GPIO controllers"); +MODULE_AUTHOR("Anton Vorontsov "); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio.c b/drivers/gpio/gpio.c index d37f5a0db..6d29224f9 100644 --- a/drivers/gpio/gpio.c +++ b/drivers/gpio/gpio.c @@ -189,6 +189,11 @@ int gpiochip_add(struct gpio_chip *chip) return 0; } +void gpiochip_remove(struct gpio_chip *chip) +{ + list_del(&chip->list); +} + int gpio_get_num(struct device_d *dev, int gpio) { struct gpio_chip *chip; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c506d6782..afb87dbd2 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -24,6 +24,11 @@ config MFD_STMPE depends on I2C bool "STMPE-i2c driver" +config MFD_SYSCON + bool "System Controller Register" + help + Select this option to enable accessing system control registers + config MFD_TWLCORE bool diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 542fb0fb3..1afd52bd3 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_MFD_MC34704) += mc34704.o obj-$(CONFIG_MFD_MC34708) += mc34708.o obj-$(CONFIG_MFD_MC9SDZ60) += mc9sdz60.o obj-$(CONFIG_MFD_STMPE) += stmpe-i2c.o +obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_TWLCORE) += twl-core.o obj-$(CONFIG_MFD_TWL4030) += twl4030.o obj-$(CONFIG_MFD_TWL6030) += twl6030.o diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c new file mode 100644 index 000000000..b628ab7c6 --- /dev/null +++ b/drivers/mfd/syscon.c @@ -0,0 +1,95 @@ +/* System Control Driver + * + * Based on linux driver by: + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012 Linaro Ltd. + * Author: Dong Aisheng + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include + +struct syscon { + void __iomem *base; +}; + +void __iomem *syscon_base_lookup_by_pdevname(const char *s) +{ + struct syscon *syscon; + struct device_d *dev; + + for_each_device(dev) { + if (!strcmp(dev_name(dev), s)) { + syscon = dev->priv; + return syscon->base; + } + } + + return ERR_PTR(-ENODEV); +} + +static int syscon_probe(struct device_d *dev) +{ + struct syscon *syscon; + struct resource *res; + + syscon = xzalloc(sizeof(struct syscon)); + if (!syscon) + return -ENOMEM; + + res = dev_get_resource(dev, 0); + if (!res) { + free(syscon); + return -ENOENT; + } + + res = request_iomem_region(dev_name(dev), res->start, res->end); + if (!res) { + free(syscon); + return -EBUSY; + } + + syscon->base = (void __iomem *)res->start; + dev->priv = syscon; + + dev_info(dev, "map 0x%x-0x%x registered\n", res->start, res->end); + + return 0; +} + +static struct platform_device_id syscon_ids[] = { + { "syscon", }, +#ifdef CONFIG_ARCH_CLPS711X + { "clps711x-syscon", }, +#endif + { } +}; + +static struct driver_d syscon_driver = { + .name = "syscon", + .probe = syscon_probe, + .id_table = syscon_ids, +}; + +static int __init syscon_init(void) +{ + return platform_driver_register(&syscon_driver); +} +core_initcall(syscon_init); + +MODULE_AUTHOR("Dong Aisheng "); +MODULE_DESCRIPTION("System Control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/serial/serial_clps711x.c b/drivers/serial/serial_clps711x.c index 21d0b55d3..cb250beb6 100644 --- a/drivers/serial/serial_clps711x.c +++ b/drivers/serial/serial_clps711x.c @@ -15,18 +15,21 @@ #include #include #include +#include #include struct clps711x_uart { void __iomem *UBRLCR; - void __iomem *SYSCON; - void __iomem *SYSFLG; void __iomem *UARTDR; + void __iomem *syscon; struct clk *uart_clk; struct console_device cdev; }; +#define SYSCON(x) ((x)->syscon + 0x00) +#define SYSFLG(x) ((x)->syscon + 0x40) + static int clps711x_setbaudrate(struct console_device *cdev, int baudrate) { struct clps711x_uart *s = cdev->dev->priv; @@ -48,7 +51,7 @@ static void clps711x_init_port(struct console_device *cdev) u32 tmp; /* Disable the UART */ - writel(readl(s->SYSCON) & ~SYSCON_UARTEN, s->SYSCON); + writel(readl(SYSCON(s)) & ~SYSCON_UARTEN, SYSCON(s)); /* Setup Line Control Register */ tmp = readl(s->UBRLCR) & UBRLCR_BAUD_MASK; @@ -59,7 +62,7 @@ static void clps711x_init_port(struct console_device *cdev) clps711x_setbaudrate(cdev, CONFIG_BAUDRATE); /* Enable the UART */ - writel(readl(s->SYSCON) | SYSCON_UARTEN, s->SYSCON); + writel(readl(SYSCON(s)) | SYSCON_UARTEN, SYSCON(s)); } static void clps711x_putc(struct console_device *cdev, char c) @@ -67,7 +70,7 @@ static void clps711x_putc(struct console_device *cdev, char c) struct clps711x_uart *s = cdev->dev->priv; /* Wait until there is space in the FIFO */ - while (readl(s->SYSFLG) & SYSFLG_UTXFF) + while (readl(SYSFLG(s)) & SYSFLG_UTXFF) barrier(); /* Send the character */ @@ -80,7 +83,7 @@ static int clps711x_getc(struct console_device *cdev) u16 data; /* Wait until there is data in the FIFO */ - while (readl(s->SYSFLG) & SYSFLG_URXFE) + while (readl(SYSFLG(s)) & SYSFLG_URXFE) barrier(); data = readw(s->UARTDR); @@ -96,31 +99,35 @@ static int clps711x_tstc(struct console_device *cdev) { struct clps711x_uart *s = cdev->dev->priv; - return !(readl(s->SYSFLG) & SYSFLG_URXFE); + return !(readl(SYSFLG(s)) & SYSFLG_URXFE); } static void clps711x_flush(struct console_device *cdev) { struct clps711x_uart *s = cdev->dev->priv; - while (readl(s->SYSFLG) & SYSFLG_UBUSY) + while (readl(SYSFLG(s)) & SYSFLG_UBUSY) barrier(); } static int clps711x_probe(struct device_d *dev) { struct clps711x_uart *s; + char syscon_dev[18]; - BUG_ON(dev->num_resources != 4); + BUG_ON(dev->num_resources != 2); + BUG_ON((dev->id != 0) && (dev->id != 1)); s = xzalloc(sizeof(struct clps711x_uart)); s->uart_clk = clk_get(dev, NULL); BUG_ON(IS_ERR(s->uart_clk)); s->UBRLCR = dev_get_mem_region(dev, 0); - s->SYSCON = dev_get_mem_region(dev, 1); - s->SYSFLG = dev_get_mem_region(dev, 2); - s->UARTDR = dev_get_mem_region(dev, 3); + s->UARTDR = dev_get_mem_region(dev, 1); + + sprintf(syscon_dev, "clps711x-syscon%i", dev->id + 1); + s->syscon = syscon_base_lookup_by_pdevname(syscon_dev); + BUG_ON(IS_ERR(s->syscon)); dev->priv = s; s->cdev.dev = dev; diff --git a/include/gpio.h b/include/gpio.h index eedb980d0..c2fb9f7a9 100644 --- a/include/gpio.h +++ b/include/gpio.h @@ -40,6 +40,7 @@ struct gpio_chip { }; int gpiochip_add(struct gpio_chip *chip); +void gpiochip_remove(struct gpio_chip *chip); int gpio_get_num(struct device_d *dev, int gpio); diff --git a/include/linux/basic_mmio_gpio.h b/include/linux/basic_mmio_gpio.h new file mode 100644 index 000000000..e927194b5 --- /dev/null +++ b/include/linux/basic_mmio_gpio.h @@ -0,0 +1,69 @@ +/* + * Basic memory-mapped GPIO controllers. + * + * Based on linux driver by: + * Copyright 2008 MontaVista Software, Inc. + * Copyright 2008,2010 Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __BASIC_MMIO_GPIO_H +#define __BASIC_MMIO_GPIO_H + +#include +#include +#include + +struct bgpio_pdata { + int base; + int ngpio; +}; + +struct bgpio_chip { + struct gpio_chip gc; + struct gpio_ops ops; + + unsigned int (*read_reg)(void __iomem *reg); + void (*write_reg)(void __iomem *reg, unsigned int data); + + void __iomem *reg_dat; + void __iomem *reg_set; + void __iomem *reg_clr; + void __iomem *reg_dir; + + /* Number of bits (GPIOs): * 8. */ + int bits; + + /* + * Some GPIO controllers work with the big-endian bits notation, + * e.g. in a 8-bits register, GPIO7 is the least significant bit. + */ + unsigned int (*pin2mask)(struct bgpio_chip *bgc, unsigned int pin); + + /* Shadowed data register to clear/set bits safely. */ + unsigned int data; + + /* Shadowed direction registers to clear/set direction safely. */ + unsigned int dir; +}; + +static inline struct bgpio_chip *to_bgpio_chip(struct gpio_chip *gc) +{ + return container_of(gc, struct bgpio_chip, gc); +} + +int bgpio_init(struct bgpio_chip *bgc, struct device_d *dev, + unsigned int sz, void __iomem *dat, void __iomem *set, + void __iomem *clr, void __iomem *dirout, void __iomem *dirin, + unsigned long flags); +void bgpio_remove(struct bgpio_chip *bgc); + +#define BGPIOF_BIG_ENDIAN BIT(0) +#define BGPIOF_UNREADABLE_REG_SET BIT(1) /* reg_set is unreadable */ +#define BGPIOF_UNREADABLE_REG_DIR BIT(2) /* reg_dir is unreadable */ + +#endif /* __BASIC_MMIO_GPIO_H */ diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 6d6cd68de..ff0cba070 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -111,10 +111,25 @@ struct resource { /* PCI control bits. Shares IORESOURCE_BITS with above PCI ROM. */ #define IORESOURCE_PCI_FIXED (1<<4) /* Do not move resource */ +/* Helpers to define resources */ +#define DEFINE_RES_NAMED(_start, _size, _name, _flags) \ + { \ + .start = (_start), \ + .end = (_start) + (_size) - 1, \ + .name = (_name), \ + .flags = (_flags), \ + } + +#define DEFINE_RES_MEM_NAMED(_start, _size, _name) \ + DEFINE_RES_NAMED((_start), (_size), (_name), IORESOURCE_MEM) +#define DEFINE_RES_MEM(_start, _size) \ + DEFINE_RES_MEM_NAMED((_start), (_size), NULL) + static inline resource_size_t resource_size(const struct resource *res) { return res->end - res->start + 1; } + static inline unsigned long resource_type(const struct resource *res) { return res->flags & IORESOURCE_TYPE_BITS; diff --git a/include/mfd/syscon.h b/include/mfd/syscon.h new file mode 100644 index 000000000..68432b7fe --- /dev/null +++ b/include/mfd/syscon.h @@ -0,0 +1,26 @@ +/* System Control Driver + * + * Based on linux driver by: + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012 Linaro Ltd. + * Author: Dong Aisheng + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __MFD_SYSCON_H__ +#define __MFD_SYSCON_H__ + +#ifdef CONFIG_MFD_SYSCON +void __iomem *syscon_base_lookup_by_pdevname(const char *); +#else +static inline void __iomem *syscon_base_lookup_by_pdevname(const char *) +{ + return NULL; +} +#endif + +#endif