brintos

brintos / linux-shallow public Read only

0
0
Text · 13.0 KiB · 3b92362 Raw
555 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * max44009.c - Support for MAX44009 Ambient Light Sensor4 *5 * Copyright (c) 2019 Robert Eshleman <bobbyeshleman@gmail.com>6 *7 * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX44009.pdf8 *9 * TODO: Support continuous mode and configuring from manual mode to10 *	 automatic mode.11 *12 * Default I2C address: 0x4a13 */14 15#include <linux/init.h>16#include <linux/kernel.h>17#include <linux/bits.h>18#include <linux/i2c.h>19#include <linux/iio/events.h>20#include <linux/iio/iio.h>21#include <linux/iio/sysfs.h>22#include <linux/interrupt.h>23#include <linux/module.h>24#include <linux/util_macros.h>25 26#define MAX44009_DRV_NAME "max44009"27 28/* Registers in datasheet order */29#define MAX44009_REG_INT_STATUS 0x030#define MAX44009_REG_INT_EN 0x131#define MAX44009_REG_CFG 0x232#define MAX44009_REG_LUX_HI 0x333#define MAX44009_REG_LUX_LO 0x434#define MAX44009_REG_UPPER_THR 0x535#define MAX44009_REG_LOWER_THR 0x636#define MAX44009_REG_THR_TIMER 0x737 38#define MAX44009_CFG_TIM_MASK GENMASK(2, 0)39#define MAX44009_CFG_MAN_MODE_MASK BIT(6)40 41/* The maximum rising threshold for the max44009 */42#define MAX44009_MAXIMUM_THRESHOLD 752025643 44#define MAX44009_THRESH_EXP_MASK (0xf << 4)45#define MAX44009_THRESH_EXP_RSHIFT 446#define MAX44009_THRESH_MANT_LSHIFT 447#define MAX44009_THRESH_MANT_MASK 0xf48 49#define MAX44009_UPPER_THR_MINIMUM 1550 51/* The max44009 always scales raw readings by 0.045 and is non-configurable */52#define MAX44009_SCALE_NUMERATOR 4553#define MAX44009_SCALE_DENOMINATOR 100054 55/* The fixed-point fractional multiplier for de-scaling threshold values */56#define MAX44009_FRACT_MULT 100000057 58static const u32 max44009_int_time_ns_array[] = {59	800000000,60	400000000,61	200000000,62	100000000,63	50000000, /* Manual mode only */64	25000000, /* Manual mode only */65	12500000, /* Manual mode only */66	6250000,  /* Manual mode only */67};68 69static const char max44009_int_time_str[] =70	"0.8 "71	"0.4 "72	"0.2 "73	"0.1 "74	"0.05 "75	"0.025 "76	"0.0125 "77	"0.00625";78 79struct max44009_data {80	struct i2c_client *client;81	struct mutex lock;82};83 84static const struct iio_event_spec max44009_event_spec[] = {85	{86		.type = IIO_EV_TYPE_THRESH,87		.dir = IIO_EV_DIR_RISING,88		.mask_separate = BIT(IIO_EV_INFO_VALUE) |89				 BIT(IIO_EV_INFO_ENABLE),90	},91	{92		.type = IIO_EV_TYPE_THRESH,93		.dir = IIO_EV_DIR_FALLING,94		.mask_separate = BIT(IIO_EV_INFO_VALUE) |95				 BIT(IIO_EV_INFO_ENABLE),96	},97};98 99static const struct iio_chan_spec max44009_channels[] = {100	{101		.type = IIO_LIGHT,102		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |103				      BIT(IIO_CHAN_INFO_INT_TIME),104		.event_spec = max44009_event_spec,105		.num_event_specs = ARRAY_SIZE(max44009_event_spec),106	},107};108 109static int max44009_read_int_time(struct max44009_data *data)110{111 112	int ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_CFG);113 114	if (ret < 0)115		return ret;116 117	return max44009_int_time_ns_array[ret & MAX44009_CFG_TIM_MASK];118}119 120static int max44009_write_int_time(struct max44009_data *data,121				   int val, int val2)122{123	struct i2c_client *client = data->client;124	int ret, int_time, config;125	s64 ns;126 127	ns = val * NSEC_PER_SEC + val2;128	int_time = find_closest_descending(129			ns,130			max44009_int_time_ns_array,131			ARRAY_SIZE(max44009_int_time_ns_array));132 133	ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG);134	if (ret < 0)135		return ret;136 137	config = ret;138	config &= int_time;139 140	/*141	 * To set the integration time, the device must also be in manual142	 * mode.143	 */144	config |= MAX44009_CFG_MAN_MODE_MASK;145 146	return i2c_smbus_write_byte_data(client, MAX44009_REG_CFG, config);147}148 149static int max44009_write_raw(struct iio_dev *indio_dev,150			      struct iio_chan_spec const *chan, int val,151			      int val2, long mask)152{153	struct max44009_data *data = iio_priv(indio_dev);154	int ret;155 156	if (mask == IIO_CHAN_INFO_INT_TIME && chan->type == IIO_LIGHT) {157		mutex_lock(&data->lock);158		ret = max44009_write_int_time(data, val, val2);159		mutex_unlock(&data->lock);160		return ret;161	}162	return -EINVAL;163}164 165static int max44009_write_raw_get_fmt(struct iio_dev *indio_dev,166				      struct iio_chan_spec const *chan,167				      long mask)168{169	return IIO_VAL_INT_PLUS_NANO;170}171 172static int max44009_lux_raw(u8 hi, u8 lo)173{174	int mantissa;175	int exponent;176 177	/*178	 * The mantissa consists of the low nibble of the Lux High Byte179	 * and the low nibble of the Lux Low Byte.180	 */181	mantissa = ((hi & 0xf) << 4) | (lo & 0xf);182 183	/* The exponent byte is just the upper nibble of the Lux High Byte */184	exponent = (hi >> 4) & 0xf;185 186	/*187	 * The exponent value is base 2 to the power of the raw exponent byte.188	 */189	exponent = 1 << exponent;190 191	return exponent * mantissa;192}193 194#define MAX44009_READ_LUX_XFER_LEN (4)195 196static int max44009_read_lux_raw(struct max44009_data *data)197{198	int ret;199	u8 hireg = MAX44009_REG_LUX_HI;200	u8 loreg = MAX44009_REG_LUX_LO;201	u8 lo = 0;202	u8 hi = 0;203 204	struct i2c_msg msgs[] = {205		{206			.addr = data->client->addr,207			.flags = 0,208			.len = sizeof(hireg),209			.buf = &hireg,210		},211		{212			.addr = data->client->addr,213			.flags = I2C_M_RD,214			.len = sizeof(hi),215			.buf = &hi,216		},217		{218			.addr = data->client->addr,219			.flags = 0,220			.len = sizeof(loreg),221			.buf = &loreg,222		},223		{224			.addr = data->client->addr,225			.flags = I2C_M_RD,226			.len = sizeof(lo),227			.buf = &lo,228		}229	};230 231	/*232	 * Use i2c_transfer instead of smbus read because i2c_transfer233	 * does NOT use a stop bit between address write and data read.234	 * Using a stop bit causes disjoint upper/lower byte reads and235	 * reduces accuracy.236	 */237	ret = i2c_transfer(data->client->adapter,238			   msgs, MAX44009_READ_LUX_XFER_LEN);239 240	if (ret != MAX44009_READ_LUX_XFER_LEN)241		return -EIO;242 243	return max44009_lux_raw(hi, lo);244}245 246static int max44009_read_raw(struct iio_dev *indio_dev,247			     struct iio_chan_spec const *chan, int *val,248			     int *val2, long mask)249{250	struct max44009_data *data = iio_priv(indio_dev);251	int lux_raw;252	int ret;253 254	switch (mask) {255	case IIO_CHAN_INFO_PROCESSED:256		switch (chan->type) {257		case IIO_LIGHT:258			ret = max44009_read_lux_raw(data);259			if (ret < 0)260				return ret;261			lux_raw = ret;262 263			*val = lux_raw * MAX44009_SCALE_NUMERATOR;264			*val2 = MAX44009_SCALE_DENOMINATOR;265			return IIO_VAL_FRACTIONAL;266		default:267			return -EINVAL;268		}269	case IIO_CHAN_INFO_INT_TIME:270		switch (chan->type) {271		case IIO_LIGHT:272			ret = max44009_read_int_time(data);273			if (ret < 0)274				return ret;275 276			*val2 = ret;277			*val = 0;278			return IIO_VAL_INT_PLUS_NANO;279		default:280			return -EINVAL;281		}282	default:283		return -EINVAL;284	}285}286 287static IIO_CONST_ATTR(illuminance_integration_time_available,288		      max44009_int_time_str);289 290static struct attribute *max44009_attributes[] = {291	&iio_const_attr_illuminance_integration_time_available.dev_attr.attr,292	NULL,293};294 295static const struct attribute_group max44009_attribute_group = {296	.attrs = max44009_attributes,297};298 299static int max44009_threshold_byte_from_fraction(int integral, int fractional)300{301	int mantissa, exp;302 303	if ((integral <= 0 && fractional <= 0) ||304	     integral > MAX44009_MAXIMUM_THRESHOLD ||305	     (integral == MAX44009_MAXIMUM_THRESHOLD && fractional != 0))306		return -EINVAL;307 308	/* Reverse scaling of fixed-point integral */309	mantissa = integral * MAX44009_SCALE_DENOMINATOR;310	mantissa /= MAX44009_SCALE_NUMERATOR;311 312	/* Reverse scaling of fixed-point fractional */313	mantissa += fractional / MAX44009_FRACT_MULT *314		    (MAX44009_SCALE_DENOMINATOR / MAX44009_SCALE_NUMERATOR);315 316	for (exp = 0; mantissa > 0xff; exp++)317		mantissa >>= 1;318 319	mantissa >>= 4;320	mantissa &= 0xf;321	exp <<= 4;322 323	return exp | mantissa;324}325 326static int max44009_get_thr_reg(enum iio_event_direction dir)327{328	switch (dir) {329	case IIO_EV_DIR_RISING:330		return MAX44009_REG_UPPER_THR;331	case IIO_EV_DIR_FALLING:332		return MAX44009_REG_LOWER_THR;333	default:334		return -EINVAL;335	}336}337 338static int max44009_write_event_value(struct iio_dev *indio_dev,339				      const struct iio_chan_spec *chan,340				      enum iio_event_type type,341				      enum iio_event_direction dir,342				      enum iio_event_info info,343				      int val, int val2)344{345	struct max44009_data *data = iio_priv(indio_dev);346	int reg, threshold;347 348	if (info != IIO_EV_INFO_VALUE || chan->type != IIO_LIGHT)349		return -EINVAL;350 351	threshold = max44009_threshold_byte_from_fraction(val, val2);352	if (threshold < 0)353		return threshold;354 355	reg = max44009_get_thr_reg(dir);356	if (reg < 0)357		return reg;358 359	return i2c_smbus_write_byte_data(data->client, reg, threshold);360}361 362static int max44009_read_threshold(struct iio_dev *indio_dev,363				   enum iio_event_direction dir)364{365	struct max44009_data *data = iio_priv(indio_dev);366	int byte, reg;367	int mantissa, exponent;368 369	reg = max44009_get_thr_reg(dir);370	if (reg < 0)371		return reg;372 373	byte = i2c_smbus_read_byte_data(data->client, reg);374	if (byte < 0)375		return byte;376 377	mantissa = byte & MAX44009_THRESH_MANT_MASK;378	mantissa <<= MAX44009_THRESH_MANT_LSHIFT;379 380	/*381	 * To get the upper threshold, always adds the minimum upper threshold382	 * value to the shifted byte value (see datasheet).383	 */384	if (dir == IIO_EV_DIR_RISING)385		mantissa += MAX44009_UPPER_THR_MINIMUM;386 387	/*388	 * Exponent is base 2 to the power of the threshold exponent byte389	 * value390	 */391	exponent = byte & MAX44009_THRESH_EXP_MASK;392	exponent >>= MAX44009_THRESH_EXP_RSHIFT;393 394	return (1 << exponent) * mantissa;395}396 397static int max44009_read_event_value(struct iio_dev *indio_dev,398				     const struct iio_chan_spec *chan,399				     enum iio_event_type type,400				     enum iio_event_direction dir,401				     enum iio_event_info info,402				     int *val, int *val2)403{404	int ret;405	int threshold;406 407	if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)408		return -EINVAL;409 410	ret = max44009_read_threshold(indio_dev, dir);411	if (ret < 0)412		return ret;413	threshold = ret;414 415	*val = threshold * MAX44009_SCALE_NUMERATOR;416	*val2 = MAX44009_SCALE_DENOMINATOR;417 418	return IIO_VAL_FRACTIONAL;419}420 421static int max44009_write_event_config(struct iio_dev *indio_dev,422				       const struct iio_chan_spec *chan,423				       enum iio_event_type type,424				       enum iio_event_direction dir,425				       int state)426{427	struct max44009_data *data = iio_priv(indio_dev);428	int ret;429 430	if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)431		return -EINVAL;432 433	ret = i2c_smbus_write_byte_data(data->client,434					MAX44009_REG_INT_EN, state);435	if (ret < 0)436		return ret;437 438	/*439	 * Set device to trigger interrupt immediately upon exceeding440	 * the threshold limit.441	 */442	return i2c_smbus_write_byte_data(data->client,443					 MAX44009_REG_THR_TIMER, 0);444}445 446static int max44009_read_event_config(struct iio_dev *indio_dev,447				      const struct iio_chan_spec *chan,448				      enum iio_event_type type,449				      enum iio_event_direction dir)450{451	struct max44009_data *data = iio_priv(indio_dev);452 453	if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)454		return -EINVAL;455 456	return i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_EN);457}458 459static const struct iio_info max44009_info = {460	.read_raw = max44009_read_raw,461	.write_raw = max44009_write_raw,462	.write_raw_get_fmt = max44009_write_raw_get_fmt,463	.read_event_value = max44009_read_event_value,464	.read_event_config = max44009_read_event_config,465	.write_event_value = max44009_write_event_value,466	.write_event_config = max44009_write_event_config,467	.attrs = &max44009_attribute_group,468};469 470static irqreturn_t max44009_threaded_irq_handler(int irq, void *p)471{472	struct iio_dev *indio_dev = p;473	struct max44009_data *data = iio_priv(indio_dev);474	int ret;475 476	ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_STATUS);477	if (ret) {478		iio_push_event(indio_dev,479			       IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,480						    IIO_EV_TYPE_THRESH,481						    IIO_EV_DIR_EITHER),482			       iio_get_time_ns(indio_dev));483 484		return IRQ_HANDLED;485	}486 487	return IRQ_NONE;488}489 490static int max44009_probe(struct i2c_client *client)491{492	struct max44009_data *data;493	struct iio_dev *indio_dev;494	int ret;495 496	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));497	if (!indio_dev)498		return -ENOMEM;499 500	data = iio_priv(indio_dev);501	i2c_set_clientdata(client, indio_dev);502	data->client = client;503	indio_dev->info = &max44009_info;504	indio_dev->modes = INDIO_DIRECT_MODE;505	indio_dev->name = MAX44009_DRV_NAME;506	indio_dev->channels = max44009_channels;507	indio_dev->num_channels = ARRAY_SIZE(max44009_channels);508	mutex_init(&data->lock);509 510	/* Clear any stale interrupt bit */511	ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG);512	if (ret < 0)513		return ret;514 515	if (client->irq > 0) {516		ret = devm_request_threaded_irq(&client->dev, client->irq,517						NULL,518						max44009_threaded_irq_handler,519						IRQF_TRIGGER_FALLING |520						IRQF_ONESHOT | IRQF_SHARED,521						"max44009_event",522						indio_dev);523		if (ret < 0)524			return ret;525	}526 527	return devm_iio_device_register(&client->dev, indio_dev);528}529 530static const struct of_device_id max44009_of_match[] = {531	{ .compatible = "maxim,max44009" },532	{ }533};534MODULE_DEVICE_TABLE(of, max44009_of_match);535 536static const struct i2c_device_id max44009_id[] = {537	{ "max44009" },538	{ }539};540MODULE_DEVICE_TABLE(i2c, max44009_id);541 542static struct i2c_driver max44009_driver = {543	.driver = {544		.name = MAX44009_DRV_NAME,545		.of_match_table = max44009_of_match,546	},547	.probe = max44009_probe,548	.id_table = max44009_id,549};550module_i2c_driver(max44009_driver);551 552MODULE_AUTHOR("Robert Eshleman <bobbyeshleman@gmail.com>");553MODULE_LICENSE("GPL v2");554MODULE_DESCRIPTION("MAX44009 ambient light sensor driver");555