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