diff options
Diffstat (limited to 'drivers/usb/gadget/s3c-hsudc.c')
-rw-r--r-- | drivers/usb/gadget/s3c-hsudc.c | 136 |
1 files changed, 70 insertions, 66 deletions
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 20a553b46aed..df8661d266cb 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c | |||
@@ -28,9 +28,10 @@ | |||
28 | #include <linux/usb/gadget.h> | 28 | #include <linux/usb/gadget.h> |
29 | #include <linux/usb/otg.h> | 29 | #include <linux/usb/otg.h> |
30 | #include <linux/prefetch.h> | 30 | #include <linux/prefetch.h> |
31 | #include <linux/platform_data/s3c-hsudc.h> | ||
32 | #include <linux/regulator/consumer.h> | ||
31 | 33 | ||
32 | #include <mach/regs-s3c2443-clock.h> | 34 | #include <mach/regs-s3c2443-clock.h> |
33 | #include <plat/udc.h> | ||
34 | 35 | ||
35 | #define S3C_HSUDC_REG(x) (x) | 36 | #define S3C_HSUDC_REG(x) (x) |
36 | 37 | ||
@@ -87,6 +88,12 @@ | |||
87 | #define DATA_STATE_XMIT (1) | 88 | #define DATA_STATE_XMIT (1) |
88 | #define DATA_STATE_RECV (2) | 89 | #define DATA_STATE_RECV (2) |
89 | 90 | ||
91 | static const char * const s3c_hsudc_supply_names[] = { | ||
92 | "vdda", /* analog phy supply, 3.3V */ | ||
93 | "vddi", /* digital phy supply, 1.2V */ | ||
94 | "vddosc", /* oscillator supply, 1.8V - 3.3V */ | ||
95 | }; | ||
96 | |||
90 | /** | 97 | /** |
91 | * struct s3c_hsudc_ep - Endpoint representation used by driver. | 98 | * struct s3c_hsudc_ep - Endpoint representation used by driver. |
92 | * @ep: USB gadget layer representation of device endpoint. | 99 | * @ep: USB gadget layer representation of device endpoint. |
@@ -139,6 +146,7 @@ struct s3c_hsudc { | |||
139 | struct device *dev; | 146 | struct device *dev; |
140 | struct s3c24xx_hsudc_platdata *pd; | 147 | struct s3c24xx_hsudc_platdata *pd; |
141 | struct otg_transceiver *transceiver; | 148 | struct otg_transceiver *transceiver; |
149 | struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsudc_supply_names)]; | ||
142 | spinlock_t lock; | 150 | spinlock_t lock; |
143 | void __iomem *regs; | 151 | void __iomem *regs; |
144 | struct resource *mem_rsrc; | 152 | struct resource *mem_rsrc; |
@@ -153,7 +161,6 @@ struct s3c_hsudc { | |||
153 | #define ep_index(_ep) ((_ep)->bEndpointAddress & \ | 161 | #define ep_index(_ep) ((_ep)->bEndpointAddress & \ |
154 | USB_ENDPOINT_NUMBER_MASK) | 162 | USB_ENDPOINT_NUMBER_MASK) |
155 | 163 | ||
156 | static struct s3c_hsudc *the_controller; | ||
157 | static const char driver_name[] = "s3c-udc"; | 164 | static const char driver_name[] = "s3c-udc"; |
158 | static const char ep0name[] = "ep0-control"; | 165 | static const char ep0name[] = "ep0-control"; |
159 | 166 | ||
@@ -282,8 +289,7 @@ static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status) | |||
282 | * All the endpoints are stopped and any pending transfer requests if any on | 289 | * All the endpoints are stopped and any pending transfer requests if any on |
283 | * the endpoint are terminated. | 290 | * the endpoint are terminated. |
284 | */ | 291 | */ |
285 | static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc, | 292 | static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc) |
286 | struct usb_gadget_driver *driver) | ||
287 | { | 293 | { |
288 | struct s3c_hsudc_ep *hsep; | 294 | struct s3c_hsudc_ep *hsep; |
289 | int epnum; | 295 | int epnum; |
@@ -295,10 +301,6 @@ static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc, | |||
295 | hsep->stopped = 1; | 301 | hsep->stopped = 1; |
296 | s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN); | 302 | s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN); |
297 | } | 303 | } |
298 | |||
299 | spin_unlock(&hsudc->lock); | ||
300 | driver->disconnect(&hsudc->gadget); | ||
301 | spin_lock(&hsudc->lock); | ||
302 | } | 304 | } |
303 | 305 | ||
304 | /** | 306 | /** |
@@ -1135,16 +1137,15 @@ static irqreturn_t s3c_hsudc_irq(int irq, void *_dev) | |||
1135 | return IRQ_HANDLED; | 1137 | return IRQ_HANDLED; |
1136 | } | 1138 | } |
1137 | 1139 | ||
1138 | static int s3c_hsudc_start(struct usb_gadget_driver *driver, | 1140 | static int s3c_hsudc_start(struct usb_gadget *gadget, |
1139 | int (*bind)(struct usb_gadget *)) | 1141 | struct usb_gadget_driver *driver) |
1140 | { | 1142 | { |
1141 | struct s3c_hsudc *hsudc = the_controller; | 1143 | struct s3c_hsudc *hsudc = to_hsudc(gadget); |
1142 | int ret; | 1144 | int ret; |
1143 | 1145 | ||
1144 | if (!driver | 1146 | if (!driver |
1145 | || driver->speed < USB_SPEED_FULL | 1147 | || driver->max_speed < USB_SPEED_FULL |
1146 | || !bind | 1148 | || !driver->setup) |
1147 | || !driver->unbind || !driver->disconnect || !driver->setup) | ||
1148 | return -EINVAL; | 1149 | return -EINVAL; |
1149 | 1150 | ||
1150 | if (!hsudc) | 1151 | if (!hsudc) |
@@ -1155,21 +1156,12 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver, | |||
1155 | 1156 | ||
1156 | hsudc->driver = driver; | 1157 | hsudc->driver = driver; |
1157 | hsudc->gadget.dev.driver = &driver->driver; | 1158 | hsudc->gadget.dev.driver = &driver->driver; |
1158 | hsudc->gadget.speed = USB_SPEED_UNKNOWN; | ||
1159 | ret = device_add(&hsudc->gadget.dev); | ||
1160 | if (ret) { | ||
1161 | dev_err(hsudc->dev, "failed to probe gadget device"); | ||
1162 | return ret; | ||
1163 | } | ||
1164 | 1159 | ||
1165 | ret = bind(&hsudc->gadget); | 1160 | ret = regulator_bulk_enable(ARRAY_SIZE(hsudc->supplies), |
1166 | if (ret) { | 1161 | hsudc->supplies); |
1167 | dev_err(hsudc->dev, "%s: bind failed\n", hsudc->gadget.name); | 1162 | if (ret != 0) { |
1168 | device_del(&hsudc->gadget.dev); | 1163 | dev_err(hsudc->dev, "failed to enable supplies: %d\n", ret); |
1169 | 1164 | goto err_supplies; | |
1170 | hsudc->driver = NULL; | ||
1171 | hsudc->gadget.dev.driver = NULL; | ||
1172 | return ret; | ||
1173 | } | 1165 | } |
1174 | 1166 | ||
1175 | /* connect to bus through transceiver */ | 1167 | /* connect to bus through transceiver */ |
@@ -1178,13 +1170,7 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver, | |||
1178 | if (ret) { | 1170 | if (ret) { |
1179 | dev_err(hsudc->dev, "%s: can't bind to transceiver\n", | 1171 | dev_err(hsudc->dev, "%s: can't bind to transceiver\n", |
1180 | hsudc->gadget.name); | 1172 | hsudc->gadget.name); |
1181 | driver->unbind(&hsudc->gadget); | 1173 | goto err_otg; |
1182 | |||
1183 | device_del(&hsudc->gadget.dev); | ||
1184 | |||
1185 | hsudc->driver = NULL; | ||
1186 | hsudc->gadget.dev.driver = NULL; | ||
1187 | return ret; | ||
1188 | } | 1174 | } |
1189 | } | 1175 | } |
1190 | 1176 | ||
@@ -1197,34 +1183,43 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver, | |||
1197 | hsudc->pd->gpio_init(); | 1183 | hsudc->pd->gpio_init(); |
1198 | 1184 | ||
1199 | return 0; | 1185 | return 0; |
1186 | err_otg: | ||
1187 | regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); | ||
1188 | err_supplies: | ||
1189 | hsudc->driver = NULL; | ||
1190 | hsudc->gadget.dev.driver = NULL; | ||
1191 | return ret; | ||
1200 | } | 1192 | } |
1201 | 1193 | ||
1202 | static int s3c_hsudc_stop(struct usb_gadget_driver *driver) | 1194 | static int s3c_hsudc_stop(struct usb_gadget *gadget, |
1195 | struct usb_gadget_driver *driver) | ||
1203 | { | 1196 | { |
1204 | struct s3c_hsudc *hsudc = the_controller; | 1197 | struct s3c_hsudc *hsudc = to_hsudc(gadget); |
1205 | unsigned long flags; | 1198 | unsigned long flags; |
1206 | 1199 | ||
1207 | if (!hsudc) | 1200 | if (!hsudc) |
1208 | return -ENODEV; | 1201 | return -ENODEV; |
1209 | 1202 | ||
1210 | if (!driver || driver != hsudc->driver || !driver->unbind) | 1203 | if (!driver || driver != hsudc->driver) |
1211 | return -EINVAL; | 1204 | return -EINVAL; |
1212 | 1205 | ||
1213 | spin_lock_irqsave(&hsudc->lock, flags); | 1206 | spin_lock_irqsave(&hsudc->lock, flags); |
1214 | hsudc->driver = 0; | 1207 | hsudc->driver = NULL; |
1208 | hsudc->gadget.dev.driver = NULL; | ||
1209 | hsudc->gadget.speed = USB_SPEED_UNKNOWN; | ||
1215 | s3c_hsudc_uninit_phy(); | 1210 | s3c_hsudc_uninit_phy(); |
1216 | if (hsudc->pd->gpio_uninit) | 1211 | if (hsudc->pd->gpio_uninit) |
1217 | hsudc->pd->gpio_uninit(); | 1212 | hsudc->pd->gpio_uninit(); |
1218 | s3c_hsudc_stop_activity(hsudc, driver); | 1213 | s3c_hsudc_stop_activity(hsudc); |
1219 | spin_unlock_irqrestore(&hsudc->lock, flags); | 1214 | spin_unlock_irqrestore(&hsudc->lock, flags); |
1220 | 1215 | ||
1221 | if (hsudc->transceiver) | 1216 | if (hsudc->transceiver) |
1222 | (void) otg_set_peripheral(hsudc->transceiver, NULL); | 1217 | (void) otg_set_peripheral(hsudc->transceiver, NULL); |
1223 | 1218 | ||
1224 | driver->unbind(&hsudc->gadget); | ||
1225 | device_del(&hsudc->gadget.dev); | ||
1226 | disable_irq(hsudc->irq); | 1219 | disable_irq(hsudc->irq); |
1227 | 1220 | ||
1221 | regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); | ||
1222 | |||
1228 | dev_info(hsudc->dev, "unregistered gadget driver '%s'\n", | 1223 | dev_info(hsudc->dev, "unregistered gadget driver '%s'\n", |
1229 | driver->driver.name); | 1224 | driver->driver.name); |
1230 | return 0; | 1225 | return 0; |
@@ -1242,7 +1237,7 @@ static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget) | |||
1242 | 1237 | ||
1243 | static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA) | 1238 | static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA) |
1244 | { | 1239 | { |
1245 | struct s3c_hsudc *hsudc = the_controller; | 1240 | struct s3c_hsudc *hsudc = to_hsudc(gadget); |
1246 | 1241 | ||
1247 | if (!hsudc) | 1242 | if (!hsudc) |
1248 | return -ENODEV; | 1243 | return -ENODEV; |
@@ -1255,18 +1250,18 @@ static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA) | |||
1255 | 1250 | ||
1256 | static struct usb_gadget_ops s3c_hsudc_gadget_ops = { | 1251 | static struct usb_gadget_ops s3c_hsudc_gadget_ops = { |
1257 | .get_frame = s3c_hsudc_gadget_getframe, | 1252 | .get_frame = s3c_hsudc_gadget_getframe, |
1258 | .start = s3c_hsudc_start, | 1253 | .udc_start = s3c_hsudc_start, |
1259 | .stop = s3c_hsudc_stop, | 1254 | .udc_stop = s3c_hsudc_stop, |
1260 | .vbus_draw = s3c_hsudc_vbus_draw, | 1255 | .vbus_draw = s3c_hsudc_vbus_draw, |
1261 | }; | 1256 | }; |
1262 | 1257 | ||
1263 | static int s3c_hsudc_probe(struct platform_device *pdev) | 1258 | static int __devinit s3c_hsudc_probe(struct platform_device *pdev) |
1264 | { | 1259 | { |
1265 | struct device *dev = &pdev->dev; | 1260 | struct device *dev = &pdev->dev; |
1266 | struct resource *res; | 1261 | struct resource *res; |
1267 | struct s3c_hsudc *hsudc; | 1262 | struct s3c_hsudc *hsudc; |
1268 | struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data; | 1263 | struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data; |
1269 | int ret; | 1264 | int ret, i; |
1270 | 1265 | ||
1271 | hsudc = kzalloc(sizeof(struct s3c_hsudc) + | 1266 | hsudc = kzalloc(sizeof(struct s3c_hsudc) + |
1272 | sizeof(struct s3c_hsudc_ep) * pd->epnum, | 1267 | sizeof(struct s3c_hsudc_ep) * pd->epnum, |
@@ -1276,13 +1271,22 @@ static int s3c_hsudc_probe(struct platform_device *pdev) | |||
1276 | return -ENOMEM; | 1271 | return -ENOMEM; |
1277 | } | 1272 | } |
1278 | 1273 | ||
1279 | the_controller = hsudc; | ||
1280 | platform_set_drvdata(pdev, dev); | 1274 | platform_set_drvdata(pdev, dev); |
1281 | hsudc->dev = dev; | 1275 | hsudc->dev = dev; |
1282 | hsudc->pd = pdev->dev.platform_data; | 1276 | hsudc->pd = pdev->dev.platform_data; |
1283 | 1277 | ||
1284 | hsudc->transceiver = otg_get_transceiver(); | 1278 | hsudc->transceiver = otg_get_transceiver(); |
1285 | 1279 | ||
1280 | for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++) | ||
1281 | hsudc->supplies[i].supply = s3c_hsudc_supply_names[i]; | ||
1282 | |||
1283 | ret = regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies), | ||
1284 | hsudc->supplies); | ||
1285 | if (ret != 0) { | ||
1286 | dev_err(dev, "failed to request supplies: %d\n", ret); | ||
1287 | goto err_supplies; | ||
1288 | } | ||
1289 | |||
1286 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1290 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1287 | if (!res) { | 1291 | if (!res) { |
1288 | dev_err(dev, "unable to obtain driver resource data\n"); | 1292 | dev_err(dev, "unable to obtain driver resource data\n"); |
@@ -1307,10 +1311,9 @@ static int s3c_hsudc_probe(struct platform_device *pdev) | |||
1307 | 1311 | ||
1308 | spin_lock_init(&hsudc->lock); | 1312 | spin_lock_init(&hsudc->lock); |
1309 | 1313 | ||
1310 | device_initialize(&hsudc->gadget.dev); | ||
1311 | dev_set_name(&hsudc->gadget.dev, "gadget"); | 1314 | dev_set_name(&hsudc->gadget.dev, "gadget"); |
1312 | 1315 | ||
1313 | hsudc->gadget.is_dualspeed = 1; | 1316 | hsudc->gadget.max_speed = USB_SPEED_HIGH; |
1314 | hsudc->gadget.ops = &s3c_hsudc_gadget_ops; | 1317 | hsudc->gadget.ops = &s3c_hsudc_gadget_ops; |
1315 | hsudc->gadget.name = dev_name(dev); | 1318 | hsudc->gadget.name = dev_name(dev); |
1316 | hsudc->gadget.dev.parent = dev; | 1319 | hsudc->gadget.dev.parent = dev; |
@@ -1319,6 +1322,7 @@ static int s3c_hsudc_probe(struct platform_device *pdev) | |||
1319 | 1322 | ||
1320 | hsudc->gadget.is_otg = 0; | 1323 | hsudc->gadget.is_otg = 0; |
1321 | hsudc->gadget.is_a_peripheral = 0; | 1324 | hsudc->gadget.is_a_peripheral = 0; |
1325 | hsudc->gadget.speed = USB_SPEED_UNKNOWN; | ||
1322 | 1326 | ||
1323 | s3c_hsudc_setup_ep(hsudc); | 1327 | s3c_hsudc_setup_ep(hsudc); |
1324 | 1328 | ||
@@ -1348,12 +1352,20 @@ static int s3c_hsudc_probe(struct platform_device *pdev) | |||
1348 | disable_irq(hsudc->irq); | 1352 | disable_irq(hsudc->irq); |
1349 | local_irq_enable(); | 1353 | local_irq_enable(); |
1350 | 1354 | ||
1355 | ret = device_register(&hsudc->gadget.dev); | ||
1356 | if (ret) { | ||
1357 | put_device(&hsudc->gadget.dev); | ||
1358 | goto err_add_device; | ||
1359 | } | ||
1360 | |||
1351 | ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget); | 1361 | ret = usb_add_gadget_udc(&pdev->dev, &hsudc->gadget); |
1352 | if (ret) | 1362 | if (ret) |
1353 | goto err_add_udc; | 1363 | goto err_add_udc; |
1354 | 1364 | ||
1355 | return 0; | 1365 | return 0; |
1356 | err_add_udc: | 1366 | err_add_udc: |
1367 | device_unregister(&hsudc->gadget.dev); | ||
1368 | err_add_device: | ||
1357 | clk_disable(hsudc->uclk); | 1369 | clk_disable(hsudc->uclk); |
1358 | clk_put(hsudc->uclk); | 1370 | clk_put(hsudc->uclk); |
1359 | err_clk: | 1371 | err_clk: |
@@ -1362,10 +1374,13 @@ err_irq: | |||
1362 | iounmap(hsudc->regs); | 1374 | iounmap(hsudc->regs); |
1363 | 1375 | ||
1364 | err_remap: | 1376 | err_remap: |
1365 | release_resource(hsudc->mem_rsrc); | 1377 | release_mem_region(res->start, resource_size(res)); |
1366 | kfree(hsudc->mem_rsrc); | ||
1367 | |||
1368 | err_res: | 1378 | err_res: |
1379 | if (hsudc->transceiver) | ||
1380 | otg_put_transceiver(hsudc->transceiver); | ||
1381 | |||
1382 | regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); | ||
1383 | err_supplies: | ||
1369 | kfree(hsudc); | 1384 | kfree(hsudc); |
1370 | return ret; | 1385 | return ret; |
1371 | } | 1386 | } |
@@ -1377,21 +1392,10 @@ static struct platform_driver s3c_hsudc_driver = { | |||
1377 | }, | 1392 | }, |
1378 | .probe = s3c_hsudc_probe, | 1393 | .probe = s3c_hsudc_probe, |
1379 | }; | 1394 | }; |
1380 | MODULE_ALIAS("platform:s3c-hsudc"); | ||
1381 | |||
1382 | static int __init s3c_hsudc_modinit(void) | ||
1383 | { | ||
1384 | return platform_driver_register(&s3c_hsudc_driver); | ||
1385 | } | ||
1386 | 1395 | ||
1387 | static void __exit s3c_hsudc_modexit(void) | 1396 | module_platform_driver(s3c_hsudc_driver); |
1388 | { | ||
1389 | platform_driver_unregister(&s3c_hsudc_driver); | ||
1390 | } | ||
1391 | |||
1392 | module_init(s3c_hsudc_modinit); | ||
1393 | module_exit(s3c_hsudc_modexit); | ||
1394 | 1397 | ||
1395 | MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver"); | 1398 | MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver"); |
1396 | MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>"); | 1399 | MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>"); |
1397 | MODULE_LICENSE("GPL"); | 1400 | MODULE_LICENSE("GPL"); |
1401 | MODULE_ALIAS("platform:s3c-hsudc"); | ||