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