2008-10-31 10:14:31 -06:00
|
|
|
/* linux/arch/arm/plat-s3c/gpio.c
|
|
|
|
*
|
|
|
|
* Copyright 2008 Simtec Electronics
|
|
|
|
* Ben Dooks <ben@simtec.co.uk>
|
|
|
|
* http://armlinux.simtec.co.uk/
|
|
|
|
*
|
|
|
|
* S3C series GPIO core
|
|
|
|
*
|
|
|
|
* 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/init.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/gpio.h>
|
|
|
|
|
2010-01-19 01:14:46 -07:00
|
|
|
#include <plat/gpio-core.h>
|
2008-10-31 10:14:31 -06:00
|
|
|
|
2008-10-31 10:14:32 -06:00
|
|
|
#ifdef CONFIG_S3C_GPIO_TRACK
|
|
|
|
struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END];
|
|
|
|
|
|
|
|
static __init void s3c_gpiolib_track(struct s3c_gpio_chip *chip)
|
|
|
|
{
|
|
|
|
unsigned int gpn;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
gpn = chip->chip.base;
|
|
|
|
for (i = 0; i < chip->chip.ngpio; i++, gpn++) {
|
2009-09-18 13:44:17 -06:00
|
|
|
BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios));
|
2008-10-31 10:14:32 -06:00
|
|
|
s3c_gpios[gpn] = chip;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_S3C_GPIO_TRACK */
|
|
|
|
|
2008-10-31 10:14:31 -06:00
|
|
|
/* Default routines for controlling GPIO, based on the original S3C24XX
|
|
|
|
* GPIO functions which deal with the case where each gpio bank of the
|
|
|
|
* chip is as following:
|
|
|
|
*
|
|
|
|
* base + 0x00: Control register, 2 bits per gpio
|
|
|
|
* gpio n: 2 bits starting at (2*n)
|
|
|
|
* 00 = input, 01 = output, others mean special-function
|
|
|
|
* base + 0x04: Data register, 1 bit per gpio
|
|
|
|
* bit n: data bit n
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int s3c_gpiolib_input(struct gpio_chip *chip, unsigned offset)
|
|
|
|
{
|
|
|
|
struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
|
|
|
|
void __iomem *base = ourchip->base;
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned long con;
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
|
|
|
|
|
|
|
con = __raw_readl(base + 0x00);
|
|
|
|
con &= ~(3 << (offset * 2));
|
|
|
|
|
|
|
|
__raw_writel(con, base + 0x00);
|
|
|
|
|
|
|
|
local_irq_restore(flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int s3c_gpiolib_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 flags;
|
|
|
|
unsigned long dat;
|
|
|
|
unsigned long con;
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
|
|
|
|
|
|
|
dat = __raw_readl(base + 0x04);
|
|
|
|
dat &= ~(1 << offset);
|
|
|
|
if (value)
|
|
|
|
dat |= 1 << offset;
|
|
|
|
__raw_writel(dat, base + 0x04);
|
|
|
|
|
|
|
|
con = __raw_readl(base + 0x00);
|
|
|
|
con &= ~(3 << (offset * 2));
|
|
|
|
con |= 1 << (offset * 2);
|
|
|
|
|
|
|
|
__raw_writel(con, base + 0x00);
|
|
|
|
__raw_writel(dat, base + 0x04);
|
|
|
|
|
|
|
|
local_irq_restore(flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void s3c_gpiolib_set(struct gpio_chip *chip,
|
|
|
|
unsigned offset, int value)
|
|
|
|
{
|
|
|
|
struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
|
|
|
|
void __iomem *base = ourchip->base;
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned long dat;
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
|
|
|
|
|
|
|
dat = __raw_readl(base + 0x04);
|
|
|
|
dat &= ~(1 << offset);
|
|
|
|
if (value)
|
|
|
|
dat |= 1 << offset;
|
|
|
|
__raw_writel(dat, base + 0x04);
|
|
|
|
|
|
|
|
local_irq_restore(flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int s3c_gpiolib_get(struct gpio_chip *chip, unsigned offset)
|
|
|
|
{
|
|
|
|
struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
|
|
|
|
unsigned long val;
|
|
|
|
|
|
|
|
val = __raw_readl(ourchip->base + 0x04);
|
|
|
|
val >>= offset;
|
|
|
|
val &= 1;
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip)
|
|
|
|
{
|
|
|
|
struct gpio_chip *gc = &chip->chip;
|
2008-10-31 10:14:32 -06:00
|
|
|
int ret;
|
2008-10-31 10:14:31 -06:00
|
|
|
|
|
|
|
BUG_ON(!chip->base);
|
|
|
|
BUG_ON(!gc->label);
|
|
|
|
BUG_ON(!gc->ngpio);
|
|
|
|
|
|
|
|
if (!gc->direction_input)
|
|
|
|
gc->direction_input = s3c_gpiolib_input;
|
|
|
|
if (!gc->direction_output)
|
|
|
|
gc->direction_output = s3c_gpiolib_output;
|
|
|
|
if (!gc->set)
|
|
|
|
gc->set = s3c_gpiolib_set;
|
|
|
|
if (!gc->get)
|
|
|
|
gc->get = s3c_gpiolib_get;
|
|
|
|
|
2008-12-11 17:24:30 -07:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
if (chip->pm != NULL) {
|
|
|
|
if (!chip->pm->save || !chip->pm->resume)
|
|
|
|
printk(KERN_ERR "gpio: %s has missing PM functions\n",
|
|
|
|
gc->label);
|
|
|
|
} else
|
|
|
|
printk(KERN_ERR "gpio: %s has no PM function\n", gc->label);
|
|
|
|
#endif
|
|
|
|
|
2008-10-31 10:14:31 -06:00
|
|
|
/* gpiochip_add() prints own failure message on error. */
|
2008-10-31 10:14:32 -06:00
|
|
|
ret = gpiochip_add(gc);
|
|
|
|
if (ret >= 0)
|
|
|
|
s3c_gpiolib_track(chip);
|
2008-10-31 10:14:31 -06:00
|
|
|
}
|