summaryrefslogtreecommitdiffstats
path: root/kernel/arch/powerpc/sysdev/simple_gpio.c
blob: ff5e73230a365933bb826bd36a20c8506d88026d (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
149
150
151
152
/*
 * Simple Memory-Mapped GPIOs
 *
 * Copyright (c) MontaVista Software, Inc. 2008.
 *
 * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <asm/prom.h>
#include "simple_gpio.h"

struct u8_gpio_chip {
	struct of_mm_gpio_chip mm_gc;
	spinlock_t lock;

	/* shadowed data register to clear/set bits safely */
	u8 data;
};

static struct u8_gpio_chip *to_u8_gpio_chip(struct of_mm_gpio_chip *mm_gc)
{
	return container_of(mm_gc, struct u8_gpio_chip, mm_gc);
}

static u8 u8_pin2mask(unsigned int pin)
{
	return 1 << (8 - 1 - pin);
}

static int u8_gpio_get(struct gpio_chip *gc, unsigned int gpio)
{
	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);

	return in_8(mm_gc->regs) & u8_pin2mask(gpio);
}

static void u8_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
{
	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
	struct u8_gpio_chip *u8_gc = to_u8_gpio_chip(mm_gc);
	unsigned long flags;

	spin_lock_irqsave(&u8_gc->lock, flags);

	if (val)
		u8_gc->data |= u8_pin2mask(gpio);
	else
		u8_gc->data &= ~u8_pin2mask(gpio);

	out_8(mm_gc->regs, u8_gc->data);

	spin_unlock_irqrestore(&u8_gc->lock, flags);
}

static int u8_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
{
	return 0;
}

static int u8_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
{
	u8_gpio_set(gc, gpio, val);
	return 0;
}

static void u8_gpio_save_regs(struct of_mm_gpio_chip *mm_gc)
{
	struct u8_gpio_chip *u8_gc = to_u8_gpio_chip(mm_gc);

	u8_gc->data = in_8(mm_gc->regs);
}

static int __init u8_simple_gpiochip_add(struct device_node *np)
{
	int ret;
	struct u8_gpio_chip *u8_gc;
	struct of_mm_gpio_chip *mm_gc;
	struct gpio_chip *gc;

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

	spin_lock_init(&u8_gc->lock);

	mm_gc = &u8_gc->mm_gc;
	gc = &mm_gc->gc;

	mm_gc->save_regs = u8_gpio_save_regs;
	gc->ngpio = 8;
	gc->direction_input = u8_gpio_dir_in;
	gc->direction_output = u8_gpio_dir_out;
	gc->get = u8_gpio_get;
	gc->set = u8_gpio_set;

	ret = of_mm_gpiochip_add(np, mm_gc);
	if (ret)
		goto err;
	return 0;
err:
	kfree(u8_gc);
	return ret;
}

void __init simple_gpiochip_init(const char *compatible)
{
	struct device_node *np;

	for_each_compatible_node(np, NULL, compatible) {
		int ret;
		struct resource r;

		ret = of_address_to_resource(np, 0, &r);
		if (ret)
			goto err;

		switch (resource_size(&r)) {
		case 1:
			ret = u8_simple_gpiochip_add(np);
			if (ret)
				goto err;
			break;
		default:
			/*
			 * Whenever you need support for GPIO bank width > 1,
			 * please just turn u8_ code into huge macros, and
			 * construct needed uX_ code with it.
			 */
			ret = -ENOSYS;
			goto err;
		}
		continue;
err:
		pr_err("%s: registration failed, status %d\n",
		       np->full_name, ret);
	}
}