From c524b9193a75a79167a77217664f0062a2d2e2bf Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E8=B0=A2=E4=BF=AE=E9=91=AB?= Date: Tue, 25 May 2010 15:17:32 +0000 Subject: [PATCH] add pca9554.c --- drivers/gpio/pca9554.c | 970 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 970 insertions(+) create mode 100644 drivers/gpio/pca9554.c diff --git a/drivers/gpio/pca9554.c b/drivers/gpio/pca9554.c new file mode 100644 index 000000000000..e6813e2a003f --- /dev/null +++ b/drivers/gpio/pca9554.c @@ -0,0 +1,970 @@ +/* arch/arm/mach-rk2818/example.c + * + * Copyright (C) 2010 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/*******************************************************************/ +/* COPYRIGHT (C) ROCK-CHIPS FUZHOU . ALL RIGHTS RESERVED. */ +/******************************************************************* +FILE : PCA9554.C +DESC : À©Õ¹GPIO µÄÇý¶¯Ïà¹Ø³ÌÐò +AUTHOR : ZHONGYW +DATE : 2009-4-26 +NOTES : +$LOG: GPIO.C,V $ +REVISION 0.01 +********************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define PCA9554_GPIO_IRQNum 8 +#define PCA9554_PortNum 1 +#define PCA9554_PortPinNum 8 +#define PCA9554_TotalPortPinNum (PCA9554_PortNum*PCA9554_PortPinNum) + +#define PCA9554_InputLevel_Reg 0x0 //r only +#define PCA9554_OutputLevel_Reg 0x1 // r/w default ffff +#define PCA9554_Invert_Reg 0x2 // r/w default 0 +#define PCA9554_Config_Reg 0x3 // r/w default ffff + +//#define PCA9554_INPUT_REG_IRQ_PIN RK2818_PIN_PE2 + +#define PCA9554_OUTREGLOCK +#define PCA9554_INPUTREGLOCK +#define PCA9554_CONFIGREGLOCK + +struct pca9554_chip { + /* the first extern gpio number in all of gpio groups */ + unsigned gpio_start; + unsigned gpio_pin_num; + /* the first gpio irq number in all of irq source */ + + unsigned gpio_irq_start; + unsigned irq_pin_num; //ÖжϵĸöÊý + unsigned irq_gpiopin; //¸¸ÖжϵÄÖжϺŠ+ unsigned irq_chain; //¸¸ÖжϵÄÖжϺŠ+ uint8_t reg_input[PCA9554_PortNum]; + uint8_t reg_output[PCA9554_PortNum]; + uint8_t reg_direction[PCA9554_PortNum]; + uint8_t interrupt_en[PCA9554_PortNum];// 0 dis + uint8_t interrupt_mask[PCA9554_PortNum];// 0 unmask + uint8_t inttype_set[PCA9554_PortNum]; // Inttype enable + uint8_t inttype[PCA9554_PortNum]; // + uint8_t inttype1[PCA9554_PortNum]; + + +#ifdef PCA9554_OUTREGLOCK + struct mutex outreglock; +#endif +#ifdef PCA9554_INPUTREGLOCK + struct mutex inputreglock; +#endif +#ifdef PCA9554_CONFIGREGLOCK + struct mutex configreglock; +#endif + + struct i2c_client *client; + struct pca9554_platform_data *dyn_pdata; + struct work_struct pca9554_work; + + struct gpio_chip gpio_chip; + + char **names; +}; + + +#if 1 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif + +#if 1 +#define DBGERR(x...) printk(KERN_INFO x) +#else +#define DBGERR(x...) +#endif + + +struct i2c_client *pca9554_client; +#define pca9554getbit(a,num) (((a)>>(num))&0x01) +#define pca9554setbit(a,num) ((a)|(0x01<<(num))) +#define pca9554clearbit(a,num) ((a)&(~(0x01<<(num)))) + +static short int portnum[PCA9554_PortNum]={PCA9554_PortPinNum}; +extern inline struct gpio_chip *gpio_to_chip(unsigned gpio); +extern struct lock_class_key gpio_lock_class; +extern struct lock_class_key gpio_lock_class; +struct workqueue_struct *pca9554workqueue; + +static const struct i2c_device_id pca9554_id[] = +{ + { "extend_gpio_pca9554", 8, }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, pca9554_id); + + +static int pca9554_write_reg(struct i2c_client *client, uint8_t reg, uint8_t val) +{ + int ret=-1; + + struct i2c_adapter *adap; + struct i2c_msg msg; + char tx_buf[2]; + if(!client) + return ret; + adap = client->adapter; + tx_buf[0] = reg; + tx_buf[1]=val; + + msg.addr = client->addr; + msg.buf = &tx_buf[0]; + msg.len = 1 +1; + msg.flags = client->flags; + msg.scl_rate = 200*1000; + + ret = i2c_transfer(adap, &msg, 1); + return ret; +} + +static int pca9554_read_reg(struct i2c_client *client, uint8_t reg, uint8_t *val) +{ + + int ret; + struct i2c_adapter *adap; + struct i2c_msg msgs[2]; + if(!client) + return ret; + adap = client->adapter; + //·¢ËͼĴæÆ÷µØÖ· + msgs[0].addr = client->addr; + msgs[0].buf = ® + msgs[0].flags = client->flags; + msgs[0].len = 1; + msgs[0].scl_rate = 200*1000; + //½ÓÊÕÊý¾Ý + msgs[1].buf = val; + msgs[1].addr = client->addr; + msgs[1].flags = client->flags | I2C_M_RD; + msgs[1].len = 1; + msgs[1].scl_rate = 200*1000; + + ret = i2c_transfer(adap, msgs, 2); + //DBG("**has run at %s %d ret=%d**\n",__FUNCTION__,__LINE__,ret); + + return ret; +} + +static int pca9554_gpio_direction_input(struct gpio_chip *gc, unsigned off) +{ + struct pca9554_chip *chip; + + uint8_t gpioPortNum; + uint8_t gpioPortPinNum; + uint8_t reg_val; + int ret=-1; + DBG("**run in the %s**\n",__FUNCTION__); + chip = container_of(gc, struct pca9554_chip, gpio_chip); + + gpioPortNum = off/8; + gpioPortPinNum= off%8; + if((gpioPortNum>=PCA9554_PortNum)||(gpioPortPinNum>=portnum[gpioPortNum])) + return ret; + +#ifdef PCA9554_CONFIGREGLOCK + if (!mutex_trylock(&chip->configreglock)) + { + DBGERR("**%s[%d]Did not get the configreglock**\n",__FUNCTION__,__LINE__); + return ret; + } +#endif + + reg_val = pca9554setbit(chip->reg_direction[gpioPortNum], gpioPortPinNum); + ret = pca9554_write_reg(chip->client, PCA9554_Config_Reg, reg_val); + if(ret<0) + goto err; + + chip->reg_direction[gpioPortNum] = reg_val; +err: + + DBG("**%s[%d],reg_val=%x,ret=%d**\n",__FUNCTION__,__LINE__,reg_val,ret); + + #ifdef PCA9554_CONFIGREGLOCK + mutex_unlock(&chip->configreglock); + #endif + + return (ret<0)?-1:0; +} + +static int pca9554_gpio_direction_output(struct gpio_chip *gc,unsigned off, int val) +{ + struct pca9554_chip *chip; + uint8_t gpioPortNum; + uint8_t gpioPortPinNum; + uint8_t reg_val; + int ret=-1; + DBG("**run in the %s**\n",__FUNCTION__); + + chip = container_of(gc, struct pca9554_chip, gpio_chip); + + gpioPortNum = off/8; + gpioPortPinNum= off%8; + if((gpioPortNum>=PCA9554_PortNum)||(gpioPortPinNum>=portnum[gpioPortNum])) + return ret; + + #ifdef PCA9554_CONFIGREGLOCK + if (!mutex_trylock(&chip->configreglock)) + { + DBGERR("**%s[%d]Did not get the configreglock**\n",__FUNCTION__,__LINE__); + return -1; + } + #endif + /* then direction */ + reg_val = pca9554clearbit(chip->reg_direction[gpioPortNum], gpioPortPinNum); + ret = pca9554_write_reg(chip->client, PCA9554_Config_Reg, reg_val); + if(ret<0) + { + #ifdef PCA9554_CONFIGREGLOCK + mutex_unlock(&chip->configreglock); + #endif + DBGERR("**%s[%d] set direction reg is error,reg_val=%x,ret=%d**\n",__FUNCTION__,__LINE__,reg_val,ret); + return ret; + } + chip->reg_direction[gpioPortNum] = reg_val; +#ifdef PCA9554_CONFIGREGLOCK + mutex_unlock(&chip->configreglock); +#endif + ret=-1; + #ifdef PCA9554_OUTREGLOCK + if (!mutex_trylock(&chip->outreglock)) + { + DBGERR("**%s[%d] Did not get the outreglock**\n",__FUNCTION__,__LINE__); + return ret; + } + #endif + /* set output level */ + if (val) + reg_val = pca9554setbit(chip->reg_output[gpioPortNum], gpioPortPinNum); + else + reg_val = pca9554clearbit(chip->reg_output[gpioPortNum], gpioPortPinNum); + + ret = pca9554_write_reg(chip->client, PCA9554_OutputLevel_Reg, reg_val); + if (ret<0) + { + #ifdef PCA9554_OUTREGLOCK + mutex_unlock(&chip->outreglock); + #endif + DBGERR("**%s[%d] set out reg is error,reg_val=%x,ret=%d**\n",__FUNCTION__,__LINE__,reg_val,ret); + return ret; + } + chip->reg_output[gpioPortNum] = reg_val; + + #ifdef PCA9554_OUTREGLOCK + mutex_unlock(&chip->outreglock); + #endif + return (ret<0)?-1:0; +} + +static int pca9554_gpio_get_value(struct gpio_chip *gc, unsigned off) +{ + struct pca9554_chip *chip; + uint8_t gpioPortNum; + uint8_t gpioPortPinNum; + uint8_t reg_val; + int ret=-1; + DBG("**run in the %s**\n",__FUNCTION__); + + chip = container_of(gc, struct pca9554_chip, gpio_chip); + gpioPortNum = off/8; + gpioPortPinNum= off%8; + + if((gpioPortNum>=PCA9554_PortNum)||(gpioPortPinNum>=portnum[gpioPortNum])) + return -1; + + if(!pca9554getbit(chip->reg_direction[gpioPortNum],gpioPortPinNum)) //ÅжϸÃpinÊÇ·ñÉèÖóÉinput + return -1; +#ifdef PCA9554_INPUTREGLOCK + if (!mutex_trylock(&chip->inputreglock)) + { + DBGERR("**%s[%d]Did not get the inputreglock**\n",__FUNCTION__,__LINE__); + return -1; + } +#endif + + ret = pca9554_read_reg(chip->client, PCA9554_InputLevel_Reg, ®_val); + if (ret < 0) + goto err; + chip->reg_input[gpioPortNum] = reg_val; +err: +#ifdef PCA9554_CONFIGREGLOCK + mutex_unlock(&chip->inputreglock); +#endif + DBGERR("**%s[%d]reg_val=%x,ret=%d**\n",__FUNCTION__,__LINE__,reg_val,ret); + + return (ret < 0)?-1:((chip->reg_input[gpioPortNum] >> gpioPortPinNum) & 0x01); +} + +static void pca9554_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +{ + struct pca9554_chip *chip; + uint8_t gpioPortNum; + uint8_t gpioPortPinNum; + uint8_t reg_val; + int ret=-1; + DBG("**run in the %s**\n",__FUNCTION__); + + chip = container_of(gc, struct pca9554_chip, gpio_chip); + + gpioPortNum = off/8; + gpioPortPinNum= off%8; + + if((gpioPortNum>=PCA9554_PortNum)||(gpioPortPinNum>=portnum[gpioPortNum])) + return;// -1; + + if(pca9554getbit(chip->reg_direction[gpioPortNum],gpioPortPinNum)) // input state + return;// -1; + +#ifdef PCA9554_OUTREGLOCK + if (!mutex_trylock(&chip->outreglock)) + { + DBGERR("**%s[%d] Did not get the outreglock**\n",__FUNCTION__,__LINE__); + return;// -1; + } +#endif + if (val) + reg_val = pca9554setbit(chip->reg_output[gpioPortNum], gpioPortPinNum); + else + reg_val = pca9554clearbit(chip->reg_output[gpioPortNum], gpioPortPinNum); + + ret = pca9554_write_reg(chip->client, PCA9554_OutputLevel_Reg, reg_val); + if (ret<0) + goto err; + chip->reg_output[gpioPortNum] = reg_val; +err: + #ifdef PCA9554_OUTREGLOCK + mutex_unlock(&chip->outreglock); + #endif + + DBG("**%s[%d],reg_val=%x,ret=%d**\n",__FUNCTION__,__LINE__,reg_val,ret); + + return;// (ret<0)?-1:0; + +} +static int pca9554_gpio_to_irq(struct gpio_chip *chip,unsigned offset) +{ + struct pca9554_chip *pca_chip = container_of(chip, struct pca9554_chip, gpio_chip); + if(((pca_chip->gpio_start+offset)>=chip->base)&&((pca_chip->gpio_start+offset)<(chip->base+chip->ngpio))) + { + //DBG("**%s,offset=%d,gpio_irq_start=%d,base=%d,ngpio=%d,gpio_irq_start=%d**\n", + // __FUNCTION__,offset,pca_chip->gpio_irq_start,chip->base,chip->ngpio,pca_chip->gpio_irq_start); + return (offset+pca_chip->gpio_irq_start); + } + else + return -1; +} +static void pca9554_setup_gpio(struct pca9554_chip *chip, int gpios) +{ + struct gpio_chip *gc; + + gc = &chip->gpio_chip; + + gc->direction_input = pca9554_gpio_direction_input; + gc->direction_output = pca9554_gpio_direction_output; + gc->get = pca9554_gpio_get_value; + gc->set = pca9554_gpio_set_value; + gc->to_irq =pca9554_gpio_to_irq; + + gc->can_sleep = 1; + + gc->base = chip->gpio_start; + gc->ngpio = chip->gpio_pin_num; + gc->label = chip->client->name; + gc->dev = &chip->client->dev; + gc->owner = THIS_MODULE; + gc->names = chip->names; +} + +int pca9554_checkrange(int start,int num,int val) +{ + + if((val<(start+num))&&(val>=start)) + return 0; + else return -1; + +} +static void pca9554_gpio_irq_enable(unsigned irq) +{ + struct irq_desc *desc = irq_to_desc(irq); + //int gpiopinnum; + struct pca9554_chip *pchip=(struct pca9554_chip *)desc->chip_data; + //struct gpio_chip *chip_gpio; + unsigned gpio_num; + + uint8_t gpioPortNum; + uint8_t gpioPortPinNum; + uint8_t pca9554pinnum; + + + if(!pca9554_checkrange(pchip->gpio_irq_start,pchip->irq_pin_num,irq)) + { + pca9554pinnum=irq-pchip->gpio_irq_start; + } + else return; + gpioPortNum = pca9554pinnum/8; + gpioPortPinNum= pca9554pinnum%8; + gpio_num=pchip->gpio_start+pca9554pinnum; + if((gpioPortNum>=PCA9554_PortNum)||(gpioPortPinNum>=portnum[gpioPortNum])) + return; + DBG("**%s**\n",__FUNCTION__); + pchip->interrupt_en[gpioPortNum]=pca9554setbit(pchip->interrupt_en[gpioPortNum],gpioPortPinNum); +} +static void pca9554_gpio_irq_disable(unsigned irq) +{ + struct irq_desc *desc = irq_to_desc(irq); + struct pca9554_chip *pchip=(struct pca9554_chip *)desc->chip_data; + uint8_t gpioPortNum; + uint8_t gpioPortPinNum; + uint8_t pca9554pinnum; + + + if(!pca9554_checkrange(pchip->gpio_irq_start,pchip->irq_pin_num,irq)) + { + pca9554pinnum=irq-pchip->gpio_irq_start;//irq_to_gpio(irq) + } + else return; + gpioPortNum = pca9554pinnum/8; + gpioPortPinNum= pca9554pinnum%8; + + if((gpioPortNum>=PCA9554_PortNum)||(gpioPortPinNum>=portnum[gpioPortNum])) + return; + DBG("**%s**\n",__FUNCTION__); + + pchip->interrupt_en[gpioPortNum]=pca9554clearbit(pchip->interrupt_en[gpioPortNum],gpioPortPinNum); + +} + + +static void pca9554_gpio_irq_mask(unsigned irq) +{ + struct irq_desc *desc = irq_to_desc(irq); + struct pca9554_chip *pchip=(struct pca9554_chip *)desc->chip_data; + uint8_t gpioPortNum; + uint8_t gpioPortPinNum; + uint8_t pca9554pinnum; + + + if(!pca9554_checkrange(pchip->gpio_irq_start,pchip->irq_pin_num,irq)) + { + pca9554pinnum=irq-pchip->gpio_irq_start;//irq_to_gpio(irq) + } + else return; + gpioPortNum = pca9554pinnum/8; + gpioPortPinNum= pca9554pinnum%8; + + if((gpioPortNum>=PCA9554_PortNum)||(gpioPortPinNum>=portnum[gpioPortNum])) + return; + + DBG("**%s**\n",__FUNCTION__); + + pchip->interrupt_mask[gpioPortNum]=pca9554setbit(pchip->interrupt_mask[gpioPortNum],gpioPortPinNum); + +} + +static void pca9554_gpio_irq_unmask(unsigned irq) +{ + struct irq_desc *desc = irq_to_desc(irq); + //int gpiopinnum;//=irq_to_gpio(irq); + struct pca9554_chip *pchip=(struct pca9554_chip *)desc->chip_data; + uint8_t gpioPortNum; + uint8_t gpioPortPinNum; + uint8_t pca9554pinnum; + + DBG("**%s**\n",__FUNCTION__); + + if(!pca9554_checkrange(pchip->gpio_irq_start,pchip->irq_pin_num,irq)) + { + pca9554pinnum=irq-pchip->gpio_irq_start;//irq_to_gpio(irq) + } + else return; + gpioPortNum = pca9554pinnum/8; + gpioPortPinNum= pca9554pinnum%8; + + if((gpioPortNum>=PCA9554_PortNum)||(gpioPortPinNum>=portnum[gpioPortNum])) + return; + + + pchip->interrupt_mask[gpioPortNum]=pca9554clearbit(pchip->interrupt_mask[gpioPortNum],gpioPortPinNum); +} + +static int pca9554_gpio_irq_type(unsigned int irq, unsigned int type) +{ + struct irq_desc *desc_irq=irq_to_desc(irq); + struct pca9554_chip *pchip=(struct pca9554_chip *)desc_irq->chip_data; + //struct gpio_chip *chip_gpio; + int gpio_num; + uint8_t gpioPortNum; + uint8_t gpioPortPinNum; + uint8_t pca9554pinnum; + if(!pca9554_checkrange(pchip->gpio_irq_start,pchip->irq_pin_num,irq)) + { + pca9554pinnum=irq-pchip->gpio_irq_start;//irq_to_gpio(irq) + gpio_num=pchip->gpio_start+pca9554pinnum; + } + else + return -1; + + gpioPortNum = pca9554pinnum/8; + gpioPortPinNum= pca9554pinnum%8; + //DBG("**%s %d gpio_num=%d,PortNum=%d,PortPinNum=%d**\n",__FUNCTION__,__LINE__,gpio_num,gpioPortNum,gpioPortPinNum); + switch (type) { + case IRQ_TYPE_NONE: + pchip->inttype_set[gpioPortNum]=pca9554clearbit(pchip->inttype_set[gpioPortNum],gpioPortPinNum); + DBG("**%s IRQ_TYPE_NONE**\n",__FUNCTION__); + break; + case IRQ_TYPE_EDGE_RISING: + pchip->inttype_set[gpioPortNum]=pca9554setbit(pchip->inttype_set[gpioPortNum],gpioPortPinNum); + pchip->inttype[gpioPortNum]=pca9554setbit(pchip->inttype[gpioPortNum],gpioPortPinNum); + pchip->inttype1[gpioPortNum]=pca9554clearbit(pchip->inttype1[gpioPortNum],gpioPortPinNum); + DBG("**%s IRQ_TYPE_EDGE_RISING,inttype=%x,inttype1=%x**\n",__FUNCTION__,pchip->inttype[gpioPortNum],pchip->inttype1[gpioPortNum]); + + break; + case IRQ_TYPE_EDGE_FALLING: + pchip->inttype_set[gpioPortNum]=pca9554setbit(pchip->inttype_set[gpioPortNum],gpioPortPinNum); + pchip->inttype[gpioPortNum]=pca9554clearbit(pchip->inttype[gpioPortNum],gpioPortPinNum); + pchip->inttype1[gpioPortNum]=pca9554clearbit(pchip->inttype1[gpioPortNum],gpioPortPinNum); + DBG("**%s IRQ_TYPE_EDGE_RISING,inttype=%x,inttype1=%x**\n",__FUNCTION__,pchip->inttype[gpioPortNum],pchip->inttype1[gpioPortNum]); + + break; + case IRQ_TYPE_EDGE_BOTH: + pchip->inttype_set[gpioPortNum]=pca9554setbit(pchip->inttype_set[gpioPortNum],gpioPortPinNum); + pchip->inttype1[gpioPortNum]=pca9554setbit(pchip->inttype1[gpioPortNum],gpioPortPinNum); + DBG("**%s IRQ_TYPE_EDGE_RISING,inttype=%x,inttype1=%x**\n",__FUNCTION__,pchip->inttype[gpioPortNum],pchip->inttype1[gpioPortNum]); + break; + case IRQ_TYPE_LEVEL_HIGH: + pchip->inttype_set[gpioPortNum]=pca9554clearbit(pchip->inttype_set[gpioPortNum],gpioPortPinNum); + DBG("extern gpios does not support IRQ_TYPE_LEVEL_HIGH irq typ"); + break; + case IRQ_TYPE_LEVEL_LOW: + pchip->inttype_set[gpioPortNum]=pca9554clearbit(pchip->inttype_set[gpioPortNum],gpioPortPinNum); + DBG("extern gpios does not support IRQ_TYPE_LEVEL_LOW irq typ"); + break; + default: + return -EINVAL; + } +return 0; +} + +static int pca9554_gpio_irq_set_wake(unsigned irq, unsigned state) +{ + //no irq wake + return 0; + +} +static struct irq_chip pca9554_gpio_irqchip = { + .name = "GPIO_EXT", + .enable = pca9554_gpio_irq_enable, + .disable = pca9554_gpio_irq_disable, + .mask = pca9554_gpio_irq_mask, + .unmask = pca9554_gpio_irq_unmask, + .set_type = pca9554_gpio_irq_type, + .set_wake = pca9554_gpio_irq_set_wake, +}; + +static void pca9554_extend_gpio_irq_handler(struct work_struct *work) +{ + struct pca9554_chip *pchip=container_of(work, struct pca9554_chip,pca9554_work); + u8 tempintputreg[PCA9554_PortNum]={0}; + u8 tempallowint=0; + u8 levelchg=0; + u8 intbit=0; + u8 tempinttype=0; + int i,j; + struct irq_desc *gpio_irq_desc; + unsigned int irq; + if(pca9554_read_reg(pchip->client,PCA9554_InputLevel_Reg,&tempintputreg[0])<0) + { + + DBG("**%s[%d] reading reg is error\n",__FUNCTION__,__LINE__); + enable_irq(pchip->irq_chain); + return; + } + + DBG("**has run at %s,intputreg=%x**\n",__FUNCTION__,tempintputreg[0]); + if((pchip->interrupt_en[0]==0)) + { + memcpy(&pchip->reg_input[0],&tempintputreg[0],sizeof(tempintputreg)); + DBGERR("there are no pin reg irq\n"); + enable_irq(pchip->irq_chain); + return; + } + + for(i=0;iinterrupt_en[i]&pchip->reg_direction[i]&(~pchip->interrupt_mask[i]);// Âú×ãÖжÏÌõ¼þ + levelchg=pchip->reg_input[i]^tempintputreg[i];// ÕÒ³öÇ°ºó״̬²»Ò»ÑùµÄpin + tempinttype=~(tempintputreg[i]^pchip->inttype[i]);// ÕÒ³ö´¥·¢×´Ì¬ºÍµ±Ç°pin״̬һÑùµÄpin£¬×¢ÒâÖ»Ö§³Ölow highÁ½ÖÖpin´¥·¢ + + tempinttype=(~pchip->inttype1[i])&tempinttype;// inttype1 ΪÕæµÄλ¶ÔÓ¦µÄtempinttypeλÇåÁ㣬ÒòΪ¸ÃλֻÊÜinttype1¿ØÖÆ + tempinttype|=pchip->inttype1[i];//µçƽֻҪÊDZ仯¾Í²úÉúÖÐ¶Ï + tempinttype&=pchip->inttype_set[i];//ÒѾ­ÉèÖÃÁËtypeÀàÐÍ + + intbit=tempallowint&levelchg&tempinttype; + //DBG(" tempallowint=%x,levelchg=%x,tempinttype=%x,intbit=%d\n",tempallowint,levelchg,tempinttype,intbit); + + if(intbit) + for(j=0;jgpio_irq_start+PCA9554_PortPinNum*i+j; + gpio_irq_desc = irq_to_desc(irq); + gpio_irq_desc->chip->mask(irq); + generic_handle_irq(irq); + gpio_irq_desc->chip->unmask(irq); + + } + } + + pchip->reg_input[i]=tempintputreg[i]; + + } + enable_irq(pchip->irq_chain); + return; +} + +static irqreturn_t pca9554_gpio_irq_handler(int irq, void * dev_id) +{ + + struct irq_desc *gpio_irq_desc = irq_to_desc(irq); + struct pca9554_chip *pchip=(struct pca9554_chip *)gpio_irq_desc->chip_data; + + //DBG("**%s**\n",__FUNCTION__); + disable_irq_nosync(pchip->irq_chain); + queue_work(pca9554workqueue,&pchip->pca9554_work); + return IRQ_HANDLED; +} +static void pca9554_gpio_irq_setup(struct pca9554_chip *pchip) +{ + unsigned pioc, irq_num; + int ret; + struct irq_desc *desc; + irq_num = pchip->gpio_irq_start; //ÖжϺţ¬À©Õ¹ioµÄÖжϺÅÓ¦¸Ã½ô¸úÔÚÄÚ²¿ioÖжϺŵĺóÃæ¡£ÈçrkÄÚ²¿ÖжÏ48¸ö£¬¼ÓÉÏÄÚ²¿gpio 16¸öÐéÄâÖжϣ¬ÕâÀïpinÓ¦¸Ã´Ó48+16¿ªÊ¼ + + for (pioc = 0; pioc < pchip->irq_pin_num; pioc++,irq_num++) + { + lockdep_set_class(&irq_desc[irq_num].lock, &gpio_lock_class); + /* + * Can use the "simple" and not "edge" handler since it's + * shorter, and the AIC handles interrupts sanely. + */ + set_irq_chip(irq_num, &pca9554_gpio_irqchip); + set_irq_handler(irq_num, handle_simple_irq); + set_irq_chip_data(irq_num,(void *)pchip); + desc = irq_to_desc(irq_num); + //DBG("**%s line=%d,desc=%x,chipdate=%x,pchip=%x,irq_num=%d**\n",__FUNCTION__,__LINE__,desc,desc->chip_data,pchip,irq_num); + + set_irq_flags(irq_num, IRQF_VALID); + } + ret=gpio_request(pchip->irq_gpiopin,NULL); + if(ret!=0) + { + gpio_free(pchip->irq_gpiopin); + DBG("pca9554_gpio_irq_setup request gpio is err\n"); + } + + gpio_pull_updown(pchip->irq_gpiopin, GPIOPullUp); //gpio ÐèÒªÀ­¸ßirq_to_gpio(pchip->irq_chain) + +#if 0 + + set_irq_chip_data(pchip->irq_chain, pchip); + set_irq_chained_handler(pchip->irq_chain, gpio_irq_handlerxxx); + set_irq_type(pchip->irq_chain,IRQ_TYPE_LEVEL_LOW); + enable_irq(pchip->irq_chain); + +#else + pca9554workqueue=create_workqueue("pca9554 workqueue"); + INIT_WORK(&pchip->pca9554_work,pca9554_extend_gpio_irq_handler); + + set_irq_chip_data(pchip->irq_chain, pchip); + if(request_irq(pchip->irq_chain,pca9554_gpio_irq_handler, IRQF_TRIGGER_LOW, "pca6424", pchip)!=0) + { + DBG("**%s line=%d is err**\n",__FUNCTION__,__LINE__); + } + +#endif +} +static irqreturn_t pca9554_gpio_irq_handler_test1(int irq, void * dev_id) +{ + + DBG("***************************%s irq=%d***\n",__FUNCTION__,irq); + return IRQ_HANDLED; +} +static irqreturn_t pca9554_gpio_irq_handler_test2(int irq, void * dev_id) +{ + DBG("**************************%s irq=%d***\n",__FUNCTION__,irq); + return IRQ_HANDLED; +} + +void pca9554_test(struct pca9554_chip *chip) +{ + struct i2c_client *client=chip->client; + int ret; + int irq1,irq2; + + + + + ret=gpio_request(RK2818_PIN_PI4,NULL); + if(ret!=0) + DBG("*gpio_request in %d ret=%d*\n",__LINE__,ret); + + ret=gpio_request(RK2818_PIN_PI5,NULL); + if(ret!=0) + DBG("*gpio_request in %d ret=%d*\n",__LINE__,ret); + ret=gpio_request(RK2818_PIN_PI6,NULL); + if(ret!=0) + DBG("*gpio_request in %d ret=%d*\n",__LINE__,ret); + ret=gpio_request(RK2818_PIN_PI7,NULL); + if(ret!=0) + DBG("*gpio_request in %d ret=%d*\n",__LINE__,ret); + + irq1=gpio_to_irq(RK2818_PIN_PI4); + request_irq(irq1, pca9554_gpio_irq_handler_test1, IRQF_TRIGGER_LOW,"xxx1", chip); + + irq2=gpio_to_irq(RK2818_PIN_PI5); + request_irq(irq2, pca9554_gpio_irq_handler_test2, IRQF_TRIGGER_HIGH, "xxx2", chip); + + ret=gpio_direction_output(RK2818_PIN_PI6,GPIO_HIGH); + ret=gpio_direction_output(RK2818_PIN_PI7,GPIO_LOW); + + +} + +int pca9554_init_pintype(struct pca9554_chip *chip,struct i2c_client *client) +{ + int i; + struct pca9554_platform_data *platform_data=(struct pca9554_platform_data *)client->dev.platform_data; + struct rk2818_gpio_expander_info *pca9554_gpio_settinginfo; + uint8_t reg_output[PCA9554_PortNum]={0}; + uint8_t reg_direction[PCA9554_PortNum]={0}; + uint8_t pca9554_pin_num; + uint8_t gpioPortNum; + uint8_t gpioPortPinNum,pca9554_settingpin_num=0; + + if(platform_data) + { + pca9554_gpio_settinginfo=platform_data->settinginfo; + if(pca9554_gpio_settinginfo) + { + pca9554_settingpin_num=platform_data->settinginfolen; + for(i=0;igpio_start,chip->gpio_pin_num,pca9554_gpio_settinginfo[i].gpio_num)) + { + pca9554_pin_num=pca9554_gpio_settinginfo[i].gpio_num-chip->gpio_start; + gpioPortNum = pca9554_pin_num/PCA9554_PortPinNum; + gpioPortPinNum= pca9554_pin_num%PCA9554_PortPinNum; + //DBG("gpioPortNum=%d,gpioPortNum=%d,pca9554_pin_num=%d,reg_direction=%x,reg_output=%x,reg_input=%x\n",gpioPortNum,gpioPortPinNum,pca9554_pin_num,reg_direction[i],reg_output[i]); + if((gpioPortNum>=PCA9554_PortNum)||(gpioPortPinNum>=portnum[gpioPortNum])) + continue; + if(pca9554_gpio_settinginfo[i].pin_type==GPIO_IN) + reg_direction[gpioPortNum]=pca9554setbit(reg_direction[gpioPortNum],gpioPortPinNum); + else + { + if(pca9554_gpio_settinginfo[i].pin_value==GPIO_HIGH) + reg_output[gpioPortNum]=pca9554setbit(reg_output[gpioPortNum],gpioPortPinNum); + } + + + } + } + } + } + #ifdef PCA9554_OUTREGLOCK + mutex_init(&chip->outreglock); + #endif + #ifdef PCA9554_INPUTREGLOCK + mutex_init(&chip->inputreglock); + #endif + #ifdef PCA9554_OUTREGLOCK + mutex_init(&chip->configreglock); + #endif + + for(i=0; ireg_direction[i]=reg_direction[i]; + if (pca9554_write_reg(client, PCA9554_OutputLevel_Reg, reg_output[i])<0) + { + DBGERR("*%s %d write reg err*\n",__FUNCTION__,__LINE__); + return -1; + } + chip->reg_output[i]=reg_output[i]; + + if(pca9554_read_reg(client, PCA9554_InputLevel_Reg, &chip->reg_input[i])<0) + { + DBGERR("*%s %d read reg err*\n",__FUNCTION__,__LINE__); + return -1; + } + //DBG("reg_direction=%x,reg_output=%x,reg_input=%x\n",chip->reg_direction[i],chip->reg_output[i],chip->reg_input[i]); + + } + + return 0; +} +static int __devinit pca9554_probe(struct i2c_client *client,const struct i2c_device_id *id) +{ + struct pca9554_chip *chip; + struct pca9554_platform_data *pdata; + int ret; + + DBG("**gpio %s in %d line,dev adr is %x**\n",__FUNCTION__,__LINE__,client->addr); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EIO; + + chip = kzalloc(sizeof(struct pca9554_chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + pdata = client->dev.platform_data; + if (pdata == NULL) { + DBGERR(" %s no platform data\n",__FUNCTION__); + ret = -EINVAL; + goto out_failed; + } + + chip->gpio_start = pdata->gpio_base; + chip->gpio_irq_start =pdata->gpio_irq_start; + chip->gpio_pin_num=pdata->gpio_pin_num; + chip->irq_pin_num = pdata->irq_pin_num; + chip->irq_gpiopin=pdata->pca9954_irq_pin; + chip->irq_chain = gpio_to_irq(pdata->pca9954_irq_pin); + chip->names =pdata->names; + + //DBG("**%s in %d start=%d,irq_start=%d,pin_num=%d,irq_pin_num=%d,irq_gpiopin=%d,irq_chain=%d,**\n", + // __FUNCTION__,__LINE__,chip->gpio_start,chip->gpio_irq_start,chip->gpio_pin_num,chip->irq_pin_num,chip->irq_gpiopin + // ,chip->irq_chain); + + /* initialize cached registers from their original values. + * we can't share this chip with another i2c master. + */ + pca9554_setup_gpio(chip, id->driver_data); + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto out_failed; + + if(pca9554_init_pintype(chip,client)) + goto out_failed; + + if (pdata->setup) { + ret = pdata->setup(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) + DBGERR(" %s setup failed, %d\n",__FUNCTION__,ret); + } + + + pca9554_gpio_irq_setup(chip); + + i2c_set_clientdata(client, chip); + + + + chip->client = client; + + pca9554_test(chip); + + return 0; + + out_failed: + + kfree(chip); + return 0; +} + +static int pca9554_remove(struct i2c_client *client) +{ + struct pca9554_platform_data *pdata = client->dev.platform_data; + struct pca9554_chip *chip = i2c_get_clientdata(client); + int ret = 0; + + if (pdata->teardown) { + ret = pdata->teardown(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) { + DBGERR(" %s failed, %d\n",__FUNCTION__,ret); + return ret; + } + } + + ret = gpiochip_remove(&chip->gpio_chip); + if (ret) { + dev_err(&client->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + kfree(chip); + return 0; +} + +static struct i2c_driver pca9554_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "extend_gpio_pca9554", + }, + .probe = pca9554_probe, + .remove = pca9554_remove, + .id_table = pca9554_id, +}; + +static int __init pca9554_init(void) +{ + DBG("**pca9554_init**\n"); + return i2c_add_driver(&pca9554_driver); +} +static void __exit pca9554_exit(void) +{ + i2c_del_driver(&pca9554_driver); +} + +module_init(pca9554_init); +module_exit(pca9554_exit); +MODULE_AUTHOR(" XXX XXX@rock-chips.com"); +MODULE_DESCRIPTION("Driver for rk2818 extend gpio device"); +MODULE_LICENSE("GPL"); + + -- 2.34.1