Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 2/4] ARM: dts: exynos: Add SCU device node to exynos4.dtsi
From: Krzysztof Kozlowski @ 2016-11-05 15:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1478230764-13748-3-git-send-email-pankaj.dubey@samsung.com>

On Fri, Nov 04, 2016 at 09:09:22AM +0530, Pankaj Dubey wrote:
> Exynos4 like other Cortex-A9 SoC's has a Snoop Control Unit(SCU)
> and its SFR are used during SMP boot and S2R. Add SCU node to the device tree.
> 
> Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
> ---
>  arch/arm/boot/dts/exynos4.dtsi | 5 +++++
>  1 file changed, 5 insertions(+)
> 

Thanks, applied.

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v4] ARM: mmp: let clk_disable() return immediately if clk is NULL
From: Masahiro Yamada @ 2016-11-05 15:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474221526-21195-1-git-send-email-yamada.masahiro@socionext.com>

Hi Eric Miao, Haojian Zhuang,

Could you pick up this patch, please?


2016-09-19 2:58 GMT+09:00 Masahiro Yamada <yamada.masahiro@socionext.com>:
> In many of clk_disable() implementations, it is a no-op for a NULL
> pointer input, but this is one of the exceptions.
>
> Making it treewide consistent will allow clock consumers to call
> clk_disable() without NULL pointer check.
>
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> ---
>
> Changes in v4:
>   - Split into per-arch patches
>
> Changes in v3:
>   - Return only when clk is NULL.  Do not take care of error pointer.
>
> Changes in v2:
>   - Rebase on Linux 4.6-rc1
>
>  arch/arm/mach-mmp/clock.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/arch/arm/mach-mmp/clock.c b/arch/arm/mach-mmp/clock.c
> index ac6633d..28fe64c 100644
> --- a/arch/arm/mach-mmp/clock.c
> +++ b/arch/arm/mach-mmp/clock.c
> @@ -67,6 +67,9 @@ void clk_disable(struct clk *clk)
>  {
>         unsigned long flags;
>
> +       if (!clk)
> +               return;
> +
>         WARN_ON(clk->enabled == 0);
>
>         spin_lock_irqsave(&clocks_lock, flags);
> --
> 1.9.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



-- 
Best Regards
Masahiro Yamada

^ permalink raw reply

* [PATCH 02/10] iio: adc: Add stm32 support
From: Jonathan Cameron @ 2016-11-05 15:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4687bde5-1e1e-83bc-0aca-3224399f0fa2@st.com>

On 03/11/16 08:20, Fabrice Gasnier wrote:
> On 10/30/2016 04:27 PM, Jonathan Cameron wrote:
>> On 25/10/16 17:25, Fabrice Gasnier wrote:
>>> This patch adds support for STMicroelectronics STM32 MCU's analog to
>>> digital converter.
>>>
>>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
>> Hi Fabrice,
>>
>> Sometimes I hate SoC ADCs.  For some reason the hardware designers seem to
>> try and throw everything and the kitchen sink at them.  Discontinuous mode
>> as an example in this device.  Not seen that particular piece of fun before
>> and glad to see you haven't 'yet' tried to support it!
>>
>> Anyhow, the complexity of the hardware leads to an initially complex driver.
>> My first thought it that this would be easier to follow / review if we
>> built it up in smaller steps.   Perhaps ditch the injected channel support
>> entirely in the first instance.  I also wonder if you don't need to support
>> that whole thing (injected sampling) as another iio device entirely using the
>> same channels.  That's kind of what it is from a data flow point of view
>> (we've had arbitary sequencers before with priorities - don't think anyone
>> ever decided the pain was worth supporting the complexity, but right answer
>> has always been multiple IIO devices).
> Hi Jonathan,
> 
> First, many thanks for your review. I agree with you, most reasonable approach is to remove some
> complexity to ease the review. Regarding injected support, basically, bellow approach is to use
> separate IIO devices for regular and injected. But, I'll remove this, at least for now, in next patch set.
> 
>> You also have at least one layer of abstraction in here that serves no
>> current purpose.  Please clear that out for now. It'll make the code
>> shorter and easier to follow.  If/when other parts are introduced then
>> is the time to do that transistion to having the abstraction.
> 
> From your suggestion, this may end-up in a single driver file in drivers/iio.
> I think I'll try to keep simple routines like start, stop, conf_scan and so on, but
> remove indirection routines from stm32-adc.h file (e.g. stm32_adc_ops).
> Is it in line with your suggestions ?
Sure, some of those will want to be in their own functions so it sounds
about right.
> 
>>
>> My first thought on the double / tripple adc handling is that you'd be better
>> off handling them as 3 separate devices then doing some 'unusual' trigger
>> handling to support the weird sequencing.  Guessing you thought about that?
>> If so could you lay out your reasoning for the single driver instance approach.
>> I'm not arguing against it btw, merely want to understand your reasoning!
> 
> I mainly came up with a single driver instance approach because there are basically
> 3 identical ADC instances 'mapped' in a single IP with few common resources.
> I usually see mfd are more heterogeneous and declare cells for various subsystem drivers.
> But I can try to move to mfd as you're suggesting.
> I just hope this will not bring more complexity.
If anything I suspect it'll end up simpler to read (be it a tiny bit longer
in terms of lines of code).
> 
>>
>> It would be tricky given one set of channels are selectable over 3 devices
>> and there are constraints to enforce (not sampling same channel on two ADCs
>> at the same time) but not impossible...  Perhaps what you have here is
>> indeed simpler!
>>
>> Whilst it's been a nasty job to review, I'm guessing writing it was
>> much worse ;)  Pretty good starting point though might take a little while
>> to pin down the remaining questions on how best to handle this particular
>> monster.
> My apologies... I hope you didn't had much of a headache :-) by reading me.
> More questions bellow.
> 
>> Jonathan
>>> ---
>>>   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 @@
> [snip]
> 
>>> +
>>> +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;
>>> +    }
>> It's horrible but to end up in the 'obvious' state I'd disable the adc
>> again assuming that doesn't kill the stuff that is configured.
> I'll check this and try to come up with something.
> 
>>> +
>>> +    return 0;
>>> +
>>> +err_dis:
>>> +    stm32_adc_disable(adc);
>>> +
>>> +    return ret;
>>> +}
>>> +
> [snip]
>>> +/**
>>> + * 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);
>> This is known maximum length... I'd just avoid the complexity of allocating
>> it like this - a comment would do the job to say it is the right length.
> Do you suggest to use a predefined variable (like unsigned long scan_mask) directly ?
> And add a more basic test on 'masklength', to be sure ?
A test would make sense as would prevent any problems if this driver
is extended to support much larger devices in future.
> 
>>> +    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);
>> Put it back again afterwards?  Otherwise some nasty race conditions look
>> likely to me.. (userspace sets trigger and is about to enable the buffer
>> when along comes this code and changes it underneath).
> I'll fix this.
> 
>>> +    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
>> As this is kernel doc please document the parameter as well.  Otherwise
>> we'll get a pile of warnings!
> Sure.
>>> + */
>>> +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);
>> Want to do this in the non injected case? it's a noop isn't it?
> I'll rework this.
> 
>>
>>> +    status &= mask;
>>> +
>>> +    /* Regular data */
>>> +    if (status & reginfo->eoc) {
>> Hmm.. this is a little bit of 'missuse' of the standard trigger architecture
>> but as long as it's restricted to just this device I don't suppose we need
>> to care.  Only reason we need it is to provide control of 'which' hardware
>> trigger is being used.
>>
>> Guessing the DMA will almost always be turned on and will make this oddity
>> effectively disappear.
> I'm not sure to understand your remark. Above test checks end of conversion status flag.
> Or do you talk about bellow lines ? Can you please clarify ?
Took me  a while to figure this out. (i.e. I wasn't sure what I meant either!)

This setup corresponds (more or less) to having an external trigger fire
off a software based sequencer.  So we'd expect the 'loop' element of this
to run in the buffer handler rather than the trigger handler.  In theory
that would allow other triggers to be used as well as the ones supported
in hardware.

Here that is somewhat of a pain however.  If there weren't multiple triggers
to select between I'd just suggest dropping the trigger interface entirely
(it's optional) but then we'd have to do something custom to select
which of the device supplied triggers to use.

Hence probably best plan is leave it as it is.  Sometimes hardware
just doesn't fit the conceptual model we have for it!
> 
>>> +        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);
>> Hmm.. ret |= is rather fragile.  Preferable to make the handling of NONE
>> vs IRQ_HANDLED explicit.
>>
>> If you were to split the driver up as I suggested might make sense above,
>> then this would be done with an irq chip in a top level device (effectively
>> a very simple mfd).
> I'll look into it.
> 
>>> +
>>> +    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)
>> I'm glad you kept this relatively simple compared to some of the
>> 'fun' the hardware is capable of. Very wise!
>>> +{
>>> +    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);
>> This is a surprise as postdisbale should balance preenable...
>> Ah, you have update scan mode enabling the adc.  If you can balance it
>> better by moving that to preenable please do as it is more 'obviously' correct.
> I'll try to rework this.
>>> +
>>> +    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);
>> I'd be more cynical.  It's not that big a memory allocation at worst.
>> Just put a big enough buffer in your adc structure and don't bother doing
>> it dynamically.
>>
>> If you didn't want to do it, it should be in the preenable callback rather
>> than the trigger state one (for semantic reasons rather than because it's a
>> bug)
> I'll fix this.
>>> +        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);
>>> +    }
>>> +}
>>> +
>> I'd like a bit of documentation on this and a few of the other more
>> complex functions.  Here it wasn't immediately obvious to me that it
>> was registering a large set of triggers.  Also, silly question but
>> do you have any means of controlling the various timer setups from userspace?
> Sorry about this, I'll try to comment about trigger list to make it more obvious.
> There is no mean to setup timers via userspace, yet...
> I can remove them from the list for now, until this is supported.
> BTW I have some questions on trigger...
> 
>>
>> There have been numerous discussions over the years on having a generic
>> timer subsystem, but if anything got written it passed me by. I have a couple
>> of boards where it would be handy but never had the time to do more than
>> talk about it ;)
> 
> This is interesting... I'd be glad to hear more about it. Can you
> point some discussions if you have it in mind?
Err. Not sure I'll actually find an email thread on this.  I can find
a reference to earlier discussions on it in the original IIO submission
over 8 years ago...  So it was before we had an IIO list which means
it must have been on lkml.

As far as I know, no one ever took this futher though...
> 
> In this driver, validate_trigger routine enforces that only triggers
> allocated for current indio_dev can be used. What if all timer
> triggers are put in a separate driver ? (e.g. like hrtimer in
> drivers/iio/trigger/) ? Purpose would be to tune 'sampling_frequency'
> and so on, on similar model, and have it configured basically when
> using it (e.g. cat trigger/name>trigger/current_trigger.).
I think we'd be closer to having a timer subsystem offer some 
'services' to the drivers and then have your driver make use of those.
As the timer is hard wired to the actual adc timing here (rather than
a signal passing through kernel space like the high resolution timer
triggers are) I think the ADC driver will need to know about it
directly in some fashion.
> 
> Is it a viable option, not to declare timer triggers in stm32-adc.c,
> but use pre-defined list of triggers, and separate trigger driver ? 
> I'm thinking then, of simple string based list... But maybe you
> already though about this king of things ?
A simple string based identification might be prone to problems as
any driver could define it's own naming.

So I think the triggers need to be supplied by stm32-adc.c as the
driver needs to know about the 'hard wired' nature of which timers
can be used.  The underlying handling of timer configuring etc
might be provided by a separate 'provider' module - similar to we
do for clock sources or regulators for example.  Both of these
have the same characteristic of being separate 'hardware blocks'
that can be connected to lots of things, but are in this case
directly feeding the devices using them.

(we do this with IIO consumers too but that is a different game
and not relevant here + harder to follow than simple regs and
clocks).
> 
> Please kindly share you view on this.

It's a non trivial job, but if you ultimately want to be able
to use those periodic timers for multiple possible purposes then
you'll need to do a fair bit of the work towards a generic
subsystem for timers anyway (callbacks etc).

Whether it is worth supporting the more 'soft' connected
equivalents (blackfin timer which is in staging as an IIO trigger
or the pxa271 periodic timer driver I wrote years ago - which
cheated and created a pile of RTCs to expose the interface)
is unclear.  A lot of those usecases are well handled by
the High Resolution Timer trigger.

Jonathan

> 
> Thanks again for your review.
> Best Regards,
> Fabrice
> 
>>> +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;
>> This is one of those cases where actually I'd argue just having the number
>> here and not under a define would be clearer!  So just put 16 here.
>>> +    chan->scan_type.shift = 0;
>> Should be unneeded.  Shift of 0 is the obvious default so no info provided
>> to readers of the code either really.
> I'll fix this
>>> +}
>>> +
>>> +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", &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");
>> How would we have instantiated this if there was not a suitable match?
>> As such what does this check give us? (confused!)
> I'll fix this.
>>> +        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");
>> No benefit in this info being provided (it's obvious, device just turned up
>> in sysfs :) So drop it.
> I'll fix this.
>>> +
>>> +    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
>>> + */
>> This is a big chunk of abstraction that seems excessive at the moment.
>> I'd rather see it introduced only just before it's actually used..
>> (I'm guessing it's intended for support of similar parts?)
>>
>> Right now it just makes the driver harder to review.
>>> +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");
>>>
> 
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v4] ARM: w90x900: let clk_disable() return immediately if clk is NULL
From: Masahiro Yamada @ 2016-11-05 15:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474221559-21269-1-git-send-email-yamada.masahiro@socionext.com>

