mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-24 00:45:19 +07:00
78077256bc
This set is larger than I would like, partly due to my lack of review time in the weeks before the merge window and partly because a couple of large drivers and the subsystem as a whole seem to be getting a lot more exposure and testing recently. 1) A long term bug in trigger handling gave a double free of the device. 2) Wrong return value handling means offsets are ignored in iio_convert_raw_to_processed_unlocked. 3) The iio_channel_has_info utility function was incorrectly updated during the recent info_mask split, this is now fixed. 4) mxs-lradc has a couple of little fixes. 5) A couple of missing .driver_module entries meant that drivers could be removed from underneath their users. 6) Error path fixes for ad7303 and lis3l02dq. 7) The scale value for presure in the lps331ap driver was out by a factor of 100. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.20 (GNU/Linux) iQIcBAABAgAGBQJR5QAJAAoJEFSFNJnE9BaIG2oP/1kV33gxoFwoKMblG7J1paHU h8XdffT93XJTHnCMykCgI9KjohKtr0UmtDLXLXuZD9mkq6T9e6qh6xwpap68Nmwn bCPdaYfqU8cmJ794JV3uK2MUqQphTvV5WhjH4U7qJU8jDesSinPYxEH04DXVnCLC 600PHw8bbl4DGCYLgpDKeq0zZ/4joKFhItVNaRPCpJJB/MYbLkOXE8CLhSoGslKo IfpcBvuN7oYS4CbLqvnkMOmrSaI4a4eI3L6vG42zNuOQLPyL2dNZK3/ViJCJFbqX FbYbYvgApn04liyVkSDQAgW3fkQivF0Lh9R2kkHQ/meTqDMr+r6uvyQ6KDYgw6Px Xh79hwHn/mjT3xRWqtMeMJ85v/9I2dGpKeX2EUm4a8o3+5/gq/oyHR2eF2OZfvBn b3X1PYk29TkGN2K42JKwMJUqHFnVc4w91A9/TWTqz/cqFvBWvdJb8IJmijSug4NG /MN9U07QJgsH5sgRjhkorRe68dXvvBNAhka5ZX3TzrxUO74AdmJ/FscoFulevPgs MTpP8/OxRs4ez94SMNiudK6o6ycNCJYsyKEaF9utDhsJf3W04bXMIP2HvEmZRz14 DCh6v33/vqVjSnrHt95XVa37ahoGUClzAyqvJ+AbnlCXH9yvLjtvP6zLL2p014Zu UcK4x2wZrEhCH6RHPK1y =ratf -----END PGP SIGNATURE----- Merge tag 'iio-fixes-for-3.11a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-linus Jonathan writes: The first round of IIO fixes for the 3.11 cycle. This set is larger than I would like, partly due to my lack of review time in the weeks before the merge window and partly because a couple of large drivers and the subsystem as a whole seem to be getting a lot more exposure and testing recently. 1) A long term bug in trigger handling gave a double free of the device. 2) Wrong return value handling means offsets are ignored in iio_convert_raw_to_processed_unlocked. 3) The iio_channel_has_info utility function was incorrectly updated during the recent info_mask split, this is now fixed. 4) mxs-lradc has a couple of little fixes. 5) A couple of missing .driver_module entries meant that drivers could be removed from underneath their users. 6) Error path fixes for ad7303 and lis3l02dq. 7) The scale value for presure in the lps331ap driver was out by a factor of 100.
330 lines
7.8 KiB
C
330 lines
7.8 KiB
C
/*
|
|
* TI ADC MFD driver
|
|
*
|
|
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation version 2.
|
|
*
|
|
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
|
* kind, whether express or implied; without even the implied warranty
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/err.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/io.h>
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/iio/machine.h>
|
|
#include <linux/iio/driver.h>
|
|
|
|
#include <linux/mfd/ti_am335x_tscadc.h>
|
|
|
|
struct tiadc_device {
|
|
struct ti_tscadc_dev *mfd_tscadc;
|
|
int channels;
|
|
u8 channel_line[8];
|
|
u8 channel_step[8];
|
|
};
|
|
|
|
static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
|
|
{
|
|
return readl(adc->mfd_tscadc->tscadc_base + reg);
|
|
}
|
|
|
|
static void tiadc_writel(struct tiadc_device *adc, unsigned int reg,
|
|
unsigned int val)
|
|
{
|
|
writel(val, adc->mfd_tscadc->tscadc_base + reg);
|
|
}
|
|
|
|
static u32 get_adc_step_mask(struct tiadc_device *adc_dev)
|
|
{
|
|
u32 step_en;
|
|
|
|
step_en = ((1 << adc_dev->channels) - 1);
|
|
step_en <<= TOTAL_STEPS - adc_dev->channels + 1;
|
|
return step_en;
|
|
}
|
|
|
|
static void tiadc_step_config(struct tiadc_device *adc_dev)
|
|
{
|
|
unsigned int stepconfig;
|
|
int i, steps;
|
|
u32 step_en;
|
|
|
|
/*
|
|
* There are 16 configurable steps and 8 analog input
|
|
* lines available which are shared between Touchscreen and ADC.
|
|
*
|
|
* Steps backwards i.e. from 16 towards 0 are used by ADC
|
|
* depending on number of input lines needed.
|
|
* Channel would represent which analog input
|
|
* needs to be given to ADC to digitalize data.
|
|
*/
|
|
|
|
steps = TOTAL_STEPS - adc_dev->channels;
|
|
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
|
|
|
|
for (i = 0; i < adc_dev->channels; i++) {
|
|
int chan;
|
|
|
|
chan = adc_dev->channel_line[i];
|
|
tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
|
|
stepconfig | STEPCONFIG_INP(chan));
|
|
tiadc_writel(adc_dev, REG_STEPDELAY(steps),
|
|
STEPCONFIG_OPENDLY);
|
|
adc_dev->channel_step[i] = steps;
|
|
steps++;
|
|
}
|
|
step_en = get_adc_step_mask(adc_dev);
|
|
am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en);
|
|
}
|
|
|
|
static const char * const chan_name_ain[] = {
|
|
"AIN0",
|
|
"AIN1",
|
|
"AIN2",
|
|
"AIN3",
|
|
"AIN4",
|
|
"AIN5",
|
|
"AIN6",
|
|
"AIN7",
|
|
};
|
|
|
|
static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
|
|
{
|
|
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
|
struct iio_chan_spec *chan_array;
|
|
struct iio_chan_spec *chan;
|
|
int i;
|
|
|
|
indio_dev->num_channels = channels;
|
|
chan_array = kcalloc(channels,
|
|
sizeof(struct iio_chan_spec), GFP_KERNEL);
|
|
if (chan_array == NULL)
|
|
return -ENOMEM;
|
|
|
|
chan = chan_array;
|
|
for (i = 0; i < channels; i++, chan++) {
|
|
|
|
chan->type = IIO_VOLTAGE;
|
|
chan->indexed = 1;
|
|
chan->channel = adc_dev->channel_line[i];
|
|
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
|
chan->datasheet_name = chan_name_ain[chan->channel];
|
|
chan->scan_type.sign = 'u';
|
|
chan->scan_type.realbits = 12;
|
|
chan->scan_type.storagebits = 32;
|
|
}
|
|
|
|
indio_dev->channels = chan_array;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void tiadc_channels_remove(struct iio_dev *indio_dev)
|
|
{
|
|
kfree(indio_dev->channels);
|
|
}
|
|
|
|
static int tiadc_read_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int *val, int *val2, long mask)
|
|
{
|
|
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
|
int i;
|
|
unsigned int fifo1count, read;
|
|
u32 step = UINT_MAX;
|
|
bool found = false;
|
|
|
|
/*
|
|
* When the sub-system is first enabled,
|
|
* the sequencer will always start with the
|
|
* lowest step (1) and continue until step (16).
|
|
* For ex: If we have enabled 4 ADC channels and
|
|
* currently use only 1 out of them, the
|
|
* sequencer still configures all the 4 steps,
|
|
* leading to 3 unwanted data.
|
|
* Hence we need to flush out this data.
|
|
*/
|
|
|
|
for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) {
|
|
if (chan->channel == adc_dev->channel_line[i]) {
|
|
step = adc_dev->channel_step[i];
|
|
break;
|
|
}
|
|
}
|
|
if (WARN_ON_ONCE(step == UINT_MAX))
|
|
return -EINVAL;
|
|
|
|
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
|
for (i = 0; i < fifo1count; i++) {
|
|
read = tiadc_readl(adc_dev, REG_FIFO1);
|
|
if (read >> 16 == step) {
|
|
*val = read & 0xfff;
|
|
found = true;
|
|
}
|
|
}
|
|
am335x_tsc_se_update(adc_dev->mfd_tscadc);
|
|
if (found == false)
|
|
return -EBUSY;
|
|
return IIO_VAL_INT;
|
|
}
|
|
|
|
static const struct iio_info tiadc_info = {
|
|
.read_raw = &tiadc_read_raw,
|
|
.driver_module = THIS_MODULE,
|
|
};
|
|
|
|
static int tiadc_probe(struct platform_device *pdev)
|
|
{
|
|
struct iio_dev *indio_dev;
|
|
struct tiadc_device *adc_dev;
|
|
struct device_node *node = pdev->dev.of_node;
|
|
struct property *prop;
|
|
const __be32 *cur;
|
|
int err;
|
|
u32 val;
|
|
int channels = 0;
|
|
|
|
if (!node) {
|
|
dev_err(&pdev->dev, "Could not find valid DT data.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
indio_dev = iio_device_alloc(sizeof(struct tiadc_device));
|
|
if (indio_dev == NULL) {
|
|
dev_err(&pdev->dev, "failed to allocate iio device\n");
|
|
err = -ENOMEM;
|
|
goto err_ret;
|
|
}
|
|
adc_dev = iio_priv(indio_dev);
|
|
|
|
adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
|
|
|
|
of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
|
|
adc_dev->channel_line[channels] = val;
|
|
channels++;
|
|
}
|
|
adc_dev->channels = channels;
|
|
|
|
indio_dev->dev.parent = &pdev->dev;
|
|
indio_dev->name = dev_name(&pdev->dev);
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
indio_dev->info = &tiadc_info;
|
|
|
|
tiadc_step_config(adc_dev);
|
|
|
|
err = tiadc_channel_init(indio_dev, adc_dev->channels);
|
|
if (err < 0)
|
|
goto err_free_device;
|
|
|
|
err = iio_device_register(indio_dev);
|
|
if (err)
|
|
goto err_free_channels;
|
|
|
|
platform_set_drvdata(pdev, indio_dev);
|
|
|
|
return 0;
|
|
|
|
err_free_channels:
|
|
tiadc_channels_remove(indio_dev);
|
|
err_free_device:
|
|
iio_device_free(indio_dev);
|
|
err_ret:
|
|
return err;
|
|
}
|
|
|
|
static int tiadc_remove(struct platform_device *pdev)
|
|
{
|
|
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
|
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
|
u32 step_en;
|
|
|
|
iio_device_unregister(indio_dev);
|
|
tiadc_channels_remove(indio_dev);
|
|
|
|
step_en = get_adc_step_mask(adc_dev);
|
|
am335x_tsc_se_clr(adc_dev->mfd_tscadc, step_en);
|
|
|
|
iio_device_free(indio_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int tiadc_suspend(struct device *dev)
|
|
{
|
|
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
|
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
|
struct ti_tscadc_dev *tscadc_dev;
|
|
unsigned int idle;
|
|
|
|
tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
|
|
if (!device_may_wakeup(tscadc_dev->dev)) {
|
|
idle = tiadc_readl(adc_dev, REG_CTRL);
|
|
idle &= ~(CNTRLREG_TSCSSENB);
|
|
tiadc_writel(adc_dev, REG_CTRL, (idle |
|
|
CNTRLREG_POWERDOWN));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tiadc_resume(struct device *dev)
|
|
{
|
|
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
|
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
|
unsigned int restore;
|
|
|
|
/* Make sure ADC is powered up */
|
|
restore = tiadc_readl(adc_dev, REG_CTRL);
|
|
restore &= ~(CNTRLREG_POWERDOWN);
|
|
tiadc_writel(adc_dev, REG_CTRL, restore);
|
|
|
|
tiadc_step_config(adc_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops tiadc_pm_ops = {
|
|
.suspend = tiadc_suspend,
|
|
.resume = tiadc_resume,
|
|
};
|
|
#define TIADC_PM_OPS (&tiadc_pm_ops)
|
|
#else
|
|
#define TIADC_PM_OPS NULL
|
|
#endif
|
|
|
|
static const struct of_device_id ti_adc_dt_ids[] = {
|
|
{ .compatible = "ti,am3359-adc", },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
|
|
|
|
static struct platform_driver tiadc_driver = {
|
|
.driver = {
|
|
.name = "TI-am335x-adc",
|
|
.owner = THIS_MODULE,
|
|
.pm = TIADC_PM_OPS,
|
|
.of_match_table = of_match_ptr(ti_adc_dt_ids),
|
|
},
|
|
.probe = tiadc_probe,
|
|
.remove = tiadc_remove,
|
|
};
|
|
module_platform_driver(tiadc_driver);
|
|
|
|
MODULE_DESCRIPTION("TI ADC controller driver");
|
|
MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
|
|
MODULE_LICENSE("GPL");
|