Commit 230f7ede6c2f0e403f29e03e0251a470aa9350dd

Authored by Anatolij Gustschin
Committed by Greg Kroah-Hartman
1 parent 126512e3f2

USB: add USB EHCI support for MPC5121 SoC

Extends FSL EHCI platform driver glue layer to support
MPC5121 USB controllers. MPC5121 Rev 2.0 silicon EHCI
registers are in big endian format. The appropriate flags
are set using the information in the platform data structure.
MPC83xx system interface registers are not available on
MPC512x, so the access to these registers is isolated in
MPC512x case. Furthermore the USB controller clocks
must be enabled before 512x register accesses which is
done by providing platform specific init callback.

The MPC512x internal USB PHY doesn't provide supply voltage.
For boards using different power switches allow specifying
DRVVBUS and PWR_FAULT signal polarity of the MPC5121 internal
PHY using "fsl,invert-drvvbus" and "fsl,invert-pwr-fault"
properties in the device tree USB nodes. Adds documentation
for this new device tree bindings.

Signed-off-by: Anatolij Gustschin <agust@denx.de>
Cc: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Showing 8 changed files with 215 additions and 32 deletions Side-by-side Diff

Documentation/powerpc/dts-bindings/fsl/usb.txt
... ... @@ -8,6 +8,7 @@
8 8 Required properties :
9 9 - compatible : Should be "fsl-usb2-mph" for multi port host USB
10 10 controllers, or "fsl-usb2-dr" for dual role USB controllers
  11 + or "fsl,mpc5121-usb2-dr" for dual role USB controllers of MPC5121
11 12 - phy_type : For multi port host USB controllers, should be one of
12 13 "ulpi", or "serial". For dual role USB controllers, should be
13 14 one of "ulpi", "utmi", "utmi_wide", or "serial".
... ... @@ -33,6 +34,12 @@
33 34 - interrupt-parent : the phandle for the interrupt controller that
34 35 services interrupts for this device.
35 36  
  37 +Optional properties :
  38 + - fsl,invert-drvvbus : boolean; for MPC5121 USB0 only. Indicates the
  39 + port power polarity of internal PHY signal DRVVBUS is inverted.
  40 + - fsl,invert-pwr-fault : boolean; for MPC5121 USB0 only. Indicates
  41 + the PWR_FAULT signal polarity is inverted.
  42 +
36 43 Example multi port host USB controller device node :
37 44 usb@22000 {
38 45 compatible = "fsl-usb2-mph";
... ... @@ -56,5 +63,20 @@
56 63 interrupts = <26 1>;
57 64 dr_mode = "otg";
58 65 phy = "ulpi";
  66 + };
  67 +
  68 +Example dual role USB controller device node for MPC5121ADS:
  69 +
  70 + usb@4000 {
  71 + compatible = "fsl,mpc5121-usb2-dr";
  72 + reg = <0x4000 0x1000>;
  73 + #address-cells = <1>;
  74 + #size-cells = <0>;
  75 + interrupt-parent = < &ipic >;
  76 + interrupts = <44 0x8>;
  77 + dr_mode = "otg";
  78 + phy_type = "utmi_wide";
  79 + fsl,invert-drvvbus;
  80 + fsl,invert-pwr-fault;
59 81 };
... ... @@ -59,6 +59,7 @@
59 59 config USB_ARCH_HAS_EHCI
60 60 boolean
61 61 default y if PPC_83xx
  62 + default y if PPC_MPC512x
62 63 default y if SOC_AU1200
63 64 default y if ARCH_IXP4XX
64 65 default y if ARCH_W90X900
drivers/usb/host/Kconfig
... ... @@ -93,12 +93,14 @@
93 93  
94 94 config USB_EHCI_BIG_ENDIAN_MMIO
95 95 bool
96   - depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX)
  96 + depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || \
  97 + XPS_USB_HCD_XILINX || PPC_MPC512x)
