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