diff options
Diffstat (limited to 'arch/mips/jz4740')
-rw-r--r-- | arch/mips/jz4740/Kconfig | 12 | ||||
-rw-r--r-- | arch/mips/jz4740/Makefile | 20 | ||||
-rw-r--r-- | arch/mips/jz4740/Platform | 3 | ||||
-rw-r--r-- | arch/mips/jz4740/board-qi_lb60.c | 471 | ||||
-rw-r--r-- | arch/mips/jz4740/clock-debugfs.c | 109 | ||||
-rw-r--r-- | arch/mips/jz4740/clock.c | 924 | ||||
-rw-r--r-- | arch/mips/jz4740/clock.h | 76 | ||||
-rw-r--r-- | arch/mips/jz4740/dma.c | 289 | ||||
-rw-r--r-- | arch/mips/jz4740/gpio.c | 604 | ||||
-rw-r--r-- | arch/mips/jz4740/irq.c | 167 | ||||
-rw-r--r-- | arch/mips/jz4740/irq.h | 21 | ||||
-rw-r--r-- | arch/mips/jz4740/platform.c | 291 | ||||
-rw-r--r-- | arch/mips/jz4740/pm.c | 56 | ||||
-rw-r--r-- | arch/mips/jz4740/prom.c | 68 | ||||
-rw-r--r-- | arch/mips/jz4740/pwm.c | 177 | ||||
-rw-r--r-- | arch/mips/jz4740/reset.c | 79 | ||||
-rw-r--r-- | arch/mips/jz4740/reset.h | 6 | ||||
-rw-r--r-- | arch/mips/jz4740/serial.c | 33 | ||||
-rw-r--r-- | arch/mips/jz4740/serial.h | 20 | ||||
-rw-r--r-- | arch/mips/jz4740/setup.c | 29 | ||||
-rw-r--r-- | arch/mips/jz4740/time.c | 144 | ||||
-rw-r--r-- | arch/mips/jz4740/timer.c | 48 | ||||
-rw-r--r-- | arch/mips/jz4740/timer.h | 136 |
23 files changed, 3783 insertions, 0 deletions
diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig new file mode 100644 index 000000000000..3e7141f0746c --- /dev/null +++ b/arch/mips/jz4740/Kconfig | |||
@@ -0,0 +1,12 @@ | |||
1 | choice | ||
2 | prompt "Machine type" | ||
3 | depends on MACH_JZ4740 | ||
4 | default JZ4740_QI_LB60 | ||
5 | |||
6 | config JZ4740_QI_LB60 | ||
7 | bool "Qi Hardware Ben NanoNote" | ||
8 | |||
9 | endchoice | ||
10 | |||
11 | config HAVE_PWM | ||
12 | bool | ||
diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile new file mode 100644 index 000000000000..a604eaeb6c08 --- /dev/null +++ b/arch/mips/jz4740/Makefile | |||
@@ -0,0 +1,20 @@ | |||
1 | # | ||
2 | # Makefile for the Ingenic JZ4740. | ||
3 | # | ||
4 | |||
5 | # Object file lists. | ||
6 | |||
7 | obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ | ||
8 | gpio.o clock.o platform.o timer.o pwm.o serial.o | ||
9 | |||
10 | obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o | ||
11 | |||
12 | # board specific support | ||
13 | |||
14 | obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o | ||
15 | |||
16 | # PM support | ||
17 | |||
18 | obj-$(CONFIG_PM) += pm.o | ||
19 | |||
20 | EXTRA_CFLAGS += -Werror -Wall | ||
diff --git a/arch/mips/jz4740/Platform b/arch/mips/jz4740/Platform new file mode 100644 index 000000000000..6a97230e3d05 --- /dev/null +++ b/arch/mips/jz4740/Platform | |||
@@ -0,0 +1,3 @@ | |||
1 | core-$(CONFIG_MACH_JZ4740) += arch/mips/jz4740/ | ||
2 | cflags-$(CONFIG_MACH_JZ4740) += -I$(srctree)/arch/mips/include/asm/mach-jz4740 | ||
3 | load-$(CONFIG_MACH_JZ4740) += 0xffffffff80010000 | ||
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c new file mode 100644 index 000000000000..5742bb4d78f4 --- /dev/null +++ b/arch/mips/jz4740/board-qi_lb60.c | |||
@@ -0,0 +1,471 @@ | |||
1 | /* | ||
2 | * linux/arch/mips/jz4740/board-qi_lb60.c | ||
3 | * | ||
4 | * QI_LB60 board support | ||
5 | * | ||
6 | * Copyright (c) 2009 Qi Hardware inc., | ||
7 | * Author: Xiangfu Liu <xiangfu@qi-hardware.com> | ||
8 | * Copyright 2010, Lars-Petrer Clausen <lars@metafoo.de> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 or later | ||
12 | * as published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/gpio.h> | ||
18 | |||
19 | #include <linux/input.h> | ||
20 | #include <linux/gpio_keys.h> | ||
21 | #include <linux/input/matrix_keypad.h> | ||
22 | #include <linux/spi/spi.h> | ||
23 | #include <linux/spi/spi_gpio.h> | ||
24 | #include <linux/power_supply.h> | ||
25 | #include <linux/power/jz4740-battery.h> | ||
26 | |||
27 | #include <asm/mach-jz4740/jz4740_fb.h> | ||
28 | #include <asm/mach-jz4740/jz4740_mmc.h> | ||
29 | #include <asm/mach-jz4740/jz4740_nand.h> | ||
30 | |||
31 | #include <linux/regulator/fixed.h> | ||
32 | #include <linux/regulator/machine.h> | ||
33 | |||
34 | #include <linux/leds_pwm.h> | ||
35 | |||
36 | #include <asm/mach-jz4740/platform.h> | ||
37 | |||
38 | #include "clock.h" | ||
39 | |||
40 | static bool is_avt2; | ||
41 | |||
42 | /* GPIOs */ | ||
43 | #define QI_LB60_GPIO_SD_CD JZ_GPIO_PORTD(0) | ||
44 | #define QI_LB60_GPIO_SD_VCC_EN_N JZ_GPIO_PORTD(2) | ||
45 | |||
46 | #define QI_LB60_GPIO_KEYOUT(x) (JZ_GPIO_PORTC(10) + (x)) | ||
47 | #define QI_LB60_GPIO_KEYIN(x) (JZ_GPIO_PORTD(18) + (x)) | ||
48 | #define QI_LB60_GPIO_KEYIN8 JZ_GPIO_PORTD(26) | ||
49 | |||
50 | /* NAND */ | ||
51 | static struct nand_ecclayout qi_lb60_ecclayout_1gb = { | ||
52 | /* .eccbytes = 36, | ||
53 | .eccpos = { | ||
54 | 6, 7, 8, 9, 10, 11, 12, 13, | ||
55 | 14, 15, 16, 17, 18, 19, 20, 21, | ||
56 | 22, 23, 24, 25, 26, 27, 28, 29, | ||
57 | 30, 31, 32, 33, 34, 35, 36, 37, | ||
58 | 38, 39, 40, 41 | ||
59 | },*/ | ||
60 | .oobfree = { | ||
61 | { .offset = 2, .length = 4 }, | ||
62 | { .offset = 42, .length = 22 } | ||
63 | }, | ||
64 | }; | ||
65 | |||
66 | /* Early prototypes of the QI LB60 had only 1GB of NAND. | ||
67 | * In order to support these devices aswell the partition and ecc layout is | ||
68 | * initalized depending on the NAND size */ | ||
69 | static struct mtd_partition qi_lb60_partitions_1gb[] = { | ||
70 | { | ||
71 | .name = "NAND BOOT partition", | ||
72 | .offset = 0 * 0x100000, | ||
73 | .size = 4 * 0x100000, | ||
74 | }, | ||
75 | { | ||
76 | .name = "NAND KERNEL partition", | ||
77 | .offset = 4 * 0x100000, | ||
78 | .size = 4 * 0x100000, | ||
79 | }, | ||
80 | { | ||
81 | .name = "NAND ROOTFS partition", | ||
82 | .offset = 8 * 0x100000, | ||
83 | .size = (504 + 512) * 0x100000, | ||
84 | }, | ||
85 | }; | ||
86 | |||
87 | static struct nand_ecclayout qi_lb60_ecclayout_2gb = { | ||
88 | /* .eccbytes = 72, | ||
89 | .eccpos = { | ||
90 | 12, 13, 14, 15, 16, 17, 18, 19, | ||
91 | 20, 21, 22, 23, 24, 25, 26, 27, | ||
92 | 28, 29, 30, 31, 32, 33, 34, 35, | ||
93 | 36, 37, 38, 39, 40, 41, 42, 43, | ||
94 | 44, 45, 46, 47, 48, 49, 50, 51, | ||
95 | 52, 53, 54, 55, 56, 57, 58, 59, | ||
96 | 60, 61, 62, 63, 64, 65, 66, 67, | ||
97 | 68, 69, 70, 71, 72, 73, 74, 75, | ||
98 | 76, 77, 78, 79, 80, 81, 82, 83 | ||
99 | },*/ | ||
100 | .oobfree = { | ||
101 | { .offset = 2, .length = 10 }, | ||
102 | { .offset = 84, .length = 44 }, | ||
103 | }, | ||
104 | }; | ||
105 | |||
106 | static struct mtd_partition qi_lb60_partitions_2gb[] = { | ||
107 | { | ||
108 | .name = "NAND BOOT partition", | ||
109 | .offset = 0 * 0x100000, | ||
110 | .size = 4 * 0x100000, | ||
111 | }, | ||
112 | { | ||
113 | .name = "NAND KERNEL partition", | ||
114 | .offset = 4 * 0x100000, | ||
115 | .size = 4 * 0x100000, | ||
116 | }, | ||
117 | { | ||
118 | .name = "NAND ROOTFS partition", | ||
119 | .offset = 8 * 0x100000, | ||
120 | .size = (504 + 512 + 1024) * 0x100000, | ||
121 | }, | ||
122 | }; | ||
123 | |||
124 | static void qi_lb60_nand_ident(struct platform_device *pdev, | ||
125 | struct nand_chip *chip, struct mtd_partition **partitions, | ||
126 | int *num_partitions) | ||
127 | { | ||
128 | if (chip->page_shift == 12) { | ||
129 | chip->ecc.layout = &qi_lb60_ecclayout_2gb; | ||
130 | *partitions = qi_lb60_partitions_2gb; | ||
131 | *num_partitions = ARRAY_SIZE(qi_lb60_partitions_2gb); | ||
132 | } else { | ||
133 | chip->ecc.layout = &qi_lb60_ecclayout_1gb; | ||
134 | *partitions = qi_lb60_partitions_1gb; | ||
135 | *num_partitions = ARRAY_SIZE(qi_lb60_partitions_1gb); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | static struct jz_nand_platform_data qi_lb60_nand_pdata = { | ||
140 | .ident_callback = qi_lb60_nand_ident, | ||
141 | .busy_gpio = 94, | ||
142 | }; | ||
143 | |||
144 | /* Keyboard*/ | ||
145 | |||
146 | #define KEY_QI_QI KEY_F13 | ||
147 | #define KEY_QI_UPRED KEY_RIGHTALT | ||
148 | #define KEY_QI_VOLUP KEY_VOLUMEUP | ||
149 | #define KEY_QI_VOLDOWN KEY_VOLUMEDOWN | ||
150 | #define KEY_QI_FN KEY_LEFTCTRL | ||
151 | |||
152 | static const uint32_t qi_lb60_keymap[] = { | ||
153 | KEY(0, 0, KEY_F1), /* S2 */ | ||
154 | KEY(0, 1, KEY_F2), /* S3 */ | ||
155 | KEY(0, 2, KEY_F3), /* S4 */ | ||
156 | KEY(0, 3, KEY_F4), /* S5 */ | ||
157 | KEY(0, 4, KEY_F5), /* S6 */ | ||
158 | KEY(0, 5, KEY_F6), /* S7 */ | ||
159 | KEY(0, 6, KEY_F7), /* S8 */ | ||
160 | |||
161 | KEY(1, 0, KEY_Q), /* S10 */ | ||
162 | KEY(1, 1, KEY_W), /* S11 */ | ||
163 | KEY(1, 2, KEY_E), /* S12 */ | ||
164 | KEY(1, 3, KEY_R), /* S13 */ | ||
165 | KEY(1, 4, KEY_T), /* S14 */ | ||
166 | KEY(1, 5, KEY_Y), /* S15 */ | ||
167 | KEY(1, 6, KEY_U), /* S16 */ | ||
168 | KEY(1, 7, KEY_I), /* S17 */ | ||
169 | KEY(2, 0, KEY_A), /* S18 */ | ||
170 | KEY(2, 1, KEY_S), /* S19 */ | ||
171 | KEY(2, 2, KEY_D), /* S20 */ | ||
172 | KEY(2, 3, KEY_F), /* S21 */ | ||
173 | KEY(2, 4, KEY_G), /* S22 */ | ||
174 | KEY(2, 5, KEY_H), /* S23 */ | ||
175 | KEY(2, 6, KEY_J), /* S24 */ | ||
176 | KEY(2, 7, KEY_K), /* S25 */ | ||
177 | KEY(3, 0, KEY_ESC), /* S26 */ | ||
178 | KEY(3, 1, KEY_Z), /* S27 */ | ||
179 | KEY(3, 2, KEY_X), /* S28 */ | ||
180 | KEY(3, 3, KEY_C), /* S29 */ | ||
181 | KEY(3, 4, KEY_V), /* S30 */ | ||
182 | KEY(3, 5, KEY_B), /* S31 */ | ||
183 | KEY(3, 6, KEY_N), /* S32 */ | ||
184 | KEY(3, 7, KEY_M), /* S33 */ | ||
185 | KEY(4, 0, KEY_TAB), /* S34 */ | ||
186 | KEY(4, 1, KEY_CAPSLOCK), /* S35 */ | ||
187 | KEY(4, 2, KEY_BACKSLASH), /* S36 */ | ||
188 | KEY(4, 3, KEY_APOSTROPHE), /* S37 */ | ||
189 | KEY(4, 4, KEY_COMMA), /* S38 */ | ||
190 | KEY(4, 5, KEY_DOT), /* S39 */ | ||
191 | KEY(4, 6, KEY_SLASH), /* S40 */ | ||
192 | KEY(4, 7, KEY_UP), /* S41 */ | ||
193 | KEY(5, 0, KEY_O), /* S42 */ | ||
194 | KEY(5, 1, KEY_L), /* S43 */ | ||
195 | KEY(5, 2, KEY_EQUAL), /* S44 */ | ||
196 | KEY(5, 3, KEY_QI_UPRED), /* S45 */ | ||
197 | KEY(5, 4, KEY_SPACE), /* S46 */ | ||
198 | KEY(5, 5, KEY_QI_QI), /* S47 */ | ||
199 | KEY(5, 6, KEY_RIGHTCTRL), /* S48 */ | ||
200 | KEY(5, 7, KEY_LEFT), /* S49 */ | ||
201 | KEY(6, 0, KEY_F8), /* S50 */ | ||
202 | KEY(6, 1, KEY_P), /* S51 */ | ||
203 | KEY(6, 2, KEY_BACKSPACE),/* S52 */ | ||
204 | KEY(6, 3, KEY_ENTER), /* S53 */ | ||
205 | KEY(6, 4, KEY_QI_VOLUP), /* S54 */ | ||
206 | KEY(6, 5, KEY_QI_VOLDOWN), /* S55 */ | ||
207 | KEY(6, 6, KEY_DOWN), /* S56 */ | ||
208 | KEY(6, 7, KEY_RIGHT), /* S57 */ | ||
209 | |||
210 | KEY(7, 0, KEY_LEFTSHIFT), /* S58 */ | ||
211 | KEY(7, 1, KEY_LEFTALT), /* S59 */ | ||
212 | KEY(7, 2, KEY_QI_FN), /* S60 */ | ||
213 | }; | ||
214 | |||
215 | static const struct matrix_keymap_data qi_lb60_keymap_data = { | ||
216 | .keymap = qi_lb60_keymap, | ||
217 | .keymap_size = ARRAY_SIZE(qi_lb60_keymap), | ||
218 | }; | ||
219 | |||
220 | static const unsigned int qi_lb60_keypad_cols[] = { | ||
221 | QI_LB60_GPIO_KEYOUT(0), | ||
222 | QI_LB60_GPIO_KEYOUT(1), | ||
223 | QI_LB60_GPIO_KEYOUT(2), | ||
224 | QI_LB60_GPIO_KEYOUT(3), | ||
225 | QI_LB60_GPIO_KEYOUT(4), | ||
226 | QI_LB60_GPIO_KEYOUT(5), | ||
227 | QI_LB60_GPIO_KEYOUT(6), | ||
228 | QI_LB60_GPIO_KEYOUT(7), | ||
229 | }; | ||
230 | |||
231 | static const unsigned int qi_lb60_keypad_rows[] = { | ||
232 | QI_LB60_GPIO_KEYIN(0), | ||
233 | QI_LB60_GPIO_KEYIN(1), | ||
234 | QI_LB60_GPIO_KEYIN(2), | ||
235 | QI_LB60_GPIO_KEYIN(3), | ||
236 | QI_LB60_GPIO_KEYIN(4), | ||
237 | QI_LB60_GPIO_KEYIN(5), | ||
238 | QI_LB60_GPIO_KEYIN(7), | ||
239 | QI_LB60_GPIO_KEYIN8, | ||
240 | }; | ||
241 | |||
242 | static struct matrix_keypad_platform_data qi_lb60_pdata = { | ||
243 | .keymap_data = &qi_lb60_keymap_data, | ||
244 | .col_gpios = qi_lb60_keypad_cols, | ||
245 | .row_gpios = qi_lb60_keypad_rows, | ||
246 | .num_col_gpios = ARRAY_SIZE(qi_lb60_keypad_cols), | ||
247 | .num_row_gpios = ARRAY_SIZE(qi_lb60_keypad_rows), | ||
248 | .col_scan_delay_us = 10, | ||
249 | .debounce_ms = 10, | ||
250 | .wakeup = 1, | ||
251 | .active_low = 1, | ||
252 | }; | ||
253 | |||
254 | static struct platform_device qi_lb60_keypad = { | ||
255 | .name = "matrix-keypad", | ||
256 | .id = -1, | ||
257 | .dev = { | ||
258 | .platform_data = &qi_lb60_pdata, | ||
259 | }, | ||
260 | }; | ||
261 | |||
262 | /* Display */ | ||
263 | static struct fb_videomode qi_lb60_video_modes[] = { | ||
264 | { | ||
265 | .name = "320x240", | ||
266 | .xres = 320, | ||
267 | .yres = 240, | ||
268 | .refresh = 30, | ||
269 | .left_margin = 140, | ||
270 | .right_margin = 273, | ||
271 | .upper_margin = 20, | ||
272 | .lower_margin = 2, | ||
273 | .hsync_len = 1, | ||
274 | .vsync_len = 1, | ||
275 | .sync = 0, | ||
276 | .vmode = FB_VMODE_NONINTERLACED, | ||
277 | }, | ||
278 | }; | ||
279 | |||
280 | static struct jz4740_fb_platform_data qi_lb60_fb_pdata = { | ||
281 | .width = 60, | ||
282 | .height = 45, | ||
283 | .num_modes = ARRAY_SIZE(qi_lb60_video_modes), | ||
284 | .modes = qi_lb60_video_modes, | ||
285 | .bpp = 24, | ||
286 | .lcd_type = JZ_LCD_TYPE_8BIT_SERIAL, | ||
287 | .pixclk_falling_edge = 1, | ||
288 | }; | ||
289 | |||
290 | struct spi_gpio_platform_data spigpio_platform_data = { | ||
291 | .sck = JZ_GPIO_PORTC(23), | ||
292 | .mosi = JZ_GPIO_PORTC(22), | ||
293 | .miso = -1, | ||
294 | .num_chipselect = 1, | ||
295 | }; | ||
296 | |||
297 | static struct platform_device spigpio_device = { | ||
298 | .name = "spi_gpio", | ||
299 | .id = 1, | ||
300 | .dev = { | ||
301 | .platform_data = &spigpio_platform_data, | ||
302 | }, | ||
303 | }; | ||
304 | |||
305 | static struct spi_board_info qi_lb60_spi_board_info[] = { | ||
306 | { | ||
307 | .modalias = "ili8960", | ||
308 | .controller_data = (void *)JZ_GPIO_PORTC(21), | ||
309 | .chip_select = 0, | ||
310 | .bus_num = 1, | ||
311 | .max_speed_hz = 30 * 1000, | ||
312 | .mode = SPI_3WIRE, | ||
313 | }, | ||
314 | }; | ||
315 | |||
316 | /* Battery */ | ||
317 | static struct jz_battery_platform_data qi_lb60_battery_pdata = { | ||
318 | .gpio_charge = JZ_GPIO_PORTC(27), | ||
319 | .gpio_charge_active_low = 1, | ||
320 | .info = { | ||
321 | .name = "battery", | ||
322 | .technology = POWER_SUPPLY_TECHNOLOGY_LIPO, | ||
323 | .voltage_max_design = 4200000, | ||
324 | .voltage_min_design = 3600000, | ||
325 | }, | ||
326 | }; | ||
327 | |||
328 | /* GPIO Key: power */ | ||
329 | static struct gpio_keys_button qi_lb60_gpio_keys_buttons[] = { | ||
330 | [0] = { | ||
331 | .code = KEY_POWER, | ||
332 | .gpio = JZ_GPIO_PORTD(29), | ||
333 | .active_low = 1, | ||
334 | .desc = "Power", | ||
335 | .wakeup = 1, | ||
336 | }, | ||
337 | }; | ||
338 | |||
339 | static struct gpio_keys_platform_data qi_lb60_gpio_keys_data = { | ||
340 | .nbuttons = ARRAY_SIZE(qi_lb60_gpio_keys_buttons), | ||
341 | .buttons = qi_lb60_gpio_keys_buttons, | ||
342 | }; | ||
343 | |||
344 | static struct platform_device qi_lb60_gpio_keys = { | ||
345 | .name = "gpio-keys", | ||
346 | .id = -1, | ||
347 | .dev = { | ||
348 | .platform_data = &qi_lb60_gpio_keys_data, | ||
349 | } | ||
350 | }; | ||
351 | |||
352 | static struct jz4740_mmc_platform_data qi_lb60_mmc_pdata = { | ||
353 | .gpio_card_detect = QI_LB60_GPIO_SD_CD, | ||
354 | .gpio_read_only = -1, | ||
355 | .gpio_power = QI_LB60_GPIO_SD_VCC_EN_N, | ||
356 | .power_active_low = 1, | ||
357 | }; | ||
358 | |||
359 | /* OHCI */ | ||
360 | static struct regulator_consumer_supply avt2_usb_regulator_consumer = | ||
361 | REGULATOR_SUPPLY("vbus", "jz4740-ohci"); | ||
362 | |||
363 | static struct regulator_init_data avt2_usb_regulator_init_data = { | ||
364 | .num_consumer_supplies = 1, | ||
365 | .consumer_supplies = &avt2_usb_regulator_consumer, | ||
366 | .constraints = { | ||
367 | .name = "USB power", | ||
368 | .min_uV = 5000000, | ||
369 | .max_uV = 5000000, | ||
370 | .valid_modes_mask = REGULATOR_MODE_NORMAL, | ||
371 | .valid_ops_mask = REGULATOR_CHANGE_STATUS, | ||
372 | }, | ||
373 | }; | ||
374 | |||
375 | static struct fixed_voltage_config avt2_usb_regulator_data = { | ||
376 | .supply_name = "USB power", | ||
377 | .microvolts = 5000000, | ||
378 | .gpio = JZ_GPIO_PORTB(17), | ||
379 | .init_data = &avt2_usb_regulator_init_data, | ||
380 | }; | ||
381 | |||
382 | static struct platform_device avt2_usb_regulator_device = { | ||
383 | .name = "reg-fixed-voltage", | ||
384 | .id = -1, | ||
385 | .dev = { | ||
386 | .platform_data = &avt2_usb_regulator_data, | ||
387 | } | ||
388 | }; | ||
389 | |||
390 | /* beeper */ | ||
391 | static struct platform_device qi_lb60_pwm_beeper = { | ||
392 | .name = "pwm-beeper", | ||
393 | .id = -1, | ||
394 | .dev = { | ||
395 | .platform_data = (void *)4, | ||
396 | }, | ||
397 | }; | ||
398 | |||
399 | static struct platform_device *jz_platform_devices[] __initdata = { | ||
400 | &jz4740_udc_device, | ||
401 | &jz4740_mmc_device, | ||
402 | &jz4740_nand_device, | ||
403 | &qi_lb60_keypad, | ||
404 | &spigpio_device, | ||
405 | &jz4740_framebuffer_device, | ||
406 | &jz4740_pcm_device, | ||
407 | &jz4740_i2s_device, | ||
408 | &jz4740_codec_device, | ||
409 | &jz4740_rtc_device, | ||
410 | &jz4740_adc_device, | ||
411 | &qi_lb60_gpio_keys, | ||
412 | &qi_lb60_pwm_beeper, | ||
413 | }; | ||
414 | |||
415 | static void __init board_gpio_setup(void) | ||
416 | { | ||
417 | /* We only need to enable/disable pullup here for pins used in generic | ||
418 | * drivers. Everything else is done by the drivers themselfs. */ | ||
419 | jz_gpio_disable_pullup(QI_LB60_GPIO_SD_VCC_EN_N); | ||
420 | jz_gpio_disable_pullup(QI_LB60_GPIO_SD_CD); | ||
421 | } | ||
422 | |||
423 | static int __init qi_lb60_init_platform_devices(void) | ||
424 | { | ||
425 | jz4740_framebuffer_device.dev.platform_data = &qi_lb60_fb_pdata; | ||
426 | jz4740_nand_device.dev.platform_data = &qi_lb60_nand_pdata; | ||
427 | jz4740_adc_device.dev.platform_data = &qi_lb60_battery_pdata; | ||
428 | jz4740_mmc_device.dev.platform_data = &qi_lb60_mmc_pdata; | ||
429 | |||
430 | jz4740_serial_device_register(); | ||
431 | |||
432 | spi_register_board_info(qi_lb60_spi_board_info, | ||
433 | ARRAY_SIZE(qi_lb60_spi_board_info)); | ||
434 | |||
435 | if (is_avt2) { | ||
436 | platform_device_register(&avt2_usb_regulator_device); | ||
437 | platform_device_register(&jz4740_usb_ohci_device); | ||
438 | } | ||
439 | |||
440 | return platform_add_devices(jz_platform_devices, | ||
441 | ARRAY_SIZE(jz_platform_devices)); | ||
442 | |||
443 | } | ||
444 | |||
445 | struct jz4740_clock_board_data jz4740_clock_bdata = { | ||
446 | .ext_rate = 12000000, | ||
447 | .rtc_rate = 32768, | ||
448 | }; | ||
449 | |||
450 | static __init int board_avt2(char *str) | ||
451 | { | ||
452 | qi_lb60_mmc_pdata.card_detect_active_low = 1; | ||
453 | is_avt2 = true; | ||
454 | |||
455 | return 1; | ||
456 | } | ||
457 | __setup("avt2", board_avt2); | ||
458 | |||
459 | static int __init qi_lb60_board_setup(void) | ||
460 | { | ||
461 | printk(KERN_INFO "Qi Hardware JZ4740 QI %s setup\n", | ||
462 | is_avt2 ? "AVT2" : "LB60"); | ||
463 | |||
464 | board_gpio_setup(); | ||
465 | |||
466 | if (qi_lb60_init_platform_devices()) | ||
467 | panic("Failed to initalize platform devices\n"); | ||
468 | |||
469 | return 0; | ||
470 | } | ||
471 | arch_initcall(qi_lb60_board_setup); | ||
diff --git a/arch/mips/jz4740/clock-debugfs.c b/arch/mips/jz4740/clock-debugfs.c new file mode 100644 index 000000000000..330a0f2bf17b --- /dev/null +++ b/arch/mips/jz4740/clock-debugfs.c | |||
@@ -0,0 +1,109 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 SoC clock support debugfs entries | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/err.h> | ||
20 | |||
21 | #include <linux/debugfs.h> | ||
22 | #include <linux/uaccess.h> | ||
23 | |||
24 | #include <asm/mach-jz4740/clock.h> | ||
25 | #include "clock.h" | ||
26 | |||
27 | static struct dentry *jz4740_clock_debugfs; | ||
28 | |||
29 | static int jz4740_clock_debugfs_show_enabled(void *data, uint64_t *value) | ||
30 | { | ||
31 | struct clk *clk = data; | ||
32 | *value = clk_is_enabled(clk); | ||
33 | |||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | static int jz4740_clock_debugfs_set_enabled(void *data, uint64_t value) | ||
38 | { | ||
39 | struct clk *clk = data; | ||
40 | |||
41 | if (value) | ||
42 | return clk_enable(clk); | ||
43 | else | ||
44 | clk_disable(clk); | ||
45 | |||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | DEFINE_SIMPLE_ATTRIBUTE(jz4740_clock_debugfs_ops_enabled, | ||
50 | jz4740_clock_debugfs_show_enabled, | ||
51 | jz4740_clock_debugfs_set_enabled, | ||
52 | "%llu\n"); | ||
53 | |||
54 | static int jz4740_clock_debugfs_show_rate(void *data, uint64_t *value) | ||
55 | { | ||
56 | struct clk *clk = data; | ||
57 | *value = clk_get_rate(clk); | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | DEFINE_SIMPLE_ATTRIBUTE(jz4740_clock_debugfs_ops_rate, | ||
63 | jz4740_clock_debugfs_show_rate, | ||
64 | NULL, | ||
65 | "%llu\n"); | ||
66 | |||
67 | void jz4740_clock_debugfs_add_clk(struct clk *clk) | ||
68 | { | ||
69 | if (!jz4740_clock_debugfs) | ||
70 | return; | ||
71 | |||
72 | clk->debugfs_entry = debugfs_create_dir(clk->name, jz4740_clock_debugfs); | ||
73 | debugfs_create_file("rate", S_IWUGO | S_IRUGO, clk->debugfs_entry, clk, | ||
74 | &jz4740_clock_debugfs_ops_rate); | ||
75 | debugfs_create_file("enabled", S_IRUGO, clk->debugfs_entry, clk, | ||
76 | &jz4740_clock_debugfs_ops_enabled); | ||
77 | |||
78 | if (clk->parent) { | ||
79 | char parent_path[100]; | ||
80 | snprintf(parent_path, 100, "../%s", clk->parent->name); | ||
81 | clk->debugfs_parent_entry = debugfs_create_symlink("parent", | ||
82 | clk->debugfs_entry, | ||
83 | parent_path); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | /* TODO: Locking */ | ||
88 | void jz4740_clock_debugfs_update_parent(struct clk *clk) | ||
89 | { | ||
90 | if (clk->debugfs_parent_entry) | ||
91 | debugfs_remove(clk->debugfs_parent_entry); | ||
92 | |||
93 | if (clk->parent) { | ||
94 | char parent_path[100]; | ||
95 | snprintf(parent_path, 100, "../%s", clk->parent->name); | ||
96 | clk->debugfs_parent_entry = debugfs_create_symlink("parent", | ||
97 | clk->debugfs_entry, | ||
98 | parent_path); | ||
99 | } else { | ||
100 | clk->debugfs_parent_entry = NULL; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | void jz4740_clock_debugfs_init(void) | ||
105 | { | ||
106 | jz4740_clock_debugfs = debugfs_create_dir("jz4740-clock", NULL); | ||
107 | if (IS_ERR(jz4740_clock_debugfs)) | ||
108 | jz4740_clock_debugfs = NULL; | ||
109 | } | ||
diff --git a/arch/mips/jz4740/clock.c b/arch/mips/jz4740/clock.c new file mode 100644 index 000000000000..118a8a5562dd --- /dev/null +++ b/arch/mips/jz4740/clock.c | |||
@@ -0,0 +1,924 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 SoC clock support | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/list.h> | ||
23 | #include <linux/err.h> | ||
24 | |||
25 | #include <asm/mach-jz4740/clock.h> | ||
26 | #include <asm/mach-jz4740/base.h> | ||
27 | |||
28 | #include "clock.h" | ||
29 | |||
30 | #define JZ_REG_CLOCK_CTRL 0x00 | ||
31 | #define JZ_REG_CLOCK_LOW_POWER 0x04 | ||
32 | #define JZ_REG_CLOCK_PLL 0x10 | ||
33 | #define JZ_REG_CLOCK_GATE 0x20 | ||
34 | #define JZ_REG_CLOCK_SLEEP_CTRL 0x24 | ||
35 | #define JZ_REG_CLOCK_I2S 0x60 | ||
36 | #define JZ_REG_CLOCK_LCD 0x64 | ||
37 | #define JZ_REG_CLOCK_MMC 0x68 | ||
38 | #define JZ_REG_CLOCK_UHC 0x6C | ||
39 | #define JZ_REG_CLOCK_SPI 0x74 | ||
40 | |||
41 | #define JZ_CLOCK_CTRL_I2S_SRC_PLL BIT(31) | ||
42 | #define JZ_CLOCK_CTRL_KO_ENABLE BIT(30) | ||
43 | #define JZ_CLOCK_CTRL_UDC_SRC_PLL BIT(29) | ||
44 | #define JZ_CLOCK_CTRL_UDIV_MASK 0x1f800000 | ||
45 | #define JZ_CLOCK_CTRL_CHANGE_ENABLE BIT(22) | ||
46 | #define JZ_CLOCK_CTRL_PLL_HALF BIT(21) | ||
47 | #define JZ_CLOCK_CTRL_LDIV_MASK 0x001f0000 | ||
48 | #define JZ_CLOCK_CTRL_UDIV_OFFSET 23 | ||
49 | #define JZ_CLOCK_CTRL_LDIV_OFFSET 16 | ||
50 | #define JZ_CLOCK_CTRL_MDIV_OFFSET 12 | ||
51 | #define JZ_CLOCK_CTRL_PDIV_OFFSET 8 | ||
52 | #define JZ_CLOCK_CTRL_HDIV_OFFSET 4 | ||
53 | #define JZ_CLOCK_CTRL_CDIV_OFFSET 0 | ||
54 | |||
55 | #define JZ_CLOCK_GATE_UART0 BIT(0) | ||
56 | #define JZ_CLOCK_GATE_TCU BIT(1) | ||
57 | #define JZ_CLOCK_GATE_RTC BIT(2) | ||
58 | #define JZ_CLOCK_GATE_I2C BIT(3) | ||
59 | #define JZ_CLOCK_GATE_SPI BIT(4) | ||
60 | #define JZ_CLOCK_GATE_AIC BIT(5) | ||
61 | #define JZ_CLOCK_GATE_I2S BIT(6) | ||
62 | #define JZ_CLOCK_GATE_MMC BIT(7) | ||
63 | #define JZ_CLOCK_GATE_ADC BIT(8) | ||
64 | #define JZ_CLOCK_GATE_CIM BIT(9) | ||
65 | #define JZ_CLOCK_GATE_LCD BIT(10) | ||
66 | #define JZ_CLOCK_GATE_UDC BIT(11) | ||
67 | #define JZ_CLOCK_GATE_DMAC BIT(12) | ||
68 | #define JZ_CLOCK_GATE_IPU BIT(13) | ||
69 | #define JZ_CLOCK_GATE_UHC BIT(14) | ||
70 | #define JZ_CLOCK_GATE_UART1 BIT(15) | ||
71 | |||
72 | #define JZ_CLOCK_I2S_DIV_MASK 0x01ff | ||
73 | |||
74 | #define JZ_CLOCK_LCD_DIV_MASK 0x01ff | ||
75 | |||
76 | #define JZ_CLOCK_MMC_DIV_MASK 0x001f | ||
77 | |||
78 | #define JZ_CLOCK_UHC_DIV_MASK 0x000f | ||
79 | |||
80 | #define JZ_CLOCK_SPI_SRC_PLL BIT(31) | ||
81 | #define JZ_CLOCK_SPI_DIV_MASK 0x000f | ||
82 | |||
83 | #define JZ_CLOCK_PLL_M_MASK 0x01ff | ||
84 | #define JZ_CLOCK_PLL_N_MASK 0x001f | ||
85 | #define JZ_CLOCK_PLL_OD_MASK 0x0003 | ||
86 | #define JZ_CLOCK_PLL_STABLE BIT(10) | ||
87 | #define JZ_CLOCK_PLL_BYPASS BIT(9) | ||
88 | #define JZ_CLOCK_PLL_ENABLED BIT(8) | ||
89 | #define JZ_CLOCK_PLL_STABLIZE_MASK 0x000f | ||
90 | #define JZ_CLOCK_PLL_M_OFFSET 23 | ||
91 | #define JZ_CLOCK_PLL_N_OFFSET 18 | ||
92 | #define JZ_CLOCK_PLL_OD_OFFSET 16 | ||
93 | |||
94 | #define JZ_CLOCK_LOW_POWER_MODE_DOZE BIT(2) | ||
95 | #define JZ_CLOCK_LOW_POWER_MODE_SLEEP BIT(0) | ||
96 | |||
97 | #define JZ_CLOCK_SLEEP_CTRL_SUSPEND_UHC BIT(7) | ||
98 | #define JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC BIT(6) | ||
99 | |||
100 | static void __iomem *jz_clock_base; | ||
101 | static spinlock_t jz_clock_lock; | ||
102 | static LIST_HEAD(jz_clocks); | ||
103 | |||
104 | struct main_clk { | ||
105 | struct clk clk; | ||
106 | uint32_t div_offset; | ||
107 | }; | ||
108 | |||
109 | struct divided_clk { | ||
110 | struct clk clk; | ||
111 | uint32_t reg; | ||
112 | uint32_t mask; | ||
113 | }; | ||
114 | |||
115 | struct static_clk { | ||
116 | struct clk clk; | ||
117 | unsigned long rate; | ||
118 | }; | ||
119 | |||
120 | static uint32_t jz_clk_reg_read(int reg) | ||
121 | { | ||
122 | return readl(jz_clock_base + reg); | ||
123 | } | ||
124 | |||
125 | static void jz_clk_reg_write_mask(int reg, uint32_t val, uint32_t mask) | ||
126 | { | ||
127 | uint32_t val2; | ||
128 | |||
129 | spin_lock(&jz_clock_lock); | ||
130 | val2 = readl(jz_clock_base + reg); | ||
131 | val2 &= ~mask; | ||
132 | val2 |= val; | ||
133 | writel(val2, jz_clock_base + reg); | ||
134 | spin_unlock(&jz_clock_lock); | ||
135 | } | ||
136 | |||
137 | static void jz_clk_reg_set_bits(int reg, uint32_t mask) | ||
138 | { | ||
139 | uint32_t val; | ||
140 | |||
141 | spin_lock(&jz_clock_lock); | ||
142 | val = readl(jz_clock_base + reg); | ||
143 | val |= mask; | ||
144 | writel(val, jz_clock_base + reg); | ||
145 | spin_unlock(&jz_clock_lock); | ||
146 | } | ||
147 | |||
148 | static void jz_clk_reg_clear_bits(int reg, uint32_t mask) | ||
149 | { | ||
150 | uint32_t val; | ||
151 | |||
152 | spin_lock(&jz_clock_lock); | ||
153 | val = readl(jz_clock_base + reg); | ||
154 | val &= ~mask; | ||
155 | writel(val, jz_clock_base + reg); | ||
156 | spin_unlock(&jz_clock_lock); | ||
157 | } | ||
158 | |||
159 | static int jz_clk_enable_gating(struct clk *clk) | ||
160 | { | ||
161 | if (clk->gate_bit == JZ4740_CLK_NOT_GATED) | ||
162 | return -EINVAL; | ||
163 | |||
164 | jz_clk_reg_clear_bits(JZ_REG_CLOCK_GATE, clk->gate_bit); | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static int jz_clk_disable_gating(struct clk *clk) | ||
169 | { | ||
170 | if (clk->gate_bit == JZ4740_CLK_NOT_GATED) | ||
171 | return -EINVAL; | ||
172 | |||
173 | jz_clk_reg_set_bits(JZ_REG_CLOCK_GATE, clk->gate_bit); | ||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static int jz_clk_is_enabled_gating(struct clk *clk) | ||
178 | { | ||
179 | if (clk->gate_bit == JZ4740_CLK_NOT_GATED) | ||
180 | return 1; | ||
181 | |||
182 | return !(jz_clk_reg_read(JZ_REG_CLOCK_GATE) & clk->gate_bit); | ||
183 | } | ||
184 | |||
185 | static unsigned long jz_clk_static_get_rate(struct clk *clk) | ||
186 | { | ||
187 | return ((struct static_clk *)clk)->rate; | ||
188 | } | ||
189 | |||
190 | static int jz_clk_ko_enable(struct clk *clk) | ||
191 | { | ||
192 | jz_clk_reg_set_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_KO_ENABLE); | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static int jz_clk_ko_disable(struct clk *clk) | ||
197 | { | ||
198 | jz_clk_reg_clear_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_KO_ENABLE); | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int jz_clk_ko_is_enabled(struct clk *clk) | ||
203 | { | ||
204 | return !!(jz_clk_reg_read(JZ_REG_CLOCK_CTRL) & JZ_CLOCK_CTRL_KO_ENABLE); | ||
205 | } | ||
206 | |||
207 | static const int pllno[] = {1, 2, 2, 4}; | ||
208 | |||
209 | static unsigned long jz_clk_pll_get_rate(struct clk *clk) | ||
210 | { | ||
211 | uint32_t val; | ||
212 | int m; | ||
213 | int n; | ||
214 | int od; | ||
215 | |||
216 | val = jz_clk_reg_read(JZ_REG_CLOCK_PLL); | ||
217 | |||
218 | if (val & JZ_CLOCK_PLL_BYPASS) | ||
219 | return clk_get_rate(clk->parent); | ||
220 | |||
221 | m = ((val >> 23) & 0x1ff) + 2; | ||
222 | n = ((val >> 18) & 0x1f) + 2; | ||
223 | od = (val >> 16) & 0x3; | ||
224 | |||
225 | return ((clk_get_rate(clk->parent) / n) * m) / pllno[od]; | ||
226 | } | ||
227 | |||
228 | static unsigned long jz_clk_pll_half_get_rate(struct clk *clk) | ||
229 | { | ||
230 | uint32_t reg; | ||
231 | |||
232 | reg = jz_clk_reg_read(JZ_REG_CLOCK_CTRL); | ||
233 | if (reg & JZ_CLOCK_CTRL_PLL_HALF) | ||
234 | return jz_clk_pll_get_rate(clk->parent); | ||
235 | return jz_clk_pll_get_rate(clk->parent) >> 1; | ||
236 | } | ||
237 | |||
238 | static const int jz_clk_main_divs[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; | ||
239 | |||
240 | static unsigned long jz_clk_main_round_rate(struct clk *clk, unsigned long rate) | ||
241 | { | ||
242 | unsigned long parent_rate = jz_clk_pll_get_rate(clk->parent); | ||
243 | int div; | ||
244 | |||
245 | div = parent_rate / rate; | ||
246 | if (div > 32) | ||
247 | return parent_rate / 32; | ||
248 | else if (div < 1) | ||
249 | return parent_rate; | ||
250 | |||
251 | div &= (0x3 << (ffs(div) - 1)); | ||
252 | |||
253 | return parent_rate / div; | ||
254 | } | ||
255 | |||
256 | static unsigned long jz_clk_main_get_rate(struct clk *clk) | ||
257 | { | ||
258 | struct main_clk *mclk = (struct main_clk *)clk; | ||
259 | uint32_t div; | ||
260 | |||
261 | div = jz_clk_reg_read(JZ_REG_CLOCK_CTRL); | ||
262 | |||
263 | div >>= mclk->div_offset; | ||
264 | div &= 0xf; | ||
265 | |||
266 | if (div >= ARRAY_SIZE(jz_clk_main_divs)) | ||
267 | div = ARRAY_SIZE(jz_clk_main_divs) - 1; | ||
268 | |||
269 | return jz_clk_pll_get_rate(clk->parent) / jz_clk_main_divs[div]; | ||
270 | } | ||
271 | |||
272 | static int jz_clk_main_set_rate(struct clk *clk, unsigned long rate) | ||
273 | { | ||
274 | struct main_clk *mclk = (struct main_clk *)clk; | ||
275 | int i; | ||
276 | int div; | ||
277 | unsigned long parent_rate = jz_clk_pll_get_rate(clk->parent); | ||
278 | |||
279 | rate = jz_clk_main_round_rate(clk, rate); | ||
280 | |||
281 | div = parent_rate / rate; | ||
282 | |||
283 | i = (ffs(div) - 1) << 1; | ||
284 | if (i > 0 && !(div & BIT(i-1))) | ||
285 | i -= 1; | ||
286 | |||
287 | jz_clk_reg_write_mask(JZ_REG_CLOCK_CTRL, i << mclk->div_offset, | ||
288 | 0xf << mclk->div_offset); | ||
289 | |||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | static struct clk_ops jz_clk_static_ops = { | ||
294 | .get_rate = jz_clk_static_get_rate, | ||
295 | .enable = jz_clk_enable_gating, | ||
296 | .disable = jz_clk_disable_gating, | ||
297 | .is_enabled = jz_clk_is_enabled_gating, | ||
298 | }; | ||
299 | |||
300 | static struct static_clk jz_clk_ext = { | ||
301 | .clk = { | ||
302 | .name = "ext", | ||
303 | .gate_bit = JZ4740_CLK_NOT_GATED, | ||
304 | .ops = &jz_clk_static_ops, | ||
305 | }, | ||
306 | }; | ||
307 | |||
308 | static struct clk_ops jz_clk_pll_ops = { | ||
309 | .get_rate = jz_clk_pll_get_rate, | ||
310 | }; | ||
311 | |||
312 | static struct clk jz_clk_pll = { | ||
313 | .name = "pll", | ||
314 | .parent = &jz_clk_ext.clk, | ||
315 | .ops = &jz_clk_pll_ops, | ||
316 | }; | ||
317 | |||
318 | static struct clk_ops jz_clk_pll_half_ops = { | ||
319 | .get_rate = jz_clk_pll_half_get_rate, | ||
320 | }; | ||
321 | |||
322 | static struct clk jz_clk_pll_half = { | ||
323 | .name = "pll half", | ||
324 | .parent = &jz_clk_pll, | ||
325 | .ops = &jz_clk_pll_half_ops, | ||
326 | }; | ||
327 | |||
328 | static const struct clk_ops jz_clk_main_ops = { | ||
329 | .get_rate = jz_clk_main_get_rate, | ||
330 | .set_rate = jz_clk_main_set_rate, | ||
331 | .round_rate = jz_clk_main_round_rate, | ||
332 | }; | ||
333 | |||
334 | static struct main_clk jz_clk_cpu = { | ||
335 | .clk = { | ||
336 | .name = "cclk", | ||
337 | .parent = &jz_clk_pll, | ||
338 | .ops = &jz_clk_main_ops, | ||
339 | }, | ||
340 | .div_offset = JZ_CLOCK_CTRL_CDIV_OFFSET, | ||
341 | }; | ||
342 | |||
343 | static struct main_clk jz_clk_memory = { | ||
344 | .clk = { | ||
345 | .name = "mclk", | ||
346 | .parent = &jz_clk_pll, | ||
347 | .ops = &jz_clk_main_ops, | ||
348 | }, | ||
349 | .div_offset = JZ_CLOCK_CTRL_MDIV_OFFSET, | ||
350 | }; | ||
351 | |||
352 | static struct main_clk jz_clk_high_speed_peripheral = { | ||
353 | .clk = { | ||
354 | .name = "hclk", | ||
355 | .parent = &jz_clk_pll, | ||
356 | .ops = &jz_clk_main_ops, | ||
357 | }, | ||
358 | .div_offset = JZ_CLOCK_CTRL_HDIV_OFFSET, | ||
359 | }; | ||
360 | |||
361 | |||
362 | static struct main_clk jz_clk_low_speed_peripheral = { | ||
363 | .clk = { | ||
364 | .name = "pclk", | ||
365 | .parent = &jz_clk_pll, | ||
366 | .ops = &jz_clk_main_ops, | ||
367 | }, | ||
368 | .div_offset = JZ_CLOCK_CTRL_PDIV_OFFSET, | ||
369 | }; | ||
370 | |||
371 | static const struct clk_ops jz_clk_ko_ops = { | ||
372 | .enable = jz_clk_ko_enable, | ||
373 | .disable = jz_clk_ko_disable, | ||
374 | .is_enabled = jz_clk_ko_is_enabled, | ||
375 | }; | ||
376 | |||
377 | static struct clk jz_clk_ko = { | ||
378 | .name = "cko", | ||
379 | .parent = &jz_clk_memory.clk, | ||
380 | .ops = &jz_clk_ko_ops, | ||
381 | }; | ||
382 | |||
383 | static int jz_clk_spi_set_parent(struct clk *clk, struct clk *parent) | ||
384 | { | ||
385 | if (parent == &jz_clk_pll) | ||
386 | jz_clk_reg_set_bits(JZ_CLOCK_SPI_SRC_PLL, JZ_REG_CLOCK_SPI); | ||
387 | else if (parent == &jz_clk_ext.clk) | ||
388 | jz_clk_reg_clear_bits(JZ_CLOCK_SPI_SRC_PLL, JZ_REG_CLOCK_SPI); | ||
389 | else | ||
390 | return -EINVAL; | ||
391 | |||
392 | clk->parent = parent; | ||
393 | |||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | static int jz_clk_i2s_set_parent(struct clk *clk, struct clk *parent) | ||
398 | { | ||
399 | if (parent == &jz_clk_pll_half) | ||
400 | jz_clk_reg_set_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_I2S_SRC_PLL); | ||
401 | else if (parent == &jz_clk_ext.clk) | ||
402 | jz_clk_reg_clear_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_I2S_SRC_PLL); | ||
403 | else | ||
404 | return -EINVAL; | ||
405 | |||
406 | clk->parent = parent; | ||
407 | |||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | static int jz_clk_udc_enable(struct clk *clk) | ||
412 | { | ||
413 | jz_clk_reg_set_bits(JZ_REG_CLOCK_SLEEP_CTRL, | ||
414 | JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC); | ||
415 | |||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | static int jz_clk_udc_disable(struct clk *clk) | ||
420 | { | ||
421 | jz_clk_reg_clear_bits(JZ_REG_CLOCK_SLEEP_CTRL, | ||
422 | JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC); | ||
423 | |||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | static int jz_clk_udc_is_enabled(struct clk *clk) | ||
428 | { | ||
429 | return !!(jz_clk_reg_read(JZ_REG_CLOCK_SLEEP_CTRL) & | ||
430 | JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC); | ||
431 | } | ||
432 | |||
433 | static int jz_clk_udc_set_parent(struct clk *clk, struct clk *parent) | ||
434 | { | ||
435 | if (parent == &jz_clk_pll_half) | ||
436 | jz_clk_reg_set_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_UDC_SRC_PLL); | ||
437 | else if (parent == &jz_clk_ext.clk) | ||
438 | jz_clk_reg_clear_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_UDC_SRC_PLL); | ||
439 | else | ||
440 | return -EINVAL; | ||
441 | |||
442 | clk->parent = parent; | ||
443 | |||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | static int jz_clk_udc_set_rate(struct clk *clk, unsigned long rate) | ||
448 | { | ||
449 | int div; | ||
450 | |||
451 | if (clk->parent == &jz_clk_ext.clk) | ||
452 | return -EINVAL; | ||
453 | |||
454 | div = clk_get_rate(clk->parent) / rate - 1; | ||
455 | |||
456 | if (div < 0) | ||
457 | div = 0; | ||
458 | else if (div > 63) | ||
459 | div = 63; | ||
460 | |||
461 | jz_clk_reg_write_mask(JZ_REG_CLOCK_CTRL, div << JZ_CLOCK_CTRL_UDIV_OFFSET, | ||
462 | JZ_CLOCK_CTRL_UDIV_MASK); | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | static unsigned long jz_clk_udc_get_rate(struct clk *clk) | ||
467 | { | ||
468 | int div; | ||
469 | |||
470 | if (clk->parent == &jz_clk_ext.clk) | ||
471 | return clk_get_rate(clk->parent); | ||
472 | |||
473 | div = (jz_clk_reg_read(JZ_REG_CLOCK_CTRL) & JZ_CLOCK_CTRL_UDIV_MASK); | ||
474 | div >>= JZ_CLOCK_CTRL_UDIV_OFFSET; | ||
475 | div += 1; | ||
476 | |||
477 | return clk_get_rate(clk->parent) / div; | ||
478 | } | ||
479 | |||
480 | static unsigned long jz_clk_divided_get_rate(struct clk *clk) | ||
481 | { | ||
482 | struct divided_clk *dclk = (struct divided_clk *)clk; | ||
483 | int div; | ||
484 | |||
485 | if (clk->parent == &jz_clk_ext.clk) | ||
486 | return clk_get_rate(clk->parent); | ||
487 | |||
488 | div = (jz_clk_reg_read(dclk->reg) & dclk->mask) + 1; | ||
489 | |||
490 | return clk_get_rate(clk->parent) / div; | ||
491 | } | ||
492 | |||
493 | static int jz_clk_divided_set_rate(struct clk *clk, unsigned long rate) | ||
494 | { | ||
495 | struct divided_clk *dclk = (struct divided_clk *)clk; | ||
496 | int div; | ||
497 | |||
498 | if (clk->parent == &jz_clk_ext.clk) | ||
499 | return -EINVAL; | ||
500 | |||
501 | div = clk_get_rate(clk->parent) / rate - 1; | ||
502 | |||
503 | if (div < 0) | ||
504 | div = 0; | ||
505 | else if (div > dclk->mask) | ||
506 | div = dclk->mask; | ||
507 | |||
508 | jz_clk_reg_write_mask(dclk->reg, div, dclk->mask); | ||
509 | |||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static unsigned long jz_clk_ldclk_round_rate(struct clk *clk, unsigned long rate) | ||
514 | { | ||
515 | int div; | ||
516 | unsigned long parent_rate = jz_clk_pll_half_get_rate(clk->parent); | ||
517 | |||
518 | if (rate > 150000000) | ||
519 | return 150000000; | ||
520 | |||
521 | div = parent_rate / rate; | ||
522 | if (div < 1) | ||
523 | div = 1; | ||
524 | else if (div > 32) | ||
525 | div = 32; | ||
526 | |||
527 | return parent_rate / div; | ||
528 | } | ||
529 | |||
530 | static int jz_clk_ldclk_set_rate(struct clk *clk, unsigned long rate) | ||
531 | { | ||
532 | int div; | ||
533 | |||
534 | if (rate > 150000000) | ||
535 | return -EINVAL; | ||
536 | |||
537 | div = jz_clk_pll_half_get_rate(clk->parent) / rate - 1; | ||
538 | if (div < 0) | ||
539 | div = 0; | ||
540 | else if (div > 31) | ||
541 | div = 31; | ||
542 | |||
543 | jz_clk_reg_write_mask(JZ_REG_CLOCK_CTRL, div << JZ_CLOCK_CTRL_LDIV_OFFSET, | ||
544 | JZ_CLOCK_CTRL_LDIV_MASK); | ||
545 | |||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | static unsigned long jz_clk_ldclk_get_rate(struct clk *clk) | ||
550 | { | ||
551 | int div; | ||
552 | |||
553 | div = jz_clk_reg_read(JZ_REG_CLOCK_CTRL) & JZ_CLOCK_CTRL_LDIV_MASK; | ||
554 | div >>= JZ_CLOCK_CTRL_LDIV_OFFSET; | ||
555 | |||
556 | return jz_clk_pll_half_get_rate(clk->parent) / (div + 1); | ||
557 | } | ||
558 | |||
559 | static const struct clk_ops jz_clk_ops_ld = { | ||
560 | .set_rate = jz_clk_ldclk_set_rate, | ||
561 | .get_rate = jz_clk_ldclk_get_rate, | ||
562 | .round_rate = jz_clk_ldclk_round_rate, | ||
563 | .enable = jz_clk_enable_gating, | ||
564 | .disable = jz_clk_disable_gating, | ||
565 | .is_enabled = jz_clk_is_enabled_gating, | ||
566 | }; | ||
567 | |||
568 | static struct clk jz_clk_ld = { | ||
569 | .name = "lcd", | ||
570 | .gate_bit = JZ_CLOCK_GATE_LCD, | ||
571 | .parent = &jz_clk_pll_half, | ||
572 | .ops = &jz_clk_ops_ld, | ||
573 | }; | ||
574 | |||
575 | static const struct clk_ops jz_clk_i2s_ops = { | ||
576 | .set_rate = jz_clk_divided_set_rate, | ||
577 | .get_rate = jz_clk_divided_get_rate, | ||
578 | .enable = jz_clk_enable_gating, | ||
579 | .disable = jz_clk_disable_gating, | ||
580 | .is_enabled = jz_clk_is_enabled_gating, | ||
581 | .set_parent = jz_clk_i2s_set_parent, | ||
582 | }; | ||
583 | |||
584 | static const struct clk_ops jz_clk_spi_ops = { | ||
585 | .set_rate = jz_clk_divided_set_rate, | ||
586 | .get_rate = jz_clk_divided_get_rate, | ||
587 | .enable = jz_clk_enable_gating, | ||
588 | .disable = jz_clk_disable_gating, | ||
589 | .is_enabled = jz_clk_is_enabled_gating, | ||
590 | .set_parent = jz_clk_spi_set_parent, | ||
591 | }; | ||
592 | |||
593 | static const struct clk_ops jz_clk_divided_ops = { | ||
594 | .set_rate = jz_clk_divided_set_rate, | ||
595 | .get_rate = jz_clk_divided_get_rate, | ||
596 | .enable = jz_clk_enable_gating, | ||
597 | .disable = jz_clk_disable_gating, | ||
598 | .is_enabled = jz_clk_is_enabled_gating, | ||
599 | }; | ||
600 | |||
601 | static struct divided_clk jz4740_clock_divided_clks[] = { | ||
602 | [0] = { | ||
603 | .clk = { | ||
604 | .name = "i2s", | ||
605 | .parent = &jz_clk_ext.clk, | ||
606 | .gate_bit = JZ_CLOCK_GATE_I2S, | ||
607 | .ops = &jz_clk_i2s_ops, | ||
608 | }, | ||
609 | .reg = JZ_REG_CLOCK_I2S, | ||
610 | .mask = JZ_CLOCK_I2S_DIV_MASK, | ||
611 | }, | ||
612 | [1] = { | ||
613 | .clk = { | ||
614 | .name = "spi", | ||
615 | .parent = &jz_clk_ext.clk, | ||
616 | .gate_bit = JZ_CLOCK_GATE_SPI, | ||
617 | .ops = &jz_clk_spi_ops, | ||
618 | }, | ||
619 | .reg = JZ_REG_CLOCK_SPI, | ||
620 | .mask = JZ_CLOCK_SPI_DIV_MASK, | ||
621 | }, | ||
622 | [2] = { | ||
623 | .clk = { | ||
624 | .name = "lcd_pclk", | ||
625 | .parent = &jz_clk_pll_half, | ||
626 | .gate_bit = JZ4740_CLK_NOT_GATED, | ||
627 | .ops = &jz_clk_divided_ops, | ||
628 | }, | ||
629 | .reg = JZ_REG_CLOCK_LCD, | ||
630 | .mask = JZ_CLOCK_LCD_DIV_MASK, | ||
631 | }, | ||
632 | [3] = { | ||
633 | .clk = { | ||
634 | .name = "mmc", | ||
635 | .parent = &jz_clk_pll_half, | ||
636 | .gate_bit = JZ_CLOCK_GATE_MMC, | ||
637 | .ops = &jz_clk_divided_ops, | ||
638 | }, | ||
639 | .reg = JZ_REG_CLOCK_MMC, | ||
640 | .mask = JZ_CLOCK_MMC_DIV_MASK, | ||
641 | }, | ||
642 | [4] = { | ||
643 | .clk = { | ||
644 | .name = "uhc", | ||
645 | .parent = &jz_clk_pll_half, | ||
646 | .gate_bit = JZ_CLOCK_GATE_UHC, | ||
647 | .ops = &jz_clk_divided_ops, | ||
648 | }, | ||
649 | .reg = JZ_REG_CLOCK_UHC, | ||
650 | .mask = JZ_CLOCK_UHC_DIV_MASK, | ||
651 | }, | ||
652 | }; | ||
653 | |||
654 | static const struct clk_ops jz_clk_udc_ops = { | ||
655 | .set_parent = jz_clk_udc_set_parent, | ||
656 | .set_rate = jz_clk_udc_set_rate, | ||
657 | .get_rate = jz_clk_udc_get_rate, | ||
658 | .enable = jz_clk_udc_enable, | ||
659 | .disable = jz_clk_udc_disable, | ||
660 | .is_enabled = jz_clk_udc_is_enabled, | ||
661 | }; | ||
662 | |||
663 | static const struct clk_ops jz_clk_simple_ops = { | ||
664 | .enable = jz_clk_enable_gating, | ||
665 | .disable = jz_clk_disable_gating, | ||
666 | .is_enabled = jz_clk_is_enabled_gating, | ||
667 | }; | ||
668 | |||
669 | static struct clk jz4740_clock_simple_clks[] = { | ||
670 | [0] = { | ||
671 | .name = "udc", | ||
672 | .parent = &jz_clk_ext.clk, | ||
673 | .ops = &jz_clk_udc_ops, | ||
674 | }, | ||
675 | [1] = { | ||
676 | .name = "uart0", | ||
677 | .parent = &jz_clk_ext.clk, | ||
678 | .gate_bit = JZ_CLOCK_GATE_UART0, | ||
679 | .ops = &jz_clk_simple_ops, | ||
680 | }, | ||
681 | [2] = { | ||
682 | .name = "uart1", | ||
683 | .parent = &jz_clk_ext.clk, | ||
684 | .gate_bit = JZ_CLOCK_GATE_UART1, | ||
685 | .ops = &jz_clk_simple_ops, | ||
686 | }, | ||
687 | [3] = { | ||
688 | .name = "dma", | ||
689 | .parent = &jz_clk_high_speed_peripheral.clk, | ||
690 | .gate_bit = JZ_CLOCK_GATE_UART0, | ||
691 | .ops = &jz_clk_simple_ops, | ||
692 | }, | ||
693 | [4] = { | ||
694 | .name = "ipu", | ||
695 | .parent = &jz_clk_high_speed_peripheral.clk, | ||
696 | .gate_bit = JZ_CLOCK_GATE_IPU, | ||
697 | .ops = &jz_clk_simple_ops, | ||
698 | }, | ||
699 | [5] = { | ||
700 | .name = "adc", | ||
701 | .parent = &jz_clk_ext.clk, | ||
702 | .gate_bit = JZ_CLOCK_GATE_ADC, | ||
703 | .ops = &jz_clk_simple_ops, | ||
704 | }, | ||
705 | [6] = { | ||
706 | .name = "i2c", | ||
707 | .parent = &jz_clk_ext.clk, | ||
708 | .gate_bit = JZ_CLOCK_GATE_I2C, | ||
709 | .ops = &jz_clk_simple_ops, | ||
710 | }, | ||
711 | [7] = { | ||
712 | .name = "aic", | ||
713 | .parent = &jz_clk_ext.clk, | ||
714 | .gate_bit = JZ_CLOCK_GATE_AIC, | ||
715 | .ops = &jz_clk_simple_ops, | ||
716 | }, | ||
717 | }; | ||
718 | |||
719 | static struct static_clk jz_clk_rtc = { | ||
720 | .clk = { | ||
721 | .name = "rtc", | ||
722 | .gate_bit = JZ_CLOCK_GATE_RTC, | ||
723 | .ops = &jz_clk_static_ops, | ||
724 | }, | ||
725 | .rate = 32768, | ||
726 | }; | ||
727 | |||
728 | int clk_enable(struct clk *clk) | ||
729 | { | ||
730 | if (!clk->ops->enable) | ||
731 | return -EINVAL; | ||
732 | |||
733 | return clk->ops->enable(clk); | ||
734 | } | ||
735 | EXPORT_SYMBOL_GPL(clk_enable); | ||
736 | |||
737 | void clk_disable(struct clk *clk) | ||
738 | { | ||
739 | if (clk->ops->disable) | ||
740 | clk->ops->disable(clk); | ||
741 | } | ||
742 | EXPORT_SYMBOL_GPL(clk_disable); | ||
743 | |||
744 | int clk_is_enabled(struct clk *clk) | ||
745 | { | ||
746 | if (clk->ops->is_enabled) | ||
747 | return clk->ops->is_enabled(clk); | ||
748 | |||
749 | return 1; | ||
750 | } | ||
751 | |||
752 | unsigned long clk_get_rate(struct clk *clk) | ||
753 | { | ||
754 | if (clk->ops->get_rate) | ||
755 | return clk->ops->get_rate(clk); | ||
756 | if (clk->parent) | ||
757 | return clk_get_rate(clk->parent); | ||
758 | |||
759 | return -EINVAL; | ||
760 | } | ||
761 | EXPORT_SYMBOL_GPL(clk_get_rate); | ||
762 | |||
763 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
764 | { | ||
765 | if (!clk->ops->set_rate) | ||
766 | return -EINVAL; | ||
767 | return clk->ops->set_rate(clk, rate); | ||
768 | } | ||
769 | EXPORT_SYMBOL_GPL(clk_set_rate); | ||
770 | |||
771 | long clk_round_rate(struct clk *clk, unsigned long rate) | ||
772 | { | ||
773 | if (clk->ops->round_rate) | ||
774 | return clk->ops->round_rate(clk, rate); | ||
775 | |||
776 | return -EINVAL; | ||
777 | } | ||
778 | EXPORT_SYMBOL_GPL(clk_round_rate); | ||
779 | |||
780 | int clk_set_parent(struct clk *clk, struct clk *parent) | ||
781 | { | ||
782 | int ret; | ||
783 | int enabled; | ||
784 | |||
785 | if (!clk->ops->set_parent) | ||
786 | return -EINVAL; | ||
787 | |||
788 | enabled = clk_is_enabled(clk); | ||
789 | if (enabled) | ||
790 | clk_disable(clk); | ||
791 | ret = clk->ops->set_parent(clk, parent); | ||
792 | if (enabled) | ||
793 | clk_enable(clk); | ||
794 | |||
795 | jz4740_clock_debugfs_update_parent(clk); | ||
796 | |||
797 | return ret; | ||
798 | } | ||
799 | EXPORT_SYMBOL_GPL(clk_set_parent); | ||
800 | |||
801 | struct clk *clk_get(struct device *dev, const char *name) | ||
802 | { | ||
803 | struct clk *clk; | ||
804 | |||
805 | list_for_each_entry(clk, &jz_clocks, list) { | ||
806 | if (strcmp(clk->name, name) == 0) | ||
807 | return clk; | ||
808 | } | ||
809 | return ERR_PTR(-ENXIO); | ||
810 | } | ||
811 | EXPORT_SYMBOL_GPL(clk_get); | ||
812 | |||
813 | void clk_put(struct clk *clk) | ||
814 | { | ||
815 | } | ||
816 | EXPORT_SYMBOL_GPL(clk_put); | ||
817 | |||
818 | static inline void clk_add(struct clk *clk) | ||
819 | { | ||
820 | list_add_tail(&clk->list, &jz_clocks); | ||
821 | |||
822 | jz4740_clock_debugfs_add_clk(clk); | ||
823 | } | ||
824 | |||
825 | static void clk_register_clks(void) | ||
826 | { | ||
827 | size_t i; | ||
828 | |||
829 | clk_add(&jz_clk_ext.clk); | ||
830 | clk_add(&jz_clk_pll); | ||
831 | clk_add(&jz_clk_pll_half); | ||
832 | clk_add(&jz_clk_cpu.clk); | ||
833 | clk_add(&jz_clk_high_speed_peripheral.clk); | ||
834 | clk_add(&jz_clk_low_speed_peripheral.clk); | ||
835 | clk_add(&jz_clk_ko); | ||
836 | clk_add(&jz_clk_ld); | ||
837 | clk_add(&jz_clk_rtc.clk); | ||
838 | |||
839 | for (i = 0; i < ARRAY_SIZE(jz4740_clock_divided_clks); ++i) | ||
840 | clk_add(&jz4740_clock_divided_clks[i].clk); | ||
841 | |||
842 | for (i = 0; i < ARRAY_SIZE(jz4740_clock_simple_clks); ++i) | ||
843 | clk_add(&jz4740_clock_simple_clks[i]); | ||
844 | } | ||
845 | |||
846 | void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode) | ||
847 | { | ||
848 | switch (mode) { | ||
849 | case JZ4740_WAIT_MODE_IDLE: | ||
850 | jz_clk_reg_clear_bits(JZ_REG_CLOCK_LOW_POWER, JZ_CLOCK_LOW_POWER_MODE_SLEEP); | ||
851 | break; | ||
852 | case JZ4740_WAIT_MODE_SLEEP: | ||
853 | jz_clk_reg_set_bits(JZ_REG_CLOCK_LOW_POWER, JZ_CLOCK_LOW_POWER_MODE_SLEEP); | ||
854 | break; | ||
855 | } | ||
856 | } | ||
857 | |||
858 | void jz4740_clock_udc_disable_auto_suspend(void) | ||
859 | { | ||
860 | jz_clk_reg_clear_bits(JZ_REG_CLOCK_GATE, JZ_CLOCK_GATE_UDC); | ||
861 | } | ||
862 | EXPORT_SYMBOL_GPL(jz4740_clock_udc_disable_auto_suspend); | ||
863 | |||
864 | void jz4740_clock_udc_enable_auto_suspend(void) | ||
865 | { | ||
866 | jz_clk_reg_set_bits(JZ_REG_CLOCK_GATE, JZ_CLOCK_GATE_UDC); | ||
867 | } | ||
868 | EXPORT_SYMBOL_GPL(jz4740_clock_udc_enable_auto_suspend); | ||
869 | |||
870 | void jz4740_clock_suspend(void) | ||
871 | { | ||
872 | jz_clk_reg_set_bits(JZ_REG_CLOCK_GATE, | ||
873 | JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0); | ||
874 | |||
875 | jz_clk_reg_clear_bits(JZ_REG_CLOCK_PLL, JZ_CLOCK_PLL_ENABLED); | ||
876 | } | ||
877 | |||
878 | void jz4740_clock_resume(void) | ||
879 | { | ||
880 | uint32_t pll; | ||
881 | |||
882 | jz_clk_reg_set_bits(JZ_REG_CLOCK_PLL, JZ_CLOCK_PLL_ENABLED); | ||
883 | |||
884 | do { | ||
885 | pll = jz_clk_reg_read(JZ_REG_CLOCK_PLL); | ||
886 | } while (!(pll & JZ_CLOCK_PLL_STABLE)); | ||
887 | |||
888 | jz_clk_reg_clear_bits(JZ_REG_CLOCK_GATE, | ||
889 | JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0); | ||
890 | } | ||
891 | |||
892 | static int jz4740_clock_init(void) | ||
893 | { | ||
894 | uint32_t val; | ||
895 | |||
896 | jz_clock_base = ioremap(JZ4740_CPM_BASE_ADDR, 0x100); | ||
897 | if (!jz_clock_base) | ||
898 | return -EBUSY; | ||
899 | |||
900 | spin_lock_init(&jz_clock_lock); | ||
901 | |||
902 | jz_clk_ext.rate = jz4740_clock_bdata.ext_rate; | ||
903 | jz_clk_rtc.rate = jz4740_clock_bdata.rtc_rate; | ||
904 | |||
905 | val = jz_clk_reg_read(JZ_REG_CLOCK_SPI); | ||
906 | |||
907 | if (val & JZ_CLOCK_SPI_SRC_PLL) | ||
908 | jz4740_clock_divided_clks[1].clk.parent = &jz_clk_pll_half; | ||
909 | |||
910 | val = jz_clk_reg_read(JZ_REG_CLOCK_CTRL); | ||
911 | |||
912 | if (val & JZ_CLOCK_CTRL_I2S_SRC_PLL) | ||
913 | jz4740_clock_divided_clks[0].clk.parent = &jz_clk_pll_half; | ||
914 | |||
915 | if (val & JZ_CLOCK_CTRL_UDC_SRC_PLL) | ||
916 | jz4740_clock_simple_clks[0].parent = &jz_clk_pll_half; | ||
917 | |||
918 | jz4740_clock_debugfs_init(); | ||
919 | |||
920 | clk_register_clks(); | ||
921 | |||
922 | return 0; | ||
923 | } | ||
924 | arch_initcall(jz4740_clock_init); | ||
diff --git a/arch/mips/jz4740/clock.h b/arch/mips/jz4740/clock.h new file mode 100644 index 000000000000..5d07499d7461 --- /dev/null +++ b/arch/mips/jz4740/clock.h | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 SoC clock support | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #ifndef __MIPS_JZ4740_CLOCK_H__ | ||
17 | #define __MIPS_JZ4740_CLOCK_H__ | ||
18 | |||
19 | #include <linux/list.h> | ||
20 | |||
21 | struct jz4740_clock_board_data { | ||
22 | unsigned long ext_rate; | ||
23 | unsigned long rtc_rate; | ||
24 | }; | ||
25 | |||
26 | extern struct jz4740_clock_board_data jz4740_clock_bdata; | ||
27 | |||
28 | void jz4740_clock_suspend(void); | ||
29 | void jz4740_clock_resume(void); | ||
30 | |||
31 | struct clk; | ||
32 | |||
33 | struct clk_ops { | ||
34 | unsigned long (*get_rate)(struct clk *clk); | ||
35 | unsigned long (*round_rate)(struct clk *clk, unsigned long rate); | ||
36 | int (*set_rate)(struct clk *clk, unsigned long rate); | ||
37 | int (*enable)(struct clk *clk); | ||
38 | int (*disable)(struct clk *clk); | ||
39 | int (*is_enabled)(struct clk *clk); | ||
40 | |||
41 | int (*set_parent)(struct clk *clk, struct clk *parent); | ||
42 | |||
43 | }; | ||
44 | |||
45 | struct clk { | ||
46 | const char *name; | ||
47 | struct clk *parent; | ||
48 | |||
49 | uint32_t gate_bit; | ||
50 | |||
51 | const struct clk_ops *ops; | ||
52 | |||
53 | struct list_head list; | ||
54 | |||
55 | #ifdef CONFIG_DEBUG_FS | ||
56 | struct dentry *debugfs_entry; | ||
57 | struct dentry *debugfs_parent_entry; | ||
58 | #endif | ||
59 | |||
60 | }; | ||
61 | |||
62 | #define JZ4740_CLK_NOT_GATED ((uint32_t)-1) | ||
63 | |||
64 | int clk_is_enabled(struct clk *clk); | ||
65 | |||
66 | #ifdef CONFIG_DEBUG_FS | ||
67 | void jz4740_clock_debugfs_init(void); | ||
68 | void jz4740_clock_debugfs_add_clk(struct clk *clk); | ||
69 | void jz4740_clock_debugfs_update_parent(struct clk *clk); | ||
70 | #else | ||
71 | static inline void jz4740_clock_debugfs_init(void) {}; | ||
72 | static inline void jz4740_clock_debugfs_add_clk(struct clk *clk) {}; | ||
73 | static inline void jz4740_clock_debugfs_update_parent(struct clk *clk) {}; | ||
74 | #endif | ||
75 | |||
76 | #endif | ||
diff --git a/arch/mips/jz4740/dma.c b/arch/mips/jz4740/dma.c new file mode 100644 index 000000000000..5ebe75a68350 --- /dev/null +++ b/arch/mips/jz4740/dma.c | |||
@@ -0,0 +1,289 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 SoC DMA support | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/spinlock.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | |||
21 | #include <linux/dma-mapping.h> | ||
22 | #include <asm/mach-jz4740/dma.h> | ||
23 | #include <asm/mach-jz4740/base.h> | ||
24 | |||
25 | #define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20) | ||
26 | #define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20) | ||
27 | #define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20) | ||
28 | #define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20) | ||
29 | #define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20) | ||
30 | #define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20) | ||
31 | #define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20) | ||
32 | |||
33 | #define JZ_REG_DMA_CTRL 0x300 | ||
34 | #define JZ_REG_DMA_IRQ 0x304 | ||
35 | #define JZ_REG_DMA_DOORBELL 0x308 | ||
36 | #define JZ_REG_DMA_DOORBELL_SET 0x30C | ||
37 | |||
38 | #define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31) | ||
39 | #define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6) | ||
40 | #define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4) | ||
41 | #define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3) | ||
42 | #define JZ_DMA_STATUS_CTRL_HALT BIT(2) | ||
43 | #define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1) | ||
44 | #define JZ_DMA_STATUS_CTRL_ENABLE BIT(0) | ||
45 | |||
46 | #define JZ_DMA_CMD_SRC_INC BIT(23) | ||
47 | #define JZ_DMA_CMD_DST_INC BIT(22) | ||
48 | #define JZ_DMA_CMD_RDIL_MASK (0xf << 16) | ||
49 | #define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14) | ||
50 | #define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12) | ||
51 | #define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8) | ||
52 | #define JZ_DMA_CMD_BLOCK_MODE BIT(7) | ||
53 | #define JZ_DMA_CMD_DESC_VALID BIT(4) | ||
54 | #define JZ_DMA_CMD_DESC_VALID_MODE BIT(3) | ||
55 | #define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2) | ||
56 | #define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1) | ||
57 | #define JZ_DMA_CMD_LINK_ENABLE BIT(0) | ||
58 | |||
59 | #define JZ_DMA_CMD_FLAGS_OFFSET 22 | ||
60 | #define JZ_DMA_CMD_RDIL_OFFSET 16 | ||
61 | #define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14 | ||
62 | #define JZ_DMA_CMD_DST_WIDTH_OFFSET 12 | ||
63 | #define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8 | ||
64 | #define JZ_DMA_CMD_MODE_OFFSET 7 | ||
65 | |||
66 | #define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8) | ||
67 | #define JZ_DMA_CTRL_HALT BIT(3) | ||
68 | #define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2) | ||
69 | #define JZ_DMA_CTRL_ENABLE BIT(0) | ||
70 | |||
71 | |||
72 | static void __iomem *jz4740_dma_base; | ||
73 | static spinlock_t jz4740_dma_lock; | ||
74 | |||
75 | static inline uint32_t jz4740_dma_read(size_t reg) | ||
76 | { | ||
77 | return readl(jz4740_dma_base + reg); | ||
78 | } | ||
79 | |||
80 | static inline void jz4740_dma_write(size_t reg, uint32_t val) | ||
81 | { | ||
82 | writel(val, jz4740_dma_base + reg); | ||
83 | } | ||
84 | |||
85 | static inline void jz4740_dma_write_mask(size_t reg, uint32_t val, uint32_t mask) | ||
86 | { | ||
87 | uint32_t val2; | ||
88 | val2 = jz4740_dma_read(reg); | ||
89 | val2 &= ~mask; | ||
90 | val2 |= val; | ||
91 | jz4740_dma_write(reg, val2); | ||
92 | } | ||
93 | |||
94 | struct jz4740_dma_chan { | ||
95 | unsigned int id; | ||
96 | void *dev; | ||
97 | const char *name; | ||
98 | |||
99 | enum jz4740_dma_flags flags; | ||
100 | uint32_t transfer_shift; | ||
101 | |||
102 | jz4740_dma_complete_callback_t complete_cb; | ||
103 | |||
104 | unsigned used:1; | ||
105 | }; | ||
106 | |||
107 | #define JZ4740_DMA_CHANNEL(_id) { .id = _id } | ||
108 | |||
109 | struct jz4740_dma_chan jz4740_dma_channels[] = { | ||
110 | JZ4740_DMA_CHANNEL(0), | ||
111 | JZ4740_DMA_CHANNEL(1), | ||
112 | JZ4740_DMA_CHANNEL(2), | ||
113 | JZ4740_DMA_CHANNEL(3), | ||
114 | JZ4740_DMA_CHANNEL(4), | ||
115 | JZ4740_DMA_CHANNEL(5), | ||
116 | }; | ||
117 | |||
118 | struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name) | ||
119 | { | ||
120 | unsigned int i; | ||
121 | struct jz4740_dma_chan *dma = NULL; | ||
122 | |||
123 | spin_lock(&jz4740_dma_lock); | ||
124 | |||
125 | for (i = 0; i < ARRAY_SIZE(jz4740_dma_channels); ++i) { | ||
126 | if (!jz4740_dma_channels[i].used) { | ||
127 | dma = &jz4740_dma_channels[i]; | ||
128 | dma->used = 1; | ||
129 | break; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | spin_unlock(&jz4740_dma_lock); | ||
134 | |||
135 | if (!dma) | ||
136 | return NULL; | ||
137 | |||
138 | dma->dev = dev; | ||
139 | dma->name = name; | ||
140 | |||
141 | return dma; | ||
142 | } | ||
143 | EXPORT_SYMBOL_GPL(jz4740_dma_request); | ||
144 | |||
145 | void jz4740_dma_configure(struct jz4740_dma_chan *dma, | ||
146 | const struct jz4740_dma_config *config) | ||
147 | { | ||
148 | uint32_t cmd; | ||
149 | |||
150 | switch (config->transfer_size) { | ||
151 | case JZ4740_DMA_TRANSFER_SIZE_2BYTE: | ||
152 | dma->transfer_shift = 1; | ||
153 | break; | ||
154 | case JZ4740_DMA_TRANSFER_SIZE_4BYTE: | ||
155 | dma->transfer_shift = 2; | ||
156 | break; | ||
157 | case JZ4740_DMA_TRANSFER_SIZE_16BYTE: | ||
158 | dma->transfer_shift = 4; | ||
159 | break; | ||
160 | case JZ4740_DMA_TRANSFER_SIZE_32BYTE: | ||
161 | dma->transfer_shift = 5; | ||
162 | break; | ||
163 | default: | ||
164 | dma->transfer_shift = 0; | ||
165 | break; | ||
166 | } | ||
167 | |||
168 | cmd = config->flags << JZ_DMA_CMD_FLAGS_OFFSET; | ||
169 | cmd |= config->src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET; | ||
170 | cmd |= config->dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET; | ||
171 | cmd |= config->transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET; | ||
172 | cmd |= config->mode << JZ_DMA_CMD_MODE_OFFSET; | ||
173 | cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE; | ||
174 | |||
175 | jz4740_dma_write(JZ_REG_DMA_CMD(dma->id), cmd); | ||
176 | jz4740_dma_write(JZ_REG_DMA_STATUS_CTRL(dma->id), 0); | ||
177 | jz4740_dma_write(JZ_REG_DMA_REQ_TYPE(dma->id), config->request_type); | ||
178 | } | ||
179 | EXPORT_SYMBOL_GPL(jz4740_dma_configure); | ||
180 | |||
181 | void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src) | ||
182 | { | ||
183 | jz4740_dma_write(JZ_REG_DMA_SRC_ADDR(dma->id), src); | ||
184 | } | ||
185 | EXPORT_SYMBOL_GPL(jz4740_dma_set_src_addr); | ||
186 | |||
187 | void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst) | ||
188 | { | ||
189 | jz4740_dma_write(JZ_REG_DMA_DST_ADDR(dma->id), dst); | ||
190 | } | ||
191 | EXPORT_SYMBOL_GPL(jz4740_dma_set_dst_addr); | ||
192 | |||
193 | void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count) | ||
194 | { | ||
195 | count >>= dma->transfer_shift; | ||
196 | jz4740_dma_write(JZ_REG_DMA_TRANSFER_COUNT(dma->id), count); | ||
197 | } | ||
198 | EXPORT_SYMBOL_GPL(jz4740_dma_set_transfer_count); | ||
199 | |||
200 | void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma, | ||
201 | jz4740_dma_complete_callback_t cb) | ||
202 | { | ||
203 | dma->complete_cb = cb; | ||
204 | } | ||
205 | EXPORT_SYMBOL_GPL(jz4740_dma_set_complete_cb); | ||
206 | |||
207 | void jz4740_dma_free(struct jz4740_dma_chan *dma) | ||
208 | { | ||
209 | dma->dev = NULL; | ||
210 | dma->complete_cb = NULL; | ||
211 | dma->used = 0; | ||
212 | } | ||
213 | EXPORT_SYMBOL_GPL(jz4740_dma_free); | ||
214 | |||
215 | void jz4740_dma_enable(struct jz4740_dma_chan *dma) | ||
216 | { | ||
217 | jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), | ||
218 | JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE, | ||
219 | JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC | | ||
220 | JZ_DMA_STATUS_CTRL_ENABLE); | ||
221 | |||
222 | jz4740_dma_write_mask(JZ_REG_DMA_CTRL, | ||
223 | JZ_DMA_CTRL_ENABLE, | ||
224 | JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE); | ||
225 | } | ||
226 | EXPORT_SYMBOL_GPL(jz4740_dma_enable); | ||
227 | |||
228 | void jz4740_dma_disable(struct jz4740_dma_chan *dma) | ||
229 | { | ||
230 | jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0, | ||
231 | JZ_DMA_STATUS_CTRL_ENABLE); | ||
232 | } | ||
233 | EXPORT_SYMBOL_GPL(jz4740_dma_disable); | ||
234 | |||
235 | uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma) | ||
236 | { | ||
237 | uint32_t residue; | ||
238 | residue = jz4740_dma_read(JZ_REG_DMA_TRANSFER_COUNT(dma->id)); | ||
239 | return residue << dma->transfer_shift; | ||
240 | } | ||
241 | EXPORT_SYMBOL_GPL(jz4740_dma_get_residue); | ||
242 | |||
243 | static void jz4740_dma_chan_irq(struct jz4740_dma_chan *dma) | ||
244 | { | ||
245 | uint32_t status; | ||
246 | |||
247 | status = jz4740_dma_read(JZ_REG_DMA_STATUS_CTRL(dma->id)); | ||
248 | |||
249 | jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0, | ||
250 | JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_TRANSFER_DONE); | ||
251 | |||
252 | if (dma->complete_cb) | ||
253 | dma->complete_cb(dma, 0, dma->dev); | ||
254 | } | ||
255 | |||
256 | static irqreturn_t jz4740_dma_irq(int irq, void *dev_id) | ||
257 | { | ||
258 | uint32_t irq_status; | ||
259 | unsigned int i; | ||
260 | |||
261 | irq_status = readl(jz4740_dma_base + JZ_REG_DMA_IRQ); | ||
262 | |||
263 | for (i = 0; i < 6; ++i) { | ||
264 | if (irq_status & (1 << i)) | ||
265 | jz4740_dma_chan_irq(&jz4740_dma_channels[i]); | ||
266 | } | ||
267 | |||
268 | return IRQ_HANDLED; | ||
269 | } | ||
270 | |||
271 | static int jz4740_dma_init(void) | ||
272 | { | ||
273 | unsigned int ret; | ||
274 | |||
275 | jz4740_dma_base = ioremap(JZ4740_DMAC_BASE_ADDR, 0x400); | ||
276 | |||
277 | if (!jz4740_dma_base) | ||
278 | return -EBUSY; | ||
279 | |||
280 | spin_lock_init(&jz4740_dma_lock); | ||
281 | |||
282 | ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL); | ||
283 | |||
284 | if (ret) | ||
285 | printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret); | ||
286 | |||
287 | return ret; | ||
288 | } | ||
289 | arch_initcall(jz4740_dma_init); | ||
diff --git a/arch/mips/jz4740/gpio.c b/arch/mips/jz4740/gpio.c new file mode 100644 index 000000000000..38f60f35156c --- /dev/null +++ b/arch/mips/jz4740/gpio.c | |||
@@ -0,0 +1,604 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 platform GPIO support | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/init.h> | ||
19 | |||
20 | #include <linux/spinlock.h> | ||
21 | #include <linux/sysdev.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <linux/gpio.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/bitops.h> | ||
27 | |||
28 | #include <linux/debugfs.h> | ||
29 | #include <linux/seq_file.h> | ||
30 | |||
31 | #include <asm/mach-jz4740/base.h> | ||
32 | |||
33 | #define JZ4740_GPIO_BASE_A (32*0) | ||
34 | #define JZ4740_GPIO_BASE_B (32*1) | ||
35 | #define JZ4740_GPIO_BASE_C (32*2) | ||
36 | #define JZ4740_GPIO_BASE_D (32*3) | ||
37 | |||
38 | #define JZ4740_GPIO_NUM_A 32 | ||
39 | #define JZ4740_GPIO_NUM_B 32 | ||
40 | #define JZ4740_GPIO_NUM_C 31 | ||
41 | #define JZ4740_GPIO_NUM_D 32 | ||
42 | |||
43 | #define JZ4740_IRQ_GPIO_BASE_A (JZ4740_IRQ_GPIO(0) + JZ4740_GPIO_BASE_A) | ||
44 | #define JZ4740_IRQ_GPIO_BASE_B (JZ4740_IRQ_GPIO(0) + JZ4740_GPIO_BASE_B) | ||
45 | #define JZ4740_IRQ_GPIO_BASE_C (JZ4740_IRQ_GPIO(0) + JZ4740_GPIO_BASE_C) | ||
46 | #define JZ4740_IRQ_GPIO_BASE_D (JZ4740_IRQ_GPIO(0) + JZ4740_GPIO_BASE_D) | ||
47 | |||
48 | #define JZ_REG_GPIO_PIN 0x00 | ||
49 | #define JZ_REG_GPIO_DATA 0x10 | ||
50 | #define JZ_REG_GPIO_DATA_SET 0x14 | ||
51 | #define JZ_REG_GPIO_DATA_CLEAR 0x18 | ||
52 | #define JZ_REG_GPIO_MASK 0x20 | ||
53 | #define JZ_REG_GPIO_MASK_SET 0x24 | ||
54 | #define JZ_REG_GPIO_MASK_CLEAR 0x28 | ||
55 | #define JZ_REG_GPIO_PULL 0x30 | ||
56 | #define JZ_REG_GPIO_PULL_SET 0x34 | ||
57 | #define JZ_REG_GPIO_PULL_CLEAR 0x38 | ||
58 | #define JZ_REG_GPIO_FUNC 0x40 | ||
59 | #define JZ_REG_GPIO_FUNC_SET 0x44 | ||
60 | #define JZ_REG_GPIO_FUNC_CLEAR 0x48 | ||
61 | #define JZ_REG_GPIO_SELECT 0x50 | ||
62 | #define JZ_REG_GPIO_SELECT_SET 0x54 | ||
63 | #define JZ_REG_GPIO_SELECT_CLEAR 0x58 | ||
64 | #define JZ_REG_GPIO_DIRECTION 0x60 | ||
65 | #define JZ_REG_GPIO_DIRECTION_SET 0x64 | ||
66 | #define JZ_REG_GPIO_DIRECTION_CLEAR 0x68 | ||
67 | #define JZ_REG_GPIO_TRIGGER 0x70 | ||
68 | #define JZ_REG_GPIO_TRIGGER_SET 0x74 | ||
69 | #define JZ_REG_GPIO_TRIGGER_CLEAR 0x78 | ||
70 | #define JZ_REG_GPIO_FLAG 0x80 | ||
71 | #define JZ_REG_GPIO_FLAG_CLEAR 0x14 | ||
72 | |||
73 | #define GPIO_TO_BIT(gpio) BIT(gpio & 0x1f) | ||
74 | #define GPIO_TO_REG(gpio, reg) (gpio_to_jz_gpio_chip(gpio)->base + (reg)) | ||
75 | #define CHIP_TO_REG(chip, reg) (gpio_chip_to_jz_gpio_chip(chip)->base + (reg)) | ||
76 | |||
77 | struct jz_gpio_chip { | ||
78 | unsigned int irq; | ||
79 | unsigned int irq_base; | ||
80 | uint32_t wakeup; | ||
81 | uint32_t suspend_mask; | ||
82 | uint32_t edge_trigger_both; | ||
83 | |||
84 | void __iomem *base; | ||
85 | |||
86 | spinlock_t lock; | ||
87 | |||
88 | struct gpio_chip gpio_chip; | ||
89 | struct irq_chip irq_chip; | ||
90 | struct sys_device sysdev; | ||
91 | }; | ||
92 | |||
93 | static struct jz_gpio_chip jz4740_gpio_chips[]; | ||
94 | |||
95 | static inline struct jz_gpio_chip *gpio_to_jz_gpio_chip(unsigned int gpio) | ||
96 | { | ||
97 | return &jz4740_gpio_chips[gpio >> 5]; | ||
98 | } | ||
99 | |||
100 | static inline struct jz_gpio_chip *gpio_chip_to_jz_gpio_chip(struct gpio_chip *gpio_chip) | ||
101 | { | ||
102 | return container_of(gpio_chip, struct jz_gpio_chip, gpio_chip); | ||
103 | } | ||
104 | |||
105 | static inline struct jz_gpio_chip *irq_to_jz_gpio_chip(unsigned int irq) | ||
106 | { | ||
107 | return get_irq_chip_data(irq); | ||
108 | } | ||
109 | |||
110 | static inline void jz_gpio_write_bit(unsigned int gpio, unsigned int reg) | ||
111 | { | ||
112 | writel(GPIO_TO_BIT(gpio), GPIO_TO_REG(gpio, reg)); | ||
113 | } | ||
114 | |||
115 | int jz_gpio_set_function(int gpio, enum jz_gpio_function function) | ||
116 | { | ||
117 | if (function == JZ_GPIO_FUNC_NONE) { | ||
118 | jz_gpio_write_bit(gpio, JZ_REG_GPIO_FUNC_CLEAR); | ||
119 | jz_gpio_write_bit(gpio, JZ_REG_GPIO_SELECT_CLEAR); | ||
120 | jz_gpio_write_bit(gpio, JZ_REG_GPIO_TRIGGER_CLEAR); | ||
121 | } else { | ||
122 | jz_gpio_write_bit(gpio, JZ_REG_GPIO_FUNC_SET); | ||
123 | jz_gpio_write_bit(gpio, JZ_REG_GPIO_TRIGGER_CLEAR); | ||
124 | switch (function) { | ||
125 | case JZ_GPIO_FUNC1: | ||
126 | jz_gpio_write_bit(gpio, JZ_REG_GPIO_SELECT_CLEAR); | ||
127 | break; | ||
128 | case JZ_GPIO_FUNC3: | ||
129 | jz_gpio_write_bit(gpio, JZ_REG_GPIO_TRIGGER_SET); | ||
130 | case JZ_GPIO_FUNC2: /* Falltrough */ | ||
131 | jz_gpio_write_bit(gpio, JZ_REG_GPIO_SELECT_SET); | ||
132 | break; | ||
133 | default: | ||
134 | BUG(); | ||
135 | break; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | EXPORT_SYMBOL_GPL(jz_gpio_set_function); | ||
142 | |||
143 | int jz_gpio_bulk_request(const struct jz_gpio_bulk_request *request, size_t num) | ||
144 | { | ||
145 | size_t i; | ||
146 | int ret; | ||
147 | |||
148 | for (i = 0; i < num; ++i, ++request) { | ||
149 | ret = gpio_request(request->gpio, request->name); | ||
150 | if (ret) | ||
151 | goto err; | ||
152 | jz_gpio_set_function(request->gpio, request->function); | ||
153 | } | ||
154 | |||
155 | return 0; | ||
156 | |||
157 | err: | ||
158 | for (--request; i > 0; --i, --request) { | ||
159 | gpio_free(request->gpio); | ||
160 | jz_gpio_set_function(request->gpio, JZ_GPIO_FUNC_NONE); | ||
161 | } | ||
162 | |||
163 | return ret; | ||
164 | } | ||
165 | EXPORT_SYMBOL_GPL(jz_gpio_bulk_request); | ||
166 | |||
167 | void jz_gpio_bulk_free(const struct jz_gpio_bulk_request *request, size_t num) | ||
168 | { | ||
169 | size_t i; | ||
170 | |||
171 | for (i = 0; i < num; ++i, ++request) { | ||
172 | gpio_free(request->gpio); | ||
173 | jz_gpio_set_function(request->gpio, JZ_GPIO_FUNC_NONE); | ||
174 | } | ||
175 | |||
176 | } | ||
177 | EXPORT_SYMBOL_GPL(jz_gpio_bulk_free); | ||
178 | |||
179 | void jz_gpio_bulk_suspend(const struct jz_gpio_bulk_request *request, size_t num) | ||
180 | { | ||
181 | size_t i; | ||
182 | |||
183 | for (i = 0; i < num; ++i, ++request) { | ||
184 | jz_gpio_set_function(request->gpio, JZ_GPIO_FUNC_NONE); | ||
185 | jz_gpio_write_bit(request->gpio, JZ_REG_GPIO_DIRECTION_CLEAR); | ||
186 | jz_gpio_write_bit(request->gpio, JZ_REG_GPIO_PULL_SET); | ||
187 | } | ||
188 | } | ||
189 | EXPORT_SYMBOL_GPL(jz_gpio_bulk_suspend); | ||
190 | |||
191 | void jz_gpio_bulk_resume(const struct jz_gpio_bulk_request *request, size_t num) | ||
192 | { | ||
193 | size_t i; | ||
194 | |||
195 | for (i = 0; i < num; ++i, ++request) | ||
196 | jz_gpio_set_function(request->gpio, request->function); | ||
197 | } | ||
198 | EXPORT_SYMBOL_GPL(jz_gpio_bulk_resume); | ||
199 | |||
200 | void jz_gpio_enable_pullup(unsigned gpio) | ||
201 | { | ||
202 | jz_gpio_write_bit(gpio, JZ_REG_GPIO_PULL_CLEAR); | ||
203 | } | ||
204 | EXPORT_SYMBOL_GPL(jz_gpio_enable_pullup); | ||
205 | |||
206 | void jz_gpio_disable_pullup(unsigned gpio) | ||
207 | { | ||
208 | jz_gpio_write_bit(gpio, JZ_REG_GPIO_PULL_SET); | ||
209 | } | ||
210 | EXPORT_SYMBOL_GPL(jz_gpio_disable_pullup); | ||
211 | |||
212 | static int jz_gpio_get_value(struct gpio_chip *chip, unsigned gpio) | ||
213 | { | ||
214 | return !!(readl(CHIP_TO_REG(chip, JZ_REG_GPIO_PIN)) & BIT(gpio)); | ||
215 | } | ||
216 | |||
217 | static void jz_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value) | ||
218 | { | ||
219 | uint32_t __iomem *reg = CHIP_TO_REG(chip, JZ_REG_GPIO_DATA_SET); | ||
220 | reg += !value; | ||
221 | writel(BIT(gpio), reg); | ||
222 | } | ||
223 | |||
224 | static int jz_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, | ||
225 | int value) | ||
226 | { | ||
227 | writel(BIT(gpio), CHIP_TO_REG(chip, JZ_REG_GPIO_DIRECTION_SET)); | ||
228 | jz_gpio_set_value(chip, gpio, value); | ||
229 | |||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static int jz_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) | ||
234 | { | ||
235 | writel(BIT(gpio), CHIP_TO_REG(chip, JZ_REG_GPIO_DIRECTION_CLEAR)); | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | int jz_gpio_port_direction_input(int port, uint32_t mask) | ||
241 | { | ||
242 | writel(mask, GPIO_TO_REG(port, JZ_REG_GPIO_DIRECTION_CLEAR)); | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | EXPORT_SYMBOL(jz_gpio_port_direction_input); | ||
247 | |||
248 | int jz_gpio_port_direction_output(int port, uint32_t mask) | ||
249 | { | ||
250 | writel(mask, GPIO_TO_REG(port, JZ_REG_GPIO_DIRECTION_SET)); | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | EXPORT_SYMBOL(jz_gpio_port_direction_output); | ||
255 | |||
256 | void jz_gpio_port_set_value(int port, uint32_t value, uint32_t mask) | ||
257 | { | ||
258 | writel(~value & mask, GPIO_TO_REG(port, JZ_REG_GPIO_DATA_CLEAR)); | ||
259 | writel(value & mask, GPIO_TO_REG(port, JZ_REG_GPIO_DATA_SET)); | ||
260 | } | ||
261 | EXPORT_SYMBOL(jz_gpio_port_set_value); | ||
262 | |||
263 | uint32_t jz_gpio_port_get_value(int port, uint32_t mask) | ||
264 | { | ||
265 | uint32_t value = readl(GPIO_TO_REG(port, JZ_REG_GPIO_PIN)); | ||
266 | |||
267 | return value & mask; | ||
268 | } | ||
269 | EXPORT_SYMBOL(jz_gpio_port_get_value); | ||
270 | |||
271 | int gpio_to_irq(unsigned gpio) | ||
272 | { | ||
273 | return JZ4740_IRQ_GPIO(0) + gpio; | ||
274 | } | ||
275 | EXPORT_SYMBOL_GPL(gpio_to_irq); | ||
276 | |||
277 | int irq_to_gpio(unsigned irq) | ||
278 | { | ||
279 | return irq - JZ4740_IRQ_GPIO(0); | ||
280 | } | ||
281 | EXPORT_SYMBOL_GPL(irq_to_gpio); | ||
282 | |||
283 | #define IRQ_TO_BIT(irq) BIT(irq_to_gpio(irq) & 0x1f) | ||
284 | |||
285 | static void jz_gpio_check_trigger_both(struct jz_gpio_chip *chip, unsigned int irq) | ||
286 | { | ||
287 | uint32_t value; | ||
288 | void __iomem *reg; | ||
289 | uint32_t mask = IRQ_TO_BIT(irq); | ||
290 | |||
291 | if (!(chip->edge_trigger_both & mask)) | ||
292 | return; | ||
293 | |||
294 | reg = chip->base; | ||
295 | |||
296 | value = readl(chip->base + JZ_REG_GPIO_PIN); | ||
297 | if (value & mask) | ||
298 | reg += JZ_REG_GPIO_DIRECTION_CLEAR; | ||
299 | else | ||
300 | reg += JZ_REG_GPIO_DIRECTION_SET; | ||
301 | |||
302 | writel(mask, reg); | ||
303 | } | ||
304 | |||
305 | static void jz_gpio_irq_demux_handler(unsigned int irq, struct irq_desc *desc) | ||
306 | { | ||
307 | uint32_t flag; | ||
308 | unsigned int gpio_irq; | ||
309 | unsigned int gpio_bank; | ||
310 | struct jz_gpio_chip *chip = get_irq_desc_data(desc); | ||
311 | |||
312 | gpio_bank = JZ4740_IRQ_GPIO0 - irq; | ||
313 | |||
314 | flag = readl(chip->base + JZ_REG_GPIO_FLAG); | ||
315 | |||
316 | if (!flag) | ||
317 | return; | ||
318 | |||
319 | gpio_irq = __fls(flag); | ||
320 | |||
321 | jz_gpio_check_trigger_both(chip, irq); | ||
322 | |||
323 | gpio_irq += (gpio_bank << 5) + JZ4740_IRQ_GPIO(0); | ||
324 | |||
325 | generic_handle_irq(gpio_irq); | ||
326 | }; | ||
327 | |||
328 | static inline void jz_gpio_set_irq_bit(unsigned int irq, unsigned int reg) | ||
329 | { | ||
330 | struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(irq); | ||
331 | writel(IRQ_TO_BIT(irq), chip->base + reg); | ||
332 | } | ||
333 | |||
334 | static void jz_gpio_irq_mask(unsigned int irq) | ||
335 | { | ||
336 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_MASK_SET); | ||
337 | }; | ||
338 | |||
339 | static void jz_gpio_irq_unmask(unsigned int irq) | ||
340 | { | ||
341 | struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(irq); | ||
342 | |||
343 | jz_gpio_check_trigger_both(chip, irq); | ||
344 | |||
345 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_MASK_CLEAR); | ||
346 | }; | ||
347 | |||
348 | /* TODO: Check if function is gpio */ | ||
349 | static unsigned int jz_gpio_irq_startup(unsigned int irq) | ||
350 | { | ||
351 | struct irq_desc *desc = irq_to_desc(irq); | ||
352 | |||
353 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_SELECT_SET); | ||
354 | |||
355 | desc->status &= ~IRQ_MASKED; | ||
356 | jz_gpio_irq_unmask(irq); | ||
357 | |||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | static void jz_gpio_irq_shutdown(unsigned int irq) | ||
362 | { | ||
363 | struct irq_desc *desc = irq_to_desc(irq); | ||
364 | |||
365 | jz_gpio_irq_mask(irq); | ||
366 | desc->status |= IRQ_MASKED; | ||
367 | |||
368 | /* Set direction to input */ | ||
369 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_DIRECTION_CLEAR); | ||
370 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_SELECT_CLEAR); | ||
371 | } | ||
372 | |||
373 | static void jz_gpio_irq_ack(unsigned int irq) | ||
374 | { | ||
375 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_FLAG_CLEAR); | ||
376 | }; | ||
377 | |||
378 | static int jz_gpio_irq_set_type(unsigned int irq, unsigned int flow_type) | ||
379 | { | ||
380 | struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(irq); | ||
381 | struct irq_desc *desc = irq_to_desc(irq); | ||
382 | |||
383 | jz_gpio_irq_mask(irq); | ||
384 | |||
385 | if (flow_type == IRQ_TYPE_EDGE_BOTH) { | ||
386 | uint32_t value = readl(chip->base + JZ_REG_GPIO_PIN); | ||
387 | if (value & IRQ_TO_BIT(irq)) | ||
388 | flow_type = IRQ_TYPE_EDGE_FALLING; | ||
389 | else | ||
390 | flow_type = IRQ_TYPE_EDGE_RISING; | ||
391 | chip->edge_trigger_both |= IRQ_TO_BIT(irq); | ||
392 | } else { | ||
393 | chip->edge_trigger_both &= ~IRQ_TO_BIT(irq); | ||
394 | } | ||
395 | |||
396 | switch (flow_type) { | ||
397 | case IRQ_TYPE_EDGE_RISING: | ||
398 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_DIRECTION_SET); | ||
399 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_TRIGGER_SET); | ||
400 | break; | ||
401 | case IRQ_TYPE_EDGE_FALLING: | ||
402 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_DIRECTION_CLEAR); | ||
403 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_TRIGGER_SET); | ||
404 | break; | ||
405 | case IRQ_TYPE_LEVEL_HIGH: | ||
406 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_DIRECTION_SET); | ||
407 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_TRIGGER_CLEAR); | ||
408 | break; | ||
409 | case IRQ_TYPE_LEVEL_LOW: | ||
410 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_DIRECTION_CLEAR); | ||
411 | jz_gpio_set_irq_bit(irq, JZ_REG_GPIO_TRIGGER_CLEAR); | ||
412 | break; | ||
413 | default: | ||
414 | return -EINVAL; | ||
415 | } | ||
416 | |||
417 | if (!(desc->status & IRQ_MASKED)) | ||
418 | jz_gpio_irq_unmask(irq); | ||
419 | |||
420 | return 0; | ||
421 | } | ||
422 | |||
423 | static int jz_gpio_irq_set_wake(unsigned int irq, unsigned int on) | ||
424 | { | ||
425 | struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(irq); | ||
426 | spin_lock(&chip->lock); | ||
427 | if (on) | ||
428 | chip->wakeup |= IRQ_TO_BIT(irq); | ||
429 | else | ||
430 | chip->wakeup &= ~IRQ_TO_BIT(irq); | ||
431 | spin_unlock(&chip->lock); | ||
432 | |||
433 | set_irq_wake(chip->irq, on); | ||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | /* | ||
438 | * This lock class tells lockdep that GPIO irqs are in a different | ||
439 | * category than their parents, so it won't report false recursion. | ||
440 | */ | ||
441 | static struct lock_class_key gpio_lock_class; | ||
442 | |||
443 | #define JZ4740_GPIO_CHIP(_bank) { \ | ||
444 | .irq_base = JZ4740_IRQ_GPIO_BASE_ ## _bank, \ | ||
445 | .gpio_chip = { \ | ||
446 | .label = "Bank " # _bank, \ | ||
447 | .owner = THIS_MODULE, \ | ||
448 | .set = jz_gpio_set_value, \ | ||
449 | .get = jz_gpio_get_value, \ | ||
450 | .direction_output = jz_gpio_direction_output, \ | ||
451 | .direction_input = jz_gpio_direction_input, \ | ||
452 | .base = JZ4740_GPIO_BASE_ ## _bank, \ | ||
453 | .ngpio = JZ4740_GPIO_NUM_ ## _bank, \ | ||
454 | }, \ | ||
455 | .irq_chip = { \ | ||
456 | .name = "GPIO Bank " # _bank, \ | ||
457 | .mask = jz_gpio_irq_mask, \ | ||
458 | .unmask = jz_gpio_irq_unmask, \ | ||
459 | .ack = jz_gpio_irq_ack, \ | ||
460 | .startup = jz_gpio_irq_startup, \ | ||
461 | .shutdown = jz_gpio_irq_shutdown, \ | ||
462 | .set_type = jz_gpio_irq_set_type, \ | ||
463 | .set_wake = jz_gpio_irq_set_wake, \ | ||
464 | }, \ | ||
465 | } | ||
466 | |||
467 | static struct jz_gpio_chip jz4740_gpio_chips[] = { | ||
468 | JZ4740_GPIO_CHIP(A), | ||
469 | JZ4740_GPIO_CHIP(B), | ||
470 | JZ4740_GPIO_CHIP(C), | ||
471 | JZ4740_GPIO_CHIP(D), | ||
472 | }; | ||
473 | |||
474 | static inline struct jz_gpio_chip *sysdev_to_chip(struct sys_device *dev) | ||
475 | { | ||
476 | return container_of(dev, struct jz_gpio_chip, sysdev); | ||
477 | } | ||
478 | |||
479 | static int jz4740_gpio_suspend(struct sys_device *dev, pm_message_t state) | ||
480 | { | ||
481 | struct jz_gpio_chip *chip = sysdev_to_chip(dev); | ||
482 | |||
483 | chip->suspend_mask = readl(chip->base + JZ_REG_GPIO_MASK); | ||
484 | writel(~(chip->wakeup), chip->base + JZ_REG_GPIO_MASK_SET); | ||
485 | writel(chip->wakeup, chip->base + JZ_REG_GPIO_MASK_CLEAR); | ||
486 | |||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | static int jz4740_gpio_resume(struct sys_device *dev) | ||
491 | { | ||
492 | struct jz_gpio_chip *chip = sysdev_to_chip(dev); | ||
493 | uint32_t mask = chip->suspend_mask; | ||
494 | |||
495 | writel(~mask, chip->base + JZ_REG_GPIO_MASK_CLEAR); | ||
496 | writel(mask, chip->base + JZ_REG_GPIO_MASK_SET); | ||
497 | |||
498 | return 0; | ||
499 | } | ||
500 | |||
501 | static struct sysdev_class jz4740_gpio_sysdev_class = { | ||
502 | .name = "gpio", | ||
503 | .suspend = jz4740_gpio_suspend, | ||
504 | .resume = jz4740_gpio_resume, | ||
505 | }; | ||
506 | |||
507 | static int jz4740_gpio_chip_init(struct jz_gpio_chip *chip, unsigned int id) | ||
508 | { | ||
509 | int ret, irq; | ||
510 | |||
511 | chip->sysdev.id = id; | ||
512 | chip->sysdev.cls = &jz4740_gpio_sysdev_class; | ||
513 | ret = sysdev_register(&chip->sysdev); | ||
514 | |||
515 | if (ret) | ||
516 | return ret; | ||
517 | |||
518 | spin_lock_init(&chip->lock); | ||
519 | |||
520 | chip->base = ioremap(JZ4740_GPIO_BASE_ADDR + (id * 0x100), 0x100); | ||
521 | |||
522 | gpiochip_add(&chip->gpio_chip); | ||
523 | |||
524 | chip->irq = JZ4740_IRQ_INTC_GPIO(id); | ||
525 | set_irq_data(chip->irq, chip); | ||
526 | set_irq_chained_handler(chip->irq, jz_gpio_irq_demux_handler); | ||
527 | |||
528 | for (irq = chip->irq_base; irq < chip->irq_base + chip->gpio_chip.ngpio; ++irq) { | ||
529 | lockdep_set_class(&irq_desc[irq].lock, &gpio_lock_class); | ||
530 | set_irq_chip_data(irq, chip); | ||
531 | set_irq_chip_and_handler(irq, &chip->irq_chip, handle_level_irq); | ||
532 | } | ||
533 | |||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static int __init jz4740_gpio_init(void) | ||
538 | { | ||
539 | unsigned int i; | ||
540 | int ret; | ||
541 | |||
542 | ret = sysdev_class_register(&jz4740_gpio_sysdev_class); | ||
543 | if (ret) | ||
544 | return ret; | ||
545 | |||
546 | for (i = 0; i < ARRAY_SIZE(jz4740_gpio_chips); ++i) | ||
547 | jz4740_gpio_chip_init(&jz4740_gpio_chips[i], i); | ||
548 | |||
549 | printk(KERN_INFO "JZ4740 GPIO initalized\n"); | ||
550 | |||
551 | return 0; | ||
552 | } | ||
553 | arch_initcall(jz4740_gpio_init); | ||
554 | |||
555 | #ifdef CONFIG_DEBUG_FS | ||
556 | |||
557 | static inline void gpio_seq_reg(struct seq_file *s, struct jz_gpio_chip *chip, | ||
558 | const char *name, unsigned int reg) | ||
559 | { | ||
560 | seq_printf(s, "\t%s: %08x\n", name, readl(chip->base + reg)); | ||
561 | } | ||
562 | |||
563 | static int gpio_regs_show(struct seq_file *s, void *unused) | ||
564 | { | ||
565 | struct jz_gpio_chip *chip = jz4740_gpio_chips; | ||
566 | int i; | ||
567 | |||
568 | for (i = 0; i < ARRAY_SIZE(jz4740_gpio_chips); ++i, ++chip) { | ||
569 | seq_printf(s, "==GPIO %d==\n", i); | ||
570 | gpio_seq_reg(s, chip, "Pin", JZ_REG_GPIO_PIN); | ||
571 | gpio_seq_reg(s, chip, "Data", JZ_REG_GPIO_DATA); | ||
572 | gpio_seq_reg(s, chip, "Mask", JZ_REG_GPIO_MASK); | ||
573 | gpio_seq_reg(s, chip, "Pull", JZ_REG_GPIO_PULL); | ||
574 | gpio_seq_reg(s, chip, "Func", JZ_REG_GPIO_FUNC); | ||
575 | gpio_seq_reg(s, chip, "Select", JZ_REG_GPIO_SELECT); | ||
576 | gpio_seq_reg(s, chip, "Direction", JZ_REG_GPIO_DIRECTION); | ||
577 | gpio_seq_reg(s, chip, "Trigger", JZ_REG_GPIO_TRIGGER); | ||
578 | gpio_seq_reg(s, chip, "Flag", JZ_REG_GPIO_FLAG); | ||
579 | } | ||
580 | |||
581 | return 0; | ||
582 | } | ||
583 | |||
584 | static int gpio_regs_open(struct inode *inode, struct file *file) | ||
585 | { | ||
586 | return single_open(file, gpio_regs_show, NULL); | ||
587 | } | ||
588 | |||
589 | static const struct file_operations gpio_regs_operations = { | ||
590 | .open = gpio_regs_open, | ||
591 | .read = seq_read, | ||
592 | .llseek = seq_lseek, | ||
593 | .release = single_release, | ||
594 | }; | ||
595 | |||
596 | static int __init gpio_debugfs_init(void) | ||
597 | { | ||
598 | (void) debugfs_create_file("jz_regs_gpio", S_IFREG | S_IRUGO, | ||
599 | NULL, NULL, &gpio_regs_operations); | ||
600 | return 0; | ||
601 | } | ||
602 | subsys_initcall(gpio_debugfs_init); | ||
603 | |||
604 | #endif | ||
diff --git a/arch/mips/jz4740/irq.c b/arch/mips/jz4740/irq.c new file mode 100644 index 000000000000..7d33ff83580f --- /dev/null +++ b/arch/mips/jz4740/irq.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 platform IRQ support | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/errno.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/ioport.h> | ||
21 | #include <linux/timex.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/delay.h> | ||
24 | |||
25 | #include <linux/debugfs.h> | ||
26 | #include <linux/seq_file.h> | ||
27 | |||
28 | #include <asm/io.h> | ||
29 | #include <asm/mipsregs.h> | ||
30 | #include <asm/irq_cpu.h> | ||
31 | |||
32 | #include <asm/mach-jz4740/base.h> | ||
33 | |||
34 | static void __iomem *jz_intc_base; | ||
35 | static uint32_t jz_intc_wakeup; | ||
36 | static uint32_t jz_intc_saved; | ||
37 | |||
38 | #define JZ_REG_INTC_STATUS 0x00 | ||
39 | #define JZ_REG_INTC_MASK 0x04 | ||
40 | #define JZ_REG_INTC_SET_MASK 0x08 | ||
41 | #define JZ_REG_INTC_CLEAR_MASK 0x0c | ||
42 | #define JZ_REG_INTC_PENDING 0x10 | ||
43 | |||
44 | #define IRQ_BIT(x) BIT((x) - JZ4740_IRQ_BASE) | ||
45 | |||
46 | static void intc_irq_unmask(unsigned int irq) | ||
47 | { | ||
48 | writel(IRQ_BIT(irq), jz_intc_base + JZ_REG_INTC_CLEAR_MASK); | ||
49 | } | ||
50 | |||
51 | static void intc_irq_mask(unsigned int irq) | ||
52 | { | ||
53 | writel(IRQ_BIT(irq), jz_intc_base + JZ_REG_INTC_SET_MASK); | ||
54 | } | ||
55 | |||
56 | static int intc_irq_set_wake(unsigned int irq, unsigned int on) | ||
57 | { | ||
58 | if (on) | ||
59 | jz_intc_wakeup |= IRQ_BIT(irq); | ||
60 | else | ||
61 | jz_intc_wakeup &= ~IRQ_BIT(irq); | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static struct irq_chip intc_irq_type = { | ||
67 | .name = "INTC", | ||
68 | .mask = intc_irq_mask, | ||
69 | .mask_ack = intc_irq_mask, | ||
70 | .unmask = intc_irq_unmask, | ||
71 | .set_wake = intc_irq_set_wake, | ||
72 | }; | ||
73 | |||
74 | static irqreturn_t jz4740_cascade(int irq, void *data) | ||
75 | { | ||
76 | uint32_t irq_reg; | ||
77 | |||
78 | irq_reg = readl(jz_intc_base + JZ_REG_INTC_PENDING); | ||
79 | |||
80 | if (irq_reg) | ||
81 | generic_handle_irq(__fls(irq_reg) + JZ4740_IRQ_BASE); | ||
82 | |||
83 | return IRQ_HANDLED; | ||
84 | } | ||
85 | |||
86 | static struct irqaction jz4740_cascade_action = { | ||
87 | .handler = jz4740_cascade, | ||
88 | .name = "JZ4740 cascade interrupt", | ||
89 | }; | ||
90 | |||
91 | void __init arch_init_irq(void) | ||
92 | { | ||
93 | int i; | ||
94 | mips_cpu_irq_init(); | ||
95 | |||
96 | jz_intc_base = ioremap(JZ4740_INTC_BASE_ADDR, 0x14); | ||
97 | |||
98 | for (i = JZ4740_IRQ_BASE; i < JZ4740_IRQ_BASE + 32; i++) { | ||
99 | intc_irq_mask(i); | ||
100 | set_irq_chip_and_handler(i, &intc_irq_type, handle_level_irq); | ||
101 | } | ||
102 | |||
103 | setup_irq(2, &jz4740_cascade_action); | ||
104 | } | ||
105 | |||
106 | asmlinkage void plat_irq_dispatch(void) | ||
107 | { | ||
108 | unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM; | ||
109 | if (pending & STATUSF_IP2) | ||
110 | do_IRQ(2); | ||
111 | else if (pending & STATUSF_IP3) | ||
112 | do_IRQ(3); | ||
113 | else | ||
114 | spurious_interrupt(); | ||
115 | } | ||
116 | |||
117 | void jz4740_intc_suspend(void) | ||
118 | { | ||
119 | jz_intc_saved = readl(jz_intc_base + JZ_REG_INTC_MASK); | ||
120 | writel(~jz_intc_wakeup, jz_intc_base + JZ_REG_INTC_SET_MASK); | ||
121 | writel(jz_intc_wakeup, jz_intc_base + JZ_REG_INTC_CLEAR_MASK); | ||
122 | } | ||
123 | |||
124 | void jz4740_intc_resume(void) | ||
125 | { | ||
126 | writel(~jz_intc_saved, jz_intc_base + JZ_REG_INTC_CLEAR_MASK); | ||
127 | writel(jz_intc_saved, jz_intc_base + JZ_REG_INTC_SET_MASK); | ||
128 | } | ||
129 | |||
130 | #ifdef CONFIG_DEBUG_FS | ||
131 | |||
132 | static inline void intc_seq_reg(struct seq_file *s, const char *name, | ||
133 | unsigned int reg) | ||
134 | { | ||
135 | seq_printf(s, "%s:\t\t%08x\n", name, readl(jz_intc_base + reg)); | ||
136 | } | ||
137 | |||
138 | static int intc_regs_show(struct seq_file *s, void *unused) | ||
139 | { | ||
140 | intc_seq_reg(s, "Status", JZ_REG_INTC_STATUS); | ||
141 | intc_seq_reg(s, "Mask", JZ_REG_INTC_MASK); | ||
142 | intc_seq_reg(s, "Pending", JZ_REG_INTC_PENDING); | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | static int intc_regs_open(struct inode *inode, struct file *file) | ||
148 | { | ||
149 | return single_open(file, intc_regs_show, NULL); | ||
150 | } | ||
151 | |||
152 | static const struct file_operations intc_regs_operations = { | ||
153 | .open = intc_regs_open, | ||
154 | .read = seq_read, | ||
155 | .llseek = seq_lseek, | ||
156 | .release = single_release, | ||
157 | }; | ||
158 | |||
159 | static int __init intc_debugfs_init(void) | ||
160 | { | ||
161 | (void) debugfs_create_file("jz_regs_intc", S_IFREG | S_IRUGO, | ||
162 | NULL, NULL, &intc_regs_operations); | ||
163 | return 0; | ||
164 | } | ||
165 | subsys_initcall(intc_debugfs_init); | ||
166 | |||
167 | #endif | ||
diff --git a/arch/mips/jz4740/irq.h b/arch/mips/jz4740/irq.h new file mode 100644 index 000000000000..56b5eadd1fa2 --- /dev/null +++ b/arch/mips/jz4740/irq.h | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the | ||
6 | * Free Software Foundation; either version 2 of the License, or (at your | ||
7 | * option) any later version. | ||
8 | * | ||
9 | * You should have received a copy of the GNU General Public License along | ||
10 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
11 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #ifndef __MIPS_JZ4740_IRQ_H__ | ||
16 | #define __MIPS_JZ4740_IRQ_H__ | ||
17 | |||
18 | extern void jz4740_intc_suspend(void); | ||
19 | extern void jz4740_intc_resume(void); | ||
20 | |||
21 | #endif | ||
diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c new file mode 100644 index 000000000000..95bc2b5b14f1 --- /dev/null +++ b/arch/mips/jz4740/platform.c | |||
@@ -0,0 +1,291 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 platform devices | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/device.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/resource.h> | ||
21 | |||
22 | #include <linux/dma-mapping.h> | ||
23 | |||
24 | #include <asm/mach-jz4740/platform.h> | ||
25 | #include <asm/mach-jz4740/base.h> | ||
26 | #include <asm/mach-jz4740/irq.h> | ||
27 | |||
28 | #include <linux/serial_core.h> | ||
29 | #include <linux/serial_8250.h> | ||
30 | |||
31 | #include "serial.h" | ||
32 | #include "clock.h" | ||
33 | |||
34 | /* OHCI controller */ | ||
35 | static struct resource jz4740_usb_ohci_resources[] = { | ||
36 | { | ||
37 | .start = JZ4740_UHC_BASE_ADDR, | ||
38 | .end = JZ4740_UHC_BASE_ADDR + 0x1000 - 1, | ||
39 | .flags = IORESOURCE_MEM, | ||
40 | }, | ||
41 | { | ||
42 | .start = JZ4740_IRQ_UHC, | ||
43 | .end = JZ4740_IRQ_UHC, | ||
44 | .flags = IORESOURCE_IRQ, | ||
45 | }, | ||
46 | }; | ||
47 | |||
48 | struct platform_device jz4740_usb_ohci_device = { | ||
49 | .name = "jz4740-ohci", | ||
50 | .id = -1, | ||
51 | .dev = { | ||
52 | .dma_mask = &jz4740_usb_ohci_device.dev.coherent_dma_mask, | ||
53 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
54 | }, | ||
55 | .num_resources = ARRAY_SIZE(jz4740_usb_ohci_resources), | ||
56 | .resource = jz4740_usb_ohci_resources, | ||
57 | }; | ||
58 | |||
59 | /* UDC (USB gadget controller) */ | ||
60 | static struct resource jz4740_usb_gdt_resources[] = { | ||
61 | { | ||
62 | .start = JZ4740_UDC_BASE_ADDR, | ||
63 | .end = JZ4740_UDC_BASE_ADDR + 0x1000 - 1, | ||
64 | .flags = IORESOURCE_MEM, | ||
65 | }, | ||
66 | { | ||
67 | .start = JZ4740_IRQ_UDC, | ||
68 | .end = JZ4740_IRQ_UDC, | ||
69 | .flags = IORESOURCE_IRQ, | ||
70 | }, | ||
71 | }; | ||
72 | |||
73 | struct platform_device jz4740_udc_device = { | ||
74 | .name = "jz-udc", | ||
75 | .id = -1, | ||
76 | .dev = { | ||
77 | .dma_mask = &jz4740_udc_device.dev.coherent_dma_mask, | ||
78 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
79 | }, | ||
80 | .num_resources = ARRAY_SIZE(jz4740_usb_gdt_resources), | ||
81 | .resource = jz4740_usb_gdt_resources, | ||
82 | }; | ||
83 | |||
84 | /* MMC/SD controller */ | ||
85 | static struct resource jz4740_mmc_resources[] = { | ||
86 | { | ||
87 | .start = JZ4740_MSC_BASE_ADDR, | ||
88 | .end = JZ4740_MSC_BASE_ADDR + 0x1000 - 1, | ||
89 | .flags = IORESOURCE_MEM, | ||
90 | }, | ||
91 | { | ||
92 | .start = JZ4740_IRQ_MSC, | ||
93 | .end = JZ4740_IRQ_MSC, | ||
94 | .flags = IORESOURCE_IRQ, | ||
95 | } | ||
96 | }; | ||
97 | |||
98 | struct platform_device jz4740_mmc_device = { | ||
99 | .name = "jz4740-mmc", | ||
100 | .id = 0, | ||
101 | .dev = { | ||
102 | .dma_mask = &jz4740_mmc_device.dev.coherent_dma_mask, | ||
103 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
104 | }, | ||
105 | .num_resources = ARRAY_SIZE(jz4740_mmc_resources), | ||
106 | .resource = jz4740_mmc_resources, | ||
107 | }; | ||
108 | |||
109 | /* RTC controller */ | ||
110 | static struct resource jz4740_rtc_resources[] = { | ||
111 | { | ||
112 | .start = JZ4740_RTC_BASE_ADDR, | ||
113 | .end = JZ4740_RTC_BASE_ADDR + 0x38 - 1, | ||
114 | .flags = IORESOURCE_MEM, | ||
115 | }, | ||
116 | { | ||
117 | .start = JZ4740_IRQ_RTC, | ||
118 | .end = JZ4740_IRQ_RTC, | ||
119 | .flags = IORESOURCE_IRQ, | ||
120 | }, | ||
121 | }; | ||
122 | |||
123 | struct platform_device jz4740_rtc_device = { | ||
124 | .name = "jz4740-rtc", | ||
125 | .id = -1, | ||
126 | .num_resources = ARRAY_SIZE(jz4740_rtc_resources), | ||
127 | .resource = jz4740_rtc_resources, | ||
128 | }; | ||
129 | |||
130 | /* I2C controller */ | ||
131 | static struct resource jz4740_i2c_resources[] = { | ||
132 | { | ||
133 | .start = JZ4740_I2C_BASE_ADDR, | ||
134 | .end = JZ4740_I2C_BASE_ADDR + 0x1000 - 1, | ||
135 | .flags = IORESOURCE_MEM, | ||
136 | }, | ||
137 | { | ||
138 | .start = JZ4740_IRQ_I2C, | ||
139 | .end = JZ4740_IRQ_I2C, | ||
140 | .flags = IORESOURCE_IRQ, | ||
141 | } | ||
142 | }; | ||
143 | |||
144 | struct platform_device jz4740_i2c_device = { | ||
145 | .name = "jz4740-i2c", | ||
146 | .id = 0, | ||
147 | .num_resources = ARRAY_SIZE(jz4740_i2c_resources), | ||
148 | .resource = jz4740_i2c_resources, | ||
149 | }; | ||
150 | |||
151 | /* NAND controller */ | ||
152 | static struct resource jz4740_nand_resources[] = { | ||
153 | { | ||
154 | .name = "mmio", | ||
155 | .start = JZ4740_EMC_BASE_ADDR, | ||
156 | .end = JZ4740_EMC_BASE_ADDR + 0x1000 - 1, | ||
157 | .flags = IORESOURCE_MEM, | ||
158 | }, | ||
159 | { | ||
160 | .name = "bank", | ||
161 | .start = 0x18000000, | ||
162 | .end = 0x180C0000 - 1, | ||
163 | .flags = IORESOURCE_MEM, | ||
164 | }, | ||
165 | }; | ||
166 | |||
167 | struct platform_device jz4740_nand_device = { | ||
168 | .name = "jz4740-nand", | ||
169 | .num_resources = ARRAY_SIZE(jz4740_nand_resources), | ||
170 | .resource = jz4740_nand_resources, | ||
171 | }; | ||
172 | |||
173 | /* LCD controller */ | ||
174 | static struct resource jz4740_framebuffer_resources[] = { | ||
175 | { | ||
176 | .start = JZ4740_LCD_BASE_ADDR, | ||
177 | .end = JZ4740_LCD_BASE_ADDR + 0x1000 - 1, | ||
178 | .flags = IORESOURCE_MEM, | ||
179 | }, | ||
180 | }; | ||
181 | |||
182 | struct platform_device jz4740_framebuffer_device = { | ||
183 | .name = "jz4740-fb", | ||
184 | .id = -1, | ||
185 | .num_resources = ARRAY_SIZE(jz4740_framebuffer_resources), | ||
186 | .resource = jz4740_framebuffer_resources, | ||
187 | .dev = { | ||
188 | .dma_mask = &jz4740_framebuffer_device.dev.coherent_dma_mask, | ||
189 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
190 | }, | ||
191 | }; | ||
192 | |||
193 | /* I2S controller */ | ||
194 | static struct resource jz4740_i2s_resources[] = { | ||
195 | { | ||
196 | .start = JZ4740_AIC_BASE_ADDR, | ||
197 | .end = JZ4740_AIC_BASE_ADDR + 0x38 - 1, | ||
198 | .flags = IORESOURCE_MEM, | ||
199 | }, | ||
200 | }; | ||
201 | |||
202 | struct platform_device jz4740_i2s_device = { | ||
203 | .name = "jz4740-i2s", | ||
204 | .id = -1, | ||
205 | .num_resources = ARRAY_SIZE(jz4740_i2s_resources), | ||
206 | .resource = jz4740_i2s_resources, | ||
207 | }; | ||
208 | |||
209 | /* PCM */ | ||
210 | struct platform_device jz4740_pcm_device = { | ||
211 | .name = "jz4740-pcm", | ||
212 | .id = -1, | ||
213 | }; | ||
214 | |||
215 | /* Codec */ | ||
216 | static struct resource jz4740_codec_resources[] = { | ||
217 | { | ||
218 | .start = JZ4740_AIC_BASE_ADDR + 0x80, | ||
219 | .end = JZ4740_AIC_BASE_ADDR + 0x88 - 1, | ||
220 | .flags = IORESOURCE_MEM, | ||
221 | }, | ||
222 | }; | ||
223 | |||
224 | struct platform_device jz4740_codec_device = { | ||
225 | .name = "jz4740-codec", | ||
226 | .id = -1, | ||
227 | .num_resources = ARRAY_SIZE(jz4740_codec_resources), | ||
228 | .resource = jz4740_codec_resources, | ||
229 | }; | ||
230 | |||
231 | /* ADC controller */ | ||
232 | static struct resource jz4740_adc_resources[] = { | ||
233 | { | ||
234 | .start = JZ4740_SADC_BASE_ADDR, | ||
235 | .end = JZ4740_SADC_BASE_ADDR + 0x30, | ||
236 | .flags = IORESOURCE_MEM, | ||
237 | }, | ||
238 | { | ||
239 | .start = JZ4740_IRQ_SADC, | ||
240 | .end = JZ4740_IRQ_SADC, | ||
241 | .flags = IORESOURCE_IRQ, | ||
242 | }, | ||
243 | { | ||
244 | .start = JZ4740_IRQ_ADC_BASE, | ||
245 | .end = JZ4740_IRQ_ADC_BASE, | ||
246 | .flags = IORESOURCE_IRQ, | ||
247 | }, | ||
248 | }; | ||
249 | |||
250 | struct platform_device jz4740_adc_device = { | ||
251 | .name = "jz4740-adc", | ||
252 | .id = -1, | ||
253 | .num_resources = ARRAY_SIZE(jz4740_adc_resources), | ||
254 | .resource = jz4740_adc_resources, | ||
255 | }; | ||
256 | |||
257 | /* Serial */ | ||
258 | #define JZ4740_UART_DATA(_id) \ | ||
259 | { \ | ||
260 | .flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE, \ | ||
261 | .iotype = UPIO_MEM, \ | ||
262 | .regshift = 2, \ | ||
263 | .serial_out = jz4740_serial_out, \ | ||
264 | .type = PORT_16550, \ | ||
265 | .mapbase = JZ4740_UART ## _id ## _BASE_ADDR, \ | ||
266 | .irq = JZ4740_IRQ_UART ## _id, \ | ||
267 | } | ||
268 | |||
269 | static struct plat_serial8250_port jz4740_uart_data[] = { | ||
270 | JZ4740_UART_DATA(0), | ||
271 | JZ4740_UART_DATA(1), | ||
272 | {}, | ||
273 | }; | ||
274 | |||
275 | static struct platform_device jz4740_uart_device = { | ||
276 | .name = "serial8250", | ||
277 | .id = 0, | ||
278 | .dev = { | ||
279 | .platform_data = jz4740_uart_data, | ||
280 | }, | ||
281 | }; | ||
282 | |||
283 | void jz4740_serial_device_register(void) | ||
284 | { | ||
285 | struct plat_serial8250_port *p; | ||
286 | |||
287 | for (p = jz4740_uart_data; p->flags != 0; ++p) | ||
288 | p->uartclk = jz4740_clock_bdata.ext_rate; | ||
289 | |||
290 | platform_device_register(&jz4740_uart_device); | ||
291 | } | ||
diff --git a/arch/mips/jz4740/pm.c b/arch/mips/jz4740/pm.c new file mode 100644 index 000000000000..a9994585424d --- /dev/null +++ b/arch/mips/jz4740/pm.c | |||
@@ -0,0 +1,56 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 SoC power management support | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/init.h> | ||
17 | #include <linux/pm.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/suspend.h> | ||
20 | |||
21 | #include <asm/mach-jz4740/clock.h> | ||
22 | |||
23 | #include "clock.h" | ||
24 | #include "irq.h" | ||
25 | |||
26 | static int jz4740_pm_enter(suspend_state_t state) | ||
27 | { | ||
28 | jz4740_intc_suspend(); | ||
29 | jz4740_clock_suspend(); | ||
30 | |||
31 | jz4740_clock_set_wait_mode(JZ4740_WAIT_MODE_SLEEP); | ||
32 | |||
33 | __asm__(".set\tmips3\n\t" | ||
34 | "wait\n\t" | ||
35 | ".set\tmips0"); | ||
36 | |||
37 | jz4740_clock_set_wait_mode(JZ4740_WAIT_MODE_IDLE); | ||
38 | |||
39 | jz4740_clock_resume(); | ||
40 | jz4740_intc_resume(); | ||
41 | |||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | static struct platform_suspend_ops jz4740_pm_ops = { | ||
46 | .valid = suspend_valid_only_mem, | ||
47 | .enter = jz4740_pm_enter, | ||
48 | }; | ||
49 | |||
50 | static int __init jz4740_pm_init(void) | ||
51 | { | ||
52 | suspend_set_ops(&jz4740_pm_ops); | ||
53 | return 0; | ||
54 | |||
55 | } | ||
56 | late_initcall(jz4740_pm_init); | ||
diff --git a/arch/mips/jz4740/prom.c b/arch/mips/jz4740/prom.c new file mode 100644 index 000000000000..cfeac15eb2e4 --- /dev/null +++ b/arch/mips/jz4740/prom.c | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 SoC prom code | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/string.h> | ||
20 | |||
21 | #include <linux/serial_reg.h> | ||
22 | |||
23 | #include <asm/bootinfo.h> | ||
24 | #include <asm/mach-jz4740/base.h> | ||
25 | |||
26 | void jz4740_init_cmdline(int argc, char *argv[]) | ||
27 | { | ||
28 | unsigned int count = COMMAND_LINE_SIZE - 1; | ||
29 | int i; | ||
30 | char *dst = &(arcs_cmdline[0]); | ||
31 | char *src; | ||
32 | |||
33 | for (i = 1; i < argc && count; ++i) { | ||
34 | src = argv[i]; | ||
35 | while (*src && count) { | ||
36 | *dst++ = *src++; | ||
37 | --count; | ||
38 | } | ||
39 | *dst++ = ' '; | ||
40 | } | ||
41 | if (i > 1) | ||
42 | --dst; | ||
43 | |||
44 | *dst = 0; | ||
45 | } | ||
46 | |||
47 | void __init prom_init(void) | ||
48 | { | ||
49 | jz4740_init_cmdline((int)fw_arg0, (char **)fw_arg1); | ||
50 | mips_machtype = MACH_INGENIC_JZ4740; | ||
51 | } | ||
52 | |||
53 | void __init prom_free_prom_memory(void) | ||
54 | { | ||
55 | } | ||
56 | |||
57 | #define UART_REG(_reg) ((void __iomem *)CKSEG1ADDR(JZ4740_UART0_BASE_ADDR + (_reg << 2))) | ||
58 | |||
59 | void prom_putchar(char c) | ||
60 | { | ||
61 | uint8_t lsr; | ||
62 | |||
63 | do { | ||
64 | lsr = readb(UART_REG(UART_LSR)); | ||
65 | } while ((lsr & UART_LSR_TEMT) == 0); | ||
66 | |||
67 | writeb(c, UART_REG(UART_TX)); | ||
68 | } | ||
diff --git a/arch/mips/jz4740/pwm.c b/arch/mips/jz4740/pwm.c new file mode 100644 index 000000000000..a26a6faec9a6 --- /dev/null +++ b/arch/mips/jz4740/pwm.c | |||
@@ -0,0 +1,177 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 platform PWM support | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | |||
18 | #include <linux/clk.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/pwm.h> | ||
21 | #include <linux/gpio.h> | ||
22 | |||
23 | #include <asm/mach-jz4740/gpio.h> | ||
24 | #include "timer.h" | ||
25 | |||
26 | static struct clk *jz4740_pwm_clk; | ||
27 | |||
28 | DEFINE_MUTEX(jz4740_pwm_mutex); | ||
29 | |||
30 | struct pwm_device { | ||
31 | unsigned int id; | ||
32 | unsigned int gpio; | ||
33 | bool used; | ||
34 | }; | ||
35 | |||
36 | static struct pwm_device jz4740_pwm_list[] = { | ||
37 | { 2, JZ_GPIO_PWM2, false }, | ||
38 | { 3, JZ_GPIO_PWM3, false }, | ||
39 | { 4, JZ_GPIO_PWM4, false }, | ||
40 | { 5, JZ_GPIO_PWM5, false }, | ||
41 | { 6, JZ_GPIO_PWM6, false }, | ||
42 | { 7, JZ_GPIO_PWM7, false }, | ||
43 | }; | ||
44 | |||
45 | struct pwm_device *pwm_request(int id, const char *label) | ||
46 | { | ||
47 | int ret = 0; | ||
48 | struct pwm_device *pwm; | ||
49 | |||
50 | if (id < 2 || id > 7 || !jz4740_pwm_clk) | ||
51 | return ERR_PTR(-ENODEV); | ||
52 | |||
53 | mutex_lock(&jz4740_pwm_mutex); | ||
54 | |||
55 | pwm = &jz4740_pwm_list[id - 2]; | ||
56 | if (pwm->used) | ||
57 | ret = -EBUSY; | ||
58 | else | ||
59 | pwm->used = true; | ||
60 | |||
61 | mutex_unlock(&jz4740_pwm_mutex); | ||
62 | |||
63 | if (ret) | ||
64 | return ERR_PTR(ret); | ||
65 | |||
66 | ret = gpio_request(pwm->gpio, label); | ||
67 | |||
68 | if (ret) { | ||
69 | printk(KERN_ERR "Failed to request pwm gpio: %d\n", ret); | ||
70 | pwm->used = false; | ||
71 | return ERR_PTR(ret); | ||
72 | } | ||
73 | |||
74 | jz_gpio_set_function(pwm->gpio, JZ_GPIO_FUNC_PWM); | ||
75 | |||
76 | jz4740_timer_start(id); | ||
77 | |||
78 | return pwm; | ||
79 | } | ||
80 | |||
81 | void pwm_free(struct pwm_device *pwm) | ||
82 | { | ||
83 | pwm_disable(pwm); | ||
84 | jz4740_timer_set_ctrl(pwm->id, 0); | ||
85 | |||
86 | jz_gpio_set_function(pwm->gpio, JZ_GPIO_FUNC_NONE); | ||
87 | gpio_free(pwm->gpio); | ||
88 | |||
89 | jz4740_timer_stop(pwm->id); | ||
90 | |||
91 | pwm->used = false; | ||
92 | } | ||
93 | |||
94 | int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) | ||
95 | { | ||
96 | unsigned long long tmp; | ||
97 | unsigned long period, duty; | ||
98 | unsigned int prescaler = 0; | ||
99 | unsigned int id = pwm->id; | ||
100 | uint16_t ctrl; | ||
101 | bool is_enabled; | ||
102 | |||
103 | if (duty_ns < 0 || duty_ns > period_ns) | ||
104 | return -EINVAL; | ||
105 | |||
106 | tmp = (unsigned long long)clk_get_rate(jz4740_pwm_clk) * period_ns; | ||
107 | do_div(tmp, 1000000000); | ||
108 | period = tmp; | ||
109 | |||
110 | while (period > 0xffff && prescaler < 6) { | ||
111 | period >>= 2; | ||
112 | ++prescaler; | ||
113 | } | ||
114 | |||
115 | if (prescaler == 6) | ||
116 | return -EINVAL; | ||
117 | |||
118 | tmp = (unsigned long long)period * duty_ns; | ||
119 | do_div(tmp, period_ns); | ||
120 | duty = period - tmp; | ||
121 | |||
122 | if (duty >= period) | ||
123 | duty = period - 1; | ||
124 | |||
125 | is_enabled = jz4740_timer_is_enabled(id); | ||
126 | if (is_enabled) | ||
127 | pwm_disable(pwm); | ||
128 | |||
129 | jz4740_timer_set_count(id, 0); | ||
130 | jz4740_timer_set_duty(id, duty); | ||
131 | jz4740_timer_set_period(id, period); | ||
132 | |||
133 | ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT | | ||
134 | JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN; | ||
135 | |||
136 | jz4740_timer_set_ctrl(id, ctrl); | ||
137 | |||
138 | if (is_enabled) | ||
139 | pwm_enable(pwm); | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | int pwm_enable(struct pwm_device *pwm) | ||
145 | { | ||
146 | uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id); | ||
147 | |||
148 | ctrl |= JZ_TIMER_CTRL_PWM_ENABLE; | ||
149 | jz4740_timer_set_ctrl(pwm->id, ctrl); | ||
150 | jz4740_timer_enable(pwm->id); | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | void pwm_disable(struct pwm_device *pwm) | ||
156 | { | ||
157 | uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id); | ||
158 | |||
159 | ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE; | ||
160 | jz4740_timer_disable(pwm->id); | ||
161 | jz4740_timer_set_ctrl(pwm->id, ctrl); | ||
162 | } | ||
163 | |||
164 | static int __init jz4740_pwm_init(void) | ||
165 | { | ||
166 | int ret = 0; | ||
167 | |||
168 | jz4740_pwm_clk = clk_get(NULL, "ext"); | ||
169 | |||
170 | if (IS_ERR(jz4740_pwm_clk)) { | ||
171 | ret = PTR_ERR(jz4740_pwm_clk); | ||
172 | jz4740_pwm_clk = NULL; | ||
173 | } | ||
174 | |||
175 | return ret; | ||
176 | } | ||
177 | subsys_initcall(jz4740_pwm_init); | ||
diff --git a/arch/mips/jz4740/reset.c b/arch/mips/jz4740/reset.c new file mode 100644 index 000000000000..5f1fb95c0d0d --- /dev/null +++ b/arch/mips/jz4740/reset.c | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the | ||
6 | * Free Software Foundation; either version 2 of the License, or (at your | ||
7 | * option) any later version. | ||
8 | * | ||
9 | * You should have received a copy of the GNU General Public License along | ||
10 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
11 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/io.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/pm.h> | ||
18 | |||
19 | #include <asm/reboot.h> | ||
20 | |||
21 | #include <asm/mach-jz4740/base.h> | ||
22 | #include <asm/mach-jz4740/timer.h> | ||
23 | |||
24 | static void jz4740_halt(void) | ||
25 | { | ||
26 | while (1) { | ||
27 | __asm__(".set push;\n" | ||
28 | ".set mips3;\n" | ||
29 | "wait;\n" | ||
30 | ".set pop;\n" | ||
31 | ); | ||
32 | } | ||
33 | } | ||
34 | |||
35 | #define JZ_REG_WDT_DATA 0x00 | ||
36 | #define JZ_REG_WDT_COUNTER_ENABLE 0x04 | ||
37 | #define JZ_REG_WDT_COUNTER 0x08 | ||
38 | #define JZ_REG_WDT_CTRL 0x0c | ||
39 | |||
40 | static void jz4740_restart(char *command) | ||
41 | { | ||
42 | void __iomem *wdt_base = ioremap(JZ4740_WDT_BASE_ADDR, 0x0f); | ||
43 | |||
44 | jz4740_timer_enable_watchdog(); | ||
45 | |||
46 | writeb(0, wdt_base + JZ_REG_WDT_COUNTER_ENABLE); | ||
47 | |||
48 | writew(0, wdt_base + JZ_REG_WDT_COUNTER); | ||
49 | writew(0, wdt_base + JZ_REG_WDT_DATA); | ||
50 | writew(BIT(2), wdt_base + JZ_REG_WDT_CTRL); | ||
51 | |||
52 | writeb(1, wdt_base + JZ_REG_WDT_COUNTER_ENABLE); | ||
53 | jz4740_halt(); | ||
54 | } | ||
55 | |||
56 | #define JZ_REG_RTC_CTRL 0x00 | ||
57 | #define JZ_REG_RTC_HIBERNATE 0x20 | ||
58 | |||
59 | #define JZ_RTC_CTRL_WRDY BIT(7) | ||
60 | |||
61 | static void jz4740_power_off(void) | ||
62 | { | ||
63 | void __iomem *rtc_base = ioremap(JZ4740_RTC_BASE_ADDR, 0x24); | ||
64 | uint32_t ctrl; | ||
65 | |||
66 | do { | ||
67 | ctrl = readl(rtc_base + JZ_REG_RTC_CTRL); | ||
68 | } while (!(ctrl & JZ_RTC_CTRL_WRDY)); | ||
69 | |||
70 | writel(1, rtc_base + JZ_REG_RTC_HIBERNATE); | ||
71 | jz4740_halt(); | ||
72 | } | ||
73 | |||
74 | void jz4740_reset_init(void) | ||
75 | { | ||
76 | _machine_restart = jz4740_restart; | ||
77 | _machine_halt = jz4740_halt; | ||
78 | pm_power_off = jz4740_power_off; | ||
79 | } | ||
diff --git a/arch/mips/jz4740/reset.h b/arch/mips/jz4740/reset.h new file mode 100644 index 000000000000..5202ab4ad9db --- /dev/null +++ b/arch/mips/jz4740/reset.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef __MIPS_JZ4740_RESET_H__ | ||
2 | #define __MIPS_JZ4740_RESET_H__ | ||
3 | |||
4 | extern void jz4740_reset_init(void); | ||
5 | |||
6 | #endif | ||
diff --git a/arch/mips/jz4740/serial.c b/arch/mips/jz4740/serial.c new file mode 100644 index 000000000000..d23de45826d1 --- /dev/null +++ b/arch/mips/jz4740/serial.c | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 serial support | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/io.h> | ||
17 | #include <linux/serial_core.h> | ||
18 | #include <linux/serial_reg.h> | ||
19 | |||
20 | void jz4740_serial_out(struct uart_port *p, int offset, int value) | ||
21 | { | ||
22 | switch (offset) { | ||
23 | case UART_FCR: | ||
24 | value |= 0x10; /* Enable uart module */ | ||
25 | break; | ||
26 | case UART_IER: | ||
27 | value |= (value & 0x4) << 2; | ||
28 | break; | ||
29 | default: | ||
30 | break; | ||
31 | } | ||
32 | writeb(value, p->membase + (offset << p->regshift)); | ||
33 | } | ||
diff --git a/arch/mips/jz4740/serial.h b/arch/mips/jz4740/serial.h new file mode 100644 index 000000000000..b9fe3ade0289 --- /dev/null +++ b/arch/mips/jz4740/serial.h | |||
@@ -0,0 +1,20 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 serial support | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #ifndef __MIPS_JZ4740_SERIAL_H__ | ||
17 | |||
18 | void jz4740_serial_out(struct uart_port *p, int offset, int value); | ||
19 | |||
20 | #endif | ||
diff --git a/arch/mips/jz4740/setup.c b/arch/mips/jz4740/setup.c new file mode 100644 index 000000000000..6a9e14dab91e --- /dev/null +++ b/arch/mips/jz4740/setup.c | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 setup code | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/init.h> | ||
17 | #include <linux/kernel.h> | ||
18 | |||
19 | #include "reset.h" | ||
20 | |||
21 | void __init plat_mem_setup(void) | ||
22 | { | ||
23 | jz4740_reset_init(); | ||
24 | } | ||
25 | |||
26 | const char *get_system_type(void) | ||
27 | { | ||
28 | return "JZ4740"; | ||
29 | } | ||
diff --git a/arch/mips/jz4740/time.c b/arch/mips/jz4740/time.c new file mode 100644 index 000000000000..fe01678d94fd --- /dev/null +++ b/arch/mips/jz4740/time.c | |||
@@ -0,0 +1,144 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 platform time support | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/time.h> | ||
19 | |||
20 | #include <linux/clockchips.h> | ||
21 | |||
22 | #include <asm/mach-jz4740/irq.h> | ||
23 | #include <asm/time.h> | ||
24 | |||
25 | #include "clock.h" | ||
26 | #include "timer.h" | ||
27 | |||
28 | #define TIMER_CLOCKEVENT 0 | ||
29 | #define TIMER_CLOCKSOURCE 1 | ||
30 | |||
31 | static uint16_t jz4740_jiffies_per_tick; | ||
32 | |||
33 | static cycle_t jz4740_clocksource_read(struct clocksource *cs) | ||
34 | { | ||
35 | return jz4740_timer_get_count(TIMER_CLOCKSOURCE); | ||
36 | } | ||
37 | |||
38 | static struct clocksource jz4740_clocksource = { | ||
39 | .name = "jz4740-timer", | ||
40 | .rating = 200, | ||
41 | .read = jz4740_clocksource_read, | ||
42 | .mask = CLOCKSOURCE_MASK(16), | ||
43 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
44 | }; | ||
45 | |||
46 | static irqreturn_t jz4740_clockevent_irq(int irq, void *devid) | ||
47 | { | ||
48 | struct clock_event_device *cd = devid; | ||
49 | |||
50 | jz4740_timer_ack_full(TIMER_CLOCKEVENT); | ||
51 | |||
52 | if (cd->mode != CLOCK_EVT_MODE_PERIODIC) | ||
53 | jz4740_timer_disable(TIMER_CLOCKEVENT); | ||
54 | |||
55 | cd->event_handler(cd); | ||
56 | |||
57 | return IRQ_HANDLED; | ||
58 | } | ||
59 | |||
60 | static void jz4740_clockevent_set_mode(enum clock_event_mode mode, | ||
61 | struct clock_event_device *cd) | ||
62 | { | ||
63 | switch (mode) { | ||
64 | case CLOCK_EVT_MODE_PERIODIC: | ||
65 | jz4740_timer_set_count(TIMER_CLOCKEVENT, 0); | ||
66 | jz4740_timer_set_period(TIMER_CLOCKEVENT, jz4740_jiffies_per_tick); | ||
67 | case CLOCK_EVT_MODE_RESUME: | ||
68 | jz4740_timer_irq_full_enable(TIMER_CLOCKEVENT); | ||
69 | jz4740_timer_enable(TIMER_CLOCKEVENT); | ||
70 | break; | ||
71 | case CLOCK_EVT_MODE_ONESHOT: | ||
72 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
73 | jz4740_timer_disable(TIMER_CLOCKEVENT); | ||
74 | break; | ||
75 | default: | ||
76 | break; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | static int jz4740_clockevent_set_next(unsigned long evt, | ||
81 | struct clock_event_device *cd) | ||
82 | { | ||
83 | jz4740_timer_set_count(TIMER_CLOCKEVENT, 0); | ||
84 | jz4740_timer_set_period(TIMER_CLOCKEVENT, evt); | ||
85 | jz4740_timer_enable(TIMER_CLOCKEVENT); | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static struct clock_event_device jz4740_clockevent = { | ||
91 | .name = "jz4740-timer", | ||
92 | .features = CLOCK_EVT_FEAT_PERIODIC, | ||
93 | .set_next_event = jz4740_clockevent_set_next, | ||
94 | .set_mode = jz4740_clockevent_set_mode, | ||
95 | .rating = 200, | ||
96 | .irq = JZ4740_IRQ_TCU0, | ||
97 | }; | ||
98 | |||
99 | static struct irqaction timer_irqaction = { | ||
100 | .handler = jz4740_clockevent_irq, | ||
101 | .flags = IRQF_PERCPU | IRQF_TIMER, | ||
102 | .name = "jz4740-timerirq", | ||
103 | .dev_id = &jz4740_clockevent, | ||
104 | }; | ||
105 | |||
106 | void __init plat_time_init(void) | ||
107 | { | ||
108 | int ret; | ||
109 | uint32_t clk_rate; | ||
110 | uint16_t ctrl; | ||
111 | |||
112 | jz4740_timer_init(); | ||
113 | |||
114 | clk_rate = jz4740_clock_bdata.ext_rate >> 4; | ||
115 | jz4740_jiffies_per_tick = DIV_ROUND_CLOSEST(clk_rate, HZ); | ||
116 | |||
117 | clockevent_set_clock(&jz4740_clockevent, clk_rate); | ||
118 | jz4740_clockevent.min_delta_ns = clockevent_delta2ns(100, &jz4740_clockevent); | ||
119 | jz4740_clockevent.max_delta_ns = clockevent_delta2ns(0xffff, &jz4740_clockevent); | ||
120 | jz4740_clockevent.cpumask = cpumask_of(0); | ||
121 | |||
122 | clockevents_register_device(&jz4740_clockevent); | ||
123 | |||
124 | clocksource_set_clock(&jz4740_clocksource, clk_rate); | ||
125 | ret = clocksource_register(&jz4740_clocksource); | ||
126 | |||
127 | if (ret) | ||
128 | printk(KERN_ERR "Failed to register clocksource: %d\n", ret); | ||
129 | |||
130 | setup_irq(JZ4740_IRQ_TCU0, &timer_irqaction); | ||
131 | |||
132 | ctrl = JZ_TIMER_CTRL_PRESCALE_16 | JZ_TIMER_CTRL_SRC_EXT; | ||
133 | |||
134 | jz4740_timer_set_ctrl(TIMER_CLOCKEVENT, ctrl); | ||
135 | jz4740_timer_set_ctrl(TIMER_CLOCKSOURCE, ctrl); | ||
136 | |||
137 | jz4740_timer_set_period(TIMER_CLOCKEVENT, jz4740_jiffies_per_tick); | ||
138 | jz4740_timer_irq_full_enable(TIMER_CLOCKEVENT); | ||
139 | |||
140 | jz4740_timer_set_period(TIMER_CLOCKSOURCE, 0xffff); | ||
141 | |||
142 | jz4740_timer_enable(TIMER_CLOCKEVENT); | ||
143 | jz4740_timer_enable(TIMER_CLOCKSOURCE); | ||
144 | } | ||
diff --git a/arch/mips/jz4740/timer.c b/arch/mips/jz4740/timer.c new file mode 100644 index 000000000000..b2c015129055 --- /dev/null +++ b/arch/mips/jz4740/timer.c | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 platform timer support | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/io.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | |||
20 | #include "timer.h" | ||
21 | |||
22 | #include <asm/mach-jz4740/base.h> | ||
23 | |||
24 | void __iomem *jz4740_timer_base; | ||
25 | |||
26 | void jz4740_timer_enable_watchdog(void) | ||
27 | { | ||
28 | writel(BIT(16), jz4740_timer_base + JZ_REG_TIMER_STOP_CLEAR); | ||
29 | } | ||
30 | |||
31 | void jz4740_timer_disable_watchdog(void) | ||
32 | { | ||
33 | writel(BIT(16), jz4740_timer_base + JZ_REG_TIMER_STOP_SET); | ||
34 | } | ||
35 | |||
36 | void __init jz4740_timer_init(void) | ||
37 | { | ||
38 | jz4740_timer_base = ioremap(JZ4740_TCU_BASE_ADDR, 0x100); | ||
39 | |||
40 | if (!jz4740_timer_base) | ||
41 | panic("Failed to ioremap timer registers"); | ||
42 | |||
43 | /* Disable all timer clocks except for those used as system timers */ | ||
44 | writel(0x000100fc, jz4740_timer_base + JZ_REG_TIMER_STOP_SET); | ||
45 | |||
46 | /* Timer irqs are unmasked by default, mask them */ | ||
47 | writel(0x00ff00ff, jz4740_timer_base + JZ_REG_TIMER_MASK_SET); | ||
48 | } | ||
diff --git a/arch/mips/jz4740/timer.h b/arch/mips/jz4740/timer.h new file mode 100644 index 000000000000..fca3994f2e6d --- /dev/null +++ b/arch/mips/jz4740/timer.h | |||
@@ -0,0 +1,136 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | ||
3 | * JZ4740 platform timer support | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License along | ||
11 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #ifndef __MIPS_JZ4740_TIMER_H__ | ||
17 | #define __MIPS_JZ4740_TIMER_H__ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/io.h> | ||
21 | |||
22 | #define JZ_REG_TIMER_STOP 0x0C | ||
23 | #define JZ_REG_TIMER_STOP_SET 0x1C | ||
24 | #define JZ_REG_TIMER_STOP_CLEAR 0x2C | ||
25 | #define JZ_REG_TIMER_ENABLE 0x00 | ||
26 | #define JZ_REG_TIMER_ENABLE_SET 0x04 | ||
27 | #define JZ_REG_TIMER_ENABLE_CLEAR 0x08 | ||
28 | #define JZ_REG_TIMER_FLAG 0x10 | ||
29 | #define JZ_REG_TIMER_FLAG_SET 0x14 | ||
30 | #define JZ_REG_TIMER_FLAG_CLEAR 0x18 | ||
31 | #define JZ_REG_TIMER_MASK 0x20 | ||
32 | #define JZ_REG_TIMER_MASK_SET 0x24 | ||
33 | #define JZ_REG_TIMER_MASK_CLEAR 0x28 | ||
34 | |||
35 | #define JZ_REG_TIMER_DFR(x) (((x) * 0x10) + 0x30) | ||
36 | #define JZ_REG_TIMER_DHR(x) (((x) * 0x10) + 0x34) | ||
37 | #define JZ_REG_TIMER_CNT(x) (((x) * 0x10) + 0x38) | ||
38 | #define JZ_REG_TIMER_CTRL(x) (((x) * 0x10) + 0x3C) | ||
39 | |||
40 | #define JZ_TIMER_IRQ_HALF(x) BIT((x) + 0x10) | ||
41 | #define JZ_TIMER_IRQ_FULL(x) BIT(x) | ||
42 | |||
43 | #define JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN BIT(9) | ||
44 | #define JZ_TIMER_CTRL_PWM_ACTIVE_LOW BIT(8) | ||
45 | #define JZ_TIMER_CTRL_PWM_ENABLE BIT(7) | ||
46 | #define JZ_TIMER_CTRL_PRESCALE_MASK 0x1c | ||
47 | #define JZ_TIMER_CTRL_PRESCALE_OFFSET 0x3 | ||
48 | #define JZ_TIMER_CTRL_PRESCALE_1 (0 << 3) | ||
49 | #define JZ_TIMER_CTRL_PRESCALE_4 (1 << 3) | ||
50 | #define JZ_TIMER_CTRL_PRESCALE_16 (2 << 3) | ||
51 | #define JZ_TIMER_CTRL_PRESCALE_64 (3 << 3) | ||
52 | #define JZ_TIMER_CTRL_PRESCALE_256 (4 << 3) | ||
53 | #define JZ_TIMER_CTRL_PRESCALE_1024 (5 << 3) | ||
54 | |||
55 | #define JZ_TIMER_CTRL_PRESCALER(x) ((x) << JZ_TIMER_CTRL_PRESCALE_OFFSET) | ||
56 | |||
57 | #define JZ_TIMER_CTRL_SRC_EXT BIT(2) | ||
58 | #define JZ_TIMER_CTRL_SRC_RTC BIT(1) | ||
59 | #define JZ_TIMER_CTRL_SRC_PCLK BIT(0) | ||
60 | |||
61 | extern void __iomem *jz4740_timer_base; | ||
62 | void __init jz4740_timer_init(void); | ||
63 | |||
64 | static inline void jz4740_timer_stop(unsigned int timer) | ||
65 | { | ||
66 | writel(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_STOP_SET); | ||
67 | } | ||
68 | |||
69 | static inline void jz4740_timer_start(unsigned int timer) | ||
70 | { | ||
71 | writel(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_STOP_CLEAR); | ||
72 | } | ||
73 | |||
74 | static inline bool jz4740_timer_is_enabled(unsigned int timer) | ||
75 | { | ||
76 | return readb(jz4740_timer_base + JZ_REG_TIMER_ENABLE) & BIT(timer); | ||
77 | } | ||
78 | |||
79 | static inline void jz4740_timer_enable(unsigned int timer) | ||
80 | { | ||
81 | writeb(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_ENABLE_SET); | ||
82 | } | ||
83 | |||
84 | static inline void jz4740_timer_disable(unsigned int timer) | ||
85 | { | ||
86 | writeb(BIT(timer), jz4740_timer_base + JZ_REG_TIMER_ENABLE_CLEAR); | ||
87 | } | ||
88 | |||
89 | |||
90 | static inline void jz4740_timer_set_period(unsigned int timer, uint16_t period) | ||
91 | { | ||
92 | writew(period, jz4740_timer_base + JZ_REG_TIMER_DFR(timer)); | ||
93 | } | ||
94 | |||
95 | static inline void jz4740_timer_set_duty(unsigned int timer, uint16_t duty) | ||
96 | { | ||
97 | writew(duty, jz4740_timer_base + JZ_REG_TIMER_DHR(timer)); | ||
98 | } | ||
99 | |||
100 | static inline void jz4740_timer_set_count(unsigned int timer, uint16_t count) | ||
101 | { | ||
102 | writew(count, jz4740_timer_base + JZ_REG_TIMER_CNT(timer)); | ||
103 | } | ||
104 | |||
105 | static inline uint16_t jz4740_timer_get_count(unsigned int timer) | ||
106 | { | ||
107 | return readw(jz4740_timer_base + JZ_REG_TIMER_CNT(timer)); | ||
108 | } | ||
109 | |||
110 | static inline void jz4740_timer_ack_full(unsigned int timer) | ||
111 | { | ||
112 | writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_FLAG_CLEAR); | ||
113 | } | ||
114 | |||
115 | static inline void jz4740_timer_irq_full_enable(unsigned int timer) | ||
116 | { | ||
117 | writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_FLAG_CLEAR); | ||
118 | writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_MASK_CLEAR); | ||
119 | } | ||
120 | |||
121 | static inline void jz4740_timer_irq_full_disable(unsigned int timer) | ||
122 | { | ||
123 | writel(JZ_TIMER_IRQ_FULL(timer), jz4740_timer_base + JZ_REG_TIMER_MASK_SET); | ||
124 | } | ||
125 | |||
126 | static inline void jz4740_timer_set_ctrl(unsigned int timer, uint16_t ctrl) | ||
127 | { | ||
128 | writew(ctrl, jz4740_timer_base + JZ_REG_TIMER_CTRL(timer)); | ||
129 | } | ||
130 | |||
131 | static inline uint16_t jz4740_timer_get_ctrl(unsigned int timer) | ||
132 | { | ||
133 | return readw(jz4740_timer_base + JZ_REG_TIMER_CTRL(timer)); | ||
134 | } | ||
135 | |||
136 | #endif | ||