* Adding a .platform_init callback to sdhci_arasan_ops
From: Doug Anderson @ 2016-11-28 20:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <9c783843-d0e4-92ef-d156-6826e38d9fa8@free.fr>
Hi,
On Mon, Nov 28, 2016 at 11:32 AM, Mason <slash.tmp@free.fr> wrote:
> On 28/11/2016 18:46, Doug Anderson wrote:
>
>> As argued in my original patch the field "corecfg_baseclkfreq" is
>> documented in the generic Arasan document
>> <https://arasan.com/wp-content/media/eMMC-5-1-Total-Solution_Rev-1-3.pdf>
>> and thus is unlikely to be Rockchip specific.
>
> I downloaded the data sheet, which doesn't mention registers,
> but "pins" and "signals". Does that mean it is up to every
> platform to decide how to group these wires into individual
> registers?
As I understand it: yes. ...but remember that I'm not arasan nor even
Rockchip and I don't have any access to that they provide their IP
licensees.
That's why I structured my config the way it did. It means that no
matter how a licensee stuffs these configs into their register map we
can support it. Every configuration is individually described by a
register offset, width, and mask. Technically the width _ought_ to be
the same for all users, so I guess you could try to optimize that out.
...but it doesn't hurt the way it is.
> corecfg_baseclkfreq[7:0]
> Base Clock Frequency for SD Clock.
> This is the frequency of the xin_clk.
>
> How can 8 bits encode a frequency?
> Is there an implicit LUT? Is it a MHz count?
Use the source. :)
* The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin.
It also turns out to match the SDHCI spec "Base Clock Frequency for SD
Clock". See below.
---
It actually turns out that both of the "corecfg" bits we're setting
right now for Rockchip are a bit on the silly side. All they do is
get mirrored out the other end in the "caps" register. I found that
out from folks at Rockchip much later after I wrote my patch.
So, for instance, setting corecfg_clockmultiplier (I'm told) does
nothing more than control the bits read here:
(caps[1] & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT;
I can certainly see that they are mirrored, but I have to just take it
on faith that it does nothing else.
>>> w(grf + 0xf02c, 0x00ff0011)
>>> hex((r(sdhci + 0x44) & 0x00ff0000) >> 16)
'0x11L'
...and similar for the speed:
>>> w(grf + 0xf000, 0xff009700)
>>> hex((r(sdhci + 0x40) & 0x0000ff00) >> 8)
'0x97L'
-Doug
^ permalink raw reply
* [PATCH 5/7] add bindings for stm32 IIO timer drivers
From: Rob Herring @ 2016-11-28 21:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CA+M3ks7MgnUJoDoNG157+MbdhgbqwzRmhPcD-uQKC7_DJVAK6A@mail.gmail.com>
On Wed, Nov 23, 2016 at 09:17:58AM +0100, Benjamin Gaignard wrote:
> If it is ok for you I will add "id" parameter in mfd driver and
> forward it to the sub-devices drivers
> to be able to distinguish the hardware blocks
Please don't top post.
No, it's not okay. If the counter sizes are different, then have a
property for the counter size. Describe how they are different without
numbering them. If you can't describe the differences, then it shouldn't
matter which ones the OS picks to use.
> 2016-11-22 18:18 GMT+01:00 Lee Jones <lee.jones@linaro.org>:
> > On Tue, 22 Nov 2016, Benjamin Gaignard wrote:
> >
> >> [snip]
> >> >> + "st,stm32-iio-timer5"
> >> >> + "st,stm32-iio-timer6"
> >> >> + "st,stm32-iio-timer7"
> >> >> + "st,stm32-iio-timer8"
> >> >> + "st,stm32-iio-timer9"
> >> >> + "st,stm32-iio-timer10"
> >> >> + "st,stm32-iio-timer11"
> >> >> + "st,stm32-iio-timer12"
> >> >> + "st,stm32-iio-timer13"
> >> >> + "st,stm32-iio-timer14"
I doubt the h/w manual calls these "IIO timers".
> >> >
> >> > We can't do this. This is a binding for a driver, not for the hardware.
> >> >
> >>
> >> Unfortunately each instance for the hardware IP have little
> >> differences like which triggers they could accept or size of the
> >> counter register,
> >> and I doesn't have value inside the hardware to distinguish them so
> >> the only way I found is to use compatible.
> >
> > Can't you represent these as properties?
> >
> > --
> > Lee Jones
> > Linaro STMicroelectronics Landing Team Lead
> > Linaro.org ? Open source software for ARM SoCs
> > Follow Linaro: Facebook | Twitter | Blog
>
>
>
> --
> Benjamin Gaignard
>
> Graphic Study Group
>
> Linaro.org ? Open source software for ARM SoCs
>
> Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [PATCH 1/3] devicetree: bindings: pinctrl: Add binding for ti,da850-pupd
From: Rob Herring @ 2016-11-28 21:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1479871767-20160-2-git-send-email-david@lechnology.com>
On Tue, Nov 22, 2016 at 09:29:25PM -0600, David Lechner wrote:
> Device-tree bindings for TI DA8XX/OMAP-L138/AM18XX pullup/pulldown
> pinconf controller.
>
> Signed-off-by: David Lechner <david@lechnology.com>
> ---
> .../devicetree/bindings/pinctrl/ti,da850-pupd.txt | 55 ++++++++++++++++++++++
> 1 file changed, 55 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,da850-pupd.txt
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* [PATCH V7 2/3] ACPI: Add support for ResourceSource/IRQ domain mapping
From: Agustin Vega-Frias @ 2016-11-28 22:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161125114035.GA13739@red-moon>
Hi Rafael,
Can you chime in on Lorenzo's feedback and the discussion below?
It would be great if you can comment on the reason ACPI does things
in a certain way.
Hi Lorenzo,
On 2016-11-25 06:40, Lorenzo Pieralisi wrote:
> Hi Agustin,
>
> On Thu, Nov 24, 2016 at 04:15:48PM +0000, Lorenzo Pieralisi wrote:
>
> [...]
>
>> > @@ -448,6 +449,7 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
>> > {
>> > struct acpi_resource_irq *irq;
>> > struct acpi_resource_extended_irq *ext_irq;
>> > + struct fwnode_handle *src;
>> >
>> > switch (ares->type) {
>> > case ACPI_RESOURCE_TYPE_IRQ:
>> > @@ -460,7 +462,7 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
>> > acpi_dev_irqresource_disabled(res, 0);
>> > return false;
>> > }
>> > - acpi_dev_get_irqresource(res, irq->interrupts[index],
>> > + acpi_dev_get_irqresource(res, irq->interrupts[index], NULL,
>> > irq->triggering, irq->polarity,
>> > irq->sharable, true);
>> > break;
>> > @@ -470,7 +472,8 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
>> > acpi_dev_irqresource_disabled(res, 0);
>> > return false;
>> > }
>> > - acpi_dev_get_irqresource(res, ext_irq->interrupts[index],
>> > + src = acpi_get_irq_source_fwhandle(&ext_irq->resource_source);
>>
>> Is there a reason why we need to do the domain look-up here ?
Because we need to pass the resource down to acpi_dev_get_irqresource
which does the mapping through acpi_register_irq/acpi_register_gsi.
>>
>> I would like to understand if, by reshuffling the code (and by
>> returning
>> the resource_source to the calling code - somehow), it would be
>> possible
>> to just mirror what the OF code does in of_irq_get(), namely:
>>
>> (1) parse the irq entry -> of_irq_parse_one()
>> (2) look the domain up -> irq_find_host()
>> (3) create the mapping -> irq_create_of_mapping()
>>
>> You wrote the code already, I think it is just a matter of shuffling
>> it around (well, minus returning the resource_source to the caller
>> which is phandle equivalent in DT).
This is one area in which DT and ACPI are fundamentally different. In DT
once the flattened blob is expanded the data is fixed. In ACPI the data
returned by a method can change. In reality most methods like CRS return
constants, but given that per-spec they are methods the interpreter has
to be involved, which makes it an expensive operation. I believe that is
the reason the resource parsing code in ACPI attempts all mappings
during
the bus scan. Rafael can you comment on this?
One way to do what you suggest would be to defer IRQ mapping by, e.g.,
populating res->start with the HW IRQ number and res->end with the
fwnode.
That way we can avoid having to walk the resource buffer when a mapping
is needed. I don't think that approach would deviate much more from
the spec from what the current ahead-of-time mapping does, but it would
require more changes in the core code. An alternative would be to do
that only for resources that fail to map.
>>
>> You abstracted away (2) and (3) behind acpi_register_irq(), that
>> on anything than does not use ACPI_GENERIC_GSI is just glue code
>> to acpi_register_gsi().
>>
>> Also, it is not a question on this patch but I ask it here because it
>> is related. On ACPI you are doing the reverse of what is done in
>> DT in platform_get_irq():
>>
>> - get the resources already parsed -> platform_get_resource()
>> - if they are disabled -> acpi_irq_get()
>>
>> and I think the ordering is tied to my question above because
>> you carry out the domain look up in acpi_dev_resource_interrupt()
>> so that if for any reason it fails the corresponding resource
>> is disabled so that we try to get it again through acpi_irq_get().
>>
>> I suspect you did it this way to make sure:
>>
>> a) keep the current ACPI IRQ parsing interface changes to a mininum
>> b) avoid changing the behaviour on x86/ia64; in particular, calling
>> acpi_register_gsi() for the _same_ mapping (an IRQ that was already
>> registered at device creation resource parsing) multiple times can
>> trigger issues on x86/ia64
You are correct about my reasons. I wanted to keep ACPI core code
changes
to a minimum, and I also needed to work within the current
implementation
which uses the pre-converted IRQ resources.
>>
>> I think that's a reasonable approach but I wanted to get these
>> clarifications, I do not think you are far from getting this
>> done but since it is a significant change I think it is worth
>> discussing the points I raised above because I think the DT code
>> sequence in of_irq_get() (1-2-3 above) is cleaner from an IRQ
>> layer perspective (instead of having the domain look-up buried
>> inside the ACPI IRQ resource parsing API).
>
> I had another look and to achieve the above one way of doing that is to
> implement acpi_irq_get() only for ACPI_GENERIC_GSI and stub it out for
> !ACPI_GENERIC_GSI (ie return an error code so that on !ACPI_GENERIC_GSI
> we would fall back to current solution for ACPI). Within acpi_irq_get()
> you can easily carry out the same steps (1->2->3) above in ACPI you
> have
> the code already there I think it is easy to change the
> acpi_irq_get_cb() interface to return a filled in struct irq_fwspec and
> the interface would become identical to of_irq_get() that is an
> advantage to maintain it from an IRQ maintainership perspective I
> think,
> that's my opinion.
I think I get what you mean. I'll take a stab at implementing
acpi_irq_get()
in the way you suggest.
>
> There is still a nagging snag though. When platform devices are
> created, core ACPI code parse the resources through:
>
> acpi_dev_get_resources()
>
> and we _have_ to have way to avoid initializing IRQ resources that
> have a dependency (ie there is a resource_source pointer that is valid
> in their descriptors) that's easy to do if we think that's the right
> thing to do and can hardly break current code (which ignores the
> resource_source altogether).
I'd rather keep the core code as-is with regard to the ahead-of-time
conversion. Whether a resource source is available at the time of the
bus
scan should be transparent to the code in drivers/acpi/resource.c, and
we need the initialization as a disabled resource to signal the need
to retry anyway.
Rafael, do you have any other suggestions/feedback on how to go about
doing this?
Thanks,
Agustin
>
> It is an important difference with DT probing, where the IRQ
> resources are only created if the domain reference (ie interrupt
> controller phandle) is satisfied at of_device_alloc() time
> (see of_device_alloc()).
>
> Thoughts ? Please let me know, the code to implement what I say
> is already in these patches, it is just a matter of reshuffling it.
>
> Thanks !
> Lorenzo
--
Qualcomm Datacenter Technologies, Inc. on behalf of the Qualcomm
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a
Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH] arm64: dts: Add symlinks for cros-ec-keyboard and cros-ec-sbs
From: Brian Norris @ 2016-11-28 23:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1463090530-21864-1-git-send-email-dianders@chromium.org>
Hi Olof, Arnd,
On Thu, May 12, 2016 at 03:02:10PM -0700, Doug Anderson wrote:
> We'd like to be able to use the cros-ec-keyboard.dtsi and
> cros-ec-sbs.dtsi snippets for arm64 devices. Currently those files live
> in the arm/boot/dts directory.
>
> Let's follow the convention set by commit 8ee57b8182c4 ("ARM64: dts:
> vexpress: Use a symlink to vexpress-v2m-rs1.dtsi from arch=arm") and use
> a symlink. Note that in this case we put the files in a new
> "include/common" directory since these snippets may need to be
> referenced by dts files in many different subdirectories.
>
> Signed-off-by: Douglas Anderson <dianders@chromium.org>
> ---
> Note that, as of right now, there are no users of this. However, given
> development happening it is almost 100% certain that users will arrive
> soon. If we need to wait for the first user before landing this we can
> leave this on the back burner. If it's OK to have no users (yet), let's
> land.
This may not fit your definition of "soon", but I'm looking to start
using these files in arch/arm64/. I'll probably carry along this patch
and resend when I'm ready, but it'd be just as well if you'd merge it
now (or express a preference for a different directory structure).
> arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi | 1 +
> arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi | 1 +
> 2 files changed, 2 insertions(+)
> create mode 120000 arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
> create mode 120000 arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
>
> \ No newline at end of file
>
> diff --git a/arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi b/arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
> new file mode 120000
> index 000000000000..1c1889f0a791
> --- /dev/null
> +++ b/arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
> @@ -0,0 +1 @@
> +../../../../../arm/boot/dts/cros-ec-keyboard.dtsi
> \ No newline at end of file
> diff --git a/arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi b/arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
> new file mode 120000
> index 000000000000..3d7ae9c88bcd
> --- /dev/null
> +++ b/arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
> @@ -0,0 +1 @@
> +../../../../../arm/boot/dts/cros-ec-sbs.dtsi
FWIW:
Reviewed-by: Brian Norris <briannorris@chromium.org>
^ permalink raw reply
* [PATCH v5 8/8] iommu/rockchip: Enable Rockchip IOMMU on ARM64
From: Brian Norris @ 2016-11-29 0:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1466734413-7453-9-git-send-email-zhengsq@rock-chips.com>
Hi,
On Fri, Jun 24, 2016 at 10:13:33AM +0800, Shunqian Zheng wrote:
> From: Simon Xue <xxm@rock-chips.com>
>
> This patch makes it possible to compile the rockchip-iommu driver on
> ARM64, so that it can be used with 64-bit SoCs equipped with this type
> of IOMMU.
>
> Signed-off-by: Simon Xue <xxm@rock-chips.com>
> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> ---
> drivers/iommu/Kconfig | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index ad08603..5572621 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -218,7 +218,7 @@ config OMAP_IOMMU_DEBUG
>
> config ROCKCHIP_IOMMU
> bool "Rockchip IOMMU Support"
> - depends on ARM
> + depends on ARM || ARM64
> depends on ARCH_ROCKCHIP || COMPILE_TEST
> select IOMMU_API
> select ARM_DMA_USE_IOMMU
Whatever happened with the rest of this series? Some of the IOMMU bits
made it, but the DRM fixes never did, and so this didn't get applied.
This leaves the whole DRM stack unusable on ARM64 Rockchip systems.
The patch context has changed a bit on patch 7 (and maybe 6?), so
somebody will need to refresh those on the latest upstream.
Brian
^ permalink raw reply
* [bug report v4.8] fs/locks.c: kernel oops during posix lock stress test
From: Ming Lei @ 2016-11-29 1:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480340400.2606.10.camel@poochiereds.net>
Hi Jeff,
On Mon, Nov 28, 2016 at 9:40 PM, Jeff Layton <jlayton@poochiereds.net> wrote:
> On Mon, 2016-11-28 at 11:10 +0800, Ming Lei wrote:
>> Hi Guys,
>>
>> When I run stress-ng via the following steps on one ARM64 dual
>> socket system(Cavium Thunder), the kernel oops[1] can often be
>> triggered after running the stress test for several hours(sometimes
>> it may take longer):
>>
>> - git clone git://kernel.ubuntu.com/cking/stress-ng.git
>> - apply the attachment patch which just makes the posix file
>> lock stress test more aggressive
>> - run the test via '~/git/stress-ng$./stress-ng --lockf 128 --aggressive'
>>
>>
>> From the oops log, looks one garbage file_lock node is got
>> from the linked list of 'ctx->flc_posix' when the issue happens.
>>
>> BTW, the issue isn't observed on single socket Cavium Thunder yet,
>> and the same issue can be seen on Ubuntu Xenial(v4.4 based kernel)
>> too.
>>
>> Thanks,
>> Ming
>>
>
> Some questions just for clarification:
>
> - I assume this is being run on a local fs of some sort? ext4 or xfs or
> something?
Yes, I just tested it on local ext4, and not test it on other filesystems yet.
>
> - have you seen this on any other arch, besides ARM?
I run the same tests on x86 before, and not see the issue.
>
> The file locking code does do some lockless checking to see whether the
> i_flctx is even present and whether the list is empty in
> locks_remove_posix. It's possible we have some barrier problems there,
> but I don't quite see how that would cause us to have a corrupt lock on
> the flc_posix list.
Yeah, I looked at the function of posix_lock_inode(), seems both add and
remove are protected by the lock.
Thanks,
Ming
^ permalink raw reply
* [PATCH v4] clkdev: add devm_of_clk_get()
From: Kuninori Morimoto @ 2016-11-29 1:21 UTC (permalink / raw)
To: linux-arm-kernel
From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Current Linux has of_clk_get(), but doesn't have devm_of_clk_get().
This patch adds it. It is implemeted in clk-devres.c to share
devm_clk_release().
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
v3 -> v4
- git log explain why it is implemeted in clk-devres
- it is related to CONFIG_HAVE_CLK
drivers/clk/clk-devres.c | 21 +++++++++++++++++++++
include/linux/clk.h | 27 +++++++++++++++++++++++----
2 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c
index 8f57154..2449b25 100644
--- a/drivers/clk/clk-devres.c
+++ b/drivers/clk/clk-devres.c
@@ -53,3 +53,24 @@ void devm_clk_put(struct device *dev, struct clk *clk)
WARN_ON(ret);
}
EXPORT_SYMBOL(devm_clk_put);
+
+struct clk *devm_of_clk_get(struct device *dev,
+ struct device_node *np, int index)
+{
+ struct clk **ptr, *clk;
+
+ ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ clk = of_clk_get(np, index);
+ if (!IS_ERR(clk)) {
+ *ptr = clk;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return clk;
+}
+EXPORT_SYMBOL(devm_of_clk_get);
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 123c027..7f50c5f 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -17,8 +17,9 @@
#include <linux/notifier.h>
struct device;
-
struct clk;
+struct device_node;
+struct of_phandle_args;
/**
* DOC: clk notifier callback types
@@ -249,6 +250,21 @@ static inline void clk_unprepare(struct clk *clk)
struct clk *devm_clk_get(struct device *dev, const char *id);
/**
+ * devm_clk_get - lookup and obtain a managed reference to a clock producer.
+ * @dev: device for clock "consumer"
+ * @np: pointer to clock consumer node
+ * @index: clock index
+ *
+ * This function parses the clocks, and uses them to look up the
+ * struct clk from the registered list of clock providers by using
+ * @np and @index.
+ *
+ * The clock will automatically be freed when the device is unbound
+ * from the bus.
+ */
+struct clk *devm_of_clk_get(struct device *dev, struct device_node *np, int index);
+
+/**
* clk_enable - inform the system when the clock source should be running.
* @clk: clock source
*
@@ -432,6 +448,12 @@ static inline struct clk *devm_clk_get(struct device *dev, const char *id)
return NULL;
}
+static inline struct clk *devm_of_clk_get(struct device *dev,
+ struct device_node *np, int index)
+{
+ return NULL;
+}
+
static inline void clk_put(struct clk *clk) {}
static inline void devm_clk_put(struct device *dev, struct clk *clk) {}
@@ -501,9 +523,6 @@ static inline void clk_disable_unprepare(struct clk *clk)
clk_unprepare(clk);
}
-struct device_node;
-struct of_phandle_args;
-
#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
struct clk *of_clk_get(struct device_node *np, int index);
struct clk *of_clk_get_by_name(struct device_node *np, const char *name);
--
1.9.1
^ permalink raw reply related
* [PATCH V8 2/6] thermal: bcm2835: add thermal driver for bcm2835 soc
From: Eduardo Valentin @ 2016-11-29 1:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87twarz6s1.fsf@eliezer.anholt.net>
Hello Eric, Martin,
On Mon, Nov 28, 2016 at 12:30:38PM -0800, Eric Anholt wrote:
> Eduardo Valentin <edubezval@gmail.com> writes:
>
<cut>
> The firmware today always initializes thermal. I suggested adding the
> init code because we (myself and the Pi Foundation) would like to reduce
> how much closed firmware code is required in the platform, and the Linux
> driver doing this would help make that possible in the future.
Oh I see. Backup code for future chips/firmware.
>
> >> > Who has the ownership of this device?
> >>
> >> Joined ownership I suppose...
> >>
> >
> > with no synchronization mechanism?
>
> Correct, because none is necessary.
>
<cut>
>
> Either the device was initialized by the firmware before handing off to
> ARM (today's firmware) or it never will be (potential future firmware).
And do you have a way to check if the firmware has the initialization
code or not? By firmware version, for example. Or even, chip version,
maybe?
If the current firmware will always initialize the chip, I would say,
ARM should simply read the registers, no initialization, unless it is
known that the firmware intentionally left the device uninitialized.
Again, just trying to avoid obscure misbehavior, when running into a
faulty state, and running silently broken.
^ permalink raw reply
* [PATCH] arm64: head.S: Fix CNTHCTL_EL2 access on VHE system
From: Jintack Lim @ 2016-11-29 2:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <871868f8-563c-98a1-e3f7-f3ac9457265e@arm.com>
On Mon, Nov 28, 2016 at 11:56 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 28/11/16 16:43, Jintack Lim wrote:
>> From: Jintack <jintack@cs.columbia.edu>
>>
>> Bit positions of CNTHCTL_EL2 are changing depending on HCR_EL2.E2H bit.
>> EL1PCEN and EL1PCTEN are 1st and 0th bits when E2H is not set, but they
>> are 11th and 10th bits respectively when E2H is set. Current code is
>> unintentionally setting wrong bits to CNTHCTL_EL2 with E2H set.
>>
>> In fact, we don't need to set those two bits, which allow EL1 and EL0 to
>> access physical timer and counter respectively, if E2H and TGE are set
>> for the host kernel. They will be configured later as necessary. First,
>> we don't need to configure those bits for EL1, since the host kernel
>> runs in EL2. It is a hypervisor's responsibility to configure them
>> before entering a VM, which runs in EL0 and EL1. Second, EL0 accesses
>> are configured in the later stage of boot process.
>>
>> Signed-off-by: Jintack Lim <jintack@cs.columbia.edu>
>> ---
>> arch/arm64/kernel/head.S | 8 +++++++-
>> 1 file changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
>> index 332e331..bc3d2db 100644
>> --- a/arch/arm64/kernel/head.S
>> +++ b/arch/arm64/kernel/head.S
>> @@ -524,10 +524,16 @@ set_hcr:
>> msr hcr_el2, x0
>> isb
>>
>> - /* Generic timers. */
>> + /*
>> + * Allow Non-secure EL1 and EL0 to access physical timer and counter.
>> + * This is not necessary for VHE, since the host kernel runs in EL2,
>> + * and EL0 accesses are configured in the later stage of boot process.
>> + */
>> + cbnz x2, 1f
>> mrs x0, cnthctl_el2
>> orr x0, x0, #3 // Enable EL1 physical timers
>> msr cnthctl_el2, x0
>> +1:
>> msr cntvoff_el2, xzr // Clear virtual offset
>>
>> #ifdef CONFIG_ARM_GIC_V3
>>
>
> Nice catch. It may be worth documenting that when HCR_EL2.E2H == 1,
> CNTHCTL_EL2 has the same bit layout as CNTKCTL_EL1, allowing the kernel
> designed to run at EL1 to transparently mess with the EL0 bits (as
> CNTHCTL_EL2 and CNTKCTL_EL1 are the same register in this configuration).
Hi Marc, thanks for the review. I'll add this comment and send out v2.
>
> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
>
> Thanks,
>
> M.
> --
> Jazz is not dead. It just smells funny...
>
^ permalink raw reply
* [PATCH v2] arm64: head.S: Fix CNTHCTL_EL2 access on VHE system
From: Jintack Lim @ 2016-11-29 2:13 UTC (permalink / raw)
To: linux-arm-kernel
From: Jintack <jintack@cs.columbia.edu>
Bit positions of CNTHCTL_EL2 are changing depending on HCR_EL2.E2H bit.
EL1PCEN and EL1PCTEN are 1st and 0th bits when E2H is not set, but they
are 11th and 10th bits respectively when E2H is set. Current code is
unintentionally setting wrong bits to CNTHCTL_EL2 with E2H set.
In fact, we don't need to set those two bits, which allow EL1 and EL0 to
access physical timer and counter respectively, if E2H and TGE are set
for the host kernel. They will be configured later as necessary. First,
we don't need to configure those bits for EL1, since the host kernel
runs in EL2. It is a hypervisor's responsibility to configure them
before entering a VM, which runs in EL0 and EL1. Second, EL0 accesses
are configured in the later stage of boot process.
Signed-off-by: Jintack Lim <jintack@cs.columbia.edu>
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
---
v2: Add comments about accessing CNTHCTL_EL2 when HCR_EL2.ECH == 1
---
arch/arm64/kernel/head.S | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 332e331..136cb0d 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -524,10 +524,21 @@ set_hcr:
msr hcr_el2, x0
isb
- /* Generic timers. */
+ /*
+ * Allow Non-secure EL1 and EL0 to access physical timer and counter.
+ * This is not necessary for VHE, since the host kernel runs in EL2,
+ * and EL0 accesses are configured in the later stage of boot process.
+ * Note that when HCR_EL2.E2H == 1, CNTHCTL_EL2 has the same bit layout
+ * as CNTKCTL_EL1, and CNTKCTL_EL1 accessing instructions are redefined
+ * to access CNTHCTL_EL2. This allows the kernel designed to run at EL1
+ * to transparently mess with the EL0 bits via CNTKCTL_EL1 access in
+ * EL2.
+ */
+ cbnz x2, 1f
mrs x0, cnthctl_el2
orr x0, x0, #3 // Enable EL1 physical timers
msr cnthctl_el2, x0
+1:
msr cntvoff_el2, xzr // Clear virtual offset
#ifdef CONFIG_ARM_GIC_V3
--
1.9.1
^ permalink raw reply related
* [PATCH v2] irqchip/gicv3-its: Enable cacheable attribute Read-allocate hints
From: Shanker Donthineni @ 2016-11-29 2:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478589498-17696-1-git-send-email-shankerd@codeaurora.org>
Hi Marc,
Please comment on this patch. I'm seeing ~1000 cpu cycles interrupt
latency improvement on Qualcomm server platforms if the ITS tables are
not in L1/L2/L3 cache. You can find the test code at
https://patchwork.kernel.org/patch/9416793 which I used to validate my
code changes.
Thanks,
Shanker
On 11/08/2016 01:18 AM, Shanker Donthineni wrote:
> Read-allocation hints are not enabled for both the GIC-ITS and GICR
> tables. This forces the hardware to always read the table contents
> from an external memory (DDR) which is slow compared to cache memory.
> Most of the tables are often read by hardware. So, it's better to
> enable Read-allocate hints in addition to Write-allocate hints in
> order to improve the GICR_PEND, GICR_PROP, Collection, Device, and
> vCPU tables lookup time.
>
> Signed-off-by: Shanker Donthineni <shankerd@codeaurora.org>
> ---
> Implemented a test case to prove that enabling Read Allocation hints
> improves ITS lookup time ~15% while delivering a LPI event. Used the
> ITS command INV to analyze time spent in device, collection, prop and
> pending table lookups.
>
> Pseudo code:
> Create a fake ITS device.
> Record PMU cycle counter before sending INV command.
> Build and send ITS INT command.
> ITS hardware triggers device table lookup.
> ITTE table & collection table lookup.
> ITS property table lookup.
> ITS pending table lookup.
> Deliver interrupt to CPU interface.
> do_IRQ() called.
> Measure the total CPU cycle spent to reach this point.
>
> Without ReadAllocation hints:
> /sys/kernel/debug # echo 100 > lpitest
> [ 94.693968] CPU[1] niter=100 cycles=0x8dfc0 avg=0x16b7 min=0x1652
>
> With ReadAllocation hints:
> /sys/kernel/debug # echo 100 > lpitest
> [ 98.617873] CPU[1] niter=100 cycles=0x7df49 avg=0x1427 min=0x1388
>
> drivers/irqchip/irq-gic-v3-its.c | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/irqchip/irq-gic-v3-its.c
> b/drivers/irqchip/irq-gic-v3-its.c
> index c5dee30..227a1eb 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -961,7 +961,7 @@ static bool its_parse_baser_device(struct its_node
> *its, struct its_baser *baser
> u32 psz, u32 *order)
> {
> u64 esz = GITS_BASER_ENTRY_SIZE(its_read_baser(its, baser));
> - u64 val = GITS_BASER_InnerShareable | GITS_BASER_WaWb;
> + u64 val = GITS_BASER_InnerShareable | GITS_BASER_RaWaWb;
> u32 ids = its->device_ids;
> u32 new_order = *order;
> bool indirect = false;
> @@ -1026,7 +1026,7 @@ static int its_alloc_tables(struct its_node *its)
> u64 typer = gic_read_typer(its->base + GITS_TYPER);
> u32 ids = GITS_TYPER_DEVBITS(typer);
> u64 shr = GITS_BASER_InnerShareable;
> - u64 cache = GITS_BASER_WaWb;
> + u64 cache = GITS_BASER_RaWaWb;
> u32 psz = SZ_64K;
> int err, i;
>
> @@ -1123,7 +1123,7 @@ static void its_cpu_init_lpis(void)
> /* set PROPBASE */
> val = (page_to_phys(gic_rdists->prop_page) |
> GICR_PROPBASER_InnerShareable |
> - GICR_PROPBASER_WaWb |
> + GICR_PROPBASER_RaWaWb |
> ((LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK));
>
> writeq_relaxed(val, rbase + GICR_PROPBASER);
> @@ -1148,7 +1148,7 @@ static void its_cpu_init_lpis(void)
> /* set PENDBASE */
> val = (page_to_phys(pend_page) |
> GICR_PENDBASER_InnerShareable |
> - GICR_PENDBASER_WaWb);
> + GICR_PENDBASER_RaWaWb);
>
> writeq_relaxed(val, rbase + GICR_PENDBASER);
> tmp = readq_relaxed(rbase + GICR_PENDBASER);
> @@ -1712,7 +1712,7 @@ static int __init its_probe_one(struct resource
> *res,
> goto out_free_tables;
>
> baser = (virt_to_phys(its->cmd_base) |
> - GITS_CBASER_WaWb |
> + GITS_CBASER_RaWaWb |
> GITS_CBASER_InnerShareable |
> (ITS_CMD_QUEUE_SZ / SZ_4K - 1) |
> GITS_CBASER_VALID);
--
Shanker Donthineni
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH 7/10] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC
From: Ziji Hu @ 2016-11-29 2:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAPDyKFr8rX04iY92OeQpSkS+3HN2-FxijCCiDb2sSKvP+TZPog@mail.gmail.com>
Hi Ulf,
On 2016/11/28 23:16, Ulf Hansson wrote:
> On 28 November 2016 at 12:38, Ziji Hu <huziji@marvell.com> wrote:
>> Hi Ulf,
>>
>> On 2016/11/28 19:13, Ulf Hansson wrote:
>>>>
>>>> As you suggest, I replace mmc_wait_for_cmd() with mmc_send_tuning(), to
>>>> send commands for testing current sampling point set in our host PHY.
>>>>
>>>> According to my test result, it shows that mmc_send_tuning() can only support
>>>> tuning command (CMD21/CMD19).
>>>> As a result, we cannot use mmc_send_tuning() when card is in the speed modes
>>>> which doesn't support tuning, such as eMMC HS SDR, eMMC HS DRR and
>>>> SD SDR 12/SDR25/DDR50. Card will not response to tuning commands in those
>>>> speed modes.
>>>>
>>>> Could you please provide suggestions for the speed mode in which tuning is
>>>> not available?
>>>>
>>>
>>> Normally the mmc host driver shouldn't have to care about what the
>>> card supports, as that is the responsibility of the mmc core to
>>> manage.
>>>
>>> The host should only need to implement the ->execute_tuning() ops,
>>> which gets called when the card supports tuning (CMD19/21). Does it
>>> make sense?
>>>
>> I think it is irrelevant to tuning procedure.
>>
>> Our host requires to adjust PHY setting after each time ios setting
>> (SDCLK/bus width/speed mode) is changed.
>> The simplified sequence is:
>> mmc change ios --> mmc_set_ios() --> ->set_ios() --> after sdhci_set_ios(),
>> adjust PHY setting.
>> During PHY setting adjustment, out host driver has to send commands to
>> test current sampling point. Tuning is another independent step.
>
> For those speed modes (or other ios changes) that *don't* requires
> tuning, then what will you do when you send the command to confirm the
> change of PHY setting and it fails?
>
> My assumption is that you will fail anyway, by propagating the error
> to the mmc core. At least that what was my understanding from your
> earlier replies, right!?
>
> Then, I think there are no point having the host driver sending a
> command to confirm the PHY settings, as the mmc core will anyway
> discover if something goes wrong when the next command is sent.
>
> Please correct me if I am wrong!
>
Sorry that I didn't make myself clear.
Our host PHY delay line consists of hundreds of sampling points.
Each sampling point represents a different phase shift.
In lower speed mode, our host driver will scan the delay line.
It will select and test multiple sampling points, other than testing
only single sampling point.
If a sampling point fails to transfer cmd/data, our host driver will
move to test next sampling point, until we find out a group of successful
sampling points which can transfer cmd/data. At last we will select
a perfect one from them.
Thank you.
Best regards,
Hu Ziji
>>
>> Thus our host needs a valid command in PHY setting adjustment. Tuning command
>> can be borrowed to complete this task in SD SDR50. But for other speed mode,
>> we have to find out a valid command.
>
> I thought we agreed on this wasn't necessary? Please see my upper response.
>
> Kind regards
> Uffe
>
^ permalink raw reply
* [PATCH] KVM: arm/arm64: Access CNTHCTL_EL2 bit fields correctly
From: Jintack Lim @ 2016-11-29 3:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <799d03f5-a929-9547-1ae7-94026b76f116@arm.com>
On Mon, Nov 28, 2016 at 1:39 PM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On 28/11/16 17:43, Marc Zyngier wrote:
>> Hi Jintack,
Hi Marc,
>>
>> On 28/11/16 16:46, Jintack Lim wrote:
>>> Bit positions of CNTHCTL_EL2 are changing depending on HCR_EL2.E2H bit.
>>> EL1PCEN and EL1PCTEN are 1st and 0th bits when E2H is not set, but they
>>> are 11th and 10th bits respectively when E2H is set. Current code is
>>> unintentionally setting wrong bits to CNTHCTL_EL2 with E2H set, which
>>> may allow guest OS to access physical timer. So, fix it.
>>>
>>> Signed-off-by: Jintack Lim <jintack@cs.columbia.edu>
>>> ---
>>> arch/arm/include/asm/kvm_timer.h | 33 +++++++++++++++++++
>>> arch/arm64/include/asm/kvm_timer.h | 62 ++++++++++++++++++++++++++++++++++++
>>> include/clocksource/arm_arch_timer.h | 6 ++--
>>> virt/kvm/arm/hyp/timer-sr.c | 8 ++---
>>> 4 files changed, 103 insertions(+), 6 deletions(-)
>>> create mode 100644 arch/arm/include/asm/kvm_timer.h
>>> create mode 100644 arch/arm64/include/asm/kvm_timer.h
>>>
>
> [...]
>
>> We could make it nicer (read "faster") by introducing a
>> hyp_alternate_select construct that only returns a value instead
>> of calling a function. I remember writing something like that
>> at some point, and dropping it...
>
> So here's what this could look like (warning, wacky code ahead,
> though I fixed a stupid bug that was present in the previous patch).
> The generated code is quite nice (no branch, only an extra mov
> instruction on the default path)... Of course, completely untested!
This looks much cleaner than my patch.
While we are at it, is it worth to consider that we just need to set
those bits once for VHE case, not for every world switch as an
optimization?
>
> diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
> index b18e852..d173902 100644
> --- a/arch/arm64/include/asm/kvm_hyp.h
> +++ b/arch/arm64/include/asm/kvm_hyp.h
> @@ -121,6 +121,17 @@ typeof(orig) * __hyp_text fname(void) \
> return val; \
> }
>
> +#define hyp_alternate_select_const(fname, orig, alt, cond) \
> +inline const typeof(orig) __hyp_text fname(void) \
> +{ \
> + typeof(alt) val = orig; \
> + asm volatile(ALTERNATIVE("nop \n", \
> + "mov %0, %1 \n", \
> + cond) \
> + : "+r" (val) : "r" (alt)); \
> + return val; \
> +}
> +
> void __vgic_v2_save_state(struct kvm_vcpu *vcpu);
> void __vgic_v2_restore_state(struct kvm_vcpu *vcpu);
> int __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu);
> diff --git a/virt/kvm/arm/hyp/timer-sr.c b/virt/kvm/arm/hyp/timer-sr.c
> index 798866a..7a783af 100644
> --- a/virt/kvm/arm/hyp/timer-sr.c
> +++ b/virt/kvm/arm/hyp/timer-sr.c
> @@ -21,11 +21,29 @@
>
> #include <asm/kvm_hyp.h>
>
> +#ifdef CONFIG_ARM64
> +static hyp_alternate_select_const(cnthclt_shift, 0, 10,
> + ARM64_HAS_VIRT_HOST_EXTN)
> +
> +#else
> +#define cnthclt_shift() (0)
> +#endif
> +
> +static inline void __hyp_text cnthctl_rmw(u32 clr, u32 set)
> +{
> + u32 val;
> + int shift = cnthclt_shift();
> +
> + val = read_sysreg(cnthctl_el2);
> + val &= ~(clr << shift);
> + val |= set << shift;
> + write_sysreg(val, cnthctl_el2);
> +}
> +
> /* vcpu is already in the HYP VA space */
> void __hyp_text __timer_save_state(struct kvm_vcpu *vcpu)
> {
> struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
> - u64 val;
>
> if (timer->enabled) {
> timer->cntv_ctl = read_sysreg_el0(cntv_ctl);
> @@ -36,9 +54,7 @@ void __hyp_text __timer_save_state(struct kvm_vcpu *vcpu)
> write_sysreg_el0(0, cntv_ctl);
>
> /* Allow physical timer/counter access for the host */
> - val = read_sysreg(cnthctl_el2);
> - val |= CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN;
> - write_sysreg(val, cnthctl_el2);
> + cnthctl_rmw(0, CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN);
>
> /* Clear cntvoff for the host */
> write_sysreg(0, cntvoff_el2);
> @@ -48,16 +64,12 @@ void __hyp_text __timer_restore_state(struct kvm_vcpu *vcpu)
> {
> struct kvm *kvm = kern_hyp_va(vcpu->kvm);
> struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
> - u64 val;
>
> /*
> * Disallow physical timer access for the guest
> * Physical counter access is allowed
> */
> - val = read_sysreg(cnthctl_el2);
> - val &= ~CNTHCTL_EL1PCEN;
> - val |= CNTHCTL_EL1PCTEN;
> - write_sysreg(val, cnthctl_el2);
> + cnthctl_rmw(CNTHCTL_EL1PCEN, CNTHCTL_EL1PCTEN);
>
> if (timer->enabled) {
> write_sysreg(kvm->arch.timer.cntvoff, cntvoff_el2);
>
> Thanks,
>
> M.
> --
> Jazz is not dead. It just smells funny...
>
^ permalink raw reply
* [PATCH v28 4/9] arm64: kdump: implement machine_crash_shutdown()
From: AKASHI Takahiro @ 2016-11-29 5:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161128150433.GC17125@e104818-lin.cambridge.arm.com>
On Mon, Nov 28, 2016 at 03:04:33PM +0000, Catalin Marinas wrote:
> On Thu, Nov 24, 2016 at 06:58:05PM +0900, AKASHI Takahiro wrote:
> > diff --git a/arch/arm64/include/asm/kexec.h b/arch/arm64/include/asm/kexec.h
> > index 04744dc..b5168e8 100644
> > --- a/arch/arm64/include/asm/kexec.h
> > +++ b/arch/arm64/include/asm/kexec.h
> > @@ -40,7 +40,47 @@
> > static inline void crash_setup_regs(struct pt_regs *newregs,
> > struct pt_regs *oldregs)
> > {
> > - /* Empty routine needed to avoid build errors. */
> > + if (oldregs) {
> > + memcpy(newregs, oldregs, sizeof(*newregs));
> > + } else {
> > + u64 tmp1, tmp2;
> > +
> > + __asm__ __volatile__ (
> > + "stp x0, x1, [%2, #16 * 0]\n"
> > + "stp x2, x3, [%2, #16 * 1]\n"
> > + "stp x4, x5, [%2, #16 * 2]\n"
> > + "stp x6, x7, [%2, #16 * 3]\n"
> > + "stp x8, x9, [%2, #16 * 4]\n"
> > + "stp x10, x11, [%2, #16 * 5]\n"
> > + "stp x12, x13, [%2, #16 * 6]\n"
> > + "stp x14, x15, [%2, #16 * 7]\n"
> > + "stp x16, x17, [%2, #16 * 8]\n"
> > + "stp x18, x19, [%2, #16 * 9]\n"
> > + "stp x20, x21, [%2, #16 * 10]\n"
> > + "stp x22, x23, [%2, #16 * 11]\n"
> > + "stp x24, x25, [%2, #16 * 12]\n"
> > + "stp x26, x27, [%2, #16 * 13]\n"
> > + "stp x28, x29, [%2, #16 * 14]\n"
> > + "mov %0, sp\n"
> > + "stp x30, %0, [%2, #16 * 15]\n"
> > +
> > + "/* faked current PSTATE */\n"
> > + "mrs %0, CurrentEL\n"
> > + "mrs %1, SPSEL\n"
> > + "orr %0, %0, %1\n"
> > + "mrs %1, DAIF\n"
> > + "orr %0, %0, %1\n"
> > + "mrs %1, NZCV\n"
> > + "orr %0, %0, %1\n"
> > + /* pc */
> > + "adr %1, 1f\n"
> > + "1:\n"
> > + "stp %1, %0, [%2, #16 * 16]\n"
> > + : "+r" (tmp1), "+r" (tmp2)
> > + : "r" (newregs)
> > + : "memory"
>
> tmp1 and tmp2 are not input arguments here, so you should use the "=&"
> modifier. With my compiler, I get warnings of these variables being used
> uninitialised.
Thank you for this heads-up.
(I misunderstood "+" a bit.)
With this change applied, I confirmed that kdump still works.
-Takahiro AKASHI
> --
> Catalin
^ permalink raw reply
* arasan,sdhci.txt "compatibility" DT binding
From: Rameshwar Sahu @ 2016-11-29 7:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <583C60CC.2020408@free.fr>
Hi Mason,
Nowhere in the documentation do they specify an "IP version".
Some documents do provide a revision number, but that's just
a *documentation* revision number, e.g.
changes in version 3.6 : fix typos
changes in version 9.1a : update company logo
That's why Xilinx used "arasan,sdhci-8.9a" and APM used
"arasan,sdhci-4.9a". These are documentation revisions.
In my opinion, that information is mostly worthless.
Arasan SD/SDIO/eMMC IP has a register which tells about the SD
specification version and Vendor version number
Reg Name: Host controller version register (offset 0FEh)
bit [15:8] is for vendor version number,
But, I have seen that Arasaan vendor version number is same as
document revision number.
On Mon, Nov 28, 2016 at 10:22 PM, Mason <slash.tmp@free.fr> wrote:
> On 28/11/2016 17:15, Arnd Bergmann wrote:
>
>> On Monday, November 28, 2016 4:44:39 PM CET Mason wrote:
>>
>>> Hello,
>>>
>>> @Shawn Lin, could you take a look below and tell me exactly
>>> which IP core(s) Rockchip is using in its SoCs?
>>>
>>> Based on the feedback I received, here is an updated list of
>>> compatible strings and controller versions dealt with by the
>>> drivers/mmc/host/sdhci-of-arasan.c code.
>>>
>>>
>>> Xilinx Zynq:
>>> "SD2.0 / SDIO2.0 / MMC3.31 AHB Host Controller"
>>> "arasan,sdhci-8.9a"
>>> NB: 8.9a is the documentation revision (dated 2011-10-19)
>>> subsequent tweaks labeled 9.0a, 9.1a, 9.2a
>>>
>>> Xilinx ZynqMP:
>>> "SD3.0 / SDIO3.0 / eMMC4.51 AHB Host Controller"
>>> "arasan,sdhci-8.9a"
>>> NB: using the same compatible string as Zynq
>>>
>>> Sigma SMP87xx
>>> "SD3.0 / SDIO3.0 / eMMC4.4 AHB Host Controller"
>>> no compatible string yet, platform-specific init required
>>>
>>> APM:
>>> "SD3.0 / SDIO3.0 / eMMC4.41 AHB Host Controller"
>>> "arasan,sdhci-4.9a"
>>> NB: 4.9a appears to be the documentation revision
>>> no functional diff with "arasan,sdhci-8.9a"
>>>
>>> Rockchip
>>> Exact IP unknown, waiting for Shawn's answer
>>> "arasan,sdhci-5.1"
>>> NB: 5.1 appears to refer to the eMMC standard supported
>>>
>>>
>>> On a final note, there are many variations of the Arasan IP.
>>> I've tracked down at least the following:
>>>
>>> SD_2.0_SDIO_2.0__MMC_3.31_AHB_Host_Controller.pdf
>>> SD_3.0_SDIO_3.0_eMMC_4.41_OCP_Host_Controller.pdf
>>> SD_3.0_SDIO_3.0_eMMC_4.4__AHB_Host_Controller.pdf
>>> SD_3.0_SDIO_3.0_eMMC_4.51_Host_Controller.pdf
>>> SD_3.0_SDIO_3.0_eMMC_4.5__Host_Controller.pdf
>>> SD_4.1_SDIO_4.1_eMMC_4.51_Host_Controller.pdf
>>> SD_4.1_SDIO_4.1_eMMC_5.1__Host_Controller.pdf
>>>
>>> It seems to me the compatible string should specify
>>> the SD/SDIO version AND the eMMC version, since it
>>> seems many combinations are allowed, e.g. eMMC 4.51
>>> has two possible SD versions.
>>>
>>> What do you think?
>>
>> It seems wrong to have the eMMC or SD version in the compatible
>> string. Is that the only difference between the documents you
>> found? Normally there should be a version of IP block itself,
>> besides the supported protocol.
>
> But that is exactly the problem :-)
>
> Nowhere in the documentation do they specify an "IP version".
> Some documents do provide a revision number, but that's just
> a *documentation* revision number, e.g.
>
> changes in version 3.6 : fix typos
> changes in version 9.1a : update company logo
>
> That's why Xilinx used "arasan,sdhci-8.9a" and APM used
> "arasan,sdhci-4.9a". These are documentation revisions.
> In my opinion, that information is mostly worthless.
>
>
> Looking more closely at SD_3.0_SDIO_3.0_eMMC_4.4__AHB_Host_Controller.pdf
> (User Guide, which has more info than Datasheet) I see this:
>
> Changed Host Controller Version Register value from 16'h0002 to 16'h7501
> Changed Host Controller Version Register value from 16'h8301 to 16'h8401
> Changed Host Controller Version Register value from 16'h8401 to 16'h8501
> Changed Host Controller Version Register to 16'h9502
> Changed Host Controller Version Register to 16'h9602
> Changed Host Controller Version Register to 16'h9902
>
> Host controller version register (offset 0FEh)
>
> Vendor Version Number 15:8
> HwInit=0x99
> This status is reserved for the vendor version number.
> The HD should not use this status.
>
> Specification Version Number 7:0
> HwInit=0x02
> This status indicates the Host Controller Spec. Version.
> The upper and lower 4-bits indicate the version.
> Description
> 00 - SD Host Specification version 1.0
> 01 - SD Host Specification version 2.00
> including only the feature of the Test Register
> 02 - SD Host Specification Version 3.00
> others - Reserved
>
> I'm not sure what this "Vendor Version Number" specifies, nor if is
> guaranteed to be unique across controllers.
>
> In SD_3.0_SDIO_3.0_eMMC_4.5__Host_Controller_UserGuide.pdf,
> they write "The Vendor Version Number is set to 0x10 (1.0)"
>
> I don't have a UserGuide for "arasan,sdhci-5.1".
>
> Regards.
>
^ permalink raw reply
* [PATCH v9 06/11] arm/arm64: vgic: Implement VGICv3 CPU interface access
From: Vijay Kilari @ 2016-11-29 7:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161128193938.GF18170@cbox>
On Tue, Nov 29, 2016 at 1:09 AM, Christoffer Dall
<christoffer.dall@linaro.org> wrote:
> On Wed, Nov 23, 2016 at 06:31:53PM +0530, vijay.kilari at gmail.com wrote:
>> From: Vijaya Kumar K <Vijaya.Kumar@cavium.com>
>>
>> VGICv3 CPU interface registers are accessed using
>> KVM_DEV_ARM_VGIC_CPU_SYSREGS ioctl. These registers are accessed
>> as 64-bit. The cpu MPIDR value is passed along with register id.
>> is used to identify the cpu for registers access.
>>
>> The VM that supports SEIs expect it on destination machine to handle
>> guest aborts and hence checked for ICC_CTLR_EL1.SEIS compatibility.
>> Similarly, VM that supports Affinity Level 3 that is required for AArch64
>> mode, is required to be supported on destination machine. Hence checked
>> for ICC_CTLR_EL1.A3V compatibility.
>>
>> The CPU system register handling is spitted into two files
>
> spitted? Did you mean 'split into' ?
>
>> vgic-sys-reg-common.c and vgic-sys-reg-v3.c.
>> The vgic-sys-reg-common.c handles read and write of VGIC CPU registers
>
> So this is weird because everything in virt/kvm/arm/ is exactly supposed
> to be common between arm and arm64 already.
>
> I would rather that you had a copy of vgic-sys-reg-v3.c in arch/arm/kvm/
> and in arch/arm64/kvm/ each taking care of its own architecture.
>
> But note that I didn't actually require that you implemented support for
> GICv3 migration on AArch32 hosts for these patches, I just didn't want
> thigns to silently break.
>
> If we cannot test the AArch32 implementation, we should potentially just
> make sure that is not supported yet, return a proper error to userspace
> and get the AArch64 host implementation correct.
>
> I suggest you move your:
> virt/kvm/arm/vgic/vgic-sys-reg-v3.c to
> arch/arm64/kvm/vgic-sys-reg-v3.c
>
> and rename
> virt/kvm/arm/vgic/vgic-sys-reg-common.c to
> virt/kvm/arm/vgic/vgic-sys-reg-v3.c
>
> And then wait with the AArch32 host side for now, but just make sure it
> compiles and returns an error as opposed to crashing the system if
> someone tries to excercise this interface on an AArch32 host.
I will add arch/arm/kvm/vgic-coproc-v3.c (pls check if file name is ok or not?)
and return -ENXIO as shown below and update document accordingly.
int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, bool is_write, u64 id,
u64 *reg)
{
/*
* TODO: Implement for AArch32
*/
return -ENXIO;
}
int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, bool is_write, u64 id,
u64 *reg)
{
/*
* TODO: Implement for AArch32
*/
return -ENXIO;
}
>
>> for both AArch64 and AArch32 mode. The vgic-sys-reg-v3.c handles AArch64
>> mode and is compiled only for AArch64 mode.
>>
>> Updated arch/arm/include/uapi/asm/kvm.h with new definitions
>> required to compile for AArch32.
>>
>> The version of VGIC v3 specification is define here
>> Documentation/virtual/kvm/devices/arm-vgic-v3.txt
>>
>> Signed-off-by: Pavel Fedin <p.fedin@samsung.com>
>> Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@cavium.com>
>> ---
[...]
>> +static bool access_gic_aprn(struct kvm_vcpu *vcpu, bool is_write, u8 apr,
>> + u8 idx, unsigned long *reg)
>> +{
>> + struct vgic_cpu *vgic_v3_cpu = &vcpu->arch.vgic_cpu;
>> +
>> + /* num_pri_bits are initialized with HW supported values.
>> + * We can rely safely on num_pri_bits even if VM has not
>> + * restored ICC_CTLR_EL1 before restoring APnR registers.
>> + */
>
> nit: commenting style
ok
>
>> + switch (vgic_v3_cpu->num_pri_bits) {
>> + case 7:
>> + vgic_v3_access_apr_reg(vcpu, is_write, apr, idx, reg);
>> + break;
>> + case 6:
>> + if (idx > 1)
>> + goto err;
>> + vgic_v3_access_apr_reg(vcpu, is_write, apr, idx, reg);
>> + break;
>> + default:
>> + if (idx > 0)
>> + goto err;
>> + vgic_v3_access_apr_reg(vcpu, is_write, apr, idx, reg);
>> + }
>
> It looks to me like userspace can then program active priorities with
> higher numbers than what it will program num_pri_bits to later. Is that
> not weird, or am I missing something?
As long as it is within HW supported priorities it is safe.
>
>> +
>> + return true;
>> +err:
>> + if (!is_write)
>> + *reg = 0;
>> +
>> + return false;
>> +}
>> +
>> +bool access_gic_ap0r_reg(struct kvm_vcpu *vcpu, bool is_write, u8 idx,
>> + unsigned long *reg)
>> +{
>> + return access_gic_aprn(vcpu, is_write, 0, idx, reg);
>> +}
>> +
>> +bool access_gic_ap1r_reg(struct kvm_vcpu *vcpu, bool is_write, u8 idx,
>> + unsigned long *reg)
>> +{
>> + return access_gic_aprn(vcpu, is_write, 1, idx, reg);
>> +}
>> +
>> +bool access_gic_sre_reg(struct kvm_vcpu *vcpu, bool is_write,
>> + unsigned long *reg)
>> +{
>> + struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3;
>> +
>> + /* Validate SRE bit */
>> + if (is_write) {
>> + if (!(*reg & ICC_SRE_EL1_SRE))
>> + return false;
>> + } else {
>> + *reg = vgicv3->vgic_sre;
>> + }
>> +
>> + return true;
>> +}
>> diff --git a/virt/kvm/arm/vgic/vgic-sys-reg-v3.c b/virt/kvm/arm/vgic/vgic-sys-reg-v3.c
>> new file mode 100644
>> index 0000000..82c2f02
>> --- /dev/null
>> +++ b/virt/kvm/arm/vgic/vgic-sys-reg-v3.c
>> @@ -0,0 +1,142 @@
>> +/*
>> + * VGIC system registers handling functions
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/kvm.h>
>> +#include <linux/kvm_host.h>
>> +#include <asm/kvm_emulate.h>
>> +#include "vgic.h"
>> +#include "sys_regs.h"
>> +
>> +#define ACCESS_SYS_REG(REG) \
>> +static bool access_gic_##REG##_sys_reg(struct kvm_vcpu *vcpu, \
>> + struct sys_reg_params *p, \
>> + const struct sys_reg_desc *r) \
>> +{ \
>> + unsigned long tmp; \
>> + bool ret; \
>> + \
>> + if (p->is_write) \
>> + tmp = p->regval; \
>> + ret = access_gic_##REG##_reg(vcpu, p->is_write, &tmp); \
>> + if (!p->is_write) \
>> + p->regval = tmp; \
>> + \
>> + return ret; \
>> +}
>> +
>> +ACCESS_SYS_REG(ctlr)
>> +ACCESS_SYS_REG(pmr)
>> +ACCESS_SYS_REG(bpr0)
>> +ACCESS_SYS_REG(bpr1)
>> +ACCESS_SYS_REG(sre)
>> +ACCESS_SYS_REG(grpen0)
>> +ACCESS_SYS_REG(grpen1)
>> +
>> +#define ACCESS_APNR_SYS_REG(REG) \
>> +static bool access_gic_##REG##_sys_reg(struct kvm_vcpu *vcpu, \
>> + struct sys_reg_params *p, \
>> + const struct sys_reg_desc *r) \
>> +{ \
>> + unsigned long tmp; \
>> + u8 idx = p->Op2 & 3; \
>> + bool ret; \
>> + \
>> + if (p->is_write) \
>> + tmp = p->regval; \
>> + ret = access_gic_##REG##_reg(vcpu, p->is_write, idx, &tmp); \
>> + if (!p->is_write) \
>> + p->regval = tmp; \
>> + \
>> + return ret; \
>> +}
>> +
>> +ACCESS_APNR_SYS_REG(ap0r)
>> +ACCESS_APNR_SYS_REG(ap1r)
>
> I don't get these indirections. Why can't you call the functions
> directly?
The code is same for accessing the registers hence added this indirection.
>
>> +
>> +static const struct sys_reg_desc gic_v3_icc_reg_descs[] = {
>> + /* ICC_PMR_EL1 */
>> + { Op0(3), Op1(0), CRn(4), CRm(6), Op2(0), access_gic_pmr_sys_reg },
>> + /* ICC_BPR0_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(8), Op2(3), access_gic_bpr0_sys_reg },
>> + /* ICC_AP0R0_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(8), Op2(4), access_gic_ap0r_sys_reg },
>> + /* ICC_AP0R1_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(8), Op2(5), access_gic_ap0r_sys_reg },
>> + /* ICC_AP0R2_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(8), Op2(6), access_gic_ap0r_sys_reg },
>> + /* ICC_AP0R3_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(8), Op2(7), access_gic_ap0r_sys_reg },
>> + /* ICC_AP1R0_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(9), Op2(0), access_gic_ap1r_sys_reg },
>> + /* ICC_AP1R1_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(9), Op2(1), access_gic_ap1r_sys_reg },
>> + /* ICC_AP1R2_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(9), Op2(2), access_gic_ap1r_sys_reg },
>> + /* ICC_AP1R3_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(9), Op2(3), access_gic_ap1r_sys_reg },
>> + /* ICC_BPR1_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(12), Op2(3), access_gic_bpr1_sys_reg },
>> + /* ICC_CTLR_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(12), Op2(4), access_gic_ctlr_sys_reg },
>> + /* ICC_SRE_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(12), Op2(5), access_gic_sre_sys_reg },
>> + /* ICC_IGRPEN0_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(12), Op2(6), access_gic_grpen0_sys_reg },
>> + /* ICC_GRPEN1_EL1 */
>> + { Op0(3), Op1(0), CRn(12), CRm(12), Op2(7), access_gic_grpen1_sys_reg },
>> +};
>> +
>> +int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, bool is_write, u64 id,
>> + u64 *reg)
>> +{
>> + struct sys_reg_params params;
>> + u64 sysreg = (id & KVM_DEV_ARM_VGIC_SYSREG_MASK) | KVM_REG_SIZE_U64;
>> +
>> + params.regval = *reg;
>> + params.is_write = is_write;
>> + params.is_aarch32 = false;
>> + params.is_32bit = false;
>> +
>> + if (find_reg_by_id(sysreg, ¶ms, gic_v3_icc_reg_descs,
>> + ARRAY_SIZE(gic_v3_icc_reg_descs)))
>> + return 0;
>> +
>> + return -ENXIO;
>> +}
>> +
>> +int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, bool is_write, u64 id,
>> + u64 *reg)
>> +{
>> + struct sys_reg_params params;
>> + const struct sys_reg_desc *r;
>> + u64 sysreg = (id & KVM_DEV_ARM_VGIC_SYSREG_MASK) | KVM_REG_SIZE_U64;
>> +
>> + if (is_write)
>> + params.regval = *reg;
>> + params.is_write = is_write;
>> + params.is_aarch32 = false;
>> + params.is_32bit = false;
>> +
>> + r = find_reg_by_id(sysreg, ¶ms, gic_v3_icc_reg_descs,
>> + ARRAY_SIZE(gic_v3_icc_reg_descs));
>> + if (!r)
>> + return -ENXIO;
>> +
>> + if (!r->access(vcpu, ¶ms, r))
>> + return -EINVAL;
>> +
>> + if (!is_write)
>> + *reg = params.regval;
>> +
>> + return 0;
>> +}
>> diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
>> index a3ff04b..6e7400e 100644
>> --- a/virt/kvm/arm/vgic/vgic-v3.c
>> +++ b/virt/kvm/arm/vgic/vgic-v3.c
>> @@ -240,6 +240,13 @@ void vgic_v3_enable(struct kvm_vcpu *vcpu)
>> vgic_v3->vgic_sre = 0;
>> }
>>
>> + vcpu->arch.vgic_cpu.num_id_bits = (kvm_vgic_global_state.ich_vtr_el2 &
>> + ICH_VTR_ID_BITS_MASK) >>
>> + ICH_VTR_ID_BITS_SHIFT;
>> + vcpu->arch.vgic_cpu.num_pri_bits = ((kvm_vgic_global_state.ich_vtr_el2 &
>> + ICH_VTR_PRI_BITS_MASK) >>
>> + ICH_VTR_PRI_BITS_SHIFT) + 1;
>> +
>> /* Get the show on the road... */
>> vgic_v3->vgic_hcr = ICH_HCR_EN;
>> }
>> @@ -340,6 +347,7 @@ int vgic_v3_probe(const struct gic_kvm_info *info)
>> */
>> kvm_vgic_global_state.nr_lr = (ich_vtr_el2 & 0xf) + 1;
>> kvm_vgic_global_state.can_emulate_gicv2 = false;
>> + kvm_vgic_global_state.ich_vtr_el2 = ich_vtr_el2;
>>
>> if (!info->vcpu.start) {
>> kvm_info("GICv3: no GICV resource entry\n");
>> diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
>> index 9232791..af23399 100644
>> --- a/virt/kvm/arm/vgic/vgic.h
>> +++ b/virt/kvm/arm/vgic/vgic.h
>> @@ -140,6 +140,28 @@ int vgic_v3_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
>> int offset, u32 *val);
>> int vgic_v3_redist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
>> int offset, u32 *val);
>> +int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, bool is_write,
>> + u64 id, u64 *val);
>> +int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, bool is_write, u64 id,
>> + u64 *reg);
>> +bool access_gic_ctlr_reg(struct kvm_vcpu *vcpu, bool is_write,
>> + unsigned long *reg);
>> +bool access_gic_pmr_reg(struct kvm_vcpu *vcpu, bool is_write,
>> + unsigned long *reg);
>> +bool access_gic_bpr0_reg(struct kvm_vcpu *vcpu, bool is_write,
>> + unsigned long *reg);
>> +bool access_gic_bpr1_reg(struct kvm_vcpu *vcpu, bool is_write,
>> + unsigned long *reg);
>> +bool access_gic_grpen0_reg(struct kvm_vcpu *vcpu, bool is_write,
>> + unsigned long *reg);
>> +bool access_gic_grpen1_reg(struct kvm_vcpu *vcpu, bool is_write,
>> + unsigned long *reg);
>> +bool access_gic_ap0r_reg(struct kvm_vcpu *vcpu, bool is_write,
>> + u8 idx, unsigned long *reg);
>> +bool access_gic_ap1r_reg(struct kvm_vcpu *vcpu, bool is_write,
>> + u8 idx, unsigned long *reg);
>> +bool access_gic_sre_reg(struct kvm_vcpu *vcpu, bool is_write,
>> + unsigned long *reg);
>
> nit: since all of this is exported, I would name them vgic_access_ctlr()
> and so on. The _reg postfix is probably also unnecessary for all of
> these.
OK
>
>> int kvm_register_vgic_device(unsigned long type);
>> void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
>> void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
>> --
>> 1.9.1
>>
>
> Thanks,
> -Christoffer
^ permalink raw reply
* [PATCH 0/2] Support ARM SMCC SoC vendor quirks
From: Andy Gross @ 2016-11-29 7:44 UTC (permalink / raw)
To: linux-arm-kernel
At least one SoC vendor (Qualcomm) requires additional processing done
during ARM SMCCC calls. As such, an additional parameter to the
arm_smccc_smc is required to be able to handle SoC specific quirks.
The Qualcomm quirk is necessary due to the fact that the scm call can
be interrupted on Qualcomm ARM64 platforms. When this occurs, the
call must be restarted using information that was passed back during
the original smc call.
The first patch in this series adds a quirk structure and also adds a
quirk paramter to arm_smccc_smc calls. All of the current users of
the call are modified to accomodate the new API parameter.
The second patch adds the Qualcomm quirk and also implements the
Qualcomm firmware changes required to handle the restarting of the
interrupted SMC call.
The original patch set for the SMCCC session ID is located at:
https://lkml.org/lkml/2016/8/20/7
Andy Gross (2):
arm: kernel: Add SMC structure parameter
firmware: qcom: scm: Fix interrupted SCM calls
arch/arm/kernel/smccc-call.S | 3 ++-
arch/arm/mach-artpec/board-artpec6.c | 2 +-
arch/arm64/kernel/asm-offsets.c | 7 +++++--
arch/arm64/kernel/smccc-call.S | 12 ++++++++++--
drivers/clk/rockchip/clk-ddr.c | 6 +++---
drivers/devfreq/rk3399_dmc.c | 6 +++---
drivers/firmware/meson/meson_sm.c | 2 +-
drivers/firmware/psci.c | 2 +-
drivers/firmware/qcom_scm-64.c | 13 ++++++++++---
drivers/gpu/drm/mediatek/mtk_hdmi.c | 2 +-
include/linux/arm-smccc.h | 29 ++++++++++++++++++++++++-----
11 files changed, 61 insertions(+), 23 deletions(-)
--
1.9.1
^ permalink raw reply
* [PATCH 1/2] arm: kernel: Add SMC structure parameter
From: Andy Gross @ 2016-11-29 7:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480405463-23165-1-git-send-email-andy.gross@linaro.org>
This patch adds a quirk parameter to the arm_smccc_smc call. The quirk
structure allows for specialized SMC operations due to SoC specific
requirements.
This patch also fixes up all the current users of the arm_smccc_smc API.
This patch and partial implementation was suggested by Will Deacon.
Signed-off-by: Andy Gross <andy.gross@linaro.org>
---
arch/arm/kernel/smccc-call.S | 3 ++-
arch/arm/mach-artpec/board-artpec6.c | 2 +-
arch/arm64/kernel/asm-offsets.c | 7 +++++--
arch/arm64/kernel/smccc-call.S | 3 ++-
drivers/clk/rockchip/clk-ddr.c | 6 +++---
drivers/devfreq/rk3399_dmc.c | 6 +++---
drivers/firmware/meson/meson_sm.c | 2 +-
drivers/firmware/psci.c | 2 +-
drivers/firmware/qcom_scm-64.c | 4 ++--
drivers/gpu/drm/mediatek/mtk_hdmi.c | 2 +-
include/linux/arm-smccc.h | 18 ++++++++++++++++--
11 files changed, 37 insertions(+), 18 deletions(-)
diff --git a/arch/arm/kernel/smccc-call.S b/arch/arm/kernel/smccc-call.S
index 37669e7..e77950a 100644
--- a/arch/arm/kernel/smccc-call.S
+++ b/arch/arm/kernel/smccc-call.S
@@ -47,7 +47,8 @@ UNWIND( .fnend)
/*
* void smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
* unsigned long a3, unsigned long a4, unsigned long a5,
- * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
+ * struct arm_smccc_quirk *quirk)
*/
ENTRY(arm_smccc_smc)
SMCCC SMCCC_SMC
diff --git a/arch/arm/mach-artpec/board-artpec6.c b/arch/arm/mach-artpec/board-artpec6.c
index a0b1979..3a4d330 100644
--- a/arch/arm/mach-artpec/board-artpec6.c
+++ b/arch/arm/mach-artpec/board-artpec6.c
@@ -50,7 +50,7 @@ static void artpec6_l2c310_write_sec(unsigned long val, unsigned reg)
struct arm_smccc_res res;
arm_smccc_smc(SECURE_OP_L2C_WRITEREG, reg, val, 0,
- 0, 0, 0, 0, &res);
+ 0, 0, 0, 0, &res, NULL);
WARN_ON(res.a0);
}
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 4a2f0f0..c58ddf8 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -140,8 +140,11 @@ int main(void)
DEFINE(SLEEP_STACK_DATA_SYSTEM_REGS, offsetof(struct sleep_stack_data, system_regs));
DEFINE(SLEEP_STACK_DATA_CALLEE_REGS, offsetof(struct sleep_stack_data, callee_saved_regs));
#endif
- DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0));
- DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2));
+ DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0));
+ DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2));
+ DEFINE(ARM_SMCCC_QUIRK_ID_OFFS, offsetof(struct arm_smccc_quirk, id));
+ DEFINE(ARM_SMCCC_QUIRK_STATE_OFFS, offsetof(struct arm_smccc_quirk, state));
+
BLANK();
DEFINE(HIBERN_PBE_ORIG, offsetof(struct pbe, orig_address));
DEFINE(HIBERN_PBE_ADDR, offsetof(struct pbe, address));
diff --git a/arch/arm64/kernel/smccc-call.S b/arch/arm64/kernel/smccc-call.S
index ae0496f..7b0b3f6 100644
--- a/arch/arm64/kernel/smccc-call.S
+++ b/arch/arm64/kernel/smccc-call.S
@@ -27,7 +27,8 @@
/*
* void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
* unsigned long a3, unsigned long a4, unsigned long a5,
- * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
+ * struct arm_smccc_quirk *quirk)
*/
ENTRY(arm_smccc_smc)
SMCCC smc
diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c
index 8feba93..cbdc0f8 100644
--- a/drivers/clk/rockchip/clk-ddr.c
+++ b/drivers/clk/rockchip/clk-ddr.c
@@ -45,7 +45,7 @@ static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate,
spin_lock_irqsave(ddrclk->lock, flags);
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0,
ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE,
- 0, 0, 0, 0, &res);
+ 0, 0, 0, 0, &res, NULL);
spin_unlock_irqrestore(ddrclk->lock, flags);
return res.a0;
@@ -59,7 +59,7 @@ static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate,
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE,
- 0, 0, 0, 0, &res);
+ 0, 0, 0, 0, &res, NULL);
return res.a0;
}
@@ -72,7 +72,7 @@ static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw,
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0,
ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE,
- 0, 0, 0, 0, &res);
+ 0, 0, 0, 0, &res, NULL);
return res.a0;
}
diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c
index e24b73d..a2e1f4c 100644
--- a/drivers/devfreq/rk3399_dmc.c
+++ b/drivers/devfreq/rk3399_dmc.c
@@ -258,7 +258,7 @@ static irqreturn_t rk3399_dmc_irq(int irq, void *dev_id)
/* Clear the DCF interrupt */
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
ROCKCHIP_SIP_CONFIG_DRAM_CLR_IRQ,
- 0, 0, 0, 0, &res);
+ 0, 0, 0, 0, &res, NULL);
return IRQ_HANDLED;
}
@@ -395,7 +395,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev)
for (index = 0; index < size; index++) {
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, *timing++, index,
ROCKCHIP_SIP_CONFIG_DRAM_SET_PARAM,
- 0, 0, 0, 0, &res);
+ 0, 0, 0, 0, &res, NULL);
if (res.a0) {
dev_err(dev, "Failed to set dram param: %ld\n",
res.a0);
@@ -406,7 +406,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev)
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
ROCKCHIP_SIP_CONFIG_DRAM_INIT,
- 0, 0, 0, 0, &res);
+ 0, 0, 0, 0, &res, NULL);
/*
* We add a devfreq driver to our parent since it has a device tree node
diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c
index b0d2549..8863637 100644
--- a/drivers/firmware/meson/meson_sm.c
+++ b/drivers/firmware/meson/meson_sm.c
@@ -74,7 +74,7 @@ static u32 __meson_sm_call(u32 cmd, u32 arg0, u32 arg1, u32 arg2,
{
struct arm_smccc_res res;
- arm_smccc_smc(cmd, arg0, arg1, arg2, arg3, arg4, 0, 0, &res);
+ arm_smccc_smc(cmd, arg0, arg1, arg2, arg3, arg4, 0, 0, &res, NULL);
return res.a0;
}
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index 8263429..130c50f 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -126,7 +126,7 @@ static unsigned long __invoke_psci_fn_smc(unsigned long function_id,
{
struct arm_smccc_res res;
- arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
+ arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res, NULL);
return res.a0;
}
diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c
index 4a0f5ea..d164a9b 100644
--- a/drivers/firmware/qcom_scm-64.c
+++ b/drivers/firmware/qcom_scm-64.c
@@ -134,7 +134,7 @@ static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
do {
arm_smccc_smc(cmd, desc->arginfo, desc->args[0],
desc->args[1], desc->args[2], x5, 0, 0,
- res);
+ res, NULL);
} while (res->a0 == QCOM_SCM_INTERRUPTED);
mutex_unlock(&qcom_scm_lock);
@@ -253,7 +253,7 @@ void __qcom_scm_init(void)
ARM_SMCCC_OWNER_SIP, function);
arm_smccc_smc(cmd, QCOM_SCM_ARGS(1), cmd & (~BIT(ARM_SMCCC_TYPE_SHIFT)),
- 0, 0, 0, 0, 0, &res);
+ 0, 0, 0, 0, 0, &res, NULL);
if (!res.a0 && res.a1)
qcom_smccc_convention = ARM_SMCCC_SMC_64;
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index 71227de..073f3845 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -240,7 +240,7 @@ static void mtk_hdmi_hw_make_reg_writable(struct mtk_hdmi *hdmi, bool enable)
* this control bit to enable HDMI output in supervisor mode.
*/
arm_smccc_smc(MTK_SIP_SET_AUTHORIZED_SECURE_REG, 0x14000904, 0x80000000,
- 0, 0, 0, 0, 0, &res);
+ 0, 0, 0, 0, 0, &res, NULL);
regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20,
HDMI_PCLK_FREE_RUN, enable ? HDMI_PCLK_FREE_RUN : 0);
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index b5abfda..74231b4c 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -72,19 +72,33 @@ struct arm_smccc_res {
};
/**
+ * struct arm_smccc_quirk - Contains quirk information
+ * id contains quirk identification
+ * state contains the quirk specific information
+ */
+struct arm_smccc_quirk {
+ int id;
+ union {
+ unsigned long a6;
+ } state;
+};
+
+/**
* arm_smccc_smc() - make SMC calls
* @a0-a7: arguments passed in registers 0 to 7
* @res: result values from registers 0 to 3
+ * @quirk: optional quirk structure
*
* This function is used to make SMC calls following SMC Calling Convention.
* The content of the supplied param are copied to registers 0 to 7 prior
* to the SMC instruction. The return values are updated with the content
- * from register 0 to 3 on return from the SMC instruction.
+ * from register 0 to 3 on return from the SMC instruction. An optional
+ * quirk structure provides vendor specific behavior.
*/
asmlinkage void arm_smccc_smc(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, unsigned long a6, unsigned long a7,
- struct arm_smccc_res *res);
+ struct arm_smccc_res *res, struct arm_smccc_quirk *quirk);
/**
* arm_smccc_hvc() - make HVC calls
--
1.9.1
^ permalink raw reply related
* [PATCH 2/2] firmware: qcom: scm: Fix interrupted SCM calls
From: Andy Gross @ 2016-11-29 7:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480405463-23165-1-git-send-email-andy.gross@linaro.org>
This patch adds a Qualcomm specific quirk to the arm_smccc_smc call.
On Qualcomm ARM64 platforms, the SMC call can return before it has
completed. If this occurs, the call can be restarted, but it requires
using the returned session ID value from the interrupted SMC call.
The quirk stores off the session ID from the interrupted call in the
quirk structure so that it can be used by the caller.
This patch folds in a fix given by Sricharan R:
https://lkml.org/lkml/2016/9/28/272
Signed-off-by: Andy Gross <andy.gross@linaro.org>
---
arch/arm64/kernel/smccc-call.S | 9 ++++++++-
drivers/firmware/qcom_scm-64.c | 11 +++++++++--
include/linux/arm-smccc.h | 11 ++++++++---
3 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/kernel/smccc-call.S b/arch/arm64/kernel/smccc-call.S
index 7b0b3f6..8191fde 100644
--- a/arch/arm64/kernel/smccc-call.S
+++ b/arch/arm64/kernel/smccc-call.S
@@ -12,6 +12,7 @@
*
*/
#include <linux/linkage.h>
+#include <linux/arm-smccc.h>
#include <asm/asm-offsets.h>
.macro SMCCC instr
@@ -20,7 +21,13 @@
ldr x4, [sp]
stp x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
stp x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
- ret
+ ldr x4, [sp, #8]
+ cbz x4, 1f /* no quirk structure */
+ ldr x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS]
+ cmp x9, #ARM_SMCCC_QUIRK_QCOM_A6
+ b.ne 1f
+ str x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS]
+1: ret
.cfi_endproc
.endm
diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c
index d164a9b..6e997a6 100644
--- a/drivers/firmware/qcom_scm-64.c
+++ b/drivers/firmware/qcom_scm-64.c
@@ -91,6 +91,7 @@ static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
dma_addr_t args_phys = 0;
void *args_virt = NULL;
size_t alloc_len;
+ struct arm_smccc_quirk quirk = {.id = ARM_SMCCC_QUIRK_QCOM_A6};
if (unlikely(arglen > N_REGISTER_ARGS)) {
alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64);
@@ -131,10 +132,16 @@ static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
qcom_smccc_convention,
ARM_SMCCC_OWNER_SIP, fn_id);
+ quirk.state.a6 = 0;
+
do {
arm_smccc_smc(cmd, desc->arginfo, desc->args[0],
- desc->args[1], desc->args[2], x5, 0, 0,
- res, NULL);
+ desc->args[1], desc->args[2], x5,
+ quirk.state.a6, 0, res, &quirk);
+
+ if (res->a0 == QCOM_SCM_INTERRUPTED)
+ cmd = res->a0;
+
} while (res->a0 == QCOM_SCM_INTERRUPTED);
mutex_unlock(&qcom_scm_lock);
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index 74231b4c..0a239a0 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -14,9 +14,6 @@
#ifndef __LINUX_ARM_SMCCC_H
#define __LINUX_ARM_SMCCC_H
-#include <linux/linkage.h>
-#include <linux/types.h>
-
/*
* This file provides common defines for ARM SMC Calling Convention as
* specified in
@@ -60,6 +57,13 @@
#define ARM_SMCCC_OWNER_TRUSTED_OS 50
#define ARM_SMCCC_OWNER_TRUSTED_OS_END 63
+#define ARM_SMCCC_QUIRK_NONE 0
+#define ARM_SMCCC_QUIRK_QCOM_A6 1 /* Save/restore register a6 */
+
+#ifndef __ASSEMBLY__
+
+#include <linux/linkage.h>
+#include <linux/types.h>
/**
* struct arm_smccc_res - Result from SMC/HVC call
* @a0-a3 result values from registers 0 to 3
@@ -115,4 +119,5 @@ asmlinkage void arm_smccc_hvc(unsigned long a0, unsigned long a1,
unsigned long a5, unsigned long a6, unsigned long a7,
struct arm_smccc_res *res);
+#endif /*__ASSEMBLY__*/
#endif /*__LINUX_ARM_SMCCC_H*/
--
1.9.1
^ permalink raw reply related
* [PATCH 7/10] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC
From: Ulf Hansson @ 2016-11-29 7:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <c30cead8-17b6-48b0-7355-cd82268842e1@marvell.com>
On 29 November 2016 at 03:53, Ziji Hu <huziji@marvell.com> wrote:
> Hi Ulf,
>
> On 2016/11/28 23:16, Ulf Hansson wrote:
>> On 28 November 2016 at 12:38, Ziji Hu <huziji@marvell.com> wrote:
>>> Hi Ulf,
>>>
>>> On 2016/11/28 19:13, Ulf Hansson wrote:
>>>>>
>>>>> As you suggest, I replace mmc_wait_for_cmd() with mmc_send_tuning(), to
>>>>> send commands for testing current sampling point set in our host PHY.
>>>>>
>>>>> According to my test result, it shows that mmc_send_tuning() can only support
>>>>> tuning command (CMD21/CMD19).
>>>>> As a result, we cannot use mmc_send_tuning() when card is in the speed modes
>>>>> which doesn't support tuning, such as eMMC HS SDR, eMMC HS DRR and
>>>>> SD SDR 12/SDR25/DDR50. Card will not response to tuning commands in those
>>>>> speed modes.
>>>>>
>>>>> Could you please provide suggestions for the speed mode in which tuning is
>>>>> not available?
>>>>>
>>>>
>>>> Normally the mmc host driver shouldn't have to care about what the
>>>> card supports, as that is the responsibility of the mmc core to
>>>> manage.
>>>>
>>>> The host should only need to implement the ->execute_tuning() ops,
>>>> which gets called when the card supports tuning (CMD19/21). Does it
>>>> make sense?
>>>>
>>> I think it is irrelevant to tuning procedure.
>>>
>>> Our host requires to adjust PHY setting after each time ios setting
>>> (SDCLK/bus width/speed mode) is changed.
>>> The simplified sequence is:
>>> mmc change ios --> mmc_set_ios() --> ->set_ios() --> after sdhci_set_ios(),
>>> adjust PHY setting.
>>> During PHY setting adjustment, out host driver has to send commands to
>>> test current sampling point. Tuning is another independent step.
>>
>> For those speed modes (or other ios changes) that *don't* requires
>> tuning, then what will you do when you send the command to confirm the
>> change of PHY setting and it fails?
>>
>> My assumption is that you will fail anyway, by propagating the error
>> to the mmc core. At least that what was my understanding from your
>> earlier replies, right!?
>>
>> Then, I think there are no point having the host driver sending a
>> command to confirm the PHY settings, as the mmc core will anyway
>> discover if something goes wrong when the next command is sent.
>>
>> Please correct me if I am wrong!
>>
>
> Sorry that I didn't make myself clear.
>
> Our host PHY delay line consists of hundreds of sampling points.
> Each sampling point represents a different phase shift.
>
> In lower speed mode, our host driver will scan the delay line.
> It will select and test multiple sampling points, other than testing
> only single sampling point.
>
> If a sampling point fails to transfer cmd/data, our host driver will
> move to test next sampling point, until we find out a group of successful
> sampling points which can transfer cmd/data. At last we will select
> a perfect one from them.
Ahh, I see. Unfortunate, this is going to be very hard to implement properly.
The main problem is that the host driver has *no* knowledge about the
internal state of the card, as that is the responsibility of the mmc
core to keep track of.
If the host driver would send a command during every update of the
"ios" setting, from ->set_ios(), for sure it would lead to commands
being sent that are "forbidden" in the current internal state of the
card.
This would lead to that the card initialization sequence fails,
because the card may move to an unknown internal state and the mmc
core would have no knowledge about what happened.
Hmm..
Can you specify, *exactly*, under which "ios updates" you need to
verify updated PHY setting changes by sending a cmd/data? Also, please
specify if it's enough to only test the CMD line or also DATA lines.
Kind regards
Uffe
^ permalink raw reply
* [PATCH] crypto: arm64/sha2: add generated .S files to .gitignore
From: Herbert Xu @ 2016-11-29 8:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480340269-30418-1-git-send-email-ard.biesheuvel@linaro.org>
On Mon, Nov 28, 2016 at 02:37:49PM +0100, Ard Biesheuvel wrote:
> Add the files that are generated by the recently merged OpenSSL
> SHA-256/512 implementation to .gitignore so Git disregards them
> when showing untracked files.
>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Patch applied. Thanks.
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* [PATCH v9 06/11] arm/arm64: vgic: Implement VGICv3 CPU interface access
From: Christoffer Dall @ 2016-11-29 8:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CALicx6s4E0-VJMm6Ff5qu=sZgXu-mUnJdDCmaiZO0i3yzF8wxA@mail.gmail.com>
On Tue, Nov 29, 2016 at 01:08:26PM +0530, Vijay Kilari wrote:
> On Tue, Nov 29, 2016 at 1:09 AM, Christoffer Dall
> <christoffer.dall@linaro.org> wrote:
> > On Wed, Nov 23, 2016 at 06:31:53PM +0530, vijay.kilari at gmail.com wrote:
> >> From: Vijaya Kumar K <Vijaya.Kumar@cavium.com>
> >>
> >> VGICv3 CPU interface registers are accessed using
> >> KVM_DEV_ARM_VGIC_CPU_SYSREGS ioctl. These registers are accessed
> >> as 64-bit. The cpu MPIDR value is passed along with register id.
> >> is used to identify the cpu for registers access.
> >>
> >> The VM that supports SEIs expect it on destination machine to handle
> >> guest aborts and hence checked for ICC_CTLR_EL1.SEIS compatibility.
> >> Similarly, VM that supports Affinity Level 3 that is required for AArch64
> >> mode, is required to be supported on destination machine. Hence checked
> >> for ICC_CTLR_EL1.A3V compatibility.
> >>
> >> The CPU system register handling is spitted into two files
> >
> > spitted? Did you mean 'split into' ?
> >
> >> vgic-sys-reg-common.c and vgic-sys-reg-v3.c.
> >> The vgic-sys-reg-common.c handles read and write of VGIC CPU registers
> >
> > So this is weird because everything in virt/kvm/arm/ is exactly supposed
> > to be common between arm and arm64 already.
> >
> > I would rather that you had a copy of vgic-sys-reg-v3.c in arch/arm/kvm/
> > and in arch/arm64/kvm/ each taking care of its own architecture.
> >
> > But note that I didn't actually require that you implemented support for
> > GICv3 migration on AArch32 hosts for these patches, I just didn't want
> > thigns to silently break.
> >
> > If we cannot test the AArch32 implementation, we should potentially just
> > make sure that is not supported yet, return a proper error to userspace
> > and get the AArch64 host implementation correct.
> >
> > I suggest you move your:
> > virt/kvm/arm/vgic/vgic-sys-reg-v3.c to
> > arch/arm64/kvm/vgic-sys-reg-v3.c
> >
> > and rename
> > virt/kvm/arm/vgic/vgic-sys-reg-common.c to
> > virt/kvm/arm/vgic/vgic-sys-reg-v3.c
> >
> > And then wait with the AArch32 host side for now, but just make sure it
> > compiles and returns an error as opposed to crashing the system if
> > someone tries to excercise this interface on an AArch32 host.
>
> I will add arch/arm/kvm/vgic-coproc-v3.c (pls check if file name is ok or not?)
I would call it vgic-v3-coproc.c
> and return -ENXIO as shown below and update document accordingly.
>
> int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, bool is_write, u64 id,
> u64 *reg)
> {
> /*
> * TODO: Implement for AArch32
> */
> return -ENXIO;
> }
>
> int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, bool is_write, u64 id,
> u64 *reg)
> {
> /*
> * TODO: Implement for AArch32
> */
> return -ENXIO;
> }
>
> >
> >> for both AArch64 and AArch32 mode. The vgic-sys-reg-v3.c handles AArch64
> >> mode and is compiled only for AArch64 mode.
> >>
> >> Updated arch/arm/include/uapi/asm/kvm.h with new definitions
> >> required to compile for AArch32.
> >>
> >> The version of VGIC v3 specification is define here
> >> Documentation/virtual/kvm/devices/arm-vgic-v3.txt
> >>
> >> Signed-off-by: Pavel Fedin <p.fedin@samsung.com>
> >> Signed-off-by: Vijaya Kumar K <Vijaya.Kumar@cavium.com>
> >> ---
> [...]
> >> +static bool access_gic_aprn(struct kvm_vcpu *vcpu, bool is_write, u8 apr,
> >> + u8 idx, unsigned long *reg)
> >> +{
> >> + struct vgic_cpu *vgic_v3_cpu = &vcpu->arch.vgic_cpu;
> >> +
> >> + /* num_pri_bits are initialized with HW supported values.
> >> + * We can rely safely on num_pri_bits even if VM has not
> >> + * restored ICC_CTLR_EL1 before restoring APnR registers.
> >> + */
> >
> > nit: commenting style
> ok
> >
> >> + switch (vgic_v3_cpu->num_pri_bits) {
> >> + case 7:
> >> + vgic_v3_access_apr_reg(vcpu, is_write, apr, idx, reg);
> >> + break;
> >> + case 6:
> >> + if (idx > 1)
> >> + goto err;
> >> + vgic_v3_access_apr_reg(vcpu, is_write, apr, idx, reg);
> >> + break;
> >> + default:
> >> + if (idx > 0)
> >> + goto err;
> >> + vgic_v3_access_apr_reg(vcpu, is_write, apr, idx, reg);
> >> + }
> >
> > It looks to me like userspace can then program active priorities with
> > higher numbers than what it will program num_pri_bits to later. Is that
> > not weird, or am I missing something?
>
> As long as it is within HW supported priorities it is safe.
I know that it is safe on the hardware, but it is weird to define a VM
with some max priority and still be able to set a higher active priority
is it not?
On the other hand, if we cannot enforce this at runtime, it may not
matter?
Hint: I'd like for you to actually think about these constraints and
make sure the sematics of the emulated VM environment remain intact
across migrations.
> >
> >> +
> >> + return true;
> >> +err:
> >> + if (!is_write)
> >> + *reg = 0;
> >> +
> >> + return false;
> >> +}
> >> +
> >> +bool access_gic_ap0r_reg(struct kvm_vcpu *vcpu, bool is_write, u8 idx,
> >> + unsigned long *reg)
> >> +{
> >> + return access_gic_aprn(vcpu, is_write, 0, idx, reg);
> >> +}
> >> +
> >> +bool access_gic_ap1r_reg(struct kvm_vcpu *vcpu, bool is_write, u8 idx,
> >> + unsigned long *reg)
> >> +{
> >> + return access_gic_aprn(vcpu, is_write, 1, idx, reg);
> >> +}
> >> +
> >> +bool access_gic_sre_reg(struct kvm_vcpu *vcpu, bool is_write,
> >> + unsigned long *reg)
> >> +{
> >> + struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3;
> >> +
> >> + /* Validate SRE bit */
> >> + if (is_write) {
> >> + if (!(*reg & ICC_SRE_EL1_SRE))
> >> + return false;
> >> + } else {
> >> + *reg = vgicv3->vgic_sre;
> >> + }
> >> +
> >> + return true;
> >> +}
> >> diff --git a/virt/kvm/arm/vgic/vgic-sys-reg-v3.c b/virt/kvm/arm/vgic/vgic-sys-reg-v3.c
> >> new file mode 100644
> >> index 0000000..82c2f02
> >> --- /dev/null
> >> +++ b/virt/kvm/arm/vgic/vgic-sys-reg-v3.c
> >> @@ -0,0 +1,142 @@
> >> +/*
> >> + * VGIC system registers handling functions
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License version 2 as
> >> + * published by the Free Software Foundation.
> >> + *
> >> + * This program is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> >> + * GNU General Public License for more details.
> >> + */
> >> +
> >> +#include <linux/kvm.h>
> >> +#include <linux/kvm_host.h>
> >> +#include <asm/kvm_emulate.h>
> >> +#include "vgic.h"
> >> +#include "sys_regs.h"
> >> +
> >> +#define ACCESS_SYS_REG(REG) \
> >> +static bool access_gic_##REG##_sys_reg(struct kvm_vcpu *vcpu, \
> >> + struct sys_reg_params *p, \
> >> + const struct sys_reg_desc *r) \
> >> +{ \
> >> + unsigned long tmp; \
> >> + bool ret; \
> >> + \
> >> + if (p->is_write) \
> >> + tmp = p->regval; \
> >> + ret = access_gic_##REG##_reg(vcpu, p->is_write, &tmp); \
> >> + if (!p->is_write) \
> >> + p->regval = tmp; \
> >> + \
> >> + return ret; \
> >> +}
> >> +
> >> +ACCESS_SYS_REG(ctlr)
> >> +ACCESS_SYS_REG(pmr)
> >> +ACCESS_SYS_REG(bpr0)
> >> +ACCESS_SYS_REG(bpr1)
> >> +ACCESS_SYS_REG(sre)
> >> +ACCESS_SYS_REG(grpen0)
> >> +ACCESS_SYS_REG(grpen1)
> >> +
> >> +#define ACCESS_APNR_SYS_REG(REG) \
> >> +static bool access_gic_##REG##_sys_reg(struct kvm_vcpu *vcpu, \
> >> + struct sys_reg_params *p, \
> >> + const struct sys_reg_desc *r) \
> >> +{ \
> >> + unsigned long tmp; \
> >> + u8 idx = p->Op2 & 3; \
> >> + bool ret; \
> >> + \
> >> + if (p->is_write) \
> >> + tmp = p->regval; \
> >> + ret = access_gic_##REG##_reg(vcpu, p->is_write, idx, &tmp); \
> >> + if (!p->is_write) \
> >> + p->regval = tmp; \
> >> + \
> >> + return ret; \
> >> +}
> >> +
> >> +ACCESS_APNR_SYS_REG(ap0r)
> >> +ACCESS_APNR_SYS_REG(ap1r)
> >
> > I don't get these indirections. Why can't you call the functions
> > directly?
>
> The code is same for accessing the registers hence added this indirection.
>
That's not answering my question.
What is the benefit of adding this indirection as opposed to having the
functions called directly?
To make my point clear: I hate this kind of preprocessor macro fun, and
I think it should only ever be used when there's a huge benefit in terms
of code reuse or simplicity of some sort. I don't see anything like
that in this case.
Thanks,
-Christoffer
^ permalink raw reply
* [PATCH v7 3/8] drm: sun8i: add HDMI video support to A83T and H3
From: Jean-Francois Moine @ 2016-11-29 8:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1480414715.git.moinejf@free.fr>
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
drivers/gpu/drm/sun8i/Kconfig | 7 +
drivers/gpu/drm/sun8i/Makefile | 2 +
drivers/gpu/drm/sun8i/de2_hdmi.c | 440 +++++++++++++++++++
drivers/gpu/drm/sun8i/de2_hdmi.h | 51 +++
drivers/gpu/drm/sun8i/de2_hdmi_io.c | 843 ++++++++++++++++++++++++++++++++++++
5 files changed, 1343 insertions(+)
create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.c
create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi.h
create mode 100644 drivers/gpu/drm/sun8i/de2_hdmi_io.c
diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig
index 6940895..5c4607b 100644
--- a/drivers/gpu/drm/sun8i/Kconfig
+++ b/drivers/gpu/drm/sun8i/Kconfig
@@ -17,3 +17,10 @@ config DRM_SUN8I_DE2
Choose this option if your Allwinner chipset has the DE2 interface
as the A64, A83T and H3. If M is selected the module will be called
sun8i-de2-drm.
+
+config DRM_SUN8I_DE2_HDMI
+ tristate "Support for DE2 HDMI"
+ depends on DRM_SUN8I_DE2
+ help
+ Choose this option if you use want HDMI on DE2.
+ If M is selected the module will be called sun8i-de2-hdmi.
diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile
index f107919..6ba97c2 100644
--- a/drivers/gpu/drm/sun8i/Makefile
+++ b/drivers/gpu/drm/sun8i/Makefile
@@ -3,5 +3,7 @@
#
sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
+sun8i-de2-hdmi-objs := de2_hdmi.o de2_hdmi_io.o
obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o
+obj-$(CONFIG_DRM_SUN8I_DE2_HDMI) += sun8i-de2-hdmi.o
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi.c b/drivers/gpu/drm/sun8i/de2_hdmi.c
new file mode 100644
index 0000000..9ff6132
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi.c
@@ -0,0 +1,440 @@
+/*
+ * Allwinner DRM driver - HDMI
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/hdmi.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include "de2_hdmi.h"
+
+static const struct of_device_id de2_hdmi_dt_ids[] = {
+ { .compatible = "allwinner,sun8i-a83t-hdmi",
+ .data = (void *) SOC_A83T },
+ { .compatible = "allwinner,sun8i-h3-hdmi",
+ .data = (void *) SOC_H3 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, de2_hdmi_dt_ids);
+
+#define conn_to_priv(x) \
+ container_of(x, struct de2_hdmi_priv, connector)
+
+#define enc_to_priv(x) \
+ container_of(x, struct de2_hdmi_priv, encoder)
+
+/* --- encoder functions --- */
+
+static int de2_hdmi_set_clock(struct de2_hdmi_priv *priv,
+ int rate)
+{
+ struct clk *parent_clk;
+ u32 parent_rate;
+ int ret;
+
+ /* determine and set the best rate for the parent clock (pll-video) */
+ if ((270000 * 2) % rate == 0)
+ parent_rate = 270000000;
+ else if (297000 % rate == 0)
+ parent_rate = 297000000;
+ else
+ return -EINVAL; /* unsupported clock */
+
+ parent_clk = clk_get_parent(priv->clk);
+
+ ret = clk_set_rate(parent_clk, parent_rate);
+ if (ret) {
+ dev_err(priv->dev, "set parent rate failed %d\n", ret);
+ return ret;
+ }
+ ret = clk_set_rate(priv->clk, rate * 1000);
+ if (ret)
+ dev_err(priv->dev, "set rate failed %d\n", ret);
+
+ return ret;
+}
+
+static void de2_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ priv->cea_mode = drm_match_cea_mode(mode);
+
+ DRM_DEBUG_DRIVER("cea_mode %d\n", priv->cea_mode);
+
+ if (de2_hdmi_set_clock(priv, mode->clock) < 0)
+ return;
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_mode_set(priv, mode);
+ mutex_unlock(&priv->mutex);
+}
+
+static void de2_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_on(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void de2_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_off(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static const struct drm_encoder_helper_funcs de2_hdmi_encoder_helper_funcs = {
+ .mode_set = de2_hdmi_encoder_mode_set,
+ .enable = de2_hdmi_encoder_enable,
+ .disable = de2_hdmi_encoder_disable,
+};
+
+static const struct drm_encoder_funcs de2_hdmi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+/* --- connector functions --- */
+
+static int de2_hdmi_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ int cea_mode = drm_match_cea_mode(mode);
+
+ return hdmi_io_mode_valid(cea_mode) < 0 ? MODE_NOMODE : MODE_OK;
+}
+
+static enum drm_connector_status de2_hdmi_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ struct de2_hdmi_priv *priv = conn_to_priv(connector);
+ int ret;
+
+ mutex_lock(&priv->mutex);
+ ret = hdmi_io_get_hpd(priv);
+ mutex_unlock(&priv->mutex);
+
+ return ret ? connector_status_connected :
+ connector_status_disconnected;
+}
+
+static int read_edid_block(void *data, u8 *buf,
+ unsigned int blk, size_t length)
+{
+ struct de2_hdmi_priv *priv = data;
+ int ret;
+
+ mutex_lock(&priv->mutex);
+ ret = hdmi_io_ddc_read(priv, blk / 2, (blk & 1) ? 128 : 0,
+ length, buf);
+ mutex_unlock(&priv->mutex);
+
+ return ret;
+}
+
+/* values duplicated from edid_cea_modes[] */
+static const struct drm_display_mode lmodes_tb[] = {
+ /* 2 - 720x480 at 60Hz */
+ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
+ 798, 858, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
+ /* 4 - 1280x720 at 60Hz */
+ { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+ /* 16 - 1920x1080 at 60Hz */
+ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+ .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+};
+
+static int de2_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+ struct de2_hdmi_priv *priv = conn_to_priv(connector);
+ struct drm_display_mode *mode;
+ const struct drm_display_mode *lmode;
+ struct edid *edid;
+ int n;
+
+ edid = drm_do_get_edid(connector, read_edid_block, priv);
+
+ if (!edid) {
+ dev_warn(priv->dev, "failed to read EDID\n");
+ if (!connector->cmdline_mode.specified)
+ return 0;
+
+ if (connector->cmdline_mode.xres == 1920 &&
+ connector->cmdline_mode.yres == 1080)
+ lmode = &lmodes_tb[2];
+ else if (connector->cmdline_mode.xres == 1280 &&
+ connector->cmdline_mode.yres == 720)
+ lmode = &lmodes_tb[1];
+ else
+ lmode = &lmodes_tb[0];
+
+ mode = drm_mode_duplicate(connector->dev, lmode);
+ if (!mode)
+ return 0;
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+ }
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ n = drm_add_edid_modes(connector, edid);
+
+ drm_edid_to_eld(connector, edid);
+
+ kfree(edid);
+
+ DRM_DEBUG_DRIVER("%s EDID ok %d modes\n",
+ connector->eld[0] ? "HDMI" : "DVI", n);
+
+ return n;
+}
+
+static const
+struct drm_connector_helper_funcs de2_hdmi_connector_helper_funcs = {
+ .get_modes = de2_hdmi_connector_get_modes,
+ .mode_valid = de2_hdmi_connector_mode_valid,
+};
+
+static const struct drm_connector_funcs de2_hdmi_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = de2_hdmi_connector_detect,
+ .destroy = drm_connector_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static void de2_hdmi_cleanup(struct de2_hdmi_priv *priv)
+{
+ clk_disable_unprepare(priv->clk_ddc);
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->gate);
+ reset_control_assert(priv->reset1);
+ reset_control_assert(priv->reset0);
+}
+
+static int de2_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm = data;
+ struct de2_hdmi_priv *priv = dev_get_drvdata(dev);
+ struct drm_encoder *encoder = &priv->encoder;
+ struct drm_connector *connector = &priv->connector;
+ int ret;
+
+ encoder->possible_crtcs =
+ drm_of_find_possible_crtcs(drm, dev->of_node);
+
+ /* if no CRTC, delay */
+ if (encoder->possible_crtcs == 0)
+ return -EPROBE_DEFER;
+
+ /* HDMI init */
+ ret = reset_control_deassert(priv->reset0);
+ if (ret)
+ goto err;
+ ret = reset_control_deassert(priv->reset1);
+ if (ret)
+ goto err;
+
+ ret = clk_prepare_enable(priv->clk_ddc);
+ if (ret)
+ goto err;
+
+ de2_hdmi_set_clock(priv, 147500); /* set a valid clock rate */
+ ret = clk_prepare_enable(priv->gate);
+ if (ret)
+ goto err;
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ goto err;
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_init(priv);
+ mutex_unlock(&priv->mutex);
+
+ /* encoder init */
+ ret = drm_encoder_init(drm, encoder, &de2_hdmi_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ goto err;
+
+ drm_encoder_helper_add(encoder, &de2_hdmi_encoder_helper_funcs);
+
+ /* connector init */
+ ret = drm_connector_init(drm, connector,
+ &de2_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret)
+ goto err_connector;
+
+ connector->interlace_allowed = 1;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
+ drm_connector_helper_add(connector,
+ &de2_hdmi_connector_helper_funcs);
+
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ return 0;
+
+err_connector:
+ drm_encoder_cleanup(encoder);
+err:
+ dev_err(dev, "err %d\n", ret);
+ return ret;
+}
+
+static void de2_hdmi_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct de2_hdmi_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->connector.dev)
+ drm_connector_cleanup(&priv->connector);
+ drm_encoder_cleanup(&priv->encoder);
+ de2_hdmi_cleanup(priv);
+}
+
+static const struct component_ops de2_hdmi_ops = {
+ .bind = de2_hdmi_bind,
+ .unbind = de2_hdmi_unbind,
+};
+
+static int de2_hdmi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct de2_hdmi_priv *priv;
+ struct resource *res;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->dev = dev;
+
+ mutex_init(&priv->mutex);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get memory resource\n");
+ return -ENXIO;
+ }
+ priv->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->mmio)) {
+ ret = PTR_ERR(priv->mmio);
+ dev_err(dev, "failed to map registers err %d\n", ret);
+ return ret;
+ }
+
+ priv->gate = devm_clk_get(dev, "bus");
+ if (IS_ERR(priv->gate)) {
+ ret = PTR_ERR(priv->gate);
+ dev_err(dev, "gate clock err %d\n", ret);
+ return ret;
+ }
+
+ priv->clk = devm_clk_get(dev, "clock");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_err(dev, "hdmi clock err %d\n", ret);
+ return ret;
+ }
+
+ priv->clk_ddc = devm_clk_get(dev, "ddc-clock");
+ if (IS_ERR(priv->clk_ddc)) {
+ ret = PTR_ERR(priv->clk_ddc);
+ dev_err(dev, "hdmi-ddc clock err %d\n", ret);
+ return ret;
+ }
+
+ priv->reset0 = devm_reset_control_get(dev, "hdmi0");
+ if (IS_ERR(priv->reset0)) {
+ ret = PTR_ERR(priv->reset0);
+ dev_err(dev, "reset controller err %d\n", ret);
+ return ret;
+ }
+
+ priv->reset1 = devm_reset_control_get(dev, "hdmi1");
+ if (IS_ERR(priv->reset1)) {
+ ret = PTR_ERR(priv->reset1);
+ dev_err(dev, "reset controller err %d\n", ret);
+ return ret;
+ }
+
+ priv->soc_type = (int) of_match_device(de2_hdmi_dt_ids,
+ &pdev->dev)->data;
+
+ de2_hdmi_audio_register(dev);
+
+ return component_add(dev, &de2_hdmi_ops);
+}
+
+static int de2_hdmi_remove(struct platform_device *pdev)
+{
+ de2_hdmi_audio_unregister(&pdev->dev);
+ component_del(&pdev->dev, &de2_hdmi_ops);
+
+ return 0;
+}
+
+static struct platform_driver de2_hdmi_driver = {
+ .probe = de2_hdmi_probe,
+ .remove = de2_hdmi_remove,
+ .driver = {
+ .name = "sun8i-de2-hdmi",
+ .of_match_table = of_match_ptr(de2_hdmi_dt_ids),
+ },
+};
+
+/* create the video HDMI driver and the sound card driver */
+static int __init de2_hdmi_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&de2_hdmi_driver);
+
+ return ret;
+}
+
+static void __exit de2_hdmi_fini(void)
+{
+ platform_driver_unregister(&de2_hdmi_driver);
+}
+
+module_init(de2_hdmi_init);
+module_exit(de2_hdmi_fini);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("Allwinner DE2 HDMI encoder/connector");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi.h b/drivers/gpu/drm/sun8i/de2_hdmi.h
new file mode 100644
index 0000000..6711a76
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi.h
@@ -0,0 +1,51 @@
+#ifndef __DE2_HDMI_H__
+#define __DE2_HDMI_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <drm/drmP.h>
+
+/* SoC types */
+#define SOC_A83T 0
+#define SOC_H3 1
+
+struct de2_hdmi_priv {
+ struct device *dev;
+ void __iomem *mmio;
+
+ struct drm_encoder encoder;
+ struct drm_connector connector;
+
+ struct clk *clk;
+ struct clk *clk_ddc;
+ struct clk *gate;
+ struct reset_control *reset0;
+ struct reset_control *reset1;
+
+ struct mutex mutex;
+ u8 soc_type;
+ u8 cea_mode;
+};
+
+/* in de2_hdmi_io.c */
+void hdmi_io_init(struct de2_hdmi_priv *priv);
+void hdmi_io_video_on(struct de2_hdmi_priv *priv);
+void hdmi_io_video_off(struct de2_hdmi_priv *priv);
+void hdmi_io_mode_set(struct de2_hdmi_priv *priv,
+ struct drm_display_mode *mode);
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+ char pointer, char offset,
+ int nbyte, char *pbuf);
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv);
+int hdmi_io_mode_valid(int cea_mode);
+
+#endif /* __DE2_HDMI_H__ */
diff --git a/drivers/gpu/drm/sun8i/de2_hdmi_io.c b/drivers/gpu/drm/sun8i/de2_hdmi_io.c
new file mode 100644
index 0000000..b746a52
--- /dev/null
+++ b/drivers/gpu/drm/sun8i/de2_hdmi_io.c
@@ -0,0 +1,843 @@
+/*
+ * Allwinner A83T and H3 HDMI lowlevel functions
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ * Adapted from the sun8iw6 and sun8iw7 disp2 drivers
+ * Copyright (c) 2016 Allwinnertech Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+/*
+ * The HDMI controller in the A83T and H3 seems to be a
+ * Synopsys DesignWare HDMI controller.
+ * The PHYs are unknown.
+ * Documentation:
+ * https://linux-sunxi.org/DWC_HDMI_Controller
+ * https://www.synopsys.com/dw/doc.php/ds/c/dwc_hdmi_tx_csds.pdf
+ */
+
+#include <linux/hdmi.h>
+
+#include "de2_hdmi.h"
+
+static int hdmi_mode = 1;
+MODULE_PARM_DESC(de2_hdmi_mode, "Force HDMI mode.\n"
+"When set, if the display device is connected by HDMI, switch to this mode.\n"
+"When unset, stay in DVI mode (useful when screen overscan).\n");
+module_param_named(de2_hdmi_mode, hdmi_mode, int, 0644);
+
+/* guessed PHY registers */
+#define HDMI_PHY_LOCK_READ_REG 0x10010
+#define HDMI_PHY_CTRL_REG 0x10020
+#define HDMI_PHY_24_REG 0x10024
+#define HDMI_PHY_28_REG 0x10028
+#define HDMI_PHY_PLL_REG 0x1002c
+#define HDMI_PHY_CLK_REG 0x10030
+#define HDMI_PHY_34_REG 0x10034
+#define HDMI_PHY_STATUS_REG 0x10038
+
+/* DW registers (obfuscated addresses) */
+
+/* Interrupt Registers */
+#define R_0100_HDMI_IH_FC_STAT0 0x0010
+#define R_0101_HDMI_IH_FC_STAT1 0x0011
+#define R_0102_HDMI_IH_FC_STAT2 0x8010
+#define R_0103_HDMI_IH_AS_STAT0 0x8011
+#define R_0104_HDMI_IH_PHY_STAT0 0x0012
+#define R_0105_HDMI_IH_I2CM_STAT0 0x0013
+#define R_0106_HDMI_IH_CEC_STAT0 0x8012
+#define R_0107_HDMI_IH_VP_STAT0 0x8013
+#define R_0108_HDMI_IH_I2CMPHY_STAT0 0x4010
+#define R_01ff_HDMI_IH_MUTE 0xf01f
+
+/* Video Sample Registers */
+#define R_0200_HDMI_TX_INVID0 0x0800
+#define R_0201_HDMI_TX_INSTUFFING 0x0801
+#define R_0202_HDMI_TX_GYDATA0 0x8800
+#define R_0203_HDMI_TX_GYDATA1 0x8801
+#define R_0204_HDMI_TX_RCRDATA0 0x0802
+#define R_0205_HDMI_TX_RCRDATA1 0x0803
+#define R_0206_HDMI_TX_BCBDATA0 0x8802
+#define R_0207_HDMI_TX_BCBDATA1 0x8803
+
+/* Video Packetizer Registers */
+#define R_0801_HDMI_VP_PR_CD 0x0401
+#define R_0802_HDMI_VP_STUFF 0x8400
+#define R_0803_HDMI_VP_REMAP 0x8401
+#define R_0804_HDMI_VP_CONF 0x0402
+#define R_0807_HDMI_VP_MASK 0x8403
+
+/* Frame Composer Registers */
+#define R_1000_HDMI_FC_INVIDCONF 0x0040
+#define HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH 0x10
+#define HDMI_FC_INVIDCONF_HDMI_MODE 0x08
+#define R_1001_HDMI_FC_INHACTV0 0x0041
+#define R_1002_HDMI_FC_INHACTV1 0x8040
+#define R_1003_HDMI_FC_INHBLANK0 0x8041
+#define R_1004_HDMI_FC_INHBLANK1 0x0042
+#define R_1005_HDMI_FC_INVACTV0 0x0043
+#define R_1006_HDMI_FC_INVACTV1 0x8042
+#define R_1007_HDMI_FC_INVBLANK 0x8043
+#define R_1008_HDMI_FC_HSYNCINDELAY0 0x4040
+#define R_1009_HDMI_FC_HSYNCINDELAY1 0x4041
+#define R_100a_HDMI_FC_HSYNCINWIDTH0 0xc040
+#define R_100b_HDMI_FC_HSYNCINWIDTH1 0xc041
+#define R_100c_HDMI_FC_VSYNCINDELAY 0x4042
+#define R_100d_HDMI_FC_VSYNCINWIDTH 0x4043
+#define R_1011_HDMI_FC_CTRLDUR 0x0045
+#define R_1012_HDMI_FC_EXCTRLDUR 0x8044
+#define R_1013_HDMI_FC_EXCTRLSPAC 0x8045
+#define R_1014_HDMI_FC_CH0PREAM 0x0046
+#define R_1015_HDMI_FC_CH1PREAM 0x0047
+#define R_1016_HDMI_FC_CH2PREAM 0x8046
+#define R_1018_HDMI_FC_GCP 0x4044
+#define R_1019_HDMI_FC_AVICONF0 0x4045
+#define HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN 0x20
+#define R_101a_HDMI_FC_AVICONF1 0xc044
+#define R_101b_HDMI_FC_AVICONF2 0xc045
+#define R_101c_HDMI_FC_AVIVID 0x4046
+#define R_1025_HDMI_FC_AUDICONF0 0x2043
+#define R_1026_HDMI_FC_AUDICONF1 0xa042
+#define R_1027_HDMI_FC_AUDICONF2 0xa043
+#define R_1028_HDMI_FC_AUDICONF3 0x6040
+#define R_1029_HDMI_FC_VSDIEEEID0 0x6041
+#define R_1030_HDMI_FC_VSDIEEEID1 0x2044
+#define R_1031_HDMI_FC_VSDIEEEID2 0x2045
+#define R_1032_HDMI_FC_VSDPAYLOAD0 0xa044
+#define R_1033_HDMI_FC_VSDPAYLOAD1 0xa045
+#define R_1034_HDMI_FC_VSDPAYLOAD2 0x2046
+#define R_1063_HDMI_FC_AUDSCONF 0xa049
+#define R_1065_HDMI_FC_AUDSV 0x204b
+#define R_1066_HDMI_FC_AUDSU 0xa04a
+#define R_1067_HDMI_FC_AUDSCHNLS0 0xa04b
+#define HDMI_FC_AUDSCHNLS0_CGMSA 0x30
+#define R_1068_HDMI_FC_AUDSCHNLS1 0x6048
+#define R_1069_HDMI_FC_AUDSCHNLS2 0x6049
+#define R_106a_HDMI_FC_AUDSCHNLS3 0xe048
+#define HDMI_FC_AUDSCHNLS3_OIEC_CH0(v) (v)
+#define HDMI_FC_AUDSCHNLS3_OIEC_CH1(v) (v << 4)
+#define R_106b_HDMI_FC_AUDSCHNLS4 0xe049
+#define HDMI_FC_AUDSCHNLS4_OIEC_CH2(v) (v)
+#define HDMI_FC_AUDSCHNLS4_OIEC_CH3(v) (v << 4)
+#define R_106c_HDMI_FC_AUDSCHNLS5 0x604a
+#define HDMI_FC_AUDSCHNLS5_OIEC_CH0(v) (v)
+#define HDMI_FC_AUDSCHNLS5_OIEC_CH1(v) (v << 4)
+#define R_106d_HDMI_FC_AUDSCHNLS6 0x604b
+#define HDMI_FC_AUDSCHNLS6_OIEC_CH2(v) (v)
+#define HDMI_FC_AUDSCHNLS6_OIEC_CH3(v) (v << 4)
+#define R_106e_HDMI_FC_AUDSCHNLS7 0xe04a
+#define R_106f_HDMI_FC_AUDSCHNLS8 0xe04b
+#define HDMI_FC_AUDSCHNLS8_WORDLENGTH(v) (v)
+#define R_10b3_HDMI_FC_DATAUTO0 0xb045
+#define R_10b4_HDMI_FC_DATAUTO1 0x3046
+#define R_10b5_HDMI_FC_DATAUTO2 0x3047
+#define R_10d2_HDMI_FC_MASK0 0x904c
+#define R_10d6_HDMI_FC_MASK1 0x904e
+#define R_10da_HDMI_FC_MASK2 0xd04c
+#define R_10e0_HDMI_FC_PRCONF 0x3048
+#define R_1103_HDMI_FC_GMD_CONF 0x8051
+#define R_1104_HDMI_FC_GMD_HB 0x0052
+#define R_1200_HDMI_FC_DBGFORCE 0x0840
+#define HDMI_FC_DBGFORCE_FORCEAUDIO BIT(4)
+#define HDMI_FC_DBGFORCE_FORCEVIDEO BIT(0)
+#define R_1219_HDMI_FC_DBGTMDS0 0x4845
+
+/* HDMI Source PHY Registers */
+#define R_3000_HDMI_PHY_CONF0 0x0240
+#define HDMI_PHY_CONF0_PDZ BIT(7)
+#define HDMI_PHY_CONF0_ENTMDS BIT(6)
+#define HDMI_PHY_CONF0_SPARECTRL BIT(5)
+#define HDMI_PHY_CONF0_GEN2_PDDQ BIT(4)
+#define HDMI_PHY_CONF0_GEN2_TXPWRON BIT(3)
+#define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE BIT(2)
+#define HDMI_PHY_CONF0_SELDATAENPOL BIT(1)
+#define HDMI_PHY_CONF0_SELDIPIF BIT(0)
+#define R_3001_HDMI_PHY_TST0 0x0241
+#define HDMI_PHY_TST0_TSTCLR BIT(5)
+#define R_3005_HDMI_PHY_INT0 0x0243
+#define R_3006_HDMI_PHY_MASK0 0x8242
+
+/* HDMI Master PHY Registers */
+#define R_3020_HDMI_PHY_I2CM_SLAVE_ADDR 0x2240
+#define HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 0x69
+#define R_3021_HDMI_PHY_I2CM_ADDRESS_ADDR 0x2241
+#define R_3022_HDMI_PHY_I2CM_DATAO_1_ADDR 0xa240
+#define R_3023_HDMI_PHY_I2CM_DATAO_0_ADDR 0xa241
+#define R_3026_HDMI_PHY_I2CM_OPERATION_ADDR 0xa242
+#define HDMI_PHY_I2CM_OPERATION_ADDR_WRITE 0x10
+#define R_3027_HDMI_PHY_I2CM_INT_ADDR 0xa243
+#define R_3028_HDMI_PHY_I2CM_CTLINT_ADDR 0x6240
+
+/* Audio Sampler Registers */
+#define R_3100_HDMI_AUD_CONF0 0x0250
+#define HDMI_AUD_CONF0_SW_RESET 0x80
+#define HDMI_AUD_CONF0_I2S_ALL_ENABLE 0x2f
+#define R_3101_HDMI_AUD_CONF1 0x0251
+#define R_3102_HDMI_AUD_INT 0x8250
+#define R_3103_HDMI_AUD_CONF2 0x8251
+#define R_3200_HDMI_AUD_N1 0x0a40
+#define R_3201_HDMI_AUD_N2 0x0a41
+#define R_3202_HDMI_AUD_N3 0x8a40
+#define R_3205_HDMI_AUD_CTS3 0x0a43
+#define R_3206_HDMI_AUD_INPUTCLKFS 0x8a42
+#define HDMI_AUD_INPUTCLKFS_64FS 0x04
+#define R_3302_HDMI_AUD_SPDIFINT 0x8a50
+
+/* Generic Parallel Audio Interface Registers */
+#define R_3506_HDMI_GP_POL 0x8272
+
+/* Main Controller Registers */
+#define R_4001_HDMI_MC_CLKDIS 0x0081
+#define HDMI_MC_CLKDIS_HDCPCLK_DISABLE BIT(6)
+#define HDMI_MC_CLKDIS_AUDCLK_DISABLE BIT(3)
+#define HDMI_MC_CLKDIS_TMDSCLK_DISABLE BIT(1)
+#define R_4002_HDMI_MC_SWRSTZ 0x8080
+#define R_4004_HDMI_MC_FLOWCTRL 0x0082
+#define R_4005_HDMI_MC_PHYRSTZ 0x0083
+#define HDMI_MC_PHYRSTZ_DEASSERT BIT(0)
+
+/* HDCP Encryption Engine Registers */
+#define R_5000_HDMI_A_HDCPCFG0 0x00c0
+#define R_5001_HDMI_A_HDCPCFG1 0x00c1
+#define HDMI_A_HDCPCFG1_PH2UPSHFTENC BIT(2)
+#define HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE BIT(1)
+#define HDMI_A_HDCPCFG1_SWRESET BIT(0)
+#define R_5008_HDMI_A_APIINTMSK 0x40c0
+#define R_5009_HDMI_A_VIDPOLCFG 0x40c1
+#define HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH BIT(4)
+
+/* CEC Engine Registers */
+#define R_7d02_HDMI_CEC_MASK 0x86f0
+
+/* I2C Master Registers (E-DDC) */
+#define R_7e00_HDMI_I2CM_SLAVE 0x0ee0
+#define R_7e01_HDMI_I2CM_ADDRESS 0x0ee1
+#define R_7e03_HDMI_I2CM_DATAI 0x8ee1
+#define R_7e04_HDMI_I2CM_OPERATION 0x0ee2
+#define HDMI_I2CM_OPERATION_DDC_READ 0x02
+#define R_7e05_HDMI_I2CM_INT 0x0ee3
+#define R_7e06_HDMI_I2CM_CTLINT 0x8ee2
+#define R_7e07_HDMI_I2CM_DIV 0x8ee3
+#define R_7e08_HDMI_I2CM_SEGADDR 0x4ee0
+#define R_7e09_HDMI_I2CM_SOFTRSTZ 0x4ee1
+#define R_7e0a_HDMI_I2CM_SEGPTR 0xcee0
+#define R_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x4ee2
+#define R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0xcee2
+
+#define VIC_720x480_60 2
+#define VIC_1280x720_60 4
+#define VIC_1920x1080i_60 5
+#define VIC_720x480i_60 6
+#define VIC_1920x1080_60 16
+#define VIC_720x576_50 17
+#define VIC_1280x720_50 19
+#define VIC_1920x1080i_50 20
+#define VIC_720x576i_50 21
+#define VIC_1920x1080_50 31
+#define VIC_1920x1080_24 32
+#define VIC_1920x1080_25 33
+#define VIC_1920x1080_30 34
+
+static inline u8 hdmi_readb(struct de2_hdmi_priv *priv, u32 addr)
+{
+ return readb_relaxed(priv->mmio + addr);
+}
+
+static inline u32 hdmi_readl(struct de2_hdmi_priv *priv, u32 addr)
+{
+ return readl_relaxed(priv->mmio + addr);
+}
+
+static inline void hdmi_writeb(struct de2_hdmi_priv *priv, u32 addr, u8 data)
+{
+ writeb_relaxed(data, priv->mmio + addr);
+}
+
+static inline void hdmi_writel(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+ writel_relaxed(data, priv->mmio + addr);
+}
+
+static inline void hdmi_orb(struct de2_hdmi_priv *priv, u32 addr, u8 data)
+{
+ writeb_relaxed(readb_relaxed(priv->mmio + addr) | data,
+ priv->mmio + addr);
+}
+
+static inline void hdmi_orl(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+ writel_relaxed(readl_relaxed(priv->mmio + addr) | data,
+ priv->mmio + addr);
+}
+
+static inline void hdmi_andl(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+ writel_relaxed(readl_relaxed(priv->mmio + addr) & data,
+ priv->mmio + addr);
+}
+
+/* read on/off functions */
+static inline void hdmi_read_on(struct de2_hdmi_priv *priv)
+{
+ hdmi_writel(priv, HDMI_PHY_LOCK_READ_REG, 0x54524545);
+}
+static inline void hdmi_read_off(struct de2_hdmi_priv *priv)
+{
+ hdmi_writel(priv, HDMI_PHY_LOCK_READ_REG, 0x57415452);
+}
+
+static void hdmi_inner_init(struct de2_hdmi_priv *priv)
+{
+ u8 clkdis = priv->soc_type == SOC_H3 ?
+ ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE : 0xff;
+
+ hdmi_read_on(priv);
+
+ /* software reset */
+ hdmi_writeb(priv, R_4002_HDMI_MC_SWRSTZ, 0x00);
+ udelay(2);
+
+ /* mask all interrupts */
+ hdmi_writeb(priv, R_01ff_HDMI_IH_MUTE, 0x00);
+ hdmi_writeb(priv, R_0807_HDMI_VP_MASK, 0xff);
+ hdmi_writeb(priv, R_10d2_HDMI_FC_MASK0, 0xff);
+ hdmi_writeb(priv, R_10d6_HDMI_FC_MASK1, 0xff);
+ hdmi_writeb(priv, R_10da_HDMI_FC_MASK2, 0xff);
+ hdmi_writeb(priv, R_3102_HDMI_AUD_INT, 0xff);
+ hdmi_writeb(priv, R_3302_HDMI_AUD_SPDIFINT, 0xff);
+ hdmi_writeb(priv, R_3506_HDMI_GP_POL, 0xff);
+ hdmi_writeb(priv, R_5008_HDMI_A_APIINTMSK, 0xff);
+ hdmi_writeb(priv, R_7d02_HDMI_CEC_MASK, 0xff);
+ hdmi_writeb(priv, R_7e05_HDMI_I2CM_INT, 0xff);
+ hdmi_writeb(priv, R_7e06_HDMI_I2CM_CTLINT, 0xff);
+
+ hdmi_writeb(priv, R_1063_HDMI_FC_AUDSCONF, 0xf0);
+ hdmi_writeb(priv, R_10b3_HDMI_FC_DATAUTO0, 0x1e);
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1, 0x00);
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE |
+ HDMI_A_HDCPCFG1_SWRESET);
+ hdmi_writeb(priv, R_5000_HDMI_A_HDCPCFG0, 0x00);
+ hdmi_writeb(priv, R_5009_HDMI_A_VIDPOLCFG,
+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, clkdis);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, clkdis);
+ hdmi_writeb(priv, R_0100_HDMI_IH_FC_STAT0, 0xff);
+ hdmi_writeb(priv, R_0101_HDMI_IH_FC_STAT1, 0xff);
+ hdmi_writeb(priv, R_0102_HDMI_IH_FC_STAT2, 0xff);
+ hdmi_writeb(priv, R_0103_HDMI_IH_AS_STAT0, 0xff);
+ hdmi_writeb(priv, R_0105_HDMI_IH_I2CM_STAT0, 0xff);
+ hdmi_writeb(priv, R_0106_HDMI_IH_CEC_STAT0, 0xff);
+ hdmi_writeb(priv, R_0107_HDMI_IH_VP_STAT0, 0xff);
+}
+
+static void hdmi_phy_init_a83t(struct de2_hdmi_priv *priv)
+{
+ hdmi_inner_init(priv);
+
+ hdmi_writeb(priv, 0x10000, 0x01);
+ hdmi_writeb(priv, 0x10001, 0x00);
+ hdmi_writeb(priv, 0x10002, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+ hdmi_writeb(priv, 0x10003, 0x00);
+ hdmi_writeb(priv, 0x10007, 0xa0);
+ hdmi_writeb(priv, R_4005_HDMI_MC_PHYRSTZ,
+ HDMI_MC_PHYRSTZ_DEASSERT);
+ udelay(1);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3006_HDMI_PHY_MASK0, 0xf0);
+ hdmi_writeb(priv, R_3027_HDMI_PHY_I2CM_INT_ADDR, 0xff);
+ hdmi_writeb(priv, R_3028_HDMI_PHY_I2CM_CTLINT_ADDR, 0xff);
+ hdmi_writeb(priv, R_0104_HDMI_IH_PHY_STAT0, 0xff);
+ hdmi_writeb(priv, R_0108_HDMI_IH_I2CMPHY_STAT0, 0xff);
+ hdmi_writeb(priv, R_4005_HDMI_MC_PHYRSTZ, 0x00);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3001_HDMI_PHY_TST0, HDMI_PHY_TST0_TSTCLR);
+ hdmi_writeb(priv, R_3020_HDMI_PHY_I2CM_SLAVE_ADDR,
+ HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+ hdmi_writeb(priv, R_3001_HDMI_PHY_TST0, 0x00);
+}
+
+static void hdmi_phy_init_h3(struct de2_hdmi_priv *priv)
+{
+ int to_cnt;
+ u32 tmp;
+
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 1 << 0);
+ udelay(5);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 16);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 1);
+ udelay(10);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 2);
+ udelay(5);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 3);
+ usleep_range(40, 50);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 19);
+ usleep_range(100, 120);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 18);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 7 << 4);
+
+ to_cnt = 10;
+ while (1) {
+ if (hdmi_readl(priv, HDMI_PHY_STATUS_REG) & 0x80)
+ break;
+ usleep_range(200, 250);
+ if (--to_cnt == 0) {
+ dev_err(priv->dev, "hdmi phy init timeout\n");
+ break;
+ }
+ }
+
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 0xf << 8);
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 1 << 7);
+
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084343);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(100);
+ tmp = hdmi_readl(priv, HDMI_PHY_STATUS_REG);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, (tmp >> 11) & 0x3f);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ff0f7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x80639000);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+
+ hdmi_inner_init(priv);
+}
+
+static int get_divider(int rate)
+{
+ if (rate <= 27000)
+ return 11;
+ if (rate <= 74250)
+ return 4;
+ if (rate <= 148500)
+ return 2;
+ return 1;
+}
+
+static void hdmi_i2cm_write(struct de2_hdmi_priv *priv,
+ int addr, u8 valh, u8 vall)
+{
+ hdmi_writeb(priv, R_3021_HDMI_PHY_I2CM_ADDRESS_ADDR, addr);
+ hdmi_writeb(priv, R_3022_HDMI_PHY_I2CM_DATAO_1_ADDR, valh);
+ hdmi_writeb(priv, R_3023_HDMI_PHY_I2CM_DATAO_0_ADDR, vall);
+ hdmi_writeb(priv, R_3026_HDMI_PHY_I2CM_OPERATION_ADDR,
+ HDMI_PHY_I2CM_OPERATION_ADDR_WRITE);
+ usleep_range(2000, 2500);
+}
+
+static void hdmi_phy_set_a83t(struct de2_hdmi_priv *priv,
+ struct drm_display_mode *mode)
+{
+ switch (get_divider(mode->clock)) {
+ case 1:
+ hdmi_i2cm_write(priv, 0x06, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x0f);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x02);
+ hdmi_i2cm_write(priv, 0x0e, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x2b);
+ break;
+ case 2: /* 1080P @ 60 & 50 */
+ hdmi_i2cm_write(priv, 0x06, 0x04, 0xa0);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x0a);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x02);
+ hdmi_i2cm_write(priv, 0x0e, 0x00, 0x21);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x29);
+ break;
+ case 4: /* 720P @ 50 & 60, 1080I, 1080 */
+ hdmi_i2cm_write(priv, 0x06, 0x05, 0x40);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x05);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x07);
+ hdmi_i2cm_write(priv, 0x0e, 0x02, 0xb5);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x09);
+ break;
+/* case 11: * 480P/576P */
+ default:
+ hdmi_i2cm_write(priv, 0x06, 0x01,
+ mode->flags & DRM_MODE_FLAG_DBLCLK ? 0xe3 : 0xe0);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x10, 0x08, 0xda);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x07);
+ hdmi_i2cm_write(priv, 0x0e, 0x03, 0x18);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x09);
+ break;
+ }
+ hdmi_i2cm_write(priv, 0x1e, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x13, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x17, 0x00, 0x00);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_TXPWRON |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+}
+
+static void hdmi_phy_set_h3(struct de2_hdmi_priv *priv,
+ struct drm_display_mode *mode)
+{
+ u32 tmp;
+
+ hdmi_andl(priv, HDMI_PHY_CTRL_REG, ~0xf000);
+
+ switch (get_divider(mode->clock)) {
+ case 1:
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x31dc5fc0);
+ /* or 0x30dc5fc0 ? */
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x800863c0);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(200);
+ tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ if (tmp < 0x3d)
+ tmp += 2;
+ else
+ tmp = 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+ msleep(100);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f8246b5);
+ break;
+ case 2: /* 1080P @ 60 & 50 */
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084381);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063a800);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c485);
+ break;
+ case 4: /* 720P @ 50 & 60, 1080I, 1080 */
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x80084343);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+ break;
+ default:
+/* case 11: * 480P/576P */
+ hdmi_writel(priv, HDMI_PHY_PLL_REG, 0x39dc5040);
+ hdmi_writel(priv, HDMI_PHY_CLK_REG, 0x8008430a);
+ msleep(20);
+ hdmi_writel(priv, HDMI_PHY_34_REG, 0x00000001);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, HDMI_PHY_STATUS_REG) >> 11) & 0x3f;
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, 0xc0000000);
+ hdmi_orl(priv, HDMI_PHY_PLL_REG, tmp);
+ hdmi_writel(priv, HDMI_PHY_CTRL_REG, 0x01ffff7f);
+ hdmi_writel(priv, HDMI_PHY_24_REG, 0x8063b000);
+ hdmi_writel(priv, HDMI_PHY_28_REG, 0x0f81c405);
+ break;
+ }
+}
+
+/* HDMI functions */
+
+/* hardware init */
+void hdmi_io_init(struct de2_hdmi_priv *priv)
+{
+ if (priv->soc_type == SOC_H3)
+ hdmi_phy_init_h3(priv);
+ else
+ hdmi_phy_init_a83t(priv);
+
+ /* disable hdcp */
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1,
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS,
+ HDMI_MC_CLKDIS_HDCPCLK_DISABLE);
+}
+
+/* check if the resolution is supported */
+int hdmi_io_mode_valid(int cea_mode)
+{
+ switch (cea_mode) {
+ case VIC_720x480_60:
+ case VIC_1280x720_60:
+ case VIC_1920x1080i_60:
+ case VIC_720x480i_60:
+ case VIC_1920x1080_60:
+ case VIC_720x576_50:
+ case VIC_1280x720_50:
+ case VIC_1920x1080i_50:
+ case VIC_720x576i_50:
+ case VIC_1920x1080_50:
+ case VIC_1920x1080_24:
+ case VIC_1920x1080_25:
+ case VIC_1920x1080_30:
+ return 1;
+ }
+ return -1;
+}
+
+/* output init */
+void hdmi_io_mode_set(struct de2_hdmi_priv *priv,
+ struct drm_display_mode *mode)
+{
+ int avi_d2; /* AVI InfoFrame Data Byte 2 */
+ int h_blank, h_sync_w, h_front_p;
+ int invidconf;
+
+ /* colorimetry and aspect ratio */
+ switch (priv->cea_mode) {
+ case 0:
+ return; /* bad mode */
+ case VIC_720x480_60:
+ case VIC_720x480i_60:
+ case VIC_720x576_50:
+ case VIC_720x576i_50:
+ avi_d2 = (HDMI_COLORIMETRY_ITU_601 << 6) |
+ (HDMI_PICTURE_ASPECT_4_3 << 4) | 0x08;
+ break;
+ default:
+ avi_d2 = (HDMI_COLORIMETRY_ITU_709 << 6) |
+ (HDMI_PICTURE_ASPECT_16_9 << 4) | 0x08;
+ break;
+ }
+
+ h_blank = mode->htotal - mode->hdisplay;
+ h_sync_w = mode->hsync_end - mode->hsync_start;
+ h_front_p = mode->hsync_start - mode->hdisplay;
+
+ invidconf = 0;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ invidconf |= 0x01;
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ invidconf |= 0x20;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ invidconf |= 0x40;
+
+ if (priv->soc_type == SOC_H3) {
+ hdmi_phy_set_h3(priv, mode);
+ hdmi_inner_init(priv);
+ } else {
+ hdmi_phy_init_a83t(priv);
+ }
+
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE,
+ HDMI_FC_DBGFORCE_FORCEVIDEO);
+ hdmi_writeb(priv, R_1219_HDMI_FC_DBGTMDS0, 0x00);
+ hdmi_writeb(priv, R_1000_HDMI_FC_INVIDCONF,
+ invidconf |
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH);
+ hdmi_writeb(priv, 0x10001, invidconf < 0x60 ? 0x03 : 0x00);
+ hdmi_writeb(priv, R_1002_HDMI_FC_INHACTV1, mode->hdisplay >> 8);
+ hdmi_writeb(priv, R_100d_HDMI_FC_VSYNCINWIDTH,
+ mode->vsync_end - mode->vsync_start);
+ hdmi_writeb(priv, R_1006_HDMI_FC_INVACTV1, mode->vdisplay >> 8);
+ hdmi_writeb(priv, R_1004_HDMI_FC_INHBLANK1, h_blank >> 8);
+ hdmi_writeb(priv, R_100c_HDMI_FC_VSYNCINDELAY,
+ mode->vsync_start - mode->vdisplay);
+ hdmi_writeb(priv, R_1009_HDMI_FC_HSYNCINDELAY1, h_front_p >> 8);
+ hdmi_writeb(priv, R_100b_HDMI_FC_HSYNCINWIDTH1, h_sync_w >> 8);
+ hdmi_writeb(priv, R_1001_HDMI_FC_INHACTV0, mode->hdisplay);
+ hdmi_writeb(priv, R_1003_HDMI_FC_INHBLANK0, h_blank);
+ hdmi_writeb(priv, R_1008_HDMI_FC_HSYNCINDELAY0, h_front_p);
+ hdmi_writeb(priv, R_100a_HDMI_FC_HSYNCINWIDTH0, h_sync_w);
+ hdmi_writeb(priv, R_1005_HDMI_FC_INVACTV0, mode->vdisplay);
+ hdmi_writeb(priv, R_1007_HDMI_FC_INVBLANK,
+ mode->vtotal - mode->vdisplay);
+ hdmi_writeb(priv, R_1011_HDMI_FC_CTRLDUR, 12);
+ hdmi_writeb(priv, R_1012_HDMI_FC_EXCTRLDUR, 32);
+ hdmi_writeb(priv, R_1013_HDMI_FC_EXCTRLSPAC, 1);
+ hdmi_writeb(priv, R_1014_HDMI_FC_CH0PREAM, 0x0b);
+ hdmi_writeb(priv, R_1015_HDMI_FC_CH1PREAM, 0x16);
+ hdmi_writeb(priv, R_1016_HDMI_FC_CH2PREAM, 0x21);
+ hdmi_writeb(priv, R_10e0_HDMI_FC_PRCONF,
+ mode->flags & DRM_MODE_FLAG_DBLCLK ? 0x21 : 0x10);
+ hdmi_writeb(priv, R_0801_HDMI_VP_PR_CD,
+ mode->flags & DRM_MODE_FLAG_DBLCLK ? 0x41 : 0x40);
+ hdmi_writeb(priv, R_0802_HDMI_VP_STUFF, 0x07);
+ hdmi_writeb(priv, R_0803_HDMI_VP_REMAP, 0x00);
+ hdmi_writeb(priv, R_0804_HDMI_VP_CONF, 0x47);
+ hdmi_writeb(priv, R_0200_HDMI_TX_INVID0, 0x01);
+ hdmi_writeb(priv, R_0201_HDMI_TX_INSTUFFING, 0x07);
+ hdmi_writeb(priv, R_0202_HDMI_TX_GYDATA0, 0x00);
+ hdmi_writeb(priv, R_0203_HDMI_TX_GYDATA1, 0x00);
+ hdmi_writeb(priv, R_0204_HDMI_TX_RCRDATA0, 0x00);
+ hdmi_writeb(priv, R_0205_HDMI_TX_RCRDATA1, 0x00);
+ hdmi_writeb(priv, R_0206_HDMI_TX_BCBDATA0, 0x00);
+ hdmi_writeb(priv, R_0207_HDMI_TX_BCBDATA1, 0x00);
+
+ if (priv->connector.eld[0]) { /* if audio/HDMI */
+ hdmi_writeb(priv, R_10b3_HDMI_FC_DATAUTO0, 0x08);
+ hdmi_writeb(priv, R_1031_HDMI_FC_VSDIEEEID2, 0x00);
+ hdmi_writeb(priv, R_1030_HDMI_FC_VSDIEEEID1,
+ HDMI_IEEE_OUI >> 8);
+ hdmi_writeb(priv, R_1029_HDMI_FC_VSDIEEEID0,
+ HDMI_IEEE_OUI & 0xff);
+ hdmi_writeb(priv, R_1032_HDMI_FC_VSDPAYLOAD0, 0x00);
+ hdmi_writeb(priv, R_1033_HDMI_FC_VSDPAYLOAD1, 0x00);
+ hdmi_writeb(priv, R_1034_HDMI_FC_VSDPAYLOAD2, 0x00);
+ hdmi_writeb(priv, R_10b4_HDMI_FC_DATAUTO1, 0x01);
+ hdmi_writeb(priv, R_10b5_HDMI_FC_DATAUTO2, 0x11);
+ hdmi_writeb(priv, R_1018_HDMI_FC_GCP, 0x00);
+ hdmi_writeb(priv, R_1104_HDMI_FC_GMD_HB, 0x00);
+ hdmi_writeb(priv, R_1103_HDMI_FC_GMD_CONF, 0x11);
+
+ /* switch to HDMI mode */
+ if (hdmi_mode) {
+ hdmi_read_on(priv);
+ hdmi_orb(priv, R_1000_HDMI_FC_INVIDCONF,
+ HDMI_FC_INVIDCONF_HDMI_MODE);
+ hdmi_read_off(priv);
+ }
+
+ /* AVI */
+ hdmi_writeb(priv, R_1019_HDMI_FC_AVICONF0,
+ HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN);
+ hdmi_writeb(priv, R_101a_HDMI_FC_AVICONF1, avi_d2);
+ hdmi_writeb(priv, R_101b_HDMI_FC_AVICONF2, 0x08);
+ hdmi_writeb(priv, R_101c_HDMI_FC_AVIVID, priv->cea_mode);
+ }
+
+ hdmi_writeb(priv, R_4004_HDMI_MC_FLOWCTRL, 0x00);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00); /* enable all clocks */
+
+ if (priv->soc_type != SOC_H3)
+ hdmi_phy_set_a83t(priv, mode);
+
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE, 0x00);
+
+}
+
+void hdmi_io_video_on(struct de2_hdmi_priv *priv)
+{
+ if (!priv->cea_mode)
+ return;
+pr_info("*jfm* hdmi video on\n");
+ if (priv->soc_type == SOC_H3)
+ hdmi_orl(priv, HDMI_PHY_CTRL_REG, 0x0f << 12);
+}
+
+void hdmi_io_video_off(struct de2_hdmi_priv *priv)
+{
+ if (!priv->cea_mode)
+ return;
+pr_info("*jfm* hdmi video off\n");
+ if (priv->soc_type == SOC_H3)
+ hdmi_andl(priv, HDMI_PHY_CTRL_REG, ~(0x0f << 12));
+}
+
+/* get a block of EDID */
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+ char pointer, char off,
+ int nbyte, char *pbuf)
+{
+ unsigned int to_cnt;
+ u8 reg;
+ int ret = 0;
+
+ hdmi_read_on(priv);
+ hdmi_writeb(priv, R_7e09_HDMI_I2CM_SOFTRSTZ, 0x00);
+ to_cnt = 50;
+ while (!(hdmi_readb(priv, R_7e09_HDMI_I2CM_SOFTRSTZ) & 0x01)) {
+ udelay(10);
+ if (--to_cnt == 0) { /* wait for 500us for timeout */
+ dev_err(priv->dev, "hdmi ddc reset timeout\n");
+ break;
+ }
+ }
+
+ hdmi_writeb(priv, R_7e07_HDMI_I2CM_DIV, 0x05);
+ hdmi_writeb(priv, R_7e05_HDMI_I2CM_INT, 0x08);
+ hdmi_writeb(priv, R_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR, 0xd8);
+ hdmi_writeb(priv, R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR, 0xfe);
+
+ while (nbyte > 0) {
+ hdmi_writeb(priv, R_7e00_HDMI_I2CM_SLAVE, 0xa0 >> 1);
+ hdmi_writeb(priv, R_7e01_HDMI_I2CM_ADDRESS, off);
+ hdmi_writeb(priv, R_7e08_HDMI_I2CM_SEGADDR, 0x60 >> 1);
+ hdmi_writeb(priv, R_7e0a_HDMI_I2CM_SEGPTR, pointer);
+ hdmi_writeb(priv, R_7e04_HDMI_I2CM_OPERATION,
+ HDMI_I2CM_OPERATION_DDC_READ);
+
+ to_cnt = 200; /* timeout 100ms */
+ while (1) {
+ reg = hdmi_readb(priv, R_0105_HDMI_IH_I2CM_STAT0);
+ hdmi_writeb(priv, R_0105_HDMI_IH_I2CM_STAT0, reg);
+ if (reg & 0x02) {
+ *pbuf++ = hdmi_readb(priv,
+ R_7e03_HDMI_I2CM_DATAI);
+ break;
+ }
+ if (reg & 0x01) {
+ dev_err(priv->dev, "hdmi ddc read error\n");
+ ret = -1;
+ break;
+ }
+ if (--to_cnt == 0) {
+ if (!ret) {
+ dev_err(priv->dev,
+ "hdmi ddc read timeout\n");
+ ret = -1;
+ }
+ break;
+ }
+ usleep_range(500, 800);
+ }
+ if (ret)
+ break;
+ nbyte--;
+ off++;
+ }
+ hdmi_read_off(priv);
+
+ return ret;
+}
+
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv)
+{
+ int ret;
+
+ hdmi_read_on(priv);
+
+ if (priv->soc_type == SOC_H3)
+ ret = hdmi_readl(priv, HDMI_PHY_STATUS_REG) & 0x80000;
+ else
+ ret = hdmi_readb(priv, R_3005_HDMI_PHY_INT0) & 0x02;
+
+ hdmi_read_off(priv);
+
+ return ret != 0;
+}
--
2.10.2
^ permalink raw reply related
* [PATCH] PM / Domains: Fix compatible for domain idle state
From: Ulf Hansson @ 2016-11-29 8:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161110195832.4nz7lxlmshaemcbb@rob-hp-laptop>
On 10 November 2016 at 20:58, Rob Herring <robh@kernel.org> wrote:
> On Mon, Nov 07, 2016 at 12:14:28PM +0100, Ulf Hansson wrote:
>> On 3 November 2016 at 22:54, Lina Iyer <lina.iyer@linaro.org> wrote:
>> > Re-using idle state definition provided by arm,idle-state for domain
>> > idle states creates a lot of confusion and limits further evolution of
>> > the domain idle definition. To keep things clear and simple, define a
>> > idle states for domain using a new compatible "domain-idle-state".
>> >
>> > Fix existing PM domains code to look for the newly defined compatible.
>> >
>> > Cc: <devicetree@vger.kernel.org>
>> > Cc: Rob Herring <robh@kernel.org>
>> > Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>> > ---
>> > .../bindings/power/domain-idle-state.txt | 33 ++++++++++++++++++++++
>> > .../devicetree/bindings/power/power_domain.txt | 8 +++---
>> > drivers/base/power/domain.c | 2 +-
>> > 3 files changed, 38 insertions(+), 5 deletions(-)
>> > create mode 100644 Documentation/devicetree/bindings/power/domain-idle-state.txt
>> >
>> > diff --git a/Documentation/devicetree/bindings/power/domain-idle-state.txt b/Documentation/devicetree/bindings/power/domain-idle-state.txt
>> > new file mode 100644
>> > index 0000000..eefc7ed
>> > --- /dev/null
>> > +++ b/Documentation/devicetree/bindings/power/domain-idle-state.txt
>> > @@ -0,0 +1,33 @@
>> > +PM Domain Idle State Node:
>> > +
>> > +A domain idle state node represents the state parameters that will be used to
>> > +select the state when there are no active components in the domain.
>> > +
>> > +The state node has the following parameters -
>> > +
>> > +- compatible:
>> > + Usage: Required
>> > + Value type: <string>
>> > + Definition: Must be "domain-idle-state".
>> > +
>> > +- entry-latency-us
>> > + Usage: Required
>> > + Value type: <prop-encoded-array>
>> > + Definition: u32 value representing worst case latency in
>> > + microseconds required to enter the idle state.
>> > + The exit-latency-us duration may be guaranteed
>> > + only after entry-latency-us has passed.
>>
>> As we anyway are going to change this, why not use an u64 and have the
>> value in ns instead of us?
>
> I can't imagine that you would need more resolution or range. For times
> less than 1us, s/w and register access times are going to dominate the
> time.
>
> Unless there is a real need, I'd keep alignment with the existing
> binding.
Rob, are you fine with this? I thought it would be great to get this
in for 4.10 rc1.
Kind regards
Uffe
^ 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