Linux Audio

Check our new training course

Yocto / OpenEmbedded training

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