97 98 default y
98 99  
99 100 config USB_EHCI_BIG_ENDIAN_DESC
100 101 bool
101   - depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX)
  102 + depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \
  103 + PPC_MPC512x)
102 104 default y
103 105  
104 106 config XPS_USB_HCD_XILINX
drivers/usb/host/ehci-fsl.c
... ... @@ -116,14 +116,34 @@
116 116 goto err3;
117 117 }
118 118  
119   - /* Enable USB controller */
120   - temp = in_be32(hcd->regs + 0x500);
121   - out_be32(hcd->regs + 0x500, temp | 0x4);
  119 + pdata->regs = hcd->regs;
122 120  
123   - /* Set to Host mode */
124   - temp = in_le32(hcd->regs + 0x1a8);
125   - out_le32(hcd->regs + 0x1a8, temp | 0x3);
  121 + /*
  122 + * do platform specific init: check the clock, grab/config pins, etc.
  123 + */
  124 + if (pdata->init && pdata->init(pdev)) {
  125 + retval = -ENODEV;
  126 + goto err3;
  127 + }
126 128  
  129 + /*
  130 + * Check if it is MPC5121 SoC, otherwise set pdata->have_sysif_regs
  131 + * flag for 83xx or 8536 system interface registers.
  132 + */
  133 + if (pdata->big_endian_mmio)
  134 + temp = in_be32(hcd->regs + FSL_SOC_USB_ID);
  135 + else
  136 + temp = in_le32(hcd->regs + FSL_SOC_USB_ID);
  137 +
  138 + if ((temp & ID_MSK) != (~((temp & NID_MSK) >> 8) & ID_MSK))
  139 + pdata->have_sysif_regs = 1;
  140 +
  141 + /* Enable USB controller, 83xx or 8536 */
  142 + if (pdata->have_sysif_regs)
  143 + setbits32(hcd->regs + FSL_SOC_USB_CTRL, 0x4);
  144 +
  145 + /* Don't need to set host mode here. It will be done by tdi_reset() */
  146 +
127 147 retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
128 148 if (retval != 0)
129 149 goto err4;
... ... @@ -137,6 +157,8 @@
137 157 usb_put_hcd(hcd);
138 158 err1:
139 159 dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval);
  160 + if (pdata->exit)
  161 + pdata->exit(pdev);
140 162 return retval;
141 163 }
142 164  
143 165  
144 166  
145 167  
... ... @@ -154,17 +176,30 @@
154 176 static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
155 177 struct platform_device *pdev)
156 178 {
  179 + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
  180 +
157 181 usb_remove_hcd(hcd);
  182 +
  183 + /*
  184 + * do platform specific un-initialization:
  185 + * release iomux pins, disable clock, etc.
  186 + */
  187 + if (pdata->exit)
  188 + pdata->exit(pdev);
158 189 iounmap(hcd->regs);
159 190 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
160 191 usb_put_hcd(hcd);
161 192 }
162 193  
163   -static void mpc83xx_setup_phy(struct ehci_hcd *ehci,
164   - enum fsl_usb2_phy_modes phy_mode,
165   - unsigned int port_offset)
  194 +static void ehci_fsl_setup_phy(struct ehci_hcd *ehci,
  195 + enum fsl_usb2_phy_modes phy_mode,
  196 + unsigned int port_offset)
166 197 {
167   - u32 portsc = 0;
  198 + u32 portsc;
  199 +
  200 + portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]);
  201 + portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW);
  202 +
168 203 switch (phy_mode) {
169 204 case FSL_USB2_PHY_ULPI:
170 205 portsc |= PORT_PTS_ULPI;
171 206  
172 207  
173 208  
... ... @@ -184,20 +219,21 @@
184 219 ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]);
185 220 }
186 221  
187   -static void mpc83xx_usb_setup(struct usb_hcd *hcd)
  222 +static void ehci_fsl_usb_setup(struct ehci_hcd *ehci)
