Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 2/2] drivers: clk: Add ZynqMP clock driver
From: Philippe Ombredanne @ 2018-01-09 12:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515449797-5629-3-git-send-email-jollys@xilinx.com>

Jolly,

On Mon, Jan 8, 2018 at 11:16 PM, Jolly Shah <jolly.shah@xilinx.com> wrote:
> This patch adds CCF compliant clock driver for ZynqMP.
> Clock driver queries supported clock information from
> firmware and regiters pll and output clocks with CCF.
>
> Signed-off-by: Jolly Shah <jollys@xilinx.com>
> Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
> Signed-off-by: Tejas Patel <tejasp@xilinx.com>
> Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@xilinx.com>
> ---

<snip>


>  .../devicetree/bindings/clock/zynq_mpsoc.txt       | 163 +++++
>  drivers/clk/Kconfig                                |   1 +
>  drivers/clk/Makefile                               |   1 +
>  drivers/clk/zynqmp/Kconfig                         |   8 +
>  drivers/clk/zynqmp/Makefile                        |   3 +
>  drivers/clk/zynqmp/clk-gate-zynqmp.c               | 158 +++++
>  drivers/clk/zynqmp/clk-mux-zynqmp.c                | 190 ++++++
>  drivers/clk/zynqmp/clkc.c                          | 707 +++++++++++++++++++++
>  drivers/clk/zynqmp/divider.c                       | 239 +++++++
>  drivers/clk/zynqmp/pll.c                           | 384 +++++++++++
>  include/linux/clk/zynqmp.h                         |  46 ++
>  11 files changed, 1900 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/zynq_mpsoc.txt
>  create mode 100644 drivers/clk/zynqmp/Kconfig
>  create mode 100644 drivers/clk/zynqmp/Makefile
>  create mode 100644 drivers/clk/zynqmp/clk-gate-zynqmp.c
>  create mode 100644 drivers/clk/zynqmp/clk-mux-zynqmp.c
>  create mode 100644 drivers/clk/zynqmp/clkc.c
>  create mode 100644 drivers/clk/zynqmp/divider.c
>  create mode 100644 drivers/clk/zynqmp/pll.c
>  create mode 100644 include/linux/clk/zynqmp.h
>
> diff --git a/Documentation/devicetree/bindings/clock/zynq_mpsoc.txt b/Documentation/devicetree/bindings/clock/zynq_mpsoc.txt
> new file mode 100644
> index 0000000..9061b57
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/zynq_mpsoc.txt
> @@ -0,0 +1,163 @@
> +Device Tree Clock bindings for the Zynq Ultrascale+ MPSoC
> +
> +The Zynq Ultrascale+ MPSoC has several different clk providers,
> +each with there own bindings.
> +The purpose of this document is to document their usage.
> +
> +See clock_bindings.txt for more information on the generic clock bindings.
> +
> +== Clock Controller ==
> +The clock controller is a logical abstraction of Zynq Ultrascale+ MPSoC clock
> +tree. It reads required input clock frequencies from the devicetree and acts
> +as clock provider for all clock consumers of PS clocks.
> +
> +Required properties:
> + - #clock-cells : Must be 1
> + - compatible : "xlnx,zynqmp-clkc"
> + - clocks : list of clock specifiers which are external input clocks to the
> +           given clock controller. Please refer the next section to find
> +           the input clocks for a given controller.
> + - clock-names : list of names of clocks which are exteral input clocks to the
> +                given clock controller. Please refer to the clock bindings
> +                for more details
> +
> +Input clocks for zynqmp Ultrascale+ clock controller:
> +The Zynq UltraScale+ MPSoC has one primary and four alternative reference clock
> +inputs.
> +These required clock inputs are the
> + - pss_ref_clk (PS reference clock)
> + - video_clk (reference clock for video system )
> + - pss_alt_ref_clk (alternative PS reference clock)
> + - aux_ref_clk
> + - gt_crx_ref_clk (transceiver reference clock)
> +
> +The following strings are optional parameters to the 'clock-names' property in
> +order to provide an optional (E)MIO clock source.
> + - swdt0_ext_clk
> + - swdt1_ext_clk
> + - gem0_emio_clk
> + - gem1_emio_clk
> + - gem2_emio_clk
> + - gem3_emio_clk
> + - mio_clk_XX          # with XX = 00..77
> + - mio_clk_50_or_51    #for the mux clock to gem tsu from 50 or 51
> +
> +
> +Output clocks for zynqmp Ultrascale+ clock controller:
> +Output clocks are registered based on clock information received from firmware.
> +Output clock indexes are mentioned below:
> +
> +Clock ID:      Output clock name:
> +-------------------------------------
> +0              iopll
> +1              rpll
> +2              apll
> +3              dpll
> +4              vpll
> +5              iopll_to_fpd
> +6              rpll_to_fpd
> +7              apll_to_lpd
> +8              dpll_to_lpd
> +9              vpll_to_lpd
> +10             acpu
> +11             acpu_half
> +12             dbf_fpd
> +13             dbf_lpd
> +14             dbg_trace
> +15             dbg_tstmp
> +16             dp_video_ref
> +17             dp_audio_ref
> +18             dp_stc_ref
> +19             gdma_ref
> +20             dpdma_ref
> +21             ddr_ref
> +22             sata_ref
> +23             pcie_ref
> +24             gpu_ref
> +25             gpu_pp0_ref
> +26             gpu_pp1_ref
> +27             topsw_main
> +28             topsw_lsbus
> +29             gtgref0_ref
> +30             lpd_switch
> +31             lpd_lsbus
> +32             usb0_bus_ref
> +33             usb1_bus_ref
> +34             usb3_dual_ref
> +35             usb0
> +36             usb1
> +37             cpu_r5
> +38             cpu_r5_core
> +39             csu_spb
> +40             csu_pll
> +41             pcap
> +42             iou_switch
> +43             gem_tsu_ref
> +44             gem_tsu
> +45             gem0_ref
> +46             gem1_ref
> +47             gem2_ref
> +48             gem3_ref
> +49             gem0_tx
> +50             gem1_tx
> +51             gem2_tx
> +52             gem3_tx
> +53             qspi_ref
> +54             sdio0_ref
> +55             sdio1_ref
> +56             uart0_ref
> +57             uart1_ref
> +58             spi0_ref
> +59             spi1_ref
> +60             nand_ref
> +61             i2c0_ref
> +62             i2c1_ref
> +63             can0_ref
> +64             can1_ref
> +65             can0
> +66             can1
> +67             dll_ref
> +68             adma_ref
> +69             timestamp_ref
> +70             ams_ref
> +71             pl0_ref
> +72             pl1_ref
> +73             pl2_ref
> +74             pl3_ref
> +75             wdt
> +76             iopll_int
> +77             iopll_pre_src
> +78             iopll_half
> +79             iopll_int_mux
> +80             iopll_post_src
> +81             rpll_int
> +82             rpll_pre_src
> +83             rpll_half
> +84             rpll_int_mux
> +85             rpll_post_src
> +86             apll_int
> +87             apll_pre_src
> +88             apll_half
> +89             apll_int_mux
> +90             apll_post_src
> +91             dpll_int
> +92             dpll_pre_src
> +93             dpll_half
> +94             dpll_int_mux
> +95             dpll_post_src
> +96             vpll_int
> +97             vpll_pre_src
> +98             vpll_half
> +99             vpll_int_mux
> +100            vpll_post_src
> +101            can0_mio
> +102            can1_mio
> +
> +Example:
> +
> +clkc: clkc at ff5e0020 {
> +       #clock-cells = <1>;
> +       compatible = "xlnx,zynqmp-clkc";
> +       clocks = <&pss_ref_clk>, <&video_clk>, <&pss_alt_ref_clk>, <&aux_ref_clk>, <&gt_crx_ref_clk>;
> +       clock-names = "pss_ref_clk", "video_clk", "pss_alt_ref_clk","aux_ref_clk", "gt_crx_ref_clk"
> +};
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 1c4e1aa..526f4f5 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -239,6 +239,7 @@ source "drivers/clk/samsung/Kconfig"
>  source "drivers/clk/sunxi-ng/Kconfig"
>  source "drivers/clk/tegra/Kconfig"
>  source "drivers/clk/ti/Kconfig"
> +source "drivers/clk/zynqmp/Kconfig"
>  source "drivers/clk/uniphier/Kconfig"
>
>  endmenu
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index f7f761b..d7328b4 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -98,3 +98,4 @@ obj-$(CONFIG_X86)                     += x86/
>  endif
>  obj-$(CONFIG_ARCH_ZX)                  += zte/
>  obj-$(CONFIG_ARCH_ZYNQ)                        += zynq/
> +obj-$(CONFIG_COMMON_CLK_ZYNQMP)         += zynqmp/
> diff --git a/drivers/clk/zynqmp/Kconfig b/drivers/clk/zynqmp/Kconfig
> new file mode 100644
> index 0000000..a6d54e9
> --- /dev/null
> +++ b/drivers/clk/zynqmp/Kconfig
> @@ -0,0 +1,8 @@
> +config COMMON_CLK_ZYNQMP
> +       bool "Support for Xilinx ZynqMP Ultrascale+ clock controllers"
> +       depends on OF
> +       depends on ARCH_ZYNQMP || COMPILE_TEST
> +       help
> +         Support for the Zynqmp Ultrascale clock controller.
> +         It has a dependency on the PMU firmware.
> +         Say Y if you want to support clock support
> diff --git a/drivers/clk/zynqmp/Makefile b/drivers/clk/zynqmp/Makefile
> new file mode 100644
> index 0000000..7d50f7a
> --- /dev/null
> +++ b/drivers/clk/zynqmp/Makefile
> @@ -0,0 +1,3 @@
> +# Zynq Ultrascale+ MPSoC clock specific Makefile
> +
> +obj-$(CONFIG_ARCH_ZYNQMP)      += pll.o clk-gate-zynqmp.o divider.o clk-mux-zynqmp.o clkc.o
> diff --git a/drivers/clk/zynqmp/clk-gate-zynqmp.c b/drivers/clk/zynqmp/clk-gate-zynqmp.c
> new file mode 100644
> index 0000000..45eeed8
> --- /dev/null
> +++ b/drivers/clk/zynqmp/clk-gate-zynqmp.c
> @@ -0,0 +1,158 @@
> +/*
> + * Zynq UltraScale+ MPSoC clock controller
> + *
> + *  Copyright (C) 2016-2017 Xilinx
> + *
> + * SPDX-License-Identifier:     GPL-2.0+

This tag should be on the fist line this way:
// SPDX-License-Identifier: GPL-2.0+

> +
> +#include <linux/clk-provider.h>
> +#include <linux/clk/zynqmp.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/string.h>
> +
> +/**
> + * struct clk_gate - gating clock
> + *
> + * @hw:        handle between common and hardware-specific interfaces
> + * @flags:     hardware-specific flags
> + * @clk_id:    Id of clock
> + */
> +struct zynqmp_clk_gate {
> +       struct clk_hw hw;
> +       u8 flags;
> +       u32 clk_id;
> +};
> +
> +#define to_zynqmp_clk_gate(_hw) container_of(_hw, struct zynqmp_clk_gate, hw)
> +
> +/**
> + * zynqmp_clk_gate_enable - Enable clock
> + * @hw: handle between common and hardware-specific interfaces
> + *
> + * Return: 0 always
> + */
> +static int zynqmp_clk_gate_enable(struct clk_hw *hw)
> +{
> +       struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 clk_id = gate->clk_id;
> +       int ret = 0;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_enable)
> +               return -ENXIO;
> +
> +       ret = eemi_ops->clock_enable(clk_id);
> +
> +       if (ret)
> +               pr_warn_once("%s() clock enabled failed for %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +
> +       return 0;
> +}
> +
> +/*
> + * zynqmp_clk_gate_disable - Disable clock
> + * @hw: handle between common and hardware-specific interfaces
> + */
> +static void zynqmp_clk_gate_disable(struct clk_hw *hw)
> +{
> +       struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 clk_id = gate->clk_id;
> +       int ret = 0;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_disable)
> +               return;
> +
> +       ret = eemi_ops->clock_disable(clk_id);
> +
> +       if (ret)
> +               pr_warn_once("%s() clock disable failed for %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +}
> +
> +/**
> + * zynqmp_clk_gate_is_enable - Check clock state
> + * @hw: handle between common and hardware-specific interfaces
> + *
> + * Return: 1 if enabled
> + *         0 if disabled
> + */
> +static int zynqmp_clk_gate_is_enabled(struct clk_hw *hw)
> +{
> +       struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 clk_id = gate->clk_id;
> +       int state, ret;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_getstate)
> +               return 0;
> +
> +       ret = eemi_ops->clock_getstate(clk_id, &state);
> +       if (ret)
> +               pr_warn_once("%s() clock get state failed for %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +
> +       return state ? 1 : 0;
> +}
> +
> +const struct clk_ops zynqmp_clk_gate_ops = {
> +       .enable = zynqmp_clk_gate_enable,
> +       .disable = zynqmp_clk_gate_disable,
> +       .is_enabled = zynqmp_clk_gate_is_enabled,
> +};
> +EXPORT_SYMBOL_GPL(zynqmp_clk_gate_ops);
> +
> +/**
> + * zynqmp_clk_register_gate - register a gate clock with the clock framework
> + * @dev: device that is registering this clock
> + * @name: name of this clock
> + * @clk_id: Id of this clock
> + * @parents: name of this clock's parents
> + * @num_parents: number of parents
> + * @flags: framework-specific flags for this clock
> + * @clk_gate_flags: gate-specific flags for this clock
> + *
> + * Return: clock handle of the registered clock gate
> + */
> +struct clk *zynqmp_clk_register_gate(struct device *dev, const char *name,
> +                                    u32 clk_id, const char * const *parents,
> +                                    u8 num_parents, unsigned long flags,
> +                                    u8 clk_gate_flags)
> +{
> +       struct zynqmp_clk_gate *gate;
> +       struct clk *clk;
> +       struct clk_init_data init;
> +
> +       /* allocate the gate */
> +       gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> +       if (!gate)
> +               return ERR_PTR(-ENOMEM);
> +
> +       init.name = name;
> +       init.ops = &zynqmp_clk_gate_ops;
> +       init.flags = flags;
> +       init.parent_names = parents;
> +       init.num_parents = num_parents;
> +
> +       /* struct clk_gate assignments */
> +       gate->flags = clk_gate_flags;
> +       gate->hw.init = &init;
> +       gate->clk_id = clk_id;
> +
> +       clk = clk_register(dev, &gate->hw);
> +
> +       if (IS_ERR(clk))
> +               kfree(gate);
> +
> +       return clk;
> +}
> diff --git a/drivers/clk/zynqmp/clk-mux-zynqmp.c b/drivers/clk/zynqmp/clk-mux-zynqmp.c
> new file mode 100644
> index 0000000..ee36244
> --- /dev/null
> +++ b/drivers/clk/zynqmp/clk-mux-zynqmp.c
> @@ -0,0 +1,190 @@
> +/*
> + * Zynq UltraScale+ MPSoC mux
> + *
> + *  Copyright (C) 2016-2017 Xilinx
> + *
> + * SPDX-License-Identifier:     GPL-2.0+

This tag should be on the fist line this way:
// SPDX-License-Identifier: GPL-2.0+

> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/clk/zynqmp.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +
> +/*
> + * DOC: basic adjustable multiplexer clock that cannot gate
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare only ensures that parents are prepared
> + * enable - clk_enable only ensures that parents are enabled
> + * rate - rate is only affected by parent switching.  No clk_set_rate support
> + * parent - parent is adjustable through clk_set_parent
> + */
> +
> +/**
> + * struct zynqmp_clk_mux - multiplexer clock
> + *
> + * @hw: handle between common and hardware-specific interfaces
> + * @flags: hardware-specific flags
> + * @clk_id: Id of clock
> + */
> +struct zynqmp_clk_mux {
> +       struct clk_hw hw;
> +       u8 flags;
> +       u32 clk_id;
> +};
> +
> +#define to_zynqmp_clk_mux(_hw) container_of(_hw, struct zynqmp_clk_mux, hw)
> +
> +/**
> + * zynqmp_clk_mux_get_parent - Get parent of clock
> + * @hw: handle between common and hardware-specific interfaces
> + *
> + * Return: Parent index
> + */
> +static u8 zynqmp_clk_mux_get_parent(struct clk_hw *hw)
> +{
> +       struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 clk_id = mux->clk_id;
> +       u32 val;
> +       int ret;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_getparent)
> +               return -ENXIO;
> +
> +       ret = eemi_ops->clock_getparent(clk_id, &val);
> +
> +       if (ret)
> +               pr_warn_once("%s() getparent failed for clock: %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +
> +       if (val && (mux->flags & CLK_MUX_INDEX_BIT))
> +               val = ffs(val) - 1;
> +
> +       if (val && (mux->flags & CLK_MUX_INDEX_ONE))
> +               val--;
> +
> +       return val;
> +}
> +
> +/**
> + * zynqmp_clk_mux_set_parent - Set parent of clock
> + * @hw: handle between common and hardware-specific interfaces
> + * @index: Parent index
> + *
> + * Return: 0 always
> + */
> +static int zynqmp_clk_mux_set_parent(struct clk_hw *hw, u8 index)
> +{
> +       struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 clk_id = mux->clk_id;
> +       int ret;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_setparent)
> +               return -ENXIO;
> +
> +       if (mux->flags & CLK_MUX_INDEX_BIT)
> +               index = 1 << index;
> +
> +       if (mux->flags & CLK_MUX_INDEX_ONE)
> +               index++;
> +
> +       ret = eemi_ops->clock_setparent(clk_id, index);
> +
> +       if (ret)
> +               pr_warn_once("%s() set parent failed for clock: %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +
> +       return 0;
> +}
> +
> +const struct clk_ops zynqmp_clk_mux_ops = {
> +       .get_parent = zynqmp_clk_mux_get_parent,
> +       .set_parent = zynqmp_clk_mux_set_parent,
> +       .determine_rate = __clk_mux_determine_rate,
> +};
> +EXPORT_SYMBOL_GPL(zynqmp_clk_mux_ops);
> +
> +const struct clk_ops zynqmp_clk_mux_ro_ops = {
> +       .get_parent = zynqmp_clk_mux_get_parent,
> +};
> +EXPORT_SYMBOL_GPL(zynqmp_clk_mux_ro_ops);
> +
> +/**
> + * zynqmp_clk_register_mux_table - register a mux table with the clock framework
> + * @dev: device that is registering this clock
> + * @name: name of this clock
> + * @clk_id: Id of this clock
> + * @parent_names: name of this clock's parents
> + * @num_parents: number of parents
> + * @flags: framework-specific flags for this clock
> + * @clk_mux_flags: mux-specific flags for this clock
> + *
> + * Return: clock handle of the registered clock mux
> + */
> +struct clk *zynqmp_clk_register_mux_table(struct device *dev, const char *name,
> +                                         u32 clk_id,
> +                                         const char * const *parent_names,
> +                                         u8 num_parents,
> +                                         unsigned long flags,
> +                                         u8 clk_mux_flags)
> +{
> +       struct zynqmp_clk_mux *mux;
> +       struct clk *clk;
> +       struct clk_init_data init;
> +
> +       /* allocate the mux */
> +       mux = kzalloc(sizeof(*mux), GFP_KERNEL);
> +       if (!mux)
> +               return ERR_PTR(-ENOMEM);
> +
> +       init.name = name;
> +       if (clk_mux_flags & CLK_MUX_READ_ONLY)
> +               init.ops = &zynqmp_clk_mux_ro_ops;
> +       else
> +               init.ops = &zynqmp_clk_mux_ops;
> +       init.flags = flags;
> +       init.parent_names = parent_names;
> +       init.num_parents = num_parents;
> +
> +       /* struct clk_mux assignments */
> +       mux->flags = clk_mux_flags;
> +       mux->hw.init = &init;
> +       mux->clk_id = clk_id;
> +
> +       clk = clk_register(dev, &mux->hw);
> +
> +       if (IS_ERR(clk))
> +               kfree(mux);
> +
> +       return clk;
> +}
> +EXPORT_SYMBOL_GPL(zynqmp_clk_register_mux_table);
> +
> +/**
> + * zynqmp_clk_register_mux - register a mux clock with the clock framework
> + * @dev: device that is registering this clock
> + * @name: name of this clock
> + * @clk_id: Id of this clock
> + * @parent_names: name of this clock's parents
> + * @num_parents: number of parents
> + * @flags: framework-specific flags for this clock
> + * @clk_mux_flags: mux-specific flags for this clock
> + *
> + * Return: clock handle of the registered clock mux
> + */
> +struct clk *zynqmp_clk_register_mux(struct device *dev, const char *name,
> +                                   u32 clk_id, const char **parent_names,
> +                                   u8 num_parents, unsigned long flags,
> +                                   u8 clk_mux_flags)
> +{
> +       return zynqmp_clk_register_mux_table(dev, name, clk_id, parent_names,
> +                                            num_parents, flags, clk_mux_flags);
> +}
> +EXPORT_SYMBOL_GPL(zynqmp_clk_register_mux);
> diff --git a/drivers/clk/zynqmp/clkc.c b/drivers/clk/zynqmp/clkc.c
> new file mode 100644
> index 0000000..36bf1c1
> --- /dev/null
> +++ b/drivers/clk/zynqmp/clkc.c
> @@ -0,0 +1,707 @@
> +/*
> + * Zynq UltraScale+ MPSoC clock controller
> + *
> + *  Copyright (C) 2016-2017 Xilinx
> + *
> + * SPDX-License-Identifier:     GPL-2.0+

This tag should be on the fist line this way:
// SPDX-License-Identifier: GPL-2.0+

> + *
> + * Based on drivers/clk/zynq/clkc.c
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clk/zynqmp.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +
> +#define MAX_PARENT                     100
> +#define MAX_NODES                      6
> +#define MAX_NAME_LEN                   50
> +#define MAX_CLOCK                      300
> +
> +#define CLK_INIT_ENABLE_SHIFT           1
> +#define CLK_TYPE_SHIFT                  2
> +
> +#define PM_API_PAYLOAD_LEN             3
> +
> +#define NA_PARENT                      -1
> +#define DUMMY_PARENT                   -2
> +
> +#define CLK_TYPE_FIELD_LEN             4
> +#define CLK_TOPOLOGY_NODE_OFFSET       16
> +#define NODES_PER_RESP                 3
> +
> +#define CLK_TYPE_FIELD_MASK            0xF
> +#define CLK_FLAG_FIELD_SHIFT           8
> +#define CLK_FLAG_FIELD_MASK            0x3FFF
> +#define CLK_TYPE_FLAG_FIELD_SHIFT      24
> +#define CLK_TYPE_FLAG_FIELD_MASK       0xFF
> +
> +#define CLK_PARENTS_ID_LEN              16
> +#define CLK_PARENTS_ID_MASK            0xFFFF
> +
> +/* Flags for parents */
> +#define PARENT_CLK_SELF                        0
> +#define PARENT_CLK_NODE1               1
> +#define PARENT_CLK_NODE2               2
> +#define PARENT_CLK_NODE3               3
> +#define PARENT_CLK_NODE4               4
> +#define PARENT_CLK_EXTERNAL            5
> +
> +#define END_OF_CLK_NAME                        "END_OF_CLK"
> +#define RESERVED_CLK_NAME              ""
> +
> +#define CLK_VALID_MASK                 0x1
> +#define CLK_INIT_ENABLE_MASK           (0x1 << CLK_INIT_ENABLE_SHIFT)
> +
> +enum clk_type {
> +       CLK_TYPE_OUTPUT,
> +       CLK_TYPE_EXTERNAL,
> +};
> +
> +/**
> + * struct clock_parent - Structure for parent of clock
> + * @id:        Parent clock ID
> + * @flag: Parent flags
> + */
> +struct clock_parent {
> +       char name[MAX_NAME_LEN];
> +       int id;
> +       u32 flag;
> +};
> +
> +/**
> + * struct clock_topology - Structure for topology of clock
> + * @type: Type of topology
> + * @flag: Topology flags
> + * @type_flag: Topology type specific flag
> + */
> +struct clock_topology {
> +       u32 type;
> +       u32 flag;
> +       u32 type_flag;
> +};
> +
> +/**
> + * struct zynqmp_clock - Structure for clock
> + * @clk_name: Clock name
> + * @valid: Validity flag of clock
> + * @init_enable: init_enable flag of clock
> + * @topology: structure of topology of clock
> + * @num_node: Number of nodes present in topology
> + * @parent: structure of parent of clock
> + * @num_parents: Number of parents of clock
> + * @type: Type of clock
> + */
> +struct zynqmp_clock {
> +       char clk_name[MAX_NAME_LEN];
> +       u32 valid;
> +       u32 init_enable;
> +       enum clk_type type;
> +       struct clock_topology node[MAX_NODES];
> +       u32 num_nodes;
> +       struct clock_parent parent[MAX_PARENT];
> +       u32 num_parents;
> +};
> +
> +static const char clk_type_postfix[][10] = {
> +       [TYPE_INVALID] = "",
> +       [TYPE_MUX] = "_mux",
> +       [TYPE_GATE] = "",
> +       [TYPE_DIV1] = "_div1",
> +       [TYPE_DIV2] = "_div2",
> +       [TYPE_FIXEDFACTOR] = "_ff",
> +       [TYPE_PLL] = ""
> +};
> +
> +static struct zynqmp_clock clock[MAX_CLOCK];
> +static struct clk_onecell_data zynqmp_clk_data;
> +static struct clk *zynqmp_clks[MAX_CLOCK];
> +static unsigned int clock_max_idx;
> +static const struct zynqmp_eemi_ops *eemi_ops;
> +
> +/**
> + * is_valid_clock - Check whether clock is valid or not
> + * @clk_id: Clock Index
> + * @valid: 1: if clock is valid
> + *         0: invalid clock
> + *
> + * Return: 0 Success
> + *         Error code: Failure
> + */
> +static int is_valid_clock(u32 clk_id, u32 *valid)
> +{
> +       if (clk_id < 0 || clk_id > clock_max_idx)
> +               return -ENODEV;
> +
> +       *valid = clock[clk_id].valid;
> +
> +       return *valid ? 0 : -EINVAL;
> +}
> +
> +/**
> + * zynqmp_get_clock_name - Get name of clock from clock index
> + * @clk_id: Clock index
> + * @clk_name: Name of clock
> + *
> + * Return: 0: Success
> + *         Error code: failure
> + */
> +static int zynqmp_get_clock_name(u32 clk_id, char *clk_name)
> +{
> +       int ret;
> +       u32 valid;
> +
> +       ret = is_valid_clock(clk_id, &valid);
> +       if (!ret && valid) {
> +               strncpy(clk_name, clock[clk_id].clk_name, MAX_NAME_LEN);
> +               return 0;
> +       } else {
> +               return ret;
> +       }
> +}
> +
> +/**
> + * get_clock_type - Get type of clock
> + * @clk_id: Clock Index
> + * @type: Clock type: CLK_TYPE_OUTPUT or CLK_TYPE_EXTERNAL
> + *
> + * Return: 0: Success
> + *         Error code: failure
> + */
> +static int get_clock_type(u32 clk_id, u32 *type)
> +{
> +       int ret;
> +       u32 valid;
> +
> +       ret = is_valid_clock(clk_id, &valid);
> +       if (!ret && valid) {
> +               *type = clock[clk_id].type;
> +               return 0;
> +       } else {
> +               return ret;
> +       }
> +}
> +
> +/**
> + * zynqmp_pm_clock_get_name - Get the name of clock for given id
> + * @clock_id: ID of the clock to be queried
> + * @name: Name of given clock
> + *
> + * This function is used to get name of clock specified by given
> + * clock ID.
> + *
> + * Return: Returns status, in case of error name would be 0.
> + */
> +static int zynqmp_pm_clock_get_name(u32 clock_id, char *name)
> +{
> +       struct zynqmp_pm_query_data qdata = {0};
> +       u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +       qdata.qid = PM_QID_CLOCK_GET_NAME;
> +       qdata.arg1 = clock_id;
> +
> +       eemi_ops->query_data(qdata, ret_payload);
> +       memcpy(name, ret_payload, CLK_GET_NAME_RESP_LEN);
> +
> +       return 0;
> +}
> +
> +/**
> + * zynqmp_pm_clock_get_topology - Get the topology of clock for given id
> + * @clock_id: ID of the clock to be queried
> + * @index: Node index of clock topology
> + * @topology: Buffer to store nodes in topology and flags
> + *
> + * This function is used to get topology information for the clock
> + * specified by given clock ID.
> + *
> + * This API will return 3 node of topology with a single response. To get
> + * other nodes, master should call same API in loop with new
> + * index till error is returned. E.g First call should have
> + * index 0 which will return nodes 0,1 and 2. Next call, index
> + * should be 3 which will return nodes 3,4 and 5 and so on.
> + *
> + * Return: Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_get_topology(u32 clock_id, u32 index, u32 *topology)
> +{
> +       struct zynqmp_pm_query_data qdata = {0};
> +       u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +       qdata.qid = PM_QID_CLOCK_GET_TOPOLOGY;
> +       qdata.arg1 = clock_id;
> +       qdata.arg2 = index;
> +
> +       eemi_ops->query_data(qdata, ret_payload);
> +       memcpy(topology, &ret_payload[1], CLK_GET_TOPOLOGY_RESP_WORDS * 4);
> +
> +       return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_clock_get_fixedfactor_params - Get the clock's fixed factor
> + *                                         parameters for fixed clock
> + * @clock_id: Clock ID
> + * @mul: Multiplication value
> + * @div: Divisor value
> + *
> + * This function is used to get fixed factor parameers for the fixed
> + * clock. This API is application only for the fixed clock.
> + *
> + * Return: Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_get_fixedfactor_params(u32 clock_id,
> +                                                 u32 *mul,
> +                                                 u32 *div)
> +{
> +       struct zynqmp_pm_query_data qdata = {0};
> +       u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +       qdata.qid = PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS;
> +       qdata.arg1 = clock_id;
> +
> +       eemi_ops->query_data(qdata, ret_payload);
> +       *mul = ret_payload[1];
> +       *div = ret_payload[2];
> +
> +       return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_clock_get_parents - Get the first 3 parents of clock for given id
> + * @clock_id: Clock ID
> + * @index: Parent index
> + * @parents: 3 parents of the given clock
> + *
> + * This function is used to get 3 parents for the clock specified by
> + * given clock ID.
> + *
> + * This API will return 3 parents with a single response. To get
> + * other parents, master should call same API in loop with new
> + * parent index till error is returned. E.g First call should have
> + * index 0 which will return parents 0,1 and 2. Next call, index
> + * should be 3 which will return parent 3,4 and 5 and so on.
> + *
> + * Return: Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_get_parents(u32 clock_id, u32 index, u32 *parents)
> +{
> +       struct zynqmp_pm_query_data qdata = {0};
> +       u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +       qdata.qid = PM_QID_CLOCK_GET_PARENTS;
> +       qdata.arg1 = clock_id;
> +       qdata.arg2 = index;
> +
> +       eemi_ops->query_data(qdata, ret_payload);
> +       memcpy(parents, &ret_payload[1], CLK_GET_PARENTS_RESP_WORDS * 4);
> +
> +       return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_clock_get_attributes - Get the attributes of clock for given id
> + * @clock_id: Clock ID
> + * @attributes: Clock attributes
> + *
> + * This function is used to get clock's attributes(e.g. valid, clock type, etc).
> + *
> + * Return: Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_get_attributes(u32 clock_id, u32 *attr)
> +{
> +       struct zynqmp_pm_query_data qdata = {0};
> +       u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +       qdata.qid = PM_QID_CLOCK_GET_ATTRIBUTES;
> +       qdata.arg1 = clock_id;
> +
> +       eemi_ops->query_data(qdata, ret_payload);
> +       memcpy(attr, &ret_payload[1], CLK_GET_ATTR_RESP_WORDS * 4);
> +
> +       return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * clock_get_topology: Get topology of clock from firmware using PM_API
> + * @clk_id: Clock Index
> + * @clk_topology: Structure of clock topology
> + * @num_nodes: number of nodes
> + *
> + * Return: 0: Success
> + *         Error Code: Failure
> + */
> +static int clock_get_topology(u32 clk_id, struct clock_topology *clk_topology,
> +                             u32 *num_nodes)
> +{
> +       int j, k = 0, ret;
> +       u32 pm_resp[PM_API_PAYLOAD_LEN] = {0};
> +
> +       *num_nodes = 0;
> +       for (j = 0; j <= MAX_NODES; j += 3) {
> +               ret = zynqmp_pm_clock_get_topology(clk_id, j, pm_resp);
> +               if (ret)
> +                       return ret;
> +               for (k = 0; k < PM_API_PAYLOAD_LEN; k++) {
> +                       if (!(pm_resp[k] & CLK_TYPE_FIELD_MASK))
> +                               goto done;
> +                       clk_topology[*num_nodes].type = pm_resp[k] &
> +                                                       CLK_TYPE_FIELD_MASK;
> +                       clk_topology[*num_nodes].flag =
> +                                       (pm_resp[k] >> CLK_FLAG_FIELD_SHIFT) &
> +                                       CLK_FLAG_FIELD_MASK;
> +                       clk_topology[*num_nodes].type_flag =
> +                               (pm_resp[k] >> CLK_TYPE_FLAG_FIELD_SHIFT) &
> +                               CLK_TYPE_FLAG_FIELD_MASK;
> +                       (*num_nodes)++;
> +               }
> +       }
> +done:
> +       return 0;
> +}
> +
> +/**
> + * clock_get_parents: Get parents info from firmware using PM_API
> + * @clk_id: Clock Index
> + * @parent: Structure of parent information
> + * @num_parents: Total number of parents
> + *
> + * Return: 0: Success
> + *         Error code: Failure
> + */
> +static int clock_get_parents(u32 clk_id, struct clock_parent *parents,
> +                            u32 *num_parents)
> +{
> +       int j = 0, k, ret, total_parents = 0;
> +       u32 pm_resp[PM_API_PAYLOAD_LEN] = {0};
> +
> +       do {
> +               /* Get parents from firmware */
> +               ret = zynqmp_pm_clock_get_parents(clk_id, j, pm_resp);
> +               if (ret)
> +                       return ret;
> +
> +               for (k = 0; k < PM_API_PAYLOAD_LEN; k++) {
> +                       if (pm_resp[k] == (u32)NA_PARENT) {
> +                               *num_parents = total_parents;
> +                               return 0;
> +                       }
> +
> +                       parents[k + j].id = pm_resp[k] & CLK_PARENTS_ID_MASK;
> +                       if (parents[k + j].id == DUMMY_PARENT) {
> +                               strncpy(parents[k + j].name,
> +                                       "dummy_name", MAX_NAME_LEN);
> +                               parents[k + j].flag = 0;
> +                       } else {
> +                               parents[k + j].flag = pm_resp[k] >>
> +                                                       CLK_PARENTS_ID_LEN;
> +                               if (zynqmp_get_clock_name(parents[k + j].id,
> +                                                         parents[k + j].name))
> +                                       continue;
> +                       }
> +                       total_parents++;
> +               }
> +               j += PM_API_PAYLOAD_LEN;
> +       } while (total_parents <= MAX_PARENT);
> +       return 0;
> +}
> +
> +/**
> + * get_parent_list: Create list of parents name
> + * @np:                Device node
> + * @clk_id: Clock Index
> + * @parent_list List of parent's name
> + * @num_parents: Total number of parents
> + *
> + # Return: 0: Success
> + *         Error code: Failure
> + */
> +static int get_parent_list(struct device_node *np, u32 clk_id,
> +                          const char **parent_list, u32 *num_parents)
> +{
> +       int i = 0, ret;
> +       u32 total_parents = clock[clk_id].num_parents;
> +       struct clock_topology *clk_nodes;
> +       struct clock_parent *parents;
> +
> +       clk_nodes = clock[clk_id].node;
> +       parents = clock[clk_id].parent;
> +
> +       for (i = 0; i < total_parents; i++) {
> +               if (!parents[i].flag) {
> +                       parent_list[i] = parents[i].name;
> +               } else if (parents[i].flag == PARENT_CLK_EXTERNAL) {
> +                       ret = of_property_match_string(np, "clock-names",
> +                                                      parents[i].name);
> +                       if (ret < 0)
> +                               strncpy(parents[i].name,
> +                                       "dummy_name", MAX_NAME_LEN);
> +                       parent_list[i] = parents[i].name;
> +               } else {
> +                       strcat(parents[i].name,
> +                              clk_type_postfix[clk_nodes[parents[i].flag - 1].
> +                              type]);
> +                       parent_list[i] = parents[i].name;
> +               }
> +       }
> +
> +       *num_parents = total_parents;
> +       return 0;
> +}
> +
> +/**
> + * zynqmp_register_clk_topology: Register clock topology
> + * @clk_id: Clock Index
> + * @clk_name: Clock Name
> + * @num_parents: Total number of parents
> + * @parent_names: List of parents name
> + *
> + * Return: 0: Success
> + *         Error code: Failure
> + */
> +static struct clk *zynqmp_register_clk_topology(int clk_id, char *clk_name,
> +                                               int num_parents,
> +                                               const char **parent_names)
> +{
> +       int j, ret;
> +       u32 num_nodes, mult, div;
> +       char *clk_out = NULL;
> +       struct clock_topology *nodes;
> +       struct clk *clk = NULL;
> +
> +       nodes = clock[clk_id].node;
> +       num_nodes = clock[clk_id].num_nodes;
> +
> +       for (j = 0; j < num_nodes; j++) {
> +               if (j != (num_nodes - 1)) {
> +                       clk_out = kasprintf(GFP_KERNEL, "%s%s", clk_name,
> +                                           clk_type_postfix[nodes[j].type]);
> +               } else {
> +                       clk_out = kasprintf(GFP_KERNEL, "%s", clk_name);
> +               }
> +
> +               switch (nodes[j].type) {
> +               case TYPE_MUX:
> +                       clk = zynqmp_clk_register_mux(NULL, clk_out,
> +                                                     clk_id, parent_names,
> +                                                     num_parents,
> +                                                     nodes[j].flag,
> +                                                     nodes[j].type_flag);
> +                       break;
> +               case TYPE_PLL:
> +                       clk = clk_register_zynqmp_pll(clk_out, clk_id,
> +                                                     parent_names, 1,
> +                                                     nodes[j].flag);
> +                       break;
> +               case TYPE_FIXEDFACTOR:
> +                       ret = zynqmp_pm_clock_get_fixedfactor_params(clk_id,
> +                                                                    &mult,
> +                                                                    &div);
> +                       clk = clk_register_fixed_factor(NULL, clk_out,
> +                                                       parent_names[0],
> +                                                       nodes[j].flag, mult,
> +                                                       div);
> +                       break;
> +               case TYPE_DIV1:
> +               case TYPE_DIV2:
> +                       clk = zynqmp_clk_register_divider(NULL, clk_out, clk_id,
> +                                                         nodes[j].type,
> +                                                         parent_names, 1,
> +                                                         nodes[j].flag,
> +                                                         nodes[j].type_flag);
> +                       break;
> +               case TYPE_GATE:
> +                       clk = zynqmp_clk_register_gate(NULL, clk_out, clk_id,
> +                                                      parent_names, 1,
> +                                                      nodes[j].flag,
> +                                                      nodes[j].type_flag);
> +                       break;
> +               default:
> +                       pr_err("%s() Unknown topology for %s\n",
> +                              __func__, clk_out);
> +                       break;
> +               }
> +               if (IS_ERR(clk))
> +                       pr_warn_once("%s() %s register fail with %ld\n",
> +                                    __func__, clk_name, PTR_ERR(clk));
> +
> +               parent_names[0] = clk_out;
> +       }
> +       kfree(clk_out);
> +       return clk;
> +}
> +
> +/**
> + * zynqmp_register_clocks: Register clocks
> + * @np: Device node
> + *
> + * Return: 0: Success
> + *         Error code: failure
> + */
> +static int zynqmp_register_clocks(struct device_node *np)
> +{
> +       int ret;
> +       u32 i, total_parents = 0, type = 0;
> +       const char *parent_names[MAX_PARENT];
> +
> +       for (i = 0; i < clock_max_idx; i++) {
> +               char clk_name[MAX_NAME_LEN];
> +
> +               /* get clock name, continue to next clock if name not found */
> +               if (zynqmp_get_clock_name(i, clk_name))
> +                       continue;
> +
> +               /* Check if clock is valid and output clock.
> +                * Do not regiter invalid or external clock.
> +                */
> +               ret = get_clock_type(i, &type);
> +               if (ret || type != CLK_TYPE_OUTPUT)
> +                       continue;
> +
> +               /* Get parents of clock*/
> +               if (get_parent_list(np, i, parent_names, &total_parents)) {
> +                       WARN_ONCE(1, "No parents found for %s\n",
> +                                 clock[i].clk_name);
> +                       continue;
> +               }
> +
> +               zynqmp_clks[i] = zynqmp_register_clk_topology(i, clk_name,
> +                                                             total_parents,
> +                                                             parent_names);
> +
> +               /* Enable clock if init_enable flag is 1 */
> +               if (clock[i].init_enable)
> +                       clk_prepare_enable(zynqmp_clks[i]);
> +       }
> +
> +       for (i = 0; i < clock_max_idx; i++) {
> +               if (IS_ERR(zynqmp_clks[i])) {
> +                       pr_err("Zynq Ultrascale+ MPSoC clk %s: register failed with %ld\n",
> +                              clock[i].clk_name, PTR_ERR(zynqmp_clks[i]));
> +                       WARN_ON(1);
> +               }
> +       }
> +       return 0;
> +}
> +
> +/**
> + * zynqmp_get_clock_info - Get clock information from firmware using PM_API
> + */
> +static void zynqmp_get_clock_info(void)
> +{
> +       int i, ret;
> +       u32 attr, type = 0;
> +
> +       memset(clock, 0, sizeof(clock));
> +       for (i = 0; i < MAX_CLOCK; i++) {
> +               zynqmp_pm_clock_get_name(i, clock[i].clk_name);
> +               if (!strncmp(clock[i].clk_name, END_OF_CLK_NAME,
> +                            MAX_NAME_LEN)) {
> +                       clock_max_idx = i;
> +                       break;
> +               } else if (!strncmp(clock[i].clk_name, RESERVED_CLK_NAME,
> +                                   MAX_NAME_LEN)) {
> +                       continue;
> +               }
> +
> +               ret = zynqmp_pm_clock_get_attributes(i, &attr);
> +               if (ret)
> +                       continue;
> +
> +               clock[i].valid = attr & CLK_VALID_MASK;
> +               clock[i].init_enable = !!(attr & CLK_INIT_ENABLE_MASK);
> +               clock[i].type = attr >> CLK_TYPE_SHIFT ? CLK_TYPE_EXTERNAL :
> +                                                       CLK_TYPE_OUTPUT;
> +       }
> +
> +       /* Get topology of all clock */
> +       for (i = 0; i < clock_max_idx; i++) {
> +               ret = get_clock_type(i, &type);
> +               if (ret || type != CLK_TYPE_OUTPUT)
> +                       continue;
> +
> +               ret = clock_get_topology(i, clock[i].node, &clock[i].num_nodes);
> +               if (ret)
> +                       continue;
> +
> +               ret = clock_get_parents(i, clock[i].parent,
> +                                       &clock[i].num_parents);
> +               if (ret)
> +                       continue;
> +       }
> +}
> +
> +/**
> + * zynqmp_clk_setup -  Setup the clock framework and register clocks
> + * @np: Device node
> + */
> +static void __init zynqmp_clk_setup(struct device_node *np)
> +{
> +       int idx;
> +
> +       idx = of_property_match_string(np, "clock-names", "pss_ref_clk");
> +       if (idx < 0) {
> +               pr_err("pss_ref_clk not provided\n");
> +               return;
> +       }
> +       idx = of_property_match_string(np, "clock-names", "video_clk");
> +       if (idx < 0) {
> +               pr_err("video_clk not provided\n");
> +               return;
> +       }
> +       idx = of_property_match_string(np, "clock-names", "pss_alt_ref_clk");
> +       if (idx < 0) {
> +               pr_err("pss_alt_ref_clk not provided\n");
> +               return;
> +       }
> +       idx = of_property_match_string(np, "clock-names", "aux_ref_clk");
> +       if (idx < 0) {
> +               pr_err("aux_ref_clk not provided\n");
> +               return;
> +       }
> +       idx = of_property_match_string(np, "clock-names", "gt_crx_ref_clk");
> +       if (idx < 0) {
> +               pr_err("aux_ref_clk not provided\n");
> +               return;
> +       }
> +
> +       zynqmp_get_clock_info();
> +       zynqmp_register_clocks(np);
> +
> +       zynqmp_clk_data.clks = zynqmp_clks;
> +       zynqmp_clk_data.clk_num = clock_max_idx;
> +       of_clk_add_provider(np, of_clk_src_onecell_get, &zynqmp_clk_data);
> +}
> +
> +/**
> + * zynqmp_clock_init -  Initialize zynqmp clocks
> + *
> + * Return: 0 always
> + */
> +static int __init zynqmp_clock_init(void)
> +{
> +       struct device_node *np;
> +
> +       np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp-clkc");
> +       if (!np) {
> +               pr_err("%s: clkc node not found\n", __func__);
> +               of_node_put(np);
> +               return 0;
> +       }
> +
> +       eemi_ops = get_eemi_ops();
> +       if (!eemi_ops || !eemi_ops->query_data) {
> +               pr_err("%s: clk data not found\n", __func__);
> +               of_node_put(np);
> +               return 0;
> +       }
> +
> +       zynqmp_clk_setup(np);
> +
> +       return 0;
> +}
> +arch_initcall(zynqmp_clock_init);
> diff --git a/drivers/clk/zynqmp/divider.c b/drivers/clk/zynqmp/divider.c
> new file mode 100644
> index 0000000..1a1473c
> --- /dev/null
> +++ b/drivers/clk/zynqmp/divider.c
> @@ -0,0 +1,239 @@
> +/*
> + * Zynq UltraScale+ MPSoC Divider support
> + *
> + *  Copyright (C) 2016-2017 Xilinx
> + *
> + * SPDX-License-Identifier:     GPL-2.0+

Same as above: This tag should be on the fist line this way:
// SPDX-License-Identifier: GPL-2.0+

> + *
> + * Adjustable divider clock implementation
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clk/zynqmp.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/string.h>
> +#include <linux/log2.h>
> +
> +/*
> + * DOC: basic adjustable divider clock that cannot gate
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare only ensures that parents are prepared
> + * enable - clk_enable only ensures that parents are enabled
> + * rate - rate is adjustable.  clk->rate = ceiling(parent->rate / divisor)
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define to_zynqmp_clk_divider(_hw)             \
> +       container_of(_hw, struct zynqmp_clk_divider, hw)
> +
> +/**
> + * struct zynqmp_clk_divider - adjustable divider clock
> + *
> + * @hw:        handle between common and hardware-specific interfaces
> + * @flags: Hardware specific flags
> + * @clk_id: Id of clock
> + * @div_type: divisor type (TYPE_DIV1 or TYPE_DIV2)
> + */
> +struct zynqmp_clk_divider {
> +       struct clk_hw hw;
> +       u8 flags;
> +       u32 clk_id;
> +       u32 div_type;
> +};
> +
> +static int zynqmp_divider_get_val(unsigned long parent_rate, unsigned long rate)
> +{
> +       return DIV_ROUND_UP_ULL((u64)parent_rate, rate);
> +}
> +
> +static unsigned long zynqmp_clk_divider_recalc_rate(struct clk_hw *hw,
> +                                                   unsigned long parent_rate)
> +{
> +       struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 clk_id = divider->clk_id;
> +       u32 div_type = divider->div_type;
> +       u32 div, value;
> +       int ret;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_getdivider)
> +               return -ENXIO;
> +
> +       ret = eemi_ops->clock_getdivider(clk_id, &div);
> +
> +       if (ret)
> +               pr_warn_once("%s() get divider failed for %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +
> +       if (div_type == TYPE_DIV1)
> +               value = div & 0xFFFF;
> +       else
> +               value = (div >> 16) & 0xFFFF;
> +
> +       return zynqmp_divider_get_val((u64)parent_rate, value);
> +}
> +
> +static long zynqmp_clk_divider_round_rate(struct clk_hw *hw,
> +                                         unsigned long rate,
> +                                         unsigned long *prate)
> +{
> +       struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 clk_id = divider->clk_id;
> +       u32 div_type = divider->div_type;
> +       u32 bestdiv;
> +       int ret;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_getdivider)
> +               return -ENXIO;
> +
> +       /* if read only, just return current value */
> +       if (divider->flags & CLK_DIVIDER_READ_ONLY) {
> +               ret = eemi_ops->clock_getdivider(clk_id, &bestdiv);
> +
> +               if (ret)
> +                       pr_warn_once("%s() get divider failed for %s, ret = %d\n",
> +                                    __func__, clk_name, ret);
> +               if (div_type == TYPE_DIV1)
> +                       bestdiv = bestdiv & 0xFFFF;
> +               else
> +                       bestdiv  = (bestdiv >> 16) & 0xFFFF;
> +
> +               return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
> +       }
> +
> +       bestdiv = zynqmp_divider_get_val(*prate, rate);
> +
> +       if ((clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) &&
> +           ((clk_hw_get_flags(hw) & CLK_FRAC)))
> +               bestdiv = rate % *prate ? 1 : bestdiv;
> +       *prate = rate * bestdiv;
> +
> +       return rate;
> +}
> +
> +/**
> + * zynqmp_clk_divider_set_rate - Set rate of divider clock
> + * @hw:        handle between common and hardware-specific interfaces
> + * @rate: rate of clock to be set
> + * @parent_rate: rate of parent clock
> + *
> + * Return: 0 always
> + */
> +static int zynqmp_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
> +                                      unsigned long parent_rate)
> +{
> +       struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 clk_id = divider->clk_id;
> +       u32 div_type = divider->div_type;
> +       u32 value, div;
> +       int ret;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_setdivider)
> +               return -ENXIO;
> +
> +       value = zynqmp_divider_get_val(parent_rate, rate);
> +       if (div_type == TYPE_DIV1) {
> +               div = value & 0xFFFF;
> +               div |= ((u16)-1) << 16;
> +       } else {
> +               div = ((u16)-1);
> +               div |= value << 16;
> +       }
> +
> +       ret = eemi_ops->clock_setdivider(clk_id, div);
> +
> +       if (ret)
> +               pr_warn_once("%s() set divider failed for %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +
> +       return 0;
> +}
> +
> +static const struct clk_ops zynqmp_clk_divider_ops = {
> +       .recalc_rate = zynqmp_clk_divider_recalc_rate,
> +       .round_rate = zynqmp_clk_divider_round_rate,
> +       .set_rate = zynqmp_clk_divider_set_rate,
> +};
> +
> +/**
> + * _register_divider - register a divider clock
> + * @dev: device registering this clock
> + * @name: name of this clock
> + * @clk_id: Id of clock
> + * @div_type: Type of divisor
> + * @parents: name of clock's parents
> + * @num_parents: number of parents
> + * @flags: framework-specific flags
> + * @clk_divider_flags: divider-specific flags for this clock
> + *
> + * Return: handle to registered clock divider
> + */
> +static struct clk *_register_divider(struct device *dev, const char *name,
> +                                    u32 clk_id, u32 div_type,
> +                                    const char * const *parents,
> +                                    u8 num_parents, unsigned long flags,
> +                                    u8 clk_divider_flags)
> +{
> +       struct zynqmp_clk_divider *div;
> +       struct clk *clk;
> +       struct clk_init_data init;
> +
> +       /* allocate the divider */
> +       div = kzalloc(sizeof(*div), GFP_KERNEL);
> +       if (!div)
> +               return ERR_PTR(-ENOMEM);
> +
> +       init.name = name;
> +       init.ops = &zynqmp_clk_divider_ops;
> +       init.flags = flags;
> +       init.parent_names = parents;
> +       init.num_parents = num_parents;
> +
> +       /* struct clk_divider assignments */
> +       div->flags = clk_divider_flags;
> +       div->hw.init = &init;
> +       div->clk_id = clk_id;
> +       div->div_type = div_type;
> +
> +       /* register the clock */
> +       clk = clk_register(dev, &div->hw);
> +
> +       if (IS_ERR(clk))
> +               kfree(div);
> +
> +       return clk;
> +}
> +
> +/**
> + * zynqmp_clk_register_divider - register a divider clock
> + * @dev: device registering this clock
> + * @name: name of this clock
> + * @clk_id: Id of clock
> + * @div_type: Type of divisor
> + * @parents: name of clock's parents
> + * @num_parents: number of parents
> + * @flags: framework-specific flags
> + * @clk_divider_flags: divider-specific flags for this clock
> + *
> + * Return: handle to registered clock divider
> + */
> +struct clk *zynqmp_clk_register_divider(struct device *dev, const char *name,
> +                                       u32 clk_id, u32 div_type,
> +                                       const char * const *parents,
> +                                       u8 num_parents, unsigned long flags,
> +                                       u8 clk_divider_flags)
> +{
> +       return _register_divider(dev, name, clk_id, div_type, parents,
> +                                num_parents, flags, clk_divider_flags);
> +}
> +EXPORT_SYMBOL_GPL(zynqmp_clk_register_divider);
> diff --git a/drivers/clk/zynqmp/pll.c b/drivers/clk/zynqmp/pll.c
> new file mode 100644
> index 0000000..75def21
> --- /dev/null
> +++ b/drivers/clk/zynqmp/pll.c
> @@ -0,0 +1,384 @@
> +/*
> + * Zynq UltraScale+ MPSoC PLL driver
> + *
> + *  Copyright (C) 2016-2017 Xilinx
> + *
> + * SPDX-License-Identifier:     GPL-2.0+

Same as above: This tag should be on the fist line this way:
// SPDX-License-Identifier: GPL-2.0+

> + */
> +#include <linux/clk.h>
> +#include <linux/clk/zynqmp.h>
> +#include <linux/clk-provider.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +
> +/**
> + * struct zynqmp_pll - Structure for PLL clock
> + * @hw:                Handle between common and hardware-specific interfaces
> + * @clk_id:    PLL clock ID
> + */
> +struct zynqmp_pll {
> +       struct clk_hw hw;
> +       u32 clk_id;
> +};
> +
> +#define to_zynqmp_pll(_hw)     container_of(_hw, struct zynqmp_pll, hw)
> +
> +/* Register bitfield defines */
> +#define PLLCTRL_FBDIV_MASK     0x7f00
> +#define PLLCTRL_FBDIV_SHIFT    8
> +#define PLLCTRL_BP_MASK                BIT(3)
> +#define PLLCTRL_DIV2_MASK      BIT(16)
> +#define PLLCTRL_RESET_MASK     1
> +#define PLLCTRL_RESET_VAL      1
> +#define PLL_STATUS_LOCKED      1
> +#define PLLCTRL_RESET_SHIFT    0
> +#define PLLCTRL_DIV2_SHIFT     16
> +
> +#define PLL_FBDIV_MIN  25
> +#define PLL_FBDIV_MAX  125
> +
> +#define PS_PLL_VCO_MIN 1500000000
> +#define PS_PLL_VCO_MAX 3000000000UL
> +
> +enum pll_mode {
> +       PLL_MODE_INT,
> +       PLL_MODE_FRAC,
> +};
> +
> +#define FRAC_OFFSET 0x8
> +#define PLLFCFG_FRAC_EN        BIT(31)
> +#define FRAC_DIV  0x10000  /* 2^16 */
> +
> +/**
> + * pll_get_mode - Get mode of PLL
> + * @hw: Handle between common and hardware-specific interfaces
> + *
> + * Return: Mode of PLL
> + */
> +static inline enum pll_mode pll_get_mode(struct clk_hw *hw)
> +{
> +       struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> +       u32 clk_id = clk->clk_id;
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 ret_payload[PAYLOAD_ARG_CNT];
> +       int ret;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->ioctl)
> +               return -ENXIO;
> +
> +       ret = eemi_ops->ioctl(0, IOCTL_GET_PLL_FRAC_MODE, clk_id, 0,
> +                             ret_payload);
> +       if (ret)
> +               pr_warn_once("%s() PLL get frac mode failed for %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +
> +       return ret_payload[1];
> +}
> +
> +/**
> + * pll_set_mode - Set the PLL mode
> + * @hw:                Handle between common and hardware-specific interfaces
> + * @on:                Flag to determine the mode
> + */
> +static inline void pll_set_mode(struct clk_hw *hw, bool on)
> +{
> +       struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> +       u32 clk_id = clk->clk_id;
> +       const char *clk_name = clk_hw_get_name(hw);
> +       int ret;
> +       u32 mode;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->ioctl) {
> +               pr_warn_once("eemi_ops not found\n");
> +               return;
> +       }
> +
> +       if (on)
> +               mode = PLL_MODE_FRAC;
> +       else
> +               mode = PLL_MODE_INT;
> +
> +       ret = eemi_ops->ioctl(0, IOCTL_SET_PLL_FRAC_MODE, clk_id, mode, NULL);
> +       if (ret)
> +               pr_warn_once("%s() PLL set frac mode failed for %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +}
> +
> +/**
> + * zynqmp_pll_round_rate - Round a clock frequency
> + * @hw:                Handle between common and hardware-specific interfaces
> + * @rate:      Desired clock frequency
> + * @prate:     Clock frequency of parent clock
> + *
> + * Return:     Frequency closest to @rate the hardware can generate
> + */
> +static long zynqmp_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +                                 unsigned long *prate)
> +{
> +       u32 fbdiv;
> +       long rate_div, f;
> +
> +       /* Enable the fractional mode if needed */
> +       rate_div = ((rate * FRAC_DIV) / *prate);
> +       f = rate_div % FRAC_DIV;
> +       pll_set_mode(hw, !!f);
> +
> +       if (pll_get_mode(hw) == PLL_MODE_FRAC) {
> +               if (rate > PS_PLL_VCO_MAX) {
> +                       fbdiv = rate / PS_PLL_VCO_MAX;
> +                       rate = rate / (fbdiv + 1);
> +               }
> +               if (rate < PS_PLL_VCO_MIN) {
> +                       fbdiv = DIV_ROUND_UP(PS_PLL_VCO_MIN, rate);
> +                       rate = rate * fbdiv;
> +               }
> +               return rate;
> +       }
> +
> +       fbdiv = DIV_ROUND_CLOSEST(rate, *prate);
> +       fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
> +       return *prate * fbdiv;
> +}
> +
> +/**
> + * zynqmp_pll_recalc_rate - Recalculate clock frequency
> + * @hw:                        Handle between common and hardware-specific interfaces
> + * @parent_rate:       Clock frequency of parent clock
> + * Return:             Current clock frequency
> + */
> +static unsigned long zynqmp_pll_recalc_rate(struct clk_hw *hw,
> +                                           unsigned long parent_rate)
> +{
> +       struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> +       u32 clk_id = clk->clk_id;
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 fbdiv, data;
> +       unsigned long rate, frac;
> +       u32 ret_payload[PAYLOAD_ARG_CNT];
> +       int ret;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_getdivider)
> +               return 0;
> +
> +       /*
> +        * makes probably sense to redundantly save fbdiv in the struct
> +        * zynqmp_pll to save the IO access.
> +        */
> +       ret = eemi_ops->clock_getdivider(clk_id, &fbdiv);
> +       if (ret)
> +               pr_warn_once("%s() get divider failed for %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +
> +       rate =  parent_rate * fbdiv;
> +       if (pll_get_mode(hw) == PLL_MODE_FRAC) {
> +               eemi_ops->ioctl(0, IOCTL_GET_PLL_FRAC_DATA, clk_id, 0,
> +                               ret_payload);
> +               data = ret_payload[1];
> +               frac = (parent_rate * data) / FRAC_DIV;
> +               rate = rate + frac;
> +       }
> +
> +       return rate;
> +}
> +
> +/**
> + * zynqmp_pll_set_rate - Set rate of PLL
> + * @hw:                        Handle between common and hardware-specific interfaces
> + * @rate:              Frequency of clock to be set
> + * @parent_rate:       Clock frequency of parent clock
> + */
> +static int zynqmp_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +                              unsigned long parent_rate)
> +{
> +       struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> +       u32 clk_id = clk->clk_id;
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 fbdiv, data;
> +       long rate_div, frac, m, f;
> +       int ret;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_setdivider)
> +               return -ENXIO;
> +
> +       if (pll_get_mode(hw) == PLL_MODE_FRAC) {
> +               unsigned int children;
> +
> +               /*
> +                * We're running on a ZynqMP compatible machine, make sure the
> +                * VPLL only has one child.
> +                */
> +               children = clk_get_children("vpll");
> +
> +               /* Account for vpll_to_lpd and dp_video_ref */
> +               if (children > 2)
> +                       WARN(1, "Two devices are using vpll which is forbidden\n");
> +
> +               rate_div = ((rate * FRAC_DIV) / parent_rate);
> +               m = rate_div / FRAC_DIV;
> +               f = rate_div % FRAC_DIV;
> +               m = clamp_t(u32, m, (PLL_FBDIV_MIN), (PLL_FBDIV_MAX));
> +               rate = parent_rate * m;
> +               frac = (parent_rate * f) / FRAC_DIV;
> +
> +               ret = eemi_ops->clock_setdivider(clk_id, m);
> +               if (ret)
> +                       pr_warn_once("%s() set divider failed for %s, ret = %d\n",
> +                                    __func__, clk_name, ret);
> +
> +               data = (FRAC_DIV * f) / FRAC_DIV;
> +               eemi_ops->ioctl(0, IOCTL_SET_PLL_FRAC_DATA, clk_id, data, NULL);
> +
> +               return (rate + frac);
> +       }
> +
> +       fbdiv = DIV_ROUND_CLOSEST(rate, parent_rate);
> +       fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
> +       ret = eemi_ops->clock_setdivider(clk_id, fbdiv);
> +       if (ret)
> +               pr_warn_once("%s() set divider failed for %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +
> +       return parent_rate * fbdiv;
> +}
> +
> +/**
> + * zynqmp_pll_is_enabled - Check if a clock is enabled
> + * @hw:                Handle between common and hardware-specific interfaces
> + *
> + * Return:     1 if the clock is enabled, 0 otherwise
> + */
> +static int zynqmp_pll_is_enabled(struct clk_hw *hw)
> +{
> +       struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 clk_id = clk->clk_id;
> +       unsigned int state;
> +       int ret;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_getstate)
> +               return 0;
> +
> +       ret = eemi_ops->clock_getstate(clk_id, &state);
> +       if (ret)
> +               pr_warn_once("%s() clock get state failed for %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +
> +       return state ? 1 : 0;
> +}
> +
> +/**
> + * zynqmp_pll_enable - Enable clock
> + * @hw:                Handle between common and hardware-specific interfaces
> + *
> + * Return:     0 always
> + */
> +static int zynqmp_pll_enable(struct clk_hw *hw)
> +{
> +       struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 clk_id = clk->clk_id;
> +       int ret;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_enable)
> +               return 0;
> +
> +       if (zynqmp_pll_is_enabled(hw))
> +               return 0;
> +
> +       pr_info("PLL: enable\n");
> +
> +       ret = eemi_ops->clock_enable(clk_id);
> +       if (ret)
> +               pr_warn_once("%s() clock enable failed for %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +
> +       return 0;
> +}
> +
> +/**
> + * zynqmp_pll_disable - Disable clock
> + * @hw:                Handle between common and hardware-specific interfaces
> + *
> + */
> +static void zynqmp_pll_disable(struct clk_hw *hw)
> +{
> +       struct zynqmp_pll *clk = to_zynqmp_pll(hw);
> +       const char *clk_name = clk_hw_get_name(hw);
> +       u32 clk_id = clk->clk_id;
> +       int ret;
> +       const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +       if (!eemi_ops || !eemi_ops->clock_disable)
> +               return;
> +
> +       if (!zynqmp_pll_is_enabled(hw))
> +               return;
> +
> +       pr_info("PLL: shutdown\n");
> +
> +       ret = eemi_ops->clock_disable(clk_id);
> +       if (ret)
> +               pr_warn_once("%s() clock disable failed for %s, ret = %d\n",
> +                            __func__, clk_name, ret);
> +}
> +
> +static const struct clk_ops zynqmp_pll_ops = {
> +       .enable = zynqmp_pll_enable,
> +       .disable = zynqmp_pll_disable,
> +       .is_enabled = zynqmp_pll_is_enabled,
> +       .round_rate = zynqmp_pll_round_rate,
> +       .recalc_rate = zynqmp_pll_recalc_rate,
> +       .set_rate = zynqmp_pll_set_rate,
> +};
> +
> +/**
> + * clk_register_zynqmp_pll - Register PLL with the clock framework
> + * @name:      PLL name
> + * @flag:      PLL flags
> + * @parents:   Parent clock names
> + * @num_parents:Number of parents
> + * @pll_ctrl:  Pointer to PLL control register
> + * @pll_status:        Pointer to PLL status register
> + * @lock_index:        Bit index to this PLL's lock status bit in @pll_status
> + *
> + * Return:     Handle to the registered clock
> + */
> +struct clk *clk_register_zynqmp_pll(const char *name, u32 clk_id,
> +                                   const char * const *parents,
> +                                   u8 num_parents, unsigned long flag)
> +{
> +       struct zynqmp_pll *pll;
> +       struct clk *clk;
> +       struct clk_init_data init;
> +       int status;
> +
> +       init.name = name;
> +       init.ops = &zynqmp_pll_ops;
> +       init.flags = flag;
> +       init.parent_names = parents;
> +       init.num_parents = num_parents;
> +
> +       pll = kmalloc(sizeof(*pll), GFP_KERNEL);
> +       if (!pll)
> +               return ERR_PTR(-ENOMEM);
> +
> +       /* Populate the struct */
> +       pll->hw.init = &init;
> +       pll->clk_id = clk_id;
> +
> +       clk = clk_register(NULL, &pll->hw);
> +       if (WARN_ON(IS_ERR(clk)))
> +               kfree(pll);
> +
> +       status = clk_set_rate_range(clk, PS_PLL_VCO_MIN, PS_PLL_VCO_MAX);
> +       if (status < 0)
> +               pr_err("%s:ERROR clk_set_rate_range failed %d\n", name, status);
> +
> +       return clk;
> +}
> diff --git a/include/linux/clk/zynqmp.h b/include/linux/clk/zynqmp.h
> new file mode 100644
> index 0000000..024ebf8
> --- /dev/null
> +++ b/include/linux/clk/zynqmp.h
> @@ -0,0 +1,46 @@
> +/*
> + *  Copyright (C) 2016-2017 Xilinx
> + *
> + * SPDX-License-Identifier:     GPL-2.0+
> + */

