* [PATCH V3 2/2] scsi: hpsa: drop shutdown callback
From: Don Brace @ 2018-05-30 19:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <51bb7b82-c4a1-435c-7668-639db96d76e1@finnie.org>
> -----Original Message-----
> From: Ryan Finnie [mailto:ryan at finnie.org]
> Sent: Tuesday, May 29, 2018 8:50 PM
> To: Sinan Kaya <okaya@codeaurora.org>; linux-pci at vger.kernel.org;
> timur at codeaurora.org
> Cc: linux-arm-msm at vger.kernel.org; linux-arm-kernel at lists.infradead.org;
> stable at vger.kernel.org; Don Brace <don.brace@microsemi.com>; James E.J.
> Bottomley <jejb@linux.vnet.ibm.com>; Martin K. Petersen
> <martin.petersen@oracle.com>; esc.storagedev
> <esc.storagedev@microsemi.com>; open list:HEWLETT-PACKARD SMART ARRAY
> RAID DRIVER (hpsa) <linux-scsi@vger.kernel.org>; open list <linux-
> kernel at vger.kernel.org>
> Subject: Re: [PATCH V3 2/2] scsi: hpsa: drop shutdown callback
>
> EXTERNAL EMAIL
>
>
> On 05/28/2018 02:21 PM, Sinan Kaya wrote:
> > 'Commit cc27b735ad3a ("PCI/portdrv: Turn off PCIe services during
> > shutdown")' has been added to kernel to shutdown pending PCIe port
> > service interrupts during reboot so that a newly started kexec kernel
> > wouldn't observe pending interrupts.
> >
> > pcie_port_device_remove() is disabling the root port and switches by
> > calling pci_disable_device() after all PCIe service drivers are shutdown.
> >
> > This has been found to cause crashes on HP DL360 Gen9 machines during
> > reboot due to hpsa driver not clearing the bus master bit during the
> > shutdown procedure by calling pci_disable_device().
> >
> > Drop the shutdown API and do an orderly clean up by using the remove.
> >
> > Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
> > Link: https://bugzilla.kernel.org/show_bug.cgi?id=199779
> > Fixes: cc27b735ad3a ("PCI/portdrv: Turn off PCIe services during shutdown")
> > Cc: stable at vger.kernel.org
> > Reported-by: Ryan Finnie <ryan@finnie.org>
>
> Tested successfully on DL360 Gen9 and DL380 Gen9.
>
> Tested-by: Ryan Finnie <ryan@finnie.org>
The shutdown path issues a cache flush to the controller.
Without this flush, you will see "Dirty Cache" messages at POST.
It is best to keep the shutdown path.
Thanks,
Don Brace
ESC - Smart Storage
Microsemi Corporation
^ permalink raw reply
* [PATCH RFC] ARM: dts: add Raspberry Pi Compute Module and IO board
From: Eric Anholt @ 2018-05-30 19:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1670815145.72020.1526930907735@email.1und1.de>
Stefan Wahren <stefan.wahren@i2se.com> writes:
>> Eric Anholt <eric@anholt.net> hat am 21. Mai 2018 um 20:26 geschrieben:
>>
>>
>> Stefan Wahren <stefan.wahren@i2se.com> writes:
>>
>> > The Raspberry Pi Compute Module (CM1) is a SoM which contains a
>> > BCM2835 processor, 512 MB RAM and a 4 GB eMMC. There is also a carrier
>> > board which is called Compute Module IO Board.
>> >
>> > Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
>> > ---
>> > arch/arm/boot/dts/Makefile | 1 +
>> > arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts | 92 +++++++++++++++++++++++++++++++
>> > arch/arm/boot/dts/bcm2835-rpi-cm1.dtsi | 34 ++++++++++++
>> > 3 files changed, 127 insertions(+)
>> > create mode 100644 arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts
>> > create mode 100644 arch/arm/boot/dts/bcm2835-rpi-cm1.dtsi
>> >
>> > diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
>> > index ec2024e..a9883e8 100644
>> > --- a/arch/arm/boot/dts/Makefile
>> > +++ b/arch/arm/boot/dts/Makefile
>> > @@ -73,6 +73,7 @@ dtb-$(CONFIG_ARCH_BCM2835) += \
>> > bcm2835-rpi-b-rev2.dtb \
>> > bcm2835-rpi-b-plus.dtb \
>> > bcm2835-rpi-a-plus.dtb \
>> > + bcm2835-rpi-cm1-io1.dtb \
>> > bcm2836-rpi-2-b.dtb \
>> > bcm2837-rpi-3-b.dtb \
>> > bcm2837-rpi-3-b-plus.dtb \
>> > diff --git a/arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts b/arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts
>> > new file mode 100644
>> > index 0000000..4d9aa22
>> > --- /dev/null
>> > +++ b/arch/arm/boot/dts/bcm2835-rpi-cm1-io1.dts
>> > @@ -0,0 +1,92 @@
>> > +// SPDX-License-Identifier: GPL-2.0
>> > +/dts-v1/;
>> > +#include "bcm2835-rpi-cm1.dtsi"
>> > +#include "bcm283x-rpi-usb-host.dtsi"
>> > +
>> > +/ {
>> > + compatible = "raspberrypi,compute-module", "brcm,bcm2835";
>> > + model = "Raspberry Pi Compute Module IO board rev1";
>> > +};
>> > +
>> > +&dsi1 {
>> > + status = "okay";
>> > +};
>> > +
>> > +&gpio {
>> > + /*
>> > + * This is based on the official GPU firmware DT blob.
>> > + *
>> > + * Legend:
>> > + * "NC" = not connected (no rail from the SoC)
>> > + * "FOO" = GPIO line named "FOO" on the schematic
>> > + * "FOO_N" = GPIO line named "FOO" on schematic, active low
>> > + */
>> > + gpio-line-names = "GPIO0",
>> > + "GPIO1",
>> > + "GPIO2",
>> > + "GPIO3",
>> > + "GPIO4",
>> > + "GPIO5",
>> > + "GPIO6",
>> > + "GPIO7",
>> > + "GPIO8",
>> > + "GPIO9",
>> > + "GPIO10",
>> > + "GPIO11",
>> > + "GPIO12",
>> > + "GPIO13",
>> > + "GPIO14",
>> > + "GPIO15",
>> > + "GPIO16",
>> > + "GPIO17",
>> > + "GPIO18",
>> > + "GPIO19",
>> > + "GPIO20",
>> > + "GPIO21",
>> > + "GPIO22",
>> > + "GPIO23",
>> > + "GPIO24",
>> > + "GPIO25",
>> > + "GPIO26",
>> > + "GPIO27",
>> > + "GPIO28",
>> > + "GPIO29",
>> > + "GPIO30",
>> > + "GPIO31",
>> > + "GPIO32",
>> > + "GPIO33",
>> > + "GPIO34",
>> > + "GPIO35",
>> > + "GPIO36",
>> > + "GPIO37",
>> > + "GPIO38",
>> > + "GPIO39",
>> > + "GPIO40",
>> > + "GPIO41",
>> > + "GPIO42",
>> > + "GPIO43",
>> > + "GPIO44",
>> > + "GPIO45",
>> > + "HDMI_HPD_N",
>> > + /* Also used as ACT LED */
>> > + "EMMC_EN_N",
>> > + /* Used by eMMC */
>> > + "SD_CLK_R",
>> > + "SD_CMD_R",
>> > + "SD_DATA0_R",
>> > + "SD_DATA1_R",
>> > + "SD_DATA2_R",
>> > + "SD_DATA3_R";
>> > +
>> > + pinctrl-0 = <&gpioout &alt0>;
>> > +};
>> > +
>> > +&hdmi {
>> > + hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>;
>> > +};
>>
>> I think this should be ACTIVE_LOW, since it's "HDMI_HPD_N_1V8", right?
>
> I just copy & paste from the rpi-4.14/bcm2708-rpi-cm.dts. I thought
> the HDMI interface on my IO board is broken, but maybe this is a
> downstream issue.
Downstream gets HDMI wrong pretty frequently, so it's not surprising.
Watching xrandr -q with X11 running and plugging and unplugging HDMI can
help figure out whether we got the polarity right. I think you can also
do this with libdrm's modetest, if you don't have X11 up.
>> > +
>> > +&uart0 {
>> > + pinctrl-names = "default";
>> > + pinctrl-0 = <&uart0_gpio14>;
>> > + status = "okay";
>> > +};
>> > diff --git a/arch/arm/boot/dts/bcm2835-rpi-cm1.dtsi b/arch/arm/boot/dts/bcm2835-rpi-cm1.dtsi
>> > new file mode 100644
>> > index 0000000..ef22c2d
>> > --- /dev/null
>> > +++ b/arch/arm/boot/dts/bcm2835-rpi-cm1.dtsi
>> > @@ -0,0 +1,34 @@
>> > +// SPDX-License-Identifier: GPL-2.0
>> > +/dts-v1/;
>> > +#include "bcm2835.dtsi"
>> > +#include "bcm2835-rpi.dtsi"
>> > +
>> > +/ {
>> > + leds {
>> > + act {
>> > + gpios = <&gpio 47 GPIO_ACTIVE_LOW>;
>> > + };
>> > + };
>> > +
>> > + reg_3v3: fixed-regulator {
>> > + compatible = "regulator-fixed";
>> > + regulator-name = "3V3";
>> > + regulator-min-microvolt = <3300000>;
>> > + regulator-max-microvolt = <3300000>;
>> > + regulator-always-on;
>> > + };
>> > +
>> > + reg_1v8: fixed-regulator {
>> > + compatible = "regulator-fixed";
>> > + regulator-name = "1V8";
>> > + regulator-min-microvolt = <1800000>;
>> > + regulator-max-microvolt = <1800000>;
>> > + regulator-always-on;
>> > + };
>> > +};
>> > +
>> > +&sdhost {
>> > + non-removable;
>> > + vmmc-supply = <®_3v3>;
>> > + vqmmc-supply = <®_1v8>;
>> > +};
>>
>> Also, looking at some datasheets I have laying around, it says "eMMC I/O
>> Voltage fixed at 1V8" -- is this regulator setup right, in that case?
>
> Usually an eMMC has 2 different voltage sources:
> vqmmc-supply -> supply node for IO line power (usually switchable, but fixed on Compute Module)
> vmmc-supply -> supply node for card's power (usually fixed)
>
> Do you have a specific concern (voltage, naming)?
It was just "I was reviewing, and this stood out." That explanation
there seems sufficient to me. Thanks!
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 832 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180530/a42a5482/attachment.sig>
^ permalink raw reply
* [PATCH 00/12] introduce support for early platform drivers
From: Michael Turquette @ 2018-05-30 19:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAL_JsqJ=DdkDR3LtnTMHPUpwaUbjPEBgkaCV8ja+p-mTvWZuYA@mail.gmail.com>
Hi Rob,
Quoting Rob Herring (2018-05-14 06:20:57)
> On Mon, May 14, 2018 at 6:38 AM, Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> > 2018-05-11 22:13 GMT+02:00 Rob Herring <robh+dt@kernel.org>:
> >> On Fri, May 11, 2018 at 11:20 AM, Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> >>> This series is a follow-up to the RFC[1] posted a couple days ago.
> >>>
> >>> NOTE: this series applies on top of my recent patches[2] that move the previous
> >>> implementation of early platform devices to arch/sh.
> >>>
> >>> Problem:
> >>>
> >>> Certain class of devices, such as timers, certain clock drivers and irq chip
> >>> drivers need to be probed early in the boot sequence. The currently preferred
> >>> approach is using one of the OF_DECLARE() macros. This however does not create
> >>> a platform device which has many drawbacks - such as not being able to use
> >>> devres routines, dev_ log functions or no way of deferring the init OF function
> >>> if some other resources are missing.
> >>
> >> I skimmed though this and it doesn't look horrible (how's that for
> >> positive feedback? ;) ). But before going into the details, I think
> >> first there needs to be agreement this is the right direction.
> >>
> >> The question does remain though as to whether this class of devices
> >> should be platform drivers. They can't be modules. They can't be
> >> hotplugged. Can they be runtime-pm enabled? So the advantage is ...
> >>
> >
> > The main (but not the only) advantage for drivers that can both be
> > platform drivers and OF_DECLARE drivers is that we get a single entry
> > point and can reuse code without resorting to checking if (!dev). It
> > results in more consistent code base. Another big advantage is
> > consolidation of device tree and machine code for SoC drivers used in
> > different boards of which some are still using board files and others
> > are defined in DT (see: DaVinci).
> >
> >> I assume that the clock maintainers had some reason to move clocks to
> >> be platform drivers. It's just not clear to me what that was.
> >>
> >>> For drivers that use both platform drivers and OF_DECLARE the situation is even
> >>> more complicated as the code needs to take into account that there can possibly
> >>> be no struct device present. For a specific use case that we're having problems
> >>> with, please refer to the recent DaVinci common-clock conversion patches and
> >>> the nasty workaround that this problem implies[3].
> >>
> >> So devm_kzalloc will work with this solution? Why did we need
> >> devm_kzalloc in the first place? The clocks can never be removed and
> >> cleaning up on error paths is kind of pointless. The system would be
> >> hosed, right?
> >>
> >
> > It depends - not all clocks are necessary for system to boot.
>
> That doesn't matter. You have a single driver for all/most of the
> clocks, so the driver can't be removed.
-ECANOFWORMS
A couple of quick rebuttals, but I imagine we're going to disagree on
the platform_driver thing as a matter of taste no matter what...
1) There should be multiple clk drivers in a properly modeled system.
Some folks still incorrectly put all clocks in a system into a single
driver because That's How We Used To Do It, and some systems (simpler
ones) really only have a single clock generator IP block.
Excepting those two reasons above, we really should have separate
drivers for clocks controlled by the PMIC, for the (one or more) clock
generator blocks inside of the AP/SoC, and then even more for the
drivers that map to IP blocks that have their own clock gens.
Good examples of the latter are display controllers that generate their
own PLL and pixel clock. Or MMC controllers that have a
runtime-programmable clock divider. Examples of these are merged into
mainline.
2) Stephen and I wanted clock drivers to actually be represented in the
driver model. There were these gigantic clock drivers that exclusively
used CLK_OF_DECLARE and they just sort of floated out there in the
ether... no representation in sysfs, no struct device to map onto a
clock controller struct, etc.
I'd be happy to hear why you think that platform_driver is a bad fit for
a device driver that generally manages memory-mapped system resources
that are part of the system glue and not really tied to a specific bus.
Sounds like a good fit to me.
If platform_driver doesn't handle the early boot thing well, that tells
me that we have a problem to solve in platform_driver, not in the clk
subsystem or drivers.
3) Lots of clock controllers should be loadable modules. E.g. i2c clock
expanders, potentially external PMIC-related drivers, external audio
codecs, etc.
Again, repeating my point #1 above, just because many platforms have a
monolithic clock driver does not mean that this is the Right Way to do
it.
Best regards,
Mike
>
> >>> We also used to have an early platform drivers implementation but they were not
> >>> integrated with the linux device model at all - they merely used the same data
> >>> structures. The users could not use devres, defer probe and the early devices
> >>> never became actual platform devices later on.
> >>>
> >>> Proposed solution:
> >>>
> >>> This series aims at solving this problem by (re-)introducing the concept of
> >>> early platform drivers and devices - this time however in a way that seamlessly
> >>> integrates with the existing platform drivers and also offers device-tree
> >>> support.
> >>>
> >>> The idea is to provide a way for users to probe devices early, while already
> >>> being able to use devres, devices resources and properties and also deferred
> >>> probing.
> >>>
> >>> New structures are introduced: the early platform driver contains the
> >>> early_probe callback which has the same signature as regular platform_device
> >>> probe. This callback is called early on. The user can have both the early and
> >>> regular probe speficied or only one of them and they both receive the same
> >>> platform device object as argument. Any device data allocated early will be
> >>> carried over to the normal probe.
> >>>
> >>> The architecture code is responsible for calling early_platform_start() in
> >>> which the early drivers will be registered and devices populated from DT.
> >>
> >> Can we really do this in one spot for different devices (clk, timers,
> >> irq). The sequence is all very carefully crafted. Platform specific
> >> hooks is another thing to consider.
> >>
> >
> > This is why I added support for early probe deferral - so that we can
> > stop caring for the order as long as the drivers are aware of other
> > resources they need and we call early_platform_start() the moment the
> > earliest of the early devices is needed.
>
> Deferred probe helps for inter-device dependencies, but I am more
> concerned about timing of trying to register clocksources, irqchips,
> etc. What happens if we probe drivers before the infrastructure is
> initialized?
>
> Rob
^ permalink raw reply
* [PATCH 6/9] clk: davinci: pll: allow dev == NULL
From: Michael Turquette @ 2018-05-30 19:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180525181150.17873-7-david@lechnology.com>
Hi David,
Quoting David Lechner (2018-05-25 11:11:47)
> This modifies the TI Davinci PLL clock driver to allow for the case
> when dev == NULL. On some (most) SoCs that use this driver, the PLL
> clock needs to be registered during early boot because it is used
> for clocksource/clkevent and there will be no platform device available.
A lot of this stuff feels like a step backwards. E.g:
> diff --git a/drivers/clk/davinci/pll.c b/drivers/clk/davinci/pll.c
> index 23a24c944f1d..2eb981e61185 100644
> --- a/drivers/clk/davinci/pll.c
> +++ b/drivers/clk/davinci/pll.c
> @@ -11,6 +11,7 @@
>
> #include <linux/clk-provider.h>
> #include <linux/clk.h>
> +#include <linux/clk/davinci.h>
> #include <linux/delay.h>
> #include <linux/err.h>
> #include <linux/io.h>
> @@ -223,6 +224,7 @@ static const struct clk_ops dm365_pll_ops = {
>
> /**
> * davinci_pll_div_register - common *DIV clock implementation
> + * @dev: The PLL platform device or NULL
> * @name: the clock name
> * @parent_name: the parent clock name
> * @reg: the *DIV register
> @@ -240,17 +242,21 @@ static struct clk *davinci_pll_div_register(struct device *dev,
> const struct clk_ops *divider_ops = &clk_divider_ops;
> struct clk_gate *gate;
> struct clk_divider *divider;
> + struct clk *clk;
> + int ret;
>
> - gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
> + gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> if (!gate)
> return ERR_PTR(-ENOMEM);
>
> gate->reg = reg;
> gate->bit_idx = DIV_ENABLE_SHIFT;
>
> - divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL);
> - if (!divider)
> - return ERR_PTR(-ENOMEM);
> + divider = kzalloc(sizeof(*divider), GFP_KERNEL);
> + if (!divider) {
> + ret = -ENOMEM;
> + goto err_free_gate;
> + }
>
> divider->reg = reg;
> divider->shift = DIV_RATIO_SHIFT;
Oh no my poor devm_ helpers!
I understand that we need to support early boot drivers better, so this
patch can be merged.
However I'm curious if you're tracking Bartosz's early_platform_driver
efforts? Converting to that if it is ever merged would likely be
cleaner:
https://lkml.kernel.org/r/20180511162028.20616-1-brgl at bgdev.pl
Best regards,
Mike
^ permalink raw reply
* [PATCH 04/13] staging: vc04_services: no need to check debugfs return values
From: Eric Anholt @ 2018-05-30 19:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180529142947.3250-4-gregkh@linuxfoundation.org>
Greg Kroah-Hartman <gregkh@linuxfoundation.org> writes:
> When calling debugfs functions, there is no need to ever check the
> return value. The function can work or not, but the code logic should
> never do something different based on this.
>
> Clean up the vchiq_arm code by not caring about the value of debugfs
> calls. This ends up removing a number of lines of code that are not
> needed.
>
> Cc: Eric Anholt <eric@anholt.net>
> Cc: Stefan Wahren <stefan.wahren@i2se.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Dan Carpenter <dan.carpenter@oracle.com>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Keerthi Reddy <keerthigd4990@gmail.com>
> Cc: linux-rpi-kernel at lists.infradead.org
> Cc: linux-arm-kernel at lists.infradead.org
> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> ---
> .../interface/vchiq_arm/vchiq_arm.c | 13 +---
> .../interface/vchiq_arm/vchiq_debugfs.c | 72 +++----------------
> .../interface/vchiq_arm/vchiq_debugfs.h | 4 +-
> 3 files changed, 15 insertions(+), 74 deletions(-)
>
> diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c
> index 766b4fe5f32c..103fec955e2c 100644
> --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c
> +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c
> @@ -314,31 +284,13 @@ void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance)
> debugfs_remove_recursive(node->dentry);
> }
>
> -int vchiq_debugfs_init(void)
> +void vchiq_debugfs_init(void)
> {
> - BUG_ON(debugfs_info.vchiq_cfg_dir != NULL);
> -
> debugfs_info.vchiq_cfg_dir = debugfs_create_dir("vchiq", NULL);
> - if (debugfs_info.vchiq_cfg_dir == NULL)
> - goto fail;
> -
I think now that we allow successful probe with this value NULL, module
remove could vchiq_debugfs_deinit() -> vchiq_debugfs_top() -> BUG_ON().
I think the right solution would be to just drop that BUG_ON(). With
that change (and probably just garbage-collecting that function), this
will be enthusiastically:
Reviewed-by: Eric Anholt <eric@anholt.net>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 832 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180530/2fb101af/attachment-0001.sig>
^ permalink raw reply
* [PATCH 6/9] clk: davinci: pll: allow dev == NULL
From: David Lechner @ 2018-05-30 19:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180530194559.982.42844@harbor.lan>
On 05/30/2018 02:46 PM, Michael Turquette wrote:
> Hi David,
>
> Quoting David Lechner (2018-05-25 11:11:47)
>> This modifies the TI Davinci PLL clock driver to allow for the case
>> when dev == NULL. On some (most) SoCs that use this driver, the PLL
>> clock needs to be registered during early boot because it is used
>> for clocksource/clkevent and there will be no platform device available.
>
> A lot of this stuff feels like a step backwards. E.g:
>
>> diff --git a/drivers/clk/davinci/pll.c b/drivers/clk/davinci/pll.c
>> index 23a24c944f1d..2eb981e61185 100644
>> --- a/drivers/clk/davinci/pll.c
>> +++ b/drivers/clk/davinci/pll.c
>> @@ -11,6 +11,7 @@
>>
>> #include <linux/clk-provider.h>
>> #include <linux/clk.h>
>> +#include <linux/clk/davinci.h>
>> #include <linux/delay.h>
>> #include <linux/err.h>
>> #include <linux/io.h>
>> @@ -223,6 +224,7 @@ static const struct clk_ops dm365_pll_ops = {
>>
>> /**
>> * davinci_pll_div_register - common *DIV clock implementation
>> + * @dev: The PLL platform device or NULL
>> * @name: the clock name
>> * @parent_name: the parent clock name
>> * @reg: the *DIV register
>> @@ -240,17 +242,21 @@ static struct clk *davinci_pll_div_register(struct device *dev,
>> const struct clk_ops *divider_ops = &clk_divider_ops;
>> struct clk_gate *gate;
>> struct clk_divider *divider;
>> + struct clk *clk;
>> + int ret;
>>
>> - gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
>> + gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>> if (!gate)
>> return ERR_PTR(-ENOMEM);
>>
>> gate->reg = reg;
>> gate->bit_idx = DIV_ENABLE_SHIFT;
>>
>> - divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL);
>> - if (!divider)
>> - return ERR_PTR(-ENOMEM);
>> + divider = kzalloc(sizeof(*divider), GFP_KERNEL);
>> + if (!divider) {
>> + ret = -ENOMEM;
>> + goto err_free_gate;
>> + }
>>
>> divider->reg = reg;
>> divider->shift = DIV_RATIO_SHIFT;
>
> Oh no my poor devm_ helpers!
>
> I understand that we need to support early boot drivers better, so this
> patch can be merged.
>
> However I'm curious if you're tracking Bartosz's early_platform_driver
> efforts? Converting to that if it is ever merged would likely be
> cleaner:
>
> https://lkml.kernel.org/r/20180511162028.20616-1-brgl at bgdev.pl
>
> Best regards,
> Mike
>
Yes. In fact, this is what got Bartosz working on it in the first
place. :-)
^ permalink raw reply
* [PATCH v2] dmaengine: pxa: add a default requestor policy
From: Robert Jarzmik @ 2018-05-30 20:00 UTC (permalink / raw)
To: linux-arm-kernel
As what former drcmr -1 value meant, add a this as a default to each
channel, ie. that by default no requestor line is used.
This is specifically used for network drivers smc91x and smc911x, and
needed for their port to slave maps.
Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
Since v1: changed -1 to U32_MAX
---
drivers/dma/pxa_dma.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c
index 9505334f9c6e..b31c28b67ad3 100644
--- a/drivers/dma/pxa_dma.c
+++ b/drivers/dma/pxa_dma.c
@@ -762,6 +762,8 @@ static void pxad_free_chan_resources(struct dma_chan *dchan)
dma_pool_destroy(chan->desc_pool);
chan->desc_pool = NULL;
+ chan->drcmr = U32_MAX;
+ chan->prio = PXAD_PRIO_LOWEST;
}
static void pxad_free_desc(struct virt_dma_desc *vd)
@@ -1386,6 +1388,9 @@ static int pxad_init_dmadev(struct platform_device *op,
c = devm_kzalloc(&op->dev, sizeof(*c), GFP_KERNEL);
if (!c)
return -ENOMEM;
+
+ c->drcmr = U32_MAX;
+ c->prio = PXAD_PRIO_LOWEST;
c->vc.desc_free = pxad_free_desc;
vchan_init(&c->vc, &pdev->slave);
init_waitqueue_head(&c->wq_state);
--
2.11.0
^ permalink raw reply related
* [PATCH 0/9] clk: davinci: outstanding fixes
From: Michael Turquette @ 2018-05-30 20:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180525181150.17873-1-david@lechnology.com>
Hi David,
Quoting David Lechner (2018-05-25 11:11:41)
> This is a resend of all of the outstanding DaVinci clock patches plus one new
> patch. All of the patches (except the new one) have been reviewed and tested
> by someone other than me.
>
> The new patch ("clk: davinci: Fix link errors when not all SoCs are enabled")
> should be fairly trivial.
>
> I am resending them all in one series to hopefully make it easier to get them
> picked up by having them in the correct order to avoid merge conflicts. This
> series is based on the clk/clk-davinci-psc-da830 branch.
I picked them all and pushed to clk/clk-davinci based on -rc1. There are
some comments on the individual patches, all of which are of the "please
revisit this and fix it up later" variety.
Going forward I'm happy for you and/or Sekhar to send clk PRs to Stephen
and myself. The same rules apply for PRs: all patches need to be posted
to the list the old fashioned way for review. But PRs make our lives a
bit easier, especially when dealing with chip variants from the same SoC
family like we have with davinci.
Best regards,
Mike
>
> David Lechner (7):
> clk: davinci: pll-dm355: drop pll2_sysclk2
> clk: davinci: pll-dm355: fix SYSCLKn parent names
> clk: davinci: psc-dm355: fix ASP0/1 clkdev lookups
> clk: davinci: pll: allow dev == NULL
> clk: davinci: da850-pll: change PLL0 to CLK_OF_DECLARE
> clk: davinci: psc: allow for dev == NULL
> clk: davinci: Fix link errors when not all SoCs are enabled
>
> Sekhar Nori (2):
> clk: davinci: pll-dm646x: keep PLL2 SYSCLK1 always enabled
> clk: davinci: psc-dm365: fix few clocks
>
> drivers/clk/davinci/pll-da830.c | 5 +-
> drivers/clk/davinci/pll-da850.c | 37 ++--
> drivers/clk/davinci/pll-dm355.c | 22 ++-
> drivers/clk/davinci/pll-dm365.c | 9 +-
> drivers/clk/davinci/pll-dm644x.c | 9 +-
> drivers/clk/davinci/pll-dm646x.c | 11 +-
> drivers/clk/davinci/pll.c | 299 +++++++++++++++++++++----------
> drivers/clk/davinci/pll.h | 41 +++--
> drivers/clk/davinci/psc-dm355.c | 7 +-
> drivers/clk/davinci/psc-dm365.c | 22 ++-
> drivers/clk/davinci/psc-dm644x.c | 3 +-
> drivers/clk/davinci/psc-dm646x.c | 3 +-
> drivers/clk/davinci/psc.c | 72 ++++++--
> drivers/clk/davinci/psc.h | 12 ++
> include/linux/clk/davinci.h | 40 +++++
> 15 files changed, 418 insertions(+), 174 deletions(-)
> create mode 100644 include/linux/clk/davinci.h
>
> --
> 2.17.0
>
^ permalink raw reply
* [RESEND v2] dmaengine: pxa: add a default requestor policy
From: Robert Jarzmik @ 2018-05-30 20:12 UTC (permalink / raw)
To: linux-arm-kernel
As what former drcmr -1 value meant, add a this as a default to each
channel, ie. that by default no requestor line is used.
This is specifically used for network drivers smc91x and smc911x, and
needed for their port to slave maps.
Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
---
Since v1: changed -1 to U32_MAX
---
drivers/dma/pxa_dma.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c
index 9505334f9c6e..b31c28b67ad3 100644
--- a/drivers/dma/pxa_dma.c
+++ b/drivers/dma/pxa_dma.c
@@ -762,6 +762,8 @@ static void pxad_free_chan_resources(struct dma_chan *dchan)
dma_pool_destroy(chan->desc_pool);
chan->desc_pool = NULL;
+ chan->drcmr = U32_MAX;
+ chan->prio = PXAD_PRIO_LOWEST;
}
static void pxad_free_desc(struct virt_dma_desc *vd)
@@ -1386,6 +1388,9 @@ static int pxad_init_dmadev(struct platform_device *op,
c = devm_kzalloc(&op->dev, sizeof(*c), GFP_KERNEL);
if (!c)
return -ENOMEM;
+
+ c->drcmr = U32_MAX;
+ c->prio = PXAD_PRIO_LOWEST;
c->vc.desc_free = pxad_free_desc;
vchan_init(&c->vc, &pdev->slave);
init_waitqueue_head(&c->wq_state);
--
2.11.0
^ permalink raw reply related
* [PATCH] kbuild: add machine size to CHEKCFLAGS
From: Luc Van Oostenryck @ 2018-05-30 20:48 UTC (permalink / raw)
To: linux-arm-kernel
By default, sparse assumes a 64bit machine when compiled on x86-64
and 32bit when compiled on anything else.
This can of course create all sort of problems for the other archs, like
issuing false warnings ('shift too big (32) for type unsigned long'), or
worse, failing to emit legitimate warnings.
Fix this by adding the -m32/-m64 flag, depending on CONFIG_64BIT,
to CHECKFLAGS in the main Makefile (and so for all archs).
Also, remove the now unneeded -m32/-m64 in arch specific Makefiles.
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
---
Makefile | 3 +++
arch/alpha/Makefile | 2 +-
arch/arm/Makefile | 2 +-
arch/arm64/Makefile | 2 +-
arch/ia64/Makefile | 2 +-
arch/mips/Makefile | 3 ---
arch/parisc/Makefile | 2 +-
arch/sparc/Makefile | 2 +-
arch/x86/Makefile | 2 +-
9 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/Makefile b/Makefile
index 6c6610913..18379987c 100644
--- a/Makefile
+++ b/Makefile
@@ -881,6 +881,9 @@ endif
# insure the checker run with the right endianness
CHECKFLAGS += $(if $(CONFIG_CPU_BIG_ENDIAN),-mbig-endian,-mlittle-endian)
+# the checker needs the correct machine size
+CHECKFLAGS += $(if $(CONFIG_64BIT),-m64,-m32)
+
# Default kernel image to build when no specific target is given.
# KBUILD_IMAGE may be overruled on the command line or
# set in the environment
diff --git a/arch/alpha/Makefile b/arch/alpha/Makefile
index 2cc3cc519..c5ec8c09c 100644
--- a/arch/alpha/Makefile
+++ b/arch/alpha/Makefile
@@ -11,7 +11,7 @@
NM := $(NM) -B
LDFLAGS_vmlinux := -static -N #-relax
-CHECKFLAGS += -D__alpha__ -m64
+CHECKFLAGS += -D__alpha__
cflags-y := -pipe -mno-fp-regs -ffixed-8
cflags-y += $(call cc-option, -fno-jump-tables)
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index e4e537f27..f32a5468d 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -135,7 +135,7 @@ endif
KBUILD_CFLAGS +=$(CFLAGS_ABI) $(CFLAGS_ISA) $(arch-y) $(tune-y) $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,)) -msoft-float -Uarm
KBUILD_AFLAGS +=$(CFLAGS_ABI) $(AFLAGS_ISA) $(arch-y) $(tune-y) -include asm/unified.h -msoft-float
-CHECKFLAGS += -D__arm__ -m32
+CHECKFLAGS += -D__arm__
#Default value
head-y := arch/arm/kernel/head$(MMUEXT).o
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 87f7d2f9f..3c353b471 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -78,7 +78,7 @@ LDFLAGS += -maarch64linux
UTS_MACHINE := aarch64
endif
-CHECKFLAGS += -D__aarch64__ -m64
+CHECKFLAGS += -D__aarch64__
ifeq ($(CONFIG_ARM64_MODULE_PLTS),y)
KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/arm64/kernel/module.lds
diff --git a/arch/ia64/Makefile b/arch/ia64/Makefile
index 2dd7f519a..45f59808b 100644
--- a/arch/ia64/Makefile
+++ b/arch/ia64/Makefile
@@ -18,7 +18,7 @@ READELF := $(CROSS_COMPILE)readelf
export AWK
-CHECKFLAGS += -m64 -D__ia64=1 -D__ia64__=1 -D_LP64 -D__LP64__
+CHECKFLAGS += -D__ia64=1 -D__ia64__=1 -D_LP64 -D__LP64__
OBJCOPYFLAGS := --strip-all
LDFLAGS_vmlinux := -static
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 5e9fce076..e2122cca4 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -309,9 +309,6 @@ ifdef CONFIG_MIPS
CHECKFLAGS += $(shell $(CC) $(KBUILD_CFLAGS) -dM -E -x c /dev/null | \
egrep -vw '__GNUC_(|MINOR_|PATCHLEVEL_)_' | \
sed -e "s/^\#define /-D'/" -e "s/ /'='/" -e "s/$$/'/" -e 's/\$$/&&/g')
-ifdef CONFIG_64BIT
-CHECKFLAGS += -m64
-endif
endif
OBJCOPYFLAGS += --remove-section=.reginfo
diff --git a/arch/parisc/Makefile b/arch/parisc/Makefile
index 348ae4779..714284ea6 100644
--- a/arch/parisc/Makefile
+++ b/arch/parisc/Makefile
@@ -28,7 +28,7 @@ export LIBGCC
ifdef CONFIG_64BIT
UTS_MACHINE := parisc64
-CHECKFLAGS += -D__LP64__=1 -m64
+CHECKFLAGS += -D__LP64__=1
CC_ARCHES = hppa64
LD_BFD := elf64-hppa-linux
else # 32-bit
diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
index edac927e4..966a13d2b 100644
--- a/arch/sparc/Makefile
+++ b/arch/sparc/Makefile
@@ -39,7 +39,7 @@ else
# sparc64
#
-CHECKFLAGS += -D__sparc__ -D__sparc_v9__ -D__arch64__ -m64
+CHECKFLAGS += -D__sparc__ -D__sparc_v9__ -D__arch64__
LDFLAGS := -m elf64_sparc
export BITS := 64
UTS_MACHINE := sparc64
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 60135cbd9..f0a6ea224 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -94,7 +94,7 @@ ifeq ($(CONFIG_X86_32),y)
else
BITS := 64
UTS_MACHINE := x86_64
- CHECKFLAGS += -D__x86_64__ -m64
+ CHECKFLAGS += -D__x86_64__
biarch := -m64
KBUILD_AFLAGS += -m64
--
2.17.0
^ permalink raw reply related
* linux-next: manual merge of the regulator tree with the arm-soc tree
From: Janusz Krzysztofik @ 2018-05-30 20:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180530150711.2c7c1fe9@canb.auug.org.au>
On Wednesday, May 30, 2018 7:07:11 AM CEST Stephen Rothwell wrote:
> Hi all,
>
> Today's linux-next merge of the regulator tree got a conflict in:
>
> arch/arm/mach-omap1/board-ams-delta.c
>
> between commit:
>
> 0486738928bf ("ARM: OMAP1: ams-delta: add GPIO lookup tables")
>
> from the arm-soc tree and commit:
>
> 6059577cb28d ("regulator: fixed: Convert to use GPIO descriptor only")
>
> from the regulator tree.
>
> I fixed it up (see below - it may be better done) and can carry the fix
> as necessary.
Hi Stephen,
Your fix looks pretty good to me, but anyway, I will test it and confirm in a
day or two, as soon as I have access to the device.
Thanks,
Janusz
^ permalink raw reply
* [PATCH v7 00/10] drivers: Introduce firmware dnd clock river for ZynqMP core
From: Jolly Shah @ 2018-05-30 20:55 UTC (permalink / raw)
To: linux-arm-kernel
v7:
- Removed xilinx specific clock debugfs API support
- Added reviewed-by tags for FW and clock bindings
- Updated clock node name to clock-controller
v6:
- Broke patch series to have base FW driver and Clock driver user
- Incorporated code review comments from last FW and Clock driver patch series. Discussed below:
https://patchwork.kernel.org/patch/10230759/
https://patchwork.kernel.org/patch/10250047/
v5:
- Added ATF version check support
- Updated some functions to be static
- Minor function name corrections
v4:
- Changed clock setrate/getrate API prototype to support 64 bit rate
- Defined macros for get_node_status return values
- Moved DT node as a child of firmware
- Changed debugfs APIs to return data to debugfs buffer instead of dumping to kernel log
- Minor changes to incorporate other review comments from v3 patch series
v3:
- added some fixes to firmware-ggs.c
- updated pinmux get/set function argument names to specify function id instead of node id
- added new pinctrl query macros
- incorporated review comments from v2 patch series
v2:
- change SPDX-License-Identifier license text style
- split patch into multiple patches
- Updated copyrights
- Added ABI documentation
- incorporated logical review comments from previuos patch. Discussed below:
https://patchwork.kernel.org/patch/10150665/
Jolly Shah (1):
drivers: clk: Add ZynqMP clock driver
Rajan Vaja (9):
dt-bindings: firmware: Add bindings for ZynqMP firmware
firmware: xilinx: Add Zynqmp firmware driver
firmware: xilinx: Add zynqmp IOCTL API for device control
firmware: xilinx: Add query data API
firmware: xilinx: Add clock APIs
firmware: xilinx: Add debugfs interface
firmware: xilinx: Add debugfs for IOCTL API
firmware: xilinx: Add debugfs for query data API
dt-bindings: clock: Add bindings for ZynqMP clock driver
.../firmware/xilinx/xlnx,zynqmp-firmware.txt | 82 +++
arch/arm64/Kconfig.platforms | 1 +
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/zynqmp/Kconfig | 11 +
drivers/clk/zynqmp/Makefile | 4 +
drivers/clk/zynqmp/clk-gate-zynqmp.c | 146 ++++
drivers/clk/zynqmp/clk-mux-zynqmp.c | 150 +++++
drivers/clk/zynqmp/clk-zynqmp.h | 53 ++
drivers/clk/zynqmp/clkc.c | 737 +++++++++++++++++++++
drivers/clk/zynqmp/divider.c | 219 ++++++
drivers/clk/zynqmp/pll.c | 345 ++++++++++
drivers/firmware/Kconfig | 1 +
drivers/firmware/Makefile | 1 +
drivers/firmware/xilinx/Kconfig | 23 +
drivers/firmware/xilinx/Makefile | 5 +
drivers/firmware/xilinx/zynqmp-debug.c | 249 +++++++
drivers/firmware/xilinx/zynqmp-debug.h | 22 +
drivers/firmware/xilinx/zynqmp.c | 562 ++++++++++++++++
include/dt-bindings/clock/xlnx,zynqmp-clk.h | 116 ++++
include/linux/firmware/xlnx-zynqmp.h | 115 ++++
21 files changed, 2844 insertions(+)
create mode 100644 Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.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/clk-zynqmp.h
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 drivers/firmware/xilinx/Kconfig
create mode 100644 drivers/firmware/xilinx/Makefile
create mode 100644 drivers/firmware/xilinx/zynqmp-debug.c
create mode 100644 drivers/firmware/xilinx/zynqmp-debug.h
create mode 100644 drivers/firmware/xilinx/zynqmp.c
create mode 100644 include/dt-bindings/clock/xlnx,zynqmp-clk.h
create mode 100644 include/linux/firmware/xlnx-zynqmp.h
--
2.7.4
^ permalink raw reply
* [PATCH v7 01/10] dt-bindings: firmware: Add bindings for ZynqMP firmware
From: Jolly Shah @ 2018-05-30 20:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1527713725-1086-1-git-send-email-jollys@xilinx.com>
From: Rajan Vaja <rajanv@xilinx.com>
Add documentation to describe Xilinx ZynqMP firmware driver
bindings. Firmware driver provides an interface to firmware
APIs. Interface APIs can be used by any driver to communicate
to PMUFW (Platform Management Unit).
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
.../firmware/xilinx/xlnx,zynqmp-firmware.txt | 29 ++++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
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..1b431d9
--- /dev/null
+++ b/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
@@ -0,0 +1,29 @@
+-----------------------------------------------------------------
+Device Tree Bindings for the Xilinx Zynq MPSoC Firmware Interface
+-----------------------------------------------------------------
+
+The zynqmp-firmware node describes the interface to platform firmware.
+ZynqMP has an interface to communicate with secure firmware. Firmware
+driver provides an interface to firmware APIs. Interface APIs can be
+used by any driver to communicate to PMUFW(Platform Management Unit).
+These requests include clock management, pin control, device control,
+power management service, FPGA service and other platform management
+services.
+
+Required properties:
+ - compatible: Must contain: "xlnx,zynqmp-firmware"
+ - method: The method of calling the PM-API firmware layer.
+ Permitted values are:
+ - "smc" : SMC #0, following the SMCCC
+ - "hvc" : HVC #0, following the SMCCC
+
+-------
+Example
+-------
+
+firmware {
+ zynqmp_firmware: zynqmp-firmware {
+ compatible = "xlnx,zynqmp-firmware";
+ method = "smc";
+ };
+};
--
2.7.4
^ permalink raw reply related
* [PATCH v7 02/10] firmware: xilinx: Add Zynqmp firmware driver
From: Jolly Shah @ 2018-05-30 20:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1527713725-1086-1-git-send-email-jollys@xilinx.com>
From: Rajan Vaja <rajanv@xilinx.com>
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.
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
---
arch/arm64/Kconfig.platforms | 1 +
drivers/firmware/Kconfig | 1 +
drivers/firmware/Makefile | 1 +
drivers/firmware/xilinx/Kconfig | 16 ++
drivers/firmware/xilinx/Makefile | 4 +
drivers/firmware/xilinx/zynqmp.c | 337 +++++++++++++++++++++++++++++++++++
include/linux/firmware/xlnx-zynqmp.h | 63 +++++++
7 files changed, 423 insertions(+)
create mode 100644 drivers/firmware/xilinx/Kconfig
create mode 100644 drivers/firmware/xilinx/Makefile
create mode 100644 drivers/firmware/xilinx/zynqmp.c
create mode 100644 include/linux/firmware/xlnx-zynqmp.h
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index fbedbd8..6454458 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -274,6 +274,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 b7c7482..f41eb0d 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -257,5 +257,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 b248238..f90363e 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -31,3 +31,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..cce4e4f
--- /dev/null
+++ b/drivers/firmware/xilinx/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+# Kconfig for Xilinx firmwares
+
+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
+
+endmenu
diff --git a/drivers/firmware/xilinx/Makefile b/drivers/firmware/xilinx/Makefile
new file mode 100644
index 0000000..29f7bf2
--- /dev/null
+++ b/drivers/firmware/xilinx/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for Xilinx firmwares
+
+obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
new file mode 100644
index 0000000..70e335a
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Zynq MPSoC Firmware layer
+ *
+ * Copyright (C) 2014-2018 Xilinx, Inc.
+ *
+ * Michal Simek <michal.simek@xilinx.com>
+ * Davorin Mista <davorin.mista@aggios.com>
+ * Jolly Shah <jollys@xilinx.com>
+ * Rajan Vaja <rajanv@xilinx.com>
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/compiler.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <linux/firmware/xlnx-zynqmp.h>
+
+/**
+ * zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes
+ * @ret_status: PMUFW return code
+ *
+ * Return: corresponding Linux error code
+ */
+static 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 platform 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
+ *
+ * Invoke platform management function via SMC call (no hypervisor present).
+ *
+ * Return: Returns status, either success or error+reason
+ */
+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);
+ }
+
+ return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
+}
+
+/**
+ * do_fw_call_hvc() - Call system-level platform 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
+ *
+ * Invoke platform management function via HVC
+ * HVC-based for communication through hypervisor
+ * (no direct communication with ATF).
+ *
+ * Return: Returns status, either success or error+reason
+ */
+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);
+ }
+
+ return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
+}
+
+/**
+ * zynqmp_pm_invoke_fn() - Invoke the system-level platform 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
+ *
+ * Invoke platform 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 - Platform Management API ID.
+ *
+ * Return: Returns status, either success or error+reason
+ */
+int zynqmp_pm_invoke_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;
+static u32 pm_tz_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];
+ int ret;
+
+ if (!version)
+ return -EINVAL;
+
+ /* Check is PM API version already verified */
+ if (pm_api_version > 0) {
+ *version = pm_api_version;
+ return 0;
+ }
+ ret = zynqmp_pm_invoke_fn(PM_GET_API_VERSION, 0, 0, 0, 0, ret_payload);
+ *version = ret_payload[1];
+
+ return ret;
+}
+
+/**
+ * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version
+ * @version: Returned version value
+ *
+ * Return: Returns status, either success or error+reason
+ */
+static int zynqmp_pm_get_trustzone_version(u32 *version)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ if (!version)
+ return -EINVAL;
+
+ /* Check is PM trustzone version already verified */
+ if (pm_tz_version > 0) {
+ *version = pm_tz_version;
+ return 0;
+ }
+ ret = zynqmp_pm_invoke_fn(PM_GET_TRUSTZONE_VERSION, 0, 0,
+ 0, 0, ret_payload);
+ *version = ret_payload[1];
+
+ return ret;
+}
+
+/**
+ * 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.
+ *
+ * Return: Returns 0 on success or error code
+ */
+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;
+}
+
+static const struct zynqmp_eemi_ops eemi_ops = {
+ .get_api_version = zynqmp_pm_get_api_version,
+};
+
+/**
+ * zynqmp_pm_get_eemi_ops - Get eemi ops functions
+ *
+ * Return: pointer of eemi_ops structure
+ */
+const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void)
+{
+ return &eemi_ops;
+}
+EXPORT_SYMBOL_GPL(zynqmp_pm_get_eemi_ops);
+
+static int zynqmp_firmware_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ return of_platform_populate(dev->of_node, NULL, NULL, dev);
+}
+
+static const struct of_device_id zynqmp_firmware_of_match[] = {
+ {.compatible = "xlnx,zynqmp-firmware"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match);
+
+static struct platform_driver zynqmp_firmware_driver = {
+ .driver = {
+ .name = "zynqmp_firmware",
+ .of_match_table = zynqmp_firmware_of_match,
+ },
+ .probe = zynqmp_firmware_probe,
+};
+module_platform_driver(zynqmp_firmware_driver);
+
+static int __init zynqmp_plat_init(void)
+{
+ int ret;
+ struct device_node *np;
+
+ 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 zynqmp-firmware node is mandatory.
+ */
+ np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp-firmware");
+ if (!np) {
+ pr_warn("%s: zynqmp-firmware 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 Platform 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 Platform Management API v%d.%d\n", __func__,
+ pm_api_version >> 16, pm_api_version & 0xFFFF);
+
+ /* Check trustzone version number */
+ ret = zynqmp_pm_get_trustzone_version(&pm_tz_version);
+ if (ret)
+ panic("Legacy trustzone found without version support\n");
+
+ if (pm_tz_version < ZYNQMP_TZ_VERSION)
+ panic("%s Trustzone version error. Expected: v%d.%d - Found: v%d.%d\n",
+ __func__,
+ ZYNQMP_TZ_VERSION_MAJOR, ZYNQMP_TZ_VERSION_MINOR,
+ pm_tz_version >> 16, pm_tz_version & 0xFFFF);
+
+ pr_info("%s Trustzone version v%d.%d\n", __func__,
+ pm_tz_version >> 16, pm_tz_version & 0xFFFF);
+
+ of_node_put(np);
+
+ return ret;
+}
+early_initcall(zynqmp_plat_init);
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
new file mode 100644
index 0000000..cb63bed
--- /dev/null
+++ b/include/linux/firmware/xlnx-zynqmp.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Xilinx Zynq MPSoC Firmware layer
+ *
+ * Copyright (C) 2014-2018 Xilinx
+ *
+ * Michal Simek <michal.simek@xilinx.com>
+ * Davorin Mista <davorin.mista@aggios.com>
+ * Jolly Shah <jollys@xilinx.com>
+ * Rajan Vaja <rajanv@xilinx.com>
+ */
+
+#ifndef __FIRMWARE_ZYNQMP_H__
+#define __FIRMWARE_ZYNQMP_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_TZ_VERSION_MAJOR 1
+#define ZYNQMP_TZ_VERSION_MINOR 0
+
+#define ZYNQMP_TZ_VERSION ((ZYNQMP_TZ_VERSION_MAJOR << 16) | \
+ ZYNQMP_TZ_VERSION_MINOR)
+
+/* SMC SIP service Call Function Identifier Prefix */
+#define PM_SIP_SVC 0xC2000000
+#define PM_GET_TRUSTZONE_VERSION 0xa03
+
+/* Number of 32bits values in payload */
+#define PAYLOAD_ARG_CNT 4U
+
+enum pm_api_id {
+ PM_GET_API_VERSION = 1,
+};
+
+/* 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,
+};
+
+struct zynqmp_eemi_ops {
+ int (*get_api_version)(u32 *version);
+};
+
+#if IS_REACHABLE(CONFIG_ARCH_ZYNQMP)
+const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void);
+#else
+static inline struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void)
+{
+ return NULL;
+}
+#endif
+
+#endif /* __FIRMWARE_ZYNQMP_H__ */
--
2.7.4
^ permalink raw reply related
* [PATCH v7 03/10] firmware: xilinx: Add zynqmp IOCTL API for device control
From: Jolly Shah @ 2018-05-30 20:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1527713725-1086-1-git-send-email-jollys@xilinx.com>
From: Rajan Vaja <rajanv@xilinx.com>
Add ZynqMP firmware IOCTL API to control and configure
devices like PLLs, SD, Gem, etc.
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
---
drivers/firmware/xilinx/zynqmp.c | 20 ++++++++++++++++++++
include/linux/firmware/xlnx-zynqmp.h | 2 ++
2 files changed, 22 insertions(+)
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
index 70e335a..34c5ad5 100644
--- a/drivers/firmware/xilinx/zynqmp.c
+++ b/drivers/firmware/xilinx/zynqmp.c
@@ -241,8 +241,28 @@ static int get_set_conduit_method(struct device_node *np)
return 0;
}
+/**
+ * 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.
+ *
+ * Return: Returns status, either success or error+reason
+ */
+static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2,
+ u32 *out)
+{
+ return zynqmp_pm_invoke_fn(PM_IOCTL, node_id, ioctl_id,
+ arg1, arg2, out);
+}
+
static const struct zynqmp_eemi_ops eemi_ops = {
.get_api_version = zynqmp_pm_get_api_version,
+ .ioctl = zynqmp_pm_ioctl,
};
/**
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
index cb63bed..2eec6e7 100644
--- a/include/linux/firmware/xlnx-zynqmp.h
+++ b/include/linux/firmware/xlnx-zynqmp.h
@@ -34,6 +34,7 @@
enum pm_api_id {
PM_GET_API_VERSION = 1,
+ PM_IOCTL = 34,
};
/* PMU-FW return status codes */
@@ -49,6 +50,7 @@ enum pm_ret_status {
struct zynqmp_eemi_ops {
int (*get_api_version)(u32 *version);
+ int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out);
};
#if IS_REACHABLE(CONFIG_ARCH_ZYNQMP)
--
2.7.4
^ permalink raw reply related
* [PATCH v7 04/10] firmware: xilinx: Add query data API
From: Jolly Shah @ 2018-05-30 20:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1527713725-1086-1-git-send-email-jollys@xilinx.com>
From: Rajan Vaja <rajanv@xilinx.com>
Add ZynqMP firmware query data API to query platform
specific information(clocks, pins) from firmware.
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
---
drivers/firmware/xilinx/zynqmp.c | 14 ++++++++++++++
include/linux/firmware/xlnx-zynqmp.h | 20 ++++++++++++++++++++
2 files changed, 34 insertions(+)
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
index 34c5ad5..86d9bb8 100644
--- a/drivers/firmware/xilinx/zynqmp.c
+++ b/drivers/firmware/xilinx/zynqmp.c
@@ -260,9 +260,23 @@ static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2,
arg1, arg2, out);
}
+/**
+ * zynqmp_pm_query_data() - Get query data from firmware
+ * @qdata: Variable to the zynqmp_pm_query_data structure
+ * @out: Returned output value
+ *
+ * Return: Returns status, either success or error+reason
+ */
+static int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out)
+{
+ return zynqmp_pm_invoke_fn(PM_QUERY_DATA, qdata.qid, qdata.arg1,
+ qdata.arg2, qdata.arg3, out);
+}
+
static const struct zynqmp_eemi_ops eemi_ops = {
.get_api_version = zynqmp_pm_get_api_version,
.ioctl = zynqmp_pm_ioctl,
+ .query_data = zynqmp_pm_query_data,
};
/**
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
index 2eec6e7..354385d 100644
--- a/include/linux/firmware/xlnx-zynqmp.h
+++ b/include/linux/firmware/xlnx-zynqmp.h
@@ -35,6 +35,7 @@
enum pm_api_id {
PM_GET_API_VERSION = 1,
PM_IOCTL = 34,
+ PM_QUERY_DATA,
};
/* PMU-FW return status codes */
@@ -48,9 +49,28 @@ enum pm_ret_status {
XST_PM_ABORT_SUSPEND,
};
+enum pm_query_id {
+ PM_QID_INVALID,
+};
+
+/**
+ * struct zynqmp_pm_query_data - PM query data structure
+ * @qid: query ID
+ * @arg1: Argument 1 of query data
+ * @arg2: Argument 2 of query data
+ * @arg3: Argument 3 of query data
+ */
+struct zynqmp_pm_query_data {
+ u32 qid;
+ u32 arg1;
+ u32 arg2;
+ u32 arg3;
+};
+
struct zynqmp_eemi_ops {
int (*get_api_version)(u32 *version);
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);
};
#if IS_REACHABLE(CONFIG_ARCH_ZYNQMP)
--
2.7.4
^ permalink raw reply related
* [PATCH v7 05/10] firmware: xilinx: Add clock APIs
From: Jolly Shah @ 2018-05-30 20:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1527713725-1086-1-git-send-email-jollys@xilinx.com>
From: Rajan Vaja <rajanv@xilinx.com>
Add clock APIs to control clocks through firmware
interface.
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
---
drivers/firmware/xilinx/zynqmp.c | 186 ++++++++++++++++++++++++++++++++++-
include/linux/firmware/xlnx-zynqmp.h | 30 ++++++
2 files changed, 214 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
index 86d9bb8..c764d6e 100644
--- a/drivers/firmware/xilinx/zynqmp.c
+++ b/drivers/firmware/xilinx/zynqmp.c
@@ -269,14 +269,196 @@ static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2,
*/
static int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out)
{
- return zynqmp_pm_invoke_fn(PM_QUERY_DATA, qdata.qid, qdata.arg1,
- qdata.arg2, qdata.arg3, out);
+ int ret;
+
+ ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, qdata.qid, qdata.arg1,
+ qdata.arg2, qdata.arg3, out);
+
+ /*
+ * For clock name query, all bytes in SMC response are clock name
+ * characters and return code is always success. For invalid clocks,
+ * clock name bytes would be 0s.
+ */
+ return qdata.qid == PM_QID_CLOCK_GET_NAME ? 0 : ret;
+}
+
+/**
+ * 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 zynqmp_pm_invoke_fn(PM_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 zynqmp_pm_invoke_fn(PM_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];
+ int ret;
+
+ ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETSTATE, clock_id, 0,
+ 0, 0, ret_payload);
+ *state = ret_payload[1];
+
+ return ret;
+}
+
+/**
+ * zynqmp_pm_clock_setdivider() - Set the clock divider for given id
+ * @clock_id: ID of the clock
+ * @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 zynqmp_pm_invoke_fn(PM_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
+ * @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];
+ int ret;
+
+ ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETDIVIDER, clock_id, 0,
+ 0, 0, ret_payload);
+ *divider = ret_payload[1];
+
+ return ret;
+}
+
+/**
+ * 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, u64 rate)
+{
+ return zynqmp_pm_invoke_fn(PM_CLOCK_SETRATE, clock_id,
+ rate & 0xFFFFFFFF,
+ (rate >> 32) & 0xFFFFFFFF,
+ 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, u64 *rate)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETRATE, clock_id, 0,
+ 0, 0, ret_payload);
+ *rate = ((u64)ret_payload[2] << 32) | ret_payload[1];
+
+ return ret;
+}
+
+/**
+ * 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 zynqmp_pm_invoke_fn(PM_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];
+ int ret;
+
+ ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETPARENT, clock_id, 0,
+ 0, 0, ret_payload);
+ *parent_id = ret_payload[1];
+
+ return ret;
}
static const struct zynqmp_eemi_ops eemi_ops = {
.get_api_version = zynqmp_pm_get_api_version,
.ioctl = zynqmp_pm_ioctl,
.query_data = zynqmp_pm_query_data,
+ .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,
};
/**
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
index 354385d..29fb352 100644
--- a/include/linux/firmware/xlnx-zynqmp.h
+++ b/include/linux/firmware/xlnx-zynqmp.h
@@ -36,6 +36,15 @@ enum pm_api_id {
PM_GET_API_VERSION = 1,
PM_IOCTL = 34,
PM_QUERY_DATA,
+ PM_CLOCK_ENABLE,
+ PM_CLOCK_DISABLE,
+ PM_CLOCK_GETSTATE,
+ PM_CLOCK_SETDIVIDER,
+ PM_CLOCK_GETDIVIDER,
+ PM_CLOCK_SETRATE,
+ PM_CLOCK_GETRATE,
+ PM_CLOCK_SETPARENT,
+ PM_CLOCK_GETPARENT,
};
/* PMU-FW return status codes */
@@ -49,8 +58,20 @@ enum pm_ret_status {
XST_PM_ABORT_SUSPEND,
};
+enum pm_ioctl_id {
+ IOCTL_SET_PLL_FRAC_MODE = 8,
+ IOCTL_GET_PLL_FRAC_MODE,
+ IOCTL_SET_PLL_FRAC_DATA,
+ IOCTL_GET_PLL_FRAC_DATA,
+};
+
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,
};
/**
@@ -71,6 +92,15 @@ struct zynqmp_eemi_ops {
int (*get_api_version)(u32 *version);
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 (*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, u64 rate);
+ int (*clock_getrate)(u32 clock_id, u64 *rate);
+ int (*clock_setparent)(u32 clock_id, u32 parent_id);
+ int (*clock_getparent)(u32 clock_id, u32 *parent_id);
};
#if IS_REACHABLE(CONFIG_ARCH_ZYNQMP)
--
2.7.4
^ permalink raw reply related
* [PATCH v7 06/10] firmware: xilinx: Add debugfs interface
From: Jolly Shah @ 2018-05-30 20:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1527713725-1086-1-git-send-email-jollys@xilinx.com>
From: Rajan Vaja <rajanv@xilinx.com>
Firmware-debug provides debugfs interface to all APIs.
Debugfs can be used to call firmware APIs with required
parameters.
Usage:
* Calling firmware API through debugfs:
# echo "<api-name> <arg1> .. <argn>" > /sys/.../zynqmp-firmware/pm
* Read output of last called firmware API:
# cat /sys/.../zynqmp-firmware/pm
Refer ug1200 for more information on these APIs:
* https://www.xilinx.com/support/documentation/user_guides/ug1200-eemi-api.pdf
Add basic debugfs file to get API version.
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
---
drivers/firmware/xilinx/Kconfig | 7 ++
drivers/firmware/xilinx/Makefile | 1 +
drivers/firmware/xilinx/zynqmp-debug.c | 212 +++++++++++++++++++++++++++++++++
drivers/firmware/xilinx/zynqmp-debug.h | 22 ++++
drivers/firmware/xilinx/zynqmp.c | 9 ++
5 files changed, 251 insertions(+)
create mode 100644 drivers/firmware/xilinx/zynqmp-debug.c
create mode 100644 drivers/firmware/xilinx/zynqmp-debug.h
diff --git a/drivers/firmware/xilinx/Kconfig b/drivers/firmware/xilinx/Kconfig
index cce4e4f..10faa10 100644
--- a/drivers/firmware/xilinx/Kconfig
+++ b/drivers/firmware/xilinx/Kconfig
@@ -13,4 +13,11 @@ config ZYNQMP_FIRMWARE
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 ZYNQMP_FIRMWARE && DEBUG_FS
+ help
+ Say yes to enable ZynqMP firmware interface debug APIs.
+ In doubt, say N
+
endmenu
diff --git a/drivers/firmware/xilinx/Makefile b/drivers/firmware/xilinx/Makefile
index 29f7bf2..875a537 100644
--- a/drivers/firmware/xilinx/Makefile
+++ b/drivers/firmware/xilinx/Makefile
@@ -2,3 +2,4 @@
# Makefile for Xilinx firmwares
obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o
+obj-$(CONFIG_ZYNQMP_FIRMWARE_DEBUG) += zynqmp-debug.o
diff --git a/drivers/firmware/xilinx/zynqmp-debug.c b/drivers/firmware/xilinx/zynqmp-debug.c
new file mode 100644
index 0000000..4800366
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp-debug.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Zynq MPSoC Firmware layer for debugfs APIs
+ *
+ * Copyright (C) 2014-2018 Xilinx, Inc.
+ *
+ * Michal Simek <michal.simek@xilinx.com>
+ * Davorin Mista <davorin.mista@aggios.com>
+ * Jolly Shah <jollys@xilinx.com>
+ * Rajan Vaja <rajanv@xilinx.com>
+ */
+
+#include <linux/compiler.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+
+#include <linux/firmware/xlnx-zynqmp.h>
+#include "zynqmp-debug.h"
+
+#define PM_API_NAME_LEN 50
+
+struct pm_api_info {
+ u32 api_id;
+ char api_name[PM_API_NAME_LEN];
+ char api_name_len;
+};
+
+static char debugfs_buf[PAGE_SIZE];
+
+#define PM_API(id) {id, #id, strlen(#id)}
+static struct pm_api_info pm_api_list[] = {
+ PM_API(PM_GET_API_VERSION),
+};
+
+/**
+ * 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;
+}
+
+/**
+ * get_pm_api_id() - Extract API-ID from a PM-API request
+ * @pm_api_req: Entered PM-API argument in string format
+ * @pm_id: API-ID
+ *
+ * Return: 0 on success else error code
+ */
+static int get_pm_api_id(char *pm_api_req, u32 *pm_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pm_api_list) ; i++) {
+ if (!strncasecmp(pm_api_req, pm_api_list[i].api_name,
+ pm_api_list[i].api_name_len)) {
+ *pm_id = pm_api_list[i].api_id;
+ break;
+ }
+ }
+
+ /* If no name was entered look for PM-API ID instead */
+ if (i == ARRAY_SIZE(pm_api_list) && kstrtouint(pm_api_req, 10, pm_id))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret)
+{
+ const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
+ u32 pm_api_version;
+ int ret;
+
+ if (!eemi_ops)
+ return -ENXIO;
+
+ switch (pm_id) {
+ case PM_GET_API_VERSION:
+ ret = eemi_ops->get_api_version(&pm_api_version);
+ sprintf(debugfs_buf, "PM-API Version = %d.%d\n",
+ pm_api_version >> 16, pm_api_version & 0xffff);
+ break;
+ default:
+ sprintf(debugfs_buf, "Unsupported PM-API request\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * 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
+ *
+ * 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
+ *
+ * Return: Number of bytes copied if PM-API request succeeds,
+ * the corresponding error code otherwise
+ */
+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] = {0, 0, 0, 0};
+ /* Return values from PM APIs calls */
+ u32 pm_api_ret[4] = {0, 0, 0, 0};
+
+ int ret;
+ int i = 0;
+
+ strcpy(debugfs_buf, "");
+
+ if (*off != 0 || len == 0)
+ return -EINVAL;
+
+ kern_buff = kzalloc(len, GFP_KERNEL);
+ if (!kern_buff)
+ return -ENOMEM;
+
+ tmp_buff = kern_buff;
+
+ 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, " ");
+
+ ret = get_pm_api_id(pm_api_req, &pm_id);
+ if (ret < 0)
+ goto err;
+
+ /* Read node_id and arguments from the PM-API request */
+ 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, " ");
+ }
+
+ ret = process_api_request(pm_id, pm_api_arg, pm_api_ret);
+
+err:
+ kfree(tmp_buff);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+/**
+ * zynqmp_pm_debugfs_api_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
+ * else error code
+ */
+static ssize_t zynqmp_pm_debugfs_api_read(struct file *file, char __user *ptr,
+ size_t len, loff_t *off)
+{
+ return simple_read_from_buffer(ptr, len, off, debugfs_buf,
+ strlen(debugfs_buf));
+}
+
+/* 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_read,
+};
+
+/**
+ * zynqmp_pm_api_debugfs_init - Initialize debugfs interface
+ *
+ * Return: None
+ */
+void zynqmp_pm_api_debugfs_init(void)
+{
+ struct dentry *root_dir;
+
+ /* Initialize debugfs interface */
+ root_dir = debugfs_create_dir("zynqmp-firmware", NULL);
+ debugfs_create_file("pm", 0660, root_dir, NULL,
+ &fops_zynqmp_pm_dbgfs);
+}
diff --git a/drivers/firmware/xilinx/zynqmp-debug.h b/drivers/firmware/xilinx/zynqmp-debug.h
new file mode 100644
index 0000000..3303b37
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp-debug.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Xilinx Zynq MPSoC Firmware layer
+ *
+ * Copyright (C) 2014-2018 Xilinx
+ *
+ * Michal Simek <michal.simek@xilinx.com>
+ * Davorin Mista <davorin.mista@aggios.com>
+ * Jolly Shah <jollys@xilinx.com>
+ * Rajan Vaja <rajanv@xilinx.com>
+ */
+
+#ifndef __FIRMWARE_ZYNQMP_DEBUG_H__
+#define __FIRMWARE_ZYNQMP_DEBUG_H__
+
+#if IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE_DEBUG)
+void zynqmp_pm_api_debugfs_init(void);
+#else
+static inline void zynqmp_pm_api_debugfs_init(void) { }
+#endif
+
+#endif /* __FIRMWARE_ZYNQMP_DEBUG_H__ */
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
index c764d6e..83d2699 100644
--- a/drivers/firmware/xilinx/zynqmp.c
+++ b/drivers/firmware/xilinx/zynqmp.c
@@ -21,6 +21,7 @@
#include <linux/uaccess.h>
#include <linux/firmware/xlnx-zynqmp.h>
+#include "zynqmp-debug.h"
/**
* zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes
@@ -551,3 +552,11 @@ static int __init zynqmp_plat_init(void)
return ret;
}
early_initcall(zynqmp_plat_init);
+
+static int zynqmp_firmware_init(void)
+{
+ zynqmp_pm_api_debugfs_init();
+
+ return 0;
+}
+device_initcall(zynqmp_firmware_init);
--
2.7.4
^ permalink raw reply related
* [PATCH v7 07/10] firmware: xilinx: Add debugfs for IOCTL API
From: Jolly Shah @ 2018-05-30 20:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1527713725-1086-1-git-send-email-jollys@xilinx.com>
From: Rajan Vaja <rajanv@xilinx.com>
Add debugfs file to set/get IOCTL using debugfs interface.
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
---
drivers/firmware/xilinx/zynqmp-debug.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/firmware/xilinx/zynqmp-debug.c b/drivers/firmware/xilinx/zynqmp-debug.c
index 4800366..148dbff 100644
--- a/drivers/firmware/xilinx/zynqmp-debug.c
+++ b/drivers/firmware/xilinx/zynqmp-debug.c
@@ -32,6 +32,7 @@ static char debugfs_buf[PAGE_SIZE];
#define PM_API(id) {id, #id, strlen(#id)}
static struct pm_api_info pm_api_list[] = {
PM_API(PM_GET_API_VERSION),
+ PM_API(PM_IOCTL),
};
/**
@@ -95,6 +96,15 @@ static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret)
sprintf(debugfs_buf, "PM-API Version = %d.%d\n",
pm_api_version >> 16, pm_api_version & 0xffff);
break;
+ case PM_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 (!ret && (pm_api_arg[1] == IOCTL_GET_PLL_FRAC_MODE ||
+ pm_api_arg[1] == IOCTL_GET_PLL_FRAC_DATA))
+ sprintf(debugfs_buf, "IOCTL return value: %u\n",
+ pm_api_ret[1]);
+ break;
default:
sprintf(debugfs_buf, "Unsupported PM-API request\n");
ret = -EINVAL;
--
2.7.4
^ permalink raw reply related
* [PATCH v7 08/10] firmware: xilinx: Add debugfs for query data API
From: Jolly Shah @ 2018-05-30 20:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1527713725-1086-1-git-send-email-jollys@xilinx.com>
From: Rajan Vaja <rajanv@xilinx.com>
Add debugfs file to query platform specific data from firmware
using debugfs interface.
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
---
drivers/firmware/xilinx/zynqmp-debug.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/firmware/xilinx/zynqmp-debug.c b/drivers/firmware/xilinx/zynqmp-debug.c
index 148dbff..1cb69f7 100644
--- a/drivers/firmware/xilinx/zynqmp-debug.c
+++ b/drivers/firmware/xilinx/zynqmp-debug.c
@@ -33,6 +33,7 @@ static char debugfs_buf[PAGE_SIZE];
static struct pm_api_info pm_api_list[] = {
PM_API(PM_GET_API_VERSION),
PM_API(PM_IOCTL),
+ PM_API(PM_QUERY_DATA),
};
/**
@@ -105,6 +106,32 @@ static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret)
sprintf(debugfs_buf, "IOCTL return value: %u\n",
pm_api_ret[1]);
break;
+ case PM_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);
+ if (ret)
+ break;
+
+ if (qdata.qid == PM_QID_CLOCK_GET_NAME)
+ sprintf(debugfs_buf, "Clock name = %s\n",
+ (char *)pm_api_ret);
+ else if (qdata.qid == PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS)
+ sprintf(debugfs_buf, "Multiplier = %d, Divider = %d\n",
+ pm_api_ret[1], pm_api_ret[2]);
+ else
+ sprintf(debugfs_buf,
+ "data[0] = 0x%08x\ndata[1] = 0x%08x\n data[2] = 0x%08x\ndata[3] = 0x%08x\n",
+ pm_api_ret[0], pm_api_ret[1],
+ pm_api_ret[2], pm_api_ret[3]);
+ break;
+ }
default:
sprintf(debugfs_buf, "Unsupported PM-API request\n");
ret = -EINVAL;
--
2.7.4
^ permalink raw reply related
* [PATCH v7 09/10] dt-bindings: clock: Add bindings for ZynqMP clock driver
From: Jolly Shah @ 2018-05-30 20:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1527713725-1086-1-git-send-email-jollys@xilinx.com>
From: Rajan Vaja <rajanv@xilinx.com>
Add documentation to describe Xilinx ZynqMP clock driver
bindings.
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
.../firmware/xilinx/xlnx,zynqmp-firmware.txt | 53 ++++++++++
include/dt-bindings/clock/xlnx,zynqmp-clk.h | 116 +++++++++++++++++++++
2 files changed, 169 insertions(+)
create mode 100644 include/dt-bindings/clock/xlnx,zynqmp-clk.h
diff --git a/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt b/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
index 1b431d9..d215d15 100644
--- a/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
+++ b/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
@@ -17,6 +17,53 @@ Required properties:
- "smc" : SMC #0, following the SMCCC
- "hvc" : HVC #0, following the SMCCC
+--------------------------------------------------------------------------
+Device Tree Clock bindings for the Zynq Ultrascale+ MPSoC controlled using
+Zynq MPSoC firmware interface
+--------------------------------------------------------------------------
+The clock controller is a h/w block 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.
+
+See clock_bindings.txt for more information on the generic clock bindings.
+
+Required properties:
+ - #clock-cells: Must be 1
+ - compatible: Must contain: "xlnx,zynqmp-clk"
+ - 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 clock names 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:
+ - 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 are registered based on clock information received
+from firmware. Output clocks indexes are mentioned in
+include/dt-bindings/clock/xlnx,zynqmp-clk.h.
+
-------
Example
-------
@@ -25,5 +72,11 @@ firmware {
zynqmp_firmware: zynqmp-firmware {
compatible = "xlnx,zynqmp-firmware";
method = "smc";
+ zynqmp_clk : clock-controller {
+ #clock-cells = <1>;
+ compatible = "xlnx,zynqmp-clk";
+ clocks = <&pss_ref_clk>, <&video_clk>, <&pss_alt_ref_clk>, <&aux_ref_clk>, <>_crx_ref_clk>;
+ clock-names = "pss_ref_clk", "video_clk", "pss_alt_ref_clk","aux_ref_clk", "gt_crx_ref_clk";
+ };
};
};
diff --git a/include/dt-bindings/clock/xlnx,zynqmp-clk.h b/include/dt-bindings/clock/xlnx,zynqmp-clk.h
new file mode 100644
index 0000000..4aebe6e
--- /dev/null
+++ b/include/dt-bindings/clock/xlnx,zynqmp-clk.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Xilinx Zynq MPSoC Firmware layer
+ *
+ * Copyright (C) 2014-2018 Xilinx, Inc.
+ *
+ */
+
+#ifndef _DT_BINDINGS_CLK_ZYNQMP_H
+#define _DT_BINDINGS_CLK_ZYNQMP_H
+
+#define IOPLL 0
+#define RPLL 1
+#define APLL 2
+#define DPLL 3
+#define VPLL 4
+#define IOPLL_TO_FPD 5
+#define RPLL_TO_FPD 6
+#define APLL_TO_LPD 7
+#define DPLL_TO_LPD 8
+#define VPLL_TO_LPD 9
+#define ACPU 10
+#define ACPU_HALF 11
+#define DBF_FPD 12
+#define DBF_LPD 13
+#define DBG_TRACE 14
+#define DBG_TSTMP 15
+#define DP_VIDEO_REF 16
+#define DP_AUDIO_REF 17
+#define DP_STC_REF 18
+#define GDMA_REF 19
+#define DPDMA_REF 20
+#define DDR_REF 21
+#define SATA_REF 22
+#define PCIE_REF 23
+#define GPU_REF 24
+#define GPU_PP0_REF 25
+#define GPU_PP1_REF 26
+#define TOPSW_MAIN 27
+#define TOPSW_LSBUS 28
+#define GTGREF0_REF 29
+#define LPD_SWITCH 30
+#define LPD_LSBUS 31
+#define USB0_BUS_REF 32
+#define USB1_BUS_REF 33
+#define USB3_DUAL_REF 34
+#define USB0 35
+#define USB1 36
+#define CPU_R5 37
+#define CPU_R5_CORE 38
+#define CSU_SPB 39
+#define CSU_PLL 40
+#define PCAP 41
+#define IOU_SWITCH 42
+#define GEM_TSU_REF 43
+#define GEM_TSU 44
+#define GEM0_REF 45
+#define GEM1_REF 46
+#define GEM2_REF 47
+#define GEM3_REF 48
+#define GEM0_TX 49
+#define GEM1_TX 50
+#define GEM2_TX 51
+#define GEM3_TX 52
+#define QSPI_REF 53
+#define SDIO0_REF 54
+#define SDIO1_REF 55
+#define UART0_REF 56
+#define UART1_REF 57
+#define SPI0_REF 58
+#define SPI1_REF 59
+#define NAND_REF 60
+#define I2C0_REF 61
+#define I2C1_REF 62
+#define CAN0_REF 63
+#define CAN1_REF 64
+#define CAN0 65
+#define CAN1 66
+#define DLL_REF 67
+#define ADMA_REF 68
+#define TIMESTAMP_REF 69
+#define AMS_REF 70
+#define PL0_REF 71
+#define PL1_REF 72
+#define PL2_REF 73
+#define PL3_REF 74
+#define WDT 75
+#define IOPLL_INT 76
+#define IOPLL_PRE_SRC 77
+#define IOPLL_HALF 78
+#define IOPLL_INT_MUX 79
+#define IOPLL_POST_SRC 80
+#define RPLL_INT 81
+#define RPLL_PRE_SRC 82
+#define RPLL_HALF 83
+#define RPLL_INT_MUX 84
+#define RPLL_POST_SRC 85
+#define APLL_INT 86
+#define APLL_PRE_SRC 87
+#define APLL_HALF 88
+#define APLL_INT_MUX 89
+#define APLL_POST_SRC 90
+#define DPLL_INT 91
+#define DPLL_PRE_SRC 92
+#define DPLL_HALF 93
+#define DPLL_INT_MUX 94
+#define DPLL_POST_SRC 95
+#define VPLL_INT 96
+#define VPLL_PRE_SRC 97
+#define VPLL_HALF 98
+#define VPLL_INT_MUX 99
+#define VPLL_POST_SRC 100
+#define CAN0_MIO 101
+#define CAN1_MIO 102
+
+#endif
--
2.7.4
^ permalink raw reply related
* [PATCH v7 10/10] drivers: clk: Add ZynqMP clock driver
From: Jolly Shah @ 2018-05-30 20:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1527713725-1086-1-git-send-email-jollys@xilinx.com>
From: Jolly Shah <jolly.shah@xilinx.com>
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: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Tejas Patel <tejasp@xilinx.com>
Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
---
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/zynqmp/Kconfig | 11 +
drivers/clk/zynqmp/Makefile | 4 +
drivers/clk/zynqmp/clk-gate-zynqmp.c | 146 +++++++
drivers/clk/zynqmp/clk-mux-zynqmp.c | 150 +++++++
drivers/clk/zynqmp/clk-zynqmp.h | 53 +++
drivers/clk/zynqmp/clkc.c | 737 +++++++++++++++++++++++++++++++++++
drivers/clk/zynqmp/divider.c | 219 +++++++++++
drivers/clk/zynqmp/pll.c | 345 ++++++++++++++++
10 files changed, 1667 insertions(+)
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/clk-zynqmp.h
create mode 100644 drivers/clk/zynqmp/clkc.c
create mode 100644 drivers/clk/zynqmp/divider.c
create mode 100644 drivers/clk/zynqmp/pll.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 98ce9fc..a2ebcf7 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -252,6 +252,7 @@ source "drivers/clk/sprd/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 71ec41e..b6ac0d2 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -100,3 +100,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..fe815f7
--- /dev/null
+++ b/drivers/clk/zynqmp/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config COMMON_CLK_ZYNQMP
+ bool "Support for Xilinx ZynqMP Ultrascale+ clock controllers"
+ depends on OF
+ depends on ARCH_ZYNQMP || COMPILE_TEST
+ depends on ZYNQMP_FIRMWARE
+ 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..0ec24bf
--- /dev/null
+++ b/drivers/clk/zynqmp/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+# 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..b927eb1
--- /dev/null
+++ b/drivers/clk/zynqmp/clk-gate-zynqmp.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Zynq UltraScale+ MPSoC clock controller
+ *
+ * Copyright (C) 2016-2018 Xilinx
+ *
+ * Gated clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include "clk-zynqmp.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 on success else error code
+ */
+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;
+ const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
+
+ 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 ret;
+}
+
+/*
+ * 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;
+ const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
+
+ 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 else error code
+ */
+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 = zynqmp_pm_get_eemi_ops();
+
+ 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 -EIO;
+ }
+
+ 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
+ * @parent: name of this clock's parent
+ * @flags: framework-specific flags for this clock
+ * @clk_gate_flags: gate-specific flags for this clock
+ *
+ * Return: clock hardware of the registered clock gate
+ */
+struct clk_hw *zynqmp_clk_register_gate(struct device *dev, const char *name,
+ u32 clk_id, const char *parent,
+ unsigned long flags,
+ u8 clk_gate_flags)
+{
+ struct zynqmp_clk_gate *gate;
+ struct clk_hw *hw;
+ int ret;
+ 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 = &parent;
+ init.num_parents = 1;
+
+ /* struct clk_gate assignments */
+ gate->flags = clk_gate_flags;
+ gate->hw.init = &init;
+ gate->clk_id = clk_id;
+
+ hw = &gate->hw;
+ ret = clk_hw_register(dev, hw);
+ if (ret) {
+ kfree(gate);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
diff --git a/drivers/clk/zynqmp/clk-mux-zynqmp.c b/drivers/clk/zynqmp/clk-mux-zynqmp.c
new file mode 100644
index 0000000..a0b452d
--- /dev/null
+++ b/drivers/clk/zynqmp/clk-mux-zynqmp.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Zynq UltraScale+ MPSoC mux
+ *
+ * Copyright (C) 2016-2018 Xilinx
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include "clk-zynqmp.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 = zynqmp_pm_get_eemi_ops();
+
+ 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);
+
+ return val;
+}
+
+/**
+ * zynqmp_clk_mux_set_parent() - Set parent of clock
+ * @hw: handle between common and hardware-specific interfaces
+ * @index: Parent index
+ *
+ * Return: Returns status, either success or error+reason
+ */
+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 = zynqmp_pm_get_eemi_ops();
+
+ 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 ret;
+}
+
+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() - 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
+ * @parents: 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 hardware of the registered clock mux
+ */
+struct clk_hw *zynqmp_clk_register_mux(struct device *dev, const char *name,
+ u32 clk_id,
+ const char * const *parents,
+ u8 num_parents,
+ unsigned long flags,
+ u8 clk_mux_flags)
+{
+ struct zynqmp_clk_mux *mux;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ /* 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 = parents;
+ init.num_parents = num_parents;
+
+ /* struct clk_mux assignments */
+ mux->flags = clk_mux_flags;
+ mux->hw.init = &init;
+ mux->clk_id = clk_id;
+
+ hw = &mux->hw;
+ ret = clk_hw_register(dev, hw);
+ if (ret) {
+ kfree(hw);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
+EXPORT_SYMBOL_GPL(zynqmp_clk_register_mux);
diff --git a/drivers/clk/zynqmp/clk-zynqmp.h b/drivers/clk/zynqmp/clk-zynqmp.h
new file mode 100644
index 0000000..57e81d45
--- /dev/null
+++ b/drivers/clk/zynqmp/clk-zynqmp.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2016-2018 Xilinx
+ */
+
+#ifndef __LINUX_CLK_ZYNQMP_H_
+#define __LINUX_CLK_ZYNQMP_H_
+
+#include <linux/spinlock.h>
+
+#include <linux/firmware/xlnx-zynqmp.h>
+
+/* Clock APIs payload parameters */
+#define CLK_GET_NAME_RESP_LEN 16
+#define CLK_GET_TOPOLOGY_RESP_WORDS 3
+#define CLK_GET_PARENTS_RESP_WORDS 3
+#define CLK_GET_ATTR_RESP_WORDS 1
+
+enum topology_type {
+ TYPE_INVALID,
+ TYPE_MUX,
+ TYPE_PLL,
+ TYPE_FIXEDFACTOR,
+ TYPE_DIV1,
+ TYPE_DIV2,
+ TYPE_GATE,
+};
+
+struct clk_hw *zynqmp_clk_register_pll(struct device *dev, const char *name,
+ u32 clk_id,
+ const char *parent,
+ unsigned long flag);
+
+struct clk_hw *zynqmp_clk_register_gate(struct device *dev, const char *name,
+ u32 clk_id,
+ const char *parent,
+ unsigned long flags,
+ u8 clk_gate_flags);
+
+struct clk_hw *zynqmp_clk_register_divider(struct device *dev,
+ const char *name,
+ u32 clk_id, u32 div_type,
+ const char *parent,
+ unsigned long flags,
+ u8 clk_divider_flags);
+
+struct clk_hw *zynqmp_clk_register_mux(struct device *dev, const char *name,
+ u32 clk_id,
+ const char * const *parents,
+ u8 num_parents,
+ unsigned long flags,
+ u8 clk_mux_flags);
+#endif
diff --git a/drivers/clk/zynqmp/clkc.c b/drivers/clk/zynqmp/clkc.c
new file mode 100644
index 0000000..a315fc2
--- /dev/null
+++ b/drivers/clk/zynqmp/clkc.c
@@ -0,0 +1,737 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Zynq UltraScale+ MPSoC clock controller
+ *
+ * Copyright (C) 2016-2018 Xilinx
+ *
+ * Based on drivers/clk/zynq/clkc.c
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "clk-zynqmp.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 0xFFFFFFFF
+#define DUMMY_PARENT 0xFFFFFFFE
+
+#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_MASK GENMASK(21, 8)
+#define CLK_TYPE_FLAG_FIELD_MASK GENMASK(31, 24)
+
+#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
+ * @name: Parent name
+ * @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
+ * @type: Clock type (Output/External)
+ * @node: Clock tolology nodes
+ * @num_nodes: Number of nodes present in topology
+ * @parent: structure of parent of clock
+ * @num_parents: Number of parents 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_hw_onecell_data *zynqmp_data;
+static unsigned int clock_max_idx;
+static const struct zynqmp_eemi_ops *eemi_ops;
+
+/**
+ * zynqmp_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 on success else error code
+ */
+static int zynqmp_is_valid_clock(u32 clk_id, u32 *valid)
+{
+ if (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 on success else error code
+ */
+static int zynqmp_get_clock_name(u32 clk_id, char *clk_name)
+{
+ int ret;
+ u32 valid;
+
+ ret = zynqmp_is_valid_clock(clk_id, &valid);
+ if (!ret && valid) {
+ strncpy(clk_name, clock[clk_id].clk_name, MAX_NAME_LEN);
+ return ret;
+ } else {
+ return ret;
+ }
+}
+
+/**
+ * zynqmp_get_clock_type() - Get type of clock
+ * @clk_id: Clock index
+ * @type: Clock type: CLK_TYPE_OUTPUT or CLK_TYPE_EXTERNAL
+ *
+ * Return: 0 on success else error code
+ */
+static int zynqmp_get_clock_type(u32 clk_id, u32 *type)
+{
+ int ret;
+ u32 valid;
+
+ ret = zynqmp_is_valid_clock(clk_id, &valid);
+ if (!ret && valid) {
+ *type = clock[clk_id].type;
+ return ret;
+ } 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 0, 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];
+ int ret;
+
+ qdata.qid = PM_QID_CLOCK_GET_TOPOLOGY;
+ qdata.arg1 = clock_id;
+ qdata.arg2 = index;
+
+ ret = eemi_ops->query_data(qdata, ret_payload);
+ memcpy(topology, &ret_payload[1], CLK_GET_TOPOLOGY_RESP_WORDS * 4);
+
+ return ret;
+}
+
+/**
+ * zynqmp_pm_clock_get_fixedfactor_params() - Get clock's fixed factor params
+ * @clock_id: Clock ID
+ * @mul: Multiplication value
+ * @div: Divisor value
+ *
+ * This function is used to get fixed factor parameters for the fixed
+ * clock. This API is applicable 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];
+ int ret;
+
+ qdata.qid = PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS;
+ qdata.arg1 = clock_id;
+
+ ret = eemi_ops->query_data(qdata, ret_payload);
+ *mul = ret_payload[1];
+ *div = ret_payload[2];
+
+ return ret;
+}
+
+/**
+ * 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];
+ int ret;
+
+ qdata.qid = PM_QID_CLOCK_GET_PARENTS;
+ qdata.arg1 = clock_id;
+ qdata.arg2 = index;
+
+ ret = eemi_ops->query_data(qdata, ret_payload);
+ memcpy(parents, &ret_payload[1], CLK_GET_PARENTS_RESP_WORDS * 4);
+
+ return ret;
+}
+
+/**
+ * zynqmp_pm_clock_get_attributes() - Get the attributes of clock for given id
+ * @clock_id: Clock ID
+ * @attr: 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];
+ int ret;
+
+ qdata.qid = PM_QID_CLOCK_GET_ATTRIBUTES;
+ qdata.arg1 = clock_id;
+
+ ret = eemi_ops->query_data(qdata, ret_payload);
+ memcpy(attr, &ret_payload[1], CLK_GET_ATTR_RESP_WORDS * 4);
+
+ return ret;
+}
+
+/**
+ * zynqmp_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: Returns status, either success or error+reason
+ */
+static int zynqmp_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))
+ return 0;
+ clk_topology[*num_nodes].type = pm_resp[k] &
+ CLK_TYPE_FIELD_MASK;
+ clk_topology[*num_nodes].flag =
+ FIELD_GET(CLK_FLAG_FIELD_MASK,
+ pm_resp[k]);
+ clk_topology[*num_nodes].type_flag =
+ FIELD_GET(CLK_TYPE_FLAG_FIELD_MASK,
+ pm_resp[k]);
+ (*num_nodes)++;
+ }
+ }
+ return 0;
+}
+
+/**
+ * zynqmp_clock_get_parents() - Get parents info from firmware using PM_API
+ * @clk_id: Clock index
+ * @parents: Structure of parent information
+ * @num_parents: Total number of parents
+ *
+ * Return: Returns status, either success or error+reason
+ */
+static int zynqmp_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};
+ struct clock_parent *parent;
+
+ 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] == NA_PARENT) {
+ *num_parents = total_parents;
+ return 0;
+ }
+
+ parent = &parents[k + j];
+ parent->id = pm_resp[k] & CLK_PARENTS_ID_MASK;
+ if (pm_resp[k] == DUMMY_PARENT) {
+ strcpy(parent->name, "dummy_name");
+ parent->flag = 0;
+ } else {
+ parent->flag = pm_resp[k] >>
+ CLK_PARENTS_ID_LEN;
+ if (zynqmp_get_clock_name(parent->id,
+ parent->name))
+ continue;
+ }
+ total_parents++;
+ }
+ j += PM_API_PAYLOAD_LEN;
+ } while (total_parents <= MAX_PARENT);
+ return 0;
+}
+
+/**
+ * zynqmp_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: Returns status, either success or error+reason
+ */
+static int zynqmp_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)
+ strcpy(parents[i].name, "dummy_name");
+ 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: Returns either clock hardware or error+reason
+ */
+static struct clk_hw *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_hw *hw = NULL;
+
+ nodes = clock[clk_id].node;
+ num_nodes = clock[clk_id].num_nodes;
+
+ for (j = 0; j < num_nodes; j++) {
+ /*
+ * Clock name received from firmware is output clock name.
+ * Intermediate clock names are postfixed with type of clock.
+ */
+ 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:
+ hw = zynqmp_clk_register_mux(NULL, clk_out,
+ clk_id, parent_names,
+ num_parents,
+ nodes[j].flag,
+ nodes[j].type_flag);
+ break;
+ case TYPE_PLL:
+ hw = zynqmp_clk_register_pll(NULL, clk_out, clk_id,
+ parent_names[0],
+ nodes[j].flag);
+ break;
+ case TYPE_FIXEDFACTOR:
+ ret = zynqmp_pm_clock_get_fixedfactor_params(clk_id,
+ &mult,
+ &div);
+ hw = clk_hw_register_fixed_factor(NULL, clk_out,
+ parent_names[0],
+ nodes[j].flag, mult,
+ div);
+ break;
+ case TYPE_DIV1:
+ case TYPE_DIV2:
+ hw = zynqmp_clk_register_divider(NULL, clk_out, clk_id,
+ nodes[j].type,
+ parent_names[0],
+ nodes[j].flag,
+ nodes[j].type_flag);
+ break;
+ case TYPE_GATE:
+
+ hw = zynqmp_clk_register_gate(NULL, clk_out, clk_id,
+ parent_names[0],
+ nodes[j].flag,
+ nodes[j].type_flag);
+ break;
+ default:
+ pr_err("%s() Unknown topology for %s\n",
+ __func__, clk_out);
+ break;
+ }
+ if (IS_ERR(hw))
+ pr_warn_once("%s() %s register fail with %ld\n",
+ __func__, clk_name, PTR_ERR(hw));
+
+ parent_names[0] = clk_out;
+ }
+ kfree(clk_out);
+ return hw;
+}
+
+/**
+ * zynqmp_register_clocks() - Register clocks
+ * @np: Device node
+ *
+ * Return: 0 on success else error code
+ */
+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 = zynqmp_get_clock_type(i, &type);
+ if (ret || type != CLK_TYPE_OUTPUT)
+ continue;
+
+ /* Get parents of clock*/
+ if (zynqmp_get_parent_list(np, i, parent_names,
+ &total_parents)) {
+ WARN_ONCE(1, "No parents found for %s\n",
+ clock[i].clk_name);
+ continue;
+ }
+
+ zynqmp_data->hws[i] =
+ zynqmp_register_clk_topology(i, clk_name,
+ total_parents,
+ parent_names);
+ }
+
+ for (i = 0; i < clock_max_idx; i++) {
+ if (IS_ERR(zynqmp_data->hws[i])) {
+ pr_err("Zynq Ultrascale+ MPSoC clk %s: register failed with %ld\n",
+ clock[i].clk_name, PTR_ERR(zynqmp_data->hws[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 (!strcmp(clock[i].clk_name, END_OF_CLK_NAME)) {
+ clock_max_idx = i;
+ break;
+ } else if (!strcmp(clock[i].clk_name, RESERVED_CLK_NAME)) {
+ 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 = zynqmp_get_clock_type(i, &type);
+ if (ret || type != CLK_TYPE_OUTPUT)
+ continue;
+
+ ret = zynqmp_clock_get_topology(i, clock[i].node,
+ &clock[i].num_nodes);
+ if (ret)
+ continue;
+
+ ret = zynqmp_clock_get_parents(i, clock[i].parent,
+ &clock[i].num_parents);
+ if (ret)
+ continue;
+ }
+}
+
+/**
+ * zynqmp_validate_eemi_ops() - Validate eemi ops
+ *
+ * Return: 0 on success else error code
+ */
+static inline int zynqmp_validate_eemi_ops(void)
+{
+ eemi_ops = zynqmp_pm_get_eemi_ops();
+ if (!eemi_ops || !eemi_ops->query_data ||
+ !eemi_ops->clock_setdivider ||
+ !eemi_ops->clock_getdivider ||
+ !eemi_ops->clock_setparent ||
+ !eemi_ops->clock_getparent ||
+ !eemi_ops->clock_getstate ||
+ !eemi_ops->clock_disable ||
+ !eemi_ops->clock_enable ||
+ !eemi_ops->ioctl)
+ return -ENXIO;
+
+ return 0;
+}
+
+/**
+ * zynqmp_clk_setup() - Setup the clock framework and register clocks
+ * @np: Device node
+ *
+ * Return: 0 on success else error code
+ */
+static int __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 -ENOENT;
+ }
+ idx = of_property_match_string(np, "clock-names", "video_clk");
+ if (idx < 0) {
+ pr_err("video_clk not provided\n");
+ return -ENOENT;
+ }
+ 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 -ENOENT;
+ }
+ idx = of_property_match_string(np, "clock-names", "aux_ref_clk");
+ if (idx < 0) {
+ pr_err("aux_ref_clk not provided\n");
+ return -ENOENT;
+ }
+ idx = of_property_match_string(np, "clock-names", "gt_crx_ref_clk");
+ if (idx < 0) {
+ pr_err("aux_ref_clk not provided\n");
+ return -ENOENT;
+ }
+
+ zynqmp_data = kzalloc(sizeof(*zynqmp_data) + sizeof(*zynqmp_data) *
+ MAX_CLOCK, GFP_KERNEL);
+ if (!zynqmp_data)
+ return -ENOMEM;
+
+ zynqmp_get_clock_info();
+ zynqmp_register_clocks(np);
+
+ zynqmp_data->num = clock_max_idx;
+ of_clk_add_hw_provider(np, of_clk_hw_onecell_get, zynqmp_data);
+
+ return 0;
+}
+
+/**
+ * zynqmp_clock_init() - Initialize zynqmp clocks
+ *
+ * Return: 0 on success else error code
+ */
+static int __init zynqmp_clock_init(void)
+{
+ int ret;
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp");
+ if (!np)
+ return -ENOENT;
+ of_node_put(np);
+
+ np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp-clk");
+ if (!np) {
+ pr_err("%s: clk node not found\n", __func__);
+ return -ENOENT;
+ }
+
+ ret = zynqmp_validate_eemi_ops();
+ if (ret) {
+ pr_err("%s: eemi ops validation fail\n", __func__);
+ of_node_put(np);
+ return ret;
+ }
+
+ ret = zynqmp_clk_setup(np);
+ of_node_put(np);
+
+ return ret;
+}
+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..ef3e2e9
--- /dev/null
+++ b/drivers/clk/zynqmp/divider.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Zynq UltraScale+ MPSoC Divider support
+ *
+ * Copyright (C) 2016-2018 Xilinx
+ *
+ * Adjustable divider clock implementation
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include "clk-zynqmp.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)
+
+#define CLK_FRAC BIT(13) /* has a fractional parent */
+
+/**
+ * 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_CLOSEST(parent_rate, rate);
+}
+
+/**
+ * zynqmp_clk_divider_recalc_rate() - Recalc rate of divider clock
+ * @hw: handle between common and hardware-specific interfaces
+ * @parent_rate: rate of parent clock
+ *
+ * Return: Returns status, either success or error+reason
+ */
+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 = zynqmp_pm_get_eemi_ops();
+
+ 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 DIV_ROUND_UP_ULL(parent_rate, value);
+}
+
+/**
+ * zynqmp_clk_divider_round_rate() - Round rate of divider clock
+ * @hw: handle between common and hardware-specific interfaces
+ * @rate: rate of clock to be set
+ * @prate: rate of parent clock
+ *
+ * Return: Returns status, either success or error+reason
+ */
+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 = zynqmp_pm_get_eemi_ops();
+
+ /* 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: Returns status, either success or error+reason
+ */
+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 = zynqmp_pm_get_eemi_ops();
+
+ value = zynqmp_divider_get_val(parent_rate, rate);
+ if (div_type == TYPE_DIV1) {
+ div = value & 0xFFFF;
+ div |= 0xffff << 16;
+ } else {
+ div = 0xffff;
+ 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 ret;
+}
+
+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,
+};
+
+/**
+ * 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
+ * @parent: name of clock's parent
+ * @flags: framework-specific flags
+ * @clk_divider_flags: divider-specific flags for this clock
+ *
+ * Return: clock hardware to registered clock divider
+ */
+struct clk_hw *zynqmp_clk_register_divider(struct device *dev,
+ const char *name,
+ u32 clk_id, u32 div_type,
+ const char *parent,
+ unsigned long flags,
+ u8 clk_divider_flags)
+{
+ struct zynqmp_clk_divider *div;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ /* 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 = &parent;
+ init.num_parents = 1;
+
+ /* struct clk_divider assignments */
+ div->flags = clk_divider_flags;
+ div->hw.init = &init;
+ div->clk_id = clk_id;
+ div->div_type = div_type;
+
+ hw = &div->hw;
+ ret = clk_hw_register(dev, hw);
+ if (ret) {
+ kfree(div);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
+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..1782829
--- /dev/null
+++ b/drivers/clk/zynqmp/pll.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Zynq UltraScale+ MPSoC PLL driver
+ *
+ * Copyright (C) 2016-2018 Xilinx
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include "clk-zynqmp.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)
+
+#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 BIT(16) /* 2^16 */
+
+/**
+ * zynqmp_pll_get_mode() - Get mode of PLL
+ * @hw: Handle between common and hardware-specific interfaces
+ *
+ * Return: Mode of PLL
+ */
+static inline enum pll_mode zynqmp_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 = zynqmp_pm_get_eemi_ops();
+
+ 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];
+}
+
+/**
+ * zynqmp_pll_set_mode() - Set the PLL mode
+ * @hw: Handle between common and hardware-specific interfaces
+ * @on: Flag to determine the mode
+ */
+static inline void zynqmp_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 = zynqmp_pm_get_eemi_ops();
+
+ 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;
+ zynqmp_pll_set_mode(hw, !!f);
+
+ if (zynqmp_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 = zynqmp_pm_get_eemi_ops();
+
+ /*
+ * 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 (zynqmp_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
+ *
+ * Set PLL divider to set desired rate.
+ *
+ * Returns: rate which is set on success else error code
+ */
+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 = zynqmp_pm_get_eemi_ops();
+
+ if (zynqmp_pll_get_mode(hw) == PLL_MODE_FRAC) {
+ 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 = zynqmp_pm_get_eemi_ops();
+
+ 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 -EIO;
+ }
+
+ return state ? 1 : 0;
+}
+
+/**
+ * zynqmp_pll_enable() - Enable clock
+ * @hw: Handle between common and hardware-specific interfaces
+ *
+ * Return: 0 on success else error code
+ */
+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 = zynqmp_pm_get_eemi_ops();
+
+ 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 ret;
+}
+
+/**
+ * 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 = zynqmp_pm_get_eemi_ops();
+
+ 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,
+};
+
+/**
+ * zynqmp_clk_register_pll() - Register PLL with the clock framework
+ * @dev: Device pointer
+ * @name: PLL name
+ * @clk_id: Clock ID
+ * @parent: Parent clock name
+ * @flag: PLL flgas
+ *
+ * Return: clock hardware to the registered clock
+ */
+struct clk_hw *zynqmp_clk_register_pll(struct device *dev, const char *name,
+ u32 clk_id,
+ const char *parent,
+ unsigned long flag)
+{
+ struct zynqmp_pll *pll;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
+
+ init.name = name;
+ init.ops = &zynqmp_pll_ops;
+ init.flags = flag;
+ init.parent_names = &parent;
+ init.num_parents = 1;
+
+ pll = kmalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ /* Populate the struct */
+ pll->hw.init = &init;
+ pll->clk_id = clk_id;
+
+ hw = &pll->hw;
+ ret = clk_hw_register(dev, hw);
+ if (ret) {
+ kfree(pll);
+ return ERR_PTR(ret);
+ }
+
+ clk_hw_set_rate_range(hw, PS_PLL_VCO_MIN, PS_PLL_VCO_MAX);
+ if (ret < 0)
+ pr_err("%s:ERROR clk_set_rate_range failed %d\n", name, ret);
+
+ return hw;
+}
--
2.7.4
^ permalink raw reply related
* [PATCH v7 10/10] drivers: clk: Add ZynqMP clock driver
From: Randy Dunlap @ 2018-05-30 21:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1527713725-1086-11-git-send-email-jollys@xilinx.com>
On 05/30/2018 01:55 PM, Jolly Shah wrote:
> From: Jolly Shah <jolly.shah@xilinx.com>
> diff --git a/drivers/clk/zynqmp/Kconfig b/drivers/clk/zynqmp/Kconfig
> new file mode 100644
> index 0000000..fe815f7
> --- /dev/null
> +++ b/drivers/clk/zynqmp/Kconfig
> @@ -0,0 +1,11 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config COMMON_CLK_ZYNQMP
> + bool "Support for Xilinx ZynqMP Ultrascale+ clock controllers"
> + depends on OF
> + depends on ARCH_ZYNQMP || COMPILE_TEST
> + depends on ZYNQMP_FIRMWARE
> + 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
Say Y if you want to include clock support.
--
~Randy
^ permalink raw reply
* [PATCH] ARM: pxa, regulator: fix building ezx e680
From: Arnd Bergmann @ 2018-05-30 21:24 UTC (permalink / raw)
To: linux-arm-kernel
The reference to camera_supply_gpiod_table was added in the wrong function,
as observed from this randconfig build failure:
arch/arm/mach-pxa/ezx.c: In function 'e680_init':
arch/arm/mach-pxa/ezx.c:905:26: error: 'camera_supply_gpiod_table' undeclared (first use in this function)
gpiod_add_lookup_table(&camera_supply_gpiod_table);
Fixes: 6059577cb28d ("regulator: fixed: Convert to use GPIO descriptor only")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
arch/arm/mach-pxa/ezx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/mach-pxa/ezx.c b/arch/arm/mach-pxa/ezx.c
index 2b4bd6d94855..4f33eea73f9a 100644
--- a/arch/arm/mach-pxa/ezx.c
+++ b/arch/arm/mach-pxa/ezx.c
@@ -902,7 +902,6 @@ static void __init e680_init(void)
pxa_set_keypad_info(&e680_keypad_platform_data);
- gpiod_add_lookup_table(&camera_supply_gpiod_table);
pwm_add_table(ezx_pwm_lookup, ARRAY_SIZE(ezx_pwm_lookup));
platform_add_devices(ARRAY_AND_SIZE(ezx_devices));
platform_add_devices(ARRAY_AND_SIZE(e680_devices));
@@ -1132,6 +1131,7 @@ static void __init a910_init(void)
if (a910_camera_init() == 0)
pxa_set_camera_info(&a910_pxacamera_platform_data);
+ gpiod_add_lookup_table(&camera_supply_gpiod_table);
pwm_add_table(ezx_pwm_lookup, ARRAY_SIZE(ezx_pwm_lookup));
platform_add_devices(ARRAY_AND_SIZE(ezx_devices));
platform_add_devices(ARRAY_AND_SIZE(a910_devices));
--
2.9.0
^ permalink raw reply related
* [PATCH v2] spi: bcm2835aux: ensure interrupts are enabled for shared handler
From: Rob Herring @ 2018-05-30 21:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87sh69nelw.fsf@anholt.net>
On Wed, May 30, 2018 at 2:11 PM, Eric Anholt <eric@anholt.net> wrote:
> Rob Herring <robh@kernel.org> writes:
>
>> The BCM2835 AUX SPI has a shared interrupt line (with AUX UART).
>> Downstream fixes this with an AUX irqchip to demux the IRQ sources and a
>> DT change which breaks compatibility with older kernels. The AUX irqchip
>> was already rejected for upstream[1] and the DT change would break
>> working systems if the DTB is updated to a newer one. The latter issue
>> was brought to my attention by Alex Graf.
>>
>> The root cause however is a bug in the shared handler. A shared handler
>> must correctly identify it actually handled an interrupt. The handler
>> here was processing data whether interrupts were enabled or not.
>> It would return IRQ_HANDLED if there was any data and not only when
>> there was an actual interrupt pending. The result is that another
>> device's IRQ could cause the SPI's IRQ handler to run and process data
>> when the the SPI driver working in polled mode. Fix this by adding a
>> check in the IRQ handler that the TXEMPTY or IDLE interrupts are enabled
>> and always return IRQ_NONE when they are not.
>
> FWIW, I see v1 already applied in -next.
Sigh, indeed. I thought I had checked that. Though Mark had comments
on the commit msg, so I assumed he wanted changes. The automated
applied emails just get lost in my inbox. It would be nice if they
kept the subject instead of adding 'Applied "...' so gmail would group
them. Of course, it would be nice if gmail could honor threading too.
Rob
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox