Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpio: gpio-wcove: Fix GPIO control register offset calculation

According to Whiskey Cove PMIC GPIO controller specification, for GPIO
pins 0-12, GPIO input and output register control address range from,

0x4e44-0x4e50 for GPIO outputs control register

0x4e51-0x4e5d for GPIO input control register

But, currently when calculating the GPIO register offsets in to_reg()
function, all GPIO pins in the same bank uses the same GPIO control
register address. This logic is incorrect. This patch fixes this
issue.

This patch also adds support to selectively skip register modification
for virtual GPIOs.

In case of Whiskey Cove PMIC, ACPI code may use up 94 virtual GPIOs.
These virtual GPIOs are used by the ACPI code as means to access various
non GPIO bits of PMIC. So for these virtual GPIOs, we don't need to
manipulate the physical GPIO pin register. A similar patch has been
merged recently by Hans for Crystal Cove PMIC GPIO driver. You can
find more details about it in Commit 9a752b4c9ab9 ("gpio: crystalcove:
Do not write regular gpio registers for virtual GPIOs")

Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Reported-by: Jukka Laitinen <jukka.laitinen@intel.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

authored by

Kuppuswamy Sathyanarayanan and committed by
Linus Walleij
3a02dc97 5c7f2c76

+51 -24
+51 -24
drivers/gpio/gpio-wcove.c
··· 108 108 static inline unsigned int to_reg(int gpio, enum ctrl_register reg_type) 109 109 { 110 110 unsigned int reg; 111 - int bank; 112 111 113 - if (gpio < BANK0_NR_PINS) 114 - bank = 0; 115 - else if (gpio < BANK0_NR_PINS + BANK1_NR_PINS) 116 - bank = 1; 117 - else 118 - bank = 2; 112 + if (gpio >= WCOVE_GPIO_NUM) 113 + return -EOPNOTSUPP; 119 114 120 115 if (reg_type == CTRL_IN) 121 - reg = GPIO_IN_CTRL_BASE + bank; 116 + reg = GPIO_IN_CTRL_BASE + gpio; 122 117 else 123 - reg = GPIO_OUT_CTRL_BASE + bank; 118 + reg = GPIO_OUT_CTRL_BASE + gpio; 124 119 125 120 return reg; 126 121 } ··· 140 145 141 146 static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio) 142 147 { 143 - unsigned int reg = to_reg(gpio, CTRL_IN); 148 + int reg = to_reg(gpio, CTRL_IN); 149 + 150 + if (reg < 0) 151 + return; 144 152 145 153 regmap_update_bits(wg->regmap, reg, CTLI_INTCNT_BE, wg->intcnt); 146 154 } ··· 151 153 static int wcove_gpio_dir_in(struct gpio_chip *chip, unsigned int gpio) 152 154 { 153 155 struct wcove_gpio *wg = gpiochip_get_data(chip); 156 + int reg = to_reg(gpio, CTRL_OUT); 154 157 155 - return regmap_write(wg->regmap, to_reg(gpio, CTRL_OUT), 156 - CTLO_INPUT_SET); 158 + if (reg < 0) 159 + return 0; 160 + 161 + return regmap_write(wg->regmap, reg, CTLO_INPUT_SET); 157 162 } 158 163 159 164 static int wcove_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio, 160 165 int value) 161 166 { 162 167 struct wcove_gpio *wg = gpiochip_get_data(chip); 168 + int reg = to_reg(gpio, CTRL_OUT); 163 169 164 - return regmap_write(wg->regmap, to_reg(gpio, CTRL_OUT), 165 - CTLO_OUTPUT_SET | value); 170 + if (reg < 0) 171 + return 0; 172 + 173 + return regmap_write(wg->regmap, reg, CTLO_OUTPUT_SET | value); 166 174 } 167 175 168 176 static int wcove_gpio_get_direction(struct gpio_chip *chip, unsigned int gpio) 169 177 { 170 178 struct wcove_gpio *wg = gpiochip_get_data(chip); 171 179 unsigned int val; 172 - int ret; 180 + int ret, reg = to_reg(gpio, CTRL_OUT); 173 181 174 - ret = regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &val); 182 + if (reg < 0) 183 + return 0; 184 + 185 + ret = regmap_read(wg->regmap, reg, &val); 175 186 if (ret) 176 187 return ret; 177 188 ··· 191 184 { 192 185 struct wcove_gpio *wg = gpiochip_get_data(chip); 193 186 unsigned int val; 194 - int ret; 187 + int ret, reg = to_reg(gpio, CTRL_IN); 195 188 196 - ret = regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &val); 189 + if (reg < 0) 190 + return 0; 191 + 192 + ret = regmap_read(wg->regmap, reg, &val); 197 193 if (ret) 198 194 return ret; 199 195 ··· 207 197 unsigned int gpio, int value) 208 198 { 209 199 struct wcove_gpio *wg = gpiochip_get_data(chip); 200 + int reg = to_reg(gpio, CTRL_OUT); 201 + 202 + if (reg < 0) 203 + return; 210 204 211 205 if (value) 212 - regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 1, 1); 206 + regmap_update_bits(wg->regmap, reg, 1, 1); 213 207 else 214 - regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 1, 0); 208 + regmap_update_bits(wg->regmap, reg, 1, 0); 215 209 } 216 210 217 211 static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio, 218 212 unsigned long config) 219 213 { 220 214 struct wcove_gpio *wg = gpiochip_get_data(chip); 215 + int reg = to_reg(gpio, CTRL_OUT); 216 + 217 + if (reg < 0) 218 + return 0; 221 219 222 220 switch (pinconf_to_config_param(config)) { 223 221 case PIN_CONFIG_DRIVE_OPEN_DRAIN: 224 - return regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 225 - CTLO_DRV_MASK, CTLO_DRV_OD); 222 + return regmap_update_bits(wg->regmap, reg, CTLO_DRV_MASK, 223 + CTLO_DRV_OD); 226 224 case PIN_CONFIG_DRIVE_PUSH_PULL: 227 - return regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 228 - CTLO_DRV_MASK, CTLO_DRV_CMOS); 225 + return regmap_update_bits(wg->regmap, reg, CTLO_DRV_MASK, 226 + CTLO_DRV_CMOS); 229 227 default: 230 228 break; 231 229 } ··· 245 227 { 246 228 struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 247 229 struct wcove_gpio *wg = gpiochip_get_data(chip); 230 + 231 + if (data->hwirq >= WCOVE_GPIO_NUM) 232 + return 0; 248 233 249 234 switch (type) { 250 235 case IRQ_TYPE_NONE: ··· 299 278 struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 300 279 struct wcove_gpio *wg = gpiochip_get_data(chip); 301 280 281 + if (data->hwirq >= WCOVE_GPIO_NUM) 282 + return; 283 + 302 284 wg->set_irq_mask = false; 303 285 wg->update |= UPDATE_IRQ_MASK; 304 286 } ··· 310 286 { 311 287 struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 312 288 struct wcove_gpio *wg = gpiochip_get_data(chip); 289 + 290 + if (data->hwirq >= WCOVE_GPIO_NUM) 291 + return; 313 292 314 293 wg->set_irq_mask = true; 315 294 wg->update |= UPDATE_IRQ_MASK;