示例#1
0
文件: ipc.c 项目: coreboot/chrome-ec
/**
 * 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);
	}
}
示例#2
0
文件: ipc.c 项目: AshishNamdev/linux
/**
 * 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(&reg, &r_buf[length >> 2], rem);
		ish_reg_write(dev, reg_addr, reg);
	}