linux/drivers/gpio/gpio-janz-ttl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Janz MODULbus VMOD-TTL GPIO Driver
   4 *
   5 * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <linux/init.h>
  11#include <linux/interrupt.h>
  12#include <linux/delay.h>
  13#include <linux/platform_device.h>
  14#include <linux/io.h>
  15#include <linux/gpio/driver.h>
  16#include <linux/slab.h>
  17#include <linux/bitops.h>
  18
  19#include <linux/mfd/janz.h>
  20
  21#define DRV_NAME "janz-ttl"
  22
  23#define PORTA_DIRECTION         0x23
  24#define PORTB_DIRECTION         0x2B
  25#define PORTC_DIRECTION         0x06
  26#define PORTA_IOCTL             0x24
  27#define PORTB_IOCTL             0x2C
  28#define PORTC_IOCTL             0x07
  29
  30#define MASTER_INT_CTL          0x00
  31#define MASTER_CONF_CTL         0x01
  32
  33#define CONF_PAE                BIT(2)
  34#define CONF_PBE                BIT(7)
  35#define CONF_PCE                BIT(4)
  36
  37struct ttl_control_regs {
  38        __be16 portc;
  39        __be16 portb;
  40        __be16 porta;
  41        __be16 control;
  42};
  43
  44struct ttl_module {
  45        struct gpio_chip gpio;
  46
  47        /* base address of registers */
  48        struct ttl_control_regs __iomem *regs;
  49
  50        u8 portc_shadow;
  51        u8 portb_shadow;
  52        u8 porta_shadow;
  53
  54        spinlock_t lock;
  55};
  56
  57static int ttl_get_value(struct gpio_chip *gpio, unsigned offset)
  58{
  59        struct ttl_module *mod = dev_get_drvdata(gpio->parent);
  60        u8 *shadow;
  61        int ret;
  62
  63        if (offset < 8) {
  64                shadow = &mod->porta_shadow;
  65        } else if (offset < 16) {
  66                shadow = &mod->portb_shadow;
  67                offset -= 8;
  68        } else {
  69                shadow = &mod->portc_shadow;
  70                offset -= 16;
  71        }
  72
  73        spin_lock(&mod->lock);
  74        ret = *shadow & BIT(offset);
  75        spin_unlock(&mod->lock);
  76        return !!ret;
  77}
  78
  79static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value)
  80{
  81        struct ttl_module *mod = dev_get_drvdata(gpio->parent);
  82        void __iomem *port;
  83        u8 *shadow;
  84
  85        if (offset < 8) {
  86                port = &mod->regs->porta;
  87                shadow = &mod->porta_shadow;
  88        } else if (offset < 16) {
  89                port = &mod->regs->portb;
  90                shadow = &mod->portb_shadow;
  91                offset -= 8;
  92        } else {
  93                port = &mod->regs->portc;
  94                shadow = &mod->portc_shadow;
  95                offset -= 16;
  96        }
  97
  98        spin_lock(&mod->lock);
  99        if (value)
 100                *shadow |= BIT(offset);
 101        else
 102                *shadow &= ~BIT(offset);
 103
 104        iowrite16be(*shadow, port);
 105        spin_unlock(&mod->lock);
 106}
 107
 108static void ttl_write_reg(struct ttl_module *mod, u8 reg, u16 val)
 109{
 110        iowrite16be(reg, &mod->regs->control);
 111        iowrite16be(val, &mod->regs->control);
 112}
 113
 114static void ttl_setup_device(struct ttl_module *mod)
 115{
 116        /* reset the device to a known state */
 117        iowrite16be(0x0000, &mod->regs->control);
 118        iowrite16be(0x0001, &mod->regs->control);
 119        iowrite16be(0x0000, &mod->regs->control);
 120
 121        /* put all ports in open-drain mode */
 122        ttl_write_reg(mod, PORTA_IOCTL, 0x00ff);
 123        ttl_write_reg(mod, PORTB_IOCTL, 0x00ff);
 124        ttl_write_reg(mod, PORTC_IOCTL, 0x000f);
 125
 126        /* set all ports as outputs */
 127        ttl_write_reg(mod, PORTA_DIRECTION, 0x0000);
 128        ttl_write_reg(mod, PORTB_DIRECTION, 0x0000);
 129        ttl_write_reg(mod, PORTC_DIRECTION, 0x0000);
 130
 131        /* set all ports to drive zeroes */
 132        iowrite16be(0x0000, &mod->regs->porta);
 133        iowrite16be(0x0000, &mod->regs->portb);
 134        iowrite16be(0x0000, &mod->regs->portc);
 135
 136        /* enable all ports */
 137        ttl_write_reg(mod, MASTER_CONF_CTL, CONF_PAE | CONF_PBE | CONF_PCE);
 138}
 139
 140static int ttl_probe(struct platform_device *pdev)
 141{
 142        struct janz_platform_data *pdata;
 143        struct ttl_module *mod;
 144        struct gpio_chip *gpio;
 145        int ret;
 146
 147        pdata = dev_get_platdata(&pdev->dev);
 148        if (!pdata) {
 149                dev_err(&pdev->dev, "no platform data\n");
 150                return -ENXIO;
 151        }
 152
 153        mod = devm_kzalloc(&pdev->dev, sizeof(*mod), GFP_KERNEL);
 154        if (!mod)
 155                return -ENOMEM;
 156
 157        platform_set_drvdata(pdev, mod);
 158        spin_lock_init(&mod->lock);
 159
 160        /* get access to the MODULbus registers for this module */
 161        mod->regs = devm_platform_ioremap_resource(pdev, 0);
 162        if (IS_ERR(mod->regs))
 163                return PTR_ERR(mod->regs);
 164
 165        ttl_setup_device(mod);
 166
 167        /* Initialize the GPIO data structures */
 168        gpio = &mod->gpio;
 169        gpio->parent = &pdev->dev;
 170        gpio->label = pdev->name;
 171        gpio->get = ttl_get_value;
 172        gpio->set = ttl_set_value;
 173        gpio->owner = THIS_MODULE;
 174
 175        /* request dynamic allocation */
 176        gpio->base = -1;
 177        gpio->ngpio = 20;
 178
 179        ret = devm_gpiochip_add_data(&pdev->dev, gpio, NULL);
 180        if (ret) {
 181                dev_err(&pdev->dev, "unable to add GPIO chip\n");
 182                return ret;
 183        }
 184
 185        return 0;
 186}
 187
 188static struct platform_driver ttl_driver = {
 189        .driver         = {
 190                .name   = DRV_NAME,
 191        },
 192        .probe          = ttl_probe,
 193};
 194
 195module_platform_driver(ttl_driver);
 196
 197MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
 198MODULE_DESCRIPTION("Janz MODULbus VMOD-TTL Driver");
 199MODULE_LICENSE("GPL");
 200MODULE_ALIAS("platform:janz-ttl");
 201