~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/tty/hvc/hvc_opal.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * opal driver interface to hvc_console.c
 
3
 *
 
4
 * Copyright 2011 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
19
 *
 
20
 */
 
21
 
 
22
#undef DEBUG
 
23
 
 
24
#include <linux/types.h>
 
25
#include <linux/init.h>
 
26
#include <linux/delay.h>
 
27
#include <linux/slab.h>
 
28
#include <linux/console.h>
 
29
#include <linux/of.h>
 
30
#include <linux/of_platform.h>
 
31
#include <linux/export.h>
 
32
 
 
33
#include <asm/hvconsole.h>
 
34
#include <asm/prom.h>
 
35
#include <asm/firmware.h>
 
36
#include <asm/hvsi.h>
 
37
#include <asm/udbg.h>
 
38
#include <asm/opal.h>
 
39
 
 
40
#include "hvc_console.h"
 
41
 
 
42
static const char hvc_opal_name[] = "hvc_opal";
 
43
 
 
44
static struct of_device_id hvc_opal_match[] __devinitdata = {
 
45
        { .name = "serial", .compatible = "ibm,opal-console-raw" },
 
46
        { .name = "serial", .compatible = "ibm,opal-console-hvsi" },
 
47
        { },
 
48
};
 
49
 
 
50
typedef enum hv_protocol {
 
51
        HV_PROTOCOL_RAW,
 
52
        HV_PROTOCOL_HVSI
 
53
} hv_protocol_t;
 
54
 
 
55
struct hvc_opal_priv {
 
56
        hv_protocol_t           proto;  /* Raw data or HVSI packets */
 
57
        struct hvsi_priv        hvsi;   /* HVSI specific data */
 
58
};
 
59
static struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES];
 
60
 
 
61
/* For early boot console */
 
62
static struct hvc_opal_priv hvc_opal_boot_priv;
 
63
static u32 hvc_opal_boot_termno;
 
64
 
 
65
static const struct hv_ops hvc_opal_raw_ops = {
 
66
        .get_chars = opal_get_chars,
 
67
        .put_chars = opal_put_chars,
 
68
        .notifier_add = notifier_add_irq,
 
69
        .notifier_del = notifier_del_irq,
 
70
        .notifier_hangup = notifier_hangup_irq,
 
71
};
 
72
 
 
73
static int hvc_opal_hvsi_get_chars(uint32_t vtermno, char *buf, int count)
 
74
{
 
75
        struct hvc_opal_priv *pv = hvc_opal_privs[vtermno];
 
76
 
 
77
        if (WARN_ON(!pv))
 
78
                return -ENODEV;
 
79
 
 
80
        return hvsilib_get_chars(&pv->hvsi, buf, count);
 
81
}
 
82
 
 
83
static int hvc_opal_hvsi_put_chars(uint32_t vtermno, const char *buf, int count)
 
84
{
 
85
        struct hvc_opal_priv *pv = hvc_opal_privs[vtermno];
 
86
 
 
87
        if (WARN_ON(!pv))
 
88
                return -ENODEV;
 
89
 
 
90
        return hvsilib_put_chars(&pv->hvsi, buf, count);
 
91
}
 
92
 
 
93
static int hvc_opal_hvsi_open(struct hvc_struct *hp, int data)
 
94
{
 
95
        struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
 
96
        int rc;
 
97
 
 
98
        pr_devel("HVSI@%x: do open !\n", hp->vtermno);
 
99
 
 
100
        rc = notifier_add_irq(hp, data);
 
101
        if (rc)
 
102
                return rc;
 
103
 
 
104
        return hvsilib_open(&pv->hvsi, hp);
 
105
}
 
106
 
 
107
static void hvc_opal_hvsi_close(struct hvc_struct *hp, int data)
 
108
{
 
109
        struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
 
110
 
 
111
        pr_devel("HVSI@%x: do close !\n", hp->vtermno);
 
112
 
 
113
        hvsilib_close(&pv->hvsi, hp);
 
114
 
 
115
        notifier_del_irq(hp, data);
 
116
}
 
117
 
 
118
void hvc_opal_hvsi_hangup(struct hvc_struct *hp, int data)
 
119
{
 
120
        struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
 
121
 
 
122
        pr_devel("HVSI@%x: do hangup !\n", hp->vtermno);
 
123
 
 
124
        hvsilib_close(&pv->hvsi, hp);
 
125
 
 
126
        notifier_hangup_irq(hp, data);
 
127
}
 
