[PATCH v2 3/4] pwm: jz4740: Make PWM start with the active part

2020-05-25 Thread Paul Cercueil
The PWM in Ingenic SoCs starts in inactive state until the internal
timer reaches the duty value, then becomes active until the timer
reaches the period value. In theory, we should then use (period - duty)
as the real duty value, as a high duty value would otherwise result in
the PWM pin being inactive most of the time.

This is the reason why the duty value was inverted in the driver until
now, but it still had the problem that it would not start with the
active part.

To address this remaining issue, the common trick is to invert the
duty, and invert the polarity when the PWM is enabled.

Since the duty was already inverted, and we invert it again, we now
program the hardware for the requested duty, and simply invert the
polarity when the PWM is enabled.

Signed-off-by: Paul Cercueil 
---

Notes:
v2: Add documentation about why we invert the polarity, and
improve commit message

 drivers/pwm/pwm-jz4740.c | 25 -
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index 4fe9d99ac9a9..fe06ca8ce30f 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -6,7 +6,6 @@
  * Limitations:
  * - The .apply callback doesn't complete the currently running period before
  *   reconfiguring the hardware.
- * - Each period starts with the inactive part.
  */
 
 #include 
@@ -163,7 +162,7 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct 
pwm_device *pwm,
/* Calculate duty value */
tmp = (unsigned long long)rate * state->duty_cycle;
do_div(tmp, NSEC_PER_SEC);
-   duty = period - tmp;
+   duty = tmp;
 
if (duty >= period)
duty = period - 1;
@@ -189,18 +188,26 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct 
pwm_device *pwm,
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
   TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);
 
-   /* Set polarity */
-   switch (state->polarity) {
-   case PWM_POLARITY_NORMAL:
+   /*
+* Set polarity.
+*
+* The PWM starts in inactive state until the internal timer reaches the
+* duty value, then becomes active until the timer reaches the period
+* value. In theory, we should then use (period - duty) as the real duty
+* value, as a high duty value would otherwise result in the PWM pin
+* being inactive most of the time.
+*
+* Here, we don't do that, and instead invert the polarity of the PWM
+* when it is active. This trick makes the PWM start with its active
+* state instead of its inactive state.
+*/
+   if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled)
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
   TCU_TCSR_PWM_INITL_HIGH, 0);
-   break;
-   case PWM_POLARITY_INVERSED:
+   else
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
   TCU_TCSR_PWM_INITL_HIGH,
   TCU_TCSR_PWM_INITL_HIGH);
-   break;
-   }
 
if (state->enabled)
jz4740_pwm_enable(chip, pwm);
-- 
2.26.2



Re: [PATCH 3/3] pwm: jz4740: Add support for the JZ4725B

2020-05-24 Thread Paul Cercueil

Hi Uwe,

Le dim. 24 mai 2020 à 19:37, Uwe Kleine-König 
 a écrit :

On Mon, Apr 13, 2020 at 02:14:45PM +0200, Paul Cercueil wrote:
 The PWM hardware in the JZ4725B works the same as in the JZ4740, 
but has

 only six channels available.

 Signed-off-by: Paul Cercueil 
 ---

 Notes:
 I did not add documentation for the new jz4725b-pwm compatible
 string on purpose. The reason is that the documentation file
 for the Timer/Counter Unit (TCU) of Ingenic SoCs will be
 completely rewritten from .txt to YAML in a separate patchset.

  drivers/pwm/pwm-jz4740.c | 20 +---
  1 file changed, 17 insertions(+), 3 deletions(-)

 diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
 index f566f9d248d6..bb27934fb6c2 100644
 --- a/drivers/pwm/pwm-jz4740.c
 +++ b/drivers/pwm/pwm-jz4740.c
 @@ -22,6 +22,10 @@

  #define NUM_PWM 8

 +struct soc_info {
 +  unsigned int num_pwms;
 +};
 +
  struct jz4740_pwm_chip {
struct pwm_chip chip;
struct regmap *map;
 @@ -36,7 +40,7 @@ static bool jz4740_pwm_can_use_chn(struct 
jz4740_pwm_chip *jz,

   unsigned int channel)
  {
  	/* Enable all TCU channels for PWM use by default except channels 
0/1 */

 -  u32 pwm_channels_mask = GENMASK(NUM_PWM - 1, 2);
 +  u32 pwm_channels_mask = GENMASK(jz->chip.npwm - 1, 2);

device_property_read_u32(jz->chip.dev->parent,
 "ingenic,pwm-channels-mask",
 @@ -214,6 +218,7 @@ static int jz4740_pwm_probe(struct 
platform_device *pdev)

  {
struct device *dev = >dev;
struct jz4740_pwm_chip *jz4740;
 +  const struct soc_info *info = device_get_match_data(dev);

jz4740 = devm_kzalloc(dev, sizeof(*jz4740), GFP_KERNEL);
if (!jz4740)
 @@ -227,8 +232,8 @@ static int jz4740_pwm_probe(struct 
platform_device *pdev)


jz4740->chip.dev = dev;
jz4740->chip.ops = _pwm_ops;
 -  jz4740->chip.npwm = NUM_PWM;
jz4740->chip.base = -1;
 +  jz4740->chip.npwm = info ? info->num_pwms : NUM_PWM;


Can info be actually NULL? I don't think so, so you can just use
info->num_pwms here and drop the definition of NUM_PWM.


In *theory* it can be NULL if the kernel is configured without 
CONFIG_OF, which will never happen on any board supported by this 
driver. I can add a dependency on CONFIG_OF in V2, then use 
info->num_pwms there.


cheers,
-Paul


jz4740->chip.of_xlate = of_pwm_xlate_with_flags;
jz4740->chip.of_pwm_n_cells = 3;



Best regards
Uwe

--
Pengutronix e.K.   | Uwe Kleine-König
|
Industrial Linux Solutions | 
https://www.pengutronix.de/ |





Re: [PATCH 1/3] pwm: jz4740: Drop dependency on MACH_INGENIC

2020-05-24 Thread Paul Cercueil

Hi,

Any feedback on this patchset?

Cheers,
-Paul


Le lun. 13 avril 2020 à 14:14, Paul Cercueil  a 
écrit :
Depending on MACH_INGENIC prevent us from creating a generic kernel 
that
works on more than one MIPS board. Instead, we just depend on MIPS 
being

set.

Signed-off-by: Paul Cercueil 
---
 drivers/pwm/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index eebbc917ac97..7814e5b2cad7 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -234,7 +234,7 @@ config PWM_IMX_TPM

 config PWM_JZ4740
tristate "Ingenic JZ47xx PWM support"
-   depends on MACH_INGENIC
+   depends on MIPS
depends on COMMON_CLK
select MFD_SYSCON
help
--
2.25.1






[PATCH] ASoC: ingenic: Unconditionally depend on devicetree

2020-05-23 Thread Paul Cercueil
All boards with Ingenic SoCs probe with devicetree already, we have no
use for a non-devicetree path.

This solves some compilation warnings that were caused by unused
variables in the case where CONFIG_OF was disabled.

Signed-off-by: Paul Cercueil 
Reported-by: kbuild test robot 
---
 sound/soc/codecs/Kconfig  | 3 +++
 sound/soc/codecs/jz4725b.c| 4 +---
 sound/soc/codecs/jz4740.c | 4 +---
 sound/soc/codecs/jz4770.c | 2 +-
 sound/soc/jz4740/Kconfig  | 2 +-
 sound/soc/jz4740/jz4740-i2s.c | 4 +---
 6 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index e60e0b6a689c..3a0a6824e278 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -681,6 +681,7 @@ config SND_SOC_CX2072X
 
 config SND_SOC_JZ4740_CODEC
depends on MIPS || COMPILE_TEST
+   depends on OF
select REGMAP_MMIO
tristate "Ingenic JZ4740 internal CODEC"
help
@@ -692,6 +693,7 @@ config SND_SOC_JZ4740_CODEC
 
 config SND_SOC_JZ4725B_CODEC
depends on MIPS || COMPILE_TEST
+   depends on OF
select REGMAP
tristate "Ingenic JZ4725B internal CODEC"
help
@@ -703,6 +705,7 @@ config SND_SOC_JZ4725B_CODEC
 
 config SND_SOC_JZ4770_CODEC
depends on MIPS || COMPILE_TEST
+   depends on OF
select REGMAP
tristate "Ingenic JZ4770 internal CODEC"
help
diff --git a/sound/soc/codecs/jz4725b.c b/sound/soc/codecs/jz4725b.c
index 2567a5d15b55..e49374c72e70 100644
--- a/sound/soc/codecs/jz4725b.c
+++ b/sound/soc/codecs/jz4725b.c
@@ -574,19 +574,17 @@ static int jz4725b_codec_probe(struct platform_device 
*pdev)
return ret;
 }
 
-#ifdef CONFIG_OF
 static const struct of_device_id jz4725b_codec_of_matches[] = {
{ .compatible = "ingenic,jz4725b-codec", },
{ }
 };
 MODULE_DEVICE_TABLE(of, jz4725b_codec_of_matches);
-#endif
 
 static struct platform_driver jz4725b_codec_driver = {
.probe = jz4725b_codec_probe,
.driver = {
.name = "jz4725b-codec",
-   .of_match_table = of_match_ptr(jz4725b_codec_of_matches),
+   .of_match_table = jz4725b_codec_of_matches,
},
 };
 module_platform_driver(jz4725b_codec_driver);
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 460aa1fd1efe..c9900d1cd5c2 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -344,19 +344,17 @@ static int jz4740_codec_probe(struct platform_device 
*pdev)
return ret;
 }
 
-#ifdef CONFIG_OF
 static const struct of_device_id jz4740_codec_of_matches[] = {
{ .compatible = "ingenic,jz4740-codec", },
{ }
 };
 MODULE_DEVICE_TABLE(of, jz4740_codec_of_matches);
-#endif
 
 static struct platform_driver jz4740_codec_driver = {
.probe = jz4740_codec_probe,
.driver = {
.name = "jz4740-codec",
-   .of_match_table = of_match_ptr(jz4740_codec_of_matches),
+   .of_match_table = jz4740_codec_of_matches,
},
 };
 
diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c
index e7cf2c107607..34775aa62402 100644
--- a/sound/soc/codecs/jz4770.c
+++ b/sound/soc/codecs/jz4770.c
@@ -937,7 +937,7 @@ static struct platform_driver jz4770_codec_driver = {
.probe  = jz4770_codec_probe,
.driver = {
.name   = "jz4770-codec",
-   .of_match_table = of_match_ptr(jz4770_codec_of_matches),
+   .of_match_table = jz4770_codec_of_matches,
},
 };
 module_platform_driver(jz4770_codec_driver);
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
index e72f826062e9..29144720cb62 100644
--- a/sound/soc/jz4740/Kconfig
+++ b/sound/soc/jz4740/Kconfig
@@ -2,7 +2,7 @@
 config SND_JZ4740_SOC_I2S
tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC"
depends on MIPS || COMPILE_TEST
-   depends on HAS_IOMEM
+   depends on OF && HAS_IOMEM
select SND_SOC_GENERIC_DMAENGINE_PCM
help
  Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index 6f6f8dad0356..52460adf6ca1 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -504,7 +504,6 @@ static const struct snd_soc_component_driver 
jz4740_i2s_component = {
.resume = jz4740_i2s_resume,
 };
 
-#ifdef CONFIG_OF
 static const struct of_device_id jz4740_of_matches[] = {
{ .compatible = "ingenic,jz4740-i2s", .data = _i2s_soc_info },
{ .compatible = "ingenic,jz4760-i2s", .data = _i2s_soc_info },
@@ -513,7 +512,6 @@ static const struct of_device_id jz4740_of_matches[] = {
{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, jz4740_of_matches);
-#endif
 
 static int jz4

Re: [PATCH v7 3/5] remoteproc: Add support for runtime PM

2020-05-22 Thread Paul Cercueil

Hi Suman,

Le ven. 22 mai 2020 à 11:47, Suman Anna  a écrit :

Hi Paul,

On 5/15/20 5:43 AM, Paul Cercueil wrote:

Call pm_runtime_get_sync() before the firmware is loaded, and
pm_runtime_put() after the remote processor has been stopped.

Even though the remoteproc device has no PM callbacks, this allows 
the

parent device's PM callbacks to be properly called.


I see this patch staged now for 5.8, and the latest -next branch has 
broken the pm-runtime autosuspend feature we have in the OMAP 
remoteproc driver. See commit 5f31b232c674 ("remoteproc/omap: Add 
support for runtime auto-suspend/resume").


What was the original purpose of this patch, because there can be 
differing backends across different SoCs.


Did you try pm_suspend_ignore_children()? It looks like it was made for 
your use-case.


Cheers,
-Paul



regards
Suman



Signed-off-by: Paul Cercueil 
---

Notes:
 v2-v4: No change
 v5: Move calls to prepare/unprepare to 
rproc_fw_boot/rproc_shutdown
 v6: Instead of prepare/unprepare callbacks, use PM runtime 
callbacks

 v7: Check return value of pm_runtime_get_sync()

  drivers/remoteproc/remoteproc_core.c | 17 -
  1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/remoteproc/remoteproc_core.c 
b/drivers/remoteproc/remoteproc_core.c

index a7f96bc98406..e33d1ef27981 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -29,6 +29,7 @@
  #include 
  #include 
  #include 
+#include 
  #include 
  #include 
  #include 
@@ -1382,6 +1383,12 @@ static int rproc_fw_boot(struct rproc *rproc, 
const struct firmware *fw)

if (ret)
return ret;
  +ret = pm_runtime_get_sync(dev);
+   if (ret < 0) {
+   dev_err(dev, "pm_runtime_get_sync failed: %d\n", ret);
+   return ret;
+   }
+
dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
   /*
@@ -1391,7 +1398,7 @@ static int rproc_fw_boot(struct rproc *rproc, 
const struct firmware *fw)

ret = rproc_enable_iommu(rproc);
if (ret) {
dev_err(dev, "can't enable iommu: %d\n", ret);
-   return ret;
+   goto put_pm_runtime;
}
   rproc->bootaddr = rproc_get_boot_addr(rproc, fw);
@@ -1435,6 +1442,8 @@ static int rproc_fw_boot(struct rproc *rproc, 
const struct firmware *fw)

rproc->table_ptr = NULL;
  disable_iommu:
rproc_disable_iommu(rproc);
+put_pm_runtime:
+   pm_runtime_put(dev);
return ret;
  }
  @@ -1840,6 +1849,8 @@ void rproc_shutdown(struct rproc *rproc)
   rproc_disable_iommu(rproc);
  +pm_runtime_put(dev);
+
/* Free the copy of the resource table */
kfree(rproc->cached_table);
rproc->cached_table = NULL;
@@ -2118,6 +2129,9 @@ struct rproc *rproc_alloc(struct device *dev, 
const char *name,

   rproc->state = RPROC_OFFLINE;
  +pm_runtime_no_callbacks(>dev);
+   pm_runtime_enable(>dev);
+
return rproc;
  }
  EXPORT_SYMBOL(rproc_alloc);
@@ -2133,6 +2147,7 @@ EXPORT_SYMBOL(rproc_alloc);
   */
  void rproc_free(struct rproc *rproc)
  {
+   pm_runtime_disable(>dev);
put_device(>dev);
  }
  EXPORT_SYMBOL(rproc_free);








Re: [PATCH] usb: musb: jz4740: Prevent lockup when CONFIG_SMP is set

2020-05-21 Thread Paul Cercueil

Hi Bin,

The patch it fixes was introduced in 5.7-rc1, is it possible to queue 
it for the next -rc? Otherwise I'll need to Cc it to linux-stable.


-Paul

Le jeu. 21 mai 2020 à 15:34, Bin Liu  a écrit :

On Wed, May 20, 2020 at 05:01:11PM +0200, Paul Cercueil wrote:
 The function dma_controller_irq() locks up the exact same spinlock 
we
 locked before calling it, which obviously resulted in a deadlock 
when
 CONFIG_SMP was enabled. This flew under the radar as none of the 
boards

 supported by this driver needs SMP.

 Fixes: 57aadb46bd63 ("usb: musb: jz4740: Add support for DMA")
 Signed-off-by: Paul Cercueil 


Queued for v5.8. Thanks.
-Bin.





[PATCH] usb: musb: jz4740: Prevent lockup when CONFIG_SMP is set

2020-05-20 Thread Paul Cercueil
The function dma_controller_irq() locks up the exact same spinlock we
locked before calling it, which obviously resulted in a deadlock when
CONFIG_SMP was enabled. This flew under the radar as none of the boards
supported by this driver needs SMP.

Fixes: 57aadb46bd63 ("usb: musb: jz4740: Add support for DMA")
Signed-off-by: Paul Cercueil 
---
 drivers/usb/musb/jz4740.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c
index 54e7b30acc69..42c1e8bfc4be 100644
--- a/drivers/usb/musb/jz4740.c
+++ b/drivers/usb/musb/jz4740.c
@@ -69,11 +69,11 @@ static irqreturn_t jz4740_musb_interrupt(int irq, void 
*__hci)
irqreturn_t retval = IRQ_NONE, retval_dma = IRQ_NONE;
struct musb *musb = __hci;
 
-   spin_lock_irqsave(>lock, flags);
-
if (IS_ENABLED(CONFIG_USB_INVENTRA_DMA) && musb->dma_controller)
retval_dma = dma_controller_irq(irq, musb->dma_controller);
 
+   spin_lock_irqsave(>lock, flags);
+
musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
-- 
2.26.2



Re: [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.

2020-05-20 Thread Paul Cercueil

Hi Zhou,

Le mer. 20 mai 2020 à 15:23, Zhou Yanjie  a 
écrit :

Hi Paul,

On 2020年05月20日 03:41, Paul Cercueil wrote:

Hi Zhou,

Le mar. 19 mai 2020 à 22:35, 周琰杰 (Zhou Yanjie) 
 a écrit :

Forward port smp support from kernel 3.18.3 of CI20_linux
to upstream kernel 5.6.

Tested-by: H. Nikolaus Schaller 
Tested-by: Paul Boddie 
Signed-off-by: 周琰杰 (Zhou Yanjie) 
Reviewed-by: Jiaxun Yang 
---

Notes:
v1->v2:
1.Remove unnecessary "plat_irq_dispatch(void)" in irq-ingenic.c.
2.Add a timeout check for "jz4780_boot_secondary()" to avoid a 
dead loop.

3.Replace hard code in smp.c with macro.

v2->v3:
1.Remove unnecessary "extern void (*r4k_blast_dcache)(void)" in 
smp.c.
2.Use "for_each_of_cpu_node" instead "for_each_compatible_node" 
in smp.c.
3.Use "of_cpu_node_to_id" instead "of_property_read_u32_index" 
in smp.c.

4.Move LCR related operations to jz4780-cgu.c.

v3->v4:
Rebase on top of kernel 5.6-rc1.

v4->v5:
1.Splitting changes involving "jz4780-cgu.c" into separate 
commit.

2.Use "request_irq()" replace "setup_irq()".

v5->v6:
In order to have a kernel that works on multiple SoCs at the 
same

time, use "IS_ENABLED()" replace "#ifdef".

v6->v7:
1.SMP has be decoupled from the SoC version.
2.Add mailboxes 3 and 4 for XBurst.
3.Adjust code in "jz4780_smp_prepare_cpus()".
4."jz4780_smp_init()" has be marked "__init".

v7->v8:
No change.

 arch/mips/include/asm/mach-jz4740/smp.h |  87 +++
 arch/mips/jz4740/Kconfig|   2 +
 arch/mips/jz4740/Makefile   |   5 +
 arch/mips/jz4740/prom.c |   4 +
 arch/mips/jz4740/smp-entry.S|  57 +++
 arch/mips/jz4740/smp.c  | 258 


 arch/mips/kernel/idle.c |  35 -
 7 files changed, 447 insertions(+), 1 deletion(-)
 create mode 100644 arch/mips/include/asm/mach-jz4740/smp.h
 create mode 100644 arch/mips/jz4740/smp-entry.S
 create mode 100644 arch/mips/jz4740/smp.c

diff --git a/arch/mips/include/asm/mach-jz4740/smp.h 
b/arch/mips/include/asm/mach-jz4740/smp.h

new file mode 100644
index ..86f660f
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/smp.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *  Copyright (C) 2013, Paul Burton 
+ *  JZ4780 SMP definitions
+ */
+
+#ifndef __MIPS_ASM_MACH_JZ4740_SMP_H__
+#define __MIPS_ASM_MACH_JZ4740_SMP_H__
+
+#define read_c0_corectrl()__read_32bit_c0_register($12, 2)
+#define write_c0_corectrl(val) __write_32bit_c0_register($12, 2, 
val)

+
+#define read_c0_corestatus() __read_32bit_c0_register($12, 3)
+#define write_c0_corestatus(val) __write_32bit_c0_register($12, 3, 
val)

+
+#define read_c0_reim()__read_32bit_c0_register($12, 4)
+#define write_c0_reim(val) __write_32bit_c0_register($12, 4, val)
+
+#define read_c0_mailbox0()__read_32bit_c0_register($20, 0)
+#define write_c0_mailbox0(val) __write_32bit_c0_register($20, 0, 
val)

+
+#define read_c0_mailbox1()__read_32bit_c0_register($20, 1)
+#define write_c0_mailbox1(val) __write_32bit_c0_register($20, 1, 
val)

+
+#define read_c0_mailbox2()__read_32bit_c0_register($20, 2)
+#define write_c0_mailbox2(val) __write_32bit_c0_register($20, 2, 
val)

+
+#define read_c0_mailbox3()__read_32bit_c0_register($20, 3)
+#define write_c0_mailbox3(val) __write_32bit_c0_register($20, 3, 
val)

+
+#define smp_clr_pending(mask) do {\
+unsigned int stat;\
+stat = read_c0_corestatus();\
+stat &= ~((mask) & 0xff);\
+write_c0_corestatus(stat);\
+} while (0)
+
+/*
+ * Core Control register
+ */
+#define CORECTRL_SLEEP1M_SHIFT17
+#define CORECTRL_SLEEP1M(_ULCAST_(0x1) << 
CORECTRL_SLEEP1M_SHIFT)

+#define CORECTRL_SLEEP0M_SHIFT16
+#define CORECTRL_SLEEP0M(_ULCAST_(0x1) << 
CORECTRL_SLEEP0M_SHIFT)

+#define CORECTRL_RPC1_SHIFT9
+#define CORECTRL_RPC1(_ULCAST_(0x1) << CORECTRL_RPC1_SHIFT)
+#define CORECTRL_RPC0_SHIFT8
+#define CORECTRL_RPC0(_ULCAST_(0x1) << CORECTRL_RPC0_SHIFT)
+#define CORECTRL_SWRST1_SHIFT1
+#define CORECTRL_SWRST1(_ULCAST_(0x1) << 
CORECTRL_SWRST1_SHIFT)

+#define CORECTRL_SWRST0_SHIFT0
+#define CORECTRL_SWRST0(_ULCAST_(0x1) << 
CORECTRL_SWRST0_SHIFT)

+
+/*
+ * Core Status register
+ */
+#define CORESTATUS_SLEEP1_SHIFT17
+#define CORESTATUS_SLEEP1(_ULCAST_(0x1) << 
CORESTATUS_SLEEP1_SHIFT)

+#define CORESTATUS_SLEEP0_SHIFT16
+#define CORESTATUS_SLEEP0(_ULCAST_(0x1) << 
CORESTATUS_SLEEP0_SHIFT)

+#define CORESTATUS_IRQ1P_SHIFT9
+#define CORESTATUS_IRQ1P(_ULCAST_(0x1) << 
CORESTATUS_I

[PATCH v2 1/3] dt-bindings: pinctrl: Convert ingenic,pinctrl.txt to YAML

2020-05-19 Thread Paul Cercueil
Convert the ingenic,pinctrl.txt doc file to ingenic,pinctrl.yaml.

In the process, some compatible strings now require a fallback, as the
corresponding SoCs are pin-compatible with their fallback variant.

Signed-off-by: Paul Cercueil 
---

Notes:
v2: - Use 'pinctrl' instead of 'pin-controller' as the node name
- remove 'additionalProperties: false' since we will have pin conf nodes

 .../bindings/pinctrl/ingenic,pinctrl.txt  |  81 ---
 .../bindings/pinctrl/ingenic,pinctrl.yaml | 136 ++
 2 files changed, 136 insertions(+), 81 deletions(-)
 delete mode 100644 
Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt
 create mode 100644 
Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.yaml

diff --git a/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt 
b/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt
deleted file mode 100644
index d9b2100c98e8..
--- a/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.txt
+++ /dev/null
@@ -1,81 +0,0 @@
-Ingenic XBurst pin controller
-
-Please refer to pinctrl-bindings.txt in this directory for details of the
-common pinctrl bindings used by client devices, including the meaning of the
-phrase "pin configuration node".
-
-For the XBurst SoCs, pin control is tightly bound with GPIO ports. All pins may
-be used as GPIOs, multiplexed device functions are configured within the
-GPIO port configuration registers and it is typical to refer to pins using the
-naming scheme "PxN" where x is a character identifying the GPIO port with
-which the pin is associated and N is an integer from 0 to 31 identifying the
-pin within that GPIO port. For example PA0 is the first pin in GPIO port A, and
-PB31 is the last pin in GPIO port B. The jz4740, the x1000 and the x1830
-contains 4 GPIO ports, PA to PD, for a total of 128 pins. The jz4760, the
-jz4770 and the jz4780 contains 6 GPIO ports, PA to PF, for a total of 192 pins.
-
-
-Required properties:
-
-
- - compatible: One of:
-- "ingenic,jz4740-pinctrl"
-- "ingenic,jz4725b-pinctrl"
-- "ingenic,jz4760-pinctrl"
-- "ingenic,jz4760b-pinctrl"
-- "ingenic,jz4770-pinctrl"
-- "ingenic,jz4780-pinctrl"
-- "ingenic,x1000-pinctrl"
-- "ingenic,x1000e-pinctrl"
-- "ingenic,x1500-pinctrl"
-- "ingenic,x1830-pinctrl"
- - reg: Address range of the pinctrl registers.
-
-
-Required properties for sub-nodes (GPIO chips):

-
- - compatible: Must contain one of:
-- "ingenic,jz4740-gpio"
-- "ingenic,jz4760-gpio"
-- "ingenic,jz4770-gpio"
-- "ingenic,jz4780-gpio"
-- "ingenic,x1000-gpio"
-- "ingenic,x1830-gpio"
- - reg: The GPIO bank number.
- - interrupt-controller: Marks the device node as an interrupt controller.
- - interrupts: Interrupt specifier for the controllers interrupt.
- - #interrupt-cells: Should be 2. Refer to
-   ../interrupt-controller/interrupts.txt for more details.
- - gpio-controller: Marks the device node as a GPIO controller.
- - #gpio-cells: Should be 2. The first cell is the GPIO number and the second
-cell specifies GPIO flags, as defined in . Only 
the
-GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags are supported.
- - gpio-ranges: Range of pins managed by the GPIO controller. Refer to
-   ../gpio/gpio.txt for more details.
-
-
-Example:
-
-
-pinctrl: pin-controller@1001 {
-   compatible = "ingenic,jz4740-pinctrl";
-   reg = <0x1001 0x400>;
-   #address-cells = <1>;
-   #size-cells = <0>;
-
-   gpa: gpio@0 {
-   compatible = "ingenic,jz4740-gpio";
-   reg = <0>;
-
-   gpio-controller;
-   gpio-ranges = < 0 0 32>;
-   #gpio-cells = <2>;
-
-   interrupt-controller;
-   #interrupt-cells = <2>;
-
-   interrupt-parent = <>;
-   interrupts = <28>;
-   };
-};
diff --git a/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.yaml 
b/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.yaml
new file mode 100644
index ..5be2b1e95b36
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/ingenic,pinctrl.yaml
@@ -0,0 +1,136 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/ingenic,pinctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ingenic SoCs pin controller devicetree bindings
+
+description: >
+  Please refer to pinctrl-bindings.txt in this directory for details of the
+  common pinctrl bindings used by client devices, including the meaning of the
+  phrase "pin configuration node&q

[PATCH v2 3/3] dt-bindings: mtd: Convert ingenic,jz4780-nand.txt to YAML

2020-05-19 Thread Paul Cercueil
Convert the ingenic,jz4780-nand.txt doc file to ingenic,nand.yaml.

Signed-off-by: Paul Cercueil 
---

Notes:
v2: - Don't include ingenic,nemc-client.yaml which is gone
- Use 'partitions' property instead of '^partitions$' pattern

 .../bindings/mtd/ingenic,jz4780-nand.txt  |  92 
 .../devicetree/bindings/mtd/ingenic,nand.yaml | 132 ++
 2 files changed, 132 insertions(+), 92 deletions(-)
 delete mode 100644 
Documentation/devicetree/bindings/mtd/ingenic,jz4780-nand.txt
 create mode 100644 Documentation/devicetree/bindings/mtd/ingenic,nand.yaml

diff --git a/Documentation/devicetree/bindings/mtd/ingenic,jz4780-nand.txt 
b/Documentation/devicetree/bindings/mtd/ingenic,jz4780-nand.txt
deleted file mode 100644
index c02259353327..
--- a/Documentation/devicetree/bindings/mtd/ingenic,jz4780-nand.txt
+++ /dev/null
@@ -1,92 +0,0 @@
-* Ingenic JZ4780 NAND/ECC
-
-This file documents the device tree bindings for NAND flash devices on the
-JZ4780. NAND devices are connected to the NEMC controller (described in
-memory-controllers/ingenic,jz4780-nemc.txt), and thus NAND device nodes must
-be children of the NEMC node.
-
-Required NAND controller device properties:
-- compatible: Should be one of:
-  * ingenic,jz4740-nand
-  * ingenic,jz4725b-nand
-  * ingenic,jz4780-nand
-- reg: For each bank with a NAND chip attached, should specify a bank number,
-  an offset of 0 and a size of 0x100 (i.e. the whole NEMC bank).
-
-Optional NAND controller device properties:
-- ecc-engine: To make use of the hardware ECC controller, this
-  property must contain a phandle for the ECC controller node. The required
-  properties for this node are described below. If this is not specified,
-  software ECC will be used instead.
-
-Optional children nodes:
-- Individual NAND chips are children of the NAND controller node.
-
-Required children node properties:
-- reg: An integer ranging from 1 to 6 representing the CS line to use.
-
-Optional children node properties:
-- nand-ecc-step-size: ECC block size in bytes.
-- nand-ecc-strength: ECC strength (max number of correctable bits).
-- nand-ecc-mode: String, operation mode of the NAND ecc mode. "hw" by default
-- nand-on-flash-bbt: boolean to enable on flash bbt option, if not present 
false
-- rb-gpios: GPIO specifier for the busy pin.
-- wp-gpios: GPIO specifier for the write protect pin.
-
-Optional child node of NAND chip nodes:
-- partitions: see Documentation/devicetree/bindings/mtd/partition.txt
-
-Example:
-
-nemc: nemc@1341 {
-   ...
-
-   nandc: nand-controller@1 {
-   compatible = "ingenic,jz4780-nand";
-   reg = <1 0 0x100>;  /* Bank 1 */
-
-   #address-cells = <1>;
-   #size-cells = <0>;
-
-   ecc-engine = <>;
-
-   nand@1 {
-   reg = <1>;
-
-   nand-ecc-step-size = <1024>;
-   nand-ecc-strength = <24>;
-   nand-ecc-mode = "hw";
-   nand-on-flash-bbt;
-
-   rb-gpios = < 20 GPIO_ACTIVE_LOW>;
-   wp-gpios = < 22 GPIO_ACTIVE_LOW>;
-
-   partitions {
-   #address-cells = <2>;
-   #size-cells = <2>;
-   ...
-   }
-   };
-   };
-};
-
-The ECC controller is a separate SoC component used for error correction on
-NAND devices. The following is a description of the device properties for a
-ECC controller.
-
-Required ECC properties:
-- compatible: Should be one of:
-  * ingenic,jz4740-ecc
-  * ingenic,jz4725b-bch
-  * ingenic,jz4780-bch
-- reg: Should specify the ECC controller registers location and length.
-- clocks: Clock for the ECC controller.
-
-Example:
-
-bch: bch@134d {
-   compatible = "ingenic,jz4780-bch";
-   reg = <0x134d 0x1>;
-
-   clocks = < JZ4780_CLK_BCH>;
-};
diff --git a/Documentation/devicetree/bindings/mtd/ingenic,nand.yaml 
b/Documentation/devicetree/bindings/mtd/ingenic,nand.yaml
new file mode 100644
index ..8abb6d463cb6
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/ingenic,nand.yaml
@@ -0,0 +1,132 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mtd/ingenic,nand.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ingenic SoCs NAND controller devicetree bindings
+
+maintainers:
+  - Paul Cercueil 
+
+allOf:
+  - $ref: nand-controller.yaml#
+
+properties:
+  compatible:
+enum:
+  - ingenic,jz4740-nand
+  - ingenic,jz4725b-nand
+  - ingenic,jz4780-nand
+
+  reg:
+items:
+  - description: Bank number, offset and size of first attached NAND chip
+  - description: Bank num

[PATCH v2 2/3] dt-bindings: memory: Convert ingenic,jz4780-nemc.txt to YAML

2020-05-19 Thread Paul Cercueil
Convert the ingenic,jz4780-nemc.txt doc file to ingenic,nemc.yaml.

The ingenic,jz4725b-nemc compatible string was added in the process,
with a fallback to ingenic,jz4740-nemc.

Signed-off-by: Paul Cercueil 
---

Notes:
v2: - Inline content of ingenic,nemc-client.yaml inside ingenic,nemc.yaml
- Add missing 'reg' property to sub-nodes and mark it as required
- Use a more generic wildcard to match all sub-nodes.

 .../ingenic,jz4780-nemc.txt   |  76 ---
 .../memory-controllers/ingenic,nemc.yaml  | 126 ++
 2 files changed, 126 insertions(+), 76 deletions(-)
 delete mode 100644 
Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt
 create mode 100644 
Documentation/devicetree/bindings/memory-controllers/ingenic,nemc.yaml

diff --git 
a/Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt 
b/Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt
deleted file mode 100644
index 59b8dcc118ee..
--- 
a/Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt
+++ /dev/null
@@ -1,76 +0,0 @@
-* Ingenic JZ4780 NAND/external memory controller (NEMC)
-
-This file documents the device tree bindings for the NEMC external memory
-controller in Ingenic JZ4780
-
-Required properties:
-- compatible: Should be set to one of:
-"ingenic,jz4740-nemc" (JZ4740)
-"ingenic,jz4780-nemc" (JZ4780)
-- reg: Should specify the NEMC controller registers location and length.
-- clocks: Clock for the NEMC controller.
-- #address-cells: Must be set to 2.
-- #size-cells: Must be set to 1.
-- ranges: A set of ranges for each bank describing the physical memory layout.
-  Each should specify the following 4 integer values:
-
- 0  
-
-Each child of the NEMC node describes a device connected to the NEMC.
-
-Required child node properties:
-- reg: Should contain at least one register specifier, given in the following
-  format:
-
-  
-
-  Multiple registers can be specified across multiple banks. This is needed,
-  for example, for packaged NAND devices with multiple dies. Such devices
-  should be grouped into a single node.
-
-Optional child node properties:
-- ingenic,nemc-bus-width: Specifies the bus width in bits. Defaults to 8 bits.
-- ingenic,nemc-tAS: Address setup time in nanoseconds.
-- ingenic,nemc-tAH: Address hold time in nanoseconds.
-- ingenic,nemc-tBP: Burst pitch time in nanoseconds.
-- ingenic,nemc-tAW: Access wait time in nanoseconds.
-- ingenic,nemc-tSTRV: Static memory recovery time in nanoseconds.
-
-If a child node references multiple banks in its "reg" property, the same value
-for all optional parameters will be configured for all banks. If any optional
-parameters are omitted, they will be left unchanged from whatever they are
-configured to when the NEMC device is probed (which may be the reset value as
-given in the hardware reference manual, or a value configured by the boot
-loader).
-
-Example (NEMC node with a NAND child device attached at CS1):
-
-nemc: nemc@1341 {
-   compatible = "ingenic,jz4780-nemc";
-   reg = <0x1341 0x1>;
-
-   #address-cells = <2>;
-   #size-cells = <1>;
-
-   ranges = <1 0 0x1b00 0x100
- 2 0 0x1a00 0x100
- 3 0 0x1900 0x100
- 4 0 0x1800 0x100
- 5 0 0x1700 0x100
- 6 0 0x1600 0x100>;
-
-   clocks = < JZ4780_CLK_NEMC>;
-
-   nand: nand@1 {
-   compatible = "ingenic,jz4780-nand";
-   reg = <1 0 0x100>;
-
-   ingenic,nemc-tAS = <10>;
-   ingenic,nemc-tAH = <5>;
-   ingenic,nemc-tBP = <10>;
-   ingenic,nemc-tAW = <15>;
-   ingenic,nemc-tSTRV = <100>;
-
-   ...
-   };
-};
diff --git 
a/Documentation/devicetree/bindings/memory-controllers/ingenic,nemc.yaml 
b/Documentation/devicetree/bindings/memory-controllers/ingenic,nemc.yaml
new file mode 100644
index ..9b478da0c479
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/ingenic,nemc.yaml
@@ -0,0 +1,126 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/memory-controllers/ingenic,nemc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ingenic SoCs NAND / External Memory Controller (NEMC) devicetree 
bindings
+
+maintainers:
+  - Paul Cercueil 
+
+properties:
+  $nodename:
+pattern: "^memory-controller@[0-9a-f]+$"
+
+  compatible:
+oneOf:
+  - enum:
+- ingenic,jz4740-nemc
+- ingenic,jz4780-nemc
+  - items:
+- const: ingenic,jz4725b-nemc
+- const: ingenic,jz4740-nemc
+
+  "#address-cells":
+const: 2
+
+  &

[PATCH] MIPS: ingenic: Add missing include

2020-05-19 Thread Paul Cercueil
Add missing include which adds the prototype to plat_time_init().

