226 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * GPIO Driver for Dialog DA9052 PMICs.4 *5 * Copyright(c) 2011 Dialog Semiconductor Ltd.6 *7 * Author: David Dajun Chen <dchen@diasemi.com>8 */9#include <linux/fs.h>10#include <linux/gpio/driver.h>11#include <linux/module.h>12#include <linux/platform_device.h>13#include <linux/syscalls.h>14#include <linux/uaccess.h>15 16#include <linux/mfd/da9052/da9052.h>17#include <linux/mfd/da9052/pdata.h>18#include <linux/mfd/da9052/reg.h>19 20#define DA9052_INPUT 121#define DA9052_OUTPUT_OPENDRAIN 222#define DA9052_OUTPUT_PUSHPULL 323 24#define DA9052_SUPPLY_VDD_IO1 025 26#define DA9052_DEBOUNCING_OFF 027#define DA9052_DEBOUNCING_ON 128 29#define DA9052_OUTPUT_LOWLEVEL 030 31#define DA9052_ACTIVE_LOW 032#define DA9052_ACTIVE_HIGH 133 34#define DA9052_GPIO_MAX_PORTS_PER_REGISTER 835#define DA9052_GPIO_SHIFT_COUNT(no) (no%8)36#define DA9052_GPIO_MASK_UPPER_NIBBLE 0xF037#define DA9052_GPIO_MASK_LOWER_NIBBLE 0x0F38#define DA9052_GPIO_NIBBLE_SHIFT 439#define DA9052_IRQ_GPI0 1640#define DA9052_GPIO_ODD_SHIFT 741#define DA9052_GPIO_EVEN_SHIFT 342 43struct da9052_gpio {44 struct da9052 *da9052;45 struct gpio_chip gp;46};47 48static unsigned char da9052_gpio_port_odd(unsigned offset)49{50 return offset % 2;51}52 53static int da9052_gpio_get(struct gpio_chip *gc, unsigned offset)54{55 struct da9052_gpio *gpio = gpiochip_get_data(gc);56 int da9052_port_direction = 0;57 int ret;58 59 ret = da9052_reg_read(gpio->da9052,60 DA9052_GPIO_0_1_REG + (offset >> 1));61 if (ret < 0)62 return ret;63 64 if (da9052_gpio_port_odd(offset)) {65 da9052_port_direction = ret & DA9052_GPIO_ODD_PORT_PIN;66 da9052_port_direction >>= 4;67 } else {68 da9052_port_direction = ret & DA9052_GPIO_EVEN_PORT_PIN;69 }70 71 switch (da9052_port_direction) {72 case DA9052_INPUT:73 if (offset < DA9052_GPIO_MAX_PORTS_PER_REGISTER)74 ret = da9052_reg_read(gpio->da9052,75 DA9052_STATUS_C_REG);76 else77 ret = da9052_reg_read(gpio->da9052,78 DA9052_STATUS_D_REG);79 if (ret < 0)80 return ret;81 return !!(ret & (1 << DA9052_GPIO_SHIFT_COUNT(offset)));82 case DA9052_OUTPUT_PUSHPULL:83 if (da9052_gpio_port_odd(offset))84 return !!(ret & DA9052_GPIO_ODD_PORT_MODE);85 else86 return !!(ret & DA9052_GPIO_EVEN_PORT_MODE);87 default:88 return -EINVAL;89 }90}91 92static void da9052_gpio_set(struct gpio_chip *gc, unsigned offset, int value)93{94 struct da9052_gpio *gpio = gpiochip_get_data(gc);95 int ret;96 97 if (da9052_gpio_port_odd(offset)) {98 ret = da9052_reg_update(gpio->da9052, (offset >> 1) +99 DA9052_GPIO_0_1_REG,100 DA9052_GPIO_ODD_PORT_MODE,101 value << DA9052_GPIO_ODD_SHIFT);102 if (ret != 0)103 dev_err(gpio->da9052->dev,104 "Failed to updated gpio odd reg,%d",105 ret);106 } else {107 ret = da9052_reg_update(gpio->da9052, (offset >> 1) +108 DA9052_GPIO_0_1_REG,109 DA9052_GPIO_EVEN_PORT_MODE,110 value << DA9052_GPIO_EVEN_SHIFT);111 if (ret != 0)112 dev_err(gpio->da9052->dev,113 "Failed to updated gpio even reg,%d",114 ret);115 }116}117 118static int da9052_gpio_direction_input(struct gpio_chip *gc, unsigned offset)119{120 struct da9052_gpio *gpio = gpiochip_get_data(gc);121 unsigned char register_value;122 int ret;123 124 /* Format: function - 2 bits type - 1 bit mode - 1 bit */125 register_value = DA9052_INPUT | DA9052_ACTIVE_LOW << 2 |126 DA9052_DEBOUNCING_ON << 3;127 128 if (da9052_gpio_port_odd(offset))129 ret = da9052_reg_update(gpio->da9052, (offset >> 1) +130 DA9052_GPIO_0_1_REG,131 DA9052_GPIO_MASK_UPPER_NIBBLE,132 (register_value <<133 DA9052_GPIO_NIBBLE_SHIFT));134 else135 ret = da9052_reg_update(gpio->da9052, (offset >> 1) +136 DA9052_GPIO_0_1_REG,137 DA9052_GPIO_MASK_LOWER_NIBBLE,138 register_value);139 140 return ret;141}142 143static int da9052_gpio_direction_output(struct gpio_chip *gc,144 unsigned offset, int value)145{146 struct da9052_gpio *gpio = gpiochip_get_data(gc);147 unsigned char register_value;148 int ret;149 150 /* Format: Function - 2 bits Type - 1 bit Mode - 1 bit */151 register_value = DA9052_OUTPUT_PUSHPULL | DA9052_SUPPLY_VDD_IO1 << 2 |152 value << 3;153 154 if (da9052_gpio_port_odd(offset))155 ret = da9052_reg_update(gpio->da9052, (offset >> 1) +156 DA9052_GPIO_0_1_REG,157 DA9052_GPIO_MASK_UPPER_NIBBLE,158 (register_value <<159 DA9052_GPIO_NIBBLE_SHIFT));160 else161 ret = da9052_reg_update(gpio->da9052, (offset >> 1) +162 DA9052_GPIO_0_1_REG,163 DA9052_GPIO_MASK_LOWER_NIBBLE,164 register_value);165 166 return ret;167}168 169static int da9052_gpio_to_irq(struct gpio_chip *gc, u32 offset)170{171 struct da9052_gpio *gpio = gpiochip_get_data(gc);172 struct da9052 *da9052 = gpio->da9052;173 174 int irq;175 176 irq = regmap_irq_get_virq(da9052->irq_data, DA9052_IRQ_GPI0 + offset);177 178 return irq;179}180 181static const struct gpio_chip reference_gp = {182 .label = "da9052-gpio",183 .owner = THIS_MODULE,184 .get = da9052_gpio_get,185 .set = da9052_gpio_set,186 .direction_input = da9052_gpio_direction_input,187 .direction_output = da9052_gpio_direction_output,188 .to_irq = da9052_gpio_to_irq,189 .can_sleep = true,190 .ngpio = 16,191 .base = -1,192};193 194static int da9052_gpio_probe(struct platform_device *pdev)195{196 struct da9052_gpio *gpio;197 struct da9052_pdata *pdata;198 199 gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);200 if (!gpio)201 return -ENOMEM;202 203 gpio->da9052 = dev_get_drvdata(pdev->dev.parent);204 pdata = dev_get_platdata(gpio->da9052->dev);205 206 gpio->gp = reference_gp;207 if (pdata && pdata->gpio_base)208 gpio->gp.base = pdata->gpio_base;209 210 return devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio);211}212 213static struct platform_driver da9052_gpio_driver = {214 .probe = da9052_gpio_probe,215 .driver = {216 .name = "da9052-gpio",217 },218};219 220module_platform_driver(da9052_gpio_driver);221 222MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");223MODULE_DESCRIPTION("DA9052 GPIO Device Driver");224MODULE_LICENSE("GPL");225MODULE_ALIAS("platform:da9052-gpio");226