Re: [PATCH 2/2] riscv: Fix text patching when IPI are used

2024-02-28 Thread Samuel Holland
Hi Alex,

On 2024-02-28 11:51 AM, Alexandre Ghiti wrote:
> For now, we use stop_machine() to patch the text and when we use IPIs for
> remote icache flushes (which is emitted in patch_text_nosync()), the system
> hangs.
> 
> So instead, make sure every cpu executes the stop_machine() patching
> function and emit a local icache flush there.
> 
> Co-developed-by: Björn Töpel 
> Signed-off-by: Björn Töpel 
> Signed-off-by: Alexandre Ghiti 
> ---
>  arch/riscv/include/asm/patch.h |  1 +
>  arch/riscv/kernel/ftrace.c | 42 ++
>  arch/riscv/kernel/patch.c  | 18 +--
>  3 files changed, 50 insertions(+), 11 deletions(-)
> 
> diff --git a/arch/riscv/include/asm/patch.h b/arch/riscv/include/asm/patch.h
> index e88b52d39eac..9f5d6e14c405 100644
> --- a/arch/riscv/include/asm/patch.h
> +++ b/arch/riscv/include/asm/patch.h
> @@ -6,6 +6,7 @@
>  #ifndef _ASM_RISCV_PATCH_H
>  #define _ASM_RISCV_PATCH_H
>  
> +int patch_insn_write(void *addr, const void *insn, size_t len);
>  int patch_text_nosync(void *addr, const void *insns, size_t len);
>  int patch_text_set_nosync(void *addr, u8 c, size_t len);
>  int patch_text(void *addr, u32 *insns, int ninsns);
> diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
> index f5aa24d9e1c1..5654966c4e7d 100644
> --- a/arch/riscv/kernel/ftrace.c
> +++ b/arch/riscv/kernel/ftrace.c
> @@ -8,6 +8,7 @@
>  #include 
>  #include 
>  #include 
> +#include 
>  #include 
>  #include 
>  
> @@ -75,8 +76,7 @@ static int __ftrace_modify_call(unsigned long hook_pos, 
> unsigned long target,
>   make_call_t0(hook_pos, target, call);
>  
>   /* Replace the auipc-jalr pair at once. Return -EPERM on write error. */
> - if (patch_text_nosync
> - ((void *)hook_pos, enable ? call : nops, MCOUNT_INSN_SIZE))
> + if (patch_insn_write((void *)hook_pos, enable ? call : nops, 
> MCOUNT_INSN_SIZE))
>   return -EPERM;
>  
>   return 0;
> @@ -88,7 +88,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long 
> addr)
>  
>   make_call_t0(rec->ip, addr, call);
>  
> - if (patch_text_nosync((void *)rec->ip, call, MCOUNT_INSN_SIZE))
> + if (patch_insn_write((void *)rec->ip, call, MCOUNT_INSN_SIZE))
>   return -EPERM;
>  
>   return 0;
> @@ -99,7 +99,7 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace 
> *rec,
>  {
>   unsigned int nops[2] = {NOP4, NOP4};
>  
> - if (patch_text_nosync((void *)rec->ip, nops, MCOUNT_INSN_SIZE))
> + if (patch_insn_write((void *)rec->ip, nops, MCOUNT_INSN_SIZE))
>   return -EPERM;
>  
>   return 0;
> @@ -134,6 +134,40 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
>  
>   return ret;
>  }
> +
> +struct ftrace_modify_param {
> + int command;
> + atomic_t cpu_count;
> +};
> +
> +static int __ftrace_modify_code(void *data)
> +{
> + struct ftrace_modify_param *param = data;
> +
> + if (atomic_inc_return(>cpu_count) == num_online_cpus()) {
> + ftrace_modify_all_code(param->command);
> + /*
> +  * Make sure the patching store is effective *before* we
> +  * increment the counter which releases all waiting cpus
> +  * by using the release version of atomic increment.
> +  */
> + atomic_inc_return_release(>cpu_count);
> + } else {
> + while (atomic_read(>cpu_count) <= num_online_cpus())
> + cpu_relax();
> + }
> +
> + local_flush_icache_all();
> +
> + return 0;
> +}
> +
> +void arch_ftrace_update_code(int command)
> +{
> + struct ftrace_modify_param param = { command, ATOMIC_INIT(0) };
> +
> + stop_machine(__ftrace_modify_code, , cpu_online_mask);
> +}
>  #endif
>  
>  #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
> diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c
> index 0b5c16dfe3f4..82d8508c765b 100644
> --- a/arch/riscv/kernel/patch.c
> +++ b/arch/riscv/kernel/patch.c
> @@ -188,7 +188,7 @@ int patch_text_set_nosync(void *addr, u8 c, size_t len)
>  }
>  NOKPROBE_SYMBOL(patch_text_set_nosync);
>  
> -static int patch_insn_write(void *addr, const void *insn, size_t len)
> +int patch_insn_write(void *addr, const void *insn, size_t len)
>  {
>   size_t patched = 0;
>   size_t size;
> @@ -211,11 +211,9 @@ NOKPROBE_SYMBOL(patch_insn_write);
>  
>  int patch_text_nosync(void *addr, const void *insns, size_t len)
>  {
> - u32 *tp = addr;
>   int ret;
>  
> - ret = patch_insn_write(tp, insns, len);
> -
> + ret = patch_insn_write(addr, insns, len);
>   if (!ret)
>   flush_icache_range((uintptr_t) tp, (uintptr_t) tp + len);

This only happens to compile because flush_icache_range() is a macro that
ignores its parameters. You could replace tp with addr in this line as well, but
that seems like more of a cosmetic change and should be a separate patch (like
in [1] which covers both related functions) if you 

[PATCH 2/2] arm64: dts: allwinner: Enforce consistent MMC numbering

2021-04-18 Thread Samuel Holland
Traditionally, the sunxi-mmc device numbers matched the register address
order. However, that was broken by asynchronous probe, and now the MMC
device numbers are not deterministic. Add aliases to keep the device
numbers consistent between boots. Use the traditional order, since there
is no need to change it.

Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 6 ++
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi  | 6 ++
 2 files changed, 12 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 9b58cfbefa6d..3df70a41b3b9 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -17,6 +17,12 @@ / {
#address-cells = <1>;
#size-cells = <1>;
 
+   aliases {
+   mmc0 = 
+   mmc1 = 
+   mmc2 = 
+   };
+
chosen {
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
index d8ebc1a84af9..4bdc48caf68a 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -16,6 +16,12 @@ / {
#address-cells = <1>;
#size-cells = <1>;
 
+   aliases {
+   mmc0 = 
+   mmc1 = 
+   mmc2 = 
+   };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
-- 
2.26.3



[PATCH 1/2] ARM: dts: sunxi: h3/h5: Enforce consistent MMC numbering

2021-04-18 Thread Samuel Holland
Traditionally, the sunxi-mmc device numbers matched the register address
order. However, that was broken by asynchronous probe, and now the MMC
device numbers are not deterministic. Add aliases to keep the device
numbers consistent between boots. Use the traditional order, since there
is no need to change it.

Signed-off-by: Samuel Holland 
---
 arch/arm/boot/dts/sunxi-h3-h5.dtsi | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi 
b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
index e27399aa052c..1cb669c835bd 100644
--- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
+++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
@@ -53,6 +53,12 @@ / {
#address-cells = <1>;
#size-cells = <1>;
 
+   aliases {
+   mmc0 = 
+   mmc1 = 
+   mmc2 = 
+   };
+
chosen {
#address-cells = <1>;
#size-cells = <1>;
-- 
2.26.3



[PATCH 0/2] sunxi: Enforce consistent MMC numbering

2021-04-18 Thread Samuel Holland
Dealing with the inconsistent numbering has been a major pain, and
there is a solution with (as far as I can tell) no tangible downsides.
So let's use it.

Yes, I know the kernel supports UUIDs for root=. But UUIDs do not help
when referencing the whole, unpartitioned device, like is needed for
updating the bootloader and firmware. So for the use case of "write a
bootloader to the SD card, regardless of where the board is currently
booted from", I know of two options:
  - Dig around in sysfs to find the mmc number from the MMIO address,
which means I have to know the MMIO addresses for every SoC, or
  - Apply patches like these.

Samuel Holland (2):
  ARM: dts: sunxi: h3/h5: Enforce consistent MMC numbering
  arm64: dts: allwinner: Enforce consistent MMC numbering

 arch/arm/boot/dts/sunxi-h3-h5.dtsi| 6 ++
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 6 ++
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi  | 6 ++
 3 files changed, 18 insertions(+)

-- 
2.26.3



[PATCH] nvmem: sunxi_sid: Set type to OTP

2021-04-18 Thread Samuel Holland
This device currently reports an "Unknown" type in sysfs.
Since it is an eFuse hardware device, set its type to OTP.

Signed-off-by: Samuel Holland 
---
 drivers/nvmem/sunxi_sid.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c
index e26ef1bbf198..275b9155e473 100644
--- a/drivers/nvmem/sunxi_sid.c
+++ b/drivers/nvmem/sunxi_sid.c
@@ -142,6 +142,7 @@ static int sunxi_sid_probe(struct platform_device *pdev)
 
nvmem_cfg->dev = dev;
nvmem_cfg->name = "sunxi-sid";
+   nvmem_cfg->type = NVMEM_TYPE_OTP;
nvmem_cfg->read_only = true;
nvmem_cfg->size = cfg->size;
nvmem_cfg->word_size = 1;
-- 
2.26.3



[PATCH] rtc: sun6i: Add NVMEM provider

2021-04-18 Thread Samuel Holland
The sun6i RTC provides 32 bytes of general-purpose data registers.
They can be used to save data in the always-on RTC power domain.
The registers are writable via 32-bit MMIO accesses only.

Expose the region as a NVMEM provider so it can be used by userspace and
other drivers.

Signed-off-by: Samuel Holland 
---
 drivers/rtc/rtc-sun6i.c | 42 +
 1 file changed, 42 insertions(+)

diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index e75020ab8024..f4a5e7465148 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -69,6 +69,10 @@
 #define SUN6I_LOSC_OUT_GATING  0x0060
 #define SUN6I_LOSC_OUT_GATING_EN_OFFSET0
 
+/* General-purpose data */
+#define SUN6I_GP_DATA  0x0100
+#define SUN6I_GP_DATA_SIZE 0x20
+
 /*
  * Get date values
  */
@@ -641,6 +645,39 @@ static const struct rtc_class_ops sun6i_rtc_ops = {
.alarm_irq_enable   = sun6i_rtc_alarm_irq_enable
 };
 
+static int sun6i_rtc_nvmem_read(void *priv, unsigned int offset, void *_val, 
size_t bytes)
+{
+   struct sun6i_rtc_dev *chip = priv;
+   u32 *val = _val;
+   int i;
+
+   for (i = 0; i < bytes / 4; ++i)
+   val[i] = readl(chip->base + SUN6I_GP_DATA + offset + 4 * i);
+
+   return 0;
+}
+
+static int sun6i_rtc_nvmem_write(void *priv, unsigned int offset, void *_val, 
size_t bytes)
+{
+   struct sun6i_rtc_dev *chip = priv;
+   u32 *val = _val;
+   int i;
+
+   for (i = 0; i < bytes / 4; ++i)
+   writel(val[i], chip->base + SUN6I_GP_DATA + offset + 4 * i);
+
+   return 0;
+}
+
+static struct nvmem_config sun6i_rtc_nvmem_cfg = {
+   .type   = NVMEM_TYPE_BATTERY_BACKED,
+   .reg_read   = sun6i_rtc_nvmem_read,
+   .reg_write  = sun6i_rtc_nvmem_write,
+   .size   = SUN6I_GP_DATA_SIZE,
+   .word_size  = 4,
+   .stride = 4,
+};
+
 /* Enable IRQ wake on suspend, to wake up from RTC. */
 static int sun6i_rtc_suspend(struct device *dev)
 {
@@ -728,6 +765,11 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
if (ret)
return ret;
 
+   sun6i_rtc_nvmem_cfg.priv = chip;
+   ret = devm_rtc_nvmem_register(chip->rtc, _rtc_nvmem_cfg);
+   if (ret)
+   return ret;
+
dev_info(>dev, "RTC enabled\n");
 
return 0;
-- 
2.26.3



Re: [PATCH] debugfs: Fix use-after-free in debugfs_create_devm_seqfile()

2021-04-04 Thread Samuel Holland
On 4/4/21 5:08 AM, Greg Kroah-Hartman wrote:
> On Sat, Apr 03, 2021 at 07:45:04PM -0500, Samuel Holland wrote:
>> This function uses devres to clean up its allocation, but it never removes 
>> the
>> file referencing that allocation. This causes a use-after-free and an oops if
>> the file is accessed after the owning device is removed.
> 
> What in-kernel user of this is having this problem?
> 
> The driver should clean up the debugfs file, it is not the debugfs
> core's job to auto-remove the file.

The function returns void. debugfs_remove() requires the dentry pointer,
which the caller does not have. How is the driver expected to clean up
the file?

Do you expect the driver to remove the file as a side effect of
recursively removing its parent? If so, that conflicts with the
documentation for debugfs_create_devm_seqfile(), which describes NULL as
a valid parent:

@parent: a pointer to the parent dentry for this file.  This should be a
 directory dentry if set.  If this parameter is %NULL, then the
 file will be created in the root of the debugfs filesystem.

There is one in-kernel caller that uses a NULL parent, in
drivers/gpio/gpio-tegra.c

> The resource is what is being cleaned up by the devm usage in debugfs,
> that's all, not the file.
> 
> Please fix up the driver that is creating the file but then not removing
> it.

In that case, the function documentation should be modified to state
that the driver is responsible for removing the parent directory, and
that NULL is not a valid parent here. I can send a patch doing that instead.

Samuel


[PATCH] debugfs: Fix use-after-free in debugfs_create_devm_seqfile()

2021-04-03 Thread Samuel Holland
This function uses devres to clean up its allocation, but it never removes the
file referencing that allocation. This causes a use-after-free and an oops if
the file is accessed after the owning device is removed.

Fixes: 98210b7f73f1d ("debugfs: add helper function to create device related 
seq_file")
Fixes: 0d519cbf38eed ("debugfs: remove return value of 
debugfs_create_devm_seqfile()")
Signed-off-by: Samuel Holland 
---
 fs/debugfs/file.c | 19 ---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index 686e0ad28788..64f1f918e119 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -1100,6 +1100,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_regset32);
 struct debugfs_devm_entry {
int (*read)(struct seq_file *seq, void *data);
struct device *dev;
+   struct dentry *dentry;
 };
 
 static int debugfs_devm_entry_open(struct inode *inode, struct file *f)
@@ -1117,6 +1118,13 @@ static const struct file_operations 
debugfs_devm_entry_ops = {
.llseek = seq_lseek
 };
 
+static void debugfs_devm_entry_release(struct device *dev, void *res)
+{
+   struct debugfs_devm_entry *entry = res;
+
+   debugfs_remove(entry->dentry);
+}
+
 /**
  * debugfs_create_devm_seqfile - create a debugfs file that is bound to device.
  *
@@ -1136,14 +1144,19 @@ void debugfs_create_devm_seqfile(struct device *dev, 
const char *name,
if (IS_ERR(parent))
return;
 
-   entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL);
+   entry = devres_alloc(debugfs_devm_entry_release, sizeof(*entry), 
GFP_KERNEL);
if (!entry)
return;
 
entry->read = read_fn;
entry->dev = dev;
+   entry->dentry = debugfs_create_file(name, S_IRUGO, parent, entry,
+   _devm_entry_ops);
+   if (IS_ERR(entry->dentry)) {
+   devres_free(entry);
+   return;
+   }
 
-   debugfs_create_file(name, S_IRUGO, parent, entry,
-   _devm_entry_ops);
+   devres_add(dev, entry);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_devm_seqfile);
-- 
2.26.2



Re: [RFC PATCH] arm64: dts: allwinner: a64/h5: Add CPU idle states

2021-03-23 Thread Samuel Holland
On 3/22/21 8:56 PM, Andre Przywara wrote:
>> I'm sending this patch as an RFC because it raises questions about how
>> we handle firmware versioning. How far back does (or should) our support
>> for old TF-A and Crust versions go?
>>
>> cpuidle has a problem that without working firmware support, CPUs will
>> enter idle states and be unable to wake up. As a result, the system will
>> hang at some point during boot, usually before getting to userspace.
>>
>> For over a year[0], TF-A has exposed the PSCI CPU_SUSPEND function when
>> a SCPI implementation is present[1]. Implementing CPU_SUSPEND is
>> required for implementing SYSTEM_SUSPEND[2], even if CPU_SUSPEND is not
>> itself used for anything. 
>>
>> However, there was no code to actually wake up a CPU once it called the
>> CPU_SUSPEND function, because I could not find the register providing
>> the necessary information. The fact that CPU_SUSPEND was broken affected
>> nobody, because nothing ever called it -- there were no idle states in
>> the DTS. In hindsight, what I should have done was always return failure
>> from sunxi_validate_power_state(), but that ship has long sailed.
>>
>> I finally found the elusive register and implemented the wakeup code
>> earlier this month[3]. So now, CPU_SUSPEND actually works, if all of
>> your firmware is up to date, and cpuidle works if you add the states in
>> your device tree.
>>
>> Unfortunately, there is currently nothing verifying that compatibility.
>> So you can get into four possible scenarios:
>>   1) No idle states in DTS, any firmware => Linux works, with baseline
>>  power consumption.
>>   2) Idle states added to DTS, no Crust/SCPI => Linux works, but every
>>  attempt to enter an idle state is rejected because CPU_SUSPEND is
>>  not hooked up. So power consumption increases by a sizable amount.
>>   3) Idle states added to DTS, "old" Crust/SCPI (before [3]) => Linux
>>  fails to boot, because CPUs never return from idle states.
>>   4) Idle states added to DTS, "new" Crust/SCPI (after [3]) => Linux
>>  works, with improved power consumption compared to the baseline.
>>
>> Obviously, we want to prevent scenario 3 if possible.
> 
> So I think the core of the problem is that the DT describes some
> firmware feature, but we have the DT bundled with the kernel, not the
> firmware.

I would say the core problem is that the firmware lies about supporting
PSCI CPU_SUSPEND. Linux shouldn't be calling CPU_SUSPEND if the firmware
declares it as unavailable, regardless of what is in the DTS.
(Technically, per the PSCI standard, CPU_SUSPEND is a mandatory
function, but a quick survey of the TF-A platforms shows it is far from
universally implemented.)

> So is there any way we can detect an older crust version in U-Boot,
> then remove any potential idle states from the DT?

Let's assume that we are using a functioning SoC (H3) or the secure fuse
is blown (A64) and therefore U-Boot cannot access SRAM A2. I can think
of three ways it can learn about crust:

a) PSCI_FEATURES (e.g. is CPU_SUSPEND supported)
b) Metadata in the FIT image
c) Custom SMCs

TF-A has some additional methods available:

d) The SCPI-reported firmware version
e) The magic number at the beginning of the firmware binary

> Granted, this requires recent U-Boot as well, but at least we could try
> to mitigate the worst case a bit?

If we're okay with modifying firmware to solve this problem, then I
propose the following solution:

1) Version bump crust or change its magic number.
2) Modify TF-A to only report CPU_SUSPEND as available if it detects the
   new crust version. This would involve conditionally setting
   sunxi_scpi_psci_ops.validate_power_state, and updating psci_setup.c
   to also check for .validate_power_state when setting psci_caps.
3) Modify the Linux PSCI client to respect PSCI_FEATURES when setting
   psci_ops.cpu_suspend. cpuidle-psci checks for this function before
   setting up idle states.
4) Finally, after some time, add the idle states to the DTS.

In fact, this solution solves both scenarios 2 and 3, because it also
takes care of the native PM implementation, which doesn't implement
CPU_SUSPEND at all.

Does that sound workable?

Regards,
Samuel

> A better solution could be to only *add* the idle states if the rest of
> the firmware is deemed worthy. So the mainline DTs would not carry the
> properties in the first place, and only U-Boot adds them, on detecting
> a capable firmware?
> Admittedly this changes the "flow" of the DT, where the kernel is the
> authority, but it might help to solve this problem?
> 
> Or any other way, which involves U-Boot patching the DTB? (This would
> apply to the DTB passed to the kernel, regardless of where and when
> it's loaded from)
> 
> Any opinions?
> 
> Cheers,
> Andre
> 
>> Enter the current patch: I chose the arm,psci-suspend-param values
>> specifically so they would be _rejected_ by the current TF-A code. This
>> makes scenario 3 behave like scenario 2. I 

Re: [PATCH v2 0/5] arm64: sunxi: Enable the sun4i timer

2021-03-23 Thread Samuel Holland
On 3/22/21 9:18 AM, Daniel Lezcano wrote:
> On 22/03/2021 05:47, Samuel Holland wrote:
>> In preparation for adding CPU idle states, hook up the sun4i timer.
>> Having a non-c3stop clockevent source available is necessary for all
>> CPUs to simultaneously enter a local-timer-stop idle state.
> 
> Why simultaneously ?
Because the CPU providing (the hrtimer providing) the broadcast timer
cannot enter an idle state which would stop that timer. So in my case,
with 4 CPUs, I was seeing at most 3 CPUs enter idle at any given time.
This prevented any cluster-level idle states from doing anything. After
applying this series, I was able to observe the whole cluster powering
down when appropriate.

Regards,
Samuel

>> Changes from v1:
>>   - Removed H616 changes (depends on an unmerged patch set)
>>   - Reworded the patch 4-5 commit messages for clarity
>>   - Added Acked-by tags
>>
>> Samuel Holland (5):
>>   dt-bindings: timer: Simplify conditional expressions
>>   dt-bindings: timer: Add compatibles for sun50i timers
>>   arm64: dts: allwinner: a64: Sort watchdog node
>>   arm64: dts: allwinner: Add sun4i MMIO timer nodes
>>   arm64: sunxi: Build the sun4i timer driver
>>
>>  .../timer/allwinner,sun4i-a10-timer.yaml  | 42 +--
>>  arch/arm64/Kconfig.platforms  |  1 +
>>  arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 25 +++
>>  arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi  |  9 
>>  4 files changed, 46 insertions(+), 31 deletions(-)
>>
> 
> 



Re: [PATCH v4 2/4] drm: sun4i: dsi: Add bridge support

2021-03-23 Thread Samuel Holland
On 3/22/21 9:01 AM, Jagan Teki wrote:
> Some display panels would come up with a non-DSI output which
> can have an option to connect DSI interface by means of bridge
> converter.
> 
> This DSI to non-DSI bridge converter would require a bridge
> driver that would communicate the DSI controller for bridge
> functionalities.
> 
> So, add support for bridge functionalities in Allwinner DSI
> controller.
> 
> Cc: Samuel Holland 
> Signed-off-by: Jagan Teki 
> ---
> Note: 
> Samuel Holland, The existing kms hotplug dropped in order to 
> attach the bridge properly. 
> 
> However, I did try several ways to support hotplug with the 
> bridge but it's resulting in a deadlock where bind never attach 
> bridge until bridge pointer found and bridge pointer cannot 
> found until bind finishes. Any inputs on this would be appreciated.

The intended behavior is that sun6i_dsi_bind() is independent of any DSI
device. And sun6i_dsi_attach() must only be called after bind completes
and the DRM device is registered. This design allows the rest of the
display engine (such as the HDMI output) to work even if no panel is
listed in the device tree, or if a panel driver is missing.

> Changes for v4:
> - none
> Changes for v3:
> - updated with new API's 
> 
>  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 34 +-
>  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h |  2 +-
>  2 files changed, 23 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c 
> b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> index 2e9e7b2d4145..39321299dc27 100644
> --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> @@ -773,6 +773,9 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder 
> *encoder)
>   if (dsi->panel)
>   drm_panel_prepare(dsi->panel);
>  
> + if (dsi->panel_bridge)
> + dsi->panel_bridge->funcs->pre_enable(dsi->panel_bridge);
> +
>   /*
>* FIXME: This should be moved after the switch to HS mode.
>*
> @@ -788,6 +791,9 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder 
> *encoder)
>   if (dsi->panel)
>   drm_panel_enable(dsi->panel);
>  
> + if (dsi->panel_bridge)
> + dsi->panel_bridge->funcs->enable(dsi->panel_bridge);
> +
>   sun6i_dsi_start(dsi, DSI_START_HSC);
>  
>   udelay(1000);
> @@ -804,6 +810,9 @@ static void sun6i_dsi_encoder_disable(struct drm_encoder 
> *encoder)
>   if (dsi->panel) {
>   drm_panel_disable(dsi->panel);
>   drm_panel_unprepare(dsi->panel);
> + } else if (dsi->panel_bridge) {
> + dsi->panel_bridge->funcs->disable(dsi->panel_bridge);
> + dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge);
>   }
>  
>   phy_power_off(dsi->dphy);
> @@ -964,23 +973,17 @@ static int sun6i_dsi_attach(struct mipi_dsi_host *host,
>   struct mipi_dsi_device *device)
>  {
>   struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);
> - struct drm_panel *panel;
>   int ret;
>  
>   ret = drm_of_find_panel_or_bridge(dsi->dev->of_node, 0, 0,
> -   , NULL);
> +   >panel, >panel_bridge);
>   if (ret)
>   return ret;
>  
> - if (!dsi->drm || !dsi->drm->registered)
> - return -EPROBE_DEFER;
> -
> - dsi->panel = panel;
>   dsi->device = device;
>  
> - drm_kms_helper_hotplug_event(dsi->drm);
> -
> - dev_info(host->dev, "Attached device %s\n", device->name);
> + dev_info(host->dev, "Attached %s %s\n",
> +  device->name, dsi->panel ? "panel" : "bridge");
>  
>   return 0;
>  }
> @@ -991,9 +994,10 @@ static int sun6i_dsi_detach(struct mipi_dsi_host *host,
>   struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);
>  
>   dsi->panel = NULL;
> + dsi->panel_bridge = NULL;
>   dsi->device = NULL;
>  
> - drm_kms_helper_hotplug_event(dsi->drm);
> + drm_of_panel_bridge_remove(dsi->dev->of_node, 0, 0);
>  
>   return 0;
>  }
> @@ -1082,7 +1086,13 @@ static int sun6i_dsi_bind(struct device *dev, struct 
> device *master,
>  
>   drm_connector_attach_encoder(>connector, >encoder);
>  
> - dsi->drm = drm;
> + if (dsi->panel_bridge) {
> + ret = drm_bridge_attach(>encoder, dsi->panel_bridge, NULL, 
> 0);
> + if (ret) {
> +

Re: [PATCH v4 1/4] drm: sun4i: dsi: Use drm_of_find_panel_or_bridge

2021-03-23 Thread Samuel Holland
On 3/23/21 5:53 PM, Laurent Pinchart wrote:
> Hi Jagan,
> 
> Thank you for the patch.
> 
> On Mon, Mar 22, 2021 at 07:31:49PM +0530, Jagan Teki wrote:
>> Replace of_drm_find_panel with drm_of_find_panel_or_bridge
>> for finding panel, this indeed help to find the bridge if
>> bridge support added.
>>
>> Added NULL in bridge argument, same will replace with bridge
>> parameter once bridge supported.
>>
>> Signed-off-by: Jagan Teki 
> 
> Looks good, there should be no functional change.

Actually this breaks all existing users of this driver, see below.

> Reviewed-by: Laurent Pinchart 
> 
>> ---
>> Changes for v4, v3:
>> - none
>>
>>  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 11 ---
>>  1 file changed, 8 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c 
>> b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
>> index 4f5efcace68e..2e9e7b2d4145 100644
>> --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
>> +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
>> @@ -21,6 +21,7 @@
>>  
>>  #include 
>>  #include 
>> +#include 
>>  #include 
>>  #include 
>>  #include 
>> @@ -963,10 +964,14 @@ static int sun6i_dsi_attach(struct mipi_dsi_host *host,
>>  struct mipi_dsi_device *device)
>>  {
>>  struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);
>> -struct drm_panel *panel = of_drm_find_panel(device->dev.of_node);

This is using the OF node of the DSI device, which is a direct child of
the DSI host's OF node. There is no OF graph involved.

>> +struct drm_panel *panel;
>> +int ret;
>> +
>> +ret = drm_of_find_panel_or_bridge(dsi->dev->of_node, 0, 0,
>> +  , NULL);

However, this function expects to find the panel using OF graph. This
does not work with existing device trees (PinePhone, PineTab) which do
not use OF graph to connect the panel. And it cannot work, because the
DSI host's binding specifies a single port: the input port from the
display engine.

Regards,
Samuel

>> +if (ret)
>> +return ret;
>>  
>> -if (IS_ERR(panel))
>> -return PTR_ERR(panel);
>>  if (!dsi->drm || !dsi->drm->registered)
>>  return -EPROBE_DEFER;
>>  
> 



[RFC PATCH] arm64: dts: allwinner: a64/h5: Add CPU idle states

2021-03-22 Thread Samuel Holland
Powering off idle CPUs saves about 33 mW compared to using WFI only.
Additional power savings are possible by idling the L2 and downclocking
the cluster when all CPUs are idle.

Entry and exit latency were measured using a logic analyzer, with GPIO
pins toggled in Linux after the calls to trace_cpu_idle() in
cpuidle_enter_state(), and in the power management firmware after CPU
power-off completes and immediately after detecting an interrupt.

800 us and 1500 us are worst-case values, largely driven by the fact
that the power management firmware is single threaded. It can only
handle commands to power off CPUs one at a time, and it cannot process
any commands while powering on a CPU in response to an interrupt.

The cluster suspend process reliably takes 36 us; I rounded this up to
50 us. If all CPUs enter the cluster idle state at the same time, exit
latency is actually reduced, because there is no contention in that
case. However, if only some CPUs enter the cluster idle state, behavior
is the same as for CPU idle.

Polling delay for the power management firmware to detect a pending
interrupt is insignificant; it is less than 20 us.

min-residency was chosen as the point where enabling the idle state
consumed no more average power than disabling the idle state at a
variety of interrupt rates.

Signed-off-by: Samuel Holland 
---

I'm sending this patch as an RFC because it raises questions about how
we handle firmware versioning. How far back does (or should) our support
for old TF-A and Crust versions go?

cpuidle has a problem that without working firmware support, CPUs will
enter idle states and be unable to wake up. As a result, the system will
hang at some point during boot, usually before getting to userspace.

For over a year[0], TF-A has exposed the PSCI CPU_SUSPEND function when
a SCPI implementation is present[1]. Implementing CPU_SUSPEND is
required for implementing SYSTEM_SUSPEND[2], even if CPU_SUSPEND is not
itself used for anything. 

However, there was no code to actually wake up a CPU once it called the
CPU_SUSPEND function, because I could not find the register providing
the necessary information. The fact that CPU_SUSPEND was broken affected
nobody, because nothing ever called it -- there were no idle states in
the DTS. In hindsight, what I should have done was always return failure
from sunxi_validate_power_state(), but that ship has long sailed.

I finally found the elusive register and implemented the wakeup code
earlier this month[3]. So now, CPU_SUSPEND actually works, if all of
your firmware is up to date, and cpuidle works if you add the states in
your device tree.

Unfortunately, there is currently nothing verifying that compatibility.
So you can get into four possible scenarios:
  1) No idle states in DTS, any firmware => Linux works, with baseline
 power consumption.
  2) Idle states added to DTS, no Crust/SCPI => Linux works, but every
 attempt to enter an idle state is rejected because CPU_SUSPEND is
 not hooked up. So power consumption increases by a sizable amount.
  3) Idle states added to DTS, "old" Crust/SCPI (before [3]) => Linux
 fails to boot, because CPUs never return from idle states.
  4) Idle states added to DTS, "new" Crust/SCPI (after [3]) => Linux
 works, with improved power consumption compared to the baseline.

Obviously, we want to prevent scenario 3 if possible.

Enter the current patch: I chose the arm,psci-suspend-param values
specifically so they would be _rejected_ by the current TF-A code. This
makes scenario 3 behave like scenario 2. I then have some follow-up TF-A
patches (not yet submitted) to switch to the new parameter encoding[4].

This brings me back to my original question. Once the TF-A patches in
[4] are merged, scenario 3 (with an updated TF-A but an old Crust) would
fail to boot again. Do we care?

Should I implement some kind of runtime version checking, so TF-A can
disable CPU_SUSPEND if it would be broken? Or instead, should we wait
some amount of time to merge this patch (or the patches at [4]) and
assume people have upgraded?

Where would people expect this sort of possibly-breaking change to be
documented?

Separately, since I assume most A64/H5 users (outside of LibreELEC and
the PinePhone) are not using Crust, scenario 2 would be very common. If
merging this patch increases their idle power draw by 500 mW, is that an
acceptable cost for decreasing other users' idle power draw by 50 mW?

Sorry for the wall of text,
Samuel

[0]: 
https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/commit/plat/allwinner/common/sunxi_pm.c?id=e382c88e2a26995099bb931d49e754dcaebc5593
[1]: 
https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/tree/plat/allwinner/common/sunxi_scpi_pm.c?id=2e0e51f42586826a1f6f6c1e532f90e6df642cf5#n190
[2]: 
https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/tree/lib/psci/psci_setup.c?id=2e0e51f42586826a1f6f6c1e532f90e6df642cf5#n251
[3]: https:/

[PATCH v2 1/5] dt-bindings: timer: Simplify conditional expressions

2021-03-21 Thread Samuel Holland
The sun4i timer IP block has a variable number of interrupts based on
the compatible. Use enums to combine the two sections for the existing
3-interrupt variants, and to simplify adding new compatible strings.

Acked-by: Maxime Ripard 
Signed-off-by: Samuel Holland 
---
 .../timer/allwinner,sun4i-a10-timer.yaml  | 25 ++-
 1 file changed, 7 insertions(+), 18 deletions(-)

diff --git 
a/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml 
b/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml
index 1c7cf32e7ac2..3462598e609d 100644
--- a/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml
+++ b/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml
@@ -34,8 +34,8 @@ allOf:
   - if:
   properties:
 compatible:
-  items:
-const: allwinner,sun4i-a10-timer
+  enum:
+- allwinner,sun4i-a10-timer
 
 then:
   properties:
@@ -46,8 +46,8 @@ allOf:
   - if:
   properties:
 compatible:
-  items:
-const: allwinner,sun8i-a23-timer
+  enum:
+- allwinner,sun8i-a23-timer
 
 then:
   properties:
@@ -58,20 +58,9 @@ allOf:
   - if:
   properties:
 compatible:
-  items:
-const: allwinner,sun8i-v3s-timer
-
-then:
-  properties:
-interrupts:
-  minItems: 3
-  maxItems: 3
-
-  - if:
-  properties:
-compatible:
-  items:
-const: allwinner,suniv-f1c100s-timer
+  enum:
+- allwinner,sun8i-v3s-timer
+- allwinner,suniv-f1c100s-timer
 
 then:
   properties:
-- 
2.26.2



[PATCH v2 4/5] arm64: dts: allwinner: Add sun4i MMIO timer nodes

2021-03-21 Thread Samuel Holland
For a CPU to enter an idle state, some timer must be available to
trigger an IRQ and wake it back up. The local ARM architectural timer is
not sufficient, because that timer stops when the CPU is powered down.
The ARM architectural timer from some other CPU can be used, but doing
so prevents that other CPU from entering an idle state. For all CPUs to
power down at the same time, Linux needs a timer which is not tied to
any CPU.

Hook up the "sun4i" timer so it can be used for this purpose. It runs at
24 MHz, which balances resolution and power consumption.

Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 9 +
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi  | 9 +
 2 files changed, 18 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 9cac88576975..c89032dfb316 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -798,6 +798,15 @@ uart4_rts_cts_pins: uart4-rts-cts-pins {
};
};
 
+   timer@1c20c00 {
+   compatible = "allwinner,sun50i-a64-timer",
+"allwinner,sun8i-a23-timer";
+   reg = <0x01c20c00 0xa0>;
+   interrupts = ,
+;
+   clocks = <>;
+   };
+
wdt0: watchdog@1c20ca0 {
compatible = "allwinner,sun50i-a64-wdt",
 "allwinner,sun6i-a31-wdt";
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
index 49e979794094..01884b32390d 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -271,6 +271,15 @@ cpu_speed_grade: cpu-speed-grade@1c {
};
};
 
+   timer@3009000 {
+   compatible = "allwinner,sun50i-h6-timer",
+"allwinner,sun8i-a23-timer";
+   reg = <0x03009000 0xa0>;
+   interrupts = ,
+;
+   clocks = <>;
+   };
+
watchdog: watchdog@30090a0 {
compatible = "allwinner,sun50i-h6-wdt",
 "allwinner,sun6i-a31-wdt";
-- 
2.26.2



[PATCH v2 5/5] arm64: sunxi: Build the sun4i timer driver

2021-03-21 Thread Samuel Holland
While the ARM architectural timer is generatlly the best timer to use,
a non-c3stop timer is needed for cpuidle.

Build the "sun4i" timer driver so it can be used for this purpose.
It is present on all 64-bit sunxi SoCs.

Signed-off-by: Samuel Holland 
---
 arch/arm64/Kconfig.platforms | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index cdfd5fed457f..7f6a66431fa7 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -26,6 +26,7 @@ config ARCH_SUNXI
select IRQ_FASTEOI_HIERARCHY_HANDLERS
select PINCTRL
select RESET_CONTROLLER
+   select SUN4I_TIMER
help
  This enables support for Allwinner sunxi based SoCs like the A64.
 
-- 
2.26.2



[PATCH v2 2/5] dt-bindings: timer: Add compatibles for sun50i timers

2021-03-21 Thread Samuel Holland
The sun50i SoCs contain timer blocks which are useful as broadcast
clockevent sources. They each have 2 interrupts, matching the A23
variant, so add the new compatible strings with the A23 compatible
as a fallback.

Acked-by: Maxime Ripard 
Signed-off-by: Samuel Holland 
---
 .../timer/allwinner,sun4i-a10-timer.yaml| 17 -
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git 
a/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml 
b/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml
index 3462598e609d..53fd24bdc34e 100644
--- a/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml
+++ b/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml
@@ -12,11 +12,18 @@ maintainers:
 
 properties:
   compatible:
-enum:
-  - allwinner,sun4i-a10-timer
-  - allwinner,sun8i-a23-timer
-  - allwinner,sun8i-v3s-timer
-  - allwinner,suniv-f1c100s-timer
+oneOf:
+  - enum:
+  - allwinner,sun4i-a10-timer
+  - allwinner,sun8i-a23-timer
+  - allwinner,sun8i-v3s-timer
+  - allwinner,suniv-f1c100s-timer
+  - items:
+  - enum:
+  - allwinner,sun50i-a64-timer
+  - allwinner,sun50i-h6-timer
+  - allwinner,sun50i-h616-timer
+  - const: allwinner,sun8i-a23-timer
 
   reg:
 maxItems: 1
-- 
2.26.2



[PATCH v2 0/5] arm64: sunxi: Enable the sun4i timer

2021-03-21 Thread Samuel Holland
In preparation for adding CPU idle states, hook up the sun4i timer.
Having a non-c3stop clockevent source available is necessary for all
CPUs to simultaneously enter a local-timer-stop idle state.

Changes from v1:
  - Removed H616 changes (depends on an unmerged patch set)
  - Reworded the patch 4-5 commit messages for clarity
  - Added Acked-by tags

Samuel Holland (5):
  dt-bindings: timer: Simplify conditional expressions
  dt-bindings: timer: Add compatibles for sun50i timers
  arm64: dts: allwinner: a64: Sort watchdog node
  arm64: dts: allwinner: Add sun4i MMIO timer nodes
  arm64: sunxi: Build the sun4i timer driver

 .../timer/allwinner,sun4i-a10-timer.yaml  | 42 +--
 arch/arm64/Kconfig.platforms  |  1 +
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 25 +++
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi  |  9 
 4 files changed, 46 insertions(+), 31 deletions(-)

-- 
2.26.2



[PATCH v2 3/5] arm64: dts: allwinner: a64: Sort watchdog node

2021-03-21 Thread Samuel Holland
Nodes should be sorted by unit address. Move the watchdog node to the
correct place, so it will be next to the timer node when that is added.

Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 16 
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 57786fc120c3..9cac88576975 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -798,6 +798,14 @@ uart4_rts_cts_pins: uart4-rts-cts-pins {
};
};
 
+   wdt0: watchdog@1c20ca0 {
+   compatible = "allwinner,sun50i-a64-wdt",
+"allwinner,sun6i-a31-wdt";
+   reg = <0x01c20ca0 0x20>;
+   interrupts = ;
+   clocks = <>;
+   };
+
spdif: spdif@1c21000 {
#sound-dai-cells = <0>;
compatible = "allwinner,sun50i-a64-spdif",
@@ -1321,13 +1329,5 @@ r_rsb: rsb@1f03400 {
#address-cells = <1>;
#size-cells = <0>;
};
-
-   wdt0: watchdog@1c20ca0 {
-   compatible = "allwinner,sun50i-a64-wdt",
-"allwinner,sun6i-a31-wdt";
-   reg = <0x01c20ca0 0x20>;
-   interrupts = ;
-   clocks = <>;
-   };
};
 };
-- 
2.26.2



Re: [PATCH 4/5] arm64: dts: allwinner: Add sun4i MMIO timer nodes

2021-03-15 Thread Samuel Holland
On 3/15/21 1:32 PM, Jernej Škrabec wrote:
> Hi!
> 
> Dne ponedeljek, 15. marec 2021 ob 05:32:49 CET je Samuel Holland napisal(a):
>> For a CPU to enter an idle state, there must be some timer which can
>> trigger an IRQ to wake it back up. The local ARM architectural timer is
>> not sufficient, because that timer stops when the CPU is powered down.
>> Some other CPU's ARM architectural timer can be used, but this prevents
>> that other CPU from entering an idle state. So to allow all CPUs to
>> enter an idle state at the same time, some MMIO timer must be available
>> that is not tied to any CPU.
>>
>> The basic "sun4i" timer seems most appropriate for this purpose due to
>> its moderate rate, balancing precision and power consumption.
>>
>> Signed-off-by: Samuel Holland 
>> ---
>>  arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi  | 9 +
>>  arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi   | 9 +
>>  arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi | 9 +
>>  3 files changed, 27 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
>> b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index
>> 33df866f6ea9..64e8b4a372cc 100644
>> --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
>> +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
>> @@ -905,6 +905,15 @@ uart4_rts_cts_pins: uart4-rts-cts-pins {
>>  };
>>  };
>>
>> +timer@1c20c00 {
>> +compatible = "allwinner,sun50i-a64-timer",
>> + "allwinner,sun8i-a23-timer";
>> +reg = <0x01c20c00 0xa0>;
>> +interrupts =  IRQ_TYPE_LEVEL_HIGH>,
>> +  IRQ_TYPE_LEVEL_HIGH>;
>> +clocks = <>;
>> +};
>> +
>>  wdt0: watchdog@1c20ca0 {
>>  compatible = "allwinner,sun50i-a64-wdt",
>>   "allwinner,sun6i-a31-wdt";
>> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
>> b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi index
>> 62334054c710..9ba3b30e11fa 100644
>> --- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
>> +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
>> @@ -332,6 +332,15 @@ cpu_speed_grade: cpu-speed-grade@1c {
>>  };
>>  };
>>
>> +timer@3009000 {
>> +compatible = "allwinner,sun50i-h6-timer",
>> + "allwinner,sun8i-a23-timer";
>> +reg = <0x03009000 0xa0>;
>> +interrupts =  IRQ_TYPE_LEVEL_HIGH>,
>> +  IRQ_TYPE_LEVEL_HIGH>;
>> +clocks = <>;
>> +};
>> +
>>  watchdog: watchdog@30090a0 {
>>  compatible = "allwinner,sun50i-h6-wdt",
>>   "allwinner,sun6i-a31-wdt";
>> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
>> b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi index
>> c277b53f94ea..ff55712ce96e 100644
>> --- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
>> +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
> 
> This file does not exist yet upstream.

Right. I will remove this section for v2.

Cheers,
Samuel

> Best regards,
> Jernej
> 
>> @@ -128,6 +128,15 @@ ccu: clock@3001000 {
>>  #reset-cells = <1>;
>>  };
>>
>> +timer@3009000 {
>> +compatible = "allwinner,sun50i-h616-timer",
>> + "allwinner,sun8i-a23-timer";
>> +reg = <0x03009000 0xa0>;
>> +interrupts =  IRQ_TYPE_LEVEL_HIGH>,
>> +  IRQ_TYPE_LEVEL_HIGH>;
>> +clocks = <>;
>> +};
>> +
>>  watchdog: watchdog@30090a0 {
>>  compatible = "allwinner,sun50i-h616-wdt",
>>   "allwinner,sun6i-a31-wdt";
> 
> 
> 
> 



Re: [PATCH v7 0/2] hwspinlock: add sun6i hardware spinlock support

2021-03-15 Thread Samuel Holland
On 3/14/21 4:30 AM, Wilken Gottwalt wrote:
> Wilken Gottwalt (2):
>   dt-bindings: hwlock: add sun6i_hwspinlock
>   hwspinlock: add sun6i hardware spinlock support
> 
>  .../allwinner,sun6i-a31-hwspinlock.yaml   |  45 
>  MAINTAINERS   |   6 +
>  drivers/hwspinlock/Kconfig|   9 +
>  drivers/hwspinlock/Makefile   |   1 +
>  drivers/hwspinlock/sun6i_hwspinlock.c | 210 ++
>  5 files changed, 271 insertions(+)
>  create mode 100644 
> Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml
>  create mode 100644 drivers/hwspinlock/sun6i_hwspinlock.c
> 

Thanks for the very thorough testing!

For both patches:
Reviewed-by: Samuel Holland 


[PATCH 5/5] arm64: sunxi: Build the sun4i timer driver

2021-03-14 Thread Samuel Holland
While the ARM architectural timer is generatlly the best timer to use,
a non-c3stop timer is needed for cpuidle. Use the sun4i timer for this
purpose, which is present on all 64-bit sunxi SoCs.

Signed-off-by: Samuel Holland 
---
 arch/arm64/Kconfig.platforms | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index cdfd5fed457f..7f6a66431fa7 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -26,6 +26,7 @@ config ARCH_SUNXI
select IRQ_FASTEOI_HIERARCHY_HANDLERS
select PINCTRL
select RESET_CONTROLLER
+   select SUN4I_TIMER
help
  This enables support for Allwinner sunxi based SoCs like the A64.
 
-- 
2.26.2



[PATCH 0/5] arm64: sunxi: Enable the sun4i timer

2021-03-14 Thread Samuel Holland
In preparation for adding CPU idle states, hook up the sun4i timer.
Having a non-c3stop clockevent source available is necessary for all
CPUs to simultaneously enter a local-timer-stop idle state.

Samuel Holland (5):
  dt-bindings: timer: Simplify conditional expressions
  dt-bindings: timer: Add compatibles for sun50i timers
  arm64: dts: allwinner: a64: Sort watchdog node
  arm64: dts: allwinner: Add sun4i MMIO timer nodes
  arm64: sunxi: Build the sun4i timer driver

 .../timer/allwinner,sun4i-a10-timer.yaml  | 42 +--
 arch/arm64/Kconfig.platforms  |  1 +
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 25 +++
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi  |  9 
 .../arm64/boot/dts/allwinner/sun50i-h616.dtsi |  9 
 5 files changed, 55 insertions(+), 31 deletions(-)

-- 
2.26.2



[PATCH 2/5] dt-bindings: timer: Add compatibles for sun50i timers

2021-03-14 Thread Samuel Holland
The sun50i SoCs contain timer blocks which are useful as broadcast
clockevent sources. They each have 2 interrupts, matching the A23
variant, so add the new compatible strings with the A23 compatible
as a fallback.

Signed-off-by: Samuel Holland 
---
 .../timer/allwinner,sun4i-a10-timer.yaml| 17 -
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git 
a/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml 
b/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml
index 3462598e609d..53fd24bdc34e 100644
--- a/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml
+++ b/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml
@@ -12,11 +12,18 @@ maintainers:
 
 properties:
   compatible:
-enum:
-  - allwinner,sun4i-a10-timer
-  - allwinner,sun8i-a23-timer
-  - allwinner,sun8i-v3s-timer
-  - allwinner,suniv-f1c100s-timer
+oneOf:
+  - enum:
+  - allwinner,sun4i-a10-timer
+  - allwinner,sun8i-a23-timer
+  - allwinner,sun8i-v3s-timer
+  - allwinner,suniv-f1c100s-timer
+  - items:
+  - enum:
+  - allwinner,sun50i-a64-timer
+  - allwinner,sun50i-h6-timer
+  - allwinner,sun50i-h616-timer
+  - const: allwinner,sun8i-a23-timer
 
   reg:
 maxItems: 1
-- 
2.26.2



[PATCH 4/5] arm64: dts: allwinner: Add sun4i MMIO timer nodes

2021-03-14 Thread Samuel Holland
For a CPU to enter an idle state, there must be some timer which can
trigger an IRQ to wake it back up. The local ARM architectural timer is
not sufficient, because that timer stops when the CPU is powered down.
Some other CPU's ARM architectural timer can be used, but this prevents
that other CPU from entering an idle state. So to allow all CPUs to
enter an idle state at the same time, some MMIO timer must be available
that is not tied to any CPU.

The basic "sun4i" timer seems most appropriate for this purpose due to
its moderate rate, balancing precision and power consumption.

Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi  | 9 +
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi   | 9 +
 arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi | 9 +
 3 files changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 33df866f6ea9..64e8b4a372cc 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -905,6 +905,15 @@ uart4_rts_cts_pins: uart4-rts-cts-pins {
};
};
 
+   timer@1c20c00 {
+   compatible = "allwinner,sun50i-a64-timer",
+"allwinner,sun8i-a23-timer";
+   reg = <0x01c20c00 0xa0>;
+   interrupts = ,
+;
+   clocks = <>;
+   };
+
wdt0: watchdog@1c20ca0 {
compatible = "allwinner,sun50i-a64-wdt",
 "allwinner,sun6i-a31-wdt";
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
index 62334054c710..9ba3b30e11fa 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -332,6 +332,15 @@ cpu_speed_grade: cpu-speed-grade@1c {
};
};
 
+   timer@3009000 {
+   compatible = "allwinner,sun50i-h6-timer",
+"allwinner,sun8i-a23-timer";
+   reg = <0x03009000 0xa0>;
+   interrupts = ,
+;
+   clocks = <>;
+   };
+
watchdog: watchdog@30090a0 {
compatible = "allwinner,sun50i-h6-wdt",
 "allwinner,sun6i-a31-wdt";
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
index c277b53f94ea..ff55712ce96e 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
@@ -128,6 +128,15 @@ ccu: clock@3001000 {
#reset-cells = <1>;
};
 
+   timer@3009000 {
+   compatible = "allwinner,sun50i-h616-timer",
+"allwinner,sun8i-a23-timer";
+   reg = <0x03009000 0xa0>;
+   interrupts = ,
+;
+   clocks = <>;
+   };
+
watchdog: watchdog@30090a0 {
compatible = "allwinner,sun50i-h616-wdt",
 "allwinner,sun6i-a31-wdt";
-- 
2.26.2



[PATCH 3/5] arm64: dts: allwinner: a64: Sort watchdog node

2021-03-14 Thread Samuel Holland
Nodes should be sorted by unit address. Move the watchdog node to the
correct place, so it will be next to the timer node when that is added.

Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 16 
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 206693423aa6..33df866f6ea9 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -905,6 +905,14 @@ uart4_rts_cts_pins: uart4-rts-cts-pins {
};
};
 
+   wdt0: watchdog@1c20ca0 {
+   compatible = "allwinner,sun50i-a64-wdt",
+"allwinner,sun6i-a31-wdt";
+   reg = <0x01c20ca0 0x20>;
+   interrupts = ;
+   clocks = <>;
+   };
+
spdif: spdif@1c21000 {
#sound-dai-cells = <0>;
compatible = "allwinner,sun50i-a64-spdif",
@@ -1436,13 +1444,5 @@ r_rsb: rsb@1f03400 {
#address-cells = <1>;
#size-cells = <0>;
};
-
-   wdt0: watchdog@1c20ca0 {
-   compatible = "allwinner,sun50i-a64-wdt",
-"allwinner,sun6i-a31-wdt";
-   reg = <0x01c20ca0 0x20>;
-   interrupts = ;
-   clocks = <>;
-   };
};
 };
-- 
2.26.2



[PATCH 1/5] dt-bindings: timer: Simplify conditional expressions

2021-03-14 Thread Samuel Holland
The sun4i timer IP block has a variable number of interrupts based on
the compatible. Use enums to combine the two sections for the existing
3-interrupt variants, and to simplify adding new compatible strings.

Signed-off-by: Samuel Holland 
---
 .../timer/allwinner,sun4i-a10-timer.yaml  | 25 ++-
 1 file changed, 7 insertions(+), 18 deletions(-)

diff --git 
a/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml 
b/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml
index 1c7cf32e7ac2..3462598e609d 100644
--- a/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml
+++ b/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml
@@ -34,8 +34,8 @@ allOf:
   - if:
   properties:
 compatible:
-  items:
-const: allwinner,sun4i-a10-timer
+  enum:
+- allwinner,sun4i-a10-timer
 
 then:
   properties:
@@ -46,8 +46,8 @@ allOf:
   - if:
   properties:
 compatible:
-  items:
-const: allwinner,sun8i-a23-timer
+  enum:
+- allwinner,sun8i-a23-timer
 
 then:
   properties:
@@ -58,20 +58,9 @@ allOf:
   - if:
   properties:
 compatible:
-  items:
-const: allwinner,sun8i-v3s-timer
-
-then:
-  properties:
-interrupts:
-  minItems: 3
-  maxItems: 3
-
-  - if:
-  properties:
-compatible:
-  items:
-const: allwinner,suniv-f1c100s-timer
+  enum:
+- allwinner,sun8i-v3s-timer
+- allwinner,suniv-f1c100s-timer
 
 then:
   properties:
-- 
2.26.2



Re: [PATCH RESEND 0/2] Common protected-clocks implementation

2021-03-10 Thread Samuel Holland
On 3/10/21 2:56 AM, Maxime Ripard wrote:
> Hi,
> 
> On Tue, Mar 09, 2021 at 09:03:14AM +0100, Rasmus Villemoes wrote:
>> On 03/09/2020 06.00, Samuel Holland wrote:
>>> Stephen, Maxime,
>>>
>>> You previously asked me to implement the protected-clocks property in a
>>> driver-independent way:
>>>
>>> https://www.spinics.net/lists/arm-kernel/msg753832.html
>>>
>>> I provided an implementation 6 months ago, which I am resending now:
>>>
>>> https://patchwork.kernel.org/patch/11398629/
>>>
>>> Do you have any comments on it?
>>
>> I'm also interested [1] in getting something like this supported in a
>> generic fashion - i.e., being able to mark a clock as
>> protected/critical/whatnot by just adding an appropriate property in the
>> clock provider's DT node, but without modifying the driver to opt-in to
>> handling it.
>>
>> Now, as to this implementation, the commit 48d7f160b1 which added the
>> common protected-clocks binding says
>>
>>   For example, on some Qualcomm firmwares reading or writing certain clk
>>   registers causes the entire system to reboot,
>>
>> so I'm not sure handling protected-clocks by translating it to
>> CLK_CRITICAL and thus calling prepare/enable on it is the right thing to
>> do - clks that behave like above are truly "hands off, kernel", so the
>> current driver-specific implementation of simply not registering those
>> clocks seems to be the right thing to do - or at least the clk framework
>> would need to be taught to not actually call any methods on such
>> protected clocks.
> 
> The idea to use CLK_CRITICAL was also there to allow any clock to be
> marked as protected, and not just the root ones. Indeed, by just
> ignoring that clock, the parent clock could end up in a situation where
> it doesn't have any (registered) child and thus would be disabled,
> disabling the ignored clock as well. Reparenting could cause the same
> issue.
> 
> Calling clk_prepare_enable just allows the kernel to track that it (and
> thus its parent) should never be disabled, ever.
> 
>> For my use case, either "hands off kernel" or "make sure this clock is
>> enabled" would work since the bootloader anyway enables the clock.
> 
> The current protected clocks semantics have been that the clock
> shouldn't be disabled by the kernel, but "hands off kernel" clocks imply
> a slightly different one. I would expect that the bootloader in this
> case wouldn't expect any parent or rate (or phase, or any other
> parameter really) change, while it might be ok for some other use cases
> (like the one Samuel was trying to address for example).

Right. In my scenario, the clock is needed for MMIO access to a low-speed
peripheral. I don't care what the clock rate is as long as it keeps running.

When writing the patch, I initially protected the rate as well, but that had the
unintended effect of protecting the rate of every ancestor clock. Maybe you need
rate protection for a "hands off, kernel" type of clock? If so, ignoring the
clock at probe time does not work, because the kernel does not know to protect
its parent rate.

So it sounds like the Qualcomm case is less about "don't touch the clock output"
as it is "don't touch the clock control registers".

> And even if we wanted the kernel to behave that way (making sure there's
> no way to change the rate, parents, phase, etc.), the kernel would have
> to have knowledge of it to also propagate that restriction to the whole
> chain of parents.

Yes, you can set additional flags in __clk_protect() to achieve that:
CLK_SET_RATE_GATE, CLK_SET_PARENT_GATE. This is a bit of a trick; it relies on
CLK_IS_CRITICAL preventing the clock from ever being gated.

Cheers,
Samuel


[PATCH net-next v2 5/5] net: stmmac: dwmac-sun8i: Add a shutdown callback

2021-02-16 Thread Samuel Holland
The Ethernet MAC and PHY are usually major consumers of power on boards
which may not be able to fully power off (those with no PMIC). Powering
down the MAC and internal PHY saves power while these boards are "off".

Reviewed-by: Chen-Yu Tsai 
Signed-off-by: Samuel Holland 
---
 drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index a3d333b652836..6b75cf2603ffc 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -1284,6 +1284,15 @@ static int sun8i_dwmac_remove(struct platform_device 
*pdev)
return 0;
 }
 
+static void sun8i_dwmac_shutdown(struct platform_device *pdev)
+{
+   struct net_device *ndev = platform_get_drvdata(pdev);
+   struct stmmac_priv *priv = netdev_priv(ndev);
+   struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
+
+   sun8i_dwmac_exit(pdev, gmac);
+}
+
 static const struct of_device_id sun8i_dwmac_match[] = {
{ .compatible = "allwinner,sun8i-h3-emac",
.data = _variant_h3 },
@@ -1304,6 +1313,7 @@ MODULE_DEVICE_TABLE(of, sun8i_dwmac_match);
 static struct platform_driver sun8i_dwmac_driver = {
.probe  = sun8i_dwmac_probe,
.remove = sun8i_dwmac_remove,
+   .shutdown = sun8i_dwmac_shutdown,
.driver = {
.name   = "dwmac-sun8i",
.pm = _pltfr_pm_ops,
-- 
2.26.2



[PATCH net-next v2 1/5] net: stmmac: dwmac-sun8i: Return void from PHY unpower

2021-02-16 Thread Samuel Holland
This is a deinitialization function that always returned zero, and that
return value was always ignored. Have it return void instead.

Reviewed-by: Chen-Yu Tsai 
Signed-off-by: Samuel Holland 
---
 drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index a5e0eff4a3874..8e505019adf85 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -820,15 +820,14 @@ static int sun8i_dwmac_power_internal_phy(struct 
stmmac_priv *priv)
return 0;
 }
 
-static int sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac)
+static void sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac)
 {
if (!gmac->internal_phy_powered)
-   return 0;
+   return;
 
clk_disable_unprepare(gmac->ephy_clk);
reset_control_assert(gmac->rst_ephy);
gmac->internal_phy_powered = false;
-   return 0;
 }
 
 /* MDIO multiplexing switch function
-- 
2.26.2



[PATCH net-next v2 3/5] net: stmmac: dwmac-sun8i: Use reset_control_reset

2021-02-16 Thread Samuel Holland
Use the appropriate function instead of reimplementing it,
and update the error message to match the code.

Reviewed-by: Chen-Yu Tsai 
Signed-off-by: Samuel Holland 
---
 drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 8 
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index 3c3d0b99d3e8c..b61f442ed3033 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -805,12 +805,12 @@ static int sun8i_dwmac_power_internal_phy(struct 
stmmac_priv *priv)
 
/* Make sure the EPHY is properly reseted, as U-Boot may leave
 * it at deasserted state, and thus it may fail to reset EMAC.
+*
+* This assumes the driver has exclusive access to the EPHY reset.
 */
-   reset_control_assert(gmac->rst_ephy);
-
-   ret = reset_control_deassert(gmac->rst_ephy);
+   ret = reset_control_reset(gmac->rst_ephy);
if (ret) {
-   dev_err(priv->device, "Cannot deassert internal phy\n");
+   dev_err(priv->device, "Cannot reset internal PHY\n");
clk_disable_unprepare(gmac->ephy_clk);
return ret;
}
-- 
2.26.2



[PATCH net-next v2 0/5] dwmac-sun8i cleanup and shutdown hook

2021-02-16 Thread Samuel Holland
These patches clean up some things I noticed while fixing suspend/resume
behavior. The first four are minor code improvements. The last one adds
a shutdown hook to minimize power consumption on boards without a PMIC.

Changes v1 to v2:
  - Note the assumption of exclusive reset controller access in patch 3

Samuel Holland (5):
  net: stmmac: dwmac-sun8i: Return void from PHY unpower
  net: stmmac: dwmac-sun8i: Remove unnecessary PHY power check
  net: stmmac: dwmac-sun8i: Use reset_control_reset
  net: stmmac: dwmac-sun8i: Minor probe function cleanup
  net: stmmac: dwmac-sun8i: Add a shutdown callback

 .../net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 33 ---
 1 file changed, 21 insertions(+), 12 deletions(-)

-- 
2.26.2



[PATCH net-next v2 4/5] net: stmmac: dwmac-sun8i: Minor probe function cleanup

2021-02-16 Thread Samuel Holland
Adjust the spacing and use an explicit "return 0" in the success path
to make the function easier to parse.

Reviewed-by: Chen-Yu Tsai 
Signed-off-by: Samuel Holland 
---
 drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index b61f442ed3033..a3d333b652836 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -1229,6 +1229,7 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
 
ndev = dev_get_drvdata(>dev);
priv = netdev_priv(ndev);
+
/* The mux must be registered after parent MDIO
 * so after stmmac_dvr_probe()
 */
@@ -1247,7 +1248,8 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
goto dwmac_remove;
}
 
-   return ret;
+   return 0;
+
 dwmac_mux:
reset_control_put(gmac->rst_ephy);
clk_put(gmac->ephy_clk);
-- 
2.26.2



[PATCH net-next v2 2/5] net: stmmac: dwmac-sun8i: Remove unnecessary PHY power check

2021-02-16 Thread Samuel Holland
sun8i_dwmac_unpower_internal_phy already checks if the PHY is powered,
so there is no need to do it again here.

Reviewed-by: Chen-Yu Tsai 
Signed-off-by: Samuel Holland 
---
 drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 6 ++
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index 8e505019adf85..3c3d0b99d3e8c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -1018,10 +1018,8 @@ static void sun8i_dwmac_exit(struct platform_device 
*pdev, void *priv)
 {
struct sunxi_priv_data *gmac = priv;
 
-   if (gmac->variant->soc_has_internal_phy) {
-   if (gmac->internal_phy_powered)
-   sun8i_dwmac_unpower_internal_phy(gmac);
-   }
+   if (gmac->variant->soc_has_internal_phy)
+   sun8i_dwmac_unpower_internal_phy(gmac);
 
clk_disable_unprepare(gmac->tx_clk);
 
-- 
2.26.2



Re: [PATCH net-next RESEND 3/5] net: stmmac: dwmac-sun8i: Use reset_control_reset

2021-02-08 Thread Samuel Holland
On 2/8/21 10:29 AM, Alexander Duyck wrote:
> On Sun, Feb 7, 2021 at 10:32 PM Samuel Holland  wrote:
>>
>> Use the appropriate function instead of reimplementing it,
>> and update the error message to match the code.
>>
>> Reviewed-by: Chen-Yu Tsai 
>> Signed-off-by: Samuel Holland 
>> ---
>>  drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 6 ++
>>  1 file changed, 2 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c 
>> b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
>> index 3c3d0b99d3e8..0e8d88417251 100644
>> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
>> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
>> @@ -806,11 +806,9 @@ static int sun8i_dwmac_power_internal_phy(struct 
>> stmmac_priv *priv)
>> /* Make sure the EPHY is properly reseted, as U-Boot may leave
>>  * it at deasserted state, and thus it may fail to reset EMAC.
>>  */
>> -   reset_control_assert(gmac->rst_ephy);
>> -
>> -   ret = reset_control_deassert(gmac->rst_ephy);
>> +   ret = reset_control_reset(gmac->rst_ephy);
>> if (ret) {
>> -   dev_err(priv->device, "Cannot deassert internal phy\n");
>> +   dev_err(priv->device, "Cannot reset internal PHY\n");
>> clk_disable_unprepare(gmac->ephy_clk);
>> return ret;
>> }
> 
> I'm assuming you have exclusive access to the phy and this isn't a
> shared line? Just wanting to confirm since the function call has the
> following comment in the header for the documentation.

Yes, this driver has exclusive access:

gmac->rst_ephy = of_reset_control_get_exclusive(iphynode, NULL);

And this is a reset line for the Ethernet PHY inside the SoC, that as
far as I can tell is not shared with anything else.

>  * Consumers must not use reset_control_(de)assert on shared reset lines when
>  * reset_control_reset has been used.
>  *
> 
> If that is the case it might not hurt to add some documentation to
> your call to reset_control_reset here explaining that it is safe to do
> so since you have exclusive access.

I can expand the comment above this line for v2.

Cheers,
Samuel


Re: [PATCH] i2c: mv64xxx: Fix check for missing clock

2021-02-08 Thread Samuel Holland
On 2/8/21 7:20 AM, Andrew Lunn wrote:
> On Mon, Feb 08, 2021 at 12:31:34AM -0600, Samuel Holland wrote:
>> On 2/8/21 12:28 AM, Samuel Holland wrote:
>>> In commit e5c02cf54154 ("i2c: mv64xxx: Add runtime PM support"), error
>>> pointers to optional clocks were replaced by NULL to simplify the resume
>>> callback implementation. However, that commit missed that the IS_ERR
>>> check in mv64xxx_of_config should be replaced with a NULL check. As a
>>> result, the check always passes, even for an invalid device tree.
>>
>> Sorry, please ignore this unrelated patch. I accidentally copied it to
>> the wrong directory before sending this series.
> 
> Hi Samuel
> 
> This patch looks correct. But i don't see it in i2c/for-next, where as
> e5c02cf54154 is. I just want to make sure it does not get lost...

Thanks for the concern. I had already sent it separately[0], to the
appropriate maintainers, so this submission was a duplicate.

Cheers,
Samuel

[0]:
https://lore.kernel.org/lkml/20210208061922.10073-1-sam...@sholland.org/

>Andrew
> 



Re: [PATCH] i2c: mv64xxx: Fix check for missing clock

2021-02-07 Thread Samuel Holland
On 2/8/21 12:28 AM, Samuel Holland wrote:
> In commit e5c02cf54154 ("i2c: mv64xxx: Add runtime PM support"), error
> pointers to optional clocks were replaced by NULL to simplify the resume
> callback implementation. However, that commit missed that the IS_ERR
> check in mv64xxx_of_config should be replaced with a NULL check. As a
> result, the check always passes, even for an invalid device tree.

Sorry, please ignore this unrelated patch. I accidentally copied it to
the wrong directory before sending this series.

Samuel


[PATCH net-next RESEND 5/5] net: stmmac: dwmac-sun8i: Add a shutdown callback

2021-02-07 Thread Samuel Holland
The Ethernet MAC and PHY are usually major consumers of power on boards
which may not be able to fully power off (those with no PMIC). Powering
down the MAC and internal PHY saves power while these boards are "off".

Reviewed-by: Chen-Yu Tsai 
Signed-off-by: Samuel Holland 
---
 drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index 4638d4203af5..926e8d5e8963 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -1282,6 +1282,15 @@ static int sun8i_dwmac_remove(struct platform_device 
*pdev)
return 0;
 }
 
+static void sun8i_dwmac_shutdown(struct platform_device *pdev)
+{
+   struct net_device *ndev = platform_get_drvdata(pdev);
+   struct stmmac_priv *priv = netdev_priv(ndev);
+   struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
+
+   sun8i_dwmac_exit(pdev, gmac);
+}
+
 static const struct of_device_id sun8i_dwmac_match[] = {
{ .compatible = "allwinner,sun8i-h3-emac",
.data = _variant_h3 },
@@ -1302,6 +1311,7 @@ MODULE_DEVICE_TABLE(of, sun8i_dwmac_match);
 static struct platform_driver sun8i_dwmac_driver = {
.probe  = sun8i_dwmac_probe,
.remove = sun8i_dwmac_remove,
+   .shutdown = sun8i_dwmac_shutdown,
.driver = {
.name   = "dwmac-sun8i",
.pm = _pltfr_pm_ops,
-- 
2.26.2



[PATCH net-next RESEND 4/5] net: stmmac: dwmac-sun8i: Minor probe function cleanup

2021-02-07 Thread Samuel Holland
Adjust the spacing and use an explicit "return 0" in the success path
to make the function easier to parse.

Reviewed-by: Chen-Yu Tsai 
Signed-off-by: Samuel Holland 
---
 drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index 0e8d88417251..4638d4203af5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -1227,6 +1227,7 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
 
ndev = dev_get_drvdata(>dev);
priv = netdev_priv(ndev);
+
/* The mux must be registered after parent MDIO
 * so after stmmac_dvr_probe()
 */
@@ -1245,7 +1246,8 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
goto dwmac_remove;
}
 
-   return ret;
+   return 0;
+
 dwmac_mux:
reset_control_put(gmac->rst_ephy);
clk_put(gmac->ephy_clk);
-- 
2.26.2



[PATCH net-next RESEND 0/5] dwmac-sun8i cleanup and shutdown hook

2021-02-07 Thread Samuel Holland
These patches clean up some things I noticed while fixing suspend/resume
behavior. The first four are minor code improvements. The last one adds
a shutdown hook to minimize power consumption on boards without a PMIC.

Now that the fixes series is merged, I'm resending this series rebased
on top of net-next and with Chen-Yu's Reviewed-by tags.

Samuel Holland (5):
  net: stmmac: dwmac-sun8i: Return void from PHY unpower
  net: stmmac: dwmac-sun8i: Remove unnecessary PHY power check
  net: stmmac: dwmac-sun8i: Use reset_control_reset
  net: stmmac: dwmac-sun8i: Minor probe function cleanup
  net: stmmac: dwmac-sun8i: Add a shutdown callback

 .../net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 31 ---
 1 file changed, 19 insertions(+), 12 deletions(-)

-- 
2.26.2



[PATCH net-next RESEND 2/5] net: stmmac: dwmac-sun8i: Remove unnecessary PHY power check

2021-02-07 Thread Samuel Holland
sun8i_dwmac_unpower_internal_phy already checks if the PHY is powered,
so there is no need to do it again here.

Reviewed-by: Chen-Yu Tsai 
Signed-off-by: Samuel Holland 
---
 drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 6 ++
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index 8e505019adf8..3c3d0b99d3e8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -1018,10 +1018,8 @@ static void sun8i_dwmac_exit(struct platform_device 
*pdev, void *priv)
 {
struct sunxi_priv_data *gmac = priv;
 
-   if (gmac->variant->soc_has_internal_phy) {
-   if (gmac->internal_phy_powered)
-   sun8i_dwmac_unpower_internal_phy(gmac);
-   }
+   if (gmac->variant->soc_has_internal_phy)
+   sun8i_dwmac_unpower_internal_phy(gmac);
 
clk_disable_unprepare(gmac->tx_clk);
 
-- 
2.26.2



[PATCH net-next RESEND 1/5] net: stmmac: dwmac-sun8i: Return void from PHY unpower

2021-02-07 Thread Samuel Holland
This is a deinitialization function that always returned zero, and that
return value was always ignored. Have it return void instead.

Reviewed-by: Chen-Yu Tsai 
Signed-off-by: Samuel Holland 
---
 drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index a5e0eff4a387..8e505019adf8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -820,15 +820,14 @@ static int sun8i_dwmac_power_internal_phy(struct 
stmmac_priv *priv)
return 0;
 }
 
-static int sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac)
+static void sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac)
 {
if (!gmac->internal_phy_powered)
-   return 0;
+   return;
 
clk_disable_unprepare(gmac->ephy_clk);
reset_control_assert(gmac->rst_ephy);
gmac->internal_phy_powered = false;
-   return 0;
 }
 
 /* MDIO multiplexing switch function
-- 
2.26.2



[PATCH net-next RESEND 3/5] net: stmmac: dwmac-sun8i: Use reset_control_reset

2021-02-07 Thread Samuel Holland
Use the appropriate function instead of reimplementing it,
and update the error message to match the code.

Reviewed-by: Chen-Yu Tsai 
Signed-off-by: Samuel Holland 
---
 drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 6 ++
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c 
b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index 3c3d0b99d3e8..0e8d88417251 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -806,11 +806,9 @@ static int sun8i_dwmac_power_internal_phy(struct 
stmmac_priv *priv)
/* Make sure the EPHY is properly reseted, as U-Boot may leave
 * it at deasserted state, and thus it may fail to reset EMAC.
 */
-   reset_control_assert(gmac->rst_ephy);
-
-   ret = reset_control_deassert(gmac->rst_ephy);
+   ret = reset_control_reset(gmac->rst_ephy);
if (ret) {
-   dev_err(priv->device, "Cannot deassert internal phy\n");
+   dev_err(priv->device, "Cannot reset internal PHY\n");
clk_disable_unprepare(gmac->ephy_clk);
return ret;
}
-- 
2.26.2



[PATCH] i2c: mv64xxx: Fix check for missing clock

2021-02-07 Thread Samuel Holland
In commit e5c02cf54154 ("i2c: mv64xxx: Add runtime PM support"), error
pointers to optional clocks were replaced by NULL to simplify the resume
callback implementation. However, that commit missed that the IS_ERR
check in mv64xxx_of_config should be replaced with a NULL check. As a
result, the check always passes, even for an invalid device tree.

Fixes: e5c02cf54154 ("i2c: mv64xxx: Add runtime PM support")
Reported-by: Dan Carpenter 
Signed-off-by: Samuel Holland 
---
 drivers/i2c/busses/i2c-mv64xxx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index b03c344323d1..c590d36b5fd1 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -813,7 +813,7 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
 * need to know tclk in order to calculate bus clock
 * factors.
 */
-   if (IS_ERR(drv_data->clk)) {
+   if (!drv_data->clk) {
rc = -ENODEV;
goto out;
}
-- 
2.26.2



[PATCH] i2c: mv64xxx: Fix check for missing clock

2021-02-07 Thread Samuel Holland
In commit e5c02cf54154 ("i2c: mv64xxx: Add runtime PM support"), error
pointers to optional clocks were replaced by NULL to simplify the resume
callback implementation. However, that commit missed that the IS_ERR
check in mv64xxx_of_config should be replaced with a NULL check. As a
result, the check always passes, even for an invalid device tree.

Fixes: e5c02cf54154 ("i2c: mv64xxx: Add runtime PM support")
Reported-by: Dan Carpenter 
Signed-off-by: Samuel Holland 
---
 drivers/i2c/busses/i2c-mv64xxx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index b03c344323d1..c590d36b5fd1 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -813,7 +813,7 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
 * need to know tclk in order to calculate bus clock
 * factors.
 */
-   if (IS_ERR(drv_data->clk)) {
+   if (!drv_data->clk) {
rc = -ENODEV;
goto out;
}
-- 
2.26.2



Re: Fail to boot on bpim3 with next-20210128

2021-01-30 Thread Samuel Holland
On 1/30/21 11:00 AM, Corentin Labbe wrote:
> Hello
> 
> next-20210128 cannot be booted on my bpim3.
> It start booting then freeze.
> 
> [0.00] Linux version 5.11.0-rc5-03084-g7426957d237f (compile@Red) 
> (armv7a-unknown-linux-gnueabihf-gcc (Gentoo 9.3.0-r2 p4) 9.3.0, GNU ld 
> (Gentoo 2.34 p6) 2.34.0) #390 SMP Sat Jan 30 14:59:48 CET 2021
> [0.00] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c5387d
> [0.00] CPU: div instructions available: patching division code
> [0.00] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing 
> instruction cache
> [0.00] OF: fdt: Machine model: Banana Pi BPI-M3
> [0.00] earlycon: uart0 at MMIO32 0x01c28000 (options '')
> [0.00] printk: bootconsole [uart0] enabled
> [0.00] Memory policy: Data cache writealloc
> [0.00] cma: Reserved 16 MiB at 0xbf00
> [0.00] Zone ranges:
> [0.00]   Normal   [mem 0x4000-0x6fff]
> [0.00]   HighMem  [mem 0x7000-0xbfff]
> [0.00] Movable zone start for each node
> [0.00] Early memory node ranges
> [0.00]   node   0: [mem 0x4000-0xbfff]
> [0.00] Initmem setup node 0 [mem 
> 0x4000-0xbfff]
> [0.00] percpu: Embedded 15 pages/cpu s31244 r8192 d22004 u61440
> [0.00] Built 1 zonelists, mobility grouping on.  Total pages: 522752
> [0.00] Kernel command line: console=ttyS0,115200n8 root=/dev/ram0 
> earlycon=uart,mmio32,0x01c28000 ip=dhcp
> [0.00] Dentry cache hash table entries: 131072 (order: 7, 524288 
> bytes, linear)
> [0.00] Inode-cache hash table entries: 65536 (order: 6, 262144 bytes, 
> linear)
> [0.00] mem auto-init: stack:off, heap alloc:off, heap free:off
> [0.00] Memory: 2021376K/2097152K available (7168K kernel code, 934K 
> rwdata, 2316K rodata, 1024K init, 240K bss, 59392K reserved, 16384K 
> cma-reserved, 1294336K highmem)
> [0.00] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=8, Nodes=1
> [0.00] rcu: Hierarchical RCU implementation.
> [0.00] rcu:   RCU event tracing is enabled.
> [0.00] rcu: RCU calculated value of scheduler-enlistment delay is 10 
> jiffies.
> [0.00] NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
> [0.00] random: get_random_bytes called from start_kernel+0x350/0x4e0 
> with crng_init=0
> [0.00] arch_timer: cp15 timer(s) running at 24.00MHz (virt).
> [0.00] clocksource: arch_sys_counter: mask: 0xff 
> max_cycles: 0x588fe9dc0, max_idle_ns: 440795202592 ns
> [0.03] sched_clock: 56 bits at 24MHz, resolution 41ns, wraps every 
> 4398046511097ns
> [0.008640] Switching to timer-based delay loop, resolution 41ns
> [0.015437] clocksource: timer: mask: 0x max_cycles: 0x, 
> max_idle_ns: 79635851949 ns
> [0.025858] Console: colour dummy device 80x30
> [0.030736] Calibrating delay loop (skipped), value calculated using timer 
> frequency.. 48.00 BogoMIPS (lpj=24)
> [0.041941] pid_max: default: 32768 minimum: 301
> [0.047080] Mount-cache hash table entries: 2048 (order: 1, 8192 bytes, 
> linear)
> [0.054990] Mountpoint-cache hash table entries: 2048 (order: 1, 8192 
> bytes, linear)
> [0.064421] CPU: Testing write buffer coherency: ok
> [0.070242] /cpus/cpu@0 missing clock-frequency property
> [0.076025] /cpus/cpu@1 missing clock-frequency property
> [0.081805] /cpus/cpu@2 missing clock-frequency property
> [0.087546] /cpus/cpu@3 missing clock-frequency property
> [0.093300] /cpus/cpu@100 missing clock-frequency property
> [0.099227] /cpus/cpu@101 missing clock-frequency property
> [0.105173] /cpus/cpu@102 missing clock-frequency property
> [0.11] /cpus/cpu@103 missing clock-frequency property
> [0.117026] CPU0: thread -1, cpu 0, socket 0, mpidr 8000
> [0.123888] Setting up static identity map for 0x4010 - 0x40100060
> [0.131237] ARM CCI driver probed
> [0.135530] sunxi multi cluster SMP support installed
> [0.141501] rcu: Hierarchical SRCU implementation.
> [0.147277] smp: Bringing up secondary CPUs ...
> [0.153237] CPU1: thread -1, cpu 1, socket 0, mpidr 8001
> [0.154443] CPU2: thread -1, cpu 2, socket 0, mpidr 8002
> [0.155544] CPU3: thread -1, cpu 3, socket 0, mpidr 8003
> [0.156837] CPU4: thread -1, cpu 0, socket 1, mpidr 8100
> [0.158221] CPU5: thread -1, cpu 1, socket 1, mpidr 8101
> [0.159456] CPU6: thread -1, cpu 2, socket 1, mpidr 8102
> [0.160663] CPU7: thread -1, cpu 3, socket 1, mpidr 8103
> [0.160825] smp: Brought up 1 node, 8 CPUs
> [0.207953] SMP: Total of 8 processors activated (384.00 BogoMIPS).
> [0.214714] CPU: All CPU(s) started in SVC mode.
> [0.221355] devtmpfs: initialized
> [0.232737] VFP support v0.3: implementor 41 architecture 2 part 

[PATCH] power: supply: axp20x_usb_power: Init work before enabling IRQs

2021-01-24 Thread Samuel Holland
The IRQ handler calls mod_delayed_work() on power->vbus_detect. However,
that work item is not initialized until after the IRQs are enabled. If
an IRQ is already pending when the driver is probed, the driver calls
mod_delayed_work() on an uninitialized work item, which causes an oops.

Fixes: bcfb7ae3f50b ("power: supply: axp20x_usb_power: Only poll while offline")
Signed-off-by: Samuel Holland 
---
 drivers/power/supply/axp20x_usb_power.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/power/supply/axp20x_usb_power.c 
b/drivers/power/supply/axp20x_usb_power.c
index 20817a49110b..02aba3da271a 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -711,20 +711,21 @@ static int axp20x_usb_power_probe(struct platform_device 
*pdev)
 struct_size(power, irqs, axp_data->num_irq_names),
 GFP_KERNEL);
if (!power)
return -ENOMEM;
 
platform_set_drvdata(pdev, power);
 
power->axp20x_id = axp_data->axp20x_id;
power->regmap = axp20x->regmap;
power->num_irqs = axp_data->num_irq_names;
+   INIT_DELAYED_WORK(>vbus_detect, axp20x_usb_power_poll_vbus);
 
if (power->axp20x_id == AXP202_ID) {
/* Enable vbus valid checking */
ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
 AXP20X_VBUS_MON_VBUS_VALID,
 AXP20X_VBUS_MON_VBUS_VALID);
if (ret)
return ret;
 
if (IS_ENABLED(CONFIG_AXP20X_ADC))
@@ -763,21 +764,20 @@ static int axp20x_usb_power_probe(struct platform_device 
*pdev)
ret = devm_request_any_context_irq(>dev, power->irqs[i],
   axp20x_usb_power_irq, 0,
   DRVNAME, power);
if (ret < 0) {
dev_err(>dev, "Error requesting %s IRQ: %d\n",
axp_data->irq_names[i], ret);
return ret;
}
}
 
-   INIT_DELAYED_WORK(>vbus_detect, axp20x_usb_power_poll_vbus);
if (axp20x_usb_vbus_needs_polling(power))
queue_delayed_work(system_power_efficient_wq, 
>vbus_detect, 0);
 
return 0;
 }
 
 static int axp20x_usb_power_remove(struct platform_device *pdev)
 {
struct axp20x_usb_power *power = platform_get_drvdata(pdev);
 
-- 
2.26.2



Re: [PATCH v5 00/10] sunxi: Support IRQ wakeup from deep sleep

2021-01-22 Thread Samuel Holland
On 1/22/21 4:47 AM, Maxime Ripard wrote:
> On Thu, Jan 21, 2021 at 07:33:54PM -0600, Samuel Holland wrote:
>> On 1/21/21 2:35 PM, Marc Zyngier wrote:
>>> On Sun, 17 Jan 2021 23:50:30 -0600, Samuel Holland wrote:
>>>> Allwinner sun6i/sun8i/sun50i SoCs (A31 and newer) have two interrupt
>>>> controllers: GIC and R_INTC. GIC does not support wakeup. R_INTC handles
>>>> the external NMI pin, and provides 32+ IRQs to the ARISC. The first 16
>>>> of these correspond 1:1 to a block of GIC IRQs starting with the NMI.
>>>> The last 13-16 multiplex the first (up to) 128 GIC SPIs.
>>>>
>>>> This series replaces the existing chained irqchip driver that could only
>>>> control the NMI, with a stacked irqchip driver that also provides wakeup
>>>> capability for those multiplexed SPI IRQs. The idea is to preconfigure
>>>> the ARISC's IRQ controller, and then the ARISC firmware knows to wake up
>>>> as soon as it receives an IRQ. It can also decide how deep it can
>>>> suspend based on the enabled wakeup IRQs.
>>>>
>>>> [...]
>>>
>>> Applied to irq/irqchip-5.12, thanks!
>>>
>>> [01/10] dt-bindings: irq: sun6i-r: Split the binding from sun7i-nmi
>>> commit: ad6b47cdef760410311f41876b21eb0c6fda4717
>>> [02/10] dt-bindings: irq: sun6i-r: Add a compatible for the H3
>>> commit: 6436eb4417094ea3308b33d8392fc02a1068dc78
>>> [03/10] irqchip/sun6i-r: Use a stacked irqchip driver
>>> commit: 4e34614636b31747b190488240a95647c227021f
>>> [04/10] irqchip/sun6i-r: Add wakeup support
>>> commit: 7ab365f6cd6de1e2b0cb1e1e3873dbf68e6f1003
>>>
>>> Please route the dts patches via the soc tree. Also, I had to
>>> manually fix the first patch as it wouldn't apply on top of
>>> 5.11-rc4 (which tree has it been diffed against?). Please
>>> check that the resolution is correct.
>>
>> This series was based on sunxi/for-next, which contains commit
>> 752b0aac99c7 ("dt-bindings: irq: sun7i-nmi: Add binding documentation
>> for the V3s NMI")[1].
> 
> I assume merging the DT bits alone would break things? If so, I guess we
> can wait for 5.12 to be released before merging it

Patch 5 does not depend on the new driver, so it could be merged at any
time. Yes, the remaining patches would break things if merged without
the driver.

Cheers,
Samuel


Re: [PATCH v5 00/10] sunxi: Support IRQ wakeup from deep sleep

2021-01-21 Thread Samuel Holland
On 1/21/21 2:35 PM, Marc Zyngier wrote:
> On Sun, 17 Jan 2021 23:50:30 -0600, Samuel Holland wrote:
>> Allwinner sun6i/sun8i/sun50i SoCs (A31 and newer) have two interrupt
>> controllers: GIC and R_INTC. GIC does not support wakeup. R_INTC handles
>> the external NMI pin, and provides 32+ IRQs to the ARISC. The first 16
>> of these correspond 1:1 to a block of GIC IRQs starting with the NMI.
>> The last 13-16 multiplex the first (up to) 128 GIC SPIs.
>>
>> This series replaces the existing chained irqchip driver that could only
>> control the NMI, with a stacked irqchip driver that also provides wakeup
>> capability for those multiplexed SPI IRQs. The idea is to preconfigure
>> the ARISC's IRQ controller, and then the ARISC firmware knows to wake up
>> as soon as it receives an IRQ. It can also decide how deep it can
>> suspend based on the enabled wakeup IRQs.
>>
>> [...]
> 
> Applied to irq/irqchip-5.12, thanks!
> 
> [01/10] dt-bindings: irq: sun6i-r: Split the binding from sun7i-nmi
> commit: ad6b47cdef760410311f41876b21eb0c6fda4717
> [02/10] dt-bindings: irq: sun6i-r: Add a compatible for the H3
> commit: 6436eb4417094ea3308b33d8392fc02a1068dc78
> [03/10] irqchip/sun6i-r: Use a stacked irqchip driver
> commit: 4e34614636b31747b190488240a95647c227021f
> [04/10] irqchip/sun6i-r: Add wakeup support
> commit: 7ab365f6cd6de1e2b0cb1e1e3873dbf68e6f1003
> 
> Please route the dts patches via the soc tree. Also, I had to
> manually fix the first patch as it wouldn't apply on top of
> 5.11-rc4 (which tree has it been diffed against?). Please
> check that the resolution is correct.

This series was based on sunxi/for-next, which contains commit
752b0aac99c7 ("dt-bindings: irq: sun7i-nmi: Add binding documentation
for the V3s NMI")[1].

[1]:
https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux.git/commit/?h=sunxi/for-next=752b0aac99c7e0b179875cdfa102d378ccb794a2

> Cheers,
> 
>   M.
> 



Re: [PATCH v2 0/4] media: sunxi-cir: Cleanup and power management

2021-01-17 Thread Samuel Holland
On 1/18/21 12:00 AM, Samuel Holland wrote:
> This series cleans up some dead code in the sunxi-cir driver and adds
> system power management hooks.
> 
> ---
> Changes from v1:
>   - Unregister the RC device first thing in sunxi_ir_remove() [3]

I forgot to add:

Acked-by: Maxime Ripard 

from v1.

> Samuel Holland (4):
>   media: sunxi-cir: Clean up dead register writes
>   media: sunxi-cir: Remove unnecessary spinlock
>   media: sunxi-cir: Factor out hardware initialization
>   media: sunxi-cir: Implement suspend/resume/shutdown callbacks
> 
>  drivers/media/rc/sunxi-cir.c | 169 ---
>  1 file changed, 95 insertions(+), 74 deletions(-)
> 



[PATCH v2 0/4] media: sunxi-cir: Cleanup and power management

2021-01-17 Thread Samuel Holland
This series cleans up some dead code in the sunxi-cir driver and adds
system power management hooks.

---
Changes from v1:
  - Unregister the RC device first thing in sunxi_ir_remove() [3]

Samuel Holland (4):
  media: sunxi-cir: Clean up dead register writes
  media: sunxi-cir: Remove unnecessary spinlock
  media: sunxi-cir: Factor out hardware initialization
  media: sunxi-cir: Implement suspend/resume/shutdown callbacks

 drivers/media/rc/sunxi-cir.c | 169 ---
 1 file changed, 95 insertions(+), 74 deletions(-)

-- 
2.26.2



[PATCH v2 3/4] media: sunxi-cir: Factor out hardware initialization

2021-01-17 Thread Samuel Holland
In preparation for adding suspend/resume hooks, factor out the hardware
initialization from the driver probe/remove functions.

The timeout programmed during init is taken from the `struct rc_dev` so
it is maintained across an exit/init cycle.

This resolves some trivial issues with the probe function: throwing away
the error from clk_prepare_enable and using the wrong type for the
temporary register value.

It also fixes the order of the remove function to unregister the RC
device before turning off the hardware. This prevents userspace from
triggering register writes (via LIRC_SET_REC_TIMEOUT) while the hardware
is disabled.

Signed-off-by: Samuel Holland 
---
 drivers/media/rc/sunxi-cir.c | 128 ---
 1 file changed, 74 insertions(+), 54 deletions(-)

diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 48be400421cd..2d099da8d3cc 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -169,10 +169,74 @@ static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, 
unsigned int timeout)
return 0;
 }
 
+static int sunxi_ir_hw_init(struct device *dev)
+{
+   struct sunxi_ir *ir = dev_get_drvdata(dev);
+   u32 tmp;
+   int ret;
+
+   ret = reset_control_deassert(ir->rst);
+   if (ret)
+   return ret;
+
+   ret = clk_prepare_enable(ir->apb_clk);
+   if (ret) {
+   dev_err(dev, "failed to enable apb clk\n");
+   goto exit_assert_reset;
+   }
+
+   ret = clk_prepare_enable(ir->clk);
+   if (ret) {
+   dev_err(dev, "failed to enable ir clk\n");
+   goto exit_disable_apb_clk;
+   }
+
+   /* Enable CIR Mode */
+   writel(REG_CTL_MD, ir->base + SUNXI_IR_CTL_REG);
+
+   /* Set noise threshold and idle threshold */
+   sunxi_ir_set_timeout(ir->rc, ir->rc->timeout);
+
+   /* Invert Input Signal */
+   writel(REG_RXCTL_RPPI, ir->base + SUNXI_IR_RXCTL_REG);
+
+   /* Clear All Rx Interrupt Status */
+   writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
+
+   /*
+* Enable IRQ on overflow, packet end, FIFO available with trigger
+* level
+*/
+   writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN |
+  REG_RXINT_RAI_EN | REG_RXINT_RAL(ir->fifo_size / 2 - 1),
+  ir->base + SUNXI_IR_RXINT_REG);
+
+   /* Enable IR Module */
+   tmp = readl(ir->base + SUNXI_IR_CTL_REG);
+   writel(tmp | REG_CTL_GEN | REG_CTL_RXEN, ir->base + SUNXI_IR_CTL_REG);
+
+   return 0;
+
+exit_disable_apb_clk:
+   clk_disable_unprepare(ir->apb_clk);
+exit_assert_reset:
+   reset_control_assert(ir->rst);
+
+   return ret;
+}
+
+static void sunxi_ir_hw_exit(struct device *dev)
+{
+   struct sunxi_ir *ir = dev_get_drvdata(dev);
+
+   clk_disable_unprepare(ir->clk);
+   clk_disable_unprepare(ir->apb_clk);
+   reset_control_assert(ir->rst);
+}
+
 static int sunxi_ir_probe(struct platform_device *pdev)
 {
int ret = 0;
-   unsigned long tmp = 0;
 
struct device *dev = >dev;
struct device_node *dn = dev->of_node;
@@ -213,43 +277,26 @@ static int sunxi_ir_probe(struct platform_device *pdev)
ir->rst = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(ir->rst))
return PTR_ERR(ir->rst);
-   ret = reset_control_deassert(ir->rst);
-   if (ret)
-   return ret;
}
 
ret = clk_set_rate(ir->clk, b_clk_freq);
if (ret) {
dev_err(dev, "set ir base clock failed!\n");
-   goto exit_reset_assert;
+   return ret;
}
dev_dbg(dev, "set base clock frequency to %d Hz.\n", b_clk_freq);
 
-   if (clk_prepare_enable(ir->apb_clk)) {
-   dev_err(dev, "try to enable apb_ir_clk failed\n");
-   ret = -EINVAL;
-   goto exit_reset_assert;
-   }
-
-   if (clk_prepare_enable(ir->clk)) {
-   dev_err(dev, "try to enable ir_clk failed\n");
-   ret = -EINVAL;
-   goto exit_clkdisable_apb_clk;
-   }
-
/* IO */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ir->base = devm_ioremap_resource(dev, res);
if (IS_ERR(ir->base)) {
-   ret = PTR_ERR(ir->base);
-   goto exit_clkdisable_clk;
+   return PTR_ERR(ir->base);
}
 
ir->rc = rc_allocate_device(RC_DRIVER_IR_RAW);
if (!ir->rc) {
dev_err(dev, "failed to allocate device\n");
-   ret = -ENOMEM;
-   goto exit_clkdisable_clk;
+   return -ENOMEM;
}
 
ir->rc->priv = ir;
@@ -265,6 +312,7 @@ static int sunxi_

[PATCH v2 2/4] media: sunxi-cir: Remove unnecessary spinlock

2021-01-17 Thread Samuel Holland
Only one register, SUNXI_IR_CIR_REG, is accessed from outside the
interrupt handler, and that register is not accessed from inside it.
As there is no overlap between different contexts, no lock is needed.

Signed-off-by: Samuel Holland 
---
 drivers/media/rc/sunxi-cir.c | 10 --
 1 file changed, 10 deletions(-)

diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 0a7f7eab3cc3..48be400421cd 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -86,7 +86,6 @@ struct sunxi_ir_quirks {
 };
 
 struct sunxi_ir {
-   spinlock_t  ir_lock;
struct rc_dev   *rc;
void __iomem*base;
int irq;
@@ -105,8 +104,6 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id)
struct sunxi_ir *ir = dev_id;
struct ir_raw_event rawir = {};
 
-   spin_lock(>ir_lock);
-
status = readl(ir->base + SUNXI_IR_RXSTA_REG);
 
/* clean all pending statuses */
@@ -137,8 +134,6 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id)
ir_raw_event_handle(ir->rc);
}
 
-   spin_unlock(>ir_lock);
-
return IRQ_HANDLED;
 }
 
@@ -160,17 +155,14 @@ static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, 
unsigned int timeout)
 {
struct sunxi_ir *ir = rc_dev->priv;
unsigned int base_clk = clk_get_rate(ir->clk);
-   unsigned long flags;
 
unsigned int ithr = sunxi_usec_to_ithr(base_clk, timeout);
 
dev_dbg(rc_dev->dev.parent, "setting idle threshold to %u\n", ithr);
 
-   spin_lock_irqsave(>ir_lock, flags);
/* Set noise threshold and idle threshold */
writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr),
   ir->base + SUNXI_IR_CIR_REG);
-   spin_unlock_irqrestore(>ir_lock, flags);
 
rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ithr);
 
