[RFC PATCH 00/16] Introduce ICSSG Ethernet driver
Andrew Davis
afd at ti.com
Tue Jan 2 14:56:27 CET 2024
On 12/27/23 12:56 AM, MD Danish Anwar wrote:
>
>
> On 22/12/23 6:13 pm, Roger Quadros wrote:
>>
>> On 22/12/2023 12:26, MD Danish Anwar wrote:
>>> Hi Roger,
>>>
>>> On 20/12/23 3:29 pm, Roger Quadros wrote:
>>>> Hi,
>>>>
>>>> On 19/12/2023 12:11, MD Danish Anwar wrote:
>>>>> Introduce ICSSG PRUETH support in uboot. The ICSSG driver is used in TI
>>>>> AM654 SR2.0.
>>>>>
>>>>> The ICSSG PRU Sub-system runs on EMAC firmware. This series Introduces
>>>>> support for ICSSG driver in uboot. This series also adds the driver's
>>>>> dependencies.
>>>>>
>>>>> The ICSSG2 node is added in device tree overlay so that it remains in
>>>>> sync with linux kernel.
>>>>>
>>>>> The series introduces device tree and config changes and AM65x
>>>>> to enable ICSSG driver. The series also enables SPL_LOAD_FIT_APPLY_OVERLAY
>>>>> for AM65x in order to load overlay over spl.
>>>>>
>>>>> This series has been tested on AM65x SR2.0, and the ICSSG interface is
>>>>> able to ping / dhcp and boot kernel using tftp in uboot.
>>>>>
>>>>> To use ICSSG2 ethernet, the ICSSG firmware needs to be loaded to PRU RPROC
>>>>> cores and RPROC cores need to be booted with the firmware. This step is
>>>>> done inside driver in kernel as kernel supports APIs like
>>>>> rproc_set_firmware() and rproc_fw_boot(). But as u-boot doesn't have these
>>>>> APIs, the same needs to be done via u-boot cmds.
>>>>>
>>>>> To make sure icssg-eth works we need to do below steps.
>>>>>
>>>>> 1. Initialize rproc cores i.e. rproc_init()
>>>>> 2. Load $firmware_file from partition '1:2' (root) on device (mmc in this
>>>>> example)
>>>>> 3. Load the firmware file to rproc cores passing. i.e. rproc_load()
>>>>> taking rproc_id, loadaddr and file size as arguments.
>>>>> 4. Start rproc cores. i.e. rproc_start() taking rproc_id as arguments
>>>>>
>>>>> The above steps are done by running the below commands at u-boot prompt.
>>>>>
>>>>> => setenv start_icssg2 'rproc start 14; rproc start 15; rproc start 16; rproc start 17; rproc start 18; rproc start 19'
>>>>> => setenv stop_icssg2 'rproc stop 14; rproc stop 15; rproc stop 16; rproc stop 17; rproc stop 18; rproc stop 19'
>>>>> => setenv firmware_dir '/lib/firmware/ti-pruss'
>>>>> => setenv get_firmware_mmc 'load mmc ${bootpart} ${loadaddr} ${firmware_dir}/${firmware_file}'
>>>>>
>>>>> => setenv init_icssg2 'setenv ethact icssg2-eth; setenv autoload no; rproc init; setenv loadaddr 0x80000000; \
>>>>> setenv firmware_file am65x-sr2-pru0-prueth-fw.elf; run get_firmware_mmc; rproc load 14 0x80000000 ${filesize}; \
>>>>> setenv loadaddr 0x89000000; setenv firmware_file am65x-sr2-rtu0-prueth-fw.elf; run get_firmware_mmc; rproc load 15 0x89000000 ${filesize}; \
>>>>> setenv loadaddr 0x90000000; setenv firmware_file am65x-sr2-txpru0-prueth-fw.elf; run get_firmware_mmc; rproc load 16 0x90000000 ${filesize}; \
>>>>> setenv loadaddr 0x80000000; setenv firmware_file am65x-sr2-pru1-prueth-fw.elf; run get_firmware_mmc; rproc load 17 0x80000000 ${filesize}; \
>>>>> setenv loadaddr 0x89000000; setenv firmware_file am65x-sr2-rtu1-prueth-fw.elf; run get_firmware_mmc; rproc load 18 0x89000000 ${filesize}; \
>>>>> setenv loadaddr 0x90000000; setenv firmware_file am65x-sr2-txpru1-prueth-fw.elf; run get_firmware_mmc; rproc load 19 0x90000000 ${filesize}; \
>>>>> run start_icssg2;'
>>>>
>>>> A whole bunch of commands are required to get ethernet functional.
>>>> This is not at all user friendly and will be a maintenance nightmare.
>>>> What worries me is tracking the 6 different rproc cores and the 6 different firmware files to start 1 ethernet device.
>>>> This will get even more interesting when we have to deal with different ICSSG instances on different boards.
>>>>
>>>> What is preventing the driver from starting the rproc cores it needs so user doesn't need to care about it?
>>>> All the necessary information is in the Device tree. At least this is how it is done on Linux.
>>>>
>>>
>>> I tried removing the need for these commands and implementing them
>>> inside the driver only. I am able to load the firmware from driver using
>>> the fs_loader API request_firmware_into_buf(). It requires changes to
>>> dt. A DT node called fs-loader needs to be added also CONFIG_FS_LOADER
>>> needs to enabled. In the DT node we need to specify the storage media
>>> that we are using i.e. mmc, ospi, usb. It's upto user to modify the
>>> storage media, the driver will take the media from DT and try to laod
>>> firmware from their.
>>>
>>> For loading firmwares to rproc cores, rproc_load() API is needed. Now
>>> this API takes rproc_id, loadaddr and firmware_size as arguments.
>>> loadaddr is fixed for all three pru cores. firmware_size is obtained
>>> from request_firmware_into_buf() but I couldn't find a way to get the
>>> rproc_id. For now based on the ICSSG instance and slice number I am
>>> figuring out the rproc_id and passing it to rproc_load() and
>>> rproc_start() APIs. Please let me know if you have any other / better
>>> way of finding rproc_id.
>>>
>>> Below is the entire diff to remove these commands and move their
>>> functionality to driver. Please have a look and let me know if this is ok.
>>>
>>
>> Good to see you got something working so quickly.
>> It has some rough edges but nothing that is blocking.
>>
>>>
>>> Save New Duplicate & Edit Just Text Twitter
>>> diff --git a/arch/arm/dts/k3-am654-base-board.dts
>>> b/arch/arm/dts/k3-am654-base-board.dts
>>> index cfbcebfa37..c8da72e49c 100644
>>> --- a/arch/arm/dts/k3-am654-base-board.dts
>>> +++ b/arch/arm/dts/k3-am654-base-board.dts
>>> @@ -16,6 +16,13 @@
>>> chosen {
>>> stdout-path = "serial2:115200n8";
>>> bootargs = "earlycon=ns16550a,mmio32,0x02800000";
>>> + firmware-loader = <&fs_loader0>;
>>> + };
>>> +
>>> + fs_loader0: fs-loader {
>>> + bootph-all;
>>> + compatible = "u-boot,fs-loader";
>>> + phandlepart = <&sdhci1 2>;
>>> };
>>
>> This is has 2 issues
>> 1) It will not be accepted in Kernel DT. Maybe it could be done in -u-boot.dtsi file.
>
> Sure. I'll move this to k3-am654-base-board-u-boot.dtsi
>
>> 2) You cannot assume boot medium is always sdhci1 2
>>
>
> Yes. It's upto user to provide the boot medium and boot partition for
> loading firmware. By default the firmware is loacated in root partion of
> shdci1 on am65x so I am adding this as default. But user can easily
> modify this to any other medium / partition needed.
>
Users should not have to modify DT to pick a boot medium/partition.
What would you do for complex cases or non block devices like eth/uart
booting? This is one reason kernel moved firmware loading to
userspace. The equivalant in U-Boot is the shell and scripts. Your
original command based loading was the correct solution IMHO.
If we want to try to hide some of that then we need a way to
run a user provided script from the environement to handle
the general cases for firmware loading.
Andrew
>>>
>>> memory at 80000000 {
>>> diff --git a/configs/am65x_evm_a53_defconfig
>>> b/configs/am65x_evm_a53_defconfig
>>> index 2755d7082f..c53e938abb 100644
>>> --- a/configs/am65x_evm_a53_defconfig
>>> +++ b/configs/am65x_evm_a53_defconfig
>>> @@ -112,6 +112,7 @@ CONFIG_DM_I2C_GPIO=y
>>> CONFIG_SYS_I2C_OMAP24XX=y
>>> CONFIG_DM_MAILBOX=y
>>> CONFIG_K3_SEC_PROXY=y
>>> +CONFIG_FS_LOADER=y
>>> CONFIG_SUPPORT_EMMC_BOOT=y
>>> CONFIG_MMC_IO_VOLTAGE=y
>>> CONFIG_MMC_UHS_SUPPORT=y
>>> diff --git a/configs/am65x_evm_r5_defconfig b/configs/am65x_evm_r5_defconfig
>>> index b2f1e721b3..2d19935a41 100644
>>> --- a/configs/am65x_evm_r5_defconfig
>>> +++ b/configs/am65x_evm_r5_defconfig
>>> @@ -98,6 +98,7 @@ CONFIG_I2C_SET_DEFAULT_BUS_NUM=y
>>> CONFIG_SYS_I2C_OMAP24XX=y
>>> CONFIG_DM_MAILBOX=y
>>> CONFIG_K3_SEC_PROXY=y
>>> +CONFIG_FS_LOADER=y
>>> CONFIG_K3_AVS0=y
>>> CONFIG_SUPPORT_EMMC_BOOT=y
>>> CONFIG_MMC_HS200_SUPPORT=y
>>> diff --git a/drivers/net/ti/icssg_prueth.c b/drivers/net/ti/icssg_prueth.c
>>> index 40ad827e49..1c4edeb7b7 100644
>>> --- a/drivers/net/ti/icssg_prueth.c
>>> +++ b/drivers/net/ti/icssg_prueth.c
>>> @@ -227,6 +227,10 @@ static int prueth_start(struct udevice *dev)
>>> void *config;
>>> int ret, i;
>>>
>>> + ret = icssg_start_pru_cores(dev);
>>> + if (ret)
>>> + return ret;
>>> +
>>> /* To differentiate channels for SLICE0 vs SLICE1 */
>>> snprintf(chn_name, sizeof(chn_name), "tx%d-0", priv->slice);
>>>
>>> @@ -355,9 +359,11 @@ static void prueth_stop(struct udevice *dev)
>>> phy_shutdown(priv->phydev);
>>>
>>> dma_disable(&priv->dma_tx);
>>> - dma_free(&priv->dma_tx);
>>> -
>>> dma_disable(&priv->dma_rx);
>>> +
>>> + icssg_stop_pru_cores(dev);
>>> +
>>> + dma_free(&priv->dma_tx);
>>> dma_free(&priv->dma_rx);
>>> }
>>>
>>> @@ -434,6 +440,181 @@ static const struct soc_attr k3_mdio_soc_data[] = {
>>> { /* sentinel */ },
>>> };
>>>
>>> +struct icssg_firmware_load_address {
>>> + u32 pru;
>>> + u32 rtu;
>>> + u32 txpru;
>>> +};
>>> +
>>> +struct icssg_firmwares {
>>> + char *pru;
>>> + char *rtu;
>>> + char *txpru;
>>> +};
>>> +
>>> +static struct icssg_firmwares icssg_emac_firmwares[] = {
>>> + {
>>> + .pru = "/lib/firmware/ti-pruss/am65x-sr2-pru0-prueth-fw.elf",
>>> + .rtu = "/lib/firmware/ti-pruss/am65x-sr2-rtu0-prueth-fw.elf",
>>> + .txpru = "/lib/firmware/ti-pruss/am65x-sr2-txpru0-prueth-fw.elf",
>>> + },
>>> + {
>>> + .pru = "/lib/firmware/ti-pruss/am65x-sr2-pru1-prueth-fw.elf",
>>> + .rtu = "/lib/firmware/ti-pruss/am65x-sr2-rtu1-prueth-fw.elf",
>>> + .txpru = "/lib/firmware/ti-pruss/am65x-sr2-txpru1-prueth-fw.elf",
>>> + }
>>> +};
>>
>> This information is contained in the DT.
>>
>> firmware-name = "ti-pruss/am65x-sr2-pru0-prueth-fw.elf",
>> "ti-pruss/am65x-sr2-rtu0-prueth-fw.elf",
>> "ti-pruss/am65x-sr2-txpru0-prueth-fw.elf",
>> "ti-pruss/am65x-sr2-pru1-prueth-fw.elf",
>> "ti-pruss/am65x-sr2-rtu1-prueth-fw.elf",
>> "ti-pruss/am65x-sr2-txpru1-prueth-fw.elf";
>>
>
> Yes it is. But the linux driver uses the firmware strcuture inside
> driver only. The firmware-name property is not being used. The firmware
> names for all applicable SoCs (AM65x and AM64x) is same so instead of
> reading it from DT, the driver hardcodes the firmware names in linux. I
> think the thought here was to use firmware-name property from DT when
> different firmwares are needed for different SoCs. I am just following
> the same here in uboot as well.
>
>> You will need to introduce a rproc_set_firmware() API so clients can
>> set their own firmware names.
>>
>
> Sure. I will add an element 'fw_name' in struct dm_rproc_uclass_pdata
> and set this fw_name to the firmware name requested by client using
> rproc_set_firmware().
>
> The client drivers can call rproc_set_firmware(dev, firmware_name) to
> set the firmware name.
>
>>> +
>>> +int load_firmware(char *name_fw, u32 *loadaddr)
>>> +{
>>> + struct udevice *fsdev;
>>> + int size = 0;
>>> +
>>> + if (!IS_ENABLED(CONFIG_FS_LOADER))
>>> + return -EINVAL;
>>> +
>>> + if (!*loadaddr)
>>> + return -EINVAL;
>>> +
>>> + if (!get_fs_loader(&fsdev))
>>> + size = request_firmware_into_buf(fsdev, name_fw, (void *)*loadaddr,
>>> 40524, 0);
>>> +
>>> + return size;
>>> +}
>>
>> On Linux rproc_boot() does both loading the firmware and starting the rproc
>> as that is farely generic.
>> You should introduce rproc_boot() API so loading is taken care of at rproc driver.
>> All you need to do is call rproc_set_firmware() before rproc_boot().
>>
>
> Sure. I'll introduce rproc_boot() which will load the firmware to a
> buffer and then load the firmware / buffer to rproc core usinig rproc_load.
>
> For allocating memory for reading firmware we need to provide the size
> of memory required as well. So rproc_boot will take two arguments.
> 1. dev (rproc device)
> 2. size (Maximum memory required to read firmware)
>
>>> +
>>> +static int icssg_get_instance(struct udevice *dev)
>>> +{
>>> + if (!strcmp(dev->name, "icssg2-eth"))
>>> + return 2;
>>> + else if (!strcmp(dev->name, "icssg1-eth"))
>>> + return 1;
>>> + else if (!strcmp(dev->name, "icssg0-eth"))
>>> + return 0;
>>> +
>>> + dev_err(dev, "Invalid icssg instance\n");
>>> + return -EINVAL;
>>> +}
>>> +
>>> +static int icssg_get_pru_core_number(struct udevice *dev, int slice)
>>> +{
>>> + int instance, num_r5_cores;
>>> +
>>> + instance = icssg_get_instance(dev);
>>> + if (instance < 0)
>>> + return instance;
>>> +
>>> + if (IS_ENABLED(CONFIG_REMOTEPROC_TI_K3_R5F))
>>> + num_r5_cores = 2;
>>> +
>>> + return num_r5_cores +
>>> + instance * PRU_TYPE_MAX * PRUETH_NUM_MACS +
>>> + slice * PRU_TYPE_MAX;
>>
>> All this doesn't look right. What we need is the rproc device
>> that matches the PRU/RTU rprocs that we are interested in.
>>
>> The DT already has this information
>>
>> ti,prus = <&pru2_0>, <&rtu2_0>, <&tx_pru2_0>,
>> <&pru2_1>, <&rtu2_1>, <&tx_pru2_1>;
>>
>>
>> All the current rproc APIs use the below to get rproc device from ID
>> ret = uclass_get_device_by_seq(UCLASS_REMOTEPROC, id, &dev);
>>
>> You just need to introduce APIs that takes rproc device directly as argument.
>>
>> In your driver you can call uclass_get_device_by_phandle_id() to get the
>> rproc device from the rproc phandle?
>
> Sure. I'll do that. Based on the icssg slice first I will get the
> phandle for prus.
>
> Index will be 0,1,2 for slice 0 and 3,4,5 for slice 1.
>
> ofnode_read_u32_index(dev_ofnode(dev), "ti,prus", index, &phandle);
>
> Then for each phandle I can call uclass_get_device_by_phandle_id() and
> get the rproc device.
>
>>
>>
>>> +}
>>> +
>>> +int icssg_start_pru_cores(struct udevice *dev)
>>> +{
>>> + struct prueth *prueth = dev_get_priv(dev);
>>> + struct icssg_firmwares *firmwares;
>>> + u32 pru_fw_loadaddr = 0x80000000;
>>> + u32 rtu_fw_loadaddr = 0x89000000;
>>> + u32 txpru_fw_loadaddr = 0x90000000;
>>
>> Please avoid hardcoding. You can use malloc to get a temporary buffer area?
>>
>> Why do you need 3 different addresses?
>> Once you do a rproc_load isn't the buffer already copied to rproc's memory
>> so you can discard it or use it for the other rprocs?
>>
>
> Sure I'll use malloc to get temporary buffer. Only thing is that to get
> temporary buffer I will need to provide size as well.
>
> void *addr = malloc(fw_size);
>
> User needs to provide this fw_size to rproc_boot(). In my case the max
> size the firmware can have is 64KB so I will be passing 64K as size to
> rproc driver.
>
>>> + int slice, ret, core_id;
>>> +
>>> + firmwares = icssg_emac_firmwares;
>>> + slice = prueth->slice;
>>> +
>>> + core_id = icssg_get_pru_core_number(dev, slice);
>>> + if (core_id < 0)
>>> + return core_id;
>>> +
>>> + /* Initialize all rproc cores */
>>> + rproc_init();
>>> +
>>> + /* Loading PRU firmware to PRU core */
>>> + ret = load_firmware(firmwares[slice].pru, &pru_fw_loadaddr);
>>
>> On Linux, loading the firmware is the responsibility of the rproc driver.
>> Shouldn't it be the same in u-boot?
>>
>
> Sure I'll move this to rproc driver.
>
>>> +
>>> + if (ret < 0) {
>>> + dev_err(dev, "Unable to load_firmware %s at addr 0x%x err %d\n",
>>> + firmwares[slice].pru, pru_fw_loadaddr, ret);
>>> + return ret;
>>> + }
>>> +
>>> + dev_info(dev, "Loaded FW %s successfully at addr 0x%x Size = %d Bytes\n",
>>> + firmwares[slice].pru, pru_fw_loadaddr, ret);
>>
>> dev_dbg() here an at all dev_info().
>>
>>> + rproc_load(core_id + PRU_TYPE_PRU, pru_fw_loadaddr, ret);
>>> +
>>> + /* Loading RTU firmware to RTU core */
>>> + ret = load_firmware(firmwares[slice].rtu, &rtu_fw_loadaddr);
>>> +
>>> + if (ret < 0) {
>>> + dev_err(dev, "Unable to load_firmware %s at addr 0x%x err %d\n",
>>> + firmwares[slice].rtu, rtu_fw_loadaddr, ret);
>>> + return ret;
>>> + }
>>> +
>>> + dev_info(dev, "Loaded FW %s successfully at addr 0x%x Size = %d Bytes\n",
>>> + firmwares[slice].rtu, rtu_fw_loadaddr, ret);
>>> + rproc_load(core_id + PRU_TYPE_RTU, rtu_fw_loadaddr, ret);
>>> +
>>> + /* Loading TX_PRU firmware to TX_PRU core */
>>> + ret = load_firmware(firmwares[slice].txpru, &txpru_fw_loadaddr);
>>> +
>>> + if (ret < 0) {
>>> + dev_err(dev, "Unable to load_firmware %s at addr 0x%x err %d\n",
>>> + firmwares[slice].txpru, txpru_fw_loadaddr, ret);
>>> + return ret;
>>> + }
>>> +
>>> + dev_info(dev, "Loaded FW %s successfully at addr 0x%x Size = %d Bytes\n",
>>> + firmwares[slice].txpru, txpru_fw_loadaddr, ret);
>>> + rproc_load(core_id + PRU_TYPE_TX_PRU, txpru_fw_loadaddr, ret);
>>> +
>>> + ret = rproc_start(core_id + PRU_TYPE_PRU);
>>> + if (ret) {
>>> + dev_err(dev, "failed to start PRU%d: %d\n", slice, ret);
>>> + return ret;
>>> + }
>>> +
>>> + ret = rproc_start(core_id + PRU_TYPE_RTU);
>>> + if (ret) {
>>> + dev_err(dev, "failed to start RTU%d: %d\n", slice, ret);
>>> + goto halt_pru;
>>> + }
>>> +
>>> + ret = rproc_start(core_id + PRU_TYPE_TX_PRU);
>>> + if (ret) {
>>> + dev_err(dev, "failed to start TX_PRU%d: %d\n", slice, ret);
>>> + goto halt_rtu;
>>> + }
>>> +
>>> + return 0;
>>> +
>>> +halt_rtu:
>>> + rproc_stop(core_id + PRU_TYPE_RTU);
>>> +
>>> +halt_pru:
>>> + rproc_stop(PRU_TYPE_PRU);
>>> + return ret;
>>> +}
>>> +
>>> +int icssg_stop_pru_cores(struct udevice *dev)
>>> +{
>>> + struct prueth *prueth = dev_get_priv(dev);
>>> + int slice, core_id;
>>> +
>>> + slice = prueth->slice;
>>> +
>>> + core_id = icssg_get_pru_core_number(dev, slice);
>>> + if (core_id < 0)
>>> + return core_id;
>>> +
>>> + rproc_stop(core_id + PRU_TYPE_PRU);
>>> + rproc_stop(core_id + PRU_TYPE_RTU);
>>> + rproc_stop(core_id + PRU_TYPE_TX_PRU);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> static int prueth_probe(struct udevice *dev)
>>> {
>>> ofnode eth_ports_node, eth0_node, eth1_node, eth_node;
>>> diff --git a/include/linux/pruss_driver.h b/include/linux/pruss_driver.h
>>> index 25272e850e..f17fe8bf58 100644
>>> --- a/include/linux/pruss_driver.h
>>> +++ b/include/linux/pruss_driver.h
>>> @@ -114,6 +114,21 @@ enum pru_ctable_idx {
>>> PRU_C31,
>>> };
>>>
>>> +/**
>>> + * enum pru_type - PRU core type identifier
>>> + *
>>> + * @PRU_TYPE_PRU: Programmable Real-time Unit
>>> + * @PRU_TYPE_RTU: Auxiliary Programmable Real-Time Unit
>>> + * @PRU_TYPE_TX_PRU: Transmit Programmable Real-Time Unit
>>> + * @PRU_TYPE_MAX: just keep this one at the end
>>> + */
>>> +enum pru_type {
>>> + PRU_TYPE_PRU,
>>> + PRU_TYPE_RTU,
>>> + PRU_TYPE_TX_PRU,
>>> + PRU_TYPE_MAX,
>>> +};
>>> +
>>> /**
>>> * enum pruss_mem - PRUSS memory range identifiers
>>> */
>>>
>>> with this diff, user don't need to run any extra commands at u-boot.
>>> Once u-boot prompt is reached, just running ping / dhcp will suffice.
>>>
>> Great!
>>
>> <snip>
>>
>
> I have addressed all the above comments and made the changes needed.
> below is the diff. Please have a look at it and let me know if it looks OK.
>
> diff --git a/arch/arm/dts/k3-am654-base-board-u-boot.dtsi
> b/arch/arm/dts/k3-am654-base-board-u-boot.dtsi
> index 11d83927ac..0b2b72b2c5 100644
> --- a/arch/arm/dts/k3-am654-base-board-u-boot.dtsi
> +++ b/arch/arm/dts/k3-am654-base-board-u-boot.dtsi
> @@ -6,6 +6,18 @@
> #include "k3-am654-r5-base-board-u-boot.dtsi"
> #include "k3-am65x-binman.dtsi"
>
> +/ {
> + chosen {
> + firmware-loader = <&fs_loader0>;
> + };
> +
> + fs_loader0: fs-loader {
> + bootph-all;
> + compatible = "u-boot,fs-loader";
> + phandlepart = <&sdhci1 2>;
> + };
> +};
> +
> &pru0_0 {
> remoteproc-name = "pru0_0";
> };
> diff --git a/configs/am65x_evm_a53_defconfig
> b/configs/am65x_evm_a53_defconfig
> index 2755d7082f..c53e938abb 100644
> --- a/configs/am65x_evm_a53_defconfig
> +++ b/configs/am65x_evm_a53_defconfig
> @@ -112,6 +112,7 @@ CONFIG_DM_I2C_GPIO=y
> CONFIG_SYS_I2C_OMAP24XX=y
> CONFIG_DM_MAILBOX=y
> CONFIG_K3_SEC_PROXY=y
> +CONFIG_FS_LOADER=y
> CONFIG_SUPPORT_EMMC_BOOT=y
> CONFIG_MMC_IO_VOLTAGE=y
> CONFIG_MMC_UHS_SUPPORT=y
> diff --git a/configs/am65x_evm_r5_defconfig b/configs/am65x_evm_r5_defconfig
> index b2f1e721b3..2d19935a41 100644
> --- a/configs/am65x_evm_r5_defconfig
> +++ b/configs/am65x_evm_r5_defconfig
> @@ -98,6 +98,7 @@ CONFIG_I2C_SET_DEFAULT_BUS_NUM=y
> CONFIG_SYS_I2C_OMAP24XX=y
> CONFIG_DM_MAILBOX=y
> CONFIG_K3_SEC_PROXY=y
> +CONFIG_FS_LOADER=y
> CONFIG_K3_AVS0=y
> CONFIG_SUPPORT_EMMC_BOOT=y
> CONFIG_MMC_HS200_SUPPORT=y
> diff --git a/drivers/net/ti/icssg_prueth.c b/drivers/net/ti/icssg_prueth.c
> index 40ad827e49..a2e3595956 100644
> --- a/drivers/net/ti/icssg_prueth.c
> +++ b/drivers/net/ti/icssg_prueth.c
> @@ -62,6 +62,9 @@
> /* Management packet type */
> #define PRUETH_PKT_TYPE_CMD 0x10
>
> +/* Number of PRU Cores per Slice */
> +#define ICSSG_NUM_PRU_CORES 3
> +
> static int icssg_phy_init(struct udevice *dev)
> {
> struct prueth *priv = dev_get_priv(dev);
> @@ -218,6 +221,116 @@ static int icssg_update_link(struct prueth *priv)
> return phy->link;
> }
>
> +struct icssg_firmwares {
> + char *pru;
> + char *rtu;
> + char *txpru;
> +};
> +
> +static struct icssg_firmwares icssg_emac_firmwares[] = {
> + {
> + .pru = "/lib/firmware/ti-pruss/am65x-sr2-pru0-prueth-fw.elf",
> + .rtu = "/lib/firmware/ti-pruss/am65x-sr2-rtu0-prueth-fw.elf",
> + .txpru = "/lib/firmware/ti-pruss/am65x-sr2-txpru0-prueth-fw.elf",
> + },
> + {
> + .pru = "/lib/firmware/ti-pruss/am65x-sr2-pru1-prueth-fw.elf",
> + .rtu = "/lib/firmware/ti-pruss/am65x-sr2-rtu1-prueth-fw.elf",
> + .txpru = "/lib/firmware/ti-pruss/am65x-sr2-txpru1-prueth-fw.elf",
> + }
> +};
> +
> +static int icssg_start_pru_cores(struct udevice *dev)
> +{
> + struct prueth *prueth = dev_get_priv(dev);
> + struct icssg_firmwares *firmwares;
> + struct udevice *rproc_dev = NULL;
> + int ret, slice;
> + u32 phandle;
> + u8 index;
> +
> + slice = prueth->slice;
> + index = slice * ICSSG_NUM_PRU_CORES;
> + firmwares = icssg_emac_firmwares;
> +
> + ofnode_read_u32_index(dev_ofnode(dev), "ti,prus", index, &phandle);
> + ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle,
> &rproc_dev);
> + if (ret) {
> + dev_err(dev, "Unknown remote processor with phandle '0x%x'
> requested(%d)\n",
> + phandle, ret);
> + return ret;
> + }
> +
> + prueth->pru_core_id = dev_seq(rproc_dev);
> + ret = rproc_set_firmware(rproc_dev, firmwares[slice].pru);
> + if (ret)
> + return ret;
> +
> + ret = rproc_boot(rproc_dev, SZ_64K);
> + if (ret) {
> + dev_err(dev, "failed to boot PRU%d: %d\n", slice, ret);
> + return -EINVAL;
> + }
> +
> + ofnode_read_u32_index(dev_ofnode(dev), "ti,prus", index + 1, &phandle);
> + ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle,
> &rproc_dev);
> + if (ret) {
> + dev_err(dev, "Unknown remote processor with phandle '0x%x'
> requested(%d)\n",
> + phandle, ret);
> + goto halt_pru;
> + }
> +
> + prueth->rtu_core_id = dev_seq(rproc_dev);
> + ret = rproc_set_firmware(rproc_dev, firmwares[slice].rtu);
> + if (ret)
> + goto halt_pru;
> +
> + ret = rproc_boot(rproc_dev, SZ_64K);
> + if (ret) {
> + dev_err(dev, "failed to boot RTU%d: %d\n", slice, ret);
> + goto halt_pru;
> + }
> +
> + ofnode_read_u32_index(dev_ofnode(dev), "ti,prus", index + 2, &phandle);
> + ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle,
> &rproc_dev);
> + if (ret) {
> + dev_err(dev, "Unknown remote processor with phandle '0x%x'
> requested(%d)\n",
> + phandle, ret);
> + goto halt_rtu;
> + }
> +
> + prueth->txpru_core_id = dev_seq(rproc_dev);
> + ret = rproc_set_firmware(rproc_dev, firmwares[slice].txpru);
> + if (ret)
> + goto halt_rtu;
> +
> + ret = rproc_boot(rproc_dev, SZ_64K);
> + if (ret) {
> + dev_err(dev, "failed to boot TXPRU%d: %d\n", slice, ret);
> + goto halt_rtu;
> + }
> +
> + return 0;
> +
> +halt_rtu:
> + rproc_stop(prueth->rtu_core_id);
> +
> +halt_pru:
> + rproc_stop(prueth->pru_core_id);
> + return ret;
> +}
> +
> +static int icssg_stop_pru_cores(struct udevice *dev)
> +{
> + struct prueth *prueth = dev_get_priv(dev);
> +
> + rproc_stop(prueth->pru_core_id);
> + rproc_stop(prueth->rtu_core_id);
> + rproc_stop(prueth->txpru_core_id);
> +
> + return 0;
> +}
> +
> static int prueth_start(struct udevice *dev)
> {
> struct ti_udma_drv_chan_cfg_data *dma_rx_cfg_data;
> @@ -227,6 +340,10 @@ static int prueth_start(struct udevice *dev)
> void *config;
> int ret, i;
>
> + ret = icssg_start_pru_cores(dev);
> + if (ret)
> + return ret;
> +
> /* To differentiate channels for SLICE0 vs SLICE1 */
> snprintf(chn_name, sizeof(chn_name), "tx%d-0", priv->slice);
>
> @@ -355,9 +472,11 @@ static void prueth_stop(struct udevice *dev)
> phy_shutdown(priv->phydev);
>
> dma_disable(&priv->dma_tx);
> - dma_free(&priv->dma_tx);
> -
> dma_disable(&priv->dma_rx);
> +
> + icssg_stop_pru_cores(dev);
> +
> + dma_free(&priv->dma_tx);
> dma_free(&priv->dma_rx);
> }
>
> diff --git a/drivers/net/ti/icssg_prueth.h b/drivers/net/ti/icssg_prueth.h
> index e41ed16a05..c92660401b 100644
> --- a/drivers/net/ti/icssg_prueth.h
> +++ b/drivers/net/ti/icssg_prueth.h
> @@ -69,6 +69,9 @@ struct prueth {
> int speed;
> int duplex;
> u8 icssg_hwcmdseq;
> + u8 pru_core_id;
> + u8 rtu_core_id;
> + u8 txpru_core_id;
> };
>
> /* config helpers */
> diff --git a/drivers/remoteproc/rproc-uclass.c
> b/drivers/remoteproc/rproc-uclass.c
> index 28b362c887..a085c44eaa 100644
> --- a/drivers/remoteproc/rproc-uclass.c
> +++ b/drivers/remoteproc/rproc-uclass.c
> @@ -13,6 +13,7 @@
> #include <log.h>
> #include <malloc.h>
> #include <virtio_ring.h>
> +#include <fs_loader.h>
> #include <remoteproc.h>
> #include <asm/io.h>
> #include <dm/device-internal.h>
> @@ -961,3 +962,90 @@ unsigned long rproc_parse_resource_table(struct
> udevice *dev, struct rproc *cfg)
>
> return 1;
> }
> +
> +int rproc_set_firmware(struct udevice *rproc_dev, const char *fw_name)
> +{
> + struct dm_rproc_uclass_pdata *uc_pdata;
> + int len;
> + char *p;
> +
> + if (!rproc_dev || !fw_name)
> + return -EINVAL;
> +
> + uc_pdata = dev_get_uclass_plat(rproc_dev);
> +
> + len = strcspn(fw_name, "\n");
> + if (!len) {
> + debug("can't provide empty string for firmware name\n");
> + return -EINVAL;
> + }
> +
> + p = strndup(fw_name, len);
> + if (!p)
> + return -ENOMEM;
> +
> + uc_pdata->fw_name = p;
> +
> + return 0;
> +}
> +
> +int rproc_boot(struct udevice *rproc_dev, size_t fw_size)
> +{
> + struct dm_rproc_uclass_pdata *uc_pdata;
> + struct udevice *fs_loader;
> + void *addr = malloc(fw_size);
> + int core_id, ret = 0;
> + char *firmware;
> + ulong rproc_addr;
> +
> + if (!rproc_dev)
> + return -EINVAL;
> +
> + if (!addr)
> + return -ENOMEM;
> +
> + uc_pdata = dev_get_uclass_plat(rproc_dev);
> + core_id = dev_seq(rproc_dev);
> + firmware = uc_pdata->fw_name;
> +
> + if (!firmware) {
> + debug("No firmware set for rproc core %d\n", core_id);
> + return -EINVAL;
> + }
> +
> + /* Initialize all rproc cores */
> + rproc_init();
> +
> + /* Loading firmware to a given address */
> + ret = get_fs_loader(&fs_loader);
> + if (ret) {
> + debug("could not get fs loader: %d\n", ret);
> + return ret;
> + }
> +
> + ret = request_firmware_into_buf(fs_loader, firmware, addr, fw_size, 0);
> + if (ret < 0) {
> + debug("could not request %s: %d\n", firmware, ret);
> + return ret;
> + }
> +
> + rproc_addr = (ulong)addr;
> +
> + debug("Loaded %s to 0x%08lX size = %d Bytes\n",
> + uc_pdata->fw_name, rproc_addr, ret);
> +
> + ret = rproc_load(core_id, rproc_addr, ret);
> + if (ret) {
> + debug("failed to load %s to rproc core %d from addr 0x%08lX err %d\n",
> + uc_pdata->fw_name, core_id, rproc_addr, ret);
> + return ret;
> + }
> +
> + ret = rproc_start(core_id);
> + if (ret) {
> + debug("failed to start rproc core %d\n", core_id);
> + return ret;
> + }
> +
> + return ret;
> +}
> diff --git a/include/remoteproc.h b/include/remoteproc.h
> index a11dc8a9b6..65b0ff7477 100644
> --- a/include/remoteproc.h
> +++ b/include/remoteproc.h
> @@ -402,6 +402,7 @@ enum rproc_mem_type {
> * @name: Platform-specific way of naming the Remote proc
> * @mem_type: one of 'enum rproc_mem_type'
> * @driver_plat_data: driver specific platform data that may be needed.
> + * @fw_name: firmware name
> *
> * This can be accessed with dev_get_uclass_plat() for any
> UCLASS_REMOTEPROC
> * device.
> @@ -411,6 +412,7 @@ struct dm_rproc_uclass_pdata {
> const char *name;
> enum rproc_mem_type mem_type;
> void *driver_plat_data;
> + char *fw_name;
> };
>
> /**
> @@ -704,6 +706,35 @@ unsigned long rproc_parse_resource_table(struct
> udevice *dev,
> struct resource_table *rproc_find_resource_table(struct udevice *dev,
> unsigned int addr,
> int *tablesz);
> +/**
> + * rproc_set_firmware() - assign a new firmware
> + * @rproc_dev: device for wich new firmware is being assigned
> + * @fw_name: new firmware name to be assigned
> + *
> + * This function allows remoteproc drivers or clients to configure a custom
> + * firmware name. The function does not trigger a remote processor boot,
> + * only sets the firmware name used for a subsequent boot.
> + *
> + * This function sets the fw_name field in uclass pdata of the Remote proc
> + *
> + * Return: 0 on success or a negative value upon failure
> + */
> +int rproc_set_firmware(struct udevice *rproc_dev, const char *fw_name);
> +
> +/**
> + * rproc_boot() - boot a remote processor
> + * @rproc_dev: rproc device to boot
> + * @fw_size: Size of the memory to allocate for firmeware
> + *
> + * Boot a remote processor (i.e. load its firmware, power it on, ...).
> + *
> + * This function first loads the firmware set in the uclass pdata of Remote
> + * processor to a buffer and then loads firmware to the remote processor
> + * using rproc_load().
> + *
> + * Return: 0 on success, and an appropriate error value otherwise
> + */
> +int rproc_boot(struct udevice *rproc_dev, size_t fw_size);
> #else
> static inline int rproc_init(void) { return -ENOSYS; }
> static inline int rproc_dev_init(int id) { return -ENOSYS; }
> @@ -743,6 +774,10 @@ static inline int rproc_elf_load_rsc_table(struct
> udevice *dev, ulong fw_addr,
> ulong fw_size, ulong *rsc_addr,
> ulong *rsc_size)
> { return -ENOSYS; }
> +static inline int rproc_set_firmware(struct udevice *rproc_dev, const
> char *fw_name)
> +{ return -ENOSYS; }
> +static inline int rproc_boot(struct udevice *rproc_dev, size_t fw_size)
> +{ return -ENOSYS; }
> #endif
>
> #endif /* _RPROC_H_ */
>
>
More information about the U-Boot
mailing list