2 * SuperH EHCI host controller driver
4 * Copyright (C) 2010 Paul Mundt
6 * Based on ohci-sh.c and ehci-atmel.c.
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file "COPYING" in the main directory of this archive
12 #include <linux/platform_device.h>
13 #include <linux/clk.h>
16 struct clk
*iclk
, *fclk
;
20 static int ehci_sh_reset(struct usb_hcd
*hcd
)
22 struct ehci_hcd
*ehci
= hcd_to_ehci(hcd
);
25 ehci
->caps
= hcd
->regs
;
26 ehci
->regs
= hcd
->regs
+ HC_LENGTH(ehci_readl(ehci
,
27 &ehci
->caps
->hc_capbase
));
29 dbg_hcs_params(ehci
, "reset");
30 dbg_hcc_params(ehci
, "reset");
32 ehci
->hcs_params
= ehci_readl(ehci
, &ehci
->caps
->hcs_params
);
34 ret
= ehci_halt(ehci
);
45 ehci_port_power(ehci
, 0);
50 static const struct hc_driver ehci_sh_hc_driver
= {
51 .description
= hcd_name
,
52 .product_desc
= "SuperH EHCI",
53 .hcd_priv_size
= sizeof(struct ehci_hcd
),
56 * generic hardware linkage
59 .flags
= HCD_USB2
| HCD_MEMORY
,
62 * basic lifecycle operations
64 .reset
= ehci_sh_reset
,
67 .shutdown
= ehci_shutdown
,
70 * managing i/o requests and associated device resources
72 .urb_enqueue
= ehci_urb_enqueue
,
73 .urb_dequeue
= ehci_urb_dequeue
,
74 .endpoint_disable
= ehci_endpoint_disable
,
75 .endpoint_reset
= ehci_endpoint_reset
,
80 .get_frame_number
= ehci_get_frame
,
85 .hub_status_data
= ehci_hub_status_data
,
86 .hub_control
= ehci_hub_control
,
89 .bus_suspend
= ehci_bus_suspend
,
90 .bus_resume
= ehci_bus_resume
,
93 .relinquish_port
= ehci_relinquish_port
,
94 .port_handed_over
= ehci_port_handed_over
,
95 .clear_tt_buffer_complete
= ehci_clear_tt_buffer_complete
,
98 static int ehci_hcd_sh_probe(struct platform_device
*pdev
)
100 const struct hc_driver
*driver
= &ehci_sh_hc_driver
;
101 struct resource
*res
;
102 struct ehci_sh_priv
*priv
;
109 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
112 "Found HC with no register addr. Check %s setup!\n",
113 dev_name(&pdev
->dev
));
115 goto fail_create_hcd
;
118 irq
= platform_get_irq(pdev
, 0);
121 "Found HC with no IRQ. Check %s setup!\n",
122 dev_name(&pdev
->dev
));
124 goto fail_create_hcd
;
128 hcd
= usb_create_hcd(&ehci_sh_hc_driver
, &pdev
->dev
,
129 dev_name(&pdev
->dev
));
132 goto fail_create_hcd
;
135 hcd
->rsrc_start
= res
->start
;
136 hcd
->rsrc_len
= resource_size(res
);
138 if (!request_mem_region(hcd
->rsrc_start
, hcd
->rsrc_len
,
139 driver
->description
)) {
140 dev_dbg(&pdev
->dev
, "controller already in use\n");
142 goto fail_request_resource
;
145 hcd
->regs
= ioremap_nocache(hcd
->rsrc_start
, hcd
->rsrc_len
);
146 if (hcd
->regs
== NULL
) {
147 dev_dbg(&pdev
->dev
, "error mapping memory\n");
152 priv
= kmalloc(sizeof(struct ehci_sh_priv
), GFP_KERNEL
);
154 dev_dbg(&pdev
->dev
, "error allocating priv data\n");
159 /* These are optional, we don't care if they fail */
160 priv
->fclk
= clk_get(&pdev
->dev
, "usb_fck");
161 if (IS_ERR(priv
->fclk
))
164 priv
->iclk
= clk_get(&pdev
->dev
, "usb_ick");
165 if (IS_ERR(priv
->iclk
))
168 clk_enable(priv
->fclk
);
169 clk_enable(priv
->iclk
);
171 ret
= usb_add_hcd(hcd
, irq
, IRQF_DISABLED
| IRQF_SHARED
);
173 dev_err(&pdev
->dev
, "Failed to add hcd");
178 platform_set_drvdata(pdev
, priv
);
183 clk_disable(priv
->iclk
);
184 clk_disable(priv
->fclk
);
193 release_mem_region(hcd
->rsrc_start
, hcd
->rsrc_len
);
194 fail_request_resource
:
197 dev_err(&pdev
->dev
, "init %s fail, %d\n", dev_name(&pdev
->dev
), ret
);
202 static int __exit
ehci_hcd_sh_remove(struct platform_device
*pdev
)
204 struct ehci_sh_priv
*priv
= platform_get_drvdata(pdev
);
205 struct usb_hcd
*hcd
= priv
->hcd
;
209 release_mem_region(hcd
->rsrc_start
, hcd
->rsrc_len
);
211 platform_set_drvdata(pdev
, NULL
);
213 clk_disable(priv
->fclk
);
214 clk_disable(priv
->iclk
);
224 static void ehci_hcd_sh_shutdown(struct platform_device
*pdev
)
226 struct ehci_sh_priv
*priv
= platform_get_drvdata(pdev
);
227 struct usb_hcd
*hcd
= priv
->hcd
;
229 if (hcd
->driver
->shutdown
)
230 hcd
->driver
->shutdown(hcd
);
233 static struct platform_driver ehci_hcd_sh_driver
= {
234 .probe
= ehci_hcd_sh_probe
,
235 .remove
= __exit_p(ehci_hcd_sh_remove
),
236 .shutdown
= ehci_hcd_sh_shutdown
,
239 .owner
= THIS_MODULE
,
243 MODULE_ALIAS("platform:sh_ehci");