Hi Wan,

This patch was acked by you long before,
but it has not been pulled-in yet for some reasons.

Now the patch was split per platform.

So, could you apply this patch to your tree, please?


2016-09-19 2:59 GMT+09:00 Masahiro Yamada <yamada.masahiro@socionext.com>:
> In many of clk_disable() implementations, it is a no-op for a NULL
> pointer input, but this is one of the exceptions.
>
> Making it treewide consistent will allow clock consumers to call
> clk_disable() without NULL pointer check.
>
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> Acked-by: Wan Zongshun <mcuos.com@gmail.com>
> ---
>
> Changes in v4:
>   - Split into per-arch patches
>
> Changes in v3:
>   - Return only when clk is NULL.  Do not take care of error pointer.
>
> Changes in v2:
>   - Rebase on Linux 4.6-rc1
>
>  arch/arm/mach-w90x900/clock.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/arch/arm/mach-w90x900/clock.c b/arch/arm/mach-w90x900/clock.c
> index 2c371ff..ac6fd1a 100644
> --- a/arch/arm/mach-w90x900/clock.c
> +++ b/arch/arm/mach-w90x900/clock.c
> @@ -46,6 +46,9 @@ void clk_disable(struct clk *clk)
>  {
>         unsigned long flags;
>
> +       if (!clk)
> +               return;
> +
>         WARN_ON(clk->enabled == 0);
>
>         spin_lock_irqsave(&clocks_lock, flags);
> --
> 1.9.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



-- 
Best Regards
Masahiro Yamada

^ permalink raw reply

* Low network throughput on i.MX28
From: Koul, Vinod @ 2016-11-05 15:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1478351681.353.5.camel@embedded.rocks>

On Sat, 2016-11-05 at 14:14 +0100, J?rg Krause wrote:
> On Sat, 2016-11-05 at 12:39 +0000, Koul, Vinod wrote:
> > 
> > On Sat, 2016-11-05 at 13:06 +0100, J?rg Krause wrote:
> > > 
> > > @ Vinod
> > > In short, I noticed poor performance in the SSP2 (MMC/SD/SDIO)
> > > interface on a custom i.MX28 board with a wifi chip attached.
> > > Comparing
> > > the bandwith with iperf I get >20Mbits/sec on the vendor kernel
> > > and
> > > <5Mbits/sec on the mainline kernel. I am trying to investigate
> > > what
> > > the
> > > bottleneck is.
> > is this imx-dma or imx-sdma..
> > 
> > > 
> > > 
> > > @ Stefan, all
> > > My understanding is that the tasklet in this case is responsible
> > > for
> > > reading the response registers of the DMA controller and return
> > > the
> > > response to the MMC host driver.
> > > 
> > > The vendor kernel does this in the interrupt routine of mxs-mmc by
> > > issueing a complete whereas the mainline kernel does this in the
> > > interrupt routine in mxs-dma by scheduling the tasklet.
> > Is vendor kernel using dmaengine APIs or not?
> It's this engine [1].
> 
> [1] http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/tree/a
> r
> ch/arm/plat-mxs/dmaengine.c?h=imx_2.6.35_1.1.0

Thanks for info, this looks okay.

First can you confirm that register configuration for DMA transaction is
same in both cases.

Second, looking at the driver I see that interrupt handler is not
pushing next descriptor. Also the tasklet is doing callback action and
not pushing any descriptors, did I miss anything in this?

For good dma throughput, you should have multiple dma transactions
queued up and submitted as fast as possible. Can you check if this is
being done.?

We need to minimize/eliminate the delay between two transactions. This
can be done in SW or HW based on support from HW. If HW supports
chaining of descriptors then next transaction which is given to
dmaengine driver should be appended at the end. If not submit the
descriptor to hw immediately on interrupt.?

For good example of latter please look at?drivers/dma/sa11x0-dma.c

HTH
-- 
~Vinod

^ permalink raw reply

* [PATCH] ARM: S3C24XX: Add DMA slave maps for remaining s3c24xx SoCs
From: Krzysztof Kozlowski @ 2016-11-05 15:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1478252753-8231-1-git-send-email-s.nawrocki@samsung.com>

On Fri, Nov 04, 2016 at 10:45:53AM +0100, Sylwester Nawrocki wrote:
> This patch adds DMA slave map tables for the remaining s3c24xx
> SoC types so the whole platform can be switched to the new API.
> A few devices for which there was no DMA support with current
> code are omitted from the tables.
> 
> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
> ---
>  arch/arm/mach-s3c24xx/common.c | 76 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 76 insertions(+)

Thanks, applied.

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v2 2/9] drm/sun4i: support A33 tcon
From: Chen-Yu Tsai @ 2016-11-05 15:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160906144620.6322-3-maxime.ripard@free-electrons.com>

Hi,

On Tue, Sep 6, 2016 at 10:46 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> The A33 has a significantly different pipeline, with components that differ
> too.
>
> Make sure we had compatible for them.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt | 7 ++++++-
>  drivers/gpu/drm/sun4i/sun4i_backend.c                         | 1 +
>  drivers/gpu/drm/sun4i/sun4i_drv.c                             | 8 +++++---
>  drivers/gpu/drm/sun4i/sun4i_tcon.c                            | 8 +++++++-
>  4 files changed, 19 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
> index df8f4aeefe4c..bd3136a5cba5 100644
> --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
> +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
> @@ -26,7 +26,9 @@ TCON
>  The TCON acts as a timing controller for RGB, LVDS and TV interfaces.
>
>  Required properties:
> - - compatible: value should be "allwinner,sun5i-a13-tcon".
> + - compatible: value must be either:
> +   * allwinner,sun5i-a13-tcon
> +   * allwinner,sun8i-a33-tcon
>   - reg: base address and size of memory-mapped region
>   - interrupts: interrupt associated to this IP
>   - clocks: phandles to the clocks feeding the TCON. Three are needed:
> @@ -59,6 +61,7 @@ system.
>  Required properties:
>    - compatible: value must be one of:
>      * allwinner,sun5i-a13-display-backend
> +    * allwinner,sun8i-a33-display-backend
>    - reg: base address and size of the memory-mapped region.
>    - clocks: phandles to the clocks feeding the frontend and backend
>      * ahb: the backend interface clock
> @@ -80,6 +83,7 @@ deinterlacing and color space conversion.
>  Required properties:
>    - compatible: value must be one of:
>      * allwinner,sun5i-a13-display-frontend
> +    * allwinner,sun8i-a33-display-frontend

I just looked at the A23. It seems it's the same display frontend as the A33.
Should we change the compatible string to a23 while it's still in RC?

The backend is probably different. The A33 only claims to support 2048x2048
layers, while the A23 claims to support 8192x8192 layers.

Regards
ChenYu

