Commit 800e69fbd2faea906cabd10ddb77e36410f2bd9c

Authored by Ira W. Snyder
Committed by Samuel Ortiz
1 parent 631eb22784

gpio: Add support for Janz VMOD-TTL Digital IO module

The Janz VMOD-TTL is a MODULbus daughterboard which fits onto any MODULbus
carrier board. It essentially consists of some various logic and a Zilog
Z8536 CIO Counter/Timer and Parallel IO Unit.

The board must be physically configured with jumpers to enable a user to
drive output signals. I am only interested in outputs, so I have made this
driver as simple as possible. It only supports a very minimal subset of the
features provided by the Z8536 chip.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

Showing 3 changed files with 269 additions and 0 deletions Side-by-side Diff

drivers/gpio/Kconfig
... ... @@ -325,5 +325,15 @@
325 325 To compile this driver as a module, choose M here: the
326 326 module will be called ucb1400_gpio.
327 327  
  328 +comment "MODULbus GPIO expanders:"
  329 +
  330 +config GPIO_JANZ_TTL
  331 + tristate "Janz VMOD-TTL Digital IO Module"
  332 + depends on MFD_JANZ_CMODIO
  333 + help
  334 + This enables support for the Janz VMOD-TTL Digital IO module.
  335 + This driver provides support for driving the pins in output
  336 + mode only. Input mode is not supported.
  337 +
328 338 endif
drivers/gpio/Makefile
... ... @@ -29,4 +29,5 @@
29 29 obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o
30 30 obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
31 31 obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o
  32 +obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
