Commit 18973ceb authored by Lee Jones's avatar Lee Jones

Merge branches 'ib-mfd-gpio-4.12', 'ib-mfd-iio-input-4.12',...

Merge branches 'ib-mfd-gpio-4.12', 'ib-mfd-iio-input-4.12', 'ib-mfd-input-4.12', 'ib-mfd-leds-4.12', 'ib-mfd-phy-4.12' and 'ib-mfd-pinctrl-samsung-4.12' into ibs-for-mfd-merged
Motorola CPCAP on key
This module is part of the CPCAP. For more details about the whole
chip see Documentation/devicetree/bindings/mfd/motorola-cpcap.txt.
This module provides a simple power button event via an Interrupt.
Required properties:
- compatible: should be one of the following
- "motorola,cpcap-pwrbutton"
- interrupts: irq specifier for CPCAP's ON IRQ
Example:
&cpcap {
cpcap_pwrbutton: pwrbutton {
compatible = "motorola,cpcap-pwrbutton";
interrupts = <23 IRQ_TYPE_NONE>;
};
};
...@@ -229,6 +229,19 @@ config EXYNOS_ADC ...@@ -229,6 +229,19 @@ config EXYNOS_ADC
To compile this driver as a module, choose M here: the module will be To compile this driver as a module, choose M here: the module will be
called exynos_adc. called exynos_adc.
config MXS_LRADC_ADC
tristate "Freescale i.MX23/i.MX28 LRADC ADC"
depends on MFD_MXS_LRADC
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for the ADC functions of the
i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings,
battery voltage measurement, and die temperature measurement.
This driver can also be built as a module. If so, the module will be
called mxs-lradc-adc.
config FSL_MX25_ADC config FSL_MX25_ADC
tristate "Freescale MX25 ADC driver" tristate "Freescale MX25 ADC driver"
depends on MFD_MX25_TSADC depends on MFD_MX25_TSADC
...@@ -411,20 +424,6 @@ config MESON_SARADC ...@@ -411,20 +424,6 @@ config MESON_SARADC
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called meson_saradc. module will be called meson_saradc.
config MXS_LRADC
tristate "Freescale i.MX23/i.MX28 LRADC"
depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
depends on INPUT
select STMP_DEVICE
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for i.MX23/i.MX28 LRADC convertor
built into these chips.
To compile this driver as a module, choose M here: the
module will be called mxs-lradc.
config NAU7802 config NAU7802
tristate "Nuvoton NAU7802 ADC driver" tristate "Nuvoton NAU7802 ADC driver"
depends on I2C depends on I2C
......
...@@ -39,7 +39,7 @@ obj-$(CONFIG_MCP3422) += mcp3422.o ...@@ -39,7 +39,7 @@ obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
......
/*
* Freescale MXS LRADC ADC driver
*
* Copyright (c) 2012 DENX Software Engineering, GmbH.
* Copyright (c) 2017 Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
*
* Authors:
* Marek Vasut <marex@denx.de>
* Ksenija Stanojevic <ksenija.stanojevic@gmail.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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/mfd/mxs-lradc.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/sysfs.h>
/*
* Make this runtime configurable if necessary. Currently, if the buffered mode
* is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
* triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
* seconds. The result is that the samples arrive every 500mS.
*/
#define LRADC_DELAY_TIMER_PER 200
#define LRADC_DELAY_TIMER_LOOP 5
#define VREF_MV_BASE 1850
const char *mx23_lradc_adc_irq_names[] = {
"mxs-lradc-channel0",
"mxs-lradc-channel1",
"mxs-lradc-channel2",
"mxs-lradc-channel3",
"mxs-lradc-channel4",
"mxs-lradc-channel5",
};
const char *mx28_lradc_adc_irq_names[] = {
"mxs-lradc-thresh0",
"mxs-lradc-thresh1",
"mxs-lradc-channel0",
"mxs-lradc-channel1",
"mxs-lradc-channel2",
"mxs-lradc-channel3",
"mxs-lradc-channel4",
"mxs-lradc-channel5",
"mxs-lradc-button0",
"mxs-lradc-button1",
};
static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = {
[IMX23_LRADC] = {
VREF_MV_BASE, /* CH0 */
VREF_MV_BASE, /* CH1 */
VREF_MV_BASE, /* CH2 */
VREF_MV_BASE, /* CH3 */
VREF_MV_BASE, /* CH4 */
VREF_MV_BASE, /* CH5 */
VREF_MV_BASE * 2, /* CH6 VDDIO */
VREF_MV_BASE * 4, /* CH7 VBATT */
VREF_MV_BASE, /* CH8 Temp sense 0 */
VREF_MV_BASE, /* CH9 Temp sense 1 */
VREF_MV_BASE, /* CH10 */
VREF_MV_BASE, /* CH11 */
VREF_MV_BASE, /* CH12 USB_DP */
VREF_MV_BASE, /* CH13 USB_DN */
VREF_MV_BASE, /* CH14 VBG */
VREF_MV_BASE * 4, /* CH15 VDD5V */
},
[IMX28_LRADC] = {
VREF_MV_BASE, /* CH0 */
VREF_MV_BASE, /* CH1 */
VREF_MV_BASE, /* CH2 */
VREF_MV_BASE, /* CH3 */
VREF_MV_BASE, /* CH4 */
VREF_MV_BASE, /* CH5 */
VREF_MV_BASE, /* CH6 */
VREF_MV_BASE * 4, /* CH7 VBATT */
VREF_MV_BASE, /* CH8 Temp sense 0 */
VREF_MV_BASE, /* CH9 Temp sense 1 */
VREF_MV_BASE * 2, /* CH10 VDDIO */
VREF_MV_BASE, /* CH11 VTH */
VREF_MV_BASE * 2, /* CH12 VDDA */
VREF_MV_BASE, /* CH13 VDDD */
VREF_MV_BASE, /* CH14 VBG */
VREF_MV_BASE * 4, /* CH15 VDD5V */
},
};
enum mxs_lradc_divbytwo {
MXS_LRADC_DIV_DISABLED = 0,
MXS_LRADC_DIV_ENABLED,
};
struct mxs_lradc_scale {
unsigned int integer;
unsigned int nano;
};
struct mxs_lradc_adc {
struct mxs_lradc *lradc;
struct device *dev;
void __iomem *base;
u32 buffer[10];
struct iio_trigger *trig;
struct completion completion;
spinlock_t lock;
const u32 *vref_mv;
struct mxs_lradc_scale scale_avail[LRADC_MAX_TOTAL_CHANS][2];
unsigned long is_divided;
};
/* Raw I/O operations */
static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan,
int *val)
{
struct mxs_lradc_adc *adc = iio_priv(iio_dev);
struct mxs_lradc *lradc = adc->lradc;
int ret;
/*
* See if there is no buffered operation in progress. If there is simply
* bail out. This can be improved to support both buffered and raw IO at
* the same time, yet the code becomes horribly complicated. Therefore I
* applied KISS principle here.
*/
ret = iio_device_claim_direct_mode(iio_dev);
if (ret)
return ret;
reinit_completion(&adc->completion);
/*
* No buffered operation in progress, map the channel and trigger it.
* Virtual channel 0 is always used here as the others are always not
* used if doing raw sampling.
*/
if (lradc->soc == IMX28_LRADC)
writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
writel(0x1, adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
/* Enable / disable the divider per requirement */
if (test_bit(chan, &adc->is_divided))
writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_SET);
else
writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_CLR);
/* Clean the slot's previous content, then set new one. */
writel(LRADC_CTRL4_LRADCSELECT_MASK(0),
adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
writel(chan, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
writel(0, adc->base + LRADC_CH(0));
/* Enable the IRQ and start sampling the channel. */
writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
writel(BIT(0), adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
/* Wait for completion on the channel, 1 second max. */
ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
if (!ret)
ret = -ETIMEDOUT;
if (ret < 0)
goto err;
/* Read the data. */
*val = readl(adc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
ret = IIO_VAL_INT;
err:
writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
iio_device_release_direct_mode(iio_dev);
return ret;
}
static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
{
int ret, min, max;
ret = mxs_lradc_adc_read_single(iio_dev, 8, &min);
if (ret != IIO_VAL_INT)
return ret;
ret = mxs_lradc_adc_read_single(iio_dev, 9, &max);
if (ret != IIO_VAL_INT)
return ret;
*val = max - min;
return IIO_VAL_INT;
}
static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long m)
{
struct mxs_lradc_adc *adc = iio_priv(iio_dev);
switch (m) {
case IIO_CHAN_INFO_RAW:
if (chan->type == IIO_TEMP)
return mxs_lradc_adc_read_temp(iio_dev, val);
return mxs_lradc_adc_read_single(iio_dev, chan->channel, val);
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_TEMP) {
/*
* From the datasheet, we have to multiply by 1.012 and
* divide by 4
*/
*val = 0;
*val2 = 253000;
return IIO_VAL_INT_PLUS_MICRO;
}
*val = adc->vref_mv[chan->channel];
*val2 = chan->scan_type.realbits -
test_bit(chan->channel, &adc->is_divided);
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_OFFSET:
if (chan->type == IIO_TEMP) {
/*
* The calculated value from the ADC is in Kelvin, we
* want Celsius for hwmon so the offset is -273.15
* The offset is applied before scaling so it is
* actually -213.15 * 4 / 1.012 = -1079.644268
*/
*val = -1079;
*val2 = 644268;
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
default:
break;
}
return -EINVAL;
}
static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
int val, int val2, long m)
{
struct mxs_lradc_adc *adc = iio_priv(iio_dev);
struct mxs_lradc_scale *scale_avail =
adc->scale_avail[chan->channel];
int ret;
ret = iio_device_claim_direct_mode(iio_dev);
if (ret)
return ret;
switch (m) {
case IIO_CHAN_INFO_SCALE:
ret = -EINVAL;
if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
/* divider by two disabled */
clear_bit(chan->channel, &adc->is_divided);
ret = 0;
} else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
/* divider by two enabled */
set_bit(chan->channel, &adc->is_divided);
ret = 0;
}
break;
default:
ret = -EINVAL;
break;
}
iio_device_release_direct_mode(iio_dev);
return ret;
}
static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
long m)
{
return IIO_VAL_INT_PLUS_NANO;
}
static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *iio = dev_to_iio_dev(dev);
struct mxs_lradc_adc *adc = iio_priv(iio);
struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
int i, ch, len = 0;
ch = iio_attr->address;
for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
len += sprintf(buf + len, "%u.%09u ",
adc->scale_avail[ch][i].integer,
adc->scale_avail[ch][i].nano);
len += sprintf(buf + len, "\n");
return len;
}
#define SHOW_SCALE_AVAILABLE_ATTR(ch)\
IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, 0444,\
mxs_lradc_adc_show_scale_avail, NULL, ch)
SHOW_SCALE_AVAILABLE_ATTR(0);
SHOW_SCALE_AVAILABLE_ATTR(1);
SHOW_SCALE_AVAILABLE_ATTR(2);
SHOW_SCALE_AVAILABLE_ATTR(3);
SHOW_SCALE_AVAILABLE_ATTR(4);
SHOW_SCALE_AVAILABLE_ATTR(5);
SHOW_SCALE_AVAILABLE_ATTR(6);
SHOW_SCALE_AVAILABLE_ATTR(7);
SHOW_SCALE_AVAILABLE_ATTR(10);
SHOW_SCALE_AVAILABLE_ATTR(11);
SHOW_SCALE_AVAILABLE_ATTR(12);
SHOW_SCALE_AVAILABLE_ATTR(13);
SHOW_SCALE_AVAILABLE_ATTR(14);
SHOW_SCALE_AVAILABLE_ATTR(15);
static struct attribute *mxs_lradc_adc_attributes[] = {
&iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
NULL
};
static const struct attribute_group mxs_lradc_adc_attribute_group = {
.attrs = mxs_lradc_adc_attributes,
};
static const struct iio_info mxs_lradc_adc_iio_info = {
.driver_module = THIS_MODULE,
.read_raw = mxs_lradc_adc_read_raw,
.write_raw = mxs_lradc_adc_write_raw,
.write_raw_get_fmt = mxs_lradc_adc_write_raw_get_fmt,
.attrs = &mxs_lradc_adc_attribute_group,
};
/* IRQ Handling */
static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data)
{
struct iio_dev *iio = data;
struct mxs_lradc_adc *adc = iio_priv(iio);
struct mxs_lradc *lradc = adc->lradc;
unsigned long reg = readl(adc->base + LRADC_CTRL1);
unsigned long flags;
if (!(reg & mxs_lradc_irq_mask(lradc)))
return IRQ_NONE;
if (iio_buffer_enabled(iio)) {
if (reg & lradc->buffer_vchans) {
spin_lock_irqsave(&adc->lock, flags);
iio_trigger_poll(iio->trig);
spin_unlock_irqrestore(&adc->lock, flags);
}
} else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
complete(&adc->completion);
}
writel(reg & mxs_lradc_irq_mask(lradc),
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
return IRQ_HANDLED;
}
/* Trigger handling */
static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *iio = pf->indio_dev;
struct mxs_lradc_adc *adc = iio_priv(iio);
const u32 chan_value = LRADC_CH_ACCUMULATE |
((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
unsigned int i, j = 0;
for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
adc->buffer[j] = readl(adc->base + LRADC_CH(j));
writel(chan_value, adc->base + LRADC_CH(j));
adc->buffer[j] &= LRADC_CH_VALUE_MASK;
adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
j++;
}
iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp);
iio_trigger_notify_done(iio->trig);
return IRQ_HANDLED;
}
static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state)
{
struct iio_dev *iio = iio_trigger_get_drvdata(trig);
struct mxs_lradc_adc *adc = iio_priv(iio);
const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
writel(LRADC_DELAY_KICK, adc->base + (LRADC_DELAY(0) + st));
return 0;
}
static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = {
.owner = THIS_MODULE,
.set_trigger_state = &mxs_lradc_adc_configure_trigger,
};
static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
{
int ret;
struct iio_trigger *trig;
struct mxs_lradc_adc *adc = iio_priv(iio);
trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name,
iio->id);
trig->dev.parent = adc->dev;
iio_trigger_set_drvdata(trig, iio);
trig->ops = &mxs_lradc_adc_trigger_ops;
ret = iio_trigger_register(trig);
if (ret)
return ret;
adc->trig = trig;
return 0;
}
static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio)
{
struct mxs_lradc_adc *adc = iio_priv(iio);
iio_trigger_unregister(adc->trig);
}
static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio)
{
struct mxs_lradc_adc *adc = iio_priv(iio);
struct mxs_lradc *lradc = adc->lradc;
int chan, ofs = 0;
unsigned long enable = 0;
u32 ctrl4_set = 0;
u32 ctrl4_clr = 0;
u32 ctrl1_irq = 0;
const u32 chan_value = LRADC_CH_ACCUMULATE |
((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
if (lradc->soc == IMX28_LRADC)
writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
writel(lradc->buffer_vchans,
adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
writel(chan_value, adc->base + LRADC_CH(ofs));
bitmap_set(&enable, ofs, 1);
ofs++;
}
writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
writel(ctrl4_clr, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
writel(ctrl4_set, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
writel(ctrl1_irq, adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_SET);
return 0;
}
static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
{
struct mxs_lradc_adc *adc = iio_priv(iio);
struct mxs_lradc *lradc = adc->lradc;
writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
writel(lradc->buffer_vchans,
adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
if (lradc->soc == IMX28_LRADC)
writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
return 0;
}
static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio,
const unsigned long *mask)
{
struct mxs_lradc_adc *adc = iio_priv(iio);
struct mxs_lradc *lradc = adc->lradc;
const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
int rsvd_chans = 0;
unsigned long rsvd_mask = 0;
if (lradc->use_touchbutton)
rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_4WIRE)
rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_5WIRE)
rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
if (lradc->use_touchbutton)
rsvd_chans++;
if (lradc->touchscreen_wire)
rsvd_chans += 2;
/* Test for attempts to map channels with special mode of operation. */
if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
return false;
/* Test for attempts to map more channels then available slots. */
if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
return false;
return true;
}
static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = {
.preenable = &mxs_lradc_adc_buffer_preenable,
.postenable = &iio_triggered_buffer_postenable,
.predisable = &iio_triggered_buffer_predisable,
.postdisable = &mxs_lradc_adc_buffer_postdisable,
.validate_scan_mask = &mxs_lradc_adc_validate_scan_mask,
};
/* Driver initialization */
#define MXS_ADC_CHAN(idx, chan_type, name) { \
.type = (chan_type), \
.indexed = 1, \
.scan_index = (idx), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.channel = (idx), \
.address = (idx), \
.scan_type = { \
.sign = 'u', \
.realbits = LRADC_RESOLUTION, \
.storagebits = 32, \
}, \
.datasheet_name = (name), \
}
static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
/* Combined Temperature sensors */
{
.type = IIO_TEMP,
.indexed = 1,
.scan_index = 8,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
.channel = 8,
.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
.datasheet_name = "TEMP_DIE",
},
/* Hidden channel to keep indexes */
{
.type = IIO_TEMP,
.indexed = 1,
.scan_index = -1,
.channel = 9,
},
MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
};
static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
/* Combined Temperature sensors */
{
.type = IIO_TEMP,
.indexed = 1,
.scan_index = 8,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
.channel = 8,
.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
.datasheet_name = "TEMP_DIE",
},
/* Hidden channel to keep indexes */
{
.type = IIO_TEMP,
.indexed = 1,
.scan_index = -1,
.channel = 9,
},
MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
};
static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc)
{
/* The ADC always uses DELAY CHANNEL 0. */
const u32 adc_cfg =
(1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
(LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
/* Configure DELAY CHANNEL 0 for generic ADC sampling. */
writel(adc_cfg, adc->base + LRADC_DELAY(0));
/*
* Start internal temperature sensing by clearing bit
* HW_LRADC_CTRL2_TEMPSENSE_PWD. This bit can be left cleared
* after power up.
*/
writel(0, adc->base + LRADC_CTRL2);
}
static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc)
{
writel(0, adc->base + LRADC_DELAY(0));
}
static int mxs_lradc_adc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mxs_lradc *lradc = dev_get_drvdata(dev->parent);
struct mxs_lradc_adc *adc;
struct iio_dev *iio;
struct resource *iores;
int ret, irq, virq, i, s, n;
u64 scale_uv;
const char **irq_name;
/* Allocate the IIO device. */
iio = devm_iio_device_alloc(dev, sizeof(*adc));
if (!iio) {
dev_err(dev, "Failed to allocate IIO device\n");
return -ENOMEM;
}
adc = iio_priv(iio);
adc->lradc = lradc;
adc->dev = dev;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
adc->base = devm_ioremap(dev, iores->start, resource_size(iores));
if (IS_ERR(adc->base))
return PTR_ERR(adc->base);
init_completion(&adc->completion);
spin_lock_init(&adc->lock);
platform_set_drvdata(pdev, iio);
iio->name = pdev->name;
iio->dev.parent = dev;
iio->dev.of_node = dev->parent->of_node;
iio->info = &mxs_lradc_adc_iio_info;
iio->modes = INDIO_DIRECT_MODE;
iio->masklength = LRADC_MAX_TOTAL_CHANS;
if (lradc->soc == IMX23_LRADC) {
iio->channels = mx23_lradc_chan_spec;
iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
irq_name = mx23_lradc_adc_irq_names;
n = ARRAY_SIZE(mx23_lradc_adc_irq_names);
} else {
iio->channels = mx28_lradc_chan_spec;
iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
irq_name = mx28_lradc_adc_irq_names;
n = ARRAY_SIZE(mx28_lradc_adc_irq_names);
}
ret = stmp_reset_block(adc->base);
if (ret)
return ret;
for (i = 0; i < n; i++) {
irq = platform_get_irq_byname(pdev, irq_name[i]);
if (irq < 0)
return irq;
virq = irq_of_parse_and_map(dev->parent->of_node, irq);
ret = devm_request_irq(dev, virq, mxs_lradc_adc_handle_irq,
0, irq_name[i], iio);
if (ret)
return ret;
}
ret = mxs_lradc_adc_trigger_init(iio);
if (ret)
goto err_trig;
ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
&mxs_lradc_adc_trigger_handler,
&mxs_lradc_adc_buffer_ops);
if (ret)
return ret;
adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
/* Populate available ADC input ranges */
for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) {
/*
* [s=0] = optional divider by two disabled (default)
* [s=1] = optional divider by two enabled
*
* The scale is calculated by doing:
* Vref >> (realbits - s)
* which multiplies by two on the second component
* of the array.
*/
scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
(LRADC_RESOLUTION - s);
adc->scale_avail[i][s].nano =
do_div(scale_uv, 100000000) * 10;
adc->scale_avail[i][s].integer = scale_uv;
}
}
/* Configure the hardware. */
mxs_lradc_adc_hw_init(adc);
/* Register IIO device. */
ret = iio_device_register(iio);
if (ret) {
dev_err(dev, "Failed to register IIO device\n");
goto err_dev;
}
return 0;
err_dev:
mxs_lradc_adc_hw_stop(adc);
mxs_lradc_adc_trigger_remove(iio);
err_trig:
iio_triggered_buffer_cleanup(iio);
return ret;
}
static int mxs_lradc_adc_remove(struct platform_device *pdev)
{
struct iio_dev *iio = platform_get_drvdata(pdev);
struct mxs_lradc_adc *adc = iio_priv(iio);
iio_device_unregister(iio);
mxs_lradc_adc_hw_stop(adc);
mxs_lradc_adc_trigger_remove(iio);
iio_triggered_buffer_cleanup(iio);
return 0;
}
static struct platform_driver mxs_lradc_adc_driver = {
.driver = {
.name = "mxs-lradc-adc",
},
.probe = mxs_lradc_adc_probe,
.remove = mxs_lradc_adc_remove,
};
module_platform_driver(mxs_lradc_adc_driver);
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:mxs-lradc-adc");
/*
* Freescale MXS LRADC driver
*
* Copyright (c) 2012 DENX Software Engineering, GmbH.
* Marek Vasut <marex@denx.de>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/stmp_device.h>
#include <linux/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/sysfs.h>
#define DRIVER_NAME "mxs-lradc"
#define LRADC_MAX_DELAY_CHANS 4
#define LRADC_MAX_MAPPED_CHANS 8
#define LRADC_MAX_TOTAL_CHANS 16
#define LRADC_DELAY_TIMER_HZ 2000
/*
* Make this runtime configurable if necessary. Currently, if the buffered mode
* is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
* triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
* seconds. The result is that the samples arrive every 500mS.
*/
#define LRADC_DELAY_TIMER_PER 200
#define LRADC_DELAY_TIMER_LOOP 5
/*
* Once the pen touches the touchscreen, the touchscreen switches from
* IRQ-driven mode to polling mode to prevent interrupt storm. The polling
* is realized by worker thread, which is called every 20 or so milliseconds.
* This gives the touchscreen enough fluency and does not strain the system
* too much.
*/
#define LRADC_TS_SAMPLE_DELAY_MS 5
/*
* The LRADC reads the following amount of samples from each touchscreen
* channel and the driver then computes average of these.
*/
#define LRADC_TS_SAMPLE_AMOUNT 4
enum mxs_lradc_id {
IMX23_LRADC,
IMX28_LRADC,
};
static const char * const mx23_lradc_irq_names[] = {
"mxs-lradc-touchscreen",
"mxs-lradc-channel0",
"mxs-lradc-channel1",
"mxs-lradc-channel2",
"mxs-lradc-channel3",
"mxs-lradc-channel4",
"mxs-lradc-channel5",
"mxs-lradc-channel6",
"mxs-lradc-channel7",
};
static const char * const mx28_lradc_irq_names[] = {
"mxs-lradc-touchscreen",
"mxs-lradc-thresh0",
"mxs-lradc-thresh1",
"mxs-lradc-channel0",
"mxs-lradc-channel1",
"mxs-lradc-channel2",
"mxs-lradc-channel3",
"mxs-lradc-channel4",
"mxs-lradc-channel5",
"mxs-lradc-channel6",
"mxs-lradc-channel7",
"mxs-lradc-button0",
"mxs-lradc-button1",
};
struct mxs_lradc_of_config {
const int irq_count;
const char * const *irq_name;
const u32 *vref_mv;
};
#define VREF_MV_BASE 1850
static const u32 mx23_vref_mv[LRADC_MAX_TOTAL_CHANS] = {
VREF_MV_BASE, /* CH0 */
VREF_MV_BASE, /* CH1 */
VREF_MV_BASE, /* CH2 */
VREF_MV_BASE, /* CH3 */
VREF_MV_BASE, /* CH4 */
VREF_MV_BASE, /* CH5 */
VREF_MV_BASE * 2, /* CH6 VDDIO */
VREF_MV_BASE * 4, /* CH7 VBATT */
VREF_MV_BASE, /* CH8 Temp sense 0 */
VREF_MV_BASE, /* CH9 Temp sense 1 */
VREF_MV_BASE, /* CH10 */
VREF_MV_BASE, /* CH11 */
VREF_MV_BASE, /* CH12 USB_DP */
VREF_MV_BASE, /* CH13 USB_DN */
VREF_MV_BASE, /* CH14 VBG */
VREF_MV_BASE * 4, /* CH15 VDD5V */
};
static const u32 mx28_vref_mv[LRADC_MAX_TOTAL_CHANS] = {
VREF_MV_BASE, /* CH0 */
VREF_MV_BASE, /* CH1 */
VREF_MV_BASE, /* CH2 */
VREF_MV_BASE, /* CH3 */
VREF_MV_BASE, /* CH4 */
VREF_MV_BASE, /* CH5 */
VREF_MV_BASE, /* CH6 */
VREF_MV_BASE * 4, /* CH7 VBATT */
VREF_MV_BASE, /* CH8 Temp sense 0 */
VREF_MV_BASE, /* CH9 Temp sense 1 */
VREF_MV_BASE * 2, /* CH10 VDDIO */
VREF_MV_BASE, /* CH11 VTH */
VREF_MV_BASE * 2, /* CH12 VDDA */
VREF_MV_BASE, /* CH13 VDDD */
VREF_MV_BASE, /* CH14 VBG */
VREF_MV_BASE * 4, /* CH15 VDD5V */
};
static const struct mxs_lradc_of_config mxs_lradc_of_config[] = {
[IMX23_LRADC] = {
.irq_count = ARRAY_SIZE(mx23_lradc_irq_names),
.irq_name = mx23_lradc_irq_names,
.vref_mv = mx23_vref_mv,
},
[IMX28_LRADC] = {
.irq_count = ARRAY_SIZE(mx28_lradc_irq_names),
.irq_name = mx28_lradc_irq_names,
.vref_mv = mx28_vref_mv,
},
};
enum mxs_lradc_ts {
MXS_LRADC_TOUCHSCREEN_NONE = 0,
MXS_LRADC_TOUCHSCREEN_4WIRE,
MXS_LRADC_TOUCHSCREEN_5WIRE,
};
/*
* Touchscreen handling
*/
enum lradc_ts_plate {
LRADC_TOUCH = 0,
LRADC_SAMPLE_X,
LRADC_SAMPLE_Y,
LRADC_SAMPLE_PRESSURE,
LRADC_SAMPLE_VALID,
};
enum mxs_lradc_divbytwo {
MXS_LRADC_DIV_DISABLED = 0,
MXS_LRADC_DIV_ENABLED,
};
struct mxs_lradc_scale {
unsigned int integer;
unsigned int nano;
};
struct mxs_lradc {
struct device *dev;
void __iomem *base;
int irq[13];
struct clk *clk;
u32 *buffer;
struct iio_trigger *trig;
struct mutex lock;
struct completion completion;
const u32 *vref_mv;
struct mxs_lradc_scale scale_avail[LRADC_MAX_TOTAL_CHANS][2];
unsigned long is_divided;
/*
* When the touchscreen is enabled, we give it two private virtual
* channels: #6 and #7. This means that only 6 virtual channels (instead
* of 8) will be available for buffered capture.
*/
#define TOUCHSCREEN_VCHANNEL1 7
#define TOUCHSCREEN_VCHANNEL2 6
#define BUFFER_VCHANS_LIMITED 0x3f
#define BUFFER_VCHANS_ALL 0xff
u8 buffer_vchans;
/*
* Furthermore, certain LRADC channels are shared between touchscreen
* and/or touch-buttons and generic LRADC block. Therefore when using
* either of these, these channels are not available for the regular
* sampling. The shared channels are as follows:
*
* CH0 -- Touch button #0
* CH1 -- Touch button #1
* CH2 -- Touch screen XPUL
* CH3 -- Touch screen YPLL
* CH4 -- Touch screen XNUL
* CH5 -- Touch screen YNLR
* CH6 -- Touch screen WIPER (5-wire only)
*
* The bit fields below represents which parts of the LRADC block are
* switched into special mode of operation. These channels can not
* be sampled as regular LRADC channels. The driver will refuse any
* attempt to sample these channels.
*/
#define CHAN_MASK_TOUCHBUTTON (BIT(1) | BIT(0))
#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 2)
#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 2)
enum mxs_lradc_ts use_touchscreen;
bool use_touchbutton;
struct input_dev *ts_input;
enum mxs_lradc_id soc;
enum lradc_ts_plate cur_plate; /* state machine */
bool ts_valid;
unsigned ts_x_pos;
unsigned ts_y_pos;
unsigned ts_pressure;
/* handle touchscreen's physical behaviour */
/* samples per coordinate */
unsigned over_sample_cnt;
/* time clocks between samples */
unsigned over_sample_delay;
/* time in clocks to wait after the plates where switched */
unsigned settling_delay;
};
#define LRADC_CTRL0 0x00
# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE BIT(23)
# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE BIT(22)
# define LRADC_CTRL0_MX28_YNNSW /* YM */ BIT(21)
# define LRADC_CTRL0_MX28_YPNSW /* YP */ BIT(20)
# define LRADC_CTRL0_MX28_YPPSW /* YP */ BIT(19)
# define LRADC_CTRL0_MX28_XNNSW /* XM */ BIT(18)
# define LRADC_CTRL0_MX28_XNPSW /* XM */ BIT(17)
# define LRADC_CTRL0_MX28_XPPSW /* XP */ BIT(16)
# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE BIT(20)
# define LRADC_CTRL0_MX23_YM BIT(19)
# define LRADC_CTRL0_MX23_XM BIT(18)
# define LRADC_CTRL0_MX23_YP BIT(17)
# define LRADC_CTRL0_MX23_XP BIT(16)
# define LRADC_CTRL0_MX28_PLATE_MASK \
(LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \
LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \
LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \
LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW)
# define LRADC_CTRL0_MX23_PLATE_MASK \
(LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \
LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \
LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP)
#define LRADC_CTRL1 0x10
#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN BIT(24)
#define LRADC_CTRL1_LRADC_IRQ_EN(n) (1 << ((n) + 16))
#define LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK (0x1fff << 16)
#define LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK (0x01ff << 16)
#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET 16
#define LRADC_CTRL1_TOUCH_DETECT_IRQ BIT(8)
#define LRADC_CTRL1_LRADC_IRQ(n) (1 << (n))
#define LRADC_CTRL1_MX28_LRADC_IRQ_MASK 0x1fff
#define LRADC_CTRL1_MX23_LRADC_IRQ_MASK 0x01ff
#define LRADC_CTRL1_LRADC_IRQ_OFFSET 0
#define LRADC_CTRL2 0x20
#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET 24
#define LRADC_CTRL2_TEMPSENSE_PWD BIT(15)
#define LRADC_STATUS 0x40
#define LRADC_STATUS_TOUCH_DETECT_RAW BIT(0)
#define LRADC_CH(n) (0x50 + (0x10 * (n)))
#define LRADC_CH_ACCUMULATE BIT(29)
#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24)
#define LRADC_CH_NUM_SAMPLES_OFFSET 24
#define LRADC_CH_NUM_SAMPLES(x) \
((x) << LRADC_CH_NUM_SAMPLES_OFFSET)
#define LRADC_CH_VALUE_MASK 0x3ffff
#define LRADC_CH_VALUE_OFFSET 0
#define LRADC_DELAY(n) (0xd0 + (0x10 * (n)))
#define LRADC_DELAY_TRIGGER_LRADCS_MASK (0xffUL << 24)
#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET 24
#define LRADC_DELAY_TRIGGER(x) \
(((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \
LRADC_DELAY_TRIGGER_LRADCS_MASK)
#define LRADC_DELAY_KICK BIT(20)
#define LRADC_DELAY_TRIGGER_DELAYS_MASK (0xf << 16)
#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET 16
#define LRADC_DELAY_TRIGGER_DELAYS(x) \
(((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \
LRADC_DELAY_TRIGGER_DELAYS_MASK)
#define LRADC_DELAY_LOOP_COUNT_MASK (0x1f << 11)
#define LRADC_DELAY_LOOP_COUNT_OFFSET 11
#define LRADC_DELAY_LOOP(x) \
(((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \
LRADC_DELAY_LOOP_COUNT_MASK)
#define LRADC_DELAY_DELAY_MASK 0x7ff
#define LRADC_DELAY_DELAY_OFFSET 0
#define LRADC_DELAY_DELAY(x) \
(((x) << LRADC_DELAY_DELAY_OFFSET) & \
LRADC_DELAY_DELAY_MASK)
#define LRADC_CTRL4 0x140
#define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4))
#define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4)
#define LRADC_CTRL4_LRADCSELECT(n, x) \
(((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \
LRADC_CTRL4_LRADCSELECT_MASK(n))
#define LRADC_RESOLUTION 12
#define LRADC_SINGLE_SAMPLE_MASK ((1 << LRADC_RESOLUTION) - 1)
static void mxs_lradc_reg_set(struct mxs_lradc *lradc, u32 val, u32 reg)
{
writel(val, lradc->base + reg + STMP_OFFSET_REG_SET);
}
static void mxs_lradc_reg_clear(struct mxs_lradc *lradc, u32 val, u32 reg)
{
writel(val, lradc->base + reg + STMP_OFFSET_REG_CLR);
}
static void mxs_lradc_reg_wrt(struct mxs_lradc *lradc, u32 val, u32 reg)
{
writel(val, lradc->base + reg);
}
static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
{
if (lradc->soc == IMX23_LRADC)
return LRADC_CTRL0_MX23_PLATE_MASK;
return LRADC_CTRL0_MX28_PLATE_MASK;
}
static u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc)
{
if (lradc->soc == IMX23_LRADC)
return LRADC_CTRL1_MX23_LRADC_IRQ_MASK;
return LRADC_CTRL1_MX28_LRADC_IRQ_MASK;
}
static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
{
if (lradc->soc == IMX23_LRADC)
return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
}
static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
{
if (lradc->soc == IMX23_LRADC)
return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
}
static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
{
if (lradc->soc == IMX23_LRADC)
return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
}
static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
{
if (lradc->soc == IMX23_LRADC)
return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
}
static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
{
return !!(readl(lradc->base + LRADC_STATUS) &
LRADC_STATUS_TOUCH_DETECT_RAW);
}
static void mxs_lradc_map_channel(struct mxs_lradc *lradc, unsigned vch,
unsigned ch)
{
mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
LRADC_CTRL4);
mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
}
static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch)
{
/*
* prepare for oversampling conversion
*
* from the datasheet:
* "The ACCUMULATE bit in the appropriate channel register
* HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
* otherwise, the IRQs will not fire."
*/
mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
LRADC_CH_NUM_SAMPLES(lradc->over_sample_cnt - 1),
LRADC_CH(ch));
/*
* from the datasheet:
* "Software must clear this register in preparation for a
* multi-cycle accumulation.
*/
mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
/*
* prepare the delay/loop unit according to the oversampling count
*
* from the datasheet:
* "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
* HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
* the LRADC will not trigger the delay group."
*/
mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
LRADC_DELAY_TRIGGER_DELAYS(0) |
LRADC_DELAY_LOOP(lradc->over_sample_cnt - 1) |
LRADC_DELAY_DELAY(lradc->over_sample_delay - 1),
LRADC_DELAY(3));
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
/*
* after changing the touchscreen plates setting
* the signals need some initial time to settle. Start the
* SoC's delay unit and start the conversion later
* and automatically.
*/
mxs_lradc_reg_wrt(
lradc,
LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
LRADC_DELAY_KICK |
LRADC_DELAY_DELAY(lradc->settling_delay),
LRADC_DELAY(2));
}
/*
* Pressure detection is special:
* We want to do both required measurements for the pressure detection in
* one turn. Use the hardware features to chain both conversions and let the
* hardware report one interrupt if both conversions are done
*/
static void mxs_lradc_setup_ts_pressure(struct mxs_lradc *lradc, unsigned ch1,
unsigned ch2)
{
u32 reg;
/*
* prepare for oversampling conversion
*
* from the datasheet:
* "The ACCUMULATE bit in the appropriate channel register
* HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
* otherwise, the IRQs will not fire."
*/
reg = LRADC_CH_ACCUMULATE |
LRADC_CH_NUM_SAMPLES(lradc->over_sample_cnt - 1);
mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
/*
* from the datasheet:
* "Software must clear this register in preparation for a
* multi-cycle accumulation.
*/
mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
/* prepare the delay/loop unit according to the oversampling count */
mxs_lradc_reg_wrt(
lradc,
LRADC_DELAY_TRIGGER(1 << ch1) |
LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
LRADC_DELAY_TRIGGER_DELAYS(0) |
LRADC_DELAY_LOOP(lradc->over_sample_cnt - 1) |
LRADC_DELAY_DELAY(lradc->over_sample_delay - 1),
LRADC_DELAY(3));
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
/*
* after changing the touchscreen plates setting
* the signals need some initial time to settle. Start the
* SoC's delay unit and start the conversion later
* and automatically.
*/
mxs_lradc_reg_wrt(
lradc,
LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
LRADC_DELAY_KICK |
LRADC_DELAY_DELAY(lradc->settling_delay), LRADC_DELAY(2));
}
static unsigned mxs_lradc_read_raw_channel(struct mxs_lradc *lradc,
unsigned channel)
{
u32 reg;
unsigned num_samples, val;
reg = readl(lradc->base + LRADC_CH(channel));
if (reg & LRADC_CH_ACCUMULATE)
num_samples = lradc->over_sample_cnt;
else
num_samples = 1;
val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
return val / num_samples;
}
static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc *lradc,
unsigned ch1, unsigned ch2)
{
u32 reg, mask;
unsigned pressure, m1, m2;
mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
reg = readl(lradc->base + LRADC_CTRL1) & mask;
while (reg != mask) {
reg = readl(lradc->base + LRADC_CTRL1) & mask;
dev_dbg(lradc->dev, "One channel is still busy: %X\n", reg);
}
m1 = mxs_lradc_read_raw_channel(lradc, ch1);
m2 = mxs_lradc_read_raw_channel(lradc, ch2);
if (m2 == 0) {
dev_warn(lradc->dev, "Cannot calculate pressure\n");
return 1 << (LRADC_RESOLUTION - 1);
}
/* simply scale the value from 0 ... max ADC resolution */
pressure = m1;
pressure *= (1 << LRADC_RESOLUTION);
pressure /= m2;
dev_dbg(lradc->dev, "Pressure = %u\n", pressure);
return pressure;
}
#define TS_CH_XP 2
#define TS_CH_YP 3
#define TS_CH_XM 4
#define TS_CH_YM 5
/*
* YP(open)--+-------------+
* | |--+
* | | |
* YM(-)--+-------------+ |
* +--------------+
* | |
* XP(weak+) XM(open)
*
* "weak+" means 200k Ohm VDDIO
* (-) means GND
*/
static void mxs_lradc_setup_touch_detection(struct mxs_lradc *lradc)
{
/*
* In order to detect a touch event the 'touch detect enable' bit
* enables:
* - a weak pullup to the X+ connector
* - a strong ground at the Y- connector
*/
mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
LRADC_CTRL0);
}
/*
* YP(meas)--+-------------+
* | |--+
* | | |
* YM(open)--+-------------+ |
* +--------------+
* | |
* XP(+) XM(-)
*
* (+) means here 1.85 V
* (-) means here GND
*/
static void mxs_lradc_prepare_x_pos(struct mxs_lradc *lradc)
{
mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
lradc->cur_plate = LRADC_SAMPLE_X;
mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1);
}
/*
* YP(+)--+-------------+
* | |--+
* | | |
* YM(-)--+-------------+ |
* +--------------+
* | |
* XP(open) XM(meas)
*
* (+) means here 1.85 V
* (-) means here GND
*/
static void mxs_lradc_prepare_y_pos(struct mxs_lradc *lradc)
{
mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
lradc->cur_plate = LRADC_SAMPLE_Y;
mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1);
}
/*
* YP(+)--+-------------+
* | |--+
* | | |
* YM(meas)--+-------------+ |
* +--------------+
* | |
* XP(meas) XM(-)
*
* (+) means here 1.85 V
* (-) means here GND
*/
static void mxs_lradc_prepare_pressure(struct mxs_lradc *lradc)
{
mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
lradc->cur_plate = LRADC_SAMPLE_PRESSURE;
mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
mxs_lradc_setup_ts_pressure(lradc, TOUCHSCREEN_VCHANNEL2,
TOUCHSCREEN_VCHANNEL1);
}
static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc)
{
/* Configure the touchscreen type */
if (lradc->soc == IMX28_LRADC) {
mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
LRADC_CTRL0);
if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
mxs_lradc_reg_set(lradc,
LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
LRADC_CTRL0);
}
mxs_lradc_setup_touch_detection(lradc);
lradc->cur_plate = LRADC_TOUCH;
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
}
static void mxs_lradc_start_touch_event(struct mxs_lradc *lradc)
{
mxs_lradc_reg_clear(lradc,
LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
LRADC_CTRL1);
mxs_lradc_reg_set(lradc,
LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
LRADC_CTRL1);
/*
* start with the Y-pos, because it uses nearly the same plate
* settings like the touch detection
*/
mxs_lradc_prepare_y_pos(lradc);
}
static void mxs_lradc_report_ts_event(struct mxs_lradc *lradc)
{
input_report_abs(lradc->ts_input, ABS_X, lradc->ts_x_pos);
input_report_abs(lradc->ts_input, ABS_Y, lradc->ts_y_pos);
input_report_abs(lradc->ts_input, ABS_PRESSURE, lradc->ts_pressure);
input_report_key(lradc->ts_input, BTN_TOUCH, 1);
input_sync(lradc->ts_input);
}
static void mxs_lradc_complete_touch_event(struct mxs_lradc *lradc)
{
mxs_lradc_setup_touch_detection(lradc);
lradc->cur_plate = LRADC_SAMPLE_VALID;
/*
* start a dummy conversion to burn time to settle the signals
* note: we are not interested in the conversion's value
*/
mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
mxs_lradc_reg_clear(lradc,
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
LRADC_CTRL1);
mxs_lradc_reg_wrt(
lradc,
LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
LRADC_DELAY(2));
}
/*
* in order to avoid false measurements, report only samples where
* the surface is still touched after the position measurement
*/
static void mxs_lradc_finish_touch_event(struct mxs_lradc *lradc, bool valid)
{
/* if it is still touched, report the sample */
if (valid && mxs_lradc_check_touch_event(lradc)) {
lradc->ts_valid = true;
mxs_lradc_report_ts_event(lradc);
}
/* if it is even still touched, continue with the next measurement */
if (mxs_lradc_check_touch_event(lradc)) {
mxs_lradc_prepare_y_pos(lradc);
return;
}
if (lradc->ts_valid) {
/* signal the release */
lradc->ts_valid = false;
input_report_key(lradc->ts_input, BTN_TOUCH, 0);
input_sync(lradc->ts_input);
}
/* if it is released, wait for the next touch via IRQ */
lradc->cur_plate = LRADC_TOUCH;
mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
mxs_lradc_reg_clear(lradc,
LRADC_CTRL1_TOUCH_DETECT_IRQ |
LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
LRADC_CTRL1);
mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
}
/* touchscreen's state machine */
static void mxs_lradc_handle_touch(struct mxs_lradc *lradc)
{
switch (lradc->cur_plate) {
case LRADC_TOUCH:
if (mxs_lradc_check_touch_event(lradc))
mxs_lradc_start_touch_event(lradc);
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
LRADC_CTRL1);
return;
case LRADC_SAMPLE_Y:
lradc->ts_y_pos =
mxs_lradc_read_raw_channel(lradc,
TOUCHSCREEN_VCHANNEL1);
mxs_lradc_prepare_x_pos(lradc);
return;
case LRADC_SAMPLE_X:
lradc->ts_x_pos =
mxs_lradc_read_raw_channel(lradc,
TOUCHSCREEN_VCHANNEL1);
mxs_lradc_prepare_pressure(lradc);
return;
case LRADC_SAMPLE_PRESSURE:
lradc->ts_pressure =
mxs_lradc_read_ts_pressure(lradc,
TOUCHSCREEN_VCHANNEL2,
TOUCHSCREEN_VCHANNEL1);
mxs_lradc_complete_touch_event(lradc);
return;
case LRADC_SAMPLE_VALID:
mxs_lradc_finish_touch_event(lradc, 1);
break;
}
}
/*
* Raw I/O operations
*/
static int mxs_lradc_read_single(struct iio_dev *iio_dev, int chan, int *val)
{
struct mxs_lradc *lradc = iio_priv(iio_dev);
int ret;
/*
* See if there is no buffered operation in progress. If there is, simply
* bail out. This can be improved to support both buffered and raw IO at
* the same time, yet the code becomes horribly complicated. Therefore I
* applied KISS principle here.
*/
ret = mutex_trylock(&lradc->lock);
if (!ret)
return -EBUSY;
reinit_completion(&lradc->completion);
/*
* No buffered operation in progress, map the channel and trigger it.
* Virtual channel 0 is always used here as the others are always not
* used if doing raw sampling.
*/
if (lradc->soc == IMX28_LRADC)
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0),
LRADC_CTRL1);
mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0);
/* Enable / disable the divider per requirement */
if (test_bit(chan, &lradc->is_divided))
mxs_lradc_reg_set(lradc,
1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
LRADC_CTRL2);
else
mxs_lradc_reg_clear(lradc,
1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
LRADC_CTRL2);
/* Clean the slot's previous content, then set new one. */
mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0),
LRADC_CTRL4);
mxs_lradc_reg_set(lradc, chan, LRADC_CTRL4);
mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0));
/* Enable the IRQ and start sampling the channel. */
mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
mxs_lradc_reg_set(lradc, BIT(0), LRADC_CTRL0);
/* Wait for completion on the channel, 1 second max. */
ret = wait_for_completion_killable_timeout(&lradc->completion, HZ);
if (!ret)
ret = -ETIMEDOUT;
if (ret < 0)
goto err;
/* Read the data. */
*val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
ret = IIO_VAL_INT;
err:
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
mutex_unlock(&lradc->lock);
return ret;
}
static int mxs_lradc_read_temp(struct iio_dev *iio_dev, int *val)
{
int ret, min, max;
ret = mxs_lradc_read_single(iio_dev, 8, &min);
if (ret != IIO_VAL_INT)
return ret;
ret = mxs_lradc_read_single(iio_dev, 9, &max);
if (ret != IIO_VAL_INT)
return ret;
*val = max - min;
return IIO_VAL_INT;
}
static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long m)
{
struct mxs_lradc *lradc = iio_priv(iio_dev);
switch (m) {
case IIO_CHAN_INFO_RAW:
if (chan->type == IIO_TEMP)
return mxs_lradc_read_temp(iio_dev, val);
return mxs_lradc_read_single(iio_dev, chan->channel, val);
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_TEMP) {
/*
* From the datasheet, we have to multiply by 1.012 and
* divide by 4
*/
*val = 0;
*val2 = 253000;
return IIO_VAL_INT_PLUS_MICRO;
}
*val = lradc->vref_mv[chan->channel];
*val2 = chan->scan_type.realbits -
test_bit(chan->channel, &lradc->is_divided);
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_OFFSET:
if (chan->type == IIO_TEMP) {
/*
* The calculated value from the ADC is in Kelvin, we
* want Celsius for hwmon so the offset is -273.15
* The offset is applied before scaling so it is
* actually -213.15 * 4 / 1.012 = -1079.644268
*/
*val = -1079;
*val2 = 644268;
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
default:
break;
}
return -EINVAL;
}
static int mxs_lradc_write_raw(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
int val, int val2, long m)
{
struct mxs_lradc *lradc = iio_priv(iio_dev);
struct mxs_lradc_scale *scale_avail =
lradc->scale_avail[chan->channel];
int ret;
ret = mutex_trylock(&lradc->lock);
if (!ret)
return -EBUSY;
switch (m) {
case IIO_CHAN_INFO_SCALE:
ret = -EINVAL;
if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
/* divider by two disabled */
clear_bit(chan->channel, &lradc->is_divided);
ret = 0;
} else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
/* divider by two enabled */
set_bit(chan->channel, &lradc->is_divided);
ret = 0;
}
break;
default:
ret = -EINVAL;
break;
}
mutex_unlock(&lradc->lock);
return ret;
}
static int mxs_lradc_write_raw_get_fmt(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
long m)
{
return IIO_VAL_INT_PLUS_NANO;
}
static ssize_t mxs_lradc_show_scale_available_ch(struct device *dev,
struct device_attribute *attr,
char *buf,
int ch)
{
struct iio_dev *iio = dev_to_iio_dev(dev);
struct mxs_lradc *lradc = iio_priv(iio);
int i, len = 0;
for (i = 0; i < ARRAY_SIZE(lradc->scale_avail[ch]); i++)
len += sprintf(buf + len, "%u.%09u ",
lradc->scale_avail[ch][i].integer,
lradc->scale_avail[ch][i].nano);
len += sprintf(buf + len, "\n");
return len;
}
static ssize_t mxs_lradc_show_scale_available(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
return mxs_lradc_show_scale_available_ch(dev, attr, buf,
iio_attr->address);
}
#define SHOW_SCALE_AVAILABLE_ATTR(ch) \
static IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, S_IRUGO, \
mxs_lradc_show_scale_available, NULL, ch)
SHOW_SCALE_AVAILABLE_ATTR(0);
SHOW_SCALE_AVAILABLE_ATTR(1);
SHOW_SCALE_AVAILABLE_ATTR(2);
SHOW_SCALE_AVAILABLE_ATTR(3);
SHOW_SCALE_AVAILABLE_ATTR(4);
SHOW_SCALE_AVAILABLE_ATTR(5);
SHOW_SCALE_AVAILABLE_ATTR(6);
SHOW_SCALE_AVAILABLE_ATTR(7);
SHOW_SCALE_AVAILABLE_ATTR(10);
SHOW_SCALE_AVAILABLE_ATTR(11);
SHOW_SCALE_AVAILABLE_ATTR(12);
SHOW_SCALE_AVAILABLE_ATTR(13);
SHOW_SCALE_AVAILABLE_ATTR(14);
SHOW_SCALE_AVAILABLE_ATTR(15);
static struct attribute *mxs_lradc_attributes[] = {
&iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
NULL
};
static const struct attribute_group mxs_lradc_attribute_group = {
.attrs = mxs_lradc_attributes,
};
static const struct iio_info mxs_lradc_iio_info = {
.driver_module = THIS_MODULE,
.read_raw = mxs_lradc_read_raw,
.write_raw = mxs_lradc_write_raw,
.write_raw_get_fmt = mxs_lradc_write_raw_get_fmt,
.attrs = &mxs_lradc_attribute_group,
};
static int mxs_lradc_ts_open(struct input_dev *dev)
{
struct mxs_lradc *lradc = input_get_drvdata(dev);
/* Enable the touch-detect circuitry. */
mxs_lradc_enable_touch_detection(lradc);
return 0;
}
static void mxs_lradc_disable_ts(struct mxs_lradc *lradc)
{
/* stop all interrupts from firing */
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
/* Power-down touchscreen touch-detect circuitry. */
mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
}
static void mxs_lradc_ts_close(struct input_dev *dev)
{
struct mxs_lradc *lradc = input_get_drvdata(dev);
mxs_lradc_disable_ts(lradc);
}
static int mxs_lradc_ts_register(struct mxs_lradc *lradc)
{
struct input_dev *input;
struct device *dev = lradc->dev;
if (!lradc->use_touchscreen)
return 0;
input = devm_input_allocate_device(dev);
if (!input)
return -ENOMEM;
input->name = DRIVER_NAME;
input->id.bustype = BUS_HOST;
input->open = mxs_lradc_ts_open;
input->close = mxs_lradc_ts_close;
__set_bit(EV_ABS, input->evbit);
__set_bit(EV_KEY, input->evbit);
__set_bit(BTN_TOUCH, input->keybit);
__set_bit(INPUT_PROP_DIRECT, input->propbit);
input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
0, 0);
lradc->ts_input = input;
input_set_drvdata(input, lradc);
return input_register_device(input);
}
/*
* IRQ Handling
*/
static irqreturn_t mxs_lradc_handle_irq(int irq, void *data)
{
struct iio_dev *iio = data;
struct mxs_lradc *lradc = iio_priv(iio);
unsigned long reg = readl(lradc->base + LRADC_CTRL1);
u32 clr_irq = mxs_lradc_irq_mask(lradc);
const u32 ts_irq_mask =
LRADC_CTRL1_TOUCH_DETECT_IRQ |
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
if (!(reg & mxs_lradc_irq_mask(lradc)))
return IRQ_NONE;
if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
mxs_lradc_handle_touch(lradc);
/* Make sure we don't clear the next conversion's interrupt. */
clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
}
if (iio_buffer_enabled(iio)) {
if (reg & lradc->buffer_vchans)
iio_trigger_poll(iio->trig);
} else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
complete(&lradc->completion);
}
mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
return IRQ_HANDLED;
}
/*
* Trigger handling
*/
static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *iio = pf->indio_dev;
struct mxs_lradc *lradc = iio_priv(iio);
const u32 chan_value = LRADC_CH_ACCUMULATE |
((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
unsigned int i, j = 0;
for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
lradc->buffer[j] = readl(lradc->base + LRADC_CH(j));
mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(j));
lradc->buffer[j] &= LRADC_CH_VALUE_MASK;
lradc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
j++;
}
iio_push_to_buffers_with_timestamp(iio, lradc->buffer, pf->timestamp);
iio_trigger_notify_done(iio->trig);
return IRQ_HANDLED;
}
static int mxs_lradc_configure_trigger(struct iio_trigger *trig, bool state)
{
struct iio_dev *iio = iio_trigger_get_drvdata(trig);
struct mxs_lradc *lradc = iio_priv(iio);
const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
mxs_lradc_reg_wrt(lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st);
return 0;
}
static const struct iio_trigger_ops mxs_lradc_trigger_ops = {
.owner = THIS_MODULE,
.set_trigger_state = &mxs_lradc_configure_trigger,
};
static int mxs_lradc_trigger_init(struct iio_dev *iio)
{
int ret;
struct iio_trigger *trig;
struct mxs_lradc *lradc = iio_priv(iio);
trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id);
if (!trig)
return -ENOMEM;
trig->dev.parent = lradc->dev;
iio_trigger_set_drvdata(trig, iio);
trig->ops = &mxs_lradc_trigger_ops;
ret = iio_trigger_register(trig);
if (ret) {
iio_trigger_free(trig);
return ret;
}
lradc->trig = trig;
return 0;
}
static void mxs_lradc_trigger_remove(struct iio_dev *iio)
{
struct mxs_lradc *lradc = iio_priv(iio);
iio_trigger_unregister(lradc->trig);
iio_trigger_free(lradc->trig);
}
static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
{
struct mxs_lradc *lradc = iio_priv(iio);
int ret = 0, chan, ofs = 0;
unsigned long enable = 0;
u32 ctrl4_set = 0;
u32 ctrl4_clr = 0;
u32 ctrl1_irq = 0;
const u32 chan_value = LRADC_CH_ACCUMULATE |
((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
const int len = bitmap_weight(iio->active_scan_mask,
LRADC_MAX_TOTAL_CHANS);
if (!len)
return -EINVAL;
/*
* Lock the driver so raw access can not be done during buffered
* operation. This simplifies the code a lot.
*/
ret = mutex_trylock(&lradc->lock);
if (!ret)
return -EBUSY;
lradc->buffer = kmalloc_array(len, sizeof(*lradc->buffer), GFP_KERNEL);
if (!lradc->buffer) {
ret = -ENOMEM;
goto err_mem;
}
if (lradc->soc == IMX28_LRADC)
mxs_lradc_reg_clear(
lradc,
lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
LRADC_CTRL1);
mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs));
bitmap_set(&enable, ofs, 1);
ofs++;
}
mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
LRADC_DELAY_KICK, LRADC_DELAY(0));
mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4);
mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4);
mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1);
mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
LRADC_DELAY(0));
return 0;
err_mem:
mutex_unlock(&lradc->lock);
return ret;
}
static int mxs_lradc_buffer_postdisable(struct iio_dev *iio)
{
struct mxs_lradc *lradc = iio_priv(iio);
mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
LRADC_DELAY_KICK, LRADC_DELAY(0));
mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
if (lradc->soc == IMX28_LRADC)
mxs_lradc_reg_clear(
lradc,
lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
LRADC_CTRL1);
kfree(lradc->buffer);
mutex_unlock(&lradc->lock);
return 0;
}
static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio,
const unsigned long *mask)
{
struct mxs_lradc *lradc = iio_priv(iio);
const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
int rsvd_chans = 0;
unsigned long rsvd_mask = 0;
if (lradc->use_touchbutton)
rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE)
rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
if (lradc->use_touchbutton)
rsvd_chans++;
if (lradc->use_touchscreen)
rsvd_chans += 2;
/* Test for attempts to map channels with special mode of operation. */
if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
return false;
/* Test for attempts to map more channels then available slots. */
if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
return false;
return true;
}
static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = {
.preenable = &mxs_lradc_buffer_preenable,
.postenable = &iio_triggered_buffer_postenable,
.predisable = &iio_triggered_buffer_predisable,
.postdisable = &mxs_lradc_buffer_postdisable,
.validate_scan_mask = &mxs_lradc_validate_scan_mask,
};
/*
* Driver initialization
*/
#define MXS_ADC_CHAN(idx, chan_type, name) { \
.type = (chan_type), \
.indexed = 1, \
.scan_index = (idx), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.channel = (idx), \
.address = (idx), \
.scan_type = { \
.sign = 'u', \
.realbits = LRADC_RESOLUTION, \
.storagebits = 32, \
}, \
.datasheet_name = (name), \
}
static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
/* Combined Temperature sensors */
{
.type = IIO_TEMP,
.indexed = 1,
.scan_index = 8,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
.channel = 8,
.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
.datasheet_name = "TEMP_DIE",
},
/* Hidden channel to keep indexes */
{
.type = IIO_TEMP,
.indexed = 1,
.scan_index = -1,
.channel = 9,
},
MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
};
static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
/* Combined Temperature sensors */
{
.type = IIO_TEMP,
.indexed = 1,
.scan_index = 8,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
.channel = 8,
.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
.datasheet_name = "TEMP_DIE",
},
/* Hidden channel to keep indexes */
{
.type = IIO_TEMP,
.indexed = 1,
.scan_index = -1,
.channel = 9,
},
MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
};
static void mxs_lradc_hw_init(struct mxs_lradc *lradc)
{
/* The ADC always uses DELAY CHANNEL 0. */
const u32 adc_cfg =
(1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
(LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
/* Configure DELAY CHANNEL 0 for generic ADC sampling. */
mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0));
/* Disable remaining DELAY CHANNELs */
mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(1));
mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
/* Start internal temperature sensing. */
mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2);
}
static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
{
int i;
mxs_lradc_reg_clear(lradc,
lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
LRADC_CTRL1);
for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i));
}
static const struct of_device_id mxs_lradc_dt_ids[] = {
{ .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
{ .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
static int mxs_lradc_probe_touchscreen(struct mxs_lradc *lradc,
struct device_node *lradc_node)
{
int ret;
u32 ts_wires = 0, adapt;
ret = of_property_read_u32(lradc_node, "fsl,lradc-touchscreen-wires",
&ts_wires);
if (ret)
return -ENODEV; /* touchscreen feature disabled */
switch (ts_wires) {
case 4:
lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
break;
case 5:
if (lradc->soc == IMX28_LRADC) {
lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
break;
}
/* fall through an error message for i.MX23 */
default:
dev_err(lradc->dev,
"Unsupported number of touchscreen wires (%d)\n",
ts_wires);
return -EINVAL;
}
if (of_property_read_u32(lradc_node, "fsl,ave-ctrl", &adapt)) {
lradc->over_sample_cnt = 4;
} else {
if (adapt < 1 || adapt > 32) {
dev_err(lradc->dev, "Invalid sample count (%u)\n",
adapt);
return -EINVAL;
}
lradc->over_sample_cnt = adapt;
}
if (of_property_read_u32(lradc_node, "fsl,ave-delay", &adapt)) {
lradc->over_sample_delay = 2;
} else {
if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
dev_err(lradc->dev, "Invalid sample delay (%u)\n",
adapt);
return -EINVAL;
}
lradc->over_sample_delay = adapt;
}
if (of_property_read_u32(lradc_node, "fsl,settling", &adapt)) {
lradc->settling_delay = 10;
} else {
if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
dev_err(lradc->dev, "Invalid settling delay (%u)\n",
adapt);
return -EINVAL;
}
lradc->settling_delay = adapt;
}
return 0;
}
static int mxs_lradc_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(mxs_lradc_dt_ids, &pdev->dev);
const struct mxs_lradc_of_config *of_cfg =
&mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data];
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct mxs_lradc *lradc;
struct iio_dev *iio;
struct resource *iores;
int ret = 0, touch_ret;
int i, s;
u64 scale_uv;
/* Allocate the IIO device. */
iio = devm_iio_device_alloc(dev, sizeof(*lradc));
if (!iio) {
dev_err(dev, "Failed to allocate IIO device\n");
return -ENOMEM;
}
lradc = iio_priv(iio);
lradc->soc = (enum mxs_lradc_id)of_id->data;
/* Grab the memory area */
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
lradc->dev = &pdev->dev;
lradc->base = devm_ioremap_resource(dev, iores);
if (IS_ERR(lradc->base))
return PTR_ERR(lradc->base);
lradc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(lradc->clk)) {
dev_err(dev, "Failed to get the delay unit clock\n");
return PTR_ERR(lradc->clk);
}
ret = clk_prepare_enable(lradc->clk);
if (ret != 0) {
dev_err(dev, "Failed to enable the delay unit clock\n");
return ret;
}
touch_ret = mxs_lradc_probe_touchscreen(lradc, node);
if (touch_ret == 0)
lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
else
lradc->buffer_vchans = BUFFER_VCHANS_ALL;
/* Grab all IRQ sources */
for (i = 0; i < of_cfg->irq_count; i++) {
lradc->irq[i] = platform_get_irq(pdev, i);
if (lradc->irq[i] < 0) {
ret = lradc->irq[i];
goto err_clk;
}
ret = devm_request_irq(dev, lradc->irq[i],
mxs_lradc_handle_irq, 0,
of_cfg->irq_name[i], iio);
if (ret)
goto err_clk;
}
lradc->vref_mv = of_cfg->vref_mv;
platform_set_drvdata(pdev, iio);
init_completion(&lradc->completion);
mutex_init(&lradc->lock);
iio->name = pdev->name;
iio->dev.parent = &pdev->dev;
iio->info = &mxs_lradc_iio_info;
iio->modes = INDIO_DIRECT_MODE;
iio->masklength = LRADC_MAX_TOTAL_CHANS;
if (lradc->soc == IMX23_LRADC) {
iio->channels = mx23_lradc_chan_spec;
iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
} else {
iio->channels = mx28_lradc_chan_spec;
iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
}
ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
&mxs_lradc_trigger_handler,
&mxs_lradc_buffer_ops);
if (ret)
goto err_clk;
ret = mxs_lradc_trigger_init(iio);
if (ret)
goto err_trig;
/* Populate available ADC input ranges */
for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
for (s = 0; s < ARRAY_SIZE(lradc->scale_avail[i]); s++) {
/*
* [s=0] = optional divider by two disabled (default)
* [s=1] = optional divider by two enabled
*
* The scale is calculated by doing:
* Vref >> (realbits - s)
* which multiplies by two on the second component
* of the array.
*/
scale_uv = ((u64)lradc->vref_mv[i] * 100000000) >>
(LRADC_RESOLUTION - s);
lradc->scale_avail[i][s].nano =
do_div(scale_uv, 100000000) * 10;
lradc->scale_avail[i][s].integer = scale_uv;
}
}
ret = stmp_reset_block(lradc->base);
if (ret)
goto err_dev;
/* Configure the hardware. */
mxs_lradc_hw_init(lradc);
/* Register the touchscreen input device. */
if (touch_ret == 0) {
ret = mxs_lradc_ts_register(lradc);
if (ret)
goto err_ts_register;
}
/* Register IIO device. */
ret = iio_device_register(iio);
if (ret) {
dev_err(dev, "Failed to register IIO device\n");
return ret;
}
return 0;
err_ts_register:
mxs_lradc_hw_stop(lradc);
err_dev:
mxs_lradc_trigger_remove(iio);
err_trig:
iio_triggered_buffer_cleanup(iio);
err_clk:
clk_disable_unprepare(lradc->clk);
return ret;
}
static int mxs_lradc_remove(struct platform_device *pdev)
{
struct iio_dev *iio = platform_get_drvdata(pdev);
struct mxs_lradc *lradc = iio_priv(iio);
iio_device_unregister(iio);
mxs_lradc_hw_stop(lradc);
mxs_lradc_trigger_remove(iio);
iio_triggered_buffer_cleanup(iio);
clk_disable_unprepare(lradc->clk);
return 0;
}
static struct platform_driver mxs_lradc_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = mxs_lradc_dt_ids,
},
.probe = mxs_lradc_probe,
.remove = mxs_lradc_remove,
};
module_platform_driver(mxs_lradc_driver);
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_DESCRIPTION("Freescale MXS LRADC driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRIVER_NAME);
...@@ -316,6 +316,16 @@ config INPUT_COBALT_BTNS ...@@ -316,6 +316,16 @@ config INPUT_COBALT_BTNS
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called cobalt_btns. module will be called cobalt_btns.
config INPUT_CPCAP_PWRBUTTON
tristate "CPCAP OnKey"
depends on MFD_CPCAP
help
Say Y here if you want to enable power key reporting via the
Motorola CPCAP chip.
To compile this driver as a module, choose M here. The module will
be called cpcap-pwrbutton.
config INPUT_WISTRON_BTNS config INPUT_WISTRON_BTNS
tristate "x86 Wistron laptop button interface" tristate "x86 Wistron laptop button interface"
depends on X86_32 depends on X86_32
......
...@@ -24,6 +24,7 @@ obj-$(CONFIG_INPUT_CM109) += cm109.o ...@@ -24,6 +24,7 @@ obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_CPCAP_PWRBUTTON) += cpcap-pwrbutton.o
obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
obj-$(CONFIG_INPUT_DA9063_ONKEY) += da9063_onkey.o obj-$(CONFIG_INPUT_DA9063_ONKEY) += da9063_onkey.o
......
/**
* CPCAP Power Button Input Driver
*
* Copyright (C) 2017 Sebastian Reichel <sre@kernel.org>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/mfd/motorola-cpcap.h>
#define CPCAP_IRQ_ON 23
#define CPCAP_IRQ_ON_BITMASK (1 << (CPCAP_IRQ_ON % 16))
struct cpcap_power_button {
struct regmap *regmap;
struct input_dev *idev;
struct device *dev;
};
static irqreturn_t powerbutton_irq(int irq, void *_button)
{
struct cpcap_power_button *button = _button;
int val;
val = cpcap_sense_virq(button->regmap, irq);
if (val < 0) {
dev_err(button->dev, "irq read failed: %d", val);
return IRQ_HANDLED;
}
pm_wakeup_event(button->dev, 0);
input_report_key(button->idev, KEY_POWER, val);
input_sync(button->idev);
return IRQ_HANDLED;
}
static int cpcap_power_button_probe(struct platform_device *pdev)
{
struct cpcap_power_button *button;
int irq = platform_get_irq(pdev, 0);
int err;
button = devm_kmalloc(&pdev->dev, sizeof(*button), GFP_KERNEL);
if (!button)
return -ENOMEM;
button->idev = devm_input_allocate_device(&pdev->dev);
if (!button->idev)
return -ENOMEM;
button->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!button->regmap)
return -ENODEV;
button->dev = &pdev->dev;
button->idev->name = "cpcap-pwrbutton";
button->idev->phys = "cpcap-pwrbutton/input0";
button->idev->dev.parent = button->dev;
input_set_capability(button->idev, EV_KEY, KEY_POWER);
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
powerbutton_irq, IRQF_ONESHOT, "cpcap_pwrbutton", button);
if (err < 0) {
dev_err(&pdev->dev, "IRQ request failed: %d\n", err);
return err;
}
err = input_register_device(button->idev);
if (err) {
dev_err(&pdev->dev, "Input register failed: %d\n", err);
return err;
}
device_init_wakeup(&pdev->dev, true);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id cpcap_pwrbutton_dt_match_table[] = {
{ .compatible = "motorola,cpcap-pwrbutton" },
{},
};
MODULE_DEVICE_TABLE(of, cpcap_pwrbutton_dt_match_table);
#endif
static struct platform_driver cpcap_power_button_driver = {
.probe = cpcap_power_button_probe,
.driver = {
.name = "cpcap-pwrbutton",
.of_match_table = of_match_ptr(cpcap_pwrbutton_dt_match_table),
},
};
module_platform_driver(cpcap_power_button_driver);
MODULE_ALIAS("platform:cpcap-pwrbutton");
MODULE_DESCRIPTION("CPCAP Power Button");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
...@@ -829,6 +829,16 @@ config TOUCHSCREEN_USB_COMPOSITE ...@@ -829,6 +829,16 @@ config TOUCHSCREEN_USB_COMPOSITE
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called usbtouchscreen. module will be called usbtouchscreen.
config TOUCHSCREEN_MXS_LRADC
tristate "Freescale i.MX23/i.MX28 LRADC touchscreen"
depends on MFD_MXS_LRADC
help
Say Y here if you have a touchscreen connected to the low-resolution
analog-to-digital converter (LRADC) on an i.MX23 or i.MX28 processor.
To compile this driver as a module, choose M here: the module will be
called mxs-lradc-ts.
config TOUCHSCREEN_MX25 config TOUCHSCREEN_MX25
tristate "Freescale i.MX25 touchscreen input driver" tristate "Freescale i.MX25 touchscreen input driver"
depends on MFD_MX25_TSADC depends on MFD_MX25_TSADC
......
...@@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o ...@@ -45,6 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o
obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC) += mxs-lradc-ts.o
obj-$(CONFIG_TOUCHSCREEN_MX25) += fsl-imx25-tcq.o obj-$(CONFIG_TOUCHSCREEN_MX25) += fsl-imx25-tcq.o
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
......
/*
* Freescale MXS LRADC touchscreen driver
*
* Copyright (c) 2012 DENX Software Engineering, GmbH.
* Copyright (c) 2017 Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
*
* Authors:
* Marek Vasut <marex@denx.de>
* Ksenija Stanojevic <ksenija.stanojevic@gmail.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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mfd/core.h>
#include <linux/mfd/mxs-lradc.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
const char *mxs_lradc_ts_irq_names[] = {
"mxs-lradc-touchscreen",
"mxs-lradc-channel6",
"mxs-lradc-channel7",
};
/*
* Touchscreen handling
*/
enum mxs_lradc_ts_plate {
LRADC_TOUCH = 0,
LRADC_SAMPLE_X,
LRADC_SAMPLE_Y,
LRADC_SAMPLE_PRESSURE,
LRADC_SAMPLE_VALID,
};
struct mxs_lradc_ts {
struct mxs_lradc *lradc;
struct device *dev;
void __iomem *base;
/*
* When the touchscreen is enabled, we give it two private virtual
* channels: #6 and #7. This means that only 6 virtual channels (instead
* of 8) will be available for buffered capture.
*/
#define TOUCHSCREEN_VCHANNEL1 7
#define TOUCHSCREEN_VCHANNEL2 6
struct input_dev *ts_input;
enum mxs_lradc_ts_plate cur_plate; /* state machine */
bool ts_valid;
unsigned int ts_x_pos;
unsigned int ts_y_pos;
unsigned int ts_pressure;
/* handle touchscreen's physical behaviour */
/* samples per coordinate */
unsigned int over_sample_cnt;
/* time clocks between samples */
unsigned int over_sample_delay;
/* time in clocks to wait after the plates where switched */
unsigned int settling_delay;
spinlock_t lock;
};
struct state_info {
u32 mask;
u32 bit;
u32 x_plate;
u32 y_plate;
u32 pressure;
};
static struct state_info info[] = {
{LRADC_CTRL0_MX23_PLATE_MASK, LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE,
LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM,
LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM,
LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM},
{LRADC_CTRL0_MX28_PLATE_MASK, LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE,
LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW,
LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW,
LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW}
};
static bool mxs_lradc_check_touch_event(struct mxs_lradc_ts *ts)
{
return !!(readl(ts->base + LRADC_STATUS) &
LRADC_STATUS_TOUCH_DETECT_RAW);
}
static void mxs_lradc_map_ts_channel(struct mxs_lradc_ts *ts, unsigned int vch,
unsigned int ch)
{
writel(LRADC_CTRL4_LRADCSELECT_MASK(vch),
ts->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
writel(LRADC_CTRL4_LRADCSELECT(vch, ch),
ts->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
}
static void mxs_lradc_setup_ts_channel(struct mxs_lradc_ts *ts, unsigned int ch)
{
/*
* prepare for oversampling conversion
*
* from the datasheet:
* "The ACCUMULATE bit in the appropriate channel register
* HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
* otherwise, the IRQs will not fire."
*/
writel(LRADC_CH_ACCUMULATE |
LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1),
ts->base + LRADC_CH(ch));
/* from the datasheet:
* "Software must clear this register in preparation for a
* multi-cycle accumulation.
*/
writel(LRADC_CH_VALUE_MASK,
ts->base + LRADC_CH(ch) + STMP_OFFSET_REG_CLR);
/*
* prepare the delay/loop unit according to the oversampling count
*
* from the datasheet:
* "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
* HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
* the LRADC will not trigger the delay group."
*/
writel(LRADC_DELAY_TRIGGER(1 << ch) | LRADC_DELAY_TRIGGER_DELAYS(0) |
LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
ts->base + LRADC_DELAY(3));
writel(LRADC_CTRL1_LRADC_IRQ(ch),
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
/*
* after changing the touchscreen plates setting
* the signals need some initial time to settle. Start the
* SoC's delay unit and start the conversion later
* and automatically.
*/
writel(LRADC_DELAY_TRIGGER(0) | LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) |
LRADC_DELAY_KICK | LRADC_DELAY_DELAY(ts->settling_delay),
ts->base + LRADC_DELAY(2));
}
/*
* Pressure detection is special:
* We want to do both required measurements for the pressure detection in
* one turn. Use the hardware features to chain both conversions and let the
* hardware report one interrupt if both conversions are done
*/
static void mxs_lradc_setup_ts_pressure(struct mxs_lradc_ts *ts,
unsigned int ch1, unsigned int ch2)
{
u32 reg;
/*
* prepare for oversampling conversion
*
* from the datasheet:
* "The ACCUMULATE bit in the appropriate channel register
* HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
* otherwise, the IRQs will not fire."
*/
reg = LRADC_CH_ACCUMULATE |
LRADC_CH_NUM_SAMPLES(ts->over_sample_cnt - 1);
writel(reg, ts->base + LRADC_CH(ch1));
writel(reg, ts->base + LRADC_CH(ch2));
/* from the datasheet:
* "Software must clear this register in preparation for a
* multi-cycle accumulation.
*/
writel(LRADC_CH_VALUE_MASK,
ts->base + LRADC_CH(ch1) + STMP_OFFSET_REG_CLR);
writel(LRADC_CH_VALUE_MASK,
ts->base + LRADC_CH(ch2) + STMP_OFFSET_REG_CLR);
/* prepare the delay/loop unit according to the oversampling count */
writel(LRADC_DELAY_TRIGGER(1 << ch1) | LRADC_DELAY_TRIGGER(1 << ch2) |
LRADC_DELAY_TRIGGER_DELAYS(0) |
LRADC_DELAY_LOOP(ts->over_sample_cnt - 1) |
LRADC_DELAY_DELAY(ts->over_sample_delay - 1),
ts->base + LRADC_DELAY(3));
writel(LRADC_CTRL1_LRADC_IRQ(ch2),
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
/*
* after changing the touchscreen plates setting
* the signals need some initial time to settle. Start the
* SoC's delay unit and start the conversion later
* and automatically.
*/
writel(LRADC_DELAY_TRIGGER(0) | LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) |
LRADC_DELAY_KICK | LRADC_DELAY_DELAY(ts->settling_delay),
ts->base + LRADC_DELAY(2));
}
static unsigned int mxs_lradc_ts_read_raw_channel(struct mxs_lradc_ts *ts,
unsigned int channel)
{
u32 reg;
unsigned int num_samples, val;
reg = readl(ts->base + LRADC_CH(channel));
if (reg & LRADC_CH_ACCUMULATE)
num_samples = ts->over_sample_cnt;
else
num_samples = 1;
val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
return val / num_samples;
}
static unsigned int mxs_lradc_read_ts_pressure(struct mxs_lradc_ts *ts,
unsigned int ch1, unsigned int ch2)
{
u32 reg, mask;
unsigned int pressure, m1, m2;
mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
reg = readl(ts->base + LRADC_CTRL1) & mask;
while (reg != mask) {
reg = readl(ts->base + LRADC_CTRL1) & mask;
dev_dbg(ts->dev, "One channel is still busy: %X\n", reg);
}
m1 = mxs_lradc_ts_read_raw_channel(ts, ch1);
m2 = mxs_lradc_ts_read_raw_channel(ts, ch2);
if (m2 == 0) {
dev_warn(ts->dev, "Cannot calculate pressure\n");
return 1 << (LRADC_RESOLUTION - 1);
}
/* simply scale the value from 0 ... max ADC resolution */
pressure = m1;
pressure *= (1 << LRADC_RESOLUTION);
pressure /= m2;
dev_dbg(ts->dev, "Pressure = %u\n", pressure);
return pressure;
}
#define TS_CH_XP 2
#define TS_CH_YP 3
#define TS_CH_XM 4
#define TS_CH_YM 5
/*
* YP(open)--+-------------+
* | |--+
* | | |
* YM(-)--+-------------+ |
* +--------------+
* | |
* XP(weak+) XM(open)
*
* "weak+" means 200k Ohm VDDIO
* (-) means GND
*/
static void mxs_lradc_setup_touch_detection(struct mxs_lradc_ts *ts)
{
struct mxs_lradc *lradc = ts->lradc;
/*
* In order to detect a touch event the 'touch detect enable' bit
* enables:
* - a weak pullup to the X+ connector
* - a strong ground at the Y- connector
*/
writel(info[lradc->soc].mask,
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
writel(info[lradc->soc].bit,
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
}
/*
* YP(meas)--+-------------+
* | |--+
* | | |
* YM(open)--+-------------+ |
* +--------------+
* | |
* XP(+) XM(-)
*
* (+) means here 1.85 V
* (-) means here GND
*/
static void mxs_lradc_prepare_x_pos(struct mxs_lradc_ts *ts)
{
struct mxs_lradc *lradc = ts->lradc;
writel(info[lradc->soc].mask,
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
writel(info[lradc->soc].x_plate,
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
ts->cur_plate = LRADC_SAMPLE_X;
mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
}
/*
* YP(+)--+-------------+
* | |--+
* | | |
* YM(-)--+-------------+ |
* +--------------+
* | |
* XP(open) XM(meas)
*
* (+) means here 1.85 V
* (-) means here GND
*/
static void mxs_lradc_prepare_y_pos(struct mxs_lradc_ts *ts)
{
struct mxs_lradc *lradc = ts->lradc;
writel(info[lradc->soc].mask,
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
writel(info[lradc->soc].y_plate,
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
ts->cur_plate = LRADC_SAMPLE_Y;
mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
mxs_lradc_setup_ts_channel(ts, TOUCHSCREEN_VCHANNEL1);
}
/*
* YP(+)--+-------------+
* | |--+
* | | |
* YM(meas)--+-------------+ |
* +--------------+
* | |
* XP(meas) XM(-)
*
* (+) means here 1.85 V
* (-) means here GND
*/
static void mxs_lradc_prepare_pressure(struct mxs_lradc_ts *ts)
{
struct mxs_lradc *lradc = ts->lradc;
writel(info[lradc->soc].mask,
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
writel(info[lradc->soc].pressure,
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
ts->cur_plate = LRADC_SAMPLE_PRESSURE;
mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
mxs_lradc_map_ts_channel(ts, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
mxs_lradc_setup_ts_pressure(ts, TOUCHSCREEN_VCHANNEL2,
TOUCHSCREEN_VCHANNEL1);
}
static void mxs_lradc_enable_touch_detection(struct mxs_lradc_ts *ts)
{
mxs_lradc_setup_touch_detection(ts);
ts->cur_plate = LRADC_TOUCH;
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ | LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
}
static void mxs_lradc_start_touch_event(struct mxs_lradc_ts *ts)
{
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
writel(LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1),
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
/*
* start with the Y-pos, because it uses nearly the same plate
* settings like the touch detection
*/
mxs_lradc_prepare_y_pos(ts);
}
static void mxs_lradc_report_ts_event(struct mxs_lradc_ts *ts)
{
input_report_abs(ts->ts_input, ABS_X, ts->ts_x_pos);
input_report_abs(ts->ts_input, ABS_Y, ts->ts_y_pos);
input_report_abs(ts->ts_input, ABS_PRESSURE, ts->ts_pressure);
input_report_key(ts->ts_input, BTN_TOUCH, 1);
input_sync(ts->ts_input);
}
static void mxs_lradc_complete_touch_event(struct mxs_lradc_ts *ts)
{
mxs_lradc_setup_touch_detection(ts);
ts->cur_plate = LRADC_SAMPLE_VALID;
/*
* start a dummy conversion to burn time to settle the signals
* note: we are not interested in the conversion's value
*/
writel(0, ts->base + LRADC_CH(TOUCHSCREEN_VCHANNEL1));
writel(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2),
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
writel(LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10),
ts->base + LRADC_DELAY(2));
}
/*
* in order to avoid false measurements, report only samples where
* the surface is still touched after the position measurement
*/
static void mxs_lradc_finish_touch_event(struct mxs_lradc_ts *ts, bool valid)
{
/* if it is still touched, report the sample */
if (valid && mxs_lradc_check_touch_event(ts)) {
ts->ts_valid = true;
mxs_lradc_report_ts_event(ts);
}
/* if it is even still touched, continue with the next measurement */
if (mxs_lradc_check_touch_event(ts)) {
mxs_lradc_prepare_y_pos(ts);
return;
}
if (ts->ts_valid) {
/* signal the release */
ts->ts_valid = false;
input_report_key(ts->ts_input, BTN_TOUCH, 0);
input_sync(ts->ts_input);
}
/* if it is released, wait for the next touch via IRQ */
ts->cur_plate = LRADC_TOUCH;
writel(0, ts->base + LRADC_DELAY(2));
writel(0, ts->base + LRADC_DELAY(3));
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ |
LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1),
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
}
/* touchscreen's state machine */
static void mxs_lradc_handle_touch(struct mxs_lradc_ts *ts)
{
switch (ts->cur_plate) {
case LRADC_TOUCH:
if (mxs_lradc_check_touch_event(ts))
mxs_lradc_start_touch_event(ts);
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ,
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
return;
case LRADC_SAMPLE_Y:
ts->ts_y_pos =
mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
mxs_lradc_prepare_x_pos(ts);
return;
case LRADC_SAMPLE_X:
ts->ts_x_pos =
mxs_lradc_ts_read_raw_channel(ts, TOUCHSCREEN_VCHANNEL1);
mxs_lradc_prepare_pressure(ts);
return;
case LRADC_SAMPLE_PRESSURE:
ts->ts_pressure =
mxs_lradc_read_ts_pressure(ts,
TOUCHSCREEN_VCHANNEL2,
TOUCHSCREEN_VCHANNEL1);
mxs_lradc_complete_touch_event(ts);
return;
case LRADC_SAMPLE_VALID:
mxs_lradc_finish_touch_event(ts, 1);
break;
}
}
/* IRQ Handling */
static irqreturn_t mxs_lradc_ts_handle_irq(int irq, void *data)
{
struct mxs_lradc_ts *ts = data;
struct mxs_lradc *lradc = ts->lradc;
unsigned long reg = readl(ts->base + LRADC_CTRL1);
u32 clr_irq = mxs_lradc_irq_mask(lradc);
const u32 ts_irq_mask =
LRADC_CTRL1_TOUCH_DETECT_IRQ |
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
unsigned long flags;
if (!(reg & mxs_lradc_irq_mask(lradc)))
return IRQ_NONE;
if (reg & ts_irq_mask) {
spin_lock_irqsave(&ts->lock, flags);
mxs_lradc_handle_touch(ts);
spin_unlock_irqrestore(&ts->lock, flags);
/* Make sure we don't clear the next conversion's interrupt. */
clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
writel(reg & clr_irq,
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
}
return IRQ_HANDLED;
}
static int mxs_lradc_ts_open(struct input_dev *dev)
{
struct mxs_lradc_ts *ts = input_get_drvdata(dev);
/* Enable the touch-detect circuitry. */
mxs_lradc_enable_touch_detection(ts);
return 0;
}
static void mxs_lradc_ts_stop(struct mxs_lradc_ts *ts)
{
int i;
struct mxs_lradc *lradc = ts->lradc;
/* stop all interrupts from firing */
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2),
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
/* Power-down touchscreen touch-detect circuitry. */
writel(info[lradc->soc].mask,
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
ts->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
for (i = 1; i < LRADC_MAX_DELAY_CHANS; i++)
writel(0, ts->base + LRADC_DELAY(i));
}
static void mxs_lradc_ts_close(struct input_dev *dev)
{
struct mxs_lradc_ts *ts = input_get_drvdata(dev);
mxs_lradc_ts_stop(ts);
}
static void mxs_lradc_ts_hw_init(struct mxs_lradc_ts *ts)
{
struct mxs_lradc *lradc = ts->lradc;
/* Configure the touchscreen type */
if (lradc->soc == IMX28_LRADC) {
writel(LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_5WIRE)
writel(LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
ts->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
}
}
static int mxs_lradc_ts_register(struct mxs_lradc_ts *ts)
{
struct input_dev *input = ts->ts_input;
struct device *dev = ts->dev;
input = devm_input_allocate_device(dev);
if (!input)
return -ENOMEM;
input->name = "mxs-lradc-ts";
input->id.bustype = BUS_HOST;
input->open = mxs_lradc_ts_open;
input->close = mxs_lradc_ts_close;
__set_bit(INPUT_PROP_DIRECT, input->propbit);
input_set_capability(input, EV_KEY, BTN_TOUCH);
input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
0, 0);
ts->ts_input = input;
input_set_drvdata(input, ts);
return input_register_device(input);
}
static int mxs_lradc_ts_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->parent->of_node;
struct mxs_lradc *lradc = dev_get_drvdata(dev->parent);
struct mxs_lradc_ts *ts;
struct resource *iores;
int ret, irq, virq, i;
u32 ts_wires = 0, adapt;
ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
return -ENOMEM;
platform_set_drvdata(pdev, ts);
ts->lradc = lradc;
ts->dev = dev;
spin_lock_init(&ts->lock);
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ts->base = devm_ioremap(dev, iores->start, resource_size(iores));
if (IS_ERR(ts->base))
return PTR_ERR(ts->base);
ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
&ts_wires);
if (ret)
return ret;
if (of_property_read_u32(node, "fsl,ave-ctrl", &adapt)) {
ts->over_sample_cnt = 4;
} else {
if (adapt >= 1 || adapt <= 32) {
ts->over_sample_cnt = adapt;
} else {
dev_err(ts->dev, "Invalid sample count (%u)\n",
adapt);
return -EINVAL;
}
}
if (of_property_read_u32(node, "fsl,ave-delay", &adapt)) {
ts->over_sample_delay = 2;
} else {
if (adapt >= 2 || adapt <= LRADC_DELAY_DELAY_MASK + 1) {
ts->over_sample_delay = adapt;
} else {
dev_err(ts->dev, "Invalid sample delay (%u)\n",
adapt);
return -EINVAL;
}
}
if (of_property_read_u32(node, "fsl,settling", &adapt)) {
ts->settling_delay = 10;
} else {
if (adapt >= 1 || adapt <= LRADC_DELAY_DELAY_MASK) {
ts->settling_delay = adapt;
} else {
dev_err(ts->dev, "Invalid settling delay (%u)\n",
adapt);
return -EINVAL;
}
}
ret = stmp_reset_block(ts->base);
if (ret)
return ret;
mxs_lradc_ts_hw_init(ts);
for (i = 0; i < 3; i++) {
irq = platform_get_irq_byname(pdev, mxs_lradc_ts_irq_names[i]);
if (irq < 0)
return irq;
virq = irq_of_parse_and_map(node, irq);
mxs_lradc_ts_stop(ts);
ret = devm_request_irq(dev, virq,
mxs_lradc_ts_handle_irq,
0, mxs_lradc_ts_irq_names[i], ts);
if (ret)
return ret;
}
return mxs_lradc_ts_register(ts);
}
static struct platform_driver mxs_lradc_ts_driver = {
.driver = {
.name = "mxs-lradc-ts",
},
.probe = mxs_lradc_ts_probe,
};
module_platform_driver(mxs_lradc_ts_driver);
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_DESCRIPTION("Freescale MXS LRADC touchscreen driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:mxs-lradc-ts");
...@@ -344,6 +344,23 @@ config MFD_MC13XXX_I2C ...@@ -344,6 +344,23 @@ config MFD_MC13XXX_I2C
help help
Select this if your MC13xxx is connected via an I2C bus. Select this if your MC13xxx is connected via an I2C bus.
config MFD_MXS_LRADC
tristate "Freescale i.MX23/i.MX28 LRADC"
depends on ARCH_MXS || COMPILE_TEST
select MFD_CORE
select STMP_DEVICE
help
Say yes here to build support for the Low Resolution
Analog-to-Digital Converter (LRADC) found on the i.MX23 and i.MX28
processors. This driver provides common support for accessing the
device, additional drivers must be enabled in order to use the
functionality of the device:
mxs-lradc-adc for ADC readings
mxs-lradc-ts for touchscreen support
This driver can also be built as a module. If so, the module will be
called mxs-lradc.
config MFD_MX25_TSADC config MFD_MX25_TSADC
tristate "Freescale i.MX25 integrated Touchscreen and ADC unit" tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
select REGMAP_MMIO select REGMAP_MMIO
......
...@@ -215,3 +215,4 @@ obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o ...@@ -215,3 +215,4 @@ obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o
obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o
...@@ -18,11 +18,11 @@ ...@@ -18,11 +18,11 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/exynos5-pmu.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>
#include <linux/types.h> #include <linux/types.h>
/* LPASS Top register definitions */ /* LPASS Top register definitions */
...@@ -83,7 +83,7 @@ static void exynos_lpass_enable(struct exynos_lpass *lpass) ...@@ -83,7 +83,7 @@ static void exynos_lpass_enable(struct exynos_lpass *lpass)
/* Activate related PADs from retention state */ /* Activate related PADs from retention state */
regmap_write(lpass->pmu, EXYNOS5433_PAD_RETENTION_AUD_OPTION, regmap_write(lpass->pmu, EXYNOS5433_PAD_RETENTION_AUD_OPTION,
EXYNOS5433_PAD_INITIATE_WAKEUP_FROM_LOWPWR); EXYNOS_WAKEUP_FROM_LOWPWR);
exynos_lpass_core_sw_reset(lpass, LPASS_I2S_SW_RESET); exynos_lpass_core_sw_reset(lpass, LPASS_I2S_SW_RESET);
exynos_lpass_core_sw_reset(lpass, LPASS_DMA_SW_RESET); exynos_lpass_core_sw_reset(lpass, LPASS_DMA_SW_RESET);
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
#define CPCAP_NR_IRQ_REG_BANKS 6 #define CPCAP_NR_IRQ_REG_BANKS 6
#define CPCAP_NR_IRQ_CHIPS 3 #define CPCAP_NR_IRQ_CHIPS 3
#define CPCAP_REGISTER_SIZE 4
#define CPCAP_REGISTER_BITS 16
struct cpcap_ddata { struct cpcap_ddata {
struct spi_device *spi; struct spi_device *spi;
...@@ -32,6 +34,32 @@ struct cpcap_ddata { ...@@ -32,6 +34,32 @@ struct cpcap_ddata {
struct regmap *regmap; struct regmap *regmap;
}; };
static int cpcap_sense_irq(struct regmap *regmap, int irq)
{
int regnum = irq / CPCAP_REGISTER_BITS;
int mask = BIT(irq % CPCAP_REGISTER_BITS);
int reg = CPCAP_REG_INTS1 + (regnum * CPCAP_REGISTER_SIZE);
int err, val;
if (reg < CPCAP_REG_INTS1 || reg > CPCAP_REG_INTS4)
return -EINVAL;
err = regmap_read(regmap, reg, &val);
if (err)
return err;
return !!(val & mask);
}
int cpcap_sense_virq(struct regmap *regmap, int virq)
{
struct regmap_irq_chip_data *d = irq_get_chip_data(virq);
int irq_base = regmap_irq_chip_get_base(d);
return cpcap_sense_irq(regmap, virq - irq_base);
}
EXPORT_SYMBOL_GPL(cpcap_sense_virq);
static int cpcap_check_revision(struct cpcap_ddata *cpcap) static int cpcap_check_revision(struct cpcap_ddata *cpcap)
{ {
u16 vendor, rev; u16 vendor, rev;
......
/*
* Freescale MXS Low Resolution Analog-to-Digital Converter driver
*
* Copyright (c) 2012 DENX Software Engineering, GmbH.
* Copyright (c) 2017 Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
*
* Authors:
* Marek Vasut <marex@denx.de>
* Ksenija Stanojevic <ksenija.stanojevic@gmail.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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/mxs-lradc.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#define ADC_CELL 0
#define TSC_CELL 1
#define RES_MEM 0
enum mx23_lradc_irqs {
MX23_LRADC_TS_IRQ = 0,
MX23_LRADC_CH0_IRQ,
MX23_LRADC_CH1_IRQ,
MX23_LRADC_CH2_IRQ,
MX23_LRADC_CH3_IRQ,
MX23_LRADC_CH4_IRQ,
MX23_LRADC_CH5_IRQ,
MX23_LRADC_CH6_IRQ,
MX23_LRADC_CH7_IRQ,
};
enum mx28_lradc_irqs {
MX28_LRADC_TS_IRQ = 0,
MX28_LRADC_TRESH0_IRQ,
MX28_LRADC_TRESH1_IRQ,
MX28_LRADC_CH0_IRQ,
MX28_LRADC_CH1_IRQ,
MX28_LRADC_CH2_IRQ,
MX28_LRADC_CH3_IRQ,
MX28_LRADC_CH4_IRQ,
MX28_LRADC_CH5_IRQ,
MX28_LRADC_CH6_IRQ,
MX28_LRADC_CH7_IRQ,
MX28_LRADC_BUTTON0_IRQ,
MX28_LRADC_BUTTON1_IRQ,
};
static struct resource mx23_adc_resources[] = {
DEFINE_RES_MEM(0x0, 0x0),
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH0_IRQ, "mxs-lradc-channel0"),
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH1_IRQ, "mxs-lradc-channel1"),
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH2_IRQ, "mxs-lradc-channel2"),
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH3_IRQ, "mxs-lradc-channel3"),
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH4_IRQ, "mxs-lradc-channel4"),
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH5_IRQ, "mxs-lradc-channel5"),
};
static struct resource mx23_touchscreen_resources[] = {
DEFINE_RES_MEM(0x0, 0x0),
DEFINE_RES_IRQ_NAMED(MX23_LRADC_TS_IRQ, "mxs-lradc-touchscreen"),
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH6_IRQ, "mxs-lradc-channel6"),
DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH7_IRQ, "mxs-lradc-channel7"),
};
static struct resource mx28_adc_resources[] = {
DEFINE_RES_MEM(0x0, 0x0),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_TRESH0_IRQ, "mxs-lradc-thresh0"),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_TRESH1_IRQ, "mxs-lradc-thresh1"),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH0_IRQ, "mxs-lradc-channel0"),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH1_IRQ, "mxs-lradc-channel1"),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH2_IRQ, "mxs-lradc-channel2"),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH3_IRQ, "mxs-lradc-channel3"),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH4_IRQ, "mxs-lradc-channel4"),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH5_IRQ, "mxs-lradc-channel5"),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_BUTTON0_IRQ, "mxs-lradc-button0"),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_BUTTON1_IRQ, "mxs-lradc-button1"),
};
static struct resource mx28_touchscreen_resources[] = {
DEFINE_RES_MEM(0x0, 0x0),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_TS_IRQ, "mxs-lradc-touchscreen"),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH6_IRQ, "mxs-lradc-channel6"),
DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH7_IRQ, "mxs-lradc-channel7"),
};
static struct mfd_cell mx23_cells[] = {
{
.name = "mxs-lradc-adc",
.resources = mx23_adc_resources,
.num_resources = ARRAY_SIZE(mx23_adc_resources),
},
{
.name = "mxs-lradc-ts",
.resources = mx23_touchscreen_resources,
.num_resources = ARRAY_SIZE(mx23_touchscreen_resources),
},
};
static struct mfd_cell mx28_cells[] = {
{
.name = "mxs-lradc-adc",
.resources = mx28_adc_resources,
.num_resources = ARRAY_SIZE(mx28_adc_resources),
},
{
.name = "mxs-lradc-ts",
.resources = mx28_touchscreen_resources,
.num_resources = ARRAY_SIZE(mx28_touchscreen_resources),
}
};
static const struct of_device_id mxs_lradc_dt_ids[] = {
{ .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
{ .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
static int mxs_lradc_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct mxs_lradc *lradc;
struct mfd_cell *cells = NULL;
struct resource *res;
int ret = 0;
u32 ts_wires = 0;
lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL);
if (!lradc)
return -ENOMEM;
of_id = of_match_device(mxs_lradc_dt_ids, &pdev->dev);
if (!of_id)
return -EINVAL;
lradc->soc = (enum mxs_lradc_id)of_id->data;
lradc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(lradc->clk)) {
dev_err(dev, "Failed to get the delay unit clock\n");
return PTR_ERR(lradc->clk);
}
ret = clk_prepare_enable(lradc->clk);
if (ret) {
dev_err(dev, "Failed to enable the delay unit clock\n");
return ret;
}
ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
&ts_wires);
if (!ret) {
lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
switch (ts_wires) {
case 4:
lradc->touchscreen_wire = MXS_LRADC_TOUCHSCREEN_4WIRE;
break;
case 5:
if (lradc->soc == IMX28_LRADC) {
lradc->touchscreen_wire =
MXS_LRADC_TOUCHSCREEN_5WIRE;
break;
}
/* fall through to an error message for i.MX23 */
default:
dev_err(&pdev->dev,
"Unsupported number of touchscreen wires (%d)\n"
, ts_wires);
ret = -EINVAL;
goto err_clk;
}
} else {
lradc->buffer_vchans = BUFFER_VCHANS_ALL;
}
platform_set_drvdata(pdev, lradc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENOMEM;
switch (lradc->soc) {
case IMX23_LRADC:
mx23_adc_resources[RES_MEM] = *res;
mx23_touchscreen_resources[RES_MEM] = *res;
cells = mx23_cells;
break;
case IMX28_LRADC:
mx28_adc_resources[RES_MEM] = *res;
mx28_touchscreen_resources[RES_MEM] = *res;
cells = mx28_cells;
break;
default:
dev_err(dev, "Unsupported SoC\n");
ret = -ENODEV;
goto err_clk;
}
ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
&cells[ADC_CELL], 1, NULL, 0, NULL);
if (ret) {
dev_err(&pdev->dev, "Failed to add the ADC subdevice\n");
goto err_clk;
}
if (!lradc->touchscreen_wire)
return 0;
ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
&cells[TSC_CELL], 1, NULL, 0, NULL);
if (ret) {
dev_err(&pdev->dev,
"Failed to add the touchscreen subdevice\n");
goto err_clk;
}
return 0;
err_clk:
clk_disable_unprepare(lradc->clk);
return ret;
}
static int mxs_lradc_remove(struct platform_device *pdev)
{
struct mxs_lradc *lradc = platform_get_drvdata(pdev);
clk_disable_unprepare(lradc->clk);
return 0;
}
static struct platform_driver mxs_lradc_driver = {
.driver = {
.name = "mxs-lradc",
.of_match_table = mxs_lradc_dt_ids,
},
.probe = mxs_lradc_probe,
.remove = mxs_lradc_remove,
};
module_platform_driver(mxs_lradc_driver);
MODULE_AUTHOR("Ksenija Stanojevic <ksenija.stanojevic@gmail.com>");
MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:mxs-lradc");
...@@ -777,6 +777,7 @@ exynos_retention_init(struct samsung_pinctrl_drv_data *drvdata, ...@@ -777,6 +777,7 @@ exynos_retention_init(struct samsung_pinctrl_drv_data *drvdata,
{ {
struct samsung_retention_ctrl *ctrl; struct samsung_retention_ctrl *ctrl;
struct regmap *pmu_regs; struct regmap *pmu_regs;
int i;
ctrl = devm_kzalloc(drvdata->dev, sizeof(*ctrl), GFP_KERNEL); ctrl = devm_kzalloc(drvdata->dev, sizeof(*ctrl), GFP_KERNEL);
if (!ctrl) if (!ctrl)
...@@ -794,6 +795,10 @@ exynos_retention_init(struct samsung_pinctrl_drv_data *drvdata, ...@@ -794,6 +795,10 @@ exynos_retention_init(struct samsung_pinctrl_drv_data *drvdata,
ctrl->enable = exynos_retention_enable; ctrl->enable = exynos_retention_enable;
ctrl->disable = exynos_retention_disable; ctrl->disable = exynos_retention_disable;
/* Ensure that retention is disabled on driver init */
for (i = 0; i < ctrl->nr_regs; i++)
regmap_write(pmu_regs, ctrl->regs[i], ctrl->value);
return ctrl; return ctrl;
} }
...@@ -1546,6 +1551,54 @@ static const struct samsung_pin_bank_data exynos5433_pin_banks9[] __initconst = ...@@ -1546,6 +1551,54 @@ static const struct samsung_pin_bank_data exynos5433_pin_banks9[] __initconst =
EXYNOS_PIN_BANK_EINTG(3, 0x000, "gpj1", 0x00), EXYNOS_PIN_BANK_EINTG(3, 0x000, "gpj1", 0x00),
}; };
/* PMU pin retention groups registers for Exynos5433 (without audio & fsys) */
static const u32 exynos5433_retention_regs[] = {
EXYNOS5433_PAD_RETENTION_TOP_OPTION,
EXYNOS5433_PAD_RETENTION_UART_OPTION,
EXYNOS5433_PAD_RETENTION_EBIA_OPTION,
EXYNOS5433_PAD_RETENTION_EBIB_OPTION,
EXYNOS5433_PAD_RETENTION_SPI_OPTION,
EXYNOS5433_PAD_RETENTION_MIF_OPTION,
EXYNOS5433_PAD_RETENTION_USBXTI_OPTION,
EXYNOS5433_PAD_RETENTION_BOOTLDO_OPTION,
EXYNOS5433_PAD_RETENTION_UFS_OPTION,
EXYNOS5433_PAD_RETENTION_FSYSGENIO_OPTION,
};
static const struct samsung_retention_data exynos5433_retention_data __initconst = {
.regs = exynos5433_retention_regs,
.nr_regs = ARRAY_SIZE(exynos5433_retention_regs),
.value = EXYNOS_WAKEUP_FROM_LOWPWR,
.refcnt = &exynos_shared_retention_refcnt,
.init = exynos_retention_init,
};
/* PMU retention control for audio pins can be tied to audio pin bank */
static const u32 exynos5433_audio_retention_regs[] = {
EXYNOS5433_PAD_RETENTION_AUD_OPTION,
};
static const struct samsung_retention_data exynos5433_audio_retention_data __initconst = {
.regs = exynos5433_audio_retention_regs,
.nr_regs = ARRAY_SIZE(exynos5433_audio_retention_regs),
.value = EXYNOS_WAKEUP_FROM_LOWPWR,
.init = exynos_retention_init,
};
/* PMU retention control for mmc pins can be tied to fsys pin bank */
static const u32 exynos5433_fsys_retention_regs[] = {
EXYNOS5433_PAD_RETENTION_MMC0_OPTION,
EXYNOS5433_PAD_RETENTION_MMC1_OPTION,
EXYNOS5433_PAD_RETENTION_MMC2_OPTION,
};
static const struct samsung_retention_data exynos5433_fsys_retention_data __initconst = {
.regs = exynos5433_fsys_retention_regs,
.nr_regs = ARRAY_SIZE(exynos5433_fsys_retention_regs),
.value = EXYNOS_WAKEUP_FROM_LOWPWR,
.init = exynos_retention_init,
};
/* /*
* Samsung pinctrl driver data for Exynos5433 SoC. Exynos5433 SoC includes * Samsung pinctrl driver data for Exynos5433 SoC. Exynos5433 SoC includes
* ten gpio/pin-mux/pinconfig controllers. * ten gpio/pin-mux/pinconfig controllers.
...@@ -1559,6 +1612,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = { ...@@ -1559,6 +1612,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = {
.suspend = exynos_pinctrl_suspend, .suspend = exynos_pinctrl_suspend,
.resume = exynos_pinctrl_resume, .resume = exynos_pinctrl_resume,
.nr_ext_resources = 1, .nr_ext_resources = 1,
.retention_data = &exynos5433_retention_data,
}, { }, {
/* pin-controller instance 1 data */ /* pin-controller instance 1 data */
.pin_banks = exynos5433_pin_banks1, .pin_banks = exynos5433_pin_banks1,
...@@ -1566,6 +1620,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = { ...@@ -1566,6 +1620,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = {
.eint_gpio_init = exynos_eint_gpio_init, .eint_gpio_init = exynos_eint_gpio_init,
.suspend = exynos_pinctrl_suspend, .suspend = exynos_pinctrl_suspend,
.resume = exynos_pinctrl_resume, .resume = exynos_pinctrl_resume,
.retention_data = &exynos5433_audio_retention_data,
}, { }, {
/* pin-controller instance 2 data */ /* pin-controller instance 2 data */
.pin_banks = exynos5433_pin_banks2, .pin_banks = exynos5433_pin_banks2,
...@@ -1573,6 +1628,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = { ...@@ -1573,6 +1628,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = {
.eint_gpio_init = exynos_eint_gpio_init, .eint_gpio_init = exynos_eint_gpio_init,
.suspend = exynos_pinctrl_suspend, .suspend = exynos_pinctrl_suspend,
.resume = exynos_pinctrl_resume, .resume = exynos_pinctrl_resume,
.retention_data = &exynos5433_retention_data,
}, { }, {
/* pin-controller instance 3 data */ /* pin-controller instance 3 data */
.pin_banks = exynos5433_pin_banks3, .pin_banks = exynos5433_pin_banks3,
...@@ -1580,6 +1636,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = { ...@@ -1580,6 +1636,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = {
.eint_gpio_init = exynos_eint_gpio_init, .eint_gpio_init = exynos_eint_gpio_init,
.suspend = exynos_pinctrl_suspend, .suspend = exynos_pinctrl_suspend,
.resume = exynos_pinctrl_resume, .resume = exynos_pinctrl_resume,
.retention_data = &exynos5433_retention_data,
}, { }, {
/* pin-controller instance 4 data */ /* pin-controller instance 4 data */
.pin_banks = exynos5433_pin_banks4, .pin_banks = exynos5433_pin_banks4,
...@@ -1587,6 +1644,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = { ...@@ -1587,6 +1644,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = {
.eint_gpio_init = exynos_eint_gpio_init, .eint_gpio_init = exynos_eint_gpio_init,
.suspend = exynos_pinctrl_suspend, .suspend = exynos_pinctrl_suspend,
.resume = exynos_pinctrl_resume, .resume = exynos_pinctrl_resume,
.retention_data = &exynos5433_retention_data,
}, { }, {
/* pin-controller instance 5 data */ /* pin-controller instance 5 data */
.pin_banks = exynos5433_pin_banks5, .pin_banks = exynos5433_pin_banks5,
...@@ -1594,6 +1652,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = { ...@@ -1594,6 +1652,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = {
.eint_gpio_init = exynos_eint_gpio_init, .eint_gpio_init = exynos_eint_gpio_init,
.suspend = exynos_pinctrl_suspend, .suspend = exynos_pinctrl_suspend,
.resume = exynos_pinctrl_resume, .resume = exynos_pinctrl_resume,
.retention_data = &exynos5433_fsys_retention_data,
}, { }, {
/* pin-controller instance 6 data */ /* pin-controller instance 6 data */
.pin_banks = exynos5433_pin_banks6, .pin_banks = exynos5433_pin_banks6,
...@@ -1601,6 +1660,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = { ...@@ -1601,6 +1660,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = {
.eint_gpio_init = exynos_eint_gpio_init, .eint_gpio_init = exynos_eint_gpio_init,
.suspend = exynos_pinctrl_suspend, .suspend = exynos_pinctrl_suspend,
.resume = exynos_pinctrl_resume, .resume = exynos_pinctrl_resume,
.retention_data = &exynos5433_retention_data,
}, { }, {
/* pin-controller instance 7 data */ /* pin-controller instance 7 data */
.pin_banks = exynos5433_pin_banks7, .pin_banks = exynos5433_pin_banks7,
...@@ -1608,6 +1668,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = { ...@@ -1608,6 +1668,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = {
.eint_gpio_init = exynos_eint_gpio_init, .eint_gpio_init = exynos_eint_gpio_init,
.suspend = exynos_pinctrl_suspend, .suspend = exynos_pinctrl_suspend,
.resume = exynos_pinctrl_resume, .resume = exynos_pinctrl_resume,
.retention_data = &exynos5433_retention_data,
}, { }, {
/* pin-controller instance 8 data */ /* pin-controller instance 8 data */
.pin_banks = exynos5433_pin_banks8, .pin_banks = exynos5433_pin_banks8,
...@@ -1615,6 +1676,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = { ...@@ -1615,6 +1676,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = {
.eint_gpio_init = exynos_eint_gpio_init, .eint_gpio_init = exynos_eint_gpio_init,
.suspend = exynos_pinctrl_suspend, .suspend = exynos_pinctrl_suspend,
.resume = exynos_pinctrl_resume, .resume = exynos_pinctrl_resume,
.retention_data = &exynos5433_retention_data,
}, { }, {
/* pin-controller instance 9 data */ /* pin-controller instance 9 data */
.pin_banks = exynos5433_pin_banks9, .pin_banks = exynos5433_pin_banks9,
...@@ -1622,6 +1684,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = { ...@@ -1622,6 +1684,7 @@ const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = {
.eint_gpio_init = exynos_eint_gpio_init, .eint_gpio_init = exynos_eint_gpio_init,
.suspend = exynos_pinctrl_suspend, .suspend = exynos_pinctrl_suspend,
.resume = exynos_pinctrl_resume, .resume = exynos_pinctrl_resume,
.retention_data = &exynos5433_retention_data,
}, },
}; };
......
...@@ -14,6 +14,9 @@ ...@@ -14,6 +14,9 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/device.h>
#include <linux/regmap.h>
#define CPCAP_VENDOR_ST 0 #define CPCAP_VENDOR_ST 0
#define CPCAP_VENDOR_TI 1 #define CPCAP_VENDOR_TI 1
...@@ -290,3 +293,5 @@ static inline int cpcap_get_vendor(struct device *dev, ...@@ -290,3 +293,5 @@ static inline int cpcap_get_vendor(struct device *dev,
return 0; return 0;
} }
extern int cpcap_sense_virq(struct regmap *regmap, int virq);
/*
* Freescale MXS Low Resolution Analog-to-Digital Converter driver
*
* Copyright (c) 2012 DENX Software Engineering, GmbH.
* Copyright (c) 2016 Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
*
* Author: Marek Vasut <marex@denx.de>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __MFD_MXS_LRADC_H
#define __MFD_MXS_LRADC_H
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/stmp_device.h>
#define LRADC_MAX_DELAY_CHANS 4
#define LRADC_MAX_MAPPED_CHANS 8
#define LRADC_MAX_TOTAL_CHANS 16
#define LRADC_DELAY_TIMER_HZ 2000
#define LRADC_CTRL0 0x00
# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE BIT(23)
# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE BIT(22)
# define LRADC_CTRL0_MX28_YNNSW /* YM */ BIT(21)
# define LRADC_CTRL0_MX28_YPNSW /* YP */ BIT(20)
# define LRADC_CTRL0_MX28_YPPSW /* YP */ BIT(19)
# define LRADC_CTRL0_MX28_XNNSW /* XM */ BIT(18)
# define LRADC_CTRL0_MX28_XNPSW /* XM */ BIT(17)
# define LRADC_CTRL0_MX28_XPPSW /* XP */ BIT(16)
# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE BIT(20)
# define LRADC_CTRL0_MX23_YM BIT(19)
# define LRADC_CTRL0_MX23_XM BIT(18)
# define LRADC_CTRL0_MX23_YP BIT(17)
# define LRADC_CTRL0_MX23_XP BIT(16)
# define LRADC_CTRL0_MX28_PLATE_MASK \
(LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \
LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \
LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \
LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW)
# define LRADC_CTRL0_MX23_PLATE_MASK \
(LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \
LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \
LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP)
#define LRADC_CTRL1 0x10
#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN BIT(24)
#define LRADC_CTRL1_LRADC_IRQ_EN(n) (1 << ((n) + 16))
#define LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK (0x1fff << 16)
#define LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK (0x01ff << 16)
#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET 16
#define LRADC_CTRL1_TOUCH_DETECT_IRQ BIT(8)
#define LRADC_CTRL1_LRADC_IRQ(n) BIT(n)
#define LRADC_CTRL1_MX28_LRADC_IRQ_MASK 0x1fff
#define LRADC_CTRL1_MX23_LRADC_IRQ_MASK 0x01ff
#define LRADC_CTRL1_LRADC_IRQ_OFFSET 0
#define LRADC_CTRL2 0x20
#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET 24
#define LRADC_CTRL2_TEMPSENSE_PWD BIT(15)
#define LRADC_STATUS 0x40
#define LRADC_STATUS_TOUCH_DETECT_RAW BIT(0)
#define LRADC_CH(n) (0x50 + (0x10 * (n)))
#define LRADC_CH_ACCUMULATE BIT(29)
#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24)
#define LRADC_CH_NUM_SAMPLES_OFFSET 24
#define LRADC_CH_NUM_SAMPLES(x) \
((x) << LRADC_CH_NUM_SAMPLES_OFFSET)
#define LRADC_CH_VALUE_MASK 0x3ffff
#define LRADC_CH_VALUE_OFFSET 0
#define LRADC_DELAY(n) (0xd0 + (0x10 * (n)))
#define LRADC_DELAY_TRIGGER_LRADCS_MASK (0xffUL << 24)
#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET 24
#define LRADC_DELAY_TRIGGER(x) \
(((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \
LRADC_DELAY_TRIGGER_LRADCS_MASK)
#define LRADC_DELAY_KICK BIT(20)
#define LRADC_DELAY_TRIGGER_DELAYS_MASK (0xf << 16)
#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET 16
#define LRADC_DELAY_TRIGGER_DELAYS(x) \
(((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \
LRADC_DELAY_TRIGGER_DELAYS_MASK)
#define LRADC_DELAY_LOOP_COUNT_MASK (0x1f << 11)
#define LRADC_DELAY_LOOP_COUNT_OFFSET 11
#define LRADC_DELAY_LOOP(x) \
(((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \
LRADC_DELAY_LOOP_COUNT_MASK)
#define LRADC_DELAY_DELAY_MASK 0x7ff
#define LRADC_DELAY_DELAY_OFFSET 0
#define LRADC_DELAY_DELAY(x) \
(((x) << LRADC_DELAY_DELAY_OFFSET) & \
LRADC_DELAY_DELAY_MASK)
#define LRADC_CTRL4 0x140
#define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4))
#define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4)
#define LRADC_CTRL4_LRADCSELECT(n, x) \
(((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \
LRADC_CTRL4_LRADCSELECT_MASK(n))
#define LRADC_RESOLUTION 12
#define LRADC_SINGLE_SAMPLE_MASK ((1 << LRADC_RESOLUTION) - 1)
#define BUFFER_VCHANS_LIMITED 0x3f
#define BUFFER_VCHANS_ALL 0xff
/*
* Certain LRADC channels are shared between touchscreen
* and/or touch-buttons and generic LRADC block. Therefore when using
* either of these, these channels are not available for the regular
* sampling. The shared channels are as follows:
*
* CH0 -- Touch button #0
* CH1 -- Touch button #1
* CH2 -- Touch screen XPUL
* CH3 -- Touch screen YPLL
* CH4 -- Touch screen XNUL
* CH5 -- Touch screen YNLR
* CH6 -- Touch screen WIPER (5-wire only)
*
* The bit fields below represents which parts of the LRADC block are
* switched into special mode of operation. These channels can not
* be sampled as regular LRADC channels. The driver will refuse any
* attempt to sample these channels.
*/
#define CHAN_MASK_TOUCHBUTTON (BIT(1) | BIT(0))
#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 2)
#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 2)
enum mxs_lradc_id {
IMX23_LRADC,
IMX28_LRADC,
};
enum mxs_lradc_ts_wires {
MXS_LRADC_TOUCHSCREEN_NONE = 0,
MXS_LRADC_TOUCHSCREEN_4WIRE,
MXS_LRADC_TOUCHSCREEN_5WIRE,
};
/**
* struct mxs_lradc
* @soc: soc type (IMX23 or IMX28)
* @clk: 2 kHz clock for delay units
* @buffer_vchans: channels that can be used during buffered capture
* @touchscreen_wire: touchscreen type (4-wire or 5-wire)
* @use_touchbutton: button state (on or off)
*/
struct mxs_lradc {
enum mxs_lradc_id soc;
struct clk *clk;
u8 buffer_vchans;
enum mxs_lradc_ts_wires touchscreen_wire;
bool use_touchbutton;
};
static inline u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc)
{
switch (lradc->soc) {
case IMX23_LRADC:
return LRADC_CTRL1_MX23_LRADC_IRQ_MASK;
case IMX28_LRADC:
return LRADC_CTRL1_MX28_LRADC_IRQ_MASK;
default:
return 0;
}
}
#endif /* __MXS_LRADC_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment