xref: /linux/drivers/usb/host/uhci-platform.c (revision f5e9d31e79c1ce8ba948ecac74d75e9c8d2f0c87)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Generic UHCI HCD (Host Controller Driver) for Platform Devices
4  *
5  * Copyright (c) 2011 Tony Prisk <linux@prisktech.co.nz>
6  *
7  * This file is based on uhci-grlib.c
8  * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
9  */
10 
11 #include <linux/of.h>
12 #include <linux/device.h>
13 #include <linux/platform_device.h>
14 #include <linux/reset.h>
15 
uhci_platform_init(struct usb_hcd * hcd)16 static int uhci_platform_init(struct usb_hcd *hcd)
17 {
18 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
19 
20 	/* Probe number of ports if not already provided by DT */
21 	if (!uhci->rh_numports)
22 		uhci->rh_numports = uhci_count_ports(hcd);
23 
24 	/* Set up pointers to to generic functions */
25 	uhci->reset_hc = uhci_generic_reset_hc;
26 	uhci->check_and_reset_hc = uhci_generic_check_and_reset_hc;
27 
28 	/* No special actions need to be taken for the functions below */
29 	uhci->configure_hc = NULL;
30 	uhci->resume_detect_interrupts_are_broken = NULL;
31 	uhci->global_suspend_mode_is_broken = NULL;
32 
33 	/* Reset if the controller isn't already safely quiescent. */
34 	check_and_reset_hc(uhci);
35 	return 0;
36 }
37 
38 static const struct hc_driver uhci_platform_hc_driver = {
39 	.description =		hcd_name,
40 	.product_desc =		"Generic UHCI Host Controller",
41 	.hcd_priv_size =	sizeof(struct uhci_hcd),
42 
43 	/* Generic hardware linkage */
44 	.irq =			uhci_irq,
45 	.flags =		HCD_MEMORY | HCD_DMA | HCD_USB11,
46 
47 	/* Basic lifecycle operations */
48 	.reset =		uhci_platform_init,
49 	.start =		uhci_start,
50 #ifdef CONFIG_PM
51 	.pci_suspend =		NULL,
52 	.pci_resume =		NULL,
53 	.bus_suspend =		uhci_rh_suspend,
54 	.bus_resume =		uhci_rh_resume,
55 #endif
56 	.stop =			uhci_stop,
57 
58 	.urb_enqueue =		uhci_urb_enqueue,
59 	.urb_dequeue =		uhci_urb_dequeue,
60 
61 	.endpoint_disable =	uhci_hcd_endpoint_disable,
62 	.get_frame_number =	uhci_hcd_get_frame_number,
63 
64 	.hub_status_data =	uhci_hub_status_data,
65 	.hub_control =		uhci_hub_control,
66 };
67 
uhci_hcd_platform_probe(struct platform_device * pdev)68 static int uhci_hcd_platform_probe(struct platform_device *pdev)
69 {
70 	struct device_node *np = pdev->dev.of_node;
71 	bool dma_mask_64 = false;
72 	struct usb_hcd *hcd;
73 	struct uhci_hcd	*uhci;
74 	struct resource *res;
75 	int ret;
76 
77 	if (usb_disabled())
78 		return -ENODEV;
79 
80 	/*
81 	 * Right now device-tree probed devices don't get dma_mask set.
82 	 * Since shared usb code relies on it, set it here for now.
83 	 * Once we have dma capability bindings this can go away.
84 	 */
85 	if (of_device_get_match_data(&pdev->dev))
86 		dma_mask_64 = true;
87 
88 	ret = dma_coerce_mask_and_coherent(&pdev->dev,
89 		dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
90 	if (ret)
91 		return ret;
92 
93 	hcd = usb_create_hcd(&uhci_platform_hc_driver, &pdev->dev,
94 			pdev->name);
95 	if (!hcd)
96 		return -ENOMEM;
97 
98 	uhci = hcd_to_uhci(hcd);
99 
100 	hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
101 	if (IS_ERR(hcd->regs)) {
102 		ret = PTR_ERR(hcd->regs);
103 		goto err_rmr;
104 	}
105 	hcd->rsrc_start = res->start;
106 	hcd->rsrc_len = resource_size(res);
107 
108 	uhci->regs = hcd->regs;
109 
110 	/* Grab some things from the device-tree */
111 	if (np) {
112 		u32 num_ports;
113 
114 		if (of_property_read_u32(np, "#ports", &num_ports) == 0) {
115 			uhci->rh_numports = num_ports;
116 			dev_info(&pdev->dev,
117 				"Detected %d ports from device-tree\n",
118 				num_ports);
119 		}
120 		if (of_device_is_compatible(np, "aspeed,ast2400-uhci") ||
121 		    of_device_is_compatible(np, "aspeed,ast2500-uhci") ||
122 		    of_device_is_compatible(np, "aspeed,ast2600-uhci") ||
123 		    of_device_is_compatible(np, "aspeed,ast2700-uhci")) {
124 			uhci->is_aspeed = 1;
125 			dev_info(&pdev->dev,
126 				 "Enabled Aspeed implementation workarounds\n");
127 		}
128 	}
129 
130 	/* Get and enable clock if any specified */
131 	uhci->clk = devm_clk_get_optional(&pdev->dev, NULL);
132 	if (IS_ERR(uhci->clk)) {
133 		ret = PTR_ERR(uhci->clk);
134 		goto err_rmr;
135 	}
136 	ret = clk_prepare_enable(uhci->clk);
137 	if (ret) {
138 		dev_err(&pdev->dev, "Error couldn't enable clock (%d)\n", ret);
139 		goto err_rmr;
140 	}
141 
142 	uhci->rsts = devm_reset_control_array_get_optional_shared(&pdev->dev);
143 	if (IS_ERR(uhci->rsts)) {
144 		ret = PTR_ERR(uhci->rsts);
145 		goto err_clk;
146 	}
147 	ret = reset_control_deassert(uhci->rsts);
148 	if (ret)
149 		goto err_clk;
150 
151 	ret = platform_get_irq(pdev, 0);
152 	if (ret < 0)
153 		goto err_reset;
154 
155 	ret = usb_add_hcd(hcd, ret, IRQF_SHARED);
156 	if (ret)
157 		goto err_reset;
158 
159 	device_wakeup_enable(hcd->self.controller);
160 	return 0;
161 
162 err_reset:
163 	reset_control_assert(uhci->rsts);
164 err_clk:
165 	clk_disable_unprepare(uhci->clk);
166 err_rmr:
167 	usb_put_hcd(hcd);
168 
169 	return ret;
170 }
171 
uhci_hcd_platform_remove(struct platform_device * pdev)172 static void uhci_hcd_platform_remove(struct platform_device *pdev)
173 {
174 	struct usb_hcd *hcd = platform_get_drvdata(pdev);
175 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
176 
177 	reset_control_assert(uhci->rsts);
178 	clk_disable_unprepare(uhci->clk);
179 	usb_remove_hcd(hcd);
180 	usb_put_hcd(hcd);
181 }
182 
183 /* Make sure the controller is quiescent and that we're not using it
184  * any more.  This is mainly for the benefit of programs which, like kexec,
185  * expect the hardware to be idle: not doing DMA or generating IRQs.
186  *
187  * This routine may be called in a damaged or failing kernel.  Hence we
188  * do not acquire the spinlock before shutting down the controller.
189  */
uhci_hcd_platform_shutdown(struct platform_device * op)190 static void uhci_hcd_platform_shutdown(struct platform_device *op)
191 {
192 	struct usb_hcd *hcd = platform_get_drvdata(op);
193 
194 	uhci_hc_died(hcd_to_uhci(hcd));
195 }
196 
197 static const struct of_device_id platform_uhci_ids[] = {
198 	{ .compatible = "generic-uhci", },
199 	{ .compatible = "platform-uhci", },
200 	{ .compatible = "aspeed,ast2700-uhci", .data = (void *)1 },
201 	{}
202 };
203 MODULE_DEVICE_TABLE(of, platform_uhci_ids);
204 
205 static struct platform_driver uhci_platform_driver = {
206 	.probe		= uhci_hcd_platform_probe,
207 	.remove		= uhci_hcd_platform_remove,
208 	.shutdown	= uhci_hcd_platform_shutdown,
209 	.driver = {
210 		.name = "platform-uhci",
211 		.of_match_table = platform_uhci_ids,
212 	},
213 };
214