Fixes: f932449c11da ("MIPS: ingenic: Drop obsolete code, merge the rest in 
setup.c")
Signed-off-by: Paul Cercueil 
Reported-by: kbuild test robot 
---
 arch/mips/jz4740/setup.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/mips/jz4740/setup.c b/arch/mips/jz4740/setup.c
index 81428ddcaa97..c627fbd90892 100644
--- a/arch/mips/jz4740/setup.c
+++ b/arch/mips/jz4740/setup.c
@@ -20,6 +20,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #define JZ4740_EMC_BASE_ADDR 0x1301
 
-- 
2.26.2



Re: [PATCH v7 7/7] input: joystick: Add ADC attached joystick driver.

2020-05-19 Thread Paul Cercueil

Hi Andy,

Le mar. 19 mai 2020 à 23:43, Andy Shevchenko 
 a écrit :
On Sun, May 17, 2020 at 10:49 PM Artur Rojek  
wrote:


 Add a driver for joystick devices connected to ADC controllers
 supporting the Industrial I/O subsystem.


...


 +static int adc_joystick_handle(const void *data, void *private)
 +{
 +   struct adc_joystick *joy = private;
 +   enum iio_endian endianness;
 +   int bytes, msb, val, i;
 +   bool sign;
 +
 +   bytes = joy->chans[0].channel->scan_type.storagebits >> 3;
 +
 +   for (i = 0; i < joy->num_chans; ++i) {
 +   endianness = 
joy->chans[i].channel->scan_type.endianness;

 +   msb = joy->chans[i].channel->scan_type.realbits - 1;


 +   sign = 
(tolower(joy->chans[i].channel->scan_type.sign) == 's');


Do we need tolower()?


I'll answer this one:

The sign can be uppercase to specify that the value is sign-extended in 
all the storage bits.


-Paul


 +
 +   switch (bytes) {
 +   case 1:
 +   val = ((const u8 *)data)[i];
 +   break;
 +   case 2:
 +   if (endianness == IIO_BE)


 +   val = be16_to_cpu(((const u16 
*)data)[i]);


Yeah, you have to provide bitwise types to satisfy sparse.
Maybe using *_to_cpup() will cure this.


 +   else if (endianness == IIO_LE)
 +   val = le16_to_cpu(((const u16 
*)data)[i]);

 +   else /* IIO_CPU */
 +   val = ((const u16 *)data)[i];
 +   break;
 +   default:
 +   return -EINVAL;
 +   }
 +
 +   val >>= joy->chans[i].channel->scan_type.shift;
 +   if (sign)
 +   val = sign_extend32(val, msb);
 +   else
 +   val &= GENMASK(msb, 0);
 +   input_report_abs(joy->input, joy->axes[i].code, 
val);

 +   }
 +
 +   input_sync(joy->input);
 +
 +   return 0;
 +}


...


 +   /* Count how many channels we got. NULL terminated. */
 +   while (joy->chans[joy->num_chans].indio_dev)
 +   joy->num_chans++;


I don't see how useful this is. Why not simple do below...


 +   bits = joy->chans[0].channel->scan_type.storagebits;
 +   if (!bits || (bits > 16)) {
 +   dev_err(dev, "Unsupported channel storage size");
 +   return -EINVAL;
 +   }
 +   for (i = 1; i < joy->num_chans; ++i)
 +   if (joy->chans[i].channel->scan_type.storagebits != 
bits) {
 +   dev_err(dev, "Channels must have equal 
storage size");

 +   return -EINVAL;
 +   }


...something like

  for (i = 0; joy->chans[i].indio_dev; i++) {
bits = joy->chans[i].channel->scan_type.storagebits;
if (bits ...) {
  ...error handling...
}
if (bits != joy->chans[0].channel->scan_type.storagebits) {
  ...second level of error handling...
}
 }

...


 +static const struct of_device_id adc_joystick_of_match[] = {
 +   { .compatible = "adc-joystick", },



 +   { },


No need comma.


 +};


--
With Best Regards,
Andy Shevchenko





[PATCH] clocksource: Ingenic: Add high resolution timer support for SMP.

2020-05-19 Thread Paul Cercueil
From: 周琰杰 (Zhou Yanjie) 

Enable clock event handling on per CPU core basis.
Make sure that interrupts raised on the first core execute
event handlers on the correct CPU core.

Tested-by: H. Nikolaus Schaller 
Tested-by: Paul Boddie 
Signed-off-by: 周琰杰 (Zhou Yanjie) 
Signed-off-by: Paul Cercueil 
---

Zhou:

I took the liberty to clean your patch so that it doesn't create a
struct ingenic_tcu per CPU timer.

Tested, and fully working on the JZ4770 with CONFIG_SMP disabled, and
also with CONFIG_SMP enabled (even though JZ4770 has only one CPU) with
a fixed smp.c and USB disabled (otherwise it crashes at boot).

Cheers,
-Paul

 drivers/clocksource/ingenic-timer.c | 180 +++-
 1 file changed, 123 insertions(+), 57 deletions(-)

diff --git a/drivers/clocksource/ingenic-timer.c 
b/drivers/clocksource/ingenic-timer.c
index 496333650de2..a068e4620c44 100644
--- a/drivers/clocksource/ingenic-timer.c
+++ b/drivers/clocksource/ingenic-timer.c
@@ -2,6 +2,7 @@
 /*
  * JZ47xx SoCs TCU IRQ driver
  * Copyright (C) 2019 Paul Cercueil 
+ * Copyright (C) 2020 周琰杰 (Zhou Yanjie) 
  */
 
 #include 
@@ -15,24 +16,35 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 
 #include 
 
+static DEFINE_PER_CPU(call_single_data_t, ingenic_cevt_csd);
+
 struct ingenic_soc_info {
unsigned int num_channels;
 };
 
+struct ingenic_tcu_timer {
+   unsigned int cpu;
+   unsigned int channel;
+   struct clock_event_device cevt;
+   struct clk *clk;
+   char name[8];
+};
+
 struct ingenic_tcu {
struct regmap *map;
-   struct clk *timer_clk, *cs_clk;
-   unsigned int timer_channel, cs_channel;
-   struct clock_event_device cevt;
+   struct device_node *np;
+   struct clk *cs_clk;
+   unsigned int cs_channel;
struct clocksource cs;
-   char name[4];
unsigned long pwm_channels_mask;
+   struct ingenic_tcu_timer timers[];
 };
 
 static struct ingenic_tcu *ingenic_tcu;
@@ -52,16 +64,24 @@ static u64 notrace ingenic_tcu_timer_cs_read(struct 
clocksource *cs)
return ingenic_tcu_timer_read();
 }
 
-static inline struct ingenic_tcu *to_ingenic_tcu(struct clock_event_device 
*evt)
+static inline struct ingenic_tcu *
+to_ingenic_tcu(struct ingenic_tcu_timer *timer)
+{
+   return container_of(timer, struct ingenic_tcu, timers[timer->cpu]);
+}
+
+static inline struct ingenic_tcu_timer *
+to_ingenic_tcu_timer(struct clock_event_device *evt)
 {
-   return container_of(evt, struct ingenic_tcu, cevt);
+   return container_of(evt, struct ingenic_tcu_timer, cevt);
 }
 
 static int ingenic_tcu_cevt_set_state_shutdown(struct clock_event_device *evt)
 {
-   struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
+   struct ingenic_tcu_timer *timer = to_ingenic_tcu_timer(evt);
+   struct ingenic_tcu *tcu = to_ingenic_tcu(timer);
 
-   regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel));
+   regmap_write(tcu->map, TCU_REG_TECR, BIT(timer->channel));
 
return 0;
 }
@@ -69,27 +89,40 @@ static int ingenic_tcu_cevt_set_state_shutdown(struct 
clock_event_device *evt)
 static int ingenic_tcu_cevt_set_next(unsigned long next,
 struct clock_event_device *evt)
 {
-   struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
+   struct ingenic_tcu_timer *timer = to_ingenic_tcu_timer(evt);
+   struct ingenic_tcu *tcu = to_ingenic_tcu(timer);
 
if (next > 0x)
return -EINVAL;
 
-   regmap_write(tcu->map, TCU_REG_TDFRc(tcu->timer_channel), next);
-   regmap_write(tcu->map, TCU_REG_TCNTc(tcu->timer_channel), 0);
-   regmap_write(tcu->map, TCU_REG_TESR, BIT(tcu->timer_channel));
+   regmap_write(tcu->map, TCU_REG_TDFRc(timer->channel), next);
+   regmap_write(tcu->map, TCU_REG_TCNTc(timer->channel), 0);
+   regmap_write(tcu->map, TCU_REG_TESR, BIT(timer->channel));
 
return 0;
 }
 
+static void ingenic_per_cpu_event_handler(void *info)
+{
+   struct clock_event_device *cevt = (struct clock_event_device *) info;
+
+   cevt->event_handler(cevt);
+}
+
 static irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id)
 {
-   struct clock_event_device *evt = dev_id;
-   struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
+   struct ingenic_tcu_timer *timer = dev_id;
+   struct ingenic_tcu *tcu = to_ingenic_tcu(timer);
+   call_single_data_t *csd;
 
-   regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel));
+   regmap_write(tcu->map, TCU_REG_TECR, BIT(timer->channel));
 
-   if (evt->event_handler)
-   evt->event_handler(evt);
+   if (timer->cevt.event_handler) {
+   csd = _cpu(ingenic_cevt_csd, timer->cpu);
+   csd->info = (void *) >cevt;
+   csd->func = ingenic_per_cpu_event_handler;
+   smp_call_function_single_async

Re: [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.

2020-05-19 Thread Paul Cercueil

Hi Zhou,

Le mar. 19 mai 2020 à 22:35, 周琰杰 (Zhou Yanjie) 
 a écrit :

Forward port smp support from kernel 3.18.3 of CI20_linux
to upstream kernel 5.6.

Tested-by: H. Nikolaus Schaller 
Tested-by: Paul Boddie 
Signed-off-by: 周琰杰 (Zhou Yanjie) 
Reviewed-by: Jiaxun Yang 
---

Notes:
v1->v2:
1.Remove unnecessary "plat_irq_dispatch(void)" in irq-ingenic.c.
2.Add a timeout check for "jz4780_boot_secondary()" to avoid a 
dead loop.

3.Replace hard code in smp.c with macro.

v2->v3:
1.Remove unnecessary "extern void (*r4k_blast_dcache)(void)" in 
smp.c.
2.Use "for_each_of_cpu_node" instead "for_each_compatible_node" 
in smp.c.
3.Use "of_cpu_node_to_id" instead "of_property_read_u32_index" in 
smp.c.

4.Move LCR related operations to jz4780-cgu.c.

v3->v4:
Rebase on top of kernel 5.6-rc1.

v4->v5:
1.Splitting changes involving "jz4780-cgu.c" into separate commit.
2.Use "request_irq()" replace "setup_irq()".

v5->v6:
In order to have a kernel that works on multiple SoCs at the same
time, use "IS_ENABLED()" replace "#ifdef".

v6->v7:
1.SMP has be decoupled from the SoC version.
2.Add mailboxes 3 and 4 for XBurst.
3.Adjust code in "jz4780_smp_prepare_cpus()".
4."jz4780_smp_init()" has be marked "__init".

v7->v8:
No change.

 arch/mips/include/asm/mach-jz4740/smp.h |  87 +++
 arch/mips/jz4740/Kconfig|   2 +
 arch/mips/jz4740/Makefile   |   5 +
 arch/mips/jz4740/prom.c |   4 +
 arch/mips/jz4740/smp-entry.S|  57 +++
 arch/mips/jz4740/smp.c  | 258 


 arch/mips/kernel/idle.c |  35 -
 7 files changed, 447 insertions(+), 1 deletion(-)
 create mode 100644 arch/mips/include/asm/mach-jz4740/smp.h
 create mode 100644 arch/mips/jz4740/smp-entry.S
 create mode 100644 arch/mips/jz4740/smp.c

diff --git a/arch/mips/include/asm/mach-jz4740/smp.h 
b/arch/mips/include/asm/mach-jz4740/smp.h

new file mode 100644
index ..86f660f
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/smp.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *  Copyright (C) 2013, Paul Burton 
+ *  JZ4780 SMP definitions
+ */
+
+#ifndef __MIPS_ASM_MACH_JZ4740_SMP_H__
+#define __MIPS_ASM_MACH_JZ4740_SMP_H__
+
+#define read_c0_corectrl() __read_32bit_c0_register($12, 2)
+#define write_c0_corectrl(val)		__write_32bit_c0_register($12, 2, 
val)

+
+#define read_c0_corestatus()   __read_32bit_c0_register($12, 3)
+#define write_c0_corestatus(val)	__write_32bit_c0_register($12, 3, 
val)

+
+#define read_c0_reim() __read_32bit_c0_register($12, 4)
+#define write_c0_reim(val) __write_32bit_c0_register($12, 4, val)
+
+#define read_c0_mailbox0() __read_32bit_c0_register($20, 0)
+#define write_c0_mailbox0(val)		__write_32bit_c0_register($20, 0, 
val)

+
+#define read_c0_mailbox1() __read_32bit_c0_register($20, 1)
+#define write_c0_mailbox1(val)		__write_32bit_c0_register($20, 1, 
val)

+
+#define read_c0_mailbox2() __read_32bit_c0_register($20, 2)
+#define write_c0_mailbox2(val)		__write_32bit_c0_register($20, 2, 
val)

+
+#define read_c0_mailbox3() __read_32bit_c0_register($20, 3)
+#define write_c0_mailbox3(val)		__write_32bit_c0_register($20, 3, 
val)

+
+#define smp_clr_pending(mask) do { \
+   unsigned int stat;  \
+   stat = read_c0_corestatus();\
+   stat &= ~((mask) & 0xff);   \
+   write_c0_corestatus(stat);  \
+   } while (0)
+
+/*
+ * Core Control register
+ */
+#define CORECTRL_SLEEP1M_SHIFT 17
+#define CORECTRL_SLEEP1M   (_ULCAST_(0x1) << CORECTRL_SLEEP1M_SHIFT)
+#define CORECTRL_SLEEP0M_SHIFT 16
+#define CORECTRL_SLEEP0M   (_ULCAST_(0x1) << CORECTRL_SLEEP0M_SHIFT)
+#define CORECTRL_RPC1_SHIFT9
+#define CORECTRL_RPC1  (_ULCAST_(0x1) << CORECTRL_RPC1_SHIFT)
+#define CORECTRL_RPC0_SHIFT8
+#define CORECTRL_RPC0  (_ULCAST_(0x1) << CORECTRL_RPC0_SHIFT)
+#define CORECTRL_SWRST1_SHIFT  1
+#define CORECTRL_SWRST1(_ULCAST_(0x1) << CORECTRL_SWRST1_SHIFT)
+#define CORECTRL_SWRST0_SHIFT  0
+#define CORECTRL_SWRST0(_ULCAST_(0x1) << CORECTRL_SWRST0_SHIFT)
+
+/*
+ * Core Status register
+ */
+#define CORESTATUS_SLEEP1_SHIFT17
+#define CORESTATUS_SLEEP1  (_ULCAST_(0x1) << CORESTATUS_SLEEP1_SHIFT)
+#define CORESTATUS_SLEEP0_SHIFT16
+#define CORESTATUS_SLEEP0  (_ULCAST_(0x1) << CORESTATUS_SLEEP0_SHIFT)
+#define CORESTATUS_IRQ1P_SHIFT 9
+#define CORESTATUS_IRQ1P   (_ULCAST_(0x1) << CORESTATUS_IRQ1P_SHIFT)
+#define CORESTATUS_IRQ0P_SHIFT 8
+#define CORESTATUS_IRQ0P   (_ULCAST_(0x1) << CORESTATUS_IRQ8P_SHIFT)
+#define CORESTATUS_MIRQ1P_SHIFT1
+#define CORESTATUS_MIRQ1P  (_ULCAST_(0x1) << CORESTATUS_MIRQ1P_SHIFT)
+#define 

Re: [PATCH v8 3/6] clocksource: Ingenic: Add high resolution timer support for SMP.

2020-05-19 Thread Paul Cercueil

Hi Zhou,

Le mar. 19 mai 2020 à 22:35, 周琰杰 (Zhou Yanjie) 
 a écrit :

Enable clock event handling on per CPU core basis.
Make sure that interrupts raised on the first core execute
event handlers on the correct CPU core.

Tested-by: H. Nikolaus Schaller 
Tested-by: Paul Boddie 
Signed-off-by: 周琰杰 (Zhou Yanjie) 
---

Notes:
v1->v2:
1.Adjust function naming to make it more reasonable.
2.Replace function smp_call_function_single() with
  smp_call_function_single_async() in order to resolve
  the warning below:

[0.350942] smp: Brought up 1 node, 2 CPUs
[0.365497] [ cut here ]
[0.365522] WARNING: CPU: 0 PID: 1 at kernel/smp.c:300 
smp_call_function_single+0x110/0x200
[0.365533] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 
5.5.0-rc1+ #5
[0.365537] Stack :  59c73bcd 0037 80074e80 
8000 8067 805a 80620590
[0.365557] 8065ce38 8fc0dc8c 806d  
8067 0001 8fc0dc20 59c73bcd
[0.365574]   806f 8067 
 806dab00  2d302e35
[0.365591] 203a6d6d 806e 806e 70617773 
8067   0009
[0.365610]  8fc94e20 8fc0de30 8069 
0018 803592dc  806d

[0.365627] ...
[0.365634] Call Trace:
[0.365647] [<8001b9a0>] show_stack+0x6c/0x12c
[0.365663] [<804aed20>] dump_stack+0x98/0xc8
[0.365673] [<8003044c>] __warn+0xc4/0xe8
[0.365682] [<800304f4>] warn_slowpath_fmt+0x84/0xb8
[0.365690] [<800a886c>] smp_call_function_single+0x110/0x200
[0.365703] ---[ end trace 5785856ca39c79d5 ]---
[0.365557] 8065ce38 8fc0dc8c 806d  
8067 0001 8fc0dc20 59c73bcd
[0.365574]   806f 8067 
 806dab00  2d302e35
[0.365591] 203a6d6d 806e 806e 70617773 
8067   0009
[0.365610]  8fc94e20 8fc0de30 8069 
0018 803592dc  806d

[0.365627] ...
[0.365634] Call Trace:
[0.365647] [<8001b9a0>] show_stack+0x6c/0x12c
[0.365663] [<804aed20>] dump_stack+0x98/0xc8
[0.365673] [<8003044c>] __warn+0xc4/0xe8
[0.365682] [<800304f4>] warn_slowpath_fmt+0x84/0xb8
[0.365690] [<800a886c>] smp_call_function_single+0x110/0x200
[0.365703] ---[ end trace 5785856ca39c79d5 ]---

v2->v3:
No Change.

v3->v4:
Rebase on top of kernel 5.6-rc1.

v4->v5:
Move the check for (evt->event_handler) from 
"ingenic_per_cpu_event_handler"

to "ingenic_tcu_cevt_cb".

v5->v6:
No change.

v6->v7:
Remove unnecessary check for "NR_CPUS > 1".

v7->v8:
Use "num_possible_cpus()" instead "NR_CPUS".
Reported-by: kbuild test robot 

 drivers/clocksource/ingenic-timer.c | 103 


 1 file changed, 82 insertions(+), 21 deletions(-)

diff --git a/drivers/clocksource/ingenic-timer.c 
b/drivers/clocksource/ingenic-timer.c

index 4963336..230e996 100644
--- a/drivers/clocksource/ingenic-timer.c
+++ b/drivers/clocksource/ingenic-timer.c
@@ -1,7 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * JZ47xx SoCs TCU IRQ driver
+ * XBurst SoCs TCU IRQ driver


If you want to get rid of the JZ47xx, then just write 'Ingenic SoCs TCU 
IRQ driver', since XBurst is only the name of the CPU. Also, this 
belongs to a separate patch.



  * Copyright (C) 2019 Paul Cercueil 
+ * Copyright (C) 2020 周琰杰 (Zhou Yanjie) 


  */

 #include 
@@ -21,18 +22,23 @@

 #include 

+static DEFINE_PER_CPU(call_single_data_t, ingenic_cevt_csd);
+
 struct ingenic_soc_info {
unsigned int num_channels;
 };

 struct ingenic_tcu {
struct regmap *map;
+   struct device_node *np;
struct clk *timer_clk, *cs_clk;
+   unsigned int timer_local[NR_CPUS];


NR_CPUS can be very big, so this would make the struct ingenic_tcu very 
large. What you could do, is have a variable array at the end of the 
struct:


unsigned int timer_local[];

Then you can create the ingenic_tcu struct using struct_size() from 
:


tcu = kzalloc(struct_size(tcu, timer_local, num_possible_cpus()), 
GFP_KERNEL);


-Paul


unsigned int timer_channel, cs_channel;
struct clock_event_device cevt;
struct clocksource cs;
-   char name[4];
+   char name[8];
unsigned long pwm_channels_mask;
+   int cpu;
 };

 static struct ingenic_tcu *ingenic_tcu;
@@ -81,6 +87,24 @@ static int ingenic_tcu_cevt_set_next(unsigned long 
next,

return 0;
 }

+static void ingenic_per_cpu_event_handler(void *info)
+{
+	struct clock_event_device *cevt = (struct clock_event_device *) 
info;

+
+   cevt->event_han

Re: [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.

2020-05-19 Thread Paul Cercueil

Hi Zhou,

Le mar. 19 mai 2020 à 22:35, 周琰杰 (Zhou Yanjie) 
 a écrit :

Forward port smp support from kernel 3.18.3 of CI20_linux
to upstream kernel 5.6.

Tested-by: H. Nikolaus Schaller 
Tested-by: Paul Boddie 
Signed-off-by: 周琰杰 (Zhou Yanjie) 
Reviewed-by: Jiaxun Yang 
---

Notes:
v1->v2:
1.Remove unnecessary "plat_irq_dispatch(void)" in irq-ingenic.c.
2.Add a timeout check for "jz4780_boot_secondary()" to avoid a 
dead loop.

3.Replace hard code in smp.c with macro.

v2->v3:
1.Remove unnecessary "extern void (*r4k_blast_dcache)(void)" in 
smp.c.
2.Use "for_each_of_cpu_node" instead "for_each_compatible_node" 
in smp.c.
3.Use "of_cpu_node_to_id" instead "of_property_read_u32_index" in 
smp.c.

4.Move LCR related operations to jz4780-cgu.c.

v3->v4:
Rebase on top of kernel 5.6-rc1.

v4->v5:
1.Splitting changes involving "jz4780-cgu.c" into separate commit.
2.Use "request_irq()" replace "setup_irq()".

v5->v6:
In order to have a kernel that works on multiple SoCs at the same
time, use "IS_ENABLED()" replace "#ifdef".

v6->v7:
1.SMP has be decoupled from the SoC version.
2.Add mailboxes 3 and 4 for XBurst.
3.Adjust code in "jz4780_smp_prepare_cpus()".
4."jz4780_smp_init()" has be marked "__init".

v7->v8:
No change.

 arch/mips/include/asm/mach-jz4740/smp.h |  87 +++
 arch/mips/jz4740/Kconfig|   2 +
 arch/mips/jz4740/Makefile   |   5 +
 arch/mips/jz4740/prom.c |   4 +
 arch/mips/jz4740/smp-entry.S|  57 +++
 arch/mips/jz4740/smp.c  | 258 


 arch/mips/kernel/idle.c |  35 -
 7 files changed, 447 insertions(+), 1 deletion(-)
 create mode 100644 arch/mips/include/asm/mach-jz4740/smp.h
 create mode 100644 arch/mips/jz4740/smp-entry.S
 create mode 100644 arch/mips/jz4740/smp.c

diff --git a/arch/mips/include/asm/mach-jz4740/smp.h 
b/arch/mips/include/asm/mach-jz4740/smp.h

new file mode 100644
index ..86f660f
--- /dev/null
+++ b/arch/mips/include/asm/mach-jz4740/smp.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *  Copyright (C) 2013, Paul Burton 
+ *  JZ4780 SMP definitions
+ */
+
+#ifndef __MIPS_ASM_MACH_JZ4740_SMP_H__
+#define __MIPS_ASM_MACH_JZ4740_SMP_H__
+
+#define read_c0_corectrl() __read_32bit_c0_register($12, 2)
+#define write_c0_corectrl(val)		__write_32bit_c0_register($12, 2, 
val)

+
+#define read_c0_corestatus()   __read_32bit_c0_register($12, 3)
+#define write_c0_corestatus(val)	__write_32bit_c0_register($12, 3, 
val)

+
+#define read_c0_reim() __read_32bit_c0_register($12, 4)
+#define write_c0_reim(val) __write_32bit_c0_register($12, 4, val)
+
+#define read_c0_mailbox0() __read_32bit_c0_register($20, 0)
+#define write_c0_mailbox0(val)		__write_32bit_c0_register($20, 0, 
val)

+
+#define read_c0_mailbox1() __read_32bit_c0_register($20, 1)
+#define write_c0_mailbox1(val)		__write_32bit_c0_register($20, 1, 
val)

+
+#define read_c0_mailbox2() __read_32bit_c0_register($20, 2)
+#define write_c0_mailbox2(val)		__write_32bit_c0_register($20, 2, 
val)

+
+#define read_c0_mailbox3() __read_32bit_c0_register($20, 3)
+#define write_c0_mailbox3(val)		__write_32bit_c0_register($20, 3, 
val)

+
+#define smp_clr_pending(mask) do { \
+   unsigned int stat;  \
+   stat = read_c0_corestatus();\
+   stat &= ~((mask) & 0xff);   \
+   write_c0_corestatus(stat);  \
+   } while (0)
+
+/*
+ * Core Control register
+ */
+#define CORECTRL_SLEEP1M_SHIFT 17
+#define CORECTRL_SLEEP1M   (_ULCAST_(0x1) << CORECTRL_SLEEP1M_SHIFT)
+#define CORECTRL_SLEEP0M_SHIFT 16
+#define CORECTRL_SLEEP0M   (_ULCAST_(0x1) << CORECTRL_SLEEP0M_SHIFT)
+#define CORECTRL_RPC1_SHIFT9
+#define CORECTRL_RPC1  (_ULCAST_(0x1) << CORECTRL_RPC1_SHIFT)
+#define CORECTRL_RPC0_SHIFT8
+#define CORECTRL_RPC0  (_ULCAST_(0x1) << CORECTRL_RPC0_SHIFT)
+#define CORECTRL_SWRST1_SHIFT  1
+#define CORECTRL_SWRST1(_ULCAST_(0x1) << CORECTRL_SWRST1_SHIFT)
+#define CORECTRL_SWRST0_SHIFT  0
+#define CORECTRL_SWRST0(_ULCAST_(0x1) << CORECTRL_SWRST0_SHIFT)
+
+/*
+ * Core Status register
+ */
+#define CORESTATUS_SLEEP1_SHIFT17
+#define CORESTATUS_SLEEP1  (_ULCAST_(0x1) << CORESTATUS_SLEEP1_SHIFT)
+#define CORESTATUS_SLEEP0_SHIFT16
+#define CORESTATUS_SLEEP0  (_ULCAST_(0x1) << CORESTATUS_SLEEP0_SHIFT)
+#define CORESTATUS_IRQ1P_SHIFT 9
+#define CORESTATUS_IRQ1P   (_ULCAST_(0x1) << CORESTATUS_IRQ1P_SHIFT)
+#define CORESTATUS_IRQ0P_SHIFT 8
+#define CORESTATUS_IRQ0P   (_ULCAST_(0x1) << CORESTATUS_IRQ8P_SHIFT)
+#define CORESTATUS_MIRQ1P_SHIFT1
+#define CORESTATUS_MIRQ1P  (_ULCAST_(0x1) << CORESTATUS_MIRQ1P_SHIFT)
+#define 

Re: [PATCH 11/12] gpu/drm: Ingenic: Add support for the IPU

2020-05-18 Thread Paul Cercueil

Hi Emil,

Le lun. 18 mai 2020 à 11:48, Emil Velikov  a 
écrit :

Hi Paul,

Disclaimer: I don't know much about the hardware :-P

On Sun, 17 May 2020 at 00:31, Paul Cercueil  
wrote:


 Add support for the Image Processing Unit (IPU) found in all Ingenic
 SoCs.


Since the IPU is present on all devices supported, does it make sense
to have it as separate module?
Didn't check closely although I suspect doing that will remove the
need for the component patch.


It makes sense to me; you may not want to enable the IPU on platforms 
where it doesn't add much, e.g. on the JZ4725B where it only handles 
YUV and not RGB.


