| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2 |  * PPC System library functions | 
 | 3 |  * | 
| Kumar Gala | 4c8d3d9 | 2005-11-13 16:06:30 -0800 | [diff] [blame] | 4 |  * Maintainer: Kumar Gala <galak@kernel.crashing.org> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5 |  * | 
 | 6 |  * Copyright 2005 Freescale Semiconductor Inc. | 
| Kumar Gala | 88adfe7 | 2005-09-03 15:55:46 -0700 | [diff] [blame] | 7 |  * Copyright 2005 MontaVista, Inc. by Vitaly Bordug <vbordug@ru.mvista.com> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 8 |  * | 
 | 9 |  * This program is free software; you can redistribute  it and/or modify it | 
 | 10 |  * under  the terms of  the GNU General  Public License as published by the | 
 | 11 |  * Free Software Foundation;  either version 2 of the  License, or (at your | 
 | 12 |  * option) any later version. | 
 | 13 |  */ | 
 | 14 |  | 
| Tim Schmielau | 8c65b4a | 2005-11-07 00:59:43 -0800 | [diff] [blame] | 15 | #include <linux/string.h> | 
| Vitaly Bordug | 75288c7 | 2006-01-20 22:22:34 +0300 | [diff] [blame] | 16 | #include <linux/bootmem.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 17 | #include <asm/ppc_sys.h> | 
 | 18 |  | 
 | 19 | int (*ppc_sys_device_fixup) (struct platform_device * pdev); | 
 | 20 |  | 
 | 21 | static int ppc_sys_inited; | 
| Vitaly Bordug | 75288c7 | 2006-01-20 22:22:34 +0300 | [diff] [blame] | 22 | static int ppc_sys_func_inited; | 
 | 23 |  | 
 | 24 | static const char *ppc_sys_func_names[] = { | 
 | 25 | 	[PPC_SYS_FUNC_DUMMY] = "dummy", | 
 | 26 | 	[PPC_SYS_FUNC_ETH] = "eth", | 
 | 27 | 	[PPC_SYS_FUNC_UART] = "uart", | 
 | 28 | 	[PPC_SYS_FUNC_HLDC] = "hldc", | 
 | 29 | 	[PPC_SYS_FUNC_USB] = "usb", | 
 | 30 | 	[PPC_SYS_FUNC_IRDA] = "irda", | 
 | 31 | }; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 32 |  | 
 | 33 | void __init identify_ppc_sys_by_id(u32 id) | 
 | 34 | { | 
 | 35 | 	unsigned int i = 0; | 
 | 36 | 	while (1) { | 
 | 37 | 		if ((ppc_sys_specs[i].mask & id) == ppc_sys_specs[i].value) | 
 | 38 | 			break; | 
 | 39 | 		i++; | 
 | 40 | 	} | 
 | 41 |  | 
 | 42 | 	cur_ppc_sys_spec = &ppc_sys_specs[i]; | 
 | 43 |  | 
 | 44 | 	return; | 
 | 45 | } | 
 | 46 |  | 
 | 47 | void __init identify_ppc_sys_by_name(char *name) | 
 | 48 | { | 
| Kumar Gala | 88adfe7 | 2005-09-03 15:55:46 -0700 | [diff] [blame] | 49 | 	unsigned int i = 0; | 
| Vitaly Bordug | 75288c7 | 2006-01-20 22:22:34 +0300 | [diff] [blame] | 50 | 	while (ppc_sys_specs[i].ppc_sys_name[0]) { | 
| Kumar Gala | 88adfe7 | 2005-09-03 15:55:46 -0700 | [diff] [blame] | 51 | 		if (!strcmp(ppc_sys_specs[i].ppc_sys_name, name)) | 
 | 52 | 			break; | 
 | 53 | 		i++; | 
 | 54 | 	} | 
 | 55 | 	cur_ppc_sys_spec = &ppc_sys_specs[i]; | 
| Vitaly Bordug | 75288c7 | 2006-01-20 22:22:34 +0300 | [diff] [blame] | 56 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 57 | 	return; | 
 | 58 | } | 
 | 59 |  | 
