一个可做模版的i2c设备驱动

原创 2017年07月12日 13:22:07
/*
zk.xu add for halo mini keypad 


 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/debugfs.h>
#include <linux/cdev.h>
#include <linux/err.h>
#include <linux/limits.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/platform_device.h>
#include <linux/gpio_event.h>
#include <linux/wakelock.h>
#include <asm/uaccess.h>
#include <linux/videodev2.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <asm/atomic.h>
#include <linux/regulator/consumer.h>
#include <linux/fb.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#define MTK_ANDROID_KERNEL318
//Include MTK
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/rtpm_prio.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/dma-mapping.h>


#ifndef MTK_ANDROID_KERNEL318
#include <mach/mt_pm_ldo.h>
#include <mach/mt_typedefs.h>
#include <mach/mt_boot.h>
#include <cust_eint.h>
#include <cust_gpio_usage.h>
#include <pmic_drv.h>


#endif


#define PCA9533A
//#define USE_CHIP_TCA9539A 
// 
#ifdef USE_CHIP_TCA9539A
#define INPUT_START_ADDR 0 // read port o and port 1
#define OUTPUT_START_ADDR
#define INVERT_START_ADDR 4
#define DIRECTION_START_ADDR 6
#define CHIP_I2C_ADDR 0x74
#else
#define INPUT_START_ADDR 0 // read port o and port 1
#define OUTPUT_START_ADDR
#define INVERT_START_ADDR 4
#define DIRECTION_START_ADDR 6
#define CHIP_I2C_ADDR 0x20
#endif 


//wangcq
bool minikey_active = false;
static int minikey_thread_handler(void *);
static DECLARE_WAIT_QUEUE_HEAD(waiter);
static u8 minikey_interrupt_flag;
static struct task_struct *thread_minikey;
static struct kp_data *lp;
static struct notifier_block mini_key_fb_notifier;


#define MINIKEY_LIGHT_CIRCLE 3000
static struct delayed_work minikey_light_work;
static struct workqueue_struct *minikey_light_workqueue = NULL;


#define DRV_NAME "mini_keypad"


static const struct of_device_id mini_kbd_id_table[] = {
{.compatible = "mediatek,mini_keypad"},
{},
};
MODULE_DEVICE_TABLE(of, mini_kbd_id);


static struct pinctrl *minikey_ctrl = NULL;
static struct pinctrl_state *minikey_eint = NULL;
static struct pinctrl_state *minikey_power_h = NULL;
static struct pinctrl_state *minikey_power_l = NULL;
static struct pinctrl_state *minikey_rst_h = NULL;
static struct pinctrl_state *minikey_rst_l = NULL;
static struct pinctrl_state *minikey_enable_h = NULL;
static struct pinctrl_state *minikey_enable_l = NULL;
//




#if 1 //// gaopu
static const int mini_kbd_code[] = {
[0] = KEY_DOWN,
[1] = KEY_BACK,
[2] = KEY_MUTE,//vol_no
[3] = KEY_UP,

[4] = KEY_LEFT,
[5] = KEY_ENTER ,
[6] = KEY_VOLUMEDOWN,//vol-
[7] = KEY_VOLUMEUP,//vol+
[8] = KEY_MENU ,//Menu

[9] =  KEY_RIGHT,
[10] = BTN_SELECT,
[11] = BTN_START,

[12] = KEY_RESERVED,
[13] = KEY_RESERVED,
[14] = KEY_RESERVED,
[15] = KEY_RESERVED,
};


static const char mini_kbd_name[16][16] = {
[0] = "KEY_UP",
[1] = "KEY_DOWN",
[2] = "KEY_LEFT",
[3] = "KEY_RIGHT",

[4] = "KEY_PLAYPAUSE",//0x10 left pad center
[5] = "BTN_Y",// 0x20
[6] = "KEY_BACK",// right pad center
[7] = "BTN_B",

[8] =  "BTN_A",// 0x100
[9] =  "BTN_X", //0x200
[10] = "BTN_SELECT",
[11] = "BTN_START",

[12] = "KEY_RSV12", //0x1000
[13] = "KEY_RSV13",
[14] = "KEY_RSV14",
[15] = "KEY_RSV15",
};




#else
static const int mini_kbd_code[] = {
[0] = KEY_UP,
[1] = KEY_DOWN,
[2] = KEY_LEFT,
[3] = KEY_RIGHT,

[4] = BTN_SELECT,//0x10 left pad center
[5] = BTN_Y,// 0x20
[6] = BTN_START,// right pad center
[7] = BTN_B ,

[8] =  BTN_A,// 0x100
[9] =  BTN_X, //0x200
[10] = BTN_TL,
[11] = BTN_TR,

[12] = KEY_RESERVED, //0x1000
[13] = KEY_RESERVED,
[14] = KEY_RESERVED,
[15] = KEY_RESERVED,
};


static const char mini_kbd_name[16][16] = {
[0] = "KEY_UP",
[1] = "KEY_DOWN",
[2] = "KEY_LEFT",
[3] = "KEY_RIGHT",

[4] = "BTN_SELECT",//0x10 left pad center
[5] = "BTN_Y",// 0x20
[6] = "BTN_START",// right pad center
[7] = "BTN_B",

[8] =  "BTN_A",// 0x100
[9] =  "BTN_X", //0x200
[10] = "BTN_TL",
[11] = "BTN_TR",

[12] = "KEY_RSV12", //0x1000
[13] = "KEY_RSV13",
[14] = "KEY_RSV14",
[15] = "KEY_RSV15",
};
#endif




struct kp_data {
int btncode[ARRAY_SIZE(mini_kbd_code)];
//char keyname[ARRAY_SIZE(mini_kbd_code)][16];
struct input_dev *idev;
struct i2c_client *client;
char name[64];
char phys[32];
u16 laststate;
struct mutex i2c_lock;
    struct work_struct  eint_work;


};


static int mini_key_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
{
struct fb_event *evdata = NULL;
int blank;


evdata = data;
/* If we aren't interested in this event, skip it immediately ... */
if (event != FB_EVENT_BLANK)
return 0;

