Merge tag 'irqchip-4.13-3' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm...
[linux-2.6-microblaze.git] / drivers / usb / host / uhci-platform.c
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         /* Probe number of ports if not already provided by DT */
19         if (!uhci->rh_numports)
20                 uhci->rh_numports = uhci_count_ports(hcd);
21
22         /* Set up pointers to to generic functions */
23         uhci->reset_hc = uhci_generic_reset_hc;
24         uhci->check_and_reset_hc = uhci_generic_check_and_reset_hc;
25
26         /* No special actions need to be taken for the functions below */
27         uhci->configure_hc = NULL;
28         uhci->resume_detect_interrupts_are_broken = NULL;
29         uhci->global_suspend_mode_is_broken = NULL;
30
31         /* Reset if the controller isn't already safely quiescent. */
32         check_and_reset_hc(uhci);
33         return 0;
34 }
35
36 static const struct hc_driver uhci_platform_hc_driver = {
37         .description =          hcd_name,
38         .product_desc =         "Generic UHCI Host Controller",
39         .hcd_priv_size =        sizeof(struct uhci_hcd),
40
41         /* Generic hardware linkage */
42         .irq =                  uhci_irq,
43         .flags =                HCD_MEMORY | HCD_USB11,
44
45         /* Basic lifecycle operations */
46         .reset =                uhci_platform_init,
47         .start =                uhci_start,
48 #ifdef CONFIG_PM
49         .pci_suspend =          NULL,
50         .pci_resume =           NULL,
51         .bus_suspend =          uhci_rh_suspend,
52         .bus_resume =           uhci_rh_resume,
53 #endif
54         .stop =                 uhci_stop,
55
56         .urb_enqueue =          uhci_urb_enqueue,
57         .urb_dequeue =          uhci_urb_dequeue,
58
59         .endpoint_disable =     uhci_hcd_endpoint_disable,
60         .get_frame_number =     uhci_hcd_get_frame_number,
61
62         .hub_status_data =      uhci_hub_status_data,
63         .hub_control =          uhci_hub_control,
64 };
65
66 static int uhci_hcd_platform_probe(struct platform_device *pdev)
67 {
68         struct device_node *np = pdev->dev.of_node;
69         struct usb_hcd *hcd;
70         struct uhci_hcd *uhci;
71         struct resource *res;
72         int ret;
73
74         if (usb_disabled())
75                 return -ENODEV;
76
77         /*
78          * Right now device-tree probed devices don't get dma_mask set.
79          * Since shared usb code relies on it, set it here for now.
80          * Once we have dma capability bindings this can go away.
81          */
82         ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
83         if (ret)
84                 return ret;
85
86         hcd = usb_create_hcd(&uhci_platform_hc_driver, &pdev->dev,
87                         pdev->name);
88         if (!hcd)
89                 return -ENOMEM;
90
91         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
92         hcd->regs = devm_ioremap_resource(&pdev->dev, res);
93         if (IS_ERR(hcd->regs)) {
94                 ret = PTR_ERR(hcd->regs);
95                 goto err_rmr;
96         }
97         hcd->rsrc_start = res->start;
98         hcd->rsrc_len = resource_size(res);
99
100         uhci = hcd_to_uhci(hcd);
101
102         uhci->regs = hcd->regs;
103
104         /* Grab some things from the device-tree */
105         if (np) {
106                 u32 num_ports;
107
108                 if (of_property_read_u32(np, "#ports", &num_ports) == 0) {
109                         uhci->rh_numports = num_ports;
110                         dev_info(&pdev->dev,
111                                 "Detected %d ports from device-tree\n",
112                                 num_ports);
113                 }
114                 if (of_device_is_compatible(np, "aspeed,ast2400-uhci") ||
115                     of_device_is_compatible(np, "aspeed,ast2500-uhci")) {
116                         uhci->is_aspeed = 1;
117                         dev_info(&pdev->dev,
118                                  "Enabled Aspeed implementation workarounds\n");
119                 }
120         }
121         ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED);
122         if (ret)
123                 goto err_rmr;
124
125         device_wakeup_enable(hcd->self.controller);
126         return 0;
127
128 err_rmr:
129         usb_put_hcd(hcd);
130
131         return ret;
132 }
133
134 static int uhci_hcd_platform_remove(struct platform_device *pdev)
135 {
136         struct usb_hcd *hcd = platform_get_drvdata(pdev);
137
138         usb_remove_hcd(hcd);
139         usb_put_hcd(hcd);
140
141         return 0;
142 }
143
144 /* Make sure the controller is quiescent and that we're not using it
145  * any more.  This is mainly for the benefit of programs which, like kexec,
146  * expect the hardware to be idle: not doing DMA or generating IRQs.
147  *
148  * This routine may be called in a damaged or failing kernel.  Hence we
149  * do not acquire the spinlock before shutting down the controller.
150  */
151 static void uhci_hcd_platform_shutdown(struct platform_device *op)
152 {
153         struct usb_hcd *hcd = platform_get_drvdata(op);
154
155         uhci_hc_died(hcd_to_uhci(hcd));
156 }
157
158 static const struct of_device_id platform_uhci_ids[] = {
159         { .compatible = "generic-uhci", },
160         { .compatible = "platform-uhci", },
161         {}
162 };
163 MODULE_DEVICE_TABLE(of, platform_uhci_ids);
164
165 static struct platform_driver uhci_platform_driver = {
166         .probe          = uhci_hcd_platform_probe,
167         .remove         = uhci_hcd_platform_remove,
168         .shutdown       = uhci_hcd_platform_shutdown,
169         .driver = {
170                 .name = "platform-uhci",
171                 .of_match_table = platform_uhci_ids,
172         },
173 };