1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (C) 2023-2026 Intel Corporation */ 3 #include <linux/atomic.h> 4 #include <linux/device.h> 5 #include <linux/dev_printk.h> 6 #include <linux/err.h> 7 #include <linux/export.h> 8 #include <linux/jiffies.h> 9 #include <linux/kthread.h> 10 #include <linux/slab.h> 11 #include <linux/wait.h> 12 13 #include "cdev.h" 14 #include "fw_client.h" 15 #include "host_client.h" 16 #include "ham.h" 17 #include "issei_dev.h" 18 19 static void issei_rst_state_set(struct issei_device *idev, enum issei_rst_state state) 20 { 21 idev->rst_state = state; 22 /* wake up the thread */ 23 if (waitqueue_active(&idev->wait_rst_state)) 24 wake_up(&idev->wait_rst_state); 25 } 26 27 static int issei_reset(struct issei_device *idev) 28 { 29 int ret; 30 31 idev->ops->irq_clear(idev); 32 33 issei_cl_all_disconnect(idev); 34 issei_fw_cl_remove_all(idev); 35 /* No need to check for overflow here, the counter is used only for info */ 36 idev->all_reset_count++; 37 ret = idev->ops->hw_reset(idev, !idev->power_down); 38 issei_dmam_setup(idev); 39 if (ret) { 40 dev_err(&idev->dev, "hw_reset failed ret = %d\n", ret); 41 return ret; 42 } 43 44 if (idev->power_down) { 45 dev_dbg(&idev->dev, "powering down: end of reset\n"); 46 issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); 47 return -ENODEV; 48 } 49 return 0; 50 } 51 52 static int issei_process_read_msg(struct issei_device *idev) 53 { 54 struct issei_dma_data data = {}; 55 int ret; 56 57 ret = issei_dma_read(idev, &data); 58 if (ret) 59 return ret; 60 61 dev_dbg(&idev->dev, "Processing response %u %u %u %u\n", data.fw_id, data.host_id, 62 data.status, data.length); 63 if (data.status != HAMS_SUCCESS) { 64 dev_err(&idev->dev, "Command failed with status 0x%02X", data.status); 65 kfree(data.buf); 66 ret = -EIO; 67 } else { 68 if (issei_is_ham_rsp(data.fw_id, data.host_id)) 69 ret = issei_ham_process_ham_rsp(idev, data.buf, data.length); 70 else 71 ret = issei_cl_read_buf(idev, data.fw_id, data.host_id, 72 data.buf, data.length); 73 } 74 idev->ops->irq_write_generate(idev); 75 return ret; 76 } 77 78 static int issei_process_write_msg(struct issei_device *idev) 79 { 80 if (idev->rst_state != ISSEI_RST_STATE_DONE) 81 return 0; 82 83 return issei_cl_write_from_queue(idev); 84 } 85 86 static int issei_process_thread(void *_dev) 87 { 88 long timeout, old_timeout = MAX_SCHEDULE_TIMEOUT; 89 struct issei_device *idev = _dev; 90 int ret; 91 92 while (!kthread_should_stop()) { 93 dev_dbg(&idev->dev, "process_work in %d\n", idev->rst_state); 94 if (!idev->ops->hw_is_ready(idev) && idev->rst_state > ISSEI_RST_STATE_HW_READY) { 95 if (!idev->power_down) 96 dev_dbg(&idev->dev, "HW not ready, resetting\n"); 97 idev->rst_state = ISSEI_RST_STATE_INIT; 98 } 99 if (idev->power_down) 100 idev->rst_state = ISSEI_RST_STATE_INIT; 101 WRITE_ONCE(idev->has_data, false); 102 dev_dbg(&idev->dev, "reset_step in %d\n", idev->rst_state); 103 timeout = MAX_SCHEDULE_TIMEOUT; 104 ret = 0; 105 switch (idev->rst_state) { 106 case ISSEI_RST_STATE_DISABLED: 107 if (idev->power_down) { 108 dev_dbg(&idev->dev, "Interrupt in power down?\n"); 109 break; 110 } 111 idev->rst_state = ISSEI_RST_STATE_INIT; 112 fallthrough; 113 114 case ISSEI_RST_STATE_INIT: 115 idev->ops->irq_clear(idev); 116 idev->ops->irq_sync(idev); 117 118 if (!idev->power_down) { 119 idev->reset_count++; 120 if (idev->reset_count > ISSEI_MAX_CONSEC_RESET) { 121 dev_err(&idev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); 122 issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); 123 break; 124 } 125 } 126 127 ret = issei_reset(idev); 128 if (idev->power_down) { 129 dev_dbg(&idev->dev, "Powering down\n"); 130 return 0; 131 } 132 if (ret) 133 break; 134 135 idev->rst_state = ISSEI_RST_STATE_HW_READY; 136 timeout = msecs_to_jiffies(ISSEI_RST_HW_READY_TIMEOUT_MSEC); 137 break; 138 139 case ISSEI_RST_STATE_HW_READY: 140 if (!idev->ops->hw_is_ready(idev)) { 141 dev_dbg(&idev->dev, "HW is not ready?\n"); 142 timeout = old_timeout; 143 break; 144 } 145 146 dev_dbg(&idev->dev, "HW is ready\n"); 147 idev->ops->hw_reset_release(idev); 148 idev->ops->host_set_ready(idev); 149 ret = idev->ops->setup_message_send(idev); 150 if (ret) 151 break; 152 153 idev->rst_state = ISSEI_RST_STATE_SETUP; 154 timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); 155 break; 156 157 case ISSEI_RST_STATE_SETUP: 158 ret = idev->ops->setup_message_recv(idev); 159 if (ret) { 160 if (ret == -ENODATA) { 161 ret = 0; 162 timeout = old_timeout; 163 } 164 } else { 165 timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); 166 ret = issei_ham_send_start_req(idev); 167 idev->rst_state = ISSEI_RST_STATE_START; 168 } 169 break; 170 171 case ISSEI_RST_STATE_START: 172 ret = issei_process_read_msg(idev); 173 if (!ret) { 174 timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); 175 idev->rst_state = ISSEI_RST_STATE_CLIENT_ENUM; 176 } else if (ret == -ENODATA) { 177 ret = 0; 178 timeout = old_timeout; 179 } 180 break; 181 182 case ISSEI_RST_STATE_CLIENT_ENUM: 183 ret = issei_process_read_msg(idev); 184 if (ret) { 185 if (ret == -ENODATA) { 186 ret = 0; 187 timeout = old_timeout; 188 } 189 } else { 190 idev->reset_count = 0; 191 idev->rst_state = ISSEI_RST_STATE_DONE; 192 dev_dbg(&idev->dev, "Reset finished successfully\n"); 193 } 194 break; 195 196 case ISSEI_RST_STATE_DONE: 197 ret = issei_process_read_msg(idev); 198 if (ret != 0 && ret != -ENODATA) 199 break; 200 201 ret = issei_process_write_msg(idev); 202 break; 203 } 204 205 if (ret) { 206 dev_warn(&idev->dev, "Process failed ret = %d\n", ret); 207 idev->rst_state = ISSEI_RST_STATE_INIT; 208 continue; 209 } 210 211 /* 212 * Every thread that has data to process sets the 'has_data' flag and 213 * triggers the wait queue. 214 * The processing thread, in each loop iteration, resets 'has_data' 215 * and processes all available data. 216 * 217 * After processing, the thread waits for 'has_data' to be set again. 218 * 219 * If the wait function times out but 'has_data' becomes 1 before 220 * the subsequent atomic read check, this is acceptable from a flow 221 * perspective - the thread will continue processing the data. 222 * 223 * The 'has_data' flag cannot become 0 between the wait function and 224 * the atomic read check, since only this thread is allowed to reset it to 0. 225 */ 226 227 old_timeout = wait_event_interruptible_timeout(idev->wait_has_data, 228 READ_ONCE(idev->has_data), 229 timeout); 230 if (idev->rst_state == ISSEI_RST_STATE_DISABLED) 231 continue; 232 233 if (!READ_ONCE(idev->has_data)) { 234 dev_warn(&idev->dev, "Timed out at state %d, resetting\n", 235 idev->rst_state); 236 idev->rst_state = ISSEI_RST_STATE_INIT; 237 } 238 } 239 240 return 0; 241 } 242 243 /** 244 * issei_start - configure HW device and start processing thread. 245 * @idev: the device structure 246 * 247 * Return: 0 on success, < 0 on failure 248 */ 249 int issei_start(struct issei_device *idev) 250 { 251 int ret; 252 253 idev->power_down = false; 254 255 ret = issei_dmam_setup(idev); 256 if (ret) 257 return ret; 258 259 idev->ops->irq_clear(idev); 260 261 ret = idev->ops->hw_config(idev); 262 if (ret) 263 return ret; 264 265 idev->process_thread = kthread_run(issei_process_thread, idev, 266 "kisseiprocess/%s", dev_name(&idev->dev)); 267 if (IS_ERR(idev->process_thread)) { 268 ret = PTR_ERR(idev->process_thread); 269 dev_err(&idev->dev, "unable to create process thread. ret = %d\n", ret); 270 return ret; 271 } 272 273 issei_poke_process_thread(idev); 274 return 0; 275 } 276 EXPORT_SYMBOL_GPL(issei_start); 277 278 /** 279 * issei_stop - stop interrupts and processing thread. 280 * @idev: the device structure 281 */ 282 void issei_stop(struct issei_device *idev) 283 { 284 idev->power_down = true; 285 286 idev->ops->irq_clear(idev); 287 idev->ops->irq_sync(idev); 288 289 issei_poke_process_thread(idev); 290 291 wait_event_timeout(idev->wait_rst_state, 292 (idev->rst_state == ISSEI_RST_STATE_DISABLED), 293 msecs_to_jiffies(ISSEI_STOP_TIMEOUT_MSEC)); 294 kthread_stop(idev->process_thread); 295 } 296 EXPORT_SYMBOL_GPL(issei_stop); 297