188 223 {
189   - struct ehci_hcd *ehci = hcd_to_ehci(hcd);
  224 + struct usb_hcd *hcd = ehci_to_hcd(ehci);
190 225 struct fsl_usb2_platform_data *pdata;
191 226 void __iomem *non_ehci = hcd->regs;
192 227 u32 temp;
193 228  
194   - pdata =
195   - (struct fsl_usb2_platform_data *)hcd->self.controller->
196   - platform_data;
  229 + pdata = hcd->self.controller->platform_data;
  230 +
197 231 /* Enable PHY interface in the control reg. */
198   - temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
199   - out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004);
200   - out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
  232 + if (pdata->have_sysif_regs) {
  233 + temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
  234 + out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004);
  235 + out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
  236 + }
201 237  
202 238 #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
203 239 /*
... ... @@ -214,7 +250,7 @@
214 250  
215 251 if ((pdata->operating_mode == FSL_USB2_DR_HOST) ||
216 252 (pdata->operating_mode == FSL_USB2_DR_OTG))
217   - mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
  253 + ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0);
218 254  
219 255 if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
220 256 unsigned int chip, rev, svr;
221 257  
222 258  
223 259  
224 260  
225 261  
226 262  
... ... @@ -228,25 +264,27 @@
228 264 ehci->has_fsl_port_bug = 1;
229 265  
230 266 if (pdata->port_enables & FSL_USB2_PORT0_ENABLED)
231   - mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
  267 + ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0);
232 268 if (pdata->port_enables & FSL_USB2_PORT1_ENABLED)
233   - mpc83xx_setup_phy(ehci, pdata->phy_mode, 1);
  269 + ehci_fsl_setup_phy(ehci, pdata->phy_mode, 1);
234 270 }
235 271  
  272 + if (pdata->have_sysif_regs) {
236 273 #ifdef CONFIG_PPC_85xx
237   - out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
238   - out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
  274 + out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
  275 + out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
239 276 #else
240   - out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
241   - out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
  277 + out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
  278 + out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
242 279 #endif
243   - out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
  280 + out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
  281 + }
244 282 }
245 283  
246 284 /* called after powerup, by probe or system-pm "wakeup" */
247 285 static int ehci_fsl_reinit(struct ehci_hcd *ehci)
248 286 {
249   - mpc83xx_usb_setup(ehci_to_hcd(ehci));
  287 + ehci_fsl_usb_setup(ehci);
250 288 ehci_port_power(ehci, 0);
251 289  
252 290 return 0;
253 291  
... ... @@ -257,7 +295,12 @@
257 295 {
258 296 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
259 297 int retval;
  298 + struct fsl_usb2_platform_data *pdata;
260 299  
  300 + pdata = hcd->self.controller->platform_data;
  301 + ehci->big_endian_desc = pdata->big_endian_desc;
  302 + ehci->big_endian_mmio = pdata->big_endian_mmio;
  303 +
261 304 /* EHCI registers start at offset 0x100 */
262 305 ehci->caps = hcd->regs + 0x100;
263 306 ehci->regs = hcd->regs + 0x100 +
... ... @@ -370,7 +413,7 @@
370 413 * generic hardware linkage
371 414 */
372 415 .irq = ehci_irq,
373   - .flags = HCD_USB2,
  416 + .flags = HCD_USB2 | HCD_MEMORY,
374 417  
375 418 /*
376 419 * basic lifecycle operations
drivers/usb/host/ehci-fsl.h
1   -/* Copyright (c) 2005 freescale semiconductor
  1 +/* Copyright (C) 2005-2010 Freescale Semiconductor, Inc.
2 2 * Copyright (c) 2005 MontaVista Software
3 3 *
4 4 * This program is free software; you can redistribute it and/or modify it
... ... @@ -19,6 +19,9 @@
19 19 #define _EHCI_FSL_H
20 20  
21 21 /* offsets for the non-ehci registers in the FSL SOC USB controller */
  22 +#define FSL_SOC_USB_ID 0x0
  23 +#define ID_MSK 0x3f
  24 +#define NID_MSK 0x3f00
22 25 #define FSL_SOC_USB_ULPIVP 0x170
23 26 #define FSL_SOC_USB_PORTSC1 0x184
24 27 #define PORT_PTS_MSK (3<<30)
... ... @@ -27,6 +30,14 @@
27 30 #define PORT_PTS_SERIAL (3<<30)
28 31 #define PORT_PTS_PTW (1<<28)
29 32 #define FSL_SOC_USB_PORTSC2 0x188
  33 +
  34 +#define FSL_SOC_USB_USBGENCTRL 0x200
  35 +#define USBGENCTRL_PPP (1 << 3)
  36 +#define USBGENCTRL_PFP (1 << 2)
  37 +#define FSL_SOC_USB_ISIPHYCTRL 0x204
  38 +#define ISIPHYCTRL_PXE (1)
  39 +#define ISIPHYCTRL_PHYE (1 << 4)
  40 +
30 41 #define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */
31 42 #define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */
32 43 #define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */
drivers/usb/host/ehci-mem.c
... ... @@ -40,7 +40,7 @@
40 40 {
41 41 memset (qtd, 0, sizeof *qtd);
42 42 qtd->qtd_dma = dma;
43   - qtd->hw_token = cpu_to_le32 (QTD_STS_HALT);
  43 + qtd->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
44 44 qtd->hw_next = EHCI_LIST_END(ehci);
45 45 qtd->hw_alt_next = EHCI_LIST_END(ehci);
46 46 INIT_LIST_HEAD (&qtd->qtd_list);
drivers/usb/host/fsl-mph-dr-of.c
... ... @@ -15,6 +15,7 @@
15 15 #include <linux/err.h>
16 16 #include <linux/io.h>
17 17 #include <linux/of_platform.h>
  18 +#include <linux/clk.h>
18 19  
19 20 struct fsl_usb2_dev_data {
20 21 char *dr_mode; /* controller mode */
... ... @@ -153,6 +154,12 @@
153 154  
154 155 pdata->operating_mode = FSL_USB2_MPH_HOST;
155 156 } else {
  157 + if (of_get_property(np, "fsl,invert-drvvbus", NULL))
  158 + pdata->invert_drvvbus = 1;
  159 +
  160 + if (of_get_property(np, "fsl,invert-pwr-fault", NULL))
  161 + pdata->invert_pwr_fault = 1;
  162 +
156 163 /* setup mode selected in the device tree */
157 164 pdata->operating_mode = dev_data->op_mode;
158 165 }
159 166  
... ... @@ -186,9 +193,91 @@
186 193 return 0;
187 194 }
188 195  
  196 +#ifdef CONFIG_PPC_MPC512x
  197 +
  198 +#define USBGENCTRL 0x200 /* NOTE: big endian */
  199 +#define GC_WU_INT_CLR (1 << 5) /* Wakeup int clear */
  200 +#define GC_ULPI_SEL (1 << 4) /* ULPI i/f select (usb0 only)*/
  201 +#define GC_PPP (1 << 3) /* Inv. Port Power Polarity */
  202 +#define GC_PFP (1 << 2) /* Inv. Power Fault Polarity */
  203 +#define GC_WU_ULPI_EN (1 << 1) /* Wakeup on ULPI event */
  204 +#define GC_WU_IE (1 << 1) /* Wakeup interrupt enable */
  205 +
  206 +#define ISIPHYCTRL 0x204 /* NOTE: big endian */
  207 +#define PHYCTRL_PHYE (1 << 4) /* On-chip UTMI PHY enable */
  208 +#define PHYCTRL_BSENH (1 << 3) /* Bit Stuff Enable High */
  209 +#define PHYCTRL_BSEN (1 << 2) /* Bit Stuff Enable */
  210 +#define PHYCTRL_LSFE (1 << 1) /* Line State Filter Enable */
  211 +#define PHYCTRL_PXE (1 << 0) /* PHY oscillator enable */
  212 +
  213 +int fsl_usb2_mpc5121_init(struct platform_device *pdev)
  214 +{
  215 + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
  216 + struct clk *clk;
  217 + char clk_name[10];
  218 + int base, clk_num;
  219 +
  220 + base = pdev->resource->start & 0xf000;
  221 + if (base == 0x3000)
  222 + clk_num = 1;
  223 + else if (base == 0x4000)
  224 + clk_num = 2;
  225 + else
  226 + return -ENODEV;
  227 +
  228 + snprintf(clk_name, sizeof(clk_name), "usb%d_clk", clk_num);
  229 + clk = clk_get(&pdev->dev, clk_name);
  230 + if (IS_ERR(clk)) {
  231 + dev_err(&pdev->dev, "failed to get clk\n");
  232 + return PTR_ERR(clk);
  233 + }
  234 +
  235 + clk_enable(clk);
  236 + pdata->clk = clk;
  237 +
  238 + if (pdata->phy_mode == FSL_USB2_PHY_UTMI_WIDE) {
  239 + u32 reg = 0;
  240 +
  241 + if (pdata->invert_drvvbus)
  242 + reg |= GC_PPP;
  243 +
  244 + if (pdata->invert_pwr_fault)
  245 + reg |= GC_PFP;
  246 +
  247 + out_be32(pdata->regs + ISIPHYCTRL, PHYCTRL_PHYE | PHYCTRL_PXE);
  248 + out_be32(pdata->regs + USBGENCTRL, reg);
  249 + }
  250 + return 0;
  251 +}
  252 +
  253 +static void fsl_usb2_mpc5121_exit(struct platform_device *pdev)
  254 +{
  255 + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
  256 +
  257 + pdata->regs = NULL;
  258 +
  259 + if (pdata->clk) {
  260 + clk_disable(pdata->clk);
  261 + clk_put(pdata->clk);
  262 + }
  263 +}
  264 +
  265 +struct fsl_usb2_platform_data fsl_usb2_mpc5121_pd = {
  266 + .big_endian_desc = 1,
  267 + .big_endian_mmio = 1,
  268 + .es = 1,
  269 + .le_setup_buf = 1,
  270 + .init = fsl_usb2_mpc5121_init,
  271 + .exit = fsl_usb2_mpc5121_exit,
  272 +};
  273 +#endif /* CONFIG_PPC_MPC512x */
  274 +
189 275 static const struct of_device_id fsl_usb2_mph_dr_of_match[] = {
190 276 { .compatible = "fsl-usb2-mph", },
191 277 { .compatible = "fsl-usb2-dr", },
  278 +#ifdef CONFIG_PPC_MPC512x
  279 + { .compatible = "fsl,mpc5121-usb2-dr", .data = &fsl_usb2_mpc5121_pd, },
  280 +#endif
192 281 {},
193 282 };
194 283  
include/linux/fsl_devices.h
... ... @@ -58,11 +58,26 @@
58 58 FSL_USB2_PHY_SERIAL,
59 59 };
60 60  
  61 +struct clk;
  62 +struct platform_device;
  63 +
61 64 struct fsl_usb2_platform_data {
62 65 /* board specific information */
63 66 enum fsl_usb2_operating_modes operating_mode;
64 67 enum fsl_usb2_phy_modes phy_mode;
65 68 unsigned int port_enables;
  69 +
  70 + int (*init)(struct platform_device *);
  71 + void (*exit)(struct platform_device *);
  72 + void __iomem *regs; /* ioremap'd register base */
  73 + struct clk *clk;
  74 + unsigned big_endian_mmio:1;
  75 + unsigned big_endian_desc:1;
  76 + unsigned es:1; /* need USBMODE:ES */
  77 + unsigned le_setup_buf:1;
  78 + unsigned have_sysif_regs:1;
  79 + unsigned invert_drvvbus:1;
  80 + unsigned invert_pwr_fault:1;
66 81 };
67 82  
68 83 /* Flags in fsl_usb2_mph_platform_data */