1/*
2 * Moschip MCS814x GPIO support
3 *
4 * Copyright (C) 2012, Florian Fainelli <florian@openwrt.org>
5 *
6 * Licensed under the GPLv2
7 */
8
9#include <linux/init.h>
10#include <linux/module.h>
11#include <linux/kernel.h>
12#include <linux/slab.h>
13#include <linux/platform_device.h>
14#include <linux/gpio.h>
15#include <linux/io.h>
16#include <linux/of.h>
17#include <linux/of_address.h>
18
19struct mcs814x_gpio_chip {
20	void __iomem *regs;
21	struct gpio_chip chip;
22};
23
24#define GPIO_PIN	0x00
25#define GPIO_DIR	0x04
26
27#define to_mcs814x_gpio_chip(x)	container_of(x, struct mcs814x_gpio_chip, chip)
28
29static int mcs814x_gpio_get(struct gpio_chip *chip, unsigned offset)
30{
31	struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip);
32
33	return readl_relaxed(mcs814x->regs + GPIO_PIN) & (1 << offset);
34}
35
36static void mcs814x_gpio_set(struct gpio_chip *chip,
37				unsigned offset, int value)
38{
39	struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip);
40	u32 mask;
41
42	mask = readl_relaxed(mcs814x->regs + GPIO_PIN);
43	if (value)
44		mask |= (1 << offset);
45	else
46		mask &= ~(1 << offset);
47	writel_relaxed(mask, mcs814x->regs + GPIO_PIN);
48}
49
50static int mcs814x_gpio_direction_output(struct gpio_chip *chip,
51					unsigned offset, int value)
52{
53	struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip);
54	u32 mask;
55
56	mask = readl_relaxed(mcs814x->regs + GPIO_DIR);
57	mask &= ~(1 << offset);
58	writel_relaxed(mask, mcs814x->regs + GPIO_DIR);
59
60	return 0;
61}
62
63static int mcs814x_gpio_direction_input(struct gpio_chip *chip,
64					unsigned offset)
65{
66	struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip);
67	u32 mask;
68
69	mask = readl_relaxed(mcs814x->regs + GPIO_DIR);
70	mask |= (1 << offset);
71	writel_relaxed(mask, mcs814x->regs + GPIO_DIR);
72
73	return 0;
74}
75
76static int mcs814x_gpio_probe(struct platform_device *pdev)
77{
78	struct resource *res;
79	struct mcs814x_gpio_chip *mcs814x_chip;
80	int ret;
81	const unsigned int *num_gpios;
82
83	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
84	if (!res)
85		return -ENODEV;
86
87	num_gpios = of_get_property(pdev->dev.of_node, "num-gpios", NULL);
88	if (!num_gpios)
89		dev_err(&pdev->dev, "FIXME: no num-gpios property\n");
90
91	mcs814x_chip = kzalloc(sizeof(*mcs814x_chip), GFP_KERNEL);
92	if (!mcs814x_chip)
93		return -ENOMEM;
94
95	mcs814x_chip->regs = devm_ioremap_resource(&pdev->dev, res);
96	if (!mcs814x_chip->regs) {
97		ret = -ENOMEM;
98		goto out;
99	}
100
101	platform_set_drvdata(pdev, mcs814x_chip);
102
103#ifdef CONFIG_OF_GPIO
104	mcs814x_chip->chip.of_node = pdev->dev.of_node;
105#endif
106
107	mcs814x_chip->chip.label = pdev->name;
108	mcs814x_chip->chip.get = mcs814x_gpio_get;
109	mcs814x_chip->chip.set = mcs814x_gpio_set;
110	mcs814x_chip->chip.direction_input = mcs814x_gpio_direction_input;
111	mcs814x_chip->chip.direction_output = mcs814x_gpio_direction_output;
112	mcs814x_chip->chip.ngpio = be32_to_cpup(num_gpios);
113	/* we want dynamic base allocation */
114	mcs814x_chip->chip.base = -1;
115
116	ret = gpiochip_add(&mcs814x_chip->chip);
117	if (ret) {
118		dev_err(&pdev->dev, "failed to register gpiochip\n");
119		goto out;
120	}
121
122	return 0;
123
124out:
125	platform_set_drvdata(pdev, NULL);
126	kfree(mcs814x_chip);
127	return ret;
128}
129
130static struct of_device_id mcs814x_gpio_ids[] = {
131	{ .compatible = "moschip,mcs814x-gpio" },
132	{ /* sentinel */ },
133};
134
135static struct platform_driver mcs814x_gpio_driver = {
136	.driver	= {
137		.name	= "mcs814x-gpio",
138		.owner	= THIS_MODULE,
139		.of_match_table = mcs814x_gpio_ids,
140	},
141	.probe	= mcs814x_gpio_probe,
142};
143
144int __init mcs814x_gpio_init(void)
145{
146	return platform_driver_register(&mcs814x_gpio_driver);
147}
148postcore_initcall(mcs814x_gpio_init);
149