Besides, while the older JZ4740 SoC has a IPU, it's mem-to-mem and 
cannot output directly to the CRTC, so it cannot be represented as a 
plane. On this SoC the current IPU code cannot be used. So the Ingenic 
DRM driver must still be able to probe with zero components.



 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c
 +++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
 @@ -50,7 +50,7 @@ struct jz_soc_info {

  struct ingenic_drm {
 struct drm_device drm;
 -   struct drm_plane f0, f1;
 +   struct drm_plane f0, f1, *ipu_plane;
 struct drm_crtc crtc;
 struct drm_encoder encoder;

 @@ -186,13 +186,16 @@ static void 
ingenic_drm_crtc_update_timings(struct ingenic_drm *priv,

 regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16,
JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16);
 +
 +   regmap_write(priv->map, JZ_REG_LCD_IPUR, JZ_LCD_IPUR_IPUREN 
|

 +(ht * vpe / 3) << JZ_LCD_IPUR_IPUR_LSB);


This hunk also indicates that it may be better to merge the IPU within
the existing driver.


This writes the IPUR register of the CRTC, nothing wrong here.

-Paul




Re: [PATCH 05/12] gpu/drm: Ingenic: Fix opaque pointer casted to wrong type

2020-05-17 Thread Paul Cercueil

Hi Sam,

Le dim. 17 mai 2020 à 8:21, Sam Ravnborg  a écrit :

On Sat, May 16, 2020 at 11:50:50PM +0200, Paul Cercueil wrote:

 The opaque pointer passed to the IRQ handler is a pointer to the
 drm_device, not a pointer to our ingenic_drm structure.

 It still worked, because our ingenic_drm structure contains the
 drm_device as its first field, so the pointer received had the same
 value, but this was not semantically correct.

 Cc: sta...@vger.kernel.org # v5.3
 Fixes: 90b86fcc47b4 ("DRM: Add KMS driver for the Ingenic JZ47xx 
SoCs")

 Signed-off-by: Paul Cercueil 

Acked-by: Sam Ravnborg 


Pushed to drm-misc-fixes, thanks for the review.

-Paul


 ---
  drivers/gpu/drm/ingenic/ingenic-drm.c | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)

 diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c

 index 0c472382a08b..97244462599b 100644
 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c
 +++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
 @@ -476,7 +476,7 @@ static int 
ingenic_drm_encoder_atomic_check(struct drm_encoder *encoder,


  static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg)
  {
 -  struct ingenic_drm *priv = arg;
 +  struct ingenic_drm *priv = drm_device_get_priv(arg);
unsigned int state;

regmap_read(priv->map, JZ_REG_LCD_STATE, );
 --
 2.26.2

 ___
 dri-devel mailing list
 dri-de...@lists.freedesktop.org
 https://lists.freedesktop.org/mailman/listinfo/dri-devel





Re: [PATCH 04/12] gpu/drm: ingenic: Fix bogus crtc_atomic_check callback

2020-05-17 Thread Paul Cercueil

Hi Sam,

Le dim. 17 mai 2020 à 8:17, Sam Ravnborg  a écrit :

On Sat, May 16, 2020 at 11:50:49PM +0200, Paul Cercueil wrote:
 The code was comparing the SoC's maximum height with the mode's 
width,

 and vice-versa. D'oh.

 Cc: sta...@vger.kernel.org # v5.6
 Fixes: a7c909b7c037 ("gpu/drm: ingenic: Check for display size in 
CRTC atomic check")

 Signed-off-by: Paul Cercueil 


Looks correct.
Acked-by: Sam Ravnborg 


Pushed to drm-misc-fixes, thanks for the review.

-Paul


 ---

 Notes:
 This patch was previously sent standalone.
 I marked it as superseded in patchwork.
 Nothing has been changed here.

  drivers/gpu/drm/ingenic/ingenic-drm.c | 4 ++--
  1 file changed, 2 insertions(+), 2 deletions(-)

 diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c

 index 632d72177123..0c472382a08b 100644
 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c
 +++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
 @@ -330,8 +330,8 @@ static int ingenic_drm_crtc_atomic_check(struct 
drm_crtc *crtc,

if (!drm_atomic_crtc_needs_modeset(state))
return 0;

 -  if (state->mode.hdisplay > priv->soc_info->max_height ||
 -  state->mode.vdisplay > priv->soc_info->max_width)
 +  if (state->mode.hdisplay > priv->soc_info->max_width ||
 +  state->mode.vdisplay > priv->soc_info->max_height)
return -EINVAL;

rate = clk_round_rate(priv->pix_clk,
 --
 2.26.2

 ___
 dri-devel mailing list
 dri-de...@lists.freedesktop.org
 https://lists.freedesktop.org/mailman/listinfo/dri-devel





Re: [PATCH 02/12] dt-bindings: display: Add ingenic,ipu.yaml

2020-05-17 Thread Paul Cercueil

Hi Sam,

Le dim. 17 mai 2020 à 8:17, Sam Ravnborg  a écrit :

Hi Paul.
On Sat, May 16, 2020 at 11:50:47PM +0200, Paul Cercueil wrote:
 Add documentation of the Device Tree bindings for the Image 
Processing

 Unit (IPU) found in most Ingenic SoCs.

 Signed-off-by: Paul Cercueil 


For me it fails like this:


Oops. I missed the 'const:' in the item list. Will fix when I send a 
V2, and verify it this time.


Cheers,
-Paul

/Documentation/devicetree/bindings/display/ingenic,ipu.yaml: 
ignoring, error in schema: properties: compatible: oneOf: 1: items
warning: no schema found in file: 
Documentation/devicetree/bindings/display/ingenic,ipu.yaml
make[2]: *** [Documentation/devicetree/bindings/Makefile:42: 
Documentation/devicetree/bindings/processed-schema.yaml] Error 255

make[2]: *** Waiting for unfinished jobs
Documentation/devicetree/bindings/display/ingenic,ipu.yaml: 
properties:compatible:oneOf:1:items: ['ingenic,jz4770-ipu', 
'ingenic,jz4760-ipu'] is not valid under any of the given schemas 
(Possible causes of the failure):
	Documentation/devicetree/bindings/display/ingenic,ipu.yaml: 
properties:compatible:oneOf:1:items: ['ingenic,jz4770-ipu', 
'ingenic,jz4760-ipu'] is not of type 'object', 'boolean'
	Documentation/devicetree/bindings/display/ingenic,ipu.yaml: 
properties:compatible:oneOf:1:items:0: 'ingenic,jz4770-ipu' is not of 
type 'object', 'boolean'
	Documentation/devicetree/bindings/display/ingenic,ipu.yaml: 
properties:compatible:oneOf:1:items:1: 'ingenic,jz4760-ipu' is not of 
type 'object', 'boolean'



Sam


 ---
  .../bindings/display/ingenic,ipu.yaml | 65 
+++

  1 file changed, 65 insertions(+)
  create mode 100644 
Documentation/devicetree/bindings/display/ingenic,ipu.yaml


 diff --git 
a/Documentation/devicetree/bindings/display/ingenic,ipu.yaml 
b/Documentation/devicetree/bindings/display/ingenic,ipu.yaml

 new file mode 100644
 index ..22fe02ca866d
 --- /dev/null
 +++ b/Documentation/devicetree/bindings/display/ingenic,ipu.yaml
 @@ -0,0 +1,65 @@
 +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
 +%YAML 1.2
 +---
 +$id: http://devicetree.org/schemas/display/ingenic,ipu.yaml#
 +$schema: http://devicetree.org/meta-schemas/core.yaml#
 +
 +title: Ingenic SoCs Image Processing Unit (IPU) devicetree bindings
 +
 +maintainers:
 +  - Paul Cercueil 
 +
 +properties:
 +  compatible:
 +oneOf:
 +  - enum:
 +- ingenic,jz4725b-ipu
 +- ingenic,jz4760-ipu
 +  - items:
 +- ingenic,jz4770-ipu
 +- ingenic,jz4760-ipu
 +
 +  reg:
 +maxItems: 1
 +
 +  interrupts:
 +maxItems: 1
 +
 +  clocks:
 +maxItems: 1
 +
 +  clock-names:
 +const: ipu
 +
 +patternProperties:
 +  "^ports?$":
 +description: OF graph bindings (specified in 
bindings/graph.txt).

 +
 +required:
 +  - compatible
 +  - reg
 +  - interrupts
 +  - clocks
 +  - clock-names
 +
 +additionalProperties: false
 +
 +examples:
 +  - |
 +#include 
 +ipu@1308 {
 +  compatible = "ingenic,jz4770-ipu", "ingenic,jz4760-ipu";
 +  reg = <0x1308 0x800>;
 +
 +  interrupt-parent = <>;
 +  interrupts = <29>;
 +
 +  clocks = < JZ4770_CLK_IPU>;
 +  clock-names = "ipu";
 +
 +  port {
 +ipu_ep: endpoint {
 +  remote-endpoint = <_ep>;
 +};
 +  };
 +};
 --
 2.26.2

 ___
 dri-devel mailing list
 dri-de...@lists.freedesktop.org
 https://lists.freedesktop.org/mailman/listinfo/dri-devel





[PATCH 11/12] gpu/drm: Ingenic: Add support for the IPU

2020-05-16 Thread Paul Cercueil
Add support for the Image Processing Unit (IPU) found in all Ingenic
SoCs.

The IPU can upscale and downscale a source frame of arbitrary size
ranging from 4x4 to 4096x4096 on newer SoCs, with bicubic filtering
on newer SoCs, bilinear filtering on older SoCs. Nearest-neighbour can
also be obtained with proper coefficients.

Starting from the JZ4725B, the IPU supports a mode where its output is
sent directly to the CRTC, without having to be written to RAM first.
This makes it possible to use the IPU as a DRM plane on the compatible
SoCs, and have it convert and scale anything the userspace asks for to
what's available for the display.

Regarding pixel formats, older SoCs support packed YUV 4:2:2 and various
planar YUV formats. Newer SoCs also support RGB.

The CRTC can toggle between the primary plane and the IPU, but cannot
display both at the same time. The IPU plane is then a primary plane
itself.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/Kconfig   |  11 +
 drivers/gpu/drm/ingenic/Makefile  |   1 +
 drivers/gpu/drm/ingenic/ingenic-drm.c |  67 +-
 drivers/gpu/drm/ingenic/ingenic-drm.h |   9 +
 drivers/gpu/drm/ingenic/ingenic-ipu.c | 861 ++
 drivers/gpu/drm/ingenic/ingenic-ipu.h | 110 
 6 files changed, 1048 insertions(+), 11 deletions(-)
 create mode 100644 drivers/gpu/drm/ingenic/ingenic-ipu.c
 create mode 100644 drivers/gpu/drm/ingenic/ingenic-ipu.h

diff --git a/drivers/gpu/drm/ingenic/Kconfig b/drivers/gpu/drm/ingenic/Kconfig
index d82c3d37ec9c..80e10527d404 100644
--- a/drivers/gpu/drm/ingenic/Kconfig
+++ b/drivers/gpu/drm/ingenic/Kconfig
@@ -14,3 +14,14 @@ config DRM_INGENIC
  Choose this option for DRM support for the Ingenic SoCs.
 
  If M is selected the module will be called ingenic-drm.
+
+if DRM_INGENIC
+
+config DRM_INGENIC_IPU
+   tristate "IPU support for Ingenic SoCs"
+   help
+ Choose this option to enable support for the IPU found in Ingenic 
SoCs.
+
+ If M is selected the module will be called ingenic-ipu.
+
+endif
diff --git a/drivers/gpu/drm/ingenic/Makefile b/drivers/gpu/drm/ingenic/Makefile
index 11cac42ce0bb..df156194fbe5 100644
--- a/drivers/gpu/drm/ingenic/Makefile
+++ b/drivers/gpu/drm/ingenic/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_DRM_INGENIC) += ingenic-drm.o
+obj-$(CONFIG_DRM_INGENIC_IPU) += ingenic-ipu.o
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c
index b53db3ee001f..ad1cec4f2a4d 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -50,7 +50,7 @@ struct jz_soc_info {
 
 struct ingenic_drm {
struct drm_device drm;
-   struct drm_plane f0, f1;
+   struct drm_plane f0, f1, *ipu_plane;
struct drm_crtc crtc;
struct drm_encoder encoder;
 
@@ -186,13 +186,16 @@ static void ingenic_drm_crtc_update_timings(struct 
ingenic_drm *priv,
regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
   JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16,
   JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16);
+
+   regmap_write(priv->map, JZ_REG_LCD_IPUR, JZ_LCD_IPUR_IPUREN |
+(ht * vpe / 3) << JZ_LCD_IPUR_IPUR_LSB);
 }
 
 static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
 struct drm_crtc_state *state)
 {
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
-   struct drm_plane_state *f1_state, *f0_state;
+   struct drm_plane_state *f1_state, *f0_state, *ipu_state;
long rate;
 
if (!drm_atomic_crtc_needs_modeset(state))
@@ -213,14 +216,41 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc 
*crtc,
f0_state = drm_atomic_get_plane_state(state->state,
  >f0);
 
+   if (priv->ipu_plane)
+   ipu_state = drm_atomic_get_plane_state(state->state,
+  priv->ipu_plane);
+
+   /* IPU and F1 planes cannot be enabled at the same time. */
+   if (priv->ipu_plane && f1_state->fb && ipu_state->fb) {
+   dev_dbg(priv->dev, "Cannot enable both F1 and IPU\n");
+   return -EINVAL;
+   }
+
/* If all the planes are disabled, we won't get a VBLANK IRQ */
-   if (!f1_state->fb && !f0_state->fb)
+   if (!f1_state->fb && !f0_state->fb &&
+   !(priv->ipu_plane && ipu_state->fb))
state->no_vblank = true;
}
 
return 0;
 }
 
+static void ingenic_drm_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_crtc_state *oldstate)
+{
+   struct ingenic_drm *priv = drm_crtc_get_

[PATCH 12/12] gpu/drm: Ingenic: Support multiple panels/bridges

2020-05-16 Thread Paul Cercueil
Support multiple panels or bridges connected to the same DPI output of
the SoC. This setup can be found for instance on the GCW Zero, where the
same DPI output interfaces the internal 320x240 TFT panel, and the ITE
IT6610 HDMI chip.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm.c | 74 ---
 1 file changed, 43 insertions(+), 31 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c
index ad1cec4f2a4d..091e3d3c9027 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -52,7 +52,6 @@ struct ingenic_drm {
struct drm_device drm;
struct drm_plane f0, f1, *ipu_plane;
struct drm_crtc crtc;
-   struct drm_encoder encoder;
 
struct device *dev;
struct regmap *map;
@@ -106,12 +105,6 @@ static inline struct ingenic_drm *drm_crtc_get_priv(struct 
drm_crtc *crtc)
return container_of(crtc, struct ingenic_drm, crtc);
 }
 
-static inline struct ingenic_drm *
-drm_encoder_get_priv(struct drm_encoder *encoder)
-{
-   return container_of(encoder, struct ingenic_drm, encoder);
-}
-
 static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc,
   struct drm_crtc_state *state)
 {
@@ -456,7 +449,7 @@ static void ingenic_drm_encoder_atomic_mode_set(struct 
drm_encoder *encoder,
struct drm_crtc_state 
*crtc_state,
struct drm_connector_state 
*conn_state)
 {
-   struct ingenic_drm *priv = drm_encoder_get_priv(encoder);
+   struct ingenic_drm *priv = drm_device_get_priv(encoder->dev);
struct drm_display_mode *mode = _state->adjusted_mode;
struct drm_connector *conn = conn_state->connector;
struct drm_display_info *info = >display_info;
@@ -670,9 +663,11 @@ static int ingenic_drm_bind(struct device *dev)
struct clk *parent_clk;
struct drm_bridge *bridge;
struct drm_panel *panel;
+   struct drm_encoder *encoder;
struct drm_device *drm;
void __iomem *base;
long parent_rate;
+   unsigned int i, clone_mask = 0;
int ret, irq;
 
soc_info = of_device_get_match_data(dev);
@@ -744,17 +739,6 @@ static int ingenic_drm_bind(struct device *dev)
return PTR_ERR(priv->pix_clk);
}
 
-   ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, , );
-   if (ret) {
-   if (ret != -EPROBE_DEFER)
-   dev_err(dev, "Failed to get panel handle");
-   return ret;
-   }
-
-   if (panel)
-   bridge = devm_drm_panel_bridge_add_typed(dev, panel,
-
DRM_MODE_CONNECTOR_DPI);
-
priv->dma_hwdesc[0] = dma_alloc_coherent(dev,
 sizeof(*priv->dma_hwdesc[0]),
 >dma_hwdesc_phys[0],
@@ -821,22 +805,50 @@ static int ingenic_drm_bind(struct device *dev)
}
}
 
-   priv->encoder.possible_crtcs = 1;
+   for (i = 0; ; i++) {
+   ret = drm_of_find_panel_or_bridge(dev->of_node, 0, i,
+ , );
+   if (ret) {
+   if (ret == -ENODEV)
+   break; /* we're done */
+   if (ret != -EPROBE_DEFER)
+   dev_err(dev, "Failed to get bridge handle\n");
+   return ret;
+   }
 
-   drm_encoder_helper_add(>encoder,
-  _drm_encoder_helper_funcs);
+   if (panel)
+   bridge = devm_drm_panel_bridge_add_typed(dev, panel,
+
DRM_MODE_CONNECTOR_DPI);
 
-   ret = drm_simple_encoder_init(drm, >encoder,
- DRM_MODE_ENCODER_DPI);
-   if (ret) {
-   dev_err(dev, "Failed to init encoder: %i", ret);
-   return ret;
+   encoder = devm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL);
+   if (!encoder)
+   return -ENOMEM;
+
+   encoder->possible_crtcs = 1;
+
+   drm_encoder_helper_add(encoder,
+  _drm_encoder_helper_funcs);
+
+   ret = drm_simple_encoder_init(drm, encoder,
+ DRM_MODE_ENCODER_DPI);
+   if (ret) {
+   dev_err(dev, "Failed to init encoder: %d\n", ret);
+   return ret;
+   }
+
+   ret = drm_bridge_attach(encoder, bridge, NULL, 0);
+   if (ret) {
+

[PATCH 10/12] gpu/drm: Ingenic: Register driver as component master

2020-05-16 Thread Paul Cercueil
Register the ingenic-drm driver as a component master.

This will later allow to plug optional components to the driver, for
instance to add support for the IPU, or the SLCD module.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm.c | 61 +--
 1 file changed, 57 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c
index f139af5b7a40..b53db3ee001f 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -6,6 +6,7 @@
 
 #include "ingenic-drm.h"
 
+#include 
 #include 
 #include 
 #include 
@@ -613,10 +614,17 @@ static void ingenic_drm_free_dma_hwdesc(void *d)
  priv->dma_hwdesc[1], priv->dma_hwdesc_phys[1]);
 }
 
-static int ingenic_drm_probe(struct platform_device *pdev)
+static void ingenic_drm_unbind_all(void *d)
 {
+   struct ingenic_drm *priv = d;
+
+   component_unbind_all(priv->dev, >drm);
+}
+
+static int ingenic_drm_bind(struct device *dev)
+{
+   struct platform_device *pdev = to_platform_device(dev);
const struct jz_soc_info *soc_info;
-   struct device *dev = >dev;
struct ingenic_drm *priv;
struct clk *parent_clk;
struct drm_bridge *bridge;
@@ -653,6 +661,17 @@ static int ingenic_drm_probe(struct platform_device *pdev)
drm->mode_config.max_height = 4095;
drm->mode_config.funcs = _drm_mode_config_funcs;
 
+   ret = component_bind_all(dev, drm);
+   if (ret) {
+   if (ret != -EPROBE_DEFER)
+   dev_err(dev, "Failed to bind components: %i", ret);
+   return ret;
+   }
+
+   ret = devm_add_action_or_reset(dev, ingenic_drm_unbind_all, priv);
+   if (ret)
+   return ret;
+
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
dev_err(dev, "Failed to get memory resource");
@@ -843,9 +862,14 @@ static int ingenic_drm_probe(struct platform_device *pdev)
return ret;
 }
 
-static int ingenic_drm_remove(struct platform_device *pdev)
+static int compare_of(struct device *dev, void *data)
+{
+   return dev->of_node == data;
+}
+
+static void ingenic_drm_unbind(struct device *dev)
 {
-   struct ingenic_drm *priv = platform_get_drvdata(pdev);
+   struct ingenic_drm *priv = dev_get_drvdata(dev);
 
if (priv->lcd_clk)
clk_disable_unprepare(priv->lcd_clk);
@@ -853,6 +877,35 @@ static int ingenic_drm_remove(struct platform_device *pdev)
 
drm_dev_unregister(>drm);
drm_atomic_helper_shutdown(>drm);
+}
+
+static const struct component_master_ops ingenic_master_ops = {
+   .bind = ingenic_drm_bind,
+   .unbind = ingenic_drm_unbind,
+};
+
+static int ingenic_drm_probe(struct platform_device *pdev)
+{
+   struct device *dev = >dev;
+   struct component_match *match = NULL;
+   struct device_node *np;
+   unsigned int i;
+
+   /* Probe components at port address 8 and upwards */
+   for (i = 8; ; i++) {
+   np = of_graph_get_remote_node(dev->of_node, i, 0);
+   if (!np)
+   break;
+
+   drm_of_component_match_add(dev, , compare_of, np);
+   }
+
+   return component_master_add_with_match(dev, _master_ops, match);
+}
+
+static int ingenic_drm_remove(struct platform_device *pdev)
+{
+   component_master_del(>dev, _master_ops);
 
return 0;
 }
-- 
2.26.2



[PATCH 09/12] gpu/drm: Ingenic: Add support for OSD mode

2020-05-16 Thread Paul Cercueil
All Ingenic SoCs starting from the JZ4725B support OSD mode.

In this mode, two separate planes can be used. They can have different
positions and sizes, and one can be overlayed on top of the other.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm.c | 275 +-
 drivers/gpu/drm/ingenic/ingenic-drm.h |  35 
 2 files changed, 258 insertions(+), 52 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c
index 92c63d1303f9..f139af5b7a40 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -43,12 +43,13 @@ struct ingenic_dma_hwdesc {
 
 struct jz_soc_info {
bool needs_dev_clk;
+   bool has_osd;
unsigned int max_width, max_height;
 };
 
 struct ingenic_drm {
struct drm_device drm;
-   struct drm_plane primary;
+   struct drm_plane f0, f1;
struct drm_crtc crtc;
struct drm_encoder encoder;
 
@@ -57,8 +58,8 @@ struct ingenic_drm {
struct clk *lcd_clk, *pix_clk;
const struct jz_soc_info *soc_info;
 
-   struct ingenic_dma_hwdesc *dma_hwdesc;
-   dma_addr_t dma_hwdesc_phys;
+   struct ingenic_dma_hwdesc *dma_hwdesc[2];
+   dma_addr_t dma_hwdesc_phys[2];
 
bool panel_is_sharp;
 };
@@ -90,7 +91,7 @@ static const struct regmap_config ingenic_drm_regmap_config = 
{
.val_bits = 32,
.reg_stride = 4,
 
-   .max_register = JZ_REG_LCD_CMD1,
+   .max_register = JZ_REG_LCD_SIZE1,
.writeable_reg = ingenic_drm_writeable_reg,
 };
 
@@ -110,11 +111,6 @@ drm_encoder_get_priv(struct drm_encoder *encoder)
return container_of(encoder, struct ingenic_drm, encoder);
 }
 
-static inline struct ingenic_drm *drm_plane_get_priv(struct drm_plane *plane)
-{
-   return container_of(plane, struct ingenic_drm, primary);
-}
-
 static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc,
   struct drm_crtc_state *state)
 {
@@ -185,34 +181,17 @@ static void ingenic_drm_crtc_update_timings(struct 
ingenic_drm *priv,
regmap_write(priv->map, JZ_REG_LCD_SPL, hpe << 16 | (hpe + 1));
regmap_write(priv->map, JZ_REG_LCD_REV, mode->htotal << 16);
}
-}
-
-static void ingenic_drm_crtc_update_ctrl(struct ingenic_drm *priv,
-const struct drm_format_info *finfo)
-{
-   unsigned int ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16;
-
-   switch (finfo->format) {
-   case DRM_FORMAT_XRGB1555:
-   ctrl |= JZ_LCD_CTRL_RGB555;
-   /* fall-through */
-   case DRM_FORMAT_RGB565:
-   ctrl |= JZ_LCD_CTRL_BPP_15_16;
-   break;
-   case DRM_FORMAT_XRGB:
-   ctrl |= JZ_LCD_CTRL_BPP_18_24;
-   break;
-   }
 
regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
-  JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16 |
-  JZ_LCD_CTRL_BPP_MASK, ctrl);
+  JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16,
+  JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16);
 }
 
 static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
 struct drm_crtc_state *state)
 {
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
+   struct drm_plane_state *f1_state, *f0_state;
long rate;
 
if (!drm_atomic_crtc_needs_modeset(state))
@@ -227,6 +206,17 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc 
*crtc,
if (rate < 0)
return rate;
 
+   if (priv->soc_info->has_osd) {
+   f1_state = drm_atomic_get_plane_state(state->state,
+ >f1);
+   f0_state = drm_atomic_get_plane_state(state->state,
+ >f0);
+
+   /* If all the planes are disabled, we won't get a VBLANK IRQ */
+   if (!f1_state->fb && !f0_state->fb)
+   state->no_vblank = true;
+   }
+
return 0;
 }
 
@@ -236,14 +226,9 @@ static void ingenic_drm_crtc_atomic_flush(struct drm_crtc 
*crtc,
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
struct drm_crtc_state *state = crtc->state;
struct drm_pending_vblank_event *event = state->event;
-   struct drm_framebuffer *drm_fb = crtc->primary->state->fb;
-   const struct drm_format_info *finfo;
 
if (drm_atomic_crtc_needs_modeset(state)) {
-   finfo = drm_format_info(drm_fb->format->format);
-
ingenic_drm_crtc_update_timings(priv, >mode);
-   ingenic_drm_crtc_update_ctrl(priv, finfo);
 
clk_set_rate(priv->pix_clk, state->adjusted_mode.clock * 1000);
  

[PATCH 07/12] gpu/drm: ingenic: Set DMA descriptor chain address in probe

2020-05-16 Thread Paul Cercueil
The address of the DMA descriptor never changes. It can therefore be set
in the probe function.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c
index 3207105755c9..e43318938e9e 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -358,8 +358,6 @@ static void ingenic_drm_crtc_atomic_flush(struct drm_crtc 
*crtc,
ingenic_drm_crtc_update_ctrl(priv, finfo);
 
clk_set_rate(priv->pix_clk, state->adjusted_mode.clock * 1000);
-
-   regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc->next);
}
 
if (event) {
@@ -768,6 +766,9 @@ static int ingenic_drm_probe(struct platform_device *pdev)
}
}
 