This tag should be on the fist line this way:
/* SPDX-License-Identifier: GPL-2.0+ */

> +
> +#ifndef __LINUX_CLK_ZYNQMP_H_
> +#define __LINUX_CLK_ZYNQMP_H_
> +
> +#include <linux/spinlock.h>
> +#include <linux/firmware/xilinx/zynqmp/firmware.h>
> +
> +#define CLK_FRAC       BIT(13) /* has a fractional parent */
> +
> +struct device;
> +
> +struct clk *clk_register_zynqmp_pll(const char *name, u32 clk_id,
> +                                   const char * const *parent, u8 num_parents,
> +                                   unsigned long flag);
> +
> +struct clk *zynqmp_clk_register_gate(struct device *dev, const char *name,
> +                                    u32 clk_id,
> +                                    const char * const *parent_name,
> +                                    u8 num_parents, unsigned long flags,
> +                                    u8 clk_gate_flags);
> +
> +struct clk *zynqmp_clk_register_divider(struct device *dev, const char *name,
> +                                       u32 clk_id, u32 div_type,
> +                                       const char * const *parent_name,
> +                                       u8 num_parents,
> +                                       unsigned long flags,
> +                                       u8 clk_divider_flags);
> +
> +struct clk *zynqmp_clk_register_mux(struct device *dev, const char *name,
> +                                   u32 clk_id,
> +                                   const char **parent_names,
> +                                   u8 num_parents, unsigned long flags,
> +                                   u8 clk_mux_flags);
> +
> +struct clk *zynqmp_clk_register_mux_table(struct device *dev, const char *name,
> +                                         u32 clk_id,
> +                                         const char * const *parent_names,
> +                                         u8 num_parents, unsigned long flags,
> +                                         u8 clk_mux_flags);
> +
> +#endif
> --
> 2.7.4
>


