1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2f0347c36SKim, Milo /* 3f0347c36SKim, Milo * TI LP8788 MFD - ADC driver 4f0347c36SKim, Milo * 5f0347c36SKim, Milo * Copyright 2012 Texas Instruments 6f0347c36SKim, Milo * 7f0347c36SKim, Milo * Author: Milo(Woogyom) Kim <milo.kim@ti.com> 8f0347c36SKim, Milo */ 9f0347c36SKim, Milo 10f0347c36SKim, Milo #include <linux/delay.h> 11f0347c36SKim, Milo #include <linux/iio/iio.h> 12f0347c36SKim, Milo #include <linux/iio/driver.h> 13f0347c36SKim, Milo #include <linux/iio/machine.h> 14f0347c36SKim, Milo #include <linux/mfd/lp8788.h> 15f0347c36SKim, Milo #include <linux/module.h> 16f0347c36SKim, Milo #include <linux/mutex.h> 17f0347c36SKim, Milo #include <linux/platform_device.h> 18f0347c36SKim, Milo #include <linux/slab.h> 19f0347c36SKim, Milo 20f0347c36SKim, Milo /* register address */ 21f0347c36SKim, Milo #define LP8788_ADC_CONF 0x60 22f0347c36SKim, Milo #define LP8788_ADC_RAW 0x61 23f0347c36SKim, Milo #define LP8788_ADC_DONE 0x63 24f0347c36SKim, Milo 25f0347c36SKim, Milo #define ADC_CONV_START 1 26f0347c36SKim, Milo 27f0347c36SKim, Milo struct lp8788_adc { 28f0347c36SKim, Milo struct lp8788 *lp; 29f0347c36SKim, Milo struct iio_map *map; 30f0347c36SKim, Milo struct mutex lock; 31f0347c36SKim, Milo }; 32f0347c36SKim, Milo 33f0347c36SKim, Milo static const int lp8788_scale[LPADC_MAX] = { 34f0347c36SKim, Milo [LPADC_VBATT_5P5] = 1343101, 35f0347c36SKim, Milo [LPADC_VIN_CHG] = 3052503, 36f0347c36SKim, Milo [LPADC_IBATT] = 610500, 37f0347c36SKim, Milo [LPADC_IC_TEMP] = 61050, 38f0347c36SKim, Milo [LPADC_VBATT_6P0] = 1465201, 39f0347c36SKim, Milo [LPADC_VBATT_5P0] = 1221001, 40f0347c36SKim, Milo [LPADC_ADC1] = 610500, 41f0347c36SKim, Milo [LPADC_ADC2] = 610500, 42f0347c36SKim, Milo [LPADC_VDD] = 1025641, 43f0347c36SKim, Milo [LPADC_VCOIN] = 757020, 44f0347c36SKim, Milo [LPADC_ADC3] = 610500, 45f0347c36SKim, Milo [LPADC_ADC4] = 610500, 46f0347c36SKim, Milo }; 47f0347c36SKim, Milo 48f0347c36SKim, Milo static int lp8788_get_adc_result(struct lp8788_adc *adc, enum lp8788_adc_id id, 49f0347c36SKim, Milo int *val) 50f0347c36SKim, Milo { 51f0347c36SKim, Milo unsigned int msb; 52f0347c36SKim, Milo unsigned int lsb; 53f0347c36SKim, Milo unsigned int result; 54f0347c36SKim, Milo u8 data; 55f0347c36SKim, Milo u8 rawdata[2]; 56f0347c36SKim, Milo int size = ARRAY_SIZE(rawdata); 57f0347c36SKim, Milo int retry = 5; 58f0347c36SKim, Milo int ret; 59f0347c36SKim, Milo 60f0347c36SKim, Milo data = (id << 1) | ADC_CONV_START; 61f0347c36SKim, Milo ret = lp8788_write_byte(adc->lp, LP8788_ADC_CONF, data); 62f0347c36SKim, Milo if (ret) 63f0347c36SKim, Milo goto err_io; 64f0347c36SKim, Milo 65f0347c36SKim, Milo /* retry until adc conversion is done */ 66f0347c36SKim, Milo data = 0; 67f0347c36SKim, Milo while (retry--) { 68f0347c36SKim, Milo usleep_range(100, 200); 69f0347c36SKim, Milo 70f0347c36SKim, Milo ret = lp8788_read_byte(adc->lp, LP8788_ADC_DONE, &data); 71f0347c36SKim, Milo if (ret) 72f0347c36SKim, Milo goto err_io; 73f0347c36SKim, Milo 74f0347c36SKim, Milo /* conversion done */ 75f0347c36SKim, Milo if (data) 76f0347c36SKim, Milo break; 77f0347c36SKim, Milo } 78f0347c36SKim, Milo 79f0347c36SKim, Milo ret = lp8788_read_multi_bytes(adc->lp, LP8788_ADC_RAW, rawdata, size); 80f0347c36SKim, Milo if (ret) 81f0347c36SKim, Milo goto err_io; 82f0347c36SKim, Milo 83f0347c36SKim, Milo msb = (rawdata[0] << 4) & 0x00000ff0; 84f0347c36SKim, Milo lsb = (rawdata[1] >> 4) & 0x0000000f; 85f0347c36SKim, Milo result = msb | lsb; 86f0347c36SKim, Milo *val = result; 87f0347c36SKim, Milo 88f0347c36SKim, Milo return 0; 89f0347c36SKim, Milo 90f0347c36SKim, Milo err_io: 91f0347c36SKim, Milo return ret; 92f0347c36SKim, Milo } 93f0347c36SKim, Milo 94f0347c36SKim, Milo static int lp8788_adc_read_raw(struct iio_dev *indio_dev, 95f0347c36SKim, Milo struct iio_chan_spec const *chan, 96f0347c36SKim, Milo int *val, int *val2, long mask) 97f0347c36SKim, Milo { 98f0347c36SKim, Milo struct lp8788_adc *adc = iio_priv(indio_dev); 99f0347c36SKim, Milo enum lp8788_adc_id id = chan->channel; 100f0347c36SKim, Milo int ret; 101f0347c36SKim, Milo 102f0347c36SKim, Milo mutex_lock(&adc->lock); 103f0347c36SKim, Milo 104f0347c36SKim, Milo switch (mask) { 105f0347c36SKim, Milo case IIO_CHAN_INFO_RAW: 106f0347c36SKim, Milo ret = lp8788_get_adc_result(adc, id, val) ? -EIO : IIO_VAL_INT; 107f0347c36SKim, Milo break; 108f0347c36SKim, Milo case IIO_CHAN_INFO_SCALE: 109f0347c36SKim, Milo *val = lp8788_scale[id] / 1000000; 110f0347c36SKim, Milo *val2 = lp8788_scale[id] % 1000000; 111f0347c36SKim, Milo ret = IIO_VAL_INT_PLUS_MICRO; 112f0347c36SKim, Milo break; 113f0347c36SKim, Milo default: 114f0347c36SKim, Milo ret = -EINVAL; 115f0347c36SKim, Milo break; 116f0347c36SKim, Milo } 117f0347c36SKim, Milo 118f0347c36SKim, Milo mutex_unlock(&adc->lock); 119f0347c36SKim, Milo 120f0347c36SKim, Milo return ret; 121f0347c36SKim, Milo } 122f0347c36SKim, Milo 123f0347c36SKim, Milo static const struct iio_info lp8788_adc_info = { 124f0347c36SKim, Milo .read_raw = &lp8788_adc_read_raw, 125f0347c36SKim, Milo }; 126f0347c36SKim, Milo 127f0347c36SKim, Milo #define LP8788_CHAN(_id, _type) { \ 128f0347c36SKim, Milo .type = _type, \ 129f0347c36SKim, Milo .indexed = 1, \ 130f0347c36SKim, Milo .channel = LPADC_##_id, \ 131fb45a1b3SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 132fb45a1b3SJonathan Cameron BIT(IIO_CHAN_INFO_SCALE), \ 133f0347c36SKim, Milo .datasheet_name = #_id, \ 134f0347c36SKim, Milo } 135f0347c36SKim, Milo 136f0347c36SKim, Milo static const struct iio_chan_spec lp8788_adc_channels[] = { 137f0347c36SKim, Milo [LPADC_VBATT_5P5] = LP8788_CHAN(VBATT_5P5, IIO_VOLTAGE), 138f0347c36SKim, Milo [LPADC_VIN_CHG] = LP8788_CHAN(VIN_CHG, IIO_VOLTAGE), 139f0347c36SKim, Milo [LPADC_IBATT] = LP8788_CHAN(IBATT, IIO_CURRENT), 140f0347c36SKim, Milo [LPADC_IC_TEMP] = LP8788_CHAN(IC_TEMP, IIO_TEMP), 141f0347c36SKim, Milo [LPADC_VBATT_6P0] = LP8788_CHAN(VBATT_6P0, IIO_VOLTAGE), 142f0347c36SKim, Milo [LPADC_VBATT_5P0] = LP8788_CHAN(VBATT_5P0, IIO_VOLTAGE), 143f0347c36SKim, Milo [LPADC_ADC1] = LP8788_CHAN(ADC1, IIO_VOLTAGE), 144f0347c36SKim, Milo [LPADC_ADC2] = LP8788_CHAN(ADC2, IIO_VOLTAGE), 145f0347c36SKim, Milo [LPADC_VDD] = LP8788_CHAN(VDD, IIO_VOLTAGE), 146f0347c36SKim, Milo [LPADC_VCOIN] = LP8788_CHAN(VCOIN, IIO_VOLTAGE), 147f0347c36SKim, Milo [LPADC_ADC3] = LP8788_CHAN(ADC3, IIO_VOLTAGE), 148f0347c36SKim, Milo [LPADC_ADC4] = LP8788_CHAN(ADC4, IIO_VOLTAGE), 149f0347c36SKim, Milo }; 150f0347c36SKim, Milo 151f0347c36SKim, Milo /* default maps used by iio consumer (lp8788-charger driver) */ 152f0347c36SKim, Milo static struct iio_map lp8788_default_iio_maps[] = { 153f0347c36SKim, Milo { 154f0347c36SKim, Milo .consumer_dev_name = "lp8788-charger", 155f0347c36SKim, Milo .consumer_channel = "lp8788_vbatt_5p0", 156f0347c36SKim, Milo .adc_channel_label = "VBATT_5P0", 157f0347c36SKim, Milo }, 158f0347c36SKim, Milo { 159f0347c36SKim, Milo .consumer_dev_name = "lp8788-charger", 160f0347c36SKim, Milo .consumer_channel = "lp8788_adc1", 161f0347c36SKim, Milo .adc_channel_label = "ADC1", 162f0347c36SKim, Milo }, 163f0347c36SKim, Milo { } 164f0347c36SKim, Milo }; 165f0347c36SKim, Milo 166*9c22f459SAlexandru Ardelean static int lp8788_iio_map_register(struct device *dev, 167*9c22f459SAlexandru Ardelean struct iio_dev *indio_dev, 168f0347c36SKim, Milo struct lp8788_platform_data *pdata, 169f0347c36SKim, Milo struct lp8788_adc *adc) 170f0347c36SKim, Milo { 171f0347c36SKim, Milo struct iio_map *map; 172f0347c36SKim, Milo int ret; 173f0347c36SKim, Milo 174f0347c36SKim, Milo map = (!pdata || !pdata->adc_pdata) ? 175f0347c36SKim, Milo lp8788_default_iio_maps : pdata->adc_pdata; 176f0347c36SKim, Milo 177*9c22f459SAlexandru Ardelean ret = devm_iio_map_array_register(dev, indio_dev, map); 178f0347c36SKim, Milo if (ret) { 1795306f416SKim, Milo dev_err(&indio_dev->dev, "iio map err: %d\n", ret); 180f0347c36SKim, Milo return ret; 181f0347c36SKim, Milo } 182f0347c36SKim, Milo 183f0347c36SKim, Milo adc->map = map; 184f0347c36SKim, Milo return 0; 185f0347c36SKim, Milo } 186f0347c36SKim, Milo 187fc52692cSGreg Kroah-Hartman static int lp8788_adc_probe(struct platform_device *pdev) 188f0347c36SKim, Milo { 189f0347c36SKim, Milo struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); 190f0347c36SKim, Milo struct iio_dev *indio_dev; 191f0347c36SKim, Milo struct lp8788_adc *adc; 192f0347c36SKim, Milo int ret; 193f0347c36SKim, Milo 1948483aa5eSSachin Kamat indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); 195f0347c36SKim, Milo if (!indio_dev) 196f0347c36SKim, Milo return -ENOMEM; 197f0347c36SKim, Milo 198f0347c36SKim, Milo adc = iio_priv(indio_dev); 199f0347c36SKim, Milo adc->lp = lp; 200f0347c36SKim, Milo 201*9c22f459SAlexandru Ardelean ret = lp8788_iio_map_register(&pdev->dev, indio_dev, lp->pdata, adc); 202f0347c36SKim, Milo if (ret) 2038483aa5eSSachin Kamat return ret; 204f0347c36SKim, Milo 205f0347c36SKim, Milo mutex_init(&adc->lock); 206f0347c36SKim, Milo 207f0347c36SKim, Milo indio_dev->name = pdev->name; 208f0347c36SKim, Milo indio_dev->modes = INDIO_DIRECT_MODE; 209f0347c36SKim, Milo indio_dev->info = &lp8788_adc_info; 210f0347c36SKim, Milo indio_dev->channels = lp8788_adc_channels; 211f0347c36SKim, Milo indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels); 212f0347c36SKim, Milo 213*9c22f459SAlexandru Ardelean return devm_iio_device_register(&pdev->dev, indio_dev); 214f0347c36SKim, Milo } 215f0347c36SKim, Milo 216f0347c36SKim, Milo static struct platform_driver lp8788_adc_driver = { 217f0347c36SKim, Milo .probe = lp8788_adc_probe, 218f0347c36SKim, Milo .driver = { 219f0347c36SKim, Milo .name = LP8788_DEV_ADC, 220f0347c36SKim, Milo }, 221f0347c36SKim, Milo }; 222f0347c36SKim, Milo module_platform_driver(lp8788_adc_driver); 223f0347c36SKim, Milo 224f0347c36SKim, Milo MODULE_DESCRIPTION("Texas Instruments LP8788 ADC Driver"); 225f0347c36SKim, Milo MODULE_AUTHOR("Milo Kim"); 226f0347c36SKim, Milo MODULE_LICENSE("GPL"); 227f0347c36SKim, Milo MODULE_ALIAS("platform:lp8788-adc"); 228