ACPI: add __init to acpi_initialize_subsystem()
[sfrench/cifs-2.6.git] / arch / sparc64 / kernel / hvapi.c
1 /* hvapi.c: Hypervisor API management.
2  *
3  * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
4  */
5 #include <linux/kernel.h>
6 #include <linux/module.h>
7 #include <linux/init.h>
8 #include <linux/slab.h>
9
10 #include <asm/hypervisor.h>
11 #include <asm/oplib.h>
12
13 /* If the hypervisor indicates that the API setting
14  * calls are unsupported, by returning HV_EBADTRAP or
15  * HV_ENOTSUPPORTED, we assume that API groups with the
16  * PRE_API flag set are major 1 minor 0.
17  */
18 struct api_info {
19         unsigned long group;
20         unsigned long major;
21         unsigned long minor;
22         unsigned int refcnt;
23         unsigned int flags;
24 #define FLAG_PRE_API            0x00000001
25 };
26
27 static struct api_info api_table[] = {
28         { .group = HV_GRP_SUN4V,        .flags = FLAG_PRE_API   },
29         { .group = HV_GRP_CORE,         .flags = FLAG_PRE_API   },
30         { .group = HV_GRP_INTR,                                 },
31         { .group = HV_GRP_SOFT_STATE,                           },
32         { .group = HV_GRP_PCI,          .flags = FLAG_PRE_API   },
33         { .group = HV_GRP_LDOM,                                 },
34         { .group = HV_GRP_SVC_CHAN,     .flags = FLAG_PRE_API   },
35         { .group = HV_GRP_NCS,          .flags = FLAG_PRE_API   },
36         { .group = HV_GRP_NIAG_PERF,    .flags = FLAG_PRE_API   },
37         { .group = HV_GRP_FIRE_PERF,                            },
38         { .group = HV_GRP_DIAG,         .flags = FLAG_PRE_API   },
39 };
40
41 static DEFINE_SPINLOCK(hvapi_lock);
42
43 static struct api_info *__get_info(unsigned long group)
44 {
45         int i;
46
47         for (i = 0; i < ARRAY_SIZE(api_table); i++) {
48                 if (api_table[i].group == group)
49                         return &api_table[i];
50         }
51         return NULL;
52 }
53
54 static void __get_ref(struct api_info *p)
55 {
56         p->refcnt++;
57 }
58
59 static void __put_ref(struct api_info *p)
60 {
61         if (--p->refcnt == 0) {
62                 unsigned long ignore;
63
64                 sun4v_set_version(p->group, 0, 0, &ignore);
65                 p->major = p->minor = 0;
66         }
67 }
68
69 /* Register a hypervisor API specification.  It indicates the
70  * API group and desired major+minor.
71  *
72  * If an existing API registration exists '0' (success) will
73  * be returned if it is compatible with the one being registered.
74  * Otherwise a negative error code will be returned.
75  *
76  * Otherwise an attempt will be made to negotiate the requested
77  * API group/major/minor with the hypervisor, and errors returned
78  * if that does not succeed.
79  */
80 int sun4v_hvapi_register(unsigned long group, unsigned long major,
81                          unsigned long *minor)
82 {
83         struct api_info *p;
84         unsigned long flags;
85         int ret;
86
87         spin_lock_irqsave(&hvapi_lock, flags);
88         p = __get_info(group);
89         ret = -EINVAL;
90         if (p) {
91                 if (p->refcnt) {
92                         ret = -EINVAL;
93                         if (p->major == major) {
94                                 *minor = p->minor;
95                                 ret = 0;
96                         }
97                 } else {
98                         unsigned long actual_minor;
99                         unsigned long hv_ret;
100
101                         hv_ret = sun4v_set_version(group, major, *minor,
102                                                    &actual_minor);
103                         ret = -EINVAL;
104                         if (hv_ret == HV_EOK) {
105                                 *minor = actual_minor;
106                                 p->major = major;
107                                 p->minor = actual_minor;
108                                 ret = 0;
109                         } else if (hv_ret == HV_EBADTRAP ||
110                                    HV_ENOTSUPPORTED) {
111                                 if (p->flags & FLAG_PRE_API) {
112                                         if (major == 1) {
113                                                 p->major = 1;
114                                                 p->minor = 0;
115                                                 *minor = 0;
116                                                 ret = 0;
117                                         }
118                                 }
119                         }
120                 }
121
122                 if (ret == 0)
123                         __get_ref(p);
124         }
125         spin_unlock_irqrestore(&hvapi_lock, flags);
126
127         return ret;
128 }
129 EXPORT_SYMBOL(sun4v_hvapi_register);
130
131 void sun4v_hvapi_unregister(unsigned long group)
132 {
133         struct api_info *p;
134         unsigned long flags;
135
136         spin_lock_irqsave(&hvapi_lock, flags);
137         p = __get_info(group);
138         if (p)
139                 __put_ref(p);
140         spin_unlock_irqrestore(&hvapi_lock, flags);
141 }
142 EXPORT_SYMBOL(sun4v_hvapi_unregister);
143
144 int sun4v_hvapi_get(unsigned long group,
145                     unsigned long *major,
146                     unsigned long *minor)
147 {
148         struct api_info *p;
149         unsigned long flags;
150         int ret;
151
152         spin_lock_irqsave(&hvapi_lock, flags);
153         ret = -EINVAL;
154         p = __get_info(group);
155         if (p && p->refcnt) {
156                 *major = p->major;
157                 *minor = p->minor;
158                 ret = 0;
159         }
160         spin_unlock_irqrestore(&hvapi_lock, flags);
161
162         return ret;
163 }
164 EXPORT_SYMBOL(sun4v_hvapi_get);
165
166 void __init sun4v_hvapi_init(void)
167 {
168         unsigned long group, major, minor;
169
170         group = HV_GRP_SUN4V;
171         major = 1;
172         minor = 0;
173         if (sun4v_hvapi_register(group, major, &minor))
174                 goto bad;
175
176         group = HV_GRP_CORE;
177         major = 1;
178         minor = 1;
179         if (sun4v_hvapi_register(group, major, &minor))
180                 goto bad;
181
182         return;
183
184 bad:
185         prom_printf("HVAPI: Cannot register API group "
186                     "%lx with major(%u) minor(%u)\n",
187                     group, major, minor);
188         prom_halt();
189 }