-- 
Cordially
Philippe Ombredanne

^ permalink raw reply

* [linux-sunxi] [PATCH v2 13/16] power: supply: axp20x_battery: add support for AXP813
From: Julian Calaby @ 2018-01-09 12:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <a1c061eb50edfd3519ece6c8177085b55d7aefc6.1515486346.git-series.quentin.schulz@free-electrons.com>

Hi Quentin,

On Tue, Jan 9, 2018 at 8:33 PM, Quentin Schulz
<quentin.schulz@free-electrons.com> wrote:
> The X-Powers AXP813 PMIC has got some slight differences from
> AXP20X/AXP22X PMICs:
>  - the maximum voltage supplied by the PMIC is 4.35 instead of 4.36/4.24
>  for AXP20X/AXP22X,
>  - the constant charge current formula is different,
>
> It also has a bit to tell whether the battery percentage returned by the
> PMIC is valid.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> ---
>  drivers/power/supply/axp20x_battery.c | 42 ++++++++++++++++++++++++++++-
>  1 file changed, 42 insertions(+)
>
> diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
> index d73c78f..dad72a5 100644
> --- a/drivers/power/supply/axp20x_battery.c
> +++ b/drivers/power/supply/axp20x_battery.c
> @@ -46,6 +46,8 @@
>  #define AXP20X_CHRG_CTRL1_TGT_4_2V     (2 << 5)
>  #define AXP20X_CHRG_CTRL1_TGT_4_36V    (3 << 5)
>
> +#define AXP813_CHRG_CTRL1_TGT_4_35V    (3 << 5)
> +
>  #define AXP22X_CHRG_CTRL1_TGT_4_22V    (1 << 5)
>  #define AXP22X_CHRG_CTRL1_TGT_4_24V    (3 << 5)

