tmd26353接近传感器之i2c驱动改进,只做思路记录,代码未验证

本文介绍了如何使用Linux内核API开发TMD26353 proximity sensor driver,包括I2C通信、GPIO配置、中断处理和系统文件操作。详细展示了读写寄存器、初始化芯片ID以及处理ADC数据的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/of_device.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>

#include <linux/iio/trigger_consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>

#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/slab.h>

#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>

/*********************************************
Register
**********************************************/
#define TMD26353_ENABLE_PROXIMITY  		 	 0x80  
#define TMD26353_PRATE						 0x82	//采样时间PRATE
#define TMD26353_PCFG0						 0x8E	//增益
#define TMD26353_PCFG1						 0x8F
#define TMD26353_DEVICE_ID			 0x92
#define TMD26353_PDATAL						 0x9C   //ADC
#define TMD26353_PDATAH						 0x9D
#define TMD26353_CFG6_APC				     0xAE   //使能APC
#define TMD26353_POFFSETL					 0xC0	自动校准
#define TMD26353_POFFSETH					 0xC1
#define TMD26353_CALIBCFG					 0xD9	//样本数量CALIBCFG



struct tmd26353_data {
    struct i2c_client *client;
    struct mutex lock;
	int irq_pin;
	unsigned int irq_111 ;
	unsigned int irq_num;
	u32  read_adc;
	u32  read_enable;
	struct work_struct pen_event_work;
	struct workqueue_struct *tmd26353_workqueue;

};

static struct tmd26353_data tmd26353;
static struct task_struct *p_data_task;

enum tmd26353_register_name {
    REG_PDATA = 0,
	REG_DEVICE_ID = 1,
	REG_ENABLE = 2,
	REG_POFFSET = 3,
	REG_PRATE = 4,
	REG_CALIBCFG = 5,
	REG_CFG6_APC = 6,
	REG_PCFG0 = 7,
	REG_PCFG1 = 8,
};
struct tmd26353_register_desc {
    u8 msb;
    u8 lsb;
};
static const struct tmd26353_register_desc tmd26353_registers[] = {
    [REG_PDATA] = {
		.msb = TMD26353_PDATAH,
		.lsb = TMD26353_PDATAL,
	},
	[REG_DEVICE_ID] = {
		.lsb = TMD26353_DEVICE_ID,
	},
	[REG_ENABLE] = {
		.lsb = TMD26353_ENABLE_PROXIMITY,
	},
	[REG_POFFSET] = {
		.msb = TMD26353_POFFSETH,
		.lsb = TMD26353_POFFSETL,
	},
	[REG_PRATE] = {
		.lsb = TMD26353_PRATE,
	},
	[REG_CALIBCFG] = {
		.lsb = TMD26353_CALIBCFG,
	},
	[REG_CFG6_APC] = {
		.lsb = TMD26353_CFG6_APC,
	},
	[REG_PCFG0] = {
		.lsb = TMD26353_PCFG0,
	},
	[REG_PCFG1] = {
		.lsb = TMD26353_PCFG1,
	},
};

static int tmd26353_register_read_single(struct tmd26353_data *data,
                        enum tmd26353_register_name name,u32 *val)
{
    const struct tmd26353_register_desc *reg = &tmd26353_registers[name];
    u8 msb = 0,lsb = 0;
    s32 ret;

    mutex_lock(&data->lock);
    if(reg->msb){
        ret = i2c_smbus_read_byte_data(data->client,reg->msb);
        if(ret < 0){
            printk("read msb failed %d \n",ret);
            goto error;
        }
        msb = ret;
    }
    if(reg->lsb){
        ret = i2c_smbus_read_byte_data(data->client,reg->lsb);
        if(ret < 0){
            printk("read lsb failed %d \n",ret);
            goto error;
        }
        lsb = ret;
    }
    mutex_unlock(&data->lock);
    *val = (msb << 8) + lsb;
    return 0;

error:
    mutex_unlock(&data->lock);
    return ret;
}

static u32 tmd26353_register_write_single(struct tmd26353_data *data, 
                        enum tmd26353_register_name name,u32 value)
{
    const struct tmd26353_register_desc *reg = &tmd26353_registers[name];
    u8 msb,lsb;
    int ret;
    if(!reg->msb && value > U8_MAX){
        printk("value > U8_MAX \n");
        return -EINVAL;
    }
    if(value > U16_MAX){
        printk("value > U16_MAX \n");
        return -EINVAL;
    }
    if(!reg->msb){
        lsb = value & 0xff;
    }else{
        msb = (value >> 8) & 0xff;
        lsb = value & 0xff;
    }
    mutex_lock(&data->lock);
    if(reg->msb){
        ret = i2c_smbus_write_byte_data(data->client, reg->msb,msb);
        if(ret < 0){
            printk("write msb failed %d \n",ret);
            goto error;
        }
    }
    if(reg->lsb){
        ret = i2c_smbus_write_byte_data(data->client, reg->lsb,lsb);
        if(ret < 0){
            printk("write lsb failed %d \n",ret);
                goto error;
        }
    }
    mutex_unlock(&data->lock);
    return 0;

error:
    mutex_unlock(&data->lock);
    return ret;
}