>    - reg: base address and size of the memory-mapped region.
>    - interrupts: interrupt associated to this IP
>    - clocks: phandles to the clocks feeding the frontend and backend
> @@ -104,6 +108,7 @@ extra node.
>  Required properties:
>    - compatible: value must be one of:
>      * allwinner,sun5i-a13-display-engine
> +    * allwinner,sun8i-a33-display-engine
>
>    - allwinner,pipelines: list of phandle to the display engine
>      frontends available.
> diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c
> index 3ab560450a82..9bfd2e45fceb 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_backend.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
> @@ -345,6 +345,7 @@ static int sun4i_backend_remove(struct platform_device *pdev)
>
>  static const struct of_device_id sun4i_backend_of_table[] = {
>         { .compatible = "allwinner,sun5i-a13-display-backend" },
> +       { .compatible = "allwinner,sun8i-a33-display-backend" },
>         { }
>  };
>  MODULE_DEVICE_TABLE(of, sun4i_backend_of_table);
> diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
> index 942f62e2441c..c4d03c1b6db8 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_drv.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
> @@ -199,13 +199,14 @@ static const struct component_master_ops sun4i_drv_master_ops = {
>
>  static bool sun4i_drv_node_is_frontend(struct device_node *node)
>  {
> -       return of_device_is_compatible(node,
> -                                      "allwinner,sun5i-a13-display-frontend");
> +       return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") ||
> +               of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend");
>  }
>
>  static bool sun4i_drv_node_is_tcon(struct device_node *node)
>  {
> -       return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon");
> +       return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") ||
> +               of_device_is_compatible(node, "allwinner,sun8i-a33-tcon");
>  }
>
>  static int compare_of(struct device *dev, void *data)
> @@ -320,6 +321,7 @@ static int sun4i_drv_remove(struct platform_device *pdev)
>
>  static const struct of_device_id sun4i_drv_of_table[] = {
>         { .compatible = "allwinner,sun5i-a13-display-engine" },
> +       { .compatible = "allwinner,sun8i-a33-display-engine" },
>         { }
>  };
>  MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
> diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
> index fde6af1230d2..cadacb517f95 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
> @@ -488,8 +488,13 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
>         tcon->drm = drm;
>         tcon->dev = dev;
>
> -       if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon"))
> +       if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon")) {
>                 tcon->has_mux = true;
> +               tcon->has_channel_1 = true;
> +       } else {
> +               tcon->has_mux = false;
> +               tcon->has_channel_1 = false;
> +       }
>
>         tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
>         if (IS_ERR(tcon->lcd_rst)) {
> @@ -585,6 +590,7 @@ static int sun4i_tcon_remove(struct platform_device *pdev)
>
>  static const struct of_device_id sun4i_tcon_of_table[] = {
>         { .compatible = "allwinner,sun5i-a13-tcon" },
> +       { .compatible = "allwinner,sun8i-a33-tcon" },
>         { }
>  };
>  MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
> --
> 2.9.3
>

^ permalink raw reply

* [PATCH v2] iio: adc: at91: add suspend and resume callback
From: Jonathan Cameron @ 2016-11-05 16:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161103141654.7oitq25gxqcy7qna@rfolt0960.corp.atmel.com>

On 03/11/16 14:16, Ludovic Desroches wrote:
> On Wed, Nov 02, 2016 at 05:21:48PM +0800, Wenyou Yang wrote:
>> Add suspend/resume callback, support the pinctrl sleep state when
>> the system suspend as well.
>>
>> Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
> Acked-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Applied to the togreg branch of iio.git. Initially pushed out
as testing for the autobuilders to play with it.

Thanks,

Jonathan
> 
> Thanks
> 
>> ---
>>
>> Changes in v2:
>>  - Use CONFIG_PM_SLEEP.
>>  - Use SIMPLE_DEV_PM_OPS macro.
>>
>>  drivers/iio/adc/at91_adc.c | 28 ++++++++++++++++++++++++++++
>>  1 file changed, 28 insertions(+)
>>
>> diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
>> index bbdac07..34b928c 100644
>> --- a/drivers/iio/adc/at91_adc.c
>> +++ b/drivers/iio/adc/at91_adc.c
>> @@ -30,6 +30,7 @@
>>  #include <linux/iio/trigger.h>
>>  #include <linux/iio/trigger_consumer.h>
>>  #include <linux/iio/triggered_buffer.h>
>> +#include <linux/pinctrl/consumer.h>
>>  
>>  /* Registers */
>>  #define AT91_ADC_CR		0x00		/* Control Register */
>> @@ -1347,6 +1348,32 @@ static int at91_adc_remove(struct platform_device *pdev)
>>  	return 0;
>>  }
>>  
>> +#ifdef CONFIG_PM_SLEEP
>> +static int at91_adc_suspend(struct device *dev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
>> +	struct at91_adc_state *st = iio_priv(idev);
>> +
>> +	pinctrl_pm_select_sleep_state(dev);
>> +	clk_disable_unprepare(st->clk);
>> +
>> +	return 0;
>> +}
>> +
>> +static int at91_adc_resume(struct device *dev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
>> +	struct at91_adc_state *st = iio_priv(idev);
>> +
>> +	clk_prepare_enable(st->clk);
>> +	pinctrl_pm_select_default_state(dev);
>> +
>> +	return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume);
>> +
>>  static struct at91_adc_caps at91sam9260_caps = {
>>  	.calc_startup_ticks = calc_startup_ticks_9260,
>>  	.num_channels = 4,
>> @@ -1441,6 +1468,7 @@ static struct platform_driver at91_adc_driver = {
>>  	.driver = {
>>  		   .name = DRIVER_NAME,
>>  		   .of_match_table = of_match_ptr(at91_adc_dt_ids),
>> +		   .pm = &at91_adc_pm_ops,
>>  	},
>>  };
>>  
>> -- 
>> 2.7.4
>>

^ permalink raw reply

* [PATCH v3 2/4] drivers: iio: ti_am335x_adc: add dma support
From: Jonathan Cameron @ 2016-11-05 17:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <8ff4af23-9e90-535c-0e25-9373d845caab@ti.com>

On 05/10/16 10:26, Peter Ujfalusi wrote:
> On 10/05/16 12:04, Mugunthan V N wrote:
>> This patch adds the required pieces to ti_am335x_adc driver for
>> DMA support
>>
>> Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
Applied to a special branch in iio.git that I'll pull into togreg and then
push out as testing in a few minutes for the autobuilders to play with it.
(the special branch is incase Lee needs to later pull this into mfd as well).

Jonathan
>> ---
>>  drivers/iio/adc/ti_am335x_adc.c      | 148 ++++++++++++++++++++++++++++++++++-
>>  include/linux/mfd/ti_am335x_tscadc.h |   7 ++
>>  2 files changed, 152 insertions(+), 3 deletions(-)
> 
> Reviewed-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> 
>>
>> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
>> index c3cfacca..ad9dec3 100644
>> --- a/drivers/iio/adc/ti_am335x_adc.c
>> +++ b/drivers/iio/adc/ti_am335x_adc.c
>> @@ -30,10 +30,28 @@
>>  #include <linux/iio/buffer.h>
>>  #include <linux/iio/kfifo_buf.h>
>>  
>> +#include <linux/dmaengine.h>
>> +#include <linux/dma-mapping.h>
>> +
>> +#define DMA_BUFFER_SIZE		SZ_2K
>> +
>> +struct tiadc_dma {
>> +	struct dma_slave_config	conf;
>> +	struct dma_chan		*chan;
>> +	dma_addr_t		addr;
>> +	dma_cookie_t		cookie;
>> +	u8			*buf;
>> +	int			current_period;
>> +	int			period_size;
>> +	u8			fifo_thresh;
>> +};
>> +
>>  struct tiadc_device {
>>  	struct ti_tscadc_dev *mfd_tscadc;
>> +	struct tiadc_dma dma;
>>  	struct mutex fifo1_lock; /* to protect fifo access */
>>  	int channels;
>> +	int total_ch_enabled;
>>  	u8 channel_line[8];
>>  	u8 channel_step[8];
>>  	int buffer_en_ch_steps;
>> @@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
>>  	return IRQ_HANDLED;
>>  }
>>  
>> +static void tiadc_dma_rx_complete(void *param)
>> +{
>> +	struct iio_dev *indio_dev = param;
>> +	struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> +	struct tiadc_dma *dma = &adc_dev->dma;
>> +	u8 *data;
>> +	int i;
>> +
>> +	data = dma->buf + dma->current_period * dma->period_size;
>> +	dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
>> +
>> +	for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
>> +		iio_push_to_buffers(indio_dev, data);
>> +		data += indio_dev->scan_bytes;
>> +	}
>> +}
>> +
>> +static int tiadc_start_dma(struct iio_dev *indio_dev)
>> +{
>> +	struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> +	struct tiadc_dma *dma = &adc_dev->dma;
>> +	struct dma_async_tx_descriptor *desc;
>> +
>> +	dma->current_period = 0; /* We start to fill period 0 */
>> +	/*
>> +	 * Make the fifo thresh as the multiple of total number of
>> +	 * channels enabled, so make sure that cyclic DMA period
>> +	 * length is also a multiple of total number of channels
>> +	 * enabled. This ensures that no invalid data is reported
>> +	 * to the stack via iio_push_to_buffers().
>> +	 */
>> +	dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
>> +				     adc_dev->total_ch_enabled) - 1;
>> +	/* Make sure that period length is multiple of fifo thresh level */
>> +	dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
>> +				    (dma->fifo_thresh + 1) * sizeof(u16));
>> +
>> +	dma->conf.src_maxburst = dma->fifo_thresh + 1;
>> +	dmaengine_slave_config(dma->chan, &dma->conf);
>> +
>> +	desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
>> +					 dma->period_size * 2,
>> +					 dma->period_size, DMA_DEV_TO_MEM,
>> +					 DMA_PREP_INTERRUPT);
>> +	if (!desc)
>> +		return -EBUSY;
>> +
>> +	desc->callback = tiadc_dma_rx_complete;
>> +	desc->callback_param = indio_dev;
>> +
>> +	dma->cookie = dmaengine_submit(desc);
>> +
>> +	dma_async_issue_pending(dma->chan);
>> +
>> +	tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
>> +	tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
>> +	tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
>> +
>> +	return 0;
>> +}
>> +
>>  static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
>>  {
>>  	struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> @@ -218,20 +297,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
>>  static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
>>  {
>>  	struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> +	struct tiadc_dma *dma = &adc_dev->dma;
>> +	unsigned int irq_enable;
>>  	unsigned int enb = 0;
>>  	u8 bit;
>>  
>>  	tiadc_step_config(indio_dev);
>> -	for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
>> +	for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
>>  		enb |= (get_adc_step_bit(adc_dev, bit) << 1);
>> +		adc_dev->total_ch_enabled++;
>> +	}
>>  	adc_dev->buffer_en_ch_steps = enb;
>>  
>> +	if (dma->chan)
>> +		tiadc_start_dma(indio_dev);
>> +
>>  	am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
>>  
>>  	tiadc_writel(adc_dev,  REG_IRQSTATUS, IRQENB_FIFO1THRES
>>  				| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
>> -	tiadc_writel(adc_dev,  REG_IRQENABLE, IRQENB_FIFO1THRES
>> -				| IRQENB_FIFO1OVRRUN);
>> +
>> +	irq_enable = IRQENB_FIFO1OVRRUN;
>> +	if (!dma->chan)
>> +		irq_enable |= IRQENB_FIFO1THRES;
>> +	tiadc_writel(adc_dev,  REG_IRQENABLE, irq_enable);
>>  
>>  	return 0;
>>  }
>> @@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
>>  static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
>>  {
>>  	struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> +	struct tiadc_dma *dma = &adc_dev->dma;
>>  	int fifo1count, i, read;
>>  
>>  	tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
>>  				IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
>>  	am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
>>  	adc_dev->buffer_en_ch_steps = 0;
>> +	adc_dev->total_ch_enabled = 0;
>> +	if (dma->chan) {
>> +		tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
>> +		dmaengine_terminate_async(dma->chan);
>> +	}
>>  
>>  	/* Flush FIFO of leftover data in the time it takes to disable adc */
>>  	fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
>> @@ -430,6 +525,41 @@ static const struct iio_info tiadc_info = {
>>  	.driver_module = THIS_MODULE,
>>  };
>>  
>> +static int tiadc_request_dma(struct platform_device *pdev,
>> +			     struct tiadc_device *adc_dev)
>> +{
>> +	struct tiadc_dma	*dma = &adc_dev->dma;
>> +	dma_cap_mask_t		mask;
>> +
>> +	/* Default slave configuration parameters */
>> +	dma->conf.direction = DMA_DEV_TO_MEM;
>> +	dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
>> +	dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
>> +
>> +	dma_cap_zero(mask);
>> +	dma_cap_set(DMA_CYCLIC, mask);
>> +
>> +	/* Get a channel for RX */
>> +	dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
>> +	if (IS_ERR(dma->chan)) {
>> +		int ret = PTR_ERR(dma->chan);
>> +
>> +		dma->chan = NULL;
>> +		return ret;
>> +	}
>> +
>> +	/* RX buffer */
>> +	dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
>> +				      &dma->addr, GFP_KERNEL);
>> +	if (!dma->buf)
>> +		goto err;
>> +
>> +	return 0;
>> +err:
>> +	dma_release_channel(dma->chan);
>> +	return -ENOMEM;
>> +}
>> +
>>  static int tiadc_parse_dt(struct platform_device *pdev,
>>  			  struct tiadc_device *adc_dev)
>>  {
>> @@ -512,8 +642,14 @@ static int tiadc_probe(struct platform_device *pdev)
>>  
>>  	platform_set_drvdata(pdev, indio_dev);
>>  
>> +	err = tiadc_request_dma(pdev, adc_dev);
>> +	if (err && err == -EPROBE_DEFER)
>> +		goto err_dma;
>> +
>>  	return 0;
>>  
>> +err_dma:
>> +	iio_device_unregister(indio_dev);
>>  err_buffer_unregister:
>>  	tiadc_iio_buffered_hardware_remove(indio_dev);
>>  err_free_channels:
>> @@ -525,8 +661,14 @@ static int tiadc_remove(struct platform_device *pdev)
>>  {
>>  	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>>  	struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> +	struct tiadc_dma *dma = &adc_dev->dma;
>>  	u32 step_en;
>>  
>> +	if (dma->chan) {
>> +		dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
>> +				  dma->buf, dma->addr);
>> +		dma_release_channel(dma->chan);
>> +	}
>>  	iio_device_unregister(indio_dev);
>>  	tiadc_iio_buffered_hardware_remove(indio_dev);
>>  	tiadc_channels_remove(indio_dev);
>> diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
>> index e45a208..b9a53e0 100644
>> --- a/include/linux/mfd/ti_am335x_tscadc.h
>> +++ b/include/linux/mfd/ti_am335x_tscadc.h
>> @@ -23,6 +23,8 @@
>>  #define REG_IRQENABLE		0x02C
>>  #define REG_IRQCLR		0x030
>>  #define REG_IRQWAKEUP		0x034
>> +#define REG_DMAENABLE_SET	0x038
>> +#define REG_DMAENABLE_CLEAR	0x03c
>>  #define REG_CTRL		0x040
>>  #define REG_ADCFSM		0x044
>>  #define REG_CLKDIV		0x04C
>> @@ -36,6 +38,7 @@
>>  #define REG_FIFO0THR		0xE8
>>  #define REG_FIFO1CNT		0xF0
>>  #define REG_FIFO1THR		0xF4
>> +#define REG_DMA1REQ		0xF8
>>  #define REG_FIFO0		0x100
>>  #define REG_FIFO1		0x200
>>  
>> @@ -126,6 +129,10 @@
>>  #define FIFOREAD_DATA_MASK (0xfff << 0)
>>  #define FIFOREAD_CHNLID_MASK (0xf << 16)
>>  
>> +/* DMA ENABLE/CLEAR Register */
>> +#define DMA_FIFO0		BIT(0)
>> +#define DMA_FIFO1		BIT(1)
>> +
>>  /* Sequencer Status */
>>  #define SEQ_STATUS BIT(5)
>>  #define CHARGE_STEP		0x11
>>
> 
> 

^ permalink raw reply

* [PATCH v3 3/4] ARM: dts: am33xx: add DMA properties for tscadc
From: Jonathan Cameron @ 2016-11-05 17:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161005090443.24576-4-mugunthanvnm@ti.com>

On 05/10/16 10:04, Mugunthan V N wrote:
> Add DMA properties for tscadc
> 
> Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
The support in the driver is now working it's way through iio.git towards
linux-next. I'm guessing this and the next patch will ultimately go through
arm-soc.

Shout if you'd rather I took them through the iio tree.

Thanks,

Jonathan
> ---
>  arch/arm/boot/dts/am33xx.dtsi | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
> index 98748c6..6d607b8 100644
> --- a/arch/arm/boot/dts/am33xx.dtsi
> +++ b/arch/arm/boot/dts/am33xx.dtsi
> @@ -917,6 +917,8 @@
>  			interrupts = <16>;
>  			ti,hwmods = "adc_tsc";
>  			status = "disabled";
> +			dmas = <&edma 53 0>, <&edma 57 0>;
> +			dma-names = "fifo0", "fifo1";
>  
>  			tsc {
>  				compatible = "ti,am3359-tsc";
> 

^ permalink raw reply

* [PATCH 1/4] mfd: ti_am335x_tscadc: store physical address
From: Jonathan Cameron @ 2016-11-05 17:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161031081643.GN13127@dell>

On 31/10/16 08:16, Lee Jones wrote:
> On Sun, 30 Oct 2016, Jonathan Cameron wrote:
> 
>> On 26/10/16 13:17, Lee Jones wrote:
>>> On Fri, 30 Sep 2016, Mugunthan V N wrote:
>>>
>>>> On Wednesday 28 September 2016 01:10 AM, Lee Jones wrote:
>>>>> On Wed, 21 Sep 2016, Mugunthan V N wrote:
>>>>>
>>>>>> store the physical address of the device in its priv to use it
>>>>>> for DMA addressing in the client drivers.
>>>>>>
>>>>>> Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
>>>>>> ---
>>>>>>  drivers/mfd/ti_am335x_tscadc.c       | 1 +
>>>>>>  include/linux/mfd/ti_am335x_tscadc.h | 1 +
>>>>>>  2 files changed, 2 insertions(+)
>>>>>>
>>>>>> diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
>>>>>> index c8f027b..0f3fab4 100644
>>>>>> --- a/drivers/mfd/ti_am335x_tscadc.c
>>>>>> +++ b/drivers/mfd/ti_am335x_tscadc.c
>>>>>> @@ -183,6 +183,7 @@ static	int ti_tscadc_probe(struct platform_device *pdev)
>>>>>>  		tscadc->irq = err;
>>>>>>  
>>>>>>  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>>>> +	tscadc->tscadc_phys_base = res->start;
>>>>>
>>>>> This is unusual.  Can't you use a virt_to_phys() variant instead?
>>>>>
>>>>
>>>> I tried using virt_to_phys(), but its not working for me.
>>>> Also saw many drivers uses like this to get physical address
>>>> ("git grep -n " res->start;" drivers/*").
>>>
>>> Very well:
>>>
>>> For my own reference:
>>>   Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
>>>
>>> Let me know how you wish this set to be handled.
>> I'm happy to pick up the whole series.  There are some more mfd
>> header changes in patch 2 but as they only add defines, I
>> don't mind that much if I don't an Ack from you on those
>> (btw this got to V3 but as patch 1 didn't change I'll carry
>> your ack forwards).
>>
>> Do you want an immutable branch?  Seems unlikely to cause
>> much trouble even if there is a merge issue on all 10ish
>> lines of mfd code in the next merge window.
> 
> Not at the moment, but if you could set things up so it's possible to
> create one at a later date if things go Pete Tong, that would be
> great.
Couldn't think of an easy way to do this without creating a branch
and merging it into my normal branch.  I'll not push it out to
kernel.org though unless you tell me you need it.

Applied to the togreg branch (indirectly ;) of iio.git pushed out
as testing for the autobuilders to play with it.

Thanks,

Jonathan

> 

^ permalink raw reply

* [Bug] ARM: mxs: STI: console can't wake up from freeze
From: Russell King - ARM Linux @ 2016-11-05 18:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1113068116.152452.34665947-6f6b-41d6-92af-eabcbcb794ea.open-xchange@email.1und1.de>

On Sat, Nov 05, 2016 at 04:28:37PM +0100, Stefan Wahren wrote:
> As i wrote in my email before, i added a pr_info() into freeze_wake.
> But i never see the output of this message. So i assume freeze_wake
> is never called. Again, how could this happen?

Hmm, so the bit that you're getting stuck on is:

        wait_event(suspend_freeze_wait_head,
                   suspend_freeze_state == FREEZE_STATE_WAKE);

Now there's two things about this here - it's a non-interruptible wait,
so I think the hung task detection may trigger on that (I'm not entirely
sure on that point though, and I don't have time this evening to read
the code to find out.)

The second thing is, that in order to pass this point, something has to
call freeze_wake().

There's not that many possibilities for that:

$ git grep freeze_wake drivers kernel
drivers/base/power/wakeup.c:    freeze_wake();
drivers/base/power/wakeup.c:    freeze_wake();
kernel/power/suspend.c:void freeze_wake(void)
kernel/power/suspend.c:EXPORT_SYMBOL_GPL(freeze_wake);

One of those is pm_system_wakeup(), the other is wakeup_source_activate()
via wakeup_source_report_event() via __pm_stay_awake() or
__pm_wakeup_event().

Looking at the results of:

$ grep 'pm_wakeup_event\|pm_stay_awake\|pm_system_wakeup' drivers kernel -r

it looks like for freeze support to work, various drivers need to call
one of these functions.

The iMX serial driver doesn't call any of these functions, so I can't
see how we'd get past this point - and from that grep you'll see nothing
in kernel/irq touches any of these functions.

Documentation/power/suspend-and-interrupts.txt does a very poor job
(which is typical) of describing what the requirements are for
"suspend-to-idle", it doesn't really say that any of the above
functions must be called and it doesn't say who's responsible for
calling these functions.  It does talk about "pm_system_wakeup()"
for "rare cases".

So my conclusion, based on the poor documentation and the results of
my greps, is that "freeze" aka "suspend-to-idle" is not supported on
the majority of hardware, and attempting to use it will result in the
system locking up in exactly the way you're seeing.

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply

* [PATCH 1/9] ARM: dts: imx1: Remove skeleton.dtsi
From: Fabio Estevam @ 2016-11-05 19:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1473177196-29371-1-git-send-email-fabio.estevam@nxp.com>

Hi Shawn,

On Tue, Sep 6, 2016 at 12:53 PM, Fabio Estevam <fabio.estevam@nxp.com> wrote:
> The inclusion of skeleton.dtsi causes the following build warning:
>
> Warning (unit_address_vs_reg): Node /memory has a reg or ranges property, but no unit name
>
> Instead of fixing skeleton.dtsi, just add the top level definitions
> for address-cells and size-cell and remove its inclusion.
>
> Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>

Are you OK with this series?

^ permalink raw reply

* [PATCH RFC] ARM: dts: add support for Turris Omnia
From: Uwe Kleine-König @ 2016-11-05 20:38 UTC (permalink / raw)
  To: linux-arm-kernel

This machine is an open hardware router by cz.nic driven by a
Marvell Armada 385.

Signed-off-by: Uwe Kleine-K?nig <uwe@kleine-koenig.org>
---

Hello,

the following components are working:

 - WAN port
 - eMMC
 - UART0
 - USB

Still missing is support for the switch. Wireless fails to probe, didn't
debug this up to now. SFP is untested as is UART1.

The device tree on the device doesn't specify a board compatible, I added
"turris,omnia". Do I need to "register" turris in vendor-prefixes.txt for that?
@Tomas+Martin: Is this correct at all, or should I better reference cz.nic?

Best regards
Uwe

---
 arch/arm/boot/dts/Makefile                    |   1 +
 arch/arm/boot/dts/armada-385-turris-omnia.dts | 246 ++++++++++++++++++++++++++
 2 files changed, 247 insertions(+)
 create mode 100644 arch/arm/boot/dts/armada-385-turris-omnia.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index befcd2619902..f1d3b9ff257e 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -920,6 +920,7 @@ dtb-$(CONFIG_MACH_ARMADA_38X) += \
 	armada-385-db-ap.dtb \
 	armada-385-linksys-caiman.dtb \
 	armada-385-linksys-cobra.dtb \
+	armada-385-turris-omnia.dtb \
 	armada-388-clearfog.dtb \
 	armada-388-db.dtb \
 	armada-388-gp.dtb \
diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dts
new file mode 100644
index 000000000000..d3cd8a4d713d
--- /dev/null
+++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts
@@ -0,0 +1,246 @@
+/*
+ * Device Tree file for the Turris Omnia
+ * Schematic available at https://www.turris.cz/doc/_media/rtrom01-schema.pdf
+ *
+ * Copyright (C) 2016 Uwe Kleine-K?nig <uwe@kleine-koenig.org>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is licensed under the terms of the GNU General Public
+ *     License version 2.  This program is licensed "as is" without
+ *     any warranty of any kind, whether express or implied.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+#include "armada-385.dtsi"
+
+/ {
+	model = "Turris Omnia";
+	compatible = "turris,omnia", "marvell,armada385", "marvell,armada380";
+
+	chosen {
+		stdout-path = &uart0;
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x00000000 0x40000000>; /* 1024 MB */
+	};
+
+	soc {
+		ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
+			  MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000>;
+
+		internal-regs {
+
+			/* USB part of the eSATA/USB 2.0 port */
+			usb at 58000 {
+				status = "okay";
+			};
+
+			sata at a8000 {
+				status = "okay";
+			};
+
+			sdhci at d8000 {
+				pinctrl-names = "default";
+				pinctrl-0 = <&sdhci_pins>;
+				status = "okay";
+
+				bus-width = <8>;
+				no-1-8-v;
+				non-removable;
+			};
+
+			usb3 at f0000 {
+				status = "okay";
+			};
+
+			usb3 at f8000 {
+				status = "okay";
+			};
+		};
+
+		pcie-controller {
+			status = "okay";
+
+			pcie at 1,0 {
+				/* Port 0, Lane 0 */
+				status = "okay";
+			};
+
+			pcie at 2,0 {
+				/* Port 2, Lane 0 */
+				status = "okay";
+			};
+
+			pcie at 3,0 {
+				/* Port 3, Lane 0 */
+				status = "okay";
+			};
+		};
+	};
+};
+
+&eth0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&ge0_rgmii_pins>;
+	status = "okay";
+	phy-mode = "rgmii-id";
+
+	fixed-link {
+		speed = <1000>;
+		full-duplex;
+	};
+};
+
+&eth1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&ge1_rgmii_pins>;
+	status = "okay";
+	phy-mode = "rgmii-id";
+
+	fixed-link {
+		speed = <1000>;
+		full-duplex;
+	};
+};
+
+/* WAN port */
+&eth2 {
+	status = "okay";
+	phy-mode = "sgmii";
+	phy = <&phy1>;
+};
+
+&i2c0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c0_pins>;
+	status = "okay";
+
+	i2cmux at 70 {
+		compatible = "nxp,pca9547";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x70>;
+		status = "okay";
+
+		i2c at 0 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0>;
+			status = "okay";
+
+			/* STM32F0 at address 0x2a */
+			/* leds device at address 0x2b */
+
+			eeprom at 54 {
+				/* holds configuration about RAM, evaluated by bootloader */
+				compatible = "at,24c64";
+				reg = <0x54>;
+			};
+		};
+
+		i2c at 5 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <5>;
+
+			/* ATSHA204A at address 0x64 */
+		};
+
+		i2c at 6 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <6>;
+
+			/* exposed on pin header */
+		};
+	};
+};
+
+&mdio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&mdio_pins>;
+	status = "okay";
+
+	phy1: phy at 1 {
+		status = "okay";
+		compatible = "marvell,88E1514", "marvell,88E1510", "ethernet-phy-ieee802.3-c22";
+		reg = <1>;
+	};
+};
+
+&pinctrl {
+	spi0cs1_pins: spi0-pins-0cs1 {
+		marvell,pins = "mpp26";
+		marvell,function = "spi0";
+	};
+};
+
+&spi0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&spi0_pins &spi0cs1_pins>;
+	status = "okay";
+
+	spi-nor at 0 {
+		compatible = "spansion,s25fl164k", "jedec,spi-nor";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0>;
+		spi-max-frequency = <40000000>;
+
+		partition at 0 {
+			reg = <0x0 0x00100000>;
+			label = "U-Boot";
+		};
+
+		partition at 1 {
+			reg = <0x00100000 0x00700000>;
+			label = "Rescue system";
+		};
+	};
+
+	/* @1 is on pin header */
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_pins>;
+	status = "okay";
+};
+
+&uart1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart1_pins>;
+	status = "okay";
+};
-- 
2.10.2

