Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  1/*
  2 * EHCI HCD (Host Controller Driver) for USB.
  3 *
  4 * Bus Glue for AMD Alchemy Au1xxx
  5 *
  6 * Based on "ohci-au1xxx.c" by Matt Porter <mporter@kernel.crashing.org>
  7 *
  8 * Modified for AMD Alchemy Au1200 EHC
  9 *  by K.Boge <karsten.boge@amd.com>
 10 *
 11 * This file is licenced under the GPL.
 12 */
 13
 14#include <linux/platform_device.h>
 15#include <asm/mach-au1x00/au1000.h>
 16
 17#define USB_HOST_CONFIG   (USB_MSR_BASE + USB_MSR_MCFG)
 18#define USB_MCFG_PFEN     (1<<31)
 19#define USB_MCFG_RDCOMB   (1<<30)
 20#define USB_MCFG_SSDEN    (1<<23)
 21#define USB_MCFG_PHYPLLEN (1<<19)
 22#define USB_MCFG_UCECLKEN (1<<18)
 23#define USB_MCFG_EHCCLKEN (1<<17)
 24#ifdef CONFIG_DMA_COHERENT
 25#define USB_MCFG_UCAM     (1<<7)
 26#else
 27#define USB_MCFG_UCAM     (0)
 28#endif
 29#define USB_MCFG_EBMEN    (1<<3)
 30#define USB_MCFG_EMEMEN   (1<<2)
 31
 32#define USBH_ENABLE_CE	(USB_MCFG_PHYPLLEN | USB_MCFG_EHCCLKEN)
 33#define USBH_ENABLE_INIT (USB_MCFG_PFEN  | USB_MCFG_RDCOMB |	\
 34			  USBH_ENABLE_CE | USB_MCFG_SSDEN  |	\
 35			  USB_MCFG_UCAM  | USB_MCFG_EBMEN  |	\
 36			  USB_MCFG_EMEMEN)
 37
 38#define USBH_DISABLE      (USB_MCFG_EBMEN | USB_MCFG_EMEMEN)
 39
 40extern int usb_disabled(void);
 41
 42static void au1xxx_start_ehc(void)
 43{
 44	/* enable clock to EHCI block and HS PHY PLL*/
 45	au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_CE, USB_HOST_CONFIG);
 46	au_sync();
 47	udelay(1000);
 48
 49	/* enable EHCI mmio */
 50	au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG);
 51	au_sync();
 52	udelay(1000);
 53}
 54
 55static void au1xxx_stop_ehc(void)
 56{
 57	unsigned long c;
 58
 59	/* Disable mem */
 60	au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_DISABLE, USB_HOST_CONFIG);
 61	au_sync();
 62	udelay(1000);
 63
 64	/* Disable EHC clock. If the HS PHY is unused disable it too. */
 65	c = au_readl(USB_HOST_CONFIG) & ~USB_MCFG_EHCCLKEN;
 66	if (!(c & USB_MCFG_UCECLKEN))		/* UDC disabled? */
 67		c &= ~USB_MCFG_PHYPLLEN;	/* yes: disable HS PHY PLL */
 68	au_writel(c, USB_HOST_CONFIG);
 69	au_sync();
 70}
 71
 72static int au1xxx_ehci_setup(struct usb_hcd *hcd)
 73{
 74	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 75	int ret = ehci_init(hcd);
 76
 77	ehci->need_io_watchdog = 0;
 78	return ret;
 79}
 80
 81static const struct hc_driver ehci_au1xxx_hc_driver = {
 82	.description		= hcd_name,
 83	.product_desc		= "Au1xxx EHCI",
 84	.hcd_priv_size		= sizeof(struct ehci_hcd),
 85
 86	/*
 87	 * generic hardware linkage
 88	 */
 89	.irq			= ehci_irq,
 90	.flags			= HCD_MEMORY | HCD_USB2,
 91
 92	/*
 93	 * basic lifecycle operations
 94	 *
 95	 * FIXME -- ehci_init() doesn't do enough here.
 96	 * See ehci-ppc-soc for a complete implementation.
 97	 */
 98	.reset			= au1xxx_ehci_setup,
 99	.start			= ehci_run,
100	.stop			= ehci_stop,
101	.shutdown		= ehci_shutdown,
102
103	/*
104	 * managing i/o requests and associated device resources
105	 */
106	.urb_enqueue		= ehci_urb_enqueue,
107	.urb_dequeue		= ehci_urb_dequeue,
108	.endpoint_disable	= ehci_endpoint_disable,
109	.endpoint_reset		= ehci_endpoint_reset,
110
111	/*
112	 * scheduling support
113	 */
114	.get_frame_number	= ehci_get_frame,
115
116	/*
117	 * root hub support
118	 */
119	.hub_status_data	= ehci_hub_status_data,
120	.hub_control		= ehci_hub_control,
121	.bus_suspend		= ehci_bus_suspend,
122	.bus_resume		= ehci_bus_resume,
123	.relinquish_port	= ehci_relinquish_port,
124	.port_handed_over	= ehci_port_handed_over,
125
126	.clear_tt_buffer_complete	= ehci_clear_tt_buffer_complete,
127};
128
129static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
130{
131	struct usb_hcd *hcd;
132	struct ehci_hcd *ehci;
133	struct resource *res;
134	int ret;
135
136	if (usb_disabled())
137		return -ENODEV;
138
139#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
140	/* Au1200 AB USB does not support coherent memory */
141	if (!(read_c0_prid() & 0xff)) {
142		printk(KERN_INFO "%s: this is chip revision AB!\n", pdev->name);
143		printk(KERN_INFO "%s: update your board or re-configure"
144				 " the kernel\n", pdev->name);
145		return -ENODEV;
146	}
147#endif
148
149	if (pdev->resource[1].flags != IORESOURCE_IRQ) {
150		pr_debug("resource[1] is not IORESOURCE_IRQ");
151		return -ENOMEM;
152	}
153	hcd = usb_create_hcd(&ehci_au1xxx_hc_driver, &pdev->dev, "Au1xxx");
154	if (!hcd)
155		return -ENOMEM;
156
157	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
158	hcd->rsrc_start = res->start;
159	hcd->rsrc_len = resource_size(res);
160
161	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
162		pr_debug("request_mem_region failed");
163		ret = -EBUSY;
164		goto err1;
165	}
166
167	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
168	if (!hcd->regs) {
169		pr_debug("ioremap failed");
170		ret = -ENOMEM;
171		goto err2;
172	}
173
174	au1xxx_start_ehc();
175
176	ehci = hcd_to_ehci(hcd);
177	ehci->caps = hcd->regs;
178	ehci->regs = hcd->regs +
179		HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase));
180	/* cache this readonly data; minimize chip reads */
181	ehci->hcs_params = readl(&ehci->caps->hcs_params);
182
183	ret = usb_add_hcd(hcd, pdev->resource[1].start,
184			  IRQF_DISABLED | IRQF_SHARED);
185	if (ret == 0) {
186		platform_set_drvdata(pdev, hcd);
187		return ret;
188	}
189
190	au1xxx_stop_ehc();
191	iounmap(hcd->regs);
192err2:
193	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
194err1:
195	usb_put_hcd(hcd);
196	return ret;
197}
198
199static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
200{
201	struct usb_hcd *hcd = platform_get_drvdata(pdev);
202
203	usb_remove_hcd(hcd);
204	iounmap(hcd->regs);
205	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
206	usb_put_hcd(hcd);
207	au1xxx_stop_ehc();
208	platform_set_drvdata(pdev, NULL);
209
210	return 0;
211}
212
213#ifdef CONFIG_PM
214static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
215{
216	struct usb_hcd *hcd = dev_get_drvdata(dev);
217	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
218	unsigned long flags;
219	int rc = 0;
220
221	if (time_before(jiffies, ehci->next_statechange))
222		msleep(10);
223
224	/* Root hub was already suspended. Disable irq emission and
225	 * mark HW unaccessible.  The PM and USB cores make sure that
226	 * the root hub is either suspended or stopped.
227	 */
228	ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
229	spin_lock_irqsave(&ehci->lock, flags);
230	ehci_writel(ehci, 0, &ehci->regs->intr_enable);
231	(void)ehci_readl(ehci, &ehci->regs->intr_enable);
232
233	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
234	spin_unlock_irqrestore(&ehci->lock, flags);
235
236	// could save FLADJ in case of Vaux power loss
237	// ... we'd only use it to handle clock skew
238
239	au1xxx_stop_ehc();
240
241	return rc;
242}
243
244static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
245{
246	struct usb_hcd *hcd = dev_get_drvdata(dev);
247	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
248
249	au1xxx_start_ehc();
250
251	// maybe restore FLADJ
252
253	if (time_before(jiffies, ehci->next_statechange))
254		msleep(100);
255
256	/* Mark hardware accessible again as we are out of D3 state by now */
257	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
258
259	/* If CF is still set, we maintained PCI Vaux power.
260	 * Just undo the effect of ehci_pci_suspend().
261	 */
262	if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
263		int	mask = INTR_MASK;
264
265		ehci_prepare_ports_for_controller_resume(ehci);
266		if (!hcd->self.root_hub->do_remote_wakeup)
267			mask &= ~STS_PCD;
268		ehci_writel(ehci, mask, &ehci->regs->intr_enable);
269		ehci_readl(ehci, &ehci->regs->intr_enable);
270		return 0;
271	}
272
273	ehci_dbg(ehci, "lost power, restarting\n");
274	usb_root_hub_lost_power(hcd->self.root_hub);
275
276	/* Else reset, to cope with power loss or flush-to-storage
277	 * style "resume" having let BIOS kick in during reboot.
278	 */
279	(void) ehci_halt(ehci);
280	(void) ehci_reset(ehci);
281
282	/* emptying the schedule aborts any urbs */
283	spin_lock_irq(&ehci->lock);
284	if (ehci->reclaim)
285		end_unlink_async(ehci);
286	ehci_work(ehci);
287	spin_unlock_irq(&ehci->lock);
288
289	ehci_writel(ehci, ehci->command, &ehci->regs->command);
290	ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
291	ehci_readl(ehci, &ehci->regs->command);	/* unblock posted writes */
292
293	/* here we "know" root ports should always stay powered */
294	ehci_port_power(ehci, 1);
295
296	hcd->state = HC_STATE_SUSPENDED;
297
298	return 0;
299}
300
301static const struct dev_pm_ops au1xxx_ehci_pmops = {
302	.suspend	= ehci_hcd_au1xxx_drv_suspend,
303	.resume		= ehci_hcd_au1xxx_drv_resume,
304};
305
306#define AU1XXX_EHCI_PMOPS &au1xxx_ehci_pmops
307
308#else
309#define AU1XXX_EHCI_PMOPS NULL
310#endif
311
312static struct platform_driver ehci_hcd_au1xxx_driver = {
313	.probe		= ehci_hcd_au1xxx_drv_probe,
314	.remove		= ehci_hcd_au1xxx_drv_remove,
315	.shutdown	= usb_hcd_platform_shutdown,
316	.driver = {
317		.name	= "au1xxx-ehci",
318		.owner	= THIS_MODULE,
319		.pm	= AU1XXX_EHCI_PMOPS,
320	}
321};
322
323MODULE_ALIAS("platform:au1xxx-ehci");