//read adc
static ssize_t control_show_read_adc(struct device *dev, struct device_attribute *attr, char *buf)
{
	//tmd26353.read_adc = reg_value;
	return sprintf(buf, "%d\n", tmd26353.read_adc);
}
static ssize_t control_store_read_adc(struct device* dev,  struct device_attribute *attr, const char *buf, size_t count)
{ 
    printk("read_adc =%d \n",tmd26353.read_adc);
    return count;

}

static DEVICE_ATTR(read_adc,0664, control_show_read_adc,control_store_read_adc);

static struct attribute *tmd26353_attributes[] = {
	&dev_attr_read_adc.attr,
	//&dev_attr_set_state.attr,
	NULL
};

static const struct attribute_group tmd26353_attr_group = {
	.attrs = tmd26353_attributes,
};


//Funcktion/
static int tmd26353_verify_chip_id(struct tmd26353_data *data)//struct i2c_client *client
{
	u32 reg_value;
	int ret;
	ret = tmd26353_register_read_single(data,REG_DEVICE_ID,&reg_value);
	if(ret){
		return ret;
	}
	printk("DEVICE_ID = %x \n",reg_value);
    return 0;
}

static int tmd26353_init(struct tmd26353_data *data)
{
	
	u32 reg_value[] = {0x15,0x1F,0x54,0x3F,0x3F,0x8F,0x60};
	int ret,length,i;
	unsigned int name[] = {REG_ENABLE,REG_PRATE,REG_CALIBCFG,REG_POFFSET,
									REG_CFG6_APC,REG_PCFG0,REG_PCFG1};
	length = sizeof(reg_value)/sizeof(reg_value[0]);
	for(i=0;i<length;++i){
		ret = tmd26353_register_write_single(data,name[i],reg_value[i]);//使能
		if(ret){
			continue;
		}
		printk("name[%d]= %d,reg_value=%x\n",i,name[i],reg_value[i]);
	}
	return 0;
}

static void tmd26353_pen_irq_work(struct work_struct *work)
{
	int ret = -1;
	u32 reg_value;
	ret = tmd26353_register_read_single(&tmd26353,REG_PDATA,&reg_value); //ADC
	if(ret){
		printk("tmd26353_pen_irq_work\n");
	}
	//printk("REG_PDATA ADC= %d\n",reg_value);
	tmd26353.read_adc = reg_value;
	msleep(20);
}

static irqreturn_t tmd26353_handler(int irq, void *dev_id)
{/*
	int ret;
	u32 reg_value;
	ret = tmd26353_register_read_single(&tmd26353,REG_PDATA,&reg_value); //ADC
	if(ret){
		return ret;
	}
	printk("REG_PDATA ADC= %d\n",reg_value);*/
	struct tmd26353_data *data = dev_id;
	msleep(500);
	printk("---------------enter tmd26353_handler----------------- \n");
	
	if(!work_pending(&data->pen_event_work)){
		queue_work(data->tmd26353_workqueue,&data->pen_event_work);
	}
	
	return IRQ_HANDLED;
}

static int tmd26353_irq_init(struct i2c_client *client,struct tmd26353_data *dev)
{
	int ret;
	
	dev->irq_pin = of_get_named_gpio_flags(client->dev.of_node, "anan-gpios", 0, NULL);
	tmd26353.irq_pin = dev->irq_pin;
	if(gpio_is_valid(dev->irq_pin)){
		ret = devm_gpio_request_one(&client->dev,dev->irq_pin,GPIOF_IN,"tmd26353_irq");
		if(ret){
			dev_err(&client->dev,"Failed to request GPIO %d, error %d\n",dev->irq_pin, ret);
			return ret;
		}	
	}
	//gpio_direction_input(dev->irq_pin);
	tmd26353.irq_num = gpio_to_irq(dev->irq_pin);
	ret = devm_request_threaded_irq(&client->dev,tmd26353.irq_num,tmd26353_handler,NULL,
									IRQ_TYPE_LEVEL_LOW,client->name,dev);
	if(ret){
		dev_err(&client->dev, "Unable to request p-sensor IRQ.\n");
		return ret;
	}
	
	return 0;
	
}
/*
static int pdata_thread(void *data)
{
	int ret;
	u32 reg_value;
	while(!kthread_should_stop()){
		ret = tmd26353_register_read_single(&tmd26353,REG_PDATA,&reg_value); //ADC
		if(ret){
			continue;
		}
		//printk("REG_PDATA ADC= %d\n",reg_value);
		tmd26353.read_adc = reg_value;
		msleep(200);
		//printk("---------------enter pdata_thread----------------- \n");
	}
	return 0;
}*/
  
