[PATCH 1/3] usb: dwc3: Add Keystone specific glue layer
Felipe Balbi
balbi at ti.com
Mon Nov 25 15:39:56 EST 2013
Hi,
On Mon, Nov 25, 2013 at 03:16:19PM -0500, WingMan Kwok wrote:
> diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c
> new file mode 100644
> index 0000000..a4c9cbc
> --- /dev/null
> +++ b/drivers/usb/dwc3/dwc3-keystone.c
> @@ -0,0 +1,272 @@
> +/**
> + * dwc3-keystone.c - Keystone Specific Glue layer
> + *
> + * Copyright (C) 2010-2013 Texas Instruments Incorporated - http://www.ti.com
> + *
> + * Authors: WingMan Kwok <w-kwok2 at ti.com>
singular -> Author :-) there's only a single one.
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 of
> + * the License as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/spinlock.h>
> +#include <linux/platform_device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/ioport.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/usb/usb_phy_gen_xceiv.h>
> +
> +
> +#define BITS(n) (BIT(n) - 1)
looks like unnecessary obfuscation.
> +#define BITFIELD(x, s, n) (((x) & BITS(n)) << (s))
likewise.
> +#define MASK 0xffffffff
huh ? You probably want a better name here.
> +#define PHY_REF_SSP_EN(x) BITFIELD(x, 29, 1)
hmm, should be part of a PHY driver (drivers/phy/)
> +static u64 kdwc3_dma_mask;
> +
> +struct kdwc3_phy_ctrl_regs {
> + u32 phy_utmi;
> + u32 phy_pipe;
> + u32 phy_param_ctrl_1;
> + u32 phy_param_ctrl_2;
> + u32 phy_clock;
> + u32 phy_pll;
> +};
should be part of a PHY driver. Sorry, but I'll be very pedantic.
> +struct kdwc3_irq_regs {
> + u32 revision;
> + u32 _rsvd0[3];
> + u32 sysconfig;
> + u32 _rsvd1[1];
> + u32 irq_eoi;
> + u32 _rsvd2[1];
> + struct {
> + u32 raw_status;
> + u32 status;
> + u32 enable_set;
> + u32 enable_clr;
> + } irqs[16];
> +};
please use #define macros instead.
> +struct dwc3_keystone {
> + spinlock_t lock;
> + struct device *dev;
> + struct clk *clk;
> + int irqn;
> +
> + struct kdwc3_phy_ctrl_regs __iomem *phy_ctrl;
not part of this driver.
> + struct kdwc3_irq_regs __iomem *usbss;
> +};
> +
> +static void kdwc3_enable_irqs(struct dwc3_keystone *kdwc)
> +{
> + writel(1, &kdwc->usbss->irqs[kdwc->irqn].enable_set);
I would like to have a define here, no magic constants.
> +}
> +
> +static void kdwc3_disable_irqs(struct dwc3_keystone *kdwc)
> +{
> + writel(0, &kdwc->usbss->irqs[kdwc->irqn].enable_set);
> +}
you want these two functions to be more generic, along the lines of:
static void kdwc3_writel(void __iomem *base, u32 offset, u32 val)
{
writel(val, base + offset);
}
static u32 kdwc3_readl(void __iomem *base, u32 offset)
{
return readl(base + offset);
}
then you can add:
static void kdwc3_enable_irqs(struct dwc3_keystone *kdwc)
{
u32 reg;
reg = kdwc3_readl(kdwc->base, KEYSTONE_DWC3_IRQ);
reg |= KEYSTONE_DWC3_IRQ_ENABLE;
kdwc3_writel(kdwc->base, KEYSTONE_DWC3_IRQ, reg);
}
static void kdwc3_disable_irqs(struct dwc3_keystone *kdwc)
{
u32 reg;
reg = kdwc3_readl(kdwc->base, KEYSTONE_DWC3_IRQ);
reg &= ~KEYSTONE_DWC3_IRQ_ENABLE;
kdwc3_writel(kdwc->base, KEYSTONE_DWC3_IRQ, reg);
}
this will be much more "future-safe".
> +static int kdwc3_enable(struct dwc3_keystone *kdwc)
> +{
> + int error;
> + u32 val;
> +
> + kdwc->clk = devm_clk_get(kdwc->dev, "usb");
> + if (IS_ERR_OR_NULL(kdwc->clk)) {
> + dev_err(kdwc->dev, "unable to get kdwc usb clock\n");
> + return -ENODEV;
> + }
> +
> + val = readl(&kdwc->phy_ctrl->phy_clock);
no PHY registers will ever be accessed from a glue layer ;-) This should
all be part of a phy driver (drivers/phy/phy-keystone.c) and dwc3.ko
will take care of the rest.
> + writel(val | PHY_REF_SSP_EN(1), &kdwc->phy_ctrl->phy_clock);
> + udelay(20);
> +
> + error = clk_prepare_enable(kdwc->clk);
> + if (error < 0) {
> + dev_dbg(kdwc->dev, "unable to enable usb clock, err %d\n",
> + error);
> + writel(val, &kdwc->phy_ctrl->phy_clock);
> + return error;
> + }
> +
> + /* soft reset usbss */
> + writel(1, &kdwc->usbss->sysconfig);
shoul we access sysconfig registers from drivers ? How about using a
reset driver ? (drivers/reset/).
> + while (readl(&kdwc->usbss->sysconfig) & 1)
> + ;
infinite loop ? Never, please add a timeout.
> + val = readl(&kdwc->usbss->revision);
> + dev_info(kdwc->dev, "usbss revision %x\n", val);
unnecessary dev_info(). You're not even decoding the revision
information.
> + return 0;
> +}
> +
> +static void kdwc3_disable(struct dwc3_keystone *kdwc)
> +{
> + u32 val;
> +
> + clk_disable_unprepare(kdwc->clk);
> +
> + val = readl(&kdwc->phy_ctrl->phy_clock);
> + val &= ~PHY_REF_SSP_EN(MASK);
> + writel(val, &kdwc->phy_ctrl->phy_clock);
should not be done here.
> +}
> +
> +static irqreturn_t dwc3_keystone_interrupt(int irq, void *_kdwc)
> +{
> + struct dwc3_keystone *kdwc = _kdwc;
> + int irqn = kdwc->irqn;
why ? the irq argument should already be the correct IRQ number, right ?
> + spin_lock(&kdwc->lock);
> + writel(1, &kdwc->usbss->irqs[irqn].enable_clr);
> + writel(1, &kdwc->usbss->irqs[irqn].status);
> + writel(1, &kdwc->usbss->irqs[irqn].enable_set);
no magic constants...
> + writel(BIT(irqn), &kdwc->usbss->irq_eoi);
> + spin_unlock(&kdwc->lock);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int kdwc3_probe(struct platform_device *pdev)
> +{
> + struct device_node *node = pdev->dev.of_node;
> + struct device *dev = &pdev->dev;
> + struct dwc3_keystone *kdwc;
> + struct resource *res;
> + int error, irq;
> +
> + if (!node) {
> + dev_err(dev, "device node not found\n");
> + return -EINVAL;
> + }
if this will only be used on DT-based boot, node *must* be true. If it's
not, we're dealing with a pretty critical bug, in which case we want a
Kernel Oops to happen so we catch that bug, please drop this check.
> +
> + kdwc = devm_kzalloc(dev, sizeof(*kdwc), GFP_KERNEL);
> + if (!kdwc)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, kdwc);
> +
> + spin_lock_init(&kdwc->lock);
> + kdwc->dev = dev;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "missing usbss resource\n");
> + return -EINVAL;
> + }
> +
> + kdwc->usbss = devm_ioremap_resource(dev, res);
> + if (IS_ERR(kdwc->usbss)) {
> + dev_err(dev, "ioremap failed on usbss region\n");
no need to print anything, devm_ioremap_resource() already prints
sensible messages.
> + return PTR_ERR(kdwc->usbss);
> + }
> +
> + dev_dbg(dev, "usbss control start=%08x size=%d mapped=%08x\n",
> + (u32)(res->start), (int)resource_size(res),
> + (u32)(kdwc->usbss));
this is really unnecessary, if you really want to have it, move it to
dev_vdbg().
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + if (!res) {
> + dev_err(dev, "missing usb phy resource\n");
> + return -EINVAL;
> + }
> +
> + kdwc->phy_ctrl = devm_ioremap_resource(dev, res);
> + if (IS_ERR(kdwc->phy_ctrl)) {
> + dev_err(dev, "ioremap failed on usb phy region\n");
> + return PTR_ERR(kdwc->phy_ctrl);
> + }
second resource should be part of the PHY driver. This is even more
important considering you're using DT, we don't want to allow broken DTS
data to be accepted.
> + dev_dbg(dev, "phy control start=%08x size=%d mapped=%08x\n",
> + (u32)(res->start), (int)resource_size(res),
> + (u32)(kdwc->phy_ctrl));
unnecessary.
> + kdwc3_dma_mask = dma_get_mask(dev);
> + dev->dma_mask = &kdwc3_dma_mask;
> +
> + error = kdwc3_enable(kdwc);
I would drop this function and just add your clk_prepare() here, then
move clk_enable()/clk_disable() to ->runtime_resume/->runtime_suspend()
respectively. Then you could just call pm_runtime_get_sync() when you
need to access your registers and pm_runtime_put() when you want to drop
the clock reference.
this will even make PM implementation a lot easier for you going
forward.
> + if (error)
> + return error;
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + dev_err(&pdev->dev, "missing irq\n");
> + goto err_irq;
> + }
> +
> + error = devm_request_irq(dev, irq, dwc3_keystone_interrupt, IRQF_SHARED,
> + "keystone-dwc3", kdwc);
dev_name(dev) ?? If you happen to have multiple instances of a dwc3
core, it'll give it better names.
> + if (error) {
> + dev_err(dev, "failed to request IRQ #%d --> %d\n",
> + irq, error);
> + goto err_irq;
> + }
> +
> + kdwc->irqn = 0;
wait, what ? Can you describe how this part works ? Why do you even need
this if it's alway zero ?
> + kdwc3_enable_irqs(kdwc);
> +
> + error = of_platform_populate(node, NULL, NULL, dev);
> + if (error) {
> + dev_err(&pdev->dev, "failed to create dwc3 core\n");
> + goto err_core;
> + }
> +
> + return 0;
> +
> +err_core:
> + kdwc3_disable_irqs(kdwc);
> +err_irq:
> + kdwc3_disable(kdwc);
> +
> + return error;
> +}
> +
> +static int kdwc3_remove(struct platform_device *pdev)
> +{
> + struct dwc3_keystone *kdwc = platform_get_drvdata(pdev);
> +
> + if (kdwc) {
kdwc will never be true, you can drop this check.
> + kdwc3_disable_irqs(kdwc);
> + kdwc3_disable(kdwc);
> + platform_set_drvdata(pdev, NULL);
> + }
> + return 0;
> +}
> +
> +static const struct of_device_id kdwc3_of_match[] = {
> + { .compatible = "ti,keystone-dwc3", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, kdwc3_of_match);
> +
> +static struct platform_driver kdwc3_driver = {
> + .probe = kdwc3_probe,
> + .remove = kdwc3_remove,
> + .driver = {
> + .name = "keystone-dwc3",
> + .owner = THIS_MODULE,
> + .of_match_table = kdwc3_of_match,
> + },
> +};
> +
> +module_platform_driver(kdwc3_driver);
> +
> +MODULE_ALIAS("platform:keystone-dwc3");
> +MODULE_AUTHOR("WingMan Kwok <w-kwok2 at ti.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("DesignWare USB3 KEYSTONE Glue Layer");
> --
> 1.7.9.5
>
--
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20131125/355dad57/attachment.sig>
More information about the linux-arm-kernel
mailing list