kernel-ark/arch/arm/mach-davinci/gpio-tnetv107x.c
Hirosh Dabui c284d9fa48 davinci: tnetv107x: fix register indexing for GPIOs numbers > 31
This patch fix a bug in the register indexing for GPIOs numbers >  31
to get the relevant hardware registers of tnetv107x to control the GPIOs.

In the structure tnetv107x_gpio_regs:

struct tnetv107x_gpio_regs {
            u32     idver;
            u32     data_in[3];
            u32     data_out[3];
            u32     direction[3];
            u32     enable[3];
};

The GPIO hardware register addresses of tnetv107x are stored.
The chip implements 3 registers of each entity to serve 96 GPIOs,
each register provides a subset of 32 GPIOs.
The driver provides these macros: gpio_reg_set_bit, gpio_reg_get_bit
and gpio_reg_clear_bit.

The bug implied the use of macros to access the relevant hardware
register e.g. the driver code used the macro like this:
'gpio_reg_clear_bit(&reg->data_out, gpio)'

But it has to be used like this:
'gpio_reg_clear_bit(reg->data_out, gpio)'.

The different results are shown here:
- &reg->data_out + 1 (it will add the full array size of data_out i.e. 12 bytes)
- reg->data_out + 1 (it will increment only the size of data_out i.e. only 4 bytes)

Acked-by: Cyril Chemparathy <cyril@ti.com>
Signed-off-by: Hirosh Dabui <hirosh.dabui@snom.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
2011-02-28 14:53:19 -08:00

206 lines
5.3 KiB
C

/*
* Texas Instruments TNETV107X GPIO Controller
*
* Copyright (C) 2010 Texas Instruments
*
* 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 version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <mach/common.h>
#include <mach/tnetv107x.h>
struct tnetv107x_gpio_regs {
u32 idver;
u32 data_in[3];
u32 data_out[3];
u32 direction[3];
u32 enable[3];
};
#define gpio_reg_index(gpio) ((gpio) >> 5)
#define gpio_reg_bit(gpio) BIT((gpio) & 0x1f)
#define gpio_reg_rmw(reg, mask, val) \
__raw_writel((__raw_readl(reg) & ~(mask)) | (val), (reg))
#define gpio_reg_set_bit(reg, gpio) \
gpio_reg_rmw((reg) + gpio_reg_index(gpio), 0, gpio_reg_bit(gpio))
#define gpio_reg_clear_bit(reg, gpio) \
gpio_reg_rmw((reg) + gpio_reg_index(gpio), gpio_reg_bit(gpio), 0)
#define gpio_reg_get_bit(reg, gpio) \
(__raw_readl((reg) + gpio_reg_index(gpio)) & gpio_reg_bit(gpio))
#define chip2controller(chip) \
container_of(chip, struct davinci_gpio_controller, chip)
#define TNETV107X_GPIO_CTLRS DIV_ROUND_UP(TNETV107X_N_GPIO, 32)
static struct davinci_gpio_controller chips[TNETV107X_GPIO_CTLRS];
static int tnetv107x_gpio_request(struct gpio_chip *chip, unsigned offset)
{
struct davinci_gpio_controller *ctlr = chip2controller(chip);
struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
unsigned gpio = chip->base + offset;
unsigned long flags;
spin_lock_irqsave(&ctlr->lock, flags);
gpio_reg_set_bit(regs->enable, gpio);
spin_unlock_irqrestore(&ctlr->lock, flags);
return 0;
}
static void tnetv107x_gpio_free(struct gpio_chip *chip, unsigned offset)
{
struct davinci_gpio_controller *ctlr = chip2controller(chip);
struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
unsigned gpio = chip->base + offset;
unsigned long flags;
spin_lock_irqsave(&ctlr->lock, flags);
gpio_reg_clear_bit(regs->enable, gpio);
spin_unlock_irqrestore(&ctlr->lock, flags);
}
static int tnetv107x_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
{
struct davinci_gpio_controller *ctlr = chip2controller(chip);
struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
unsigned gpio = chip->base + offset;
unsigned long flags;
spin_lock_irqsave(&ctlr->lock, flags);
gpio_reg_set_bit(regs->direction, gpio);
spin_unlock_irqrestore(&ctlr->lock, flags);
return 0;
}
static int tnetv107x_gpio_dir_out(struct gpio_chip *chip,
unsigned offset, int value)
{
struct davinci_gpio_controller *ctlr = chip2controller(chip);
struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
unsigned gpio = chip->base + offset;
unsigned long flags;
spin_lock_irqsave(&ctlr->lock, flags);
if (value)
gpio_reg_set_bit(regs->data_out, gpio);
else
gpio_reg_clear_bit(regs->data_out, gpio);
gpio_reg_clear_bit(regs->direction, gpio);
spin_unlock_irqrestore(&ctlr->lock, flags);
return 0;
}
static int tnetv107x_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct davinci_gpio_controller *ctlr = chip2controller(chip);
struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
unsigned gpio = chip->base + offset;
int ret;
ret = gpio_reg_get_bit(regs->data_in, gpio);
return ret ? 1 : 0;
}
static void tnetv107x_gpio_set(struct gpio_chip *chip,
unsigned offset, int value)
{
struct davinci_gpio_controller *ctlr = chip2controller(chip);
struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs;
unsigned gpio = chip->base + offset;
unsigned long flags;
spin_lock_irqsave(&ctlr->lock, flags);
if (value)
gpio_reg_set_bit(regs->data_out, gpio);
else
gpio_reg_clear_bit(regs->data_out, gpio);
spin_unlock_irqrestore(&ctlr->lock, flags);
}
static int __init tnetv107x_gpio_setup(void)
{
int i, base;
unsigned ngpio;
struct davinci_soc_info *soc_info = &davinci_soc_info;
struct tnetv107x_gpio_regs *regs;
struct davinci_gpio_controller *ctlr;
if (soc_info->gpio_type != GPIO_TYPE_TNETV107X)
return 0;
ngpio = soc_info->gpio_num;
if (ngpio == 0) {
pr_err("GPIO setup: how many GPIOs?\n");
return -EINVAL;
}
if (WARN_ON(TNETV107X_N_GPIO < ngpio))
ngpio = TNETV107X_N_GPIO;
regs = ioremap(soc_info->gpio_base, SZ_4K);
if (WARN_ON(!regs))
return -EINVAL;
for (i = 0, base = 0; base < ngpio; i++, base += 32) {
ctlr = &chips[i];
ctlr->chip.label = "tnetv107x";
ctlr->chip.can_sleep = 0;
ctlr->chip.base = base;
ctlr->chip.ngpio = ngpio - base;
if (ctlr->chip.ngpio > 32)
ctlr->chip.ngpio = 32;
ctlr->chip.request = tnetv107x_gpio_request;
ctlr->chip.free = tnetv107x_gpio_free;
ctlr->chip.direction_input = tnetv107x_gpio_dir_in;
ctlr->chip.get = tnetv107x_gpio_get;
ctlr->chip.direction_output = tnetv107x_gpio_dir_out;
ctlr->chip.set = tnetv107x_gpio_set;
spin_lock_init(&ctlr->lock);
ctlr->regs = regs;
ctlr->set_data = &regs->data_out[i];
ctlr->clr_data = &regs->data_out[i];
ctlr->in_data = &regs->data_in[i];
gpiochip_add(&ctlr->chip);
}
soc_info->gpio_ctlrs = chips;
soc_info->gpio_ctlrs_num = DIV_ROUND_UP(ngpio, 32);
return 0;
}
pure_initcall(tnetv107x_gpio_setup);