Should these be "alphabetical", i.e. AXP20X, AXP22X, AXP813?

Thanks,

-- 
Julian Calaby

Email: julian.calaby@gmail.com
Profile: http://www.google.com/profiles/julian.calaby/

^ permalink raw reply

* [PATCH v3 07/13] arm64: Add skeleton to harden the branch predictor against aliasing attacks
From: Philippe Ombredanne @ 2018-01-09 12:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515432758-26440-8-git-send-email-will.deacon@arm.com>

Dear Will,

On Mon, Jan 8, 2018 at 6:32 PM, Will Deacon <will.deacon@arm.com> wrote:
> Aliasing attacks against CPU branch predictors can allow an attacker to
> redirect speculative control flow on some CPUs and potentially divulge
> information from one context to another.
>
> This patch adds initial skeleton code behind a new Kconfig option to
> enable implementation-specific mitigations against these attacks for
> CPUs that are affected.
>
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> Signed-off-by: Will Deacon <will.deacon@arm.com>

<snip>


> --- /dev/null
> +++ b/arch/arm64/kernel/bpi.S
> @@ -0,0 +1,55 @@
> +/*
> + * Contains CPU specific branch predictor invalidation sequences
> + *
> + * Copyright (C) 2018 ARM Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */

Could you consider using the new SDPX tags [1] instead of this long legalese?
Thanks!