drivers/gpio/janz-ttl.c
  1 +/*
  2 + * Janz MODULbus VMOD-TTL GPIO Driver
  3 + *
  4 + * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify it
  7 + * under the terms of the GNU General Public License as published by the
  8 + * Free Software Foundation; either version 2 of the License, or (at your
  9 + * option) any later version.
  10 + */
  11 +
  12 +#include <linux/kernel.h>
  13 +#include <linux/module.h>
  14 +#include <linux/init.h>
  15 +#include <linux/interrupt.h>
  16 +#include <linux/delay.h>
  17 +#include <linux/platform_device.h>
  18 +#include <linux/io.h>
  19 +#include <linux/gpio.h>
  20 +#include <linux/slab.h>
  21 +
  22 +#include <linux/mfd/janz.h>
  23 +
  24 +#define DRV_NAME "janz-ttl"
  25 +
  26 +#define PORTA_DIRECTION 0x23
  27 +#define PORTB_DIRECTION 0x2B
  28 +#define PORTC_DIRECTION 0x06
  29 +#define PORTA_IOCTL 0x24
  30 +#define PORTB_IOCTL 0x2C
  31 +#define PORTC_IOCTL 0x07
  32 +
  33 +#define MASTER_INT_CTL 0x00
  34 +#define MASTER_CONF_CTL 0x01
  35 +
  36 +#define CONF_PAE (1 << 2)
  37 +#define CONF_PBE (1 << 7)
  38 +#define CONF_PCE (1 << 4)
  39 +
  40 +struct ttl_control_regs {
  41 + __be16 portc;
  42 + __be16 portb;
  43 + __be16 porta;
  44 + __be16 control;
  45 +};
  46 +
  47 +struct ttl_module {
  48 + struct gpio_chip gpio;
  49 +
  50 + /* base address of registers */
  51 + struct ttl_control_regs __iomem *regs;
  52 +
  53 + u8 portc_shadow;
  54 + u8 portb_shadow;
  55 + u8 porta_shadow;
  56 +
  57 + spinlock_t lock;
  58 +};
  59 +
  60 +static int ttl_get_value(struct gpio_chip *gpio, unsigned offset)
  61 +{
  62 + struct ttl_module *mod = dev_get_drvdata(gpio->dev);
  63 + u8 *shadow;
  64 + int ret;
  65 +
  66 + if (offset < 8) {
  67 + shadow = &mod->porta_shadow;
  68 + } else if (offset < 16) {
  69 + shadow = &mod->portb_shadow;
  70 + offset -= 8;
  71 + } else {
  72 + shadow = &mod->portc_shadow;
  73 + offset -= 16;
  74 + }
  75 +
  76 + spin_lock(&mod->lock);
  77 + ret = *shadow & (1 << offset);
  78 + spin_unlock(&mod->lock);
  79 + return ret;
  80 +}
  81 +
  82 +static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value)
  83 +{
  84 + struct ttl_module *mod = dev_get_drvdata(gpio->dev);
  85 + void __iomem *port;
  86 + u8 *shadow;
  87 +
  88 + if (offset < 8) {
  89 + port = &mod->regs->porta;
  90 + shadow = &mod->porta_shadow;
  91 + } else if (offset < 16) {
  92 + port = &mod->regs->portb;
  93 + shadow = &mod->portb_shadow;
  94 + offset -= 8;
  95 + } else {
  96 + port = &mod->regs->portc;
  97 + shadow = &mod->portc_shadow;
  98 + offset -= 16;
  99 + }
  100 +
  101 + spin_lock(&mod->lock);
  102 + if (value)
  103 + *shadow |= (1 << offset);
  104 + else
  105 + *shadow &= ~(1 << offset);
  106 +
  107 + iowrite16be(*shadow, port);
  108 + spin_unlock(&mod->lock);
  109 +}
  110 +
  111 +static void __devinit ttl_write_reg(struct ttl_module *mod, u8 reg, u16 val)
  112 +{
  113 + iowrite16be(reg, &mod->regs->control);
  114 + iowrite16be(val, &mod->regs->control);
  115 +}
  116 +
  117 +static void __devinit ttl_setup_device(struct ttl_module *mod)
  118 +{
  119 + /* reset the device to a known state */
  120 + iowrite16be(0x0000, &mod->regs->control);
  121 + iowrite16be(0x0001, &mod->regs->control);
  122 + iowrite16be(0x0000, &mod->regs->control);
  123 +
  124 + /* put all ports in open-drain mode */
  125 + ttl_write_reg(mod, PORTA_IOCTL, 0x00ff);
  126 + ttl_write_reg(mod, PORTB_IOCTL, 0x00ff);
  127 + ttl_write_reg(mod, PORTC_IOCTL, 0x000f);
  128 +
  129 + /* set all ports as outputs */
  130 + ttl_write_reg(mod, PORTA_DIRECTION, 0x0000);
  131 + ttl_write_reg(mod, PORTB_DIRECTION, 0x0000);
  132 + ttl_write_reg(mod, PORTC_DIRECTION, 0x0000);
  133 +
  134 + /* set all ports to drive zeroes */
  135 + iowrite16be(0x0000, &mod->regs->porta);
  136 + iowrite16be(0x0000, &mod->regs->portb);
  137 + iowrite16be(0x0000, &mod->regs->portc);
  138 +
  139 + /* enable all ports */
  140 + ttl_write_reg(mod, MASTER_CONF_CTL, CONF_PAE | CONF_PBE | CONF_PCE);
  141 +}
  142 +
  143 +static int __devinit ttl_probe(struct platform_device *pdev)
  144 +{
  145 + struct janz_platform_data *pdata;
  146 + struct device *dev = &pdev->dev;
  147 + struct ttl_module *mod;
  148 + struct gpio_chip *gpio;
  149 + struct resource *res;
  150 + int ret;
  151 +
  152 + pdata = pdev->dev.platform_data;
  153 + if (!pdata) {
  154 + dev_err(dev, "no platform data\n");
  155 + ret = -ENXIO;
  156 + goto out_return;
  157 + }
  158 +
  159 + mod = kzalloc(sizeof(*mod), GFP_KERNEL);
  160 + if (!mod) {
  161 + dev_err(dev, "unable to allocate private data\n");
  162 + ret = -ENOMEM;
  163 + goto out_return;
  164 + }
  165 +
  166 + platform_set_drvdata(pdev, mod);
  167 + spin_lock_init(&mod->lock);
  168 +
  169 + /* get access to the MODULbus registers for this module */
  170 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  171 + if (!res) {
  172 + dev_err(dev, "MODULbus registers not found\n");
  173 + ret = -ENODEV;
  174 + goto out_free_mod;
  175 + }
  176 +
  177 + mod->regs = ioremap(res->start, resource_size(res));
  178 + if (!mod->regs) {
  179 + dev_err(dev, "MODULbus registers not ioremap\n");
  180 + ret = -ENOMEM;
  181 + goto out_free_mod;
  182 + }
  183 +
  184 + ttl_setup_device(mod);
  185 +
  186 + /* Initialize the GPIO data structures */
  187 + gpio = &mod->gpio;
  188 + gpio->dev = &pdev->dev;
  189 + gpio->label = pdev->name;
  190 + gpio->get = ttl_get_value;
  191 + gpio->set = ttl_set_value;
  192 + gpio->owner = THIS_MODULE;
  193 +
  194 + /* request dynamic allocation */
  195 + gpio->base = -1;
  196 + gpio->ngpio = 20;
  197 +
  198 + ret = gpiochip_add(gpio);
  199 + if (ret) {
  200 + dev_err(dev, "unable to add GPIO chip\n");
  201 + goto out_iounmap_regs;
  202 + }
  203 +
  204 + dev_info(&pdev->dev, "module %d: registered GPIO device\n",
  205 + pdata->modno);
  206 + return 0;
  207 +
  208 +out_iounmap_regs:
  209 + iounmap(mod->regs);
  210 +out_free_mod:
  211 + kfree(mod);
  212 +out_return:
  213 + return ret;
  214 +}
  215 +
  216 +static int __devexit ttl_remove(struct platform_device *pdev)
  217 +{
  218 + struct ttl_module *mod = platform_get_drvdata(pdev);
  219 + struct device *dev = &pdev->dev;
  220 + int ret;
  221 +
  222 + ret = gpiochip_remove(&mod->gpio);
  223 + if (ret) {
  224 + dev_err(dev, "unable to remove GPIO chip\n");
  225 + return ret;
  226 + }
  227 +
  228 + iounmap(mod->regs);
  229 + kfree(mod);
  230 + return 0;
  231 +}
  232 +
  233 +static struct platform_driver ttl_driver = {
  234 + .driver = {
  235 + .name = DRV_NAME,
  236 + .owner = THIS_MODULE,
  237 + },
  238 + .probe = ttl_probe,
  239 + .remove = __devexit_p(ttl_remove),
  240 +};
  241 +
  242 +static int __init ttl_init(void)
  243 +{
  244 + return platform_driver_register(&ttl_driver);
  245 +}
  246 +
  247 +static void __exit ttl_exit(void)
  248 +{
  249 + platform_driver_unregister(&ttl_driver);
  250 +}
  251 +
  252 +MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
  253 +MODULE_DESCRIPTION("Janz MODULbus VMOD-TTL Driver");
  254 +MODULE_LICENSE("GPL");
  255 +MODULE_ALIAS("platform:janz-ttl");
  256 +
  257 +module_init(ttl_init);
  258 +module_exit(ttl_exit);