OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [char/] [hvc_iseries.c] - Blame information for rev 65

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * iSeries vio driver interface to hvc_console.c
3
 *
4
 * This code is based heavily on hvc_vio.c and viocons.c
5
 *
6
 * Copyright (C) 2006 Stephen Rothwell, IBM Corporation
7
 *
8
 * This program is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 2 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21
 */
22
#include <stdarg.h>
23
#include <linux/types.h>
24
#include <linux/init.h>
25
#include <linux/kernel.h>
26
#include <linux/spinlock.h>
27
#include <linux/console.h>
28
 
29
#include <asm/hvconsole.h>
30
#include <asm/vio.h>
31
#include <asm/prom.h>
32
#include <asm/firmware.h>
33
#include <asm/iseries/vio.h>
34
#include <asm/iseries/hv_call.h>
35
#include <asm/iseries/hv_lp_config.h>
36
#include <asm/iseries/hv_lp_event.h>
37
 
38
#include "hvc_console.h"
39
 
40
#define VTTY_PORTS 10
41
 
42
static DEFINE_SPINLOCK(consolelock);
43
static DEFINE_SPINLOCK(consoleloglock);
44
 
45
static const char hvc_driver_name[] = "hvc_console";
46
 
47
#define IN_BUF_SIZE     200
48
 
49
/*
50
 * Our port information.
51
 */
52
static struct port_info {
53
        HvLpIndex lp;
54
        u64 seq;        /* sequence number of last HV send */
55
        u64 ack;        /* last ack from HV */
56
        struct hvc_struct *hp;
57
        int in_start;
58
        int in_end;
59
        unsigned char in_buf[IN_BUF_SIZE];
60
} port_info[VTTY_PORTS] = {
61
        [ 0 ... VTTY_PORTS - 1 ] = {
62
                .lp = HvLpIndexInvalid
63
        }
64
};
65
 
66
#define viochar_is_console(pi)  ((pi) == &port_info[0])
67
 
68
static struct vio_device_id hvc_driver_table[] __devinitdata = {
69
        {"serial", "IBM,iSeries-vty"},
70
        { "", "" }
71
};
72
MODULE_DEVICE_TABLE(vio, hvc_driver_table);
73
 
74
static void hvlog(char *fmt, ...)
75
{
76
        int i;
77
        unsigned long flags;
78
        va_list args;
79
        static char buf[256];
80
 
81
        spin_lock_irqsave(&consoleloglock, flags);
82
        va_start(args, fmt);
83
        i = vscnprintf(buf, sizeof(buf) - 1, fmt, args);
84
        va_end(args);
85
        buf[i++] = '\r';
86
        HvCall_writeLogBuffer(buf, i);
87
        spin_unlock_irqrestore(&consoleloglock, flags);
88
}
89
 
90
/*
91
 * Initialize the common fields in a charLpEvent
92
 */
93
static void init_data_event(struct viocharlpevent *viochar, HvLpIndex lp)
94
{
95
        struct HvLpEvent *hev = &viochar->event;
96
 
97
        memset(viochar, 0, sizeof(struct viocharlpevent));
98
 
99
        hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DEFERRED_ACK |
100
                HV_LP_EVENT_INT;
101
        hev->xType = HvLpEvent_Type_VirtualIo;
102
        hev->xSubtype = viomajorsubtype_chario | viochardata;
103
        hev->xSourceLp = HvLpConfig_getLpIndex();
104
        hev->xTargetLp = lp;
105
        hev->xSizeMinus1 = sizeof(struct viocharlpevent);
106
        hev->xSourceInstanceId = viopath_sourceinst(lp);
107
        hev->xTargetInstanceId = viopath_targetinst(lp);
108
}
109
 
110
static int get_chars(uint32_t vtermno, char *buf, int count)
111
{
112
        struct port_info *pi;
113
        int n = 0;
114
        unsigned long flags;
115
 
116
        if (vtermno >= VTTY_PORTS)
117
                return -EINVAL;
118
        if (count == 0)
119
                return 0;
120
 
121
        pi = &port_info[vtermno];
122
        spin_lock_irqsave(&consolelock, flags);
123
 
124
        if (pi->in_end == 0)
125
                goto done;
126
 
127
        n = pi->in_end - pi->in_start;
128
        if (n > count)
129
                n = count;
130
        memcpy(buf, &pi->in_buf[pi->in_start], n);
131
        pi->in_start += n;
132
        if (pi->in_start == pi->in_end) {
133
                pi->in_start = 0;
134
                pi->in_end = 0;
135
        }
136
done:
137
        spin_unlock_irqrestore(&consolelock, flags);
138
        return n;
139
}
140
 
