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