blank = *(int *)evdata->data;
dev_err(&lp->client->dev,"fb_notify(blank=%d)\n", blank);
switch (blank) {
case FB_BLANK_UNBLANK:
dev_err(&lp->client->dev,"LCD ON Notify\n");
enable_irq(lp->client->irq);
break;
case FB_BLANK_POWERDOWN:
dev_err(&lp->client->dev,"LCD OFF Notify\n");
disable_irq(lp->client->irq);
break;
default:
break;
}
return 0;
}




static int write16bit(struct i2c_client *client, u8 reg, u16 val)
{
int ret = 0;
int cnt=3;
    while(cnt--){
ret = i2c_smbus_write_word_data(client,reg, val);


if (ret < 0) {
dev_err(&client->dev, "failed write16bit\n");
//return ret;
}
else {return 0;}

msleep(5);
    }
return ret;
}


static int read16bit(struct i2c_client *client, u8 reg, u16 *val)
{
int ret;
int cnt=3;
    while(cnt--){
ret = i2c_smbus_read_word_data(client, reg);
if (ret < 0) {
dev_err(&client->dev, "failed read16bit\n");
//return ret;
}else{
*val = ret;
return 0;
}
msleep(5);
    }
return ret;
}




static u16 read_state(struct kp_data *lp)
{
    u16 val;
int ret;
mutex_lock(&lp->i2c_lock);
ret=read16bit(lp->client, INPUT_START_ADDR, &val);
if(ret<0){
       val =lp->laststate; // not change 
}
mutex_unlock(&lp->i2c_lock);
dev_err(&(lp->client->dev), "zk mini_kbd read_state = %4x\n",val);


return val;
}


static void minikey_light_on(bool on)
{
u16 out_state=0xffff;
read16bit(lp->client, OUTPUT_START_ADDR,&out_state);
printk("wangcq327 --- light out_state_1[0x%x]\n",out_state);
if(false == on){
write16bit(lp->client, OUTPUT_START_ADDR,out_state & 0xdfff);
}else if(true == on){
write16bit(lp->client, OUTPUT_START_ADDR,out_state | ~0xdfff);
}
read16bit(lp->client, OUTPUT_START_ADDR,&out_state);
printk("wangcq327 --- light out_state_2[0x%x]\n",out_state);
}
static void minikey_light_func(struct work_struct *work)
{
minikey_light_on(false);
}
void minikey_light_keepon(bool on)
{
if(true == on){
cancel_delayed_work(&minikey_light_work);
minikey_light_on(true);
queue_delayed_work(minikey_light_workqueue,&minikey_light_work,msecs_to_jiffies(MINIKEY_LIGHT_CIRCLE));
printk("wangcq327 --- light on 3s\n");
}else if(false == on){
cancel_delayed_work(&minikey_light_work);
minikey_light_func(NULL);
printk("wangcq327 --- light off now\n");
}
}


