diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2012-05-09 13:58:14 -0400 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2012-07-18 16:40:29 -0400 |
commit | 47fc28bff82a4dd5f6b41c97e335d10fc78a8e9a (patch) | |
tree | 829b845956f9f5f6b2997424fa1d6d72244b0d1b /drivers | |
parent | d1cc1732cc62034542b042a4506d7c5043bc5e5e (diff) |
usb: add host support for the tilegx architecture
This change adds OHCI and EHCI support for the tilegx's on-chip
USB hardware.
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ehci-tilegx.c | 214 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ohci-tilegx.c | 203 |
4 files changed, 427 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 800be38c78b4..1d9401e0990a 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -1349,6 +1349,11 @@ MODULE_LICENSE ("GPL"); | |||
1349 | #define PLATFORM_DRIVER ehci_msm_driver | 1349 | #define PLATFORM_DRIVER ehci_msm_driver |
1350 | #endif | 1350 | #endif |
1351 | 1351 | ||
1352 | #ifdef CONFIG_TILE_USB | ||
1353 | #include "ehci-tilegx.c" | ||
1354 | #define PLATFORM_DRIVER ehci_hcd_tilegx_driver | ||
1355 | #endif | ||
1356 | |||
1352 | #ifdef CONFIG_USB_EHCI_HCD_PMC_MSP | 1357 | #ifdef CONFIG_USB_EHCI_HCD_PMC_MSP |
1353 | #include "ehci-pmcmsp.c" | 1358 | #include "ehci-pmcmsp.c" |
1354 | #define PLATFORM_DRIVER ehci_hcd_msp_driver | 1359 | #define PLATFORM_DRIVER ehci_hcd_msp_driver |
diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c new file mode 100644 index 000000000000..1d215cdb9dea --- /dev/null +++ b/drivers/usb/host/ehci-tilegx.c | |||
@@ -0,0 +1,214 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Tilera Corporation. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | /* | ||
16 | * Tilera TILE-Gx USB EHCI host controller driver. | ||
17 | */ | ||
18 | |||
19 | #include <linux/irq.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/usb/tilegx.h> | ||
22 | #include <linux/usb.h> | ||
23 | |||
24 | #include <asm/homecache.h> | ||
25 | |||
26 | #include <gxio/iorpc_usb_host.h> | ||
27 | #include <gxio/usb_host.h> | ||
28 | |||
29 | static void tilegx_start_ehc(void) | ||
30 | { | ||
31 | } | ||
32 | |||
33 | static void tilegx_stop_ehc(void) | ||
34 | { | ||
35 | } | ||
36 | |||
37 | static int tilegx_ehci_setup(struct usb_hcd *hcd) | ||
38 | { | ||
39 | int ret = ehci_init(hcd); | ||
40 | |||
41 | /* | ||
42 | * Some drivers do: | ||
43 | * | ||
44 | * struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
45 | * ehci->need_io_watchdog = 0; | ||
46 | * | ||
47 | * here, but since this is a new driver we're going to leave the | ||
48 | * watchdog enabled. Later we may try to turn it off and see | ||
49 | * whether we run into any problems. | ||
50 | */ | ||
51 | |||
52 | return ret; | ||
53 | } | ||
54 | |||
55 | static const struct hc_driver ehci_tilegx_hc_driver = { | ||
56 | .description = hcd_name, | ||
57 | .product_desc = "Tile-Gx EHCI", | ||
58 | .hcd_priv_size = sizeof(struct ehci_hcd), | ||
59 | |||
60 | /* | ||
61 | * Generic hardware linkage. | ||
62 | */ | ||
63 | .irq = ehci_irq, | ||
64 | .flags = HCD_MEMORY | HCD_USB2, | ||
65 | |||
66 | /* | ||
67 | * Basic lifecycle operations. | ||
68 | */ | ||
69 | .reset = tilegx_ehci_setup, | ||
70 | .start = ehci_run, | ||
71 | .stop = ehci_stop, | ||
72 | .shutdown = ehci_shutdown, | ||
73 | |||
74 | /* | ||
75 | * Managing I/O requests and associated device resources. | ||
76 | */ | ||
77 | .urb_enqueue = ehci_urb_enqueue, | ||
78 | .urb_dequeue = ehci_urb_dequeue, | ||
79 | .endpoint_disable = ehci_endpoint_disable, | ||
80 | .endpoint_reset = ehci_endpoint_reset, | ||
81 | |||
82 | /* | ||
83 | * Scheduling support. | ||
84 | */ | ||
85 | .get_frame_number = ehci_get_frame, | ||
86 | |||
87 | /* | ||
88 | * Root hub support. | ||
89 | */ | ||
90 | .hub_status_data = ehci_hub_status_data, | ||
91 | .hub_control = ehci_hub_control, | ||
92 | .bus_suspend = ehci_bus_suspend, | ||
93 | .bus_resume = ehci_bus_resume, | ||
94 | .relinquish_port = ehci_relinquish_port, | ||
95 | .port_handed_over = ehci_port_handed_over, | ||
96 | |||
97 | .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, | ||
98 | }; | ||
99 | |||
100 | static int ehci_hcd_tilegx_drv_probe(struct platform_device *pdev) | ||
101 | { | ||
102 | struct usb_hcd *hcd; | ||
103 | struct ehci_hcd *ehci; | ||
104 | struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; | ||
105 | pte_t pte = { 0 }; | ||
106 | int my_cpu = smp_processor_id(); | ||
107 | int ret; | ||
108 | |||
109 | if (usb_disabled()) | ||
110 | return -ENODEV; | ||
111 | |||
112 | /* | ||
113 | * Try to initialize our GXIO context; if we can't, the device | ||
114 | * doesn't exist. | ||
115 | */ | ||
116 | if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 1) != 0) | ||
117 | return -ENXIO; | ||
118 | |||
119 | hcd = usb_create_hcd(&ehci_tilegx_hc_driver, &pdev->dev, | ||
120 | dev_name(&pdev->dev)); | ||
121 | if (!hcd) | ||
122 | return -ENOMEM; | ||
123 | |||
124 | /* | ||
125 | * We don't use rsrc_start to map in our registers, but seems like | ||
126 | * we ought to set it to something, so we use the register VA. | ||
127 | */ | ||
128 | hcd->rsrc_start = | ||
129 | (ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx); | ||
130 | hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx); | ||
131 | hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx); | ||
132 | |||
133 | tilegx_start_ehc(); | ||
134 | |||
135 | ehci = hcd_to_ehci(hcd); | ||
136 | ehci->caps = hcd->regs; | ||
137 | ehci->regs = | ||
138 | hcd->regs + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); | ||
139 | /* cache this readonly data; minimize chip reads */ | ||
140 | ehci->hcs_params = readl(&ehci->caps->hcs_params); | ||
141 | |||
142 | /* Create our IRQs and register them. */ | ||
143 | pdata->irq = create_irq(); | ||
144 | if (pdata->irq < 0) { | ||
145 | ret = -ENXIO; | ||
146 | goto err_no_irq; | ||
147 | } | ||
148 | |||
149 | tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU); | ||
150 | |||
151 | /* Configure interrupts. */ | ||
152 | ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx, | ||
153 | cpu_x(my_cpu), cpu_y(my_cpu), | ||
154 | KERNEL_PL, pdata->irq); | ||
155 | if (ret) { | ||
156 | ret = -ENXIO; | ||
157 | goto err_have_irq; | ||
158 | } | ||
159 | |||
160 | /* Register all of our memory. */ | ||
161 | pte = pte_set_home(pte, PAGE_HOME_HASH); | ||
162 | ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0); | ||
163 | if (ret) { | ||
164 | ret = -ENXIO; | ||
165 | goto err_have_irq; | ||
166 | } | ||
167 | |||
168 | ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED); | ||
169 | if (ret == 0) { | ||
170 | platform_set_drvdata(pdev, hcd); | ||
171 | return ret; | ||
172 | } | ||
173 | |||
174 | err_have_irq: | ||
175 | destroy_irq(pdata->irq); | ||
176 | err_no_irq: | ||
177 | tilegx_stop_ehc(); | ||
178 | usb_put_hcd(hcd); | ||
179 | gxio_usb_host_destroy(&pdata->usb_ctx); | ||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | static int ehci_hcd_tilegx_drv_remove(struct platform_device *pdev) | ||
184 | { | ||
185 | struct usb_hcd *hcd = platform_get_drvdata(pdev); | ||
186 | struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; | ||
187 | |||
188 | usb_remove_hcd(hcd); | ||
189 | usb_put_hcd(hcd); | ||
190 | tilegx_stop_ehc(); | ||
191 | gxio_usb_host_destroy(&pdata->usb_ctx); | ||
192 | destroy_irq(pdata->irq); | ||
193 | platform_set_drvdata(pdev, NULL); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static void ehci_hcd_tilegx_drv_shutdown(struct platform_device *pdev) | ||
199 | { | ||
200 | usb_hcd_platform_shutdown(pdev); | ||
201 | ehci_hcd_tilegx_drv_remove(pdev); | ||
202 | } | ||
203 | |||
204 | static struct platform_driver ehci_hcd_tilegx_driver = { | ||
205 | .probe = ehci_hcd_tilegx_drv_probe, | ||
206 | .remove = ehci_hcd_tilegx_drv_remove, | ||
207 | .shutdown = ehci_hcd_tilegx_drv_shutdown, | ||
208 | .driver = { | ||
209 | .name = "tilegx-ehci", | ||
210 | .owner = THIS_MODULE, | ||
211 | } | ||
212 | }; | ||
213 | |||
214 | MODULE_ALIAS("platform:tilegx-ehci"); | ||
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index e0adf5c0cf55..2b1e8d84c873 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c | |||
@@ -1100,6 +1100,11 @@ MODULE_LICENSE ("GPL"); | |||
1100 | #define PLATFORM_DRIVER ohci_octeon_driver | 1100 | #define PLATFORM_DRIVER ohci_octeon_driver |
1101 | #endif | 1101 | #endif |
1102 | 1102 | ||
1103 | #ifdef CONFIG_TILE_USB | ||
1104 | #include "ohci-tilegx.c" | ||
1105 | #define PLATFORM_DRIVER ohci_hcd_tilegx_driver | ||
1106 | #endif | ||
1107 | |||
1103 | #ifdef CONFIG_USB_CNS3XXX_OHCI | 1108 | #ifdef CONFIG_USB_CNS3XXX_OHCI |
1104 | #include "ohci-cns3xxx.c" | 1109 | #include "ohci-cns3xxx.c" |
1105 | #define PLATFORM_DRIVER ohci_hcd_cns3xxx_driver | 1110 | #define PLATFORM_DRIVER ohci_hcd_cns3xxx_driver |
diff --git a/drivers/usb/host/ohci-tilegx.c b/drivers/usb/host/ohci-tilegx.c new file mode 100644 index 000000000000..1ae7b28a71c2 --- /dev/null +++ b/drivers/usb/host/ohci-tilegx.c | |||
@@ -0,0 +1,203 @@ | |||
1 | /* | ||
2 | * Copyright 2012 Tilera Corporation. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | /* | ||
16 | * Tilera TILE-Gx USB OHCI host controller driver. | ||
17 | */ | ||
18 | |||
19 | #include <linux/irq.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/usb/tilegx.h> | ||
22 | #include <linux/usb.h> | ||
23 | |||
24 | #include <asm/homecache.h> | ||
25 | |||
26 | #include <gxio/iorpc_usb_host.h> | ||
27 | #include <gxio/usb_host.h> | ||
28 | |||
29 | static void tilegx_start_ohc(void) | ||
30 | { | ||
31 | } | ||
32 | |||
33 | static void tilegx_stop_ohc(void) | ||
34 | { | ||
35 | } | ||
36 | |||
37 | static int tilegx_ohci_start(struct usb_hcd *hcd) | ||
38 | { | ||
39 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
40 | int ret; | ||
41 | |||
42 | ret = ohci_init(ohci); | ||
43 | if (ret < 0) | ||
44 | return ret; | ||
45 | |||
46 | ret = ohci_run(ohci); | ||
47 | if (ret < 0) { | ||
48 | dev_err(hcd->self.controller, "can't start %s\n", | ||
49 | hcd->self.bus_name); | ||
50 | ohci_stop(hcd); | ||
51 | return ret; | ||
52 | } | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | static const struct hc_driver ohci_tilegx_hc_driver = { | ||
58 | .description = hcd_name, | ||
59 | .product_desc = "Tile-Gx OHCI", | ||
60 | .hcd_priv_size = sizeof(struct ohci_hcd), | ||
61 | |||
62 | /* | ||
63 | * Generic hardware linkage. | ||
64 | */ | ||
65 | .irq = ohci_irq, | ||
66 | .flags = HCD_MEMORY | HCD_LOCAL_MEM | HCD_USB11, | ||
67 | |||
68 | /* | ||
69 | * Basic lifecycle operations. | ||
70 | */ | ||
71 | .start = tilegx_ohci_start, | ||
72 | .stop = ohci_stop, | ||
73 | .shutdown = ohci_shutdown, | ||
74 | |||
75 | /* | ||
76 | * Managing I/O requests and associated device resources. | ||
77 | */ | ||
78 | .urb_enqueue = ohci_urb_enqueue, | ||
79 | .urb_dequeue = ohci_urb_dequeue, | ||
80 | .endpoint_disable = ohci_endpoint_disable, | ||
81 | |||
82 | /* | ||
83 | * Scheduling support. | ||
84 | */ | ||
85 | .get_frame_number = ohci_get_frame, | ||
86 | |||
87 | /* | ||
88 | * Root hub support. | ||
89 | */ | ||
90 | .hub_status_data = ohci_hub_status_data, | ||
91 | .hub_control = ohci_hub_control, | ||
92 | .start_port_reset = ohci_start_port_reset, | ||
93 | }; | ||
94 | |||
95 | static int ohci_hcd_tilegx_drv_probe(struct platform_device *pdev) | ||
96 | { | ||
97 | struct usb_hcd *hcd; | ||
98 | struct tilegx_usb_platform_data *pdata = pdev->dev.platform_data; | ||
99 | pte_t pte = { 0 }; | ||
100 | int my_cpu = smp_processor_id(); | ||
101 | int ret; | ||
102 | |||
103 | if (usb_disabled()) | ||
104 | return -ENODEV; | ||
105 | |||
106 | /* | ||
107 | * Try to initialize our GXIO context; if we can't, the device | ||
108 | * doesn't exist. | ||
109 | */ | ||
110 | if (gxio_usb_host_init(&pdata->usb_ctx, pdata->dev_index, 0) != 0) | ||
111 | return -ENXIO; | ||
112 | |||
113 | hcd = usb_create_hcd(&ohci_tilegx_hc_driver, &pdev->dev, | ||
114 | dev_name(&pdev->dev)); | ||
115 | if (!hcd) | ||
116 | return -ENOMEM; | ||
117 | |||
118 | /* | ||
119 | * We don't use rsrc_start to map in our registers, but seems like | ||
120 | * we ought to set it to something, so we use the register VA. | ||
121 | */ | ||
122 | hcd->rsrc_start = | ||
123 | (ulong) gxio_usb_host_get_reg_start(&pdata->usb_ctx); | ||
124 | hcd->rsrc_len = gxio_usb_host_get_reg_len(&pdata->usb_ctx); | ||
125 | hcd->regs = gxio_usb_host_get_reg_start(&pdata->usb_ctx); | ||
126 | |||
127 | tilegx_start_ohc(); | ||
128 | |||
129 | /* Create our IRQs and register them. */ | ||
130 | pdata->irq = create_irq(); | ||
131 | if (pdata->irq < 0) { | ||
132 | ret = -ENXIO; | ||
133 | goto err_no_irq; | ||
134 | } | ||
135 | |||
136 | tile_irq_activate(pdata->irq, TILE_IRQ_PERCPU); | ||
137 | |||
138 | /* Configure interrupts. */ | ||
139 | ret = gxio_usb_host_cfg_interrupt(&pdata->usb_ctx, | ||
140 | cpu_x(my_cpu), cpu_y(my_cpu), | ||
141 | KERNEL_PL, pdata->irq); | ||
142 | if (ret) { | ||
143 | ret = -ENXIO; | ||
144 | goto err_have_irq; | ||
145 | } | ||
146 | |||
147 | /* Register all of our memory. */ | ||
148 | pte = pte_set_home(pte, PAGE_HOME_HASH); | ||
149 | ret = gxio_usb_host_register_client_memory(&pdata->usb_ctx, pte, 0); | ||
150 | if (ret) { | ||
151 | ret = -ENXIO; | ||
152 | goto err_have_irq; | ||
153 | } | ||
154 | |||
155 | ohci_hcd_init(hcd_to_ohci(hcd)); | ||
156 | |||
157 | ret = usb_add_hcd(hcd, pdata->irq, IRQF_SHARED); | ||
158 | if (ret == 0) { | ||
159 | platform_set_drvdata(pdev, hcd); | ||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | err_have_irq: | ||
164 | destroy_irq(pdata->irq); | ||
165 | err_no_irq: | ||
166 | tilegx_stop_ohc(); | ||
167 | usb_put_hcd(hcd); | ||
168 | gxio_usb_host_destroy(&pdata->usb_ctx); | ||
169 | return ret; | ||
170 | } | ||
171 | |||
172 | static int ohci_hcd_tilegx_drv_remove(struct platform_device *pdev) | ||
173 | { | ||
174 | struct usb_hcd *hcd = platform_get_drvdata(pdev); | ||
175 | struct tilegx_usb_platform_data* pdata = pdev->dev.platform_data; | ||
176 | |||
177 | usb_remove_hcd(hcd); | ||
178 | usb_put_hcd(hcd); | ||
179 | tilegx_stop_ohc(); | ||
180 | gxio_usb_host_destroy(&pdata->usb_ctx); | ||
181 | destroy_irq(pdata->irq); | ||
182 | platform_set_drvdata(pdev, NULL); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static void ohci_hcd_tilegx_drv_shutdown(struct platform_device *pdev) | ||
188 | { | ||
189 | usb_hcd_platform_shutdown(pdev); | ||
190 | ohci_hcd_tilegx_drv_remove(pdev); | ||
191 | } | ||
192 | |||
193 | static struct platform_driver ohci_hcd_tilegx_driver = { | ||
194 | .probe = ohci_hcd_tilegx_drv_probe, | ||
195 | .remove = ohci_hcd_tilegx_drv_remove, | ||
196 | .shutdown = ohci_hcd_tilegx_drv_shutdown, | ||
197 | .driver = { | ||
198 | .name = "tilegx-ohci", | ||
199 | .owner = THIS_MODULE, | ||
200 | } | ||
201 | }; | ||
202 | |||
203 | MODULE_ALIAS("platform:tilegx-ohci"); | ||