开云体育

ctrl + shift + ? for shortcuts
© 2025 开云体育

[PATCH 2/3] designware: Use the remove() method with related drivers

Simon Glass
 

Several drivers make use of the designware Ethernet driver but do not
implement the remove() method. Add this so that DMA is stopped when the
OS is booted to avoid memory corruption, etc.

Signed-off-by: Simon Glass <sjg@...>
Reported-by: Christian Kohlschütter <christian@...>
---

drivers/net/designware.c | 2 +-
drivers/net/designware.h | 12 ++++++++++++
drivers/net/dwmac_meson8b.c | 6 ++++++
drivers/net/dwmac_s700.c | 6 ++++++
drivers/net/dwmac_socfpga.c | 6 ++++++
5 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index eebf14bd51a..d4d31925fca 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -805,7 +805,7 @@ clk_err:
return err;
}

-static int designware_eth_remove(struct udevice *dev)
+int designware_eth_remove(struct udevice *dev)
{
struct dw_eth_dev *priv = dev_get_priv(dev);

diff --git a/drivers/net/designware.h b/drivers/net/designware.h
index e47101ccaf6..8c9d0190e03 100644
--- a/drivers/net/designware.h
+++ b/drivers/net/designware.h
@@ -247,6 +247,18 @@ struct dw_eth_dev {

int designware_eth_of_to_plat(struct udevice *dev);
int designware_eth_probe(struct udevice *dev);
+
+/**
+ * designware_eth_remove() - Remove the device
+ *
+ * Disables DMA and marks the device as remove. This must be called before
+ * booting an OS, to ensure that DMA is inactive.
+ *
+ * @dev: Device to remove
+ * Return 0 if OK, -ve on error
+ */
+int designware_eth_remove(struct udevice *dev);
+
extern const struct eth_ops designware_eth_ops;

struct dw_eth_pdata {
diff --git a/drivers/net/dwmac_meson8b.c b/drivers/net/dwmac_meson8b.c
index fde4aabbace..b2152f8da9b 100644
--- a/drivers/net/dwmac_meson8b.c
+++ b/drivers/net/dwmac_meson8b.c
@@ -145,6 +145,11 @@ static int dwmac_meson8b_probe(struct udevice *dev)
return designware_eth_probe(dev);
}

+static int dwmac_meson8b_remove(struct udevice *dev)
+{
+ return designware_eth_remove(dev);
+}
+
static const struct udevice_id dwmac_meson8b_ids[] = {
{ .compatible = "amlogic,meson-gxbb-dwmac", .data = (ulong)dwmac_setup_gx },
{ .compatible = "amlogic,meson-g12a-dwmac", .data = (ulong)dwmac_setup_axg },
@@ -158,6 +163,7 @@ U_BOOT_DRIVER(dwmac_meson8b) = {
.of_match = dwmac_meson8b_ids,
.of_to_plat = dwmac_meson8b_of_to_plat,
.probe = dwmac_meson8b_probe,
+ .remove = dwmac_meson8b_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_meson8b_plat),
diff --git a/drivers/net/dwmac_s700.c b/drivers/net/dwmac_s700.c
index 969d247b4f3..cfb37c3aa71 100644
--- a/drivers/net/dwmac_s700.c
+++ b/drivers/net/dwmac_s700.c
@@ -44,6 +44,11 @@ static int dwmac_s700_probe(struct udevice *dev)
return designware_eth_probe(dev);
}

+static int dwmac_s700_remove(struct udevice *dev)
+{
+ return designware_eth_remove(dev);
+}
+
static int dwmac_s700_of_to_plat(struct udevice *dev)
{
return designware_eth_of_to_plat(dev);
@@ -60,6 +65,7 @@ U_BOOT_DRIVER(dwmac_s700) = {
.of_match = dwmac_s700_ids,
.of_to_plat = dwmac_s700_of_to_plat,
.probe = dwmac_s700_probe,
+ .remove = dwmac_s700_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct eth_pdata),
diff --git a/drivers/net/dwmac_socfpga.c b/drivers/net/dwmac_socfpga.c
index a9e2d8c0972..8e7a9975d28 100644
--- a/drivers/net/dwmac_socfpga.c
+++ b/drivers/net/dwmac_socfpga.c
@@ -130,6 +130,11 @@ static int dwmac_socfpga_probe(struct udevice *dev)
return designware_eth_probe(dev);
}

+static int dwmac_socfpga_remove(struct udevice *dev)
+{
+ return designware_eth_remove(dev);
+}
+
static const struct udevice_id dwmac_socfpga_ids[] = {
{ .compatible = "altr,socfpga-stmmac" },
{ }
@@ -141,6 +146,7 @@ U_BOOT_DRIVER(dwmac_socfpga) = {
.of_match = dwmac_socfpga_ids,
.of_to_plat = dwmac_socfpga_of_to_plat,
.probe = dwmac_socfpga_probe,
+ .remove = dwmac_socfpga_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_socfpga_plat),
--
2.43.0

base-commit: bcfa94be955ef263d37918ef4b8f898ee8964c61
branch: eth


[PATCH v2 2/4] net: designware: Mark drivers as having active DMA

Simon Glass
 

Network devices which use this driver have DMA running while receiving
packages. Add the required flag to driver model so that they will be
removed before starting the OS.

Signed-off-by: Simon Glass <sjg@...>
---

Changes in v2:
- Add a new patch to mark drivers as having active DMA

drivers/net/designware.c | 2 +-
drivers/net/dwmac_meson8b.c | 2 +-
drivers/net/dwmac_s700.c | 2 +-
drivers/net/dwmac_socfpga.c | 2 +-
drivers/net/gmac_rockchip.c | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index 5385849ea6b..9b2b0a5d602 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -885,7 +885,7 @@ U_BOOT_DRIVER(eth_designware) = {
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dw_eth_pdata),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
+ .flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_ACTIVE_DMA,
};

static struct pci_device_id supported[] = {
diff --git a/drivers/net/dwmac_meson8b.c b/drivers/net/dwmac_meson8b.c
index bf94e50842d..95a99a98e99 100644
--- a/drivers/net/dwmac_meson8b.c
+++ b/drivers/net/dwmac_meson8b.c
@@ -162,5 +162,5 @@ U_BOOT_DRIVER(dwmac_meson8b) = {
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_meson8b_plat),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
+ .flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_ACTIVE_DMA,
};
diff --git a/drivers/net/dwmac_s700.c b/drivers/net/dwmac_s700.c
index e78193f3376..21df8e3ce81 100644
--- a/drivers/net/dwmac_s700.c
+++ b/drivers/net/dwmac_s700.c
@@ -64,5 +64,5 @@ U_BOOT_DRIVER(dwmac_s700) = {
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct eth_pdata),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
+ .flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_ACTIVE_DMA,
};
diff --git a/drivers/net/dwmac_socfpga.c b/drivers/net/dwmac_socfpga.c
index dda33f90d65..9f262aaee60 100644
--- a/drivers/net/dwmac_socfpga.c
+++ b/drivers/net/dwmac_socfpga.c
@@ -145,5 +145,5 @@ U_BOOT_DRIVER(dwmac_socfpga) = {
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_socfpga_plat),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
+ .flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_ACTIVE_DMA,
};
diff --git a/drivers/net/gmac_rockchip.c b/drivers/net/gmac_rockchip.c
index 5c542d62bd8..52be3a5687f 100644
--- a/drivers/net/gmac_rockchip.c
+++ b/drivers/net/gmac_rockchip.c
@@ -760,5 +760,5 @@ U_BOOT_DRIVER(eth_gmac_rockchip) = {
.ops = &gmac_rockchip_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct gmac_rockchip_plat),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
+ .flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_ACTIVE_DMA,
};
--
2.43.0


Re: [PATCH 2/3] designware: Use the remove() method with related drivers

Jonas Karlman
 

Hi Simon,

On 2025-04-06 23:10, Simon Glass wrote:
Hi Jonas,

On Sun, 6 Apr 2025 at 21:02, Jonas Karlman <jonas@...> wrote:

Hi Simon,

On 2025-04-06 00:12, Simon Glass wrote:
Several drivers make use of the designware Ethernet driver but do not
implement the remove() method. Add this so that DMA is stopped when the
OS is booted to avoid memory corruption, etc.
The designware Ethernet driver core should not need the remove() ops as
the eth_ops.stop() ops already should stop DMA. And the eth-uclass
pre_remove() ops already call the eth_ops.stop() ops.

All these drivers seem to use designware_eth_ops and thus have correct
stop() ops configured and should really not need the remove() ops.
That's interesting. I wrote that code about 9 years ago so perhaps can
be forgiven for forgetting.

Very few drivers set the DM_FLAG_ACTIVE_DMA flag, certainly not the
designware ones. So how would they be removed?
Ahh, I see, remove() is only called for devices with ACTIVE_DMA or
OS_PREPARE. So I guess that was probably the issue all along?

There is some confusion that dm_remove_devices_active() does not remove
device_active()=true devices and instead only remove ACTIVE_DMA and/or
OS_PREPARE flagged devices.


Side note, this also misses the gmac rockchip driver that uses its own
eth_ops instead of designware_eth_ops, however it also use same stop()
ops as all these drivers.
Hmm yes I saw that but then apparently forgot to include it.

I can perhaps redo this patch to add the DMA flag.
Maybe eth_post_probe() or similar should add the OS_PREPARE or
ACTIVE_DMA flag for all eth-uclass, to ensure remove() and thus stop()
gets called when dm_remove_devices_active() is called.

There is probably no need to add all these remove() ops, as they would
still not have been called by dm_remove_devices_active().

Regards,
Jonas




Signed-off-by: Simon Glass <sjg@...>
Reported-by: Christian Kohlschütter <christian@...>
---

drivers/net/designware.c | 2 +-
drivers/net/designware.h | 12 ++++++++++++
drivers/net/dwmac_meson8b.c | 6 ++++++
drivers/net/dwmac_s700.c | 6 ++++++
drivers/net/dwmac_socfpga.c | 6 ++++++
5 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index eebf14bd51a..d4d31925fca 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -805,7 +805,7 @@ clk_err:
return err;
}