#define EINT_PIN 2
#define EN_PIN 3
#define POWER_PIN 4
#define RST_PIN 5
#define LEVEL_H 1
#define LEVEL_L 0
typedef enum {
eint_pin=EINT_PIN,
power_pin=POWER_PIN,
rst_pin=RST_PIN,
en_pin=EN_PIN,
}pin_name;


typedef enum{
level_h=LEVEL_H,
level_l=LEVEL_L,
}pin_level;


static DEFINE_MUTEX(set_minikey_pin_mutex);
static void set_minikey_pin(pin_name pin,pin_level level)
{
mutex_lock(&set_minikey_pin_mutex);
if(EINT_PIN == pin){
pinctrl_select_state(minikey_ctrl,minikey_eint);
}else if(POWER_PIN == pin){
if(LEVEL_H == level){
pinctrl_select_state(minikey_ctrl, minikey_power_h);
}else if(LEVEL_L == level){
pinctrl_select_state(minikey_ctrl, minikey_power_l);
}
}else if(RST_PIN == pin){
if(LEVEL_H == level){
pinctrl_select_state(minikey_ctrl, minikey_rst_h);
}else if(LEVEL_L == level){
pinctrl_select_state(minikey_ctrl, minikey_rst_l);
}
}else if(EN_PIN == pin){
if(LEVEL_H == level){
pinctrl_select_state(minikey_ctrl, minikey_enable_h);
}else if(LEVEL_L == level){
pinctrl_select_state(minikey_ctrl, minikey_enable_l);
}
}
mutex_unlock(&set_minikey_pin_mutex);
}


int mini_kbd_dev_init(struct kp_data *lp)
{


u16 reg_val; // 1 for input  high impedance 
int ret=0;




printk("zk :mini_kbd_dev_init start \n");
mutex_lock(&(lp->i2c_lock));


set_minikey_pin(rst_pin,LEVEL_H);
mdelay(10);
set_minikey_pin(rst_pin,LEVEL_L);
mdelay(10);
set_minikey_pin(rst_pin,LEVEL_H);
mdelay(10);


minikey_light_on(false);


//check  
reg_val=0xaabb;
ret = write16bit(lp->client, INVERT_START_ADDR, reg_val);
printk("wangcq327 --- 7bitaddr 0x%x,-----ret=%d\n",lp->client->addr,ret);
if (ret){
goto exit;
}
// read back
reg_val=0;
read16bit(lp->client, INVERT_START_ADDR, &reg_val);
dev_err(&(lp->client->dev), "zk probe:mini_kbd_dev_init reg_val =%d\n",reg_val);
    if(reg_val!=0xaabb){
ret=-1;
goto exit;
}

// check end


// not inverse 
reg_val=0x0;
ret = write16bit(lp->client, INVERT_START_ADDR, reg_val);
if (ret)
goto exit;


// set all port as input 


reg_val=0xdfff;//1101 1111 port1 bit5 as outpin
ret = write16bit(lp->client, DIRECTION_START_ADDR, reg_val);
if (ret)
goto exit;


minikey_light_on(false);
exit:
mutex_unlock(&(lp->i2c_lock));
return ret ;


}




static irqreturn_t mini_key_interrupt_handler(int irq, void *dev_id)
{
minikey_interrupt_flag = 1;
wake_up_interruptible(&waiter);
return IRQ_HANDLED;
}