[1] https://lkml.org/lkml/2017/12/28/323
-- 
Cordially
Philippe Ombredanne

^ permalink raw reply

* [PATCH 03/11] drm/bridge/synopsys: dw-hdmi: Enable workaround for v1.32a
From: Laurent Pinchart @ 2018-01-09 12:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171230210203.24115-4-jernej.skrabec@siol.net>

Hi Jernej,

Thank you for the patch.

On Saturday, 30 December 2017 23:01:55 EET Jernej Skrabec wrote:
> Allwinner SoCs have dw hdmi controller v1.32a which exhibits same
> magenta line issue as i.MX6Q and i.MX6DL. Enable workaround for it.
> 
> Tests show that one iteration is enough.
> 
> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>

This does not break R-Car DU, so

Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> ---
>  drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 8 +++++---
>  1 file changed, 5 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
> a38db40ce990..7ca14d7325b5 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -1634,9 +1634,10 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi
> *hdmi) * then write one of the FC registers several times.
>  	 *
>  	 * The number of iterations matters and depends on the HDMI TX revision
> -	 * (and possibly on the platform). So far only i.MX6Q (v1.30a) and
> -	 * i.MX6DL (v1.31a) have been identified as needing the workaround, with
> -	 * 4 and 1 iterations respectively.
> +	 * (and possibly on the platform). So far i.MX6Q (v1.30a), i.MX6DL
> +	 * (v1.31a) and multiple Allwinner SoCs (v1.32a) have been identified
> +	 * as needing the workaround, with 4 iterations for v1.30a and 1
> +	 * iteration for others.
>  	 */
> 
>  	switch (hdmi->version) {
> @@ -1644,6 +1645,7 @@ static void dw_hdmi_clear_overflow(struct dw_hdmi
> *hdmi) count = 4;
>  		break;
>  	case 0x131a:
> +	case 0x132a:
>  		count = 1;
>  		break;
>  	default:

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* [net-next: PATCH 0/8] Armada 7k/8k PP2 ACPI support
From: Andrew Lunn @ 2018-01-09 13:00 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAPv3WKehwsrxuxemgL_5fKtUT2xjqkE6yf9DMcCcO2WRJ6nQaQ@mail.gmail.com>

On Tue, Jan 09, 2018 at 11:22:00AM +0100, Marcin Wojtas wrote:
> 2018-01-09 11:19 GMT+01:00 Graeme Gregory <graeme.gregory@linaro.org>:
> > On Mon, Jan 08, 2018 at 06:17:06PM +0100, Marcin Wojtas wrote:
> >> Hi Andrew,
> >>
> >>
> >>
> >> 2018-01-08 16:42 GMT+01:00 Andrew Lunn <andrew@lunn.ch>:
> >> > w> I am not familiar with MDIO, but if its similar or a specific
> >> >> implementation of a serial bus that does sound sane!
> >> >
> >>
> >> Thanks for digging, I will check if and how we can use
> >> GenericSerialBus with MDIO.
> >>
> > Maybe Lorenzo, Hanjun, Sudeep can comment here they might have come
> > across similar on other ARM boards.
> >
> 
> I'm looking forward to their feedback, however, what I've noticed,
> each driver handles mdio/phys on its own, not using any generic
> solution, which is what I need to actually avoid :)

Agreed. Lets define it once for all drivers using phylib/phylink.

	Andrew

^ permalink raw reply

* [PATCH 5/7] ARM: dts: imx6ul: add ARM architected timer
From: Stefan Agner @ 2018-01-09 13:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180109093422.GD26312@b29396-OptiPlex-7040>

On 2018-01-09 10:34, Dong Aisheng wrote:
> Hi Stefan,
> 
> On Tue, Jan 02, 2018 at 05:42:21PM +0100, Stefan Agner wrote:
>> Add per-core ARM architected timer. Unfortunately bootloaders (U-Boot)
>> currently do not make the necessary initialization. Also specifing the
>> clock manually using the clock-frequency property seems not to help.
>> Therefor leave the timer disabled by default for now.
>>
>> Signed-off-by: Stefan Agner <stefan@agner.ch>
> 
> Any special purpose to use arch timer?
> 

It is the better option. It supports virtualization and allows direct
user space access, e.g. as used in OpenSSL through _armv7_tick.

--
Stefan

>> ---
>>  arch/arm/boot/dts/imx6ul.dtsi | 10 ++++++++++
>>  1 file changed, 10 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/imx6ul.dtsi b/arch/arm/boot/dts/imx6ul.dtsi
>> index 993fbdbdd506..4d76923e8f44 100644
>> --- a/arch/arm/boot/dts/imx6ul.dtsi
>> +++ b/arch/arm/boot/dts/imx6ul.dtsi
>> @@ -110,6 +110,16 @@
>>  		      <0x00a06000 0x2000>;
>>  	};
>>
>> +	timer {
>> +		compatible = "arm,armv7-timer";
>> +		interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
>> +			     <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
>> +			     <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
>> +			     <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
>> +		interrupt-parent = <&intc>;
>> +		status = "disabled";
>> +	};
>> +
>>  	ckil: clock-cli {
>>  		compatible = "fixed-clock";
>>  		#clock-cells = <0>;
>> --
>> 2.15.1
>>

^ permalink raw reply

* [PATCH 1/7] ARM: imx: add timer stop flag to ARM power off state
From: Stefan Agner @ 2018-01-09 13:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180109092232.GA26312@b29396-OptiPlex-7040>

On 2018-01-09 10:22, Dong Aisheng wrote:
> On Tue, Jan 02, 2018 at 05:42:17PM +0100, Stefan Agner wrote:
>> When the CPU is in ARM power off state the ARM architected
>> timers are stopped. The flag is already present in the higher
>> power WAIT mode.
>>
>> This allows to use the ARM generic timer on i.MX 6UL/6ULL SoC.
>> Without the flag the kernel freezes when the timer enters the
>> first time ARM power off mode.
>>
>> Cc: Anson Huang <anson.huang@nxp.com>
>> Signed-off-by: Stefan Agner <stefan@agner.ch>
> 
> It seems ok at my side.
> Did you meet the real issue? If yes, how to reproduce?

Enable the timer added with Patch 5, use a U-Boot with this patchset
applied:
https://www.mail-archive.com/u-boot at lists.denx.de/msg273287.html

And boot... For me it freezed somewhere early during systemd boot phase,
presumably the first time the CPU got into this idle mode.

--
Stefan

> 
> Both mx6sx and mx6ul are using GPT which do not need that flag, suppose
> we should remove it, right?
> Anson can help confirm it.
> 
> Regards
> Dong Aisheng
> 
>> ---
>>  arch/arm/mach-imx/cpuidle-imx6sx.c | 1 +
>>  1 file changed, 1 insertion(+)
>>
>> diff --git a/arch/arm/mach-imx/cpuidle-imx6sx.c b/arch/arm/mach-imx/cpuidle-imx6sx.c
>> index c5a5c3a70ab1..d0f14b761ff7 100644
>> --- a/arch/arm/mach-imx/cpuidle-imx6sx.c
>> +++ b/arch/arm/mach-imx/cpuidle-imx6sx.c
>> @@ -89,6 +89,7 @@ static struct cpuidle_driver imx6sx_cpuidle_driver = {
>>  			 */
>>  			.exit_latency = 300,
>>  			.target_residency = 500,
>> +			.flags = CPUIDLE_FLAG_TIMER_STOP,
>>  			.enter = imx6sx_enter_wait,
>>  			.name = "LOW-POWER-IDLE",
>>  			.desc = "ARM power off",
>> --
>> 2.15.1
>>

^ permalink raw reply

* [PATCH v3] dt: psci: Update DT bindings to support hierarchical PSCI states
From: Ulf Hansson @ 2018-01-09 13:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <18b1d849-038d-f768-99aa-b363e4c8b2f8@arm.com>

On 9 January 2018 at 13:09, Sudeep Holla <sudeep.holla@arm.com> wrote:
> (Removed Brendan Jackman as he is no longer works in ARM)
>
> On 09/01/18 11:55, Ulf Hansson wrote:
>> From: Lina Iyer <lina.iyer@linaro.org>
>>
>> Update DT bindings to represent hierarchical CPU and CPU domain idle states
>> for PSCI. Also update the PSCI examples to clearly show how flattened and
>> hierarchical idle states can be represented in DT.
>>
>
> It now looks good to me :)
>
> Reviewed-by: Sudeep Holla <sudeep.holla@arm.com>

Rob, Sudeep, thanks for reviews!

Rob, do you want to pick this up now? Or how do you want me to manage
the patch going forward?

Kind regards
Uffe

^ permalink raw reply

* [PATCH 01/19] drm/fourcc: Add a function to tell if the format embeds alpha
From: Maxime Ripard @ 2018-01-09 13:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1796930.sWoKI78acY@avalon>

Hi Laurent,

On Tue, Jan 09, 2018 at 02:29:58PM +0200, Laurent Pinchart wrote:
> On Tuesday, 9 January 2018 12:56:20 EET Maxime Ripard wrote:
> > There's a bunch of drivers that duplicate the same function to know if a
> > particular format embeds an alpha component or not.
> > 
> > Let's create a helper to avoid duplicating that logic.
> > 
> > Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
> > Cc: Eric Anholt <eric@anholt.net>
> > Cc: Inki Dae <inki.dae@samsung.com>
> > Cc: Joonyoung Shim <jy0922.shim@samsung.com>
> > Cc: Kyungmin Park <kyungmin.park@samsung.com>
> > Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Cc: Mark Yao <mark.yao@rock-chips.com>
> > Cc: Seung-Woo Kim <sw0312.kim@samsung.com>
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > ---
> >  drivers/gpu/drm/drm_fourcc.c | 43 +++++++++++++++++++++++++++++++++++++-
> >  include/drm/drm_fourcc.h     |  1 +-
> >  2 files changed, 44 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c
> > index 9c0152df45ad..6e6227d6a46b 100644
> > --- a/drivers/gpu/drm/drm_fourcc.c
> > +++ b/drivers/gpu/drm/drm_fourcc.c
> > @@ -348,3 +348,46 @@ int drm_format_plane_height(int height, uint32_t
> > format, int plane) return height / info->vsub;
> >  }
> >  EXPORT_SYMBOL(drm_format_plane_height);
> > +
> > +/**
> > + * drm_format_has_alpha - get whether the format embeds an alpha component
> > + * @format: pixel format (DRM_FORMAT_*)
> > + *
> > + * Returns:
> > + * true if the format embeds an alpha component, false otherwise.
> > + */
> > +bool drm_format_has_alpha(uint32_t format)
> > +{
> > +	switch (format) {
> > +	case DRM_FORMAT_ARGB4444:
> > +	case DRM_FORMAT_ABGR4444:
> > +	case DRM_FORMAT_RGBA4444:
> > +	case DRM_FORMAT_BGRA4444:
> > +	case DRM_FORMAT_ARGB1555:
> > +	case DRM_FORMAT_ABGR1555:
> > +	case DRM_FORMAT_RGBA5551:
> > +	case DRM_FORMAT_BGRA5551:
> > +	case DRM_FORMAT_ARGB8888:
> > +	case DRM_FORMAT_ABGR8888:
> > +	case DRM_FORMAT_RGBA8888:
> > +	case DRM_FORMAT_BGRA8888:
> > +	case DRM_FORMAT_ARGB2101010:
> > +	case DRM_FORMAT_ABGR2101010:
> > +	case DRM_FORMAT_RGBA1010102:
> > +	case DRM_FORMAT_BGRA1010102:
> > +	case DRM_FORMAT_AYUV:
> > +	case DRM_FORMAT_XRGB8888_A8:
> > +	case DRM_FORMAT_XBGR8888_A8:
> > +	case DRM_FORMAT_RGBX8888_A8:
> > +	case DRM_FORMAT_BGRX8888_A8:
> > +	case DRM_FORMAT_RGB888_A8:
> > +	case DRM_FORMAT_BGR888_A8:
> > +	case DRM_FORMAT_RGB565_A8:
> > +	case DRM_FORMAT_BGR565_A8:
> > +		return true;
> > +
> > +	default:
> > +		return false;
> > +	}
> > +}
> > +EXPORT_SYMBOL(drm_format_has_alpha);
> 
> How about adding the information to struct drm_format_info instead ? 
> drm_format_has_alpha() could then be implemented as
> 
> bool drm_format_has_alpha(uint32_t format)
> {
> 	const struct drm_format_info *info;
> 
> 	info = drm_format_info(format);
> 	return info ? info->has_alpha : false;
> }