128
 
 
129
static int hvc_opal_hvsi_tiocmget(struct hvc_struct *hp)
 
130
{
 
131
        struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
 
132
 
 
133
        if (!pv)
 
134
                return -EINVAL;
 
135
        return pv->hvsi.mctrl;
 
136
}
 
137
 
 
138
static int hvc_opal_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set,
 
139
                                unsigned int clear)
 
140
{
 
141
        struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
 
142
 
 
143
        pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n",
 
144
                 hp->vtermno, set, clear);
 
145
 
 
146
        if (set & TIOCM_DTR)
 
147
                hvsilib_write_mctrl(&pv->hvsi, 1);
 
148
        else if (clear & TIOCM_DTR)
 
149
                hvsilib_write_mctrl(&pv->hvsi, 0);
 
150
 
 
151
        return 0;
 
152
}
 
153
 
 
154
static const struct hv_ops hvc_opal_hvsi_ops = {
 
155
        .get_chars = hvc_opal_hvsi_get_chars,
 
156
        .put_chars = hvc_opal_hvsi_put_chars,
 
157
        .notifier_add = hvc_opal_hvsi_open,
 
158
        .notifier_del = hvc_opal_hvsi_close,
 
159
        .notifier_hangup = hvc_opal_hvsi_hangup,
 
160
        .tiocmget = hvc_opal_hvsi_tiocmget,
 
161
        .tiocmset = hvc_opal_hvsi_tiocmset,
 
162
};
 
163
 
 
164
static int __devinit hvc_opal_probe(struct platform_device *dev)
 
165
{
 
166
        const struct hv_ops *ops;
 
167
        struct hvc_struct *hp;
 
168
        struct hvc_opal_priv *pv;
 
169
        hv_protocol_t proto;
 
170
        unsigned int termno, boot = 0;
 
171
        const __be32 *reg;
 
172
 
 
173
        if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) {
 
174
                proto = HV_PROTOCOL_RAW;
 
175
                ops = &hvc_opal_raw_ops;
 
176
        } else if (of_device_is_compatible(dev->dev.of_node,
 
177
                                           "ibm,opal-console-hvsi")) {
 
178
                proto = HV_PROTOCOL_HVSI;
 
179
                ops = &hvc_opal_hvsi_ops;
 
180
        } else {
 
181
                pr_err("hvc_opal: Unkown protocol for %s\n",
 
182
                       dev->dev.of_node->full_name);
 
183
                return -ENXIO;
 
184
        }
 
185
 
 
186
        reg = of_get_property(dev->dev.of_node, "reg", NULL);
 
187
        termno = reg ? be32_to_cpup(reg) : 0;
 
188
 
 
189
        /* Is it our boot one ? */
 
190
        if (hvc_opal_privs[termno] == &hvc_opal_boot_priv) {
 
191
                pv = hvc_opal_privs[termno];
 
192
                boot = 1;
 
193
        } else if (hvc_opal_privs[termno] == NULL) {
 
194
                pv = kzalloc(sizeof(struct hvc_opal_priv), GFP_KERNEL);
 
195
                if (!pv)
 
196
                        return -ENOMEM;
 
197
                pv->proto = proto;
 
198
                hvc_opal_privs[termno] = pv;
 
199
                if (proto == HV_PROTOCOL_HVSI)
 
200
                        hvsilib_init(&pv->hvsi, opal_get_chars, opal_put_chars,
 
201
                                     termno, 0);
 
202
 
 
203
                /* Instanciate now to establish a mapping index==vtermno */
 
204
                hvc_instantiate(termno, termno, ops);
 
205
        } else {
 
206
                pr_err("hvc_opal: Device %s has duplicate terminal number #%d\n",
 
207
                       dev->dev.of_node->full_name, termno);
 
208
                return -ENXIO;
 
209
        }
 
210
 
 
211
        pr_info("hvc%d: %s protocol on %s%s\n", termno,
 
212
                proto == HV_PROTOCOL_RAW ? "raw" : "hvsi",
 
213
                dev->dev.of_node->full_name,
 
214
                boot ? " (boot console)" : "");
 
215
 
 
216
        /* We don't do IRQ yet */
 