+   /* Set address of our DMA descriptor chain */
+   regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc_phys);
+
ret = drm_dev_register(drm, 0);
if (ret) {
dev_err(dev, "Failed to register DRM driver");
-- 
2.26.2



[PATCH 08/12] gpu/drm: Ingenic: Move register definitions to ingenic-drm.h

2020-05-16 Thread Paul Cercueil
Move the register definitions to ingenic-drm.h, to keep ingenic-drm.c
tidy.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm.c | 116 +--
 drivers/gpu/drm/ingenic/ingenic-drm.h | 127 ++
 2 files changed, 129 insertions(+), 114 deletions(-)
 create mode 100644 drivers/gpu/drm/ingenic/ingenic-drm.h

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c
index e43318938e9e..92c63d1303f9 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -4,6 +4,8 @@
 //
 // Copyright (C) 2019, Paul Cercueil 
 
+#include "ingenic-drm.h"
+
 #include 
 #include 
 #include 
@@ -32,120 +34,6 @@
 #include 
 #include 
 
-#define JZ_REG_LCD_CFG 0x00
-#define JZ_REG_LCD_VSYNC   0x04
-#define JZ_REG_LCD_HSYNC   0x08
-#define JZ_REG_LCD_VAT 0x0C
-#define JZ_REG_LCD_DAH 0x10
-#define JZ_REG_LCD_DAV 0x14
-#define JZ_REG_LCD_PS  0x18
-#define JZ_REG_LCD_CLS 0x1C
-#define JZ_REG_LCD_SPL 0x20
-#define JZ_REG_LCD_REV 0x24
-#define JZ_REG_LCD_CTRL0x30
-#define JZ_REG_LCD_STATE   0x34
-#define JZ_REG_LCD_IID 0x38
-#define JZ_REG_LCD_DA0 0x40
-#define JZ_REG_LCD_SA0 0x44
-#define JZ_REG_LCD_FID00x48
-#define JZ_REG_LCD_CMD00x4C
-#define JZ_REG_LCD_DA1 0x50
-#define JZ_REG_LCD_SA1 0x54
-#define JZ_REG_LCD_FID10x58
-#define JZ_REG_LCD_CMD10x5C
-
-#define JZ_LCD_CFG_SLCDBIT(31)
-#define JZ_LCD_CFG_PS_DISABLE  BIT(23)
-#define JZ_LCD_CFG_CLS_DISABLE BIT(22)
-#define JZ_LCD_CFG_SPL_DISABLE BIT(21)
-#define JZ_LCD_CFG_REV_DISABLE BIT(20)
-#define JZ_LCD_CFG_HSYNCM  BIT(19)
-#define JZ_LCD_CFG_PCLKM   BIT(18)
-#define JZ_LCD_CFG_INV BIT(17)
-#define JZ_LCD_CFG_SYNC_DIRBIT(16)
-#define JZ_LCD_CFG_PS_POLARITY BIT(15)
-#define JZ_LCD_CFG_CLS_POLARITYBIT(14)
-#define JZ_LCD_CFG_SPL_POLARITYBIT(13)
-#define JZ_LCD_CFG_REV_POLARITYBIT(12)
-#define JZ_LCD_CFG_HSYNC_ACTIVE_LOWBIT(11)
-#define JZ_LCD_CFG_PCLK_FALLING_EDGE   BIT(10)
-#define JZ_LCD_CFG_DE_ACTIVE_LOW   BIT(9)
-#define JZ_LCD_CFG_VSYNC_ACTIVE_LOWBIT(8)
-#define JZ_LCD_CFG_18_BIT  BIT(7)
-#define JZ_LCD_CFG_PDW (BIT(5) | BIT(4))
-
-#define JZ_LCD_CFG_MODE_GENERIC_16BIT  0
-#define JZ_LCD_CFG_MODE_GENERIC_18BIT  BIT(7)
-#define JZ_LCD_CFG_MODE_GENERIC_24BIT  BIT(6)
-
-#define JZ_LCD_CFG_MODE_SPECIAL_TFT_1  1
-#define JZ_LCD_CFG_MODE_SPECIAL_TFT_2  2
-#define JZ_LCD_CFG_MODE_SPECIAL_TFT_3  3
-
-#define JZ_LCD_CFG_MODE_TV_OUT_P   4
-#define JZ_LCD_CFG_MODE_TV_OUT_I   6
-
-#define JZ_LCD_CFG_MODE_SINGLE_COLOR_STN   8
-#define JZ_LCD_CFG_MODE_SINGLE_MONOCHROME_STN  9
-#define JZ_LCD_CFG_MODE_DUAL_COLOR_STN 10
-#define JZ_LCD_CFG_MODE_DUAL_MONOCHROME_STN11
-
-#define JZ_LCD_CFG_MODE_8BIT_SERIAL12
-#define JZ_LCD_CFG_MODE_LCM13
-
-#define JZ_LCD_VSYNC_VPS_OFFSET16
-#define JZ_LCD_VSYNC_VPE_OFFSET0
-
-#define JZ_LCD_HSYNC_HPS_OFFSET16
-#define JZ_LCD_HSYNC_HPE_OFFSET0
-
-#define JZ_LCD_VAT_HT_OFFSET   16
-#define JZ_LCD_VAT_VT_OFFSET   0
-
-#define JZ_LCD_DAH_HDS_OFFSET  16
-#define JZ_LCD_DAH_HDE_OFFSET  0
-
-#define JZ_LCD_DAV_VDS_OFFSET  16
-#define JZ_LCD_DAV_VDE_OFFSET  0
-
-#define JZ_LCD_CTRL_BURST_4(0x0 << 28)
-#define JZ_LCD_CTRL_BURST_8(0x1 << 28)
-#define JZ_LCD_CTRL_BURST_16   (0x2 << 28)
-#define JZ_LCD_CTRL_RGB555 BIT(27)
-#define JZ_LCD_CTRL_OFUP   BIT(26)
-#define JZ_LCD_CTRL_FRC_GRAYSCALE_16   (0x0 << 24)
-#define JZ_LCD_CTRL_FRC_GRAYSCALE_4(0x1 << 24)
-#define JZ_LCD_CTRL_FRC_GRAYSCALE_2(0x2 << 24)
-#define JZ_LCD_CTRL_PDD_MASK   (0xff << 16)
-#define JZ_LCD_CTRL_EOF_IRQBIT(13)
-#define JZ_LCD_CTRL_SOF_IRQBIT(12)
-#define JZ_

[PATCH 06/12] gpu/drm: Ingenic: Fix incorrect assumption about plane->index

2020-05-16 Thread Paul Cercueil
plane->index is NOT the index of the color plane in a YUV frame.
Actually, a YUV frame is represented by a single drm_plane, even though
it contains three Y, U, V planes.

Cc: sta...@vger.kernel.org # v5.3
Fixes: 90b86fcc47b4 ("DRM: Add KMS driver for the Ingenic JZ47xx SoCs")
Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c
index 97244462599b..3207105755c9 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -386,7 +386,7 @@ static void ingenic_drm_plane_atomic_update(struct 
drm_plane *plane,
addr = drm_fb_cma_get_gem_addr(state->fb, state, 0);
width = state->src_w >> 16;
height = state->src_h >> 16;
-   cpp = state->fb->format->cpp[plane->index];
+   cpp = state->fb->format->cpp[0];
 
priv->dma_hwdesc->addr = addr;
priv->dma_hwdesc->cmd = width * height * cpp / 4;
-- 
2.26.2



[PATCH 04/12] gpu/drm: ingenic: Fix bogus crtc_atomic_check callback

2020-05-16 Thread Paul Cercueil
The code was comparing the SoC's maximum height with the mode's width,
and vice-versa. D'oh.

Cc: sta...@vger.kernel.org # v5.6
Fixes: a7c909b7c037 ("gpu/drm: ingenic: Check for display size in CRTC atomic 
check")
Signed-off-by: Paul Cercueil 
---

Notes:
This patch was previously sent standalone.
I marked it as superseded in patchwork.
Nothing has been changed here.

 drivers/gpu/drm/ingenic/ingenic-drm.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c
index 632d72177123..0c472382a08b 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -330,8 +330,8 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc 
*crtc,
if (!drm_atomic_crtc_needs_modeset(state))
return 0;
 
-   if (state->mode.hdisplay > priv->soc_info->max_height ||
-   state->mode.vdisplay > priv->soc_info->max_width)
+   if (state->mode.hdisplay > priv->soc_info->max_width ||
+   state->mode.vdisplay > priv->soc_info->max_height)
return -EINVAL;
 
rate = clk_round_rate(priv->pix_clk,
-- 
2.26.2



[PATCH 05/12] gpu/drm: Ingenic: Fix opaque pointer casted to wrong type

2020-05-16 Thread Paul Cercueil
The opaque pointer passed to the IRQ handler is a pointer to the
drm_device, not a pointer to our ingenic_drm structure.

It still worked, because our ingenic_drm structure contains the
drm_device as its first field, so the pointer received had the same
value, but this was not semantically correct.

Cc: sta...@vger.kernel.org # v5.3
Fixes: 90b86fcc47b4 ("DRM: Add KMS driver for the Ingenic JZ47xx SoCs")
Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c
index 0c472382a08b..97244462599b 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -476,7 +476,7 @@ static int ingenic_drm_encoder_atomic_check(struct 
drm_encoder *encoder,
 
 static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg)
 {
-   struct ingenic_drm *priv = arg;
+   struct ingenic_drm *priv = drm_device_get_priv(arg);
unsigned int state;
 
regmap_read(priv->map, JZ_REG_LCD_STATE, );
-- 
2.26.2



[PATCH 03/12] component: Support binding with no matches

2020-05-16 Thread Paul Cercueil
Support binding the master even though no components have been
registered.

This permits to support cases where components are optional.

Signed-off-by: Paul Cercueil 
---
 drivers/base/component.c | 35 ++-
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/drivers/base/component.c b/drivers/base/component.c
index e97704104784..a9de7ee1677f 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -100,7 +100,7 @@ static int component_devices_show(struct seq_file *s, void 
*data)
 
seq_printf(s, "%-40s %20s\n", "device name", "status");
seq_puts(s, 
"-\n");
-   for (i = 0; i < match->num; i++) {
+   for (i = 0; !!match && i < match->num; i++) {
struct component *component = match->compare[i].component;
 
seq_printf(s, "%-40s %20s\n",
@@ -184,6 +184,11 @@ static int find_components(struct master *master)
size_t i;
int ret = 0;
 
+   if (!match) {
+   dev_dbg(master->dev, "No components\n");
+   return 0;
+   }
+
/*
 * Scan the array of match functions and attach
 * any components which are found to this master.
@@ -218,10 +223,12 @@ static void remove_component(struct master *master, 
struct component *c)
 {
size_t i;
 
-   /* Detach the component from this master. */
-   for (i = 0; i < master->match->num; i++)
-   if (master->match->compare[i].component == c)
-   master->match->compare[i].component = NULL;
+   if (master->match) {
+   /* Detach the component from this master. */
+   for (i = 0; i < master->match->num; i++)
+   if (master->match->compare[i].component == c)
+   master->match->compare[i].component = NULL;
+   }
 }
 
 /*
@@ -470,10 +477,12 @@ int component_master_add_with_match(struct device *dev,
struct master *master;
int ret;
 
-   /* Reallocate the match array for its true size */
-   ret = component_match_realloc(dev, match, match->num);
-   if (ret)
-   return ret;
+   if (match) {
+   /* Reallocate the match array for its true size */
+   ret = component_match_realloc(dev, match, match->num);
+   if (ret)
+   return ret;
+   }
 
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
@@ -557,6 +566,10 @@ void component_unbind_all(struct device *master_dev, void 
*data)
if (!master)
return;
 
+   /* No match, nothing to unbind */
+   if (!master->match)
+   return;
+
/* Unbind components in reverse order */
for (i = master->match->num; i--; )
if (!master->match->compare[i].duplicate) {
@@ -640,6 +653,10 @@ int component_bind_all(struct device *master_dev, void 
*data)
if (!master)
return -EINVAL;
 
+   /* No match, nothing to bind */
+   if (!master->match)
+   return 0;
+
/* Bind components in match order */
for (i = 0; i < master->match->num; i++)
if (!master->match->compare[i].duplicate) {
-- 
2.26.2



[PATCH 01/12] dt-bindings: display: Convert ingenic,lcd.txt to YAML

2020-05-16 Thread Paul Cercueil
Convert the ingenic,lcd.txt to a new ingenic,lcd.yaml file.

In the process, the new ingenic,jz4780-lcd compatible string has been
added.

Signed-off-by: Paul Cercueil 
---

Notes:
This patch comes from a different patchset so it's effectively a V2.

Changes were:
- lcd_pclk and lcd clocks are in the correct order now,
- Add 'port' and 'ports' properties, and document the valid ports.

 .../bindings/display/ingenic,lcd.txt  |  45 ---
 .../bindings/display/ingenic,lcd.yaml | 126 ++
 2 files changed, 126 insertions(+), 45 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/display/ingenic,lcd.txt
 create mode 100644 Documentation/devicetree/bindings/display/ingenic,lcd.yaml

diff --git a/Documentation/devicetree/bindings/display/ingenic,lcd.txt 
b/Documentation/devicetree/bindings/display/ingenic,lcd.txt
deleted file mode 100644
index 01e3261defb6..
--- a/Documentation/devicetree/bindings/display/ingenic,lcd.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-Ingenic JZ47xx LCD driver
-
-Required properties:
-- compatible: one of:
-  * ingenic,jz4740-lcd
-  * ingenic,jz4725b-lcd
-  * ingenic,jz4770-lcd
-- reg: LCD registers location and length
-- clocks: LCD pixclock and device clock specifiers.
-  The device clock is only required on the JZ4740.
-- clock-names: "lcd_pclk" and "lcd"
-- interrupts: Specifies the interrupt line the LCD controller is connected to.
-
-Example:
-
-panel {
-   compatible = "sharp,ls020b1dd01d";
-
-   backlight = <>;
-   power-supply = <>;
-
-   port {
-   panel_input: endpoint {
-   remote-endpoint = <_output>;
-   };
-   };
-};
-
-
-lcd: lcd-controller@1305 {
-   compatible = "ingenic,jz4725b-lcd";
-   reg = <0x1305 0x1000>;
-
-   interrupt-parent = <>;
-   interrupts = <31>;
-
-   clocks = < JZ4725B_CLK_LCD>;
-   clock-names = "lcd";
-
-   port {
-   panel_output: endpoint {
-   remote-endpoint = <_input>;
-   };
-   };
-};
diff --git a/Documentation/devicetree/bindings/display/ingenic,lcd.yaml 
b/Documentation/devicetree/bindings/display/ingenic,lcd.yaml
new file mode 100644
index ..d56db1802fad
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/ingenic,lcd.yaml
@@ -0,0 +1,126 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/ingenic,lcd.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ingenic SoCs LCD controller devicetree bindings
+
+maintainers:
+  - Paul Cercueil 
+
+properties:
+  $nodename:
+pattern: "^lcd-controller@[0-9a-f]+$"
+
+  compatible:
+enum:
+  - ingenic,jz4740-lcd
+  - ingenic,jz4725b-lcd
+  - ingenic,jz4770-lcd
+  - ingenic,jz4780-lcd
+
+  reg:
+maxItems: 1
+
+  interrupts:
+maxItems: 1
+
+  clocks:
+items:
+  - description: Pixel clock
+  - description: Module clock
+minItems: 1
+
+  clock-names:
+items:
+  - const: lcd_pclk
+  - const: lcd
+minItems: 1
+
+  port:
+description: OF graph bindings (specified in bindings/graph.txt).
+
+  ports:
+description: OF graph bindings (specified in bindings/graph.txt).
+type: object
+properties:
+  port@0:
+type: object
+description: DPI output, to interface with TFT panels.
+
+  port@8:
+type: object
+description: Link to the Image Processing Unit (IPU).
+  (See ingenic,ipu.yaml).
+
+required:
+  - port@0
+
+required:
+- compatible
+- reg
+- interrupts
+- clocks
+- clock-names
+
+if:
+  properties:
+compatible:
+  contains:
+enum:
+  - ingenic,jz4740-lcd
+  - ingenic,jz4780-lcd
+then:
+  properties:
+clocks:
+  minItems: 2
+clock-names:
+  minItems: 2
+else:
+  properties:
+clocks:
+  maxItems: 1
+clock-names:
+  maxItems: 1
+
+additionalProperties: false
+
+examples:
+  - |
+#include 
+lcd-controller@1305 {
+  compatible = "ingenic,jz4740-lcd";
+  reg = <0x1305 0x1000>;
+
+  interrupt-parent = <>;
+  interrupts = <30>;
+
+  clocks = < JZ4740_CLK_LCD_PCLK>, < JZ4740_CLK_LCD>;
+  clock-names = "lcd_pclk", "lcd";
+
+  port {
+endpoint {
+  remote-endpoint = <_input>;
+};
+  };
+};
+
+  - |
+#include 
+lcd-controller@1305 {
+  compatible = "ingenic,jz4725b-lcd";
+  reg = <0x1305 0x1000>;
+
+  interrupt-parent = <>;
+  interrupts = <31>;
+
+  clocks = < JZ4725B_CLK_LCD>;
+  clock-names = "lcd_pclk";
+
+  port {
+endpoint {
+  remote-endpoint = <_input>;
+};
+  };
+};
-- 
2.26.2



[PATCH 02/12] dt-bindings: display: Add ingenic,ipu.yaml

2020-05-16 Thread Paul Cercueil
Add documentation of the Device Tree bindings for the Image Processing
Unit (IPU) found in most Ingenic SoCs.

Signed-off-by: Paul Cercueil 
---
 .../bindings/display/ingenic,ipu.yaml | 65 +++
 1 file changed, 65 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/ingenic,ipu.yaml

diff --git a/Documentation/devicetree/bindings/display/ingenic,ipu.yaml 
b/Documentation/devicetree/bindings/display/ingenic,ipu.yaml
new file mode 100644
index ..22fe02ca866d
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/ingenic,ipu.yaml
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/ingenic,ipu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ingenic SoCs Image Processing Unit (IPU) devicetree bindings
+
+maintainers:
+  - Paul Cercueil 
+
+properties:
+  compatible:
+oneOf:
+  - enum:
+- ingenic,jz4725b-ipu
+- ingenic,jz4760-ipu
+  - items:
+- ingenic,jz4770-ipu
+- ingenic,jz4760-ipu
+
+  reg:
+maxItems: 1
+
+  interrupts:
+maxItems: 1
+
+  clocks:
+maxItems: 1
+
+  clock-names:
+const: ipu
+
+patternProperties:
+  "^ports?$":
+description: OF graph bindings (specified in bindings/graph.txt).
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+
+additionalProperties: false
+
+examples:
+  - |
+#include 
+ipu@1308 {
+  compatible = "ingenic,jz4770-ipu", "ingenic,jz4760-ipu";
+  reg = <0x1308 0x800>;
+
+  interrupt-parent = <>;
+  interrupts = <29>;
+
+  clocks = < JZ4770_CLK_IPU>;
+  clock-names = "ipu";
+
+  port {
+ipu_ep: endpoint {
+  remote-endpoint = <_ep>;
+};
+  };
+};
-- 
2.26.2



[PATCH v7 5/5] MAINTAINERS: Add myself as reviewer for Ingenic rproc driver

2020-05-15 Thread Paul Cercueil
Add myself as the reviewer for the Ingenic VPU remoteproc driver.

Signed-off-by: Paul Cercueil 
---

Notes:
v4: New patch
v5-v7: No change

 MAINTAINERS | 1 +
 1 file changed, 1 insertion(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 091ec22c1a23..5d864d785574 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8414,6 +8414,7 @@ F:drivers/mtd/nand/raw/ingenic/
 F: drivers/pinctrl/pinctrl-ingenic.c
 F: drivers/power/supply/ingenic-battery.c
 F: drivers/pwm/pwm-jz4740.c
+F: drivers/remoteproc/ingenic_rproc.c
 F: drivers/rtc/rtc-jz4740.c
 F: drivers/tty/serial/8250/8250_ingenic.c
 F: drivers/usb/musb/jz4740.c
-- 
2.26.2



[PATCH v7 1/5] dt-bindings: Document JZ47xx VPU auxiliary processor

2020-05-15 Thread Paul Cercueil
Inside the Video Processing Unit (VPU) of the recent JZ47xx SoCs from
Ingenic is a second Xburst MIPS CPU very similar to the main core.
This document describes the devicetree bindings for this auxiliary
processor.

Signed-off-by: Paul Cercueil 
Reviewed-by: Rob Herring 
---

Notes:
v2: Update TCSM0 address in example
v3: Change node name to 'video-decoder'
v4: Convert to YAML. I didn't add Rob's Ack on v3 because of that (sorry 
Rob)
v5: - Fix 'reg' not in  pairs
- Add missing include to devicetree example
v6-v7: No change

 .../bindings/remoteproc/ingenic,vpu.yaml  | 77 +++
 1 file changed, 77 insertions(+)
 create mode 100644 
Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml

diff --git a/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml 
b/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml
new file mode 100644
index ..c019f9fbe916
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml
@@ -0,0 +1,77 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/remoteproc/ingenic,vpu.yaml#;
+$schema: "http://devicetree.org/meta-schemas/core.yaml#;
+
+title: Ingenic Video Processing Unit bindings
+
+description:
+  Inside the Video Processing Unit (VPU) of the recent JZ47xx SoCs from
+  Ingenic is a second Xburst MIPS CPU very similar to the main core.
+  This document describes the devicetree bindings for this auxiliary
+  processor.
+
+maintainers:
+  - Paul Cercueil 
+
+properties:
+  compatible:
+const: ingenic,jz4770-vpu-rproc
+
+  reg:
+items:
+  - description: aux registers
+  - description: tcsm0 registers
+  - description: tcsm1 registers
+  - description: sram registers
+
+  reg-names:
+items:
+  - const: aux
+  - const: tcsm0
+  - const: tcsm1
+  - const: sram
+
+  clocks:
+items:
+  - description: aux clock
+  - description: vpu clock
+
+  clock-names:
+items:
+  - const: aux
+  - const: vpu
+
+  interrupts:
+description: VPU hardware interrupt
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - clocks
+  - clock-names
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |
+#include 
+
+vpu: video-decoder@132a {
+  compatible = "ingenic,jz4770-vpu-rproc";
+
+  reg = <0x132a 0x20>, /* AUX */
+<0x132b 0x4000>, /* TCSM0 */
+<0x132c 0xc000>, /* TCSM1 */
+<0x132f 0x7000>; /* SRAM */
+  reg-names = "aux", "tcsm0", "tcsm1", "sram";
+
+  clocks = < JZ4770_CLK_AUX>, < JZ4770_CLK_VPU>;
+  clock-names = "aux", "vpu";
+
+  interrupt-parent = <>;
+  interrupts = <3>;
+};
-- 
2.26.2



[PATCH v7 2/5] remoteproc: Add device-managed variants of rproc_alloc/rproc_add

2020-05-15 Thread Paul Cercueil
Add API functions devm_rproc_alloc() and devm_rproc_add(), which behave
like rproc_alloc() and rproc_add() respectively, but register their
respective cleanup function to be called on driver detach.

Signed-off-by: Paul Cercueil 
Reviewed-by: Bjorn Andersson 
---

Notes:
v3: New patch
v4: No change
v5: - Fix return value documentation
- Fix typo in documentation
v6-v7: No change

 drivers/remoteproc/remoteproc_core.c | 67 
 include/linux/remoteproc.h   |  5 +++
 2 files changed, 72 insertions(+)

diff --git a/drivers/remoteproc/remoteproc_core.c 
b/drivers/remoteproc/remoteproc_core.c
index e12a54e67588..a7f96bc98406 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -1949,6 +1949,33 @@ int rproc_add(struct rproc *rproc)
 }
 EXPORT_SYMBOL(rproc_add);
 
+static void devm_rproc_remove(void *rproc)
+{
+   rproc_del(rproc);
+}
+
+/**
+ * devm_rproc_add() - resource managed rproc_add()
+ * @dev: the underlying device
+ * @rproc: the remote processor handle to register
+ *
+ * This function performs like rproc_add() but the registered rproc device will
+ * automatically be removed on driver detach.
+ *
+ * Returns: 0 on success, negative errno on failure
+ */
+int devm_rproc_add(struct device *dev, struct rproc *rproc)
+{
+   int err;
+
+   err = rproc_add(rproc);
+   if (err)
+   return err;
+
+   return devm_add_action_or_reset(dev, devm_rproc_remove, rproc);
+}
+EXPORT_SYMBOL(devm_rproc_add);
+
 /**
  * rproc_type_release() - release a remote processor instance
  * @dev: the rproc's device
@@ -2171,6 +2198,46 @@ int rproc_del(struct rproc *rproc)
 }
 EXPORT_SYMBOL(rproc_del);
 
+static void devm_rproc_free(struct device *dev, void *res)
+{
+   rproc_free(*(struct rproc **)res);
+}
+
+/**
+ * devm_rproc_alloc() - resource managed rproc_alloc()
+ * @dev: the underlying device
+ * @name: name of this remote processor
+ * @ops: platform-specific handlers (mainly start/stop)
+ * @firmware: name of firmware file to load, can be NULL
+ * @len: length of private data needed by the rproc driver (in bytes)
+ *
+ * This function performs like rproc_alloc() but the acquired rproc device will
+ * automatically be released on driver detach.
+ *
+ * Returns: new rproc instance, or NULL on failure
+ */
+struct rproc *devm_rproc_alloc(struct device *dev, const char *name,
+  const struct rproc_ops *ops,
+  const char *firmware, int len)
+{
+   struct rproc **ptr, *rproc;
+
+   ptr = devres_alloc(devm_rproc_free, sizeof(*ptr), GFP_KERNEL);
+   if (!ptr)
+   return ERR_PTR(-ENOMEM);
+
+   rproc = rproc_alloc(dev, name, ops, firmware, len);
+   if (rproc) {
+   *ptr = rproc;
+   devres_add(dev, ptr);
+   } else {
+   devres_free(ptr);
+   }
+
+   return rproc;
+}
+EXPORT_SYMBOL(devm_rproc_alloc);
+
 /**
  * rproc_add_subdev() - add a subdevice to a remoteproc
  * @rproc: rproc handle to add the subdevice to
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index 9c07d7958c53..8c9c0dda03c3 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -599,6 +599,11 @@ int rproc_add(struct rproc *rproc);
 int rproc_del(struct rproc *rproc);
 void rproc_free(struct rproc *rproc);
 
+struct rproc *devm_rproc_alloc(struct device *dev, const char *name,
+  const struct rproc_ops *ops,
+  const char *firmware, int len);
+int devm_rproc_add(struct device *dev, struct rproc *rproc);
+
 void rproc_add_carveout(struct rproc *rproc, struct rproc_mem_entry *mem);
 
 struct rproc_mem_entry *
-- 
2.26.2



[PATCH v7 3/5] remoteproc: Add support for runtime PM

2020-05-15 Thread Paul Cercueil
Call pm_runtime_get_sync() before the firmware is loaded, and
pm_runtime_put() after the remote processor has been stopped.

Even though the remoteproc device has no PM callbacks, this allows the
parent device's PM callbacks to be properly called.

Signed-off-by: Paul Cercueil 
---

Notes:
v2-v4: No change
v5: Move calls to prepare/unprepare to rproc_fw_boot/rproc_shutdown
v6: Instead of prepare/unprepare callbacks, use PM runtime callbacks
v7: Check return value of pm_runtime_get_sync()

 drivers/remoteproc/remoteproc_core.c | 17 -
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/remoteproc/remoteproc_core.c 
b/drivers/remoteproc/remoteproc_core.c
index a7f96bc98406..e33d1ef27981 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -29,6 +29,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -1382,6 +1383,12 @@ static int rproc_fw_boot(struct rproc *rproc, const 
struct firmware *fw)
if (ret)
return ret;
 
+   ret = pm_runtime_get_sync(dev);
+   if (ret < 0) {
+   dev_err(dev, "pm_runtime_get_sync failed: %d\n", ret);
+   return ret;
+   }
+
dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
 
/*
@@ -1391,7 +1398,7 @@ static int rproc_fw_boot(struct rproc *rproc, const 
struct firmware *fw)
ret = rproc_enable_iommu(rproc);
if (ret) {
dev_err(dev, "can't enable iommu: %d\n", ret);
-   return ret;
+   goto put_pm_runtime;
}
 
rproc->bootaddr = rproc_get_boot_addr(rproc, fw);
@@ -1435,6 +1442,8 @@ static int rproc_fw_boot(struct rproc *rproc, const 
struct firmware *fw)
rproc->table_ptr = NULL;
 disable_iommu:
rproc_disable_iommu(rproc);
+put_pm_runtime:
+   pm_runtime_put(dev);
return ret;
 }
 
@@ -1840,6 +1849,8 @@ void rproc_shutdown(struct rproc *rproc)
 
rproc_disable_iommu(rproc);
 
+   pm_runtime_put(dev);
+
/* Free the copy of the resource table */
kfree(rproc->cached_table);
rproc->cached_table = NULL;
@@ -2118,6 +2129,9 @@ struct rproc *rproc_alloc(struct device *dev, const char 
*name,
 
rproc->state = RPROC_OFFLINE;
 
+   pm_runtime_no_callbacks(>dev);
+   pm_runtime_enable(>dev);
+
return rproc;
 }
 EXPORT_SYMBOL(rproc_alloc);
@@ -2133,6 +2147,7 @@ EXPORT_SYMBOL(rproc_alloc);
  */
 void rproc_free(struct rproc *rproc)
 {
+   pm_runtime_disable(>dev);
put_device(>dev);
 }
 EXPORT_SYMBOL(rproc_free);
-- 
2.26.2



[PATCH v7 4/5] remoteproc: ingenic: Added remoteproc driver

2020-05-15 Thread Paul Cercueil
This driver is used to boot, communicate with and load firmwares to the
MIPS co-processor found in the VPU hardware of the JZ47xx SoCs from
Ingenic.

Signed-off-by: Paul Cercueil 
Acked-by: Mathieu Poirier 
---

Notes:
v2: Remove exception for always-mapped memories
v3: - Use clk_bulk API
- Move device-managed code to its own patch [3/4]
- Move devicetree table right above ingenic_rproc_driver
- Removed #ifdef CONFIG_OF around devicetree table
- Removed .owner = THIS_MODULE in ingenic_rproc_driver
- Removed useless platform_set_drvdata()
v4: - Add fix reported by Julia
- Change Kconfig symbol to INGENIC_VPU_RPROC
- Add documentation to struct vpu
- disable_irq_nosync() -> disable_irq()
v5: No change
v6: Instead of prepare/unprepare callbacks, use PM runtime callbacks
v7: - Remove use of of_match_ptr()
- Move Kconfig symbol so that it's in alphabetical order
- Add missing doc for private structure field aux_base
- Don't check for (len <= 0) in da_to_va()
- Add missing \n in dev_info/dev_err messages

 drivers/remoteproc/Kconfig |   9 +
 drivers/remoteproc/Makefile|   1 +
 drivers/remoteproc/ingenic_rproc.c | 280 +
 3 files changed, 290 insertions(+)
 create mode 100644 drivers/remoteproc/ingenic_rproc.c

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index fbaed079b299..c4d1731295eb 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -23,6 +23,15 @@ config IMX_REMOTEPROC
 
  It's safe to say N here.
 
+config INGENIC_VPU_RPROC
+   tristate "Ingenic JZ47xx VPU remoteproc support"
+   depends on MIPS || COMPILE_TEST
+   help
+ Say y or m here to support the VPU in the JZ47xx SoCs from Ingenic.
+
+ This can be either built-in or a loadable module.
+ If unsure say N.
+
 config MTK_SCP
tristate "Mediatek SCP support"
depends on ARCH_MEDIATEK
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 0effd3825035..e8b886e511f0 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -10,6 +10,7 @@ remoteproc-y  += remoteproc_sysfs.o
 remoteproc-y   += remoteproc_virtio.o
 remoteproc-y   += remoteproc_elf_loader.o
 obj-$(CONFIG_IMX_REMOTEPROC)   += imx_rproc.o
+obj-$(CONFIG_INGENIC_VPU_RPROC)+= ingenic_rproc.o
 obj-$(CONFIG_MTK_SCP)  += mtk_scp.o mtk_scp_ipi.o
 obj-$(CONFIG_OMAP_REMOTEPROC)  += omap_remoteproc.o
 obj-$(CONFIG_WKUP_M3_RPROC)+= wkup_m3_rproc.o
diff --git a/drivers/remoteproc/ingenic_rproc.c 
b/drivers/remoteproc/ingenic_rproc.c
new file mode 100644
index ..189020d77b25
--- /dev/null
+++ b/drivers/remoteproc/ingenic_rproc.c
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Ingenic JZ47xx remoteproc driver
+ * Copyright 2019, Paul Cercueil 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "remoteproc_internal.h"
+
+#define REG_AUX_CTRL   0x0
+#define REG_AUX_MSG_ACK0x10
+#define REG_AUX_MSG0x14
+#define REG_CORE_MSG_ACK   0x18
+#define REG_CORE_MSG   0x1C
+
+#define AUX_CTRL_SLEEP BIT(31)
+#define AUX_CTRL_MSG_IRQ_ENBIT(3)
+#define AUX_CTRL_NMI_RESETSBIT(2)
+#define AUX_CTRL_NMI   BIT(1)
+#define AUX_CTRL_SW_RESET  BIT(0)
+
+struct vpu_mem_map {
+   const char *name;
+   unsigned int da;
+};
+
+struct vpu_mem_info {
+   const struct vpu_mem_map *map;
+   unsigned long len;
+   void __iomem *base;
+};
+
+static const struct vpu_mem_map vpu_mem_map[] = {
+   { "tcsm0", 0x132b },
+   { "tcsm1", 0xf400 },
+   { "sram",  0x132f },
+};
+
+/**
+ * struct vpu - Ingenic VPU remoteproc private structure
+ * @irq: interrupt number
+ * @clks: pointers to the VPU and AUX clocks
+ * @aux_base: raw pointer to the AUX interface registers
+ * @mem_info: array of struct vpu_mem_info, which contain the mapping info of
+ *each of the external memories
+ * @dev: private pointer to the device
+ */
+struct vpu {
+   int irq;
+   struct clk_bulk_data clks[2];
+   void __iomem *aux_base;
+   struct vpu_mem_info mem_info[ARRAY_SIZE(vpu_mem_map)];
+   struct device *dev;
+};
+
+static int ingenic_rproc_start(struct rproc *rproc)
+{
+   struct vpu *vpu = rproc->priv;
+   u32 ctrl;
+
+   enable_irq(vpu->irq);
+
+   /* Reset the AUX and enable message IRQ */
+   ctrl = AUX_CTRL_NMI_RESETS | AUX_CTRL_NMI | AUX_CTRL_MSG_IRQ_EN;
+   writel(ctrl, vpu->aux_base + REG_AUX_CTRL);
+
+   return 0;
+}
+
+static int ingenic_rproc_stop(struct rproc *rpro

Re: [PATCH] drm/etnaviv: fix perfmon domain interation

2020-05-15 Thread Paul Cercueil

Hi Christian,

Le ven. 15 mai 2020 à 12:09, Christian Gmeiner 
 a écrit :

Am Mo., 11. Mai 2020 um 14:38 Uhr schrieb Christian Gmeiner
:


 The GC860 has one GPU device which has a 2d and 3d core. In this 
case

 we want to expose perfmon information for both cores.

 The driver has one array which contains all possible perfmon domains
 with some meta data - doms_meta. Here we can see that for the GC860
 two elements of that array are relevant:

   doms_3d: is at index 0 in the doms_meta array with 8 perfmon 
domains
   doms_2d: is at index 1 in the doms_meta array with 1 perfmon 
domain


 The userspace driver wants to get a list of all perfmon domains and
 their perfmon signals. This is done by iterating over all domains 
and
 their signals. If the userspace driver wants to access the domain 
with
 id 8 the kernel driver fails and returns invalid data from doms_3d 
with

 and invalid offset.

 This results in:
   Unable to handle kernel paging request at virtual address 

 On such a device it is not possible to use the userspace driver at 
all.


 The fix for this off-by-one error is quite simple.

 Reported-by: Paul Cercueil 
 Tested-by: Paul Cercueil 
 Fixes: ed1dd899baa3 ("drm/etnaviv: rework perfmon query 
infrastructure")

 Cc: sta...@vger.kernel.org
 Signed-off-by: Christian Gmeiner 
 ---
  drivers/gpu/drm/etnaviv/etnaviv_perfmon.c | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)

 diff --git a/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c 
b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c

 index e6795bafcbb9..35f7171e779a 100644
 --- a/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c
 +++ b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c
 @@ -453,7 +453,7 @@ static const struct etnaviv_pm_domain 
*pm_domain(const struct etnaviv_gpu *gpu,

 if (!(gpu->identity.features & meta->feature))
 continue;

 -   if (meta->nr_domains < (index - offset)) {
 +   if ((meta->nr_domains - 1) < (index - offset)) {
 offset += meta->nr_domains;
 continue;
 }
 --
 2.26.2



ping


I'll merge it tomorrow if there's no further feedback.

-Paul




Re: DRM interaction problems on Ingenic CI20 / jz4780 with dw-hdmi and ingenic-drm

2020-05-09 Thread Paul Cercueil

Hi Paul,

Le mar. 5 mai 2020 à 20:26, Paul Boddie  a écrit :

On Monday 4. May 2020 03.05.22 Paul Cercueil wrote:


 > Le sam. 11 avril 2020 à 16:14, H. Nikolaus Schaller 
 a

 > écrit :
 >>
 >> So far we have identified two issues.
 >>
 >> The first is that HPD interrupts are not properly processed.
 >>
 >> drm_helper_hpd_irq_event() is called by HPD events but
 >> dev->mode_config.poll_enabled is false.

 This is to be used when there's no hardware interrupt. I believe you
 have one, right? Then call drm_kms_helper_hotplug_event() from the
 interrupt handler instead.


What we have in drivers/gpu/drm/bridge/synopsys/dw-hdmi.c is a 
function called
dw_hdmi_irq which appears to be the thread_fn for HDMI interrupts 
(alongside

the dw_hdmi_hardirq which is the handler), initialised by a call to
devm_request_threaded_irq.

In dw_hdmi_irq, a hotplug event seems to cause 
drm_helper_hpd_irq_event to be

called. However...

[...]


 >> The jz4780 hdmi subsystem (drm/bridge/dw-hdmi.c) uses
 >>
 >>   connector->polled = DRM_CONNECTOR_POLL_HPD;
 >>
 >> but shouldn't this enable polling? Note that there seems to be
 >> no (direct) call to drm_kms_helper_poll_init().
 >>
 >> If we set dev->mode_config.poll_enabled = true in
 >> drm_helper_hpd_irq_event() things start to work.
 >>
 >> Please can you clarify what would be best practise here to
 >> get HPD event handling working.

 Remove that - this stuff is for hardware without interrupts, where
 everything has to be polled.


Yes, I think this must be a mistake in the driver. In
drm_helper_hpd_irq_event, the connector is tested for the
DRM_CONNECTOR_POLL_HPD flag and skips the connector. It isn't clear 
whether

this actually matters for the other hardware using this technology,
documentation being rather thin on the ground.


 >> The other issue is in dw-hdmi.c:
 >>
 >> We found out that ingenic_drm_encoder_atomic_check() fails 
because

 >>
 >> info->num_bus_formats == 0
 >>
 >> and not 1. This blocks further initialization.
 >>
 >> The reason seems to be that dw_hdmi_bridge_attach() does not call
 >> drm_display_info_set_bus_formats() with a proper format like
 >> other drivers (e.g. drm/bridge/ti-tfp410.c) are doing.
 >>
 >> We have patched to set a single bus format 
MEDIA_BUS_FMT_RGB888_1X24

 >> and then DRM setup seems to work (although we still have no valid
 >> HDMI signal but that is likely something else).
 >>
 >> Please can you explain how setting the bus format should be fixed
 >> in dw-hdmi.c.

 I'm not sure, but that information may come from EDID data. Are you
 able to obtain video modes from the connected monitor?


The modes are definitely received, or at least the list of modes 
given by
/sys/devices/platform/1305.lcd/drm/card0/card0-HDMI-A-1/modes is 
viable.


However, it rather looked like the bus format information wasn't 
being set and
that this inhibited the completion of the initialisation process 
which, if
completed, would ultimately cause the format to be set. (This being 
the short
version of the story as I remember it right now.) So, the problem 
presents

itself as an initialisation order problem.


It's not an initialization order problem, the ingenic-drm expects the 
bus_formats to be provided and the synopsis driver never calls 
drm_display_info_set_bus_formats().


I removed the flag from the connector to see if it makes any 
difference, but
it doesn't look like it. Here is what /sys/kernel/debug/dri/0/state 
contains:


plane[31]: plane-0
crtc=(null)
fb=0
crtc-pos=0x0+0+0
src-pos=0.00x0.00+0.00+0.00
rotation=1
normalized-zpos=0
color-encoding=ITU-R BT.601 YCbCr
color-range=YCbCr limited range
crtc[32]: crtc-0
enable=0
active=0
self_refresh_active=0
planes_changed=0
mode_changed=0
active_changed=0
connectors_changed=0
color_mgmt_changed=0
plane_mask=0
connector_mask=0
encoder_mask=0
mode: "": 0 0 0 0 0 0 0 0 0 0 0x0 0x0
connector[34]: HDMI-A-1
crtc=(null)
self_refresh_aware=0

The crtc member values do not look encouraging. In fact, it just 
looks like

most structure members are uninitialised.

Thanks for the advice: I spent some time the other day reviewing 
various
aspects of the Synopsys drivers of different vintages (Ingenic 3.0.8 
non-DRM
driver for JZ4780, MIPS/Ingenic 3.18 DRM driver for JZ4780 based on 
Freescale
driver code, the recent generic DRM bridge driver), and so this 
information is

timely and valuable.


With a hardcoded bus_format of RGB888 in the ingenic-drm driver, and 
jz4780_dw_hdmi_plat_data.input_bus_format initialized too:


modetest -D /dev/dri/card0 -M 

[PATCH 5/7] rtc: ingenic: Remove unused fields from private structure

2020-05-05 Thread Paul Cercueil
The 'clk' and 'irq' fields were only ever used in the probe function.
Therefore they can be moved to be simple local variables of the probe
function.

Signed-off-by: Paul Cercueil 
---
 drivers/rtc/rtc-jz4740.c | 26 --
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index 8927fd0fb086..3193eb8bd131 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -55,9 +55,6 @@ struct jz4740_rtc {
enum jz4740_rtc_type type;
 
struct rtc_device *rtc;
-   struct clk *clk;
-
-   int irq;
 
spinlock_t lock;
 };
@@ -313,9 +310,10 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
 {
struct device *dev = >dev;
struct device_node *np = dev->of_node;
-   int ret;
struct jz4740_rtc *rtc;
unsigned long rate;
+   struct clk *clk;
+   int ret, irq;
 
rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
@@ -323,27 +321,27 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
 
rtc->type = (enum jz4740_rtc_type)device_get_match_data(dev);
 
-   rtc->irq = platform_get_irq(pdev, 0);
-   if (rtc->irq < 0)
+   irq = platform_get_irq(pdev, 0);
+   if (irq < 0)
return -ENOENT;
 
rtc->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(rtc->base))
return PTR_ERR(rtc->base);
 
-   rtc->clk = devm_clk_get(dev, "rtc");
-   if (IS_ERR(rtc->clk)) {
+   clk = devm_clk_get(dev, "rtc");
+   if (IS_ERR(clk)) {
dev_err(dev, "Failed to get RTC clock\n");
-   return PTR_ERR(rtc->clk);
+   return PTR_ERR(clk);
}
 
-   ret = clk_prepare_enable(rtc->clk);
+   ret = clk_prepare_enable(clk);
if (ret) {
dev_err(dev, "Failed to enable clock\n");
return ret;
}
 
-   ret = devm_add_action_or_reset(dev, jz4740_rtc_clk_disable, rtc->clk);
+   ret = devm_add_action_or_reset(dev, jz4740_rtc_clk_disable, clk);
if (ret) {
dev_err(dev, "Failed to register devm action\n");
return ret;
@@ -355,7 +353,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
 
device_init_wakeup(dev, 1);
 
-   ret = dev_pm_set_wake_irq(dev, rtc->irq);
+   ret = dev_pm_set_wake_irq(dev, irq);
if (ret) {
dev_err(dev, "Failed to set wake irq: %d\n", ret);
return ret;
@@ -371,14 +369,14 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
rtc->rtc->ops = _rtc_ops;
rtc->rtc->range_max = U32_MAX;
 
-   rate = clk_get_rate(rtc->clk);
+   rate = clk_get_rate(clk);
jz4740_rtc_set_wakeup_params(rtc, np, rate);
 
ret = rtc_register_device(rtc->rtc);
if (ret)
return ret;
 
-   ret = devm_request_irq(dev, rtc->irq, jz4740_rtc_irq, 0,
+   ret = devm_request_irq(dev, irq, jz4740_rtc_irq, 0,
   pdev->name, rtc);
if (ret) {
dev_err(dev, "Failed to request rtc irq: %d\n", ret);
-- 
2.26.2



[PATCH 4/7] rtc: ingenic: Set wakeup params in probe

2020-05-05 Thread Paul Cercueil
We can write the wakeup timing parameters as soon as the driver probes,
there's no need to wait the very last moment.

Signed-off-by: Paul Cercueil 
---
 drivers/rtc/rtc-jz4740.c | 95 +++-
 1 file changed, 44 insertions(+), 51 deletions(-)

diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index 129c68cebb92..8927fd0fb086 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -60,9 +60,6 @@ struct jz4740_rtc {
int irq;
 
spinlock_t lock;
-
-   unsigned int min_wakeup_pin_assert_time;
-   unsigned int reset_pin_assert_time;
 };
 
 static struct device *dev_for_power_off;
@@ -259,38 +256,6 @@ static void jz4740_rtc_poweroff(struct device *dev)
 
 static void jz4740_rtc_power_off(void)
 {
-   struct jz4740_rtc *rtc = dev_get_drvdata(dev_for_power_off);
-   unsigned long rtc_rate;
-   unsigned long wakeup_filter_ticks;
-   unsigned long reset_counter_ticks;
-
-   rtc_rate = clk_get_rate(rtc->clk);
-
-   /*
-* Set minimum wakeup pin assertion time: 100 ms.
-* Range is 0 to 2 sec if RTC is clocked at 32 kHz.
-*/
-   wakeup_filter_ticks =
-   (rtc->min_wakeup_pin_assert_time * rtc_rate) / 1000;
-   if (wakeup_filter_ticks < JZ_RTC_WAKEUP_FILTER_MASK)
-   wakeup_filter_ticks &= JZ_RTC_WAKEUP_FILTER_MASK;
-   else
-   wakeup_filter_ticks = JZ_RTC_WAKEUP_FILTER_MASK;
-   jz4740_rtc_reg_write(rtc,
-JZ_REG_RTC_WAKEUP_FILTER, wakeup_filter_ticks);
-
-   /*
-* Set reset pin low-level assertion time after wakeup: 60 ms.
-* Range is 0 to 125 ms if RTC is clocked at 32 kHz.
-*/
-   reset_counter_ticks = (rtc->reset_pin_assert_time * rtc_rate) / 1000;
-   if (reset_counter_ticks < JZ_RTC_RESET_COUNTER_MASK)
-   reset_counter_ticks &= JZ_RTC_RESET_COUNTER_MASK;
-   else
-   reset_counter_ticks = JZ_RTC_RESET_COUNTER_MASK;
-   jz4740_rtc_reg_write(rtc,
-JZ_REG_RTC_RESET_COUNTER, reset_counter_ticks);
-
jz4740_rtc_poweroff(dev_for_power_off);
kernel_halt();
 }
@@ -308,12 +273,49 @@ static const struct of_device_id jz4740_rtc_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, jz4740_rtc_of_match);
 
+static void jz4740_rtc_set_wakeup_params(struct jz4740_rtc *rtc,
+struct device_node *np,
+unsigned long rate)
+{
+   unsigned long wakeup_ticks, reset_ticks;
+   unsigned int min_wakeup_pin_assert_time = 60; /* Default: 60ms */
+   unsigned int reset_pin_assert_time = 100; /* Default: 100ms */
+
+   of_property_read_u32(np, "ingenic,reset-pin-assert-time-ms",
+_pin_assert_time);
+   of_property_read_u32(np, "ingenic,min-wakeup-pin-assert-time-ms",
+_wakeup_pin_assert_time);
+
+   /*
+* Set minimum wakeup pin assertion time: 100 ms.
+* Range is 0 to 2 sec if RTC is clocked at 32 kHz.
+*/
+   wakeup_ticks = (min_wakeup_pin_assert_time * rate) / 1000;
+   if (wakeup_ticks < JZ_RTC_WAKEUP_FILTER_MASK)
+   wakeup_ticks &= JZ_RTC_WAKEUP_FILTER_MASK;
+   else
+   wakeup_ticks = JZ_RTC_WAKEUP_FILTER_MASK;
+   jz4740_rtc_reg_write(rtc, JZ_REG_RTC_WAKEUP_FILTER, wakeup_ticks);
+
+   /*
+* Set reset pin low-level assertion time after wakeup: 60 ms.
+* Range is 0 to 125 ms if RTC is clocked at 32 kHz.
+*/
+   reset_ticks = (reset_pin_assert_time * rate) / 1000;
+   if (reset_ticks < JZ_RTC_RESET_COUNTER_MASK)
+   reset_ticks &= JZ_RTC_RESET_COUNTER_MASK;
+   else
+   reset_ticks = JZ_RTC_RESET_COUNTER_MASK;
+   jz4740_rtc_reg_write(rtc, JZ_REG_RTC_RESET_COUNTER, reset_ticks);
+}
+
 static int jz4740_rtc_probe(struct platform_device *pdev)
 {
struct device *dev = >dev;
struct device_node *np = dev->of_node;
int ret;
struct jz4740_rtc *rtc;
+   unsigned long rate;
 
rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
@@ -369,6 +371,9 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
rtc->rtc->ops = _rtc_ops;
rtc->rtc->range_max = U32_MAX;
 
+   rate = clk_get_rate(rtc->clk);
+   jz4740_rtc_set_wakeup_params(rtc, np, rate);
+
ret = rtc_register_device(rtc->rtc);
if (ret)
return ret;
@@ -381,24 +386,12 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
}
 
if (of_device_is_system_power_controller(np)) {
-   if (!pm_power_off) {
-   /* Default: 60ms */
-   rtc->reset_pin_assert_time = 60;
-   of_property_read_u3

[PATCH 6/7] rtc: ingenic: Fix masking of error code

2020-05-05 Thread Paul Cercueil
The code was returning -ENOENT on any error of platform_get_irq(), even
if it returned a different error.

Signed-off-by: Paul Cercueil 
---
 drivers/rtc/rtc-jz4740.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index 3193eb8bd131..65e130726fc6 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -323,7 +323,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
 
irq = platform_get_irq(pdev, 0);
if (irq < 0)
-   return -ENOENT;
+   return irq;
 
rtc->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(rtc->base))
-- 
2.26.2



[PATCH 1/7] rtc: ingenic: Only support probing from devicetree

2020-05-05 Thread Paul Cercueil
With the recent work on supporting Device Tree on Ingenic SoCs, no
driver ever probes from platform code anymore, so we can clean a bit
this driver by removing the non-devicetree paths.

Signed-off-by: Paul Cercueil 
---
 drivers/rtc/Kconfig  |  1 +
 drivers/rtc/rtc-jz4740.c | 20 +++-
 2 files changed, 4 insertions(+), 17 deletions(-)

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index ec873f09c763..82a210920c1d 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1680,6 +1680,7 @@ config RTC_DRV_MPC5121
 config RTC_DRV_JZ4740
tristate "Ingenic JZ4740 SoC"
depends on MIPS || COMPILE_TEST
+   depends on OF
help
  If you say yes here you get support for the Ingenic JZ47xx SoCs RTC
  controllers.
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index e4c719085c31..949d395066e2 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -309,19 +309,13 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
 {
int ret;
struct jz4740_rtc *rtc;
-   const struct platform_device_id *id = platform_get_device_id(pdev);
-   const struct of_device_id *of_id = of_match_device(
-   jz4740_rtc_of_match, >dev);
struct device_node *np = pdev->dev.of_node;
 
rtc = devm_kzalloc(>dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return -ENOMEM;
 
-   if (of_id)
-   rtc->type = (enum jz4740_rtc_type)of_id->data;
-   else
-   rtc->type = id->driver_data;
+   rtc->type = (enum jz4740_rtc_type)device_get_match_data(dev);
 
rtc->irq = platform_get_irq(pdev, 0);
if (rtc->irq < 0)
@@ -370,7 +364,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
return ret;
}
 
-   if (np && of_device_is_system_power_controller(np)) {
+   if (of_device_is_system_power_controller(np)) {
if (!pm_power_off) {
/* Default: 60ms */
rtc->reset_pin_assert_time = 60;
@@ -395,20 +389,12 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
return 0;
 }
 
-static const struct platform_device_id jz4740_rtc_ids[] = {
-   { "jz4740-rtc", ID_JZ4740 },
-   { "jz4780-rtc", ID_JZ4780 },
-   {}
-};
-MODULE_DEVICE_TABLE(platform, jz4740_rtc_ids);
-
 static struct platform_driver jz4740_rtc_driver = {
.probe   = jz4740_rtc_probe,
.driver  = {
.name  = "jz4740-rtc",
-   .of_match_table = of_match_ptr(jz4740_rtc_of_match),
+   .of_match_table = jz4740_rtc_of_match,
},
-   .id_table = jz4740_rtc_ids,
 };
 
 module_platform_driver(jz4740_rtc_driver);
-- 
2.26.2



[PATCH 7/7] rtc: ingenic: Reset regulator register in probe

2020-05-05 Thread Paul Cercueil
The regulator register specifies how many input clock cycles (minus one)
are contained in one tick of the 1 Hz clock.

Since this register can contain bogus values after the system boots, it
needs to be reset in the probe register, otherwise the RTC may count way
to slow or way too fast.

Signed-off-by: Paul Cercueil 
---
 drivers/rtc/rtc-jz4740.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index 65e130726fc6..9607e6b6e0b3 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -372,6 +372,9 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
rate = clk_get_rate(clk);
jz4740_rtc_set_wakeup_params(rtc, np, rate);
 
+   /* Each 1 Hz pulse should happen after (rate) ticks */
+   jz4740_rtc_reg_write(rtc, JZ_REG_RTC_REGULATOR, rate - 1);
+
ret = rtc_register_device(rtc->rtc);
if (ret)
return ret;
-- 
2.26.2



[PATCH 3/7] rtc: ingenic: Enable clock in probe

2020-05-05 Thread Paul Cercueil
It makes no sense to request a clock and not enable it even though the
hardware is being used. So the driver now enables the clock in the
probe. Besides, now we can properly handle errors.

Signed-off-by: Paul Cercueil 
---
 drivers/rtc/rtc-jz4740.c | 19 +--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index 06ee08089815..129c68cebb92 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -264,8 +264,6 @@ static void jz4740_rtc_power_off(void)
unsigned long wakeup_filter_ticks;
unsigned long reset_counter_ticks;
 
-   clk_prepare_enable(rtc->clk);
-
rtc_rate = clk_get_rate(rtc->clk);
 
/*
@@ -297,6 +295,11 @@ static void jz4740_rtc_power_off(void)
kernel_halt();
 }
 
+static void jz4740_rtc_clk_disable(void *data)
+{
+   clk_disable_unprepare(data);
+}
+
 static const struct of_device_id jz4740_rtc_of_match[] = {
{ .compatible = "ingenic,jz4740-rtc", .data = (void *)ID_JZ4740 },
{ .compatible = "ingenic,jz4760-rtc", .data = (void *)ID_JZ4760 },
@@ -332,6 +335,18 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
return PTR_ERR(rtc->clk);
}
 
+   ret = clk_prepare_enable(rtc->clk);
+   if (ret) {
+   dev_err(dev, "Failed to enable clock\n");
+   return ret;
+   }
+
+   ret = devm_add_action_or_reset(dev, jz4740_rtc_clk_disable, rtc->clk);
+   if (ret) {
+   dev_err(dev, "Failed to register devm action\n");
+   return ret;
+   }
+
spin_lock_init(>lock);
 
platform_set_drvdata(pdev, rtc);
-- 
2.26.2



[PATCH 2/7] rtc: ingenic: Use local 'dev' variable in probe

2020-05-05 Thread Paul Cercueil
Clean a bit the probe function by adding a local struct device *dev
variable.

Signed-off-by: Paul Cercueil 
---
 drivers/rtc/rtc-jz4740.c | 30 +++---
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index 949d395066e2..06ee08089815 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -307,11 +307,12 @@ MODULE_DEVICE_TABLE(of, jz4740_rtc_of_match);
 
 static int jz4740_rtc_probe(struct platform_device *pdev)
 {
+   struct device *dev = >dev;
+   struct device_node *np = dev->of_node;
int ret;
struct jz4740_rtc *rtc;
-   struct device_node *np = pdev->dev.of_node;
 
-   rtc = devm_kzalloc(>dev, sizeof(*rtc), GFP_KERNEL);
+   rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return -ENOMEM;
 
@@ -325,9 +326,9 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
if (IS_ERR(rtc->base))
return PTR_ERR(rtc->base);
 
-   rtc->clk = devm_clk_get(>dev, "rtc");
+   rtc->clk = devm_clk_get(dev, "rtc");
if (IS_ERR(rtc->clk)) {
-   dev_err(>dev, "Failed to get RTC clock\n");
+   dev_err(dev, "Failed to get RTC clock\n");
return PTR_ERR(rtc->clk);
}
 
@@ -335,18 +336,18 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
 
platform_set_drvdata(pdev, rtc);
 
-   device_init_wakeup(>dev, 1);
+   device_init_wakeup(dev, 1);
 
-   ret = dev_pm_set_wake_irq(>dev, rtc->irq);
+   ret = dev_pm_set_wake_irq(dev, rtc->irq);
if (ret) {
-   dev_err(>dev, "Failed to set wake irq: %d\n", ret);
+   dev_err(dev, "Failed to set wake irq: %d\n", ret);
return ret;
}
 
-   rtc->rtc = devm_rtc_allocate_device(>dev);
+   rtc->rtc = devm_rtc_allocate_device(dev);
if (IS_ERR(rtc->rtc)) {
ret = PTR_ERR(rtc->rtc);
-   dev_err(>dev, "Failed to allocate rtc device: %d\n", ret);
+   dev_err(dev, "Failed to allocate rtc device: %d\n", ret);
return ret;
}
 
@@ -357,10 +358,10 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
if (ret)
return ret;
 
-   ret = devm_request_irq(>dev, rtc->irq, jz4740_rtc_irq, 0,
-   pdev->name, rtc);
+   ret = devm_request_irq(dev, rtc->irq, jz4740_rtc_irq, 0,
+  pdev->name, rtc);
if (ret) {
-   dev_err(>dev, "Failed to request rtc irq: %d\n", ret);
+   dev_err(dev, "Failed to request rtc irq: %d\n", ret);
return ret;
}
 
@@ -378,11 +379,10 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
 
"ingenic,min-wakeup-pin-assert-time-ms",
 >min_wakeup_pin_assert_time);
 
-   dev_for_power_off = >dev;
+   dev_for_power_off = dev;
pm_power_off = jz4740_rtc_power_off;
} else {
-   dev_warn(>dev,
-"Poweroff handler already present!\n");
+   dev_warn(dev, "Poweroff handler already present!\n");
}
}
 
-- 
2.26.2



Re: [PATCH] drm: ingenic-drm: add MODULE_DEVICE_TABLE

2020-05-04 Thread Paul Cercueil

Hi Nikolaus,


Le lun. 4 mai 2020 à 8:35, H. Nikolaus Schaller  a 
écrit :

so that the driver can load by matching the device tree
if compiled as module.

Cc: sta...@vger.kernel.org # v5.3+
Fixes: 90b86fcc47b4 ("DRM: Add KMS driver for the Ingenic JZ47xx 
SoCs")

Signed-off-by: H. Nikolaus Schaller 


Applied, thanks.

-Paul


---
 drivers/gpu/drm/ingenic/ingenic-drm.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c

index 9dfe7cb530e11..1754c05470690 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -843,6 +843,7 @@ static const struct of_device_id 
ingenic_drm_of_match[] = {

{ .compatible = "ingenic,jz4770-lcd", .data = _soc_info },
{ /* sentinel */ },
 };
+MODULE_DEVICE_TABLE(of, ingenic_drm_of_match);

 static struct platform_driver ingenic_drm_driver = {
.driver = {
--
2.26.2






Re: [RFC v3 2/8] drm: ingenic-drm: add MODULE_DEVICE_TABLE

2020-05-03 Thread Paul Cercueil

Hi Nikolaus,


Le dim. 29 mars 2020 à 19:35, H. Nikolaus Schaller  
a écrit :

so that the driver can load by matching the device tree
if compiled as module.

Signed-off-by: H. Nikolaus Schaller 


Please add:

Cc: sta...@vger.kernel.org # v5.3+
Fixes: 90b86fcc47b4 ("DRM: Add KMS driver for the Ingenic JZ47xx SoCs")

And re-send this patch alone, then I can merge it ASAP.

Cheers,
-Paul


---
 drivers/gpu/drm/ingenic/ingenic-drm.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c

index 6d47ef7b148c..bcba2f024842 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -843,6 +843,7 @@ static const struct of_device_id 
ingenic_drm_of_match[] = {

{ .compatible = "ingenic,jz4770-lcd", .data = _soc_info },
{ /* sentinel */ },
 };
+MODULE_DEVICE_TABLE(of, ingenic_drm_of_match);

 static struct platform_driver ingenic_drm_driver = {
.driver = {
--
2.25.1






Re: DRM interaction problems on Ingenic CI20 / jz4780 with dw-hdmi and ingenic-drm

2020-05-03 Thread Paul Cercueil

Hi Nikolaus,

So I just wrote a HDMI driver for a different chip, I guess I can 
answer some of your questions now.



Le lun. 13 avril 2020 à 13:25, Paul Cercueil  a 
écrit :

Hi Nikolaus,


Le sam. 11 avril 2020 à 16:14, H. Nikolaus Schaller 
 a écrit :

Hi,
we (Paul Boddie and me) are working to get HDMI functional on the
Ingenic CI20 board with jz4780 SoC which uses a specialization of
the dw-hdmi driver.


So far we have identified two issues.

The first is that HPD interrupts are not properly processed.

drm_helper_hpd_irq_event() is called by HPD events but
dev->mode_config.poll_enabled is false.



This is to be used when there's no hardware interrupt. I believe you 
have one, right? Then call drm_kms_helper_hotplug_event() from the 
interrupt handler instead.



Therefore the interrupt is ignored and nothing happens.

Now I wonder about the logic behind checking for poll_enabled.
I understand that a driver can do either polling or irq or both.

Therefore handling the irq_event shouldn't be disabled by 
poll_enabled

being false. Otherwise we can only do: nothing, polling, polling+irq
but not irq alone.

The jz4780 hdmi subsystem (drm/bridge/dw-hdmi.c) uses

connector->polled = DRM_CONNECTOR_POLL_HPD;

but shouldn't this enable polling? Note that there seems to be
no (direct) call to drm_kms_helper_poll_init().

If we set dev->mode_config.poll_enabled = true in
drm_helper_hpd_irq_event() things start to work.

Please can you clarify what would be best practise here to
get HPD event handling working.



Remove that - this stuff is for hardware without interrupts, where 
everything has to be polled.



The other issue is in dw-hdmi.c:

We found out that ingenic_drm_encoder_atomic_check() fails because

info->num_bus_formats == 0

and not 1. This blocks further initialization.

The reason seems to be that dw_hdmi_bridge_attach() does not call
drm_display_info_set_bus_formats() with a proper format like
other drivers (e.g. drm/bridge/ti-tfp410.c) are doing.

We have patched to set a single bus format MEDIA_BUS_FMT_RGB888_1X24
and then DRM setup seems to work (although we still have no valid
HDMI signal but that is likely something else).

Please can you explain how setting the bus format should be fixed
in dw-hdmi.c.



I'm not sure, but that information may come from EDID data. Are you 
able to obtain video modes from the connected monitor?


-Paul


If these questions should be forwarded to other specialists, please
do so.


It should be sent to the DRI mailing list, you missed the most 
important one.


-Paul



BR and thanks,
Nikolaus Schaller





[PATCH] gpu/drm: ingenic: Fix bogus crtc_atomic_check callback

2020-05-03 Thread Paul Cercueil
The code was comparing the SoC's maximum height with the mode's width,
and vice-versa. D'oh.

Cc: sta...@vger.kernel.org # v5.6+
Fixes: a7c909b7c037 ("gpu/drm: ingenic: Check for display size in CRTC atomic 
check")
Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c 
b/drivers/gpu/drm/ingenic/ingenic-drm.c
index 9dfe7cb530e1..5f19e07c152e 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm.c
@@ -328,8 +328,8 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc 
*crtc,
if (!drm_atomic_crtc_needs_modeset(state))
return 0;
 
-   if (state->mode.hdisplay > priv->soc_info->max_height ||
-   state->mode.vdisplay > priv->soc_info->max_width)
+   if (state->mode.hdisplay > priv->soc_info->max_width ||
+   state->mode.vdisplay > priv->soc_info->max_height)
return -EINVAL;
 
rate = clk_round_rate(priv->pix_clk,
-- 
2.26.2



[PATCH] pinctrl: ingenic: Add irq_{request,release}_resources callbacks

2020-05-03 Thread Paul Cercueil
These are called when a GPIO is to be used as IRQ.

Without these custom callbacks, when an interrupt is requested directly
and not through gpiod_to_irq(), the request fails because the GPIO is
not necesarily in input mode. These callbacks simply enforce that the
requested GPIO is in input mode.

Signed-off-by: Paul Cercueil 
---
 drivers/pinctrl/pinctrl-ingenic.c | 21 +
 1 file changed, 21 insertions(+)

diff --git a/drivers/pinctrl/pinctrl-ingenic.c 
b/drivers/pinctrl/pinctrl-ingenic.c
index 96f04d121ebd..f2b95ee31ffe 100644
--- a/drivers/pinctrl/pinctrl-ingenic.c
+++ b/drivers/pinctrl/pinctrl-ingenic.c
@@ -1933,6 +1933,25 @@ static const struct pinctrl_ops ingenic_pctlops = {
.dt_free_map = pinconf_generic_dt_free_map,
 };
 
+static int ingenic_gpio_irq_request(struct irq_data *data)
+{
+   struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
+   int ret;
+
+   ret = ingenic_gpio_direction_input(gpio_chip, data->hwirq);
+   if (ret)
+   return ret;
+
+   return gpiochip_reqres_irq(gpio_chip, data->hwirq);
+}
+
+static void ingenic_gpio_irq_release(struct irq_data *data)
+{
+   struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data);
+
+   return gpiochip_relres_irq(gpio_chip, data->hwirq);
+}
+
 static int ingenic_pinmux_set_pin_fn(struct ingenic_pinctrl *jzpc,
int pin, int func)
 {
@@ -2296,6 +2315,8 @@ static int __init ingenic_gpio_probe(struct 
ingenic_pinctrl *jzpc,
jzgc->irq_chip.irq_ack = ingenic_gpio_irq_ack;
jzgc->irq_chip.irq_set_type = ingenic_gpio_irq_set_type;
jzgc->irq_chip.irq_set_wake = ingenic_gpio_irq_set_wake;
+   jzgc->irq_chip.irq_request_resources = ingenic_gpio_irq_request;
+   jzgc->irq_chip.irq_release_resources = ingenic_gpio_irq_release;
jzgc->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND;
 
girq = >gc.irq;
-- 
2.26.2



Re: [PATCH v7 01/12] dt-bindings: add img,pvrsgx.yaml for Imagination GPUs

2020-05-03 Thread Paul Cercueil




Le dim. 3 mai 2020 à 15:31, H. Nikolaus Schaller  a 
écrit :

Hi Paul,


 Am 03.05.2020 um 14:52 schrieb Paul Cercueil :

 It's possible to forbid the presence of the 'clocks' property on 
some implementations, and require it on others.
 To be precise we have to specify the exact number of clocks 
(between 0 and 4) for every architecture.
 This also contradicts my dream to get rid of the architecture 
specific components in the long run. My dream (because I can't tell 
how it can be done) is that we can one day develop something which 
just needs compatible = img,530 or imp,540 or img,544. Then we 
can't make the number clocks depend on the implementation any more.


 As we said before, the number of clocks is a property of the GPU 
and *not* its integration into the SoC.


Well, it is a not very well documented property of the GPU. We have 
no data sheet of the standalone GPU. Only several SoC data sheets 
which give some indications.


Maybe we can nicely ask them?

I expect Paul Burton to have some contacts at ImgTec. Asking for a doc 
would be too much, but maybe they can help a bit with the DT bindings.


It appears as if some sgx5xx versions have 3 clocks and some have 4. 
So you are right, the number of clocks depends on the sgx5xx version 
and that could be made dependent in the bindings (if necessary).




 So you would *not* have a number of clocks between 0 and 4. You get 
either 0, or 4, depending on whether or not you have a wrapper.


I think this is contradicting your previous sentence. If the number 
of clocks is a property of the GPU and not the integration it must 
also not depend on whether there is a wrapper. I.e. it must be a 
constant for any type of integration.


Well, I expected all SGX versions to have 4 clocks.

If some SGX versions have 3 clocks, and others have 4 clocks, it's 
still OK as long as the number of clocks is enforced, so that all 
implementations of a given SGX core will have to use the same number of 
clocks.


The really correct variant would be to always make the SoC 
integration (wrapper) a separate subsystem (because it is never part 
of the SGX core but some interface bus) and clock provider and 
connect it explicitly to the clock inputs.


About the wrapper... I don't really know how it's done there. But you 
could very well pass all clocks unconditionally to the SGX node, even 
if it's inside a wrapper.
The wrapper itself probably needs only one clock, the one that allows 
it to access its registers.


To be clear: I am not at all against describing the clocks. I just 
doubt that the time we invest into discussing on this level of detail 
and adding conditional clock requirements is worth the result. IMHO 
the bindings and .dts do not become better by describing them in more 
detail than just "optional". It just takes our time from contributing 
to other subsystems.




You have a new SoC with a SGX, and you only need to enable one clock to 
get it to work. So you create a devicetree node which receives only one 
clock.


Turns out, that the bootloader was enabling the other 3 clocks, and 
since the last release, it doesn't anymore. You're left with having to 
support a broken devicetree.


That's the kind of problem that can be easily avoided by enforcing the 
number of clocks that have to be provided.



 See how it's done for instance on 
Documentation/devicetree/bindings/serial/samsung_uart.yaml.
 Yes I know the design pattern, but I wonder if such a move makes 
the whole thing even less maintainable.
 Assume we have finished DTS for some SoC. Then these DTS have been 
tested on real hardware and are working. Clocks are there where 
needed and missing where not. We may now forbid or not forbid them 
for some implementations in the bindings.yaml but the result of 
dtbs_check won't change! Because they are tested and working and 
the bindings.yaml has been adapted to the result. So we have just 
duplicated something for no practical benefit.
 Next, assume there is coming support for more and more new SoC. 
Then, developers not only have to figure out which clocks they need 
in the DTS but they also have to add a patch to the implementation 
specific part of the bindings.yaml to clearly define exactly the 
same what they already have written into their .dts (the clocks are 
either there for the of_node or they are not). So again the rules 
are for no benefit, since a new SoC is introduced exactly once. And 
tested if it works. And if it is there, it will stay as it is. It 
is just work for maintainers to review that patch as well.


 If you add support for a new SoC, you'd still need to modify the 
binding to add the compatible string. So the argument of "more work" 
is moot.


Agreed, I forgot this aspect. Nevertheless, it is easier to review a 
new compatible string than a new clock number rule (question: how do 
you practically review this? By looking if it does match the DTS?).


We have to add the compatible string as lon

Re: [PATCH v7 01/12] dt-bindings: add img,pvrsgx.yaml for Imagination GPUs

2020-05-03 Thread Paul Cercueil

Hi Nikolaus,

Le sam. 2 mai 2020 à 22:26, H. Nikolaus Schaller  a 
écrit :

Hi Paul,


 Am 26.04.2020 um 15:11 schrieb Paul Cercueil :

 Hi Nikolaus,

 Le ven. 24 avril 2020 à 22:34, H. Nikolaus Schaller 
 a écrit :

 The Imagination PVR/SGX GPU is part of several SoC from
 multiple vendors, e.g. TI OMAP, Ingenic JZ4780, Intel Poulsbo,
 Allwinner A83 and others.
 With this binding, we describe how the SGX processor is
 interfaced to the SoC (registers and interrupt).
 The interface also consists of clocks, reset, power but
 information from data sheets is vague and some SoC integrators
 (TI) deciced to use a PRCM wrapper (ti,sysc) which does
 all clock, reset and power-management through registers
 outside of the sgx register block.
 Therefore all these properties are optional.
 Tested by make dt_binding_check
 Signed-off-by: H. Nikolaus Schaller 
 ---
 .../devicetree/bindings/gpu/img,pvrsgx.yaml   | 150 
++

 1 file changed, 150 insertions(+)
 create mode 100644 
Documentation/devicetree/bindings/gpu/img,pvrsgx.yaml
 diff --git a/Documentation/devicetree/bindings/gpu/img,pvrsgx.yaml 
b/Documentation/devicetree/bindings/gpu/img,pvrsgx.yaml

 new file mode 100644
 index ..33a9c4c6e784
 --- /dev/null
 +++ b/Documentation/devicetree/bindings/gpu/img,pvrsgx.yaml
 @@ -0,0 +1,150 @@
 +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
 +%YAML 1.2
 +---
 +$id: http://devicetree.org/schemas/gpu/img,pvrsgx.yaml#
 +$schema: http://devicetree.org/meta-schemas/core.yaml#
 +
 +title: Imagination PVR/SGX GPU
 +
 +maintainers:
 +  - H. Nikolaus Schaller 
 +
 +description: |+
 +  This binding describes the Imagination SGX5 series of 3D 
accelerators which
 +  are found in several different SoC like TI OMAP, Sitara, 
Ingenic JZ4780,

 +  Allwinner A83, and Intel Poulsbo and CedarView and more.
 +
 +  For an extensive list see: 
https://en.wikipedia.org/wiki/PowerVR#Implementations

 +
 +  The SGX node is usually a child node of some DT node belonging 
to the SoC
 +  which handles clocks, reset and general address space mapping 
of the SGX

 +  register area. If not, an optional clock can be specified here.
 +
 +properties:
 +  $nodename:
 +pattern: '^gpu@[a-f0-9]+$'
 +  compatible:
 +oneOf:
 +  - description: SGX530-121 based SoC
 +items:
 +  - enum:
 +- ti,omap3-sgx530-121 # BeagleBoard A/B/C, 
OpenPandora 600MHz and similar

 +  - const: img,sgx530-121
 +  - const: img,sgx530
 +
 +  - description: SGX530-125 based SoC
 +items:
 +  - enum:
 +- ti,am3352-sgx530-125 # BeagleBone Black
 +- ti,am3517-sgx530-125
 +- ti,am4-sgx530-125
 +- ti,omap3-sgx530-125 # BeagleBoard XM, GTA04, 
OpenPandora 1GHz and similar

 +- ti,ti81xx-sgx530-125
 +  - const: ti,omap3-sgx530-125
 +  - const: img,sgx530-125
 +  - const: img,sgx530
 +
 +  - description: SGX535-116 based SoC
 +items:
 +  - const: intel,poulsbo-gma500-sgx535 # Atom Z5xx
 +  - const: img,sgx535-116
 +  - const: img,sgx535
 +
 +  - description: SGX540-116 based SoC
 +items:
 +  - const: intel,medfield-gma-sgx540 # Atom Z24xx
 +  - const: img,sgx540-116
 +  - const: img,sgx540
 +
 +  - description: SGX540-120 based SoC
 +items:
 +  - enum:
 +- samsung,s5pv210-sgx540-120
 +- ti,omap4-sgx540-120 # Pandaboard, Pandaboard ES and 
similar

 +  - const: img,sgx540-120
 +  - const: img,sgx540
 +
 +  - description: SGX540-130 based SoC
 +items:
 +  - enum:
 +- ingenic,jz4780-sgx540-130 # CI20
 +  - const: img,sgx540-130
 +  - const: img,sgx540
 +
 +  - description: SGX544-112 based SoC
 +items:
 +  - const: ti,omap4470-sgx544-112
 +  - const: img,sgx544-112
 +  - const: img,sgx544
 +
 +  - description: SGX544-115 based SoC
 +items:
 +  - enum:
 +- allwinner,sun8i-a31-sgx544-115
 +- allwinner,sun8i-a31s-sgx544-115
 +- allwinner,sun8i-a83t-sgx544-115 # Banana-Pi-M3 
(Allwinner A83T) and similar

 +  - const: img,sgx544-115
 +  - const: img,sgx544
 +
 +  - description: SGX544-116 based SoC
 +items:
 +  - enum:
 +- ti,dra7-sgx544-116 # DRA7
 +- ti,omap5-sgx544-116 # OMAP5 UEVM, Pyra Handheld and 
similar

 +  - const: img,sgx544-116
 +  - const: img,sgx544
 +
 +  - description: SGX545 based SoC
 +items:
 +  - const: intel,cedarview-gma3600-sgx545 # Atom N2600, 
D2500

 +  - const: img,sgx545-116
 +  - const: img,sgx545
 +
 +  reg:
 +maxItems: 1
 +
 +  interrupts:
 +maxItems: 1
 +
 +  interrupt-names:
 +maxItems: 1
 +items:
 +  - const: sgx
 +
 +  clocks:
 +maxItems: 4
 +
 +  clock-names

Re: [PATCH 07/13] MIPS: ingenic: DTS: Respect cell count of common properties

2020-04-30 Thread Paul Cercueil

Hi Thomas,

Le mer. 29 avril 2020 à 23:04, Thomas Bogendoerfer 
 a écrit :

On Mon, Apr 13, 2020 at 05:26:27PM +0200, Paul Cercueil wrote:

 If N fields of X cells should be provided, then that's what the
 devicetree should represent, instead of having one single field of
 (N*X) cells.

 Signed-off-by: Paul Cercueil 
 ---
  arch/mips/boot/dts/ingenic/jz4740.dtsi | 19 +--
  arch/mips/boot/dts/ingenic/jz4770.dtsi | 12 +---
  arch/mips/boot/dts/ingenic/jz4780.dtsi | 24 
+++-

  arch/mips/boot/dts/ingenic/x1000.dtsi  |  9 -
  4 files changed, 29 insertions(+), 35 deletions(-)


applied to mips-next. Please check if my resolution of the conflict
in arch/mips/boot/dts/ingenic/jz4780.dtsi is correct.


Looks good, thanks.

-Paul


Thomas.

--
Crap can work. Given enough thrust pigs will fly, but it's not 
necessarily a
good idea.[ RFC1925, 
2.3 ]





Re: [PATCH v7 08/12] arm: dts: s5pv210: Add node for SGX 540

2020-04-29 Thread Paul Cercueil

Hi Jonathan,

Le mar. 28 avril 2020 à 15:58, Jonathan Bakker  a 
écrit :

Hi all,

On 2020-04-28 2:39 p.m., Jonathan Bakker wrote:

 Hi Krzysztof,

 On 2020-04-27 8:46 a.m., Krzysztof Kozlowski wrote:

 On Sun, Apr 26, 2020 at 07:57:12AM -0700, Jonathan Bakker wrote:

 Hi Paul,

 On 2020-04-26 5:56 a.m., Paul Cercueil wrote:



 Le ven. 24 avril 2020 à 22:34, H. Nikolaus Schaller 
 a écrit :

 From: Jonathan Bakker 

 All s5pv210 devices have a PowerVR SGX 540 (revision 120) 
attached.


 There is no external regulator for it so it can be enabled by 
default.


 Signed-off-by: Jonathan Bakker 
 Signed-off-by: H. Nikolaus Schaller 
 ---
  arch/arm/boot/dts/s5pv210.dtsi | 13 +
  1 file changed, 13 insertions(+)

 diff --git a/arch/arm/boot/dts/s5pv210.dtsi 
b/arch/arm/boot/dts/s5pv210.dtsi

 index 2ad642f51fd9..abbdda205c1b 100644
 --- a/arch/arm/boot/dts/s5pv210.dtsi
 +++ b/arch/arm/boot/dts/s5pv210.dtsi
 @@ -512,6 +512,19 @@ vic3: interrupt-controller@f230 {
  #interrupt-cells = <1>;
  };

 +gpu: gpu@f300 {
 +compatible = "samsung,s5pv210-sgx540-120";


 This should not pass the bindings check because you missed last
 compatibles.



 Thanks for pointing that out, I'll add it and make sure it passes 
the bindings check.



 +reg = <0xf300 0x1>;
 +interrupt-parent = <>;
 +interrupts = <10>;
 +clock-names = "core";
 +clocks = < CLK_G3D>;
 +
 +assigned-clocks = < MOUT_G3D>, < 
DOUT_G3D>;

 +assigned-clock-rates = <0>, <6670>;
 +assigned-clock-parents = < MOUT_MPLL>;


 What are these clocks for, and why are they reparented / 
reclocked?


 Shouldn't they be passed to 'clocks' as well?

 -Paul



 The G3D clock system can have multiple parents, and for stable 
operation
 it's recommended to use the MPLL clock as the parent (which in 
turn
 is actually a mux as well).  MOUT_G3D is simply the mux for 
CLK_G3D
 (SGX core clock), DOUT_G3D is the divider.  DOUT_G3D could 
equally be CLK_G3D
 (and probably should be, for readability) as CLK_G3D is simply 
the gate and

 DOUT_G3D is the divider for it.


 Good point, it should be CLK_G3D instead of DOUT.  Can you fix 
this as

 well?


 Yep, will do.  Nikolaus, I'll send you an updated patch to include.



How are assigned-clocks handled in the yaml DT schema?  When running 
make dtbs_check,

I end up with messages such as

arch/arm/boot/dts/s5pv210-aquila.dt.yaml: gpu@f300: 
'assigned-clock-parents', 'assigned-clock-rates', 'assigned-clocks' 
do not match any of the regexes: 'pinctrl-[0-9]+'


Do they need to explicitly be listed as valid entries?


The assigned-* can also be moved inside the node of the clocks 
provider. I would say it makes more sense to have them there.


-Paul




Re: [PATCH 8/8] dt-bindings: display: Convert ingenic,lcd.txt to YAML

2020-04-29 Thread Paul Cercueil

This one patch will need a V2, I messed up with the clocks.

-Paul


Le dim. 26 avril 2020 à 20:58, Paul Cercueil  a 
écrit :

Convert the ingenic,lcd.txt to a new ingenic,lcd.yaml file.

In the process, the new ingenic,jz4780-lcd compatible string has been
added.

Signed-off-by: Paul Cercueil 
---
 .../bindings/display/ingenic,lcd.txt  |  45 ---
 .../bindings/display/ingenic,lcd.yaml | 113 
++

 2 files changed, 113 insertions(+), 45 deletions(-)
 delete mode 100644 
Documentation/devicetree/bindings/display/ingenic,lcd.txt
 create mode 100644 
Documentation/devicetree/bindings/display/ingenic,lcd.yaml


diff --git 
a/Documentation/devicetree/bindings/display/ingenic,lcd.txt 
b/Documentation/devicetree/bindings/display/ingenic,lcd.txt

deleted file mode 100644
index 01e3261defb6..
--- a/Documentation/devicetree/bindings/display/ingenic,lcd.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-Ingenic JZ47xx LCD driver
-
-Required properties:
-- compatible: one of:
-  * ingenic,jz4740-lcd
-  * ingenic,jz4725b-lcd
-  * ingenic,jz4770-lcd
-- reg: LCD registers location and length
-- clocks: LCD pixclock and device clock specifiers.
-  The device clock is only required on the JZ4740.
-- clock-names: "lcd_pclk" and "lcd"
-- interrupts: Specifies the interrupt line the LCD controller is 
connected to.

-
-Example:
-
-panel {
-   compatible = "sharp,ls020b1dd01d";
-
-   backlight = <>;
-   power-supply = <>;
-
-   port {
-   panel_input: endpoint {
-   remote-endpoint = <_output>;
-   };
-   };
-};
-
-
-lcd: lcd-controller@1305 {
-   compatible = "ingenic,jz4725b-lcd";
-   reg = <0x1305 0x1000>;
-
-   interrupt-parent = <>;
-   interrupts = <31>;
-
-   clocks = < JZ4725B_CLK_LCD>;
-   clock-names = "lcd";
-
-   port {
-   panel_output: endpoint {
-   remote-endpoint = <_input>;
-   };
-   };
-};
diff --git 
a/Documentation/devicetree/bindings/display/ingenic,lcd.yaml 
b/Documentation/devicetree/bindings/display/ingenic,lcd.yaml

new file mode 100644
index ..8e9c851dc7c5
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/ingenic,lcd.yaml
@@ -0,0 +1,113 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/ingenic,lcd.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ingenic SoCs LCD controller devicetree bindings
+
+maintainers:
+  - Paul Cercueil 
+
+properties:
+  $nodename:
+pattern: "^lcd-controller@[0-9a-f]+$"
+
+  compatible:
+enum:
+  - ingenic,jz4740-lcd
+  - ingenic,jz4725b-lcd
+  - ingenic,jz4770-lcd
+  - ingenic,jz4780-lcd
+
+  reg:
+maxItems: 1
+
+  interrupts:
+maxItems: 1
+
+  clocks:
+items:
+  - description: Module clock
+  - description: Pixel clock
+minItems: 1
+
+  clock-names:
+items:
+  - const: lcd
+  - const: lcd_pclk
+minItems: 1
+
+  port:
+type: object
+description:
+  A port node with endpoint definitions as defined in
+  Documentation/devicetree/bindings/media/video-interfaces.txt
+
+required:
+- compatible
+- reg
+- interrupts
+- clocks
+- clock-names
+
+if:
+  properties:
+compatible:
+  contains:
+enum:
+  - ingenic,jz4740-lcd
+  - ingenic,jz4780-lcd
+then:
+  properties:
+clocks:
+  minItems: 2
+clock-names:
+  minItems: 2
+else:
+  properties:
+clocks:
+  maxItems: 1
+clock-names:
+  maxItems: 1
+
+additionalProperties: false
+
+examples:
+  - |
+#include 
+lcd-controller@1305 {
+  compatible = "ingenic,jz4740-lcd";
+  reg = <0x1305 0x1000>;
+
+  interrupt-parent = <>;
+  interrupts = <30>;
+
+  clocks = < JZ4740_CLK_LCD>, < JZ4740_CLK_LCD_PCLK>;
+  clock-names = "lcd", "lcd_pclk";
+
+  port {
+endpoint {
+  remote-endpoint = <_input>;
+};
+  };
+};
+
+  - |
+#include 
+lcd-controller@1305 {
+  compatible = "ingenic,jz4725b-lcd";
+  reg = <0x1305 0x1000>;
+
+  interrupt-parent = <>;
+  interrupts = <31>;
+
+  clocks = < JZ4725B_CLK_LCD>;
+  clock-names = "lcd";
+
+  port {
+endpoint {
+  remote-endpoint = <_input>;
+};
+  };
+};
--
2.26.2






Re: [PATCH 2/8] dt-bindings: intc: Convert ingenic,intc.txt to YAML

2020-04-28 Thread Paul Cercueil

Hi Sergei,

Le lun. 27 avril 2020 à 12:11, Sergei Shtylyov 
 a écrit :

Hello!

On 26.04.2020 21:58, Paul Cercueil wrote:


Convert the ingenic,intc.txt doc file to ingenic,intc.yaml.

Some compatible strings now require a fallback, as the controller
generally works the same across the SoCs families.

Signed-off-by: Paul Cercueil 

[...]
diff --git 
a/Documentation/devicetree/bindings/interrupt-controller/ingenic,intc.yaml 
b/Documentation/devicetree/bindings/interrupt-controller/ingenic,intc.yaml

new file mode 100644
index ..28b27e1a6e9d
--- /dev/null
+++ 
b/Documentation/devicetree/bindings/interrupt-controller/ingenic,intc.yaml

@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: 
http://devicetree.org/schemas/interrupt-controller/ingenic,intc.yaml#

+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ingenic SoCs interrupt controller devicetree bindings
+
+maintainers:
+  - Paul Cercueil 
+
+properties:
+  $nodename:
+pattern: "^interrupt-controller@[0-9a-f]+$"
+
+  compatible:
+oneOf:
+  - enum:
+- ingenic,jz4740-intc
+- ingenic,jz4760-intc
+- ingenic,jz4780-intc
+  - items:
+- enum:
+  - ingenic,jz4775-intc
+  - ingenic,jz4770-intc
+- const: ingenic,jz4760-intc
+  - items:
+- const: ingenic,x1000-intc
+- const: ingenic,jz4780-intc
+  - items:
+- const: ingenic,jz4725b-intc
+- const: ingenic,jz4740-intc
+
+  "#interrupt-cells":
+const: 1


   Do double quotes work the same as the single ones?


Yes. The only difference is that you can escape characters in double 
quotes.


-Paul



[...]

MBR, Sergei





[PATCH v2 2/3] watchdog: jz4740: Use regmap provided by TCU driver

2019-10-23 Thread Paul Cercueil
Since we broke the ABI by changing the clock, the driver was also
updated to use the regmap provided by the TCU driver.

Signed-off-by: Paul Cercueil 
Tested-by: Mathieu Malaterre 
Tested-by: Artur Rojek 
Acked-by: Guenter Roeck 
---

Notes:
v2: Rebase on top of 5.4-rc4

 drivers/watchdog/Kconfig  |  1 +
 drivers/watchdog/jz4740_wdt.c | 35 ---
 2 files changed, 21 insertions(+), 15 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 6421187769cf..dbef995856bf 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1644,6 +1644,7 @@ config JZ4740_WDT
depends on MACH_JZ4740 || MACH_JZ4780
depends on COMMON_CLK
select WATCHDOG_CORE
+   select MFD_SYSCON
help
  Hardware driver for the built-in watchdog timer on Ingenic jz4740 
SoCs.
 
diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
index 72920f09f4a7..bdf9564efa29 100644
--- a/drivers/watchdog/jz4740_wdt.c
+++ b/drivers/watchdog/jz4740_wdt.c
@@ -5,6 +5,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -17,6 +18,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #define DEFAULT_HEARTBEAT 5
 #define MAX_HEARTBEAT 2048
@@ -36,7 +38,7 @@ MODULE_PARM_DESC(heartbeat,
 
 struct jz4740_wdt_drvdata {
struct watchdog_device wdt;
-   void __iomem *base;
+   struct regmap *map;
struct clk *clk;
unsigned long clk_rate;
 };
@@ -45,7 +47,8 @@ static int jz4740_wdt_ping(struct watchdog_device *wdt_dev)
 {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
 
-   writew(0x0, drvdata->base + TCU_REG_WDT_TCNT);
+   regmap_write(drvdata->map, TCU_REG_WDT_TCNT, 0);
+
return 0;
 }
 
@@ -54,16 +57,16 @@ static int jz4740_wdt_set_timeout(struct watchdog_device 
*wdt_dev,
 {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
u16 timeout_value = (u16)(drvdata->clk_rate * new_timeout);
-   u8 tcer;
+   unsigned int tcer;
 
-   tcer = readb(drvdata->base + TCU_REG_WDT_TCER);
-   writeb(0x0, drvdata->base + TCU_REG_WDT_TCER);
+   regmap_read(drvdata->map, TCU_REG_WDT_TCER, );
+   regmap_write(drvdata->map, TCU_REG_WDT_TCER, 0);
 
-   writew((u16)timeout_value, drvdata->base + TCU_REG_WDT_TDR);
-   writew(0x0, drvdata->base + TCU_REG_WDT_TCNT);
+   regmap_write(drvdata->map, TCU_REG_WDT_TDR, timeout_value);
+   regmap_write(drvdata->map, TCU_REG_WDT_TCNT, 0);
 
if (tcer & TCU_WDT_TCER_TCEN)
-   writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER);
+   regmap_write(drvdata->map, TCU_REG_WDT_TCER, TCU_WDT_TCER_TCEN);
 
wdt_dev->timeout = new_timeout;
return 0;
@@ -72,20 +75,20 @@ static int jz4740_wdt_set_timeout(struct watchdog_device 
*wdt_dev,
 static int jz4740_wdt_start(struct watchdog_device *wdt_dev)
 {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
+   unsigned int tcer;
int ret;
-   u8 tcer;
 
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
 
-   tcer = readb(drvdata->base + TCU_REG_WDT_TCER);
+   regmap_read(drvdata->map, TCU_REG_WDT_TCER, );
 
jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
 
/* Start watchdog if it wasn't started already */
if (!(tcer & TCU_WDT_TCER_TCEN))
-   writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER);
+   regmap_write(drvdata->map, TCU_REG_WDT_TCER, TCU_WDT_TCER_TCEN);
 
return 0;
 }
@@ -94,7 +97,7 @@ static int jz4740_wdt_stop(struct watchdog_device *wdt_dev)
 {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
 
-   writeb(0x0, drvdata->base + TCU_REG_WDT_TCER);
+   regmap_write(drvdata->map, TCU_REG_WDT_TCER, 0);
clk_disable_unprepare(drvdata->clk);
 
return 0;
@@ -172,9 +175,11 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
watchdog_set_nowayout(jz4740_wdt, nowayout);
watchdog_set_drvdata(jz4740_wdt, drvdata);
 
-   drvdata->base = devm_platform_ioremap_resource(pdev, 0);
-   if (IS_ERR(drvdata->base))
-   return PTR_ERR(drvdata->base);
+   drvdata->map = device_node_to_regmap(dev->parent->of_node);
+   if (!drvdata->map) {
+   dev_err(dev, "regmap not found\n");
+   return -EINVAL;
+   }
 
return devm_watchdog_register_device(dev, >wdt);
 }
-- 
2.23.0



[PATCH v2 3/3] watchdog: jz4740: Drop dependency on MACH_JZ47xx

2019-10-23 Thread Paul Cercueil
Depending on MACH_JZ47xx prevent us from creating a generic kernel that
works on more than one MIPS board. Instead, we just depend on MIPS being
set.

Signed-off-by: Paul Cercueil 
Acked-by: Guenter Roeck 
---

Notes:
v2: Rebase on top of 5.4-rc4

 drivers/watchdog/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index dbef995856bf..fd4844f0a8f3 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1641,7 +1641,7 @@ config INDYDOG
 
 config JZ4740_WDT
tristate "Ingenic jz4740 SoC hardware watchdog"
-   depends on MACH_JZ4740 || MACH_JZ4780
+   depends on MIPS
depends on COMMON_CLK
select WATCHDOG_CORE
select MFD_SYSCON
-- 
2.23.0



[PATCH v2 1/3] watchdog: jz4740: Use WDT clock provided by TCU driver

2019-10-23 Thread Paul Cercueil
Instead of requesting the "ext" clock and handling the watchdog clock
divider and gating in the watchdog driver, we now request and use the
"wdt" clock that is supplied by the ingenic-timer "TCU" driver.

The major benefit is that the watchdog's clock rate and parent can now
be specified from within devicetree, instead of hardcoded in the driver.

Also, this driver won't poke anymore into the TCU registers to
enable/disable the clock, as this is now handled by the TCU driver.

On the bad side, we break the ABI with devicetree - as we now request a
different clock. In this very specific case it is still okay, as every
Ingenic JZ47xx-based board out there compile the devicetree within the
kernel; so it's still time to push breaking changes, in order to get a
clean devicetree that won't break once it musn't.

Signed-off-by: Paul Cercueil 
Tested-by: Mathieu Malaterre 
Tested-by: Artur Rojek 
Acked-by: Guenter Roeck 
---

Notes:
v2: Rebase on top of 5.4-rc4

 drivers/watchdog/Kconfig  |  1 +
 drivers/watchdog/jz4740_wdt.c | 75 ++-
 2 files changed, 31 insertions(+), 45 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 58e7c100b6ad..6421187769cf 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1642,6 +1642,7 @@ config INDYDOG
 config JZ4740_WDT
tristate "Ingenic jz4740 SoC hardware watchdog"
depends on MACH_JZ4740 || MACH_JZ4780
+   depends on COMMON_CLK
select WATCHDOG_CORE
help
  Hardware driver for the built-in watchdog timer on Ingenic jz4740 
SoCs.
diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
index c6052ae54f32..72920f09f4a7 100644
--- a/drivers/watchdog/jz4740_wdt.c
+++ b/drivers/watchdog/jz4740_wdt.c
@@ -18,19 +18,6 @@
 #include 
 #include 
 
-#include 
-
-#define JZ_WDT_CLOCK_PCLK 0x1
-#define JZ_WDT_CLOCK_RTC  0x2
-#define JZ_WDT_CLOCK_EXT  0x4
-
-#define JZ_WDT_CLOCK_DIV_1(0 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_4(1 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_16   (2 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_64   (3 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_256  (4 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_1024 (5 << TCU_TCSR_PRESCALE_LSB)
-
 #define DEFAULT_HEARTBEAT 5
 #define MAX_HEARTBEAT 2048
 
@@ -50,7 +37,8 @@ MODULE_PARM_DESC(heartbeat,
 struct jz4740_wdt_drvdata {
struct watchdog_device wdt;
void __iomem *base;
-   struct clk *rtc_clk;
+   struct clk *clk;
+   unsigned long clk_rate;
 };
 
 static int jz4740_wdt_ping(struct watchdog_device *wdt_dev)
@@ -65,32 +53,14 @@ static int jz4740_wdt_set_timeout(struct watchdog_device 
*wdt_dev,
unsigned int new_timeout)
 {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
-   unsigned int rtc_clk_rate;
-   unsigned int timeout_value;
-   unsigned short clock_div = JZ_WDT_CLOCK_DIV_1;
+   u16 timeout_value = (u16)(drvdata->clk_rate * new_timeout);
u8 tcer;
 
-   rtc_clk_rate = clk_get_rate(drvdata->rtc_clk);
-
-   timeout_value = rtc_clk_rate * new_timeout;
-   while (timeout_value > 0x) {
-   if (clock_div == JZ_WDT_CLOCK_DIV_1024) {
-   /* Requested timeout too high;
-   * use highest possible value. */
-   timeout_value = 0x;
-   break;
-   }
-   timeout_value >>= 2;
-   clock_div += (1 << TCU_TCSR_PRESCALE_LSB);
-   }
-
tcer = readb(drvdata->base + TCU_REG_WDT_TCER);
writeb(0x0, drvdata->base + TCU_REG_WDT_TCER);
-   writew(clock_div, drvdata->base + TCU_REG_WDT_TCSR);
 
writew((u16)timeout_value, drvdata->base + TCU_REG_WDT_TDR);
writew(0x0, drvdata->base + TCU_REG_WDT_TCNT);
-   writew(clock_div | JZ_WDT_CLOCK_RTC, drvdata->base + TCU_REG_WDT_TCSR);
 
if (tcer & TCU_WDT_TCER_TCEN)
writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER);
@@ -102,11 +72,15 @@ static int jz4740_wdt_set_timeout(struct watchdog_device 
*wdt_dev,
 static int jz4740_wdt_start(struct watchdog_device *wdt_dev)
 {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
+   int ret;
u8 tcer;
 
+   ret = clk_prepare_enable(drvdata->clk);
+   if (ret)
+   return ret;
+
tcer = readb(drvdata->base + TCU_REG_WDT_TCER);
 
-   jz4740_timer_enable_watchdog();
jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
 
/* Start watchdog if it wasn't started already */
@@ -121,7 +95,7 @@ static int jz4740_wdt_stop(struct watchdog_device *wdt_dev)
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_de

Re: [PATCH RESEND 2/2] dmaengine: JZ4780: Add support for the X1000.

2019-10-23 Thread Paul Cercueil

Hi Zhou,


Le mer., oct. 23, 2019 at 15:02, Zhou Yanjie  a 
écrit :

Add support for probing the dma-jz4780 driver on the X1000 Soc.

Signed-off-by: Zhou Yanjie 
---
 drivers/dma/dma-jz4780.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index cafb1cc0..f809a6e 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -1019,11 +1019,18 @@ static const struct jz4780_dma_soc_data 
jz4780_dma_soc_data = {

.flags = JZ_SOC_DATA_ALLOW_LEGACY_DT | JZ_SOC_DATA_PROGRAMMABLE_DMA,
 };

+static const struct jz4780_dma_soc_data x1000_dma_soc_data = {
+   .nb_channels = 8,
+   .transfer_ord_max = 7,
+   .flags = JZ_SOC_DATA_ALLOW_LEGACY_DT | JZ_SOC_DATA_PROGRAMMABLE_DMA,


Please don't use JZ_SOC_DATA_ALLOW_LEGACY_DT for new bindings.

With that flag removed:
Reviewed-by: Paul Cercueil 



+};
+
 static const struct of_device_id jz4780_dma_dt_match[] = {
 	{ .compatible = "ingenic,jz4740-dma", .data = _dma_soc_data 
},
 	{ .compatible = "ingenic,jz4725b-dma", .data = 
_dma_soc_data },
 	{ .compatible = "ingenic,jz4770-dma", .data = _dma_soc_data 
},
 	{ .compatible = "ingenic,jz4780-dma", .data = _dma_soc_data 
},

+   { .compatible = "ingenic,x1000-dma", .data = _dma_soc_data },
{},
 };
 MODULE_DEVICE_TABLE(of, jz4780_dma_dt_match);
--
2.7.4







Re: [PATCH 4/7] pwm: jz4740: Improve algorithm of clock calculation

2019-10-21 Thread Paul Cercueil

Hi,


Le mer., août 14, 2019 at 19:32, Uwe Kleine-König 
 a écrit :

Hello Paul,

On Wed, Aug 14, 2019 at 06:10:35PM +0200, Paul Cercueil wrote:
 Le mar. 13 août 2019 à 16:09, Uwe =?iso-8859-1?q?Kleine-K=F6nig?= 
a écrit :

 > On Tue, Aug 13, 2019 at 02:47:28PM +0200, Paul Cercueil wrote:
 > > Le mar. 13 août 2019 à 14:33, Uwe Kleine-König a écrit :
 > > > On Tue, Aug 13, 2019 at 01:01:06PM +0200, Paul Cercueil wrote:
 > > > > Well, you said that I shouln't rely on the fact that 
clk_round_rate() will
 > > > > round down. That completely defeats the previous algorithm. 
So please tell

 > > > > me how to use it correctly, because I don't see it.
 > > >
 > > > Using clk_round_rate correctly without additional knowledge 
is hard. If
 > > > you assume at least some sane behaviour you'd still have to 
call it
 > > > multiple times. Assuming maxrate is the maximal rate you can 
handle

 > > > without overflowing your PWM registers you have to do:
 > > >
 > > > rate = maxrate;
 > > > rounded_rate = clk_round_rate(clk, rate);
 > > > while (rounded_rate > rate) {
 > > > if (rate < rounded_rate - rate) {
 > > > /*
 > > >  * clk doesn't support a rate smaller than
 > > >  * maxrate (or the round_rate callback doesn't
 > > >  * round consistently).
 > > >  */
 > > >  return -ESOMETHING;
 > > > }
 > > > rate = rate - (rounded_rate - rate)
 > > > rounded_rate = clk_round_rate(clk, rate);
 > > > }
 > > >
 > > > return rate;
 > > >
 > > > Probably it would be sensible to put that in a function 
provided by the
 > > > clk framework (maybe call it clk_round_rate_down and maybe 
with

 > > > additional checks).
 > >
 > >  clk_round_rate_down() has been refused multiple times in the 
past for

 > >  reasons that Stephen can explain.
 >
 > I'd be really interested in these reasons as I think the clk 
framework
 > should make it easy to solve common tasks related to clocks. And 
finding

 > out the biggest supported rate not bigger than a given maxrate is
 > something I consider such a common task.
 >
 > The first hit I found when searching was
 > https://lkml.org/lkml/2010/7/14/260 . In there Stephen suggested 
that
 > clk_round_rate with the current semantic is hardly useful and 
suggested

 > clk_round_rate_up() and clk_round_rate_down() himself.

 That's from 2010, though.


If you have a better link please tell me.

 I agree that clk_round_rate_up() and clk_round_rate_down() should 
exist.
 Even if they return -ENOSYS if it's not implemented for a given 
clock

 controller.


ack.

 > > > > I came up with a much smarter alternative, that doesn't 
rely on the rounding
 > > > > method of clk_round_rate, and which is better overall (no 
loop needed). It
 > > > > sounds to me like you're bashing the code without making 
the effort to

 > > > > understand what it does.
 > > > >
 > > > > Thierry called it a "neat trick"
 > > > > (https://patchwork.kernel.org/patch/10836879/) so it cannot 
be as bad as you

 > > > > say.
 > > >
 > > > Either that or Thierry failed to see the downside. The 
obvious downside
 > > > is that once you set the period to something long (and so the 
clk was
 > > > limited to a small frequency) you never make the clock any 
faster

 > > > afterwards.
 > >
 > >  Read the algorithm again.
 >
 > I indeed missed a call to clk_set_rate(clk, parent_rate). I 
thought I
 > grepped for clk_set_rate before claiming the code was broken. 
Sorry.

 >
 > So I think the code works indeed, but it feels like abusing
 > clk_set_max_rate. So I'd like to see some words from Stephen 
about this

 > procedure.
 >
 > Also I think this is kind of inelegant to set the maximal rate 
twice. At

 > least call clk_set_max_rate only once please.

 Ok. I can do that.


I would still prefer to hear from Stephen about this approach. It 
seems

wrong to have two different ways to achieve the same goal and my
impression is that clk_round_rate is the function designed for this 
use

case.


Stephen, any feedback?
I'm still stuck here.


 > > > > > > > > E.g. if at a rate of 12 MHz your computed hardware 
value for the period
 > > > > > > > > is 0xf000, then at a rate of 24 MHz it won't fit in 
16 bits. So 

Re: [PATCH 2/2] clk: Ingenic: Add CGU driver for X1000.

2019-10-21 Thread Paul Cercueil

Hi Zhou,


Le sam., oct. 19, 2019 at 01:50, Zhou Yanjie  a 
écrit :

Add support for the clocks provided by the CGU in the Ingenic X1000
SoC, making use of the cgu code to do the heavy lifting.

Signed-off-by: Zhou Yanjie 
---
 drivers/clk/ingenic/Kconfig |  10 ++
 drivers/clk/ingenic/Makefile|   1 +
 drivers/clk/ingenic/x1000-cgu.c | 253 


 3 files changed, 264 insertions(+)
 create mode 100644 drivers/clk/ingenic/x1000-cgu.c

diff --git a/drivers/clk/ingenic/Kconfig b/drivers/clk/ingenic/Kconfig
index fe8db93..2aebf0d 100644
--- a/drivers/clk/ingenic/Kconfig
+++ b/drivers/clk/ingenic/Kconfig
@@ -45,4 +45,14 @@ config INGENIC_CGU_JZ4780

  If building for a JZ4780 SoC, you want to say Y here.

+config INGENIC_CGU_X1000
+   bool "Ingenic X1000 CGU driver"
+   default MACH_X1000
+   select INGENIC_CGU_COMMON
+   help
+ Support the clocks provided by the CGU hardware on Ingenic X1000
+ and compatible SoCs.
+
+ If building for a X1000 SoC, you want to say Y here.
+
 endmenu
diff --git a/drivers/clk/ingenic/Makefile 
b/drivers/clk/ingenic/Makefile

index 250570a..0f0e784 100644
--- a/drivers/clk/ingenic/Makefile
+++ b/drivers/clk/ingenic/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_INGENIC_CGU_JZ4740)+= jz4740-cgu.o
 obj-$(CONFIG_INGENIC_CGU_JZ4725B)  += jz4725b-cgu.o
 obj-$(CONFIG_INGENIC_CGU_JZ4770)   += jz4770-cgu.o
 obj-$(CONFIG_INGENIC_CGU_JZ4780)   += jz4780-cgu.o
+obj-$(CONFIG_INGENIC_CGU_X1000)+= x1000-cgu.o
diff --git a/drivers/clk/ingenic/x1000-cgu.c 
b/drivers/clk/ingenic/x1000-cgu.c

new file mode 100644
index ..c9d744c
--- /dev/null
+++ b/drivers/clk/ingenic/x1000-cgu.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * X1000 SoC CGU driver
+ * Copyright (c) 2019 Zhou Yanjie 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include "cgu.h"
+
+/* CGU register offsets */
+#define CGU_REG_CPCCR  0x00
+#define CGU_REG_APLL   0x10
+#define CGU_REG_MPLL   0x14
+#define CGU_REG_CLKGR  0x20
+#define CGU_REG_OPCR   0x24
+#define CGU_REG_DDRCDR 0x2c
+#define CGU_REG_MACPHYCDR  0x54
+#define CGU_REG_I2SCDR 0x60
+#define CGU_REG_LPCDR  0x64
+#define CGU_REG_MSC0CDR0x68
+#define CGU_REG_I2SCDR10x70
+#define CGU_REG_SSICDR 0x74
+#define CGU_REG_CIMCDR 0x7c
+#define CGU_REG_PCMCDR 0x84
+#define CGU_REG_MSC1CDR0xa4
+#define CGU_REG_CMP_INTR   0xb0
+#define CGU_REG_CMP_INTRE  0xb4
+#define CGU_REG_DRCG   0xd0
+#define CGU_REG_CLOCKSTATUS0xd4
+#define CGU_REG_PCMCDR10xe0
+#define CGU_REG_MACPHYC0xe8
+
+/* bits within the OPCR register */
+#define OPCR_SPENDN0   (1 << 7)
+#define OPCR_SPENDN1   (1 << 6)


Please use the BIT() macro from 



+
+static struct ingenic_cgu *cgu;
+
+static const s8 pll_od_encoding[8] = {
+   0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3,
+};
+
+static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
+
+   /* External clocks */
+
+   [X1000_CLK_EXCLK] = { "ext", CGU_CLK_EXT },
+   [X1000_CLK_RTCLK] = { "rtc", CGU_CLK_EXT },
+
+   /* PLLs */
+
+   [X1000_CLK_APLL] = {
+   "apll", CGU_CLK_PLL,
+   .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
+   .pll = {
+   .reg = CGU_REG_APLL,
+   .m_shift = 24,
+   .m_bits = 7,
+   .m_offset = 1,
+   .n_shift = 18,
+   .n_bits = 5,
+   .n_offset = 1,
+   .od_shift = 16,
+   .od_bits = 2,
+   .od_max = 8,
+   .od_encoding = pll_od_encoding,
+   .bypass_bit = 9,
+   .enable_bit = 8,
+   .stable_bit = 10,
+   },
+   },
+
+   [X1000_CLK_MPLL] = {
+   "mpll", CGU_CLK_PLL,
+   .parents = { X1000_CLK_EXCLK, -1, -1, -1 },
+   .pll = {
+   .reg = CGU_REG_MPLL,
+   .m_shift = 24,
+   .m_bits = 7,
+   .m_offset = 1,
+   .n_shift = 18,
+   .n_bits = 5,
+   .n_offset = 1,
+   .od_shift = 16,
+   .od_bits = 2,
+   .od_max = 8,
+   .od_encoding = pll_od_encoding,
+   .bypass_bit = 6,
+   .enable_bit = 7,
+   .stable_bit = 0,
+   },
+   },
+
+   /* Muxes & dividers */
+
+   [X1000_CLK_SCLKA] = {
+   "sclk_a", CGU_CLK_MUX,
+   .parents = { -1, X1000_CLK_EXCLK, X1000_CLK_APLL, -1 },
+   .mux = { 

Re: [PATCH 6/6 v2] MMC: JZ4740: Add support for LPM.

2019-10-18 Thread Paul Cercueil

Hi Uffe,


Le ven., oct. 18, 2019 at 10:52, Ulf Hansson  a 
écrit :

On Sat, 12 Oct 2019 at 07:19, Zhou Yanjie  wrote:


 add support for low power mode of Ingenic's MMC/SD Controller.

 Signed-off-by: Zhou Yanjie 


I couldn't find a proper coverletter for the series, please provide
that next time as it really helps review. Additionally, it seems like
you forgot to change the prefix of the patches to "mmc: jz4740" (or at
least you chosed upper case letters), but I will take care of that
this time. So, I have applied the series for next, thanks!

I also have a general question. Should we perhaps rename the driver
from jz4740_mmc.c to ingenic.c (and the file for the DT bindings, the
Kconfig, etc), as that seems like a more appropriate name? No?


Is there a kernel policy regarding renaming drivers? Since it trashes 
the git history. Anyway you're the subsystem maintainer so I guess 
that's up to you. I can send a patch to rename it if you want.


Cheers,
-Paul




Kind regards
Uffe



 ---
  drivers/mmc/host/jz4740_mmc.c | 23 +++
  1 file changed, 23 insertions(+)

 diff --git a/drivers/mmc/host/jz4740_mmc.c 
b/drivers/mmc/host/jz4740_mmc.c

 index 44a04fe..4cbe7fb 100644
 --- a/drivers/mmc/host/jz4740_mmc.c
 +++ b/drivers/mmc/host/jz4740_mmc.c
 @@ -43,6 +43,7 @@
  #define JZ_REG_MMC_RESP_FIFO   0x34
  #define JZ_REG_MMC_RXFIFO  0x38
  #define JZ_REG_MMC_TXFIFO  0x3C
 +#define JZ_REG_MMC_LPM 0x40
  #define JZ_REG_MMC_DMAC0x44

  #define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7)
 @@ -102,6 +103,12 @@
  #define JZ_MMC_DMAC_DMA_SEL BIT(1)
  #define JZ_MMC_DMAC_DMA_EN BIT(0)

 +#defineJZ_MMC_LPM_DRV_RISING BIT(31)
 +#defineJZ_MMC_LPM_DRV_RISING_QTR_PHASE_DLY BIT(31)
 +#defineJZ_MMC_LPM_DRV_RISING_1NS_DLY BIT(30)
 +#defineJZ_MMC_LPM_SMP_RISING_QTR_OR_HALF_PHASE_DLY BIT(29)
 +#defineJZ_MMC_LPM_LOW_POWER_MODE_EN BIT(0)
 +
  #define JZ_MMC_CLK_RATE 2400

  enum jz4740_mmc_version {
 @@ -860,6 +867,22 @@ static int jz4740_mmc_set_clock_rate(struct 
jz4740_mmc_host *host, int rate)

 }

 writew(div, host->base + JZ_REG_MMC_CLKRT);
 +
 +   if (real_rate > 2500) {
 +   if (host->version >= JZ_MMC_X1000) {
 +   writel(JZ_MMC_LPM_DRV_RISING_QTR_PHASE_DLY |
 +  
JZ_MMC_LPM_SMP_RISING_QTR_OR_HALF_PHASE_DLY |

 +  JZ_MMC_LPM_LOW_POWER_MODE_EN,
 +  host->base + JZ_REG_MMC_LPM);
 +   } else if (host->version >= JZ_MMC_JZ4760) {
 +   writel(JZ_MMC_LPM_DRV_RISING |
 +  JZ_MMC_LPM_LOW_POWER_MODE_EN,
 +  host->base + JZ_REG_MMC_LPM);
 +   } else if (host->version >= JZ_MMC_JZ4725B)
 +   writel(JZ_MMC_LPM_LOW_POWER_MODE_EN,
 +  host->base + JZ_REG_MMC_LPM);
 +   }
 +
 return real_rate;
  }

 --
 2.7.4







Re: [PATCH] dmaengine: jz4780: Use devm_platform_ioremap_resource() in jz4780_dma_probe()

2019-10-14 Thread Paul Cercueil

Hi Vinod,



Le lun., oct. 14, 2019 at 12:44, Vinod Koul  a écrit 
:

On 24-09-19, 21:32, Paul Cercueil wrote:

 Hi Markus,


 Le dim. 22 sept. 2019 à 11:25, Markus Elfring 
 a

 écrit :
 > From: Markus Elfring 
 > Date: Sun, 22 Sep 2019 11:18:27 +0200
 >
 > Simplify this function implementation a bit by using
 > a known wrapper function.
 >
 > This issue was detected by using the Coccinelle software.
 >
 > Signed-off-by: Markus Elfring 

 Looks good to me.

 Signed-off-by: Paul Cercueil 


Did you mean Acked or Reviewed ...??


Definitely. Sorry about that.

Reviewed-by: Paul Cercueil 




--
~Vinod





Re: [PATCH 5/5 v5] irqchip: Ingenic: Add process for more than one irq at the same time.

2019-10-05 Thread Paul Cercueil




Le mer., oct. 2, 2019 at 19:25, Zhou Yanjie  a 
écrit :

Add process for the situation that more than one irq is coming to
a single chip at the same time. The original code will only respond
to the lowest setted bit in JZ_REG_INTC_PENDING, and then exit the
interrupt dispatch function. After exiting the interrupt dispatch
function, since the second interrupt has not yet responded, the
interrupt dispatch function is again entered to process the second
interrupt. This creates additional unnecessary overhead, and the
more interrupts that occur at the same time, the more overhead is
added. The improved method in this patch is to check whether there
are still unresponsive interrupts after processing the lowest
setted bit interrupt. If there are any, the processing will be
processed according to the bit in JZ_REG_INTC_PENDING, and the
interrupt dispatch function will be exited until all processing
is completed.

Signed-off-by: Zhou Yanjie 


Looks good to me.

Reviewed-by: Paul Cercueil 



---
 drivers/irqchip/irq-ingenic.c | 17 +++--
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/irqchip/irq-ingenic.c 
b/drivers/irqchip/irq-ingenic.c

index 06ab3ad..c1be3d5 100644
--- a/drivers/irqchip/irq-ingenic.c
+++ b/drivers/irqchip/irq-ingenic.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
  *  Copyright (C) 2009-2010, Lars-Peter Clausen 
- *  JZ4740 platform IRQ support
+ *  Ingenic XBurst platform IRQ support
  */

 #include 
@@ -37,18 +37,23 @@ static irqreturn_t intc_cascade(int irq, void 
*data)

struct ingenic_intc_data *intc = irq_get_handler_data(irq);
struct irq_domain *domain = intc->domain;
struct irq_chip_generic *gc;
-   uint32_t irq_reg;
+   uint32_t pending;
unsigned i;

for (i = 0; i < intc->num_chips; i++) {
gc = irq_get_domain_generic_chip(domain, i * 32);

-   irq_reg = irq_reg_readl(gc, JZ_REG_INTC_PENDING);
-   if (!irq_reg)
+   pending = irq_reg_readl(gc, JZ_REG_INTC_PENDING);
+   if (!pending)
continue;

-   irq = irq_find_mapping(domain, __fls(irq_reg) + (i * 32));
-   generic_handle_irq(irq);
+   while (pending) {
+   int bit = __fls(pending);
+
+   irq = irq_find_mapping(domain, bit + (i * 32));
+   generic_handle_irq(irq);
+   pending &= ~BIT(bit);
+   }
}

return IRQ_HANDLED;
--
2.7.4







Re: [PATCH 1/5 v5] irqchip: ingenic: Drop redundant irq_suspend / irq_resume functions

2019-10-05 Thread Paul Cercueil

Hi Zhou,


Le mer., oct. 2, 2019 at 19:25, Zhou Yanjie  a 
écrit :

From: Paul Cercueil 

The same behaviour can be obtained by using the 
IRQCHIP_MASK_ON_SUSPEND

flag on the IRQ chip.

Signed-off-by: Paul Cercueil 


If you sumbit a patchset that contains someone else's patches you need 
to add your Signed-off-by too.



---
 drivers/irqchip/irq-ingenic.c   | 24 +---
 include/linux/irqchip/ingenic.h | 14 --
 2 files changed, 1 insertion(+), 37 deletions(-)
 delete mode 100644 include/linux/irqchip/ingenic.h

diff --git a/drivers/irqchip/irq-ingenic.c 
b/drivers/irqchip/irq-ingenic.c

index f126255..06fa810 100644
--- a/drivers/irqchip/irq-ingenic.c
+++ b/drivers/irqchip/irq-ingenic.c
@@ -10,7 +10,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
@@ -50,26 +49,6 @@ static irqreturn_t intc_cascade(int irq, void 
*data)

return IRQ_HANDLED;
 }

-static void intc_irq_set_mask(struct irq_chip_generic *gc, uint32_t 
mask)

-{
-   struct irq_chip_regs *regs = >chip_types->regs;
-
-   writel(mask, gc->reg_base + regs->enable);
-   writel(~mask, gc->reg_base + regs->disable);
-}
-
-void ingenic_intc_irq_suspend(struct irq_data *data)
-{
-   struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
-   intc_irq_set_mask(gc, gc->wake_active);
-}
-
-void ingenic_intc_irq_resume(struct irq_data *data)
-{
-   struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
-   intc_irq_set_mask(gc, gc->mask_cache);
-}
-
 static struct irqaction intc_cascade_action = {
.handler = intc_cascade,
.name = "SoC intc cascade interrupt",
@@ -127,8 +106,7 @@ static int __init ingenic_intc_of_init(struct 
device_node *node,

ct->chip.irq_mask = irq_gc_mask_disable_reg;
ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
ct->chip.irq_set_wake = irq_gc_set_wake;
-   ct->chip.irq_suspend = ingenic_intc_irq_suspend;
-   ct->chip.irq_resume = ingenic_intc_irq_resume;
+   ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND;

irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0,
   IRQ_NOPROBE | IRQ_LEVEL);
diff --git a/include/linux/irqchip/ingenic.h 
b/include/linux/irqchip/ingenic.h

deleted file mode 100644
index 1465588..000
--- a/include/linux/irqchip/ingenic.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- *  Copyright (C) 2010, Lars-Peter Clausen 
- */
-
-#ifndef __LINUX_IRQCHIP_INGENIC_H__
-#define __LINUX_IRQCHIP_INGENIC_H__
-
-#include 
-
-extern void ingenic_intc_irq_suspend(struct irq_data *data);
-extern void ingenic_intc_irq_resume(struct irq_data *data);
-
-#endif
--
2.7.4







Re: [PATCH] dmaengine: jz4780: Use devm_platform_ioremap_resource() in jz4780_dma_probe()

2019-09-24 Thread Paul Cercueil

Hi Markus,


Le dim. 22 sept. 2019 à 11:25, Markus Elfring  
a écrit :

From: Markus Elfring 
Date: Sun, 22 Sep 2019 11:18:27 +0200

Simplify this function implementation a bit by using
a known wrapper function.

This issue was detected by using the Coccinelle software.

Signed-off-by: Markus Elfring 


Looks good to me.

Signed-off-by: Paul Cercueil 



---
 drivers/dma/dma-jz4780.c | 8 +---
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index cafb1cc065bb..f42b3ef8e036 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -858,13 +858,7 @@ static int jz4780_dma_probe(struct 
platform_device *pdev)

jzdma->soc_data = soc_data;
platform_set_drvdata(pdev, jzdma);

-   res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-   if (!res) {
-   dev_err(dev, "failed to get I/O memory\n");
-   return -EINVAL;
-   }
-
-   jzdma->chn_base = devm_ioremap_resource(dev, res);
+   jzdma->chn_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(jzdma->chn_base))
return PTR_ERR(jzdma->chn_base);

--
2.23.0






Re: [PATCH v2 1/3] dt-bindings: Document JZ47xx VPU auxiliary processor

2019-08-21 Thread Paul Cercueil

Hi Rob,


Le mar. 20 août 2019 à 22:50, Rob Herring  a écrit :

On Mon, Jul 29, 2019 at 02:31:07PM -0400, Paul Cercueil wrote:
 Inside the Video Processing Unit (VPU) of the recent JZ47xx SoCs 
from

 Ingenic is a second Xburst MIPS CPU very similar to the main core.
 This document describes the devicetree bindings for this auxiliary
 processor.

 Signed-off-by: Paul Cercueil 
 ---

 Notes:
 v2: Update TCSM0 address in example

  .../bindings/remoteproc/ingenic,vpu.txt   | 36 
+++

  1 file changed, 36 insertions(+)
  create mode 100644 
Documentation/devicetree/bindings/remoteproc/ingenic,vpu.txt


 diff --git 
a/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.txt 
b/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.txt

 new file mode 100644
 index ..576f9e582780
 --- /dev/null
 +++ b/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.txt
 @@ -0,0 +1,36 @@
 +* Ingenic JZ47xx auxiliary processor
 +
 +Inside the Video Processing Unit (VPU) of the recent JZ47xx SoCs 
from Ingenic

 +is a second Xburst MIPS CPU very similar to the main core.
 +This document describes the devicetree bindings for this auxiliary 
processor.

 +
 +Required properties:
 +- compatible: Should be "ingenic,jz4770-vpu-rproc"
 +- reg: Must contain the registers location and length for:
 +  * the auxiliary processor,
 +  * the Tightly Coupled Shared Memory 0 (TCSM0),
 +  * the Tightly Coupled Shared Memory 1 (TCSM1),
 +  * the shared SRAM.
 +- reg-names: Must contain "aux", "tcsm0", "tcsm1", "sram".
 +- clocks: Clock specifier for the AUX and VPU clocks.
 +- clock-names: Must contain "aux", "vpu".
 +- interrupts: Interrupt specifier for the VPU hardware block.
 +
 +Example:
 +
 +vpu: cpu@132a {


cpu is reserved for CPUs under /cpus/. Use video-codec or 
video-decoder

or ?? It's not clear what type of video processing this does.


Hardware decode and encode of mpeg-2 and h264. I guess I'll use 
'video-decoder' then.






 +  compatible = "ingenic,jz4770-vpu-rproc";
 +
 +  reg = <0x132a 0x20 /* AUX */
 + 0x132b 0x4000 /* TCSM0 */
 + 0x132c 0xc000 /* TCSM1 */
 + 0x132f 0x7000 /* SRAM */
 +  >;
 +  reg-names = "aux", "tcsm0", "tcsm1", "sram";
 +
 +  clocks = < JZ4770_CLK_AUX>, < JZ4770_CLK_VPU>;
 +  clock-names = "aux", "vpu";
 +
 +  interrupt-parent = <>;
 +  interrupts = <3>;
 +};
 --
 2.21.0.593.g511ec345e18






Re: linux-next: Tree for Aug 19 (irqchip: irq-ingenic-tcu.c)

2019-08-19 Thread Paul Cercueil

Hi Randy,

The fix was merged a couple of hours ago in mips-next.

Cheers
-Paul



Le mar. 20 août 2019 à 0:06, Randy Dunlap  a 
écrit :

On 8/19/19 2:18 AM, Stephen Rothwell wrote:

 Hi all,

 Changes since 20190816:



on i386:

ld: drivers/irqchip/irq-ingenic-tcu.o: in function 
`ingenic_tcu_intc_cascade':
irq-ingenic-tcu.c:(.text+0xb1): undefined reference to 
`irq_get_domain_generic_chip'
ld: drivers/irqchip/irq-ingenic-tcu.o: in function 
`ingenic_tcu_irq_init':
irq-ingenic-tcu.c:(.init.text+0x91): undefined reference to 
`irq_generic_chip_ops'
ld: irq-ingenic-tcu.c:(.init.text+0xd2): undefined reference to 
`__irq_alloc_domain_generic_chips'
ld: irq-ingenic-tcu.c:(.init.text+0xfb): undefined reference to 
`irq_get_domain_generic_chip'



Full randconfig file is attached.Jason Cooper 


--
~Randy





Re: [PATCH 4/7] pwm: jz4740: Improve algorithm of clock calculation

2019-08-14 Thread Paul Cercueil

Hi Uwe,


Le mar. 13 août 2019 à 16:09, Uwe =?iso-8859-1?q?Kleine-K=F6nig?= 
 a écrit :

Hello Paul,

On Tue, Aug 13, 2019 at 02:47:28PM +0200, Paul Cercueil wrote:

 Le mar. 13 août 2019 à 14:33, Uwe Kleine-König a écrit :
 > On Tue, Aug 13, 2019 at 01:01:06PM +0200, Paul Cercueil wrote:
 > > Well, you said that I shouln't rely on the fact that 
clk_round_rate() will
 > > round down. That completely defeats the previous algorithm. So 
please tell

 > > me how to use it correctly, because I don't see it.
 >
 > Using clk_round_rate correctly without additional knowledge is 
hard. If
 > you assume at least some sane behaviour you'd still have to call 
it
 > multiple times. Assuming maxrate is the maximal rate you can 
handle

 > without overflowing your PWM registers you have to do:
 >
 >   rate = maxrate;
 >   rounded_rate = clk_round_rate(clk, rate);
 >   while (rounded_rate > rate) {
 >   if (rate < rounded_rate - rate) {
 >   /*
 >* clk doesn't support a rate smaller than
 >* maxrate (or the round_rate callback doesn't
 >* round consistently).
 >*/
 >return -ESOMETHING;
 >   }
 >   rate = rate - (rounded_rate - rate)
 >   rounded_rate = clk_round_rate(clk, rate);
 >   }
 >
 >   return rate;
 >
 > Probably it would be sensible to put that in a function provided 
by the

 > clk framework (maybe call it clk_round_rate_down and maybe with
 > additional checks).

 clk_round_rate_down() has been refused multiple times in the past 
for

 reasons that Stephen can explain.


I'd be really interested in these reasons as I think the clk framework
should make it easy to solve common tasks related to clocks. And 
finding

out the biggest supported rate not bigger than a given maxrate is
something I consider such a common task.

The first hit I found when searching was
https://lkml.org/lkml/2010/7/14/260 . In there Stephen suggested that
clk_round_rate with the current semantic is hardly useful and 
suggested

clk_round_rate_up() and clk_round_rate_down() himself.


That's from 2010, though.

I agree that clk_round_rate_up() and clk_round_rate_down() should 
exist. Even if they return -ENOSYS if it's not implemented for a given 
clock controller.




 > >  I came up with a much smarter alternative, that doesn't rely 
on the rounding
 > >  method of clk_round_rate, and which is better overall (no loop 
needed). It
 > >  sounds to me like you're bashing the code without making the 
effort to

 > >  understand what it does.
 > >
 > >  Thierry called it a "neat trick"
 > >  (https://patchwork.kernel.org/patch/10836879/) so it cannot be 
as bad as you

 > >  say.
 >
 > Either that or Thierry failed to see the downside. The obvious 
downside
 > is that once you set the period to something long (and so the clk 
was

 > limited to a small frequency) you never make the clock any faster
 > afterwards.

 Read the algorithm again.


I indeed missed a call to clk_set_rate(clk, parent_rate). I thought I
grepped for clk_set_rate before claiming the code was broken. Sorry.

So I think the code works indeed, but it feels like abusing
clk_set_max_rate. So I'd like to see some words from Stephen about 
this

procedure.

Also I think this is kind of inelegant to set the maximal rate twice. 
At

least call clk_set_max_rate only once please.


Ok. I can do that.



 > > > > > >  E.g. if at a rate of 12 MHz your computed hardware 
value for the period
 > > > > > >  is 0xf000, then at a rate of 24 MHz it won't fit in 16 
bits. So the clock
 > > > > > >  rate must be reduced to the highest possible that will 
still give you a

 > > > > > >  < 16-bit value.
 > > > > > >
 > > > > > >  We always want the highest possible clock rate that 
works, for the sake of

 > > > > > >  precision.
 > > > > >
 > > > > > This is dubious; but ok to keep the driver simple. 
(Consider a PWM that
 > > > > > can run at i MHz for i in [1, .. 30]. If a period of 120 
ns and a duty
 > > > > > cycle of 40 ns is requested you can get an exact match 
with 25 MHz, but

 > > > > > not with 30 MHz.)
 > > > >
 > > > > The clock rate is actually (parent_rate >> (2 * x) )
 > > > > for x = 0, 1, 2, ...
 > > > >
 > > > > So if your parent_rate is 30 MHz the next valid one is 7.5 
MHz, and the
 > > > > next one is 1.875 MHz. It'd be very unlikely that you get a 
better match at

 > > > > a lower clock.
 > > >
 > > > If the smaller freqs are all divide

Re: [PATCH 4/7] pwm: jz4740: Improve algorithm of clock calculation

2019-08-13 Thread Paul Cercueil




Le mar. 13 août 2019 à 14:33, Uwe =?iso-8859-1?q?Kleine-K=F6nig?= 
 a écrit :

Hello Paul,

On Tue, Aug 13, 2019 at 01:01:06PM +0200, Paul Cercueil wrote:

 Le mar. 13 août 2019 à 7:27, Uwe =?iso-8859-1?q?Kleine-K=F6nig?=
  a écrit :
 > [adding Stephen Boyd to Cc]
 >
 > On Tue, Aug 13, 2019 at 12:16:23AM +0200, Paul Cercueil wrote:
 > > Le lun. 12 août 2019 à 23:48, Uwe Kleine-König a écrit :
 > > > On Mon, Aug 12, 2019 at 10:43:10PM +0200, Paul Cercueil wrote:
 > > > > Le lun. 12 août 2019 à 8:15, Uwe Kleine-König a écrit :
 > > > > > On Fri, Aug 09, 2019 at 07:14:45PM +0200, Paul Cercueil 
wrote:
 > > > > > > Le ven. 9 août 2019 à 19:05, Uwe Kleine-König a 
écrit :
 > > > > > > > On Fri, Aug 09, 2019 at 02:30:28PM +0200, Paul 
Cercueil wrote:

 > > > > > > > > [...]
 > > > > > > > >  +	/* Reset the clock to the maximum rate, and 
we'll reduce it if needed */

 > > > > > > > >  + ret = clk_set_max_rate(clk, parent_rate);
 > > > > > > >
 > > > > > > > What is the purpose of this call? IIUC this limits 
the allowed range of
 > > > > > > > rates for clk. I assume the idea is to prevent other 
consumers to change
 > > > > > > > the rate in a way that makes it unsuitable for this 
pwm. But this only
 > > > > > > > makes sense if you had a notifier for clk changes, 
doesn't it? I'm

 > > > > > > > confused.
 > > > > > >
 > > > > > > Nothing like that. The second call to 
clk_set_max_rate() might have set
 > > > > > > a maximum clock rate that's lower than the parent's 
rate, and we want to

 > > > > > > undo that.
 > > > > >
 > > > > > I still don't get the purpose of this call. Why do you 
limit the clock

 > > > > > rate at all?
 > > > >
 > > > > As it says below, we "limit the clock to a maximum rate 
that still gives
 > > > > us a period value which fits in 16 bits". So that the 
computed hardware

 > > > > values won't overflow.
 > > >
 > > > But why not just using clk_set_rate? You want to have the 
clock running
 > > > at a certain rate, not any rate below that certain rate, 
don't you?

 > >
 > >  I'll let yourself answer yourself:
 > >  https://patchwork.ozlabs.org/patch/1018969/
 >
 > In that thread I claimed that you used clk_round_rate wrongly, 
not that
 > you should use clk_set_max_rate(). (The claim was somewhat 
weakend by
 > Stephen, but still I think that clk_round_rate is the right 
approach.)


 Well, you said that I shouln't rely on the fact that 
clk_round_rate() will
 round down. That completely defeats the previous algorithm. So 
please tell

 me how to use it correctly, because I don't see it.


Using clk_round_rate correctly without additional knowledge is hard. 
If

you assume at least some sane behaviour you'd still have to call it
multiple times. Assuming maxrate is the maximal rate you can handle
without overflowing your PWM registers you have to do:

rate = maxrate;
rounded_rate = clk_round_rate(clk, rate);
while (rounded_rate > rate) {
if (rate < rounded_rate - rate) {
/*
 * clk doesn't support a rate smaller than
 * maxrate (or the round_rate callback doesn't
 * round consistently).
 */
 return -ESOMETHING;
}
rate = rate - (rounded_rate - rate)
rounded_rate = clk_round_rate(clk, rate);
}

return rate;

Probably it would be sensible to put that in a function provided by 
the

clk framework (maybe call it clk_round_rate_down and maybe with
additional checks).


clk_round_rate_down() has been refused multiple times in the past for 
reasons that Stephen can explain.





 I came up with a much smarter alternative, that doesn't rely on the 
rounding
 method of clk_round_rate, and which is better overall (no loop 
needed). It
 sounds to me like you're bashing the code without making the effort 
to

 understand what it does.

 Thierry called it a "neat trick"
 (https://patchwork.kernel.org/patch/10836879/) so it cannot be as 
bad as you

 say.


Either that or Thierry failed to see the downside. The obvious 
downside

is that once you set the period to something long (and so the clk was
limited to a small frequency) you never make the clock any faster
afterwards.


Read the algorithm again.




Also I wonder how clk_set_max_rate() is supposed to be used like that 
or

if instead some work should be invest

Re: [PATCH 4/7] pwm: jz4740: Improve algorithm of clock calculation

2019-08-13 Thread Paul Cercueil




Le mar. 13 août 2019 à 7:27, Uwe =?iso-8859-1?q?Kleine-K=F6nig?= 
 a écrit :

Hello Paul,

[adding Stephen Boyd to Cc]

On Tue, Aug 13, 2019 at 12:16:23AM +0200, Paul Cercueil wrote:

 Le lun. 12 août 2019 à 23:48, Uwe Kleine-König a écrit :
 > On Mon, Aug 12, 2019 at 10:43:10PM +0200, Paul Cercueil wrote:
 > > Le lun. 12 août 2019 à 8:15, Uwe Kleine-König a écrit :
 > > > On Fri, Aug 09, 2019 at 07:14:45PM +0200, Paul Cercueil wrote:
 > > > > Le ven. 9 août 2019 à 19:05, Uwe Kleine-König a écrit :
 > > > > > On Fri, Aug 09, 2019 at 02:30:28PM +0200, Paul Cercueil 
wrote:

 > > > > > > [...]
 > > > > > >  +	/* Reset the clock to the maximum rate, and we'll 
reduce it if needed */

 > > > > > >  +   ret = clk_set_max_rate(clk, parent_rate);
 > > > > >
 > > > > > What is the purpose of this call? IIUC this limits the 
allowed range of
 > > > > > rates for clk. I assume the idea is to prevent other 
consumers to change
 > > > > > the rate in a way that makes it unsuitable for this pwm. 
But this only
 > > > > > makes sense if you had a notifier for clk changes, 
doesn't it? I'm

 > > > > > confused.
 > > > >
 > > > > Nothing like that. The second call to clk_set_max_rate() 
might have set
 > > > > a maximum clock rate that's lower than the parent's rate, 
and we want to

 > > > > undo that.
 > > >
 > > > I still don't get the purpose of this call. Why do you limit 
the clock

 > > > rate at all?
 > >
 > > As it says below, we "limit the clock to a maximum rate that 
still gives
 > > us a period value which fits in 16 bits". So that the computed 
hardware

 > > values won't overflow.
 >
 > But why not just using clk_set_rate? You want to have the clock 
running
 > at a certain rate, not any rate below that certain rate, don't 
you?


 I'll let yourself answer yourself:
 https://patchwork.ozlabs.org/patch/1018969/


In that thread I claimed that you used clk_round_rate wrongly, not 
that

you should use clk_set_max_rate(). (The claim was somewhat weakend by
Stephen, but still I think that clk_round_rate is the right approach.)


Well, you said that I shouln't rely on the fact that clk_round_rate() 
will round down. That completely defeats the previous algorithm. So 
please tell me how to use it correctly, because I don't see it.


I came up with a much smarter alternative, that doesn't rely on the 
rounding method of clk_round_rate, and which is better overall (no loop 
needed). It sounds to me like you're bashing the code without making 
the effort to understand what it does.


Thierry called it a "neat trick" 
(https://patchwork.kernel.org/patch/10836879/) so it cannot be as bad 
as you say.





The upside of clk_round_rate is that it allows you to test for the
capabilities of the clock without actually changing it before you 
found

a setting you consider to be good.


I know what clk_round_rate() is for. But here we don't do 
trial-and-error to find the first highest clock rate that works, we 
compute the maximum clock we can use and limit the clock rate to that.





 It's enough to run it below a certain rate, yes. The actual rate 
doesn't

 actually matter that much.


1 Hz would be fine? I doubt it.


We use the highest possible clock rate. We wouldn't use 1 Hz unless 
it's the highest clock rate available.





 > >  E.g. if at a rate of 12 MHz your computed hardware value for 
the period
 > >  is 0xf000, then at a rate of 24 MHz it won't fit in 16 bits. 
So the clock
 > >  rate must be reduced to the highest possible that will still 
give you a

 > >  < 16-bit value.
 > >
 > >  We always want the highest possible clock rate that works, for 
the sake of

 > >  precision.
 >
 > This is dubious; but ok to keep the driver simple. (Consider a 
PWM that
 > can run at i MHz for i in [1, .. 30]. If a period of 120 ns and a 
duty
 > cycle of 40 ns is requested you can get an exact match with 25 
MHz, but

 > not with 30 MHz.)

 The clock rate is actually (parent_rate >> (2 * x) )
 for x = 0, 1, 2, ...

 So if your parent_rate is 30 MHz the next valid one is 7.5 MHz, and 
the
 next one is 1.875 MHz. It'd be very unlikely that you get a better 
match at

 a lower clock.


If the smaller freqs are all dividers of the fastest that's fine. 
Please

note in a code comment that you're assuming this.


No, I am not assuming this. The current driver just picks the highest 
clock rate that works. We're not changing the behaviour here.





 > >  > >  Basically, we start from the maximum clock rate we can 
get for that PWM
 > >  > >  - which is the rate of the parent clk - and from that 
compute t

Re: [PATCH 10/11] mfd: Drop obsolete JZ4740 driver

2019-08-13 Thread Paul Cercueil

Hi Philippe,


Le mar. 13 août 2019 à 10:44, Philippe 
=?iso-8859-1?q?Mathieu-Daud=E9?=  a écrit :

Hi Lee,

On 8/12/19 10:16 AM, Lee Jones wrote:

 On Thu, 25 Jul 2019, Paul Cercueil wrote:


 It has been replaced with the ingenic-iio driver for the ADC.

 Signed-off-by: Paul Cercueil 
 Tested-by: Artur Rojek 
 ---
  drivers/mfd/Kconfig  |   9 --
  drivers/mfd/Makefile |   1 -
  drivers/mfd/jz4740-adc.c | 324 
---

  3 files changed, 334 deletions(-)
  delete mode 100644 drivers/mfd/jz4740-adc.c


 Applied, thanks.


It seems the replacement is done in "MIPS: qi_lb60: Migrate to
devicetree" which is not yet merged.


It's merged in the MIPS tree, though, so it'll blend together just
fine in linux-next.



Probably easier if this patch goes thru the MIPS tree as part of the
"JZ4740 SoC cleanup" series.

Regards,

Phil.





Re: [PATCH 4/7] pwm: jz4740: Improve algorithm of clock calculation

2019-08-12 Thread Paul Cercueil

[Re-send my message in plain text, as it was bounced by the
lists - sorry about that]


Le lun. 12 août 2019 à 23:48, Uwe =?iso-8859-1?q?Kleine-K=F6nig?= 
 a écrit :

Hello Paul,

On Mon, Aug 12, 2019 at 10:43:10PM +0200, Paul Cercueil wrote:

 Le lun. 12 août 2019 à 8:15, Uwe =?iso-8859-1?q?Kleine-K=F6nig?=
  a écrit :
 > On Fri, Aug 09, 2019 at 07:14:45PM +0200, Paul Cercueil wrote:
 > >  Le ven. 9 août 2019 à 19:05, Uwe 
=?iso-8859-1?q?Kleine-K=F6nig?=

 > >   a écrit :
 > >  > On Fri, Aug 09, 2019 at 02:30:28PM +0200, Paul Cercueil 
wrote:

 > >  > > [...]
 > >  > >  +	/* Reset the clock to the maximum rate, and we'll 
reduce it if needed */

 > >  > >  +ret = clk_set_max_rate(clk, parent_rate);
 > >  >
 > >  > What is the purpose of this call? IIUC this limits the 
allowed range of
 > >  > rates for clk. I assume the idea is to prevent other 
consumers to change
 > >  > the rate in a way that makes it unsuitable for this pwm. But 
this only
 > >  > makes sense if you had a notifier for clk changes, doesn't 
it? I'm

 > >  > confused.
 > >
 > >  Nothing like that. The second call to clk_set_max_rate() might 
have set
 > >  a maximum clock rate that's lower than the parent's rate, and 
we want to

 > >  undo that.
 >
 > I still don't get the purpose of this call. Why do you limit the 
clock

 > rate at all?

 As it says below, we "limit the clock to a maximum rate that still 
gives
 us a period value which fits in 16 bits". So that the computed 
hardware

 values won't overflow.


But why not just using clk_set_rate? You want to have the clock 
running

at a certain rate, not any rate below that certain rate, don't you?


I'll let yourself answer yourself:
https://patchwork.ozlabs.org/patch/1018969/

It's enough to run it below a certain rate, yes. The actual rate doesn't
actually matter that much.




 E.g. if at a rate of 12 MHz your computed hardware value for the 
period
 is 0xf000, then at a rate of 24 MHz it won't fit in 16 bits. So the 
clock
 rate must be reduced to the highest possible that will still give 
you a

 < 16-bit value.

 We always want the highest possible clock rate that works, for the 
sake of

 precision.


This is dubious; but ok to keep the driver simple. (Consider a PWM 
that

can run at i MHz for i in [1, .. 30]. If a period of 120 ns and a duty
cycle of 40 ns is requested you can get an exact match with 25 MHz, 
but

not with 30 MHz.)


The clock rate is actually (parent_rate >> (2 * x) )
for x = 0, 1, 2, ...

So if your parent_rate is 30 MHz the next valid one is 7.5 MHz, and the
next one is 1.875 MHz. It'd be very unlikely that you get a better 
match at a

lower clock.


 > >  Basically, we start from the maximum clock rate we can get for 
that PWM
 > >  - which is the rate of the parent clk - and from that compute 
the maximum
 > >  clock rate that we can support that still gives us < 16-bits 
hardware

 > >  values for the period and duty.
 > >
 > >  We then pass that computed maximum clock rate to 
clk_set_max_rate(), which
 > >  may or may not update the current PWM clock's rate to match 
the new limits.
 > >  Finally we read back the PWM clock's rate and compute the 
period and duty

 > >  from that.
 >
 > If you change the clk rate, is this externally visible on the PWM
 > output? Does this affect other PWM instances?

 The clock rate doesn't change the PWM output because the hardware 
values for

 the period and duty are adapted accordingly to reflect the change.


It doesn't change it in the end. But in the (short) time frame between
the call to change the clock and the update of the PWM registers there
is a glitch, right?


The PWM is disabled, so the line is in inactive state, and will be in 
that state

until the PWM is enabled again. No glitch to fear.



You didn't answer to the question about other PWM instances. Does that
mean others are not affected?


Sorry. Yes, they are not affected - all PWM channels are independent.



Best regards
Uwe

PS: It would be great if you could fix your mailer to not damage the
quoted mail. Also it doesn't seem to understand how my name is encoded
in the From line. I fixed up the quotes in my reply.


I guess I'll submit a bug report to Geary then.




--
Pengutronix e.K.   | Uwe Kleine-König
|
Industrial Linux Solutions | 
http://www.pengutronix.de/  |





Re: [PATCH 6/7] pwm: jz4740: Make PWM start with the active part

2019-08-12 Thread Paul Cercueil




Le lun. 12 août 2019 à 7:55, Uwe =?iso-8859-1?q?Kleine-K=F6nig?= 
 a écrit :

On Fri, Aug 09, 2019 at 07:33:24PM +0200, Paul Cercueil wrote:



 Le ven. 9 août 2019 à 19:10, Uwe =?iso-8859-1?q?Kleine-K=F6nig?=
  a écrit :
 > On Fri, Aug 09, 2019 at 02:30:30PM +0200, Paul Cercueil wrote:
 > >  The PWM will always start with the inactive part. To counter 
that,

 > >  when PWM is enabled we switch the configured polarity, and use
 > >  'period - duty + 1' as the real duty.
 >
 > Where does the + 1 come from? This looks wrong. (So if duty=0 is
 > requested you use duty = period + 1?)

 You'd never request duty == 0, would you?

 Your duty must always be in the inclusive range [1, period]
 (hardware values, not ns). A duty of 0 is a hardware fault
 (on the jz4740 it is).


From the PWM framework's POV duty cycle = 0 is perfectly valid. 
Similar
to duty == period. Not supporting dutz cycle 0 is another limitation 
of

your PWM that should be documented.

For actual use cases of duty cycle = 0 see drivers/hwmon/pwm-fan.c or
drivers/leds/leds-pwm.c.


Perfectly valid for the PWM framework, maybe; but what is the expected
output then? A constant inactive state? Then I guess I can just disable
the PWM output in the driver when configured with duty == 0.



 If you request duty == 1 (the minimum), then the new duty is equal
 to (period - 1 + 1) == period, which is the maximum of your range.

 If you request duty == period (the maximum), then the new duty
 calculated is equal to (period - period + 1) == 1, which is the
 minimum of your range.


 > >
 > >  Signed-off-by: Paul Cercueil 
 > >  ---
 > >   drivers/pwm/pwm-jz4740.c | 22 +-
 > >   1 file changed, 13 insertions(+), 9 deletions(-)
 > >
 > >  diff --git a/drivers/pwm/pwm-jz4740.c 
b/drivers/pwm/pwm-jz4740.c

 > >  index 85e2110aae4f..8df898429d47 100644
 > >  --- a/drivers/pwm/pwm-jz4740.c
 > >  +++ b/drivers/pwm/pwm-jz4740.c
 > >  @@ -121,6 +121,7 @@ static int jz4740_pwm_apply(struct pwm_chip
 > > *chip, struct pwm_device *pwm,
 > > *parent_clk = clk_get_parent(clk);
 > >  unsigned long rate, parent_rate, period, duty;
 > >  unsigned long long tmp;
 > >  +   bool polarity_inversed;
 > >  int ret;
 > >
 > >  parent_rate = clk_get_rate(parent_clk);
 > >  @@ -183,24 +184,27 @@ static int jz4740_pwm_apply(struct 
pwm_chip

 > > *chip, struct pwm_device *pwm,
 > >  /* Reset counter to 0 */
 > >  regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0);
 > >
 > >  -   /* Set duty */
 > >  -   regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), duty);
 > >  -
 > >  /* Set period */
 > >  regmap_write(jz4740->map, TCU_REG_TDFRc(pwm->hwpwm), period);
 > >
 > >  +   /*
 > >  +	 * The PWM will always start with the inactive part. To 
counter that,
 > >  +	 * when PWM is enabled we switch the configured polarity, 
and use

 > >  +* 'period - duty + 1' as the real duty.
 > >  +*/
 > >  +
 > >  +   /* Set duty */
 > >  +	regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), period 
- duty + 1);

 > >  +
 >
 > Before you set duty first, then period, now you do it the other 
way

 > round. Is there a good reason?

 To move it below the comment that explains why we use 'period - 
duty + 1'.


 We modify that line anyway, so it's not like it makes the patch 
much more

 verbose.


It doesn't make it more verbose, but that's not the background of my
question. For most(?) PWM implementation the order of hardware 
accesses

matters and introducing such a difference as an unneeded side effect
isn't optimal.


There's no side effect. The PWM is disabled when reconfigured.


Why not add the comment above the line that already used to set the 
duty

in hardware?


I thought it made sense to have the two parts of the trick closer 
together

in the code, below the comment, so that it's clearer what it does.



 > >  /* Set polarity */
 > >  -   switch (state->polarity) {
 > >  -   case PWM_POLARITY_NORMAL:
 > >  +   polarity_inversed = state->polarity == PWM_POLARITY_INVERSED;
 > >  +   if (!polarity_inversed ^ state->enabled)
 >
 > Why does state->enabled suddenly matter here?

 The pin stay inactive when the PWM is disabled, but the level of the
 inactive state depends on the polarity of the pin. So we need to 
switch

 the polarity only when the PWM is enabled.


After some thought I got that. When knowing this, this is already
mentioned in the comment you introduced as you write about enabled 
PWMs

only. Maybe it's just me, but mentioning that case more explicit would
have helped me. Something like:

/*
 * The hardware always starts a period w

Re: [PATCH 4/7] pwm: jz4740: Improve algorithm of clock calculation

2019-08-12 Thread Paul Cercueil




Le lun. 12 août 2019 à 8:15, Uwe =?iso-8859-1?q?Kleine-K=F6nig?= 
 a écrit :

Hello Paul,

On Fri, Aug 09, 2019 at 07:14:45PM +0200, Paul Cercueil wrote:

 Le ven. 9 août 2019 à 19:05, Uwe =?iso-8859-1?q?Kleine-K=F6nig?=
  a écrit :
 > On Fri, Aug 09, 2019 at 02:30:28PM +0200, Paul Cercueil wrote:
 > >  The previous algorithm hardcoded details about how the TCU 
clocks

 > > work.
 > >  The new algorithm will use clk_round_rate to find the perfect 
clock

 > > rate
 > >  for the PWM channel.
 > >
 > >  Signed-off-by: Paul Cercueil 
 > >  Tested-by: Mathieu Malaterre 
 > >  Tested-by: Artur Rojek 
 > >  ---
 > >   drivers/pwm/pwm-jz4740.c | 60
 > > +---
 > >   1 file changed, 44 insertions(+), 16 deletions(-)
 > >
 > >  diff --git a/drivers/pwm/pwm-jz4740.c 
b/drivers/pwm/pwm-jz4740.c

 > >  index 6ec8794d3b99..f20dc2e19240 100644
 > >  --- a/drivers/pwm/pwm-jz4740.c
 > >  +++ b/drivers/pwm/pwm-jz4740.c
 > >  @@ -110,24 +110,56 @@ static int jz4740_pwm_apply(struct 
pwm_chip

 > > *chip, struct pwm_device *pwm,
 > >  struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
 > >  struct clk *clk = pwm_get_chip_data(pwm),
 > > *parent_clk = clk_get_parent(clk);
 > >  -   unsigned long rate, period, duty;
 > >  +   unsigned long rate, parent_rate, period, duty;
 > >  unsigned long long tmp;
 > >  -   unsigned int prescaler = 0;
 > >  +   int ret;
 > >
 > >  -   rate = clk_get_rate(parent_clk);
 > >  -   tmp = (unsigned long long)rate * state->period;
 > >  -   do_div(tmp, 10);
 > >  -   period = tmp;
 > >  +   parent_rate = clk_get_rate(parent_clk);
 > >  +
 > >  +   jz4740_pwm_disable(chip, pwm);
 > >
 > >  -   while (period > 0x && prescaler < 6) {
 > >  -   period >>= 2;
 > >  -   rate >>= 2;
 > >  -   ++prescaler;
 > >  +	/* Reset the clock to the maximum rate, and we'll reduce it 
if needed */

 > >  +   ret = clk_set_max_rate(clk, parent_rate);
 >
 > What is the purpose of this call? IIUC this limits the allowed 
range of
 > rates for clk. I assume the idea is to prevent other consumers to 
change
 > the rate in a way that makes it unsuitable for this pwm. But this 
only

 > makes sense if you had a notifier for clk changes, doesn't it? I'm
 > confused.

 Nothing like that. The second call to clk_set_max_rate() might have 
set
 a maximum clock rate that's lower than the parent's rate, and we 
want to

 undo that.


I still don't get the purpose of this call. Why do you limit the clock
rate at all?


As it says below, we "limit the clock to a maximum rate that still gives
us a period value which fits in 16 bits". So that the computed hardware
values won't overflow.

E.g. if at a rate of 12 MHz your computed hardware value for the period
is 0xf000, then at a rate of 24 MHz it won't fit in 16 bits. So the 
clock

rate must be reduced to the highest possible that will still give you a
< 16-bit value.

We always want the highest possible clock rate that works, for the sake 
of

precision.


 > I think this doesn't match the commit log, you didn't even 
introduced a

 > call to clk_round_rate().

 Right, I'll edit the commit message.


 > >  +   if (ret) {
 > >  +   dev_err(chip->dev, "Unable to set max rate: %d\n", ret);
 > >  +   return ret;
 > >  }
 > >
 > >  -   if (prescaler == 6)
 > >  -   return -EINVAL;
 > >  +   ret = clk_set_rate(clk, parent_rate);
 > >  +   if (ret) {
 > >  +		dev_err(chip->dev, "Unable to reset to parent rate (%lu 
Hz)",

 > >  +   parent_rate);
 > >  +   return ret;
 > >  +   }
 > >  +
 > >  +   /*
 > >  +	 * Limit the clock to a maximum rate that still gives us a 
period value

 > >  +* which fits in 16 bits.
 > >  +*/
 > >  +   tmp = 0xull * NSEC_PER_SEC;
 > >  +   do_div(tmp, state->period);
 > >
 > >  +   ret = clk_set_max_rate(clk, tmp);
 >
 > And now you change the maximal rate again?

 Basically, we start from the maximum clock rate we can get for that 
PWM
 - which is the rate of the parent clk - and from that compute the 
maximum
 clock rate that we can support that still gives us < 16-bits 
hardware

 values for the period and duty.

 We then pass that computed maximum clock rate to 
clk_set_max_rate(), which
 may or may not update the current PWM clock's rate to match the new 
limits.
 Finally we read back the PWM clock's rate and compute the period 
and duty

 from that.


If you change the clk 

[PATCH v2] clk: ingenic: Use CLK_OF_DECLARE_DRIVER macro

2019-08-10 Thread Paul Cercueil
By using CLK_OF_DECLARE_DRIVER instead of the CLK_OF_DECLARE macro, we
allow the driver to probe also as a platform driver.

While this driver does not have code to probe as a platform driver, this
is still useful for probing children devices in the case where the
device node is compatible with "simple-mfd".

Signed-off-by: Paul Cercueil 
---

Notes:
v2: Rebased on v5.3-rc3

 drivers/clk/ingenic/jz4725b-cgu.c | 2 +-
 drivers/clk/ingenic/jz4740-cgu.c  | 2 +-
 drivers/clk/ingenic/jz4770-cgu.c  | 2 +-
 drivers/clk/ingenic/jz4780-cgu.c  | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/ingenic/jz4725b-cgu.c 
b/drivers/clk/ingenic/jz4725b-cgu.c
index 2642d36d1e2c..a3b4635f6278 100644
--- a/drivers/clk/ingenic/jz4725b-cgu.c
+++ b/drivers/clk/ingenic/jz4725b-cgu.c
@@ -257,4 +257,4 @@ static void __init jz4725b_cgu_init(struct device_node *np)
 
ingenic_cgu_register_syscore_ops(cgu);
 }
-CLK_OF_DECLARE(jz4725b_cgu, "ingenic,jz4725b-cgu", jz4725b_cgu_init);
+CLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-cgu", jz4725b_cgu_init);
diff --git a/drivers/clk/ingenic/jz4740-cgu.c b/drivers/clk/ingenic/jz4740-cgu.c
index 4c0a20949c2c..dd86296fb8c1 100644
--- a/drivers/clk/ingenic/jz4740-cgu.c
+++ b/drivers/clk/ingenic/jz4740-cgu.c
@@ -241,4 +241,4 @@ static void __init jz4740_cgu_init(struct device_node *np)
 
ingenic_cgu_register_syscore_ops(cgu);
 }
-CLK_OF_DECLARE(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init);
+CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init);
diff --git a/drivers/clk/ingenic/jz4770-cgu.c b/drivers/clk/ingenic/jz4770-cgu.c
index eebc1bea3841..956dd653a43d 100644
--- a/drivers/clk/ingenic/jz4770-cgu.c
+++ b/drivers/clk/ingenic/jz4770-cgu.c
@@ -443,4 +443,4 @@ static void __init jz4770_cgu_init(struct device_node *np)
 }
 
 /* We only probe via devicetree, no need for a platform driver */
-CLK_OF_DECLARE(jz4770_cgu, "ingenic,jz4770-cgu", jz4770_cgu_init);
+CLK_OF_DECLARE_DRIVER(jz4770_cgu, "ingenic,jz4770-cgu", jz4770_cgu_init);
diff --git a/drivers/clk/ingenic/jz4780-cgu.c b/drivers/clk/ingenic/jz4780-cgu.c
index 8c67f89df25e..ea905ff72bf0 100644
--- a/drivers/clk/ingenic/jz4780-cgu.c
+++ b/drivers/clk/ingenic/jz4780-cgu.c
@@ -725,4 +725,4 @@ static void __init jz4780_cgu_init(struct device_node *np)
 
ingenic_cgu_register_syscore_ops(cgu);
 }
-CLK_OF_DECLARE(jz4780_cgu, "ingenic,jz4780-cgu", jz4780_cgu_init);
+CLK_OF_DECLARE_DRIVER(jz4780_cgu, "ingenic,jz4780-cgu", jz4780_cgu_init);
-- 
2.21.0.593.g511ec345e18



Re: [PATCH] clk: ingenic: Use CLK_OF_DECLARE_DRIVER macro

2019-08-10 Thread Paul Cercueil




Le jeu. 8 août 2019 à 6:23, Stephen Boyd  a écrit :

Quoting Paul Cercueil (2019-07-16 10:08:00)
 By using CLK_OF_DECLARE_DRIVER instead of the CLK_OF_DECLARE macro, 
we

 allow the driver to probe also as a platform driver.

 While this driver does not have code to probe as a platform driver, 
this

 is still useful for probing children devices in the case where the
 device node is compatible with "simple-mfd".

 Signed-off-by: Paul Cercueil 
 ---


What's the baseline for this? It doesn't apply cleanly to v5.3-rc1


I think it was v5.2-rc7. I'll send a V2 rebased on v5.3-rc3.

-Paul




[PATCH 1/2] mmc: jz4740: Code cleanup

2019-08-10 Thread Paul Cercueil
Fix wrong code indentation which made the code hard to read, and fix
return with value in void function.

Signed-off-by: Paul Cercueil 
---
 drivers/mmc/host/jz4740_mmc.c | 14 +++---
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index ffdbfaadd3f2..59f81e8afcce 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -186,9 +186,9 @@ static void jz4740_mmc_write_irq_reg(struct jz4740_mmc_host 
*host,
 uint32_t val)
 {
if (host->version >= JZ_MMC_JZ4780)
-   return writel(val, host->base + JZ_REG_MMC_IREG);
+   writel(val, host->base + JZ_REG_MMC_IREG);
else
-   return writew(val, host->base + JZ_REG_MMC_IREG);
+   writew(val, host->base + JZ_REG_MMC_IREG);
 }
 
 static uint32_t jz4740_mmc_read_irq_reg(struct jz4740_mmc_host *host)
@@ -820,14 +820,14 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
del_timer(>timeout_timer);
 
if (status & JZ_MMC_STATUS_TIMEOUT_RES) {
-   cmd->error = -ETIMEDOUT;
+   cmd->error = -ETIMEDOUT;
} else if (status & JZ_MMC_STATUS_CRC_RES_ERR) {
-   cmd->error = -EIO;
+   cmd->error = -EIO;
} else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR |
JZ_MMC_STATUS_CRC_WRITE_ERROR)) {
-   if (cmd->data)
-   cmd->data->error = -EIO;
-   cmd->error = -EIO;
+   if (cmd->data)
+   cmd->data->error = -EIO;
+   cmd->error = -EIO;
}
 
jz4740_mmc_set_irq_enabled(host, irq_reg, false);
-- 
2.21.0.593.g511ec345e18



[PATCH 2/2] mmc: jz4740: Drop dependency on arch header

2019-08-10 Thread Paul Cercueil
We don't need to set the 'slave_id' anymore - that field is never read
by the DMA driver.

Signed-off-by: Paul Cercueil 
---
 drivers/mmc/host/jz4740_mmc.c | 4 
 1 file changed, 4 deletions(-)

diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index 59f81e8afcce..7ff2034d739a 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -25,8 +25,6 @@
 
 #include 
 
-#include 
-
 #define JZ_REG_MMC_STRPCL  0x00
 #define JZ_REG_MMC_STATUS  0x04
 #define JZ_REG_MMC_CLKRT   0x08
@@ -292,11 +290,9 @@ static int jz4740_mmc_start_dma_transfer(struct 
jz4740_mmc_host *host,
if (data->flags & MMC_DATA_WRITE) {
conf.direction = DMA_MEM_TO_DEV;
conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO;
-   conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT;
} else {
conf.direction = DMA_DEV_TO_MEM;
conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO;
-   conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE;
}
 
sg_count = jz4740_mmc_prepare_dma_data(host, data, COOKIE_MAPPED);
-- 
2.21.0.593.g511ec345e18



Re: [PATCH 3/7] pwm: jz4740: Drop dependency on MACH_INGENIC

2019-08-09 Thread Paul Cercueil




Le ven. 9 août 2019 à 18:41, Uwe =?iso-8859-1?q?Kleine-K=F6nig?= 
 a écrit :

On Fri, Aug 09, 2019 at 02:30:27PM +0200, Paul Cercueil wrote:
 Depending on MACH_INGENIC prevent us from creating a generic kernel 
that
 works on more than one MIPS board. Instead, we just depend on MIPS 
being

 set.

 Signed-off-by: Paul Cercueil 
 ---
  drivers/pwm/Kconfig | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)

 diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
 index d2557c6fcf65..82a75e0b72e5 100644
 --- a/drivers/pwm/Kconfig
 +++ b/drivers/pwm/Kconfig
 @@ -224,7 +224,7 @@ config PWM_IMX_TPM

  config PWM_JZ4740
tristate "Ingenic JZ47xx PWM support"
 -  depends on MACH_INGENIC
 +  depends on MIPS


If this isn't actually useful on MIPS without MACH_INGENIC this is
better expressed using:

depends on MIPS
depends on MACH_INGENIC || COMPILE_TEST

This way some configuring a mips kernel without INGENIC isn't bothered
by this question.


As said in the commit message, it is useful on MIPS without 
MACH_INGENIC,

if you want to create a generic kernel that works on more than one MIPS
board.



Best regards
Uwe

--
Pengutronix e.K.   | Uwe Kleine-König
|
Industrial Linux Solutions | 
http://www.pengutronix.de/  |





Re: [PATCH 0/3] watchdog: jz4740: Driver update

2019-08-09 Thread Paul Cercueil




Le ven. 9 août 2019 à 19:29, Guenter Roeck  a 
écrit :

On Fri, Aug 09, 2019 at 06:55:26PM +0200, Paul Cercueil wrote:

 Hi Guenter,


 Le ven. 9 août 2019 à 18:52, Guenter Roeck  a 
écrit :

 >On Fri, Aug 09, 2019 at 01:59:27PM +0200, Paul Cercueil wrote:
 >> Hi,
 >>
 >> This patchset comes from a bigger patchset that was cut in 
smaller

 >> pieces for easier integration to mainline.
 >> (The patchset was https://lkml.org/lkml/2019/3/27/1837)
 >>
 >> The reviews were kept since the code mostly didn't change. The
 >>exception
 >> is the use of device_node_to_regmap() in patch 2/3. This 
function was

 >> added in a prior patch, now merged in the MIPS tree.
 >>
 >> For that reason this patchset is based on the ingenic-tcu-v5.4 
branch

 >>of
 >> the MIPS tree
 >> (git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux.git).
 >>
 >
 >What is the expectation here ? Should the series be sent upstream
 >through the watchdog tree, or through some other tree ?

 You can get it through the watchdog tree if you merge the 
ingenic-tcu-v5.4
 branch from the MIPS tree. If you'd rather not do that, I can get 
it merged

 through the MIPS tree.


I would prefer a merge through the mips tree.

Guenter


Ok; Can I get some signatures then? ;)

Thanks,
-Paul




Re: [PATCH 6/7] pwm: jz4740: Make PWM start with the active part

2019-08-09 Thread Paul Cercueil




Le ven. 9 août 2019 à 19:10, Uwe =?iso-8859-1?q?Kleine-K=F6nig?= 
 a écrit :

On Fri, Aug 09, 2019 at 02:30:30PM +0200, Paul Cercueil wrote:

 The PWM will always start with the inactive part. To counter that,
 when PWM is enabled we switch the configured polarity, and use
 'period - duty + 1' as the real duty.


Where does the + 1 come from? This looks wrong. (So if duty=0 is
requested you use duty = period + 1?)


You'd never request duty == 0, would you?

Your duty must always be in the inclusive range [1, period]
(hardware values, not ns). A duty of 0 is a hardware fault
(on the jz4740 it is).

If you request duty == 1 (the minimum), then the new duty is equal
to (period - 1 + 1) == period, which is the maximum of your range.

If you request duty == period (the maximum), then the new duty
calculated is equal to (period - period + 1) == 1, which is the
minimum of your range.




 Signed-off-by: Paul Cercueil 
 ---
  drivers/pwm/pwm-jz4740.c | 22 +-
  1 file changed, 13 insertions(+), 9 deletions(-)

 diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
 index 85e2110aae4f..8df898429d47 100644
 --- a/drivers/pwm/pwm-jz4740.c
 +++ b/drivers/pwm/pwm-jz4740.c
 @@ -121,6 +121,7 @@ static int jz4740_pwm_apply(struct pwm_chip 
*chip, struct pwm_device *pwm,

   *parent_clk = clk_get_parent(clk);
unsigned long rate, parent_rate, period, duty;
unsigned long long tmp;
 +  bool polarity_inversed;
int ret;

parent_rate = clk_get_rate(parent_clk);
 @@ -183,24 +184,27 @@ static int jz4740_pwm_apply(struct pwm_chip 
*chip, struct pwm_device *pwm,

/* Reset counter to 0 */
regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0);

 -  /* Set duty */
 -  regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), duty);
 -
/* Set period */
regmap_write(jz4740->map, TCU_REG_TDFRc(pwm->hwpwm), period);

 +  /*
 +	 * The PWM will always start with the inactive part. To counter 
that,

 +   * when PWM is enabled we switch the configured polarity, and use
 +   * 'period - duty + 1' as the real duty.
 +   */
 +
 +  /* Set duty */
 +	regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), period - 
duty + 1);

 +


Before you set duty first, then period, now you do it the other way
round. Is there a good reason?


To move it below the comment that explains why we use 'period - duty + 
1'.


We modify that line anyway, so it's not like it makes the patch much 
more

verbose.





/* Set polarity */
 -  switch (state->polarity) {
 -  case PWM_POLARITY_NORMAL:
 +  polarity_inversed = state->polarity == PWM_POLARITY_INVERSED;
 +  if (!polarity_inversed ^ state->enabled)


Why does state->enabled suddenly matter here?


The pin stay inactive when the PWM is disabled, but the level of the
inactive state depends on the polarity of the pin. So we need to switch
the polarity only when the PWM is enabled.



regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
   TCU_TCSR_PWM_INITL_HIGH, 0);
 -  break;
 -  case PWM_POLARITY_INVERSED:
 +  else
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
   TCU_TCSR_PWM_INITL_HIGH,
   TCU_TCSR_PWM_INITL_HIGH);
 -  break;
 -  }

if (state->enabled)
jz4740_pwm_enable(chip, pwm);


Best regards
Uwe

--
Pengutronix e.K.   | Uwe Kleine-König
|
Industrial Linux Solutions | 
http://www.pengutronix.de/  |





Re: [PATCH 4/7] pwm: jz4740: Improve algorithm of clock calculation

2019-08-09 Thread Paul Cercueil




Le ven. 9 août 2019 à 19:05, Uwe =?iso-8859-1?q?Kleine-K=F6nig?= 
 a écrit :

On Fri, Aug 09, 2019 at 02:30:28PM +0200, Paul Cercueil wrote:
 The previous algorithm hardcoded details about how the TCU clocks 
work.
 The new algorithm will use clk_round_rate to find the perfect clock 
rate

 for the PWM channel.

 Signed-off-by: Paul Cercueil 
 Tested-by: Mathieu Malaterre 
 Tested-by: Artur Rojek 
 ---
  drivers/pwm/pwm-jz4740.c | 60 
+---

  1 file changed, 44 insertions(+), 16 deletions(-)

 diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
 index 6ec8794d3b99..f20dc2e19240 100644
 --- a/drivers/pwm/pwm-jz4740.c
 +++ b/drivers/pwm/pwm-jz4740.c
 @@ -110,24 +110,56 @@ static int jz4740_pwm_apply(struct pwm_chip 
*chip, struct pwm_device *pwm,

struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
struct clk *clk = pwm_get_chip_data(pwm),
   *parent_clk = clk_get_parent(clk);
 -  unsigned long rate, period, duty;
 +  unsigned long rate, parent_rate, period, duty;
unsigned long long tmp;
 -  unsigned int prescaler = 0;
 +  int ret;

 -  rate = clk_get_rate(parent_clk);
 -  tmp = (unsigned long long)rate * state->period;
 -  do_div(tmp, 10);
 -  period = tmp;
 +  parent_rate = clk_get_rate(parent_clk);
 +
 +  jz4740_pwm_disable(chip, pwm);

 -  while (period > 0x && prescaler < 6) {
 -  period >>= 2;
 -  rate >>= 2;
 -  ++prescaler;
 +	/* Reset the clock to the maximum rate, and we'll reduce it if 
needed */

 +  ret = clk_set_max_rate(clk, parent_rate);


What is the purpose of this call? IIUC this limits the allowed range 
of
rates for clk. I assume the idea is to prevent other consumers to 
change

the rate in a way that makes it unsuitable for this pwm. But this only
makes sense if you had a notifier for clk changes, doesn't it? I'm
confused.


Nothing like that. The second call to clk_set_max_rate() might have set
a maximum clock rate that's lower than the parent's rate, and we want to
undo that.


I think this doesn't match the commit log, you didn't even introduced 
a

call to clk_round_rate().


Right, I'll edit the commit message.



 +  if (ret) {
 +  dev_err(chip->dev, "Unable to set max rate: %d\n", ret);
 +  return ret;
}

 -  if (prescaler == 6)
 -  return -EINVAL;
 +  ret = clk_set_rate(clk, parent_rate);
 +  if (ret) {
 +  dev_err(chip->dev, "Unable to reset to parent rate (%lu Hz)",
 +  parent_rate);
 +  return ret;
 +  }
 +
 +  /*
 +	 * Limit the clock to a maximum rate that still gives us a period 
value

 +   * which fits in 16 bits.
 +   */
 +  tmp = 0xull * NSEC_PER_SEC;
 +  do_div(tmp, state->period);

 +  ret = clk_set_max_rate(clk, tmp);


And now you change the maximal rate again?


Basically, we start from the maximum clock rate we can get for that PWM
- which is the rate of the parent clk - and from that compute the 
maximum

clock rate that we can support that still gives us < 16-bits hardware
values for the period and duty.

We then pass that computed maximum clock rate to clk_set_max_rate(), 
which
may or may not update the current PWM clock's rate to match the new 
limits.
Finally we read back the PWM clock's rate and compute the period and 
duty

from that.



 +  if (ret) {
 +  dev_err(chip->dev, "Unable to set max rate: %d\n", ret);
 +  return ret;
 +  }
 +
 +  /*
 +   * Read back the clock rate, as it may have been modified by
 +   * clk_set_max_rate()
 +   */
 +  rate = clk_get_rate(clk);
 +
 +  if (rate != parent_rate)
 +  dev_dbg(chip->dev, "PWM clock updated to %lu Hz\n", rate);
 +
 +  /* Calculate period value */
 +  tmp = (unsigned long long)rate * state->period;
 +  do_div(tmp, NSEC_PER_SEC);
 +  period = (unsigned long)tmp;
 +
 +  /* Calculate duty value */
tmp = (unsigned long long)period * state->duty_cycle;
do_div(tmp, state->period);
duty = period - tmp;
 @@ -135,14 +167,10 @@ static int jz4740_pwm_apply(struct pwm_chip 
*chip, struct pwm_device *pwm,

if (duty >= period)
duty = period - 1;

 -  jz4740_pwm_disable(chip, pwm);
 -
/* Set abrupt shutdown */
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
   TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);

 -  clk_set_rate(clk, rate);
 -


It's not obvious to me why removing these two lines belong in the
current patch.


They're not removed, they're both moved up in the function.



Best regards
Uwe

--
Pengutronix e.K.   | Uwe Kleine-König
|
Industrial Linux Solutions | 
http://www.pengutronix.de/  |





Re: [PATCH 1/7] pwm: jz4740: Obtain regmap from parent node

2019-08-09 Thread Paul Cercueil

Hi Uwe,


Le ven. 9 août 2019 à 18:51, Uwe =?iso-8859-1?q?Kleine-K=F6nig?= 
 a écrit :

On Fri, Aug 09, 2019 at 02:30:25PM +0200, Paul Cercueil wrote:

 The TCU registers are shared between a handful of drivers, accessing
 them through the same regmap.

 While this driver is devicetree-compatible, it is never (as of now)
 probed from devicetree, so this change does not introduce a ABI 
problem

 with current devicetree files.


If you adapt the binding also update the binding doc please.


It's already updated, in mips-next, so it'll be in the next -rc1.



 Signed-off-by: Paul Cercueil 
 Tested-by: Mathieu Malaterre 
 Tested-by: Artur Rojek 


nitpick: put your S-o-b line after the other tags you added.


 ---
  drivers/pwm/Kconfig  |  1 +
  drivers/pwm/pwm-jz4740.c | 80 
++--

  2 files changed, 53 insertions(+), 28 deletions(-)

 diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
 index a7e57516959e..cc4df0ec978a 100644
 --- a/drivers/pwm/Kconfig
 +++ b/drivers/pwm/Kconfig
 @@ -225,6 +225,7 @@ config PWM_IMX_TPM
  config PWM_JZ4740
tristate "Ingenic JZ47xx PWM support"
depends on MACH_INGENIC
 +  select MFD_SYSCON
help
  Generic PWM framework driver for Ingenic JZ47xx based
  machines.
 diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
 index f901e8a0d33d..7aea5e0c6e18 100644
 --- a/drivers/pwm/pwm-jz4740.c
 +++ b/drivers/pwm/pwm-jz4740.c
 @@ -8,18 +8,20 @@
  #include 
  #include 
  #include 
 +#include 
 +#include 
  #include 
  #include 
  #include 
  #include 
 -
 -#include 
 +#include 

  #define NUM_PWM 8

  struct jz4740_pwm_chip {
struct pwm_chip chip;
struct clk *clk;
 +  struct regmap *map;
  };

  static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip 
*chip)
 @@ -29,6 +31,8 @@ static inline struct jz4740_pwm_chip 
*to_jz4740(struct pwm_chip *chip)


  static int jz4740_pwm_request(struct pwm_chip *chip, struct 
pwm_device *pwm)

  {
 +  struct jz4740_pwm_chip *jz = to_jz4740(chip);
 +
/*
  	 * Timers 0 and 1 are used for system tasks, so they are 
unavailable

 * for use as PWMs.
 @@ -36,50 +40,53 @@ static int jz4740_pwm_request(struct pwm_chip 
*chip, struct pwm_device *pwm)

if (pwm->hwpwm < 2)
return -EBUSY;

 -  jz4740_timer_start(pwm->hwpwm);
 +  regmap_write(jz->map, TCU_REG_TSCR, BIT(pwm->hwpwm));


jz4740_timer_start does

writel(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_STOP_CLEAR);

with

#define JZ_REG_TIMER_STOP_CLEAR 0x2C

and

#define TCU_REG_TSCR0x3c

I wonder why the offsets are different.


The offset are different because the base is different.




return 0;
  }

  static void jz4740_pwm_free(struct pwm_chip *chip, struct 
pwm_device *pwm)

  {
 -  jz4740_timer_set_ctrl(pwm->hwpwm, 0);
 +  struct jz4740_pwm_chip *jz = to_jz4740(chip);

 -  jz4740_timer_stop(pwm->hwpwm);
 +  regmap_write(jz->map, TCU_REG_TSCR, BIT(pwm->hwpwm));


jz4740_timer_set_ctrl writes to offset (((pwm->hwpwm) * 0x10) + 0x3C)
and jz4740_timer_stop to offset 0x1c. The regmap_write doesn't do both
of them but instead writes to offset 0x3c.


I guess it should have been TCU_REG_TSSR ("Timer Stop Set Register") and
I didn't notice because the next patch drops this write anyway.

I'll do as you suggested in your other reply and swap the two patches if
it makes things easier, it'll get rid of this write.



So this doesn't seem to be a 1:1 conversion. This either needs fixing,
splitting into several patches or a better commit log.

Stopping my review here.

Best regards
Uwe

--
Pengutronix e.K.   | Uwe Kleine-König
|
Industrial Linux Solutions | 
http://www.pengutronix.de/  |





Re: [PATCH 0/3] watchdog: jz4740: Driver update

2019-08-09 Thread Paul Cercueil

Hi Guenter,


Le ven. 9 août 2019 à 18:52, Guenter Roeck  a 
écrit :

On Fri, Aug 09, 2019 at 01:59:27PM +0200, Paul Cercueil wrote:

 Hi,

 This patchset comes from a bigger patchset that was cut in smaller
 pieces for easier integration to mainline.
 (The patchset was https://lkml.org/lkml/2019/3/27/1837)

 The reviews were kept since the code mostly didn't change. The 
exception
 is the use of device_node_to_regmap() in patch 2/3. This function 
was

 added in a prior patch, now merged in the MIPS tree.

 For that reason this patchset is based on the ingenic-tcu-v5.4 
branch of

 the MIPS tree
 (git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux.git).



What is the expectation here ? Should the series be sent upstream
through the watchdog tree, or through some other tree ?


You can get it through the watchdog tree if you merge the 
ingenic-tcu-v5.4
branch from the MIPS tree. If you'd rather not do that, I can get it 
merged

through the MIPS tree.

Thanks,
-Paul




[PATCH] clocksource: Add driver for the Ingenic JZ47xx OST

2019-08-09 Thread Paul Cercueil
From: Maarten ter Huurne 

OST is the OS Timer, a 64-bit timer/counter with buffered reading.

SoCs before the JZ4770 had (if any) a 32-bit OST; the JZ4770 and
JZ4780 have a 64-bit OST.

This driver will register both a clocksource and a sched_clock to the
system.

Signed-off-by: Maarten ter Huurne 
Signed-off-by: Paul Cercueil 
Tested-by: Mathieu Malaterre 
Tested-by: Artur Rojek 
---

Hi,

This patch comes from a bigger patchset that was cut in smaller pieces
for easier integration to mainline.
(The patchset was https://lkml.org/lkml/2019/3/27/1837)

The only change is the use of device_node_to_regmap(), which was added
in a prior patchset now merged in the MIPS tree.

For that reason this patch is based on the ingenic-tcu-v5.4 branch of
the MIPS tree
(git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux.git).

Thanks,
-Paul

 drivers/clocksource/Kconfig   |   8 ++
 drivers/clocksource/Makefile  |   1 +
 drivers/clocksource/ingenic-ost.c | 221 ++
 3 files changed, 230 insertions(+)
 create mode 100644 drivers/clocksource/ingenic-ost.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index a9cdc2c4f8bd..3b1503082a23 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -696,4 +696,12 @@ config INGENIC_TIMER
help
  Support for the timer/counter unit of the Ingenic JZ SoCs.
 
+config INGENIC_OST
+   bool "Ingenic JZ47xx Operating System Timer"
+   depends on MIPS || COMPILE_TEST
+   depends on COMMON_CLK
+   select MFD_SYSCON
+   help
+ Support for the OS Timer of the Ingenic JZ4770 or similar SoC.
+
 endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 4dfe4225ece7..6bc97a6fd229 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -80,6 +80,7 @@ obj-$(CONFIG_ASM9260_TIMER)   += asm9260_timer.o
 obj-$(CONFIG_H8300_TMR8)   += h8300_timer8.o
 obj-$(CONFIG_H8300_TMR16)  += h8300_timer16.o
 obj-$(CONFIG_H8300_TPU)+= h8300_tpu.o
+obj-$(CONFIG_INGENIC_OST)  += ingenic-ost.o
 obj-$(CONFIG_INGENIC_TIMER)+= ingenic-timer.o
 obj-$(CONFIG_CLKSRC_ST_LPC)+= clksrc_st_lpc.o
 obj-$(CONFIG_X86_NUMACHIP) += numachip.o
diff --git a/drivers/clocksource/ingenic-ost.c 
b/drivers/clocksource/ingenic-ost.c
new file mode 100644
index ..c708d5d7dd15
--- /dev/null
+++ b/drivers/clocksource/ingenic-ost.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * JZ47xx SoCs TCU Operating System Timer driver
+ *
+ * Copyright (C) 2016 Maarten ter Huurne 
+ * Copyright (C) 2018 Paul Cercueil 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define TCU_OST_TCSR_MASK  0xffc0
+#define TCU_OST_TCSR_CNT_MDBIT(15)
+
+#define TCU_OST_CHANNEL15
+
+struct ingenic_ost_soc_info {
+   bool is64bit;
+};
+
+struct ingenic_ost {
+   struct regmap *map;
+   struct clk *clk;
+
+   struct clocksource cs;
+};
+
+static struct ingenic_ost *ingenic_ost;
+
+static u64 notrace ingenic_ost_read_cntl(void)
+{
+   u32 val;
+
+   regmap_read(ingenic_ost->map, TCU_REG_OST_CNTL, );
+
+   return val;
+}
+
+static u64 notrace ingenic_ost_read_cnth(void)
+{
+   u32 val;
+
+   regmap_read(ingenic_ost->map, TCU_REG_OST_CNTH, );
+
+   return val;
+}
+
+static u64 notrace ingenic_ost_clocksource_read64(struct clocksource *cs)
+{
+   u32 val1, val2;
+   u64 count, recount;
+   s64 diff;
+
+   /*
+* The buffering of the upper 32 bits of the timer prevents wrong
+* results from the bottom 32 bits overflowing due to the timer ticking
+* along. However, it does not prevent wrong results from simultaneous
+* reads of the timer, which could reset the buffer mid-read.
+* Since this kind of wrong read can happen only when the bottom bits
+* overflow, there will be minutes between wrong reads, so if we read
+* twice in succession, at least one of the reads will be correct.
+*/
+
+   /* Bypass the regmap here as we must return as soon as possible */
+   regmap_read(ingenic_ost->map, TCU_REG_OST_CNTL, );
+   regmap_read(ingenic_ost->map, TCU_REG_OST_CNTHBUF, );
+   count = (u64)val1 | (u64)val2 << 32;
+
+   regmap_read(ingenic_ost->map, TCU_REG_OST_CNTL, );
+   regmap_read(ingenic_ost->map, TCU_REG_OST_CNTHBUF, );
+   recount = (u64)val1 | (u64)val2 << 32;
+
+   /*
+* A wrong read will produce a result that is 1<<32 too high: the bottom
+* part from before overflow and the upper part from after overflow.
+* Therefore, the lower value of the two reads is the correct value.
+*/
+
+   diff = (s64)(recount - count);
+   if (unlikely(diff < 0))
+ 

[PATCH 6/7] pwm: jz4740: Make PWM start with the active part

2019-08-09 Thread Paul Cercueil
The PWM will always start with the inactive part. To counter that,
when PWM is enabled we switch the configured polarity, and use
'period - duty + 1' as the real duty.

Signed-off-by: Paul Cercueil 
---
 drivers/pwm/pwm-jz4740.c | 22 +-
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index 85e2110aae4f..8df898429d47 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -121,6 +121,7 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct 
pwm_device *pwm,
   *parent_clk = clk_get_parent(clk);
unsigned long rate, parent_rate, period, duty;
unsigned long long tmp;
+   bool polarity_inversed;
int ret;
 
parent_rate = clk_get_rate(parent_clk);
@@ -183,24 +184,27 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct 
pwm_device *pwm,
/* Reset counter to 0 */
regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0);
 
-   /* Set duty */
-   regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), duty);
-
/* Set period */
regmap_write(jz4740->map, TCU_REG_TDFRc(pwm->hwpwm), period);
 
+   /*
+* The PWM will always start with the inactive part. To counter that,
+* when PWM is enabled we switch the configured polarity, and use
+* 'period - duty + 1' as the real duty.
+*/
+
+   /* Set duty */
+   regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), period - duty + 1);
+
/* Set polarity */
-   switch (state->polarity) {
-   case PWM_POLARITY_NORMAL:
+   polarity_inversed = state->polarity == PWM_POLARITY_INVERSED;
+   if (!polarity_inversed ^ state->enabled)
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
   TCU_TCSR_PWM_INITL_HIGH, 0);
-   break;
-   case PWM_POLARITY_INVERSED:
+   else
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
   TCU_TCSR_PWM_INITL_HIGH,
   TCU_TCSR_PWM_INITL_HIGH);
-   break;
-   }
 
if (state->enabled)
jz4740_pwm_enable(chip, pwm);
-- 
2.21.0.593.g511ec345e18



[PATCH 7/7] pwm: jz4740: document known limitations

2019-08-09 Thread Paul Cercueil
From: Uwe Kleine-König 

The jz4740 PMW implementation doesn't fulfill the (up to now
insufficiently documented) requirements of the PWM API. At least
document them in the driver.

Signed-off-by: Uwe Kleine-König 
Signed-off-by: Paul Cercueil 
---
 drivers/pwm/pwm-jz4740.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index 8df898429d47..b331a73a9d5d 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -2,6 +2,10 @@
 /*
  *  Copyright (C) 2010, Lars-Peter Clausen 
  *  JZ4740 platform PWM support
+ *
+ * Limitations:
+ * - The .apply callback doesn't complete the currently running period before
+ *   reconfiguring the hardware.
  */
 
 #include 
-- 
2.21.0.593.g511ec345e18



[PATCH 3/7] pwm: jz4740: Drop dependency on MACH_INGENIC

2019-08-09 Thread Paul Cercueil
Depending on MACH_INGENIC prevent us from creating a generic kernel that
works on more than one MIPS board. Instead, we just depend on MIPS being
set.

Signed-off-by: Paul Cercueil 
---
 drivers/pwm/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index d2557c6fcf65..82a75e0b72e5 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -224,7 +224,7 @@ config PWM_IMX_TPM
 
 config PWM_JZ4740
tristate "Ingenic JZ47xx PWM support"
-   depends on MACH_INGENIC
+   depends on MIPS
depends on COMMON_CLK
select MFD_SYSCON
help
-- 
2.21.0.593.g511ec345e18



[PATCH 5/7] pwm: jz4740: Allow selection of PWM channels 0 and 1

2019-08-09 Thread Paul Cercueil
The TCU channels 0 and 1 were previously reserved for system tasks, and
thus unavailable for PWM.

Signed-off-by: Paul Cercueil 
Tested-by: Mathieu Malaterre 
Tested-by: Artur Rojek 
---
 drivers/pwm/pwm-jz4740.c | 19 ++-
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index f20dc2e19240..85e2110aae4f 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -28,6 +28,19 @@ static inline struct jz4740_pwm_chip *to_jz4740(struct 
pwm_chip *chip)
return container_of(chip, struct jz4740_pwm_chip, chip);
 }
 
+static bool jz4740_pwm_can_use_chn(struct jz4740_pwm_chip *jz,
+  unsigned int channel)
+{
+   /* Enable all TCU channels for PWM use by default except channels 0/1 */
+   u32 pwm_channels_mask = GENMASK(NUM_PWM - 1, 2);
+
+   device_property_read_u32(jz->chip.dev->parent,
+"ingenic,pwm-channels-mask",
+_channels_mask);
+
+   return !!(pwm_channels_mask & BIT(channel));
+}
+
 static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 {
struct jz4740_pwm_chip *jz = to_jz4740(chip);
@@ -35,11 +48,7 @@ static int jz4740_pwm_request(struct pwm_chip *chip, struct 
pwm_device *pwm)
char clk_name[16];
int ret;
 
-   /*
-* Timers 0 and 1 are used for system tasks, so they are unavailable
-* for use as PWMs.
-*/
-   if (pwm->hwpwm < 2)
+   if (!jz4740_pwm_can_use_chn(jz, pwm->hwpwm))
return -EBUSY;
 
snprintf(clk_name, sizeof(clk_name), "timer%u", pwm->hwpwm);
-- 
2.21.0.593.g511ec345e18



[PATCH 2/7] pwm: jz4740: Use clocks from TCU driver

2019-08-09 Thread Paul Cercueil
The ingenic-timer "TCU" driver provides us with clocks, that can be
(un)gated, reparented or reclocked from devicetree, instead of having
these settings hardcoded in this driver.

While this driver is devicetree-compatible, it is never (as of now)
probed from devicetree, so this change does not introduce a ABI problem
with current devicetree files.

Signed-off-by: Paul Cercueil 
Tested-by: Mathieu Malaterre 
Tested-by: Artur Rojek 
---
 drivers/pwm/Kconfig  |  1 +
 drivers/pwm/pwm-jz4740.c | 40 ++--
 2 files changed, 27 insertions(+), 14 deletions(-)

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index cc4df0ec978a..d2557c6fcf65 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -225,6 +225,7 @@ config PWM_IMX_TPM
 config PWM_JZ4740
tristate "Ingenic JZ47xx PWM support"
depends on MACH_INGENIC
+   depends on COMMON_CLK
select MFD_SYSCON
help
  Generic PWM framework driver for Ingenic JZ47xx based
diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index 7aea5e0c6e18..6ec8794d3b99 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -20,7 +20,6 @@
 
 struct jz4740_pwm_chip {
struct pwm_chip chip;
-   struct clk *clk;
struct regmap *map;
 };
 
@@ -32,6 +31,9 @@ static inline struct jz4740_pwm_chip *to_jz4740(struct 
pwm_chip *chip)
 static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 {
struct jz4740_pwm_chip *jz = to_jz4740(chip);
+   struct clk *clk;
+   char clk_name[16];
+   int ret;
 
/*
 * Timers 0 and 1 are used for system tasks, so they are unavailable
@@ -40,16 +42,29 @@ static int jz4740_pwm_request(struct pwm_chip *chip, struct 
pwm_device *pwm)
if (pwm->hwpwm < 2)
return -EBUSY;
 
-   regmap_write(jz->map, TCU_REG_TSCR, BIT(pwm->hwpwm));
+   snprintf(clk_name, sizeof(clk_name), "timer%u", pwm->hwpwm);
+
+   clk = clk_get(chip->dev, clk_name);
+   if (IS_ERR(clk))
+   return PTR_ERR(clk);
+
+   ret = clk_prepare_enable(clk);
+   if (ret) {
+   clk_put(clk);
+   return ret;
+   }
+
+   pwm_set_chip_data(pwm, clk);
 
return 0;
 }
 
 static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-   struct jz4740_pwm_chip *jz = to_jz4740(chip);
+   struct clk *clk = pwm_get_chip_data(pwm);
 
-   regmap_write(jz->map, TCU_REG_TSCR, BIT(pwm->hwpwm));
+   clk_disable_unprepare(clk);
+   clk_put(clk);
 }
 
 static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
@@ -93,16 +108,20 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct 
pwm_device *pwm,
struct pwm_state *state)
 {
struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
+   struct clk *clk = pwm_get_chip_data(pwm),
+  *parent_clk = clk_get_parent(clk);
+   unsigned long rate, period, duty;
unsigned long long tmp;
-   unsigned long period, duty;
unsigned int prescaler = 0;
 
-   tmp = (unsigned long long)clk_get_rate(jz4740->clk) * state->period;
+   rate = clk_get_rate(parent_clk);
+   tmp = (unsigned long long)rate * state->period;
do_div(tmp, 10);
period = tmp;
 
while (period > 0x && prescaler < 6) {
period >>= 2;
+   rate >>= 2;
++prescaler;
}
 
@@ -122,10 +141,7 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct 
pwm_device *pwm,
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
   TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);
 
-   /* Set clock prescale */
-   regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
-  TCU_TCSR_PRESCALE_MASK,
-  prescaler << TCU_TCSR_PRESCALE_LSB);
+   clk_set_rate(clk, rate);
 
/* Reset counter to 0 */
regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0);
@@ -171,10 +187,6 @@ static int jz4740_pwm_probe(struct platform_device *pdev)
if (!jz4740)
return -ENOMEM;
 
-   jz4740->clk = devm_clk_get(>dev, "ext");
-   if (IS_ERR(jz4740->clk))
-   return PTR_ERR(jz4740->clk);
-
jz4740->map = device_node_to_regmap(dev->parent->of_node);
if (!jz4740->map) {
dev_err(dev, "regmap not found\n");
-- 
2.21.0.593.g511ec345e18



[PATCH 4/7] pwm: jz4740: Improve algorithm of clock calculation

2019-08-09 Thread Paul Cercueil
The previous algorithm hardcoded details about how the TCU clocks work.
The new algorithm will use clk_round_rate to find the perfect clock rate
for the PWM channel.

Signed-off-by: Paul Cercueil 
Tested-by: Mathieu Malaterre 
Tested-by: Artur Rojek 
---
 drivers/pwm/pwm-jz4740.c | 60 +---
 1 file changed, 44 insertions(+), 16 deletions(-)

diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index 6ec8794d3b99..f20dc2e19240 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -110,24 +110,56 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct 
pwm_device *pwm,
struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
struct clk *clk = pwm_get_chip_data(pwm),
   *parent_clk = clk_get_parent(clk);
-   unsigned long rate, period, duty;
+   unsigned long rate, parent_rate, period, duty;
unsigned long long tmp;
-   unsigned int prescaler = 0;
+   int ret;
 
-   rate = clk_get_rate(parent_clk);
-   tmp = (unsigned long long)rate * state->period;
-   do_div(tmp, 10);
-   period = tmp;
+   parent_rate = clk_get_rate(parent_clk);
+
+   jz4740_pwm_disable(chip, pwm);
 
-   while (period > 0x && prescaler < 6) {
-   period >>= 2;
-   rate >>= 2;
-   ++prescaler;
+   /* Reset the clock to the maximum rate, and we'll reduce it if needed */
+   ret = clk_set_max_rate(clk, parent_rate);
+   if (ret) {
+   dev_err(chip->dev, "Unable to set max rate: %d\n", ret);
+   return ret;
}
 
-   if (prescaler == 6)
-   return -EINVAL;
+   ret = clk_set_rate(clk, parent_rate);
+   if (ret) {
+   dev_err(chip->dev, "Unable to reset to parent rate (%lu Hz)",
+   parent_rate);
+   return ret;
+   }
+
+   /*
+* Limit the clock to a maximum rate that still gives us a period value
+* which fits in 16 bits.
+*/
+   tmp = 0xull * NSEC_PER_SEC;
+   do_div(tmp, state->period);
 
+   ret = clk_set_max_rate(clk, tmp);
+   if (ret) {
+   dev_err(chip->dev, "Unable to set max rate: %d\n", ret);
+   return ret;
+   }
+
+   /*
+* Read back the clock rate, as it may have been modified by
+* clk_set_max_rate()
+*/
+   rate = clk_get_rate(clk);
+
+   if (rate != parent_rate)
+   dev_dbg(chip->dev, "PWM clock updated to %lu Hz\n", rate);
+
+   /* Calculate period value */
+   tmp = (unsigned long long)rate * state->period;
+   do_div(tmp, NSEC_PER_SEC);
+   period = (unsigned long)tmp;
+
+   /* Calculate duty value */
tmp = (unsigned long long)period * state->duty_cycle;
do_div(tmp, state->period);
duty = period - tmp;
@@ -135,14 +167,10 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct 
pwm_device *pwm,
if (duty >= period)
duty = period - 1;
 
-   jz4740_pwm_disable(chip, pwm);
-
/* Set abrupt shutdown */
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
   TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);
 
-   clk_set_rate(clk, rate);
-
/* Reset counter to 0 */
regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0);
 
-- 
2.21.0.593.g511ec345e18



[PATCH 1/7] pwm: jz4740: Obtain regmap from parent node

2019-08-09 Thread Paul Cercueil
The TCU registers are shared between a handful of drivers, accessing
them through the same regmap.

While this driver is devicetree-compatible, it is never (as of now)
probed from devicetree, so this change does not introduce a ABI problem
with current devicetree files.

Signed-off-by: Paul Cercueil 
Tested-by: Mathieu Malaterre 
Tested-by: Artur Rojek 
---
 drivers/pwm/Kconfig  |  1 +
 drivers/pwm/pwm-jz4740.c | 80 ++--
 2 files changed, 53 insertions(+), 28 deletions(-)

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index a7e57516959e..cc4df0ec978a 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -225,6 +225,7 @@ config PWM_IMX_TPM
 config PWM_JZ4740
tristate "Ingenic JZ47xx PWM support"
depends on MACH_INGENIC
+   select MFD_SYSCON
help
  Generic PWM framework driver for Ingenic JZ47xx based
  machines.
diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index f901e8a0d33d..7aea5e0c6e18 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -8,18 +8,20 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
 #include 
-
-#include 
+#include 
 
 #define NUM_PWM 8
 
 struct jz4740_pwm_chip {
struct pwm_chip chip;
struct clk *clk;
+   struct regmap *map;
 };
 
 static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip)
@@ -29,6 +31,8 @@ static inline struct jz4740_pwm_chip *to_jz4740(struct 
pwm_chip *chip)
 
 static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+   struct jz4740_pwm_chip *jz = to_jz4740(chip);
+
/*
 * Timers 0 and 1 are used for system tasks, so they are unavailable
 * for use as PWMs.
@@ -36,50 +40,53 @@ static int jz4740_pwm_request(struct pwm_chip *chip, struct 
pwm_device *pwm)
if (pwm->hwpwm < 2)
return -EBUSY;
 
-   jz4740_timer_start(pwm->hwpwm);
+   regmap_write(jz->map, TCU_REG_TSCR, BIT(pwm->hwpwm));
 
return 0;
 }
 
 static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-   jz4740_timer_set_ctrl(pwm->hwpwm, 0);
+   struct jz4740_pwm_chip *jz = to_jz4740(chip);
 
-   jz4740_timer_stop(pwm->hwpwm);
+   regmap_write(jz->map, TCU_REG_TSCR, BIT(pwm->hwpwm));
 }
 
 static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-   uint32_t ctrl = jz4740_timer_get_ctrl(pwm->pwm);
+   struct jz4740_pwm_chip *jz = to_jz4740(chip);
+
+   /* Enable PWM output */
+   regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
+  TCU_TCSR_PWM_EN, TCU_TCSR_PWM_EN);
 
-   ctrl |= JZ_TIMER_CTRL_PWM_ENABLE;
-   jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
-   jz4740_timer_enable(pwm->hwpwm);
+   /* Start counter */
+   regmap_write(jz->map, TCU_REG_TESR, BIT(pwm->hwpwm));
 
return 0;
 }
 
 static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-   uint32_t ctrl = jz4740_timer_get_ctrl(pwm->hwpwm);
+   struct jz4740_pwm_chip *jz = to_jz4740(chip);
 
/*
 * Set duty > period. This trick allows the TCU channels in TCU2 mode to
 * properly return to their init level.
 */
-   jz4740_timer_set_duty(pwm->hwpwm, 0x);
-   jz4740_timer_set_period(pwm->hwpwm, 0x0);
+   regmap_write(jz->map, TCU_REG_TDHRc(pwm->hwpwm), 0x);
+   regmap_write(jz->map, TCU_REG_TDFRc(pwm->hwpwm), 0x0);
 
/*
 * Disable PWM output.
 * In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the
 * counter is stopped, while in TCU1 mode the order does not matter.
 */
-   ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE;
-   jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
+   regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
+  TCU_TCSR_PWM_EN, 0);
 
/* Stop counter */
-   jz4740_timer_disable(pwm->hwpwm);
+   regmap_write(jz->map, TCU_REG_TECR, BIT(pwm->hwpwm));
 }
 
 static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -89,7 +96,6 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct 
pwm_device *pwm,
unsigned long long tmp;
unsigned long period, duty;
unsigned int prescaler = 0;
-   uint16_t ctrl;
 
tmp = (unsigned long long)clk_get_rate(jz4740->clk) * state->period;
do_div(tmp, 10);
@@ -112,26 +118,37 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct 
pwm_device *pwm,
 
jz4740_pwm_disable(chip, pwm);
 
-   jz4740_timer_set_count(pwm->hwpwm, 0);
-   jz4740_timer_set_duty(pwm->hwpwm, duty);
-   jz4740_timer_set_period(pwm->hwpwm, period);
+   /* Set abrupt shutdown */
+   regmap_update_bits(jz4740->map

[PATCH 0/7] pwm: jz4740: Driver update

2019-08-09 Thread Paul Cercueil
Hi,

Patches 1-5 come from a bigger patchset that was cut in smaller
pieces for easier integration to mainline.
(The patchset was https://lkml.org/lkml/2019/3/27/1837)

These patches are the exact same as the ones found in the patchset
shown above, with the exception of patch [1/7] which now uses
device_node_to_regmap(). This function was added in a prior patch,
now merged in the MIPS tree.

For that reason this patchset is based on the ingenic-tcu-v5.4 branch of
the MIPS tree
(git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux.git).

Thanks,
-Paul




[PATCH 1/3] watchdog: jz4740: Use WDT clock provided by TCU driver

2019-08-09 Thread Paul Cercueil
Instead of requesting the "ext" clock and handling the watchdog clock
divider and gating in the watchdog driver, we now request and use the
"wdt" clock that is supplied by the ingenic-timer "TCU" driver.

The major benefit is that the watchdog's clock rate and parent can now
be specified from within devicetree, instead of hardcoded in the driver.

Also, this driver won't poke anymore into the TCU registers to
enable/disable the clock, as this is now handled by the TCU driver.

On the bad side, we break the ABI with devicetree - as we now request a
different clock. In this very specific case it is still okay, as every
Ingenic JZ47xx-based board out there compile the devicetree within the
kernel; so it's still time to push breaking changes, in order to get a
clean devicetree that won't break once it musn't.

Signed-off-by: Paul Cercueil 
Reviewed-by: Guenter Roeck 
Tested-by: Mathieu Malaterre 
Tested-by: Artur Rojek 
---
 drivers/watchdog/Kconfig  |  1 +
 drivers/watchdog/jz4740_wdt.c | 75 ++-
 2 files changed, 31 insertions(+), 45 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 8188963a405b..820d8a472310 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1645,6 +1645,7 @@ config INDYDOG
 config JZ4740_WDT
tristate "Ingenic jz4740 SoC hardware watchdog"
depends on MACH_JZ4740 || MACH_JZ4780
+   depends on COMMON_CLK
select WATCHDOG_CORE
help
  Hardware driver for the built-in watchdog timer on Ingenic jz4740 
SoCs.
diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
index d4a90916dd38..07fbd9d96e84 100644
--- a/drivers/watchdog/jz4740_wdt.c
+++ b/drivers/watchdog/jz4740_wdt.c
@@ -18,19 +18,6 @@
 #include 
 #include 
 
-#include 
-
-#define JZ_WDT_CLOCK_PCLK 0x1
-#define JZ_WDT_CLOCK_RTC  0x2
-#define JZ_WDT_CLOCK_EXT  0x4
-
-#define JZ_WDT_CLOCK_DIV_1(0 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_4(1 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_16   (2 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_64   (3 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_256  (4 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_1024 (5 << TCU_TCSR_PRESCALE_LSB)
-
 #define DEFAULT_HEARTBEAT 5
 #define MAX_HEARTBEAT 2048
 
@@ -50,7 +37,8 @@ MODULE_PARM_DESC(heartbeat,
 struct jz4740_wdt_drvdata {
struct watchdog_device wdt;
void __iomem *base;
-   struct clk *rtc_clk;
+   struct clk *clk;
+   unsigned long clk_rate;
 };
 
 static int jz4740_wdt_ping(struct watchdog_device *wdt_dev)
@@ -65,32 +53,14 @@ static int jz4740_wdt_set_timeout(struct watchdog_device 
*wdt_dev,
unsigned int new_timeout)
 {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
-   unsigned int rtc_clk_rate;
-   unsigned int timeout_value;
-   unsigned short clock_div = JZ_WDT_CLOCK_DIV_1;
+   u16 timeout_value = (u16)(drvdata->clk_rate * new_timeout);
u8 tcer;
 
-   rtc_clk_rate = clk_get_rate(drvdata->rtc_clk);
-
-   timeout_value = rtc_clk_rate * new_timeout;
-   while (timeout_value > 0x) {
-   if (clock_div == JZ_WDT_CLOCK_DIV_1024) {
-   /* Requested timeout too high;
-   * use highest possible value. */
-   timeout_value = 0x;
-   break;
-   }
-   timeout_value >>= 2;
-   clock_div += (1 << TCU_TCSR_PRESCALE_LSB);
-   }
-
tcer = readb(drvdata->base + TCU_REG_WDT_TCER);
writeb(0x0, drvdata->base + TCU_REG_WDT_TCER);
-   writew(clock_div, drvdata->base + TCU_REG_WDT_TCSR);
 
writew((u16)timeout_value, drvdata->base + TCU_REG_WDT_TDR);
writew(0x0, drvdata->base + TCU_REG_WDT_TCNT);
-   writew(clock_div | JZ_WDT_CLOCK_RTC, drvdata->base + TCU_REG_WDT_TCSR);
 
if (tcer & TCU_WDT_TCER_TCEN)
writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER);
@@ -102,11 +72,15 @@ static int jz4740_wdt_set_timeout(struct watchdog_device 
*wdt_dev,
 static int jz4740_wdt_start(struct watchdog_device *wdt_dev)
 {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
+   int ret;
u8 tcer;
 
+   ret = clk_prepare_enable(drvdata->clk);
+   if (ret)
+   return ret;
+
tcer = readb(drvdata->base + TCU_REG_WDT_TCER);
 
-   jz4740_timer_enable_watchdog();
jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
 
/* Start watchdog if it wasn't started already */
@@ -121,7 +95,7 @@ static int jz4740_wdt_stop(struct watchdog_device *wdt_dev)
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
 
writeb(0x0, drvdata-

[PATCH 3/3] watchdog: jz4740: Drop dependency on MACH_JZ47xx

2019-08-09 Thread Paul Cercueil
Depending on MACH_JZ47xx prevent us from creating a generic kernel that
works on more than one MIPS board. Instead, we just depend on MIPS being
set.

Signed-off-by: Paul Cercueil 
Reviewed-by: Guenter Roeck 
---
 drivers/watchdog/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 78411609048b..90e689cbeed3 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1644,7 +1644,7 @@ config INDYDOG
 
 config JZ4740_WDT
tristate "Ingenic jz4740 SoC hardware watchdog"
-   depends on MACH_JZ4740 || MACH_JZ4780
+   depends on MIPS
depends on COMMON_CLK
select WATCHDOG_CORE
select MFD_SYSCON
-- 
2.21.0.593.g511ec345e18



[PATCH 2/3] watchdog: jz4740: Use regmap provided by TCU driver

2019-08-09 Thread Paul Cercueil
Since we broke the ABI by changing the clock, the driver was also
updated to use the regmap provided by the TCU driver.

Signed-off-by: Paul Cercueil 
Reviewed-by: Guenter Roeck 
Tested-by: Mathieu Malaterre 
Tested-by: Artur Rojek 
---
 drivers/watchdog/Kconfig  |  1 +
 drivers/watchdog/jz4740_wdt.c | 36 +++
 2 files changed, 21 insertions(+), 16 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 820d8a472310..78411609048b 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1647,6 +1647,7 @@ config JZ4740_WDT
depends on MACH_JZ4740 || MACH_JZ4780
depends on COMMON_CLK
select WATCHDOG_CORE
+   select MFD_SYSCON
help
  Hardware driver for the built-in watchdog timer on Ingenic jz4740 
SoCs.
 
diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
index 07fbd9d96e84..bdf9564efa29 100644
--- a/drivers/watchdog/jz4740_wdt.c
+++ b/drivers/watchdog/jz4740_wdt.c
@@ -5,6 +5,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -17,6 +18,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #define DEFAULT_HEARTBEAT 5
 #define MAX_HEARTBEAT 2048
@@ -36,7 +38,7 @@ MODULE_PARM_DESC(heartbeat,
 
 struct jz4740_wdt_drvdata {
struct watchdog_device wdt;
-   void __iomem *base;
+   struct regmap *map;
struct clk *clk;
unsigned long clk_rate;
 };
@@ -45,7 +47,8 @@ static int jz4740_wdt_ping(struct watchdog_device *wdt_dev)
 {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
 
-   writew(0x0, drvdata->base + TCU_REG_WDT_TCNT);
+   regmap_write(drvdata->map, TCU_REG_WDT_TCNT, 0);
+
return 0;
 }
 
@@ -54,16 +57,16 @@ static int jz4740_wdt_set_timeout(struct watchdog_device 
*wdt_dev,
 {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
u16 timeout_value = (u16)(drvdata->clk_rate * new_timeout);
-   u8 tcer;
+   unsigned int tcer;
 
-   tcer = readb(drvdata->base + TCU_REG_WDT_TCER);
-   writeb(0x0, drvdata->base + TCU_REG_WDT_TCER);
+   regmap_read(drvdata->map, TCU_REG_WDT_TCER, );
+   regmap_write(drvdata->map, TCU_REG_WDT_TCER, 0);
 
-   writew((u16)timeout_value, drvdata->base + TCU_REG_WDT_TDR);
-   writew(0x0, drvdata->base + TCU_REG_WDT_TCNT);
+   regmap_write(drvdata->map, TCU_REG_WDT_TDR, timeout_value);
+   regmap_write(drvdata->map, TCU_REG_WDT_TCNT, 0);
 
if (tcer & TCU_WDT_TCER_TCEN)
-   writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER);
+   regmap_write(drvdata->map, TCU_REG_WDT_TCER, TCU_WDT_TCER_TCEN);
 
wdt_dev->timeout = new_timeout;
return 0;
@@ -72,20 +75,20 @@ static int jz4740_wdt_set_timeout(struct watchdog_device 
*wdt_dev,
 static int jz4740_wdt_start(struct watchdog_device *wdt_dev)
 {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
+   unsigned int tcer;
int ret;
-   u8 tcer;
 
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
 
-   tcer = readb(drvdata->base + TCU_REG_WDT_TCER);
+   regmap_read(drvdata->map, TCU_REG_WDT_TCER, );
 
jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
 
/* Start watchdog if it wasn't started already */
if (!(tcer & TCU_WDT_TCER_TCEN))
-   writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER);
+   regmap_write(drvdata->map, TCU_REG_WDT_TCER, TCU_WDT_TCER_TCEN);
 
return 0;
 }
@@ -94,7 +97,7 @@ static int jz4740_wdt_stop(struct watchdog_device *wdt_dev)
 {
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
 
-   writeb(0x0, drvdata->base + TCU_REG_WDT_TCER);
+   regmap_write(drvdata->map, TCU_REG_WDT_TCER, 0);
clk_disable_unprepare(drvdata->clk);
 
return 0;
@@ -136,7 +139,6 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
struct device *dev = >dev;
struct jz4740_wdt_drvdata *drvdata;
struct watchdog_device *jz4740_wdt;
-   struct resource *res;
long rate;
int ret;
 
@@ -173,9 +175,11 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
watchdog_set_nowayout(jz4740_wdt, nowayout);
watchdog_set_drvdata(jz4740_wdt, drvdata);
 
-   drvdata->base = devm_platform_ioremap_resource(pdev, 0);
-   if (IS_ERR(drvdata->base))
-   return PTR_ERR(drvdata->base);
+   drvdata->map = device_node_to_regmap(dev->parent->of_node);
+   if (!drvdata->map) {
+   dev_err(dev, "regmap not found\n");
+   return -EINVAL;
+   }
 
return devm_watchdog_register_device(dev, >wdt);
 }
-- 
2.21.0.593.g511ec345e18



<    2   3   4   5   6   7   8   9   10   11   >