linux/drivers/gpio/gpio-max730x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/**
   3 * Copyright (C) 2006 Juergen Beisert, Pengutronix
   4 * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix
   5 * Copyright (C) 2009 Wolfram Sang, Pengutronix
   6 *
   7 * The Maxim MAX7300/1 device is an I2C/SPI driven GPIO expander. There are
   8 * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more
   9 * details
  10 * Note:
  11 * - DIN must be stable at the rising edge of clock.
  12 * - when writing:
  13 *   - always clock in 16 clocks at once
  14 *   - at DIN: D15 first, D0 last
  15 *   - D0..D7 = databyte, D8..D14 = commandbyte
  16 *   - D15 = low -> write command
  17 * - when reading
  18 *   - always clock in 16 clocks at once
  19 *   - at DIN: D15 first, D0 last
  20 *   - D0..D7 = dummy, D8..D14 = register address
  21 *   - D15 = high -> read command
  22 *   - raise CS and assert it again
  23 *   - always clock in 16 clocks at once
  24 *   - at DOUT: D15 first, D0 last
  25 *   - D0..D7 contains the data from the first cycle
  26 *
  27 * The driver exports a standard gpiochip interface
  28 */
  29
  30#include <linux/module.h>
  31#include <linux/init.h>
  32#include <linux/platform_device.h>
  33#include <linux/mutex.h>
  34#include <linux/spi/max7301.h>
  35#include <linux/gpio/driver.h>
  36#include <linux/slab.h>
  37
  38/*
  39 * Pin configurations, see MAX7301 datasheet page 6
  40 */
  41#define PIN_CONFIG_MASK 0x03
  42#define PIN_CONFIG_IN_PULLUP 0x03
  43#define PIN_CONFIG_IN_WO_PULLUP 0x02
  44#define PIN_CONFIG_OUT 0x01
  45
  46#define PIN_NUMBER 28
  47
  48static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)
  49{
  50        struct max7301 *ts = container_of(chip, struct max7301, chip);
  51        u8 *config;
  52        u8 offset_bits, pin_config;
  53        int ret;
  54
  55        /* First 4 pins are unused in the controller */
  56        offset += 4;
  57        offset_bits = (offset & 3) << 1;
  58
  59        config = &ts->port_config[offset >> 2];
  60
  61        if (ts->input_pullup_active & BIT(offset))
  62                pin_config = PIN_CONFIG_IN_PULLUP;
  63        else
  64                pin_config = PIN_CONFIG_IN_WO_PULLUP;
  65
  66        mutex_lock(&ts->lock);
  67
  68        *config = (*config & ~(PIN_CONFIG_MASK << offset_bits))
  69                           | (pin_config << offset_bits);
  70
  71        ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);
  72
  73        mutex_unlock(&ts->lock);
  74
  75        return ret;
  76}
  77
  78static int __max7301_set(struct max7301 *ts, unsigned offset, int value)
  79{
  80        if (value) {
  81                ts->out_level |= 1 << offset;
  82                return ts->write(ts->dev, 0x20 + offset, 0x01);
  83        } else {
  84                ts->out_level &= ~(1 << offset);
  85                return ts->write(ts->dev, 0x20 + offset, 0x00);
  86        }
  87}
  88
  89static int max7301_direction_output(struct gpio_chip *chip, unsigned offset,
  90                                    int value)
  91{
  92        struct max7301 *ts = container_of(chip, struct max7301, chip);
  93        u8 *config;
  94        u8 offset_bits;
  95        int ret;
  96
  97        /* First 4 pins are unused in the controller */
  98        offset += 4;
  99        offset_bits = (offset & 3) << 1;
 100
 101        config = &ts->port_config[offset >> 2];
 102
 103        mutex_lock(&ts->lock);
 104
 105        *config = (*config & ~(PIN_CONFIG_MASK << offset_bits))
 106                           | (PIN_CONFIG_OUT << offset_bits);
 107
 108        ret = __max7301_set(ts, offset, value);
 109
 110        if (!ret)
 111                ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);
 112
 113        mutex_unlock(&ts->lock);
 114
 115        return ret;
 116}
 117
 118static int max7301_get(struct gpio_chip *chip, unsigned offset)
 119{
 120        struct max7301 *ts = gpiochip_get_data(chip);
 121        int config, level = -EINVAL;
 122
 123        /* First 4 pins are unused in the controller */
 124        offset += 4;
 125
 126        mutex_lock(&ts->lock);
 127
 128        config = (ts->port_config[offset >> 2] >> ((offset & 3) << 1))
 129                        & PIN_CONFIG_MASK;
 130
 131        switch (config) {
 132        case PIN_CONFIG_OUT:
 133                /* Output: return cached level */
 134                level =  !!(ts->out_level & (1 << offset));
 135                break;
 136        case PIN_CONFIG_IN_WO_PULLUP:
 137        case PIN_CONFIG_IN_PULLUP:
 138                /* Input: read out */
 139                level = ts->read(ts->dev, 0x20 + offset) & 0x01;
 140        }
 141        mutex_unlock(&ts->lock);
 142
 143        return level;
 144}
 145
 146static void max7301_set(struct gpio_chip *chip, unsigned offset, int value)
 147{
 148        struct max7301 *ts = gpiochip_get_data(chip);
 149
 150        /* First 4 pins are unused in the controller */
 151        offset += 4;
 152
 153        mutex_lock(&ts->lock);
 154
 155        __max7301_set(ts, offset, value);
 156
 157        mutex_unlock(&ts->lock);
 158}
 159
 160int __max730x_probe(struct max7301 *ts)
 161{
 162        struct device *dev = ts->dev;
 163        struct max7301_platform_data *pdata;
 164        int i, ret;
 165
 166        pdata = dev_get_platdata(dev);
 167
 168        mutex_init(&ts->lock);
 169        dev_set_drvdata(dev, ts);
 170
 171        /* Power up the chip and disable IRQ output */
 172        ts->write(dev, 0x04, 0x01);
 173
 174        if (pdata) {
 175                ts->input_pullup_active = pdata->input_pullup_active;
 176                ts->chip.base = pdata->base;
 177        } else {
 178                ts->chip.base = -1;
 179        }
 180        ts->chip.label = dev->driver->name;
 181
 182        ts->chip.direction_input = max7301_direction_input;
 183        ts->chip.get = max7301_get;
 184        ts->chip.direction_output = max7301_direction_output;
 185        ts->chip.set = max7301_set;
 186
 187        ts->chip.ngpio = PIN_NUMBER;
 188        ts->chip.can_sleep = true;
 189        ts->chip.parent = dev;
 190        ts->chip.owner = THIS_MODULE;
 191
 192        /*
 193         * initialize pullups according to platform data and cache the
 194         * register values for later use.
 195         */
 196        for (i = 1; i < 8; i++) {
 197                int j;
 198                /*
 199                 * initialize port_config with "0xAA", which means
 200                 * input with internal pullup disabled. This is needed
 201                 * to avoid writing zeros (in the inner for loop),
 202                 * which is not allowed according to the datasheet.
 203                 */
 204                ts->port_config[i] = 0xAA;
 205                for (j = 0; j < 4; j++) {
 206                        int offset = (i - 1) * 4 + j;
 207                        ret = max7301_direction_input(&ts->chip, offset);
 208                        if (ret)
 209                                goto exit_destroy;
 210                }
 211        }
 212
 213        ret = gpiochip_add_data(&ts->chip, ts);
 214        if (!ret)
 215                return ret;
 216
 217exit_destroy:
 218        mutex_destroy(&ts->lock);
 219        return ret;
 220}
 221EXPORT_SYMBOL_GPL(__max730x_probe);
 222
 223int __max730x_remove(struct device *dev)
 224{
 225        struct max7301 *ts = dev_get_drvdata(dev);
 226
 227        if (ts == NULL)
 228                return -ENODEV;
 229
 230        /* Power down the chip and disable IRQ output */
 231        ts->write(dev, 0x04, 0x00);
 232        gpiochip_remove(&ts->chip);
 233        mutex_destroy(&ts->lock);
 234        return 0;
 235}
 236EXPORT_SYMBOL_GPL(__max730x_remove);
 237
 238MODULE_AUTHOR("Juergen Beisert, Wolfram Sang");
 239MODULE_LICENSE("GPL v2");
 240MODULE_DESCRIPTION("MAX730x GPIO-Expanders, generic parts");
 241