summaryrefslogtreecommitdiffstats
path: root/target/linux/mcs814x/files-3.3/drivers/gpio/gpio-mcs814x.c
blob: ea53c192bc5f614387cb46ec6fe4aa9f400caf5f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * Moschip MCS814x GPIO support
 *
 * Copyright (C) 2012, Florian Fainelli <florian@openwrt.org>
 *
 * Licensed under the GPLv2
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>

struct mcs814x_gpio_chip {
	void __iomem *regs;
	struct gpio_chip chip;
};

#define GPIO_PIN	0x00
#define GPIO_DIR	0x04

#define to_mcs814x_gpio_chip(x)	container_of(x, struct mcs814x_gpio_chip, chip)

static int mcs814x_gpio_get(struct gpio_chip *chip, unsigned offset)
{
	struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip);

	return __raw_readl(mcs814x->regs + GPIO_PIN) & (1 << offset);
}

static void mcs814x_gpio_set(struct gpio_chip *chip,
				unsigned offset, int value)
{
	struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip);
	u32 mask;

	mask = __raw_readl(mcs814x->regs + GPIO_PIN);
	if (value)
		mask |= (1 << offset);
	else
		mask &= ~(1 << offset);
	__raw_writel(mask, mcs814x->regs + GPIO_PIN);
}

static int mcs814x_gpio_direction_output(struct gpio_chip *chip,
					unsigned offset, int value)
{
	struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip);
	u32 mask;

	mask = __raw_readl(mcs814x->regs + GPIO_DIR);
	mask &= ~(1 << offset);
	__raw_writel(mask, mcs814x->regs + GPIO_DIR);

	return 0;
}

static int mcs814x_gpio_direction_input(struct gpio_chip *chip,
					unsigned offset)
{
	struct mcs814x_gpio_chip *mcs814x = to_mcs814x_gpio_chip(chip);
	u32 mask;

	mask = __raw_readl(mcs814x->regs + GPIO_DIR);
	mask |= (1 << offset);
	__raw_writel(mask, mcs814x->regs + GPIO_DIR);

	return 0;
}

static int __devinit mcs814x_gpio_probe(struct platform_device *pdev)
{
	struct resource *res;
	struct mcs814x_gpio_chip *mcs814x_chip;
	int ret;
	const unsigned int *num_gpios;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res)
		return -ENODEV;

	num_gpios = of_get_property(pdev->dev.of_node, "num-gpios", NULL);
	if (!num_gpios)
		dev_err(&pdev->dev, "FIXME: no num-gpios property\n");

	mcs814x_chip = kzalloc(sizeof(*mcs814x_chip), GFP_KERNEL);
	if (!mcs814x_chip)
		return -ENOMEM;

	mcs814x_chip->regs = devm_request_and_ioremap(&pdev->dev, res);
	if (!mcs814x_chip->regs) {
		ret = -ENOMEM;
		goto out;
	}

	platform_set_drvdata(pdev, mcs814x_chip);

#ifdef CONFIG_OF_GPIO
	mcs814x_chip->chip.of_node = pdev->dev.of_node;
#endif

	mcs814x_chip->chip.label = pdev->name;
	mcs814x_chip->chip.get = mcs814x_gpio_get;
	mcs814x_chip->chip.set = mcs814x_gpio_set;
	mcs814x_chip->chip.direction_input = mcs814x_gpio_direction_input;
	mcs814x_chip->chip.direction_output = mcs814x_gpio_direction_output;
	mcs814x_chip->chip.ngpio = be32_to_cpup(num_gpios);
	/* we want dynamic base allocation */
	mcs814x_chip->chip.base = -1;

	ret = gpiochip_add(&mcs814x_chip->chip);
	if (ret) {
		dev_err(&pdev->dev, "failed to register gpiochip\n");
		goto out;
	}

	return 0;

out:
	platform_set_drvdata(pdev, NULL);
	kfree(mcs814x_chip);
	return ret;
}

static struct of_device_id mcs814x_gpio_ids[] __devinitdata = {
	{ .compatible = "moschip,mcs814x-gpio" },
	{ /* sentinel */ },
};

static struct platform_driver mcs814x_gpio_driver = {
	.driver	= {
		.name	= "mcs814x-gpio",
		.owner	= THIS_MODULE,
		.of_match_table = mcs814x_gpio_ids,
	},
	.probe	= mcs814x_gpio_probe,
};

int __init mcs814x_gpio_init(void)
{
	return platform_driver_register(&mcs814x_gpio_driver);
}
postcore_initcall(mcs814x_gpio_init);