I considered it, and wasn't too sure about if adding more fields to
drm_format_info was ok. I can definitely do it that way.

> although drivers should really use the drm_framebuffer::format field directly 
> in most cases, so the helper might not be needed at all.

The drivers converted in my serie shouldn't be too hard to convert to
use drm_format_info directly, so that can be removed as well.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180109/4e77d4e3/attachment.sig>

^ permalink raw reply

* [PATCH 04/11] drm/bridge/synopsys: dw-hdmi: Export some PHY related functions
From: Laurent Pinchart @ 2018-01-09 13:30 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171230210203.24115-5-jernej.skrabec@siol.net>

Hi Jernej,

Thank you for the patch.

On Saturday, 30 December 2017 23:01:56 EET Jernej Skrabec wrote:
> Parts of PHY code could be useful also for custom PHYs. For example,
> Allwinner A83T has custom PHY which is probably Synopsys gen2 PHY
> with few additional memory mapped registers, so most of the Synopsys PHY
> related code could be reused.
> 
> It turns out that even completely custom HDMI PHYs, such as the one
> found in Allwinner H3, can reuse some of those functions. This would
> suggest that (some?) functions exported in this commit are actually part
> of generic PHY interface and not really specific to Synopsys PHYs.

That's correct, those functions control the interface between the HDMI 
controller and the PHY. They're not specific to Synopsys PHYs, but they're 
specific to the PHY interface as designed by Synopsys.

> Export useful PHY functions.
> 
> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
> ---
>  drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 45 ++++++++++++++++++++-------
>  drivers/gpu/drm/bridge/synopsys/dw-hdmi.h |  2 ++
>  include/drm/bridge/dw_hdmi.h              | 10 +++++++
>  3 files changed, 44 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
> 7ca14d7325b5..67467d0b683a 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -1037,19 +1037,21 @@ static void dw_hdmi_phy_enable_svsret(struct dw_hdmi
> *hdmi, u8 enable) HDMI_PHY_CONF0_SVSRET_MASK);
>  }
> 
> -static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
> +void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
>  {
>  	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
>  			 HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
>  			 HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
>  }
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_pddq);
> 
> -static void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable)
> +void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable)
>  {
>  	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
>  			 HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
>  			 HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
>  }
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_txpwron);
> 
>  static void dw_hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, u8 enable)
>  {
> @@ -1065,6 +1067,23 @@ static void dw_hdmi_phy_sel_interface_control(struct
> dw_hdmi *hdmi, u8 enable) HDMI_PHY_CONF0_SELDIPIF_MASK);
>  }
> 
> +void dw_hdmi_phy_gen2_reset(struct dw_hdmi *hdmi, u8 enable)
> +{
> +	hdmi_mask_writeb(hdmi, enable, HDMI_MC_PHYRSTZ,
> +			 HDMI_MC_PHYRSTZ_PHYRSTZ_OFFSET,
> +			 HDMI_MC_PHYRSTZ_PHYRSTZ_MASK);
> +}
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_gen2_reset);
> +
> +void dw_hdmi_phy_set_slave_addr(struct dw_hdmi *hdmi)
> +{
> +	hdmi_phy_test_clear(hdmi, 1);
> +	hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
> +		    HDMI_PHY_I2CM_SLAVE_ADDR);
> +	hdmi_phy_test_clear(hdmi, 0);
> +}
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_set_slave_addr);

Should the I2C address be passed as an argument ?

>  static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi)
>  {
>  	const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
> @@ -1204,15 +1223,12 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
>  		dw_hdmi_phy_enable_svsret(hdmi, 1);
> 
>  	/* PHY reset. The reset signal is active high on Gen2 PHYs. */
> -	hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
> -	hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
> +	dw_hdmi_phy_gen2_reset(hdmi, 1);
> +	dw_hdmi_phy_gen2_reset(hdmi, 0);
> 
>  	hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
> 
> -	hdmi_phy_test_clear(hdmi, 1);
> -	hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
> -		    HDMI_PHY_I2CM_SLAVE_ADDR);
> -	hdmi_phy_test_clear(hdmi, 0);
> +	dw_hdmi_phy_set_slave_addr(hdmi);
> 
>  	/* Write to the PHY as configured by the platform */
>  	if (pdata->configure_phy)
> @@ -1251,15 +1267,16 @@ static void dw_hdmi_phy_disable(struct dw_hdmi
> *hdmi, void *data) dw_hdmi_phy_power_off(hdmi);
>  }
> 
> -static enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
> -						      void *data)
> +enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
> +					       void *data)
>  {
>  	return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
>  		connector_status_connected : connector_status_disconnected;
>  }
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_read_hpd);
> 
> -static void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data,
> -				   bool force, bool disabled, bool rxsense)
> +void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data,
> +			    bool force, bool disabled, bool rxsense)
>  {
>  	u8 old_mask = hdmi->phy_mask;
> 
> @@ -1271,8 +1288,9 @@ static void dw_hdmi_phy_update_hpd(struct dw_hdmi
> *hdmi, void *data, if (old_mask != hdmi->phy_mask)
>  		hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
>  }
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_update_hpd);
> 
> -static void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data)
> +void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data)
>  {
>  	/*
>  	 * Configure the PHY RX SENSE and HPD interrupts polarities and clear
> @@ -1291,6 +1309,7 @@ static void dw_hdmi_phy_setup_hpd(struct dw_hdmi
> *hdmi, void *data) hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD |
> HDMI_IH_PHY_STAT0_RX_SENSE), HDMI_IH_MUTE_PHY_STAT0);
>  }
> +EXPORT_SYMBOL_GPL(dw_hdmi_phy_setup_hpd);
> 
>  static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
>  	.init = dw_hdmi_phy_init,
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index
> 9d90eb9c46e5..fd150430d0b3 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
> @@ -950,6 +950,8 @@ enum {
> 
>  /* MC_PHYRSTZ field values */
>  	HDMI_MC_PHYRSTZ_PHYRSTZ = 0x01,
> +	HDMI_MC_PHYRSTZ_PHYRSTZ_OFFSET = 0x00,
> +	HDMI_MC_PHYRSTZ_PHYRSTZ_MASK = 0x01,
> 
>  /* MC_HEACPHY_RST field values */
>  	HDMI_MC_HEACPHY_RST_ASSERT = 0x1,
> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
> index 182f83283e24..f5cca4362154 100644
> --- a/include/drm/bridge/dw_hdmi.h
> +++ b/include/drm/bridge/dw_hdmi.h
> @@ -159,5 +159,15 @@ void dw_hdmi_audio_disable(struct dw_hdmi *hdmi);
>  /* PHY configuration */
>  void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
>  			   unsigned char addr);
> +enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
> +					       void *data);
> +void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data,
> +			    bool force, bool disabled, bool rxsense);
> +void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data);
> +
> +void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable);
> +void dw_hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, u8 enable);
> +void dw_hdmi_phy_gen2_reset(struct dw_hdmi *hdmi, u8 enable);
> +void dw_hdmi_phy_set_slave_addr(struct dw_hdmi *hdmi);
> 
>  #endif /* __IMX_HDMI_H__ */

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* [PATCH v2] ARM: dts: sunxi: Add sid for a83t
From: Maxime Ripard @ 2018-01-09 13:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CACNAnaHOuPH05hsJ-jt1135-Giknb-DOuvjeQRSGVR2L7Xc-Cg@mail.gmail.com>

On Mon, Jan 08, 2018 at 09:30:57AM -0600, Kyle Evans wrote:
> On Thu, Jan 4, 2018 at 8:01 AM, Maxime Ripard
> <maxime.ripard@free-electrons.com> wrote:
> > On Fri, Dec 22, 2017 at 06:11:52PM +0800, Chen-Yu Tsai wrote:
> >> On Fri, Dec 22, 2017 at 6:07 PM, Emmanuel Vadot <manu@bidouilliste.com> wrote:
> >> > On Fri, 22 Dec 2017 09:35:08 +0100
> >> > Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> >> >
> >> >> On Thu, Dec 21, 2017 at 07:09:03PM +0100, Emmanuel Vadot wrote:
> >> >> >
> >> >> >  Hi Maxime,
> >> >> >
> >> >> > On Thu, 21 Dec 2017 16:26:30 +0100
> >> >> > Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> >> >> >
> >> >> > > Hi,
> >> >> > >
> >> >> > > On Thu, Dec 21, 2017 at 09:19:24AM -0600, Kyle Evans wrote:
> >> >> > > > On Thu, Dec 21, 2017 at 8:55 AM, Maxime Ripard
> >> >> > > > <maxime.ripard@free-electrons.com> wrote:
> >> >> > > > > Hi Kyle,
> >> >> > > > >
> >> >> > > > > On Tue, Dec 19, 2017 at 03:05:23PM -0600, kevans91 at ksu.edu wrote:
> >> >> > > > >> Allwinner a83t has a 1 KB sid block with efuse for security rootkey and
> >> >> > > > >> thermal calibration data, add node to describe it.
> >> >> > > > >>
> >> >> > > > >> a83t-sid is not currently supported by nvmem/sunxi-sid, but it is
> >> >> > > > >> supported in an external driver for FreeBSD.
> >> >> > > > >>
> >> >> > > > >> Signed-off-by: Kyle Evans <kevans91@ksu.edu>
> >> >> > > > >
> >> >> > > > > The patch looks fine in itself, but we've had a number of issues with
> >> >> > > > > the register layout (and access patterns) in the past, so I'd rather
> >> >> > > > > have something that works in Linux too if possible.
> >> >> > > >
> >> >> > > > I have a patch that I think should make it work fine on Linux [1], but
> >> >> > > > I'm afraid I have little to no capability to test it myself and so I
> >> >> > > > did not add it as well.
> >> >> > > >
> >> >> > > > I do know that the rootkey is offset 0x200 into the given space [2],
> >> >> > > > as is the case with the H3, and that the readout quirk is not needed.
> >> >> > > > I wasn't 100% sure that the a83t has 2Kbit worth of efuse space as the
> >> >> > > > H3, but I do know that thermal data can be found at 0x34 and 0x38 in
> >> >> > > > this space.
> >> >> > >
> >> >> > > Then maybe we should leave it aside until someone takes some time on
> >> >> > > the A83t.
> >> >> >
> >> >> >  Take some time on the Linux driver and do not apply this patch for
> >> >> > now you mean ?
> >> >>
> >> >> Yep.
> >> >>
> >> >> Maxime
> >> >
> >> >  Since linux doesn't have the compatible in it's driver what would
> >> > be the harm to add the node in the DTS ? If a quirks is needed because
> >> > some region is weird this would go in the driver right ? I don't see a
> >> > technical problem for adding this node right now.
> >> >  If Kyle confirm the lenght of the region and that no quirk is needed
> >> > will it be enough ?
> >>
> >> I guess I wasn't very clear. I'm OK with the patch going in. The device
> >> node currently says nothing about how much efuse space there is. The
> >> memory region covers that and the control section, and the size matches
> >> what the memory map says.
> >>
> >> The size and offset of the efuse space would be dealt with in the driver.
> >
> > Let's merge it then.
> >
> > Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> 
> What does the timeline for these things normally look like? I'm new to
> these parts. =)

We're one week away from the merge window, so it's a bit late for it
to be merged in 4.16, but it'll be in 4.17.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180109/bd79f4be/attachment-0001.sig>

^ permalink raw reply

* [PATCH 1/7] ARM: imx: add timer stop flag to ARM power off state
From: Stefan Agner @ 2018-01-09 13:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515492803.12538.29.camel@pengutronix.de>

On 2018-01-09 11:13, Lucas Stach wrote:
> Am Dienstag, den 09.01.2018, 09:25 +0000 schrieb Anson Huang:
>>
>> Best Regards!
>> Anson Huang
>>
>>
>> > -----Original Message-----
>> > From: Dong Aisheng [mailto:dongas86 at gmail.com]
>> > Sent: 2018-01-09 5:23 PM
>> > To: Stefan Agner <stefan@agner.ch>
>> > Cc: shawnguo at kernel.org; kernel at pengutronix.de; Fabio Estevam
>> > <fabio.estevam@nxp.com>; robh+dt at kernel.org; mark.rutland at arm.com;
>> > linux-arm-kernel at lists.infradead.org; devicetree at vger.kernel.org;
>> > linux-
>> > kernel at vger.kernel.org; Anson Huang <anson.huang@nxp.com>; dl-
>> > linux-imx
>> > <linux-imx@nxp.com>
>> > Subject: Re: [PATCH 1/7] ARM: imx: add timer stop flag to ARM power
>> > off state
>> >
>> > On Tue, Jan 02, 2018 at 05:42:17PM +0100, Stefan Agner wrote:
>> > > When the CPU is in ARM power off state the ARM architected timers
>> > > are
>> > > stopped. The flag is already present in the higher power WAIT
>> > > mode.
>> > >
>> > > This allows to use the ARM generic timer on i.MX 6UL/6ULL SoC.
>> > > Without the flag the kernel freezes when the timer enters the
>> > > first
>> > > time ARM power off mode.
>> > >
>> > > Cc: Anson Huang <anson.huang@nxp.com>
>> > > Signed-off-by: Stefan Agner <stefan@agner.ch>
>> >
>> > It seems ok at my side.
>> > Did you meet the real issue? If yes, how to reproduce?
>> >
>> > Both mx6sx and mx6ul are using GPT which do not need that flag,
>> > suppose we
>> > should remove it, right?
>> > Anson can help confirm it.
>>
>> For UP system like i.MX6SX, we do NOT enable "cortex-a9-twd-timer",
>> so local
>> timer is NOT used, GPT is used instead, GPT's clock is NOT disabled
>> when cpuidle,
>> so I think we should remove all these Timer stop flag for 6SX
>> CPUIDLE.
> 
> It's correct to set the flag even on UP systems, as the flag means the
> CPU _local_ timer is stopped in this sleep mode. Also there are systems
> out there which are using the TWD on UP, as it operates at a higher
> frequency leading to better wakeup granularity.

Documentation/devicetree/bindings/arm/twd.txt states that TWD provides
"per-cpu local timer". But as far as I can see TWD still uses SPI
interrupts, routed through GIC, so is this the differentiation?

--
Stefan

^ permalink raw reply