@@ -199,8 +191,6 @@ static int sunxi_ir_probe(struct platform_device *pdev)
return -ENODEV;
}
 
-   spin_lock_init(>ir_lock);
-
ir->fifo_size = quirks->fifo_size;
 
/* Clock */
-- 
2.26.2



[PATCH v2 1/4] media: sunxi-cir: Clean up dead register writes

2021-01-17 Thread Samuel Holland
The register writes during driver removal occur after the device is
already put back in reset, so they never had any effect.

Signed-off-by: Samuel Holland 
---
 drivers/media/rc/sunxi-cir.c | 10 --
 1 file changed, 10 deletions(-)

diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 8555c7798706..0a7f7eab3cc3 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -342,22 +342,12 @@ static int sunxi_ir_probe(struct platform_device *pdev)
 
 static int sunxi_ir_remove(struct platform_device *pdev)
 {
-   unsigned long flags;
struct sunxi_ir *ir = platform_get_drvdata(pdev);
 
clk_disable_unprepare(ir->clk);
clk_disable_unprepare(ir->apb_clk);
reset_control_assert(ir->rst);
 
-   spin_lock_irqsave(>ir_lock, flags);
-   /* disable IR IRQ */
-   writel(0, ir->base + SUNXI_IR_RXINT_REG);
-   /* clear All Rx Interrupt Status */
-   writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
-   /* disable IR */
-   writel(0, ir->base + SUNXI_IR_CTL_REG);
-   spin_unlock_irqrestore(>ir_lock, flags);
-
rc_unregister_device(ir->rc);
return 0;
 }
