1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Support for Compaq iPAQ H3100 and H3600 handheld computers (common code) |
4 | * |
5 | * Copyright (c) 2000,1 Compaq Computer Corporation. (Author: Jamey Hicks) |
6 | * Copyright (c) 2009 Dmitry Artamonow <mad_soft@inbox.ru> |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/gpio/machine.h> |
11 | #include <linux/gpio.h> |
12 | #include <linux/gpio_keys.h> |
13 | #include <linux/input.h> |
14 | #include <linux/mtd/mtd.h> |
15 | #include <linux/mtd/partitions.h> |
16 | #include <linux/platform_data/gpio-htc-egpio.h> |
17 | #include <linux/platform_data/sa11x0-serial.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/serial_core.h> |
20 | |
21 | #include <asm/mach/flash.h> |
22 | #include <asm/mach/map.h> |
23 | |
24 | #include <mach/h3xxx.h> |
25 | #include <mach/irqs.h> |
26 | |
27 | #include "generic.h" |
28 | |
29 | /* |
30 | * H3xxx flash support |
31 | */ |
32 | static struct mtd_partition h3xxx_partitions[] = { |
33 | { |
34 | .name = "H3XXX boot firmware" , |
35 | .size = 0x00040000, |
36 | .offset = 0, |
37 | .mask_flags = MTD_WRITEABLE, /* force read-only */ |
38 | }, { |
39 | .name = "H3XXX rootfs" , |
40 | .size = MTDPART_SIZ_FULL, |
41 | .offset = 0x00040000, |
42 | } |
43 | }; |
44 | |
45 | static void h3xxx_set_vpp(int vpp) |
46 | { |
47 | gpio_set_value(gpio: H3XXX_EGPIO_VPP_ON, value: vpp); |
48 | } |
49 | |
50 | static int h3xxx_flash_init(void) |
51 | { |
52 | int err = gpio_request(gpio: H3XXX_EGPIO_VPP_ON, label: "Flash Vpp" ); |
53 | if (err) { |
54 | pr_err("%s: can't request H3XXX_EGPIO_VPP_ON\n" , __func__); |
55 | return err; |
56 | } |
57 | |
58 | err = gpio_direction_output(gpio: H3XXX_EGPIO_VPP_ON, value: 0); |
59 | if (err) |
60 | gpio_free(gpio: H3XXX_EGPIO_VPP_ON); |
61 | |
62 | return err; |
63 | } |
64 | |
65 | static void h3xxx_flash_exit(void) |
66 | { |
67 | gpio_free(gpio: H3XXX_EGPIO_VPP_ON); |
68 | } |
69 | |
70 | static struct flash_platform_data h3xxx_flash_data = { |
71 | .map_name = "cfi_probe" , |
72 | .set_vpp = h3xxx_set_vpp, |
73 | .init = h3xxx_flash_init, |
74 | .exit = h3xxx_flash_exit, |
75 | .parts = h3xxx_partitions, |
76 | .nr_parts = ARRAY_SIZE(h3xxx_partitions), |
77 | }; |
78 | |
79 | static struct resource h3xxx_flash_resource = |
80 | DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M); |
81 | |
82 | |
83 | /* |
84 | * H3xxx uart support |
85 | */ |
86 | static void h3xxx_uart_pm(struct uart_port *port, u_int state, u_int oldstate) |
87 | { |
88 | if (port->mapbase == _Ser3UTCR0) { |
89 | if (!gpio_request(gpio: H3XXX_EGPIO_RS232_ON, label: "RS232 transceiver" )) { |
90 | gpio_direction_output(gpio: H3XXX_EGPIO_RS232_ON, value: !state); |
91 | gpio_free(gpio: H3XXX_EGPIO_RS232_ON); |
92 | } else { |
93 | pr_err("%s: can't request H3XXX_EGPIO_RS232_ON\n" , |
94 | __func__); |
95 | } |
96 | } |
97 | } |
98 | |
99 | /* |
100 | * Enable/Disable wake up events for this serial port. |
101 | * Obviously, we only support this on the normal COM port. |
102 | */ |
103 | static int h3xxx_uart_set_wake(struct uart_port *port, u_int enable) |
104 | { |
105 | int err = -EINVAL; |
106 | |
107 | if (port->mapbase == _Ser3UTCR0) { |
108 | if (enable) |
109 | PWER |= PWER_GPIO23 | PWER_GPIO25; /* DCD and CTS */ |
110 | else |
111 | PWER &= ~(PWER_GPIO23 | PWER_GPIO25); /* DCD and CTS */ |
112 | err = 0; |
113 | } |
114 | return err; |
115 | } |
116 | |
117 | static struct sa1100_port_fns h3xxx_port_fns __initdata = { |
118 | .pm = h3xxx_uart_pm, |
119 | .set_wake = h3xxx_uart_set_wake, |
120 | }; |
121 | |
122 | static struct gpiod_lookup_table h3xxx_uart3_gpio_table = { |
123 | .dev_id = "sa11x0-uart.3" , |
124 | .table = { |
125 | GPIO_LOOKUP("gpio" , H3XXX_GPIO_COM_DCD, "dcd" , GPIO_ACTIVE_LOW), |
126 | GPIO_LOOKUP("gpio" , H3XXX_GPIO_COM_CTS, "cts" , GPIO_ACTIVE_LOW), |
127 | GPIO_LOOKUP("gpio" , H3XXX_GPIO_COM_RTS, "rts" , GPIO_ACTIVE_LOW), |
128 | { }, |
129 | }, |
130 | }; |
131 | |
132 | /* |
133 | * EGPIO |
134 | */ |
135 | |
136 | static struct resource egpio_resources[] = { |
137 | [0] = DEFINE_RES_MEM(H3600_EGPIO_PHYS, 0x4), |
138 | }; |
139 | |
140 | static struct htc_egpio_chip egpio_chips[] = { |
141 | [0] = { |
142 | .reg_start = 0, |
143 | .gpio_base = H3XXX_EGPIO_BASE, |
144 | .num_gpios = 16, |
145 | .direction = HTC_EGPIO_OUTPUT, |
146 | .initial_values = 0x0080, /* H3XXX_EGPIO_RS232_ON */ |
147 | }, |
148 | }; |
149 | |
150 | static struct htc_egpio_platform_data egpio_info = { |
151 | .reg_width = 16, |
152 | .bus_width = 16, |
153 | .chip = egpio_chips, |
154 | .num_chips = ARRAY_SIZE(egpio_chips), |
155 | }; |
156 | |
157 | static struct platform_device h3xxx_egpio = { |
158 | .name = "htc-egpio" , |
159 | .id = -1, |
160 | .resource = egpio_resources, |
161 | .num_resources = ARRAY_SIZE(egpio_resources), |
162 | .dev = { |
163 | .platform_data = &egpio_info, |
164 | }, |
165 | }; |
166 | |
167 | /* |
168 | * GPIO keys |
169 | */ |
170 | |
171 | static struct gpio_keys_button h3xxx_button_table[] = { |
172 | { |
173 | .code = KEY_POWER, |
174 | .gpio = H3XXX_GPIO_PWR_BUTTON, |
175 | .desc = "Power Button" , |
176 | .active_low = 1, |
177 | .type = EV_KEY, |
178 | .wakeup = 1, |
179 | }, { |
180 | .code = KEY_ENTER, |
181 | .gpio = H3XXX_GPIO_ACTION_BUTTON, |
182 | .active_low = 1, |
183 | .desc = "Action button" , |
184 | .type = EV_KEY, |
185 | .wakeup = 0, |
186 | }, |
187 | }; |
188 | |
189 | static struct gpio_keys_platform_data h3xxx_keys_data = { |
190 | .buttons = h3xxx_button_table, |
191 | .nbuttons = ARRAY_SIZE(h3xxx_button_table), |
192 | }; |
193 | |
194 | static struct platform_device h3xxx_keys = { |
195 | .name = "gpio-keys" , |
196 | .id = -1, |
197 | .dev = { |
198 | .platform_data = &h3xxx_keys_data, |
199 | }, |
200 | }; |
201 | |
202 | static struct resource h3xxx_micro_resources[] = { |
203 | DEFINE_RES_MEM(0x80010000, SZ_4K), |
204 | DEFINE_RES_MEM(0x80020000, SZ_4K), |
205 | DEFINE_RES_IRQ(IRQ_Ser1UART), |
206 | }; |
207 | |
208 | struct platform_device h3xxx_micro_asic = { |
209 | .name = "ipaq-h3xxx-micro" , |
210 | .id = -1, |
211 | .resource = h3xxx_micro_resources, |
212 | .num_resources = ARRAY_SIZE(h3xxx_micro_resources), |
213 | }; |
214 | |
215 | static struct platform_device *h3xxx_devices[] = { |
216 | &h3xxx_egpio, |
217 | &h3xxx_keys, |
218 | &h3xxx_micro_asic, |
219 | }; |
220 | |
221 | static struct gpiod_lookup_table h3xxx_pcmcia_gpio_table = { |
222 | .dev_id = "sa11x0-pcmcia" , |
223 | .table = { |
224 | GPIO_LOOKUP("gpio" , H3XXX_GPIO_PCMCIA_CD0, |
225 | "pcmcia0-detect" , GPIO_ACTIVE_LOW), |
226 | GPIO_LOOKUP("gpio" , H3XXX_GPIO_PCMCIA_IRQ0, |
227 | "pcmcia0-ready" , GPIO_ACTIVE_HIGH), |
228 | GPIO_LOOKUP("gpio" , H3XXX_GPIO_PCMCIA_CD1, |
229 | "pcmcia1-detect" , GPIO_ACTIVE_LOW), |
230 | GPIO_LOOKUP("gpio" , H3XXX_GPIO_PCMCIA_IRQ1, |
231 | "pcmcia1-ready" , GPIO_ACTIVE_HIGH), |
232 | { }, |
233 | }, |
234 | }; |
235 | |
236 | void __init h3xxx_mach_init(void) |
237 | { |
238 | gpiod_add_lookup_table(table: &h3xxx_pcmcia_gpio_table); |
239 | gpiod_add_lookup_table(table: &h3xxx_uart3_gpio_table); |
240 | sa1100_register_uart_fns(fns: &h3xxx_port_fns); |
241 | sa11x0_register_mtd(flash: &h3xxx_flash_data, res: &h3xxx_flash_resource, nr: 1); |
242 | platform_add_devices(h3xxx_devices, ARRAY_SIZE(h3xxx_devices)); |
243 | } |
244 | |
245 | static struct map_desc h3600_io_desc[] __initdata = { |
246 | { /* static memory bank 2 CS#2 */ |
247 | .virtual = H3600_BANK_2_VIRT, |
248 | .pfn = __phys_to_pfn(SA1100_CS2_PHYS), |
249 | .length = 0x02800000, |
250 | .type = MT_DEVICE |
251 | }, { /* static memory bank 4 CS#4 */ |
252 | .virtual = H3600_BANK_4_VIRT, |
253 | .pfn = __phys_to_pfn(SA1100_CS4_PHYS), |
254 | .length = 0x00800000, |
255 | .type = MT_DEVICE |
256 | }, { /* EGPIO 0 CS#5 */ |
257 | .virtual = H3600_EGPIO_VIRT, |
258 | .pfn = __phys_to_pfn(H3600_EGPIO_PHYS), |
259 | .length = 0x01000000, |
260 | .type = MT_DEVICE |
261 | } |
262 | }; |
263 | |
264 | /* |
265 | * Common map_io initialization |
266 | */ |
267 | |
268 | void __init h3xxx_map_io(void) |
269 | { |
270 | sa1100_map_io(); |
271 | iotable_init(h3600_io_desc, ARRAY_SIZE(h3600_io_desc)); |
272 | |
273 | sa1100_register_uart(idx: 0, port: 3); /* Common serial port */ |
274 | // sa1100_register_uart(1, 1); /* Microcontroller on 3100/3600 */ |
275 | |
276 | /* Ensure those pins are outputs and driving low */ |
277 | PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; |
278 | PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); |
279 | |
280 | /* Configure suspend conditions */ |
281 | PGSR = 0; |
282 | PCFR = PCFR_OPDE; |
283 | PSDR = 0; |
284 | |
285 | GPCR = 0x0fffffff; /* All outputs are set low by default */ |
286 | GPDR = 0; /* Configure all GPIOs as input */ |
287 | } |
288 | |
289 | |