[PATCH 4/7] mx28: add usb host phy functions
Lin Tony-B19295
B19295 at freescale.com
Thu Jul 21 22:23:59 EDT 2011
> -----Original Message-----
> From: Sascha Hauer [mailto:s.hauer at pengutronix.de]
> Sent: Thursday, July 21, 2011 3:26 AM
> To: Lin Tony-B19295
> Cc: linux-usb at vger.kernel.org; koen.beel.barco at gmail.com; balbi at ti.com;
> linux-arm-kernel at lists.infradead.org
> Subject: Re: [PATCH 4/7] mx28: add usb host phy functions
>
> On Wed, Jul 20, 2011 at 07:08:23PM +0800, Tony Lin wrote:
> > add usb phy register definitions and functions usb host driver will
> > use these functions to initialize usb phy and change phy working mode
> >
> >
> > obj-y += devices/
> > diff --git a/arch/arm/mach-mxs/regs-usbphy-mx28.h
> > b/arch/arm/mach-mxs/regs-usbphy-mx28.h
> > new file mode 100644
> > index 0000000..a367927
> > --- /dev/null
> > +++ b/arch/arm/mach-mxs/regs-usbphy-mx28.h
> > @@ -0,0 +1,323 @@
> > +/*
> > + * Freescale USBPHY Register Definitions
> > + *
> > + * Copyright 2008-2011 Freescale Semiconductor, Inc. All Rights
> Reserved.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > +modify
> > + * it under the terms of the GNU General Public License as published
> > +by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * 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.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, write to the Free Software
> > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
> > +02111-1307 USA
> > + *
> > + * This file is created by xml file. Don't Edit it.
>
> Please remove this comment. We can and will change this header if need
> arises.
>
> > + *
> > + * Xml Revision: 1.52
> > + * Template revision: 26195
> > + */
> > +
> > +#ifndef __ARCH_ARM___USBPHY_H
> > +#define __ARCH_ARM___USBPHY_H
> > +
> > +
> > +#define HW_USBPHY_PWD (0x00000000)
> > +#define HW_USBPHY_PWD_SET (0x00000004)
> > +#define HW_USBPHY_PWD_CLR (0x00000008)
> > +#define HW_USBPHY_PWD_TOG (0x0000000c)
> > +
> > +#define BP_USBPHY_PWD_RSVD2 21
> > +#define BM_USBPHY_PWD_RSVD2 0xFFE00000
> > +#define BF_USBPHY_PWD_RSVD2(v) \
> > + (((v) << 21) & BM_USBPHY_PWD_RSVD2)
> > +#define BM_USBPHY_PWD_RXPWDRX 0x00100000
> > +#define BM_USBPHY_PWD_RXPWDDIFF 0x00080000
> > +#define BM_USBPHY_PWD_RXPWD1PT1 0x00040000
> > +#define BM_USBPHY_PWD_RXPWDENV 0x00020000
> > +#define BP_USBPHY_PWD_RSVD1 13
> > +#define BM_USBPHY_PWD_RSVD1 0x0001E000
> > +#define BF_USBPHY_PWD_RSVD1(v) \
> > + (((v) << 13) & BM_USBPHY_PWD_RSVD1)
>
> Please remove all these reserved bits. They only reduce readability
>
>
> > diff --git a/arch/arm/mach-mxs/usb_h1.c b/arch/arm/mach-mxs/usb_h1.c
>
> From what I know the i.MX28 has two identical USB phys. There shouldn't
> be a file handling only one of these.
>
> > new file mode 100644
> > index 0000000..9ca5236
> > --- /dev/null
> > +++ b/arch/arm/mach-mxs/usb_h1.c
> > @@ -0,0 +1,230 @@
> > +/*
> > + * Copyright (C) 2009-2011 Freescale Semiconductor, Inc. All Rights
> Reserved.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > +modify
> > + * it under the terms of the GNU General Public License as published
> > +by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * 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.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > +along
> > + * with this program; if not, write to the Free Software Foundation,
> > +Inc.,
> > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/types.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/io.h>
> > +#include <linux/err.h>
> > +#include <linux/fsl_devices.h>
> > +#include <linux/gpio.h>
> > +#include <asm/mach-types.h>
> > +#include <asm/mach/arch.h>
> > +#include <mach/irqs.h>
> > +#include <mach/mx28.h>
> > +#include "regs-usbphy-mx28.h"
> > +
> > +/* EHCI registers: */
> > +#define UOG_USBCMD (0x140)/* USB command register */
> > +#define UOG_PORTSC1 (0x184)/* port status and control */
> > +/* x_PORTSCx */
> > +#define PORTSC_PTS_MASK (3 << 30)/* parallel xcvr mask */
> > +#define PORTSC_PTS_UTMI (0 << 30)/* UTMI/UTMI+ */
> > +#define PORTSC_PTW (1 << 28)/* UTMI width */
> > +/* USBCMD */
> > +#define UCMD_RUN_STOP (1 << 0)/* controller run/stop */
> > +#define UCMD_RESET (1 << 1)/* controller reset */
> > +
> > +#define HOSTPHY_CONNECT_STATE (1 << 3)
> > +
> > +static struct clk *usb_clk;
> > +static struct clk *usb_phy_clk;
> > +static int internal_phy_clk_already_on;
>
> Please get rid fo these static variables. The i.MX28 has two USB phys and
> two EHCI cores. All of these should be devices and the context should be
> stored in data private to the driver.
>
> > +
> > +/* The dmamask must be set for EHCI to work */ static u64
> > +ehci_dmamask = ~(u32) 0; static int instance_id = ~(u32) 0;
> > +
> > +static int fsl_platform_get_usb_conn_status(void)
> > +{
> > + u32 status;
> > +
> > + status = __raw_readl(MX28_IO_ADDRESS(MX28_USBPHY1_BASE_ADDR) + \
> > + HW_USBPHY_STATUS);
> > + return ((status & HOSTPHY_CONNECT_STATE) == 0); }
>
> A function which takes absolutely no context looks suspicious.
>
> > +
> > +/* enable/disable high-speed disconnect detector of phy ctrl */
> > +static void fsl_platform_set_usb_phy_dis(int enable) {
> > + if (enable)
> > + __raw_writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
> > + MX28_IO_ADDRESS(MX28_USBPHY1_BASE_ADDR) + HW_USBPHY_CTRL_SET);
> > + else
> > + __raw_writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
> > + MX28_IO_ADDRESS(MX28_USBPHY1_BASE_ADDR) +
> HW_USBPHY_CTRL_CLR); }
> > +
> > +static int usb_phy_enable(struct mxc_usbh_platform_data *pdata) {
> > + u32 tmp;
> > + void __iomem *phy_reg = MX28_IO_ADDRESS(MX28_USBPHY1_BASE_ADDR);
> > + void __iomem *usb_reg = MX28_IO_ADDRESS(MX28_USBCTRL1_BASE_ADDR);
>
> This function takes device specific context and still it uses hardcoded
> usbphy1 registers.
>
> > + void __iomem *usbcmd, *phy_ctrl, *portsc;
> > + /* Reset USB IP */
> > + usbcmd = usb_reg + UOG_USBCMD;
> > + tmp = __raw_readl(usbcmd); /* usb command */
> > + tmp &= ~UCMD_RUN_STOP;
> > + __raw_writel(tmp, usbcmd);
> > + while (__raw_readl(usbcmd) & UCMD_RUN_STOP)
> > + ;
>
> No potentially endless loops please.
>
> > + tmp |= UCMD_RESET;
> > + __raw_writel(tmp, usbcmd);
> > + while (__raw_readl(usbcmd) & UCMD_RESET)
> > + ;
> > + mdelay(10);
> > + /* Reset USBPHY module */
> > + phy_ctrl = phy_reg + HW_USBPHY_CTRL;
> > + tmp = __raw_readl(phy_ctrl);
> > + tmp |= BM_USBPHY_CTRL_SFTRST;
> > + __raw_writel(tmp, phy_ctrl);
> > + udelay(10);
> > + /* Remove CLKGATE and SFTRST */
> > + tmp = __raw_readl(phy_ctrl);
> > + tmp &= ~(BM_USBPHY_CTRL_CLKGATE | BM_USBPHY_CTRL_SFTRST);
> > + __raw_writel(tmp, phy_ctrl);
> > + udelay(10);
> > + /* set UTMI xcvr */
> > + /* Workaround an IC issue for ehci driver:
> > + * when turn off root hub port power, EHCI set
> > + * PORTSC reserved bits to be 0, but PTW with 0
> > + * means 8 bits tranceiver width, here change
> > + * it back to be 16 bits and do PHY diable and
> > + * then enable.
> > + */
> > + portsc = usb_reg + UOG_PORTSC1;
> > + tmp = __raw_readl(portsc);
> > + tmp &= ~PORTSC_PTS_MASK;
> > + tmp |= (PORTSC_PTS_UTMI | PORTSC_PTW);
> > + __raw_writel(tmp, portsc);
> > + /* Power up the PHY */
> > + __raw_writel(0, phy_reg + HW_USBPHY_PWD);
> > + return 0;
> > +}
> > +
> > +static int fsl_usb_host_init(struct platform_device *pdev) {
> > + struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data;
> > + void __iomem *phy_reg = MX28_IO_ADDRESS(MX28_USBPHY1_BASE_ADDR);
> > + u32 tmp;
> > +
> > + usb_phy_enable(pdata);
> > + /* enable FS/LS device */
> > + tmp = __raw_readl(phy_reg + HW_USBPHY_CTRL);
> > + tmp |= (BM_USBPHY_CTRL_ENUTMILEVEL2 | BM_USBPHY_CTRL_ENUTMILEVEL3);
> > + __raw_writel(tmp, phy_reg + HW_USBPHY_CTRL);
> > +
> > + return 0;
> > +}
> > +
> > +static void usbh1_internal_phy_clock_gate(bool on) {
> > + u32 tmp;
> > + void __iomem *phy_reg = MX28_IO_ADDRESS(MX28_USBPHY1_BASE_ADDR);
> > +
> > + if (on) {
> > + internal_phy_clk_already_on += 1;
> > + if (internal_phy_clk_already_on == 1) {
> > + tmp = BM_USBPHY_CTRL_SFTRST | BM_USBPHY_CTRL_CLKGATE;
> > + __raw_writel(tmp, phy_reg + HW_USBPHY_CTRL_CLR);
> > + }
> > + } else {
> > + internal_phy_clk_already_on -= 1;
> > + if (internal_phy_clk_already_on == 0) {
> > + tmp = BM_USBPHY_CTRL_CLKGATE;
> > + __raw_writel(tmp, phy_reg + HW_USBPHY_CTRL_SET);
> > + }
> > + }
> > + if (internal_phy_clk_already_on < 0)
> > + printk(KERN_ERR "please check phy clock ON/OFF sequence\n"); }
> > +static int fsl_usb_host_init_ext(struct platform_device *pdev) {
> > + usb_clk = clk_get(NULL, "usb1");
> > + clk_enable(usb_clk);
> > + clk_put(usb_clk);
> > + usb_phy_clk = clk_get(NULL, "usb1_phy");
> > + clk_enable(usb_phy_clk);
> > + clk_put(usb_phy_clk);
> > +
> > + usbh1_internal_phy_clock_gate(true);
> > + return fsl_usb_host_init(pdev);
> > +}
> > +
> > +static int fsl_usb_host_uninit_ext(struct platform_device *pdev) {
> > + usbh1_internal_phy_clock_gate(false);
> > + clk_disable(usb_phy_clk);
> > + clk_disable(usb_clk);
> > + return 0;
> > +}
> > +
> > +static struct mxc_usbh_platform_data usbh1_config = {
> > + .init = fsl_usb_host_init_ext,
> > + .exit = fsl_usb_host_uninit_ext,
> > + .portsc = MXC_EHCI_MODE_ULPI,
> > + .otg = NULL,
> > + .plt_get_usb_connect_status = fsl_platform_get_usb_conn_status,
> > + .plt_usb_disconnect_detect = fsl_platform_set_usb_phy_dis, };
> > +
> > +/* The resources for kinds of usb devices */ static struct resource
> > +usbh1_resources[] = {
> > + [0] = {
> > + .start = (u32) (MX28_USBCTRL1_BASE_ADDR),
> > + .end = (u32) (MX28_USBCTRL1_BASE_ADDR + 0x1ff),
> > + .flags = IORESOURCE_MEM,
> > + },
> > + [1] = {
> > + .start = MX28_INT_USB1,
> > + .flags = IORESOURCE_IRQ,
> > + },
> > +};
> > +
> > +static int __init usbh1_init(void)
> > +{
> > + struct platform_device *pdev;
> > + int rc;
> > +
> > + if (!cpu_is_mx28())
> > + return 0;
> > +
> > + pdev = platform_device_register_simple("mxc-ehci",
> > + instance_id, usbh1_resources, ARRAY_SIZE(usbh1_resources));
>
> What is this global instance_id you use here? This function is clearly
> called only once, yet it uses a variable which is incremented below and
> then never used again.
>
> > + if (IS_ERR(pdev)) {
> > + pr_debug("can't register Host, %ld\n",
> > + PTR_ERR(pdev));
> > + return -EFAULT;
> > + }
> > +
> > + pdev->dev.coherent_dma_mask = 0xffffffff;
> > + pdev->dev.dma_mask = &ehci_dmamask;
> > +
> > + rc = platform_device_add_data(pdev, &usbh1_config,
> > + sizeof(struct mxc_usbh_platform_data));
>
> I doubt it's allowed to add platform data after registering a device.
> The driver could already be probed after register and bailed out because
> of missing platform data.
>
>
> Overall this needs major rework.
>
> Sascha
Ok, I'll consider all of your recommendations, thanks
>
>
> --
> Pengutronix e.K. |
> |
> Industrial Linux Solutions | http://www.pengutronix.de/
> |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0
> |
> Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555
> |
More information about the linux-arm-kernel
mailing list