-static int designware_eth_remove(struct udevice *dev)
+int designware_eth_remove(struct udevice *dev)
{
struct dw_eth_dev *priv = dev_get_priv(dev);

diff --git a/drivers/net/designware.h b/drivers/net/designware.h
index e47101ccaf6..8c9d0190e03 100644
--- a/drivers/net/designware.h
+++ b/drivers/net/designware.h
@@ -247,6 +247,18 @@ struct dw_eth_dev {

int designware_eth_of_to_plat(struct udevice *dev);
int designware_eth_probe(struct udevice *dev);
+
+/**
+ * designware_eth_remove() - Remove the device
+ *
+ * Disables DMA and marks the device as remove. This must be called before
+ * booting an OS, to ensure that DMA is inactive.
+ *
+ * @dev: Device to remove
+ * Return 0 if OK, -ve on error
+ */
+int designware_eth_remove(struct udevice *dev);
+
extern const struct eth_ops designware_eth_ops;

struct dw_eth_pdata {
diff --git a/drivers/net/dwmac_meson8b.c b/drivers/net/dwmac_meson8b.c
index fde4aabbace..b2152f8da9b 100644
--- a/drivers/net/dwmac_meson8b.c
+++ b/drivers/net/dwmac_meson8b.c
@@ -145,6 +145,11 @@ static int dwmac_meson8b_probe(struct udevice *dev)
return designware_eth_probe(dev);
}

+static int dwmac_meson8b_remove(struct udevice *dev)
+{
+ return designware_eth_remove(dev);
+}
+
static const struct udevice_id dwmac_meson8b_ids[] = {
{ .compatible = "amlogic,meson-gxbb-dwmac", .data = (ulong)dwmac_setup_gx },
{ .compatible = "amlogic,meson-g12a-dwmac", .data = (ulong)dwmac_setup_axg },
@@ -158,6 +163,7 @@ U_BOOT_DRIVER(dwmac_meson8b) = {
.of_match = dwmac_meson8b_ids,
.of_to_plat = dwmac_meson8b_of_to_plat,
.probe = dwmac_meson8b_probe,
+ .remove = dwmac_meson8b_remove,
There is no reason to add the dwmac_meson8b_remove function above, you
could just use designware_eth_remove directly here.

Same for remaining drivers below.
Yes, I wasn't sure which way to go on that. I'll remove them.


Regards,
Jonas

.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_meson8b_plat),
diff --git a/drivers/net/dwmac_s700.c b/drivers/net/dwmac_s700.c
index 969d247b4f3..cfb37c3aa71 100644
--- a/drivers/net/dwmac_s700.c
+++ b/drivers/net/dwmac_s700.c
@@ -44,6 +44,11 @@ static int dwmac_s700_probe(struct udevice *dev)
return designware_eth_probe(dev);
}

+static int dwmac_s700_remove(struct udevice *dev)
+{
+ return designware_eth_remove(dev);
+}
+
static int dwmac_s700_of_to_plat(struct udevice *dev)
{
return designware_eth_of_to_plat(dev);
@@ -60,6 +65,7 @@ U_BOOT_DRIVER(dwmac_s700) = {
.of_match = dwmac_s700_ids,
.of_to_plat = dwmac_s700_of_to_plat,
.probe = dwmac_s700_probe,
+ .remove = dwmac_s700_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct eth_pdata),
diff --git a/drivers/net/dwmac_socfpga.c b/drivers/net/dwmac_socfpga.c
index a9e2d8c0972..8e7a9975d28 100644
--- a/drivers/net/dwmac_socfpga.c
+++ b/drivers/net/dwmac_socfpga.c
@@ -130,6 +130,11 @@ static int dwmac_socfpga_probe(struct udevice *dev)
return designware_eth_probe(dev);
}

+static int dwmac_socfpga_remove(struct udevice *dev)
+{
+ return designware_eth_remove(dev);
+}
+
static const struct udevice_id dwmac_socfpga_ids[] = {
{ .compatible = "altr,socfpga-stmmac" },
{ }
@@ -141,6 +146,7 @@ U_BOOT_DRIVER(dwmac_socfpga) = {
.of_match = dwmac_socfpga_ids,
.of_to_plat = dwmac_socfpga_of_to_plat,
.probe = dwmac_socfpga_probe,
+ .remove = dwmac_socfpga_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_socfpga_plat),
Regards,
Simon


Re: [PATCH 2/3] designware: Use the remove() method with related drivers

Jonas Karlman
 

Hi Simon,

On 2025-04-06 00:12, Simon Glass wrote:
Several drivers make use of the designware Ethernet driver but do not
implement the remove() method. Add this so that DMA is stopped when the
OS is booted to avoid memory corruption, etc.
The designware Ethernet driver core should not need the remove() ops as
the eth_ops.stop() ops already should stop DMA. And the eth-uclass
pre_remove() ops already call the eth_ops.stop() ops.

All these drivers seem to use designware_eth_ops and thus have correct
stop() ops configured and should really not need the remove() ops.

Side note, this also misses the gmac rockchip driver that uses its own
eth_ops instead of designware_eth_ops, however it also use same stop()
ops as all these drivers.


Signed-off-by: Simon Glass <sjg@...>
Reported-by: Christian Kohlschütter <christian@...>
---

drivers/net/designware.c | 2 +-
drivers/net/designware.h | 12 ++++++++++++
drivers/net/dwmac_meson8b.c | 6 ++++++
drivers/net/dwmac_s700.c | 6 ++++++
drivers/net/dwmac_socfpga.c | 6 ++++++
5 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index eebf14bd51a..d4d31925fca 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -805,7 +805,7 @@ clk_err:
return err;
}

-static int designware_eth_remove(struct udevice *dev)
+int designware_eth_remove(struct udevice *dev)
{
struct dw_eth_dev *priv = dev_get_priv(dev);

diff --git a/drivers/net/designware.h b/drivers/net/designware.h
index e47101ccaf6..8c9d0190e03 100644
--- a/drivers/net/designware.h
+++ b/drivers/net/designware.h
@@ -247,6 +247,18 @@ struct dw_eth_dev {

int designware_eth_of_to_plat(struct udevice *dev);
int designware_eth_probe(struct udevice *dev);
+
+/**
+ * designware_eth_remove() - Remove the device
+ *
+ * Disables DMA and marks the device as remove. This must be called before
+ * booting an OS, to ensure that DMA is inactive.
+ *
+ * @dev: Device to remove
+ * Return 0 if OK, -ve on error
+ */
+int designware_eth_remove(struct udevice *dev);
+
extern const struct eth_ops designware_eth_ops;

struct dw_eth_pdata {
diff --git a/drivers/net/dwmac_meson8b.c b/drivers/net/dwmac_meson8b.c
index fde4aabbace..b2152f8da9b 100644
--- a/drivers/net/dwmac_meson8b.c
+++ b/drivers/net/dwmac_meson8b.c
@@ -145,6 +145,11 @@ static int dwmac_meson8b_probe(struct udevice *dev)
return designware_eth_probe(dev);
}

+static int dwmac_meson8b_remove(struct udevice *dev)
+{
+ return designware_eth_remove(dev);
+}
+
static const struct udevice_id dwmac_meson8b_ids[] = {
{ .compatible = "amlogic,meson-gxbb-dwmac", .data = (ulong)dwmac_setup_gx },
{ .compatible = "amlogic,meson-g12a-dwmac", .data = (ulong)dwmac_setup_axg },
@@ -158,6 +163,7 @@ U_BOOT_DRIVER(dwmac_meson8b) = {
.of_match = dwmac_meson8b_ids,
.of_to_plat = dwmac_meson8b_of_to_plat,
.probe = dwmac_meson8b_probe,
+ .remove = dwmac_meson8b_remove,
There is no reason to add the dwmac_meson8b_remove function above, you
could just use designware_eth_remove directly here.

Same for remaining drivers below.

Regards,
Jonas

.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_meson8b_plat),
diff --git a/drivers/net/dwmac_s700.c b/drivers/net/dwmac_s700.c
index 969d247b4f3..cfb37c3aa71 100644
--- a/drivers/net/dwmac_s700.c
+++ b/drivers/net/dwmac_s700.c
@@ -44,6 +44,11 @@ static int dwmac_s700_probe(struct udevice *dev)
return designware_eth_probe(dev);
}

+static int dwmac_s700_remove(struct udevice *dev)
+{
+ return designware_eth_remove(dev);
+}
+
static int dwmac_s700_of_to_plat(struct udevice *dev)
{
return designware_eth_of_to_plat(dev);
@@ -60,6 +65,7 @@ U_BOOT_DRIVER(dwmac_s700) = {
.of_match = dwmac_s700_ids,
.of_to_plat = dwmac_s700_of_to_plat,
.probe = dwmac_s700_probe,
+ .remove = dwmac_s700_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct eth_pdata),
diff --git a/drivers/net/dwmac_socfpga.c b/drivers/net/dwmac_socfpga.c
index a9e2d8c0972..8e7a9975d28 100644
--- a/drivers/net/dwmac_socfpga.c
+++ b/drivers/net/dwmac_socfpga.c
@@ -130,6 +130,11 @@ static int dwmac_socfpga_probe(struct udevice *dev)
return designware_eth_probe(dev);
}

+static int dwmac_socfpga_remove(struct udevice *dev)
+{
+ return designware_eth_remove(dev);
+}
+
static const struct udevice_id dwmac_socfpga_ids[] = {
{ .compatible = "altr,socfpga-stmmac" },
{ }
@@ -141,6 +146,7 @@ U_BOOT_DRIVER(dwmac_socfpga) = {
.of_match = dwmac_socfpga_ids,
.of_to_plat = dwmac_socfpga_of_to_plat,
.probe = dwmac_socfpga_probe,
+ .remove = dwmac_socfpga_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_socfpga_plat),


Re: [PATCH 2/3] designware: Use the remove() method with related drivers

Simon Glass
 

Hi Jonas,

On Mon, 7 Apr 2025 at 09:36, Jonas Karlman <jonas@...> wrote:

Hi Simon,

On 2025-04-06 23:10, Simon Glass wrote:
Hi Jonas,

On Sun, 6 Apr 2025 at 21:02, Jonas Karlman <jonas@...> wrote:

Hi Simon,

On 2025-04-06 00:12, Simon Glass wrote:
Several drivers make use of the designware Ethernet driver but do not
implement the remove() method. Add this so that DMA is stopped when the
OS is booted to avoid memory corruption, etc.
The designware Ethernet driver core should not need the remove() ops as
the eth_ops.stop() ops already should stop DMA. And the eth-uclass
pre_remove() ops already call the eth_ops.stop() ops.

All these drivers seem to use designware_eth_ops and thus have correct
stop() ops configured and should really not need the remove() ops.
That's interesting. I wrote that code about 9 years ago so perhaps can
be forgiven for forgetting.

Very few drivers set the DM_FLAG_ACTIVE_DMA flag, certainly not the
designware ones. So how would they be removed?
Ahh, I see, remove() is only called for devices with ACTIVE_DMA or
OS_PREPARE. So I guess that was probably the issue all along?
I suppose so, yes.


There is some confusion that dm_remove_devices_active() does not remove
device_active()=true devices and instead only remove ACTIVE_DMA and/or
OS_PREPARE flagged devices.


Side note, this also misses the gmac rockchip driver that uses its own
eth_ops instead of designware_eth_ops, however it also use same stop()
ops as all these drivers.
Hmm yes I saw that but then apparently forgot to include it.

I can perhaps redo this patch to add the DMA flag.
Maybe eth_post_probe() or similar should add the OS_PREPARE or
ACTIVE_DMA flag for all eth-uclass, to ensure remove() and thus stop()
gets called when dm_remove_devices_active() is called.
But not all network drivers use DMA. It should really be set by the driver.


There is probably no need to add all these remove() ops, as they would
still not have been called by dm_remove_devices_active().
Well, since we can rely on the eth uclass, that is correct, but I
still think it is better to add it, as an example to other drivers.

Regards,
Simon


Re: [GIT PULL] Please pull u-boot-amlogic-20250404 into master

Tom Rini
 

On Fri, 04 Apr 2025 10:40:31 +0200, Neil Armstrong wrote:

Here's a last minute fix for the libre-computer boards fixing the capsule update feature.

Thanks,
Neil

The following changes since commit 82b69fc4224432d5aefa7ca750d950374cbc7fb2:

Merge tag 'efi-2025-04-rc6' of (2025-04-03 07:31:28 -0600)

[...]
Merged into u-boot/master, thanks!

--
Tom


[PATCH v2 1/4] designware: Use the remove() method with related drivers

Simon Glass
 

Several drivers make use of the designware Ethernet driver but do not
implement the remove() method. Add this to keep things tidy.

Note that the Ethernet uclass calls each driver's stop() method on
removal, so there is no need to do that here.

Signed-off-by: Simon Glass <sjg@...>
Reported-by: Christian Kohlschütter <christian@...>
---

Changes in v2:
- Use the designware_eth_remove() method directly
- Add gmac_rockchip too
- Reword the commit message to mention the uclass' actions

drivers/net/designware.c | 2 +-
drivers/net/designware.h | 12 ++++++++++++
drivers/net/dwmac_meson8b.c | 1 +
drivers/net/dwmac_s700.c | 1 +
drivers/net/dwmac_socfpga.c | 1 +
drivers/net/gmac_rockchip.c | 1 +
6 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index 07b0f49ef58..5385849ea6b 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -805,7 +805,7 @@ clk_err:
return err;
}

-static int designware_eth_remove(struct udevice *dev)
+int designware_eth_remove(struct udevice *dev)
{
struct dw_eth_dev *priv = dev_get_priv(dev);

diff --git a/drivers/net/designware.h b/drivers/net/designware.h
index e47101ccaf6..8c9d0190e03 100644
--- a/drivers/net/designware.h
+++ b/drivers/net/designware.h
@@ -247,6 +247,18 @@ struct dw_eth_dev {

int designware_eth_of_to_plat(struct udevice *dev);
int designware_eth_probe(struct udevice *dev);
+
+/**
+ * designware_eth_remove() - Remove the device
+ *
+ * Disables DMA and marks the device as remove. This must be called before
+ * booting an OS, to ensure that DMA is inactive.
+ *
+ * @dev: Device to remove
+ * Return 0 if OK, -ve on error
+ */
+int designware_eth_remove(struct udevice *dev);
+
extern const struct eth_ops designware_eth_ops;

struct dw_eth_pdata {
diff --git a/drivers/net/dwmac_meson8b.c b/drivers/net/dwmac_meson8b.c
index fde4aabbace..bf94e50842d 100644
--- a/drivers/net/dwmac_meson8b.c
+++ b/drivers/net/dwmac_meson8b.c
@@ -158,6 +158,7 @@ U_BOOT_DRIVER(dwmac_meson8b) = {
.of_match = dwmac_meson8b_ids,
.of_to_plat = dwmac_meson8b_of_to_plat,
.probe = dwmac_meson8b_probe,
+ .remove = designware_eth_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_meson8b_plat),
diff --git a/drivers/net/dwmac_s700.c b/drivers/net/dwmac_s700.c
index 969d247b4f3..e78193f3376 100644
--- a/drivers/net/dwmac_s700.c
+++ b/drivers/net/dwmac_s700.c
@@ -60,6 +60,7 @@ U_BOOT_DRIVER(dwmac_s700) = {
.of_match = dwmac_s700_ids,
.of_to_plat = dwmac_s700_of_to_plat,
.probe = dwmac_s700_probe,
+ .remove = designware_eth_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct eth_pdata),
diff --git a/drivers/net/dwmac_socfpga.c b/drivers/net/dwmac_socfpga.c
index a9e2d8c0972..dda33f90d65 100644
--- a/drivers/net/dwmac_socfpga.c
+++ b/drivers/net/dwmac_socfpga.c
@@ -141,6 +141,7 @@ U_BOOT_DRIVER(dwmac_socfpga) = {
.of_match = dwmac_socfpga_ids,
.of_to_plat = dwmac_socfpga_of_to_plat,
.probe = dwmac_socfpga_probe,
+ .remove = designware_eth_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_socfpga_plat),
diff --git a/drivers/net/gmac_rockchip.c b/drivers/net/gmac_rockchip.c
index 8cfeeffe95b..5c542d62bd8 100644
--- a/drivers/net/gmac_rockchip.c
+++ b/drivers/net/gmac_rockchip.c
@@ -756,6 +756,7 @@ U_BOOT_DRIVER(eth_gmac_rockchip) = {
.of_match = rockchip_gmac_ids,
.of_to_plat = gmac_rockchip_of_to_plat,
.probe = gmac_rockchip_probe,
+ .remove = designware_eth_remove,
.ops = &gmac_rockchip_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct gmac_rockchip_plat),
--
2.43.0


[PATCH v2 0/4] dm: net: Assorted patches related to networking and DMA

Simon Glass
 

This series collects together a few patches related to U-Boot leaving
DMA running when booting.

Changes in v2:
- Use the designware_eth_remove() method directly
- Add gmac_rockchip too
- Reword the commit message to mention the uclass' actions
- Add a new patch to mark drivers as having active DMA

Simon Glass (4):
designware: Use the remove() method with related drivers
net: designware: Mark drivers as having active DMA
efi_loader: Move device-removal later in exit-boot-services
dm: core: Rename dm_remove_devices_active()

arch/arm/lib/bootm.c | 4 ++--
arch/riscv/lib/bootm.c | 2 +-
arch/x86/lib/bootm.c | 2 +-
drivers/core/root.c | 2 +-
drivers/net/designware.c | 4 ++--
drivers/net/designware.h | 12 ++++++++++++
drivers/net/dwmac_meson8b.c | 3 ++-
drivers/net/dwmac_s700.c | 3 ++-
drivers/net/dwmac_socfpga.c | 3 ++-
drivers/net/gmac_rockchip.c | 3 ++-
include/dm/root.h | 6 +++---
lib/efi_loader/efi_boottime.c | 21 +++++++++++++--------
test/dm/core.c | 2 +-
13 files changed, 44 insertions(+), 23 deletions(-)

--
2.43.0

base-commit: d47b5693e21f78d8cd8caa8fcd9e64fd6ae85759
branch: eth2


Re: [PATCH 2/3] designware: Use the remove() method with related drivers

Simon Glass
 

Hi Jonas,

On Sun, 6 Apr 2025 at 21:02, Jonas Karlman <jonas@...> wrote:

Hi Simon,

On 2025-04-06 00:12, Simon Glass wrote:
Several drivers make use of the designware Ethernet driver but do not
implement the remove() method. Add this so that DMA is stopped when the
OS is booted to avoid memory corruption, etc.
The designware Ethernet driver core should not need the remove() ops as
the eth_ops.stop() ops already should stop DMA. And the eth-uclass
pre_remove() ops already call the eth_ops.stop() ops.

All these drivers seem to use designware_eth_ops and thus have correct
stop() ops configured and should really not need the remove() ops.
That's interesting. I wrote that code about 9 years ago so perhaps can
be forgiven for forgetting.

Very few drivers set the DM_FLAG_ACTIVE_DMA flag, certainly not the
designware ones. So how would they be removed?

Side note, this also misses the gmac rockchip driver that uses its own
eth_ops instead of designware_eth_ops, however it also use same stop()
ops as all these drivers.
Hmm yes I saw that but then apparently forgot to include it.

I can perhaps redo this patch to add the DMA flag.



Signed-off-by: Simon Glass <sjg@...>
Reported-by: Christian Kohlschütter <christian@...>
---

drivers/net/designware.c | 2 +-
drivers/net/designware.h | 12 ++++++++++++
drivers/net/dwmac_meson8b.c | 6 ++++++
drivers/net/dwmac_s700.c | 6 ++++++
drivers/net/dwmac_socfpga.c | 6 ++++++
5 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index eebf14bd51a..d4d31925fca 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -805,7 +805,7 @@ clk_err:
return err;
}

-static int designware_eth_remove(struct udevice *dev)
+int designware_eth_remove(struct udevice *dev)
{
struct dw_eth_dev *priv = dev_get_priv(dev);

diff --git a/drivers/net/designware.h b/drivers/net/designware.h
index e47101ccaf6..8c9d0190e03 100644
--- a/drivers/net/designware.h
+++ b/drivers/net/designware.h
@@ -247,6 +247,18 @@ struct dw_eth_dev {

int designware_eth_of_to_plat(struct udevice *dev);
int designware_eth_probe(struct udevice *dev);
+
+/**
+ * designware_eth_remove() - Remove the device
+ *
+ * Disables DMA and marks the device as remove. This must be called before
+ * booting an OS, to ensure that DMA is inactive.
+ *
+ * @dev: Device to remove
+ * Return 0 if OK, -ve on error
+ */
+int designware_eth_remove(struct udevice *dev);
+
extern const struct eth_ops designware_eth_ops;

struct dw_eth_pdata {
diff --git a/drivers/net/dwmac_meson8b.c b/drivers/net/dwmac_meson8b.c
index fde4aabbace..b2152f8da9b 100644
--- a/drivers/net/dwmac_meson8b.c
+++ b/drivers/net/dwmac_meson8b.c
@@ -145,6 +145,11 @@ static int dwmac_meson8b_probe(struct udevice *dev)
return designware_eth_probe(dev);
}

+static int dwmac_meson8b_remove(struct udevice *dev)
+{
+ return designware_eth_remove(dev);
+}
+
static const struct udevice_id dwmac_meson8b_ids[] = {
{ .compatible = "amlogic,meson-gxbb-dwmac", .data = (ulong)dwmac_setup_gx },
{ .compatible = "amlogic,meson-g12a-dwmac", .data = (ulong)dwmac_setup_axg },
@@ -158,6 +163,7 @@ U_BOOT_DRIVER(dwmac_meson8b) = {
.of_match = dwmac_meson8b_ids,
.of_to_plat = dwmac_meson8b_of_to_plat,
.probe = dwmac_meson8b_probe,
+ .remove = dwmac_meson8b_remove,
There is no reason to add the dwmac_meson8b_remove function above, you
could just use designware_eth_remove directly here.

Same for remaining drivers below.
Yes, I wasn't sure which way to go on that. I'll remove them.


Regards,
Jonas

.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_meson8b_plat),
diff --git a/drivers/net/dwmac_s700.c b/drivers/net/dwmac_s700.c
index 969d247b4f3..cfb37c3aa71 100644
--- a/drivers/net/dwmac_s700.c
+++ b/drivers/net/dwmac_s700.c
@@ -44,6 +44,11 @@ static int dwmac_s700_probe(struct udevice *dev)
return designware_eth_probe(dev);
}

+static int dwmac_s700_remove(struct udevice *dev)
+{
+ return designware_eth_remove(dev);
+}
+
static int dwmac_s700_of_to_plat(struct udevice *dev)
{
return designware_eth_of_to_plat(dev);
@@ -60,6 +65,7 @@ U_BOOT_DRIVER(dwmac_s700) = {
.of_match = dwmac_s700_ids,
.of_to_plat = dwmac_s700_of_to_plat,
.probe = dwmac_s700_probe,
+ .remove = dwmac_s700_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct eth_pdata),
diff --git a/drivers/net/dwmac_socfpga.c b/drivers/net/dwmac_socfpga.c
index a9e2d8c0972..8e7a9975d28 100644
--- a/drivers/net/dwmac_socfpga.c
+++ b/drivers/net/dwmac_socfpga.c
@@ -130,6 +130,11 @@ static int dwmac_socfpga_probe(struct udevice *dev)
return designware_eth_probe(dev);
}

+static int dwmac_socfpga_remove(struct udevice *dev)
+{
+ return designware_eth_remove(dev);
+}
+
static const struct udevice_id dwmac_socfpga_ids[] = {
{ .compatible = "altr,socfpga-stmmac" },
{ }
@@ -141,6 +146,7 @@ U_BOOT_DRIVER(dwmac_socfpga) = {
.of_match = dwmac_socfpga_ids,
.of_to_plat = dwmac_socfpga_of_to_plat,
.probe = dwmac_socfpga_probe,
+ .remove = dwmac_socfpga_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_socfpga_plat),
Regards,
Simon


Re: [PATCH v2 2/4] net: designware: Mark drivers as having active DMA

 

On 07/04/2025 03:35, Simon Glass wrote:
Network devices which use this driver have DMA running while receiving
packages. Add the required flag to driver model so that they will be
removed before starting the OS.
Signed-off-by: Simon Glass <sjg@...>
---
Changes in v2:
- Add a new patch to mark drivers as having active DMA
drivers/net/designware.c | 2 +-
drivers/net/dwmac_meson8b.c | 2 +-
drivers/net/dwmac_s700.c | 2 +-
drivers/net/dwmac_socfpga.c | 2 +-
drivers/net/gmac_rockchip.c | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index 5385849ea6b..9b2b0a5d602 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -885,7 +885,7 @@ U_BOOT_DRIVER(eth_designware) = {
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dw_eth_pdata),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
+ .flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_ACTIVE_DMA,
};
static struct pci_device_id supported[] = {
diff --git a/drivers/net/dwmac_meson8b.c b/drivers/net/dwmac_meson8b.c
index bf94e50842d..95a99a98e99 100644
--- a/drivers/net/dwmac_meson8b.c
+++ b/drivers/net/dwmac_meson8b.c
@@ -162,5 +162,5 @@ U_BOOT_DRIVER(dwmac_meson8b) = {
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_meson8b_plat),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
+ .flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_ACTIVE_DMA,
};
diff --git a/drivers/net/dwmac_s700.c b/drivers/net/dwmac_s700.c
index e78193f3376..21df8e3ce81 100644
--- a/drivers/net/dwmac_s700.c
+++ b/drivers/net/dwmac_s700.c
@@ -64,5 +64,5 @@ U_BOOT_DRIVER(dwmac_s700) = {
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct eth_pdata),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
+ .flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_ACTIVE_DMA,
};
diff --git a/drivers/net/dwmac_socfpga.c b/drivers/net/dwmac_socfpga.c
index dda33f90d65..9f262aaee60 100644
--- a/drivers/net/dwmac_socfpga.c
+++ b/drivers/net/dwmac_socfpga.c
@@ -145,5 +145,5 @@ U_BOOT_DRIVER(dwmac_socfpga) = {
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_socfpga_plat),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
+ .flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_ACTIVE_DMA,
};
diff --git a/drivers/net/gmac_rockchip.c b/drivers/net/gmac_rockchip.c
index 5c542d62bd8..52be3a5687f 100644
--- a/drivers/net/gmac_rockchip.c
+++ b/drivers/net/gmac_rockchip.c
@@ -760,5 +760,5 @@ U_BOOT_DRIVER(eth_gmac_rockchip) = {
.ops = &gmac_rockchip_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct gmac_rockchip_plat),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
+ .flags = DM_FLAG_ALLOC_PRIV_DMA | DM_FLAG_ACTIVE_DMA,
};
Reviewed-by: Neil Armstrong <neil.armstrong@...>


Re: [PATCH v2 1/4] designware: Use the remove() method with related drivers

 

On 07/04/2025 03:35, Simon Glass wrote:
Several drivers make use of the designware Ethernet driver but do not
implement the remove() method. Add this to keep things tidy.
Note that the Ethernet uclass calls each driver's stop() method on
removal, so there is no need to do that here.
Signed-off-by: Simon Glass <sjg@...>
Reported-by: Christian Kohlschütter <christian@...>
---
Changes in v2:
- Use the designware_eth_remove() method directly
- Add gmac_rockchip too
- Reword the commit message to mention the uclass' actions
drivers/net/designware.c | 2 +-
drivers/net/designware.h | 12 ++++++++++++
drivers/net/dwmac_meson8b.c | 1 +
drivers/net/dwmac_s700.c | 1 +
drivers/net/dwmac_socfpga.c | 1 +
drivers/net/gmac_rockchip.c | 1 +
6 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index 07b0f49ef58..5385849ea6b 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -805,7 +805,7 @@ clk_err:
return err;
}
-static int designware_eth_remove(struct udevice *dev)
+int designware_eth_remove(struct udevice *dev)
{
struct dw_eth_dev *priv = dev_get_priv(dev);
diff --git a/drivers/net/designware.h b/drivers/net/designware.h
index e47101ccaf6..8c9d0190e03 100644
--- a/drivers/net/designware.h
+++ b/drivers/net/designware.h
@@ -247,6 +247,18 @@ struct dw_eth_dev {
int designware_eth_of_to_plat(struct udevice *dev);
int designware_eth_probe(struct udevice *dev);
+
+/**
+ * designware_eth_remove() - Remove the device
+ *
+ * Disables DMA and marks the device as remove. This must be called before
+ * booting an OS, to ensure that DMA is inactive.
+ *
+ * @dev: Device to remove
+ * Return 0 if OK, -ve on error
+ */
+int designware_eth_remove(struct udevice *dev);
+
extern const struct eth_ops designware_eth_ops;
struct dw_eth_pdata {
diff --git a/drivers/net/dwmac_meson8b.c b/drivers/net/dwmac_meson8b.c
index fde4aabbace..bf94e50842d 100644
--- a/drivers/net/dwmac_meson8b.c
+++ b/drivers/net/dwmac_meson8b.c
@@ -158,6 +158,7 @@ U_BOOT_DRIVER(dwmac_meson8b) = {
.of_match = dwmac_meson8b_ids,
.of_to_plat = dwmac_meson8b_of_to_plat,
.probe = dwmac_meson8b_probe,
+ .remove = designware_eth_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_meson8b_plat),
diff --git a/drivers/net/dwmac_s700.c b/drivers/net/dwmac_s700.c
index 969d247b4f3..e78193f3376 100644
--- a/drivers/net/dwmac_s700.c
+++ b/drivers/net/dwmac_s700.c
@@ -60,6 +60,7 @@ U_BOOT_DRIVER(dwmac_s700) = {
.of_match = dwmac_s700_ids,
.of_to_plat = dwmac_s700_of_to_plat,
.probe = dwmac_s700_probe,
+ .remove = designware_eth_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct eth_pdata),
diff --git a/drivers/net/dwmac_socfpga.c b/drivers/net/dwmac_socfpga.c
index a9e2d8c0972..dda33f90d65 100644
--- a/drivers/net/dwmac_socfpga.c
+++ b/drivers/net/dwmac_socfpga.c
@@ -141,6 +141,7 @@ U_BOOT_DRIVER(dwmac_socfpga) = {
.of_match = dwmac_socfpga_ids,
.of_to_plat = dwmac_socfpga_of_to_plat,
.probe = dwmac_socfpga_probe,
+ .remove = designware_eth_remove,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_socfpga_plat),
diff --git a/drivers/net/gmac_rockchip.c b/drivers/net/gmac_rockchip.c
index 8cfeeffe95b..5c542d62bd8 100644
--- a/drivers/net/gmac_rockchip.c
+++ b/drivers/net/gmac_rockchip.c
@@ -756,6 +756,7 @@ U_BOOT_DRIVER(eth_gmac_rockchip) = {
.of_match = rockchip_gmac_ids,
.of_to_plat = gmac_rockchip_of_to_plat,
.probe = gmac_rockchip_probe,
+ .remove = designware_eth_remove,
.ops = &gmac_rockchip_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct gmac_rockchip_plat),
Reviewed-by: Neil Armstrong <neil.armstrong@...>


[GIT PULL] Please pull u-boot-amlogic-20250404 into master

 

Hi Tom,

Here's a last minute fix for the libre-computer boards fixing the capsule update feature.

Thanks,
Neil

The following changes since commit 82b69fc4224432d5aefa7ca750d950374cbc7fb2:

Merge tag 'efi-2025-04-rc6' of (2025-04-03 07:31:28 -0600)

are available in the Git repository at:

tags/u-boot-amlogic-20250404

for you to fetch changes up to c5fd2c7ac7fa4835339ea09ead2884794314c9d8:

board: libre-computer: check interface before comparing it in set_dfu_alt_info() (2025-04-04 10:37:40 +0200)

----------------------------------------------------------------
- check interface before comparing it in set_dfu_alt_info() to avoid hard crash on capsule update

----------------------------------------------------------------
Neil Armstrong (1):
board: libre-computer: check interface before comparing it in set_dfu_alt_info()

board/libre-computer/aml-a311d-cc/aml-a311d-cc.c | 2 +-
board/libre-computer/aml-s805x-ac/aml-s805x-ac.c | 2 +-
board/libre-computer/aml-s905d3-cc/aml-s905d3-cc.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)


Re: [PATCH] configs: meson64: move DFU step at the end to give the board a chance to boot something on storage

 

On Wed Apr 2, 2025 at 2:02 PM UTC, Neil Armstrong via groups.io wrote:
The DFU was set to run at the beginning, but we may want to
boot something over USB, MMC or Ethernet even if booted over
USB, so move the DFU as the final fallback.

This keeps the current Amlogic U-Boot CI working and makes
the default config more generic.

Signed-off-by: Neil Armstrong <neil.armstrong@...>
Acked-by: Ferass El Hafidi <funderscore@...>

Thanks!

---
include/configs/meson64.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/configs/meson64.h b/include/configs/meson64.h
index f3275b37a516da6befd41324ddb2a3fe1f36fc83..675df623311f361f3a55971c3894ffbe1d07fee6 100644
--- a/include/configs/meson64.h
+++ b/include/configs/meson64.h
@@ -118,13 +118,13 @@
#ifndef BOOT_TARGET_DEVICES
#define BOOT_TARGET_DEVICES(func) \
func(ROMUSB, romusb, na) \
- func(USB_DFU, usbdfu, na) \
BOOT_TARGET_MMC(func) \
BOOT_TARGET_DEVICES_USB(func) \
BOOT_TARGET_NVME(func) \
BOOT_TARGET_SCSI(func) \
BOOT_TARGET_PXE(func) \
- BOOT_TARGET_DHCP(func)
+ BOOT_TARGET_DHCP(func) \
+ func(USB_DFU, usbdfu, na)
#endif

#define BOOTM_SIZE __stringify(0x1700000)

---
base-commit: c17f03a7e93dfbbe5d32f5738274187065d9493f
change-id: 20250402-u-boot-meson64-move-dfu-end-eb9f5fe55608

Best regards,


[PATCH] configs: meson64: move DFU step at the end to give the board a chance to boot something on storage

 

The DFU was set to run at the beginning, but we may want to
boot something over USB, MMC or Ethernet even if booted over
USB, so move the DFU as the final fallback.

This keeps the current Amlogic U-Boot CI working and makes
the default config more generic.

Signed-off-by: Neil Armstrong <neil.armstrong@...>
---
include/configs/meson64.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/configs/meson64.h b/include/configs/meson64.h
index f3275b37a516da6befd41324ddb2a3fe1f36fc83..675df623311f361f3a55971c3894ffbe1d07fee6 100644
--- a/include/configs/meson64.h
+++ b/include/configs/meson64.h
@@ -118,13 +118,13 @@
#ifndef BOOT_TARGET_DEVICES
#define BOOT_TARGET_DEVICES(func) \
func(ROMUSB, romusb, na) \
- func(USB_DFU, usbdfu, na) \
BOOT_TARGET_MMC(func) \
BOOT_TARGET_DEVICES_USB(func) \
BOOT_TARGET_NVME(func) \
BOOT_TARGET_SCSI(func) \
BOOT_TARGET_PXE(func) \
- BOOT_TARGET_DHCP(func)
+ BOOT_TARGET_DHCP(func) \
+ func(USB_DFU, usbdfu, na)
#endif

#define BOOTM_SIZE __stringify(0x1700000)

---
base-commit: c17f03a7e93dfbbe5d32f5738274187065d9493f
change-id: 20250402-u-boot-meson64-move-dfu-end-eb9f5fe55608

Best regards,
--
Neil Armstrong <neil.armstrong@...>


Re: [PATCH v1 00/11] Amlogic: ADNL and Optimus protocols support

 

On Wed Mar 19, 2025 at 8:20 PM UTC, Arseniy Krasnov via groups.io wrote:
This patchset adds support of two Amlogic firmware burning protocols:
ADNL and Optimus. Each protocol is supported on the following SoC:

axg, g12a, g12b, sm1 - Optimus.
a1, s4, a5, c1, c2, c3, sc2, t3, t7, p1 - ADNL.
Out of curiosity, don't GXL and GXBB also support Optimus? Or is it
completely different?

Both work in fastboot manner, but significant feature is that they are
supported by current Amlogic BootROMs. As transport both use USB:
device is switched to gadget mode and then it can communicate with PC.
On PC side protocols are supported by:

1) (with some limitation)

For example:
pyamlboot/./ubt.py --img <image> --wipe all --reset

Note, image has special format, specific for Amlogic.
Awesome :)


Regards.


[PATCH v1 07/11] arm: meson: a1: add A1_SYSCTRL_SEC_STICKY_REG2

Arseniy Krasnov
 

This register is used by ADNL protocol, to check boot source
of current U-boot instance.

Signed-off-by: Arseniy Krasnov <avkrasnov@...>
---
arch/arm/include/asm/arch-meson/a1.h | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/arch/arm/include/asm/arch-meson/a1.h b/arch/arm/include/asm/arch-meson/a1.h
index 86d1a68de8c..16e209ed932 100644
--- a/arch/arm/include/asm/arch-meson/a1.h
+++ b/arch/arm/include/asm/arch-meson/a1.h
@@ -13,6 +13,12 @@
#define A1_SYSCTRL_ADDR(off) (A1_SYSCTRL_BASE + ((off) << 2))

#define A1_SYSCTRL_SEC_STATUS_REG4 A1_SYSCTRL_ADDR(0xc4)
+/*
+ * Checked during boot. Tells, that previous code, e.g.
+ * executed before reboot requested to continue boot
+ * from USB. Used by ADNL protocol.
+ */
+#define A1_SYSCTRL_SEC_STICKY_REG2 A1_SYSCTRL_ADDR(0xe2)

#define A1_SYSCTRL_MEM_SIZE_MASK 0xFFFF0000
#define A1_SYSCTRL_MEM_SIZE_SHIFT 16
--
2.30.1


[PATCH v1 06/11] arch: arm: meson: bootloader write support

Arseniy Krasnov
 

BootROM of Amlogic SoCs that use ADNL/Optimus protocols needs
special layout of "bootloader" partition. So let's implement
functions that support bootloader writing on such SoCs. This is
prerequisite for ADNL/Optimus implementation.

We place such functions to 'arch/arm/mach-meson', because this
code is also needed by 'fastboot' protocol which can also write
"bootloader" partition.

Signed-off-by: Arseniy Krasnov <avkrasnov@...>
---
arch/arm/include/asm/arch-meson/nand.h | 34 +++
arch/arm/include/asm/arch-meson/spinand.h | 43 ++++
arch/arm/mach-meson/Kconfig | 31 +++
arch/arm/mach-meson/Makefile | 4 +-
arch/arm/mach-meson/rawnand.c | 291 ++++++++++++++++++++++
arch/arm/mach-meson/spinand.c | 158 ++++++++++++
6 files changed, 559 insertions(+), 2 deletions(-)
create mode 100644 arch/arm/include/asm/arch-meson/nand.h
create mode 100644 arch/arm/include/asm/arch-meson/spinand.h
create mode 100644 arch/arm/mach-meson/rawnand.c
create mode 100644 arch/arm/mach-meson/spinand.c

diff --git a/arch/arm/include/asm/arch-meson/nand.h b/arch/arm/include/asm/arch-meson/nand.h
new file mode 100644
index 00000000000..1f5a20d237b
--- /dev/null
+++ b/arch/arm/include/asm/arch-meson/nand.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2023-2024 SaluteDevices, Inc.
+ */
+
+#ifndef __MESON_NAND_H__
+#define __MESON_NAND_H__
+
+#define BOOT_LOADER "bootloader"
+#define BOOT_BL2 "bl2"
+#define BOOT_SPL "spl"
+#define BOOT_TPL "tpl"
+#define BOOT_FIP "fip"
+
+#define BL2_COPY_NUM (CONFIG_MESON_BL2_COPY_NUM)
+#define BL2_IMAGE_SIZE_PER_COPY (CONFIG_MESON_BL2_IMAGE_SIZE)
+#define BL2_TOTAL_PAGES 1024
+
+#define TPL_COPY_NUM (CONFIG_MESON_TPL_COPY_NUM)
+#define TPL_SIZE_PER_COPY 0x200000
+
+#define NAND_RSV_BLOCK_NUM 48
+
+/**
+ * meson_bootloader_write - write 'bootloader' partition to NAND
+ * according to the required layout. It will
+ * write BL2, TPL and info pages.
+ *
+ * @buf: buffer with BL2 and TPL
+ * @length: buffer length
+ * @return: 0 on success, -errno otherwise.
+ */
+int meson_bootloader_write(unsigned char *buf, size_t length);
+#endif
diff --git a/arch/arm/include/asm/arch-meson/spinand.h b/arch/arm/include/asm/arch-meson/spinand.h
new file mode 100644
index 00000000000..1628c8bd4e0
--- /dev/null
+++ b/arch/arm/include/asm/arch-meson/spinand.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2024 SaluteDevices, Inc.
+ */
+
+#ifndef __MESON_SPINAND_H__
+#define __MESON_SPINAND_H__
+
+#include <stdint.h>
+#include <string.h>
+#include <asm/types.h>
+#include <asm/arch-meson/nand.h>
+#include <linux/compiler_attributes.h>
+#include <linux/mtd/mtd.h>
+
+struct meson_spi_nand_info_page {
+ char magic[8]; /* Magic header of info page. */
+ /* Info page version, +1 when you update this struct. */
+ u8 version; /* 1 for now. */
+ u8 mode; /* 1 discrete, 0 compact. */
+ u8 bl2_num; /* bl2 copy number. */
+ u8 fip_num; /* fip copy number. */
+ union {
+ struct {
+ u8 rd_max; /* spi nand max read io. */
+ u8 oob_offset; /* User bytes offset. */
+ u8 planes_per_lun; /* number of planes per LUN. */
+ u8 rsv; /* Reserved gap. */
+ u32 fip_start; /* Start pages. */
+ u32 fip_pages; /* Pages per fip. */
+ u32 page_size; /* spi nand page size (bytes). */
+ u32 page_per_blk; /* Page number per block. */
+ u32 oob_size; /* Valid oob size (bytes). */
+ u32 bbt_start; /* BBT start pages. */
+ u32 bbt_valid; /* BBT valid offset pages. */
+ u32 bbt_size; /* BBT occupied bytes. */
+ } __packed spinand; /* spi nand. */
+ struct {
+ u32 reserved;
+ } emmc;
+ } dev;
+} __packed;
+#endif
diff --git a/arch/arm/mach-meson/Kconfig b/arch/arm/mach-meson/Kconfig
index 7570f48e25f..883ee78f49b 100644
--- a/arch/arm/mach-meson/Kconfig
+++ b/arch/arm/mach-meson/Kconfig
@@ -93,4 +93,35 @@ config SYS_BOARD
Based on this option board/<CONFIG_SYS_VENDOR>/<CONFIG_SYS_BOARD> will
be used.

+config MESON_BL2_COPY_NUM
+ depends on ADNL || FASTBOOT_FLASH
+ int "Number of BL2 copies written to storage"
+ default 8
+ help
+ The ADNL / Optimus / fastboot protocol writes several copies of BL2
+ bootloader during firmware update process.
+
+config MESON_TPL_COPY_NUM
+ depends on ADNL || FASTBOOT_FLASH
+ int "Number of TPL copies written to storage"
+ default 4
+ help
+ The ADNL / Optimus / fastboot protocol writes several copies of TPL
+ bootloader during firmware update process.
+
+config MESON_BL2_IMAGE_SIZE
+ depends on ADNL || FASTBOOT_FLASH
+ hex "Size of BL2 image"
+ default 0x10000
+ help
+ Size of BL2 image, passed by ADNL / Optimus / fastboot protocols.
+
+config MESON_BL2_IMAGE_OFFSET
+ depends on ADNL || FASTBOOT_FLASH
+ hex "Offset from info page to BL2 image"
+ default 0x800
+ help
+ For each copy of BL2, this is offset from info page to BL2 image. Used
+ by Optimus and fastboot protocols.
+
endif
diff --git a/arch/arm/mach-meson/Makefile b/arch/arm/mach-meson/Makefile
index 535b0878b91..f63f91dde35 100644
--- a/arch/arm/mach-meson/Makefile
+++ b/arch/arm/mach-meson/Makefile
@@ -4,6 +4,6 @@

obj-y += board-common.o sm.o board-info.o
obj-$(CONFIG_MESON_GX) += board-gx.o
-obj-$(CONFIG_MESON_AXG) += board-axg.o
+obj-$(CONFIG_MESON_AXG) += board-axg.o rawnand.o
obj-$(CONFIG_MESON_G12A) += board-g12a.o
-obj-$(CONFIG_MESON_A1) += board-a1.o
+obj-$(CONFIG_MESON_A1) += board-a1.o spinand.o
diff --git a/arch/arm/mach-meson/rawnand.c b/arch/arm/mach-meson/rawnand.c
new file mode 100644
index 00000000000..6b91674200e
--- /dev/null
+++ b/arch/arm/mach-meson/rawnand.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2024 SaluteDevices, Inc.
+ */
+
+#include <nand.h>
+#include <asm/types.h>
+#include <asm/arch/rawnand.h>
+#include <linux/compiler_attributes.h>
+#include <linux/mtd/mtd.h>
+#include <linux/sizes.h>
+
+struct raw_nand_setup {
+ union {
+ u32 d32;
+ struct {
+ unsigned cmd:22; /* NAND controller cmd to read. */
+ unsigned large_page:1; /* Page is large. */
+ unsigned no_rb:1; /* No RB. */
+ unsigned a2:1; /* A2 cmd enable flag. */
+ unsigned reserved25:1; /* Gap. */
+ unsigned page_list:1; /* Page list is enabled. */
+ unsigned sync_mode:2; /* Always 0 according vendor doc. */
+ unsigned size:2; /* Always 0 according vendor doc. */
+ unsigned active:1; /* Always 0 according vendor doc. */
+ } b;
+ } cfg;
+ u16 id;
+ u16 max;
+} __packed;
+
+struct raw_nand_cmd {
+ unsigned char type;
+ unsigned char val;
+} __packed;
+
+struct raw_ext_info {
+ u32 read_info;
+ u32 new_type;
+ u32 page_per_blk;
+ u32 xlc;
+ u32 ce_mask;
+ u32 boot_num;
+ u32 each_boot_pages;
+ /* for compatible reason */
+ u32 bbt_occupy_pages;
+ u32 bbt_start_block;
+} __packed;
+
+struct raw_fip_info {
+ u16 version;
+ u16 mode;
+ u32 fip_start;
+} __packed;
+
+struct raw_nand_info_page {
+ struct raw_nand_setup nand_setup;
+ unsigned char page_list[16];
+ struct raw_nand_cmd retry_usr[32];
+ struct raw_ext_info ext_info;
+ struct raw_fip_info fip_info;
+ u32 ddrp_start_page;
+} __packed;
+
+static void meson_raw_fill_oobbuf_with_magic(u8 *oobbuf, size_t oobavail)
+{
+ for (size_t j = 0; j < oobavail; j += 2) {
+ oobbuf[j] = 0x55;
+ oobbuf[j + 1] = 0xaa;
+ }
+}
+
+static int meson_raw_raw_nand_info_page_prepare(struct mtd_info *mtd, void *oobbuf)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd->parent);
+ struct raw_nand_info_page *p_nand_page0;
+ struct raw_ext_info *ext_info;
+ struct raw_nand_setup *nand_setup;
+ struct raw_fip_info *fip_info;
+ u32 config_data;
+
+ p_nand_page0 = oobbuf;
+ memset(p_nand_page0, 0x0, sizeof(struct raw_nand_info_page));
+ nand_setup = &p_nand_page0->nand_setup;
+ ext_info = &p_nand_page0->ext_info;
+
+ config_data = CMDRWGEN(NFC_CMD_N2M, 0, 1, 0, nand_chip->ecc.size >> 3,
+ nand_chip->ecc.steps);
+
+ config_data = config_data | BIT(23) | BIT(22) | BIT(21);
+ nand_setup->cfg.d32 = config_data;
+ nand_setup->id = 0;
+ nand_setup->max = 0;
+
+ ext_info->read_info = 1;
+ ext_info->page_per_blk = mtd->erasesize / mtd->writesize;
+ ext_info->ce_mask = 0x01;
+ ext_info->xlc = 1;
+ ext_info->boot_num = TPL_COPY_NUM;
+ ext_info->each_boot_pages = TPL_SIZE_PER_COPY / mtd->writesize;
+ ext_info->bbt_occupy_pages = 0x1;
+ ext_info->bbt_start_block = 0x14; /* Amlogic keeps BBT in hardcoded offset. */
+
+ p_nand_page0->ddrp_start_page = 0xb00;
+
+ fip_info = &p_nand_page0->fip_info;
+ fip_info->version = 1;
+ fip_info->mode = 1; /* Discrete mode. */
+ fip_info->fip_start = BL2_TOTAL_PAGES +
+ (NAND_RSV_BLOCK_NUM * ext_info->page_per_blk);
+
+ return 0;
+}
+
+static int meson_raw_write_info_page(struct mtd_info *mtd, loff_t offset, size_t *actual)
+{
+ int ret;
+ struct mtd_oob_ops ops;
+ u8 *datbuf = (u8 *)calloc(mtd->writesize, sizeof(u8));
+
+ if (!datbuf) {
+ pr_err("Failed to allocate data buf for info page\n");
+ return -ENOMEM;
+ }
+
+ meson_raw_raw_nand_info_page_prepare(mtd, datbuf);
+
+ memset(&ops, 0, sizeof(struct mtd_oob_ops));
+ ops.len = mtd->writesize;
+ ops.datbuf = datbuf;
+ ops.ooblen = mtd->oobavail;
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.oobbuf = calloc(mtd->oobavail, sizeof(u8));
+
+ if (!ops.oobbuf) {
+ pr_err("Failed to allocate OOB buf for info page\n");
+ free(datbuf);
+ return -ENOMEM;
+ }
+
+ meson_raw_fill_oobbuf_with_magic(ops.oobbuf, mtd->oobavail);
+
+ ret = mtd_write_oob(mtd, offset, &ops);
+ *actual = ops.retlen;
+
+ free(ops.oobbuf);
+ free(datbuf);
+
+ return ret;
+}
+
+static int meson_raw_write_bl2(unsigned char *src_data)
+{
+ int ret;
+ size_t i;
+ nand_erase_options_t opts;
+ struct mtd_info *mtd;
+ size_t bl2_copy_limit;
+ struct nand_chip *nand;
+ size_t oobbuff_size;
+ struct mtd_oob_ops ops;
+ size_t src_size = BL2_IMAGE_SIZE_PER_COPY;
+ loff_t dest = 0;
+ size_t copy_num = BL2_COPY_NUM;
+
+ pr_info("Writing BL2\n");
+
+ mtd = get_mtd_device_nm(BOOT_LOADER);
+ if (IS_ERR_OR_NULL(mtd)) {
+ pr_err("Failed to get mtd device \"bootloader\" ret: %ld\n", PTR_ERR(mtd));
+ return PTR_ERR(mtd);
+ }
+
+ if (mtd->oobavail & 1) {
+ pr_err("Invalid OOB available bytes: %u\n", mtd->oobavail);
+ return -ENODEV;
+ }
+
+ memset(&opts, 0, sizeof(opts));
+ opts.offset = dest;
+ opts.length = mtd->size;
+ if (nand_erase_opts(mtd, &opts)) {
+ pr_err("Failed to erase \"bootloader\"\n");
+ return -EIO;
+ }
+
+ bl2_copy_limit = mtd->size / BL2_COPY_NUM;
+
+ memset(&ops, 0, sizeof(struct mtd_oob_ops));
+ ops.len = src_size;
+ ops.datbuf = src_data;
+ ops.ooblen = mtd->oobavail * (src_size / mtd->writesize + 1);
+ ops.mode = MTD_OPS_AUTO_OOB;
+
+ nand = mtd_to_nand(mtd->parent);
+ oobbuff_size = (mtd->writesize / nand->ecc.size) * ops.ooblen;
+
+ ops.oobbuf = calloc(oobbuff_size, sizeof(u8));
+
+ if (!ops.oobbuf) {
+ pr_err("Failed to allocate OOB buf for BL2\n");
+ return -ENOMEM;
+ }
+
+ meson_raw_fill_oobbuf_with_magic(ops.oobbuf, ops.ooblen);
+
+ for (i = 0; i < copy_num; i++) {
+ size_t actual = 0;
+ loff_t offset_in_partition = dest + i * bl2_copy_limit;
+
+ if (mtd_block_isbad(mtd, offset_in_partition)) {
+ pr_info("Skip badblock at %llx\n", offset_in_partition);
+ continue;
+ }
+
+ ret = meson_raw_write_info_page(mtd, offset_in_partition, &actual);
+ if (ret)
+ goto free_oobbuf;
+
+ offset_in_partition += CONFIG_MESON_BL2_IMAGE_OFFSET +
+ (actual - mtd->writesize);
+ ret = mtd_write_oob(mtd, offset_in_partition, &ops);
+ if (ret)
+ goto free_oobbuf;
+ }
+
+free_oobbuf:
+ free(ops.oobbuf);
+
+ return ret;
+}
+
+static int meson_raw_write_tpl(unsigned char *src_data, size_t src_size)
+{
+ int ret;
+ size_t i;
+ struct mtd_info *mtd;
+ nand_erase_options_t opts;
+ loff_t dest = 0;
+ size_t copy_num = TPL_COPY_NUM;
+
+ pr_info("Writing TPL\n");
+ mtd = get_mtd_device_nm(BOOT_TPL);
+
+ if (IS_ERR_OR_NULL(mtd)) {
+ pr_err("Failed to get mtd device \"tpl\" ret: %ld\n", PTR_ERR(mtd));
+ return PTR_ERR(mtd);
+ }
+
+ memset(&opts, 0, sizeof(nand_erase_options_t));
+ opts.offset = dest;
+ opts.length = mtd->size;
+ if (nand_erase_opts(mtd, &opts)) {
+ pr_err("Failed to erase \"tpl\"\n");
+ return -EIO;
+ }
+
+ for (i = 0; i < copy_num; i++) {
+ loff_t offset_in_partition = dest + i * TPL_SIZE_PER_COPY;
+
+ ret = nand_write_skip_bad(mtd, offset_in_partition, &src_size,
+ NULL, mtd->size / TPL_COPY_NUM, src_data, 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int meson_bootloader_write(unsigned char *buf, size_t length)
+{
+ int ret;
+
+ if (!buf) {
+ pr_err("Empty buffer is received\n");
+ return -EINVAL;
+ }
+
+ ret = meson_raw_write_bl2(buf);
+ if (ret)
+ return ret;
+
+ /* Write FIP. FIP is also called TPL */
+ ret = meson_raw_write_tpl(buf + BL2_IMAGE_SIZE_PER_COPY,
+ length - BL2_IMAGE_SIZE_PER_COPY);
+ if (ret)
+ return ret;
+
+ pr_info("\"Bootloader (BL2+TPL)\" is written\n");
+ return 0;
+}
diff --git a/arch/arm/mach-meson/spinand.c b/arch/arm/mach-meson/spinand.c
new file mode 100644
index 00000000000..dbad4ba0e9f
--- /dev/null
+++ b/arch/arm/mach-meson/spinand.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2023 SaluteDevices, Inc.
+ */
+
+#include <nand.h>
+#include <asm/arch/spinand.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/sizes.h>
+
+#define SPINAND_MAGIC "AMLIFPG"
+#define SPINAND_INFO_VER 1
+
+static void meson_bootloader_setup_info_page(struct meson_spi_nand_info_page *info_page,
+ struct mtd_info *mtd)
+{
+ struct nand_device *nand_dev;
+ u32 page_per_blk;
+
+ nand_dev = mtd_to_nanddev(mtd->parent);
+ page_per_blk = mtd->erasesize / mtd->writesize;
+ info_page->version = SPINAND_INFO_VER;
+ /* DISCRETE only */
+ info_page->mode = 1;
+ info_page->bl2_num = BL2_COPY_NUM;
+ info_page->fip_num = TPL_COPY_NUM;
+ info_page->dev.spinand.rd_max = 2;
+ info_page->dev.spinand.fip_start = BL2_TOTAL_PAGES + NAND_RSV_BLOCK_NUM * page_per_blk;
+ info_page->dev.spinand.fip_pages = TPL_SIZE_PER_COPY / mtd->writesize;
+ info_page->dev.spinand.page_size = mtd->writesize;
+ info_page->dev.spinand.page_per_blk = page_per_blk;
+ info_page->dev.spinand.oob_size = mtd->oobsize;
+ info_page->dev.spinand.oob_offset = 0;
+ info_page->dev.spinand.bbt_start = 0;
+ info_page->dev.spinand.bbt_valid = 0;
+ info_page->dev.spinand.bbt_size = 0;
+ info_page->dev.spinand.planes_per_lun = nand_dev->memorg.planes_per_lun;
+}
+
+static int meson_store_boot_write_bl2(struct mtd_info *mtd, void *buf, unsigned int copy_idx,
+ struct meson_spi_nand_info_page *info_page)
+{
+ unsigned int size_per_copy;
+ loff_t offset;
+ size_t retlen;
+ int ret;
+
+ size_per_copy = mtd->writesize * (BL2_TOTAL_PAGES / BL2_COPY_NUM);
+ offset = mtd->offset + (copy_idx * size_per_copy);
+ ret = mtd_write(mtd, offset, BL2_IMAGE_SIZE_PER_COPY, &retlen, buf);
+ if (ret)
+ return ret;
+
+ /*
+ * Info page is written directly after each BL2 image. For
+ * example let:
+ * BL2_TOTAL_PAGES = 1024
+ * BL2_COPY_NUM = 8
+ * BL2_IMAGE_SIZE_PER_COPY = 65536 = 0x10000
+ * size_per_copy = 2048 * (1024 / 8) = 0x40000
+ *
+ * BL2 image will be written to BL2_COPY_NUM
+ * locations:
+ * 0x00000
+ * 0x40000
+ * 0x80000
+ * 0xc0000
+ * 0x100000
+ * 0x140000
+ * 0x180000
+ * 0x1c0000
+ *
+ * Info pages will be written to BL2_COPY_NUM
+ * locations:
+ * 0x10000
+ * 0x50000
+ * 0x90000
+ * 0xd0000
+ * 0x110000
+ * 0x150000
+ * 0x190000
+ * 0x1d0000
+ *
+ * Here is the same, in picture:
+ *
+ * BL2_TOTAL_PAGES == 1024
+ * BL2_COPY_NUM == 8
+ * /---------------------------------------\
+ * | |
+ *
+ * |AB |AB |AB |AB |AB |AB |AB |AB |
+ * |____|____|____|____|____|____|____|____|
+ * ^ ^
+ * | |
+ * |____|__ size_per_copy == 0x40000
+ *
+ * A - is BL2 image. It is BL2_IMAGE_SIZE_PER_COPY == 0x10000 bytes.
+ * B - is info page.
+ */
+ offset += BL2_IMAGE_SIZE_PER_COPY;
+
+ return mtd_write(mtd, offset, sizeof(*info_page),
+ &retlen, (u8 *)info_page);
+}
+
+static int meson_store_boot_write_tpl(void *buf, size_t size, unsigned int copy_idx)
+{
+ loff_t offset;
+ struct mtd_info *mtd;
+ loff_t limit;
+ size_t retlen = 0;
+
+ mtd = get_mtd_device_nm(BOOT_TPL);
+ if (IS_ERR_OR_NULL(mtd))
+ return -ENODEV;
+
+ offset = mtd->offset + (copy_idx * TPL_SIZE_PER_COPY);
+ limit = offset + TPL_SIZE_PER_COPY;
+
+ return nand_write_skip_bad(mtd->parent, offset, &size, &retlen,
+ limit, buf, 0);
+}
+
+int meson_bootloader_write(unsigned char *buf, size_t length)
+{
+ struct meson_spi_nand_info_page info_page;
+ unsigned int copy_idx;
+ struct mtd_info *mtd;
+ int ret;
+
+ /* 'buf' contains BL2 and TPL. If there is only BL2 or
+ * even less - input data is invalid.
+ */
+ if (length <= BL2_IMAGE_SIZE_PER_COPY)
+ return -EINVAL;
+
+ mtd = get_mtd_device_nm(BOOT_LOADER);
+ if (IS_ERR_OR_NULL(mtd))
+ return -ENODEV;
+
+ meson_bootloader_setup_info_page(&info_page, mtd);
+
+ for (copy_idx = 0; copy_idx < BL2_COPY_NUM; copy_idx++) {
+ ret = meson_store_boot_write_bl2(mtd, buf, copy_idx, &info_page);
+ if (ret)
+ return ret;
+ }
+
+ for (copy_idx = 0; copy_idx < TPL_COPY_NUM; copy_idx++) {
+ ret = meson_store_boot_write_tpl(buf + BL2_IMAGE_SIZE_PER_COPY,
+ length - BL2_IMAGE_SIZE_PER_COPY, copy_idx);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
--
2.30.1


[PATCH v1 03/11] usb: gadget: fastboot: make part of USB fastboot code shared

Arseniy Krasnov
 

This is preparation for fastboot-based flashing protocols (such as Amlogic
ADNL). When device enters ADNL mode, it becomes "fastboot" device from
USB point of view, so to avoid copy-paste of USB part of fastboot protocol,
reimplement it as shared code between fastboot and ADNL implementations.

Signed-off-by: Arseniy Krasnov <avkrasnov@...>
---
drivers/usb/gadget/Makefile | 2 +-
drivers/usb/gadget/f_fastboot.c | 295 +----------------------
drivers/usb/gadget/f_fastboot_common.c | 320 +++++++++++++++++++++++++
drivers/usb/gadget/f_fastboot_common.h | 71 ++++++
4 files changed, 403 insertions(+), 285 deletions(-)
create mode 100644 drivers/usb/gadget/f_fastboot_common.c
create mode 100644 drivers/usb/gadget/f_fastboot_common.h

diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 4bda224ff1a..6c16ad83f6e 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -27,7 +27,7 @@ obj-$(CONFIG_USB_GADGET_DOWNLOAD) += g_dnl.o
obj-$(CONFIG_USB_FUNCTION_THOR) += f_thor.o
obj-$(CONFIG_DFU_OVER_USB) += f_dfu.o
obj-$(CONFIG_USB_FUNCTION_MASS_STORAGE) += f_mass_storage.o
-obj-$(CONFIG_USB_FUNCTION_FASTBOOT) += f_fastboot.o
+obj-$(CONFIG_USB_FUNCTION_FASTBOOT) += f_fastboot.o f_fastboot_common.o
obj-$(CONFIG_USB_FUNCTION_SDP) += f_sdp.o
obj-$(CONFIG_USB_FUNCTION_ROCKUSB) += f_rockusb.o
obj-$(CONFIG_USB_FUNCTION_ACM) += f_acm.o
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index 3c76d42c5ef..9298658b569 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -23,29 +23,7 @@
#include <linux/compiler.h>
#include <g_dnl.h>
#include <rabbith.h>
-
-#define FASTBOOT_INTERFACE_CLASS 0xff
-#define FASTBOOT_INTERFACE_SUB_CLASS 0x42
-#define FASTBOOT_INTERFACE_PROTOCOL 0x03
-
-#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200)
-#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040)
-#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040)
-
-#define EP_BUFFER_SIZE 4096
-/*
- * EP_BUFFER_SIZE must always be an integral multiple of maxpacket size
- * (64 or 512 or 1024), else we break on certain controllers like DWC3
- * that expect bulk OUT requests to be divisible by maxpacket size.
- */
-
-struct f_fastboot {
- struct usb_function usb_function;
-
- /* IN/OUT EP's and corresponding requests */
- struct usb_ep *in_ep, *out_ep;
- struct usb_request *in_req, *out_req;
-};
+#include "f_fastboot_common.h"

static char fb_ext_prop_name[] = "DeviceInterfaceGUID";
static char fb_ext_prop_data[] = "{4866319A-F4D6-4374-93B9-DC2DEB361BA9}";
@@ -66,113 +44,7 @@ static struct usb_os_desc_table fb_os_desc_table = {
.os_desc = &fb_os_desc,
};

-static inline struct f_fastboot *func_to_fastboot(struct usb_function *f)
-{
- return container_of(f, struct f_fastboot, usb_function);
-}
-
-static struct f_fastboot *fastboot_func;
-
-static struct usb_endpoint_descriptor fs_ep_in = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(64),
-};
-
-static struct usb_endpoint_descriptor fs_ep_out = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(64),
-};
-
-static struct usb_endpoint_descriptor hs_ep_in = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor hs_ep_out = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(512),
-};
-
-static struct usb_interface_descriptor interface_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0x00,
- .bAlternateSetting = 0x00,
- .bNumEndpoints = 0x02,
- .bInterfaceClass = FASTBOOT_INTERFACE_CLASS,
- .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS,
- .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL,
-};
-
-static struct usb_descriptor_header *fb_fs_function[] = {
- (struct usb_descriptor_header *)&interface_desc,
- (struct usb_descriptor_header *)&fs_ep_in,
- (struct usb_descriptor_header *)&fs_ep_out,
- NULL,
-};
-
-static struct usb_descriptor_header *fb_hs_function[] = {
- (struct usb_descriptor_header *)&interface_desc,
- (struct usb_descriptor_header *)&hs_ep_in,
- (struct usb_descriptor_header *)&hs_ep_out,
- NULL,
-};
-
-/* Super speed */
-static struct usb_endpoint_descriptor ss_ep_in = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(1024),
-};
-
-static struct usb_endpoint_descriptor ss_ep_out = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor fb_ss_bulk_comp_desc = {
- .bLength = sizeof(fb_ss_bulk_comp_desc),
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-};
-
-static struct usb_descriptor_header *fb_ss_function[] = {
- (struct usb_descriptor_header *)&interface_desc,
- (struct usb_descriptor_header *)&ss_ep_in,
- (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc,
- (struct usb_descriptor_header *)&ss_ep_out,
- (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc,
- NULL,
-};
-
-static struct usb_endpoint_descriptor *
-fb_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
- struct usb_endpoint_descriptor *hs,
- struct usb_endpoint_descriptor *ss)
-{
- if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER)
- return ss;
-
- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
- return hs;
- return fs;
-}
+static struct fastboot_funcs *fastboot_func;

/*
* static strings, in UTF-8
@@ -196,179 +68,34 @@ static struct usb_gadget_strings *fastboot_strings[] = {

static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);

-static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
-{
- int status = req->status;
- if (!status)
- return;
- printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual);
-}
-
static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
{
- int id;
- struct usb_gadget *gadget = c->cdev->gadget;
- struct f_fastboot *f_fb = func_to_fastboot(f);
- const char *s;
-
- /* DYNAMIC interface numbers assignments */
- id = usb_interface_id(c, f);
- if (id < 0)
- return id;
- interface_desc.bInterfaceNumber = id;
-
- /* Enable OS and Extended Properties Feature Descriptor */
- c->cdev->use_os_string = 1;
- f->os_desc_table = &fb_os_desc_table;
- f->os_desc_n = 1;
- f->os_desc_table->if_id = id;
- INIT_LIST_HEAD(&fb_os_desc.ext_prop);
- fb_ext_prop.name_len = strlen(fb_ext_prop.name) * 2 + 2;
- fb_os_desc.ext_prop_len = 10 + fb_ext_prop.name_len;
- fb_os_desc.ext_prop_count = 1;
- fb_ext_prop.data_len = strlen(fb_ext_prop.data) * 2 + 2;
- fb_os_desc.ext_prop_len += fb_ext_prop.data_len + 4;
- list_add_tail(&fb_ext_prop.entry, &fb_os_desc.ext_prop);
-
- id = usb_string_id(c->cdev);
- if (id < 0)
- return id;
- fastboot_string_defs[0].id = id;
- interface_desc.iInterface = id;
-
- f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in);
- if (!f_fb->in_ep)
- return -ENODEV;
- f_fb->in_ep->driver_data = c->cdev;
-
- f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out);
- if (!f_fb->out_ep)
- return -ENODEV;
- f_fb->out_ep->driver_data = c->cdev;
-
- f->descriptors = fb_fs_function;
-
- if (gadget_is_dualspeed(gadget)) {
- /* Assume endpoint addresses are the same for both speeds */
- hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
- hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
- /* copy HS descriptors */
- f->hs_descriptors = fb_hs_function;
- }
-
- if (gadget_is_superspeed(gadget)) {
- ss_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
- ss_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
- f->ss_descriptors = fb_ss_function;
- }
-
- s = env_get("serial#");
- if (s)
- g_dnl_set_serialnumber((char *)s);
-
- return 0;
+ return fastboot_common_bind(c, f, &fb_os_desc_table, &fb_os_desc,
+ (struct usb_string **)&fastboot_string_defs,
+ &fb_ext_prop);
}

