Tom Rini | 83d290c | 2018-05-06 17:58:06 -0400 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 2 | /* |
Bo Shen | 39b787e | 2013-08-13 14:38:32 +0800 | [diff] [blame] | 3 | * Copyright (C) 2013 Bo Shen <voice.shen@atmel.com> |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 4 | * |
| 5 | * Copyright (C) 2009 Jens Scharsig (js_at_ng@scharsoft.de) |
| 6 | * |
| 7 | * Copyright (C) 2005 HP Labs |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 8 | */ |
| 9 | |
| 10 | #include <config.h> |
| 11 | #include <common.h> |
Wenyou Yang | f2f3c15 | 2017-03-23 12:46:21 +0800 | [diff] [blame] | 12 | #include <clk.h> |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 13 | #include <dm.h> |
Simon Glass | 336d461 | 2020-02-03 07:36:16 -0700 | [diff] [blame] | 14 | #include <malloc.h> |
Reinhard Meyer | 86592f6 | 2010-11-07 13:26:14 +0100 | [diff] [blame] | 15 | #include <asm/io.h> |
Alexey Brodkin | 1ace402 | 2014-02-26 17:47:58 +0400 | [diff] [blame] | 16 | #include <linux/sizes.h> |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 17 | #include <asm/gpio.h> |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 18 | #include <asm/arch/hardware.h> |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 19 | #include <asm/arch/at91_pio.h> |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 20 | |
| 21 | #define GPIO_PER_BANK 32 |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 22 | |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 23 | static struct at91_port *at91_pio_get_port(unsigned port) |
| 24 | { |
| 25 | switch (port) { |
| 26 | case AT91_PIO_PORTA: |
| 27 | return (struct at91_port *)ATMEL_BASE_PIOA; |
| 28 | case AT91_PIO_PORTB: |
| 29 | return (struct at91_port *)ATMEL_BASE_PIOB; |
| 30 | case AT91_PIO_PORTC: |
| 31 | return (struct at91_port *)ATMEL_BASE_PIOC; |
| 32 | #if (ATMEL_PIO_PORTS > 3) |
| 33 | case AT91_PIO_PORTD: |
| 34 | return (struct at91_port *)ATMEL_BASE_PIOD; |
| 35 | #if (ATMEL_PIO_PORTS > 4) |
| 36 | case AT91_PIO_PORTE: |
| 37 | return (struct at91_port *)ATMEL_BASE_PIOE; |
| 38 | #endif |
| 39 | #endif |
| 40 | default: |
Wu, Josh | 7d82d89 | 2014-05-07 16:50:45 +0800 | [diff] [blame] | 41 | printf("Error: at91_gpio: Fail to get PIO base!\n"); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 42 | return NULL; |
| 43 | } |
| 44 | } |
| 45 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 46 | static void at91_set_port_pullup(struct at91_port *at91_port, unsigned offset, |
| 47 | int use_pullup) |
| 48 | { |
| 49 | u32 mask; |
| 50 | |
| 51 | mask = 1 << offset; |
| 52 | if (use_pullup) |
| 53 | writel(mask, &at91_port->puer); |
| 54 | else |
| 55 | writel(mask, &at91_port->pudr); |
| 56 | writel(mask, &at91_port->per); |
| 57 | } |
| 58 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 59 | int at91_set_pio_pullup(unsigned port, unsigned pin, int use_pullup) |
| 60 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 61 | struct at91_port *at91_port = at91_pio_get_port(port); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 62 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 63 | if (at91_port && (pin < GPIO_PER_BANK)) |
| 64 | at91_set_port_pullup(at91_port, pin, use_pullup); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 65 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 66 | return 0; |
| 67 | } |
| 68 | |
| 69 | /* |
| 70 | * mux the pin to the "GPIO" peripheral role. |
| 71 | */ |
| 72 | int at91_set_pio_periph(unsigned port, unsigned pin, int use_pullup) |
| 73 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 74 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 75 | u32 mask; |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 76 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 77 | if (at91_port && (pin < GPIO_PER_BANK)) { |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 78 | mask = 1 << pin; |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 79 | writel(mask, &at91_port->idr); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 80 | at91_set_pio_pullup(port, pin, use_pullup); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 81 | writel(mask, &at91_port->per); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 82 | } |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 83 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 84 | return 0; |
| 85 | } |
| 86 | |
| 87 | /* |
| 88 | * mux the pin to the "A" internal peripheral role. |
| 89 | */ |
| 90 | int at91_set_a_periph(unsigned port, unsigned pin, int use_pullup) |
| 91 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 92 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 93 | u32 mask; |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 94 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 95 | if (at91_port && (pin < GPIO_PER_BANK)) { |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 96 | mask = 1 << pin; |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 97 | writel(mask, &at91_port->idr); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 98 | at91_set_pio_pullup(port, pin, use_pullup); |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 99 | writel(mask, &at91_port->mux.pio2.asr); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 100 | writel(mask, &at91_port->pdr); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 101 | } |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 102 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 103 | return 0; |
| 104 | } |
| 105 | |
| 106 | /* |
| 107 | * mux the pin to the "B" internal peripheral role. |
| 108 | */ |
| 109 | int at91_set_b_periph(unsigned port, unsigned pin, int use_pullup) |
| 110 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 111 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 112 | u32 mask; |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 113 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 114 | if (at91_port && (pin < GPIO_PER_BANK)) { |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 115 | mask = 1 << pin; |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 116 | writel(mask, &at91_port->idr); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 117 | at91_set_pio_pullup(port, pin, use_pullup); |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 118 | writel(mask, &at91_port->mux.pio2.bsr); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 119 | writel(mask, &at91_port->pdr); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 120 | } |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 121 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 122 | return 0; |
| 123 | } |
| 124 | |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 125 | /* |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 126 | * mux the pin to the "A" internal peripheral role. |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 127 | */ |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 128 | int at91_pio3_set_a_periph(unsigned port, unsigned pin, int use_pullup) |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 129 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 130 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 131 | u32 mask; |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 132 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 133 | if (at91_port && (pin < GPIO_PER_BANK)) { |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 134 | mask = 1 << pin; |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 135 | writel(mask, &at91_port->idr); |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 136 | at91_set_pio_pullup(port, pin, use_pullup); |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 137 | writel(readl(&at91_port->mux.pio3.abcdsr1) & ~mask, |
| 138 | &at91_port->mux.pio3.abcdsr1); |
| 139 | writel(readl(&at91_port->mux.pio3.abcdsr2) & ~mask, |
| 140 | &at91_port->mux.pio3.abcdsr2); |
| 141 | |
| 142 | writel(mask, &at91_port->pdr); |
| 143 | } |
| 144 | |
| 145 | return 0; |
| 146 | } |
| 147 | |
| 148 | /* |
| 149 | * mux the pin to the "B" internal peripheral role. |
| 150 | */ |
| 151 | int at91_pio3_set_b_periph(unsigned port, unsigned pin, int use_pullup) |
| 152 | { |
| 153 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 154 | u32 mask; |
| 155 | |
| 156 | if (at91_port && (pin < GPIO_PER_BANK)) { |
| 157 | mask = 1 << pin; |
| 158 | writel(mask, &at91_port->idr); |
| 159 | at91_set_pio_pullup(port, pin, use_pullup); |
| 160 | writel(readl(&at91_port->mux.pio3.abcdsr1) | mask, |
| 161 | &at91_port->mux.pio3.abcdsr1); |
| 162 | writel(readl(&at91_port->mux.pio3.abcdsr2) & ~mask, |
| 163 | &at91_port->mux.pio3.abcdsr2); |
| 164 | |
| 165 | writel(mask, &at91_port->pdr); |
| 166 | } |
| 167 | |
| 168 | return 0; |
| 169 | } |
| 170 | /* |
| 171 | * mux the pin to the "C" internal peripheral role. |
| 172 | */ |
| 173 | int at91_pio3_set_c_periph(unsigned port, unsigned pin, int use_pullup) |
| 174 | { |
| 175 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 176 | u32 mask; |
| 177 | |
| 178 | if (at91_port && (pin < GPIO_PER_BANK)) { |
| 179 | mask = 1 << pin; |
| 180 | writel(mask, &at91_port->idr); |
| 181 | at91_set_pio_pullup(port, pin, use_pullup); |
| 182 | writel(readl(&at91_port->mux.pio3.abcdsr1) & ~mask, |
| 183 | &at91_port->mux.pio3.abcdsr1); |
| 184 | writel(readl(&at91_port->mux.pio3.abcdsr2) | mask, |
| 185 | &at91_port->mux.pio3.abcdsr2); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 186 | writel(mask, &at91_port->pdr); |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 187 | } |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 188 | |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 189 | return 0; |
| 190 | } |
| 191 | |
| 192 | /* |
| 193 | * mux the pin to the "D" internal peripheral role. |
| 194 | */ |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 195 | int at91_pio3_set_d_periph(unsigned port, unsigned pin, int use_pullup) |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 196 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 197 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 198 | u32 mask; |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 199 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 200 | if (at91_port && (pin < GPIO_PER_BANK)) { |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 201 | mask = 1 << pin; |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 202 | writel(mask, &at91_port->idr); |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 203 | at91_set_pio_pullup(port, pin, use_pullup); |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 204 | writel(readl(&at91_port->mux.pio3.abcdsr1) | mask, |
| 205 | &at91_port->mux.pio3.abcdsr1); |
| 206 | writel(readl(&at91_port->mux.pio3.abcdsr2) | mask, |
| 207 | &at91_port->mux.pio3.abcdsr2); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 208 | writel(mask, &at91_port->pdr); |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 209 | } |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 210 | |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 211 | return 0; |
| 212 | } |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 213 | |
Simon Glass | bcee8d6 | 2019-12-06 21:41:35 -0700 | [diff] [blame] | 214 | #if CONFIG_IS_ENABLED(DM_GPIO) |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 215 | static bool at91_get_port_output(struct at91_port *at91_port, int offset) |
| 216 | { |
| 217 | u32 mask, val; |
| 218 | |
| 219 | mask = 1 << offset; |
| 220 | val = readl(&at91_port->osr); |
| 221 | return val & mask; |
| 222 | } |
| 223 | #endif |
| 224 | |
| 225 | static void at91_set_port_input(struct at91_port *at91_port, int offset, |
| 226 | int use_pullup) |
| 227 | { |
| 228 | u32 mask; |
| 229 | |
| 230 | mask = 1 << offset; |
| 231 | writel(mask, &at91_port->idr); |
| 232 | at91_set_port_pullup(at91_port, offset, use_pullup); |
| 233 | writel(mask, &at91_port->odr); |
| 234 | writel(mask, &at91_port->per); |
| 235 | } |
| 236 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 237 | /* |
| 238 | * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and |
| 239 | * configure it for an input. |
| 240 | */ |
| 241 | int at91_set_pio_input(unsigned port, u32 pin, int use_pullup) |
| 242 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 243 | struct at91_port *at91_port = at91_pio_get_port(port); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 244 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 245 | if (at91_port && (pin < GPIO_PER_BANK)) |
| 246 | at91_set_port_input(at91_port, pin, use_pullup); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 247 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 248 | return 0; |
| 249 | } |
| 250 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 251 | static void at91_set_port_output(struct at91_port *at91_port, int offset, |
| 252 | int value) |
| 253 | { |
| 254 | u32 mask; |
| 255 | |
| 256 | mask = 1 << offset; |
| 257 | writel(mask, &at91_port->idr); |
| 258 | writel(mask, &at91_port->pudr); |
| 259 | if (value) |
| 260 | writel(mask, &at91_port->sodr); |
| 261 | else |
| 262 | writel(mask, &at91_port->codr); |
| 263 | writel(mask, &at91_port->oer); |
| 264 | writel(mask, &at91_port->per); |
| 265 | } |
| 266 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 267 | /* |
| 268 | * mux the pin to the gpio controller (instead of "A" or "B" peripheral), |
| 269 | * and configure it for an output. |
| 270 | */ |
| 271 | int at91_set_pio_output(unsigned port, u32 pin, int value) |
| 272 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 273 | struct at91_port *at91_port = at91_pio_get_port(port); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 274 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 275 | if (at91_port && (pin < GPIO_PER_BANK)) |
| 276 | at91_set_port_output(at91_port, pin, value); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 277 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 278 | return 0; |
| 279 | } |
| 280 | |
| 281 | /* |
| 282 | * enable/disable the glitch filter. mostly used with IRQ handling. |
| 283 | */ |
| 284 | int at91_set_pio_deglitch(unsigned port, unsigned pin, int is_on) |
| 285 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 286 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 287 | u32 mask; |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 288 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 289 | if (at91_port && (pin < GPIO_PER_BANK)) { |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 290 | mask = 1 << pin; |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 291 | if (is_on) |
| 292 | writel(mask, &at91_port->ifer); |
| 293 | else |
| 294 | writel(mask, &at91_port->ifdr); |
| 295 | } |
| 296 | |
| 297 | return 0; |
| 298 | } |
| 299 | |
| 300 | /* |
| 301 | * enable/disable the glitch filter. mostly used with IRQ handling. |
| 302 | */ |
| 303 | int at91_pio3_set_pio_deglitch(unsigned port, unsigned pin, int is_on) |
| 304 | { |
| 305 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 306 | u32 mask; |
| 307 | |
| 308 | if (at91_port && (pin < GPIO_PER_BANK)) { |
| 309 | mask = 1 << pin; |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 310 | if (is_on) { |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 311 | writel(mask, &at91_port->mux.pio3.ifscdr); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 312 | writel(mask, &at91_port->ifer); |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 313 | } else { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 314 | writel(mask, &at91_port->ifdr); |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 315 | } |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 316 | } |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 317 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 318 | return 0; |
| 319 | } |
| 320 | |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 321 | /* |
| 322 | * enable/disable the debounce filter. |
| 323 | */ |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 324 | int at91_pio3_set_pio_debounce(unsigned port, unsigned pin, int is_on, int div) |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 325 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 326 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 327 | u32 mask; |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 328 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 329 | if (at91_port && (pin < GPIO_PER_BANK)) { |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 330 | mask = 1 << pin; |
| 331 | if (is_on) { |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 332 | writel(mask, &at91_port->mux.pio3.ifscer); |
| 333 | writel(div & PIO_SCDR_DIV, &at91_port->mux.pio3.scdr); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 334 | writel(mask, &at91_port->ifer); |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 335 | } else { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 336 | writel(mask, &at91_port->ifdr); |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 337 | } |
| 338 | } |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 339 | |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 340 | return 0; |
| 341 | } |
| 342 | |
| 343 | /* |
| 344 | * enable/disable the pull-down. |
| 345 | * If pull-up already enabled while calling the function, we disable it. |
| 346 | */ |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 347 | int at91_pio3_set_pio_pulldown(unsigned port, unsigned pin, int is_on) |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 348 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 349 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 350 | u32 mask; |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 351 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 352 | if (at91_port && (pin < GPIO_PER_BANK)) { |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 353 | mask = 1 << pin; |
Marek Vasut | 152ac5f | 2016-05-04 23:05:23 +0200 | [diff] [blame] | 354 | if (is_on) { |
| 355 | at91_set_pio_pullup(port, pin, 0); |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 356 | writel(mask, &at91_port->mux.pio3.ppder); |
Marek Vasut | 152ac5f | 2016-05-04 23:05:23 +0200 | [diff] [blame] | 357 | } else |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 358 | writel(mask, &at91_port->mux.pio3.ppddr); |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 359 | } |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 360 | |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 361 | return 0; |
| 362 | } |
| 363 | |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 364 | int at91_pio3_set_pio_pullup(unsigned port, unsigned pin, int use_pullup) |
| 365 | { |
| 366 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 367 | |
| 368 | if (use_pullup) |
| 369 | at91_pio3_set_pio_pulldown(port, pin, 0); |
| 370 | |
| 371 | if (at91_port && (pin < GPIO_PER_BANK)) |
| 372 | at91_set_port_pullup(at91_port, pin, use_pullup); |
| 373 | |
| 374 | return 0; |
| 375 | } |
| 376 | |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 377 | /* |
| 378 | * disable Schmitt trigger |
| 379 | */ |
Wenyou Yang | 2dc63f7 | 2017-03-23 12:44:36 +0800 | [diff] [blame] | 380 | int at91_pio3_set_pio_disable_schmitt_trig(unsigned port, unsigned pin) |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 381 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 382 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 383 | u32 mask; |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 384 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 385 | if (at91_port && (pin < GPIO_PER_BANK)) { |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 386 | mask = 1 << pin; |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 387 | writel(readl(&at91_port->schmitt) | mask, |
| 388 | &at91_port->schmitt); |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 389 | } |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 390 | |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 391 | return 0; |
| 392 | } |
Bo Shen | 2b3b1c6 | 2012-05-20 15:50:00 +0000 | [diff] [blame] | 393 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 394 | /* |
| 395 | * enable/disable the multi-driver. This is only valid for output and |
| 396 | * allows the output pin to run as an open collector output. |
| 397 | */ |
| 398 | int at91_set_pio_multi_drive(unsigned port, unsigned pin, int is_on) |
| 399 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 400 | struct at91_port *at91_port = at91_pio_get_port(port); |
| 401 | u32 mask; |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 402 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 403 | if (at91_port && (pin < GPIO_PER_BANK)) { |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 404 | mask = 1 << pin; |
| 405 | if (is_on) |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 406 | writel(mask, &at91_port->mder); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 407 | else |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 408 | writel(mask, &at91_port->mddr); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 409 | } |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 410 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 411 | return 0; |
| 412 | } |
| 413 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 414 | static void at91_set_port_value(struct at91_port *at91_port, int offset, |
| 415 | int value) |
| 416 | { |
| 417 | u32 mask; |
| 418 | |
| 419 | mask = 1 << offset; |
| 420 | if (value) |
| 421 | writel(mask, &at91_port->sodr); |
| 422 | else |
| 423 | writel(mask, &at91_port->codr); |
| 424 | } |
| 425 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 426 | /* |
| 427 | * assuming the pin is muxed as a gpio output, set its value. |
| 428 | */ |
| 429 | int at91_set_pio_value(unsigned port, unsigned pin, int value) |
| 430 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 431 | struct at91_port *at91_port = at91_pio_get_port(port); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 432 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 433 | if (at91_port && (pin < GPIO_PER_BANK)) |
| 434 | at91_set_port_value(at91_port, pin, value); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 435 | |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 436 | return 0; |
| 437 | } |
| 438 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 439 | static int at91_get_port_value(struct at91_port *at91_port, int offset) |
| 440 | { |
| 441 | u32 pdsr = 0, mask; |
| 442 | |
| 443 | mask = 1 << offset; |
| 444 | pdsr = readl(&at91_port->pdsr) & mask; |
| 445 | |
| 446 | return pdsr != 0; |
| 447 | } |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 448 | /* |
| 449 | * read the pin's value (works even if it's not muxed as a gpio). |
| 450 | */ |
| 451 | int at91_get_pio_value(unsigned port, unsigned pin) |
| 452 | { |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 453 | struct at91_port *at91_port = at91_pio_get_port(port); |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 454 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 455 | if (at91_port && (pin < GPIO_PER_BANK)) |
| 456 | return at91_get_port_value(at91_port, pin); |
Bo Shen | 4bc9b7a | 2013-08-22 15:24:40 +0800 | [diff] [blame] | 457 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 458 | return 0; |
Jens Scharsig | ea8fbba | 2010-02-03 22:46:16 +0100 | [diff] [blame] | 459 | } |
Bo Shen | 6edaea8 | 2013-08-13 14:38:31 +0800 | [diff] [blame] | 460 | |
Simon Glass | bcee8d6 | 2019-12-06 21:41:35 -0700 | [diff] [blame] | 461 | #if !CONFIG_IS_ENABLED(DM_GPIO) |
Bo Shen | 6edaea8 | 2013-08-13 14:38:31 +0800 | [diff] [blame] | 462 | /* Common GPIO API */ |
| 463 | |
Bo Shen | 6edaea8 | 2013-08-13 14:38:31 +0800 | [diff] [blame] | 464 | int gpio_request(unsigned gpio, const char *label) |
| 465 | { |
| 466 | return 0; |
| 467 | } |
| 468 | |
| 469 | int gpio_free(unsigned gpio) |
| 470 | { |
| 471 | return 0; |
| 472 | } |
| 473 | |
| 474 | int gpio_direction_input(unsigned gpio) |
| 475 | { |
| 476 | at91_set_pio_input(at91_gpio_to_port(gpio), |
| 477 | at91_gpio_to_pin(gpio), 0); |
| 478 | return 0; |
| 479 | } |
| 480 | |
| 481 | int gpio_direction_output(unsigned gpio, int value) |
| 482 | { |
| 483 | at91_set_pio_output(at91_gpio_to_port(gpio), |
| 484 | at91_gpio_to_pin(gpio), value); |
| 485 | return 0; |
| 486 | } |
| 487 | |
| 488 | int gpio_get_value(unsigned gpio) |
| 489 | { |
| 490 | return at91_get_pio_value(at91_gpio_to_port(gpio), |
| 491 | at91_gpio_to_pin(gpio)); |
| 492 | } |
| 493 | |
| 494 | int gpio_set_value(unsigned gpio, int value) |
| 495 | { |
| 496 | at91_set_pio_value(at91_gpio_to_port(gpio), |
| 497 | at91_gpio_to_pin(gpio), value); |
| 498 | |
| 499 | return 0; |
| 500 | } |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 501 | #endif |
| 502 | |
Simon Glass | bcee8d6 | 2019-12-06 21:41:35 -0700 | [diff] [blame] | 503 | #if CONFIG_IS_ENABLED(DM_GPIO) |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 504 | |
| 505 | struct at91_port_priv { |
| 506 | struct at91_port *regs; |
| 507 | }; |
| 508 | |
| 509 | /* set GPIO pin 'gpio' as an input */ |
| 510 | static int at91_gpio_direction_input(struct udevice *dev, unsigned offset) |
| 511 | { |
Axel Lin | d895821 | 2015-01-31 14:47:34 +0800 | [diff] [blame] | 512 | struct at91_port_priv *port = dev_get_priv(dev); |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 513 | |
| 514 | at91_set_port_input(port->regs, offset, 0); |
| 515 | |
| 516 | return 0; |
| 517 | } |
| 518 | |
| 519 | /* set GPIO pin 'gpio' as an output, with polarity 'value' */ |
| 520 | static int at91_gpio_direction_output(struct udevice *dev, unsigned offset, |
| 521 | int value) |
| 522 | { |
Axel Lin | d895821 | 2015-01-31 14:47:34 +0800 | [diff] [blame] | 523 | struct at91_port_priv *port = dev_get_priv(dev); |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 524 | |
| 525 | at91_set_port_output(port->regs, offset, value); |
| 526 | |
| 527 | return 0; |
| 528 | } |
| 529 | |
| 530 | /* read GPIO IN value of pin 'gpio' */ |
| 531 | static int at91_gpio_get_value(struct udevice *dev, unsigned offset) |
| 532 | { |
Axel Lin | d895821 | 2015-01-31 14:47:34 +0800 | [diff] [blame] | 533 | struct at91_port_priv *port = dev_get_priv(dev); |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 534 | |
| 535 | return at91_get_port_value(port->regs, offset); |
| 536 | } |
| 537 | |
| 538 | /* write GPIO OUT value to pin 'gpio' */ |
| 539 | static int at91_gpio_set_value(struct udevice *dev, unsigned offset, |
| 540 | int value) |
| 541 | { |
Axel Lin | d895821 | 2015-01-31 14:47:34 +0800 | [diff] [blame] | 542 | struct at91_port_priv *port = dev_get_priv(dev); |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 543 | |
| 544 | at91_set_port_value(port->regs, offset, value); |
| 545 | |
| 546 | return 0; |
| 547 | } |
| 548 | |
| 549 | static int at91_gpio_get_function(struct udevice *dev, unsigned offset) |
| 550 | { |
Axel Lin | d895821 | 2015-01-31 14:47:34 +0800 | [diff] [blame] | 551 | struct at91_port_priv *port = dev_get_priv(dev); |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 552 | |
| 553 | /* GPIOF_FUNC is not implemented yet */ |
| 554 | if (at91_get_port_output(port->regs, offset)) |
| 555 | return GPIOF_OUTPUT; |
| 556 | else |
| 557 | return GPIOF_INPUT; |
| 558 | } |
| 559 | |
James Byrne | f3510e9 | 2019-11-26 11:52:04 +0000 | [diff] [blame] | 560 | static const char *at91_get_bank_name(uint32_t base_addr) |
| 561 | { |
| 562 | switch (base_addr) { |
| 563 | case ATMEL_BASE_PIOA: |
| 564 | return "PIOA"; |
| 565 | case ATMEL_BASE_PIOB: |
| 566 | return "PIOB"; |
| 567 | case ATMEL_BASE_PIOC: |
| 568 | return "PIOC"; |
| 569 | #if (ATMEL_PIO_PORTS > 3) |
| 570 | case ATMEL_BASE_PIOD: |
| 571 | return "PIOD"; |
| 572 | #if (ATMEL_PIO_PORTS > 4) |
| 573 | case ATMEL_BASE_PIOE: |
| 574 | return "PIOE"; |
| 575 | #endif |
| 576 | #endif |
| 577 | } |
| 578 | |
| 579 | return "undefined"; |
| 580 | } |
| 581 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 582 | static const struct dm_gpio_ops gpio_at91_ops = { |
| 583 | .direction_input = at91_gpio_direction_input, |
| 584 | .direction_output = at91_gpio_direction_output, |
| 585 | .get_value = at91_gpio_get_value, |
| 586 | .set_value = at91_gpio_set_value, |
| 587 | .get_function = at91_gpio_get_function, |
| 588 | }; |
| 589 | |
| 590 | static int at91_gpio_probe(struct udevice *dev) |
| 591 | { |
| 592 | struct at91_port_priv *port = dev_get_priv(dev); |
Simon Glass | 8a8d24b | 2020-12-03 16:55:23 -0700 | [diff] [blame] | 593 | struct at91_port_plat *plat = dev_get_plat(dev); |
Simon Glass | e564f05 | 2015-03-05 12:25:20 -0700 | [diff] [blame] | 594 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); |
Wenyou Yang | f2f3c15 | 2017-03-23 12:46:21 +0800 | [diff] [blame] | 595 | struct clk clk; |
| 596 | int ret; |
| 597 | |
| 598 | ret = clk_get_by_index(dev, 0, &clk); |
| 599 | if (ret) |
| 600 | return ret; |
| 601 | |
| 602 | ret = clk_enable(&clk); |
| 603 | if (ret) |
| 604 | return ret; |
| 605 | |
Wenyou Yang | cf46888 | 2017-03-23 12:46:20 +0800 | [diff] [blame] | 606 | #if CONFIG_IS_ENABLED(OF_CONTROL) |
Masahiro Yamada | 0cbf3e0 | 2020-08-04 14:14:41 +0900 | [diff] [blame] | 607 | plat->base_addr = dev_read_addr(dev); |
Wenyou Yang | cf46888 | 2017-03-23 12:46:20 +0800 | [diff] [blame] | 608 | #endif |
James Byrne | f3510e9 | 2019-11-26 11:52:04 +0000 | [diff] [blame] | 609 | plat->bank_name = at91_get_bank_name(plat->base_addr); |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 610 | port->regs = (struct at91_port *)plat->base_addr; |
| 611 | |
James Byrne | f3510e9 | 2019-11-26 11:52:04 +0000 | [diff] [blame] | 612 | uc_priv->bank_name = plat->bank_name; |
| 613 | uc_priv->gpio_count = GPIO_PER_BANK; |
| 614 | |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 615 | return 0; |
| 616 | } |
| 617 | |
Wenyou Yang | cf46888 | 2017-03-23 12:46:20 +0800 | [diff] [blame] | 618 | #if CONFIG_IS_ENABLED(OF_CONTROL) |
| 619 | static const struct udevice_id at91_gpio_ids[] = { |
| 620 | { .compatible = "atmel,at91rm9200-gpio" }, |
| 621 | { } |
| 622 | }; |
| 623 | #endif |
| 624 | |
Walter Lozano | e3e2470 | 2020-06-25 01:10:04 -0300 | [diff] [blame] | 625 | U_BOOT_DRIVER(atmel_at91rm9200_gpio) = { |
| 626 | .name = "atmel_at91rm9200_gpio", |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 627 | .id = UCLASS_GPIO, |
Wenyou Yang | cf46888 | 2017-03-23 12:46:20 +0800 | [diff] [blame] | 628 | #if CONFIG_IS_ENABLED(OF_CONTROL) |
| 629 | .of_match = at91_gpio_ids, |
Simon Glass | 8a8d24b | 2020-12-03 16:55:23 -0700 | [diff] [blame] | 630 | .plat_auto = sizeof(struct at91_port_plat), |
Wenyou Yang | cf46888 | 2017-03-23 12:46:20 +0800 | [diff] [blame] | 631 | #endif |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 632 | .ops = &gpio_at91_ops, |
| 633 | .probe = at91_gpio_probe, |
Simon Glass | 41575d8 | 2020-12-03 16:55:17 -0700 | [diff] [blame] | 634 | .priv_auto = sizeof(struct at91_port_priv), |
Simon Glass | 918354b | 2014-10-29 13:08:57 -0600 | [diff] [blame] | 635 | }; |
| 636 | #endif |