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