| Kumar Gala | 88adfe7 | 2005-09-03 15:55:46 -0700 | [diff] [blame] | 60 | static int __init count_sys_specs(void) | 
 | 61 | { | 
 | 62 | 	int i = 0; | 
 | 63 | 	while (ppc_sys_specs[i].ppc_sys_name[0]) | 
 | 64 | 		i++; | 
 | 65 | 	return i; | 
 | 66 | } | 
 | 67 |  | 
 | 68 | static int __init find_chip_by_name_and_id(char *name, u32 id) | 
 | 69 | { | 
 | 70 | 	int ret = -1; | 
 | 71 | 	unsigned int i = 0; | 
 | 72 | 	unsigned int j = 0; | 
 | 73 | 	unsigned int dups = 0; | 
 | 74 |  | 
 | 75 | 	unsigned char matched[count_sys_specs()]; | 
 | 76 |  | 
 | 77 | 	while (ppc_sys_specs[i].ppc_sys_name[0]) { | 
 | 78 | 		if (!strcmp(ppc_sys_specs[i].ppc_sys_name, name)) | 
 | 79 | 			matched[j++] = i; | 
 | 80 | 		i++; | 
 | 81 | 	} | 
| Vitaly Bordug | 1461b4e | 2005-10-28 17:46:28 -0700 | [diff] [blame] | 82 |  | 
 | 83 | 	ret = i; | 
 | 84 |  | 
| Kumar Gala | 88adfe7 | 2005-09-03 15:55:46 -0700 | [diff] [blame] | 85 | 	if (j != 0) { | 
 | 86 | 		for (i = 0; i < j; i++) { | 
 | 87 | 			if ((ppc_sys_specs[matched[i]].mask & id) == | 
 | 88 | 			    ppc_sys_specs[matched[i]].value) { | 
 | 89 | 				ret = matched[i]; | 
 | 90 | 				dups++; | 
 | 91 | 			} | 
 | 92 | 		} | 
 | 93 | 		ret = (dups == 1) ? ret : (-1 * dups); | 
 | 94 | 	} | 
 | 95 | 	return ret; | 
 | 96 | } | 
 | 97 |  | 
 | 98 | void __init identify_ppc_sys_by_name_and_id(char *name, u32 id) | 
 | 99 | { | 
 | 100 | 	int i = find_chip_by_name_and_id(name, id); | 
 | 101 | 	BUG_ON(i < 0); | 
 | 102 | 	cur_ppc_sys_spec = &ppc_sys_specs[i]; | 
 | 103 | } | 
 | 104 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 105 | /* Update all memory resources by paddr, call before platform_device_register */ | 
 | 106 | void __init | 
 | 107 | ppc_sys_fixup_mem_resource(struct platform_device *pdev, phys_addr_t paddr) | 
 | 108 | { | 
 | 109 | 	int i; | 
 | 110 | 	for (i = 0; i < pdev->num_resources; i++) { | 
 | 111 | 		struct resource *r = &pdev->resource[i]; | 
| Vitaly Bordug | a73c87b | 2006-04-25 20:26:33 +0400 | [diff] [blame] | 112 | 		if (((r->flags & IORESOURCE_MEM) == IORESOURCE_MEM) &&  | 
 | 113 | 			((r->flags & PPC_SYS_IORESOURCE_FIXUPPED) != PPC_SYS_IORESOURCE_FIXUPPED)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 114 | 			r->start += paddr; | 
 | 115 | 			r->end += paddr; | 
| Vitaly Bordug | a73c87b | 2006-04-25 20:26:33 +0400 | [diff] [blame] | 116 | 			r->flags |= PPC_SYS_IORESOURCE_FIXUPPED; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 117 | 		} | 
 | 118 | 	} | 
 | 119 | } | 
 | 120 |  | 
 | 121 | /* Get platform_data pointer out of platform device, call before platform_device_register */ | 
 | 122 | void *__init ppc_sys_get_pdata(enum ppc_sys_devices dev) | 
 | 123 | { | 
 | 124 | 	return ppc_sys_platform_devices[dev].dev.platform_data; | 
 | 125 | } | 
 | 126 |  | 
 | 127 | void ppc_sys_device_remove(enum ppc_sys_devices dev) | 
 | 128 | { | 
 | 129 | 	unsigned int i; | 
 | 130 |  | 
 | 131 | 	if (ppc_sys_inited) { | 
 | 132 | 		platform_device_unregister(&ppc_sys_platform_devices[dev]); | 
 | 133 | 	} else { | 
 | 134 | 		if (cur_ppc_sys_spec == NULL) | 
 | 135 | 			return; | 
 | 136 | 		for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) | 
 | 137 | 			if (cur_ppc_sys_spec->device_list[i] == dev) | 
 | 138 | 				cur_ppc_sys_spec->device_list[i] = -1; | 
 | 139 | 	} | 
 | 140 | } | 
 | 141 |  | 
