diff options
-rw-r--r-- | arch/arm/mach-s3c2410/include/mach/gpio.h | 6 | ||||
-rw-r--r-- | arch/arm/mach-s3c2412/mach-jive.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-s3c2440/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c2440/mach-anubis.c | 5 | ||||
-rw-r--r-- | arch/arm/mach-s3c2440/mach-at2440evb.c | 44 | ||||
-rw-r--r-- | arch/arm/plat-s3c/Kconfig | 8 | ||||
-rw-r--r-- | arch/arm/plat-s3c/include/plat/adc.h | 29 | ||||
-rw-r--r-- | arch/arm/plat-s3c/include/plat/uncompress.h | 28 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/Kconfig | 28 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/adc.c | 372 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/devs.c | 10 |
12 files changed, 536 insertions, 3 deletions
diff --git a/arch/arm/mach-s3c2410/include/mach/gpio.h b/arch/arm/mach-s3c2410/include/mach/gpio.h index 3b52b86498a6..e0349af8a483 100644 --- a/arch/arm/mach-s3c2410/include/mach/gpio.h +++ b/arch/arm/mach-s3c2410/include/mach/gpio.h | |||
@@ -15,4 +15,10 @@ | |||
15 | #define gpio_set_value __gpio_set_value | 15 | #define gpio_set_value __gpio_set_value |
16 | #define gpio_cansleep __gpio_cansleep | 16 | #define gpio_cansleep __gpio_cansleep |
17 | 17 | ||
18 | /* some boards require extra gpio capacity to support external | ||
19 | * devices that need GPIO. | ||
20 | */ | ||
21 | |||
22 | #define ARCH_NR_GPIOS (256 + CONFIG_S3C24XX_GPIO_EXTRA) | ||
23 | |||
18 | #include <asm-generic/gpio.h> | 24 | #include <asm-generic/gpio.h> |
diff --git a/arch/arm/mach-s3c2412/mach-jive.c b/arch/arm/mach-s3c2412/mach-jive.c index 25ff1ec9f8ad..5e758cf8bf82 100644 --- a/arch/arm/mach-s3c2412/mach-jive.c +++ b/arch/arm/mach-s3c2412/mach-jive.c | |||
@@ -398,11 +398,12 @@ static struct s3c2410_spigpio_info jive_lcd_spi = { | |||
398 | .bus_num = 1, | 398 | .bus_num = 1, |
399 | .pin_clk = S3C2410_GPG8, | 399 | .pin_clk = S3C2410_GPG8, |
400 | .pin_mosi = S3C2410_GPB8, | 400 | .pin_mosi = S3C2410_GPB8, |
401 | .num_chipselect = 1, | ||
401 | .chip_select = jive_lcd_spi_chipselect, | 402 | .chip_select = jive_lcd_spi_chipselect, |
402 | }; | 403 | }; |
403 | 404 | ||
404 | static struct platform_device jive_device_lcdspi = { | 405 | static struct platform_device jive_device_lcdspi = { |
405 | .name = "s3c24xx-spi-gpio", | 406 | .name = "spi_s3c24xx_gpio", |
406 | .id = 1, | 407 | .id = 1, |
407 | .num_resources = 0, | 408 | .num_resources = 0, |
408 | .dev.platform_data = &jive_lcd_spi, | 409 | .dev.platform_data = &jive_lcd_spi, |
@@ -419,11 +420,12 @@ static struct s3c2410_spigpio_info jive_wm8750_spi = { | |||
419 | .bus_num = 2, | 420 | .bus_num = 2, |
420 | .pin_clk = S3C2410_GPB4, | 421 | .pin_clk = S3C2410_GPB4, |
421 | .pin_mosi = S3C2410_GPB9, | 422 | .pin_mosi = S3C2410_GPB9, |
423 | .num_chipselect = 1, | ||
422 | .chip_select = jive_wm8750_chipselect, | 424 | .chip_select = jive_wm8750_chipselect, |
423 | }; | 425 | }; |
424 | 426 | ||
425 | static struct platform_device jive_device_wm8750 = { | 427 | static struct platform_device jive_device_wm8750 = { |
426 | .name = "s3c24xx-spi-gpio", | 428 | .name = "spi_s3c24xx_gpio", |
427 | .id = 2, | 429 | .id = 2, |
428 | .num_resources = 0, | 430 | .num_resources = 0, |
429 | .dev.platform_data = &jive_wm8750_spi, | 431 | .dev.platform_data = &jive_wm8750_spi, |
diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig index 57b9c57ff2b4..cde5ae9a4340 100644 --- a/arch/arm/mach-s3c2440/Kconfig +++ b/arch/arm/mach-s3c2440/Kconfig | |||
@@ -32,6 +32,7 @@ config MACH_ANUBIS | |||
32 | select S3C24XX_DCLK | 32 | select S3C24XX_DCLK |
33 | select PM_SIMTEC if PM | 33 | select PM_SIMTEC if PM |
34 | select HAVE_PATA_PLATFORM | 34 | select HAVE_PATA_PLATFORM |
35 | select S3C24XX_GPIO_EXTRA64 | ||
35 | help | 36 | help |
36 | Say Y here if you are using the Simtec Electronics ANUBIS | 37 | Say Y here if you are using the Simtec Electronics ANUBIS |
37 | development system | 38 | development system |
@@ -41,6 +42,7 @@ config MACH_OSIRIS | |||
41 | select CPU_S3C2440 | 42 | select CPU_S3C2440 |
42 | select S3C24XX_DCLK | 43 | select S3C24XX_DCLK |
43 | select PM_SIMTEC if PM | 44 | select PM_SIMTEC if PM |
45 | select S3C24XX_GPIO_EXTRA128 | ||
44 | help | 46 | help |
45 | Say Y here if you are using the Simtec IM2440D20 module, also | 47 | Say Y here if you are using the Simtec IM2440D20 module, also |
46 | known as the Osiris. | 48 | known as the Osiris. |
diff --git a/arch/arm/mach-s3c2440/mach-anubis.c b/arch/arm/mach-s3c2440/mach-anubis.c index 334379bdfc6e..f151f8939929 100644 --- a/arch/arm/mach-s3c2440/mach-anubis.c +++ b/arch/arm/mach-s3c2440/mach-anubis.c | |||
@@ -366,6 +366,8 @@ static struct sm501_initdata anubis_sm501_initdata = { | |||
366 | .mask = 0, | 366 | .mask = 0, |
367 | }, | 367 | }, |
368 | 368 | ||
369 | .devices = SM501_USE_GPIO, | ||
370 | |||
369 | /* set the SDRAM and bus clocks */ | 371 | /* set the SDRAM and bus clocks */ |
370 | .mclk = 72 * MHZ, | 372 | .mclk = 72 * MHZ, |
371 | .m1xclk = 144 * MHZ, | 373 | .m1xclk = 144 * MHZ, |
@@ -373,10 +375,12 @@ static struct sm501_initdata anubis_sm501_initdata = { | |||
373 | 375 | ||
374 | static struct sm501_platdata_gpio_i2c anubis_sm501_gpio_i2c[] = { | 376 | static struct sm501_platdata_gpio_i2c anubis_sm501_gpio_i2c[] = { |
375 | [0] = { | 377 | [0] = { |
378 | .bus_num = 1, | ||
376 | .pin_scl = 44, | 379 | .pin_scl = 44, |
377 | .pin_sda = 45, | 380 | .pin_sda = 45, |
378 | }, | 381 | }, |
379 | [1] = { | 382 | [1] = { |
383 | .bus_num = 2, | ||
380 | .pin_scl = 40, | 384 | .pin_scl = 40, |
381 | .pin_sda = 41, | 385 | .pin_sda = 41, |
382 | }, | 386 | }, |
@@ -384,6 +388,7 @@ static struct sm501_platdata_gpio_i2c anubis_sm501_gpio_i2c[] = { | |||
384 | 388 | ||
385 | static struct sm501_platdata anubis_sm501_platdata = { | 389 | static struct sm501_platdata anubis_sm501_platdata = { |
386 | .init = &anubis_sm501_initdata, | 390 | .init = &anubis_sm501_initdata, |
391 | .gpio_base = -1, | ||
387 | .gpio_i2c = anubis_sm501_gpio_i2c, | 392 | .gpio_i2c = anubis_sm501_gpio_i2c, |
388 | .gpio_i2c_nr = ARRAY_SIZE(anubis_sm501_gpio_i2c), | 393 | .gpio_i2c_nr = ARRAY_SIZE(anubis_sm501_gpio_i2c), |
389 | }; | 394 | }; |
diff --git a/arch/arm/mach-s3c2440/mach-at2440evb.c b/arch/arm/mach-s3c2440/mach-at2440evb.c index 07b42a0207d1..4539b1d95877 100644 --- a/arch/arm/mach-s3c2440/mach-at2440evb.c +++ b/arch/arm/mach-s3c2440/mach-at2440evb.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <asm/mach/irq.h> | 28 | #include <asm/mach/irq.h> |
29 | 29 | ||
30 | #include <mach/hardware.h> | 30 | #include <mach/hardware.h> |
31 | #include <mach/fb.h> | ||
31 | #include <asm/irq.h> | 32 | #include <asm/irq.h> |
32 | #include <asm/mach-types.h> | 33 | #include <asm/mach-types.h> |
33 | 34 | ||
@@ -45,6 +46,7 @@ | |||
45 | #include <plat/clock.h> | 46 | #include <plat/clock.h> |
46 | #include <plat/devs.h> | 47 | #include <plat/devs.h> |
47 | #include <plat/cpu.h> | 48 | #include <plat/cpu.h> |
49 | #include <asm/plat-s3c24xx/mci.h> | ||
48 | 50 | ||
49 | static struct map_desc at2440evb_iodesc[] __initdata = { | 51 | static struct map_desc at2440evb_iodesc[] __initdata = { |
50 | /* Nothing here */ | 52 | /* Nothing here */ |
@@ -162,6 +164,43 @@ static struct platform_device at2440evb_device_eth = { | |||
162 | }, | 164 | }, |
163 | }; | 165 | }; |
164 | 166 | ||
167 | static struct s3c24xx_mci_pdata at2440evb_mci_pdata = { | ||
168 | .gpio_detect = S3C2410_GPG10, | ||
169 | }; | ||
170 | |||
171 | /* 7" LCD panel */ | ||
172 | |||
173 | static struct s3c2410fb_display at2440evb_lcd_cfg __initdata = { | ||
174 | |||
175 | .lcdcon5 = S3C2410_LCDCON5_FRM565 | | ||
176 | S3C2410_LCDCON5_INVVLINE | | ||
177 | S3C2410_LCDCON5_INVVFRAME | | ||
178 | S3C2410_LCDCON5_PWREN | | ||
179 | S3C2410_LCDCON5_HWSWP, | ||
180 | |||
181 | .type = S3C2410_LCDCON1_TFT, | ||
182 | |||
183 | .width = 800, | ||
184 | .height = 480, | ||
185 | |||
186 | .pixclock = 33333, /* HCLK 60 MHz, divisor 2 */ | ||
187 | .xres = 800, | ||
188 | .yres = 480, | ||
189 | .bpp = 16, | ||
190 | .left_margin = 88, | ||
191 | .right_margin = 40, | ||
192 | .hsync_len = 128, | ||
193 | .upper_margin = 32, | ||
194 | .lower_margin = 11, | ||
195 | .vsync_len = 2, | ||
196 | }; | ||
197 | |||
198 | static struct s3c2410fb_mach_info at2440evb_fb_info __initdata = { | ||
199 | .displays = &at2440evb_lcd_cfg, | ||
200 | .num_displays = 1, | ||
201 | .default_display = 0, | ||
202 | }; | ||
203 | |||
165 | static struct platform_device *at2440evb_devices[] __initdata = { | 204 | static struct platform_device *at2440evb_devices[] __initdata = { |
166 | &s3c_device_usb, | 205 | &s3c_device_usb, |
167 | &s3c_device_wdt, | 206 | &s3c_device_wdt, |
@@ -169,12 +208,16 @@ static struct platform_device *at2440evb_devices[] __initdata = { | |||
169 | &s3c_device_i2c, | 208 | &s3c_device_i2c, |
170 | &s3c_device_rtc, | 209 | &s3c_device_rtc, |
171 | &s3c_device_nand, | 210 | &s3c_device_nand, |
211 | &s3c_device_sdi, | ||
212 | &s3c_device_lcd, | ||
172 | &at2440evb_device_eth, | 213 | &at2440evb_device_eth, |
173 | }; | 214 | }; |
174 | 215 | ||
175 | static void __init at2440evb_map_io(void) | 216 | static void __init at2440evb_map_io(void) |
176 | { | 217 | { |
177 | s3c_device_nand.dev.platform_data = &at2440evb_nand_info; | 218 | s3c_device_nand.dev.platform_data = &at2440evb_nand_info; |
219 | s3c_device_sdi.name = "s3c2440-sdi"; | ||
220 | s3c_device_sdi.dev.platform_data = &at2440evb_mci_pdata; | ||
178 | 221 | ||
179 | s3c24xx_init_io(at2440evb_iodesc, ARRAY_SIZE(at2440evb_iodesc)); | 222 | s3c24xx_init_io(at2440evb_iodesc, ARRAY_SIZE(at2440evb_iodesc)); |
180 | s3c24xx_init_clocks(16934400); | 223 | s3c24xx_init_clocks(16934400); |
@@ -183,6 +226,7 @@ static void __init at2440evb_map_io(void) | |||
183 | 226 | ||
184 | static void __init at2440evb_init(void) | 227 | static void __init at2440evb_init(void) |
185 | { | 228 | { |
229 | s3c24xx_fb_set_platdata(&at2440evb_fb_info); | ||
186 | platform_add_devices(at2440evb_devices, ARRAY_SIZE(at2440evb_devices)); | 230 | platform_add_devices(at2440evb_devices, ARRAY_SIZE(at2440evb_devices)); |
187 | } | 231 | } |
188 | 232 | ||
diff --git a/arch/arm/plat-s3c/Kconfig b/arch/arm/plat-s3c/Kconfig index 6fa261a3d405..adb9060ec910 100644 --- a/arch/arm/plat-s3c/Kconfig +++ b/arch/arm/plat-s3c/Kconfig | |||
@@ -57,6 +57,14 @@ config S3C_BOOT_ERROR_RESET | |||
57 | Say y here to use the watchdog to reset the system if the | 57 | Say y here to use the watchdog to reset the system if the |
58 | kernel decompressor detects an error during decompression. | 58 | kernel decompressor detects an error during decompression. |
59 | 59 | ||
60 | config S3C_BOOT_UART_FORCE_FIFO | ||
61 | bool "Force UART FIFO on during boot process" | ||
62 | depends on PLAT_S3C | ||
63 | default y | ||
64 | help | ||
65 | Say Y here to force the UART FIFOs on during the kernel | ||
66 | uncompressor | ||
67 | |||
60 | comment "Power management" | 68 | comment "Power management" |
61 | 69 | ||
62 | config S3C2410_PM_DEBUG | 70 | config S3C2410_PM_DEBUG |
diff --git a/arch/arm/plat-s3c/include/plat/adc.h b/arch/arm/plat-s3c/include/plat/adc.h new file mode 100644 index 000000000000..43df2a404b0b --- /dev/null +++ b/arch/arm/plat-s3c/include/plat/adc.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* arch/arm/plat-s3c/include/plat/adc.h | ||
2 | * | ||
3 | * Copyright (c) 2008 Simtec Electronics | ||
4 | * http://armlinux.simnte.co.uk/ | ||
5 | * Ben Dooks <ben@simtec.co.uk> | ||
6 | * | ||
7 | * S3C24XX ADC driver information | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #ifndef __ASM_PLAT_ADC_H | ||
15 | #define __ASM_PLAT_ADC_H __FILE__ | ||
16 | |||
17 | struct s3c_adc_client; | ||
18 | |||
19 | extern int s3c_adc_start(struct s3c_adc_client *client, | ||
20 | unsigned int channel, unsigned int nr_samples); | ||
21 | |||
22 | extern struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev, | ||
23 | void (*select)(unsigned selected), | ||
24 | void (*conv)(unsigned d0, unsigned d1), | ||
25 | unsigned int is_ts); | ||
26 | |||
27 | extern void s3c_adc_release(struct s3c_adc_client *client); | ||
28 | |||
29 | #endif /* __ASM_PLAT_ADC_H */ | ||
diff --git a/arch/arm/plat-s3c/include/plat/uncompress.h b/arch/arm/plat-s3c/include/plat/uncompress.h index eeef32c4312d..6061de87f225 100644 --- a/arch/arm/plat-s3c/include/plat/uncompress.h +++ b/arch/arm/plat-s3c/include/plat/uncompress.h | |||
@@ -139,6 +139,28 @@ static void arch_decomp_error(const char *x) | |||
139 | 139 | ||
140 | static void error(char *err); | 140 | static void error(char *err); |
141 | 141 | ||
142 | #ifdef CONFIG_S3C_BOOT_UART_FORCE_FIFO | ||
143 | static inline void arch_enable_uart_fifo(void) | ||
144 | { | ||
145 | u32 fifocon = uart_rd(S3C2410_UFCON); | ||
146 | |||
147 | if (!(fifocon & S3C2410_UFCON_FIFOMODE)) { | ||
148 | fifocon |= S3C2410_UFCON_RESETBOTH; | ||
149 | uart_wr(S3C2410_UFCON, fifocon); | ||
150 | |||
151 | /* wait for fifo reset to complete */ | ||
152 | while (1) { | ||
153 | fifocon = uart_rd(S3C2410_UFCON); | ||
154 | if (!(fifocon & S3C2410_UFCON_RESETBOTH)) | ||
155 | break; | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | #else | ||
160 | #define arch_enable_uart_fifo() do { } while(0) | ||
161 | #endif | ||
162 | |||
163 | |||
142 | static void | 164 | static void |
143 | arch_decomp_setup(void) | 165 | arch_decomp_setup(void) |
144 | { | 166 | { |
@@ -149,6 +171,12 @@ arch_decomp_setup(void) | |||
149 | 171 | ||
150 | arch_detect_cpu(); | 172 | arch_detect_cpu(); |
151 | arch_decomp_wdog_start(); | 173 | arch_decomp_wdog_start(); |
174 | |||
175 | /* Enable the UART FIFOs if they where not enabled and our | ||
176 | * configuration says we should turn them on. | ||
177 | */ | ||
178 | |||
179 | arch_enable_uart_fifo(); | ||
152 | } | 180 | } |
153 | 181 | ||
154 | 182 | ||
diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig index f0d54fdf88d4..2c8a2f5d75ff 100644 --- a/arch/arm/plat-s3c24xx/Kconfig +++ b/arch/arm/plat-s3c24xx/Kconfig | |||
@@ -41,6 +41,27 @@ config S3C24XX_PWM | |||
41 | Support for exporting the PWM timer blocks via the pwm device | 41 | Support for exporting the PWM timer blocks via the pwm device |
42 | system. | 42 | system. |
43 | 43 | ||
44 | |||
45 | # gpio configurations | ||
46 | |||
47 | config S3C24XX_GPIO_EXTRA | ||
48 | int | ||
49 | default 128 if S3C24XX_GPIO_EXTRA128 | ||
50 | default 64 if S3C24XX_GPIO_EXTRA64 | ||
51 | default 0 | ||
52 | |||
53 | config S3C24XX_GPIO_EXTRA64 | ||
54 | bool | ||
55 | help | ||
56 | Add an extra 64 gpio numbers to the available GPIO pool. This is | ||
57 | available for boards that need extra gpios for external devices. | ||
58 | |||
59 | config S3C24XX_GPIO_EXTRA128 | ||
60 | bool | ||
61 | help | ||
62 | Add an extra 128 gpio numbers to the available GPIO pool. This is | ||
63 | available for boards that need extra gpios for external devices. | ||
64 | |||
44 | config PM_SIMTEC | 65 | config PM_SIMTEC |
45 | bool | 66 | bool |
46 | help | 67 | help |
@@ -62,6 +83,13 @@ config S3C2410_DMA_DEBUG | |||
62 | Enable debugging output for the DMA code. This option sends info | 83 | Enable debugging output for the DMA code. This option sends info |
63 | to the kernel log, at priority KERN_DEBUG. | 84 | to the kernel log, at priority KERN_DEBUG. |
64 | 85 | ||
86 | config S3C24XX_ADC | ||
87 | bool "ADC common driver support" | ||
88 | help | ||
89 | Core support for the ADC block found in the S3C24XX SoC systems | ||
90 | for drivers such as the touchscreen and hwmon to use to share | ||
91 | this resource. | ||
92 | |||
65 | # SPI default pin configuration code | 93 | # SPI default pin configuration code |
66 | 94 | ||
67 | config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13 | 95 | config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13 |
diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile index 2a65ba7eb34d..a8cfdefc29e9 100644 --- a/arch/arm/plat-s3c24xx/Makefile +++ b/arch/arm/plat-s3c24xx/Makefile | |||
@@ -31,6 +31,7 @@ obj-$(CONFIG_PM) += sleep.o | |||
31 | obj-$(CONFIG_HAVE_PWM) += pwm.o | 31 | obj-$(CONFIG_HAVE_PWM) += pwm.o |
32 | obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o | 32 | obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o |
33 | obj-$(CONFIG_S3C2410_DMA) += dma.o | 33 | obj-$(CONFIG_S3C2410_DMA) += dma.o |
34 | obj-$(CONFIG_S3C24XX_ADC) += adc.o | ||
34 | 35 | ||
35 | # SPI gpio central GPIO functions | 36 | # SPI gpio central GPIO functions |
36 | 37 | ||
diff --git a/arch/arm/plat-s3c24xx/adc.c b/arch/arm/plat-s3c24xx/adc.c new file mode 100644 index 000000000000..9a5c767e0a42 --- /dev/null +++ b/arch/arm/plat-s3c24xx/adc.c | |||
@@ -0,0 +1,372 @@ | |||
1 | /* arch/arm/plat-s3c24xx/adc.c | ||
2 | * | ||
3 | * Copyright (c) 2008 Simtec Electronics | ||
4 | * http://armlinux.simtec.co.uk/ | ||
5 | * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org> | ||
6 | * | ||
7 | * S3C24XX ADC device core | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License. | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/list.h> | ||
18 | #include <linux/err.h> | ||
19 | #include <linux/clk.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/io.h> | ||
22 | |||
23 | #include <plat/regs-adc.h> | ||
24 | #include <plat/adc.h> | ||
25 | |||
26 | /* This driver is designed to control the usage of the ADC block between | ||
27 | * the touchscreen and any other drivers that may need to use it, such as | ||
28 | * the hwmon driver. | ||
29 | * | ||
30 | * Priority will be given to the touchscreen driver, but as this itself is | ||
31 | * rate limited it should not starve other requests which are processed in | ||
32 | * order that they are received. | ||
33 | * | ||
34 | * Each user registers to get a client block which uniquely identifies it | ||
35 | * and stores information such as the necessary functions to callback when | ||
36 | * action is required. | ||
37 | */ | ||
38 | |||
39 | struct s3c_adc_client { | ||
40 | struct platform_device *pdev; | ||
41 | struct list_head pend; | ||
42 | |||
43 | unsigned int nr_samples; | ||
44 | unsigned char is_ts; | ||
45 | unsigned char channel; | ||
46 | |||
47 | void (*select_cb)(unsigned selected); | ||
48 | void (*convert_cb)(unsigned val1, unsigned val2); | ||
49 | }; | ||
50 | |||
51 | struct adc_device { | ||
52 | struct platform_device *pdev; | ||
53 | struct platform_device *owner; | ||
54 | struct clk *clk; | ||
55 | struct s3c_adc_client *cur; | ||
56 | struct s3c_adc_client *ts_pend; | ||
57 | void __iomem *regs; | ||
58 | |||
59 | unsigned int prescale; | ||
60 | |||
61 | int irq; | ||
62 | }; | ||
63 | |||
64 | static struct adc_device *adc_dev; | ||
65 | |||
66 | static LIST_HEAD(adc_pending); | ||
67 | |||
68 | #define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg) | ||
69 | |||
70 | static inline void s3c_adc_convert(struct adc_device *adc) | ||
71 | { | ||
72 | unsigned con = readl(adc->regs + S3C2410_ADCCON); | ||
73 | |||
74 | con |= S3C2410_ADCCON_ENABLE_START; | ||
75 | writel(con, adc->regs + S3C2410_ADCCON); | ||
76 | } | ||
77 | |||
78 | static inline void s3c_adc_select(struct adc_device *adc, | ||
79 | struct s3c_adc_client *client) | ||
80 | { | ||
81 | unsigned con = readl(adc->regs + S3C2410_ADCCON); | ||
82 | |||
83 | client->select_cb(1); | ||
84 | |||
85 | con &= ~S3C2410_ADCCON_MUXMASK; | ||
86 | con &= ~S3C2410_ADCCON_STDBM; | ||
87 | con &= ~S3C2410_ADCCON_STARTMASK; | ||
88 | |||
89 | if (!client->is_ts) | ||
90 | con |= S3C2410_ADCCON_SELMUX(client->channel); | ||
91 | |||
92 | writel(con, adc->regs + S3C2410_ADCCON); | ||
93 | } | ||
94 | |||
95 | static void s3c_adc_dbgshow(struct adc_device *adc) | ||
96 | { | ||
97 | adc_dbg(adc, "CON=%08x, TSC=%08x, DLY=%08x\n", | ||
98 | readl(adc->regs + S3C2410_ADCCON), | ||
99 | readl(adc->regs + S3C2410_ADCTSC), | ||
100 | readl(adc->regs + S3C2410_ADCDLY)); | ||
101 | } | ||
102 | |||
103 | void s3c_adc_try(struct adc_device *adc) | ||
104 | { | ||
105 | struct s3c_adc_client *next = adc->ts_pend; | ||
106 | |||
107 | if (!next && !list_empty(&adc_pending)) { | ||
108 | next = list_first_entry(&adc_pending, | ||
109 | struct s3c_adc_client, pend); | ||
110 | list_del(&next->pend); | ||
111 | } else | ||
112 | adc->ts_pend = NULL; | ||
113 | |||
114 | if (next) { | ||
115 | adc_dbg(adc, "new client is %p\n", next); | ||
116 | adc->cur = next; | ||
117 | s3c_adc_select(adc, next); | ||
118 | s3c_adc_convert(adc); | ||
119 | s3c_adc_dbgshow(adc); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | int s3c_adc_start(struct s3c_adc_client *client, | ||
124 | unsigned int channel, unsigned int nr_samples) | ||
125 | { | ||
126 | struct adc_device *adc = adc_dev; | ||
127 | unsigned long flags; | ||
128 | |||
129 | if (!adc) { | ||
130 | printk(KERN_ERR "%s: failed to find adc\n", __func__); | ||
131 | return -EINVAL; | ||
132 | } | ||
133 | |||
134 | if (client->is_ts && adc->ts_pend) | ||
135 | return -EAGAIN; | ||
136 | |||
137 | local_irq_save(flags); | ||
138 | |||
139 | client->channel = channel; | ||
140 | client->nr_samples = nr_samples; | ||
141 | |||
142 | if (client->is_ts) | ||
143 | adc->ts_pend = client; | ||
144 | else | ||
145 | list_add_tail(&client->pend, &adc_pending); | ||
146 | |||
147 | if (!adc->cur) | ||
148 | s3c_adc_try(adc); | ||
149 | local_irq_restore(flags); | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | EXPORT_SYMBOL_GPL(s3c_adc_start); | ||
154 | |||
155 | static void s3c_adc_default_select(unsigned select) | ||
156 | { | ||
157 | } | ||
158 | |||
159 | struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev, | ||
160 | void (*select)(unsigned int selected), | ||
161 | void (*conv)(unsigned d0, unsigned d1), | ||
162 | unsigned int is_ts) | ||
163 | { | ||
164 | struct s3c_adc_client *client; | ||
165 | |||
166 | WARN_ON(!pdev); | ||
167 | WARN_ON(!conv); | ||
168 | |||
169 | if (!select) | ||
170 | select = s3c_adc_default_select; | ||
171 | |||
172 | if (!conv || !pdev) | ||
173 | return ERR_PTR(-EINVAL); | ||
174 | |||
175 | client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL); | ||
176 | if (!client) { | ||
177 | dev_err(&pdev->dev, "no memory for adc client\n"); | ||
178 | return ERR_PTR(-ENOMEM); | ||
179 | } | ||
180 | |||
181 | client->pdev = pdev; | ||
182 | client->is_ts = is_ts; | ||
183 | client->select_cb = select; | ||
184 | client->convert_cb = conv; | ||
185 | |||
186 | return client; | ||
187 | } | ||
188 | EXPORT_SYMBOL_GPL(s3c_adc_register); | ||
189 | |||
190 | void s3c_adc_release(struct s3c_adc_client *client) | ||
191 | { | ||
192 | /* We should really check that nothing is in progress. */ | ||
193 | kfree(client); | ||
194 | } | ||
195 | EXPORT_SYMBOL_GPL(s3c_adc_release); | ||
196 | |||
197 | static irqreturn_t s3c_adc_irq(int irq, void *pw) | ||
198 | { | ||
199 | struct adc_device *adc = pw; | ||
200 | struct s3c_adc_client *client = adc->cur; | ||
201 | unsigned long flags; | ||
202 | unsigned data0, data1; | ||
203 | |||
204 | if (!client) { | ||
205 | dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__); | ||
206 | return IRQ_HANDLED; | ||
207 | } | ||
208 | |||
209 | data0 = readl(adc->regs + S3C2410_ADCDAT0); | ||
210 | data1 = readl(adc->regs + S3C2410_ADCDAT1); | ||
211 | adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1); | ||
212 | |||
213 | (client->convert_cb)(data0 & 0x3ff, data1 & 0x3ff); | ||
214 | |||
215 | if (--client->nr_samples > 0) { | ||
216 | /* fire another conversion for this */ | ||
217 | |||
218 | client->select_cb(1); | ||
219 | s3c_adc_convert(adc); | ||
220 | } else { | ||
221 | local_irq_save(flags); | ||
222 | (client->select_cb)(0); | ||
223 | adc->cur = NULL; | ||
224 | |||
225 | s3c_adc_try(adc); | ||
226 | local_irq_restore(flags); | ||
227 | } | ||
228 | |||
229 | return IRQ_HANDLED; | ||
230 | } | ||
231 | |||
232 | static int s3c_adc_probe(struct platform_device *pdev) | ||
233 | { | ||
234 | struct device *dev = &pdev->dev; | ||
235 | struct adc_device *adc; | ||
236 | struct resource *regs; | ||
237 | int ret; | ||
238 | |||
239 | adc = kzalloc(sizeof(struct adc_device), GFP_KERNEL); | ||
240 | if (adc == NULL) { | ||
241 | dev_err(dev, "failed to allocate adc_device\n"); | ||
242 | return -ENOMEM; | ||
243 | } | ||
244 | |||
245 | adc->pdev = pdev; | ||
246 | adc->prescale = S3C2410_ADCCON_PRSCVL(49); | ||
247 | |||
248 | adc->irq = platform_get_irq(pdev, 1); | ||
249 | if (adc->irq <= 0) { | ||
250 | dev_err(dev, "failed to get adc irq\n"); | ||
251 | ret = -ENOENT; | ||
252 | goto err_alloc; | ||
253 | } | ||
254 | |||
255 | ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc); | ||
256 | if (ret < 0) { | ||
257 | dev_err(dev, "failed to attach adc irq\n"); | ||
258 | goto err_alloc; | ||
259 | } | ||
260 | |||
261 | adc->clk = clk_get(dev, "adc"); | ||
262 | if (IS_ERR(adc->clk)) { | ||
263 | dev_err(dev, "failed to get adc clock\n"); | ||
264 | ret = PTR_ERR(adc->clk); | ||
265 | goto err_irq; | ||
266 | } | ||
267 | |||
268 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
269 | if (!regs) { | ||
270 | dev_err(dev, "failed to find registers\n"); | ||
271 | ret = -ENXIO; | ||
272 | goto err_clk; | ||
273 | } | ||
274 | |||
275 | adc->regs = ioremap(regs->start, resource_size(regs)); | ||
276 | if (!adc->regs) { | ||
277 | dev_err(dev, "failed to map registers\n"); | ||
278 | ret = -ENXIO; | ||
279 | goto err_clk; | ||
280 | } | ||
281 | |||
282 | clk_enable(adc->clk); | ||
283 | |||
284 | writel(adc->prescale | S3C2410_ADCCON_PRSCEN, | ||
285 | adc->regs + S3C2410_ADCCON); | ||
286 | |||
287 | dev_info(dev, "attached adc driver\n"); | ||
288 | |||
289 | platform_set_drvdata(pdev, adc); | ||
290 | adc_dev = adc; | ||
291 | |||
292 | return 0; | ||
293 | |||
294 | err_clk: | ||
295 | clk_put(adc->clk); | ||
296 | |||
297 | err_irq: | ||
298 | free_irq(adc->irq, adc); | ||
299 | |||
300 | err_alloc: | ||
301 | kfree(adc); | ||
302 | return ret; | ||
303 | } | ||
304 | |||
305 | static int s3c_adc_remove(struct platform_device *pdev) | ||
306 | { | ||
307 | struct adc_device *adc = platform_get_drvdata(pdev); | ||
308 | |||
309 | iounmap(adc->regs); | ||
310 | free_irq(adc->irq, adc); | ||
311 | clk_disable(adc->clk); | ||
312 | clk_put(adc->clk); | ||
313 | kfree(adc); | ||
314 | |||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | #ifdef CONFIG_PM | ||
319 | static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) | ||
320 | { | ||
321 | struct adc_device *adc = platform_get_drvdata(pdev); | ||
322 | u32 con; | ||
323 | |||
324 | con = readl(adc->regs + S3C2410_ADCCON); | ||
325 | con |= S3C2410_ADCCON_STDBM; | ||
326 | writel(con, adc->regs + S3C2410_ADCCON); | ||
327 | |||
328 | clk_disable(adc->clk); | ||
329 | |||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | static int s3c_adc_resume(struct platform_device *pdev) | ||
334 | { | ||
335 | struct adc_device *adc = platform_get_drvdata(pdev); | ||
336 | |||
337 | clk_enable(adc->clk); | ||
338 | |||
339 | writel(adc->prescale | S3C2410_ADCCON_PRSCEN, | ||
340 | adc->regs + S3C2410_ADCCON); | ||
341 | |||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | #else | ||
346 | #define s3c_adc_suspend NULL | ||
347 | #define s3c_adc_resume NULL | ||
348 | #endif | ||
349 | |||
350 | static struct platform_driver s3c_adc_driver = { | ||
351 | .driver = { | ||
352 | .name = "s3c24xx-adc", | ||
353 | .owner = THIS_MODULE, | ||
354 | }, | ||
355 | .probe = s3c_adc_probe, | ||
356 | .remove = __devexit_p(s3c_adc_remove), | ||
357 | .suspend = s3c_adc_suspend, | ||
358 | .resume = s3c_adc_resume, | ||
359 | }; | ||
360 | |||
361 | static int __init adc_init(void) | ||
362 | { | ||
363 | int ret; | ||
364 | |||
365 | ret = platform_driver_register(&s3c_adc_driver); | ||
366 | if (ret) | ||
367 | printk(KERN_ERR "%s: failed to add adc driver\n", __func__); | ||
368 | |||
369 | return ret; | ||
370 | } | ||
371 | |||
372 | arch_initcall(adc_init); | ||
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c index ea445850ff47..9826efb91e48 100644 --- a/arch/arm/plat-s3c24xx/devs.c +++ b/arch/arm/plat-s3c24xx/devs.c | |||
@@ -372,12 +372,20 @@ static struct resource s3c_adc_resource[] = { | |||
372 | }; | 372 | }; |
373 | 373 | ||
374 | struct platform_device s3c_device_adc = { | 374 | struct platform_device s3c_device_adc = { |
375 | .name = "s3c2410-adc", | 375 | .name = "s3c24xx-adc", |
376 | .id = -1, | 376 | .id = -1, |
377 | .num_resources = ARRAY_SIZE(s3c_adc_resource), | 377 | .num_resources = ARRAY_SIZE(s3c_adc_resource), |
378 | .resource = s3c_adc_resource, | 378 | .resource = s3c_adc_resource, |
379 | }; | 379 | }; |
380 | 380 | ||
381 | /* HWMON */ | ||
382 | |||
383 | struct platform_device s3c_device_hwmon = { | ||
384 | .name = "s3c24xx-hwmon", | ||
385 | .id = -1, | ||
386 | .dev.parent = &s3c_device_adc.dev, | ||
387 | }; | ||
388 | |||
381 | /* SDI */ | 389 | /* SDI */ |
382 | 390 | ||
383 | static struct resource s3c_sdi_resource[] = { | 391 | static struct resource s3c_sdi_resource[] = { |