11643accdaad4625c2877f7ceefa39c1cb3e90117David Daney/*
21643accdaad4625c2877f7ceefa39c1cb3e90117David Daney * EHCI HCD glue for Cavium Octeon II SOCs.
31643accdaad4625c2877f7ceefa39c1cb3e90117David Daney *
41643accdaad4625c2877f7ceefa39c1cb3e90117David Daney * Loosely based on ehci-au1xxx.c
51643accdaad4625c2877f7ceefa39c1cb3e90117David Daney *
61643accdaad4625c2877f7ceefa39c1cb3e90117David Daney * This file is subject to the terms and conditions of the GNU General Public
71643accdaad4625c2877f7ceefa39c1cb3e90117David Daney * License.  See the file "COPYING" in the main directory of this archive
81643accdaad4625c2877f7ceefa39c1cb3e90117David Daney * for more details.
91643accdaad4625c2877f7ceefa39c1cb3e90117David Daney *
101643accdaad4625c2877f7ceefa39c1cb3e90117David Daney * Copyright (C) 2010 Cavium Networks
111643accdaad4625c2877f7ceefa39c1cb3e90117David Daney *
121643accdaad4625c2877f7ceefa39c1cb3e90117David Daney */
131643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
141643accdaad4625c2877f7ceefa39c1cb3e90117David Daney#include <linux/platform_device.h>
151643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
161643accdaad4625c2877f7ceefa39c1cb3e90117David Daney#include <asm/octeon/octeon.h>
171643accdaad4625c2877f7ceefa39c1cb3e90117David Daney#include <asm/octeon/cvmx-uctlx-defs.h>
181643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
191643accdaad4625c2877f7ceefa39c1cb3e90117David Daney#define OCTEON_OHCI_HCD_NAME "octeon-ohci"
201643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
211643accdaad4625c2877f7ceefa39c1cb3e90117David Daney/* Common clock init code.  */
221643accdaad4625c2877f7ceefa39c1cb3e90117David Daneyvoid octeon2_usb_clocks_start(void);
231643accdaad4625c2877f7ceefa39c1cb3e90117David Daneyvoid octeon2_usb_clocks_stop(void);
241643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
251643accdaad4625c2877f7ceefa39c1cb3e90117David Daneystatic void ohci_octeon_hw_start(void)
261643accdaad4625c2877f7ceefa39c1cb3e90117David Daney{
271643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	union cvmx_uctlx_ohci_ctl ohci_ctl;
281643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
291643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	octeon2_usb_clocks_start();
301643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
311643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	ohci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_OHCI_CTL(0));
321643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	ohci_ctl.s.l2c_addr_msb = 0;
331643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	ohci_ctl.s.l2c_buff_emod = 1; /* Byte swapped. */
341643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	ohci_ctl.s.l2c_desc_emod = 1; /* Byte swapped. */
351643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	cvmx_write_csr(CVMX_UCTLX_OHCI_CTL(0), ohci_ctl.u64);
361643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
371643accdaad4625c2877f7ceefa39c1cb3e90117David Daney}
381643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
391643accdaad4625c2877f7ceefa39c1cb3e90117David Daneystatic void ohci_octeon_hw_stop(void)
401643accdaad4625c2877f7ceefa39c1cb3e90117David Daney{
411643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	/* Undo ohci_octeon_start() */
421643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	octeon2_usb_clocks_stop();
431643accdaad4625c2877f7ceefa39c1cb3e90117David Daney}
441643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
4541ac7b3ab7fe1d6175839947a877fdf95cbd2211Bill Pembertonstatic int ohci_octeon_start(struct usb_hcd *hcd)
461643accdaad4625c2877f7ceefa39c1cb3e90117David Daney{
471643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
481643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	int ret;
491643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
501643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	ret = ohci_init(ohci);
511643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
521643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	if (ret < 0)
531643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		return ret;
541643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
551643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	ret = ohci_run(ohci);
561643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
571643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	if (ret < 0) {
581643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		ohci_err(ohci, "can't start %s", hcd->self.bus_name);
591643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		ohci_stop(hcd);
601643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		return ret;
611643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	}
621643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
631643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	return 0;
641643accdaad4625c2877f7ceefa39c1cb3e90117David Daney}
651643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
661643accdaad4625c2877f7ceefa39c1cb3e90117David Daneystatic const struct hc_driver ohci_octeon_hc_driver = {
671643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.description		= hcd_name,
681643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.product_desc		= "Octeon OHCI",
691643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.hcd_priv_size		= sizeof(struct ohci_hcd),
701643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
711643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	/*
721643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	 * generic hardware linkage
731643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	 */
741643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.irq =			ohci_irq,
751643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.flags =		HCD_USB11 | HCD_MEMORY,
761643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
771643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	/*
781643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	 * basic lifecycle operations
791643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	 */
801643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.start =		ohci_octeon_start,
811643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.stop =			ohci_stop,
821643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.shutdown =		ohci_shutdown,
831643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
841643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	/*
851643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	 * managing i/o requests and associated device resources
861643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	 */
871643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.urb_enqueue =		ohci_urb_enqueue,
881643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.urb_dequeue =		ohci_urb_dequeue,
891643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.endpoint_disable =	ohci_endpoint_disable,
901643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
911643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	/*
921643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	 * scheduling support
931643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	 */
941643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.get_frame_number =	ohci_get_frame,
951643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
961643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	/*
971643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	 * root hub support
981643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	 */
991643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.hub_status_data =	ohci_hub_status_data,
1001643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.hub_control =		ohci_hub_control,
1011643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1021643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.start_port_reset =	ohci_start_port_reset,
1031643accdaad4625c2877f7ceefa39c1cb3e90117David Daney};
1041643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1051643accdaad4625c2877f7ceefa39c1cb3e90117David Daneystatic int ohci_octeon_drv_probe(struct platform_device *pdev)
1061643accdaad4625c2877f7ceefa39c1cb3e90117David Daney{
1071643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	struct usb_hcd *hcd;
1081643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	struct ohci_hcd *ohci;
1091643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	void *reg_base;
1101643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	struct resource *res_mem;
1111643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	int irq;
1121643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	int ret;
1131643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1141643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	if (usb_disabled())
1151643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		return -ENODEV;
1161643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1171643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	irq = platform_get_irq(pdev, 0);
1181643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	if (irq < 0) {
1191643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		dev_err(&pdev->dev, "No irq assigned\n");
1201643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		return -ENODEV;
1211643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	}
1221643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1231643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1241643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	if (res_mem == NULL) {
1251643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		dev_err(&pdev->dev, "No register space assigned\n");
1261643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		return -ENODEV;
1271643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	}
1281643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1291643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	/* Ohci is a 32-bit device. */
130e1fd7341837238c6c5380c5073887d238f706cf0Russell King	ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
13122d9d8e8316d7f69046c8805ce9aa8d9c43d4e5bRussell King	if (ret)
13222d9d8e8316d7f69046c8805ce9aa8d9c43d4e5bRussell King		return ret;
1331643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1341643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	hcd = usb_create_hcd(&ohci_octeon_hc_driver, &pdev->dev, "octeon");
1351643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	if (!hcd)
1361643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		return -ENOMEM;
1371643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1381643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	hcd->rsrc_start = res_mem->start;
13928f65c11f2ffb3957259dece647a24f8ad2e241bJoe Perches	hcd->rsrc_len = resource_size(res_mem);
1401643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
14181574fc65f67ca5948ee80af0ec030a20a802dd3Jingoo Han	reg_base = devm_ioremap_resource(&pdev->dev, res_mem);
14281574fc65f67ca5948ee80af0ec030a20a802dd3Jingoo Han	if (IS_ERR(reg_base)) {
14381574fc65f67ca5948ee80af0ec030a20a802dd3Jingoo Han		ret = PTR_ERR(reg_base);
1441643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		goto err1;
1451643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	}
1461643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1471643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	ohci_octeon_hw_start();
1481643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1491643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	hcd->regs = reg_base;
1501643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1511643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	ohci = hcd_to_ohci(hcd);
1521643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1531643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	/* Octeon OHCI matches CPU endianness. */
1541643accdaad4625c2877f7ceefa39c1cb3e90117David Daney#ifdef __BIG_ENDIAN
1551643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	ohci->flags |= OHCI_QUIRK_BE_MMIO;
1561643accdaad4625c2877f7ceefa39c1cb3e90117David Daney#endif
1571643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1581643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	ohci_hcd_init(ohci);
1591643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
160b5dd18d8747010e3f3eb1cc76a49f94291938559Yong Zhang	ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
1611643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	if (ret) {
1621643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret);
16381574fc65f67ca5948ee80af0ec030a20a802dd3Jingoo Han		goto err2;
1641643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	}
1651643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1663c9740a117d40a74412775b5d3fe2b88a7635a0ePeter Chen	device_wakeup_enable(hcd->self.controller);
1673c9740a117d40a74412775b5d3fe2b88a7635a0ePeter Chen
1681643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	platform_set_drvdata(pdev, hcd);
1691643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1701643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	return 0;
1711643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
17281574fc65f67ca5948ee80af0ec030a20a802dd3Jingoo Hanerr2:
1731643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	ohci_octeon_hw_stop();
1741643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1751643accdaad4625c2877f7ceefa39c1cb3e90117David Daneyerr1:
1761643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	usb_put_hcd(hcd);
1771643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	return ret;
1781643accdaad4625c2877f7ceefa39c1cb3e90117David Daney}
1791643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1801643accdaad4625c2877f7ceefa39c1cb3e90117David Daneystatic int ohci_octeon_drv_remove(struct platform_device *pdev)
1811643accdaad4625c2877f7ceefa39c1cb3e90117David Daney{
1821643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	struct usb_hcd *hcd = platform_get_drvdata(pdev);
1831643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1841643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	usb_remove_hcd(hcd);
1851643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1861643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	ohci_octeon_hw_stop();
1871643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	usb_put_hcd(hcd);
1881643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1891643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	return 0;
1901643accdaad4625c2877f7ceefa39c1cb3e90117David Daney}
1911643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
1921643accdaad4625c2877f7ceefa39c1cb3e90117David Daneystatic struct platform_driver ohci_octeon_driver = {
1931643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.probe		= ohci_octeon_drv_probe,
1941643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.remove		= ohci_octeon_drv_remove,
1951643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.shutdown	= usb_hcd_platform_shutdown,
1961643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	.driver = {
1971643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		.name	= OCTEON_OHCI_HCD_NAME,
1981643accdaad4625c2877f7ceefa39c1cb3e90117David Daney		.owner	= THIS_MODULE,
1991643accdaad4625c2877f7ceefa39c1cb3e90117David Daney	}
2001643accdaad4625c2877f7ceefa39c1cb3e90117David Daney};
2011643accdaad4625c2877f7ceefa39c1cb3e90117David Daney
2021643accdaad4625c2877f7ceefa39c1cb3e90117David DaneyMODULE_ALIAS("platform:" OCTEON_OHCI_HCD_NAME);
203