[PATCH 2/2] i3c/master: add the mipi-i3c-hci driver

Boris Brezillon boris.brezillon at collabora.com
Thu Aug 20 12:56:42 EDT 2020


On Thu, 20 Aug 2020 12:34:13 -0400 (EDT)
Nicolas Pitre <nico at fluxnic.net> wrote:

> On Thu, 20 Aug 2020, Miquel Raynal wrote:

> > > +
> > > +#ccflags-y := -DDEBUG  
> > 
> > Probably a leftover?  
> 
> Well, I left it there intentionally as the code is still actively being 
> developed, so full debugging can quickly be reactivated by anyone.
> I can remove it if deemed too distracting.

How about using dynamic printk instead? I'm pretty sure you don't need
to debug I3C stuff early enough to warrant usage of DDEBUG.

> 
> > [...]
> >   
> > > +#define CMD_C1_DATA_LENGTH(v)		FIELD_PREP(W1_MASK(63, 48), v)
> > > +#define CMD_C1_OFFSET(v)		FIELD_PREP(W1_MASK(47, 32), v)
> > > +#define CMD_C0_TOC			           W0_BIT_(31)
> > > +#define CMD_C0_ROC			           W0_BIT_(30)
> > > +#define CMD_C0_RNW			           W0_BIT_(29)
> > > +#define CMD_C0_MODE(v)			FIELD_PREP(W0_MASK(28, 26), v)
> > > +#define CMD_C0_16_BIT_SUBOFFSET		W0_bit(25)
> > > +#define CMD_C0_FIRST_PHASE_MODE		           W0_BIT_(24)
> > > +#define CMD_C0_DATA_LENGTH_POSITION(v)	FIELD_PREP(W0_MASK(23, 22), v)
> > > +#define CMD_C0_DEV_INDEX(v)		FIELD_PREP(W0_MASK(20, 16), v)
> > > +#define CMD_C0_CP			           W0_BIT_(15)
> > > +#define CMD_C0_CMD(v)			FIELD_PREP(W0_MASK(14, 7), v)
> > > +#define CMD_C0_TID(v)			FIELD_PREP(W0_MASK(6, 3), v)
> > > +
> > > +/*
> > > + * Internal Control Command
> > > + */
> > > +
> > > +#define CMD_0_ATTR_M			FIELD_PREP(CMD_0_ATTR, 0x7)
> > > +
> > > +#define CMD_M1_VENDOR_SPECIFIC		           W1_MASK(63, 32)
> > > +#define CMD_M0_MIPI_RESERVED		           W0_MASK(31, 12)
> > > +#define CMD_M0_MIPI_CMD			           W0_MASK(11, 8)
> > > +#define CMD_M0_VENDOR_INFO_PRESENT	           W0_BIT_(7)
> > > +#define CMD_M0_TID(v)			FIELD_PREP(W0_MASK(6, 3), v)
> > > +
> > > +
> > > +static int hci_cmd_v1_prep_ccc(struct i3c_hci *hci,
> > > +			       struct hci_xfer *xfer,
> > > +			       u8 ccc_addr, u8 ccc_cmd, bool raw)
> > > +{
> > > +	u_int dat_idx = 0;  
> > 
> > I guess u_int her and below is not the preferred way to declare an unsigned int?  
> 
> Why not?
> 
> > > +	int mode = hci_get_i3c_mode(hci);
> > > +	u8 *data = xfer->data;
> > > +	u_int data_len = xfer->data_len;
> > > +	bool rnw = xfer->rnw;
> > > +	int ret;
> > > +
> > > +	BUG_ON(raw);  
> > 
> > It looks like 'raw' cannot be used with v1 (at least you seem to take
> > care of it in v2), so maybe BUG_ON is a bit radical here and you can
> > simply return an error? I think the use of BUG() is not appreciated in
> > general.  
> 
> That depends. Judgement is needed for BUG() usage.
> 
> Here raw is absolutely impossible with v1 hardware and if ever this 
> happens this is definitely a software bug that needs fixing right away. 
> There is no point returning a runtime error code in that case as the 
> upper layer won't know what to do about it.
> 
> On the other hand, you absolutely don't want to BUG() on a condition 
> that could _eventually_ happen at run time during normal usage. But 
> that's not the case here.

Well, people have tried to eradicate BUG() occurrences, so let's not add
new ones if we can avoid it. How about a WARN_ON()+error:

	if (WARN_ON(raw))
		return -EINVAL;

> 
> > > +const struct hci_cmd_ops i3c_hci_cmd_v1 = {
> > > +	.prep_ccc		= hci_cmd_v1_prep_ccc,
> > > +	.prep_i3c_xfer		= hci_cmd_v1_prep_i3c_xfer,
> > > +	.prep_i2c_xfer		= hci_cmd_v1_prep_i2c_xfer,
> > > +	.perform_daa		= hci_cmd_v1_daa,  
> > 
> > I know Boris does not like such space alignment :)  
> 
> Well... unfortunately for Boris, this is overwhelmingly prevalent in the 
> kernel code:
> 
> $ git grep "^"$'\t'"\.[^ ]*"$'\t'"*= "
> 
> And I do like it.  ;-P

The rational being this preference is that sooner or later someone will
add a field to hci_cmd_ops that messes up your nice formatting :P.
Anyway, that's definitely not a blocker.

> 
> > > +void i3c_hci_pio_reset(struct i3c_hci *hci)
> > > +{
> > > +	reg_write(RESET_CONTROL, RX_FIFO_RST|TX_FIFO_RST|RESP_QUEUE_RST);  
> > 
> > Style with missing spaces                  ^ ^  
> 
> True. Will fix.
> 
> > > +static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m,
> > > +				struct i3c_ccc_cmd *ccc)
> > > +{
> > > +	struct i3c_hci *hci = to_i3c_hci(m);
> > > +	struct hci_xfer *xfer;
> > > +	bool raw = !!(hci->quirks & HCI_QUIRK_RAW_CCC);
> > > +	bool prefixed = raw && !!(ccc->id & I3C_CCC_DIRECT);
> > > +	u_int nxfers = ccc->ndests + prefixed;
> > > +	DECLARE_COMPLETION_ONSTACK(done);
> > > +	int i, last, ret = 0;
> > > +
> > > +	DBG("cmd=%#x rnw=%d ndests=%d data[0].len=%d",
> > > +	    ccc->id, ccc->rnw, ccc->ndests, ccc->dests[0].payload.len);
> > > +
> > > +	xfer = hci_alloc_xfer(nxfers);
> > > +	if (!xfer)
> > > +		return -ENOMEM;
> > > +
> > > +	if (prefixed) {
> > > +		xfer->data = NULL;
> > > +		xfer->data_len = 0;
> > > +		xfer->rnw = false;
> > > +		hci->cmd->prep_ccc(hci, xfer, I3C_BROADCAST_ADDR,
> > > +				   ccc->id, true);
> > > +		xfer++;
> > > +	}
> > > +
> > > +	for (i = 0; i < nxfers - prefixed; i++) {
> > > +		xfer[i].data = ccc->dests[i].payload.data;
> > > +		xfer[i].data_len = ccc->dests[i].payload.len;
> > > +		xfer[i].rnw = ccc->rnw;
> > > +		ret = hci->cmd->prep_ccc(hci, &xfer[i], ccc->dests[i].addr,
> > > +					 ccc->id, raw);
> > > +		if (ret)
> > > +			goto out;
> > > +		xfer[i].cmd_desc[0] |= CMD_0_ROC;
> > > +	}
> > > +	last = i - 1;
> > > +	xfer[last].cmd_desc[0] |= CMD_0_TOC;
> > > +	xfer[last].completion = &done;
> > > +
> > > +	if (prefixed)
> > > +		xfer--;
> > > +
> > > +	ret = hci->io->queue_xfer(hci, xfer, nxfers);
> > > +	if (ret)
> > > +		goto out;
> > > +	if (!wait_for_completion_timeout(&done, HZ) &&
> > > +	    hci->io->dequeue_xfer(hci, xfer, nxfers)) {
> > > +		ret = -ETIME;
> > > +		goto out;
> > > +	}
> > > +	for (i = prefixed; i < nxfers; i++) {
> > > +		if (ccc->rnw)
> > > +			ccc->dests[i - prefixed].payload.len =
> > > +				RESP_DATA_LENGTH(xfer[i].response);
> > > +		if (RESP_STATUS(xfer[i].response) != RESP_SUCCESS) {
> > > +			ret = -EIO;
> > > +			goto out;
> > > +		}
> > > +	}
> > > +
> > > +#if 0
> > > +	if (ccc->rnw) {
> > > +		HEXDUMP("got: ", ccc->dests[0].payload.data,
> > > +				 ccc->dests[0].payload.len);
> > > +	}
> > > +#endif  
> > 
> > I guess this debug block can be dropped too (there are many debug
> > information the should probably be dropped or turned into dev_info()
> > or similar).  
> 
> Again, hardware bringup from different vendors and other developments 
> are still ongoing. I'd wish for those to stay for the time being unless 
> people feel strongly enough about these to become a merge show stopper.

Can't we replace that by a dev_dbg() using the %*pE formater?

> 
> > > +/*
> > > + * Paul Kimelman's debug trace log facility.
> > > + * This is completely vendor and hardware specific.
> > > + */
> > > +void __PK_debug_trace(struct i3c_hci *hci, const char *func)
> > > +{
> > > +	void __iomem *trcp = (void __iomem *)hci->vendor_data + 7*4;  
> > 
> > Maybe you need to define what is 7*4 , 0*4 below, v >> 27, etc
> > 
> > Also there are many missing spaces between operators (7 * 4,w & (1 <<9).  
> 
> I think in this case this is really crossing the distraction threshold. 
> This is used to extract debugging traces out of a specific FPGA 
> implementation and is of no use to anyone else. I'll just rip that out 
> from the next submission.
> 
> > > +		if (rh->ibi_data_phys)  
> > 
> > I was told that _phys was a very bad suffix for something which is a
> > DMA address an not focibly a physical address.  
> 
> Fair enough. The HCI spec refers to these as "physical memory" hence the 
> suffix. What were you told to use instead?

Maybe _dma instead of _phys?

> 
> > > +static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
> > > +				 struct hci_xfer *xfer_list, int n)
> > > +{
> > > +	struct hci_rings_data *rings = hci->io_data;
> > > +	struct hci_rh_data *rh = &rings->headers[xfer_list[0].ring];
> > > +	u_int i;
> > > +	bool did_unqueue = false;
> > > +
> > > +	/* stop the ring */
> > > +	rh_reg_write(RING_CONTROL, RING_CTRL_ABORT);
> > > +	if (wait_for_completion_timeout(&rh->op_done, HZ) == 0) {
> > > +		/*
> > > +		 * We're deep in it if ever this condition is ever met.
> > > +		 * Hardware might still be writing to memory, etc.
> > > +		 */
> > > +		ERR("unable to abort the ring");
> > > +		BUG();  
> > 
> > Why not just treating the error as always?  
> 
> Again, if this ever happens, you're screwed. That means potential DMA 
> engines could still be alive and about to scribble over memory that is 
> about to be freed which may cause all sorts of impossible-to-find bugs 
> in unrelated parts of the kernel. There is no point going on reporting 
> such error condition to upper layers until the software, or possibly the 
> hardware, is fixed

Again, I think adding a WARN_ON() and letting hci_dma_dequeue_xfer()
return an error code is a good compromise. 

> 
> > > +struct hci_xfer {
> > > +	u32 cmd_desc[4];
> > > +	u32 response;
> > > +	bool rnw;
> > > +	void *data;
> > > +	u_int data_len;
> > > +	u_int cmd_tid;
> > > +	struct completion *completion;
> > > +	union {
> > > +		struct {
> > > +			/* PIO specific */
> > > +			struct hci_xfer *next_xfer;
> > > +			struct hci_xfer *next_data;
> > > +			struct hci_xfer *next_resp;
> > > +			u_int data_left;
> > > +			u32 data_word_before_partial;
> > > +		};  
> > 
> > I think anonymous unions are prohibited because the kernel is supposed
> > to be built with old gcc versions which do not support it.  
> 
> Hmmm... let's see:
> 
> According to Documentation/process/changes.rst the current minimal 
> required gcc version is 4.9.
> 
> The oldest gcc I have lying around is 4.5.1 and it supports anonymous 
> unions just fine.
> 
> So I think we're clear.

We had problems with a buggy version of gcc 4.<something> at some
point, and that was only when initializing anonymous unions, but I
think it's all behind us now.



More information about the linux-i3c mailing list