1驱动文件位置
/drivers/gpio/gpio-ls1x.c
2驱动适配芯片说明
该驱动支持龙芯LS1A,LS1B,LS1C芯片。
3驱动代码分析
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <asm/irq.h>
#include <ls1x_gpio.h>
/*
* ls1x specific GPIO registers
*/
const struct gpio_bank gpio_bank_ls1x[GPIO_BANK_COUNT] = {
{ LS1X_BANK0_BASE, LS1X_PAD_FUNC_MAX0_BASE, LS1X_BANK0_IRQ_START},
{ LS1X_BANK1_BASE, LS1X_PAD_FUNC_MAX1_BASE, LS1X_BANK1_IRQ_START},
// { LS1X_BANK2_BASE, LS1X_PAD_FUNC_MAX2_BASE, LS1X_BANK2_IRQ_START},
// { LS1X_BANK3_BASE, LS1X_PAD_FUNC_MAX3_BASE, LS1X_BANK3_IRQ_START},
};
struct gpio_bank *gpio_bank;
int gpio_2irq(struct gpio_chip *chip, unsigned offset);
int irq_to_gpio(unsigned irq);
inline struct gpio_bank *get_gpio_bank(int gpio)
{
if(PIN_IDX(gpio) > GPIO_BANK_COUNT)
return NULL;
return &gpio_bank_ls1x[PIN_IDX(gpio)];
}
inline int get_gpio_offset(int gpio)
{
return PIN_OFFSET(gpio);
}
static int get_gpio(struct gpio_chip *chip, unsigned int offset)
{
return (irq_to_gpio(gpio_2irq(chip, offset)));
}
/***************************************************************************
* Description:
* Parameters:
* Rutern :
* Author :Sunyoung_yg
* Date : 2015-07-31
***************************************************************************/
inline int gpio_valid(int gpio)
{
if ((gpio < 0) || (gpio> GPIO_MAX)) /*61 is max gpio num*/
return -1;
return 0;
}
int check_gpio(struct gpio_bank *bank, int offset)
{
unsigned int gpio = irq_to_gpio(gpio_2irq(&bank->chip, offset));
if (unlikely(gpio_valid(gpio))) {
printk(KERN_ERR "ls1x-gpio: invalid GPIO %d\n", gpio);
return -1;
}
return 0;
}
/***************************************************************************
* Description: set gpio direction input or output
* Parameters: bank: 抽象的一组32位gpio (同一个寄存器)
* gpio: gpio no. or gpio offset
* Author :Sunyoung_yg
* Date : 2015-02-11
***************************************************************************/
void _set_gpio_direction(struct gpio_bank *bank, int offset, int is_input)
{
u32 reg = bank->pbase+GPIO_REG_DIR;
u32 l;
if (check_gpio(bank, offset) < 0)
return;
if (!(bank->mod_usage & (1<<offset)))
return;
l = __raw_readl(reg);
if (is_input)
l |= PIN_BITSET(offset);
else
l &= ~PIN_BITSET(offset);
__raw_writel(l, reg);
}
/***************************************************************************
* Description: set gpio value
* Parameters: offset: gpio no. or gpio offset; enable: gpio value
* Rutern :void
* Author :Sunyoung_yg
* Date : 2015-02-12
***************************************************************************/
void _set_gpio_dataout(struct gpio_bank *bank, int offset, int enable)
{
u32 reg = bank->pbase+GPIO_REG_OUT;
u32 l = 0;
if (check_gpio(bank, offset) < 0)
return;
if (!(bank->mod_usage & (1<<offset)))
return;
l = __raw_readl(reg);
if(enable)
l |= PIN_BITSET(offset);
else
l &= ~(PIN_BITSET(offset));
__raw_writel(l, reg);
}
/***************************************************************************
* Description: read gpio input level
* Parameters:
* Rutern :
* Author :Sunyoung_yg
* Date : 2015-02-12
***************************************************************************/
int _get_gpio_datain(struct gpio_bank *bank, int offset)
{
u32 reg;
if (check_gpio(bank, offset) < 0)
return -EINVAL;
if (!(bank->mod_usage & (1<<offset)))
return -EINVAL;
reg = bank->pbase + GPIO_REG_IN;
return (__raw_readl(reg) & (1<<offset));
}
/***************************************************************************
* Description: read gpio output level
* Parameters:
* Rutern :
* Author :Sunyoung_yg
* Date : 2015-02-12
***************************************************************************/
int _get_gpio_dataout(struct gpio_bank *bank, int offset)
{
u32 reg;
if (check_gpio(bank, offset) < 0)
return -EINVAL;
if (!(bank->mod_usage & (1<<offset)))
return -EINVAL;
reg = bank->pbase + GPIO_REG_OUT;
return (__raw_readl(reg) & (1<<offset));
}
/*
* Note that ENAWAKEUP needs to be enabled in GPIO_SYSCONFIG register.
* 1510 does not seem to have a wake-up register. If JTAG is connected
* to the target, system will wake up always on GPIO events. While
* system is running all registered GPIO interrupts need to have wake-up
* enabled. When system is suspended, only selected GPIO interrupts need
* to have wake-up enabled.
*/
#if 0
int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
{
}
#endif
/***************************************************************************
* Description: reset gpio,set gpio input, and disable irq, clear irq status.
* Parameters:
* Author :Sunyoung_yg
* Date : 2015-02-11
***************************************************************************/
void _reset_gpio(struct gpio_bank *bank, int gpio)
{
_set_gpio_direction(bank, get_gpio_offset(gpio), GPIO_INPUT);
}
/***************************************************************************
* Description: set gpio function
* Parameters:
* Author :Sunyoung_yg
* Date : 2015-02-11
***************************************************************************/
int set_pad_func(unsigned int gpio, unsigned int type)
{
u32 flags,reg, offset;
struct gpio_bank *bank = get_gpio_bank(gpio);
offset = get_gpio_offset(gpio);
if (check_gpio(bank, offset) < 0)
return -EINVAL;
spin_lock_irqsave(&bank->lock, flags);
switch(type)
{
case PAD_FUNC_DEFAULT:
reg = __raw_readl(bank->pbase);
reg &= ~(1<<offset);
__raw_writel(reg,bank->pbase);
break;
case PAD_FUNC_GPIO:
reg = __raw_readl(bank->pbase);
reg |= (1<<offset);
__raw_writel(reg,bank->pbase);
break;
default:
break;
}
spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
/***************************************************************************
* Description:
* Parameters: chip:抽象出来的一个控制器,实际使同使用一个寄存器的gpio集合
* offset: gpio 在32位寄存器中 偏移量
* Author :Sunyoung_yg
* Date : 2015-02-11
***************************************************************************/
int ls1x_gpio_request(struct gpio_chip *chip, unsigned offset)
{
unsigned long flags;
int ret;
struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
if (check_gpio(bank, offset) < 0)
return -EINVAL;
spin_lock_irqsave(&bank->lock, flags);
/* Set trigger to none. You need to enable the desired trigger with
* request_irq() or set_irq_type().
*/
if (!(bank->mod_usage & (1<<offset))) {
set_pad_func(get_gpio(chip, offset), PAD_FUNC_GPIO);
bank->mod_usage |= 1 << offset;
ret = 0;
}
else
ret = -EINVAL;
spin_unlock_irqrestore(&bank->lock, flags);
return ret;
}
void ls1x_gpio_free(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
unsigned int tmp, flags;
spin_lock_irqsave(&bank->lock, flags);
bank->mod_usage &= ~(1 << offset);
tmp = __raw_readl(bank->pbase + GPIO_REG_CFG);
__raw_writel((tmp & ~(1<<offset)), bank->pbase + GPIO_REG_CFG);
_reset_gpio(bank, bank->chip.base + offset);
spin_unlock_irqrestore(&bank->lock, flags);
}
/*---------------------------------------------------------------------*/
/* REVISIT these are stupid implementations! replace by ones that
* don't switch on METHOD_* and which mostly avoid spinlocks
*/
/***************************************************************************
* Description: set gpio direction is input
* Parameters: chip: offset:
* Author :Sunyoung_yg
* Date : 2015-02-11
***************************************************************************/
int gpio_input(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank;
unsigned long flags;
bank = container_of(chip, struct gpio_bank, chip);
spin_lock_irqsave(&bank->lock, flags);
_set_gpio_direction(bank, offset, GPIO_INPUT);
spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
/***************************************************************************
* Description:
* Parameters:
* Rutern : 1:input ; 0:output
* Author :Sunyoung_yg
* Date : 2015-02-12
***************************************************************************/
int gpio_is_input(struct gpio_bank *bank, int offset)
{
u32 reg = bank->pbase+GPIO_REG_DIR;
if(__raw_readl(reg) & (1<<offset)) /*ls1x gpio: 0 is output; 1 is input*/
return GPIO_INPUT;
else
return GPIO_OUTPUT;
}
/***************************************************************************
* Description: get gpio level
* Parameters:
* Author : Sunyoung_yg
* Date : 2015-02-11
***************************************************************************/
int gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank;
bank = container_of(chip, struct gpio_bank, chip);
if (gpio_is_input(bank, offset))
return _get_gpio_datain(bank, offset);
else
return _get_gpio_dataout(bank, offset);
}
/***************************************************************************
* Description: set gpio direction is output, and set gpio value.
* Parameters: value : default is 0.
* Rutern : 0
* Author :Sunyoung_yg
* Date : 2015-02-12
***************************************************************************/
int gpio_output(struct gpio_chip *chip, unsigned offset, int value)
{
struct gpio_bank *bank;
unsigned long flags;
bank = container_of(chip, struct gpio_bank, chip);
spin_lock_irqsave(&bank->lock, flags);
_set_gpio_dataout(bank, offset, value);
_set_gpio_direction(bank, offset, GPIO_OUTPUT);
spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
/***************************************************************************
* Description:
* Parameters:
* Rutern :
* Author :Sunyoung_yg
* Date : 2015-02-12
***************************************************************************/
void gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct gpio_bank *bank;
unsigned long flags;
bank = container_of(chip, struct gpio_bank, chip);
spin_lock_irqsave(&bank->lock, flags);
_set_gpio_dataout(bank, offset, value);
spin_unlock_irqrestore(&bank->lock, flags);
}
/***************************************************************************
* Description: because in gpiolib.c have the function "gpio_to_irq",and
* it is equlvalent to __gpio_to_irq ,defined in ls1x_gpio.h ,
* it invoke this function(gpio_2irq)finally.
* Notice : ls1x gpio0~95 int reg is int2~int4 regs, but gpio96~104 is in
* int1 register; So gpio0~95 irq number is 64~159, and gpio96~104
* irq number is 54~62. Now we ignore gpio96~104.
* Parameters :
* Rutern :
* Author :Sunyoung_yg
* Date : 2015-07-31
***************************************************************************/
int gpio_2irq(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank;
bank = container_of(chip, struct gpio_bank, chip);
if (offset > 31)
return -EINVAL;
return bank->virtual_irq_start + offset;
}
int irq_to_gpio(unsigned irq)
{
if ( (irq < GPIO_IRQ_START)||(irq > GPIO_IRQ_END ))
return -EINVAL;
return irq - GPIO_IRQ_START;
}
/*---------------------------------------------------------------------*/
int initialized;
/* This lock class tells lockdep that GPIO irqs are in a different
* category than their parents, so it won't report false recursion.
*/
//static struct lock_class_key gpio_lock_class;
int __init _ls1x_gpio_init(void)
{
int i;
int gpio = 0;
struct gpio_bank *bank;
initialized = 1;
gpio_bank = gpio_bank_ls1x;
for (i = 0; i < GPIO_BANK_COUNT; i++) {
int gpio_count = 32;
bank = &gpio_bank[i];
spin_lock_init(&bank->lock);
bank->mod_usage = 0;
/* REVISIT eventually switch from ls1x-specific gpio structs
* over to the generic ones
*/
bank->chip.request = ls1x_gpio_request;
bank->chip.free = ls1x_gpio_free;
bank->chip.direction_input = gpio_input;
bank->chip.get = gpio_get;
bank->chip.direction_output = gpio_output;
bank->chip.set = gpio_set;
bank->chip.to_irq = gpio_2irq;
bank->chip.label = "gpio";
bank->chip.base = gpio;
gpio += gpio_count;
bank->chip.ngpio = gpio_count;
gpiochip_add(&bank->chip);
}
return 0;
}
/*
* This may get called early from board specific init
* for boards that have interrupts routed via FPGA.
*/
int __init ls1x_gpio_init(void)
{
if (!initialized)
return _ls1x_gpio_init();
else
return 0;
}
int __init ls1x_gpio_sysinit(void)
{
int ret = 0;
if (!initialized)
ret = _ls1x_gpio_init();
return ret;
}
arch_initcall(ls1x_gpio_sysinit);
2 comments
评论试试
哈哈哈