static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
{
- f->os_desc_table = NULL;
- list_del(&fb_os_desc.ext_prop);
+ fastboot_common_unbind(c, f, &fb_os_desc);
memset(fastboot_func, 0, sizeof(*fastboot_func));
}

static void fastboot_disable(struct usb_function *f)
{
- struct f_fastboot *f_fb = func_to_fastboot(f);
-
- usb_ep_disable(f_fb->out_ep);
- usb_ep_disable(f_fb->in_ep);
-
- if (f_fb->out_req) {
- free(f_fb->out_req->buf);
- usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
- f_fb->out_req = NULL;
- }
- if (f_fb->in_req) {
- free(f_fb->in_req->buf);
- usb_ep_free_request(f_fb->in_ep, f_fb->in_req);
- f_fb->in_req = NULL;
- }
-}
-
-static struct usb_request *fastboot_start_ep(struct usb_ep *ep)
-{
- struct usb_request *req;
-
- req = usb_ep_alloc_request(ep, 0);
- if (!req)
- return NULL;
-
- req->length = EP_BUFFER_SIZE;
- req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE);
- if (!req->buf) {
- usb_ep_free_request(ep, req);
- return NULL;
- }
-
- memset(req->buf, 0, req->length);
- return req;
+ fastboot_common_disable(f);
}

