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