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