1 /* 2 * Generic UHCI HCD (Host Controller Driver) for Platform Devices 3 * 4 * Copyright (c) 2011 Tony Prisk <linux@prisktech.co.nz> 5 * 6 * This file is based on uhci-grlib.c 7 * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu 8 */ 9 10 #include <linux/of.h> 11 #include <linux/device.h> 12 #include <linux/platform_device.h> 13 14 static int uhci_platform_init(struct usb_hcd *hcd) 15 { 16 struct uhci_hcd *uhci = hcd_to_uhci(hcd); 17 18 uhci->rh_numports = uhci_count_ports(hcd); 19 20 /* Set up pointers to to generic functions */ 21 uhci->reset_hc = uhci_generic_reset_hc; 22 uhci->check_and_reset_hc = uhci_generic_check_and_reset_hc; 23 24 /* No special actions need to be taken for the functions below */ 25 uhci->configure_hc = NULL; 26 uhci->resume_detect_interrupts_are_broken = NULL; 27 uhci->global_suspend_mode_is_broken = NULL; 28 29 /* Reset if the controller isn't already safely quiescent. */ 30 check_and_reset_hc(uhci); 31 return 0; 32 } 33 34 static const struct hc_driver uhci_platform_hc_driver = { 35 .description = hcd_name, 36 .product_desc = "Generic UHCI Host Controller", 37 .hcd_priv_size = sizeof(struct uhci_hcd), 38 39 /* Generic hardware linkage */ 40 .irq = uhci_irq, 41 .flags = HCD_MEMORY | HCD_USB11, 42 43 /* Basic lifecycle operations */ 44 .reset = uhci_platform_init, 45 .start = uhci_start, 46 #ifdef CONFIG_PM 47 .pci_suspend = NULL, 48 .pci_resume = NULL, 49 .bus_suspend = uhci_rh_suspend, 50 .bus_resume = uhci_rh_resume, 51 #endif 52 .stop = uhci_stop, 53 54 .urb_enqueue = uhci_urb_enqueue, 55 .urb_dequeue = uhci_urb_dequeue, 56 57 .endpoint_disable = uhci_hcd_endpoint_disable, 58 .get_frame_number = uhci_hcd_get_frame_number, 59 60 .hub_status_data = uhci_hub_status_data, 61 .hub_control = uhci_hub_control, 62 }; 63 64 static int uhci_hcd_platform_probe(struct platform_device *pdev) 65 { 66 struct usb_hcd *hcd; 67 struct uhci_hcd *uhci; 68 struct resource *res; 69 int ret; 70 71 if (usb_disabled()) 72 return -ENODEV; 73 74 /* 75 * Right now device-tree probed devices don't get dma_mask set. 76 * Since shared usb code relies on it, set it here for now. 77 * Once we have dma capability bindings this can go away. 78 */ 79 ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 80 if (ret) 81 return ret; 82 83 hcd = usb_create_hcd(&uhci_platform_hc_driver, &pdev->dev, 84 pdev->name); 85 if (!hcd) 86 return -ENOMEM; 87 88 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 89 hcd->regs = devm_ioremap_resource(&pdev->dev, res); 90 if (IS_ERR(hcd->regs)) { 91 ret = PTR_ERR(hcd->regs); 92 goto err_rmr; 93 } 94 hcd->rsrc_start = res->start; 95 hcd->rsrc_len = resource_size(res); 96 97 uhci = hcd_to_uhci(hcd); 98 99 uhci->regs = hcd->regs; 100 101 ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); 102 if (ret) 103 goto err_rmr; 104 105 device_wakeup_enable(hcd->self.controller); 106 return 0; 107 108 err_rmr: 109 usb_put_hcd(hcd); 110 111 return ret; 112 } 113 114 static int uhci_hcd_platform_remove(struct platform_device *pdev) 115 { 116 struct usb_hcd *hcd = platform_get_drvdata(pdev); 117 118 usb_remove_hcd(hcd); 119 usb_put_hcd(hcd); 120 121 return 0; 122 } 123 124 /* Make sure the controller is quiescent and that we're not using it 125 * any more. This is mainly for the benefit of programs which, like kexec, 126 * expect the hardware to be idle: not doing DMA or generating IRQs. 127 * 128 * This routine may be called in a damaged or failing kernel. Hence we 129 * do not acquire the spinlock before shutting down the controller. 130 */ 131 static void uhci_hcd_platform_shutdown(struct platform_device *op) 132 { 133 struct usb_hcd *hcd = platform_get_drvdata(op); 134 135 uhci_hc_died(hcd_to_uhci(hcd)); 136 } 137 138 static const struct of_device_id platform_uhci_ids[] = { 139 { .compatible = "generic-uhci", }, 140 { .compatible = "platform-uhci", }, 141 {} 142 }; 143 144 static struct platform_driver uhci_platform_driver = { 145 .probe = uhci_hcd_platform_probe, 146 .remove = uhci_hcd_platform_remove, 147 .shutdown = uhci_hcd_platform_shutdown, 148 .driver = { 149 .name = "platform-uhci", 150 .of_match_table = platform_uhci_ids, 151 }, 152 }; 153