Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.9.4.
  1/*
  2 * EHCI HCD glue for Cavium Octeon II SOCs.
  3 *
  4 * Loosely based on ehci-au1xxx.c
  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 * Copyright (C) 2010 Cavium Networks
 11 *
 12 */
 13
 14#include <linux/platform_device.h>
 15
 16#include <asm/octeon/octeon.h>
 17#include <asm/octeon/cvmx-uctlx-defs.h>
 18
 19#define OCTEON_OHCI_HCD_NAME "octeon-ohci"
 20
 21/* Common clock init code.  */
 22void octeon2_usb_clocks_start(void);
 23void octeon2_usb_clocks_stop(void);
 24
 25static void ohci_octeon_hw_start(void)
 26{
 27	union cvmx_uctlx_ohci_ctl ohci_ctl;
 28
 29	octeon2_usb_clocks_start();
 30
 31	ohci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_OHCI_CTL(0));
 32	ohci_ctl.s.l2c_addr_msb = 0;
 33	ohci_ctl.s.l2c_buff_emod = 1; /* Byte swapped. */
 34	ohci_ctl.s.l2c_desc_emod = 1; /* Byte swapped. */
 35	cvmx_write_csr(CVMX_UCTLX_OHCI_CTL(0), ohci_ctl.u64);
 36
 37}
 38
 39static void ohci_octeon_hw_stop(void)
 40{
 41	/* Undo ohci_octeon_start() */
 42	octeon2_usb_clocks_stop();
 43}
 44
 45static int __devinit ohci_octeon_start(struct usb_hcd *hcd)
 46{
 47	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
 48	int ret;
 49
 50	ret = ohci_init(ohci);
 51
 52	if (ret < 0)
 53		return ret;
 54
 55	ret = ohci_run(ohci);
 56
 57	if (ret < 0) {
 58		ohci_err(ohci, "can't start %s", hcd->self.bus_name);
 59		ohci_stop(hcd);
 60		return ret;
 61	}
 62
 63	return 0;
 64}
 65
 66static const struct hc_driver ohci_octeon_hc_driver = {
 67	.description		= hcd_name,
 68	.product_desc		= "Octeon OHCI",
 69	.hcd_priv_size		= sizeof(struct ohci_hcd),
 70
 71	/*
 72	 * generic hardware linkage
 73	 */
 74	.irq =			ohci_irq,
 75	.flags =		HCD_USB11 | HCD_MEMORY,
 76
 77	/*
 78	 * basic lifecycle operations
 79	 */
 80	.start =		ohci_octeon_start,
 81	.stop =			ohci_stop,
 82	.shutdown =		ohci_shutdown,
 83
 84	/*
 85	 * managing i/o requests and associated device resources
 86	 */
 87	.urb_enqueue =		ohci_urb_enqueue,
 88	.urb_dequeue =		ohci_urb_dequeue,
 89	.endpoint_disable =	ohci_endpoint_disable,
 90
 91	/*
 92	 * scheduling support
 93	 */
 94	.get_frame_number =	ohci_get_frame,
 95
 96	/*
 97	 * root hub support
 98	 */
 99	.hub_status_data =	ohci_hub_status_data,
100	.hub_control =		ohci_hub_control,
101
102	.start_port_reset =	ohci_start_port_reset,
103};
104
105static int ohci_octeon_drv_probe(struct platform_device *pdev)
106{
107	struct usb_hcd *hcd;
108	struct ohci_hcd *ohci;
109	void *reg_base;
110	struct resource *res_mem;
111	int irq;
112	int ret;
113
114	if (usb_disabled())
115		return -ENODEV;
116
117	irq = platform_get_irq(pdev, 0);
118	if (irq < 0) {
119		dev_err(&pdev->dev, "No irq assigned\n");
120		return -ENODEV;
121	}
122
123	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
124	if (res_mem == NULL) {
125		dev_err(&pdev->dev, "No register space assigned\n");
126		return -ENODEV;
127	}
128
129	/* Ohci is a 32-bit device. */
130	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
131	pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
132
133	hcd = usb_create_hcd(&ohci_octeon_hc_driver, &pdev->dev, "octeon");
134	if (!hcd)
135		return -ENOMEM;
136
137	hcd->rsrc_start = res_mem->start;
138	hcd->rsrc_len = resource_size(res_mem);
139
140	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
141				OCTEON_OHCI_HCD_NAME)) {
142		dev_err(&pdev->dev, "request_mem_region failed\n");
143		ret = -EBUSY;
144		goto err1;
145	}
146
147	reg_base = ioremap(hcd->rsrc_start, hcd->rsrc_len);
148	if (!reg_base) {
149		dev_err(&pdev->dev, "ioremap failed\n");
150		ret = -ENOMEM;
151		goto err2;
152	}
153
154	ohci_octeon_hw_start();
155
156	hcd->regs = reg_base;
157
158	ohci = hcd_to_ohci(hcd);
159
160	/* Octeon OHCI matches CPU endianness. */
161#ifdef __BIG_ENDIAN
162	ohci->flags |= OHCI_QUIRK_BE_MMIO;
163#endif
164
165	ohci_hcd_init(ohci);
166
167	ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
168	if (ret) {
169		dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret);
170		goto err3;
171	}
172
173	platform_set_drvdata(pdev, hcd);
174
175	return 0;
176
177err3:
178	ohci_octeon_hw_stop();
179
180	iounmap(hcd->regs);
181err2:
182	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
183err1:
184	usb_put_hcd(hcd);
185	return ret;
186}
187
188static int ohci_octeon_drv_remove(struct platform_device *pdev)
189{
190	struct usb_hcd *hcd = platform_get_drvdata(pdev);
191
192	usb_remove_hcd(hcd);
193
194	ohci_octeon_hw_stop();
195	iounmap(hcd->regs);
196	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
197	usb_put_hcd(hcd);
198
199	platform_set_drvdata(pdev, NULL);
200
201	return 0;
202}
203
204static struct platform_driver ohci_octeon_driver = {
205	.probe		= ohci_octeon_drv_probe,
206	.remove		= ohci_octeon_drv_remove,
207	.shutdown	= usb_hcd_platform_shutdown,
208	.driver = {
209		.name	= OCTEON_OHCI_HCD_NAME,
210		.owner	= THIS_MODULE,
211	}
212};
213
214MODULE_ALIAS("platform:" OCTEON_OHCI_HCD_NAME);