217
        hp = hvc_alloc(termno, 0, ops, MAX_VIO_PUT_CHARS);
 
218
        if (IS_ERR(hp))
 
219
                return PTR_ERR(hp);
 
220
        dev_set_drvdata(&dev->dev, hp);
 
221
 
 
222
        return 0;
 
223
}
 
224
 
 
225
static int __devexit hvc_opal_remove(struct platform_device *dev)
 
226
{
 
227
        struct hvc_struct *hp = dev_get_drvdata(&dev->dev);
 
228
        int rc, termno;
 
229
 
 
230
        termno = hp->vtermno;
 
231
        rc = hvc_remove(hp);
 
232
        if (rc == 0) {
 
233
                if (hvc_opal_privs[termno] != &hvc_opal_boot_priv)
 
234
                        kfree(hvc_opal_privs[termno]);
 
235
                hvc_opal_privs[termno] = NULL;
 
236
        }
 
237
        return rc;
 
238
}
 
239
 
 
240
static struct platform_driver hvc_opal_driver = {
 
241
        .probe          = hvc_opal_probe,
 
242
        .remove         = __devexit_p(hvc_opal_remove),
 
243
        .driver         = {
 
244
                .name   = hvc_opal_name,
 
245
                .owner  = THIS_MODULE,
 
246
                .of_match_table = hvc_opal_match,
 
247
        }
 
248
};
 
249
 
 
250
static int __init hvc_opal_init(void)
 
251
{
 
252
        if (!firmware_has_feature(FW_FEATURE_OPAL))
 
253
                return -ENODEV;
 
254
 
 
255
        /* Register as a vio device to receive callbacks */
 
256
        return platform_driver_register(&hvc_opal_driver);
 
257
}
 
258
module_init(hvc_opal_init);
 
259
 
 
260
static void __exit hvc_opal_exit(void)
 
261
{
 
262
        platform_driver_unregister(&hvc_opal_driver);
 
263
}
 
264
module_exit(hvc_opal_exit);
 
265
 
 
266
static void udbg_opal_putc(char c)
 
267
{
 
268
        unsigned int termno = hvc_opal_boot_termno;
 
269
        int count = -1;
 
270
 
 
271
        if (c == '\n')
 
272
                udbg_opal_putc('\r');
 
273
 
 
274
        do {
 
275
                switch(hvc_opal_boot_priv.proto) {
 
276
                case HV_PROTOCOL_RAW:
 
277
                        count = opal_put_chars(termno, &c, 1);
 
278
                        break;
 
279
                case HV_PROTOCOL_HVSI:
 
280
                        count = hvc_opal_hvsi_put_chars(termno, &c, 1);
 
281
                        break;
 
282
                }
 
283
        } while(count == 0 || count == -EAGAIN);
 
284
}
 
285
 
 
286
static int udbg_opal_getc_poll(void)
 
287
{
 
288
        unsigned int termno = hvc_opal_boot_termno;
 
289
        int rc = 0;
 
290
        char c;
 
291
 
 
292
        switch(hvc_opal_boot_priv.proto) {
 
293
        case HV_PROTOCOL_RAW:
 
294
                rc = opal_get_chars(termno, &c, 1);
 
295
                break;
 
296
        case HV_PROTOCOL_HVSI:
 
297
                rc = hvc_opal_hvsi_get_chars(termno, &c, 1);
 
298
                break;
 
299
        }
 
300
        if (!rc)
 
301
                return -1;
 
302
        return c;
 
303
}
 
304
 
 
305
static int udbg_opal_getc(void)
 
306
{
 
307
        int ch;
 
308
        for (;;) {
 
309
                ch = udbg_opal_getc_poll();
 
310
                if (ch == -1) {
 
311
                        /* This shouldn't be needed...but... */
 
312
                        volatile unsigned long delay;
 
313
                        for (delay=0; delay < 2000000; delay++)
 
314
                                ;
 
315
                } else {
 
316
                        return ch;
 
317
                }
 
318
        }
 
319
}
 
320
 
 
321
static void udbg_init_opal_common(void)
 
322
{
 
323
        udbg_putc = udbg_opal_putc;
 
324
        udbg_getc = udbg_opal_getc;
 
325
        udbg_getc_poll = udbg_opal_getc_poll;
 
326
        tb_ticks_per_usec = 0x200; /* Make udelay not suck */
 
327
}
 
