Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/9] soc: samsung: Add support of suspend-to-RAM on Exynos5433
From: Krzysztof Kozlowski @ 2018-01-09 11:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515484746-10656-1-git-send-email-cw00.choi@samsung.com>

On Tue, Jan 9, 2018 at 8:58 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> In the mainline, there is no case to support the suspend-to-RAM for Samsung
> Exynos SoC. This patchset support the suspend-to-RAM for 64bit Exynos SoC.
>
> For 32bit, arch/arm/mach-exynos/* directoy contains the suspend-related
> codes such as suspend.c/exynos.c. But, 64bit Exynos should contain
> the suspend-related codes in the drivers/soc/samsung/*. So, this patchset
> develop the patch4/5 for drivers/soc/samsung/exynos-pm.c. to support suspend
> 64bit Exynos SoC.
>
> But, I'm not sure what is proper approach for both 32/64bit Exynos.
> - Approach1 : Split out the supend-related codes between 32/64bit.
>   : arch/arm/mach-exynos/* contains the suspend-related codes for 32bit.
>   : drivers/soc/samsung/* contains the suspend-related codes for 64bit.
> - Approach2 : Consolidate the all suspend-related codes to drivers/soc/samsung/.

I prefer approach #2 - consolidate the code... unless this creates
some unmaintainable monster :)

Best regards,
Krzysztof

>
> Please let us know your opinion.
>
> The patch1/2/3 and 6/7/8/9 is just general patch. So, I add 'RFC' prefix to
> only cover-letter, patch4/5. If you want to add the 'RFC' prefix to all
> patches, I'll add 'RFC' prefix on v2.
>
> Based on:
> - git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux.git (branch: for-next)
>
> Need to discuss patch4/5:
> - patch4: soc: samsung: Add generic power-management driver for Exynos
> - patch5: soc: samsung: pm: Add support for suspend-to-ram of Exynos5433
>
> [Remaining Issues]
> - The hang-out happen when disabling the MMC clocks on dw_mmc.c.
>   I reported to Jaehoon Chung. He is checking this issue. To test the
>   suspend-to-ram temporarily, you need to add CLK_IS_CRITICAL flag to
>   'aclk_mmc2/aclk_mmc0/sclk_mmc2/sclk_mmc0'.
> - I enabled the at least kernel configuration in order to test
>   the suspend-to-RAM. So, I need to test it more time with DRM/Multimedia
>   and other. But, the suspend-to-RAM of cpu is successful.
> - Exynos5433 SoC has two EXYNOS5433_EINT_WAKEUP_MASKx registers. The
>   pinctr-exynos.c need to handle the extra EINT_WAKEUP_MASKx for Exynos5433.
>   The suspend-to-ram test is failed on first time and then next tryout is ok.
>   I'm developing it.
>
> Chanwoo Choi (9):
>   clk: samsung: exynos5433: Add clock flag to support suspend-to-ram
>   soc: samsung: pmu: Add powerup_conf callback
>   soc: samsung: pmu: Add the PMU data of exynos5433 to support low-power state
>   soc: samsung: Add generic power-management driver for Exynos
>   soc: samsung: pm: Add support for suspend-to-ram of Exynos5433
>   arm64: dts: exynos: Add iRAM device-tree node for Exynos5433
>   arm64: dts: exynos: Use power key as a wakeup source on TM2/TM2E board
>   arm64: dts: exynos: Add cpu_suspend property of PSCI for exynos5433
>   arm64: dts: exynos: Add cpu topology information for Exynos5433 SoC
>
>  arch/arm/mach-exynos/common.h                      |   3 -
>  arch/arm/mach-exynos/exynos.c                      |  23 +-
>  .../boot/dts/exynos/exynos5433-tm2-common.dtsi     |   1 +
>  arch/arm64/boot/dts/exynos/exynos5433.dtsi         |  47 ++++
>  drivers/clk/samsung/clk-exynos5433.c               |  22 +-
>  drivers/soc/samsung/Makefile                       |   5 +-
>  drivers/soc/samsung/exynos-pm.c                    | 214 +++++++++++++++
>  drivers/soc/samsung/exynos-pmu.c                   |   9 +
>  drivers/soc/samsung/exynos-pmu.h                   |   3 +
>  drivers/soc/samsung/exynos5433-pmu.c               | 286 +++++++++++++++++++++
>  include/linux/soc/samsung/exynos-pm.h              |  21 ++
>  include/linux/soc/samsung/exynos-pmu.h             |   1 +
>  include/linux/soc/samsung/exynos-regs-pmu.h        | 148 +++++++++++
>  13 files changed, 745 insertions(+), 38 deletions(-)
>  create mode 100644 drivers/soc/samsung/exynos-pm.c
>  create mode 100644 drivers/soc/samsung/exynos5433-pmu.c
>  create mode 100644 include/linux/soc/samsung/exynos-pm.h
>
> --
> 1.9.1
>

^ permalink raw reply

* [PATCH] iommu/exynos: Don't unconditionally steal bus ops
From: Robin Murphy @ 2018-01-09 11:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <878c7791-ef37-adc7-8186-27455f2ef67f@samsung.com>

On 09/01/18 09:59, Marek Szyprowski wrote:
> Hi Robin,
> 
> On 2018-01-08 20:27, Robin Murphy wrote:
>> Removing the early device registration hook overlooked the fact that
>> it only ran conditionally on a compatible device being present in the
>> DT. With exynos_iommu_init() now running as an unconditional initcall,
>> problems arise on non-Exynos systems when other IOMMU drivers find
>> themselves unable to install their ops on the platform bus, or at worst
>> the Exynos ops get called with someone else's domain and all hell breaks
>> loose.
>>
>> Fix this by delaying the setting of bus ops until an Exynos IOMMU is
>> actually found, to replicate the previous order of events.
>>
>> Fixes: 928055a01b3f ("iommu/exynos: Remove custom platform device 
>> registration code")
>> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
> 
> Right, my fault. However I will prefer to resurrect code added initially
> by commit a7b67cd5d9af "iommu/exynos: Play nice in multi-platform builds".
> There is no need to do all the things done in the exynos_iommu_init on
> non-Exynos platforms.

Yeah; I had a moment of doubt and left the rest as-is, but I guess all 
of that global setup could in fact be delayed until the first probe, as 
with dma_dev. Anyway, I'll respin just the minimal fix to un-break 
multiplatform, and leave any further refactoring up to you.

Thanks,
Robin.

>> ---
>> ? drivers/iommu/exynos-iommu.c | 16 +++++++---------
>> ? 1 file changed, 7 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
>> index 6a96a4c42153..e9e756156429 100644
>> --- a/drivers/iommu/exynos-iommu.c
>> +++ b/drivers/iommu/exynos-iommu.c
>> @@ -574,6 +574,12 @@ static int __init exynos_sysmmu_probe(struct 
>> platform_device *pdev)
>> ????? struct sysmmu_drvdata *data;
>> ????? struct resource *res;
>> +??? if (platform_bus_type->iommu_ops != &exynos_iommu_ops) {
>> +??????? ret = bus_set_iommu(&platform_bus_type, &exynos_iommu_ops);
>> +??????? if (ret)
>> +??????????? return ret;
>> +??? }
>> +
>> ????? data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>> ????? if (!data)
>> ????????? return -ENOMEM;
>> @@ -1367,16 +1373,8 @@ static int __init exynos_iommu_init(void)
>> ????????? goto err_zero_lv2;
>> ????? }
>> -??? ret = bus_set_iommu(&platform_bus_type, &exynos_iommu_ops);
>> -??? if (ret) {
>> -??????? pr_err("%s: Failed to register exynos-iommu driver.\n",
>> -??????????????????????????????? __func__);
>> -??????? goto err_set_iommu;
>> -??? }
>> -
>> ????? return 0;
>> -err_set_iommu:
>> -??? kmem_cache_free(lv2table_kmem_cache, zero_lv2_table);
>> +
>> ? err_zero_lv2:
>> ????? platform_driver_unregister(&exynos_sysmmu_driver);
>> ? err_reg_driver:
> 
> Best regards

^ permalink raw reply

* [PATCH v3] dt: psci: Update DT bindings to support hierarchical PSCI states
From: Sudeep Holla @ 2018-01-09 12:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515498913-24291-1-git-send-email-ulf.hansson@linaro.org>

(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>

-- 
Regards,
Sudeep

^ permalink raw reply

* [PATCH 05/10] perf tools: Add support for decoding CoreSight trace data
From: Mike Leach @ 2018-01-09 12:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171230003359.GD600@leoy-linaro>

Hi Leo,

The OCSD_GEN_TRC_ELEM_ADDR_NACC element indicates that the decoder
does not have an code image mapping for the address contained in the
trace, at the location described by this element. the payload for the
NACC element is the memory location it could not address.
This means that it cannot correctly follow the instruction execution
sequence described by the individual trace packets.

The dump option works because we do not need to follow the execution
sequence to dump raw trace packets.

It is not clear to me if the perf script option as you specified is
mapping the vmlinux image into the decoder.

Regards

Mike

On 30 December 2017 at 00:33, Leo Yan <leo.yan@linaro.org> wrote:
> Hi Mathieu, Mike,
>
> On Fri, Dec 15, 2017 at 09:44:54AM -0700, Mathieu Poirier wrote:
>> Adding functionality to create a CoreSight trace decoder capable
>> of decoding trace data pushed by a client application.
>>
>> Co-authored-by: Tor Jeremiassen <tor@ti.com>
>> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>> ---
>>  tools/perf/util/cs-etm-decoder/cs-etm-decoder.c | 119 ++++++++++++++++++++++++
>>  1 file changed, 119 insertions(+)
>>
>> diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
>> index 6a4c86b1431f..57b020b0b36f 100644
>> --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
>> +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
>> @@ -200,6 +200,121 @@ static void cs_etm_decoder__clear_buffer(struct cs_etm_decoder *decoder)
>>       }
>>  }
>>
>> +static ocsd_datapath_resp_t
>> +cs_etm_decoder__buffer_packet(struct cs_etm_decoder *decoder,
>> +                           const ocsd_generic_trace_elem *elem,
>> +                           const u8 trace_chan_id,
>> +                           enum cs_etm_sample_type sample_type)
>> +{
>> +     u32 et = 0;
>> +     struct int_node *inode = NULL;
>> +
>> +     if (decoder->packet_count >= MAX_BUFFER - 1)
>> +             return OCSD_RESP_FATAL_SYS_ERR;
>> +
>> +     /* Search the RB tree for the cpu associated with this traceID */
>> +     inode = intlist__find(traceid_list, trace_chan_id);
>> +     if (!inode)
>> +             return OCSD_RESP_FATAL_SYS_ERR;
>> +
>> +     et = decoder->tail;
>> +     decoder->packet_buffer[et].sample_type = sample_type;
>> +     decoder->packet_buffer[et].start_addr = elem->st_addr;
>> +     decoder->packet_buffer[et].end_addr = elem->en_addr;
>> +     decoder->packet_buffer[et].exc = false;
>> +     decoder->packet_buffer[et].exc_ret = false;
>> +     decoder->packet_buffer[et].cpu = *((int *)inode->priv);
>> +
>> +     /* Wrap around if need be */
>> +     et = (et + 1) & (MAX_BUFFER - 1);
>> +
>> +     decoder->tail = et;
>> +     decoder->packet_count++;
>> +
>> +     if (decoder->packet_count == MAX_BUFFER - 1)
>> +             return OCSD_RESP_WAIT;
>> +
>> +     return OCSD_RESP_CONT;
>> +}
>> +
>> +static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
>> +                             const void *context,
>> +                             const ocsd_trc_index_t indx __maybe_unused,
>> +                             const u8 trace_chan_id __maybe_unused,
>> +                             const ocsd_generic_trace_elem *elem)
>> +{
>> +     ocsd_datapath_resp_t resp = OCSD_RESP_CONT;
>> +     struct cs_etm_decoder *decoder = (struct cs_etm_decoder *) context;
>
> After apply this patch set and build 'perf' tool with linking
> OpenCSDv0.8.0 libs, I can everytime OpenCSD parses 'elem->elem_type'
> is OCSD_GEN_TRC_ELEM_ADDR_NACC but not OCSD_GEN_TRC_ELEM_INSTR_RANGE.
>
> As result, the 'perf' tool can dump the raw data with '-D' option but
> it cannot analyze the symbol and symbol offset with below command:
>
> ./perf script -v -a -F cpu,event,ip,sym,symoff -i ./perf.data -k vmlinux
> --kallsyms ./System.map
>
> Have uploaded perf.data/vmlinux/System.map in the folder:
> http://people.linaro.org/~leo.yan/binaries/perf_4.15_r4/
>
> Thanks,
> Leo Yan
>
>> +     switch (elem->elem_type) {
>> +     case OCSD_GEN_TRC_ELEM_UNKNOWN:
>> +             break;
>> +     case OCSD_GEN_TRC_ELEM_NO_SYNC:
>> +             decoder->trace_on = false;
>> +             break;
>> +     case OCSD_GEN_TRC_ELEM_TRACE_ON:
>> +             decoder->trace_on = true;
>> +             break;
>> +     case OCSD_GEN_TRC_ELEM_INSTR_RANGE:
>> +             resp = cs_etm_decoder__buffer_packet(decoder, elem,
>> +                                                  trace_chan_id,
>> +                                                  CS_ETM_RANGE);
>> +             break;
>> +     case OCSD_GEN_TRC_ELEM_EXCEPTION:
>> +             decoder->packet_buffer[decoder->tail].exc = true;
>> +             break;
>> +     case OCSD_GEN_TRC_ELEM_EXCEPTION_RET:
>> +             decoder->packet_buffer[decoder->tail].exc_ret = true;
>> +             break;
>> +     case OCSD_GEN_TRC_ELEM_PE_CONTEXT:
>> +     case OCSD_GEN_TRC_ELEM_EO_TRACE:
>> +     case OCSD_GEN_TRC_ELEM_ADDR_NACC:
>> +     case OCSD_GEN_TRC_ELEM_TIMESTAMP:
>> +     case OCSD_GEN_TRC_ELEM_CYCLE_COUNT:
>> +     case OCSD_GEN_TRC_ELEM_ADDR_UNKNOWN:
>> +     case OCSD_GEN_TRC_ELEM_EVENT:
>> +     case OCSD_GEN_TRC_ELEM_SWTRACE:
>> +     case OCSD_GEN_TRC_ELEM_CUSTOM:
>> +     default:
>> +             break;
>> +     }
>> +
>> +     return resp;
>> +}
>> +
>> +static int cs_etm_decoder__create_etm_packet_decoder(
>> +                                     struct cs_etm_trace_params *t_params,
>> +                                     struct cs_etm_decoder *decoder)
>> +{
>> +     const char *decoder_name;
>> +     ocsd_etmv4_cfg trace_config_etmv4;
>> +     void *trace_config;
>> +     u8 csid;
>> +
>> +     switch (t_params->protocol) {
>> +     case CS_ETM_PROTO_ETMV4i:
>> +             cs_etm_decoder__gen_etmv4_config(t_params, &trace_config_etmv4);
>> +             decoder_name = OCSD_BUILTIN_DCD_ETMV4I;
>> +             trace_config = &trace_config_etmv4;
>> +             break;
>> +     default:
>> +             return -1;
>> +     }
>> +
>> +     if (ocsd_dt_create_decoder(decoder->dcd_tree,
>> +                                  decoder_name,
>> +                                  OCSD_CREATE_FLG_FULL_DECODER,
>> +                                  trace_config, &csid))
>> +             return -1;
>> +
>> +     if (ocsd_dt_set_gen_elem_outfn(decoder->dcd_tree,
>> +                                    cs_etm_decoder__gen_trace_elem_printer,
>> +                                    decoder))
>> +             return -1;
>> +
>> +     return 0;
>> +}
>> +
>>  static int
>>  cs_etm_decoder__create_etm_decoder(struct cs_etm_decoder_params *d_params,
>>                                  struct cs_etm_trace_params *t_params,
>> @@ -208,6 +323,10 @@ cs_etm_decoder__create_etm_decoder(struct cs_etm_decoder_params *d_params,
>>       if (d_params->operation == CS_ETM_OPERATION_PRINT)
>>               return cs_etm_decoder__create_etm_packet_printer(t_params,
>>                                                                decoder);
>> +     else if (d_params->operation == CS_ETM_OPERATION_DECODE)
>> +             return cs_etm_decoder__create_etm_packet_decoder(t_params,
>> +                                                              decoder);
>> +
>>       return -1;
>>  }
>>
>> --
>> 2.7.4
>>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



-- 
Mike Leach
Principal Engineer, ARM Ltd.
Blackburn Design Centre. UK

^ permalink raw reply

* [PATCH] drivers: firmware: xilinx: Add ZynqMP firmware driver
From: Aishwarya Pant @ 2018-01-09 12:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515449227-5096-1-git-send-email-jollys@xilinx.com>

On Mon, Jan 08, 2018 at 02:07:07PM -0800, Jolly Shah wrote:
> This patch is adding communication layer with firmware.
> Firmware driver provides an interface to firmware APIs.
> Interface APIs can be used by any driver to communicate to
> PMUFW(Platform Management Unit). All requests go through ATF.
> Firmware-debug provides debugfs interface to all APIs.
> Firmware-ggs provides read/write interface to
> global storage registers.
> 
> Signed-off-by: Jolly Shah <jollys@xilinx.com>
> Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
> ---
>  .../firmware/xilinx/xlnx,zynqmp-firmware.txt       |   16 +
>  arch/arm64/Kconfig.platforms                       |    1 +
>  drivers/firmware/Kconfig                           |    1 +
>  drivers/firmware/Makefile                          |    1 +
>  drivers/firmware/xilinx/Kconfig                    |    4 +
>  drivers/firmware/xilinx/Makefile                   |    4 +
>  drivers/firmware/xilinx/zynqmp/Kconfig             |   23 +
>  drivers/firmware/xilinx/zynqmp/Makefile            |    5 +
>  drivers/firmware/xilinx/zynqmp/firmware-debug.c    |  540 +++++++++++
>  drivers/firmware/xilinx/zynqmp/firmware-ggs.c      |  298 ++++++
>  drivers/firmware/xilinx/zynqmp/firmware.c          | 1024 ++++++++++++++++++++
>  .../linux/firmware/xilinx/zynqmp/firmware-debug.h  |   32 +
>  include/linux/firmware/xilinx/zynqmp/firmware.h    |  573 +++++++++++
>  13 files changed, 2522 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
>  create mode 100644 drivers/firmware/xilinx/Kconfig
>  create mode 100644 drivers/firmware/xilinx/Makefile
>  create mode 100644 drivers/firmware/xilinx/zynqmp/Kconfig
>  create mode 100644 drivers/firmware/xilinx/zynqmp/Makefile
>  create mode 100644 drivers/firmware/xilinx/zynqmp/firmware-debug.c
>  create mode 100644 drivers/firmware/xilinx/zynqmp/firmware-ggs.c
>  create mode 100644 drivers/firmware/xilinx/zynqmp/firmware.c
>  create mode 100644 include/linux/firmware/xilinx/zynqmp/firmware-debug.h
>  create mode 100644 include/linux/firmware/xilinx/zynqmp/firmware.h
> 
> diff --git a/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt b/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
> new file mode 100644
> index 0000000..ace111c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
> @@ -0,0 +1,16 @@
> +Xilinx Zynq MPSoC Firmware Device Tree Bindings
> +
> +The zynqmp-firmware node describes the interface to platform firmware.
> +
> +Required properties:
> + - compatible:	Must contain:  "xlnx,zynqmp-firmware"
> + - method:	The method of calling the PM-API firmware layer.
> +		Permitted values are:
> +		 - "smc" : To be used in configurations without a hypervisor
> +		 - "hvc" : To be used when hypervisor is present
> +
> +Examples:
> +	firmware: firmware {
> +		compatible = "xlnx,zynqmp-firmware";
> +		method = "smc";
> +	};
> diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
> index 2401373..3dd3ae9 100644
> --- a/arch/arm64/Kconfig.platforms
> +++ b/arch/arm64/Kconfig.platforms
> @@ -273,6 +273,7 @@ config ARCH_ZX
>  
>  config ARCH_ZYNQMP
>  	bool "Xilinx ZynqMP Family"
> +	select ZYNQMP_FIRMWARE
>  	help
>  	  This enables support for Xilinx ZynqMP Family
>  
> diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
> index fa87a055..18fc2a8 100644
> --- a/drivers/firmware/Kconfig
> +++ b/drivers/firmware/Kconfig
> @@ -249,5 +249,6 @@ source "drivers/firmware/google/Kconfig"
>  source "drivers/firmware/efi/Kconfig"
>  source "drivers/firmware/meson/Kconfig"
>  source "drivers/firmware/tegra/Kconfig"
> +source "drivers/firmware/xilinx/Kconfig"
>  
>  endmenu
> diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
> index feaa890..43a24b5 100644
> --- a/drivers/firmware/Makefile
> +++ b/drivers/firmware/Makefile
> @@ -30,3 +30,4 @@ obj-$(CONFIG_GOOGLE_FIRMWARE)	+= google/
>  obj-$(CONFIG_EFI)		+= efi/
>  obj-$(CONFIG_UEFI_CPER)		+= efi/
>  obj-y				+= tegra/
> +obj-y				+= xilinx/
> diff --git a/drivers/firmware/xilinx/Kconfig b/drivers/firmware/xilinx/Kconfig
> new file mode 100644
> index 0000000..dd3cddb
> --- /dev/null
> +++ b/drivers/firmware/xilinx/Kconfig
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier:	GPL-2.0+
> +# Kconfig for Xilinx firmwares
> +
> +source "drivers/firmware/xilinx/zynqmp/Kconfig"
> diff --git a/drivers/firmware/xilinx/Makefile b/drivers/firmware/xilinx/Makefile
> new file mode 100644
> index 0000000..aba1f86
> --- /dev/null
> +++ b/drivers/firmware/xilinx/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier:	GPL-2.0+
> +# Makefile for Xilinx firmwares
> +
> +obj-$(CONFIG_ARCH_ZYNQMP) += zynqmp/
> diff --git a/drivers/firmware/xilinx/zynqmp/Kconfig b/drivers/firmware/xilinx/zynqmp/Kconfig
> new file mode 100644
> index 0000000..1f815e0
> --- /dev/null
> +++ b/drivers/firmware/xilinx/zynqmp/Kconfig
> @@ -0,0 +1,23 @@
> +# SPDX-License-Identifier:	GPL-2.0+
> +# Kconfig for Xilinx zynqmp firmware
> +
> +menu "Zynq MPSoC Firmware Drivers"
> +	depends on ARCH_ZYNQMP
> +
> +config ZYNQMP_FIRMWARE
> +	bool "Enable Xilinx Zynq MPSoC firmware interface"
> +	help
> +	  Firmware interface driver is used by different to
> +	  communicate with the firmware for various platform
> +	  management services.
> +	  Say yes to enable zynqmp firmware interface driver.
> +	  In doubt, say N
> +
> +config ZYNQMP_FIRMWARE_DEBUG
> +	bool "Enable Xilinx Zynq MPSoC firmware debug APIs"
> +	depends on ARCH_ZYNQMP && DEBUG_FS
> +	help
> +	  Say yes to enable zynqmp firmware interface debug APIs.
> +	  In doubt, say N
> +
> +endmenu
> diff --git a/drivers/firmware/xilinx/zynqmp/Makefile b/drivers/firmware/xilinx/zynqmp/Makefile
> new file mode 100644
> index 0000000..97086b5
> --- /dev/null
> +++ b/drivers/firmware/xilinx/zynqmp/Makefile
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier:	GPL-2.0+
> +# Makefile for Xilinx firmwares
> +
> +obj-$(CONFIG_ZYNQMP_FIRMWARE) += firmware.o firmware-ggs.o
> +obj-$(CONFIG_ZYNQMP_FIRMWARE_DEBUG) += firmware-debug.o
> diff --git a/drivers/firmware/xilinx/zynqmp/firmware-debug.c b/drivers/firmware/xilinx/zynqmp/firmware-debug.c
> new file mode 100644
> index 0000000..83b1c45
> --- /dev/null
> +++ b/drivers/firmware/xilinx/zynqmp/firmware-debug.c
> @@ -0,0 +1,540 @@
> +/*
> + * Xilinx Zynq MPSoC Firmware layer for debugfs APIs
> + *
> + *  Copyright (C) 2014-2017 Xilinx, Inc.
> + *
> + *  Michal Simek <michal.simek@xilinx.com>
> + *  Davorin Mista <davorin.mista@aggios.com>
> + *  Jolly Shah <jollys@xilinx.com>
> + *  Rajan Vaja <rajanv@xilinx.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <linux/compiler.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/debugfs.h>
> +#include <linux/uaccess.h>
> +#include <linux/firmware/xilinx/zynqmp/firmware.h>
> +#include <linux/firmware/xilinx/zynqmp/firmware-debug.h>
> +
> +#define DRIVER_NAME	"zynqmp-firmware"
> +
> +/**
> + * zynqmp_pm_self_suspend - PM call for master to suspend itself
> + * @node:	Node ID of the master or subsystem
> + * @latency:	Requested maximum wakeup latency (not supported)
> + * @state:	Requested state (not supported)
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +int zynqmp_pm_self_suspend(const u32 node,
> +			   const u32 latency,
> +			   const u32 state)
> +{
> +	return invoke_pm_fn(SELF_SUSPEND, node, latency, state, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_abort_suspend - PM call to announce that a prior suspend request
> + *				is to be aborted.
> + * @reason:	Reason for the abort
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +int zynqmp_pm_abort_suspend(const enum zynqmp_pm_abort_reason reason)
> +{
> +	return invoke_pm_fn(ABORT_SUSPEND, reason, 0, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_register_notifier - Register the PU to be notified of PM events
> + * @node:	Node ID of the slave
> + * @event:	The event to be notified about
> + * @wake:	Wake up on event
> + * @enable:	Enable or disable the notifier
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +int zynqmp_pm_register_notifier(const u32 node, const u32 event,
> +				const u32 wake, const u32 enable)
> +{
> +	return invoke_pm_fn(REGISTER_NOTIFIER, node, event,
> +			    wake, enable, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_argument_value - Extract argument value from a PM-API request
> + * @arg:	Entered PM-API argument in string format
> + *
> + * Return:	Argument value in unsigned integer format on success
> + *		0 otherwise
> + */
> +static u64 zynqmp_pm_argument_value(char *arg)
> +{
> +	u64 value;
> +
> +	if (!arg)
> +		return 0;
> +
> +	if (!kstrtou64(arg, 0, &value))
> +		return value;
> +
> +	return 0;
> +}
> +
> +static struct dentry *zynqmp_pm_debugfs_dir;
> +static struct dentry *zynqmp_pm_debugfs_power;
> +static struct dentry *zynqmp_pm_debugfs_api_version;
> +
> +/**
> + * zynqmp_pm_debugfs_api_write - debugfs write function
> + * @file:	User file structure
> + * @ptr:	User entered PM-API string
> + * @len:	Length of the userspace buffer
> + * @off:	Offset within the file
> + *
> + * Return:	Number of bytes copied if PM-API request succeeds,
> + *		the corresponding error code otherwise
> + *
> + * Used for triggering pm api functions by writing
> + * echo <pm_api_id>    > /sys/kernel/debug/zynqmp_pm/power or
> + * echo <pm_api_name>  > /sys/kernel/debug/zynqmp_pm/power
> + */
> +static ssize_t zynqmp_pm_debugfs_api_write(struct file *file,
> +					   const char __user *ptr, size_t len,
> +					   loff_t *off)
> +{
> +	char *kern_buff, *tmp_buff;
> +	char *pm_api_req;
> +	u32 pm_id = 0;
> +	u64 pm_api_arg[4];
> +	/* Return values from PM APIs calls */
> +	u32 pm_api_ret[4] = {0, 0, 0, 0};
> +	u32 pm_api_version;
> +
> +	int ret;
> +	int i = 0;
> +	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +	if (!eemi_ops)
> +		return -ENXIO;
> +
> +	if (*off != 0 || len <= 0)
> +		return -EINVAL;
> +
> +	kern_buff = kzalloc(len, GFP_KERNEL);
> +	if (!kern_buff)
> +		return -ENOMEM;
> +	tmp_buff = kern_buff;
> +
> +	while (i < ARRAY_SIZE(pm_api_arg))
> +		pm_api_arg[i++] = 0;
> +
> +	ret = strncpy_from_user(kern_buff, ptr, len);
> +	if (ret < 0) {
> +		ret = -EFAULT;
> +		goto err;
> +	}
> +
> +	/* Read the API name from a user request */
> +	pm_api_req = strsep(&kern_buff, " ");
> +
> +	if (strncasecmp(pm_api_req, "REQUEST_SUSPEND", 15) == 0)
> +		pm_id = REQUEST_SUSPEND;
> +	else if (strncasecmp(pm_api_req, "SELF_SUSPEND", 12) == 0)
> +		pm_id = SELF_SUSPEND;
> +	else if (strncasecmp(pm_api_req, "FORCE_POWERDOWN", 15) == 0)
> +		pm_id = FORCE_POWERDOWN;
> +	else if (strncasecmp(pm_api_req, "ABORT_SUSPEND", 13) == 0)
> +		pm_id = ABORT_SUSPEND;
> +	else if (strncasecmp(pm_api_req, "REQUEST_WAKEUP", 14) == 0)
> +		pm_id = REQUEST_WAKEUP;
> +	else if (strncasecmp(pm_api_req, "SET_WAKEUP_SOURCE", 17) == 0)
> +		pm_id = SET_WAKEUP_SOURCE;
> +	else if (strncasecmp(pm_api_req, "SYSTEM_SHUTDOWN", 15) == 0)
> +		pm_id = SYSTEM_SHUTDOWN;
> +	else if (strncasecmp(pm_api_req, "REQUEST_NODE", 12) == 0)
> +		pm_id = REQUEST_NODE;
> +	else if (strncasecmp(pm_api_req, "RELEASE_NODE", 12) == 0)
> +		pm_id = RELEASE_NODE;
> +	else if (strncasecmp(pm_api_req, "SET_REQUIREMENT", 15) == 0)
> +		pm_id = SET_REQUIREMENT;
> +	else if (strncasecmp(pm_api_req, "SET_MAX_LATENCY", 15) == 0)
> +		pm_id = SET_MAX_LATENCY;
> +	else if (strncasecmp(pm_api_req, "GET_API_VERSION", 15) == 0)
> +		pm_id = GET_API_VERSION;
> +	else if (strncasecmp(pm_api_req, "SET_CONFIGURATION", 17) == 0)
> +		pm_id = SET_CONFIGURATION;
> +	else if (strncasecmp(pm_api_req, "GET_NODE_STATUS", 15) == 0)
> +		pm_id = GET_NODE_STATUS;
> +	else if (strncasecmp(pm_api_req,
> +			     "GET_OPERATING_CHARACTERISTIC", 28) == 0)
> +		pm_id = GET_OPERATING_CHARACTERISTIC;
> +	else if (strncasecmp(pm_api_req, "REGISTER_NOTIFIER", 17) == 0)
> +		pm_id = REGISTER_NOTIFIER;
> +	else if (strncasecmp(pm_api_req, "RESET_ASSERT", 12) == 0)
> +		pm_id = RESET_ASSERT;
> +	else if (strncasecmp(pm_api_req, "RESET_GET_STATUS", 16) == 0)
> +		pm_id = RESET_GET_STATUS;
> +	else if (strncasecmp(pm_api_req, "MMIO_READ", 9) == 0)
> +		pm_id = MMIO_READ;
> +	else if (strncasecmp(pm_api_req, "MMIO_WRITE", 10) == 0)
> +		pm_id = MMIO_WRITE;
> +	else if (strncasecmp(pm_api_req, "GET_CHIPID", 9) == 0)
> +		pm_id = GET_CHIPID;
> +	else if (strncasecmp(pm_api_req, "PINCTRL_GET_FUNCTION", 21) == 0)
> +		pm_id = PINCTRL_GET_FUNCTION;
> +	else if (strncasecmp(pm_api_req, "PINCTRL_SET_FUNCTION", 21) == 0)
> +		pm_id = PINCTRL_SET_FUNCTION;
> +	else if (strncasecmp(pm_api_req,
> +			     "PINCTRL_CONFIG_PARAM_GET", 25) == 0)
> +		pm_id = PINCTRL_CONFIG_PARAM_GET;
> +	else if (strncasecmp(pm_api_req,
> +			     "PINCTRL_CONFIG_PARAM_SET", 25) == 0)
> +		pm_id = PINCTRL_CONFIG_PARAM_SET;
> +	else if (strncasecmp(pm_api_req, "IOCTL", 6) == 0)
> +		pm_id = IOCTL;
> +	else if (strncasecmp(pm_api_req, "CLOCK_ENABLE", 12) == 0)
> +		pm_id = CLOCK_ENABLE;
> +	else if (strncasecmp(pm_api_req, "CLOCK_DISABLE", 13) == 0)
> +		pm_id = CLOCK_DISABLE;
> +	else if (strncasecmp(pm_api_req, "CLOCK_GETSTATE", 14) == 0)
> +		pm_id = CLOCK_GETSTATE;
> +	else if (strncasecmp(pm_api_req, "CLOCK_SETDIVIDER", 16) == 0)
> +		pm_id = CLOCK_SETDIVIDER;
> +	else if (strncasecmp(pm_api_req, "CLOCK_GETDIVIDER", 16) == 0)
> +		pm_id = CLOCK_GETDIVIDER;
> +	else if (strncasecmp(pm_api_req, "CLOCK_SETRATE", 13) == 0)
> +		pm_id = CLOCK_SETRATE;
> +	else if (strncasecmp(pm_api_req, "CLOCK_GETRATE", 13) == 0)
> +		pm_id = CLOCK_GETRATE;
> +	else if (strncasecmp(pm_api_req, "CLOCK_SETPARENT", 15) == 0)
> +		pm_id = CLOCK_SETPARENT;
> +	else if (strncasecmp(pm_api_req, "CLOCK_GETPARENT", 15) == 0)
> +		pm_id = CLOCK_GETPARENT;
> +	else if (strncasecmp(pm_api_req, "QUERY_DATA", 22) == 0)
> +		pm_id = QUERY_DATA;
> +
> +	/* If no name was entered look for PM-API ID instead */
> +	else if (kstrtouint(pm_api_req, 10, &pm_id))
> +		ret = -EINVAL;
> +
> +	/* Read node_id and arguments from the PM-API request */
> +	i = 0;
> +	pm_api_req = strsep(&kern_buff, " ");
> +	while ((i < ARRAY_SIZE(pm_api_arg)) && pm_api_req) {
> +		pm_api_arg[i++] = zynqmp_pm_argument_value(pm_api_req);
> +		pm_api_req = strsep(&kern_buff, " ");
> +	}
> +
> +	switch (pm_id) {
> +	case GET_API_VERSION:
> +		eemi_ops->get_api_version(&pm_api_version);
> +		pr_info("%s PM-API Version = %d.%d\n", __func__,
> +			pm_api_version >> 16, pm_api_version & 0xffff);
> +		break;
> +	case REQUEST_SUSPEND:
> +		ret = eemi_ops->request_suspend(pm_api_arg[0],
> +						pm_api_arg[1] ? pm_api_arg[1] :
> +						ZYNQMP_PM_REQUEST_ACK_NO,
> +						pm_api_arg[2] ? pm_api_arg[2] :
> +						ZYNQMP_PM_MAX_LATENCY, 0);
> +		break;
> +	case SELF_SUSPEND:
> +		ret = zynqmp_pm_self_suspend(pm_api_arg[0],
> +					     pm_api_arg[1] ? pm_api_arg[1] :
> +					     ZYNQMP_PM_MAX_LATENCY, 0);
> +		break;
> +	case FORCE_POWERDOWN:
> +		ret = eemi_ops->force_powerdown(pm_api_arg[0],
> +						pm_api_arg[1] ? pm_api_arg[1] :
> +						ZYNQMP_PM_REQUEST_ACK_NO);
> +		break;
> +	case ABORT_SUSPEND:
> +		ret = zynqmp_pm_abort_suspend(pm_api_arg[0] ? pm_api_arg[0] :
> +					      ZYNQMP_PM_ABORT_REASON_UNKNOWN);
> +		break;
> +	case REQUEST_WAKEUP:
> +		ret = eemi_ops->request_wakeup(pm_api_arg[0],
> +					       pm_api_arg[1], pm_api_arg[2],
> +					       pm_api_arg[3] ? pm_api_arg[3] :
> +					       ZYNQMP_PM_REQUEST_ACK_NO);
> +		break;
> +	case SET_WAKEUP_SOURCE:
> +		ret = eemi_ops->set_wakeup_source(pm_api_arg[0], pm_api_arg[1],
> +						  pm_api_arg[2]);
> +		break;
> +	case SYSTEM_SHUTDOWN:
> +		ret = eemi_ops->system_shutdown(pm_api_arg[0], pm_api_arg[1]);
> +		break;
> +	case REQUEST_NODE:
> +		ret = eemi_ops->request_node(pm_api_arg[0],
> +					     pm_api_arg[1] ? pm_api_arg[1] :
> +					     ZYNQMP_PM_CAPABILITY_ACCESS,
> +					     pm_api_arg[2] ? pm_api_arg[2] : 0,
> +					     pm_api_arg[3] ? pm_api_arg[3] :
> +					     ZYNQMP_PM_REQUEST_ACK_BLOCKING);
> +		break;
> +	case RELEASE_NODE:
> +		ret = eemi_ops->release_node(pm_api_arg[0]);
> +		break;
> +	case SET_REQUIREMENT:
> +		ret = eemi_ops->set_requirement(pm_api_arg[0],
> +						pm_api_arg[1] ? pm_api_arg[1] :
> +						ZYNQMP_PM_CAPABILITY_CONTEXT,
> +						pm_api_arg[2] ?
> +						pm_api_arg[2] : 0,
> +						pm_api_arg[3] ? pm_api_arg[3] :
> +						ZYNQMP_PM_REQUEST_ACK_BLOCKING);
> +		break;
> +	case SET_MAX_LATENCY:
> +		ret = eemi_ops->set_max_latency(pm_api_arg[0],
> +						pm_api_arg[1] ? pm_api_arg[1] :
> +						ZYNQMP_PM_MAX_LATENCY);
> +		break;
> +	case SET_CONFIGURATION:
> +		ret = eemi_ops->set_configuration(pm_api_arg[0]);
> +		break;
> +	case GET_NODE_STATUS:
> +		ret = eemi_ops->get_node_status(pm_api_arg[0],
> +						&pm_api_ret[0],
> +						&pm_api_ret[1],
> +						&pm_api_ret[2]);
> +		if (!ret)
> +			pr_info("GET_NODE_STATUS:\n\tNodeId: %llu\n\tStatus: %u\n\tRequirements: %u\n\tUsage: %u\n",
> +				pm_api_arg[0], pm_api_ret[0],
> +				pm_api_ret[1], pm_api_ret[2]);
> +		break;
> +	case GET_OPERATING_CHARACTERISTIC:
> +		ret = eemi_ops->get_operating_characteristic(pm_api_arg[0],
> +				pm_api_arg[1] ? pm_api_arg[1] :
> +				ZYNQMP_PM_OPERATING_CHARACTERISTIC_POWER,
> +				&pm_api_ret[0]);
> +		if (!ret)
> +			pr_info("GET_OPERATING_CHARACTERISTIC:\n\tNodeId: %llu\n\tType: %llu\n\tResult: %u\n",
> +				pm_api_arg[0], pm_api_arg[1], pm_api_ret[0]);
> +		break;
> +	case REGISTER_NOTIFIER:
> +		ret = zynqmp_pm_register_notifier(pm_api_arg[0],
> +						  pm_api_arg[1] ?
> +						  pm_api_arg[1] : 0,
> +						  pm_api_arg[2] ?
> +						  pm_api_arg[2] : 0,
> +						  pm_api_arg[3] ?
> +						  pm_api_arg[3] : 0);
> +		break;
> +	case RESET_ASSERT:
> +		ret = eemi_ops->reset_assert(pm_api_arg[0], pm_api_arg[1]);
> +		break;
> +	case RESET_GET_STATUS:
> +		ret = eemi_ops->reset_get_status(pm_api_arg[0], &pm_api_ret[0]);
> +		pr_info("%s Reset status: %u\n", __func__, pm_api_ret[0]);
> +		break;
> +	case GET_CHIPID:
> +		ret = eemi_ops->get_chipid(&pm_api_ret[0], &pm_api_ret[1]);
> +		pr_info("%s idcode: %#x, version:%#x\n",
> +			__func__, pm_api_ret[0], pm_api_ret[1]);
> +		break;
> +	case PINCTRL_GET_FUNCTION:
> +		ret = eemi_ops->pinctrl_get_function(pm_api_arg[0],
> +						     &pm_api_ret[0]);
> +		pr_info("%s Current set function for the pin: %u\n",
> +			__func__, pm_api_ret[0]);
> +		break;
> +	case PINCTRL_SET_FUNCTION:
> +		ret = eemi_ops->pinctrl_set_function(pm_api_arg[0],
> +						     pm_api_arg[1]);
> +		break;
> +	case PINCTRL_CONFIG_PARAM_GET:
> +		ret = eemi_ops->pinctrl_get_config(pm_api_arg[0], pm_api_arg[1],
> +						   &pm_api_ret[0]);
> +		pr_info("%s pin: %llu, param: %llu, value: %u\n",
> +			__func__, pm_api_arg[0], pm_api_arg[1],
> +			pm_api_ret[0]);
> +		break;
> +	case PINCTRL_CONFIG_PARAM_SET:
> +		ret = eemi_ops->pinctrl_set_config(pm_api_arg[0],
> +						   pm_api_arg[1],
> +						   pm_api_arg[2]);
> +		break;
> +	case IOCTL:
> +		ret = eemi_ops->ioctl(pm_api_arg[0], pm_api_arg[1],
> +				      pm_api_arg[2], pm_api_arg[3],
> +				      &pm_api_ret[0]);
> +		if (pm_api_arg[1] == IOCTL_GET_RPU_OPER_MODE ||
> +		    pm_api_arg[1] == IOCTL_GET_PLL_FRAC_MODE ||
> +		    pm_api_arg[1] == IOCTL_GET_PLL_FRAC_DATA ||
> +		    pm_api_arg[1] == IOCTL_READ_GGS ||
> +		    pm_api_arg[1] == IOCTL_READ_PGGS)
> +			pr_info("%s Value: %u\n",
> +				__func__, pm_api_ret[1]);
> +		break;
> +	case CLOCK_ENABLE:
> +		ret = eemi_ops->clock_enable(pm_api_arg[0]);
> +		break;
> +	case CLOCK_DISABLE:
> +		ret = eemi_ops->clock_disable(pm_api_arg[0]);
> +		break;
> +	case CLOCK_GETSTATE:
> +		ret = eemi_ops->clock_getstate(pm_api_arg[0], &pm_api_ret[0]);
> +		pr_info("%s state: %u\n", __func__, pm_api_ret[0]);
> +		break;
> +	case CLOCK_SETDIVIDER:
> +		ret = eemi_ops->clock_setdivider(pm_api_arg[0], pm_api_arg[1]);
> +		break;
> +	case CLOCK_GETDIVIDER:
> +		ret = eemi_ops->clock_getdivider(pm_api_arg[0], &pm_api_ret[0]);
> +		pr_info("%s Divider Value: %d\n", __func__, pm_api_ret[0]);
> +		break;
> +	case CLOCK_SETRATE:
> +		ret = eemi_ops->clock_setrate(pm_api_arg[0], pm_api_arg[1]);
> +		break;
> +	case CLOCK_GETRATE:
> +		ret = eemi_ops->clock_getrate(pm_api_arg[0], &pm_api_ret[0]);
> +		pr_info("%s Rate Value: %u\n", __func__, pm_api_ret[0]);
> +		break;
> +	case CLOCK_SETPARENT:
> +		ret = eemi_ops->clock_setparent(pm_api_arg[0], pm_api_arg[1]);
> +		break;
> +	case CLOCK_GETPARENT:
> +		ret = eemi_ops->clock_getparent(pm_api_arg[0], &pm_api_ret[0]);
> +		pr_info("%s Parent Index: %u\n", __func__, pm_api_ret[0]);
> +		break;
> +	case QUERY_DATA:
> +	{
> +		struct zynqmp_pm_query_data qdata = {0};
> +
> +		qdata.qid = pm_api_arg[0];
> +		qdata.arg1 = pm_api_arg[1];
> +		qdata.arg2 = pm_api_arg[2];
> +		qdata.arg3 = pm_api_arg[3];
> +
> +		ret = eemi_ops->query_data(qdata, pm_api_ret);
> +
> +		pr_info("%s: data[0] = 0x%08x\n", __func__, pm_api_ret[0]);
> +		pr_info("%s: data[1] = 0x%08x\n", __func__, pm_api_ret[1]);
> +		pr_info("%s: data[2] = 0x%08x\n", __func__, pm_api_ret[2]);
> +		pr_info("%s: data[3] = 0x%08x\n", __func__, pm_api_ret[3]);
> +		break;
> +	}
> +	default:
> +		pr_err("%s Unsupported PM-API request\n", __func__);
> +		ret = -EINVAL;
> +	}
> +
> +err:
> +	kfree(tmp_buff);
> +	if (ret)
> +		return ret;
> +
> +	return len;
> +}
> +
> +/**
> + * zynqmp_pm_debugfs_api_version_read - debugfs read function
> + * @file:	User file structure
> + * @ptr:	Requested pm_api_version string
> + * @len:	Length of the userspace buffer
> + * @off:	Offset within the file
> + *
> + * Return:	Length of the version string on success
> + *		-EFAULT otherwise
> + *
> + * Used to display the pm api version.
> + * cat /sys/kernel/debug/zynqmp_pm/pm_api_version
> + */
> +static ssize_t zynqmp_pm_debugfs_api_version_read(struct file *file,
> +						  char __user *ptr, size_t len,
> +						  loff_t *off)
> +{
> +	char *kern_buff;
> +	int ret;
> +	int kern_buff_len;
> +	u32 pm_api_version;
> +	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +	if (!eemi_ops || !eemi_ops->get_api_version)
> +		return -ENXIO;
> +
> +	if (len <= 0)
> +		return -EINVAL;
> +
> +	if (*off != 0)
> +		return 0;
> +
> +	kern_buff = kzalloc(len, GFP_KERNEL);
> +	if (!kern_buff)
> +		return -ENOMEM;
> +
> +	eemi_ops->get_api_version(&pm_api_version);
> +	sprintf(kern_buff, "PM-API Version = %d.%d\n",
> +		pm_api_version >> 16, pm_api_version & 0xffff);
> +	kern_buff_len = strlen(kern_buff) + 1;
> +
> +	if (len > kern_buff_len)
> +		len = kern_buff_len;
> +	ret = copy_to_user(ptr, kern_buff, len);
> +
> +	kfree(kern_buff);
> +	if (ret)
> +		return -EFAULT;
> +
> +	*off = len + 1;
> +
> +	return len;
> +}
> +
> +/* Setup debugfs fops */
> +static const struct file_operations fops_zynqmp_pm_dbgfs = {
> +	.owner  =	THIS_MODULE,
> +	.write  =	zynqmp_pm_debugfs_api_write,
> +	.read   =	zynqmp_pm_debugfs_api_version_read,
> +};
> +
> +/**
> + * zynqmp_pm_api_debugfs_init - Initialize debugfs interface
> + *
> + * Return:      Returns 0 on success
> + *		Corresponding error code otherwise
> + */
> +int zynqmp_pm_api_debugfs_init(void)
> +{
> +	int err;
> +
> +	/* Initialize debugfs interface */
> +	zynqmp_pm_debugfs_dir = debugfs_create_dir(DRIVER_NAME, NULL);
> +	if (!zynqmp_pm_debugfs_dir) {
> +		pr_err("debugfs_create_dir failed\n");
> +		return -ENODEV;
> +	}
> +
> +	zynqmp_pm_debugfs_power =
> +		debugfs_create_file("pm", 0220,
> +				    zynqmp_pm_debugfs_dir, NULL,
> +				    &fops_zynqmp_pm_dbgfs);
> +	if (!zynqmp_pm_debugfs_power) {
> +		pr_err("debugfs_create_file power failed\n");
> +		err = -ENODEV;
> +		goto err_dbgfs;
> +	}
> +
> +	zynqmp_pm_debugfs_api_version =
> +		debugfs_create_file("api_version", 0444,
> +				    zynqmp_pm_debugfs_dir, NULL,
> +				    &fops_zynqmp_pm_dbgfs);
> +	if (!zynqmp_pm_debugfs_api_version) {
> +		pr_err("debugfs_create_file api_version failed\n");
> +		err = -ENODEV;
> +		goto err_dbgfs;
> +	}
> +
> +	return 0;
> +
> +err_dbgfs:
> +	debugfs_remove_recursive(zynqmp_pm_debugfs_dir);
> +	zynqmp_pm_debugfs_dir = NULL;
> +
> +	return err;
> +}
> diff --git a/drivers/firmware/xilinx/zynqmp/firmware-ggs.c b/drivers/firmware/xilinx/zynqmp/firmware-ggs.c
> new file mode 100644
> index 0000000..feb6148
> --- /dev/null
> +++ b/drivers/firmware/xilinx/zynqmp/firmware-ggs.c
> @@ -0,0 +1,298 @@
> +/*
> + * Xilinx Zynq MPSoC Firmware layer
> + *
> + *  Copyright (C) 2014-2017 Xilinx, Inc.
> + *
> + *  Rajan Vaja <rajanv@xilinx.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <linux/compiler.h>
> +#include <linux/of.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/uaccess.h>
> +#include <linux/slab.h>
> +#include <linux/platform_device.h>
> +
> +#include <linux/firmware/xilinx/zynqmp/firmware.h>
> +
> +static ssize_t read_register(char *buf, u32 ioctl_id, u32 reg)
> +{
> +	int ret;
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +	if (!eemi_ops || !eemi_ops->ioctl)
> +		return 0;
> +
> +	ret = eemi_ops->ioctl(0, ioctl_id, reg, 0, ret_payload);
> +	if (ret)
> +		return ret;
> +
> +	return snprintf(buf, PAGE_SIZE, "0x%x\n", ret_payload[1]);
> +}
> +
> +static ssize_t write_register(const char *buf, size_t count,
> +			      u32 ioctl_id, u32 reg)
> +{
> +	char *kern_buff;
> +	char *inbuf;
> +	char *tok;
> +	long mask;
> +	long value;
> +	int ret;
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
> +
> +	if (!eemi_ops || !eemi_ops->ioctl)
> +		return -EFAULT;
> +
> +	kern_buff = kzalloc(count, GFP_KERNEL);
> +	if (!kern_buff)
> +		return -ENOMEM;
> +
> +	ret = strlcpy(kern_buff, buf, count);
> +	if (ret < 0) {
> +		ret = -EFAULT;
> +		goto err;
> +	}
> +
> +	inbuf = kern_buff;
> +
> +	/* Read the write mask */
> +	tok = strsep(&inbuf, " ");
> +	if (!tok) {
> +		ret = -EFAULT;
> +		goto err;
> +	}
> +
> +	ret = kstrtol(tok, 16, &mask);
> +	if (ret) {
> +		ret = -EFAULT;
> +		goto err;
> +	}
> +
> +	/* Read the write value */
> +	tok = strsep(&inbuf, " ");
> +	if (!tok) {
> +		ret = -EFAULT;
> +		goto err;
> +	}
> +
> +	ret = kstrtol(tok, 16, &value);
> +	if (ret) {
> +		ret = -EFAULT;
> +		goto err;
> +	}
> +
> +	ret = eemi_ops->ioctl(0, ioctl_id, reg, 0, ret_payload);
> +	if (ret) {
> +		ret = -EFAULT;
> +		goto err;
> +	}
> +	ret_payload[1] &= ~mask;
> +	value &= mask;
> +	value |= ret_payload[1];
> +
> +	ret = eemi_ops->ioctl(0, ioctl_id, reg, value, NULL);
> +	if (ret)
> +		ret = -EFAULT;
> +
> +err:
> +	kfree(kern_buff);
> +	if (ret)
> +		return ret;
> +
> +	return count;
> +}
> +
> +/**
> + * ggs_show - Show global general storage (ggs) sysfs attribute
> + * @dev: Device structure
> + * @attr: Device attribute structure
> + * @buf: Requested available shutdown_scope attributes string
> + * @reg: Register number
> + *
> + * Return:Number of bytes printed into the buffer.
> + *
> + * Helper function for viewing a ggs register value.
> + *
> + * User-space interface for viewing the content of the ggs0 register.
> + * cat /sys/devices/platform/firmware/ggs0
> + */
> +static ssize_t ggs_show(struct device *dev,
> +			struct device_attribute *attr,
> +			char *buf,
> +			u32 reg)
> +{
> +	return read_register(buf, IOCTL_READ_GGS, reg);
> +}
> +
> +/**
> + * ggs_store - Store global general storage (ggs) sysfs attribute
> + * @dev: Device structure
> + * @attr: Device attribute structure
> + * @buf: User entered shutdown_scope attribute string
> + * @count: Size of buf
> + * @reg: Register number
> + *
> + * Return: count argument if request succeeds, the corresponding
> + * error code otherwise
> + *
> + * Helper function for storing a ggs register value.
> + *
> + * For example, the user-space interface for storing a value to the
> + * ggs0 register:
> + * echo 0xFFFFFFFF 0x1234ABCD > /sys/devices/platform/firmware/ggs0
> + */
> +static ssize_t ggs_store(struct device *dev,
> +			 struct device_attribute *attr,
> +			 const char *buf,
> +			 size_t count,
> +			 u32 reg)
> +{
> +	if (!dev || !attr || !buf || !count || reg >= GSS_NUM_REGS)
> +		return -EINVAL;
> +
> +	return write_register(buf, count, IOCTL_WRITE_GGS, reg);
> +}
> +
> +/* GGS register show functions */
> +#define GGS0_SHOW(N) \
> +	ssize_t ggs##N##_show(struct device *dev, \
> +			 struct device_attribute *attr, \
> +			 char *buf) \
> +	{ \
> +		return ggs_show(dev, attr, buf, N); \
> +	}
> +
> +static GGS0_SHOW(0);
> +static GGS0_SHOW(1);
> +static GGS0_SHOW(2);
> +static GGS0_SHOW(3);
> +
> +/* GGS register store function */
> +#define GGS0_STORE(N) \
> +	ssize_t ggs##N##_store(struct device *dev, \
> +				   struct device_attribute *attr, \
> +				   const char *buf, \
> +				   size_t count) \
> +	{ \
> +		return ggs_store(dev, attr, buf, count, N); \
> +	}
> +
> +static GGS0_STORE(0);
> +static GGS0_STORE(1);
> +static GGS0_STORE(2);
> +static GGS0_STORE(3);
> +
> +/* GGS register device attributes */
> +static DEVICE_ATTR_RW(ggs0);
> +static DEVICE_ATTR_RW(ggs1);
> +static DEVICE_ATTR_RW(ggs2);
> +static DEVICE_ATTR_RW(ggs3);

