/** * IPC interrupts are recieved by the FW when a) Host SW rings doorbell and * b) when Host SW clears doorbell busy bit [31]. * * Doorbell Register (DB) bits * ----+-------+--------+-----------+--------+------------+-------------------- * 31 | 30 29 | 28-20 |19 18 17 16| 15 14 | 13 12 11 10| 9 8 7 6 5 4 3 2 1 0 * ----+-------+--------+-----------+--------+------------+-------------------- * Busy|Options|Reserved| Command |Reserved| Protocol | Message Length * ----+-------+--------+-----------+--------+------------+-------------------- * * ISH Peripheral Interrupt Status Register: * Bit 0 - If set, indicates interrupt was caused by setting Host2ISH DB * * ISH Peripheral Interrupt Mask Register * Bit 0 - If set, mask interrupt caused by Host2ISH DB * * ISH Peripheral DB Clear Status Register * Bit 0 - If set, indicates interrupt was caused by clearing Host2ISH DB */ static void ipc_interrupt_handler(void) { uint32_t pisr = REG32(IPC_PISR); uint32_t pimr = REG32(IPC_PIMR); uint32_t busy_clear = REG32(IPC_BUSY_CLEAR); uint32_t drbl = REG32(IPC_ISH2HOST_MSG_REGS); uint8_t proto, cmd; if ((pisr & IPC_PISR_HOST2ISH_BIT) && (pimr & IPC_PIMR_HOST2ISH_BIT)) { /* New message arrived */ ipc_set_pimr(IPC_PEER_HOST_ID, UNSET_PIMR, PIMR_SIGNAL_IN); task_set_event(TASK_ID_IPC_COMM, EVENT_FLAG_BIT_READ_IPC, 0); proto = IPC_HEADER_GET_PROTOCOL(drbl); cmd = IPC_HEADER_GET_MNG_CMD(drbl); if ((proto == IPC_PROTOCOL_MNG) && (cmd == MNG_TIME_UPDATE)) /* Ignoring time update from host */ ; } if ((busy_clear & IPC_INT_ISH2HOST_CLR_BIT) && (pimr & IPC_PIMR_ISH2HOST_CLR_MASK_BIT)) { /* Written message cleared */ REG32(IPC_BUSY_CLEAR) = IPC_ISH_FWSTS; task_set_event(TASK_ID_IPC_COMM, EVENT_FLAG_BIT_WRITE_IPC, 0); } }
/** * write_ipc_from_queue() - try to write ipc msg from Tx queue to device * @dev: ishtp device pointer * * Check if DRBL is cleared. if it is - write the first IPC msg, then call * the callback function (unless it's NULL) * * Return: 0 for success else failure code */ static int write_ipc_from_queue(struct ishtp_device *dev) { struct wr_msg_ctl_info *ipc_link; unsigned long length; unsigned long rem; unsigned long flags; uint32_t doorbell_val; uint32_t *r_buf; uint32_t reg_addr; int i; void (*ipc_send_compl)(void *); void *ipc_send_compl_prm; static int out_ipc_locked; unsigned long out_ipc_flags; if (dev->dev_state == ISHTP_DEV_DISABLED) return -EINVAL; spin_lock_irqsave(&dev->out_ipc_spinlock, out_ipc_flags); if (out_ipc_locked) { spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags); return -EBUSY; } out_ipc_locked = 1; if (!ish_is_input_ready(dev)) { out_ipc_locked = 0; spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags); return -EBUSY; } spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags); spin_lock_irqsave(&dev->wr_processing_spinlock, flags); /* * if tx send list is empty - return 0; * may happen, as RX_COMPLETE handler doesn't check list emptiness. */ if (list_empty(&dev->wr_processing_list_head.link)) { spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); out_ipc_locked = 0; return 0; } ipc_link = list_entry(dev->wr_processing_list_head.link.next, struct wr_msg_ctl_info, link); /* first 4 bytes of the data is the doorbell value (IPC header) */ length = ipc_link->length - sizeof(uint32_t); doorbell_val = *(uint32_t *)ipc_link->inline_data; r_buf = (uint32_t *)(ipc_link->inline_data + sizeof(uint32_t)); /* If sending MNG_SYNC_FW_CLOCK, update clock again */ if (IPC_HEADER_GET_PROTOCOL(doorbell_val) == IPC_PROTOCOL_MNG && IPC_HEADER_GET_MNG_CMD(doorbell_val) == MNG_SYNC_FW_CLOCK) { struct timespec ts_system; struct timeval tv_utc; uint64_t usec_system, usec_utc; struct ipc_time_update_msg time_update; struct time_sync_format ts_format; get_monotonic_boottime(&ts_system); do_gettimeofday(&tv_utc); usec_system = (timespec_to_ns(&ts_system)) / NSEC_PER_USEC; usec_utc = (uint64_t)tv_utc.tv_sec * 1000000 + ((uint32_t)tv_utc.tv_usec); ts_format.ts1_source = HOST_SYSTEM_TIME_USEC; ts_format.ts2_source = HOST_UTC_TIME_USEC; ts_format.reserved = 0; time_update.primary_host_time = usec_system; time_update.secondary_host_time = usec_utc; time_update.sync_info = ts_format; memcpy(r_buf, &time_update, sizeof(struct ipc_time_update_msg)); } for (i = 0, reg_addr = IPC_REG_HOST2ISH_MSG; i < length >> 2; i++, reg_addr += 4) ish_reg_write(dev, reg_addr, r_buf[i]); rem = length & 0x3; if (rem > 0) { uint32_t reg = 0; memcpy(®, &r_buf[length >> 2], rem); ish_reg_write(dev, reg_addr, reg); }