cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

iqs621-als.c (15206B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Azoteq IQS621/622 Ambient Light Sensors
      4 *
      5 * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
      6 */
      7
      8#include <linux/device.h>
      9#include <linux/iio/events.h>
     10#include <linux/iio/iio.h>
     11#include <linux/kernel.h>
     12#include <linux/mfd/iqs62x.h>
     13#include <linux/module.h>
     14#include <linux/mutex.h>
     15#include <linux/notifier.h>
     16#include <linux/platform_device.h>
     17#include <linux/regmap.h>
     18
     19#define IQS621_ALS_FLAGS_LIGHT			BIT(7)
     20#define IQS621_ALS_FLAGS_RANGE			GENMASK(3, 0)
     21
     22#define IQS621_ALS_UI_OUT			0x17
     23
     24#define IQS621_ALS_THRESH_DARK			0x80
     25#define IQS621_ALS_THRESH_LIGHT			0x81
     26
     27#define IQS622_IR_RANGE				0x15
     28#define IQS622_IR_FLAGS				0x16
     29#define IQS622_IR_FLAGS_TOUCH			BIT(1)
     30#define IQS622_IR_FLAGS_PROX			BIT(0)
     31
     32#define IQS622_IR_UI_OUT			0x17
     33
     34#define IQS622_IR_THRESH_PROX			0x91
     35#define IQS622_IR_THRESH_TOUCH			0x92
     36
     37struct iqs621_als_private {
     38	struct iqs62x_core *iqs62x;
     39	struct iio_dev *indio_dev;
     40	struct notifier_block notifier;
     41	struct mutex lock;
     42	bool light_en;
     43	bool range_en;
     44	bool prox_en;
     45	u8 als_flags;
     46	u8 ir_flags_mask;
     47	u8 ir_flags;
     48	u8 thresh_light;
     49	u8 thresh_dark;
     50	u8 thresh_prox;
     51};
     52
     53static int iqs621_als_init(struct iqs621_als_private *iqs621_als)
     54{
     55	struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
     56	unsigned int event_mask = 0;
     57	int ret;
     58
     59	switch (iqs621_als->ir_flags_mask) {
     60	case IQS622_IR_FLAGS_TOUCH:
     61		ret = regmap_write(iqs62x->regmap, IQS622_IR_THRESH_TOUCH,
     62				   iqs621_als->thresh_prox);
     63		break;
     64
     65	case IQS622_IR_FLAGS_PROX:
     66		ret = regmap_write(iqs62x->regmap, IQS622_IR_THRESH_PROX,
     67				   iqs621_als->thresh_prox);
     68		break;
     69
     70	default:
     71		ret = regmap_write(iqs62x->regmap, IQS621_ALS_THRESH_LIGHT,
     72				   iqs621_als->thresh_light);
     73		if (ret)
     74			return ret;
     75
     76		ret = regmap_write(iqs62x->regmap, IQS621_ALS_THRESH_DARK,
     77				   iqs621_als->thresh_dark);
     78	}
     79
     80	if (ret)
     81		return ret;
     82
     83	if (iqs621_als->light_en || iqs621_als->range_en)
     84		event_mask |= iqs62x->dev_desc->als_mask;
     85
     86	if (iqs621_als->prox_en)
     87		event_mask |= iqs62x->dev_desc->ir_mask;
     88
     89	return regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
     90				  event_mask, 0);
     91}
     92
     93static int iqs621_als_notifier(struct notifier_block *notifier,
     94			       unsigned long event_flags, void *context)
     95{
     96	struct iqs62x_event_data *event_data = context;
     97	struct iqs621_als_private *iqs621_als;
     98	struct iio_dev *indio_dev;
     99	bool light_new, light_old;
    100	bool prox_new, prox_old;
    101	u8 range_new, range_old;
    102	s64 timestamp;
    103	int ret;
    104
    105	iqs621_als = container_of(notifier, struct iqs621_als_private,
    106				  notifier);
    107	indio_dev = iqs621_als->indio_dev;
    108	timestamp = iio_get_time_ns(indio_dev);
    109
    110	mutex_lock(&iqs621_als->lock);
    111
    112	if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) {
    113		ret = iqs621_als_init(iqs621_als);
    114		if (ret) {
    115			dev_err(indio_dev->dev.parent,
    116				"Failed to re-initialize device: %d\n", ret);
    117			ret = NOTIFY_BAD;
    118		} else {
    119			ret = NOTIFY_OK;
    120		}
    121
    122		goto err_mutex;
    123	}
    124
    125	if (!iqs621_als->light_en && !iqs621_als->range_en &&
    126	    !iqs621_als->prox_en) {
    127		ret = NOTIFY_DONE;
    128		goto err_mutex;
    129	}
    130
    131	/* IQS621 only */
    132	light_new = event_data->als_flags & IQS621_ALS_FLAGS_LIGHT;
    133	light_old = iqs621_als->als_flags & IQS621_ALS_FLAGS_LIGHT;
    134
    135	if (iqs621_als->light_en && light_new && !light_old)
    136		iio_push_event(indio_dev,
    137			       IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
    138						    IIO_EV_TYPE_THRESH,
    139						    IIO_EV_DIR_RISING),
    140			       timestamp);
    141	else if (iqs621_als->light_en && !light_new && light_old)
    142		iio_push_event(indio_dev,
    143			       IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
    144						    IIO_EV_TYPE_THRESH,
    145						    IIO_EV_DIR_FALLING),
    146			       timestamp);
    147
    148	/* IQS621 and IQS622 */
    149	range_new = event_data->als_flags & IQS621_ALS_FLAGS_RANGE;
    150	range_old = iqs621_als->als_flags & IQS621_ALS_FLAGS_RANGE;
    151
    152	if (iqs621_als->range_en && (range_new > range_old))
    153		iio_push_event(indio_dev,
    154			       IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
    155						    IIO_EV_TYPE_CHANGE,
    156						    IIO_EV_DIR_RISING),
    157			       timestamp);
    158	else if (iqs621_als->range_en && (range_new < range_old))
    159		iio_push_event(indio_dev,
    160			       IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
    161						    IIO_EV_TYPE_CHANGE,
    162						    IIO_EV_DIR_FALLING),
    163			       timestamp);
    164
    165	/* IQS622 only */
    166	prox_new = event_data->ir_flags & iqs621_als->ir_flags_mask;
    167	prox_old = iqs621_als->ir_flags & iqs621_als->ir_flags_mask;
    168
    169	if (iqs621_als->prox_en && prox_new && !prox_old)
    170		iio_push_event(indio_dev,
    171			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
    172						    IIO_EV_TYPE_THRESH,
    173						    IIO_EV_DIR_RISING),
    174			       timestamp);
    175	else if (iqs621_als->prox_en && !prox_new && prox_old)
    176		iio_push_event(indio_dev,
    177			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
    178						    IIO_EV_TYPE_THRESH,
    179						    IIO_EV_DIR_FALLING),
    180			       timestamp);
    181
    182	iqs621_als->als_flags = event_data->als_flags;
    183	iqs621_als->ir_flags = event_data->ir_flags;
    184	ret = NOTIFY_OK;
    185
    186err_mutex:
    187	mutex_unlock(&iqs621_als->lock);
    188
    189	return ret;
    190}
    191
    192static void iqs621_als_notifier_unregister(void *context)
    193{
    194	struct iqs621_als_private *iqs621_als = context;
    195	struct iio_dev *indio_dev = iqs621_als->indio_dev;
    196	int ret;
    197
    198	ret = blocking_notifier_chain_unregister(&iqs621_als->iqs62x->nh,
    199						 &iqs621_als->notifier);
    200	if (ret)
    201		dev_err(indio_dev->dev.parent,
    202			"Failed to unregister notifier: %d\n", ret);
    203}
    204
    205static int iqs621_als_read_raw(struct iio_dev *indio_dev,
    206			       struct iio_chan_spec const *chan,
    207			       int *val, int *val2, long mask)
    208{
    209	struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
    210	struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
    211	int ret;
    212	__le16 val_buf;
    213
    214	switch (chan->type) {
    215	case IIO_INTENSITY:
    216		ret = regmap_read(iqs62x->regmap, chan->address, val);
    217		if (ret)
    218			return ret;
    219
    220		*val &= IQS621_ALS_FLAGS_RANGE;
    221		return IIO_VAL_INT;
    222
    223	case IIO_PROXIMITY:
    224	case IIO_LIGHT:
    225		ret = regmap_raw_read(iqs62x->regmap, chan->address, &val_buf,
    226				      sizeof(val_buf));
    227		if (ret)
    228			return ret;
    229
    230		*val = le16_to_cpu(val_buf);
    231		return IIO_VAL_INT;
    232
    233	default:
    234		return -EINVAL;
    235	}
    236}
    237
    238static int iqs621_als_read_event_config(struct iio_dev *indio_dev,
    239					const struct iio_chan_spec *chan,
    240					enum iio_event_type type,
    241					enum iio_event_direction dir)
    242{
    243	struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
    244	int ret;
    245
    246	mutex_lock(&iqs621_als->lock);
    247
    248	switch (chan->type) {
    249	case IIO_LIGHT:
    250		ret = iqs621_als->light_en;
    251		break;
    252
    253	case IIO_INTENSITY:
    254		ret = iqs621_als->range_en;
    255		break;
    256
    257	case IIO_PROXIMITY:
    258		ret = iqs621_als->prox_en;
    259		break;
    260
    261	default:
    262		ret = -EINVAL;
    263	}
    264
    265	mutex_unlock(&iqs621_als->lock);
    266
    267	return ret;
    268}
    269
    270static int iqs621_als_write_event_config(struct iio_dev *indio_dev,
    271					 const struct iio_chan_spec *chan,
    272					 enum iio_event_type type,
    273					 enum iio_event_direction dir,
    274					 int state)
    275{
    276	struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
    277	struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
    278	unsigned int val;
    279	int ret;
    280
    281	mutex_lock(&iqs621_als->lock);
    282
    283	ret = regmap_read(iqs62x->regmap, iqs62x->dev_desc->als_flags, &val);
    284	if (ret)
    285		goto err_mutex;
    286	iqs621_als->als_flags = val;
    287
    288	switch (chan->type) {
    289	case IIO_LIGHT:
    290		ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
    291					 iqs62x->dev_desc->als_mask,
    292					 iqs621_als->range_en || state ? 0 :
    293									 0xFF);
    294		if (!ret)
    295			iqs621_als->light_en = state;
    296		break;
    297
    298	case IIO_INTENSITY:
    299		ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
    300					 iqs62x->dev_desc->als_mask,
    301					 iqs621_als->light_en || state ? 0 :
    302									 0xFF);
    303		if (!ret)
    304			iqs621_als->range_en = state;
    305		break;
    306
    307	case IIO_PROXIMITY:
    308		ret = regmap_read(iqs62x->regmap, IQS622_IR_FLAGS, &val);
    309		if (ret)
    310			goto err_mutex;
    311		iqs621_als->ir_flags = val;
    312
    313		ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
    314					 iqs62x->dev_desc->ir_mask,
    315					 state ? 0 : 0xFF);
    316		if (!ret)
    317			iqs621_als->prox_en = state;
    318		break;
    319
    320	default:
    321		ret = -EINVAL;
    322	}
    323
    324err_mutex:
    325	mutex_unlock(&iqs621_als->lock);
    326
    327	return ret;
    328}
    329
    330static int iqs621_als_read_event_value(struct iio_dev *indio_dev,
    331				       const struct iio_chan_spec *chan,
    332				       enum iio_event_type type,
    333				       enum iio_event_direction dir,
    334				       enum iio_event_info info,
    335				       int *val, int *val2)
    336{
    337	struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
    338	int ret = IIO_VAL_INT;
    339
    340	mutex_lock(&iqs621_als->lock);
    341
    342	switch (dir) {
    343	case IIO_EV_DIR_RISING:
    344		*val = iqs621_als->thresh_light * 16;
    345		break;
    346
    347	case IIO_EV_DIR_FALLING:
    348		*val = iqs621_als->thresh_dark * 4;
    349		break;
    350
    351	case IIO_EV_DIR_EITHER:
    352		if (iqs621_als->ir_flags_mask == IQS622_IR_FLAGS_TOUCH)
    353			*val = iqs621_als->thresh_prox * 4;
    354		else
    355			*val = iqs621_als->thresh_prox;
    356		break;
    357
    358	default:
    359		ret = -EINVAL;
    360	}
    361
    362	mutex_unlock(&iqs621_als->lock);
    363
    364	return ret;
    365}
    366
    367static int iqs621_als_write_event_value(struct iio_dev *indio_dev,
    368					const struct iio_chan_spec *chan,
    369					enum iio_event_type type,
    370					enum iio_event_direction dir,
    371					enum iio_event_info info,
    372					int val, int val2)
    373{
    374	struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
    375	struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
    376	unsigned int thresh_reg, thresh_val;
    377	u8 ir_flags_mask, *thresh_cache;
    378	int ret = -EINVAL;
    379
    380	mutex_lock(&iqs621_als->lock);
    381
    382	switch (dir) {
    383	case IIO_EV_DIR_RISING:
    384		thresh_reg = IQS621_ALS_THRESH_LIGHT;
    385		thresh_val = val / 16;
    386
    387		thresh_cache = &iqs621_als->thresh_light;
    388		ir_flags_mask = 0;
    389		break;
    390
    391	case IIO_EV_DIR_FALLING:
    392		thresh_reg = IQS621_ALS_THRESH_DARK;
    393		thresh_val = val / 4;
    394
    395		thresh_cache = &iqs621_als->thresh_dark;
    396		ir_flags_mask = 0;
    397		break;
    398
    399	case IIO_EV_DIR_EITHER:
    400		/*
    401		 * The IQS622 supports two detection thresholds, both measured
    402		 * in the same arbitrary units reported by read_raw: proximity
    403		 * (0 through 255 in steps of 1), and touch (0 through 1020 in
    404		 * steps of 4).
    405		 *
    406		 * Based on the single detection threshold chosen by the user,
    407		 * select the hardware threshold that gives the best trade-off
    408		 * between range and resolution.
    409		 *
    410		 * By default, the close-range (but coarse) touch threshold is
    411		 * chosen during probe.
    412		 */
    413		switch (val) {
    414		case 0 ... 255:
    415			thresh_reg = IQS622_IR_THRESH_PROX;
    416			thresh_val = val;
    417
    418			ir_flags_mask = IQS622_IR_FLAGS_PROX;
    419			break;
    420
    421		case 256 ... 1020:
    422			thresh_reg = IQS622_IR_THRESH_TOUCH;
    423			thresh_val = val / 4;
    424
    425			ir_flags_mask = IQS622_IR_FLAGS_TOUCH;
    426			break;
    427
    428		default:
    429			goto err_mutex;
    430		}
    431
    432		thresh_cache = &iqs621_als->thresh_prox;
    433		break;
    434
    435	default:
    436		goto err_mutex;
    437	}
    438
    439	if (thresh_val > 0xFF)
    440		goto err_mutex;
    441
    442	ret = regmap_write(iqs62x->regmap, thresh_reg, thresh_val);
    443	if (ret)
    444		goto err_mutex;
    445
    446	*thresh_cache = thresh_val;
    447	iqs621_als->ir_flags_mask = ir_flags_mask;
    448
    449err_mutex:
    450	mutex_unlock(&iqs621_als->lock);
    451
    452	return ret;
    453}
    454
    455static const struct iio_info iqs621_als_info = {
    456	.read_raw = &iqs621_als_read_raw,
    457	.read_event_config = iqs621_als_read_event_config,
    458	.write_event_config = iqs621_als_write_event_config,
    459	.read_event_value = iqs621_als_read_event_value,
    460	.write_event_value = iqs621_als_write_event_value,
    461};
    462
    463static const struct iio_event_spec iqs621_als_range_events[] = {
    464	{
    465		.type = IIO_EV_TYPE_CHANGE,
    466		.dir = IIO_EV_DIR_EITHER,
    467		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
    468	},
    469};
    470
    471static const struct iio_event_spec iqs621_als_light_events[] = {
    472	{
    473		.type = IIO_EV_TYPE_THRESH,
    474		.dir = IIO_EV_DIR_EITHER,
    475		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
    476	},
    477	{
    478		.type = IIO_EV_TYPE_THRESH,
    479		.dir = IIO_EV_DIR_RISING,
    480		.mask_separate = BIT(IIO_EV_INFO_VALUE),
    481	},
    482	{
    483		.type = IIO_EV_TYPE_THRESH,
    484		.dir = IIO_EV_DIR_FALLING,
    485		.mask_separate = BIT(IIO_EV_INFO_VALUE),
    486	},
    487};
    488
    489static const struct iio_chan_spec iqs621_als_channels[] = {
    490	{
    491		.type = IIO_INTENSITY,
    492		.address = IQS621_ALS_FLAGS,
    493		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
    494		.event_spec = iqs621_als_range_events,
    495		.num_event_specs = ARRAY_SIZE(iqs621_als_range_events),
    496	},
    497	{
    498		.type = IIO_LIGHT,
    499		.address = IQS621_ALS_UI_OUT,
    500		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
    501		.event_spec = iqs621_als_light_events,
    502		.num_event_specs = ARRAY_SIZE(iqs621_als_light_events),
    503	},
    504};
    505
    506static const struct iio_event_spec iqs622_als_prox_events[] = {
    507	{
    508		.type = IIO_EV_TYPE_THRESH,
    509		.dir = IIO_EV_DIR_EITHER,
    510		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
    511				 BIT(IIO_EV_INFO_VALUE),
    512	},
    513};
    514
    515static const struct iio_chan_spec iqs622_als_channels[] = {
    516	{
    517		.type = IIO_INTENSITY,
    518		.channel2 = IIO_MOD_LIGHT_BOTH,
    519		.address = IQS622_ALS_FLAGS,
    520		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
    521		.event_spec = iqs621_als_range_events,
    522		.num_event_specs = ARRAY_SIZE(iqs621_als_range_events),
    523		.modified = true,
    524	},
    525	{
    526		.type = IIO_INTENSITY,
    527		.channel2 = IIO_MOD_LIGHT_IR,
    528		.address = IQS622_IR_RANGE,
    529		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
    530		.modified = true,
    531	},
    532	{
    533		.type = IIO_PROXIMITY,
    534		.address = IQS622_IR_UI_OUT,
    535		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
    536		.event_spec = iqs622_als_prox_events,
    537		.num_event_specs = ARRAY_SIZE(iqs622_als_prox_events),
    538	},
    539};
    540
    541static int iqs621_als_probe(struct platform_device *pdev)
    542{
    543	struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
    544	struct iqs621_als_private *iqs621_als;
    545	struct iio_dev *indio_dev;
    546	unsigned int val;
    547	int ret;
    548
    549	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*iqs621_als));
    550	if (!indio_dev)
    551		return -ENOMEM;
    552
    553	iqs621_als = iio_priv(indio_dev);
    554	iqs621_als->iqs62x = iqs62x;
    555	iqs621_als->indio_dev = indio_dev;
    556
    557	if (iqs62x->dev_desc->prod_num == IQS622_PROD_NUM) {
    558		ret = regmap_read(iqs62x->regmap, IQS622_IR_THRESH_TOUCH,
    559				  &val);
    560		if (ret)
    561			return ret;
    562		iqs621_als->thresh_prox = val;
    563		iqs621_als->ir_flags_mask = IQS622_IR_FLAGS_TOUCH;
    564
    565		indio_dev->channels = iqs622_als_channels;
    566		indio_dev->num_channels = ARRAY_SIZE(iqs622_als_channels);
    567	} else {
    568		ret = regmap_read(iqs62x->regmap, IQS621_ALS_THRESH_LIGHT,
    569				  &val);
    570		if (ret)
    571			return ret;
    572		iqs621_als->thresh_light = val;
    573
    574		ret = regmap_read(iqs62x->regmap, IQS621_ALS_THRESH_DARK,
    575				  &val);
    576		if (ret)
    577			return ret;
    578		iqs621_als->thresh_dark = val;
    579
    580		indio_dev->channels = iqs621_als_channels;
    581		indio_dev->num_channels = ARRAY_SIZE(iqs621_als_channels);
    582	}
    583
    584	indio_dev->modes = INDIO_DIRECT_MODE;
    585	indio_dev->name = iqs62x->dev_desc->dev_name;
    586	indio_dev->info = &iqs621_als_info;
    587
    588	mutex_init(&iqs621_als->lock);
    589
    590	iqs621_als->notifier.notifier_call = iqs621_als_notifier;
    591	ret = blocking_notifier_chain_register(&iqs621_als->iqs62x->nh,
    592					       &iqs621_als->notifier);
    593	if (ret) {
    594		dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
    595		return ret;
    596	}
    597
    598	ret = devm_add_action_or_reset(&pdev->dev,
    599				       iqs621_als_notifier_unregister,
    600				       iqs621_als);
    601	if (ret)
    602		return ret;
    603
    604	return devm_iio_device_register(&pdev->dev, indio_dev);
    605}
    606
    607static struct platform_driver iqs621_als_platform_driver = {
    608	.driver = {
    609		.name = "iqs621-als",
    610	},
    611	.probe = iqs621_als_probe,
    612};
    613module_platform_driver(iqs621_als_platform_driver);
    614
    615MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
    616MODULE_DESCRIPTION("Azoteq IQS621/622 Ambient Light Sensors");
    617MODULE_LICENSE("GPL");
    618MODULE_ALIAS("platform:iqs621-als");