linux/drivers/tty/hvc/hvc_tile.c
<<
>>
Prefs
   1/*
   2 * Copyright 2010 Tilera Corporation. All Rights Reserved.
   3 *
   4 *   This program is free software; you can redistribute it and/or
   5 *   modify it under the terms of the GNU General Public License
   6 *   as published by the Free Software Foundation, version 2.
   7 *
   8 *   This program is distributed in the hope that it will be useful, but
   9 *   WITHOUT ANY WARRANTY; without even the implied warranty of
  10 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  11 *   NON INFRINGEMENT.  See the GNU General Public License for
  12 *   more details.
  13 *
  14 * Tilera TILE Processor hypervisor console
  15 */
  16
  17#include <linux/console.h>
  18#include <linux/delay.h>
  19#include <linux/err.h>
  20#include <linux/init.h>
  21#include <linux/interrupt.h>
  22#include <linux/irq.h>
  23#include <linux/moduleparam.h>
  24#include <linux/platform_device.h>
  25#include <linux/types.h>
  26
  27#include <asm/setup.h>
  28#include <arch/sim_def.h>
  29
  30#include <hv/hypervisor.h>
  31
  32#include "hvc_console.h"
  33
  34static int use_sim_console;
  35static int __init sim_console(char *str)
  36{
  37        use_sim_console = 1;
  38        return 0;
  39}
  40early_param("sim_console", sim_console);
  41
  42int tile_console_write(const char *buf, int count)
  43{
  44        if (unlikely(use_sim_console)) {
  45                int i;
  46                for (i = 0; i < count; ++i)
  47                        __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC |
  48                                     (buf[i] << _SIM_CONTROL_OPERATOR_BITS));
  49                __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC |
  50                             (SIM_PUTC_FLUSH_BINARY <<
  51                              _SIM_CONTROL_OPERATOR_BITS));
  52                return 0;
  53        } else {
  54                /* Translate 0 bytes written to EAGAIN for hvc_console_print. */
  55                return hv_console_write((HV_VirtAddr)buf, count) ?: -EAGAIN;
  56        }
  57}
  58
  59static int hvc_tile_put_chars(uint32_t vt, const char *buf, int count)
  60{
  61        return tile_console_write(buf, count);
  62}
  63
  64static int hvc_tile_get_chars(uint32_t vt, char *buf, int count)
  65{
  66        int i, c;
  67
  68        for (i = 0; i < count; ++i) {
  69                c = hv_console_read_if_ready();
  70                if (c < 0)
  71                        break;
  72                buf[i] = c;
  73        }
  74
  75        return i;
  76}
  77
  78#ifdef __tilegx__
  79/*
  80 * IRQ based callbacks.
  81 */
  82static int hvc_tile_notifier_add_irq(struct hvc_struct *hp, int irq)
  83{
  84        int rc;
  85        int cpu = raw_smp_processor_id();  /* Choose an arbitrary cpu */
  86        HV_Coord coord = { .x = cpu_x(cpu), .y = cpu_y(cpu) };
  87
  88        rc = notifier_add_irq(hp, irq);
  89        if (rc)
  90                return rc;
  91
  92        /*
  93         * Request that the hypervisor start sending us interrupts.
  94         * If the hypervisor returns an error, we still return 0, so that
  95         * we can fall back to polling.
  96         */
  97        if (hv_console_set_ipi(KERNEL_PL, irq, coord) < 0)
  98                notifier_del_irq(hp, irq);
  99
 100        return 0;
 101}
 102
 103static void hvc_tile_notifier_del_irq(struct hvc_struct *hp, int irq)
 104{
 105        HV_Coord coord = { 0, 0 };
 106
 107        /* Tell the hypervisor to stop sending us interrupts. */
 108        hv_console_set_ipi(KERNEL_PL, -1, coord);
 109
 110        notifier_del_irq(hp, irq);
 111}
 112
 113static void hvc_tile_notifier_hangup_irq(struct hvc_struct *hp, int irq)
 114{
 115        hvc_tile_notifier_del_irq(hp, irq);
 116}
 117#endif
 118
 119static const struct hv_ops hvc_tile_get_put_ops = {
 120        .get_chars = hvc_tile_get_chars,
 121        .put_chars = hvc_tile_put_chars,
 122#ifdef __tilegx__
 123        .notifier_add = hvc_tile_notifier_add_irq,
 124        .notifier_del = hvc_tile_notifier_del_irq,
 125        .notifier_hangup = hvc_tile_notifier_hangup_irq,
 126#endif
 127};
 128
 129
 130#ifdef __tilegx__
 131static int hvc_tile_probe(struct platform_device *pdev)
 132{
 133        struct hvc_struct *hp;
 134        int tile_hvc_irq;
 135
 136        /* Create our IRQ and register it. */
 137        tile_hvc_irq = irq_alloc_hwirq(-1);
 138        if (!tile_hvc_irq)
 139                return -ENXIO;
 140
 141        tile_irq_activate(tile_hvc_irq, TILE_IRQ_PERCPU);
 142        hp = hvc_alloc(0, tile_hvc_irq, &hvc_tile_get_put_ops, 128);
 143        if (IS_ERR(hp)) {
 144                irq_free_hwirq(tile_hvc_irq);
 145                return PTR_ERR(hp);
 146        }
 147        dev_set_drvdata(&pdev->dev, hp);
 148
 149        return 0;
 150}
 151
 152static int hvc_tile_remove(struct platform_device *pdev)
 153{
 154        int rc;
 155        struct hvc_struct *hp = dev_get_drvdata(&pdev->dev);
 156
 157        rc = hvc_remove(hp);
 158        if (rc == 0)
 159                irq_free_hwirq(hp->data);
 160
 161        return rc;
 162}
 163
 164static void hvc_tile_shutdown(struct platform_device *pdev)
 165{
 166        struct hvc_struct *hp = dev_get_drvdata(&pdev->dev);
 167
 168        hvc_tile_notifier_del_irq(hp, hp->data);
 169}
 170
 171static struct platform_device hvc_tile_pdev = {
 172        .name           = "hvc-tile",
 173        .id             = 0,
 174};
 175
 176static struct platform_driver hvc_tile_driver = {
 177        .probe          = hvc_tile_probe,
 178        .remove         = hvc_tile_remove,
 179        .shutdown       = hvc_tile_shutdown,
 180        .driver         = {
 181                .name   = "hvc-tile",
 182        }
 183};
 184#endif
 185
 186static int __init hvc_tile_console_init(void)
 187{
 188        hvc_instantiate(0, 0, &hvc_tile_get_put_ops);
 189        add_preferred_console("hvc", 0, NULL);
 190        return 0;
 191}
 192console_initcall(hvc_tile_console_init);
 193
 194static int __init hvc_tile_init(void)
 195{
 196#ifndef __tilegx__
 197        struct hvc_struct *hp;
 198        hp = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128);
 199        return PTR_ERR_OR_ZERO(hp);
 200#else
 201        platform_device_register(&hvc_tile_pdev);
 202        return platform_driver_register(&hvc_tile_driver);
 203#endif
 204}
 205device_initcall(hvc_tile_init);
 206