162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * gpiolib support for Wolfson WM835x PMICs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2009 Wolfson Microelectronics PLC. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/mfd/core.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/mfd/wm8350/core.h> 1962306a36Sopenharmony_ci#include <linux/mfd/wm8350/gpio.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct wm8350_gpio_data { 2262306a36Sopenharmony_ci struct wm8350 *wm8350; 2362306a36Sopenharmony_ci struct gpio_chip gpio_chip; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int wm8350_gpio_direction_in(struct gpio_chip *chip, unsigned offset) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip); 2962306a36Sopenharmony_ci struct wm8350 *wm8350 = wm8350_gpio->wm8350; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci return wm8350_set_bits(wm8350, WM8350_GPIO_CONFIGURATION_I_O, 3262306a36Sopenharmony_ci 1 << offset); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int wm8350_gpio_get(struct gpio_chip *chip, unsigned offset) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip); 3862306a36Sopenharmony_ci struct wm8350 *wm8350 = wm8350_gpio->wm8350; 3962306a36Sopenharmony_ci int ret; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci ret = wm8350_reg_read(wm8350, WM8350_GPIO_LEVEL); 4262306a36Sopenharmony_ci if (ret < 0) 4362306a36Sopenharmony_ci return ret; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (ret & (1 << offset)) 4662306a36Sopenharmony_ci return 1; 4762306a36Sopenharmony_ci else 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void wm8350_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip); 5462306a36Sopenharmony_ci struct wm8350 *wm8350 = wm8350_gpio->wm8350; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (value) 5762306a36Sopenharmony_ci wm8350_set_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); 5862306a36Sopenharmony_ci else 5962306a36Sopenharmony_ci wm8350_clear_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int wm8350_gpio_direction_out(struct gpio_chip *chip, 6362306a36Sopenharmony_ci unsigned offset, int value) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip); 6662306a36Sopenharmony_ci struct wm8350 *wm8350 = wm8350_gpio->wm8350; 6762306a36Sopenharmony_ci int ret; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci ret = wm8350_clear_bits(wm8350, WM8350_GPIO_CONFIGURATION_I_O, 7062306a36Sopenharmony_ci 1 << offset); 7162306a36Sopenharmony_ci if (ret < 0) 7262306a36Sopenharmony_ci return ret; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Don't have an atomic direction/value setup */ 7562306a36Sopenharmony_ci wm8350_gpio_set(chip, offset, value); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int wm8350_gpio_to_irq(struct gpio_chip *chip, unsigned offset) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip); 8362306a36Sopenharmony_ci struct wm8350 *wm8350 = wm8350_gpio->wm8350; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (!wm8350->irq_base) 8662306a36Sopenharmony_ci return -EINVAL; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return wm8350->irq_base + WM8350_IRQ_GPIO(offset); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic const struct gpio_chip template_chip = { 9262306a36Sopenharmony_ci .label = "wm8350", 9362306a36Sopenharmony_ci .owner = THIS_MODULE, 9462306a36Sopenharmony_ci .direction_input = wm8350_gpio_direction_in, 9562306a36Sopenharmony_ci .get = wm8350_gpio_get, 9662306a36Sopenharmony_ci .direction_output = wm8350_gpio_direction_out, 9762306a36Sopenharmony_ci .set = wm8350_gpio_set, 9862306a36Sopenharmony_ci .to_irq = wm8350_gpio_to_irq, 9962306a36Sopenharmony_ci .can_sleep = true, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int wm8350_gpio_probe(struct platform_device *pdev) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct wm8350 *wm8350 = dev_get_drvdata(pdev->dev.parent); 10562306a36Sopenharmony_ci struct wm8350_platform_data *pdata = dev_get_platdata(wm8350->dev); 10662306a36Sopenharmony_ci struct wm8350_gpio_data *wm8350_gpio; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci wm8350_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm8350_gpio), 10962306a36Sopenharmony_ci GFP_KERNEL); 11062306a36Sopenharmony_ci if (wm8350_gpio == NULL) 11162306a36Sopenharmony_ci return -ENOMEM; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci wm8350_gpio->wm8350 = wm8350; 11462306a36Sopenharmony_ci wm8350_gpio->gpio_chip = template_chip; 11562306a36Sopenharmony_ci wm8350_gpio->gpio_chip.ngpio = 13; 11662306a36Sopenharmony_ci wm8350_gpio->gpio_chip.parent = &pdev->dev; 11762306a36Sopenharmony_ci if (pdata && pdata->gpio_base) 11862306a36Sopenharmony_ci wm8350_gpio->gpio_chip.base = pdata->gpio_base; 11962306a36Sopenharmony_ci else 12062306a36Sopenharmony_ci wm8350_gpio->gpio_chip.base = -1; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return devm_gpiochip_add_data(&pdev->dev, &wm8350_gpio->gpio_chip, wm8350_gpio); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic struct platform_driver wm8350_gpio_driver = { 12662306a36Sopenharmony_ci .driver.name = "wm8350-gpio", 12762306a36Sopenharmony_ci .probe = wm8350_gpio_probe, 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int __init wm8350_gpio_init(void) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci return platform_driver_register(&wm8350_gpio_driver); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_cisubsys_initcall(wm8350_gpio_init); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void __exit wm8350_gpio_exit(void) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci platform_driver_unregister(&wm8350_gpio_driver); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_cimodule_exit(wm8350_gpio_exit); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 14362306a36Sopenharmony_ciMODULE_DESCRIPTION("GPIO interface for WM8350 PMICs"); 14462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 14562306a36Sopenharmony_ciMODULE_ALIAS("platform:wm8350-gpio"); 146