xref: /openbmc/linux/drivers/iio/light/acpi-als.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2feca56ffSGabriele Mazzotta /*
3feca56ffSGabriele Mazzotta  * ACPI Ambient Light Sensor Driver
4feca56ffSGabriele Mazzotta  *
5feca56ffSGabriele Mazzotta  * Based on ALS driver:
6feca56ffSGabriele Mazzotta  * Copyright (C) 2009 Zhang Rui <rui.zhang@intel.com>
7feca56ffSGabriele Mazzotta  *
8feca56ffSGabriele Mazzotta  * Rework for IIO subsystem:
9feca56ffSGabriele Mazzotta  * Copyright (C) 2012-2013 Martin Liska <marxin.liska@gmail.com>
10feca56ffSGabriele Mazzotta  *
11feca56ffSGabriele Mazzotta  * Final cleanup and debugging:
12feca56ffSGabriele Mazzotta  * Copyright (C) 2013-2014 Marek Vasut <marex@denx.de>
13feca56ffSGabriele Mazzotta  * Copyright (C) 2015 Gabriele Mazzotta <gabriele.mzt@gmail.com>
14feca56ffSGabriele Mazzotta  */
15feca56ffSGabriele Mazzotta 
16feca56ffSGabriele Mazzotta #include <linux/module.h>
17feca56ffSGabriele Mazzotta #include <linux/acpi.h>
18feca56ffSGabriele Mazzotta #include <linux/err.h>
1924b84444SGwendal Grignou #include <linux/irq.h>
20feca56ffSGabriele Mazzotta #include <linux/mutex.h>
21feca56ffSGabriele Mazzotta 
22feca56ffSGabriele Mazzotta #include <linux/iio/iio.h>
23feca56ffSGabriele Mazzotta #include <linux/iio/buffer.h>
2424b84444SGwendal Grignou #include <linux/iio/trigger.h>
2524b84444SGwendal Grignou #include <linux/iio/triggered_buffer.h>
2624b84444SGwendal Grignou #include <linux/iio/trigger_consumer.h>
27feca56ffSGabriele Mazzotta 
28feca56ffSGabriele Mazzotta #define ACPI_ALS_CLASS			"als"
29feca56ffSGabriele Mazzotta #define ACPI_ALS_DEVICE_NAME		"acpi-als"
30feca56ffSGabriele Mazzotta #define ACPI_ALS_NOTIFY_ILLUMINANCE	0x80
31feca56ffSGabriele Mazzotta 
32feca56ffSGabriele Mazzotta /*
33feca56ffSGabriele Mazzotta  * So far, there's only one channel in here, but the specification for
34feca56ffSGabriele Mazzotta  * ACPI0008 says there can be more to what the block can report. Like
35feca56ffSGabriele Mazzotta  * chromaticity and such. We are ready for incoming additions!
36feca56ffSGabriele Mazzotta  */
37feca56ffSGabriele Mazzotta static const struct iio_chan_spec acpi_als_channels[] = {
38feca56ffSGabriele Mazzotta 	{
39feca56ffSGabriele Mazzotta 		.type		= IIO_LIGHT,
40feca56ffSGabriele Mazzotta 		.scan_type	= {
41feca56ffSGabriele Mazzotta 			.sign		= 's',
42feca56ffSGabriele Mazzotta 			.realbits	= 32,
43feca56ffSGabriele Mazzotta 			.storagebits	= 32,
44feca56ffSGabriele Mazzotta 		},
45fa34e6ddSGabriele Mazzotta 		/* _RAW is here for backward ABI compatibility */
46fa34e6ddSGabriele Mazzotta 		.info_mask_separate	= BIT(IIO_CHAN_INFO_RAW) |
47fa34e6ddSGabriele Mazzotta 					  BIT(IIO_CHAN_INFO_PROCESSED),
48feca56ffSGabriele Mazzotta 	},
49dbd7e992SGwendal Grignou 	IIO_CHAN_SOFT_TIMESTAMP(1),
50feca56ffSGabriele Mazzotta };
51feca56ffSGabriele Mazzotta 
52feca56ffSGabriele Mazzotta /*
53feca56ffSGabriele Mazzotta  * The event buffer contains timestamp and all the data from
54feca56ffSGabriele Mazzotta  * the ACPI0008 block. There are multiple, but so far we only
55dbd7e992SGwendal Grignou  * support _ALI (illuminance): One channel, padding and timestamp.
56feca56ffSGabriele Mazzotta  */
5723633314SHartmut Knaack #define ACPI_ALS_EVT_BUFFER_SIZE		\
58dbd7e992SGwendal Grignou 	(sizeof(s32) + sizeof(s32) + sizeof(s64))
59feca56ffSGabriele Mazzotta 
60feca56ffSGabriele Mazzotta struct acpi_als {
61feca56ffSGabriele Mazzotta 	struct acpi_device	*device;
62feca56ffSGabriele Mazzotta 	struct mutex		lock;
6324b84444SGwendal Grignou 	struct iio_trigger	*trig;
64feca56ffSGabriele Mazzotta 
65dbd7e992SGwendal Grignou 	s32 evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE / sizeof(s32)]  __aligned(8);
66feca56ffSGabriele Mazzotta };
67feca56ffSGabriele Mazzotta 
68feca56ffSGabriele Mazzotta /*
69feca56ffSGabriele Mazzotta  * All types of properties the ACPI0008 block can report. The ALI, ALC, ALT
7023633314SHartmut Knaack  * and ALP can all be handled by acpi_als_read_value() below, while the ALR is
71feca56ffSGabriele Mazzotta  * special.
72feca56ffSGabriele Mazzotta  *
73feca56ffSGabriele Mazzotta  * The _ALR property returns tables that can be used to fine-tune the values
74feca56ffSGabriele Mazzotta  * reported by the other props based on the particular hardware type and it's
75feca56ffSGabriele Mazzotta  * location (it contains tables for "rainy", "bright inhouse lighting" etc.).
76feca56ffSGabriele Mazzotta  *
77feca56ffSGabriele Mazzotta  * So far, we support only ALI (illuminance).
78feca56ffSGabriele Mazzotta  */
79feca56ffSGabriele Mazzotta #define ACPI_ALS_ILLUMINANCE	"_ALI"
80feca56ffSGabriele Mazzotta #define ACPI_ALS_CHROMATICITY	"_ALC"
81feca56ffSGabriele Mazzotta #define ACPI_ALS_COLOR_TEMP	"_ALT"
82feca56ffSGabriele Mazzotta #define ACPI_ALS_POLLING	"_ALP"
83feca56ffSGabriele Mazzotta #define ACPI_ALS_TABLES		"_ALR"
84feca56ffSGabriele Mazzotta 
acpi_als_read_value(struct acpi_als * als,char * prop,s32 * val)8523633314SHartmut Knaack static int acpi_als_read_value(struct acpi_als *als, char *prop, s32 *val)
86feca56ffSGabriele Mazzotta {
87feca56ffSGabriele Mazzotta 	unsigned long long temp_val;
88feca56ffSGabriele Mazzotta 	acpi_status status;
89feca56ffSGabriele Mazzotta 
90feca56ffSGabriele Mazzotta 	status = acpi_evaluate_integer(als->device->handle, prop, NULL,
91feca56ffSGabriele Mazzotta 				       &temp_val);
92feca56ffSGabriele Mazzotta 
93feca56ffSGabriele Mazzotta 	if (ACPI_FAILURE(status)) {
9494e17d60SRafael J. Wysocki 		acpi_evaluation_failure_warn(als->device->handle, prop, status);
95feca56ffSGabriele Mazzotta 		return -EIO;
96feca56ffSGabriele Mazzotta 	}
97feca56ffSGabriele Mazzotta 
98feca56ffSGabriele Mazzotta 	*val = temp_val;
99feca56ffSGabriele Mazzotta 
100feca56ffSGabriele Mazzotta 	return 0;
101feca56ffSGabriele Mazzotta }
102feca56ffSGabriele Mazzotta 
acpi_als_notify(struct acpi_device * device,u32 event)103feca56ffSGabriele Mazzotta static void acpi_als_notify(struct acpi_device *device, u32 event)
104feca56ffSGabriele Mazzotta {
105feca56ffSGabriele Mazzotta 	struct iio_dev *indio_dev = acpi_driver_data(device);
106feca56ffSGabriele Mazzotta 	struct acpi_als *als = iio_priv(indio_dev);
107feca56ffSGabriele Mazzotta 
10824b84444SGwendal Grignou 	if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) {
109feca56ffSGabriele Mazzotta 		switch (event) {
110feca56ffSGabriele Mazzotta 		case ACPI_ALS_NOTIFY_ILLUMINANCE:
111*f700e55eSMehdi Djait 			iio_trigger_poll_nested(als->trig);
112feca56ffSGabriele Mazzotta 			break;
113feca56ffSGabriele Mazzotta 		default:
114feca56ffSGabriele Mazzotta 			/* Unhandled event */
11524b84444SGwendal Grignou 			dev_dbg(&device->dev,
11624b84444SGwendal Grignou 				"Unhandled ACPI ALS event (%08x)!\n",
117feca56ffSGabriele Mazzotta 				event);
118feca56ffSGabriele Mazzotta 		}
11924b84444SGwendal Grignou 	}
120feca56ffSGabriele Mazzotta }
121feca56ffSGabriele Mazzotta 
acpi_als_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)122feca56ffSGabriele Mazzotta static int acpi_als_read_raw(struct iio_dev *indio_dev,
123feca56ffSGabriele Mazzotta 			     struct iio_chan_spec const *chan, int *val,
124feca56ffSGabriele Mazzotta 			     int *val2, long mask)
125feca56ffSGabriele Mazzotta {
126feca56ffSGabriele Mazzotta 	struct acpi_als *als = iio_priv(indio_dev);
127feca56ffSGabriele Mazzotta 	s32 temp_val;
128feca56ffSGabriele Mazzotta 	int ret;
129feca56ffSGabriele Mazzotta 
130fa34e6ddSGabriele Mazzotta 	if ((mask != IIO_CHAN_INFO_PROCESSED) && (mask != IIO_CHAN_INFO_RAW))
131feca56ffSGabriele Mazzotta 		return -EINVAL;
132feca56ffSGabriele Mazzotta 
133feca56ffSGabriele Mazzotta 	/* we support only illumination (_ALI) so far. */
134feca56ffSGabriele Mazzotta 	if (chan->type != IIO_LIGHT)
135feca56ffSGabriele Mazzotta 		return -EINVAL;
136feca56ffSGabriele Mazzotta 
13723633314SHartmut Knaack 	ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &temp_val);
138feca56ffSGabriele Mazzotta 	if (ret < 0)
139feca56ffSGabriele Mazzotta 		return ret;
140feca56ffSGabriele Mazzotta 
141feca56ffSGabriele Mazzotta 	*val = temp_val;
142feca56ffSGabriele Mazzotta 
143feca56ffSGabriele Mazzotta 	return IIO_VAL_INT;
144feca56ffSGabriele Mazzotta }
145feca56ffSGabriele Mazzotta 
146feca56ffSGabriele Mazzotta static const struct iio_info acpi_als_info = {
147feca56ffSGabriele Mazzotta 	.read_raw		= acpi_als_read_raw,
148feca56ffSGabriele Mazzotta };
149feca56ffSGabriele Mazzotta 
acpi_als_trigger_handler(int irq,void * p)15024b84444SGwendal Grignou static irqreturn_t acpi_als_trigger_handler(int irq, void *p)
15124b84444SGwendal Grignou {
15224b84444SGwendal Grignou 	struct iio_poll_func *pf = p;
15324b84444SGwendal Grignou 	struct iio_dev *indio_dev = pf->indio_dev;
15424b84444SGwendal Grignou 	struct acpi_als *als = iio_priv(indio_dev);
15524b84444SGwendal Grignou 	s32 *buffer = als->evt_buffer;
15624b84444SGwendal Grignou 	s32 val;
15724b84444SGwendal Grignou 	int ret;
15824b84444SGwendal Grignou 
15924b84444SGwendal Grignou 	mutex_lock(&als->lock);
16024b84444SGwendal Grignou 
16124b84444SGwendal Grignou 	ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val);
16224b84444SGwendal Grignou 	if (ret < 0)
16324b84444SGwendal Grignou 		goto out;
16424b84444SGwendal Grignou 	*buffer = val;
16524b84444SGwendal Grignou 
16624b84444SGwendal Grignou 	/*
16724b84444SGwendal Grignou 	 * When coming from own trigger via polls, set polling function
16824b84444SGwendal Grignou 	 * timestamp here. Given ACPI notifier is already in a thread and call
16924b84444SGwendal Grignou 	 * function directly, there is no need to set the timestamp in the
17024b84444SGwendal Grignou 	 * notify function.
17124b84444SGwendal Grignou 	 *
17224b84444SGwendal Grignou 	 * If the timestamp was actually 0, the timestamp is set one more time.
17324b84444SGwendal Grignou 	 */
17424b84444SGwendal Grignou 	if (!pf->timestamp)
17524b84444SGwendal Grignou 		pf->timestamp = iio_get_time_ns(indio_dev);
17624b84444SGwendal Grignou 
17724b84444SGwendal Grignou 	iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp);
17824b84444SGwendal Grignou out:
17924b84444SGwendal Grignou 	mutex_unlock(&als->lock);
18024b84444SGwendal Grignou 	iio_trigger_notify_done(indio_dev->trig);
18124b84444SGwendal Grignou 
18224b84444SGwendal Grignou 	return IRQ_HANDLED;
18324b84444SGwendal Grignou }
18424b84444SGwendal Grignou 
acpi_als_add(struct acpi_device * device)185feca56ffSGabriele Mazzotta static int acpi_als_add(struct acpi_device *device)
186feca56ffSGabriele Mazzotta {
187ddaf14daSGwendal Grignou 	struct device *dev = &device->dev;
188feca56ffSGabriele Mazzotta 	struct iio_dev *indio_dev;
189ddaf14daSGwendal Grignou 	struct acpi_als *als;
19017395ce2SAlexandru Ardelean 	int ret;
191feca56ffSGabriele Mazzotta 
192ddaf14daSGwendal Grignou 	indio_dev = devm_iio_device_alloc(dev, sizeof(*als));
193feca56ffSGabriele Mazzotta 	if (!indio_dev)
194feca56ffSGabriele Mazzotta 		return -ENOMEM;
195feca56ffSGabriele Mazzotta 
196feca56ffSGabriele Mazzotta 	als = iio_priv(indio_dev);
197feca56ffSGabriele Mazzotta 
198feca56ffSGabriele Mazzotta 	device->driver_data = indio_dev;
199feca56ffSGabriele Mazzotta 	als->device = device;
200feca56ffSGabriele Mazzotta 	mutex_init(&als->lock);
201feca56ffSGabriele Mazzotta 
202feca56ffSGabriele Mazzotta 	indio_dev->name = ACPI_ALS_DEVICE_NAME;
203feca56ffSGabriele Mazzotta 	indio_dev->info = &acpi_als_info;
204feca56ffSGabriele Mazzotta 	indio_dev->channels = acpi_als_channels;
205feca56ffSGabriele Mazzotta 	indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
206feca56ffSGabriele Mazzotta 
20715ea2878SJonathan Cameron 	als->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name,
20815ea2878SJonathan Cameron 					   iio_device_id(indio_dev));
20924b84444SGwendal Grignou 	if (!als->trig)
21024b84444SGwendal Grignou 		return -ENOMEM;
21124b84444SGwendal Grignou 
21224b84444SGwendal Grignou 	ret = devm_iio_trigger_register(dev, als->trig);
21324b84444SGwendal Grignou 	if (ret)
21424b84444SGwendal Grignou 		return ret;
21524b84444SGwendal Grignou 	/*
21624b84444SGwendal Grignou 	 * Set hardware trigger by default to let events flow when
21724b84444SGwendal Grignou 	 * BIOS support notification.
21824b84444SGwendal Grignou 	 */
21924b84444SGwendal Grignou 	indio_dev->trig = iio_trigger_get(als->trig);
22024b84444SGwendal Grignou 
22124b84444SGwendal Grignou 	ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
22224b84444SGwendal Grignou 					      iio_pollfunc_store_time,
22324b84444SGwendal Grignou 					      acpi_als_trigger_handler,
22424b84444SGwendal Grignou 					      NULL);
22517395ce2SAlexandru Ardelean 	if (ret)
22617395ce2SAlexandru Ardelean 		return ret;
227feca56ffSGabriele Mazzotta 
228ddaf14daSGwendal Grignou 	return devm_iio_device_register(dev, indio_dev);
229feca56ffSGabriele Mazzotta }
230feca56ffSGabriele Mazzotta 
231feca56ffSGabriele Mazzotta static const struct acpi_device_id acpi_als_device_ids[] = {
232feca56ffSGabriele Mazzotta 	{"ACPI0008", 0},
233feca56ffSGabriele Mazzotta 	{},
234feca56ffSGabriele Mazzotta };
235feca56ffSGabriele Mazzotta 
236feca56ffSGabriele Mazzotta MODULE_DEVICE_TABLE(acpi, acpi_als_device_ids);
237feca56ffSGabriele Mazzotta 
238feca56ffSGabriele Mazzotta static struct acpi_driver acpi_als_driver = {
239feca56ffSGabriele Mazzotta 	.name	= "acpi_als",
240feca56ffSGabriele Mazzotta 	.class	= ACPI_ALS_CLASS,
241feca56ffSGabriele Mazzotta 	.ids	= acpi_als_device_ids,
242feca56ffSGabriele Mazzotta 	.ops = {
243feca56ffSGabriele Mazzotta 		.add	= acpi_als_add,
244feca56ffSGabriele Mazzotta 		.notify	= acpi_als_notify,
245feca56ffSGabriele Mazzotta 	},
246feca56ffSGabriele Mazzotta };
247feca56ffSGabriele Mazzotta 
248feca56ffSGabriele Mazzotta module_acpi_driver(acpi_als_driver);
249feca56ffSGabriele Mazzotta 
250feca56ffSGabriele Mazzotta MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
251feca56ffSGabriele Mazzotta MODULE_AUTHOR("Martin Liska <marxin.liska@gmail.com>");
252feca56ffSGabriele Mazzotta MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
253feca56ffSGabriele Mazzotta MODULE_DESCRIPTION("ACPI Ambient Light Sensor Driver");
254feca56ffSGabriele Mazzotta MODULE_LICENSE("GPL");
255