155682Smarkm// SPDX-License-Identifier: GPL-2.0+
2233294Sstas/*
3233294Sstas * gpiolib support for Wolfson WM835x PMICs
4233294Sstas *
555682Smarkm * Copyright 2009 Wolfson Microelectronics PLC.
6233294Sstas *
755682Smarkm * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
8233294Sstas *
9233294Sstas */
10233294Sstas
1155682Smarkm#include <linux/gpio/driver.h>
12233294Sstas#include <linux/kernel.h>
13233294Sstas#include <linux/mfd/core.h>
1455682Smarkm#include <linux/module.h>
15233294Sstas#include <linux/platform_device.h>
16233294Sstas#include <linux/slab.h>
17233294Sstas
1855682Smarkm#include <linux/mfd/wm8350/core.h>
19233294Sstas#include <linux/mfd/wm8350/gpio.h>
20233294Sstas
21233294Sstasstruct wm8350_gpio_data {
22233294Sstas	struct wm8350 *wm8350;
23233294Sstas	struct gpio_chip gpio_chip;
24233294Sstas};
25233294Sstas
26233294Sstasstatic int wm8350_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
27233294Sstas{
28233294Sstas	struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip);
29233294Sstas	struct wm8350 *wm8350 = wm8350_gpio->wm8350;
30233294Sstas
31233294Sstas	return wm8350_set_bits(wm8350, WM8350_GPIO_CONFIGURATION_I_O,
32233294Sstas			       1 << offset);
33233294Sstas}
3455682Smarkm
3555682Smarkmstatic int wm8350_gpio_get(struct gpio_chip *chip, unsigned offset)
3655682Smarkm{
37233294Sstas	struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip);
38102644Snectar	struct wm8350 *wm8350 = wm8350_gpio->wm8350;
3955682Smarkm	int ret;
4055682Smarkm
4155682Smarkm	ret = wm8350_reg_read(wm8350, WM8350_GPIO_LEVEL);
4255682Smarkm	if (ret < 0)
4355682Smarkm		return ret;
44178825Sdfr
45178825Sdfr	if (ret & (1 << offset))
46178825Sdfr		return 1;
47178825Sdfr	else
48178825Sdfr		return 0;
49178825Sdfr}
50178825Sdfr
5155682Smarkmstatic void wm8350_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
5255682Smarkm{
5355682Smarkm	struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip);
5455682Smarkm	struct wm8350 *wm8350 = wm8350_gpio->wm8350;
5555682Smarkm
5655682Smarkm	if (value)
5755682Smarkm		wm8350_set_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset);
5855682Smarkm	else
5955682Smarkm		wm8350_clear_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset);
6055682Smarkm}
6155682Smarkm
62102644Snectarstatic int wm8350_gpio_direction_out(struct gpio_chip *chip,
6355682Smarkm				     unsigned offset, int value)
64233294Sstas{
6555682Smarkm	struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip);
6655682Smarkm	struct wm8350 *wm8350 = wm8350_gpio->wm8350;
6755682Smarkm	int ret;
6855682Smarkm
6955682Smarkm	ret = wm8350_clear_bits(wm8350, WM8350_GPIO_CONFIGURATION_I_O,
7055682Smarkm				1 << offset);
7155682Smarkm	if (ret < 0)
72233294Sstas		return ret;
7355682Smarkm
7455682Smarkm	/* Don't have an atomic direction/value setup */
7555682Smarkm	wm8350_gpio_set(chip, offset, value);
76178825Sdfr
77178825Sdfr	return 0;
78178825Sdfr}
79178825Sdfr
80178825Sdfrstatic int wm8350_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
81178825Sdfr{
8255682Smarkm	struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip);
8355682Smarkm	struct wm8350 *wm8350 = wm8350_gpio->wm8350;
8455682Smarkm
85233294Sstas	if (!wm8350->irq_base)
86102644Snectar		return -EINVAL;
8755682Smarkm
8855682Smarkm	return wm8350->irq_base + WM8350_IRQ_GPIO(offset);
8955682Smarkm}
9055682Smarkm
9155682Smarkmstatic const struct gpio_chip template_chip = {
9255682Smarkm	.label			= "wm8350",
9355682Smarkm	.owner			= THIS_MODULE,
9455682Smarkm	.direction_input	= wm8350_gpio_direction_in,
9555682Smarkm	.get			= wm8350_gpio_get,
9655682Smarkm	.direction_output	= wm8350_gpio_direction_out,
9790926Snectar	.set			= wm8350_gpio_set,
9855682Smarkm	.to_irq			= wm8350_gpio_to_irq,
99233294Sstas	.can_sleep		= true,
100102644Snectar};
10190926Snectar
10255682Smarkmstatic int wm8350_gpio_probe(struct platform_device *pdev)
10355682Smarkm{
10455682Smarkm	struct wm8350 *wm8350 = dev_get_drvdata(pdev->dev.parent);
10555682Smarkm	struct wm8350_platform_data *pdata = dev_get_platdata(wm8350->dev);
10672445Sassar	struct wm8350_gpio_data *wm8350_gpio;
107233294Sstas
108233294Sstas	wm8350_gpio = devm_kzalloc(&pdev->dev, sizeof(*wm8350_gpio),
109233294Sstas				   GFP_KERNEL);
110233294Sstas	if (wm8350_gpio == NULL)
111233294Sstas		return -ENOMEM;
112233294Sstas
113233294Sstas	wm8350_gpio->wm8350 = wm8350;
114233294Sstas	wm8350_gpio->gpio_chip = template_chip;
115233294Sstas	wm8350_gpio->gpio_chip.ngpio = 13;
116233294Sstas	wm8350_gpio->gpio_chip.parent = &pdev->dev;
117233294Sstas	if (pdata && pdata->gpio_base)
118233294Sstas		wm8350_gpio->gpio_chip.base = pdata->gpio_base;
119233294Sstas	else
120102644Snectar		wm8350_gpio->gpio_chip.base = -1;
121102644Snectar
122102644Snectar	return devm_gpiochip_add_data(&pdev->dev, &wm8350_gpio->gpio_chip, wm8350_gpio);
123102644Snectar}
124102644Snectar
125233294Sstasstatic struct platform_driver wm8350_gpio_driver = {
126102644Snectar	.driver.name	= "wm8350-gpio",
127102644Snectar	.probe		= wm8350_gpio_probe,
128102644Snectar};
129102644Snectar
130102644Snectarstatic int __init wm8350_gpio_init(void)
13155682Smarkm{
132233294Sstas	return platform_driver_register(&wm8350_gpio_driver);
133233294Sstas}
134233294Sstassubsys_initcall(wm8350_gpio_init);
135233294Sstas
136233294Sstasstatic void __exit wm8350_gpio_exit(void)
137233294Sstas{
138233294Sstas	platform_driver_unregister(&wm8350_gpio_driver);
139233294Sstas}
140233294Sstasmodule_exit(wm8350_gpio_exit);
141233294Sstas
142233294SstasMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
143233294SstasMODULE_DESCRIPTION("GPIO interface for WM8350 PMICs");
144233294SstasMODULE_LICENSE("GPL");
145233294SstasMODULE_ALIAS("platform:wm8350-gpio");
146233294Sstas