162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * GPIO Driver for Dialog DA9055 PMICs. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright(c) 2012 Dialog Semiconductor Ltd. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: David Dajun Chen <dchen@diasemi.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/mfd/da9055/core.h> 1462306a36Sopenharmony_ci#include <linux/mfd/da9055/reg.h> 1562306a36Sopenharmony_ci#include <linux/mfd/da9055/pdata.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define DA9055_VDD_IO 0x0 1862306a36Sopenharmony_ci#define DA9055_PUSH_PULL 0x3 1962306a36Sopenharmony_ci#define DA9055_ACT_LOW 0x0 2062306a36Sopenharmony_ci#define DA9055_GPI 0x1 2162306a36Sopenharmony_ci#define DA9055_PORT_MASK 0x3 2262306a36Sopenharmony_ci#define DA9055_PORT_SHIFT(offset) (4 * (offset % 2)) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define DA9055_INPUT DA9055_GPI 2562306a36Sopenharmony_ci#define DA9055_OUTPUT DA9055_PUSH_PULL 2662306a36Sopenharmony_ci#define DA9055_IRQ_GPI0 3 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct da9055_gpio { 2962306a36Sopenharmony_ci struct da9055 *da9055; 3062306a36Sopenharmony_ci struct gpio_chip gp; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int da9055_gpio_get(struct gpio_chip *gc, unsigned offset) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct da9055_gpio *gpio = gpiochip_get_data(gc); 3662306a36Sopenharmony_ci int gpio_direction = 0; 3762306a36Sopenharmony_ci int ret; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* Get GPIO direction */ 4062306a36Sopenharmony_ci ret = da9055_reg_read(gpio->da9055, (offset >> 1) + DA9055_REG_GPIO0_1); 4162306a36Sopenharmony_ci if (ret < 0) 4262306a36Sopenharmony_ci return ret; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci gpio_direction = ret & (DA9055_PORT_MASK) << DA9055_PORT_SHIFT(offset); 4562306a36Sopenharmony_ci gpio_direction >>= DA9055_PORT_SHIFT(offset); 4662306a36Sopenharmony_ci switch (gpio_direction) { 4762306a36Sopenharmony_ci case DA9055_INPUT: 4862306a36Sopenharmony_ci ret = da9055_reg_read(gpio->da9055, DA9055_REG_STATUS_B); 4962306a36Sopenharmony_ci if (ret < 0) 5062306a36Sopenharmony_ci return ret; 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci case DA9055_OUTPUT: 5362306a36Sopenharmony_ci ret = da9055_reg_read(gpio->da9055, DA9055_REG_GPIO_MODE0_2); 5462306a36Sopenharmony_ci if (ret < 0) 5562306a36Sopenharmony_ci return ret; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return ret & (1 << offset); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic void da9055_gpio_set(struct gpio_chip *gc, unsigned offset, int value) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct da9055_gpio *gpio = gpiochip_get_data(gc); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci da9055_reg_update(gpio->da9055, 6762306a36Sopenharmony_ci DA9055_REG_GPIO_MODE0_2, 6862306a36Sopenharmony_ci 1 << offset, 6962306a36Sopenharmony_ci value << offset); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int da9055_gpio_direction_input(struct gpio_chip *gc, unsigned offset) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct da9055_gpio *gpio = gpiochip_get_data(gc); 7562306a36Sopenharmony_ci unsigned char reg_byte; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci reg_byte = (DA9055_ACT_LOW | DA9055_GPI) 7862306a36Sopenharmony_ci << DA9055_PORT_SHIFT(offset); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return da9055_reg_update(gpio->da9055, (offset >> 1) + 8162306a36Sopenharmony_ci DA9055_REG_GPIO0_1, 8262306a36Sopenharmony_ci DA9055_PORT_MASK << 8362306a36Sopenharmony_ci DA9055_PORT_SHIFT(offset), 8462306a36Sopenharmony_ci reg_byte); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int da9055_gpio_direction_output(struct gpio_chip *gc, 8862306a36Sopenharmony_ci unsigned offset, int value) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct da9055_gpio *gpio = gpiochip_get_data(gc); 9162306a36Sopenharmony_ci unsigned char reg_byte; 9262306a36Sopenharmony_ci int ret; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci reg_byte = (DA9055_VDD_IO | DA9055_PUSH_PULL) 9562306a36Sopenharmony_ci << DA9055_PORT_SHIFT(offset); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci ret = da9055_reg_update(gpio->da9055, (offset >> 1) + 9862306a36Sopenharmony_ci DA9055_REG_GPIO0_1, 9962306a36Sopenharmony_ci DA9055_PORT_MASK << 10062306a36Sopenharmony_ci DA9055_PORT_SHIFT(offset), 10162306a36Sopenharmony_ci reg_byte); 10262306a36Sopenharmony_ci if (ret < 0) 10362306a36Sopenharmony_ci return ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci da9055_gpio_set(gc, offset, value); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int da9055_gpio_to_irq(struct gpio_chip *gc, u32 offset) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct da9055_gpio *gpio = gpiochip_get_data(gc); 11362306a36Sopenharmony_ci struct da9055 *da9055 = gpio->da9055; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return regmap_irq_get_virq(da9055->irq_data, 11662306a36Sopenharmony_ci DA9055_IRQ_GPI0 + offset); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic const struct gpio_chip reference_gp = { 12062306a36Sopenharmony_ci .label = "da9055-gpio", 12162306a36Sopenharmony_ci .owner = THIS_MODULE, 12262306a36Sopenharmony_ci .get = da9055_gpio_get, 12362306a36Sopenharmony_ci .set = da9055_gpio_set, 12462306a36Sopenharmony_ci .direction_input = da9055_gpio_direction_input, 12562306a36Sopenharmony_ci .direction_output = da9055_gpio_direction_output, 12662306a36Sopenharmony_ci .to_irq = da9055_gpio_to_irq, 12762306a36Sopenharmony_ci .can_sleep = true, 12862306a36Sopenharmony_ci .ngpio = 3, 12962306a36Sopenharmony_ci .base = -1, 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int da9055_gpio_probe(struct platform_device *pdev) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct da9055_gpio *gpio; 13562306a36Sopenharmony_ci struct da9055_pdata *pdata; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); 13862306a36Sopenharmony_ci if (!gpio) 13962306a36Sopenharmony_ci return -ENOMEM; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci gpio->da9055 = dev_get_drvdata(pdev->dev.parent); 14262306a36Sopenharmony_ci pdata = dev_get_platdata(gpio->da9055->dev); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci gpio->gp = reference_gp; 14562306a36Sopenharmony_ci if (pdata && pdata->gpio_base) 14662306a36Sopenharmony_ci gpio->gp.base = pdata->gpio_base; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic struct platform_driver da9055_gpio_driver = { 15262306a36Sopenharmony_ci .probe = da9055_gpio_probe, 15362306a36Sopenharmony_ci .driver = { 15462306a36Sopenharmony_ci .name = "da9055-gpio", 15562306a36Sopenharmony_ci }, 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int __init da9055_gpio_init(void) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci return platform_driver_register(&da9055_gpio_driver); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_cisubsys_initcall(da9055_gpio_init); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void __exit da9055_gpio_exit(void) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci platform_driver_unregister(&da9055_gpio_driver); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_cimodule_exit(da9055_gpio_exit); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ciMODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); 17162306a36Sopenharmony_ciMODULE_DESCRIPTION("DA9055 GPIO Device Driver"); 17262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 17362306a36Sopenharmony_ciMODULE_ALIAS("platform:da9055-gpio"); 174