uboot/drivers/gpio/pm8916_gpio.c
<<
>>
Prefs
   1/*
   2 * Qualcomm pm8916 pmic gpio driver - part of Qualcomm PM8916 PMIC
   3 *
   4 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
   5 *
   6 * SPDX-License-Identifier:     GPL-2.0+
   7 */
   8
   9#include <common.h>
  10#include <dm.h>
  11#include <power/pmic.h>
  12#include <spmi/spmi.h>
  13#include <asm/io.h>
  14#include <asm/gpio.h>
  15#include <linux/bitops.h>
  16
  17DECLARE_GLOBAL_DATA_PTR;
  18
  19/* Register offset for each gpio */
  20#define REG_OFFSET(x)          ((x) * 0x100)
  21
  22/* Register maps */
  23
  24/* Type and subtype are shared for all pm8916 peripherals */
  25#define REG_TYPE               0x4
  26#define REG_SUBTYPE            0x5
  27
  28#define REG_STATUS             0x08
  29#define REG_STATUS_VAL_MASK    0x1
  30
  31/* MODE_CTL */
  32#define REG_CTL           0x40
  33#define REG_CTL_MODE_MASK       0x70
  34#define REG_CTL_MODE_INPUT      0x00
  35#define REG_CTL_MODE_INOUT      0x20
  36#define REG_CTL_MODE_OUTPUT     0x10
  37#define REG_CTL_OUTPUT_MASK     0x0F
  38
  39#define REG_DIG_VIN_CTL        0x41
  40#define REG_DIG_VIN_VIN0       0
  41
  42#define REG_DIG_PULL_CTL       0x42
  43#define REG_DIG_PULL_NO_PU     0x5
  44
  45#define REG_DIG_OUT_CTL        0x45
  46#define REG_DIG_OUT_CTL_CMOS   (0x0 << 4)
  47#define REG_DIG_OUT_CTL_DRIVE_L 0x1
  48
  49#define REG_EN_CTL             0x46
  50#define REG_EN_CTL_ENABLE      (1 << 7)
  51
  52struct pm8916_gpio_bank {
  53        uint32_t pid; /* Peripheral ID on SPMI bus */
  54};
  55
  56static int pm8916_gpio_set_direction(struct udevice *dev, unsigned offset,
  57                                     bool input, int value)
  58{
  59        struct pm8916_gpio_bank *priv = dev_get_priv(dev);
  60        uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
  61        int ret;
  62
  63        /* Disable the GPIO */
  64        ret = pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL,
  65                              REG_EN_CTL_ENABLE, 0);
  66        if (ret < 0)
  67                return ret;
  68
  69        /* Select the mode */
  70        if (input)
  71                ret = pmic_reg_write(dev->parent, gpio_base + REG_CTL,
  72                                     REG_CTL_MODE_INPUT);
  73        else
  74                ret = pmic_reg_write(dev->parent, gpio_base + REG_CTL,
  75                                     REG_CTL_MODE_INOUT | (value ? 1 : 0));
  76        if (ret < 0)
  77                return ret;
  78
  79        /* Set the right pull (no pull) */
  80        ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_PULL_CTL,
  81                             REG_DIG_PULL_NO_PU);
  82        if (ret < 0)
  83                return ret;
  84
  85        /* Configure output pin drivers if needed */
  86        if (!input) {
  87                /* Select the VIN - VIN0, pin is input so it doesn't matter */
  88                ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_VIN_CTL,
  89                                     REG_DIG_VIN_VIN0);
  90                if (ret < 0)
  91                        return ret;
  92
  93                /* Set the right dig out control */
  94                ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_OUT_CTL,
  95                                     REG_DIG_OUT_CTL_CMOS |
  96                                     REG_DIG_OUT_CTL_DRIVE_L);
  97                if (ret < 0)
  98                        return ret;
  99        }
 100
 101        /* Enable the GPIO */
 102        return pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL, 0,
 103                               REG_EN_CTL_ENABLE);
 104}
 105
 106static int pm8916_gpio_direction_input(struct udevice *dev, unsigned offset)
 107{
 108        return pm8916_gpio_set_direction(dev, offset, true, 0);
 109}
 110
 111static int pm8916_gpio_direction_output(struct udevice *dev, unsigned offset,
 112                                        int value)
 113{
 114        return pm8916_gpio_set_direction(dev, offset, false, value);
 115}
 116
 117static int pm8916_gpio_get_function(struct udevice *dev, unsigned offset)
 118{
 119        struct pm8916_gpio_bank *priv = dev_get_priv(dev);
 120        uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
 121        int reg;
 122
 123        /* Set the output value of the gpio */
 124        reg = pmic_reg_read(dev->parent, gpio_base + REG_CTL);
 125        if (reg < 0)
 126                return reg;
 127
 128        switch (reg & REG_CTL_MODE_MASK) {
 129        case REG_CTL_MODE_INPUT:
 130                return GPIOF_INPUT;
 131        case REG_CTL_MODE_INOUT: /* Fallthrough */
 132        case REG_CTL_MODE_OUTPUT:
 133                return GPIOF_OUTPUT;
 134        default:
 135                return GPIOF_UNKNOWN;
 136        }
 137}
 138
 139static int pm8916_gpio_get_value(struct udevice *dev, unsigned offset)
 140{
 141        struct pm8916_gpio_bank *priv = dev_get_priv(dev);
 142        uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
 143        int reg;
 144
 145        reg = pmic_reg_read(dev->parent, gpio_base + REG_STATUS);
 146        if (reg < 0)
 147                return reg;
 148
 149        return !!(reg & REG_STATUS_VAL_MASK);
 150}
 151
 152static int pm8916_gpio_set_value(struct udevice *dev, unsigned offset,
 153                                 int value)
 154{
 155        struct pm8916_gpio_bank *priv = dev_get_priv(dev);
 156        uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
 157
 158        /* Set the output value of the gpio */
 159        return pmic_clrsetbits(dev->parent, gpio_base + REG_CTL,
 160                               REG_CTL_OUTPUT_MASK, !!value);
 161}
 162
 163static const struct dm_gpio_ops pm8916_gpio_ops = {
 164        .direction_input        = pm8916_gpio_direction_input,
 165        .direction_output       = pm8916_gpio_direction_output,
 166        .get_value              = pm8916_gpio_get_value,
 167        .set_value              = pm8916_gpio_set_value,
 168        .get_function           = pm8916_gpio_get_function,
 169};
 170
 171static int pm8916_gpio_probe(struct udevice *dev)
 172{
 173        struct pm8916_gpio_bank *priv = dev_get_priv(dev);
 174        int reg;
 175
 176        priv->pid = dev_get_addr(dev);
 177        if (priv->pid == FDT_ADDR_T_NONE)
 178                return -EINVAL;
 179
 180        /* Do a sanity check */
 181        reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE);
 182        if (reg != 0x10)
 183                return -ENODEV;
 184
 185        reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE);
 186        if (reg != 0x5)
 187                return -ENODEV;
 188
 189        return 0;
 190}
 191
 192static int pm8916_gpio_ofdata_to_platdata(struct udevice *dev)
 193{
 194        struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 195
 196        uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
 197                                             "gpio-count", 0);
 198        uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset,
 199                                         "gpio-bank-name", NULL);
 200        if (uc_priv->bank_name == NULL)
 201                uc_priv->bank_name = "pm8916";
 202
 203        return 0;
 204}
 205
 206static const struct udevice_id pm8916_gpio_ids[] = {
 207        { .compatible = "qcom,pm8916-gpio" },
 208        { }
 209};
 210
 211U_BOOT_DRIVER(gpio_pm8916) = {
 212        .name   = "gpio_pm8916",
 213        .id     = UCLASS_GPIO,
 214        .of_match = pm8916_gpio_ids,
 215        .ofdata_to_platdata = pm8916_gpio_ofdata_to_platdata,
 216        .probe  = pm8916_gpio_probe,
 217        .ops    = &pm8916_gpio_ops,
 218        .priv_auto_alloc_size = sizeof(struct pm8916_gpio_bank),
 219};
 220
 221
 222/* Add pmic buttons as GPIO as well - there is no generic way for now */
 223#define PON_INT_RT_STS                        0x10
 224#define KPDPWR_ON_INT_BIT                     0
 225#define RESIN_ON_INT_BIT                      1
 226
 227static int pm8941_pwrkey_get_function(struct udevice *dev, unsigned offset)
 228{
 229        return GPIOF_INPUT;
 230}
 231
 232static int pm8941_pwrkey_get_value(struct udevice *dev, unsigned offset)
 233{
 234        struct pm8916_gpio_bank *priv = dev_get_priv(dev);
 235
 236        int reg = pmic_reg_read(dev->parent, priv->pid + PON_INT_RT_STS);
 237
 238        if (reg < 0)
 239                return 0;
 240
 241        switch (offset) {
 242        case 0: /* Power button */
 243                return (reg & BIT(KPDPWR_ON_INT_BIT)) != 0;
 244                break;
 245        case 1: /* Reset button */
 246        default:
 247                return (reg & BIT(RESIN_ON_INT_BIT)) != 0;
 248                break;
 249        }
 250}
 251
 252static const struct dm_gpio_ops pm8941_pwrkey_ops = {
 253        .get_value              = pm8941_pwrkey_get_value,
 254        .get_function           = pm8941_pwrkey_get_function,
 255};
 256
 257static int pm8941_pwrkey_probe(struct udevice *dev)
 258{
 259        struct pm8916_gpio_bank *priv = dev_get_priv(dev);
 260        int reg;
 261
 262        priv->pid = dev_get_addr(dev);
 263        if (priv->pid == FDT_ADDR_T_NONE)
 264                return -EINVAL;
 265
 266        /* Do a sanity check */
 267        reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE);
 268        if (reg != 0x1)
 269                return -ENODEV;
 270
 271        reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE);
 272        if (reg != 0x1)
 273                return -ENODEV;
 274
 275        return 0;
 276}
 277
 278static int pm8941_pwrkey_ofdata_to_platdata(struct udevice *dev)
 279{
 280        struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 281
 282        uc_priv->gpio_count = 2;
 283        if (uc_priv->bank_name == NULL)
 284                uc_priv->bank_name = "pm8916_key";
 285
 286        return 0;
 287}
 288
 289static const struct udevice_id pm8941_pwrkey_ids[] = {
 290        { .compatible = "qcom,pm8916-pwrkey" },
 291        { }
 292};
 293
 294U_BOOT_DRIVER(pwrkey_pm8941) = {
 295        .name   = "pwrkey_pm8916",
 296        .id     = UCLASS_GPIO,
 297        .of_match = pm8941_pwrkey_ids,
 298        .ofdata_to_platdata = pm8941_pwrkey_ofdata_to_platdata,
 299        .probe  = pm8941_pwrkey_probe,
 300        .ops    = &pm8941_pwrkey_ops,
 301        .priv_auto_alloc_size = sizeof(struct pm8916_gpio_bank),
 302};
 303