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