/* Task that listens for incomming IPC messages from Host and initiate host * command processing. */ void ipc_comm_task(void) { int ret = 0; uint32_t out_drbl, pkt_len; for (;;) { ret = task_wait_event_mask(EVENT_FLAG_BIT_READ_IPC | EVENT_FLAG_BIT_WRITE_IPC, -1); if ((ret & EVENT_FLAG_BIT_WRITE_IPC)) continue; else if (!(ret & EVENT_FLAG_BIT_READ_IPC)) continue; /* Read the command byte. This clears the FRMH bit in * the status byte. */ out_drbl = REG32(IPC_HOST2ISH_DOORBELL); pkt_len = out_drbl & IPC_HEADER_LENGTH_MASK; ret = ipc_read(IPC_PEER_HOST_ID, ipc_host_args, pkt_len); host_cmd_args.command = EC_COMMAND_PROTOCOL_3; host_cmd_args.result = EC_RES_SUCCESS; host_cmd_flags = ipc_host_args->flags; /* We only support new style command (v3) now */ if (host_cmd_args.command == EC_COMMAND_PROTOCOL_3) { ipc_packet.send_response = ipc_send_response_packet; ipc_packet.request = (const void *)ipc_get_hostcmd_data_range(); ipc_packet.request_temp = params_copy; ipc_packet.request_max = sizeof(params_copy); /* Don't know the request size so pass in * the entire buffer */ ipc_packet.request_size = EC_LPC_HOST_PACKET_SIZE; ipc_packet.response = (void *)ipc_get_hostcmd_data_range(); ipc_packet.response_max = EC_LPC_HOST_PACKET_SIZE; ipc_packet.response_size = 0; ipc_packet.driver_result = EC_RES_SUCCESS; host_packet_receive(&ipc_packet); usleep(10); /* To force yield */ continue; } else { /* Old style command unsupported */ host_cmd_args.result = EC_RES_INVALID_COMMAND; } /* Hand off to host command handler */ host_command_received(&host_cmd_args); } }
/** * Flush an ADC sequencer and initiate a read. * * @param seq Sequencer to read * @return Raw ADC value. */ static int flush_and_read(enum lm4_adc_sequencer seq) { /* * This is currently simple because we can dedicate a sequencer to each * ADC channel. If we have enough channels that's no longer possible, * this code will need to become more complex. For example, we could: * * 1) Read them all using a timer interrupt, and then return the most * recent value? This is lowest-latency for the caller, but won't * return accurate data if read frequently. * * 2) Reserve SS3 for reading a single value, and configure it on each * read? Needs mutex if we could have multiple callers; doesn't matter * if just used for debugging. * * 3) Both? */ volatile uint32_t scratch __attribute__((unused)); int event; /* Empty the FIFO of any previous results */ while (!(LM4_ADC_SSFSTAT(seq) & 0x100)) scratch = LM4_ADC_SSFIFO(seq); /* * This assumes we don't have multiple tasks accessing the same * sequencer. Add mutex lock if needed. */ task_waiting_on_ss[seq] = task_get_current(); /* Clear the interrupt status */ LM4_ADC_ADCISC |= 0x01 << seq; /* Enable interrupt */ LM4_ADC_ADCIM |= 0x01 << seq; /* Initiate sample sequence */ LM4_ADC_ADCPSSI |= 0x01 << seq; /* Wait for interrupt */ event = task_wait_event_mask(TASK_EVENT_ADC_DONE, ADC_TIMEOUT_US); /* Disable interrupt */ LM4_ADC_ADCIM &= ~(0x01 << seq); task_waiting_on_ss[seq] = TASK_ID_INVALID; if (!(event & TASK_EVENT_ADC_DONE)) return ADC_READ_ERROR; /* Read the FIFO and convert to temperature */ return LM4_ADC_SSFIFO(seq); }
static int ipc_wait_until_msg_consumed(struct ipc_if_ctx *ctx, int timeout) { int wait_sts = 0; uint32_t drbl; drbl = REG32(ctx->out_drbl_reg); if (!(drbl & IPC_DRBL_BUSY_BIT)) { /* doorbell is already cleared. we can continue */ return 0; } while (1) { wait_sts = task_wait_event_mask(EVENT_FLAG_BIT_WRITE_IPC, timeout); drbl = REG32(ctx->out_drbl_reg); if (!(drbl & IPC_DRBL_BUSY_BIT)) { return 0; } else if (wait_sts != 0) { /* timeout */ return wait_sts; } } }
int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size, uint8_t *in, int in_size, int flags) { struct i2c_port_data *pd = pdata + port; uint32_t events = 0; if (out_size == 0 && in_size == 0) return EC_SUCCESS; if (pd->i2ccs) { if ((flags & I2C_XFER_SINGLE) == I2C_XFER_SINGLE) flags &= ~I2C_XFER_START; } /* Copy data to port struct */ pd->out = out; pd->out_size = out_size; pd->in = in; pd->in_size = in_size; pd->flags = flags; pd->widx = 0; pd->ridx = 0; pd->err = 0; pd->addr = slave_addr; if (port < I2C_STANDARD_PORT_COUNT) { /* Make sure we're in a good state to start */ if ((flags & I2C_XFER_START) && (i2c_is_busy(port) || (IT83XX_SMB_HOSTA(port) & HOSTA_ALL_WC_BIT) || (i2c_get_line_levels(port) != I2C_LINE_IDLE))) { /* Attempt to unwedge the port. */ i2c_unwedge(port); /* reset i2c port */ i2c_reset(port, I2C_RC_NO_IDLE_FOR_START); } } else { /* Make sure we're in a good state to start */ if ((flags & I2C_XFER_START) && (i2c_is_busy(port) || (i2c_get_line_levels(port) != I2C_LINE_IDLE))) { /* Attempt to unwedge the port. */ i2c_unwedge(port); /* reset i2c port */ i2c_reset(port, I2C_RC_NO_IDLE_FOR_START); } } pd->task_waiting = task_get_current(); if (pd->flags & I2C_XFER_START) { pd->i2ccs = I2C_CH_NORMAL; /* enable i2c interrupt */ task_clear_pending_irq(i2c_ctrl_regs[port].irq); task_enable_irq(i2c_ctrl_regs[port].irq); } /* Start transaction */ i2c_transaction(port); /* Wait for transfer complete or timeout */ events = task_wait_event_mask(TASK_EVENT_I2C_IDLE, pd->timeout_us); /* disable i2c interrupt */ task_disable_irq(i2c_ctrl_regs[port].irq); pd->task_waiting = TASK_ID_INVALID; /* Handle timeout */ if (!(events & TASK_EVENT_I2C_IDLE)) { pd->err = EC_ERROR_TIMEOUT; /* reset i2c port */ i2c_reset(port, I2C_RC_TIMEOUT); } /* reset i2c channel status */ if (pd->err) pd->i2ccs = I2C_CH_NORMAL; return pd->err; }
enum smb_error i2c_master_transaction(int controller) { /* Set i2c mode to object */ int events = 0; volatile struct i2c_status *p_status = i2c_stsobjs + controller; /* Assign current SMB status of controller */ if (p_status->oper_state == SMB_IDLE) { /* New transaction */ p_status->oper_state = SMB_MASTER_START; } else if (p_status->oper_state == SMB_WRITE_SUSPEND) { if (p_status->sz_txbuf == 0) { /* Read bytes from next transaction */ p_status->oper_state = SMB_REPEAT_START; CPUTS("R"); } else { /* Continue to write the other bytes */ p_status->oper_state = SMB_WRITE_OPER; I2C_WRITE_BYTE(controller, p_status->tx_buf[p_status->idx_buf++]); CPRINTS("-W(%02x)", p_status->tx_buf[p_status->idx_buf-1]); } } else if (p_status->oper_state == SMB_READ_SUSPEND) { /* Need to read the other bytes from next transaction */ uint8_t data; uint8_t timeout = 10; /* unit: us */ p_status->oper_state = SMB_READ_OPER; /* wait for SDAST issue */ while (timeout > 0) { if (IS_BIT_SET(NPCX_SMBST(controller), NPCX_SMBST_SDAST)) break; if (--timeout > 0) usleep(10); } if (timeout == 0) return EC_ERROR_TIMEOUT; /* * Read first byte from SMBSDA in case SDAST interrupt occurs * immediately before task_wait_event_mask() func */ I2C_READ_BYTE(controller, data); CPRINTS("-R(%02x)", data); /* Read to buffer */ p_status->rx_buf[p_status->idx_buf++] = data; } /* Generate a START condition */ if (p_status->oper_state == SMB_MASTER_START || p_status->oper_state == SMB_REPEAT_START) { I2C_START(controller); CPUTS("ST"); } /* Enable SMB interrupt and New Address Match interrupt source */ i2c_interrupt(controller, 1); /* Wait for transfer complete or timeout */ events = task_wait_event_mask(TASK_EVENT_I2C_IDLE, p_status->timeout_us); /* Handle bus timeout */ if ((events & TASK_EVENT_I2C_IDLE) == 0) { /* Recovery I2C controller */ i2c_recovery(controller); p_status->err_code = SMB_TIMEOUT_ERROR; } /* * In slave write operation, NACK is OK, otherwise it is a problem */ else if (p_status->err_code == SMB_BUS_ERROR || p_status->err_code == SMB_MASTER_NO_ADDRESS_MATCH){ i2c_recovery(controller); } /* Wait till STOP condition is generated */ if (p_status->err_code == SMB_OK && i2c_wait_stop_completed(controller, I2C_MIN_TIMEOUT) != EC_SUCCESS) { cprints(CC_I2C, "STOP fail! scl %02x is held by slave device!", controller); p_status->err_code = SMB_TIMEOUT_ERROR; } return p_status->err_code; }