* [PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Add per channel "smpr" IIO extended attribute, to allow sampling
time configuration.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/stm32/stm32-adc.c | 75 +++++++++++++++++++++++++++++++++++++
drivers/iio/adc/stm32/stm32-adc.h | 25 +++++++++++++
drivers/iio/adc/stm32/stm32f4-adc.c | 48 ++++++++++++++++++++++++
3 files changed, 148 insertions(+)
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 9b4b459..1681f75 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -38,6 +38,61 @@
#include "stm32-adc.h"
/**
+ * stm32_adc_conf_smp() - Configure sampling time for each channel
+ * @indio_dev: IIO device
+ * @scan_mask: channels to be converted
+ */
+static int stm32_adc_conf_smp(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_regs *smp =
+ adc->common->data->adc_reginfo->smpr_regs;
+ struct stm32_adc_chan *stm32_chan;
+ const struct iio_chan_spec *chan;
+ u32 bit, val;
+ int i;
+
+ for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
+ chan = indio_dev->channels + bit;
+ stm32_chan = to_stm32_chan(adc, chan);
+ i = chan->channel;
+
+ if (i >= adc->max_channels)
+ return -EINVAL;
+
+ val = stm32_adc_readl(adc, smp[i].reg);
+ val &= ~smp[i].mask;
+ val |= (stm32_chan->smpr << smp[i].shift) & smp[i].mask;
+ stm32_adc_writel(adc, smp[i].reg, val);
+ }
+
+ return 0;
+}
+
+int stm32_adc_set_smpr(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int smpr)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_chan *stm32_chan = to_stm32_chan(adc, chan);
+
+ stm32_chan->smpr = smpr;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_set_smpr);
+
+int stm32_adc_get_smpr(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_chan *stm32_chan = to_stm32_chan(adc, chan);
+
+ return stm32_chan->smpr;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_get_smpr);
+
+/**
* stm32_adc_conf_scan_seq() - Build regular or injected channels scan sequence
* @indio_dev: IIO device
* @scan_mask: channels to be converted
@@ -126,6 +181,12 @@ static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
return ret;
}
+ ret = stm32_adc_conf_smp(indio_dev, scan_mask);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to configure samp time\n");
+ goto err_dis;
+ }
+
ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
if (ret) {
dev_err(&indio_dev->dev, "Failed to configure sequence\n");
@@ -938,6 +999,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
const struct stm32_adc_info *adc_info)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_common *common = adc->common;
struct device_node *node = indio_dev->dev.of_node;
struct property *prop;
const __be32 *cur;
@@ -945,6 +1007,19 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
int scan_index = 0, num_channels = 0;
u32 val;
+ if (!common->stm32_chans[adc->id]) {
+ /* Allocate extended attributes structure for an instance */
+ struct stm32_adc_chan *stm32_chans;
+
+ stm32_chans = devm_kcalloc(&indio_dev->dev,
+ adc_info->max_channels,
+ sizeof(*stm32_chans), GFP_KERNEL);
+ if (!stm32_chans)
+ return -ENOMEM;
+
+ common->stm32_chans[adc->id] = stm32_chans;
+ }
+
of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
num_channels++;
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index 6c9b70d..8cf1d5c 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -143,6 +143,14 @@ struct stm32_adc_chan_spec {
};
/**
+ * struct stm32_adc_chan - Extended specifications of stm32 adc channels
+ * @smpr: per channel sampling time selection
+ */
+struct stm32_adc_chan {
+ unsigned smpr:3;
+};
+
+/**
* struct stm32_adc_trig_info - ADC trigger info
* @extsel: trigger selection for regular or injected
* @name: name of the trigger, corresponding to its source
@@ -206,6 +214,7 @@ struct stm32_adc_trig_reginfo {
* @jdr: injected data registers offsets
* @sqr_regs: Regular sequence registers description
* @jsqr_reg: Injected sequence register description
+ * @smpr_regs: Sampling time registers description
* @trig_reginfo: regular trigger control registers description
* @jtrig_reginfo: injected trigger control registers description
*/
@@ -220,6 +229,7 @@ struct stm32_adc_reginfo {
u32 jdr[4];
const struct stm32_adc_regs *sqr_regs;
const struct stm32_adc_regs *jsqr_reg;
+ const struct stm32_adc_regs *smpr_regs;
const struct stm32_adc_trig_reginfo *trig_reginfo;
const struct stm32_adc_trig_reginfo *jtrig_reginfo;
};
@@ -328,6 +338,7 @@ struct stm32_adc {
* @aclk: common clock for the analog circuitry
* @vref: regulator reference
* @vref_mv: vref voltage (mv)
+ * @stm32_chans: stm32 channels extended specification data
* @gpio_descs: gpio descriptor used to configure EXTi triggers
* @lock: mutex
*/
@@ -341,11 +352,21 @@ struct stm32_adc_common {
struct clk *aclk;
struct regulator *vref;
int vref_mv;
+ struct stm32_adc_chan *stm32_chans[STM32_ADC_ID_MAX];
struct gpio_descs *gpios;
struct mutex lock; /* read_raw lock */
};
/* Helper routines */
+static inline struct stm32_adc_chan *to_stm32_chan(struct stm32_adc *adc,
+ const struct iio_chan_spec
+ *chan)
+{
+ struct stm32_adc_chan *stm32_chans = adc->common->stm32_chans[adc->id];
+
+ return &stm32_chans[chan->channel];
+}
+
static inline int stm32_adc_start_conv(struct stm32_adc *adc)
{
return adc->common->data->start_conv(adc);
@@ -458,6 +479,10 @@ static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
/* STM32 common extended attributes */
extern const struct iio_enum stm32_adc_trig_pol;
+int stm32_adc_set_smpr(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int smpr);
+int stm32_adc_get_smpr(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan);
int stm32_adc_probe(struct platform_device *pdev);
int stm32_adc_remove(struct platform_device *pdev);
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
index e033a68..e0e6211 100644
--- a/drivers/iio/adc/stm32/stm32f4-adc.c
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -330,6 +330,32 @@ enum stm32f4_adc_smpr {
};
/**
+ * stm32f4_smpr_regs[] - describe sampling time registers & bit fields
+ * Sorted so it can be indexed by channel number.
+ */
+static const struct stm32_adc_regs stm32f4_smpr_regs[] = {
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP0_MASK, STM32F4_SMP0_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP1_MASK, STM32F4_SMP1_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP2_MASK, STM32F4_SMP2_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP3_MASK, STM32F4_SMP3_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP4_MASK, STM32F4_SMP4_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP5_MASK, STM32F4_SMP5_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP6_MASK, STM32F4_SMP6_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP7_MASK, STM32F4_SMP7_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP8_MASK, STM32F4_SMP8_SHIFT },
+ { STM32F4_ADCX_SMPR2, STM32F4_SMP9_MASK, STM32F4_SMP9_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP10_MASK, STM32F4_SMP10_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP11_MASK, STM32F4_SMP11_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP12_MASK, STM32F4_SMP12_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP13_MASK, STM32F4_SMP13_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP14_MASK, STM32F4_SMP14_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP15_MASK, STM32F4_SMP15_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP16_MASK, STM32F4_SMP16_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP17_MASK, STM32F4_SMP17_SHIFT },
+ { STM32F4_ADCX_SMPR1, STM32F4_SMP18_MASK, STM32F4_SMP18_SHIFT },
+};
+
+/**
* stm32f4_sqr_regs - describe regular sequence registers
* - L: sequence len (register & bit field)
* - SQ1..SQ16: sequence entries (register & bit field)
@@ -407,6 +433,7 @@ enum stm32f4_adc_smpr {
},
.sqr_regs = stm32f4_sqr_regs,
.jsqr_reg = stm32f4_jsqr_reg,
+ .smpr_regs = stm32f4_smpr_regs,
.trig_reginfo = &stm32f4_adc_trig_reginfo,
.jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
};
@@ -555,7 +582,28 @@ static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
return 0;
}
+/* stm32f4_smpr_items : Channel-wise programmable sampling time */
+static const char * const stm32f4_smpr_items[] = {
+ [STM32F4_SMPR_3_CK_CYCLES] = "3_cycles",
+ [STM32F4_SMPR_15_CK_CYCLES] = "15_cycles",
+ [STM32F4_SMPR_28_CK_CYCLES] = "28_cycles",
+ [STM32F4_SMPR_56_CK_CYCLES] = "56_cycles",
+ [STM32F4_SMPR_84_CK_CYCLES] = "84_cycles",
+ [STM32F4_SMPR_112_CK_CYCLES] = "112_cycles",
+ [STM32F4_SMPR_144_CK_CYCLES] = "144_cycles",
+ [STM32F4_SMPR_480_CK_CYCLES] = "480_cycles",
+};
+
+static const struct iio_enum stm32f4_smpr = {
+ .items = stm32f4_smpr_items,
+ .num_items = ARRAY_SIZE(stm32f4_smpr_items),
+ .get = stm32_adc_get_smpr,
+ .set = stm32_adc_set_smpr,
+};
+
static const struct iio_chan_spec_ext_info stm32f4_adc_ext_info[] = {
+ IIO_ENUM("smpr", IIO_SEPARATE, &stm32f4_smpr),
+ IIO_ENUM_AVAILABLE("smpr", &stm32f4_smpr),
IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
{
.name = "trigger_pol_available",
--
1.9.1
^ permalink raw reply related
* [PATCH 05/10] iio: adc: stm32: add trigger polarity ext attr
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Add trigger polarity extended attribute.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/stm32/stm32-adc.c | 41 ++++++++++++++++++++++++++++++++++++-
drivers/iio/adc/stm32/stm32-adc.h | 4 ++++
drivers/iio/adc/stm32/stm32f4-adc.c | 12 +++++++++++
3 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 1a13450..9b4b459 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -207,7 +207,11 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
/* trigger source */
extsel = trig_info[ret].extsel;
- exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
+
+ /* default to rising edge if no polarity */
+ if (adc->exten == STM32_EXTEN_SWTRIG)
+ adc->exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
+ exten = adc->exten;
}
spin_lock_irqsave(&adc->lock, flags);
@@ -221,6 +225,40 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
return 0;
}
+static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int type)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ adc->exten = type;
+
+ return 0;
+}
+
+static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ return adc->exten;
+}
+
+static const char * const stm32_trig_pol_items[] = {
+ [STM32_EXTEN_SWTRIG] = "swtrig",
+ [STM32_EXTEN_HWTRIG_RISING_EDGE] = "rising-edge",
+ [STM32_EXTEN_HWTRIG_FALLING_EDGE] = "falling-edge",
+ [STM32_EXTEN_HWTRIG_BOTH_EDGES] = "both-edges",
+};
+
+const struct iio_enum stm32_adc_trig_pol = {
+ .items = stm32_trig_pol_items,
+ .num_items = ARRAY_SIZE(stm32_trig_pol_items),
+ .get = stm32_adc_get_trig_pol,
+ .set = stm32_adc_set_trig_pol,
+};
+EXPORT_SYMBOL_GPL(stm32_adc_trig_pol);
+
/**
* stm32_adc_conv_irq_enable() - Unmask end of conversion irq
* @adc: stm32 adc instance
@@ -893,6 +931,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
chan->scan_type.realbits = adc->common->data->highres;
chan->scan_type.storagebits = STM32_STORAGEBITS;
chan->scan_type.shift = 0;
+ chan->ext_info = adc->common->data->ext_info;
}
static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index fe3568b..6c9b70d 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -234,6 +234,7 @@ struct stm32_adc_reginfo {
* @ext_triggers: Reference to trigger info for regular channels
* @jext_triggers: Reference to trigger info for injected channels
* @adc_reginfo: stm32 ADC registers description
+ * @ext_info: Extended channel info
* @highres: Max resolution
* @max_clock_rate: Max input clock rate
* @clk_sel: routine to select common clock and prescaler
@@ -251,6 +252,7 @@ struct stm32_adc_ops {
const struct stm32_adc_trig_info *ext_triggers;
const struct stm32_adc_trig_info *jext_triggers;
const struct stm32_adc_reginfo *adc_reginfo;
+ const struct iio_chan_spec_ext_info *ext_info;
int highres;
unsigned long max_clock_rate;
int (*clk_sel)(struct stm32_adc *adc);
@@ -273,6 +275,7 @@ struct stm32_adc_ops {
* @id: ADC instance number (e.g. adc 1, 2 or 3)
* @offset: ADC instance register offset in ADC block
* @max_channels: Max channels number for this ADC.
+ * @exten: External trigger config (enable/polarity)
* @extrig_list: External trigger list (for regular channel)
* @completion: end of single conversion completion
* @buffer: data buffer
@@ -295,6 +298,7 @@ struct stm32_adc {
int id;
int offset;
int max_channels;
+ enum stm32_adc_exten exten;
struct list_head extrig_list;
struct completion completion;
u16 *buffer;
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
index 4d7a2a8..e033a68 100644
--- a/drivers/iio/adc/stm32/stm32f4-adc.c
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -555,11 +555,23 @@ static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
return 0;
}
+static const struct iio_chan_spec_ext_info stm32f4_adc_ext_info[] = {
+ IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
+ {
+ .name = "trigger_pol_available",
+ .shared = IIO_SHARED_BY_ALL,
+ .read = iio_enum_available_read,
+ .private = (uintptr_t)&stm32_adc_trig_pol,
+ },
+ {},
+};
+
static const struct stm32_adc_ops stm32f4_adc_ops = {
.adc_info = stm32f4_adc_info,
.ext_triggers = stm32f4_adc_ext_triggers,
.jext_triggers = stm32f4_adc_jext_triggers,
.adc_reginfo = &stm32f4_adc_reginfo,
+ .ext_info = stm32f4_adc_ext_info,
.highres = 12,
.max_clock_rate = 36000000,
.clk_sel = stm32f4_adc_clk_sel,
--
1.9.1
^ permalink raw reply related
* [PATCH 04/10] iio: adc: stm32: add optional support for exti gpios
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
STM32 ADC may use EXTi signals routed internally as trigger source
for conversions. Configure them as interrupt to configure this path
in HW to adc.
Note: interrupt handler isn't required here, and corresponding interrupt
can be kept masked at exti controller level.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/stm32/stm32-adc.c | 45 +++++++++++++++++++++++++++++++++++++++
drivers/iio/adc/stm32/stm32-adc.h | 3 +++
2 files changed, 48 insertions(+)
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 25d0307..1a13450 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -482,6 +482,12 @@ static irqreturn_t stm32_adc_common_isr(int irq, void *data)
return ret;
}
+static irqreturn_t stm32_adc_exti_handler(int irq, void *data)
+{
+ /* Exti handler should not be invoqued, and is not used */
+ return IRQ_HANDLED;
+}
+
/**
* stm32_adc_validate_trigger() - validate trigger for stm32 adc
* @indio_dev: IIO device
@@ -1051,6 +1057,41 @@ static void stm32_adc_unregister(struct stm32_adc *adc)
}
}
+static int stm32_adc_exti_probe(struct stm32_adc_common *common)
+{
+ int i, irq, ret;
+
+ common->gpios = devm_gpiod_get_array_optional(common->dev, NULL,
+ GPIOD_IN);
+ if (!common->gpios)
+ return 0;
+
+ for (i = 0; i < common->gpios->ndescs; i++) {
+ irq = gpiod_to_irq(common->gpios->desc[i]);
+ if (irq < 0) {
+ dev_err(common->dev, "gpio %d to irq failed\n", i);
+ return irq;
+ }
+
+ ret = devm_request_irq(common->dev, irq, stm32_adc_exti_handler,
+ 0, dev_name(common->dev), common);
+ if (ret) {
+ dev_err(common->dev, "request IRQ %d failed\n", irq);
+ return ret;
+ }
+
+ /*
+ * gpios are configured as interrupts, so exti trigger path is
+ * configured in HW. But getting interrupts when starting
+ * conversions is unused here, so mask it in on exti
+ * controller by default.
+ */
+ disable_irq(irq);
+ }
+
+ return 0;
+}
+
int stm32_adc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node, *child;
@@ -1131,6 +1172,10 @@ int stm32_adc_probe(struct platform_device *pdev)
goto err_clk_disable;
}
+ ret = stm32_adc_exti_probe(common);
+ if (ret)
+ goto err_clk_disable;
+
/* Parse adc child nodes to retrieve master/slave instances data */
for_each_available_child_of_node(np, child) {
ret = stm32_adc_register(common, child);
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index 01a0dda..fe3568b 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -23,6 +23,7 @@
#define __STM32_ADC_H
#include <linux/dmaengine.h>
+#include <linux/gpio/consumer.h>
#include <linux/irq_work.h>
/*
@@ -323,6 +324,7 @@ struct stm32_adc {
* @aclk: common clock for the analog circuitry
* @vref: regulator reference
* @vref_mv: vref voltage (mv)
+ * @gpio_descs: gpio descriptor used to configure EXTi triggers
* @lock: mutex
*/
struct stm32_adc_common {
@@ -335,6 +337,7 @@ struct stm32_adc_common {
struct clk *aclk;
struct regulator *vref;
int vref_mv;
+ struct gpio_descs *gpios;
struct mutex lock; /* read_raw lock */
};
--
1.9.1
^ permalink raw reply related
* [PATCH 03/10] iio: adc: stm32: add optional dma support
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
Add optional DMA support to STM32 ADC.
Use dma cyclic mode with at least two periods.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/stm32/Kconfig | 2 +
drivers/iio/adc/stm32/stm32-adc.c | 222 ++++++++++++++++++++++++++++++++----
drivers/iio/adc/stm32/stm32-adc.h | 15 +++
drivers/iio/adc/stm32/stm32f4-adc.c | 20 +++-
4 files changed, 235 insertions(+), 24 deletions(-)
diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
index 245d037..5554ac8 100644
--- a/drivers/iio/adc/stm32/Kconfig
+++ b/drivers/iio/adc/stm32/Kconfig
@@ -8,6 +8,7 @@ config STM32_ADC
select REGULATOR_FIXED_VOLTAGE
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
+ select IRQ_WORK
help
Say yes here to build the driver for the STMicroelectronics
STM32 analog-to-digital converter (ADC).
@@ -18,6 +19,7 @@ config STM32_ADC
config STM32F4_ADC
tristate "STMicroelectronics STM32F4 adc"
depends on ARCH_STM32 || COMPILE_TEST
+ depends on HAS_DMA
depends on OF
select STM32_ADC
help
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 1e0850d..25d0307 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -21,6 +21,7 @@
#include <linux/clk.h>
#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
@@ -371,6 +372,34 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
return ret;
}
+static int stm32_adc_residue(struct stm32_adc *adc)
+{
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ if (!adc->rx_buf)
+ return 0;
+
+ status = dmaengine_tx_status(adc->dma_chan,
+ adc->dma_chan->cookie,
+ &state);
+ if (status == DMA_IN_PROGRESS) {
+ /* Residue is size in bytes from end of buffer */
+ int i = adc->rx_buf_sz - state.residue;
+ int size;
+
+ /* Return available bytes */
+ if (i >= adc->bufi)
+ size = i - adc->bufi;
+ else
+ size = adc->rx_buf_sz - adc->bufi + i;
+
+ return size;
+ }
+
+ return 0;
+}
+
/**
* stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
*/
@@ -394,8 +423,8 @@ static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
status &= mask;
- /* Regular data */
- if (status & reginfo->eoc) {
+ /* Regular data (when dma isn't used) */
+ if ((status & reginfo->eoc) && (!adc->rx_buf)) {
adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
if (iio_buffer_enabled(indio_dev)) {
adc->bufi++;
@@ -553,29 +582,154 @@ static int stm32_adc_validate_device(struct iio_trigger *trig,
return indio != indio_dev ? -EINVAL : 0;
}
-static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
- bool state)
+static void stm32_adc_dma_irq_work(struct irq_work *work)
{
- struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct stm32_adc *adc = container_of(work, struct stm32_adc, work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ /**
+ * iio_trigger_poll calls generic_handle_irq(). So, it requires hard
+ * irq context, and cannot be called directly from dma callback,
+ * dma cb has to schedule this work instead.
+ */
+ iio_trigger_poll(indio_dev->trig);
+}
+
+static void stm32_adc_dma_buffer_done(void *data)
+{
+ struct iio_dev *indio_dev = data;
struct stm32_adc *adc = iio_priv(indio_dev);
- int ret;
- if (state) {
- /* Reset adc buffer index */
- adc->bufi = 0;
+ /* invoques iio_trigger_poll() from hard irq context */
+ irq_work_queue(&adc->work);
+}
+
+static int stm32_adc_buffer_alloc_dma_start(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_reginfo *reginfo =
+ adc->common->data->adc_reginfo;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_slave_config config;
+ dma_cookie_t cookie;
+ int ret, size, watermark;
+ /* Reset adc buffer index */
+ adc->bufi = 0;
+
+ if (!adc->dma_chan) {
/* Allocate adc buffer */
adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
if (!adc->buffer)
return -ENOMEM;
+ return 0;
+ }
+
+ /*
+ * Allocate at least twice the buffer size for dma cyclic transfers, so
+ * we can work with at least two dma periods. There should be :
+ * - always one buffer (period) dma is working on
+ * - one buffer (period) driver can push with iio_trigger_poll().
+ */
+ size = indio_dev->buffer->bytes_per_datum * indio_dev->buffer->length;
+ size = max(indio_dev->scan_bytes * 2, size);
+
+ adc->rx_buf = dma_alloc_coherent(adc->common->dev, PAGE_ALIGN(size),
+ &adc->rx_dma_buf,
+ GFP_KERNEL);
+ if (!adc->rx_buf)
+ return -ENOMEM;
+ adc->rx_buf_sz = size;
+ watermark = indio_dev->buffer->bytes_per_datum
+ * indio_dev->buffer->watermark;
+ watermark = max(indio_dev->scan_bytes, watermark);
+ watermark = rounddown(watermark, indio_dev->scan_bytes);
+
+ dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, size,
+ watermark);
+
+ /* Configure DMA channel to read data register */
+ memset(&config, 0, sizeof(config));
+ config.src_addr = (dma_addr_t)adc->common->phys_base;
+ config.src_addr += adc->offset + reginfo->dr;
+ config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ ret = dmaengine_slave_config(adc->dma_chan, &config);
+ if (ret)
+ goto config_err;
+
+ /* Prepare a DMA cyclic transaction */
+ desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
+ adc->rx_dma_buf,
+ size, watermark,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc) {
+ ret = -ENODEV;
+ goto config_err;
+ }
+
+ desc->callback = stm32_adc_dma_buffer_done;
+ desc->callback_param = indio_dev;
+
+ cookie = dmaengine_submit(desc);
+ if (dma_submit_error(cookie)) {
+ ret = dma_submit_error(cookie);
+ goto config_err;
+ }
+
+ /* Issue pending DMA requests */
+ dma_async_issue_pending(adc->dma_chan);
+
+ return 0;
+
+config_err:
+ dma_free_coherent(adc->common->dev, PAGE_ALIGN(size), adc->rx_buf,
+ adc->rx_dma_buf);
+
+ return ret;
+}
+
+static void stm32_adc_dma_stop_buffer_free(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ if (!adc->dma_chan) {
+ kfree(adc->buffer);
+ } else {
+ dmaengine_terminate_all(adc->dma_chan);
+ irq_work_sync(&adc->work);
+ dma_free_coherent(adc->common->dev, PAGE_ALIGN(adc->rx_buf_sz),
+ adc->rx_buf, adc->rx_dma_buf);
+ adc->rx_buf = NULL;
+ }
+
+ adc->buffer = NULL;
+}
+
+static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ if (state) {
+ ret = stm32_adc_buffer_alloc_dma_start(indio_dev);
+ if (ret) {
+ dev_err(&indio_dev->dev, "alloc failed\n");
+ return ret;
+ }
+
ret = stm32_adc_set_trig(indio_dev, trig);
if (ret) {
dev_err(&indio_dev->dev, "Can't set trigger\n");
goto err_buffer_free;
}
- stm32_adc_conv_irq_enable(adc);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_enable(adc);
ret = stm32_adc_start_conv(adc);
if (ret) {
@@ -589,25 +743,25 @@ static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
return ret;
}
- stm32_adc_conv_irq_disable(adc);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_disable(adc);
ret = stm32_adc_set_trig(indio_dev, NULL);
if (ret)
dev_warn(&indio_dev->dev, "Can't clear trigger\n");
- kfree(adc->buffer);
- adc->buffer = NULL;
+ stm32_adc_dma_stop_buffer_free(indio_dev);
}
return 0;
err_irq_trig_disable:
- stm32_adc_conv_irq_disable(adc);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_disable(adc);
stm32_adc_set_trig(indio_dev, NULL);
err_buffer_free:
- kfree(adc->buffer);
- adc->buffer = NULL;
+ stm32_adc_dma_stop_buffer_free(indio_dev);
return ret;
}
@@ -624,17 +778,30 @@ static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
struct iio_dev *indio_dev = pf->indio_dev;
struct stm32_adc *adc = iio_priv(indio_dev);
- dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
-
- /* reset buffer index */
- adc->bufi = 0;
- iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
- pf->timestamp);
+ if (!adc->dma_chan) {
+ adc->bufi = 0;
+ iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
+ pf->timestamp);
+ } else {
+ int residue = stm32_adc_residue(adc);
+
+ while (residue >= indio_dev->scan_bytes) {
+ adc->buffer = (u16 *)&adc->rx_buf[adc->bufi];
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ adc->buffer,
+ pf->timestamp);
+ residue -= indio_dev->scan_bytes;
+ adc->bufi += indio_dev->scan_bytes;
+ if (adc->bufi >= adc->rx_buf_sz)
+ adc->bufi = 0;
+ }
+ }
iio_trigger_notify_done(indio_dev->trig);
/* re-enable eoc irq */
- stm32_adc_conv_irq_enable(adc);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_enable(adc);
return IRQ_HANDLED;
}
@@ -827,6 +994,10 @@ static int stm32_adc_register(struct stm32_adc_common *common,
if (ret)
goto err_clk_disable;
+ adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
+ if (adc->dma_chan)
+ init_irq_work(&adc->work, stm32_adc_dma_irq_work);
+
ret = iio_triggered_buffer_setup(indio_dev,
&iio_pollfunc_store_time,
&stm32_adc_trigger_handler,
@@ -850,6 +1021,8 @@ static int stm32_adc_register(struct stm32_adc_common *common,
iio_triggered_buffer_cleanup(indio_dev);
err_trig_unregister:
+ if (adc->dma_chan)
+ dma_release_channel(adc->dma_chan);
stm32_adc_trig_unregister(indio_dev);
err_clk_disable:
@@ -870,6 +1043,8 @@ static void stm32_adc_unregister(struct stm32_adc *adc)
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
stm32_adc_trig_unregister(indio_dev);
+ if (adc->dma_chan)
+ dma_release_channel(adc->dma_chan);
if (adc->clk) {
clk_disable_unprepare(adc->clk);
clk_put(adc->clk);
@@ -900,6 +1075,7 @@ int stm32_adc_probe(struct platform_device *pdev)
common->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(common->base))
return PTR_ERR(common->base);
+ common->phys_base = res->start;
common->data = match->data;
common->dev = &pdev->dev;
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index 0be603c..01a0dda 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -22,6 +22,9 @@
#ifndef __STM32_ADC_H
#define __STM32_ADC_H
+#include <linux/dmaengine.h>
+#include <linux/irq_work.h>
+
/*
* STM32 - ADC global register map
* ________________________________________________________
@@ -276,6 +279,11 @@ struct stm32_adc_ops {
* @num_conv: expected number of scan conversions
* @injected: use injected channels on this adc
* @lock: spinlock
+ * @work: irq work used to call trigger poll routine
+ * @dma_chan: dma channel
+ * @rx_buf: dma rx buffer cpu address
+ * @rx_dma_buf: dma rx buffer bus address
+ * @rx_buf_sz: dma rx buffer size
* @clk: optional adc clock, for this adc instance
* @calib: optional calibration data
* @en: emulates enabled state on some stm32 adc
@@ -293,6 +301,11 @@ struct stm32_adc {
int num_conv;
bool injected;
spinlock_t lock; /* interrupt lock */
+ struct irq_work work;
+ struct dma_chan *dma_chan;
+ u8 *rx_buf;
+ dma_addr_t rx_dma_buf;
+ int rx_buf_sz;
struct clk *clk;
void *calib;
bool en;
@@ -302,6 +315,7 @@ struct stm32_adc {
* struct stm32_adc_common - private data of ADC driver, common to all
* ADC instances (ADC block)
* @dev: device for this controller
+ * @phys_base: control registers base physical addr
* @base: control registers base cpu addr
* @irq: Common irq line for all adc instances
* @data: STM32 dependent data from compatible
@@ -313,6 +327,7 @@ struct stm32_adc {
*/
struct stm32_adc_common {
struct device *dev;
+ phys_addr_t phys_base;
void __iomem *base;
int irq;
const struct stm32_adc_ops *data;
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
index 147fe9c..4d7a2a8 100644
--- a/drivers/iio/adc/stm32/stm32f4-adc.c
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -437,16 +437,30 @@ static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
* @adc: stm32 adc instance
*
* Start single conversions for regular or injected channels.
+ * Also take care of normal or DMA mode. DMA is used in circular mode for
+ * regular conversions, in IIO buffer modes. Rely on rx_buf as raw
+ * read doesn't use dma, but direct DR read.
*/
static int stm32f4_adc_start_conv(struct stm32_adc *adc)
{
- u32 trig_msk, start_msk;
+ u32 val, trig_msk, start_msk;
+ unsigned long flags;
dev_dbg(adc->common->dev, "%s %s\n", __func__,
adc->injected ? "injected" : "regular");
stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+ if (!adc->injected) {
+ spin_lock_irqsave(&adc->lock, flags);
+ val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
+ val &= ~(STM32F4_DMA | STM32F4_DDS);
+ if (adc->rx_buf)
+ val |= STM32F4_DMA | STM32F4_DDS;
+ stm32_adc_writel(adc, STM32F4_ADCX_CR2, val);
+ spin_unlock_irqrestore(&adc->lock, flags);
+ }
+
if (!stm32f4_adc_is_started(adc)) {
stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
STM32F4_EOCS | STM32F4_ADON);
@@ -494,6 +508,10 @@ static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
}
+ if (!adc->injected)
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2,
+ STM32F4_DMA | STM32F4_DDS);
+
return 0;
}
--
1.9.1
^ permalink raw reply related
* [PATCH 02/10] iio: adc: Add stm32 support
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
This patch adds support for STMicroelectronics STM32 MCU's analog to
digital converter.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/Kconfig | 2 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32/Kconfig | 34 ++
drivers/iio/adc/stm32/Makefile | 4 +
drivers/iio/adc/stm32/stm32-adc.c | 999 ++++++++++++++++++++++++++++++++++++
drivers/iio/adc/stm32/stm32-adc.h | 442 ++++++++++++++++
drivers/iio/adc/stm32/stm32f4-adc.c | 574 +++++++++++++++++++++
7 files changed, 2056 insertions(+)
create mode 100644 drivers/iio/adc/stm32/Kconfig
create mode 100644 drivers/iio/adc/stm32/Makefile
create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7edcf32..5c96a55 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -583,4 +583,6 @@ config XILINX_XADC
The driver can also be build as a module. If so, the module will be called
xilinx-xadc.
+source "drivers/iio/adc/stm32/Kconfig"
+
endmenu
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..a9dbf3a 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
+obj-$(CONFIG_ARCH_STM32) += stm32/
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
new file mode 100644
index 0000000..245d037
--- /dev/null
+++ b/drivers/iio/adc/stm32/Kconfig
@@ -0,0 +1,34 @@
+#
+# STM32 familly ADC drivers
+#
+
+config STM32_ADC
+ tristate
+ select REGULATOR
+ select REGULATOR_FIXED_VOLTAGE
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build the driver for the STMicroelectronics
+ STM32 analog-to-digital converter (ADC).
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-adc.
+
+config STM32F4_ADC
+ tristate "STMicroelectronics STM32F4 adc"
+ depends on ARCH_STM32 || COMPILE_TEST
+ depends on OF
+ select STM32_ADC
+ help
+ Say yes here to build support for STMicroelectronics stm32f4 Analog
+ to Digital Converter (ADC).
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32f4-adc.
+
+config STM32_ADC_DEBUG
+ bool "Enable debug for stm32 ADC drivers"
+ depends on STM32_ADC
+ help
+ Say "yes" to enable debug messages, on stm32 ADC drivers.
diff --git a/drivers/iio/adc/stm32/Makefile b/drivers/iio/adc/stm32/Makefile
new file mode 100644
index 0000000..83e8154
--- /dev/null
+++ b/drivers/iio/adc/stm32/Makefile
@@ -0,0 +1,4 @@
+# Core
+subdir-ccflags-$(CONFIG_STM32_ADC_DEBUG) := -DDEBUG
+obj-$(CONFIG_STM32_ADC) += stm32-adc.o
+obj-$(CONFIG_STM32F4_ADC) += stm32f4-adc.o
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
new file mode 100644
index 0000000..1e0850d
--- /dev/null
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -0,0 +1,999 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include "stm32-adc.h"
+
+/**
+ * stm32_adc_conf_scan_seq() - Build regular or injected channels scan sequence
+ * @indio_dev: IIO device
+ * @scan_mask: channels to be converted
+ *
+ * Conversion sequence :
+ * Configure ADC scan sequence based on selected channels in scan_mask.
+ * Add channels to SQR or JSQR registers, from scan_mask LSB to MSB, then
+ * program sequence len.
+ *
+ * Note: This can be done only when ADC enabled and no conversion is ongoing.
+ */
+static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_regs *sq;
+ const struct iio_chan_spec *chan;
+ u32 val, bit;
+ int sq_max, i = 0;
+
+ if (!stm32_adc_is_enabled(adc))
+ return -EBUSY;
+
+ if (adc->injected) {
+ if (stm32_adc_injected_started(adc))
+ return -EBUSY;
+
+ sq = adc->common->data->adc_reginfo->jsqr_reg;
+ sq_max = STM32_ADC_MAX_JSQ;
+ } else {
+ if (stm32_adc_regular_started(adc))
+ return -EBUSY;
+
+ sq = adc->common->data->adc_reginfo->sqr_regs;
+ sq_max = STM32_ADC_MAX_SQ;
+ }
+
+ /* Build sequence for regular or injected channels */
+ for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
+ chan = indio_dev->channels + bit;
+ /*
+ * Assign one channel per SQ/JSQ entry in regular/injected
+ * sequence, starting with SQ1/JSQ1.
+ */
+ i++;
+ if (i > sq_max)
+ return -EINVAL;
+
+ dev_dbg(adc->common->dev, "%s chan %d to %s%d\n",
+ __func__, chan->channel, adc->injected ? "JSQ" : "SQ",
+ i);
+
+ val = stm32_adc_readl(adc, sq[i].reg);
+ val &= ~sq[i].mask;
+ val |= (chan->channel << sq[i].shift) & sq[i].mask;
+ stm32_adc_writel(adc, sq[i].reg, val);
+ }
+
+ if (!i)
+ return -EINVAL;
+
+ /* Sequence len */
+ val = stm32_adc_readl(adc, sq[0].reg);
+ val &= ~sq[0].mask;
+ val |= ((i - 1) << sq[0].shift) & sq[0].mask;
+ stm32_adc_writel(adc, sq[0].reg, val);
+
+ return 0;
+}
+
+static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ ret = stm32_adc_clk_sel(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Clock sel failed\n");
+ return ret;
+ }
+
+ ret = stm32_adc_enable(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to enable adc\n");
+ return ret;
+ }
+
+ ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to configure sequence\n");
+ goto err_dis;
+ }
+
+ return 0;
+
+err_dis:
+ stm32_adc_disable(adc);
+
+ return ret;
+}
+
+/**
+ * stm32_adc_get_trig_index() - Get trigger index
+ * @indio_dev: IIO device
+ * @trig: trigger
+ *
+ * Returns trigger index, if trig matches one of the triggers registered by
+ * stm32 adc driver, -EINVAL otherwise.
+ */
+static int stm32_adc_get_trig_index(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct iio_trigger *tr;
+ int i = 0;
+
+ list_for_each_entry(tr, &adc->extrig_list, alloc_list) {
+ if (tr == trig)
+ return i;
+ i++;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * stm32_adc_set_trig() - Set a regular or injected trigger
+ * @indio_dev: IIO device
+ * @trig: IIO trigger
+ *
+ * Set trigger source/polarity (e.g. SW, or HW with polarity) :
+ * - if HW trigger disabled (e.g. trig == NULL, conversion launched by sw)
+ * - if HW trigger enabled, set source & polarity (trigger_pol / jtrigger_pol)
+ *
+ * Note: must be called when ADC is enabled
+ */
+static int stm32_adc_set_trig(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_trig_info *trig_info;
+ const struct stm32_adc_trig_reginfo *reginfo;
+ u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG;
+ unsigned long flags;
+ int ret;
+
+ if (!stm32_adc_is_enabled(adc))
+ return -EBUSY;
+
+ if (adc->injected) {
+ if (stm32_adc_injected_started(adc))
+ return -EBUSY;
+ reginfo = adc->common->data->adc_reginfo->jtrig_reginfo;
+ trig_info = adc->common->data->jext_triggers;
+ } else {
+ if (stm32_adc_regular_started(adc))
+ return -EBUSY;
+ reginfo = adc->common->data->adc_reginfo->trig_reginfo;
+ trig_info = adc->common->data->ext_triggers;
+ }
+
+ if (trig) {
+ ret = stm32_adc_get_trig_index(indio_dev, trig);
+ if (ret < 0)
+ return ret;
+
+ /* trigger source */
+ extsel = trig_info[ret].extsel;
+ exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
+ }
+
+ spin_lock_irqsave(&adc->lock, flags);
+ val = stm32_adc_readl(adc, reginfo->reg);
+ val &= ~(reginfo->exten_mask | reginfo->extsel_mask);
+ val |= (exten << reginfo->exten_shift) & reginfo->exten_mask;
+ val |= (extsel << reginfo->extsel_shift) & reginfo->extsel_mask;
+ stm32_adc_writel(adc, reginfo->reg, val);
+ spin_unlock_irqrestore(&adc->lock, flags);
+
+ return 0;
+}
+
+/**
+ * stm32_adc_conv_irq_enable() - Unmask end of conversion irq
+ * @adc: stm32 adc instance
+ *
+ * Unmask either eoc or jeoc, depending on injected configuration.
+ */
+static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
+{
+ const struct stm32_adc_reginfo *reginfo =
+ adc->common->data->adc_reginfo;
+ u32 mask;
+
+ if (adc->injected)
+ mask = reginfo->jeocie;
+ else
+ mask = reginfo->eocie;
+
+ stm32_adc_set_bits(adc, reginfo->ier, mask);
+}
+
+/**
+ * stm32_adc_conv_irq_disable() - Mask end of conversion irq
+ * @adc: stm32 adc instance
+ *
+ * Mask either eoc or jeoc, depending on injected configuration.
+ */
+static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
+{
+ const struct stm32_adc_reginfo *reginfo =
+ adc->common->data->adc_reginfo;
+ u32 mask;
+
+ if (adc->injected)
+ mask = reginfo->jeocie;
+ else
+ mask = reginfo->eocie;
+
+ stm32_adc_clr_bits(adc, reginfo->ier, mask);
+}
+
+/**
+ * stm32_adc_single_conv() - perform a single conversion
+ * @indio_dev: IIO device
+ * @chan: IIO channel
+ * @result: conversion result
+ *
+ * The function performs a single conversion on a given channel, by
+ * by:
+ * - creating scan mask with only one channel
+ * - using SW trigger
+ * - then start single conv
+ */
+static int stm32_adc_single_conv(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ unsigned long *scan_mask;
+ long timeout;
+ u16 result;
+ int ret;
+
+ scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
+ GFP_KERNEL);
+ if (!scan_mask)
+ return -ENOMEM;
+
+ set_bit(chan->scan_index, scan_mask);
+
+ reinit_completion(&adc->completion);
+
+ adc->bufi = 0;
+ adc->num_conv = 1;
+ adc->buffer = &result;
+
+ ret = stm32_adc_conf_scan(indio_dev, scan_mask);
+ if (ret)
+ goto free;
+
+ /* No HW trigger: conversion can be launched in SW */
+ ret = stm32_adc_set_trig(indio_dev, NULL);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't set SW trigger\n");
+ goto adc_disable;
+ }
+
+ stm32_adc_conv_irq_enable(adc);
+
+ ret = stm32_adc_start_conv(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to start single conv\n");
+ goto irq_disable;
+ }
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &adc->completion, STM32_ADC_TIMEOUT);
+ if (timeout == 0) {
+ dev_warn(&indio_dev->dev, "Conversion timed out!\n");
+ ret = -ETIMEDOUT;
+ } else if (timeout < 0) {
+ dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
+ ret = -EINTR;
+ } else {
+ *val = result & STM32_RESULT_MASK;
+ ret = IIO_VAL_INT;
+ }
+
+ if (stm32_adc_stop_conv(adc))
+ dev_err(&indio_dev->dev, "stop failed\n");
+
+irq_disable:
+ stm32_adc_conv_irq_disable(adc);
+
+adc_disable:
+ stm32_adc_disable(adc);
+
+free:
+ kfree(scan_mask);
+ adc->buffer = NULL;
+
+ return ret;
+}
+
+static int stm32_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ if (chan->type == IIO_VOLTAGE)
+ ret = stm32_adc_single_conv(indio_dev, chan, val);
+ iio_device_release_direct_mode(indio_dev);
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = adc->common->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ ret = IIO_VAL_FRACTIONAL_LOG2;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
+ */
+static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ const struct stm32_adc_reginfo *reginfo =
+ adc->common->data->adc_reginfo;
+ u32 mask, clr_mask, status = stm32_adc_readl(adc, reginfo->isr);
+
+ if (adc->injected) {
+ mask = reginfo->jeoc;
+ clr_mask = mask;
+ } else {
+ mask = reginfo->eoc;
+ /* don't clear 'eoc' as it is cleared when reading 'dr' */
+ clr_mask = 0;
+ }
+
+ /* clear irq */
+ stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
+ status &= mask;
+
+ /* Regular data */
+ if (status & reginfo->eoc) {
+ adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
+ if (iio_buffer_enabled(indio_dev)) {
+ adc->bufi++;
+ if (adc->bufi >= adc->num_conv) {
+ stm32_adc_conv_irq_disable(adc);
+ iio_trigger_poll(indio_dev->trig);
+ }
+ } else {
+ complete(&adc->completion);
+ }
+ }
+
+ /* Injected data */
+ if (status & reginfo->jeoc) {
+ int i;
+
+ for (i = 0; i < adc->num_conv; i++) {
+ adc->buffer[i] = stm32_adc_readl(adc, reginfo->jdr[i]);
+ adc->bufi++;
+ }
+
+ if (iio_buffer_enabled(indio_dev)) {
+ stm32_adc_conv_irq_disable(adc);
+ iio_trigger_poll(indio_dev->trig);
+ } else {
+ complete(&adc->completion);
+ }
+ }
+
+ /*
+ * In case end of conversion flags have been handled, this has been
+ * handled for this ADC instance
+ */
+ if (status)
+ return IRQ_HANDLED;
+
+ /* This adc instance didn't trigger this interrupt */
+ return IRQ_NONE;
+}
+
+/**
+ * stm32_adc_common_isr() - Common isr for the whole ADC block
+ *
+ * There is one IRQ for all ADCs in ADC block, check all instances.
+ */
+static irqreturn_t stm32_adc_common_isr(int irq, void *data)
+{
+ struct stm32_adc_common *common = data;
+ irqreturn_t ret = IRQ_NONE;
+ struct stm32_adc *adc;
+
+ list_for_each_entry(adc, &common->adc_list, adc_list)
+ ret |= stm32_adc_isr(adc);
+
+ return ret;
+}
+
+/**
+ * stm32_adc_validate_trigger() - validate trigger for stm32 adc
+ * @indio_dev: IIO device
+ * @trig: new trigger
+ *
+ * Returns: 0 if trig matches one of the triggers registered by stm32 adc
+ * driver, -EINVAL otherwise.
+ */
+static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ return stm32_adc_get_trig_index(indio_dev, trig) < 0 ? -EINVAL : 0;
+}
+
+static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+ u32 bit;
+
+ adc->num_conv = 0;
+ for_each_set_bit(bit, scan_mask, indio_dev->masklength)
+ adc->num_conv++;
+
+ ret = stm32_adc_conf_scan(indio_dev, scan_mask);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ int i;
+
+ for (i = 0; i < indio_dev->num_channels; i++)
+ if (indio_dev->channels[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+/**
+ * stm32_adc_debugfs_reg_access - read or write register value
+ *
+ * To read a value from an ADC register:
+ * echo [ADC reg offset] > direct_reg_access
+ * cat direct_reg_access
+ *
+ * To write a value in a ADC register:
+ * echo [ADC_reg_offset] [value] > direct_reg_access
+ */
+static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ if (!readval)
+ stm32_adc_writel(adc, reg, writeval);
+ else
+ *readval = stm32_adc_readl(adc, reg);
+
+ return 0;
+}
+
+static const struct iio_info stm32_adc_iio_info = {
+ .read_raw = stm32_adc_read_raw,
+ .validate_trigger = stm32_adc_validate_trigger,
+ .update_scan_mode = stm32_adc_update_scan_mode,
+ .debugfs_reg_access = stm32_adc_debugfs_reg_access,
+ .of_xlate = stm32_adc_of_xlate,
+ .driver_module = THIS_MODULE,
+};
+
+static int stm32_adc_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ stm32_adc_disable(adc);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .postenable = &iio_triggered_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+ .postdisable = &stm32_adc_buffer_postdisable,
+};
+
+static int stm32_adc_validate_device(struct iio_trigger *trig,
+ struct iio_dev *indio_dev)
+{
+ struct iio_dev *indio = iio_trigger_get_drvdata(trig);
+
+ return indio != indio_dev ? -EINVAL : 0;
+}
+
+static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ if (state) {
+ /* Reset adc buffer index */
+ adc->bufi = 0;
+
+ /* Allocate adc buffer */
+ adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (!adc->buffer)
+ return -ENOMEM;
+
+ ret = stm32_adc_set_trig(indio_dev, trig);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't set trigger\n");
+ goto err_buffer_free;
+ }
+
+ stm32_adc_conv_irq_enable(adc);
+
+ ret = stm32_adc_start_conv(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to start\n");
+ goto err_irq_trig_disable;
+ }
+ } else {
+ ret = stm32_adc_stop_conv(adc);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Failed to stop\n");
+ return ret;
+ }
+
+ stm32_adc_conv_irq_disable(adc);
+
+ ret = stm32_adc_set_trig(indio_dev, NULL);
+ if (ret)
+ dev_warn(&indio_dev->dev, "Can't clear trigger\n");
+
+ kfree(adc->buffer);
+ adc->buffer = NULL;
+ }
+
+ return 0;
+
+err_irq_trig_disable:
+ stm32_adc_conv_irq_disable(adc);
+ stm32_adc_set_trig(indio_dev, NULL);
+
+err_buffer_free:
+ kfree(adc->buffer);
+ adc->buffer = NULL;
+
+ return ret;
+}
+
+static const struct iio_trigger_ops stm32_adc_trigger_ops = {
+ .owner = THIS_MODULE,
+ .validate_device = stm32_adc_validate_device,
+ .set_trigger_state = stm32_adc_set_trigger_state,
+};
+
+static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
+
+ /* reset buffer index */
+ adc->bufi = 0;
+ iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
+ pf->timestamp);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ /* re-enable eoc irq */
+ stm32_adc_conv_irq_enable(adc);
+
+ return IRQ_HANDLED;
+}
+
+static void stm32_adc_trig_unregister(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct iio_trigger *trig, *_t;
+
+ list_for_each_entry_safe(trig, _t, &adc->extrig_list, alloc_list) {
+ iio_trigger_unregister(trig);
+ list_del(&trig->alloc_list);
+ }
+}
+
+static int stm32_adc_trig_register(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_common *common = adc->common;
+ const struct stm32_adc_trig_info *ext = common->data->ext_triggers;
+ struct iio_trigger *trig;
+ int i, ret = 0;
+
+ if (adc->injected)
+ ext = common->data->jext_triggers;
+ else
+ ext = common->data->ext_triggers;
+
+ for (i = 0; ext && ext[i].name; i++) {
+ trig = devm_iio_trigger_alloc(common->dev, "%s_%s%d_%s",
+ indio_dev->name,
+ adc->injected ? "jext" : "ext",
+ ext[i].extsel, ext[i].name);
+ if (!trig) {
+ dev_err(common->dev, "trig %s_%s%d_%s alloc failed\n",
+ indio_dev->name,
+ adc->injected ? "jext" : "ext",
+ ext[i].extsel, ext[i].name);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ trig->dev.parent = common->dev;
+ trig->ops = &stm32_adc_trigger_ops;
+ iio_trigger_set_drvdata(trig, indio_dev);
+
+ ret = iio_trigger_register(trig);
+ if (ret) {
+ dev_err(common->dev,
+ "trig %s_%s%d_%s register failed\n",
+ indio_dev->name,
+ adc->injected ? "jext" : "ext",
+ ext[i].extsel, ext[i].name);
+ goto err;
+ }
+
+ list_add_tail(&trig->alloc_list, &adc->extrig_list);
+ }
+
+ return 0;
+err:
+ stm32_adc_trig_unregister(indio_dev);
+
+ return ret;
+}
+
+static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan,
+ const struct stm32_adc_chan_spec *channel,
+ int scan_index)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ chan->type = channel->type;
+ chan->channel = channel->channel;
+ chan->datasheet_name = channel->name;
+ chan->extend_name = channel->name;
+ chan->scan_index = scan_index;
+ chan->indexed = 1;
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = adc->common->data->highres;
+ chan->scan_type.storagebits = STM32_STORAGEBITS;
+ chan->scan_type.shift = 0;
+}
+
+static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
+ const struct stm32_adc_info *adc_info)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device_node *node = indio_dev->dev.of_node;
+ struct property *prop;
+ const __be32 *cur;
+ struct iio_chan_spec *channels;
+ int scan_index = 0, num_channels = 0;
+ u32 val;
+
+ of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
+ num_channels++;
+
+ channels = devm_kcalloc(&indio_dev->dev, num_channels,
+ sizeof(struct iio_chan_spec), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+ &adc_info->channels[val],
+ scan_index);
+ scan_index++;
+ }
+
+ adc->max_channels = adc_info->max_channels;
+ indio_dev->num_channels = scan_index;
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int stm32_adc_register(struct stm32_adc_common *common,
+ struct device_node *child)
+{
+ struct iio_dev *indio_dev;
+ struct stm32_adc *adc;
+ int i, ret;
+ u32 reg;
+
+ ret = of_property_read_u32(child, "reg", ®);
+ if (ret != 0) {
+ dev_err(common->dev, "missing reg property\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; common->data->adc_info[i].channels; i++)
+ if (common->data->adc_info[i].reg == reg)
+ break;
+
+ if (i >= STM32_ADC_ID_MAX || !common->data->adc_info[i].channels) {
+ dev_err(common->dev, "bad adc reg offset\n");
+ return -ENOENT;
+ }
+
+ indio_dev = devm_iio_device_alloc(common->dev, sizeof(*adc));
+ if (!indio_dev) {
+ dev_err(common->dev, "iio device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ adc = iio_priv(indio_dev);
+ adc->id = i;
+ adc->offset = reg;
+ adc->common = common;
+ INIT_LIST_HEAD(&adc->extrig_list);
+ spin_lock_init(&adc->lock);
+ init_completion(&adc->completion);
+
+ if (child->name)
+ indio_dev->name = child->name;
+ else
+ indio_dev->name = common->data->adc_info[i].name;
+ indio_dev->dev.parent = common->dev;
+ indio_dev->dev.of_node = child;
+ indio_dev->info = &stm32_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (of_property_read_bool(child, "st,injected")) {
+ dev_dbg(common->dev, "%s Configured to use injected\n",
+ indio_dev->name);
+ adc->injected = true;
+ }
+
+ adc->clk = of_clk_get(child, 0);
+ if (IS_ERR(adc->clk)) {
+ adc->clk = NULL;
+ dev_dbg(common->dev, "No child clk found\n");
+ } else {
+ ret = clk_prepare_enable(adc->clk);
+ if (ret < 0)
+ goto err_clk_put;
+ }
+
+ ret = stm32_adc_chan_of_init(indio_dev, &common->data->adc_info[i]);
+ if (ret < 0) {
+ dev_err(common->dev, "iio channels init failed\n");
+ goto err_clk_disable;
+ }
+
+ ret = stm32_adc_trig_register(indio_dev);
+ if (ret)
+ goto err_clk_disable;
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time,
+ &stm32_adc_trigger_handler,
+ &iio_triggered_buffer_setup_ops);
+ if (ret) {
+ dev_err(common->dev, "buffer setup failed\n");
+ goto err_trig_unregister;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(common->dev, "iio dev register failed\n");
+ goto err_buffer_cleanup;
+ }
+
+ list_add_tail(&adc->adc_list, &common->adc_list);
+
+ return 0;
+
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+err_trig_unregister:
+ stm32_adc_trig_unregister(indio_dev);
+
+err_clk_disable:
+ if (adc->clk)
+ clk_disable_unprepare(adc->clk);
+
+err_clk_put:
+ if (adc->clk)
+ clk_put(adc->clk);
+
+ return ret;
+}
+
+static void stm32_adc_unregister(struct stm32_adc *adc)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ stm32_adc_trig_unregister(indio_dev);
+ if (adc->clk) {
+ clk_disable_unprepare(adc->clk);
+ clk_put(adc->clk);
+ }
+}
+
+int stm32_adc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node, *child;
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *match;
+ struct stm32_adc_common *common;
+ struct stm32_adc *adc;
+ struct resource *res;
+ int ret;
+
+ match = of_match_device(dev->driver->of_match_table, &pdev->dev);
+ if (!match || !match->data) {
+ dev_err(&pdev->dev, "compatible data not provided\n");
+ return -EINVAL;
+ }
+
+ common = devm_kzalloc(&pdev->dev, sizeof(*common), GFP_KERNEL);
+ if (!common)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ common->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(common->base))
+ return PTR_ERR(common->base);
+
+ common->data = match->data;
+ common->dev = &pdev->dev;
+ platform_set_drvdata(pdev, common);
+ mutex_init(&common->lock);
+ INIT_LIST_HEAD(&common->adc_list);
+
+ common->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(common->vref)) {
+ ret = PTR_ERR(common->vref);
+ dev_err(&pdev->dev, "vref get failed, %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(common->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref enable failed\n");
+ return ret;
+ }
+
+ ret = regulator_get_voltage(common->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
+ goto err_regulator_disable;
+ }
+ common->vref_mv = ret / 1000;
+ dev_dbg(&pdev->dev, "vref+=%dmV\n", common->vref_mv);
+
+ common->aclk = devm_clk_get(&pdev->dev, "adc");
+ if (IS_ERR(common->aclk)) {
+ ret = PTR_ERR(common->aclk);
+ dev_err(&pdev->dev, "Can't get 'adc' clock\n");
+ goto err_regulator_disable;
+ }
+
+ ret = clk_prepare_enable(common->aclk);
+ if (ret < 0) {
+ dev_err(common->dev, "adc clk enable failed\n");
+ goto err_regulator_disable;
+ }
+
+ common->irq = platform_get_irq(pdev, 0);
+ if (common->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ ret = common->irq;
+ goto err_clk_disable;
+ }
+
+ ret = devm_request_irq(&pdev->dev, common->irq, stm32_adc_common_isr,
+ 0, pdev->name, common);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ goto err_clk_disable;
+ }
+
+ /* Parse adc child nodes to retrieve master/slave instances data */
+ for_each_available_child_of_node(np, child) {
+ ret = stm32_adc_register(common, child);
+ if (ret)
+ goto err_unregister;
+ }
+
+ dev_info(&pdev->dev, "registered\n");
+
+ return 0;
+
+err_unregister:
+ list_for_each_entry(adc, &common->adc_list, adc_list)
+ stm32_adc_unregister(adc);
+
+err_clk_disable:
+ clk_disable_unprepare(common->aclk);
+
+err_regulator_disable:
+ regulator_disable(common->vref);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_probe);
+
+int stm32_adc_remove(struct platform_device *pdev)
+{
+ struct stm32_adc_common *common = platform_get_drvdata(pdev);
+ struct stm32_adc *adc;
+
+ list_for_each_entry(adc, &common->adc_list, adc_list)
+ stm32_adc_unregister(adc);
+ clk_disable_unprepare(common->aclk);
+ regulator_disable(common->vref);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_remove);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
new file mode 100644
index 0000000..0be603c
--- /dev/null
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -0,0 +1,442 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STM32_ADC_H
+#define __STM32_ADC_H
+
+/*
+ * STM32 - ADC global register map
+ * ________________________________________________________
+ * | Offset | Register |
+ * --------------------------------------------------------
+ * | 0x000 | Master ADC1 |
+ * --------------------------------------------------------
+ * | 0x100 | Slave ADC2 |
+ * --------------------------------------------------------
+ * | 0x200 | Slave ADC3 |
+ * --------------------------------------------------------
+ * | 0x300 | Master & Slave common regs |
+ * --------------------------------------------------------
+ */
+#define STM32_ADCX_COMN_OFFSET 0x300
+#define STM32_ADC_ID_MAX 3
+#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */
+#define STM32_ADC_MAX_JSQ 4 /* JSQ1..JSQ4 */
+
+/* STM32 value masks */
+#define STM32_RESULT_MASK GENMASK(15, 0)
+#define STM32_STORAGEBITS 16
+
+/* External trigger enable for regular or injected channels (exten/jexten) */
+enum stm32_adc_exten {
+ STM32_EXTEN_SWTRIG,
+ STM32_EXTEN_HWTRIG_RISING_EDGE,
+ STM32_EXTEN_HWTRIG_FALLING_EDGE,
+ STM32_EXTEN_HWTRIG_BOTH_EDGES,
+};
+
+enum stm32_adc_extsel {
+ STM32_EXT0,
+ STM32_EXT1,
+ STM32_EXT2,
+ STM32_EXT3,
+ STM32_EXT4,
+ STM32_EXT5,
+ STM32_EXT6,
+ STM32_EXT7,
+ STM32_EXT8,
+ STM32_EXT9,
+ STM32_EXT10,
+ STM32_EXT11,
+ STM32_EXT12,
+ STM32_EXT13,
+ STM32_EXT14,
+ STM32_EXT15,
+ STM32_EXT16,
+ STM32_EXT17,
+ STM32_EXT18,
+ STM32_EXT19,
+ STM32_EXT20,
+ STM32_EXT21,
+ STM32_EXT22,
+ STM32_EXT23,
+ STM32_EXT24,
+ STM32_EXT25,
+ STM32_EXT26,
+ STM32_EXT27,
+ STM32_EXT28,
+ STM32_EXT29,
+ STM32_EXT30,
+ STM32_EXT31,
+};
+
+enum stm32_adc_jextsel {
+ STM32_JEXT0,
+ STM32_JEXT1,
+ STM32_JEXT2,
+ STM32_JEXT3,
+ STM32_JEXT4,
+ STM32_JEXT5,
+ STM32_JEXT6,
+ STM32_JEXT7,
+ STM32_JEXT8,
+ STM32_JEXT9,
+ STM32_JEXT10,
+ STM32_JEXT11,
+ STM32_JEXT12,
+ STM32_JEXT13,
+ STM32_JEXT14,
+ STM32_JEXT15,
+ STM32_JEXT16,
+ STM32_JEXT17,
+ STM32_JEXT18,
+ STM32_JEXT19,
+ STM32_JEXT20,
+ STM32_JEXT21,
+ STM32_JEXT22,
+ STM32_JEXT23,
+ STM32_JEXT24,
+ STM32_JEXT25,
+ STM32_JEXT26,
+ STM32_JEXT27,
+ STM32_JEXT28,
+ STM32_JEXT29,
+ STM32_JEXT30,
+ STM32_JEXT31,
+};
+
+#define STM32_ADC_TIMEOUT_US 100000
+#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+
+/**
+ * struct stm32_adc_chan_spec - specification of stm32 adc channel
+ * @type: IIO channel type
+ * @channel: channel number (single ended)
+ * @name: channel name (single ended)
+ */
+struct stm32_adc_chan_spec {
+ enum iio_chan_type type;
+ int channel;
+ const char *name;
+};
+
+/**
+ * struct stm32_adc_trig_info - ADC trigger info
+ * @extsel: trigger selection for regular or injected
+ * @name: name of the trigger, corresponding to its source
+ */
+struct stm32_adc_trig_info {
+ u32 extsel;
+ const char *name;
+};
+
+/**
+ * struct stm32_adc_info - stm32 ADC, per instance config data
+ * @name: default name for this instance (like "adc1")
+ * @reg: reg offset for this instance (e.g. 0x0 for adc1...)
+ * @channels: Reference to stm32 channels spec
+ * @max_channels: Number of single ended channels
+ */
+struct stm32_adc_info {
+ const char *name;
+ u32 reg;
+ const struct stm32_adc_chan_spec *channels;
+ int max_channels;
+};
+
+/**
+ * stm32_adc_regs - stm32 ADC misc registers & bitfield desc
+ * @reg: register offset
+ * @mask: bitfield mask
+ * @shift: left shift
+ */
+struct stm32_adc_regs {
+ int reg;
+ int mask;
+ int shift;
+};
+
+/**
+ * stm32_adc_trig_reginfo - stm32 ADC trigger control registers description
+ * @reg: trigger control register offset (exten/jexten)
+ * @exten_mask: external trigger en/polarity mask in @reg
+ * @exten_shift: external trigger en/polarity shift in @reg
+ * @extsel_mask: external trigger source mask in @reg
+ * @extsel_shift: external trigger source shift in @reg
+ */
+struct stm32_adc_trig_reginfo {
+ u32 reg;
+ u32 exten_mask;
+ u32 exten_shift;
+ u32 extsel_mask;
+ u32 extsel_shift;
+};
+
+/**
+ * struct stm32_adc_reginfo - stm32 ADC registers description
+ * @isr: interrupt status register offset
+ * @eoc: end of conversion mask in @isr
+ * @jeoc: end of injected conversion sequence mask in @isr
+ * @ier: interrupt enable register offset
+ * @eocie: end of conversion interrupt enable mask in @ier
+ * @jeocie: end of injected conversion sequence interrupt en mask
+ * @dr: data register offset
+ * @jdr: injected data registers offsets
+ * @sqr_regs: Regular sequence registers description
+ * @jsqr_reg: Injected sequence register description
+ * @trig_reginfo: regular trigger control registers description
+ * @jtrig_reginfo: injected trigger control registers description
+ */
+struct stm32_adc_reginfo {
+ u32 isr;
+ u32 eoc;
+ u32 jeoc;
+ u32 ier;
+ u32 eocie;
+ u32 jeocie;
+ u32 dr;
+ u32 jdr[4];
+ const struct stm32_adc_regs *sqr_regs;
+ const struct stm32_adc_regs *jsqr_reg;
+ const struct stm32_adc_trig_reginfo *trig_reginfo;
+ const struct stm32_adc_trig_reginfo *jtrig_reginfo;
+};
+
+struct stm32_adc;
+
+/**
+ * struct stm32_adc_ops - stm32 ADC, compatible dependent data
+ * - stm32 ADC may work as single ADC, or as tightly coupled master/slave ADCs.
+ *
+ * @adc_info: Array spec for stm32 adc master/slaves instances
+ * @ext_triggers: Reference to trigger info for regular channels
+ * @jext_triggers: Reference to trigger info for injected channels
+ * @adc_reginfo: stm32 ADC registers description
+ * @highres: Max resolution
+ * @max_clock_rate: Max input clock rate
+ * @clk_sel: routine to select common clock and prescaler
+ * @start_conv: routine to start conversions
+ * @stop_conv: routine to stop conversions
+ * @is_started: routine to get adc 'started' state
+ * @regular_started routine to check regular conversions status
+ * @injected_started routine to check injected conversions status
+ * @enable: optional routine to enable stm32 adc
+ * @disable: optional routine to disable stm32 adc
+ * @is_enabled reports enabled state
+ */
+struct stm32_adc_ops {
+ const struct stm32_adc_info *adc_info;
+ const struct stm32_adc_trig_info *ext_triggers;
+ const struct stm32_adc_trig_info *jext_triggers;
+ const struct stm32_adc_reginfo *adc_reginfo;
+ int highres;
+ unsigned long max_clock_rate;
+ int (*clk_sel)(struct stm32_adc *adc);
+ int (*start_conv)(struct stm32_adc *adc);
+ int (*stop_conv)(struct stm32_adc *adc);
+ bool (*is_started)(struct stm32_adc *adc);
+ bool (*regular_started)(struct stm32_adc *adc);
+ bool (*injected_started)(struct stm32_adc *adc);
+ int (*enable)(struct stm32_adc *adc);
+ void (*disable)(struct stm32_adc *adc);
+ bool (*is_enabled)(struct stm32_adc *adc);
+};
+
+struct stm32_adc_common;
+
+/**
+ * struct stm32_adc - private data of each ADC IIO instance
+ * @common: reference to ADC block common data
+ * @adc_list: current ADC entry in common ADC list
+ * @id: ADC instance number (e.g. adc 1, 2 or 3)
+ * @offset: ADC instance register offset in ADC block
+ * @max_channels: Max channels number for this ADC.
+ * @extrig_list: External trigger list (for regular channel)
+ * @completion: end of single conversion completion
+ * @buffer: data buffer
+ * @bufi: data buffer index
+ * @num_conv: expected number of scan conversions
+ * @injected: use injected channels on this adc
+ * @lock: spinlock
+ * @clk: optional adc clock, for this adc instance
+ * @calib: optional calibration data
+ * @en: emulates enabled state on some stm32 adc
+ */
+struct stm32_adc {
+ struct stm32_adc_common *common;
+ struct list_head adc_list;
+ int id;
+ int offset;
+ int max_channels;
+ struct list_head extrig_list;
+ struct completion completion;
+ u16 *buffer;
+ int bufi;
+ int num_conv;
+ bool injected;
+ spinlock_t lock; /* interrupt lock */
+ struct clk *clk;
+ void *calib;
+ bool en;
+};
+
+/**
+ * struct stm32_adc_common - private data of ADC driver, common to all
+ * ADC instances (ADC block)
+ * @dev: device for this controller
+ * @base: control registers base cpu addr
+ * @irq: Common irq line for all adc instances
+ * @data: STM32 dependent data from compatible
+ * @adc_list: list of all stm32 ADC in this ADC block
+ * @aclk: common clock for the analog circuitry
+ * @vref: regulator reference
+ * @vref_mv: vref voltage (mv)
+ * @lock: mutex
+ */
+struct stm32_adc_common {
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+ const struct stm32_adc_ops *data;
+ struct list_head adc_list;
+ struct clk *aclk;
+ struct regulator *vref;
+ int vref_mv;
+ struct mutex lock; /* read_raw lock */
+};
+
+/* Helper routines */
+static inline int stm32_adc_start_conv(struct stm32_adc *adc)
+{
+ return adc->common->data->start_conv(adc);
+}
+
+static inline int stm32_adc_stop_conv(struct stm32_adc *adc)
+{
+ return adc->common->data->stop_conv(adc);
+}
+
+static inline bool stm32_adc_is_started(struct stm32_adc *adc)
+{
+ return adc->common->data->is_started(adc);
+}
+
+static inline bool stm32_adc_regular_started(struct stm32_adc *adc)
+{
+ return adc->common->data->regular_started(adc);
+}
+
+static inline bool stm32_adc_injected_started(struct stm32_adc *adc)
+{
+ return adc->common->data->injected_started(adc);
+}
+
+static inline bool stm32_adc_clk_sel(struct stm32_adc *adc)
+{
+ return adc->common->data->clk_sel(adc);
+}
+
+static inline int stm32_adc_enable(struct stm32_adc *adc)
+{
+ if (adc->common->data->enable)
+ return adc->common->data->enable(adc);
+
+ adc->en = true;
+
+ return 0;
+}
+
+static inline bool stm32_adc_is_enabled(struct stm32_adc *adc)
+{
+ if (adc->common->data->is_enabled)
+ return adc->common->data->is_enabled(adc);
+ else
+ return adc->en;
+}
+
+static inline void stm32_adc_disable(struct stm32_adc *adc)
+{
+ /* Check there is no regular or injected on-going conversions */
+ if (stm32_adc_is_started(adc))
+ return;
+
+ if (adc->common->data->disable)
+ adc->common->data->disable(adc);
+ else
+ adc->en = false;
+}
+
+/* STM32 ADC registers access routines */
+static inline u32 stm32_adc_common_readl(struct stm32_adc_common *com, u32 reg)
+{
+ u32 val = readl_relaxed(com->base + reg);
+
+ return val;
+}
+
+static inline void stm32_adc_common_writel(struct stm32_adc_common *com,
+ u32 reg, u32 val)
+{
+ writel_relaxed(val, com->base + reg);
+}
+
+static inline u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
+{
+ u32 val = readl_relaxed(adc->common->base + adc->offset + reg);
+
+ return val;
+}
+
+#define stm32_adc_readl_addr(addr) stm32_adc_readl(adc, addr)
+
+#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \
+ readx_poll_timeout(stm32_adc_readl_addr, reg, val, \
+ cond, sleep_us, timeout_us)
+
+static inline void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
+{
+ writel_relaxed(val, adc->common->base + adc->offset + reg);
+}
+
+static inline void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+/* STM32 common extended attributes */
+extern const struct iio_enum stm32_adc_trig_pol;
+int stm32_adc_probe(struct platform_device *pdev);
+int stm32_adc_remove(struct platform_device *pdev);
+
+#endif
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
new file mode 100644
index 0000000..147fe9c
--- /dev/null
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -0,0 +1,574 @@
+/*
+ * This file is part of STM32F4 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/platform_device.h>
+#include "stm32-adc.h"
+
+/*
+ * STM32F4 - ADC global register map
+ * ________________________________________________________
+ * | Offset | Register |
+ * --------------------------------------------------------
+ * | 0x000 | Master ADC1 |
+ * --------------------------------------------------------
+ * | 0x100 | Slave ADC2 |
+ * --------------------------------------------------------
+ * | 0x200 | Slave ADC3 |
+ * --------------------------------------------------------
+ * | 0x300 | Master & Slave common regs |
+ * --------------------------------------------------------
+ */
+
+/* STM32F4 - Registers for each ADC instance */
+#define STM32F4_ADCX_SR 0x00
+#define STM32F4_ADCX_CR1 0x04
+#define STM32F4_ADCX_CR2 0x08
+#define STM32F4_ADCX_SMPR1 0x0C
+#define STM32F4_ADCX_SMPR2 0x10
+#define STM32F4_ADCX_HTR 0x24
+#define STM32F4_ADCX_LTR 0x28
+#define STM32F4_ADCX_SQR1 0x2C
+#define STM32F4_ADCX_SQR2 0x30
+#define STM32F4_ADCX_SQR3 0x34
+#define STM32F4_ADCX_JSQR 0x38
+#define STM32F4_ADCX_JDR1 0x3C
+#define STM32F4_ADCX_JDR2 0x40
+#define STM32F4_ADCX_JDR3 0x44
+#define STM32F4_ADCX_JDR4 0x48
+#define STM32F4_ADCX_DR 0x4C
+
+/* STM32 - Master & slave registers (common for all instances: 1, 2 & 3) */
+#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
+#define STM32F4_ADC_CDR (STM32_ADCX_COMN_OFFSET + 0x08)
+
+/* STM32F4_ADCX_SR - bit fields */
+#define STM32F4_OVR BIT(5)
+#define STM32F4_STRT BIT(4)
+#define STM32F4_JSTRT BIT(3)
+#define STM32F4_JEOC BIT(2)
+#define STM32F4_EOC BIT(1)
+#define STM32F4_AWD BIT(0)
+
+/* STM32F4_ADCX_CR1 - bit fields */
+#define STM32F4_OVRIE BIT(26)
+#define STM32F4_RES_SHIFT 24
+#define STM32F4_RES_MASK GENMASK(25, 24)
+#define STM32F4_AWDEN BIT(23)
+#define STM32F4_JAWDEN BIT(22)
+#define STM32F4_DISCNUM_SHIFT 13
+#define STM32F4_DISCNUM_MASK GENMASK(15, 13)
+#define STM32F4_JDISCEN BIT(12)
+#define STM32F4_DISCEN BIT(11)
+#define STM32F4_JAUTO BIT(10)
+#define STM32F4_AWDSGL BIT(9)
+#define STM32F4_SCAN BIT(8)
+#define STM32F4_JEOCIE BIT(7)
+#define STM32F4_AWDIE BIT(6)
+#define STM32F4_EOCIE BIT(5)
+#define STM32F4_AWDCH_SHIFT 0
+#define STM32F4_AWDCH_MASK GENMASK(4, 0)
+
+/* STM32F4_ADCX_CR2 - bit fields */
+#define STM32F4_SWSTART BIT(30)
+#define STM32F4_EXTEN_SHIFT 28
+#define STM32F4_EXTEN_MASK GENMASK(29, 28)
+#define STM32F4_EXTSEL_SHIFT 24
+#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
+#define STM32F4_JSWSTART BIT(22)
+#define STM32F4_JEXTEN_SHIFT 20
+#define STM32F4_JEXTEN_MASK GENMASK(21, 20)
+#define STM32F4_JEXTSEL_SHIFT 16
+#define STM32F4_JEXTSEL_MASK GENMASK(19, 16)
+#define STM32F4_ALIGN BIT(11)
+#define STM32F4_EOCS BIT(10)
+#define STM32F4_DDS BIT(9)
+#define STM32F4_DMA BIT(8)
+#define STM32F4_CONT BIT(1)
+#define STM32F4_ADON BIT(0)
+
+/* STM32F4_ADCX_SMPR1 - bit fields */
+#define STM32F4_SMP18_SHIFT 24
+#define STM32F4_SMP18_MASK GENMASK(26, 24)
+#define STM32F4_SMP17_SHIFT 21
+#define STM32F4_SMP17_MASK GENMASK(23, 21)
+#define STM32F4_SMP16_SHIFT 18
+#define STM32F4_SMP16_MASK GENMASK(20, 18)
+#define STM32F4_SMP15_SHIFT 15
+#define STM32F4_SMP15_MASK GENMASK(17, 15)
+#define STM32F4_SMP14_SHIFT 12
+#define STM32F4_SMP14_MASK GENMASK(14, 12)
+#define STM32F4_SMP13_SHIFT 9
+#define STM32F4_SMP13_MASK GENMASK(11, 9)
+#define STM32F4_SMP12_SHIFT 6
+#define STM32F4_SMP12_MASK GENMASK(8, 6)
+#define STM32F4_SMP11_SHIFT 3
+#define STM32F4_SMP11_MASK GENMASK(5, 3)
+#define STM32F4_SMP10_SHIFT 0
+#define STM32F4_SMP10_MASK GENMASK(2, 0)
+
+/* STM32F4_ADCX_SMPR2 - bit fields */
+#define STM32F4_SMP9_SHIFT 27
+#define STM32F4_SMP9_MASK GENMASK(29, 27)
+#define STM32F4_SMP8_SHIFT 24
+#define STM32F4_SMP8_MASK GENMASK(26, 24)
+#define STM32F4_SMP7_SHIFT 21
+#define STM32F4_SMP7_MASK GENMASK(23, 21)
+#define STM32F4_SMP6_SHIFT 18
+#define STM32F4_SMP6_MASK GENMASK(20, 18)
+#define STM32F4_SMP5_SHIFT 15
+#define STM32F4_SMP5_MASK GENMASK(17, 15)
+#define STM32F4_SMP4_SHIFT 12
+#define STM32F4_SMP4_MASK GENMASK(14, 12)
+#define STM32F4_SMP3_SHIFT 9
+#define STM32F4_SMP3_MASK GENMASK(11, 9)
+#define STM32F4_SMP2_SHIFT 6
+#define STM32F4_SMP2_MASK GENMASK(8, 6)
+#define STM32F4_SMP1_SHIFT 3
+#define STM32F4_SMP1_MASK GENMASK(5, 3)
+#define STM32F4_SMP0_SHIFT 0
+#define STM32F4_SMP0_MASK GENMASK(2, 0)
+enum stm32f4_adc_smpr {
+ STM32F4_SMPR_3_CK_CYCLES,
+ STM32F4_SMPR_15_CK_CYCLES,
+ STM32F4_SMPR_28_CK_CYCLES,
+ STM32F4_SMPR_56_CK_CYCLES,
+ STM32F4_SMPR_84_CK_CYCLES,
+ STM32F4_SMPR_112_CK_CYCLES,
+ STM32F4_SMPR_144_CK_CYCLES,
+ STM32F4_SMPR_480_CK_CYCLES,
+};
+
+/* STM32F4_ADCX_SQR1 - bit fields */
+#define STM32F4_L_SHIFT 20
+#define STM32F4_L_MASK GENMASK(23, 20)
+#define STM32F4_SQ16_SHIFT 15
+#define STM32F4_SQ16_MASK GENMASK(19, 15)
+#define STM32F4_SQ15_SHIFT 10
+#define STM32F4_SQ15_MASK GENMASK(14, 10)
+#define STM32F4_SQ14_SHIFT 5
+#define STM32F4_SQ14_MASK GENMASK(9, 5)
+#define STM32F4_SQ13_SHIFT 0
+#define STM32F4_SQ13_MASK GENMASK(4, 0)
+
+/* STM32F4_ADCX_SQR2 - bit fields */
+#define STM32F4_SQ12_SHIFT 25
+#define STM32F4_SQ12_MASK GENMASK(29, 25)
+#define STM32F4_SQ11_SHIFT 20
+#define STM32F4_SQ11_MASK GENMASK(24, 20)
+#define STM32F4_SQ10_SHIFT 15
+#define STM32F4_SQ10_MASK GENMASK(19, 15)
+#define STM32F4_SQ9_SHIFT 10
+#define STM32F4_SQ9_MASK GENMASK(14, 10)
+#define STM32F4_SQ8_SHIFT 5
+#define STM32F4_SQ8_MASK GENMASK(9, 5)
+#define STM32F4_SQ7_SHIFT 0
+#define STM32F4_SQ7_MASK GENMASK(4, 0)
+
+/* STM32F4_ADCX_SQR3 - bit fields */
+#define STM32F4_SQ6_SHIFT 25
+#define STM32F4_SQ6_MASK GENMASK(29, 25)
+#define STM32F4_SQ5_SHIFT 20
+#define STM32F4_SQ5_MASK GENMASK(24, 20)
+#define STM32F4_SQ4_SHIFT 15
+#define STM32F4_SQ4_MASK GENMASK(19, 15)
+#define STM32F4_SQ3_SHIFT 10
+#define STM32F4_SQ3_MASK GENMASK(14, 10)
+#define STM32F4_SQ2_SHIFT 5
+#define STM32F4_SQ2_MASK GENMASK(9, 5)
+#define STM32F4_SQ1_SHIFT 0
+#define STM32F4_SQ1_MASK GENMASK(4, 0)
+
+/* STM32F4_ADCX_JSQR - bit fields */
+#define STM32F4_JL_SHIFT 20
+#define STM32F4_JL_MASK GENMASK(21, 20)
+#define STM32F4_JSQ4_SHIFT 15
+#define STM32F4_JSQ4_MASK GENMASK(19, 15)
+#define STM32F4_JSQ3_SHIFT 10
+#define STM32F4_JSQ3_MASK GENMASK(14, 10)
+#define STM32F4_JSQ2_SHIFT 5
+#define STM32F4_JSQ2_MASK GENMASK(9, 5)
+#define STM32F4_JSQ1_SHIFT 0
+#define STM32F4_JSQ1_MASK GENMASK(4, 0)
+
+/* STM32F4_ADC_CCR - bit fields */
+#define STM32F4_ADC_ADCPRE_SHIFT 16
+#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
+
+/*
+ * stm32 ADC1, ADC2 & ADC3 are tightly coupled and may be used in multi mode
+ * Define here all inputs for all ADC instances
+ */
+static const struct stm32_adc_chan_spec stm32f4_adc1_channels[] = {
+ /* master ADC1 */
+ { IIO_VOLTAGE, 0, "in0" },
+ { IIO_VOLTAGE, 1, "in1" },
+ { IIO_VOLTAGE, 2, "in2" },
+ { IIO_VOLTAGE, 3, "in3" },
+ { IIO_VOLTAGE, 4, "in4" },
+ { IIO_VOLTAGE, 5, "in5" },
+ { IIO_VOLTAGE, 6, "in6" },
+ { IIO_VOLTAGE, 7, "in7" },
+ { IIO_VOLTAGE, 8, "in8" },
+ { IIO_VOLTAGE, 9, "in9" },
+ { IIO_VOLTAGE, 10, "in10" },
+ { IIO_VOLTAGE, 11, "in11" },
+ { IIO_VOLTAGE, 12, "in12" },
+ { IIO_VOLTAGE, 13, "in13" },
+ { IIO_VOLTAGE, 14, "in14" },
+ { IIO_VOLTAGE, 15, "in15" },
+ /* internal analog sources available on input 16 to 18 */
+ { IIO_VOLTAGE, 16, "in16" },
+ { IIO_VOLTAGE, 17, "in17" },
+ { IIO_VOLTAGE, 18, "in18" },
+};
+
+static const struct stm32_adc_chan_spec stm32f4_adc23_channels[] = {
+ /* slave ADC2 / ADC3 */
+ { IIO_VOLTAGE, 0, "in0" },
+ { IIO_VOLTAGE, 1, "in1" },
+ { IIO_VOLTAGE, 2, "in2" },
+ { IIO_VOLTAGE, 3, "in3" },
+ { IIO_VOLTAGE, 4, "in4" },
+ { IIO_VOLTAGE, 5, "in5" },
+ { IIO_VOLTAGE, 6, "in6" },
+ { IIO_VOLTAGE, 7, "in7" },
+ { IIO_VOLTAGE, 8, "in8" },
+ { IIO_VOLTAGE, 9, "in9" },
+ { IIO_VOLTAGE, 10, "in10" },
+ { IIO_VOLTAGE, 11, "in11" },
+ { IIO_VOLTAGE, 12, "in12" },
+ { IIO_VOLTAGE, 13, "in13" },
+ { IIO_VOLTAGE, 14, "in14" },
+ { IIO_VOLTAGE, 15, "in15" },
+};
+
+/* Triggers for regular channels */
+static const struct stm32_adc_trig_info stm32f4_adc_ext_triggers[] = {
+ { STM32_EXT0, "TIM1_CH1" },
+ { STM32_EXT1, "TIM1_CH2" },
+ { STM32_EXT2, "TIM1_CH3" },
+ { STM32_EXT3, "TIM2_CH2" },
+ { STM32_EXT4, "TIM2_CH3" },
+ { STM32_EXT5, "TIM2_CH4" },
+ { STM32_EXT6, "TIM2_TRGO" },
+ { STM32_EXT7, "TIM3_CH1" },
+ { STM32_EXT8, "TIM3_TRGO" },
+ { STM32_EXT9, "TIM4_CH4" },
+ { STM32_EXT10, "TIM5_CH1" },
+ { STM32_EXT11, "TIM5_CH2" },
+ { STM32_EXT12, "TIM5_CH3" },
+ { STM32_EXT13, "TIM8_CH1" },
+ { STM32_EXT14, "TIM8_TRGO" },
+ { STM32_EXT15, "EXTI_11" },
+ {},
+};
+
+/* Triggers for injected channels */
+static const struct stm32_adc_trig_info stm32f4_adc_jext_triggers[] = {
+ { STM32_JEXT0, "TIM1_CH4" },
+ { STM32_JEXT1, "TIM1_TRGO" },
+ { STM32_JEXT2, "TIM2_CH1" },
+ { STM32_JEXT3, "TIM2_TRGO" },
+ { STM32_JEXT4, "TIM3_CH2" },
+ { STM32_JEXT5, "TIM3_CH4" },
+ { STM32_JEXT6, "TIM4_CH1" },
+ { STM32_JEXT7, "TIM4_CH2" },
+ { STM32_JEXT8, "TIM4_CH3" },
+ { STM32_JEXT9, "TIM4_TRGO" },
+ { STM32_JEXT10, "TIM5_CH4" },
+ { STM32_JEXT11, "TIM5_TRGO" },
+ { STM32_JEXT12, "TIM8_CH2" },
+ { STM32_JEXT13, "TIM8_CH3" },
+ { STM32_JEXT14, "TIM8_CH4" },
+ { STM32_JEXT15, "EXTI_15" },
+ {},
+};
+
+static const struct stm32_adc_info stm32f4_adc_info[] = {
+ {
+ .name = "adc1-master",
+ .reg = 0x0,
+ .channels = stm32f4_adc1_channels,
+ .max_channels = ARRAY_SIZE(stm32f4_adc1_channels),
+ },
+ {
+ .name = "adc2-slave",
+ .reg = 0x100,
+ .channels = stm32f4_adc23_channels,
+ .max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
+ },
+ {
+ .name = "adc3-slave",
+ .reg = 0x200,
+ .channels = stm32f4_adc23_channels,
+ .max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
+ },
+ {},
+};
+
+/**
+ * stm32f4_sqr_regs - describe regular sequence registers
+ * - L: sequence len (register & bit field)
+ * - SQ1..SQ16: sequence entries (register & bit field)
+ */
+static const struct stm32_adc_regs stm32f4_sqr_regs[STM32_ADC_MAX_SQ + 1] = {
+ /* L: len bit field description to be kept as first element */
+ { STM32F4_ADCX_SQR1, STM32F4_L_MASK, STM32F4_L_SHIFT },
+ /* SQ1..SQ16 registers & bit fields */
+ { STM32F4_ADCX_SQR3, STM32F4_SQ1_MASK, STM32F4_SQ1_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ2_MASK, STM32F4_SQ2_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ3_MASK, STM32F4_SQ3_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ4_MASK, STM32F4_SQ4_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ5_MASK, STM32F4_SQ5_SHIFT },
+ { STM32F4_ADCX_SQR3, STM32F4_SQ6_MASK, STM32F4_SQ6_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ7_MASK, STM32F4_SQ7_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ8_MASK, STM32F4_SQ8_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ9_MASK, STM32F4_SQ9_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ10_MASK, STM32F4_SQ10_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ11_MASK, STM32F4_SQ11_SHIFT },
+ { STM32F4_ADCX_SQR2, STM32F4_SQ12_MASK, STM32F4_SQ12_SHIFT },
+ { STM32F4_ADCX_SQR1, STM32F4_SQ13_MASK, STM32F4_SQ13_SHIFT },
+ { STM32F4_ADCX_SQR1, STM32F4_SQ14_MASK, STM32F4_SQ14_SHIFT },
+ { STM32F4_ADCX_SQR1, STM32F4_SQ15_MASK, STM32F4_SQ15_SHIFT },
+ { STM32F4_ADCX_SQR1, STM32F4_SQ16_MASK, STM32F4_SQ16_SHIFT },
+};
+
+/**
+ * stm32f4_jsqr_reg - describe injected sequence register:
+ * - JL: injected sequence len
+ * - JSQ4..SQ1: sequence entries
+ * When JL == 3, ADC converts JSQ1, JSQ2, JSQ3, JSQ4
+ * When JL == 2, ADC converts JSQ2, JSQ3, JSQ4
+ * When JL == 1, ADC converts JSQ3, JSQ4
+ * When JL == 0, ADC converts JSQ4
+ */
+static const struct stm32_adc_regs stm32f4_jsqr_reg[STM32_ADC_MAX_JSQ + 1] = {
+ /* JL: len bit field description to be kept as first element */
+ {STM32F4_ADCX_JSQR, STM32F4_JL_MASK, STM32F4_JL_SHIFT},
+ /* JSQ4..JSQ1 registers & bit fields */
+ {STM32F4_ADCX_JSQR, STM32F4_JSQ4_MASK, STM32F4_JSQ4_SHIFT},
+ {STM32F4_ADCX_JSQR, STM32F4_JSQ3_MASK, STM32F4_JSQ3_SHIFT},
+ {STM32F4_ADCX_JSQR, STM32F4_JSQ2_MASK, STM32F4_JSQ2_SHIFT},
+ {STM32F4_ADCX_JSQR, STM32F4_JSQ1_MASK, STM32F4_JSQ1_SHIFT},
+};
+
+static const struct stm32_adc_trig_reginfo stm32f4_adc_trig_reginfo = {
+ .reg = STM32F4_ADCX_CR2,
+ .exten_mask = STM32F4_EXTEN_MASK,
+ .exten_shift = STM32F4_EXTEN_SHIFT,
+ .extsel_mask = STM32F4_EXTSEL_MASK,
+ .extsel_shift = STM32F4_EXTSEL_SHIFT,
+};
+
+static const struct stm32_adc_trig_reginfo stm32f4_adc_jtrig_reginfo = {
+ .reg = STM32F4_ADCX_CR2,
+ .exten_mask = STM32F4_JEXTEN_MASK,
+ .exten_shift = STM32F4_JEXTEN_SHIFT,
+ .extsel_mask = STM32F4_JEXTSEL_MASK,
+ .extsel_shift = STM32F4_JEXTSEL_SHIFT,
+};
+
+static const struct stm32_adc_reginfo stm32f4_adc_reginfo = {
+ .isr = STM32F4_ADCX_SR,
+ .eoc = STM32F4_EOC,
+ .jeoc = STM32F4_JEOC,
+ .ier = STM32F4_ADCX_CR1,
+ .eocie = STM32F4_EOCIE,
+ .jeocie = STM32F4_JEOCIE,
+ .dr = STM32F4_ADCX_DR,
+ .jdr = {
+ STM32F4_ADCX_JDR1,
+ STM32F4_ADCX_JDR2,
+ STM32F4_ADCX_JDR3,
+ STM32F4_ADCX_JDR4,
+ },
+ .sqr_regs = stm32f4_sqr_regs,
+ .jsqr_reg = stm32f4_jsqr_reg,
+ .trig_reginfo = &stm32f4_adc_trig_reginfo,
+ .jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
+};
+
+static bool stm32f4_adc_is_started(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32F4_ADCX_CR2) & STM32F4_ADON;
+
+ return !!val;
+}
+
+static bool stm32f4_adc_regular_started(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_STRT;
+
+ return !!val;
+}
+
+static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_JSTRT;
+
+ return !!val;
+}
+
+/**
+ * stm32f4_adc_start_conv() - Start regular or injected conversions
+ * @adc: stm32 adc instance
+ *
+ * Start single conversions for regular or injected channels.
+ */
+static int stm32f4_adc_start_conv(struct stm32_adc *adc)
+{
+ u32 trig_msk, start_msk;
+
+ dev_dbg(adc->common->dev, "%s %s\n", __func__,
+ adc->injected ? "injected" : "regular");
+
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+
+ if (!stm32f4_adc_is_started(adc)) {
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
+ STM32F4_EOCS | STM32F4_ADON);
+
+ /* Wait for Power-up time (tSTAB from datasheet) */
+ usleep_range(2, 3);
+ }
+
+ if (adc->injected) {
+ trig_msk = STM32F4_JEXTEN_MASK;
+ start_msk = STM32F4_JSWSTART;
+ } else {
+ trig_msk = STM32F4_EXTEN_MASK;
+ start_msk = STM32F4_SWSTART;
+ }
+
+ /* Software start ? (e.g. trigger detection disabled ?) */
+ if (!(stm32_adc_readl(adc, STM32F4_ADCX_CR2) & trig_msk))
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, start_msk);
+
+ return 0;
+}
+
+static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
+{
+ u32 val;
+
+ dev_dbg(adc->common->dev, "%s %s\n", __func__,
+ adc->injected ? "injected" : "regular");
+
+ /* First disable trigger for either regular or injected channels */
+ if (adc->injected) {
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_JEXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_JSTRT);
+ } else {
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
+ }
+
+ /* Disable adc when all triggered conversion have been disabled */
+ val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
+ val &= STM32F4_EXTEN_MASK | STM32F4_JEXTEN_MASK;
+ if (!val) {
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
+ }
+
+ return 0;
+}
+
+/* ADC internal common clock prescaler division ratios */
+static int stm32f4_pclk_div[] = {2, 4, 6, 8};
+
+/**
+ * stm32f4_adc_clk_sel() - Select ADC common clock prescaler
+ * @adc: stm32 adc instance
+ * Select clock prescaler used for analog conversions.
+ */
+static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
+{
+ struct stm32_adc_common *common = adc->common;
+ unsigned long rate;
+ u32 val;
+ int i;
+
+ /* Common prescaler is set only once, when 1st ADC instance starts */
+ list_for_each_entry(adc, &common->adc_list, adc_list)
+ if (stm32f4_adc_is_started(adc))
+ return 0;
+
+ rate = clk_get_rate(common->aclk);
+ for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
+ if ((rate / stm32f4_pclk_div[i]) <=
+ common->data->max_clock_rate)
+ break;
+ }
+ if (i >= ARRAY_SIZE(stm32f4_pclk_div))
+ return -EINVAL;
+
+ val = stm32_adc_common_readl(common, STM32F4_ADC_CCR);
+ val &= ~STM32F4_ADC_ADCPRE_MASK;
+ val |= i << STM32F4_ADC_ADCPRE_SHIFT;
+ stm32_adc_common_writel(common, STM32F4_ADC_CCR, val);
+
+ dev_dbg(common->dev, "Using analog clock source at %ld kHz\n",
+ rate / (stm32f4_pclk_div[i] * 1000));
+
+ return 0;
+}
+
+static const struct stm32_adc_ops stm32f4_adc_ops = {
+ .adc_info = stm32f4_adc_info,
+ .ext_triggers = stm32f4_adc_ext_triggers,
+ .jext_triggers = stm32f4_adc_jext_triggers,
+ .adc_reginfo = &stm32f4_adc_reginfo,
+ .highres = 12,
+ .max_clock_rate = 36000000,
+ .clk_sel = stm32f4_adc_clk_sel,
+ .start_conv = stm32f4_adc_start_conv,
+ .stop_conv = stm32f4_adc_stop_conv,
+ .is_started = stm32f4_adc_is_started,
+ .regular_started = stm32f4_adc_regular_started,
+ .injected_started = stm32f4_adc_injected_started,
+};
+
+static const struct of_device_id stm32f4_adc_of_match[] = {
+ { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_ops},
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32f4_adc_of_match);
+
+static struct platform_driver stm32f4_adc_driver = {
+ .probe = stm32_adc_probe,
+ .remove = stm32_adc_remove,
+ .driver = {
+ .name = "stm32f4-adc",
+ .of_match_table = stm32f4_adc_of_match,
+ },
+};
+
+module_platform_driver(stm32f4_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32F4 ADC driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
^ permalink raw reply related
* [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477412722-24061-1-git-send-email-fabrice.gasnier@st.com>
This patch adds documentation of device tree bindings for the STM32 ADC.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 78 ++++++++++++++++++++++
1 file changed, 78 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
new file mode 100644
index 0000000..a9a8b3c
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -0,0 +1,78 @@
+STMicroelectronics STM32 ADC device driver
+
+STM32 ADC is a successive approximation analog-to-digital converter.
+It has several multiplexed input channels. Conversions can be performed
+in single, continuous, scan or discontinuous mode. Result of the ADC is
+stored in a left-aligned or right-aligned 32-bit data register.
+Conversions can be launched in software or using hardware triggers.
+
+The analog watchdog feature allows the application to detect if the input
+voltage goes beyond the user-defined, higher or lower thresholds.
+
+Each STM32 ADC block can have up to 3 ADC instances.
+
+Each instance supports two contexts to manage conversions, each one has its
+own configurable sequence and trigger:
+- regular conversion can be done in sequence, running in background
+- injected conversions have higher priority, and so have the ability to
+ interrupt regular conversion sequence (either triggered in SW or HW).
+ Regular sequence is resumed, in case it has been interrupted.
+
+Required properties:
+- compatible: Should be "st,stm32f4-adc".
+- reg: Offset and length of the ADC block register set.
+- interrupts: Must contain the interrupt for ADC.
+- clocks: Clock for the analog circuitry (common to all ADCs).
+- clock-names: Must be "adc".
+- vref-supply: Phandle to the vref input analog reference voltage.
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties:
+- A pinctrl state named "default" for each ADC channel may be defined to set
+ inX ADC pins in mode of operation for analog input on external pin.
+- gpios: Array of gpios that may be configured as EXTi trigger sources.
+
+Example:
+ adc: adc at 40012000 {
+ compatible = "st,stm32f4-adc";
+ reg = <0x40012000 0x400>;
+ interrupts = <18>;
+ clocks = <&rcc 0 168>;
+ clock-names = "adc";
+ vref-supply = <®_vref>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&adc3_in8_pin>;
+ gpios = <&gpioa 11 0>,
+ <&gpioa 15 0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc1: adc1-master at 0 {
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ st,adc-channels = <8>;
+ };
+ ...
+ other adc child nodes follow...
+ };
+
+Contents of a stm32 adc child node:
+-----------------------------------
+An ADC block node should contain at least one subnode, representing an
+ADC instance available on the machine.
+
+Required properties:
+- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
+- st,adc-channels: List of single-ended channels muxed for this ADC.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
+ Documentation/devicetree/bindings/iio/iio-bindings.txt
+
+Optional properties:
+- clocks: Input clock private to this ADC instance.
+- st,injected: Use injected conversion sequence on an ADC, rather than regular.
+- dmas: Phandle to dma channel for this ADC instance, only for regular
+ conversions. See ../../dma/dma.txt for details.
+- dma-names: Must be "rx" when dmas property is being used.
--
1.9.1
^ permalink raw reply related
* [PATCH 00/10] Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
To: linux-arm-kernel
This series adds support for STM32F4 ADC into IIO framework.
STM32F4 ADC is a 12-bit successive approximation analog-to-digital
converter. It has up to 19 multiplexed input channels. Conversions can
be performed in single, continuous, scan or discontinuous mode.
Conversions can be launched in software or using hardware triggers.
This driver has been developed and tested on STM32F429 eval board.
It consist of a core driver, to ease support for other STM32 family
ADCs, and a specific driver for STM32F4 ADC.
It allows to use direct or triggered buffer modes with triggers.
Optional support for DMA and few extended attributes is included.
Fabrice Gasnier (10):
Documentation: dt-bindings: Document STM32 ADC DT bindings
iio: adc: Add stm32 support
iio: adc: stm32: add optional dma support
iio: adc: stm32: add optional support for exti gpios
iio: adc: stm32: add trigger polarity ext attr
iio: adc: stm32: add ext attrs to configure sampling time
ARM: configs: stm32: enable IIO ADC driver
ARM: dts: stm32f429: Add adc support
ARM: dts: stm32f429: enable adc on eval board
ARM: dts: stm32f429: Add dma support to adc
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 78 ++
arch/arm/boot/dts/stm32429i-eval.dts | 30 +
arch/arm/boot/dts/stm32f429.dtsi | 68 +
arch/arm/configs/stm32_defconfig | 2 +
drivers/iio/adc/Kconfig | 2 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32/Kconfig | 36 +
drivers/iio/adc/stm32/Makefile | 4 +
drivers/iio/adc/stm32/stm32-adc.c | 1334 ++++++++++++++++++++
drivers/iio/adc/stm32/stm32-adc.h | 489 +++++++
drivers/iio/adc/stm32/stm32f4-adc.c | 652 ++++++++++
11 files changed, 2696 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
create mode 100644 drivers/iio/adc/stm32/Kconfig
create mode 100644 drivers/iio/adc/stm32/Makefile
create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c
--
1.9.1
^ permalink raw reply
* [PATCH 3/4] dt-bindings: Update domain-idle-state binding to use correct compatibles
From: Lina Iyer @ 2016-10-25 16:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <383a62dd-b4d7-f764-baf1-ff73691b3cbc@arm.com>
On Tue, Oct 25 2016 at 09:59 -0600, Sudeep Holla wrote:
>
>
>On 25/10/16 16:26, Lina Iyer wrote:
>>Update domain-idle-state binding to use "domain-idle-state" compatible
>>from Documentation/devicetree/bindings/arm/idle-states.txt.
>>
>>Cc: <devicetree@vger.kernel.org>
>>Cc: Rob Herring <robh@kernel.org>
>>Suggested-by: Sudeep Holla <sudeep.holla@arm.com>
>>Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>>---
>> Documentation/devicetree/bindings/power/power_domain.txt | 9 +++++----
>> 1 file changed, 5 insertions(+), 4 deletions(-)
>>
>>diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
>>index e165036..6fb53a3 100644
>>--- a/Documentation/devicetree/bindings/power/power_domain.txt
>>+++ b/Documentation/devicetree/bindings/power/power_domain.txt
>>@@ -30,8 +30,9 @@ Optional properties:
>> available in the next section.
>>
>> - domain-idle-states : A phandle of an idle-state that shall be soaked into a
>>- generic domain power state. The idle state definitions are
>>- compatible with arm,idle-state specified in [1].
>>+ generic domain power state. The idle state definitions must be
>>+ compatible with "domain-idle-state"
>
>I would reword the below a bit different so that it's flexible to be
>reused without "arm,idle-state".
>
>>as well as
>>+ "arm,idle-state" as defined in [1].
>
>'Idle states that are "arm,idle-state" compatible are generally
>"domain-idle-state" compatible as well if it's a PM domain.'
>
I believe we should have both compatible strings. Per [1], any CPU that
follows the idle state compatible *must* have "arm,idle-state" as a
compatible. Since we are re-using the same compatible, its only correct
that we retain what is already spec'd up in [1] and in addition provide
this new compatible.
Thanks,
Lina
>or something like that in line with what's in patch 2/4.
>
>That would give us the scope of reuse of "domain-idle-state" in device
>for future. Also it aligns with your patch 4/4.
>
>Otherwise, it looks good.
>
>--
>Regards,
>Sudeep
^ permalink raw reply
* [PATCH/RFT v2 11/17] USB: OHCI: make ohci-da8xx a separate driver
From: David Lechner @ 2016-10-25 16:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKXjFTM8RwsAut8cD0n0foxueSjR4VB4k4WJizXo9SyaW3viOA@mail.gmail.com>
On 10/25/2016 11:21 AM, Axel Haslam wrote:
> On Tue, Oct 25, 2016 at 6:12 PM, David Lechner <david@lechnology.com> wrote:
>> On 10/25/2016 02:39 AM, Axel Haslam wrote:
>>>
>>> On Tue, Oct 25, 2016 at 2:38 AM, David Lechner <david@lechnology.com>
>>> wrote:
>>>>
>>>> On 10/24/2016 11:46 AM, ahaslam at baylibre.com wrote:
>>>>>
>>>>>
>>>>> -#ifndef CONFIG_ARCH_DAVINCI_DA8XX
>>>>> -#error "This file is DA8xx bus glue. Define
>>>>> CONFIG_ARCH_DAVINCI_DA8XX."
>>>>> -#endif
>>>>> +#include "ohci.h"
>>>>> +
>>>>> +#define DRIVER_DESC "OHCI DA8XX driver"
>>>>> +
>>>>> +static const char hcd_name[] = "ohci-da8xx";
>>>>
>>>>
>>>>
>>>> why static const char instead of #define? This is only used one time in a
>>>> pr_info, so it seems kind of pointless anyway.
>>>
>>>
>>> Other drivers are using static const for the same variable.
>>> i think static const is preferred over #define because #define doet give a
>>> type.
>>> If you dont mind ill keep it static const.
>>>
>>
>> If this string was used in this file more than one place, I would agree with
>> you, but currently it is only used as the argument of a pr_info(). The
>> string "ohci-da8xx" could just be included in the fmt string instead of
>> using "%s".
>
> I think the purpose was to use it in the .name of the platform_driver
> structure, too. only that not everybody is doing that, i looked at some bad
> examples :(
>
> would you agree to keep it if we use it in .name too?
>
> -Axel
>
>>
Yes, that will make more sense. ;-)
^ permalink raw reply
* [PATCH] tty/serial: at91: fix hardware handshake on Atmel platforms
From: Alexandre Belloni @ 2016-10-25 16:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161025161135.7316-1-richard.genoud@gmail.com>
Hi,
On 25/10/2016 at 18:11:35 +0200, Richard Genoud wrote :
> commit 1cf6e8fc8341 ("tty/serial: at91: fix RTS line management when
> hardware handshake is enabled"), despite its title, broke hardware
> handshake on *every* Atmel platforms.
>
> The only one partially working is the SAMA5D2.
>
[...]
> Changes since v4:
> - the mctrl_gpio_use_rtscts() is gone since it was atmel_serial
> specific. (so patch 1 is gone)
> - patches 2 and 3 have been merged together since it didn't make
> a lot of sense to correct the GPIO case in one separate patch.
> - ATMEL_US_USMODE_HWHS is now unset for platform with PDC
>
> Changes since v3:
> - remove superfluous #include <linux/err.h> (thanks to Uwe)
> - rebase on next-20160930
>
> Changes since v2:
> - remove IS_ERR_OR_NULL() test in patch 1/3 as Uwe suggested.
> - fix typos in patch 2/3
> - rebase on next-20160927
> - simplify the logic in patch 3/3.
>
> Changes since v1:
> - Correct patch 1 with the error found by kbuild.
> - Add Alexandre's Acked-by on patch 2
> - Rewrite patch 3 logic in the light of the on-going discussion
> with Cyrille and Alexandre.
>
The changelog has to go after the --- marker.
> * the list may not be exhaustive
>
> Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
> ---
> drivers/tty/serial/atmel_serial.c | 25 +++++++++++++++++++++----
> 1 file changed, 21 insertions(+), 4 deletions(-)
>
> I think this should go in the stable tree since it fixes the flow
> control broken since v4.0.
> But It won't compile on versions before 4.9rc1 because:
> function atmel_use_fifo was introduced in 4.4.12 / 4.7
> variable atmel_port was introduced in 4.9rc1
>
> That's why I didn't add the Cc stable in the email body.
>
>
> diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
> index fd8aa1f4ba78..2c7c45904ba7 100644
> --- a/drivers/tty/serial/atmel_serial.c
> +++ b/drivers/tty/serial/atmel_serial.c
> @@ -2132,11 +2132,28 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
> mode |= ATMEL_US_USMODE_RS485;
> } else if (termios->c_cflag & CRTSCTS) {
> /* RS232 with hardware handshake (RTS/CTS) */
> - if (atmel_use_dma_rx(port) && !atmel_use_fifo(port)) {
> - dev_info(port->dev, "not enabling hardware flow control because DMA is used");
> - termios->c_cflag &= ~CRTSCTS;
> - } else {
> + if (atmel_use_fifo(port) &&
> + !mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS)) {
> + /*
> + * with ATMEL_US_USMODE_HWHS set, the controller will
> + * be able to drive the RTS pin high/low when the RX
> + * FIFO is above RXFTHRES/below RXFTHRES2.
> + * It will also disable the transmitter when the CTS
> + * pin is high.
> + * This mode is not activated if CTS pin is a GPIO
> + * because in this case, the transmitter is always
> + * disabled.
> + * If the RTS pin is a GPIO, the controller won't be
> + * able to drive it according to the FIFO thresholds,
> + * but it will be handled by the driver.
> + */
> mode |= ATMEL_US_USMODE_HWHS;
> + } else {
> + /*
> + * For platforms without FIFO, the flow control is
> + * handled by the driver.
> + */
> + mode |= ATMEL_US_USMODE_NORMAL;
> }
> } else {
> /* RS232 without hadware handshake */
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* [PATCH/RFT v2 11/17] USB: OHCI: make ohci-da8xx a separate driver
From: Axel Haslam @ 2016-10-25 16:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <21019d11-3b01-3e0e-60bc-c4873cf20237@lechnology.com>
On Tue, Oct 25, 2016 at 6:12 PM, David Lechner <david@lechnology.com> wrote:
> On 10/25/2016 02:39 AM, Axel Haslam wrote:
>>
>> On Tue, Oct 25, 2016 at 2:38 AM, David Lechner <david@lechnology.com>
>> wrote:
>>>
>>> On 10/24/2016 11:46 AM, ahaslam at baylibre.com wrote:
>>>>
>>>>
>>>> -#ifndef CONFIG_ARCH_DAVINCI_DA8XX
>>>> -#error "This file is DA8xx bus glue. Define
>>>> CONFIG_ARCH_DAVINCI_DA8XX."
>>>> -#endif
>>>> +#include "ohci.h"
>>>> +
>>>> +#define DRIVER_DESC "OHCI DA8XX driver"
>>>> +
>>>> +static const char hcd_name[] = "ohci-da8xx";
>>>
>>>
>>>
>>> why static const char instead of #define? This is only used one time in a
>>> pr_info, so it seems kind of pointless anyway.
>>
>>
>> Other drivers are using static const for the same variable.
>> i think static const is preferred over #define because #define doet give a
>> type.
>> If you dont mind ill keep it static const.
>>
>
> If this string was used in this file more than one place, I would agree with
> you, but currently it is only used as the argument of a pr_info(). The
> string "ohci-da8xx" could just be included in the fmt string instead of
> using "%s".
I think the purpose was to use it in the .name of the platform_driver
structure, too. only that not everybody is doing that, i looked at some bad
examples :(
would you agree to keep it if we use it in .name too?
-Axel
>
^ permalink raw reply
* [PATCH] ARM: DT: stm32: move dma translation to board files
From: Alexandre TORGUE @ 2016-10-25 16:16 UTC (permalink / raw)
To: linux-arm-kernel
stm32f469-disco and stm32f429-eval boards use SDRAM start address remapping
(to @0) to boost performances. A DMA translation through "dma-ranges"
property was needed for other masters than the M4 CPU.
stm32f429-disco doesn't use remapping so doesn't need this DMA translation.
This patches moves this DMA translation definition from stm32f429 soc file
to board files.
Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com>
diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index 13c7cd2..a763c15 100644
--- a/arch/arm/boot/dts/stm32429i-eval.dts
+++ b/arch/arm/boot/dts/stm32429i-eval.dts
@@ -82,6 +82,10 @@
};
};
+ soc {
+ dma-ranges = <0xc0000000 0x0 0x10000000>;
+ };
+
usbotg_hs_phy: usbphy {
#phy-cells = <0>;
compatible = "usb-nop-xceiv";
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 0596d60..3a1cfdd 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -59,8 +59,6 @@
};
soc {
- dma-ranges = <0xc0000000 0x0 0x10000000>;
-
timer2: timer at 40000000 {
compatible = "st,stm32-timer";
reg = <0x40000000 0x400>;
diff --git a/arch/arm/boot/dts/stm32f469-disco.dts b/arch/arm/boot/dts/stm32f469-disco.dts
index 9e73656..c2213c0 100644
--- a/arch/arm/boot/dts/stm32f469-disco.dts
+++ b/arch/arm/boot/dts/stm32f469-disco.dts
@@ -64,6 +64,10 @@
aliases {
serial0 = &usart3;
};
+
+ soc {
+ dma-ranges = <0xc0000000 0x0 0x10000000>;
+ };
};
&clk_hse {
--
1.9.1
^ permalink raw reply related
* [PATCH/RFT v2 11/17] USB: OHCI: make ohci-da8xx a separate driver
From: David Lechner @ 2016-10-25 16:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKXjFTPvEXPRyq0HZiF7mcHWFaFoH7MjFqEKnTyuSCUOB=U7Aw@mail.gmail.com>
On 10/25/2016 02:39 AM, Axel Haslam wrote:
> On Tue, Oct 25, 2016 at 2:38 AM, David Lechner <david@lechnology.com> wrote:
>> On 10/24/2016 11:46 AM, ahaslam at baylibre.com wrote:
>>>
>>> -#ifndef CONFIG_ARCH_DAVINCI_DA8XX
>>> -#error "This file is DA8xx bus glue. Define CONFIG_ARCH_DAVINCI_DA8XX."
>>> -#endif
>>> +#include "ohci.h"
>>> +
>>> +#define DRIVER_DESC "OHCI DA8XX driver"
>>> +
>>> +static const char hcd_name[] = "ohci-da8xx";
>>
>>
>> why static const char instead of #define? This is only used one time in a
>> pr_info, so it seems kind of pointless anyway.
>
> Other drivers are using static const for the same variable.
> i think static const is preferred over #define because #define doet give a type.
> If you dont mind ill keep it static const.
>
If this string was used in this file more than one place, I would agree
with you, but currently it is only used as the argument of a pr_info().
The string "ohci-da8xx" could just be included in the fmt string instead
of using "%s".
^ permalink raw reply
* [PATCH] tty/serial: at91: fix hardware handshake on Atmel platforms
From: Richard Genoud @ 2016-10-25 16:11 UTC (permalink / raw)
To: linux-arm-kernel
commit 1cf6e8fc8341 ("tty/serial: at91: fix RTS line management when
hardware handshake is enabled"), despite its title, broke hardware
handshake on *every* Atmel platforms.
The only one partially working is the SAMA5D2.
To understand why, one has to understand the flag ATMEL_US_USMODE_HWHS
first:
Before commit 1cf6e8fc8341 ("tty/serial: at91: fix RTS line management
when hardware handshake is enabled"), this flag was never set.
Thus, the CTS/RTS where only handled by serial_core (and everything
worked just fine).
This commit introduced the use of the ATMEL_US_USMODE_HWHS flag,
enabling it for all boards when the user space enables flow control.
When the ATMEL_US_USMODE_HWHS is set, the Atmel USART controller
handles a part of the flow control job:
- disable the transmitter when the CTS pin gets high.
- drive the RTS pin high when the DMA buffer transfer is completed or
PDC RX buffer full or RX FIFO is beyond threshold. (depending on the
controller version).
NB: This feature is *not* mandatory for the flow control to work.
Now, the specifics of the ATMEL_US_USMODE_HWHS flag:
- For platforms with DMAC and no FIFOs (sam9x25, sam9x35, sama5D3,
sama5D4, sam9g15, sam9g25, sam9g35)* this feature simply doesn't work.
( source: https://lkml.org/lkml/2016/9/7/598 )
Tested it on sam9g35, the RTS pins always stays up, even when RXEN=1
or a new DMA transfer descriptor is set.
=> ATMEL_US_USMODE_HWHS should not be used for those platforms
- For platforms with a PDC (sam926{0,1,3}, sam9g10, sam9g20, sam9g45,
sam9g46)*, there's another kind of problem. Once the flag
ATMEL_US_USMODE_HWHS is set, the RTS pin can't be driven anymore via
RTSEN/RTSDIS in USART Control Register. The RTS pin can only be driven
by enabling/disabling the receiver or setting RCR=RNCR=0 in the PDC
(Receive (Next) Counter Register).
=> Doing this is beyond the scope of this patch and could add other
bugs, so the original (and working) behaviour should be set for those
platforms (meaning ATMEL_US_USMODE_HWHS flag should be unset).
- For platforms with a FIFO (sama5d2)*, the RTS pin is driven according
to the RX FIFO thresholds, and can be also driven by RTSEN/RTSDIS in
USART Control Register. No problem here.
(This was the use case of commit 1cf6e8fc8341 ("tty/serial: at91: fix
RTS line management when hardware handshake is enabled"))
NB: If the CTS pin declared as a GPIO in the DTS, (for instance
cts-gpios = <&pioA PIN_PB31 GPIO_ACTIVE_LOW>), the transmitter will be
disabled.
=> ATMEL_US_USMODE_HWHS flag can be set for this platform ONLY IF the
CTS pin is not a GPIO.
So, the only case when ATMEL_US_USMODE_HWHS can be enabled is when
(atmel_use_fifo(port) &&
!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS))
Tested on all Atmel USART controller flavours:
AT91SAM9G35-CM, AT91SAM9G20-EK and SAMA5D2xplained
^^^^ ^^^^ ^^^^
(DMAC flavour), (PDC flavour) and (FIFO flavour)
Changes since v4:
- the mctrl_gpio_use_rtscts() is gone since it was atmel_serial
specific. (so patch 1 is gone)
- patches 2 and 3 have been merged together since it didn't make
a lot of sense to correct the GPIO case in one separate patch.
- ATMEL_US_USMODE_HWHS is now unset for platform with PDC
Changes since v3:
- remove superfluous #include <linux/err.h> (thanks to Uwe)
- rebase on next-20160930
Changes since v2:
- remove IS_ERR_OR_NULL() test in patch 1/3 as Uwe suggested.
- fix typos in patch 2/3
- rebase on next-20160927
- simplify the logic in patch 3/3.
Changes since v1:
- Correct patch 1 with the error found by kbuild.
- Add Alexandre's Acked-by on patch 2
- Rewrite patch 3 logic in the light of the on-going discussion
with Cyrille and Alexandre.
* the list may not be exhaustive
Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
---
drivers/tty/serial/atmel_serial.c | 25 +++++++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
I think this should go in the stable tree since it fixes the flow
control broken since v4.0.
But It won't compile on versions before 4.9rc1 because:
function atmel_use_fifo was introduced in 4.4.12 / 4.7
variable atmel_port was introduced in 4.9rc1
That's why I didn't add the Cc stable in the email body.
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index fd8aa1f4ba78..2c7c45904ba7 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -2132,11 +2132,28 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
mode |= ATMEL_US_USMODE_RS485;
} else if (termios->c_cflag & CRTSCTS) {
/* RS232 with hardware handshake (RTS/CTS) */
- if (atmel_use_dma_rx(port) && !atmel_use_fifo(port)) {
- dev_info(port->dev, "not enabling hardware flow control because DMA is used");
- termios->c_cflag &= ~CRTSCTS;
- } else {
+ if (atmel_use_fifo(port) &&
+ !mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS)) {
+ /*
+ * with ATMEL_US_USMODE_HWHS set, the controller will
+ * be able to drive the RTS pin high/low when the RX
+ * FIFO is above RXFTHRES/below RXFTHRES2.
+ * It will also disable the transmitter when the CTS
+ * pin is high.
+ * This mode is not activated if CTS pin is a GPIO
+ * because in this case, the transmitter is always
+ * disabled.
+ * If the RTS pin is a GPIO, the controller won't be
+ * able to drive it according to the FIFO thresholds,
+ * but it will be handled by the driver.
+ */
mode |= ATMEL_US_USMODE_HWHS;
+ } else {
+ /*
+ * For platforms without FIFO, the flow control is
+ * handled by the driver.
+ */
+ mode |= ATMEL_US_USMODE_NORMAL;
}
} else {
/* RS232 without hadware handshake */
^ permalink raw reply related
* [PATCH/RFT v2 07/17] ARM: davinci: da8xx: Enable the usb20 "per" clk on phy_clk_enable
From: David Lechner @ 2016-10-25 16:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <833b6b60-4504-dac7-ccbb-bc08c985ad01@ti.com>
On 10/25/2016 05:12 AM, Sekhar Nori wrote:
> On Monday 24 October 2016 10:16 PM, ahaslam at baylibre.com wrote:
>> diff --git a/arch/arm/mach-davinci/usb-da8xx.c b/arch/arm/mach-davinci/usb-da8xx.c
>> index 9e41a7f..982e105 100644
>> --- a/arch/arm/mach-davinci/usb-da8xx.c
>> +++ b/arch/arm/mach-davinci/usb-da8xx.c
>> @@ -53,11 +53,19 @@ int __init da8xx_register_usb_refclkin(int rate)
>>
>> static void usb20_phy_clk_enable(struct clk *clk)
>> {
>> + struct clk *usb20_clk;
>> u32 val;
>> u32 timeout = 500000; /* 500 msec */
>>
>> val = readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG));
>>
>> + usb20_clk = clk_get(NULL, "usb20");
>
> We should not be using a NULL device pointer here. Can you pass the musb
> device pointer available in the same file? Also, da850_clks[] in da850.c
> needs to be fixed to add the matching device name.
This clock can be used for usb 1.1 PHY even when musb is not being used,
so I don't think we can depend on having a musb device here.
Also, in a previous review, it was decided that the usb clocks should
*not* be added to da850_clks[] [1]. Instead, they are dynamically
registered elsewhere.
[1]: http://www.gossamer-threads.com/lists/linux/kernel/2396533
>
>> + if (IS_ERR(usb20_clk)) {
>> + pr_err("could not get usb20 clk\n");
>> + return;
>> + }
>
> Thanks,
> Sekhar
>
^ permalink raw reply
* [PATCH 3/4] dt-bindings: Update domain-idle-state binding to use correct compatibles
From: Sudeep Holla @ 2016-10-25 15:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477409199-52182-4-git-send-email-lina.iyer@linaro.org>
On 25/10/16 16:26, Lina Iyer wrote:
> Update domain-idle-state binding to use "domain-idle-state" compatible
> from Documentation/devicetree/bindings/arm/idle-states.txt.
>
> Cc: <devicetree@vger.kernel.org>
> Cc: Rob Herring <robh@kernel.org>
> Suggested-by: Sudeep Holla <sudeep.holla@arm.com>
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
> ---
> Documentation/devicetree/bindings/power/power_domain.txt | 9 +++++----
> 1 file changed, 5 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
> index e165036..6fb53a3 100644
> --- a/Documentation/devicetree/bindings/power/power_domain.txt
> +++ b/Documentation/devicetree/bindings/power/power_domain.txt
> @@ -30,8 +30,9 @@ Optional properties:
> available in the next section.
>
> - domain-idle-states : A phandle of an idle-state that shall be soaked into a
> - generic domain power state. The idle state definitions are
> - compatible with arm,idle-state specified in [1].
> + generic domain power state. The idle state definitions must be
> + compatible with "domain-idle-state"
I would reword the below a bit different so that it's flexible to be
reused without "arm,idle-state".
> as well as
> + "arm,idle-state" as defined in [1].
'Idle states that are "arm,idle-state" compatible are generally
"domain-idle-state" compatible as well if it's a PM domain.'
or something like that in line with what's in patch 2/4.
That would give us the scope of reuse of "domain-idle-state" in device
for future. Also it aligns with your patch 4/4.
Otherwise, it looks good.
--
Regards,
Sudeep
^ permalink raw reply
* [PATCH] ARM: socfpga_defconfig: Enable HIGHMEM
From: dinguyen at opensource.altera.com @ 2016-10-25 15:59 UTC (permalink / raw)
To: linux-arm-kernel
From: Dinh Nguyen <dinguyen@opensource.altera.com>
All of the SoCFPGA boards have at least 1GB of RAM, so enabling HIGHMEM
is necessary to avoid the following warning:
[ 0.000000] Truncating RAM at 0x00000000-0x40000000 to -0x30000000
[ 0.000000] Consider using a HIGHMEM enabled kernel.
Signed-off-by: Dinh Nguyen <dinguyen@opensource.altera.com>
---
arch/arm/configs/socfpga_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig
index 4660506..f5b9bc5 100644
--- a/arch/arm/configs/socfpga_defconfig
+++ b/arch/arm/configs/socfpga_defconfig
@@ -25,6 +25,7 @@ CONFIG_PCIE_ALTERA_MSI=y
CONFIG_SMP=y
CONFIG_NR_CPUS=2
CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_VFP=y
--
1.7.9.5
^ permalink raw reply related
* [PATCH/RFT v2 02/17] ARM: davinci: da8xx: Add CFGCHIP syscon platform declaration.
From: David Lechner @ 2016-10-25 15:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <758f206c-d0bd-6f70-da1e-42c88d6dd1f0@ti.com>
Hi Sekhar,
On 10/25/2016 05:17 AM, Sekhar Nori wrote:
> On Tuesday 25 October 2016 03:07 PM, Axel Haslam wrote:
>> Hi Sekar,
>>
>> On Tue, Oct 25, 2016 at 10:10 AM, Sekhar Nori <nsekhar@ti.com> wrote:
>>> On Monday 24 October 2016 10:16 PM, ahaslam at baylibre.com wrote:
>>>> From: David Lechner <david@lechnology.com>
>>>>
>>>> The CFGCHIP registers are used by a number of devices, so using a syscon
>>>> device to share them. The first consumer of this will by the phy-da8xx-usb
>>>> driver.
>>>>
>>>> Signed-off-by: David Lechner <david@lechnology.com>
>>>> [Axel: minor fix: change id to -1]
>>>
>>> Can you please clarify this change? There could be other syscon devices
>>> on the chip for other common registers. Why use the singular device-id?
>>>
>>
>> in the case of non DT boot, the phy driver is looking for "syscon" :
>>
>> d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon");
>>
>> if we register the syscon driver with id = 0, the actual name of the syscon
>> device will be "syscon.0" and the phy driver will fail to probe, because
>> the strncmp match in the syscon driver (syscon_match_pdevname)
>> will fail.
>>
>> should i change the phy driver instead?
>
> Yes, please. Forcing only one syscon region for the whole chip will be
> too restrictive, I am pretty sure.
>
> Thanks,
> Sekhar
>
In the previous review, you requested that this be changed to -1 [1].
If we change it back to 0, it will also require reverting a patch to the
phy driver that has already been merged[2].
[1]: http://www.gossamer-threads.com/lists/linux/kernel/2435807?page=last
[2]: http://www.gossamer-threads.com/lists/linux/kernel/2518804
^ permalink raw reply
* [PATCH] drivers: mfd: ti_am335x_tscadc: increase ADC ref clock to 24MHz
From: John Syne @ 2016-10-25 15:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161025063847.GD8574@dell>
> On Oct 24, 2016, at 11:38 PM, Lee Jones <lee.jones@linaro.org> wrote:
>
> On Mon, 24 Oct 2016, John Syne wrote:
>>> On Oct 24, 2016, at 11:01 PM, John Syne <john3909@gmail.com> wrote:
>>>> On Oct 24, 2016, at 10:52 PM, Mugunthan V N <mugunthanvnm@ti.com> wrote:
>>>>
>>>> On Tuesday 25 October 2016 02:28 AM, John Syne wrote:
>>>>>>> On Oct 23, 2016, at 11:02 PM, Mugunthan V N <mugunthanvnm@ti.com> wrote:
>>>>>>>
>>>>>>> Increase ADC reference clock from 3MHz to 24MHz so that the
>>>>>>> sampling rates goes up from 100K samples per second to 800K
>>>>>>> samples per second on AM335x and AM437x SoC.
>>>>>>>
>>>>>>> Also increase opendelay for touchscreen configuration to
>>>>>>> equalize the increase in ADC reference clock frequency,
>>>>>>> which results in the same amount touch events reported via
>>>>>>> evtest on AM335x GP EVM.
>>>>>>>
>>>>>>> Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
>>>>>>> ---
>>>>>>>
>>>>>>> This patch depends on ADC DMA patch series [1]
>>>>>>>
>>>>>>> Without DMA support, when ADC ref clock is set at 24MHz, I am
>>>>>>> seeing fifo overflow as CPU is not able to pull the ADC samples.
>>>>>>> This answers that DMA support is must for ADC to consume the
>>>>>>> samples generated at 24MHz with no open, step delay or
>>>>>>> averaging with patch [2].
>>>>>>>
>>>>>>> Measured the performance with the iio_generic_buffer with the
>>>>>>> patch [3] applied
>>>>>>>
>>>>>>> [1] - http://www.spinics.net/lists/devicetree/msg145045.html
>>>>>>> [2] - http://pastebin.ubuntu.com/23357935/
>>>>>>> [3] - http://pastebin.ubuntu.com/23357939/
>>>>>>>
>>>>>>> ---
>>>>>>> include/linux/mfd/ti_am335x_tscadc.h | 4 ++--
>>>>>>> 1 file changed, 2 insertions(+), 2 deletions(-)
>>>>>>>
>>>>>>> diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
>>>>>>> index b9a53e0..96c4207 100644
>>>>>>> --- a/include/linux/mfd/ti_am335x_tscadc.h
>>>>>>> +++ b/include/linux/mfd/ti_am335x_tscadc.h
>>>>>>> @@ -90,7 +90,7 @@
>>>>>>> /* Delay register */
>>>>>>> #define STEPDELAY_OPEN_MASK (0x3FFFF << 0)
>>>>>>> #define STEPDELAY_OPEN(val) ((val) << 0)
>>>>>>> -#define STEPCONFIG_OPENDLY STEPDELAY_OPEN(0x098)
>>>>> Wouldn?t this be better to add this to the devicetree?
>>>>>
>>>>> ti,chan-step-avg = <0x16 0x16 0x16 0x16 0x16 0x16 0x16>;
>>>>> ti,chan-step-opendelay = <0x500 0x500 0x500 0x500 0x500 0x500 0x500>;
>>>>> ti,chan-step-sampledelay = <0x0 0x0 0x0 0x0 0x0 0x0 0x0>;
>>>>
>>>> For a touch screen, there is not need to change in these parameter
>>>> settings, so my opinion is to keep it as is. Or am I missing something?
>>> I was thinking that if you are using this driver as an ADC, you may want the flexibility to make these changes in the DT. I?m doing this by connecting sensors to the ADC inputs. I?m not using this driver for a touchscreen.
>>
>> Here is a DT overlay were this gets using on the BeagleBoneBlack.
>>
>> https://github.com/RobertCNelson/bb.org-overlays/blob/master/src/arm/BB-ADC-00A0.dts
>>
>> Besides, these DT features are already implemented in the driver so it is just a matter of adding these entries to the am33xx.dtsi & am4372.dtsi, which you modified in this patch series.
>
> This looks like configuration, no?
>
> DT should be used to describe the hardware.
You may be right, but how is this different to setting the baud rate on a serial channel or sampling rate on a audio channel? Looking through the DT, there are many configuration settings, so I?m not sure what is the correct way to handle this. Surely it is better to handle this in DT vs hard coding these settings?
Regards,
John
>
> --
> Lee Jones
> Linaro STMicroelectronics Landing Team Lead
> Linaro.org ? Open source software for ARM SoCs
> Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [PATCH] drivers: iommu: constify iommu_gather_ops structures
From: Julia Lawall @ 2016-10-25 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477409624-30949-1-git-send-email-bhumirks@gmail.com>
On Tue, 25 Oct 2016, Bhumika Goyal wrote:
> Check for iommu_gather_ops structures that are only stored in the tlb
> field of an io_pgtable_cfg structure. The tlb field is of type
> const struct iommu_gather_ops *, so iommu_gather_ops structures
> having this property can be declared as const.
> Done using Coccinelle:
>
> @r1 disable optional_qualifier @
> identifier i;
> position p;
> @@
> static struct iommu_gather_ops i at p = {...};
>
> @ok1@
> identifier r1.i;
> position p;
> struct io_pgtable_cfg q;
> @@
> q.tlb=&i at p;
>
> @bad@
> position p!={r1.p,ok1.p};
> identifier r1.i;
> @@
> i at p
>
> @depends on !bad disable optional_qualifier@
> identifier r1.i;
> @@
> static
> +const
> struct iommu_gather_ops i={...};
>
> @depends on !bad disable optional_qualifier@
> identifier r1.i;
> @@
> +const
> struct iommu_gather_ops i;
>
> Signed-off-by: Bhumika Goyal <bhumirks@gmail.com>
Acked-by: Julia Lawall <julia.lawall@lip6.fr>
> ---
> drivers/iommu/io-pgtable-arm-v7s.c | 2 +-
> drivers/iommu/ipmmu-vmsa.c | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c
> index f50e51c..1465bbc 100644
> --- a/drivers/iommu/io-pgtable-arm-v7s.c
> +++ b/drivers/iommu/io-pgtable-arm-v7s.c
> @@ -741,7 +741,7 @@ static void dummy_tlb_sync(void *cookie)
> WARN_ON(cookie != cfg_cookie);
> }
>
> -static struct iommu_gather_ops dummy_tlb_ops = {
> +static const struct iommu_gather_ops dummy_tlb_ops = {
> .tlb_flush_all = dummy_tlb_flush_all,
> .tlb_add_flush = dummy_tlb_add_flush,
> .tlb_sync = dummy_tlb_sync,
> diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
> index ace331d..b8bcf18 100644
> --- a/drivers/iommu/ipmmu-vmsa.c
> +++ b/drivers/iommu/ipmmu-vmsa.c
> @@ -283,7 +283,7 @@ static void ipmmu_tlb_add_flush(unsigned long iova, size_t size,
> /* The hardware doesn't support selective TLB flush. */
> }
>
> -static struct iommu_gather_ops ipmmu_gather_ops = {
> +static const struct iommu_gather_ops ipmmu_gather_ops = {
> .tlb_flush_all = ipmmu_tlb_flush_all,
> .tlb_add_flush = ipmmu_tlb_add_flush,
> .tlb_sync = ipmmu_tlb_flush_all,
> --
> 1.9.1
>
>
^ permalink raw reply
* [PATCH v3] drivers: psci: PSCI checker module
From: Lorenzo Pieralisi @ 2016-10-25 15:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161020145115.6326-1-kevin.brodsky@arm.com>
[ +Paul, James ]
Left most of the patch content in place so that they can follow it even
if they weren't CC'ed.
On Thu, Oct 20, 2016 at 03:51:15PM +0100, Kevin Brodsky wrote:
> On arm and arm64, PSCI is one of the possible firmware interfaces
> used for power management. This includes both turning CPUs on and off,
> and suspending them (entering idle states).
>
> This patch adds a PSCI checker module that enables basic testing of
> PSCI operations during startup. There are two main tests: CPU
> hotplugging and suspending.
>
> In the hotplug tests, the hotplug API is used to turn off and on again
> all CPUs in the system, and then all CPUs in each cluster, checking
> the consistency of the return codes.
>
> In the suspend tests, a high-priority thread is created on each core
> and uses low-level cpuidle functionalities to enter suspend, in all
> the possible states and multiple times. This should allow a maximum
> number of CPUs to enter the same sleep state at the same or slightly
> different time.
>
> In essence, the suspend tests use a principle similar to that of the
> intel_powerclamp driver (drivers/thermal/intel_powerclamp.c), but the
> threads are only kept for the duration of the test (they are already
> gone when userspace is started).
>
> While in theory power management PSCI functions (CPU_{ON,OFF,SUSPEND})
> could be directly called, this proved too difficult as it would imply
> the duplication of all the logic used by the kernel to allow for a
> clean shutdown/bringup/suspend of the CPU (the deepest sleep states
> implying potentially the shutdown of the CPU).
>
> Note that this file cannot be compiled as a loadable module, since it
> uses a number of non-exported identifiers (essentially for
> PSCI-specific checks and direct use of cpuidle) and relies on the
> absence of userspace to avoid races when calling hotplug and cpuidle
> functions.
>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Kevin Hilman <khilman@kernel.org>
> Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Sudeep Holla <sudeep.holla@arm.com>
> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Signed-off-by: Kevin Brodsky <kevin.brodsky@arm.com>
> ---
[...]
> +static int find_clusters(const struct cpumask *cpus,
> + const struct cpumask **clusters)
> +{
> + unsigned int nb = 0;
> + cpumask_var_t tmp;
> +
> + if (!alloc_cpumask_var(&tmp, GFP_KERNEL))
> + return -ENOMEM;
> + cpumask_copy(tmp, cpus);
> +
> + while (!cpumask_empty(tmp)) {
> + const struct cpumask *cluster =
> + topology_core_cpumask(cpumask_any(tmp));
> +
> + clusters[nb++] = cluster;
> + cpumask_andnot(tmp, tmp, cluster);
> + }
> +
> + free_cpumask_var(tmp);
> + return nb;
> +}
> +
> +/*
> + * offlined_cpus is a temporary array but passing it as an argument avoids
> + * multiple allocations.
> + */
> +static unsigned int down_and_up_cpus(const struct cpumask *cpus,
> + struct cpumask *offlined_cpus)
> +{
> + int cpu;
> + int err = 0;
> +
> + cpumask_clear(offlined_cpus);
> +
> + /* Try to power down all CPUs in the mask. */
> + for_each_cpu(cpu, cpus) {
> + int ret = cpu_down(cpu);
> +
> + /*
> + * cpu_down() checks the number of online CPUs before the TOS
> + * resident CPU.
> + */
> + if (cpumask_weight(offlined_cpus) + 1 == nb_available_cpus) {
> + if (ret != -EBUSY) {
> + pr_err("Unexpected return code %d while trying "
> + "to power down last online CPU %d\n",
> + ret, cpu);
> + ++err;
> + }
> + } else if (cpu == tos_resident_cpu) {
> + if (ret != -EPERM) {
> + pr_err("Unexpected return code %d while trying "
> + "to power down TOS resident CPU %d\n",
> + ret, cpu);
> + ++err;
> + }
> + } else if (ret != 0) {
> + pr_err("Error occurred (%d) while trying "
> + "to power down CPU %d\n", ret, cpu);
> + ++err;
> + }
> +
> + if (ret == 0)
> + cpumask_set_cpu(cpu, offlined_cpus);
> + }
> +
> + /* Try to power up all the CPUs that have been offlined. */
> + for_each_cpu(cpu, offlined_cpus) {
> + int ret = cpu_up(cpu);
> +
> + if (ret != 0) {
> + pr_err("Error occurred (%d) while trying "
> + "to power up CPU %d\n", ret, cpu);
> + ++err;
> + } else {
> + cpumask_clear_cpu(cpu, offlined_cpus);
> + }
> + }
> +
> + /*
> + * Something went bad at some point and some CPUs could not be turned
> + * back on.
> + */
> + WARN_ON(!cpumask_empty(offlined_cpus) ||
> + num_online_cpus() != nb_available_cpus);
> +
> + return err;
> +}
> +
> +static int hotplug_tests(void)
> +{
> + int err;
> + cpumask_var_t offlined_cpus;
> + int i, nb_cluster;
> + const struct cpumask **clusters;
> + char *page_buf;
> +
> + err = -ENOMEM;
> + if (!alloc_cpumask_var(&offlined_cpus, GFP_KERNEL))
> + return err;
> + /* We may have up to nb_available_cpus clusters. */
> + clusters = kmalloc_array(nb_available_cpus, sizeof(*clusters),
> + GFP_KERNEL);
> + if (!clusters)
> + goto out_free_cpus;
> + page_buf = (char *)__get_free_page(GFP_KERNEL);
> + if (!page_buf)
> + goto out_free_clusters;
> +
> + err = 0;
> + nb_cluster = find_clusters(cpu_online_mask, clusters);
> +
> + /*
> + * Of course the last CPU cannot be powered down and cpu_down() should
> + * refuse doing that.
> + */
> + pr_info("Trying to turn off and on again all CPUs\n");
> + err += down_and_up_cpus(cpu_online_mask, offlined_cpus);
> +
> + /*
> + * Take down CPUs by cluster this time. When the last CPU is turned
> + * off, the cluster itself should shut down.
> + */
> + for (i = 0; i < nb_cluster; ++i) {
> + int cluster_id =
> + topology_physical_package_id(cpumask_any(clusters[i]));
> + ssize_t len = cpumap_print_to_pagebuf(true, page_buf,
> + clusters[i]);
> + /* Remove trailing newline. */
> + page_buf[len - 1] = '\0';
> + pr_info("Trying to turn off and on again cluster %d "
> + "(CPUs %s)\n", cluster_id, page_buf);
> + err += down_and_up_cpus(clusters[i], offlined_cpus);
> + }
> +
> + free_page((unsigned long)page_buf);
> +out_free_clusters:
> + kfree(clusters);
> +out_free_cpus:
> + free_cpumask_var(offlined_cpus);
> + return err;
> +}
> +
> +static void dummy_callback(unsigned long ignored) {}
> +
> +static int suspend_cpu(int index, bool broadcast)
> +{
> + int ret;
> +
> + arch_cpu_idle_enter();
> +
> + if (broadcast) {
> + /*
> + * The local timer will be shut down, we need to enter tick
> + * broadcast.
> + */
> + ret = tick_broadcast_enter();
> + if (ret) {
> + /*
> + * In the absence of hardware broadcast mechanism,
> + * this CPU might be used to broadcast wakeups, which
> + * may be why entering tick broadcast has failed.
> + * There is little the kernel can do to work around
> + * that, so enter WFI instead (idle state 0).
> + */
> + cpu_do_idle();
> + ret = 0;
> + goto out_arch_exit;
> + }
> + }
> +
> + /*
> + * Replicate the common ARM cpuidle enter function
> + * (arm_enter_idle_state).
> + */
> + ret = CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, index);
> +
> + if (broadcast)
> + tick_broadcast_exit();
> +
> +out_arch_exit:
> + arch_cpu_idle_exit();
> +
> + return ret;
> +}
> +
> +static int suspend_test_thread(void *arg)
> +{
> + int cpu = (long)arg;
> + int i, nb_suspend = 0, nb_shallow_sleep = 0, nb_err = 0;
> + struct sched_param sched_priority = { .sched_priority = MAX_RT_PRIO-1 };
> + struct cpuidle_device *dev;
> + struct cpuidle_driver *drv;
> + /* No need for an actual callback, we just want to wake up the CPU. */
> + struct timer_list wakeup_timer =
> + TIMER_INITIALIZER(dummy_callback, 0, 0);
> +
> + /* Wait for the main thread to give the start signal. */
> + wait_for_completion(&suspend_threads_started);
> +
> + /* Set maximum priority to preempt all other threads on this CPU. */
> + if (sched_setscheduler_nocheck(current, SCHED_FIFO, &sched_priority))
> + pr_warn("Failed to set suspend thread scheduler on CPU %d\n",
> + cpu);
> +
> + dev = this_cpu_read(cpuidle_devices);
> + drv = cpuidle_get_cpu_driver(dev);
> +
> + pr_info("CPU %d entering suspend cycles, states 1 through %d\n",
> + cpu, drv->state_count - 1);
> +
> + for (i = 0; i < NUM_SUSPEND_CYCLE; ++i) {
> + int index;
> + /*
> + * Test all possible states, except 0 (which is usually WFI and
> + * doesn't use PSCI).
> + */
> + for (index = 1; index < drv->state_count; ++index) {
> + struct cpuidle_state *state = &drv->states[index];
> + bool broadcast = state->flags & CPUIDLE_FLAG_TIMER_STOP;
> + int ret;
> +
> + /*
> + * Set the timer to wake this CPU up in some time (which
> + * should be largely sufficient for entering suspend).
> + * If the local tick is disabled when entering suspend,
> + * suspend_cpu() takes care of switching to a broadcast
> + * tick, so the timer will still wake us up.
> + */
> + mod_timer(&wakeup_timer, jiffies +
> + usecs_to_jiffies(state->target_residency));
> +
> + /* IRQs must be disabled during suspend operations. */
> + local_irq_disable();
> +
> + ret = suspend_cpu(index, broadcast);
> +
> + /*
> + * We have woken up. Re-enable IRQs to handle any
> + * pending interrupt, do not wait until the end of the
> + * loop.
> + */
> + local_irq_enable();
> +
> + if (ret == index) {
> + ++nb_suspend;
> + } else if (ret >= 0) {
> + /* We did not enter the expected state. */
> + ++nb_shallow_sleep;
> + } else {
> + pr_err("Failed to suspend CPU %d: error %d "
> + "(requested state %d, cycle %d)\n",
> + cpu, ret, index, i);
> + ++nb_err;
> + }
> + }
> + }
> +
> + /*
> + * Disable the timer to make sure that the timer will not trigger
> + * later.
> + */
> + del_timer(&wakeup_timer);
> +
> + if (atomic_dec_return_relaxed(&nb_active_threads) == 0)
> + complete(&suspend_threads_done);
> +
> + /* Give up on RT scheduling and wait for termination. */
> + sched_priority.sched_priority = 0;
> + if (sched_setscheduler_nocheck(current, SCHED_NORMAL, &sched_priority))
> + pr_warn("Failed to set suspend thread scheduler on CPU %d\n",
> + cpu);
> + for (;;) {
> + /* Needs to be set first to avoid missing a wakeup. */
> + set_current_state(TASK_INTERRUPTIBLE);
> + if (kthread_should_stop()) {
> + __set_current_state(TASK_RUNNING);
> + break;
> + }
> + schedule();
> + }
> +
> + pr_info("CPU %d suspend test results: success %d, shallow states %d, errors %d\n",
> + cpu, nb_suspend, nb_shallow_sleep, nb_err);
> +
> + return nb_err;
> +}
> +
> +static int suspend_tests(void)
> +{
> + int i, cpu, err = 0;
> + struct task_struct **threads;
> + int nb_threads = 0;
> +
> + threads = kmalloc_array(nb_available_cpus, sizeof(*threads),
> + GFP_KERNEL);
> + if (!threads)
> + return -ENOMEM;
> +
> + for_each_online_cpu(cpu) {
> + struct task_struct *thread;
> + /* Check that cpuidle is available on that CPU. */
> + struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
> + struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
> +
> + if (cpuidle_not_available(drv, dev)) {
> + pr_warn("cpuidle not available on CPU %d, ignoring\n",
> + cpu);
> + continue;
> + }
> +
> + thread = kthread_create_on_cpu(suspend_test_thread,
> + (void *)(long)cpu, cpu,
> + "psci_suspend_test");
> + if (IS_ERR(thread))
> + pr_err("Failed to create kthread on CPU %d\n", cpu);
> + else
> + threads[nb_threads++] = thread;
> + }
> + if (nb_threads < 1) {
> + kfree(threads);
> + return -ENODEV;
> + }
> +
> + atomic_set(&nb_active_threads, nb_threads);
> +
> + /*
> + * Stop cpuidle to prevent the idle tasks from entering a deep sleep
> + * mode, as it might interfere with the suspend threads on other CPUs.
> + * This does not prevent the suspend threads from using cpuidle (only
> + * the idle tasks check this status).
> + */
> + cpuidle_pause();
> +
> + /*
> + * Wake up the suspend threads. To avoid the main thread being preempted
> + * before all the threads have been unparked, the suspend threads will
> + * wait for the completion of suspend_threads_started.
> + */
> + for (i = 0; i < nb_threads; ++i)
> + wake_up_process(threads[i]);
> + complete_all(&suspend_threads_started);
> +
> + wait_for_completion(&suspend_threads_done);
> +
> + cpuidle_resume();
> +
> + /* Stop and destroy all threads, get return status. */
> + for (i = 0; i < nb_threads; ++i)
> + err += kthread_stop(threads[i]);
> +
> + kfree(threads);
> + return err;
> +}
> +
> +static int __init psci_checker(void)
> +{
> + int ret;
> +
> + /*
> + * Since we're in an initcall, we assume that all the CPUs that all
> + * CPUs that can be onlined have been onlined.
> + *
> + * The tests assume that hotplug is enabled but nobody else is using it,
> + * otherwise the results will be unpredictable. However, since there
> + * is no userspace yet in initcalls, that should be fine.
I do not think it is. If you run a kernel with, say,
CONFIG_LOCK_TORTURE_TEST, cpus may disappear from the radar while
running the PSCI checker test itself; that at least would confuse the
checker, and that's just an example.
I added Paul to check what are the assumptions behind the torture test
hotplug tests, in particular if there are any implicit assumptions for
it to work (ie it is the only kernel test hotplugging cpus in and out
(?)), what I know is that the PSCI checker assumptions are not correct.
There is something simple you can do to get this "fixed".
You can use the new API James implemented for hibernate,
that allows you to disable (ie PSCI CPU OFF) all "secondary" cpus
other than the primary one passed in as parameter:
freeze_secondary_cpus(int primary);
that function will _cpu_down() all online cpus other than "primary"
in one go, without any interference allowed from other bits of the
kernel. It requires an enable_nonboot_cpus() counterpart, and you
can do that for every online cpus you detect (actually you can even
avoid using the online cpu mask and use the present mask to carry
out the test). If there is a resident trusted OS you can just
trigger the test with primary == tos_resident_cpu, since all
others are bound to fail (well, you can run them and check they
do fail, it is a checker after all).
You would lose the capability of hotplugging a "cluster" at a time, but
I do not think it is a big problem, the test above would cover it
and more importantly, it is safe to execute.
Or we can augment the torture test API to restrict the cpumask
it actually uses to offline/online cpus (I am referring to
torture_onoff(), kernel/torture.c).
Further comments appreciated since I am not sure I grokked the
assumption the torture tests make about hotplugging cpus in and out,
I will go through the commits logs again to find more info.
Thanks !
Lorenzo
> + */
> + nb_available_cpus = num_online_cpus();
> +
> + /* Check PSCI operations are set up and working. */
> + ret = psci_ops_check();
> + if (ret)
> + return ret;
> +
> + pr_info("PSCI checker started using %u CPUs\n", nb_available_cpus);
> +
> + pr_info("Starting hotplug tests\n");
> + ret = hotplug_tests();
> + if (ret == 0)
> + pr_info("Hotplug tests passed OK\n");
> + else if (ret > 0)
> + pr_err("%d error(s) encountered in hotplug tests\n", ret);
> + else {
> + pr_err("Out of memory\n");
> + return ret;
> + }
> +
> + pr_info("Starting suspend tests (%d cycles per state)\n",
> + NUM_SUSPEND_CYCLE);
> + ret = suspend_tests();
> + if (ret == 0)
> + pr_info("Suspend tests passed OK\n");
> + else if (ret > 0)
> + pr_err("%d error(s) encountered in suspend tests\n", ret);
> + else {
> + switch (ret) {
> + case -ENOMEM:
> + pr_err("Out of memory\n");
> + break;
> + case -ENODEV:
> + pr_warn("Could not start suspend tests on any CPU\n");
> + break;
> + }
> + }
> +
> + pr_info("PSCI checker completed\n");
> + return ret < 0 ? ret : 0;
> +}
> +late_initcall(psci_checker);
> --
> 2.10.0
>
^ permalink raw reply
* [PATCH 5/5] ARM: dts: Add LEGO MINDSTORTMS EV3 dts
From: David Lechner @ 2016-10-25 15:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <0b7cf7a1-e234-2868-dfde-347c92c23829@ti.com>
On 10/25/2016 05:58 AM, Sekhar Nori wrote:
> On Tuesday 25 October 2016 02:50 AM, David Lechner wrote:
>> On 10/24/2016 02:50 PM, David Lechner wrote:
>>> On 10/24/2016 10:50 AM, David Lechner wrote:
>>>> On 10/24/2016 06:58 AM, Sekhar Nori wrote:
>>>>> On Saturday 22 October 2016 12:06 AM, David Lechner wrote:
>>>>>
>>>>>> +&ehrpwm1 {
>>>>>> + status = "disabled";
>>>>>
>>>>> Hmm, disabled? Can you add this node when you actually use it?
>>>>
>>>> Not sure why I have this disabled. Like the gpios, the pwms can be used
>>>> via sysfs, so I would like to leave them.
>>>>
>>>
>>> Now I remember why these are disabled. The clock matching is broken.
>>> Only the first ehrpwm and the first ecap get clocks. The others fail.
>>>
>>> I can change these to "okay". It will just result in a kernel error
>>> message until the clocks are fixed.
>>>
>>
>> correction: it is not the clocks that are broken. it is the device names.
>>
>> In arch/arm/mach-davinci/da8xx-dt.c, we have...
>>
>>
>> OF_DEV_AUXDATA("ti,da850-ehrpwm", 0x01f00000, "ehrpwm", NULL),
>> OF_DEV_AUXDATA("ti,da850-ehrpwm", 0x01f02000, "ehrpwm", NULL),
>> OF_DEV_AUXDATA("ti,da850-ecap", 0x01f06000, "ecap", NULL),
>> OF_DEV_AUXDATA("ti,da850-ecap", 0x01f07000, "ecap", NULL),
>> OF_DEV_AUXDATA("ti,da850-ecap", 0x01f08000, "ecap", NULL),
>>
>>
>> Which causes each device to have the same device node name. This causes
>> sysfs errors because it is trying to register a second device at the
>> same sysfs path.
>>
>> If you change the names here, then the device do not work because the
>> clock lookup fails.
>
> Yeah, this is incorrect (I should have caught it in review). The device
> id should have been present in the lookup. Can you fix auxdata and clock
> lookup too and send a separate patch? Its probably a v4.9-rc candidate.
>
> Thanks,
> Sekhar
>
Yes, I can do this.
^ permalink raw reply
* [PATCH 3/5] staging/vchi: Fix some pointer math for 64-bit.
From: Eric Anholt @ 2016-10-25 15:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161025075515.GA29765@kroah.com>
Greg Kroah-Hartman <gregkh@linuxfoundation.org> writes:
> On Mon, Oct 17, 2016 at 12:44:04PM -0700, Eric Anholt wrote:
>> These were throwing warnings on aarch64, and all are trivially
>> converted to longs.
>>
>> Signed-off-by: Eric Anholt <eric@anholt.net>
>> ---
>> drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c | 6 +++---
>> drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c | 5 +++--
>> 2 files changed, 6 insertions(+), 5 deletions(-)
>
> This didnt apply anymore as I think I took the other fixups, sorry.
No problem, I'm happy to see Michael's stuff getting merged.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 800 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161025/36d2d692/attachment-0001.sig>
^ permalink raw reply
* [PATCH] drivers: mfd: ti_am335x_tscadc: increase ADC ref clock to 24MHz
From: John Syne @ 2016-10-25 15:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <7024bec3-2ebe-db82-12e2-6d6bcc664c6b@ti.com>
> On Oct 24, 2016, at 11:37 PM, Vignesh R <vigneshr@ti.com> wrote:
>
>
>
> On Tuesday 25 October 2016 11:46 AM, John Syne wrote:
>>
>>> On Oct 24, 2016, at 11:01 PM, John Syne <john3909@gmail.com> wrote:
>>>
>>>>
>>>> On Oct 24, 2016, at 10:52 PM, Mugunthan V N <mugunthanvnm@ti.com> wrote:
>>>>
> [...]
>>>>>>>
>>>>>>> ---
>>>>>>> include/linux/mfd/ti_am335x_tscadc.h | 4 ++--
>>>>>>> 1 file changed, 2 insertions(+), 2 deletions(-)
>>>>>>>
>>>>>>> diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
>>>>>>> index b9a53e0..96c4207 100644
>>>>>>> --- a/include/linux/mfd/ti_am335x_tscadc.h
>>>>>>> +++ b/include/linux/mfd/ti_am335x_tscadc.h
>>>>>>> @@ -90,7 +90,7 @@
>>>>>>> /* Delay register */
>>>>>>> #define STEPDELAY_OPEN_MASK (0x3FFFF << 0)
>>>>>>> #define STEPDELAY_OPEN(val) ((val) << 0)
>>>>>>> -#define STEPCONFIG_OPENDLY STEPDELAY_OPEN(0x098)
>>>>> Wouldn?t this be better to add this to the devicetree?
>>>>>
>>>>> ti,chan-step-avg = <0x16 0x16 0x16 0x16 0x16 0x16 0x16>;
>>>>> ti,chan-step-opendelay = <0x500 0x500 0x500 0x500 0x500 0x500 0x500>;
>>>>> ti,chan-step-sampledelay = <0x0 0x0 0x0 0x0 0x0 0x0 0x0>;
>>>>
>>>> For a touch screen, there is not need to change in these parameter
>>>> settings, so my opinion is to keep it as is. Or am I missing something?
>>> I was thinking that if you are using this driver as an ADC, you may want the flexibility to make these changes in the DT. I?m doing this by connecting sensors to the ADC inputs. I?m not using this driver for a touchscreen.
>>
>
> ti_am335x_adc driver already supports above DT parameters and its upto
> the user to adjust these parameters as required.
>
>> Here is a DT overlay were this gets using on the BeagleBoneBlack.
>>
>> https://github.com/RobertCNelson/bb.org-overlays/blob/master/src/arm/BB-ADC-00A0.dts
>>
>> Besides, these DT features are already implemented in the driver so it is just a matter of adding these entries to the am33xx.dtsi & am4372.dtsi, which you modified in this patch series.
>>
>
> Touchscreen driver (ti_am335x_tsc.c) does not support above DT parameters.
This patch series also modifies ti_am335x_adc.c
https://github.com/analogdevicesinc/linux/blob/master/drivers/iio/adc/ti_am335x_adc.c#L447
Regards,
John
>
> --
> Regards
> Vignesh
^ permalink raw reply
* [PATCH 3/3] ARM: dts: socfpga: Enable QSPI on the Cyclone5 sockit
From: Graham Moore @ 2016-10-25 15:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <c62baad6-2528-a9df-6b09-3dba66785102@opensource.altera.com>
On 10/20/2016 09:12 AM, Dinh Nguyen wrote:
>
>
> On 10/20/2016 02:19 AM, Steffen Trumtrar wrote:
>
>>> + cdns,tslch-ns = <4>;
>>> +
>>> + partition at qspi-boot {
>>> + /* 8MB for raw data. */
>>> + label = "Flash 0 Raw Data";
>>> + reg = <0x0 0x800000>;
>>> + };
>>> +
>>> + partition at qspi-rootfs {
>>> + /* 120MB for jffs2 data. */
>>> + label = "Flash 0 jffs2 Filesystem";
>>> + reg = <0x800000 0x7800000>;
>>> + };
>>> + };
>>> +};
>>> +
>>
>> What is the current preferred way of handling the partitions?
>> This doesn't fit my Sockit configuration for example. So I would always
>> have to patch the devicetree.
>
> I'm not 100% sure on this. Graham, do you have any insight?
>>
Well, strictly speaking, these partitions are only for the socdk, the
Altera dev kit. Our sample designs and file systems expect this layout.
Therefore, these partitions are not required for any other dev kits, and
can probably be left out.
Or, Steffen, if you have a standard layout you'd like to see, then put
that in there.
I think some people will have to patch the layout regardless.
-Graham
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox