1/* 2 * Bus Glue for Loongson LS1X built-in EHCI controller. 3 * 4 * Copyright (c) 2012 Zhang, Keguang <keguang.zhang@gmail.com> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 as published 8 * by the Free Software Foundation. 9 */ 10 11 12#include <linux/platform_device.h> 13 14static int ehci_ls1x_reset(struct usb_hcd *hcd) 15{ 16 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 17 int ret; 18 19 ehci->caps = hcd->regs; 20 21 ret = ehci_setup(hcd); 22 if (ret) 23 return ret; 24 25 ehci_port_power(ehci, 0); 26 27 return 0; 28} 29 30static const struct hc_driver ehci_ls1x_hc_driver = { 31 .description = hcd_name, 32 .product_desc = "LOONGSON1 EHCI", 33 .hcd_priv_size = sizeof(struct ehci_hcd), 34 35 /* 36 * generic hardware linkage 37 */ 38 .irq = ehci_irq, 39 .flags = HCD_MEMORY | HCD_USB2, 40 41 /* 42 * basic lifecycle operations 43 */ 44 .reset = ehci_ls1x_reset, 45 .start = ehci_run, 46 .stop = ehci_stop, 47 .shutdown = ehci_shutdown, 48 49 /* 50 * managing i/o requests and associated device resources 51 */ 52 .urb_enqueue = ehci_urb_enqueue, 53 .urb_dequeue = ehci_urb_dequeue, 54 .endpoint_disable = ehci_endpoint_disable, 55 .endpoint_reset = ehci_endpoint_reset, 56 57 /* 58 * scheduling support 59 */ 60 .get_frame_number = ehci_get_frame, 61 62 /* 63 * root hub support 64 */ 65 .hub_status_data = ehci_hub_status_data, 66 .hub_control = ehci_hub_control, 67 .relinquish_port = ehci_relinquish_port, 68 .port_handed_over = ehci_port_handed_over, 69 70 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 71}; 72 73static int ehci_hcd_ls1x_probe(struct platform_device *pdev) 74{ 75 struct usb_hcd *hcd; 76 struct resource *res; 77 int irq; 78 int ret; 79 80 pr_debug("initializing loongson1 ehci USB Controller\n"); 81 82 if (usb_disabled()) 83 return -ENODEV; 84 85 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 86 if (!res) { 87 dev_err(&pdev->dev, 88 "Found HC with no IRQ. Check %s setup!\n", 89 dev_name(&pdev->dev)); 90 return -ENODEV; 91 } 92 irq = res->start; 93 94 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 95 if (!res) { 96 dev_err(&pdev->dev, 97 "Found HC with no register addr. Check %s setup!\n", 98 dev_name(&pdev->dev)); 99 return -ENODEV; 100 } 101 102 hcd = usb_create_hcd(&ehci_ls1x_hc_driver, &pdev->dev, 103 dev_name(&pdev->dev)); 104 if (!hcd) 105 return -ENOMEM; 106 hcd->rsrc_start = res->start; 107 hcd->rsrc_len = resource_size(res); 108 109 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { 110 dev_dbg(&pdev->dev, "controller already in use\n"); 111 ret = -EBUSY; 112 goto err_put_hcd; 113 } 114 115 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 116 if (hcd->regs == NULL) { 117 dev_dbg(&pdev->dev, "error mapping memory\n"); 118 ret = -EFAULT; 119 goto err_release_region; 120 } 121 122 ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); 123 if (ret) 124 goto err_iounmap; 125 126 return ret; 127 128err_iounmap: 129 iounmap(hcd->regs); 130err_release_region: 131 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 132err_put_hcd: 133 usb_put_hcd(hcd); 134 return ret; 135} 136 137static int ehci_hcd_ls1x_remove(struct platform_device *pdev) 138{ 139 struct usb_hcd *hcd = platform_get_drvdata(pdev); 140 141 usb_remove_hcd(hcd); 142 iounmap(hcd->regs); 143 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 144 usb_put_hcd(hcd); 145 146 return 0; 147} 148 149static struct platform_driver ehci_ls1x_driver = { 150 .probe = ehci_hcd_ls1x_probe, 151 .remove = ehci_hcd_ls1x_remove, 152 .shutdown = usb_hcd_platform_shutdown, 153 .driver = { 154 .name = "ls1x-ehci", 155 .owner = THIS_MODULE, 156 }, 157}; 158 159MODULE_ALIAS(PLATFORM_MODULE_PREFIX "ls1x-ehci"); 160