linux/arch/mips/kernel/rtlx.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2005 MIPS Technologies, Inc.  All rights reserved.
   3 * Copyright (C) 2005, 06 Ralf Baechle (ralf@linux-mips.org)
   4 *
   5 *  This program is free software; you can distribute it and/or modify it
   6 *  under the terms of the GNU General Public License (Version 2) as
   7 *  published by the Free Software Foundation.
   8 *
   9 *  This program is distributed in the hope it will be useful, but WITHOUT
  10 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12 *  for more details.
  13 *
  14 *  You should have received a copy of the GNU General Public License along
  15 *  with this program; if not, write to the Free Software Foundation, Inc.,
  16 *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
  17 *
  18 */
  19
  20#include <linux/device.h>
  21#include <linux/kernel.h>
  22#include <linux/fs.h>
  23#include <linux/init.h>
  24#include <asm/uaccess.h>
  25#include <linux/list.h>
  26#include <linux/vmalloc.h>
  27#include <linux/elf.h>
  28#include <linux/seq_file.h>
  29#include <linux/syscalls.h>
  30#include <linux/moduleloader.h>
  31#include <linux/interrupt.h>
  32#include <linux/poll.h>
  33#include <linux/sched.h>
  34#include <linux/wait.h>
  35#include <asm/mipsmtregs.h>
  36#include <asm/mips_mt.h>
  37#include <asm/cacheflush.h>
  38#include <linux/atomic.h>
  39#include <asm/cpu.h>
  40#include <asm/processor.h>
  41#include <asm/vpe.h>
  42#include <asm/rtlx.h>
  43#include <asm/setup.h>
  44
  45static struct rtlx_info *rtlx;
  46static int major;
  47static char module_name[] = "rtlx";
  48
  49static struct chan_waitqueues {
  50        wait_queue_head_t rt_queue;
  51        wait_queue_head_t lx_queue;
  52        atomic_t in_open;
  53        struct mutex mutex;
  54} channel_wqs[RTLX_CHANNELS];
  55
  56static struct vpe_notifications notify;
  57static int sp_stopping;
  58
  59extern void *vpe_get_shared(int index);
  60
  61static void rtlx_dispatch(void)
  62{
  63        do_IRQ(MIPS_CPU_IRQ_BASE + MIPS_CPU_RTLX_IRQ);
  64}
  65
  66
  67/* Interrupt handler may be called before rtlx_init has otherwise had
  68   a chance to run.
  69*/
  70static irqreturn_t rtlx_interrupt(int irq, void *dev_id)
  71{
  72        unsigned int vpeflags;
  73        unsigned long flags;
  74        int i;
  75
  76        /* Ought not to be strictly necessary for SMTC builds */
  77        local_irq_save(flags);
  78        vpeflags = dvpe();
  79        set_c0_status(0x100 << MIPS_CPU_RTLX_IRQ);
  80        irq_enable_hazard();
  81        evpe(vpeflags);
  82        local_irq_restore(flags);
  83
  84        for (i = 0; i < RTLX_CHANNELS; i++) {
  85                        wake_up(&channel_wqs[i].lx_queue);
  86                        wake_up(&channel_wqs[i].rt_queue);
  87        }
  88
  89        return IRQ_HANDLED;
  90}
  91
  92static void __used dump_rtlx(void)
  93{
  94        int i;
  95
  96        printk("id 0x%lx state %d\n", rtlx->id, rtlx->state);
  97
  98        for (i = 0; i < RTLX_CHANNELS; i++) {
  99                struct rtlx_channel *chan = &rtlx->channel[i];
 100
 101                printk(" rt_state %d lx_state %d buffer_size %d\n",
 102                       chan->rt_state, chan->lx_state, chan->buffer_size);
 103
 104                printk(" rt_read %d rt_write %d\n",
 105                       chan->rt_read, chan->rt_write);
 106
 107                printk(" lx_read %d lx_write %d\n",
 108                       chan->lx_read, chan->lx_write);
 109
 110                printk(" rt_buffer <%s>\n", chan->rt_buffer);
 111                printk(" lx_buffer <%s>\n", chan->lx_buffer);
 112        }
 113}
 114
 115/* call when we have the address of the shared structure from the SP side. */
 116static int rtlx_init(struct rtlx_info *rtlxi)
 117{
 118        if (rtlxi->id != RTLX_ID) {
 119                printk(KERN_ERR "no valid RTLX id at 0x%p 0x%lx\n",
 120                        rtlxi, rtlxi->id);
 121                return -ENOEXEC;
 122        }
 123
 124        rtlx = rtlxi;
 125
 126        return 0;
 127}
 128
 129/* notifications */
 130static void starting(int vpe)
 131{
 132        int i;
 133        sp_stopping = 0;
 134
 135        /* force a reload of rtlx */
 136        rtlx=NULL;
 137
 138        /* wake up any sleeping rtlx_open's */
 139        for (i = 0; i < RTLX_CHANNELS; i++)
 140                wake_up_interruptible(&channel_wqs[i].lx_queue);
 141}
 142
 143static void stopping(int vpe)
 144{
 145        int i;
 146
 147        sp_stopping = 1;
 148        for (i = 0; i < RTLX_CHANNELS; i++)
 149                wake_up_interruptible(&channel_wqs[i].lx_queue);
 150}
 151
 152
 153int rtlx_open(int index, int can_sleep)
 154{
 155        struct rtlx_info **p;
 156        struct rtlx_channel *chan;
 157        enum rtlx_state state;
 158        int ret = 0;
 159
 160        if (index >= RTLX_CHANNELS) {
 161                printk(KERN_DEBUG "rtlx_open index out of range\n");
 162                return -ENOSYS;
 163        }
 164
 165        if (atomic_inc_return(&channel_wqs[index].in_open) > 1) {
 166                printk(KERN_DEBUG "rtlx_open channel %d already opened\n",
 167                       index);
 168                ret = -EBUSY;
 169                goto out_fail;
 170        }
 171
 172        if (rtlx == NULL) {
 173                if( (p = vpe_get_shared(tclimit)) == NULL) {
 174                    if (can_sleep) {
 175                        __wait_event_interruptible(channel_wqs[index].lx_queue,
 176                                (p = vpe_get_shared(tclimit)), ret);
 177                        if (ret)
 178                                goto out_fail;
 179                    } else {
 180                        printk(KERN_DEBUG "No SP program loaded, and device "
 181                                        "opened with O_NONBLOCK\n");
 182                        ret = -ENOSYS;
 183                        goto out_fail;
 184                    }
 185                }
 186
 187                smp_rmb();
 188                if (*p == NULL) {
 189                        if (can_sleep) {
 190                                DEFINE_WAIT(wait);
 191
 192                                for (;;) {
 193                                        prepare_to_wait(
 194                                                &channel_wqs[index].lx_queue,
 195                                                &wait, TASK_INTERRUPTIBLE);
 196                                        smp_rmb();
 197                                        if (*p != NULL)
 198                                                break;
 199                                        if (!signal_pending(current)) {
 200                                                schedule();
 201                                                continue;
 202                                        }
 203                                        ret = -ERESTARTSYS;
 204                                        goto out_fail;
 205                                }
 206                                finish_wait(&channel_wqs[index].lx_queue, &wait);
 207                        } else {
 208                                pr_err(" *vpe_get_shared is NULL. "
 209                                       "Has an SP program been loaded?\n");
 210                                ret = -ENOSYS;
 211                                goto out_fail;
 212                        }
 213                }
 214
 215                if ((unsigned int)*p < KSEG0) {
 216                        printk(KERN_WARNING "vpe_get_shared returned an "
 217                               "invalid pointer maybe an error code %d\n",
 218                               (int)*p);
 219                        ret = -ENOSYS;
 220                        goto out_fail;
 221                }
 222
 223                if ((ret = rtlx_init(*p)) < 0)
 224                        goto out_ret;
 225        }
 226
 227        chan = &rtlx->channel[index];
 228
 229        state = xchg(&chan->lx_state, RTLX_STATE_OPENED);
 230        if (state == RTLX_STATE_OPENED) {
 231                ret = -EBUSY;
 232                goto out_fail;
 233        }
 234
 235out_fail:
 236        smp_mb();
 237        atomic_dec(&channel_wqs[index].in_open);
 238        smp_mb();
 239
 240out_ret:
 241        return ret;
 242}
 243
 244int rtlx_release(int index)
 245{
 246        if (rtlx == NULL) {
 247                pr_err("rtlx_release() with null rtlx\n");
 248                return 0;
 249        }
 250        rtlx->channel[index].lx_state = RTLX_STATE_UNUSED;
 251        return 0;
 252}
 253
 254unsigned int rtlx_read_poll(int index, int can_sleep)
 255{
 256        struct rtlx_channel *chan;
 257
 258        if (rtlx == NULL)
 259                return 0;
 260
 261        chan = &rtlx->channel[index];
 262
 263        /* data available to read? */
 264        if (chan->lx_read == chan->lx_write) {
 265                if (can_sleep) {
 266                        int ret = 0;
 267
 268                        __wait_event_interruptible(channel_wqs[index].lx_queue,
 269                                (chan->lx_read != chan->lx_write) ||
 270                                sp_stopping, ret);
 271                        if (ret)
 272                                return ret;
 273
 274                        if (sp_stopping)
 275                                return 0;
 276                } else
 277                        return 0;
 278        }
 279
 280        return (chan->lx_write + chan->buffer_size - chan->lx_read)
 281               % chan->buffer_size;
 282}
 283
 284static inline int write_spacefree(int read, int write, int size)
 285{
 286        if (read == write) {
 287                /*
 288                 * Never fill the buffer completely, so indexes are always
 289                 * equal if empty and only empty, or !equal if data available
 290                 */
 291                return size - 1;
 292        }
 293
 294        return ((read + size - write) % size) - 1;
 295}
 296
 297unsigned int rtlx_write_poll(int index)
 298{
 299        struct rtlx_channel *chan = &rtlx->channel[index];
 300
 301        return write_spacefree(chan->rt_read, chan->rt_write,
 302                                chan->buffer_size);
 303}
 304
 305ssize_t rtlx_read(int index, void __user *buff, size_t count)
 306{
 307        size_t lx_write, fl = 0L;
 308        struct rtlx_channel *lx;
 309        unsigned long failed;
 310
 311        if (rtlx == NULL)
 312                return -ENOSYS;
 313
 314        lx = &rtlx->channel[index];
 315
 316        mutex_lock(&channel_wqs[index].mutex);
 317        smp_rmb();
 318        lx_write = lx->lx_write;
 319
 320        /* find out how much in total */
 321        count = min(count,
 322                     (size_t)(lx_write + lx->buffer_size - lx->lx_read)
 323                     % lx->buffer_size);
 324
 325        /* then how much from the read pointer onwards */
 326        fl = min(count, (size_t)lx->buffer_size - lx->lx_read);
 327
 328        failed = copy_to_user(buff, lx->lx_buffer + lx->lx_read, fl);
 329        if (failed)
 330                goto out;
 331
 332        /* and if there is anything left at the beginning of the buffer */
 333        if (count - fl)
 334                failed = copy_to_user(buff + fl, lx->lx_buffer, count - fl);
 335
 336out:
 337        count -= failed;
 338
 339        smp_wmb();
 340        lx->lx_read = (lx->lx_read + count) % lx->buffer_size;
 341        smp_wmb();
 342        mutex_unlock(&channel_wqs[index].mutex);
 343
 344        return count;
 345}
 346
 347ssize_t rtlx_write(int index, const void __user *buffer, size_t count)
 348{
 349        struct rtlx_channel *rt;
 350        unsigned long failed;
 351        size_t rt_read;
 352        size_t fl;
 353
 354        if (rtlx == NULL)
 355                return(-ENOSYS);
 356
 357        rt = &rtlx->channel[index];
 358
 359        mutex_lock(&channel_wqs[index].mutex);
 360        smp_rmb();
 361        rt_read = rt->rt_read;
 362
 363        /* total number of bytes to copy */
 364        count = min(count, (size_t)write_spacefree(rt_read, rt->rt_write,
 365                                                        rt->buffer_size));
 366
 367        /* first bit from write pointer to the end of the buffer, or count */
 368        fl = min(count, (size_t) rt->buffer_size - rt->rt_write);
 369
 370        failed = copy_from_user(rt->rt_buffer + rt->rt_write, buffer, fl);
 371        if (failed)
 372                goto out;
 373
 374        /* if there's any left copy to the beginning of the buffer */
 375        if (count - fl) {
 376                failed = copy_from_user(rt->rt_buffer, buffer + fl, count - fl);
 377        }
 378
 379out:
 380        count -= failed;
 381
 382        smp_wmb();
 383        rt->rt_write = (rt->rt_write + count) % rt->buffer_size;
 384        smp_wmb();
 385        mutex_unlock(&channel_wqs[index].mutex);
 386
 387        return count;
 388}
 389
 390
 391static int file_open(struct inode *inode, struct file *filp)
 392{
 393        return rtlx_open(iminor(inode), (filp->f_flags & O_NONBLOCK) ? 0 : 1);
 394}
 395
 396static int file_release(struct inode *inode, struct file *filp)
 397{
 398        return rtlx_release(iminor(inode));
 399}
 400
 401static unsigned int file_poll(struct file *file, poll_table * wait)
 402{
 403        int minor = iminor(file_inode(file));
 404        unsigned int mask = 0;
 405
 406        poll_wait(file, &channel_wqs[minor].rt_queue, wait);
 407        poll_wait(file, &channel_wqs[minor].lx_queue, wait);
 408
 409        if (rtlx == NULL)
 410                return 0;
 411
 412        /* data available to read? */
 413        if (rtlx_read_poll(minor, 0))
 414                mask |= POLLIN | POLLRDNORM;
 415
 416        /* space to write */
 417        if (rtlx_write_poll(minor))
 418                mask |= POLLOUT | POLLWRNORM;
 419
 420        return mask;
 421}
 422
 423static ssize_t file_read(struct file *file, char __user * buffer, size_t count,
 424                         loff_t * ppos)
 425{
 426        int minor = iminor(file_inode(file));
 427
 428        /* data available? */
 429        if (!rtlx_read_poll(minor, (file->f_flags & O_NONBLOCK) ? 0 : 1)) {
 430                return 0;       // -EAGAIN makes cat whinge
 431        }
 432
 433        return rtlx_read(minor, buffer, count);
 434}
 435
 436static ssize_t file_write(struct file *file, const char __user * buffer,
 437                          size_t count, loff_t * ppos)
 438{
 439        int minor = iminor(file_inode(file));
 440        struct rtlx_channel *rt = &rtlx->channel[minor];
 441
 442        /* any space left... */
 443        if (!rtlx_write_poll(minor)) {
 444                int ret = 0;
 445
 446                if (file->f_flags & O_NONBLOCK)
 447                        return -EAGAIN;
 448
 449                __wait_event_interruptible(channel_wqs[minor].rt_queue,
 450                                           rtlx_write_poll(minor),
 451                                           ret);
 452                if (ret)
 453                        return ret;
 454        }
 455
 456        return rtlx_write(minor, buffer, count);
 457}
 458
 459static const struct file_operations rtlx_fops = {
 460        .owner =   THIS_MODULE,
 461        .open =    file_open,
 462        .release = file_release,
 463        .write =   file_write,
 464        .read =    file_read,
 465        .poll =    file_poll,
 466        .llseek =  noop_llseek,
 467};
 468
 469static struct irqaction rtlx_irq = {
 470        .handler        = rtlx_interrupt,
 471        .name           = "RTLX",
 472};
 473
 474static int rtlx_irq_num = MIPS_CPU_IRQ_BASE + MIPS_CPU_RTLX_IRQ;
 475
 476static char register_chrdev_failed[] __initdata =
 477        KERN_ERR "rtlx_module_init: unable to register device\n";
 478
 479static int __init rtlx_module_init(void)
 480{
 481        struct device *dev;
 482        int i, err;
 483
 484        if (!cpu_has_mipsmt) {
 485                printk("VPE loader: not a MIPS MT capable processor\n");
 486                return -ENODEV;
 487        }
 488
 489        if (tclimit == 0) {
 490                printk(KERN_WARNING "No TCs reserved for AP/SP, not "
 491                       "initializing RTLX.\nPass maxtcs=<n> argument as kernel "
 492                       "argument\n");
 493
 494                return -ENODEV;
 495        }
 496
 497        major = register_chrdev(0, module_name, &rtlx_fops);
 498        if (major < 0) {
 499                printk(register_chrdev_failed);
 500                return major;
 501        }
 502
 503        /* initialise the wait queues */
 504        for (i = 0; i < RTLX_CHANNELS; i++) {
 505                init_waitqueue_head(&channel_wqs[i].rt_queue);
 506                init_waitqueue_head(&channel_wqs[i].lx_queue);
 507                atomic_set(&channel_wqs[i].in_open, 0);
 508                mutex_init(&channel_wqs[i].mutex);
 509
 510                dev = device_create(mt_class, NULL, MKDEV(major, i), NULL,
 511                                    "%s%d", module_name, i);
 512                if (IS_ERR(dev)) {
 513                        err = PTR_ERR(dev);
 514                        goto out_chrdev;
 515                }
 516        }
 517
 518        /* set up notifiers */
 519        notify.start = starting;
 520        notify.stop = stopping;
 521        vpe_notify(tclimit, &notify);
 522
 523        if (cpu_has_vint)
 524                set_vi_handler(MIPS_CPU_RTLX_IRQ, rtlx_dispatch);
 525        else {
 526                pr_err("APRP RTLX init on non-vectored-interrupt processor\n");
 527                err = -ENODEV;
 528                goto out_chrdev;
 529        }
 530
 531        rtlx_irq.dev_id = rtlx;
 532        setup_irq(rtlx_irq_num, &rtlx_irq);
 533
 534        return 0;
 535
 536out_chrdev:
 537        for (i = 0; i < RTLX_CHANNELS; i++)
 538                device_destroy(mt_class, MKDEV(major, i));
 539
 540        return err;
 541}
 542
 543static void __exit rtlx_module_exit(void)
 544{
 545        int i;
 546
 547        for (i = 0; i < RTLX_CHANNELS; i++)
 548                device_destroy(mt_class, MKDEV(major, i));
 549
 550        unregister_chrdev(major, module_name);
 551}
 552
 553module_init(rtlx_module_init);
 554module_exit(rtlx_module_exit);
 555
 556MODULE_DESCRIPTION("MIPS RTLX");
 557MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
 558MODULE_LICENSE("GPL");
 559