^ permalink raw reply related

* [PATCH v3 0/2] Add TI SCI Reset Driver
From: santosh.shilimkar at oracle.com @ 2016-11-05 21:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161104174240.9688-1-afd@ti.com>



On 11/4/16 11:42 AM, Andrew F. Davis wrote:
> Hello all,
>
> This series adds a reset controller driver that uses the TI SCI
> protocol to manage resets.
>
> The TI SCI protocol is used to communicate with power management
> controllers used by some SoCs. These controllers manage the various
> power domains, clocks, and resets available on a SoC.
>
> This series is based on drivers for TI SCI and the first two controlled
> elements above, these series can be found here:
>
> TI-SCI: http://www.spinics.net/lists/arm-kernel/msg536851.html
> PM Domains: http://www.spinics.net/lists/devicetree/msg146621.html
> Clocks: https://www.spinics.net/lists/linux-clk/msg12785.html
>
> Thanks,
> Andrew
>
> Changes from v2:
>  - Merged DT binding patch and reset header patch
>  - Added locking for reset bit mask
>
> Changes from v1:
>  - Revised dt binding
>  - CC Linux ARM list
>
> Andrew F. Davis (2):
>   Documentation: dt: reset: Add TI SCI reset binding
>   reset: Add the TI SCI reset driver
>
Acked-by: Santosh Shilimkar <ssantosh@kernel.org>

^ permalink raw reply

* [PATCH RFC] ARM: dts: add support for Turris Omnia
From: Andrew Lunn @ 2016-11-05 21:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161105203841.9661-1-uwe@kleine-koenig.org>