static int fastboot_set_alt(struct usb_function *f,
unsigned interface, unsigned alt)
{
- int ret;
- struct usb_composite_dev *cdev = f->config->cdev;
- struct usb_gadget *gadget = cdev->gadget;
- struct f_fastboot *f_fb = func_to_fastboot(f);
- const struct usb_endpoint_descriptor *d;
-
- debug("%s: func: %s intf: %d alt: %d\n",
- __func__, f->name, interface, alt);
-
- d = fb_ep_desc(gadget, &fs_ep_out, &hs_ep_out, &ss_ep_out);
- ret = usb_ep_enable(f_fb->out_ep, d);
- if (ret) {
- puts("failed to enable out ep\n");
- return ret;
- }
-
- f_fb->out_req = fastboot_start_ep(f_fb->out_ep);
- if (!f_fb->out_req) {
- puts("failed to alloc out req\n");
- ret = -EINVAL;
- goto err;
- }
- f_fb->out_req->complete = rx_handler_command;
-
- d = fb_ep_desc(gadget, &fs_ep_in, &hs_ep_in, &ss_ep_in);
- ret = usb_ep_enable(f_fb->in_ep, d);
- if (ret) {
- puts("failed to enable in ep\n");
- goto err;
- }
-
- f_fb->in_req = fastboot_start_ep(f_fb->in_ep);
- if (!f_fb->in_req) {
- puts("failed alloc req in\n");
- ret = -EINVAL;
- goto err;
- }
- f_fb->in_req->complete = fastboot_complete;
-
- ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0);
- if (ret)
- goto err;
-
- return 0;
-err:
- fastboot_disable(f);
- return ret;
+ return fastboot_common_set_alt(f, interface, alt,
+ rx_handler_command);
}

