diff options
| -rw-r--r-- | drivers/usb/host/ohci-hcd.c | 5 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-sm501.c | 264 |
2 files changed, 269 insertions, 0 deletions
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index b8ed84d5406d..dd4798ee028e 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c | |||
| @@ -1052,6 +1052,11 @@ MODULE_LICENSE ("GPL"); | |||
| 1052 | #define SSB_OHCI_DRIVER ssb_ohci_driver | 1052 | #define SSB_OHCI_DRIVER ssb_ohci_driver |
| 1053 | #endif | 1053 | #endif |
| 1054 | 1054 | ||
| 1055 | #ifdef CONFIG_MFD_SM501 | ||
| 1056 | #include "ohci-sm501.c" | ||
| 1057 | #define PLATFORM_DRIVER ohci_hcd_sm501_driver | ||
| 1058 | #endif | ||
| 1059 | |||
| 1055 | #if !defined(PCI_DRIVER) && \ | 1060 | #if !defined(PCI_DRIVER) && \ |
| 1056 | !defined(PLATFORM_DRIVER) && \ | 1061 | !defined(PLATFORM_DRIVER) && \ |
| 1057 | !defined(OF_PLATFORM_DRIVER) && \ | 1062 | !defined(OF_PLATFORM_DRIVER) && \ |
diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c new file mode 100644 index 000000000000..a97070142869 --- /dev/null +++ b/drivers/usb/host/ohci-sm501.c | |||
| @@ -0,0 +1,264 @@ | |||
| 1 | /* | ||
| 2 | * OHCI HCD (Host Controller Driver) for USB. | ||
| 3 | * | ||
| 4 | * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> | ||
| 5 | * (C) Copyright 2000-2005 David Brownell | ||
| 6 | * (C) Copyright 2002 Hewlett-Packard Company | ||
| 7 | * (C) Copyright 2008 Magnus Damm | ||
| 8 | * | ||
| 9 | * SM501 Bus Glue - based on ohci-omap.c | ||
| 10 | * | ||
| 11 | * This file is licenced under the GPL. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/interrupt.h> | ||
| 15 | #include <linux/jiffies.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | #include <linux/dma-mapping.h> | ||
| 18 | #include <linux/sm501.h> | ||
| 19 | #include <linux/sm501-regs.h> | ||
| 20 | |||
| 21 | static int ohci_sm501_init(struct usb_hcd *hcd) | ||
| 22 | { | ||
| 23 | return ohci_init(hcd_to_ohci(hcd)); | ||
| 24 | } | ||
| 25 | |||
| 26 | static int ohci_sm501_start(struct usb_hcd *hcd) | ||
| 27 | { | ||
| 28 | struct device *dev = hcd->self.controller; | ||
| 29 | int ret; | ||
| 30 | |||
| 31 | ret = ohci_run(hcd_to_ohci(hcd)); | ||
| 32 | if (ret < 0) { | ||
| 33 | dev_err(dev, "can't start %s", hcd->self.bus_name); | ||
| 34 | ohci_stop(hcd); | ||
| 35 | } | ||
| 36 | |||
| 37 | return ret; | ||
| 38 | } | ||
| 39 | |||
| 40 | /*-------------------------------------------------------------------------*/ | ||
| 41 | |||
| 42 | static const struct hc_driver ohci_sm501_hc_driver = { | ||
| 43 | .description = hcd_name, | ||
| 44 | .product_desc = "SM501 OHCI", | ||
| 45 | .hcd_priv_size = sizeof(struct ohci_hcd), | ||
| 46 | |||
| 47 | /* | ||
| 48 | * generic hardware linkage | ||
| 49 | */ | ||
| 50 | .irq = ohci_irq, | ||
| 51 | .flags = HCD_USB11 | HCD_MEMORY | HCD_LOCAL_MEM, | ||
| 52 | |||
| 53 | /* | ||
| 54 | * basic lifecycle operations | ||
| 55 | */ | ||
| 56 | .reset = ohci_sm501_init, | ||
| 57 | .start = ohci_sm501_start, | ||
| 58 | .stop = ohci_stop, | ||
| 59 | .shutdown = ohci_shutdown, | ||
| 60 | |||
| 61 | /* | ||
| 62 | * managing i/o requests and associated device resources | ||
| 63 | */ | ||
| 64 | .urb_enqueue = ohci_urb_enqueue, | ||
| 65 | .urb_dequeue = ohci_urb_dequeue, | ||
| 66 | .endpoint_disable = ohci_endpoint_disable, | ||
| 67 | |||
| 68 | /* | ||
| 69 | * scheduling support | ||
| 70 | */ | ||
| 71 | .get_frame_number = ohci_get_frame, | ||
| 72 | |||
| 73 | /* | ||
| 74 | * root hub support | ||
| 75 | */ | ||
| 76 | .hub_status_data = ohci_hub_status_data, | ||
| 77 | .hub_control = ohci_hub_control, | ||
| 78 | .hub_irq_enable = ohci_rhsc_enable, | ||
| 79 | #ifdef CONFIG_PM | ||
| 80 | .bus_suspend = ohci_bus_suspend, | ||
| 81 | .bus_resume = ohci_bus_resume, | ||
| 82 | #endif | ||
| 83 | .start_port_reset = ohci_start_port_reset, | ||
| 84 | }; | ||
| 85 | |||
| 86 | /*-------------------------------------------------------------------------*/ | ||
| 87 | |||
| 88 | static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) | ||
| 89 | { | ||
| 90 | const struct hc_driver *driver = &ohci_sm501_hc_driver; | ||
| 91 | struct device *dev = &pdev->dev; | ||
| 92 | struct resource *res, *mem; | ||
| 93 | int retval, irq; | ||
| 94 | struct usb_hcd *hcd = 0; | ||
| 95 | |||
| 96 | irq = retval = platform_get_irq(pdev, 0); | ||
| 97 | if (retval < 0) | ||
| 98 | goto err0; | ||
| 99 | |||
| 100 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
| 101 | if (mem == NULL) { | ||
| 102 | dev_err(dev, "no resource definition for memory\n"); | ||
| 103 | retval = -ENOENT; | ||
| 104 | goto err0; | ||
| 105 | } | ||
| 106 | |||
| 107 | if (!request_mem_region(mem->start, mem->end - mem->start + 1, | ||
| 108 | pdev->name)) { | ||
| 109 | dev_err(dev, "request_mem_region failed\n"); | ||
| 110 | retval = -EBUSY; | ||
| 111 | goto err0; | ||
| 112 | } | ||
| 113 | |||
| 114 | /* The sm501 chip is equipped with local memory that may be used | ||
| 115 | * by on-chip devices such as the video controller and the usb host. | ||
| 116 | * This driver uses dma_declare_coherent_memory() to make sure | ||
| 117 | * usb allocations with dma_alloc_coherent() allocate from | ||
| 118 | * this local memory. The dma_handle returned by dma_alloc_coherent() | ||
| 119 | * will be an offset starting from 0 for the first local memory byte. | ||
| 120 | * | ||
| 121 | * So as long as data is allocated using dma_alloc_coherent() all is | ||
| 122 | * fine. This is however not always the case - buffers may be allocated | ||
| 123 | * using kmalloc() - so the usb core needs to be told that it must copy | ||
| 124 | * data into our local memory if the buffers happen to be placed in | ||
| 125 | * regular memory. The HCD_LOCAL_MEM flag does just that. | ||
| 126 | */ | ||
| 127 | |||
| 128 | if (!dma_declare_coherent_memory(dev, mem->start, | ||
| 129 | mem->start - mem->parent->start, | ||
| 130 | (mem->end - mem->start) + 1, | ||
| 131 | DMA_MEMORY_MAP | | ||
| 132 | DMA_MEMORY_EXCLUSIVE)) { | ||
| 133 | dev_err(dev, "cannot declare coherent memory\n"); | ||
| 134 | retval = -ENXIO; | ||
| 135 | goto err1; | ||
| 136 | } | ||
| 137 | |||
| 138 | /* allocate, reserve and remap resources for registers */ | ||
| 139 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 140 | if (res == NULL) { | ||
| 141 | dev_err(dev, "no resource definition for registers\n"); | ||
| 142 | retval = -ENOENT; | ||
| 143 | goto err2; | ||
| 144 | } | ||
| 145 | |||
| 146 | hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id); | ||
| 147 | if (!hcd) { | ||
| 148 | retval = -ENOMEM; | ||
| 149 | goto err2; | ||
| 150 | } | ||
| 151 | |||
| 152 | hcd->rsrc_start = res->start; | ||
| 153 | hcd->rsrc_len = res->end - res->start + 1; | ||
| 154 | |||
| 155 | if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, pdev->name)) { | ||
| 156 | dev_err(dev, "request_mem_region failed\n"); | ||
| 157 | retval = -EBUSY; | ||
| 158 | goto err3; | ||
| 159 | } | ||
| 160 | |||
| 161 | hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); | ||
| 162 | if (hcd->regs == NULL) { | ||
| 163 | dev_err(dev, "cannot remap registers\n"); | ||
| 164 | retval = -ENXIO; | ||
| 165 | goto err4; | ||
| 166 | } | ||
| 167 | |||
| 168 | ohci_hcd_init(hcd_to_ohci(hcd)); | ||
| 169 | |||
| 170 | retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); | ||
| 171 | if (retval) | ||
| 172 | goto err4; | ||
| 173 | |||
| 174 | /* enable power and unmask interrupts */ | ||
| 175 | |||
| 176 | sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 1); | ||
| 177 | sm501_modify_reg(dev->parent, SM501_IRQ_MASK, 1 << 6, 0); | ||
| 178 | |||
| 179 | return 0; | ||
| 180 | err4: | ||
| 181 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | ||
| 182 | err3: | ||
| 183 | usb_put_hcd(hcd); | ||
| 184 | err2: | ||
| 185 | dma_release_declared_memory(dev); | ||
| 186 | err1: | ||
| 187 | release_mem_region(mem->start, mem->end - mem->start + 1); | ||
| 188 | err0: | ||
| 189 | return retval; | ||
| 190 | } | ||
| 191 | |||
| 192 | static int ohci_hcd_sm501_drv_remove(struct platform_device *pdev) | ||
| 193 | { | ||
| 194 | struct usb_hcd *hcd = platform_get_drvdata(pdev); | ||
| 195 | struct resource *mem; | ||
| 196 | |||
| 197 | usb_remove_hcd(hcd); | ||
| 198 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | ||
| 199 | usb_put_hcd(hcd); | ||
| 200 | dma_release_declared_memory(&pdev->dev); | ||
| 201 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
| 202 | release_mem_region(mem->start, mem->end - mem->start + 1); | ||
| 203 | |||
| 204 | /* mask interrupts and disable power */ | ||
| 205 | |||
| 206 | sm501_modify_reg(pdev->dev.parent, SM501_IRQ_MASK, 0, 1 << 6); | ||
| 207 | sm501_unit_power(pdev->dev.parent, SM501_GATE_USB_HOST, 0); | ||
| 208 | |||
| 209 | platform_set_drvdata(pdev, NULL); | ||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | /*-------------------------------------------------------------------------*/ | ||
| 214 | |||
| 215 | #ifdef CONFIG_PM | ||
| 216 | static int ohci_sm501_suspend(struct platform_device *pdev, pm_message_t msg) | ||
| 217 | { | ||
| 218 | struct device *dev = &pdev->dev; | ||
| 219 | struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(pdev)); | ||
| 220 | |||
| 221 | if (time_before(jiffies, ohci->next_statechange)) | ||
| 222 | msleep(5); | ||
| 223 | ohci->next_statechange = jiffies; | ||
| 224 | |||
| 225 | sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 0); | ||
| 226 | ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; | ||
| 227 | dev->power.power_state = PMSG_SUSPEND; | ||
| 228 | return 0; | ||
| 229 | } | ||
| 230 | |||
| 231 | static int ohci_sm501_resume(struct platform_device *pdev) | ||
| 232 | { | ||
| 233 | struct device *dev = &pdev->dev; | ||
| 234 | struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(pdev)); | ||
| 235 | |||
| 236 | if (time_before(jiffies, ohci->next_statechange)) | ||
| 237 | msleep(5); | ||
| 238 | ohci->next_statechange = jiffies; | ||
| 239 | |||
| 240 | sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 1); | ||
| 241 | dev->power.power_state = PMSG_ON; | ||
| 242 | usb_hcd_resume_root_hub(platform_get_drvdata(pdev)); | ||
| 243 | return 0; | ||
| 244 | } | ||
| 245 | #endif | ||
| 246 | |||
| 247 | /*-------------------------------------------------------------------------*/ | ||
| 248 | |||
| 249 | /* | ||
| 250 | * Driver definition to register with the SM501 bus | ||
| 251 | */ | ||
| 252 | static struct platform_driver ohci_hcd_sm501_driver = { | ||
| 253 | .probe = ohci_hcd_sm501_drv_probe, | ||
| 254 | .remove = ohci_hcd_sm501_drv_remove, | ||
| 255 | .shutdown = usb_hcd_platform_shutdown, | ||
| 256 | #ifdef CONFIG_PM | ||
| 257 | .suspend = ohci_sm501_suspend, | ||
| 258 | .resume = ohci_sm501_resume, | ||
| 259 | #endif | ||
| 260 | .driver = { | ||
| 261 | .owner = THIS_MODULE, | ||
| 262 | .name = "sm501-usb", | ||
| 263 | }, | ||
| 264 | }; | ||