static int minikey_thread_handler(void * unused)
{
u16 nextstate,xorstate=0,bkstate;
int key_down,keycode,i;
struct sched_param param = {.sched_priority = RTPM_PRIO_TPD};
sched_setscheduler(current,SCHED_RR,&param);


do{
set_current_state(TASK_INTERRUPTIBLE);
wait_event_interruptible(waiter,minikey_interrupt_flag == 1);
minikey_interrupt_flag = 0;
set_current_state(TASK_RUNNING);

nextstate = read_state(lp);
bkstate=nextstate;


if (lp->laststate != nextstate) { // key changed 
xorstate=lp->laststate^nextstate;

for(i=0;i<ARRAY_SIZE(lp->btncode);i++){

if(xorstate!=0){
if(xorstate&0x0001){ /*this bit has changed */
keycode=lp->btncode[i];
if(nextstate&0x0001){
key_down=1;
}else{
key_down=0;
}

if(keycode!=KEY_RESERVED){
if(key_down == 1){
minikey_light_keepon(true);
}
dev_err(&lp->client->dev, "minikpd name=%s ,keycode=%d , kydown=%d\n",mini_kbd_name[i],keycode, key_down);
input_report_key(lp->idev, keycode, key_down);
input_sync(lp->idev);
}
}


xorstate=xorstate>>1;
nextstate=nextstate>>1;
}

}
}
lp->laststate = bkstate;

}while(!kthread_should_stop());

return 0;
}




static int of_get_minikey_gpio_info(struct device* dev)
{
int ret=0;
dev_err(dev,"find minikey_ctrl pinctrl start +++++++++++++!");
minikey_ctrl = devm_pinctrl_get(dev);
if (IS_ERR(minikey_ctrl)) {
ret = PTR_ERR(minikey_ctrl);
dev_err(dev,"Cannot find minikey_ctrl pinctrl!");
return ret;
}

minikey_eint = pinctrl_lookup_state(minikey_ctrl, "state_ic_pins_as_eint");
if (IS_ERR(minikey_eint)) {
ret = PTR_ERR(minikey_eint);
dev_err(dev,"%s : pinctrl err, minikey_eint\n", __func__);
return ret;
}
minikey_power_h = pinctrl_lookup_state(minikey_ctrl, "state_ic_pins_pow_h");
if (IS_ERR(minikey_power_h)) {
ret = PTR_ERR(minikey_power_h);
dev_err(dev,"%s : pinctrl err, minikey_power_h\n", __func__);
return ret;
}
minikey_power_l = pinctrl_lookup_state(minikey_ctrl, "state_ic_pins_pow_l");
if (IS_ERR(minikey_power_l)) {
ret = PTR_ERR(minikey_power_l);
dev_err(dev,"%s : pinctrl err, minikey_power_l\n", __func__);
return ret;
}


minikey_rst_h = pinctrl_lookup_state(minikey_ctrl, "state_ic_pins_rst_h");
if (IS_ERR(minikey_rst_h)) {
ret = PTR_ERR(minikey_rst_h);
dev_err(dev,"%s : pinctrl err, minikey_rst_h\n", __func__);
return ret;
}
minikey_rst_l = pinctrl_lookup_state(minikey_ctrl, "state_ic_pins_rst_l");
if (IS_ERR(minikey_rst_l)) {
ret = PTR_ERR(minikey_rst_l);
dev_err(dev,"%s : pinctrl err, minikey_rst_l\n", __func__);
return ret;
}


minikey_enable_h = pinctrl_lookup_state(minikey_ctrl, "state_ic_pins_enable_h");
if (IS_ERR(minikey_enable_h)) {
ret = PTR_ERR(minikey_enable_h);
dev_err(dev,"%s : pinctrl err, minikey_enable_h\n", __func__);
return ret;
}
minikey_enable_l = pinctrl_lookup_state(minikey_ctrl, "state_ic_pins_enable_l");
if (IS_ERR(minikey_enable_l)) {
ret = PTR_ERR(minikey_enable_l);
dev_err(dev,"%s : pinctrl err, minikey_enable_l\n", __func__);
return ret;
}
dev_err(dev,"find minikey_ctrl pinctrl end ++++++++++++++!");
return 0;

}




static int mipikey_irq_register(struct i2c_client *client)
{
int ret=1;
u32 ints[2]={0,0};
struct of_device_id touch_of_match[] = {
{ .compatible = "mediatek,mt6797-touch", },
{},
};
if(client->dev.of_node){
struct device_node *node=NULL;
node = of_find_matching_node(node,touch_of_match);
if(node){
//step 1:
set_minikey_pin(eint_pin,LEVEL_H);
//step 2:
of_property_read_u32_array(node,"debunce",ints,ARRAY_SIZE(ints));
gpio_set_debounce(ints[0],ints[1]);
client->irq = irq_of_parse_and_map(node,0);
//step 3:
ret = request_irq(client->irq,mini_key_interrupt_handler,IRQF_TRIGGER_LOW | IRQF_ONESHOT,"minikey-eint",lp);
dev_err(&client->dev,"minikey_irq_num == %d\n",client->irq);
if(ret > 0){
dev_err(&client->dev,"minikey_irq register fail!");
}
}else{
dev_err(&client->dev,"Cannot find device!");
}
}
return ret;
}






