diff options
Diffstat (limited to 'drivers/base/platform.c')
-rw-r--r-- | drivers/base/platform.c | 323 |
1 files changed, 283 insertions, 40 deletions
diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 349a1013603f..456594bd97bc 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c | |||
@@ -69,7 +69,8 @@ EXPORT_SYMBOL_GPL(platform_get_irq); | |||
69 | * @name: resource name | 69 | * @name: resource name |
70 | */ | 70 | */ |
71 | struct resource *platform_get_resource_byname(struct platform_device *dev, | 71 | struct resource *platform_get_resource_byname(struct platform_device *dev, |
72 | unsigned int type, char *name) | 72 | unsigned int type, |
73 | const char *name) | ||
73 | { | 74 | { |
74 | int i; | 75 | int i; |
75 | 76 | ||
@@ -88,7 +89,7 @@ EXPORT_SYMBOL_GPL(platform_get_resource_byname); | |||
88 | * @dev: platform device | 89 | * @dev: platform device |
89 | * @name: IRQ name | 90 | * @name: IRQ name |
90 | */ | 91 | */ |
91 | int platform_get_irq_byname(struct platform_device *dev, char *name) | 92 | int platform_get_irq_byname(struct platform_device *dev, const char *name) |
92 | { | 93 | { |
93 | struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ, | 94 | struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ, |
94 | name); | 95 | name); |
@@ -244,7 +245,7 @@ int platform_device_add(struct platform_device *pdev) | |||
244 | if (pdev->id != -1) | 245 | if (pdev->id != -1) |
245 | dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); | 246 | dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); |
246 | else | 247 | else |
247 | dev_set_name(&pdev->dev, pdev->name); | 248 | dev_set_name(&pdev->dev, "%s", pdev->name); |
248 | 249 | ||
249 | for (i = 0; i < pdev->num_resources; i++) { | 250 | for (i = 0; i < pdev->num_resources; i++) { |
250 | struct resource *p, *r = &pdev->resource[i]; | 251 | struct resource *p, *r = &pdev->resource[i]; |
@@ -469,22 +470,6 @@ static void platform_drv_shutdown(struct device *_dev) | |||
469 | drv->shutdown(dev); | 470 | drv->shutdown(dev); |
470 | } | 471 | } |
471 | 472 | ||
472 | static int platform_drv_suspend(struct device *_dev, pm_message_t state) | ||
473 | { | ||
474 | struct platform_driver *drv = to_platform_driver(_dev->driver); | ||
475 | struct platform_device *dev = to_platform_device(_dev); | ||
476 | |||
477 | return drv->suspend(dev, state); | ||
478 | } | ||
479 | |||
480 | static int platform_drv_resume(struct device *_dev) | ||
481 | { | ||
482 | struct platform_driver *drv = to_platform_driver(_dev->driver); | ||
483 | struct platform_device *dev = to_platform_device(_dev); | ||
484 | |||
485 | return drv->resume(dev); | ||
486 | } | ||
487 | |||
488 | /** | 473 | /** |
489 | * platform_driver_register | 474 | * platform_driver_register |
490 | * @drv: platform driver structure | 475 | * @drv: platform driver structure |
@@ -498,10 +483,7 @@ int platform_driver_register(struct platform_driver *drv) | |||
498 | drv->driver.remove = platform_drv_remove; | 483 | drv->driver.remove = platform_drv_remove; |
499 | if (drv->shutdown) | 484 | if (drv->shutdown) |
500 | drv->driver.shutdown = platform_drv_shutdown; | 485 | drv->driver.shutdown = platform_drv_shutdown; |
501 | if (drv->suspend) | 486 | |
502 | drv->driver.suspend = platform_drv_suspend; | ||
503 | if (drv->resume) | ||
504 | drv->driver.resume = platform_drv_resume; | ||
505 | return driver_register(&drv->driver); | 487 | return driver_register(&drv->driver); |
506 | } | 488 | } |
507 | EXPORT_SYMBOL_GPL(platform_driver_register); | 489 | EXPORT_SYMBOL_GPL(platform_driver_register); |
@@ -584,10 +566,25 @@ static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
584 | { | 566 | { |
585 | struct platform_device *pdev = to_platform_device(dev); | 567 | struct platform_device *pdev = to_platform_device(dev); |
586 | 568 | ||
587 | add_uevent_var(env, "MODALIAS=platform:%s", pdev->name); | 569 | add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, |
570 | (pdev->id_entry) ? pdev->id_entry->name : pdev->name); | ||
588 | return 0; | 571 | return 0; |
589 | } | 572 | } |
590 | 573 | ||
574 | static const struct platform_device_id *platform_match_id( | ||
575 | struct platform_device_id *id, | ||
576 | struct platform_device *pdev) | ||
577 | { | ||
578 | while (id->name[0]) { | ||
579 | if (strcmp(pdev->name, id->name) == 0) { | ||
580 | pdev->id_entry = id; | ||
581 | return id; | ||
582 | } | ||
583 | id++; | ||
584 | } | ||
585 | return NULL; | ||
586 | } | ||
587 | |||
591 | /** | 588 | /** |
592 | * platform_match - bind platform device to platform driver. | 589 | * platform_match - bind platform device to platform driver. |
593 | * @dev: device. | 590 | * @dev: device. |
@@ -603,9 +600,14 @@ static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
603 | */ | 600 | */ |
604 | static int platform_match(struct device *dev, struct device_driver *drv) | 601 | static int platform_match(struct device *dev, struct device_driver *drv) |
605 | { | 602 | { |
606 | struct platform_device *pdev; | 603 | struct platform_device *pdev = to_platform_device(dev); |
604 | struct platform_driver *pdrv = to_platform_driver(drv); | ||
605 | |||
606 | /* match against the id table first */ | ||
607 | if (pdrv->id_table) | ||
608 | return platform_match_id(pdrv->id_table, pdev) != NULL; | ||
607 | 609 | ||
608 | pdev = container_of(dev, struct platform_device, dev); | 610 | /* fall-back to driver name match */ |
609 | return (strcmp(pdev->name, drv->name) == 0); | 611 | return (strcmp(pdev->name, drv->name) == 0); |
610 | } | 612 | } |
611 | 613 | ||
@@ -613,46 +615,48 @@ static int platform_match(struct device *dev, struct device_driver *drv) | |||
613 | 615 | ||
614 | static int platform_legacy_suspend(struct device *dev, pm_message_t mesg) | 616 | static int platform_legacy_suspend(struct device *dev, pm_message_t mesg) |
615 | { | 617 | { |
618 | struct platform_driver *pdrv = to_platform_driver(dev->driver); | ||
619 | struct platform_device *pdev = to_platform_device(dev); | ||
616 | int ret = 0; | 620 | int ret = 0; |
617 | 621 | ||
618 | if (dev->driver && dev->driver->suspend) | 622 | if (dev->driver && pdrv->suspend) |
619 | ret = dev->driver->suspend(dev, mesg); | 623 | ret = pdrv->suspend(pdev, mesg); |
620 | 624 | ||
621 | return ret; | 625 | return ret; |
622 | } | 626 | } |
623 | 627 | ||
624 | static int platform_legacy_suspend_late(struct device *dev, pm_message_t mesg) | 628 | static int platform_legacy_suspend_late(struct device *dev, pm_message_t mesg) |
625 | { | 629 | { |
626 | struct platform_driver *drv = to_platform_driver(dev->driver); | 630 | struct platform_driver *pdrv = to_platform_driver(dev->driver); |
627 | struct platform_device *pdev; | 631 | struct platform_device *pdev = to_platform_device(dev); |
628 | int ret = 0; | 632 | int ret = 0; |
629 | 633 | ||
630 | pdev = container_of(dev, struct platform_device, dev); | 634 | if (dev->driver && pdrv->suspend_late) |
631 | if (dev->driver && drv->suspend_late) | 635 | ret = pdrv->suspend_late(pdev, mesg); |
632 | ret = drv->suspend_late(pdev, mesg); | ||
633 | 636 | ||
634 | return ret; | 637 | return ret; |
635 | } | 638 | } |
636 | 639 | ||
637 | static int platform_legacy_resume_early(struct device *dev) | 640 | static int platform_legacy_resume_early(struct device *dev) |
638 | { | 641 | { |
639 | struct platform_driver *drv = to_platform_driver(dev->driver); | 642 | struct platform_driver *pdrv = to_platform_driver(dev->driver); |
640 | struct platform_device *pdev; | 643 | struct platform_device *pdev = to_platform_device(dev); |
641 | int ret = 0; | 644 | int ret = 0; |
642 | 645 | ||
643 | pdev = container_of(dev, struct platform_device, dev); | 646 | if (dev->driver && pdrv->resume_early) |
644 | if (dev->driver && drv->resume_early) | 647 | ret = pdrv->resume_early(pdev); |
645 | ret = drv->resume_early(pdev); | ||
646 | 648 | ||
647 | return ret; | 649 | return ret; |
648 | } | 650 | } |
649 | 651 | ||
650 | static int platform_legacy_resume(struct device *dev) | 652 | static int platform_legacy_resume(struct device *dev) |
651 | { | 653 | { |
654 | struct platform_driver *pdrv = to_platform_driver(dev->driver); | ||
655 | struct platform_device *pdev = to_platform_device(dev); | ||
652 | int ret = 0; | 656 | int ret = 0; |
653 | 657 | ||
654 | if (dev->driver && dev->driver->resume) | 658 | if (dev->driver && pdrv->resume) |
655 | ret = dev->driver->resume(dev); | 659 | ret = pdrv->resume(pdev); |
656 | 660 | ||
657 | return ret; | 661 | return ret; |
658 | } | 662 | } |
@@ -956,6 +960,8 @@ int __init platform_bus_init(void) | |||
956 | { | 960 | { |
957 | int error; | 961 | int error; |
958 | 962 | ||
963 | early_platform_cleanup(); | ||
964 | |||
959 | error = device_register(&platform_bus); | 965 | error = device_register(&platform_bus); |
960 | if (error) | 966 | if (error) |
961 | return error; | 967 | return error; |
@@ -986,3 +992,240 @@ u64 dma_get_required_mask(struct device *dev) | |||
986 | } | 992 | } |
987 | EXPORT_SYMBOL_GPL(dma_get_required_mask); | 993 | EXPORT_SYMBOL_GPL(dma_get_required_mask); |
988 | #endif | 994 | #endif |
995 | |||
996 | static __initdata LIST_HEAD(early_platform_driver_list); | ||
997 | static __initdata LIST_HEAD(early_platform_device_list); | ||
998 | |||
999 | /** | ||
1000 | * early_platform_driver_register | ||
1001 | * @epdrv: early_platform driver structure | ||
1002 | * @buf: string passed from early_param() | ||
1003 | */ | ||
1004 | int __init early_platform_driver_register(struct early_platform_driver *epdrv, | ||
1005 | char *buf) | ||
1006 | { | ||
1007 | unsigned long index; | ||
1008 | int n; | ||
1009 | |||
1010 | /* Simply add the driver to the end of the global list. | ||
1011 | * Drivers will by default be put on the list in compiled-in order. | ||
1012 | */ | ||
1013 | if (!epdrv->list.next) { | ||
1014 | INIT_LIST_HEAD(&epdrv->list); | ||
1015 | list_add_tail(&epdrv->list, &early_platform_driver_list); | ||
1016 | } | ||
1017 | |||
1018 | /* If the user has specified device then make sure the driver | ||
1019 | * gets prioritized. The driver of the last device specified on | ||
1020 | * command line will be put first on the list. | ||
1021 | */ | ||
1022 | n = strlen(epdrv->pdrv->driver.name); | ||
1023 | if (buf && !strncmp(buf, epdrv->pdrv->driver.name, n)) { | ||
1024 | list_move(&epdrv->list, &early_platform_driver_list); | ||
1025 | |||
1026 | if (!strcmp(buf, epdrv->pdrv->driver.name)) | ||
1027 | epdrv->requested_id = -1; | ||
1028 | else if (buf[n] == '.' && strict_strtoul(&buf[n + 1], 10, | ||
1029 | &index) == 0) | ||
1030 | epdrv->requested_id = index; | ||
1031 | else | ||
1032 | epdrv->requested_id = EARLY_PLATFORM_ID_ERROR; | ||
1033 | } | ||
1034 | |||
1035 | return 0; | ||
1036 | } | ||
1037 | |||
1038 | /** | ||
1039 | * early_platform_add_devices - add a numbers of early platform devices | ||
1040 | * @devs: array of early platform devices to add | ||
1041 | * @num: number of early platform devices in array | ||
1042 | */ | ||
1043 | void __init early_platform_add_devices(struct platform_device **devs, int num) | ||
1044 | { | ||
1045 | struct device *dev; | ||
1046 | int i; | ||
1047 | |||
1048 | /* simply add the devices to list */ | ||
1049 | for (i = 0; i < num; i++) { | ||
1050 | dev = &devs[i]->dev; | ||
1051 | |||
1052 | if (!dev->devres_head.next) { | ||
1053 | INIT_LIST_HEAD(&dev->devres_head); | ||
1054 | list_add_tail(&dev->devres_head, | ||
1055 | &early_platform_device_list); | ||
1056 | } | ||
1057 | } | ||
1058 | } | ||
1059 | |||
1060 | /** | ||
1061 | * early_platform_driver_register_all | ||
1062 | * @class_str: string to identify early platform driver class | ||
1063 | */ | ||
1064 | void __init early_platform_driver_register_all(char *class_str) | ||
1065 | { | ||
1066 | /* The "class_str" parameter may or may not be present on the kernel | ||
1067 | * command line. If it is present then there may be more than one | ||
1068 | * matching parameter. | ||
1069 | * | ||
1070 | * Since we register our early platform drivers using early_param() | ||
1071 | * we need to make sure that they also get registered in the case | ||
1072 | * when the parameter is missing from the kernel command line. | ||
1073 | * | ||
1074 | * We use parse_early_options() to make sure the early_param() gets | ||
1075 | * called at least once. The early_param() may be called more than | ||
1076 | * once since the name of the preferred device may be specified on | ||
1077 | * the kernel command line. early_platform_driver_register() handles | ||
1078 | * this case for us. | ||
1079 | */ | ||
1080 | parse_early_options(class_str); | ||
1081 | } | ||
1082 | |||
1083 | /** | ||
1084 | * early_platform_match | ||
1085 | * @epdrv: early platform driver structure | ||
1086 | * @id: id to match against | ||
1087 | */ | ||
1088 | static __init struct platform_device * | ||
1089 | early_platform_match(struct early_platform_driver *epdrv, int id) | ||
1090 | { | ||
1091 | struct platform_device *pd; | ||
1092 | |||
1093 | list_for_each_entry(pd, &early_platform_device_list, dev.devres_head) | ||
1094 | if (platform_match(&pd->dev, &epdrv->pdrv->driver)) | ||
1095 | if (pd->id == id) | ||
1096 | return pd; | ||
1097 | |||
1098 | return NULL; | ||
1099 | } | ||
1100 | |||
1101 | /** | ||
1102 | * early_platform_left | ||
1103 | * @epdrv: early platform driver structure | ||
1104 | * @id: return true if id or above exists | ||
1105 | */ | ||
1106 | static __init int early_platform_left(struct early_platform_driver *epdrv, | ||
1107 | int id) | ||
1108 | { | ||
1109 | struct platform_device *pd; | ||
1110 | |||
1111 | list_for_each_entry(pd, &early_platform_device_list, dev.devres_head) | ||
1112 | if (platform_match(&pd->dev, &epdrv->pdrv->driver)) | ||
1113 | if (pd->id >= id) | ||
1114 | return 1; | ||
1115 | |||
1116 | return 0; | ||
1117 | } | ||
1118 | |||
1119 | /** | ||
1120 | * early_platform_driver_probe_id | ||
1121 | * @class_str: string to identify early platform driver class | ||
1122 | * @id: id to match against | ||
1123 | * @nr_probe: number of platform devices to successfully probe before exiting | ||
1124 | */ | ||
1125 | static int __init early_platform_driver_probe_id(char *class_str, | ||
1126 | int id, | ||
1127 | int nr_probe) | ||
1128 | { | ||
1129 | struct early_platform_driver *epdrv; | ||
1130 | struct platform_device *match; | ||
1131 | int match_id; | ||
1132 | int n = 0; | ||
1133 | int left = 0; | ||
1134 | |||
1135 | list_for_each_entry(epdrv, &early_platform_driver_list, list) { | ||
1136 | /* only use drivers matching our class_str */ | ||
1137 | if (strcmp(class_str, epdrv->class_str)) | ||
1138 | continue; | ||
1139 | |||
1140 | if (id == -2) { | ||
1141 | match_id = epdrv->requested_id; | ||
1142 | left = 1; | ||
1143 | |||
1144 | } else { | ||
1145 | match_id = id; | ||
1146 | left += early_platform_left(epdrv, id); | ||
1147 | |||
1148 | /* skip requested id */ | ||
1149 | switch (epdrv->requested_id) { | ||
1150 | case EARLY_PLATFORM_ID_ERROR: | ||
1151 | case EARLY_PLATFORM_ID_UNSET: | ||
1152 | break; | ||
1153 | default: | ||
1154 | if (epdrv->requested_id == id) | ||
1155 | match_id = EARLY_PLATFORM_ID_UNSET; | ||
1156 | } | ||
1157 | } | ||
1158 | |||
1159 | switch (match_id) { | ||
1160 | case EARLY_PLATFORM_ID_ERROR: | ||
1161 | pr_warning("%s: unable to parse %s parameter\n", | ||
1162 | class_str, epdrv->pdrv->driver.name); | ||
1163 | /* fall-through */ | ||
1164 | case EARLY_PLATFORM_ID_UNSET: | ||
1165 | match = NULL; | ||
1166 | break; | ||
1167 | default: | ||
1168 | match = early_platform_match(epdrv, match_id); | ||
1169 | } | ||
1170 | |||
1171 | if (match) { | ||
1172 | if (epdrv->pdrv->probe(match)) | ||
1173 | pr_warning("%s: unable to probe %s early.\n", | ||
1174 | class_str, match->name); | ||
1175 | else | ||
1176 | n++; | ||
1177 | } | ||
1178 | |||
1179 | if (n >= nr_probe) | ||
1180 | break; | ||
1181 | } | ||
1182 | |||
1183 | if (left) | ||
1184 | return n; | ||
1185 | else | ||
1186 | return -ENODEV; | ||
1187 | } | ||
1188 | |||
1189 | /** | ||
1190 | * early_platform_driver_probe | ||
1191 | * @class_str: string to identify early platform driver class | ||
1192 | * @nr_probe: number of platform devices to successfully probe before exiting | ||
1193 | * @user_only: only probe user specified early platform devices | ||
1194 | */ | ||
1195 | int __init early_platform_driver_probe(char *class_str, | ||
1196 | int nr_probe, | ||
1197 | int user_only) | ||
1198 | { | ||
1199 | int k, n, i; | ||
1200 | |||
1201 | n = 0; | ||
1202 | for (i = -2; n < nr_probe; i++) { | ||
1203 | k = early_platform_driver_probe_id(class_str, i, nr_probe - n); | ||
1204 | |||
1205 | if (k < 0) | ||
1206 | break; | ||
1207 | |||
1208 | n += k; | ||
1209 | |||
1210 | if (user_only) | ||
1211 | break; | ||
1212 | } | ||
1213 | |||
1214 | return n; | ||
1215 | } | ||
1216 | |||
1217 | /** | ||
1218 | * early_platform_cleanup - clean up early platform code | ||
1219 | */ | ||
1220 | void __init early_platform_cleanup(void) | ||
1221 | { | ||
1222 | struct platform_device *pd, *pd2; | ||
1223 | |||
1224 | /* clean up the devres list used to chain devices */ | ||
1225 | list_for_each_entry_safe(pd, pd2, &early_platform_device_list, | ||
1226 | dev.devres_head) { | ||
1227 | list_del(&pd->dev.devres_head); | ||
1228 | memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head)); | ||
1229 | } | ||
1230 | } | ||
1231 | |||