* Re: [PATCH 2/2] Documentation: DT: Add bmi160 imu binding
From: Rob Herring @ 2016-11-10 18:55 UTC (permalink / raw)
To: Marcin Niestroj
Cc: Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Daniel Baluta, Gregor Boirie,
Sanchayan Maity, Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161103112527.29623-2-m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
On Thu, Nov 03, 2016 at 12:25:27PM +0100, Marcin Niestroj wrote:
> This adds documentation for Bosch BMI160 Inertial Measurement Unit
> device-tree bindings.
>
> Signed-off-by: Marcin Niestroj <m.niestroj-z3quKL4iOrmQ6ZAhV5LmOA@public.gmane.org>
> ---
> .../devicetree/bindings/iio/imu/bmi160.txt | 34 ++++++++++++++++++++++
> 1 file changed, 34 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/imu/bmi160.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/imu/bmi160.txt b/Documentation/devicetree/bindings/iio/imu/bmi160.txt
> new file mode 100644
> index 0000000..b02ef3e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/imu/bmi160.txt
> @@ -0,0 +1,34 @@
> +Bosch BMI160 - Inertial Measurement Unit with Accelerometer, Gyroscope
> +and externally connectable Magnetometer
> +
> +https://www.bosch-sensortec.com/bst/products/all_products/bmi160
> +
> +Required properties:
> + - compatible : should be "bosch,bmi160"
> + - reg : the I2C address or SPI chip select number of the sensor
> + - spi-max-frequency : set maximum clock frequency (only for SPI)
> +
> +Optional properties:
> + - interrupt-parent : should be the phandle of the interrupt controller
> + - interrupts : interrupt mapping for GPIO IRQ, must be IRQ_TYPE_LEVEL_LOW
The fact that a GPIO is typically used is outside the scope of this doc.
> + - interrupt-names : set to "INT2" if using INT2 pin
Normally there's no point to have names property when there is a single
interrupt. However, it seems this could be either INT1 or INT2 or both
connected. You need to specify all the options.
Rob
^ permalink raw reply
* Re: [PATCH 13/13] Documentation: synopsys-dw-mshc: remove the unused properties
From: Rob Herring @ 2016-11-10 18:47 UTC (permalink / raw)
To: Jaehoon Chung
Cc: linux-mmc-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
ulf.hansson-QSEj5FYQhm4dnm+yROfE0A, krzk-DgEjT+Ai2ygdnm+yROfE0A,
heiko-4mtYJXux2i+zQB+pC5nmwQ, shawn.lin-TNX95d0MmH7DzftRWevZcw
In-Reply-To: <20161103062135.10697-14-jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
On Thu, Nov 03, 2016 at 03:21:35PM +0900, Jaehoon Chung wrote:
> "support-highspeed" was the obsoleted property.
> And "broken-cd" is not synopsys specific property.
> It can be referred to mmc.txt binding Documentation.
>
> Signed-off-by: Jaehoon Chung <jh80.chung-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> ---
> Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 5 -----
> 1 file changed, 5 deletions(-)
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
From: Mark Rutland @ 2016-11-10 18:30 UTC (permalink / raw)
To: Anurup M
Cc: devicetree, linux-arm-kernel, linux-doc, will.deacon, corbet,
catalin.marinas, robh+dt, arnd, f.fainelli, rmk+kernel, krzk,
anurup.m, zhangshaokun, tanxiaojun, xuwei5, sanil.kumar,
john.garry, gabriele.paoloni, shiju.jose, wangkefeng.wang,
guohanjun, shyju.pv, linuxarm
In-Reply-To: <1478151727-20250-6-git-send-email-anurup.m@huawei.com>
Hi,
On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:
> 1) Device tree bindings for Hisilicon SoC PMU.
> 2) Add example for Hisilicon L3 cache, MN and DDRC PMU.
>
> Signed-off-by: Anurup M <anurup.m@huawei.com>
> Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
> ---
> .../devicetree/bindings/arm/hisilicon/pmu.txt | 127 +++++++++++++++++++++
> 1 file changed, 127 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>
> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> new file mode 100644
> index 0000000..e7b35e0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> @@ -0,0 +1,127 @@
> +Hisilicon SoC hip05/06/07 ARMv8 PMU
> +===================================
> +
> +The Hisilicon SoC chips like hip05/06/07 etc. consist of varous independent
> +system device PMU's such as L3 cache (L3C), Miscellaneous Nodes(MN) and DDR
s/PMU's/PMUs/
> +comtroller. These PMU devices are independent and have hardware logic to
s/comtroller/controller/
> +gather statistics and performance information.
> +
> +HiSilicon SoC chip is encapsulated by multiple CPU and IO die's. The CPU die
s/die's/dies/
> +is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
> +is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
> +e.g. In the case of hip05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
> +
> +The Hisilicon SoC PMU DT node bindigs for uncore PMU devices are as below.
s/bindigs/bindings/
> +For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
> +the parent node will be the djtag node of the corresponding CPU die(SCCL).
> +
> +For uncore PMU devices there are some common required properties as detailed
> +below.
> +
> +Required properties:
> + - compatible : This field contain two values. The first value is
> + always "hisilicon" and second value is the Module type as shown
> + in below examples:
Just say:
- Compatible: should contain one of:
> + (a) "hisilicon,hisi-pmu-l3c-v1" for Hisilicon SoC L3C PMU
> + device (Version 1)
> + (b) "hisilicon,hisi-pmu-mn-v1" for Hisilicon SoC MN PMU
> + device (Version 1)
> + (c) "hisilicon,hisi-pmu-ddrc-v1" for Hisilicon SoC DDRC PMU
> + device (Version 1)
> + The hip05/06/07 chips have v1 hardware for L3C, MN and DDRC.
> +
> + - scl-id : The Super Cluster ID. This can be the ID of the CPU die
> + or IO die in the chip.
What's this needed for?
> + - num-events : No of events supported by this PMU device.
> +
> + - num-counters : No of hardware counters available for counting.
This isn't probeable or well-known?
> +
> +L3 cache
> +--------
> +The L3 cache is dedicated for each SCCL and hence there are separate DT nodes
> +for L3 cache for each SCCL. For L3 cache PMU the additional required properties
> +are
> + - counter-reg : Counter register offset.
> +
> + - evtype-reg : Event select register offset.
> +
> + - evctrl-reg : Event counting control(LAUCTRL) register offset.
Surely for a given revision of the chip these offsets are known? i.e.
surely the compatible string implies specific offsets?
> + - event-en : Event enable value.
Huh?
> + - module-id : Module ID to input for djtag. This property is an array of
> + module_id for each L3 cache banks.
> +
> + - num-banks : Number of banks or instances of the device.
What's a bank? Surely they have separate instances of the PMU?
What order are these in?
> + - cfgen-map : Config enable array to select the bank.
Huh?
> +Miscellaneous Node
> +-------------------
> +The MN is dedicated for each SCCL and hence there are separate DT nodes for MN
> +for each SCCL. For MN PMU the additional required properties are
> + - counter-reg : Counter register offset.
> +
> + - evtype-reg : Event select register offset.
> +
> + - evctrl-reg : Event counting control register offset.
Likewise, surely this is well-known for a given revision of the chip?
> +
> + - module-id : Module ID to input for djtag. As MN doesnot have multiple banks
> + this property is a single value.
> +
> + - cfgen-map : Config enable to select the bank. For MN it is a single value
> +
> + - event-en : Event enable value.
Same comments as for the L3 cache nodes
[...]
> +DDR controller
> +--------------
> +Each SCCL in Hip05/06/07 chips have 2 DDR channels and hence 2 DDR controllers.
> +There are separate DT nodes for each DDR channel.
> +For DDRC PMU the additional required properties are
> +
> + - ch-id : DDRC Channel ID.
Why is this necessary?
Thanks,
Mark.
> + - reg : Register base address and range for the DDRC channel.
> +
> +Example:
> + /* DDRC for CPU die scl #2 Channel #1 for hip05 */
> + pmu_sccl0_ddrc1: pmu_ddrc1@80358000 {
> + compatible = "hisilicon,hisi-pmu-ddrc-v1";
> + scl-id = <0x02>;
> + ch-id = <0x1>;
> + num-events = <0x0D>;
> + num-counters = <0x04>;
> + reg = <0x80358000 0x10000>; /* TOTEMC DDRC1 */
> + };
> --
> 2.1.4
>
^ permalink raw reply
* Re: [PATCH v2 1/3] ARM: dts: imx6qdl-apalis: Do not rely on DDC I2C bus bitbang for HDMI
From: Vladimir Zapolskiy @ 2016-11-10 18:29 UTC (permalink / raw)
To: Stefan Agner, Philipp Zabel
Cc: maitysanchayan-Re5JQEeQqe8AvxtiuMwx3w, Shawn Guo,
marcel.ziswiler-2KBjVHiyJgBBDgjK7y7TUQ,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <57268103e8fabf344723eae99b45ec7d-XLVq0VzYD2Y@public.gmane.org>
Hi Stefan, Philipp,
On 11/09/2016 02:50 AM, Stefan Agner wrote:
> On 2016-11-08 09:33, maitysanchayan-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote:
>> Hello Shawn,
>>
>> On 16-10-22 15:43:04, Vladimir Zapolskiy wrote:
>>> Hi Shawn,
>>>
>>> On 10/22/2016 06:25 AM, Shawn Guo wrote:
>>>> On Mon, Sep 19, 2016 at 10:41:51AM +0530, Sanchayan Maity wrote:
>>>>> Remove the use of DDC I2C bus bitbang to support reading of EDID
>>>>> and rely on support from internal HDMI I2C master controller instead.
>>>>> As a result remove the device tree property ddc-i2c-bus.
>>>>>
>>>>> Signed-off-by: Sanchayan Maity <maitysanchayan-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>>>
>>>> I think that the dw-hdmi i2c support [1] is a prerequisite of this
>>>> patch. I do not see it lands on v4.9-rc1. Or am I missing something?
>>>>
>>>> Shawn
>>>>
>>>> [1] https://patchwork.kernel.org/patch/9296883/
>>>>
>>>
>>> I'm adding Philipp to Cc, since he is the last one who tested the change
>>> and helped me to push the change to the mainline:
>>>
>>> https://lists.freedesktop.org/archives/dri-devel/2016-September/118569.html
>>>
>>> The problem is that there is no official DW HDMI bridge maintainer, may be
>>> you can review the change, and if you find it satisfactory push it through
>>> ARM/iMX tree.
>>
>> Shawn, is it okay if that patch goes through your ARM/iMX tree?
>
> I don't think it makes sense that the DRM bridge changes go through
> Shawn's tree. Dave should merge Philipps pull request...
>
Philipp, do you mind to submit a rebased pull request one more time?
--
With best wishes,
Vladimir
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 2/3] irqchip: mtk-cirq: Add mediatek mtk-cirq implement
From: Marc Zyngier @ 2016-11-10 18:24 UTC (permalink / raw)
To: Youlin Pei
Cc: Rob Herring, Matthias Brugger, Thomas Gleixner, Jason Cooper,
Mark Rutland, Russell King, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
hongkun.cao-NuS5LvNUpcJWk0Htik3J/w,
yong.wu-NuS5LvNUpcJWk0Htik3J/w, erin.lo-NuS5LvNUpcJWk0Htik3J/w,
chieh-jay.liu-NuS5LvNUpcJWk0Htik3J/w
In-Reply-To: <1478573833.32099.11.camel@mtksdaap41>
On 08/11/16 02:57, Youlin Pei wrote:
> On Fri, 2016-11-04 at 22:21 +0000, Marc Zyngier wrote:
>> On Fri, Nov 04 2016 at 04:42:57 AM, Youlin Pei <youlin.pei-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org> wrote:
>>> On Tue, 2016-11-01 at 20:49 +0000, Marc Zyngier wrote:
>>>> On Tue, Nov 01 2016 at 11:52:01 AM, Youlin Pei <youlin.pei-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org> wrote:
>>>>> In Mediatek SOCs, the CIRQ is a low power interrupt controller
>>>>> designed to works outside MCUSYS which comprises with Cortex-Ax
>>>>> cores,CCI and GIC.
>>>>>
>>>>> The CIRQ controller is integrated in between MCUSYS( include
>>>>> Cortex-Ax, CCI and GIC ) and interrupt sources as the second
>>>>> level interrupt controller. The external interrupts which outside
>>>>> MCUSYS will feed through CIRQ then bypass to GIC. CIRQ can monitors
>>>>> all edge trigger interupts. When an edge interrupt is triggered,
>>>>> CIRQ can record the status and generate a pulse signal to GIC when
>>>>> flush command executed.
>>>>>
>>>>> When system enters sleep mode, MCUSYS will be turned off to improve
>>>>> power consumption, also GIC is power down. The edge trigger interrupts
>>>>> will be lost in this scenario without CIRQ.
>>>>>
>>>>> This commit provides the CIRQ irqchip implement.
>>>>>
>>>>> Signed-off-by: Youlin Pei <youlin.pei-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
>>>>> ---
>>>>> drivers/irqchip/Makefile | 2 +-
>>>>> drivers/irqchip/irq-mtk-cirq.c | 262 ++++++++++++++++++++++++++++++++++++++++
>>>>> 2 files changed, 263 insertions(+), 1 deletion(-)
>>>>> create mode 100644 drivers/irqchip/irq-mtk-cirq.c
>>>>>
>>>>> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
>>>>> index e4dbfc8..8f33580 100644
>>>>> --- a/drivers/irqchip/Makefile
>>>>> +++ b/drivers/irqchip/Makefile
>>>>> @@ -60,7 +60,7 @@ obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
>>>>> obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
>>>>> obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
>>>>> obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o
>>>>> -obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o
>>>>> +obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o irq-mtk-cirq.o
>>>>> obj-$(CONFIG_ARCH_DIGICOLOR) += irq-digicolor.o
>>>>> obj-$(CONFIG_RENESAS_H8300H_INTC) += irq-renesas-h8300h.o
>>>>> obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o
>>>>> diff --git a/drivers/irqchip/irq-mtk-cirq.c b/drivers/irqchip/irq-mtk-cirq.c
>>>>> new file mode 100644
>>>>> index 0000000..fc43ef3
>>>>> --- /dev/null
>>>>> +++ b/drivers/irqchip/irq-mtk-cirq.c
>>>>> @@ -0,0 +1,262 @@
>>>>> +/*
>>>>> + * Copyright (c) 2016 MediaTek Inc.
>>>>> + * Author: Youlin.Pei <youlin.pei-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
>>>>> + *
>>>>> + * 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.
>>>>> + */
>>>>> +
>>>>> +#include <linux/irq.h>
>>>>> +#include <linux/irqchip.h>
>>>>> +#include <linux/irqdomain.h>
>>>>> +#include <linux/of.h>
>>>>> +#include <linux/of_irq.h>
>>>>> +#include <linux/of_address.h>
>>>>> +#include <linux/io.h>
>>>>> +#include <linux/slab.h>
>>>>> +#include <linux/syscore_ops.h>
>>>>> +
>>>>> +#define CIRQ_ACK 0x40
>>>>> +#define CIRQ_MASK_SET 0xc0
>>>>> +#define CIRQ_MASK_CLR 0x100
>>>>> +#define CIRQ_SENS_SET 0x180
>>>>> +#define CIRQ_SENS_CLR 0x1c0
>>>>> +#define CIRQ_POL_SET 0x240
>>>>> +#define CIRQ_POL_CLR 0x280
>>>>> +#define CIRQ_CONTROL 0x300
>>>>> +
>>>>> +#define CIRQ_EN 0x1
>>>>> +#define CIRQ_EDGE 0x2
>>>>> +#define CIRQ_FLUSH 0x4
>>>>> +
>>>>> +#define CIRQ_IRQ_NUM 0x200
>>>>> +
>>>>> +struct mtk_cirq_chip_data {
>>>>> + void __iomem *base;
>>>>> + unsigned int ext_irq_start;
>>>>> +};
>>>>> +
>>>>> +static struct mtk_cirq_chip_data *cirq_data;
>>>>
>>>> Are you guaranteed that you'll only ever have a single CIRQ in any
>>>> system?
>>>
>>> In Mediatek's SOC, only hace a single CIRQ.
>>>
>>>>
>>>>> +
>>>>> +static void mtk_cirq_write_mask(struct irq_data *data, unsigned int offset)
>>>>> +{
>>>>> + struct mtk_cirq_chip_data *chip_data = data->chip_data;
>>>>> + unsigned int cirq_num = data->hwirq;
>>>>> + u32 mask = 1 << (cirq_num % 32);
>>>>> +
>>>>> + writel(mask, chip_data->base + offset + (cirq_num / 32) * 4);
>>>>
>>>> Why can't you use the relaxed accessors?
>>>
>>> It seems that i use wrong function, i will change the writel to
>>> writel_relaxed in next ve
>>>
>>>>
>>>>> +}
>>>>> +
>>>>> +static void mtk_cirq_mask(struct irq_data *data)
>>>>> +{
>>>>> + mtk_cirq_write_mask(data, CIRQ_MASK_SET);
>>>>> + irq_chip_mask_parent(data);
>>>>> +}
>>>>> +
>>>>> +static void mtk_cirq_unmask(struct irq_data *data)
>>>>> +{
>>>>> + mtk_cirq_write_mask(data, CIRQ_MASK_CLR);
>>>>> + irq_chip_unmask_parent(data);
>>>>> +}
>>>>> +
>>>>> +static void mtk_cirq_eoi(struct irq_data *data)
>>>>> +{
>>>>> + mtk_cirq_write_mask(data, CIRQ_ACK);
>>>>
>>>> EOI and ACK have very different semantics. What is this write actually
>>>> doing? Also, you're now doing an additional MMIO write on each interrupt
>>>> EOI, doubling its cost. Do you really need to do actually signal the HW
>>>> that we've EOIed an interrupt? I would have hoped that you'd be able to
>>>> put it in "bypass" mode as long as you're not suspending...
>>>>
>>>
>>> When external interrupt happened, CIRQ status register record the status
>>> even CIRQ is not enabled. when execute the flush command, CIRQ will
>>> resend the signals according to the status. So if don't clear the
>>> status, CIRQ will resend the wrong signals. the ACK write operation will
>>> clear the status.
>>
>> But at this time, we haven't suspended yet, and there is nothing to
>> replay. Also, you only enable the edge capture when suspending. So what
>> are you ACKing here? Can't you simply clear everything right when
>> suspending and not do it at all on the fast path?
>
> I had planned to ACK the status in cirq suspend callback, but
> encountered a problem.
> following is a piece of code from
> http://lxr.free-electrons.com/source/kernel/power/suspend.c#L361
>
> arch_suspend_disable_irqs(); ---step1
> BUG_ON(!irqs_disabled());
>
> error = syscore_suspend();
> |
> ---cirq suspend(); ---step2
>
> if ack the status in cirq suspend, the interrupts will be lost which
> happened during step1 to step2.
>
> So would you mind give me some suggestions about this?
> Thanks a lot!
Right. So maybe there is a way:
- On suspend you can iterate over all the cirq interrupts that have been
recorded
- For each of those, you inspect if it is pending at the GIC level
(using the irq_get_irqchip_state helper)
- if not pending, you Ack it, because it cannot be delivered anymore
- If it is pending, then you know it happened between step 1 and step 2.
- You then have the choice of either going into suspend and waking up
immediately, or failing the suspend.
Thoughts?
M.
--
Jazz is not dead. It just smells funny...
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH V3 1/9] PM / OPP: Reword binding supporting multiple regulators per device
From: Viresh Kumar @ 2016-11-10 18:09 UTC (permalink / raw)
To: Mark Brown
Cc: Rafael Wysocki, nm, sboyd, Viresh Kumar, linaro-kernel, linux-pm,
linux-kernel, Vincent Guittot, robh, d-gerlach, devicetree
In-Reply-To: <20161110163659.rk67qrmnrovq2ktf@sirena.org.uk>
On 10-11-16, 16:36, Mark Brown wrote:
> On Thu, Nov 10, 2016 at 09:34:40AM +0530, Viresh Kumar wrote:
> > On 09-11-16, 14:58, Mark Brown wrote:
> > > On Wed, Oct 26, 2016 at 12:02:56PM +0530, Viresh Kumar wrote:
>
> > > > + Entries for multiple regulators shall be provided in the same field separated
> > > > + by angular brackets <>. The OPP binding doesn't provide any provisions to
> > > > + relate the values to their power supplies or the order in which the supplies
> > > > + need to be configured.
>
> > > I don't understand how this works. If we have an unordered list of
> > > values to set for regulators how will we make sense of them?
>
> > The platform driver is responsible to identify the order and pass it on to the
> > OPP core. And the platform driver needs to have that hard coded.
>
> That *really* should be in the binding.
Okay, how do you suggest doing that? Will a property like supply-names
in the OPP table be fine? Like this:
@@ -369,13 +378,16 @@ Example 4: Handling multiple regulators
compatible = "arm,cortex-a7";
...
- cpu-supply = <&cpu_supply0>, <&cpu_supply1>, <&cpu_supply2>;
+ vcc0-supply = <&cpu_supply0>;
+ vcc1-supply = <&cpu_supply1>;
+ vcc2-supply = <&cpu_supply2>;
operating-points-v2 = <&cpu0_opp_table>;
};
};
cpu0_opp_table: opp_table0 {
compatible = "operating-points-v2";
+ supply-names = "vcc0", "vcc1", "vcc2";
opp-shared;
--
viresh
^ permalink raw reply
* Re: [RESEND PATCH] regulator: pwm: fix syntax errors in the examples
From: Mark Brown @ 2016-11-10 18:02 UTC (permalink / raw)
To: Peter Rosin
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Liam Girdwood, Rob Herring,
Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <cb8dbca0-1f7b-d336-4347-1e665d57f060-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 1258 bytes --]
On Thu, Nov 10, 2016 at 06:05:59PM +0100, Peter Rosin wrote:
> On 2016-11-10 17:18, Mark Brown wrote:
> > On Thu, Nov 10, 2016 at 11:25:27AM +0100, Peter Rosin wrote:
> >> While at it, clean up some other things as well.
> > This isn't a useful changelog, it's just saying that there's some
> > random changes for no reason. Please make proper patches with
> > changelogs that explain them.
> It's a very uninteresting patch, I guess I made a changelog to match.
> But I can't help myself from asking a few qustions...
> Patch*es* and changelog*s*?
> Oh, you want me to split into a series of several patches? One for the
> quotes, one for the "../regultor/" removal, one for the empty
> comment removal and one for the s/pwm_reglator/pwm-regulator/ thing?
> Be careful what you ask for, you might get it...
The above is not a problem for me, indeed it's actually good as it makes
it *much* quicker and simpler for me to read the changes and figure out
if they all make sense and do what was intended. The less the changelog
says about what the change is supposed to do and the more different
things the change does the more effort is required to work out if it's
sensible and the more chance there is of one of the changes blocking the
other ones.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]
^ permalink raw reply
* Re: [RESEND PATCH v1 03/11] drivers: soc: hisi: Add support for Hisilicon Djtag driver
From: Mark Rutland @ 2016-11-10 17:55 UTC (permalink / raw)
To: Anurup M
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-doc-u79uwXL29TY76Z2rM5mHXA, will.deacon-5wv7dgnIgG8,
corbet-T1hC0tSOHrs, catalin.marinas-5wv7dgnIgG8,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, arnd-r2nGTMty4D4,
f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
anurup.m-hv44wF8Li93QT0dZR+AlfA,
zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
john.garry-hv44wF8Li93QT0dZR+AlfA,
gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
shiju.jose-hv44wF8Li93QT0dZR+AlfA,
wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA,
guohanjun-hv44wF8Li93QT0dZR+AlfA, shyju.pv-hv44wF8Li93QT0dZR+AlfA,
linuxarm-hv44wF8Li93QT0dZR+AlfA
In-Reply-To: <1478151727-20250-4-git-send-email-anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
On Thu, Nov 03, 2016 at 01:41:59AM -0400, Anurup M wrote:
> From: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>
> The Hisilicon Djtag is an independent component which connects
> with some other components in the SoC by Debug Bus. This driver
> can be configured to access the registers of connecting components
> (like L3 cache) during real time debugging.
>
Just to check, is this likely to be used in multi-socket hardware, and
if so, are instances always-on?
> Signed-off-by: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> Signed-off-by: John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> ---
> drivers/soc/Kconfig | 1 +
> drivers/soc/Makefile | 1 +
> drivers/soc/hisilicon/Kconfig | 12 +
> drivers/soc/hisilicon/Makefile | 1 +
> drivers/soc/hisilicon/djtag.c | 639 ++++++++++++++++++++++++++++++++++++
> include/linux/soc/hisilicon/djtag.h | 38 +++
> 6 files changed, 692 insertions(+)
> create mode 100644 drivers/soc/hisilicon/Kconfig
> create mode 100644 drivers/soc/hisilicon/Makefile
> create mode 100644 drivers/soc/hisilicon/djtag.c
> create mode 100644 include/linux/soc/hisilicon/djtag.h
Other than the PMU driver(s), what is going to use this?
If you don't have something in particular, please also place this under
drivers/perf/hisilicon, along with the PMU driver(s).
We can always move it later if necessary.
[...]
> +#define SC_DJTAG_TIMEOUT 100000 /* 100ms */
This would be better as:
#define SC_DJTAG_TIMEOUT_US (100 * USEC_PER_MSEC)
(you'll need to include <linux/time64.h>)
[...]
> +static void djtag_read32_relaxed(void __iomem *regs_base, u32 off, u32 *value)
> +{
> + void __iomem *reg_addr = regs_base + off;
> +
> + *value = readl_relaxed(reg_addr);
> +}
> +
> +static void djtag_write32(void __iomem *regs_base, u32 off, u32 val)
> +{
> + void __iomem *reg_addr = regs_base + off;
> +
> + writel(val, reg_addr);
> +}
I think these make the driver harder to read, especially given the read
function is void and takes an output pointer.
In either case you can call readl/writel directly with base + off for
the __iomem ptr. Please do that.
> +
> +/*
> + * djtag_readwrite_v1/v2: djtag read/write interface
> + * @reg_base: djtag register base address
> + * @offset: register's offset
> + * @mod_sel: module selection
> + * @mod_mask: mask to select specific modules for write
> + * @is_w: write -> true, read -> false
> + * @wval: value to register for write
> + * @chain_id: which sub module for read
> + * @rval: value in register for read
> + *
> + * Return non-zero if error, else return 0.
> + */
> +static int djtag_readwrite_v1(void __iomem *regs_base, u32 offset, u32 mod_sel,
> + u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
> +{
> + u32 rd;
> + int timeout = SC_DJTAG_TIMEOUT;
> +
> + if (!(mod_mask & CHAIN_UNIT_CFG_EN)) {
> + pr_warn("djtag: do nothing.\n");
> + return 0;
> + }
> +
> + /* djtag mster enable & accelerate R,W */
> + djtag_write32(regs_base, SC_DJTAG_MSTR_EN,
> + DJTAG_NOR_CFG | DJTAG_MSTR_EN);
> +
> + /* select module */
> + djtag_write32(regs_base, SC_DJTAG_DEBUG_MODULE_SEL, mod_sel);
> + djtag_write32(regs_base, SC_DJTAG_CHAIN_UNIT_CFG_EN,
> + mod_mask & CHAIN_UNIT_CFG_EN);
> +
> + if (is_w) {
> + djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_W);
> + djtag_write32(regs_base, SC_DJTAG_MSTR_DATA, wval);
> + } else
> + djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_R);
> +
> + /* address offset */
> + djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR, offset);
> +
> + /* start to write to djtag register */
> + djtag_write32(regs_base, SC_DJTAG_MSTR_START_EN, DJTAG_MSTR_START_EN);
> +
> + /* ensure the djtag operation is done */
> + do {
> + djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN, &rd);
> + if (!(rd & DJTAG_MSTR_EN))
> + break;
> +
> + udelay(1);
> + } while (timeout--);
> +
> + if (timeout < 0) {
> + pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
> + return -EBUSY;
> + }
> +
> + if (!is_w)
> + djtag_read32_relaxed(regs_base,
> + SC_DJTAG_RD_DATA_BASE + chain_id * 0x4, rval);
> +
> + return 0;
> +}
Please factor out the common bits into helpers and have separate
read/write functions. It's incredibly difficult to follow the code with
read/write hidden behind a boolean parameter.
> +static const struct of_device_id djtag_of_match[] = {
> + /* for hip05(D02) cpu die */
> + { .compatible = "hisilicon,hip05-cpu-djtag-v1",
> + .data = (void *)djtag_readwrite_v1 },
> + /* for hip05(D02) io die */
> + { .compatible = "hisilicon,hip05-io-djtag-v1",
> + .data = (void *)djtag_readwrite_v1 },
> + /* for hip06(D03) cpu die */
> + { .compatible = "hisilicon,hip06-cpu-djtag-v1",
> + .data = (void *)djtag_readwrite_v1 },
> + /* for hip06(D03) io die */
> + { .compatible = "hisilicon,hip06-io-djtag-v2",
> + .data = (void *)djtag_readwrite_v2 },
> + /* for hip07(D05) cpu die */
> + { .compatible = "hisilicon,hip07-cpu-djtag-v2",
> + .data = (void *)djtag_readwrite_v2 },
> + /* for hip07(D05) io die */
> + { .compatible = "hisilicon,hip07-io-djtag-v2",
> + .data = (void *)djtag_readwrite_v2 },
> + {},
> +};
Binding documentation for all of these should be added *before* this
patch, per Documentation/devicetree/bindings/submitting-patches.txt.
Please move any relevant binding documentation earlier in the series.
> +MODULE_DEVICE_TABLE(of, djtag_of_match);
> +
> +static ssize_t
> +show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> + struct hisi_djtag_client *client = to_hisi_djtag_client(dev);
> +
> + return sprintf(buf, "%s%s\n", MODULE_PREFIX, client->name);
> +}
> +static DEVICE_ATTR(modalias, 0444, show_modalias, NULL);
> +
> +static struct attribute *hisi_djtag_dev_attrs[] = {
> + NULL,
> + /* modalias helps coldplug: modprobe $(cat .../modalias) */
> + &dev_attr_modalias.attr,
> + NULL
> +};
> +ATTRIBUTE_GROUPS(hisi_djtag_dev);
Why do we need to expose this under sysfs?
> +struct bus_type hisi_djtag_bus = {
> + .name = "hisi-djtag",
> + .match = hisi_djtag_device_match,
> + .probe = hisi_djtag_device_probe,
> + .remove = hisi_djtag_device_remove,
> +};
I take it the core bus code handles reference-counting users of the bus?
> +struct hisi_djtag_client *hisi_djtag_new_device(struct hisi_djtag_host *host,
> + struct device_node *node)
> +{
> + struct hisi_djtag_client *client;
> + int status;
> +
> + client = kzalloc(sizeof(*client), GFP_KERNEL);
> + if (!client)
> + return NULL;
> +
> + client->host = host;
> +
> + client->dev.parent = &client->host->dev;
> + client->dev.bus = &hisi_djtag_bus;
> + client->dev.type = &hisi_djtag_client_type;
> + client->dev.of_node = node;
I suspect that we should do:
client->dev.of_node = of_node_get(node);
... so that it's guaranteed we retain a reference.
> + snprintf(client->name, DJTAG_CLIENT_NAME_LEN, "%s%s",
> + DJTAG_PREFIX, node->name);
> + dev_set_name(&client->dev, "%s", client->name);
> +
> + status = device_register(&client->dev);
> + if (status < 0) {
> + pr_err("error adding new device, status=%d\n", status);
> + kfree(client);
> + return NULL;
> + }
> +
> + return client;
> +}
> +
> +static struct hisi_djtag_client *hisi_djtag_of_register_device(
> + struct hisi_djtag_host *host,
> + struct device_node *node)
> +{
> + struct hisi_djtag_client *client;
> +
> + client = hisi_djtag_new_device(host, node);
> + if (client == NULL) {
> + dev_err(&host->dev, "error registering device %s\n",
> + node->full_name);
> + of_node_put(node);
I don't think this should be here, given what djtag_register_devices()
does.
> + return ERR_PTR(-EINVAL);
> + }
> +
> + return client;
> +}
> +
> +static void djtag_register_devices(struct hisi_djtag_host *host)
> +{
> + struct device_node *node;
> + struct hisi_djtag_client *client;
> +
> + if (!host->of_node)
> + return;
> +
> + for_each_available_child_of_node(host->of_node, node) {
> + if (of_node_test_and_set_flag(node, OF_POPULATED))
> + continue;
> + client = hisi_djtag_of_register_device(host, node);
> + list_add(&client->next, &host->client_list);
> + }
> +}
Given hisi_djtag_of_register_device() can return ERR_PTR(-EINVAL), the
list_add is not safe.
> +static int djtag_host_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct hisi_djtag_host *host;
> + const struct of_device_id *of_id;
> + struct resource *res;
> + int rc;
> +
> + of_id = of_match_device(djtag_of_match, dev);
> + if (!of_id)
> + return -EINVAL;
> +
> + host = kzalloc(sizeof(*host), GFP_KERNEL);
> + if (!host)
> + return -ENOMEM;
> +
> + host->of_node = dev->of_node;
host->of_node = of_node_get(dev->of_node);
> + host->djtag_readwrite = of_id->data;
> + spin_lock_init(&host->lock);
> +
> + INIT_LIST_HEAD(&host->client_list);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(&pdev->dev, "No reg resorces!\n");
> + kfree(host);
> + return -EINVAL;
> + }
> +
> + if (!resource_size(res)) {
> + dev_err(&pdev->dev, "Zero reg entry!\n");
> + kfree(host);
> + return -EINVAL;
> + }
> +
> + host->sysctl_reg_map = devm_ioremap_resource(dev, res);
> + if (IS_ERR(host->sysctl_reg_map)) {
> + dev_warn(dev, "Unable to map sysctl registers.\n");
> + kfree(host);
> + return -EINVAL;
> + }
Please have a common error path rather than duplicating this free.
e.g. at the end of the function have:
err_free:
kfree(host);
return err;
... and in cases like this, have:
if (whatever()) {
dev_warn(dev, "this failed xxx\n");
err = -EINVAL;
goto err_free;
}
Thanks,
Mark.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 2/6] mfd: dt: Add bindings for the Aspeed SoC Display Controller (GFX)
From: Rob Herring @ 2016-11-10 17:40 UTC (permalink / raw)
To: Joel Stanley
Cc: Andrew Jeffery, Lee Jones, Linus Walleij, Mark Rutland,
linux-gpio@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, devicetree@vger.kernel.org
In-Reply-To: <CACPK8XeG2WnpNuHUf9VD+mZenWboq-4wei=LOkN4qVR72QbGXQ@mail.gmail.com>
On Wed, Nov 9, 2016 at 9:19 PM, Joel Stanley <joel@jms.id.au> wrote:
> On Thu, Nov 10, 2016 at 4:56 AM, Rob Herring <robh@kernel.org> wrote:
>> On Thu, Nov 03, 2016 at 01:07:57AM +1030, Andrew Jeffery wrote:
>>> The Aspeed SoC Display Controller is presented as a syscon device to
>>> arbitrate access by display and pinmux drivers. Video pinmux
>>> configuration on fifth generation SoCs depends on bits in both the
>>> System Control Unit and the Display Controller.
>>>
>>> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>>> ---
>>> Documentation/devicetree/bindings/mfd/aspeed-gfx.txt | 17 +++++++++++++++++
>>
>> The register space can't be split to 2 nodes?
>
> Do you mean splitting the GFX IP and enable register into two nodes?
>
> We can't. Pinmux needs to check bit 6 and 7 in GFX064, which is in the
> middle the IP block:
>
> GFX060: CRT Control Register I
> GFX064: CRT Control Register II
> GFX068: CRT Status Register
> GFX06C: CRT Misc Setting Register
Okay.
>>> +The Aspeed SoC Display Controller primarily does as its name suggests, but also
>>> +participates in pinmux requests on the g5 SoCs. It is therefore considered a
>>> +syscon device.
>>> +
>>> +Required properties:
>>> +- compatible: "aspeed,ast2500-gfx", "syscon"
>>
>> I think perhaps we should drop the syscon here and the driver should
>> just register as a syscon.
>
> We want the regmap to be present whenever the GFX driver or pinmux is
> loaded. If we register it in pinmux but chose to not build in that
> driver, we lack the regmap. Same for the case where a user builds in
> the GFX driver and not pinmux. I think this means we want the syscon
> compatible string, unless my understanding is wrong?
Right.
Acked-by: Rob Herring <robh@kernel.org>
Rob
^ permalink raw reply
* Re: [PATCH v3 00/10] Add DT support for ohci-da8xx
From: Axel Haslam @ 2016-11-10 17:28 UTC (permalink / raw)
To: Greg KH
Cc: devicetree, David Lechner, Kevin Hilman, linux-usb, Sekhar Nori,
linux-kernel, robh+dt, Alan Stern, linux-arm-kernel
In-Reply-To: <20161110120253.GA5385@kroah.com>
On Thu, Nov 10, 2016 at 1:02 PM, Greg KH <gregkh@linuxfoundation.org> wrote:
> On Tue, Nov 08, 2016 at 05:37:41PM +0100, Axel Haslam wrote:
>> Hi,
>>
>> On Mon, Nov 7, 2016 at 9:39 PM, Axel Haslam <ahaslam@baylibre.com> wrote:
>> > The purpose of this patch series is to add DT support for the davinci
>> > ohci driver.
>> >
>>
>> To make it easier to review. I will split the arch/arm and driver
>> patches into separate series.
>
> I don't think it's easier, as now I have no idea what order, or what
> tree it should go through :(
Hi Greg,
i will resend the series one by one and wait for each to be merged
before sending the next, so that there is no confusion on the ordering
or on what tree they should apply.
Regards
Axel.
>
> I'm guessing not mine, so all are now deleted from my patch queue...
>
> greg k-h
^ permalink raw reply
* Re: [RESEND PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings
From: Mark Rutland @ 2016-11-10 17:23 UTC (permalink / raw)
To: Anurup M
Cc: devicetree, linux-arm-kernel, linux-doc, will.deacon, corbet,
catalin.marinas, robh+dt, arnd, f.fainelli, rmk+kernel, krzk,
anurup.m, zhangshaokun, tanxiaojun, xuwei5, sanil.kumar,
john.garry, gabriele.paoloni, shiju.jose, wangkefeng.wang,
guohanjun, shyju.pv, linuxarm
In-Reply-To: <1478151727-20250-3-git-send-email-anurup.m@huawei.com>
Hi,
On Thu, Nov 03, 2016 at 01:41:58AM -0400, Anurup M wrote:
> From: Tan Xiaojun <tanxiaojun@huawei.com>
>
> 1) Add Hisilicon HiP05/06/07 CPU and ALGSUB system controller dts
> bindings.
> 2) Add Hisilicon Djtag dts binding.
>
> Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
> Signed-off-by: Anurup M <anurup.m@huawei.com>
> ---
> .../bindings/arm/hisilicon/hisilicon.txt | 82 ++++++++++++++++++++++
> 1 file changed, 82 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> index 7df79a7..341cbb9 100644
> --- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> @@ -270,3 +270,85 @@ Required Properties:
> [1]: bootwrapper size
> [2]: relocation physical address
> [3]: relocation size
> +-----------------------------------------------------------------------
> +The Hisilicon Djtag in CPU die is an independent component which connects with
> +some other components in the SoC by Debug Bus. This driver can be configured
> +to access the registers of connecting components (like L3 cache, l3 cache PMU
> + etc.) during real time debugging by sysctrl. These components appear as child
> +nodes of djtag.
Please put the djtag binding in a new file.
It's clearly unrelated to many other things in this file, which should
also be split out.
> +The Hip05/06/07 CPU system controller(sysctrl) support to manage some important
> +components (such as clock, reset, soft reset, secure debugger, etc.).
> +The CPU sysctrl registers in hip05/06/07 doesnot use syscon but will be mapped
> +by djtag driver for use by connecting components.
The djtag driver is irrelvant here.
If there's a realtionship between the two, please define that in the
binding rather than implicitly assuming it in the driver.
> +
> +Required properties:
> + - compatible : "hisilicon,hip05-cpu-djtag-v1"
> + - reg : Register address and size
> +
> +Hisilicon HiP06 djtag for CPU sysctrl
> +Required properties:
> +- compatible : "hisilicon,hip06-sysctrl", "syscon", "simple-mfd";
This looks messy. Why is this syscon and a simple-mfd?
We should kill off / deprecate the syscon binding. It's completely
meaningless.
> +- reg : Register address and size
> +- djtag :
> + - compatible : "hisilicon,hip06-cpu-djtag-v1"
> + - reg : Register address and size
> +
> +Hisilicon HiP07 djtag for CPU sysctrl
> +Required properties:
> + - compatible : "hisilicon,hip07-cpu-djtag-v2"
> + - reg : Register address and size
> +
> +Example:
> + /* for Hisilicon HiP05 djtag for CPU sysctrl */
> + djtag0: djtag@80010000 {
> + compatible = "hisilicon,hip05-cpu-djtag-v1";
> + reg = <0x0 0x80010000 0x0 0x10000>;
> +
> + /* For L3 cache PMU */
> + pmul3c0 {
> + compatible = "hisilicon,hisi-pmu-l3c-v1";
> + scl-id = <0x02>;
> + num-events = <0x16>;
> + num-counters = <0x08>;
> + module-id = <0x04>;
> + num-banks = <0x04>;
> + cfgen-map = <0x02 0x04 0x01 0x08>;
> + counter-reg = <0x170>;
> + evctrl-reg = <0x04>;
> + event-en = <0x1000000>;
> + evtype-reg = <0x140>;
> + };
This sub-node needs a binding document.
These properties are completely opaque
> + };
> +
> +-----------------------------------------------------------------------
> +The Hisilicon HiP05/06/07 ALGSUB system controller(sysctrl) is in IO die
> +of SoC. It has a similar function as the Hisilicon HiP05/06/07 CPU system
> +controller in CPU die and it manage different components, like RSA, etc.
> +The Hisilicon Djtag in IO die has a similar function as in CPU die and maps
> +the sysctrl registers for use by connecting components.
> +All connecting components shall appear as child nodes of djtag.
I don't follow the above. This describes an ALGSUB system controllerm
but the documentation below is all about djtag.
Thanks,
Mark.
> +Hisilicon HiP05 djtag for ALGSUB sysctrl
> +Required properties:
> + - compatible : "hisilicon,hip05-io-djtag-v1"
> + - reg : Register address and size
> +
> +Hisilicon HiP06 djtag for ALGSUB sysctrl
> +Required properties:
> + - compatible : "hisilicon,hip06-io-djtag-v2"
> + - reg : Register address and size
> +
> +Hisilicon HiP07 djtag for ALGSUB sysctrl
> +Required properties:
> + - compatible : "hisilicon,hip07-io-djtag-v2"
> + - reg : Register address and size
> +
> +Example:
> + /* for Hisilicon HiP05 djtag for alg sysctrl */
> + djtag0: djtag@d0000000 {
> + compatible = "hisilicon,hip05-io-djtag-v1";
> + reg = <0x0 0xd0000000 0x0 0x10000>;
> + };
> --
> 2.1.4
>
^ permalink raw reply
* [PATCH] lm87: Allow LM87_REG_CHANNEL_MODE to be set via DT
From: Jason Gunthorpe @ 2016-11-10 17:19 UTC (permalink / raw)
To: Guenter Roeck; +Cc: devicetree, linux-kernel, linux-hwmon
This compliments the existing scheme that lets it be set via
platform_data.
Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
---
Documentation/devicetree/bindings/hwmon/lm87.txt | 27 ++++++++++++++++++++++++
drivers/hwmon/lm87.c | 8 ++++++-
2 files changed, 34 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/hwmon/lm87.txt
diff --git a/Documentation/devicetree/bindings/hwmon/lm87.txt b/Documentation/devicetree/bindings/hwmon/lm87.txt
new file mode 100644
index 00000000000000..4ea2646ce8d2f0
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/lm87.txt
@@ -0,0 +1,27 @@
+* LM87 I2C Temperature and Fan sensor
+
+Required properties:
+- compatible: should contain one of
+ * "national,lm87"
+ * "ti,lm87"
+
+Optional properties:
+- channel-mode: Write the specified value to register 0x16. If not specified
+ the driver will read the value from the chip at startup.
+
+ The value controls how the driver operates certain multi-use
+ pins, eg bit 2 will select between temp_input3 or
+ in0&in5 operation.
+
+See Documentation/devicetree/bindings/i2c/i2c.txt for more required and
+optional properties.
+
+Example:
+
+i2c_master {
+ temperature-sensor@0 {
+ compatible = "ti,lm87";
+ channel-mode = <0x4>;
+ reg = <0>;
+ };
+};
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index 81cb898245a107..ccc0c913b4534e 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -853,9 +853,15 @@ static void lm87_restore_config(void *arg)
static int lm87_init_client(struct i2c_client *client)
{
struct lm87_data *data = i2c_get_clientdata(client);
+ u32 channel;
int rc;
- if (dev_get_platdata(&client->dev)) {
+ if (client->dev.of_node &&
+ !of_property_read_u32(client->dev.of_node, "channel-mode",
+ &channel)) {
+ data->channel = channel;
+ lm87_write_value(client, LM87_REG_CHANNEL_MODE, data->channel);
+ } else if (dev_get_platdata(&client->dev)) {
data->channel = *(u8 *)dev_get_platdata(&client->dev);
lm87_write_value(client,
LM87_REG_CHANNEL_MODE, data->channel);
--
2.7.4
^ permalink raw reply related
* Re: [RESEND PATCH] regulator: pwm: fix syntax errors in the examples
From: Peter Rosin @ 2016-11-10 17:05 UTC (permalink / raw)
To: Mark Brown
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Liam Girdwood, Rob Herring,
Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161110161801.7462udnohbaik5nr-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
On 2016-11-10 17:18, Mark Brown wrote:
> On Thu, Nov 10, 2016 at 11:25:27AM +0100, Peter Rosin wrote:
>> While at it, clean up some other things as well.
>
> This isn't a useful changelog, it's just saying that there's some
> random changes for no reason. Please make proper patches with
> changelogs that explain them.
It's a very uninteresting patch, I guess I made a changelog to match.
But I can't help myself from asking a few qustions...
Patch*es* and changelog*s*?
Oh, you want me to split into a series of several patches? One for the
quotes, one for the "../regultor/" removal, one for the empty
comment removal and one for the s/pwm_reglator/pwm-regulator/ thing?
Be careful what you ask for, you might get it...
Or are you saying that patches that I make have poor descriptions
in general?
Anyway, I'm done with that file, if you want crappy docs, by all
means throw the patch away.
Cheers,
Peter
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v2] dt-bindings: video: exynos7-decon: Remove obsolete samsung,power-domain property
From: Krzysztof Kozlowski @ 2016-11-10 16:50 UTC (permalink / raw)
To: Inki Dae, Joonyoung Shim, Seung-Woo Kim, Kyungmin Park,
David Airlie, Rob Herring, Mark Rutland, Kukjin Kim,
Krzysztof Kozlowski, Javier Martinez Canillas,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
The samsung,power-domain property is obsolete since commit 0da658704136
("ARM: dts: convert to generic power domain bindings for exynos DT").
Replace it with generic one.
Signed-off-by: Krzysztof Kozlowski <krzk-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Reviewed-by: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Reviewed-by: Javier Martinez Canillas <javier-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Changes since v1:
1. Just add the acks/reviews.
I though it will be applied by Exynos DRM maintainer but there was no
response.
Dear Rob, could you pick it up?
---
Documentation/devicetree/bindings/display/exynos/exynos7-decon.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/display/exynos/exynos7-decon.txt b/Documentation/devicetree/bindings/display/exynos/exynos7-decon.txt
index 3938caacf11c..8346fb18a358 100644
--- a/Documentation/devicetree/bindings/display/exynos/exynos7-decon.txt
+++ b/Documentation/devicetree/bindings/display/exynos/exynos7-decon.txt
@@ -33,7 +33,7 @@ Required properties:
- i80-if-timings: timing configuration for lcd i80 interface support.
Optional Properties:
-- samsung,power-domain: a phandle to DECON power domain node.
+- power-domains: a phandle to DECON power domain node.
- display-timings: timing settings for DECON, as described in document [1].
Can be used in case timings cannot be provided otherwise
or to override timings provided by the panel.
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [v17 2/2] drm/bridge: Add I2C based driver for ps8640 bridge
From: Enric Balletbo Serra @ 2016-11-10 16:39 UTC (permalink / raw)
To: Jitao Shi
Cc: David Airlie, Thierry Reding, Matthias Brugger, Mark Rutland,
stonea168, dri-devel, Andy Yan, Ajay Kumar, Vincent Palatin,
cawa cheng, bibby.hsieh, CK HU, Russell King,
devicetree@vger.kernel.org, Sascha Hauer, Pawel Moll,
Ian Campbell, Inki Dae, Rob Herring, ARM/Mediatek
In-Reply-To: <1472280263-18177-2-git-send-email-jitao.shi@mediatek.com>
Hi Jitao,
2016-08-27 8:44 GMT+02:00 Jitao Shi <jitao.shi@mediatek.com>:
> This patch adds drm_bridge driver for parade DSI to eDP bridge chip.
>
> Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
> Reviewed-by: Daniel Kurtz <djkurtz@chromium.org>
> ---
> Changes since v16:
> - Disable ps8640 DSI MCS Function.
> - Rename gpios name more clearly.
> - Tune the ps8640 power on sequence.
>
> Changes since v15:
> - Drop drm_connector_(un)register calls from parade ps8640.
> The main DRM driver mtk_drm_drv now calls
> drm_connector_register_all() after drm_dev_register() in the
> mtk_drm_bind() function. That function should iterate over all
> connectors and call drm_connector_register() for each of them.
> So, remove drm_connector_(un)register calls from parade ps8640.
>
> Changes since v14:
> - update copyright info.
> - change bridge_to_ps8640 and connector_to_ps8640 to inline function.
> - fix some coding style.
> - use sizeof as array counter.
> - use drm_get_edid when read edid.
> - add mutex when firmware updating.
>
> Changes since v13:
> - add const on data, ps8640_write_bytes(struct i2c_client *client, const u8 *data, u16 data_len)
> - fix PAGE2_SW_REST tyro.
> - move the buf[3] init to entrance of the function.
>
> Changes since v12:
> - fix hw_chip_id build warning
>
> Changes since v11:
> - Remove depends on I2C, add DRM depends
> - Reuse ps8640_write_bytes() in ps8640_write_byte()
> - Use timer check for polling like the routines in <linux/iopoll.h>
> - Fix no drm_connector_unregister/drm_connector_cleanup when ps8640_bridge_attach fail
> - Check the ps8640 hardware id in ps8640_validate_firmware
> - Remove fw_version check
> - Move ps8640_validate_firmware before ps8640_enter_bl
> - Add ddc_i2c unregister when probe fail and ps8640_remove
> ---
> drivers/gpu/drm/bridge/Kconfig | 12 +
> drivers/gpu/drm/bridge/Makefile | 1 +
> drivers/gpu/drm/bridge/parade-ps8640.c | 1077 ++++++++++++++++++++++++++++++++
> 3 files changed, 1090 insertions(+)
> create mode 100644 drivers/gpu/drm/bridge/parade-ps8640.c
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index b590e67..c59d043 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -50,6 +50,18 @@ config DRM_PARADE_PS8622
> ---help---
> Parade eDP-LVDS bridge chip driver.
>
> +config DRM_PARADE_PS8640
> + tristate "Parade PS8640 MIPI DSI to eDP Converter"
> + depends on DRM
> + depends on OF
> + select DRM_KMS_HELPER
> + select DRM_MIPI_DSI
> + select DRM_PANEL
> + ---help---
> + Choose this option if you have PS8640 for display
> + The PS8640 is a high-performance and low-power
> + MIPI DSI to eDP converter
> +
> config DRM_SII902X
> tristate "Silicon Image sii902x RGB/HDMI bridge"
> depends on OF
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index efdb07e..3360537 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -5,6 +5,7 @@ obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
> obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
> obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
> obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
> +obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
> obj-$(CONFIG_DRM_SII902X) += sii902x.o
> obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
> obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
> diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c
> new file mode 100644
> index 0000000..7d67431
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/parade-ps8640.c
> @@ -0,0 +1,1077 @@
> +/*
> + * Copyright (c) 2016 MediaTek Inc.
> + *
> + * 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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/firmware.h>
> +#include <linux/gpio.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_graph.h>
> +#include <linux/regulator/consumer.h>
> +#include <asm/unaligned.h>
> +#include <drm/drm_panel.h>
> +
> +#include <drmP.h>
> +#include <drm_atomic_helper.h>
> +#include <drm_crtc_helper.h>
> +#include <drm_crtc.h>
> +#include <drm_edid.h>
> +#include <drm_mipi_dsi.h>
> +
> +#define PAGE1_VSTART 0x6b
> +#define PAGE2_SPI_CFG3 0x82
> +#define I2C_TO_SPI_RESET 0x20
> +#define PAGE2_ROMADD_BYTE1 0x8e
> +#define PAGE2_ROMADD_BYTE2 0x8f
> +#define PAGE2_SWSPI_WDATA 0x90
> +#define PAGE2_SWSPI_RDATA 0x91
> +#define PAGE2_SWSPI_LEN 0x92
> +#define PAGE2_SWSPI_CTL 0x93
> +#define TRIGGER_NO_READBACK 0x05
> +#define TRIGGER_READBACK 0x01
> +#define PAGE2_SPI_STATUS 0x9e
> +#define SPI_READY 0x0c
> +#define PAGE2_GPIO_L 0xa6
> +#define PAGE2_GPIO_H 0xa7
> +#define PS_GPIO9 BIT(1)
> +#define PAGE2_IROM_CTRL 0xb0
> +#define IROM_ENABLE 0xc0
> +#define IROM_DISABLE 0x80
> +#define PAGE2_SW_RESET 0xbc
> +#define SPI_SW_RESET BIT(7)
> +#define MPU_SW_RESET BIT(6)
> +#define PAGE2_ENCTLSPI_WR 0xda
> +#define PAGE2_I2C_BYPASS 0xea
> +#define I2C_BYPASS_EN 0xd0
> +#define PAGE2_MCS_EN 0xf3
> +#define MCS_EN BIT(0)
> +#define PAGE3_SET_ADD 0xfe
> +#define PAGE3_SET_VAL 0xff
> +#define VDO_CTL_ADD 0x13
> +#define VDO_DIS 0x18
> +#define VDO_EN 0x1c
> +#define PAGE4_REV_L 0xf0
> +#define PAGE4_REV_H 0xf1
> +#define PAGE4_CHIP_L 0xf2
> +#define PAGE4_CHIP_H 0xf3
> +
> +/* Firmware */
> +#define PS_FW_NAME "ps864x_fw.bin"
> +
About the firmware discussion I think that if you want to maintain the
upgrade firmware thing you should also include this patch in the
series.
https://chromium-review.googlesource.com/#/c/317221/
Otherwise, if this is not really needed I think that remove this from
the driver is the best. Just an opinion, this is something the
maintainer should decide.
> +#define FW_CHIP_ID_OFFSET 0
> +#define FW_VERSION_OFFSET 2
> +#define EDID_I2C_ADDR 0x50
> +
> +#define WRITE_STATUS_REG_CMD 0x01
> +#define READ_STATUS_REG_CMD 0x05
> +#define BUSY BIT(0)
> +#define CLEAR_ALL_PROTECT 0x00
> +#define BLK_PROTECT_BITS 0x0c
> +#define STATUS_REG_PROTECT BIT(7)
> +#define WRITE_ENABLE_CMD 0x06
> +#define CHIP_ERASE_CMD 0xc7
> +#define MAX_DEVS 0x8
> +
> +struct ps8640_info {
> + u8 family_id;
> + u8 variant_id;
> + u16 version;
> +};
> +
> +struct ps8640 {
> + struct drm_connector connector;
> + struct drm_bridge bridge;
> + struct edid *edid;
> + struct mipi_dsi_device dsi;
> + struct i2c_client *page[MAX_DEVS];
> + struct i2c_client *ddc_i2c;
> + struct regulator_bulk_data supplies[2];
> + struct drm_panel *panel;
> + struct gpio_desc *gpio_reset;
> + struct gpio_desc *gpio_power_down;
> + struct gpio_desc *gpio_mode_sel;
> + bool enabled;
> +
> + /* firmware file info */
> + struct ps8640_info info;
> + bool in_fw_update;
> + /* for firmware update protect */
> + struct mutex fw_mutex;
> +};
> +
> +static const u8 enc_ctrl_code[6] = { 0xaa, 0x55, 0x50, 0x41, 0x52, 0x44 };
> +static const u8 hw_chip_id[4] = { 0x00, 0x0a, 0x00, 0x30 };
> +
> +static inline struct ps8640 *bridge_to_ps8640(struct drm_bridge *e)
> +{
> + return container_of(e, struct ps8640, bridge);
> +}
> +
> +static inline struct ps8640 *connector_to_ps8640(struct drm_connector *e)
> +{
> + return container_of(e, struct ps8640, connector);
> +}
> +
> +static int ps8640_read(struct i2c_client *client, u8 reg, u8 *data,
> + u16 data_len)
> +{
> + int ret;
> + struct i2c_msg msgs[] = {
> + {
> + .addr = client->addr,
> + .flags = 0,
> + .len = 1,
> + .buf = ®,
> + },
> + {
> + .addr = client->addr,
> + .flags = I2C_M_RD,
> + .len = data_len,
> + .buf = data,
> + }
> + };
> +
> + ret = i2c_transfer(client->adapter, msgs, 2);
> +
> + if (ret == 2)
> + return 0;
> + if (ret < 0)
> + return ret;
> + else
> + return -EIO;
> +}
> +
> +static int ps8640_write_bytes(struct i2c_client *client, const u8 *data,
> + u16 data_len)
> +{
> + int ret;
> + struct i2c_msg msg;
> +
> + msg.addr = client->addr;
> + msg.flags = 0;
> + msg.len = data_len;
> + msg.buf = (u8 *)data;
> +
> + ret = i2c_transfer(client->adapter, &msg, 1);
> + if (ret == 1)
> + return 0;
> + if (ret < 0)
> + return ret;
> + else
> + return -EIO;
> +}
> +
> +static int ps8640_write_byte(struct i2c_client *client, u8 reg, u8 data)
> +{
> + u8 buf[] = { reg, data };
> +
> + return ps8640_write_bytes(client, buf, sizeof(buf));
> +}
> +
> +static void ps8640_get_mcu_fw_version(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[5];
> + u8 fw_ver[2];
> +
> + ps8640_read(client, 0x4, fw_ver, sizeof(fw_ver));
> + ps_bridge->info.version = (fw_ver[0] << 8) | fw_ver[1];
> +
> + DRM_INFO_ONCE("ps8640 rom fw version %d.%d\n", fw_ver[0], fw_ver[1]);
> +}
> +
> +static int ps8640_bridge_unmute(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[3];
> + u8 vdo_ctrl_buf[3] = { PAGE3_SET_ADD, VDO_CTL_ADD, VDO_EN };
> +
> + return ps8640_write_bytes(client, vdo_ctrl_buf, sizeof(vdo_ctrl_buf));
> +}
> +
> +static int ps8640_bridge_mute(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[3];
> + u8 vdo_ctrl_buf[3] = { PAGE3_SET_ADD, VDO_CTL_ADD, VDO_DIS };
> +
> + return ps8640_write_bytes(client, vdo_ctrl_buf, sizeof(vdo_ctrl_buf));
> +}
> +
> +static void ps8640_pre_enable(struct drm_bridge *bridge)
> +{
> + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> + struct i2c_client *client = ps_bridge->page[2];
> + struct i2c_client *page1 = ps_bridge->page[1];
> + int err;
> + u8 set_vdo_done, mcs_en, vstart;
> + ktime_t timeout;
> +
> + if (ps_bridge->in_fw_update)
> + return;
> +
> + if (ps_bridge->enabled)
> + return;
> +
> + err = drm_panel_prepare(ps_bridge->panel);
> + if (err < 0) {
> + DRM_ERROR("failed to prepare panel: %d\n", err);
> + return;
> + }
> +
> + err = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies),
> + ps_bridge->supplies);
> + if (err < 0) {
> + DRM_ERROR("cannot enable regulators %d\n", err);
> + goto err_panel_unprepare;
> + }
> +
> + gpiod_set_value(ps_bridge->gpio_power_down, 1);
> + gpiod_set_value(ps_bridge->gpio_reset, 0);
> + usleep_range(2000, 2500);
> + gpiod_set_value(ps_bridge->gpio_reset, 1);
> +
> + /*
> + * Wait for the ps8640 embed mcu ready
> + * First wait 200ms and then check the mcu ready flag every 20ms
> + */
> + msleep(200);
> +
> + timeout = ktime_add_ms(ktime_get(), 200);
> + for (;;) {
> + err = ps8640_read(client, PAGE2_GPIO_H, &set_vdo_done, 1);
> + if (err < 0) {
> + DRM_ERROR("failed read PAGE2_GPIO_H: %d\n", err);
> + goto err_regulators_disable;
> + }
> + if ((set_vdo_done & PS_GPIO9) == PS_GPIO9)
> + break;
> + if (ktime_compare(ktime_get(), timeout) > 0)
> + break;
> + msleep(20);
> + }
> +
> + msleep(50);
> +
> + ps8640_read(page1, PAGE1_VSTART, &vstart, 1);
> + DRM_INFO("PS8640 PAGE1.0x6B = 0x%x\n", vstart);
> +
> + /**
> + * The Manufacturer Command Set (MCS) is a device dependent interface
> + * intended for factory programming of the display module default
> + * parameters. Once the display module is configured, the MCS shall be
> + * disabled by the manufacturer. Once disabled, all MCS commands are
> + * ignored by the display interface.
> + */
> + ps8640_read(client, PAGE2_MCS_EN, &mcs_en, 1);
> + ps8640_write_byte(client, PAGE2_MCS_EN, mcs_en & ~MCS_EN);
> +
> + if (ps_bridge->info.version == 0)
> + ps8640_get_mcu_fw_version(ps_bridge);
> +
> + err = ps8640_bridge_unmute(ps_bridge);
> + if (err)
> + DRM_ERROR("failed to enable unmutevideo: %d\n", err);
> + /* Switch access edp panel's edid through i2c */
> + ps8640_write_byte(client, PAGE2_I2C_BYPASS, I2C_BYPASS_EN);
> + ps_bridge->enabled = true;
> +
> + return;
> +
> +err_regulators_disable:
> + regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies),
> + ps_bridge->supplies);
> +err_panel_unprepare:
> + drm_panel_unprepare(ps_bridge->panel);
> +}
> +
> +static void ps8640_enable(struct drm_bridge *bridge)
> +{
> + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> + int err;
> +
> + err = drm_panel_enable(ps_bridge->panel);
> + if (err < 0)
> + DRM_ERROR("failed to enable panel: %d\n", err);
> +}
> +
> +static void ps8640_disable(struct drm_bridge *bridge)
> +{
> + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> + int err;
> +
> + err = drm_panel_disable(ps_bridge->panel);
> + if (err < 0)
> + DRM_ERROR("failed to disable panel: %d\n", err);
> +}
> +
> +static void ps8640_post_disable(struct drm_bridge *bridge)
> +{
> + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> + int err;
> +
> + if (ps_bridge->in_fw_update)
> + return;
> +
> + if (!ps_bridge->enabled)
> + return;
> +
> + ps_bridge->enabled = false;
> +
> + err = ps8640_bridge_mute(ps_bridge);
> + if (err < 0)
> + DRM_ERROR("failed to unmutevideo: %d\n", err);
> +
> + gpiod_set_value(ps_bridge->gpio_reset, 0);
> + gpiod_set_value(ps_bridge->gpio_power_down, 0);
> + err = regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies),
> + ps_bridge->supplies);
> + if (err < 0)
> + DRM_ERROR("cannot disable regulators %d\n", err);
> +
> + err = drm_panel_unprepare(ps_bridge->panel);
> + if (err)
> + DRM_ERROR("failed to unprepare panel: %d\n", err);
> +}
> +
> +static int ps8640_get_modes(struct drm_connector *connector)
> +{
> + struct ps8640 *ps_bridge = connector_to_ps8640(connector);
> + struct edid *edid;
> + int num_modes = 0;
> + bool power_off;
> +
> + if (ps_bridge->edid)
> + return drm_add_edid_modes(connector, ps_bridge->edid);
> +
> + power_off = !ps_bridge->enabled;
> + ps8640_pre_enable(&ps_bridge->bridge);
> +
> + edid = drm_get_edid(connector, ps_bridge->ddc_i2c->adapter);
> + if (!edid)
> + goto out;
> +
> + ps_bridge->edid = edid;
> + drm_mode_connector_update_edid_property(connector, ps_bridge->edid);
> + num_modes = drm_add_edid_modes(connector, ps_bridge->edid);
> +
> +out:
> + if (power_off)
> + ps8640_post_disable(&ps_bridge->bridge);
> +
> + return num_modes;
> +}
> +
> +static struct drm_encoder *ps8640_best_encoder(struct drm_connector *connector)
> +{
> + struct ps8640 *ps_bridge = connector_to_ps8640(connector);
> +
> + return ps_bridge->bridge.encoder;
> +}
> +
> +static const struct drm_connector_helper_funcs ps8640_connector_helper_funcs = {
> + .get_modes = ps8640_get_modes,
> + .best_encoder = ps8640_best_encoder,
> +};
> +
> +static enum drm_connector_status ps8640_detect(struct drm_connector *connector,
> + bool force)
> +{
> + return connector_status_connected;
> +}
> +
> +static const struct drm_connector_funcs ps8640_connector_funcs = {
> + .dpms = drm_atomic_helper_connector_dpms,
> + .fill_modes = drm_helper_probe_single_connector_modes,
> + .detect = ps8640_detect,
> + .reset = drm_atomic_helper_connector_reset,
> + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +int ps8640_bridge_attach(struct drm_bridge *bridge)
> +{
> + struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> + struct device *dev = &ps_bridge->page[0]->dev;
> + struct device_node *port, *in_ep;
> + struct device_node *dsi_node = NULL;
> + struct mipi_dsi_host *host = NULL;
> + int ret;
> +
> + ret = drm_connector_init(bridge->dev, &ps_bridge->connector,
> + &ps8640_connector_funcs,
> + DRM_MODE_CONNECTOR_eDP);
> +
> + if (ret) {
> + DRM_ERROR("Failed to initialize connector with drm: %d\n", ret);
> + return ret;
> + }
> +
> + drm_connector_helper_add(&ps_bridge->connector,
> + &ps8640_connector_helper_funcs);
> +
> + ps_bridge->connector.dpms = DRM_MODE_DPMS_ON;
> + drm_mode_connector_attach_encoder(&ps_bridge->connector,
> + bridge->encoder);
> +
> + if (ps_bridge->panel)
> + drm_panel_attach(ps_bridge->panel, &ps_bridge->connector);
> +
> + /* port@0 is ps8640 dsi input port */
> + port = of_graph_get_port_by_id(dev->of_node, 0);
> + if (port) {
> + in_ep = of_get_child_by_name(port, "endpoint");
> + of_node_put(port);
> + if (in_ep) {
> + dsi_node = of_graph_get_remote_port_parent(in_ep);
> + of_node_put(in_ep);
> + }
> + }
> + if (dsi_node) {
> + host = of_find_mipi_dsi_host_by_node(dsi_node);
> + of_node_put(dsi_node);
> + if (!host) {
> + ret = -ENODEV;
> + goto err;
> + }
> + }
> +
> + ps_bridge->dsi.host = host;
> + ps_bridge->dsi.mode_flags = MIPI_DSI_MODE_VIDEO |
> + MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
> + ps_bridge->dsi.format = MIPI_DSI_FMT_RGB888;
> + ps_bridge->dsi.lanes = 4;
> + ret = mipi_dsi_attach(&ps_bridge->dsi);
> + if (ret)
> + goto err;
> +
> + return 0;
> +err:
> + if (ps_bridge->panel)
> + drm_panel_detach(ps_bridge->panel);
> + drm_connector_cleanup(&ps_bridge->connector);
> + return ret;
> +}
> +
> +static const struct drm_bridge_funcs ps8640_bridge_funcs = {
> + .attach = ps8640_bridge_attach,
> + .disable = ps8640_disable,
> + .post_disable = ps8640_post_disable,
> + .pre_enable = ps8640_pre_enable,
> + .enable = ps8640_enable,
> +};
> +
> +/* Firmware Version is returned as Major.Minor */
> +static ssize_t ps8640_fw_version_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct ps8640 *ps_bridge = dev_get_drvdata(dev);
> + struct ps8640_info *info = &ps_bridge->info;
> +
> + return scnprintf(buf, PAGE_SIZE, "%u.%u\n", info->version >> 8,
> + info->version & 0xff);
> +}
> +
> +/* Hardware Version is returned as FamilyID.VariantID */
> +static ssize_t ps8640_hw_version_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct ps8640 *ps_bridge = dev_get_drvdata(dev);
> + struct ps8640_info *info = &ps_bridge->info;
> +
> + return scnprintf(buf, PAGE_SIZE, "ps%u.%u\n", info->family_id,
> + info->variant_id);
> +}
> +
> +static int ps8640_spi_send_cmd(struct ps8640 *ps_bridge, u8 *cmd, u8 cmd_len)
> +{
> + struct i2c_client *client = ps_bridge->page[2];
> + u8 i, buf[3] = { PAGE2_SWSPI_LEN, cmd_len - 1, TRIGGER_NO_READBACK };
> + int ret;
> +
> + ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE);
> + if (ret)
> + goto err;
> +
> + /* write command in write port */
> + for (i = 0; i < cmd_len; i++) {
> + ret = ps8640_write_byte(client, PAGE2_SWSPI_WDATA, cmd[i]);
> + if (ret)
> + goto err_irom_disable;
> + }
> +
> + ret = ps8640_write_bytes(client, buf, sizeof(buf));
> + if (ret)
> + goto err_irom_disable;
> +
> + ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
> + if (ret)
> + goto err;
> +
> + return 0;
> +err_irom_disable:
> + ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
> +err:
> + dev_err(&client->dev, "send command err: %d\n", ret);
> + return ret;
> +}
> +
> +static int ps8640_wait_spi_ready(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[2];
> + u8 spi_rdy_st;
> + ktime_t timeout;
> +
> + timeout = ktime_add_ms(ktime_get(), 200);
> + for (;;) {
> + ps8640_read(client, PAGE2_SPI_STATUS, &spi_rdy_st, 1);
> + if ((spi_rdy_st & SPI_READY) != SPI_READY)
> + break;
> +
> + if (ktime_compare(ktime_get(), timeout) > 0) {
> + dev_err(&client->dev, "wait spi ready timeout\n");
> + return -EBUSY;
> + }
> +
> + msleep(20);
> + }
> +
> + return 0;
> +}
> +
> +static int ps8640_wait_spi_nobusy(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[2];
> + u8 spi_status, buf[3] = { PAGE2_SWSPI_LEN, 0, TRIGGER_READBACK };
> + int ret;
> + ktime_t timeout;
> +
> + timeout = ktime_add_ms(ktime_get(), 500);
> + for (;;) {
> + /* 0x05 RDSR; Read-Status-Register */
> + ret = ps8640_write_byte(client, PAGE2_SWSPI_WDATA,
> + READ_STATUS_REG_CMD);
> + if (ret)
> + goto err_send_cmd_exit;
> +
> + ret = ps8640_write_bytes(client, buf, 3);
> + if (ret)
> + goto err_send_cmd_exit;
> +
> + /* delay for cmd send */
> + usleep_range(300, 500);
> + /* wait for SPI ROM until not busy */
> + ret = ps8640_read(client, PAGE2_SWSPI_RDATA, &spi_status, 1);
> + if (ret)
> + goto err_send_cmd_exit;
> +
> + if (!(spi_status & BUSY))
> + break;
> +
> + if (ktime_compare(ktime_get(), timeout) > 0) {
> + dev_err(&client->dev, "wait spi no busy timeout: %d\n",
> + ret);
> + return -EBUSY;
> + }
> + }
> +
> + return 0;
> +
> +err_send_cmd_exit:
> + dev_err(&client->dev, "send command err: %d\n", ret);
> + return ret;
> +}
> +
> +static int ps8640_wait_rom_idle(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[0];
> + int ret;
> +
> + ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE);
> + if (ret)
> + goto exit;
> +
> + ret = ps8640_wait_spi_ready(ps_bridge);
> + if (ret)
> + goto err_spi;
> +
> + ret = ps8640_wait_spi_nobusy(ps_bridge);
> + if (ret)
> + goto err_spi;
> +
> + ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
> + if (ret)
> + goto exit;
> +
> + return 0;
> +
> +err_spi:
> + ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
> +exit:
> + dev_err(&client->dev, "wait ps8640 rom idle fail: %d\n", ret);
> +
> + return ret;
> +}
> +
> +static int ps8640_spi_dl_mode(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[2];
> + int ret;
> +
> + /* switch ps8640 mode to spi dl mode */
> + if (ps_bridge->gpio_mode_sel)
> + gpiod_set_value(ps_bridge->gpio_mode_sel, 0);
> +
> + /* reset spi interface */
> + ret = ps8640_write_byte(client, PAGE2_SW_RESET,
> + SPI_SW_RESET | MPU_SW_RESET);
> + if (ret)
> + goto exit;
> +
> + ret = ps8640_write_byte(client, PAGE2_SW_RESET, MPU_SW_RESET);
> + if (ret)
> + goto exit;
> +
> + return 0;
> +
> +exit:
> + dev_err(&client->dev, "fail reset spi interface: %d\n", ret);
> +
> + return ret;
> +}
> +
> +static int ps8640_rom_prepare(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[2];
> + struct device *dev = &client->dev;
> + u8 i, cmd[2];
> + int ret;
> +
> + cmd[0] = WRITE_ENABLE_CMD;
> + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1);
> + if (ret) {
> + dev_err(dev, "failed enable-write-status-register: %d\n", ret);
> + return ret;
> + }
> +
> + cmd[0] = WRITE_STATUS_REG_CMD;
> + cmd[1] = CLEAR_ALL_PROTECT;
> + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 2);
> + if (ret) {
> + dev_err(dev, "fail disable all protection: %d\n", ret);
> + return ret;
> + }
> +
> + /* wait for SPI module ready */
> + ret = ps8640_wait_rom_idle(ps_bridge);
> + if (ret) {
> + dev_err(dev, "fail wait rom idle: %d\n", ret);
> + return ret;
> + }
> +
> + ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE);
> + for (i = 0; i < ARRAY_SIZE(enc_ctrl_code); i++)
> + ps8640_write_byte(client, PAGE2_ENCTLSPI_WR, enc_ctrl_code[i]);
> + ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
> +
> + /* Enable-Write-Status-Register */
> + cmd[0] = WRITE_ENABLE_CMD;
> + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1);
> + if (ret) {
> + dev_err(dev, "fail enable-write-status-register: %d\n", ret);
> + return ret;
> + }
> +
> + /* chip erase command */
> + cmd[0] = CHIP_ERASE_CMD;
> + ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1);
> + if (ret) {
> + dev_err(dev, "fail disable all protection: %d\n", ret);
> + return ret;
> + }
> +
> + ret = ps8640_wait_rom_idle(ps_bridge);
> + if (ret) {
> + dev_err(dev, "fail wait rom idle: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int ps8640_check_chip_id(struct ps8640 *ps_bridge)
> +{
> + struct i2c_client *client = ps_bridge->page[4];
> + u8 buf[4];
> +
> + ps8640_read(client, PAGE4_REV_L, buf, 4);
> + return memcmp(buf, hw_chip_id, sizeof(buf));
> +}
> +
> +static int ps8640_validate_firmware(struct ps8640 *ps_bridge,
> + const struct firmware *fw)
> +{
> + struct i2c_client *client = ps_bridge->page[0];
> + u16 fw_chip_id;
> +
> + /*
> + * Get the chip_id from the firmware. Make sure that it is the
> + * right controller to do the firmware and config update.
> + */
> + fw_chip_id = get_unaligned_le16(fw->data + FW_CHIP_ID_OFFSET);
> +
> + if (fw_chip_id != 0x8640 && ps8640_check_chip_id(ps_bridge) == 0) {
> + dev_err(&client->dev,
> + "chip id mismatch: fw 0x%x vs. chip 0x8640\n",
> + fw_chip_id);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int ps8640_write_rom(struct ps8640 *ps_bridge, const struct firmware *fw)
> +{
> + struct i2c_client *client = ps_bridge->page[0];
> + struct device *dev = &client->dev;
> + struct i2c_client *client2 = ps_bridge->page[2];
> + struct i2c_client *client7 = ps_bridge->page[7];
> + size_t pos, cpy_len;
> + u8 buf[257];
> + int ret;
> +
> + ps8640_write_byte(client2, PAGE2_SPI_CFG3, I2C_TO_SPI_RESET);
> + msleep(100);
> + ps8640_write_byte(client2, PAGE2_SPI_CFG3, 0x00);
> +
> + for (pos = 0; pos < fw->size; pos += cpy_len) {
> + buf[0] = PAGE2_ROMADD_BYTE1;
> + buf[1] = pos >> 8;
> + buf[2] = pos >> 16;
> + ret = ps8640_write_bytes(client2, buf, 3);
> + if (ret)
> + goto error;
> + cpy_len = fw->size >= 256 + pos ? 256 : fw->size - pos;
> + buf[0] = 0;
> + memcpy(buf + 1, fw->data + pos, cpy_len);
> + ret = ps8640_write_bytes(client7, buf, cpy_len + 1);
> + if (ret)
> + goto error;
> +
> + dev_dbg(dev, "fw update completed %zu / %zu bytes\n", pos,
> + fw->size);
> + }
> + return 0;
> +
> +error:
> + dev_err(dev, "failed write external flash, %d\n", ret);
> + return ret;
> +}
> +
> +static int ps8640_spi_normal_mode(struct ps8640 *ps_bridge)
> +{
> + u8 cmd[2];
> + struct i2c_client *client = ps_bridge->page[2];
> +
> + /* Enable-Write-Status-Register */
> + cmd[0] = WRITE_ENABLE_CMD;
> + ps8640_spi_send_cmd(ps_bridge, cmd, 1);
> +
> + /* protect BPL/BP0/BP1 */
> + cmd[0] = WRITE_STATUS_REG_CMD;
> + cmd[1] = BLK_PROTECT_BITS | STATUS_REG_PROTECT;
> + ps8640_spi_send_cmd(ps_bridge, cmd, 2);
> +
> + /* wait for SPI rom ready */
> + ps8640_wait_rom_idle(ps_bridge);
> +
> + /* disable PS8640 mapping function */
> + ps8640_write_byte(client, PAGE2_ENCTLSPI_WR, 0x00);
> +
> + if (ps_bridge->gpio_mode_sel)
> + gpiod_set_value(ps_bridge->gpio_mode_sel, 1);
> + return 0;
> +}
> +
> +static int ps8640_enter_bl(struct ps8640 *ps_bridge)
> +{
> + ps_bridge->in_fw_update = true;
> + return ps8640_spi_dl_mode(ps_bridge);
> +}
> +
> +static void ps8640_exit_bl(struct ps8640 *ps_bridge, const struct firmware *fw)
> +{
> + ps8640_spi_normal_mode(ps_bridge);
> + ps_bridge->in_fw_update = false;
> +}
> +
> +static int ps8640_load_fw(struct ps8640 *ps_bridge, const struct firmware *fw)
> +{
> + struct i2c_client *client = ps_bridge->page[0];
> + struct device *dev = &client->dev;
> + int ret;
> + bool ps8640_status_backup = ps_bridge->enabled;
> +
> + ret = ps8640_validate_firmware(ps_bridge, fw);
> + if (ret)
> + return ret;
> +
> + mutex_lock(&ps_bridge->fw_mutex);
> + if (!ps_bridge->in_fw_update) {
> + if (!ps8640_status_backup)
> + ps8640_pre_enable(&ps_bridge->bridge);
> +
> + ret = ps8640_enter_bl(ps_bridge);
> + if (ret)
> + goto exit;
> + }
> +
> + ret = ps8640_rom_prepare(ps_bridge);
> + if (ret)
> + goto exit;
> +
> + ret = ps8640_write_rom(ps_bridge, fw);
> +
> +exit:
> + if (ret)
> + dev_err(dev, "Failed to load firmware, %d\n", ret);
> +
> + ps8640_exit_bl(ps_bridge, fw);
> + if (!ps8640_status_backup)
> + ps8640_post_disable(&ps_bridge->bridge);
> + mutex_unlock(&ps_bridge->fw_mutex);
> + return ret;
> +}
> +
> +static ssize_t ps8640_update_fw_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct ps8640 *ps_bridge = i2c_get_clientdata(client);
> + const struct firmware *fw;
> + int error;
> +
> + error = request_firmware(&fw, PS_FW_NAME, dev);
> + if (error) {
> + dev_err(dev, "Unable to open firmware %s: %d\n",
> + PS_FW_NAME, error);
> + return error;
> + }
> +
> + error = ps8640_load_fw(ps_bridge, fw);
> + if (error)
> + dev_err(dev, "The firmware update failed(%d)\n", error);
> + else
> + dev_info(dev, "The firmware update succeeded\n");
> +
> + release_firmware(fw);
> + return error ? error : count;
> +}
> +
> +static DEVICE_ATTR(fw_version, S_IRUGO, ps8640_fw_version_show, NULL);
> +static DEVICE_ATTR(hw_version, S_IRUGO, ps8640_hw_version_show, NULL);
> +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, ps8640_update_fw_store);
> +
> +static struct attribute *ps8640_attrs[] = {
> + &dev_attr_fw_version.attr,
> + &dev_attr_hw_version.attr,
> + &dev_attr_update_fw.attr,
> + NULL
> +};
> +
> +static const struct attribute_group ps8640_attr_group = {
> + .attrs = ps8640_attrs,
> +};
> +
> +static void ps8640_remove_sysfs_group(void *data)
> +{
> + struct ps8640 *ps_bridge = data;
> +
> + sysfs_remove_group(&ps_bridge->page[0]->dev.kobj, &ps8640_attr_group);
> +}
> +
> +static int ps8640_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct device *dev = &client->dev;
> + struct ps8640 *ps_bridge;
> + struct device_node *np = dev->of_node;
> + struct device_node *port, *out_ep;
> + struct device_node *panel_node = NULL;
> + int ret;
> + u32 i;
> +
> + ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL);
> + if (!ps_bridge)
> + return -ENOMEM;
> +
> + /* port@1 is ps8640 output port */
> + port = of_graph_get_port_by_id(np, 1);
> + if (port) {
> + out_ep = of_get_child_by_name(port, "endpoint");
> + of_node_put(port);
> + if (out_ep) {
> + panel_node = of_graph_get_remote_port_parent(out_ep);
> + of_node_put(out_ep);
> + }
> + }
> + if (panel_node) {
> + ps_bridge->panel = of_drm_find_panel(panel_node);
> + of_node_put(panel_node);
> + if (!ps_bridge->panel)
> + return -EPROBE_DEFER;
> + }
> +
> + mutex_init(&ps_bridge->fw_mutex);
> + ps_bridge->supplies[0].supply = "vdd33";
> + ps_bridge->supplies[1].supply = "vdd12";
> + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ps_bridge->supplies),
> + ps_bridge->supplies);
> + if (ret) {
> + dev_info(dev, "failed to get regulators: %d\n", ret);
> + return ret;
> + }
> +
> + ps_bridge->gpio_mode_sel = devm_gpiod_get_optional(&client->dev,
> + "mode-sel",
> + GPIOD_OUT_HIGH);
> + if (IS_ERR(ps_bridge->gpio_mode_sel)) {
> + ret = PTR_ERR(ps_bridge->gpio_mode_sel);
> + dev_err(dev, "cannot get mode-sel %d\n", ret);
> + return ret;
> + }
> +
> + ps_bridge->gpio_power_down = devm_gpiod_get(&client->dev, "sleep",
> + GPIOD_OUT_LOW);
> + if (IS_ERR(ps_bridge->gpio_power_down)) {
> + ret = PTR_ERR(ps_bridge->gpio_power_down);
> + dev_err(dev, "cannot get sleep: %d\n", ret);
> + return ret;
> + }
> +
> + /*
> + * Request the reset pin low to avoid the bridge being
> + * initialized prematurely
> + */
> + ps_bridge->gpio_reset = devm_gpiod_get(&client->dev, "reset",
> + GPIOD_OUT_LOW);
> + if (IS_ERR(ps_bridge->gpio_reset)) {
> + ret = PTR_ERR(ps_bridge->gpio_reset);
> + dev_err(dev, "cannot get reset: %d\n", ret);
> + return ret;
> + }
> +
> + ps_bridge->bridge.funcs = &ps8640_bridge_funcs;
> + ps_bridge->bridge.of_node = dev->of_node;
> +
> + ps_bridge->page[0] = client;
> + ps_bridge->ddc_i2c = i2c_new_dummy(client->adapter, EDID_I2C_ADDR);
> + if (!ps_bridge->ddc_i2c) {
> + dev_err(dev, "failed ddc_i2c dummy device, address%02x\n",
> + EDID_I2C_ADDR);
> + return -EBUSY;
> + }
> + /*
> + * ps8640 uses multiple addresses, use dummy devices for them
> + * page[0]: for DP control
> + * page[1]: for VIDEO Bridge
> + * page[2]: for control top
> + * page[3]: for DSI Link Control1
> + * page[4]: for MIPI Phy
> + * page[5]: for VPLL
> + * page[6]: for DSI Link Control2
> + * page[7]: for spi rom mapping
> + */
> + for (i = 1; i < MAX_DEVS; i++) {
> + ps_bridge->page[i] = i2c_new_dummy(client->adapter,
> + client->addr + i);
> + if (!ps_bridge->page[i]) {
> + dev_err(dev, "failed i2c dummy device, address%02x\n",
> + client->addr + i);
> + ret = -EBUSY;
> + goto exit_dummy;
> + }
> + }
> + i2c_set_clientdata(client, ps_bridge);
> +
> + ret = sysfs_create_group(&client->dev.kobj, &ps8640_attr_group);
> + if (ret) {
> + dev_err(dev, "failed to create sysfs entries: %d\n", ret);
> + goto exit_dummy;
> + }
> +
> + ret = devm_add_action(dev, ps8640_remove_sysfs_group, ps_bridge);
> + if (ret) {
> + dev_err(dev, "failed to add sysfs cleanup action: %d\n", ret);
> + goto exit_remove_sysfs;
> + }
> +
> + ret = drm_bridge_add(&ps_bridge->bridge);
> + if (ret) {
> + dev_err(dev, "Failed to add bridge: %d\n", ret);
> + goto exit_remove_sysfs;
> + }
> + return 0;
> +
> +exit_remove_sysfs:
> + sysfs_remove_group(&ps_bridge->page[0]->dev.kobj, &ps8640_attr_group);
> +exit_dummy:
> + while (--i)
> + i2c_unregister_device(ps_bridge->page[i]);
> + i2c_unregister_device(ps_bridge->ddc_i2c);
> + return ret;
> +}
> +
> +static int ps8640_remove(struct i2c_client *client)
> +{
> + struct ps8640 *ps_bridge = i2c_get_clientdata(client);
> + int i = MAX_DEVS;
> +
> + drm_bridge_remove(&ps_bridge->bridge);
> + sysfs_remove_group(&ps_bridge->page[0]->dev.kobj, &ps8640_attr_group);
> + while (--i)
> + i2c_unregister_device(ps_bridge->page[i]);
> +
> + i2c_unregister_device(ps_bridge->ddc_i2c);
> + return 0;
> +}
> +
> +static const struct i2c_device_id ps8640_i2c_table[] = {
> + { "parade,ps8640", 0 },
I think that you should remove the manufacturer prefix here, note that
the I2C core removes the manufacturer prefix from the compatible field
so it reports to user-space the uevent: i2c:ps8640, but this doesn't
match with the device id which is parade,ps8640. If you build the
driver as a module will not autoload the driver, to fix this just
remove the manufacturer prefix.
> + {},
nit: add { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(i2c, ps8640_i2c_table);
> +
> +static const struct of_device_id ps8640_match[] = {
> + { .compatible = "parade,ps8640" },
> + {},
nit: add { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, ps8640_match);
> +
> +static struct i2c_driver ps8640_driver = {
> + .id_table = ps8640_i2c_table,
> + .probe = ps8640_probe,
> + .remove = ps8640_remove,
> + .driver = {
> + .name = "parade,ps8640",
Also remove the manufacturer from the name, guess it's more common.
> + .of_match_table = ps8640_match,
> + },
> +};
> +module_i2c_driver(ps8640_driver);
> +
> +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
> +MODULE_AUTHOR("CK Hu <ck.hu@mediatek.com>");
> +MODULE_DESCRIPTION("PARADE ps8640 DSI-eDP converter driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.7.9.5
>
A part of these minor things.
Reviewed-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
>
> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek
^ permalink raw reply
* Re: [PATCH V3 1/9] PM / OPP: Reword binding supporting multiple regulators per device
From: Mark Brown @ 2016-11-10 16:36 UTC (permalink / raw)
To: Viresh Kumar
Cc: Rafael Wysocki, nm-l0cyMroinI0, sboyd-sgV2jX0FEOL9JmXXK+q4OQ,
Viresh Kumar, linaro-kernel-cunTk1MwBs8s++Sfvej+rw,
linux-pm-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Vincent Guittot,
robh-DgEjT+Ai2ygdnm+yROfE0A, d-gerlach-l0cyMroinI0,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161110040440.GA11670@vireshk-i7>
[-- Attachment #1: Type: text/plain, Size: 1563 bytes --]
On Thu, Nov 10, 2016 at 09:34:40AM +0530, Viresh Kumar wrote:
> On 09-11-16, 14:58, Mark Brown wrote:
> > On Wed, Oct 26, 2016 at 12:02:56PM +0530, Viresh Kumar wrote:
> > > + Entries for multiple regulators shall be provided in the same field separated
> > > + by angular brackets <>. The OPP binding doesn't provide any provisions to
> > > + relate the values to their power supplies or the order in which the supplies
> > > + need to be configured.
> > I don't understand how this works. If we have an unordered list of
> > values to set for regulators how will we make sense of them?
> The platform driver is responsible to identify the order and pass it on to the
> OPP core. And the platform driver needs to have that hard coded.
That *really* should be in the binding. Honestly if the binding is this
vague I'm not even clear that it's worth documenting these properties at
this level, might be better to just put the documentation in the
platform driver bindings.
> > > - cpu-supply = <&cpu_supply0>, <&cpu_supply1>, <&cpu_supply2>;
> > > + vcc0-supply = <&cpu_supply0>;
> > > + vcc1-supply = <&cpu_supply1>;
> > > + vcc2-supply = <&cpu_supply2>;
> > This change doesn't seem to correspond to the documentation change.
> This rectifies the incorrect binding previously added to the example, which I
> realized to be incorrect only while attempting to code for it. And so it brings
> the example on the same state as the documentation now.
Then that should be in a separate patch with a changelog explaining what
the change is doing.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]
^ permalink raw reply
* [PATCH v2 1/3] dt/binding: ti-tsc-adc: deprecate ti,charge-delay and add binding doc for ti,charge-delay-ns
From: Mugunthan V N @ 2016-11-10 16:35 UTC (permalink / raw)
To: linux-input-u79uwXL29TY76Z2rM5mHXA
Cc: Dmitry Torokhov, Jonathan Cameron, Rob Herring, Mark Rutland,
Lee Jones, Sekhar Nori, Vignesh R,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Mugunthan V N
In-Reply-To: <20161110163515.27598-1-mugunthanvnm-l0cyMroinI0@public.gmane.org>
ti,charge-delay represents the duration that ADC should wait
before sampling the ADC line to detect the touch location and pen
up/downs. Currently the ADC clock is set at 3MHz. The device-tree
entry for ti,charge-delay is based on assumption of ADC clock at
3MHz, but it can be operated up to 24MHz clock. Representing the
charge delay of touchscreen in terms of ADC clocks is incorrect.
So change this representation to ti,charge-delay-ns, which driver
can convert it to number clock cycles based on ref clock
frequency.
Signed-off-by: Mugunthan V N <mugunthanvnm-l0cyMroinI0@public.gmane.org>
---
.../bindings/input/touchscreen/ti-tsc-adc.txt | 32 ++++++++++++++--------
1 file changed, 21 insertions(+), 11 deletions(-)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt b/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt
index b1163bf97146..83570776c804 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt
@@ -30,17 +30,27 @@ Required properties:
Optional properties:
- child "tsc"
- ti,charge-delay: Length of touch screen charge delay step in terms of
- ADC clock cycles. Charge delay value should be large
- in order to avoid false pen-up events. This value
- effects the overall sampling speed, hence need to be
- kept as low as possible, while avoiding false pen-up
- event. Start from a lower value, say 0x400, and
- increase value until false pen-up events are avoided.
- The pen-up detection happens immediately after the
- charge step, so this does in fact function as a
- hardware knob for adjusting the amount of "settling
- time".
+ ti,charge-delay-ns: Length of touch screen charge delay step in terms of
+ nano Seconds. Charge delay value should be large
+ in order to avoid false pen-up events. This value
+ effects the overall sampling speed, hence need to be
+ kept as low as possible, while avoiding false pen-up
+ event. Start from a lower value, say 41000nS, and
+ increase value until false pen-up events are avoided.
+ The pen-up detection happens immediately after the
+ charge step, so this does in fact function as a
+ hardware knob for adjusting the amount of "settling
+ time".
+ ti,charge-delay: Deprecated as representing charge delay should be
+ represented in time as the ADC clock may be different.
+ Currently this value is calculated based on 3MHz ADC
+ ref clock, but ADC clock can operate upto 24MHz clock.
+ ADC clock will be changed to 24MHz to increase the
+ number of ADC samples. So to keep the backward
+ compatibility, driver will consider this value is
+ calculated for 3MHz clock so the value will be
+ multiplied by 8 to compensate the change in reference
+ clock.
- child "adc"
ti,chan-step-opendelay: List of open delays for each channel of
--
2.11.0.rc0.7.gbe5a750
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 0/3] increase TSCADC clock to 24MHz and fix ti,charge-delay to represent in nS
From: Mugunthan V N @ 2016-11-10 16:35 UTC (permalink / raw)
To: linux-input
Cc: Dmitry Torokhov, Jonathan Cameron, Rob Herring, Mark Rutland,
Lee Jones, Sekhar Nori, Vignesh R, devicetree, linux-omap,
linux-kernel, Mugunthan V N
This patch series enables ADC to be clocked at 24MHz as the
TI AM335x ADC driver has already adopted to use DMA to transfer
ADC samples. Now ADC can generated upto 800K Samples per second
with the patch [1] on AM335x BBB and AM437x GP EVM.
when ADC ref clock is set at 24MHz, I am seeing some issue with
touch screen pointer as the pointer jumps to random locations
with free draw application. The issue is due to increase in ADC
clock and charge delay for the touchscreen ADC line duration
reduced.
So the notation of ti,charge-delay in terms of ADC clock is
wrong, it has to be represented in time and driver has to convert
the charge delay time to ADC clocks based on what ADC clock
frequency is set.
Measured the performance with the iio_generic_buffer with the
patch [2] applied
Verified the touch screen on AM335x GP EVM and AM335x BBB LCD7
cape with [3] dts for display and touch screen to work.
Changes from initial version:
* Removed modification to STEPCONFIG_OPENDLY defined as it
doesn't affect/improve touchscreen performance.
* Changed ti,charge-delay to ti,charge-delay-ns
[1] - http://pastebin.ubuntu.com/23357935/
[2] - http://pastebin.ubuntu.com/23357939/
[3] - http://pastebin.ubuntu.com/23456616/
Mugunthan V N (3):
dt/binding: ti-tsc-adc: deprecate ti,charge-delay and add binding doc
for ti,charge-delay-ns
Input: ti_am335x_tsc: Add support for ti,charge-delay-ns
drivers: mfd: ti_am335x_tscadc: increase ADC ref clock to 24MHz
.../bindings/input/touchscreen/ti-tsc-adc.txt | 32 ++++++++++++++--------
drivers/input/touchscreen/ti_am335x_tsc.c | 31 +++++++++++++++------
include/linux/mfd/ti_am335x_tscadc.h | 2 +-
3 files changed, 45 insertions(+), 20 deletions(-)
--
2.11.0.rc0.7.gbe5a750
^ permalink raw reply
* [PATCH v2 6/6] ARM: dts: stm32f429: enable adc on eval board
From: Fabrice Gasnier @ 2016-11-10 16:18 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: jic23-DgEjT+Ai2ygdnm+yROfE0A, lee.jones-QSEj5FYQhm4dnm+yROfE0A,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg,
fabrice.gasnier-qxv4g6HH51o
In-Reply-To: <1478794738-28933-1-git-send-email-fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
Enable analog to digital converter on stm32f429i-eval board.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
---
arch/arm/boot/dts/stm32429i-eval.dts | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index 6bfc595..c144735 100644
--- a/arch/arm/boot/dts/stm32429i-eval.dts
+++ b/arch/arm/boot/dts/stm32429i-eval.dts
@@ -65,6 +65,20 @@
serial0 = &usart1;
};
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_vref: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "vref";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
+
leds {
compatible = "gpio-leds";
green {
@@ -123,3 +137,14 @@
pinctrl-names = "default";
status = "okay";
};
+
+&adc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&adc3_in8_pin>;
+ vref-supply = <®_vref>;
+ status = "okay";
+ adc3: adc@200 {
+ st,adc-channels = <8>;
+ status = "okay";
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH v2 5/6] ARM: dts: stm32f429: Add adc support
From: Fabrice Gasnier @ 2016-11-10 16:18 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: mark.rutland, lars, alexandre.torgue, pmeerw, linux, robh+dt,
mcoquelin.stm32, knaack.h, fabrice.gasnier, lee.jones, jic23
In-Reply-To: <1478794738-28933-1-git-send-email-fabrice.gasnier@st.com>
Add adc support & pinctrl analog phandle (adc3_in8) to stm32f429.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/boot/dts/stm32f429.dtsi | 49 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 336ee4f..f198132 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -172,6 +172,49 @@
status = "disabled";
};
+ adc: adc@40012000 {
+ compatible = "st,stm32f4-adc-core";
+ reg = <0x40012000 0x400>;
+ interrupts = <18>;
+ clocks = <&rcc 0 168>;
+ clock-names = "adc";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+
+ adc1: adc@0 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ interrupt-parent = <&adc>;
+ interrupts = <0>;
+ status = "disabled";
+ };
+
+ adc2: adc@100 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x100>;
+ clocks = <&rcc 0 169>;
+ interrupt-parent = <&adc>;
+ interrupts = <1>;
+ status = "disabled";
+ };
+
+ adc3: adc@200 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x200>;
+ clocks = <&rcc 0 170>;
+ interrupt-parent = <&adc>;
+ interrupts = <2>;
+ status = "disabled";
+ };
+ };
+
syscfg: system-config@40013800 {
compatible = "syscon";
reg = <0x40013800 0x400>;
@@ -332,6 +375,12 @@
slew-rate = <2>;
};
};
+
+ adc3_in8_pin: adc@200 {
+ pins {
+ pinmux = <STM32F429_PF10_FUNC_ANALOG>;
+ };
+ };
};
rcc: rcc@40023810 {
--
1.9.1
^ permalink raw reply related
* [PATCH v2 4/6] ARM: configs: stm32: enable ADC driver
From: Fabrice Gasnier @ 2016-11-10 16:18 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: mark.rutland, lars, alexandre.torgue, pmeerw, linux, robh+dt,
mcoquelin.stm32, knaack.h, fabrice.gasnier, lee.jones, jic23
In-Reply-To: <1478794738-28933-1-git-send-email-fabrice.gasnier@st.com>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/configs/stm32_defconfig | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
index 1e5ec2a..e012570 100644
--- a/arch/arm/configs/stm32_defconfig
+++ b/arch/arm/configs/stm32_defconfig
@@ -49,6 +49,7 @@ CONFIG_SERIAL_STM32=y
CONFIG_SERIAL_STM32_CONSOLE=y
# CONFIG_HW_RANDOM is not set
# CONFIG_HWMON is not set
+CONFIG_MFD_STM32_ADC=y
# CONFIG_USB_SUPPORT is not set
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
@@ -57,6 +58,8 @@ CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_DMADEVICES=y
CONFIG_STM32_DMA=y
+CONFIG_IIO=y
+CONFIG_STM32_ADC=y
# CONFIG_FILE_LOCKING is not set
# CONFIG_DNOTIFY is not set
# CONFIG_INOTIFY_USER is not set
--
1.9.1
^ permalink raw reply related
* [PATCH v2 3/6] iio: adc: Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-11-10 16:18 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: jic23, lee.jones, linux, robh+dt, mark.rutland, mcoquelin.stm32,
alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier
In-Reply-To: <1478794738-28933-1-git-send-email-fabrice.gasnier@st.com>
This patch adds support for STMicroelectronics STM32 MCU's analog to
digital converter.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/Kconfig | 10 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32-adc.c | 525 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 536 insertions(+)
create mode 100644 drivers/iio/adc/stm32-adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7edcf32..61ba674 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -419,6 +419,16 @@ config ROCKCHIP_SARADC
To compile this driver as a module, choose M here: the
module will be called rockchip_saradc.
+config STM32_ADC
+ tristate "STMicroelectronics STM32 adc"
+ depends on MFD_STM32_ADC
+ help
+ Say yes here to build support for 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 STX104
tristate "Apex Embedded Systems STX104 driver"
depends on X86 && ISA_BUS_API
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..df7a221 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_STX104) += stx104.o
+obj-$(CONFIG_STM32_ADC) += stm32-adc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
new file mode 100644
index 0000000..2be5fee
--- /dev/null
+++ b/drivers/iio/adc/stm32-adc.c
@@ -0,0 +1,525 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/stm32-adc-core.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+/* 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
+
+/* STM32F4_ADCX_SR - bit fields */
+#define STM32F4_OVR BIT(5)
+#define STM32F4_STRT BIT(4)
+#define STM32F4_EOC BIT(1)
+
+/* STM32F4_ADCX_CR1 - bit fields */
+#define STM32F4_OVRIE BIT(26)
+#define STM32F4_SCAN BIT(8)
+#define STM32F4_EOCIE BIT(5)
+
+/* STM32F4_ADCX_CR2 - bit fields */
+#define STM32F4_SWSTART BIT(30)
+#define STM32F4_EXTEN_MASK GENMASK(29, 28)
+#define STM32F4_EOCS BIT(10)
+#define STM32F4_ADON BIT(0)
+
+/* STM32F4_ADCX_SQR1 - bit fields */
+#define STM32F4_L_SHIFT 20
+#define STM32F4_L_MASK GENMASK(23, 20)
+
+/* STM32F4_ADCX_SQR3 - bit fields */
+#define STM32F4_SQ1_SHIFT 0
+#define STM32F4_SQ1_MASK GENMASK(4, 0)
+
+#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */
+#define STM32_ADC_TIMEOUT_US 100000
+#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+
+/**
+ * struct stm32_adc - private data of each ADC IIO instance
+ * @common: reference to ADC block common data
+ * @offset: ADC instance register offset in ADC block
+ * @completion: end of single conversion completion
+ * @buffer: data buffer
+ * @clk: optional adc clock, for this adc instance
+ * @irq: interrupt for this adc instance
+ * @lock: spinlock
+ */
+struct stm32_adc {
+ struct stm32_adc_common *common;
+ u32 offset;
+ struct completion completion;
+ u16 *buffer;
+ struct clk *clk;
+ int irq;
+ spinlock_t lock; /* interrupt lock */
+};
+
+/**
+ * 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;
+};
+
+/* Input definitions common for all STM32F4 instances */
+static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
+ { 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" },
+};
+
+/**
+ * STM32 ADC registers access routines
+ * @adc: stm32 adc instance
+ * @reg: reg offset in adc instance
+ *
+ * Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp.
+ * for adc1, adc2 and adc3.
+ */
+static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
+{
+ return readl_relaxed(adc->common->base + adc->offset + reg);
+}
+
+static u32 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
+{
+ return readw_relaxed(adc->common->base + adc->offset + reg);
+}
+
+static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
+{
+ writel_relaxed(val, adc->common->base + adc->offset + reg);
+}
+
+static 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 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_adc_conv_irq_enable() - Enable end of conversion interrupt
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
+{
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_EOCIE);
+};
+
+/**
+ * stm32_adc_conv_irq_disable() - Disable end of conversion interrupt
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
+{
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_EOCIE);
+}
+
+/**
+ * stm32_adc_start_conv() - Start conversions for regular channels.
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_start_conv(struct stm32_adc *adc)
+{
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, STM32F4_EOCS | STM32F4_ADON);
+
+ /* Wait for Power-up time (tSTAB from datasheet) */
+ usleep_range(2, 3);
+
+ /* Software start ? (e.g. trigger detection disabled ?) */
+ if (!(stm32_adc_readl(adc, STM32F4_ADCX_CR2) & STM32F4_EXTEN_MASK))
+ stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, STM32F4_SWSTART);
+}
+
+static void stm32_adc_stop_conv(struct stm32_adc *adc)
+{
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
+
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
+}
+
+/**
+ * stm32_adc_single_conv() - Performs a single conversion
+ * @indio_dev: IIO device
+ * @chan: IIO channel
+ * @res: conversion result
+ *
+ * The function performs a single conversion on a given channel:
+ * - Program sequencer with one channel (e.g. in SQ1 with len = 1)
+ * - Use SW trigger
+ * - Start conversion, then wait for interrupt completion.
+ */
+static int stm32_adc_single_conv(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *res)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ long timeout;
+ u32 val;
+ u16 result;
+ int ret;
+
+ reinit_completion(&adc->completion);
+
+ adc->buffer = &result;
+
+ /* Program chan number in regular sequence */
+ val = stm32_adc_readl(adc, STM32F4_ADCX_SQR3);
+ val &= ~STM32F4_SQ1_MASK;
+ val |= chan->channel << STM32F4_SQ1_SHIFT;
+ stm32_adc_writel(adc, STM32F4_ADCX_SQR3, val);
+
+ /* Set regular sequence len (0 for 1 conversion) */
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_SQR1, STM32F4_L_MASK);
+
+ /* Trigger detection disabled (conversion can be launched in SW) */
+ stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
+
+ stm32_adc_conv_irq_enable(adc);
+
+ stm32_adc_start_conv(adc);
+
+ 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 {
+ *res = result;
+ ret = IIO_VAL_INT;
+ }
+
+ stm32_adc_stop_conv(adc);
+
+ stm32_adc_conv_irq_disable(adc);
+
+ 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);
+ else
+ ret = -EINVAL;
+ 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;
+}
+
+static irqreturn_t stm32_adc_isr(int irq, void *data)
+{
+ struct stm32_adc *adc = data;
+ u32 status = stm32_adc_readl(adc, STM32F4_ADCX_SR);
+ irqreturn_t ret = IRQ_NONE;
+
+ if (status & STM32F4_EOC) {
+ *adc->buffer = stm32_adc_readw(adc, STM32F4_ADCX_DR);
+ complete(&adc->completion);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+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,
+ .debugfs_reg_access = stm32_adc_debugfs_reg_access,
+ .of_xlate = stm32_adc_of_xlate,
+ .driver_module = THIS_MODULE,
+};
+
+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)
+{
+ 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 = 12;
+ chan->scan_type.storagebits = 16;
+}
+
+static int stm32_adc_chan_of_init(struct iio_dev *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;
+ u32 val;
+
+ num_channels = of_property_count_u32_elems(node, "st,adc-channels");
+ if (num_channels < 0 ||
+ num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) {
+ dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
+ return num_channels < 0 ? num_channels : -EINVAL;
+ }
+
+ 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) {
+ if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) {
+ dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
+ return -EINVAL;
+ }
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+ &stm32f4_adc123_channels[val],
+ scan_index);
+ scan_index++;
+ }
+
+ indio_dev->num_channels = scan_index;
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int stm32_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct stm32_adc *adc;
+ int ret;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->common = dev_get_drvdata(pdev->dev.parent);
+ spin_lock_init(&adc->lock);
+ init_completion(&adc->completion);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &stm32_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ platform_set_drvdata(pdev, adc);
+
+ ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "missing reg property\n");
+ return -EINVAL;
+ }
+
+ adc->irq = platform_get_irq(pdev, 0);
+ if (adc->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return adc->irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
+ 0, pdev->name, adc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ return ret;
+ }
+
+ adc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(adc->clk)) {
+ adc->clk = NULL;
+ dev_dbg(&pdev->dev, "No child clk found\n");
+ } else {
+ ret = clk_prepare_enable(adc->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "clk enable failed\n");
+ return ret;
+ }
+ }
+
+ ret = stm32_adc_chan_of_init(indio_dev);
+ if (ret < 0)
+ goto err_clk_disable;
+
+ ret = devm_iio_device_register(&pdev->dev, indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "iio dev register failed\n");
+ goto err_clk_disable;
+ }
+
+ return 0;
+
+err_clk_disable:
+ if (adc->clk)
+ clk_disable_unprepare(adc->clk);
+
+ return ret;
+}
+
+static int stm32_adc_remove(struct platform_device *pdev)
+{
+ struct stm32_adc *adc = platform_get_drvdata(pdev);
+
+ if (adc->clk)
+ clk_disable_unprepare(adc->clk);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_adc_of_match[] = {
+ { .compatible = "st,stm32f4-adc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
+
+static struct platform_driver stm32_adc_driver = {
+ .probe = stm32_adc_probe,
+ .remove = stm32_adc_remove,
+ .driver = {
+ .name = "stm32-adc",
+ .of_match_table = stm32_adc_of_match,
+ },
+};
+module_platform_driver(stm32_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-adc");
--
1.9.1
^ permalink raw reply related
* [PATCH v2 2/6] mfd: stm32-adc: Add support for stm32 ADC
From: Fabrice Gasnier @ 2016-11-10 16:18 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: jic23, lee.jones, linux, robh+dt, mark.rutland, mcoquelin.stm32,
alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier
In-Reply-To: <1478794738-28933-1-git-send-email-fabrice.gasnier@st.com>
Add core driver for STMicroelectronics STM32 ADC (Analog to Digital
Converter). STM32 ADC can be composed of up to 3 ADCs with shared
resources like clock prescaler, common interrupt line and analog
reference voltage.
This core driver basically manages shared resources.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/mfd/Kconfig | 14 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/stm32-adc-core.c | 301 +++++++++++++++++++++++++++++++++++++
include/linux/mfd/stm32-adc-core.h | 52 +++++++
4 files changed, 368 insertions(+)
create mode 100644 drivers/mfd/stm32-adc-core.c
create mode 100644 include/linux/mfd/stm32-adc-core.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c6df644..2580cee 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1152,6 +1152,20 @@ config MFD_PALMAS
If you say yes here you get support for the Palmas
series of PMIC chips from Texas Instruments.
+config MFD_STM32_ADC
+ tristate "STMicroelectronics STM32 adc"
+ depends on ARCH_STM32 || COMPILE_TEST
+ depends on OF
+ select MFD_CORE
+ select REGULATOR
+ select REGULATOR_FIXED_VOLTAGE
+ help
+ Select this option to enable the core driver for 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-core.
+
config TPS6105X
tristate "TI TPS61050/61052 Boost Converters"
depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9834e66..4571506 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -185,6 +185,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o
obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
obj-$(CONFIG_MFD_PALMAS) += palmas.o
+obj-$(CONFIG_MFD_STM32_ADC) += stm32-adc-core.o
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
obj-$(CONFIG_MFD_RK808) += rk808.o
diff --git a/drivers/mfd/stm32-adc-core.c b/drivers/mfd/stm32-adc-core.c
new file mode 100644
index 0000000..bcf52fb
--- /dev/null
+++ b/drivers/mfd/stm32-adc-core.c
@@ -0,0 +1,301 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * Inspired from: fsl-imx25-tsadc
+ *
+ * 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/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdesc.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/stm32-adc-core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
+#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
+
+/* STM32F4_ADC_CSR - bit fields */
+#define STM32F4_EOC3 BIT(17)
+#define STM32F4_EOC2 BIT(9)
+#define STM32F4_EOC1 BIT(1)
+
+/* STM32F4_ADC_CCR - bit fields */
+#define STM32F4_ADC_ADCPRE_SHIFT 16
+#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
+
+/* STM32 F4 maximum analog clock rate (from datasheet) */
+#define STM32F4_ADC_MAX_CLK_RATE 36000000
+
+/**
+ * struct stm32_adc_priv - stm32 ADC core private data
+ * @irq: irq for ADC block
+ * @domain: irq domain reference
+ * @aclk: clock reference for the analog circuitry
+ * @vref: regulator reference
+ * @common: common data for all ADC instances
+ */
+struct stm32_adc_priv {
+ int irq;
+ struct irq_domain *domain;
+ struct clk *aclk;
+ struct regulator *vref;
+ struct stm32_adc_common common;
+};
+
+static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
+{
+ return container_of(com, struct stm32_adc_priv, common);
+}
+
+/* STM32F4 ADC internal common clock prescaler division ratios */
+static int stm32f4_pclk_div[] = {2, 4, 6, 8};
+
+/**
+ * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler
+ * @priv: stm32 ADC core private data
+ * Select clock prescaler used for analog conversions, before using ADC.
+ */
+static int stm32f4_adc_clk_sel(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ unsigned long rate;
+ u32 val;
+ int i;
+
+ rate = clk_get_rate(priv->aclk);
+ for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
+ if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
+ break;
+ }
+ if (i >= ARRAY_SIZE(stm32f4_pclk_div))
+ return -EINVAL;
+
+ val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
+ val &= ~STM32F4_ADC_ADCPRE_MASK;
+ val |= i << STM32F4_ADC_ADCPRE_SHIFT;
+ writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
+
+ dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
+ rate / (stm32f4_pclk_div[i] * 1000));
+
+ return 0;
+}
+
+/* ADC common interrupt for all instances */
+static void stm32_adc_irq_handler(struct irq_desc *desc)
+{
+ struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u32 status;
+
+ chained_irq_enter(chip, desc);
+ status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
+
+ if (status & STM32F4_EOC1)
+ generic_handle_irq(irq_find_mapping(priv->domain, 0));
+
+ if (status & STM32F4_EOC2)
+ generic_handle_irq(irq_find_mapping(priv->domain, 1));
+
+ if (status & STM32F4_EOC3)
+ generic_handle_irq(irq_find_mapping(priv->domain, 2));
+
+ chained_irq_exit(chip, desc);
+};
+
+static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_data(irq, d->host_data);
+ irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
+
+ return 0;
+}
+
+static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops stm32_adc_domain_ops = {
+ .map = stm32_adc_domain_map,
+ .unmap = stm32_adc_domain_unmap,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int stm32_adc_irq_probe(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ struct device_node *np = pdev->dev.of_node;
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (priv->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return priv->irq;
+ }
+
+ priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
+ &stm32_adc_domain_ops,
+ priv);
+ if (!priv->domain) {
+ dev_err(&pdev->dev, "Failed to add irq domain\n");
+ return -ENOMEM;
+ }
+
+ irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
+ irq_set_handler_data(priv->irq, priv);
+
+ return 0;
+}
+
+static void stm32_adc_irq_remove(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ int hwirq;
+
+ for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
+ irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
+ irq_domain_remove(priv->domain);
+ irq_set_chained_handler(priv->irq, NULL);
+}
+
+static int stm32_adc_probe(struct platform_device *pdev)
+{
+ struct stm32_adc_priv *priv;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res;
+ int ret;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->common.base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->common.base))
+ return PTR_ERR(priv->common.base);
+
+ priv->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(priv->vref)) {
+ ret = PTR_ERR(priv->vref);
+ dev_err(&pdev->dev, "vref get failed, %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(priv->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref enable failed\n");
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
+ goto err_regulator_disable;
+ }
+ priv->common.vref_mv = ret / 1000;
+ dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
+
+ priv->aclk = devm_clk_get(&pdev->dev, "adc");
+ if (IS_ERR(priv->aclk)) {
+ ret = PTR_ERR(priv->aclk);
+ dev_err(&pdev->dev, "Can't get 'adc' clock\n");
+ goto err_regulator_disable;
+ }
+
+ ret = clk_prepare_enable(priv->aclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "adc clk enable failed\n");
+ goto err_regulator_disable;
+ }
+
+ ret = stm32f4_adc_clk_sel(pdev, priv);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "adc clk selection failed\n");
+ goto err_clk_disable;
+ }
+
+ ret = stm32_adc_irq_probe(pdev, priv);
+ if (ret < 0)
+ goto err_clk_disable;
+
+ platform_set_drvdata(pdev, &priv->common);
+
+ ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to populate DT children\n");
+ goto err_irq_remove;
+ }
+
+ return 0;
+
+err_irq_remove:
+ stm32_adc_irq_remove(pdev, priv);
+
+err_clk_disable:
+ clk_disable_unprepare(priv->aclk);
+
+err_regulator_disable:
+ regulator_disable(priv->vref);
+
+ return ret;
+}
+
+static int stm32_adc_remove(struct platform_device *pdev)
+{
+ struct stm32_adc_common *common = platform_get_drvdata(pdev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+
+ of_platform_depopulate(&pdev->dev);
+ stm32_adc_irq_remove(pdev, priv);
+ clk_disable_unprepare(priv->aclk);
+ regulator_disable(priv->vref);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_adc_of_match[] = {
+ { .compatible = "st,stm32f4-adc-core" },
+};
+MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
+
+static struct platform_driver stm32_adc_driver = {
+ .probe = stm32_adc_probe,
+ .remove = stm32_adc_remove,
+ .driver = {
+ .name = "stm32-adc-core",
+ .of_match_table = stm32_adc_of_match,
+ },
+};
+module_platform_driver(stm32_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC MFD driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-adc-core");
diff --git a/include/linux/mfd/stm32-adc-core.h b/include/linux/mfd/stm32-adc-core.h
new file mode 100644
index 0000000..081fa5f
--- /dev/null
+++ b/include/linux/mfd/stm32-adc-core.h
@@ -0,0 +1,52 @@
+/*
+ * 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_ADC_MAX_ADCS 3
+#define STM32_ADCX_COMN_OFFSET 0x300
+
+/**
+ * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
+ * @base: control registers base cpu addr
+ * @vref_mv: vref voltage (mv)
+ */
+struct stm32_adc_common {
+ void __iomem *base;
+ int vref_mv;
+};
+
+#endif
--
1.9.1
^ permalink raw reply related
* [PATCH v2 1/6] Documentation: dt-bindings: Document STM32 ADC DT bindings
From: Fabrice Gasnier @ 2016-11-10 16:18 UTC (permalink / raw)
To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
Cc: jic23, lee.jones, linux, robh+dt, mark.rutland, mcoquelin.stm32,
alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier
In-Reply-To: <1478794738-28933-1-git-send-email-fabrice.gasnier@st.com>
This patch adds documentation of device tree bindings for the STM32 ADC.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 85 ++++++++++++++++++++++
1 file changed, 85 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
new file mode 100644
index 0000000..8b20c23
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -0,0 +1,85 @@
+STMicroelectronics STM32 ADC device driver
+
+STM32 ADC is a successive approximation analog-to-digital converter.
+It has several multiplexed input channels. Conversions can be performed
+in single, continuous, scan or discontinuous mode. Result of the ADC is
+stored in a left-aligned or right-aligned 32-bit data register.
+Conversions can be launched in software or using hardware triggers.
+
+The analog watchdog feature allows the application to detect if the input
+voltage goes beyond the user-defined, higher or lower thresholds.
+
+Each STM32 ADC block can have up to 3 ADC instances.
+
+Each instance supports two contexts to manage conversions, each one has its
+own configurable sequence and trigger:
+- regular conversion can be done in sequence, running in background
+- injected conversions have higher priority, and so have the ability to
+ interrupt regular conversion sequence (either triggered in SW or HW).
+ Regular sequence is resumed, in case it has been interrupted.
+
+Contents of a stm32 adc root node:
+-----------------------------------
+Required properties:
+- compatible: Should be "st,stm32f4-adc-core".
+- reg: Offset and length of the ADC block register set.
+- interrupts: Must contain the interrupt for ADC block.
+- clocks: Clock for the analog circuitry (common to all ADCs).
+- clock-names: Must be "adc".
+- interrupt-controller: Identifies the controller node as interrupt-parent
+- vref-supply: Phandle to the vref input analog reference voltage.
+- #interrupt-cells = <1>;
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties:
+- A pinctrl state named "default" for each ADC channel may be defined to set
+ inX ADC pins in mode of operation for analog input on external pin.
+
+Contents of a stm32 adc child node:
+-----------------------------------
+An ADC block node should contain at least one subnode, representing an
+ADC instance available on the machine.
+
+Required properties:
+- compatible: Should be "st,stm32f4-adc".
+- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
+- st,adc-channels: List of single-ended channels muxed for this ADC.
+ It can have up to 16 channels, numbered from 0 to 15 (resp. for in0..in15).
+- interrupt-parent: Phandle to the parent interrupt controller.
+- interrupts: IRQ Line for the ADC (e.g. may be 0 for adc@0, 1 for adc@100 or
+ 2 for adc@200).
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
+ Documentation/devicetree/bindings/iio/iio-bindings.txt
+
+Optional properties:
+- clocks: Input clock private to this ADC instance.
+
+Example:
+ adc: adc@40012000 {
+ compatible = "st,stm32f4-adc-core";
+ reg = <0x40012000 0x400>;
+ interrupts = <18>;
+ clocks = <&rcc 0 168>;
+ clock-names = "adc";
+ vref-supply = <®_vref>;
+ interrupt-controller;
+ pinctrl-names = "default";
+ pinctrl-0 = <&adc3_in8_pin>;
+
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@0 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ interrupt-parent = <&adc>;
+ interrupts = <0>;
+ st,adc-channels = <8>;
+ };
+ ...
+ other adc child nodes follow...
+ };
--
1.9.1
^ permalink raw reply related
* [PATCH v2 0/6] Add support for STM32 ADC
From: Fabrice Gasnier @ 2016-11-10 16:18 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: jic23-DgEjT+Ai2ygdnm+yROfE0A, lee.jones-QSEj5FYQhm4dnm+yROfE0A,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg,
fabrice.gasnier-qxv4g6HH51o
This series adds support for STM32F4 ADC into IIO framework.
STM32F4 ADC is a 12-bit successive approximation analog-to-digital
converter with multiplexed input channels. Conversions can
be performed in single, continuous, scan or discontinuous mode.
Conversions can be launched in software or using hardware triggers.
This driver has been developed and tested on STM32F429 eval board.
It consist of a MFD core driver, to manage common resources shared
between up to 3 ADC instances.
Changes in v2:
- Replace single driver model by MFD approach, to handle up to 3 ADCs
as separate devices. Each ADC device then registers a unique IIO
device.
- Make driver as simple as possible for the first instance, to ease
review. For now, I dropped complexity by removing injected support,
triggered buffer mode, dmas.
- Removed abstraction layer (indirection routines, ops) as only stm32f4
is supported.
Fabrice Gasnier (6):
Documentation: dt-bindings: Document STM32 ADC DT bindings
mfd: stm32-adc: Add support for stm32 ADC
iio: adc: Add support for STM32 ADC
ARM: configs: stm32: enable ADC driver
ARM: dts: stm32f429: Add adc support
ARM: dts: stm32f429: enable adc on eval board
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 85 ++++
arch/arm/boot/dts/stm32429i-eval.dts | 25 +
arch/arm/boot/dts/stm32f429.dtsi | 49 ++
arch/arm/configs/stm32_defconfig | 3 +
drivers/iio/adc/Kconfig | 10 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32-adc.c | 525 +++++++++++++++++++++
drivers/mfd/Kconfig | 14 +
drivers/mfd/Makefile | 1 +
drivers/mfd/stm32-adc-core.c | 301 ++++++++++++
include/linux/mfd/stm32-adc-core.h | 52 ++
11 files changed, 1066 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
create mode 100644 drivers/iio/adc/stm32-adc.c
create mode 100644 drivers/mfd/stm32-adc-core.c
create mode 100644 include/linux/mfd/stm32-adc-core.h
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox