| /* | |
| * 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); |