RE: [RFC PATCH 1/6] Core files for the DWC2 driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> From: Felipe Balbi [mailto:balbi@xxxxxx]
> Sent: Friday, January 11, 2013 1:44 AM
> 
> Hi,

Hi Felipe, thanks for the review.

> On Mon, Jan 07, 2013 at 11:51:36AM -0800, Paul Zimmerman wrote:
> >
> > Signed-off-by: Paul Zimmerman <paulz@xxxxxxxxxxxx>
> 
> no commit log == no commit ;-)

Will fix next time.

> Also, you shouldn't have reset the version counter. 2 years ago this
> same driver was already on v17, then someone else tried and did a few
> extra versions...

I meant to just remove the version number, will do that in the next
revision.

> ... There are a few comments below, probably should be
> more but all your files are so big that I just got bored, sorry.

Sorry. I know it's a freaking huge driver, but this core has a lot of
optional features, three DMA modes, yadda yadda. I actually
removed code for supporting several more recent features before
submitting this! Would it help if I did one patch per file?

By the way, I probably should have mentioned this originally. I did not
really write this driver. I took our vendor driver that was written by
another team, and modified it so it would at least have a chance of
being acceptable for the kernel. So I replaced the proprietary license
with BSD/GPL, removed the portability abstraction layer, and fixed up
the coding and comment style. I did fix some logic problems along
the way. And I did become somewhat familiar with how the driver
works while fixing the bugs I introduced while modifying it. But I am
far from being an expert on how it all works.

> I would like it much better if you would follow what we did for dwc3 and
> have core IP in a separate driver with pci/exynos/whatever glue in a
> parent device driver. Still, if you really want to make core IP driver a
> library, then at a minimum do it right, don't expose ALL functions, make
> sure you have a small set of APIs for the glue to call. Encapsulate
> implementation details from your users, they really don't need to call
> each function separately. Ideally pci glue would call dwc2_initialize()
> on probe() and that's it.

Once we get to the point of integrating another interface, like for a
platform driver, I plan to make the core code a separate module, so
the code can be shared instead of being duplicated in each driver. But
we're not quite there yet.

But I disagree with the "small set of APIs" thing. It would be a HUGE
amount of effort to rewrite the code like that. The reason the code
is in separate files is to give it a somewhat logical arrangement, and to
avoid having one gargantuan source file. There is no intention to have
individual APIs for each of the files. I disagree that there are any
external "users" like you mention above, it is all one driver.

As a global response to your complaints about all the debug
statements: I said in the first email in the series that I would like to
keep those for the time being, until we have the s3c-hsotg driver
integrated and there are no major issues remaining, and then
strip them all out. I found them _very_ useful for catching my
mistakes after doing the initial conversion from the vendor driver,
and I am sure they will be needed once we start integrating the
s3c-hsotg code.