141
static int put_chars(uint32_t vtermno, const char *buf, int count)
142
{
143
        struct viocharlpevent *viochar;
144
        struct port_info *pi;
145
        HvLpEvent_Rc hvrc;
146
        unsigned long flags;
147
        int sent = 0;
148
 
149
        if (vtermno >= VTTY_PORTS)
150
                return -EINVAL;
151
 
152
        pi = &port_info[vtermno];
153
 
154
        spin_lock_irqsave(&consolelock, flags);
155
 
156
        if (viochar_is_console(pi) && !viopath_isactive(pi->lp)) {
157
                HvCall_writeLogBuffer(buf, count);
158
                sent = count;
159
                goto done;
160
        }
161
 
162
        viochar = vio_get_event_buffer(viomajorsubtype_chario);
163
        if (viochar == NULL) {
164
                hvlog("\n\rviocons: Can't get viochar buffer.");
165
                goto done;
166
        }
167
 
168
        while ((count > 0) && ((pi->seq - pi->ack) < VIOCHAR_WINDOW)) {
169
                int len;
170
 
171
                len = (count > VIOCHAR_MAX_DATA) ? VIOCHAR_MAX_DATA : count;
172
 
173
                if (viochar_is_console(pi))
174
                        HvCall_writeLogBuffer(buf, len);
175
 
176
                init_data_event(viochar, pi->lp);
177
 
178
                viochar->len = len;
179
                viochar->event.xCorrelationToken = pi->seq++;
180
                viochar->event.xSizeMinus1 =
181
                        offsetof(struct viocharlpevent, data) + len;
182
 
183
                memcpy(viochar->data, buf, len);
184
 
185
                hvrc = HvCallEvent_signalLpEvent(&viochar->event);
186
                if (hvrc)
187
                        hvlog("\n\rerror sending event! return code %d\n\r",
188
                                (int)hvrc);
189
                sent += len;
190
                count -= len;
191
                buf += len;
192
        }
193
 
194
        vio_free_event_buffer(viomajorsubtype_chario, viochar);
195
done:
196
        spin_unlock_irqrestore(&consolelock, flags);
197
        return sent;
198
}
199
 
200
static struct hv_ops hvc_get_put_ops = {
201
        .get_chars = get_chars,
202
        .put_chars = put_chars,
203
};
204
 
205
static int __devinit hvc_vio_probe(struct vio_dev *vdev,
206
                        const struct vio_device_id *id)
207
{
208
        struct hvc_struct *hp;
209
        struct port_info *pi;
210
 
211
        /* probed with invalid parameters. */
212
        if (!vdev || !id)
213
                return -EPERM;
214
 
215
        if (vdev->unit_address >= VTTY_PORTS)
216
                return -ENODEV;
217
 
218
        pi = &port_info[vdev->unit_address];
219
 
220
        hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops,
221
                        VIOCHAR_MAX_DATA);
222
        if (IS_ERR(hp))
223
                return PTR_ERR(hp);
224
        pi->hp = hp;
225
        dev_set_drvdata(&vdev->dev, pi);
226
 
227
        return 0;
228
}
229
 
230
static int __devexit hvc_vio_remove(struct vio_dev *vdev)
231
{
232
        struct port_info *pi = dev_get_drvdata(&vdev->dev);
233
        struct hvc_struct *hp = pi->hp;
234
 
235
        return hvc_remove(hp);
236
}
237
 
238
static struct vio_driver hvc_vio_driver = {
239
        .id_table       = hvc_driver_table,
240
        .probe          = hvc_vio_probe,
241
        .remove         = hvc_vio_remove,
242
        .driver         = {
243
                .name   = hvc_driver_name,
244
                .owner  = THIS_MODULE,
245
        }
246
};
247
 
