summaryrefslogtreecommitdiff
path: root/drivers/gpio/db8500_gpio.c
blob: eefb56d83f69537b2d34d780ea32f665d6ece3d9 (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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/*
 * Code ported from Nomadik GPIO driver in ST-Ericsson Linux kernel code.
 * The purpose is that GPIO config found in kernel should work by simply
 * copy-paste it to U-Boot.
 *
 * Original Linux authors:
 * Copyright (C) 2008,2009 STMicroelectronics
 * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it>
 *   Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.com>
 *
 * Ported to U-Boot by:
 * Copyright (C) 2010 Joakim Axelsson <joakim.axelsson AT stericsson.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <common.h>
#include <asm/io.h>

#include <asm/arch/db8500_gpio.h>
#include <asm/arch/db8500_pincfg.h>
#include <linux/compiler.h>

#define IO_ADDR(x) (void *) (x)

/*
 * The GPIO module in the db8500 Systems-on-Chip is an
 * AMBA device, managing 32 pins and alternate functions. The logic block
 * is currently only used in the db8500.
 */

#define GPIO_TOTAL_PINS		268
#define GPIO_PINS_PER_BLOCK	32
#define GPIO_BLOCKS_COUNT	(GPIO_TOTAL_PINS/GPIO_PINS_PER_BLOCK + 1)
#define GPIO_BLOCK(pin)		(((pin + GPIO_PINS_PER_BLOCK) >> 5) - 1)
#define GPIO_PIN_WITHIN_BLOCK(pin)	((pin)%(GPIO_PINS_PER_BLOCK))

/* Register in the logic block */
#define DB8500_GPIO_DAT		0x00
#define DB8500_GPIO_DATS	0x04
#define DB8500_GPIO_DATC	0x08
#define DB8500_GPIO_PDIS	0x0c
#define DB8500_GPIO_DIR		0x10
#define DB8500_GPIO_DIRS	0x14
#define DB8500_GPIO_DIRC	0x18
#define DB8500_GPIO_SLPC	0x1c
#define DB8500_GPIO_AFSLA	0x20
#define DB8500_GPIO_AFSLB	0x24

#define DB8500_GPIO_RIMSC	0x40
#define DB8500_GPIO_FIMSC	0x44
#define DB8500_GPIO_IS		0x48
#define DB8500_GPIO_IC		0x4c
#define DB8500_GPIO_RWIMSC	0x50
#define DB8500_GPIO_FWIMSC	0x54
#define DB8500_GPIO_WKS		0x58

static void __iomem *get_gpio_addr(unsigned gpio)
{
	/* Our list of GPIO chips */
	static void __iomem *gpio_addrs[GPIO_BLOCKS_COUNT] = {
		IO_ADDR(CFG_GPIO_0_BASE),
		IO_ADDR(CFG_GPIO_1_BASE),
		IO_ADDR(CFG_GPIO_2_BASE),
		IO_ADDR(CFG_GPIO_3_BASE),
		IO_ADDR(CFG_GPIO_4_BASE),
		IO_ADDR(CFG_GPIO_5_BASE),
		IO_ADDR(CFG_GPIO_6_BASE),
		IO_ADDR(CFG_GPIO_7_BASE),
		IO_ADDR(CFG_GPIO_8_BASE)
	};

	return gpio_addrs[GPIO_BLOCK(gpio)];
}

static unsigned get_gpio_offset(unsigned gpio)
{
	return GPIO_PIN_WITHIN_BLOCK(gpio);
}

/* Can only be called from config_pin. Don't configure alt-mode directly */
static void gpio_set_mode(unsigned gpio, enum db8500_gpio_alt mode)
{
	void __iomem *addr = get_gpio_addr(gpio);
	unsigned offset = get_gpio_offset(gpio);
	u32 bit = 1 << offset;
	u32 afunc, bfunc;

	afunc = readl(addr + DB8500_GPIO_AFSLA) & ~bit;
	bfunc = readl(addr + DB8500_GPIO_AFSLB) & ~bit;
	if (mode & DB8500_GPIO_ALT_A)
		afunc |= bit;
	if (mode & DB8500_GPIO_ALT_B)
		bfunc |= bit;
	writel(afunc, addr + DB8500_GPIO_AFSLA);
	writel(bfunc, addr + DB8500_GPIO_AFSLB);
}

/**
 * db8500_gpio_set_pull() - enable/disable pull up/down on a gpio
 * @gpio: pin number
 * @pull: one of DB8500_GPIO_PULL_DOWN, DB8500_GPIO_PULL_UP,
 *  and DB8500_GPIO_PULL_NONE
 *
 * Enables/disables pull up/down on a specified pin.  This only takes effect if
 * the pin is configured as an input (either explicitly or by the alternate
 * function).
 *
 * NOTE: If enabling the pull up/down, the caller must ensure that the GPIO is
 * configured as an input.  Otherwise, due to the way the controller registers
 * work, this function will change the value output on the pin.
 */
void db8500_gpio_set_pull(unsigned gpio, enum db8500_gpio_pull pull)
{
	void __iomem *addr = get_gpio_addr(gpio);
	unsigned offset = get_gpio_offset(gpio);
	u32 bit = 1 << offset;
	u32 pdis;

	pdis = readl(addr + DB8500_GPIO_PDIS);
	if (pull == DB8500_GPIO_PULL_NONE)
		pdis |= bit;
	else
		pdis &= ~bit;
	writel(pdis, addr + DB8500_GPIO_PDIS);

	if (pull == DB8500_GPIO_PULL_UP)
		writel(bit, addr + DB8500_GPIO_DATS);
	else if (pull == DB8500_GPIO_PULL_DOWN)
		writel(bit, addr + DB8500_GPIO_DATC);
}

void db8500_gpio_make_input(unsigned gpio)
{
	void __iomem *addr = get_gpio_addr(gpio);
	unsigned offset = get_gpio_offset(gpio);

	writel(1 << offset, addr + DB8500_GPIO_DIRC);
}

int db8500_gpio_get_input(unsigned gpio)
{
	void __iomem *addr = get_gpio_addr(gpio);
	unsigned offset = get_gpio_offset(gpio);
	u32 bit = 1 << offset;

	printf("db8500_gpio_get_input gpio=%u addr=%p offset=%u bit=%#x\n",
		gpio, addr, offset, bit);

	return (readl(addr + DB8500_GPIO_DAT) & bit) != 0;
}

void db8500_gpio_make_output(unsigned gpio, int val)
{
	void __iomem *addr = get_gpio_addr(gpio);
	unsigned offset = get_gpio_offset(gpio);

	writel(1 << offset, addr + DB8500_GPIO_DIRS);
	db8500_gpio_set_output(gpio, val);
}

void db8500_gpio_set_output(unsigned gpio, int val)
{
	void __iomem *addr = get_gpio_addr(gpio);
	unsigned offset = get_gpio_offset(gpio);

	if (val)
		writel(1 << offset, addr + DB8500_GPIO_DATS);
	else
		writel(1 << offset, addr + DB8500_GPIO_DATC);
}

/**
 * config_pin - configure a pin's mux attributes
 * @cfg: pin configuration
 *
 * Configures a pin's mode (alternate function or GPIO), its pull up status,
 * and its sleep mode based on the specified configuration.  The @cfg is
 * usually one of the SoC specific macros defined in mach/<soc>-pins.h.  These
 * are constructed using, and can be further enhanced with, the macros in
 * plat/pincfg.h.
 *
 * If a pin's mode is set to GPIO, it is configured as an input to avoid
 * side-effects.  The gpio can be manipulated later using standard GPIO API
 * calls.
 */
static void config_pin(unsigned long cfg)
{
	int pin = PIN_NUM(cfg);
	int pull = PIN_PULL(cfg);
	int af = PIN_ALT(cfg);
	int output = PIN_DIR(cfg);
	int val = PIN_VAL(cfg);

	if (output)
		db8500_gpio_make_output(pin, val);
	else {
		db8500_gpio_make_input(pin);
		db8500_gpio_set_pull(pin, pull);
	}

	gpio_set_mode(pin, af);
}

/**
 * db8500_config_pins - configure several pins at once
 * @cfgs: array of pin configurations
 * @num: number of elments in the array
 *
 * Configures several pins using config_pin(). Refer to that function for
 * further information.
 */
void db8500_gpio_config_pins(unsigned long *cfgs, size_t num)
{
	size_t i;

	for (i = 0; i < num; i++)
		config_pin(cfgs[i]);
}