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

vcnl3020.c (15819B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Support for Vishay VCNL3020 proximity sensor on i2c bus.
      4 * Based on Vishay VCNL4000 driver code.
      5 */
      6
      7#include <linux/module.h>
      8#include <linux/i2c.h>
      9#include <linux/err.h>
     10#include <linux/delay.h>
     11#include <linux/regmap.h>
     12#include <linux/interrupt.h>
     13
     14#include <linux/iio/iio.h>
     15#include <linux/iio/events.h>
     16
     17#define VCNL3020_PROD_ID	0x21
     18
     19#define VCNL_COMMAND		0x80 /* Command register */
     20#define VCNL_PROD_REV		0x81 /* Product ID and Revision ID */
     21#define VCNL_PROXIMITY_RATE	0x82 /* Rate of Proximity Measurement */
     22#define VCNL_LED_CURRENT	0x83 /* IR LED current for proximity mode */
     23#define VCNL_PS_RESULT_HI	0x87 /* Proximity result register, MSB */
     24#define VCNL_PS_RESULT_LO	0x88 /* Proximity result register, LSB */
     25#define VCNL_PS_ICR		0x89 /* Interrupt Control Register */
     26#define VCNL_PS_LO_THR_HI	0x8a /* High byte of low threshold value */
     27#define VCNL_PS_LO_THR_LO	0x8b /* Low byte of low threshold value */
     28#define VCNL_PS_HI_THR_HI	0x8c /* High byte of high threshold value */
     29#define VCNL_PS_HI_THR_LO	0x8d /* Low byte of high threshold value */
     30#define VCNL_ISR		0x8e /* Interrupt Status Register */
     31#define VCNL_PS_MOD_ADJ		0x8f /* Proximity Modulator Timing Adjustment */
     32
     33/* Bit masks for COMMAND register */
     34#define VCNL_PS_RDY		BIT(5) /* proximity data ready? */
     35#define VCNL_PS_OD		BIT(3) /* start on-demand proximity
     36					* measurement
     37					*/
     38
     39/* Enables periodic proximity measurement */
     40#define VCNL_PS_EN		BIT(1)
     41
     42/* Enables state machine and LP oscillator for self timed  measurements */
     43#define VCNL_PS_SELFTIMED_EN	BIT(0)
     44
     45/* Bit masks for ICR */
     46
     47/* Enable interrupts on low or high thresholds */
     48#define  VCNL_ICR_THRES_EN	BIT(1)
     49
     50/* Bit masks for ISR */
     51#define VCNL_INT_TH_HI		BIT(0)	/* High threshold hit */
     52#define VCNL_INT_TH_LOW		BIT(1)	/* Low threshold hit */
     53
     54#define VCNL_ON_DEMAND_TIMEOUT_US	100000
     55#define VCNL_POLL_US			20000
     56
     57static const int vcnl3020_prox_sampling_frequency[][2] = {
     58	{1, 950000},
     59	{3, 906250},
     60	{7, 812500},
     61	{16, 625000},
     62	{31, 250000},
     63	{62, 500000},
     64	{125, 0},
     65	{250, 0},
     66};
     67
     68/**
     69 * struct vcnl3020_data - vcnl3020 specific data.
     70 * @regmap:	device register map.
     71 * @dev:	vcnl3020 device.
     72 * @rev:	revision id.
     73 * @lock:	lock for protecting access to device hardware registers.
     74 * @buf:	DMA safe __be16 buffer.
     75 */
     76struct vcnl3020_data {
     77	struct regmap *regmap;
     78	struct device *dev;
     79	u8 rev;
     80	struct mutex lock;
     81	__be16 buf ____cacheline_aligned;
     82};
     83
     84/**
     85 * struct vcnl3020_property - vcnl3020 property.
     86 * @name:	property name.
     87 * @reg:	i2c register offset.
     88 * @conversion_func:	conversion function.
     89 */
     90struct vcnl3020_property {
     91	const char *name;
     92	u32 reg;
     93	u32 (*conversion_func)(u32 *val);
     94};
     95
     96static u32 microamp_to_reg(u32 *val)
     97{
     98	/*
     99	 * An example of conversion from uA to reg val:
    100	 * 200000 uA == 200 mA == 20
    101	 */
    102	return *val /= 10000;
    103};
    104
    105static struct vcnl3020_property vcnl3020_led_current_property = {
    106	.name = "vishay,led-current-microamp",
    107	.reg = VCNL_LED_CURRENT,
    108	.conversion_func = microamp_to_reg,
    109};
    110
    111static int vcnl3020_get_and_apply_property(struct vcnl3020_data *data,
    112					   struct vcnl3020_property prop)
    113{
    114	int rc;
    115	u32 val;
    116
    117	rc = device_property_read_u32(data->dev, prop.name, &val);
    118	if (rc)
    119		return 0;
    120
    121	if (prop.conversion_func)
    122		prop.conversion_func(&val);
    123
    124	rc = regmap_write(data->regmap, prop.reg, val);
    125	if (rc) {
    126		dev_err(data->dev, "Error (%d) setting property (%s)\n",
    127			rc, prop.name);
    128	}
    129
    130	return rc;
    131}
    132
    133static int vcnl3020_init(struct vcnl3020_data *data)
    134{
    135	int rc;
    136	unsigned int reg;
    137
    138	rc = regmap_read(data->regmap, VCNL_PROD_REV, &reg);
    139	if (rc) {
    140		dev_err(data->dev,
    141			"Error (%d) reading product revision\n", rc);
    142		return rc;
    143	}
    144
    145	if (reg != VCNL3020_PROD_ID) {
    146		dev_err(data->dev,
    147			"Product id (%x) did not match vcnl3020 (%x)\n", reg,
    148			VCNL3020_PROD_ID);
    149		return -ENODEV;
    150	}
    151
    152	data->rev = reg;
    153	mutex_init(&data->lock);
    154
    155	return vcnl3020_get_and_apply_property(data,
    156					       vcnl3020_led_current_property);
    157};
    158
    159static bool vcnl3020_is_in_periodic_mode(struct vcnl3020_data *data)
    160{
    161	int rc;
    162	unsigned int cmd;
    163
    164	rc = regmap_read(data->regmap, VCNL_COMMAND, &cmd);
    165	if (rc) {
    166		dev_err(data->dev,
    167			"Error (%d) reading command register\n", rc);
    168		return false;
    169	}
    170
    171	return !!(cmd & VCNL_PS_SELFTIMED_EN);
    172}
    173
    174static int vcnl3020_measure_proximity(struct vcnl3020_data *data, int *val)
    175{
    176	int rc;
    177	unsigned int reg;
    178
    179	mutex_lock(&data->lock);
    180
    181	/* Protect against event capture. */
    182	if (vcnl3020_is_in_periodic_mode(data)) {
    183		rc = -EBUSY;
    184		goto err_unlock;
    185	}
    186
    187	rc = regmap_write(data->regmap, VCNL_COMMAND, VCNL_PS_OD);
    188	if (rc)
    189		goto err_unlock;
    190
    191	/* wait for data to become ready */
    192	rc = regmap_read_poll_timeout(data->regmap, VCNL_COMMAND, reg,
    193				      reg & VCNL_PS_RDY, VCNL_POLL_US,
    194				      VCNL_ON_DEMAND_TIMEOUT_US);
    195	if (rc) {
    196		dev_err(data->dev,
    197			"Error (%d) reading vcnl3020 command register\n", rc);
    198		goto err_unlock;
    199	}
    200
    201	/* high & low result bytes read */
    202	rc = regmap_bulk_read(data->regmap, VCNL_PS_RESULT_HI, &data->buf,
    203			      sizeof(data->buf));
    204	if (rc)
    205		goto err_unlock;
    206
    207	*val = be16_to_cpu(data->buf);
    208
    209err_unlock:
    210	mutex_unlock(&data->lock);
    211
    212	return rc;
    213}
    214
    215static int vcnl3020_read_proxy_samp_freq(struct vcnl3020_data *data, int *val,
    216					 int *val2)
    217{
    218	int rc;
    219	unsigned int prox_rate;
    220
    221	rc = regmap_read(data->regmap, VCNL_PROXIMITY_RATE, &prox_rate);
    222	if (rc)
    223		return rc;
    224
    225	if (prox_rate >= ARRAY_SIZE(vcnl3020_prox_sampling_frequency))
    226		return -EINVAL;
    227
    228	*val = vcnl3020_prox_sampling_frequency[prox_rate][0];
    229	*val2 = vcnl3020_prox_sampling_frequency[prox_rate][1];
    230
    231	return 0;
    232}
    233
    234static int vcnl3020_write_proxy_samp_freq(struct vcnl3020_data *data, int val,
    235					  int val2)
    236{
    237	unsigned int i;
    238	int index = -1;
    239	int rc;
    240
    241	mutex_lock(&data->lock);
    242
    243	/* Protect against event capture. */
    244	if (vcnl3020_is_in_periodic_mode(data)) {
    245		rc = -EBUSY;
    246		goto err_unlock;
    247	}
    248
    249	for (i = 0; i < ARRAY_SIZE(vcnl3020_prox_sampling_frequency); i++) {
    250		if (val == vcnl3020_prox_sampling_frequency[i][0] &&
    251		    val2 == vcnl3020_prox_sampling_frequency[i][1]) {
    252			index = i;
    253			break;
    254		}
    255	}
    256
    257	if (index < 0) {
    258		rc = -EINVAL;
    259		goto err_unlock;
    260	}
    261
    262	rc = regmap_write(data->regmap, VCNL_PROXIMITY_RATE, index);
    263	if (rc)
    264		dev_err(data->dev,
    265			"Error (%d) writing proximity rate register\n", rc);
    266
    267err_unlock:
    268	mutex_unlock(&data->lock);
    269
    270	return rc;
    271}
    272
    273static bool vcnl3020_is_thr_enabled(struct vcnl3020_data *data)
    274{
    275	int rc;
    276	unsigned int icr;
    277
    278	rc = regmap_read(data->regmap, VCNL_PS_ICR, &icr);
    279	if (rc) {
    280		dev_err(data->dev,
    281			"Error (%d) reading ICR register\n", rc);
    282		return false;
    283	}
    284
    285	return !!(icr & VCNL_ICR_THRES_EN);
    286}
    287
    288static int vcnl3020_read_event(struct iio_dev *indio_dev,
    289			       const struct iio_chan_spec *chan,
    290			       enum iio_event_type type,
    291			       enum iio_event_direction dir,
    292			       enum iio_event_info info,
    293			       int *val, int *val2)
    294{
    295	int rc;
    296	struct vcnl3020_data *data = iio_priv(indio_dev);
    297
    298	switch (info) {
    299	case IIO_EV_INFO_VALUE:
    300		switch (dir) {
    301		case IIO_EV_DIR_RISING:
    302			rc = regmap_bulk_read(data->regmap, VCNL_PS_HI_THR_HI,
    303					      &data->buf, sizeof(data->buf));
    304			if (rc < 0)
    305				return rc;
    306			*val = be16_to_cpu(data->buf);
    307			return IIO_VAL_INT;
    308		case IIO_EV_DIR_FALLING:
    309			rc = regmap_bulk_read(data->regmap, VCNL_PS_LO_THR_HI,
    310					      &data->buf, sizeof(data->buf));
    311			if (rc < 0)
    312				return rc;
    313			*val = be16_to_cpu(data->buf);
    314			return IIO_VAL_INT;
    315		default:
    316			return -EINVAL;
    317		}
    318	default:
    319		return -EINVAL;
    320	}
    321}
    322
    323static int vcnl3020_write_event(struct iio_dev *indio_dev,
    324				const struct iio_chan_spec *chan,
    325				enum iio_event_type type,
    326				enum iio_event_direction dir,
    327				enum iio_event_info info,
    328				int val, int val2)
    329{
    330	int rc;
    331	struct vcnl3020_data *data = iio_priv(indio_dev);
    332
    333	mutex_lock(&data->lock);
    334
    335	switch (info) {
    336	case IIO_EV_INFO_VALUE:
    337		switch (dir) {
    338		case IIO_EV_DIR_RISING:
    339			/* 16 bit word/ low * high */
    340			data->buf = cpu_to_be16(val);
    341			rc = regmap_bulk_write(data->regmap, VCNL_PS_HI_THR_HI,
    342					       &data->buf, sizeof(data->buf));
    343			if (rc < 0)
    344				goto err_unlock;
    345			rc = IIO_VAL_INT;
    346			goto err_unlock;
    347		case IIO_EV_DIR_FALLING:
    348			data->buf = cpu_to_be16(val);
    349			rc = regmap_bulk_write(data->regmap, VCNL_PS_LO_THR_HI,
    350					       &data->buf, sizeof(data->buf));
    351			if (rc < 0)
    352				goto err_unlock;
    353			rc = IIO_VAL_INT;
    354			goto err_unlock;
    355		default:
    356			rc = -EINVAL;
    357			goto err_unlock;
    358		}
    359	default:
    360		rc = -EINVAL;
    361		goto err_unlock;
    362	}
    363err_unlock:
    364	mutex_unlock(&data->lock);
    365
    366	return rc;
    367}
    368
    369static int vcnl3020_enable_periodic(struct iio_dev *indio_dev,
    370				    struct vcnl3020_data *data)
    371{
    372	int rc;
    373	int cmd;
    374
    375	mutex_lock(&data->lock);
    376
    377	/* Enable periodic measurement of proximity data. */
    378	cmd = VCNL_PS_EN | VCNL_PS_SELFTIMED_EN;
    379
    380	rc = regmap_write(data->regmap, VCNL_COMMAND, cmd);
    381	if (rc) {
    382		dev_err(data->dev,
    383			"Error (%d) writing command register\n", rc);
    384		goto err_unlock;
    385	}
    386
    387	/*
    388	 * Enable interrupts on threshold, for proximity data by
    389	 * default.
    390	 */
    391	rc = regmap_write(data->regmap, VCNL_PS_ICR, VCNL_ICR_THRES_EN);
    392	if (rc)
    393		dev_err(data->dev,
    394			"Error (%d) reading ICR register\n", rc);
    395
    396err_unlock:
    397	mutex_unlock(&data->lock);
    398
    399	return rc;
    400}
    401
    402static int vcnl3020_disable_periodic(struct iio_dev *indio_dev,
    403				     struct vcnl3020_data *data)
    404{
    405	int rc;
    406
    407	mutex_lock(&data->lock);
    408
    409	rc = regmap_write(data->regmap, VCNL_COMMAND, 0);
    410	if (rc) {
    411		dev_err(data->dev,
    412			"Error (%d) writing command register\n", rc);
    413		goto err_unlock;
    414	}
    415
    416	rc = regmap_write(data->regmap, VCNL_PS_ICR, 0);
    417	if (rc) {
    418		dev_err(data->dev,
    419			"Error (%d) writing ICR register\n", rc);
    420		goto err_unlock;
    421	}
    422
    423	/* Clear interrupt flag bit */
    424	rc = regmap_write(data->regmap, VCNL_ISR, 0);
    425	if (rc)
    426		dev_err(data->dev,
    427			"Error (%d) writing ISR register\n", rc);
    428
    429err_unlock:
    430	mutex_unlock(&data->lock);
    431
    432	return rc;
    433}
    434
    435static int vcnl3020_config_threshold(struct iio_dev *indio_dev, bool state)
    436{
    437	struct vcnl3020_data *data = iio_priv(indio_dev);
    438
    439	if (state) {
    440		return vcnl3020_enable_periodic(indio_dev, data);
    441	} else {
    442		if (!vcnl3020_is_thr_enabled(data))
    443			return 0;
    444		return vcnl3020_disable_periodic(indio_dev, data);
    445	}
    446}
    447
    448static int vcnl3020_write_event_config(struct iio_dev *indio_dev,
    449				       const struct iio_chan_spec *chan,
    450				       enum iio_event_type type,
    451				       enum iio_event_direction dir,
    452				       int state)
    453{
    454	switch (chan->type) {
    455	case IIO_PROXIMITY:
    456		return vcnl3020_config_threshold(indio_dev, state);
    457	default:
    458		return -EINVAL;
    459	}
    460}
    461
    462static int vcnl3020_read_event_config(struct iio_dev *indio_dev,
    463				      const struct iio_chan_spec *chan,
    464				      enum iio_event_type type,
    465				      enum iio_event_direction dir)
    466{
    467	struct vcnl3020_data *data = iio_priv(indio_dev);
    468
    469	switch (chan->type) {
    470	case IIO_PROXIMITY:
    471		return vcnl3020_is_thr_enabled(data);
    472	default:
    473		return -EINVAL;
    474	}
    475}
    476
    477static const struct iio_event_spec vcnl3020_event_spec[] = {
    478	{
    479		.type = IIO_EV_TYPE_THRESH,
    480		.dir = IIO_EV_DIR_RISING,
    481		.mask_separate = BIT(IIO_EV_INFO_VALUE),
    482	}, {
    483		.type = IIO_EV_TYPE_THRESH,
    484		.dir = IIO_EV_DIR_FALLING,
    485		.mask_separate = BIT(IIO_EV_INFO_VALUE),
    486	}, {
    487		.type = IIO_EV_TYPE_THRESH,
    488		.dir = IIO_EV_DIR_EITHER,
    489		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
    490	},
    491};
    492
    493static const struct iio_chan_spec vcnl3020_channels[] = {
    494	{
    495		.type = IIO_PROXIMITY,
    496		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
    497				      BIT(IIO_CHAN_INFO_SAMP_FREQ),
    498		.info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
    499		.event_spec = vcnl3020_event_spec,
    500		.num_event_specs = ARRAY_SIZE(vcnl3020_event_spec),
    501	},
    502};
    503
    504static int vcnl3020_read_raw(struct iio_dev *indio_dev,
    505			     struct iio_chan_spec const *chan, int *val,
    506			     int *val2, long mask)
    507{
    508	int rc;
    509	struct vcnl3020_data *data = iio_priv(indio_dev);
    510
    511	switch (mask) {
    512	case IIO_CHAN_INFO_RAW:
    513		rc = vcnl3020_measure_proximity(data, val);
    514		if (rc)
    515			return rc;
    516		return IIO_VAL_INT;
    517	case IIO_CHAN_INFO_SAMP_FREQ:
    518		rc = vcnl3020_read_proxy_samp_freq(data, val, val2);
    519		if (rc < 0)
    520			return rc;
    521		return IIO_VAL_INT_PLUS_MICRO;
    522	default:
    523		return -EINVAL;
    524	}
    525}
    526
    527static int vcnl3020_write_raw(struct iio_dev *indio_dev,
    528			      struct iio_chan_spec const *chan,
    529			      int val, int val2, long mask)
    530{
    531	struct vcnl3020_data *data = iio_priv(indio_dev);
    532
    533	switch (mask) {
    534	case IIO_CHAN_INFO_SAMP_FREQ:
    535		return vcnl3020_write_proxy_samp_freq(data, val, val2);
    536	default:
    537		return -EINVAL;
    538	}
    539}
    540
    541static int vcnl3020_read_avail(struct iio_dev *indio_dev,
    542			       struct iio_chan_spec const *chan,
    543			       const int **vals, int *type, int *length,
    544			       long mask)
    545{
    546	switch (mask) {
    547	case IIO_CHAN_INFO_SAMP_FREQ:
    548		*vals = (int *)vcnl3020_prox_sampling_frequency;
    549		*type = IIO_VAL_INT_PLUS_MICRO;
    550		*length = 2 * ARRAY_SIZE(vcnl3020_prox_sampling_frequency);
    551		return IIO_AVAIL_LIST;
    552	default:
    553		return -EINVAL;
    554	}
    555}
    556
    557static const struct iio_info vcnl3020_info = {
    558	.read_raw = vcnl3020_read_raw,
    559	.write_raw = vcnl3020_write_raw,
    560	.read_avail = vcnl3020_read_avail,
    561	.read_event_value = vcnl3020_read_event,
    562	.write_event_value = vcnl3020_write_event,
    563	.read_event_config = vcnl3020_read_event_config,
    564	.write_event_config = vcnl3020_write_event_config,
    565};
    566
    567static const struct regmap_config vcnl3020_regmap_config = {
    568	.reg_bits	= 8,
    569	.val_bits	= 8,
    570	.max_register	= VCNL_PS_MOD_ADJ,
    571};
    572
    573static irqreturn_t vcnl3020_handle_irq_thread(int irq, void *p)
    574{
    575	struct iio_dev *indio_dev = p;
    576	struct vcnl3020_data *data = iio_priv(indio_dev);
    577	unsigned int isr;
    578	int rc;
    579
    580	rc = regmap_read(data->regmap, VCNL_ISR, &isr);
    581	if (rc) {
    582		dev_err(data->dev, "Error (%d) reading reg (0x%x)\n",
    583			rc, VCNL_ISR);
    584		return IRQ_HANDLED;
    585	}
    586
    587	if (!(isr & VCNL_ICR_THRES_EN))
    588		return IRQ_NONE;
    589
    590	iio_push_event(indio_dev,
    591		       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1,
    592				            IIO_EV_TYPE_THRESH,
    593				            IIO_EV_DIR_RISING),
    594		       iio_get_time_ns(indio_dev));
    595
    596	rc = regmap_write(data->regmap, VCNL_ISR, isr & VCNL_ICR_THRES_EN);
    597	if (rc)
    598		dev_err(data->dev, "Error (%d) writing in reg (0x%x)\n",
    599			rc, VCNL_ISR);
    600
    601	return IRQ_HANDLED;
    602}
    603
    604static int vcnl3020_probe(struct i2c_client *client)
    605{
    606	struct vcnl3020_data *data;
    607	struct iio_dev *indio_dev;
    608	struct regmap *regmap;
    609	int rc;
    610
    611	regmap = devm_regmap_init_i2c(client, &vcnl3020_regmap_config);
    612	if (IS_ERR(regmap)) {
    613		dev_err(&client->dev, "regmap_init failed\n");
    614		return PTR_ERR(regmap);
    615	}
    616
    617	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
    618	if (!indio_dev)
    619		return -ENOMEM;
    620
    621	data = iio_priv(indio_dev);
    622	i2c_set_clientdata(client, indio_dev);
    623	data->regmap = regmap;
    624	data->dev = &client->dev;
    625
    626	rc = vcnl3020_init(data);
    627	if (rc)
    628		return rc;
    629
    630	indio_dev->info = &vcnl3020_info;
    631	indio_dev->channels = vcnl3020_channels;
    632	indio_dev->num_channels = ARRAY_SIZE(vcnl3020_channels);
    633	indio_dev->name = "vcnl3020";
    634	indio_dev->modes = INDIO_DIRECT_MODE;
    635
    636	if (client->irq) {
    637		rc = devm_request_threaded_irq(&client->dev, client->irq,
    638					       NULL, vcnl3020_handle_irq_thread,
    639					       IRQF_ONESHOT, indio_dev->name,
    640					       indio_dev);
    641		if (rc) {
    642			dev_err(&client->dev,
    643				"Error (%d) irq request failed (%u)\n", rc,
    644				client->irq);
    645			return rc;
    646		}
    647	}
    648
    649	return devm_iio_device_register(&client->dev, indio_dev);
    650}
    651
    652static const struct of_device_id vcnl3020_of_match[] = {
    653	{
    654		.compatible = "vishay,vcnl3020",
    655	},
    656	{}
    657};
    658MODULE_DEVICE_TABLE(of, vcnl3020_of_match);
    659
    660static struct i2c_driver vcnl3020_driver = {
    661	.driver = {
    662		.name   = "vcnl3020",
    663		.of_match_table = vcnl3020_of_match,
    664	},
    665	.probe_new  = vcnl3020_probe,
    666};
    667module_i2c_driver(vcnl3020_driver);
    668
    669MODULE_AUTHOR("Ivan Mikhaylov <i.mikhaylov@yadro.com>");
    670MODULE_DESCRIPTION("Vishay VCNL3020 proximity sensor driver");
    671MODULE_LICENSE("GPL");