Hi

You added some files to the sysfs ABI. These interfaces should be documented in
Documentation/ABI.

> +
> +#define CREATE_GGS_DEVICE(dev, N) \
> +do { \
> +	if (device_create_file(dev, &dev_attr_ggs##N)) \
> +		dev_err(dev, "unable to create ggs%d attribute\n", N); \
> +} while (0)
> +
> +/**
> + * pggs_show - Show persistent global general storage (pggs) sysfs attribute
> + * @dev: Device structure
> + * @attr: Device attribute structure
> + * @buf: Requested available shutdown_scope attributes string
> + * @reg: Register number
> + *
> + * Return:Number of bytes printed into the buffer.
> + *
> + * Helper function for viewing a pggs register value.
> + */
> +static ssize_t pggs_show(struct device *dev,
> +			 struct device_attribute *attr,
> +			 char *buf,
> +			 u32 reg)
> +{
> +	return read_register(buf, IOCTL_READ_GGS, reg);
> +}
> +
> +/**
> + * pggs_store - Store persistent global general storage (pggs) sysfs attribute
> + * @dev: Device structure
> + * @attr: Device attribute structure
> + * @buf: User entered shutdown_scope attribute string
> + * @count: Size of buf
> + * @reg: Register number
> + *
> + * Return: count argument if request succeeds, the corresponding
> + * error code otherwise
> + *
> + * Helper function for storing a pggs register value.
> + */
> +static ssize_t pggs_store(struct device *dev,
> +			  struct device_attribute *attr,
> +			  const char *buf,
> +			  size_t count,
> +			  u32 reg)
> +{
> +	return write_register(buf, count, IOCTL_WRITE_PGGS, reg);
> +}
> +
> +#define PGGS0_SHOW(N) \
> +	ssize_t pggs##N##_show(struct device *dev, \
> +			 struct device_attribute *attr, \
> +			 char *buf) \
> +	{ \
> +		return pggs_show(dev, attr, buf, N); \
> +	}
> +
> +/* PGGS register show functions */
> +static PGGS0_SHOW(0);
> +static PGGS0_SHOW(1);
> +static PGGS0_SHOW(2);
> +static PGGS0_SHOW(3);
> +
> +#define PGGS0_STORE(N) \
> +	ssize_t pggs##N##_store(struct device *dev, \
> +				   struct device_attribute *attr, \
> +				   const char *buf, \
> +				   size_t count) \
> +	{ \
> +		return pggs_store(dev, attr, buf, count, N); \
> +	}
> +
> +/* PGGS register store functions */
> +static PGGS0_STORE(0);
> +static PGGS0_STORE(1);
> +static PGGS0_STORE(2);
> +static PGGS0_STORE(3);
> +
> +/* PGGS register device attributes */
> +static DEVICE_ATTR_RW(pggs0);
> +static DEVICE_ATTR_RW(pggs1);
> +static DEVICE_ATTR_RW(pggs2);
> +static DEVICE_ATTR_RW(pggs3);
> +
> +#define CREATE_PGGS_DEVICE(dev, N) \
> +do { \
> +	if (device_create_file(dev, &dev_attr_pggs##N)) \
> +		dev_err(dev, "unable to create pggs%d attribute\n", N); \
> +} while (0)
> +
> +void zynqmp_pm_ggs_init(struct device *dev)
> +{
> +	/* Create Global General Storage register. */
> +	CREATE_GGS_DEVICE(dev, 0);
> +	CREATE_GGS_DEVICE(dev, 1);
> +	CREATE_GGS_DEVICE(dev, 2);
> +	CREATE_GGS_DEVICE(dev, 3);
> +
> +	/* Create Persistent Global General Storage register. */
> +	CREATE_PGGS_DEVICE(dev, 0);
> +	CREATE_PGGS_DEVICE(dev, 1);
> +	CREATE_PGGS_DEVICE(dev, 2);
> +	CREATE_PGGS_DEVICE(dev, 3);
> +}
> diff --git a/drivers/firmware/xilinx/zynqmp/firmware.c b/drivers/firmware/xilinx/zynqmp/firmware.c
> new file mode 100644
> index 0000000..edce5eb
> --- /dev/null
> +++ b/drivers/firmware/xilinx/zynqmp/firmware.c
> @@ -0,0 +1,1024 @@
> +/*
> + * Xilinx Zynq MPSoC Firmware layer
> + *
> + *  Copyright (C) 2014-2017 Xilinx, Inc.
> + *
> + *  Michal Simek <michal.simek@xilinx.com>
> + *  Davorin Mista <davorin.mista@aggios.com>
> + *  Jolly Shah <jollys@xilinx.com>
> + *  Rajan Vaja <rajanv@xilinx.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <linux/compiler.h>
> +#include <linux/arm-smccc.h>
> +#include <linux/of.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/uaccess.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +
> +#include <linux/firmware/xilinx/zynqmp/firmware.h>
> +#include <linux/firmware/xilinx/zynqmp/firmware-debug.h>
> +
> +#define DRIVER_NAME	"zynqmp_firmware"
> +
> +/**
> + * zynqmp_pm_ret_code - Convert PMU-FW error codes to Linux error codes
> + * @ret_status:		PMUFW return code
> + *
> + * Return:		corresponding Linux error code
> + */
> +int zynqmp_pm_ret_code(u32 ret_status)
> +{
> +	switch (ret_status) {
> +	case XST_PM_SUCCESS:
> +	case XST_PM_DOUBLE_REQ:
> +		return 0;
> +	case XST_PM_NO_ACCESS:
> +		return -EACCES;
> +	case XST_PM_ABORT_SUSPEND:
> +		return -ECANCELED;
> +	case XST_PM_INTERNAL:
> +	case XST_PM_CONFLICT:
> +	case XST_PM_INVALID_NODE:
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static noinline int do_fw_call_fail(u64 arg0, u64 arg1, u64 arg2,
> +				    u32 *ret_payload)
> +{
> +	return -ENODEV;
> +}
> +
> +/*
> + * PM function call wrapper
> + * Invoke do_fw_call_smc or do_fw_call_hvc, depending on the configuration
> + */
> +static int (*do_fw_call)(u64, u64, u64, u32 *ret_payload) = do_fw_call_fail;
> +
> +/**
> + * do_fw_call_smc - Call system-level power management layer (SMC)
> + * @arg0:		Argument 0 to SMC call
> + * @arg1:		Argument 1 to SMC call
> + * @arg2:		Argument 2 to SMC call
> + * @ret_payload:	Returned value array
> + *
> + * Return:		Returns status, either success or error+reason
> + *
> + * Invoke power management function via SMC call (no hypervisor present)
> + */
> +static noinline int do_fw_call_smc(u64 arg0, u64 arg1, u64 arg2,
> +				   u32 *ret_payload)
> +{
> +	struct arm_smccc_res res;
> +
> +	arm_smccc_smc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res);
> +
> +	if (ret_payload) {
> +		ret_payload[0] = lower_32_bits(res.a0);
> +		ret_payload[1] = upper_32_bits(res.a0);
> +		ret_payload[2] = lower_32_bits(res.a1);
> +		ret_payload[3] = upper_32_bits(res.a1);
> +		ret_payload[4] = lower_32_bits(res.a2);
> +	}
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
> +}
> +
> +/**
> + * do_fw_call_hvc - Call system-level power management layer (HVC)
> + * @arg0:		Argument 0 to HVC call
> + * @arg1:		Argument 1 to HVC call
> + * @arg2:		Argument 2 to HVC call
> + * @ret_payload:	Returned value array
> + *
> + * Return:		Returns status, either success or error+reason
> + *
> + * Invoke power management function via HVC
> + * HVC-based for communication through hypervisor
> + * (no direct communication with ATF)
> + */
> +static noinline int do_fw_call_hvc(u64 arg0, u64 arg1, u64 arg2,
> +				   u32 *ret_payload)
> +{
> +	struct arm_smccc_res res;
> +
> +	arm_smccc_hvc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res);
> +
> +	if (ret_payload) {
> +		ret_payload[0] = lower_32_bits(res.a0);
> +		ret_payload[1] = upper_32_bits(res.a0);
> +		ret_payload[2] = lower_32_bits(res.a1);
> +		ret_payload[3] = upper_32_bits(res.a1);
> +		ret_payload[4] = lower_32_bits(res.a2);
> +	}
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
> +}
> +
> +/**
> + * invoke_pm_fn - Invoke the system-level power management layer caller
> + *			function depending on the configuration
> + * @pm_api_id:         Requested PM-API call
> + * @arg0:              Argument 0 to requested PM-API call
> + * @arg1:              Argument 1 to requested PM-API call
> + * @arg2:              Argument 2 to requested PM-API call
> + * @arg3:              Argument 3 to requested PM-API call
> + * @ret_payload:       Returned value array
> + *
> + * Return:             Returns status, either success or error+reason
> + *
> + * Invoke power management function for SMC or HVC call, depending on
> + * configuration
> + * Following SMC Calling Convention (SMCCC) for SMC64:
> + * Pm Function Identifier,
> + * PM_SIP_SVC + PM_API_ID =
> + *     ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT)
> + *     ((SMC_64) << FUNCID_CC_SHIFT)
> + *     ((SIP_START) << FUNCID_OEN_SHIFT)
> + *     ((PM_API_ID) & FUNCID_NUM_MASK))
> + *
> + * PM_SIP_SVC  - Registered ZynqMP SIP Service Call
> + * PM_API_ID   - Power Management API ID
> + */
> +int invoke_pm_fn(u32 pm_api_id, u32 arg0, u32 arg1, u32 arg2, u32 arg3,
> +		 u32 *ret_payload)
> +{
> +	/*
> +	 * Added SIP service call Function Identifier
> +	 * Make sure to stay in x0 register
> +	 */
> +	u64 smc_arg[4];
> +
> +	smc_arg[0] = PM_SIP_SVC | pm_api_id;
> +	smc_arg[1] = ((u64)arg1 << 32) | arg0;
> +	smc_arg[2] = ((u64)arg3 << 32) | arg2;
> +
> +	return do_fw_call(smc_arg[0], smc_arg[1], smc_arg[2], ret_payload);
> +}
> +
> +static u32 pm_api_version;
> +
> +/**
> + * zynqmp_pm_get_api_version - Get version number of PMU PM firmware
> + * @version:	Returned version value
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_get_api_version(u32 *version)
> +{
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +	if (!version)
> +		return zynqmp_pm_ret_code(XST_PM_CONFLICT);
> +
> +	/* Check is PM API version already verified */
> +	if (pm_api_version > 0) {
> +		*version = pm_api_version;
> +		return XST_PM_SUCCESS;
> +	}
> +	invoke_pm_fn(GET_API_VERSION, 0, 0, 0, 0, ret_payload);
> +	*version = ret_payload[1];
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_get_chipid - Get silicon ID registers
> + * @idcode:	IDCODE register
> + * @version:	version register
> + *
> + * Return:	Returns the status of the operation and the idcode and version
> + *		registers in @idcode and @version.
> + */
> +static int zynqmp_pm_get_chipid(u32 *idcode, u32 *version)
> +{
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +	if (!idcode || !version)
> +		return -EINVAL;
> +
> +	invoke_pm_fn(GET_CHIPID, 0, 0, 0, 0, ret_payload);
> +	*idcode = ret_payload[1];
> +	*version = ret_payload[2];
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * get_set_conduit_method - Choose SMC or HVC based communication
> + * @np:	Pointer to the device_node structure
> + *
> + * Use SMC or HVC-based functions to communicate with EL2/EL3
> + */
> +static int get_set_conduit_method(struct device_node *np)
> +{
> +	const char *method;
> +
> +	if (of_property_read_string(np, "method", &method)) {
> +		pr_warn("%s missing \"method\" property\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	if (!strcmp("hvc", method)) {
> +		do_fw_call = do_fw_call_hvc;
> +	} else if (!strcmp("smc", method)) {
> +		do_fw_call = do_fw_call_smc;
> +	} else {
> +		pr_warn("%s Invalid \"method\" property: %s\n",
> +			__func__, method);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * zynqmp_pm_reset_assert - Request setting of reset (1 - assert, 0 - release)
> + * @reset:		Reset to be configured
> + * @assert_flag:	Flag stating should reset be asserted (1) or
> + *			released (0)
> + *
> + * Return:		Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_reset_assert(const enum zynqmp_pm_reset reset,
> +			   const enum zynqmp_pm_reset_action assert_flag)
> +{
> +	return invoke_pm_fn(RESET_ASSERT, reset, assert_flag, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_reset_get_status - Get status of the reset
> + * @reset:	Reset whose status should be returned
> + * @status:	Returned status
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset,
> +				      u32 *status)
> +{
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +	if (!status)
> +		return zynqmp_pm_ret_code(XST_PM_CONFLICT);
> +
> +	invoke_pm_fn(RESET_GET_STATUS, reset, 0, 0, 0, ret_payload);
> +	*status = ret_payload[1];
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_fpga_load - Perform the fpga load
> + * @address:    Address to write to
> + * @size:       pl bitstream size
> + * @flags:
> + *	BIT(0) - Bit-stream type.
> + *		 0 - Full Bit-stream.
> + *		 1 - Partial Bit-stream.
> + *	BIT(1) - Authentication.
> + *		 1 - Enable.
> + *		 0 - Disable.
> + *	BIT(2) - Encryption.
> + *		 1 - Enable.
> + *		 0 - Disable.
> + * NOTE -
> + *	The current implementation supports only Full Bit-stream.
> + *
> + * This function provides access to xilfpga library to transfer
> + * the required bitstream into PL.
> + *
> + * Return:      Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_fpga_load(const u64 address, const u32 size,
> +			       const u32 flags)
> +{
> +	return invoke_pm_fn(FPGA_LOAD, (u32)address,
> +			    ((u32)(address >> 32)), size, flags, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_fpga_get_status - Read value from PCAP status register
> + * @value:      Value to read
> + *
> + *This function provides access to the xilfpga library to get
> + *the PCAP status
> + *
> + * Return:      Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_fpga_get_status(u32 *value)
> +{
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +	if (!value)
> +		return -EINVAL;
> +
> +	invoke_pm_fn(FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload);
> +	*value = ret_payload[1];
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_request_suspend - PM call to request for another PU or subsystem to
> + *					be suspended gracefully.
> + * @node:	Node ID of the targeted PU or subsystem
> + * @ack:	Flag to specify whether acknowledge is requested
> + * @latency:	Requested wakeup latency (not supported)
> + * @state:	Requested state (not supported)
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_request_suspend(const u32 node,
> +			      const enum zynqmp_pm_request_ack ack,
> +			      const u32 latency,
> +			      const u32 state)
> +{
> +	return invoke_pm_fn(REQUEST_SUSPEND, node, ack,
> +			    latency, state, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_force_powerdown - PM call to request for another PU or subsystem to
> + *				be powered down forcefully
> + * @target:	Node ID of the targeted PU or subsystem
> + * @ack:	Flag to specify whether acknowledge is requested
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_force_powerdown(const u32 target,
> +				     const enum zynqmp_pm_request_ack ack)
> +{
> +	return invoke_pm_fn(FORCE_POWERDOWN, target, ack, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_request_wakeup - PM call to wake up selected master or subsystem
> + * @node:	Node ID of the master or subsystem
> + * @set_addr:	Specifies whether the address argument is relevant
> + * @address:	Address from which to resume when woken up
> + * @ack:	Flag to specify whether acknowledge requested
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_request_wakeup(const u32 node,
> +				    const bool set_addr,
> +				    const u64 address,
> +				    const enum zynqmp_pm_request_ack ack)
> +{
> +	/* set_addr flag is encoded into 1st bit of address */
> +	return invoke_pm_fn(REQUEST_WAKEUP, node, address | set_addr,
> +			    address >> 32, ack, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_set_wakeup_source - PM call to specify the wakeup source
> + *					while suspended
> + * @target:	Node ID of the targeted PU or subsystem
> + * @wakeup_node:Node ID of the wakeup peripheral
> + * @enable:	Enable or disable the specified peripheral as wake source
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_set_wakeup_source(const u32 target,
> +				       const u32 wakeup_node,
> +				       const u32 enable)
> +{
> +	return invoke_pm_fn(SET_WAKEUP_SOURCE, target,
> +			    wakeup_node, enable, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_system_shutdown - PM call to request a system shutdown or restart
> + * @type:	Shutdown or restart? 0 for shutdown, 1 for restart
> + * @subtype:	Specifies which system should be restarted or shut down
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype)
> +{
> +	return invoke_pm_fn(SYSTEM_SHUTDOWN, type, subtype, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_request_node - PM call to request a node with specific capabilities
> + * @node:		Node ID of the slave
> + * @capabilities:	Requested capabilities of the slave
> + * @qos:		Quality of service (not supported)
> + * @ack:		Flag to specify whether acknowledge is requested
> + *
> + * Return:		Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_request_node(const u32 node, const u32 capabilities,
> +				  const u32 qos,
> +				  const enum zynqmp_pm_request_ack ack)
> +{
> +	return invoke_pm_fn(REQUEST_NODE, node, capabilities,
> +			    qos, ack, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_release_node - PM call to release a node
> + * @node:	Node ID of the slave
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_release_node(const u32 node)
> +{
> +	return invoke_pm_fn(RELEASE_NODE, node, 0, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_set_requirement - PM call to set requirement for PM slaves
> + * @node:		Node ID of the slave
> + * @capabilities:	Requested capabilities of the slave
> + * @qos:		Quality of service (not supported)
> + * @ack:		Flag to specify whether acknowledge is requested
> + *
> + * This API function is to be used for slaves a PU already has requested
> + *
> + * Return:		Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities,
> +				     const u32 qos,
> +				     const enum zynqmp_pm_request_ack ack)
> +{
> +	return invoke_pm_fn(SET_REQUIREMENT, node, capabilities,
> +			    qos, ack, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_set_max_latency - PM call to set wakeup latency requirements
> + * @node:	Node ID of the slave
> + * @latency:	Requested maximum wakeup latency
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_set_max_latency(const u32 node, const u32 latency)
> +{
> +	return invoke_pm_fn(SET_MAX_LATENCY, node, latency, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_set_configuration - PM call to set system configuration
> + * @physical_addr:	Physical 32-bit address of data structure in memory
> + *
> + * Return:		Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_set_configuration(const u32 physical_addr)
> +{
> +	return invoke_pm_fn(SET_CONFIGURATION, physical_addr, 0, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_get_node_status - PM call to request a node's current power state
> + * @node:		ID of the component or sub-system in question
> + * @status:		Current operating state of the requested node
> + * @requirements:	Current requirements asserted on the node,
> + *			used for slave nodes only.
> + * @usage:		Usage information, used for slave nodes only:
> + *			0 - No master is currently using the node
> + *			1 - Only requesting master is currently using the node
> + *			2 - Only other masters are currently using the node
> + *			3 - Both the current and at least one other master
> + *			is currently using the node
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_get_node_status(const u32 node, u32 *const status,
> +				     u32 *const requirements, u32 *const usage)
> +{
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +	if (!status)
> +		return -EINVAL;
> +
> +	invoke_pm_fn(GET_NODE_STATUS, node, 0, 0, 0, ret_payload);
> +	if (ret_payload[0] == XST_PM_SUCCESS) {
> +		*status = ret_payload[1];
> +		if (requirements)
> +			*requirements = ret_payload[2];
> +		if (usage)
> +			*usage = ret_payload[3];
> +	}
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_get_operating_characteristic - PM call to request operating
> + *						characteristic information
> + * @node:	Node ID of the slave
> + * @type:	Type of the operating characteristic requested
> + * @result:	Used to return the requsted operating characteristic
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_get_operating_characteristic(const u32 node,
> +						const enum zynqmp_pm_opchar_type
> +						type, u32 *const result)
> +{
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +	if (!result)
> +		return -EINVAL;
> +
> +	invoke_pm_fn(GET_OPERATING_CHARACTERISTIC,
> +		     node, type, 0, 0, ret_payload);
> +	if (ret_payload[0] == XST_PM_SUCCESS)
> +		*result = ret_payload[1];
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_init_finalize - PM call to informi firmware that the caller master
> + *				has initialized its own power management
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_init_finalize(void)
> +{
> +	return invoke_pm_fn(PM_INIT_FINALIZE, 0, 0, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_get_callback_data - Get callback data from firmware
> + * @buf:	Buffer to store payload data
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_get_callback_data(u32 *buf)
> +{
> +	return invoke_pm_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf);
> +}
> +
> +/**
> + * zynqmp_pm_set_suspend_mode	- Set system suspend mode
> + *
> + * @mode:	Mode to set for system suspend
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_set_suspend_mode(u32 mode)
> +{
> +	return invoke_pm_fn(SET_SUSPEND_MODE, mode, 0, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_sha_hash - Access the SHA engine to calculate the hash
> + * @address:	Address of the data/ Address of output buffer where
> + *		hash should be stored.
> + * @size:	Size of the data.
> + * @flags:
> + *	BIT(0) - Sha3 init (Here address and size inputs can be NULL)
> + *	BIT(1) - Sha3 update (address should holds the )
> + *	BIT(2) - Sha3 final (address should hold the address of
> + *		 buffer to store hash)
> + *
> + * Return:	Returns status, either success or error code.
> + */
> +static int zynqmp_pm_sha_hash(const u64 address, const u32 size,
> +			      const u32 flags)
> +{
> +	u32 lower_32_bits = (u32)address;
> +	u32 upper_32_bits = (u32)(address >> 32);
> +
> +	return invoke_pm_fn(SECURE_SHA, upper_32_bits, lower_32_bits,
> +			    size, flags, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_rsa - Access RSA hardware to encrypt/decrypt the data with RSA.
> + * @address:	Address of the data
> + * @size:	Size of the data.
> + * @flags:
> + *		BIT(0) - Encryption/Decryption
> + *			 0 - RSA decryption with private key
> + *			 1 - RSA encryption with public key.
> + *
> + * Return:	Returns status, either success or error code.
> + */
> +static int zynqmp_pm_rsa(const u64 address, const u32 size, const u32 flags)
> +{
> +	u32 lower_32_bits = (u32)address;
> +	u32 upper_32_bits = (u32)(address >> 32);
> +
> +	return invoke_pm_fn(SECURE_RSA, upper_32_bits, lower_32_bits,
> +			    size, flags, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_pinctrl_request - Request Pin from firmware
> + * @pin:	Pin number to request
> + *
> + * This function requests pin from firmware.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_pinctrl_request(const u32 pin)
> +{
> +	return invoke_pm_fn(PINCTRL_REQUEST, pin, 0, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_pinctrl_release - Inform firmware that Pin control is released
> + * @pin:	Pin number to release
> + *
> + * This function release pin from firmware.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_pinctrl_release(const u32 pin)
> +{
> +	return invoke_pm_fn(PINCTRL_RELEASE, pin, 0, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_pinctrl_get_function() - Read function id set for the given pin
> + * @pin:	Pin number
> + * @node:	Buffer to store node ID matching current function
> + *
> + * This function provides the function currently set for the given pin.
> + *
> + * Return:	Returns status, either success or error+reason
> + */
> +static int zynqmp_pm_pinctrl_get_function(const u32 pin, u32 *node)
> +{
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +	if (!node)
> +		return -EINVAL;
> +
> +	invoke_pm_fn(PINCTRL_GET_FUNCTION, pin, 0, 0, 0, ret_payload);
> +	*node = ret_payload[1];
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_pinctrl_set_function - Set requested function for the pin
> + * @pin:	Pin number
> + * @node:	Node ID mapped with the requested function
> + *
> + * This function sets requested function for the given pin.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_pinctrl_set_function(const u32 pin, const u32 node)
> +{
> +	return invoke_pm_fn(PINCTRL_SET_FUNCTION, pin, node, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_pinctrl_get_config - Get configuration parameter for the pin
> + * @pin:	Pin number
> + * @param:	Parameter to get
> + * @value:	Buffer to store parameter value
> + *
> + * This function gets requested configuration parameter for the given pin.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_pinctrl_get_config(const u32 pin, const u32 param,
> +					u32 *value)
> +{
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +	if (!value)
> +		return -EINVAL;
> +
> +	invoke_pm_fn(PINCTRL_CONFIG_PARAM_GET, pin,
> +		     param, 0, 0, ret_payload);
> +	*value = ret_payload[1];
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_pinctrl_set_config - Set configuration parameter for the pin
> + * @pin:	Pin number
> + * @param:	Parameter to set
> + * @value:	Parameter value to set
> + *
> + * This function sets requested configuration parameter for the given pin.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param,
> +					u32 value)
> +{
> +	return invoke_pm_fn(PINCTRL_CONFIG_PARAM_SET, pin,
> +			    param, value, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_ioctl - PM IOCTL API for device control and configs
> + * @node_id:	Node ID of the device
> + * @ioctl_id:	ID of the requested IOCTL
> + * @arg1:	Argument 1 to requested IOCTL call
> + * @arg2:	Argument 2 to requested IOCTL call
> + * @out:	Returned output value
> + *
> + * This function calls IOCTL to firmware for device control and configuration.
> + */
> +static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2,
> +			   u32 *out)
> +{
> +	return invoke_pm_fn(IOCTL, node_id, ioctl_id, arg1, arg2, out);
> +}
> +
> +static int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out)
> +{
> +	return invoke_pm_fn(QUERY_DATA, qdata.qid, qdata.arg1,
> +			    qdata.arg2, qdata.arg3, out);
> +}
> +
> +/**
> + * zynqmp_pm_clock_enable - Enable the clock for given id
> + * @clock_id:	ID of the clock to be enabled
> + *
> + * This function is used by master to enable the clock
> + * including peripherals and PLL clocks.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_enable(u32 clock_id)
> +{
> +	return invoke_pm_fn(CLOCK_ENABLE, clock_id, 0, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_clock_disable - Disable the clock for given id
> + * @clock_id:	ID of the clock to be disable
> + *
> + * This function is used by master to disable the clock
> + * including peripherals and PLL clocks.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_disable(u32 clock_id)
> +{
> +	return invoke_pm_fn(CLOCK_DISABLE, clock_id, 0, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_clock_getstate - Get the clock state for given id
> + * @clock_id:	ID of the clock to be queried
> + * @state:	1/0 (Enabled/Disabled)
> + *
> + * This function is used by master to get the state of clock
> + * including peripherals and PLL clocks.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_getstate(u32 clock_id, u32 *state)
> +{
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +	invoke_pm_fn(CLOCK_GETSTATE, clock_id, 0, 0, 0, ret_payload);
> +	*state = ret_payload[1];
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_clock_setdivider - Set the clock divider for given id
> + * @clock_id:	ID of the clock
> + * @div_type:	TYPE_DIV1: div1
> + *		TYPE_DIV2: div2
> + * @divider:	divider value.
> + *
> + * This function is used by master to set divider for any clock
> + * to achieve desired rate.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_setdivider(u32 clock_id, u32 divider)
> +{
> +	return invoke_pm_fn(CLOCK_SETDIVIDER, clock_id, divider, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_clock_getdivider - Get the clock divider for given id
> + * @clock_id:	ID of the clock
> + * @div_type:	TYPE_DIV1: div1
> + *		TYPE_DIV2: div2
> + * @divider:	divider value.
> + *
> + * This function is used by master to get divider values
> + * for any clock.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_getdivider(u32 clock_id, u32 *divider)
> +{
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +	invoke_pm_fn(CLOCK_GETDIVIDER, clock_id, 0, 0, 0, ret_payload);
> +	*divider = ret_payload[1];
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_clock_setrate - Set the clock rate for given id
> + * @clock_id:	ID of the clock
> + * @rate:	rate value in hz
> + *
> + * This function is used by master to set rate for any clock.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_setrate(u32 clock_id, u32 rate)
> +{
> +	return invoke_pm_fn(CLOCK_SETRATE, clock_id, rate, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_clock_getrate - Get the clock rate for given id
> + * @clock_id:	ID of the clock
> + * @rate:	rate value in hz
> + *
> + * This function is used by master to get rate
> + * for any clock.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_getrate(u32 clock_id, u32 *rate)
> +{
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +	invoke_pm_fn(CLOCK_GETRATE, clock_id, 0, 0, 0, ret_payload);
> +	*rate = ret_payload[1];
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +/**
> + * zynqmp_pm_clock_setparent - Set the clock parent for given id
> + * @clock_id:	ID of the clock
> + * @parent_id:	parent id
> + *
> + * This function is used by master to set parent for any clock.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_setparent(u32 clock_id, u32 parent_id)
> +{
> +	return invoke_pm_fn(CLOCK_SETPARENT, clock_id, parent_id, 0, 0, NULL);
> +}
> +
> +/**
> + * zynqmp_pm_clock_getparent - Get the clock parent for given id
> + * @clock_id:	ID of the clock
> + * @parent_id:	parent id
> + *
> + * This function is used by master to get parent index
> + * for any clock.
> + *
> + * Return:	Returns status, either success or error+reason.
> + */
> +static int zynqmp_pm_clock_getparent(u32 clock_id, u32 *parent_id)
> +{
> +	u32 ret_payload[PAYLOAD_ARG_CNT];
> +
> +	invoke_pm_fn(CLOCK_GETPARENT, clock_id, 0, 0, 0, ret_payload);
> +	*parent_id = ret_payload[1];
> +
> +	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
> +}
> +
> +static const struct zynqmp_eemi_ops eemi_ops  = {
> +	.get_api_version = zynqmp_pm_get_api_version,
> +	.get_chipid = zynqmp_pm_get_chipid,
> +	.reset_assert = zynqmp_pm_reset_assert,
> +	.reset_get_status = zynqmp_pm_reset_get_status,
> +	.fpga_load = zynqmp_pm_fpga_load,
> +	.fpga_get_status = zynqmp_pm_fpga_get_status,
> +	.sha_hash = zynqmp_pm_sha_hash,
> +	.rsa = zynqmp_pm_rsa,
> +	.request_suspend = zynqmp_pm_request_suspend,
> +	.force_powerdown = zynqmp_pm_force_powerdown,
> +	.request_wakeup = zynqmp_pm_request_wakeup,
> +	.set_wakeup_source = zynqmp_pm_set_wakeup_source,
> +	.system_shutdown = zynqmp_pm_system_shutdown,
> +	.request_node = zynqmp_pm_request_node,
> +	.release_node = zynqmp_pm_release_node,
> +	.set_requirement = zynqmp_pm_set_requirement,
> +	.set_max_latency = zynqmp_pm_set_max_latency,
> +	.set_configuration = zynqmp_pm_set_configuration,
> +	.get_node_status = zynqmp_pm_get_node_status,
> +	.get_operating_characteristic = zynqmp_pm_get_operating_characteristic,
> +	.init_finalize = zynqmp_pm_init_finalize,
> +	.get_callback_data = zynqmp_pm_get_callback_data,
> +	.set_suspend_mode = zynqmp_pm_set_suspend_mode,
> +	.ioctl = zynqmp_pm_ioctl,
> +	.query_data = zynqmp_pm_query_data,
> +	.pinctrl_request = zynqmp_pm_pinctrl_request,
> +	.pinctrl_release = zynqmp_pm_pinctrl_release,
> +	.pinctrl_get_function = zynqmp_pm_pinctrl_get_function,
> +	.pinctrl_set_function = zynqmp_pm_pinctrl_set_function,
> +	.pinctrl_get_config = zynqmp_pm_pinctrl_get_config,
> +	.pinctrl_set_config = zynqmp_pm_pinctrl_set_config,
> +	.clock_enable = zynqmp_pm_clock_enable,
> +	.clock_disable = zynqmp_pm_clock_disable,
> +	.clock_getstate = zynqmp_pm_clock_getstate,
> +	.clock_setdivider = zynqmp_pm_clock_setdivider,
> +	.clock_getdivider = zynqmp_pm_clock_getdivider,
> +	.clock_setrate = zynqmp_pm_clock_setrate,
> +	.clock_getrate = zynqmp_pm_clock_getrate,
> +	.clock_setparent = zynqmp_pm_clock_setparent,
> +	.clock_getparent = zynqmp_pm_clock_getparent,
> +};
> +
> +/**
> + * get_eemi_ops	- Get eemi ops functions
> + *
> + * Return:	- pointer of eemi_ops structure
> + */
> +const struct zynqmp_eemi_ops *get_eemi_ops(void)
> +{
> +	return &eemi_ops;
> +}
> +EXPORT_SYMBOL_GPL(get_eemi_ops);
> +
> +static int __init zynqmp_plat_init(void)
> +{
> +	struct device_node *np;
> +	int ret = 0;
> +
> +	np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp");
> +	if (!np)
> +		return 0;
> +	of_node_put(np);
> +
> +	/* We're running on a ZynqMP machine, the PM node is mandatory. */
> +	np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp-firmware");
> +	if (!np) {
> +		pr_warn("%s: pm node not found\n", __func__);
> +		return -ENXIO;
> +	}
> +
> +	ret = get_set_conduit_method(np);
> +	if (ret) {
> +		of_node_put(np);
> +		return ret;
> +	}
> +
> +	/* Check PM API version number */
> +	zynqmp_pm_get_api_version(&pm_api_version);
> +	if (pm_api_version != ZYNQMP_PM_VERSION) {
> +		panic("%s power management API version error. Expected: v%d.%d - Found: v%d.%d\n",
> +		      __func__,
> +		      ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR,
> +		      pm_api_version >> 16, pm_api_version & 0xffff);
> +	}
> +
> +	pr_info("%s Power management API v%d.%d\n", __func__,
> +		ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR);
> +
> +	of_node_put(np);
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id firmware_of_match[] = {
> +	{ .compatible = "xlnx,zynqmp-firmware", },
> +	{ /* end of table */ },
> +};
> +
> +MODULE_DEVICE_TABLE(of, firmware_of_match);
> +
> +static int zynqmp_firmware_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	ret = zynqmp_pm_api_debugfs_init();
> +	if (ret) {
> +		pr_err("%s() debugfs init fail with error %d\n", __func__, ret);
> +		return ret;
> +	}
> +
> +	zynqmp_pm_ggs_init(&pdev->dev);
> +
> +	return ret;
> +}
> +
> +static struct platform_driver zynqmp_firmware_platform_driver = {
> +	.probe   = zynqmp_firmware_probe,
> +	.driver  = {
> +			.name             = DRIVER_NAME,
> +			.of_match_table   = firmware_of_match,
> +		   },
> +};
> +builtin_platform_driver(zynqmp_firmware_platform_driver);
> +
> +early_initcall(zynqmp_plat_init);
> diff --git a/include/linux/firmware/xilinx/zynqmp/firmware-debug.h b/include/linux/firmware/xilinx/zynqmp/firmware-debug.h
> new file mode 100644
> index 0000000..a388621
> --- /dev/null
> +++ b/include/linux/firmware/xilinx/zynqmp/firmware-debug.h
> @@ -0,0 +1,32 @@
> +/*
> + * Xilinx Zynq MPSoC Firmware layer
> + *
> + *  Copyright (C) 2014-2017 Xilinx
> + *
> + *  Michal Simek <michal.simek@xilinx.com>
> + *  Davorin Mista <davorin.mista@aggios.com>
> + *  Jolly Shah <jollys@xilinx.com>
> + *  Rajan Vaja <rajanv@xilinx.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#ifndef __SOC_ZYNQMP_FIRMWARE_DEBUG_H__
> +#define __SOC_ZYNQMP_FIRMWARE_DEBUG_H__
> +
> +#include <linux/firmware/xilinx/zynqmp/firmware.h>
> +
> +int zynqmp_pm_self_suspend(const u32 node,
> +			   const u32 latency,
> +			   const u32 state);
> +int zynqmp_pm_abort_suspend(const enum zynqmp_pm_abort_reason reason);
> +int zynqmp_pm_register_notifier(const u32 node, const u32 event,
> +				const u32 wake, const u32 enable);
> +
> +#if IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE_DEBUG)
> +int zynqmp_pm_api_debugfs_init(void);
> +#else
> +static inline int zynqmp_pm_api_debugfs_init(void) { return 0; }
> +#endif
> +
> +#endif /* __SOC_ZYNQMP_FIRMWARE_DEBUG_H__ */
> diff --git a/include/linux/firmware/xilinx/zynqmp/firmware.h b/include/linux/firmware/xilinx/zynqmp/firmware.h
> new file mode 100644
> index 0000000..2088b15
> --- /dev/null
> +++ b/include/linux/firmware/xilinx/zynqmp/firmware.h
> @@ -0,0 +1,573 @@
> +/*
> + * Xilinx Zynq MPSoC Firmware layer
> + *
> + *  Copyright (C) 2014-2017 Xilinx
> + *
> + *  Michal Simek <michal.simek@xilinx.com>
> + *  Davorin Mista <davorin.mista@aggios.com>
> + *  Jolly Shah <jollys@xilinx.com>
> + *  Rajan Vaja <rajanv@xilinx.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#ifndef __SOC_ZYNQMP_FIRMWARE_H__
> +#define __SOC_ZYNQMP_FIRMWARE_H__
> +
> +#include <linux/device.h>
> +
> +#define ZYNQMP_PM_VERSION_MAJOR	1
> +#define ZYNQMP_PM_VERSION_MINOR	0
> +
> +#define ZYNQMP_PM_VERSION	((ZYNQMP_PM_VERSION_MAJOR << 16) | \
> +					ZYNQMP_PM_VERSION_MINOR)
> +
> +#define ZYNQMP_PM_MAX_LATENCY	(~0U)
> +#define ZYNQMP_PM_MAX_QOS	100U
> +
> +/* SMC SIP service Call Function Identifier Prefix */
> +#define PM_SIP_SVC	0xC2000000
> +#define GET_CALLBACK_DATA 0xa01
> +#define SET_SUSPEND_MODE  0xa02
> +
> +/* Number of 32bits values in payload */
> +#define PAYLOAD_ARG_CNT	5U
> +
> +/* Number of arguments for a callback */
> +#define CB_ARG_CNT	4
> +
> +/* Payload size (consists of callback API ID + arguments) */
> +#define CB_PAYLOAD_SIZE	(CB_ARG_CNT + 1)
> +
> +/* Global general storage register base address */
> +#define GGS_BASEADDR	(0xFFD80030U)
> +#define GSS_NUM_REGS	(4)
> +
> +/* Persistent global general storage register base address */
> +#define PGGS_BASEADDR	(0xFFD80050U)
> +#define PGSS_NUM_REGS	(4)
> +
> +/* Capabilities for RAM */
> +#define	ZYNQMP_PM_CAPABILITY_ACCESS	0x1U
> +#define	ZYNQMP_PM_CAPABILITY_CONTEXT	0x2U
> +#define	ZYNQMP_PM_CAPABILITY_WAKEUP	0x4U
> +#define	ZYNQMP_PM_CAPABILITY_POWER	0x8U
> +
> +/* Clock APIs payload parameters */
> +#define CLK_GET_NAME_RESP_LEN				16
> +#define CLK_GET_TOPOLOGY_RESP_WORDS			3
> +#define CLK_GET_FIXEDFACTOR_RESP_WORDS			2
> +#define CLK_GET_PARENTS_RESP_WORDS			3
> +#define CLK_GET_ATTR_RESP_WORDS				1
> +
> +enum pm_api_id {
> +	/* Miscellaneous API functions: */
> +	GET_API_VERSION = 1,
> +	SET_CONFIGURATION,
> +	GET_NODE_STATUS,
> +	GET_OPERATING_CHARACTERISTIC,
> +	REGISTER_NOTIFIER,
> +	/* API for suspending of PUs: */
> +	REQUEST_SUSPEND,
> +	SELF_SUSPEND,
> +	FORCE_POWERDOWN,
> +	ABORT_SUSPEND,
> +	REQUEST_WAKEUP,
> +	SET_WAKEUP_SOURCE,
> +	SYSTEM_SHUTDOWN,
> +	/* API for managing PM slaves: */
> +	REQUEST_NODE,
> +	RELEASE_NODE,
> +	SET_REQUIREMENT,
> +	SET_MAX_LATENCY,
> +	/* Direct control API functions: */
> +	RESET_ASSERT,
> +	RESET_GET_STATUS,
> +	MMIO_WRITE,
> +	MMIO_READ,
> +	PM_INIT_FINALIZE,
> +	FPGA_LOAD,
> +	FPGA_GET_STATUS,
> +	GET_CHIPID,
> +	/* ID 25 is been used by U-boot to process secure boot images */
> +	/* Secure library generic API functions */
> +	SECURE_SHA = 26,
> +	SECURE_RSA,
> +	/* Pin control API functions */
> +	PINCTRL_REQUEST,
> +	PINCTRL_RELEASE,
> +	PINCTRL_GET_FUNCTION,
> +	PINCTRL_SET_FUNCTION,
> +	PINCTRL_CONFIG_PARAM_GET,
> +	PINCTRL_CONFIG_PARAM_SET,
> +	/* PM IOCTL API */
> +	IOCTL,
> +	/* API to query information from firmware */
> +	QUERY_DATA,
> +	/* Clock control API functions */
> +	CLOCK_ENABLE,
> +	CLOCK_DISABLE,
> +	CLOCK_GETSTATE,
> +	CLOCK_SETDIVIDER,
> +	CLOCK_GETDIVIDER,
> +	CLOCK_SETRATE,
> +	CLOCK_GETRATE,
> +	CLOCK_SETPARENT,
> +	CLOCK_GETPARENT,
> +};
> +
> +/* PMU-FW return status codes */
> +enum pm_ret_status {
> +	XST_PM_SUCCESS = 0,
> +	XST_PM_INTERNAL	= 2000,
> +	XST_PM_CONFLICT,
> +	XST_PM_NO_ACCESS,
> +	XST_PM_INVALID_NODE,
> +	XST_PM_DOUBLE_REQ,
> +	XST_PM_ABORT_SUSPEND,
> +};
> +
> +enum zynqmp_pm_reset_action {
> +	PM_RESET_ACTION_RELEASE,
> +	PM_RESET_ACTION_ASSERT,
> +	PM_RESET_ACTION_PULSE,
> +};
> +
> +enum zynqmp_pm_reset {
> +	ZYNQMP_PM_RESET_START = 999,
> +	ZYNQMP_PM_RESET_PCIE_CFG,
> +	ZYNQMP_PM_RESET_PCIE_BRIDGE,
> +	ZYNQMP_PM_RESET_PCIE_CTRL,
> +	ZYNQMP_PM_RESET_DP,
> +	ZYNQMP_PM_RESET_SWDT_CRF,
> +	ZYNQMP_PM_RESET_AFI_FM5,
> +	ZYNQMP_PM_RESET_AFI_FM4,
> +	ZYNQMP_PM_RESET_AFI_FM3,
> +	ZYNQMP_PM_RESET_AFI_FM2,
> +	ZYNQMP_PM_RESET_AFI_FM1,
> +	ZYNQMP_PM_RESET_AFI_FM0,
> +	ZYNQMP_PM_RESET_GDMA,
> +	ZYNQMP_PM_RESET_GPU_PP1,
> +	ZYNQMP_PM_RESET_GPU_PP0,
> +	ZYNQMP_PM_RESET_GPU,
> +	ZYNQMP_PM_RESET_GT,
> +	ZYNQMP_PM_RESET_SATA,
> +	ZYNQMP_PM_RESET_ACPU3_PWRON,
> +	ZYNQMP_PM_RESET_ACPU2_PWRON,
> +	ZYNQMP_PM_RESET_ACPU1_PWRON,
> +	ZYNQMP_PM_RESET_ACPU0_PWRON,
> +	ZYNQMP_PM_RESET_APU_L2,
> +	ZYNQMP_PM_RESET_ACPU3,
> +	ZYNQMP_PM_RESET_ACPU2,
> +	ZYNQMP_PM_RESET_ACPU1,
> +	ZYNQMP_PM_RESET_ACPU0,
> +	ZYNQMP_PM_RESET_DDR,
> +	ZYNQMP_PM_RESET_APM_FPD,
> +	ZYNQMP_PM_RESET_SOFT,
> +	ZYNQMP_PM_RESET_GEM0,
> +	ZYNQMP_PM_RESET_GEM1,
> +	ZYNQMP_PM_RESET_GEM2,
> +	ZYNQMP_PM_RESET_GEM3,
> +	ZYNQMP_PM_RESET_QSPI,
> +	ZYNQMP_PM_RESET_UART0,
> +	ZYNQMP_PM_RESET_UART1,
> +	ZYNQMP_PM_RESET_SPI0,
> +	ZYNQMP_PM_RESET_SPI1,
> +	ZYNQMP_PM_RESET_SDIO0,
> +	ZYNQMP_PM_RESET_SDIO1,
> +	ZYNQMP_PM_RESET_CAN0,
> +	ZYNQMP_PM_RESET_CAN1,
> +	ZYNQMP_PM_RESET_I2C0,
> +	ZYNQMP_PM_RESET_I2C1,
> +	ZYNQMP_PM_RESET_TTC0,
> +	ZYNQMP_PM_RESET_TTC1,
> +	ZYNQMP_PM_RESET_TTC2,
> +	ZYNQMP_PM_RESET_TTC3,
> +	ZYNQMP_PM_RESET_SWDT_CRL,
> +	ZYNQMP_PM_RESET_NAND,
> +	ZYNQMP_PM_RESET_ADMA,
> +	ZYNQMP_PM_RESET_GPIO,
> +	ZYNQMP_PM_RESET_IOU_CC,
> +	ZYNQMP_PM_RESET_TIMESTAMP,
> +	ZYNQMP_PM_RESET_RPU_R50,
> +	ZYNQMP_PM_RESET_RPU_R51,
> +	ZYNQMP_PM_RESET_RPU_AMBA,
> +	ZYNQMP_PM_RESET_OCM,
> +	ZYNQMP_PM_RESET_RPU_PGE,
> +	ZYNQMP_PM_RESET_USB0_CORERESET,
> +	ZYNQMP_PM_RESET_USB1_CORERESET,
> +	ZYNQMP_PM_RESET_USB0_HIBERRESET,
> +	ZYNQMP_PM_RESET_USB1_HIBERRESET,
> +	ZYNQMP_PM_RESET_USB0_APB,
> +	ZYNQMP_PM_RESET_USB1_APB,
> +	ZYNQMP_PM_RESET_IPI,
> +	ZYNQMP_PM_RESET_APM_LPD,
> +	ZYNQMP_PM_RESET_RTC,
> +	ZYNQMP_PM_RESET_SYSMON,
> +	ZYNQMP_PM_RESET_AFI_FM6,
> +	ZYNQMP_PM_RESET_LPD_SWDT,
> +	ZYNQMP_PM_RESET_FPD,
> +	ZYNQMP_PM_RESET_RPU_DBG1,
> +	ZYNQMP_PM_RESET_RPU_DBG0,
> +	ZYNQMP_PM_RESET_DBG_LPD,
> +	ZYNQMP_PM_RESET_DBG_FPD,
> +	ZYNQMP_PM_RESET_APLL,
> +	ZYNQMP_PM_RESET_DPLL,
> +	ZYNQMP_PM_RESET_VPLL,
> +	ZYNQMP_PM_RESET_IOPLL,
> +	ZYNQMP_PM_RESET_RPLL,
> +	ZYNQMP_PM_RESET_GPO3_PL_0,
> +	ZYNQMP_PM_RESET_GPO3_PL_1,
> +	ZYNQMP_PM_RESET_GPO3_PL_2,
> +	ZYNQMP_PM_RESET_GPO3_PL_3,
> +	ZYNQMP_PM_RESET_GPO3_PL_4,
> +	ZYNQMP_PM_RESET_GPO3_PL_5,
> +	ZYNQMP_PM_RESET_GPO3_PL_6,
> +	ZYNQMP_PM_RESET_GPO3_PL_7,
> +	ZYNQMP_PM_RESET_GPO3_PL_8,
> +	ZYNQMP_PM_RESET_GPO3_PL_9,
> +	ZYNQMP_PM_RESET_GPO3_PL_10,
> +	ZYNQMP_PM_RESET_GPO3_PL_11,
> +	ZYNQMP_PM_RESET_GPO3_PL_12,
> +	ZYNQMP_PM_RESET_GPO3_PL_13,
> +	ZYNQMP_PM_RESET_GPO3_PL_14,
> +	ZYNQMP_PM_RESET_GPO3_PL_15,
> +	ZYNQMP_PM_RESET_GPO3_PL_16,
> +	ZYNQMP_PM_RESET_GPO3_PL_17,
> +	ZYNQMP_PM_RESET_GPO3_PL_18,
> +	ZYNQMP_PM_RESET_GPO3_PL_19,
> +	ZYNQMP_PM_RESET_GPO3_PL_20,
> +	ZYNQMP_PM_RESET_GPO3_PL_21,
> +	ZYNQMP_PM_RESET_GPO3_PL_22,
> +	ZYNQMP_PM_RESET_GPO3_PL_23,
> +	ZYNQMP_PM_RESET_GPO3_PL_24,
> +	ZYNQMP_PM_RESET_GPO3_PL_25,
> +	ZYNQMP_PM_RESET_GPO3_PL_26,
> +	ZYNQMP_PM_RESET_GPO3_PL_27,
> +	ZYNQMP_PM_RESET_GPO3_PL_28,
> +	ZYNQMP_PM_RESET_GPO3_PL_29,
> +	ZYNQMP_PM_RESET_GPO3_PL_30,
> +	ZYNQMP_PM_RESET_GPO3_PL_31,
> +	ZYNQMP_PM_RESET_RPU_LS,
> +	ZYNQMP_PM_RESET_PS_ONLY,
> +	ZYNQMP_PM_RESET_PL,
> +	ZYNQMP_PM_RESET_END
> +};
> +
> +enum zynqmp_pm_request_ack {
> +	ZYNQMP_PM_REQUEST_ACK_NO = 1,
> +	ZYNQMP_PM_REQUEST_ACK_BLOCKING,
> +	ZYNQMP_PM_REQUEST_ACK_NON_BLOCKING,
> +};
> +
> +enum zynqmp_pm_abort_reason {
> +	ZYNQMP_PM_ABORT_REASON_WAKEUP_EVENT = 100,
> +	ZYNQMP_PM_ABORT_REASON_POWER_UNIT_BUSY,
> +	ZYNQMP_PM_ABORT_REASON_NO_POWERDOWN,
> +	ZYNQMP_PM_ABORT_REASON_UNKNOWN,
> +};
> +
> +enum zynqmp_pm_suspend_reason {
> +	ZYNQMP_PM_SUSPEND_REASON_POWER_UNIT_REQUEST = 201,
> +	ZYNQMP_PM_SUSPEND_REASON_ALERT,
> +	ZYNQMP_PM_SUSPEND_REASON_SYSTEM_SHUTDOWN,
> +};
> +
> +enum zynqmp_pm_ram_state {
> +	ZYNQMP_PM_RAM_STATE_OFF = 1,
> +	ZYNQMP_PM_RAM_STATE_RETENTION,
> +	ZYNQMP_PM_RAM_STATE_ON,
> +};
> +
> +enum zynqmp_pm_opchar_type {
> +	ZYNQMP_PM_OPERATING_CHARACTERISTIC_POWER = 1,
> +	ZYNQMP_PM_OPERATING_CHARACTERISTIC_ENERGY,
> +	ZYNQMP_PM_OPERATING_CHARACTERISTIC_TEMPERATURE,
> +};
> +
> +enum pm_node_id {
> +	NODE_UNKNOWN = 0,
> +	NODE_APU,
> +	NODE_APU_0,
> +	NODE_APU_1,
> +	NODE_APU_2,
> +	NODE_APU_3,
> +	NODE_RPU,
> +	NODE_RPU_0,
> +	NODE_RPU_1,
> +	NODE_PLD,
> +	NODE_FPD,
> +	NODE_OCM_BANK_0,
> +	NODE_OCM_BANK_1,
> +	NODE_OCM_BANK_2,
> +	NODE_OCM_BANK_3,
> +	NODE_TCM_0_A,
> +	NODE_TCM_0_B,
> +	NODE_TCM_1_A,
> +	NODE_TCM_1_B,
> +	NODE_L2,
> +	NODE_GPU_PP_0,
> +	NODE_GPU_PP_1,
> +	NODE_USB_0,
> +	NODE_USB_1,
> +	NODE_TTC_0,
> +	NODE_TTC_1,
> +	NODE_TTC_2,
> +	NODE_TTC_3,
> +	NODE_SATA,
> +	NODE_ETH_0,
> +	NODE_ETH_1,
> +	NODE_ETH_2,
> +	NODE_ETH_3,
> +	NODE_UART_0,
> +	NODE_UART_1,
> +	NODE_SPI_0,
> +	NODE_SPI_1,
> +	NODE_I2C_0,
> +	NODE_I2C_1,
> +	NODE_SD_0,
> +	NODE_SD_1,
> +	NODE_DP,
> +	NODE_GDMA,
> +	NODE_ADMA,
> +	NODE_NAND,
> +	NODE_QSPI,
> +	NODE_GPIO,
> +	NODE_CAN_0,
> +	NODE_CAN_1,
> +	NODE_EXTERN,
> +	NODE_APLL,
> +	NODE_VPLL,
> +	NODE_DPLL,
> +	NODE_RPLL,
> +	NODE_IOPLL,
> +	NODE_DDR,
> +	NODE_IPI_APU,
> +	NODE_IPI_RPU_0,
> +	NODE_GPU,
> +	NODE_PCIE,
> +	NODE_PCAP,
> +	NODE_RTC,
> +	NODE_LPD,
> +	NODE_VCU,
> +	NODE_IPI_RPU_1,
> +	NODE_IPI_PL_0,
> +	NODE_IPI_PL_1,
> +	NODE_IPI_PL_2,
> +	NODE_IPI_PL_3,
> +	NODE_PL,
> +	NODE_GEM_TSU,
> +	NODE_SWDT_0,
> +	NODE_SWDT_1,
> +	NODE_CSU,
> +	NODE_PJTAG,
> +	NODE_TRACE,
> +	NODE_TESTSCAN,
> +	NODE_PMU,
> +	NODE_MAX,
> +};
> +
> +enum pm_pinctrl_config_param {
> +	PM_PINCTRL_CONFIG_SLEW_RATE,
> +	PM_PINCTRL_CONFIG_BIAS_STATUS,
> +	PM_PINCTRL_CONFIG_PULL_CTRL,
> +	PM_PINCTRL_CONFIG_SCHMITT_CMOS,
> +	PM_PINCTRL_CONFIG_DRIVE_STRENGTH,
> +	PM_PINCTRL_CONFIG_VOLTAGE_STATUS,
> +	PM_PINCTRL_CONFIG_MAX,
> +};
> +
> +enum pm_pinctrl_slew_rate {
> +	PM_PINCTRL_SLEW_RATE_FAST,
> +	PM_PINCTRL_SLEW_RATE_SLOW,
> +};
> +
> +enum pm_pinctrl_bias_status {
> +	PM_PINCTRL_BIAS_DISABLE,
> +	PM_PINCTRL_BIAS_ENABLE,
> +};
> +
> +enum pm_pinctrl_pull_ctrl {
> +	PM_PINCTRL_BIAS_PULL_DOWN,
> +	PM_PINCTRL_BIAS_PULL_UP,
> +};
> +
> +enum pm_pinctrl_schmitt_cmos {
> +	PM_PINCTRL_INPUT_TYPE_CMOS,
> +	PM_PINCTRL_INPUT_TYPE_SCHMITT,
> +};
> +
> +enum pm_pinctrl_drive_strength {
> +	PM_PINCTRL_DRIVE_STRENGTH_2MA,
> +	PM_PINCTRL_DRIVE_STRENGTH_4MA,
> +	PM_PINCTRL_DRIVE_STRENGTH_8MA,
> +	PM_PINCTRL_DRIVE_STRENGTH_12MA,
> +};
> +
> +enum pm_ioctl_id {
> +	IOCTL_GET_RPU_OPER_MODE,
> +	IOCTL_SET_RPU_OPER_MODE,
> +	IOCTL_RPU_BOOT_ADDR_CONFIG,
> +	IOCTL_TCM_COMB_CONFIG,
> +	IOCTL_SET_TAPDELAY_BYPASS,
> +	IOCTL_SET_SGMII_MODE,
> +	IOCTL_SD_DLL_RESET,
> +	IOCTL_SET_SD_TAPDELAY,
> +	/* Ioctl for clock driver */
> +	IOCTL_SET_PLL_FRAC_MODE,
> +	IOCTL_GET_PLL_FRAC_MODE,
> +	IOCTL_SET_PLL_FRAC_DATA,
> +	IOCTL_GET_PLL_FRAC_DATA,
> +	IOCTL_WRITE_GGS,
> +	IOCTL_READ_GGS,
> +	IOCTL_WRITE_PGGS,
> +	IOCTL_READ_PGGS,
> +};
> +
> +enum rpu_oper_mode {
> +	PM_RPU_MODE_LOCKSTEP,
> +	PM_RPU_MODE_SPLIT,
> +};
> +
> +enum rpu_boot_mem {
> +	PM_RPU_BOOTMEM_LOVEC,
> +	PM_RPU_BOOTMEM_HIVEC,
> +};
> +
> +enum rpu_tcm_comb {
> +	PM_RPU_TCM_SPLIT,
> +	PM_RPU_TCM_COMB,
> +};
> +
> +enum tap_delay_signal_type {
> +	PM_TAPDELAY_NAND_DQS_IN,
> +	PM_TAPDELAY_NAND_DQS_OUT,
> +	PM_TAPDELAY_QSPI,
> +	PM_TAPDELAY_MAX,
> +};
> +
> +enum tap_delay_bypass_ctrl {
> +	PM_TAPDELAY_BYPASS_DISABLE,
> +	PM_TAPDELAY_BYPASS_ENABLE,
> +};
> +
> +enum sgmii_mode {
> +	PM_SGMII_DISABLE,
> +	PM_SGMII_ENABLE,
> +};
> +
> +enum tap_delay_type {
> +	PM_TAPDELAY_INPUT,
> +	PM_TAPDELAY_OUTPUT,
> +};
> +
> +enum dll_reset_type {
> +	PM_DLL_RESET_ASSERT,
> +	PM_DLL_RESET_RELEASE,
> +	PM_DLL_RESET_PULSE,
> +};
> +
> +enum topology_type {
> +	TYPE_INVALID,
> +	TYPE_MUX,
> +	TYPE_PLL,
> +	TYPE_FIXEDFACTOR,
> +	TYPE_DIV1,
> +	TYPE_DIV2,
> +	TYPE_GATE,
> +};
> +
> +enum pm_query_id {
> +	PM_QID_INVALID,
> +	PM_QID_CLOCK_GET_NAME,
> +	PM_QID_CLOCK_GET_TOPOLOGY,
> +	PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS,
> +	PM_QID_CLOCK_GET_PARENTS,
> +	PM_QID_CLOCK_GET_ATTRIBUTES,
> +};
> +
> +struct zynqmp_pm_query_data {
> +	u32 qid;
> +	u32 arg1;
> +	u32 arg2;
> +	u32 arg3;
> +};
> +
> +struct zynqmp_eemi_ops {
> +	int (*get_api_version)(u32 *version);
> +	int (*get_chipid)(u32 *idcode, u32 *version);
> +	int (*reset_assert)(const enum zynqmp_pm_reset reset,
> +			    const enum zynqmp_pm_reset_action assert_flag);
> +	int (*reset_get_status)(const enum zynqmp_pm_reset reset, u32 *status);
> +	int (*fpga_load)(const u64 address, const u32 size, const u32 flags);
> +	int (*fpga_get_status)(u32 *value);
> +	int (*sha_hash)(const u64 address, const u32 size, const u32 flags);
> +	int (*rsa)(const u64 address, const u32 size, const u32 flags);
> +	int (*request_suspend)(const u32 node,
> +			       const enum zynqmp_pm_request_ack ack,
> +			       const u32 latency,
> +			       const u32 state);
> +	int (*force_powerdown)(const u32 target,
> +			       const enum zynqmp_pm_request_ack ack);
> +	int (*request_wakeup)(const u32 node,
> +			      const bool set_addr,
> +			      const u64 address,
> +			      const enum zynqmp_pm_request_ack ack);
> +	int (*set_wakeup_source)(const u32 target,
> +				 const u32 wakeup_node,
> +				 const u32 enable);
> +	int (*system_shutdown)(const u32 type, const u32 subtype);
> +	int (*request_node)(const u32 node,
> +			    const u32 capabilities,
> +			    const u32 qos,
> +			    const enum zynqmp_pm_request_ack ack);
> +	int (*release_node)(const u32 node);
> +	int (*set_requirement)(const u32 node,
> +			       const u32 capabilities,
> +			       const u32 qos,
> +			       const enum zynqmp_pm_request_ack ack);
> +	int (*set_max_latency)(const u32 node, const u32 latency);
> +	int (*set_configuration)(const u32 physical_addr);
> +	int (*get_node_status)(const u32 node, u32 *const status,
> +			       u32 *const requirements, u32 *const usage);
> +	int (*get_operating_characteristic)(const u32 node,
> +					    const enum zynqmp_pm_opchar_type
> +					    type, u32 *const result);
> +	int (*init_finalize)(void);
> +	int (*get_callback_data)(u32 *buf);
> +	int (*set_suspend_mode)(u32 mode);
> +	int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out);
> +	int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out);
> +	int (*pinctrl_request)(const u32 pin);
> +	int (*pinctrl_release)(const u32 pin);
> +	int (*pinctrl_get_function)(const u32 pin, u32 *node);
> +	int (*pinctrl_set_function)(const u32 pin, const u32 node);
> +	int (*pinctrl_get_config)(const u32 pin, const u32 param, u32 *value);
> +	int (*pinctrl_set_config)(const u32 pin, const u32 param, u32 value);
> +	int (*clock_enable)(u32 clock_id);
> +	int (*clock_disable)(u32 clock_id);
> +	int (*clock_getstate)(u32 clock_id, u32 *state);
> +	int (*clock_setdivider)(u32 clock_id, u32 divider);
> +	int (*clock_getdivider)(u32 clock_id, u32 *divider);
> +	int (*clock_setrate)(u32 clock_id, u32 rate);
> +	int (*clock_getrate)(u32 clock_id, u32 *rate);
> +	int (*clock_setparent)(u32 clock_id, u32 parent_id);
> +	int (*clock_getparent)(u32 clock_id, u32 *parent_id);
> +};
> +
> +/*
> + * Internal functions
> + */
> +int invoke_pm_fn(u32 pm_api_id, u32 arg0, u32 arg1, u32 arg2, u32 arg3,
> +		 u32 *ret_payload);
> +int zynqmp_pm_ret_code(u32 ret_status);
> +
> +void zynqmp_pm_ggs_init(struct device *dev);
> +
> +#if IS_REACHABLE(CONFIG_ARCH_ZYNQMP)
> +const struct zynqmp_eemi_ops *get_eemi_ops(void);
> +#else
> +static inline struct zynqmp_eemi_ops *get_eemi_ops(void) { return NULL; }
> +#endif
> +
> +#endif /* __SOC_ZYNQMP_FIRMWARE_H__ */
> -- 
> 2.7.4
> 

^ permalink raw reply

* [PATCH 3/9] soc: samsung: pmu: Add the PMU data of exynos5433 to support low-power state
From: Krzysztof Kozlowski @ 2018-01-09 12:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515484746-10656-4-git-send-email-cw00.choi@samsung.com>

On Tue, Jan 9, 2018 at 8:59 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> This patch adds the PMU (Power Management Unit) data of exynos5433 SoC
> in order to support the various power modes. Each power mode has
> the different value for reducing the power-consumption.
>
> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> ---
>  arch/arm/mach-exynos/common.h               |   2 -
>  drivers/soc/samsung/Makefile                |   3 +-
>  drivers/soc/samsung/exynos-pmu.c            |   1 +
>  drivers/soc/samsung/exynos-pmu.h            |   2 +
>  drivers/soc/samsung/exynos5433-pmu.c        | 286 ++++++++++++++++++++++++++++
>  include/linux/soc/samsung/exynos-regs-pmu.h | 148 ++++++++++++++
>  6 files changed, 439 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/soc/samsung/exynos5433-pmu.c
>
> diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
> index 098f84a149a3..afbc143a3d5d 100644
> --- a/arch/arm/mach-exynos/common.h
> +++ b/arch/arm/mach-exynos/common.h
> @@ -125,8 +125,6 @@ enum {
>  void exynos_set_boot_flag(unsigned int cpu, unsigned int mode);
>  void exynos_clear_boot_flag(unsigned int cpu, unsigned int mode);
>
> -extern u32 exynos_get_eint_wake_mask(void);
> -

This does not look good. Does it compile without warnings on ARMv7 platforms?

>  #ifdef CONFIG_PM_SLEEP
>  extern void __init exynos_pm_init(void);
>  #else
> diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
> index 29f294baac6e..d2e637339a45 100644
> --- a/drivers/soc/samsung/Makefile
> +++ b/drivers/soc/samsung/Makefile
> @@ -2,5 +2,6 @@
>  obj-$(CONFIG_EXYNOS_PMU)       += exynos-pmu.o
>
>  obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS)   += exynos3250-pmu.o exynos4-pmu.o \
> -                                       exynos5250-pmu.o exynos5420-pmu.o
> +                                       exynos5250-pmu.o exynos5420-pmu.o \
> +                                       exynos5433-pmu.o
>  obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o
> diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
> index cfc9de518344..7112d7b2749b 100644
> --- a/drivers/soc/samsung/exynos-pmu.c
> +++ b/drivers/soc/samsung/exynos-pmu.c
> @@ -97,6 +97,7 @@ void exynos_sys_powerup_conf(enum sys_powerdown mode)
>                 .data = exynos_pmu_data_arm_ptr(exynos5420_pmu_data),
>         }, {
>                 .compatible = "samsung,exynos5433-pmu",
> +               .data = exynos_pmu_data_arm_ptr(exynos5433_pmu_data),
>         },
>         { /*sentinel*/ },
>  };
> diff --git a/drivers/soc/samsung/exynos-pmu.h b/drivers/soc/samsung/exynos-pmu.h
> index efbaf8929252..895c786a2f4c 100644
> --- a/drivers/soc/samsung/exynos-pmu.h
> +++ b/drivers/soc/samsung/exynos-pmu.h
> @@ -28,6 +28,7 @@ struct exynos_pmu_data {
>  };
>
>  extern void __iomem *pmu_base_addr;
> +extern u32 exynos_get_eint_wake_mask(void);
>
>  #ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS
>  /* list of all exported SoC specific data */
> @@ -36,6 +37,7 @@ struct exynos_pmu_data {
>  extern const struct exynos_pmu_data exynos4412_pmu_data;
>  extern const struct exynos_pmu_data exynos5250_pmu_data;
>  extern const struct exynos_pmu_data exynos5420_pmu_data;
> +extern const struct exynos_pmu_data exynos5433_pmu_data;
>  #endif
>
>  extern void pmu_raw_writel(u32 val, u32 offset);
> diff --git a/drivers/soc/samsung/exynos5433-pmu.c b/drivers/soc/samsung/exynos5433-pmu.c
> new file mode 100644
> index 000000000000..2571e61522f0
> --- /dev/null
> +++ b/drivers/soc/samsung/exynos5433-pmu.c
> @@ -0,0 +1,286 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (c) 2018 Samsung Electronics Co., Ltd.
> +// Copyright (c) Jonghwa Lee <jonghwa3.lee@samsung.com>
> +// Copyright (c) Chanwoo Choi <cw00.choi@samsung.com>

Did you want to add here authorship notice or personal copyrights?

> +//
> +// EXYNOS5433 - CPU PMU (Power Management Unit) support
> +
> +#include <linux/soc/samsung/exynos-regs-pmu.h>
> +#include <linux/soc/samsung/exynos-pmu.h>
> +
> +#include "exynos-pmu.h"
> +
> +static struct exynos_pmu_conf exynos5433_pmu_config[] = {

This should be also const.

> +       /* { .offset = address, .val = { AFTR, LPA, SLEEP } } */
> +       { EXYNOS5433_ATLAS_CPU0_SYS_PWR_REG,                    { 0x0, 0x0, 0x8 } },
> +       { EXYNOS5433_DIS_IRQ_ATLAS_CPU0_CENTRAL_SYS_PWR_REG,    { 0x0, 0x0, 0x0 } },
> +       { EXYNOS5433_ATLAS_CPU1_SYS_PWR_REG,                    { 0x0, 0x0, 0x8 } },
> +       { EXYNOS5433_DIS_IRQ_ATLAS_CPU1_CENTRAL_SYS_PWR_REG,    { 0x0, 0x0, 0x0 } },
> +       { EXYNOS5433_ATLAS_CPU2_SYS_PWR_REG,                    { 0x0, 0x0, 0x8 } },
> +       { EXYNOS5433_DIS_IRQ_ATLAS_CPU2_CENTRAL_SYS_PWR_REG,    { 0x0, 0x0, 0x0 } },
> +       { EXYNOS5433_ATLAS_CPU3_SYS_PWR_REG,                    { 0x0, 0x0, 0x8 } },
> +       { EXYNOS5433_DIS_IRQ_ATLAS_CPU3_CENTRAL_SYS_PWR_REG,    { 0x0, 0x0, 0x0 } },
> +       { EXYNOS5433_APOLLO_CPU0_SYS_PWR_REG,                   { 0x0, 0x0, 0x8 } },
> +       { EXYNOS5433_DIS_IRQ_APOLLO_CPU0_CENTRAL_SYS_PWR_REG,   { 0x0, 0x0, 0x0 } },
> +       { EXYNOS5433_APOLLO_CPU1_SYS_PWR_REG,                   { 0x0, 0x0, 0x8 } },
> +       { EXYNOS5433_DIS_IRQ_APOLLO_CPU1_CENTRAL_SYS_PWR_REG,   { 0x0, 0x0, 0x0 } },
> +       { EXYNOS5433_APOLLO_CPU2_SYS_PWR_REG,                   { 0x0, 0x0, 0x8 } },
> +       { EXYNOS5433_DIS_IRQ_APOLLO_CPU2_CENTRAL_SYS_PWR_REG,   { 0x0, 0x0, 0x0 } },
> +       { EXYNOS5433_APOLLO_CPU3_SYS_PWR_REG,                   { 0x0, 0x0, 0x8 } },
> +       { EXYNOS5433_DIS_IRQ_APOLLO_CPU3_CENTRAL_SYS_PWR_REG,   { 0x0, 0x0, 0x0 } },
> +       { EXYNOS5433_ATLAS_NONCPU_SYS_PWR_REG,                  { 0x0, 0x0, 0x8 } },
> +       { EXYNOS5433_APOLLO_NONCPU_SYS_PWR_REG,                 { 0x0, 0x0, 0x8 } },
> +       { EXYNOS5433_A5IS_SYS_PWR_REG,                          { 0x0, 0x0, 0x0 } },
> +       { EXYNOS5433_DIS_IRQ_A5IS_LOCAL_SYS_PWR_REG,            { 0x0, 0x0, 0x0 } },
> +       { EXYNOS5433_DIS_IRQ_A5IS_CENTRAL_SYS_PWR_REG,          { 0x0, 0x0, 0x0 } },
> +       { EXYNOS5433_ATLAS_L2_SYS_PWR_REG,                      { 0x0, 0x0, 0x7 } },
> +       { EXYNOS5433_APOLLO_L2_SYS_PWR_REG,                     { 0x0, 0x0, 0x7 } },
> +       { EXYNOS5433_CLKSTOP_CMU_TOP_SYS_PWR_REG,               { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_CLKRUN_CMU_TOP_SYS_PWR_REG,                { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_RESET_CMU_TOP_SYS_PWR_REG,                 { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_RESET_CPUCLKSTOP_SYS_PWR_REG,              { 0x1, 0x1, 0x0 } },
> +       { EXYNOS5433_CLKSTOP_CMU_MIF_SYS_PWR_REG,               { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_CLKRUN_CMU_MIF_SYS_PWR_REG,                { 0x1, 0x1, 0x0 } },
> +       { EXYNOS5433_RESET_CMU_MIF_SYS_PWR_REG,                 { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_DDRPHY_DLLLOCK_SYS_PWR_REG,                { 0x1, 0x1, 0x1 } },
> +       { EXYNOS5433_DISABLE_PLL_CMU_TOP_SYS_PWR_REG,           { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_DISABLE_PLL_AUD_PLL_SYS_PWR_REG,           { 0x1, 0x1, 0x0 } },
> +       { EXYNOS5433_DISABLE_PLL_CMU_MIF_SYS_PWR_REG,           { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_TOP_BUS_SYS_PWR_REG,                       { 0x7, 0x0, 0x0 } },
> +       { EXYNOS5433_TOP_RETENTION_SYS_PWR_REG,                 { 0x1, 0x0, 0x1 } },
> +       { EXYNOS5433_TOP_PWR_SYS_PWR_REG,                       { 0x3, 0x0, 0x3 } },
> +       { EXYNOS5433_TOP_BUS_MIF_SYS_PWR_REG,                   { 0x7, 0x0, 0x0 } },
> +       { EXYNOS5433_TOP_RETENTION_MIF_SYS_PWR_REG,             { 0x1, 0x0, 0x1 } },
> +       { EXYNOS5433_TOP_PWR_MIF_SYS_PWR_REG,                   { 0x3, 0x0, 0x3 } },
> +       { EXYNOS5433_LOGIC_RESET_SYS_PWR_REG,                   { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_OSCCLK_GATE_SYS_PWR_REG,                   { 0x1, 0x0, 0x1 } },
> +       { EXYNOS5433_SLEEP_RESET_SYS_PWR_REG,                   { 0x1, 0x1, 0x0 } },
> +       { EXYNOS5433_LOGIC_RESET_MIF_SYS_PWR_REG,               { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_OSCCLK_GATE_MIF_SYS_PWR_REG,               { 0x1, 0x0, 0x1 } },
> +       { EXYNOS5433_SLEEP_RESET_MIF_SYS_PWR_REG,               { 0x1, 0x1, 0x0 } },
> +       { EXYNOS5433_MEMORY_TOP_SYS_PWR_REG,                    { 0x3, 0x0, 0x0 } },
> +       { EXYNOS5433_PAD_RETENTION_LPDDR3_SYS_PWR_REG,          { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_PAD_RETENTION_JTAG_SYS_PWR_REG,            { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_PAD_RETENTION_TOP_SYS_PWR_REG,             { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_PAD_RETENTION_UART_SYS_PWR_REG,            { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_PAD_RETENTION_EBIA_SYS_PWR_REG,            { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_PAD_RETENTION_EBIB_SYS_PWR_REG,            { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_PAD_RETENTION_SPI_SYS_PWR_REG,             { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_PAD_RETENTION_MIF_SYS_PWR_REG,             { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_PAD_ISOLATION_SYS_PWR_REG,                 { 0x1, 0x0, 0x1 } },
> +       { EXYNOS5433_PAD_RETENTION_USBXTI_SYS_PWR_REG,          { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_PAD_RETENTION_BOOTLDO_SYS_PWR_REG,         { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_PAD_ISOLATION_MIF_SYS_PWR_REG,             { 0x1, 0x0, 0x1 } },
> +       { EXYNOS5433_PAD_RETENTION_FSYSGENIO_SYS_PWR_REG,       { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_PAD_ALV_SEL_SYS_PWR_REG,                   { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_XXTI_SYS_PWR_REG,                          { 0x1, 0x1, 0x0 } },
> +       { EXYNOS5433_XXTI26_SYS_PWR_REG,                        { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_EXT_REGULATOR_SYS_PWR_REG,                 { 0x1, 0x1, 0x0 } },
> +       { EXYNOS5433_GPIO_MODE_SYS_PWR_REG,                     { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_GPIO_MODE_FSYS0_SYS_PWR_REG,               { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_GPIO_MODE_MIF_SYS_PWR_REG,                 { 0x1, 0x0, 0x0 } },
> +       { EXYNOS5433_GPIO_MODE_AUD_SYS_PWR_REG,                 { 0x1, 0x1, 0x0 } },
> +       { EXYNOS5433_GSCL_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
> +       { EXYNOS5433_CAM0_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
> +       { EXYNOS5433_MSCL_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
> +       { EXYNOS5433_G3D_SYS_PWR_REG,                           { 0xF, 0x0, 0x0 } },
> +       { EXYNOS5433_DISP_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
> +       { EXYNOS5433_CAM1_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
> +       { EXYNOS5433_AUD_SYS_PWR_REG,                           { 0xF, 0xF, 0x0 } },
> +       { EXYNOS5433_FSYS_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
> +       { EXYNOS5433_BUS2_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
> +       { EXYNOS5433_G2D_SYS_PWR_REG,                           { 0xF, 0x0, 0x0 } },
> +       { EXYNOS5433_ISP0_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
> +       { EXYNOS5433_MFC_SYS_PWR_REG,                           { 0xF, 0x0, 0x0 } },
> +       { EXYNOS5433_HEVC_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
> +       { EXYNOS5433_RESET_SLEEP_FSYS_SYS_PWR_REG,              { 0x1, 0x1, 0x0 } },
> +       { EXYNOS5433_RESET_SLEEP_BUS2_SYS_PWR_REG,              { 0x1, 0x1, 0x0 } },
> +       { PMU_TABLE_END, },
> +};
> +
> +static unsigned int const exynos5433_list_feed[] = {
> +       EXYNOS5433_ATLAS_NONCPU_OPTION,
> +       EXYNOS5433_APOLLO_NONCPU_OPTION,
> +       EXYNOS5433_TOP_PWR_OPTION,
> +       EXYNOS5433_TOP_PWR_MIF_OPTION,
> +       EXYNOS5433_AUD_OPTION,
> +       EXYNOS5433_CAM0_OPTION,
> +       EXYNOS5433_DISP_OPTION,
> +       EXYNOS5433_G2D_OPTION,
> +       EXYNOS5433_G3D_OPTION,
> +       EXYNOS5433_HEVC_OPTION,
> +       EXYNOS5433_MSCL_OPTION,
> +       EXYNOS5433_MFC_OPTION,
> +       EXYNOS5433_GSCL_OPTION,
> +       EXYNOS5433_FSYS_OPTION,
> +       EXYNOS5433_ISP_OPTION,
> +       EXYNOS5433_BUS2_OPTION,
> +};
> +
> +static unsigned int const exynos5433_list_pad_retention[] = {
> +       EXYNOS5433_PAD_RETENTION_LPDDR3_OPTION,
> +       EXYNOS5433_PAD_RETENTION_AUD_OPTION,
> +       EXYNOS5433_PAD_RETENTION_MMC2_OPTION,
> +       EXYNOS5433_PAD_RETENTION_TOP_OPTION,
> +       EXYNOS5433_PAD_RETENTION_UART_OPTION,
> +       EXYNOS5433_PAD_RETENTION_MMC0_OPTION,
> +       EXYNOS5433_PAD_RETENTION_MMC1_OPTION,
> +       EXYNOS5433_PAD_RETENTION_EBIA_OPTION,
> +       EXYNOS5433_PAD_RETENTION_EBIB_OPTION,
> +       EXYNOS5433_PAD_RETENTION_SPI_OPTION,
> +       EXYNOS5433_PAD_RETENTION_MIF_OPTION,
> +       EXYNOS5433_PAD_RETENTION_USBXTI_OPTION,
> +       EXYNOS5433_PAD_RETENTION_BOOTLDO_OPTION,
> +       EXYNOS5433_PAD_RETENTION_UFS_OPTION,
> +       EXYNOS5433_PAD_RETENTION_FSYSGENIO_OPTION,

Looks like conflicting with existing
drivers/pinctrl/samsung/pinctrl-exynos-arm64.c... and probably this
should be part of pinctrl driver's suspend/resume paths.

> +};
> +
> +static void exynos5433_set_wakeupmask(enum sys_powerdown mode)
> +{
> +       u32 intmask = 0;
> +
> +       pmu_raw_writel(exynos_get_eint_wake_mask(),
> +                                       EXYNOS5433_EINT_WAKEUP_MASK);
> +
> +       /* Disable WAKEUP event monitor */
> +       intmask = pmu_raw_readl(EXYNOS5433_WAKEUP_MASK);
> +       intmask &= ~(1 << 31);

This should have a define. Maybe it is an already defined field like
S5P_CORE_AUTOWAKEUP_EN or S5P_PS_HOLD_EN?

> +       pmu_raw_writel(intmask, EXYNOS5433_WAKEUP_MASK);
> +
> +       pmu_raw_writel(0xFFFF0000, EXYNOS5433_WAKEUP_MASK2);
> +       pmu_raw_writel(0xFFFF0000, EXYNOS5433_WAKEUP_MASK3);

Both need explaining what you are masking, preferably by appropriate
comment and maybe also define for raw constants.

> +}
> +
> +static void exynos5433_pmu_central_seq(bool enable)
> +{
> +       unsigned int tmp;
> +
> +       tmp = pmu_raw_readl(EXYNOS5433_CENTRAL_SEQ_CONFIGURATION);
> +       if (enable)
> +               tmp &= ~EXYNOS5433_CENTRALSEQ_PWR_CFG;
> +       else
> +               tmp |= EXYNOS5433_CENTRALSEQ_PWR_CFG;
> +       pmu_raw_writel(tmp, EXYNOS5433_CENTRAL_SEQ_CONFIGURATION);
> +
> +       tmp = pmu_raw_readl(EXYNOS5433_CENTRAL_SEQ_MIF_CONFIGURATION);
> +       if (enable)
> +               tmp &= ~EXYNOS5433_CENTRALSEQ_PWR_CFG;
> +       else
> +               tmp |= EXYNOS5433_CENTRALSEQ_PWR_CFG;
> +       pmu_raw_writel(tmp, EXYNOS5433_CENTRAL_SEQ_MIF_CONFIGURATION);
> +}
> +
> +static void exynos5433_pmu_pad_retention_release(void)
> +{
> +       unsigned int tmp;
> +       int i;

unsigned int i

> +
> +       for (i = 0 ; i < ARRAY_SIZE(exynos5433_list_pad_retention) ; i++) {
> +               tmp = pmu_raw_readl(exynos5433_list_pad_retention[i]);
> +               tmp |= EXYNOS5433_INITIATE_WAKEUP_FROM_LOWPOWER;
> +               pmu_raw_writel(tmp, exynos5433_list_pad_retention[i]);
> +       }
> +}
> +
> +static void exynos5433_pmu_init(void)
> +{
> +       unsigned int tmp;
> +       int i, cluster, cpu;

unsigned int i

> +
> +       /* Enable non retention flip-flop reset for wakeup */
> +       tmp = pmu_raw_readl(EXYNOS5433_PMU_SPARE0);
> +       tmp |= EXYNOS5433_EN_NONRET_RESET;
> +       pmu_raw_writel(tmp, EXYNOS5433_PMU_SPARE0);

This is spare register. Who is using it? Firmware? Please add its
usage also in Documentation/arm/Samsung/Bootloader-interface.txt.

> +
> +        /* Enable only SC_FEEDBACK for the register list */
> +       for (i = 0 ; i < ARRAY_SIZE(exynos5433_list_feed) ; i++) {
> +               tmp = pmu_raw_readl(exynos5433_list_feed[i]);
> +               tmp &= ~EXYNOS5_USE_SC_COUNTER;
> +               tmp |= EXYNOS5_USE_SC_FEEDBACK;
> +               pmu_raw_writel(tmp, exynos5433_list_feed[i]);
> +       }
> +
> +       /*
> +        * Disable automatic L2 flush, Disable L2 retention and
> +        * Enable STANDBYWFIL2, ACE/ACP
> +        */
> +       for (cluster = 0; cluster < 2; cluster++) {
> +               tmp = pmu_raw_readl(EXYNOS5433_ATLAS_L2_OPTION + (cluster * 0x20));

I would prefer to follow the convention for similar registers for cores, like:
EXYNOS_ARM_CORE_CONFIGURATION
EXYNOS_ARM_CORE_STATUS

This moves the offset into the header, along to other register offsets.

> +               tmp &= ~(EXYNOS5433_USE_AUTO_L2FLUSHREQ | EXYNOS5433_USE_RETENTION);
> +
> +               if (cluster == 0) {
> +                       tmp |= (EXYNOS5433_USE_STANDBYWFIL2 |
> +                               EXYNOS5433_USE_DEACTIVATE_ACE |
> +                               EXYNOS5433_USE_DEACTIVATE_ACP);
> +               }
> +               pmu_raw_writel(tmp, EXYNOS5433_ATLAS_L2_OPTION + (cluster * 0x20));
> +       }
> +
> +       /*
> +        * Enable both SC_COUNTER and SC_FEEDBACK for the CPUs
> +        * Use STANDBYWFI and SMPEN to indicate that core is ready to enter
> +        * low power mode
> +        */
> +       for (cpu = 0; cpu < 8; cpu++) {
> +               tmp = pmu_raw_readl(EXYNOS5433_CPU_OPTION(cpu));
> +               tmp |= (EXYNOS5_USE_SC_FEEDBACK | EXYNOS5_USE_SC_COUNTER);
> +               tmp |= EXYNOS5433_USE_SMPEN;
> +               tmp |= EXYNOS5433_USE_STANDBYWFI;
> +               tmp &= ~EXYNOS5433_USE_STANDBYWFE;
> +               pmu_raw_writel(tmp, EXYNOS5433_CPU_OPTION(cpu));
> +
> +               tmp = pmu_raw_readl(EXYNOS5433_CPU_DURATION(cpu));
> +               tmp |= EXYNOS5433_DUR_WAIT_RESET;
> +               tmp &= ~EXYNOS5433_DUR_SCALL;
> +               tmp |= EXYNOS5433_DUR_SCALL_VALUE;
> +               pmu_raw_writel(tmp, EXYNOS5433_CPU_DURATION(cpu));
> +       }
> +
> +       /* Skip atlas block power-off during automatic power down sequence */
> +       tmp = pmu_raw_readl(EXYNOS5433_ATLAS_CPUSEQUENCER_OPTION);
> +       tmp |= EXYNOS5433_SKIP_BLK_PWR_DOWN;
> +       pmu_raw_writel(tmp, EXYNOS5433_ATLAS_CPUSEQUENCER_OPTION);
> +
> +       /* Limit in-rush current during local power up of cores */
> +       tmp = pmu_raw_readl(EXYNOS5433_UP_SCHEDULER);
> +       tmp |= EXYNOS5433_ENABLE_ATLAS_CPU;
> +       pmu_raw_writel(tmp, EXYNOS5433_UP_SCHEDULER);
> +}
> +
> +static void exynos5433_powerdown_conf(enum sys_powerdown mode)
> +{
> +       switch (mode) {
> +       case SYS_SLEEP:
> +               exynos5433_set_wakeupmask(mode);
> +               exynos5433_pmu_central_seq(true);
> +               break;
> +       default:
> +               break;
> +       };
> +}
> +
> +static void exynos5433_powerup_conf(enum sys_powerdown mode)
> +{
> +       unsigned int wakeup;
> +
> +       switch (mode) {
> +       case SYS_SLEEP:
> +               wakeup = pmu_raw_readl(EXYNOS5433_CENTRAL_SEQ_CONFIGURATION);
> +               wakeup &= EXYNOS5433_CENTRALSEQ_PWR_CFG;
> +               if (wakeup)
> +                       exynos5433_pmu_pad_retention_release();
> +               else
> +                       exynos5433_pmu_central_seq(false);

I do not understand what you want to achieve here. Re-suspend?

> +               break;
> +       default:
> +               break;
> +       };
> +}
> +
> +const struct exynos_pmu_data exynos5433_pmu_data = {
> +       .pmu_config             = exynos5433_pmu_config,
> +       .pmu_init               = exynos5433_pmu_init,
> +       .powerdown_conf         = exynos5433_powerdown_conf,
> +       .powerup_conf           = exynos5433_powerup_conf,
> +};
> diff --git a/include/linux/soc/samsung/exynos-regs-pmu.h b/include/linux/soc/samsung/exynos-regs-pmu.h
> index bebdde5dccd6..93a52d133ba1 100644
> --- a/include/linux/soc/samsung/exynos-regs-pmu.h
> +++ b/include/linux/soc/samsung/exynos-regs-pmu.h
> @@ -645,7 +645,110 @@
>                                          | EXYNOS5420_KFC_USE_STANDBY_WFI3)
>
>  /* For EXYNOS5433 */
> +#define EXYNOS5433_UP_SCHEDULER                                        (0x0120)
> +#define EXYNOS5433_CENTRAL_SEQ_CONFIGURATION                   (0x0200)
> +#define EXYNOS5433_CENTRAL_SEQ_MIF_CONFIGURATION               (0x0240)
> +#define EXYNOS5433_EINT_WAKEUP_MASK                            (0x060C)
> +#define EXYNOS5433_WAKEUP_MASK                                 (0x0610)
> +#define EXYNOS5433_WAKEUP_MASK2                                        (0x0614)
> +#define EXYNOS5433_WAKEUP_MASK3                                        (0x0618)
> +#define EXYNOS5433_EINT_WAKEUP_MASK1                           (0x062C)
>  #define EXYNOS5433_USBHOST30_PHY_CONTROL                       (0x0728)
> +#define EXYNOS5433_PMU_SPARE0                                  (0x0900)
> +#define EXYNOS5433_ATLAS_CPU0_SYS_PWR_REG                      (0x1000)
> +#define EXYNOS5433_DIS_IRQ_ATLAS_CPU0_CENTRAL_SYS_PWR_REG      (0x1008)
> +#define EXYNOS5433_ATLAS_CPU1_SYS_PWR_REG                      (0x1010)
> +#define EXYNOS5433_DIS_IRQ_ATLAS_CPU1_CENTRAL_SYS_PWR_REG      (0x1018)
> +#define EXYNOS5433_ATLAS_CPU2_SYS_PWR_REG                      (0x1020)
> +#define EXYNOS5433_DIS_IRQ_ATLAS_CPU2_CENTRAL_SYS_PWR_REG      (0x1028)
> +#define EXYNOS5433_ATLAS_CPU3_SYS_PWR_REG                      (0x1030)
> +#define EXYNOS5433_DIS_IRQ_ATLAS_CPU3_CENTRAL_SYS_PWR_REG      (0x1038)
> +#define EXYNOS5433_APOLLO_CPU0_SYS_PWR_REG                     (0x1040)
> +#define EXYNOS5433_DIS_IRQ_APOLLO_CPU0_CENTRAL_SYS_PWR_REG     (0x1048)
> +#define EXYNOS5433_APOLLO_CPU1_SYS_PWR_REG                     (0x1050)
> +#define EXYNOS5433_DIS_IRQ_APOLLO_CPU1_CENTRAL_SYS_PWR_REG     (0x1058)
> +#define EXYNOS5433_APOLLO_CPU2_SYS_PWR_REG                     (0x1060)
> +#define EXYNOS5433_DIS_IRQ_APOLLO_CPU2_CENTRAL_SYS_PWR_REG     (0x1068)
> +#define EXYNOS5433_APOLLO_CPU3_SYS_PWR_REG                     (0x1070)
> +#define EXYNOS5433_DIS_IRQ_APOLLO_CPU3_CENTRAL_SYS_PWR_REG     (0x1078)
> +#define EXYNOS5433_ATLAS_NONCPU_SYS_PWR_REG                    (0x1080)
> +#define EXYNOS5433_ATLAS_L2_SYS_PWR_REG                                (0x10C0)
> +#define EXYNOS5433_APOLLO_L2_SYS_PWR_REG                       (0x10C4)
> +#define EXYNOS5433_APOLLO_NONCPU_SYS_PWR_REG                   (0x1084)
> +#define EXYNOS5433_A5IS_SYS_PWR_REG                            (0x10B0)
> +#define EXYNOS5433_DIS_IRQ_A5IS_LOCAL_SYS_PWR_REG              (0x10B4)
> +#define EXYNOS5433_DIS_IRQ_A5IS_CENTRAL_SYS_PWR_REG            (0x10B8)
> +#define EXYNOS5433_CLKSTOP_CMU_TOP_SYS_PWR_REG                 (0x1100)
> +#define EXYNOS5433_CLKRUN_CMU_TOP_SYS_PWR_REG                  (0x1104)
> +#define EXYNOS5433_RESET_CMU_TOP_SYS_PWR_REG                   (0x110C)
> +#define EXYNOS5433_RESET_CPUCLKSTOP_SYS_PWR_REG                        (0x111C)
> +#define EXYNOS5433_CLKSTOP_CMU_MIF_SYS_PWR_REG                 (0x1120)
> +#define EXYNOS5433_CLKRUN_CMU_MIF_SYS_PWR_REG                  (0x1124)
> +#define EXYNOS5433_RESET_CMU_MIF_SYS_PWR_REG                   (0x112C)
> +#define EXYNOS5433_DDRPHY_DLLLOCK_SYS_PWR_REG                  (0x1138)
> +#define EXYNOS5433_DISABLE_PLL_CMU_TOP_SYS_PWR_REG             (0x1140)
> +#define EXYNOS5433_DISABLE_PLL_AUD_PLL_SYS_PWR_REG             (0x1144)
> +#define EXYNOS5433_DISABLE_PLL_CMU_MIF_SYS_PWR_REG             (0x1160)
> +#define EXYNOS5433_TOP_BUS_SYS_PWR_REG                         (0x1180)
> +#define EXYNOS5433_TOP_RETENTION_SYS_PWR_REG                   (0x1184)
> +#define EXYNOS5433_TOP_PWR_SYS_PWR_REG                         (0x1188)
> +#define EXYNOS5433_TOP_BUS_MIF_SYS_PWR_REG                     (0x1190)
> +#define EXYNOS5433_TOP_RETENTION_MIF_SYS_PWR_REG               (0x1194)
> +#define EXYNOS5433_TOP_PWR_MIF_SYS_PWR_REG                     (0x1198)
> +#define EXYNOS5433_LOGIC_RESET_SYS_PWR_REG                     (0x11A0)
> +#define EXYNOS5433_OSCCLK_GATE_SYS_PWR_REG                     (0x11A4)
> +#define EXYNOS5433_SLEEP_RESET_SYS_PWR_REG                     (0x11A8)
> +#define EXYNOS5433_LOGIC_RESET_MIF_SYS_PWR_REG                 (0x11B0)
> +#define EXYNOS5433_OSCCLK_GATE_MIF_SYS_PWR_REG                 (0x11B4)
> +#define EXYNOS5433_SLEEP_RESET_MIF_SYS_PWR_REG                 (0x11B8)
> +#define EXYNOS5433_MEMORY_TOP_SYS_PWR_REG                      (0x11C0)
> +#define EXYNOS5433_PAD_RETENTION_LPDDR3_SYS_PWR_REG            (0x1200)
> +#define EXYNOS5433_PAD_RETENTION_JTAG_SYS_PWR_REG              (0x1208)
> +#define EXYNOS5433_PAD_RETENTION_TOP_SYS_PWR_REG               (0x1220)
> +#define EXYNOS5433_PAD_RETENTION_UART_SYS_PWR_REG              (0x1224)
> +#define EXYNOS5433_PAD_RETENTION_EBIA_SYS_PWR_REG              (0x1230)
> +#define EXYNOS5433_PAD_RETENTION_EBIB_SYS_PWR_REG              (0x1234)
> +#define EXYNOS5433_PAD_RETENTION_SPI_SYS_PWR_REG               (0x1238)
> +#define EXYNOS5433_PAD_RETENTION_MIF_SYS_PWR_REG               (0x123C)
> +#define EXYNOS5433_PAD_ISOLATION_SYS_PWR_REG                   (0x1240)
> +#define EXYNOS5433_PAD_RETENTION_USBXTI_SYS_PWR_REG            (0x1244)
> +#define EXYNOS5433_PAD_RETENTION_BOOTLDO_SYS_PWR_REG           (0x1248)
> +#define EXYNOS5433_PAD_ISOLATION_MIF_SYS_PWR_REG               (0x1250)
> +#define EXYNOS5433_PAD_RETENTION_FSYSGENIO_SYS_PWR_REG         (0x1254)
> +#define EXYNOS5433_PAD_ALV_SEL_SYS_PWR_REG                     (0x1260)
> +#define EXYNOS5433_XXTI_SYS_PWR_REG                            (0x1284)
> +#define EXYNOS5433_XXTI26_SYS_PWR_REG                          (0x1288)
> +#define EXYNOS5433_EXT_REGULATOR_SYS_PWR_REG                   (0x12C0)
> +#define EXYNOS5433_GPIO_MODE_SYS_PWR_REG                       (0x1300)
> +#define EXYNOS5433_GPIO_MODE_FSYS0_SYS_PWR_REG                 (0x1304)
> +#define EXYNOS5433_GPIO_MODE_MIF_SYS_PWR_REG                   (0x1320)
> +#define EXYNOS5433_GPIO_MODE_AUD_SYS_PWR_REG                   (0x1340)
> +#define EXYNOS5433_GSCL_SYS_PWR_REG                            (0x1400)
> +#define EXYNOS5433_CAM0_SYS_PWR_REG                            (0x1404)
> +#define EXYNOS5433_MSCL_SYS_PWR_REG                            (0x1408)
> +#define EXYNOS5433_G3D_SYS_PWR_REG                             (0x140C)
> +#define EXYNOS5433_DISP_SYS_PWR_REG                            (0x1410)
> +#define EXYNOS5433_CAM1_SYS_PWR_REG                            (0x1414)
> +#define EXYNOS5433_AUD_SYS_PWR_REG                             (0x1418)
> +#define EXYNOS5433_FSYS_SYS_PWR_REG                            (0x141C)
> +#define EXYNOS5433_BUS2_SYS_PWR_REG                            (0x1420)
> +#define EXYNOS5433_G2D_SYS_PWR_REG                             (0x1424)
> +#define EXYNOS5433_ISP0_SYS_PWR_REG                            (0x1428)
> +#define EXYNOS5433_MFC_SYS_PWR_REG                             (0x1430)
> +#define EXYNOS5433_HEVC_SYS_PWR_REG                            (0x1438)
> +#define EXYNOS5433_RESET_SLEEP_FSYS_SYS_PWR_REG                        (0x15DC)
> +#define EXYNOS5433_RESET_SLEEP_BUS2_SYS_PWR_REG                        (0x15E0)
> +#define EXYNOS5433_ATLAS_CPU0_OPTION                           (0x2008)
> +#define EXYNOS5433_CPU_OPTION(_nr)                             (EXYNOS5433_ATLAS_CPU0_OPTION + (_nr) * 0x80)
> +#define EXYNOS5433_ATLAS_CPU0_DURATION0                                (0x2010)
> +#define EXYNOS5433_CPU_DURATION(_nr)                           (EXYNOS5433_ATLAS_CPU0_DURATION0 + (_nr) * 0x80)
> +#define EXYNOS5433_ATLAS_NONCPU_OPTION                         (0x2408)
> +#define EXYNOS5433_APOLLO_NONCPU_OPTION                                (0x2428)
> +#define EXYNOS5433_ATLAS_CPUSEQUENCER_OPTION                   (0x2488)
> +#define EXYNOS5433_ATLAS_L2_OPTION                             (0x2608)
> +#define EXYNOS5433_TOP_PWR_MIF_OPTION                          (0x2CC8)
> +#define EXYNOS5433_TOP_PWR_OPTION                              (0x2C48)
> +#define EXYNOS5433_PAD_RETENTION_LPDDR3_OPTION                 (0x3008)
>  #define EXYNOS5433_PAD_RETENTION_AUD_OPTION                    (0x3028)
>  #define EXYNOS5433_PAD_RETENTION_MMC2_OPTION                   (0x30C8)
>  #define EXYNOS5433_PAD_RETENTION_TOP_OPTION                    (0x3108)
> @@ -660,5 +763,50 @@
>  #define EXYNOS5433_PAD_RETENTION_BOOTLDO_OPTION                        (0x3248)
>  #define EXYNOS5433_PAD_RETENTION_UFS_OPTION                    (0x3268)
>  #define EXYNOS5433_PAD_RETENTION_FSYSGENIO_OPTION              (0x32A8)
> +#define EXYNOS5433_PS_HOLD_CONTROL                             (0x330C)
> +#define EXYNOS5433_GSCL_OPTION                                 (0x4008)
> +#define EXYNOS5433_CAM0_OPTION                                 (0x4028)
> +#define EXYNOS5433_MSCL_OPTION                                 (0x4048)
> +#define EXYNOS5433_G3D_OPTION                                  (0x4068)
> +#define EXYNOS5433_DISP_OPTION                                 (0x4088)
> +#define EXYNOS5433_AUD_OPTION                                  (0x40C8)
> +#define EXYNOS5433_FSYS_OPTION                                 (0x40E8)
> +#define EXYNOS5433_BUS2_OPTION                                 (0x4108)
> +#define EXYNOS5433_G2D_OPTION                                  (0x4128)
> +#define EXYNOS5433_ISP_OPTION                                  (0x4148)
> +#define EXYNOS5433_MFC_OPTION                                  (0x4188)
> +#define EXYNOS5433_HEVC_OPTION                                 (0x41C8)
> +
> +/* EXYNOS5433_PMU_SPARE0 */
> +#define EXYNOS5433_EN_NONRET_RESET                             (1 << 0)

Use BIT(0) here and in other places.

Best regards,
Krzysztof

> +
> +/* EXYNOS5433_CENTRAL_SEQ_CONFIGURATION */
> +#define EXYNOS5433_CENTRALSEQ_PWR_CFG                          (0x1 << 16)
> +
> +/* EXYNOS5433_ATLAS_L2_OPTION */
> +#define EXYNOS5433_USE_DEACTIVATE_ACE                          (0x1 << 19)
> +#define EXYNOS5433_USE_DEACTIVATE_ACP                          (0x1 << 18)
> +#define EXYNOS5433_USE_AUTO_L2FLUSHREQ                         (0x1 << 17)
> +#define EXYNOS5433_USE_STANDBYWFIL2                            (0x1 << 16)
> +#define EXYNOS5433_USE_RETENTION                               (0x1 << 4)
> +
> +/* EXYNOS5433_CPU_OPTION */
> +#define EXYNOS5433_USE_SMPEN                                   (0x1 << 28)
> +#define EXYNOS5433_USE_STANDBYWFE                              (0x1 << 24)
> +#define EXYNOS5433_USE_STANDBYWFI                              (0x1 << 16)
> +
> +/* EXYNOS5433_PAD_RETENTION_*_OPTION */
> +#define EXYNOS5433_INITIATE_WAKEUP_FROM_LOWPOWER               (0x1 << 28)
> +
> +/* EXYNOS5433_CPU_DURATION */
> +#define EXYNOS5433_DUR_WAIT_RESET                              (0xF << 20)
> +#define EXYNOS5433_DUR_SCALL                                   (0xF << 4)
> +#define EXYNOS5433_DUR_SCALL_VALUE                             (0x1 << 4)
> +
> +/* EXYNOS5433_ATLAS_CPUSEQUENCER_OPTION */
> +#define EXYNOS5433_SKIP_BLK_PWR_DOWN                           (0x1 << 8)
> +
> +/* EXYNOS5433_UP_SCHEDULER */
> +#define EXYNOS5433_ENABLE_ATLAS_CPU                            (0x1 << 0)
>
>  #endif /* __LINUX_SOC_EXYNOS_REGS_PMU_H */
> --
> 1.9.1
>

^ permalink raw reply

* [PATCH 01/19] drm/fourcc: Add a function to tell if the format embeds alpha
From: Boris Brezillon @ 2018-01-09 12:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <b58c65b26e59dff7917d90846cb797a11ca09efa.1515494838.git-series.maxime.ripard@free-electrons.com>

On Tue,  9 Jan 2018 11:56:20 +0100
Maxime Ripard <maxime.ripard@free-electrons.com> 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>

Reviewed-by: 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);
> diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h
> index 6942e84b6edd..e08fc22c5f78 100644
> --- a/include/drm/drm_fourcc.h
> +++ b/include/drm/drm_fourcc.h
> @@ -69,5 +69,6 @@ int drm_format_vert_chroma_subsampling(uint32_t format);
>  int drm_format_plane_width(int width, uint32_t format, int plane);
>  int drm_format_plane_height(int height, uint32_t format, int plane);
>  const char *drm_get_format_name(uint32_t format, struct drm_format_name_buf *buf);
> +bool drm_format_has_alpha(uint32_t format);
>  
>  #endif /* __DRM_FOURCC_H__ */

^ permalink raw reply

* [PATCH 02/19] drm/atmel-hlcdc: Use the alpha format helper
From: Boris Brezillon @ 2018-01-09 12:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <22c5a25fbdd56193ece1de90dc17cfa8747e7136.1515494838.git-series.maxime.ripard@free-electrons.com>

On Tue,  9 Jan 2018 11:56:21 +0100
Maxime Ripard <maxime.ripard@free-electrons.com> wrote:

> Now that the core has a drm format helper to tell if a format embeds an
> alpha component in it, let's use it.
> 
> Cc: Boris Brezillon <boris.brezillon@free-electrons.com>

Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>

> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 20 ++----------------
>  1 file changed, 3 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> index 703c2d13603f..1a9318810a29 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> @@ -194,20 +194,6 @@ static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
>  	return 0;
>  }
>  
> -static bool atmel_hlcdc_format_embeds_alpha(u32 format)
> -{
> -	int i;
> -
> -	for (i = 0; i < sizeof(format); i++) {
> -		char tmp = (format >> (8 * i)) & 0xff;
> -
> -		if (tmp == 'A')
> -			return true;
> -	}
> -
> -	return false;
> -}
> -
>  static u32 heo_downscaling_xcoef[] = {
>  	0x11343311,
>  	0x000000f7,
> @@ -395,7 +381,7 @@ atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
>  		cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
>  		       ATMEL_HLCDC_LAYER_ITER;
>  
> -		if (atmel_hlcdc_format_embeds_alpha(format))
> +		if (drm_format_has_alpha(format))
>  			cfg |= ATMEL_HLCDC_LAYER_LAEN;
>  		else
>  			cfg |= ATMEL_HLCDC_LAYER_GAEN |
> @@ -566,7 +552,7 @@ atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
>  		ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s);
>  
>  		if (!ovl_s->fb ||
> -		    atmel_hlcdc_format_embeds_alpha(ovl_s->fb->format->format) ||
> +		    drm_format_has_alpha(ovl_s->fb->format->format) ||
>  		    ovl_state->alpha != 255)
>  			continue;
>  
> @@ -769,7 +755,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
>  
>  	if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
>  	    (!desc->layout.memsize ||
> -	     atmel_hlcdc_format_embeds_alpha(state->base.fb->format->format)))
> +	     drm_format_has_alpha(state->base.fb->format->format)))
>  		return -EINVAL;
>  
>  	if (state->crtc_x < 0 || state->crtc_y < 0)

^ permalink raw reply

* [PATCH 01/19] drm/fourcc: Add a function to tell if the format embeds alpha
From: Laurent Pinchart @ 2018-01-09 12:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <b58c65b26e59dff7917d90846cb797a11ca09efa.1515494838.git-series.maxime.ripard@free-electrons.com>

Hi Maxime,

Thank you for the patch.

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;
}

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

> diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h
> index 6942e84b6edd..e08fc22c5f78 100644
> --- a/include/drm/drm_fourcc.h
> +++ b/include/drm/drm_fourcc.h
> @@ -69,5 +69,6 @@ int drm_format_vert_chroma_subsampling(uint32_t format);
>  int drm_format_plane_width(int width, uint32_t format, int plane);
>  int drm_format_plane_height(int height, uint32_t format, int plane);
>  const char *drm_get_format_name(uint32_t format, struct drm_format_name_buf
> *buf);
> +bool drm_format_has_alpha(uint32_t format);
> 
>  #endif /* __DRM_FOURCC_H__ */

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* [PATCH 06/19] drm/blend: Add a generic alpha property
From: Boris Brezillon @ 2018-01-09 12:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5c765fc730d75cb362dc37bcdb3b3aeacc7bdb30.1515494838.git-series.maxime.ripard@free-electrons.com>

On Tue,  9 Jan 2018 11:56:25 +0100
Maxime Ripard <maxime.ripard@free-electrons.com> 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>

Reviewed-by: 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
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index c2da5585e201..ade18cf62c89 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -749,6 +749,8 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane,
>  		state->src_w = val;
>  	} else if (property == config->prop_src_h) {
>  		state->src_h = val;
> +	} else if (property == plane->alpha_property) {
> +		state->alpha = val;
>  	} else if (property == plane->rotation_property) {
>  		if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK))
>  			return -EINVAL;
> @@ -810,6 +812,8 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
>  		*val = state->src_w;
>  	} else if (property == config->prop_src_h) {
>  		*val = state->src_h;
> +	} else if (property == plane->alpha_property) {
> +		*val = state->alpha;
>  	} else if (property == plane->rotation_property) {
>  		*val = state->rotation;
>  	} else if (property == plane->zpos_property) {
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 71d712f1b56a..018993df4c18 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -3372,6 +3372,7 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
>  
>  	if (plane->state) {
>  		plane->state->plane = plane;
> +		plane->state->alpha = 255;
>  		plane->state->rotation = DRM_MODE_ROTATE_0;
>  	}
>  }
> diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c
> index 2e5e089dd912..8eea2a8af458 100644
> --- a/drivers/gpu/drm/drm_blend.c
> +++ b/drivers/gpu/drm/drm_blend.c
> @@ -104,6 +104,38 @@
>   */
>  
>  /**
> + * drm_plane_create_alpha_property - create a new alpha property
> + * @plane: drm plane
> + * @alpha: initial value of alpha, from 0 (transparent) to 255 (opaque)
> + *
> + * 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.
> + *
> + * 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);
> +	if (!prop)
> +		return -ENOMEM;
> +
> +	drm_object_attach_property(&plane->base, prop, alpha);
> +	plane->alpha_property = prop;
> +
> +	if (plane->state)
> +		plane->state->alpha = alpha;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_plane_create_alpha_property);
> +
> +/**
>   * drm_plane_create_rotation_property - create a new rotation property
>   * @plane: drm plane
>   * @rotation: initial value of the rotation property
> diff --git a/include/drm/drm_blend.h b/include/drm/drm_blend.h
> index 17606026590b..5979a8fce453 100644
> --- a/include/drm/drm_blend.h
> +++ b/include/drm/drm_blend.h
> @@ -36,6 +36,7 @@ static inline bool drm_rotation_90_or_270(unsigned int rotation)
>  	return rotation & (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270);
>  }
>  
> +int drm_plane_create_alpha_property(struct drm_plane *plane, u8 alpha);
>  int drm_plane_create_rotation_property(struct drm_plane *plane,
>  				       unsigned int rotation,
>  				       unsigned int supported_rotations);
> diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
> index 571615079230..a5e26064b132 100644
> --- a/include/drm/drm_plane.h
> +++ b/include/drm/drm_plane.h
> @@ -42,6 +42,7 @@ struct drm_modeset_acquire_ctx;
>   *	plane (in 16.16)
>   * @src_w: width of visible portion of plane (in 16.16)
>   * @src_h: height of visible portion of plane (in 16.16)
> + * @alpha: opacity of the plane
>   * @rotation: rotation of the plane
>   * @zpos: priority of the given plane on crtc (optional)
>   *	Note that multiple active planes on the same crtc can have an identical
> @@ -105,6 +106,9 @@ struct drm_plane_state {
>  	uint32_t src_x, src_y;
>  	uint32_t src_h, src_w;
>  
> +	/* Plane opacity */
> +	u8 alpha;
> +
>  	/* Plane rotation */
>  	unsigned int rotation;
>  
> @@ -481,6 +485,7 @@ enum drm_plane_type {
>   * @funcs: helper functions
>   * @properties: property tracking for this plane
>   * @type: type of plane (overlay, primary, cursor)
> + * @alpha_property: alpha property for this plane
>   * @zpos_property: zpos property for this plane
>   * @rotation_property: rotation property for this plane
>   * @helper_private: mid-layer private data
> @@ -546,6 +551,7 @@ struct drm_plane {
>  	 */
>  	struct drm_plane_state *state;
>  
> +	struct drm_property *alpha_property;
>  	struct drm_property *zpos_property;
>  	struct drm_property *rotation_property;
>  };

^ permalink raw reply

* [PATCH 07/19] drm/atmel-hclcdc: Convert to the new generic alpha property
From: Boris Brezillon @ 2018-01-09 12:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <87b4f265eaade03c5afdbd55a46851bbe4d8d2fe.1515494838.git-series.maxime.ripard@free-electrons.com>

On Tue,  9 Jan 2018 11:56:26 +0100
Maxime Ripard <maxime.ripard@free-electrons.com> wrote:

> Now that we have support for per-plane alpha in the core, let's use it.
> 
> Cc: Boris Brezillon <boris.brezillon@free-electrons.com>

Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>

> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h    | 13 +---
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 89 ++----------------
>  2 files changed, 14 insertions(+), 88 deletions(-)
> 
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> index 6833ee253cfa..704cac6399eb 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
> @@ -298,7 +298,6 @@ struct atmel_hlcdc_layer {
>  struct atmel_hlcdc_plane {
>  	struct drm_plane base;
>  	struct atmel_hlcdc_layer layer;
> -	struct atmel_hlcdc_plane_properties *properties;
>  };
>  
>  static inline struct atmel_hlcdc_plane *
> @@ -345,18 +344,6 @@ struct atmel_hlcdc_dc_desc {
>  };
>  
>  /**
> - * Atmel HLCDC Plane properties.
> - *
> - * This structure stores plane property definitions.
> - *
> - * @alpha: alpha blending (or transparency) property
> - * @rotation: rotation property
> - */
> -struct atmel_hlcdc_plane_properties {
> -	struct drm_property *alpha;
> -};
> -
> -/**
>   * Atmel HLCDC Display Controller.
>   *
>   * @desc: HLCDC Display Controller description
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> index 1a9318810a29..dbc508889e87 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
> @@ -31,7 +31,6 @@
>   * @src_y: y buffer position
>   * @src_w: buffer width
>   * @src_h: buffer height
> - * @alpha: alpha blending of the plane
>   * @disc_x: x discard position
>   * @disc_y: y discard position
>   * @disc_w: discard width
> @@ -54,8 +53,6 @@ struct atmel_hlcdc_plane_state {
>  	uint32_t src_w;
>  	uint32_t src_h;
>  
> -	u8 alpha;
> -
>  	int disc_x;
>  	int disc_y;
>  	int disc_w;
> @@ -385,7 +382,7 @@ atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
>  			cfg |= ATMEL_HLCDC_LAYER_LAEN;
>  		else
>  			cfg |= ATMEL_HLCDC_LAYER_GAEN |
> -			       ATMEL_HLCDC_LAYER_GA(state->alpha);
> +			       ATMEL_HLCDC_LAYER_GA(state->base.alpha);
>  	}
>  
>  	if (state->disc_h && state->disc_w)
> @@ -553,7 +550,7 @@ atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
>  
>  		if (!ovl_s->fb ||
>  		    drm_format_has_alpha(ovl_s->fb->format->format) ||
> -		    ovl_state->alpha != 255)
> +		    ovl_s->alpha != 255)
>  			continue;
>  
>  		/* TODO: implement a smarter hidden area detection */
> @@ -829,51 +826,18 @@ static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
>  	drm_plane_cleanup(p);
>  }
>  
> -static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p,
> -						 struct drm_plane_state *s,
> -						 struct drm_property *property,
> -						 uint64_t val)
> -{
> -	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
> -	struct atmel_hlcdc_plane_properties *props = plane->properties;
> -	struct atmel_hlcdc_plane_state *state =
> -			drm_plane_state_to_atmel_hlcdc_plane_state(s);
> -
> -	if (property == props->alpha)
> -		state->alpha = val;
> -	else
> -		return -EINVAL;
> -
> -	return 0;
> -}
> -
> -static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p,
> -					const struct drm_plane_state *s,
> -					struct drm_property *property,
> -					uint64_t *val)
> -{
> -	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
> -	struct atmel_hlcdc_plane_properties *props = plane->properties;
> -	const struct atmel_hlcdc_plane_state *state =
> -		container_of(s, const struct atmel_hlcdc_plane_state, base);
> -
> -	if (property == props->alpha)
> -		*val = state->alpha;
> -	else
> -		return -EINVAL;
> -
> -	return 0;
> -}
> -
> -static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
> -				struct atmel_hlcdc_plane_properties *props)
> +static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane)
>  {
>  	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
>  
>  	if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
> -	    desc->type == ATMEL_HLCDC_CURSOR_LAYER)
> -		drm_object_attach_property(&plane->base.base,
> -					   props->alpha, 255);
> +	    desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
> +		int ret;
> +
> +		ret = drm_plane_create_alpha_property(&plane->base, 255);
> +		if (ret)
> +			return ret;
> +	}
>  
>  	if (desc->layout.xstride && desc->layout.pstride) {
>  		int ret;
> @@ -988,8 +952,8 @@ static void atmel_hlcdc_plane_reset(struct drm_plane *p)
>  			return;
>  		}
>  
> -		state->alpha = 255;
>  		p->state = &state->base;
> +		p->state->alpha = 255;
>  		p->state->plane = p;
>  	}
>  }
> @@ -1042,13 +1006,10 @@ static const struct drm_plane_funcs layer_plane_funcs = {
>  	.reset = atmel_hlcdc_plane_reset,
>  	.atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,
>  	.atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,
> -	.atomic_set_property = atmel_hlcdc_plane_atomic_set_property,
> -	.atomic_get_property = atmel_hlcdc_plane_atomic_get_property,
>  };
>  
>  static int atmel_hlcdc_plane_create(struct drm_device *dev,
> -				    const struct atmel_hlcdc_layer_desc *desc,
> -				    struct atmel_hlcdc_plane_properties *props)
> +				    const struct atmel_hlcdc_layer_desc *desc)
>  {
>  	struct atmel_hlcdc_dc *dc = dev->dev_private;
>  	struct atmel_hlcdc_plane *plane;
> @@ -1060,7 +1021,6 @@ static int atmel_hlcdc_plane_create(struct drm_device *dev,
>  		return -ENOMEM;
>  
>  	atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
> -	plane->properties = props;
>  
>  	if (desc->type == ATMEL_HLCDC_BASE_LAYER)
>  		type = DRM_PLANE_TYPE_PRIMARY;
> @@ -1081,7 +1041,7 @@ static int atmel_hlcdc_plane_create(struct drm_device *dev,
>  			     &atmel_hlcdc_layer_plane_helper_funcs);
>  
>  	/* Set default property values*/
> -	ret = atmel_hlcdc_plane_init_properties(plane, props);
> +	ret = atmel_hlcdc_plane_init_properties(plane);
>  	if (ret)
>  		return ret;
>  
> @@ -1090,34 +1050,13 @@ static int atmel_hlcdc_plane_create(struct drm_device *dev,
>  	return 0;
>  }
>  
> -static struct atmel_hlcdc_plane_properties *
> -atmel_hlcdc_plane_create_properties(struct drm_device *dev)
> -{
> -	struct atmel_hlcdc_plane_properties *props;
> -
> -	props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
> -	if (!props)
> -		return ERR_PTR(-ENOMEM);
> -
> -	props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
> -	if (!props->alpha)
> -		return ERR_PTR(-ENOMEM);
> -
> -	return props;
> -}
> -
>  int atmel_hlcdc_create_planes(struct drm_device *dev)
>  {
>  	struct atmel_hlcdc_dc *dc = dev->dev_private;
> -	struct atmel_hlcdc_plane_properties *props;
>  	const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
>  	int nlayers = dc->desc->nlayers;
>  	int i, ret;
>  
> -	props = atmel_hlcdc_plane_create_properties(dev);
> -	if (IS_ERR(props))
> -		return PTR_ERR(props);
> -
>  	dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,
>  				sizeof(struct atmel_hlcdc_dma_channel_dscr),
>  				sizeof(u64), 0);
> @@ -1130,7 +1069,7 @@ int atmel_hlcdc_create_planes(struct drm_device *dev)
>  		    descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)
>  			continue;
>  
> -		ret = atmel_hlcdc_plane_create(dev, &descs[i], props);
> +		ret = atmel_hlcdc_plane_create(dev, &descs[i]);
>  		if (ret)
>  			return ret;
>  	}

^ permalink raw reply

* [PATCH 06/19] drm/blend: Add a generic alpha property
From: Daniel Vetter @ 2018-01-09 12:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5c765fc730d75cb362dc37bcdb3b3aeacc7bdb30.1515494838.git-series.maxime.ripard@free-electrons.com>

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? Is encoding a fixed 0-255 range really the
best idea?

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).
-Daniel

> ---
>  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
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index c2da5585e201..ade18cf62c89 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -749,6 +749,8 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane,
>  		state->src_w = val;
>  	} else if (property == config->prop_src_h) {
>  		state->src_h = val;
> +	} else if (property == plane->alpha_property) {
> +		state->alpha = val;
>  	} else if (property == plane->rotation_property) {
>  		if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK))
>  			return -EINVAL;
> @@ -810,6 +812,8 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
>  		*val = state->src_w;
>  	} else if (property == config->prop_src_h) {
>  		*val = state->src_h;
> +	} else if (property == plane->alpha_property) {
> +		*val = state->alpha;
>  	} else if (property == plane->rotation_property) {
>  		*val = state->rotation;
>  	} else if (property == plane->zpos_property) {
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 71d712f1b56a..018993df4c18 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -3372,6 +3372,7 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
>  
>  	if (plane->state) {
>  		plane->state->plane = plane;
> +		plane->state->alpha = 255;
>  		plane->state->rotation = DRM_MODE_ROTATE_0;
>  	}
>  }
> diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c
> index 2e5e089dd912..8eea2a8af458 100644
> --- a/drivers/gpu/drm/drm_blend.c
> +++ b/drivers/gpu/drm/drm_blend.c
> @@ -104,6 +104,38 @@
>   */
>  
>  /**
> + * drm_plane_create_alpha_property - create a new alpha property
> + * @plane: drm plane
> + * @alpha: initial value of alpha, from 0 (transparent) to 255 (opaque)
> + *
> + * 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.
> + *
> + * 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);
> +	if (!prop)
> +		return -ENOMEM;
> +
> +	drm_object_attach_property(&plane->base, prop, alpha);
> +	plane->alpha_property = prop;
> +
> +	if (plane->state)
> +		plane->state->alpha = alpha;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_plane_create_alpha_property);
> +
> +/**
>   * drm_plane_create_rotation_property - create a new rotation property
>   * @plane: drm plane
>   * @rotation: initial value of the rotation property
> diff --git a/include/drm/drm_blend.h b/include/drm/drm_blend.h
> index 17606026590b..5979a8fce453 100644
> --- a/include/drm/drm_blend.h
> +++ b/include/drm/drm_blend.h
> @@ -36,6 +36,7 @@ static inline bool drm_rotation_90_or_270(unsigned int rotation)
>  	return rotation & (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270);
>  }
>  
> +int drm_plane_create_alpha_property(struct drm_plane *plane, u8 alpha);
>  int drm_plane_create_rotation_property(struct drm_plane *plane,
>  				       unsigned int rotation,
>  				       unsigned int supported_rotations);
> diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
> index 571615079230..a5e26064b132 100644
> --- a/include/drm/drm_plane.h
> +++ b/include/drm/drm_plane.h
> @@ -42,6 +42,7 @@ struct drm_modeset_acquire_ctx;
>   *	plane (in 16.16)
>   * @src_w: width of visible portion of plane (in 16.16)
>   * @src_h: height of visible portion of plane (in 16.16)
> + * @alpha: opacity of the plane
>   * @rotation: rotation of the plane
>   * @zpos: priority of the given plane on crtc (optional)
>   *	Note that multiple active planes on the same crtc can have an identical
> @@ -105,6 +106,9 @@ struct drm_plane_state {
>  	uint32_t src_x, src_y;
>  	uint32_t src_h, src_w;
>  
> +	/* Plane opacity */
> +	u8 alpha;
> +
>  	/* Plane rotation */
>  	unsigned int rotation;
>  
> @@ -481,6 +485,7 @@ enum drm_plane_type {
>   * @funcs: helper functions
>   * @properties: property tracking for this plane
>   * @type: type of plane (overlay, primary, cursor)
> + * @alpha_property: alpha property for this plane
>   * @zpos_property: zpos property for this plane
>   * @rotation_property: rotation property for this plane
>   * @helper_private: mid-layer private data
> @@ -546,6 +551,7 @@ struct drm_plane {
>  	 */
>  	struct drm_plane_state *state;
>  
> +	struct drm_property *alpha_property;
>  	struct drm_property *zpos_property;
>  	struct drm_property *rotation_property;
>  };
> -- 
> git-series 0.9.1
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply

* [PATCH 3/9] soc: samsung: pmu: Add the PMU data of exynos5433 to support low-power state
From: Krzysztof Kozlowski @ 2018-01-09 12:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515484746-10656-4-git-send-email-cw00.choi@samsung.com>

On Tue, Jan 9, 2018 at 8:59 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> This patch adds the PMU (Power Management Unit) data of exynos5433 SoC
> in order to support the various power modes. Each power mode has
> the different value for reducing the power-consumption.
>
> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> ---
>  arch/arm/mach-exynos/common.h               |   2 -
>  drivers/soc/samsung/Makefile                |   3 +-
>  drivers/soc/samsung/exynos-pmu.c            |   1 +
>  drivers/soc/samsung/exynos-pmu.h            |   2 +
>  drivers/soc/samsung/exynos5433-pmu.c        | 286 ++++++++++++++++++++++++++++
>  include/linux/soc/samsung/exynos-regs-pmu.h | 148 ++++++++++++++
>  6 files changed, 439 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/soc/samsung/exynos5433-pmu.c
>
> diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
> index 098f84a149a3..afbc143a3d5d 100644
> --- a/arch/arm/mach-exynos/common.h
> +++ b/arch/arm/mach-exynos/common.h
> @@ -125,8 +125,6 @@ enum {
>  void exynos_set_boot_flag(unsigned int cpu, unsigned int mode);
>  void exynos_clear_boot_flag(unsigned int cpu, unsigned int mode);
>
> -extern u32 exynos_get_eint_wake_mask(void);
> -
>  #ifdef CONFIG_PM_SLEEP
>  extern void __init exynos_pm_init(void);
>  #else
> diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
> index 29f294baac6e..d2e637339a45 100644
> --- a/drivers/soc/samsung/Makefile
> +++ b/drivers/soc/samsung/Makefile
> @@ -2,5 +2,6 @@
>  obj-$(CONFIG_EXYNOS_PMU)       += exynos-pmu.o
>
>  obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS)   += exynos3250-pmu.o exynos4-pmu.o \
> -                                       exynos5250-pmu.o exynos5420-pmu.o
> +                                       exynos5250-pmu.o exynos5420-pmu.o \
> +                                       exynos5433-pmu.o

... ah, I forgot. No. Exynos5433 is not ARMv7.

 Best regards,
Krzysztof

^ permalink raw reply

* [PATCH 06/19] drm/blend: Add a generic alpha property
From: Laurent Pinchart @ 2018-01-09 12:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5c765fc730d75cb362dc37bcdb3b3aeacc7bdb30.1515494838.git-series.maxime.ripard@free-electrons.com>

Hi Maxime,

Thank you for the patch.

On Tuesday, 9 January 2018 12:56:25 EET 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>
> ---
>  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.

> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index c2da5585e201..ade18cf62c89 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -749,6 +749,8 @@ static int drm_atomic_plane_set_property(struct
> drm_plane *plane, state->src_w = val;
>  	} else if (property == config->prop_src_h) {
>  		state->src_h = val;
> +	} else if (property == plane->alpha_property) {
> +		state->alpha = val;
>  	} else if (property == plane->rotation_property) {
>  		if (!is_power_of_2(val & DRM_MODE_ROTATE_MASK))
>  			return -EINVAL;
> @@ -810,6 +812,8 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
>  		*val = state->src_w;
>  	} else if (property == config->prop_src_h) {
>  		*val = state->src_h;
> +	} else if (property == plane->alpha_property) {
> +		*val = state->alpha;
>  	} else if (property == plane->rotation_property) {
>  		*val = state->rotation;
>  	} else if (property == plane->zpos_property) {
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c
> b/drivers/gpu/drm/drm_atomic_helper.c index 71d712f1b56a..018993df4c18
> 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -3372,6 +3372,7 @@ void drm_atomic_helper_plane_reset(struct drm_plane
> *plane)
> 
>  	if (plane->state) {
>  		plane->state->plane = plane;
> +		plane->state->alpha = 255;

If you keep the ability to select an initial value other than fully opaque 
(see my comment below about that) you should reset to that value instead of 
hardcoding 255.

>  		plane->state->rotation = DRM_MODE_ROTATE_0;
>  	}
>  }
> diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c
> index 2e5e089dd912..8eea2a8af458 100644
> --- a/drivers/gpu/drm/drm_blend.c
> +++ b/drivers/gpu/drm/drm_blend.c
> @@ -104,6 +104,38 @@
>   */
> 
>  /**
> + * 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 ?

> + * 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 
?

> + * 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 ?

> +	if (!prop)
> +		return -ENOMEM;
> +
> +	drm_object_attach_property(&plane->base, prop, alpha);
> +	plane->alpha_property = prop;
> +
> +	if (plane->state)
> +		plane->state->alpha = alpha;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_plane_create_alpha_property);
> +
> +/**
>   * drm_plane_create_rotation_property - create a new rotation property
>   * @plane: drm plane
>   * @rotation: initial value of the rotation property
> diff --git a/include/drm/drm_blend.h b/include/drm/drm_blend.h
> index 17606026590b..5979a8fce453 100644
> --- a/include/drm/drm_blend.h
> +++ b/include/drm/drm_blend.h
> @@ -36,6 +36,7 @@ static inline bool drm_rotation_90_or_270(unsigned int
> rotation) return rotation & (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270);
>  }
> 
> +int drm_plane_create_alpha_property(struct drm_plane *plane, u8 alpha);
>  int drm_plane_create_rotation_property(struct drm_plane *plane,
>  				       unsigned int rotation,
>  				       unsigned int supported_rotations);
> diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
> index 571615079230..a5e26064b132 100644
> --- a/include/drm/drm_plane.h
> +++ b/include/drm/drm_plane.h
> @@ -42,6 +42,7 @@ struct drm_modeset_acquire_ctx;
>   *	plane (in 16.16)
>   * @src_w: width of visible portion of plane (in 16.16)
>   * @src_h: height of visible portion of plane (in 16.16)
> + * @alpha: opacity of the plane
>   * @rotation: rotation of the plane
>   * @zpos: priority of the given plane on crtc (optional)
>   *	Note that multiple active planes on the same crtc can have an identical
> @@ -105,6 +106,9 @@ struct drm_plane_state {
>  	uint32_t src_x, src_y;
>  	uint32_t src_h, src_w;
> 
> +	/* Plane opacity */
> +	u8 alpha;
> +
>  	/* Plane rotation */
>  	unsigned int rotation;
> 
> @@ -481,6 +485,7 @@ enum drm_plane_type {
>   * @funcs: helper functions
>   * @properties: property tracking for this plane
>   * @type: type of plane (overlay, primary, cursor)
> + * @alpha_property: alpha property for this plane
>   * @zpos_property: zpos property for this plane
>   * @rotation_property: rotation property for this plane
>   * @helper_private: mid-layer private data
> @@ -546,6 +551,7 @@ struct drm_plane {
>  	 */
>  	struct drm_plane_state *state;
> 
> +	struct drm_property *alpha_property;
>  	struct drm_property *zpos_property;
>  	struct drm_property *rotation_property;
>  };

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* [PATCH 05/19] drm/vc4: Use the alpha format helper
From: Daniel Vetter @ 2018-01-09 12:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <78890e262e8394b2319f15c11de3282a2a2a2efd.1515494838.git-series.maxime.ripard@free-electrons.com>

On Tue, Jan 09, 2018 at 11:56:24AM +0100, Maxime Ripard wrote:
> Now that the core has a drm format helper to tell if a format embeds an
> alpha component in it, let's use it.
> 
> Cc: Eric Anholt <eric@anholt.net>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

On patches 1-5:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> ---
>  drivers/gpu/drm/vc4/vc4_plane.c | 19 +++++++++----------
>  1 file changed, 9 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
> index 423a23ed8fc2..2c0e25128dcd 100644
> --- a/drivers/gpu/drm/vc4/vc4_plane.c
> +++ b/drivers/gpu/drm/vc4/vc4_plane.c
> @@ -85,40 +85,39 @@ static const struct hvs_format {
>  	u32 drm; /* DRM_FORMAT_* */
>  	u32 hvs; /* HVS_FORMAT_* */
>  	u32 pixel_order;
> -	bool has_alpha;
>  	bool flip_cbcr;
>  } hvs_formats[] = {
>  	{
>  		.drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
> -		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
> +		.pixel_order = HVS_PIXEL_ORDER_ABGR,
>  	},
>  	{
>  		.drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
> -		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
> +		.pixel_order = HVS_PIXEL_ORDER_ABGR,
>  	},
>  	{
>  		.drm = DRM_FORMAT_ABGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
> -		.pixel_order = HVS_PIXEL_ORDER_ARGB, .has_alpha = true,
> +		.pixel_order = HVS_PIXEL_ORDER_ARGB,
>  	},
>  	{
>  		.drm = DRM_FORMAT_XBGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
> -		.pixel_order = HVS_PIXEL_ORDER_ARGB, .has_alpha = false,
> +		.pixel_order = HVS_PIXEL_ORDER_ARGB,
>  	},
>  	{
>  		.drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565,
> -		.pixel_order = HVS_PIXEL_ORDER_XRGB, .has_alpha = false,
> +		.pixel_order = HVS_PIXEL_ORDER_XRGB,
>  	},
>  	{
>  		.drm = DRM_FORMAT_BGR565, .hvs = HVS_PIXEL_FORMAT_RGB565,
> -		.pixel_order = HVS_PIXEL_ORDER_XBGR, .has_alpha = false,
> +		.pixel_order = HVS_PIXEL_ORDER_XBGR,
>  	},
>  	{
>  		.drm = DRM_FORMAT_ARGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
> -		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
> +		.pixel_order = HVS_PIXEL_ORDER_ABGR,
>  	},
>  	{
>  		.drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
> -		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
> +		.pixel_order = HVS_PIXEL_ORDER_ABGR,
>  	},
>  	{
>  		.drm = DRM_FORMAT_YUV422,
> @@ -601,7 +600,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
>  	/* Position Word 2: Source Image Size, Alpha Mode */
>  	vc4_state->pos2_offset = vc4_state->dlist_count;
>  	vc4_dlist_write(vc4_state,
> -			VC4_SET_FIELD(format->has_alpha ?
> +			VC4_SET_FIELD(drm_format_has_alpha(format->drm) ?
>  				      SCALER_POS2_ALPHA_MODE_PIPELINE :
>  				      SCALER_POS2_ALPHA_MODE_FIXED,
>  				      SCALER_POS2_ALPHA_MODE) |
> -- 
> git-series 0.9.1
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply

* [PATCH v5 01/44] dt-bindings: clock: Add new bindings for TI Davinci PLL clocks
From: Sekhar Nori @ 2018-01-09 12:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <22409e49-5c14-4068-b137-7535afaf90d7@lechnology.com>

On Monday 08 January 2018 09:59 PM, David Lechner wrote:
> On 01/08/2018 08:00 AM, Sekhar Nori wrote:
>> On Monday 08 January 2018 07:47 AM, David Lechner wrote:
>>> This adds a new binding for the PLL IP blocks in the mach-davinci family
>>> of processors. Currently, only the SYSCLKn and AUXCLK outputs are
>>> needed,
>>> but in the future additional child nodes could be added for OBSCLK and
>>> BPDIV.
>>>
>>> Note: Although these PLL controllers are very similar to the TI Keystone
>>> SoCs, we are not re-using those bindings. The Keystone bindings use a
>>> legacy one-node-per-clock binding. Furthermore, the mach-davinici SoCs
>>
>> Not sure what is meant by "legacy one-node-per-clock binding"
> 
> It's a term I picked up from of_clk_detect_critical()
> 
> ?* Do not use this function. It exists only for legacy Device Tree
> ?* bindings, such as the one-clock-per-node style that are outdated.
> ?* Those bindings typically put all clock data into .dts and the Linux
> ?* driver has no clock data, thus making it impossible to set this flag
> ?* correctly from the driver. Only those drivers may call
> ?* of_clk_detect_critical from their setup functions.

Okay, I still don't understand the outdated style. I looked at clocks
defined in arch/arm/boot/dts/stih407-clock.dtsi which is the only file
that uses clock-critical and don't particularly see anything wrong with
the way clocks are defined there.

Anyway, I guess we digress. As long as this patch series is not using
the "legacy style", we are good :)

>>> have a slightly different PLL register layout and a number of quirks
>>> that
>>> can't be handled by the existing bindings, so the keystone bindings
>>> could
>>> not be used as-is anyway.
>>
>> Right, I think different register layout between the processors is the
>> main reason for a new driver. This should be sufficient reason IMO.
>>
>>>
>>> Signed-off-by: David Lechner <david@lechnology.com>
>>> ---
>>> ? .../devicetree/bindings/clock/ti/davinci/pll.txt?? | 47
>>> ++++++++++++++++++++++
>>> ? 1 file changed, 47 insertions(+)
>>> ? create mode 100644
>>> Documentation/devicetree/bindings/clock/ti/davinci/pll.txt
>>>
>>> diff --git
>>> a/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt
>>> b/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt
>>> new file mode 100644
>>> index 0000000..99bf5da
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt
>>> @@ -0,0 +1,47 @@
>>> +Binding for TI DaVinci PLL Controllers
>>> +
>>> +The PLL provides clocks to most of the components on the SoC. In
>>> addition
>>> +to the PLL itself, this controller also contains bypasses, gates,
>>> dividers,
>>> +an multiplexers for various clock signals.
>>> +
>>> +Required properties:
>>> +- compatible: shall be one of:
>>> +??? - "ti,da850-pll0" for PLL0 on DA850/OMAP-L138/AM18XX
>>> +??? - "ti,da850-pll1" for PLL1 on DA850/OMAP-L138/AM18XX
>>
>> These PLLs are same IP so they should use the same compatible. You can
>> initialize both PLLs for DA850 based on the same compatible.
>>
> 
> But they are not exactly the same. For example, PLL0 has 7 PLLDIV clocks
> while
> PLL1 only has 3. PLL0 has PREDIV while PLL1 does not. PLL0 has certain
> SYSCLKs
> that are fixed-ratio but PLL1 does not have any of these. There are even
> more
> differences, but these are the ones we are actually using.

We need each element of the PLLC to be modeled individually as a clock
node. That is, PLL should only model the multiplier, the dividers
including post and prediv should be modeled as divider clocks (hopefully
being able to use the clk-divider.c library). The sysclks can be
fixed-factor-clock type clocks.

Without this flexible mechanism, we cannot (at least later) model things
like DIV4.5 clock which is the only clock which derives from the output
of PLL multiplier before the post divider is applied.

Since with DT there are are no retakes, we need to get this right the
first time and modifying later will not be an option.

> 
> So, if we use the same compatible, we either have to come up with device
> tree
> bindings to describe all of this (yuck) or I suppose we can look at the
> REVID
> register to electronically determine exactly what we have. I went with the
> simpler option of just creating two different compatible strings.

Thanks,
Sekhar

^ permalink raw reply

* [PATCH 08/19] drm/rcar-du: Convert to the new generic alpha property
From: Laurent Pinchart @ 2018-01-09 12:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <f29de1e6964c393afaa21564d73425139a546672.1515494838.git-series.maxime.ripard@free-electrons.com>

Hi Maxime,

Thank you for the patch.

On Tuesday, 9 January 2018 12:56:27 EET Maxime Ripard wrote:
> Now that we have support for per-plane alpha in the core, let's use it.
> 
> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

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

> ---
>  drivers/gpu/drm/rcar-du/rcar_du_drv.h   |  1 +-
>  drivers/gpu/drm/rcar-du/rcar_du_kms.c   |  5 +---
>  drivers/gpu/drm/rcar-du/rcar_du_plane.c | 15 +++------
>  drivers/gpu/drm/rcar-du/rcar_du_plane.h |  2 +-
>  drivers/gpu/drm/rcar-du/rcar_du_vsp.c   | 42 ++------------------------
>  drivers/gpu/drm/rcar-du/rcar_du_vsp.h   |  2 +-
>  6 files changed, 9 insertions(+), 58 deletions(-)
> 
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
> b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index f8cd79488ece..aff04adaae53
> 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
> @@ -89,7 +89,6 @@ struct rcar_du_device {
>  	struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS];
> 
>  	struct {
> -		struct drm_property *alpha;
>  		struct drm_property *colorkey;
>  	} props;
> 
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 566d1a948c8f..e1b5a7b460cc
> 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
> @@ -417,11 +417,6 @@ static int rcar_du_encoders_init(struct rcar_du_device
> *rcdu)
> 
>  static int rcar_du_properties_init(struct rcar_du_device *rcdu)
>  {
> -	rcdu->props.alpha =
> -		drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
> -	if (rcdu->props.alpha == NULL)
> -		return -ENOMEM;
> -
>  	/*
>  	 * The color key is expressed as an RGB888 triplet stored in a 32-bit
>  	 * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0)
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 61833cc1c699..5b34e8092c8b
> 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> @@ -423,7 +423,7 @@ static void rcar_du_plane_setup_mode(struct
> rcar_du_group *rgrp, rcar_du_plane_write(rgrp, index, PnALPHAR,
> PnALPHAR_ABIT_0);
>  	else
>  		rcar_du_plane_write(rgrp, index, PnALPHAR,
> -				    PnALPHAR_ABIT_X | state->alpha);
> +				    PnALPHAR_ABIT_X | state->state.alpha);
> 
>  	pnmr = PnMR_BM_MD | state->format->pnmr;
> 
> @@ -667,11 +667,11 @@ static void rcar_du_plane_reset(struct drm_plane
> *plane)
> 
>  	state->hwindex = -1;
>  	state->source = RCAR_DU_PLANE_MEMORY;
> -	state->alpha = 255;
>  	state->colorkey = RCAR_DU_COLORKEY_NONE;
>  	state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
> 
>  	plane->state = &state->state;
> +	plane->state->alpha = 255;
>  	plane->state->plane = plane;
>  }
> 
> @@ -683,9 +683,7 @@ static int rcar_du_plane_atomic_set_property(struct
> drm_plane *plane, struct rcar_du_plane_state *rstate =
> to_rcar_plane_state(state); struct rcar_du_device *rcdu =
> to_rcar_plane(plane)->group->dev;
> 
> -	if (property == rcdu->props.alpha)
> -		rstate->alpha = val;
> -	else if (property == rcdu->props.colorkey)
> +	if (property == rcdu->props.colorkey)
>  		rstate->colorkey = val;
>  	else
>  		return -EINVAL;
> @@ -701,9 +699,7 @@ static int rcar_du_plane_atomic_get_property(struct
> drm_plane *plane, container_of(state, const struct rcar_du_plane_state,
> state);
>  	struct rcar_du_device *rcdu = to_rcar_plane(plane)->group->dev;
> 
> -	if (property == rcdu->props.alpha)
> -		*val = rstate->alpha;
> -	else if (property == rcdu->props.colorkey)
> +	if (property == rcdu->props.colorkey)
>  		*val = rstate->colorkey;
>  	else
>  		return -EINVAL;
> @@ -772,10 +768,9 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp)
>  			continue;
> 
>  		drm_object_attach_property(&plane->plane.base,
> -					   rcdu->props.alpha, 255);
> -		drm_object_attach_property(&plane->plane.base,
>  					   rcdu->props.colorkey,
>  					   RCAR_DU_COLORKEY_NONE);
> +		drm_plane_create_alpha_property(&plane->plane, 255);
>  		drm_plane_create_zpos_property(&plane->plane, 1, 1, 7);
>  	}
> 
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h
> b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index f62e09f195de..2dc793ebd1a2
> 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
> @@ -50,7 +50,6 @@ static inline struct rcar_du_plane *to_rcar_plane(struct
> drm_plane *plane) * @state: base DRM plane state
>   * @format: information about the pixel format used by the plane
>   * @hwindex: 0-based hardware plane index, -1 means unused
> - * @alpha: value of the plane alpha property
>   * @colorkey: value of the plane colorkey property
>   */
>  struct rcar_du_plane_state {
> @@ -60,7 +59,6 @@ struct rcar_du_plane_state {
>  	int hwindex;
>  	enum rcar_du_plane_source source;
> 
> -	unsigned int alpha;
>  	unsigned int colorkey;
>  };
> 
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
> b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c index 2c96147bc444..ee85f6fdffad
> 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
> @@ -54,6 +54,7 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
>  	};
>  	struct rcar_du_plane_state state = {
>  		.state = {
> +			.alpha = 255,
>  			.crtc = &crtc->crtc,
>  			.crtc_x = 0,
>  			.crtc_y = 0,
> @@ -67,7 +68,6 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
>  		},
>  		.format = rcar_du_format_info(DRM_FORMAT_ARGB8888),
>  		.source = RCAR_DU_PLANE_VSPD1,
> -		.alpha = 255,
>  		.colorkey = 0,
>  	};
> 
> @@ -173,7 +173,7 @@ static void rcar_du_vsp_plane_setup(struct
> rcar_du_vsp_plane *plane) struct vsp1_du_atomic_config cfg = {
>  		.pixelformat = 0,
>  		.pitch = fb->pitches[0],
> -		.alpha = state->alpha,
> +		.alpha = state->state.alpha,
>  		.zpos = state->state.zpos,
>  	};
>  	unsigned int i;
> @@ -351,44 +351,13 @@ static void rcar_du_vsp_plane_reset(struct drm_plane
> *plane) if (state == NULL)
>  		return;
> 
> -	state->alpha = 255;
> +	state->state.alpha = 255;
>  	state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
> 
>  	plane->state = &state->state;
>  	plane->state->plane = plane;
>  }
> 
> -static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane,
> -	struct drm_plane_state *state, struct drm_property *property,
> -	uint64_t val)
> -{
> -	struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
> -	struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev;
> -
> -	if (property == rcdu->props.alpha)
> -		rstate->alpha = val;
> -	else
> -		return -EINVAL;
> -
> -	return 0;
> -}
> -
> -static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane,
> -	const struct drm_plane_state *state, struct drm_property *property,
> -	uint64_t *val)
> -{
> -	const struct rcar_du_vsp_plane_state *rstate =
> -		container_of(state, const struct rcar_du_vsp_plane_state, state);
> -	struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev;
> -
> -	if (property == rcdu->props.alpha)
> -		*val = rstate->alpha;
> -	else
> -		return -EINVAL;
> -
> -	return 0;
> -}
> -
>  static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
>  	.update_plane = drm_atomic_helper_update_plane,
>  	.disable_plane = drm_atomic_helper_disable_plane,
> @@ -396,8 +365,6 @@ static const struct drm_plane_funcs
> rcar_du_vsp_plane_funcs = { .destroy = drm_plane_cleanup,
>  	.atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
>  	.atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
> -	.atomic_set_property = rcar_du_vsp_plane_atomic_set_property,
> -	.atomic_get_property = rcar_du_vsp_plane_atomic_get_property,
>  };
> 
>  int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
> @@ -454,8 +421,7 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct
> device_node *np, if (type == DRM_PLANE_TYPE_PRIMARY)
>  			continue;
> 
> -		drm_object_attach_property(&plane->plane.base,
> -					   rcdu->props.alpha, 255);
> +		drm_plane_create_alpha_property(&plane->plane, 255);
>  		drm_plane_create_zpos_property(&plane->plane, 1, 1,
>  					       vsp->num_planes - 1);
>  	}
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
> b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h index f876c512163c..8b19914761e4
> 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h
> @@ -44,7 +44,6 @@ static inline struct rcar_du_vsp_plane
> *to_rcar_vsp_plane(struct drm_plane *p) * @state: base DRM plane state
>   * @format: information about the pixel format used by the plane
>   * @sg_tables: scatter-gather tables for the frame buffer memory
> - * @alpha: value of the plane alpha property
>   * @zpos: value of the plane zpos property
>   */
>  struct rcar_du_vsp_plane_state {
> @@ -53,7 +52,6 @@ struct rcar_du_vsp_plane_state {
>  	const struct rcar_du_format_info *format;
>  	struct sg_table sg_tables[3];
> 
> -	unsigned int alpha;
>  	unsigned int zpos;
>  };

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* [RFC PATCH 4/9] soc: samsung: Add generic power-management driver for Exynos
From: Krzysztof Kozlowski @ 2018-01-09 12:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515484746-10656-5-git-send-email-cw00.choi@samsung.com>

On Tue, Jan 9, 2018 at 8:59 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> To enter suspend, Exynos SoC requires the some machine dependent procedures.
> This patch introduces the generic power-management driver to support
> those requirements and generic interface for power state management.
>
> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> ---
>  arch/arm/mach-exynos/common.h         |   1 -
>  arch/arm/mach-exynos/exynos.c         |  23 +----
>  drivers/soc/samsung/Makefile          |   2 +-
>  drivers/soc/samsung/exynos-pm.c       | 176 ++++++++++++++++++++++++++++++++++
>  include/linux/soc/samsung/exynos-pm.h |  21 ++++
>  5 files changed, 199 insertions(+), 24 deletions(-)
>  create mode 100644 drivers/soc/samsung/exynos-pm.c
>  create mode 100644 include/linux/soc/samsung/exynos-pm.h
>
> diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
> index afbc143a3d5d..ad482c0fc131 100644
> --- a/arch/arm/mach-exynos/common.h
> +++ b/arch/arm/mach-exynos/common.h
> @@ -119,7 +119,6 @@ enum {
>   * Magic values for bootloader indicating chosen low power mode.
>   * See also Documentation/arm/Samsung/Bootloader-interface.txt
>   */
> -#define EXYNOS_SLEEP_MAGIC     0x00000bad
>  #define EXYNOS_AFTR_MAGIC      0xfcba0d10
>
>  void exynos_set_boot_flag(unsigned int cpu, unsigned int mode);
> diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
> index fbd108ce8745..0d5265d175c4 100644
> --- a/arch/arm/mach-exynos/exynos.c
> +++ b/arch/arm/mach-exynos/exynos.c
> @@ -12,6 +12,7 @@
>  #include <linux/of_fdt.h>

of_address.h might be not needed anymore.

>  #include <linux/platform_device.h>
>  #include <linux/irqchip.h>
> +#include <linux/soc/samsung/exynos-pm.h>
>  #include <linux/soc/samsung/exynos-regs-pmu.h>
>
>  #include <asm/cacheflush.h>
> @@ -41,28 +42,6 @@
>         .id                = -1,
>  };
>
> -void __iomem *sysram_base_addr __ro_after_init;
> -void __iomem *sysram_ns_base_addr __ro_after_init;
> -
> -void __init exynos_sysram_init(void)
> -{
> -       struct device_node *node;
> -
> -       for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram") {
> -               if (!of_device_is_available(node))
> -                       continue;
> -               sysram_base_addr = of_iomap(node, 0);
> -               break;
> -       }
> -
> -       for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram-ns") {
> -               if (!of_device_is_available(node))
> -                       continue;
> -               sysram_ns_base_addr = of_iomap(node, 0);
> -               break;
> -       }
> -}
> -
>  static void __init exynos_init_late(void)
>  {
>         if (of_machine_is_compatible("samsung,exynos5440"))
> diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
> index d2e637339a45..58ca5bdabf1f 100644
> --- a/drivers/soc/samsung/Makefile
> +++ b/drivers/soc/samsung/Makefile
> @@ -1,5 +1,5 @@
>  # SPDX-License-Identifier: GPL-2.0
> -obj-$(CONFIG_EXYNOS_PMU)       += exynos-pmu.o
> +obj-$(CONFIG_EXYNOS_PMU)       += exynos-pmu.o exynos-pm.o
>
>  obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS)   += exynos3250-pmu.o exynos4-pmu.o \
>                                         exynos5250-pmu.o exynos5420-pmu.o \
> diff --git a/drivers/soc/samsung/exynos-pm.c b/drivers/soc/samsung/exynos-pm.c
> new file mode 100644
> index 000000000000..45d84bbe5e61
> --- /dev/null
> +++ b/drivers/soc/samsung/exynos-pm.c
> @@ -0,0 +1,176 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// based on arch/arm/mach-exynos/suspend.c
> +// Copyright (c) 2018 Samsung Electronics Co., Ltd.
> +//
> +// Exynos Power Management support driver
> +
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_fdt.h>
> +#include <linux/kernel.h>
> +#include <linux/regulator/machine.h>
> +#include <linux/syscore_ops.h>
> +#include <linux/suspend.h>
> +
> +#include <asm/cpuidle.h>
> +#include <asm/io.h>
> +#include <asm/suspend.h>
> +
> +#include <linux/soc/samsung/exynos-pm.h>
> +#include <linux/soc/samsung/exynos-pmu.h>
> +
> +/*
> + * The struct exynos_pm_data contains the callbacks of
> + * both struct platform_suspend_ops and syscore_ops.
> + * This structure is listed according to the call order,
> + * because the callback call order for the two structures is mixed.
> + */
> +struct exynos_pm_data {
> +       int (*prepare)(void);                   /* for platform_suspend_ops */
> +       int (*suspend)(void);                   /* for syscore_ops */
> +       int (*enter)(suspend_state_t state);    /* for platform_suspend_ops */
> +       void (*resume)(void);                   /* for syscore_ops */
> +       void (*finish)(void);                   /* for platform_suspend_ops */
> +};
> +
> +static struct platform_suspend_ops exynos_pm_suspend_ops;
> +static struct syscore_ops exynos_pm_syscore_ops;
> +static const struct exynos_pm_data *pm_data  __ro_after_init;

It is already const, so __initconst?

> +
> +void __iomem *sysram_base_addr __ro_after_init;
> +void __iomem *sysram_ns_base_addr __ro_after_init;
> +
> +static int exynos_pm_prepare(void)
> +{
> +       int ret;
> +
> +       /*
> +        * REVISIT: It would be better if struct platform_suspend_ops
> +        * .prepare handler get the suspend_state_t as a parameter to
> +        * avoid hard-coding the suspend to mem state. It's safe to do
> +        * it now only because the suspend_valid_only_mem function is
> +        * used as the .valid callback used to check if a given state
> +        * is supported by the platform anyways.
> +        */
> +       ret = regulator_suspend_prepare(PM_SUSPEND_MEM);
> +       if (ret) {
> +               pr_err("Failed to prepare regulators for suspend (%d)\n", ret);
> +               return ret;
> +       }
> +
> +       if (pm_data->prepare) {
> +               ret = pm_data->prepare();
> +               if (ret) {
> +                       pr_err("Failed to prepare for suspend (%d)\n", ret);
> +                       return ret;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static int exynos_pm_suspend(void)
> +{
> +       if (pm_data->suspend)
> +               return pm_data->suspend();
> +
> +       return 0;
> +}
> +
> +static int exynos_pm_enter(suspend_state_t state)
> +{
> +       int ret;
> +
> +       exynos_sys_powerdown_conf(SYS_SLEEP);
> +
> +       ret = pm_data->enter(state);
> +       if (ret) {
> +               pr_err("Failed to enter sleep\n");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static void exynos_pm_resume(void)
> +{
> +       exynos_sys_powerup_conf(SYS_SLEEP);
> +
> +       if (pm_data->resume)
> +               pm_data->resume();
> +}
> +
> +static void exynos_pm_finish(void)
> +{
> +       int ret;
> +
> +       ret = regulator_suspend_finish();
> +       if (ret)
> +               pr_warn("Failed to resume regulators from suspend (%d)\n", ret);
> +
> +       if (pm_data->finish)
> +               pm_data->finish();
> +}
> +
> +/*
> + * Split the data between ARM architectures because it is relatively big
> + * and useless on other arch.
> + */
> +#ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS
> +#define exynos_pm_data_arm_ptr(data)   (&data)
> +#else
> +#define exynos_pm_data_arm_ptr(data)   NULL
> +#endif
> +
> +static const struct of_device_id exynos_pm_of_device_ids[] = {
> +       { /*sentinel*/ },
> +};
> +
> +void __init exynos_sysram_init(void)
> +{
> +       struct device_node *np;
> +
> +       for_each_compatible_node(np, NULL, "samsung,exynos4210-sysram") {
> +               if (!of_device_is_available(np))
> +                       continue;
> +               sysram_base_addr = of_iomap(np, 0);
> +               break;
> +       }
> +
> +       for_each_compatible_node(np, NULL, "samsung,exynos4210-sysram-ns") {
> +               if (!of_device_is_available(np))
> +                       continue;
> +               sysram_ns_base_addr = of_iomap(np, 0);
> +               break;
> +       }
> +}
> +
> +static int __init exynos_pm_init(void)
> +{
> +       const struct of_device_id *match;
> +       struct device_node *np;
> +
> +       np = of_find_matching_node_and_match(NULL,
> +                                       exynos_pm_of_device_ids, &match);
> +       if (!np) {
> +               pr_err("Failed to find PMU node for Exynos Power-Management\n");
> +               return -ENODEV;
> +       }
> +       pm_data = (const struct exynos_pm_data *) match->data;
> +
> +       exynos_sysram_init();
> +
> +       exynos_pm_suspend_ops.valid     = suspend_valid_only_mem;
> +       exynos_pm_suspend_ops.prepare   = exynos_pm_prepare;
> +       exynos_pm_syscore_ops.suspend   = exynos_pm_suspend;
> +       exynos_pm_suspend_ops.enter     = exynos_pm_enter;
> +       exynos_pm_syscore_ops.resume    = exynos_pm_resume;
> +       exynos_pm_suspend_ops.finish    = exynos_pm_finish;
> +
> +       register_syscore_ops(&exynos_pm_syscore_ops);
> +       suspend_set_ops(&exynos_pm_suspend_ops);
> +
> +       return 0;
> +}
> +postcore_initcall(exynos_pm_init);

As I mentioned in cover letter, please move here first ARMv7 code. Now
it looks like duplicating the existing code.

> diff --git a/include/linux/soc/samsung/exynos-pm.h b/include/linux/soc/samsung/exynos-pm.h
> new file mode 100644
> index 000000000000..b1afe95ed10c
> --- /dev/null
> +++ b/include/linux/soc/samsung/exynos-pm.h
> @@ -0,0 +1,21 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (c) 2018 Samsung Electronics Co., Ltd.
> +//
> +// Header for Exynos Power-Management support driver

Use header-style SPDX and comment.

Best regards,
Krzysztof

> +
> +#ifndef __LINUX_SOC_EXYNOS_PM_H
> +#define __LINUX_SOC_EXYNOS_PM_H
> +
> +/*
> + * Magic values for bootloader indicating chosen low power mode.
> + * See also Documentation/arm/Samsung/Bootloader-interface.txt
> + */
> +#define EXYNOS_SLEEP_MAGIC     0x00000bad
> +
> +extern void __iomem *sysram_base_addr;
> +extern void __iomem *sysram_ns_base_addr;

Since these are now global symbols, they need nice exynos prefix.
Also, probably they should not be globally modifiable. Only
exynos_sysram_init() should write there. Instead export a global
accessor (get()) and rest should use that one.

Best regards,
Krzysztof

> +
> +extern void exynos_sysram_init(void);
> +
> +#endif /* __LINUX_SOC_EXYNOS_PMU_H */
> --
> 1.9.1
>

^ permalink raw reply

* [RFC PATCH 5/9] soc: samsung: pm: Add support for suspend-to-ram of Exynos5433
From: Krzysztof Kozlowski @ 2018-01-09 12:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515484746-10656-6-git-send-email-cw00.choi@samsung.com>

On Tue, Jan 9, 2018 at 8:59 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> This patch adds the specific exynos_pm_data instance for Exynos5433
> in order to support the suspend-to-ram. Exynos5433 SoC need to write
> the 'cpu_resume' poiter address and the specific magic number
> for suspend mode.
>
> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> ---
>  drivers/soc/samsung/exynos-pm.c | 38 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 38 insertions(+)
>
> diff --git a/drivers/soc/samsung/exynos-pm.c b/drivers/soc/samsung/exynos-pm.c
> index 45d84bbe5e61..70d949ba5cab 100644
> --- a/drivers/soc/samsung/exynos-pm.c
> +++ b/drivers/soc/samsung/exynos-pm.c
> @@ -12,6 +12,7 @@
>  #include <linux/regulator/machine.h>
>  #include <linux/syscore_ops.h>
>  #include <linux/suspend.h>
> +#include <linux/psci.h>
>
>  #include <asm/cpuidle.h>
>  #include <asm/io.h>
> @@ -123,7 +124,44 @@ static void exynos_pm_finish(void)
>  #define exynos_pm_data_arm_ptr(data)   NULL
>  #endif
>
> +static int exynos5433_pm_suspend(unsigned long unused)
> +{
> +       /*
> +        * Exynos5433 uses PSCI v0.1 which provides the only one
> +        * entry point (psci_ops.cpu_suspend) for both cpuidle and
> +        * suspend-to-RAM. Also, PSCI v0.1 needs the specific 'power_state'
> +        * parameter for the suspend mode. In order to enter suspend mode,
> +        * Exynos5433 calls the 'psci_ops.cpu_suspend' with '0x3010000'
> +        * power_state parameter.
> +        *
> +        * '0x3010000' means that both cluster and system are going to enter
> +        * the power-down state as following:
> +        * - [25:24] 0x3 : Indicate the cluster and system.
> +        * - [16]    0x1 : Indicate power-down state.

Define them, it will be more readable (name of define given just as an example):

EXYNOS5433_PCSI_SUSPEND_SYSTEM (0x3 << 24)
(or even split per system and cluser and use BIT() if this is real meaning)
and probably reuse existing S5P_CENTRAL_LOWPWR_CFG

> +        */
> +       return psci_ops.cpu_suspend(0x3010000, __pa_symbol(cpu_resume));
> +}
> +
> +static int exynos5433_pm_suspend_enter(suspend_state_t state)
> +{
> +       if (!sysram_ns_base_addr)
> +               return -EINVAL;
> +
> +       __raw_writel(virt_to_phys(cpu_resume), sysram_ns_base_addr + 0x8);
> +       __raw_writel(EXYNOS_SLEEP_MAGIC, sysram_ns_base_addr + 0xc);

Document them in Documentation/arm/Samsung/Bootloader-interface.txt.

Best regards,
Krzysztof

> +
> +       return cpu_suspend(0, exynos5433_pm_suspend);
> +}
> +
> +const struct exynos_pm_data exynos5433_pm_data = {
> +       .enter          = exynos5433_pm_suspend_enter,
> +};
> +
>  static const struct of_device_id exynos_pm_of_device_ids[] = {
> +       {
> +               .compatible = "samsung,exynos5433-pmu",
> +               .data = exynos_pm_data_arm_ptr(exynos5433_pm_data),
> +       },
>         { /*sentinel*/ },
>  };
>
> --
> 1.9.1
>

^ permalink raw reply

* [PATCH] drivers: firmware: xilinx: Add ZynqMP firmware driver
From: Philippe Ombredanne @ 2018-01-09 12:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515449227-5096-1-git-send-email-jollys@xilinx.com>

Jolly,

On Mon, Jan 8, 2018 at 11:07 PM, Jolly Shah <jolly.shah@xilinx.com> wrote:
> This patch is adding communication layer with firmware.
> Firmware driver provides an interface to firmware APIs.
> Interface APIs can be used by any driver to communicate to
> PMUFW(Platform Management Unit). All requests go through ATF.
> Firmware-debug provides debugfs interface to all APIs.
> Firmware-ggs provides read/write interface to
> global storage registers.
>
> Signed-off-by: Jolly Shah <jollys@xilinx.com>
> Signed-off-by: Rajan Vaja <rajanv@xilinx.com>

<snip>

> --- /dev/null
> +++ b/drivers/firmware/xilinx/Kconfig
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier:     GPL-2.0+

Thank you++ for using the SPDX tags: here is my cheerful ack for this:

Acked-by: Philippe Ombredanne <pombredanne@nexb.com>

^ permalink raw reply

* [PATCH 6/9] arm64: dts: exynos: Add iRAM device-tree node for Exynos5433
From: Krzysztof Kozlowski @ 2018-01-09 12:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515484746-10656-7-git-send-email-cw00.choi@samsung.com>

On Tue, Jan 9, 2018 at 8:59 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> This patch adds the iRAM device-tree node of Exynos5433 which
> defines the memory map of iRAM as following and it is used for suspend.
> - address: 0x0202_0000 ~ 0x3000_0000
>
> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> ---
>  arch/arm64/boot/dts/exynos/exynos5433.dtsi | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/exynos/exynos5433.dtsi b/arch/arm64/boot/dts/exynos/exynos5433.dtsi
> index 62f276970174..77f4321b247c 100644
> --- a/arch/arm64/boot/dts/exynos/exynos5433.dtsi
> +++ b/arch/arm64/boot/dts/exynos/exynos5433.dtsi
> @@ -262,6 +262,20 @@
>                         interrupt-affinity = <&cpu4>, <&cpu5>, <&cpu6>, <&cpu7>;
>                 };
>
> +               sysram at 02020000 {

Remove leading 0.

Best regards,
Krzysztof

> +                       compatible = "mmio-sram";
> +                       reg = <0x02020000 0x5c000>;
> +                       #address-cells = <1>;
> +                       #size-cells = <1>;
> +                       ranges = <0 0x02020000 0x5c000>;
> +                       status = "okay";
> +
> +                       smp-sysram at 5b000 {
> +                               compatible = "samsung,exynos4210-sysram-ns";
> +                               reg = <0x5b000 0x1000>;
> +                       };
> +               };
> +
>                 chipid at 10000000 {
>                         compatible = "samsung,exynos4210-chipid";
>                         reg = <0x10000000 0x100>;
> --
> 1.9.1
>

^ permalink raw reply

* [PATCH v2] arm64: Branch predictor hardening for Cavium ThunderX2
From: Jayachandran C @ 2018-01-09 12:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180108164651.GQ25869@arm.com>

Use PSCI based mitigation for speculative execution attacks targeting
the branch predictor. The approach is similar to the one used for
Cortex-A CPUs, but in case of ThunderX2 we add another SMC call to
test if the firmware supports the capability.

If the secure firmware has been updated with the mitigation code to
invalidate the branch target buffer, we use the PSCI version call to
invoke it.

Signed-off-by: Jayachandran C <jnair@caviumnetworks.com>
---
v2:
 - rebased on top of the latest kpti branch
 - use pr_info_once/pr_warn_once to avoid excessive prints
 - using .desc generated too many prints, dropped plan for using it
 - fixed up a return

 arch/arm64/kernel/cpu_errata.c | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 70e5f18..c626914 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -128,6 +128,7 @@ static void  install_bp_hardening_cb(const struct arm64_cpu_capabilities *entry,
 	__install_bp_hardening_cb(fn, hyp_vecs_start, hyp_vecs_end);
 }
 
+#include <linux/arm-smccc.h>
 #include <linux/psci.h>
 
 static int enable_psci_bp_hardening(void *data)
@@ -165,6 +166,33 @@ static int qcom_enable_link_stack_sanitization(void *data)
 
 	return 0;
 }
+
+#define CAVIUM_TX2_SIP_SMC_CALL		0xC200FF00
+#define CAVIUM_TX2_BTB_HARDEN_CAP	0xB0A0
+
+static int enable_tx2_psci_bp_hardening(void *data)
+{
+	const struct arm64_cpu_capabilities *entry = data;
+	struct arm_smccc_res res;
+
+	if (!entry->matches(entry, SCOPE_LOCAL_CPU))
+		return 0;
+
+	arm_smccc_smc(CAVIUM_TX2_SIP_SMC_CALL, CAVIUM_TX2_BTB_HARDEN_CAP, 0, 0, 0, 0, 0, 0, &res);
+	if (res.a0 != 0) {
+		pr_warn_once("Error: CONFIG_HARDEN_BRANCH_PREDICTOR enabled, but firmware does not support it\n");
+		return 0;
+	}
+	if (res.a1 == 1 && psci_ops.get_version) {
+		pr_info_once("Branch predictor hardening: Enabled, using PSCI version call.\n");
+		install_bp_hardening_cb(entry,
+				       (bp_hardening_cb_t)psci_ops.get_version,
+				       __psci_hyp_bp_inval_start,
+				       __psci_hyp_bp_inval_end);
+	}
+
+	return 0;
+}
 #endif	/* CONFIG_HARDEN_BRANCH_PREDICTOR */
 
 #define MIDR_RANGE(model, min, max) \
@@ -338,6 +366,16 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
 		.capability = ARM64_HARDEN_BP_POST_GUEST_EXIT,
 		MIDR_ALL_VERSIONS(MIDR_QCOM_FALKOR_V1),
 	},
+	{
+		.capability = ARM64_HARDEN_BRANCH_PREDICTOR,
+		MIDR_ALL_VERSIONS(MIDR_BRCM_VULCAN),
+		.enable = enable_tx2_psci_bp_hardening,
+	},
+	{
+		.capability = ARM64_HARDEN_BRANCH_PREDICTOR,
+		MIDR_ALL_VERSIONS(MIDR_CAVIUM_THUNDERX2),
+		.enable = enable_tx2_psci_bp_hardening,
+	},
 #endif
 	{
 	}
-- 
2.7.4

^ permalink raw reply related

* [RFC PATCH] drivers: soc: xilinx: Add ZynqMP power domain driver
From: Philippe Ombredanne @ 2018-01-09 12:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515449572-5398-1-git-send-email-jollys@xilinx.com>

Jolly,

On Mon, Jan 8, 2018 at 11:12 PM, Jolly Shah <jolly.shah@xilinx.com> wrote:
> The zynqmp-genpd driver communicates the usage requirements
> for logical power domains / devices to the platform FW.
> FW is responsible for choosing appropriate power states,
> taking Linux' usage information into account.
>
> Signed-off-by: Jolly Shah <jollys@xilinx.com>
> Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
> ---

<snip>

> --- /dev/null
> +++ b/drivers/soc/xilinx/zynqmp/pm_domains.c
> @@ -0,0 +1,343 @@
> +/*
> + * ZynqMP Generic PM domain support
> + *
> + *  Copyright (C) 2014-2017 Xilinx, Inc.
> + *
> + *  Davorin Mista <davorin.mista@aggios.com>
> + *  Jolly Shah <jollys@xilinx.com>
> + *  Rajan Vaja <rajanv@xilinx.com>
> + *
> + * SPDX-License-Identifier:     GPL-2.0+
> + */

This tag should be on the fist line as this:

// SPDX-License-Identifier: GPL-2.0+

-- 
Cordially
Philippe Ombredanne

^ permalink raw reply

* [PATCH] ARM: i.MX6: add new silicon revision number 1.6
From: Christoph Fritz @ 2018-01-09 12:51 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds new silicon revision number 1.6 as specified in document
IMX6DQCEC.pdf.

Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
---
 arch/arm/mach-imx/anatop.c | 4 ++++
 include/soc/imx/revision.h | 1 +
 2 files changed, 5 insertions(+)

diff --git a/arch/arm/mach-imx/anatop.c b/arch/arm/mach-imx/anatop.c
index 649a84c..deb98b8 100644
--- a/arch/arm/mach-imx/anatop.c
+++ b/arch/arm/mach-imx/anatop.c
@@ -157,6 +157,10 @@ void __init imx_init_revision_from_anatop(void)
 		 */
 		revision = IMX_CHIP_REVISION_1_5;
 		break;
+	case 6:
+		/* marked as 'E' in part number last character */
+		revision = IMX_CHIP_REVISION_1_6;
+		break;
 	default:
 		/*
 		 * Fail back to return raw register value instead of 0xff.
diff --git a/include/soc/imx/revision.h b/include/soc/imx/revision.h
index 9ea3469..cd4c847 100644
--- a/include/soc/imx/revision.h
+++ b/include/soc/imx/revision.h
@@ -15,6 +15,7 @@
 #define IMX_CHIP_REVISION_1_3		0x13
 #define IMX_CHIP_REVISION_1_4		0x14
 #define IMX_CHIP_REVISION_1_5		0x15
+#define IMX_CHIP_REVISION_1_6		0x16
 #define IMX_CHIP_REVISION_2_0		0x20
 #define IMX_CHIP_REVISION_2_1		0x21
 #define IMX_CHIP_REVISION_2_2		0x22
-- 
2.1.4

^ permalink raw reply related

* [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


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