我是靠谱客的博主 平常大树,最近开发中收集的这篇文章主要介绍Linux AP3216C驱动1. 介绍2. 代码3. 设备树4. 运行,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Linux AP3216C驱动

  • 1. 介绍
  • 2. 代码
  • 3. 设备树
  • 4. 运行

AP3216C是一个近距离环境光传感器,包含了光强传感器(ALS:AmbientLight Sensor),接近传感器(PS: Proximity Sensor),还有一个红外LED(IR LED)。

1. 介绍

AP3216C是通过IIC进行操作的,操作地址为0x1E,芯片有多种工作模式使用,详细的寄存器操作可以阅读芯片手册。我这里利用IIC子系统和IIO子系统进行开发,向应用层提供的sysfs接口。

2. 代码

// SPDX-License-Identifier: GPL-2.0-only

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>

#define AP3216C_REGMAP_NAME	"ap3216c_regmap"
#define AP3216C_DRV_NAME	"ap3216c"


#define AP3216C_SYSTEMCONG	0x00	/* System Configuration */
#define AP3216C_INTSTATUS	0X01	/* Interrupt Status */
#define AP3216C_INTCLEAR	0X02	/* INT Clear Manner */
#define AP3216C_IRDATALOW	0x0A	/* IR Data Low */
#define AP3216C_IRDATAHIGH	0x0B	/* IR Data High */
#define AP3216C_ALSDATALOW	0x0C	/* ALS Data Low */
#define AP3216C_ALSDATAHIGH	0X0D	/* ALS Data High */
#define AP3216C_PSDATALOW	0X0E	/* PS Data Low */
#define AP3216C_PSDATAHIGH	0X0F	/* PS Data High */

#define AP3216C_ALSCONFIG	0X10	/* ALS Configuration */
#define AP3216C_PSCONFIG	0X20	/* PS Configuration */
#define AP3216C_PSLEDCONFIG 0X21	/* PS LED Driver */

enum ap3216c_idx {
	AP3216C_ALS,
	AP3216C_PS,
	AP3216C_IR,
};

struct ap3216c_data {
	struct i2c_client *client;
	struct iio_dev *indio_dev;
	struct mutex lock;
	struct regmap *regmap;
};

static const struct regmap_config ap3216c_regmap_config = {
	.name = AP3216C_REGMAP_NAME,
	.reg_bits = 8,
	.val_bits = 8,
};

static const unsigned long ap3216c_scan_masks[] = {
	BIT(AP3216C_ALS) | BIT(AP3216C_PS) | BIT(AP3216C_IR),
	0
};

static const struct iio_chan_spec ap3216c_channels[] = {
	/* ALS */
	{
		.type = IIO_INTENSITY,
		.modified = 1,
		.channel2 = IIO_MOD_LIGHT_BOTH,
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
		.scan_index = AP3216C_ALS,
		.scan_type = {
			.sign = 'u',
			.realbits = 16,
			.storagebits = 16,
			.endianness = IIO_LE,
		},
	},

	/* PS */
	{
		.type = IIO_PROXIMITY,
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
		.scan_index = AP3216C_PS,
		.scan_type = {
			.sign = 'u',
			.realbits = 10,
			.storagebits = 16,
			.endianness = IIO_LE,
		},
	},

	/* IR */
	{
		.type = IIO_INTENSITY,
		.modified = 1,
		.channel2 = IIO_MOD_LIGHT_IR,
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
		.scan_index = AP3216C_IR,
		.scan_type = {
			.sign = 'u',
			.realbits = 10,
			.storagebits = 16,
			.endianness = IIO_LE,
		},
	},
};

static int ap3216c_read_als(struct ap3216c_data *data, int *val)
{
	unsigned char reg[2];
	int ret;

	ret = regmap_bulk_read(data->regmap, AP3216C_ALSDATALOW, reg, 2);
	if (ret)
		return ret;

	*val = ((int)reg[1] << 8) | reg[0];

	return ret;
}