static int mini_kbd_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int i, ret;
struct input_dev *idev=NULL;
dev_err(&client->dev, "wangcq327 --- step0:[sucess] into probe \n");
lp = kzalloc(sizeof(*lp), GFP_KERNEL);
if (!lp)
return -ENOMEM;
lp->client = client;

ret = of_get_minikey_gpio_info(&client->dev);
if(ret != 0){
dev_err(&client->dev, "get_mini_gpio_info fail\n");
goto fail_allocate;
}
dev_err(&client->dev, "wangcq327 --- step1:[sucess] get_mini_gpio_info \n");
#if 1
set_minikey_pin(power_pin,LEVEL_H);//VDD_3V3
set_minikey_pin(en_pin,LEVEL_H);//VDD_3V3
#else
hwPowerOn(MT6328_POWER_LDO_VGP1, VOL_2800,"minikpd");//always on //"minikpd"
#endif
mutex_init(&lp->i2c_lock);




#if 1
set_minikey_pin(eint_pin,LEVEL_H);
ret = mipikey_irq_register(client);
if (ret) {
dev_err(&client->dev, "IRQ %d is not free\n", client->irq);
goto fail_free_irq;
}
dev_err(&client->dev, "wangcq327 --- step2:[sucess] irq_register\n");
#else
mt_set_gpio_mode(GPIO_CTP_EINT_PIN, GPIO_CTP_EINT_PIN_M_EINT);
mt_set_gpio_dir(GPIO_CTP_EINT_PIN, GPIO_DIR_IN);
mt_set_gpio_pull_enable(GPIO_CTP_EINT_PIN, GPIO_PULL_ENABLE);
mt_set_gpio_pull_select(GPIO_CTP_EINT_PIN, GPIO_PULL_UP);
mt_eint_set_hw_debounce(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_DEBOUNCE_CN);
mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINTF_TRIGGER_FALLING, mini_kbd_irq, 0);
mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
#endif


if (mini_kbd_dev_init(lp) != 0) {
#if 1
#else
hwPowerDown(MT6328_POWER_LDO_VGP1, "minikpd");//always on
#endif
dev_err(&client->dev, "probe: init fail\n");
goto fail_free_irq;
}
dev_err(&client->dev, "wangcq327 --- step2:[sucess] ic_init\n");
//  irq
INIT_DELAYED_WORK(&minikey_light_work,minikey_light_func);
minikey_light_workqueue = create_workqueue("minikey_light_workqueue");
//queue_delayed_work(minikey_light_workqueue,&minikey_light_work,MINIKEY_LIGHT_CIRCLE);





idev = input_allocate_device();
if (!idev) {
dev_err(&client->dev, "Can't allocate input device\n");
ret = -ENOMEM;
goto fail_free_irq;
}


lp->idev = idev;
idev->evbit[0] = BIT_MASK(EV_KEY);
//idev->keycode = lp->btncode;
//idev->keycodesize = sizeof(lp->btncode[0]);
//idev->keycodemax = ARRAY_SIZE(lp->btncode);


for (i = 0; i < ARRAY_SIZE(mini_kbd_code); i++) {
lp->btncode[i] = mini_kbd_code[i];
__set_bit(lp->btncode[i] , idev->keybit);//& KEY_MAX
}
set_bit(EV_KEY, idev->evbit);
set_bit(EV_SYN, idev->evbit);


//set_bit(KEY_ENTER, idev->evbit);
//set_bit(BTN_A, idev->evbit); 
//set_bit(KEY_MENU, idev->evbit); 
sprintf(lp->name, DRV_NAME);
sprintf(lp->phys, "kp_mini/input0");


idev->name = lp->name;
idev->phys = lp->phys;
idev->id.bustype = BUS_I2C;
idev->id.vendor = 0x0011;
idev->id.product = 0x0001;
idev->id.version = 0x0100;


lp->laststate = read_state(lp);


ret = input_register_device(idev);
if (ret) {
dev_err(&client->dev, "input_register_device() failed\n");
goto fail_free_device;
}
dev_err(&client->dev, "wangcq327 --- step4:[sucess] input_device register\n");


