diff options
| -rw-r--r-- | Documentation/driver-model/platform.txt | 59 | ||||
| -rw-r--r-- | drivers/base/platform.c | 239 | ||||
| -rw-r--r-- | include/linux/init.h | 1 | ||||
| -rw-r--r-- | include/linux/platform_device.h | 42 | ||||
| -rw-r--r-- | init/main.c | 7 |
5 files changed, 347 insertions, 1 deletions
diff --git a/Documentation/driver-model/platform.txt b/Documentation/driver-model/platform.txt index 83009fdcbbc8..2e2c2ea90ceb 100644 --- a/Documentation/driver-model/platform.txt +++ b/Documentation/driver-model/platform.txt | |||
| @@ -169,3 +169,62 @@ three different ways to find such a match: | |||
| 169 | be probed later if another device registers. (Which is OK, since | 169 | be probed later if another device registers. (Which is OK, since |
| 170 | this interface is only for use with non-hotpluggable devices.) | 170 | this interface is only for use with non-hotpluggable devices.) |
| 171 | 171 | ||
| 172 | |||
| 173 | Early Platform Devices and Drivers | ||
| 174 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 175 | The early platform interfaces provide platform data to platform device | ||
| 176 | drivers early on during the system boot. The code is built on top of the | ||
| 177 | early_param() command line parsing and can be executed very early on. | ||
| 178 | |||
| 179 | Example: "earlyprintk" class early serial console in 6 steps | ||
| 180 | |||
| 181 | 1. Registering early platform device data | ||
| 182 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 183 | The architecture code registers platform device data using the function | ||
| 184 | early_platform_add_devices(). In the case of early serial console this | ||
| 185 | should be hardware configuration for the serial port. Devices registered | ||
| 186 | at this point will later on be matched against early platform drivers. | ||
| 187 | |||
| 188 | 2. Parsing kernel command line | ||
| 189 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 190 | The architecture code calls parse_early_param() to parse the kernel | ||
| 191 | command line. This will execute all matching early_param() callbacks. | ||
| 192 | User specified early platform devices will be registered at this point. | ||
| 193 | For the early serial console case the user can specify port on the | ||
| 194 | kernel command line as "earlyprintk=serial.0" where "earlyprintk" is | ||
| 195 | the class string, "serial" is the name of the platfrom driver and | ||
| 196 | 0 is the platform device id. If the id is -1 then the dot and the | ||
| 197 | id can be omitted. | ||
| 198 | |||
| 199 | 3. Installing early platform drivers belonging to a certain class | ||
| 200 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 201 | The architecture code may optionally force registration of all early | ||
| 202 | platform drivers belonging to a certain class using the function | ||
| 203 | early_platform_driver_register_all(). User specified devices from | ||
| 204 | step 2 have priority over these. This step is omitted by the serial | ||
| 205 | driver example since the early serial driver code should be disabled | ||
| 206 | unless the user has specified port on the kernel command line. | ||
| 207 | |||
| 208 | 4. Early platform driver registration | ||
| 209 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 210 | Compiled-in platform drivers making use of early_platform_init() are | ||
| 211 | automatically registered during step 2 or 3. The serial driver example | ||
| 212 | should use early_platform_init("earlyprintk", &platform_driver). | ||
| 213 | |||
| 214 | 5. Probing of early platform drivers belonging to a certain class | ||
| 215 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 216 | The architecture code calls early_platform_driver_probe() to match | ||
| 217 | registered early platform devices associated with a certain class with | ||
| 218 | registered early platform drivers. Matched devices will get probed(). | ||
| 219 | This step can be executed at any point during the early boot. As soon | ||
| 220 | as possible may be good for the serial port case. | ||
| 221 | |||
| 222 | 6. Inside the early platform driver probe() | ||
| 223 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 224 | The driver code needs to take special care during early boot, especially | ||
| 225 | when it comes to memory allocation and interrupt registration. The code | ||
| 226 | in the probe() function can use is_early_platform_device() to check if | ||
| 227 | it is called at early platform device or at the regular platform device | ||
| 228 | time. The early serial driver performs register_console() at this point. | ||
| 229 | |||
| 230 | For further information, see <linux/platform_device.h>. | ||
diff --git a/drivers/base/platform.c b/drivers/base/platform.c index d2198f64ad4e..b5b6c973a2e0 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c | |||
| @@ -990,6 +990,8 @@ int __init platform_bus_init(void) | |||
| 990 | { | 990 | { |
| 991 | int error; | 991 | int error; |
| 992 | 992 | ||
| 993 | early_platform_cleanup(); | ||
| 994 | |||
| 993 | error = device_register(&platform_bus); | 995 | error = device_register(&platform_bus); |
| 994 | if (error) | 996 | if (error) |
| 995 | return error; | 997 | return error; |
| @@ -1020,3 +1022,240 @@ u64 dma_get_required_mask(struct device *dev) | |||
| 1020 | } | 1022 | } |
| 1021 | EXPORT_SYMBOL_GPL(dma_get_required_mask); | 1023 | EXPORT_SYMBOL_GPL(dma_get_required_mask); |
| 1022 | #endif | 1024 | #endif |
| 1025 | |||
| 1026 | static __initdata LIST_HEAD(early_platform_driver_list); | ||
| 1027 | static __initdata LIST_HEAD(early_platform_device_list); | ||
| 1028 | |||
| 1029 | /** | ||
| 1030 | * early_platform_driver_register | ||
| 1031 | * @edrv: early_platform driver structure | ||
| 1032 | * @buf: string passed from early_param() | ||
| 1033 | */ | ||
| 1034 | int __init early_platform_driver_register(struct early_platform_driver *epdrv, | ||
| 1035 | char *buf) | ||
| 1036 | { | ||
| 1037 | unsigned long index; | ||
| 1038 | int n; | ||
| 1039 | |||
| 1040 | /* Simply add the driver to the end of the global list. | ||
| 1041 | * Drivers will by default be put on the list in compiled-in order. | ||
| 1042 | */ | ||
| 1043 | if (!epdrv->list.next) { | ||
| 1044 | INIT_LIST_HEAD(&epdrv->list); | ||
| 1045 | list_add_tail(&epdrv->list, &early_platform_driver_list); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | /* If the user has specified device then make sure the driver | ||
| 1049 | * gets prioritized. The driver of the last device specified on | ||
| 1050 | * command line will be put first on the list. | ||
| 1051 | */ | ||
| 1052 | n = strlen(epdrv->pdrv->driver.name); | ||
| 1053 | if (buf && !strncmp(buf, epdrv->pdrv->driver.name, n)) { | ||
| 1054 | list_move(&epdrv->list, &early_platform_driver_list); | ||
| 1055 | |||
| 1056 | if (!strcmp(buf, epdrv->pdrv->driver.name)) | ||
| 1057 | epdrv->requested_id = -1; | ||
| 1058 | else if (buf[n] == '.' && strict_strtoul(&buf[n + 1], 10, | ||
| 1059 | &index) == 0) | ||
| 1060 | epdrv->requested_id = index; | ||
| 1061 | else | ||
| 1062 | epdrv->requested_id = EARLY_PLATFORM_ID_ERROR; | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | return 0; | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | /** | ||
| 1069 | * early_platform_add_devices - add a numbers of early platform devices | ||
| 1070 | * @devs: array of early platform devices to add | ||
| 1071 | * @num: number of early platform devices in array | ||
| 1072 | */ | ||
| 1073 | void __init early_platform_add_devices(struct platform_device **devs, int num) | ||
| 1074 | { | ||
| 1075 | struct device *dev; | ||
| 1076 | int i; | ||
| 1077 | |||
| 1078 | /* simply add the devices to list */ | ||
| 1079 | for (i = 0; i < num; i++) { | ||
| 1080 | dev = &devs[i]->dev; | ||
| 1081 | |||
| 1082 | if (!dev->devres_head.next) { | ||
| 1083 | INIT_LIST_HEAD(&dev->devres_head); | ||
| 1084 | list_add_tail(&dev->devres_head, | ||
| 1085 | &early_platform_device_list); | ||
| 1086 | } | ||
| 1087 | } | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | /** | ||
| 1091 | * early_platform_driver_register_all | ||
| 1092 | * @class_str: string to identify early platform driver class | ||
| 1093 | */ | ||
| 1094 | void __init early_platform_driver_register_all(char *class_str) | ||
| 1095 | { | ||
| 1096 | /* The "class_str" parameter may or may not be present on the kernel | ||
| 1097 | * command line. If it is present then there may be more than one | ||
| 1098 | * matching parameter. | ||
| 1099 | * | ||
| 1100 | * Since we register our early platform drivers using early_param() | ||
| 1101 | * we need to make sure that they also get registered in the case | ||
| 1102 | * when the parameter is missing from the kernel command line. | ||
| 1103 | * | ||
| 1104 | * We use parse_early_options() to make sure the early_param() gets | ||
| 1105 | * called at least once. The early_param() may be called more than | ||
| 1106 | * once since the name of the preferred device may be specified on | ||
| 1107 | * the kernel command line. early_platform_driver_register() handles | ||
| 1108 | * this case for us. | ||
| 1109 | */ | ||
| 1110 | parse_early_options(class_str); | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | /** | ||
| 1114 | * early_platform_match | ||
| 1115 | * @edrv: early platform driver structure | ||
| 1116 | * @id: id to match against | ||
| 1117 | */ | ||
| 1118 | static __init struct platform_device * | ||
| 1119 | early_platform_match(struct early_platform_driver *epdrv, int id) | ||
| 1120 | { | ||
| 1121 | struct platform_device *pd; | ||
| 1122 | |||
| 1123 | list_for_each_entry(pd, &early_platform_device_list, dev.devres_head) | ||
| 1124 | if (platform_match(&pd->dev, &epdrv->pdrv->driver)) | ||
| 1125 | if (pd->id == id) | ||
| 1126 | return pd; | ||
| 1127 | |||
| 1128 | return NULL; | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | /** | ||
| 1132 | * early_platform_left | ||
| 1133 | * @edrv: early platform driver structure | ||
| 1134 | * @id: return true if id or above exists | ||
| 1135 | */ | ||
| 1136 | static __init int early_platform_left(struct early_platform_driver *epdrv, | ||
| 1137 | int id) | ||
| 1138 | { | ||
| 1139 | struct platform_device *pd; | ||
| 1140 | |||
| 1141 | list_for_each_entry(pd, &early_platform_device_list, dev.devres_head) | ||
| 1142 | if (platform_match(&pd->dev, &epdrv->pdrv->driver)) | ||
| 1143 | if (pd->id >= id) | ||
| 1144 | return 1; | ||
| 1145 | |||
| 1146 | return 0; | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | /** | ||
| 1150 | * early_platform_driver_probe_id | ||
| 1151 | * @class_str: string to identify early platform driver class | ||
| 1152 | * @id: id to match against | ||
| 1153 | * @nr_probe: number of platform devices to successfully probe before exiting | ||
| 1154 | */ | ||
| 1155 | static int __init early_platform_driver_probe_id(char *class_str, | ||
| 1156 | int id, | ||
| 1157 | int nr_probe) | ||
| 1158 | { | ||
| 1159 | struct early_platform_driver *epdrv; | ||
| 1160 | struct platform_device *match; | ||
| 1161 | int match_id; | ||
| 1162 | int n = 0; | ||
| 1163 | int left = 0; | ||
| 1164 | |||
| 1165 | list_for_each_entry(epdrv, &early_platform_driver_list, list) { | ||
| 1166 | /* only use drivers matching our class_str */ | ||
| 1167 | if (strcmp(class_str, epdrv->class_str)) | ||
| 1168 | continue; | ||
| 1169 | |||
| 1170 | if (id == -2) { | ||
| 1171 | match_id = epdrv->requested_id; | ||
| 1172 | left = 1; | ||
| 1173 | |||
| 1174 | } else { | ||
| 1175 | match_id = id; | ||
| 1176 | left += early_platform_left(epdrv, id); | ||
| 1177 | |||
| 1178 | /* skip requested id */ | ||
| 1179 | switch (epdrv->requested_id) { | ||
| 1180 | case EARLY_PLATFORM_ID_ERROR: | ||
| 1181 | case EARLY_PLATFORM_ID_UNSET: | ||
| 1182 | break; | ||
| 1183 | default: | ||
| 1184 | if (epdrv->requested_id == id) | ||
| 1185 | match_id = EARLY_PLATFORM_ID_UNSET; | ||
| 1186 | } | ||
| 1187 | } | ||
| 1188 | |||
| 1189 | switch (match_id) { | ||
| 1190 | case EARLY_PLATFORM_ID_ERROR: | ||
| 1191 | pr_warning("%s: unable to parse %s parameter\n", | ||
| 1192 | class_str, epdrv->pdrv->driver.name); | ||
| 1193 | /* fall-through */ | ||
| 1194 | case EARLY_PLATFORM_ID_UNSET: | ||
| 1195 | match = NULL; | ||
| 1196 | break; | ||
| 1197 | default: | ||
| 1198 | match = early_platform_match(epdrv, match_id); | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | if (match) { | ||
| 1202 | if (epdrv->pdrv->probe(match)) | ||
| 1203 | pr_warning("%s: unable to probe %s early.\n", | ||
| 1204 | class_str, match->name); | ||
| 1205 | else | ||
| 1206 | n++; | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | if (n >= nr_probe) | ||
| 1210 | break; | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | if (left) | ||
| 1214 | return n; | ||
| 1215 | else | ||
| 1216 | return -ENODEV; | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | /** | ||
| 1220 | * early_platform_driver_probe | ||
| 1221 | * @class_str: string to identify early platform driver class | ||
| 1222 | * @nr_probe: number of platform devices to successfully probe before exiting | ||
| 1223 | * @user_only: only probe user specified early platform devices | ||
| 1224 | */ | ||
| 1225 | int __init early_platform_driver_probe(char *class_str, | ||
| 1226 | int nr_probe, | ||
| 1227 | int user_only) | ||
| 1228 | { | ||
| 1229 | int k, n, i; | ||
| 1230 | |||
| 1231 | n = 0; | ||
| 1232 | for (i = -2; n < nr_probe; i++) { | ||
| 1233 | k = early_platform_driver_probe_id(class_str, i, nr_probe - n); | ||
| 1234 | |||
| 1235 | if (k < 0) | ||
| 1236 | break; | ||
| 1237 | |||
| 1238 | n += k; | ||
| 1239 | |||
| 1240 | if (user_only) | ||
| 1241 | break; | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | return n; | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | /** | ||
| 1248 | * early_platform_cleanup - clean up early platform code | ||
| 1249 | */ | ||
| 1250 | void __init early_platform_cleanup(void) | ||
| 1251 | { | ||
| 1252 | struct platform_device *pd, *pd2; | ||
| 1253 | |||
| 1254 | /* clean up the devres list used to chain devices */ | ||
| 1255 | list_for_each_entry_safe(pd, pd2, &early_platform_device_list, | ||
| 1256 | dev.devres_head) { | ||
| 1257 | list_del(&pd->dev.devres_head); | ||
| 1258 | memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head)); | ||
| 1259 | } | ||
| 1260 | } | ||
| 1261 | |||
diff --git a/include/linux/init.h b/include/linux/init.h index 68cb0265d009..f121a7a10c3d 100644 --- a/include/linux/init.h +++ b/include/linux/init.h | |||
| @@ -247,6 +247,7 @@ struct obs_kernel_param { | |||
| 247 | 247 | ||
| 248 | /* Relies on boot_command_line being set */ | 248 | /* Relies on boot_command_line being set */ |
| 249 | void __init parse_early_param(void); | 249 | void __init parse_early_param(void); |
| 250 | void __init parse_early_options(char *cmdline); | ||
| 250 | #endif /* __ASSEMBLY__ */ | 251 | #endif /* __ASSEMBLY__ */ |
| 251 | 252 | ||
| 252 | /** | 253 | /** |
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 76e470a299bf..72736fd8223c 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h | |||
| @@ -77,4 +77,46 @@ extern int platform_driver_probe(struct platform_driver *driver, | |||
| 77 | #define platform_get_drvdata(_dev) dev_get_drvdata(&(_dev)->dev) | 77 | #define platform_get_drvdata(_dev) dev_get_drvdata(&(_dev)->dev) |
| 78 | #define platform_set_drvdata(_dev,data) dev_set_drvdata(&(_dev)->dev, (data)) | 78 | #define platform_set_drvdata(_dev,data) dev_set_drvdata(&(_dev)->dev, (data)) |
| 79 | 79 | ||
| 80 | /* early platform driver interface */ | ||
| 81 | struct early_platform_driver { | ||
| 82 | const char *class_str; | ||
| 83 | struct platform_driver *pdrv; | ||
| 84 | struct list_head list; | ||
| 85 | int requested_id; | ||
| 86 | }; | ||
| 87 | |||
| 88 | #define EARLY_PLATFORM_ID_UNSET -2 | ||
| 89 | #define EARLY_PLATFORM_ID_ERROR -3 | ||
| 90 | |||
| 91 | extern int early_platform_driver_register(struct early_platform_driver *epdrv, | ||
| 92 | char *buf); | ||
| 93 | extern void early_platform_add_devices(struct platform_device **devs, int num); | ||
| 94 | |||
| 95 | static inline int is_early_platform_device(struct platform_device *pdev) | ||
| 96 | { | ||
| 97 | return !pdev->dev.driver; | ||
| 98 | } | ||
| 99 | |||
| 100 | extern void early_platform_driver_register_all(char *class_str); | ||
| 101 | extern int early_platform_driver_probe(char *class_str, | ||
| 102 | int nr_probe, int user_only); | ||
| 103 | extern void early_platform_cleanup(void); | ||
| 104 | |||
| 105 | |||
| 106 | #ifndef MODULE | ||
| 107 | #define early_platform_init(class_string, platform_driver) \ | ||
| 108 | static __initdata struct early_platform_driver early_driver = { \ | ||
| 109 | .class_str = class_string, \ | ||
| 110 | .pdrv = platform_driver, \ | ||
| 111 | .requested_id = EARLY_PLATFORM_ID_UNSET, \ | ||
| 112 | }; \ | ||
| 113 | static int __init early_platform_driver_setup_func(char *buf) \ | ||
| 114 | { \ | ||
| 115 | return early_platform_driver_register(&early_driver, buf); \ | ||
| 116 | } \ | ||
| 117 | early_param(class_string, early_platform_driver_setup_func) | ||
| 118 | #else /* MODULE */ | ||
| 119 | #define early_platform_init(class_string, platform_driver) | ||
| 120 | #endif /* MODULE */ | ||
| 121 | |||
| 80 | #endif /* _PLATFORM_DEVICE_H_ */ | 122 | #endif /* _PLATFORM_DEVICE_H_ */ |
diff --git a/init/main.c b/init/main.c index 3585f073d636..3bbf93be744c 100644 --- a/init/main.c +++ b/init/main.c | |||
| @@ -492,6 +492,11 @@ static int __init do_early_param(char *param, char *val) | |||
| 492 | return 0; | 492 | return 0; |
| 493 | } | 493 | } |
| 494 | 494 | ||
| 495 | void __init parse_early_options(char *cmdline) | ||
| 496 | { | ||
| 497 | parse_args("early options", cmdline, NULL, 0, do_early_param); | ||
| 498 | } | ||
| 499 | |||
| 495 | /* Arch code calls this early on, or if not, just before other parsing. */ | 500 | /* Arch code calls this early on, or if not, just before other parsing. */ |
| 496 | void __init parse_early_param(void) | 501 | void __init parse_early_param(void) |
| 497 | { | 502 | { |
| @@ -503,7 +508,7 @@ void __init parse_early_param(void) | |||
| 503 | 508 | ||
| 504 | /* All fall through to do_early_param. */ | 509 | /* All fall through to do_early_param. */ |
| 505 | strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE); | 510 | strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE); |
| 506 | parse_args("early options", tmp_cmdline, NULL, 0, do_early_param); | 511 | parse_early_options(tmp_cmdline); |
| 507 | done = 1; | 512 | done = 1; |
| 508 | } | 513 | } |
| 509 | 514 | ||