| Vitaly Bordug | 75288c7 | 2006-01-20 22:22:34 +0300 | [diff] [blame] | 142 | /* Platform-notify mapping | 
 | 143 |  * Helper function for BSP code to assign board-specific platfom-divice bits | 
 | 144 |  */ | 
 | 145 |  | 
 | 146 | void platform_notify_map(const struct platform_notify_dev_map *map, | 
 | 147 | 			 struct device *dev) | 
 | 148 | { | 
 | 149 | 	struct platform_device *pdev; | 
 | 150 | 	int len, idx; | 
 | 151 | 	const char *s; | 
 | 152 |  | 
 | 153 | 	/* do nothing if no device or no bus_id */ | 
 | 154 | 	if (!dev || !dev->bus_id) | 
 | 155 | 		return; | 
 | 156 |  | 
 | 157 | 	/* call per device map */ | 
 | 158 | 	while (map->bus_id != NULL) { | 
 | 159 | 		idx = -1; | 
 | 160 | 		s = strrchr(dev->bus_id, '.'); | 
| Vitaly Bordug | 132058f | 2006-04-03 15:26:32 +0400 | [diff] [blame] | 161 | 		if (s != NULL) { | 
| Vitaly Bordug | 75288c7 | 2006-01-20 22:22:34 +0300 | [diff] [blame] | 162 | 			idx = (int)simple_strtol(s + 1, NULL, 10); | 
| Vitaly Bordug | 132058f | 2006-04-03 15:26:32 +0400 | [diff] [blame] | 163 | 			len = s - dev->bus_id; | 
 | 164 | 		} else { | 
| Vitaly Bordug | 75288c7 | 2006-01-20 22:22:34 +0300 | [diff] [blame] | 165 | 			s = dev->bus_id; | 
| Vitaly Bordug | 132058f | 2006-04-03 15:26:32 +0400 | [diff] [blame] | 166 | 			len = strlen(dev->bus_id); | 
 | 167 | 		} | 
| Vitaly Bordug | 75288c7 | 2006-01-20 22:22:34 +0300 | [diff] [blame] | 168 |  | 
 | 169 | 		if (!strncmp(dev->bus_id, map->bus_id, len)) { | 
 | 170 | 			pdev = container_of(dev, struct platform_device, dev); | 
 | 171 | 			map->rtn(pdev, idx); | 
 | 172 | 		} | 
 | 173 | 		map++; | 
 | 174 | 	} | 
 | 175 | } | 
 | 176 |  | 
 | 177 | /* | 
 | 178 |    Function assignment stuff. | 
 | 179 |  Intended to work as follows: | 
 | 180 |  the device name defined in foo_devices.c will be concatenated with :"func", | 
 | 181 |  where func is string map of respective function from platfom_device_func enum | 
 | 182 |  | 
 | 183 |  The PPC_SYS_FUNC_DUMMY function is intended to remove all assignments, making the device to appear | 
 | 184 |  in platform bus with unmodified name. | 
 | 185 |  */ | 
 | 186 |  | 
 | 187 | /* | 
 | 188 |    Here we'll replace .name pointers with fixed-lenght strings | 
 | 189 |    Hereby, this should be called *before* any func stuff triggeded. | 
 | 190 |  */ | 
 | 191 | void ppc_sys_device_initfunc(void) | 
 | 192 | { | 
 | 193 | 	int i; | 
 | 194 | 	const char *name; | 
 | 195 | 	static char new_names[NUM_PPC_SYS_DEVS][BUS_ID_SIZE]; | 
 | 196 | 	enum ppc_sys_devices cur_dev; | 
 | 197 |  | 
 | 198 | 	/* If inited yet, do nothing */ | 
 | 199 | 	if (ppc_sys_func_inited) | 
 | 200 | 		return; | 
 | 201 |  | 
 | 202 | 	for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) { | 
 | 203 | 		if ((cur_dev = cur_ppc_sys_spec->device_list[i]) < 0) | 
 | 204 | 			continue; | 
 | 205 |  | 
 | 206 | 		if (ppc_sys_platform_devices[cur_dev].name) { | 
 | 207 | 			/*backup name */ | 
 | 208 | 			name = ppc_sys_platform_devices[cur_dev].name; | 
 | 209 | 			strlcpy(new_names[i], name, BUS_ID_SIZE); | 
 | 210 | 			ppc_sys_platform_devices[cur_dev].name = new_names[i]; | 
 | 211 | 		} | 
 | 212 | 	} | 
 | 213 |  | 
 | 214 | 	ppc_sys_func_inited = 1; | 
 | 215 | } | 
 | 216 |  | 
 | 217 | /*The "engine" of the func stuff. Here we either concat specified function string description | 
 | 218 |  to the name, or remove it if PPC_SYS_FUNC_DUMMY parameter is passed here*/ | 
 | 219 | void ppc_sys_device_setfunc(enum ppc_sys_devices dev, | 
 | 220 | 			    enum platform_device_func func) | 
 | 221 | { | 
 | 222 | 	char *s; | 
 | 223 | 	char *name = (char *)ppc_sys_platform_devices[dev].name; | 
 | 224 | 	char tmp[BUS_ID_SIZE]; | 
 | 225 |  | 
 | 226 | 	if (!ppc_sys_func_inited) { | 
 | 227 | 		printk(KERN_ERR "Unable to alter function - not inited!\n"); | 
 | 228 | 		return; | 
 | 229 | 	} | 
 | 230 |  | 
 | 231 | 	if (ppc_sys_inited) { | 
 | 232 | 		platform_device_unregister(&ppc_sys_platform_devices[dev]); | 
 | 233 | 	} | 
 | 234 |  | 
 | 235 | 	if ((s = (char *)strchr(name, ':')) != NULL) {	/* reassign */ | 
 | 236 | 		/* Either change the name after ':' or remove func modifications */ | 
 | 237 | 		if (func != PPC_SYS_FUNC_DUMMY) | 
 | 238 | 			strlcpy(s + 1, ppc_sys_func_names[func], BUS_ID_SIZE); | 
 | 239 | 		else | 
 | 240 | 			*s = 0; | 
 | 241 | 	} else if (func != PPC_SYS_FUNC_DUMMY) { | 
 | 242 | 		/* do assignment if it is not just "clear"  request */ | 
 | 243 | 		sprintf(tmp, "%s:%s", name, ppc_sys_func_names[func]); | 
 | 244 | 		strlcpy(name, tmp, BUS_ID_SIZE); | 
 | 245 | 	} | 
 | 246 |  | 
 | 247 | 	if (ppc_sys_inited) { | 
 | 248 | 		platform_device_register(&ppc_sys_platform_devices[dev]); | 
 | 249 | 	} | 
 | 250 | } | 
 | 251 |  | 
 | 252 | void ppc_sys_device_disable(enum ppc_sys_devices dev) | 
 | 253 | { | 
 | 254 | 	BUG_ON(cur_ppc_sys_spec == NULL); | 
 | 255 |  | 
 | 256 | 	/*Check if it is enabled*/ | 
 | 257 | 	if(!(cur_ppc_sys_spec->config[dev] & PPC_SYS_CONFIG_DISABLED)) { | 
 | 258 | 		if (ppc_sys_inited) { | 
 | 259 | 			platform_device_unregister(&ppc_sys_platform_devices[dev]); | 
 | 260 | 		} | 
 | 261 | 		cur_ppc_sys_spec->config[dev] |= PPC_SYS_CONFIG_DISABLED; | 
 | 262 | 	} | 
 | 263 | } | 
 | 264 |  | 
 | 265 | void ppc_sys_device_enable(enum ppc_sys_devices dev) | 
 | 266 | { | 
 | 267 | 	BUG_ON(cur_ppc_sys_spec == NULL); | 
 | 268 |  | 
 | 269 | 	/*Check if it is disabled*/ | 
 | 270 | 	if(cur_ppc_sys_spec->config[dev] & PPC_SYS_CONFIG_DISABLED) { | 
 | 271 | 		if (ppc_sys_inited) { | 
 | 272 | 			platform_device_register(&ppc_sys_platform_devices[dev]); | 
 | 273 | 		} | 
 | 274 | 		cur_ppc_sys_spec->config[dev] &= ~PPC_SYS_CONFIG_DISABLED; | 
 | 275 | 	} | 
 | 276 |  | 
 | 277 | } | 
 | 278 |  | 
 | 279 | void ppc_sys_device_enable_all(void) | 
 | 280 | { | 
 | 281 | 	enum ppc_sys_devices cur_dev; | 
 | 282 | 	int i; | 
 | 283 |  | 
 | 284 | 	for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) { | 
 | 285 | 		cur_dev = cur_ppc_sys_spec->device_list[i]; | 
 | 286 | 		ppc_sys_device_enable(cur_dev); | 
 | 287 | 	} | 
 | 288 | } | 
 | 289 |  | 
 | 290 | void ppc_sys_device_disable_all(void) | 
 | 291 | { | 
 | 292 | 	enum ppc_sys_devices cur_dev; | 
 | 293 | 	int i; | 
 | 294 |  | 
 | 295 | 	for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) { | 
 | 296 | 		cur_dev = cur_ppc_sys_spec->device_list[i]; | 
 | 297 | 		ppc_sys_device_disable(cur_dev); | 
 | 298 | 	} | 
 | 299 | } | 
 | 300 |  | 
 | 301 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 302 | static int __init ppc_sys_init(void) | 
 | 303 | { | 
 | 304 | 	unsigned int i, dev_id, ret = 0; | 
 | 305 |  | 
 | 306 | 	BUG_ON(cur_ppc_sys_spec == NULL); | 
 | 307 |  | 
 | 308 | 	for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) { | 
 | 309 | 		dev_id = cur_ppc_sys_spec->device_list[i]; | 
| Vitaly Bordug | 75288c7 | 2006-01-20 22:22:34 +0300 | [diff] [blame] | 310 | 		if ((dev_id != -1) && | 
 | 311 | 		!(cur_ppc_sys_spec->config[dev_id] & PPC_SYS_CONFIG_DISABLED)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 312 | 			if (ppc_sys_device_fixup != NULL) | 
 | 313 | 				ppc_sys_device_fixup(&ppc_sys_platform_devices | 
 | 314 | 						     [dev_id]); | 
 | 315 | 			if (platform_device_register | 
 | 316 | 			    (&ppc_sys_platform_devices[dev_id])) { | 
 | 317 | 				ret = 1; | 
 | 318 | 				printk(KERN_ERR | 
 | 319 | 				       "unable to register device %d\n", | 
 | 320 | 				       dev_id); | 
 | 321 | 			} | 
 | 322 | 		} | 
 | 323 | 	} | 
 | 324 |  | 
 | 325 | 	ppc_sys_inited = 1; | 
 | 326 | 	return ret; | 
 | 327 | } | 
 | 328 |  | 
 | 329 | subsys_initcall(ppc_sys_init); |