* Re: [PATCH v1 03/11] dt-bindings: display/bridge: Add config property for ti948
From: Michael Drake @ 2019-07-12 12:43 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Andrzej Hajda, dri-devel, devicetree, linux-kernel, David Airlie,
Daniel Vetter, Rob Herring, Mark Rutland, linux-kernel,
Patrick Glaser, Nate Case
In-Reply-To: <20190611180710.GT5016@pendragon.ideasonboard.com>
Hi Laurent,
On 11/06/2019 19:07, Laurent Pinchart wrote:
> Hi Michael,
>
> Thank you for the patch.
My pleasure, and thank you for the feedback!
> On Tue, Jun 11, 2019 at 03:04:04PM +0100, Michael Drake wrote:
>> The config property can be used to provide an array of
>> register addresses and values to be written to configure
>> the device for the board.
>
> Please don't. DT describes the hardware (or more accurately the system),
> it's not meant to store arbitrary configuration data. All the registers
> specified below should instead be set by the driver based on a
> combination of hardware description and information obtained at runtime.
OK, understood. I'll work on this. For some of them
explicit firmware properties would be appropriate.
I'll go through it to ascertain what can be determined
at runtime.
>> Signed-off-by: Michael Drake <michael.drake@codethink.co.uk>
>> Cc: Patrick Glaser <pglaser@tesla.com>
>> Cc: Nate Case <ncase@tesla.com>
>> ---
>> .../bindings/display/bridge/ti,ds90ub948.txt | 21 +++++++++++++++++++
>> 1 file changed, 21 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/display/bridge/ti,ds90ub948.txt b/Documentation/devicetree/bindings/display/bridge/ti,ds90ub948.txt
>> index f9e86cb22900..1e7033b0f3b7 100644
>> --- a/Documentation/devicetree/bindings/display/bridge/ti,ds90ub948.txt
>> +++ b/Documentation/devicetree/bindings/display/bridge/ti,ds90ub948.txt
>> @@ -12,6 +12,8 @@ Required properties:
>> Optional properties:
>>
>> - regulators: List of regulator name strings to enable for operation of device.
>> +- config: List of <register address>,<value> pairs to be set to configure
>> + device on powerup. The register addresses and values are 8bit.
>>
>> Example
>> -------
>> @@ -21,4 +23,23 @@ ti948: ds90ub948@0 {
>>
>> regulators: "vcc",
>> "vcc_disp";
>> + config:
>> + /* set error count to max */
>> + <0x41>, <0x1f>,
>> + /* sets output mode, no change noticed */
>> + <0x49>, <0xe0>,
>> + /* speed up I2C, 0xE is around 480KHz */
>> + <0x26>, <0x0e>,
>> + /* speed up I2C, 0xE is around 480KHz */
>> + <0x27>, <0x0e>,
>> + /* sets GPIO0 as an input */
>> + <0x1D>, <0x13>,
>> + /* set GPIO2 high, backlight PWM (set to 0x50 for normal use) */
>> + <0x1E>, <0x50>,
>> + /* sets GPIO3 as an output with remote control for touch XRES */
>> + <0x1F>, <0x05>,
>> + /* set GPIO5 high, backlight enable on new display */
>> + <0x20>, <0x09>,
>> + /* set GPIO7 and GPIO8 high to enable touch power and prox sense */
>> + <0x21>, <0x91>;
>> };
>
--
Michael Drake https://www.codethink.co.uk/
^ permalink raw reply
* Re: [PATCH v1 01/11] dt-bindings: display/bridge: Add bindings for ti948
From: Michael Drake @ 2019-07-12 12:42 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Andrzej Hajda, dri-devel, devicetree, linux-kernel, David Airlie,
Daniel Vetter, Rob Herring, Mark Rutland, linux-kernel,
Patrick Glaser, Nate Case
In-Reply-To: <20190611180316.GS5016@pendragon.ideasonboard.com>
Hi Laurent,
On 11/06/2019 19:03, Laurent Pinchart wrote:
> Hi Michael,
>
> Thank you for the patch.
My pleasure, and thank you for the feedback! I'm sorry it's
taken me a while to respond to it.
> On Tue, Jun 11, 2019 at 03:04:02PM +0100, Michael Drake wrote:
>> Adds device tree bindings for:
>>
>> TI DS90UB948-Q1 2K FPD-Link III to OpenLDI Deserializer
>>
>> The device has the compatible string "ti,ds90ub948", and
>> and allows an arrray of strings to be provided as regulator
>
> s/arrray/array/
Thanks. Fixed for the next version.
>> names to enable for operation of the device.
>>
>> Signed-off-by: Michael Drake <michael.drake@codethink.co.uk>
>> Cc: Patrick Glaser <pglaser@tesla.com>
>> Cc: Nate Case <ncase@tesla.com>
>> ---
>> .../bindings/display/bridge/ti,ds90ub948.txt | 24 +++++++++++++++++++
>> 1 file changed, 24 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/display/bridge/ti,ds90ub948.txt
>>
>> diff --git a/Documentation/devicetree/bindings/display/bridge/ti,ds90ub948.txt b/Documentation/devicetree/bindings/display/bridge/ti,ds90ub948.txt
>> new file mode 100644
>> index 000000000000..f9e86cb22900
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/display/bridge/ti,ds90ub948.txt
>> @@ -0,0 +1,24 @@
>> +TI DS90UB948-Q1 2K FPD-Link III to OpenLDI Deserializer
>> +=======================================================
>> +
>> +This is the binding for Texas Instruments DS90UB948-Q1 bridge deserializer.
>> +
>> +This device supports I2C only.
>
> Then the DT node should be a child of its I2C controller, and have a reg
> property.
OK, thank you. I'm not actually using DT, I'm using ACPI tables.
For DT, would this cause the device to get bound to the driver on
startup?
I tried adding a reg property with the value set to the device's
I2C address with device tree compatible properties, coupled with
the DT "compatible" string property, but that was insufficient to
bind the device to the driver. At the moment, I'm using the ACPI
mechanism for achieving this.
So all I've done for this is add the "reg" property to the device
tree ti,ds90ub948.txt documentation file, in the Example section.
Is this what you were expecting?
For the other point, that the DT node should be a child of its I2C
controller, I've made the devices descendants of the I2C controller
in ACPI, but one of them is not a direct child.
The ti949 is a child of its I2C controller node, but the ti948
is a child of the ti949 node in the ACPI table. I did this to
ensure that linux was aware of the dependency of the devices, so
that they would be brought up in the correct order and suspended
and resumed in the correct order.
>> +
>> +Required properties:
>> +
>> +- compatible: "ti,ds90ub948"
>> +
>> +Optional properties:
>> +
>> +- regulators: List of regulator name strings to enable for operation of device.
>
> There's a standard binding for regulators, using *-supply properties.
> Please use them.
Thank you! I've switched to using that, and it is less code. :)
> You should also use the OF graph DT bindings to model the data
> connections.
I'm not clear on the implications of this from the driver code.
Does it need any code changes to the driver to support these
endpoints? How are they used?
If it's just a documentation thing, I can easily do it, but
otherwise, I can't test Device Tree because we're using ACPI.
>> +
>> +Example
>> +-------
>> +
>> +ti948: ds90ub948@0 {
>> + compatible = "ti,ds90ub948";
>> +
>> + regulators: "vcc",
>> + "vcc_disp";
>> +};
>
--
Michael Drake https://www.codethink.co.uk/
^ permalink raw reply
* Re: [PATCH 1/3] arm64: dts: renesas: hihope-common: Add PCA9654 I/O expander
From: Geert Uytterhoeven @ 2019-07-12 12:29 UTC (permalink / raw)
To: Biju Das
Cc: Rob Herring, Mark Rutland, Simon Horman, Geert Uytterhoeven,
Magnus Damm, Linux-Renesas,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Chris Paterson, Fabrizio Castro
In-Reply-To: <1562321720-18735-2-git-send-email-biju.das@bp.renesas.com>
Hi Biju,
On Fri, Jul 5, 2019 at 12:20 PM Biju Das <biju.das@bp.renesas.com> wrote:
> Enable PCA9654 GPIO expander, so that we can configure its GPIOs later.
>
> Signed-off-by: Biju Das <biju.das@bp.renesas.com>
> --- a/arch/arm64/boot/dts/renesas/hihope-common.dtsi
> +++ b/arch/arm64/boot/dts/renesas/hihope-common.dtsi
> @@ -162,6 +162,13 @@
> clock-frequency = <400000>;
> status = "okay";
>
> + gpio_expander: gpio@20 {
> + compatible = "onnn,pca9654";
> + reg = <0x20>;
> + gpio-controller;
> + #gpio-cells = <2>;
You may want to describe that the interrupt output is tied to GP5_25?
> + };
> +
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [PATCH] dtc: Use pkg-config to locate libyaml
From: Pavel Modilaynen @ 2019-07-12 11:52 UTC (permalink / raw)
To: robh+dt, frowand.list; +Cc: devicetree, Pavel Modilaynen
From: Pavel Modilaynen <pavel.modilaynen@axis.com>
Using Makefile's wildcard with absolute path to detect
the presence of libyaml results in false-positive
detection when cross-compiling e.g. in yocto environment.
The latter results in build error:
| scripts/dtc/yamltree.o: In function `yaml_propval_int':
| yamltree.c: undefined reference to `yaml_sequence_start_event_initialize'
| yamltree.c: undefined reference to `yaml_emitter_emit'
| yamltree.c: undefined reference to `yaml_scalar_event_initialize'
...
Use pkg-config to locate libyaml to address this scenario.
Signed-off-by: Pavel Modilaynen <pavel.modilaynen@axis.com>
---
scripts/dtc/Makefile | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/scripts/dtc/Makefile b/scripts/dtc/Makefile
index 82160808765c..99d51b665432 100644
--- a/scripts/dtc/Makefile
+++ b/scripts/dtc/Makefile
@@ -11,7 +11,7 @@ dtc-objs += dtc-lexer.lex.o dtc-parser.tab.o
# Source files need to get at the userspace version of libfdt_env.h to compile
HOST_EXTRACFLAGS := -I $(srctree)/$(src)/libfdt
-ifeq ($(wildcard /usr/include/yaml.h),)
+ifeq ($(shell pkg-config --exists yaml-0.1 && echo yes),)
ifneq ($(CHECK_DTBS),)
$(error dtc needs libyaml for DT schema validation support. \
Install the necessary libyaml development package.)
@@ -19,7 +19,7 @@ endif
HOST_EXTRACFLAGS += -DNO_YAML
else
dtc-objs += yamltree.o
-HOSTLDLIBS_dtc := -lyaml
+HOSTLDLIBS_dtc := $(shell pkg-config yaml-0.1 --libs)
endif
# Generated files need one more search path to include headers in source tree
--
2.11.0
^ permalink raw reply related
* Re: [PATCH 3/3] drm/bridge: sii902x: make audio mclk optional
From: Andrzej Hajda @ 2019-07-12 10:45 UTC (permalink / raw)
To: Olivier Moysan, narmstrong, Laurent.pinchart, jonas,
jernej.skrabec, airlied, daniel, dri-devel, linux-kernel,
benjamin.gaignard, alexandre.torgue, linux-arm-kernel,
linux-stm32, jsarha, robh+dt, mark.rutland, devicetree
In-Reply-To: <1562082426-14876-4-git-send-email-olivier.moysan@st.com>
On 02.07.2019 17:47, Olivier Moysan wrote:
> The master clock on i2s bus is not mandatory,
> as sii902X internal PLL can be used instead.
> Make use of mclk optional.
>
> Fixes: ff5781634c41 ("drm/bridge: sii902x: Implement HDMI audio support")
>
> Signed-off-by: Olivier Moysan <olivier.moysan@st.com>
> ---
> drivers/gpu/drm/bridge/sii902x.c | 39 +++++++++++++++++++++++----------------
> 1 file changed, 23 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
> index 36acc256e67e..a08bd9fdc046 100644
> --- a/drivers/gpu/drm/bridge/sii902x.c
> +++ b/drivers/gpu/drm/bridge/sii902x.c
> @@ -562,19 +562,21 @@ static int sii902x_audio_hw_params(struct device *dev, void *data,
> }
> }
>
> - ret = clk_prepare_enable(sii902x->audio.mclk);
> - if (ret) {
> - dev_err(dev, "Enabling mclk failed: %d\n", ret);
> - return ret;
> - }
> + if (sii902x->audio.mclk) {
> + ret = clk_prepare_enable(sii902x->audio.mclk);
> + if (ret) {
> + dev_err(dev, "Enabling mclk failed: %d\n", ret);
> + return ret;
> + }
>
> - mclk_rate = clk_get_rate(sii902x->audio.mclk);
> + mclk_rate = clk_get_rate(sii902x->audio.mclk);
>
> - ret = sii902x_select_mclk_div(&i2s_config_reg, params->sample_rate,
> - mclk_rate);
> - if (mclk_rate != ret * params->sample_rate)
> - dev_dbg(dev, "Inaccurate reference clock (%ld/%d != %u)\n",
> - mclk_rate, ret, params->sample_rate);
> + ret = sii902x_select_mclk_div(&i2s_config_reg,
> + params->sample_rate, mclk_rate);
> + if (mclk_rate != ret * params->sample_rate)
> + dev_dbg(dev, "Inaccurate reference clock (%ld/%d != %u)\n",
> + mclk_rate, ret, params->sample_rate);
> + }
>
> mutex_lock(&sii902x->mutex);
>
> @@ -640,7 +642,8 @@ static int sii902x_audio_hw_params(struct device *dev, void *data,
> mutex_unlock(&sii902x->mutex);
>
> if (ret) {
> - clk_disable_unprepare(sii902x->audio.mclk);
> + if (sii902x->audio.mclk)
> + clk_disable_unprepare(sii902x->audio.mclk);
"if" clause is not necessary
> dev_err(dev, "%s: hdmi audio enable failed: %d\n", __func__,
> ret);
> }
> @@ -659,7 +662,8 @@ static void sii902x_audio_shutdown(struct device *dev, void *data)
>
> mutex_unlock(&sii902x->mutex);
>
> - clk_disable_unprepare(sii902x->audio.mclk);
> + if (sii902x->audio.mclk)
ditto
> + clk_disable_unprepare(sii902x->audio.mclk);
> }
>
> int sii902x_audio_digital_mute(struct device *dev, void *data, bool enable)
> @@ -752,9 +756,12 @@ static int sii902x_audio_codec_init(struct sii902x *sii902x,
>
> sii902x->audio.mclk = devm_clk_get(dev, "mclk");
> if (IS_ERR(sii902x->audio.mclk)) {
> - dev_err(dev, "%s: No clock (audio mclk) found: %ld\n",
> - __func__, PTR_ERR(sii902x->audio.mclk));
> - return 0;
> + if (PTR_ERR(sii902x->audio.mclk) != -ENOENT) {
> + dev_err(dev, "%s: No clock (audio mclk) found: %ld\n",
> + __func__, PTR_ERR(sii902x->audio.mclk));
> + return PTR_ERR(sii902x->audio.mclk);
> + }
> + sii902x->audio.mclk = NULL;
devm_clk_get_optional should be used here.
Summarizing, clk framework supports NULL clocks so you can adjust code
to benefit from it: no need to checks "if (sii902x->audio.mclk)". The
only place you should care is near clk_get_rate, for null clock it will
return 0, so you should react appropriately.
With that changed:
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
--
Regards
Andrzej
> }
>
> sii902x->audio.pdev = platform_device_register_data(
^ permalink raw reply
* Re: [PATCH 2/3] dt-bindings: display: sii902x: Change audio mclk binding
From: Andrzej Hajda @ 2019-07-12 10:31 UTC (permalink / raw)
To: Olivier Moysan, narmstrong, Laurent.pinchart, jonas,
jernej.skrabec, airlied, daniel, dri-devel, linux-kernel,
benjamin.gaignard, alexandre.torgue, linux-arm-kernel,
linux-stm32, jsarha, robh+dt, mark.rutland, devicetree
In-Reply-To: <1562082426-14876-3-git-send-email-olivier.moysan@st.com>
On 02.07.2019 17:47, Olivier Moysan wrote:
> As stated in SiL9022/24 datasheet, master clock is not required for I2S.
> Make mclk property optional in DT bindings.
>
> Fixes: 3f18021f43a3 ("dt-bindings: display: sii902x: Add HDMI audio bindings")
>
> Signed-off-by: Olivier Moysan <olivier.moysan@st.com>
> ---
> Documentation/devicetree/bindings/display/bridge/sii902x.txt | 5 ++---
> 1 file changed, 2 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/display/bridge/sii902x.txt b/Documentation/devicetree/bindings/display/bridge/sii902x.txt
> index 2df44b7d3821..6e14e087c0d0 100644
> --- a/Documentation/devicetree/bindings/display/bridge/sii902x.txt
> +++ b/Documentation/devicetree/bindings/display/bridge/sii902x.txt
> @@ -26,9 +26,8 @@ Optional properties:
> - clocks: phandle and clock specifier for each clock listed in
> the clock-names property
> - clock-names: "mclk"
> - Describes SII902x MCLK input. MCLK is used to produce
> - HDMI audio CTS values. This property is required if
> - "#sound-dai-cells"-property is present. This property follows
> + Describes SII902x MCLK input. MCLK can be used to produce
> + HDMI audio CTS values. This property follows
> Documentation/devicetree/bindings/clock/clock-bindings.txt
> consumer binding.
>
Acked-by: Andrzej Hajda <a.hajda@samsung.com>
--
Regards
Andrzej
^ permalink raw reply
* Re: [PATCH 1/3] drm/bridge: sii902x: fix missing reference to mclk clock
From: Andrzej Hajda @ 2019-07-12 10:31 UTC (permalink / raw)
To: Olivier Moysan, narmstrong, Laurent.pinchart, jonas,
jernej.skrabec, airlied, daniel, dri-devel, linux-kernel,
benjamin.gaignard, alexandre.torgue, linux-arm-kernel,
linux-stm32, jsarha, robh+dt, mark.rutland, devicetree
In-Reply-To: <1562082426-14876-2-git-send-email-olivier.moysan@st.com>
On 02.07.2019 17:47, Olivier Moysan wrote:
> Add devm_clk_get call to retrieve reference to master clock.
>
> Fixes: ff5781634c41 ("drm/bridge: sii902x: Implement HDMI audio support")
>
> Signed-off-by: Olivier Moysan <olivier.moysan@st.com>
> ---
> drivers/gpu/drm/bridge/sii902x.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
> index dd7aa466b280..36acc256e67e 100644
> --- a/drivers/gpu/drm/bridge/sii902x.c
> +++ b/drivers/gpu/drm/bridge/sii902x.c
> @@ -750,6 +750,7 @@ static int sii902x_audio_codec_init(struct sii902x *sii902x,
> sii902x->audio.i2s_fifo_sequence[i] |= audio_fifo_id[i] |
> i2s_lane_id[lanes[i]] | SII902X_TPI_I2S_FIFO_ENABLE;
>
> + sii902x->audio.mclk = devm_clk_get(dev, "mclk");
> if (IS_ERR(sii902x->audio.mclk)) {
> dev_err(dev, "%s: No clock (audio mclk) found: %ld\n",
> __func__, PTR_ERR(sii902x->audio.mclk));
Acked-by: Andrzej Hajda <a.hajda@samsung.com>
--
Regards
Andrzej
^ permalink raw reply
* RE: [PATCH v2 3/5] perf: stm32: ddrperfm driver creation
From: Gerald BAEZA @ 2019-07-12 10:00 UTC (permalink / raw)
To: Mark Rutland
Cc: devicetree@vger.kernel.org, Alexandre TORGUE, arnd@arndb.de,
corbet@lwn.net, linux-doc@vger.kernel.org, Will Deacon,
linux@armlinux.org.uk, linux-kernel@vger.kernel.org,
robh+dt@kernel.org, mcoquelin.stm32@gmail.com, olof@lixom.net,
horms+renesas@verge.net.au,
linux-stm32@st-md-mailman.stormreply.com,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <20190626122228.GB20313@lakrids.cambridge.arm.com>
Hi Mark
Thanks a lot for your review !
I started to rework the driver, based on your feedback but I have some questions for you: they are in line below... (where I also answer to your questions)
Best regards
Gérald
> -----Original Message-----
> From: Mark Rutland <Mark.Rutland@arm.com>
> Sent: mercredi 26 juin 2019 14:23
> To: Gerald BAEZA <gerald.baeza@st.com>
> Cc: Will Deacon <Will.Deacon@arm.com>; robh+dt@kernel.org;
> mcoquelin.stm32@gmail.com; Alexandre TORGUE
> <alexandre.torgue@st.com>; corbet@lwn.net; linux@armlinux.org.uk;
> olof@lixom.net; horms+renesas@verge.net.au; arnd@arndb.de; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; linux-stm32@st-
> md-mailman.stormreply.com; linux-kernel@vger.kernel.org; linux-
> doc@vger.kernel.org
> Subject: Re: [PATCH v2 3/5] perf: stm32: ddrperfm driver creation
>
> On Mon, May 20, 2019 at 03:27:17PM +0000, Gerald BAEZA wrote:
> > The DDRPERFM is the DDR Performance Monitor embedded in STM32MP1
> SOC.
> >
> > This perf drivers supports the read, write, activate, idle and total
> > time counters, described in the reference manual RM0436.
>
> Is this document publicly accessible anywhere?
Yes
> If so, could you please provide a link?
https://www.st.com/resource/en/reference_manual/DM00327659.pdf
> > A 'bandwidth' attribute is added in the 'ddrperfm' event_source in
> > order to directly get the read and write bandwidths (in MB/s), from
> > the last read, write and total time counters reading.
> > This attribute is aside the 'events' attributes group because it is
> > not a counter, as seen by perf tool.
>
> This should be removed. This is unusually stateful, and this can be calculated
> in userspace by using the events. I'm also not keen on creating a precedent
> for weird sysfs bits for PMUs.
Ok, I will remove it in the v3.
This will also have impact on the bindings and dts since the DDR frequency
will disappear from the clocks.
Just to be sure : should I do the bandwidth computing on userspace (via
a script, for instance) or are you suggesting to do this in perf tool ?
> > Signed-off-by: Gerald Baeza <gerald.baeza@st.com>
> > ---
> > drivers/perf/Kconfig | 6 +
> > drivers/perf/Makefile | 1 +
> > drivers/perf/stm32_ddr_pmu.c | 512
> > +++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 519 insertions(+)
> > create mode 100644 drivers/perf/stm32_ddr_pmu.c
> >
> > diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig index
> > a94e586..9add8a7 100644
> > --- a/drivers/perf/Kconfig
> > +++ b/drivers/perf/Kconfig
> > @@ -105,6 +105,12 @@ config THUNDERX2_PMU
> > The SoC has PMU support in its L3 cache controller (L3C) and
> > in the DDR4 Memory Controller (DMC).
> >
> > +config STM32_DDR_PMU
> > + tristate "STM32 DDR PMU"
> > + depends on MACH_STM32MP157
> > + help
> > + Support for STM32 DDR performance monitor (DDRPERFM).
> > +
> > config XGENE_PMU
> > depends on ARCH_XGENE
> > bool "APM X-Gene SoC PMU"
> > diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile index
> > 3048994..fa64719 100644
> > --- a/drivers/perf/Makefile
> > +++ b/drivers/perf/Makefile
> > @@ -8,6 +8,7 @@ obj-$(CONFIG_ARM_SMMU_V3_PMU) += arm_smmuv3_pmu.o
> > obj-$(CONFIG_HISI_PMU) += hisilicon/
> > obj-$(CONFIG_QCOM_L2_PMU)+= qcom_l2_pmu.o
> > obj-$(CONFIG_QCOM_L3_PMU) += qcom_l3_pmu.o
> > +obj-$(CONFIG_STM32_DDR_PMU) += stm32_ddr_pmu.o
> > obj-$(CONFIG_THUNDERX2_PMU) += thunderx2_pmu.o
> > obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
> > obj-$(CONFIG_ARM_SPE_PMU) += arm_spe_pmu.o diff --git
> > a/drivers/perf/stm32_ddr_pmu.c b/drivers/perf/stm32_ddr_pmu.c new file
> > mode 100644 index 0000000..ae4a813
> > --- /dev/null
> > +++ b/drivers/perf/stm32_ddr_pmu.c
> > @@ -0,0 +1,512 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * This file is the STM32 DDR performance monitor (DDRPERFM) driver
> > + *
> > + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
> > + * Author: Gerald Baeza <gerald.baeza@st.com> */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/hrtimer.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/perf_event.h>
> > +#include <linux/reset.h>
> > +#include <linux/slab.h>
> > +#include <linux/types.h>
> > +
> > +#define POLL_MS4000/* The counter roll over after ~8s @533MHz */
>
> I take it this is because there's no IRQ? If so, please expand the comment to
> call that out, e.g.
>
> /*
> * The PMU has no overflow IRQ, so we must poll to avoid losing events.
> * The fastest counter can overflow in ~8s @533MHz, so we poll in 4s
> * intervals to ensure we don't miss a rollover.
> */
> #define POLL_MS4000
The IP is able to freeze all counters and generate an interrupt when there is
a counter overflow. But, relying on this means that I lose all the events that
occur between the freeze and the interrupt handler execution, corresponding
to the interrupt latency. I can avoid such events losing in polling, so that is
why I preferred to use a polling mechanism.
> Which clock specifically is operating at 533MHz, and is this something that
> may vary? Is it possible that this may go higher in future?
533 MHz is the DDR frequency and this is the maximum for STM32MP15.
> > +#define WORD_LENGTH4/* Bytes */
> > +#define BURST_LENGTH8/* Words */
> > +
> > +#define DDRPERFM_CTL0x000
> > +#define DDRPERFM_CFG0x004
> > +#define DDRPERFM_STATUS 0x008
> > +#define DDRPERFM_CCR0x00C
> > +#define DDRPERFM_IER0x010
> > +#define DDRPERFM_ISR0x014
> > +#define DDRPERFM_ICR0x018
> > +#define DDRPERFM_TCNT0x020
> > +#define DDRPERFM_CNT(X)(0x030 + 8 * (X))
> > +#define DDRPERFM_HWCFG0x3F0
> > +#define DDRPERFM_VER0x3F4
> > +#define DDRPERFM_ID0x3F8
> > +#define DDRPERFM_SID0x3FC
> > +
> > +#define CTL_START0x00000001
> > +#define CTL_STOP0x00000002
> > +#define CCR_CLEAR_ALL0x8000000F
> > +#define SID_MAGIC_ID0xA3C5DD01
> > +
> > +#define STRING "Read = %llu, Write = %llu, Read & Write = %llu (MB/s)\n"
>
> If you need this, please expand it in-place. As-is, this is needless obfuscation.
This will be removed in v3, so no problem.
> > +
> > +enum {
> > +READ_CNT,
> > +WRITE_CNT,
> > +ACTIVATE_CNT,
> > +IDLE_CNT,
> > +TIME_CNT,
> > +PMU_NR_COUNTERS
> > +};
>
> I take it these are fixed-purpose counters in the hardware?
Yes
> > +
> > +struct stm32_ddr_pmu {
> > +struct pmu pmu;
> > +void __iomem *membase;
> > +struct clk *clk;
> > +struct clk *clk_ddr;
> > +unsigned long clk_ddr_rate;
> > +struct hrtimer hrtimer;
> > +ktime_t poll_period;
> > +spinlock_t lock; /* for shared registers access */
>
> All accesses to a PMU instance should be serialized on the same CPU, so you
> shouldn't need a lock (though you will need to disable IRQs to serialize with
> the interrupt handler).
>
> The perf subsystem cannot sanely access the PMU across multiple CPUs.
Ok, so I will only control the IP from one core and remove the lock in v3.
> > +struct perf_event *events[PMU_NR_COUNTERS];
> > +u64 events_cnt[PMU_NR_COUNTERS];
> > +};
> > +
> > +static inline struct stm32_ddr_pmu *pmu_to_stm32_ddr_pmu(struct
> pmu
> > +*p) { return container_of(p, struct stm32_ddr_pmu, pmu); }
> > +
> > +static inline struct stm32_ddr_pmu *hrtimer_to_stm32_ddr_pmu(struct
> > +hrtimer *h) { return container_of(h, struct stm32_ddr_pmu, hrtimer);
> > +}
> > +
> > +static u64 stm32_ddr_pmu_compute_bw(struct stm32_ddr_pmu
> *stm32_ddr_pmu,
> > + int counter)
> > +{
> > +u64 bw = stm32_ddr_pmu->events_cnt[counter], tmp;
> > +u64 div = stm32_ddr_pmu->events_cnt[TIME_CNT];
> > +u32 prediv = 1, premul = 1;
> > +
> > +if (bw && div) {
> > +/* Maximize the dividend into 64 bits */ while ((bw <
> > +0x8000000000000000ULL) &&
> > + (premul < 0x40000000UL)) {
> > +bw = bw << 1;
> > +premul *= 2;
> > +}
> > +/* Minimize the dividor to fit in 32 bits */ while ((div >
> > +0xffffffffUL) && (prediv < 0x40000000UL)) { div = div >> 1; prediv *=
> > +2; }
> > +/* Divide counter per time and multiply per DDR settings */
> > +do_div(bw, div); tmp = bw * BURST_LENGTH * WORD_LENGTH; tmp *=
> > +stm32_ddr_pmu->clk_ddr_rate; if (tmp < bw) goto error; bw = tmp;
> > +/* Cancel the prediv and premul factors */ while (prediv > 1) { bw =
> > +bw >> 1; prediv /= 2; } while (premul > 1) { bw = bw >> 1; premul /=
> > +2; }
> > +/* Convert MHz to Hz and B to MB, to finally get MB/s */ tmp = bw *
> > +1000000; if (tmp < bw) goto error; bw = tmp; premul = 1024 * 1024;
> > +while (premul > 1) { bw = bw >> 1; premul /= 2; } } return bw;
> > +
> > +error:
> > +pr_warn("stm32-ddr-pmu: overflow detected\n"); return 0; }
>
> IIUC this is for the stateful sysfs stuff, which should be removed.
>
Yes, you are right : to be removed in v3.
> > +
> > +static void stm32_ddr_pmu_event_configure(struct perf_event *event) {
> > +struct stm32_ddr_pmu *stm32_ddr_pmu =
> > +pmu_to_stm32_ddr_pmu(event->pmu); unsigned long lock_flags,
> > +config_base = event->hw.config_base;
> > +u32 val;
> > +
> > +spin_lock_irqsave(&stm32_ddr_pmu->lock, lock_flags);
> > +writel_relaxed(CTL_STOP, stm32_ddr_pmu->membase + DDRPERFM_CTL);
> > +
> > +if (config_base < TIME_CNT) {
> > +val = readl_relaxed(stm32_ddr_pmu->membase + DDRPERFM_CFG); val |= (1
> > +<< config_base); writel_relaxed(val, stm32_ddr_pmu->membase +
> > +DDRPERFM_CFG); } spin_unlock_irqrestore(&stm32_ddr_pmu->lock,
> > +lock_flags); }
>
> You don't need the lock here. This is called from your pmu::{start,add}
> callbacks, and the perf core already ensures those are serialized (via the
> context lock), and called with IRQs disabled.
>
Ok
> > +
> > +static void stm32_ddr_pmu_event_read(struct perf_event *event) {
> > +struct stm32_ddr_pmu *stm32_ddr_pmu =
> > +pmu_to_stm32_ddr_pmu(event->pmu); unsigned long flags, config_base =
> > +event->hw.config_base; struct hw_perf_event *hw = &event->hw;
> > +u64 prev_count, new_count, mask;
> > +u32 val, offset, bit;
> > +
> > +spin_lock_irqsave(&stm32_ddr_pmu->lock, flags);
> > +
> > +writel_relaxed(CTL_STOP, stm32_ddr_pmu->membase + DDRPERFM_CTL);
> > +
> > +if (config_base == TIME_CNT) {
> > +offset = DDRPERFM_TCNT;
> > +bit = 1 << 31;
> > +} else {
> > +offset = DDRPERFM_CNT(config_base);
> > +bit = 1 << config_base;
> > +}
> > +val = readl_relaxed(stm32_ddr_pmu->membase + DDRPERFM_STATUS);
> > +if (val & bit) pr_warn("stm32_ddr_pmu hardware overflow\n"); val =
> > +readl_relaxed(stm32_ddr_pmu->membase + offset); writel_relaxed(bit,
> > +stm32_ddr_pmu->membase + DDRPERFM_CCR);
> > + writel_relaxed(CTL_START, stm32_ddr_pmu->membase + DDRPERFM_CTL);
> > +
> > +do {
> > +prev_count = local64_read(&hw->prev_count); new_count = prev_count
> > +val; } while (local64_xchg(&hw->prev_count, new_count) !=
> > +prev_count);
> > +
> > +mask = GENMASK_ULL(31, 0);
> > +local64_add(val & mask, &event->count);
> > +
> > +if (new_count < prev_count)
> > +pr_warn("STM32 DDR PMU counter saturated\n");
>
> Do the counter saturate, or do they roll-over?
>
> I think that the message is misleading here, but I just want to check.
This check is on the software counter, that may roll-over after a very long capture time.
I took the "counter saturated" warning from "cache-l2x0-pmu.c" but I can change to
something more explicit.
The hardware counters, in DDRPERFM IP, are frozen in case of overflow (this is when
the interrupt mentioned above can be generated).
If this occurs, a "stm32_ddr_pmu hardware overflow" warning is generated.
Is this clearer ? Should I change anything ?
> > +
> > +spin_unlock_irqrestore(&stm32_ddr_pmu->lock, flags); }
> > +
> > +static void stm32_ddr_pmu_event_start(struct perf_event *event, int
> > +flags) { struct stm32_ddr_pmu *stm32_ddr_pmu =
> > +pmu_to_stm32_ddr_pmu(event->pmu);
> > +struct hw_perf_event *hw = &event->hw; unsigned long lock_flags;
> > +
> > +if (WARN_ON_ONCE(!(hw->state & PERF_HES_STOPPED))) return;
> > +
> > +if (flags & PERF_EF_RELOAD)
> > +WARN_ON_ONCE(!(hw->state & PERF_HES_UPTODATE));
> > +
> > +stm32_ddr_pmu_event_configure(event);
> > +
> > +/* Clear all counters to synchronize them, then start */
> > +spin_lock_irqsave(&stm32_ddr_pmu->lock, lock_flags);
> > +writel_relaxed(CCR_CLEAR_ALL, stm32_ddr_pmu->membase + DDRPERFM_CCR);
> > +writel_relaxed(CTL_START, stm32_ddr_pmu->membase + DDRPERFM_CTL);
> > +spin_unlock_irqrestore(&stm32_ddr_pmu->lock, lock_flags);
>
> By 'clear' do you mean set the counters to zero?
>
> Or is there a control bit that determine whether they count?
There is one "enable" bit per counter (except the total counter that is enabled by default).
"Clear" means that the counters are set to zero and the counting will only be started with
the "Start" for all enabled counters.
> If we're updating the counter, we could update prev_count at the same
> instant.
This synchronization is just for the start because the events are enabled & started one
per one by perf tool. Since each counter has to be compared to the "time" count that is
the overall reference for all, I want to ensure that "time" and all the enabled counters
finally start at the same time. Thus, this synchro.
Do you mean that, to be consistent, I should set prev_count to zero here also ?
>
> > +
> > +hw->state = 0;
> > +}
> > +
> > +static void stm32_ddr_pmu_event_stop(struct perf_event *event, int
> > +flags) { struct stm32_ddr_pmu *stm32_ddr_pmu =
> > +pmu_to_stm32_ddr_pmu(event->pmu);
> > +unsigned long lock_flags, config_base = event->hw.config_base; struct
> > +hw_perf_event *hw = &event->hw;
> > +u32 val, bit;
> > +
> > +if (WARN_ON_ONCE(hw->state & PERF_HES_STOPPED)) return;
> > +
> > +spin_lock_irqsave(&stm32_ddr_pmu->lock, lock_flags);
> > +writel_relaxed(CTL_STOP, stm32_ddr_pmu->membase +
> DDRPERFM_CTL); if
> > +(config_base == TIME_CNT) bit = 1 << 31; else bit = 1 << config_base;
> > +writel_relaxed(bit, stm32_ddr_pmu->membase + DDRPERFM_CCR); if
> > +(config_base < TIME_CNT) { val = readl_relaxed(stm32_ddr_pmu-
> >membase
> > ++ DDRPERFM_CFG); val &= ~bit; writel_relaxed(val,
> > +stm32_ddr_pmu->membase + DDRPERFM_CFG); }
> > +spin_unlock_irqrestore(&stm32_ddr_pmu->lock, lock_flags);
> > +
> > +hw->state |= PERF_HES_STOPPED;
> > +
> > +if (flags & PERF_EF_UPDATE) {
> > +stm32_ddr_pmu_event_read(event);
> > +hw->state |= PERF_HES_UPTODATE;
> > +}
> > +}
> > +
> > +static int stm32_ddr_pmu_event_add(struct perf_event *event, int
> > +flags) { struct stm32_ddr_pmu *stm32_ddr_pmu =
> > +pmu_to_stm32_ddr_pmu(event->pmu);
> > +unsigned long config_base = event->hw.config_base; struct
> > +hw_perf_event *hw = &event->hw;
> > +
> > +stm32_ddr_pmu->events_cnt[config_base] = 0;
> > +stm32_ddr_pmu->events[config_base] = event;
> > +
> > +clk_enable(stm32_ddr_pmu->clk);
> > +hrtimer_start(&stm32_ddr_pmu->hrtimer, stm32_ddr_pmu-poll_period,
> > + HRTIMER_MODE_REL);
> > +
> > +stm32_ddr_pmu_event_configure(event);
> > +
> > +hw->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
> > +
> > +if (flags & PERF_EF_START)
> > +stm32_ddr_pmu_event_start(event, 0);
> > +
> > +return 0;
> > +}
> > +
> > +static void stm32_ddr_pmu_event_del(struct perf_event *event, int
> > +flags) { struct stm32_ddr_pmu *stm32_ddr_pmu =
> > +pmu_to_stm32_ddr_pmu(event->pmu);
> > +unsigned long config_base = event->hw.config_base; bool stop = true;
> > +int i;
> > +
> > +stm32_ddr_pmu_event_stop(event, PERF_EF_UPDATE);
> > +
> > +stm32_ddr_pmu->events_cnt[config_base] +=
> > +local64_read(&event->count); stm32_ddr_pmu->events[config_base] =
> > +NULL;
> > +
> > +for (i = 0; i < PMU_NR_COUNTERS; i++) if (stm32_ddr_pmu->events[i])
> > +stop = false; if (stop) hrtimer_cancel(&stm32_ddr_pmu->hrtimer);
> > +
> > +clk_disable(stm32_ddr_pmu->clk);
>
> This doesn't look right. If I add two events A and B, then delete event A,
> surely we want the clock on for event B?
>
clk_enable() is called for each event enable and clk_disable() is called for
each event disable. So with your example we have:
+A -> clk_enable()
+B -> clk_enable()
-B -> clk_disable()
And the clock remains enabled until A is also deleted.
> Does the clock only affect whether the counters count, or does it also affect
> whether the register file is usable?
Both
>
> > +}
> > +
> > +static int stm32_ddr_pmu_event_init(struct perf_event *event) {
> > +struct hw_perf_event *hw = &event->hw;
> > +
> > +if (is_sampling_event(event))
> > +return -EINVAL;
> > +
> > +if (event->attach_state & PERF_ATTACH_TASK) return -EINVAL;
> > +
> > +if (event->attr.exclude_user ||
> > + event->attr.exclude_kernel ||
> > + event->attr.exclude_hv ||
> > + event->attr.exclude_idle ||
> > + event->attr.exclude_host ||
> > + event->attr.exclude_guest)
> > +return -EINVAL;
> > +
> > +if (event->cpu < 0)
> > +return -EINVAL;
>
> For a system PMU like this, you must ensure that all events are assigned to
> the same CPU.
>
> You'll need to designate some arbitrary CPU to mange the PMU, expose that
> under sysfs, and upon hotplug events you must choose a new CPU and
> migrate existing events.
>
> For a simple example, see arch/arm/mm/cache-l2x0-pmu.c.
Among CPU#0 and CPU#1, the CPU#0 is the only one that can be running alone
(in other words, we disable CPU#1 before low power transition and CPU#0 will
finally enter low power).
So I think I can avoid to manage any migration by systematically launching the PMU
on CPU#0.
Is this fine for you or do you anyway prefer a generic approach that I could find
in arch/arm/mm/cache-l2x0-pmu.c ?
> > +
> > +hw->config_base = event->attr.config;
> > +
> > +return 0;
> > +}
> > +
> > +static enum hrtimer_restart stm32_ddr_pmu_poll(struct hrtimer
> > +*hrtimer) { struct stm32_ddr_pmu *stm32_ddr_pmu =
> > +hrtimer_to_stm32_ddr_pmu(hrtimer);
> > +int i;
> > +
> > +for (i = 0; i < PMU_NR_COUNTERS; i++) if (stm32_ddr_pmu->events[i])
> > +stm32_ddr_pmu_event_read(stm32_ddr_pmu->events[i]);
> > +
> > +hrtimer_forward_now(hrtimer, stm32_ddr_pmu->poll_period);
> > +
> > +return HRTIMER_RESTART;
> > +}
> > +
> > +static ssize_t stm32_ddr_pmu_sysfs_show(struct device *dev, struct
> > +device_attribute *attr, char *buf) { struct dev_ext_attribute *eattr;
> > +
> > +eattr = container_of(attr, struct dev_ext_attribute, attr);
> > +
> > +return sprintf(buf, "config=0x%lx\n", (unsigned long)eattr->var); }
> > +
> > +static ssize_t bandwidth_show(struct device *dev,
> > + struct device_attribute *attr,
> > + char *buf)
> > +{
> > +struct stm32_ddr_pmu *stm32_ddr_pmu = dev_get_drvdata(dev);
> > +u64 r_bw, w_bw;
> > +int ret;
> > +
> > +if (stm32_ddr_pmu->events_cnt[TIME_CNT]) { r_bw =
> > +stm32_ddr_pmu_compute_bw(stm32_ddr_pmu, READ_CNT); w_bw =
> > +stm32_ddr_pmu_compute_bw(stm32_ddr_pmu, WRITE_CNT);
> > +
> > +ret = snprintf(buf, PAGE_SIZE, STRING,
> > + r_bw, w_bw, (r_bw + w_bw));
> > +} else {
> > +ret = snprintf(buf, PAGE_SIZE, "No data available\n"); }
> > +
> > +return ret;
> > +}
>
> As commented above, this should not be exposed under sysfs. It doesn't
> follow the usual ABI rules, it's weirdly stateful, and it's redundant.
>
Ok
> > +
> > +#define STM32_DDR_PMU_ATTR(_name, _func, _config)\ (&((struct
> > +dev_ext_attribute[]) {\
> > +{ __ATTR(_name, 0444, _func, NULL), (void *)_config } \
> > +})[0].attr.attr)
> > +
> > +#define STM32_DDR_PMU_EVENT_ATTR(_name, _config)\
> > +STM32_DDR_PMU_ATTR(_name, stm32_ddr_pmu_sysfs_show,\
> > + (unsigned long)_config)
> > +
> > +static struct attribute *stm32_ddr_pmu_event_attrs[] = {
> > +STM32_DDR_PMU_EVENT_ATTR(read_cnt, READ_CNT),
> > +STM32_DDR_PMU_EVENT_ATTR(write_cnt, WRITE_CNT),
> > +STM32_DDR_PMU_EVENT_ATTR(activate_cnt, ACTIVATE_CNT),
> > +STM32_DDR_PMU_EVENT_ATTR(idle_cnt, IDLE_CNT),
> > +STM32_DDR_PMU_EVENT_ATTR(time_cnt, TIME_CNT), NULL };
> > +
> > +static DEVICE_ATTR_RO(bandwidth);
> > +static struct attribute *stm32_ddr_pmu_bandwidth_attrs[] = {
> > +&dev_attr_bandwidth.attr, NULL, };
> > +
> > +static struct attribute_group stm32_ddr_pmu_event_attrs_group = {
> > +.name = "events", .attrs = stm32_ddr_pmu_event_attrs, };
> > +
> > +static struct attribute_group stm32_ddr_pmu_bandwidth_attrs_group = {
> > +.attrs = stm32_ddr_pmu_bandwidth_attrs, };
> > +
> > +static const struct attribute_group *stm32_ddr_pmu_attr_groups[] = {
> > +&stm32_ddr_pmu_event_attrs_group,
> > +&stm32_ddr_pmu_bandwidth_attrs_group,
> > +NULL,
> > +};
> > +
> > +static int stm32_ddr_pmu_device_probe(struct platform_device *pdev) {
> > +struct stm32_ddr_pmu *stm32_ddr_pmu; struct reset_control *rst;
> > +struct resource *res; int i, ret;
> > +u32 val;
> > +
> > +stm32_ddr_pmu = devm_kzalloc(&pdev->dev, sizeof(struct
> stm32_ddr_pmu),
> > + GFP_KERNEL);
> > +if (!stm32_ddr_pmu)
> > +return -ENOMEM;
> > +platform_set_drvdata(pdev, stm32_ddr_pmu);
> > +
> > +res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +stm32_ddr_pmu->membase = devm_ioremap_resource(&pdev->dev,
> res); if
> > +(IS_ERR(stm32_ddr_pmu->membase)) { pr_warn("Unable to get STM32
> DDR
> > +PMU membase\n"); return PTR_ERR(stm32_ddr_pmu->membase); }
> > +
> > +stm32_ddr_pmu->clk = devm_clk_get(&pdev->dev, "bus"); if
> > +(IS_ERR(stm32_ddr_pmu->clk)) { pr_warn("Unable to get STM32 DDR
> PMU
> > +clock\n"); return PTR_ERR(stm32_ddr_pmu->clk); }
> > +
> > +ret = clk_prepare_enable(stm32_ddr_pmu->clk);
> > +if (ret) {
> > +pr_warn("Unable to prepare STM32 DDR PMU clock\n"); return ret; }
> > +
> > +stm32_ddr_pmu->clk_ddr = devm_clk_get(&pdev->dev, "ddr"); if
> > +(IS_ERR(stm32_ddr_pmu->clk_ddr)) { pr_warn("Unable to get STM32
> DDR
> > +clock\n"); return PTR_ERR(stm32_ddr_pmu->clk_ddr); }
> > +stm32_ddr_pmu->clk_ddr_rate = clk_get_rate(stm32_ddr_pmu-
> >clk_ddr);
> > +stm32_ddr_pmu->clk_ddr_rate /= 1000000;
> > +
> > +stm32_ddr_pmu->poll_period = ms_to_ktime(POLL_MS);
> > +hrtimer_init(&stm32_ddr_pmu->hrtimer, CLOCK_MONOTONIC,
> > + HRTIMER_MODE_REL);
> > +stm32_ddr_pmu->hrtimer.function = stm32_ddr_pmu_poll;
> > +spin_lock_init(&stm32_ddr_pmu->lock);
> > +
> > +for (i = 0; i < PMU_NR_COUNTERS; i++) { stm32_ddr_pmu->events[i] =
> > +NULL; stm32_ddr_pmu->events_cnt[i] = 0; }
> > +
> > +val = readl_relaxed(stm32_ddr_pmu->membase + DDRPERFM_SID); if
> (val
> > +!= SID_MAGIC_ID) return -EINVAL;
> > +
> > +stm32_ddr_pmu->pmu = (struct pmu) {
> > +.task_ctx_nr = perf_invalid_context,
> > +.start = stm32_ddr_pmu_event_start,
> > +.stop = stm32_ddr_pmu_event_stop,
> > +.add = stm32_ddr_pmu_event_add,
> > +.del = stm32_ddr_pmu_event_del,
> > +.event_init = stm32_ddr_pmu_event_init, .attr_groups =
> > +stm32_ddr_pmu_attr_groups, }; ret =
> > +perf_pmu_register(&stm32_ddr_pmu->pmu, "ddrperfm", -1);
>
> Please give this a better name. The usual convention is to use "_pmu" as the
> suffix, so we should use that rather than "perfm".
>
> To be unambiguous, something like "stm32_ddr_pmu" would be good.
Ok, understood
>
> Thanks,
> Mark.
>
> > +if (ret) {
> > +pr_warn("Unable to register STM32 DDR PMU\n"); return ret; }
> > +
> > +rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if
> > +(!IS_ERR(rst)) { reset_control_assert(rst); udelay(2);
> > +reset_control_deassert(rst); }
> > +
> > +pr_info("stm32-ddr-pmu: probed (ID=0x%08x VER=0x%08x),
> DDR@%luMHz\n",
> > +readl_relaxed(stm32_ddr_pmu->membase + DDRPERFM_ID),
> > +readl_relaxed(stm32_ddr_pmu->membase + DDRPERFM_VER),
> > +stm32_ddr_pmu->clk_ddr_rate);
> > +
> > +clk_disable(stm32_ddr_pmu->clk);
> > +
> > +return 0;
> > +}
> > +
> > +static int stm32_ddr_pmu_device_remove(struct platform_device *pdev)
> > +{ struct stm32_ddr_pmu *stm32_ddr_pmu =
> platform_get_drvdata(pdev);
> > +
> > +perf_pmu_unregister(&stm32_ddr_pmu->pmu);
> > +
> > +return 0;
> > +}
> > +
> > +static const struct of_device_id stm32_ddr_pmu_of_match[] = { {
> > +.compatible = "st,stm32-ddr-pmu" }, { }, };
> > +
> > +static struct platform_driver stm32_ddr_pmu_driver = { .driver = {
> > +.name= "stm32-ddr-pmu", .of_match_table =
> > +of_match_ptr(stm32_ddr_pmu_of_match),
> > +},
> > +.probe = stm32_ddr_pmu_device_probe,
> > +.remove = stm32_ddr_pmu_device_remove, };
> > +
> > +module_platform_driver(stm32_ddr_pmu_driver);
> > +
> > +MODULE_DESCRIPTION("Perf driver for STM32 DDR performance
> monitor");
> > +MODULE_AUTHOR("Gerald Baeza <gerald.baeza@st.com>");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.7.4
> IMPORTANT NOTICE: The contents of this email and any attachments are
> confidential and may also be privileged. If you are not the intended recipient,
> please notify the sender immediately and do not disclose the contents to any
> other person, use it for any purpose, or store or copy the information in any
> medium. Thank you.
^ permalink raw reply
* Re: [PATCH v2 2/2] drm/panel: Add Ilitek ILI9341 parallel RGB panel driver
From: Josef Luštický @ 2019-07-12 9:53 UTC (permalink / raw)
To: Sam Ravnborg; +Cc: devicetree, airlied, dri-devel, thierry.reding
In-Reply-To: <20190710134722.GC11791@ravnborg.org>
Hi Sam,
thank you for comments.
st 10. 7. 2019 v 15:47 odesílatel Sam Ravnborg <sam@ravnborg.org> napsal:
>
> Hi Josef.
>
> Thanks for updating the driver.
>
> On Mon, Jul 08, 2019 at 04:56:18PM +0200, Josef Lusticky wrote:
> > Add driver for Ilitek ILI9341 panels in parallel RGB mode
> >
> > Signed-off-by: Josef Lusticky <josef@lusticky.cz>
> > + */
> > +
> > +#include <linux/backlight.h>
> > +#include <linux/delay.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/module.h>
> > +#include <linux/of_device.h>
> > +#include <linux/spi/spi.h>
> > +
> > +#include <video/mipi_display.h>
> > +
> > +#include <drm/drm_modes.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/tinydrm/mipi-dbi.h>
> Good to see drivers that no longer uses drmP.h :-)
>
> > +
> > +/* ILI9341 extended register set (Vendor Command Set) */
> > +#define ILI9341_IFMODE 0xB0 // clock polarity
> > +#define ILI9341_IFCTL 0xF6 // interface conrol
> > +#define ILI9341_PGAMCTRL 0xE0 // positive gamma control
> > +#define ILI9341_NGAMCTRL 0xE1 // negative gamma control
> > +
> > +#define ILI9341_MADCTL_MV BIT(5)
> > +#define ILI9341_MADCTL_MX BIT(6)
> > +#define ILI9341_MADCTL_MY BIT(7)
> > +
> > +/**
> > + * struct ili9341_config - the display specific configuration
> > + * @width_mm: physical panel width [mm]
> > + * @height_mm: physical panel height [mm]
> > + */
> > +struct ili9341_config {
> > + u32 width_mm;
> > + u32 height_mm;
> > +};
> > +
> > +struct ili9341 {
> > + struct drm_panel panel;
> > + struct mipi_dbi *mipi;
> > + const struct ili9341_config *conf;
> > +};
> > +
> > +static inline struct ili9341 *panel_to_ili9341(struct drm_panel *panel)
> > +{
> > + return container_of(panel, struct ili9341, panel);
> > +}
> > +
> > +static int ili9341_deinit(struct drm_panel *panel, struct ili9341 *ili)
> > +{
> > + mipi_dbi_command(ili->mipi, MIPI_DCS_SET_DISPLAY_OFF);
> > + mipi_dbi_command(ili->mipi, MIPI_DCS_ENTER_SLEEP_MODE);
> > + msleep(5);
> > + return 0;
> > +}
> > +
> > +static int ili9341_init(struct drm_panel *panel, struct ili9341 *ili)
> > +{
> > + /* HW / SW Reset display and wait */
> > + if (ili->mipi->reset)
> > + mipi_dbi_hw_reset(ili->mipi);
> > +
> > + mipi_dbi_command(ili->mipi, MIPI_DCS_SOFT_RESET);
> > + msleep(120);
> Consider a usleep_range here - to have the waiting a little more relaxed
> in the system.
>
I am using msleep here as it is recommended for sleeping for 10ms+
by Documentation/timers/timers-howto.txt.
Anyway, I'll change this to call the
mipi_dbi_poweron_reset():mipi_dbi.c function,
which does the same as above.
Plus, I should change the msleep(5) in ili9341_deinit() to
usleep_range(5000, 20000).
> > +
> > + /* Polarity */
> > + mipi_dbi_command(ili->mipi, ILI9341_IFMODE, 0xC0);
> > +
> > + /* Interface control */
> > + mipi_dbi_command(ili->mipi, ILI9341_IFCTL, 0x09, 0x01, 0x26);
> > +
> > + /* Pixel format */
> > + mipi_dbi_command(ili->mipi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_18BIT << 4);
> > +
> > + /* Gamma */
> > + mipi_dbi_command(ili->mipi, MIPI_DCS_SET_GAMMA_CURVE, 0x01);
> > + mipi_dbi_command(ili->mipi, ILI9341_PGAMCTRL,
> > + 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1,
> > + 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00);
> > + mipi_dbi_command(ili->mipi, ILI9341_NGAMCTRL,
> > + 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1,
> > + 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f);
> > +
> > + /* Rotation */
> > + mipi_dbi_command(ili->mipi, MIPI_DCS_SET_ADDRESS_MODE, ILI9341_MADCTL_MX);
> > +
> > + /* Exit sleep mode */
> > + mipi_dbi_command(ili->mipi, MIPI_DCS_EXIT_SLEEP_MODE);
> > + msleep(120);
> > +
> > + mipi_dbi_command(ili->mipi, MIPI_DCS_SET_DISPLAY_ON);
> > +
> > + return 0;
> > +}
> > +
> > +static int ili9341_unprepare(struct drm_panel *panel)
> > +{
> > + struct ili9341 *ili = panel_to_ili9341(panel);
> > +
> > + return ili9341_deinit(panel, ili);
> > +}
> > +
> > +static int ili9341_prepare(struct drm_panel *panel)
> > +{
> > + struct ili9341 *ili = panel_to_ili9341(panel);
> > + int ret;
> > +
> > + ret = ili9341_init(panel, ili);
> > + if (ret < 0)
> > + ili9341_unprepare(panel);
> > + return ret;
> > +}
> > +
> > +static int ili9341_enable(struct drm_panel *panel)
> > +{
> > + struct ili9341 *ili = panel_to_ili9341(panel);
> > +
> > + return backlight_enable(ili->mipi->backlight);
> > +}
> > +
> > +static int ili9341_disable(struct drm_panel *panel)
> > +{
> > + struct ili9341 *ili = panel_to_ili9341(panel);
> > +
> > + return backlight_disable(ili->mipi->backlight);
> > +}
> > +
> > +static const struct drm_display_mode prgb_240x320_mode = {
> > + .clock = 6350,
> > +
> > + .hdisplay = 240,
> > + .hsync_start = 240 + 10,
> > + .hsync_end = 240 + 10 + 10,
> > + .htotal = 240 + 10 + 10 + 20,
> > +
> > + .vdisplay = 320,
> > + .vsync_start = 320 + 4,
> > + .vsync_end = 320 + 4 + 2,
> > + .vtotal = 320 + 4 + 2 + 2,
> > +
> > + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
> > + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED
> > +};
> > +
> > +static int ili9341_get_modes(struct drm_panel *panel)
> > +{
> > + struct drm_connector *connector = panel->connector;
> > + struct ili9341 *ili = panel_to_ili9341(panel);
> > + struct drm_display_mode *mode;
> > +
> > + mode = drm_mode_duplicate(panel->drm, &prgb_240x320_mode);
> > + if (!mode) {
> > + DRM_DEV_ERROR(panel->drm->dev, "bad mode or failed to add mode\n");
> > + return -ENOMEM;
> > + }
> > +
> > + drm_mode_set_name(mode);
> > +
> > + mode->width_mm = ili->conf->width_mm;
> > + mode->height_mm = ili->conf->height_mm;
> > +
> > + connector->display_info.width_mm = mode->width_mm;
> > + connector->display_info.height_mm = mode->height_mm;
> > + connector->display_info.bus_flags |= DRM_BUS_FLAG_DE_HIGH |
> > + DRM_BUS_FLAG_PIXDATA_POSEDGE | DRM_BUS_FLAG_SYNC_NEGEDGE;
> > +
> > + drm_mode_probed_add(connector, mode);
> > +
> > + return 1; /* Number of modes */
> > +}
> > +
> > +static const struct drm_panel_funcs ili9341_drm_funcs = {
> > + .disable = ili9341_disable,
> > + .unprepare = ili9341_unprepare,
> > + .prepare = ili9341_prepare,
> > + .enable = ili9341_enable,
> > + .get_modes = ili9341_get_modes,
> > +};
> > +
> > +static int ili9341_probe(struct spi_device *spi)
> > +{
> > + struct device *dev = &spi->dev;
> > + struct ili9341 *ili;
> > + struct mipi_dbi *mipi;
> > + struct gpio_desc *dc_gpio;
> > + int ret;
> > +
> > + mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
> > + if (!mipi)
> > + return -ENOMEM;
> > +
> > + ili = devm_kzalloc(dev, sizeof(*ili), GFP_KERNEL);
> > + if (!ili)
> > + return -ENOMEM;
> > +
> > + ili->mipi = mipi;
> > +
> > + spi_set_drvdata(spi, ili);
> > +
> > + /*
> > + * Every new incarnation of this display must have a unique
> > + * data entry for the system in this driver.
> > + */
> > + ili->conf = of_device_get_match_data(dev);
> > + if (!ili->conf) {
> > + DRM_DEV_ERROR(dev, "missing device configuration\n");
> > + return -ENODEV;
> > + }
> > +
> > + ili->mipi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
> > + if (IS_ERR(ili->mipi->reset)) {
> > + DRM_DEV_ERROR(dev, "failed to get gpio 'reset'\n");
> > + return PTR_ERR(ili->mipi->reset);
> > + }
> > +
> > + ili->mipi->backlight = devm_of_find_backlight(dev);
> > + if (IS_ERR(ili->mipi->backlight)) {
> > + DRM_DEV_ERROR(dev, "failed to get backlight\n");
> > + return PTR_ERR(ili->mipi->backlight);
> > + }
> > +
> > + dc_gpio = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW);
> > + if (IS_ERR(dc_gpio)) {
> > + DRM_DEV_ERROR(dev, "failed to get gpio 'dc'\n");
> > + return PTR_ERR(dc_gpio);
> > + }
> > +
> > + ret = mipi_dbi_spi_init(spi, ili->mipi, dc_gpio);
> > + if (ret) {
> > + DRM_DEV_ERROR(dev, "MIPI-DBI SPI setup failed\n");
> > + return ret;
> > + }
> > +
> > + drm_panel_init(&ili->panel);
> > + ili->panel.dev = dev;
> > + ili->panel.funcs = &ili9341_drm_funcs;
> > +
> > + return drm_panel_add(&ili->panel);
> > +}
> > +
> > +static int ili9341_remove(struct spi_device *spi)
> > +{
> > + struct ili9341 *ili = spi_get_drvdata(spi);
> > +
> > + drm_panel_remove(&ili->panel);
> > +
> > + ili9341_disable(&ili->panel);
> > + ili9341_unprepare(&ili->panel);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct ili9341_config dt024ctft_data = {
> > + .width_mm = 37,
> > + .height_mm = 49,
> > +};
> > +
> > +static const struct of_device_id ili9341_of_match[] = {
> > + { .compatible = "displaytech,dt024ctft", .data = &dt024ctft_data },
> > + { /* sentinel */ }
> > +};
> If another display is supported then the drm_display_mode may not match.
> So the above may not prove enough in the future.
> for now it should be fine.
>
>
> > +MODULE_DEVICE_TABLE(of, ili9341_of_match);
> > +
> > +static struct spi_driver ili9341_driver = {
> > + .probe = ili9341_probe,
> > + .remove = ili9341_remove,
> > + .driver = {
> > + .name = "panel-ilitek-ili9341",
> > + .of_match_table = ili9341_of_match,
> > + },
> > +};
> > +module_spi_driver(ili9341_driver);
> > +
> > +MODULE_AUTHOR("Josef Lusticky <josef@lusticky.cz>");
> > +MODULE_DESCRIPTION("ILI9341 LCD panel driver");
> > +MODULE_LICENSE("GPL");
>
> Looks good.
> Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
>
> Sam
Thank you!
Josef
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* [PATCH v6 8/8] arm64: defconfig: add CONFIG_COMMON_CLK_MT6765_XXX clocks
From: Macpaul Lin @ 2019-07-12 9:43 UTC (permalink / raw)
To: Rob Herring, Marc Zyngier, Ryder Lee, Stephen Boyd, Sean Wang,
Mars Cheng, Owen Chen, Matthias Brugger, linux-arm-kernel,
linux-mediatek, linux-kernel
Cc: wsd_upstream, CC Hwang, Loda Chou, devicetree, linux-serial,
linux-clk, Macpaul Lin
In-Reply-To: <1562924653-10056-1-git-send-email-macpaul.lin@mediatek.com>
From: Owen Chen <owen.chen@mediatek.com>
Enable MT6765 clock configs, include topckgen, apmixedsys,
infracfg, and subsystem clocks.
Signed-off-by: Owen Chen <owen.chen@mediatek.com>
Signed-off-by: Macpaul Lin <macpaul.lin@mediatek.com>
---
arch/arm64/configs/defconfig | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 4d583514258c..7ab4d09120bb 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -455,6 +455,12 @@ CONFIG_REGULATOR_QCOM_SMD_RPM=y
CONFIG_REGULATOR_QCOM_SPMI=y
CONFIG_REGULATOR_RK808=y
CONFIG_REGULATOR_S2MPS11=y
+CONFIG_COMMON_CLK_MT6765_AUDIOSYS=y
+CONFIG_COMMON_CLK_MT6765_CAMSYS=y
+CONFIG_COMMON_CLK_MT6765_MMSYS=y
+CONFIG_COMMON_CLK_MT6765_IMGSYS=y
+CONFIG_COMMON_CLK_MT6765_VCODECSYS=y
+CONFIG_COMMON_CLK_MT6765_MIPI0ASYS=y
CONFIG_REGULATOR_VCTRL=m
CONFIG_RC_CORE=m
CONFIG_RC_DECODERS=y
--
2.18.0
^ permalink raw reply related
* [PATCH v6 7/8] arm64: dts: mediatek: add mt6765 support
From: Macpaul Lin @ 2019-07-12 9:43 UTC (permalink / raw)
To: Rob Herring, Marc Zyngier, Ryder Lee, Stephen Boyd, Sean Wang,
Mars Cheng, Owen Chen, Matthias Brugger, linux-arm-kernel,
linux-mediatek, linux-kernel
Cc: wsd_upstream, CC Hwang, Loda Chou, devicetree, linux-serial,
linux-clk, Macpaul Lin
In-Reply-To: <1562924653-10056-1-git-send-email-macpaul.lin@mediatek.com>
From: Mars Cheng <mars.cheng@mediatek.com>
Add basic chip support for Mediatek 6765, include
uart node with correct uart clocks, pwrap device
Add clock controller nodes, include topckgen, infracfg,
apmixedsys and subsystem.
Signed-off-by: Mars Cheng <mars.cheng@mediatek.com>
Signed-off-by: Owen Chen <owen.chen@mediatek.com>
Signed-off-by: Macpaul Lin <macpaul.lin@mediatek.com>
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/boot/dts/mediatek/Makefile | 1 +
arch/arm64/boot/dts/mediatek/mt6765-evb.dts | 33 +++
arch/arm64/boot/dts/mediatek/mt6765.dtsi | 253 ++++++++++++++++++++
3 files changed, 287 insertions(+)
create mode 100644 arch/arm64/boot/dts/mediatek/mt6765-evb.dts
create mode 100644 arch/arm64/boot/dts/mediatek/mt6765.dtsi
diff --git a/arch/arm64/boot/dts/mediatek/Makefile b/arch/arm64/boot/dts/mediatek/Makefile
index 458bbc422a94..22bdf1a99a62 100644
--- a/arch/arm64/boot/dts/mediatek/Makefile
+++ b/arch/arm64/boot/dts/mediatek/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
dtb-$(CONFIG_ARCH_MEDIATEK) += mt2712-evb.dtb
dtb-$(CONFIG_ARCH_MEDIATEK) += mt6755-evb.dtb
+dtb-$(CONFIG_ARCH_MEDIATEK) += mt6765-evb.dtb
dtb-$(CONFIG_ARCH_MEDIATEK) += mt6795-evb.dtb
dtb-$(CONFIG_ARCH_MEDIATEK) += mt6797-evb.dtb
dtb-$(CONFIG_ARCH_MEDIATEK) += mt6797-x20-dev.dtb
diff --git a/arch/arm64/boot/dts/mediatek/mt6765-evb.dts b/arch/arm64/boot/dts/mediatek/mt6765-evb.dts
new file mode 100644
index 000000000000..36dddff2b7f8
--- /dev/null
+++ b/arch/arm64/boot/dts/mediatek/mt6765-evb.dts
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dts file for Mediatek MT6765
+ *
+ * (C) Copyright 2018. Mediatek, Inc.
+ *
+ * Mars Cheng <mars.cheng@mediatek.com>
+ */
+
+/dts-v1/;
+#include "mt6765.dtsi"
+
+/ {
+ model = "MediaTek MT6765 EVB";
+ compatible = "mediatek,mt6765-evb", "mediatek,mt6765";
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ memory@40000000 {
+ device_type = "memory";
+ reg = <0 0x40000000 0 0x1e800000>;
+ };
+
+ chosen {
+ stdout-path = "serial0:921600n8";
+ };
+};
+
+&uart0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/mediatek/mt6765.dtsi b/arch/arm64/boot/dts/mediatek/mt6765.dtsi
new file mode 100644
index 000000000000..2662470fe607
--- /dev/null
+++ b/arch/arm64/boot/dts/mediatek/mt6765.dtsi
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dts file for Mediatek MT6765
+ *
+ * (C) Copyright 2018. Mediatek, Inc.
+ *
+ * Mars Cheng <mars.cheng@mediatek.com>
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/clock/mt6765-clk.h>
+
+/ {
+ compatible = "mediatek,mt6765";
+ interrupt-parent = <&sysirq>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ psci {
+ compatible = "arm,psci-0.2";
+ method = "smc";
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ enable-method = "psci";
+ reg = <0x000>;
+ };
+
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ enable-method = "psci";
+ reg = <0x001>;
+ };
+
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ enable-method = "psci";
+ reg = <0x002>;
+ };
+
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ enable-method = "psci";
+ reg = <0x003>;
+ };
+
+ cpu@100 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ enable-method = "psci";
+ reg = <0x100>;
+ };
+
+ cpu@101 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ enable-method = "psci";
+ reg = <0x101>;
+ };
+
+ cpu@102 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ enable-method = "psci";
+ reg = <0x102>;
+ };
+
+ cpu@103 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53";
+ enable-method = "psci";
+ reg = <0x103>;
+ };
+ };
+
+ clocks {
+ clk26m: clk26m {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <26000000>;
+ };
+
+ clk32k: clk32k {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <32000>;
+ };
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ };
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ compatible = "simple-bus";
+ ranges;
+
+ gic: interrupt-controller@c000000 {
+ compatible = "arm,gic-v3";
+ #interrupt-cells = <3>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ interrupt-parent = <&gic>;
+ interrupt-controller;
+ reg = <0 0x0c000000 0 0x40000>, /* GICD */
+ <0 0x0c100000 0 0x200000>, /* GICR */
+ <0 0x0c400000 0 0x2000>, /* GICC */
+ <0 0x0c410000 0 0x2000>, /* GICH */
+ <0 0x0c420000 0 0x20000>; /* GICV */
+ interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ topckgen: syscon@10000000 {
+ compatible = "mediatek,mt6765-topckgen", "syscon";
+ reg = <0 0x10000000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ infracfg: syscon@10001000 {
+ compatible = "mediatek,mt6765-infracfg", "syscon";
+ reg = <0 0x10001000 0 0x1000>;
+ interrupts = <GIC_SPI 147 IRQ_TYPE_EDGE_RISING>;
+ #clock-cells = <1>;
+ };
+
+ pericfg: pericfg@10003000 {
+ compatible = "mediatek,mt6765-pericfg", "syscon";
+ reg = <0 0x10003000 0 0x1000>;
+ };
+
+ scpsys: scpsys@10006000 {
+ compatible = "mediatek,mt6765-scpsys";
+ reg = <0 0x10006000 0 0x1000>; /* spm */
+ #power-domain-cells = <1>;
+ clocks = <&topckgen CLK_TOP_MFG_SEL>,
+ <&topckgen CLK_TOP_MM_SEL>,
+ <&mmsys_config CLK_MM_SMI_COMMON>,
+ <&mmsys_config CLK_MM_SMI_COMM0>,
+ <&mmsys_config CLK_MM_SMI_COMM1>,
+ <&mmsys_config CLK_MM_SMI_LARB0>,
+ <&imgsys CLK_IMG_LARB2>,
+ <&mmsys_config CLK_MM_SMI_IMG>,
+ <&camsys CLK_CAM_LARB3>,
+ <&camsys CLK_CAM_DFP_VAD>,
+ <&camsys CLK_CAM>,
+ <&camsys CLK_CAM_CCU>,
+ <&mmsys_config CLK_MM_SMI_CAM>;
+ clock-names = "mfg", "mm",
+ "mm-0", "mm-1", "mm-2", "mm-3",
+ "isp-0", "isp-1", "cam-0", "cam-1",
+ "cam-2", "cam-3", "cam-4";
+ infracfg = <&infracfg>;
+ smi_comm = <&smi_common>;
+ };
+
+ apmixed: syscon@1000c000 {
+ compatible = "mediatek,mt6765-apmixedsys", "syscon";
+ reg = <0 0x1000c000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ sysirq: interrupt-controller@10200a80 {
+ compatible = "mediatek,mt6765-sysirq",
+ "mediatek,mt6577-sysirq";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ interrupt-parent = <&gic>;
+ reg = <0 0x10200a80 0 0x50>;
+ };
+
+ uart0: serial@11002000 {
+ compatible = "mediatek,mt6765-uart",
+ "mediatek,mt6577-uart";
+ reg = <0 0x11002000 0 0x400>;
+ interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&infracfg CLK_IFR_UART0>,
+ <&infracfg CLK_IFR_AP_DMA>;
+ clock-names = "baud", "bus";
+ status = "disabled";
+ };
+
+ uart1: serial@11003000 {
+ compatible = "mediatek,mt6765-uart",
+ "mediatek,mt6577-uart";
+ reg = <0 0x11003000 0 0x400>;
+ interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&infracfg CLK_IFR_UART1>,
+ <&infracfg CLK_IFR_AP_DMA>;
+ clock-names = "baud", "bus";
+ status = "disabled";
+ };
+
+ audio: syscon@11220000 {
+ compatible = "mediatek,mt6765-audsys", "syscon";
+ reg = <0 0x11220000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ mipi_rx_ana_csi0a: syscon@11c10000 {
+ compatible = "mediatek,mt6765-mipi0a",
+ "syscon";
+ reg = <0 0x11c10000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ mmsys_config: syscon@14000000 {
+ compatible = "mediatek,mt6765-mmsys", "syscon";
+ reg = <0 0x14000000 0 0x1000>;
+ interrupts = <GIC_SPI 227 IRQ_TYPE_LEVEL_LOW>;
+ #clock-cells = <1>;
+ };
+
+ smi_common: smi_common@14002000 {
+ compatible = "mediatek,mt6765-smi-common", "syscon";
+ reg = <0 0x14002000 0 0x1000>;
+ };
+
+ imgsys: syscon@15020000 {
+ compatible = "mediatek,mt6765-imgsys", "syscon";
+ reg = <0 0x15020000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ venc_gcon: syscon@17000000 {
+ compatible = "mediatek,mt6765-vcodecsys", "syscon";
+ reg = <0 0x17000000 0 0x10000>;
+ #clock-cells = <1>;
+ };
+
+ camsys: syscon@1a000000 {
+ compatible = "mediatek,mt6765-camsys", "syscon";
+ reg = <0 0x1a000000 0 0x1000>;
+ #clock-cells = <1>;
+ };
+ }; /* end of soc */
+};
--
2.18.0
^ permalink raw reply related
* [PATCH v6 6/8] soc: mediatek: add MT6765 scpsys and subdomain support
From: Macpaul Lin @ 2019-07-12 9:43 UTC (permalink / raw)
To: Rob Herring, Marc Zyngier, Ryder Lee, Stephen Boyd, Sean Wang,
Mars Cheng, Owen Chen, Matthias Brugger, linux-arm-kernel,
linux-mediatek, linux-kernel
Cc: wsd_upstream, CC Hwang, Loda Chou, devicetree, linux-serial,
linux-clk, Macpaul Lin
In-Reply-To: <1562924653-10056-1-git-send-email-macpaul.lin@mediatek.com>
From: Mars Cheng <mars.cheng@mediatek.com>
This adds scpsys support for MT6765
Add subdomain support for MT6765:
isp, mm, connsys, mfg, and cam.
Signed-off-by: Mars Cheng <mars.cheng@mediatek.com>
Signed-off-by: Owen Chen <owen.chen@mediatek.com>
Signed-off-by: Macpaul Lin <macpaul.lin@mediatek.com>
---
drivers/soc/mediatek/mtk-scpsys.c | 130 ++++++++++++++++++++++++++++++
1 file changed, 130 insertions(+)
diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c
index ea5a221a16e9..ff124c514e9c 100644
--- a/drivers/soc/mediatek/mtk-scpsys.c
+++ b/drivers/soc/mediatek/mtk-scpsys.c
@@ -16,6 +16,7 @@
#include <dt-bindings/power/mt2701-power.h>
#include <dt-bindings/power/mt2712-power.h>
+#include <dt-bindings/power/mt6765-power.h>
#include <dt-bindings/power/mt6797-power.h>
#include <dt-bindings/power/mt7622-power.h>
#include <dt-bindings/power/mt7623a-power.h>
@@ -869,6 +870,120 @@ static const struct scp_subdomain scp_subdomain_mt2712[] = {
{MT2712_POWER_DOMAIN_MFG_SC2, MT2712_POWER_DOMAIN_MFG_SC3},
};
+/*
+ * MT6765 power domain support
+ */
+#define SPM_PWR_STATUS_MT6765 0x0180
+#define SPM_PWR_STATUS_2ND_MT6765 0x0184
+
+static const struct scp_domain_data scp_domain_data_mt6765[] = {
+ [MT6765_POWER_DOMAIN_VCODEC] = {
+ .name = "vcodec",
+ .sta_mask = BIT(26),
+ .ctl_offs = 0x300,
+ .sram_pdn_bits = GENMASK(8, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12),
+ },
+ [MT6765_POWER_DOMAIN_ISP] = {
+ .name = "isp",
+ .sta_mask = BIT(5),
+ .ctl_offs = 0x308,
+ .sram_pdn_bits = GENMASK(8, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12),
+ .subsys_clk_prefix = "isp",
+ .bp_table = {
+ BUS_PROT(IFR_TYPE, 0x2A8, 0x2AC, 0, 0x258,
+ BIT(20), BIT(20)),
+ BUS_PROT(SMI_TYPE, 0x3C4, 0x3C8, 0, 0x3C0,
+ BIT(2), BIT(2)),
+ },
+ },
+ [MT6765_POWER_DOMAIN_MM] = {
+ .name = "mm",
+ .sta_mask = BIT(3),
+ .ctl_offs = 0x30C,
+ .sram_pdn_bits = GENMASK(8, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12),
+ .basic_clk_id = {"mm"},
+ .subsys_clk_prefix = "mm",
+ .bp_table = {
+ BUS_PROT(IFR_TYPE, 0x2A8, 0x2AC, 0, 0x258,
+ BIT(16) | BIT(17), BIT(16) | BIT(17)),
+ BUS_PROT(IFR_TYPE, 0x2A0, 0x2A4, 0, 0x228,
+ BIT(10) | BIT(11), BIT(10) | BIT(11)),
+ BUS_PROT(IFR_TYPE, 0x2A0, 0x2A4, 0, 0x228,
+ BIT(1) | BIT(2), BIT(1) | BIT(2)),
+ },
+ },
+ [MT6765_POWER_DOMAIN_CONN] = {
+ .name = "conn",
+ .sta_mask = BIT(1),
+ .ctl_offs = 0x32C,
+ .sram_pdn_bits = 0,
+ .sram_pdn_ack_bits = 0,
+ .bp_table = {
+ BUS_PROT(IFR_TYPE, 0x2A0, 0x2A4, 0, 0x228,
+ BIT(13), BIT(13)),
+ BUS_PROT(IFR_TYPE, 0x2A8, 0x2AC, 0, 0x258,
+ BIT(18), BIT(18)),
+ BUS_PROT(IFR_TYPE, 0x2A0, 0x2A4, 0, 0x228,
+ BIT(14) | BIT(16), BIT(14) | BIT(16)),
+ },
+ },
+ [MT6765_POWER_DOMAIN_MFG_ASYNC] = {
+ .name = "mfg_async",
+ .sta_mask = BIT(23),
+ .ctl_offs = 0x334,
+ .sram_pdn_bits = 0,
+ .sram_pdn_ack_bits = 0,
+ .basic_clk_id = {"mfg"},
+ },
+ [MT6765_POWER_DOMAIN_MFG] = {
+ .name = "mfg",
+ .sta_mask = BIT(4),
+ .ctl_offs = 0x338,
+ .sram_pdn_bits = GENMASK(8, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12),
+ .bp_table = {
+ BUS_PROT(IFR_TYPE, 0x2A0, 0x2A4, 0, 0x228,
+ BIT(25), BIT(25)),
+ BUS_PROT(IFR_TYPE, 0x2A0, 0x2A4, 0, 0x228,
+ BIT(21) | BIT(22), BIT(21) | BIT(22)),
+ }
+ },
+ [MT6765_POWER_DOMAIN_CAM] = {
+ .name = "cam",
+ .sta_mask = BIT(25),
+ .ctl_offs = 0x344,
+ .sram_pdn_bits = GENMASK(8, 9),
+ .sram_pdn_ack_bits = GENMASK(12, 13),
+ .subsys_clk_prefix = "cam",
+ .bp_table = {
+ BUS_PROT(IFR_TYPE, 0x2A8, 0x2AC, 0, 0x258,
+ BIT(19) | BIT(21), BIT(19) | BIT(21)),
+ BUS_PROT(IFR_TYPE, 0x2A0, 0x2A4, 0, 0x228,
+ BIT(20), BIT(20)),
+ BUS_PROT(SMI_TYPE, 0x3C4, 0x3C8, 0, 0x3C0,
+ BIT(3), BIT(3)),
+ }
+ },
+ [MT6765_POWER_DOMAIN_MFG_CORE0] = {
+ .name = "mfg_core0",
+ .sta_mask = BIT(7),
+ .ctl_offs = 0x34C,
+ .sram_pdn_bits = GENMASK(8, 8),
+ .sram_pdn_ack_bits = GENMASK(12, 12),
+ },
+};
+
+static const struct scp_subdomain scp_subdomain_mt6765[] = {
+ {MT6765_POWER_DOMAIN_MM, MT6765_POWER_DOMAIN_CAM},
+ {MT6765_POWER_DOMAIN_MM, MT6765_POWER_DOMAIN_ISP},
+ {MT6765_POWER_DOMAIN_MM, MT6765_POWER_DOMAIN_VCODEC},
+ {MT6765_POWER_DOMAIN_MFG_ASYNC, MT6765_POWER_DOMAIN_MFG},
+ {MT6765_POWER_DOMAIN_MFG, MT6765_POWER_DOMAIN_MFG_CORE0},
+};
+
/*
* MT6797 power domain support
*/
@@ -1363,6 +1478,18 @@ static const struct scp_soc_data mt2712_data = {
.bus_prot_reg_update = false,
};
+static const struct scp_soc_data mt6765_data = {
+ .domains = scp_domain_data_mt6765,
+ .num_domains = ARRAY_SIZE(scp_domain_data_mt6765),
+ .subdomains = scp_subdomain_mt6765,
+ .num_subdomains = ARRAY_SIZE(scp_subdomain_mt6765),
+ .regs = {
+ .pwr_sta_offs = SPM_PWR_STATUS_MT6765,
+ .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND_MT6765,
+ },
+ .bus_prot_reg_update = true,
+};
+
static const struct scp_soc_data mt6797_data = {
.domains = scp_domain_data_mt6797,
.num_domains = ARRAY_SIZE(scp_domain_data_mt6797),
@@ -1429,6 +1556,9 @@ static const struct of_device_id of_scpsys_match_tbl[] = {
}, {
.compatible = "mediatek,mt2712-scpsys",
.data = &mt2712_data,
+ }, {
+ .compatible = "mediatek,mt6765-scpsys",
+ .data = &mt6765_data,
}, {
.compatible = "mediatek,mt6797-scpsys",
.data = &mt6797_data,
--
2.18.0
^ permalink raw reply related
* [PATCH v6 5/8] clk: mediatek: Add MT6765 clock support
From: Macpaul Lin @ 2019-07-12 9:43 UTC (permalink / raw)
To: Rob Herring, Marc Zyngier, Ryder Lee, Stephen Boyd, Sean Wang,
Mars Cheng, Owen Chen, Matthias Brugger, linux-arm-kernel,
linux-mediatek, linux-kernel
Cc: wsd_upstream, CC Hwang, Loda Chou, devicetree, linux-serial,
linux-clk, Macpaul Lin
In-Reply-To: <1562924653-10056-1-git-send-email-macpaul.lin@mediatek.com>
From: Owen Chen <owen.chen@mediatek.com>
Add MT6765 clock support, include topckgen, apmixedsys,
infracfg, mcucfg and subsystem clocks.
Signed-off-by: Owen Chen <owen.chen@mediatek.com>
Signed-off-by: Mars Cheng <mars.cheng@mediatek.com>
Signed-off-by: Macpaul Lin <macpaul.lin@mediatek.com>
---
drivers/clk/mediatek/Kconfig | 86 ++
drivers/clk/mediatek/Makefile | 7 +
drivers/clk/mediatek/clk-mt6765-audio.c | 109 +++
drivers/clk/mediatek/clk-mt6765-cam.c | 83 ++
drivers/clk/mediatek/clk-mt6765-img.c | 79 ++
drivers/clk/mediatek/clk-mt6765-mipi0a.c | 77 ++
drivers/clk/mediatek/clk-mt6765-mm.c | 105 +++
drivers/clk/mediatek/clk-mt6765-vcodec.c | 79 ++
drivers/clk/mediatek/clk-mt6765.c | 961 +++++++++++++++++++++++
9 files changed, 1586 insertions(+)
create mode 100644 drivers/clk/mediatek/clk-mt6765-audio.c
create mode 100644 drivers/clk/mediatek/clk-mt6765-cam.c
create mode 100644 drivers/clk/mediatek/clk-mt6765-img.c
create mode 100644 drivers/clk/mediatek/clk-mt6765-mipi0a.c
create mode 100644 drivers/clk/mediatek/clk-mt6765-mm.c
create mode 100644 drivers/clk/mediatek/clk-mt6765-vcodec.c
create mode 100644 drivers/clk/mediatek/clk-mt6765.c
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index 4d8a9aef95f6..1a8db28f9d10 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -116,6 +116,92 @@ config COMMON_CLK_MT2712_VENCSYS
---help---
This driver supports MediaTek MT2712 vencsys clocks.
+config COMMON_CLK_MT6765
+ bool "Clock driver for MediaTek MT6765"
+ depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST
+ select COMMON_CLK_MEDIATEK
+ default ARCH_MEDIATEK && ARM64
+ help
+ This driver supports MediaTek MT6765 basic clocks.
+
+config COMMON_CLK_MT6765_AUDIOSYS
+ bool "Clock driver for MediaTek MT6765 audiosys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 audiosys clocks.
+
+config COMMON_CLK_MT6765_CAMSYS
+ bool "Clock driver for MediaTek MT6765 camsys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 camsys clocks.
+
+config COMMON_CLK_MT6765_GCESYS
+ bool "Clock driver for MediaTek MT6765 gcesys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 gcesys clocks.
+
+config COMMON_CLK_MT6765_MMSYS
+ bool "Clock driver for MediaTek MT6765 mmsys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 mmsys clocks.
+
+config COMMON_CLK_MT6765_IMGSYS
+ bool "Clock driver for MediaTek MT6765 imgsys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 imgsys clocks.
+
+config COMMON_CLK_MT6765_VCODECSYS
+ bool "Clock driver for MediaTek MT6765 vcodecsys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 vcodecsys clocks.
+
+config COMMON_CLK_MT6765_MFGSYS
+ bool "Clock driver for MediaTek MT6765 mfgsys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 mfgsys clocks.
+
+config COMMON_CLK_MT6765_MIPI0ASYS
+ bool "Clock driver for MediaTek MT6765 mipi0asys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 mipi0asys clocks.
+
+config COMMON_CLK_MT6765_MIPI0BSYS
+ bool "Clock driver for MediaTek MT6765 mipi0bsys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 mipi0bsys clocks.
+
+config COMMON_CLK_MT6765_MIPI1ASYS
+ bool "Clock driver for MediaTek MT6765 mipi1asys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 mipi1asys clocks.
+
+config COMMON_CLK_MT6765_MIPI1BSYS
+ bool "Clock driver for MediaTek MT6765 mipi1bsys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 mipi1bsys clocks.
+
+config COMMON_CLK_MT6765_MIPI2ASYS
+ bool "Clock driver for MediaTek MT6765 mipi2asys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 mipi2asys clocks.
+
+config COMMON_CLK_MT6765_MIPI2BSYS
+ bool "Clock driver for MediaTek MT6765 mipi2bsys"
+ depends on COMMON_CLK_MT6765
+ help
+ This driver supports MediaTek MT6765 mipi2bsys clocks.
+
config COMMON_CLK_MT6797
bool "Clock driver for MediaTek MT6797"
depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index f74937b35f68..c368442914a8 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -1,6 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o clk-cpumux.o reset.o clk-mux.o
+obj-$(CONFIG_COMMON_CLK_MT6765) += clk-mt6765.o
+obj-$(CONFIG_COMMON_CLK_MT6765_AUDIOSYS) += clk-mt6765-audio.o
+obj-$(CONFIG_COMMON_CLK_MT6765_CAMSYS) += clk-mt6765-cam.o
+obj-$(CONFIG_COMMON_CLK_MT6765_IMGSYS) += clk-mt6765-img.o
+obj-$(CONFIG_COMMON_CLK_MT6765_MIPI0ASYS) += clk-mt6765-mipi0a.o
+obj-$(CONFIG_COMMON_CLK_MT6765_MMSYS) += clk-mt6765-mm.o
+obj-$(CONFIG_COMMON_CLK_MT6765_VCODECSYS) += clk-mt6765-vcodec.o
obj-$(CONFIG_COMMON_CLK_MT6797) += clk-mt6797.o
obj-$(CONFIG_COMMON_CLK_MT6797_IMGSYS) += clk-mt6797-img.o
obj-$(CONFIG_COMMON_CLK_MT6797_MMSYS) += clk-mt6797-mm.o
diff --git a/drivers/clk/mediatek/clk-mt6765-audio.c b/drivers/clk/mediatek/clk-mt6765-audio.c
new file mode 100644
index 000000000000..41f19343dfb9
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6765-audio.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Owen Chen <owen.chen@mediatek.com>
+ *
+ * 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/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt6765-clk.h>
+
+static const struct mtk_gate_regs audio0_cg_regs = {
+ .set_ofs = 0x0,
+ .clr_ofs = 0x0,
+ .sta_ofs = 0x0,
+};
+
+static const struct mtk_gate_regs audio1_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x4,
+ .sta_ofs = 0x4,
+};
+
+#define GATE_AUDIO0(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &audio0_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr, \
+ }
+
+#define GATE_AUDIO1(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &audio1_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr, \
+ }
+
+static const struct mtk_gate audio_clks[] = {
+ /* AUDIO0 */
+ GATE_AUDIO0(CLK_AUDIO_AFE, "aud_afe", "audio_ck", 2),
+ GATE_AUDIO0(CLK_AUDIO_22M, "aud_22m", "aud_engen1_ck", 8),
+ GATE_AUDIO0(CLK_AUDIO_APLL_TUNER, "aud_apll_tuner",
+ "aud_engen1_ck", 19),
+ GATE_AUDIO0(CLK_AUDIO_ADC, "aud_adc", "audio_ck", 24),
+ GATE_AUDIO0(CLK_AUDIO_DAC, "aud_dac", "audio_ck", 25),
+ GATE_AUDIO0(CLK_AUDIO_DAC_PREDIS, "aud_dac_predis",
+ "audio_ck", 26),
+ GATE_AUDIO0(CLK_AUDIO_TML, "aud_tml", "audio_ck", 27),
+ /* AUDIO1 */
+ GATE_AUDIO1(CLK_AUDIO_I2S1_BCLK, "aud_i2s1_bclk",
+ "audio_ck", 4),
+ GATE_AUDIO1(CLK_AUDIO_I2S2_BCLK, "aud_i2s2_bclk",
+ "audio_ck", 5),
+ GATE_AUDIO1(CLK_AUDIO_I2S3_BCLK, "aud_i2s3_bclk",
+ "audio_ck", 6),
+ GATE_AUDIO1(CLK_AUDIO_I2S4_BCLK, "aud_i2s4_bclk",
+ "audio_ck", 7),
+};
+
+static int clk_mt6765_audio_probe(struct platform_device *pdev)
+{
+ struct clk_onecell_data *clk_data;
+ int r;
+ struct device_node *node = pdev->dev.of_node;
+
+ clk_data = mtk_alloc_clk_data(CLK_AUDIO_NR_CLK);
+
+ mtk_clk_register_gates(node, audio_clks,
+ ARRAY_SIZE(audio_clks), clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ return r;
+}
+
+static const struct of_device_id of_match_clk_mt6765_audio[] = {
+ { .compatible = "mediatek,mt6765-audsys", },
+ {}
+};
+
+static struct platform_driver clk_mt6765_audio_drv = {
+ .probe = clk_mt6765_audio_probe,
+ .driver = {
+ .name = "clk-mt6765-audio",
+ .of_match_table = of_match_clk_mt6765_audio,
+ },
+};
+
+builtin_platform_driver(clk_mt6765_audio_drv);
diff --git a/drivers/clk/mediatek/clk-mt6765-cam.c b/drivers/clk/mediatek/clk-mt6765-cam.c
new file mode 100644
index 000000000000..d0542f07192c
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6765-cam.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Owen Chen <owen.chen@mediatek.com>
+ *
+ * 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/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt6765-clk.h>
+
+static const struct mtk_gate_regs cam_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_CAM(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &cam_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+static const struct mtk_gate cam_clks[] = {
+ GATE_CAM(CLK_CAM_LARB3, "cam_larb3", "mm_ck", 0),
+ GATE_CAM(CLK_CAM_DFP_VAD, "cam_dfp_vad", "mm_ck", 1),
+ GATE_CAM(CLK_CAM, "cam", "mm_ck", 6),
+ GATE_CAM(CLK_CAMTG, "camtg", "mm_ck", 7),
+ GATE_CAM(CLK_CAM_SENINF, "cam_seninf", "mm_ck", 8),
+ GATE_CAM(CLK_CAMSV0, "camsv0", "mm_ck", 9),
+ GATE_CAM(CLK_CAMSV1, "camsv1", "mm_ck", 10),
+ GATE_CAM(CLK_CAMSV2, "camsv2", "mm_ck", 11),
+ GATE_CAM(CLK_CAM_CCU, "cam_ccu", "mm_ck", 12),
+};
+
+static int clk_mt6765_cam_probe(struct platform_device *pdev)
+{
+ struct clk_onecell_data *clk_data;
+ int r;
+ struct device_node *node = pdev->dev.of_node;
+
+ clk_data = mtk_alloc_clk_data(CLK_CAM_NR_CLK);
+
+ mtk_clk_register_gates(node, cam_clks, ARRAY_SIZE(cam_clks), clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ return r;
+}
+
+static const struct of_device_id of_match_clk_mt6765_cam[] = {
+ { .compatible = "mediatek,mt6765-camsys", },
+ {}
+};
+
+static struct platform_driver clk_mt6765_cam_drv = {
+ .probe = clk_mt6765_cam_probe,
+ .driver = {
+ .name = "clk-mt6765-cam",
+ .of_match_table = of_match_clk_mt6765_cam,
+ },
+};
+
+builtin_platform_driver(clk_mt6765_cam_drv);
diff --git a/drivers/clk/mediatek/clk-mt6765-img.c b/drivers/clk/mediatek/clk-mt6765-img.c
new file mode 100644
index 000000000000..eaf8c25ce8b6
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6765-img.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Owen Chen <owen.chen@mediatek.com>
+ *
+ * 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/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt6765-clk.h>
+
+static const struct mtk_gate_regs img_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_IMG(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &img_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+static const struct mtk_gate img_clks[] = {
+ GATE_IMG(CLK_IMG_LARB2, "img_larb2", "mm_ck", 0),
+ GATE_IMG(CLK_IMG_DIP, "img_dip", "mm_ck", 2),
+ GATE_IMG(CLK_IMG_FDVT, "img_fdvt", "mm_ck", 3),
+ GATE_IMG(CLK_IMG_DPE, "img_dpe", "mm_ck", 4),
+ GATE_IMG(CLK_IMG_RSC, "img_rsc", "mm_ck", 5),
+};
+
+static int clk_mt6765_img_probe(struct platform_device *pdev)
+{
+ struct clk_onecell_data *clk_data;
+ int r;
+ struct device_node *node = pdev->dev.of_node;
+
+ clk_data = mtk_alloc_clk_data(CLK_IMG_NR_CLK);
+
+ mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks), clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ return r;
+}
+
+static const struct of_device_id of_match_clk_mt6765_img[] = {
+ { .compatible = "mediatek,mt6765-imgsys", },
+ {}
+};
+
+static struct platform_driver clk_mt6765_img_drv = {
+ .probe = clk_mt6765_img_probe,
+ .driver = {
+ .name = "clk-mt6765-img",
+ .of_match_table = of_match_clk_mt6765_img,
+ },
+};
+
+builtin_platform_driver(clk_mt6765_img_drv);
diff --git a/drivers/clk/mediatek/clk-mt6765-mipi0a.c b/drivers/clk/mediatek/clk-mt6765-mipi0a.c
new file mode 100644
index 000000000000..f6c45326dd52
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6765-mipi0a.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Owen Chen <owen.chen@mediatek.com>
+ *
+ * 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/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt6765-clk.h>
+
+static const struct mtk_gate_regs mipi0a_cg_regs = {
+ .set_ofs = 0x80,
+ .clr_ofs = 0x80,
+ .sta_ofs = 0x80,
+};
+
+#define GATE_MIPI0A(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mipi0a_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
+ }
+
+static const struct mtk_gate mipi0a_clks[] = {
+ GATE_MIPI0A(CLK_MIPI0A_CSR_CSI_EN_0A,
+ "mipi0a_csr_0a", "f_fseninf_ck", 1),
+};
+
+static int clk_mt6765_mipi0a_probe(struct platform_device *pdev)
+{
+ struct clk_onecell_data *clk_data;
+ int r;
+ struct device_node *node = pdev->dev.of_node;
+
+ clk_data = mtk_alloc_clk_data(CLK_MIPI0A_NR_CLK);
+
+ mtk_clk_register_gates(node, mipi0a_clks,
+ ARRAY_SIZE(mipi0a_clks), clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ return r;
+}
+
+static const struct of_device_id of_match_clk_mt6765_mipi0a[] = {
+ { .compatible = "mediatek,mt6765-mipi0a", },
+ {}
+};
+
+static struct platform_driver clk_mt6765_mipi0a_drv = {
+ .probe = clk_mt6765_mipi0a_probe,
+ .driver = {
+ .name = "clk-mt6765-mipi0a",
+ .of_match_table = of_match_clk_mt6765_mipi0a,
+ },
+};
+
+builtin_platform_driver(clk_mt6765_mipi0a_drv);
diff --git a/drivers/clk/mediatek/clk-mt6765-mm.c b/drivers/clk/mediatek/clk-mt6765-mm.c
new file mode 100644
index 000000000000..97b13fe3a4fe
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6765-mm.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Owen Chen <owen.chen@mediatek.com>
+ *
+ * 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/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt6765-clk.h>
+
+static const struct mtk_gate_regs mm_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x108,
+ .sta_ofs = 0x100,
+};
+
+#define GATE_MM(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &mm_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+static const struct mtk_gate mm_clks[] = {
+ /* MM */
+ GATE_MM(CLK_MM_MDP_RDMA0, "mm_mdp_rdma0", "mm_ck", 0),
+ GATE_MM(CLK_MM_MDP_CCORR0, "mm_mdp_ccorr0", "mm_ck", 1),
+ GATE_MM(CLK_MM_MDP_RSZ0, "mm_mdp_rsz0", "mm_ck", 2),
+ GATE_MM(CLK_MM_MDP_RSZ1, "mm_mdp_rsz1", "mm_ck", 3),
+ GATE_MM(CLK_MM_MDP_TDSHP0, "mm_mdp_tdshp0", "mm_ck", 4),
+ GATE_MM(CLK_MM_MDP_WROT0, "mm_mdp_wrot0", "mm_ck", 5),
+ GATE_MM(CLK_MM_MDP_WDMA0, "mm_mdp_wdma0", "mm_ck", 6),
+ GATE_MM(CLK_MM_DISP_OVL0, "mm_disp_ovl0", "mm_ck", 7),
+ GATE_MM(CLK_MM_DISP_OVL0_2L, "mm_disp_ovl0_2l", "mm_ck", 8),
+ GATE_MM(CLK_MM_DISP_RSZ0, "mm_disp_rsz0", "mm_ck", 9),
+ GATE_MM(CLK_MM_DISP_RDMA0, "mm_disp_rdma0", "mm_ck", 10),
+ GATE_MM(CLK_MM_DISP_WDMA0, "mm_disp_wdma0", "mm_ck", 11),
+ GATE_MM(CLK_MM_DISP_COLOR0, "mm_disp_color0", "mm_ck", 12),
+ GATE_MM(CLK_MM_DISP_CCORR0, "mm_disp_ccorr0", "mm_ck", 13),
+ GATE_MM(CLK_MM_DISP_AAL0, "mm_disp_aal0", "mm_ck", 14),
+ GATE_MM(CLK_MM_DISP_GAMMA0, "mm_disp_gamma0", "mm_ck", 15),
+ GATE_MM(CLK_MM_DISP_DITHER0, "mm_disp_dither0", "mm_ck", 16),
+ GATE_MM(CLK_MM_DSI0, "mm_dsi0", "mm_ck", 17),
+ GATE_MM(CLK_MM_FAKE_ENG, "mm_fake_eng", "mm_ck", 18),
+ GATE_MM(CLK_MM_SMI_COMMON, "mm_smi_common", "mm_ck", 19),
+ GATE_MM(CLK_MM_SMI_LARB0, "mm_smi_larb0", "mm_ck", 20),
+ GATE_MM(CLK_MM_SMI_COMM0, "mm_smi_comm0", "mm_ck", 21),
+ GATE_MM(CLK_MM_SMI_COMM1, "mm_smi_comm1", "mm_ck", 22),
+ GATE_MM(CLK_MM_CAM_MDP, "mm_cam_mdp_ck", "mm_ck", 23),
+ GATE_MM(CLK_MM_SMI_IMG, "mm_smi_img_ck", "mm_ck", 24),
+ GATE_MM(CLK_MM_SMI_CAM, "mm_smi_cam_ck", "mm_ck", 25),
+ GATE_MM(CLK_MM_IMG_DL_RELAY, "mm_img_dl_relay", "mm_ck", 26),
+ GATE_MM(CLK_MM_IMG_DL_ASYNC_TOP, "mm_imgdl_async", "mm_ck", 27),
+ GATE_MM(CLK_MM_DIG_DSI, "mm_dig_dsi_ck", "mm_ck", 28),
+ GATE_MM(CLK_MM_F26M_HRTWT, "mm_hrtwt", "f_f26m_ck", 29),
+};
+
+static int clk_mt6765_mm_probe(struct platform_device *pdev)
+{
+ struct clk_onecell_data *clk_data;
+ int r;
+ struct device_node *node = pdev->dev.of_node;
+
+ clk_data = mtk_alloc_clk_data(CLK_MM_NR_CLK);
+
+ mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks), clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ return r;
+}
+
+static const struct of_device_id of_match_clk_mt6765_mm[] = {
+ { .compatible = "mediatek,mt6765-mmsys", },
+ {}
+};
+
+static struct platform_driver clk_mt6765_mm_drv = {
+ .probe = clk_mt6765_mm_probe,
+ .driver = {
+ .name = "clk-mt6765-mm",
+ .of_match_table = of_match_clk_mt6765_mm,
+ },
+};
+
+builtin_platform_driver(clk_mt6765_mm_drv);
diff --git a/drivers/clk/mediatek/clk-mt6765-vcodec.c b/drivers/clk/mediatek/clk-mt6765-vcodec.c
new file mode 100644
index 000000000000..eb9ae1c2c99c
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6765-vcodec.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Owen Chen <owen.chen@mediatek.com>
+ *
+ * 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/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt6765-clk.h>
+
+static const struct mtk_gate_regs venc_cg_regs = {
+ .set_ofs = 0x4,
+ .clr_ofs = 0x8,
+ .sta_ofs = 0x0,
+};
+
+#define GATE_VENC(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &venc_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr_inv, \
+ }
+
+static const struct mtk_gate venc_clks[] = {
+ GATE_VENC(CLK_VENC_SET0_LARB, "venc_set0_larb", "mm_ck", 0),
+ GATE_VENC(CLK_VENC_SET1_VENC, "venc_set1_venc", "mm_ck", 4),
+ GATE_VENC(CLK_VENC_SET2_JPGENC, "jpgenc", "mm_ck", 8),
+ GATE_VENC(CLK_VENC_SET3_VDEC, "venc_set3_vdec", "mm_ck", 12),
+};
+
+static int clk_mt6765_vcodec_probe(struct platform_device *pdev)
+{
+ struct clk_onecell_data *clk_data;
+ int r;
+ struct device_node *node = pdev->dev.of_node;
+
+ clk_data = mtk_alloc_clk_data(CLK_VENC_NR_CLK);
+
+ mtk_clk_register_gates(node, venc_clks,
+ ARRAY_SIZE(venc_clks), clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ return r;
+}
+
+static const struct of_device_id of_match_clk_mt6765_vcodec[] = {
+ { .compatible = "mediatek,mt6765-vcodecsys", },
+ {}
+};
+
+static struct platform_driver clk_mt6765_vcodec_drv = {
+ .probe = clk_mt6765_vcodec_probe,
+ .driver = {
+ .name = "clk-mt6765-vcodec",
+ .of_match_table = of_match_clk_mt6765_vcodec,
+ },
+};
+
+builtin_platform_driver(clk_mt6765_vcodec_drv);
diff --git a/drivers/clk/mediatek/clk-mt6765.c b/drivers/clk/mediatek/clk-mt6765.c
new file mode 100644
index 000000000000..f716a48a926d
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt6765.c
@@ -0,0 +1,961 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Owen Chen <owen.chen@mediatek.com>
+ *
+ * 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/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+#include "clk-mux.h"
+
+#include <dt-bindings/clock/mt6765-clk.h>
+
+/*fmeter div select 4*/
+#define _DIV4_ 1
+
+static DEFINE_SPINLOCK(mt6765_clk_lock);
+
+/* Total 12 subsys */
+static void __iomem *cksys_base;
+static void __iomem *apmixed_base;
+
+/* CKSYS */
+#define CLK_SCP_CFG_0 (cksys_base + 0x200)
+#define CLK_SCP_CFG_1 (cksys_base + 0x204)
+
+/* CG */
+#define AP_PLL_CON3 (apmixed_base + 0x0C)
+#define PLLON_CON0 (apmixed_base + 0x44)
+#define PLLON_CON1 (apmixed_base + 0x48)
+
+/* clk cfg update */
+#define CLK_CFG_0 0x40
+#define CLK_CFG_0_SET 0x44
+#define CLK_CFG_0_CLR 0x48
+#define CLK_CFG_1 0x50
+#define CLK_CFG_1_SET 0x54
+#define CLK_CFG_1_CLR 0x58
+#define CLK_CFG_2 0x60
+#define CLK_CFG_2_SET 0x64
+#define CLK_CFG_2_CLR 0x68
+#define CLK_CFG_3 0x70
+#define CLK_CFG_3_SET 0x74
+#define CLK_CFG_3_CLR 0x78
+#define CLK_CFG_4 0x80
+#define CLK_CFG_4_SET 0x84
+#define CLK_CFG_4_CLR 0x88
+#define CLK_CFG_5 0x90
+#define CLK_CFG_5_SET 0x94
+#define CLK_CFG_5_CLR 0x98
+#define CLK_CFG_6 0xa0
+#define CLK_CFG_6_SET 0xa4
+#define CLK_CFG_6_CLR 0xa8
+#define CLK_CFG_7 0xb0
+#define CLK_CFG_7_SET 0xb4
+#define CLK_CFG_7_CLR 0xb8
+#define CLK_CFG_8 0xc0
+#define CLK_CFG_8_SET 0xc4
+#define CLK_CFG_8_CLR 0xc8
+#define CLK_CFG_9 0xd0
+#define CLK_CFG_9_SET 0xd4
+#define CLK_CFG_9_CLR 0xd8
+#define CLK_CFG_10 0xe0
+#define CLK_CFG_10_SET 0xe4
+#define CLK_CFG_10_CLR 0xe8
+#define CLK_CFG_UPDATE 0x004
+
+static const struct mtk_fixed_clk fixed_clks[] = {
+ FIXED_CLK(CLK_TOP_F_FRTC, "f_frtc_ck", "clk32k", 32768),
+ FIXED_CLK(CLK_TOP_CLK26M, "clk_26m_ck", "clk26m", 26000000),
+ FIXED_CLK(CLK_TOP_DMPLL, "dmpll_ck", NULL, 466000000),
+};
+
+static const struct mtk_fixed_factor top_divs[] = {
+ FACTOR(CLK_TOP_SYSPLL, "syspll_ck", "mainpll", 1, 1),
+ FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "syspll_d2", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "syspll_d2", 1, 4),
+ FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "syspll_d2", 1, 8),
+ FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "syspll_d2", 1, 16),
+ FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll", 1, 3),
+ FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "syspll_d3", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "syspll_d3", 1, 4),
+ FACTOR(CLK_TOP_SYSPLL2_D8, "syspll2_d8", "syspll_d3", 1, 8),
+ FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll", 1, 5),
+ FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "syspll_d5", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "syspll_d5", 1, 4),
+ FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "mainpll", 1, 7),
+ FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "syspll_d7", 1, 2),
+ FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "syspll_d7", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL, "univpll", "univ2pll", 1, 2),
+ FACTOR(CLK_TOP_USB20_192M, "usb20_192m_ck", "univpll", 2, 13),
+ FACTOR(CLK_TOP_USB20_192M_D4, "usb20_192m_d4", "usb20_192m_ck", 1, 4),
+ FACTOR(CLK_TOP_USB20_192M_D8, "usb20_192m_d8", "usb20_192m_ck", 1, 8),
+ FACTOR(CLK_TOP_USB20_192M_D16,
+ "usb20_192m_d16", "usb20_192m_ck", 1, 16),
+ FACTOR(CLK_TOP_USB20_192M_D32,
+ "usb20_192m_d32", "usb20_192m_ck", 1, 32),
+ FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_d2", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_d2", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3),
+ FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll_d3", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll_d3", 1, 4),
+ FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll_d3", 1, 8),
+ FACTOR(CLK_TOP_UNIVPLL2_D32, "univpll2_d32", "univpll_d3", 1, 32),
+ FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
+ FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univpll_d5", 1, 2),
+ FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univpll_d5", 1, 4),
+ FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1),
+ FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll_ck", 1, 2),
+ FACTOR(CLK_TOP_MPLL, "mpll_ck", "mpll", 1, 1),
+ FACTOR(CLK_TOP_DA_MPLL_104M_DIV, "mpll_104m_div", "mpll_ck", 1, 2),
+ FACTOR(CLK_TOP_DA_MPLL_52M_DIV, "mpll_52m_div", "mpll_ck", 1, 4),
+ FACTOR(CLK_TOP_MFGPLL, "mfgpll_ck", "mfgpll", 1, 1),
+ FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1),
+ FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll_ck", 1, 2),
+ FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1, 1),
+ FACTOR(CLK_TOP_APLL1_D2, "apll1_d2", "apll1_ck", 1, 2),
+ FACTOR(CLK_TOP_APLL1_D4, "apll1_d4", "apll1_ck", 1, 4),
+ FACTOR(CLK_TOP_APLL1_D8, "apll1_d8", "apll1_ck", 1, 8),
+ FACTOR(CLK_TOP_ULPOSC1, "ulposc1_ck", "ulposc1", 1, 1),
+ FACTOR(CLK_TOP_ULPOSC1_D2, "ulposc1_d2", "ulposc1_ck", 1, 2),
+ FACTOR(CLK_TOP_ULPOSC1_D4, "ulposc1_d4", "ulposc1_ck", 1, 4),
+ FACTOR(CLK_TOP_ULPOSC1_D8, "ulposc1_d8", "ulposc1_ck", 1, 8),
+ FACTOR(CLK_TOP_ULPOSC1_D16, "ulposc1_d16", "ulposc1_ck", 1, 16),
+ FACTOR(CLK_TOP_ULPOSC1_D32, "ulposc1_d32", "ulposc1_ck", 1, 32),
+ FACTOR(CLK_TOP_F_F26M, "f_f26m_ck", "clk_26m_ck", 1, 1),
+ FACTOR(CLK_TOP_AXI, "axi_ck", "axi_sel", 1, 1),
+ FACTOR(CLK_TOP_MM, "mm_ck", "mm_sel", 1, 1),
+ FACTOR(CLK_TOP_SCP, "scp_ck", "scp_sel", 1, 1),
+ FACTOR(CLK_TOP_MFG, "mfg_ck", "mfg_sel", 1, 1),
+ FACTOR(CLK_TOP_F_FUART, "f_fuart_ck", "uart_sel", 1, 1),
+ FACTOR(CLK_TOP_SPI, "spi_ck", "spi_sel", 1, 1),
+ FACTOR(CLK_TOP_MSDC50_0, "msdc50_0_ck", "msdc50_0_sel", 1, 1),
+ FACTOR(CLK_TOP_MSDC30_1, "msdc30_1_ck", "msdc30_1_sel", 1, 1),
+ FACTOR(CLK_TOP_AUDIO, "audio_ck", "audio_sel", 1, 1),
+ FACTOR(CLK_TOP_AUD_1, "aud_1_ck", "aud_1_sel", 1, 1),
+ FACTOR(CLK_TOP_AUD_ENGEN1, "aud_engen1_ck", "aud_engen1_sel", 1, 1),
+ FACTOR(CLK_TOP_F_FDISP_PWM, "f_fdisp_pwm_ck", "disp_pwm_sel", 1, 1),
+ FACTOR(CLK_TOP_SSPM, "sspm_ck", "sspm_sel", 1, 1),
+ FACTOR(CLK_TOP_DXCC, "dxcc_ck", "dxcc_sel", 1, 1),
+ FACTOR(CLK_TOP_I2C, "i2c_ck", "i2c_sel", 1, 1),
+ FACTOR(CLK_TOP_F_FPWM, "f_fpwm_ck", "pwm_sel", 1, 1),
+ FACTOR(CLK_TOP_F_FSENINF, "f_fseninf_ck", "seninf_sel", 1, 1),
+ FACTOR(CLK_TOP_AES_FDE, "aes_fde_ck", "aes_fde_sel", 1, 1),
+ FACTOR(CLK_TOP_F_BIST2FPC, "f_bist2fpc_ck", "univpll2_d2", 1, 1),
+ FACTOR(CLK_TOP_ARMPLL_DIVIDER_PLL0, "arm_div_pll0", "syspll_d2", 1, 1),
+ FACTOR(CLK_TOP_ARMPLL_DIVIDER_PLL1, "arm_div_pll1", "syspll_ck", 1, 1),
+ FACTOR(CLK_TOP_ARMPLL_DIVIDER_PLL2, "arm_div_pll2", "univpll_d2", 1, 1),
+ FACTOR(CLK_TOP_DA_USB20_48M_DIV,
+ "usb20_48m_div", "usb20_192m_d4", 1, 1),
+ FACTOR(CLK_TOP_DA_UNIV_48M_DIV, "univ_48m_div", "usb20_192m_d4", 1, 1),
+};
+
+static const char * const axi_parents[] = {
+ "clk26m",
+ "syspll_d7",
+ "syspll1_d4",
+ "syspll3_d2"
+};
+
+static const char * const mem_parents[] = {
+ "clk26m",
+ "dmpll_ck",
+ "apll1_ck"
+};
+
+static const char * const mm_parents[] = {
+ "clk26m",
+ "mmpll_ck",
+ "syspll1_d2",
+ "syspll_d5",
+ "syspll1_d4",
+ "univpll_d5",
+ "univpll1_d2",
+ "mmpll_d2"
+};
+
+static const char * const scp_parents[] = {
+ "clk26m",
+ "syspll4_d2",
+ "univpll2_d2",
+ "syspll1_d2",
+ "univpll1_d2",
+ "syspll_d3",
+ "univpll_d3"
+};
+
+static const char * const mfg_parents[] = {
+ "clk26m",
+ "mfgpll_ck",
+ "syspll_d3",
+ "univpll_d3"
+};
+
+static const char * const atb_parents[] = {
+ "clk26m",
+ "syspll1_d4",
+ "syspll1_d2"
+};
+
+static const char * const camtg_parents[] = {
+ "clk26m",
+ "usb20_192m_d8",
+ "univpll2_d8",
+ "usb20_192m_d4",
+ "univpll2_d32",
+ "usb20_192m_d16",
+ "usb20_192m_d32"
+};
+
+static const char * const uart_parents[] = {
+ "clk26m",
+ "univpll2_d8"
+};
+
+static const char * const spi_parents[] = {
+ "clk26m",
+ "syspll3_d2",
+ "syspll4_d2",
+ "syspll2_d4"
+};
+
+static const char * const msdc5hclk_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "univpll1_d4",
+ "syspll2_d2"
+};
+
+static const char * const msdc50_0_parents[] = {
+ "clk26m",
+ "msdcpll_ck",
+ "syspll2_d2",
+ "syspll4_d2",
+ "univpll1_d2",
+ "syspll1_d2",
+ "univpll_d5",
+ "univpll1_d4"
+};
+
+static const char * const msdc30_1_parents[] = {
+ "clk26m",
+ "msdcpll_d2",
+ "univpll2_d2",
+ "syspll2_d2",
+ "syspll1_d4",
+ "univpll1_d4",
+ "usb20_192m_d4",
+ "syspll2_d4"
+};
+
+static const char * const audio_parents[] = {
+ "clk26m",
+ "syspll3_d4",
+ "syspll4_d4",
+ "syspll1_d16"
+};
+
+static const char * const aud_intbus_parents[] = {
+ "clk26m",
+ "syspll1_d4",
+ "syspll4_d2"
+};
+
+static const char * const aud_1_parents[] = {
+ "clk26m",
+ "apll1_ck"
+};
+
+static const char * const aud_engen1_parents[] = {
+ "clk26m",
+ "apll1_d2",
+ "apll1_d4",
+ "apll1_d8"
+};
+
+static const char * const disp_pwm_parents[] = {
+ "clk26m",
+ "univpll2_d4",
+ "ulposc1_d2",
+ "ulposc1_d8"
+};
+
+static const char * const sspm_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "syspll_d3"
+};
+
+static const char * const dxcc_parents[] = {
+ "clk26m",
+ "syspll1_d2",
+ "syspll1_d4",
+ "syspll1_d8"
+};
+
+static const char * const usb_top_parents[] = {
+ "clk26m",
+ "univpll3_d4"
+};
+
+static const char * const spm_parents[] = {
+ "clk26m",
+ "syspll1_d8"
+};
+
+static const char * const i2c_parents[] = {
+ "clk26m",
+ "univpll3_d4",
+ "univpll3_d2",
+ "syspll1_d8",
+ "syspll2_d8"
+};
+
+static const char * const pwm_parents[] = {
+ "clk26m",
+ "univpll3_d4",
+ "syspll1_d8"
+};
+
+static const char * const seninf_parents[] = {
+ "clk26m",
+ "univpll1_d4",
+ "univpll1_d2",
+ "univpll2_d2"
+};
+
+static const char * const aes_fde_parents[] = {
+ "clk26m",
+ "msdcpll_ck",
+ "univpll_d3",
+ "univpll2_d2",
+ "univpll1_d2",
+ "syspll1_d2"
+};
+
+static const char * const ulposc_parents[] = {
+ "clk26m",
+ "ulposc1_d4",
+ "ulposc1_d8",
+ "ulposc1_d16",
+ "ulposc1_d32"
+};
+
+static const char * const camtm_parents[] = {
+ "clk26m",
+ "univpll1_d4",
+ "univpll1_d2",
+ "univpll2_d2"
+};
+
+#define INVALID_UPDATE_REG 0xFFFFFFFF
+#define INVALID_UPDATE_SHIFT -1
+#define INVALID_MUX_GATE -1
+
+static const struct mtk_mux top_muxes[] = {
+ /* CLK_CFG_0 */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_AXI_SEL, "axi_sel", axi_parents,
+ CLK_CFG_0, CLK_CFG_0_SET, CLK_CFG_0_CLR,
+ 0, 2, 7, CLK_CFG_UPDATE, 0, CLK_IS_CRITICAL),
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MEM_SEL, "mem_sel", mem_parents,
+ CLK_CFG_0, CLK_CFG_0_SET, CLK_CFG_0_CLR,
+ 8, 2, 15, CLK_CFG_UPDATE, 1, CLK_IS_CRITICAL),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MM_SEL, "mm_sel", mm_parents, CLK_CFG_0,
+ CLK_CFG_0_SET, CLK_CFG_0_CLR, 16, 3, 23,
+ CLK_CFG_UPDATE, 2),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SCP_SEL, "scp_sel", scp_parents, CLK_CFG_0,
+ CLK_CFG_0_SET, CLK_CFG_0_CLR, 24, 3, 31,
+ CLK_CFG_UPDATE, 3),
+ /* CLK_CFG_1 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, CLK_CFG_1,
+ CLK_CFG_1_SET, CLK_CFG_1_CLR, 0, 2, 7,
+ CLK_CFG_UPDATE, 4),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, CLK_CFG_1,
+ CLK_CFG_1_SET, CLK_CFG_1_CLR, 8, 2, 15,
+ CLK_CFG_UPDATE, 5),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG_SEL, "camtg_sel",
+ camtg_parents, CLK_CFG_1, CLK_CFG_1_SET,
+ CLK_CFG_1_CLR, 16, 3, 23, CLK_CFG_UPDATE, 6),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG1_SEL, "camtg1_sel", camtg_parents,
+ CLK_CFG_1, CLK_CFG_1_SET, CLK_CFG_1_CLR,
+ 24, 3, 31, CLK_CFG_UPDATE, 7),
+ /* CLK_CFG_2 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG2_SEL, "camtg2_sel",
+ camtg_parents, CLK_CFG_2, CLK_CFG_2_SET,
+ CLK_CFG_2_CLR, 0, 3, 7, CLK_CFG_UPDATE, 8),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG3_SEL, "camtg3_sel", camtg_parents,
+ CLK_CFG_2, CLK_CFG_2_SET, CLK_CFG_2_CLR,
+ 8, 3, 15, CLK_CFG_UPDATE, 9),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_UART_SEL, "uart_sel", uart_parents,
+ CLK_CFG_2, CLK_CFG_2_SET, CLK_CFG_2_CLR, 16, 1, 23,
+ CLK_CFG_UPDATE, 10),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, CLK_CFG_2,
+ CLK_CFG_2_SET, CLK_CFG_2_CLR, 24, 2, 31,
+ CLK_CFG_UPDATE, 11),
+ /* CLK_CFG_3 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0_HCLK_SEL, "msdc5hclk",
+ msdc5hclk_parents, CLK_CFG_3, CLK_CFG_3_SET,
+ CLK_CFG_3_CLR, 0, 2, 7, CLK_CFG_UPDATE, 12),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel",
+ msdc50_0_parents, CLK_CFG_3, CLK_CFG_3_SET,
+ CLK_CFG_3_CLR, 8, 3, 15, CLK_CFG_UPDATE, 13),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel",
+ msdc30_1_parents, CLK_CFG_3, CLK_CFG_3_SET,
+ CLK_CFG_3_CLR, 16, 3, 23, CLK_CFG_UPDATE, 14),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents,
+ CLK_CFG_3, CLK_CFG_3_SET, CLK_CFG_3_CLR,
+ 24, 2, 31, CLK_CFG_UPDATE, 15),
+ /* CLK_CFG_4 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel",
+ aud_intbus_parents, CLK_CFG_4, CLK_CFG_4_SET,
+ CLK_CFG_4_CLR, 0, 2, 7, CLK_CFG_UPDATE, 16),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents,
+ CLK_CFG_4, CLK_CFG_4_SET, CLK_CFG_4_CLR,
+ 8, 1, 15, CLK_CFG_UPDATE, 17),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_ENGEN1_SEL, "aud_engen1_sel",
+ aud_engen1_parents, CLK_CFG_4, CLK_CFG_4_SET,
+ CLK_CFG_4_CLR, 16, 2, 23, CLK_CFG_UPDATE, 18),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DISP_PWM_SEL, "disp_pwm_sel",
+ disp_pwm_parents, CLK_CFG_4, CLK_CFG_4_SET,
+ CLK_CFG_4_CLR, 24, 2, 31, CLK_CFG_UPDATE, 19),
+ /* CLK_CFG_5 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SSPM_SEL, "sspm_sel", sspm_parents,
+ CLK_CFG_5, CLK_CFG_5_SET, CLK_CFG_5_CLR, 0, 2, 7,
+ CLK_CFG_UPDATE, 20),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_DXCC_SEL, "dxcc_sel", dxcc_parents,
+ CLK_CFG_5, CLK_CFG_5_SET, CLK_CFG_5_CLR, 8, 2, 15,
+ CLK_CFG_UPDATE, 21),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_TOP_SEL, "usb_top_sel",
+ usb_top_parents, CLK_CFG_5, CLK_CFG_5_SET,
+ CLK_CFG_5_CLR, 16, 1, 23, CLK_CFG_UPDATE, 22),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SPM_SEL, "spm_sel", spm_parents, CLK_CFG_5,
+ CLK_CFG_5_SET, CLK_CFG_5_CLR, 24, 1, 31,
+ CLK_CFG_UPDATE, 23),
+ /* CLK_CFG_6 */
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_I2C_SEL, "i2c_sel", i2c_parents, CLK_CFG_6,
+ CLK_CFG_6_SET, CLK_CFG_6_CLR, 0, 3, 7, CLK_CFG_UPDATE,
+ 24),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, CLK_CFG_6,
+ CLK_CFG_6_SET, CLK_CFG_6_CLR, 8, 2, 15, CLK_CFG_UPDATE,
+ 25),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_SENINF_SEL, "seninf_sel", seninf_parents,
+ CLK_CFG_6, CLK_CFG_6_SET, CLK_CFG_6_CLR, 16, 2, 23,
+ CLK_CFG_UPDATE, 26),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_AES_FDE_SEL, "aes_fde_sel",
+ aes_fde_parents, CLK_CFG_6, CLK_CFG_6_SET,
+ CLK_CFG_6_CLR, 24, 3, 31, CLK_CFG_UPDATE, 27),
+ /* CLK_CFG_7 */
+ MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_PWRAP_ULPOSC_SEL, "ulposc_sel",
+ ulposc_parents, CLK_CFG_7, CLK_CFG_7_SET,
+ CLK_CFG_7_CLR, 0, 3, 7, CLK_CFG_UPDATE, 28,
+ CLK_IS_CRITICAL),
+ MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTM_SEL, "camtm_sel", camtm_parents,
+ CLK_CFG_7, CLK_CFG_7_SET, CLK_CFG_7_CLR, 8, 2, 15,
+ CLK_CFG_UPDATE, 29),
+};
+
+static const struct mtk_gate_regs top0_cg_regs = {
+ .set_ofs = 0x0,
+ .clr_ofs = 0x0,
+ .sta_ofs = 0x0,
+};
+
+static const struct mtk_gate_regs top1_cg_regs = {
+ .set_ofs = 0x104,
+ .clr_ofs = 0x104,
+ .sta_ofs = 0x104,
+};
+
+static const struct mtk_gate_regs top2_cg_regs = {
+ .set_ofs = 0x320,
+ .clr_ofs = 0x320,
+ .sta_ofs = 0x320,
+};
+
+#define GATE_TOP0(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &top0_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr, \
+ }
+
+#define GATE_TOP1(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &top1_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
+ }
+
+#define GATE_TOP2(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &top2_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr, \
+ }
+
+static const struct mtk_gate top_clks[] = {
+ /* TOP0 */
+ GATE_TOP0(CLK_TOP_MD_32K, "md_32k", "f_frtc_ck", 8),
+ GATE_TOP0(CLK_TOP_MD_26M, "md_26m", "f_f26m_ck", 9),
+ GATE_TOP0(CLK_TOP_MD2_32K, "md2_32k", "f_frtc_ck", 10),
+ GATE_TOP0(CLK_TOP_MD2_26M, "md2_26m", "f_f26m_ck", 11),
+ /* TOP1 */
+ GATE_TOP1(CLK_TOP_ARMPLL_DIVIDER_PLL0_EN,
+ "arm_div_pll0_en", "arm_div_pll0", 3),
+ GATE_TOP1(CLK_TOP_ARMPLL_DIVIDER_PLL1_EN,
+ "arm_div_pll1_en", "arm_div_pll1", 4),
+ GATE_TOP1(CLK_TOP_ARMPLL_DIVIDER_PLL2_EN,
+ "arm_div_pll2_en", "arm_div_pll2", 5),
+ GATE_TOP1(CLK_TOP_FMEM_OCC_DRC_EN, "drc_en", "univpll2_d2", 6),
+ GATE_TOP1(CLK_TOP_USB20_48M_EN, "usb20_48m_en", "usb20_48m_div", 8),
+ GATE_TOP1(CLK_TOP_UNIVPLL_48M_EN, "univpll_48m_en", "univ_48m_div", 9),
+ GATE_TOP1(CLK_TOP_F_UFS_MP_SAP_CFG_EN, "ufs_sap", "f_f26m_ck", 12),
+ GATE_TOP1(CLK_TOP_F_BIST2FPC_EN, "bist2fpc", "f_bist2fpc_ck", 16),
+ /* TOP2 */
+ GATE_TOP2(CLK_TOP_APLL12_DIV0, "apll12_div0", "aud_1_ck", 2),
+ GATE_TOP2(CLK_TOP_APLL12_DIV1, "apll12_div1", "aud_1_ck", 3),
+ GATE_TOP2(CLK_TOP_APLL12_DIV2, "apll12_div2", "aud_1_ck", 4),
+ GATE_TOP2(CLK_TOP_APLL12_DIV3, "apll12_div3", "aud_1_ck", 5),
+};
+
+static const struct mtk_gate_regs ifr0_cg_regs = {
+ .set_ofs = 0x200,
+ .clr_ofs = 0x200,
+ .sta_ofs = 0x200,
+};
+
+static const struct mtk_gate_regs ifr1_cg_regs = {
+ .set_ofs = 0x74,
+ .clr_ofs = 0x74,
+ .sta_ofs = 0x74,
+};
+
+static const struct mtk_gate_regs ifr2_cg_regs = {
+ .set_ofs = 0x80,
+ .clr_ofs = 0x84,
+ .sta_ofs = 0x90,
+};
+
+static const struct mtk_gate_regs ifr3_cg_regs = {
+ .set_ofs = 0x88,
+ .clr_ofs = 0x8c,
+ .sta_ofs = 0x94,
+};
+
+static const struct mtk_gate_regs ifr4_cg_regs = {
+ .set_ofs = 0xa4,
+ .clr_ofs = 0xa8,
+ .sta_ofs = 0xac,
+};
+
+static const struct mtk_gate_regs ifr5_cg_regs = {
+ .set_ofs = 0xc0,
+ .clr_ofs = 0xc4,
+ .sta_ofs = 0xc8,
+};
+
+#define GATE_IFR0(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ifr0_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
+ }
+
+#define GATE_IFR1(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ifr1_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr, \
+ }
+
+#define GATE_IFR2(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ifr2_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+#define GATE_IFR3(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ifr3_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+#define GATE_IFR4(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ifr4_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+#define GATE_IFR5(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &ifr5_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_setclr, \
+ }
+
+static const struct mtk_gate ifr_clks[] = {
+ /* INFRA_TOPAXI */
+ /* INFRA PERI */
+ /* INFRA mode 0 */
+ GATE_IFR2(CLK_IFR_ICUSB, "ifr_icusb", "axi_ck", 8),
+ GATE_IFR2(CLK_IFR_GCE, "ifr_gce", "axi_ck", 9),
+ GATE_IFR2(CLK_IFR_THERM, "ifr_therm", "axi_ck", 10),
+ GATE_IFR2(CLK_IFR_I2C_AP, "ifr_i2c_ap", "i2c_ck", 11),
+ GATE_IFR2(CLK_IFR_I2C_CCU, "ifr_i2c_ccu", "i2c_ck", 12),
+ GATE_IFR2(CLK_IFR_I2C_SSPM, "ifr_i2c_sspm", "i2c_ck", 13),
+ GATE_IFR2(CLK_IFR_I2C_RSV, "ifr_i2c_rsv", "i2c_ck", 14),
+ GATE_IFR2(CLK_IFR_PWM_HCLK, "ifr_pwm_hclk", "axi_ck", 15),
+ GATE_IFR2(CLK_IFR_PWM1, "ifr_pwm1", "f_fpwm_ck", 16),
+ GATE_IFR2(CLK_IFR_PWM2, "ifr_pwm2", "f_fpwm_ck", 17),
+ GATE_IFR2(CLK_IFR_PWM3, "ifr_pwm3", "f_fpwm_ck", 18),
+ GATE_IFR2(CLK_IFR_PWM4, "ifr_pwm4", "f_fpwm_ck", 19),
+ GATE_IFR2(CLK_IFR_PWM5, "ifr_pwm5", "f_fpwm_ck", 20),
+ GATE_IFR2(CLK_IFR_PWM, "ifr_pwm", "f_fpwm_ck", 21),
+ GATE_IFR2(CLK_IFR_UART0, "ifr_uart0", "f_fuart_ck", 22),
+ GATE_IFR2(CLK_IFR_UART1, "ifr_uart1", "f_fuart_ck", 23),
+ GATE_IFR2(CLK_IFR_GCE_26M, "ifr_gce_26m", "f_f26m_ck", 27),
+ GATE_IFR2(CLK_IFR_CQ_DMA_FPC, "ifr_dma", "axi_ck", 28),
+ GATE_IFR2(CLK_IFR_BTIF, "ifr_btif", "axi_ck", 31),
+ /* INFRA mode 1 */
+ GATE_IFR3(CLK_IFR_SPI0, "ifr_spi0", "spi_ck", 1),
+ GATE_IFR3(CLK_IFR_MSDC0, "ifr_msdc0", "msdc5hclk", 2),
+ GATE_IFR3(CLK_IFR_MSDC1, "ifr_msdc1", "axi_ck", 4),
+ GATE_IFR3(CLK_IFR_TRNG, "ifr_trng", "axi_ck", 9),
+ GATE_IFR3(CLK_IFR_AUXADC, "ifr_auxadc", "f_f26m_ck", 10),
+ GATE_IFR3(CLK_IFR_CCIF1_AP, "ifr_ccif1_ap", "axi_ck", 12),
+ GATE_IFR3(CLK_IFR_CCIF1_MD, "ifr_ccif1_md", "axi_ck", 13),
+ GATE_IFR3(CLK_IFR_AUXADC_MD, "ifr_auxadc_md", "f_f26m_ck", 14),
+ GATE_IFR3(CLK_IFR_AP_DMA, "ifr_ap_dma", "axi_ck", 18),
+ GATE_IFR3(CLK_IFR_DEVICE_APC, "ifr_dapc", "axi_ck", 20),
+ GATE_IFR3(CLK_IFR_CCIF_AP, "ifr_ccif_ap", "axi_ck", 23),
+ GATE_IFR3(CLK_IFR_AUDIO, "ifr_audio", "axi_ck", 25),
+ GATE_IFR3(CLK_IFR_CCIF_MD, "ifr_ccif_md", "axi_ck", 26),
+ /* INFRA mode 2 */
+ GATE_IFR4(CLK_IFR_RG_PWM_FBCLK6, "ifr_pwmfb", "f_f26m_ck", 0),
+ GATE_IFR4(CLK_IFR_DISP_PWM, "ifr_disp_pwm", "f_fdisp_pwm_ck", 2),
+ GATE_IFR4(CLK_IFR_CLDMA_BCLK, "ifr_cldmabclk", "axi_ck", 3),
+ GATE_IFR4(CLK_IFR_AUDIO_26M_BCLK, "ifr_audio26m", "f_f26m_ck", 4),
+ GATE_IFR4(CLK_IFR_SPI1, "ifr_spi1", "spi_ck", 6),
+ GATE_IFR4(CLK_IFR_I2C4, "ifr_i2c4", "i2c_ck", 7),
+ GATE_IFR4(CLK_IFR_SPI2, "ifr_spi2", "spi_ck", 9),
+ GATE_IFR4(CLK_IFR_SPI3, "ifr_spi3", "spi_ck", 10),
+ GATE_IFR4(CLK_IFR_I2C5, "ifr_i2c5", "i2c_ck", 18),
+ GATE_IFR4(CLK_IFR_I2C5_ARBITER, "ifr_i2c5a", "i2c_ck", 19),
+ GATE_IFR4(CLK_IFR_I2C5_IMM, "ifr_i2c5_imm", "i2c_ck", 20),
+ GATE_IFR4(CLK_IFR_I2C1_ARBITER, "ifr_i2c1a", "i2c_ck", 21),
+ GATE_IFR4(CLK_IFR_I2C1_IMM, "ifr_i2c1_imm", "i2c_ck", 22),
+ GATE_IFR4(CLK_IFR_I2C2_ARBITER, "ifr_i2c2a", "i2c_ck", 23),
+ GATE_IFR4(CLK_IFR_I2C2_IMM, "ifr_i2c2_imm", "i2c_ck", 24),
+ GATE_IFR4(CLK_IFR_SPI4, "ifr_spi4", "spi_ck", 25),
+ GATE_IFR4(CLK_IFR_SPI5, "ifr_spi5", "spi_ck", 26),
+ GATE_IFR4(CLK_IFR_CQ_DMA, "ifr_cq_dma", "axi_ck", 27),
+ GATE_IFR4(CLK_IFR_FAES_FDE, "ifr_faes_fde_ck", "aes_fde_ck", 29),
+ /* INFRA mode 3 */
+ GATE_IFR5(CLK_IFR_MSDC0_SELF, "ifr_msdc0sf", "msdc50_0_ck", 0),
+ GATE_IFR5(CLK_IFR_MSDC1_SELF, "ifr_msdc1sf", "msdc50_0_ck", 1),
+ GATE_IFR5(CLK_IFR_I2C6, "ifr_i2c6", "i2c_ck", 6),
+ GATE_IFR5(CLK_IFR_AP_MSDC0, "ifr_ap_msdc0", "msdc50_0_ck", 7),
+ GATE_IFR5(CLK_IFR_MD_MSDC0, "ifr_md_msdc0", "msdc50_0_ck", 8),
+ GATE_IFR5(CLK_IFR_MSDC0_SRC, "ifr_msdc0_clk", "msdc50_0_ck", 9),
+ GATE_IFR5(CLK_IFR_MSDC1_SRC, "ifr_msdc1_clk", "msdc30_1_ck", 10),
+ GATE_IFR5(CLK_IFR_MCU_PM_BCLK, "ifr_mcu_pm_bclk", "axi_ck", 17),
+ GATE_IFR5(CLK_IFR_CCIF2_AP, "ifr_ccif2_ap", "axi_ck", 18),
+ GATE_IFR5(CLK_IFR_CCIF2_MD, "ifr_ccif2_md", "axi_ck", 19),
+ GATE_IFR5(CLK_IFR_CCIF3_AP, "ifr_ccif3_ap", "axi_ck", 20),
+ GATE_IFR5(CLK_IFR_CCIF3_MD, "ifr_ccif3_md", "axi_ck", 21),
+};
+
+/* additional CCF control for mipi26M race condition(disp/camera) */
+static const struct mtk_gate_regs apmixed_cg_regs = {
+ .set_ofs = 0x14,
+ .clr_ofs = 0x14,
+ .sta_ofs = 0x14,
+};
+
+#define GATE_APMIXED(_id, _name, _parent, _shift) { \
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .regs = &apmixed_cg_regs, \
+ .shift = _shift, \
+ .ops = &mtk_clk_gate_ops_no_setclr_inv, \
+ }
+
+static const struct mtk_gate apmixed_clks[] = {
+ /* AUDIO0 */
+ GATE_APMIXED(CLK_APMIXED_SSUSB26M, "apmixed_ssusb26m", "f_f26m_ck",
+ 4),
+ GATE_APMIXED(CLK_APMIXED_APPLL26M, "apmixed_appll26m", "f_f26m_ck",
+ 5),
+ GATE_APMIXED(CLK_APMIXED_MIPIC0_26M, "apmixed_mipic026m", "f_f26m_ck",
+ 6),
+ GATE_APMIXED(CLK_APMIXED_MDPLLGP26M, "apmixed_mdpll26m", "f_f26m_ck",
+ 7),
+ GATE_APMIXED(CLK_APMIXED_MMSYS_F26M, "apmixed_mmsys26m", "f_f26m_ck",
+ 8),
+ GATE_APMIXED(CLK_APMIXED_UFS26M, "apmixed_ufs26m", "f_f26m_ck",
+ 9),
+ GATE_APMIXED(CLK_APMIXED_MIPIC1_26M, "apmixed_mipic126m", "f_f26m_ck",
+ 11),
+ GATE_APMIXED(CLK_APMIXED_MEMPLL26M, "apmixed_mempll26m", "f_f26m_ck",
+ 13),
+ GATE_APMIXED(CLK_APMIXED_CLKSQ_LVPLL_26M, "apmixed_lvpll26m",
+ "f_f26m_ck", 14),
+ GATE_APMIXED(CLK_APMIXED_MIPID0_26M, "apmixed_mipid026m", "f_f26m_ck",
+ 16),
+};
+
+#define MT6765_PLL_FMAX (3800UL * MHZ)
+#define MT6765_PLL_FMIN (1500UL * MHZ)
+
+#define CON0_MT6765_RST_BAR BIT(23)
+
+#define PLL_INFO_NULL (0xFF)
+
+#define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pcwibits, _pd_reg, _pd_shift, _tuner_reg, _tuner_en_reg,\
+ _tuner_en_bit, _pcw_reg, _pcw_shift, _div_table) {\
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .pwr_reg = _pwr_reg, \
+ .en_mask = _en_mask, \
+ .flags = _flags, \
+ .rst_bar_mask = CON0_MT6765_RST_BAR, \
+ .fmax = MT6765_PLL_FMAX, \
+ .fmin = MT6765_PLL_FMIN, \
+ .pcwbits = _pcwbits, \
+ .pcwibits = _pcwibits, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .tuner_reg = _tuner_reg, \
+ .tuner_en_reg = _tuner_en_reg, \
+ .tuner_en_bit = _tuner_en_bit, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .div_table = _div_table, \
+ }
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \
+ _pcwibits, _pd_reg, _pd_shift, _tuner_reg, \
+ _tuner_en_reg, _tuner_en_bit, _pcw_reg, \
+ _pcw_shift) \
+ PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \
+ _pcwbits, _pcwibits, _pd_reg, _pd_shift, \
+ _tuner_reg, _tuner_en_reg, _tuner_en_bit, \
+ _pcw_reg, _pcw_shift, NULL) \
+
+static const struct mtk_pll_data plls[] = {
+ PLL(CLK_APMIXED_ARMPLL_L, "armpll_l", 0x021C, 0x0228, BIT(0),
+ PLL_AO, 22, 8, 0x0220, 24, 0, 0, 0, 0x0220, 0),
+ PLL(CLK_APMIXED_ARMPLL, "armpll", 0x020C, 0x0218, BIT(0),
+ PLL_AO, 22, 8, 0x0210, 24, 0, 0, 0, 0x0210, 0),
+ PLL(CLK_APMIXED_CCIPLL, "ccipll", 0x022C, 0x0238, BIT(0),
+ PLL_AO, 22, 8, 0x0230, 24, 0, 0, 0, 0x0230, 0),
+ PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x023C, 0x0248, BIT(0),
+ (HAVE_RST_BAR | PLL_AO), 22, 8, 0x0240, 24, 0, 0, 0, 0x0240,
+ 0),
+ PLL(CLK_APMIXED_MFGPLL, "mfgpll", 0x024C, 0x0258, BIT(0),
+ 0, 22, 8, 0x0250, 24, 0, 0, 0, 0x0250, 0),
+ PLL(CLK_APMIXED_MMPLL, "mmpll", 0x025C, 0x0268, BIT(0),
+ 0, 22, 8, 0x0260, 24, 0, 0, 0, 0x0260, 0),
+ PLL(CLK_APMIXED_UNIV2PLL, "univ2pll", 0x026C, 0x0278, BIT(0),
+ HAVE_RST_BAR, 22, 8, 0x0270, 24, 0, 0, 0, 0x0270, 0),
+ PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x027C, 0x0288, BIT(0),
+ 0, 22, 8, 0x0280, 24, 0, 0, 0, 0x0280, 0),
+ PLL(CLK_APMIXED_APLL1, "apll1", 0x028C, 0x029C, BIT(0),
+ 0, 32, 8, 0x0290, 24, 0x0040, 0x000C, 0, 0x0294, 0),
+ PLL(CLK_APMIXED_MPLL, "mpll", 0x02A0, 0x02AC, BIT(0),
+ PLL_AO, 22, 8, 0x02A4, 24, 0, 0, 0, 0x02A4, 0),
+};
+
+static int clk_mt6765_apmixed_probe(struct platform_device *pdev)
+{
+ struct clk_onecell_data *clk_data;
+ int r;
+ struct device_node *node = pdev->dev.of_node;
+ void __iomem *base;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ pr_err("%s(): ioremap failed\n", __func__);
+ return PTR_ERR(base);
+ }
+
+ clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
+
+ mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
+
+ mtk_clk_register_gates(node, apmixed_clks,
+ ARRAY_SIZE(apmixed_clks), clk_data);
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ apmixed_base = base;
+ /* MPLL, CCIPLL, MAINPLL set HW mode, TDCLKSQ, CLKSQ1 */
+ writel(readl(AP_PLL_CON3) & 0xFFFFFFE1, AP_PLL_CON3);
+ writel(readl(PLLON_CON0) & 0x01041041, PLLON_CON0);
+ writel(readl(PLLON_CON1) & 0x01041041, PLLON_CON1);
+
+ return r;
+}
+
+static int clk_mt6765_top_probe(struct platform_device *pdev)
+{
+ int r;
+ struct device_node *node = pdev->dev.of_node;
+ void __iomem *base;
+ struct clk_onecell_data *clk_data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ pr_err("%s(): ioremap failed\n", __func__);
+ return PTR_ERR(base);
+ }
+
+ clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
+
+ mtk_clk_register_fixed_clks(fixed_clks, ARRAY_SIZE(fixed_clks),
+ clk_data);
+ mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs),
+ clk_data);
+ mtk_clk_register_muxes(top_muxes, ARRAY_SIZE(top_muxes), node,
+ &mt6765_clk_lock, clk_data);
+ mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks),
+ clk_data);
+
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ cksys_base = base;
+ /* [4]:no need */
+ writel(readl(CLK_SCP_CFG_0) | 0x3EF, CLK_SCP_CFG_0);
+ /*[1,2,3,8]: no need*/
+ writel(readl(CLK_SCP_CFG_1) | 0x1, CLK_SCP_CFG_1);
+
+ return r;
+}
+
+static int clk_mt6765_ifr_probe(struct platform_device *pdev)
+{
+ struct clk_onecell_data *clk_data;
+ int r;
+ struct device_node *node = pdev->dev.of_node;
+ void __iomem *base;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ pr_err("%s(): ioremap failed\n", __func__);
+ return PTR_ERR(base);
+ }
+
+ clk_data = mtk_alloc_clk_data(CLK_IFR_NR_CLK);
+
+ mtk_clk_register_gates(node, ifr_clks, ARRAY_SIZE(ifr_clks),
+ clk_data);
+ r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+ if (r)
+ pr_err("%s(): could not register clock provider: %d\n",
+ __func__, r);
+
+ return r;
+}
+
+static const struct of_device_id of_match_clk_mt6765[] = {
+ {
+ .compatible = "mediatek,mt6765-apmixedsys",
+ .data = clk_mt6765_apmixed_probe,
+ }, {
+ .compatible = "mediatek,mt6765-topckgen",
+ .data = clk_mt6765_top_probe,
+ }, {
+ .compatible = "mediatek,mt6765-infracfg",
+ .data = clk_mt6765_ifr_probe,
+ }, {
+ /* sentinel */
+ }
+};
+
+static int clk_mt6765_probe(struct platform_device *pdev)
+{
+ int (*clk_probe)(struct platform_device *d);
+ int r;
+
+ clk_probe = of_device_get_match_data(&pdev->dev);
+ if (!clk_probe)
+ return -EINVAL;
+
+ r = clk_probe(pdev);
+ if (r)
+ dev_err(&pdev->dev,
+ "could not register clock provider: %s: %d\n",
+ pdev->name, r);
+
+ return r;
+}
+
+static struct platform_driver clk_mt6765_drv = {
+ .probe = clk_mt6765_probe,
+ .driver = {
+ .name = "clk-mt6765",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_clk_mt6765,
+ },
+};
+
+static int __init clk_mt6765_init(void)
+{
+ return platform_driver_register(&clk_mt6765_drv);
+}
+
+arch_initcall(clk_mt6765_init);
--
2.18.0
^ permalink raw reply related
* [PATCH v6 4/8] clk: mediatek: add mt6765 clock IDs
From: Macpaul Lin @ 2019-07-12 9:43 UTC (permalink / raw)
To: Rob Herring, Marc Zyngier, Ryder Lee, Stephen Boyd, Sean Wang,
Mars Cheng, Owen Chen, Matthias Brugger, linux-arm-kernel,
linux-mediatek, linux-kernel
Cc: wsd_upstream, CC Hwang, Loda Chou, devicetree, linux-serial,
linux-clk, Macpaul Lin
In-Reply-To: <1562924653-10056-1-git-send-email-macpaul.lin@mediatek.com>
From: Mars Cheng <mars.cheng@mediatek.com>
Add MT6765 clock dt-bindings, include topckgen, apmixedsys,
infracfg, mcucfg and subsystem clocks.
Signed-off-by: Mars Cheng <mars.cheng@mediatek.com>
Signed-off-by: Owen Chen <owen.chen@mediatek.com>
Signed-off-by: Macpaul Lin <macpaul.lin@mediatek.com>
---
include/dt-bindings/clock/mt6765-clk.h | 313 +++++++++++++++++++++++++
1 file changed, 313 insertions(+)
create mode 100644 include/dt-bindings/clock/mt6765-clk.h
diff --git a/include/dt-bindings/clock/mt6765-clk.h b/include/dt-bindings/clock/mt6765-clk.h
new file mode 100644
index 000000000000..eb97e568518e
--- /dev/null
+++ b/include/dt-bindings/clock/mt6765-clk.h
@@ -0,0 +1,313 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _DT_BINDINGS_CLK_MT6765_H
+#define _DT_BINDINGS_CLK_MT6765_H
+
+/* FIX Clks */
+#define CLK_TOP_CLK26M 0
+
+/* APMIXEDSYS */
+#define CLK_APMIXED_ARMPLL_L 0
+#define CLK_APMIXED_ARMPLL 1
+#define CLK_APMIXED_CCIPLL 2
+#define CLK_APMIXED_MAINPLL 3
+#define CLK_APMIXED_MFGPLL 4
+#define CLK_APMIXED_MMPLL 5
+#define CLK_APMIXED_UNIV2PLL 6
+#define CLK_APMIXED_MSDCPLL 7
+#define CLK_APMIXED_APLL1 8
+#define CLK_APMIXED_MPLL 9
+#define CLK_APMIXED_ULPOSC1 10
+#define CLK_APMIXED_ULPOSC2 11
+#define CLK_APMIXED_SSUSB26M 12
+#define CLK_APMIXED_APPLL26M 13
+#define CLK_APMIXED_MIPIC0_26M 14
+#define CLK_APMIXED_MDPLLGP26M 15
+#define CLK_APMIXED_MMSYS_F26M 16
+#define CLK_APMIXED_UFS26M 17
+#define CLK_APMIXED_MIPIC1_26M 18
+#define CLK_APMIXED_MEMPLL26M 19
+#define CLK_APMIXED_CLKSQ_LVPLL_26M 20
+#define CLK_APMIXED_MIPID0_26M 21
+#define CLK_APMIXED_NR_CLK 22
+
+/* TOPCKGEN */
+#define CLK_TOP_SYSPLL 0
+#define CLK_TOP_SYSPLL_D2 1
+#define CLK_TOP_SYSPLL1_D2 2
+#define CLK_TOP_SYSPLL1_D4 3
+#define CLK_TOP_SYSPLL1_D8 4
+#define CLK_TOP_SYSPLL1_D16 5
+#define CLK_TOP_SYSPLL_D3 6
+#define CLK_TOP_SYSPLL2_D2 7
+#define CLK_TOP_SYSPLL2_D4 8
+#define CLK_TOP_SYSPLL2_D8 9
+#define CLK_TOP_SYSPLL_D5 10
+#define CLK_TOP_SYSPLL3_D2 11
+#define CLK_TOP_SYSPLL3_D4 12
+#define CLK_TOP_SYSPLL_D7 13
+#define CLK_TOP_SYSPLL4_D2 14
+#define CLK_TOP_SYSPLL4_D4 15
+#define CLK_TOP_USB20_192M 16
+#define CLK_TOP_USB20_192M_D4 17
+#define CLK_TOP_USB20_192M_D8 18
+#define CLK_TOP_USB20_192M_D16 19
+#define CLK_TOP_USB20_192M_D32 20
+#define CLK_TOP_UNIVPLL 21
+#define CLK_TOP_UNIVPLL_D2 22
+#define CLK_TOP_UNIVPLL1_D2 23
+#define CLK_TOP_UNIVPLL1_D4 24
+#define CLK_TOP_UNIVPLL_D3 25
+#define CLK_TOP_UNIVPLL2_D2 26
+#define CLK_TOP_UNIVPLL2_D4 27
+#define CLK_TOP_UNIVPLL2_D8 28
+#define CLK_TOP_UNIVPLL2_D32 29
+#define CLK_TOP_UNIVPLL_D5 30
+#define CLK_TOP_UNIVPLL3_D2 31
+#define CLK_TOP_UNIVPLL3_D4 32
+#define CLK_TOP_MMPLL 33
+#define CLK_TOP_MMPLL_D2 34
+#define CLK_TOP_MPLL 35
+#define CLK_TOP_DA_MPLL_104M_DIV 36
+#define CLK_TOP_DA_MPLL_52M_DIV 37
+#define CLK_TOP_MFGPLL 38
+#define CLK_TOP_MSDCPLL 39
+#define CLK_TOP_MSDCPLL_D2 40
+#define CLK_TOP_APLL1 41
+#define CLK_TOP_APLL1_D2 42
+#define CLK_TOP_APLL1_D4 43
+#define CLK_TOP_APLL1_D8 44
+#define CLK_TOP_ULPOSC1 45
+#define CLK_TOP_ULPOSC1_D2 46
+#define CLK_TOP_ULPOSC1_D4 47
+#define CLK_TOP_ULPOSC1_D8 48
+#define CLK_TOP_ULPOSC1_D16 49
+#define CLK_TOP_ULPOSC1_D32 50
+#define CLK_TOP_DMPLL 51
+#define CLK_TOP_F_FRTC 52
+#define CLK_TOP_F_F26M 53
+#define CLK_TOP_AXI 54
+#define CLK_TOP_MM 55
+#define CLK_TOP_SCP 56
+#define CLK_TOP_MFG 57
+#define CLK_TOP_F_FUART 58
+#define CLK_TOP_SPI 59
+#define CLK_TOP_MSDC50_0 60
+#define CLK_TOP_MSDC30_1 61
+#define CLK_TOP_AUDIO 62
+#define CLK_TOP_AUD_1 63
+#define CLK_TOP_AUD_ENGEN1 64
+#define CLK_TOP_F_FDISP_PWM 65
+#define CLK_TOP_SSPM 66
+#define CLK_TOP_DXCC 67
+#define CLK_TOP_I2C 68
+#define CLK_TOP_F_FPWM 69
+#define CLK_TOP_F_FSENINF 70
+#define CLK_TOP_AES_FDE 71
+#define CLK_TOP_F_BIST2FPC 72
+#define CLK_TOP_ARMPLL_DIVIDER_PLL0 73
+#define CLK_TOP_ARMPLL_DIVIDER_PLL1 74
+#define CLK_TOP_ARMPLL_DIVIDER_PLL2 75
+#define CLK_TOP_DA_USB20_48M_DIV 76
+#define CLK_TOP_DA_UNIV_48M_DIV 77
+#define CLK_TOP_APLL12_DIV0 78
+#define CLK_TOP_APLL12_DIV1 79
+#define CLK_TOP_APLL12_DIV2 80
+#define CLK_TOP_APLL12_DIV3 81
+#define CLK_TOP_ARMPLL_DIVIDER_PLL0_EN 82
+#define CLK_TOP_ARMPLL_DIVIDER_PLL1_EN 83
+#define CLK_TOP_ARMPLL_DIVIDER_PLL2_EN 84
+#define CLK_TOP_FMEM_OCC_DRC_EN 85
+#define CLK_TOP_USB20_48M_EN 86
+#define CLK_TOP_UNIVPLL_48M_EN 87
+#define CLK_TOP_MPLL_104M_EN 88
+#define CLK_TOP_MPLL_52M_EN 89
+#define CLK_TOP_F_UFS_MP_SAP_CFG_EN 90
+#define CLK_TOP_F_BIST2FPC_EN 91
+#define CLK_TOP_MD_32K 92
+#define CLK_TOP_MD_26M 93
+#define CLK_TOP_MD2_32K 94
+#define CLK_TOP_MD2_26M 95
+#define CLK_TOP_AXI_SEL 96
+#define CLK_TOP_MEM_SEL 97
+#define CLK_TOP_MM_SEL 98
+#define CLK_TOP_SCP_SEL 99
+#define CLK_TOP_MFG_SEL 100
+#define CLK_TOP_ATB_SEL 101
+#define CLK_TOP_CAMTG_SEL 102
+#define CLK_TOP_CAMTG1_SEL 103
+#define CLK_TOP_CAMTG2_SEL 104
+#define CLK_TOP_CAMTG3_SEL 105
+#define CLK_TOP_UART_SEL 106
+#define CLK_TOP_SPI_SEL 107
+#define CLK_TOP_MSDC50_0_HCLK_SEL 108
+#define CLK_TOP_MSDC50_0_SEL 109
+#define CLK_TOP_MSDC30_1_SEL 110
+#define CLK_TOP_AUDIO_SEL 111
+#define CLK_TOP_AUD_INTBUS_SEL 112
+#define CLK_TOP_AUD_1_SEL 113
+#define CLK_TOP_AUD_ENGEN1_SEL 114
+#define CLK_TOP_DISP_PWM_SEL 115
+#define CLK_TOP_SSPM_SEL 116
+#define CLK_TOP_DXCC_SEL 117
+#define CLK_TOP_USB_TOP_SEL 118
+#define CLK_TOP_SPM_SEL 119
+#define CLK_TOP_I2C_SEL 120
+#define CLK_TOP_PWM_SEL 121
+#define CLK_TOP_SENINF_SEL 122
+#define CLK_TOP_AES_FDE_SEL 123
+#define CLK_TOP_PWRAP_ULPOSC_SEL 124
+#define CLK_TOP_CAMTM_SEL 125
+#define CLK_TOP_NR_CLK 126
+
+/* INFRACFG */
+#define CLK_IFR_ICUSB 0
+#define CLK_IFR_GCE 1
+#define CLK_IFR_THERM 2
+#define CLK_IFR_I2C_AP 3
+#define CLK_IFR_I2C_CCU 4
+#define CLK_IFR_I2C_SSPM 5
+#define CLK_IFR_I2C_RSV 6
+#define CLK_IFR_PWM_HCLK 7
+#define CLK_IFR_PWM1 8
+#define CLK_IFR_PWM2 9
+#define CLK_IFR_PWM3 10
+#define CLK_IFR_PWM4 11
+#define CLK_IFR_PWM5 12
+#define CLK_IFR_PWM 13
+#define CLK_IFR_UART0 14
+#define CLK_IFR_UART1 15
+#define CLK_IFR_GCE_26M 16
+#define CLK_IFR_CQ_DMA_FPC 17
+#define CLK_IFR_BTIF 18
+#define CLK_IFR_SPI0 19
+#define CLK_IFR_MSDC0 20
+#define CLK_IFR_MSDC1 21
+#define CLK_IFR_TRNG 22
+#define CLK_IFR_AUXADC 23
+#define CLK_IFR_CCIF1_AP 24
+#define CLK_IFR_CCIF1_MD 25
+#define CLK_IFR_AUXADC_MD 26
+#define CLK_IFR_AP_DMA 27
+#define CLK_IFR_DEVICE_APC 28
+#define CLK_IFR_CCIF_AP 29
+#define CLK_IFR_AUDIO 30
+#define CLK_IFR_CCIF_MD 31
+#define CLK_IFR_RG_PWM_FBCLK6 32
+#define CLK_IFR_DISP_PWM 33
+#define CLK_IFR_CLDMA_BCLK 34
+#define CLK_IFR_AUDIO_26M_BCLK 35
+#define CLK_IFR_SPI1 36
+#define CLK_IFR_I2C4 37
+#define CLK_IFR_SPI2 38
+#define CLK_IFR_SPI3 39
+#define CLK_IFR_I2C5 40
+#define CLK_IFR_I2C5_ARBITER 41
+#define CLK_IFR_I2C5_IMM 42
+#define CLK_IFR_I2C1_ARBITER 43
+#define CLK_IFR_I2C1_IMM 44
+#define CLK_IFR_I2C2_ARBITER 45
+#define CLK_IFR_I2C2_IMM 46
+#define CLK_IFR_SPI4 47
+#define CLK_IFR_SPI5 48
+#define CLK_IFR_CQ_DMA 49
+#define CLK_IFR_FAES_FDE 50
+#define CLK_IFR_MSDC0_SELF 51
+#define CLK_IFR_MSDC1_SELF 52
+#define CLK_IFR_I2C6 53
+#define CLK_IFR_AP_MSDC0 54
+#define CLK_IFR_MD_MSDC0 55
+#define CLK_IFR_MSDC0_SRC 56
+#define CLK_IFR_MSDC1_SRC 57
+#define CLK_IFR_AES_TOP0_BCLK 58
+#define CLK_IFR_MCU_PM_BCLK 59
+#define CLK_IFR_CCIF2_AP 60
+#define CLK_IFR_CCIF2_MD 61
+#define CLK_IFR_CCIF3_AP 62
+#define CLK_IFR_CCIF3_MD 63
+#define CLK_IFR_NR_CLK 64
+
+/* AUDIO */
+#define CLK_AUDIO_AFE 0
+#define CLK_AUDIO_22M 1
+#define CLK_AUDIO_APLL_TUNER 2
+#define CLK_AUDIO_ADC 3
+#define CLK_AUDIO_DAC 4
+#define CLK_AUDIO_DAC_PREDIS 5
+#define CLK_AUDIO_TML 6
+#define CLK_AUDIO_I2S1_BCLK 7
+#define CLK_AUDIO_I2S2_BCLK 8
+#define CLK_AUDIO_I2S3_BCLK 9
+#define CLK_AUDIO_I2S4_BCLK 10
+#define CLK_AUDIO_NR_CLK 11
+
+/* MIPI_RX_ANA_CSI0A */
+
+#define CLK_MIPI0A_CSR_CSI_EN_0A 0
+#define CLK_MIPI0A_NR_CLK 1
+
+/* MMSYS_CONFIG */
+
+#define CLK_MM_MDP_RDMA0 0
+#define CLK_MM_MDP_CCORR0 1
+#define CLK_MM_MDP_RSZ0 2
+#define CLK_MM_MDP_RSZ1 3
+#define CLK_MM_MDP_TDSHP0 4
+#define CLK_MM_MDP_WROT0 5
+#define CLK_MM_MDP_WDMA0 6
+#define CLK_MM_DISP_OVL0 7
+#define CLK_MM_DISP_OVL0_2L 8
+#define CLK_MM_DISP_RSZ0 9
+#define CLK_MM_DISP_RDMA0 10
+#define CLK_MM_DISP_WDMA0 11
+#define CLK_MM_DISP_COLOR0 12
+#define CLK_MM_DISP_CCORR0 13
+#define CLK_MM_DISP_AAL0 14
+#define CLK_MM_DISP_GAMMA0 15
+#define CLK_MM_DISP_DITHER0 16
+#define CLK_MM_DSI0 17
+#define CLK_MM_FAKE_ENG 18
+#define CLK_MM_SMI_COMMON 19
+#define CLK_MM_SMI_LARB0 20
+#define CLK_MM_SMI_COMM0 21
+#define CLK_MM_SMI_COMM1 22
+#define CLK_MM_CAM_MDP 23
+#define CLK_MM_SMI_IMG 24
+#define CLK_MM_SMI_CAM 25
+#define CLK_MM_IMG_DL_RELAY 26
+#define CLK_MM_IMG_DL_ASYNC_TOP 27
+#define CLK_MM_DIG_DSI 28
+#define CLK_MM_F26M_HRTWT 29
+#define CLK_MM_NR_CLK 30
+
+/* IMGSYS */
+
+#define CLK_IMG_LARB2 0
+#define CLK_IMG_DIP 1
+#define CLK_IMG_FDVT 2
+#define CLK_IMG_DPE 3
+#define CLK_IMG_RSC 4
+#define CLK_IMG_NR_CLK 5
+
+/* VENCSYS */
+
+#define CLK_VENC_SET0_LARB 0
+#define CLK_VENC_SET1_VENC 1
+#define CLK_VENC_SET2_JPGENC 2
+#define CLK_VENC_SET3_VDEC 3
+#define CLK_VENC_NR_CLK 4
+
+/* CAMSYS */
+
+#define CLK_CAM_LARB3 0
+#define CLK_CAM_DFP_VAD 1
+#define CLK_CAM 2
+#define CLK_CAMTG 3
+#define CLK_CAM_SENINF 4
+#define CLK_CAMSV0 5
+#define CLK_CAMSV1 6
+#define CLK_CAMSV2 7
+#define CLK_CAM_CCU 8
+#define CLK_CAM_NR_CLK 9
+
+#endif /* _DT_BINDINGS_CLK_MT6765_H */
--
2.18.0
^ permalink raw reply related
* [PATCH v6 3/8] dt-bindings: mediatek: add MT6765 power dt-bindings
From: Macpaul Lin @ 2019-07-12 9:43 UTC (permalink / raw)
To: Rob Herring, Marc Zyngier, Ryder Lee, Stephen Boyd, Sean Wang,
Mars Cheng, Owen Chen, Matthias Brugger, linux-arm-kernel,
linux-mediatek, linux-kernel
Cc: wsd_upstream, CC Hwang, Loda Chou, devicetree, linux-serial,
linux-clk, Macpaul Lin
In-Reply-To: <1562924653-10056-1-git-send-email-macpaul.lin@mediatek.com>
From: Mars Cheng <mars.cheng@mediatek.com>
This adds power dt-bindings for MT6765
Signed-off-by: Mars Cheng <mars.cheng@mediatek.com>
Signed-off-by: Owen Chen <owen.chen@mediatek.com>
Signed-off-by: Macpaul Lin <macpaul.lin@mediatek.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
.../devicetree/bindings/soc/mediatek/scpsys.txt | 6 ++++++
include/dt-bindings/power/mt6765-power.h | 14 ++++++++++++++
2 files changed, 20 insertions(+)
create mode 100644 include/dt-bindings/power/mt6765-power.h
diff --git a/Documentation/devicetree/bindings/soc/mediatek/scpsys.txt b/Documentation/devicetree/bindings/soc/mediatek/scpsys.txt
index 00eab7e6ff22..6109b4992522 100644
--- a/Documentation/devicetree/bindings/soc/mediatek/scpsys.txt
+++ b/Documentation/devicetree/bindings/soc/mediatek/scpsys.txt
@@ -10,6 +10,7 @@ domain control.
The driver implements the Generic PM domain bindings described in
power/power_domain.txt. It provides the power domains defined in
- include/dt-bindings/power/mt8173-power.h
+- include/dt-bindings/power/mt6765-power.h
- include/dt-bindings/power/mt6797-power.h
- include/dt-bindings/power/mt2701-power.h
- include/dt-bindings/power/mt2712-power.h
@@ -20,6 +21,7 @@ Required properties:
- compatible: Should be one of:
- "mediatek,mt2701-scpsys"
- "mediatek,mt2712-scpsys"
+ - "mediatek,mt6765-scpsys"
- "mediatek,mt6797-scpsys"
- "mediatek,mt7622-scpsys"
- "mediatek,mt7623-scpsys", "mediatek,mt2701-scpsys": For MT7623 SoC
@@ -38,6 +40,10 @@ Required properties:
enabled before releasing bus protection.
Required clocks for MT2701 or MT7623: "mm", "mfg", "ethif"
Required clocks for MT2712: "mm", "mfg", "venc", "jpgdec", "audio", "vdec"
+ Required clocks for MT6765: MUX: "mm", "mfg"
+ CG: "mm-0", "mm-1", "mm-2", "mm-3", "isp-0",
+ "isp-1", "cam-0", "cam-1", "cam-2",
+ "cam-3","cam-4"
Required clocks for MT6797: "mm", "mfg", "vdec"
Required clocks for MT7622 or MT7629: "hif_sel"
Required clocks for MT7623A: "ethif"
diff --git a/include/dt-bindings/power/mt6765-power.h b/include/dt-bindings/power/mt6765-power.h
new file mode 100644
index 000000000000..d347b4ee9eed
--- /dev/null
+++ b/include/dt-bindings/power/mt6765-power.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _DT_BINDINGS_POWER_MT6765_POWER_H
+#define _DT_BINDINGS_POWER_MT6765_POWER_H
+
+#define MT6765_POWER_DOMAIN_CONN 0
+#define MT6765_POWER_DOMAIN_MM 1
+#define MT6765_POWER_DOMAIN_MFG_ASYNC 2
+#define MT6765_POWER_DOMAIN_ISP 3
+#define MT6765_POWER_DOMAIN_MFG 4
+#define MT6765_POWER_DOMAIN_MFG_CORE0 5
+#define MT6765_POWER_DOMAIN_CAM 6
+#define MT6765_POWER_DOMAIN_VCODEC 7
+
+#endif /* _DT_BINDINGS_POWER_MT6765_POWER_H */
--
2.18.0
^ permalink raw reply related
* [PATCH v6 2/8] dt-bindings: mediatek: Add smi dts binding for Mediatek MT6765 SoC
From: Macpaul Lin @ 2019-07-12 9:43 UTC (permalink / raw)
To: Rob Herring, Marc Zyngier, Ryder Lee, Stephen Boyd, Sean Wang,
Mars Cheng, Owen Chen, Matthias Brugger, linux-arm-kernel,
linux-mediatek, linux-kernel
Cc: wsd_upstream, CC Hwang, Loda Chou, devicetree, linux-serial,
linux-clk, Macpaul Lin
In-Reply-To: <1562924653-10056-1-git-send-email-macpaul.lin@mediatek.com>
From: Mars Cheng <mars.cheng@mediatek.com>
This patch adds MT6765 smi binding document
Signed-off-by: Mars Cheng <mars.cheng@mediatek.com>
Signed-off-by: Owen Chen <owen.chen@mediatek.com>
Signed-off-by: Macpaul Lin <macpaul.lin@mediatek.com>
Acked-by: Rob Herring <robh@kernel.org>
---
.../bindings/memory-controllers/mediatek,smi-common.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.txt b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.txt
index 01744ec6a75b..f7122d88a885 100644
--- a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.txt
+++ b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.txt
@@ -18,6 +18,7 @@ Required properties:
- compatible : must be one of :
"mediatek,mt2701-smi-common"
"mediatek,mt2712-smi-common"
+ "mediatek,mt6765-smi-common", "syscon"
"mediatek,mt7623-smi-common", "mediatek,mt2701-smi-common"
"mediatek,mt8173-smi-common"
"mediatek,mt8183-smi-common", "syscon"
--
2.18.0
^ permalink raw reply related
* [PATCH v6 1/8] dt-bindings: clock: mediatek: document clk bindings for Mediatek MT6765 SoC
From: Macpaul Lin @ 2019-07-12 9:43 UTC (permalink / raw)
To: Rob Herring, Marc Zyngier, Ryder Lee, Stephen Boyd, Sean Wang,
Mars Cheng, Owen Chen, Matthias Brugger, linux-arm-kernel,
linux-mediatek, linux-kernel
Cc: wsd_upstream, CC Hwang, Loda Chou, devicetree, linux-serial,
linux-clk, Macpaul Lin
In-Reply-To: <1562924653-10056-1-git-send-email-macpaul.lin@mediatek.com>
From: Mars Cheng <mars.cheng@mediatek.com>
This patch adds the binding documentation for apmixedsys, audsys, camsys,
imgsys, infracfg, mipi0a, topckgen, vcodecsys
Signed-off-by: Mars Cheng <mars.cheng@mediatek.com>
Signed-off-by: Owen Chen <owen.chen@mediatek.com>
Signed-off-by: Macpaul Lin <macpaul.lin@mediatek.com>
---
.../arm/mediatek/mediatek,apmixedsys.txt | 1 +
.../bindings/arm/mediatek/mediatek,audsys.txt | 1 +
.../bindings/arm/mediatek/mediatek,camsys.txt | 1 +
.../bindings/arm/mediatek/mediatek,imgsys.txt | 1 +
.../arm/mediatek/mediatek,infracfg.txt | 1 +
.../bindings/arm/mediatek/mediatek,mipi0a.txt | 28 +++++++++++++++++++
.../bindings/arm/mediatek/mediatek,mmsys.txt | 1 +
.../arm/mediatek/mediatek,pericfg.txt | 1 +
.../arm/mediatek/mediatek,topckgen.txt | 1 +
.../arm/mediatek/mediatek,vcodecsys.txt | 27 ++++++++++++++++++
10 files changed, 63 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,mipi0a.txt
create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,vcodecsys.txt
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt
index 161e63a6c254..5f2757e0f844 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt
@@ -8,6 +8,7 @@ Required Properties:
- compatible: Should be one of:
- "mediatek,mt2701-apmixedsys"
- "mediatek,mt2712-apmixedsys", "syscon"
+ - "mediatek,mt6765-apmixedsys", "syscon"
- "mediatek,mt6797-apmixedsys"
- "mediatek,mt7622-apmixedsys"
- "mediatek,mt7623-apmixedsys", "mediatek,mt2701-apmixedsys"
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,audsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,audsys.txt
index f3cef1a6d95c..243db5275438 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,audsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,audsys.txt
@@ -7,6 +7,7 @@ Required Properties:
- compatible: Should be one of:
- "mediatek,mt2701-audsys", "syscon"
+ - "mediatek,mt6765-audsys", "syscon"
- "mediatek,mt7622-audsys", "syscon"
- "mediatek,mt7623-audsys", "mediatek,mt2701-audsys", "syscon"
- "mediatek,mt8183-audiosys", "syscon"
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,camsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,camsys.txt
index d8930f64aa98..17acc4c5402c 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,camsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,camsys.txt
@@ -6,6 +6,7 @@ The MediaTek camsys controller provides various clocks to the system.
Required Properties:
- compatible: Should be one of:
+ - "mediatek,mt6765-camsys", "syscon"
- "mediatek,mt8183-camsys", "syscon"
- #clock-cells: Must be 1
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt
index e3bc4a1e7a6e..4e7b617acfb6 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt
@@ -8,6 +8,7 @@ Required Properties:
- compatible: Should be one of:
- "mediatek,mt2701-imgsys", "syscon"
- "mediatek,mt2712-imgsys", "syscon"
+ - "mediatek,mt6765-imgsys", "syscon"
- "mediatek,mt6797-imgsys", "syscon"
- "mediatek,mt7623-imgsys", "mediatek,mt2701-imgsys", "syscon"
- "mediatek,mt8173-imgsys", "syscon"
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt
index a90913988d7e..6a6ffb61dd29 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt
@@ -9,6 +9,7 @@ Required Properties:
- compatible: Should be one of:
- "mediatek,mt2701-infracfg", "syscon"
- "mediatek,mt2712-infracfg", "syscon"
+ - "mediatek,mt6765-infracfg", "syscon"
- "mediatek,mt6797-infracfg", "syscon"
- "mediatek,mt7622-infracfg", "syscon"
- "mediatek,mt7623-infracfg", "mediatek,mt2701-infracfg", "syscon"
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mipi0a.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mipi0a.txt
new file mode 100644
index 000000000000..49313055e574
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mipi0a.txt
@@ -0,0 +1,28 @@
+Mediatek mipi0a (mipi_rx_ana_csi0a) controller
+============================
+
+The Mediatek mipi0a controller provides various clocks
+to the system.
+
+Required Properties:
+
+- compatible: Should be one of:
+ - "mediatek,mt6765-mipi0a", "syscon"
+- #clock-cells: Must be 1
+
+The mipi0a controller uses the common clk binding from
+Documentation/devicetree/bindings/clock/clock-bindings.txt
+The available clocks are defined in dt-bindings/clock/mt*-clk.h.
+
+The mipi0a controller also uses the common power domain from
+Documentation/devicetree/bindings/soc/mediatek/scpsys.txt
+The available power doamins are defined in dt-bindings/power/mt*-power.h.
+
+Example:
+
+mipi0a: mipi0a@11c10000 {
+ compatible = "mediatek,mt6765-mipi0a", "syscon";
+ reg = <0 0x11c10000 0 0x1000>;
+ power-domains = <&scpsys MT6765_POWER_DOMAIN_CAM>;
+ #clock-cells = <1>;
+};
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt
index 545eab717c96..0c7b1698b98e 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt
@@ -8,6 +8,7 @@ Required Properties:
- compatible: Should be one of:
- "mediatek,mt2701-mmsys", "syscon"
- "mediatek,mt2712-mmsys", "syscon"
+ - "mediatek,mt6765-mmsys", "syscon"
- "mediatek,mt6797-mmsys", "syscon"
- "mediatek,mt7623-mmsys", "mediatek,mt2701-mmsys", "syscon"
- "mediatek,mt8173-mmsys", "syscon"
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
index 4c7e478117a0..b49b40741be1 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
@@ -9,6 +9,7 @@ Required Properties:
- compatible: Should be one of:
- "mediatek,mt2701-pericfg", "syscon"
- "mediatek,mt2712-pericfg", "syscon"
+ - "mediatek,mt6765-pericfg", "syscon"
- "mediatek,mt7622-pericfg", "syscon"
- "mediatek,mt7623-pericfg", "mediatek,mt2701-pericfg", "syscon"
- "mediatek,mt7629-pericfg", "syscon"
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt
index a023b8338960..21ad416bfeec 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt
@@ -8,6 +8,7 @@ Required Properties:
- compatible: Should be one of:
- "mediatek,mt2701-topckgen"
- "mediatek,mt2712-topckgen", "syscon"
+ - "mediatek,mt6765-topckgen", "syscon"
- "mediatek,mt6797-topckgen"
- "mediatek,mt7622-topckgen"
- "mediatek,mt7623-topckgen", "mediatek,mt2701-topckgen"
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,vcodecsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,vcodecsys.txt
new file mode 100644
index 000000000000..83f7f8634943
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,vcodecsys.txt
@@ -0,0 +1,27 @@
+Mediatek vcodecsys controller
+============================
+
+The Mediatek vcodecsys controller provides various clocks to the system.
+
+Required Properties:
+
+- compatible: Should be one of:
+ - "mediatek,mt6765-vcodecsys", "syscon"
+- #clock-cells: Must be 1
+
+The vcodecsys controller uses the common clk binding from
+Documentation/devicetree/bindings/clock/clock-bindings.txt
+The available clocks are defined in dt-bindings/clock/mt*-clk.h.
+
+The vcodecsys controller also uses the common power domain from
+Documentation/devicetree/bindings/soc/mediatek/scpsys.txt
+The available power doamins are defined in dt-bindings/power/mt*-power.h.
+
+Example:
+
+venc_gcon: venc_gcon@17000000 {
+ compatible = "mediatek,mt6765-vcodecsys", "syscon";
+ reg = <0 0x17000000 0 0x10000>;
+ power-domains = <&scpsys MT6765_POWER_DOMAIN_VCODEC>;
+ #clock-cells = <1>;
+};
--
2.18.0
^ permalink raw reply related
* [PATCH v6 0/8] Add basic SoC support for mt6765
From: Macpaul Lin @ 2019-07-12 9:43 UTC (permalink / raw)
To: Rob Herring, Marc Zyngier, Ryder Lee, Stephen Boyd, Sean Wang,
Mars Cheng, Owen Chen, Matthias Brugger, linux-arm-kernel,
linux-mediatek, linux-kernel
Cc: wsd_upstream, CC Hwang, Loda Chou, devicetree, linux-serial,
linux-clk, Macpaul Lin
This patch adds basic SoC support for Mediatek's new 8-core SoC,
MT6765, which is mainly for smartphone application.
Changes in V6:
1. Adapt V5's patchset to latest kernel tree.
Origin V5 patchset.
https://lore.kernel.org/patchwork/cover/963612/
2. Due to clk's common code has been submit by other platform,
this patch set will have dependencies with the following patchsets
as the following orders.
2.a. [v8,00/21] MT8183 IOMMU SUPPORT
https://patchwork.kernel.org/cover/11023585/
2.b. [v11,0/6] Add basic node support for Mediatek MT8183 SoC
https://patchwork.kernel.org/cover/10962385/
2.c. [v6,00/14] Mediatek MT8183 scpsys support
https://patchwork.kernel.org/cover/11005751/
3. Correct power related patches into dt-binding patches.
4. Re-order V5's 4/11, 6/11, and 7/11 due clk common code change
and make dependencies in order.
5. Update some commit message in clk related patches.
Changes in V5:
1. add clk support
Changes in V4:
1. add gic's settings in reg properties
2. remove some patches about dt-bindings since GKH already took them
Changes in V3:
1. split dt-binding document patchs
2. fix mt6765.dtsi warnings with W=12
3. remove uncessary PPI affinity for timer
4. add gicc base for gic dt node
Changes in V2:
1. fix clk properties in uart dts node
2. fix typo in submit title
3. add simple-bus in mt6765.dtsi
4. use correct SPDX license format
Mars Cheng (6):
dt-bindings: clock: mediatek: document clk bindings for Mediatek
MT6765 SoC
dt-bindings: mediatek: Add smi dts binding for Mediatek MT6765 SoC
dt-bindings: mediatek: add MT6765 power dt-bindings
clk: mediatek: add mt6765 clock IDs
soc: mediatek: add MT6765 scpsys and subdomain support
arm64: dts: mediatek: add mt6765 support
Owen Chen (2):
clk: mediatek: Add MT6765 clock support
arm64: defconfig: add CONFIG_COMMON_CLK_MT6765_XXX clocks
.../arm/mediatek/mediatek,apmixedsys.txt | 1 +
.../bindings/arm/mediatek/mediatek,audsys.txt | 1 +
.../bindings/arm/mediatek/mediatek,camsys.txt | 1 +
.../bindings/arm/mediatek/mediatek,imgsys.txt | 1 +
.../arm/mediatek/mediatek,infracfg.txt | 1 +
.../bindings/arm/mediatek/mediatek,mipi0a.txt | 28 +
.../bindings/arm/mediatek/mediatek,mmsys.txt | 1 +
.../arm/mediatek/mediatek,pericfg.txt | 1 +
.../arm/mediatek/mediatek,topckgen.txt | 1 +
.../arm/mediatek/mediatek,vcodecsys.txt | 27 +
.../mediatek,smi-common.txt | 1 +
.../bindings/soc/mediatek/scpsys.txt | 6 +
arch/arm64/boot/dts/mediatek/Makefile | 1 +
arch/arm64/boot/dts/mediatek/mt6765-evb.dts | 33 +
arch/arm64/boot/dts/mediatek/mt6765.dtsi | 253 +++++
arch/arm64/configs/defconfig | 6 +
drivers/clk/mediatek/Kconfig | 86 ++
drivers/clk/mediatek/Makefile | 7 +
drivers/clk/mediatek/clk-mt6765-audio.c | 109 ++
drivers/clk/mediatek/clk-mt6765-cam.c | 83 ++
drivers/clk/mediatek/clk-mt6765-img.c | 79 ++
drivers/clk/mediatek/clk-mt6765-mipi0a.c | 77 ++
drivers/clk/mediatek/clk-mt6765-mm.c | 105 ++
drivers/clk/mediatek/clk-mt6765-vcodec.c | 79 ++
drivers/clk/mediatek/clk-mt6765.c | 961 ++++++++++++++++++
drivers/soc/mediatek/mtk-scpsys.c | 130 +++
include/dt-bindings/clock/mt6765-clk.h | 313 ++++++
include/dt-bindings/power/mt6765-power.h | 14 +
28 files changed, 2406 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,mipi0a.txt
create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,vcodecsys.txt
create mode 100644 arch/arm64/boot/dts/mediatek/mt6765-evb.dts
create mode 100644 arch/arm64/boot/dts/mediatek/mt6765.dtsi
create mode 100644 drivers/clk/mediatek/clk-mt6765-audio.c
create mode 100644 drivers/clk/mediatek/clk-mt6765-cam.c
create mode 100644 drivers/clk/mediatek/clk-mt6765-img.c
create mode 100644 drivers/clk/mediatek/clk-mt6765-mipi0a.c
create mode 100644 drivers/clk/mediatek/clk-mt6765-mm.c
create mode 100644 drivers/clk/mediatek/clk-mt6765-vcodec.c
create mode 100644 drivers/clk/mediatek/clk-mt6765.c
create mode 100644 include/dt-bindings/clock/mt6765-clk.h
create mode 100644 include/dt-bindings/power/mt6765-power.h
--
2.18.0
^ permalink raw reply
* Re: [PATCH v3] arm64: dts: sdm845: Add video nodes
From: Rajendra Nayak @ 2019-07-12 9:25 UTC (permalink / raw)
To: Aniket Masule, andy.gross, david.brown, robh+dt, mark.rutland
Cc: devicetree, linux-kernel, linux-arm-msm, vgarodia, Malathi Gottam
In-Reply-To: <1562069549-25384-1-git-send-email-amasule@codeaurora.org>
On 7/2/2019 5:42 PM, Aniket Masule wrote:
> From: Malathi Gottam <mgottam@codeaurora.org>
>
> This adds video nodes to sdm845 based on the examples
> in the bindings.
>
> Signed-off-by: Malathi Gottam <mgottam@codeaurora.org>
> Co-developed-by: Aniket Masule <amasule@codeaurora.org>
> Signed-off-by: Aniket Masule <amasule@codeaurora.org>
Reviewed-by: Rajendra Nayak <rnayak@codeaurora.org>
> ---
> arch/arm64/boot/dts/qcom/sdm845.dtsi | 30 ++++++++++++++++++++++++++++++
> 1 file changed, 30 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> index fcb9330..f3cd94f 100644
> --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
> +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> @@ -1893,6 +1893,36 @@
> };
> };
>
> + video-codec@aa00000 {
> + compatible = "qcom,sdm845-venus";
> + reg = <0 0x0aa00000 0 0xff000>;
> + interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
> + power-domains = <&videocc VENUS_GDSC>;
> + clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>,
> + <&videocc VIDEO_CC_VENUS_AHB_CLK>,
> + <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>;
> + clock-names = "core", "iface", "bus";
> + iommus = <&apps_smmu 0x10a0 0x8>,
> + <&apps_smmu 0x10b0 0x0>;
> + memory-region = <&venus_mem>;
> +
> + video-core0 {
> + compatible = "venus-decoder";
> + clocks = <&videocc VIDEO_CC_VCODEC0_CORE_CLK>,
> + <&videocc VIDEO_CC_VCODEC0_AXI_CLK>;
> + clock-names = "core", "bus";
> + power-domains = <&videocc VCODEC0_GDSC>;
> + };
> +
> + video-core1 {
> + compatible = "venus-encoder";
> + clocks = <&videocc VIDEO_CC_VCODEC1_CORE_CLK>,
> + <&videocc VIDEO_CC_VCODEC1_AXI_CLK>;
> + clock-names = "core", "bus";
> + power-domains = <&videocc VCODEC1_GDSC>;
> + };
> + };
> +
> videocc: clock-controller@ab00000 {
> compatible = "qcom,sdm845-videocc";
> reg = <0 0x0ab00000 0 0x10000>;
>
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply
* [PATCH] fdt: Properly handle "no-map" field in the memory region
From: KarimAllah Ahmed @ 2019-07-12 8:31 UTC (permalink / raw)
To: linux-kernel, devicetree; +Cc: KarimAllah Ahmed, Rob Herring, Frank Rowand
Mark the memory region with NOMAP flag instead of completely removing it
from the memory blocks. That makes the FDT handling consistent with the EFI
memory map handling.
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Frank Rowand <frowand.list@gmail.com>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: KarimAllah Ahmed <karahmed@amazon.de>
---
drivers/of/fdt.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index de893c9..77982ae 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -1175,7 +1175,7 @@ int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
phys_addr_t size, bool nomap)
{
if (nomap)
- return memblock_remove(base, size);
+ return memblock_mark_nomap(base, size);
return memblock_reserve(base, size);
}
--
2.7.4
^ permalink raw reply related
* Re: [PATCH v3 0/9] i2c: add support for filters
From: Ludovic Desroches @ 2019-07-12 8:20 UTC (permalink / raw)
To: Eugen Hristev - M18282
Cc: wsa@the-dreams.de, peda@axentia.se, mark.rutland@arm.com,
linux-i2c@vger.kernel.org, devicetree@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, pierre-yves.mordret@st.com,
alexandre.belloni@bootlin.com, robh+dt@kernel.org,
Nicolas Ferre - M43238
In-Reply-To: <1562678049-17581-1-git-send-email-eugen.hristev@microchip.com>
On Tue, Jul 09, 2019 at 03:19:26PM +0200, Eugen Hristev - M18282 wrote:
> From: Eugen Hristev <eugen.hristev@microchip.com>
>
> Hello,
>
> This series adds support for analog and digital filters for i2c controllers
>
> This series is based on the series:
> [PATCH v2 0/9] i2c: at91: filters support for at91 SoCs
> and enhanced to add the bindings for all controllers plus an extra binding
> for the width of the spikes in nanoseconds.
>
> First, bindings are created for
> 'i2c-ana-filter'
> 'i2c-dig-filter'
> 'i2c-filter-width-ns'
>
> The support is added in the i2c core to retrieve filter width and add it
> to the timings structure.
> Next, the at91 driver is enhanced for supporting digital filter, advanced
> digital filter (with selectable spike width) and the analog filter.
>
> Finally the device tree for two boards are modified to make use of the
> new properties.
>
> This series is the result of the comments on the ML in the direction
> requested: to make the bindings globally available for i2c drivers.
>
> Changes in v3:
> - made bindings global for i2c controllers and modified accordingly
> - gave up PADFCDF bit because it's a lack in datasheet
> - the computation on the width of the spike is based on periph clock as it
> is done for hold time.
>
> Changes in v2:
> - added device tree bindings and support for enable-ana-filt and
> enable-dig-filt
> - added the new properties to the DT for sama5d4_xplained/sama5d2_xplained
>
> Eugen Hristev (9):
> dt-bindings: i2c: at91: add new compatible
> dt-bindings: i2c: add bindings for i2c analog and digital filter
> i2c: add support for filter-width-ns optional property
> i2c: at91: add new platform support for sam9x60
> i2c: at91: add support for digital filtering
> i2c: at91: add support for advanced digital filtering
> i2c: at91: add support for analog filtering
> ARM: dts: at91: sama5d2_xplained: add analog and digital filter for
> i2c
> ARM: dts: at91: sama5d4_xplained: add analog filter for i2c
>
> Documentation/devicetree/bindings/i2c/i2c-at91.txt | 3 +-
> Documentation/devicetree/bindings/i2c/i2c.txt | 11 +++++
> arch/arm/boot/dts/at91-sama5d2_xplained.dts | 6 +++
> arch/arm/boot/dts/at91-sama5d4_xplained.dts | 1 +
> drivers/i2c/busses/i2c-at91-core.c | 38 +++++++++++++++++
> drivers/i2c/busses/i2c-at91-master.c | 49 ++++++++++++++++++++--
> drivers/i2c/busses/i2c-at91.h | 13 ++++++
> drivers/i2c/i2c-core-base.c | 2 +
> include/linux/i2c.h | 2 +
> 9 files changed, 121 insertions(+), 4 deletions(-)
Hi,
I don't know if it will fit other vendors need concerning the binding
but for Microchip it sounds good.
Acked-by: Ludovic Desroches <ludovic.desroches@microchip.com>
for the whole serie.
Regards
Ludovic
^ permalink raw reply
* [PATCH v9 18/18] MAINTAINERS: add proc sysctl KUnit test to PROC SYSCTL section
From: Brendan Higgins @ 2019-07-12 8:17 UTC (permalink / raw)
To: frowand.list, gregkh, jpoimboe, keescook, kieran.bingham, mcgrof,
peterz, robh, sboyd, shuah, tytso, yamada.masahiro
Cc: pmladek, linux-doc, amir73il, Brendan Higgins, dri-devel,
Alexander.Levin, linux-kselftest, linux-nvdimm, khilman,
knut.omang, wfg, joel, rientjes, Iurii Zaikin, jdike,
dan.carpenter, devicetree, linux-kbuild, Tim.Bird, linux-um,
rostedt, julia.lawall, kunit-dev, richard, rdunlap, linux-kernel,
mpe, linux-fsdevel, logang
In-Reply-To: <20190712081744.87097-1-brendanhiggins@google.com>
Add entry for the new proc sysctl KUnit test to the PROC SYSCTL section,
and add Iurii as a maintainer.
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Cc: Iurii Zaikin <yzaikin@google.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
Acked-by: Luis Chamberlain <mcgrof@kernel.org>
---
MAINTAINERS | 2 ++
1 file changed, 2 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 48d04d180a988..f8204c75114da 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12721,12 +12721,14 @@ F: Documentation/filesystems/proc.txt
PROC SYSCTL
M: Luis Chamberlain <mcgrof@kernel.org>
M: Kees Cook <keescook@chromium.org>
+M: Iurii Zaikin <yzaikin@google.com>
L: linux-kernel@vger.kernel.org
L: linux-fsdevel@vger.kernel.org
S: Maintained
F: fs/proc/proc_sysctl.c
F: include/linux/sysctl.h
F: kernel/sysctl.c
+F: kernel/sysctl-test.c
F: tools/testing/selftests/sysctl/
PS3 NETWORK SUPPORT
--
2.22.0.410.gd8fdbe21b5-goog
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply related
* [PATCH v9 17/18] kernel/sysctl-test: Add null pointer test for sysctl.c:proc_dointvec()
From: Brendan Higgins @ 2019-07-12 8:17 UTC (permalink / raw)
To: frowand.list-Re5JQEeQqe8AvxtiuMwx3w,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
jpoimboe-H+wXaHxf7aLQT0dZR+AlfA, keescook-hpIqsD4AKlfQT0dZR+AlfA,
kieran.bingham-ryLnwIuWjnjg/C1BVhZhaw,
mcgrof-DgEjT+Ai2ygdnm+yROfE0A, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
robh-DgEjT+Ai2ygdnm+yROfE0A, sboyd-DgEjT+Ai2ygdnm+yROfE0A,
shuah-DgEjT+Ai2ygdnm+yROfE0A, tytso-3s7WtUTddSA,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A
Cc: pmladek-IBi9RG/b67k, linux-doc-u79uwXL29TY76Z2rM5mHXA,
amir73il-Re5JQEeQqe8AvxtiuMwx3w, Brendan Higgins,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
Alexander.Levin-0li6OtcxBFHby3iVrkZq2A,
linux-kselftest-u79uwXL29TY76Z2rM5mHXA,
linux-nvdimm-hn68Rpc1hR1g9hUCZPvPmw,
khilman-rdvid1DuHRBWk0Htik3J/w, knut.omang-QHcLZuEGTsvQT0dZR+AlfA,
wfg-VuQAYsv1563Yd54FQh9/CA, joel-U3u1mxZcP9KHXe+LvDLADg,
rientjes-hpIqsD4AKlfQT0dZR+AlfA, Iurii Zaikin,
jdike-OPE4K8JWMJJBDgjK7y7TUQ,
dan.carpenter-QHcLZuEGTsvQT0dZR+AlfA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kbuild-u79uwXL29TY76Z2rM5mHXA, Tim.Bird-7U/KSKJipcs,
linux-um-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
rostedt-nx8X9YLhiw1AfugRpC6u6w, julia.lawall-L2FTfq7BK8M,
kunit-dev-/JYPxA39Uh5TLH3MbocFFw, richard-/L3Ra7n9ekc,
rdunlap-wEGCiKHe2LqWVfeAwA7xHQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, daniel-/w4YWyX8dFk,
mpe-Gsx/Oe8HsFggBc27wqDAHg, linux-fsdevel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20190712081744.87097-1-brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
From: Iurii Zaikin <yzaikin-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
KUnit tests for initialized data behavior of proc_dointvec that is
explicitly checked in the code. Includes basic parsing tests including
int min/max overflow.
Signed-off-by: Iurii Zaikin <yzaikin-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Brendan Higgins <brendanhiggins-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Reviewed-by: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
Reviewed-by: Logan Gunthorpe <logang-OTvnGxWRz7hWk0Htik3J/w@public.gmane.org>
Acked-by: Luis Chamberlain <mcgrof-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
kernel/Makefile | 2 +
kernel/sysctl-test.c | 392 +++++++++++++++++++++++++++++++++++++++++++
lib/Kconfig.debug | 11 ++
3 files changed, 405 insertions(+)
create mode 100644 kernel/sysctl-test.c
diff --git a/kernel/Makefile b/kernel/Makefile
index a8d923b5481ba..50fd511cd0ee0 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -114,6 +114,8 @@ obj-$(CONFIG_HAS_IOMEM) += iomem.o
obj-$(CONFIG_ZONE_DEVICE) += memremap.o
obj-$(CONFIG_RSEQ) += rseq.o
+obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o
+
obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o
KASAN_SANITIZE_stackleak.o := n
KCOV_INSTRUMENT_stackleak.o := n
diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
new file mode 100644
index 0000000000000..2a63241a8453b
--- /dev/null
+++ b/kernel/sysctl-test.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test of proc sysctl.
+ */
+
+#include <kunit/test.h>
+#include <linux/sysctl.h>
+
+#define KUNIT_PROC_READ 0
+#define KUNIT_PROC_WRITE 1
+
+static int i_zero;
+static int i_one_hundred = 100;
+
+/*
+ * Test that proc_dointvec will not try to use a NULL .data field even when the
+ * length is non-zero.
+ */
+static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
+{
+ struct ctl_table null_data_table = {
+ .procname = "foo",
+ /*
+ * Here we are testing that proc_dointvec behaves correctly when
+ * we give it a NULL .data field. Normally this would point to a
+ * piece of memory where the value would be stored.
+ */
+ .data = NULL,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ /*
+ * proc_dointvec expects a buffer in user space, so we allocate one. We
+ * also need to cast it to __user so sparse doesn't get mad.
+ */
+ void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
+ GFP_USER);
+ size_t len;
+ loff_t pos;
+
+ /*
+ * We don't care what the starting length is since proc_dointvec should
+ * not try to read because .data is NULL.
+ */
+ len = 1234;
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&null_data_table,
+ KUNIT_PROC_READ, buffer, &len,
+ &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+
+ /*
+ * See above.
+ */
+ len = 1234;
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&null_data_table,
+ KUNIT_PROC_WRITE, buffer, &len,
+ &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+}
+
+/*
+ * Similar to the previous test, we create a struct ctrl_table that has a .data
+ * field that proc_dointvec cannot do anything with; however, this time it is
+ * because we tell proc_dointvec that the size is 0.
+ */
+static void sysctl_test_api_dointvec_table_maxlen_unset(struct kunit *test)
+{
+ int data = 0;
+ struct ctl_table data_maxlen_unset_table = {
+ .procname = "foo",
+ .data = &data,
+ /*
+ * So .data is no longer NULL, but we tell proc_dointvec its
+ * length is 0, so it still shouldn't try to use it.
+ */
+ .maxlen = 0,
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
+ GFP_USER);
+ size_t len;
+ loff_t pos;
+
+ /*
+ * As before, we don't care what buffer length is because proc_dointvec
+ * cannot do anything because its internal .data buffer has zero length.
+ */
+ len = 1234;
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&data_maxlen_unset_table,
+ KUNIT_PROC_READ, buffer, &len,
+ &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+
+ /*
+ * See previous comment.
+ */
+ len = 1234;
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&data_maxlen_unset_table,
+ KUNIT_PROC_WRITE, buffer, &len,
+ &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+}
+
+/*
+ * Here we provide a valid struct ctl_table, but we try to read and write from
+ * it using a buffer of zero length, so it should still fail in a similar way as
+ * before.
+ */
+static void sysctl_test_api_dointvec_table_len_is_zero(struct kunit *test)
+{
+ int data = 0;
+ /* Good table. */
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
+ GFP_USER);
+ /*
+ * However, now our read/write buffer has zero length.
+ */
+ size_t len = 0;
+ loff_t pos;
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ, buffer,
+ &len, &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE, buffer,
+ &len, &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+}
+
+/*
+ * Test that proc_dointvec refuses to read when the file position is non-zero.
+ */
+static void sysctl_test_api_dointvec_table_read_but_position_set(
+ struct kunit *test)
+{
+ int data = 0;
+ /* Good table. */
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
+ GFP_USER);
+ /*
+ * We don't care about our buffer length because we start off with a
+ * non-zero file position.
+ */
+ size_t len = 1234;
+ /*
+ * proc_dointvec should refuse to read into the buffer since the file
+ * pos is non-zero.
+ */
+ loff_t pos = 1;
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ, buffer,
+ &len, &pos));
+ KUNIT_EXPECT_EQ(test, (size_t)0, len);
+}
+
+/*
+ * Test that we can read a two digit number in a sufficiently size buffer.
+ * Nothing fancy.
+ */
+static void sysctl_test_dointvec_read_happy_single_positive(struct kunit *test)
+{
+ int data = 0;
+ /* Good table. */
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ size_t len = 4;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ /* Store 13 in the data field. */
+ *((int *)table.data) = 13;
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ,
+ user_buffer, &len, &pos));
+ KUNIT_ASSERT_EQ(test, (size_t)3, len);
+ buffer[len] = '\0';
+ /* And we read 13 back out. */
+ KUNIT_EXPECT_STREQ(test, "13\n", buffer);
+}
+
+/*
+ * Same as previous test, just now with negative numbers.
+ */
+static void sysctl_test_dointvec_read_happy_single_negative(struct kunit *test)
+{
+ int data = 0;
+ /* Good table. */
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ size_t len = 5;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ *((int *)table.data) = -16;
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ,
+ user_buffer, &len, &pos));
+ KUNIT_ASSERT_EQ(test, (size_t)4, len);
+ buffer[len] = '\0';
+ KUNIT_EXPECT_STREQ(test, "-16\n", (char *)buffer);
+}
+
+/*
+ * Test that a simple positive write works.
+ */
+static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
+{
+ int data = 0;
+ /* Good table. */
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ char input[] = "9";
+ size_t len = sizeof(input) - 1;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+
+ memcpy(buffer, input, len);
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len);
+ KUNIT_EXPECT_EQ(test, sizeof(input) - 1, (size_t)pos);
+ KUNIT_EXPECT_EQ(test, 9, *((int *)table.data));
+}
+
+/*
+ * Same as previous test, but now with negative numbers.
+ */
+static void sysctl_test_dointvec_write_happy_single_negative(struct kunit *test)
+{
+ int data = 0;
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ char input[] = "-9";
+ size_t len = sizeof(input) - 1;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+
+ memcpy(buffer, input, len);
+
+ KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len);
+ KUNIT_EXPECT_EQ(test, sizeof(input) - 1, (size_t)pos);
+ KUNIT_EXPECT_EQ(test, -9, *((int *)table.data));
+}
+
+/*
+ * Test that writing a value smaller than the minimum possible value is not
+ * allowed.
+ */
+static void sysctl_test_api_dointvec_write_single_less_int_min(
+ struct kunit *test)
+{
+ int data = 0;
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ size_t max_len = 32, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ unsigned long abs_of_less_than_min = (unsigned long)INT_MAX
+ - (INT_MAX + INT_MIN) + 1;
+
+ /*
+ * We use this rigmarole to create a string that contains a value one
+ * less than the minimum accepted value.
+ */
+ KUNIT_ASSERT_LT(test,
+ (size_t)snprintf(buffer, max_len, "-%lu",
+ abs_of_less_than_min),
+ max_len);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_EXPECT_EQ(test, max_len, len);
+ KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
+}
+
+/*
+ * Test that writing the maximum possible value works.
+ */
+static void sysctl_test_api_dointvec_write_single_greater_int_max(
+ struct kunit *test)
+{
+ int data = 0;
+ struct ctl_table table = {
+ .procname = "foo",
+ .data = &data,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &i_zero,
+ .extra2 = &i_one_hundred,
+ };
+ size_t max_len = 32, len = max_len;
+ loff_t pos = 0;
+ char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
+ char __user *user_buffer = (char __user *)buffer;
+ unsigned long greater_than_max = (unsigned long)INT_MAX + 1;
+
+ KUNIT_ASSERT_GT(test, greater_than_max, (unsigned long)INT_MAX);
+ KUNIT_ASSERT_LT(test, (size_t)snprintf(buffer, max_len, "%lu",
+ greater_than_max),
+ max_len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec(&table, KUNIT_PROC_WRITE,
+ user_buffer, &len, &pos));
+ KUNIT_ASSERT_EQ(test, max_len, len);
+ KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
+}
+
+static struct kunit_case sysctl_test_cases[] = {
+ KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data),
+ KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset),
+ KUNIT_CASE(sysctl_test_api_dointvec_table_len_is_zero),
+ KUNIT_CASE(sysctl_test_api_dointvec_table_read_but_position_set),
+ KUNIT_CASE(sysctl_test_dointvec_read_happy_single_positive),
+ KUNIT_CASE(sysctl_test_dointvec_read_happy_single_negative),
+ KUNIT_CASE(sysctl_test_dointvec_write_happy_single_positive),
+ KUNIT_CASE(sysctl_test_dointvec_write_happy_single_negative),
+ KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min),
+ KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max),
+ {}
+};
+
+static struct kunit_suite sysctl_test_suite = {
+ .name = "sysctl_test",
+ .test_cases = sysctl_test_cases,
+};
+
+kunit_test_suite(sysctl_test_suite);
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index cbdfae3798965..6f8007800a76f 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1939,6 +1939,17 @@ config TEST_SYSCTL
If unsure, say N.
+config SYSCTL_KUNIT_TEST
+ bool "KUnit test for sysctl"
+ depends on KUNIT
+ help
+ This builds the proc sysctl unit test, which runs on boot.
+ Tests the API contract and implementation correctness of sysctl.
+ For more information on KUnit and unit tests in general please refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
+
config TEST_UDELAY
tristate "udelay test driver"
help
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH v9 16/18] MAINTAINERS: add entry for KUnit the unit testing framework
From: Brendan Higgins @ 2019-07-12 8:17 UTC (permalink / raw)
To: frowand.list, gregkh, jpoimboe, keescook, kieran.bingham, mcgrof,
peterz, robh, sboyd, shuah, tytso, yamada.masahiro
Cc: devicetree, dri-devel, kunit-dev, linux-doc, linux-fsdevel,
linux-kbuild, linux-kernel, linux-kselftest, linux-nvdimm,
linux-um, Alexander.Levin, Tim.Bird, amir73il, dan.carpenter,
daniel, jdike, joel, julia.lawall, khilman, knut.omang, logang,
mpe, pmladek, rdunlap, richard, rientjes, rostedt, wfg,
Brendan Higgins
In-Reply-To: <20190712081744.87097-1-brendanhiggins@google.com>
Add myself as maintainer of KUnit, the Linux kernel's unit testing
framework.
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
---
MAINTAINERS | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 677ef41cb012c..48d04d180a988 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8599,6 +8599,17 @@ S: Maintained
F: tools/testing/selftests/
F: Documentation/dev-tools/kselftest*
+KERNEL UNIT TESTING FRAMEWORK (KUnit)
+M: Brendan Higgins <brendanhiggins@google.com>
+L: linux-kselftest@vger.kernel.org
+L: kunit-dev@googlegroups.com
+W: https://google.github.io/kunit-docs/third_party/kernel/docs/
+S: Maintained
+F: Documentation/dev-tools/kunit/
+F: include/kunit/
+F: kunit/
+F: tools/testing/kunit/
+
KERNEL USERMODE HELPER
M: Luis Chamberlain <mcgrof@kernel.org>
L: linux-kernel@vger.kernel.org
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
* [PATCH v9 15/18] Documentation: kunit: add documentation for KUnit
From: Brendan Higgins @ 2019-07-12 8:17 UTC (permalink / raw)
To: frowand.list, gregkh, jpoimboe, keescook, kieran.bingham, mcgrof,
peterz, robh, sboyd, shuah, tytso, yamada.masahiro
Cc: devicetree, dri-devel, kunit-dev, linux-doc, linux-fsdevel,
linux-kbuild, linux-kernel, linux-kselftest, linux-nvdimm,
linux-um, Alexander.Levin, Tim.Bird, amir73il, dan.carpenter,
daniel, jdike, joel, julia.lawall, khilman, knut.omang, logang,
mpe, pmladek, rdunlap, richard, rientjes, rostedt, wfg,
Brendan Higgins, Felix Guo, Jonathan Corbet
In-Reply-To: <20190712081744.87097-1-brendanhiggins@google.com>
Add documentation for KUnit, the Linux kernel unit testing framework.
- Add intro and usage guide for KUnit
- Add API reference
Signed-off-by: Felix Guo <felixguoxiuping@gmail.com>
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
---
Documentation/dev-tools/index.rst | 1 +
Documentation/dev-tools/kunit/api/index.rst | 16 +
Documentation/dev-tools/kunit/api/test.rst | 14 +
Documentation/dev-tools/kunit/faq.rst | 62 +++
Documentation/dev-tools/kunit/index.rst | 79 +++
Documentation/dev-tools/kunit/start.rst | 180 ++++++
Documentation/dev-tools/kunit/usage.rst | 575 ++++++++++++++++++++
7 files changed, 927 insertions(+)
create mode 100644 Documentation/dev-tools/kunit/api/index.rst
create mode 100644 Documentation/dev-tools/kunit/api/test.rst
create mode 100644 Documentation/dev-tools/kunit/faq.rst
create mode 100644 Documentation/dev-tools/kunit/index.rst
create mode 100644 Documentation/dev-tools/kunit/start.rst
create mode 100644 Documentation/dev-tools/kunit/usage.rst
diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst
index b0522a4dd1073..09dee10d25928 100644
--- a/Documentation/dev-tools/index.rst
+++ b/Documentation/dev-tools/index.rst
@@ -24,6 +24,7 @@ whole; patches welcome!
gdb-kernel-debugging
kgdb
kselftest
+ kunit/index
.. only:: subproject and html
diff --git a/Documentation/dev-tools/kunit/api/index.rst b/Documentation/dev-tools/kunit/api/index.rst
new file mode 100644
index 0000000000000..9b9bffe5d41a0
--- /dev/null
+++ b/Documentation/dev-tools/kunit/api/index.rst
@@ -0,0 +1,16 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============
+API Reference
+=============
+.. toctree::
+
+ test
+
+This section documents the KUnit kernel testing API. It is divided into the
+following sections:
+
+================================= ==============================================
+:doc:`test` documents all of the standard testing API
+ excluding mocking or mocking related features.
+================================= ==============================================
diff --git a/Documentation/dev-tools/kunit/api/test.rst b/Documentation/dev-tools/kunit/api/test.rst
new file mode 100644
index 0000000000000..d0ce19b1e1185
--- /dev/null
+++ b/Documentation/dev-tools/kunit/api/test.rst
@@ -0,0 +1,14 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+========
+Test API
+========
+
+This file documents all of the standard testing API excluding mocking or mocking
+related features.
+
+.. kernel-doc:: include/kunit/test.h
+ :internal:
+
+.. kernel-doc:: include/kunit/kunit-stream.h
+ :internal:
diff --git a/Documentation/dev-tools/kunit/faq.rst b/Documentation/dev-tools/kunit/faq.rst
new file mode 100644
index 0000000000000..bf2095112d899
--- /dev/null
+++ b/Documentation/dev-tools/kunit/faq.rst
@@ -0,0 +1,62 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==========================
+Frequently Asked Questions
+==========================
+
+How is this different from Autotest, kselftest, etc?
+====================================================
+KUnit is a unit testing framework. Autotest, kselftest (and some others) are
+not.
+
+A `unit test <https://martinfowler.com/bliki/UnitTest.html>`_ is supposed to
+test a single unit of code in isolation, hence the name. A unit test should be
+the finest granularity of testing and as such should allow all possible code
+paths to be tested in the code under test; this is only possible if the code
+under test is very small and does not have any external dependencies outside of
+the test's control like hardware.
+
+There are no testing frameworks currently available for the kernel that do not
+require installing the kernel on a test machine or in a VM and all require
+tests to be written in userspace and run on the kernel under test; this is true
+for Autotest, kselftest, and some others, disqualifying any of them from being
+considered unit testing frameworks.
+
+Does KUnit support running on architectures other than UML?
+===========================================================
+
+Yes, well, mostly.
+
+For the most part, the KUnit core framework (what you use to write the tests)
+can compile to any architecture; it compiles like just another part of the
+kernel and runs when the kernel boots. However, there is some infrastructure,
+like the KUnit Wrapper (``tools/testing/kunit/kunit.py``) that does not support
+other architectures.
+
+In short, this means that, yes, you can run KUnit on other architectures, but
+it might require more work than using KUnit on UML.
+
+For more information, see :ref:`kunit-on-non-uml`.
+
+What is the difference between a unit test and these other kinds of tests?
+==========================================================================
+Most existing tests for the Linux kernel would be categorized as an integration
+test, or an end-to-end test.
+
+- A unit test is supposed to test a single unit of code in isolation, hence the
+ name. A unit test should be the finest granularity of testing and as such
+ should allow all possible code paths to be tested in the code under test; this
+ is only possible if the code under test is very small and does not have any
+ external dependencies outside of the test's control like hardware.
+- An integration test tests the interaction between a minimal set of components,
+ usually just two or three. For example, someone might write an integration
+ test to test the interaction between a driver and a piece of hardware, or to
+ test the interaction between the userspace libraries the kernel provides and
+ the kernel itself; however, one of these tests would probably not test the
+ entire kernel along with hardware interactions and interactions with the
+ userspace.
+- An end-to-end test usually tests the entire system from the perspective of the
+ code under test. For example, someone might write an end-to-end test for the
+ kernel by installing a production configuration of the kernel on production
+ hardware with a production userspace and then trying to exercise some behavior
+ that depends on interactions between the hardware, the kernel, and userspace.
diff --git a/Documentation/dev-tools/kunit/index.rst b/Documentation/dev-tools/kunit/index.rst
new file mode 100644
index 0000000000000..a317ab45bfe2d
--- /dev/null
+++ b/Documentation/dev-tools/kunit/index.rst
@@ -0,0 +1,79 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================================
+KUnit - Unit Testing for the Linux Kernel
+=========================================
+
+.. toctree::
+ :maxdepth: 2
+
+ start
+ usage
+ api/index
+ faq
+
+What is KUnit?
+==============
+
+KUnit is a lightweight unit testing and mocking framework for the Linux kernel.
+These tests are able to be run locally on a developer's workstation without a VM
+or special hardware.
+
+KUnit is heavily inspired by JUnit, Python's unittest.mock, and
+Googletest/Googlemock for C++. KUnit provides facilities for defining unit test
+cases, grouping related test cases into test suites, providing common
+infrastructure for running tests, and much more.
+
+Get started now: :doc:`start`
+
+Why KUnit?
+==========
+
+A unit test is supposed to test a single unit of code in isolation, hence the
+name. A unit test should be the finest granularity of testing and as such should
+allow all possible code paths to be tested in the code under test; this is only
+possible if the code under test is very small and does not have any external
+dependencies outside of the test's control like hardware.
+
+Outside of KUnit, there are no testing frameworks currently
+available for the kernel that do not require installing the kernel on a test
+machine or in a VM and all require tests to be written in userspace running on
+the kernel; this is true for Autotest, and kselftest, disqualifying
+any of them from being considered unit testing frameworks.
+
+KUnit addresses the problem of being able to run tests without needing a virtual
+machine or actual hardware with User Mode Linux. User Mode Linux is a Linux
+architecture, like ARM or x86; however, unlike other architectures it compiles
+to a standalone program that can be run like any other program directly inside
+of a host operating system; to be clear, it does not require any virtualization
+support; it is just a regular program.
+
+KUnit is fast. Excluding build time, from invocation to completion KUnit can run
+several dozen tests in only 10 to 20 seconds; this might not sound like a big
+deal to some people, but having such fast and easy to run tests fundamentally
+changes the way you go about testing and even writing code in the first place.
+Linus himself said in his `git talk at Google
+<https://gist.github.com/lorn/1272686/revisions#diff-53c65572127855f1b003db4064a94573R874>`_:
+
+ "... a lot of people seem to think that performance is about doing the
+ same thing, just doing it faster, and that is not true. That is not what
+ performance is all about. If you can do something really fast, really
+ well, people will start using it differently."
+
+In this context Linus was talking about branching and merging,
+but this point also applies to testing. If your tests are slow, unreliable, are
+difficult to write, and require a special setup or special hardware to run,
+then you wait a lot longer to write tests, and you wait a lot longer to run
+tests; this means that tests are likely to break, unlikely to test a lot of
+things, and are unlikely to be rerun once they pass. If your tests are really
+fast, you run them all the time, every time you make a change, and every time
+someone sends you some code. Why trust that someone ran all their tests
+correctly on every change when you can just run them yourself in less time than
+it takes to read his / her test log?
+
+How do I use it?
+================
+
+* :doc:`start` - for new users of KUnit
+* :doc:`usage` - for a more detailed explanation of KUnit features
+* :doc:`api/index` - for the list of KUnit APIs used for testing
diff --git a/Documentation/dev-tools/kunit/start.rst b/Documentation/dev-tools/kunit/start.rst
new file mode 100644
index 0000000000000..6dc229e46bb34
--- /dev/null
+++ b/Documentation/dev-tools/kunit/start.rst
@@ -0,0 +1,180 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============
+Getting Started
+===============
+
+Installing dependencies
+=======================
+KUnit has the same dependencies as the Linux kernel. As long as you can build
+the kernel, you can run KUnit.
+
+KUnit Wrapper
+=============
+Included with KUnit is a simple Python wrapper that helps format the output to
+easily use and read KUnit output. It handles building and running the kernel, as
+well as formatting the output.
+
+The wrapper can be run with:
+
+.. code-block:: bash
+
+ ./tools/testing/kunit/kunit.py run
+
+Creating a kunitconfig
+======================
+The Python script is a thin wrapper around Kbuild as such, it needs to be
+configured with a ``kunitconfig`` file. This file essentially contains the
+regular Kernel config, with the specific test targets as well.
+
+.. code-block:: bash
+
+ git clone -b master https://kunit.googlesource.com/kunitconfig $PATH_TO_KUNITCONFIG_REPO
+ cd $PATH_TO_LINUX_REPO
+ ln -s $PATH_TO_KUNIT_CONFIG_REPO/kunitconfig kunitconfig
+
+You may want to add kunitconfig to your local gitignore.
+
+Verifying KUnit Works
+---------------------
+
+To make sure that everything is set up correctly, simply invoke the Python
+wrapper from your kernel repo:
+
+.. code-block:: bash
+
+ ./tools/testing/kunit/kunit.py
+
+.. note::
+ You may want to run ``make mrproper`` first.
+
+If everything worked correctly, you should see the following:
+
+.. code-block:: bash
+
+ Generating .config ...
+ Building KUnit Kernel ...
+ Starting KUnit Kernel ...
+
+followed by a list of tests that are run. All of them should be passing.
+
+.. note::
+ Because it is building a lot of sources for the first time, the ``Building
+ kunit kernel`` step may take a while.
+
+Writing your first test
+=======================
+
+In your kernel repo let's add some code that we can test. Create a file
+``drivers/misc/example.h`` with the contents:
+
+.. code-block:: c
+
+ int misc_example_add(int left, int right);
+
+create a file ``drivers/misc/example.c``:
+
+.. code-block:: c
+
+ #include <linux/errno.h>
+
+ #include "example.h"
+
+ int misc_example_add(int left, int right)
+ {
+ return left + right;
+ }
+
+Now add the following lines to ``drivers/misc/Kconfig``:
+
+.. code-block:: kconfig
+
+ config MISC_EXAMPLE
+ bool "My example"
+
+and the following lines to ``drivers/misc/Makefile``:
+
+.. code-block:: make
+
+ obj-$(CONFIG_MISC_EXAMPLE) += example.o
+
+Now we are ready to write the test. The test will be in
+``drivers/misc/example-test.c``:
+
+.. code-block:: c
+
+ #include <kunit/test.h>
+ #include "example.h"
+
+ /* Define the test cases. */
+
+ static void misc_example_add_test_basic(struct kunit *test)
+ {
+ KUNIT_EXPECT_EQ(test, 1, misc_example_add(1, 0));
+ KUNIT_EXPECT_EQ(test, 2, misc_example_add(1, 1));
+ KUNIT_EXPECT_EQ(test, 0, misc_example_add(-1, 1));
+ KUNIT_EXPECT_EQ(test, INT_MAX, misc_example_add(0, INT_MAX));
+ KUNIT_EXPECT_EQ(test, -1, misc_example_add(INT_MAX, INT_MIN));
+ }
+
+ static void misc_example_test_failure(struct kunit *test)
+ {
+ KUNIT_FAIL(test, "This test never passes.");
+ }
+
+ static struct kunit_case misc_example_test_cases[] = {
+ KUNIT_CASE(misc_example_add_test_basic),
+ KUNIT_CASE(misc_example_test_failure),
+ {}
+ };
+
+ static struct kunit_suite misc_example_test_suite = {
+ .name = "misc-example",
+ .test_cases = misc_example_test_cases,
+ };
+ kunit_test_suite(misc_example_test_suite);
+
+Now add the following to ``drivers/misc/Kconfig``:
+
+.. code-block:: kconfig
+
+ config MISC_EXAMPLE_TEST
+ bool "Test for my example"
+ depends on MISC_EXAMPLE && KUNIT
+
+and the following to ``drivers/misc/Makefile``:
+
+.. code-block:: make
+
+ obj-$(CONFIG_MISC_EXAMPLE_TEST) += example-test.o
+
+Now add it to your ``kunitconfig``:
+
+.. code-block:: none
+
+ CONFIG_MISC_EXAMPLE=y
+ CONFIG_MISC_EXAMPLE_TEST=y
+
+Now you can run the test:
+
+.. code-block:: bash
+
+ ./tools/testing/kunit/kunit.py
+
+You should see the following failure:
+
+.. code-block:: none
+
+ ...
+ [16:08:57] [PASSED] misc-example:misc_example_add_test_basic
+ [16:08:57] [FAILED] misc-example:misc_example_test_failure
+ [16:08:57] EXPECTATION FAILED at drivers/misc/example-test.c:17
+ [16:08:57] This test never passes.
+ ...
+
+Congrats! You just wrote your first KUnit test!
+
+Next Steps
+==========
+* Check out the :doc:`usage` page for a more
+ in-depth explanation of KUnit.
diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
new file mode 100644
index 0000000000000..049886006dbce
--- /dev/null
+++ b/Documentation/dev-tools/kunit/usage.rst
@@ -0,0 +1,575 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========
+Using KUnit
+===========
+
+The purpose of this document is to describe what KUnit is, how it works, how it
+is intended to be used, and all the concepts and terminology that are needed to
+understand it. This guide assumes a working knowledge of the Linux kernel and
+some basic knowledge of testing.
+
+For a high level introduction to KUnit, including setting up KUnit for your
+project, see :doc:`start`.
+
+Organization of this document
+=============================
+
+This document is organized into two main sections: Testing and Isolating
+Behavior. The first covers what a unit test is and how to use KUnit to write
+them. The second covers how to use KUnit to isolate code and make it possible
+to unit test code that was otherwise un-unit-testable.
+
+Testing
+=======
+
+What is KUnit?
+--------------
+
+"K" is short for "kernel" so "KUnit" is the "(Linux) Kernel Unit Testing
+Framework." KUnit is intended first and foremost for writing unit tests; it is
+general enough that it can be used to write integration tests; however, this is
+a secondary goal. KUnit has no ambition of being the only testing framework for
+the kernel; for example, it does not intend to be an end-to-end testing
+framework.
+
+What is Unit Testing?
+---------------------
+
+A `unit test <https://martinfowler.com/bliki/UnitTest.html>`_ is a test that
+tests code at the smallest possible scope, a *unit* of code. In the C
+programming language that's a function.
+
+Unit tests should be written for all the publicly exposed functions in a
+compilation unit; so that is all the functions that are exported in either a
+*class* (defined below) or all functions which are **not** static.
+
+Writing Tests
+-------------
+
+Test Cases
+~~~~~~~~~~
+
+The fundamental unit in KUnit is the test case. A test case is a function with
+the signature ``void (*)(struct kunit *test)``. It calls a function to be tested
+and then sets *expectations* for what should happen. For example:
+
+.. code-block:: c
+
+ void example_test_success(struct kunit *test)
+ {
+ }
+
+ void example_test_failure(struct kunit *test)
+ {
+ KUNIT_FAIL(test, "This test never passes.");
+ }
+
+In the above example ``example_test_success`` always passes because it does
+nothing; no expectations are set, so all expectations pass. On the other hand
+``example_test_failure`` always fails because it calls ``KUNIT_FAIL``, which is
+a special expectation that logs a message and causes the test case to fail.
+
+Expectations
+~~~~~~~~~~~~
+An *expectation* is a way to specify that you expect a piece of code to do
+something in a test. An expectation is called like a function. A test is made
+by setting expectations about the behavior of a piece of code under test; when
+one or more of the expectations fail, the test case fails and information about
+the failure is logged. For example:
+
+.. code-block:: c
+
+ void add_test_basic(struct kunit *test)
+ {
+ KUNIT_EXPECT_EQ(test, 1, add(1, 0));
+ KUNIT_EXPECT_EQ(test, 2, add(1, 1));
+ }
+
+In the above example ``add_test_basic`` makes a number of assertions about the
+behavior of a function called ``add``; the first parameter is always of type
+``struct kunit *``, which contains information about the current test context;
+the second parameter, in this case, is what the value is expected to be; the
+last value is what the value actually is. If ``add`` passes all of these
+expectations, the test case, ``add_test_basic`` will pass; if any one of these
+expectations fail, the test case will fail.
+
+It is important to understand that a test case *fails* when any expectation is
+violated; however, the test will continue running, potentially trying other
+expectations until the test case ends or is otherwise terminated. This is as
+opposed to *assertions* which are discussed later.
+
+To learn about more expectations supported by KUnit, see :doc:`api/test`.
+
+.. note::
+ A single test case should be pretty short, pretty easy to understand,
+ focused on a single behavior.
+
+For example, if we wanted to properly test the add function above, we would
+create additional tests cases which would each test a different property that an
+add function should have like this:
+
+.. code-block:: c
+
+ void add_test_basic(struct kunit *test)
+ {
+ KUNIT_EXPECT_EQ(test, 1, add(1, 0));
+ KUNIT_EXPECT_EQ(test, 2, add(1, 1));
+ }
+
+ void add_test_negative(struct kunit *test)
+ {
+ KUNIT_EXPECT_EQ(test, 0, add(-1, 1));
+ }
+
+ void add_test_max(struct kunit *test)
+ {
+ KUNIT_EXPECT_EQ(test, INT_MAX, add(0, INT_MAX));
+ KUNIT_EXPECT_EQ(test, -1, add(INT_MAX, INT_MIN));
+ }
+
+ void add_test_overflow(struct kunit *test)
+ {
+ KUNIT_EXPECT_EQ(test, INT_MIN, add(INT_MAX, 1));
+ }
+
+Notice how it is immediately obvious what all the properties that we are testing
+for are.
+
+Assertions
+~~~~~~~~~~
+
+KUnit also has the concept of an *assertion*. An assertion is just like an
+expectation except the assertion immediately terminates the test case if it is
+not satisfied.
+
+For example:
+
+.. code-block:: c
+
+ static void mock_test_do_expect_default_return(struct kunit *test)
+ {
+ struct mock_test_context *ctx = test->priv;
+ struct mock *mock = ctx->mock;
+ int param0 = 5, param1 = -5;
+ const char *two_param_types[] = {"int", "int"};
+ const void *two_params[] = {¶m0, ¶m1};
+ const void *ret;
+
+ ret = mock->do_expect(mock,
+ "test_printk", test_printk,
+ two_param_types, two_params,
+ ARRAY_SIZE(two_params));
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ret);
+ KUNIT_EXPECT_EQ(test, -4, *((int *) ret));
+ }
+
+In this example, the method under test should return a pointer to a value, so
+if the pointer returned by the method is null or an errno, we don't want to
+bother continuing the test since the following expectation could crash the test
+case. `ASSERT_NOT_ERR_OR_NULL(...)` allows us to bail out of the test case if
+the appropriate conditions have not been satisfied to complete the test.
+
+Test Suites
+~~~~~~~~~~~
+
+Now obviously one unit test isn't very helpful; the power comes from having
+many test cases covering all of your behaviors. Consequently it is common to
+have many *similar* tests; in order to reduce duplication in these closely
+related tests most unit testing frameworks provide the concept of a *test
+suite*, in KUnit we call it a *test suite*; all it is is just a collection of
+test cases for a unit of code with a set up function that gets invoked before
+every test cases and then a tear down function that gets invoked after every
+test case completes.
+
+Example:
+
+.. code-block:: c
+
+ static struct kunit_case example_test_cases[] = {
+ KUNIT_CASE(example_test_foo),
+ KUNIT_CASE(example_test_bar),
+ KUNIT_CASE(example_test_baz),
+ {}
+ };
+
+ static struct kunit_suite example_test_suite = {
+ .name = "example",
+ .init = example_test_init,
+ .exit = example_test_exit,
+ .test_cases = example_test_cases,
+ };
+ kunit_test_suite(example_test_suite);
+
+In the above example the test suite, ``example_test_suite``, would run the test
+cases ``example_test_foo``, ``example_test_bar``, and ``example_test_baz``,
+each would have ``example_test_init`` called immediately before it and would
+have ``example_test_exit`` called immediately after it.
+``kunit_test_suite(example_test_suite)`` registers the test suite with the
+KUnit test framework.
+
+.. note::
+ A test case will only be run if it is associated with a test suite.
+
+For a more information on these types of things see the :doc:`api/test`.
+
+Isolating Behavior
+==================
+
+The most important aspect of unit testing that other forms of testing do not
+provide is the ability to limit the amount of code under test to a single unit.
+In practice, this is only possible by being able to control what code gets run
+when the unit under test calls a function and this is usually accomplished
+through some sort of indirection where a function is exposed as part of an API
+such that the definition of that function can be changed without affecting the
+rest of the code base. In the kernel this primarily comes from two constructs,
+classes, structs that contain function pointers that are provided by the
+implementer, and architecture specific functions which have definitions selected
+at compile time.
+
+Classes
+-------
+
+Classes are not a construct that is built into the C programming language;
+however, it is an easily derived concept. Accordingly, pretty much every project
+that does not use a standardized object oriented library (like GNOME's GObject)
+has their own slightly different way of doing object oriented programming; the
+Linux kernel is no exception.
+
+The central concept in kernel object oriented programming is the class. In the
+kernel, a *class* is a struct that contains function pointers. This creates a
+contract between *implementers* and *users* since it forces them to use the
+same function signature without having to call the function directly. In order
+for it to truly be a class, the function pointers must specify that a pointer
+to the class, known as a *class handle*, be one of the parameters; this makes
+it possible for the member functions (also known as *methods*) to have access
+to member variables (more commonly known as *fields*) allowing the same
+implementation to have multiple *instances*.
+
+Typically a class can be *overridden* by *child classes* by embedding the
+*parent class* in the child class. Then when a method provided by the child
+class is called, the child implementation knows that the pointer passed to it is
+of a parent contained within the child; because of this, the child can compute
+the pointer to itself because the pointer to the parent is always a fixed offset
+from the pointer to the child; this offset is the offset of the parent contained
+in the child struct. For example:
+
+.. code-block:: c
+
+ struct shape {
+ int (*area)(struct shape *this);
+ };
+
+ struct rectangle {
+ struct shape parent;
+ int length;
+ int width;
+ };
+
+ int rectangle_area(struct shape *this)
+ {
+ struct rectangle *self = container_of(this, struct shape, parent);
+
+ return self->length * self->width;
+ };
+
+ void rectangle_new(struct rectangle *self, int length, int width)
+ {
+ self->parent.area = rectangle_area;
+ self->length = length;
+ self->width = width;
+ }
+
+In this example (as in most kernel code) the operation of computing the pointer
+to the child from the pointer to the parent is done by ``container_of``.
+
+Faking Classes
+~~~~~~~~~~~~~~
+
+In order to unit test a piece of code that calls a method in a class, the
+behavior of the method must be controllable, otherwise the test ceases to be a
+unit test and becomes an integration test.
+
+A fake just provides an implementation of a piece of code that is different than
+what runs in a production instance, but behaves identically from the standpoint
+of the callers; this is usually done to replace a dependency that is hard to
+deal with, or is slow.
+
+A good example for this might be implementing a fake EEPROM that just stores the
+"contents" in an internal buffer. For example, let's assume we have a class that
+represents an EEPROM:
+
+.. code-block:: c
+
+ struct eeprom {
+ ssize_t (*read)(struct eeprom *this, size_t offset, char *buffer, size_t count);
+ ssize_t (*write)(struct eeprom *this, size_t offset, const char *buffer, size_t count);
+ };
+
+And we want to test some code that buffers writes to the EEPROM:
+
+.. code-block:: c
+
+ struct eeprom_buffer {
+ ssize_t (*write)(struct eeprom_buffer *this, const char *buffer, size_t count);
+ int flush(struct eeprom_buffer *this);
+ size_t flush_count; /* Flushes when buffer exceeds flush_count. */
+ };
+
+ struct eeprom_buffer *new_eeprom_buffer(struct eeprom *eeprom);
+ void destroy_eeprom_buffer(struct eeprom *eeprom);
+
+We can easily test this code by *faking out* the underlying EEPROM:
+
+.. code-block:: c
+
+ struct fake_eeprom {
+ struct eeprom parent;
+ char contents[FAKE_EEPROM_CONTENTS_SIZE];
+ };
+
+ ssize_t fake_eeprom_read(struct eeprom *parent, size_t offset, char *buffer, size_t count)
+ {
+ struct fake_eeprom *this = container_of(parent, struct fake_eeprom, parent);
+
+ count = min(count, FAKE_EEPROM_CONTENTS_SIZE - offset);
+ memcpy(buffer, this->contents + offset, count);
+
+ return count;
+ }
+
+ ssize_t fake_eeprom_write(struct eeprom *this, size_t offset, const char *buffer, size_t count)
+ {
+ struct fake_eeprom *this = container_of(parent, struct fake_eeprom, parent);
+
+ count = min(count, FAKE_EEPROM_CONTENTS_SIZE - offset);
+ memcpy(this->contents + offset, buffer, count);
+
+ return count;
+ }
+
+ void fake_eeprom_init(struct fake_eeprom *this)
+ {
+ this->parent.read = fake_eeprom_read;
+ this->parent.write = fake_eeprom_write;
+ memset(this->contents, 0, FAKE_EEPROM_CONTENTS_SIZE);
+ }
+
+We can now use it to test ``struct eeprom_buffer``:
+
+.. code-block:: c
+
+ struct eeprom_buffer_test {
+ struct fake_eeprom *fake_eeprom;
+ struct eeprom_buffer *eeprom_buffer;
+ };
+
+ static void eeprom_buffer_test_does_not_write_until_flush(struct kunit *test)
+ {
+ struct eeprom_buffer_test *ctx = test->priv;
+ struct eeprom_buffer *eeprom_buffer = ctx->eeprom_buffer;
+ struct fake_eeprom *fake_eeprom = ctx->fake_eeprom;
+ char buffer[] = {0xff};
+
+ eeprom_buffer->flush_count = SIZE_MAX;
+
+ eeprom_buffer->write(eeprom_buffer, buffer, 1);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0);
+
+ eeprom_buffer->write(eeprom_buffer, buffer, 1);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[1], 0);
+
+ eeprom_buffer->flush(eeprom_buffer);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0xff);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[1], 0xff);
+ }
+
+ static void eeprom_buffer_test_flushes_after_flush_count_met(struct kunit *test)
+ {
+ struct eeprom_buffer_test *ctx = test->priv;
+ struct eeprom_buffer *eeprom_buffer = ctx->eeprom_buffer;
+ struct fake_eeprom *fake_eeprom = ctx->fake_eeprom;
+ char buffer[] = {0xff};
+
+ eeprom_buffer->flush_count = 2;
+
+ eeprom_buffer->write(eeprom_buffer, buffer, 1);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0);
+
+ eeprom_buffer->write(eeprom_buffer, buffer, 1);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0xff);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[1], 0xff);
+ }
+
+ static void eeprom_buffer_test_flushes_increments_of_flush_count(struct kunit *test)
+ {
+ struct eeprom_buffer_test *ctx = test->priv;
+ struct eeprom_buffer *eeprom_buffer = ctx->eeprom_buffer;
+ struct fake_eeprom *fake_eeprom = ctx->fake_eeprom;
+ char buffer[] = {0xff, 0xff};
+
+ eeprom_buffer->flush_count = 2;
+
+ eeprom_buffer->write(eeprom_buffer, buffer, 1);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0);
+
+ eeprom_buffer->write(eeprom_buffer, buffer, 2);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0xff);
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[1], 0xff);
+ /* Should have only flushed the first two bytes. */
+ KUNIT_EXPECT_EQ(test, fake_eeprom->contents[2], 0);
+ }
+
+ static int eeprom_buffer_test_init(struct kunit *test)
+ {
+ struct eeprom_buffer_test *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ ctx->fake_eeprom = kunit_kzalloc(test, sizeof(*ctx->fake_eeprom), GFP_KERNEL);
+ ASSERT_NOT_ERR_OR_NULL(test, ctx->fake_eeprom);
+
+ ctx->eeprom_buffer = new_eeprom_buffer(&ctx->fake_eeprom->parent);
+ ASSERT_NOT_ERR_OR_NULL(test, ctx->eeprom_buffer);
+
+ test->priv = ctx;
+
+ return 0;
+ }
+
+ static void eeprom_buffer_test_exit(struct kunit *test)
+ {
+ struct eeprom_buffer_test *ctx = test->priv;
+
+ destroy_eeprom_buffer(ctx->eeprom_buffer);
+ }
+
+.. _kunit-on-non-uml:
+
+KUnit on non-UML architectures
+==============================
+
+By default KUnit uses UML as a way to provide dependencies for code under test.
+Under most circumstances KUnit's usage of UML should be treated as an
+implementation detail of how KUnit works under the hood. Nevertheless, there
+are instances where being able to run architecture specific code, or test
+against real hardware is desirable. For these reasons KUnit supports running on
+other architectures.
+
+Running existing KUnit tests on non-UML architectures
+-----------------------------------------------------
+
+There are some special considerations when running existing KUnit tests on
+non-UML architectures:
+
+* Hardware may not be deterministic, so a test that always passes or fails
+ when run under UML may not always do so on real hardware.
+* Hardware and VM environments may not be hermetic. KUnit tries its best to
+ provide a hermetic environment to run tests; however, it cannot manage state
+ that it doesn't know about outside of the kernel. Consequently, tests that
+ may be hermetic on UML may not be hermetic on other architectures.
+* Some features and tooling may not be supported outside of UML.
+* Hardware and VMs are slower than UML.
+
+None of these are reasons not to run your KUnit tests on real hardware; they are
+only things to be aware of when doing so.
+
+The biggest impediment will likely be that certain KUnit features and
+infrastructure may not support your target environment. For example, at this
+time the KUnit Wrapper (``tools/testing/kunit/kunit.py``) does not work outside
+of UML. Unfortunately, there is no way around this. Using UML (or even just a
+particular architecture) allows us to make a lot of assumptions that make it
+possible to do things which might otherwise be impossible.
+
+Nevertheless, all core KUnit framework features are fully supported on all
+architectures, and using them is straightforward: all you need to do is to take
+your kunitconfig, your Kconfig options for the tests you would like to run, and
+merge them into whatever config your are using for your platform. That's it!
+
+For example, let's say you have the following kunitconfig:
+
+.. code-block:: none
+
+ CONFIG_KUNIT=y
+ CONFIG_KUNIT_EXAMPLE_TEST=y
+
+If you wanted to run this test on an x86 VM, you might add the following config
+options to your ``.config``:
+
+.. code-block:: none
+
+ CONFIG_KUNIT=y
+ CONFIG_KUNIT_EXAMPLE_TEST=y
+ CONFIG_SERIAL_8250=y
+ CONFIG_SERIAL_8250_CONSOLE=y
+
+All these new options do is enable support for a common serial console needed
+for logging.
+
+Next, you could build a kernel with these tests as follows:
+
+
+.. code-block:: bash
+
+ make ARCH=x86 olddefconfig
+ make ARCH=x86
+
+Once you have built a kernel, you could run it on QEMU as follows:
+
+.. code-block:: bash
+
+ qemu-system-x86_64 -enable-kvm \
+ -m 1024 \
+ -kernel arch/x86_64/boot/bzImage \
+ -append 'console=ttyS0' \
+ --nographic
+
+Interspersed in the kernel logs you might see the following:
+
+.. code-block:: none
+
+ TAP version 14
+ # Subtest: example
+ 1..1
+ # example_simple_test: initializing
+ ok 1 - example_simple_test
+ ok 1 - example
+
+Congratulations, you just ran a KUnit test on the x86 architecture!
+
+Writing new tests for other architectures
+-----------------------------------------
+
+The first thing you must do is ask yourself whether it is necessary to write a
+KUnit test for a specific architecture, and then whether it is necessary to
+write that test for a particular piece of hardware. In general, writing a test
+that depends on having access to a particular piece of hardware or software (not
+included in the Linux source repo) should be avoided at all costs.
+
+Even if you only ever plan on running your KUnit test on your hardware
+configuration, other people may want to run your tests and may not have access
+to your hardware. If you write your test to run on UML, then anyone can run your
+tests without knowing anything about your particular setup, and you can still
+run your tests on your hardware setup just by compiling for your architecture.
+
+.. important::
+ Always prefer tests that run on UML to tests that only run under a particular
+ architecture, and always prefer tests that run under QEMU or another easy
+ (and monitarily free) to obtain software environment to a specific piece of
+ hardware.
+
+Nevertheless, there are still valid reasons to write an architecture or hardware
+specific test: for example, you might want to test some code that really belongs
+in ``arch/some-arch/*``. Even so, try your best to write the test so that it
+does not depend on physical hardware: if some of your test cases don't need the
+hardware, only require the hardware for tests that actually need it.
+
+Now that you have narrowed down exactly what bits are hardware specific, the
+actual procedure for writing and running the tests is pretty much the same as
+writing normal KUnit tests. One special caveat is that you have to reset
+hardware state in between test cases; if this is not possible, you may only be
+able to run one test case per invocation.
+
+.. TODO(brendanhiggins@google.com): Add an actual example of an architecture
+ dependent KUnit test.
--
2.22.0.410.gd8fdbe21b5-goog
^ permalink raw reply related
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