Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * ADC driver for Basin Cove PMIC
0004  *
0005  * Copyright (C) 2012 Intel Corporation
0006  * Author: Bin Yang <bin.yang@intel.com>
0007  *
0008  * Rewritten for upstream by:
0009  *   Vincent Pelletier <plr.vincent@gmail.com>
0010  *   Andy Shevchenko <andriy.shevchenko@linux.intel.com>
0011  */
0012 
0013 #include <linux/bitops.h>
0014 #include <linux/completion.h>
0015 #include <linux/interrupt.h>
0016 #include <linux/mfd/intel_soc_pmic.h>
0017 #include <linux/mfd/intel_soc_pmic_mrfld.h>
0018 #include <linux/mod_devicetable.h>
0019 #include <linux/module.h>
0020 #include <linux/mutex.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/regmap.h>
0023 
0024 #include <linux/iio/driver.h>
0025 #include <linux/iio/iio.h>
0026 #include <linux/iio/machine.h>
0027 
0028 #include <asm/unaligned.h>
0029 
0030 #define BCOVE_GPADCREQ          0xDC
0031 #define BCOVE_GPADCREQ_BUSY     BIT(0)
0032 #define BCOVE_GPADCREQ_IRQEN        BIT(1)
0033 
0034 #define BCOVE_ADCIRQ_ALL (      \
0035     BCOVE_ADCIRQ_BATTEMP |      \
0036     BCOVE_ADCIRQ_SYSTEMP |      \
0037     BCOVE_ADCIRQ_BATTID |       \
0038     BCOVE_ADCIRQ_VIBATT |       \
0039     BCOVE_ADCIRQ_CCTICK)
0040 
0041 #define BCOVE_ADC_TIMEOUT       msecs_to_jiffies(1000)
0042 
0043 static const u8 mrfld_adc_requests[] = {
0044     BCOVE_ADCIRQ_VIBATT,
0045     BCOVE_ADCIRQ_BATTID,
0046     BCOVE_ADCIRQ_VIBATT,
0047     BCOVE_ADCIRQ_SYSTEMP,
0048     BCOVE_ADCIRQ_BATTEMP,
0049     BCOVE_ADCIRQ_BATTEMP,
0050     BCOVE_ADCIRQ_SYSTEMP,
0051     BCOVE_ADCIRQ_SYSTEMP,
0052     BCOVE_ADCIRQ_SYSTEMP,
0053 };
0054 
0055 struct mrfld_adc {
0056     struct regmap *regmap;
0057     struct completion completion;
0058     /* Lock to protect the IPC transfers */
0059     struct mutex lock;
0060 };
0061 
0062 static irqreturn_t mrfld_adc_thread_isr(int irq, void *data)
0063 {
0064     struct iio_dev *indio_dev = data;
0065     struct mrfld_adc *adc = iio_priv(indio_dev);
0066 
0067     complete(&adc->completion);
0068     return IRQ_HANDLED;
0069 }
0070 
0071 static int mrfld_adc_single_conv(struct iio_dev *indio_dev,
0072                  struct iio_chan_spec const *chan,
0073                  int *result)
0074 {
0075     struct mrfld_adc *adc = iio_priv(indio_dev);
0076     struct regmap *regmap = adc->regmap;
0077     unsigned int req;
0078     long timeout;
0079     __be16 value;
0080     int ret;
0081 
0082     reinit_completion(&adc->completion);
0083 
0084     regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0);
0085     regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0);
0086 
0087     ret = regmap_read_poll_timeout(regmap, BCOVE_GPADCREQ, req,
0088                        !(req & BCOVE_GPADCREQ_BUSY),
0089                        2000, 1000000);
0090     if (ret)
0091         goto done;
0092 
0093     req = mrfld_adc_requests[chan->channel];
0094     ret = regmap_write(regmap, BCOVE_GPADCREQ, BCOVE_GPADCREQ_IRQEN | req);
0095     if (ret)
0096         goto done;
0097 
0098     timeout = wait_for_completion_interruptible_timeout(&adc->completion,
0099                                 BCOVE_ADC_TIMEOUT);
0100     if (timeout < 0) {
0101         ret = timeout;
0102         goto done;
0103     }
0104     if (timeout == 0) {
0105         ret = -ETIMEDOUT;
0106         goto done;
0107     }
0108 
0109     ret = regmap_bulk_read(regmap, chan->address, &value, sizeof(value));
0110     if (ret)
0111         goto done;
0112 
0113     *result = be16_to_cpu(value);
0114     ret = IIO_VAL_INT;
0115 
0116 done:
0117     regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0xff);
0118     regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0xff);
0119 
0120     return ret;
0121 }
0122 
0123 static int mrfld_adc_read_raw(struct iio_dev *indio_dev,
0124                   struct iio_chan_spec const *chan,
0125                   int *val, int *val2, long mask)
0126 {
0127     struct mrfld_adc *adc = iio_priv(indio_dev);
0128     int ret;
0129 
0130     switch (mask) {
0131     case IIO_CHAN_INFO_RAW:
0132         mutex_lock(&adc->lock);
0133         ret = mrfld_adc_single_conv(indio_dev, chan, val);
0134         mutex_unlock(&adc->lock);
0135         return ret;
0136     default:
0137         return -EINVAL;
0138     }
0139 }
0140 
0141 static const struct iio_info mrfld_adc_iio_info = {
0142     .read_raw = &mrfld_adc_read_raw,
0143 };
0144 
0145 #define BCOVE_ADC_CHANNEL(_type, _channel, _datasheet_name, _address)   \
0146     {                               \
0147         .indexed = 1,                       \
0148         .type = _type,                      \
0149         .channel = _channel,                    \
0150         .address = _address,                    \
0151         .datasheet_name = _datasheet_name,          \
0152         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),       \
0153     }
0154 
0155 static const struct iio_chan_spec mrfld_adc_channels[] = {
0156     BCOVE_ADC_CHANNEL(IIO_VOLTAGE,    0, "CH0", 0xE9),
0157     BCOVE_ADC_CHANNEL(IIO_RESISTANCE, 1, "CH1", 0xEB),
0158     BCOVE_ADC_CHANNEL(IIO_CURRENT,    2, "CH2", 0xED),
0159     BCOVE_ADC_CHANNEL(IIO_TEMP,       3, "CH3", 0xCC),
0160     BCOVE_ADC_CHANNEL(IIO_TEMP,       4, "CH4", 0xC8),
0161     BCOVE_ADC_CHANNEL(IIO_TEMP,       5, "CH5", 0xCA),
0162     BCOVE_ADC_CHANNEL(IIO_TEMP,       6, "CH6", 0xC2),
0163     BCOVE_ADC_CHANNEL(IIO_TEMP,       7, "CH7", 0xC4),
0164     BCOVE_ADC_CHANNEL(IIO_TEMP,       8, "CH8", 0xC6),
0165 };
0166 
0167 static struct iio_map iio_maps[] = {
0168     IIO_MAP("CH0", "bcove-battery", "VBATRSLT"),
0169     IIO_MAP("CH1", "bcove-battery", "BATTID"),
0170     IIO_MAP("CH2", "bcove-battery", "IBATRSLT"),
0171     IIO_MAP("CH3", "bcove-temp",    "PMICTEMP"),
0172     IIO_MAP("CH4", "bcove-temp",    "BATTEMP0"),
0173     IIO_MAP("CH5", "bcove-temp",    "BATTEMP1"),
0174     IIO_MAP("CH6", "bcove-temp",    "SYSTEMP0"),
0175     IIO_MAP("CH7", "bcove-temp",    "SYSTEMP1"),
0176     IIO_MAP("CH8", "bcove-temp",    "SYSTEMP2"),
0177     {}
0178 };
0179 
0180 static int mrfld_adc_probe(struct platform_device *pdev)
0181 {
0182     struct device *dev = &pdev->dev;
0183     struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent);
0184     struct iio_dev *indio_dev;
0185     struct mrfld_adc *adc;
0186     int irq;
0187     int ret;
0188 
0189     indio_dev = devm_iio_device_alloc(dev, sizeof(struct mrfld_adc));
0190     if (!indio_dev)
0191         return -ENOMEM;
0192 
0193     adc = iio_priv(indio_dev);
0194 
0195     mutex_init(&adc->lock);
0196     init_completion(&adc->completion);
0197     adc->regmap = pmic->regmap;
0198 
0199     irq = platform_get_irq(pdev, 0);
0200     if (irq < 0)
0201         return irq;
0202 
0203     ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_adc_thread_isr,
0204                     IRQF_ONESHOT | IRQF_SHARED, pdev->name,
0205                     indio_dev);
0206     if (ret)
0207         return ret;
0208 
0209     indio_dev->name = pdev->name;
0210 
0211     indio_dev->channels = mrfld_adc_channels;
0212     indio_dev->num_channels = ARRAY_SIZE(mrfld_adc_channels);
0213     indio_dev->info = &mrfld_adc_iio_info;
0214     indio_dev->modes = INDIO_DIRECT_MODE;
0215 
0216     ret = devm_iio_map_array_register(dev, indio_dev, iio_maps);
0217     if (ret)
0218         return ret;
0219 
0220     return devm_iio_device_register(dev, indio_dev);
0221 }
0222 
0223 static const struct platform_device_id mrfld_adc_id_table[] = {
0224     { .name = "mrfld_bcove_adc" },
0225     {}
0226 };
0227 MODULE_DEVICE_TABLE(platform, mrfld_adc_id_table);
0228 
0229 static struct platform_driver mrfld_adc_driver = {
0230     .driver = {
0231         .name = "mrfld_bcove_adc",
0232     },
0233     .probe = mrfld_adc_probe,
0234     .id_table = mrfld_adc_id_table,
0235 };
0236 module_platform_driver(mrfld_adc_driver);
0237 
0238 MODULE_AUTHOR("Bin Yang <bin.yang@intel.com>");
0239 MODULE_AUTHOR("Vincent Pelletier <plr.vincent@gmail.com>");
0240 MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
0241 MODULE_DESCRIPTION("ADC driver for Basin Cove PMIC");
0242 MODULE_LICENSE("GPL v2");