248
static void hvc_open_event(struct HvLpEvent *event)
249
{
250
        unsigned long flags;
251
        struct viocharlpevent *cevent = (struct viocharlpevent *)event;
252
        u8 port = cevent->virtual_device;
253
        struct port_info *pi;
254
        int reject = 0;
255
 
256
        if (hvlpevent_is_ack(event)) {
257
                if (port >= VTTY_PORTS)
258
                        return;
259
 
260
                spin_lock_irqsave(&consolelock, flags);
261
 
262
                pi = &port_info[port];
263
                if (event->xRc == HvLpEvent_Rc_Good) {
264
                        pi->seq = pi->ack = 0;
265
                        /*
266
                         * This line allows connections from the primary
267
                         * partition but once one is connected from the
268
                         * primary partition nothing short of a reboot
269
                         * of linux will allow access from the hosting
270
                         * partition again without a required iSeries fix.
271
                         */
272
                        pi->lp = event->xTargetLp;
273
                }
274
 
275
                spin_unlock_irqrestore(&consolelock, flags);
276
                if (event->xRc != HvLpEvent_Rc_Good)
277
                        printk(KERN_WARNING
278
                               "hvc: handle_open_event: event->xRc == (%d).\n",
279
                               event->xRc);
280
 
281
                if (event->xCorrelationToken != 0) {
282
                        atomic_t *aptr= (atomic_t *)event->xCorrelationToken;
283
                        atomic_set(aptr, 1);
284
                } else
285
                        printk(KERN_WARNING
286
                               "hvc: weird...got open ack without atomic\n");
287
                return;
288
        }
289
 
290
        /* This had better require an ack, otherwise complain */
291
        if (!hvlpevent_need_ack(event)) {
292
                printk(KERN_WARNING "hvc: viocharopen without ack bit!\n");
293
                return;
294
        }
295
 
296
        spin_lock_irqsave(&consolelock, flags);
297
 
298
        /* Make sure this is a good virtual tty */
299
        if (port >= VTTY_PORTS) {
300
                event->xRc = HvLpEvent_Rc_SubtypeError;
301
                cevent->subtype_result_code = viorc_openRejected;
302
                /*
303
                 * Flag state here since we can't printk while holding
304
                 * the consolelock spinlock.
305
                 */
306
                reject = 1;
307
        } else {
308
                pi = &port_info[port];
309
                if ((pi->lp != HvLpIndexInvalid) &&
310
                                (pi->lp != event->xSourceLp)) {
311
                        /*
312
                         * If this is tty is already connected to a different
313
                         * partition, fail.
314
                         */
315
                        event->xRc = HvLpEvent_Rc_SubtypeError;
316
                        cevent->subtype_result_code = viorc_openRejected;
317
                        reject = 2;
318
                } else {
319
                        pi->lp = event->xSourceLp;
320
                        event->xRc = HvLpEvent_Rc_Good;
321
                        cevent->subtype_result_code = viorc_good;
322
                        pi->seq = pi->ack = 0;
323
                }
324
        }
325
 
326
        spin_unlock_irqrestore(&consolelock, flags);
327
 
328
        if (reject == 1)
329
                printk(KERN_WARNING "hvc: open rejected: bad virtual tty.\n");
330
        else if (reject == 2)
331
                printk(KERN_WARNING "hvc: open rejected: console in exclusive "
332
                                "use by another partition.\n");
333
 
334
        /* Return the acknowledgement */
335
        HvCallEvent_ackLpEvent(event);
336
}
337
 
338
/*
339
 * Handle a close charLpEvent.  This should ONLY be an Interrupt because the
340
 * virtual console should never actually issue a close event to the hypervisor
341
 * because the virtual console never goes away.  A close event coming from the
342
 * hypervisor simply means that there are no client consoles connected to the
343
 * virtual console.
344
 */
345
static void hvc_close_event(struct HvLpEvent *event)
346
{
347
        unsigned long flags;
348
        struct viocharlpevent *cevent = (struct viocharlpevent *)event;
349
        u8 port = cevent->virtual_device;
350
 
351
        if (!hvlpevent_is_int(event)) {
352
                printk(KERN_WARNING
353
                        "hvc: got unexpected close acknowlegement\n");
354
                return;
355
        }
356
 
357
        if (port >= VTTY_PORTS) {
358
                printk(KERN_WARNING
359
                        "hvc: close message from invalid virtual device.\n");
360
                return;
361
        }
362
 
363
        /* For closes, just mark the console partition invalid */
364
        spin_lock_irqsave(&consolelock, flags);
365
 
366
        if (port_info[port].lp == event->xSourceLp)
367
                port_info[port].lp = HvLpIndexInvalid;
368
 
369
        spin_unlock_irqrestore(&consolelock, flags);
370
}
371
 
