File Index
Files and directories
Sign in
FILE

uhci-platform.c

File preview with syntax highlighting.

linux-6.19.14/drivers/usb/host/uhci-platform.c C
  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
 16static 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
 38static 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
 68static 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
162err_reset:
163	reset_control_assert(uhci->rsts);
164err_clk:
165	clk_disable_unprepare(uhci->clk);
166err_rmr:
167	usb_put_hcd(hcd);
168
169	return ret;
170}
171
172static 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 */
190static 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
197static 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};
203MODULE_DEVICE_TABLE(of, platform_uhci_ids);
204
205static 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};
214MODULE_SOFTDEP("pre: ehci_platform");
~/ Read-only file preview.