Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * PMC MSP EHCI (Host Controller Driver) for USB.
  4 *
  5 * (C) Copyright 2006-2010 PMC-Sierra Inc
  6 */
  7
  8/* includes */
  9#include <linux/platform_device.h>
 10#include <linux/gpio.h>
 11#include <linux/usb.h>
 12#include <msp_usb.h>
 13
 14/* stream disable*/
 15#define USB_CTRL_MODE_STREAM_DISABLE	0x10
 16
 17/* threshold */
 18#define USB_CTRL_FIFO_THRESH		0x00300000
 19
 20/* register offset for usb_mode */
 21#define USB_EHCI_REG_USB_MODE		0x68
 22
 23/* register offset for usb fifo */
 24#define USB_EHCI_REG_USB_FIFO		0x24
 25
 26/* register offset for usb status */
 27#define USB_EHCI_REG_USB_STATUS		0x44
 28
 29/* serial/parallel transceiver */
 30#define USB_EHCI_REG_BIT_STAT_STS	(1<<29)
 31
 32/* TWI USB0 host device pin */
 33#define MSP_PIN_USB0_HOST_DEV		49
 34
 35/* TWI USB1 host device pin */
 36#define MSP_PIN_USB1_HOST_DEV		50
 37
 38
 39static void usb_hcd_tdi_set_mode(struct ehci_hcd *ehci)
 40{
 41	u8 *base;
 42	u8 *statreg;
 43	u8 *fiforeg;
 44	u32 val;
 45	struct ehci_regs *reg_base = ehci->regs;
 46
 47	/* get register base */
 48	base = (u8 *)reg_base + USB_EHCI_REG_USB_MODE;
 49	statreg = (u8 *)reg_base + USB_EHCI_REG_USB_STATUS;
 50	fiforeg = (u8 *)reg_base + USB_EHCI_REG_USB_FIFO;
 51
 52	/* Disable controller mode stream */
 53	val = ehci_readl(ehci, (u32 *)base);
 54	ehci_writel(ehci, (val | USB_CTRL_MODE_STREAM_DISABLE),
 55			(u32 *)base);
 56
 57	/* clear STS to select parallel transceiver interface */
 58	val = ehci_readl(ehci, (u32 *)statreg);
 59	val = val & ~USB_EHCI_REG_BIT_STAT_STS;
 60	ehci_writel(ehci, val, (u32 *)statreg);
 61
 62	/* write to set the proper fifo threshold */
 63	ehci_writel(ehci, USB_CTRL_FIFO_THRESH, (u32 *)fiforeg);
 64
 65	/* set TWI GPIO USB_HOST_DEV pin high */
 66	gpio_direction_output(MSP_PIN_USB0_HOST_DEV, 1);
 67}
 68
 69/* called during probe() after chip reset completes */
 70static int ehci_msp_setup(struct usb_hcd *hcd)
 71{
 72	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
 73	int			retval;
 74
 75	ehci->big_endian_mmio = 1;
 76	ehci->big_endian_desc = 1;
 77
 78	ehci->caps = hcd->regs;
 79	hcd->has_tt = 1;
 80
 81	retval = ehci_setup(hcd);
 82	if (retval)
 83		return retval;
 84
 85	usb_hcd_tdi_set_mode(ehci);
 86
 87	return retval;
 88}
 89
 90
 91/* configure so an HC device and id are always provided
 92 * always called with process context; sleeping is OK
 93 */
 94
 95static int usb_hcd_msp_map_regs(struct mspusb_device *dev)
 96{
 97	struct resource *res;
 98	struct platform_device *pdev = &dev->dev;
 99	u32 res_len;
100	int retval;
101
102	/* MAB register space */
103	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
104	if (res == NULL)
105		return -ENOMEM;
106	res_len = resource_size(res);
107	if (!request_mem_region(res->start, res_len, "mab regs"))
108		return -EBUSY;
109
110	dev->mab_regs = ioremap(res->start, res_len);
111	if (dev->mab_regs == NULL) {
112		retval = -ENOMEM;
113		goto err1;
114	}
115
116	/* MSP USB register space */
117	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
118	if (res == NULL) {
119		retval = -ENOMEM;
120		goto err2;
121	}
122	res_len = resource_size(res);
123	if (!request_mem_region(res->start, res_len, "usbid regs")) {
124		retval = -EBUSY;
125		goto err2;
126	}
127	dev->usbid_regs = ioremap(res->start, res_len);
128	if (dev->usbid_regs == NULL) {
129		retval = -ENOMEM;
130		goto err3;
131	}
132
133	return 0;
134err3:
135	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
136	res_len = resource_size(res);
137	release_mem_region(res->start, res_len);
138err2:
139	iounmap(dev->mab_regs);
140err1:
141	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
142	res_len = resource_size(res);
143	release_mem_region(res->start, res_len);
144	dev_err(&pdev->dev, "Failed to map non-EHCI regs.\n");
145	return retval;
146}
147
148/**
149 * usb_hcd_msp_probe - initialize PMC MSP-based HCDs
150 * @driver:	Pointer to hc driver instance
151 * @dev:	USB controller to probe
152 *
153 * Context: task context, might sleep
154 *
155 * Allocates basic resources for this USB host controller, and
156 * then invokes the start() method for the HCD associated with it
157 * through the hotplug entry's driver_data.
158 */
159int usb_hcd_msp_probe(const struct hc_driver *driver,
160			  struct platform_device *dev)
161{
162	int retval;
163	struct usb_hcd *hcd;
164	struct resource *res;
165	struct ehci_hcd		*ehci ;
166
167	hcd = usb_create_hcd(driver, &dev->dev, "pmcmsp");
168	if (!hcd)
169		return -ENOMEM;
170
171	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
172	if (res == NULL) {
173		pr_debug("No IOMEM resource info for %s.\n", dev->name);
174		retval = -ENOMEM;
175		goto err1;
176	}
177	hcd->rsrc_start = res->start;
178	hcd->rsrc_len = resource_size(res);
179	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, dev->name)) {
180		retval = -EBUSY;
181		goto err1;
182	}
183	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
184	if (!hcd->regs) {
185		pr_debug("ioremap failed");
186		retval = -ENOMEM;
187		goto err2;
188	}
189
190	res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
191	if (res == NULL) {
192		dev_err(&dev->dev, "No IRQ resource info for %s.\n", dev->name);
193		retval = -ENOMEM;
194		goto err3;
195	}
196
197	/* Map non-EHCI register spaces */
198	retval = usb_hcd_msp_map_regs(to_mspusb_device(dev));
199	if (retval != 0)
200		goto err3;
201
202	ehci = hcd_to_ehci(hcd);
203	ehci->big_endian_mmio = 1;
204	ehci->big_endian_desc = 1;
205
206
207	retval = usb_add_hcd(hcd, res->start, IRQF_SHARED);
208	if (retval == 0) {
209		device_wakeup_enable(hcd->self.controller);
210		return 0;
211	}
212
213	usb_remove_hcd(hcd);
214err3:
215	iounmap(hcd->regs);
216err2:
217	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
218err1:
219	usb_put_hcd(hcd);
220
221	return retval;
222}
223
224
225
226/**
227 * usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs
228 * @hcd: USB Host Controller being removed
229 *
230 * Context: task context, might sleep
231 *
232 * Reverses the effect of usb_hcd_msp_probe(), first invoking
233 * the HCD's stop() method.  It is always called from a thread
234 * context, normally "rmmod", "apmd", or something similar.
235 *
236 * may be called without controller electrically present
237 * may be called with controller, bus, and devices active
238 */
239static void usb_hcd_msp_remove(struct usb_hcd *hcd)
240{
241	usb_remove_hcd(hcd);
242	iounmap(hcd->regs);
243	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
244	usb_put_hcd(hcd);
245}
246
247static const struct hc_driver ehci_msp_hc_driver = {
248	.description =		hcd_name,
249	.product_desc =		"PMC MSP EHCI",
250	.hcd_priv_size =	sizeof(struct ehci_hcd),
251
252	/*
253	 * generic hardware linkage
254	 */
255	.irq =			ehci_irq,
256	.flags =		HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
257
258	/*
259	 * basic lifecycle operations
260	 */
261	.reset			= ehci_msp_setup,
262	.shutdown		= ehci_shutdown,
263	.start			= ehci_run,
264	.stop			= ehci_stop,
265
266	/*
267	 * managing i/o requests and associated device resources
268	 */
269	.urb_enqueue		= ehci_urb_enqueue,
270	.urb_dequeue		= ehci_urb_dequeue,
271	.endpoint_disable	= ehci_endpoint_disable,
272	.endpoint_reset		= ehci_endpoint_reset,
273
274	/*
275	 * scheduling support
276	 */
277	.get_frame_number	= ehci_get_frame,
278
279	/*
280	 * root hub support
281	 */
282	.hub_status_data	= ehci_hub_status_data,
283	.hub_control		= ehci_hub_control,
284	.bus_suspend		= ehci_bus_suspend,
285	.bus_resume		= ehci_bus_resume,
286	.relinquish_port	= ehci_relinquish_port,
287	.port_handed_over	= ehci_port_handed_over,
288
289	.clear_tt_buffer_complete	= ehci_clear_tt_buffer_complete,
290};
291
292static int ehci_hcd_msp_drv_probe(struct platform_device *pdev)
293{
294	int ret;
295
296	pr_debug("In ehci_hcd_msp_drv_probe");
297
298	if (usb_disabled())
299		return -ENODEV;
300
301	gpio_request(MSP_PIN_USB0_HOST_DEV, "USB0_HOST_DEV_GPIO");
302
303	ret = usb_hcd_msp_probe(&ehci_msp_hc_driver, pdev);
304
305	return ret;
306}
307
308static int ehci_hcd_msp_drv_remove(struct platform_device *pdev)
309{
310	struct usb_hcd *hcd = platform_get_drvdata(pdev);
311
312	usb_hcd_msp_remove(hcd);
313
314	/* free TWI GPIO USB_HOST_DEV pin */
315	gpio_free(MSP_PIN_USB0_HOST_DEV);
316
317	return 0;
318}
319
320MODULE_ALIAS("pmcmsp-ehci");
321
322static struct platform_driver ehci_hcd_msp_driver = {
323	.probe		= ehci_hcd_msp_drv_probe,
324	.remove		= ehci_hcd_msp_drv_remove,
325	.driver		= {
326		.name	= "pmcmsp-ehci",
327	},
328};