> +&mdio {
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&mdio_pins>;
> +	status = "okay";
> +
> +	phy1: phy at 1 {
> +		status = "okay";
> +		compatible = "marvell,88E1514", "marvell,88E1510", "ethernet-phy-ieee802.3-c22";
> +		reg = <1>;
> +	};

phy.txt says:

- compatible: Compatible list, may contain
  "ethernet-phy-ieee802.3-c22" or "ethernet-phy-ieee802.3-c45" for
  PHYs that implement IEEE802.3 clause 22 or IEEE802.3 clause 45
  specifications. If neither of these are specified, the default is to
  assume clause 22.

  If the phy's identifier is known then the list may contain an entry
  of the form: "ethernet-phy-idAAAA.BBBB" where
     AAAA - The value of the 16 bit Phy Identifier 1 register as
            4 hex digits. This is the chip vendor OUI bits 3:18
     BBBB - The value of the 16 bit Phy Identifier 2 register as
            4 hex digits. This is the chip vendor OUI bits 19:24,
            followed by 10 bits of a vendor specific ID.

  The compatible list should not contain other values than those
  listed here.

Please don't list the "marvell,*" names.

       Andrew

^ permalink raw reply

* [PATCH RFC] ARM: dts: add support for Turris Omnia
From: Andrew Lunn @ 2016-11-05 21:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161105203841.9661-1-uwe@kleine-koenig.org>

> Still missing is support for the switch.

Is it a Marvell Switch? armada-370-rd.dts would be a good start for
the old binding? vf610-zii-dev-rev-b.dts uses the new binding.

> SFP is untested as is UART1.

UART would be unusual. They are normally i2c.

> Do I need to "register" turris in vendor-prefixes.txt for that?

Yes please.

    Andrew

^ permalink raw reply

* [PATCH RFC] ARM: dts: add support for Turris Omnia
From: Uwe Kleine-König @ 2016-11-05 21:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161105212326.GC1216@lunn.ch>

Hello Andrew,

On Sat, Nov 05, 2016 at 10:23:26PM +0100, Andrew Lunn wrote:
> > Still missing is support for the switch.
> 
> Is it a Marvell Switch? armada-370-rd.dts would be a good start for
> the old binding? vf610-zii-dev-rev-b.dts uses the new binding.

Yeah, a 88E6176. I already try to understand vf610-zii-dev-rev-b.dts. Do
you know if this driver works for the 88E6176?

> > SFP is untested as is UART1.
> 
> UART would be unusual. They are normally i2c.

I wanted to say: SFP is untested, and UART1 is untested too. Yes, SFP is
connected via i2c.

> > Do I need to "register" turris in vendor-prefixes.txt for that?
> 
> Yes please.

OK, will wait for Martin to comment what we want there. cznic or turris.

Thanks
Uwe
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 455 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161105/7d3c829b/attachment.sig>

^ permalink raw reply

* [PATCH RFC] ARM: dts: add support for Turris Omnia
From: Andrew Lunn @ 2016-11-05 21:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161105212748.vtdprlxxismy5xmk@perseus.defre.kleine-koenig.org>

On Sat, Nov 05, 2016 at 10:27:49PM +0100, Uwe Kleine-K?nig wrote:
> Hello Andrew,
> 
> On Sat, Nov 05, 2016 at 10:23:26PM +0100, Andrew Lunn wrote:
> > > Still missing is support for the switch.
> > 
> > Is it a Marvell Switch? armada-370-rd.dts would be a good start for
> > the old binding? vf610-zii-dev-rev-b.dts uses the new binding.
> 
> Yeah, a 88E6176. I already try to understand vf610-zii-dev-rev-b.dts. Do
> you know if this driver works for the 88E6176?

Yes it does. 

The vf610-zii-dev-rev-b is a bit complex because it has three
switches, and an mdio mux. You should be able to transplant the
switch0: switch0 at 0 part into the mdio node.

	 Andrew

^ permalink raw reply

* [PATCH RFC] ARM: dts: add support for Turris Omnia
From: Uwe Kleine-König @ 2016-11-05 22:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161105210441.GB1216@lunn.ch>

On Sat, Nov 05, 2016 at 10:04:41PM +0100, Andrew Lunn wrote:
> > +&mdio {
> > +	pinctrl-names = "default";
> > +	pinctrl-0 = <&mdio_pins>;
> > +	status = "okay";
> > +
> > +	phy1: phy at 1 {
> > +		status = "okay";
> > +		compatible = "marvell,88E1514", "marvell,88E1510", "ethernet-phy-ieee802.3-c22";
> > +		reg = <1>;
> > +	};
> 
> phy.txt says:
> 
> - compatible: Compatible list, may contain
>   "ethernet-phy-ieee802.3-c22" or "ethernet-phy-ieee802.3-c45" for
>   PHYs that implement IEEE802.3 clause 22 or IEEE802.3 clause 45
>   specifications. If neither of these are specified, the default is to
>   assume clause 22.
> 
>   If the phy's identifier is known then the list may contain an entry
>   of the form: "ethernet-phy-idAAAA.BBBB" where
>      AAAA - The value of the 16 bit Phy Identifier 1 register as
>             4 hex digits. This is the chip vendor OUI bits 3:18
>      BBBB - The value of the 16 bit Phy Identifier 2 register as
>             4 hex digits. This is the chip vendor OUI bits 19:24,
>             followed by 10 bits of a vendor specific ID.
> 
>   The compatible list should not contain other values than those
>   listed here.
> 
> Please don't list the "marvell,*" names.

Will do for v2. arch/arm/boot/dts/keystone-* needs fixing in this
regard, too.

Best regards
Uwe
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 455 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161105/813ef036/attachment.sig>

^ permalink raw reply

* [PATCH 7/8] ARM64: dts: meson-gxl-p23x: Add SD/SDIO/MMC and PWM nodes
From: Kevin Hilman @ 2016-11-05 22:10 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1477932286-27482-8-git-send-email-narmstrong@baylibre.com>

Neil Armstrong <narmstrong@baylibre.com> writes:

> Add SD/SDIO/MMC nodes and PWM 32768Hz clock configuration to provide
> storage and WiFi functionality on the p23x boards.

Just curious... what storage functionality are you referring to here?

Kevin

> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
>  .../boot/dts/amlogic/meson-gxl-s905d-p23x.dtsi     | 112 +++++++++++++++++++++
>  1 file changed, 112 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p23x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p23x.dtsi
> index 666fe2b..7830809 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p23x.dtsi
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p23x.dtsi
> @@ -56,6 +56,46 @@
>  		device_type = "memory";
>  		reg = <0x0 0x0 0x0 0x80000000>;
>  	};
> +
> +	vddio_boot: regulator-vddio_boot {
> +		compatible = "regulator-fixed";
> +		regulator-name = "VDDIO_BOOT";
> +		regulator-min-microvolt = <1800000>;
> +		regulator-max-microvolt = <1800000>;
> +	};
> +
> +	vddao_3v3: regulator-vddao_3v3 {
> +		compatible = "regulator-fixed";
> +		regulator-name = "VDDAO_3V3";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +	};
> +
> +	vcc_3v3: regulator-vcc_3v3 {
> +		compatible = "regulator-fixed";
> +		regulator-name = "VCC_3V3";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +	};
> +
> +	emmc_pwrseq: emmc-pwrseq {
> +		compatible = "mmc-pwrseq-emmc";
> +		reset-gpios = <&gpio BOOT_9 GPIO_ACTIVE_LOW>;
> +	};
> +
> +	wifi32k: wifi32k {
> +		compatible = "pwm-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <32768>;
> +		pwms = <&pwm_ef 0 30518 0>; /* PWM_E at 32.768KHz */
> +	};
> +
> +	sdio_pwrseq: sdio-pwrseq {
> +		compatible = "mmc-pwrseq-simple";
> +		reset-gpios = <&gpio GPIOX_6 GPIO_ACTIVE_LOW>;
> +		clocks = <&wifi32k>;
> +		clock-names = "ext_clock";
> +	};
>  };
>  
>  /* This UART is brought out to the DB9 connector */
> @@ -64,3 +104,75 @@
>  	pinctrl-0 = <&uart_ao_a_pins>;
>  	pinctrl-names = "default";
>  };
> +
> +/* Wireless SDIO Module */
> +&sd_emmc_a {
> +	status = "okay";
> +	pinctrl-0 = <&sdio_pins>;
> +	pinctrl-names = "default";
> +	#address-cells = <1>;
> +	#size-cells = <0>;
> +
> +	bus-width = <4>;
> +	cap-sd-highspeed;
> +	max-frequency = <100000000>;
> +
> +	non-removable;
> +	disable-wp;
> +
> +	mmc-pwrseq = <&sdio_pwrseq>;
> +
> +	vmmc-supply = <&vddao_3v3>;
> +	vqmmc-supply = <&vddio_boot>;
> +
> +	brcmf: bcrmf at 1 {
> +		reg = <1>;
> +		compatible = "brcm,bcm4329-fmac";
> +	};
> +};
> +
> +/* SD card */
> +&sd_emmc_b {
> +	status = "okay";
> +	pinctrl-0 = <&sdcard_pins>;
> +	pinctrl-names = "default";
> +
> +	bus-width = <4>;
> +	cap-sd-highspeed;
> +	max-frequency = <100000000>;
> +	disable-wp;
> +
> +	cd-gpios = <&gpio CARD_6 GPIO_ACTIVE_HIGH>;
> +	cd-inverted;
> +
> +	vmmc-supply = <&vddao_3v3>;
> +	vqmmc-supply = <&vddio_boot>;
> +};
> +
> +/* eMMC */
> +&sd_emmc_c {
> +	status = "okay";
> +	pinctrl-0 = <&emmc_pins>;
> +	pinctrl-names = "default";
> +
> +	bus-width = <8>;
> +	cap-sd-highspeed;
> +	cap-mmc-highspeed;
> +	max-frequency = <200000000>;
> +	non-removable;
> +	disable-wp;
> +	mmc-ddr-1_8v;
> +	mmc-hs200-1_8v;
> +
> +	mmc-pwrseq = <&emmc_pwrseq>;
> +	vmmc-supply = <&vcc_3v3>;
> +	vqmmc-supply = <&vddio_boot>;
> +};
> +
> +&pwm_ef {
> +	status = "okay";
> +	pinctrl-0 = <&pwm_e_pins>;
> +	pinctrl-names = "default";
> +	clocks = <&clkc CLKID_FCLK_DIV4>;
> +	clock-names = "clkin0";
> +};

^ permalink raw reply

* [PATCH] clk: rockchip: remove more CLK_IGNORE_UNUSED for rk3399 clocktree
From: Heiko Stuebner @ 2016-11-05 22:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1478144333-13436-1-git-send-email-jay.xu@rock-chips.com>

Am Donnerstag, 3. November 2016, 11:38:53 CET schrieb Jianqun Xu:
> Optimize rk3399 clocktree by removing CLK_IGNORE_UNUSED of some clocks.
> 
> clocks will managered by usb:
> - clk_usbphy0_480m_src
> - clk_usbphy1_480m_src
> - clk_usbphy_480m
> 
> clocks will be managered by pvtm:
> - clk_pvtm_core_l
> - clk_pvtm_core_b
> - clk_pvtm_ddr
> 
> clocks will be managered by dfi:
> - pclk_ddr_mon
> - clk_dfimon0_timer
> - clk_dfimon1_timer
> - aclk_dcf
> - pclk_dcf
> 
> Signed-off-by: Jianqun Xu <jay.xu@rock-chips.com>

I gave this a test on a rk3399-gru device and was still able to boot 
sucessfully. So I've applied the patch to my clk-branch for 4.10

Thanks
Heiko

^ permalink raw reply

* Low network throughput on i.MX28
From: Jörg Krause @ 2016-11-05 22:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1478360733.3405.17.camel@intel.com>

On Sat, 2016-11-05 at 15:45 +0000, Koul, Vinod wrote:
> On Sat, 2016-11-05 at 14:14 +0100, J?rg Krause wrote:
> > On Sat, 2016-11-05 at 12:39 +0000, Koul, Vinod wrote:
> > > 
> > > On Sat, 2016-11-05 at 13:06 +0100, J?rg Krause wrote:
> > > > 
> > > > @ Vinod
> > > > In short, I noticed poor performance in the SSP2 (MMC/SD/SDIO)
> > > > interface on a custom i.MX28 board with a wifi chip attached.
> > > > Comparing
> > > > the bandwith with iperf I get >20Mbits/sec on the vendor kernel
> > > > and
> > > > <5Mbits/sec on the mainline kernel. I am trying to investigate
> > > > what
> > > > the
> > > > bottleneck is.
> > > 
> > > is this imx-dma or imx-sdma..
> > > 
> > > > 
> > > > 
> > > > @ Stefan, all
> > > > My understanding is that the tasklet in this case is
> > > > responsible
> > > > for
> > > > reading the response registers of the DMA controller and return
> > > > the
> > > > response to the MMC host driver.
> > > > 
> > > > The vendor kernel does this in the interrupt routine of mxs-mmc 
> > > > by
> > > > issueing a complete whereas the mainline kernel does this in
> > > > the
> > > > interrupt routine in mxs-dma by scheduling the tasklet.
> > > 
> > > Is vendor kernel using dmaengine APIs or not?
> > 
> > It's this engine [1].
> > 
> > [1] http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/tre
> > e/a
> > r
> > ch/arm/plat-mxs/dmaengine.c?h=imx_2.6.35_1.1.0
> 
> Thanks for info, this looks okay.
> 
> First can you confirm that register configuration for DMA transaction
> is
> same in both cases.

They are almost identical. The difference is that the mainline MMC
driver has SDIO IRQ enabled and the APB bus has burst mode enable. Both
don't have any influence.

> Second, looking at the driver I see that interrupt handler is not
> pushing next descriptor. Also the tasklet is doing callback action
> and
> not pushing any descriptors, did I miss anything in this?

Right. However, after observing the registers I noticed that the vendor
MMC kernel driver only issues one DMA command, whereas the mainline
driver issues two chained DMA commands. The relevant function in both
drivers is mxs_mmc_adtc().

The mainline function issues a DMA transaction with setting the PIO
words only and appends the data from the MMC host.

The vendor function copies the MMC host data from the scatterlist into
an owned DMA buffer, sets the buffer address as the next command
address and issues the descriptor to the DMA engine.

> For good dma throughput, you should have multiple dma transactions
> queued up and submitted as fast as possible. Can you check if this is
> being done.?
> 
> We need to minimize/eliminate the delay between two transactions.
> This
> can be done in SW or HW based on support from HW. If HW supports
> chaining of descriptors then next transaction which is given to
> dmaengine driver should be appended at the end. If not submit the
> descriptor to hw immediately on interrupt.?

I see! In this particular case, the vendor driver reduces the chaining
of descriptors, whereas the mainline driver chains two DMA commands.
Note, that the i.MX28 hardware does support chaining. So, might this be
an issue for poor performance?

J?rg

^ permalink raw reply

* [PATCH] convert orion5x ls-chl to device tree
From: Ash Hughes @ 2016-11-05 23:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <87zilfmn39.fsf@free-electrons.com>

Sorry about that, formatting error with tabs being converted to spaces in email. This should now apply.

Ash
--- 
>From 398d2c6e5c834e6887a5e6b5e898455977d0c00b Mon Sep 17 00:00:00 2001
From: Ashley Hughes <ashley.hughes@blueyonder.co.uk>
Date: Sun, 9 Oct 2016 17:04:12 +0100
Subject: [PATCH] convert ls-chl to FDT

Signed-off-by: Ashley Hughes <ashley.hughes@blueyonder.co.uk>
---
 arch/arm/boot/dts/Makefile           |   1 +
 arch/arm/boot/dts/orion5x-lschl.dts  | 171 ++++++++++++++++++
 arch/arm/mach-orion5x/Kconfig        |   4 +-
 arch/arm/mach-orion5x/Makefile       |   1 -
 arch/arm/mach-orion5x/ls-chl-setup.c | 331 -----------------------------------
 5 files changed, 174 insertions(+), 334 deletions(-)
 create mode 100644 arch/arm/boot/dts/orion5x-lschl.dts
 delete mode 100644 arch/arm/mach-orion5x/ls-chl-setup.c

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index befcd26..4853049 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -597,6 +597,7 @@ dtb-$(CONFIG_ARCH_ORION5X) += \
 	orion5x-lacie-ethernet-disk-mini-v2.dtb \
 	orion5x-linkstation-lsgl.dtb \
 	orion5x-linkstation-lswtgl.dtb \