* [PATCH 3/4] bcm2835-gpio-exp: Driver for GPIO expander via mailbox service
From: Baruch Siach @ 2018-01-09 13:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <2013811470.181895.1514918984637@email.1und1.de>

Hi Stefan,

On Tue, Jan 02, 2018 at 07:49:44PM +0100, Stefan Wahren wrote:

[...]

> > +	ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_GET_GPIO_CONFIG,
> > +				    &get, sizeof(get));
> > +	if (ret) {
> > +		dev_err(gpio->dev,
> > +			"Failed to get GPIO %u config (%d)\n", off, ret);
> > +		return ret;
> > +	}
> 
> Shouldn't we also check the in-bound status at get.gpio?

What is the in-bound status value? May you refer me to the documentation?

> And in all the other gpio ops?

Thanks,
baruch

-- 
     http://baruch.siach.name/blog/                  ~. .~   Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
   - baruch at tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -

^ permalink raw reply

* [PATCH] arm64: mm: Add additional parameter to uaccess_ttbr0_enable
From: Christoffer Dall @ 2018-01-09 13:43 UTC (permalink / raw)
  To: linux-arm-kernel

Add an extra temporary register parameter to uaccess_ttbr0_enable which
is about to be required for arm64 PAN support.

This patch doesn't introduce any functional change but ensures that the
kernel compiles once the KVM/ARM tree is merged with the arm64 tree by
ensuring a trivially mergable conflict.

Cc: Will Deacon <will.deacon@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
 arch/arm64/include/asm/asm-uaccess.h | 4 ++--
 arch/arm64/mm/cache.S                | 4 ++--
 arch/arm64/xen/hypercall.S           | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/include/asm/asm-uaccess.h b/arch/arm64/include/asm/asm-uaccess.h
index b3da6c886835..b67563d2024c 100644
--- a/arch/arm64/include/asm/asm-uaccess.h
+++ b/arch/arm64/include/asm/asm-uaccess.h
@@ -31,7 +31,7 @@ alternative_if_not ARM64_HAS_PAN
 alternative_else_nop_endif
 	.endm
 
-	.macro	uaccess_ttbr0_enable, tmp1, tmp2
+	.macro	uaccess_ttbr0_enable, tmp1, tmp2, tmp3
 alternative_if_not ARM64_HAS_PAN
 	save_and_disable_irq \tmp2		// avoid preemption
 	__uaccess_ttbr0_enable \tmp1
@@ -42,7 +42,7 @@ alternative_else_nop_endif
 	.macro	uaccess_ttbr0_disable, tmp1
 	.endm
 
-	.macro	uaccess_ttbr0_enable, tmp1, tmp2
+	.macro	uaccess_ttbr0_enable, tmp1, tmp2, tmp3
 	.endm
 #endif
 
diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S
index bedd23da83f4..5a52811f47e9 100644
--- a/arch/arm64/mm/cache.S
+++ b/arch/arm64/mm/cache.S
@@ -49,7 +49,7 @@ ENTRY(flush_icache_range)
  *	- end     - virtual end address of region
  */
 ENTRY(__flush_cache_user_range)
-	uaccess_ttbr0_enable x2, x3
+	uaccess_ttbr0_enable x2, x3, x4
 	dcache_line_size x2, x3
 	sub	x3, x2, #1
 	bic	x4, x0, x3
@@ -80,7 +80,7 @@ ENDPROC(__flush_cache_user_range)
  *	- end     - virtual end address of region
  */
 ENTRY(invalidate_icache_range)
-	uaccess_ttbr0_enable x2, x3
+	uaccess_ttbr0_enable x2, x3, x4
 
 	invalidate_icache_by_line x0, x1, x2, x3, 2f
 	mov	x0, xzr
diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S
index 401ceb71540c..acdbd2c9e899 100644
--- a/arch/arm64/xen/hypercall.S
+++ b/arch/arm64/xen/hypercall.S
@@ -101,7 +101,7 @@ ENTRY(privcmd_call)
 	 * need the explicit uaccess_enable/disable if the TTBR0 PAN emulation
 	 * is enabled (it implies that hardware UAO and PAN disabled).
 	 */
-	uaccess_ttbr0_enable x6, x7
+	uaccess_ttbr0_enable x6, x7, x8
 	hvc XEN_IMM
 
 	/*
-- 
2.14.2

^ permalink raw reply related

* [PATCH 1/2] MAINTAINERS: linux-media: update Microchip ISI and ISC entries
From: Nicolas Ferre @ 2018-01-09 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

These two image capture interface drivers are now handled
by Wenyou Yang.
I benefit from this change to update the two entries by correcting the
binding documentation link for ISC and moving the ISI to the new
MICROCHIP / ATMEL location.

Signed-off-by: Nicolas Ferre <nicolas.ferre@microchip.com>
---
Hi,

Patch against next-20180109.
Note that I didn't find it useful to have several patches for these changes.
Tell me if you feel differently.

I would like to have the Ack from Ludovic and Wenyou obviously. I don't know if
Songjun can answer as he's not with Microchip anymore.

Best regards,
  Nicolas

 MAINTAINERS | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index a7d10a2bb980..65c4b59b582f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2353,13 +2353,6 @@ L:	linux-i2c at vger.kernel.org
 S:	Supported
 F:	drivers/i2c/busses/i2c-at91.c
 
-ATMEL ISI DRIVER
-M:	Ludovic Desroches <ludovic.desroches@microchip.com>
-L:	linux-media at vger.kernel.org
-S:	Supported
-F:	drivers/media/platform/atmel/atmel-isi.c
-F:	include/media/atmel-isi.h
-
 ATMEL LCDFB DRIVER
 M:	Nicolas Ferre <nicolas.ferre@microchip.com>
 L:	linux-fbdev at vger.kernel.org
@@ -9102,12 +9095,20 @@ S:	Maintained
 F:	drivers/crypto/atmel-ecc.*
 
 MICROCHIP / ATMEL ISC DRIVER
-M:	Songjun Wu <songjun.wu@microchip.com>
+M:	Wenyou Yang <wenyou.yang@microchip.com>
 L:	linux-media at vger.kernel.org
 S:	Supported
 F:	drivers/media/platform/atmel/atmel-isc.c
 F:	drivers/media/platform/atmel/atmel-isc-regs.h
-F:	devicetree/bindings/media/atmel-isc.txt
+F:	Documentation/devicetree/bindings/media/atmel-isc.txt
+
+MICROCHIP / ATMEL ISI DRIVER
+M:	Wenyou Yang <wenyou.yang@microchip.com>
+L:	linux-media at vger.kernel.org
+S:	Supported
+F:	drivers/media/platform/atmel/atmel-isi.c
+F:	include/media/atmel-isi.h
+F:	Documentation/devicetree/bindings/media/atmel-isi.txt
 
 MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
 M:	Woojung Huh <Woojung.Huh@microchip.com>
-- 
2.9.0

^ permalink raw reply related

* [PATCH 2/2] MAINTAINERS: mtd/nand: update Microchip nand entry
From: Nicolas Ferre @ 2018-01-09 13:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <eb6b3cbe8e48faee7e88eca0649e42cbde91ffa6.1515503733.git.nicolas.ferre@microchip.com>

Update Wenyou Yang email address.
Take advantage of this update to move this entry to the MICROCHIP / ATMEL
location and add the DT binding documentation link.

Signed-off-by: Nicolas Ferre <nicolas.ferre@microchip.com>
---
Hi,

Patch against next-20180109.
This patch is somehow dependent on the previous one in the series
("MAINTAINERS: linux-media: update Microchip ISI and ISC entries") but can be
rebased easily.

I don't know if it's better to have them added at the end of the development
cycle or just after rc1: let me know if you plan to take them or if I need to
rebase them for next cycle.

Best regards,
  Nicolas


 MAINTAINERS | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 65c4b59b582f..b48e217d41fb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2373,13 +2373,6 @@ F:	Documentation/devicetree/bindings/input/atmel,maxtouch.txt
 F:	drivers/input/touchscreen/atmel_mxt_ts.c
 F:	include/linux/platform_data/atmel_mxt_ts.h
 
-ATMEL NAND DRIVER
-M:	Wenyou Yang <wenyou.yang@atmel.com>
-M:	Josh Wu <rainyfeeling@outlook.com>
-L:	linux-mtd at lists.infradead.org
-S:	Supported
-F:	drivers/mtd/nand/atmel/*
-
 ATMEL SAMA5D2 ADC DRIVER
 M:	Ludovic Desroches <ludovic.desroches@microchip.com>
 L:	linux-iio at vger.kernel.org
@@ -9110,6 +9103,14 @@ F:	drivers/media/platform/atmel/atmel-isi.c
 F:	include/media/atmel-isi.h
 F:	Documentation/devicetree/bindings/media/atmel-isi.txt
 
+MICROCHIP / ATMEL NAND DRIVER
+M:	Wenyou Yang <wenyou.yang@microchip.com>
+M:	Josh Wu <rainyfeeling@outlook.com>
+L:	linux-mtd at lists.infradead.org
+S:	Supported
+F:	drivers/mtd/nand/atmel/*
+F:	Documentation/devicetree/bindings/mtd/atmel-nand.txt
+
 MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
 M:	Woojung Huh <Woojung.Huh@microchip.com>
 M:	Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
-- 
2.9.0

^ permalink raw reply related

* [PATCH 3/3] [v6] pinctrl: qcom: qdf2xxx: add support for new ACPI HID QCOM8002
From: Linus Walleij @ 2018-01-09 13:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515081875.7000.683.camel@linux.intel.com>

On Thu, Jan 4, 2018 at 5:04 PM, Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
> On Thu, 2018-01-04 at 09:46 -0600, Timur Tabi wrote:
>> On 12/21/2017 07:46 PM, Stephen Boyd wrote:
>> >
>> > Maybe future HIDs could follow the DT design and then we can look
>> > for the same device property name in both firmwares.
>>
>> DSDs generally don't have the vendor prefix that DT properties do.
>
> There are more means to check hardware revisions:
> HID - Hardware ID
> CID - Compatible ID
> UID - Unique ID (good to distinguish instances of the same device on the
> board)
> _HRV - Hardware Revision (6.1.6 describes this one)

Thanks Andy.

I expect a patch using these features, also include Andy on CC
as it appears he knows how this should be done. (In difference
from me...) I'm pretty much relying on other people to understand
ACPI for me, maybe I should attend a course or something.

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH] imx6: fix pcie enumeration
From: Niklas Cassel @ 2018-01-09 13:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1684b8c6-1006-948b-f4f9-c9aaf9cf26a8@ncentric.com>

On 08/01/18 12:13, Koen Vandeputte wrote:
> 
> 
> On 2018-01-08 12:00, Lorenzo Pieralisi wrote:
>> [+cc Joao, Jingoo]
>>
>> On Mon, Jan 08, 2018 at 09:51:37AM +0100, Koen Vandeputte wrote:
>>
>> [...]
>>
>>> [ Node 4 | node-4 ] lspci -v
>>> 00:00.0 PCI bridge: Synopsys, Inc. Device abcd (rev 01) (prog-if 00
>>> [Normal decode])
>>> ???? Flags: bus master, fast devsel, latency 0, IRQ 298
>>> ???? Memory at 01000000 (32-bit, non-prefetchable) [size=1M]
>>> ???? Bus: primary=00, secondary=01, subordinate=01, sec-latency=0
>> ????????????????????????????????????? ^^^^^^^^^^^^^^
>>
>> So basically, the subordinate number in the root port does not
>> affect config space forwarding from what I see and it has always
>> been like that for dwc.
>>
>> You are forced to update it to 0xff because otherwise the kernel
>> stops enumerating bus numbers > 1
> Indeed, which affects all devices using Designware PCIe init + a PCIe bridge downstream
>> but that's a software issue
>> not HW - the subordinate bus number does not seem to affect anything
>> here.
> 
>> Sigh.
>>
>> Another option would consist in forcing the kernel to reassign
>> all bus numbers by setting the PCI_REASSIGN_ALL_BUS flag but
>> that's not a good idea given how inconsistent that flag usage is.
>>
>> I think that updating the subordinate bus numbers in the DWC
>> config register is the correct solution to make sure the kernel
>> won't get confused anymore by what seems to be a fake root port,
>> I need input from DWC maintainers to confirm my understanding.
>>
>> Thanks,
>> Lorenzo
>>
> 
> The patch I'm currently using internally:
> 
> 
> --- a/drivers/pci/host/pcie-designware.c
> +++ b/drivers/pci/host/pcie-designware.c
> @@ -861,7 +861,7 @@ void dw_pcie_setup_rc(struct pcie_port *
> ???? /* setup bus numbers */
> ???? val = dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS);
> ???? val &= 0xff000000;
> -??? val |= 0x00010100;
> +??? val |= 0x00ff0100;
> ???? dw_pcie_writel_rc(pp, PCI_PRIMARY_BUS, val);
> 
> ???? /* setup command register */
> 
> 
> Above version logically fixes it for all dwc devices using a bridge after the RC, not only imx6.
> If this is fine, I would submit the patch above and drop the current one.

