1 /*
2  *  Bus Glue for Loongson LS1X built-in EHCI controller.
3  *
4  *  Copyright (c) 2012 Zhang, Keguang <keguang.zhang@gmail.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify it
7  *  under the terms of the GNU General Public License version 2 as published
8  *  by the Free Software Foundation.
9  */
10 
11 
12 #include <linux/platform_device.h>
13 
ehci_ls1x_reset(struct usb_hcd * hcd)14 static int ehci_ls1x_reset(struct usb_hcd *hcd)
15 {
16 	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
17 	int ret;
18 
19 	ehci->caps = hcd->regs;
20 
21 	ret = ehci_setup(hcd);
22 	if (ret)
23 		return ret;
24 
25 	ehci_port_power(ehci, 0);
26 
27 	return 0;
28 }
29 
30 static const struct hc_driver ehci_ls1x_hc_driver = {
31 	.description		= hcd_name,
32 	.product_desc		= "LOONGSON1 EHCI",
33 	.hcd_priv_size		= sizeof(struct ehci_hcd),
34 
35 	/*
36 	 * generic hardware linkage
37 	 */
38 	.irq			= ehci_irq,
39 	.flags			= HCD_MEMORY | HCD_USB2,
40 
41 	/*
42 	 * basic lifecycle operations
43 	 */
44 	.reset			= ehci_ls1x_reset,
45 	.start			= ehci_run,
46 	.stop			= ehci_stop,
47 	.shutdown		= ehci_shutdown,
48 
49 	/*
50 	 * managing i/o requests and associated device resources
51 	 */
52 	.urb_enqueue		= ehci_urb_enqueue,
53 	.urb_dequeue		= ehci_urb_dequeue,
54 	.endpoint_disable	= ehci_endpoint_disable,
55 	.endpoint_reset		= ehci_endpoint_reset,
56 
57 	/*
58 	 * scheduling support
59 	 */
60 	.get_frame_number	= ehci_get_frame,
61 
62 	/*
63 	 * root hub support
64 	 */
65 	.hub_status_data	= ehci_hub_status_data,
66 	.hub_control		= ehci_hub_control,
67 	.relinquish_port	= ehci_relinquish_port,
68 	.port_handed_over	= ehci_port_handed_over,
69 
70 	.clear_tt_buffer_complete	= ehci_clear_tt_buffer_complete,
71 };
72 
ehci_hcd_ls1x_probe(struct platform_device * pdev)73 static int ehci_hcd_ls1x_probe(struct platform_device *pdev)
74 {
75 	struct usb_hcd *hcd;
76 	struct resource *res;
77 	int irq;
78 	int ret;
79 
80 	pr_debug("initializing loongson1 ehci USB Controller\n");
81 
82 	if (usb_disabled())
83 		return -ENODEV;
84 
85 	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
86 	if (!res) {
87 		dev_err(&pdev->dev,
88 			"Found HC with no IRQ. Check %s setup!\n",
89 			dev_name(&pdev->dev));
90 		return -ENODEV;
91 	}
92 	irq = res->start;
93 
94 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
95 	if (!res) {
96 		dev_err(&pdev->dev,
97 			"Found HC with no register addr. Check %s setup!\n",
98 			dev_name(&pdev->dev));
99 		return -ENODEV;
100 	}
101 
102 	hcd = usb_create_hcd(&ehci_ls1x_hc_driver, &pdev->dev,
103 				dev_name(&pdev->dev));
104 	if (!hcd)
105 		return -ENOMEM;
106 	hcd->rsrc_start	= res->start;
107 	hcd->rsrc_len	= resource_size(res);
108 
109 	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
110 		dev_dbg(&pdev->dev, "controller already in use\n");
111 		ret = -EBUSY;
112 		goto err_put_hcd;
113 	}
114 
115 	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
116 	if (hcd->regs == NULL) {
117 		dev_dbg(&pdev->dev, "error mapping memory\n");
118 		ret = -EFAULT;
119 		goto err_release_region;
120 	}
121 
122 	ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
123 	if (ret)
124 		goto err_iounmap;
125 
126 	return ret;
127 
128 err_iounmap:
129 	iounmap(hcd->regs);
130 err_release_region:
131 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
132 err_put_hcd:
133 	usb_put_hcd(hcd);
134 	return ret;
135 }
136 
ehci_hcd_ls1x_remove(struct platform_device * pdev)137 static int ehci_hcd_ls1x_remove(struct platform_device *pdev)
138 {
139 	struct usb_hcd *hcd = platform_get_drvdata(pdev);
140 
141 	usb_remove_hcd(hcd);
142 	iounmap(hcd->regs);
143 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
144 	usb_put_hcd(hcd);
145 
146 	return 0;
147 }
148 
149 static struct platform_driver ehci_ls1x_driver = {
150 	.probe = ehci_hcd_ls1x_probe,
151 	.remove = ehci_hcd_ls1x_remove,
152 	.shutdown = usb_hcd_platform_shutdown,
153 	.driver = {
154 		.name = "ls1x-ehci",
155 		.owner	= THIS_MODULE,
156 	},
157 };
158 
159 MODULE_ALIAS(PLATFORM_MODULE_PREFIX "ls1x-ehci");
160