+	orion5x-lschl.dtb \
 	orion5x-lswsgl.dtb \
 	orion5x-maxtor-shared-storage-2.dtb \
 	orion5x-netgear-wnr854t.dtb \
diff --git a/arch/arm/boot/dts/orion5x-lschl.dts b/arch/arm/boot/dts/orion5x-lschl.dts
new file mode 100644
index 0000000..9474092
--- /dev/null
+++ b/arch/arm/boot/dts/orion5x-lschl.dts
@@ -0,0 +1,171 @@
+/*
+ * Device Tree file for Buffalo Linkstation LS-CHLv3
+ *
+ * Copyright (C) 2016 Ash Hughes <ashley.hughes@blueyonder.co.uk>
+ * Copyright (C) 2015, 2016
+ * Roger Shimizu <rogershimizu@gmail.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include "orion5x-linkstation.dtsi"
+#include "mvebu-linkstation-gpio-simple.dtsi"
+#include "mvebu-linkstation-fan.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+	model = "Buffalo Linkstation Live v3 (LS-CHL)";
+	compatible = "buffalo,lschl", "marvell,orion5x-88f5182", "marvell,orion5x";
+
+	memory { /* 128 MB */
+		device_type = "memory";
+		reg = <0x00000000 0x8000000>;
+	};
+
+	gpio_keys {
+		func {
+			label = "Function Button";
+			linux,code = <KEY_OPTION>;
+			gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;
+		};
+
+		power-on-switch {
+			gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
+		};
+
+		power-auto-switch {
+			gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
+		};
+	};
+
+	gpio_leds {
+		pinctrl-0 = <&pmx_led_power &pmx_led_alarm &pmx_led_info &pmx_led_func>;
+		blue-power-led {
+			gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
+		};
+
+		red-alarm-led {
+			gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
+		};
+
+		amber-info-led {
+			gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
+		};
+
+		func {
+			label = "lschl:func:blue:top";
+			gpios = <&gpio0 17 GPIO_ACTIVE_LOW>;
+		};
+	};
+
+	gpio_fan {
+		gpios = <&gpio0 14 GPIO_ACTIVE_LOW
+			 &gpio0 16 GPIO_ACTIVE_LOW>;
+
+		alarm-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
+	};
+};
+
+&pinctrl {
+	pmx_led_power: pmx-leds {
+		marvell,pins = "mpp0";
+		marvell,function = "gpio";
+	};
+
+	pmx_power_hdd: pmx-power-hdd {
+		marvell,pins = "mpp1";
+		marvell,function = "gpio";
+	};
+
+	pmx_led_alarm: pmx-leds {
+		marvell,pins = "mpp2";
+		marvell,function = "gpio";
+	};
+
+	pmx_led_info: pmx-leds {
+		marvell,pins = "mpp3";
+		marvell,function = "gpio";
+	};
+
+	pmx_fan_lock: pmx-fan-lock {
+		marvell,pins = "mpp6";
+		marvell,function = "gpio";
+	};
+
+	pmx_power_switch: pmx-power-switch {
+		marvell,pins = "mpp8", "mpp10", "mpp15";
+		marvell,function = "gpio";
+	};
+
+	pmx_power_usb: pmx-power-usb {
+		marvell,pins = "mpp9";
+		marvell,function = "gpio";
+	};
+
+	pmx_fan_high: pmx-fan-high {
+		marvell,pins = "mpp14";
+		marvell,function = "gpio";
+	};
+
+	pmx_fan_low: pmx-fan-low {
+		marvell,pins = "mpp16";
+		marvell,function = "gpio";
+	};
+
+	pmx_led_func: pmx-leds {
+		marvell,pins = "mpp17";
+		marvell,function = "gpio";
+	};
+
+	pmx_sw_init: pmx-sw-init {
+		marvell,pins = "mpp7";
+		marvell,function = "gpio";
+	};
+};
+
+&hdd_power {
+	gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
+};
+
+&usb_power {
+	gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
+};
+
diff --git a/arch/arm/mach-orion5x/Kconfig b/arch/arm/mach-orion5x/Kconfig
index 89bb0fc..793efa9 100644
--- a/arch/arm/mach-orion5x/Kconfig
+++ b/arch/arm/mach-orion5x/Kconfig
@@ -85,8 +85,8 @@ config MACH_LINKSTATION_PRO
 	  v2 devices are supported.
 
 config MACH_LINKSTATION_LSCHL
-	bool "Buffalo Linkstation Live v3 (LS-CHL)"
-	select I2C_BOARDINFO if I2C
+	bool "Buffalo Linkstation Live v3 (LS-CHL) (Flattened Device Tree)"
+	select ARCH_ORION5X_DT
 	help
 	  Say 'Y' here if you want your kernel to support the
 	  Buffalo Linkstation Live v3 (LS-CHL) platform.
diff --git a/arch/arm/mach-orion5x/Makefile b/arch/arm/mach-orion5x/Makefile
index 4b2502b..ae91872 100644
--- a/arch/arm/mach-orion5x/Makefile
+++ b/arch/arm/mach-orion5x/Makefile
@@ -18,7 +18,6 @@ obj-$(CONFIG_MACH_WNR854T)	+= wnr854t-setup.o
 obj-$(CONFIG_MACH_RD88F5181L_GE)	+= rd88f5181l-ge-setup.o
 obj-$(CONFIG_MACH_RD88F5181L_FXO)	+= rd88f5181l-fxo-setup.o
 obj-$(CONFIG_MACH_RD88F6183AP_GE)	+= rd88f6183ap-ge-setup.o
-obj-$(CONFIG_MACH_LINKSTATION_LSCHL)	+= ls-chl-setup.o
 
 obj-$(CONFIG_ARCH_ORION5X_DT)		+= board-dt.o
 obj-$(CONFIG_MACH_D2NET_DT)	+= board-d2net.o
diff --git a/arch/arm/mach-orion5x/ls-chl-setup.c b/arch/arm/mach-orion5x/ls-chl-setup.c
deleted file mode 100644
index dfdaa8a..0000000
--- a/arch/arm/mach-orion5x/ls-chl-setup.c
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * arch/arm/mach-orion5x/ls-chl-setup.c
- *
- * Maintainer: Ash Hughes <ashley.hughes@blueyonder.co.uk>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2.  This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/mtd/physmap.h>
-#include <linux/mv643xx_eth.h>
-#include <linux/leds.h>
-#include <linux/gpio_keys.h>
-#include <linux/gpio-fan.h>
-#include <linux/input.h>
-#include <linux/i2c.h>
-#include <linux/ata_platform.h>
-#include <linux/gpio.h>
-#include <asm/mach-types.h>
-#include <asm/mach/arch.h>
-#include "common.h"
-#include "mpp.h"
-#include "orion5x.h"
-
-/*****************************************************************************
- * Linkstation LS-CHL Info
- ****************************************************************************/
-
-/*
- * 256K NOR flash Device bus boot chip select
- */
-
-#define LSCHL_NOR_BOOT_BASE	0xf4000000
-#define LSCHL_NOR_BOOT_SIZE	SZ_256K
-
-/*****************************************************************************
- * 256KB NOR Flash on BOOT Device
- ****************************************************************************/
-
-static struct physmap_flash_data lschl_nor_flash_data = {
-	.width = 1,
-};
-
-static struct resource lschl_nor_flash_resource = {
-	.flags	= IORESOURCE_MEM,
-	.start	= LSCHL_NOR_BOOT_BASE,
-	.end	= LSCHL_NOR_BOOT_BASE + LSCHL_NOR_BOOT_SIZE - 1,
-};
-
-static struct platform_device lschl_nor_flash = {
-	.name = "physmap-flash",
-	.id = 0,
-	.dev = {
-		.platform_data	= &lschl_nor_flash_data,
-	},
-	.num_resources = 1,
-	.resource = &lschl_nor_flash_resource,
-};
-
-/*****************************************************************************
- * Ethernet
- ****************************************************************************/
-
-static struct mv643xx_eth_platform_data lschl_eth_data = {
-	.phy_addr = MV643XX_ETH_PHY_ADDR(8),
-};
-
-/*****************************************************************************
- * RTC 5C372a on I2C bus
- ****************************************************************************/
-
-static struct i2c_board_info __initdata lschl_i2c_rtc = {
-	I2C_BOARD_INFO("rs5c372a", 0x32),
-};
-
-/*****************************************************************************
- * LEDs attached to GPIO
- ****************************************************************************/
-
-#define LSCHL_GPIO_LED_ALARM	2
-#define LSCHL_GPIO_LED_INFO	3
-#define LSCHL_GPIO_LED_FUNC	17
-#define LSCHL_GPIO_LED_PWR	0
-
-static struct gpio_led lschl_led_pins[] = {
-	{
-		.name = "alarm:red",
-		.gpio = LSCHL_GPIO_LED_ALARM,
-		.active_low = 1,
-	}, {
-		.name = "info:amber",
-		.gpio = LSCHL_GPIO_LED_INFO,
-		.active_low = 1,
-	}, {
-		.name = "func:blue:top",
-		.gpio = LSCHL_GPIO_LED_FUNC,
-		.active_low = 1,
-	}, {
-		.name = "power:blue:bottom",
-		.gpio = LSCHL_GPIO_LED_PWR,
-	},
-};
-
-static struct gpio_led_platform_data lschl_led_data = {
-	.leds = lschl_led_pins,
-	.num_leds = ARRAY_SIZE(lschl_led_pins),
-};
-
-static struct platform_device lschl_leds = {
-	.name = "leds-gpio",
-	.id = -1,
-	.dev = {
-		.platform_data = &lschl_led_data,
-	},
-};
-
-/*****************************************************************************
- * SATA
- ****************************************************************************/
-static struct mv_sata_platform_data lschl_sata_data = {
-	.n_ports = 2,
-};
-
-/*****************************************************************************
- * LS-CHL specific power off method: reboot
- ****************************************************************************/
-/*
- * On the LS-CHL, the shutdown process is following:
- * - Userland monitors key events until the power switch goes to off position
- * - The board reboots
- * - U-boot starts and goes into an idle mode waiting for the user
- *   to move the switch to ON position
- *
- */
-
-static void lschl_power_off(void)
-{
-	orion5x_restart(REBOOT_HARD, NULL);
-}
-
-/*****************************************************************************
- * General Setup
- ****************************************************************************/
-#define LSCHL_GPIO_USB_POWER	9
-#define LSCHL_GPIO_AUTO_POWER	17
-#define LSCHL_GPIO_POWER	18
-
-/****************************************************************************
- * GPIO Attached Keys
- ****************************************************************************/
-#define LSCHL_GPIO_KEY_FUNC		15
-#define LSCHL_GPIO_KEY_POWER		8
-#define LSCHL_GPIO_KEY_AUTOPOWER	10
-#define LSCHL_SW_POWER		0x00
-#define LSCHL_SW_AUTOPOWER	0x01
-#define LSCHL_SW_FUNC		0x02
-
-static struct gpio_keys_button lschl_buttons[] = {
-	{
-		.type = EV_SW,
-		.code = LSCHL_SW_POWER,
-		.gpio = LSCHL_GPIO_KEY_POWER,
-		.desc = "Power-on Switch",
-		.active_low = 1,
-	}, {
-		.type = EV_SW,
-		.code = LSCHL_SW_AUTOPOWER,
-		.gpio = LSCHL_GPIO_KEY_AUTOPOWER,
-		.desc = "Power-auto Switch",
-		.active_low = 1,
-	}, {
-		.type = EV_SW,
-		.code = LSCHL_SW_FUNC,
-		.gpio = LSCHL_GPIO_KEY_FUNC,
-		.desc = "Function Switch",
-		.active_low = 1,
-	},
-};
-
-static struct gpio_keys_platform_data lschl_button_data = {
-	.buttons = lschl_buttons,
-	.nbuttons = ARRAY_SIZE(lschl_buttons),
-};
-
-static struct platform_device lschl_button_device = {
-	.name = "gpio-keys",
-	.id = -1,
-	.num_resources = 0,
-	.dev = {
-		.platform_data = &lschl_button_data,
-	},
-};
-
-#define LSCHL_GPIO_HDD_POWER	1
-
-/****************************************************************************
- * GPIO Fan
- ****************************************************************************/
-
-#define LSCHL_GPIO_FAN_LOW	16
-#define LSCHL_GPIO_FAN_HIGH	14
-#define LSCHL_GPIO_FAN_LOCK	6
-
-static struct gpio_fan_alarm lschl_alarm = {
-	.gpio = LSCHL_GPIO_FAN_LOCK,
-};
-
-static struct gpio_fan_speed lschl_speeds[] = {
-	{
-		.rpm = 0,
-		.ctrl_val = 3,
-	}, {
-		.rpm = 1500,
-		.ctrl_val = 2,
-	}, {
-		.rpm = 3250,
-		.ctrl_val = 1,
-	}, {
-		.rpm = 5000,
-		.ctrl_val = 0,
-	},
-};
-
-static int lschl_gpio_list[] = {
-	LSCHL_GPIO_FAN_HIGH, LSCHL_GPIO_FAN_LOW,
-};
-
-static struct gpio_fan_platform_data lschl_fan_data = {
-	.num_ctrl = ARRAY_SIZE(lschl_gpio_list),
-	.ctrl = lschl_gpio_list,
-	.alarm = &lschl_alarm,
-	.num_speed = ARRAY_SIZE(lschl_speeds),
-	.speed = lschl_speeds,
-};
-
-static struct platform_device lschl_fan_device = {
-	.name = "gpio-fan",
-	.id = -1,
-	.num_resources = 0,
-	.dev = {
-		.platform_data = &lschl_fan_data,
-	},
-};
-
-/****************************************************************************
- * GPIO Data
- ****************************************************************************/
-
-static unsigned int lschl_mpp_modes[] __initdata = {
-	MPP0_GPIO, /* LED POWER */
-	MPP1_GPIO, /* HDD POWER */
-	MPP2_GPIO, /* LED ALARM */
-	MPP3_GPIO, /* LED INFO */
-	MPP4_UNUSED,
-	MPP5_UNUSED,
-	MPP6_GPIO, /* FAN LOCK */
-	MPP7_GPIO, /* SW INIT */
-	MPP8_GPIO, /* SW POWER */
-	MPP9_GPIO, /* USB POWER */
-	MPP10_GPIO, /* SW AUTO POWER */
-	MPP11_UNUSED,
-	MPP12_UNUSED,
-	MPP13_UNUSED,
-	MPP14_GPIO, /* FAN HIGH */
-	MPP15_GPIO, /* SW FUNC */
-	MPP16_GPIO, /* FAN LOW */
-	MPP17_GPIO, /* LED FUNC */
-	MPP18_UNUSED,
-	MPP19_UNUSED,
-	0,
-};
-
-static void __init lschl_init(void)
-{
-	/*
-	 * Setup basic Orion functions. Needs to be called early.
-	 */
-	orion5x_init();
-
-	orion5x_mpp_conf(lschl_mpp_modes);
-
-	/*
-	 * Configure peripherals.
-	 */
-	orion5x_ehci0_init();
-	orion5x_ehci1_init();
-	orion5x_eth_init(&lschl_eth_data);
-	orion5x_i2c_init();
-	orion5x_sata_init(&lschl_sata_data);
-	orion5x_uart0_init();
-	orion5x_xor_init();
-
-	mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_BOOT_TARGET,
-				    ORION_MBUS_DEVBUS_BOOT_ATTR,
-				    LSCHL_NOR_BOOT_BASE,
-				    LSCHL_NOR_BOOT_SIZE);
-	platform_device_register(&lschl_nor_flash);
-
-	platform_device_register(&lschl_leds);
-
-	platform_device_register(&lschl_button_device);
-
-	platform_device_register(&lschl_fan_device);
-
-	i2c_register_board_info(0, &lschl_i2c_rtc, 1);
-
-	/* usb power on */
-	gpio_set_value(LSCHL_GPIO_USB_POWER, 1);
-
-	/* register power-off method */
-	pm_power_off = lschl_power_off;
-
-	pr_info("%s: finished\n", __func__);
-}
-
-MACHINE_START(LINKSTATION_LSCHL, "Buffalo Linkstation LiveV3 (LS-CHL)")
-	/* Maintainer: Ash Hughes <ashley.hughes@blueyonder.co.uk> */
-	.atag_offset	= 0x100,
-	.nr_irqs	= ORION5X_NR_IRQS,
-	.init_machine	= lschl_init,
-	.map_io		= orion5x_map_io,
-	.init_early	= orion5x_init_early,
-	.init_irq	= orion5x_init_irq,
-	.init_time	= orion5x_timer_init,
-	.fixup		= tag_fixup_mem32,
-	.restart	= orion5x_restart,
-MACHINE_END
-- 
2.7.4


