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