thread_minikey = kthread_run(minikey_thread_handler,0,DRV_NAME);
if(IS_ERR(thread_minikey)){
dev_err(&client->dev, "create kernel_thread fail:%ld\n",PTR_ERR(thread_minikey));
goto fail_free_register_input_device;
}
mini_key_fb_notifier.notifier_call = mini_key_notifier_callback;
if(fb_register_client(&mini_key_fb_notifier)){
printk("register fb_notifier fail\n");
}
minikey_active = true;
minikey_light_keepon(true);
i2c_set_clientdata(client, lp);


printk("zk :mini_kbd_probe ok \n");
return 0;


fail_free_register_input_device:
input_unregister_device(idev);
fail_free_device:
input_free_device(idev);
fail_free_irq:
free_irq(client->irq, lp);
set_minikey_pin(power_pin,LEVEL_L);//VDD_3V3
set_minikey_pin(en_pin,LEVEL_L);//VDD_3V3
fail_allocate:
kfree(lp);
return -1;

}




static int /*__devexit*/ mini_kbd_remove(struct i2c_client *client)
{
struct kp_data *lp = i2c_get_clientdata(client);
free_irq(client->irq, lp);
input_unregister_device(lp->idev);
kfree(lp);


return 0;
}
#if 0


int mini_kbd_suspend_1(struct i2c_client *client,pm_message_t mesg)
{
dev_err(&client->dev, "suspend\n");
set_minikey_pin(power_pin,LEVEL_L);//VDD_3V3
set_minikey_pin(en_pin,LEVEL_L);//VDD_3V3
disable_irq(client->irq);
return 0;
}


int mini_kbd_resume_1(struct i2c_client *client)
{
dev_err(&client->dev, "resume\n");
set_minikey_pin(power_pin,LEVEL_H);//VDD_3V3
set_minikey_pin(en_pin,LEVEL_H);//VDD_3V3
enable_irq(client->irq);
return 0;
}
#endif


#if CONFIG_PM
static int mini_kbd_resume(struct device *dev)
{
dev_err(dev, "resume\n");
//enddable_irq(lp->client->irq);
//minikey_light_keepon(true);
return 0;
}


static int mini_kbd_suspend(struct device *dev)
{
dev_err(dev, "suspend\n");
minikey_light_keepon(false);
//disable_irq(lp->client->irq);
return 0;
}


static const struct dev_pm_ops mini_kbd_pm_ops = {
.suspend = mini_kbd_suspend,
.resume = mini_kbd_resume,
};


#else
# define mini_kbd_resume  NULL
# define mini_kbd_suspend NULL
#endif


static const struct i2c_device_id mini_kbd_id[] = {
{DRV_NAME,0},
};
MODULE_DEVICE_TABLE(i2c,mini_kbd_id);


static struct i2c_driver mini_kbd_driver = {
.driver = {
.name  = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(mini_kbd_id_table),
#ifdef CONFIG_PM
.pm = &mini_kbd_pm_ops,
#endif
},
//.suspend = mini_kbd_suspend_1,
//.resume = mini_kbd_resume_1,
.probe    = mini_kbd_probe,
.remove   = mini_kbd_remove,
.id_table = mini_kbd_id,
};






static int __init mini_kbd_init(void)
{
   printk("mini_kbd_init\n");
   i2c_add_driver(&mini_kbd_driver);
   printk("mini_kbd_init end\n");
   return 0;
}


static void __exit mini_kbd_exit(void)
{
  i2c_del_driver(&mini_kbd_driver);  
}




module_init(mini_kbd_init);
module_exit(mini_kbd_exit);




MODULE_DESCRIPTION("ivglass meta mini keyboard");
MODULE_VERSION("2017.06.23");
MODULE_AUTHOR("zk.xu");

MODULE_LICENSE("GPL");



// kernel-3.18/arch/arm64/boot/dts/meta_m67_5u.dts