> Anyway, more comments below.
> 
> > +void dwc2_enable_global_interrupts(struct dwc2_hcd *hcd)
> > +{
> > +	u32 ahbcfg = readl(hcd->regs + GAHBCFG);
> > +
> > +	ahbcfg |= GAHBCFG_GlblIntrEn;
> > +	writel(ahbcfg, hcd->regs + GAHBCFG);
> 
> personally, I would add private static inline wrappers around writel()
> and readl(), but not a big deal if you want to use directly.

I think I will leave it as is, unless you have a concrete reason for having
a wrapper?

> > +static void enable_common_interrupts(struct dwc2_hcd *hcd)
> 
> even though it's a static function, I still like to see consistency on
> function names, please prepend this one too with dwc2_.

I would rather not. There are a heap of static functions in all of these
files, and it seems pointless to rename them all for no good reason
other than style.

> > +	intmsk |= GINTSTS_ConIDStsChng;
> > +	intmsk |= GINTSTS_WkUpInt;
> > +	intmsk |= GINTSTS_USBSusp;
> > +	intmsk |= GINTSTS_SessReqInt;
> 
> no CaMeLcAsE in kernel code. Make it all upper case for the defines.

We are reusing s3c-hsotg.h for a lot of the register definitions, so I can't
change this without breaking that driver. Once this driver is integrated with
the s3c-hsotg driver, we can make a cleanup patch and fix them all.

> > +/*
> > + * Initializes the FSLSPClkSel field of the HCFG register depending on the
> > + * PHY type
> > + */
> > +static void init_fslspclksel(struct dwc2_hcd *hcd)
> 
> this name can be improved.

Sure, no problem.

> > +	if (((hcd->hwcfg2 & GHWCFG2_HS_PHY_TYPE_MASK) ==
> > +	     GHWCFG2_HS_PHY_TYPE_ULPI &&
> > +	     (hcd->hwcfg2 & GHWCFG2_FS_PHY_TYPE_MASK) ==
> > +	     GHWCFG2_FS_PHY_TYPE_DEDICATED &&
> > +	     hcd->core_params->ulpi_fs_ls > 0) ||
> > +	    hcd->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) {
> > +		/* Full speed PHY */
> > +		val = HCFG_FSLSPCLKSEL_48_MHZ;
> > +	} else {
> > +		/* High speed PHY running at full speed or high speed */
> > +		val = HCFG_FSLSPCLKSEL_30_60_MHZ;
> > +	}
> 
> how about:
> 
> 	phy_type = hcd->hwcfg2 & GHWCFG2_HS_PHY_TYPE_MASK;
> 	switch(phy_type) {
> 	case GHWCFG2_HS_PHY_TYPE_ULPI:
> 	case GHWCFG2_HS_PHY_TYPE_DEDICATED:
> 		foo();
> 		break;
> 	default:
> 		bar();
> 	}

OK.

> +	/* Wait for AHB master IDLE state */
> > +	do {
> > +		msleep(20);
> > +		greset = readl(hcd->regs + GRSTCTL);
> > +		if (++count > 500) {
> 
> hmm... 10 seconds waiting ? No no no... maybe you meant usleep above ?
> If that's the case, use usleep_range(1000, 5000) or something similar.

No, I meant msleep. I agree 10 seconds is pretty excessive, I will reduce it
to something sane.

> > +	do {
> > +		msleep(20);
> > +		greset = readl(hcd->regs + GRSTCTL);
> > +		if (++count > 500) {
> 
> 10 more seconds ?!? wow

ditto.

> > +	/*
> > +	 * NOTE: This long sleep is _very_ important, otherwise the core will
> > +	 * not stay in host mode after a connector ID change!
> > +	 */
> > +	msleep(100);
> 
> this comment needs further explanation, badly :-)

I know, but I don't know the reason :) See "I didn't really write this driver" above.

> > +	/*
> > +	 * This programming sequence needs to happen in FS mode before any other
> > +	 * programming occurs
> > +	 */
> 
> why ?

Ditto.

> > +	if ((hcd->hwcfg2 & GHWCFG2_HS_PHY_TYPE_MASK) ==
> > +	    GHWCFG2_HS_PHY_TYPE_ULPI &&
> > +	    (hcd->hwcfg2 & GHWCFG2_FS_PHY_TYPE_MASK) ==
> > +	    GHWCFG2_FS_PHY_TYPE_DEDICATED && hcd->core_params->ulpi_fs_ls > 0) {
> 
> switch ?

Sure.

> > +	case GHWCFG2_INT_DMA_ARCH:
> > +		dev_dbg(hcd->dev, "Internal DMA Mode\n");
> > +		/*
> > +		 * Old value was GAHBCFG_HBstLen_Incr - done for
> > +		 * Host mode ISOC in issue fix - vahrama
> > +		 */
> > +		ahbcfg &= ~GAHBCFG_HBstLen_MASK;
> > +		ahbcfg |= GAHBCFG_HBstLen_Incr4;
> > +		hcd->dma_enable = hcd->core_params->dma_enable > 0;
> > +		hcd->dma_desc_enable = hcd->core_params->dma_desc_enable > 0;
> > +		break;
> 
> if you can have internal and external DMA support, you ought to use
> dmaengine API. In case of internal DMA, make this driver register a dma
> engine device and request it back.

I will need to look into that, I'm not familiar with the dmaengine API.

> > +	/* Set OTG version supported */
> > +	hcd->otg_ver = hcd->core_params->otg_ver > 0;
> 
> comment is wrong, right hand side will evaluate to true or false, not
> the OTG version supported.

Will fix.

> BTW, what a HUGE function... You can surely refactor parts of it to
> other functions which are called by this one. Also, I don't think driver
> initialization should be exposed, the only reason for that is that
> you're not giving the core driver its own platform_device and, believe
> me, you will be hurt by this choice.
> 
> We have gone through that before, giving core driver its own
> platform_device will make it a lot easier to "hide" platform details on
> a parent platform_device, just like Sebastian and I did for dwc3.

Sorry, after trying to add some features to the dwc3 driver, I became
convinced that making the core code a separate platform device is not
something I want to do. As I said, I will make it a separate module once
we have a second bus interface to connect.

I will look at refactoring the function, though.

> > +/**
> > + * dwc2_enable_host_interrupts() - Enables the Host mode interrupts
> > + *
> > + * @hcd: Programming view of DWC_otg controller
> > + */
> > +void dwc2_enable_host_interrupts(struct dwc2_hcd *hcd)
> 
> should be static.

OK. There may be a lot of functions that could be static but are not. I
converted some of them but haven't swept the whole driver yet.

> > +/**
> > + * dwc2_disable_host_interrupts() - Disables the Host Mode interrupts
> > + *
> > + * @hcd: Programming view of DWC_otg controller
> > + */
> > +void dwc2_disable_host_interrupts(struct dwc2_hcd *hcd)
>
> should be static

OK.

> > +		if (!((hcd->hwcfg4 & GHWCFG4_DESC_DMA) &&
> > +		      hcd->snpsid >= DWC2_CORE_REV_2_90a &&
> > +		      (op_mode == GHWCFG2_OP_MODE_HNP_SRP_CAPABLE ||
> > +		       op_mode == GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE ||
> > +		       op_mode == GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE ||
> > +		       op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST ||
> > +		       op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST))) {
> > +
> > +			dev_err(hcd->dev,
> > +				"Host can't operate in Descriptor DMA mode.\n"
> > +				"Either core version is below 2.90a or "
> > +				"GHWCFG2, GHWCFG4 register values do not "
> > +				"allow Descriptor DMA in host mode.\nTo "
> > +				"run the driver in Buffer DMA host mode set "
> > +				"dma_desc_enable module parameter to 0.\n");
> > +			return;
> > +		}
> 
> this big if just to print a dev_err() ? And what a dev_err()... wow.
> Instead of erroring out, you could just fix the parameters yourself in
> runtime.

We want to inform the user that they did not get descriptor DMA mode
although they asked for it. I agree it's a little verbose, something like
"hardware does not support DDMA mode" would suffice.

Your right about fixing the parameters at runtime, I plan to do that in
the next revision.

> > +void dwc2_hc_init(struct dwc2_hcd *hcd, struct dwc2_hc *hc)
>
> static

OK

> > +#ifdef DEBUG
> > +		dev_info(hcd->dev,
> > +			"*** %s: Channel %d, hc->halt_pending already set ***\n",
> > +			__func__, hc->hc_num);
> > +
> > +#endif
> 
> this is called dev_dbg().

Right, will fix.

> 
> > +/**
> > + * dwc2_dump_spram() - Reads the SPRAM and prints its content
> > + *
> > + * @hcd: Programming view of DWC_otg controller
> > + */
> > +void dwc2_dump_spram(struct dwc2_hcd *hcd)
> 
> debugfs using regsets

I don't think it's useful, I'll just remove it.

> > +/**
> > + * dwc2_dump_host_registers() - Reads the host registers and prints them
> > + *
> > + * @hcd: Programming view of DWC_otg controller
> > + */
> > +void dwc2_dump_host_registers(struct dwc2_hcd *hcd)
> 
> debugfs using regsets.

Actually, I want to keep it this way for now. This makes it easy to just insert
a call to dwc2_dump_host_registers() in a troublesome spot in the code
while debugging, and get a snapshot of the registers in the dmesg. I don't
think I can do that with regsets, can I? I any case, this will be removed once
the driver is stable.

> > +int dwc2_check_core_status(struct dwc2_hcd *hcd)
> > +{
> > +	if (readl(hcd->regs + GSNPSID) == 0xffffffff)
> > +		return -1;
> > +	else
> > +		return 0;
> > +}
> 
> the whole thing is exposed. I don't like that at all, you need to
> encapsulate implementation details. In fact, core.c should be a
> platform_driver in its own right. Your PCI layer should just be a parent
> device to this one just like on dwc3.

I'm not sure how that applies here. This is just a simple routine to check
that the hardware has not gone away unexpectedly.

> > +#if 0
> 
> we don't like commented code, remove it.
> 
> > +static inline void do_write(u32 value, void *addr)
> > +{
> > +	writel(value, addr);
> > +	pr_info("INFO:: wrote %08x to %p\n", value, addr);
> > +}
> > +
> > +#undef writel
> > +#define writel(v, a)	do_write(v, a)
> > +#endif

This is part of the debugging code I mentioned. By enabling this, I get a trace
of every register write the driver does. This is indispensable for debugging.
Again, it will be removed once the driver is stable.

> > +/*
> > + * Host channel descriptor. This structure represents the state of a single
> > + * host channel when acting in host mode. It contains the data items needed to
> > + * transfer packets to an endpoint via a host channel.
> > + */
> > +struct dwc2_hc {
> > +	/* Host channel number used for register address lookup */
> > +	u8 hc_num;
> > +
> > +	/* Device to access */
> > +	unsigned dev_addr:7;
> 
> this is not how we document structures

Right, kerneldoc. I will add that to all of the structure definitions.

> > +static const char *op_state_str(struct dwc2_hcd *hcd)
> > +{
> > +#ifdef DEBUG
> > +	return (hcd->op_state == A_HOST ? "a_host" :
> > +		(hcd->op_state == A_SUSPEND ? "a_suspend" :
> > +		 (hcd->op_state == A_PERIPHERAL ? "a_peripheral" :
> > +		  (hcd->op_state == B_PERIPHERAL ? "b_peripheral" :
> > +		   (hcd->op_state == B_HOST ? "b_host" : "unknown")))));
> 
> switch ?

OK.

> > +
> > +#ifndef DWC_HOST_ONLY
> > +	if (dwc2_is_device_mode(hcd)) {
> > +		dev_info(hcd->dev, "SRP: Device mode\n");
> > +	} else {
> > +		u32 hprt0;
> > +
> > +		dev_info(hcd->dev, "SRP: Host mode\n");
> > +
> > +		/* Turn on the port power bit */
> > +		hprt0 = dwc2_read_hprt0(hcd);
> > +		hprt0 |= HPRT0_PWR;
> > +		writel(hprt0, hcd->regs + HPRT0);
> > +
> > +		/*
> > +		 * Start the Connection timer. So a message can be displayed if
> > +		 * connect does not occur within 10 seconds.
> > +		 */
> > +		dwc2_hcd_session_start(hcd);
> > +	}
> > +#endif
> 
> this check should be done in runtime, no ?

Right now we don't have a device-mode driver, so this is code is just a
placeholder until then.

> > +#ifdef DEBUG
> > +	/* If any common interrupts set */
> > +	if (gintsts & gintmsk_common)
> > +		dev_dbg(hcd->dev, "gintsts=%08x  gintmsk=%08x\n",
> > +			gintsts, gintmsk);
> > +#endif
> 
> dev_vdbg() or just remove the ifdef. dev_dbg() is only valid when
> DEBUG=y

OK

> > +int dwc2_handle_common_intr(void *dev)
> 
> even your interrupt handler is exposed ? wow
> 
> > +#include "s3c-hsotg.h"
> 
> we don't want to depend on that, how about just copying it here ? In
> fact, this file isn't available...

Yes, it is available, I added "-I drivers/usb/gadget" to the Makefile. I
thought unneeded code duplication is a big no-no in the kernel, am I
wrong?

-- 
Paul

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux