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