static int ap3216c_read_ps(struct ap3216c_data *data, int *val)
{
	unsigned char reg[2];
	int ret;

	ret = regmap_bulk_read(data->regmap, AP3216C_PSDATALOW, reg, 2);
	if (ret)
		return ret;

	*val = ((int)(reg[1] & 0X3F) << 4) | (reg[0] & 0X0F);

	return ret;
}

static int ap3216c_read_ir(struct ap3216c_data *data, int *val)
{
	unsigned char reg[2];
	int ret;

	ret = regmap_bulk_read(data->regmap, AP3216C_IRDATALOW, reg, 2);
	if (ret)
		return ret;

	*val = ((int)reg[1] << 2) | (reg[0] & 0X03);

	return ret;
}

#define AP3216C_SCALE(range)  (int)(((long long)range * 1000000) / 65535)
static const int ap3216c_als_scale[] = {AP3216C_SCALE(23360), AP3216C_SCALE(5840), 
										AP3216C_SCALE(1460), AP3216C_SCALE(365)};

static int ap3216c_read_als_scale(struct ap3216c_data *data, int *val)
{
	unsigned int reg;
	int ret;	

	ret = regmap_read(data->regmap, AP3216C_ALSCONFIG, &reg);
	if (ret)
		return ret;

	*val = ap3216c_als_scale[((reg & 0X30) >> 4)];

	return ret;
}

static int ap3216c_write_als_scale(struct ap3216c_data *data, int val)
{
	int i, ret = 0;	

	for (i=0; i<ARRAY_SIZE(ap3216c_als_scale); ++i)
	{
		if (ap3216c_als_scale[i] == val)
			break;
	}

	if(i == ARRAY_SIZE(ap3216c_als_scale))
	{
		ret = -EINVAL;
	}
	else
	{
		ret = regmap_write(data->regmap, AP3216C_ALSCONFIG, (unsigned char)(i<<4));
	}
		
	return ret;
}

static int ap3216c_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{
	struct ap3216c_data *data = iio_priv(indio_dev);
	int ret = -EINVAL;

	switch (mask) {
	case IIO_CHAN_INFO_RAW:
		switch (chan->type) {
		case IIO_INTENSITY:
			switch (chan->channel2) {
			case IIO_MOD_LIGHT_BOTH:
				mutex_lock(&data->lock);
				ret = ap3216c_read_als(data, val);
				mutex_unlock(&data->lock);
				if (ret)
					return ret;
				ret = IIO_VAL_INT;
				break;
			case IIO_MOD_LIGHT_IR:
				mutex_lock(&data->lock);
				ret = ap3216c_read_ir(data, val);
				mutex_unlock(&data->lock);
				if (ret)
					return ret;
				ret = IIO_VAL_INT;
				break;
			default:
				ret = -EINVAL;
				break;
			}
			break;
		case IIO_PROXIMITY:
			mutex_lock(&data->lock);
			ret = ap3216c_read_ps(data, val);
			mutex_unlock(&data->lock);
			if (ret)
				return ret;
			ret = IIO_VAL_INT;
			break;
		default:
			ret = -EINVAL;
			break;
		}
		break;
	case IIO_CHAN_INFO_SCALE:
		switch (chan->type) {
		case IIO_INTENSITY:
			mutex_lock(&data->lock);
			ret = ap3216c_read_als_scale(data, val2);
			mutex_unlock(&data->lock);
			if (ret)
				return ret;
			*val = 0;
			ret = IIO_VAL_INT_PLUS_MICRO;
			break;
		default:
			ret = -EINVAL;
			break;
		}
		break;
	default:
		ret = -EINVAL;
		break;
	}

	return ret;
}

static const struct iio_info ap3216c_info = {
	.read_raw = ap3216c_read_raw,
};

