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>;
};
};
系统启动后会创建/sys/devices/platform/soc/2203000.i2c/i2c-1/1-005a
如果没成功创建设备节点,加载驱动时probe不会调用
#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;
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
static int I2C_write(struct apt32f101_priv *priv, int len, ...)
{
int ret, i;
u8 data[LM8323_MAX_DATA];
if (unlikely(len > LM8323_MAX_DATA)) {
dev_err(&priv->client->dev, "tried to send %d bytes\n", len);
return 0;
}
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;
}
static int I2C_read(struct apt32f101_priv *priv, u8 cmd, u8 *buf, int len)
{
int ret;
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);
}
#endif
#if 0
u8 buffer[10];
buffer[0] = 0x01;
if (device_i2c_rxdata(priv_p->client, buffer, 4)!= 0)
{
dprintk("**************************\n");
} else {
dprintk("==========================\n");
}
#endif
return 0;
}
int apt_read(struct file *filp, char __user *buf, size_t count, loff_t *offset)
{
int ret = 0;
return ret;
}
int apt_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset)
{
int ret = 0;
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)
{
#if 0
input_report_key(priv_p->input_dev, KEY_MUTE, 1);
input_sync(priv_p->input_dev);
input_report_key(priv_p->input_dev, KEY_MUTE, 0);
input_sync(priv_p->input_dev);
#endif
systimer.expires = jiffies + 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);
if(code != KEYCODE_DUMP) {
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) {
pressed = 0;
input_report_key(priv_p->input_dev, get_keycode_keymap(change), 0);
input_sync(priv_p->input_dev);
} else {
}
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)
{
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
code = read_r8d8(priv_p->client, SYSREG_KEY_CODE);
event = read_r8d8(priv_p->client, SYSREG_KEY_EVENT);
if(code != KEYCODE_DUMP) {
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) {
pressed = 0;
input_report_key(priv_p->input_dev, get_keycode_keymap(change), 0);
input_sync(priv_p->input_dev);
} else {
}
#endif
msleep(100);
} 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},
{ }
};
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;
#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);
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);
if (of_property_read_bool(client->dev.of_node, "autorepeat"))
__set_bit(EV_REP, input_dev->evbit);
input_dev->name = "apt32f101-key";
input_dev->dev.parent = &client->dev;
input_dev->id.bustype = BUS_I2C;
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);
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
ret = misc_register(&apt_device);
if(ret)
{
dprintk("miscdevice register error.\n");
return -1;
}
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);
#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");
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
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");
#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);
}
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");
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");
MODULE_LICENSE("GPL v2");