| /* | 
 |  * PPC System library functions | 
 |  * | 
 |  * Maintainer: Kumar Gala <galak@kernel.crashing.org> | 
 |  * | 
 |  * Copyright 2005 Freescale Semiconductor Inc. | 
 |  * Copyright 2005 MontaVista, Inc. by Vitaly Bordug <vbordug@ru.mvista.com> | 
 |  * | 
 |  * This program is free software; you can redistribute  it and/or modify it | 
 |  * under  the terms of  the GNU General  Public License as published by the | 
 |  * Free Software Foundation;  either version 2 of the  License, or (at your | 
 |  * option) any later version. | 
 |  */ | 
 |  | 
 | #include <linux/string.h> | 
 | #include <linux/bootmem.h> | 
 | #include <asm/ppc_sys.h> | 
 |  | 
 | int (*ppc_sys_device_fixup) (struct platform_device * pdev); | 
 |  | 
 | static int ppc_sys_inited; | 
 | static int ppc_sys_func_inited; | 
 |  | 
 | static const char *ppc_sys_func_names[] = { | 
 | 	[PPC_SYS_FUNC_DUMMY] = "dummy", | 
 | 	[PPC_SYS_FUNC_ETH] = "eth", | 
 | 	[PPC_SYS_FUNC_UART] = "uart", | 
 | 	[PPC_SYS_FUNC_HLDC] = "hldc", | 
 | 	[PPC_SYS_FUNC_USB] = "usb", | 
 | 	[PPC_SYS_FUNC_IRDA] = "irda", | 
 | }; | 
 |  | 
 | void __init identify_ppc_sys_by_id(u32 id) | 
 | { | 
 | 	unsigned int i = 0; | 
 | 	while (1) { | 
 | 		if ((ppc_sys_specs[i].mask & id) == ppc_sys_specs[i].value) | 
 | 			break; | 
 | 		i++; | 
 | 	} | 
 |  | 
 | 	cur_ppc_sys_spec = &ppc_sys_specs[i]; | 
 |  | 
 | 	return; | 
 | } | 
 |  | 
 | void __init identify_ppc_sys_by_name(char *name) | 
 | { | 
 | 	unsigned int i = 0; | 
 | 	while (ppc_sys_specs[i].ppc_sys_name[0]) { | 
 | 		if (!strcmp(ppc_sys_specs[i].ppc_sys_name, name)) | 
 | 			break; | 
 | 		i++; | 
 | 	} | 
 | 	cur_ppc_sys_spec = &ppc_sys_specs[i]; | 
 |  | 
 | 	return; | 
 | } | 
 |  | 
 | static int __init count_sys_specs(void) | 
 | { | 
 | 	int i = 0; | 
 | 	while (ppc_sys_specs[i].ppc_sys_name[0]) | 
 | 		i++; | 
 | 	return i; | 
 | } | 
 |  | 
 | static int __init find_chip_by_name_and_id(char *name, u32 id) | 
 | { | 
 | 	int ret = -1; | 
 | 	unsigned int i = 0; | 
 | 	unsigned int j = 0; | 
 | 	unsigned int dups = 0; | 
 |  | 
 | 	unsigned char matched[count_sys_specs()]; | 
 |  | 
 | 	while (ppc_sys_specs[i].ppc_sys_name[0]) { | 
 | 		if (!strcmp(ppc_sys_specs[i].ppc_sys_name, name)) | 
 | 			matched[j++] = i; | 
 | 		i++; | 
 | 	} | 
 |  | 
 | 	ret = i; | 
 |  | 
 | 	if (j != 0) { | 
 | 		for (i = 0; i < j; i++) { | 
 | 			if ((ppc_sys_specs[matched[i]].mask & id) == | 
 | 			    ppc_sys_specs[matched[i]].value) { | 
 | 				ret = matched[i]; | 
 | 				dups++; | 
 | 			} | 
 | 		} | 
 | 		ret = (dups == 1) ? ret : (-1 * dups); | 
 | 	} | 
 | 	return ret; | 
 | } | 
 |  | 
 | void __init identify_ppc_sys_by_name_and_id(char *name, u32 id) | 
 | { | 
 | 	int i = find_chip_by_name_and_id(name, id); | 
 | 	BUG_ON(i < 0); | 
 | 	cur_ppc_sys_spec = &ppc_sys_specs[i]; | 
 | } | 
 |  | 
 | /* Update all memory resources by paddr, call before platform_device_register */ | 
 | void __init | 
 | ppc_sys_fixup_mem_resource(struct platform_device *pdev, phys_addr_t paddr) | 
 | { | 
 | 	int i; | 
 | 	for (i = 0; i < pdev->num_resources; i++) { | 
 | 		struct resource *r = &pdev->resource[i]; | 
 | 		if (((r->flags & IORESOURCE_MEM) == IORESOURCE_MEM) &&  | 
 | 			((r->flags & PPC_SYS_IORESOURCE_FIXUPPED) != PPC_SYS_IORESOURCE_FIXUPPED)) { | 
 | 			r->start += paddr; | 
 | 			r->end += paddr; | 
 | 			r->flags |= PPC_SYS_IORESOURCE_FIXUPPED; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | /* Get platform_data pointer out of platform device, call before platform_device_register */ | 
 | void *__init ppc_sys_get_pdata(enum ppc_sys_devices dev) | 
 | { | 
 | 	return ppc_sys_platform_devices[dev].dev.platform_data; | 
 | } | 
 |  | 
 | void ppc_sys_device_remove(enum ppc_sys_devices dev) | 
 | { | 
 | 	unsigned int i; | 
 |  | 
 | 	if (ppc_sys_inited) { | 
 | 		platform_device_unregister(&ppc_sys_platform_devices[dev]); | 
 | 	} else { | 
 | 		if (cur_ppc_sys_spec == NULL) | 
 | 			return; | 
 | 		for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) | 
 | 			if (cur_ppc_sys_spec->device_list[i] == dev) | 
 | 				cur_ppc_sys_spec->device_list[i] = -1; | 
 | 	} | 
 | } | 
 |  | 
 | /* Platform-notify mapping | 
 |  * Helper function for BSP code to assign board-specific platfom-divice bits | 
 |  */ | 
 |  | 
 | void platform_notify_map(const struct platform_notify_dev_map *map, | 
 | 			 struct device *dev) | 
 | { | 
 | 	struct platform_device *pdev; | 
 | 	int len, idx; | 
 | 	const char *s; | 
 |  | 
 | 	/* do nothing if no device or no bus_id */ | 
 | 	if (!dev || !dev->bus_id) | 
 | 		return; | 
 |  | 
 | 	/* call per device map */ | 
 | 	while (map->bus_id != NULL) { | 
 | 		idx = -1; | 
 | 		s = strrchr(dev->bus_id, '.'); | 
 | 		if (s != NULL) { | 
 | 			idx = (int)simple_strtol(s + 1, NULL, 10); | 
 | 			len = s - dev->bus_id; | 
 | 		} else { | 
 | 			s = dev->bus_id; | 
 | 			len = strlen(dev->bus_id); | 
 | 		} | 
 |  | 
 | 		if (!strncmp(dev->bus_id, map->bus_id, len)) { | 
 | 			pdev = container_of(dev, struct platform_device, dev); | 
 | 			map->rtn(pdev, idx); | 
 | 		} | 
 | 		map++; | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |    Function assignment stuff. | 
 |  Intended to work as follows: | 
 |  the device name defined in foo_devices.c will be concatenated with :"func", | 
 |  where func is string map of respective function from platfom_device_func enum | 
 |  | 
 |  The PPC_SYS_FUNC_DUMMY function is intended to remove all assignments, making the device to appear | 
 |  in platform bus with unmodified name. | 
 |  */ | 
 |  | 
 | /* | 
 |    Here we'll replace .name pointers with fixed-lenght strings | 
 |    Hereby, this should be called *before* any func stuff triggeded. | 
 |  */ | 
 | void ppc_sys_device_initfunc(void) | 
 | { | 
 | 	int i; | 
 | 	const char *name; | 
 | 	static char new_names[NUM_PPC_SYS_DEVS][BUS_ID_SIZE]; | 
 | 	enum ppc_sys_devices cur_dev; | 
 |  | 
 | 	/* If inited yet, do nothing */ | 
 | 	if (ppc_sys_func_inited) | 
 | 		return; | 
 |  | 
 | 	for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) { | 
 | 		if ((cur_dev = cur_ppc_sys_spec->device_list[i]) < 0) | 
 | 			continue; | 
 |  | 
 | 		if (ppc_sys_platform_devices[cur_dev].name) { | 
 | 			/*backup name */ | 
 | 			name = ppc_sys_platform_devices[cur_dev].name; | 
 | 			strlcpy(new_names[i], name, BUS_ID_SIZE); | 
 | 			ppc_sys_platform_devices[cur_dev].name = new_names[i]; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	ppc_sys_func_inited = 1; | 
 | } | 
 |  | 
 | /*The "engine" of the func stuff. Here we either concat specified function string description | 
 |  to the name, or remove it if PPC_SYS_FUNC_DUMMY parameter is passed here*/ | 
 | void ppc_sys_device_setfunc(enum ppc_sys_devices dev, | 
 | 			    enum platform_device_func func) | 
 | { | 
 | 	char *s; | 
 | 	char *name = (char *)ppc_sys_platform_devices[dev].name; | 
 | 	char tmp[BUS_ID_SIZE]; | 
 |  | 
 | 	if (!ppc_sys_func_inited) { | 
 | 		printk(KERN_ERR "Unable to alter function - not inited!\n"); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if (ppc_sys_inited) { | 
 | 		platform_device_unregister(&ppc_sys_platform_devices[dev]); | 
 | 	} | 
 |  | 
 | 	if ((s = (char *)strchr(name, ':')) != NULL) {	/* reassign */ | 
 | 		/* Either change the name after ':' or remove func modifications */ | 
 | 		if (func != PPC_SYS_FUNC_DUMMY) | 
 | 			strlcpy(s + 1, ppc_sys_func_names[func], BUS_ID_SIZE); | 
 | 		else | 
 | 			*s = 0; | 
 | 	} else if (func != PPC_SYS_FUNC_DUMMY) { | 
 | 		/* do assignment if it is not just "clear"  request */ | 
 | 		sprintf(tmp, "%s:%s", name, ppc_sys_func_names[func]); | 
 | 		strlcpy(name, tmp, BUS_ID_SIZE); | 
 | 	} | 
 |  | 
 | 	if (ppc_sys_inited) { | 
 | 		platform_device_register(&ppc_sys_platform_devices[dev]); | 
 | 	} | 
 | } | 
 |  | 
 | void ppc_sys_device_disable(enum ppc_sys_devices dev) | 
 | { | 
 | 	BUG_ON(cur_ppc_sys_spec == NULL); | 
 |  | 
 | 	/*Check if it is enabled*/ | 
 | 	if(!(cur_ppc_sys_spec->config[dev] & PPC_SYS_CONFIG_DISABLED)) { | 
 | 		if (ppc_sys_inited) { | 
 | 			platform_device_unregister(&ppc_sys_platform_devices[dev]); | 
 | 		} | 
 | 		cur_ppc_sys_spec->config[dev] |= PPC_SYS_CONFIG_DISABLED; | 
 | 	} | 
 | } | 
 |  | 
 | void ppc_sys_device_enable(enum ppc_sys_devices dev) | 
 | { | 
 | 	BUG_ON(cur_ppc_sys_spec == NULL); | 
 |  | 
 | 	/*Check if it is disabled*/ | 
 | 	if(cur_ppc_sys_spec->config[dev] & PPC_SYS_CONFIG_DISABLED) { | 
 | 		if (ppc_sys_inited) { | 
 | 			platform_device_register(&ppc_sys_platform_devices[dev]); | 
 | 		} | 
 | 		cur_ppc_sys_spec->config[dev] &= ~PPC_SYS_CONFIG_DISABLED; | 
 | 	} | 
 |  | 
 | } | 
 |  | 
 | void ppc_sys_device_enable_all(void) | 
 | { | 
 | 	enum ppc_sys_devices cur_dev; | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) { | 
 | 		cur_dev = cur_ppc_sys_spec->device_list[i]; | 
 | 		ppc_sys_device_enable(cur_dev); | 
 | 	} | 
 | } | 
 |  | 
 | void ppc_sys_device_disable_all(void) | 
 | { | 
 | 	enum ppc_sys_devices cur_dev; | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) { | 
 | 		cur_dev = cur_ppc_sys_spec->device_list[i]; | 
 | 		ppc_sys_device_disable(cur_dev); | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | static int __init ppc_sys_init(void) | 
 | { | 
 | 	unsigned int i, dev_id, ret = 0; | 
 |  | 
 | 	BUG_ON(cur_ppc_sys_spec == NULL); | 
 |  | 
 | 	for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) { | 
 | 		dev_id = cur_ppc_sys_spec->device_list[i]; | 
 | 		if ((dev_id != -1) && | 
 | 		!(cur_ppc_sys_spec->config[dev_id] & PPC_SYS_CONFIG_DISABLED)) { | 
 | 			if (ppc_sys_device_fixup != NULL) | 
 | 				ppc_sys_device_fixup(&ppc_sys_platform_devices | 
 | 						     [dev_id]); | 
 | 			if (platform_device_register | 
 | 			    (&ppc_sys_platform_devices[dev_id])) { | 
 | 				ret = 1; | 
 | 				printk(KERN_ERR | 
 | 				       "unable to register device %d\n", | 
 | 				       dev_id); | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	ppc_sys_inited = 1; | 
 | 	return ret; | 
 | } | 
 |  | 
 | subsys_initcall(ppc_sys_init); |