static int tmd26353_probe(struct i2c_client *client,
                const struct i2c_device_id *id)
{
	int ret;
	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
	struct tmd26353_data *data;
	
	
	printk("----------tmd26353_probe------------ \n");
	if(!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE
					    | I2C_FUNC_SMBUS_READ_BYTE_DATA)){
		ret = -EIO;
		dev_err(&client->dev,"i2c_check_functionality failed \n");
		goto exit;
	}
	data = kzalloc(sizeof(struct tmd26353_data), GFP_KERNEL);
	if(!data){
		ret = -EIO;
		dev_err(&client->dev,"kzalloc failed \n");
		goto exit;
	}
	data->client = client;
	tmd26353.client = client;
	i2c_set_clientdata(client,data);
	mutex_init(&data->lock);
	
	
	ret = tmd26353_verify_chip_id(data);//client
	if(ret){
		dev_err(&client->dev,"tmd26353_verify_chip_id failed %d \n",ret);
		goto exit_kfree;
	}
	ret = tmd26353_init(data);
	if(ret){
		dev_err(&client->dev,"tmd26353_init failed %d \n",ret);
		goto exit_kfree;
	}
	///
	INIT_WORK(&data->pen_event_work,tmd26353_pen_irq_work);
	data->tmd26353_workqueue = create_singlethread_workqueue(dev_name(&client->dev));
	if(!data->tmd26353_workqueue){
		dev_err(&client->dev,"tmd26353_workqueue failed %d \n",ret);
		goto exit_kfree;
	}
	
	//irq
	ret = tmd26353_irq_init(client,data);
	if(ret){
		dev_err(&client->dev,"tmd26353_irq_init failed %d \n",ret);
		goto irq_failed;
	}
	/*
	//thread
	p_data_task = kthread_create(pdata_thread,NULL,"pdata_thread");
	if(IS_ERR(p_data_task)){
		dev_err(&client->dev,"kthread_create failed \n");
		ret = PTR_ERR(p_data_task);
		p_data_task = NULL;
		goto exit_kfree;
	}
	wake_up_process(p_data_task);*/
	/* Register sysfs hooks */
	ret = sysfs_create_group(&client->dev.kobj, &tmd26353_attr_group);
	if (ret)
		goto sysfs_create_group_failed;

	
	
	return 0;

irq_failed:
sysfs_create_group_failed:
	cancel_work_sync(&data->pen_event_work);
	destroy_workqueue(data->tmd26353_workqueue);
exit_kfree:
	i2c_set_clientdata(client, NULL);
	kfree(data);
exit:
	return ret;
}

static int tmd26353_remove(struct i2c_client *client)
{
    //struct iio_dev *indio_dev = i2c_get_clientdata(client);
    //struct tmd26353_data *data = iio_priv(indio_dev);
    //iio_device_unregister(indio_dev);
    //iio_device_free(indio_dev);
	//free_irq(tmd26353.irq_num,&tmd26353);
	//kthread_stop(p_data_task);
	//p_data_task = NULL;
	struct tmd26353_data *data = i2c_get_clientdata(client);
	sysfs_remove_group(&client->dev.kobj, &tmd26353_attr_group);
	cancel_work_sync(&data->pen_event_work);
	destroy_workqueue(data->tmd26353_workqueue);
	kfree(data);

    return 0;
};
static const struct i2c_device_id tmd26353_id[] = {
    {"tmd26353",0},
    {}
};
MODULE_DEVICE_TABLE(i2c,tmd26353_id);

static const struct of_device_id tmd26353_of_match[] = {
    {.compatible = "p-sensor,tmd26353",},
    {}
};
MODULE_DEVICE_TABLE(of,tmd26353_of_match);

static struct i2c_driver tmd26353_driver = {
    .driver = {
        .name = "tmd26353",
        .of_match_table = of_match_ptr(tmd26353_of_match),
    },
    .probe = tmd26353_probe,
    .remove = tmd26353_remove,
    .id_table = tmd26353_id,
};
module_i2c_driver(tmd26353_driver);

MODULE_AUTHOR("A_NAN");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("TMD26353 Proximity driver");

在学习中进步,如有错误,请多多批评指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeAmmon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值