&i2c4 {
mini_keypad@74 {
compatible = "mediatek,mini_keypad";
reg = <0x20>;
pinctrl-names = "default","state_ic_pins_as_eint","state_ic_pins_pow_h","state_ic_pins_pow_l","state_ic_pins_rst_h","state_ic_pins_rst_l","state_ic_pins_enable_h","state_ic_pins_enable_l";
pinctrl-0 = <&gpio_default>;
pinctrl-1 = <&ic_pins_as_eint>;
pinctrl-2 = <&ic_pins_pow_h>;
pinctrl-3 = <&ic_pins_pow_l>;
pinctrl-4 = <&ic_pins_rst_h>;
pinctrl-5 = <&ic_pins_rst_l>;
pinctrl-6 = <&ic_pins_enable_h>;
pinctrl-7 = <&ic_pins_enable_l>;
status = "okay";


};
};
&pio {
gpio_default: gpiodefault {
};
ic_pins_as_eint: ic_pins_as_eint {
   pins_cmd_dat {
pins = <PINMUX_GPIO85__FUNC_GPIO85>;
slew-rate = <1>;
output-low;
};
};
ic_pins_pow_h: ic_pins_pow_h {
   pins_cmd_dat {
pins = <PINMUX_GPIO45__FUNC_GPIO45>;
slew-rate = <1>;
output-high;
};
};
ic_pins_pow_l: ic_pins_pow_l {
   pins_cmd_dat {
pins = <PINMUX_GPIO45__FUNC_GPIO45>;
slew-rate = <1>;
output-low;
};
};
ic_pins_rst_h: ic_pins_rst_h {
   pins_cmd_dat {
pins = <PINMUX_GPIO68__FUNC_GPIO68>;
slew-rate = <1>;
output-high;
};
};
ic_pins_rst_l: ic_pins_rst_l {
   pins_cmd_dat {
pins = <PINMUX_GPIO68__FUNC_GPIO68>;
slew-rate = <1>;
output-low;
};
};
ic_pins_enable_h: ic_pins_enable_h {
   pins_cmd_dat {
pins = <PINMUX_GPIO46__FUNC_GPIO46>;
slew-rate = <1>;
output-high;
};
};
ic_pins_enable_l: ic_pins_enable_l {
   pins_cmd_dat {
pins = <PINMUX_GPIO46__FUNC_GPIO46>;
slew-rate = <1>;
output-low;
};
};
};

版权声明:QQ719502447

【I2C设备驱动】必须将id_table[]数组的最后一个元素设置为空的原因

忘记了在哪本书上看到过,说必须给 I2C 设备驱动的 id 表数组添加上一个空元素作为最后一个元素,就像下面的代码所展示的那样:struct i2c_device_id { char name...

Linux设备驱动之I2C架构分析

  • 2014年06月24日 14:31
  • 71KB
  • 下载

I2C设备驱动示例

  • 2014年01月04日 18:27
  • 10KB
  • 下载

Linux设备驱动之——I2C总线

2  I2C子系统 2.1 LinuxI2C子系统架构 在内核中已经提供I2C子系统,所以在做I2C驱动之前,就必须要熟悉该子系统。 2.2 三大组成部分 1、I2C核心(i2c-co...
  • jmq_0000
  • jmq_0000
  • 2012年04月23日 14:37
  • 23160

A20_I2C设备驱动开发

  • 2014年05月03日 17:14
  • 1.4MB
  • 下载

Linux设备驱动之I2C架构分析

  • 2010年11月04日 22:52
  • 814KB
  • 下载

Linux设备驱动第十三天(I2C、DS18B20)

回顾: I2C总线: 概念:两线式串行总线 SDA SCL 一个时钟周期传输一个bit位 画简易的连接图 问:CPU如何访问I2C总线上的某个外设? 问:CPU如何通过I2C总线实现与...
  • PZ0605
  • PZ0605
  • 2017年01月07日 01:24
  • 575

linux i2c设备驱动

linux下i2c设备驱动框架linux下i2c体系结构分为三部分: 1. i2c core 层:提供设备驱动,总线驱动的接口API,i2c 通讯方法,探测设备。 2. i2c 总线驱动:实现...

linux I2C 设备驱动学习笔记

linux下的驱动思路:内核态驱动和用户态驱动 一是把I2C设备当作一个普通的字符设备来处理,用i2c-dev.c文件提供的API,封装设备时序数据,直接操作i2c适配器驱动对应的设备文件,实现与设...

《精通Linux设备驱动开发》——i2c协议(代码1)

/* 代码清单8-2 */ /* 初始化EEPROM驱动程序 */ /* Driver entry points */ static struct file_operation eep_fops = ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:一个可做模版的i2c设备驱动
举报原因:
原因补充:

(最多只允许输入30个字)