372
static void hvc_data_event(struct HvLpEvent *event)
373
{
374
        unsigned long flags;
375
        struct viocharlpevent *cevent = (struct viocharlpevent *)event;
376
        struct port_info *pi;
377
        int n;
378
        u8 port = cevent->virtual_device;
379
 
380
        if (port >= VTTY_PORTS) {
381
                printk(KERN_WARNING "hvc: data on invalid virtual device %d\n",
382
                                port);
383
                return;
384
        }
385
        if (cevent->len == 0)
386
                return;
387
 
388
        /*
389
         * Change 05/01/2003 - Ryan Arnold: If a partition other than
390
         * the current exclusive partition tries to send us data
391
         * events then just drop them on the floor because we don't
392
         * want his stinking data.  He isn't authorized to receive
393
         * data because he wasn't the first one to get the console,
394
         * therefore he shouldn't be allowed to send data either.
395
         * This will work without an iSeries fix.
396
         */
397
        pi = &port_info[port];
398
        if (pi->lp != event->xSourceLp)
399
                return;
400
 
401
        spin_lock_irqsave(&consolelock, flags);
402
 
403
        n = IN_BUF_SIZE - pi->in_end;
404
        if (n > cevent->len)
405
                n = cevent->len;
406
        if (n > 0) {
407
                memcpy(&pi->in_buf[pi->in_end], cevent->data, n);
408
                pi->in_end += n;
409
        }
410
        spin_unlock_irqrestore(&consolelock, flags);
411
        if (n == 0)
412
                printk(KERN_WARNING "hvc: input buffer overflow\n");
413
}
414
 
415
static void hvc_ack_event(struct HvLpEvent *event)
416
{
417
        struct viocharlpevent *cevent = (struct viocharlpevent *)event;
418
        unsigned long flags;
419
        u8 port = cevent->virtual_device;
420
 
421
        if (port >= VTTY_PORTS) {
422
                printk(KERN_WARNING "hvc: data on invalid virtual device\n");
423
                return;
424
        }
425
 
426
        spin_lock_irqsave(&consolelock, flags);
427
        port_info[port].ack = event->xCorrelationToken;
428
        spin_unlock_irqrestore(&consolelock, flags);
429
}
430
 
431
static void hvc_config_event(struct HvLpEvent *event)
432
{
433
        struct viocharlpevent *cevent = (struct viocharlpevent *)event;
434
 
435
        if (cevent->data[0] == 0x01)
436
                printk(KERN_INFO "hvc: window resized to %d: %d: %d: %d\n",
437
                       cevent->data[1], cevent->data[2],
438
                       cevent->data[3], cevent->data[4]);
439
        else
440
                printk(KERN_WARNING "hvc: unknown config event\n");
441
}
442
 
443
static void hvc_handle_event(struct HvLpEvent *event)
444
{
445
        int charminor;
446
 
447
        if (event == NULL)
448
                return;
449
 
450
        charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK;
451
        switch (charminor) {
452
        case viocharopen:
453
                hvc_open_event(event);
454
                break;
455
        case viocharclose:
456
                hvc_close_event(event);
457
                break;
458
        case viochardata:
459
                hvc_data_event(event);
460
                break;
461
        case viocharack:
462
                hvc_ack_event(event);
463
                break;
464
        case viocharconfig:
465
                hvc_config_event(event);
466
                break;
467
        default:
468
                if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) {
469
                        event->xRc = HvLpEvent_Rc_InvalidSubtype;
470
                        HvCallEvent_ackLpEvent(event);
471
                }
472
        }
473
}
474
 
475
static int __init send_open(HvLpIndex remoteLp, void *sem)
476
{
477
        return HvCallEvent_signalLpEventFast(remoteLp,
478
                        HvLpEvent_Type_VirtualIo,
479
                        viomajorsubtype_chario | viocharopen,
480
                        HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
481
                        viopath_sourceinst(remoteLp),
482
                        viopath_targetinst(remoteLp),
483
                        (u64)(unsigned long)sem, VIOVERSION << 16,
484
                        0, 0, 0, 0);
485
}
486
 
