阅读背景:

Linux I2C 设备驱动

来源:互联网 
dts device tree 文件描述 i2c1: i2c@02203000 { #address-cells = <1>; #size-cells = <0>; compatible = "snps,designware-i2c"; reg = <0x02203000 0x1000>; clocks = <&apb0>; interrupts = <2>; status = "okay"; //新添加的设备描述 apt: apt32f101xx@5a { compatible = "apt,apt32f101xx"; 驱动文件of_match #address-cells = <1>; #size-cells = <0>; reg = <0x5a>; // 设备I2C地址7bit (8bit addr /2) 右移1位 , //reg = <0x3c>; // 0x78(8bit), 0x78 / 2 = 0x3c(7bit) }; }; 系统启动后会创建/sys/devices/platform/soc/2203000.i2c/i2c-1/1-005a 如果没成功创建设备节点,加载驱动时probe不会调用 /************************************************************************************************************************************** * * File Name : apt32f101.c * Date : 2018-06-25 * Author : [email protected] * * function : APT32F101 MCU driver. * **************************************************************************************************************************************/ #include <linux/init.h> #include <linux/i2c.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/types.h> #include <linux/types.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/input-polldev.h> #include <linux/delay.h> #include <linux/wait.h> #include <linux/poll.h> #include <linux/slab.h> #include <linux/freezer.h> #include <linux/uaccess.h> #include <linux/miscdevice.h> #include <linux/pm_runtime.h> #include <linux/atomic.h> #include <linux/of_device.h> #include <linux/device.h> #include <linux/sysfs.h> #include <linux/string.h> #include <linux/dmi.h> #include <linux/idr.h> #include <linux/mutex.h> #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> #include <linux/kdev_t.h> #include <linux/devcoredump.h> #include <linux/list.h> #include <linux/fs.h> #include <linux/workqueue.h> #include <linux/timer.h> #include <linux/kthread.h> #include "apt32f101.h" #define SYSTEM_STATUS_CHECK_TIME (3 * 100) // n秒 #define D_SUPPORT_CLASS_SYSFS 0 #define D_SUPPORT_KTHREAD #define D_SUUPORT_DELAYED_TASKLET #define D_SUPPORT_KEY_IRQ #define dprintk printk // 高字节命令码,低字节指令码 // 系统命令码定义 #define D_SYSCMD_CODE_MOTOR 0x11 // 马达电机 #define D_SYSCMD_CODE_EYELED 0x12 // 眼灯控制 #define D_SYSCMD_CODE_EARLED 0x13 // 耳灯控制 #define D_SYSCMD_CODE_STATUS 0x14 // 系统状态 电池电量,充电检查 // 马达运动命令 #define D_MOTOR_ACTION_WHEEL_STOP (D_SYSCMD_CODE_MOTOR << 8 | 0x00) #define D_MOTOR_ACTION_WHEEL_BREAK (D_SYSCMD_CODE_MOTOR << 8 | 0x01) #define D_MOTOR_ACTION_WHEEL_FORWARD (D_SYSCMD_CODE_MOTOR << 8 | 0x02) #define D_MOTOR_ACTION_WHEEL_BACKWARD (D_SYSCMD_CODE_MOTOR << 8 | 0x03) #define D_MOTOR_ACTION_WHEEL_ROTATION_LEFT (D_SYSCMD_CODE_MOTOR << 8 | 0x04) #define D_MOTOR_ACTION_WHEEL_ROTATION_RIGHT (D_SYSCMD_CODE_MOTOR << 8 | 0x05) #define D_MOTOR_ACTION_WHEEL_LEFT_STOP (D_SYSCMD_CODE_MOTOR << 8 | 0x06) #define D_MOTOR_ACTION_WHEEL_LEFT_BREAK (D_SYSCMD_CODE_MOTOR << 8 | 0x07) #define D_MOTOR_ACTION_WHEEL_LEFT_FORWARD (D_SYSCMD_CODE_MOTOR << 8 | 0x08) #define D_MOTOR_ACTION_WHEEL_LEFT_BACKWARD (D_SYSCMD_CODE_MOTOR << 8 | 0x09) #define D_MOTOR_ACTION_WHEEL_RIGHT_STOP (D_SYSCMD_CODE_MOTOR << 8 | 0x0a) #define D_MOTOR_ACTION_WHEEL_RIGHT_BREAK (D_SYSCMD_CODE_MOTOR << 8 | 0x0b) #define D_MOTOR_ACTION_WHEEL_RIGHT_FORWARD (D_SYSCMD_CODE_MOTOR << 8 | 0x0c) #define D_MOTOR_ACTION_WHEEL_RIGHT_BACKWARD (D_SYSCMD_CODE_MOTOR << 8 | 0x0d) #define D_MOTOR_ACTION_HEAD_STOP (D_SYSCMD_CODE_MOTOR << 8 | 0x10) #define D_MOTOR_ACTION_HEAD_BREAK (D_SYSCMD_CODE_MOTOR << 8 | 0x11) #define D_MOTOR_ACTION_HEAD_LEFT (D_SYSCMD_CODE_MOTOR << 8 | 0x12) #define D_MOTOR_ACTION_HEAD_RIGHT (D_SYSCMD_CODE_MOTOR << 8 | 0x13) // 系统状态控制命令 #define D_SYSSTATUS_GET_BAT (D_SYSCMD_CODE_STATUS << 8 | 0x01) // 获取电池电量 #define D_SYSSTATUS_GET_CHARGE (D_SYSCMD_CODE_STATUS << 8 | 0x02) // 获取充电状态 static const keymap_t keymap[] = { {KEYCODE_POWER, KEY_POWER}, {KEYCODE_VOLUME_DOWN,KEY_VOLUMEDOWN}, {KEYCODE_VOLUME_UP, KEY_VOLUMEUP}, {KEYCODE_MUTE, KEY_MUTE}, {KEYCODE_HOME, KEY_HOME}, {KEYCODE_WIFI, KEY_W}, {KEYCODE_PREVIOUS, KEY_PREVIOUS}, }; #ifdef D_SUPPORT_KEY_IRQ static struct gpio_desc *gpio_irq_key; #endif struct apt32f101_priv *priv_p; struct apt32f101_priv *priv; struct apt32f101_priv { int irq; const struct apt32f101_chipdef *cdef; struct i2c_client *client; struct input_dev *input_dev; #ifdef D_SUPPORT_KTHREAD struct task_struct *pthread; #endif #ifdef D_SUUPORT_DELAYED_TASKLET struct delayed_work apt_work; struct workqueue_struct *apt_workqueue; #endif }; struct apt32f101_chipdef { u8 key_reg; // 按键值寄读取存器 u8 battery_reg; // 电池电量值读取寄存器 u8 motor_cmd_reg; // 马达命令写入寄存器 u8 sys_status_reg; // 系统状态读取寄存器 }; static const struct apt32f101_chipdef apt32f101_cdef = { .key_reg = 0x00, // 按键值寄读取存器 .battery_reg = 0x01, // 电池电量值读取寄存器 .motor_cmd_reg = 0x02, // 马达命令写入寄存器 .sys_status_reg = 0x03, // 系统状态读取寄存器 }; struct timer_list systimer; //i2c_smbus_read_byte() 从设备读取一个字节(不定义位置偏移,使用以前发起的命令的偏移) //i2c_smbus_write_byte() 从设备写入一个字节(使用以前发起的命令的偏移) //i2c_smbus_write_quick() 向设备发送一个比特 ( 取代清单 8.1 中的 Rd/Wr 位 ). //i2c_smbus_read_byte_data() 从设备指定偏移处读取一个字节 //i2c_smbus_write_byte_data() 向设备指定偏移处写入一个字节 //i2c_smbus_read_word_data() 从设备指定偏移处读取二个字节 //i2c_smbus_write_word_data() 向设备指定偏移处写入二个字节 //i2c_smbus_read_block_data() 从设备指定偏移处读取一块数据 . //i2c_smbus_write_block_data() //向设备指定偏移处写入一块数据 . (<= 32 字 /* I2C bus functions */ static int write_d8(void *client, u8 val) { return i2c_smbus_write_byte(client, val); } static int write_r8d8(void *client, u8 reg, u8 val) { return i2c_smbus_write_byte_data(client, reg, val); } static int write_r8d16(void *client, u8 reg, u16 val) { return i2c_smbus_write_word_data(client, reg, val); } static int read_d8(void *client) { return i2c_smbus_read_byte(client); } static int read_r8d8(void *client, u8 reg) { return i2c_smbus_read_byte_data(client, reg); } static int read_r8d16(void *client, u8 reg) { return i2c_smbus_read_word_data(client, reg); } static int device_i2c_rxdata( struct i2c_client *client, unsigned char *rxData, int length) { struct i2c_msg msgs[] = { { .addr = client->addr, .flags = 0, .len = 1, .buf = rxData, }, { .addr = client->addr, .flags = I2C_M_RD, .len = length, .buf = rxData, }, }; if (i2c_transfer(client->adapter, msgs, 2) < 0) { dev_err(&client->dev, "%s: transfer failed.", __func__); return -EIO; } return 0; } static int device_i2c_txdata( struct i2c_client *client, unsigned char *txData, int length) { struct i2c_msg msg[] = { { .addr = client->addr, .flags = 0, .len = length, .buf = txData, }, }; if (i2c_transfer(client->adapter, msg, 1) < 0) { dev_err(&client->dev, "%s: transfer failed.", __func__); return -EIO; } return 0; } #define LM8323_MAX_DATA 8 /* * To write, we just access the chip's address in write mode, and dump the * command and data out on the bus. The command byte and data are taken as * sequential u8s out of varargs, to a maximum of LM8323_MAX_DATA. */ static int I2C_write(struct apt32f101_priv *priv, int len, ...) { int ret, i; //va_list ap; u8 data[LM8323_MAX_DATA]; //va_start(ap, len); if (unlikely(len > LM8323_MAX_DATA)) { dev_err(&priv->client->dev, "tried to send %d bytes\n", len); //va_end(ap); return 0; } //for (i = 0; i < len; i++) // data[i] = va_arg(ap, int); //va_end(ap); /* * If the host is asleep while we send the data, we can get a NACK * back while it wakes up, so try again, once. */ ret = i2c_master_send(priv->client, data, len); if (unlikely(ret == -EREMOTEIO)) ret = i2c_master_send(priv->client, data, len); if (unlikely(ret != len)) dev_err(&priv->client->dev, "sent %d bytes of %d total\n", len, ret); return ret; } /* * To read, we first send the command byte to the chip and end the transaction, * then access the chip in read mode, at which point it will send the data. */ static int I2C_read(struct apt32f101_priv *priv, u8 cmd, u8 *buf, int len) { int ret; /* * If the host is asleep while we send the byte, we can get a NACK * back while it wakes up, so try again, once. */ ret = i2c_master_send(priv->client, &cmd, 1); if (unlikely(ret == -EREMOTEIO)) ret = i2c_master_send(priv->client, &cmd, 1); if (unlikely(ret != 1)) { dev_err(&priv->client->dev, "sending read cmd 0x%02x failed\n", cmd); return 0; } ret = i2c_master_recv(priv->client, buf, len); if (unlikely(ret != len)) dev_err(&priv->client->dev, "wanted %d bytes, got %d\n", len, ret); return ret; } static int apt32f101_smbus_read(struct apt32f101_priv *priv, u8 reg) { int ret; dev_dbg(&priv->client->dev, "read register 0x%02X=", reg); ret = i2c_smbus_read_byte_data(priv->client, reg); if (ret) { dev_err(&priv->client->dev, "register read to 0x%02X failed (error %d)", reg, ret); } return ret; } static int apt32f101_smbus_write(struct apt32f101_priv *priv, u8 reg, u8 val) { int ret; dev_dbg(&priv->client->dev, "writing register 0x%02X=0x%02X", reg, val); ret = i2c_smbus_write_byte_data(priv->client, reg, val); if (ret) { dev_err(&priv->client->dev, "register write to 0x%02X failed (error %d)", reg, ret); } return ret; } #if D_SUPPORT_CLASS_SYSFS static ssize_t action_show(struct class *clazz, struct class_attribute *attr, char *buf) { dprintk("%s:%d\n", __FUNCTION__, __LINE__); return 0; } static ssize_t action_store(struct class *clazz, struct class_attribute *attr, const char *buf, ssize_t size) { dprintk("%s:%d\n", __FUNCTION__, __LINE__); return 0; } static class_attribute motor_class_attrs[] = { __ATTR(action, S_IRUGO | S_IWUSR, action_show, action_store), __ATTR_NULL, }; static struct class motor_class = { .name = "apt32f101", .owner = THIS_MODULE, .class_attrs = motor_class_attrs, }; #endif static long apt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { u8 cmd_buf[8] = {0}; dprintk("%s:%d, cmd:0x%04x\n",__FUNCTION__, __LINE__, cmd); cmd_buf[0] = (char)((cmd >> 8) & 0xFF); cmd_buf[1] = (char)(cmd & 0xFF); dprintk("ioctl cmd = 0x%02x, data = 0x%02x\n", cmd_buf[0], cmd_buf[1]); switch(cmd_buf[0]) { case D_SYSCMD_CODE_STATUS: { if (cmd_buf[1] == D_SYSSTATUS_GET_BAT) { return read_r8d8(priv_p->client, SYSREG_BAT); } if (cmd_buf[1] == D_SYSSTATUS_GET_CHARGE) { return read_r8d8(priv_p->client, SYSREG_ACIN); } } break; case D_SYSCMD_CODE_MOTOR: { write_r8d8(priv_p->client, SYSREG_CMD_CODE, cmd_buf[0]); write_r8d8(priv_p->client, SYSREG_CMD_DATA, cmd_buf[1]); } break; default: printk("APTF32101 IOCTL UNKNOW CMD\n"); break; } #if 0 int i=0; for(i = 0; i<10; i++) { dprintk("keycode:0x%2x\n", read_r8d8(priv_p->client, 0x00)); } #endif #if 0 //read adc val int i = 0; int val = 0; val = 0; for (i=0; i<21; i++) { u16 adc=0; adc = (read_r8d8(priv_p->client, 0x19) << 8) & 0xf00; adc |= read_r8d8(priv_p->client, 0x20); val += adc; dprintk("i = %d, key adc:%d\n",i, adc); //dprintk("key adc: 0x%2x%2x\n", read_r8d8(priv_p->client, 0x00), read_r8d8(priv_p->client, 0x01)); } //dprintk(" i = %d ,key adc :%d\n",i, val/i); #endif #if 0 u8 buffer[10]; /* get xyz high/low bytes, 0x12 */ buffer[0] = 0x01; /* Read acceleration data */ if (device_i2c_rxdata(priv_p->client, buffer, 4)!= 0) { dprintk("**************************\n"); } else { dprintk("==========================\n"); } //I2C_write(priv_p, 3, 0x00, send_buf[0], send_buf[1]); //I2C_write(priv_p, 3, 0x01, send_buf[1]); //I2C_read(priv_p, 0x01, buf, 8); #endif return 0; } int apt_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) { int ret = 0; //motor_dev_t *pdev = filp->private_data; //ret = copy_to_user(buf, pdev->obsavo_status, count); return ret; } int apt_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset) { int ret = 0; //motor_dev_t *pdev = filp->private_data; //ret = copy_from_user(pdev->obsavo_status, buf, count); return ret; } static int apt_open(struct inode *inode, struct file *file) { int ret; dprintk("%s:%d\n", __FUNCTION__, __LINE__); ret = nonseekable_open(inode, file); if( ret < 0 ) return ret; file->private_data = NULL; return 0; } static int apt_release(struct inode *inode, struct file *file) { dprintk("%s:%d\n", __FUNCTION__, __LINE__); return 0; } static struct file_operations apt_fops = { .owner = THIS_MODULE, .open = apt_open, .read = apt_read, .write = apt_write, .release = apt_release, .unlocked_ioctl = apt_ioctl, }; static struct miscdevice apt_device = { .minor = MISC_DYNAMIC_MINOR, .name = "apt32f101xx", .fops = &apt_fops, }; unsigned int get_keycode_keymap(unsigned char key) { int i=0; for(i=0; i < sizeof(keymap)/sizeof(keymap_t); i++) { if(keymap[i].key_val == key) return keymap[i].key_code; } } static void apt_timer_func(unsigned long arg) { //dprintk("Timer do func \n"); #if 0 input_report_key(priv_p->input_dev, KEY_MUTE, 1); input_sync(priv_p->input_dev); //msleep(1000); input_report_key(priv_p->input_dev, KEY_MUTE, 0); input_sync(priv_p->input_dev); #endif systimer.expires = jiffies + SYSTEM_STATUS_CHECK_TIME;// msecs_to_jiffies(10); // SYSTEM_STATUS_CHECK_TIME; add_timer(&systimer); } #ifdef D_SUUPORT_DELAYED_TASKLET static void apt_work_func(struct work_struct *work) { u8 event, code, change, pressed; code = read_r8d8(priv_p->client, SYSREG_KEY_CODE); event = read_r8d8(priv_p->client, SYSREG_KEY_EVENT); //dprintk("keyevent:0x%02x, keycode:0x%02x\n", event, code); //dprintk("keycode:0x%2x\n", read_r8d8(priv_p->client, 0x00)); if(code != KEYCODE_DUMP) { // key pressed pressed = 1; change = code; input_report_key(priv_p->input_dev, get_keycode_keymap(change), 1); input_sync(priv_p->input_dev); } else if (pressed) { // key release pressed = 0; input_report_key(priv_p->input_dev, get_keycode_keymap(change), 0); input_sync(priv_p->input_dev); } else { // no event } //dprintk("keyevent:0x%02x, keycode:0x%02x\n", event, code); queue_delayed_work(priv_p->apt_workqueue, &priv_p->apt_work, 20); } #endif #ifdef D_SUPPORT_KEY_IRQ static irqreturn_t apt_key_isr(int irq, void *dev_id) { //struct apt32f101_priv *priv = (struct apt32f101_priv*)dev_id; //printk("%s enter, %s: gpio:%d, irq: %d\n", __func__, data->name, data->gpio, data->irq); dprintk("==========key irq==========\n"); queue_delayed_work(priv_p->apt_workqueue, &priv_p->apt_work, 20); return IRQ_HANDLED; } #endif #ifdef D_SUPPORT_KTHREAD int apt_thread_func(void *data) { u8 event, code, pressed, change = 0; do{ #if 0 //dprintk("==============\n"); code = read_r8d8(priv_p->client, SYSREG_KEY_CODE); event = read_r8d8(priv_p->client, SYSREG_KEY_EVENT); //dprintk("keyevent:0x%02x, keycode:0x%02x\n", event, code); //dprintk("keycode:0x%2x\n", read_r8d8(priv_p->client, 0x00)); if(code != KEYCODE_DUMP) { // key pressed pressed = 1; change = code; input_report_key(priv_p->input_dev, get_keycode_keymap(change), 1); input_sync(priv_p->input_dev); } else if (pressed) { // key release pressed = 0; input_report_key(priv_p->input_dev, get_keycode_keymap(change), 0); input_sync(priv_p->input_dev); } else { // no event } #endif msleep(100); //dprintk("usb-in :0x%2x, battery :%d\n", read_r8d8(priv_p->client, SYSREG_ACIN) , read_r8d8(priv_p->client, SYSREG_BAT)); //ssleep(1); } while(!kthread_should_stop()); } #endif static int apt32f101_parse_dt(struct device *dev, struct apt32f101_priv *priv) { struct device_node *child; int ret = 0; return ret; } static const struct of_device_id of_apt32f101_match[] = { {.compatible = "apt,apt32f101" , 0}, //.data = &apt32f101_cdef { } }; MODULE_DEVICE_TABLE(of, of_apt32f101_match); static int aptf32101_probe(struct i2c_client *client, const struct i2c_device_id *id) { const struct apt32f101_chipdef *cdef; const struct of_device_id *of_dev_id; struct device *dev = &client->dev; struct input_dev *input_dev; int count; int ret = 0; int key_irq_gpio; dprintk(&client->dev, "I2C Address: 0x%02x\n", client->addr); if (!client->dev.of_node) { dprintk("apt32f101 device tree node not found.\n"); return -ENODEV; } if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { return -ENXIO; } of_dev_id = of_match_device(of_apt32f101_match, dev); if (!of_dev_id) return -EINVAL; //cdef = of_dev_id->data; #if 0 cdef = dev_get_platdata(&client->dev); if (cdef == NULL) { dev_err(&client->dev, "platform data required error\n"); return -ENODEV; } #endif priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->client = client; priv->cdef = cdef; i2c_set_clientdata(client, priv); //设置client data //ret = apt32f101_parse_dt(dev, priv); //if (ret) // return ret; input_dev = devm_input_allocate_device(&client->dev); if (!input_dev) { dprintk(&client->dev, "failed to allocate the input device\n"); return -ENOMEM; } __set_bit(EV_KEY, input_dev->evbit); /* Enable auto repeat feature of Linux input subsystem */ if (of_property_read_bool(client->dev.of_node, "autorepeat")) __set_bit(EV_REP, input_dev->evbit); input_dev->name = "apt32f101-key"; //input_dev->phys = "keypad/input3"; input_dev->dev.parent = &client->dev; input_dev->id.bustype = BUS_I2C; //BUS_HOST; input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0001; input_dev->id.version = 0x0002; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); //input_set_drvdata(input_dev, priv); //input_set_capability(input_dev, EV_KEY, KEY_HOME); //input_set_capability(input_dev, EV_KEY, KEY_MUTE); int i=0; for(i=0; i < sizeof(keymap)/sizeof(keymap_t); i++) { input_set_capability(input_dev, EV_KEY, keymap[i].key_code); } priv->input_dev = input_dev; priv_p = priv; ret = input_register_device(input_dev); if (ret) { dprintk(&client->dev, "failed to register input device\n"); return ret; } #if D_SUPPORT_CLASS_SYSFS if (class_register(&motor_class) < 0) { dprintk("register class error.\n"); } #endif // miscdevie init ret = misc_register(&apt_device); if(ret) { dprintk("miscdevice register error.\n"); return -1; } // timer init init_timer(&systimer); systimer.function = &apt_timer_func; systimer.data = (unsigned long)&apt_device;; systimer.expires = jiffies + SYSTEM_STATUS_CHECK_TIME; add_timer(&systimer); #ifdef D_SUPPORT_KTHREAD priv_p->pthread = kthread_run(apt_thread_func, NULL, "apt32_thread-%d", 1); if (IS_ERR(priv_p->pthread)) { priv_p->pthread = NULL; } #endif #ifdef D_SUUPORT_DELAYED_TASKLET INIT_DELAYED_WORK(&priv_p->apt_work, apt_work_func); priv_p->apt_workqueue = create_workqueue("apg_workqueue"); queue_delayed_work(priv_p->apt_workqueue, &priv_p->apt_work, 20); // cancel_delayed_work_sync(&priv_p->apt_work); #endif #ifdef D_SUPPORT_KEY_IRQ dprintk("=========get key irq==========\n"); gpio_irq_key = devm_gpiod_get(dev, "key_irq", GPIOD_IN); if (IS_ERR(gpio_irq_key)){ ret = PTR_ERR(gpio_irq_key); dev_err(&dev, "Failed to gpio get irq\n"); return ret; } dprintk("=========key irq==========\n"); key_irq_gpio = gpiod_to_irq(gpio_irq_key); if (key_irq_gpio < 0){ printk("%s get key_irq_gpio error\n", client->name); } dprintk("========request irq==========\n"); // IRQ_TYPE_EDGE_FALLING IRQF_SHARED ret = devm_request_irq(dev, key_irq_gpio, apt_key_isr, IRQ_TYPE_LEVEL_HIGH, "key-isr", priv_p); if(ret){ printk("fail to request_irq err:%d\n", ret); } #endif // 发送LINUX启动完成 TO MCU write_r8d8(priv_p->client, SYSREG_LINUX_STARTED, D_ARM_LINUX_OS_STARTED_MAGIC); return 0; } static int aptf32101_remove(struct i2c_client *client) { printk("aptf32101_remove\n"); //struct apt32f101_priv *priv = i2c_getclientdata(client); #ifdef D_SUPPORT_KEY_IRQ devm_gpiod_put(&priv_p->client->dev, gpio_irq_key); #endif #ifdef D_SUPPORT_KTHREAD if (!IS_ERR(priv_p->pthread)) { kthread_stop(priv_p->pthread); } #endif #ifdef D_SUUPORT_DELAYED_TASKLET cancel_delayed_work(&priv_p->apt_work); flush_workqueue(priv_p->apt_workqueue); destroy_workqueue(priv_p->apt_workqueue); #endif #if D_SUPPORT_CLASS_SYSFS class_unregister(&motor_class); #endif input_unregister_device(priv_p->input_dev); misc_deregister(&apt_device); del_timer(&systimer); } /* * i2c-core (and modalias) requires that id_table be properly filled, * even though it is not used for DeviceTree based instantiation. */ static const struct i2c_device_id aptf32101_id[] = { { "apt32f101"}, { } }; MODULE_DEVICE_TABLE(i2c, aptf32101_id); static struct i2c_driver apt32f101_driver = { .driver = { .name = "apt32f101xx", .of_match_table = of_match_ptr(of_apt32f101_match), }, .probe = aptf32101_probe, .remove = aptf32101_remove, .id_table = aptf32101_id, }; #if 1 module_i2c_driver(apt32f101_driver); #else static int __init apt32f101_init(void) { int ret; printk("===apt32f101_init==\n"); //return i2c_register_driver(THIS_MODULE, &apt32f101_driver); ret = i2c_add_driver(&apt32f101_driver); printk("i2c_add_driver: %d\n", ret); return ret; } static void __exit apt32f101_exit(void) { return i2c_del_driver(&apt32f101_driver); } module_init(apt32f101_init); module_exit(apt32f101_exit); #endif MODULE_AUTHOR("www.globalpat.com <[email protected]>"); MODULE_DESCRIPTION("Driver APT32F101. MCU I2C devices"); /* a few non-GPL license types are also allowed */ MODULE_LICENSE("GPL v2"); dts device tree 文件描述 i2c1: i2c@02203000 { #addres



你的当前访问异常,请进行认证后继续阅读剩余内容。

分享到: