149ce92fbSShannon Nelson // SPDX-License-Identifier: GPL-2.0
249ce92fbSShannon Nelson /* Copyright(c) 2023 Advanced Micro Devices, Inc */
349ce92fbSShannon Nelson
449ce92fbSShannon Nelson #include "core.h"
549ce92fbSShannon Nelson
649ce92fbSShannon Nelson /* The worst case wait for the install activity is about 25 minutes when
749ce92fbSShannon Nelson * installing a new CPLD, which is very seldom. Normal is about 30-35
849ce92fbSShannon Nelson * seconds. Since the driver can't tell if a CPLD update will happen we
949ce92fbSShannon Nelson * set the timeout for the ugly case.
1049ce92fbSShannon Nelson */
1149ce92fbSShannon Nelson #define PDSC_FW_INSTALL_TIMEOUT (25 * 60)
1249ce92fbSShannon Nelson #define PDSC_FW_SELECT_TIMEOUT 30
1349ce92fbSShannon Nelson
1449ce92fbSShannon Nelson /* Number of periodic log updates during fw file download */
1549ce92fbSShannon Nelson #define PDSC_FW_INTERVAL_FRACTION 32
1649ce92fbSShannon Nelson
pdsc_devcmd_fw_download_locked(struct pdsc * pdsc,u64 addr,u32 offset,u32 length)1749ce92fbSShannon Nelson static int pdsc_devcmd_fw_download_locked(struct pdsc *pdsc, u64 addr,
1849ce92fbSShannon Nelson u32 offset, u32 length)
1949ce92fbSShannon Nelson {
2049ce92fbSShannon Nelson union pds_core_dev_cmd cmd = {
2149ce92fbSShannon Nelson .fw_download.opcode = PDS_CORE_CMD_FW_DOWNLOAD,
2249ce92fbSShannon Nelson .fw_download.offset = cpu_to_le32(offset),
2349ce92fbSShannon Nelson .fw_download.addr = cpu_to_le64(addr),
2449ce92fbSShannon Nelson .fw_download.length = cpu_to_le32(length),
2549ce92fbSShannon Nelson };
2649ce92fbSShannon Nelson union pds_core_dev_comp comp;
2749ce92fbSShannon Nelson
2849ce92fbSShannon Nelson return pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
2949ce92fbSShannon Nelson }
3049ce92fbSShannon Nelson
pdsc_devcmd_fw_install(struct pdsc * pdsc)3149ce92fbSShannon Nelson static int pdsc_devcmd_fw_install(struct pdsc *pdsc)
3249ce92fbSShannon Nelson {
3349ce92fbSShannon Nelson union pds_core_dev_cmd cmd = {
3449ce92fbSShannon Nelson .fw_control.opcode = PDS_CORE_CMD_FW_CONTROL,
3549ce92fbSShannon Nelson .fw_control.oper = PDS_CORE_FW_INSTALL_ASYNC
3649ce92fbSShannon Nelson };
3749ce92fbSShannon Nelson union pds_core_dev_comp comp;
3849ce92fbSShannon Nelson int err;
3949ce92fbSShannon Nelson
4049ce92fbSShannon Nelson err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
4149ce92fbSShannon Nelson if (err < 0)
4249ce92fbSShannon Nelson return err;
4349ce92fbSShannon Nelson
4449ce92fbSShannon Nelson return comp.fw_control.slot;
4549ce92fbSShannon Nelson }
4649ce92fbSShannon Nelson
pdsc_devcmd_fw_activate(struct pdsc * pdsc,enum pds_core_fw_slot slot)4749ce92fbSShannon Nelson static int pdsc_devcmd_fw_activate(struct pdsc *pdsc,
4849ce92fbSShannon Nelson enum pds_core_fw_slot slot)
4949ce92fbSShannon Nelson {
5049ce92fbSShannon Nelson union pds_core_dev_cmd cmd = {
5149ce92fbSShannon Nelson .fw_control.opcode = PDS_CORE_CMD_FW_CONTROL,
5249ce92fbSShannon Nelson .fw_control.oper = PDS_CORE_FW_ACTIVATE_ASYNC,
5349ce92fbSShannon Nelson .fw_control.slot = slot
5449ce92fbSShannon Nelson };
5549ce92fbSShannon Nelson union pds_core_dev_comp comp;
5649ce92fbSShannon Nelson
5749ce92fbSShannon Nelson return pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
5849ce92fbSShannon Nelson }
5949ce92fbSShannon Nelson
pdsc_fw_status_long_wait(struct pdsc * pdsc,const char * label,unsigned long timeout,u8 fw_cmd,struct netlink_ext_ack * extack)6049ce92fbSShannon Nelson static int pdsc_fw_status_long_wait(struct pdsc *pdsc,
6149ce92fbSShannon Nelson const char *label,
6249ce92fbSShannon Nelson unsigned long timeout,
6349ce92fbSShannon Nelson u8 fw_cmd,
6449ce92fbSShannon Nelson struct netlink_ext_ack *extack)
6549ce92fbSShannon Nelson {
6649ce92fbSShannon Nelson union pds_core_dev_cmd cmd = {
6749ce92fbSShannon Nelson .fw_control.opcode = PDS_CORE_CMD_FW_CONTROL,
6849ce92fbSShannon Nelson .fw_control.oper = fw_cmd,
6949ce92fbSShannon Nelson };
7049ce92fbSShannon Nelson union pds_core_dev_comp comp;
7149ce92fbSShannon Nelson unsigned long start_time;
7249ce92fbSShannon Nelson unsigned long end_time;
7349ce92fbSShannon Nelson int err;
7449ce92fbSShannon Nelson
7549ce92fbSShannon Nelson /* Ping on the status of the long running async install
7649ce92fbSShannon Nelson * command. We get EAGAIN while the command is still
7749ce92fbSShannon Nelson * running, else we get the final command status.
7849ce92fbSShannon Nelson */
7949ce92fbSShannon Nelson start_time = jiffies;
8049ce92fbSShannon Nelson end_time = start_time + (timeout * HZ);
8149ce92fbSShannon Nelson do {
8249ce92fbSShannon Nelson err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
8349ce92fbSShannon Nelson msleep(20);
8449ce92fbSShannon Nelson } while (time_before(jiffies, end_time) &&
8549ce92fbSShannon Nelson (err == -EAGAIN || err == -ETIMEDOUT));
8649ce92fbSShannon Nelson
8749ce92fbSShannon Nelson if (err == -EAGAIN || err == -ETIMEDOUT) {
8849ce92fbSShannon Nelson NL_SET_ERR_MSG_MOD(extack, "Firmware wait timed out");
8949ce92fbSShannon Nelson dev_err(pdsc->dev, "DEV_CMD firmware wait %s timed out\n",
9049ce92fbSShannon Nelson label);
9149ce92fbSShannon Nelson } else if (err) {
9249ce92fbSShannon Nelson NL_SET_ERR_MSG_MOD(extack, "Firmware wait failed");
9349ce92fbSShannon Nelson }
9449ce92fbSShannon Nelson
9549ce92fbSShannon Nelson return err;
9649ce92fbSShannon Nelson }
9749ce92fbSShannon Nelson
pdsc_firmware_update(struct pdsc * pdsc,const struct firmware * fw,struct netlink_ext_ack * extack)9849ce92fbSShannon Nelson int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
9949ce92fbSShannon Nelson struct netlink_ext_ack *extack)
10049ce92fbSShannon Nelson {
10149ce92fbSShannon Nelson u32 buf_sz, copy_sz, offset;
10249ce92fbSShannon Nelson struct devlink *dl;
10349ce92fbSShannon Nelson int next_interval;
10449ce92fbSShannon Nelson u64 data_addr;
10549ce92fbSShannon Nelson int err = 0;
10649ce92fbSShannon Nelson int fw_slot;
10749ce92fbSShannon Nelson
10849ce92fbSShannon Nelson dev_info(pdsc->dev, "Installing firmware\n");
10949ce92fbSShannon Nelson
110*e96094c1SBrett Creeley if (!pdsc->cmd_regs)
111*e96094c1SBrett Creeley return -ENXIO;
112*e96094c1SBrett Creeley
11349ce92fbSShannon Nelson dl = priv_to_devlink(pdsc);
11449ce92fbSShannon Nelson devlink_flash_update_status_notify(dl, "Preparing to flash",
11549ce92fbSShannon Nelson NULL, 0, 0);
11649ce92fbSShannon Nelson
11749ce92fbSShannon Nelson buf_sz = sizeof(pdsc->cmd_regs->data);
11849ce92fbSShannon Nelson
11949ce92fbSShannon Nelson dev_dbg(pdsc->dev,
12049ce92fbSShannon Nelson "downloading firmware - size %d part_sz %d nparts %lu\n",
12149ce92fbSShannon Nelson (int)fw->size, buf_sz, DIV_ROUND_UP(fw->size, buf_sz));
12249ce92fbSShannon Nelson
12349ce92fbSShannon Nelson offset = 0;
12449ce92fbSShannon Nelson next_interval = 0;
12549ce92fbSShannon Nelson data_addr = offsetof(struct pds_core_dev_cmd_regs, data);
12649ce92fbSShannon Nelson while (offset < fw->size) {
12749ce92fbSShannon Nelson if (offset >= next_interval) {
12849ce92fbSShannon Nelson devlink_flash_update_status_notify(dl, "Downloading",
12949ce92fbSShannon Nelson NULL, offset,
13049ce92fbSShannon Nelson fw->size);
13149ce92fbSShannon Nelson next_interval = offset +
13249ce92fbSShannon Nelson (fw->size / PDSC_FW_INTERVAL_FRACTION);
13349ce92fbSShannon Nelson }
13449ce92fbSShannon Nelson
13549ce92fbSShannon Nelson copy_sz = min_t(unsigned int, buf_sz, fw->size - offset);
13649ce92fbSShannon Nelson mutex_lock(&pdsc->devcmd_lock);
13749ce92fbSShannon Nelson memcpy_toio(&pdsc->cmd_regs->data, fw->data + offset, copy_sz);
13849ce92fbSShannon Nelson err = pdsc_devcmd_fw_download_locked(pdsc, data_addr,
13949ce92fbSShannon Nelson offset, copy_sz);
14049ce92fbSShannon Nelson mutex_unlock(&pdsc->devcmd_lock);
14149ce92fbSShannon Nelson if (err) {
14249ce92fbSShannon Nelson dev_err(pdsc->dev,
14349ce92fbSShannon Nelson "download failed offset 0x%x addr 0x%llx len 0x%x: %pe\n",
14449ce92fbSShannon Nelson offset, data_addr, copy_sz, ERR_PTR(err));
14549ce92fbSShannon Nelson NL_SET_ERR_MSG_MOD(extack, "Segment download failed");
14649ce92fbSShannon Nelson goto err_out;
14749ce92fbSShannon Nelson }
14849ce92fbSShannon Nelson offset += copy_sz;
14949ce92fbSShannon Nelson }
15049ce92fbSShannon Nelson devlink_flash_update_status_notify(dl, "Downloading", NULL,
15149ce92fbSShannon Nelson fw->size, fw->size);
15249ce92fbSShannon Nelson
15349ce92fbSShannon Nelson devlink_flash_update_timeout_notify(dl, "Installing", NULL,
15449ce92fbSShannon Nelson PDSC_FW_INSTALL_TIMEOUT);
15549ce92fbSShannon Nelson
15649ce92fbSShannon Nelson fw_slot = pdsc_devcmd_fw_install(pdsc);
15749ce92fbSShannon Nelson if (fw_slot < 0) {
15849ce92fbSShannon Nelson err = fw_slot;
15949ce92fbSShannon Nelson dev_err(pdsc->dev, "install failed: %pe\n", ERR_PTR(err));
16049ce92fbSShannon Nelson NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware install");
16149ce92fbSShannon Nelson goto err_out;
16249ce92fbSShannon Nelson }
16349ce92fbSShannon Nelson
16449ce92fbSShannon Nelson err = pdsc_fw_status_long_wait(pdsc, "Installing",
16549ce92fbSShannon Nelson PDSC_FW_INSTALL_TIMEOUT,
16649ce92fbSShannon Nelson PDS_CORE_FW_INSTALL_STATUS,
16749ce92fbSShannon Nelson extack);
16849ce92fbSShannon Nelson if (err)
16949ce92fbSShannon Nelson goto err_out;
17049ce92fbSShannon Nelson
17149ce92fbSShannon Nelson devlink_flash_update_timeout_notify(dl, "Selecting", NULL,
17249ce92fbSShannon Nelson PDSC_FW_SELECT_TIMEOUT);
17349ce92fbSShannon Nelson
17449ce92fbSShannon Nelson err = pdsc_devcmd_fw_activate(pdsc, fw_slot);
17549ce92fbSShannon Nelson if (err) {
17649ce92fbSShannon Nelson NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware select");
17749ce92fbSShannon Nelson goto err_out;
17849ce92fbSShannon Nelson }
17949ce92fbSShannon Nelson
18049ce92fbSShannon Nelson err = pdsc_fw_status_long_wait(pdsc, "Selecting",
18149ce92fbSShannon Nelson PDSC_FW_SELECT_TIMEOUT,
18249ce92fbSShannon Nelson PDS_CORE_FW_ACTIVATE_STATUS,
18349ce92fbSShannon Nelson extack);
18449ce92fbSShannon Nelson if (err)
18549ce92fbSShannon Nelson goto err_out;
18649ce92fbSShannon Nelson
18749ce92fbSShannon Nelson dev_info(pdsc->dev, "Firmware update completed, slot %d\n", fw_slot);
18849ce92fbSShannon Nelson
18949ce92fbSShannon Nelson err_out:
19049ce92fbSShannon Nelson if (err)
19149ce92fbSShannon Nelson devlink_flash_update_status_notify(dl, "Flash failed",
19249ce92fbSShannon Nelson NULL, 0, 0);
19349ce92fbSShannon Nelson else
19449ce92fbSShannon Nelson devlink_flash_update_status_notify(dl, "Flash done",
19549ce92fbSShannon Nelson NULL, 0, 0);
19649ce92fbSShannon Nelson return err;
19749ce92fbSShannon Nelson }
198