I can confirm that commit a20c7f36bd3d ("PCI: Do not allocate more buses than
available in parent") broke enumerating PCIe devices behind a PCIe switch
on ARTPEC-6 (which uses the DWC IP).
(FTR, arch/arm/boot/dts/artpec6.dtsi specifies bus-range = <0x00 0xff>).

This patch resolves the problem.
(I verified on linux-next: next-20180109).

However, note that I had to patch the file
drivers/pci/dwc/pcie-designware-host.c
rather than
drivers/pci/host/pcie-designware.c,
which this patch suggests.

Please feel free to submit a new patch with:

Tested-by: Niklas Cassel <niklas.cassel@axis.com>

> 
> Backporting this to stable kernels (4.9 .. 4.4 .. etc) will fix all nasty warnings on these setups during boot without any change in functionality.
> These kernels will require a separate patch as this source file got moved & renamed.
> 
> 
> Thanks for your time and analysis so far,
> 
> Koen

^ permalink raw reply

* [PATCH 06/19] drm/blend: Add a generic alpha property
From: Maxime Ripard @ 2018-01-09 13:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180109123241.GU26573@phenom.ffwll.local>

On Tue, Jan 09, 2018 at 01:32:41PM +0100, Daniel Vetter wrote:
> On Tue, Jan 09, 2018 at 11:56:25AM +0100, Maxime Ripard wrote:
> > Some drivers duplicate the logic to create a property to store a per-plane
> > alpha.
> > 
> > Let's create a helper in order to move that to the core.
> > 
> > Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
> > Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> 
> Do we have userspace for this?

Wayland seems to be on its way to implement this, with ChromeOS using
it:
https://lists.freedesktop.org/archives/wayland-devel/2017-August/034741.html

and more specifically:
https://chromium.googlesource.com/chromium/src/+/master/third_party/wayland-protocols/unstable/alpha-compositing/alpha-compositing-unstable-v1.xml#118

> Is encoding a fixed 0-255 range really the best idea?

I don't really know, is there hardware or formats where there is more
than 255? Or did you mean less than that?

> I know other drivers have skimped on the rules here a bit ... But at least
> internally (i.e. within the drm_plane_state) we probably should restrict
> ourselves to u8. And this needs real docs (i.e. the full blend equation
> drivers are supposed to implement).

You mean straight vs premultiplied? Maybe we should implement this as
an additional property in read only depending on how the hardware
behaves?

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180109/a0708274/attachment.sig>

^ permalink raw reply

* [PATCH] arm64: mm: Add additional parameter to uaccess_ttbr0_enable
From: Marc Zyngier @ 2018-01-09 13:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180109134326.17479-1-christoffer.dall@linaro.org>

On 09/01/18 13:43, Christoffer Dall wrote:
> Add an extra temporary register parameter to uaccess_ttbr0_enable which
> is about to be required for arm64 PAN support.
> 
> This patch doesn't introduce any functional change but ensures that the
> kernel compiles once the KVM/ARM tree is merged with the arm64 tree by
> ensuring a trivially mergable conflict.
> 
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Marc Zyngier <marc.zyngier@arm.com>
> Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>

Maybe worth adding that this matches
27a921e75711d924617269e0ba4adb8bae9fd0d1 ("arm64: mm: Fix and re-enable
ARM64_SW_TTBR0_PAN")?

Otherwise:

Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>

Thanks for taking care of this.

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply

* [PATCH v2] ARM: dts: sunxi: Add sid for a83t
From: Kyle Evans @ 2018-01-09 13:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180109133412.uyai4s6vfnh37hn2@flea>

On Tue, Jan 9, 2018 at 7:34 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> On Mon, Jan 08, 2018 at 09:30:57AM -0600, Kyle Evans wrote:
>> On Thu, Jan 4, 2018 at 8:01 AM, Maxime Ripard
>> <maxime.ripard@free-electrons.com> wrote:
>> > On Fri, Dec 22, 2017 at 06:11:52PM +0800, Chen-Yu Tsai wrote:
>> >> On Fri, Dec 22, 2017 at 6:07 PM, Emmanuel Vadot <manu@bidouilliste.com> wrote:
>> >> > On Fri, 22 Dec 2017 09:35:08 +0100
>> >> > Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
>> >> >
>> >> >> On Thu, Dec 21, 2017 at 07:09:03PM +0100, Emmanuel Vadot wrote:
>> >> >> >
>> >> >> >  Hi Maxime,
>> >> >> >
>> >> >> > On Thu, 21 Dec 2017 16:26:30 +0100
>> >> >> > Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
>> >> >> >
>> >> >> > > Hi,
>> >> >> > >
>> >> >> > > On Thu, Dec 21, 2017 at 09:19:24AM -0600, Kyle Evans wrote:
>> >> >> > > > On Thu, Dec 21, 2017 at 8:55 AM, Maxime Ripard
>> >> >> > > > <maxime.ripard@free-electrons.com> wrote:
>> >> >> > > > > Hi Kyle,
>> >> >> > > > >
>> >> >> > > > > On Tue, Dec 19, 2017 at 03:05:23PM -0600, kevans91 at ksu.edu wrote:
>> >> >> > > > >> Allwinner a83t has a 1 KB sid block with efuse for security rootkey and
>> >> >> > > > >> thermal calibration data, add node to describe it.
>> >> >> > > > >>
>> >> >> > > > >> a83t-sid is not currently supported by nvmem/sunxi-sid, but it is
>> >> >> > > > >> supported in an external driver for FreeBSD.
>> >> >> > > > >>
>> >> >> > > > >> Signed-off-by: Kyle Evans <kevans91@ksu.edu>
>> >> >> > > > >
>> >> >> > > > > The patch looks fine in itself, but we've had a number of issues with
>> >> >> > > > > the register layout (and access patterns) in the past, so I'd rather
>> >> >> > > > > have something that works in Linux too if possible.
>> >> >> > > >
>> >> >> > > > I have a patch that I think should make it work fine on Linux [1], but
>> >> >> > > > I'm afraid I have little to no capability to test it myself and so I
>> >> >> > > > did not add it as well.
>> >> >> > > >
>> >> >> > > > I do know that the rootkey is offset 0x200 into the given space [2],
>> >> >> > > > as is the case with the H3, and that the readout quirk is not needed.
>> >> >> > > > I wasn't 100% sure that the a83t has 2Kbit worth of efuse space as the
>> >> >> > > > H3, but I do know that thermal data can be found at 0x34 and 0x38 in
>> >> >> > > > this space.
>> >> >> > >
>> >> >> > > Then maybe we should leave it aside until someone takes some time on
>> >> >> > > the A83t.
>> >> >> >
>> >> >> >  Take some time on the Linux driver and do not apply this patch for
>> >> >> > now you mean ?
>> >> >>
>> >> >> Yep.
>> >> >>
>> >> >> Maxime
>> >> >
>> >> >  Since linux doesn't have the compatible in it's driver what would
>> >> > be the harm to add the node in the DTS ? If a quirks is needed because
>> >> > some region is weird this would go in the driver right ? I don't see a
>> >> > technical problem for adding this node right now.
>> >> >  If Kyle confirm the lenght of the region and that no quirk is needed
>> >> > will it be enough ?
>> >>
>> >> I guess I wasn't very clear. I'm OK with the patch going in. The device
>> >> node currently says nothing about how much efuse space there is. The
>> >> memory region covers that and the control section, and the size matches
>> >> what the memory map says.
>> >>
>> >> The size and offset of the efuse space would be dealt with in the driver.
>> >
>> > Let's merge it then.
>> >
>> > Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>>
>> What does the timeline for these things normally look like? I'm new to
>> these parts. =)
>
> We're one week away from the merge window, so it's a bit late for it
> to be merged in 4.16, but it'll be in 4.17.

Ok, cool, that makes sense.

Thanks!

^ permalink raw reply

* [PATCH] imx6: fix pcie enumeration
From: Koen Vandeputte @ 2018-01-09 13:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <3c4b9de6-de1d-55a4-5f44-b3815fa5bf85@axis.com>



On 2018-01-09 14:48, Niklas Cassel wrote:
> <snip>

>>  ???? /* setup bus numbers */
>>  ???? val = dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS);
>>  ???? val &= 0xff000000;
>> -??? val |= 0x00010100;
>> +??? val |= 0x00ff0100;
>>  ???? dw_pcie_writel_rc(pp, PCI_PRIMARY_BUS, val);
>>
>>  ???? /* setup command register */
>>
>>
>> Above version logically fixes it for all dwc devices using a bridge after the RC, not only imx6.
>> If this is fine, I would submit the patch above and drop the current one.
> I can confirm that commit a20c7f36bd3d ("PCI: Do not allocate more buses than
> available in parent") broke enumerating PCIe devices behind a PCIe switch
> on ARTPEC-6 (which uses the DWC IP).
> (FTR, arch/arm/boot/dts/artpec6.dtsi specifies bus-range = <0x00 0xff>).
>
> This patch resolves the problem.
> (I verified on linux-next: next-20180109).
>
> However, note that I had to patch the file
> drivers/pci/dwc/pcie-designware-host.c
> rather than
> drivers/pci/host/pcie-designware.c,
> which this patch suggests.
Above internally used example patch is based on kernel 4.9.74, where 
this file hasn't moved/renamed yet to dwc subfolder
I'll post a proper one shortly for the upstream tree.

Thanks for testing,

Koen
> Please feel free to submit a new patch with:
>
> Tested-by: Niklas Cassel <niklas.cassel@axis.com>
>

^ permalink raw reply

* [PATCH 06/19] drm/blend: Add a generic alpha property
From: Maxime Ripard @ 2018-01-09 13:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <2691997.qmgyvrLtFu@avalon>

Hi Laurent,

On Tue, Jan 09, 2018 at 02:34:47PM +0200, Laurent Pinchart wrote:
> > Some drivers duplicate the logic to create a property to store a per-plane
> > alpha.
> > 
> > Let's create a helper in order to move that to the core.
> > 
> > Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
> > Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > ---
> >  Documentation/gpu/kms-properties.csv |  2 +-
> >  drivers/gpu/drm/drm_atomic.c         |  4 ++++-
> >  drivers/gpu/drm/drm_atomic_helper.c  |  1 +-
> >  drivers/gpu/drm/drm_blend.c          | 32 +++++++++++++++++++++++++++++-
> >  include/drm/drm_blend.h              |  1 +-
> >  include/drm/drm_plane.h              |  6 +++++-
> >  6 files changed, 45 insertions(+), 1 deletion(-)
> > 
> > diff --git a/Documentation/gpu/kms-properties.csv
> > b/Documentation/gpu/kms-properties.csv index 927b65e14219..a3c3969c1992
> > 100644
> > --- a/Documentation/gpu/kms-properties.csv
> > +++ b/Documentation/gpu/kms-properties.csv
> > @@ -99,5 +99,5 @@ radeon,DVI-I,?coherent?,RANGE,"Min=0, Max=1",Connector,TBD
> > ,,"""underscan vborder""",RANGE,"Min=0, Max=128",Connector,TBD
> >  ,Audio,?audio?,ENUM,"{ ""off"", ""on"", ""auto"" }",Connector,TBD
> >  ,FMT Dithering,?dither?,ENUM,"{ ""off"", ""on"" }",Connector,TBD
> > -rcar-du,Generic,"""alpha""",RANGE,"Min=0, Max=255",Plane,TBD
> > +,,"""alpha""",RANGE,"Min=0, Max=255",Plane,Opacity of the plane from
> > transparent (0) to opaque (255) ,,"""colorkey""",RANGE,"Min=0,
> > Max=0x01ffffff",Plane,TBD
> 
> I think more documentation is needed. You should explain how the property 
> operates and which formats it is applicable to. For instance you need to 
> clarify what happens for format that contain an alpha component.

That's a very good point, and I'm not quite sure what should happen in
the first place :)

Should we forbid that configuration?

> >  /**
> > + * drm_plane_create_alpha_property - create a new alpha property
> > + * @plane: drm plane
> > + * @alpha: initial value of alpha, from 0 (transparent) to 255 (opaque)
> 
> Do you have a use case for initializing the alpha value to something else than 
> fully opaque ?

I don't, but thought it might be useful. Maybe it isn't, in which case
I'm totally fine with it getting away.

> > + * This function initializes a generic, mutable, alpha property and
> > + * enables support for it in the DRM core.
> > + *
> > + * Drivers can then attach this property to their plane to enable
> > + * support for configurable plane alpha.
> 
> The function attaches the property to the plane, is the
> documentation outdated ?

Yes, I'll fix it.

> > + * Returns:
> > + * 0 on success, negative error code on failure.
> > + */
> > +int drm_plane_create_alpha_property(struct drm_plane *plane, u8 alpha)
> > +{
> > +	struct drm_property *prop;
> > +
> > +	prop = drm_property_create_range(plane->dev, 0, "alpha", 0, 255);
> 
> Do you think the 0-255 range will fit all use cases ?

This is kind of the same discussion we're having with Daniel, but are
there hardware or formats with an alpha component wider than 255?

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180109/813be6e9/attachment.sig>

^ permalink raw reply

* [PATCH 1/2] MAINTAINERS: linux-media: update Microchip ISI and ISC entries
From: Ludovic Desroches @ 2018-01-09 14:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <eb6b3cbe8e48faee7e88eca0649e42cbde91ffa6.1515503733.git.nicolas.ferre@microchip.com>

On Tue, Jan 09, 2018 at 02:46:39PM +0100, Nicolas Ferre wrote:
> These two image capture interface drivers are now handled
> by Wenyou Yang.
> I benefit from this change to update the two entries by correcting the
> binding documentation link for ISC and moving the ISI to the new
> MICROCHIP / ATMEL location.
> 
> Signed-off-by: Nicolas Ferre <nicolas.ferre@microchip.com>
Acked-by: Ludovic Desroches <ludovic.desroches@microchip.com>

> ---
> Hi,
> 
> Patch against next-20180109.
> Note that I didn't find it useful to have several patches for these changes.
> Tell me if you feel differently.
> 
> I would like to have the Ack from Ludovic and Wenyou obviously. I don't know if
> Songjun can answer as he's not with Microchip anymore.
> 
> Best regards,
>   Nicolas
> 
>  MAINTAINERS | 19 ++++++++++---------
>  1 file changed, 10 insertions(+), 9 deletions(-)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a7d10a2bb980..65c4b59b582f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2353,13 +2353,6 @@ L:	linux-i2c at vger.kernel.org
>  S:	Supported
>  F:	drivers/i2c/busses/i2c-at91.c
>  
> -ATMEL ISI DRIVER
> -M:	Ludovic Desroches <ludovic.desroches@microchip.com>
> -L:	linux-media at vger.kernel.org
> -S:	Supported
> -F:	drivers/media/platform/atmel/atmel-isi.c
> -F:	include/media/atmel-isi.h
> -
>  ATMEL LCDFB DRIVER
>  M:	Nicolas Ferre <nicolas.ferre@microchip.com>
>  L:	linux-fbdev at vger.kernel.org
> @@ -9102,12 +9095,20 @@ S:	Maintained
>  F:	drivers/crypto/atmel-ecc.*
>  
>  MICROCHIP / ATMEL ISC DRIVER
> -M:	Songjun Wu <songjun.wu@microchip.com>
> +M:	Wenyou Yang <wenyou.yang@microchip.com>
>  L:	linux-media at vger.kernel.org
>  S:	Supported
>  F:	drivers/media/platform/atmel/atmel-isc.c
>  F:	drivers/media/platform/atmel/atmel-isc-regs.h
> -F:	devicetree/bindings/media/atmel-isc.txt
> +F:	Documentation/devicetree/bindings/media/atmel-isc.txt
> +
> +MICROCHIP / ATMEL ISI DRIVER
> +M:	Wenyou Yang <wenyou.yang@microchip.com>
> +L:	linux-media at vger.kernel.org
> +S:	Supported
> +F:	drivers/media/platform/atmel/atmel-isi.c
> +F:	include/media/atmel-isi.h
> +F:	Documentation/devicetree/bindings/media/atmel-isi.txt
>  
>  MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
>  M:	Woojung Huh <Woojung.Huh@microchip.com>
> -- 
> 2.9.0
> 

^ permalink raw reply

* [PATCH 1/7] ARM: imx: add timer stop flag to ARM power off state
From: Lucas Stach @ 2018-01-09 14:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <bacee8fe00b0ab54dfa6692bfc956ffd@agner.ch>

Am Dienstag, den 09.01.2018, 14:37 +0100 schrieb Stefan Agner:
> On 2018-01-09 11:13, Lucas Stach wrote:
> > Am Dienstag, den 09.01.2018, 09:25 +0000 schrieb Anson Huang:
> > > 
> > > Best Regards!
> > > Anson Huang
> > > 
> > > 
> > > > -----Original Message-----
> > > > From: Dong Aisheng [mailto:dongas86 at gmail.com]
> > > > Sent: 2018-01-09 5:23 PM
> > > > To: Stefan Agner <stefan@agner.ch>
> > > > Cc: shawnguo at kernel.org; kernel at pengutronix.de; Fabio Estevam
> > > > <fabio.estevam@nxp.com>; robh+dt at kernel.org; mark.rutland at arm.c
> > > > om;
> > > > linux-arm-kernel at lists.infradead.org; devicetree at vger.kernel.or
> > > > g;
> > > > linux-
> > > > kernel at vger.kernel.org; Anson Huang <anson.huang@nxp.com>; dl-
> > > > linux-imx
> > > > <linux-imx@nxp.com>
> > > > Subject: Re: [PATCH 1/7] ARM: imx: add timer stop flag to ARM
> > > > power
> > > > off state
> > > > 
> > > > On Tue, Jan 02, 2018 at 05:42:17PM +0100, Stefan Agner wrote:
> > > > > When the CPU is in ARM power off state the ARM architected
> > > > > timers
> > > > > are
> > > > > stopped. The flag is already present in the higher power WAIT
> > > > > mode.
> > > > > 
> > > > > This allows to use the ARM generic timer on i.MX 6UL/6ULL
> > > > > SoC.
> > > > > Without the flag the kernel freezes when the timer enters the
> > > > > first
> > > > > time ARM power off mode.
> > > > > 
> > > > > Cc: Anson Huang <anson.huang@nxp.com>
> > > > > Signed-off-by: Stefan Agner <stefan@agner.ch>
> > > > 
> > > > It seems ok at my side.
> > > > Did you meet the real issue? If yes, how to reproduce?
> > > > 
> > > > Both mx6sx and mx6ul are using GPT which do not need that flag,
> > > > suppose we
> > > > should remove it, right?
> > > > Anson can help confirm it.
> > > 
> > > For UP system like i.MX6SX, we do NOT enable "cortex-a9-twd-
> > > timer",
> > > so local
> > > timer is NOT used, GPT is used instead, GPT's clock is NOT
> > > disabled
> > > when cpuidle,
> > > so I think we should remove all these Timer stop flag for 6SX
> > > CPUIDLE.
> > 
> > It's correct to set the flag even on UP systems, as the flag means
> > the
> > CPU _local_ timer is stopped in this sleep mode. Also there are
> > systems
> > out there which are using the TWD on UP, as it operates at a higher
> > frequency leading to better wakeup granularity.
> 
> Documentation/devicetree/bindings/arm/twd.txt states that TWD
> provides
> "per-cpu local timer". But as far as I can see TWD still uses SPI
> interrupts, routed through GIC, so is this the differentiation?

Maybe what I wrote wasn't entirely clear. I completely agree with this
patch.

The TWD on Cortex-A9 is a CPU local timer, same as the architected
timer in later cores. It doesn't provide all the benefits of the
architected timer (the clock frequency varies with CPU core clock and
it's not virt capable), but some systems still prefer it over the i.MX
GPT, as it provides much better wakeup granularity.

So annotating the CPU idle states with the timer stop flag is the right
thing to do. This flag has nothing to with the usage of GPT or TWD on a
specific system.

Regards,
Lucas

^ permalink raw reply


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