static bool hci_pio_process_ibi()

in master/mipi-i3c-hci/pio.c [873:939]


static bool hci_pio_process_ibi(struct i3c_hci *hci, struct hci_pio_data *pio)
{
	struct hci_pio_ibi_data *ibi = &pio->ibi;

	if (!ibi->slot && !ibi->seg_cnt && ibi->last_seg)
		if (!hci_pio_prep_new_ibi(hci, pio))
			return false;

	for (;;) {
		u32 ibi_status;
		unsigned int ibi_addr;

		if (ibi->slot) {
			if (!hci_pio_get_ibi_segment(hci, pio))
				return false;
			ibi->slot->len += ibi->seg_len;
			ibi->data_ptr += ibi->seg_len;
			if (ibi->last_seg) {
				/* was the last segment: submit it and leave */
				i3c_master_queue_ibi(ibi->slot->dev, ibi->slot);
				ibi->slot = NULL;
				hci_pio_set_ibi_thresh(hci, pio, 1);
				return true;
			}
		} else if (ibi->seg_cnt) {
			/*
			 * No slot but a non-zero count. This is the result
			 * of some error and the payload must be drained.
			 * This normally does not happen therefore no need
			 * to be extra optimized here.
			 */
			hci_pio_set_ibi_thresh(hci, pio, 1);
			do {
				if (!(pio_reg_read(INTR_STATUS) & STAT_IBI_STATUS_THLD))
					return false;
				pio_reg_read(IBI_PORT);
			} while (--ibi->seg_cnt);
			if (ibi->last_seg)
				return true;
		}

		/* try to move to the next segment right away */
		hci_pio_set_ibi_thresh(hci, pio, 1);
		if (!(pio_reg_read(INTR_STATUS) & STAT_IBI_STATUS_THLD))
			return false;
		ibi_status = pio_reg_read(IBI_PORT);
		ibi_addr = FIELD_GET(IBI_TARGET_ADDR, ibi_status);
		if (ibi->addr != ibi_addr) {
			/* target address changed before last segment */
			dev_err(&hci->master.dev,
				"unexp IBI address changed from %d to %d\n",
				ibi->addr, ibi_addr);
			hci_pio_free_ibi_slot(hci, pio);
		}
		ibi->last_seg = ibi_status & IBI_LAST_STATUS;
		ibi->seg_len = FIELD_GET(IBI_DATA_LENGTH, ibi_status);
		ibi->seg_cnt = ibi->seg_len;
		if (ibi->slot && ibi->slot->len + ibi->seg_len > ibi->max_len) {
			dev_err(&hci->master.dev,
				"IBI payload too big (%d > %d)\n",
				ibi->slot->len + ibi->seg_len, ibi->max_len);
			hci_pio_free_ibi_slot(hci, pio);
		}
	}

	return false;
}