Merge tag 'v6.4-p2' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[linux-2.6-block.git] / drivers / net / ethernet / amd / pds_core / fw.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright(c) 2023 Advanced Micro Devices, Inc */
3
4 #include "core.h"
5
6 /* The worst case wait for the install activity is about 25 minutes when
7  * installing a new CPLD, which is very seldom.  Normal is about 30-35
8  * seconds.  Since the driver can't tell if a CPLD update will happen we
9  * set the timeout for the ugly case.
10  */
11 #define PDSC_FW_INSTALL_TIMEOUT (25 * 60)
12 #define PDSC_FW_SELECT_TIMEOUT  30
13
14 /* Number of periodic log updates during fw file download */
15 #define PDSC_FW_INTERVAL_FRACTION       32
16
17 static int pdsc_devcmd_fw_download_locked(struct pdsc *pdsc, u64 addr,
18                                           u32 offset, u32 length)
19 {
20         union pds_core_dev_cmd cmd = {
21                 .fw_download.opcode = PDS_CORE_CMD_FW_DOWNLOAD,
22                 .fw_download.offset = cpu_to_le32(offset),
23                 .fw_download.addr = cpu_to_le64(addr),
24                 .fw_download.length = cpu_to_le32(length),
25         };
26         union pds_core_dev_comp comp;
27
28         return pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
29 }
30
31 static int pdsc_devcmd_fw_install(struct pdsc *pdsc)
32 {
33         union pds_core_dev_cmd cmd = {
34                 .fw_control.opcode = PDS_CORE_CMD_FW_CONTROL,
35                 .fw_control.oper = PDS_CORE_FW_INSTALL_ASYNC
36         };
37         union pds_core_dev_comp comp;
38         int err;
39
40         err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
41         if (err < 0)
42                 return err;
43
44         return comp.fw_control.slot;
45 }
46
47 static int pdsc_devcmd_fw_activate(struct pdsc *pdsc,
48                                    enum pds_core_fw_slot slot)
49 {
50         union pds_core_dev_cmd cmd = {
51                 .fw_control.opcode = PDS_CORE_CMD_FW_CONTROL,
52                 .fw_control.oper = PDS_CORE_FW_ACTIVATE_ASYNC,
53                 .fw_control.slot = slot
54         };
55         union pds_core_dev_comp comp;
56
57         return pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
58 }
59
60 static int pdsc_fw_status_long_wait(struct pdsc *pdsc,
61                                     const char *label,
62                                     unsigned long timeout,
63                                     u8 fw_cmd,
64                                     struct netlink_ext_ack *extack)
65 {
66         union pds_core_dev_cmd cmd = {
67                 .fw_control.opcode = PDS_CORE_CMD_FW_CONTROL,
68                 .fw_control.oper = fw_cmd,
69         };
70         union pds_core_dev_comp comp;
71         unsigned long start_time;
72         unsigned long end_time;
73         int err;
74
75         /* Ping on the status of the long running async install
76          * command.  We get EAGAIN while the command is still
77          * running, else we get the final command status.
78          */
79         start_time = jiffies;
80         end_time = start_time + (timeout * HZ);
81         do {
82                 err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
83                 msleep(20);
84         } while (time_before(jiffies, end_time) &&
85                  (err == -EAGAIN || err == -ETIMEDOUT));
86
87         if (err == -EAGAIN || err == -ETIMEDOUT) {
88                 NL_SET_ERR_MSG_MOD(extack, "Firmware wait timed out");
89                 dev_err(pdsc->dev, "DEV_CMD firmware wait %s timed out\n",
90                         label);
91         } else if (err) {
92                 NL_SET_ERR_MSG_MOD(extack, "Firmware wait failed");
93         }
94
95         return err;
96 }
97
98 int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
99                          struct netlink_ext_ack *extack)
100 {
101         u32 buf_sz, copy_sz, offset;
102         struct devlink *dl;
103         int next_interval;
104         u64 data_addr;
105         int err = 0;
106         int fw_slot;
107
108         dev_info(pdsc->dev, "Installing firmware\n");
109
110         dl = priv_to_devlink(pdsc);
111         devlink_flash_update_status_notify(dl, "Preparing to flash",
112                                            NULL, 0, 0);
113
114         buf_sz = sizeof(pdsc->cmd_regs->data);
115
116         dev_dbg(pdsc->dev,
117                 "downloading firmware - size %d part_sz %d nparts %lu\n",
118                 (int)fw->size, buf_sz, DIV_ROUND_UP(fw->size, buf_sz));
119
120         offset = 0;
121         next_interval = 0;
122         data_addr = offsetof(struct pds_core_dev_cmd_regs, data);
123         while (offset < fw->size) {
124                 if (offset >= next_interval) {
125                         devlink_flash_update_status_notify(dl, "Downloading",
126                                                            NULL, offset,
127                                                            fw->size);
128                         next_interval = offset +
129                                         (fw->size / PDSC_FW_INTERVAL_FRACTION);
130                 }
131
132                 copy_sz = min_t(unsigned int, buf_sz, fw->size - offset);
133                 mutex_lock(&pdsc->devcmd_lock);
134                 memcpy_toio(&pdsc->cmd_regs->data, fw->data + offset, copy_sz);
135                 err = pdsc_devcmd_fw_download_locked(pdsc, data_addr,
136                                                      offset, copy_sz);
137                 mutex_unlock(&pdsc->devcmd_lock);
138                 if (err) {
139                         dev_err(pdsc->dev,
140                                 "download failed offset 0x%x addr 0x%llx len 0x%x: %pe\n",
141                                 offset, data_addr, copy_sz, ERR_PTR(err));
142                         NL_SET_ERR_MSG_MOD(extack, "Segment download failed");
143                         goto err_out;
144                 }
145                 offset += copy_sz;
146         }
147         devlink_flash_update_status_notify(dl, "Downloading", NULL,
148                                            fw->size, fw->size);
149
150         devlink_flash_update_timeout_notify(dl, "Installing", NULL,
151                                             PDSC_FW_INSTALL_TIMEOUT);
152
153         fw_slot = pdsc_devcmd_fw_install(pdsc);
154         if (fw_slot < 0) {
155                 err = fw_slot;
156                 dev_err(pdsc->dev, "install failed: %pe\n", ERR_PTR(err));
157                 NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware install");
158                 goto err_out;
159         }
160
161         err = pdsc_fw_status_long_wait(pdsc, "Installing",
162                                        PDSC_FW_INSTALL_TIMEOUT,
163                                        PDS_CORE_FW_INSTALL_STATUS,
164                                        extack);
165         if (err)
166                 goto err_out;
167
168         devlink_flash_update_timeout_notify(dl, "Selecting", NULL,
169                                             PDSC_FW_SELECT_TIMEOUT);
170
171         err = pdsc_devcmd_fw_activate(pdsc, fw_slot);
172         if (err) {
173                 NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware select");
174                 goto err_out;
175         }
176
177         err = pdsc_fw_status_long_wait(pdsc, "Selecting",
178                                        PDSC_FW_SELECT_TIMEOUT,
179                                        PDS_CORE_FW_ACTIVATE_STATUS,
180                                        extack);
181         if (err)
182                 goto err_out;
183
184         dev_info(pdsc->dev, "Firmware update completed, slot %d\n", fw_slot);
185
186 err_out:
187         if (err)
188                 devlink_flash_update_status_notify(dl, "Flash failed",
189                                                    NULL, 0, 0);
190         else
191                 devlink_flash_update_status_notify(dl, "Flash done",
192                                                    NULL, 0, 0);
193         return err;
194 }