1/*
2 *  linux/drivers/gpio/gpio-mb86s7x.c
3 *
4 *  Copyright (C) 2015 Fujitsu Semiconductor Limited
5 *  Copyright (C) 2015 Linaro Ltd.
6 *
7 *  This program is free software: you can redistribute it and/or modify
8 *  it under the terms of the GNU General Public License as published by
9 *  the Free Software Foundation, version 2 of the License.
10 *
11 *  This program is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU General Public License for more details.
15 */
16
17#include <linux/io.h>
18#include <linux/init.h>
19#include <linux/clk.h>
20#include <linux/module.h>
21#include <linux/err.h>
22#include <linux/errno.h>
23#include <linux/ioport.h>
24#include <linux/of_device.h>
25#include <linux/gpio/driver.h>
26#include <linux/platform_device.h>
27#include <linux/spinlock.h>
28#include <linux/slab.h>
29
30/*
31 * Only first 8bits of a register correspond to each pin,
32 * so there are 4 registers for 32 pins.
33 */
34#define PDR(x)	(0x0 + x / 8 * 4)
35#define DDR(x)	(0x10 + x / 8 * 4)
36#define PFR(x)	(0x20 + x / 8 * 4)
37
38#define OFFSET(x)	BIT((x) % 8)
39
40struct mb86s70_gpio_chip {
41	struct gpio_chip gc;
42	void __iomem *base;
43	struct clk *clk;
44	spinlock_t lock;
45};
46
47static inline struct mb86s70_gpio_chip *chip_to_mb86s70(struct gpio_chip *gc)
48{
49	return container_of(gc, struct mb86s70_gpio_chip, gc);
50}
51
52static int mb86s70_gpio_request(struct gpio_chip *gc, unsigned gpio)
53{
54	struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc);
55	unsigned long flags;
56	u32 val;
57
58	spin_lock_irqsave(&gchip->lock, flags);
59
60	val = readl(gchip->base + PFR(gpio));
61	if (!(val & OFFSET(gpio))) {
62		spin_unlock_irqrestore(&gchip->lock, flags);
63		return -EINVAL;
64	}
65
66	val &= ~OFFSET(gpio);
67	writel(val, gchip->base + PFR(gpio));
68
69	spin_unlock_irqrestore(&gchip->lock, flags);
70
71	return 0;
72}
73
74static void mb86s70_gpio_free(struct gpio_chip *gc, unsigned gpio)
75{
76	struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc);
77	unsigned long flags;
78	u32 val;
79
80	spin_lock_irqsave(&gchip->lock, flags);
81
82	val = readl(gchip->base + PFR(gpio));
83	val |= OFFSET(gpio);
84	writel(val, gchip->base + PFR(gpio));
85
86	spin_unlock_irqrestore(&gchip->lock, flags);
87}
88
89static int mb86s70_gpio_direction_input(struct gpio_chip *gc, unsigned gpio)
90{
91	struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc);
92	unsigned long flags;
93	unsigned char val;
94
95	spin_lock_irqsave(&gchip->lock, flags);
96
97	val = readl(gchip->base + DDR(gpio));
98	val &= ~OFFSET(gpio);
99	writel(val, gchip->base + DDR(gpio));
100
101	spin_unlock_irqrestore(&gchip->lock, flags);
102
103	return 0;
104}
105
106static int mb86s70_gpio_direction_output(struct gpio_chip *gc,
107					 unsigned gpio, int value)
108{
109	struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc);
110	unsigned long flags;
111	unsigned char val;
112
113	spin_lock_irqsave(&gchip->lock, flags);
114
115	val = readl(gchip->base + PDR(gpio));
116	if (value)
117		val |= OFFSET(gpio);
118	else
119		val &= ~OFFSET(gpio);
120	writel(val, gchip->base + PDR(gpio));
121
122	val = readl(gchip->base + DDR(gpio));
123	val |= OFFSET(gpio);
124	writel(val, gchip->base + DDR(gpio));
125
126	spin_unlock_irqrestore(&gchip->lock, flags);
127
128	return 0;
129}
130
131static int mb86s70_gpio_get(struct gpio_chip *gc, unsigned gpio)
132{
133	struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc);
134
135	return !!(readl(gchip->base + PDR(gpio)) & OFFSET(gpio));
136}
137
138static void mb86s70_gpio_set(struct gpio_chip *gc, unsigned gpio, int value)
139{
140	struct mb86s70_gpio_chip *gchip = chip_to_mb86s70(gc);
141	unsigned long flags;
142	unsigned char val;
143
144	spin_lock_irqsave(&gchip->lock, flags);
145
146	val = readl(gchip->base + PDR(gpio));
147	if (value)
148		val |= OFFSET(gpio);
149	else
150		val &= ~OFFSET(gpio);
151	writel(val, gchip->base + PDR(gpio));
152
153	spin_unlock_irqrestore(&gchip->lock, flags);
154}
155
156static int mb86s70_gpio_probe(struct platform_device *pdev)
157{
158	struct mb86s70_gpio_chip *gchip;
159	struct resource *res;
160	int ret;
161
162	gchip = devm_kzalloc(&pdev->dev, sizeof(*gchip), GFP_KERNEL);
163	if (gchip == NULL)
164		return -ENOMEM;
165
166	platform_set_drvdata(pdev, gchip);
167
168	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
169	gchip->base = devm_ioremap_resource(&pdev->dev, res);
170	if (IS_ERR(gchip->base))
171		return PTR_ERR(gchip->base);
172
173	gchip->clk = devm_clk_get(&pdev->dev, NULL);
174	if (IS_ERR(gchip->clk))
175		return PTR_ERR(gchip->clk);
176
177	clk_prepare_enable(gchip->clk);
178
179	spin_lock_init(&gchip->lock);
180
181	gchip->gc.direction_output = mb86s70_gpio_direction_output;
182	gchip->gc.direction_input = mb86s70_gpio_direction_input;
183	gchip->gc.request = mb86s70_gpio_request;
184	gchip->gc.free = mb86s70_gpio_free;
185	gchip->gc.get = mb86s70_gpio_get;
186	gchip->gc.set = mb86s70_gpio_set;
187	gchip->gc.label = dev_name(&pdev->dev);
188	gchip->gc.ngpio = 32;
189	gchip->gc.owner = THIS_MODULE;
190	gchip->gc.dev = &pdev->dev;
191	gchip->gc.base = -1;
192
193	platform_set_drvdata(pdev, gchip);
194
195	ret = gpiochip_add(&gchip->gc);
196	if (ret) {
197		dev_err(&pdev->dev, "couldn't register gpio driver\n");
198		clk_disable_unprepare(gchip->clk);
199	}
200
201	return ret;
202}
203
204static int mb86s70_gpio_remove(struct platform_device *pdev)
205{
206	struct mb86s70_gpio_chip *gchip = platform_get_drvdata(pdev);
207
208	gpiochip_remove(&gchip->gc);
209	clk_disable_unprepare(gchip->clk);
210
211	return 0;
212}
213
214static const struct of_device_id mb86s70_gpio_dt_ids[] = {
215	{ .compatible = "fujitsu,mb86s70-gpio" },
216	{ /* sentinel */ }
217};
218MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids);
219
220static struct platform_driver mb86s70_gpio_driver = {
221	.driver = {
222		.name = "mb86s70-gpio",
223		.of_match_table = mb86s70_gpio_dt_ids,
224	},
225	.probe = mb86s70_gpio_probe,
226	.remove = mb86s70_gpio_remove,
227};
228
229static int __init mb86s70_gpio_init(void)
230{
231	return platform_driver_register(&mb86s70_gpio_driver);
232}
233module_init(mb86s70_gpio_init);
234
235MODULE_DESCRIPTION("MB86S7x GPIO Driver");
236MODULE_ALIAS("platform:mb86s70-gpio");
237MODULE_LICENSE("GPL");
238