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