On 04/11/16 12:44, Gregory CLEMENT wrote:
> Hi Ash,
>  
>  On mar., oct. 25 2016, Ash Hughes <sehguh.hsa@gmail.com> wrote:
>
>> Hi all,
>>
>> This patch converts my orion5x ls-chl Linkstation device to device
>> tree.
> I was about to apply your patch but it does not apply on mvebu/dt or
> even on v4.9-rc1.
>
> Could you rebase it?
>
> Thanks,
>
> Gregory
>
>> Signed-off-by: Ashley Hughes <ashley.hughes@blueyonder.co.uk>
>> ---
>>  arch/arm/boot/dts/Makefile           |   1 +
>>  arch/arm/boot/dts/orion5x-lschl.dts  | 171 ++++++++++++++++++
>>  arch/arm/mach-orion5x/Kconfig        |   4 +-
>>  arch/arm/mach-orion5x/Makefile       |   1 -
>>  arch/arm/mach-orion5x/ls-chl-setup.c | 331 -----------------------------------
>>  5 files changed, 174 insertions(+), 334 deletions(-)
>>  create mode 100644 arch/arm/boot/dts/orion5x-lschl.dts
>>  delete mode 100644 arch/arm/mach-orion5x/ls-chl-setup.c
>>
>> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
>> index befcd26..4853049 100644
>> --- a/arch/arm/boot/dts/Makefile
>> +++ b/arch/arm/boot/dts/Makefile
>> @@ -597,6 +597,7 @@ dtb-$(CONFIG_ARCH_ORION5X) += \
>>      orion5x-lacie-ethernet-disk-mini-v2.dtb \
>>      orion5x-linkstation-lsgl.dtb \
>>      orion5x-linkstation-lswtgl.dtb \
>> +    orion5x-lschl.dtb \
>>      orion5x-lswsgl.dtb \
>>      orion5x-maxtor-shared-storage-2.dtb \
>>      orion5x-netgear-wnr854t.dtb \
>> diff --git a/arch/arm/boot/dts/orion5x-lschl.dts b/arch/arm/boot/dts/orion5x-lschl.dts
>> new file mode 100644
>> index 0000000..9474092
>> --- /dev/null
>> +++ b/arch/arm/boot/dts/orion5x-lschl.dts
>> @@ -0,0 +1,171 @@
>> +/*
>> + * Device Tree file for Buffalo Linkstation LS-CHLv3
>> + *
>> + * Copyright (C) 2016 Ash Hughes <ashley.hughes@blueyonder.co.uk>
>> + * Copyright (C) 2015, 2016
>> + * Roger Shimizu <rogershimizu@gmail.com>
>> + *
>> + * This file is dual-licensed: you can use it either under the terms
>> + * of the GPL or the X11 license, at your option. Note that this dual
>> + * licensing only applies to this file, and not this project as a
>> + * whole.
>> + *
>> + *  a) This file is free software; you can redistribute it and/or
>> + *     modify it under the terms of the GNU General Public License as
>> + *     published by the Free Software Foundation; either version 2 of the
>> + *     License, or (at your option) any later version.
>> + *
>> + *     This file 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.
>> + *
>> + * Or, alternatively
>> + *
>> + *  b) Permission is hereby granted, free of charge, to any person
>> + *     obtaining a copy of this software and associated documentation
>> + *     files (the "Software"), to deal in the Software without
>> + *     restriction, including without limitation the rights to use
>> + *     copy, modify, merge, publish, distribute, sublicense, and/or
>> + *     sell copies of the Software, and to permit persons to whom the
>> + *     Software is furnished to do so, subject to the following
>> + *     conditions:
>> + *
>> + *     The above copyright notice and this permission notice shall be
>> + *     included in all copies or substantial portions of the Software.
>> + *
>> + *     THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
>> + *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
>> + *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
>> + *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
>> + *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
>> + *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
>> + *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
>> + *     OTHER DEALINGS IN THE SOFTWARE.
>> + */
>> +
>> +/dts-v1/;
>> +
>> +#include "orion5x-linkstation.dtsi"
>> +#include "mvebu-linkstation-gpio-simple.dtsi"
>> +#include "mvebu-linkstation-fan.dtsi"
>> +#include <dt-bindings/gpio/gpio.h>
>> +
>> +/ {
>> +    model = "Buffalo Linkstation Live v3 (LS-CHL)";
>> +    compatible = "buffalo,lschl", "marvell,orion5x-88f5182", "marvell,orion5x";
>> +
>> +    memory { /* 128 MB */
>> +        device_type = "memory";
>> +        reg = <0x00000000 0x8000000>;
>> +    };
>> +
>> +    gpio_keys {
>> +        func {
>> +            label = "Function Button";
>> +            linux,code = <KEY_OPTION>;
>> +            gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;
>> +        };
>> +
>> +        power-on-switch {
>> +            gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
>> +        };
>> +
>> +        power-auto-switch {
>> +            gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
>> +        };
>> +    };
>> +
>> +    gpio_leds {
>> +        pinctrl-0 = <&pmx_led_power &pmx_led_alarm &pmx_led_info &pmx_led_func>;
>> +        blue-power-led {
>> +            gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
>> +        };
>> +
>> +        red-alarm-led {
>> +            gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
>> +        };
>> +
>> +        amber-info-led {
>> +            gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
>> +        };
>> +
>> +        func {
>> +            label = "lschl:func:blue:top";
>> +            gpios = <&gpio0 17 GPIO_ACTIVE_LOW>;
>> +        };
>> +    };
>> +
>> +    gpio_fan {
>> +        gpios = <&gpio0 14 GPIO_ACTIVE_LOW
>> +             &gpio0 16 GPIO_ACTIVE_LOW>;
>> +
>> +        alarm-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
>> +    };
>> +};
>> +
>> +&pinctrl {
>> +    pmx_led_power: pmx-leds {
>> +        marvell,pins = "mpp0";
>> +        marvell,function = "gpio";
>> +    };
>> +
>> +    pmx_power_hdd: pmx-power-hdd {
>> +        marvell,pins = "mpp1";
>> +        marvell,function = "gpio";
>> +    };
>> +
>> +    pmx_led_alarm: pmx-leds {
>> +        marvell,pins = "mpp2";
>> +        marvell,function = "gpio";
>> +    };
>> +
>> +    pmx_led_info: pmx-leds {
>> +        marvell,pins = "mpp3";
>> +        marvell,function = "gpio";
>> +    };
>> +
>> +    pmx_fan_lock: pmx-fan-lock {
>> +        marvell,pins = "mpp6";
>> +        marvell,function = "gpio";
>> +    };
>> +
>> +    pmx_power_switch: pmx-power-switch {
>> +        marvell,pins = "mpp8", "mpp10", "mpp15";
>> +        marvell,function = "gpio";
>> +    };
>> +
>> +    pmx_power_usb: pmx-power-usb {
>> +        marvell,pins = "mpp9";
>> +        marvell,function = "gpio";
>> +    };
>> +
>> +    pmx_fan_high: pmx-fan-high {
>> +        marvell,pins = "mpp14";
>> +        marvell,function = "gpio";
>> +    };
>> +
>> +    pmx_fan_low: pmx-fan-low {
>> +        marvell,pins = "mpp16";
>> +        marvell,function = "gpio";
>> +    };
>> +
>> +    pmx_led_func: pmx-leds {
>> +        marvell,pins = "mpp17";
>> +        marvell,function = "gpio";
>> +    };
>> +
>> +    pmx_sw_init: pmx-sw-init {
>> +        marvell,pins = "mpp7";
>> +        marvell,function = "gpio";
>> +    };
>> +};
>> +
>> +&hdd_power {
>> +    gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
>> +};
>> +
>> +&usb_power {
>> +    gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
>> +};
>> +
>> diff --git a/arch/arm/mach-orion5x/Kconfig b/arch/arm/mach-orion5x/Kconfig
>> index 89bb0fc..793efa9 100644
>> --- a/arch/arm/mach-orion5x/Kconfig
>> +++ b/arch/arm/mach-orion5x/Kconfig
>> @@ -85,8 +85,8 @@ config MACH_LINKSTATION_PRO
>>        v2 devices are supported.
>>  
>>  config MACH_LINKSTATION_LSCHL
>> -    bool "Buffalo Linkstation Live v3 (LS-CHL)"
>> -    select I2C_BOARDINFO if I2C
>> +    bool "Buffalo Linkstation Live v3 (LS-CHL) (Flattened Device Tree)"
>> +    select ARCH_ORION5X_DT
>>      help
>>        Say 'Y' here if you want your kernel to support the
>>        Buffalo Linkstation Live v3 (LS-CHL) platform.
>> diff --git a/arch/arm/mach-orion5x/Makefile b/arch/arm/mach-orion5x/Makefile
>> index 4b2502b..ae91872 100644
>> --- a/arch/arm/mach-orion5x/Makefile
>> +++ b/arch/arm/mach-orion5x/Makefile
>> @@ -18,7 +18,6 @@ obj-$(CONFIG_MACH_WNR854T)    += wnr854t-setup.o
>>  obj-$(CONFIG_MACH_RD88F5181L_GE)    += rd88f5181l-ge-setup.o
>>  obj-$(CONFIG_MACH_RD88F5181L_FXO)    += rd88f5181l-fxo-setup.o
>>  obj-$(CONFIG_MACH_RD88F6183AP_GE)    += rd88f6183ap-ge-setup.o
>> -obj-$(CONFIG_MACH_LINKSTATION_LSCHL)    += ls-chl-setup.o
>>  
>>  obj-$(CONFIG_ARCH_ORION5X_DT)        += board-dt.o
>>  obj-$(CONFIG_MACH_D2NET_DT)    += board-d2net.o
>> diff --git a/arch/arm/mach-orion5x/ls-chl-setup.c b/arch/arm/mach-orion5x/ls-chl-setup.c
>> deleted file mode 100644
>> index dfdaa8a..0000000
>> --- a/arch/arm/mach-orion5x/ls-chl-setup.c
>> +++ /dev/null
>> @@ -1,331 +0,0 @@
>> -/*
>> - * arch/arm/mach-orion5x/ls-chl-setup.c
>> - *
>> - * Maintainer: Ash Hughes <ashley.hughes@blueyonder.co.uk>
>> - *
>> - * This file is licensed under the terms of the GNU General Public
>> - * License version 2.  This program is licensed "as is" without any
>> - * warranty of any kind, whether express or implied.
>> - */
>> -
>> -#include <linux/kernel.h>
>> -#include <linux/init.h>
>> -#include <linux/platform_device.h>
>> -#include <linux/mtd/physmap.h>
>> -#include <linux/mv643xx_eth.h>
>> -#include <linux/leds.h>
>> -#include <linux/gpio_keys.h>
>> -#include <linux/gpio-fan.h>
>> -#include <linux/input.h>
>> -#include <linux/i2c.h>
>> -#include <linux/ata_platform.h>
>> -#include <linux/gpio.h>
>> -#include <asm/mach-types.h>
>> -#include <asm/mach/arch.h>
>> -#include "common.h"
>> -#include "mpp.h"
>> -#include "orion5x.h"
>> -
>> -/*****************************************************************************
>> - * Linkstation LS-CHL Info
>> - ****************************************************************************/
>> -
>> -/*
>> - * 256K NOR flash Device bus boot chip select
>> - */
>> -
>> -#define LSCHL_NOR_BOOT_BASE    0xf4000000
>> -#define LSCHL_NOR_BOOT_SIZE    SZ_256K
>> -
>> -/*****************************************************************************
>> - * 256KB NOR Flash on BOOT Device
>> - ****************************************************************************/
>> -
>> -static struct physmap_flash_data lschl_nor_flash_data = {
>> -    .width = 1,
>> -};
>> -
>> -static struct resource lschl_nor_flash_resource = {
>> -    .flags    = IORESOURCE_MEM,
>> -    .start    = LSCHL_NOR_BOOT_BASE,
>> -    .end    = LSCHL_NOR_BOOT_BASE + LSCHL_NOR_BOOT_SIZE - 1,
>> -};
>> -
>> -static struct platform_device lschl_nor_flash = {
>> -    .name = "physmap-flash",
>> -    .id = 0,
>> -    .dev = {
>> -        .platform_data    = &lschl_nor_flash_data,
>> -    },
>> -    .num_resources = 1,
>> -    .resource = &lschl_nor_flash_resource,
>> -};
>> -
>> -/*****************************************************************************
>> - * Ethernet
>> - ****************************************************************************/
>> -
>> -static struct mv643xx_eth_platform_data lschl_eth_data = {
>> -    .phy_addr = MV643XX_ETH_PHY_ADDR(8),
>> -};
>> -
>> -/*****************************************************************************
>> - * RTC 5C372a on I2C bus
>> - ****************************************************************************/
>> -
>> -static struct i2c_board_info __initdata lschl_i2c_rtc = {
>> -    I2C_BOARD_INFO("rs5c372a", 0x32),
>> -};
>> -
>> -/*****************************************************************************
>> - * LEDs attached to GPIO
>> - ****************************************************************************/
>> -
>> -#define LSCHL_GPIO_LED_ALARM    2
>> -#define LSCHL_GPIO_LED_INFO    3
>> -#define LSCHL_GPIO_LED_FUNC    17
>> -#define LSCHL_GPIO_LED_PWR    0
>> -
>> -static struct gpio_led lschl_led_pins[] = {
>> -    {
>> -        .name = "alarm:red",
>> -        .gpio = LSCHL_GPIO_LED_ALARM,
>> -        .active_low = 1,
>> -    }, {
>> -        .name = "info:amber",
>> -        .gpio = LSCHL_GPIO_LED_INFO,
>> -        .active_low = 1,
>> -    }, {
>> -        .name = "func:blue:top",
>> -        .gpio = LSCHL_GPIO_LED_FUNC,
>> -        .active_low = 1,
>> -    }, {
>> -        .name = "power:blue:bottom",
>> -        .gpio = LSCHL_GPIO_LED_PWR,
>> -    },
>> -};
>> -
>> -static struct gpio_led_platform_data lschl_led_data = {
>> -    .leds = lschl_led_pins,
>> -    .num_leds = ARRAY_SIZE(lschl_led_pins),
>> -};
>> -
>> -static struct platform_device lschl_leds = {
>> -    .name = "leds-gpio",
>> -    .id = -1,
>> -    .dev = {
>> -        .platform_data = &lschl_led_data,
>> -    },
>> -};
>> -
>> -/*****************************************************************************
>> - * SATA
>> - ****************************************************************************/
>> -static struct mv_sata_platform_data lschl_sata_data = {
>> -    .n_ports = 2,
>> -};
>> -
>> -/*****************************************************************************
>> - * LS-CHL specific power off method: reboot
>> - ****************************************************************************/
>> -/*
>> - * On the LS-CHL, the shutdown process is following:
>> - * - Userland monitors key events until the power switch goes to off position
>> - * - The board reboots
>> - * - U-boot starts and goes into an idle mode waiting for the user
>> - *   to move the switch to ON position
>> - *
>> - */
>> -
>> -static void lschl_power_off(void)
>> -{
>> -    orion5x_restart(REBOOT_HARD, NULL);
>> -}
>> -
>> -/*****************************************************************************
>> - * General Setup
>> - ****************************************************************************/
>> -#define LSCHL_GPIO_USB_POWER    9
>> -#define LSCHL_GPIO_AUTO_POWER    17
>> -#define LSCHL_GPIO_POWER    18
>> -
>> -/****************************************************************************
>> - * GPIO Attached Keys
>> - ****************************************************************************/
>> -#define LSCHL_GPIO_KEY_FUNC        15
>> -#define LSCHL_GPIO_KEY_POWER        8
>> -#define LSCHL_GPIO_KEY_AUTOPOWER    10
>> -#define LSCHL_SW_POWER        0x00
>> -#define LSCHL_SW_AUTOPOWER    0x01
>> -#define LSCHL_SW_FUNC        0x02
>> -
>> -static struct gpio_keys_button lschl_buttons[] = {
>> -    {
>> -        .type = EV_SW,
>> -        .code = LSCHL_SW_POWER,
>> -        .gpio = LSCHL_GPIO_KEY_POWER,
>> -        .desc = "Power-on Switch",
>> -        .active_low = 1,
>> -    }, {
>> -        .type = EV_SW,
>> -        .code = LSCHL_SW_AUTOPOWER,
>> -        .gpio = LSCHL_GPIO_KEY_AUTOPOWER,
>> -        .desc = "Power-auto Switch",
>> -        .active_low = 1,
>> -    }, {
>> -        .type = EV_SW,
>> -        .code = LSCHL_SW_FUNC,
>> -        .gpio = LSCHL_GPIO_KEY_FUNC,
>> -        .desc = "Function Switch",
>> -        .active_low = 1,
>> -    },
>> -};
>> -
>> -static struct gpio_keys_platform_data lschl_button_data = {
>> -    .buttons = lschl_buttons,
>> -    .nbuttons = ARRAY_SIZE(lschl_buttons),
>> -};
>> -
>> -static struct platform_device lschl_button_device = {
>> -    .name = "gpio-keys",
>> -    .id = -1,
>> -    .num_resources = 0,
>> -    .dev = {
>> -        .platform_data = &lschl_button_data,
>> -    },
>> -};
>> -
>> -#define LSCHL_GPIO_HDD_POWER    1
>> -
>> -/****************************************************************************
>> - * GPIO Fan
>> - ****************************************************************************/
>> -
>> -#define LSCHL_GPIO_FAN_LOW    16
>> -#define LSCHL_GPIO_FAN_HIGH    14
>> -#define LSCHL_GPIO_FAN_LOCK    6
>> -
>> -static struct gpio_fan_alarm lschl_alarm = {
>> -    .gpio = LSCHL_GPIO_FAN_LOCK,
>> -};
>> -
>> -static struct gpio_fan_speed lschl_speeds[] = {
>> -    {
>> -        .rpm = 0,
>> -        .ctrl_val = 3,
>> -    }, {
>> -        .rpm = 1500,
>> -        .ctrl_val = 2,
>> -    }, {
>> -        .rpm = 3250,
>> -        .ctrl_val = 1,
>> -    }, {
>> -        .rpm = 5000,
>> -        .ctrl_val = 0,
>> -    },
>> -};
>> -
>> -static int lschl_gpio_list[] = {
>> -    LSCHL_GPIO_FAN_HIGH, LSCHL_GPIO_FAN_LOW,
>> -};
>> -
>> -static struct gpio_fan_platform_data lschl_fan_data = {
>> -    .num_ctrl = ARRAY_SIZE(lschl_gpio_list),
>> -    .ctrl = lschl_gpio_list,
>> -    .alarm = &lschl_alarm,
>> -    .num_speed = ARRAY_SIZE(lschl_speeds),
>> -    .speed = lschl_speeds,
>> -};
>> -
>> -static struct platform_device lschl_fan_device = {
>> -    .name = "gpio-fan",
>> -    .id = -1,
>> -    .num_resources = 0,
>> -    .dev = {
>> -        .platform_data = &lschl_fan_data,
>> -    },
>> -};
>> -
>> -/****************************************************************************
>> - * GPIO Data
>> - ****************************************************************************/
>> -
>> -static unsigned int lschl_mpp_modes[] __initdata = {
>> -    MPP0_GPIO, /* LED POWER */
>> -    MPP1_GPIO, /* HDD POWER */
>> -    MPP2_GPIO, /* LED ALARM */
>> -    MPP3_GPIO, /* LED INFO */
>> -    MPP4_UNUSED,
>> -    MPP5_UNUSED,
>> -    MPP6_GPIO, /* FAN LOCK */
>> -    MPP7_GPIO, /* SW INIT */
>> -    MPP8_GPIO, /* SW POWER */
>> -    MPP9_GPIO, /* USB POWER */
>> -    MPP10_GPIO, /* SW AUTO POWER */
>> -    MPP11_UNUSED,
>> -    MPP12_UNUSED,
>> -    MPP13_UNUSED,
>> -    MPP14_GPIO, /* FAN HIGH */
>> -    MPP15_GPIO, /* SW FUNC */
>> -    MPP16_GPIO, /* FAN LOW */
>> -    MPP17_GPIO, /* LED FUNC */
>> -    MPP18_UNUSED,
>> -    MPP19_UNUSED,
>> -    0,
>> -};
>> -
>> -static void __init lschl_init(void)
>> -{
>> -    /*
>> -     * Setup basic Orion functions. Needs to be called early.
>> -     */
>> -    orion5x_init();
>> -
>> -    orion5x_mpp_conf(lschl_mpp_modes);
>> -
>> -    /*
>> -     * Configure peripherals.
>> -     */
>> -    orion5x_ehci0_init();
>> -    orion5x_ehci1_init();
>> -    orion5x_eth_init(&lschl_eth_data);
>> -    orion5x_i2c_init();
>> -    orion5x_sata_init(&lschl_sata_data);
>> -    orion5x_uart0_init();
>> -    orion5x_xor_init();
>> -
>> -    mvebu_mbus_add_window_by_id(ORION_MBUS_DEVBUS_BOOT_TARGET,
>> -                    ORION_MBUS_DEVBUS_BOOT_ATTR,
>> -                    LSCHL_NOR_BOOT_BASE,
>> -                    LSCHL_NOR_BOOT_SIZE);
>> -    platform_device_register(&lschl_nor_flash);
>> -
>> -    platform_device_register(&lschl_leds);
>> -
>> -    platform_device_register(&lschl_button_device);
>> -
>> -    platform_device_register(&lschl_fan_device);
>> -
>> -    i2c_register_board_info(0, &lschl_i2c_rtc, 1);
>> -
>> -    /* usb power on */
>> -    gpio_set_value(LSCHL_GPIO_USB_POWER, 1);
>> -
>> -    /* register power-off method */
>> -    pm_power_off = lschl_power_off;
>> -
>> -    pr_info("%s: finished\n", __func__);
>> -}
>> -
>> -MACHINE_START(LINKSTATION_LSCHL, "Buffalo Linkstation LiveV3 (LS-CHL)")
>> -    /* Maintainer: Ash Hughes <ashley.hughes@blueyonder.co.uk> */
>> -    .atag_offset    = 0x100,
>> -    .nr_irqs    = ORION5X_NR_IRQS,
>> -    .init_machine    = lschl_init,
>> -    .map_io        = orion5x_map_io,
>> -    .init_early    = orion5x_init_early,
>> -    .init_irq    = orion5x_init_irq,
>> -    .init_time    = orion5x_timer_init,
>> -    .fixup        = tag_fixup_mem32,
>> -    .restart    = orion5x_restart,
>> -MACHINE_END
>> -- 2.7.4
>>
>>

^ permalink raw reply related

* [PATCH] MAINTAINERS: Update Broadcom Vulcan maintainer email
From: Florian Fainelli @ 2016-11-06  0:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1478249673-8163-1-git-send-email-c.jayachandran@gmail.com>

Le 04/11/2016 ? 01:54, Jayachandran C a ?crit :
> Update Broadcom Vulcan maintainer's email address, the broadcom.com
> address is no longer valid.
> 
> Signed-off-by: Jayachandran C <c.jayachandran@gmail.com>

Applied; thanks JC!
-- 
Florian

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox