Re: [PATCH RFC 3/4] pwm: sunxi: Add support Allwinner D1 PWM

2024-05-20 Thread John Watts
On Sat, May 18, 2024 at 01:54:45PM +1000, John Watts wrote:
> This driver documents and handles setting up PWM on the D1.
> 
> Signed-off-by: John Watts 
> ---
>  drivers/pwm/Kconfig|   6 +
>  drivers/pwm/Makefile   |   1 +
>  drivers/pwm/sunxi_pwm_d1.c | 542 
> +
>  3 files changed, 549 insertions(+)

Patch v9 of the kernel uses apb now instead of apb0, so this should be changed
to fit once the kernel driver is merged.

> 
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 6e79868d0e..8c4c910ea7 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -112,6 +112,12 @@ config PWM_SUNXI
> This PWM is found on H3, A64 and other Allwinner SoCs. It supports a
> programmable period and duty cycle. A 16-bit counter is used.
>  
> +config PWM_SUNXI_D1
> + bool "Enable support for the Allwinner D1 Sunxi PWM"
> + depends on DM_PWM
> + help
> +   This PWM is found on D1, T113-S3 and R329 SoCs.
> +
>  config PWM_TI_EHRPWM
>   bool "Enable support for EHRPWM PWM"
>   depends on DM_PWM && ARCH_OMAP2PLUS
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index e4d10c8dc3..ea96e7159b 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -23,4 +23,5 @@ obj-$(CONFIG_PWM_SANDBOX)   += sandbox_pwm.o
>  obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
>  obj-$(CONFIG_PWM_TEGRA)  += tegra_pwm.o
>  obj-$(CONFIG_PWM_SUNXI)  += sunxi_pwm.o
> +obj-$(CONFIG_PWM_SUNXI_D1)   += sunxi_pwm_d1.o
>  obj-$(CONFIG_PWM_TI_EHRPWM)  += pwm-ti-ehrpwm.o
> diff --git a/drivers/pwm/sunxi_pwm_d1.c b/drivers/pwm/sunxi_pwm_d1.c
> new file mode 100644
> index 00..6c57bc6e85
> --- /dev/null
> +++ b/drivers/pwm/sunxi_pwm_d1.c
> @@ -0,0 +1,542 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright 2022 Jookia 
> +/*
> + * The Allwinner D1's PWM channels are 16-bit counters with up to
> + * 65537 cycles (yes, you read that correctly).
> + *
> + * Each channel must be programmed using three variables:
> + * - The entire cycle count (used for the period)
> + * - The active cycle count (the count of inactive cycles)
> + * - The polarity (specifies if the signal is active high or low)
> + * The cycle counts are at minimum 1 and at maximum 65536.
> + *
> + * The controller will output the number of entire cycles plus one
> + * extra, with any cycles after the active cycle output as active.
> + *
> + * Consider a PWM period of 128 nanoseconds and a cycle period of 32.
> + * Setting the entire cycle count to 3 and active cycle count to 4
> + * gives an output like so:
> + *
> + * - Cycle 1 runs 0 to 32 nanoseconds, inactive
> + * - Cycle 2 runs 32 to 64 nanoseconds, inactive
> + * - Cycle 3 runs 64 to 96 nanoseconds, inactive
> + * - Cycle 4 runs 96 to 128 nanoseconds, inactive
> + * - Cycle 5 is skipped but would run 128 to 160 nanoseconds, active
> + *
> + * If we set the entire count to 4, cycle 5 would run and we wouldn't be
> + * able to specify it as inactive as the active count only goes up to 4.
> + *
> + * In practice this means we want to set the entire cycle to be one less
> + * then the actual number of cycles we want, so we can set the number of
> + * active cycles to be up to maximum for a fully inactive signal.
> + *
> + * The PWM channels are paired and clocked together, resulting in a
> + * cycle time found using the following formula:
> + *
> + * PWM0_CYCLE_NS = 10 / (BUS_CLOCK / COMMON_DIV / PWM0_PRESCALER_K)
> + * PWM1_CYCLE_NS = 10 / (BUS_CLOCK / COMMON_DIV / PWM1_PRESCALER_K)
> + *
> + * This means both clocks should ideally be set at the same time and not
> + * impact each other too much.
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +/* PWM channel information */
> +struct pwm_channel {
> + uint period_ns;
> + uint duty_ns;
> + bool polarity;
> + bool enable;
> + bool updated;
> +};
> +
> +/* Timings found for a PWM channel */
> +struct pwm_timings {
> + uint cycle_ns;
> + uint period_ns;
> + uint duty_ns;
> + uint clock_id;
> + uint common_div;
> + uint prescale_k;
> + uint entire_cycles;
> + uint active_cycles;
> + uint polarity;
> +};
> +
> +/* Driver state */
> +struct sunxi_pwm_d1_priv {
> + void *base;
> + struct clk *clk_bus;
> + struct clk *clk_srcs[3]; /* Last value must be NULL */
> + struct reset_ctl *reset;
> + int npwm;
> + struct pwm_channel *channels;
> +};
> +
> +/* Divides a nanosecond 

Re: [PATCH FOR TESTING ONLY RFC 4/4] sunxi-d1s-t113: Add D1 and T113 PWM node

2024-05-20 Thread John Watts
On Mon, May 20, 2024 at 03:58:12PM +0530, Sumit Garg wrote:
> This change shouldn't be needed for your testing purposes too. It only
> comes into picture once you enable OF_UPSTREAM. BTW, DT source files
> in dts/upstream are strictly following/syncing against Linux kernel DT
> sources. So all the custom U-Boot specific DT stuff belongs to
> arch/${ARCH}/dts/.
> 
> -Sumit

Hi,

I use OF_UPSTREAM.

John.


Re: [PATCH RFC 3/4] pwm: sunxi: Add support Allwinner D1 PWM

2024-05-20 Thread John Watts
After a long time reading the datasheet I found out my approach was
actually wrong and led to an off by on error.

Signed-off-by: John Watts 
---
 drivers/pwm/sunxi_pwm_d1.c | 57 +-
 1 file changed, 32 insertions(+), 25 deletions(-)

diff --git a/drivers/pwm/sunxi_pwm_d1.c b/drivers/pwm/sunxi_pwm_d1.c
index 6c57bc6e85..f396afff3e 100644
--- a/drivers/pwm/sunxi_pwm_d1.c
+++ b/drivers/pwm/sunxi_pwm_d1.c
@@ -1,34 +1,41 @@
 // SPDX-License-Identifier: GPL-2.0
 // Copyright 2022 Jookia 
 /*
- * The Allwinner D1's PWM channels are 16-bit counters with up to
- * 65537 cycles (yes, you read that correctly).
+ * The Allwinner D1's PWM channels are paired 16-bit counters.
  *
  * Each channel must be programmed using three variables:
  * - The entire cycle count (used for the period)
- * - The active cycle count (the count of inactive cycles)
- * - The polarity (specifies if the signal is active high or low)
- * The cycle counts are at minimum 1 and at maximum 65536.
+ * - The active cycle count (used for the duty cycle)
+ * - The active state polarity (specifies if the signal goes high or low)
  *
- * The controller will output the number of entire cycles plus one
- * extra, with any cycles after the active cycle output as active.
+ * All counts are zero based, but the datasheet spends a lot of time
+ * adding 1 to the entire cycle count. There's no hidden extra cycle,
+ * it's just trying to make it human understandable. After all, you can
+ * have 0 active counts for a 100% duty cycle, but 0 entire cycles
+ * doesn't really mean sense or work in time calculations.
  *
- * Consider a PWM period of 128 nanoseconds and a cycle period of 32.
- * Setting the entire cycle count to 3 and active cycle count to 4
- * gives an output like so:
+ * The counter works like this (quoting the datasheet):
+ * - PCNTR = (PCNTR == PWM_ENTIRE_CYCLE) ? 0 : PCNTR + 1
+ * - PCNTR > (PWM_ENTIRE_CYCLE - PWM_ACT_CYCLE) = Output active state
+ * - PCNTR <= (PWM_ENTIRE_CYCLE - PWM_ACT_CYCLE) = Output inactive state
  *
- * - Cycle 1 runs 0 to 32 nanoseconds, inactive
- * - Cycle 2 runs 32 to 64 nanoseconds, inactive
- * - Cycle 3 runs 64 to 96 nanoseconds, inactive
- * - Cycle 4 runs 96 to 128 nanoseconds, inactive
- * - Cycle 5 is skipped but would run 128 to 160 nanoseconds, active
+ * Here's a 2-bit table of cycle counts versus active cycle counts:
+ *   Active:  |   0 |1  |2  |3 |
+ *   Count 0  | Active  | Inactive  | Inactive  | Inactive |
+ *   Count 1  | Active  | Active| Inactive  | Inactive |
+ *   Count 2  | Active  | Active| Active| Inactive |
+ *   Count 3  | Active  | Active| Active| Active   |
  *
- * If we set the entire count to 4, cycle 5 would run and we wouldn't be
- * able to specify it as inactive as the active count only goes up to 4.
+ * An entire count of 2 and active count of 3 would always be inactive.
  *
- * In practice this means we want to set the entire cycle to be one less
- * then the actual number of cycles we want, so we can set the number of
- * active cycles to be up to maximum for a fully inactive signal.
+ * The main takeaways here for us are:
+ * - The counter wraps around when it hits the entire cycle count
+ * - The output is active after the counter equals the active cycle count
+ * - An active count of 0 means the period is a 100% active cycle
+ * - An active count larger than the entire cycle count is a 0% active cycle
+ *
+ * This driver deals with the last problem by limiting the entire cycles
+ * to 65534 so we can always specify 65535 for a 0% active cycle.
  *
  * The PWM channels are paired and clocked together, resulting in a
  * cycle time found using the following formula:
@@ -123,7 +130,7 @@ int find_channel_dividers(uint period_ns,
  uint parent_hz,
  struct pwm_timings *out)
 {
-   uint ideal_cycle_ns = div_ns(period_ns, 65536);
+   uint ideal_cycle_ns = div_ns(period_ns, 65535);
int common_div = out->common_div;
int prescaler = 1;
uint cycle_ns = 0;
@@ -160,10 +167,10 @@ int find_channel_timings(const struct pwm_channel *in,
if (find_channel_dividers(in->period_ns, parent_hz, ))
return -1;
 
-   new.entire_cycles = (in->period_ns / new.cycle_ns) - 1;
+   new.entire_cycles = (in->period_ns / new.cycle_ns);
new.active_cycles = (in->duty_ns / new.cycle_ns);
-   new.period_ns = (new.entire_cycles + 1) * new.cycle_ns;
-   new.duty_ns = new.active_cycles * new.cycle_ns;
+   new.period_ns = (new.entire_cycles * new.cycle_ns);
+   new.duty_ns = (new.active_cycles * new.cycle_ns);
new.polarity = in->polarity;
 
if (error_too_large(new.period_ns, in->period_ns))
@@ -311,7 +318,7 @@ void enable_channel(void *base, int channel, struct 
pwm_timings *timings)
 {
u32 pwm_active = (timings->polarity) ? 0 : PCR_PWM_ACTIVE;
 

Re: [PATCH RFC 3/4] pwm: sunxi: Add support Allwinner D1 PWM

2024-05-20 Thread John Watts
After a long time reading the datasheet I found out my approach was
actually wrong and led to an off by on error.

Signed-off-by: John Watts 
---
 drivers/pwm/sunxi_pwm_d1.c | 57 +-
 1 file changed, 32 insertions(+), 25 deletions(-)

diff --git a/drivers/pwm/sunxi_pwm_d1.c b/drivers/pwm/sunxi_pwm_d1.c
index 6c57bc6e85..f396afff3e 100644
--- a/drivers/pwm/sunxi_pwm_d1.c
+++ b/drivers/pwm/sunxi_pwm_d1.c
@@ -1,34 +1,41 @@
 // SPDX-License-Identifier: GPL-2.0
 // Copyright 2022 Jookia 
 /*
- * The Allwinner D1's PWM channels are 16-bit counters with up to
- * 65537 cycles (yes, you read that correctly).
+ * The Allwinner D1's PWM channels are paired 16-bit counters.
  *
  * Each channel must be programmed using three variables:
  * - The entire cycle count (used for the period)
- * - The active cycle count (the count of inactive cycles)
- * - The polarity (specifies if the signal is active high or low)
- * The cycle counts are at minimum 1 and at maximum 65536.
+ * - The active cycle count (used for the duty cycle)
+ * - The active state polarity (specifies if the signal goes high or low)
  *
- * The controller will output the number of entire cycles plus one
- * extra, with any cycles after the active cycle output as active.
+ * All counts are zero based, but the datasheet spends a lot of time
+ * adding 1 to the entire cycle count. There's no hidden extra cycle,
+ * it's just trying to make it human understandable. After all, you can
+ * have 0 active counts for a 100% duty cycle, but 0 entire cycles
+ * doesn't really mean sense or work in time calculations.
  *
- * Consider a PWM period of 128 nanoseconds and a cycle period of 32.
- * Setting the entire cycle count to 3 and active cycle count to 4
- * gives an output like so:
+ * The counter works like this (quoting the datasheet):
+ * - PCNTR = (PCNTR == PWM_ENTIRE_CYCLE) ? 0 : PCNTR + 1
+ * - PCNTR > (PWM_ENTIRE_CYCLE - PWM_ACT_CYCLE) = Output active state
+ * - PCNTR <= (PWM_ENTIRE_CYCLE - PWM_ACT_CYCLE) = Output inactive state
  *
- * - Cycle 1 runs 0 to 32 nanoseconds, inactive
- * - Cycle 2 runs 32 to 64 nanoseconds, inactive
- * - Cycle 3 runs 64 to 96 nanoseconds, inactive
- * - Cycle 4 runs 96 to 128 nanoseconds, inactive
- * - Cycle 5 is skipped but would run 128 to 160 nanoseconds, active
+ * Here's a 2-bit table of cycle counts versus active cycle counts:
+ *   Active:  |   0 |1  |2  |3 |
+ *   Count 0  | Active  | Inactive  | Inactive  | Inactive |
+ *   Count 1  | Active  | Active| Inactive  | Inactive |
+ *   Count 2  | Active  | Active| Active| Inactive |
+ *   Count 3  | Active  | Active| Active| Active   |
  *
- * If we set the entire count to 4, cycle 5 would run and we wouldn't be
- * able to specify it as inactive as the active count only goes up to 4.
+ * An entire count of 2 and active count of 3 would always be inactive.
  *
- * In practice this means we want to set the entire cycle to be one less
- * then the actual number of cycles we want, so we can set the number of
- * active cycles to be up to maximum for a fully inactive signal.
+ * The main takeaways here for us are:
+ * - The counter wraps around when it hits the entire cycle count
+ * - The output is active after the counter equals the active cycle count
+ * - An active count of 0 means the period is a 100% active cycle
+ * - An active count larger than the entire cycle count is a 0% active cycle
+ *
+ * This driver deals with the last problem by limiting the entire cycles
+ * to 65534 so we can always specify 65535 for a 0% active cycle.
  *
  * The PWM channels are paired and clocked together, resulting in a
  * cycle time found using the following formula:
@@ -123,7 +130,7 @@ int find_channel_dividers(uint period_ns,
  uint parent_hz,
  struct pwm_timings *out)
 {
-   uint ideal_cycle_ns = div_ns(period_ns, 65536);
+   uint ideal_cycle_ns = div_ns(period_ns, 65535);
int common_div = out->common_div;
int prescaler = 1;
uint cycle_ns = 0;
@@ -160,10 +167,10 @@ int find_channel_timings(const struct pwm_channel *in,
if (find_channel_dividers(in->period_ns, parent_hz, ))
return -1;
 
-   new.entire_cycles = (in->period_ns / new.cycle_ns) - 1;
+   new.entire_cycles = (in->period_ns / new.cycle_ns);
new.active_cycles = (in->duty_ns / new.cycle_ns);
-   new.period_ns = (new.entire_cycles + 1) * new.cycle_ns;
-   new.duty_ns = new.active_cycles * new.cycle_ns;
+   new.period_ns = (new.entire_cycles * new.cycle_ns);
+   new.duty_ns = (new.active_cycles * new.cycle_ns);
new.polarity = in->polarity;
 
if (error_too_large(new.period_ns, in->period_ns))
@@ -311,7 +318,7 @@ void enable_channel(void *base, int channel, struct 
pwm_timings *timings)
 {
u32 pwm_active = (timings->polarity) ? 0 : PCR_PWM_ACTIVE;
 

[PATCH FOR TESTING ONLY RFC 4/4] sunxi-d1s-t113: Add D1 and T113 PWM node

2024-05-17 Thread John Watts
This is based on the binding from the as yet unmerged kernel series:

https://lore.kernel.org/linux-kernel/20240131125920.2879433-2-privates...@gmail.com/

Signed-off-by: John Watts 
---
 arch/riscv/dts/sunxi-d1s-t113.dtsi   | 12 
 dts/upstream/src/riscv/allwinner/sunxi-d1s-t113.dtsi | 12 
 2 files changed, 24 insertions(+)

diff --git a/arch/riscv/dts/sunxi-d1s-t113.dtsi 
b/arch/riscv/dts/sunxi-d1s-t113.dtsi
index 822f022eec..92b6432f77 100644
--- a/arch/riscv/dts/sunxi-d1s-t113.dtsi
+++ b/arch/riscv/dts/sunxi-d1s-t113.dtsi
@@ -145,6 +145,18 @@
};
};
 
+   pwm: pwm@2000c00 {
+   compatible = "allwinner,sun20i-d1-pwm";
+   reg = <0x02000c00 0x400>;
+   clocks = < CLK_BUS_PWM>,
+<>,
+< CLK_APB0>;
+   clock-names = "bus", "hosc", "apb0";
+   resets = < RST_BUS_PWM>;
+   status = "disabled";
+   #pwm-cells = <0x3>;
+   };
+
ccu: clock-controller@2001000 {
compatible = "allwinner,sun20i-d1-ccu";
reg = <0x2001000 0x1000>;
diff --git a/dts/upstream/src/riscv/allwinner/sunxi-d1s-t113.dtsi 
b/dts/upstream/src/riscv/allwinner/sunxi-d1s-t113.dtsi
index 5a9d7f5a75..435a1e66aa 100644
--- a/dts/upstream/src/riscv/allwinner/sunxi-d1s-t113.dtsi
+++ b/dts/upstream/src/riscv/allwinner/sunxi-d1s-t113.dtsi
@@ -145,6 +145,18 @@
};
};
 
+   pwm: pwm@2000c00 {
+   compatible = "allwinner,sun20i-d1-pwm";
+   reg = <0x02000c00 0x400>;
+   clocks = < CLK_BUS_PWM>,
+<>,
+< CLK_APB0>;
+   clock-names = "bus", "hosc", "apb0";
+   resets = < RST_BUS_PWM>;
+   status = "disabled";
+   #pwm-cells = <0x3>;
+   };
+
ccu: clock-controller@2001000 {
compatible = "allwinner,sun20i-d1-ccu";
reg = <0x2001000 0x1000>;

-- 
2.45.1



[PATCH RFC 3/4] pwm: sunxi: Add support Allwinner D1 PWM

2024-05-17 Thread John Watts
This driver documents and handles setting up PWM on the D1.

Signed-off-by: John Watts 
---
 drivers/pwm/Kconfig|   6 +
 drivers/pwm/Makefile   |   1 +
 drivers/pwm/sunxi_pwm_d1.c | 542 +
 3 files changed, 549 insertions(+)

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 6e79868d0e..8c4c910ea7 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -112,6 +112,12 @@ config PWM_SUNXI
  This PWM is found on H3, A64 and other Allwinner SoCs. It supports a
  programmable period and duty cycle. A 16-bit counter is used.
 
+config PWM_SUNXI_D1
+   bool "Enable support for the Allwinner D1 Sunxi PWM"
+   depends on DM_PWM
+   help
+ This PWM is found on D1, T113-S3 and R329 SoCs.
+
 config PWM_TI_EHRPWM
bool "Enable support for EHRPWM PWM"
depends on DM_PWM && ARCH_OMAP2PLUS
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index e4d10c8dc3..ea96e7159b 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -23,4 +23,5 @@ obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o
 obj-$(CONFIG_PWM_SIFIVE)   += pwm-sifive.o
 obj-$(CONFIG_PWM_TEGRA)+= tegra_pwm.o
 obj-$(CONFIG_PWM_SUNXI)+= sunxi_pwm.o
+obj-$(CONFIG_PWM_SUNXI_D1) += sunxi_pwm_d1.o
 obj-$(CONFIG_PWM_TI_EHRPWM)+= pwm-ti-ehrpwm.o
diff --git a/drivers/pwm/sunxi_pwm_d1.c b/drivers/pwm/sunxi_pwm_d1.c
new file mode 100644
index 00..6c57bc6e85
--- /dev/null
+++ b/drivers/pwm/sunxi_pwm_d1.c
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2022 Jookia 
+/*
+ * The Allwinner D1's PWM channels are 16-bit counters with up to
+ * 65537 cycles (yes, you read that correctly).
+ *
+ * Each channel must be programmed using three variables:
+ * - The entire cycle count (used for the period)
+ * - The active cycle count (the count of inactive cycles)
+ * - The polarity (specifies if the signal is active high or low)
+ * The cycle counts are at minimum 1 and at maximum 65536.
+ *
+ * The controller will output the number of entire cycles plus one
+ * extra, with any cycles after the active cycle output as active.
+ *
+ * Consider a PWM period of 128 nanoseconds and a cycle period of 32.
+ * Setting the entire cycle count to 3 and active cycle count to 4
+ * gives an output like so:
+ *
+ * - Cycle 1 runs 0 to 32 nanoseconds, inactive
+ * - Cycle 2 runs 32 to 64 nanoseconds, inactive
+ * - Cycle 3 runs 64 to 96 nanoseconds, inactive
+ * - Cycle 4 runs 96 to 128 nanoseconds, inactive
+ * - Cycle 5 is skipped but would run 128 to 160 nanoseconds, active
+ *
+ * If we set the entire count to 4, cycle 5 would run and we wouldn't be
+ * able to specify it as inactive as the active count only goes up to 4.
+ *
+ * In practice this means we want to set the entire cycle to be one less
+ * then the actual number of cycles we want, so we can set the number of
+ * active cycles to be up to maximum for a fully inactive signal.
+ *
+ * The PWM channels are paired and clocked together, resulting in a
+ * cycle time found using the following formula:
+ *
+ * PWM0_CYCLE_NS = 10 / (BUS_CLOCK / COMMON_DIV / PWM0_PRESCALER_K)
+ * PWM1_CYCLE_NS = 10 / (BUS_CLOCK / COMMON_DIV / PWM1_PRESCALER_K)
+ *
+ * This means both clocks should ideally be set at the same time and not
+ * impact each other too much.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/* PWM channel information */
+struct pwm_channel {
+   uint period_ns;
+   uint duty_ns;
+   bool polarity;
+   bool enable;
+   bool updated;
+};
+
+/* Timings found for a PWM channel */
+struct pwm_timings {
+   uint cycle_ns;
+   uint period_ns;
+   uint duty_ns;
+   uint clock_id;
+   uint common_div;
+   uint prescale_k;
+   uint entire_cycles;
+   uint active_cycles;
+   uint polarity;
+};
+
+/* Driver state */
+struct sunxi_pwm_d1_priv {
+   void *base;
+   struct clk *clk_bus;
+   struct clk *clk_srcs[3]; /* Last value must be NULL */
+   struct reset_ctl *reset;
+   int npwm;
+   struct pwm_channel *channels;
+};
+
+/* Divides a nanosecond value, rounding up for very low values */
+uint div_ns(uint ns, uint div)
+{
+   uint result = (ns / div);
+
+   /* If the number is less than 1000, round it to the nearest digit */
+   if (result < 1000)
+   result = (ns + (div - 1)) / div;
+
+   if (result < 1)
+   result = 1;
+
+   return result;
+}
+
+/* Checks if an error is relatively too large */
+int error_too_large(uint actual, uint target)
+{
+   /* For a target of zero we want zero */
+   if (target == 0)
+   return (actual == 0);
+
+   /* Don't overflow large numbers when we multiply by 100 */
+   while (actual > 1000) {
+   actual /= 100;
+   target /= 100;
+   }
+
+   

[PATCH RFC 2/4] pinctrl: sunxi: Add PWM7 pinctrl for the D1

2024-05-17 Thread John Watts
This is currently only used for PD22 on the Mango Pi MQ-Dual.

Signed-off-by: John Watts 
---
 drivers/pinctrl/sunxi/pinctrl-sunxi.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c 
b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
index 37ea93715d..b3f2568ffe 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -615,6 +615,7 @@ static const struct sunxi_pinctrl_function 
sun20i_d1_pinctrl_functions[] = {
{ "uart1",  2 },/* PG6-PG7 */
{ "uart2",  7 },/* PB0-PB1 */
{ "uart3",  7 },/* PB6-PB7 */
+   { "pwm7",   5 },/* PD22 */
 };
 
 static const struct sunxi_pinctrl_desc __maybe_unused sun20i_d1_pinctrl_desc = 
{

-- 
2.45.1



[PATCH RFC 1/4] clk: sunxi: Add PWM bus clock and reset

2024-05-17 Thread John Watts
This is needed for the D1 PWM driver.

Signed-off-by: John Watts 
---
 drivers/clk/sunxi/clk_d1.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/clk/sunxi/clk_d1.c b/drivers/clk/sunxi/clk_d1.c
index 9dae761de8..6577d86e0b 100644
--- a/drivers/clk/sunxi/clk_d1.c
+++ b/drivers/clk/sunxi/clk_d1.c
@@ -15,6 +15,8 @@
 static struct ccu_clk_gate d1_gates[] = {
[CLK_APB0]  = GATE_DUMMY,
 
+   [CLK_BUS_PWM]   = GATE(0x7ac, BIT(0)),
+
[CLK_BUS_MMC0]  = GATE(0x84c, BIT(0)),
[CLK_BUS_MMC1]  = GATE(0x84c, BIT(1)),
[CLK_BUS_MMC2]  = GATE(0x84c, BIT(2)),
@@ -48,6 +50,8 @@ static struct ccu_clk_gate d1_gates[] = {
 };
 
 static struct ccu_reset d1_resets[] = {
+   [RST_BUS_PWM]   = RESET(0x7ac, BIT(16)),
+
[RST_BUS_MMC0]  = RESET(0x84c, BIT(16)),
[RST_BUS_MMC1]  = RESET(0x84c, BIT(17)),
[RST_BUS_MMC2]  = RESET(0x84c, BIT(18)),

-- 
2.45.1



[PATCH RFC 0/4] pwm: sunxi: Add support Allwinner D1 PWM

2024-05-17 Thread John Watts
This patch series adds support for the Allwinner D1, T113 and R329 PWM.

This code isn't based on any kernel code but instead written from scratch with
the goal of handling the PWM pairs deterministically.

I've tested this on T113 hardware and it works very well.

Signed-off-by: John Watts 
---
John Watts (4):
  clk: sunxi: Add PWM bus clock and reset
  pinctrl: sunxi: Add PWM7 pinctrl for the D1
  pwm: sunxi: Add support Allwinner D1 PWM
  [FOR TESTING ONLY] sunxi-d1s-t113: Add D1 and T113 PWM node

 arch/riscv/dts/sunxi-d1s-t113.dtsi |  12 +
 drivers/clk/sunxi/clk_d1.c |   4 +
 drivers/pinctrl/sunxi/pinctrl-sunxi.c  |   1 +
 drivers/pwm/Kconfig|   6 +
 drivers/pwm/Makefile   |   1 +
 drivers/pwm/sunxi_pwm_d1.c | 542 +
 .../src/riscv/allwinner/sunxi-d1s-t113.dtsi|  12 +
 7 files changed, 578 insertions(+)
---
base-commit: 312efdf9c9297382b4e1d04c50376573764b5c00
change-id: 20240518-pwm_d1-b43d263ea143

Best regards,
-- 
John Watts 



[PATCH 4/4] spi: rockchip_sfc: Select BOUNCE_BUFFER

2024-04-26 Thread John Watts
This is required for compiling.

Signed-off-by: John Watts 
---
 drivers/spi/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 025c81adc9..f1c6a838ae 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -417,6 +417,7 @@ config RENESAS_RPC_SPI
 
 config ROCKCHIP_SFC
bool "Rockchip SFC Driver"
+   select BOUNCE_BUFFER
help
  Enable the Rockchip SFC Driver for SPI NOR flash. This device is
  a limited purpose SPI controller for driving NOR flash on certain

-- 
2.44.0



[PATCH 3/4] spi: ca_sflash: Add missing dm include

2024-04-26 Thread John Watts
This code uses dev_err which is defined in dm/device_compat.h

Signed-off-by: John Watts 
---
 drivers/spi/ca_sflash.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/spi/ca_sflash.c b/drivers/spi/ca_sflash.c
index 38bddd3861..78e442cac9 100644
--- a/drivers/spi/ca_sflash.c
+++ b/drivers/spi/ca_sflash.c
@@ -11,6 +11,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 

-- 
2.44.0



[PATCH 2/4] spi: mtk_spim: Remove completion.h include

2024-04-26 Thread John Watts
This created a conflict when linking.

Signed-off-by: John Watts 
---
 drivers/spi/mtk_spim.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/spi/mtk_spim.c b/drivers/spi/mtk_spim.c
index 90f4c3cecb..2979487fff 100644
--- a/drivers/spi/mtk_spim.c
+++ b/drivers/spi/mtk_spim.c
@@ -18,7 +18,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 

-- 
2.44.0



[PATCH 1/4] spi: Kconfig: Add some required arch depends for drivers

2024-04-26 Thread John Watts
These dependencies are required for building the drivers and create
compile errors if not enabled.

Signed-off-by: John Watts 
---
 drivers/spi/Kconfig | 16 
 1 file changed, 16 insertions(+)

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 612434633b..025c81adc9 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -93,6 +93,7 @@ config ATMEL_QSPI
 
 config ATMEL_SPI
bool "Atmel SPI driver"
+   depends on ARCH_AT91
default y if ARCH_AT91
help
  This enables driver for the Atmel SPI Controller, present on
@@ -126,6 +127,7 @@ config BCM63XX_SPI
 
 config BCMSTB_SPI
bool "BCMSTB SPI driver"
+   depends on ARCH_BCMSTB
help
  Enable the Broadcom set-top box SPI driver. This driver can
  be used to access the SPI flash on platforms embedding this
@@ -164,6 +166,7 @@ config CADENCE_OSPI_VERSAL
 
 config CF_SPI
 bool "ColdFire SPI driver"
+depends on M68K
 help
   Enable the ColdFire SPI driver. This driver can be used on
   some m68k SoCs.
@@ -183,6 +186,7 @@ config DESIGNWARE_SPI
 
 config EXYNOS_SPI
bool "Samsung Exynos SPI driver"
+   depends on ARCH_EXYNOS
help
  Enable the Samsung Exynos SPI driver. This driver can be used to
  access the SPI NOR flash on platforms embedding this Samsung
@@ -190,6 +194,7 @@ config EXYNOS_SPI
 
 config FSL_DSPI
bool "Freescale DSPI driver"
+   depends on FSL_LAYERSCAPE || ARCH_VF610 || ARCH_LS1021A || ARCH_LS1028A
help
  Enable the Freescale DSPI driver. This driver can be used to
  access the SPI NOR flash and SPI Data flash on platforms embedding
@@ -220,6 +225,7 @@ config GXP_SPI
 
 config ICH_SPI
bool "Intel ICH SPI driver"
+   depends on X86
help
  Enable the Intel ICH SPI driver. This driver can be used to
  access the SPI NOR flash on platforms embedding this Intel
@@ -233,6 +239,7 @@ config IPROC_QSPI
 
 config KIRKWOOD_SPI
bool "Marvell Kirkwood SPI Driver"
+   depends on ARCH_KIRKWOOD
help
  Enable support for SPI on various Marvell SoCs, such as
  Kirkwood and Armada 375.
@@ -268,6 +275,7 @@ config MPC8XX_SPI
 
 config MPC8XXX_SPI
bool "MPC8XXX SPI Driver"
+   depends on MPC83xx || MPC85xx
help
  Enable support for SPI on the MPC8XXX PowerPC SoCs.
 
@@ -327,6 +335,7 @@ config MVEBU_A3700_SPI
 
 config MXS_SPI
bool "MXS SPI Driver"
+   depends on MACH_IMX
help
  Enable the MXS SPI controller driver. This driver can be used
  on the i.MX23 and i.MX28 SoCs.
@@ -512,6 +521,7 @@ config STM32_SPI
 
 config TEGRA114_SPI
bool "nVidia Tegra114 SPI driver"
+   depends on ARCH_TEGRA
help
  Enable the nVidia Tegra114 SPI driver. This driver can be used to
  access the SPI NOR flash on platforms embedding this nVidia Tegra114
@@ -522,6 +532,7 @@ config TEGRA114_SPI
 
 config TEGRA20_SFLASH
bool "nVidia Tegra20 Serial Flash controller driver"
+   depends on ARCH_TEGRA
help
  Enable the nVidia Tegra20 Serial Flash controller driver. This driver
  can be used to access the SPI NOR flash on platforms embedding this
@@ -529,6 +540,7 @@ config TEGRA20_SFLASH
 
 config TEGRA20_SLINK
bool "nVidia Tegra20/Tegra30 SLINK driver"
+   depends on ARCH_TEGRA
help
  Enable the nVidia Tegra20/Tegra30 SLINK driver. This driver can
  be used to access the SPI NOR flash on platforms embedding this
@@ -536,6 +548,7 @@ config TEGRA20_SLINK
 
 config TEGRA210_QSPI
bool "nVidia Tegra210 QSPI driver"
+   depends on ARCH_TEGRA
help
  Enable the Tegra Quad-SPI (QSPI) driver for T210. This driver
  be used to access SPI chips on platforms embedding this
@@ -544,6 +557,7 @@ config TEGRA210_QSPI
 config TI_QSPI
bool "TI QSPI driver"
imply TI_EDMA3
+   depends on ARCH_OMAP2PLUS
help
  Enable the TI Quad-SPI (QSPI) driver for DRA7xx and AM43xx evms.
  This driver support spi flash single, quad and memory reads.
@@ -599,12 +613,14 @@ config FSL_ESPI
 
 config SH_QSPI
bool "Renesas Quad SPI driver"
+   depends on ARCH_RENESAS
help
  Enable the Renesas Quad SPI controller driver. This driver can be
  used on Renesas SoCs.
 
 config MXC_SPI
bool "MXC SPI Driver"
+   depends on MACH_IMX
help
  Enable the MXC SPI controller driver. This driver can be used
  on various i.MX SoCs such as i.MX31/35/51/6/7.

-- 
2.44.0



[PATCH 0/4] spi: Various Kconfig fixes

2024-04-26 Thread John Watts
I'm doing some SPI work so I tried to compile all the drivers on my
sunxi board to try and avoid some regressions. This failed, so here are
some fixes for this.

Signed-off-by: John Watts 
---
John Watts (4):
  spi: Kconfig: Add some required arch depends for drivers
  spi: mtk_spim: Remove completion.h include
  spi: ca_sflash: Add missing dm include
  spi: rockchip_sfc: Select BOUNCE_BUFFER

 drivers/spi/Kconfig | 17 +
 drivers/spi/ca_sflash.c |  1 +
 drivers/spi/mtk_spim.c  |  1 -
 3 files changed, 18 insertions(+), 1 deletion(-)
---
base-commit: 174ac987655c888017c82df1883c0c2ea0dc2495
change-id: 20240427-spikconfig-2417bbcf4c14

Best regards,
-- 
John Watts 



[PATCH RFC 9/9] sunxi: video: support HDMI on H6/H616

2024-04-19 Thread John Watts
The H6 and H616 support outputting HDMI through the Display Engine.
Set up the clocks and resets appropriately for the HDMI controller.

This turns out to be a little tricky as the HDMI clock requires a
different parent on the H6 compared to the H616. So we have to end
up choosing VIDEO1 on the H616 and VIDEO0 elsewhere.

Signed-off-by: John Watts 
---
 arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h |  9 +++
 arch/arm/mach-sunxi/clock_sun50i_h6.c | 10 
 drivers/video/sunxi/sunxi_dw_hdmi.c   | 70 ---
 3 files changed, 80 insertions(+), 9 deletions(-)

diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h 
b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
index dfe8d9315f..35bd3dd2d8 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
@@ -376,13 +376,21 @@ struct sunxi_ccm_reg {
 /* TCON0 clock bit field */
 #define CCM_TCON0_CTRL_ENABLE  (0x1 << 31)
 #define CCM_TCON0_CTRL_VIDEO0_4X   (0x1 << 24)
+#define CCM_TCON0_CTRL_VIDEO1_4X   (0x3 << 24)
 #define CCM_TCON0_CTRL_M(m)m) - 1) & 0xf) << 0)
 
 /* TCON1 clock bit field */
 #define CCM_TCON1_CTRL_ENABLE  (0x1 << 31)
 #define CCM_TCON1_CTRL_VIDEO0_4X   (0x1 << 24)
+#define CCM_TCON1_CTRL_VIDEO1_4X   (0x3 << 24)
 #define CCM_TCON1_CTRL_M(m)m) - 1) & 0xf) << 0)
 
+/* HDMI clock bit field */
+#define CCM_HDMI_CTRL_ENABLE   (0x1 << 31)
+#define CCM_HDMI_CTRL_VIDEO1_4X_H6 (0x2 << 24)
+#define CCM_HDMI_CTRL_VIDEO0_4X_H616   (0x1 << 24)
+#define CCM_HDMI_CTRL_M(m) m) - 1) & 0xf) << 0)
+
 /* CCM bits common to all Display Engine 2.0 clock ctrl regs */
 #define CCM_DE2_CTRL_M(n)  n) - 1) & 0xf) << 0)
 #define CCM_DE2_CTRL_PLL_MASK  (3 << 24)
@@ -399,6 +407,7 @@ void clock_set_pll3(unsigned int hz);
 void clock_set_video1(unsigned int hz);
 void clock_set_pll10(unsigned int hz);
 unsigned int clock_get_pll3(void);
+unsigned int clock_get_video1(void);
 #endif
 #endif
 
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c 
b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index 11e303f801..23b7c13e28 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -230,4 +230,14 @@ unsigned int clock_get_pll3(void)
return 12000 * n * 1000;
 }
 
+unsigned int clock_get_video1(void)
+{
+   struct sunxi_ccm_reg *const ccm =
+   (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+   u32 rval = readl(>pll_video1_cfg);
+   int n = ((rval & CCM_VIDEO1_CTRL_N_MASK) >> CCM_VIDEO1_CTRL_N_SHIFT) + 
1;
+
+   return 12000 * n * 1000;
+}
+
 #endif
diff --git a/drivers/video/sunxi/sunxi_dw_hdmi.c 
b/drivers/video/sunxi/sunxi_dw_hdmi.c
index 34a6b8bab7..475d61a888 100644
--- a/drivers/video/sunxi/sunxi_dw_hdmi.c
+++ b/drivers/video/sunxi/sunxi_dw_hdmi.c
@@ -198,6 +198,12 @@ static void sunxi_dw_hdmi_pll_set(uint clk_khz, int 
*phy_div)
 {
int value, n, m, div, diff;
int best_n = 0, best_m = 0, best_div = 0, best_diff = 0x0FFF;
+   int step = 24000, max_m = 16, pll_value = 0;
+
+   if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) || 
IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) {
+   step = 12000;
+   max_m = 1;
+   }
 
/*
 * Find the lowest divider resulting in a matching clock. If there
@@ -212,11 +218,11 @@ static void sunxi_dw_hdmi_pll_set(uint clk_khz, int 
*phy_div)
if (target > 912000)
continue;
 
-   for (m = 1; m <= 16; m++) {
-   n = (m * target) / 24000;
+   for (m = 1; m <= max_m; m++) {
+   n = (m * target) / step;
 
if (n >= 1 && n <= 128) {
-   value = (24000 * n) / m / div;
+   value = (step * n) / m / div;
diff = clk_khz - value;
if (diff < best_diff) {
best_diff = diff;
@@ -231,13 +237,20 @@ static void sunxi_dw_hdmi_pll_set(uint clk_khz, int 
*phy_div)
*phy_div = best_div;
 
 #if IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)
-   panic("setting HDMI pll not implemented");
+   if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) {
+   clock_set_video1(step * best_n);
+   pll_value = clock_get_video1();
+   } else {
+   clock_set_pll3(step * best_n);
+   pll_value = clock_get_pll3();
+   }
 #else
clock_set_pll3_factors(best_m, best_n);
+   pll_value = clock_get_pll3();
 #endif
 
debug("dotclock: %dkHz = %dkHz: (24MHz * %d) / %d / %d\n",
- clk_khz, (clock_get_pll3() / 1000) / best_div

[PATCH RFC 8/9] sunxi: Enable display engine on H6/D1

2024-04-19 Thread John Watts
Now that all the code is present and can compile, enable the DE2 so
people can use it.

Signed-off-by: John Watts 
---
 arch/arm/mach-sunxi/Kconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index ddf9414b08..9d76e429ef 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -202,6 +202,7 @@ config SUN50I_GEN_H6
select SPL_LOAD_FIT if SPL
select MMC_SUNXI_HAS_NEW_MODE
select SUPPORT_SPL
+   select SUNXI_DE2
---help---
Select this for sunxi SoCs which have H6 like peripherals, clocks
and memory map.
@@ -210,6 +211,7 @@ config SUNXI_GEN_NCAT2
bool
select MMC_SUNXI_HAS_NEW_MODE
select SUPPORT_SPL
+   select SUNXI_DE2
---help---
Select this for sunxi SoCs which have D1 like peripherals, clocks
and memory map.

-- 
2.44.0



[PATCH RFC 7/9] sunxi: video: dummy out HDMI on H6/D1

2024-04-19 Thread John Watts
I don't have any hardware to implement HDMI support on, but it's still
worth making the code compile for future work.

Adding HDMI support shouldn't be too difficult, though beware: The H6
and H616 both have a different set of HDMI clock parents. Future code
will most likely need to pick VIDEO0 or VIDEO1 to run the TCON and
HDMI encoder based on the chip.

Signed-off-by: John Watts 
---
 drivers/video/sunxi/sunxi_dw_hdmi.c | 13 +
 1 file changed, 13 insertions(+)

diff --git a/drivers/video/sunxi/sunxi_dw_hdmi.c 
b/drivers/video/sunxi/sunxi_dw_hdmi.c
index 0324a050d0..34a6b8bab7 100644
--- a/drivers/video/sunxi/sunxi_dw_hdmi.c
+++ b/drivers/video/sunxi/sunxi_dw_hdmi.c
@@ -230,7 +230,12 @@ static void sunxi_dw_hdmi_pll_set(uint clk_khz, int 
*phy_div)
 
*phy_div = best_div;
 
+#if IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)
+   panic("setting HDMI pll not implemented");
+#else
clock_set_pll3_factors(best_m, best_n);
+#endif
+
debug("dotclock: %dkHz = %dkHz: (24MHz * %d) / %d / %d\n",
  clk_khz, (clock_get_pll3() / 1000) / best_div,
  best_n, best_m, best_div);
@@ -244,6 +249,9 @@ static void sunxi_dw_hdmi_lcdc_init(int mux, const struct 
display_timing *edid,
int div = DIV_ROUND_UP(clock_get_pll3(), edid->pixelclock.typ);
struct sunxi_lcdc_reg *lcdc;
 
+#if IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)
+   panic("initializing HDMI lcdc not implemented");
+#else
if (mux == 0) {
lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
 
@@ -265,6 +273,7 @@ static void sunxi_dw_hdmi_lcdc_init(int mux, const struct 
display_timing *edid,
writel(CCM_LCD1_CTRL_GATE | CCM_LCD1_CTRL_M(div),
   >lcd1_clk_cfg);
}
+#endif
 
lcdc_init(lcdc);
lcdc_tcon1_mode_set(lcdc, edid, false, false);
@@ -338,6 +347,9 @@ static int sunxi_dw_hdmi_probe(struct udevice *dev)
if (priv->hvcc)
regulator_set_enable(priv->hvcc, true);
 
+#if IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)
+   panic("initializing HDMI not implemented");
+#else
/* Set pll3 to 297 MHz */
clock_set_pll3(29700);
 
@@ -347,6 +359,7 @@ static int sunxi_dw_hdmi_probe(struct udevice *dev)
 
/* This reset is referenced from the PHY devicetree node. */
setbits_le32(>ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI2);
+#endif
 
ret = reset_deassert_bulk(>resets);
if (ret)

-- 
2.44.0



[PATCH RFC 6/9] sunxi: video: silence unused use_mipi_pll warning

2024-04-19 Thread John Watts
This variable is only used sometimes, so gate it behind an #ifdef.

Signed-off-by: John Watts 
---
 drivers/video/sunxi/lcdc.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/video/sunxi/lcdc.c b/drivers/video/sunxi/lcdc.c
index ea21d602be..1474f6cb2e 100644
--- a/drivers/video/sunxi/lcdc.c
+++ b/drivers/video/sunxi/lcdc.c
@@ -216,7 +216,10 @@ void lcdc_pll_set(struct sunxi_ccm_reg *ccm, int tcon, int 
dotclock,
int value, n, m, min_m, max_m, diff, step;
int best_n = 0, best_m = 0, best_diff = 0x0FFF;
int best_double = 0;
+
+#ifdef CONFIG_MACH_SUN6I
bool use_mipi_pll = false;
+#endif
 
 #ifdef CONFIG_SUNXI_DE2
step = 6000;

-- 
2.44.0



[PATCH RFC 5/9] sunxi: video: enable DE2 on H6/D1

2024-04-19 Thread John Watts
This requires just a little change to clocking and reset logic.

Signed-off-by: John Watts 
---
 drivers/video/sunxi/sunxi_de2.c | 18 ++
 1 file changed, 18 insertions(+)

diff --git a/drivers/video/sunxi/sunxi_de2.c b/drivers/video/sunxi/sunxi_de2.c
index e02d359cd2..9b6c41b212 100644
--- a/drivers/video/sunxi/sunxi_de2.c
+++ b/drivers/video/sunxi/sunxi_de2.c
@@ -45,6 +45,23 @@ static void sunxi_de2_composer_init(void)
writel(reg_value, SUNXI_SRAMC_BASE + 0x04);
 #endif
 
+#if IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)
+   if (IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) {
+   /* Set DE parent to video1 */
+   clock_set_video1(43200);
+   clrsetbits_le32(>de_clk_cfg, CCM_DE2_CTRL_PLL_MASK,
+   CCM_DE2_CTRL_VIDEO1_4X_NCAT);
+   } else {
+   /* Set DE parent to pll10 */
+   clock_set_pll10(43200);
+   clrsetbits_le32(>de_clk_cfg, CCM_DE2_CTRL_PLL_MASK,
+   CCM_DE2_CTRL_PLL10_H6);
+   }
+
+   /* Ungate the DE */
+   setbits_le32(>de_gate_reset, BIT(RESET_SHIFT));
+   setbits_le32(>de_gate_reset, BIT(GATE_SHIFT));
+#else
clock_set_pll10(43200);
 
/* Set DE parent to pll10 */
@@ -54,6 +71,7 @@ static void sunxi_de2_composer_init(void)
/* Set ahb gating to pass */
setbits_le32(>ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE);
setbits_le32(>ahb_gate1, 1 << AHB_GATE_OFFSET_DE);
+#endif
 
/* Clock on */
setbits_le32(>de_clk_cfg, CCM_DE2_CTRL_GATE);

-- 
2.44.0



[PATCH RFC 4/9] sunxi: video: support LCD on H6/D1

2024-04-19 Thread John Watts
The H6/D1/R528 don't require any big changes, just some small
tweaks to support new clock and reset logic.

Signed-off-by: John Watts 
---
 drivers/video/sunxi/lcdc.c  | 20 
 drivers/video/sunxi/sunxi_lcd.c |  6 --
 2 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/drivers/video/sunxi/lcdc.c b/drivers/video/sunxi/lcdc.c
index 73033c3b85..ea21d602be 100644
--- a/drivers/video/sunxi/lcdc.c
+++ b/drivers/video/sunxi/lcdc.c
@@ -277,6 +277,12 @@ void lcdc_pll_set(struct sunxi_ccm_reg *ccm, int tcon, int 
dotclock,
}
}
 
+#if IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)
+   /* No need to clock doubling, just ask for a higher PLL clock */
+   best_double = 0;
+   step *= 2;
+#endif
+
 #ifdef CONFIG_MACH_SUN6I
/*
 * Use the MIPI pll if we've been unable to find any matching setting
@@ -302,6 +308,19 @@ void lcdc_pll_set(struct sunxi_ccm_reg *ccm, int tcon, int 
dotclock,
  best_double + 1, step, best_n, best_m);
}
 
+#if IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)
+   if (tcon == 0) {
+   writel(CCM_TCON0_CTRL_VIDEO0_4X | CCM_TCON0_CTRL_ENABLE,
+  >tcon_lcd0_clk_cfg);
+   setbits_le32(>tcon_lcd_gate_reset, BIT(RESET_SHIFT));
+   setbits_le32(>tcon_lcd_gate_reset, BIT(GATE_SHIFT));
+   } else {
+   writel(CCM_TCON1_CTRL_VIDEO0_4X | CCM_TCON1_CTRL_ENABLE,
+  >tcon_tv0_clk_cfg);
+   setbits_le32(>tcon_tv_gate_reset, BIT(RESET_SHIFT));
+   setbits_le32(>tcon_tv_gate_reset, BIT(GATE_SHIFT));
+   }
+#else
if (tcon == 0) {
u32 pll;
 
@@ -329,6 +348,7 @@ void lcdc_pll_set(struct sunxi_ccm_reg *ccm, int tcon, int 
dotclock,
setbits_le32(>lcd0_ch1_clk_cfg,
 CCM_LCD_CH1_CTRL_HALF_SCLK1);
}
+#endif
 #endif
 
*clk_div = best_m;
diff --git a/drivers/video/sunxi/sunxi_lcd.c b/drivers/video/sunxi/sunxi_lcd.c
index 7a01cc343c..3b0e63233f 100644
--- a/drivers/video/sunxi/sunxi_lcd.c
+++ b/drivers/video/sunxi/sunxi_lcd.c
@@ -26,7 +26,7 @@ struct sunxi_lcd_priv {
 
 static void sunxi_lcdc_config_pinmux(void)
 {
-#ifdef CONFIG_MACH_SUN50I
+#if IS_ENABLED(CONFIG_MACH_SUN50I) || IS_ENABLED(CONFIG_SUN50I_GEN_H6) || 
IS_ENABLED(CONFIG_MACH_SUN8I_R528)
int pin;
 
for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(21); pin++) {
@@ -47,10 +47,12 @@ static int sunxi_lcd_enable(struct udevice *dev, int bpp,
struct udevice *backlight;
int clk_div, clk_double, ret;
 
+#if !IS_ENABLED(CONFIG_SUN50I_GEN_H6) && !IS_ENABLED(CONFIG_MACH_SUN8I_R528)
/* Reset off */
setbits_le32(>ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD0);
/* Clock on */
setbits_le32(>ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
+#endif
 
lcdc_init(lcdc);
sunxi_lcdc_config_pinmux();
@@ -146,7 +148,7 @@ U_BOOT_DRIVER(sunxi_lcd) = {
.priv_auto  = sizeof(struct sunxi_lcd_priv),
 };
 
-#ifdef CONFIG_MACH_SUN50I
+#if IS_ENABLED(CONFIG_MACH_SUN50I) || IS_ENABLED(CONFIG_SUN50I_GEN_H6) || 
IS_ENABLED(CONFIG_MACH_SUN8I_R528)
 U_BOOT_DRVINFO(sunxi_lcd) = {
.name = "sunxi_lcd"
 };

-- 
2.44.0



[PATCH RFC 3/9] sunxi: sun50i-h6: Specify DE2 and LCD0 base addresses

2024-04-19 Thread John Watts
These are used for operating the LCD on the H6/H616.

Signed-off-by: John Watts 
---
 arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h 
b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
index 8a3f465545..2c9fe18289 100644
--- a/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h
@@ -31,6 +31,9 @@
 #define SUNXI_DRAM_PHY0_BASE   0x0480
 #endif
 
+#define SUNXI_DE2_BASE 0x0100
+#define SUNXI_LCD0_BASE0x06515000
+
 #define SUNXI_TWI0_BASE0x05002000
 #define SUNXI_TWI1_BASE0x05002400
 #define SUNXI_TWI2_BASE0x05002800

-- 
2.44.0



[PATCH RFC 2/9] sunxi: ncat2: Specify DE2 and LCD0 base addresses

2024-04-19 Thread John Watts
These are used for operating the LCD on the D1/T113.

Signed-off-by: John Watts 
---
 arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h 
b/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
index 908a582ae0..4ae93b5095 100644
--- a/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
+++ b/arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h
@@ -25,6 +25,9 @@
 #define SUNXI_MMC1_BASE0x04021000
 #define SUNXI_MMC2_BASE0x04022000
 
+#define SUNXI_DE2_BASE 0x0500
+#define SUNXI_LCD0_BASE0x05461000
+
 #define SUNXI_R_CPUCFG_BASE0x07000400
 #define SUNXI_PRCM_BASE0x0701
 

-- 
2.44.0



[PATCH RFC 1/9] sunxi: clock: support H6/D1 video clocks

2024-04-19 Thread John Watts
This code adds support for clocking VIDEO0 and VIDEO1, as well as
registers used for the DE2.

This code deliberately uses a 12MHz step in clocking to align with
the DE2 code's expectation of double 6MHz steps.

Signed-off-by: John Watts 
---
 arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h | 56 ++
 arch/arm/mach-sunxi/clock_sun50i_h6.c | 71 +++
 2 files changed, 127 insertions(+)

diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h 
b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
index a84a57e5b4..dfe8d9315f 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h
@@ -236,6 +236,28 @@ struct sunxi_ccm_reg {
 #define CCM_PLL1_CTRL_P(p) ((p) << 16)
 #define CCM_PLL1_CTRL_N(n) (((n) - 1) << 8)
 
+/* pll3 (video0) bit field */
+#define CCM_PLL3_CTRL_EN   BIT(31)
+#define CCM_PLL3_LDO_ENBIT(30)
+#define CCM_PLL3_LOCK_EN   BIT(29)
+#define CCM_PLL3_LOCK  BIT(28)
+#define CCM_PLL3_OUT_ENBIT(27)
+#define CCM_PLL3_INPUT_DIV2BIT(1)
+#define CCM_PLL3_CTRL_N(n) (((n) - 1) << 8)
+#define CCM_PLL3_CTRL_N_SHIFT  8
+#define CCM_PLL3_CTRL_N_MASK   (0xff << CCM_PLL3_CTRL_N_SHIFT)
+
+/* video1 bit field */
+#define CCM_VIDEO1_CTRL_EN BIT(31)
+#define CCM_VIDEO1_LDO_EN  BIT(30)
+#define CCM_VIDEO1_LOCK_EN BIT(29)
+#define CCM_VIDEO1_LOCKBIT(28)
+#define CCM_VIDEO1_OUT_EN  BIT(27)
+#define CCM_VIDEO1_INPUT_DIV2  BIT(1)
+#define CCM_VIDEO1_CTRL_N(n)   (((n) - 1) << 8)
+#define CCM_VIDEO1_CTRL_N_SHIFT8
+#define CCM_VIDEO1_CTRL_N_MASK (0xff << CCM_VIDEO1_CTRL_N_SHIFT)
+
 /* pll5 bit field */
 #define CCM_PLL5_CTRL_EN   BIT(31)
 #define CCM_PLL5_LOCK_EN   BIT(29)
@@ -258,6 +280,16 @@ struct sunxi_ccm_reg {
 #define CCM_PLL6_CTRL_DIV2_SHIFT   1
 #define CCM_PLL6_CTRL_DIV2_MASK(0x1 << 
CCM_PLL6_CTRL_DIV2_SHIFT)
 
+/* pll10 bit field */
+#define CCM_PLL10_CTRL_EN  BIT(31)
+#define CCM_PLL10_LOCK_EN  BIT(29)
+#define CCM_PLL10_LOCK BIT(28)
+#define CCM_PLL10_OUT_EN   BIT(27)
+#define CCM_PLL10_INPUT_DIV2   BIT(1)
+#define CCM_PLL10_CTRL_N(n)(((n) - 1) << 8)
+#define CCM_PLL10_CTRL_N_SHIFT 8
+#define CCM_PLL10_CTRL_N_MASK  (0xff << CCM_PLL10_CTRL_N_SHIFT)
+
 /* cpu_axi bit field*/
 #define CCM_CPU_AXI_MUX_MASK   (0x3 << 24)
 #define CCM_CPU_AXI_MUX_OSC24M (0x0 << 24)
@@ -341,9 +373,33 @@ struct sunxi_ccm_reg {
 #define CCM_MMC_CTRL_OCLK_DLY(a)   ((void) (a), 0)
 #define CCM_MMC_CTRL_SCLK_DLY(a)   ((void) (a), 0)
 
+/* TCON0 clock bit field */
+#define CCM_TCON0_CTRL_ENABLE  (0x1 << 31)
+#define CCM_TCON0_CTRL_VIDEO0_4X   (0x1 << 24)
+#define CCM_TCON0_CTRL_M(m)m) - 1) & 0xf) << 0)
+
+/* TCON1 clock bit field */
+#define CCM_TCON1_CTRL_ENABLE  (0x1 << 31)
+#define CCM_TCON1_CTRL_VIDEO0_4X   (0x1 << 24)
+#define CCM_TCON1_CTRL_M(m)m) - 1) & 0xf) << 0)
+
+/* CCM bits common to all Display Engine 2.0 clock ctrl regs */
+#define CCM_DE2_CTRL_M(n)  n) - 1) & 0xf) << 0)
+#define CCM_DE2_CTRL_PLL_MASK  (3 << 24)
+#define CCM_DE2_CTRL_PLL10_H6  (0 << 24)
+#define CCM_DE2_CTRL_VIDEO1_4X_NCAT(2 << 24)
+#define CCM_DE2_CTRL_GATE  (0x1 << 31)
+
 #ifndef __ASSEMBLY__
 void clock_set_pll1(unsigned int hz);
 unsigned int clock_get_pll6(void);
+
+#ifdef CONFIG_SUNXI_DE2
+void clock_set_pll3(unsigned int hz);
+void clock_set_video1(unsigned int hz);
+void clock_set_pll10(unsigned int hz);
+unsigned int clock_get_pll3(void);
+#endif
 #endif
 
 #endif /* _SUNXI_CLOCK_SUN50I_H6_H */
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c 
b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index dac3663e1b..11e303f801 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -160,3 +160,74 @@ int clock_twi_onoff(int port, int state)
 
return 0;
 }
+
+#ifdef CONFIG_SUNXI_DE2
+
+void clock_set_pll3(unsigned int clk)
+{
+   struct sunxi_ccm_reg * const ccm =
+   (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+
+   if (clk == 0) {
+   clrbits_le32(>pll3_cfg, CCM_PLL3_CTRL_EN);
+   return;
+   }
+
+   /* PLL3 rate = 2400 * n / 2 */
+   writel(CCM_PLL3_CTRL_EN | CCM_PLL3_LOCK_EN | CCM_PLL3_OUT_EN | 
CCM_PLL3_LDO_EN |
+  CCM_PLL3_INPUT_DIV2 | CCM_PLL3_CTRL_N(clk / 1200),
+  >pll3_cfg);
+
+   while (!(readl(>pll3_cfg) & CCM_PLL3_LOCK))
+  

[PATCH RFC 0/9] sunxi: video: Support LCD and HDMI output on H6/D1

2024-04-19 Thread John Watts
This patch series is my first attempt at supporting an LCD panel on the
T113. I can confirm this specific use case works, but I've added code
for the H6/H616 too in the process as a lot of the logic is the same.

I've also included an untested patch for HDMI support at the end in case
anyone wants to work on it. It compiles and looks like it should work,
but I haven't ungated/reset the slow HDMI clock, or HDMI sub.

Signed-off-by: John Watts 
---
John Watts (9):
  sunxi: clock: support H6/D1 video clocks
  sunxi: ncat2: Specify DE2 and LCD0 base addresses
  sunxi: sun50i-h6: Specify DE2 and LCD0 base addresses
  sunxi: video: support LCD on H6/D1
  sunxi: video: enable DE2 on H6/D1
  sunxi: video: silence unused use_mipi_pll warning
  sunxi: video: dummy out HDMI on H6/D1
  sunxi: Enable display engine on H6/D1
  sunxi: video: support HDMI on H6/H616

 arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h | 65 ++
 arch/arm/include/asm/arch-sunxi/cpu_sun50i_h6.h   |  3 +
 arch/arm/include/asm/arch-sunxi/cpu_sunxi_ncat2.h |  3 +
 arch/arm/mach-sunxi/Kconfig   |  2 +
 arch/arm/mach-sunxi/clock_sun50i_h6.c | 81 +++
 drivers/video/sunxi/lcdc.c| 23 +++
 drivers/video/sunxi/sunxi_de2.c   | 18 +
 drivers/video/sunxi/sunxi_dw_hdmi.c   | 77 +++--
 drivers/video/sunxi/sunxi_lcd.c   |  6 +-
 9 files changed, 270 insertions(+), 8 deletions(-)
---
base-commit: 1f1bbbdf85b67316770a6964a4a02b73a720a167
change-id: 20240420-d1_de2-9be81d56bd85

Best regards,
-- 
John Watts 



Re: [PATCH v2 0/2] sunxi, usb: UDC/DM gadget model support

2024-04-16 Thread John Watts
On Thu, Jun 08, 2023 at 01:56:29PM -0600, Sam Edwards wrote:
> Happy Thursday, U-Boot list!
> 
> Here is attempt 2 at making this USB controller driver compatible with
> DM's gadget model, following what most of the other musb variants do.
> 
> v2 removes the unwanted printfs in the probe func, per feedback from Marek.
> I received no other feedback against v1 of this patch.

Hi Sam,

I did some more testing and I believe my USB issues mentioned in the
other subthread are unrelated to this patch.

As such, here's my reviewed-by and tested-by. :)

John.

Reviewed-by: John Watts 
Tested-by: John Watts 

> 
> Cheers,
> Sam
> 
> Sam Edwards (2):
>   usb: musb-new: sunxi: remove unwanted printfs
>   usb: musb-new: sunxi: make compatible with UDC/DM gadget model
> 
>  drivers/usb/musb-new/sunxi.c | 52 +---
>  1 file changed, 31 insertions(+), 21 deletions(-)
> 
> -- 
> 2.39.2
> 


Re: [PATCH RFC 00/15] Support SPI NAND booting on the T113

2024-04-11 Thread John Watts
On Thu, Apr 11, 2024 at 05:27:08PM -0600, Sam Edwards wrote:
> Hi John,
> 
> It doesn't look like I was sent the whole series (only 00 and 01), but
> I was able to find it on Patchwork and sift through it. A few general
> comments follow:
> 
> The introduction of `SUNXI_BOOTED_FROM_SPINAND` is the right call,
> since the newer sunxis use this for SPI-NAND, and
> `SUNXI_BOOTED_FROM_SPI` for SPI-NOR. The older sunxis, however, will
> use `SUNXI_BOOTED_FROM_SPI` for "it's either SPI-NAND or SPI-NOR, have
> fun figuring out which." While the rationale in 09/15 ("Instead of
> trying to boot from SPI NAND then SPI NOR in series, select one based
> on the current boot device.") is solid, we still need some code
> (perhaps in/called from `sunxi_get_boot_device`?) to handle the
> `SUNXI_BOOTED_FROM_SPI` case by performing flash type detection --
> disabled for those sunxis known to use `SUNXI_BOOTED_FROM_SPINAND`, of
> course.

Is there already code that can do this probing somewhere in U-Boot?
I'd rather not try and support older boards with an ambiguous boot
method, they already don't work.

> 06/15 ("sunxi: enable support for SPI NAND booting on SUNIV") should
> be dropped from the series. You are updating `suniv_get_boot_source`
> when introducing `SUNXI_BOOTED_FROM_SPINAND` anyway, so you can just
> hook up `SUNIV_BOOTED_FROM_NAND` at that time (and a heads-up: I think
> you got it wired backwards in this version of the series).

Some of the redunancy in patches are from including Icenowy's patchset.

> On a more fundamental note: I am hesitant about the overall approach
> of having NAND reading code in `arch/arm/mach-sunxi/spl_spi_sunxi.c`.
> NANDs are more complex than NORs (requiring e.g. bad block handling)
> and U-Boot generally keeps the SPL load methods in
> `common/spl/spl_*.c`. It's good that the UBI-on-SPI-NAND method is
> hooked up there, but the "U-Boot image follows the (good) blocks of
> the SPL in NAND" method should probably live in the same directory.
> 
> Here's what I'd like to explore: can we introduce a
> `common/spl/spl_spinand.c` and update the `drivers/mtd/nand/spi/*.c`
> code to support running in SPL? This way
> `arch/arm/mach-sunxi/spl_spi_sunxi.c` must only provide the SPL-mode
> implementation of SPI -- that is, the actual sunxi-specific bit. Also,
> updating the `drivers/mtd/nand/spi/*.c` drivers for SPL-friendliness
> will mean dropping those `SPL_SPINAND_{PAGE,BLOCK}_SIZE` configuration
> options: the tables in the drivers will be the ones providing those
> after NAND autodetection.

This is a little bit of a mess:

common/spl/spl_spi.c implements general SPI NOR reading
drivers/mtd/spi/sf-uclass.c wraps SPI flash access using DM
drivers/spi/spi-sunxi.c implements the SPI controller
arch/arm/mach-sunxi/spl_spi_sunxi.c overrides spl_spi

It would be good to somehow have spl_spi provide functions spl_spi
uses instead of having spl_spi_sunxi implement it directly.

common/spl/spl_nand.c implements general NAND reading
common/spl/spl_ubi.c implements UBI reading
drivers/mtd/nand/raw/sunxi_nand.c implements sunxi NAND reading
drivers/mtd/nand/raw/nand_spl_simple.c implmements a generic solution
drivers/mtd/nand/raw/sunxi_nand_spl.c implements sunxi SPL NAND reading

spl_ubi requires nand_spl_simple callbacks, sunxi_nand_spl doesn't
implement these.

It's possible spl_spi, spl_nand and spl_ubi all work if the DM code is
used for sunxi. Maybe that's a good goal here? Getting DM in SPL working
on these boards then hooking it in to spl_ubi seems like it could solve
some pain. I'm not too concerned about old SoCs at this point.

For non-DM Adding spl_spinand would work for the case of reading the
next good block. I'm not actually sure if the SPI NAND code supports the
bad block table yet. But we would still need an API for spl_ubi to
access.

> 
> Thoughts?
> 
> Cheers,
> Sam

John.


Re: [PATCH v2 0/2] sunxi, usb: UDC/DM gadget model support

2024-04-11 Thread John Watts
On Thu, Apr 11, 2024 at 03:53:51PM -0600, Sam Edwards wrote:
> Hi John,

Hi Sam,

> Ahh I see the problem. In U-Boot, `ubi` isn't actually a block device:
> it's implemented as a stub in the block layer, and the filesystem
> layer redirects `ubi` accesses to the currently-mounted ubifs instead.
> But the `ums` command works on the block layer, so it's not being
> intercepted, and instead hitting that stub and likely crashing. In the
> spirit of the Linux ubiblock driver, I have another patchset[1] I've
> been working on[2] to expose static ubivols as true read-only block
> devices. Note that this only works for static volumes: the access
> semantics of dynamic volumes are too flash-like to support block
> device access, so accessing them with `ums` likely will never work
> like users expect.

I'll give a patchset a test when I can.

> Still, there could be some interaction issue between `ums`<->USB that
> I haven't identified. Could you try with mmc (if available) or a
> ramdisk (otherwise) just to confirm that `ums` is fine?

I'll try testing with those if possible.

I did find out that fastboot actually works, and is much, much faster than DFU
for some reason.

John.

> 
> Regards,
> Sam
> 
> [1] 
> https://lore.kernel.org/u-boot/20230812000606.72319-1-cfswo...@gmail.com/T/
> [2] Not very diligently; if you're interested in helping test it, I'd
> love to get back to it.


Re: [PATCH] boot: Pass baud rate to stdout-path

2024-04-11 Thread John Watts
On Thu, Apr 11, 2024 at 05:11:46PM +0200, Mark Kettenis wrote:
> You probably should fix this by making sure the device tree you're
> using has the appropriate stdout-path node.  Because I think the
> functionality you're trying to use here is deprecated:

Hi Mark,

Interesting, I'll go with that approach instead.

> ...
> 
> A particular case that I'm dealing with is the default speed of
> 150 that the various Rockchip SoCs use.  This doesn't work with
> many of the USB-to-serial interfaces on the market and is also rather
> susceptible to line noise.  So for OpenBSD packages I've decide to use
> 115200 instead.  But that means I have to patch all the device trees
> in addition to changing the CONFIG_BAUDRATE setting.  If U-Boot would
> tweak the stdout-path property based on CONFIG_BAUDRATE that would
> make things easier.

That's an interesting use case! Would you mind testing this patch if possible?

> Cheers,
> 
> Mark

John.


Re: [PATCH] usb: musb-new: sunxi: support usage with DM_USB_GADGET

2024-04-11 Thread John Watts
On Sun, Dec 31, 2023 at 03:38:37PM -0500, Aren Moynihan wrote:
> Add support for building the sunxi-musb driver with DM_USB_GADGET
> including adding a separate IRQ handling function and registering the
> driver with the musb system differently.

Hi there,

Were you aware of this similar patch?

https://lore.kernel.org/u-boot/20230608195631.55364-1-cfswo...@gmail.com/

If not you might want to test it and compare it.

John.

> The implementation of usb_gadget_register_driver from
> musb-new/musb_uboot.c only works when the gadget driver for the device
> has already been probed and has called musb_register. On the pinephone
> (using a allwinner a64 processor) this causes issues when trying to use
> usb gadget mode (such as from the ums command) and CONFIG_USB_ETHER is
> disabled.
> 
> The implementation of usb_gadget_register_driver provided when
> DM_USB_GADGET is enabled will probe the necessary drivers when it's
> called.
> 
> Without the patch, this is what the error condition looks like:
> => ums 0 mmc 1
> UMS: LUN 0, dev mmc 1, hwpart 0, sector 0x0, count 0x3a3e000
> Controller uninitialized
> g_dnl_register: failed!, error: -6
> g_dnl_register failed
> 
> based on:
> commit 2e4865bc6486 ("musb-new: omap2430: fix compiling in DM_USB_GADGET 
> config")
> 
> Signed-off-by: Aren Moynihan 


Re: [PATCH v2 0/2] sunxi, usb: UDC/DM gadget model support

2024-04-11 Thread John Watts
Hi there,

I've tested this patch and it seems to support the gadget model, but I'm having
a lot of USB errors. What device did you test this on?

John.

On Thu, Jun 08, 2023 at 01:56:29PM -0600, Sam Edwards wrote:
> Happy Thursday, U-Boot list!
> 
> Here is attempt 2 at making this USB controller driver compatible with
> DM's gadget model, following what most of the other musb variants do.
> 
> v2 removes the unwanted printfs in the probe func, per feedback from Marek.
> I received no other feedback against v1 of this patch.
> 
> Cheers,
> Sam
> 
> Sam Edwards (2):
>   usb: musb-new: sunxi: remove unwanted printfs
>   usb: musb-new: sunxi: make compatible with UDC/DM gadget model
> 
>  drivers/usb/musb-new/sunxi.c | 52 +---
>  1 file changed, 31 insertions(+), 21 deletions(-)
> 
> -- 
> 2.39.2
> 


[PATCH 2/2] sunxi: Support UART2 on the T113

2024-04-10 Thread John Watts
The T113 supports UART2 on PD1 and PD2. Add it as an option.

Signed-off-by: John Watts 
---
 arch/arm/mach-sunxi/board.c | 4 
 include/sunxi_gpio.h| 1 +
 2 files changed, 5 insertions(+)

diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index f5da50b43a..4176543cd9 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -166,6 +166,10 @@ static int gpio_init(void)
sunxi_gpio_set_cfgpin(SUNXI_GPB(0), SUN8I_GPB_UART2);
sunxi_gpio_set_cfgpin(SUNXI_GPB(1), SUN8I_GPB_UART2);
sunxi_gpio_set_pull(SUNXI_GPB(1), SUNXI_GPIO_PULL_UP);
+#elif CONFIG_CONS_INDEX == 3 && defined(CONFIG_MACH_SUN8I_R528)
+   sunxi_gpio_set_cfgpin(SUNXI_GPD(1), SUN8I_T113_GPD_UART2);
+   sunxi_gpio_set_cfgpin(SUNXI_GPD(2), SUN8I_T113_GPD_UART2);
+   sunxi_gpio_set_pull(SUNXI_GPD(2), SUNXI_GPIO_PULL_UP);
 #elif CONFIG_CONS_INDEX == 4 && defined(CONFIG_MACH_SUN8I_R528)
sunxi_gpio_set_cfgpin(SUNXI_GPB(6), 7);
sunxi_gpio_set_cfgpin(SUNXI_GPB(7), 7);
diff --git a/include/sunxi_gpio.h b/include/sunxi_gpio.h
index db3742c039..88ab1d192f 100644
--- a/include/sunxi_gpio.h
+++ b/include/sunxi_gpio.h
@@ -120,6 +120,7 @@ enum sunxi_gpio_number {
 #define SUN8I_A83T_GPB_UART0   2
 #define SUN8I_V3S_GPB_UART03
 #define SUN50I_GPB_UART0   4
+#define SUN8I_T113_GPD_UART2   5
 
 #define SUNXI_GPC_NAND 2
 #define SUNXI_GPC_SPI0 3

-- 
2.44.0



[PATCH 1/2] sunxi: Support UART1 on the T113

2024-04-10 Thread John Watts
The T113 supports UART1 on pins PG6 and PG7, add support for it here.

Signed-off-by: John Watts 
---
 arch/arm/mach-sunxi/board.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index f4dbb2a740..f5da50b43a 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -174,8 +174,9 @@ static int gpio_init(void)
sunxi_gpio_set_cfgpin(SUNXI_GPL(2), SUN8I_GPL_R_UART);
sunxi_gpio_set_cfgpin(SUNXI_GPL(3), SUN8I_GPL_R_UART);
sunxi_gpio_set_pull(SUNXI_GPL(3), SUNXI_GPIO_PULL_UP);
-#elif CONFIG_CONS_INDEX == 2 && defined(CONFIG_MACH_SUN8I) && \
-   !defined(CONFIG_MACH_SUN8I_R40)
+#elif CONFIG_CONS_INDEX == 2 && ((defined(CONFIG_MACH_SUN8I) && \
+   !defined(CONFIG_MACH_SUN8I_R40)) || \
+   defined(CONFIG_MACH_SUN8I_R528))
sunxi_gpio_set_cfgpin(SUNXI_GPG(6), SUN8I_GPG_UART1);
sunxi_gpio_set_cfgpin(SUNXI_GPG(7), SUN8I_GPG_UART1);
sunxi_gpio_set_pull(SUNXI_GPG(7), SUNXI_GPIO_PULL_UP);

-- 
2.44.0



[PATCH 0/2] sunxi: Support UART1 and UART2 on the T113

2024-04-10 Thread John Watts
The T113 supports UART1 and UART2 on PG and PD pins respectively.
Add support for these in U-Boot so we can use them.

Note: I'm not entirely sure if the PD pins should be default, they
overlap with the LCD pins. I am however using this on a real board.

Signed-off-by: John Watts 
---
John Watts (2):
  sunxi: Support UART1 on the T113
  sunxi: Support UART2 on the T113

 arch/arm/mach-sunxi/board.c | 9 +++--
 include/sunxi_gpio.h| 1 +
 2 files changed, 8 insertions(+), 2 deletions(-)
---
base-commit: 777c28460947371ada40868dc994dfe8537d7115
change-id: 20240411-t113serial-a6e9ca8d8848

Best regards,
-- 
John Watts 



[PATCH] ubi: Depend on MTD

2024-04-10 Thread John Watts
UBI required MTD to build correctly, add it as a Kconfig dependency.

Signed-off-by: John Watts 
---
While working with UBI on my SPI NAND patch series I found it was
possible to enable it without enabling the MTD subsystem.
Add a Kconfig option to solve this.
---
 drivers/mtd/ubi/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 5783d36c04..fd446d6efb 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -9,6 +9,7 @@ config UBI_SILENCE_MSG
 
 config MTD_UBI
bool "Enable UBI - Unsorted block images"
+   depends on MTD
select RBTREE
select MTD_PARTITIONS
help

---
base-commit: 777c28460947371ada40868dc994dfe8537d7115
change-id: 20240411-mtd-d2e811e17cc8

Best regards,
-- 
John Watts 



[PATCH] boot: Pass baud rate to stdout-path

2024-04-10 Thread John Watts
Linux might use the wrong baud rate such as 9600 by default, make sure
to specify it when passing the serial port over.

Signed-off-by: John Watts 
---
On my board at least (a sunxi T113) the serial console will initialize
as 9600 baud instead of the set baud. Pass the baud with the serial
device to Linux to solve this issue.
---
 boot/fdt_support.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/boot/fdt_support.c b/boot/fdt_support.c
index 090d82ee80..83e62f47b5 100644
--- a/boot/fdt_support.c
+++ b/boot/fdt_support.c
@@ -153,9 +153,9 @@ static int fdt_fixup_stdout(void *fdt, int chosenoff)
}
 
/* fdt_setprop may break "path" so we copy it to tmp buffer */
-   memcpy(tmp, path, len);
+   len = sprintf(tmp, "%.*s:%d", len, (char *)path, CONFIG_BAUDRATE);
 
-   err = fdt_setprop(fdt, chosenoff, "linux,stdout-path", tmp, len);
+   err = fdt_setprop(fdt, chosenoff, "linux,stdout-path", tmp, len + 1);
if (err < 0)
printf("WARNING: could not set linux,stdout-path %s.\n",
   fdt_strerror(err));

---
base-commit: 777c28460947371ada40868dc994dfe8537d7115
change-id: 20240411-stdout-4f91b566a0f2

Best regards,
-- 
John Watts 



Re: [PATCH 0/8] SUNIV SPI NAND support in SPL

2024-04-10 Thread John Watts
Hello,

I've used this code extensively, incorporated it in to an RFC branch of
mine during development and reviewed it in the process.

John.

Reviewed-by: John Watts 
Tested-by: John Watts 

On Fri, Oct 14, 2022 at 11:05:12AM +0800, Icenowy Zheng wrote:
> This patchset tries to extend SPI-based boot code in sunxi SPL to
> support SPI NAND, following the same principle with current SPI NOR code
> (mimicking the behavior of sunxi BROM). In addition, as part of test to
> this patchset, some patches for Source Parts Inc. PopStick is attached,
> although marked DO NOT MERGE because the DT should come from Linux after
> it's ready.
> 
> To keep thr code that accesses SPI NAND as simple as possible, it
> assumes fixed page size, which is also what sunxi BROM does. The SUNIV
> SPL assumes 0x400 page size, but here to utilize the space better, in
> the attached example of PopStick, U-Boot main part is assumed to be
> with 0x800 page size (which is the real situation of the W25N01 flash
> used by PopStick).
> 
> Icenowy Zheng (8):
>   sunxi: SPL SPI: extract code for doing SPI transfer
>   sunxi: SPL SPI: add support for read command with 2 byte address
>   sunxi: SPL SPI: allow multiple boot attempt
>   sunxi: SPL SPI: add initial support for booting from SPI NAND
>   sunxi: enable support for SPI NAND booting on SUNIV
>   [DO NOT MERGE] sunxi: sync DT from my tree for PopStick
>   [DO NOT MERGE, DIRTY HACK] sunxi: use UBI for environement storage
>   [DO NOT MERGE] sunxi: add a defconfig for PopStick
> 
>  arch/arm/dts/Makefile|   3 +-
>  arch/arm/dts/suniv-f1c100s-licheepi-nano.dts |  16 ++
>  arch/arm/dts/suniv-f1c100s.dtsi  |  26 ++
>  arch/arm/dts/suniv-f1c200s-popstick-v1.1.dts | 101 
>  arch/arm/mach-sunxi/Kconfig  |  16 ++
>  arch/arm/mach-sunxi/board.c  |   4 +-
>  arch/arm/mach-sunxi/spl_spi_sunxi.c  | 247 ++-
>  board/sunxi/board.c  |   1 +
>  configs/popstick_defconfig   |  35 +++
>  9 files changed, 377 insertions(+), 72 deletions(-)
>  create mode 100644 arch/arm/dts/suniv-f1c200s-popstick-v1.1.dts
>  create mode 100644 configs/popstick_defconfig
> 
> -- 
> 2.37.1
> 


Re: [PATCH v1 1/2] sunxi: SPL SPI: Add SPI boot support for the Allwinner R528/T113 SoCs

2024-04-10 Thread John Watts
Hi there,

I've been using my own independent implementation of this patch but
today I gave this one a test in my tree and found out it works.

The code looks fine in comparison, so here's a Tested-by and a
Reviewed-by.

John.

Tested-by: John Watts 
Reviewed-by: John Watts 

On Sat, Nov 11, 2023 at 04:33:07PM +0300, Maksim Kiselev wrote:
> R528/T113 SoCs uses the same SPI IP as the H6, also have the same clocks
> and reset bits layout, but the CCU base is different. Another difference
> is that the new SoCs do not have a clock divider inside. Instead of this
> we should configure sample mode depending on input clock rate.
> 
> The pin assignment is also different: the H6 uses PC0, the R528/T113 PC4
> instead. This makes for a change in spi0_pinmux_setup() routine.
> 
> This patch extends the H6/H616 #ifdef guards to also cover the R528/T113,
> using the shared CONFIG_SUNXI_GEN_NCAT2 and CONFIG_MACH_SUN8I_R528
> symbols. Also use CONFIG_SUNXI_GEN_NCAT2 symbol for the Kconfig
> dependency.
> 
> Signed-off-by: Maksim Kiselev 
> Tested-by: Sam Edwards 
> ---
>  arch/arm/mach-sunxi/Kconfig |  2 +-
>  arch/arm/mach-sunxi/spl_spi_sunxi.c | 78 +
>  2 files changed, 58 insertions(+), 22 deletions(-)
> 
> diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> index a10e4c06b6..732f60821a 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -1044,7 +1044,7 @@ config SPL_STACK_R_ADDR
>  
>  config SPL_SPI_SUNXI
>   bool "Support for SPI Flash on Allwinner SoCs in SPL"
> - depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUNXI_H3_H5 
> || MACH_SUN50I || MACH_SUN8I_R40 || SUN50I_GEN_H6 || MACH_SUNIV
> + depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUNXI_H3_H5 
> || MACH_SUN50I || MACH_SUN8I_R40 || SUN50I_GEN_H6 || MACH_SUNIV || 
> SUNXI_GEN_NCAT2
>   help
> Enable support for SPI Flash. This option allows SPL to read from
> sunxi SPI Flash. It uses the same method as the boot ROM, so does
> diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c 
> b/arch/arm/mach-sunxi/spl_spi_sunxi.c
> index c2410dd7bb..ba3b1579f0 100644
> --- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
> +++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
> @@ -73,18 +73,27 @@
>  #define SUN6I_CTL_ENABLEBIT(0)
>  #define SUN6I_CTL_MASTERBIT(1)
>  #define SUN6I_CTL_SRST  BIT(31)
> +#define SUN6I_TCR_SDM   BIT(13)
>  #define SUN6I_TCR_XCH   BIT(31)
>  
>  
> /*/
>  
> -#define CCM_AHB_GATING0 (0x01C2 + 0x60)
> -#define CCM_H6_SPI_BGR_REG  (0x03001000 + 0x96c)
> -#ifdef CONFIG_SUN50I_GEN_H6
> -#define CCM_SPI0_CLK(0x03001000 + 0x940)
> +#if IS_ENABLED(CONFIG_SUN50I_GEN_H6)
> +#define CCM_BASE0x03001000
> +#elif IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)
> +#define CCM_BASE0x02001000
>  #else
> -#define CCM_SPI0_CLK(0x01C2 + 0xA0)
> +#define CCM_BASE0x01C2
>  #endif
> -#define SUN6I_BUS_SOFT_RST_REG0 (0x01C2 + 0x2C0)
> +
> +#define CCM_AHB_GATING0 (CCM_BASE + 0x60)
> +#define CCM_H6_SPI_BGR_REG  (CCM_BASE + 0x96c)
> +#if IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)
> +#define CCM_SPI0_CLK(CCM_BASE + 0x940)
> +#else
> +#define CCM_SPI0_CLK(CCM_BASE + 0xA0)
> +#endif
> +#define SUN6I_BUS_SOFT_RST_REG0 (CCM_BASE + 0x2C0)
>  
>  #define AHB_RESET_SPI0_SHIFT20
>  #define AHB_GATE_OFFSET_SPI020
> @@ -102,17 +111,22 @@
>   */
>  static void spi0_pinmux_setup(unsigned int pin_function)
>  {
> - /* All chips use PC0 and PC2. */
> - sunxi_gpio_set_cfgpin(SUNXI_GPC(0), pin_function);
> + /* All chips use PC2. And all chips use PC0, except R528/T113 */
> + if (!IS_ENABLED(CONFIG_MACH_SUN8I_R528))
> + sunxi_gpio_set_cfgpin(SUNXI_GPC(0), pin_function);
> +
>   sunxi_gpio_set_cfgpin(SUNXI_GPC(2), pin_function);
>  
> - /* All chips except H6 and H616 use PC1. */
> - if (!IS_ENABLED(CONFIG_SUN50I_GEN_H6))
> + /* All chips except H6/H616/R528/T113 use PC1. */
> + if (!IS_ENABLED(CONFIG_SUN50I_GEN_H6) &&
> + !IS_ENABLED(CONFIG_MACH_SUN8I_R528))
>   sunxi_gpio_set_cfgpin(SUNXI_GPC(1), pin_function);
>  
> - if (IS_ENABLED(CONFIG_MACH_SUN50I_H6))
> + if (IS_ENABLED(CONFIG_MACH_SUN50I_H6) ||
> + IS_ENABLED(CONFIG_MACH_SUN8I_R528))
>   sunxi_gpio_set_cfgpin(SUNXI_GPC(5), pin_function);
> -

[PATCH RFC 15/15] spl: Support loading FIT images in UBI

2024-04-10 Thread John Watts
The FIT loader doesn't support access through UBI, so load the FIT
image ourself in to memory then boot it normally.

Signed-off-by: John Watts 
---
 common/spl/spl_ubi.c | 34 ++
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/common/spl/spl_ubi.c b/common/spl/spl_ubi.c
index 72b4b195ed..b64f595160 100644
--- a/common/spl/spl_ubi.c
+++ b/common/spl/spl_ubi.c
@@ -12,6 +12,16 @@
 #include 
 #include 
 
+static ulong ram_spl_load_read(struct spl_load_info *load, ulong sector,
+  ulong count, void *buf)
+{
+   char *ubi_contents = load->priv;
+
+   memcpy(buf, ubi_contents + sector, count);
+
+   return count;
+}
+
 int spl_ubi_load_image(struct spl_image_info *spl_image,
   struct spl_boot_device *bootdev)
 {
@@ -69,10 +79,11 @@ int spl_ubi_load_image(struct spl_image_info *spl_image,
puts("Loading Linux failed, falling back to U-Boot.\n");
}
 #endif
-   header = spl_get_load_buffer(-sizeof(*header), sizeof(header));
+   /* Ensure there's enough room for the full UBI volume! */
+   header = (void *)CONFIG_SYS_LOAD_ADDR;
 #ifdef CONFIG_SPL_UBI_LOAD_BY_VOLNAME
volumes[0].vol_id = -1;
-   strncpy(volumes[0].name,
+   strlcpy(volumes[0].name,
CONFIG_SPL_UBI_LOAD_MONITOR_VOLNAME,
UBI_VOL_NAME_MAX + 1);
 #else
@@ -81,8 +92,23 @@ int spl_ubi_load_image(struct spl_image_info *spl_image,
volumes[0].load_addr = (void *)header;
 
ret = ubispl_load_volumes(, volumes, 1);
-   if (!ret)
-   spl_parse_image_header(spl_image, bootdev, header);
+   if (ret)
+   goto out;
+
+   spl_parse_image_header(spl_image, bootdev, header);
+
+   if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&
+   image_get_magic(header) == FDT_MAGIC) {
+   struct spl_load_info load;
+
+   printf("Found FIT\n");
+   load.priv = (char *)header;
+   load.read = ram_spl_load_read;
+   spl_set_bl_len(, 1);
+
+   ret = spl_load_simple_fit(spl_image, , 0, header);
+   }
+
 out:
 #ifdef CONFIG_SPL_SPINAND_SUPPORT
if (bootdev->boot_device == BOOT_DEVICE_SPINAND)

-- 
2.44.0



[PATCH RFC 14/15] spl: Support SPI NAND boot in UBI

2024-04-10 Thread John Watts
UBI supports traditional NAND and oneNAND devices already, so add
support for booting from SPI NAND devices.

Signed-off-by: John Watts 
---
 common/spl/spl_ubi.c | 15 ++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/common/spl/spl_ubi.c b/common/spl/spl_ubi.c
index d7ab9efd11..72b4b195ed 100644
--- a/common/spl/spl_ubi.c
+++ b/common/spl/spl_ubi.c
@@ -21,6 +21,13 @@ int spl_ubi_load_image(struct spl_image_info *spl_image,
int ret = 1;
 
switch (bootdev->boot_device) {
+#ifdef CONFIG_SPL_SPINAND_SUPPORT
+   case BOOT_DEVICE_SPINAND:
+   spinand_init();
+   info.read = spinand_spl_read_block;
+   info.peb_size = CONFIG_SPL_SPINAND_BLOCK_SIZE;
+   break;
+#endif
 #ifdef CONFIG_SPL_NAND_SUPPORT
case BOOT_DEVICE_NAND:
nand_init();
@@ -77,12 +84,18 @@ int spl_ubi_load_image(struct spl_image_info *spl_image,
if (!ret)
spl_parse_image_header(spl_image, bootdev, header);
 out:
+#ifdef CONFIG_SPL_SPINAND_SUPPORT
+   if (bootdev->boot_device == BOOT_DEVICE_SPINAND)
+   spinand_deselect();
+#endif
 #ifdef CONFIG_SPL_NAND_SUPPORT
if (bootdev->boot_device == BOOT_DEVICE_NAND)
nand_deselect();
 #endif
return ret;
 }
-/* Use priorty 0 so that Ubi will override NAND and ONENAND methods */
+
+/* Use priority 0 so that UBI will override all NAND methods */
 SPL_LOAD_IMAGE_METHOD("NAND", 0, BOOT_DEVICE_NAND, spl_ubi_load_image);
 SPL_LOAD_IMAGE_METHOD("OneNAND", 0, BOOT_DEVICE_ONENAND, spl_ubi_load_image);
+SPL_LOAD_IMAGE_METHOD("SPINAND", 0, BOOT_DEVICE_SPINAND, spl_ubi_load_image);

-- 
2.44.0



[PATCH RFC 13/15] sunxi: Implement spinand_ helpers

2024-04-10 Thread John Watts
These are used by NAND-aware loaders such as UBI.

Signed-off-by: John Watts 
---
 arch/arm/mach-sunxi/spl_spi_sunxi.c | 21 +
 1 file changed, 21 insertions(+)

diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c 
b/arch/arm/mach-sunxi/spl_spi_sunxi.c
index 602ebfe8c5..d6b03678d0 100644
--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
+++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
@@ -450,6 +450,27 @@ static ulong spi_load_read_nand(struct spl_load_info 
*load, ulong sector,
 
return count;
 }
+
+void spinand_init(void)
+{
+   spi0_init();
+   spi0_nand_reset();
+}
+
+void spinand_deselect(void)
+{
+   spi0_deinit();
+}
+
+int spinand_spl_read_block(int block, int offset, int len, void *dst)
+{
+   ulong byte_offset = (block * CONFIG_SPL_SPINAND_BLOCK_SIZE) + offset;
+
+   spi_load_read_nand(NULL, byte_offset, len, dst);
+
+   return 0;
+}
+
 #endif
 
 /*/

-- 
2.44.0



[PATCH RFC 12/15] nand: Add spinand_ helper functions

2024-04-10 Thread John Watts
These are implemented by the board-specific SPL code for use with
NAND-aware loaders like UBI.

Signed-off-by: John Watts 
---
 include/nand.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/nand.h b/include/nand.h
index 220ffa202e..9fb8941dce 100644
--- a/include/nand.h
+++ b/include/nand.h
@@ -10,6 +10,7 @@
 
 #include 
 
+extern void spinand_init(void);
 extern void nand_init(void);
 void nand_reinit(void);
 unsigned long nand_size(void);
@@ -110,7 +111,9 @@ int nand_get_lock_status(struct mtd_info *mtd, loff_t 
offset);
 u32 nand_spl_adjust_offset(u32 sector, u32 offs);
 int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst);
 int nand_spl_read_block(int block, int offset, int len, void *dst);
+int spinand_spl_read_block(int block, int offset, int len, void *dst);
 void nand_deselect(void);
+void spinand_deselect(void);
 
 #ifdef CONFIG_SYS_NAND_SELECT_DEVICE
 void board_nand_select_device(struct nand_chip *nand, int chip);

-- 
2.44.0



[PATCH RFC 11/15] sunxi: Use SPL_SPINAND for configuration

2024-04-10 Thread John Watts
Use the newly created SPL_SPINAND configuration options instead of
sunxi-only options.

No backwards compatibility is needed as the SPI NAND patches are not
mainline yet.

Signed-off-by: John Watts 
---
 arch/arm/mach-sunxi/Kconfig | 16 
 arch/arm/mach-sunxi/spl_spi_sunxi.c | 10 +-
 2 files changed, 5 insertions(+), 21 deletions(-)

diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index cc576fc84d..ddf9414b08 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -1084,22 +1084,6 @@ config SPL_SPI_SUNXI
  sunxi SPI Flash. It uses the same method as the boot ROM, so does
  not need any extra configuration.
 
-config SPL_SPI_SUNXI_NAND
-   bool "Support for SPI NAND Flash on Allwinner SoCs in SPL"
-   depends on SPL_SPI_SUNXI
-   help
- Enable support for SPI NAND Flash. This option allows SPL to mimic
- Allwinner boot ROM's behavior to gain support for SPI NAND Flash;
- a fixed page size needs to be assumed when building the SPL image.
-
-config SPL_SPI_SUNXI_NAND_ASSUMED_PAGESIZE
-   hex "Assumed pagesize for SPI NAND Flash in SPL"
-   depends on SPL_SPI_SUNXI_NAND
-   default 0x400 if MACH_SUNIV
-   help
- Set the page size assumed by the SPL SPI NAND code, the default
- value is the same with the boot ROM.
-
 config PINE64_DT_SELECTION
bool "Enable Pine64 device tree selection code"
depends on MACH_SUN50I
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c 
b/arch/arm/mach-sunxi/spl_spi_sunxi.c
index 46db2900ca..602ebfe8c5 100644
--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
+++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
@@ -344,7 +344,7 @@ static void spi0_xfer(const u8 *txbuf, u32 txlen, u8 
*rxbuf, u32 rxlen)
}
 }
 
-#if defined(CONFIG_SPL_SPI_SUNXI_NAND)
+#if defined(CONFIG_SPL_SPINAND_SUPPORT)
 static int spi0_nand_switch_page(u32 page)
 {
unsigned count;
@@ -430,11 +430,11 @@ static ulong spi_load_read_nor(struct spl_load_info 
*load, ulong sector,
return count;
 }
 
-#if defined(CONFIG_SPL_SPI_SUNXI_NAND)
+#if defined(CONFIG_SPL_SPINAND_SUPPORT)
 static ulong spi_load_read_nand(struct spl_load_info *load, ulong sector,
   ulong count, void *buf)
 {
-   const ulong pagesize = CONFIG_SPL_SPI_SUNXI_NAND_ASSUMED_PAGESIZE;
+   const ulong pagesize = CONFIG_SPL_SPINAND_PAGE_SIZE;
ulong remain = count;
 
while (remain) {
@@ -501,7 +501,7 @@ static int spl_spi_load_image(struct spl_image_info 
*spl_image,
spi0_init();
 
switch (bootdev->boot_device) {
-#if defined(CONFIG_SPL_SPI_SUNXI_NAND)
+#if defined(CONFIG_SPL_SPINAND_SUPPORT)
case BOOT_DEVICE_SPINAND:
spi0_nand_reset();
load.read = spi_load_read_nand;
@@ -524,6 +524,6 @@ static int spl_spi_load_image(struct spl_image_info 
*spl_image,
 /* Use priorty 0 to override the default if it happens to be linked in */
 SPL_LOAD_IMAGE_METHOD("sunxi SPI", 0, BOOT_DEVICE_SPI, spl_spi_load_image);
 
-#if IS_ENABLED(CONFIG_SPL_SPI_SUNXI_NAND)
+#if IS_ENABLED(CONFIG_SPL_SPINAND_SUPPORT)
 SPL_LOAD_IMAGE_METHOD("sunxi SPI NAND", 0, BOOT_DEVICE_SPINAND, 
spl_spi_load_image);
 #endif

-- 
2.44.0



[PATCH RFC 10/15] spl: Add SPL_SPINAND configuration options

2024-04-10 Thread John Watts
Boards that support SPI NAND need to specify the page and eraseblock
size. Add those as Kconfig options.

Signed-off-by: John Watts 
---
 common/spl/Kconfig | 21 +
 1 file changed, 21 insertions(+)

diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index 6405374bcc..51d1f9f59d 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -906,6 +906,27 @@ config SPL_MUSB_NEW
  the drivers in drivers/usb/musb-new as part of an SPL build. The
  old drivers are in drivers/usb/musb.
 
+config SPL_SPINAND_SUPPORT
+   bool "Support SPINAND flash"
+   help
+ Enable support for SPINAND (Negative AND) flash in SPL. SPINAND flash
+ can be used to allow SPL to load U-Boot from supported devices.
+
+config SPL_SPINAND_PAGE_SIZE
+hex "SPINAND chip page size"
+depends on SPL_SPINAND_SUPPORT
+help
+  Number of data bytes in one page for the SPINAND chip on the
+  board, not including the OOB area.
+
+config SPL_SPINAND_BLOCK_SIZE
+hex "SPINAND chip eraseblock size"
+depends on SPL_SPINAND_SUPPORT
+help
+  Number of data bytes in one eraseblock for the SPINAND chip on the
+  board. This is the multiple of SPINAND_PAGE_SIZE and the number of
+  pages.
+
 config SPL_NAND_SUPPORT
bool "Support NAND flash"
select SPL_LOAD_BLOCK

-- 
2.44.0



[PATCH RFC 08/15] spl: Add BOOT_DEVICE_SPINAND option

2024-04-10 Thread John Watts
Currently there are two different boot device options: SPI and NAND.
One is intended for SPI NOR operation, the other is intended for dedicated
NAND operation.

Add a new option for SPI NAND operation.

Signed-off-by: John Watts 
---
 arch/arm/include/asm/spl.h | 1 +
 arch/mips/include/asm/spl.h| 1 +
 arch/riscv/include/asm/spl.h   | 1 +
 arch/sandbox/include/asm/spl.h | 1 +
 4 files changed, 4 insertions(+)

diff --git a/arch/arm/include/asm/spl.h b/arch/arm/include/asm/spl.h
index ee79a19c05..92deba8019 100644
--- a/arch/arm/include/asm/spl.h
+++ b/arch/arm/include/asm/spl.h
@@ -22,6 +22,7 @@ enum {
BOOT_DEVICE_NOR,
BOOT_DEVICE_UART,
BOOT_DEVICE_SPI,
+   BOOT_DEVICE_SPINAND,
BOOT_DEVICE_USB,
BOOT_DEVICE_SATA,
BOOT_DEVICE_I2C,
diff --git a/arch/mips/include/asm/spl.h b/arch/mips/include/asm/spl.h
index 0a847edec8..02b580079a 100644
--- a/arch/mips/include/asm/spl.h
+++ b/arch/mips/include/asm/spl.h
@@ -16,6 +16,7 @@ enum {
BOOT_DEVICE_NOR,
BOOT_DEVICE_UART,
BOOT_DEVICE_SPI,
+   BOOT_DEVICE_SPINAND,
BOOT_DEVICE_USB,
BOOT_DEVICE_SATA,
BOOT_DEVICE_I2C,
diff --git a/arch/riscv/include/asm/spl.h b/arch/riscv/include/asm/spl.h
index 9c0bf9755c..56b5bf9d7e 100644
--- a/arch/riscv/include/asm/spl.h
+++ b/arch/riscv/include/asm/spl.h
@@ -18,6 +18,7 @@ enum {
BOOT_DEVICE_NOR,
BOOT_DEVICE_UART,
BOOT_DEVICE_SPI,
+   BOOT_DEVICE_SPINAND,
BOOT_DEVICE_USB,
BOOT_DEVICE_SATA,
BOOT_DEVICE_NVME,
diff --git a/arch/sandbox/include/asm/spl.h b/arch/sandbox/include/asm/spl.h
index 4fab24cd15..70f8ad4b58 100644
--- a/arch/sandbox/include/asm/spl.h
+++ b/arch/sandbox/include/asm/spl.h
@@ -16,6 +16,7 @@ enum {
BOOT_DEVICE_NOR,
BOOT_DEVICE_SPI,
BOOT_DEVICE_NAND,
+   BOOT_DEVICE_SPINAND,
 };
 
 /**

-- 
2.44.0



[PATCH RFC 09/15] sunxi: Implement BOOT_DEVICE_SPINAND in SPL

2024-04-10 Thread John Watts
Instead of trying to boot from SPI NAND then SPI NOR in series, select
one based on the current boot device.

Signed-off-by: John Watts 
---
 arch/arm/include/asm/arch-sunxi/spl.h |  1 +
 arch/arm/mach-sunxi/board.c   |  5 -
 arch/arm/mach-sunxi/spl_spi_sunxi.c   | 28 ++--
 3 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/arch/arm/include/asm/arch-sunxi/spl.h 
b/arch/arm/include/asm/arch-sunxi/spl.h
index 92be936d56..a9b7c0daca 100644
--- a/arch/arm/include/asm/arch-sunxi/spl.h
+++ b/arch/arm/include/asm/arch-sunxi/spl.h
@@ -16,6 +16,7 @@
 #define SUNXI_BOOTED_FROM_NAND 1
 #define SUNXI_BOOTED_FROM_MMC2 2
 #define SUNXI_BOOTED_FROM_SPI  3
+#define SUNXI_BOOTED_FROM_SPINAND  4
 
 /*
  * Values taken from the F1C200s BootROM stack
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index 7f4ee92991..e374b75ac2 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -222,11 +222,12 @@ static int suniv_get_boot_source(void)
switch (brom_call) {
case SUNIV_BOOTED_FROM_MMC0:
return SUNXI_BOOTED_FROM_MMC0;
-   case SUNIV_BOOTED_FROM_SPI:
case SUNIV_BOOTED_FROM_NAND:
return SUNXI_BOOTED_FROM_SPI;
case SUNIV_BOOTED_FROM_MMC1:
return SUNXI_BOOTED_FROM_MMC2;
+   case SUNIV_BOOTED_FROM_SPI:
+   return SUNXI_BOOTED_FROM_SPINAND;
}
/* If we get here something went wrong try to boot from FEL.*/
printf("Unknown boot source from BROM: 0x%x\n", brom_call);
@@ -306,6 +307,8 @@ uint32_t sunxi_get_boot_device(void)
return BOOT_DEVICE_MMC2;
case SUNXI_BOOTED_FROM_SPI:
return BOOT_DEVICE_SPI;
+   case SUNXI_BOOTED_FROM_SPINAND:
+   return BOOT_DEVICE_SPINAND;
}
 
panic("Unknown boot source %d\n", boot_source);
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c 
b/arch/arm/mach-sunxi/spl_spi_sunxi.c
index 7ecde2b753..46db2900ca 100644
--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
+++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
@@ -494,28 +494,36 @@ static int spl_spi_load_image(struct spl_image_info 
*spl_image,
int ret = 0;
uint32_t load_offset = sunxi_get_spl_size();
struct spl_load_info load;
+   bool allow_raw = false;
 
load_offset = max_t(uint32_t, load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS);
 
spi0_init();
 
+   switch (bootdev->boot_device) {
 #if defined(CONFIG_SPL_SPI_SUNXI_NAND)
-   spi0_nand_reset();
-   load.read = spi_load_read_nand;
-   spl_set_bl_len(, 1);
-   ret = spl_spi_try_load(spl_image, bootdev, , load_offset, false);
-   if (!ret)
-   goto out;
+   case BOOT_DEVICE_SPINAND:
+   spi0_nand_reset();
+   load.read = spi_load_read_nand;
+   spl_set_bl_len(, 1);
+   break;
 #endif
+   case BOOT_DEVICE_SPI:
+   load.read = spi_load_read_nor;
+   spl_set_bl_len(, 1);
+   allow_raw = true;
+   break;
+   }
 
-   spl_set_bl_len(, 1);
-   load.read = spi_load_read_nor;
-   ret = spl_spi_try_load(spl_image, bootdev, , load_offset, true);
+   ret = spl_spi_try_load(spl_image, bootdev, , load_offset, 
allow_raw);
 
-out:
spi0_deinit();
 
return ret;
 }
 /* Use priorty 0 to override the default if it happens to be linked in */
 SPL_LOAD_IMAGE_METHOD("sunxi SPI", 0, BOOT_DEVICE_SPI, spl_spi_load_image);
+
+#if IS_ENABLED(CONFIG_SPL_SPI_SUNXI_NAND)
+SPL_LOAD_IMAGE_METHOD("sunxi SPI NAND", 0, BOOT_DEVICE_SPINAND, 
spl_spi_load_image);
+#endif

-- 
2.44.0



[PATCH RFC 07/15] sunxi: Separate boot device and boot position

2024-04-10 Thread John Watts
While MMC1 and MMC2 each currently have only one upper byte possibility,
SPI NAND has quite a few. To solve this, split up the byte handling across
two functions in preparation for SPI NAND support.

I have not tested this patch to validate that MMC SPL offsets are working.
It looks like it should work though.

Signed-off-by: John Watts 
---
 arch/arm/include/asm/arch-sunxi/spl.h |  2 --
 arch/arm/mach-sunxi/board.c   | 22 +-
 2 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/arch/arm/include/asm/arch-sunxi/spl.h 
b/arch/arm/include/asm/arch-sunxi/spl.h
index 14944a20ea..92be936d56 100644
--- a/arch/arm/include/asm/arch-sunxi/spl.h
+++ b/arch/arm/include/asm/arch-sunxi/spl.h
@@ -16,8 +16,6 @@
 #define SUNXI_BOOTED_FROM_NAND 1
 #define SUNXI_BOOTED_FROM_MMC2 2
 #define SUNXI_BOOTED_FROM_SPI  3
-#define SUNXI_BOOTED_FROM_MMC0_HIGH0x10
-#define SUNXI_BOOTED_FROM_MMC2_HIGH0x12
 
 /*
  * Values taken from the F1C200s BootROM stack
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index 9b0d68a7a0..7f4ee92991 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -276,6 +276,7 @@ static int sunxi_get_boot_source(void)
 uint32_t sunxi_get_boot_device(void)
 {
int boot_source = sunxi_get_boot_source();
+   int boot_dev = (boot_source & 0xF); /* Low nibble is device */
 
/*
 * When booting from the SD card or NAND memory, the "eGON.BT0"
@@ -293,16 +294,15 @@ uint32_t sunxi_get_boot_device(void)
 * binary over USB. If it is found, it determines where SPL was
 * read from.
 */
-   switch (boot_source) {
-   case SUNXI_INVALID_BOOT_SOURCE:
+   if (boot_source == SUNXI_INVALID_BOOT_SOURCE)
return BOOT_DEVICE_BOARD;
+
+   switch (boot_dev) {
case SUNXI_BOOTED_FROM_MMC0:
-   case SUNXI_BOOTED_FROM_MMC0_HIGH:
return BOOT_DEVICE_MMC1;
case SUNXI_BOOTED_FROM_NAND:
return BOOT_DEVICE_NAND;
case SUNXI_BOOTED_FROM_MMC2:
-   case SUNXI_BOOTED_FROM_MMC2_HIGH:
return BOOT_DEVICE_MMC2;
case SUNXI_BOOTED_FROM_SPI:
return BOOT_DEVICE_SPI;
@@ -312,6 +312,14 @@ uint32_t sunxi_get_boot_device(void)
return -1;  /* Never reached */
 }
 
+uint32_t sunxi_get_boot_position(void)
+{
+   int boot_source = sunxi_get_boot_source();
+   int boot_pos = ((boot_source >> 8) & 0xF); /* High nibble is position */
+
+   return boot_pos;
+}
+
 #ifdef CONFIG_SPL_BUILD
 uint32_t sunxi_get_spl_size(void)
 {
@@ -343,12 +351,8 @@ unsigned long board_spl_mmc_get_uboot_raw_sector(struct 
mmc *mmc,
 
sector = max(raw_sect, spl_size / 512);
 
-   switch (sunxi_get_boot_source()) {
-   case SUNXI_BOOTED_FROM_MMC0_HIGH:
-   case SUNXI_BOOTED_FROM_MMC2_HIGH:
+   if (sunxi_get_boot_position() == 1)
sector += (128 - 8) * 2;
-   break;
-   }
 
return sector;
 }

-- 
2.44.0



[PATCH RFC 06/15] sunxi: enable support for SPI NAND booting on SUNIV

2024-04-10 Thread John Watts
From: Icenowy Zheng 

As we added support for SPI NAND to the existing SPL SPI codepath, route
the boot code to it when it detects the BROM loads SPL from SPI NAND, as
for SoCs with both SPI NAND and boot media indicator support, the boot
media indicator is the same for SPI NOR and NAND.

Signed-off-by: Icenowy Zheng 
Reviewed-by: Samuel Holland 
---
 arch/arm/mach-sunxi/board.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index f4dbb2a740..9b0d68a7a0 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -223,12 +223,10 @@ static int suniv_get_boot_source(void)
case SUNIV_BOOTED_FROM_MMC0:
return SUNXI_BOOTED_FROM_MMC0;
case SUNIV_BOOTED_FROM_SPI:
+   case SUNIV_BOOTED_FROM_NAND:
return SUNXI_BOOTED_FROM_SPI;
case SUNIV_BOOTED_FROM_MMC1:
return SUNXI_BOOTED_FROM_MMC2;
-   /* SPI NAND is not supported yet. */
-   case SUNIV_BOOTED_FROM_NAND:
-   return SUNXI_INVALID_BOOT_SOURCE;
}
/* If we get here something went wrong try to boot from FEL.*/
printf("Unknown boot source from BROM: 0x%x\n", brom_call);

-- 
2.44.0



[PATCH RFC 05/15] sunxi: SPL SPI: add initial support for booting from SPI NAND

2024-04-10 Thread John Watts
From: Icenowy Zheng 

This commit adds support for booting from SPI NAND to SPL SPI code by
mimicing the behavior of boot ROM (use fixed page size and sequentially
try SPI NOR and NAND).

Signed-off-by: Icenowy Zheng 
Tested-by: Samuel Holland  # Orange Pi Zero Plus
---
 arch/arm/mach-sunxi/Kconfig | 16 
 arch/arm/mach-sunxi/spl_spi_sunxi.c | 75 +
 2 files changed, 91 insertions(+)

diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index ddf9414b08..cc576fc84d 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -1084,6 +1084,22 @@ config SPL_SPI_SUNXI
  sunxi SPI Flash. It uses the same method as the boot ROM, so does
  not need any extra configuration.
 
+config SPL_SPI_SUNXI_NAND
+   bool "Support for SPI NAND Flash on Allwinner SoCs in SPL"
+   depends on SPL_SPI_SUNXI
+   help
+ Enable support for SPI NAND Flash. This option allows SPL to mimic
+ Allwinner boot ROM's behavior to gain support for SPI NAND Flash;
+ a fixed page size needs to be assumed when building the SPL image.
+
+config SPL_SPI_SUNXI_NAND_ASSUMED_PAGESIZE
+   hex "Assumed pagesize for SPI NAND Flash in SPL"
+   depends on SPL_SPI_SUNXI_NAND
+   default 0x400 if MACH_SUNIV
+   help
+ Set the page size assumed by the SPL SPI NAND code, the default
+ value is the same with the boot ROM.
+
 config PINE64_DT_SELECTION
bool "Enable Pine64 device tree selection code"
depends on MACH_SUN50I
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c 
b/arch/arm/mach-sunxi/spl_spi_sunxi.c
index 58b1422da1..7ecde2b753 100644
--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
+++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
@@ -344,6 +344,49 @@ static void spi0_xfer(const u8 *txbuf, u32 txlen, u8 
*rxbuf, u32 rxlen)
}
 }
 
+#if defined(CONFIG_SPL_SPI_SUNXI_NAND)
+static int spi0_nand_switch_page(u32 page)
+{
+   unsigned count;
+   u8 buf[4];
+
+   /* Configure the Page Data Read (13h) command header */
+   buf[0] = 0x13;
+   buf[1] = (u8)(page >> 16);
+   buf[2] = (u8)(page >> 8);
+   buf[3] = (u8)(page);
+
+   spi0_xfer(buf, 4, NULL, 0);
+
+   /* Wait for NAND chip to exit busy state */
+   buf[0] = 0x0f;
+   buf[1] = 0xc0;
+
+   /* Load a NAND page can take up to 2-decimal-digit microseconds */
+   for (count = 0; count < 100; count ++) {
+   udelay(1);
+   spi0_xfer(buf, 2, buf+2, 1);
+   if (!(buf[2] & 0x1))
+   return 0;
+   }
+
+   return -ETIMEDOUT;
+}
+
+static void spi0_nand_reset(void)
+{
+   u8 buf[1];
+
+   /* Configure the Device RESET (ffh) command */
+   buf[0] = 0xff;
+
+   spi0_xfer(buf, 1, NULL, 0);
+
+   /* Wait for the NAND to finish resetting */
+   udelay(10);
+}
+#endif
+
 static void spi0_read_data(void *buf, u32 addr, u32 len, u32 addr_len)
 {
u8 *buf8 = buf;
@@ -387,6 +430,28 @@ static ulong spi_load_read_nor(struct spl_load_info *load, 
ulong sector,
return count;
 }
 
+#if defined(CONFIG_SPL_SPI_SUNXI_NAND)
+static ulong spi_load_read_nand(struct spl_load_info *load, ulong sector,
+  ulong count, void *buf)
+{
+   const ulong pagesize = CONFIG_SPL_SPI_SUNXI_NAND_ASSUMED_PAGESIZE;
+   ulong remain = count;
+
+   while (remain) {
+   ulong count_in_page = min(remain, pagesize - (sector % 
pagesize));
+   ulong current_page = sector / pagesize;
+   if (spi0_nand_switch_page(current_page) != 0)
+   return 0;
+   spi0_read_data(buf, sector % pagesize, count_in_page, 2);
+   remain -= count_in_page;
+   sector += count_in_page;
+   buf += count_in_page;
+   }
+
+   return count;
+}
+#endif
+
 /*/
 
 static int spl_spi_try_load(struct spl_image_info *spl_image,
@@ -434,10 +499,20 @@ static int spl_spi_load_image(struct spl_image_info 
*spl_image,
 
spi0_init();
 
+#if defined(CONFIG_SPL_SPI_SUNXI_NAND)
+   spi0_nand_reset();
+   load.read = spi_load_read_nand;
+   spl_set_bl_len(, 1);
+   ret = spl_spi_try_load(spl_image, bootdev, , load_offset, false);
+   if (!ret)
+   goto out;
+#endif
+
spl_set_bl_len(, 1);
load.read = spi_load_read_nor;
ret = spl_spi_try_load(spl_image, bootdev, , load_offset, true);
 
+out:
spi0_deinit();
 
return ret;

-- 
2.44.0



[PATCH RFC 04/15] sunxi: SPL SPI: allow multiple boot attempt

2024-04-10 Thread John Watts
From: Icenowy Zheng 

As we're going to add support for SPI NAND to this code, add code that
allows multiple boot attempts with different load offsets and functions.

To keep compatibility with loading raw binary on SPI NOR, a bool
parameter is used to allow booting without valid magic number when
booting with SPI NOR.

Signed-off-by: Icenowy Zheng 
Reviewed-by: Samuel Holland 
Tested-by: Samuel Holland  # Orange Pi Zero Plus
---
 arch/arm/mach-sunxi/spl_spi_sunxi.c | 51 -
 1 file changed, 34 insertions(+), 17 deletions(-)

diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c 
b/arch/arm/mach-sunxi/spl_spi_sunxi.c
index fe95878eae..58b1422da1 100644
--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
+++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
@@ -379,8 +379,8 @@ static void spi0_read_data(void *buf, u32 addr, u32 len, 
u32 addr_len)
}
 }
 
-static ulong spi_load_read(struct spl_load_info *load, ulong sector,
-  ulong count, void *buf)
+static ulong spi_load_read_nor(struct spl_load_info *load, ulong sector,
+  ulong count, void *buf)
 {
spi0_read_data(buf, sector, count, 3);
 
@@ -389,38 +389,55 @@ static ulong spi_load_read(struct spl_load_info *load, 
ulong sector,
 
 /*/
 
-static int spl_spi_load_image(struct spl_image_info *spl_image,
- struct spl_boot_device *bootdev)
+static int spl_spi_try_load(struct spl_image_info *spl_image,
+   struct spl_boot_device *bootdev,
+   struct spl_load_info *load, u32 offset,
+   bool allow_raw)
 {
int ret = 0;
struct legacy_img_hdr *header;
-   uint32_t load_offset = sunxi_get_spl_size();
-
header = (struct legacy_img_hdr *)CONFIG_TEXT_BASE;
-   load_offset = max_t(uint32_t, load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS);
 
-   spi0_init();
-
-   spi0_read_data((void *)header, load_offset, 0x40, 3);
+   if (load->read(load, offset, 0x40, (void *)header) == 0)
+   return -EINVAL;
 
 if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&
image_get_magic(header) == FDT_MAGIC) {
-   struct spl_load_info load;
 
debug("Found FIT image\n");
-   spl_set_bl_len(, 1);
-   load.read = spi_load_read;
-   ret = spl_load_simple_fit(spl_image, ,
- load_offset, header);
+   ret = spl_load_simple_fit(spl_image, load,
+ offset, header);
} else {
+   if (!allow_raw && image_get_magic(header) != IH_MAGIC)
+   return -EINVAL;
+
ret = spl_parse_image_header(spl_image, bootdev, header);
if (ret)
return ret;
 
-   spi0_read_data((void *)spl_image->load_addr,
-  load_offset, spl_image->size, 3);
+   if (load->read(load, offset, spl_image->size,
+  (void *)spl_image->load_addr) == 0)
+   ret = -EINVAL;
}
 
+   return ret;
+}
+
+static int spl_spi_load_image(struct spl_image_info *spl_image,
+ struct spl_boot_device *bootdev)
+{
+   int ret = 0;
+   uint32_t load_offset = sunxi_get_spl_size();
+   struct spl_load_info load;
+
+   load_offset = max_t(uint32_t, load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS);
+
+   spi0_init();
+
+   spl_set_bl_len(, 1);
+   load.read = spi_load_read_nor;
+   ret = spl_spi_try_load(spl_image, bootdev, , load_offset, true);
+
spi0_deinit();
 
return ret;

-- 
2.44.0



[PATCH RFC 03/15] sunxi: SPL SPI: add support for read command with 2 byte address

2024-04-10 Thread John Watts
From: Icenowy Zheng 

This kind of read command is utilized in SPI NANDs for reading data
inside a selected page, which is obviously smaller than how much 2
byte address can address. So 2 bytes are used for the address and one
dummy byte is needed after the real address. As the address is sent out
in bit endian, this makes it not compatible with usual 3 byte address.

Signed-off-by: Icenowy Zheng 
Reviewed-by: Samuel Holland 
Tested-by: Samuel Holland  # Orange Pi Zero Plus
---
 arch/arm/mach-sunxi/spl_spi_sunxi.c | 20 +---
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c 
b/arch/arm/mach-sunxi/spl_spi_sunxi.c
index e85ae96485..fe95878eae 100644
--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
+++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
@@ -344,7 +344,7 @@ static void spi0_xfer(const u8 *txbuf, u32 txlen, u8 
*rxbuf, u32 rxlen)
}
 }
 
-static void spi0_read_data(void *buf, u32 addr, u32 len)
+static void spi0_read_data(void *buf, u32 addr, u32 len, u32 addr_len)
 {
u8 *buf8 = buf;
u32 chunk_len;
@@ -355,9 +355,15 @@ static void spi0_read_data(void *buf, u32 addr, u32 len)
 
/* Configure the Read Data Bytes (03h) command header */
txbuf[0] = 0x03;
-   txbuf[1] = (u8)(addr >> 16);
-   txbuf[2] = (u8)(addr >> 8);
-   txbuf[3] = (u8)(addr);
+   if (addr_len == 3) {
+   txbuf[1] = (u8)(addr >> 16);
+   txbuf[2] = (u8)(addr >> 8);
+   txbuf[3] = (u8)(addr);
+   } else if (addr_len == 2) {
+   txbuf[1] = (u8)(addr >> 8);
+   txbuf[2] = (u8)(addr);
+   txbuf[3] = 0; /* dummy */
+   }
 
if (chunk_len > SPI_READ_MAX_SIZE)
chunk_len = SPI_READ_MAX_SIZE;
@@ -376,7 +382,7 @@ static void spi0_read_data(void *buf, u32 addr, u32 len)
 static ulong spi_load_read(struct spl_load_info *load, ulong sector,
   ulong count, void *buf)
 {
-   spi0_read_data(buf, sector, count);
+   spi0_read_data(buf, sector, count, 3);
 
return count;
 }
@@ -395,7 +401,7 @@ static int spl_spi_load_image(struct spl_image_info 
*spl_image,
 
spi0_init();
 
-   spi0_read_data((void *)header, load_offset, 0x40);
+   spi0_read_data((void *)header, load_offset, 0x40, 3);
 
 if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&
image_get_magic(header) == FDT_MAGIC) {
@@ -412,7 +418,7 @@ static int spl_spi_load_image(struct spl_image_info 
*spl_image,
return ret;
 
spi0_read_data((void *)spl_image->load_addr,
-  load_offset, spl_image->size);
+  load_offset, spl_image->size, 3);
}
 
spi0_deinit();

-- 
2.44.0



[PATCH RFC 02/15] sunxi: SPL SPI: extract code for doing SPI transfer

2024-04-10 Thread John Watts
From: Icenowy Zheng 

To support SPI NAND flashes, more commands than Read (03h) are needed.

Extract the code for doing SPI transfer from the reading code for code
reuse.

Signed-off-by: Icenowy Zheng 
Reviewed-by: Samuel Holland 
Tested-by: Samuel Holland  # Orange Pi Zero Plus
---
 arch/arm/mach-sunxi/spl_spi_sunxi.c | 105 
 1 file changed, 59 insertions(+), 46 deletions(-)

diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c 
b/arch/arm/mach-sunxi/spl_spi_sunxi.c
index 7acb44f52a..e85ae96485 100644
--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
+++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
@@ -282,77 +282,90 @@ static void spi0_deinit(void)
 
 #define SPI_READ_MAX_SIZE 60 /* FIFO size, minus 4 bytes of the header */
 
-static void sunxi_spi0_read_data(u8 *buf, u32 addr, u32 bufsize,
-ulong spi_ctl_reg,
-ulong spi_ctl_xch_bitmask,
-ulong spi_fifo_reg,
-ulong spi_tx_reg,
-ulong spi_rx_reg,
-ulong spi_bc_reg,
-ulong spi_tc_reg,
-ulong spi_bcc_reg)
+static void sunxi_spi0_xfer(const u8 *txbuf, u32 txlen,
+   u8 *rxbuf, u32 rxlen,
+   ulong spi_ctl_reg,
+   ulong spi_ctl_xch_bitmask,
+   ulong spi_fifo_reg,
+   ulong spi_tx_reg,
+   ulong spi_rx_reg,
+   ulong spi_bc_reg,
+   ulong spi_tc_reg,
+   ulong spi_bcc_reg)
 {
-   writel(4 + bufsize, spi_bc_reg); /* Burst counter (total bytes) */
-   writel(4, spi_tc_reg);   /* Transfer counter (bytes to send) */
+   writel(txlen + rxlen, spi_bc_reg); /* Burst counter (total bytes) */
+   writel(txlen, spi_tc_reg); /* Transfer counter (bytes to send) 
*/
if (spi_bcc_reg)
-   writel(4, spi_bcc_reg);  /* SUN6I also needs this */
+   writel(txlen, spi_bcc_reg);  /* SUN6I also needs this */
 
-   /* Send the Read Data Bytes (03h) command header */
-   writeb(0x03, spi_tx_reg);
-   writeb((u8)(addr >> 16), spi_tx_reg);
-   writeb((u8)(addr >> 8), spi_tx_reg);
-   writeb((u8)(addr), spi_tx_reg);
+   for (u32 i = 0; i < txlen; i++)
+   writeb(*(txbuf++), spi_tx_reg);
 
/* Start the data transfer */
setbits_le32(spi_ctl_reg, spi_ctl_xch_bitmask);
 
/* Wait until everything is received in the RX FIFO */
-   while ((readl(spi_fifo_reg) & 0x7F) < 4 + bufsize)
+   while ((readl(spi_fifo_reg) & 0x7F) < txlen + rxlen)
;
 
-   /* Skip 4 bytes */
-   readl(spi_rx_reg);
+   /* Skip txlen bytes */
+   for (u32 i = 0; i < txlen; i++)
+   readb(spi_rx_reg);
 
/* Read the data */
-   while (bufsize-- > 0)
-   *buf++ = readb(spi_rx_reg);
+   while (rxlen-- > 0)
+   *rxbuf++ = readb(spi_rx_reg);
+}
+
+static void spi0_xfer(const u8 *txbuf, u32 txlen, u8 *rxbuf, u32 rxlen)
+{
+   uintptr_t base = spi0_base_address();
 
-   /* tSHSL time is up to 100 ns in various SPI flash datasheets */
-   udelay(1);
+   if (is_sun6i_gen_spi()) {
+   sunxi_spi0_xfer(txbuf, txlen, rxbuf, rxlen,
+   base + SUN6I_SPI0_TCR,
+   SUN6I_TCR_XCH,
+   base + SUN6I_SPI0_FIFO_STA,
+   base + SUN6I_SPI0_TXD,
+   base + SUN6I_SPI0_RXD,
+   base + SUN6I_SPI0_MBC,
+   base + SUN6I_SPI0_MTC,
+   base + SUN6I_SPI0_BCC);
+   } else {
+   sunxi_spi0_xfer(txbuf, txlen, rxbuf, rxlen,
+   base + SUN4I_SPI0_CTL,
+   SUN4I_CTL_XCH,
+   base + SUN4I_SPI0_FIFO_STA,
+   base + SUN4I_SPI0_TX,
+   base + SUN4I_SPI0_RX,
+   base + SUN4I_SPI0_BC,
+   base + SUN4I_SPI0_TC,
+   0);
+   }
 }
 
 static void spi0_read_data(void *buf, u32 addr, u32 len)
 {
u8 *buf8 = buf;
u32 chunk_len;
-   uintptr_t base = spi0_base_address();
+   u8 txbuf[4];
 
while (len > 0) {
chunk_len = len;
+
+   /* Configure the Read Data Bytes (03h) command header */
+   txbuf[0] = 0x03;
+   txbuf[1] = (u8)(addr >> 16);
+   txbuf[2] = (u8)(addr >> 8);
+   txbuf[3] = (u8)(addr);
+
if (chunk_len > SPI_READ_MAX_SIZE)
chunk_len = 

[PATCH RFC 01/15] sunxi: SPL SPI: Add SPI boot support for the Allwinner R528/T113 SoCs

2024-04-10 Thread John Watts
From: Maksim Kiselev 

R528/T113 SoCs uses the same SPI IP as the H6, also have the same clocks
and reset bits layout, but the CCU base is different. Another difference
is that the new SoCs do not have a clock divider inside. Instead of this
we should configure sample mode depending on input clock rate.

The pin assignment is also different: the H6 uses PC0, the R528/T113 PC4
instead. This makes for a change in spi0_pinmux_setup() routine.

This patch extends the H6/H616 #ifdef guards to also cover the R528/T113,
using the shared CONFIG_SUNXI_GEN_NCAT2 and CONFIG_MACH_SUN8I_R528
symbols. Also use CONFIG_SUNXI_GEN_NCAT2 symbol for the Kconfig
dependency.

Signed-off-by: Maksim Kiselev 
Tested-by: Sam Edwards 
---
 arch/arm/mach-sunxi/Kconfig |  2 +-
 arch/arm/mach-sunxi/spl_spi_sunxi.c | 78 +++--
 2 files changed, 58 insertions(+), 22 deletions(-)

diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index fe89aec6b9..ddf9414b08 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -1078,7 +1078,7 @@ config SPL_STACK_R_ADDR
 
 config SPL_SPI_SUNXI
bool "Support for SPI Flash on Allwinner SoCs in SPL"
-   depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUNXI_H3_H5 
|| MACH_SUN50I || MACH_SUN8I_R40 || SUN50I_GEN_H6 || MACH_SUNIV
+   depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUNXI_H3_H5 
|| MACH_SUN50I || MACH_SUN8I_R40 || SUN50I_GEN_H6 || MACH_SUNIV || 
SUNXI_GEN_NCAT2
help
  Enable support for SPI Flash. This option allows SPL to read from
  sunxi SPI Flash. It uses the same method as the boot ROM, so does
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c 
b/arch/arm/mach-sunxi/spl_spi_sunxi.c
index 72faa7171c..7acb44f52a 100644
--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
+++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
@@ -72,18 +72,27 @@
 #define SUN6I_CTL_ENABLEBIT(0)
 #define SUN6I_CTL_MASTERBIT(1)
 #define SUN6I_CTL_SRST  BIT(31)
+#define SUN6I_TCR_SDM   BIT(13)
 #define SUN6I_TCR_XCH   BIT(31)
 
 /*/
 
-#define CCM_AHB_GATING0 (0x01C2 + 0x60)
-#define CCM_H6_SPI_BGR_REG  (0x03001000 + 0x96c)
-#ifdef CONFIG_SUN50I_GEN_H6
-#define CCM_SPI0_CLK(0x03001000 + 0x940)
+#if IS_ENABLED(CONFIG_SUN50I_GEN_H6)
+#define CCM_BASE0x03001000
+#elif IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)
+#define CCM_BASE0x02001000
 #else
-#define CCM_SPI0_CLK(0x01C2 + 0xA0)
+#define CCM_BASE0x01C2
 #endif
-#define SUN6I_BUS_SOFT_RST_REG0 (0x01C2 + 0x2C0)
+
+#define CCM_AHB_GATING0 (CCM_BASE + 0x60)
+#define CCM_H6_SPI_BGR_REG  (CCM_BASE + 0x96c)
+#if IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)
+#define CCM_SPI0_CLK(CCM_BASE + 0x940)
+#else
+#define CCM_SPI0_CLK(CCM_BASE + 0xA0)
+#endif
+#define SUN6I_BUS_SOFT_RST_REG0 (CCM_BASE + 0x2C0)
 
 #define AHB_RESET_SPI0_SHIFT20
 #define AHB_GATE_OFFSET_SPI020
@@ -101,17 +110,22 @@
  */
 static void spi0_pinmux_setup(unsigned int pin_function)
 {
-   /* All chips use PC0 and PC2. */
-   sunxi_gpio_set_cfgpin(SUNXI_GPC(0), pin_function);
+   /* All chips use PC2. And all chips use PC0, except R528/T113 */
+   if (!IS_ENABLED(CONFIG_MACH_SUN8I_R528))
+   sunxi_gpio_set_cfgpin(SUNXI_GPC(0), pin_function);
+
sunxi_gpio_set_cfgpin(SUNXI_GPC(2), pin_function);
 
-   /* All chips except H6 and H616 use PC1. */
-   if (!IS_ENABLED(CONFIG_SUN50I_GEN_H6))
+   /* All chips except H6/H616/R528/T113 use PC1. */
+   if (!IS_ENABLED(CONFIG_SUN50I_GEN_H6) &&
+   !IS_ENABLED(CONFIG_MACH_SUN8I_R528))
sunxi_gpio_set_cfgpin(SUNXI_GPC(1), pin_function);
 
-   if (IS_ENABLED(CONFIG_MACH_SUN50I_H6))
+   if (IS_ENABLED(CONFIG_MACH_SUN50I_H6) ||
+   IS_ENABLED(CONFIG_MACH_SUN8I_R528))
sunxi_gpio_set_cfgpin(SUNXI_GPC(5), pin_function);
-   if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
+   if (IS_ENABLED(CONFIG_MACH_SUN50I_H616) ||
+   IS_ENABLED(CONFIG_MACH_SUN8I_R528))
sunxi_gpio_set_cfgpin(SUNXI_GPC(4), pin_function);
 
/* Older generations use PC23 for CS, newer ones use PC3. */
@@ -125,7 +139,8 @@ static void spi0_pinmux_setup(unsigned int pin_function)
 static bool is_sun6i_gen_spi(void)
 {
return IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I) ||
-  IS_ENABLED(CONFIG_SUN50I_GEN_H6);
+  IS_ENABLED(CONFIG_SUN50I_GEN_H6) ||
+  IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2);
 }
 
 static uintptr_t spi0_base_address(void)
@@ -136,6 +151,9 @@ static uintptr_t spi0_base_address(void)
if (IS_ENABLED(CONFIG_SUN50I_GEN_H6))
return 0x0501;
 
+   

[PATCH RFC 00/15] Support SPI NAND booting on the T113

2024-04-10 Thread John Watts
This series is my current working and tested setup for booting from
SPI NAND chips on the Allwinner T113.

I have included the following patches from others. I may have modified
them to work with the latest mainline:

https://lore.kernel.org/all/20221014030520.3067228-1-...@icenowy.me/
https://lore.kernel.org/all/2023133432.755363-2-biguncle...@gmail.com/

Hopefully this can get the ball rolling on how to properly implement
SPI NAND support in mainline U-Boot.

Signed-off-by: John Watts 
---
Icenowy Zheng (5):
  sunxi: SPL SPI: extract code for doing SPI transfer
  sunxi: SPL SPI: add support for read command with 2 byte address
  sunxi: SPL SPI: allow multiple boot attempt
  sunxi: SPL SPI: add initial support for booting from SPI NAND
  sunxi: enable support for SPI NAND booting on SUNIV

John Watts (9):
  sunxi: Separate boot device and boot position
  spl: Add BOOT_DEVICE_SPINAND option
  sunxi: Implement BOOT_DEVICE_SPINAND in SPL
  spl: Add SPL_SPINAND configuration options
  sunxi: Use SPL_SPINAND for configuration
  nand: Add spinand_ helper functions
  sunxi: Implement spinand_ helpers
  spl: Support SPI NAND boot in UBI
  spl: Support loading FIT images in UBI

Maksim Kiselev (1):
  sunxi: SPL SPI: Add SPI boot support for the Allwinner R528/T113 SoCs

 arch/arm/include/asm/arch-sunxi/spl.h |   3 +-
 arch/arm/include/asm/spl.h|   1 +
 arch/arm/mach-sunxi/Kconfig   |   2 +-
 arch/arm/mach-sunxi/board.c   |  31 +--
 arch/arm/mach-sunxi/spl_spi_sunxi.c   | 348 +-
 arch/mips/include/asm/spl.h   |   1 +
 arch/riscv/include/asm/spl.h  |   1 +
 arch/sandbox/include/asm/spl.h|   1 +
 common/spl/Kconfig|  21 ++
 common/spl/spl_ubi.c  |  49 -
 include/nand.h|   3 +
 11 files changed, 354 insertions(+), 107 deletions(-)
---
base-commit: 777c28460947371ada40868dc994dfe8537d7115
change-id: 20240411-spinand-eb7d8319813b

Best regards,
-- 
John Watts 



T113 sunxi SPI NAND thoughts, feedback wanted

2023-12-11 Thread John Watts
Greetings sunxi and U-Boot friends!

Over the past five months I've managed to slog through getting a
complete SPI NAND U-Boot and Linux setup running on my Mango Pi MQ.

My tree is here, but I will be slowly trying to upstream my work over
the next few months: https://github.com/Jookia/u-boot/commits/jookia_t113/

It is based on master at the moment with the following patches:
- SUNIV SPI NAND support in SPL
  
(https://lore.kernel.org/u-boot/8034158c-03ab-7488-6afa-a67f04264...@gmail.com)
- UART1 and UART2 support for the Mango Pi MQ
- SPI NAND device tree addition for my device
- A new boot option: BOOT_DEVICE_SPINAND
- SPL SPI booting on the T113 (superseeded by someone else)
- SPI controller support for the T113 (superseeded by someone else)
- MTD Kconfig requirement for UBI
- spinand_ helper functions to support UBI in the SPL
- UBI SPINAND support
- UBI SPL FIT support
- musb gadget suport (superseeded by someone else)

This patch seems to be independently written for the T113 SPI support
but looks the same as what I've done:
https://lore.kernel.org/u-boot/2023133432.755363-1-biguncle...@gmail.com/

The same with this USB fix:
https://lore.kernel.org/u-boot/20230615190701.327852-1-cfswo...@gmail.com/

I plan to add feedback and review to both these patches. Though USB is a
separate subject, I would be interested to know what speeds people are
getting over USB gadget on the T113. DFU seems to cap out at 70KiB/s,
much lower than SSH in Linux.

Anyway, talking in IRC and reading patches on the mailing list, it seems
there's a little lack of direction for SPI NAND support in U-Boot.
Particularly around how to integrate it alongside existing NAND support
and handling bad blocks.

I'd like to first talk about the boot device situation. It works like
this:

- BOOT_DEVICE_SPI means SPI NOR memory
- BOOT_DEVICE_NAND means parallel NAND memory
- BOOT_DEVICE_ONENAND means OneNAND memory
- drivers/mtd/spi uses drivers/spi/spi-mem
- drivers/mtd/nand/spi uses drivers/spi/spi-mem
- drivers/spi/spi-mem uses drivers/spi/spi-sunxi
- common/spl/spl_spi uses drivers/spi/spi-mem and BOOT_DEVICE_SPI
- common/spl/spl_nand uses mtd/nand/raw and BOOT_DEVICE_NAND
- common/spl/spl_onenand uses drivers/mtd/onenand and BOOT_DEVICE_ONENAND
- common/spl/spl_ubi uses drivers/mtd/nand/raw or drivers/mtd/onenand
  and either BOOT_DEVICE_NAND or BOOT_DEVICE_ONENAND
- drivers/nand/raw has a sunxi NAND and sunxi NAND SPL driver
- arch/arm/mach-sunxi/spl_spi_sunxi implements its own SPI loader

A quick grep shows the following custom SPL_LOAD_IMAGE_METHODs in arch:

arch/arm/mach-sunxi/spl_spi_sunxi.c:SPL_LOAD_IMAGE_METHOD("sunxi SPI", 0, 
BOOT_DEVICE_SPI, spl_spi_load_image);
arch/x86/cpu/apollolake/spl.c:SPL_LOAD_IMAGE_METHOD("Mapped SPI", 2, 
BOOT_DEVICE_SPI_MMAP, rom_load_image);
arch/x86/cpu/apollolake/spl.c:SPL_LOAD_IMAGE_METHOD("Fast SPI", 1, 
BOOT_DEVICE_FAST_SPI,

Looking closer at the NAND APIs SPL uses, it calls the following
functions and defines:

nand_init();
nand_deselect();
int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst);
int nand_spl_read_block(int block, int offset, int len, void *dst);
int onenand_spl_read_block(int block, int offset, int len, void *dst);
int onenand_spl_load_image(uint32_t offs, uint32_t size, void *dst);
BOOT_DEVICE_NAND
BOOT_DEVICE_ONENAND
CONFIG_SYS_ONENAND_PAGE_SIZE
CONFIG_SYS_NAND_U_BOOT_OFFS
CONFIG_SYS_NAND_BLOCK_SIZE
CONFIG_SYS_NAND_PAGE_SIZE
CONFIG_SPL_SPI_SUNXI_NAND_ASSUMED_PAGESIZE (used by SUNIV NAND patch)

In the SUNIV NAND patch there was a question of whether to add a
device-specific BOOT_DEVICE. I think the answer to that heavily depends
on whether the current OneNAND/NAND separation makes sense. The APIs are
basically the same, just with different implementations. The only thing
the BOOT_DEVICE_ does here is indicate that there is a distinct boot
ROM option and that we have booted from it. This disambiguates which
NAND we would be using on sunxi: Parallel or SPI.

In my patches I went ahead and added a new SPI NAND API:

spinand_init();
spinand_deselect();
spinand_spl_read_block(int block, int offset, int len, void *dst);
BOOT_DEVICE_SPINAND
CONFIG_SPL_SPINAND_PAGE_SIZE
CONFIG_SPL_SPINAND_BLOCK_SIZE
(I removed the SUNIV NAND config)

I implemented this in the custom sunxi loader code and made UBI use it.
It might be better off as its own thing in drivers/mtd/nand/spi. Then
perhaps arch/arm/mach-sunxi/spl_spi_sunxi could be refactored and moved
to drivers/mtd/spi/, or removed entirely.

I also want to note that with NAND there's also the question of what to
do with bad blocks: The flash I use has 128KiB block sizes. The SPL is
32KiB and U-Boot is 444KiB, needing 4 blocks.

The boot ROM will try multiple pages. To quote
https://linux-sunxi.org/Bootable_SPI_flash:

"Some SoCs can also boot from SPI NAND flash. Here the BROM tries to
read a valid first stage bootloader starting from page number 0, 32, 64,
96, 128, 160, 192 and 224. It only reads the first 1024