* [PATCH] ARM: DT: STM32: add dma for usart3 on F429
From: Alexandre TORGUE @ 2016-10-24 13:31 UTC (permalink / raw)
To: linux-arm-kernel
Add DMA support for USART3 on STM32F429 MCU.
Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com>
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 11e8a69..0596d60 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -122,6 +122,9 @@
interrupts = <39>;
clocks = <&rcc 0 146>;
status = "disabled";
+ dmas = <&dma1 1 4 0x400 0x0>,
+ <&dma1 3 4 0x400 0x0>;
+ dma-names = "rx", "tx";
};
usart4: serial at 40004c00 {
--
1.9.1
^ permalink raw reply related
* [PATCH v3 3/8] PM / Domains: Allow domain power states to be read from DT
From: Sudeep Holla @ 2016-10-24 13:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476467276-75094-4-git-send-email-lina.iyer@linaro.org>
On 14/10/16 18:47, Lina Iyer wrote:
> This patch allows domains to define idle states in the DT. SoC's can
> define domain idle states in DT using the "domain-idle-states" property
> of the domain provider. Add API to read the idle states from DT that can
> be set in the genpd object.
>
> This patch is based on the original patch by Marc Titinger.
>
> Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
> ---
> drivers/base/power/domain.c | 94 +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/pm_domain.h | 8 ++++
> 2 files changed, 102 insertions(+)
>
> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
> index 37ab7f1..9af75ba 100644
> --- a/drivers/base/power/domain.c
> +++ b/drivers/base/power/domain.c
> @@ -1916,6 +1916,100 @@ out:
> return ret ? -EPROBE_DEFER : 0;
> }
> EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
> +
> +static const struct of_device_id idle_state_match[] = {
> + { .compatible = "arm,idle-state", },
> + { }
> +};
> +
I still think it's better to have another compatible to serve this
purpose. We don't want to end up creating genpd domains just because
they are "arm,idle-state" compatible IMO ?
I agree you can prevent it checking for OSC mode support in the
firmware. But I want to understand if you have any strong reasons for
avoiding that approach.
--
Regards,
Sudeep
^ permalink raw reply
* [PATCH 1/3] arm64: arch_timer: Add device tree binding for hisilicon-161x01 erratum
From: Mark Rutland @ 2016-10-24 13:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1dcfb21a-7417-282e-f187-425d2c148672@huawei.com>
On Mon, Oct 24, 2016 at 09:23:10PM +0800, Ding Tianhong wrote:
> On 2016/10/24 21:16, Mark Rutland wrote:
> > On Mon, Oct 24, 2016 at 08:40:01PM +0800, Ding Tianhong wrote:
> >> On 2016/10/24 19:16, Mark Rutland wrote:
> >>> Is "161x01" the *exact* erratum number, or is the 'x' a wildcard?
> >>
> >> The 'x' is a wildcard, it will cover 161001 to 161601 several numbers,
> >
> > Given you're using a wildcard, I take it that this is a *part* number?
>
> Yes, I was doubt how to fix this, should I choose a better erratum number?
Typically, we expect that each vendor has some central database of their
errata, with each having a unique ID.
If Huawei do not have such a database, I do not think that we should
invent an erratum number here.
Thanks,
Mark.
^ permalink raw reply
* [PATCH] ARM: sti: stih410-clocks: Add PROC_STFE as a critical clock
From: Peter Griffin @ 2016-10-24 13:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161024085304.GE14477@dell>
Hi Lee,
On Mon, 24 Oct 2016, Lee Jones wrote:
> On Tue, 18 Oct 2016, Peter Griffin wrote:
>
> > Once the ST frontend demux HW IP has been enabled, the clock can't
> > be disabled otherwise the system will hang and the board will
> > be unserviceable.
> >
> > To allow balanced clock enable/disable calls in the driver we use
> > the critical clock infrastructure to take an extra reference on the
> > clock so the clock will never actually be disabled.
>
> This is an abuse of the critical-clocks framework, and is exactly the
> type of hack I promised the clk guys I'd try to prevent.
I expect the best way to do this would be to write some documentation on the
clock-critical DT binding and/or CRITICAL_CLK flag. The only documentation I can
find currently is with the initial patch series [1] and the comment in
clk-provider.h of
#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */
Or the patch decription
"Critical clocks are those which must not be gated, else undefined
or catastrophic failure would occur. Here we have chosen to
ensure the prepare/enable counts are correctly incremented, so as
not to confuse users with enabled clocks with no visible users."
Which is the functionality I want for this clock.
> If this, or
> any other IP has some quirks (i.e. once enabled, if this clock is
> subsequently disabled it will have a catastrophic effect on the
> platform), then they should be worked around in the driver.
>
> The correct thing to do here is craft a clk-keep-on flag and ensure it
> is set to true for the effected platform(s)' platform data.
I'm always wary of creating a driver specific flag, especially when its
purpose is to do the same thing as an existing mechanism provided by the
subsystem of not gating the clock.
I can see a couple of problems with what you propose:
1) You have to put the clk-keep-on flag in every driver which consumes the
clock. IMO it is much better to have this knowledge in the SoC's
clock driver so every consumer of the clock automatically benefits.
2) You don't benefit from the CLK_IS_CRITICAL reference counting logic in
clk.c. So then each driver has to also work around that to get sensible reference
counts. e.g.
if (!__clk_is_enabled(clk) && pdata->clk-keep-on)
clk_enable(clk)
Which seems to me to be fighting against the subsystem. Given that the only use of
_clk_is_enabled() outside drivers/clk is in an old arch/arm/mach-omap2/pm24xx.c
driver I suspect its use is frowned upon, and it shouldn't really be an EXPORTED_SYMBOL.
regards,
Peter.
[1] https://lkml.org/lkml/2016/1/18/272
^ permalink raw reply
* [PATCH] arm64: SMMU-v2: Workaround for Cavium ThunderX erratum 28168
From: Marc Zyngier @ 2016-10-24 13:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477112061-12868-1-git-send-email-gakula@caviumnetworks.com>
Geetha,
On 22/10/16 05:54, Geetha sowjanya wrote:
> From: Tirumalesh Chalamarla <Tirumalesh.Chalamarla@cavium.com>
>
> This patch implements Cavium ThunderX erratum 28168.
>
> PCI requires stores complete in order. Due to erratum #28168
> PCI-inbound MSI-X store to the interrupt controller are delivered
> to the interrupt controller before older PCI-inbound memory stores
> are committed.
> Doing a sync on SMMU will make sure all prior transactions are
> completed.
>
> Signed-off-by: Tirumalesh Chalamarla <Tirumalesh.Chalamarla@cavium.com>
> Signed-off-by: Geetha sowjanya <gakula@caviumnetworks.com>
> ---
> arch/arm64/Kconfig | 11 +++++++++++
> drivers/iommu/arm-smmu.c | 38 ++++++++++++++++++++++++++++++++++++++
> drivers/irqchip/irq-gic-common.h | 1 +
> drivers/irqchip/irq-gic-v3-its.c | 22 ++++++++++++++++++++++
> kernel/irq/chip.c | 4 ++++
Thanks Robin for looping me in. Geetha, please use get_maintainers.pl to
keep the relevant people on CC, specially as you're touching some of the
core infrastructure.
> 5 files changed, 76 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 30398db..57f5c9b 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -474,6 +474,17 @@ config CAVIUM_ERRATUM_27456
>
> If unsure, say Y.
>
> +config CAVIUM_ERRATUM_28168
> + bool "Cavium erratum 28168: Make sure ITS and SMMU TLB are in sync"
> + default y
> + help
> + Due to erratum #28168 PCI-inbound MSI-X store to the interrupt
> + controller are delivered to the interrupt controller before older
> + PCI-inbound memory stores are committed. Doing a sync on SMMU
> + will make sure all prior transactions are completed.
> +
> + If unsure, say Y.
Please add an entry to Documentation/arm64/silicon-errata.txt.
> +
> endmenu
>
>
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
> index 9740846..20a61c6 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
I'll skip the SMMU code on which Robin has commented already, and move
to the irq part, which is equally entertaining.
[...]
> diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h
> index 205e5fd..0228ba0 100644
> --- a/drivers/irqchip/irq-gic-common.h
> +++ b/drivers/irqchip/irq-gic-common.h
> @@ -38,4 +38,5 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
>
> void gic_set_kvm_info(const struct gic_kvm_info *info);
>
> +void cavium_smmu_tlb_sync(void *iommu);
> #endif /* _IRQ_GIC_COMMON_H */
> diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
> index 003495d..88e9958 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -112,6 +112,7 @@ struct its_device {
> struct its_node *its;
> struct event_lpi_map event_map;
> void *itt;
> + struct device *dev;
This doesn't work in the presence of anything that will multiplex
multiple RequesterIDs onto a single DeviceID (non transparent PCI
bridge, for example).
> u32 nr_ites;
> u32 device_id;
> };
> @@ -664,10 +665,29 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
> iommu_dma_map_msi_msg(d->irq, msg);
> }
>
> +/**
> + * Due to erratum in ThunderX,
> + * we need to make sure SMMU is in sync with ITS translations.
> + **/
> +static void its_ack_irq(struct irq_data *d)
> +{
> + struct its_device *its_dev = irq_data_get_irq_chip_data(d);
> + struct pci_dev *pdev;
> +
> + if (!dev_is_pci(its_dev->dev))
> + return;
How about non PCI devices?
> +
> + pdev = to_pci_dev(its_dev->dev);
> + if (pdev->vendor != 0x177d)
> + cavium_smmu_tlb_sync(its_dev->dev);
What makes Cavium devices so special that they do not need to respect
the PCI memory ordering with respect to MSI delivery?
> +
> +}
> +
> static struct irq_chip its_irq_chip = {
> .name = "ITS",
> .irq_mask = its_mask_irq,
> .irq_unmask = its_unmask_irq,
> + .irq_ack = its_ack_irq,
Nice try, but no, thank you. If you really want to go down that road,
have a look at CONFIG_IRQ_PREFLOW_FASTEOI, and make this workaround a
per interrupt thing. At least, you won't pollute the core code with
another hack.
Also, it would be good to find a way for that hack to be confined to the
SMMU driver, since that's where the oddity is being handled. Something
that would occur when the device is mapping memory is probably on good spot.
> .irq_eoi = irq_chip_eoi_parent,
> .irq_set_affinity = its_set_affinity,
> .irq_compose_msi_msg = its_irq_compose_msi_msg,
> @@ -1422,6 +1442,8 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
> if (!its_dev)
> return -ENOMEM;
>
> + its_dev->dev = dev;
> +
> pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec));
> out:
> info->scratchpad[0].ptr = its_dev;
> diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
> index be3c34e..6add8da 100644
> --- a/kernel/irq/chip.c
> +++ b/kernel/irq/chip.c
> @@ -585,6 +585,10 @@ void handle_fasteoi_irq(struct irq_desc *desc)
> goto out;
> }
>
> +#ifdef CONFIG_CAVIUM_ERRATUM_28168
> + if (chip->irq_ack)
> + chip->irq_ack(&desc->irq_data);
> +#endif
> kstat_incr_irqs_this_cpu(desc);
> if (desc->istate & IRQS_ONESHOT)
> mask_irq(desc);
>
Overall, this workaround is not acceptable as it is. You need to find
ways to make it less invasive, and hopefully the above pointers will
help. Please keep the current distribution list posted once you update
this patch.
Thanks,
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* [PATCH v5 1/7] drm: sunxi: Add a basic DRM driver for Allwinner DE2
From: Maxime Ripard @ 2016-10-24 14:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8afc5e020c5767face34fe3a9ab300ce9e67ba00.1477142934.git.moinejf@free.fr>
Hi,
On Fri, Oct 21, 2016 at 09:26:18AM +0200, Jean-Francois Moine wrote:
> Allwinner's recent SoCs, as A64, A83T and H3, contain a new display
> engine, DE2.
> This patch adds a DRM video driver for this device.
>
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Output from checkpatch:
total: 0 errors, 20 warnings, 83 checks, 1799 lines checked
> ---
> .../bindings/display/sunxi/sunxi-de2.txt | 83 +++
> drivers/gpu/drm/Kconfig | 2 +
> drivers/gpu/drm/Makefile | 1 +
> drivers/gpu/drm/sunxi/Kconfig | 21 +
> drivers/gpu/drm/sunxi/Makefile | 7 +
> drivers/gpu/drm/sunxi/de2_crtc.c | 475 +++++++++++++++++
> drivers/gpu/drm/sunxi/de2_crtc.h | 63 +++
> drivers/gpu/drm/sunxi/de2_de.c | 591 +++++++++++++++++++++
> drivers/gpu/drm/sunxi/de2_drm.h | 47 ++
> drivers/gpu/drm/sunxi/de2_drv.c | 378 +++++++++++++
> drivers/gpu/drm/sunxi/de2_plane.c | 119 +++++
> 11 files changed, 1787 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
> create mode 100644 drivers/gpu/drm/sunxi/Kconfig
> create mode 100644 drivers/gpu/drm/sunxi/Makefile
> create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.h
> create mode 100644 drivers/gpu/drm/sunxi/de2_de.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_drm.h
> create mode 100644 drivers/gpu/drm/sunxi/de2_drv.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_plane.c
>
> diff --git a/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt b/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
> new file mode 100644
> index 0000000..f9cd67a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
> @@ -0,0 +1,83 @@
> +Allwinner sunxi Display Engine 2 subsystem
> +==========================================
> +
> +The sunxi DE2 subsystem contains a display controller (DE2),
sunxi is a made up name, and doesn't really mean anything. You can
call it either sun8i (because it was introduced in that family).
> +one or two LCD controllers (TCON) and their external interfaces.
> +
> +Display controller
> +==================
> +
> +Required properties:
> +
> +- compatible: value should be one of the following
> + "allwinner,sun8i-a83t-display-engine"
> + "allwinner,sun8i-h3-display-engine"
> +
> +- clocks: must include clock specifiers corresponding to entries in the
> + clock-names property.
> +
> +- clock-names: must contain
> + "gate": for DE activation
> + "clock": DE clock
We've been calling them bus and mod.
> +
> +- resets: phandle to the reset of the device
> +
> +- ports: phandle's to the LCD ports
Please use the OF graph.
> +
> +LCD controller
> +==============
> +
> +Required properties:
> +
> +- compatible: value should be one of the following
> + "allwinner,sun8i-a83t-lcd"
> +
> +- clocks: must include clock specifiers corresponding to entries in the
> + clock-names property.
> +
> +- clock-names: must contain
> + "gate": for LCD activation
> + "clock": pixel clock
> +
> +- resets: phandle to the reset of the device
> +
> +- port: port node with endpoint definitions as defined in
> + Documentation/devicetree/bindings/media/video-interfaces.txt
> +
> +Example:
> +
> + de: de-controller at 01000000 {
> + compatible = "allwinner,sun8i-h3-display-engine";
> + ...
> + clocks = <&&ccu CLK_BUS_DE>, <&ccu CLK_DE>;
> + clock-names = "gate", "clock";
> + resets = <&ccu RST_BUS_DE>;
> + ports = <&lcd0_p>;
> + };
> +
> + lcd0: lcd-controller at 01c0c000 {
> + compatible = "allwinner,sun8i-a83t-lcd";
> + ...
> + clocks = <&ccu CLK_BUS_TCON0>, <&ccu CLK_TCON0>;
> + clock-names = "gate", "clock";
> + resets = <&ccu RST_BUS_TCON0>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> + lcd0_p: port {
> + lcd0_ep: endpoint {
> + remote-endpoint = <&hdmi_ep>;
> + };
> + };
> + };
> +
> + hdmi: hdmi at 01ee0000 {
> + ...
> + #address-cells = <1>;
> + #size-cells = <0>;
> + port {
> + type = "video";
> + hdmi_ep: endpoint {
> + remote-endpoint = <&lcd0_ep>;
> + };
> + };
> + };
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 483059a..afd576f 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -187,6 +187,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
>
> source "drivers/gpu/drm/sun4i/Kconfig"
>
> +source "drivers/gpu/drm/sunxi/Kconfig"
> +
> source "drivers/gpu/drm/omapdrm/Kconfig"
>
> source "drivers/gpu/drm/tilcdc/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 25c7204..120d0bf 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -70,6 +70,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
> obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
> obj-y += omapdrm/
> obj-$(CONFIG_DRM_SUN4I) += sun4i/
> +obj-$(CONFIG_DRM_SUNXI) += sunxi/
> obj-y += tilcdc/
> obj-$(CONFIG_DRM_QXL) += qxl/
> obj-$(CONFIG_DRM_BOCHS) += bochs/
> diff --git a/drivers/gpu/drm/sunxi/Kconfig b/drivers/gpu/drm/sunxi/Kconfig
> new file mode 100644
> index 0000000..56bde2e
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/Kconfig
> @@ -0,0 +1,21 @@
> +#
> +# Allwinner Video configuration
> +#
> +
> +config DRM_SUNXI
> + tristate "DRM Support for Allwinner Video"
> + depends on DRM && OF
> + depends on ARCH_SUNXI || COMPILE_TEST
> + select DRM_KMS_HELPER
> + select DRM_KMS_CMA_HELPER
> + select DRM_GEM_CMA_HELPER
> + help
> + Choose this option if you have a Allwinner chipset.
> +
> +config DRM_SUNXI_DE2
> + tristate "Support for Allwinner Video with DE2 interface"
> + depends on DRM_SUNXI
> + help
> + 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
> + sunxi-de2-drm.
> diff --git a/drivers/gpu/drm/sunxi/Makefile b/drivers/gpu/drm/sunxi/Makefile
> new file mode 100644
> index 0000000..62220cb
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for Allwinner's sun8i DRM device driver
> +#
> +
> +sunxi-de2-drm-objs := de2_drv.o de2_de.o de2_crtc.o de2_plane.o
> +
> +obj-$(CONFIG_DRM_SUNXI_DE2) += sunxi-de2-drm.o
> diff --git a/drivers/gpu/drm/sunxi/de2_crtc.c b/drivers/gpu/drm/sunxi/de2_crtc.c
> new file mode 100644
> index 0000000..dae0fab
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/de2_crtc.c
> @@ -0,0 +1,475 @@
> +/*
> + * Allwinner DRM driver - DE2 CRTC
> + *
> + * 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/component.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <asm/io.h>
> +#include <linux/of_irq.h>
> +
> +#include "de2_drm.h"
> +#include "de2_crtc.h"
> +
> +/* I/O map */
> +
> +struct tcon {
> + u32 gctl;
> +#define TCON_GCTL_TCON_En BIT(31)
> + u32 gint0;
> +#define TCON_GINT0_TCON1_Vb_Int_En BIT(30)
> +#define TCON_GINT0_TCON1_Vb_Int_Flag BIT(14)
> + u32 gint1;
> + u32 dum0[13];
> + u32 tcon0_ctl; /* 0x40 */
> +#define TCON0_CTL_TCON_En BIT(31)
> + u32 dum1[19];
> + u32 tcon1_ctl; /* 0x90 */
> +#define TCON1_CTL_TCON_En BIT(31)
> +#define TCON1_CTL_Interlace_En BIT(20)
> +#define TCON1_CTL_Start_Delay_SHIFT 4
> +#define TCON1_CTL_Start_Delay_MASK GENMASK(8, 4)
> + u32 basic0; /* XI/YI */
> + u32 basic1; /* LS_XO/LS_YO */
> + u32 basic2; /* XO/YO */
> + u32 basic3; /* HT/HBP */
> + u32 basic4; /* VT/VBP */
> + u32 basic5; /* HSPW/VSPW */
> + u32 dum2;
> + u32 ps_sync; /* 0xb0 */
> + u32 dum3[15];
> + u32 io_pol; /* 0xf0 */
> +#define TCON1_IO_POL_IO0_inv BIT(24)
> +#define TCON1_IO_POL_IO1_inv BIT(25)
> +#define TCON1_IO_POL_IO2_inv BIT(26)
> + u32 io_tri;
> + u32 dum4[2];
> +
> + u32 ceu_ctl; /* 0x100 */
> +#define TCON_CEU_CTL_ceu_en BIT(31)
> + u32 dum5[3];
> + u32 ceu_rr;
> + u32 ceu_rg;
> + u32 ceu_rb;
> + u32 ceu_rc;
> + u32 ceu_gr;
> + u32 ceu_gg;
> + u32 ceu_gb;
> + u32 ceu_gc;
> + u32 ceu_br;
> + u32 ceu_bg;
> + u32 ceu_bb;
> + u32 ceu_bc;
> + u32 ceu_rv;
> + u32 ceu_gv;
> + u32 ceu_bv;
> + u32 dum6[45];
> +
> + u32 mux_ctl; /* 0x200 */
> + u32 dum7[63];
> +
> + u32 fill_ctl; /* 0x300 */
> + u32 fill_start0;
> + u32 fill_end0;
> + u32 fill_data0;
> +};
Please use defines instead of the structures.
> +
> +#define XY(x, y) (((x) << 16) | (y))
> +
> +#define tcon_read(base, member) \
> + readl_relaxed(base + offsetof(struct tcon, member))
> +#define tcon_write(base, member, data) \
> + writel_relaxed(data, base + offsetof(struct tcon, member))
> +
> +/* vertical blank functions */
> +static void de2_atomic_flush(struct drm_crtc *crtc,
> + struct drm_crtc_state *old_state)
> +{
> + struct drm_pending_vblank_event *event = crtc->state->event;
> +
> + if (event) {
> + crtc->state->event = NULL;
> + spin_lock_irq(&crtc->dev->event_lock);
> + if (drm_crtc_vblank_get(crtc) == 0)
> + drm_crtc_arm_vblank_event(crtc, event);
> + else
> + drm_crtc_send_vblank_event(crtc, event);
> + spin_unlock_irq(&crtc->dev->event_lock);
> + }
> +}
> +
> +static irqreturn_t de2_lcd_irq(int irq, void *dev_id)
> +{
> + struct lcd *lcd = (struct lcd *) dev_id;
> + u32 isr;
> +
> + isr = tcon_read(lcd->mmio, gint0);
> +
> + drm_crtc_handle_vblank(&lcd->crtc);
> +
> + tcon_write(lcd->mmio, gint0, isr & ~TCON_GINT0_TCON1_Vb_Int_Flag);
> +
> + return IRQ_HANDLED;
> +}
> +
> +int de2_enable_vblank(struct drm_device *drm, unsigned crtc)
> +{
> + struct priv *priv = drm->dev_private;
> + struct lcd *lcd = priv->lcds[crtc];
> +
> + tcon_write(lcd->mmio, gint0,
> + tcon_read(lcd->mmio, gint0) |
> + TCON_GINT0_TCON1_Vb_Int_En);
That's a weird indentation
> + return 0;
> +}
> +
> +void de2_disable_vblank(struct drm_device *drm, unsigned crtc)
> +{
> + struct priv *priv = drm->dev_private;
> + struct lcd *lcd = priv->lcds[crtc];
> +
> + tcon_write(lcd->mmio, gint0,
> + tcon_read(lcd->mmio, gint0) &
> + ~TCON_GINT0_TCON1_Vb_Int_En);
> +}
> +
> +/* panel functions */
Panel functions? In the CRTC driver?
> +static void de2_set_frame_timings(struct lcd *lcd)
> +{
> + struct drm_crtc *crtc = &lcd->crtc;
> + const struct drm_display_mode *mode = &crtc->mode;
> + int interlace = mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 1;
> + int start_delay;
> + u32 data;
> +
> + data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);
> + tcon_write(lcd->mmio, basic0, data);
> + tcon_write(lcd->mmio, basic1, data);
> + tcon_write(lcd->mmio, basic2, data);
> + tcon_write(lcd->mmio, basic3,
> + XY(mode->htotal - 1,
> + mode->htotal - mode->hsync_start - 1));
> + tcon_write(lcd->mmio, basic4,
> + XY(mode->vtotal * (3 - interlace),
> + mode->vtotal - mode->vsync_start - 1));
> + tcon_write(lcd->mmio, basic5,
> + XY(mode->hsync_end - mode->hsync_start - 1,
> + mode->vsync_end - mode->vsync_start - 1));
> +
> + tcon_write(lcd->mmio, ps_sync, XY(1, 1));
> +
> + data = TCON1_IO_POL_IO2_inv;
> + if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> + data |= TCON1_IO_POL_IO0_inv;
> + if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> + data |= TCON1_IO_POL_IO1_inv;
> + tcon_write(lcd->mmio, io_pol, data);
> +
> + tcon_write(lcd->mmio, ceu_ctl,
> + tcon_read(lcd->mmio, ceu_ctl) & ~TCON_CEU_CTL_ceu_en);
> +
> + data = tcon_read(lcd->mmio, tcon1_ctl);
> + if (interlace == 2)
> + data |= TCON1_CTL_Interlace_En;
> + else
> + data &= ~TCON1_CTL_Interlace_En;
> + tcon_write(lcd->mmio, tcon1_ctl, data);
> +
> + tcon_write(lcd->mmio, fill_ctl, 0);
> + tcon_write(lcd->mmio, fill_start0, mode->vtotal + 1);
> + tcon_write(lcd->mmio, fill_end0, mode->vtotal);
> + tcon_write(lcd->mmio, fill_data0, 0);
> +
> + start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;
> + if (start_delay > 31)
> + start_delay = 31;
> + data = tcon_read(lcd->mmio, tcon1_ctl);
> + data &= ~TCON1_CTL_Start_Delay_MASK;
> + data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;
> + tcon_write(lcd->mmio, tcon1_ctl, data);
> +
> + tcon_write(lcd->mmio, io_tri, 0x0fffffff);
> +}
Some comments here would be nice, there's a lot of non trivial things.
> +
> +static void de2_crtc_enable(struct drm_crtc *crtc)
> +{
> + struct lcd *lcd = crtc_to_lcd(crtc);
> + struct drm_display_mode *mode = &crtc->mode;
> +
> + DRM_DEBUG_DRIVER("\n");
Log something useful, or don't.
> +
> + clk_set_rate(lcd->clk, mode->clock * 1000);
> +
> + de2_set_frame_timings(lcd);
> +
> + tcon_write(lcd->mmio, tcon1_ctl,
> + tcon_read(lcd->mmio, tcon1_ctl) | TCON1_CTL_TCON_En);
> +
> + de2_de_panel_init(lcd->priv, lcd->num, mode);
panel_init in the CRTC enable? Shouldn't that be in the panel driver?
or at least the encoder?
> +
> + drm_mode_debug_printmodeline(mode);
This is already printed by the core.
> +}
> +
> +static void de2_crtc_disable(struct drm_crtc *crtc)
> +{
> + struct lcd *lcd = crtc_to_lcd(crtc);
> + unsigned long flags;
> +
> + DRM_DEBUG_DRIVER("\n");
> +
> + tcon_write(lcd->mmio, tcon1_ctl,
> + tcon_read(lcd->mmio, tcon1_ctl) & ~TCON1_CTL_TCON_En);
> +
> + if (crtc->state->event && !crtc->state->active) {
> + spin_lock_irqsave(&crtc->dev->event_lock, flags);
> + drm_crtc_send_vblank_event(crtc, crtc->state->event);
> + spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
> + crtc->state->event = NULL;
> + }
> +}
> +
> +static const struct drm_crtc_funcs de2_crtc_funcs = {
> + .destroy = drm_crtc_cleanup,
> + .set_config = drm_atomic_helper_set_config,
> + .page_flip = drm_atomic_helper_page_flip,
> + .reset = drm_atomic_helper_crtc_reset,
> + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
> +};
> +
> +static const struct drm_crtc_helper_funcs de2_crtc_helper_funcs = {
> + .atomic_flush = de2_atomic_flush,
> + .enable = de2_crtc_enable,
> + .disable = de2_crtc_disable,
> +};
> +
> +static void de2_tcon_init(struct lcd *lcd)
> +{
> + tcon_write(lcd->mmio, tcon0_ctl,
> + tcon_read(lcd->mmio, tcon0_ctl) & ~TCON0_CTL_TCON_En);
> + tcon_write(lcd->mmio, tcon1_ctl,
> + tcon_read(lcd->mmio, tcon1_ctl) & ~TCON1_CTL_TCON_En);
> + tcon_write(lcd->mmio, gctl,
> + tcon_read(lcd->mmio, gctl) & ~TCON_GCTL_TCON_En);
> +
> + /* disable/ack interrupts */
> + tcon_write(lcd->mmio, gint0, 0);
> +}
> +
> +static void de2_tcon_enable(struct lcd *lcd)
> +{
> + tcon_write(lcd->mmio, gctl,
> + tcon_read(lcd->mmio, gctl) | TCON_GCTL_TCON_En);
> +}
> +
> +static int de2_crtc_init(struct drm_device *drm, struct lcd *lcd)
> +{
> + struct drm_crtc *crtc = &lcd->crtc;
> + int ret;
> +
> + ret = de2_plane_init(drm, lcd);
> + if (ret < 0)
> + return ret;
> +
> + drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);
> +
> + ret = drm_crtc_init_with_planes(drm, crtc,
> + &lcd->planes[DE2_PRIMARY_PLANE],
> + &lcd->planes[DE2_CURSOR_PLANE],
> + &de2_crtc_funcs, NULL);
> + if (ret < 0)
> + return ret;
> +
> + de2_tcon_enable(lcd);
> +
> + de2_de_enable(lcd->priv, lcd->num);
> +
> + return 0;
> +}
> +
> +/*
> + * device init
> + */
> +static int de2_lcd_bind(struct device *dev, struct device *master,
> + void *data)
> +{
> + struct drm_device *drm = data;
> + struct priv *priv = drm->dev_private;
> + struct lcd *lcd = dev_get_drvdata(dev);
> + int ret;
> +
> + lcd->priv = priv;
> +
> + /* (only 2 LCDs) */
> + lcd->crtc_idx = priv->lcds[0] ? 1 : 0;
> + priv->lcds[lcd->crtc_idx] = lcd;
> +
> + ret = de2_crtc_init(drm, lcd);
> + if (ret < 0) {
> + dev_err(dev, "failed to init the crtc\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void de2_lcd_unbind(struct device *dev, struct device *master,
> + void *data)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct lcd *lcd = platform_get_drvdata(pdev);
> +
> + if (lcd->mmio) {
> + if (lcd->priv)
> + de2_de_disable(lcd->priv, lcd->num);
> + tcon_write(lcd->mmio, gctl,
> + tcon_read(lcd->mmio, gctl) & ~TCON_GCTL_TCON_En);
> + }
> +}
> +
> +static const struct component_ops de2_lcd_ops = {
> + .bind = de2_lcd_bind,
> + .unbind = de2_lcd_unbind,
> +};
> +
> +static int de2_lcd_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node, *tmp, *parent, *port;
> + struct lcd *lcd;
> + struct resource *res;
> + int id, irq, ret;
> +
> + id = of_alias_get_id(np, "lcd");
> + if (id < 0) {
> + dev_err(dev, "no alias for lcd\n");
> + id = 0;
> + }
> + lcd = devm_kzalloc(dev, sizeof *lcd, GFP_KERNEL);
> + if (!lcd) {
> + dev_err(dev, "failed to allocate private data\n");
> + return -ENOMEM;
> + }
> + dev_set_drvdata(dev, lcd);
> + lcd->dev = dev;
> + lcd->num = id;
What do you need this number for?
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "failed to get memory resource\n");
> + return -EINVAL;
> + }
> +
> + lcd->mmio = devm_ioremap_resource(dev, res);
> + if (IS_ERR(lcd->mmio)) {
> + dev_err(dev, "failed to map registers\n");
> + return PTR_ERR(lcd->mmio);
> + }
> +
> + snprintf(lcd->name, sizeof(lcd->name), "sunxi-lcd%d", id);
> +
> + /* possible CRTCs */
> + parent = np;
> + tmp = of_get_child_by_name(np, "ports");
> + if (tmp)
> + parent = tmp;
> + port = of_get_child_by_name(parent, "port");
> + of_node_put(tmp);
> + if (!port) {
> + dev_err(dev, "no port node\n");
> + return -ENXIO;
> + }
> + lcd->crtc.port = port;
> +
> + lcd->gate = devm_clk_get(dev, "gate"); /* optional */
Having some kind of error checking would still be nice.
> +
> + lcd->clk = devm_clk_get(dev, "clock");
> + if (IS_ERR(lcd->clk)) {
> + dev_err(dev, "video clock err %d\n", (int) PTR_ERR(lcd->clk));
> + ret = PTR_ERR(lcd->clk);
> + goto err;
> + }
> +
> + lcd->rstc = devm_reset_control_get_optional(dev, NULL);
Ditto.
> +
> + irq = irq_of_parse_and_map(np, 0);
> + if (irq <= 0 || irq == NO_IRQ) {
> + dev_err(dev, "unable to get irq lcd %d\n", id);
> + ret = -EINVAL;
> + goto err;
> + }
You can use platform_get_irq for that.
> +
> + if (!IS_ERR(lcd->rstc)) {
> + ret = reset_control_deassert(lcd->rstc);
> + if (ret) {
> + dev_err(dev, "reset deassert err %d\n", ret);
> + goto err;
> + }
> + }
> +
> + if (!IS_ERR(lcd->gate)) {
> + ret = clk_prepare_enable(lcd->gate);
> + if (ret)
> + goto err2;
> + }
> +
> + ret = clk_prepare_enable(lcd->clk);
> + if (ret)
> + goto err2;
Is there any reason not to do that in the enable / disable? Leaving
clocks running while the device has no guarantee that it's going to be
used seems like a waste of resources.
> +
> + de2_tcon_init(lcd);
> +
> + ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,
> + lcd->name, lcd);
> + if (ret < 0) {
> + dev_err(dev, "unable to request irq %d\n", irq);
> + goto err2;
> + }
> +
> + return component_add(dev, &de2_lcd_ops);
> +
> +err2:
> + if (!IS_ERR_OR_NULL(lcd->rstc))
> + reset_control_assert(lcd->rstc);
> + clk_disable_unprepare(lcd->gate);
> + clk_disable_unprepare(lcd->clk);
> +err:
> + of_node_put(lcd->crtc.port);
> + return ret;
> +}
> +
> +static int de2_lcd_remove(struct platform_device *pdev)
> +{
> + struct lcd *lcd = platform_get_drvdata(pdev);
> +
> + component_del(&pdev->dev, &de2_lcd_ops);
> +
> + if (!IS_ERR_OR_NULL(lcd->rstc))
> + reset_control_assert(lcd->rstc);
> + clk_disable_unprepare(lcd->gate);
> + clk_disable_unprepare(lcd->clk);
> + of_node_put(lcd->crtc.port);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id de2_lcd_ids[] = {
> + { .compatible = "allwinner,sun8i-a83t-lcd", },
> + { }
> +};
> +
> +struct platform_driver de2_lcd_platform_driver = {
> + .probe = de2_lcd_probe,
> + .remove = de2_lcd_remove,
> + .driver = {
> + .name = "sunxi-de2-lcd",
> + .of_match_table = of_match_ptr(de2_lcd_ids),
> + },
> +};
> diff --git a/drivers/gpu/drm/sunxi/de2_crtc.h b/drivers/gpu/drm/sunxi/de2_crtc.h
> new file mode 100644
> index 0000000..efbe45d
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/de2_crtc.h
> @@ -0,0 +1,63 @@
> +#ifndef __DE2_CRTC_H__
> +#define __DE2_CRTC_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/clk.h>
> +#include <linux/reset.h>
> +#include <drm/drm_plane_helper.h>
> +
> +struct priv;
> +
> +enum de2_plane2 {
> + DE2_PRIMARY_PLANE,
> + DE2_CURSOR_PLANE,
> + DE2_VI_PLANE,
> + DE2_N_PLANES,
> +};
> +struct lcd {
> + void __iomem *mmio;
> +
> + struct device *dev;
> + struct drm_crtc crtc;
> + struct priv *priv; /* DRM/DE private data */
> +
> + short num; /* LCD number in hardware */
> + short crtc_idx; /* CRTC index in drm */
> +
> + struct clk *clk;
> + struct clk *gate;
> + struct reset_control *rstc;
> +
> + char name[16];
> +
> + struct drm_pending_vblank_event *event;
> +
> + struct drm_plane planes[DE2_N_PLANES];
> +};
> +
> +#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)
> +
> +/* in de2_de.c */
> +void de2_de_enable(struct priv *priv, int lcd_num);
> +void de2_de_disable(struct priv *priv, int lcd_num);
> +void de2_de_hw_init(struct priv *priv, int lcd_num);
> +void de2_de_panel_init(struct priv *priv, int lcd_num,
> + struct drm_display_mode *mode);
> +void de2_de_plane_disable(struct priv *priv,
> + int lcd_num, int plane_ix);
> +void de2_de_plane_update(struct priv *priv,
> + int lcd_num, int plane_ix,
> + struct drm_plane_state *state,
> + struct drm_plane_state *old_state);
Does it need to be exported?
> +
> +/* in de2_plane.c */
> +int de2_plane_init(struct drm_device *drm, struct lcd *lcd);
> +
> +#endif /* __DE2_CRTC_H__ */
> diff --git a/drivers/gpu/drm/sunxi/de2_de.c b/drivers/gpu/drm/sunxi/de2_de.c
> new file mode 100644
> index 0000000..0d8cb62
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/de2_de.c
> @@ -0,0 +1,591 @@
> +/*
> + * ALLWINNER DRM driver - Display Engine 2
> + *
> + * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
> + * 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.
> + */
> +
> +#include <asm/io.h>
> +#include <drm/drm_gem_cma_helper.h>
> +
> +#include "de2_drm.h"
> +#include "de2_crtc.h"
> +
> +static DEFINE_SPINLOCK(de_lock);
> +
> +#define DE_CLK_RATE_A83T 504000000 /* pll-de */
> +#define DE_CLK_RATE_H3 432000000 /* de */
This can be set in the DT.
> +
> +/* I/O map */
> +
> +#define DE_MOD_REG 0x0000 /* 1 bit per LCD */
> +#define DE_GATE_REG 0x0004
> +#define DE_RESET_REG 0x0008
> +#define DE_DIV_REG 0x000c /* 4 bits per LCD */
> +#define DE_SEL_REG 0x0010
> +
> +#define DE_MUX0_BASE 0x00100000
> +#define DE_MUX1_BASE 0x00200000
> +
> +/* MUX registers (addr / MUX base) */
> +#define DE_MUX_GLB_REGS 0x00000 /* global control */
> +#define DE_MUX_BLD_REGS 0x01000 /* alpha blending */
> +#define DE_MUX_CHAN_REGS 0x02000 /* VI/UI overlay channels */
> +#define DE_MUX_CHAN_SZ 0x1000 /* size of a channel */
> +#define DE_MUX_VSU_REGS 0x20000 /* VSU */
> +#define DE_MUX_GSU1_REGS 0x30000 /* GSUs */
> +#define DE_MUX_GSU2_REGS 0x40000
> +#define DE_MUX_GSU3_REGS 0x50000
> +#define DE_MUX_FCE_REGS 0xa0000 /* FCE */
> +#define DE_MUX_BWS_REGS 0xa2000 /* BWS */
> +#define DE_MUX_LTI_REGS 0xa4000 /* LTI */
> +#define DE_MUX_PEAK_REGS 0xa6000 /* PEAK */
> +#define DE_MUX_ASE_REGS 0xa8000 /* ASE */
> +#define DE_MUX_FCC_REGS 0xaa000 /* FCC */
> +#define DE_MUX_DCSC_REGS 0xb0000 /* DCSC/SMBL */
> +
> +/* global control */
> +struct de_glb {
> + u32 ctl;
> +#define DE_MUX_GLB_CTL_rt_en BIT(0)
> +#define DE_MUX_GLB_CTL_finish_irq_en BIT(4)
> +#define DE_MUX_GLB_CTL_rtwb_port BIT(12)
> + u32 status;
> + u32 dbuff;
> + u32 size;
> +};
> +
> +/* alpha blending */
> +struct de_bld {
> + u32 fcolor_ctl; /* 00 */
> + struct {
> + u32 fcolor;
> + u32 insize;
> + u32 offset;
> + u32 dum;
> + } attr[4];
> + u32 dum0[15]; /* (end of clear offset) */
> + u32 route; /* 80 */
> + u32 premultiply;
> + u32 bkcolor;
> + u32 output_size;
> + u32 bld_mode[4];
> + u32 dum1[4];
> + u32 ck_ctl; /* b0 */
> + u32 ck_cfg;
> + u32 dum2[2];
> + u32 ck_max[4]; /* c0 */
> + u32 dum3[4];
> + u32 ck_min[4]; /* e0 */
> + u32 dum4[3];
> + u32 out_ctl; /* fc */
> +};
> +
> +/* VI channel */
> +struct de_vi {
> + struct {
> + u32 attr;
> +#define VI_CFG_ATTR_en BIT(0)
> +#define VI_CFG_ATTR_fcolor_en BIT(4)
> +#define VI_CFG_ATTR_fmt_SHIFT 8
> +#define VI_CFG_ATTR_fmt_MASK GENMASK(12, 8)
> +#define VI_CFG_ATTR_ui_sel BIT(15)
> +#define VI_CFG_ATTR_top_down BIT(23)
> + u32 size;
> + u32 coord;
> +#define VI_N_PLANES 3
> + u32 pitch[VI_N_PLANES];
> + u32 top_laddr[VI_N_PLANES];
> + u32 bot_laddr[VI_N_PLANES];
> + } cfg[4];
> + u32 fcolor[4]; /* c0 */
> + u32 top_haddr[VI_N_PLANES]; /* d0 */
> + u32 bot_haddr[VI_N_PLANES]; /* dc */
> + u32 ovl_size[2]; /* e8 */
> + u32 hori[2]; /* f0 */
> + u32 vert[2]; /* f8 */
> +};
> +
> +/* UI channel */
> +struct de_ui {
> + struct {
> + u32 attr;
> +#define UI_CFG_ATTR_en BIT(0)
> +#define UI_CFG_ATTR_alpmod_SHIFT 1
> +#define UI_CFG_ATTR_alpmod_MASK GENMASK(2, 1)
> +#define UI_CFG_ATTR_fcolor_en BIT(4)
> +#define UI_CFG_ATTR_fmt_SHIFT 8
> +#define UI_CFG_ATTR_fmt_MASK GENMASK(12, 8)
> +#define UI_CFG_ATTR_top_down BIT(23)
> +#define UI_CFG_ATTR_alpha_SHIFT 24
> +#define UI_CFG_ATTR_alpha_MASK GENMASK(31, 24)
> + u32 size;
> + u32 coord;
> + u32 pitch;
> + u32 top_laddr;
> + u32 bot_laddr;
> + u32 fcolor;
> + u32 dum;
> + } cfg[4]; /* 00 */
> + u32 top_haddr; /* 80 */
> + u32 bot_haddr;
> + u32 ovl_size; /* 88 */
> +};
Please use defines instead of the structures.
> +
> +/* coordinates and sizes */
> +#define XY(x, y) (((y) << 16) | (x))
> +#define WH(w, h) (((h - 1) << 16) | (w - 1))
> +
> +/* UI video formats */
> +#define DE2_FORMAT_ARGB_8888 0
> +#define DE2_FORMAT_BGRA_8888 3
> +#define DE2_FORMAT_XRGB_8888 4
> +#define DE2_FORMAT_RGB_888 8
> +#define DE2_FORMAT_BGR_888 9
> +
> +/* VI video formats */
> +#define DE2_FORMAT_YUV422_I_YVYU 1 /* Y-V-Y-U */
> +#define DE2_FORMAT_YUV422_I_UYVY 2 /* U-Y-V-Y */
> +#define DE2_FORMAT_YUV422_I_YUYV 3 /* Y-U-Y-V */
> +#define DE2_FORMAT_YUV422_P 6 /* YYYY UU VV planar */
> +#define DE2_FORMAT_YUV420_P 10 /* YYYY U V planar */
> +
> +#define glb_read(base, member) \
> + readl_relaxed(base + offsetof(struct de_glb, member))
> +#define glb_write(base, member, data) \
> + writel_relaxed(data, base + offsetof(struct de_glb, member))
> +#define bld_read(base, member) \
> + readl_relaxed(base + offsetof(struct de_bld, member))
> +#define bld_write(base, member, data) \
> + writel_relaxed(data, base + offsetof(struct de_bld, member))
> +#define ui_read(base, member) \
> + readl_relaxed(base + offsetof(struct de_ui, member))
> +#define ui_write(base, member, data) \
> + writel_relaxed(data, base + offsetof(struct de_ui, member))
> +#define vi_read(base, member) \
> + readl_relaxed(base + offsetof(struct de_vi, member))
> +#define vi_write(base, member, data) \
> + writel_relaxed(data, base + offsetof(struct de_vi, member))
> +
> +static const struct {
> + char chan;
> + char layer;
> + char pipe;
> +} plane2layer[DE2_N_PLANES] = {
> + [DE2_PRIMARY_PLANE] = {0, 0, 0},
> + [DE2_CURSOR_PLANE] = {1, 0, 1},
> + [DE2_VI_PLANE] = {0, 1, 0},
> +};
Comments?
> +static inline void de_write(struct priv *priv, int reg, u32 data)
> +{
> + writel_relaxed(data, priv->mmio + reg);
> +}
> +
> +static inline u32 de_read(struct priv *priv, int reg)
> +{
> + return readl_relaxed(priv->mmio + reg);
> +}
> +
> +static void de_lcd_select(struct priv *priv,
> + int lcd_num,
> + void __iomem *mux_o)
> +{
> + u32 data;
> +
> + /* select the LCD */
> + data = de_read(priv, DE_SEL_REG);
> + data &= ~1;
> + de_write(priv, DE_SEL_REG, data);
> +
> + /* double register switch */
> + glb_write(mux_o + DE_MUX_GLB_REGS, dbuff, 1);
> +}
> +
> +void de2_de_plane_update(struct priv *priv,
> + int lcd_num, int plane_ix,
> + struct drm_plane_state *state,
> + struct drm_plane_state *old_state)
> +{
> + struct drm_framebuffer *fb = state->fb;
> + struct drm_gem_cma_object *gem;
> + void __iomem *mux_o = priv->mmio;
> + void __iomem *chan_o;
> + u32 size = WH(state->crtc_w, state->crtc_h);
> + u32 coord;
> + u32 screen_size;
> + u32 data, fcolor;
> + u32 ui_sel, alpha_glob;
> + int chan, layer, x, y;
> + unsigned fmt;
> + unsigned long flags;
> +
> + chan = plane2layer[plane_ix].chan;
> + layer = plane2layer[plane_ix].layer;
> +
> + mux_o += (lcd_num == 0) ? DE_MUX0_BASE : DE_MUX1_BASE;
> + chan_o = mux_o;
> + chan_o += DE_MUX_CHAN_REGS + DE_MUX_CHAN_SZ * chan;
> +
> + x = state->crtc_x >= 0 ? state->crtc_x : 0;
> + y = state->crtc_y >= 0 ? state->crtc_y : 0;
> + coord = XY(x, y);
> +
> + /* handle the cursor move */
> + if (plane_ix == DE2_CURSOR_PLANE
> + && fb == old_state->fb) {
> + spin_lock_irqsave(&de_lock, flags);
> + de_lcd_select(priv, lcd_num, mux_o);
> + if (chan == 0)
> + vi_write(chan_o, cfg[layer].coord, coord);
> + else
> + ui_write(chan_o, cfg[layer].coord, coord);
> + spin_unlock_irqrestore(&de_lock, flags);
> + return;
> + }
> +
> + gem = drm_fb_cma_get_gem_obj(fb, 0);
> +
> + ui_sel = alpha_glob = 0;
> + switch (fb->pixel_format) {
> + case DRM_FORMAT_ARGB8888:
> + fmt = DE2_FORMAT_ARGB_8888;
> + ui_sel = VI_CFG_ATTR_ui_sel;
> + break;
> + case DRM_FORMAT_BGRA8888:
> + fmt = DE2_FORMAT_BGRA_8888;
> + ui_sel = VI_CFG_ATTR_ui_sel;
> + break;
> + case DRM_FORMAT_XRGB8888:
> + fmt = DE2_FORMAT_XRGB_8888;
> + ui_sel = VI_CFG_ATTR_ui_sel;
> + alpha_glob = (1 << UI_CFG_ATTR_alpmod_SHIFT) |
> + (0xff << UI_CFG_ATTR_alpha_SHIFT);
> + break;
> + case DRM_FORMAT_RGB888:
> + fmt = DE2_FORMAT_RGB_888;
> + ui_sel = VI_CFG_ATTR_ui_sel;
> + break;
> + case DRM_FORMAT_BGR888:
> + fmt = DE2_FORMAT_BGR_888;
> + ui_sel = VI_CFG_ATTR_ui_sel;
> + break;
> + case DRM_FORMAT_YUYV:
> + fmt = DE2_FORMAT_YUV422_I_YUYV;
> + break;
> + case DRM_FORMAT_YVYU:
> + fmt = DE2_FORMAT_YUV422_I_YVYU;
> + break;
> + case DRM_FORMAT_YUV422:
> + fmt = DE2_FORMAT_YUV422_P;
> + break;
> + case DRM_FORMAT_YUV420:
> + fmt = DE2_FORMAT_YUV420_P;
> + break;
> + case DRM_FORMAT_UYVY:
> + fmt = DE2_FORMAT_YUV422_I_UYVY;
> + break;
> + default:
> + pr_err("format %.4s not yet treated\n",
> + (char *) &fb->pixel_format);
> + return;
> + }
> +
> + spin_lock_irqsave(&de_lock, flags);
> +
> + screen_size = plane_ix == DE2_PRIMARY_PLANE ?
> + size :
> + glb_read(mux_o + DE_MUX_GLB_REGS, size);
> +
> + /* prepare the activation of alpha blending (1 bit per plane) */
> + fcolor = bld_read(mux_o + DE_MUX_BLD_REGS, fcolor_ctl)
> + | (0x100 << plane2layer[plane_ix].pipe);
> +
> + de_lcd_select(priv, lcd_num, mux_o);
> +
> + if (chan == 0) { /* VI channel */
> + int i;
> +
> + data = VI_CFG_ATTR_en | (fmt << VI_CFG_ATTR_fmt_SHIFT) |
> + ui_sel;
> + vi_write(chan_o, cfg[layer].attr, data);
> + vi_write(chan_o, cfg[layer].size, size);
> + vi_write(chan_o, cfg[layer].coord, coord);
> + for (i = 0; i < VI_N_PLANES; i++) {
> + vi_write(chan_o, cfg[layer].pitch[i],
> + fb->pitches[i] ? fb->pitches[i] :
> + fb->pitches[0]);
> + vi_write(chan_o, cfg[layer].top_laddr[i],
> + gem->paddr + fb->offsets[i]);
> + vi_write(chan_o, fcolor[layer], 0xff000000);
> + }
> + if (layer == 0)
> + vi_write(chan_o, ovl_size[0], screen_size);
> +
> + } else { /* UI channel */
> + data = UI_CFG_ATTR_en | (fmt << UI_CFG_ATTR_fmt_SHIFT) |
> + alpha_glob;
> + ui_write(chan_o, cfg[layer].attr, data);
> + ui_write(chan_o, cfg[layer].size, size);
> + ui_write(chan_o, cfg[layer].coord, coord);
> + ui_write(chan_o, cfg[layer].pitch, fb->pitches[0]);
> + ui_write(chan_o, cfg[layer].top_laddr,
> + gem->paddr + fb->offsets[0]);
> + if (layer == 0)
> + ui_write(chan_o, ovl_size, screen_size);
> + }
> + bld_write(mux_o + DE_MUX_BLD_REGS, fcolor_ctl, fcolor);
> +
> + spin_unlock_irqrestore(&de_lock, flags);
> +}
Splitting that into functions would make it a bit more trivial and
readable.
> +void de2_de_plane_disable(struct priv *priv,
> + int lcd_num, int plane_ix)
> +{
> + void __iomem *mux_o = priv->mmio;
> + void __iomem *chan_o;
> + u32 fcolor;
> + int chan, layer, chan_disable = 0;
> + unsigned long flags;
> +
> + chan = plane2layer[plane_ix].chan;
> + layer = plane2layer[plane_ix].layer;
> +
> + mux_o += (lcd_num == 0) ? DE_MUX0_BASE : DE_MUX1_BASE;
> + chan_o = mux_o;
> + chan_o += DE_MUX_CHAN_REGS + DE_MUX_CHAN_SZ * chan;
> +
> + /* (only 2 layers) */
> + if (chan == 0) {
> + if (vi_read(chan_o, cfg[1 - layer].attr) == 0)
> + chan_disable = 1;
> + } else {
> + if (ui_read(chan_o, cfg[1 - layer].attr) == 0)
> + chan_disable = 1;
> + }
> +
> + spin_lock_irqsave(&de_lock, flags);
> +
> + fcolor = bld_read(mux_o + DE_MUX_BLD_REGS, fcolor_ctl);
> +
> + de_lcd_select(priv, lcd_num, mux_o);
> +
> + if (chan == 0)
> + vi_write(chan_o, cfg[layer].attr, 0);
> + else
> + ui_write(chan_o, cfg[layer].attr, 0);
> +
> + if (chan_disable)
> + bld_write(mux_o + DE_MUX_BLD_REGS, fcolor_ctl,
> + fcolor & ~(0x100 << plane2layer[plane_ix].pipe));
> +
> + spin_unlock_irqrestore(&de_lock, flags);
> +}
Can't you just disable it?
> +void de2_de_panel_init(struct priv *priv, int lcd_num,
> + struct drm_display_mode *mode)
> +{
> + void __iomem *mux_o = priv->mmio;
> + u32 size = WH(mode->hdisplay, mode->vdisplay);
> + unsigned i;
> + unsigned long flags;
> +
> + mux_o += (lcd_num == 0) ? DE_MUX0_BASE : DE_MUX1_BASE;
> +
> + DRM_DEBUG_DRIVER("%dx%d\n", mode->hdisplay, mode->vdisplay);
> +
> + spin_lock_irqsave(&de_lock, flags);
> +
> + de_lcd_select(priv, lcd_num, mux_o);
> +
> + glb_write(mux_o + DE_MUX_GLB_REGS, size, size);
> +
> + /* set alpha blending */
> + for (i = 0; i < 4; i++) {
> + bld_write(mux_o + DE_MUX_BLD_REGS, attr[i].fcolor, 0xff000000);
> + bld_write(mux_o + DE_MUX_BLD_REGS, attr[i].insize, size);
> + }
> + bld_write(mux_o + DE_MUX_BLD_REGS, output_size, size);
> + bld_write(mux_o + DE_MUX_BLD_REGS, out_ctl,
> + mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0);
> +
> + spin_unlock_irqrestore(&de_lock, flags);
> +}
> +
> +void de2_de_enable(struct priv *priv, int lcd_num)
> +{
> + void __iomem *mux_o = priv->mmio;
> + unsigned chan, i;
> + u32 size = WH(1920, 1080);
> + u32 data;
> + unsigned long flags;
> +
> + DRM_DEBUG_DRIVER("lcd %d\n", lcd_num);
> +
> + de_write(priv, DE_RESET_REG,
> + de_read(priv, DE_RESET_REG) |
> + (lcd_num == 0 ? 1 : 4));
> + data = 1 << lcd_num; /* 1 bit / lcd */
> + de_write(priv, DE_GATE_REG,
> + de_read(priv, DE_GATE_REG) | data);
> + de_write(priv, DE_MOD_REG,
> + de_read(priv, DE_MOD_REG) | data);
> +
> + mux_o += (lcd_num == 0) ? DE_MUX0_BASE : DE_MUX1_BASE;
> +
> + spin_lock_irqsave(&de_lock, flags);
> +
> + /* select the LCD */
> + data = de_read(priv, DE_SEL_REG);
> + if (lcd_num == 0)
> + data &= ~1;
> + else
> + data |= 1;
> + de_write(priv, DE_SEL_REG, data);
> +
> + /* start init */
> + glb_write(mux_o + DE_MUX_GLB_REGS, ctl,
> + DE_MUX_GLB_CTL_rt_en | DE_MUX_GLB_CTL_rtwb_port);
> + glb_write(mux_o + DE_MUX_GLB_REGS, status, 0);
> + glb_write(mux_o + DE_MUX_GLB_REGS, dbuff, 1); /* dble reg switch */
> + glb_write(mux_o + DE_MUX_GLB_REGS, size, size);
> +
> + /* clear the VI/UI channels */
> + for (chan = 0; chan < 4; chan++) {
> + void __iomem *chan_o = mux_o + DE_MUX_CHAN_REGS +
> + DE_MUX_CHAN_SZ * chan;
> +
> + memset_io(chan_o, 0, chan == 0 ?
> + sizeof(struct de_vi) : sizeof(struct de_ui));
> +
> + /* only 1 VI and 1 UI in lcd1 */
> + if (chan == 2 && lcd_num == 1)
> + break;
> + }
> +
> + /* clear and set alpha blending */
> + memset_io(mux_o + DE_MUX_BLD_REGS, 0, offsetof(struct de_bld, dum0));
> + bld_write(mux_o + DE_MUX_BLD_REGS, fcolor_ctl, 0x00000101);
> + /* fcolor for primary */
> +
> + /* prepare route for planes */
> + data = 0;
> + for (i = 0; i < DE2_N_PLANES; i++)
> + data |= plane2layer[i].chan << (plane2layer[i].pipe * 4);
> + bld_write(mux_o + DE_MUX_BLD_REGS, route, data);
> +
> + bld_write(mux_o + DE_MUX_BLD_REGS, premultiply, 0);
> + bld_write(mux_o + DE_MUX_BLD_REGS, bkcolor, 0xff000000);
> + bld_write(mux_o + DE_MUX_BLD_REGS, bld_mode[0], 0x03010301);
> + /* SRCOVER */
> + bld_write(mux_o + DE_MUX_BLD_REGS, bld_mode[1], 0x03010301);
> + bld_write(mux_o + DE_MUX_BLD_REGS, out_ctl, 0);
> +
> + /* disable the enhancements */
> + writel_relaxed(0, mux_o + DE_MUX_VSU_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_GSU1_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_GSU2_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_GSU3_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_FCE_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_BWS_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_LTI_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_PEAK_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_ASE_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_FCC_REGS);
> + writel_relaxed(0, mux_o + DE_MUX_DCSC_REGS);
> +
> + spin_unlock_irqrestore(&de_lock, flags);
> +}
> +
> +void de2_de_disable(struct priv *priv, int lcd_num)
> +{
> + u32 data;
> +
> + data = ~(1 << lcd_num);
> + de_write(priv, DE_MOD_REG,
> + de_read(priv, DE_MOD_REG) & data);
> + de_write(priv, DE_GATE_REG,
> + de_read(priv, DE_GATE_REG) & data);
> + de_write(priv, DE_RESET_REG,
> + de_read(priv, DE_RESET_REG) & data);
> +}
> +
> +int de2_de_init(struct priv *priv, struct device *dev)
> +{
> + struct resource *res;
> + int ret;
> +
> + DRM_DEBUG_DRIVER("\n");
> +
> + res = platform_get_resource(to_platform_device(dev),
> + IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "failed to get memory resource\n");
> + return -EINVAL;
> + }
> +
> + priv->mmio = devm_ioremap_resource(dev, res);
> + if (IS_ERR(priv->mmio)) {
> + dev_err(dev, "failed to map registers\n");
> + return PTR_ERR(priv->mmio);
> + }
> +
> + priv->gate = devm_clk_get(dev, "gate"); /* optional */
Error checking
> +
> + priv->clk = devm_clk_get(dev, "clock");
> + if (IS_ERR(priv->clk)) {
> + dev_err(dev, "video clock err %d\n", (int) PTR_ERR(priv->clk));
> + return PTR_ERR(priv->clk);
> + }
> +
> + priv->rstc = devm_reset_control_get_optional(dev, NULL);
> +
> + if (!IS_ERR(priv->rstc)) {
> + ret = reset_control_deassert(priv->rstc);
> + if (ret) {
> + dev_err(dev, "reset deassert err %d\n", ret);
> + return ret;
> + }
> + }
> +
> + if (!IS_ERR(priv->gate)) {
> + ret = clk_prepare_enable(priv->gate);
> + if (ret)
> + goto err_gate;
> + }
> +
> + ret = clk_prepare_enable(priv->clk);
> + if (ret)
> + goto err_enable;
> + if (priv->soc_type == SOC_A83T)
> + clk_set_rate(priv->clk, DE_CLK_RATE_A83T);
> + else
> + clk_set_rate(priv->clk, DE_CLK_RATE_H3);
> +
> + /* set the A83T clock divider = 500 / 250 */
> + if (priv->soc_type == SOC_A83T)
> + de_write(priv, DE_DIV_REG,
> + 0x00000011); /* div = 2 for both LCDs */
> +
> + return 0;
> +
> +err_enable:
> + clk_disable_unprepare(priv->gate);
> +err_gate:
> + if (!IS_ERR(priv->rstc))
> + reset_control_assert(priv->rstc);
> + return ret;
> +}
> +
> +void de2_de_cleanup(struct priv *priv)
> +{
> + clk_disable_unprepare(priv->clk);
> + clk_disable_unprepare(priv->gate);
> + if (!IS_ERR(priv->rstc))
> + reset_control_assert(priv->rstc);
> +}
> diff --git a/drivers/gpu/drm/sunxi/de2_drm.h b/drivers/gpu/drm/sunxi/de2_drm.h
> new file mode 100644
> index 0000000..7bb966c
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/de2_drm.h
> @@ -0,0 +1,47 @@
> +#ifndef __DE2_DRM_H__
> +#define __DE2_DRM_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/clk.h>
> +#include <linux/reset.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_fb_cma_helper.h>
> +
> +struct lcd;
> +
> +#define N_LCDS 2
> +
> +/* SoC types */
> +#define SOC_A83T 0
> +#define SOC_H3 1
> +
> +struct priv {
> + void __iomem *mmio;
> + struct clk *clk;
> + struct clk *gate;
> + struct reset_control *rstc;
> +
> + int soc_type;
> +
> + struct drm_fbdev_cma *fbdev;
> +
> + struct lcd *lcds[N_LCDS];
> +};
> +
> +/* in de2_crtc.c */
> +int de2_enable_vblank(struct drm_device *drm, unsigned crtc);
> +void de2_disable_vblank(struct drm_device *drm, unsigned crtc);
> +extern struct platform_driver de2_lcd_platform_driver;
> +
> +/* in de2_de.c */
> +int de2_de_init(struct priv *priv, struct device *dev);
> +void de2_de_cleanup(struct priv *priv);
> +
> +#endif /* __DE2_DRM_H__ */
> diff --git a/drivers/gpu/drm/sunxi/de2_drv.c b/drivers/gpu/drm/sunxi/de2_drv.c
> new file mode 100644
> index 0000000..5daa15c
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/de2_drv.c
> @@ -0,0 +1,378 @@
> +/*
> + * Allwinner DRM driver - DE2 DRM driver
> + *
> + * 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/pm.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/component.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +
> +#include "de2_drm.h"
> +
> +#define DRIVER_NAME "sunxi-de2"
> +#define DRIVER_DESC "Allwinner DRM DE2"
> +#define DRIVER_DATE "20161001"
> +#define DRIVER_MAJOR 1
> +#define DRIVER_MINOR 0
> +
> +static struct of_device_id de2_drm_of_match[] = {
> + { .compatible = "allwinner,sun8i-a83t-display-engine",
> + .data = (void *) SOC_A83T },
> + { .compatible = "allwinner,sun8i-h3-display-engine",
> + .data = (void *) SOC_H3 },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, de2_drm_of_match);
> +
> +static void de2_fb_output_poll_changed(struct drm_device *drm)
> +{
> + struct priv *priv = drm->dev_private;
> +
> + if (priv->fbdev)
> + drm_fbdev_cma_hotplug_event(priv->fbdev);
> +}
> +
> +static const struct drm_mode_config_funcs de2_mode_config_funcs = {
> + .fb_create = drm_fb_cma_create,
> + .output_poll_changed = de2_fb_output_poll_changed,
> + .atomic_check = drm_atomic_helper_check,
> + .atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +/*
> + * DRM operations:
> + */
> +static void de2_lastclose(struct drm_device *drm)
> +{
> + struct priv *priv = drm->dev_private;
> +
> + if (priv->fbdev)
> + drm_fbdev_cma_restore_mode(priv->fbdev);
> +}
> +
> +static const struct file_operations de2_fops = {
> + .owner = THIS_MODULE,
> + .open = drm_open,
> + .release = drm_release,
> + .unlocked_ioctl = drm_ioctl,
> + .poll = drm_poll,
> + .read = drm_read,
> + .llseek = no_llseek,
> + .mmap = drm_gem_cma_mmap,
> +};
> +
> +static struct drm_driver de2_drm_driver = {
> + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
> + DRIVER_ATOMIC,
> + .lastclose = de2_lastclose,
> + .get_vblank_counter = drm_vblank_no_hw_counter,
> + .enable_vblank = de2_enable_vblank,
> + .disable_vblank = de2_disable_vblank,
> + .gem_free_object = drm_gem_cma_free_object,
> + .gem_vm_ops = &drm_gem_cma_vm_ops,
> + .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> + .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> + .gem_prime_import = drm_gem_prime_import,
> + .gem_prime_export = drm_gem_prime_export,
> + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
> + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
> + .gem_prime_vmap = drm_gem_cma_prime_vmap,
> + .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
> + .gem_prime_mmap = drm_gem_cma_prime_mmap,
> + .dumb_create = drm_gem_cma_dumb_create,
> + .dumb_map_offset = drm_gem_cma_dumb_map_offset,
> + .dumb_destroy = drm_gem_dumb_destroy,
> + .fops = &de2_fops,
> + .name = DRIVER_NAME,
> + .desc = DRIVER_DESC,
> + .date = DRIVER_DATE,
> + .major = DRIVER_MAJOR,
> + .minor = DRIVER_MINOR,
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +/*
> + * Power management
> + */
> +static int de2_pm_suspend(struct device *dev)
> +{
> + struct drm_device *drm = dev_get_drvdata(dev);
> +
> + drm_kms_helper_poll_disable(drm);
> + return 0;
> +}
> +
> +static int de2_pm_resume(struct device *dev)
> +{
> + struct drm_device *drm = dev_get_drvdata(dev);
> +
> + drm_kms_helper_poll_enable(drm);
> + return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops de2_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(de2_pm_suspend, de2_pm_resume)
> +};
Why do you need that? How did you test it? There's no runtime_pm calls
in your kernel.
> +/*
> + * Platform driver
> + */
> +
> +static int de2_drm_bind(struct device *dev)
> +{
> + struct drm_device *drm;
> + struct priv *priv;
> + int ret;
> +
> + drm = drm_dev_alloc(&de2_drm_driver, dev);
> + if (!drm)
> + return -ENOMEM;
> +
> + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> + if (!priv) {
> + dev_err(dev, "failed to allocate private area\n");
> + ret = -ENOMEM;
> + goto out1;
> + }
> +
> + dev_set_drvdata(dev, drm);
> + drm->dev_private = priv;
> +
> + drm_mode_config_init(drm);
> + drm->mode_config.min_width = 32; /* needed for cursor */
> + drm->mode_config.min_height = 32;
> + drm->mode_config.max_width = 1920;
> + drm->mode_config.max_height = 1080;
> + drm->mode_config.funcs = &de2_mode_config_funcs;
> +
> + drm->irq_enabled = true;
> +
> + /* initialize the display engine */
> + priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;
> + ret = de2_de_init(priv, dev);
> + if (ret)
> + goto out2;
> +
> + /* start the subdevices */
> + ret = component_bind_all(dev, drm);
> + if (ret < 0)
> + goto out2;
> +
> + ret = drm_dev_register(drm, 0);
> + if (ret < 0)
> + goto out3;
> +
> + DRM_DEBUG_DRIVER("%d crtcs %d connectors\n",
> + drm->mode_config.num_crtc,
> + drm->mode_config.num_connector);
> +
> + ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
> + if (ret < 0)
> + dev_warn(dev, "failed to initialize vblank\n");
> +
> + drm_mode_config_reset(drm);
> +
> + priv->fbdev = drm_fbdev_cma_init(drm,
> + 32, /* bpp */
> + drm->mode_config.num_crtc,
> + drm->mode_config.num_connector);
> + if (IS_ERR(priv->fbdev)) {
> + ret = PTR_ERR(priv->fbdev);
> + priv->fbdev = NULL;
> + goto out4;
> + }
> +
> + drm_kms_helper_poll_init(drm);
> +
> + return 0;
> +
> +out4:
> + drm_dev_unregister(drm);
> +out3:
> + component_unbind_all(dev, drm);
> +out2:
> + kfree(priv);
> +out1:
> + drm_dev_unref(drm);
> + return ret;
> +}
> +
> +static void de2_drm_unbind(struct device *dev)
> +{
> + struct drm_device *drm = dev_get_drvdata(dev);
> + struct priv *priv = drm->dev_private;
> +
> + if (priv)
> + drm_fbdev_cma_fini(priv->fbdev);
> + drm_kms_helper_poll_fini(drm);
> +
> + drm_dev_unregister(drm);
> + drm_vblank_cleanup(drm);
> +
> + drm_mode_config_cleanup(drm);
> +
> + component_unbind_all(dev, drm);
> +
> + if (priv) {
> + de2_de_cleanup(priv);
> + kfree(priv);
> + }
> +
> + drm_dev_unref(drm);
> +}
> +
> +static const struct component_master_ops de2_drm_comp_ops = {
> + .bind = de2_drm_bind,
> + .unbind = de2_drm_unbind,
> +};
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> + return dev->of_node == data;
> +}
> +
> +static int de2_drm_add_components(struct device *dev,
> + int (*compare_of)(struct device *, void *),
> + const struct component_master_ops *m_ops)
> +{
> + struct device_node *ep, *port, *remote;
> + struct component_match *match = NULL;
> + int i;
> +
> + if (!dev->of_node)
> + return -EINVAL;
> +
> + /* bind the CRTCs */
> + for (i = 0; ; i++) {
> + port = of_parse_phandle(dev->of_node, "ports", i);
> + if (!port)
> + break;
> +
> + if (!of_device_is_available(port->parent)) {
> + of_node_put(port);
> + continue;
> + }
> +
> + component_match_add(dev, &match, compare_of, port->parent);
> + of_node_put(port);
> + }
> +
> + if (i == 0) {
> + dev_err(dev, "missing 'ports' property\n");
> + return -ENODEV;
> + }
> + if (!match) {
> + dev_err(dev, "no available port\n");
> + return -ENODEV;
> + }
> +
> + /* bind the encoders/connectors */
> + for (i = 0; ; i++) {
> + port = of_parse_phandle(dev->of_node, "ports", i);
> + if (!port)
> + break;
> +
> + if (!of_device_is_available(port->parent)) {
> + of_node_put(port);
> + continue;
> + }
> +
> + for_each_child_of_node(port, ep) {
> + remote = of_graph_get_remote_port_parent(ep);
> + if (!remote || !of_device_is_available(remote)) {
> + of_node_put(remote);
> + continue;
> + }
> + if (!of_device_is_available(remote->parent)) {
> + dev_warn(dev,
> + "parent device of %s is not available\n",
> + remote->full_name);
> + of_node_put(remote);
> + continue;
> + }
> +
> + component_match_add(dev, &match, compare_of, remote);
> + of_node_put(remote);
> + }
> + of_node_put(port);
> + }
> +
> + return component_master_add_with_match(dev, m_ops, match);
> +}
> +
> +static int de2_drm_probe(struct platform_device *pdev)
> +{
> + int ret;
> +
> + ret = de2_drm_add_components(&pdev->dev,
> + compare_of,
> + &de2_drm_comp_ops);
> + if (ret == -EINVAL)
> + ret = -ENXIO;
> + return ret;
> +}
> +
> +static int de2_drm_remove(struct platform_device *pdev)
> +{
> + component_master_del(&pdev->dev, &de2_drm_comp_ops);
> +
> + return 0;
> +}
> +
> +static struct platform_driver de2_drm_platform_driver = {
> + .probe = de2_drm_probe,
> + .remove = de2_drm_remove,
> + .driver = {
> + .name = DRIVER_NAME,
> + .pm = &de2_pm_ops,
> + .of_match_table = de2_drm_of_match,
> + },
> +};
> +
> +static int __init de2_drm_init(void)
> +{
> + int ret;
> +
> +/* uncomment to activate the drm traces at startup time */
> +/* drm_debug = DRM_UT_CORE | DRM_UT_DRIVER | DRM_UT_KMS |
> + DRM_UT_PRIME | DRM_UT_ATOMIC; */
That's useless.
> + DRM_DEBUG_DRIVER("\n");
> +
> + ret = platform_driver_register(&de2_lcd_platform_driver);
> + if (ret < 0)
> + return ret;
> +
> + ret = platform_driver_register(&de2_drm_platform_driver);
> + if (ret < 0)
> + platform_driver_unregister(&de2_lcd_platform_driver);
> +
> + return ret;
> +}
And that really shouldn't be done that way.
> +static void __exit de2_drm_fini(void)
> +{
> + platform_driver_unregister(&de2_lcd_platform_driver);
> + platform_driver_unregister(&de2_drm_platform_driver);
> +}
> +
> +module_init(de2_drm_init);
> +module_exit(de2_drm_fini);
> +
> +MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
> +MODULE_DESCRIPTION("Allwinner DE2 DRM Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/sunxi/de2_plane.c b/drivers/gpu/drm/sunxi/de2_plane.c
> new file mode 100644
> index 0000000..b338684
> --- /dev/null
> +++ b/drivers/gpu/drm/sunxi/de2_plane.c
> @@ -0,0 +1,119 @@
> +/*
> + * Allwinner DRM driver - DE2 planes
> + *
> + * 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 <drm/drm_atomic_helper.h>
> +#include <drm/drm_plane_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "de2_drm.h"
> +#include "de2_crtc.h"
> +
> +/* plane formats */
> +static const uint32_t ui_formats[] = {
> + DRM_FORMAT_ARGB8888,
> + DRM_FORMAT_BGRA8888,
> + DRM_FORMAT_XRGB8888,
> + DRM_FORMAT_RGB888,
> + DRM_FORMAT_BGR888,
> +};
> +
> +static const uint32_t vi_formats[] = {
> + DRM_FORMAT_XRGB8888,
> + DRM_FORMAT_YUYV,
> + DRM_FORMAT_YVYU,
> + DRM_FORMAT_YUV422,
> + DRM_FORMAT_YUV420,
> + DRM_FORMAT_UYVY,
> + DRM_FORMAT_BGRA8888,
> + DRM_FORMAT_RGB888,
> + DRM_FORMAT_BGR888,
> +};
> +
> +static void de2_plane_disable(struct drm_plane *plane,
> + struct drm_plane_state *old_state)
> +{
> + struct drm_crtc *crtc = old_state->crtc;
> + struct lcd *lcd = crtc_to_lcd(crtc);
> + int plane_num = plane - lcd->planes;
> +
> + de2_de_plane_disable(lcd->priv, lcd->num, plane_num);
> +}
> +
> +static void de2_plane_update(struct drm_plane *plane,
> + struct drm_plane_state *old_state)
> +{
> + struct drm_plane_state *state = plane->state;
> + struct drm_crtc *crtc = state->crtc;
> + struct lcd *lcd = crtc_to_lcd(crtc);
> + struct drm_framebuffer *fb = state->fb;
> + int plane_num = plane - lcd->planes;
> +
> + if (!crtc || !fb) {
> + DRM_DEBUG_DRIVER("no crtc/fb\n");
> + return;
> + }
> +
> + de2_de_plane_update(lcd->priv, lcd->num, plane_num,
> + state, old_state);
> +}
> +
> +static const struct drm_plane_helper_funcs plane_helper_funcs = {
> + .atomic_disable = de2_plane_disable,
> + .atomic_update = de2_plane_update,
> +};
> +
> +static const struct drm_plane_funcs plane_funcs = {
> + .update_plane = drm_atomic_helper_update_plane,
> + .disable_plane = drm_atomic_helper_disable_plane,
> + .destroy = drm_plane_cleanup,
> + .reset = drm_atomic_helper_plane_reset,
> + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +};
> +
> +static int de2_one_plane_init(struct drm_device *drm,
> + struct drm_plane *plane,
> + int type, int possible_crtcs,
> + const uint32_t *formats,
> + int nformats)
> +{
> + int ret;
> +
> + ret = drm_universal_plane_init(drm, plane, possible_crtcs,
> + &plane_funcs,
> + formats, nformats, type, NULL);
> + if (ret >= 0)
> + drm_plane_helper_add(plane, &plane_helper_funcs);
> +
> + return ret;
> +}
> +
> +int de2_plane_init(struct drm_device *drm, struct lcd *lcd)
> +{
> + int ret, possible_crtcs = 1 << lcd->crtc_idx;
> +
> + ret = de2_one_plane_init(drm, &lcd->planes[DE2_PRIMARY_PLANE],
> + DRM_PLANE_TYPE_PRIMARY, possible_crtcs,
> + ui_formats, ARRAY_SIZE(ui_formats));
> + if (ret >= 0)
> + ret = de2_one_plane_init(drm, &lcd->planes[DE2_CURSOR_PLANE],
> + DRM_PLANE_TYPE_CURSOR, possible_crtcs,
> + ui_formats, ARRAY_SIZE(ui_formats));
Nothing looks really special about that cursor plane. Any reasion not
to make it an overlay?
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161024/479b8a6a/attachment-0001.sig>
^ permalink raw reply
* [PATCH] ARM: dt: sun8i-h3: Add sunxi-sid to dts for sun8i-h3
From: Maxime Ripard @ 2016-10-24 14:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161023082631.GA28485@Red>
On Sun, Oct 23, 2016 at 10:26:31AM +0200, LABBE Corentin wrote:
> On Thu, Oct 20, 2016 at 10:36:54PM +0200, Maxime Ripard wrote:
> > On Wed, Oct 19, 2016 at 09:40:16AM +0200, LABBE Corentin wrote:
> > > On Wed, Oct 05, 2016 at 12:21:30PM +0200, Jean-Francois Moine wrote:
> > > > On Wed, 5 Oct 2016 11:48:24 +0200
> > > > Corentin Labbe <clabbe.montjoie@gmail.com> wrote:
> > > >
> > > > > This patch add support for the sunxi-sid driver to the device tree for sun8i-h3.
> > > > >
> > > > > Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
> > > > > ---
> > > > > arch/arm/boot/dts/sun8i-h3.dtsi | 5 +++++
> > > > > 1 file changed, 5 insertions(+)
> > > > >
> > > > > diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
> > > > > index 9f58bb4..abfd29c 100644
> > > > > --- a/arch/arm/boot/dts/sun8i-h3.dtsi
> > > > > +++ b/arch/arm/boot/dts/sun8i-h3.dtsi
> > > > > @@ -211,6 +211,11 @@
> > > > > #size-cells = <0>;
> > > > > };
> > > > >
> > > > > + sid: eeprom at 01c14200 {
> > > > > + compatible = "allwinner,sun7i-a20-sid";
> > > > > + reg = <0x01c14200 0x200>;
> > > >
> > > > The datasheet says 1Kb starting at 0x01c14000.
> > > > Is there any reason to reduce the area and to shift the offset?
> > > >
> > >
> > > According to http://linux-sunxi.org/SID_Register_Guide "For
> > > Allwinner A83T and H3 the SID address space starts at 0x01c14000,
> > > and the e-fuses are at offset 0x200".
> > >
> > > So I use this offset, since the sunxi_sid driver need the base
> > > address of e-fuses.
> > >
> > > The easiest solution is to use 0x01c14200 since the other part of
> > > sid is not used and not known (A83T/H3 user manual doesnt give any
> > > information on all sid space, worse for A64 which reference SID only
> > > in memory map).
> > >
> > > So probably for H3/A64/A83T, there will never any usage of the rest
> > > of the SID address space.
> >
> > And since we can't know that, and we have to maintain the DT ABI,
> > using the whole address map and an offset, with a new compatible, is
> > definetely the safest thing to do.
> >
>
> I have two way of doing it, which one do you prefer ?
>
> - Adding two optionnal properties: efuses-offset and efuses-size defaulting to 0 and resourcesize.
> - Adding a subnode called efuses with its own reg=<>
>
> The first one is easy and didnt need any work on previous DT entries.
I'd prefer to have a compatible, and the size and/or offset of the
efuses associated to it.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161024/d93d1bab/attachment-0001.sig>
^ permalink raw reply
* [GIT PULL] i.MX fixes for 4.9
From: Shawn Guo @ 2016-10-24 14:06 UTC (permalink / raw)
To: linux-arm-kernel
The following changes since commit 1001354ca34179f3db924eb66672442a173147dc:
Linux 4.9-rc1 (2016-10-15 12:17:50 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux.git tags/imx-fixes-4.9
for you to fetch changes up to 4edd601c5a9c5094daa714e65063e623826f3bcc:
ARM: imx: mach-imx6q: Fix the PHY ID mask for AR8031 (2016-10-24 21:26:01 +0800)
----------------------------------------------------------------
The i.MX fixes for 4.9:
- A couple of patches from Fabio to fix the GPC power domain regression
which is caused by PM Domain core change 0159ec670763dd
("PM / Domains: Verify the PM domain is present when adding a
provider"), and a related kernel crash seen with multi_v7_defconfig
build.
- Correct the PHY ID mask for AR8031 to match phy driver code.
- Apply new added timer erratum A008585 for LS1043A and LS2080A SoC.
- Correct vf610 global timer IRQ flag to avoid warning from gic driver
after commit 992345a58e0c ("irqchip/gic: WARN if setting the
interrupt type for a PPI fails").
----------------------------------------------------------------
Fabio Estevam (3):
ARM: imx: gpc: Initialize all power domains
ARM: imx: gpc: Fix the imx_gpc_genpd_init() error path
ARM: imx: mach-imx6q: Fix the PHY ID mask for AR8031
Scott Wood (1):
arm64: dts: Add timer erratum property for LS2080A and LS1043A
Stefan Agner (1):
ARM: dts: vf610: fix IRQ flag of global timer
arch/arm/boot/dts/vf500.dtsi | 2 +-
arch/arm/mach-imx/gpc.c | 15 ++++++++++++---
arch/arm/mach-imx/mach-imx6q.c | 2 +-
arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi | 1 +
arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi | 1 +
5 files changed, 16 insertions(+), 5 deletions(-)
^ permalink raw reply
* [GIT PULL] SoCFPGA DTS updates for v4.10, part 1
From: Dinh Nguyen @ 2016-10-24 14:16 UTC (permalink / raw)
To: linux-arm-kernel
Hi Arnd, Kevin, Olof:
Please pull in these DTS updates for SoCFPGA for v4.10.
Thanks,
Dinh
The following changes since commit 1001354ca34179f3db924eb66672442a173147dc:
Linux 4.9-rc1 (2016-10-15 12:17:50 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/dinguyen/linux.git tags/socfpga_dts_for_v4.10_part_1
for you to fetch changes up to c96f5919e6b0d132aa9afe9f1adc872fc107d5bb:
ARM: dts: socfpga: socrates: enable qspi (2016-10-18 22:18:14 -0500)
----------------------------------------------------------------
SoCFPGA DTS update for v4.10, part 1
- Add a Macnica sodia board
- Add support for the Arria10 System resource device
- Add support for the Arria10 LEDs
- Add QSPI to the socrates board
- Update L2 cache settings, enabling arm,shared-override
----------------------------------------------------------------
Dinh Nguyen (1):
ARM: dts: socfpga: enable arm,shared-override in the pl310
Marek Vasut (1):
ARM: dts: socfpga: Add new MCVEVK manufacturer compat
Nobuhiro Iwamatsu (1):
ARM: dts: socfpga: Add Macnica sodia board
Steffen Trumtrar (2):
ARM: dts: socfpga: add qspi node
ARM: dts: socfpga: socrates: enable qspi
Thor Thayer (4):
ARM: dts: socfpga: Add SPI Master1 for Arria10 SR chip
ARM: dts: socfpga: Add Devkit A10-SR fields for Arria10
ARM: dts: socfpga: Enable GPIO parent for Arria10 SR chip
ARM: dts: socfpga: Add LED framework to A10-SR GPIO
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/socfpga.dtsi | 15 +++
arch/arm/boot/dts/socfpga_arria10.dtsi | 18 ++++
arch/arm/boot/dts/socfpga_arria10_socdk.dtsi | 49 ++++++++++
arch/arm/boot/dts/socfpga_cyclone5_mcv.dtsi | 2 +-
arch/arm/boot/dts/socfpga_cyclone5_mcvevk.dts | 2 +-
arch/arm/boot/dts/socfpga_cyclone5_socrates.dts | 19 ++++
arch/arm/boot/dts/socfpga_cyclone5_sodia.dts | 123 ++++++++++++++++++++++++
8 files changed, 227 insertions(+), 2 deletions(-)
create mode 100644 arch/arm/boot/dts/socfpga_cyclone5_sodia.dts
^ permalink raw reply
* [PATCH 1/3] mmc: mmci: clean up header defines
From: Linus Walleij @ 2016-10-24 14:21 UTC (permalink / raw)
To: linux-arm-kernel
There was some confusion in the CPSM (Command Path State Machine)
and DPSM (Data Path State Machine) regarding the naming of the
registers, clarify the meaning of this acronym so the naming is
understandable, and consistently use BIT() to define these fields.
Include new definitions for a few bits found in a patch from
Srinivas Kandagatla.
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
drivers/mmc/host/mmci.c | 2 +-
drivers/mmc/host/mmci.h | 69 +++++++++++++++++++++++++++----------------------
2 files changed, 39 insertions(+), 32 deletions(-)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index df990bb8c873..79b135752d3d 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -210,7 +210,7 @@ static struct variant_data variant_qcom = {
MCI_QCOM_CLK_SELECT_IN_FBCLK,
.clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8,
.datactrl_mask_ddrmode = MCI_QCOM_CLK_SELECT_IN_DDR_MODE,
- .data_cmd_enable = MCI_QCOM_CSPM_DATCMD,
+ .data_cmd_enable = MCI_CPSM_QCOM_DATCMD,
.blksz_datactrl4 = true,
.datalength_bits = 24,
.pwrreg_powerup = MCI_PWR_UP,
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index a1f5e4f49e2a..8952285196cd 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -51,25 +51,27 @@
#define MCI_QCOM_CLK_SELECT_IN_DDR_MODE (BIT(14) | BIT(15))
#define MMCIARGUMENT 0x008
-#define MMCICOMMAND 0x00c
-#define MCI_CPSM_RESPONSE (1 << 6)
-#define MCI_CPSM_LONGRSP (1 << 7)
-#define MCI_CPSM_INTERRUPT (1 << 8)
-#define MCI_CPSM_PENDING (1 << 9)
-#define MCI_CPSM_ENABLE (1 << 10)
-/* Argument flag extenstions in the ST Micro versions */
-#define MCI_ST_SDIO_SUSP (1 << 11)
-#define MCI_ST_ENCMD_COMPL (1 << 12)
-#define MCI_ST_NIEN (1 << 13)
-#define MCI_ST_CE_ATACMD (1 << 14)
-/* Modified on Qualcomm Integrations */
-#define MCI_QCOM_CSPM_DATCMD BIT(12)
-#define MCI_QCOM_CSPM_MCIABORT BIT(13)
-#define MCI_QCOM_CSPM_CCSENABLE BIT(14)
-#define MCI_QCOM_CSPM_CCSDISABLE BIT(15)
-#define MCI_QCOM_CSPM_AUTO_CMD19 BIT(16)
-#define MCI_QCOM_CSPM_AUTO_CMD21 BIT(21)
+/* The command register controls the Command Path State Machine (CPSM) */
+#define MMCICOMMAND 0x00c
+#define MCI_CPSM_RESPONSE BIT(6)
+#define MCI_CPSM_LONGRSP BIT(7)
+#define MCI_CPSM_INTERRUPT BIT(8)
+#define MCI_CPSM_PENDING BIT(9)
+#define MCI_CPSM_ENABLE BIT(10)
+/* Command register flag extenstions in the ST Micro versions */
+#define MCI_CPSM_ST_SDIO_SUSP BIT(11)
+#define MCI_CPSM_ST_ENCMD_COMPL BIT(12)
+#define MCI_CPSM_ST_NIEN BIT(13)
+#define MCI_CPSM_ST_CE_ATACMD BIT(14)
+/* Command register flag extensions in the Qualcomm versions */
+#define MCI_CPSM_QCOM_PROGENA BIT(11)
+#define MCI_CPSM_QCOM_DATCMD BIT(12)
+#define MCI_CPSM_QCOM_MCIABORT BIT(13)
+#define MCI_CPSM_QCOM_CCSENABLE BIT(14)
+#define MCI_CPSM_QCOM_CCSDISABLE BIT(15)
+#define MCI_CPSM_QCOM_AUTO_CMD19 BIT(16)
+#define MCI_CPSM_QCOM_AUTO_CMD21 BIT(21)
#define MMCIRESPCMD 0x010
#define MMCIRESPONSE0 0x014
@@ -78,22 +80,27 @@
#define MMCIRESPONSE3 0x020
#define MMCIDATATIMER 0x024
#define MMCIDATALENGTH 0x028
+
+/* The data control register controls the Data Path State Machine (DPSM) */
#define MMCIDATACTRL 0x02c
-#define MCI_DPSM_ENABLE (1 << 0)
-#define MCI_DPSM_DIRECTION (1 << 1)
-#define MCI_DPSM_MODE (1 << 2)
-#define MCI_DPSM_DMAENABLE (1 << 3)
-#define MCI_DPSM_BLOCKSIZE (1 << 4)
+#define MCI_DPSM_ENABLE BIT(0)
+#define MCI_DPSM_DIRECTION BIT(1)
+#define MCI_DPSM_MODE BIT(2)
+#define MCI_DPSM_DMAENABLE BIT(3)
+#define MCI_DPSM_BLOCKSIZE BIT(4)
/* Control register extensions in the ST Micro U300 and Ux500 versions */
-#define MCI_ST_DPSM_RWSTART (1 << 8)
-#define MCI_ST_DPSM_RWSTOP (1 << 9)
-#define MCI_ST_DPSM_RWMOD (1 << 10)
-#define MCI_ST_DPSM_SDIOEN (1 << 11)
+#define MCI_ST_DPSM_RWSTART BIT(8)
+#define MCI_ST_DPSM_RWSTOP BIT(9)
+#define MCI_ST_DPSM_RWMOD BIT(10)
+#define MCI_ST_DPSM_SDIOEN BIT(11)
/* Control register extensions in the ST Micro Ux500 versions */
-#define MCI_ST_DPSM_DMAREQCTL (1 << 12)
-#define MCI_ST_DPSM_DBOOTMODEEN (1 << 13)
-#define MCI_ST_DPSM_BUSYMODE (1 << 14)
-#define MCI_ST_DPSM_DDRMODE (1 << 15)
+#define MCI_ST_DPSM_DMAREQCTL BIT(12)
+#define MCI_ST_DPSM_DBOOTMODEEN BIT(13)
+#define MCI_ST_DPSM_BUSYMODE BIT(14)
+#define MCI_ST_DPSM_DDRMODE BIT(15)
+/* Control register extensions in the Qualcomm versions */
+#define MCI_QCOM_DPSM_DATA_PEND BIT(17)
+#define MCI_QCOM_DPSM_RX_DATA_PEND BIT(20)
#define MMCIDATACNT 0x030
#define MMCISTATUS 0x034
--
2.7.4
^ permalink raw reply related
* [PATCH 2/3] mmc: mmci: refactor ST Micro busy detection
From: Linus Walleij @ 2016-10-24 14:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477318886-24851-1-git-send-email-linus.walleij@linaro.org>
The ST Micro-specific busy detection was made after the assumption
that only this variant supports busy detection. So when doing busy
detection, the host immediately tries to use some ST-specific
register bits.
Since the qualcomm variant also supports some busy detection
schemes, encapsulate the variant flags better in the variant struct
and prepare to add more variants by just providing some bitmasks
to the logic.
Put the entire busy detection logic within an if()-clause in the
mmci_cmd_irq() function so the code is only executed when busy
detection is enabled, and so that it is kept in (almost) one
place, and add comments describing what is going on so the
code can be understood.
Tested on the Ux500 by introducing some prints in the busy
detection path and noticing how the IRQ is enabled, used and
disabled successfully.
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
drivers/mmc/host/mmci.c | 113 +++++++++++++++++++++++++++++++++++-------------
drivers/mmc/host/mmci.h | 2 +-
2 files changed, 85 insertions(+), 30 deletions(-)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 79b135752d3d..06e19d8359e0 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -71,7 +71,12 @@ static unsigned int fmax = 515633;
* @f_max: maximum clk frequency supported by the controller.
* @signal_direction: input/out direction of bus signals can be indicated
* @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
- * @busy_detect: true if busy detection on dat0 is supported
+ * @busy_detect: true if the variant supports busy detection on DAT0.
+ * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM
+ * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register
+ * indicating that the card is busy
+ * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for
+ * getting busy end detection interrupts
* @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
* @explicit_mclk_control: enable explicit mclk control in driver.
* @qcom_fifo: enables qcom specific fifo pio read logic.
@@ -98,6 +103,9 @@ struct variant_data {
bool signal_direction;
bool pwrreg_clkgate;
bool busy_detect;
+ u32 busy_dpsm_flag;
+ u32 busy_detect_flag;
+ u32 busy_detect_mask;
bool pwrreg_nopower;
bool explicit_mclk_control;
bool qcom_fifo;
@@ -178,6 +186,9 @@ static struct variant_data variant_ux500 = {
.signal_direction = true,
.pwrreg_clkgate = true,
.busy_detect = true,
+ .busy_dpsm_flag = MCI_ST_DPSM_BUSYMODE,
+ .busy_detect_flag = MCI_ST_CARDBUSY,
+ .busy_detect_mask = MCI_ST_BUSYENDMASK,
.pwrreg_nopower = true,
};
@@ -199,6 +210,9 @@ static struct variant_data variant_ux500v2 = {
.signal_direction = true,
.pwrreg_clkgate = true,
.busy_detect = true,
+ .busy_dpsm_flag = MCI_ST_DPSM_BUSYMODE,
+ .busy_detect_flag = MCI_ST_CARDBUSY,
+ .busy_detect_mask = MCI_ST_BUSYENDMASK,
.pwrreg_nopower = true,
};
@@ -220,6 +234,7 @@ static struct variant_data variant_qcom = {
.qcom_dml = true,
};
+/* Busy detection for the ST Micro variant */
static int mmci_card_busy(struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
@@ -227,7 +242,7 @@ static int mmci_card_busy(struct mmc_host *mmc)
int busy = 0;
spin_lock_irqsave(&host->lock, flags);
- if (readl(host->base + MMCISTATUS) & MCI_ST_CARDBUSY)
+ if (readl(host->base + MMCISTATUS) & host->variant->busy_detect_flag)
busy = 1;
spin_unlock_irqrestore(&host->lock, flags);
@@ -294,8 +309,8 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
*/
static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
{
- /* Keep ST Micro busy mode if enabled */
- datactrl |= host->datactrl_reg & MCI_ST_DPSM_BUSYMODE;
+ /* Keep busy mode in DPSM if enabled */
+ datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag;
if (host->datactrl_reg != datactrl) {
host->datactrl_reg = datactrl;
@@ -973,37 +988,66 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
unsigned int status)
{
void __iomem *base = host->base;
- bool sbc, busy_resp;
+ bool sbc;
if (!cmd)
return;
sbc = (cmd == host->mrq->sbc);
- busy_resp = host->variant->busy_detect && (cmd->flags & MMC_RSP_BUSY);
- if (!((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
- MCI_CMDSENT|MCI_CMDRESPEND)))
+ /*
+ * We need to be one of these interrupts to be considered worth
+ * handling. Note that we tag on any latent IRQs postponed
+ * due to waiting for busy status.
+ */
+ if (!((status|host->busy_status) &
+ (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND)))
return;
- /* Check if we need to wait for busy completion. */
- if (host->busy_status && (status & MCI_ST_CARDBUSY))
- return;
+ /*
+ * ST Micro variant: handle busy detection.
+ */
+ if (host->variant->busy_detect) {
+ bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY);
- /* Enable busy completion if needed and supported. */
- if (!host->busy_status && busy_resp &&
- !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
- (readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) {
- writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND,
- base + MMCIMASK0);
- host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND);
- return;
- }
+ /* We are busy with a command, return */
+ if (host->busy_status &&
+ (status & host->variant->busy_detect_flag))
+ return;
+
+ /*
+ * We were not busy, but we now got a busy response on
+ * something that was not an error, and we double-check
+ * that the special busy status bit is still set before
+ * proceeding.
+ */
+ if (!host->busy_status && busy_resp &&
+ !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
+ (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {
+ /* Unmask the busy IRQ */
+ writel(readl(base + MMCIMASK0) |
+ host->variant->busy_detect_mask,
+ base + MMCIMASK0);
+ /*
+ * Now cache the last response status code (until
+ * the busy bit goes low), and return.
+ */
+ host->busy_status =
+ status & (MCI_CMDSENT|MCI_CMDRESPEND);
+ return;
+ }
- /* At busy completion, mask the IRQ and complete the request. */
- if (host->busy_status) {
- writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND,
- base + MMCIMASK0);
- host->busy_status = 0;
+ /*
+ * At this point we are not busy with a command, we have
+ * not recieved a new busy request, mask the busy IRQ and
+ * fall through to process the IRQ.
+ */
+ if (host->busy_status) {
+ writel(readl(base + MMCIMASK0) &
+ ~host->variant->busy_detect_mask,
+ base + MMCIMASK0);
+ host->busy_status = 0;
+ }
}
host->cmd = NULL;
@@ -1257,9 +1301,11 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
mmci_data_irq(host, host->data, status);
}
- /* Don't poll for busy completion in irq context. */
- if (host->busy_status)
- status &= ~MCI_ST_CARDBUSY;
+ /*
+ * Don't poll for busy completion in irq context.
+ */
+ if (host->variant->busy_detect && host->busy_status)
+ status &= ~host->variant->busy_detect_flag;
ret = 1;
} while (status);
@@ -1612,9 +1658,18 @@ static int mmci_probe(struct amba_device *dev,
/* We support these capabilities. */
mmc->caps |= MMC_CAP_CMD23;
+ /*
+ * Enable busy detection.
+ */
if (variant->busy_detect) {
mmci_ops.card_busy = mmci_card_busy;
- mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
+ /*
+ * Not all variants have a flag to enable busy detection
+ * in the DPSM, but if they do, set it here.
+ */
+ if (variant->busy_dpsm_flag)
+ mmci_write_datactrlreg(host,
+ host->variant->busy_dpsm_flag);
mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
mmc->max_busy_timeout = 0;
}
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 8952285196cd..38f6f1365ec4 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -174,7 +174,7 @@
/* Extended status bits for the ST Micro variants */
#define MCI_ST_SDIOITMASK (1 << 22)
#define MCI_ST_CEATAENDMASK (1 << 23)
-#define MCI_ST_BUSYEND (1 << 24)
+#define MCI_ST_BUSYENDMASK (1 << 24)
#define MMCIMASK1 0x040
#define MMCIFIFOCNT 0x048
--
2.7.4
^ permalink raw reply related
* [PATCH 3/3] RFC: mmc: mmci: add qcom specific program end support
From: Linus Walleij @ 2016-10-24 14:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477318886-24851-1-git-send-email-linus.walleij@linaro.org>
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
This patch adds support to programend interrupt which is very
specific to QCOM integration. This interrupt is use as busy signal
when a command forces the card to enter into programming state
like CMD6 writing to ext_csd registers.
Hopefully, this also fixes the __mmc_switch timeout issue reproted
with latest versions of the eMMC used on DB600c board.
This patch is based on a WIP patch from Srinivas Kandagatla and
augmented by Linus Walleij for another approach.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
Srinivas: please test to see if this fixes your problems. Sadly
it does *NOT* solve my APQ8060 issues, but it would be nice if
the common code path works for the busy detection on your
DB600c.
---
drivers/mmc/host/mmci.c | 21 +++++++++++++++++++--
drivers/mmc/host/mmci.h | 6 ++++++
2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 06e19d8359e0..c0380713df34 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -72,7 +72,10 @@ static unsigned int fmax = 515633;
* @signal_direction: input/out direction of bus signals can be indicated
* @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
* @busy_detect: true if the variant supports busy detection on DAT0.
- * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM
+ * @busy_cpsm_flag: bitmask enabling busy detection in the CPSM (command
+ * path state machine)
+ * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM (data path
+ * state machine)
* @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register
* indicating that the card is busy
* @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for
@@ -103,6 +106,7 @@ struct variant_data {
bool signal_direction;
bool pwrreg_clkgate;
bool busy_detect;
+ u32 busy_cpsm_flag;
u32 busy_dpsm_flag;
u32 busy_detect_flag;
u32 busy_detect_mask;
@@ -229,6 +233,10 @@ static struct variant_data variant_qcom = {
.datalength_bits = 24,
.pwrreg_powerup = MCI_PWR_UP,
.f_max = 208000000,
+ .busy_detect = true,
+ .busy_cpsm_flag = MCI_CPSM_QCOM_PROGENA,
+ .busy_detect_flag = MCI_QCOM_PROGDONE,
+ .busy_detect_mask = MCI_QCOM_PROGDONEMASK,
.explicit_mclk_control = true,
.qcom_fifo = true,
.qcom_dml = true,
@@ -903,6 +911,15 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
if (/*interrupt*/0)
c |= MCI_CPSM_INTERRUPT;
+ /*
+ * Enable the program end interrupt for specific commands
+ * used for busy detection.
+ */
+ if (host->variant->busy_detect &&
+ (cmd->flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
+ c |= host->variant->busy_cpsm_flag;
+ }
+
if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
c |= host->variant->data_cmd_enable;
@@ -1005,7 +1022,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
return;
/*
- * ST Micro variant: handle busy detection.
+ * ST Micro and Qualcomm variants: handle busy detection.
*/
if (host->variant->busy_detect) {
bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 38f6f1365ec4..99abf02f560e 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -130,6 +130,8 @@
#define MCI_ST_SDIOIT (1 << 22)
#define MCI_ST_CEATAEND (1 << 23)
#define MCI_ST_CARDBUSY (1 << 24)
+/* Extended status bits for the QCOM variants */
+#define MCI_QCOM_PROGDONE (1 << 23)
#define MMCICLEAR 0x038
#define MCI_CMDCRCFAILCLR (1 << 0)
@@ -147,6 +149,8 @@
#define MCI_ST_SDIOITC (1 << 22)
#define MCI_ST_CEATAENDC (1 << 23)
#define MCI_ST_BUSYENDC (1 << 24)
+/* Extended status bits for the QCOM variants */
+#define MCI_QCOM_PROGDONECLR (1 << 23)
#define MMCIMASK0 0x03c
#define MCI_CMDCRCFAILMASK (1 << 0)
@@ -175,6 +179,8 @@
#define MCI_ST_SDIOITMASK (1 << 22)
#define MCI_ST_CEATAENDMASK (1 << 23)
#define MCI_ST_BUSYENDMASK (1 << 24)
+/* Extended status bits for the Qualcomm variants */
+#define MCI_QCOM_PROGDONEMASK (1 << 23)
#define MMCIMASK1 0x040
#define MMCIFIFOCNT 0x048
--
2.7.4
^ permalink raw reply related
* [PATCH] drm/sun4i: Add a few formats
From: Maxime Ripard @ 2016-10-24 14:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAGb2v65uZFQnRme9LrRykze=a4d_7=HS=Gvu6E_eBG5tSw8nTw@mail.gmail.com>
Hi,
On Fri, Oct 21, 2016 at 11:15:32AM +0800, Chen-Yu Tsai wrote:
> On Tue, Oct 18, 2016 at 4:46 PM, Maxime Ripard
> <maxime.ripard@free-electrons.com> wrote:
> > The planes can do more than what was previously exposed. Add support for
> > them.
> >
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > ---
> > drivers/gpu/drm/sun4i/sun4i_backend.c | 20 ++++++++++++++++++++
> > drivers/gpu/drm/sun4i/sun4i_layer.c | 6 ++++++
> > 2 files changed, 26 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c
> > index afb7ddf660ef..b184a476a480 100644
> > --- a/drivers/gpu/drm/sun4i/sun4i_backend.c
> > +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
> > @@ -96,6 +96,22 @@ static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane,
> > *mode = SUN4I_BACKEND_LAY_FBFMT_ARGB8888;
> > break;
> >
> > + case DRM_FORMAT_ARGB4444:
> > + *mode = SUN4I_BACKEND_LAY_FBFMT_ARGB4444;
> > + break;
> > +
> > + case DRM_FORMAT_ARGB1555:
> > + *mode = SUN4I_BACKEND_LAY_FBFMT_ARGB1555;
> > + break;
> > +
> > + case DRM_FORMAT_RGBA5551:
> > + *mode = SUN4I_BACKEND_LAY_FBFMT_RGBA5551;
> > + break;
> > +
> > + case DRM_FORMAT_RGBA4444:
> > + *mode = SUN4I_BACKEND_LAY_FBFMT_RGBA4444;
>
> The A20 manual only lists ARGB4444, not RGBA4444. There might be
> some discrepancy here. We can deal with them
Hmm, yes, that's weird. But I guess this would be part of porting it
to the A20.
> Also there are some more formats missing from the list, could you
> add them as well?
Which one do you refer to?
> > + break;
> > +
> > case DRM_FORMAT_XRGB8888:
> > *mode = SUN4I_BACKEND_LAY_FBFMT_XRGB8888;
> > break;
> > @@ -104,6 +120,10 @@ static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane,
> > *mode = SUN4I_BACKEND_LAY_FBFMT_RGB888;
> > break;
> >
> > + case DRM_FORMAT_RGB565:
> > + *mode = SUN4I_BACKEND_LAY_FBFMT_RGB565;
> > + break;
> > +
> > default:
> > return -EINVAL;
> > }
> > diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
> > index f0035bf5efea..5d53c977bca5 100644
> > --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
> > +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
> > @@ -73,12 +73,18 @@ static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
> > static const uint32_t sun4i_backend_layer_formats_primary[] = {
> > DRM_FORMAT_ARGB8888,
> > DRM_FORMAT_RGB888,
> > + DRM_FORMAT_RGB565,
> > DRM_FORMAT_XRGB8888,
> > };
> >
> > static const uint32_t sun4i_backend_layer_formats_overlay[] = {
> > DRM_FORMAT_ARGB8888,
> > + DRM_FORMAT_ARGB4444,
> > + DRM_FORMAT_ARGB1555,
> > + DRM_FORMAT_RGBA5551,
> > + DRM_FORMAT_RGBA4444,
> > DRM_FORMAT_RGB888,
> > + DRM_FORMAT_RGB565,
> > DRM_FORMAT_XRGB8888,
>
> Could you explain in the commit log why these 2 aren't the same?
Yep, I will.
Thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161024/66862cae/attachment.sig>
^ permalink raw reply
* [PATCH] Documentation: DMA-API: Clarify semantics of dma_set_mask_and_coherent
From: Arnd Bergmann @ 2016-10-24 14:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161021150916.0d274e9f@lwn.net>
On Friday, October 21, 2016 3:09:16 PM CEST Jonathan Corbet wrote:
> On Mon, 17 Oct 2016 16:26:23 +0100
> Punit Agrawal <punit.agrawal@arm.com> wrote:
>
> > The dma mapping api howto gives the impression that using the
> > dma_set_mask_and_coherent (and related DMA APIs) will cause the kernel
> > to check all the components in the path from the device to memory for
> > addressing restrictions. In systems with address translations between
> > the device and memory (e.g., when using IOMMU), this implies that a
> > successful call to set set dma mask has checked the addressing
> > constraints of the intermediaries as well.
> >
> > For the IOMMU drivers in the tree, the check is actually performed while
> > allocating the DMA buffer rather than when the DMA mask is
> > configured. For MMUs that do not support the full device addressing
> > capability, the allocations are made from a reduced address space.
> >
> > Update the documentation to clarify that even though the call to
> > dma_set_mask_and_coherent succeeds, it may not be possible to use the
> > full addressing capability of the device.
>
> OK, so I guess I can buy this. But...
>
> > Signed-off-by: Punit Agrawal <punit.agrawal@arm.com>
> > Cc: Jonathan Corbet <corbet@lwn.net>
> > ---
> > Documentation/DMA-API-HOWTO.txt | 39 +++++++++++++++++++++++----------------
> > 1 file changed, 23 insertions(+), 16 deletions(-)
> >
> > diff --git a/Documentation/DMA-API-HOWTO.txt b/Documentation/DMA-API-HOWTO.txt
> > index 979228b..240d1ee 100644
> > --- a/Documentation/DMA-API-HOWTO.txt
> > +++ b/Documentation/DMA-API-HOWTO.txt
> > @@ -159,39 +159,46 @@ support 64-bit addressing (DAC) for all transactions. And at least
> > one platform (SGI SN2) requires 64-bit consistent allocations to
> > operate correctly when the IO bus is in PCI-X mode.
> >
> > -For correct operation, you must interrogate the kernel in your device
> > -probe routine to see if the DMA controller on the machine can properly
> > -support the DMA addressing limitation your device has. It is good
> > +For correct operation, you must inform the kernel in your device probe
> > +routine to see if the DMA controller on the machine can properly
> > +support the DMA addressing capabilities your device has. It is good
>
> Here it's still saying "to see if the DMA controller on the machine can
> properly support the DMA addressing capabilities your device has". So
> you've not really changed the sense of this sentence here.
>
> If I understand things correctly, the calls in question are storing the
> device's limitations; they will only fail if the kernel is entirely
> unable to work within the indicated range, right? I don't think there's
> ever been any guarantee that the system as a whole could use the entire
> range that is addressable by the device. I have no objection to making
> that more clear, but let's actually make it more clear by saying what the
> functions are actually doing.
>
> Make sense, or am I missing something here?
The call is a two-way interface, and the existing text tries to convey
that already: The device tells the kernel whether it is limited (< 32
bit mask) or if it can support extended addresses (> 32 bit mask),
or just handles the default 32bit mask, and the kernel should come
back saying whether that mask allows a correct operation of the device
on the given platform, as well as set it up correctly that way.
What exactly happens in dma_set_mask() and the related interfaces
is highly platform specific, including:
- if the mask is smaller than the smallest memory zone and the
swiotlb bounce buffers (if any) don't fit inside it, it has to
fail
- if the device claims to support larger mask, but the bus it
connects to does not (e.g. a 32-bit PCI host), it may
also fail (or succeed if there is no RAM outside of the
intersection of the two masks)
- if the mask is large enough to cover all RAM, we can bypass
the IOMMU and use a direct mapping
- if swiotlb is enabled or an IOMMU is present, any mask that
includes the bounce buffer area (or the virtual address space
of the IOMMU) should succeed.
Arnd
^ permalink raw reply
* [PATCH v4 6/9] clk: sunxi-ng: Add A64 clocks
From: Maxime Ripard @ 2016-10-24 15:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161020184601.GB26139@codeaurora.org>
Hi Stephen,
On Thu, Oct 20, 2016 at 11:46:01AM -0700, Stephen Boyd wrote:
> On 10/11, Maxime Ripard wrote:
> > diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
> > new file mode 100644
> > index 000000000000..c0e96bf6d104
> > --- /dev/null
> > +++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
> > +
> > +static int sun50i_a64_ccu_probe(struct platform_device *pdev)
> > +{
> > + struct resource *res;
> > + void __iomem *reg;
> > + u32 val;
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + reg = devm_ioremap_resource(&pdev->dev, res);
> > + if (IS_ERR(reg)) {
> > + dev_err(&pdev->dev, "Could not map the clock registers\n");
>
> devm_ioremap_resource() should already spit out an error.
>
> > + return PTR_ERR(reg);
> > + }
> > +
> > + /* Force the PLL-Audio-1x divider to 4 */
> > + val = readl(reg + SUN50I_A64_PLL_AUDIO_REG);
> > + val &= ~GENMASK(19, 16);
> > + writel(val | (3 << 16), reg + SUN50I_A64_PLL_AUDIO_REG);
> > +
> > + writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG);
> > +
> > + return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a64_ccu_desc);
> > +}
> > +
> > +static const struct of_device_id sun50i_a64_ccu_ids[] = {
> > + { .compatible = "allwinner,sun50i-a64-ccu" },
> > + { },
>
> Nitpick: drop the comma
Thanks for your comments, it's fixed.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161024/962b7cf5/attachment.sig>
^ permalink raw reply
* 4.9 RC2 fails to boot on mk802ii with generic DT based cpufreq driver
From: Jost Menke @ 2016-10-24 15:09 UTC (permalink / raw)
To: linux-arm-kernel
Hi all,
I ran into a problem with the recent mainline kernel on my Allwinner A10 based MK802ii. It looks like a wrong initial CPU frequency is chosen. It will boot until this point:
[3.040447] cpufreq: cpufreq_online: CPU0: Running at unlisted freq: 384000 KHz
[3.057932] cpufreq: cpufreq_online: CPU0: Unlisted initial frequency changed to: 624000 KHz
After that, it just freezes. When I compile with a different cpufreq driver, it will boot up but there doesn't seem to be any frequency scaling (/sys/devices/system/cpu/cpu0/cpufreq is empty) and I think it just remains at a very low clock speed because sysbench results are really slow.
Any ideas?
Cheers
Jost
^ permalink raw reply
* [PATCH] ARM64: dts: bcm2835: Fix bcm2837 compatible string
From: Andreas Färber @ 2016-10-24 15:09 UTC (permalink / raw)
To: linux-arm-kernel
bcm2837-rpi-3-b.dts, its only in-tree user, was overriding it as
"brcm,bcm2837" already.
Fixes: 9d56c22a7861 ("ARM: bcm2835: Add devicetree for the Raspberry Pi 3.")
Cc: Eric Anholt <eric@anholt.net>
Cc: Stephen Warren <swarren@wwwdotorg.org>
Signed-off-by: Andreas F?rber <afaerber@suse.de>
---
arch/arm64/boot/dts/broadcom/bcm2837.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/broadcom/bcm2837.dtsi b/arch/arm64/boot/dts/broadcom/bcm2837.dtsi
index 8216bbb29fe0..c1f719b7097a 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2837.dtsi
+++ b/arch/arm64/boot/dts/broadcom/bcm2837.dtsi
@@ -1,7 +1,7 @@
#include "bcm283x.dtsi"
/ {
- compatible = "brcm,bcm2836";
+ compatible = "brcm,bcm2837";
soc {
ranges = <0x7e000000 0x3f000000 0x1000000>,
--
2.6.6
^ permalink raw reply related
* [PATCH 2/5] ARM: davinci: Don't append git rev to local version
From: David Lechner @ 2016-10-24 15:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20f3efba-4f79-3675-d2ce-09efe90fb692@ti.com>
On 10/24/2016 06:35 AM, Sekhar Nori wrote:
> On Saturday 22 October 2016 12:06 AM, David Lechner wrote:
>> In the davinci default configuration, don't append the git revision to
>> the local kernel version by. This seems like the more desirable default
>> value.
>
> Why? To the contrary I actually quite like the fact that the git commit
> is appended to version string. Makes it easy for me to cross-check that
> I am booting the right image.
>
>>
>> Signed-off-by: David Lechner <david@lechnology.com>
>
> Thanks,
> Sekhar
>
Each time you make a commit, you get a new version, which installs
another copy of the kernel modules on the device. This will fill up the
SD card if you are making many commits.
Also, if someone wants to build the mainline kernel using the default
configuration, it seems odd to have a git revision tacked on to the end
even though you made no revisions.
^ permalink raw reply
* [PATCH] arm/arm64: KVM: Perform local TLB invalidation when multiplexing vcpus on a single CPU
From: Marc Zyngier @ 2016-10-24 15:31 UTC (permalink / raw)
To: linux-arm-kernel
Architecturally, TLBs are private to the (physical) CPU they're
associated with. But when multiple vcpus from the same VM are
being multiplexed on the same CPU, the TLBs are not private
to the vcpus (and are actually shared across the VMID).
Let's consider the following scenario:
- vcpu-0 maps PA to VA
- vcpu-1 maps PA' to VA
If run on the same physical CPU, vcpu-1 can hit TLB entries generated
by vcpu-0 accesses, and access the wrong physical page.
The solution to this is to keep a per-VM map of which vcpu ran last
on each given physical CPU, and invalidate local TLBs when switching
to a different vcpu from the same VM.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm/include/asm/kvm_host.h | 5 +++++
arch/arm/include/asm/kvm_hyp.h | 1 +
arch/arm/kvm/arm.c | 35 ++++++++++++++++++++++++++++++++++-
arch/arm/kvm/hyp/switch.c | 9 +++++++++
arch/arm64/include/asm/kvm_host.h | 6 +++++-
arch/arm64/kvm/hyp/switch.c | 8 ++++++++
6 files changed, 62 insertions(+), 2 deletions(-)
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index 2d19e02..035e744 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -57,6 +57,8 @@ struct kvm_arch {
/* VTTBR value associated with below pgd and vmid */
u64 vttbr;
+ int __percpu *last_vcpu_ran;
+
/* Timer */
struct arch_timer_kvm timer;
@@ -174,6 +176,9 @@ struct kvm_vcpu_arch {
/* vcpu power-off state */
bool power_off;
+ /* TLBI required */
+ bool requires_tlbi;
+
/* Don't run the guest (internal implementation need) */
bool pause;
diff --git a/arch/arm/include/asm/kvm_hyp.h b/arch/arm/include/asm/kvm_hyp.h
index 343135e..5850890 100644
--- a/arch/arm/include/asm/kvm_hyp.h
+++ b/arch/arm/include/asm/kvm_hyp.h
@@ -71,6 +71,7 @@
#define ICIALLUIS __ACCESS_CP15(c7, 0, c1, 0)
#define ATS1CPR __ACCESS_CP15(c7, 0, c8, 0)
#define TLBIALLIS __ACCESS_CP15(c8, 0, c3, 0)
+#define TLBIALL __ACCESS_CP15(c8, 0, c7, 0)
#define TLBIALLNSNHIS __ACCESS_CP15(c8, 4, c3, 4)
#define PRRR __ACCESS_CP15(c10, 0, c2, 0)
#define NMRR __ACCESS_CP15(c10, 0, c2, 1)
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 03e9273..09942f0 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -114,11 +114,18 @@ void kvm_arch_check_processor_compat(void *rtn)
*/
int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
{
- int ret = 0;
+ int ret, cpu;
if (type)
return -EINVAL;
+ kvm->arch.last_vcpu_ran = alloc_percpu(typeof(*kvm->arch.last_vcpu_ran));
+ if (!kvm->arch.last_vcpu_ran)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu)
+ *per_cpu_ptr(kvm->arch.last_vcpu_ran, cpu) = -1;
+
ret = kvm_alloc_stage2_pgd(kvm);
if (ret)
goto out_fail_alloc;
@@ -141,6 +148,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
out_free_stage2_pgd:
kvm_free_stage2_pgd(kvm);
out_fail_alloc:
+ free_percpu(kvm->arch.last_vcpu_ran);
+ kvm->arch.last_vcpu_ran = NULL;
return ret;
}
@@ -168,6 +177,9 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
{
int i;
+ free_percpu(kvm->arch.last_vcpu_ran);
+ kvm->arch.last_vcpu_ran = NULL;
+
for (i = 0; i < KVM_MAX_VCPUS; ++i) {
if (kvm->vcpus[i]) {
kvm_arch_vcpu_free(kvm->vcpus[i]);
@@ -310,6 +322,27 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
return 0;
}
+void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu)
+{
+ int *last_ran;
+
+ last_ran = per_cpu_ptr(vcpu->kvm->arch.last_vcpu_ran, cpu);
+
+ /*
+ * If we're very unlucky and get preempted before having ran
+ * this vcpu for real, we'll end-up in a situation where any
+ * vcpu that gets scheduled will perform an invalidation (this
+ * vcpu explicitely requires it, and all the others will have
+ * a different vcpu_id).
+ */
+ if (*last_ran != vcpu->vcpu_id) {
+ if (*last_ran != -1)
+ vcpu->arch.requires_tlbi = true;
+
+ *last_ran = vcpu->vcpu_id;
+ }
+}
+
void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
vcpu->cpu = cpu;
diff --git a/arch/arm/kvm/hyp/switch.c b/arch/arm/kvm/hyp/switch.c
index 92678b7..ab8ee3b 100644
--- a/arch/arm/kvm/hyp/switch.c
+++ b/arch/arm/kvm/hyp/switch.c
@@ -75,6 +75,15 @@ static void __hyp_text __activate_vm(struct kvm_vcpu *vcpu)
{
struct kvm *kvm = kern_hyp_va(vcpu->kvm);
write_sysreg(kvm->arch.vttbr, VTTBR);
+ if (vcpu->arch.requires_tlbi) {
+ /* Force vttbr to be written */
+ isb();
+ /* Local invalidate only for this VMID */
+ write_sysreg(0, TLBIALL);
+ dsb(nsh);
+ vcpu->arch.requires_tlbi = false;
+ }
+
write_sysreg(vcpu->arch.midr, VPIDR);
}
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index bd94e67..5b42010 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -62,6 +62,8 @@ struct kvm_arch {
/* VTTBR value associated with above pgd and vmid */
u64 vttbr;
+ int __percpu *last_vcpu_ran;
+
/* The maximum number of vCPUs depends on the used GIC model */
int max_vcpus;
@@ -252,6 +254,9 @@ struct kvm_vcpu_arch {
/* vcpu power-off state */
bool power_off;
+ /* TLBI required */
+ bool requires_tlbi;
+
/* Don't run the guest (internal implementation need) */
bool pause;
@@ -368,7 +373,6 @@ static inline void __cpu_reset_hyp_mode(unsigned long vector_ptr,
static inline void kvm_arch_hardware_unsetup(void) {}
static inline void kvm_arch_sync_events(struct kvm *kvm) {}
static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
-static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {}
static inline void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu) {}
void kvm_arm_init_debug(void);
diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
index 83037cd..8d9c3eb 100644
--- a/arch/arm64/kvm/hyp/switch.c
+++ b/arch/arm64/kvm/hyp/switch.c
@@ -131,6 +131,14 @@ static void __hyp_text __activate_vm(struct kvm_vcpu *vcpu)
{
struct kvm *kvm = kern_hyp_va(vcpu->kvm);
write_sysreg(kvm->arch.vttbr, vttbr_el2);
+ if (vcpu->arch.requires_tlbi) {
+ /* Force vttbr_el2 to be written */
+ isb();
+ /* Local invalidate only for this VMID */
+ asm volatile("tlbi vmalle1" : : );
+ dsb(nsh);
+ vcpu->arch.requires_tlbi = false;
+ }
}
static void __hyp_text __deactivate_vm(struct kvm_vcpu *vcpu)
--
2.1.4
^ permalink raw reply related
* [PATCH 1/3] mmc: mmci: clean up header defines
From: Robin Murphy @ 2016-10-24 15:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477318886-24851-1-git-send-email-linus.walleij@linaro.org>
Hi Linus,
One tiny nit which stands out from a cleanup perspective...
On 24/10/16 15:21, Linus Walleij wrote:
> There was some confusion in the CPSM (Command Path State Machine)
> and DPSM (Data Path State Machine) regarding the naming of the
> registers, clarify the meaning of this acronym so the naming is
> understandable, and consistently use BIT() to define these fields.
>
> Include new definitions for a few bits found in a patch from
> Srinivas Kandagatla.
>
> Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> drivers/mmc/host/mmci.c | 2 +-
> drivers/mmc/host/mmci.h | 69 +++++++++++++++++++++++++++----------------------
> 2 files changed, 39 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index df990bb8c873..79b135752d3d 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -210,7 +210,7 @@ static struct variant_data variant_qcom = {
> MCI_QCOM_CLK_SELECT_IN_FBCLK,
> .clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8,
> .datactrl_mask_ddrmode = MCI_QCOM_CLK_SELECT_IN_DDR_MODE,
> - .data_cmd_enable = MCI_QCOM_CSPM_DATCMD,
> + .data_cmd_enable = MCI_CPSM_QCOM_DATCMD,
> .blksz_datactrl4 = true,
> .datalength_bits = 24,
> .pwrreg_powerup = MCI_PWR_UP,
> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
> index a1f5e4f49e2a..8952285196cd 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -51,25 +51,27 @@
> #define MCI_QCOM_CLK_SELECT_IN_DDR_MODE (BIT(14) | BIT(15))
>
> #define MMCIARGUMENT 0x008
> -#define MMCICOMMAND 0x00c
> -#define MCI_CPSM_RESPONSE (1 << 6)
> -#define MCI_CPSM_LONGRSP (1 << 7)
> -#define MCI_CPSM_INTERRUPT (1 << 8)
> -#define MCI_CPSM_PENDING (1 << 9)
> -#define MCI_CPSM_ENABLE (1 << 10)
> -/* Argument flag extenstions in the ST Micro versions */
> -#define MCI_ST_SDIO_SUSP (1 << 11)
> -#define MCI_ST_ENCMD_COMPL (1 << 12)
> -#define MCI_ST_NIEN (1 << 13)
> -#define MCI_ST_CE_ATACMD (1 << 14)
>
> -/* Modified on Qualcomm Integrations */
> -#define MCI_QCOM_CSPM_DATCMD BIT(12)
> -#define MCI_QCOM_CSPM_MCIABORT BIT(13)
> -#define MCI_QCOM_CSPM_CCSENABLE BIT(14)
> -#define MCI_QCOM_CSPM_CCSDISABLE BIT(15)
> -#define MCI_QCOM_CSPM_AUTO_CMD19 BIT(16)
> -#define MCI_QCOM_CSPM_AUTO_CMD21 BIT(21)
> +/* The command register controls the Command Path State Machine (CPSM) */
> +#define MMCICOMMAND 0x00c
> +#define MCI_CPSM_RESPONSE BIT(6)
> +#define MCI_CPSM_LONGRSP BIT(7)
> +#define MCI_CPSM_INTERRUPT BIT(8)
> +#define MCI_CPSM_PENDING BIT(9)
> +#define MCI_CPSM_ENABLE BIT(10)
> +/* Command register flag extenstions in the ST Micro versions */
> +#define MCI_CPSM_ST_SDIO_SUSP BIT(11)
> +#define MCI_CPSM_ST_ENCMD_COMPL BIT(12)
> +#define MCI_CPSM_ST_NIEN BIT(13)
> +#define MCI_CPSM_ST_CE_ATACMD BIT(14)
> +/* Command register flag extensions in the Qualcomm versions */
> +#define MCI_CPSM_QCOM_PROGENA BIT(11)
> +#define MCI_CPSM_QCOM_DATCMD BIT(12)
> +#define MCI_CPSM_QCOM_MCIABORT BIT(13)
> +#define MCI_CPSM_QCOM_CCSENABLE BIT(14)
> +#define MCI_CPSM_QCOM_CCSDISABLE BIT(15)
> +#define MCI_CPSM_QCOM_AUTO_CMD19 BIT(16)
> +#define MCI_CPSM_QCOM_AUTO_CMD21 BIT(21)
These definitions take the format of MCI_register_{vendor_}field...
> #define MMCIRESPCMD 0x010
> #define MMCIRESPONSE0 0x014
> @@ -78,22 +80,27 @@
> #define MMCIRESPONSE3 0x020
> #define MMCIDATATIMER 0x024
> #define MMCIDATALENGTH 0x028
> +
> +/* The data control register controls the Data Path State Machine (DPSM) */
> #define MMCIDATACTRL 0x02c
> -#define MCI_DPSM_ENABLE (1 << 0)
> -#define MCI_DPSM_DIRECTION (1 << 1)
> -#define MCI_DPSM_MODE (1 << 2)
> -#define MCI_DPSM_DMAENABLE (1 << 3)
> -#define MCI_DPSM_BLOCKSIZE (1 << 4)
> +#define MCI_DPSM_ENABLE BIT(0)
> +#define MCI_DPSM_DIRECTION BIT(1)
> +#define MCI_DPSM_MODE BIT(2)
> +#define MCI_DPSM_DMAENABLE BIT(3)
> +#define MCI_DPSM_BLOCKSIZE BIT(4)
> /* Control register extensions in the ST Micro U300 and Ux500 versions */
> -#define MCI_ST_DPSM_RWSTART (1 << 8)
> -#define MCI_ST_DPSM_RWSTOP (1 << 9)
> -#define MCI_ST_DPSM_RWMOD (1 << 10)
> -#define MCI_ST_DPSM_SDIOEN (1 << 11)
> +#define MCI_ST_DPSM_RWSTART BIT(8)
> +#define MCI_ST_DPSM_RWSTOP BIT(9)
> +#define MCI_ST_DPSM_RWMOD BIT(10)
> +#define MCI_ST_DPSM_SDIOEN BIT(11)
> /* Control register extensions in the ST Micro Ux500 versions */
> -#define MCI_ST_DPSM_DMAREQCTL (1 << 12)
> -#define MCI_ST_DPSM_DBOOTMODEEN (1 << 13)
> -#define MCI_ST_DPSM_BUSYMODE (1 << 14)
> -#define MCI_ST_DPSM_DDRMODE (1 << 15)
> +#define MCI_ST_DPSM_DMAREQCTL BIT(12)
> +#define MCI_ST_DPSM_DBOOTMODEEN BIT(13)
> +#define MCI_ST_DPSM_BUSYMODE BIT(14)
> +#define MCI_ST_DPSM_DDRMODE BIT(15)
> +/* Control register extensions in the Qualcomm versions */
> +#define MCI_QCOM_DPSM_DATA_PEND BIT(17)
> +#define MCI_QCOM_DPSM_RX_DATA_PEND BIT(20)
...but these retain the previous MCI_{vendor_}register_field format - it
seems like a needless inconsistency, but if there is some good reason
then fair enough (I only have the vaguest familiarity with this IP/driver).
Robin.
>
> #define MMCIDATACNT 0x030
> #define MMCISTATUS 0x034
>
^ permalink raw reply
* [PATCH 5/5] ARM: dts: Add LEGO MINDSTORTMS EV3 dts
From: David Lechner @ 2016-10-24 15:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <b0022fad-a96e-86d4-71ba-2b803e5421fe@ti.com>
On 10/24/2016 06:58 AM, Sekhar Nori wrote:
> On Saturday 22 October 2016 12:06 AM, David Lechner wrote:
>> This adds a device tree definition file for LEGO MINDSTORMS EV3.
>
> Thanks for the patch!
>
>>
>> What is working:
>>
>> * Pin muxing
>> * MicroSD card reader
>> * UART on input port 1
>>
>> What is partially working:
>>
>> * Buttons - working after GPIO fix
>> * LEDs - working after GPIO fix
>> * Poweroff/reset - working after GPIO fix
>
> Is the GPIO fix something that will go in v4.9-rc cycle ?
Not sure. This is still being discussed.
http://www.gossamer-threads.com/lists/linux/kernel/2550178
>
>> * Flash memory - driver loads but can't read the block devices - this is
>> probably due to the fact that we are not able to configure the SPI to
>> use DMA via device tree
>
> Hmm, I would not have expected PIO mode to be so inefficient that you
> are unable to even read the block device.
I am getting a -EIO error. I haven't been able to trace down exactly
what is causing it yet though.
>
...
>> +/ {
>> + compatible = "lego,ev3", "ti,da850";
>> + model = "LEGO MINDSTORMS EV3";
>> +
>> + soc at 1c00000 {
>> + /*
>> + * (ab)using pinctrl-single to disable all internal pullups/
>> + * pulldowns on I/O.
>> + */
>> + pinmux at 22c00c {
>> + compatible = "pinctrl-single";
>> + reg = <0x22c00c 0x4>;
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + pinctrl-single,bit-per-mux;
>> + pinctrl-single,register-width = <32>;
>> + pinctrl-single,function-mask = <0xf>;
>> + /*
>> + * There is a bug in pinctrl-single that prevents us
>> + * from setting function-mask to 1, so doing things
>> + * in groups of 4. Doesn't really matter since we are
>> + * disabling all at once anyway.
>> + */
>> +
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pupu_disable>;
>> +
>> + pupu_disable: pinmux_all_pins {
>> + pinctrl-single,bits = <
>> + 0x0 0x00000000 0xffffffff
>> + >;
>> + };
>
> Sigh. This is quite an abuse :)
>
> I know we don't have a good way to configure this in kernel today. And I
> am surprised we never had to care about disabling pullups so far. Can
> you clarify why you need it? I assume there is some contention you want
> to avoid, but on which interface?
The EV3 was designed with external pullup/pulldown everywhere. I know
for certain that it breaks one of the buttons if you do not disable the
internal ones. I imagine that it would have subtle effects elsewhere if
they are not disabled.
I have not gone through each pullup/pulldown bank individually, but it
would not surprise me at all if there was at least one thing on most of
them that would be adversely affected.
>
> I dont think this can be done this way using pinctrl-single. A small
> driver to handle pullup/down control for da850 may have to be added to
> drivers/pinctrl. It will be better to check with Linus Walleij on his
> thoughts using a new thread ccing the pinctrl subsystem list as well.
I will be glad to try to make a driver, but when I ran into this problem
I could not find much information on how to handle banks of
pullup/pulldown. Most of what I saw was for ones that can be
individually controlled. If anyone knows something like this already
that I could look at, it would be helpful to me.
> [...]
>
>> + in1_pins: pinmux_in1_pins {
>> + pinctrl-single,bits = <
>> + /* GP0[15] */
>> + 0x0 0x00000008 0x0000000f
>> + /* GP0[2] */
>> + 0x4 0x00800000 0x00f00000
>> + /* GP2[2] */
>> + 0x18 0x00800000 0x00f00000
>> + /* GP8[10], GP8[11] */
>> + 0x48 0x88000000 0xff000000
>> + >;
>> + };
>
> I see that this is not really used. Can you add these when you actually
> use them. Looks like that applies to some other definitions like this below.
It will be possible to uses these gpios via sysfs (until a proper driver
for input and output ports is merged). So how about I attach these to
the gpio node for now?
>
>> +&ehrpwm1 {
>> + status = "disabled";
>
> Hmm, disabled? Can you add this node when you actually use it?
Not sure why I have this disabled. Like the gpios, the pwms can be used
via sysfs, so I would like to leave them.
>
>> + pinctrl-names = "default";
>> + /* MBPWM, MAPWM */
>> + pinctrl-0 = <&ehrpwm1a_pins>, <&ehrpwm1b_pins>;
>> +};
>> +
>> +&ecap1 {
>> + status = "disabled";
>
> same here and other places below.
>
>> + pinctrl-names = "default";
>> + /* MDPWM */
>> + pinctrl-0 = <&ecap1_pins>;
>> +};
>> +
>> +&spi0 {
>> + status = "okay";
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&spi0_pins>, <&spi0_cs0_pin>, <&spi0_cs3_pin>;
>> + dmas = <&edma0 14 0>, <&edma0 15 0>;
>> + dma-names = "rx", "tx";
>> +
>> + spi-flash at 0 {
>> + #address-cells = <1>;
>> + #size-cells = <1>;
>> + compatible = "n25q128a13", "jedec,spi-nor";
>> + reg = <0>;
>> + spi-max-frequency = <50000000>;
>> + ti,spi-wdelay = <8>;
>> +
>> + partition at 0 {
>> + label = "U-Boot";
>> + reg = <0 0x40000>;
>
> Thats 256KB for U-Boot and MLO (I assume in concatenated AIS image). Is
> that sufficient for future too? Moving partitions later is tough ask
> because that means users will lose data when they upgrade the kernel
> because of partitions moving around. Just a suggestion to keep future
> U-Boot bloat in mind and not use a "just fits" number.
The MLO is on an EEPROM in the EV3, so the U-Boot partition is just
U-boot. The SoC boots from I2C, which then runs whatever is as 0x0 on
the flash memory.
This partition table matches the partition scheme used on the official
LEGO firmware that ships with the devices. Most people running their own
kernel will probably be loading it from a microSD card, leaving the
official firmware intact and therefore will always have this partition
table.
My thinking is that if someone does want to use a different partitioning
scheme, they can build their own U-Boot and configure it to modify the
device tree with a new partition table.
The way the LEGO firmware flashing utility works, it wipes out the
entire flash memory each time you flash the firmware. So, data loss is
not a concern - you will loose your data anyway.
>
>> + };
>> +
>> + partition at 40000 {
>> + label = "U-Boot Env";
>> + reg = <0x40000 0x10000>;
>> + };
>> +
>> + partition at 50000 {
>> + label = "Kernel";
>> + reg = <0x50000 0x200000>;
>> + };
>> +
>> + partition at 250000 {
>> + label = "Filesystem";
>> + reg = <0x250000 0xa50000>;
>> + };
>> +
>> + partition at cb0000 {
>> + label = "Storage";
>> + reg = <0xcb0000 0x2f0000>;
>> + };
>> + };
>> +
>> + /* TODO: ADC goes here */
>
> I would drop this comment.
ack
>
>> +};
>> +
>> +&spi1 {
>> + status = "okay";
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&spi1_pins>, <&spi1_cs0_pin>;
>> +
>> + /* TODO: LCD Display goes here */
>
> Add this node when you actually have display working.
What if we set this up as a spidev node instead? This way the display
could be used from userspace without a driver.
^ permalink raw reply
* [linux-sunxi] [PATCH v5 3/4] Documentation: devicetree: add vendor prefix for Pine64
From: Maxime Ripard @ 2016-10-24 15:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CANwerB36N8q7ppKqocRs9m62h65+vrJFn37ncc-oh+Yozz_vFQ@mail.gmail.com>
Hi Jonathan,
On Fri, Oct 21, 2016 at 11:30:01AM +1100, Jonathan Liu wrote:
> On 21 October 2016 at 05:00, Maxime Ripard
> <maxime.ripard@free-electrons.com> wrote:
> > From: Andre Przywara <andre.przywara@arm.com>
> >
> > Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> > Acked-by: Rob Herring <robh@kernel.org>
> > Acked-by: Chen-Yu Tsai <wens@csie.org>
> > [Maxime: Change title prefix to match the usual style]
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > ---
> > Documentation/devicetree/bindings/vendor-prefixes.txt | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > index f0a48ea78659..4eefd1c3ff16 100644
> > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > @@ -208,7 +208,7 @@ parade Parade Technologies Inc.
> > pericom Pericom Technology Inc.
> > phytec PHYTEC Messtechnik GmbH
> > picochip Picochip Ltd
> > -pixcir PIXCIR MICROELECTRONICS Co., Ltd
>
> Why is "pixcir PIXCIR MICROELECTRONICS Co., Ltd" removed?
Probably a poor rebase on my side. Good catch, thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161024/66edcd0a/attachment.sig>
^ permalink raw reply
* [PATCH v4 4/9] clk: sunxi-ng: Add minimums for all the relevant structures and clocks
From: Maxime Ripard @ 2016-10-24 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <72be1b2a-e2db-7220-aa49-96d22860100c@arm.com>
On Fri, Oct 21, 2016 at 11:57:11PM +0100, Andr? Przywara wrote:
> Salut,
>
> On 11/10/16 15:28, Maxime Ripard wrote:
> > Modify the current clocks we have to be able to specify the minimum for
> > each clocks we support, just like we support the max.
> >
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > ---
> > drivers/clk/sunxi-ng/ccu_mult.c | 7 ++++++-
> > drivers/clk/sunxi-ng/ccu_nk.c | 12 ++++++++----
> > drivers/clk/sunxi-ng/ccu_nkm.c | 18 ++++++++++++------
> > drivers/clk/sunxi-ng/ccu_nkmp.c | 16 ++++++++++++----
> > drivers/clk/sunxi-ng/ccu_nm.c | 12 ++++++++----
> > 5 files changed, 46 insertions(+), 19 deletions(-)
> >
> > diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c
> > index 32a1964439a2..6a02ffee5386 100644
> > --- a/drivers/clk/sunxi-ng/ccu_mult.c
> > +++ b/drivers/clk/sunxi-ng/ccu_mult.c
> > @@ -14,7 +14,7 @@
> > #include "ccu_mult.h"
> >
> > struct _ccu_mult {
> > - unsigned long mult, max;
> > + unsigned long mult, min, max;
> > };
> >
> > static void ccu_mult_find_best(unsigned long parent, unsigned long rate,
> > @@ -23,6 +23,9 @@ static void ccu_mult_find_best(unsigned long parent, unsigned long rate,
> > int _mult;
> >
> > _mult = rate / parent;
> > + if (_mult < mult->min)
> > + _mult = mult->min;
> > +
> > if (_mult > mult->max)
> > _mult = mult->max;
> >
> > @@ -37,6 +40,7 @@ static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
> > struct ccu_mult *cm = data;
> > struct _ccu_mult _cm;
> >
> > + _cm.min = 1;
> > _cm.max = 1 << cm->mult.width;
> > ccu_mult_find_best(parent_rate, rate, &_cm);
> >
> > @@ -101,6 +105,7 @@ static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate,
> > ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
> > &parent_rate);
> >
> > + _cm.min = 1;
> > _cm.max = 1 << cm->mult.width;
> > ccu_mult_find_best(parent_rate, rate, &_cm);
> >
> > diff --git a/drivers/clk/sunxi-ng/ccu_nk.c b/drivers/clk/sunxi-ng/ccu_nk.c
> > index e7e2e75618ef..a42d870ba0ef 100644
> > --- a/drivers/clk/sunxi-ng/ccu_nk.c
> > +++ b/drivers/clk/sunxi-ng/ccu_nk.c
> > @@ -14,8 +14,8 @@
> > #include "ccu_nk.h"
> >
> > struct _ccu_nk {
> > - unsigned long n, max_n;
> > - unsigned long k, max_k;
> > + unsigned long n, min_n, max_n;
> > + unsigned long k, min_k, max_k;
> > };
> >
> > static void ccu_nk_find_best(unsigned long parent, unsigned long rate,
> > @@ -25,8 +25,8 @@ static void ccu_nk_find_best(unsigned long parent, unsigned long rate,
> > unsigned int best_k = 0, best_n = 0;
> > unsigned int _k, _n;
> >
> > - for (_k = 1; _k <= nk->max_k; _k++) {
> > - for (_n = 1; _n <= nk->max_n; _n++) {
> > + for (_k = nk->min_k; _k <= nk->max_k; _k++) {
> > + for (_n = nk->min_n; _n <= nk->max_n; _n++) {
> > unsigned long tmp_rate = parent * _n * _k;
> >
> > if (tmp_rate > rate)
> > @@ -97,7 +97,9 @@ static long ccu_nk_round_rate(struct clk_hw *hw, unsigned long rate,
> > if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
> > rate *= nk->fixed_post_div;
> >
> > + _nk.min_n = 1;
> > _nk.max_n = 1 << nk->n.width;
> > + _nk.min_k = 1;
> > _nk.max_k = 1 << nk->k.width;
> >
> > ccu_nk_find_best(*parent_rate, rate, &_nk);
> > @@ -120,7 +122,9 @@ static int ccu_nk_set_rate(struct clk_hw *hw, unsigned long rate,
> > if (nk->common.features & CCU_FEATURE_FIXED_POSTDIV)
> > rate = rate * nk->fixed_post_div;
> >
> > + _nk.min_n = 1;
> > _nk.max_n = 1 << nk->n.width;
> > + _nk.min_k = 1;
> > _nk.max_k = 1 << nk->k.width;
> >
> > ccu_nk_find_best(parent_rate, rate, &_nk);
> > diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c
> > index 0b08d000eb38..b2a5fccf2f8c 100644
> > --- a/drivers/clk/sunxi-ng/ccu_nkm.c
> > +++ b/drivers/clk/sunxi-ng/ccu_nkm.c
> > @@ -14,9 +14,9 @@
> > #include "ccu_nkm.h"
> >
> > struct _ccu_nkm {
> > - unsigned long n, max_n;
> > - unsigned long k, max_k;
> > - unsigned long m, max_m;
> > + unsigned long n, min_n, max_n;
> > + unsigned long k, min_k, max_k;
> > + unsigned long m, min_m, max_m;
> > };
> >
> > static void ccu_nkm_find_best(unsigned long parent, unsigned long rate,
> > @@ -26,9 +26,9 @@ static void ccu_nkm_find_best(unsigned long parent, unsigned long rate,
> > unsigned long best_n = 0, best_k = 0, best_m = 0;
> > unsigned long _n, _k, _m;
> >
> > - for (_k = 1; _k <= nkm->max_k; _k++) {
> > - for (_n = 1; _n <= nkm->max_n; _n++) {
> > - for (_m = 1; _n <= nkm->max_m; _m++) {
> > + for (_k = nkm->min_k; _k <= nkm->max_k; _k++) {
> > + for (_n = nkm->min_n; _n <= nkm->max_n; _n++) {
> > + for (_m = nkm->min_m; _n <= nkm->max_m; _m++) {
>
> should be _m in the condition
>
> > unsigned long tmp_rate;
> >
> > tmp_rate = parent * _n * _k / _m;
> > @@ -100,8 +100,11 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
> > struct ccu_nkm *nkm = data;
> > struct _ccu_nkm _nkm;
> >
> > + _nkm.min_n = 1;
> > _nkm.max_n = 1 << nkm->n.width;
> > + _nkm.min_k = 1;
> > _nkm.max_k = 1 << nkm->k.width;
> > + _nkm.min_m = 1;
> > _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
> >
> > ccu_nkm_find_best(parent_rate, rate, &_nkm);
> > @@ -126,8 +129,11 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
> > unsigned long flags;
> > u32 reg;
> >
> > + _nkm.min_n = 1;
> > _nkm.max_n = 1 << nkm->n.width;
> > + _nkm.min_k = 1;
> > _nkm.max_k = 1 << nkm->k.width;
> > + _nkm.min_m = 1;
> > _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
> >
> > ccu_nkm_find_best(parent_rate, rate, &_nkm);
> > diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c
> > index 4b457d8cce11..2c1398192e48 100644
> > --- a/drivers/clk/sunxi-ng/ccu_nkmp.c
> > +++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
> > @@ -27,10 +27,10 @@ static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate,
> > unsigned long best_n = 0, best_k = 0, best_m = 0, best_p = 0;
> > unsigned long _n, _k, _m, _p;
> >
> > - for (_k = 1; _k <= nkmp->max_k; _k++) {
> > - for (_n = 1; _n <= nkm->max_n; _n++) {
> > - for (_m = 1; _n <= nkm->max_m; _m++) {
> > - for (_p = 1; _p <= nkmp->max_p; _p <<= 1) {
> > + for (_k = nkmp->min_k; _k <= nkmp->max_k; _k++) {
> > + for (_n = nkmp->min_n; _n <= nkmp->max_n; _n++) {
> > + for (_m = nkmp->min_m; _n <= nkmp->max_m; _m++) {
>
> Same here: _m <= ...
>
> > + for (_p = nkmp->min_p; _p <= nkmp->max_p; _p <<= 1) {
> > unsigned long tmp_rate;
> >
> > tmp_rate = parent * _n * _k / (_m * _p);
> > @@ -107,9 +107,13 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate,
> > struct ccu_nkmp *nkmp = hw_to_ccu_nkmp(hw);
> > struct _ccu_nkmp _nkmp;
> >
> > + _nkmp.min_n = 1;
> > _nkmp.max_n = 1 << nkmp->n.width;
> > + _nkmp.min_k = 1;
> > _nkmp.max_k = 1 << nkmp->k.width;
> > + _nkmp.min_m = 1;
> > _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
> > + _nkmp.min_p = 1;
> > _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
> >
> > ccu_nkmp_find_best(*parent_rate, rate, &_nkmp);
> > @@ -125,9 +129,13 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
> > unsigned long flags;
> > u32 reg;
> >
> > + _nkmp.min_n = 1;
> > _nkmp.max_n = 1 << nkmp->n.width;
> > + _nkmp.min_k = 1;
> > _nkmp.max_k = 1 << nkmp->k.width;
> > + _nkmp.min_m = 1;
> > _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
> > + _nkmp.min_p = 1;
> > _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
> >
> > ccu_nkmp_find_best(parent_rate, rate, &_nkmp);
> > diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c
> > index c6d652289320..2a190bc032a9 100644
> > --- a/drivers/clk/sunxi-ng/ccu_nm.c
> > +++ b/drivers/clk/sunxi-ng/ccu_nm.c
> > @@ -15,8 +15,8 @@
> > #include "ccu_nm.h"
> >
> > struct _ccu_nm {
> > - unsigned long n, max_n;
> > - unsigned long m, max_m;
> > + unsigned long n, min_n, max_n;
> > + unsigned long m, min_m, max_m;
> > };
> >
> > static void ccu_nm_find_best(unsigned long parent, unsigned long rate,
> > @@ -26,8 +26,8 @@ static void ccu_nm_find_best(unsigned long parent, unsigned long rate,
> > unsigned long best_n = 0, best_m = 0;
> > unsigned long _n, _m;
> >
> > - for (_n = 1; _n <= nm->max_n; _n++) {
> > - for (_m = 1; _n <= nm->max_m; _m++) {
> > + for (_n = nm->min_n; _n <= nm->max_n; _n++) {
> > + for (_m = nm->min_m; _n <= nm->max_m; _m++) {
>
> And here: _m <= ...
>
> Interestingly those typos were already in the code before.
Good catches, I've amended the commit in my branch (since I apparently
forgot to push...). Thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161024/c7ee7fb8/attachment.sig>
^ permalink raw reply
* Disabling an interrupt in the handler locks the system up
From: Mason @ 2016-10-24 16:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <580BF1D4.2030509@free.fr>
On 23/10/2016 01:10, Mason wrote:
> Maybe the fact that disable_irq locks the system up is an orthogonal
> issue that needs to be fixed anyway.
disable_irq_nosync() eventually calls irq_disable()
void irq_disable(struct irq_desc *desc)
{
irq_state_set_disabled(desc);
if (desc->irq_data.chip->irq_disable) {
desc->irq_data.chip->irq_disable(&desc->irq_data);
irq_state_set_masked(desc);
} else if (irq_settings_disable_unlazy(desc)) {
mask_irq(desc);
}
}
irq_disable() is a NOP on my platform, because the intc driver does
not implement irq_disable, and the second test is false as well in
this instance.
The function's description is interesting.
/**
* irq_disable - Mark interrupt disabled
* @desc: irq descriptor which should be disabled
*
* If the chip does not implement the irq_disable callback, we
* use a lazy disable approach. That means we mark the interrupt
* disabled, but leave the hardware unmasked. That's an
* optimization because we avoid the hardware access for the
* common case where no interrupt happens after we marked it
* disabled. If an interrupt happens, then the interrupt flow
* handler masks the line at the hardware level and marks it
* pending.
*
* If the interrupt chip does not implement the irq_disable callback,
* a driver can disable the lazy approach for a particular irq line by
* calling 'irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY)'. This can
* be used for devices which cannot disable the interrupt at the
* device level under certain circumstances and have to use
* disable_irq[_nosync] instead.
*/
(I assume "chip" and "interrupt chip" refer to the same abstraction.)
I took a look at commit e9849777d0e27, but my brain dumped core on
the notions of "disabling unlazy" and "disabling a disable".
* IRQ_DISABLE_UNLAZY - Disable lazy irq disable
For the record, setting the IRQ_DISABLE_UNLAZY flag for this device
makes the system lock-up disappear.
Regards.
^ permalink raw reply
* [PATCH v3 [fix]] PM / doc: Update device documentation for devices in IRQ safe PM domains
From: Lina Iyer @ 2016-10-24 16:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1900178.E2KGyKAlTm@vostro.rjw.lan>
On Sat, Oct 22 2016 at 18:19 -0600, Rafael J. Wysocki wrote:
>On Friday, October 21, 2016 03:52:55 PM Lina Iyer wrote:
>> Update documentation to reflect the changes made to support IRQ safe PM
>> domains.
>>
>> Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
>> Acked-by: Ulf Hansson <ulf.hansson@linaro.org>
>> ---
>> Changes since v3:
>> - Moved para to the end of the section
>> - Added clause for all IRQ safe devices in a domain
>> - Cleanup explanation of nested domains
>> ---
>> Documentation/power/devices.txt | 11 ++++++++++-
>> 1 file changed, 10 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/power/devices.txt b/Documentation/power/devices.txt
>> index 8ba6625..9218ce6 100644
>> --- a/Documentation/power/devices.txt
>> +++ b/Documentation/power/devices.txt
>> @@ -607,7 +607,9 @@ individually. Instead, a set of devices sharing a power resource can be put
>> into a low-power state together at the same time by turning off the shared
>> power resource. Of course, they also need to be put into the full-power state
>> together, by turning the shared power resource on. A set of devices with this
>> -property is often referred to as a power domain.
>> +property is often referred to as a power domain. A power domain may also be
>> +nested inside another power domain. The nested domain is referred to as the
>> +sub-domain of the parent domain.
>>
>> Support for power domains is provided through the pm_domain field of struct
>> device. This field is a pointer to an object of type struct dev_pm_domain,
>> @@ -629,6 +631,13 @@ support for power domains into subsystem-level callbacks, for example by
>> modifying the platform bus type. Other platforms need not implement it or take
>> it into account in any way.
>>
>> +Devices and PM domains may be defined as IRQ-safe, if they can be powered
>> +on/off even when the IRQs are disabled.
>
>What IRQ-safe means for devices is that their runtime PM callbacks may be
>invoked with interrupts disabled on the local CPU. I guess the meaning of
>IRQ-safe for PM domains is analogous, but the above isn't precise enough to me.
>
>> An IRQ-safe device in a domain will
>> +disallow power management on the domain, unless the domain is also defined as
>> +IRQ-safe. In other words, a domain containing all IRQ-safe devices must also
>> +be defined as IRQ-safe. Another restriction this framework imposes on the
>> +parent domain of an IRQ-safe domain is that the parent domain must also be
>> +defined as IRQ-safe.
>
>What about this:
>
>"Devices may be defined as IRQ-safe which indicates to the PM core that their
>runtime PM callbacks may be invoked with disabled interrupts (see
>Documentation/power/runtime_pm.txt for more information). If an IRQ-safe
>device belongs to a PM domain, the runtime PM of the domain will be disallowed,
>unless the domain itself is defined as IRQ-safe. However, a PM domain can only
>be defined as IRQ-safe if all of the devices in it are IRQ-safe.
>
This is correct. But the last line may need a bit of modification. If
all devices in a PM domain are IRQ-safe and the domain is NOT, then it
it is a valid combination just that the domain would never do runtime
PM.
> Moreover, if
>an IRQ-safe domain has a parent domain, the runtime PM of the parent is only
>allowed if the parent itself is IRQ-safe too with the additional restriction
>that all child domains of an IRQ-safe parent must also be IRQ-safe."
>
>Does it actually reflect what the code does?
>
Yes, this precisely reflects the code.
Thanks,
Lina
^ 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