9
0
Fork 0

Merge branch 'for-next/clps711x'

This commit is contained in:
Sascha Hauer 2013-04-04 12:03:20 +02:00
commit 7845fd7af6
25 changed files with 1005 additions and 150 deletions

View File

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

View File

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

View File

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

View File

@ -14,13 +14,15 @@
#include <mach/clps711x.h>
#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);
}

View File

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

View File

@ -9,103 +9,119 @@
#include <common.h>
#include <init.h>
#include <clock.h>
#include <sizes.h>
#include <asm/io.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <mach/clps711x.h>
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);

View File

@ -14,6 +14,8 @@
#include <asm/io.h>
#include <asm/memory.h>
#include <linux/clk.h>
#include <mach/clps711x.h>
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);

View File

@ -0,0 +1,3 @@
#include <asm-generic/gpio.h>
#define CLPS711X_GPIO(prt,bit) ((prt) * 8 + (bit))

View File

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

View File

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

View File

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

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru>
*
* 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 <common.h>
#include <clock.h>
#include <io.h>
#include <init.h>
#include <linux/clk.h>
#include <linux/err.h>
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);

View File

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

View File

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

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru>
*
* 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 <init.h>
#include <common.h>
#include <malloc.h>
#include <linux/basic_mmio_gpio.h>
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);

428
drivers/gpio/gpio-generic.c Normal file
View File

@ -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 <cbouatmailru@gmail.com>
*
* 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 <init.h>
#include <malloc.h>
#include <linux/log2.h>
#include <linux/basic_mmio_gpio.h>
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 <cbouatmailru@gmail.com>");
MODULE_LICENSE("GPL");

View File

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

View File

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

View File

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

95
drivers/mfd/syscon.c Normal file
View File

@ -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 <dong.aisheng@linaro.org>
*
* 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 <io.h>
#include <init.h>
#include <common.h>
#include <driver.h>
#include <malloc.h>
#include <xfuncs.h>
#include <linux/err.h>
#include <mfd/syscon.h>
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 <dong.aisheng@linaro.org>");
MODULE_DESCRIPTION("System Control driver");
MODULE_LICENSE("GPL v2");

View File

@ -15,18 +15,21 @@
#include <io.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <mfd/syscon.h>
#include <mach/clps711x.h>
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;

View File

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

View File

@ -0,0 +1,69 @@
/*
* Basic memory-mapped GPIO controllers.
*
* Based on linux driver by:
* Copyright 2008 MontaVista Software, Inc.
* Copyright 2008,2010 Anton Vorontsov <cbouatmailru@gmail.com>
*
* 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 <common.h>
#include <gpio.h>
#include <io.h>
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): <register width> * 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 */

View File

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

26
include/mfd/syscon.h Normal file
View File

@ -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 <dong.aisheng@linaro.org>
*
* 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