]>
Commit | Line | Data |
---|---|---|
84dee301 MP |
1 | /* |
2 | * Code ported from Nomadik GPIO driver in ST-Ericsson Linux kernel code. | |
3 | * The purpose is that GPIO config found in kernel should work by simply | |
a187559e | 4 | * copy-paste it to U-Boot. |
84dee301 MP |
5 | * |
6 | * Original Linux authors: | |
7 | * Copyright (C) 2008,2009 STMicroelectronics | |
8 | * Copyright (C) 2009 Alessandro Rubini <[email protected]> | |
9 | * Rewritten based on work by Prafulla WADASKAR <[email protected]> | |
10 | * | |
a187559e | 11 | * Ported to U-Boot by: |
84dee301 MP |
12 | * Copyright (C) 2010 Joakim Axelsson <joakim.axelsson AT stericsson.com> |
13 | * | |
14 | * This program is free software; you can redistribute it and/or modify | |
15 | * it under the terms of the GNU General Public License version 2 as | |
16 | * published by the Free Software Foundation. | |
17 | */ | |
18 | ||
19 | #include <common.h> | |
20 | #include <asm/io.h> | |
21 | ||
22 | #include <asm/arch/db8500_gpio.h> | |
23 | #include <asm/arch/db8500_pincfg.h> | |
24 | #include <linux/compiler.h> | |
25 | ||
26 | #define IO_ADDR(x) (void *) (x) | |
27 | ||
28 | /* | |
29 | * The GPIO module in the db8500 Systems-on-Chip is an | |
30 | * AMBA device, managing 32 pins and alternate functions. The logic block | |
31 | * is currently only used in the db8500. | |
32 | */ | |
33 | ||
34 | #define GPIO_TOTAL_PINS 268 | |
35 | #define GPIO_PINS_PER_BLOCK 32 | |
36 | #define GPIO_BLOCKS_COUNT (GPIO_TOTAL_PINS/GPIO_PINS_PER_BLOCK + 1) | |
37 | #define GPIO_BLOCK(pin) (((pin + GPIO_PINS_PER_BLOCK) >> 5) - 1) | |
38 | #define GPIO_PIN_WITHIN_BLOCK(pin) ((pin)%(GPIO_PINS_PER_BLOCK)) | |
39 | ||
40 | /* Register in the logic block */ | |
41 | #define DB8500_GPIO_DAT 0x00 | |
42 | #define DB8500_GPIO_DATS 0x04 | |
43 | #define DB8500_GPIO_DATC 0x08 | |
44 | #define DB8500_GPIO_PDIS 0x0c | |
45 | #define DB8500_GPIO_DIR 0x10 | |
46 | #define DB8500_GPIO_DIRS 0x14 | |
47 | #define DB8500_GPIO_DIRC 0x18 | |
48 | #define DB8500_GPIO_SLPC 0x1c | |
49 | #define DB8500_GPIO_AFSLA 0x20 | |
50 | #define DB8500_GPIO_AFSLB 0x24 | |
51 | ||
52 | #define DB8500_GPIO_RIMSC 0x40 | |
53 | #define DB8500_GPIO_FIMSC 0x44 | |
54 | #define DB8500_GPIO_IS 0x48 | |
55 | #define DB8500_GPIO_IC 0x4c | |
56 | #define DB8500_GPIO_RWIMSC 0x50 | |
57 | #define DB8500_GPIO_FWIMSC 0x54 | |
58 | #define DB8500_GPIO_WKS 0x58 | |
59 | ||
60 | static void __iomem *get_gpio_addr(unsigned gpio) | |
61 | { | |
62 | /* Our list of GPIO chips */ | |
63 | static void __iomem *gpio_addrs[GPIO_BLOCKS_COUNT] = { | |
64 | IO_ADDR(CFG_GPIO_0_BASE), | |
65 | IO_ADDR(CFG_GPIO_1_BASE), | |
66 | IO_ADDR(CFG_GPIO_2_BASE), | |
67 | IO_ADDR(CFG_GPIO_3_BASE), | |
68 | IO_ADDR(CFG_GPIO_4_BASE), | |
69 | IO_ADDR(CFG_GPIO_5_BASE), | |
70 | IO_ADDR(CFG_GPIO_6_BASE), | |
71 | IO_ADDR(CFG_GPIO_7_BASE), | |
72 | IO_ADDR(CFG_GPIO_8_BASE) | |
73 | }; | |
74 | ||
75 | return gpio_addrs[GPIO_BLOCK(gpio)]; | |
76 | } | |
77 | ||
78 | static unsigned get_gpio_offset(unsigned gpio) | |
79 | { | |
80 | return GPIO_PIN_WITHIN_BLOCK(gpio); | |
81 | } | |
82 | ||
83 | /* Can only be called from config_pin. Don't configure alt-mode directly */ | |
84 | static void gpio_set_mode(unsigned gpio, enum db8500_gpio_alt mode) | |
85 | { | |
86 | void __iomem *addr = get_gpio_addr(gpio); | |
87 | unsigned offset = get_gpio_offset(gpio); | |
88 | u32 bit = 1 << offset; | |
89 | u32 afunc, bfunc; | |
90 | ||
91 | afunc = readl(addr + DB8500_GPIO_AFSLA) & ~bit; | |
92 | bfunc = readl(addr + DB8500_GPIO_AFSLB) & ~bit; | |
93 | if (mode & DB8500_GPIO_ALT_A) | |
94 | afunc |= bit; | |
95 | if (mode & DB8500_GPIO_ALT_B) | |
96 | bfunc |= bit; | |
97 | writel(afunc, addr + DB8500_GPIO_AFSLA); | |
98 | writel(bfunc, addr + DB8500_GPIO_AFSLB); | |
99 | } | |
100 | ||
101 | /** | |
102 | * db8500_gpio_set_pull() - enable/disable pull up/down on a gpio | |
103 | * @gpio: pin number | |
104 | * @pull: one of DB8500_GPIO_PULL_DOWN, DB8500_GPIO_PULL_UP, | |
105 | * and DB8500_GPIO_PULL_NONE | |
106 | * | |
107 | * Enables/disables pull up/down on a specified pin. This only takes effect if | |
108 | * the pin is configured as an input (either explicitly or by the alternate | |
109 | * function). | |
110 | * | |
111 | * NOTE: If enabling the pull up/down, the caller must ensure that the GPIO is | |
112 | * configured as an input. Otherwise, due to the way the controller registers | |
113 | * work, this function will change the value output on the pin. | |
114 | */ | |
115 | void db8500_gpio_set_pull(unsigned gpio, enum db8500_gpio_pull pull) | |
116 | { | |
117 | void __iomem *addr = get_gpio_addr(gpio); | |
118 | unsigned offset = get_gpio_offset(gpio); | |
119 | u32 bit = 1 << offset; | |
120 | u32 pdis; | |
121 | ||
122 | pdis = readl(addr + DB8500_GPIO_PDIS); | |
123 | if (pull == DB8500_GPIO_PULL_NONE) | |
124 | pdis |= bit; | |
125 | else | |
126 | pdis &= ~bit; | |
127 | writel(pdis, addr + DB8500_GPIO_PDIS); | |
128 | ||
129 | if (pull == DB8500_GPIO_PULL_UP) | |
130 | writel(bit, addr + DB8500_GPIO_DATS); | |
131 | else if (pull == DB8500_GPIO_PULL_DOWN) | |
132 | writel(bit, addr + DB8500_GPIO_DATC); | |
133 | } | |
134 | ||
135 | void db8500_gpio_make_input(unsigned gpio) | |
136 | { | |
137 | void __iomem *addr = get_gpio_addr(gpio); | |
138 | unsigned offset = get_gpio_offset(gpio); | |
139 | ||
140 | writel(1 << offset, addr + DB8500_GPIO_DIRC); | |
141 | } | |
142 | ||
143 | int db8500_gpio_get_input(unsigned gpio) | |
144 | { | |
145 | void __iomem *addr = get_gpio_addr(gpio); | |
146 | unsigned offset = get_gpio_offset(gpio); | |
147 | u32 bit = 1 << offset; | |
148 | ||
149 | printf("db8500_gpio_get_input gpio=%u addr=%p offset=%u bit=%#x\n", | |
150 | gpio, addr, offset, bit); | |
151 | ||
152 | return (readl(addr + DB8500_GPIO_DAT) & bit) != 0; | |
153 | } | |
154 | ||
155 | void db8500_gpio_make_output(unsigned gpio, int val) | |
156 | { | |
157 | void __iomem *addr = get_gpio_addr(gpio); | |
158 | unsigned offset = get_gpio_offset(gpio); | |
159 | ||
160 | writel(1 << offset, addr + DB8500_GPIO_DIRS); | |
161 | db8500_gpio_set_output(gpio, val); | |
162 | } | |
163 | ||
164 | void db8500_gpio_set_output(unsigned gpio, int val) | |
165 | { | |
166 | void __iomem *addr = get_gpio_addr(gpio); | |
167 | unsigned offset = get_gpio_offset(gpio); | |
168 | ||
169 | if (val) | |
170 | writel(1 << offset, addr + DB8500_GPIO_DATS); | |
171 | else | |
172 | writel(1 << offset, addr + DB8500_GPIO_DATC); | |
173 | } | |
174 | ||
175 | /** | |
176 | * config_pin - configure a pin's mux attributes | |
177 | * @cfg: pin confguration | |
178 | * | |
179 | * Configures a pin's mode (alternate function or GPIO), its pull up status, | |
180 | * and its sleep mode based on the specified configuration. The @cfg is | |
181 | * usually one of the SoC specific macros defined in mach/<soc>-pins.h. These | |
182 | * are constructed using, and can be further enhanced with, the macros in | |
183 | * plat/pincfg.h. | |
184 | * | |
185 | * If a pin's mode is set to GPIO, it is configured as an input to avoid | |
186 | * side-effects. The gpio can be manipulated later using standard GPIO API | |
187 | * calls. | |
188 | */ | |
189 | static void config_pin(unsigned long cfg) | |
190 | { | |
191 | int pin = PIN_NUM(cfg); | |
192 | int pull = PIN_PULL(cfg); | |
193 | int af = PIN_ALT(cfg); | |
194 | int output = PIN_DIR(cfg); | |
195 | int val = PIN_VAL(cfg); | |
196 | ||
197 | if (output) | |
198 | db8500_gpio_make_output(pin, val); | |
199 | else { | |
200 | db8500_gpio_make_input(pin); | |
201 | db8500_gpio_set_pull(pin, pull); | |
202 | } | |
203 | ||
204 | gpio_set_mode(pin, af); | |
205 | } | |
206 | ||
207 | /** | |
208 | * db8500_config_pins - configure several pins at once | |
209 | * @cfgs: array of pin configurations | |
210 | * @num: number of elments in the array | |
211 | * | |
212 | * Configures several pins using config_pin(). Refer to that function for | |
213 | * further information. | |
214 | */ | |
215 | void db8500_gpio_config_pins(unsigned long *cfgs, size_t num) | |
216 | { | |
217 | size_t i; | |
218 | ||
219 | for (i = 0; i < num; i++) | |
220 | config_pin(cfgs[i]); | |
221 | } |