328
 
 
329
void __init hvc_opal_init_early(void)
 
330
{
 
331
        struct device_node *stdout_node = NULL;
 
332
        const u32 *termno;
 
333
        const char *name = NULL;
 
334
        const struct hv_ops *ops;
 
335
        u32 index;
 
336
 
 
337
        /* find the boot console from /chosen/stdout */
 
338
        if (of_chosen)
 
339
                name = of_get_property(of_chosen, "linux,stdout-path", NULL);
 
340
        if (name) {
 
341
                stdout_node = of_find_node_by_path(name);
 
342
                if (!stdout_node) {
 
343
                        pr_err("hvc_opal: Failed to locate default console!\n");
 
344
                        return;
 
345
                }
 
346
        } else {
 
347
                struct device_node *opal, *np;
 
348
 
 
349
                /* Current OPAL takeover doesn't provide the stdout
 
350
                 * path, so we hard wire it
 
351
                 */
 
352
                opal = of_find_node_by_path("/ibm,opal/consoles");
 
353
                if (opal)
 
354
                        pr_devel("hvc_opal: Found consoles in new location\n");
 
355
                if (!opal) {
 
356
                        opal = of_find_node_by_path("/ibm,opal");
 
357
                        if (opal)
 
358
                                pr_devel("hvc_opal: "
 
359
                                         "Found consoles in old location\n");
 
360
                }
 
361
                if (!opal)
 
362
                        return;
 
363
                for_each_child_of_node(opal, np) {
 
364
                        if (!strcmp(np->name, "serial")) {
 
365
                                stdout_node = np;
 
366
                                break;
 
367
                        }
 
368
                }
 
369
                of_node_put(opal);
 
370
        }
 
371
        if (!stdout_node)
 
372
                return;
 
373
        termno = of_get_property(stdout_node, "reg", NULL);
 
374
        index = termno ? *termno : 0;
 
375
        if (index >= MAX_NR_HVC_CONSOLES)
 
376
                return;
 
377
        hvc_opal_privs[index] = &hvc_opal_boot_priv;
 
378
 
 
379
        /* Check the protocol */
 
380
        if (of_device_is_compatible(stdout_node, "ibm,opal-console-raw")) {
 
381
                hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW;
 
382
                ops = &hvc_opal_raw_ops;
 
383
                pr_devel("hvc_opal: Found RAW console\n");
 
384
        }
 
385
        else if (of_device_is_compatible(stdout_node,"ibm,opal-console-hvsi")) {
 
386
                hvc_opal_boot_priv.proto = HV_PROTOCOL_HVSI;
 
387
                ops = &hvc_opal_hvsi_ops;
 
388
                hvsilib_init(&hvc_opal_boot_priv.hvsi, opal_get_chars,
 
389
                             opal_put_chars, index, 1);
 
390
                /* HVSI, perform the handshake now */
 
391
                hvsilib_establish(&hvc_opal_boot_priv.hvsi);
 
392
                pr_devel("hvc_opal: Found HVSI console\n");
 
393
        } else
 
394
                goto out;
 
395
        hvc_opal_boot_termno = index;
 
396
        udbg_init_opal_common();
 
397
        add_preferred_console("hvc", index, NULL);
 
398
        hvc_instantiate(index, index, ops);
 
399
out:
 
400
        of_node_put(stdout_node);
 
401
}
 
402
 
 
403
#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_RAW
 
404
void __init udbg_init_debug_opal(void)
 
405
{
 
406
        u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO;
 
407
        hvc_opal_privs[index] = &hvc_opal_boot_priv;
 
408
        hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW;
 
409
        hvc_opal_boot_termno = index;
 
410
        udbg_init_opal_common();
 
411
}
 
412
#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_RAW */
 
413
 
 
414
#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI
 
415
void __init udbg_init_debug_opal_hvsi(void)
 
416
{
 
417
        u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO;
 
418
        hvc_opal_privs[index] = &hvc_opal_boot_priv;
 
419
        hvc_opal_boot_termno = index;
 
420
        udbg_init_opal_common();
 
421
        hvsilib_init(&hvc_opal_boot_priv.hvsi, opal_get_chars, opal_put_chars,
 
422
                     index, 1);
 
423
        hvsilib_establish(&hvc_opal_boot_priv.hvsi);
 
424
}
 
425
#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI */