linux/drivers/char/tb0219.c
<<
>>
Prefs
   1/*
   2 *  Driver for TANBAC TB0219 base board.
   3 *
   4 *  Copyright (C) 2005  Yoichi Yuasa <yuasa@linux-mips.org>
   5 *
   6 *  This program is free software; you can redistribute it and/or modify
   7 *  it under the terms of the GNU General Public License as published by
   8 *  the Free Software Foundation; either version 2 of the License, or
   9 *  (at your option) any later version.
  10 *
  11 *  This program is distributed in the hope that it will be useful,
  12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *  GNU General Public License for more details.
  15 *
  16 *  You should have received a copy of the GNU General Public License
  17 *  along with this program; if not, write to the Free Software
  18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19 */
  20#include <linux/platform_device.h>
  21#include <linux/fs.h>
  22#include <linux/init.h>
  23#include <linux/module.h>
  24#include <linux/smp_lock.h>
  25
  26#include <asm/io.h>
  27#include <asm/reboot.h>
  28#include <asm/vr41xx/giu.h>
  29#include <asm/vr41xx/tb0219.h>
  30
  31MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
  32MODULE_DESCRIPTION("TANBAC TB0219 base board driver");
  33MODULE_LICENSE("GPL");
  34
  35static int major;       /* default is dynamic major device number */
  36module_param(major, int, 0);
  37MODULE_PARM_DESC(major, "Major device number");
  38
  39static void (*old_machine_restart)(char *command);
  40static void __iomem *tb0219_base;
  41static spinlock_t tb0219_lock;
  42
  43#define tb0219_read(offset)             readw(tb0219_base + (offset))
  44#define tb0219_write(offset, value)     writew((value), tb0219_base + (offset))
  45
  46#define TB0219_START    0x0a000000UL
  47#define TB0219_SIZE     0x20UL
  48
  49#define TB0219_LED                      0x00
  50#define TB0219_GPIO_INPUT               0x02
  51#define TB0219_GPIO_OUTPUT              0x04
  52#define TB0219_DIP_SWITCH               0x06
  53#define TB0219_MISC                     0x08
  54#define TB0219_RESET                    0x0e
  55#define TB0219_PCI_SLOT1_IRQ_STATUS     0x10
  56#define TB0219_PCI_SLOT2_IRQ_STATUS     0x12
  57#define TB0219_PCI_SLOT3_IRQ_STATUS     0x14
  58
  59typedef enum {
  60        TYPE_LED,
  61        TYPE_GPIO_OUTPUT,
  62} tb0219_type_t;
  63
  64/*
  65 * Minor device number
  66 *       0 = 7 segment LED
  67 *
  68 *      16 = GPIO IN 0
  69 *      17 = GPIO IN 1
  70 *      18 = GPIO IN 2
  71 *      19 = GPIO IN 3
  72 *      20 = GPIO IN 4
  73 *      21 = GPIO IN 5
  74 *      22 = GPIO IN 6
  75 *      23 = GPIO IN 7
  76 *
  77 *      32 = GPIO OUT 0
  78 *      33 = GPIO OUT 1
  79 *      34 = GPIO OUT 2
  80 *      35 = GPIO OUT 3
  81 *      36 = GPIO OUT 4
  82 *      37 = GPIO OUT 5
  83 *      38 = GPIO OUT 6
  84 *      39 = GPIO OUT 7
  85 *
  86 *      48 = DIP switch 1
  87 *      49 = DIP switch 2
  88 *      50 = DIP switch 3
  89 *      51 = DIP switch 4
  90 *      52 = DIP switch 5
  91 *      53 = DIP switch 6
  92 *      54 = DIP switch 7
  93 *      55 = DIP switch 8
  94 */
  95
  96static inline char get_led(void)
  97{
  98        return (char)tb0219_read(TB0219_LED);
  99}
 100
 101static inline char get_gpio_input_pin(unsigned int pin)
 102{
 103        uint16_t values;
 104
 105        values = tb0219_read(TB0219_GPIO_INPUT);
 106        if (values & (1 << pin))
 107                return '1';
 108
 109        return '0';
 110}
 111
 112static inline char get_gpio_output_pin(unsigned int pin)
 113{
 114        uint16_t values;
 115
 116        values = tb0219_read(TB0219_GPIO_OUTPUT);
 117        if (values & (1 << pin))
 118                return '1';
 119
 120        return '0';
 121}
 122
 123static inline char get_dip_switch(unsigned int pin)
 124{
 125        uint16_t values;
 126
 127        values = tb0219_read(TB0219_DIP_SWITCH);
 128        if (values & (1 << pin))
 129                return '1';
 130
 131        return '0';
 132}
 133
 134static inline int set_led(char command)
 135{
 136        tb0219_write(TB0219_LED, command);
 137
 138        return 0;
 139}
 140
 141static inline int set_gpio_output_pin(unsigned int pin, char command)
 142{
 143        unsigned long flags;
 144        uint16_t value;
 145
 146        if (command != '0' && command != '1')
 147                return -EINVAL;
 148
 149        spin_lock_irqsave(&tb0219_lock, flags);
 150        value = tb0219_read(TB0219_GPIO_OUTPUT);
 151        if (command == '0')
 152                value &= ~(1 << pin);
 153        else
 154                value |= 1 << pin;
 155        tb0219_write(TB0219_GPIO_OUTPUT, value);
 156        spin_unlock_irqrestore(&tb0219_lock, flags);
 157
 158        return 0;
 159
 160}
 161
 162static ssize_t tanbac_tb0219_read(struct file *file, char __user *buf, size_t len,
 163                                  loff_t *ppos)
 164{
 165        unsigned int minor;
 166        char value;
 167
 168        minor = iminor(file->f_path.dentry->d_inode);
 169        switch (minor) {
 170        case 0:
 171                value = get_led();
 172                break;
 173        case 16 ... 23:
 174                value = get_gpio_input_pin(minor - 16);
 175                break;
 176        case 32 ... 39:
 177                value = get_gpio_output_pin(minor - 32);
 178                break;
 179        case 48 ... 55:
 180                value = get_dip_switch(minor - 48);
 181                break;
 182        default:
 183                return -EBADF;
 184        }
 185
 186        if (len <= 0)
 187                return -EFAULT;
 188
 189        if (put_user(value, buf))
 190                return -EFAULT;
 191
 192        return 1;
 193}
 194
 195static ssize_t tanbac_tb0219_write(struct file *file, const char __user *data,
 196                                   size_t len, loff_t *ppos)
 197{
 198        unsigned int minor;
 199        tb0219_type_t type;
 200        size_t i;
 201        int retval = 0;
 202        char c;
 203
 204        minor = iminor(file->f_path.dentry->d_inode);
 205        switch (minor) {
 206        case 0:
 207                type = TYPE_LED;
 208                break;
 209        case 32 ... 39:
 210                type = TYPE_GPIO_OUTPUT;
 211                break;
 212        default:
 213                return -EBADF;
 214        }
 215
 216        for (i = 0; i < len; i++) {
 217                if (get_user(c, data + i))
 218                        return -EFAULT;
 219
 220                switch (type) {
 221                case TYPE_LED:
 222                        retval = set_led(c);
 223                        break;
 224                case TYPE_GPIO_OUTPUT:
 225                        retval = set_gpio_output_pin(minor - 32, c);
 226                        break;
 227                }
 228
 229                if (retval < 0)
 230                        break;
 231        }
 232
 233        return i;
 234}
 235
 236static int tanbac_tb0219_open(struct inode *inode, struct file *file)
 237{
 238        unsigned int minor;
 239
 240        cycle_kernel_lock();
 241        minor = iminor(inode);
 242        switch (minor) {
 243        case 0:
 244        case 16 ... 23:
 245        case 32 ... 39:
 246        case 48 ... 55:
 247                return nonseekable_open(inode, file);
 248        default:
 249                break;
 250        }
 251
 252        return -EBADF;
 253}
 254
 255static int tanbac_tb0219_release(struct inode *inode, struct file *file)
 256{
 257        return 0;
 258}
 259
 260static const struct file_operations tb0219_fops = {
 261        .owner          = THIS_MODULE,
 262        .read           = tanbac_tb0219_read,
 263        .write          = tanbac_tb0219_write,
 264        .open           = tanbac_tb0219_open,
 265        .release        = tanbac_tb0219_release,
 266};
 267
 268static void tb0219_restart(char *command)
 269{
 270        tb0219_write(TB0219_RESET, 0);
 271}
 272
 273static void tb0219_pci_irq_init(void)
 274{
 275        /* PCI Slot 1 */
 276        vr41xx_set_irq_trigger(TB0219_PCI_SLOT1_PIN, IRQ_TRIGGER_LEVEL, IRQ_SIGNAL_THROUGH);
 277        vr41xx_set_irq_level(TB0219_PCI_SLOT1_PIN, IRQ_LEVEL_LOW);
 278
 279        /* PCI Slot 2 */
 280        vr41xx_set_irq_trigger(TB0219_PCI_SLOT2_PIN, IRQ_TRIGGER_LEVEL, IRQ_SIGNAL_THROUGH);
 281        vr41xx_set_irq_level(TB0219_PCI_SLOT2_PIN, IRQ_LEVEL_LOW);
 282
 283        /* PCI Slot 3 */
 284        vr41xx_set_irq_trigger(TB0219_PCI_SLOT3_PIN, IRQ_TRIGGER_LEVEL, IRQ_SIGNAL_THROUGH);
 285        vr41xx_set_irq_level(TB0219_PCI_SLOT3_PIN, IRQ_LEVEL_LOW);
 286}
 287
 288static int __devinit tb0219_probe(struct platform_device *dev)
 289{
 290        int retval;
 291
 292        if (request_mem_region(TB0219_START, TB0219_SIZE, "TB0219") == NULL)
 293                return -EBUSY;
 294
 295        tb0219_base = ioremap(TB0219_START, TB0219_SIZE);
 296        if (tb0219_base == NULL) {
 297                release_mem_region(TB0219_START, TB0219_SIZE);
 298                return -ENOMEM;
 299        }
 300
 301        retval = register_chrdev(major, "TB0219", &tb0219_fops);
 302        if (retval < 0) {
 303                iounmap(tb0219_base);
 304                tb0219_base = NULL;
 305                release_mem_region(TB0219_START, TB0219_SIZE);
 306                return retval;
 307        }
 308
 309        spin_lock_init(&tb0219_lock);
 310
 311        old_machine_restart = _machine_restart;
 312        _machine_restart = tb0219_restart;
 313
 314        tb0219_pci_irq_init();
 315
 316        if (major == 0) {
 317                major = retval;
 318                printk(KERN_INFO "TB0219: major number %d\n", major);
 319        }
 320
 321        return 0;
 322}
 323
 324static int __devexit tb0219_remove(struct platform_device *dev)
 325{
 326        _machine_restart = old_machine_restart;
 327
 328        iounmap(tb0219_base);
 329        tb0219_base = NULL;
 330
 331        release_mem_region(TB0219_START, TB0219_SIZE);
 332
 333        return 0;
 334}
 335
 336static struct platform_device *tb0219_platform_device;
 337
 338static struct platform_driver tb0219_device_driver = {
 339        .probe          = tb0219_probe,
 340        .remove         = __devexit_p(tb0219_remove),
 341        .driver         = {
 342                .name   = "TB0219",
 343                .owner  = THIS_MODULE,
 344        },
 345};
 346
 347static int __init tanbac_tb0219_init(void)
 348{
 349        int retval;
 350
 351        tb0219_platform_device = platform_device_alloc("TB0219", -1);
 352        if (!tb0219_platform_device)
 353                return -ENOMEM;
 354
 355        retval = platform_device_add(tb0219_platform_device);
 356        if (retval < 0) {
 357                platform_device_put(tb0219_platform_device);
 358                return retval;
 359        }
 360
 361        retval = platform_driver_register(&tb0219_device_driver);
 362        if (retval < 0)
 363                platform_device_unregister(tb0219_platform_device);
 364
 365        return retval;
 366}
 367
 368static void __exit tanbac_tb0219_exit(void)
 369{
 370        platform_driver_unregister(&tb0219_device_driver);
 371        platform_device_unregister(tb0219_platform_device);
 372}
 373
 374module_init(tanbac_tb0219_init);
 375module_exit(tanbac_tb0219_exit);
 376