static int ap3216c_init(struct ap3216c_data *data)
{
	struct device *dev = &data->client->dev;
	int als, ps, ir;
	int ret;

	ret = regmap_write(data->regmap, AP3216C_SYSTEMCONG, 0x04); // reset ap3216c
	if (ret)
		return ret;
	
	mdelay(50); // >=10ms

	ret = regmap_write(data->regmap, AP3216C_SYSTEMCONG, 0x03); // ALS and PS+IR functions once
	if (ret)
		return ret;

	ret = ap3216c_write_als_scale(data, ap3216c_als_scale[0]); // 23360 lux / Resolution bit
	if (ret)
		return ret;

	ret = regmap_write(data->regmap, AP3216C_PSLEDCONFIG, 0x13); // LED 1 pulse, LED driver ratio 100%
	if (ret)
		return ret;

	ap3216c_read_als(data, &als);
	ap3216c_read_ps(data, &ps);
	ap3216c_read_ir(data, &ir);
	dev_info(dev, "ALS=%d, PS=%d, IR=%dn", als, ps, ir);

	return 0;
}

static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	struct iio_dev *indio_dev;
	struct ap3216c_data *data;
	int ret;

	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
	if (!indio_dev)
		return -ENOMEM;

	indio_dev->name = AP3216C_DRV_NAME;
	indio_dev->info = &ap3216c_info;
	indio_dev->modes = INDIO_DIRECT_MODE;
	indio_dev->channels = ap3216c_channels;
	indio_dev->num_channels = ARRAY_SIZE(ap3216c_channels);
	indio_dev->available_scan_masks = ap3216c_scan_masks;

	data = iio_priv(indio_dev);
	data->indio_dev = indio_dev;
	data->client = client;
	mutex_init(&data->lock);
	i2c_set_clientdata(client, indio_dev);

	data->regmap = devm_regmap_init_i2c(client, &ap3216c_regmap_config);
	if (IS_ERR(data->regmap)) {
		dev_err(&client->dev, "regmap initialization failedn");
		return PTR_ERR(data->regmap);
	}

	ret = ap3216c_init(data);
	if (ret)
		return ret;

	return iio_device_register(indio_dev);
}

static int ap3216c_remove(struct i2c_client *client)
{
	struct iio_dev *indio_dev = i2c_get_clientdata(client);

	iio_device_unregister(indio_dev);

	return 0;
}

static const struct of_device_id ap3216c_of_match[] = {
	{ .compatible = "lsc,ap3216c" },
	{ },
};
MODULE_DEVICE_TABLE(of, ap3216c_of_match);

static struct i2c_device_id ap3216c_id[] = {
	{ "ap3216c", 0 },
	{ },
};
MODULE_DEVICE_TABLE(i2c, ap3216c_id);

static struct i2c_driver ap3216c_driver = {
	.driver = {
        .owner = THIS_MODULE,
		.name	= "ap3216c",
		.of_match_table = of_match_ptr(ap3216c_of_match),
	},
	.probe	    = ap3216c_probe,
	.remove		= ap3216c_remove,
	.id_table	= ap3216c_id,
};

module_i2c_driver(ap3216c_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sonboy");
MODULE_DESCRIPTION("Driver for AP3216C");

3. 设备树

这里示例,只需要i2c设备下添加我们的子设备。

&i2c {
	ap3216c@1e {
		compatible = "lsc,ap3216c";
		reg = <0x1e>;
	};
};

4. 运行

加载驱动打印如下信息,驱动加载成功。
在这里插入图片描述
进入/sys/bus/iio/devices/iio:device0目录,你的不一定是iio:device0。有如下文件,操作这些文件即可操作设备。
在这里插入图片描述

源码获取:

git clone https://github.com/Sonboy97/linux-drivers.git

最后

以上就是平常大树为你收集整理的Linux AP3216C驱动1. 介绍2. 代码3. 设备树4. 运行的全部内容,希望文章能够帮你解决Linux AP3216C驱动1. 介绍2. 代码3. 设备树4. 运行所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部