-- 
2.26.2



[PATCH v2 4/4] media: sunxi-cir: Implement suspend/resume/shutdown callbacks

2021-01-17 Thread Samuel Holland
To save power, gate/reset the hardware block while the system is
asleep or powered off.

Signed-off-by: Samuel Holland 
---
 drivers/media/rc/sunxi-cir.c | 21 +
 1 file changed, 21 insertions(+)

diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 2d099da8d3cc..168e1d2c876a 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -234,6 +234,20 @@ static void sunxi_ir_hw_exit(struct device *dev)
reset_control_assert(ir->rst);
 }
 
+static int __maybe_unused sunxi_ir_suspend(struct device *dev)
+{
+   sunxi_ir_hw_exit(dev);
+
+   return 0;
+}
+
+static int __maybe_unused sunxi_ir_resume(struct device *dev)
+{
+   return sunxi_ir_hw_init(dev);
+}
+
+static SIMPLE_DEV_PM_OPS(sunxi_ir_pm_ops, sunxi_ir_suspend, sunxi_ir_resume);
+
 static int sunxi_ir_probe(struct platform_device *pdev)
 {
int ret = 0;
@@ -362,6 +376,11 @@ static int sunxi_ir_remove(struct platform_device *pdev)
return 0;
 }
 
+static void sunxi_ir_shutdown(struct platform_device *pdev)
+{
+   sunxi_ir_hw_exit(>dev);
+}
+
 static const struct sunxi_ir_quirks sun4i_a10_ir_quirks = {
.has_reset = false,
.fifo_size = 16,
@@ -397,9 +416,11 @@ MODULE_DEVICE_TABLE(of, sunxi_ir_match);
 static struct platform_driver sunxi_ir_driver = {
.probe  = sunxi_ir_probe,
.remove = sunxi_ir_remove,
+   .shutdown   = sunxi_ir_shutdown,
.driver = {
.name = SUNXI_IR_DEV,
.of_match_table = sunxi_ir_match,
+   .pm = _ir_pm_ops,
},
 };
 
-- 
2.26.2



[PATCH v5 08/10] ARM: dts: sunxi: Move wakeup-capable IRQs to r_intc

2021-01-17 Thread Samuel Holland
All IRQs that can be used to wake up the system must be routed through
r_intc, so they are visible to firmware while the system is suspended.

In addition to the external NMI input, which is already routed through
r_intc, these include PIO and R_PIO (gpio-keys), the LRADC, and the RTC.

Acked-by: Maxime Ripard 
Signed-off-by: Samuel Holland 
---
 arch/arm/boot/dts/sun6i-a31.dtsi | 4 
 arch/arm/boot/dts/sun8i-a23-a33.dtsi | 4 
 arch/arm/boot/dts/sun8i-a83t.dtsi| 3 +++
 arch/arm/boot/dts/sunxi-h3-h5.dtsi   | 3 +++
 4 files changed, 14 insertions(+)

diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index faf85c5f4e1e..50324688c28a 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -611,6 +611,7 @@ ccu: clock@1c2 {
pio: pinctrl@1c20800 {
compatible = "allwinner,sun6i-a31-pinctrl";
reg = <0x01c20800 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ,
 ,
@@ -802,6 +803,7 @@ i2s1: i2s@1c22400 {
lradc: lradc@1c22800 {
compatible = "allwinner,sun4i-a10-lradc-keys";
reg = <0x01c22800 0x100>;
+   interrupt-parent = <_intc>;
interrupts = ;
status = "disabled";
};
@@ -1299,6 +1301,7 @@ rtc: rtc@1f0 {
#clock-cells = <1>;
compatible = "allwinner,sun6i-a31-rtc";
reg = <0x01f0 0x54>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ;
clocks = <>;
@@ -1383,6 +1386,7 @@ ir: ir@1f02000 {
r_pio: pinctrl@1f02c00 {
compatible = "allwinner,sun6i-a31-r-pinctrl";
reg = <0x01f02c00 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ;
clocks = <_gates 0>, <>, < 0>;
diff --git a/arch/arm/boot/dts/sun8i-a23-a33.dtsi 
b/arch/arm/boot/dts/sun8i-a23-a33.dtsi
index a84c90a660ca..4461d5098b20 100644
--- a/arch/arm/boot/dts/sun8i-a23-a33.dtsi
+++ b/arch/arm/boot/dts/sun8i-a23-a33.dtsi
@@ -338,6 +338,7 @@ ccu: clock@1c2 {
pio: pinctrl@1c20800 {
/* compatible gets set in SoC specific dtsi file */
reg = <0x01c20800 0x400>;
+   interrupt-parent = <_intc>;
/* interrupts get set in SoC specific dtsi file */
clocks = < CLK_BUS_PIO>, <>, < 0>;
clock-names = "apb", "hosc", "losc";
@@ -473,6 +474,7 @@ pwm: pwm@1c21400 {
lradc: lradc@1c22800 {
compatible = "allwinner,sun4i-a10-lradc-keys";
reg = <0x01c22800 0x100>;
+   interrupt-parent = <_intc>;
interrupts = ;
status = "disabled";
};
@@ -709,6 +711,7 @@ drc0_out_tcon0: endpoint {
rtc: rtc@1f0 {
compatible = "allwinner,sun8i-a23-rtc";
reg = <0x01f0 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ;
clock-output-names = "osc32k", "osc32k-out";
@@ -805,6 +808,7 @@ r_i2c: i2c@1f02400 {
r_pio: pinctrl@1f02c00 {
compatible = "allwinner,sun8i-a23-r-pinctrl";
reg = <0x01f02c00 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ;
clocks = <_gates 0>, <>, < 0>;
clock-names = "apb", "hosc", "losc";
diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi 
b/arch/arm/boot/dts/sun8i-a83t.dtsi
index 0fce227f56d4..2c05e296b3d8 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -708,6 +708,7 @@ ccu: clock@1c2 {
 
pio: pinctrl@1c20800 {
compatible = "allwinner,sun8i-a83t-pinctrl";
+   interrupt-parent = <_intc>;
interrupts = ,
 ,
 ;
@@ -1150,6 +1151,7 @@ r_cir: ir@1f02000 {
   

[PATCH v5 05/10] ARM: dts: sunxi: Rename nmi_intc to r_intc

2021-01-17 Thread Samuel Holland
The R_INTC block controls more than just the NMI, and it is a different
hardware block than the NMI INTC found in some other Allwinner SoCs, so
the label "nmi_intc" is inaccurate. Name it "r_intc" to match the
compatible and to match the few references in the vendor documentation.

Acked-by: Maxime Ripard 
Signed-off-by: Samuel Holland 
---
 arch/arm/boot/dts/sun6i-a31-hummingbird.dts  | 2 +-
 arch/arm/boot/dts/sun6i-a31-m9.dts   | 2 +-
 arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts | 2 +-
 arch/arm/boot/dts/sun6i-a31.dtsi | 2 +-
 arch/arm/boot/dts/sun6i-a31s-primo81.dts | 2 +-
 arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi   | 2 +-
 arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts | 2 +-
 arch/arm/boot/dts/sun6i-a31s-yones-toptech-bs1078-v2.dts | 2 +-
 arch/arm/boot/dts/sun6i-reference-design-tablet.dtsi | 2 +-
 arch/arm/boot/dts/sun8i-a23-a33.dtsi | 2 +-
 arch/arm/boot/dts/sun8i-a33-olinuxino.dts| 2 +-
 arch/arm/boot/dts/sun8i-a33-sinlinx-sina33.dts   | 2 +-
 arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts | 2 +-
 arch/arm/boot/dts/sun8i-r16-parrot.dts   | 2 +-
 arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi | 2 +-
 15 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts 
b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
index 73de34ae37fd..486cec6f71e0 100644
--- a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
+++ b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
@@ -226,7 +226,7 @@  {
axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
-   interrupt-parent = <_intc>;
+   interrupt-parent = <_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
x-powers,drive-vbus-en;
};
diff --git a/arch/arm/boot/dts/sun6i-a31-m9.dts 
b/arch/arm/boot/dts/sun6i-a31-m9.dts
index a645c8f4257c..6aeb5a9696f7 100644
--- a/arch/arm/boot/dts/sun6i-a31-m9.dts
+++ b/arch/arm/boot/dts/sun6i-a31-m9.dts
@@ -115,7 +115,7 @@  {
axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
-   interrupt-parent = <_intc>;
+   interrupt-parent = <_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
};
 };
diff --git a/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts 
b/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts
index 648f24746234..6c6c1bd22bf6 100644
--- a/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts
+++ b/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts
@@ -115,7 +115,7 @@  {
axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
-   interrupt-parent = <_intc>;
+   interrupt-parent = <_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
};
 };
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index f3425a66fc0a..6a733a36d34a 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -1305,7 +1305,7 @@ rtc: rtc@1f0 {
clock-output-names = "osc32k";
};
 
-   nmi_intc: interrupt-controller@1f00c00 {
+   r_intc: interrupt-controller@1f00c00 {
compatible = "allwinner,sun6i-a31-r-intc";
interrupt-controller;
#interrupt-cells = <2>;
diff --git a/arch/arm/boot/dts/sun6i-a31s-primo81.dts 
b/arch/arm/boot/dts/sun6i-a31s-primo81.dts
index bc3170a0b8b5..429a165b79b2 100644
--- a/arch/arm/boot/dts/sun6i-a31s-primo81.dts
+++ b/arch/arm/boot/dts/sun6i-a31s-primo81.dts
@@ -159,7 +159,7 @@  {
axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
-   interrupt-parent = <_intc>;
+   interrupt-parent = <_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
x-powers,drive-vbus-en;
};
diff --git a/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi 
b/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi
index 3099491de8c4..7455c0db4a8a 100644
--- a/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi
@@ -78,7 +78,7 @@  {
axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
-   interrupt-parent = <_intc>;
+   interrupt-parent = <_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
};
 };
diff --git a/arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts 
b/arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts
index 367006fb

[PATCH v5 09/10] arm64: dts: allwinner: Use the new r_intc binding

2021-01-17 Thread Samuel Holland
The binding of R_INTC was updated to allow specifying interrupts other
than the external NMI, since routing those interrupts through the R_INTC
driver allows using them for wakeup.

Update the device trees to use the new binding.

Acked-by: Maxime Ripard 
Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64-amarula-relic.dts | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts  | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts| 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts  | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts| 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-pinebook.dts  | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi| 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts   | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-sopine.dtsi   | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-teres-i.dts   | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi  | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-h6-beelink-gs1.dts| 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi.dtsi  | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts   | 4 ++--
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi   | 5 ++---
 17 files changed, 19 insertions(+), 20 deletions(-)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-amarula-relic.dts 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-amarula-relic.dts
index c7bd73f35ed8..f17cc89f472d 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-amarula-relic.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-amarula-relic.dts
@@ -173,7 +173,7 @@ axp803: pmic@3a3 {
compatible = "x-powers,axp803";
reg = <0x3a3>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
x-powers,drive-vbus-en; /* set N_VBUSEN as output pin */
};
 };
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts
index e5e840b9fbb4..e45bef292aa3 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts
@@ -191,7 +191,7 @@ axp803: pmic@3a3 {
compatible = "x-powers,axp803";
reg = <0x3a3>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
x-powers,drive-vbus-en; /* set N_VBUSEN as output pin */
};
 };
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts
index e58db8a6cab6..57b64c57781b 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts
@@ -152,7 +152,7 @@ axp803: pmic@3a3 {
compatible = "x-powers,axp803";
reg = <0x3a3>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
};
 };
 
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts
index f3f8e177ab61..ec7e2c0e82c1 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts
@@ -185,7 +185,7 @@ axp803: pmic@3a3 {
compatible = "x-powers,axp803";
reg = <0x3a3>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
x-powers,drive-vbus-en; /* set N_VBUSEN as output pin */
};
 };
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts
index 70e31743f0ba..097a5511523a 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts
@@ -192,7 +192,7 @@ axp803: pmic@3a3 {
compatible = "x-powers,axp803";
reg = <0x3a3>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
x-powers,drive-vbus-en; /* set N_VBUSEN as output pin */
};
 };
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
index 329cf276561e..2accb5ddf783 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
@@ -139,7 

[PATCH v5 10/10] arm64: dts: allwinner: Move wakeup-capable IRQs to r_intc

2021-01-17 Thread Samuel Holland
All IRQs that can be used to wake up the system must be routed through
r_intc, so they are visible to firmware while the system is suspended.

In addition to the external NMI input, which is already routed through
r_intc, these include PIO and R_PIO (gpio-keys), the LRADC, and the RTC.

Acked-by: Maxime Ripard 
Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 4 
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi  | 3 +++
 2 files changed, 7 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 5bf9d61fab12..5b30e6c1fa05 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -648,6 +648,7 @@ ccu: clock@1c2 {
pio: pinctrl@1c20800 {
compatible = "allwinner,sun50i-a64-pinctrl";
reg = <0x01c20800 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ,
 ;
@@ -818,6 +819,7 @@ lradc: lradc@1c21800 {
compatible = "allwinner,sun50i-a64-lradc",
 "allwinner,sun8i-a83t-r-lradc";
reg = <0x01c21800 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ;
status = "disabled";
};
@@ -1208,6 +1210,7 @@ rtc: rtc@1f0 {
compatible = "allwinner,sun50i-a64-rtc",
 "allwinner,sun8i-h3-rtc";
reg = <0x01f0 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ;
clock-output-names = "osc32k", "osc32k-out", "iosc";
@@ -1279,6 +1282,7 @@ r_pwm: pwm@1f03800 {
r_pio: pinctrl@1f02c00 {
compatible = "allwinner,sun50i-a64-r-pinctrl";
reg = <0x01f02c00 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ;
clocks = <_ccu CLK_APB0_PIO>, <>, <>;
clock-names = "apb", "hosc", "losc";
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
index 15babb635cf7..4af06f49daa7 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -294,6 +294,7 @@ pwm: pwm@300a000 {
pio: pinctrl@300b000 {
compatible = "allwinner,sun50i-h6-pinctrl";
reg = <0x0300b000 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ,
 ,
@@ -902,6 +903,7 @@ tcon_tv_out_tcon_top: endpoint@1 {
rtc: rtc@700 {
compatible = "allwinner,sun50i-h6-rtc";
reg = <0x0700 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ;
clock-output-names = "osc32k", "osc32k-out", "iosc";
@@ -937,6 +939,7 @@ r_intc: interrupt-controller@7021000 {
r_pio: pinctrl@7022000 {
compatible = "allwinner,sun50i-h6-r-pinctrl";
reg = <0x07022000 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ;
clocks = <_ccu CLK_R_APB1>, <>, < 0>;
-- 
2.26.2



[PATCH v5 03/10] irqchip/sun6i-r: Use a stacked irqchip driver

2021-01-17 Thread Samuel Holland
The R_INTC in the A31 and newer sun8i/sun50i SoCs is more similar to the
original sun4i interrupt controller than the sun7i/sun9i NMI controller.
It is used for two distinct purposes:
 - To control the trigger, latch, and mask for the NMI input pin
 - To provide the interrupt input for the ARISC coprocessor

As this interrupt controller is not documented, information about it
comes from vendor-provided firmware blobs and from experimentation.

Differences from the sun4i interrupt controller appear to be:
 - It only has one or two registers of each kind (max 32 or 64 IRQs)
 - Multiplexing logic is added to support additional inputs
 - There is no FIQ-related logic
 - There is no interrupt priority logic

In order to fulfill its two purposes, this hardware block combines four
types of IRQs. First, the NMI pin is routed to the "IRQ 0" input on this
chip, with a trigger type controlled by the NMI_CTRL_REG. The "IRQ 0
pending" output from this chip, if enabled, is then routed to a SPI IRQ
input on the GIC. In other words, bit 0 of IRQ_ENABLE_REG *does* affect
the NMI IRQ seen at the GIC.

The NMI is followed by a contiguous block of 15 "direct" (my name for
them) IRQ inputs that are connected in parallel to both R_INTC and the
GIC. Or in other words, these bits of IRQ_ENABLE_REG *do not* affect the
IRQs seen at the GIC.

Following the direct IRQs are the ARISC's copy of banked IRQs for shared
peripherals. These are not relevant to Linux. The remaining IRQs are
connected to a multiplexer and provide access to the first (up to) 128
SPIs from the ARISC. This range of SPIs overlaps with the direct IRQs.

Because of the 1:1 correspondence between R_INTC and GIC inputs, this is
a perfect scenario for using a stacked irqchip driver. We want to hook
into setting the NMI trigger type, but not actually handle any IRQ here.

To allow access to all multiplexed IRQs, this driver requires a new
binding where the interrupt number matches the GIC interrupt number.
(This moves the NMI from number 0 to 32 or 96, depending on the SoC.)
For simplicity, copy the three-cell GIC binding; this disambiguates
interrupt 0 in the old binding (the NMI) from interrupt 0 in the new
binding (SPI 0) by the number of cells.

Since R_INTC is in the always-on power domain, and its output is visible
to the power management coprocessor, a stacked irqchip driver provides a
simple way to add wakeup support to any of its IRQs. That is the next
patch; for now, just the NMI is moved over.

This commit mostly reverts commit 173bda53b340 ("irqchip/sunxi-nmi:
Support sun6i-a31-r-intc compatible").

Acked-by: Maxime Ripard 
Signed-off-by: Samuel Holland 
---
 arch/arm/mach-sunxi/Kconfig |   2 +
 arch/arm64/Kconfig.platforms|   2 +
 drivers/irqchip/Makefile|   1 +
 drivers/irqchip/irq-sun6i-r.c   | 284 
 drivers/irqchip/irq-sunxi-nmi.c |  26 +--
 5 files changed, 292 insertions(+), 23 deletions(-)
 create mode 100644 drivers/irqchip/irq-sun6i-r.c

diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index eeadb1a4dcfe..e5c2fce281cd 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -6,6 +6,8 @@ menuconfig ARCH_SUNXI
select CLKSRC_MMIO
select GENERIC_IRQ_CHIP
select GPIOLIB
+   select IRQ_DOMAIN_HIERARCHY
+   select IRQ_FASTEOI_HIERARCHY_HANDLERS
select PINCTRL
select PM_OPP
select SUN4I_TIMER
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 6eecdef538bd..f2aa1518c6f4 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -17,6 +17,8 @@ config ARCH_SUNXI
bool "Allwinner sunxi 64-bit SoC Family"
select ARCH_HAS_RESET_CONTROLLER
select GENERIC_IRQ_CHIP
+   select IRQ_DOMAIN_HIERARCHY
+   select IRQ_FASTEOI_HIERARCHY_HANDLERS
select PINCTRL
select RESET_CONTROLLER
help
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 0ac93bfaec61..95221e74ee99 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_OR1K_PIC)+= 
irq-or1k-pic.o
 obj-$(CONFIG_ORION_IRQCHIP)+= irq-orion.o
 obj-$(CONFIG_OMAP_IRQCHIP) += irq-omap-intc.o
 obj-$(CONFIG_ARCH_SUNXI)   += irq-sun4i.o
+obj-$(CONFIG_ARCH_SUNXI)   += irq-sun6i-r.o
 obj-$(CONFIG_ARCH_SUNXI)   += irq-sunxi-nmi.o
 obj-$(CONFIG_ARCH_SPEAR3XX)+= spear-shirq.o
 obj-$(CONFIG_ARM_GIC)  += irq-gic.o irq-gic-common.o
diff --git a/drivers/irqchip/irq-sun6i-r.c b/drivers/irqchip/irq-sun6i-r.c
new file mode 100644
index ..284b56905eb7
--- /dev/null
+++ b/drivers/irqchip/irq-sun6i-r.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * The R_INTC in Allwinner A31 and newer SoCs manages several types of
+ * inter

[PATCH v5 07/10] ARM: dts: sunxi: h3/h5: Add r_intc node

2021-01-17 Thread Samuel Holland
The H3 and H5 SoCs have an additional interrupt controller in the RTC
power domain that can be used to enable wakeup for certain IRQs.

Add a node for it.

Acked-by: Maxime Ripard 
Signed-off-by: Samuel Holland 
---
 arch/arm/boot/dts/sunxi-h3-h5.dtsi | 9 +
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi 
b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
index 9be13378d4df..4bf25c5b873e 100644
--- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
+++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
@@ -859,6 +859,15 @@ rtc: rtc@1f0 {
#clock-cells = <1>;
};
 
+   r_intc: interrupt-controller@1f00c00 {
+   compatible = "allwinner,sun8i-h3-r-intc",
+"allwinner,sun6i-a31-r-intc";
+   interrupt-controller;
+   #interrupt-cells = <3>;
+   reg = <0x01f00c00 0x400>;
+   interrupts = ;
+   };
+
r_ccu: clock@1f01400 {
compatible = "allwinner,sun8i-h3-r-ccu";
reg = <0x01f01400 0x100>;
-- 
2.26.2



[PATCH v5 06/10] ARM: dts: sunxi: Use the new r_intc binding

2021-01-17 Thread Samuel Holland
The binding of R_INTC was updated to allow specifying interrupts other
than the external NMI, since routing those interrupts through the R_INTC
driver allows using them for wakeup.

Update the device trees to use the new binding.

Acked-by: Maxime Ripard 
Signed-off-by: Samuel Holland 
---
 arch/arm/boot/dts/sun6i-a31-hummingbird.dts  | 2 +-
 arch/arm/boot/dts/sun6i-a31-m9.dts   | 2 +-
 arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts | 2 +-
 arch/arm/boot/dts/sun6i-a31.dtsi | 2 +-
 arch/arm/boot/dts/sun6i-a31s-primo81.dts | 2 +-
 arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi   | 2 +-
 arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts | 2 +-
 arch/arm/boot/dts/sun6i-a31s-yones-toptech-bs1078-v2.dts | 2 +-
 arch/arm/boot/dts/sun6i-reference-design-tablet.dtsi | 2 +-
 arch/arm/boot/dts/sun8i-a23-a33.dtsi | 2 +-
 arch/arm/boot/dts/sun8i-a33-olinuxino.dts| 2 +-
 arch/arm/boot/dts/sun8i-a33-sinlinx-sina33.dts   | 2 +-
 arch/arm/boot/dts/sun8i-a83t-allwinner-h8homlet-v2.dts   | 4 ++--
 arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts | 4 ++--
 arch/arm/boot/dts/sun8i-a83t-cubietruck-plus.dts | 4 ++--
 arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts| 4 ++--
 arch/arm/boot/dts/sun8i-a83t.dtsi| 2 +-
 arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts | 2 +-
 arch/arm/boot/dts/sun8i-r16-parrot.dts   | 2 +-
 arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi | 2 +-
 20 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts 
b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
index 486cec6f71e0..236ebfc06192 100644
--- a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
+++ b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
@@ -227,7 +227,7 @@ axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
x-powers,drive-vbus-en;
};
 };
diff --git a/arch/arm/boot/dts/sun6i-a31-m9.dts 
b/arch/arm/boot/dts/sun6i-a31-m9.dts
index 6aeb5a9696f7..2436b13cbce1 100644
--- a/arch/arm/boot/dts/sun6i-a31-m9.dts
+++ b/arch/arm/boot/dts/sun6i-a31-m9.dts
@@ -116,7 +116,7 @@ axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
};
 };
 
diff --git a/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts 
b/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts
index 6c6c1bd22bf6..ce712bdd8cd0 100644
--- a/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts
+++ b/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts
@@ -116,7 +116,7 @@ axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
};
 };
 
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index 6a733a36d34a..faf85c5f4e1e 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -1308,7 +1308,7 @@ rtc: rtc@1f0 {
r_intc: interrupt-controller@1f00c00 {
compatible = "allwinner,sun6i-a31-r-intc";
interrupt-controller;
-   #interrupt-cells = <2>;
+   #interrupt-cells = <3>;
reg = <0x01f00c00 0x400>;
interrupts = ;
};
diff --git a/arch/arm/boot/dts/sun6i-a31s-primo81.dts 
b/arch/arm/boot/dts/sun6i-a31s-primo81.dts
index 429a165b79b2..c5c85eb44cc7 100644
--- a/arch/arm/boot/dts/sun6i-a31s-primo81.dts
+++ b/arch/arm/boot/dts/sun6i-a31s-primo81.dts
@@ -160,7 +160,7 @@ axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
x-powers,drive-vbus-en;
};
 };
diff --git a/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi 
b/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi
index 7455c0db4a8a..227ad489731c 100644
--- a/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi
@@ -79,7 +79,7 @@ axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts

[PATCH v5 01/10] dt-bindings: irq: sun6i-r: Split the binding from sun7i-nmi

2021-01-17 Thread Samuel Holland
The R_INTC in the A31 and newer sun8i/sun50i SoCs has additional
functionality compared to the sun7i/sun9i NMI controller. Among other
things, it multiplexes access to up to 128 interrupts corresponding to
(and in parallel to) the first 128 GIC SPIs. This means the NMI is no
longer the lowest-numbered hwirq at this irqchip, since it is SPI 32 or
96 (depending on SoC). hwirq 0 now corresponds to SPI 0, usually UART0.

To allow access to all multiplexed IRQs, the R_INTC requires a new
binding where the interrupt number matches the GIC interrupt number.
Otherwise, interrupts with hwirq numbers below the NMI would not be
representable in the device tree.

For simplicity, copy the three-cell GIC binding; this disambiguates
interrupt 0 in the old binding (the NMI) from interrupt 0 in the new
binding (SPI 0) by the number of cells.

Because the H6 R_INTC has a different mapping from multiplexed IRQs to
top-level register bits, it is no longer compatible with the A31 R_INTC.

Acked-by: Maxime Ripard 
Reviewed-by: Rob Herring 
Signed-off-by: Samuel Holland 
---
 .../allwinner,sun6i-a31-r-intc.yaml   | 66 +++
 .../allwinner,sun7i-a20-sc-nmi.yaml   | 10 ---
 2 files changed, 66 insertions(+), 10 deletions(-)
 create mode 100644 
Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml

diff --git 
a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml
 
b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml
new file mode 100644
index ..50e607e607c8
--- /dev/null
+++ 
b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: 
http://devicetree.org/schemas/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner A31 NMI/Wakeup Interrupt Controller Device Tree Bindings
+
+maintainers:
+  - Chen-Yu Tsai 
+  - Maxime Ripard 
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+properties:
+  "#interrupt-cells":
+const: 3
+description:
+  The first cell is GIC_SPI (0), the second cell is the IRQ number, and
+  the third cell is the trigger type as defined in interrupt.txt in this
+  directory.
+
+  compatible:
+oneOf:
+  - const: allwinner,sun6i-a31-r-intc
+  - items:
+  - enum:
+  - allwinner,sun8i-a83t-r-intc
+  - allwinner,sun50i-a64-r-intc
+  - const: allwinner,sun6i-a31-r-intc
+  - const: allwinner,sun50i-h6-r-intc
+
+  reg:
+maxItems: 1
+
+  interrupts:
+maxItems: 1
+description:
+  The GIC interrupt labeled as "External NMI".
+
+  interrupt-controller: true
+
+required:
+  - "#interrupt-cells"
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-controller
+
+additionalProperties: false
+
+examples:
+  - |
+#include 
+
+r_intc: interrupt-controller@1f00c00 {
+compatible = "allwinner,sun50i-a64-r-intc",
+ "allwinner,sun6i-a31-r-intc";
+interrupt-controller;
+#interrupt-cells = <3>;
+reg = <0x01f00c00 0x400>;
+interrupts = ;
+};
+
+...
diff --git 
a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml
 
b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml
index 4fd1e2780026..7fc9ad5ef38c 100644
--- 
a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml
+++ 
b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml
@@ -22,26 +22,16 @@ properties:
 
   compatible:
 oneOf:
-  - const: allwinner,sun6i-a31-r-intc
   - const: allwinner,sun6i-a31-sc-nmi
 deprecated: true
   - const: allwinner,sun7i-a20-sc-nmi
-  - items:
-  - const: allwinner,sun8i-a83t-r-intc
-  - const: allwinner,sun6i-a31-r-intc
   - items:
   - const: allwinner,sun8i-v3s-nmi
   - const: allwinner,sun9i-a80-nmi
   - const: allwinner,sun9i-a80-nmi
-  - items:
-  - const: allwinner,sun50i-a64-r-intc
-  - const: allwinner,sun6i-a31-r-intc
   - items:
   - const: allwinner,sun50i-a100-nmi
   - const: allwinner,sun9i-a80-nmi
-  - items:
-  - const: allwinner,sun50i-h6-r-intc
-  - const: allwinner,sun6i-a31-r-intc
 
   reg:
 maxItems: 1
-- 
2.26.2



[PATCH v5 00/10] sunxi: Support IRQ wakeup from deep sleep

2021-01-17 Thread Samuel Holland
_intc_domain_translate() with
   irq_domain_translate_twocell().
 - Use an enum for the device tree binding.
 - Update commit messages for accuracy and typos.

Samuel Holland (10):
  dt-bindings: irq: sun6i-r: Split the binding from sun7i-nmi
  dt-bindings: irq: sun6i-r: Add a compatible for the H3
  irqchip/sun6i-r: Use a stacked irqchip driver
  irqchip/sun6i-r: Add wakeup support
  ARM: dts: sunxi: Rename nmi_intc to r_intc
  ARM: dts: sunxi: Use the new r_intc binding
  ARM: dts: sunxi: h3/h5: Add r_intc node
  ARM: dts: sunxi: Move wakeup-capable IRQs to r_intc
  arm64: dts: allwinner: Use the new r_intc binding
  arm64: dts: allwinner: Move wakeup-capable IRQs to r_intc

 .../allwinner,sun6i-a31-r-intc.yaml   |  67 
 .../allwinner,sun7i-a20-sc-nmi.yaml   |  10 -
 arch/arm/boot/dts/sun6i-a31-hummingbird.dts   |   4 +-
 arch/arm/boot/dts/sun6i-a31-m9.dts|   4 +-
 .../boot/dts/sun6i-a31-mele-a1000g-quad.dts   |   4 +-
 arch/arm/boot/dts/sun6i-a31.dtsi  |   8 +-
 arch/arm/boot/dts/sun6i-a31s-primo81.dts  |   4 +-
 .../arm/boot/dts/sun6i-a31s-sina31s-core.dtsi |   4 +-
 .../boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts   |   4 +-
 .../sun6i-a31s-yones-toptech-bs1078-v2.dts|   4 +-
 .../dts/sun6i-reference-design-tablet.dtsi|   4 +-
 arch/arm/boot/dts/sun8i-a23-a33.dtsi  |   8 +-
 arch/arm/boot/dts/sun8i-a33-olinuxino.dts |   4 +-
 .../arm/boot/dts/sun8i-a33-sinlinx-sina33.dts |   4 +-
 .../dts/sun8i-a83t-allwinner-h8homlet-v2.dts  |   4 +-
 arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts  |   4 +-
 .../boot/dts/sun8i-a83t-cubietruck-plus.dts   |   4 +-
 arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts |   4 +-
 arch/arm/boot/dts/sun8i-a83t.dtsi |   5 +-
 arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts  |   4 +-
 arch/arm/boot/dts/sun8i-r16-parrot.dts|   4 +-
 .../dts/sun8i-reference-design-tablet.dtsi|   4 +-
 arch/arm/boot/dts/sunxi-h3-h5.dtsi|  12 +
 arch/arm/mach-sunxi/Kconfig   |   2 +
 arch/arm64/Kconfig.platforms  |   2 +
 .../allwinner/sun50i-a64-amarula-relic.dts|   2 +-
 .../dts/allwinner/sun50i-a64-bananapi-m64.dts |   2 +-
 .../dts/allwinner/sun50i-a64-nanopi-a64.dts   |   2 +-
 .../dts/allwinner/sun50i-a64-olinuxino.dts|   2 +-
 .../dts/allwinner/sun50i-a64-orangepi-win.dts |   2 +-
 .../boot/dts/allwinner/sun50i-a64-pine64.dts  |   2 +-
 .../dts/allwinner/sun50i-a64-pinebook.dts |   2 +-
 .../dts/allwinner/sun50i-a64-pinephone.dtsi   |   2 +-
 .../boot/dts/allwinner/sun50i-a64-pinetab.dts |   2 +-
 .../boot/dts/allwinner/sun50i-a64-sopine.dtsi |   2 +-
 .../boot/dts/allwinner/sun50i-a64-teres-i.dts |   2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi |   6 +-
 .../dts/allwinner/sun50i-h6-beelink-gs1.dts   |   2 +-
 .../dts/allwinner/sun50i-h6-orangepi-3.dts|   2 +-
 .../dts/allwinner/sun50i-h6-orangepi.dtsi |   2 +-
 .../boot/dts/allwinner/sun50i-h6-pine-h64.dts |   4 +-
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi  |   8 +-
 drivers/irqchip/Makefile  |   1 +
 drivers/irqchip/irq-sun6i-r.c | 379 ++
 drivers/irqchip/irq-sunxi-nmi.c   |  26 +-
 45 files changed, 542 insertions(+), 92 deletions(-)
 create mode 100644 
Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml
 create mode 100644 drivers/irqchip/irq-sun6i-r.c

-- 
2.26.2



[PATCH v5 04/10] irqchip/sun6i-r: Add wakeup support

2021-01-17 Thread Samuel Holland
Maintain bitmaps of wake-enabled IRQs and mux inputs, and program them
to the hardware during the syscore phase of suspend and shutdown. Then
restore the original set of enabled IRQs (only the NMI) during resume.

This serves two purposes. First, it lets power management firmware
running on the ARISC coprocessor know which wakeup sources Linux wants
to have enabled. That way, it can avoid turning them off when it shuts
down the remainder of the clock tree. Second, it preconfigures the
coprocessor's interrupt controller, so the firmware's wakeup logic
is as simple as waiting for an interrupt to arrive.

The suspend/resume logic is not conditional on PM_SLEEP because it is
identical to the init/shutdown logic. Wake IRQs may be enabled during
shutdown to allow powering the board back on. As an example, see
commit a5c5e50cce9d ("Input: gpio-keys - add shutdown callback").

Acked-by: Maxime Ripard 
Signed-off-by: Samuel Holland 
---
 drivers/irqchip/irq-sun6i-r.c | 107 --
 1 file changed, 101 insertions(+), 6 deletions(-)

diff --git a/drivers/irqchip/irq-sun6i-r.c b/drivers/irqchip/irq-sun6i-r.c
index 284b56905eb7..4cd3e533740b 100644
--- a/drivers/irqchip/irq-sun6i-r.c
+++ b/drivers/irqchip/irq-sun6i-r.c
@@ -39,6 +39,7 @@
  * set of 128 mux bits. This requires a second set of top-level registers.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -46,6 +47,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 
@@ -67,8 +69,17 @@
 #define SUN6I_NR_DIRECT_IRQS   16
 #define SUN6I_NR_MUX_BITS  128
 
+struct sun6i_r_intc_variant {
+   u32 first_mux_irq;
+   u32 nr_mux_irqs;
+   u32 mux_valid[BITS_TO_U32(SUN6I_NR_MUX_BITS)];
+};
+
 static void __iomem *base;
 static irq_hw_number_t nmi_hwirq;
+static DECLARE_BITMAP(wake_irq_enabled, SUN6I_NR_TOP_LEVEL_IRQS);
+static DECLARE_BITMAP(wake_mux_enabled, SUN6I_NR_MUX_BITS);
+static DECLARE_BITMAP(wake_mux_valid, SUN6I_NR_MUX_BITS);
 
 static void sun6i_r_intc_ack_nmi(void)
 {
@@ -145,6 +156,21 @@ static int sun6i_r_intc_nmi_set_irqchip_state(struct 
irq_data *data,
return irq_chip_set_parent_state(data, which, state);
 }
 
+static int sun6i_r_intc_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+   unsigned long offset_from_nmi = data->hwirq - nmi_hwirq;
+
+   if (offset_from_nmi < SUN6I_NR_DIRECT_IRQS)
+   assign_bit(offset_from_nmi, wake_irq_enabled, on);
+   else if (test_bit(data->hwirq, wake_mux_valid))
+   assign_bit(data->hwirq, wake_mux_enabled, on);
+   else
+   /* Not wakeup capable. */
+   return -EPERM;
+
+   return 0;
+}
+
 static struct irq_chip sun6i_r_intc_nmi_chip = {
.name   = "sun6i-r-intc",
.irq_ack= sun6i_r_intc_nmi_ack,
@@ -154,8 +180,19 @@ static struct irq_chip sun6i_r_intc_nmi_chip = {
.irq_set_affinity   = irq_chip_set_affinity_parent,
.irq_set_type   = sun6i_r_intc_nmi_set_type,
.irq_set_irqchip_state  = sun6i_r_intc_nmi_set_irqchip_state,
-   .flags  = IRQCHIP_SET_TYPE_MASKED |
- IRQCHIP_SKIP_SET_WAKE,
+   .irq_set_wake   = sun6i_r_intc_irq_set_wake,
+   .flags  = IRQCHIP_SET_TYPE_MASKED,
+};
+
+static struct irq_chip sun6i_r_intc_wakeup_chip = {
+   .name   = "sun6i-r-intc",
+   .irq_mask   = irq_chip_mask_parent,
+   .irq_unmask = irq_chip_unmask_parent,
+   .irq_eoi= irq_chip_eoi_parent,
+   .irq_set_affinity   = irq_chip_set_affinity_parent,
+   .irq_set_type   = irq_chip_set_type_parent,
+   .irq_set_wake   = sun6i_r_intc_irq_set_wake,
+   .flags  = IRQCHIP_SET_TYPE_MASKED,
 };
 
 static int sun6i_r_intc_domain_translate(struct irq_domain *domain,
@@ -215,8 +252,8 @@ static int sun6i_r_intc_domain_alloc(struct irq_domain 
*domain,
  _r_intc_nmi_chip, 
0);
irq_set_handler(virq, handle_fasteoi_ack_irq);
} else {
-   /* Only the NMI is currently supported. */
-   return -EINVAL;
+   irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+ 
_r_intc_wakeup_chip, 0);
}
}
 
@@ -229,6 +266,22 @@ static const struct irq_domain_ops sun6i_r_intc_domain_ops 
= {
.free   = irq_domain_free_irqs_common,
 };
 
+static int sun6i_r_intc_suspend(void)
+{
+   u32 buf[BITS_TO_U32(max(SUN6I_NR_TOP_LEVEL_IRQS, SUN6I_NR_MUX_BITS))];
+   int i;
+
+   /* Wake IRQs are enabled during system sleep and shutdown. */
+   bitmap_to_arr32(buf, wake_irq_enabled, SUN6I_NR_TOP_LEVEL_

[PATCH v5 02/10] dt-bindings: irq: sun6i-r: Add a compatible for the H3

2021-01-17 Thread Samuel Holland
The Allwinner H3 SoC contains an R_INTC that is, as far as we know,
compatible with the R_INTC present in other sun8i SoCs starting with
the A31. Since the R_INTC hardware is undocumented, introduce a new
compatible for the R_INTC variant in this SoC, in case there turns out
to be some difference.

Acked-by: Maxime Ripard 
Acked-by: Rob Herring 
Signed-off-by: Samuel Holland 
---
 .../interrupt-controller/allwinner,sun6i-a31-r-intc.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git 
a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml
 
b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml
index 50e607e607c8..4db24b8a9ffe 100644
--- 
a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml
+++ 
b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml
@@ -27,6 +27,7 @@ properties:
   - items:
   - enum:
   - allwinner,sun8i-a83t-r-intc
+  - allwinner,sun8i-h3-r-intc
   - allwinner,sun50i-a64-r-intc
   - const: allwinner,sun6i-a31-r-intc
   - const: allwinner,sun50i-h6-r-intc
-- 
2.26.2



Re: [PATCH v3 19/21] arm64: dts: allwinner: Add Allwinner H616 .dtsi file

2021-01-17 Thread Samuel Holland
On 1/17/21 8:08 PM, Andre Przywara wrote:
> This (relatively) new SoC is similar to the H6, but drops the (broken)
> PCIe support and the USB 3.0 controller. It also gets the management
> controller removed, which in turn removes *some*, but not all of the
> devices formerly dedicated to the ARISC (CPUS).
> There does not seem to be an extra interrupt controller anymore, also
> it lacks the corresponding NMI pin, so no interrupts for the PMIC.
> 
> Signed-off-by: Andre Przywara 
> ---
>  .../arm64/boot/dts/allwinner/sun50i-h616.dtsi | 750 ++
>  1 file changed, 750 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
> 
> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi 
> b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
> new file mode 100644
> index ..953e8fac20f0
> --- /dev/null
> +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi
> @@ -0,0 +1,750 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +// Copyright (C) 2020 Arm Ltd.
> +// based on the H6 dtsi, which is:
> +//   Copyright (C) 2017 Icenowy Zheng 
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +/ {
> + interrupt-parent = <>;
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + cpus {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + cpu0: cpu@0 {
> + compatible = "arm,cortex-a53";
> + device_type = "cpu";
> + reg = <0>;
> + enable-method = "psci";
> + clocks = < CLK_CPUX>;
> + };
> +
> + cpu1: cpu@1 {
> + compatible = "arm,cortex-a53";
> + device_type = "cpu";
> + reg = <1>;
> + enable-method = "psci";
> + clocks = < CLK_CPUX>;
> + };
> +
> + cpu2: cpu@2 {
> + compatible = "arm,cortex-a53";
> + device_type = "cpu";
> + reg = <2>;
> + enable-method = "psci";
> + clocks = < CLK_CPUX>;
> + };
> +
> + cpu3: cpu@3 {
> + compatible = "arm,cortex-a53";
> + device_type = "cpu";
> + reg = <3>;
> + enable-method = "psci";
> + clocks = < CLK_CPUX>;
> + };
> + };
> +
> + reserved-memory {
> + #address-cells = <2>;
> + #size-cells = <2>;
> + ranges;
> +
> + /* 512KiB reserved for ARM Trusted Firmware (BL31) */
> + secmon_reserved: secmon@4000 {
> + reg = <0x0 0x4000 0x0 0x8>;
> + no-map;
> + };
> + };
> +
> + osc24M: osc24M_clk {
> + #clock-cells = <0>;
> + compatible = "fixed-clock";
> + clock-frequency = <2400>;
> + clock-output-names = "osc24M";
> + };
> +
> + pmu {
> + compatible = "arm,cortex-a53-pmu";
> + interrupts = ,
> +  ,
> +  ,
> +  ;
> + interrupt-affinity = <>, <>, <>, <>;
> + };
> +
> + psci {
> + compatible = "arm,psci-0.2";
> + method = "smc";
> + };
> +
> + timer {
> + compatible = "arm,armv8-timer";
> + arm,no-tick-in-suspend;
> + interrupts =  + (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
> +   + (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
> +   + (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
> +   + (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
> + };
> +
> + soc {
> + compatible = "simple-bus";
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges = <0x0 0x0 0x0 0x4000>;
> +
> + syscon: syscon@300 {
> + compatible = "allwinner,sun50i-h616-system-control";
> + reg = <0x0300 0x1000>;
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges;
> +
> + sram_c: sram@28000 {
> + compatible = "mmio-sram";
> + reg = <0x00028000 0x3>;
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges = <0 0x00028000 0x3>;
> + };
> + };
> +
> + ccu: clock@3001000 {
> + compatible = "allwinner,sun50i-h616-ccu";
> + reg = <0x03001000 0x1000>;
> 

Re: [PATCH v3 18/21] dt-bindings: allwinner: Add H616 compatible strings

2021-01-17 Thread Samuel Holland
On 1/17/21 8:08 PM, Andre Przywara wrote:
> Add simple "allwinner,sun50i-h616-xxx" compatible names to existing
> bindings, and pair them with an existing fallback compatible string,
> as the devices are compatible.
> This covers I2C, infrared, RTC and SPI.
> 
> Use enums to group all compatible devices together.
> 
> Signed-off-by: Andre Przywara 
> Acked-by: Rob Herring 
> Acked-by: Wolfram Sang  # for I2C
> ---
>  .../bindings/i2c/marvell,mv64xxx-i2c.yaml | 21 +++
>  .../media/allwinner,sun4i-a10-ir.yaml | 16 ++
>  .../bindings/rtc/allwinner,sun6i-a31-rtc.yaml |  3 +++
>  .../bindings/spi/allwinner,sun6i-a31-spi.yaml |  1 +
>  4 files changed, 17 insertions(+), 24 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml 
> b/Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml
> index 5b5ae402f97a..eb72dd571def 100644
> --- a/Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml
> +++ b/Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml
> @@ -18,21 +18,14 @@ properties:
>- const: allwinner,sun4i-a10-i2c
>- const: allwinner,sun6i-a31-i2c
>- items:
> -  - const: allwinner,sun8i-a23-i2c
> +  - enum:
> +  - allwinner,sun8i-a23-i2c
> +  - allwinner,sun8i-a83t-i2c
> +  - allwinner,sun50i-a64-i2c
> +  - allwinner,sun50i-a100-i2c
> +  - allwinner,sun50i-h6-i2c
> +  - allwinner,sun50i-h616-i2c
>- const: allwinner,sun6i-a31-i2c
> -  - items:
> -  - const: allwinner,sun8i-a83t-i2c
> -  - const: allwinner,sun6i-a31-i2c
> -  - items:
> -  - const: allwinner,sun50i-a64-i2c
> -  - const: allwinner,sun6i-a31-i2c
> -  - items:
> -  - const: allwinner,sun50i-a100-i2c
> -  - const: allwinner,sun6i-a31-i2c
> -  - items:
> -  - const: allwinner,sun50i-h6-i2c
> -  - const: allwinner,sun6i-a31-i2c
> -
>- const: marvell,mv64xxx-i2c
>- const: marvell,mv78230-i2c
>- const: marvell,mv78230-a0-i2c
> diff --git 
> a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-ir.yaml 
> b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-ir.yaml
> index 5fa19d4aeaf3..6d8395d6bca0 100644
> --- a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-ir.yaml
> +++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-ir.yaml
> @@ -20,16 +20,12 @@ properties:
>- const: allwinner,sun5i-a13-ir
>- const: allwinner,sun6i-a31-ir
>- items:
> -  - const: allwinner,sun8i-a83t-ir
> -  - const: allwinner,sun6i-a31-ir
> -  - items:
> -  - const: allwinner,sun8i-r40-ir
> -  - const: allwinner,sun6i-a31-ir
> -  - items:
> -  - const: allwinner,sun50i-a64-ir
> -  - const: allwinner,sun6i-a31-ir
> -  - items:
> -  - const: allwinner,sun50i-h6-ir
> +  - enum:
> +  - allwinner,sun8i-a83t-ir
> +  - allwinner,sun8i-r40-ir
> +  - allwinner,sun50i-a64-ir
> +  - allwinner,sun50i-h6-ir
> +  - allwinner,sun50i-h616-ir
>- const: allwinner,sun6i-a31-ir
>  
>reg:
> diff --git 
> a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml 
> b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
> index 37c2a601c3fa..97928efd2bc9 100644
> --- a/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
> +++ b/Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml
> @@ -26,6 +26,9 @@ properties:
>- const: allwinner,sun50i-a64-rtc
>- const: allwinner,sun8i-h3-rtc
>- const: allwinner,sun50i-h6-rtc
> +  - items:
> +  - const: allwinner,sun50i-h616-rtc
> +  - const: allwinner,sun50i-h6-rtc

Since H6, the RTC manages the 24 MHz DCXO, so it provides a fourth clock
output. If this is easy to change later, then it is fine for now, but
maybe it is better to get the H616 binding correct from the beginning?

Cheers,
Samuel

>reg:
>  maxItems: 1
> diff --git 
> a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml 
> b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml
> index 7866a655d81c..908248260afa 100644
> --- a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml
> +++ b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml
> @@ -25,6 +25,7 @@ properties:
>- enum:
>- allwinner,sun8i-r40-spi
>- allwinner,sun50i-h6-spi
> +  - allwinner,sun50i-h616-spi
>- const: allwinner,sun8i-h3-spi
>  
>reg:
> 



Re: [PATCH v3 09/21] mfd: axp20x: Allow AXP chips without interrupt lines

2021-01-17 Thread Samuel Holland
On 1/17/21 8:08 PM, Andre Przywara wrote:
> Currently the AXP chip requires to have its IRQ line connected to some
> interrupt controller, and will fail probing when this is not the case.
> 
> On a new Allwinner SoC (H616) there is no NMI pin anymore, so the
> interrupt functionality of the AXP chip is simply not available.
> 
> Check whether the DT describes the AXP chip as an interrupt controller
> before trying to register the irqchip, to avoid probe failures on
> setups without an interrupt.

The AXP305 has an IRQ pin. It is still an interrupt controller, even if
its output is not connected anywhere. And even though the NMI pin on the
H616 is gone, the PMIC IRQ line could be connected to a GPIO. So it is
not appropriate to remove "interrupt-controller".

Per the binding, both "interrupts" and "interrupt-controller" are
required properties. It would make more sense to make "interrupts"
optional. Either way, you need to update the binding.

Though I'm concerned about how this may affect drivers for regmap cells
which use interrupts (such as axp20x-pek). If the irqchip is not
registered, requesting those interrupts will fail. While I don't
currently know of any boards that have the AXP305 power key wired up, it
prevents us from modelling the hardware correctly and supporting that
configuration.

Cheers,
Samuel

> Signed-off-by: Andre Przywara 
> ---
>  drivers/mfd/axp20x.c | 17 +++--
>  1 file changed, 11 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
> index aa59496e4376..a52595c49d40 100644
> --- a/drivers/mfd/axp20x.c
> +++ b/drivers/mfd/axp20x.c
> @@ -959,12 +959,17 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
>AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE);
>   }
>  
> - ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq,
> -   IRQF_ONESHOT | IRQF_SHARED | axp20x->irq_flags,
> --1, axp20x->regmap_irq_chip, >regmap_irqc);
> - if (ret) {
> - dev_err(axp20x->dev, "failed to add irq chip: %d\n", ret);
> - return ret;
> + if (!axp20x->dev->of_node ||
> + of_property_read_bool(axp20x->dev->of_node, 
> "interrupt-controller")) {
> + ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq,
> + IRQF_ONESHOT | IRQF_SHARED | axp20x->irq_flags,
> + -1, axp20x->regmap_irq_chip,
> + >regmap_irqc);
> + if (ret) {
> + dev_err(axp20x->dev, "failed to add irq chip: %d\n",
> + ret);
> + return ret;
> + }
>   }
>  
>   ret = mfd_add_devices(axp20x->dev, -1, axp20x->cells,
> 



Re: [PATCH v4 04/10] irqchip/sun6i-r: Add wakeup support

2021-01-14 Thread Samuel Holland
On 1/14/21 3:44 PM, Marc Zyngier wrote:
> On Tue, 12 Jan 2021 05:59:44 +,
> Samuel Holland  wrote:
>>
>> Maintain bitmaps of wake-enabled IRQs and mux inputs, and program them
>> to the hardware during the syscore phase of suspend and shutdown. Then
>> restore the original set of enabled IRQs (only the NMI) during resume.
>>
>> This serves two purposes. First, it lets power management firmware
>> running on the ARISC coprocessor know which wakeup sources Linux wants
>> to have enabled. That way, it can avoid turning them off when it shuts
>> down the remainder of the clock tree. Second, it preconfigures the
>> coprocessor's interrupt controller, so the firmware's wakeup logic
>> is as simple as waiting for an interrupt to arrive.
>>
>> The suspend/resume logic is not conditional on PM_SLEEP because it is
>> identical to the init/shutdown logic. Wake IRQs may be enabled during
>> shutdown to allow powering the board back on. As an example, see
>> commit a5c5e50cce9d ("Input: gpio-keys - add shutdown callback").
>>
>> Signed-off-by: Samuel Holland 
>> ---
>>  drivers/irqchip/irq-sun6i-r.c | 107 --
>>  1 file changed, 101 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/irqchip/irq-sun6i-r.c b/drivers/irqchip/irq-sun6i-r.c
>> index d04d067423f4..a1b58c98d6ca 100644
>> --- a/drivers/irqchip/irq-sun6i-r.c
>> +++ b/drivers/irqchip/irq-sun6i-r.c
>> @@ -39,6 +39,7 @@
>>   * set of 128 mux bits. This requires a second set of top-level registers.
>>   */
>>  
>> +#include 
>>  #include 
>>  #include 
>>  #include 
>> @@ -46,6 +47,7 @@
>>  #include 
>>  #include 
>>  #include 
>> +#include 
>>  
>>  #include 
>>  
>> @@ -67,8 +69,17 @@
>>  #define SUN6I_NR_DIRECT_IRQS16
>>  #define SUN6I_NR_MUX_BITS   128
>>  
>> +struct sun6i_r_intc_variant {
>> +u32 first_mux_irq;
>> +u32 nr_mux_irqs;
>> +u32 mux_valid[BITS_TO_U32(SUN6I_NR_MUX_BITS)];
>> +};
>> +
>>  static void __iomem *base;
>>  static irq_hw_number_t nmi_hwirq;
>> +static DECLARE_BITMAP(wake_irq_enabled, SUN6I_NR_TOP_LEVEL_IRQS);
>> +static DECLARE_BITMAP(wake_mux_enabled, SUN6I_NR_MUX_BITS);
>> +static DECLARE_BITMAP(wake_mux_valid, SUN6I_NR_MUX_BITS);
>>  
>>  static void sun6i_r_intc_ack_nmi(void)
>>  {
>> @@ -145,6 +156,21 @@ static int sun6i_r_intc_nmi_set_irqchip_state(struct 
>> irq_data *data,
>>  return irq_chip_set_parent_state(data, which, state);
>>  }
>>  
>> +static int sun6i_r_intc_irq_set_wake(struct irq_data *data, unsigned int on)
>> +{
>> +unsigned long offset_from_nmi = data->hwirq - nmi_hwirq;
>> +
>> +if (offset_from_nmi < SUN6I_NR_DIRECT_IRQS)
>> +assign_bit(offset_from_nmi, wake_irq_enabled, on);
>> +else if (test_bit(data->hwirq, wake_mux_valid))
>> +assign_bit(data->hwirq, wake_mux_enabled, on);
>> +else
>> +/* Not wakeup capable. */
>> +return -EPERM;
>> +
>> +return 0;
>> +}
>> +
>>  static struct irq_chip sun6i_r_intc_nmi_chip = {
>>  .name   = "sun6i-r-intc",
>>  .irq_ack= sun6i_r_intc_nmi_ack,
>> @@ -154,8 +180,19 @@ static struct irq_chip sun6i_r_intc_nmi_chip = {
>>  .irq_set_affinity   = irq_chip_set_affinity_parent,
>>  .irq_set_type   = sun6i_r_intc_nmi_set_type,
>>  .irq_set_irqchip_state  = sun6i_r_intc_nmi_set_irqchip_state,
>> -.flags  = IRQCHIP_SET_TYPE_MASKED |
>> -  IRQCHIP_SKIP_SET_WAKE,
>> +.irq_set_wake   = sun6i_r_intc_irq_set_wake,
>> +.flags  = IRQCHIP_SET_TYPE_MASKED,
>> +};
>> +
>> +static struct irq_chip sun6i_r_intc_wakeup_chip = {
>> +.name   = "sun6i-r-intc",
>> +.irq_mask   = irq_chip_mask_parent,
>> +.irq_unmask = irq_chip_unmask_parent,
>> +.irq_eoi= irq_chip_eoi_parent,
>> +.irq_set_affinity   = irq_chip_set_affinity_parent,
>> +.irq_set_type   = irq_chip_set_type_parent,
>> +.irq_set_wake   = sun6i_r_intc_irq_set_wake,
>> +.flags  = IRQCHIP_SET_TYPE_MASKED,
> 
> Worth implementing irq_get/set_irqchip_state() using the _parent
> helper, I guess.

This is the same situation as the previous patch. Assuming it is safe to
rely on the behavior of the top-level functions, adding the callbacks
here would be redundant.

Cheers,
Samuel

> Thanks,
> 
>   M.
> 



Re: [PATCH v4 03/10] irqchip/sun6i-r: Use a stacked irqchip driver

2021-01-14 Thread Samuel Holland
Hello,

On 1/14/21 3:06 PM, Marc Zyngier wrote:
> Hi Samuel,
> 
> On 2021-01-12 05:59, Samuel Holland wrote:
> 
> [...]
> 
>> +static void sun6i_r_intc_ack_nmi(void)
>> +{
>> +writel(SUN6I_NMI_BIT, base + SUN6I_IRQ_PENDING(0));
> 
> writel_relaxed()

irq_chip_unmask_parent(), which calls gic_unmask_irq(), is called
immediately after this in .irq_unmask. Since gic_unmask_irq() also uses
writel_relaxed(), the GIC write could be ordered before the write here.

I was getting occasional spurious interrupts (1 out of each 20-25) when
using a level trigger, which were resolved by switching to writel() here.

I mentioned this in the changelog, but it probably deserves a comment in
the code as well. Or maybe I should use an explicit barrier somewhere?

>> +}
>> +
>> +static void sun6i_r_intc_nmi_ack(struct irq_data *data)
>> +{
>> +if (irqd_get_trigger_type(data) & IRQ_TYPE_EDGE_BOTH)
>> +sun6i_r_intc_ack_nmi();
>> +else
>> +data->chip_data = SUN6I_NMI_NEEDS_ACK;
>> +}
>> +
>> +static void sun6i_r_intc_nmi_eoi(struct irq_data *data)
>> +{
>> +/* For oneshot IRQs, delay the ack until the IRQ is unmasked. */
>> +if (data->chip_data == SUN6I_NMI_NEEDS_ACK && !irqd_irq_masked(data)) 
>> {
>> +sun6i_r_intc_ack_nmi();
>> +data->chip_data = 0;
> 
> nit: NULL rather than 0?

NULL seemed less appropriate since I'm not using the field as a pointer,
but I don't have a strong opinion about it.

> [...]
> 
>> +static struct irq_chip sun6i_r_intc_nmi_chip = {
>> +.name   = "sun6i-r-intc",
>> +.irq_ack= sun6i_r_intc_nmi_ack,
>> +.irq_mask   = irq_chip_mask_parent,
>> +.irq_unmask = sun6i_r_intc_nmi_unmask,
>> +.irq_eoi= sun6i_r_intc_nmi_eoi,
>> +.irq_set_affinity   = irq_chip_set_affinity_parent,
>> +.irq_set_type   = sun6i_r_intc_nmi_set_type,
>> +.irq_set_irqchip_state  = sun6i_r_intc_nmi_set_irqchip_state,
> 
> You probably also want to wire irq_get_irqchip_state(), while
> you're at it.

I thought if the interrupt was pending here, it would necessarily also
be pending at the GIC, so adding a separate layer would be redundant.

irq_set_vcpu_affinity(), __irq_get_irqchip_state(), and
irq_set_irqchip_state() [the functions, not the callbacks] have the
interesting property that they search up the irqdomain hierarchy for the
first irqdomain with the callback. So if all the callback would do is
defer to its parent, it doesn't need to be provided at all*.

*except in case this irqdomain has a child which calls
irq_chip_get_parent_state(), which does not look past its immediate
parent. But I did not think that case was worth worrying about.

Cheers,
Samuel

> Otherwise, looks pretty good now.
> 
> Thanks,
> 
>   M.
> 



Re: [PATCH 3/4] media: sunxi-cir: Factor out hardware initialization

2021-01-13 Thread Samuel Holland
On 1/13/21 8:36 AM, Sean Young wrote:
> On Tue, Jan 12, 2021 at 10:51:31PM -0600, Samuel Holland wrote:
>> In preparation for adding suspend/resume hooks, factor out the hardware
>> initialization from the driver probe/remove functions.
>>
>> The timeout programmed during init is taken from the `struct rc_dev` so
>> it is maintained across an exit/init cycle.
>>
>> This resolves some trivial issues with the probe function: throwing away
>> the error from clk_prepare_enable and using the wrong type for the
>> temporary register value.
>>
>> Signed-off-by: Samuel Holland 
>> ---
>>  drivers/media/rc/sunxi-cir.c | 128 ---
>>  1 file changed, 74 insertions(+), 54 deletions(-)
>>
>> diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
>> index 48be400421cd..ccb9d6b4225d 100644
>> --- a/drivers/media/rc/sunxi-cir.c
>> +++ b/drivers/media/rc/sunxi-cir.c
>> @@ -169,10 +169,74 @@ static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, 
>> unsigned int timeout)
>>  return 0;
>>  }
>>  
>> +static int sunxi_ir_hw_init(struct device *dev)
>> +{
>> +struct sunxi_ir *ir = dev_get_drvdata(dev);
>> +u32 tmp;
>> +int ret;
>> +
>> +ret = reset_control_deassert(ir->rst);
>> +if (ret)
>> +return ret;
>> +
>> +ret = clk_prepare_enable(ir->apb_clk);
>> +if (ret) {
>> +dev_err(dev, "failed to enable apb clk\n");
>> +goto exit_assert_reset;
>> +}
>> +
>> +ret = clk_prepare_enable(ir->clk);
>> +if (ret) {
>> +dev_err(dev, "failed to enable ir clk\n");
>> +goto exit_disable_apb_clk;
>> +}
>> +
>> +/* Enable CIR Mode */
>> +writel(REG_CTL_MD, ir->base + SUNXI_IR_CTL_REG);
>> +
>> +/* Set noise threshold and idle threshold */
>> +sunxi_ir_set_timeout(ir->rc, ir->rc->timeout);

Initializing ir->rc->timeout in .probe is needed because of this line.
As the changelog mentions, this reprograms the user-configured timeout
after an exit/init (suspend/resume) cycle. It needs some default value
the first time, when called from .probe.

>> +
>> +/* Invert Input Signal */
>> +writel(REG_RXCTL_RPPI, ir->base + SUNXI_IR_RXCTL_REG);
>> +
>> +/* Clear All Rx Interrupt Status */
>> +writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
>> +
>> +/*
>> + * Enable IRQ on overflow, packet end, FIFO available with trigger
>> + * level
>> + */
>> +writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN |
>> +   REG_RXINT_RAI_EN | REG_RXINT_RAL(ir->fifo_size / 2 - 1),
>> +   ir->base + SUNXI_IR_RXINT_REG);
>> +
>> +/* Enable IR Module */
>> +tmp = readl(ir->base + SUNXI_IR_CTL_REG);
>> +writel(tmp | REG_CTL_GEN | REG_CTL_RXEN, ir->base + SUNXI_IR_CTL_REG);
>> +
>> +return 0;
>> +
>> +exit_disable_apb_clk:
>> +clk_disable_unprepare(ir->apb_clk);
>> +exit_assert_reset:
>> +reset_control_assert(ir->rst);
>> +
>> +return ret;
>> +}
>> +
>> +static void sunxi_ir_hw_exit(struct device *dev)
>> +{
>> +struct sunxi_ir *ir = dev_get_drvdata(dev);
>> +
>> +clk_disable_unprepare(ir->clk);
>> +clk_disable_unprepare(ir->apb_clk);
>> +reset_control_assert(ir->rst);
>> +}
>> +
>>  static int sunxi_ir_probe(struct platform_device *pdev)
>>  {
>>  int ret = 0;
>> -unsigned long tmp = 0;
>>  
>>  struct device *dev = >dev;
>>  struct device_node *dn = dev->of_node;
>> @@ -213,43 +277,26 @@ static int sunxi_ir_probe(struct platform_device *pdev)
>>  ir->rst = devm_reset_control_get_exclusive(dev, NULL);
>>  if (IS_ERR(ir->rst))
>>  return PTR_ERR(ir->rst);
>> -ret = reset_control_deassert(ir->rst);
>> -if (ret)
>> -return ret;
>>  }
>>  
>>  ret = clk_set_rate(ir->clk, b_clk_freq);
>>  if (ret) {
>>  dev_err(dev, "set ir base clock failed!\n");
>> -goto exit_reset_assert;
>> +return ret;
>>  }
>>  dev_dbg(dev, "set base clock frequency to %d Hz.\n", b_clk_freq);
>>  
>> -if (clk_prepare_enable(ir->apb_clk)) {
>> -dev_err(dev

[PATCH v2 7/7] arm64: dts: allwinner: pinephone: Set audio card name

2021-01-12 Thread Samuel Holland
From: Arnaud Ferraris 

Add the "PinePhone" name to the sound card: this will make
upstreaming an ALSA UCM config easier as we can use a unique name.

It also avoids an issue where the default card name is truncated.

Signed-off-by: Arnaud Ferraris 
[Samuel: Split out change, updated commit message]
Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
index e0db2f1373bc..cf6dcdd135b5 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
@@ -425,6 +425,7 @@ _rtc_ldo {
 
  {
status = "okay";
+   simple-audio-card,name = "PinePhone";
simple-audio-card,aux-devs = <_analog>, <_amp>;
simple-audio-card,widgets = "Microphone", "Headset Microphone",
"Microphone", "Internal Microphone",
-- 
2.26.2



[PATCH v2 6/7] arm64: dts: allwinner: pinephone: Add support for Bluetooth audio

2021-01-12 Thread Samuel Holland
The PinePhone has a Bluetooth chip with its PCM interface connected to
AIF3. Add the DAI link so headeset audio can be routed in hardware.

Even though the link is 16 bit PCM, configuring the link a 32-bit slot
is required for compatibility with AIF2, which also uses a 32-bit slot,
and which shares clock dividers with AIF3. Using equal clock frequencies
allows the modem and headset to be used at the same time.

Signed-off-by: Samuel Holland 
---
 .../dts/allwinner/sun50i-a64-pinephone.dtsi   | 24 +++
 1 file changed, 24 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
index 2dfe9bae8c67..e0db2f1373bc 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
@@ -24,6 +24,11 @@ backlight: backlight {
/* Backlight configuration differs per PinePhone revision. */
};
 
+   bt_sco_codec: bt-sco-codec {
+   #sound-dai-cells = <1>;
+   compatible = "linux,bt-sco";
+   };
+
chosen {
stdout-path = "serial0:115200n8";
};
@@ -82,6 +87,8 @@ vibrator {
 };
 
  {
+   pinctrl-names = "default";
+   pinctrl-0 = <_pins>;
status = "okay";
 };
 
@@ -439,6 +446,23 @@  {
"MIC1", "Internal Microphone",
"Headset Microphone", "HBIAS",
"MIC2", "Headset Microphone";
+
+   simple-audio-card,dai-link@2 {
+   format = "dsp_a";
+   frame-master = <_codec>;
+   bitclock-master = <_codec>;
+   bitclock-inversion;
+
+   link2_cpu: cpu {
+   sound-dai = <_sco_codec 0>;
+   };
+
+   link2_codec: codec {
+   sound-dai = < 2>;
+   dai-tdm-slot-num = <1>;
+   dai-tdm-slot-width = <32>;
+   };
+   };
 };
 
  {
-- 
2.26.2



[PATCH v2 5/7] arm64: dts: allwinner: a64: Allow multiple DAI links

2021-01-12 Thread Samuel Holland
simple-audio-card supports either a single DAI link at the top level, or
subnodes with one or more DAI links. To use the secondary AIFs on the
codec, we need to add additional DAI links to the same sound card, so we
need to use the other binding.

Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 23 +++
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 4bba468d8ec2..fd9a278f014a 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -131,12 +131,10 @@ psci {
};
 
sound: sound {
+   #address-cells = <1>;
+   #size-cells = <0>;
compatible = "simple-audio-card";
simple-audio-card,name = "sun50i-a64-audio";
-   simple-audio-card,format = "i2s";
-   simple-audio-card,frame-master = <>;
-   simple-audio-card,bitclock-master = <>;
-   simple-audio-card,mclk-fs = <128>;
simple-audio-card,aux-devs = <_analog>;
simple-audio-card,routing =
"Left DAC", "DACL",
@@ -145,12 +143,19 @@ sound: sound {
"ADCR", "Right ADC";
status = "disabled";
 
-   cpudai: simple-audio-card,cpu {
-   sound-dai = <>;
-   };
+   simple-audio-card,dai-link@0 {
+   format = "i2s";
+   frame-master = <_cpu>;
+   bitclock-master = <_cpu>;
+   mclk-fs = <128>;
 
-   link_codec: simple-audio-card,codec {
-   sound-dai = < 0>;
+   link0_cpu: cpu {
+   sound-dai = <>;
+   };
+
+   link0_codec: codec {
+   sound-dai = < 0>;
+   };
};
};
 
-- 
2.26.2



[PATCH v2 2/7] ARM: dts: sun8i-a33: Allow using multiple codec DAIs

2021-01-12 Thread Samuel Holland
Increase #sound-dai-cells on the digital codec to allow using the other
DAIs provided by the codec for AIF2 and AIF3.

Signed-off-by: Samuel Holland 
---
 arch/arm/boot/dts/sun8i-a33.dtsi | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/sun8i-a33.dtsi b/arch/arm/boot/dts/sun8i-a33.dtsi
index c458f5fb124f..0b38f9f35074 100644
--- a/arch/arm/boot/dts/sun8i-a33.dtsi
+++ b/arch/arm/boot/dts/sun8i-a33.dtsi
@@ -198,7 +198,7 @@ simple-audio-card,cpu {
};
 
link_codec: simple-audio-card,codec {
-   sound-dai = <>;
+   sound-dai = < 0>;
};
};
 
@@ -238,7 +238,7 @@ dai: dai@1c22c00 {
};
 
codec: codec@1c22e00 {
-   #sound-dai-cells = <0>;
+   #sound-dai-cells = <1>;
compatible = "allwinner,sun8i-a33-codec";
reg = <0x01c22e00 0x400>;
interrupts = ;
-- 
2.26.2



[PATCH v2 0/7] PinePhone BT audio bringup

2021-01-12 Thread Samuel Holland
This series makes use of the additional DAIs recently added to the
sun8i-codec driver to add hardware routing for BT SCO (headset) audio
on the PinePhone.

The BT audio connection is represented by the "dummy" bt-sco codec. The
connection to the Quectel EG-25G modem via AIF2 works as well, but I do
not include it here because there is no appropriate codec driver in
tree. We have been using an out-of-tree "dummy" codec driver for the
modem similar to bt-sco, and I'm not sure if such a driver would be
desired upstream.

Changes from v1:
  - Fixed DT binding example to follow new binding

Arnaud Ferraris (1):
  arm64: dts: allwinner: pinephone: Set audio card name

Samuel Holland (6):
  ASoC: dt-bindings: sun8i-codec: Increase #sound-dai-cells
  ARM: dts: sun8i-a33: Allow using multiple codec DAIs
  arm64: dts: allwinner: a64: Allow using multiple codec DAIs
  arm64: dts: allwinner: a64: Add pinmux nodes for AIF2/AIF3
  arm64: dts: allwinner: a64: Allow multiple DAI links
  arm64: dts: allwinner: pinephone: Add support for Bluetooth audio

 .../sound/allwinner,sun8i-a33-codec.yaml  |  4 +-
 arch/arm/boot/dts/sun8i-a33.dtsi  |  4 +-
 .../dts/allwinner/sun50i-a64-pinephone.dtsi   | 25 +
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 37 ++-
 4 files changed, 56 insertions(+), 14 deletions(-)

-- 
2.26.2



[PATCH v2 1/7] ASoC: dt-bindings: sun8i-codec: Increase #sound-dai-cells

2021-01-12 Thread Samuel Holland
Increase sound-dai-cells to 1 to allow using the DAIs in the codec
corresponding to AIF2 and AIF3.

The generic ASoC OF code supports a #sound-dai-cells value of 0 or 1
with no impact to the driver, so this is a backward-compatible change.

Signed-off-by: Samuel Holland 
---
 .../devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git 
a/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml 
b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml
index 67405e6d8168..3e02baa1d9ce 100644
--- a/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml
+++ b/Documentation/devicetree/bindings/sound/allwinner,sun8i-a33-codec.yaml
@@ -12,7 +12,7 @@ maintainers:
 
 properties:
   "#sound-dai-cells":
-const: 0
+const: 1
 
   compatible:
 oneOf:
@@ -50,7 +50,7 @@ additionalProperties: false
 examples:
   - |
 audio-codec@1c22e00 {
-  #sound-dai-cells = <0>;
+  #sound-dai-cells = <1>;
   compatible = "allwinner,sun8i-a33-codec";
   reg = <0x01c22e00 0x400>;
   interrupts = <0 29 4>;
-- 
2.26.2



[PATCH v2 3/7] arm64: dts: allwinner: a64: Allow using multiple codec DAIs

2021-01-12 Thread Samuel Holland
Increase #sound-dai-cells on the digital codec to allow using the other
DAIs provided by the codec for AIF2 and AIF3.

Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 51cc30e84e26..e788251e582f 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -150,7 +150,7 @@ cpudai: simple-audio-card,cpu {
};
 
link_codec: simple-audio-card,codec {
-   sound-dai = <>;
+   sound-dai = < 0>;
};
};
 
@@ -874,7 +874,7 @@ dai: dai@1c22c00 {
};
 
codec: codec@1c22e00 {
-   #sound-dai-cells = <0>;
+   #sound-dai-cells = <1>;
compatible = "allwinner,sun50i-a64-codec",
 "allwinner,sun8i-a33-codec";
reg = <0x01c22e00 0x600>;
-- 
2.26.2



[PATCH v2 4/7] arm64: dts: allwinner: a64: Add pinmux nodes for AIF2/AIF3

2021-01-12 Thread Samuel Holland
Now that the sun8i-codec driver supports AIF2 and AIF3, boards can use
them in DAI links. Add the necessary pinmux nodes.

Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 12 
 1 file changed, 12 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index e788251e582f..4bba468d8ec2 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -654,6 +654,18 @@ pio: pinctrl@1c20800 {
interrupt-controller;
#interrupt-cells = <3>;
 
+   /omit-if-no-ref/
+   aif2_pins: aif2-pins {
+   pins = "PB4", "PB5", "PB6", "PB7";
+   function = "aif2";
+   };
+
+   /omit-if-no-ref/
+   aif3_pins: aif3-pins {
+   pins = "PG10", "PG11", "PG12", "PG13";
+   function = "aif3";
+   };
+
csi_pins: csi-pins {
pins = "PE0", "PE2", "PE3", "PE4", "PE5", "PE6",
   "PE7", "PE8", "PE9", "PE10", "PE11";
-- 
2.26.2



Re: [PATCH v4 3/3] scsi: 3w-9xxx: Fix endianness issues in command packets

2021-01-12 Thread Samuel Holland
On 9/2/20 10:44 PM, Samuel Holland wrote:
> The controller expects all data it sends/receives to be little-endian.
> Therefore, the packet struct definitions should use the __le16/32/64
> types. Once those are correct, sparse reports several issues with the
> driver code, which are fixed here as well.
> 
> The main issue observed was at the call to scsi_set_resid, where the
> byteswapped parameter would eventually trigger the alignment check at
> drivers/scsi/sd.c:2009. At that point, the kernel would continuously
> complain about an "Unaligned partial completion", and no further I/O
> could occur.
> 
> This gets the controller working on big endian powerpc64.
> 
> Signed-off-by: Samuel Holland 

I believe I addressed all previous comments to this series in v4.
Is there anything preventing it from being merged? Do I need to resend it?

Regards,
Samuel


[PATCH] mmc: sunxi-mmc: Ensure host is suspended during system sleep

2021-01-12 Thread Samuel Holland
If the device suspend process begins before the mmc host's autosuspend
timeout, the host will continue running during system sleep. Avoid
this by forcing runtime suspend during a global suspend transition.

Signed-off-by: Samuel Holland 
---
 drivers/mmc/host/sunxi-mmc.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index 6310693f2ac0..cfee8db7b76d 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -1507,6 +1507,8 @@ static int sunxi_mmc_runtime_suspend(struct device *dev)
 #endif
 
 static const struct dev_pm_ops sunxi_mmc_pm_ops = {
+   SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+   pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend,
   sunxi_mmc_runtime_resume,
   NULL)
-- 
2.26.2



[PATCH 4/4] media: sunxi-cir: Implement suspend/resume/shutdown callbacks

2021-01-12 Thread Samuel Holland
To save power, gate/reset the hardware block while the system is
asleep or powered off.

Signed-off-by: Samuel Holland 
---
 drivers/media/rc/sunxi-cir.c | 21 +
 1 file changed, 21 insertions(+)

diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index ccb9d6b4225d..a0bdbf6f66c9 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -234,6 +234,20 @@ static void sunxi_ir_hw_exit(struct device *dev)
reset_control_assert(ir->rst);
 }
 
+static int __maybe_unused sunxi_ir_suspend(struct device *dev)
+{
+   sunxi_ir_hw_exit(dev);
+
+   return 0;
+}
+
+static int __maybe_unused sunxi_ir_resume(struct device *dev)
+{
+   return sunxi_ir_hw_init(dev);
+}
+
+static SIMPLE_DEV_PM_OPS(sunxi_ir_pm_ops, sunxi_ir_suspend, sunxi_ir_resume);
+
 static int sunxi_ir_probe(struct platform_device *pdev)
 {
int ret = 0;
@@ -362,6 +376,11 @@ static int sunxi_ir_remove(struct platform_device *pdev)
return 0;
 }
 
+static void sunxi_ir_shutdown(struct platform_device *pdev)
+{
+   sunxi_ir_hw_exit(>dev);
+}
+
 static const struct sunxi_ir_quirks sun4i_a10_ir_quirks = {
.has_reset = false,
.fifo_size = 16,
@@ -397,9 +416,11 @@ MODULE_DEVICE_TABLE(of, sunxi_ir_match);
 static struct platform_driver sunxi_ir_driver = {
.probe  = sunxi_ir_probe,
.remove = sunxi_ir_remove,
+   .shutdown   = sunxi_ir_shutdown,
.driver = {
.name = SUNXI_IR_DEV,
.of_match_table = sunxi_ir_match,
+   .pm = _ir_pm_ops,
},
 };
 
-- 
2.26.2



[PATCH 3/4] media: sunxi-cir: Factor out hardware initialization

2021-01-12 Thread Samuel Holland
In preparation for adding suspend/resume hooks, factor out the hardware
initialization from the driver probe/remove functions.

The timeout programmed during init is taken from the `struct rc_dev` so
it is maintained across an exit/init cycle.

This resolves some trivial issues with the probe function: throwing away
the error from clk_prepare_enable and using the wrong type for the
temporary register value.

Signed-off-by: Samuel Holland 
---
 drivers/media/rc/sunxi-cir.c | 128 ---
 1 file changed, 74 insertions(+), 54 deletions(-)

diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 48be400421cd..ccb9d6b4225d 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -169,10 +169,74 @@ static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, 
unsigned int timeout)
return 0;
 }
 
+static int sunxi_ir_hw_init(struct device *dev)
+{
+   struct sunxi_ir *ir = dev_get_drvdata(dev);
+   u32 tmp;
+   int ret;
+
+   ret = reset_control_deassert(ir->rst);
+   if (ret)
+   return ret;
+
+   ret = clk_prepare_enable(ir->apb_clk);
+   if (ret) {
+   dev_err(dev, "failed to enable apb clk\n");
+   goto exit_assert_reset;
+   }
+
+   ret = clk_prepare_enable(ir->clk);
+   if (ret) {
+   dev_err(dev, "failed to enable ir clk\n");
+   goto exit_disable_apb_clk;
+   }
+
+   /* Enable CIR Mode */
+   writel(REG_CTL_MD, ir->base + SUNXI_IR_CTL_REG);
+
+   /* Set noise threshold and idle threshold */
+   sunxi_ir_set_timeout(ir->rc, ir->rc->timeout);
+
+   /* Invert Input Signal */
+   writel(REG_RXCTL_RPPI, ir->base + SUNXI_IR_RXCTL_REG);
+
+   /* Clear All Rx Interrupt Status */
+   writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
+
+   /*
+* Enable IRQ on overflow, packet end, FIFO available with trigger
+* level
+*/
+   writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN |
+  REG_RXINT_RAI_EN | REG_RXINT_RAL(ir->fifo_size / 2 - 1),
+  ir->base + SUNXI_IR_RXINT_REG);
+
+   /* Enable IR Module */
+   tmp = readl(ir->base + SUNXI_IR_CTL_REG);
+   writel(tmp | REG_CTL_GEN | REG_CTL_RXEN, ir->base + SUNXI_IR_CTL_REG);
+
+   return 0;
+
+exit_disable_apb_clk:
+   clk_disable_unprepare(ir->apb_clk);
+exit_assert_reset:
+   reset_control_assert(ir->rst);
+
+   return ret;
+}
+
+static void sunxi_ir_hw_exit(struct device *dev)
+{
+   struct sunxi_ir *ir = dev_get_drvdata(dev);
+
+   clk_disable_unprepare(ir->clk);
+   clk_disable_unprepare(ir->apb_clk);
+   reset_control_assert(ir->rst);
+}
+
 static int sunxi_ir_probe(struct platform_device *pdev)
 {
int ret = 0;
-   unsigned long tmp = 0;
 
struct device *dev = >dev;
struct device_node *dn = dev->of_node;
@@ -213,43 +277,26 @@ static int sunxi_ir_probe(struct platform_device *pdev)
ir->rst = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(ir->rst))
return PTR_ERR(ir->rst);
-   ret = reset_control_deassert(ir->rst);
-   if (ret)
-   return ret;
}
 
ret = clk_set_rate(ir->clk, b_clk_freq);
if (ret) {
dev_err(dev, "set ir base clock failed!\n");
-   goto exit_reset_assert;
+   return ret;
}
dev_dbg(dev, "set base clock frequency to %d Hz.\n", b_clk_freq);
 
-   if (clk_prepare_enable(ir->apb_clk)) {
-   dev_err(dev, "try to enable apb_ir_clk failed\n");
-   ret = -EINVAL;
-   goto exit_reset_assert;
-   }
-
-   if (clk_prepare_enable(ir->clk)) {
-   dev_err(dev, "try to enable ir_clk failed\n");
-   ret = -EINVAL;
-   goto exit_clkdisable_apb_clk;
-   }
-
/* IO */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ir->base = devm_ioremap_resource(dev, res);
if (IS_ERR(ir->base)) {
-   ret = PTR_ERR(ir->base);
-   goto exit_clkdisable_clk;
+   return PTR_ERR(ir->base);
}
 
ir->rc = rc_allocate_device(RC_DRIVER_IR_RAW);
if (!ir->rc) {
dev_err(dev, "failed to allocate device\n");
-   ret = -ENOMEM;
-   goto exit_clkdisable_clk;
+   return -ENOMEM;
}
 
ir->rc->priv = ir;
@@ -265,6 +312,7 @@ static int sunxi_ir_probe(struct platform_device *pdev)
ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
/* Frequency after IR internal divider with sample period in us */
ir->rc->rx_resolution = (USEC_PE

[PATCH 2/4] media: sunxi-cir: Remove unnecessary spinlock

2021-01-12 Thread Samuel Holland
Only one register, SUNXI_IR_CIR_REG, is accessed from outside the
interrupt handler, and that register is not accessed from inside it.
As there is no overlap between different contexts, no lock is needed.

Signed-off-by: Samuel Holland 
---
 drivers/media/rc/sunxi-cir.c | 10 --
 1 file changed, 10 deletions(-)

diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 0a7f7eab3cc3..48be400421cd 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -86,7 +86,6 @@ struct sunxi_ir_quirks {
 };
 
 struct sunxi_ir {
-   spinlock_t  ir_lock;
struct rc_dev   *rc;
void __iomem*base;
int irq;
@@ -105,8 +104,6 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id)
struct sunxi_ir *ir = dev_id;
struct ir_raw_event rawir = {};
 
-   spin_lock(>ir_lock);
-
status = readl(ir->base + SUNXI_IR_RXSTA_REG);
 
/* clean all pending statuses */
@@ -137,8 +134,6 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id)
ir_raw_event_handle(ir->rc);
}
 
-   spin_unlock(>ir_lock);
-
return IRQ_HANDLED;
 }
 
@@ -160,17 +155,14 @@ static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, 
unsigned int timeout)
 {
struct sunxi_ir *ir = rc_dev->priv;
unsigned int base_clk = clk_get_rate(ir->clk);
-   unsigned long flags;
 
unsigned int ithr = sunxi_usec_to_ithr(base_clk, timeout);
 
dev_dbg(rc_dev->dev.parent, "setting idle threshold to %u\n", ithr);
 
-   spin_lock_irqsave(>ir_lock, flags);
/* Set noise threshold and idle threshold */
writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr),
   ir->base + SUNXI_IR_CIR_REG);
-   spin_unlock_irqrestore(>ir_lock, flags);
 
rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ithr);
 
@@ -199,8 +191,6 @@ static int sunxi_ir_probe(struct platform_device *pdev)
return -ENODEV;
}
 
-   spin_lock_init(>ir_lock);
-
ir->fifo_size = quirks->fifo_size;
 
/* Clock */
-- 
2.26.2



[PATCH 1/4] media: sunxi-cir: Clean up dead register writes

2021-01-12 Thread Samuel Holland
The register writes during driver removal occur after the device is
already put back in reset, so they never had any effect.

Signed-off-by: Samuel Holland 
---
 drivers/media/rc/sunxi-cir.c | 10 --
 1 file changed, 10 deletions(-)

diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 8555c7798706..0a7f7eab3cc3 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -342,22 +342,12 @@ static int sunxi_ir_probe(struct platform_device *pdev)
 
 static int sunxi_ir_remove(struct platform_device *pdev)
 {
-   unsigned long flags;
struct sunxi_ir *ir = platform_get_drvdata(pdev);
 
clk_disable_unprepare(ir->clk);
clk_disable_unprepare(ir->apb_clk);
reset_control_assert(ir->rst);
 
-   spin_lock_irqsave(>ir_lock, flags);
-   /* disable IR IRQ */
-   writel(0, ir->base + SUNXI_IR_RXINT_REG);
-   /* clear All Rx Interrupt Status */
-   writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
-   /* disable IR */
-   writel(0, ir->base + SUNXI_IR_CTL_REG);
-   spin_unlock_irqrestore(>ir_lock, flags);
-
rc_unregister_device(ir->rc);
return 0;
 }
-- 
2.26.2



[PATCH 0/4] media: sunxi-cir: Cleanup and power management

2021-01-12 Thread Samuel Holland
This series cleans up some dead code in the sunxi-cir driver and adds
system power management hooks.

Samuel Holland (4):
  media: sunxi-cir: Clean up dead register writes
  media: sunxi-cir: Remove unnecessary spinlock
  media: sunxi-cir: Factor out hardware initialization
  media: sunxi-cir: Implement suspend/resume/shutdown callbacks

 drivers/media/rc/sunxi-cir.c | 167 ---
 1 file changed, 94 insertions(+), 73 deletions(-)

-- 
2.26.2



[PATCH v2 2/3] input: sun4i-lradc-keys - Add wakup support

2021-01-12 Thread Samuel Holland
From: Ondrej Jirman 

Allow the driver to wake the system on key press if the "wakeup-source"
property is provided in the device tree. Using the LRADC as a wakeup
source requires keeping the AVCC domain active during sleep. Since this
has a nontrivial impact on power consumption (sometimes doubling it),
disable the LRADC wakeup source by default.

Signed-off-by: Ondrej Jirman 
Signed-off-by: Samuel Holland 
---
 drivers/input/keyboard/sun4i-lradc-keys.c | 22 ++
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c 
b/drivers/input/keyboard/sun4i-lradc-keys.c
index 4a796bed48ac..a1eb2001c088 100644
--- a/drivers/input/keyboard/sun4i-lradc-keys.c
+++ b/drivers/input/keyboard/sun4i-lradc-keys.c
@@ -22,6 +22,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 #include 
 #include 
 
@@ -226,8 +228,7 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
 {
struct sun4i_lradc_data *lradc;
struct device *dev = >dev;
-   int i;
-   int error;
+   int error, i, irq;
 
lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL);
if (!lradc)
@@ -272,8 +273,13 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
if (IS_ERR(lradc->base))
return PTR_ERR(lradc->base);
 
-   error = devm_request_irq(dev, platform_get_irq(pdev, 0),
-sun4i_lradc_irq, 0,
+   irq = platform_get_irq(pdev, 0);
+   if (irq < 0) {
+   dev_err(>dev, "Failed to get IRQ\n");
+   return irq;
+   }
+
+   error = devm_request_irq(dev, irq, sun4i_lradc_irq, 0,
 "sun4i-a10-lradc-keys", lradc);
if (error)
return error;
@@ -282,6 +288,14 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
if (error)
return error;
 
+   if (device_property_read_bool(dev, "wakeup-source")) {
+   device_set_wakeup_capable(dev, true);
+
+   error = dev_pm_set_wake_irq(dev, irq);
+   if (error)
+   dev_warn(dev, "Failed to set wake IRQ\n");
+   }
+
return 0;
 }
 
-- 
2.26.2



[PATCH v2 0/3] PinePhone volume key (LRADC) wakeup support

2021-01-12 Thread Samuel Holland
This series allows the volume keys on the PinePhone to wake up the
device. As pointed out for v1, wakeup should only be enabled when a
"wakeup-source" property is present, so v2 requires DT and binding
changes in addition to the driver change.

Changes since v1:
  - Add requisite DT binding change
  - Only add wakeup capability if "wakeup-source" is present
  - Warn but do not error out if setting the wake IRQ fails
  - Add "wakeup-source" property to PinePhone device tree

Ondrej Jirman (1):
  input: sun4i-lradc-keys -  Add wakup support

Samuel Holland (2):
  dt-bindings: sun4i-a10-lradc-keys: Accept wakeup-source property
  arm64: dts: allwinner: pinephone: Support volume key wakeup

 .../input/allwinner,sun4i-a10-lradc-keys.yaml |  2 ++
 .../dts/allwinner/sun50i-a64-pinephone.dtsi   |  1 +
 drivers/input/keyboard/sun4i-lradc-keys.c | 22 +++
 3 files changed, 21 insertions(+), 4 deletions(-)

-- 
2.26.2



[PATCH v2 3/3] arm64: dts: allwinner: pinephone: Support volume key wakeup

2021-01-12 Thread Samuel Holland
PinePhone volume keys are connected to the LRADC in the A64. Users may
want to use them to wake the device from sleep. Support this by
declaring the LRADC as a wakeup source.

Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
index 2dfe9bae8c67..7f37f9fea0ab 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
@@ -196,6 +196,7 @@  {
 
  {
vref-supply = <_aldo3>;
+   wakeup-source;
status = "okay";
 
button-200 {
-- 
2.26.2



[PATCH v2 1/3] dt-bindings: sun4i-a10-lradc-keys: Accept wakeup-source property

2021-01-12 Thread Samuel Holland
The LRADC provides an interrupt that can be used to wake the system.
Signify this by accepting a "wakeup-source" property in the binding.

Signed-off-by: Samuel Holland 
---
 .../bindings/input/allwinner,sun4i-a10-lradc-keys.yaml  | 2 ++
 1 file changed, 2 insertions(+)

diff --git 
a/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml 
b/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml
index cffd02028d02..d74f2002409e 100644
--- 
a/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml
+++ 
b/Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml
@@ -29,6 +29,8 @@ properties:
 description:
   Regulator for the LRADC reference voltage
 
+  wakeup-source: true
+
 patternProperties:
   "^button-[0-9]+$":
 type: object
-- 
2.26.2



[PATCH v4 10/10] arm64: dts: allwinner: Move wakeup-capable IRQs to r_intc

2021-01-11 Thread Samuel Holland
All IRQs that can be used to wake up the system must be routed through
r_intc, so they are visible to firmware while the system is suspended.

In addition to the external NMI input, which is already routed through
r_intc, these include PIO and R_PIO (gpio-keys), the LRADC, and the RTC.

Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 4 
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi  | 3 +++
 2 files changed, 7 insertions(+)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index fd4bf90163d5..b8697e84342e 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -644,6 +644,7 @@ ccu: clock@1c2 {
pio: pinctrl@1c20800 {
compatible = "allwinner,sun50i-a64-pinctrl";
reg = <0x01c20800 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ,
 ;
@@ -814,6 +815,7 @@ lradc: lradc@1c21800 {
compatible = "allwinner,sun50i-a64-lradc",
 "allwinner,sun8i-a83t-r-lradc";
reg = <0x01c21800 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ;
status = "disabled";
};
@@ -1204,6 +1206,7 @@ rtc: rtc@1f0 {
compatible = "allwinner,sun50i-a64-rtc",
 "allwinner,sun8i-h3-rtc";
reg = <0x01f0 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ;
clock-output-names = "osc32k", "osc32k-out", "iosc";
@@ -1275,6 +1278,7 @@ r_pwm: pwm@1f03800 {
r_pio: pinctrl@1f02c00 {
compatible = "allwinner,sun50i-a64-r-pinctrl";
reg = <0x01f02c00 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ;
clocks = <_ccu CLK_APB0_PIO>, <>, <>;
clock-names = "apb", "hosc", "losc";
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
index 93b8456f3b49..d467fe1bc566 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -294,6 +294,7 @@ pwm: pwm@300a000 {
pio: pinctrl@300b000 {
compatible = "allwinner,sun50i-h6-pinctrl";
reg = <0x0300b000 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ,
 ,
@@ -895,6 +896,7 @@ tcon_tv_out_tcon_top: endpoint@1 {
rtc: rtc@700 {
compatible = "allwinner,sun50i-h6-rtc";
reg = <0x0700 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ;
clock-output-names = "osc32k", "osc32k-out", "iosc";
@@ -930,6 +932,7 @@ r_intc: interrupt-controller@7021000 {
r_pio: pinctrl@7022000 {
compatible = "allwinner,sun50i-h6-r-pinctrl";
reg = <0x07022000 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ;
clocks = <_ccu CLK_R_APB1>, <>, < 0>;
-- 
2.26.2



[PATCH v4 05/10] ARM: dts: sunxi: Rename nmi_intc to r_intc

2021-01-11 Thread Samuel Holland
The R_INTC block controls more than just the NMI, and it is a different
hardware block than the NMI INTC found in some other Allwinner SoCs, so
the label "nmi_intc" is inaccurate. Name it "r_intc" to match the
compatible and to match the few references in the vendor documentation.

Signed-off-by: Samuel Holland 
---
 arch/arm/boot/dts/sun6i-a31-hummingbird.dts  | 2 +-
 arch/arm/boot/dts/sun6i-a31-m9.dts   | 2 +-
 arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts | 2 +-
 arch/arm/boot/dts/sun6i-a31.dtsi | 2 +-
 arch/arm/boot/dts/sun6i-a31s-primo81.dts | 2 +-
 arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi   | 2 +-
 arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts | 2 +-
 arch/arm/boot/dts/sun6i-a31s-yones-toptech-bs1078-v2.dts | 2 +-
 arch/arm/boot/dts/sun6i-reference-design-tablet.dtsi | 2 +-
 arch/arm/boot/dts/sun8i-a23-a33.dtsi | 2 +-
 arch/arm/boot/dts/sun8i-a33-olinuxino.dts| 2 +-
 arch/arm/boot/dts/sun8i-a33-sinlinx-sina33.dts   | 2 +-
 arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts | 2 +-
 arch/arm/boot/dts/sun8i-r16-parrot.dts   | 2 +-
 arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi | 2 +-
 15 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts 
b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
index 73de34ae37fd..486cec6f71e0 100644
--- a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
+++ b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
@@ -226,7 +226,7 @@  {
axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
-   interrupt-parent = <_intc>;
+   interrupt-parent = <_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
x-powers,drive-vbus-en;
};
diff --git a/arch/arm/boot/dts/sun6i-a31-m9.dts 
b/arch/arm/boot/dts/sun6i-a31-m9.dts
index a645c8f4257c..6aeb5a9696f7 100644
--- a/arch/arm/boot/dts/sun6i-a31-m9.dts
+++ b/arch/arm/boot/dts/sun6i-a31-m9.dts
@@ -115,7 +115,7 @@  {
axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
-   interrupt-parent = <_intc>;
+   interrupt-parent = <_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
};
 };
diff --git a/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts 
b/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts
index 648f24746234..6c6c1bd22bf6 100644
--- a/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts
+++ b/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts
@@ -115,7 +115,7 @@  {
axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
-   interrupt-parent = <_intc>;
+   interrupt-parent = <_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
};
 };
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index f3425a66fc0a..6a733a36d34a 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -1305,7 +1305,7 @@ rtc: rtc@1f0 {
clock-output-names = "osc32k";
};
 
-   nmi_intc: interrupt-controller@1f00c00 {
+   r_intc: interrupt-controller@1f00c00 {
compatible = "allwinner,sun6i-a31-r-intc";
interrupt-controller;
#interrupt-cells = <2>;
diff --git a/arch/arm/boot/dts/sun6i-a31s-primo81.dts 
b/arch/arm/boot/dts/sun6i-a31s-primo81.dts
index bc3170a0b8b5..429a165b79b2 100644
--- a/arch/arm/boot/dts/sun6i-a31s-primo81.dts
+++ b/arch/arm/boot/dts/sun6i-a31s-primo81.dts
@@ -159,7 +159,7 @@  {
axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
-   interrupt-parent = <_intc>;
+   interrupt-parent = <_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
x-powers,drive-vbus-en;
};
diff --git a/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi 
b/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi
index 3099491de8c4..7455c0db4a8a 100644
--- a/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi
@@ -78,7 +78,7 @@  {
axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
-   interrupt-parent = <_intc>;
+   interrupt-parent = <_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
};
 };
diff --git a/arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts 
b/arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts
index 367006fb280d..0d124a4a

[PATCH v4 07/10] ARM: dts: sunxi: h3/h5: Add r_intc node

2021-01-11 Thread Samuel Holland
The H3 and H5 SoCs have an additional interrupt controller in the RTC
power domain that can be used to enable wakeup for certain IRQs.

Add a node for it.

Signed-off-by: Samuel Holland 
---
 arch/arm/boot/dts/sunxi-h3-h5.dtsi | 9 +
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi 
b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
index 9be13378d4df..4bf25c5b873e 100644
--- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
+++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
@@ -859,6 +859,15 @@ rtc: rtc@1f0 {
#clock-cells = <1>;
};
 
+   r_intc: interrupt-controller@1f00c00 {
+   compatible = "allwinner,sun8i-h3-r-intc",
+"allwinner,sun6i-a31-r-intc";
+   interrupt-controller;
+   #interrupt-cells = <3>;
+   reg = <0x01f00c00 0x400>;
+   interrupts = ;
+   };
+
r_ccu: clock@1f01400 {
compatible = "allwinner,sun8i-h3-r-ccu";
reg = <0x01f01400 0x100>;
-- 
2.26.2



[PATCH v4 04/10] irqchip/sun6i-r: Add wakeup support

2021-01-11 Thread Samuel Holland
Maintain bitmaps of wake-enabled IRQs and mux inputs, and program them
to the hardware during the syscore phase of suspend and shutdown. Then
restore the original set of enabled IRQs (only the NMI) during resume.

This serves two purposes. First, it lets power management firmware
running on the ARISC coprocessor know which wakeup sources Linux wants
to have enabled. That way, it can avoid turning them off when it shuts
down the remainder of the clock tree. Second, it preconfigures the
coprocessor's interrupt controller, so the firmware's wakeup logic
is as simple as waiting for an interrupt to arrive.

The suspend/resume logic is not conditional on PM_SLEEP because it is
identical to the init/shutdown logic. Wake IRQs may be enabled during
shutdown to allow powering the board back on. As an example, see
commit a5c5e50cce9d ("Input: gpio-keys - add shutdown callback").

Signed-off-by: Samuel Holland 
---
 drivers/irqchip/irq-sun6i-r.c | 107 --
 1 file changed, 101 insertions(+), 6 deletions(-)

diff --git a/drivers/irqchip/irq-sun6i-r.c b/drivers/irqchip/irq-sun6i-r.c
index d04d067423f4..a1b58c98d6ca 100644
--- a/drivers/irqchip/irq-sun6i-r.c
+++ b/drivers/irqchip/irq-sun6i-r.c
@@ -39,6 +39,7 @@
  * set of 128 mux bits. This requires a second set of top-level registers.
  */
 
+#include 
 #include 
 #include 
 #include 
@@ -46,6 +47,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 
@@ -67,8 +69,17 @@
 #define SUN6I_NR_DIRECT_IRQS   16
 #define SUN6I_NR_MUX_BITS  128
 
+struct sun6i_r_intc_variant {
+   u32 first_mux_irq;
+   u32 nr_mux_irqs;
+   u32 mux_valid[BITS_TO_U32(SUN6I_NR_MUX_BITS)];
+};
+
 static void __iomem *base;
 static irq_hw_number_t nmi_hwirq;
+static DECLARE_BITMAP(wake_irq_enabled, SUN6I_NR_TOP_LEVEL_IRQS);
+static DECLARE_BITMAP(wake_mux_enabled, SUN6I_NR_MUX_BITS);
+static DECLARE_BITMAP(wake_mux_valid, SUN6I_NR_MUX_BITS);
 
 static void sun6i_r_intc_ack_nmi(void)
 {
@@ -145,6 +156,21 @@ static int sun6i_r_intc_nmi_set_irqchip_state(struct 
irq_data *data,
return irq_chip_set_parent_state(data, which, state);
 }
 
+static int sun6i_r_intc_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+   unsigned long offset_from_nmi = data->hwirq - nmi_hwirq;
+
+   if (offset_from_nmi < SUN6I_NR_DIRECT_IRQS)
+   assign_bit(offset_from_nmi, wake_irq_enabled, on);
+   else if (test_bit(data->hwirq, wake_mux_valid))
+   assign_bit(data->hwirq, wake_mux_enabled, on);
+   else
+   /* Not wakeup capable. */
+   return -EPERM;
+
+   return 0;
+}
+
 static struct irq_chip sun6i_r_intc_nmi_chip = {
.name   = "sun6i-r-intc",
.irq_ack= sun6i_r_intc_nmi_ack,
@@ -154,8 +180,19 @@ static struct irq_chip sun6i_r_intc_nmi_chip = {
.irq_set_affinity   = irq_chip_set_affinity_parent,
.irq_set_type   = sun6i_r_intc_nmi_set_type,
.irq_set_irqchip_state  = sun6i_r_intc_nmi_set_irqchip_state,
-   .flags  = IRQCHIP_SET_TYPE_MASKED |
- IRQCHIP_SKIP_SET_WAKE,
+   .irq_set_wake   = sun6i_r_intc_irq_set_wake,
+   .flags  = IRQCHIP_SET_TYPE_MASKED,
+};
+
+static struct irq_chip sun6i_r_intc_wakeup_chip = {
+   .name   = "sun6i-r-intc",
+   .irq_mask   = irq_chip_mask_parent,
+   .irq_unmask = irq_chip_unmask_parent,
+   .irq_eoi= irq_chip_eoi_parent,
+   .irq_set_affinity   = irq_chip_set_affinity_parent,
+   .irq_set_type   = irq_chip_set_type_parent,
+   .irq_set_wake   = sun6i_r_intc_irq_set_wake,
+   .flags  = IRQCHIP_SET_TYPE_MASKED,
 };
 
 static int sun6i_r_intc_domain_translate(struct irq_domain *domain,
@@ -215,8 +252,8 @@ static int sun6i_r_intc_domain_alloc(struct irq_domain 
*domain,
  _r_intc_nmi_chip, 
0);
irq_set_handler(virq, handle_fasteoi_ack_irq);
} else {
-   /* Only the NMI is currently supported. */
-   return -EINVAL;
+   irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+ 
_r_intc_wakeup_chip, 0);
}
}
 
@@ -229,6 +266,22 @@ static const struct irq_domain_ops sun6i_r_intc_domain_ops 
= {
.free   = irq_domain_free_irqs_common,
 };
 
+static int sun6i_r_intc_suspend(void)
+{
+   u32 buf[BITS_TO_U32(max(SUN6I_NR_TOP_LEVEL_IRQS, SUN6I_NR_MUX_BITS))];
+   int i;
+
+   /* Wake IRQs are enabled during system sleep and shutdown. */
+   bitmap_to_arr32(buf, wake_irq_enabled, SUN6I_NR_TOP_LEVEL_IRQS);
+   for (i = 0; i &

[PATCH v4 08/10] ARM: dts: sunxi: Move wakeup-capable IRQs to r_intc

2021-01-11 Thread Samuel Holland
All IRQs that can be used to wake up the system must be routed through
r_intc, so they are visible to firmware while the system is suspended.

In addition to the external NMI input, which is already routed through
r_intc, these include PIO and R_PIO (gpio-keys), the LRADC, and the RTC.

Signed-off-by: Samuel Holland 
---
 arch/arm/boot/dts/sun6i-a31.dtsi | 4 
 arch/arm/boot/dts/sun8i-a23-a33.dtsi | 4 
 arch/arm/boot/dts/sun8i-a83t.dtsi| 3 +++
 arch/arm/boot/dts/sunxi-h3-h5.dtsi   | 3 +++
 4 files changed, 14 insertions(+)

diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index faf85c5f4e1e..50324688c28a 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -611,6 +611,7 @@ ccu: clock@1c2 {
pio: pinctrl@1c20800 {
compatible = "allwinner,sun6i-a31-pinctrl";
reg = <0x01c20800 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ,
 ,
@@ -802,6 +803,7 @@ i2s1: i2s@1c22400 {
lradc: lradc@1c22800 {
compatible = "allwinner,sun4i-a10-lradc-keys";
reg = <0x01c22800 0x100>;
+   interrupt-parent = <_intc>;
interrupts = ;
status = "disabled";
};
@@ -1299,6 +1301,7 @@ rtc: rtc@1f0 {
#clock-cells = <1>;
compatible = "allwinner,sun6i-a31-rtc";
reg = <0x01f0 0x54>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ;
clocks = <>;
@@ -1383,6 +1386,7 @@ ir: ir@1f02000 {
r_pio: pinctrl@1f02c00 {
compatible = "allwinner,sun6i-a31-r-pinctrl";
reg = <0x01f02c00 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ;
clocks = <_gates 0>, <>, < 0>;
diff --git a/arch/arm/boot/dts/sun8i-a23-a33.dtsi 
b/arch/arm/boot/dts/sun8i-a23-a33.dtsi
index a84c90a660ca..4461d5098b20 100644
--- a/arch/arm/boot/dts/sun8i-a23-a33.dtsi
+++ b/arch/arm/boot/dts/sun8i-a23-a33.dtsi
@@ -338,6 +338,7 @@ ccu: clock@1c2 {
pio: pinctrl@1c20800 {
/* compatible gets set in SoC specific dtsi file */
reg = <0x01c20800 0x400>;
+   interrupt-parent = <_intc>;
/* interrupts get set in SoC specific dtsi file */
clocks = < CLK_BUS_PIO>, <>, < 0>;
clock-names = "apb", "hosc", "losc";
@@ -473,6 +474,7 @@ pwm: pwm@1c21400 {
lradc: lradc@1c22800 {
compatible = "allwinner,sun4i-a10-lradc-keys";
reg = <0x01c22800 0x100>;
+   interrupt-parent = <_intc>;
interrupts = ;
status = "disabled";
};
@@ -709,6 +711,7 @@ drc0_out_tcon0: endpoint {
rtc: rtc@1f0 {
compatible = "allwinner,sun8i-a23-rtc";
reg = <0x01f0 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ,
 ;
clock-output-names = "osc32k", "osc32k-out";
@@ -805,6 +808,7 @@ r_i2c: i2c@1f02400 {
r_pio: pinctrl@1f02c00 {
compatible = "allwinner,sun8i-a23-r-pinctrl";
reg = <0x01f02c00 0x400>;
+   interrupt-parent = <_intc>;
interrupts = ;
clocks = <_gates 0>, <>, < 0>;
clock-names = "apb", "hosc", "losc";
diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi 
b/arch/arm/boot/dts/sun8i-a83t.dtsi
index 0fce227f56d4..2c05e296b3d8 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -708,6 +708,7 @@ ccu: clock@1c2 {
 
pio: pinctrl@1c20800 {
compatible = "allwinner,sun8i-a83t-pinctrl";
+   interrupt-parent = <_intc>;
interrupts = ,
 ,
 ;
@@ -1150,6 +1151,7 @@ r_cir: ir@1f02000 {
r_lradc: lradc@1f03

[PATCH v4 09/10] arm64: dts: allwinner: Use the new r_intc binding

2021-01-11 Thread Samuel Holland
The binding of R_INTC was updated to allow specifying interrupts other
than the external NMI, since routing those interrupts through the R_INTC
driver allows using them for wakeup.

Update the device trees to use the new binding.

Signed-off-by: Samuel Holland 
---
 arch/arm64/boot/dts/allwinner/sun50i-a64-amarula-relic.dts | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts  | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts| 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts  | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts| 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-pinebook.dts  | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi| 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-pinetab.dts   | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-sopine.dtsi   | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64-teres-i.dts   | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi  | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-h6-beelink-gs1.dts| 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi.dtsi  | 2 +-
 arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts   | 4 ++--
 arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi   | 5 ++---
 17 files changed, 19 insertions(+), 20 deletions(-)

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-amarula-relic.dts 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-amarula-relic.dts
index c7bd73f35ed8..f17cc89f472d 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-amarula-relic.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-amarula-relic.dts
@@ -173,7 +173,7 @@ axp803: pmic@3a3 {
compatible = "x-powers,axp803";
reg = <0x3a3>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
x-powers,drive-vbus-en; /* set N_VBUSEN as output pin */
};
 };
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts
index e5e840b9fbb4..e45bef292aa3 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts
@@ -191,7 +191,7 @@ axp803: pmic@3a3 {
compatible = "x-powers,axp803";
reg = <0x3a3>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
x-powers,drive-vbus-en; /* set N_VBUSEN as output pin */
};
 };
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts
index e58db8a6cab6..57b64c57781b 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-nanopi-a64.dts
@@ -152,7 +152,7 @@ axp803: pmic@3a3 {
compatible = "x-powers,axp803";
reg = <0x3a3>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
};
 };
 
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts
index f3f8e177ab61..ec7e2c0e82c1 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-olinuxino.dts
@@ -185,7 +185,7 @@ axp803: pmic@3a3 {
compatible = "x-powers,axp803";
reg = <0x3a3>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
x-powers,drive-vbus-en; /* set N_VBUSEN as output pin */
};
 };
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts
index 70e31743f0ba..097a5511523a 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-orangepi-win.dts
@@ -192,7 +192,7 @@ axp803: pmic@3a3 {
compatible = "x-powers,axp803";
reg = <0x3a3>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
x-powers,drive-vbus-en; /* set N_VBUSEN as output pin */
};
 };
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts 
b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
index 329cf276561e..2accb5ddf783 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts
@@ -139,7 +139,7 @@

[PATCH v4 06/10] ARM: dts: sunxi: Use the new r_intc binding

2021-01-11 Thread Samuel Holland
The binding of R_INTC was updated to allow specifying interrupts other
than the external NMI, since routing those interrupts through the R_INTC
driver allows using them for wakeup.

Update the device trees to use the new binding.

Signed-off-by: Samuel Holland 
---
 arch/arm/boot/dts/sun6i-a31-hummingbird.dts  | 2 +-
 arch/arm/boot/dts/sun6i-a31-m9.dts   | 2 +-
 arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts | 2 +-
 arch/arm/boot/dts/sun6i-a31.dtsi | 2 +-
 arch/arm/boot/dts/sun6i-a31s-primo81.dts | 2 +-
 arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi   | 2 +-
 arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts | 2 +-
 arch/arm/boot/dts/sun6i-a31s-yones-toptech-bs1078-v2.dts | 2 +-
 arch/arm/boot/dts/sun6i-reference-design-tablet.dtsi | 2 +-
 arch/arm/boot/dts/sun8i-a23-a33.dtsi | 2 +-
 arch/arm/boot/dts/sun8i-a33-olinuxino.dts| 2 +-
 arch/arm/boot/dts/sun8i-a33-sinlinx-sina33.dts   | 2 +-
 arch/arm/boot/dts/sun8i-a83t-allwinner-h8homlet-v2.dts   | 4 ++--
 arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts | 4 ++--
 arch/arm/boot/dts/sun8i-a83t-cubietruck-plus.dts | 4 ++--
 arch/arm/boot/dts/sun8i-a83t-tbs-a711.dts| 4 ++--
 arch/arm/boot/dts/sun8i-a83t.dtsi| 2 +-
 arch/arm/boot/dts/sun8i-r16-bananapi-m2m.dts | 2 +-
 arch/arm/boot/dts/sun8i-r16-parrot.dts   | 2 +-
 arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi | 2 +-
 20 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts 
b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
index 486cec6f71e0..236ebfc06192 100644
--- a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
+++ b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
@@ -227,7 +227,7 @@ axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
x-powers,drive-vbus-en;
};
 };
diff --git a/arch/arm/boot/dts/sun6i-a31-m9.dts 
b/arch/arm/boot/dts/sun6i-a31-m9.dts
index 6aeb5a9696f7..2436b13cbce1 100644
--- a/arch/arm/boot/dts/sun6i-a31-m9.dts
+++ b/arch/arm/boot/dts/sun6i-a31-m9.dts
@@ -116,7 +116,7 @@ axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
};
 };
 
diff --git a/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts 
b/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts
index 6c6c1bd22bf6..ce712bdd8cd0 100644
--- a/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts
+++ b/arch/arm/boot/dts/sun6i-a31-mele-a1000g-quad.dts
@@ -116,7 +116,7 @@ axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
};
 };
 
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index 6a733a36d34a..faf85c5f4e1e 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -1308,7 +1308,7 @@ rtc: rtc@1f0 {
r_intc: interrupt-controller@1f00c00 {
compatible = "allwinner,sun6i-a31-r-intc";
interrupt-controller;
-   #interrupt-cells = <2>;
+   #interrupt-cells = <3>;
reg = <0x01f00c00 0x400>;
interrupts = ;
};
diff --git a/arch/arm/boot/dts/sun6i-a31s-primo81.dts 
b/arch/arm/boot/dts/sun6i-a31s-primo81.dts
index 429a165b79b2..c5c85eb44cc7 100644
--- a/arch/arm/boot/dts/sun6i-a31s-primo81.dts
+++ b/arch/arm/boot/dts/sun6i-a31s-primo81.dts
@@ -160,7 +160,7 @@ axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
x-powers,drive-vbus-en;
};
 };
diff --git a/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi 
b/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi
index 7455c0db4a8a..227ad489731c 100644
--- a/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31s-sina31s-core.dtsi
@@ -79,7 +79,7 @@ axp22x: pmic@68 {
compatible = "x-powers,axp221";
reg = <0x68>;
interrupt-parent = <_intc>;
-   interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+   interrupts = ;
};
 };
 
diff 

[PATCH v4 00/10] sunxi: Support IRQ wakeup from deep sleep

2021-01-11 Thread Samuel Holland
Allwinner sun6i/sun8i/sun50i SoCs (A31 and newer) have two interrupt
controllers: GIC and R_INTC. GIC does not support wakeup. R_INTC handles
the external NMI pin, and provides 32+ IRQs to the ARISC. The first 16
of these correspond 1:1 to a block of GIC IRQs starting with the NMI.
The last 13-16 multiplex the first (up to) 128 GIC SPIs.

This series replaces the existing chained irqchip driver that could only
control the NMI, with a stacked irqchip driver that also provides wakeup
capability for those multiplexed SPI IRQs. The idea is to preconfigure
the ARISC's IRQ controller, and then the ARISC firmware knows to wake up
as soon as it receives an IRQ. It can also decide how deep it can
suspend based on the enabled wakeup IRQs.

As future work, it may be useful to do the chained->stacked conversion
on the sunxi-nmi driver as well.

Patches 1-2 add the new bindings.
Patch 3 adds the new driver.
Patch 4 adds wakeup capability.
Remaining patches update the device trees to use R_INTC where beneficial.

With appropriate firmware and configuration, this series allows waking
from (and it has been tested with) the RTC, NMI/PMIC (power button, A/C
plug, etc.), all GPIO ports (button, lid switch, modem, etc.), LRADC,
and UARTs. I have tested this patch set on the H3, A64, H5, and H6 SoCs.

---
Changes from v3:
 - Removed A31 fallback from H6 compatible.
 - Switch to additionalProperties in binding.
 - Replace wall of text with ASCII art.
 - Added macros for NMI_SRC_TYPE constants.
 - Renamed NR_IRQS to NR_TOP_LEVEL_IRQS to hopefully be more clear.
 - Use non-relaxed writel in sun6i_r_intc_ack_nmi to fix spurious level
   interrupts (reordering with gic_unmask_irq).
 - Use a single irq_chip for edge and level NMI configurations.
   - For edge, ack ASAP using handle_fasteoi_ack_irq.
   - For level, ack in .irq_unmask if masked at EOI, else in .irq_eoi.
 - Enforce that the R_INTC->GIC trigger is IRQ_TYPE_LEVEL_HIGH.
 - Implement .irq_set_irqchip_state.
 - Move other IRQs to a new irq_chip that only intercepts .irq_set_wake.
 - Use radix instead of linear for the IRQ domain since only a handful
   of the 128 hwirqs will ever be used.

Changes from v2:
 - Fix edge IRQs on GICv2 with EOImode == 0, as found on A83T and older.
   - Replace .irq_ack callback with .irq_mask.
   - Drop IRQCHIP_EOI_THREADED.
   - This removes the dependency on IRQ_FASTEOI_HIERARCHY_HANDLERS.
 - Move IRQ_DOMAIN_HIERARCHY selection to ARCH_SUNXI to fix A83T build.
 - Add support for the second IRQ ENABLE/PENDING register on H6 and up.
 - Add support for multiplexed IRQs beyond the initial 16.
   - This requires a new binding, but keeps old binding compatibility.
   - This requires a separate mux mapping for H6 and up.
 - Rename parent_* => nmi_* because they only apply to the NMI.
 - Merge code common to probe and resume functions.
 - Also run suspend callback at syscore shutdown, for boards with no
   PMIC where firmware is also responsible for poweroff/poweron.
   - These two changes mean nothing is conditional on CONFIG_PM_SLEEP
 anymore, since all code is used even without it.
 - Since the binding changed, update all SoC DTs, A31 and up.
 - Drop r_ir from inclusion (it needs more than an IRQ to wake) and
   include pio (the main pin controller) and (r_)lradc.
 - As there are significant changes, I did not carry forward Maxime's
   Acked-by or Rob's Reviewed-by.

Changes from v1:
 - Use writel_relaxed() instead if writel().
 - Remove use of the MASK register, as it doesn't affect the NMI as seen
   by the GIC. It only affects the IRQs seen by the coprocessor.
 - Leave NMI_HWIRQ enabled at all times, since it can be masked at the
   GIC level (removed .irq_enable and .irq_disable).
 - Use .irq_ack vs .irq_eoi depending on the trigger type, to avoid
   missing interrupts or double interrupts.
   - Because of this change, the driver needs two "irq_chip"s, one
 with .irq_eoi set to our function and one without.
   - Also because of this, we need IRQ_FASTEOI_HIERARCHY_HANDLERS for
 handle_fasteoi_ack_irq(), so our .irq_ack function gets called
 while the GIC driver works as if handle_fasteoi_irq() was used.
 - Inline the SUNXI_SRC_TYPE_* enum into sun6i_r_intc_irq_set_type().
 - Add a comment explaining how the trigger type is used.
 - Don't call irqd_set_trigger_type().
 - Set IRQCHIP_SET_TYPE_MASKED to match the GIC (since flags from this
   driver mask flags from that one).
 - Set IRQCHIP_EOI_THREADED to avoid doubled level interrupts, since the
   latch will be set again as long as the trigger is met.
 - Replace sun6i_r_intc_domain_translate() with
   irq_domain_translate_twocell().
 - Use an enum for the device tree binding.
 - Update commit messages for accuracy and typos.

Samuel Holland (10):
  dt-bindings: irq: sun6i-r: Split the binding from sun7i-nmi
  dt-bindings: irq: sun6i-r: Add a compatible for the H3
  irqchip/sun6i-r: Use a stacked irqchip driver
  irqchip/sun6i-r: Add wakeup support
  ARM:

[PATCH v4 01/10] dt-bindings: irq: sun6i-r: Split the binding from sun7i-nmi

2021-01-11 Thread Samuel Holland
The R_INTC in the A31 and newer sun8i/sun50i SoCs has additional
functionality compared to the sun7i/sun9i NMI controller. Among other
things, it multiplexes access to up to 128 interrupts corresponding to
(and in parallel to) the first 128 GIC SPIs. This means the NMI is no
longer the lowest-numbered hwirq at this irqchip, since it is SPI 32 or
96 (depending on SoC). hwirq 0 now corresponds to SPI 0, usually UART0.

To allow access to all multiplexed IRQs, the R_INTC requires a new
binding where the interrupt number matches the GIC interrupt number.
Otherwise, interrupts with hwirq numbers below the NMI would not be
representable in the device tree.

For simplicity, copy the three-cell GIC binding; this disambiguates
interrupt 0 in the old binding (the NMI) from interrupt 0 in the new
binding (SPI 0) by the number of cells.

Because the H6 R_INTC has a different mapping from multiplexed IRQs to
top-level register bits, it is no longer compatible with the A31 R_INTC.

Signed-off-by: Samuel Holland 
---
 .../allwinner,sun6i-a31-r-intc.yaml   | 66 +++
 .../allwinner,sun7i-a20-sc-nmi.yaml   | 10 ---
 2 files changed, 66 insertions(+), 10 deletions(-)
 create mode 100644 
Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml

diff --git 
a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml
 
b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml
new file mode 100644
index ..50e607e607c8
--- /dev/null
+++ 
b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: 
http://devicetree.org/schemas/interrupt-controller/allwinner,sun6i-a31-r-intc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner A31 NMI/Wakeup Interrupt Controller Device Tree Bindings
+
+maintainers:
+  - Chen-Yu Tsai 
+  - Maxime Ripard 
+
+allOf:
+  - $ref: /schemas/interrupt-controller.yaml#
+
+properties:
+  "#interrupt-cells":
+const: 3
+description:
+  The first cell is GIC_SPI (0), the second cell is the IRQ number, and
+  the third cell is the trigger type as defined in interrupt.txt in this
+  directory.
+
+  compatible:
+oneOf:
+  - const: allwinner,sun6i-a31-r-intc
+  - items:
+  - enum:
+  - allwinner,sun8i-a83t-r-intc
+  - allwinner,sun50i-a64-r-intc
+  - const: allwinner,sun6i-a31-r-intc
+  - const: allwinner,sun50i-h6-r-intc
+
+  reg:
+maxItems: 1
+
+  interrupts:
+maxItems: 1
+description:
+  The GIC interrupt labeled as "External NMI".
+
+  interrupt-controller: true
+
+required:
+  - "#interrupt-cells"
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-controller
+
+additionalProperties: false
+
+examples:
+  - |
+#include 
+
+r_intc: interrupt-controller@1f00c00 {
+compatible = "allwinner,sun50i-a64-r-intc",
+ "allwinner,sun6i-a31-r-intc";
+interrupt-controller;
+#interrupt-cells = <3>;
+reg = <0x01f00c00 0x400>;
+interrupts = ;
+};
+
+...
diff --git 
a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml
 
b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml
index 4fd1e2780026..7fc9ad5ef38c 100644
--- 
a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml
+++ 
b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml
@@ -22,26 +22,16 @@ properties:
 
   compatible:
 oneOf:
-  - const: allwinner,sun6i-a31-r-intc
   - const: allwinner,sun6i-a31-sc-nmi
 deprecated: true
   - const: allwinner,sun7i-a20-sc-nmi
-  - items:
-  - const: allwinner,sun8i-a83t-r-intc
-  - const: allwinner,sun6i-a31-r-intc
   - items:
   - const: allwinner,sun8i-v3s-nmi
   - const: allwinner,sun9i-a80-nmi
   - const: allwinner,sun9i-a80-nmi
-  - items:
-  - const: allwinner,sun50i-a64-r-intc
-  - const: allwinner,sun6i-a31-r-intc
   - items:
   - const: allwinner,sun50i-a100-nmi
   - const: allwinner,sun9i-a80-nmi
-  - items:
-  - const: allwinner,sun50i-h6-r-intc
-  - const: allwinner,sun6i-a31-r-intc
 
   reg:
 maxItems: 1
-- 
2.26.2



  1   2   3   4   5   >