linux/drivers/video/fbdev/via/via-gpio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Support for viafb GPIO ports.
   4 *
   5 * Copyright 2009 Jonathan Corbet <corbet@lwn.net>
   6 */
   7
   8#include <linux/spinlock.h>
   9#include <linux/gpio/driver.h>
  10#include <linux/platform_device.h>
  11#include <linux/via-core.h>
  12#include <linux/via-gpio.h>
  13#include <linux/export.h>
  14
  15/*
  16 * The ports we know about.  Note that the port-25 gpios are not
  17 * mentioned in the datasheet.
  18 */
  19
  20struct viafb_gpio {
  21        char *vg_name;  /* Data sheet name */
  22        u16 vg_io_port;
  23        u8  vg_port_index;
  24        int  vg_mask_shift;
  25};
  26
  27static struct viafb_gpio viafb_all_gpios[] = {
  28        {
  29                .vg_name = "VGPIO0",  /* Guess - not in datasheet */
  30                .vg_io_port = VIASR,
  31                .vg_port_index = 0x25,
  32                .vg_mask_shift = 1
  33        },
  34        {
  35                .vg_name = "VGPIO1",
  36                .vg_io_port = VIASR,
  37                .vg_port_index = 0x25,
  38                .vg_mask_shift = 0
  39        },
  40        {
  41                .vg_name = "VGPIO2",  /* aka DISPCLKI0 */
  42                .vg_io_port = VIASR,
  43                .vg_port_index = 0x2c,
  44                .vg_mask_shift = 1
  45        },
  46        {
  47                .vg_name = "VGPIO3",  /* aka DISPCLKO0 */
  48                .vg_io_port = VIASR,
  49                .vg_port_index = 0x2c,
  50                .vg_mask_shift = 0
  51        },
  52        {
  53                .vg_name = "VGPIO4",  /* DISPCLKI1 */
  54                .vg_io_port = VIASR,
  55                .vg_port_index = 0x3d,
  56                .vg_mask_shift = 1
  57        },
  58        {
  59                .vg_name = "VGPIO5",  /* DISPCLKO1 */
  60                .vg_io_port = VIASR,
  61                .vg_port_index = 0x3d,
  62                .vg_mask_shift = 0
  63        },
  64};
  65
  66#define VIAFB_NUM_GPIOS ARRAY_SIZE(viafb_all_gpios)
  67
  68/*
  69 * This structure controls the active GPIOs, which may be a subset
  70 * of those which are known.
  71 */
  72
  73struct viafb_gpio_cfg {
  74        struct gpio_chip gpio_chip;
  75        struct viafb_dev *vdev;
  76        struct viafb_gpio *active_gpios[VIAFB_NUM_GPIOS];
  77        const char *gpio_names[VIAFB_NUM_GPIOS];
  78};
  79
  80/*
  81 * GPIO access functions
  82 */
  83static void via_gpio_set(struct gpio_chip *chip, unsigned int nr,
  84                         int value)
  85{
  86        struct viafb_gpio_cfg *cfg = gpiochip_get_data(chip);
  87        u8 reg;
  88        struct viafb_gpio *gpio;
  89        unsigned long flags;
  90
  91        spin_lock_irqsave(&cfg->vdev->reg_lock, flags);
  92        gpio = cfg->active_gpios[nr];
  93        reg = via_read_reg(VIASR, gpio->vg_port_index);
  94        reg |= 0x40 << gpio->vg_mask_shift;  /* output enable */
  95        if (value)
  96                reg |= 0x10 << gpio->vg_mask_shift;
  97        else
  98                reg &= ~(0x10 << gpio->vg_mask_shift);
  99        via_write_reg(VIASR, gpio->vg_port_index, reg);
 100        spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags);
 101}
 102
 103static int via_gpio_dir_out(struct gpio_chip *chip, unsigned int nr,
 104                            int value)
 105{
 106        via_gpio_set(chip, nr, value);
 107        return 0;
 108}
 109
 110/*
 111 * Set the input direction.  I'm not sure this is right; we should
 112 * be able to do input without disabling output.
 113 */
 114static int via_gpio_dir_input(struct gpio_chip *chip, unsigned int nr)
 115{
 116        struct viafb_gpio_cfg *cfg = gpiochip_get_data(chip);
 117        struct viafb_gpio *gpio;
 118        unsigned long flags;
 119
 120        spin_lock_irqsave(&cfg->vdev->reg_lock, flags);
 121        gpio = cfg->active_gpios[nr];
 122        via_write_reg_mask(VIASR, gpio->vg_port_index, 0,
 123                        0x40 << gpio->vg_mask_shift);
 124        spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags);
 125        return 0;
 126}
 127
 128static int via_gpio_get(struct gpio_chip *chip, unsigned int nr)
 129{
 130        struct viafb_gpio_cfg *cfg = gpiochip_get_data(chip);
 131        u8 reg;
 132        struct viafb_gpio *gpio;
 133        unsigned long flags;
 134
 135        spin_lock_irqsave(&cfg->vdev->reg_lock, flags);
 136        gpio = cfg->active_gpios[nr];
 137        reg = via_read_reg(VIASR, gpio->vg_port_index);
 138        spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags);
 139        return !!(reg & (0x04 << gpio->vg_mask_shift));
 140}
 141
 142
 143static struct viafb_gpio_cfg viafb_gpio_config = {
 144        .gpio_chip = {
 145                .label = "VIAFB onboard GPIO",
 146                .owner = THIS_MODULE,
 147                .direction_output = via_gpio_dir_out,
 148                .set = via_gpio_set,
 149                .direction_input = via_gpio_dir_input,
 150                .get = via_gpio_get,
 151                .base = -1,
 152                .ngpio = 0,
 153                .can_sleep = 0
 154        }
 155};
 156
 157/*
 158 * Manage the software enable bit.
 159 */
 160static void viafb_gpio_enable(struct viafb_gpio *gpio)
 161{
 162        via_write_reg_mask(VIASR, gpio->vg_port_index, 0x02, 0x02);
 163}
 164
 165static void viafb_gpio_disable(struct viafb_gpio *gpio)
 166{
 167        via_write_reg_mask(VIASR, gpio->vg_port_index, 0, 0x02);
 168}
 169
 170#ifdef CONFIG_PM
 171
 172static int viafb_gpio_suspend(void *private)
 173{
 174        return 0;
 175}
 176
 177static int viafb_gpio_resume(void *private)
 178{
 179        int i;
 180
 181        for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i += 2)
 182                viafb_gpio_enable(viafb_gpio_config.active_gpios[i]);
 183        return 0;
 184}
 185
 186static struct viafb_pm_hooks viafb_gpio_pm_hooks = {
 187        .suspend = viafb_gpio_suspend,
 188        .resume = viafb_gpio_resume
 189};
 190#endif /* CONFIG_PM */
 191
 192/*
 193 * Look up a specific gpio and return the number it was assigned.
 194 */
 195int viafb_gpio_lookup(const char *name)
 196{
 197        int i;
 198
 199        for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i++)
 200                if (!strcmp(name, viafb_gpio_config.active_gpios[i]->vg_name))
 201                        return viafb_gpio_config.gpio_chip.base + i;
 202        return -1;
 203}
 204EXPORT_SYMBOL_GPL(viafb_gpio_lookup);
 205
 206/*
 207 * Platform device stuff.
 208 */
 209static int viafb_gpio_probe(struct platform_device *platdev)
 210{
 211        struct viafb_dev *vdev = platdev->dev.platform_data;
 212        struct via_port_cfg *port_cfg = vdev->port_cfg;
 213        int i, ngpio = 0, ret;
 214        struct viafb_gpio *gpio;
 215        unsigned long flags;
 216
 217        /*
 218         * Set up entries for all GPIOs which have been configured to
 219         * operate as such (as opposed to as i2c ports).
 220         */
 221        for (i = 0; i < VIAFB_NUM_PORTS; i++) {
 222                if (port_cfg[i].mode != VIA_MODE_GPIO)
 223                        continue;
 224                for (gpio = viafb_all_gpios;
 225                     gpio < viafb_all_gpios + VIAFB_NUM_GPIOS; gpio++)
 226                        if (gpio->vg_port_index == port_cfg[i].ioport_index) {
 227                                viafb_gpio_config.active_gpios[ngpio] = gpio;
 228                                viafb_gpio_config.gpio_names[ngpio] =
 229                                        gpio->vg_name;
 230                                ngpio++;
 231                        }
 232        }
 233        viafb_gpio_config.gpio_chip.ngpio = ngpio;
 234        viafb_gpio_config.gpio_chip.names = viafb_gpio_config.gpio_names;
 235        viafb_gpio_config.vdev = vdev;
 236        if (ngpio == 0) {
 237                printk(KERN_INFO "viafb: no GPIOs configured\n");
 238                return 0;
 239        }
 240        /*
 241         * Enable the ports.  They come in pairs, with a single
 242         * enable bit for both.
 243         */
 244        spin_lock_irqsave(&viafb_gpio_config.vdev->reg_lock, flags);
 245        for (i = 0; i < ngpio; i += 2)
 246                viafb_gpio_enable(viafb_gpio_config.active_gpios[i]);
 247        spin_unlock_irqrestore(&viafb_gpio_config.vdev->reg_lock, flags);
 248        /*
 249         * Get registered.
 250         */
 251        viafb_gpio_config.gpio_chip.base = -1;  /* Dynamic */
 252        ret = gpiochip_add_data(&viafb_gpio_config.gpio_chip,
 253                                &viafb_gpio_config);
 254        if (ret) {
 255                printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret);
 256                viafb_gpio_config.gpio_chip.ngpio = 0;
 257        }
 258#ifdef CONFIG_PM
 259        viafb_pm_register(&viafb_gpio_pm_hooks);
 260#endif
 261        return ret;
 262}
 263
 264
 265static int viafb_gpio_remove(struct platform_device *platdev)
 266{
 267        unsigned long flags;
 268        int i;
 269
 270#ifdef CONFIG_PM
 271        viafb_pm_unregister(&viafb_gpio_pm_hooks);
 272#endif
 273
 274        /*
 275         * Get unregistered.
 276         */
 277        if (viafb_gpio_config.gpio_chip.ngpio > 0) {
 278                gpiochip_remove(&viafb_gpio_config.gpio_chip);
 279        }
 280        /*
 281         * Disable the ports.
 282         */
 283        spin_lock_irqsave(&viafb_gpio_config.vdev->reg_lock, flags);
 284        for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i += 2)
 285                viafb_gpio_disable(viafb_gpio_config.active_gpios[i]);
 286        viafb_gpio_config.gpio_chip.ngpio = 0;
 287        spin_unlock_irqrestore(&viafb_gpio_config.vdev->reg_lock, flags);
 288        return 0;
 289}
 290
 291static struct platform_driver via_gpio_driver = {
 292        .driver = {
 293                .name = "viafb-gpio",
 294        },
 295        .probe = viafb_gpio_probe,
 296        .remove = viafb_gpio_remove,
 297};
 298
 299int viafb_gpio_init(void)
 300{
 301        return platform_driver_register(&via_gpio_driver);
 302}
 303
 304void viafb_gpio_exit(void)
 305{
 306        platform_driver_unregister(&via_gpio_driver);
 307}
 308