static int fastboot_add(struct usb_configuration *c)
{
- struct f_fastboot *f_fb = fastboot_func;
+ struct fastboot_funcs *f_fb = fastboot_func;
int status;

debug("%s: cdev: 0x%p\n", __func__, c->cdev);
@@ -515,7 +242,7 @@ static void multiresponse_on_complete(struct usb_ep *ep, struct usb_request *req
/* If response is final OKAY/FAIL response disconnect this handler and unset cmd */
if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4)) {
multiresponse_cmd = -1;
- fastboot_func->in_req->complete = fastboot_complete;
+ fastboot_func->in_req->complete = fastboot_common_complete;
}
}

diff --git a/drivers/usb/gadget/f_fastboot_common.c b/drivers/usb/gadget/f_fastboot_common.c
new file mode 100644
index 00000000000..9e509503d2d
--- /dev/null
+++ b/drivers/usb/gadget/f_fastboot_common.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix@...>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@...>
+ *
+ * Copyright 2014 Linaro, Ltd.
+ * Rob Herring <robh@...>
+ *
+ * Copyright (c) 2025 SaluteDevices, Inc.
+ * Author: Arseniy Krasnov <avkrasnov@...>
+ */
+
+#include <env.h>
+#include "f_fastboot_common.h"
+
+static struct usb_endpoint_descriptor fs_ep_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor fs_ep_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(64),
+};
+
+static struct usb_interface_descriptor interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0x00,
+ .bAlternateSetting = 0x00,
+ .bNumEndpoints = 0x02,
+ .bInterfaceClass = FASTBOOT_INTERFACE_CLASS,
+ .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS,
+ .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL,
+};
+
+static struct usb_descriptor_header *fb_fs_function[] = {
+ (struct usb_descriptor_header *)&interface_desc,
+ (struct usb_descriptor_header *)&fs_ep_in,
+ (struct usb_descriptor_header *)&fs_ep_out,
+ NULL,
+};
+
+static struct usb_endpoint_descriptor hs_ep_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_ep_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *fb_hs_function[] = {
+ (struct usb_descriptor_header *)&interface_desc,
+ (struct usb_descriptor_header *)&hs_ep_in,
+ (struct usb_descriptor_header *)&hs_ep_out,
+ NULL,
+};
+
+/* Super speed */
+static struct usb_endpoint_descriptor ss_ep_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_ep_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor fb_ss_bulk_comp_desc = {
+ .bLength = sizeof(fb_ss_bulk_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *fb_ss_function[] = {
+ (struct usb_descriptor_header *)&interface_desc,
+ (struct usb_descriptor_header *)&ss_ep_in,
+ (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc,
+ (struct usb_descriptor_header *)&ss_ep_out,
+ (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc,
+ NULL,
+};
+
+static struct usb_endpoint_descriptor *fastboot_common_ep_desc(struct usb_gadget *g,
+ struct usb_endpoint_descriptor *fs,
+ struct usb_endpoint_descriptor *hs,
+ struct usb_endpoint_descriptor *ss)
+{
+ if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER)
+ return ss;
+
+ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return hs;
+ return fs;
+}
+
+void fastboot_common_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ int status = req->status;
+
+ if (!status)
+ return;
+
+ printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual);
+}
+
+int fastboot_common_bind(struct usb_configuration *c, struct usb_function *f,
+ struct usb_os_desc_table *fb_os_desc_table,
+ struct usb_os_desc *fb_os_desc,
+ struct usb_string **fastboot_string_defs,
+ struct usb_os_desc_ext_prop *fb_ext_prop)
+{
+ int id;
+ struct usb_gadget *gadget = c->cdev->gadget;
+ struct fastboot_funcs *f_fb = func_to_fastboot_funcs(f);
+ const char *s;
+
+ /* DYNAMIC interface numbers assignments */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+
+ interface_desc.bInterfaceNumber = id;
+
+ /* Enable OS and Extended Properties Feature Descriptor */
+ c->cdev->use_os_string = 1;
+ f->os_desc_table = fb_os_desc_table;
+ f->os_desc_n = 1;
+ f->os_desc_table->if_id = id;
+ INIT_LIST_HEAD(&fb_os_desc->ext_prop);
+ fb_ext_prop->name_len = strlen(fb_ext_prop->name) * 2 + 2;
+ fb_os_desc->ext_prop_len = 10 + fb_ext_prop->name_len;
+ fb_os_desc->ext_prop_count = 1;
+ fb_ext_prop->data_len = strlen(fb_ext_prop->data) * 2 + 2;
+ fb_os_desc->ext_prop_len += fb_ext_prop->data_len + 4;
+ list_add_tail(&fb_ext_prop->entry, &fb_os_desc->ext_prop);
+
+ id = usb_string_id(c->cdev);
+ if (id < 0)
+ return id;
+
+ fastboot_string_defs[0]->id = id;
+ interface_desc.iInterface = id;
+
+ f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in);
+ if (!f_fb->in_ep)
+ return -ENODEV;
+
+ f_fb->in_ep->driver_data = c->cdev;
+
+ f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out);
+ if (!f_fb->out_ep)
+ return -ENODEV;
+
+ f_fb->out_ep->driver_data = c->cdev;
+
+ f->descriptors = fb_fs_function;
+
+ if (gadget_is_dualspeed(gadget)) {
+ /* Assume endpoint addresses are the same for both speeds */
+ hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
+ hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
+ /* copy HS descriptors */
+ f->hs_descriptors = fb_hs_function;
+ }
+
+ if (gadget_is_superspeed(gadget)) {
+ ss_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
+ ss_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
+ f->ss_descriptors = fb_ss_function;
+ }
+
+ s = env_get("serial#");
+ if (s)
+ g_dnl_set_serialnumber((char *)s);
+
+ return 0;
+}
+
+void fastboot_common_unbind(struct usb_configuration *c,
+ struct usb_function *f,
+ struct usb_os_desc *fb_os_desc)
+{
+ f->os_desc_table = NULL;
+ list_del(&fb_os_desc->ext_prop);
+}
+
+static void __fastboot_common_disable(struct usb_function *f, bool disable_out, bool disable_in)
+{
+ struct fastboot_funcs *f_fb = func_to_fastboot_funcs(f);
+
+ if (disable_out)
+ usb_ep_disable(f_fb->out_ep);
+
+ if (disable_in)
+ usb_ep_disable(f_fb->in_ep);
+
+ if (f_fb->out_req) {
+ free(f_fb->out_req->buf);
+ usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
+ f_fb->out_req = NULL;
+ }
+ if (f_fb->in_req) {
+ free(f_fb->in_req->buf);
+ usb_ep_free_request(f_fb->in_ep, f_fb->in_req);
+ f_fb->in_req = NULL;
+ }
+}
+
+void fastboot_common_disable(struct usb_function *f)
+{
+ return __fastboot_common_disable(f, true, true);
+}
+
+static struct usb_request *fastboot_common_start_ep(struct usb_ep *ep)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, 0);
+ if (!req)
+ return NULL;
+
+ req->length = EP_BUFFER_SIZE;
+ req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+
+ memset(req->buf, 0, req->length);
+ return req;
+}
+
+int fastboot_common_set_alt(struct usb_function *f,
+ unsigned int interface, unsigned int alt,
+ void (*rx_handler)(struct usb_ep *ep, struct usb_request *req))
+{
+ int ret;
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_gadget *gadget = cdev->gadget;
+ struct fastboot_funcs *f_adnl = func_to_fastboot_funcs(f);
+ const struct usb_endpoint_descriptor *d;
+ bool disable_out = false;
+ bool disable_in = false;
+
+ debug("%s: func: %s intf: %u alt: %u\n",
+ __func__, f->name, interface, alt);
+
+ d = fastboot_common_ep_desc(gadget, &fs_ep_out, &hs_ep_out, &ss_ep_out);
+ ret = usb_ep_enable(f_adnl->out_ep, d);
+ if (ret) {
+ puts("failed to enable out ep\n");
+ return ret;
+ }
+
+ disable_out = true;
+
+ f_adnl->out_req = fastboot_common_start_ep(f_adnl->out_ep);
+ if (!f_adnl->out_req) {
+ puts("failed to alloc out req\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ f_adnl->out_req->complete = rx_handler;
+
+ d = fastboot_common_ep_desc(gadget, &fs_ep_in, &hs_ep_in, &ss_ep_in);
+ ret = usb_ep_enable(f_adnl->in_ep, d);
+ if (ret) {
+ puts("failed to enable in ep\n");
+ goto err;
+ }
+
+ disable_in = true;
+
+ f_adnl->in_req = fastboot_common_start_ep(f_adnl->in_ep);
+ if (!f_adnl->in_req) {
+ puts("failed alloc req in\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ f_adnl->in_req->complete = fastboot_common_complete;
+
+ ret = usb_ep_queue(f_adnl->out_ep, f_adnl->out_req, 0);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ __fastboot_common_disable(f, disable_out, disable_in);
+ return ret;
+}
+
+struct fastboot_funcs *func_to_fastboot_funcs(struct usb_function *f)
+{
+ return container_of(f, struct fastboot_funcs, usb_function);
+}
diff --git a/drivers/usb/gadget/f_fastboot_common.h b/drivers/usb/gadget/f_fastboot_common.h
new file mode 100644
index 00000000000..dba7d7003ba
--- /dev/null
+++ b/drivers/usb/gadget/f_fastboot_common.h
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix@...>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@...>
+ *
+ * Copyright 2014 Linaro, Ltd.
+ * Rob Herring <robh@...>
+ *
+ * Copyright (c) 2025 SaluteDevices, Inc.
+ * Author: Arseniy Krasnov <avkrasnov@...>
+ */
+
+#ifndef __F_FASTBOOT_COMMON_H_
+#define __F_FASTBOOT_COMMON_H_
+#include <g_dnl.h>
+
+/*
+ * EP_BUFFER_SIZE must always be an integral multiple of maxpacket size
+ * (64 or 512 or 1024), else we break on certain controllers like DWC3
+ * that expect bulk OUT requests to be divisible by maxpacket size.
+ */
+#define EP_BUFFER_SIZE 4096
+
+#define FASTBOOT_INTERFACE_CLASS 0xff
+#define FASTBOOT_INTERFACE_SUB_CLASS 0x42
+#define FASTBOOT_INTERFACE_PROTOCOL 0x03
+
+struct fastboot_funcs {
+ struct usb_function usb_function;
+
+ /* IN/OUT EP's and corresponding requests */
+ struct usb_ep *in_ep, *out_ep;
+ struct usb_request *in_req, *out_req;
+};
+
+void fastboot_common_complete(struct usb_ep *ep, struct usb_request *req);
+
+/*
+ * Initializes USB function 'f' with provided resources and binds it to the
+ * USB configuration 'c'.
+ */
+int fastboot_common_bind(struct usb_configuration *c, struct usb_function *f,
+ struct usb_os_desc_table *fb_os_desc_table,
+ struct usb_os_desc *fb_os_desc,
+ struct usb_string **fastboot_string_defs,
+ struct usb_os_desc_ext_prop *fb_ext_prop);
+
+/* Reverse for 'fastboot_bind_common()'. */
+void fastboot_common_unbind(struct usb_configuration *c,
+ struct usb_function *f,
+ struct usb_os_desc *fb_os_desc);
+
+/*
+ * Setups endpoints, allocates memory and sets callbacks for both IN and
+ * OUT requests.
+ */
+int fastboot_common_set_alt(struct usb_function *f,
+ unsigned int interface, unsigned int alt,
+ void (*rx_handler)(struct usb_ep *ep,
+ struct usb_request *req));
+
+/* Disables provided USB function 'f'. */
+void fastboot_common_disable(struct usb_function *f);
+
+/* Returns parent struct of 'f'. */
+struct fastboot_funcs *func_to_fastboot_funcs(struct usb_function *f);
+
+#endif /* __F_FASTBOOT_COMMON_H_ */
--
2.30.1


[PATCH v1 02/11] arch: arm: meson: use 'meson_sm_call()' in 'meson_sm_pwrdm_set()'

Arseniy Krasnov
 

Use more generic function in 'meson_sm_pwrdm_set()'.

Signed-off-by: Arseniy Krasnov <avkrasnov@...>
---
arch/arm/mach-meson/sm.c | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/arch/arm/mach-meson/sm.c b/arch/arm/mach-meson/sm.c
index 53b07e1dfe9..0a781e9c47b 100644
--- a/arch/arm/mach-meson/sm.c
+++ b/arch/arm/mach-meson/sm.c
@@ -249,22 +249,16 @@ static int meson_sm_call(u32 smc, struct pt_regs *regs, s32 *retval)

int meson_sm_pwrdm_set(size_t index, int cmd)
{
- struct udevice *dev;
struct pt_regs regs = { 0 };
int err;

- dev = meson_get_sm_device();
- if (IS_ERR(dev))
- return PTR_ERR(dev);
-
regs.regs[1] = index;
regs.regs[2] = cmd;

- err = sm_call(dev, MESON_SMC_CMD_PWRDM_SET, NULL, &regs);
+ err = meson_sm_call(MESON_SMC_CMD_PWRDM_SET, &regs, NULL);
if (err)
pr_err("Failed to %s power domain ind=%zu (%d)\n", cmd == PWRDM_ON ?
"enable" : "disable", index, err);
-
return err;
}

--
2.30.1


[PATCH v1 01/11] arch: arm: meson: sm: add commands to reboot device in different modes

Arseniy Krasnov
 

From: Vladimir Mitrofanov <vvmitrofanov@...>

There are several commands to reboot device in specific mode
(cold, normal, etc.), these commands helps to enter flashing mode
and set boot source device:

1) MESON_SMC_CMD_BL1_FIRST_BOOT_SRC_SET

This command tells BL1 that it must load BL2 from USB data path. It is
used in ADNL protocol logic when burning process is started from the
U-Boot command line: when 'adnl' is entered, this command tells BL1 to
load BL2 from USB, then board is rebooted. After reboot BL2 and next
stage loaders will be loaded from USB, sticky register has value that
we are in ADNL mode, thus booted U-Boot enters ADNL mode to start data
burning.

Used in adnl-based SoC families: a1, s4, c1, с2, sc2, t7, etc.

2) MESON_SMC_CMD_REBOOT_MODE_SET
Used in optimus-based SoC families: axg, g12a, g12b, sm1, etc.

3) MESON_SMC_CMD_USB_BOOT_MODE_SET
Used in optimus-based SoC families: axg, g12a, g12b, sm1, etc.

Signed-off-by: Arseniy Krasnov <avkrasnov@...>
Signed-off-by: Vladimir Mitrofanov <vvmitrofanov@...>
Signed-off-by: Evgeny Bachinin <EABachinin@...>
---
arch/arm/include/asm/arch-meson/sm.h | 98 +++++++++++++++++++++++++++-
arch/arm/mach-meson/sm.c | 39 +++++++++++
drivers/sm/meson-sm.c | 3 +
include/meson/sm.h | 3 +
4 files changed, 142 insertions(+), 1 deletion(-)

diff --git a/arch/arm/include/asm/arch-meson/sm.h b/arch/arm/include/asm/arch-meson/sm.h
index 4d614955fc2..087f8b4d5fa 100644
--- a/arch/arm/include/asm/arch-meson/sm.h
+++ b/arch/arm/include/asm/arch-meson/sm.h
@@ -83,7 +83,7 @@ int meson_sm_get_serial(void *buffer, size_t size);
*/
int meson_sm_get_chip_id(struct meson_sm_chip_id *chip_id);

-enum {
+enum meson_reboot_mode {
REBOOT_REASON_COLD = 0,
REBOOT_REASON_NORMAL = 1,
REBOOT_REASON_RECOVERY = 2,
@@ -134,4 +134,100 @@ int meson_sm_pwrdm_set(size_t index, int cmd);
#define meson_sm_pwrdm_on(index) \
meson_sm_pwrdm_set(index, PWRDM_ON)

+/*
+ * Below is the list of SCPI_CMD commands, issued by U-Boot and passed to SCP
+ * co-processor via SMC to BL31 (TF-A). BL31 routes these commands to SCP
+ * via mailbox.
+ *
+ * Commands instruct the SCP how to set/control POC pins in order to choose
+ * first boot source, specified by the particular command below. These POC pins
+ * are readable by BootROM and used to determine the boot sequence. Usually,
+ * the normal default boot sequence, seen on production device is
+ * NAND/eMMC -> SD -> USB
+ *
+ * Commands below change the first boot source.
+ *
+ * NOTE:
+ * these commands are used along with meson_sm_set_bl1_first_boot_source() on
+ * Amlogic SoCs where ADNL burning protocol is utilized.
+ * Refer to meson_sm_set_usb_boot_mode() for the same functionality but on SoCs
+ * with Optimus burning protocol.
+ */
+
+/**
+ * define SCPI_CMD_USB_BOOT - sets the first boot source into USB with timeout
+ *
+ * Additionally, force the BootROM (BL1) to wait for the Host's enumeration
+ * with timeout
+ */
+#define SCPI_CMD_USB_BOOT 0xB0
+
+/**
+ * define SCPI_CMD_USB_UNBOOT - sets the first boot source into USB w/o timeout
+ *
+ * Additionally, force the BootROM to wait for the Host's enumeration with
+ * uninterruptible wait, i.e. forever
+ */
+#define SCPI_CMD_USB_UNBOOT 0xB1
+
+/**
+ * define SCPI_CMD_SDCARD_BOOT - sets the first boot source into SD card
+ */
+#define SCPI_CMD_SDCARD_BOOT 0xB2
+
+/**
+ * define SCPI_CMD_CLEAR_BOOT - sets the first boot source into default one
+ *
+ * BootROM follows a pre-defined boot sequence stored in SoC ROM code.
+ */
+#define SCPI_CMD_CLEAR_BOOT 0xB3
+
+/**
+ * meson_sm_set_bl1_first_boot_source - sets the first boot source for BootROM.
+ * @cmd: command to set first boot source visible to BootROM during boot.
+ * Use SCPI_CMD_* as commands.
+ *
+ * BootROM checks POC-pins (POC0, POC1, POC2) to determine in which order it
+ * must probe the various methods of booting the system. The order is usually
+ * pre-defined. But function changes the first boot source of that pre-defined
+ * order.
+ *
+ * @return: zero on success or error code on failure.
+ */
+int meson_sm_set_bl1_first_boot_source(u64 cmd);
+
+/**
+ * meson_sm_reboot - reboots device in a appropriate mode
+ * All possible modes described in &enum meson_reboot_mode
+ *
+ * @mode: reboot mode.
+ * @return: zero on success or error code on failure.
+ */
+int meson_sm_reboot(u64 mode);
+
+/**
+ * enum meson_usb_boot_mode - modes for meson_sm_set_usb_boot_mode()
+ * @FORCE_USB_BOOT: Forces BootROM to boot with firmware transferred by PC
+ * burning tool via USB. This mode has influence despite of the
+ * previously selected boot source. This mode persists between
+ * reboots and must be cleared.
+ * @CLEAR_USB_BOOT: Clears previous mode.
+ *
+ * These modes are used in conjunction with meson_sm_set_usb_boot_mode() on
+ * Amlogic SoCs where Optimus burning protocol is utilized.
+ */
+enum meson_usb_boot_mode {
+ CLEAR_USB_BOOT = 1,
+ FORCE_USB_BOOT = 2,
+};
+
+/**
+ * meson_sm_set_usb_boot_mode - sets the mode of usb interface that will be used during boot
+ * All possible modes described in &enum meson_usb_boot_mode
+ *
+ * @mode: reboot mode.
+ * @return: zero on success or error code on failure.
+ */
+int meson_sm_set_usb_boot_mode(u64 mode);
+
#endif /* __MESON_SM_H__ */
diff --git a/arch/arm/mach-meson/sm.c b/arch/arm/mach-meson/sm.c
index b1f91ca29ce..53b07e1dfe9 100644
--- a/arch/arm/mach-meson/sm.c
+++ b/arch/arm/mach-meson/sm.c
@@ -236,6 +236,17 @@ int meson_sm_get_reboot_reason(void)
return FIELD_GET(REBOOT_REASON_MASK, reason);
}

+static int meson_sm_call(u32 smc, struct pt_regs *regs, s32 *retval)
+{
+ struct udevice *dev;
+
+ dev = meson_get_sm_device();
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ return sm_call(dev, smc, retval, regs);
+}
+
int meson_sm_pwrdm_set(size_t index, int cmd)
{
struct udevice *dev;
@@ -256,3 +267,31 @@ int meson_sm_pwrdm_set(size_t index, int cmd)

return err;
}
+
+int meson_sm_set_bl1_first_boot_source(u64 cmd)
+{
+ struct pt_regs regs = { 0 };
+
+ regs.regs[1] = cmd;
+
+ return meson_sm_call(MESON_SMC_CMD_BL1_FIRST_BOOT_SRC_SET,
+ &regs, NULL);
+}
+
+int meson_sm_reboot(u64 mode)
+{
+ struct pt_regs regs = { 0 };
+
+ regs.regs[1] = mode;
+
+ return meson_sm_call(MESON_SMC_CMD_REBOOT_MODE_SET, &regs, NULL);
+}
+
+int meson_sm_set_usb_boot_mode(u64 mode)
+{
+ struct pt_regs regs = { 0 };
+
+ regs.regs[1] = mode;
+
+ return meson_sm_call(MESON_SMC_CMD_USB_BOOT_MODE_SET, &regs, NULL);
+}
diff --git a/drivers/sm/meson-sm.c b/drivers/sm/meson-sm.c
index 87eba1486db..f413d8335b3 100644
--- a/drivers/sm/meson-sm.c
+++ b/drivers/sm/meson-sm.c
@@ -168,8 +168,11 @@ static const struct meson_sm_data meson_sm_gxbb_data = {
.cmd = {
SET_CMD(MESON_SMC_CMD_EFUSE_READ, 0x82000030),
SET_CMD(MESON_SMC_CMD_EFUSE_WRITE, 0x82000031),
+ SET_CMD(MESON_SMC_CMD_USB_BOOT_MODE_SET, 0x82000043),
SET_CMD(MESON_SMC_CMD_CHIP_ID_GET, 0x82000044),
+ SET_CMD(MESON_SMC_CMD_BL1_FIRST_BOOT_SRC_SET, 0x82000087),
SET_CMD(MESON_SMC_CMD_PWRDM_SET, 0x82000093),
+ SET_CMD(MESON_SMC_CMD_REBOOT_MODE_SET, 0x84000009),
},
};

diff --git a/include/meson/sm.h b/include/meson/sm.h
index fbaab1f1ee0..b9621f95cbe 100644
--- a/include/meson/sm.h
+++ b/include/meson/sm.h
@@ -13,6 +13,9 @@ enum meson_smc_cmd {
MESON_SMC_CMD_EFUSE_WRITE, /* write efuse memory */
MESON_SMC_CMD_CHIP_ID_GET, /* readh chip unique id */
MESON_SMC_CMD_PWRDM_SET, /* do command at specified power domain */
+ MESON_SMC_CMD_BL1_FIRST_BOOT_SRC_SET, /* setup first boot source for BootROM */
+ MESON_SMC_CMD_REBOOT_MODE_SET, /* Reboot device in desired mode */
+ MESON_SMC_CMD_USB_BOOT_MODE_SET, /* Run boot from USB device */
MESON_SMC_CMD_COUNT,
};

--
2.30.1