162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * GPIO Driver for Dialog DA9052 PMICs. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright(c) 2011 Dialog Semiconductor Ltd. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: David Dajun Chen <dchen@diasemi.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/syscalls.h> 1462306a36Sopenharmony_ci#include <linux/uaccess.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/mfd/da9052/da9052.h> 1762306a36Sopenharmony_ci#include <linux/mfd/da9052/pdata.h> 1862306a36Sopenharmony_ci#include <linux/mfd/da9052/reg.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define DA9052_INPUT 1 2162306a36Sopenharmony_ci#define DA9052_OUTPUT_OPENDRAIN 2 2262306a36Sopenharmony_ci#define DA9052_OUTPUT_PUSHPULL 3 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define DA9052_SUPPLY_VDD_IO1 0 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DA9052_DEBOUNCING_OFF 0 2762306a36Sopenharmony_ci#define DA9052_DEBOUNCING_ON 1 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define DA9052_OUTPUT_LOWLEVEL 0 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define DA9052_ACTIVE_LOW 0 3262306a36Sopenharmony_ci#define DA9052_ACTIVE_HIGH 1 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define DA9052_GPIO_MAX_PORTS_PER_REGISTER 8 3562306a36Sopenharmony_ci#define DA9052_GPIO_SHIFT_COUNT(no) (no%8) 3662306a36Sopenharmony_ci#define DA9052_GPIO_MASK_UPPER_NIBBLE 0xF0 3762306a36Sopenharmony_ci#define DA9052_GPIO_MASK_LOWER_NIBBLE 0x0F 3862306a36Sopenharmony_ci#define DA9052_GPIO_NIBBLE_SHIFT 4 3962306a36Sopenharmony_ci#define DA9052_IRQ_GPI0 16 4062306a36Sopenharmony_ci#define DA9052_GPIO_ODD_SHIFT 7 4162306a36Sopenharmony_ci#define DA9052_GPIO_EVEN_SHIFT 3 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct da9052_gpio { 4462306a36Sopenharmony_ci struct da9052 *da9052; 4562306a36Sopenharmony_ci struct gpio_chip gp; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic unsigned char da9052_gpio_port_odd(unsigned offset) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci return offset % 2; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int da9052_gpio_get(struct gpio_chip *gc, unsigned offset) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct da9052_gpio *gpio = gpiochip_get_data(gc); 5662306a36Sopenharmony_ci int da9052_port_direction = 0; 5762306a36Sopenharmony_ci int ret; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci ret = da9052_reg_read(gpio->da9052, 6062306a36Sopenharmony_ci DA9052_GPIO_0_1_REG + (offset >> 1)); 6162306a36Sopenharmony_ci if (ret < 0) 6262306a36Sopenharmony_ci return ret; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (da9052_gpio_port_odd(offset)) { 6562306a36Sopenharmony_ci da9052_port_direction = ret & DA9052_GPIO_ODD_PORT_PIN; 6662306a36Sopenharmony_ci da9052_port_direction >>= 4; 6762306a36Sopenharmony_ci } else { 6862306a36Sopenharmony_ci da9052_port_direction = ret & DA9052_GPIO_EVEN_PORT_PIN; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci switch (da9052_port_direction) { 7262306a36Sopenharmony_ci case DA9052_INPUT: 7362306a36Sopenharmony_ci if (offset < DA9052_GPIO_MAX_PORTS_PER_REGISTER) 7462306a36Sopenharmony_ci ret = da9052_reg_read(gpio->da9052, 7562306a36Sopenharmony_ci DA9052_STATUS_C_REG); 7662306a36Sopenharmony_ci else 7762306a36Sopenharmony_ci ret = da9052_reg_read(gpio->da9052, 7862306a36Sopenharmony_ci DA9052_STATUS_D_REG); 7962306a36Sopenharmony_ci if (ret < 0) 8062306a36Sopenharmony_ci return ret; 8162306a36Sopenharmony_ci return !!(ret & (1 << DA9052_GPIO_SHIFT_COUNT(offset))); 8262306a36Sopenharmony_ci case DA9052_OUTPUT_PUSHPULL: 8362306a36Sopenharmony_ci if (da9052_gpio_port_odd(offset)) 8462306a36Sopenharmony_ci return !!(ret & DA9052_GPIO_ODD_PORT_MODE); 8562306a36Sopenharmony_ci else 8662306a36Sopenharmony_ci return !!(ret & DA9052_GPIO_EVEN_PORT_MODE); 8762306a36Sopenharmony_ci default: 8862306a36Sopenharmony_ci return -EINVAL; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void da9052_gpio_set(struct gpio_chip *gc, unsigned offset, int value) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct da9052_gpio *gpio = gpiochip_get_data(gc); 9562306a36Sopenharmony_ci int ret; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (da9052_gpio_port_odd(offset)) { 9862306a36Sopenharmony_ci ret = da9052_reg_update(gpio->da9052, (offset >> 1) + 9962306a36Sopenharmony_ci DA9052_GPIO_0_1_REG, 10062306a36Sopenharmony_ci DA9052_GPIO_ODD_PORT_MODE, 10162306a36Sopenharmony_ci value << DA9052_GPIO_ODD_SHIFT); 10262306a36Sopenharmony_ci if (ret != 0) 10362306a36Sopenharmony_ci dev_err(gpio->da9052->dev, 10462306a36Sopenharmony_ci "Failed to updated gpio odd reg,%d", 10562306a36Sopenharmony_ci ret); 10662306a36Sopenharmony_ci } else { 10762306a36Sopenharmony_ci ret = da9052_reg_update(gpio->da9052, (offset >> 1) + 10862306a36Sopenharmony_ci DA9052_GPIO_0_1_REG, 10962306a36Sopenharmony_ci DA9052_GPIO_EVEN_PORT_MODE, 11062306a36Sopenharmony_ci value << DA9052_GPIO_EVEN_SHIFT); 11162306a36Sopenharmony_ci if (ret != 0) 11262306a36Sopenharmony_ci dev_err(gpio->da9052->dev, 11362306a36Sopenharmony_ci "Failed to updated gpio even reg,%d", 11462306a36Sopenharmony_ci ret); 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int da9052_gpio_direction_input(struct gpio_chip *gc, unsigned offset) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct da9052_gpio *gpio = gpiochip_get_data(gc); 12162306a36Sopenharmony_ci unsigned char register_value; 12262306a36Sopenharmony_ci int ret; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Format: function - 2 bits type - 1 bit mode - 1 bit */ 12562306a36Sopenharmony_ci register_value = DA9052_INPUT | DA9052_ACTIVE_LOW << 2 | 12662306a36Sopenharmony_ci DA9052_DEBOUNCING_ON << 3; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (da9052_gpio_port_odd(offset)) 12962306a36Sopenharmony_ci ret = da9052_reg_update(gpio->da9052, (offset >> 1) + 13062306a36Sopenharmony_ci DA9052_GPIO_0_1_REG, 13162306a36Sopenharmony_ci DA9052_GPIO_MASK_UPPER_NIBBLE, 13262306a36Sopenharmony_ci (register_value << 13362306a36Sopenharmony_ci DA9052_GPIO_NIBBLE_SHIFT)); 13462306a36Sopenharmony_ci else 13562306a36Sopenharmony_ci ret = da9052_reg_update(gpio->da9052, (offset >> 1) + 13662306a36Sopenharmony_ci DA9052_GPIO_0_1_REG, 13762306a36Sopenharmony_ci DA9052_GPIO_MASK_LOWER_NIBBLE, 13862306a36Sopenharmony_ci register_value); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return ret; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int da9052_gpio_direction_output(struct gpio_chip *gc, 14462306a36Sopenharmony_ci unsigned offset, int value) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct da9052_gpio *gpio = gpiochip_get_data(gc); 14762306a36Sopenharmony_ci unsigned char register_value; 14862306a36Sopenharmony_ci int ret; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Format: Function - 2 bits Type - 1 bit Mode - 1 bit */ 15162306a36Sopenharmony_ci register_value = DA9052_OUTPUT_PUSHPULL | DA9052_SUPPLY_VDD_IO1 << 2 | 15262306a36Sopenharmony_ci value << 3; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (da9052_gpio_port_odd(offset)) 15562306a36Sopenharmony_ci ret = da9052_reg_update(gpio->da9052, (offset >> 1) + 15662306a36Sopenharmony_ci DA9052_GPIO_0_1_REG, 15762306a36Sopenharmony_ci DA9052_GPIO_MASK_UPPER_NIBBLE, 15862306a36Sopenharmony_ci (register_value << 15962306a36Sopenharmony_ci DA9052_GPIO_NIBBLE_SHIFT)); 16062306a36Sopenharmony_ci else 16162306a36Sopenharmony_ci ret = da9052_reg_update(gpio->da9052, (offset >> 1) + 16262306a36Sopenharmony_ci DA9052_GPIO_0_1_REG, 16362306a36Sopenharmony_ci DA9052_GPIO_MASK_LOWER_NIBBLE, 16462306a36Sopenharmony_ci register_value); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return ret; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int da9052_gpio_to_irq(struct gpio_chip *gc, u32 offset) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct da9052_gpio *gpio = gpiochip_get_data(gc); 17262306a36Sopenharmony_ci struct da9052 *da9052 = gpio->da9052; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci int irq; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci irq = regmap_irq_get_virq(da9052->irq_data, DA9052_IRQ_GPI0 + offset); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return irq; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic const struct gpio_chip reference_gp = { 18262306a36Sopenharmony_ci .label = "da9052-gpio", 18362306a36Sopenharmony_ci .owner = THIS_MODULE, 18462306a36Sopenharmony_ci .get = da9052_gpio_get, 18562306a36Sopenharmony_ci .set = da9052_gpio_set, 18662306a36Sopenharmony_ci .direction_input = da9052_gpio_direction_input, 18762306a36Sopenharmony_ci .direction_output = da9052_gpio_direction_output, 18862306a36Sopenharmony_ci .to_irq = da9052_gpio_to_irq, 18962306a36Sopenharmony_ci .can_sleep = true, 19062306a36Sopenharmony_ci .ngpio = 16, 19162306a36Sopenharmony_ci .base = -1, 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int da9052_gpio_probe(struct platform_device *pdev) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct da9052_gpio *gpio; 19762306a36Sopenharmony_ci struct da9052_pdata *pdata; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); 20062306a36Sopenharmony_ci if (!gpio) 20162306a36Sopenharmony_ci return -ENOMEM; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci gpio->da9052 = dev_get_drvdata(pdev->dev.parent); 20462306a36Sopenharmony_ci pdata = dev_get_platdata(gpio->da9052->dev); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci gpio->gp = reference_gp; 20762306a36Sopenharmony_ci if (pdata && pdata->gpio_base) 20862306a36Sopenharmony_ci gpio->gp.base = pdata->gpio_base; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic struct platform_driver da9052_gpio_driver = { 21462306a36Sopenharmony_ci .probe = da9052_gpio_probe, 21562306a36Sopenharmony_ci .driver = { 21662306a36Sopenharmony_ci .name = "da9052-gpio", 21762306a36Sopenharmony_ci }, 21862306a36Sopenharmony_ci}; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cimodule_platform_driver(da9052_gpio_driver); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciMODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); 22362306a36Sopenharmony_ciMODULE_DESCRIPTION("DA9052 GPIO Device Driver"); 22462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 22562306a36Sopenharmony_ciMODULE_ALIAS("platform:da9052-gpio"); 226