487
static int __init hvc_vio_init(void)
488
{
489
        atomic_t wait_flag;
490
        int rc;
491
 
492
        if (!firmware_has_feature(FW_FEATURE_ISERIES))
493
                return -EIO;
494
 
495
        /* +2 for fudge */
496
        rc = viopath_open(HvLpConfig_getPrimaryLpIndex(),
497
                        viomajorsubtype_chario, VIOCHAR_WINDOW + 2);
498
        if (rc)
499
                printk(KERN_WARNING "hvc: error opening to primary %d\n", rc);
500
 
501
        if (viopath_hostLp == HvLpIndexInvalid)
502
                vio_set_hostlp();
503
 
504
        /*
505
         * And if the primary is not the same as the hosting LP, open to the
506
         * hosting lp
507
         */
508
        if ((viopath_hostLp != HvLpIndexInvalid) &&
509
            (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) {
510
                printk(KERN_INFO "hvc: open path to hosting (%d)\n",
511
                                viopath_hostLp);
512
                rc = viopath_open(viopath_hostLp, viomajorsubtype_chario,
513
                                VIOCHAR_WINDOW + 2);    /* +2 for fudge */
514
                if (rc)
515
                        printk(KERN_WARNING
516
                                "error opening to partition %d: %d\n",
517
                                viopath_hostLp, rc);
518
        }
519
 
520
        if (vio_setHandler(viomajorsubtype_chario, hvc_handle_event) < 0)
521
                printk(KERN_WARNING
522
                        "hvc: error seting handler for console events!\n");
523
 
524
        /*
525
         * First, try to open the console to the hosting lp.
526
         * Wait on a semaphore for the response.
527
         */
528
        atomic_set(&wait_flag, 0);
529
        if ((viopath_isactive(viopath_hostLp)) &&
530
            (send_open(viopath_hostLp, &wait_flag) == 0)) {
531
                printk(KERN_INFO "hvc: hosting partition %d\n", viopath_hostLp);
532
                while (atomic_read(&wait_flag) == 0)
533
                        mb();
534
                atomic_set(&wait_flag, 0);
535
        }
536
 
537
        /*
538
         * If we don't have an active console, try the primary
539
         */
540
        if ((!viopath_isactive(port_info[0].lp)) &&
541
            (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) &&
542
            (send_open(HvLpConfig_getPrimaryLpIndex(), &wait_flag) == 0)) {
543
                printk(KERN_INFO "hvc: opening console to primary partition\n");
544
                while (atomic_read(&wait_flag) == 0)
545
                        mb();
546
        }
547
 
548
        /* Register as a vio device to receive callbacks */
549
        rc = vio_register_driver(&hvc_vio_driver);
550
 
551
        return rc;
552
}
553
module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */
554
 
555
static void __exit hvc_vio_exit(void)
556
{
557
        vio_unregister_driver(&hvc_vio_driver);
558
}
559
module_exit(hvc_vio_exit);
560
 
561
/* the device tree order defines our numbering */
562
static int __init hvc_find_vtys(void)
563
{
564
        struct device_node *vty;
565
        int num_found = 0;
566
 
567
        for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL;
568
                        vty = of_find_node_by_name(vty, "vty")) {
569
                const uint32_t *vtermno;
570
 
571
                /* We have statically defined space for only a certain number
572
                 * of console adapters.
573
                 */
574
                if ((num_found >= MAX_NR_HVC_CONSOLES) ||
575
                                (num_found >= VTTY_PORTS))
576
                        break;
577
 
578
                vtermno = of_get_property(vty, "reg", NULL);
579
                if (!vtermno)
580
                        continue;
581
 
582
                if (!of_device_is_compatible(vty, "IBM,iSeries-vty"))
583
                        continue;
584
 
585
                if (num_found == 0)
586
                        add_preferred_console("hvc", 0, NULL);
587
                hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops);
588
                ++num_found;
589
        }
590
 
591
        return num_found;
592
}
593
console_initcall(hvc_find_vtys);

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.