From b2a6cf3b1e099a724504ba16a4c9aea1eb0122a6 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:20 +0100 Subject: [ARM] S3C24XX: Default SPI pin configuration for SPI Add a set of default pin configuration routines for setting up the SPI gpio configuration when using the hardware SPI driver. Signed-off-by: Ben Dooks --- arch/arm/mach-s3c2410/include/mach/spi.h | 7 ++++++ arch/arm/plat-s3c24xx/Kconfig | 16 ++++++++++++ arch/arm/plat-s3c24xx/Makefile | 8 ++++++ arch/arm/plat-s3c24xx/spi-bus0-gpe11_12_13.c | 37 ++++++++++++++++++++++++++++ arch/arm/plat-s3c24xx/spi-bus1-gpg5_6_7.c | 37 ++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+) create mode 100644 arch/arm/plat-s3c24xx/spi-bus0-gpe11_12_13.c create mode 100644 arch/arm/plat-s3c24xx/spi-bus1-gpg5_6_7.c (limited to 'arch/arm') diff --git a/arch/arm/mach-s3c2410/include/mach/spi.h b/arch/arm/mach-s3c2410/include/mach/spi.h index 46d46f5b99f..774f3adfe8a 100644 --- a/arch/arm/mach-s3c2410/include/mach/spi.h +++ b/arch/arm/mach-s3c2410/include/mach/spi.h @@ -22,5 +22,12 @@ struct s3c2410_spi_info { void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol); }; +/* Standard setup / suspend routines for SPI GPIO pins. */ + +extern void s3c24xx_spi_gpiocfg_bus0_gpe11_12_13(struct s3c2410_spi_info *spi, + int enable); + +extern void s3c24xx_spi_gpiocfg_bus1_gpg5_6_7(struct s3c2410_spi_info *spi, + int enable); #endif /* __ASM_ARCH_SPI_H */ diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig index 0af3872fb76..48ebf355a45 100644 --- a/arch/arm/plat-s3c24xx/Kconfig +++ b/arch/arm/plat-s3c24xx/Kconfig @@ -49,6 +49,22 @@ config S3C2410_DMA_DEBUG Enable debugging output for the DMA code. This option sends info to the kernel log, at priority KERN_DEBUG. +# SPI default pin configuration code + +config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13 + bool + help + SPI GPIO configuration code for BUS0 when connected to + GPE11, GPE12 and GPE13. + +config S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7 + bool + help + SPI GPIO configuration code for BUS 1 when connected to + GPG5, GPG6 and GPG7. + +# common code for s3c24xx based machines, such as the SMDKs. + config MACH_SMDK bool help diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile index d82767b2b83..6b574016dc4 100644 --- a/arch/arm/plat-s3c24xx/Makefile +++ b/arch/arm/plat-s3c24xx/Makefile @@ -31,4 +31,12 @@ obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_PM) += sleep.o obj-$(CONFIG_HAVE_PWM) += pwm.o obj-$(CONFIG_S3C2410_DMA) += dma.o + +# SPI gpio central GPIO functions + +obj-$(CONFIG_S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13) += spi-bus0-gpe11_12_13.o +obj-$(CONFIG_S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7) += spi-bus1-gpg5_6_7.o + +# machine common support + obj-$(CONFIG_MACH_SMDK) += common-smdk.o diff --git a/arch/arm/plat-s3c24xx/spi-bus0-gpe11_12_13.c b/arch/arm/plat-s3c24xx/spi-bus0-gpe11_12_13.c new file mode 100644 index 00000000000..8b403cbb53d --- /dev/null +++ b/arch/arm/plat-s3c24xx/spi-bus0-gpe11_12_13.c @@ -0,0 +1,37 @@ +/* linux/arch/arm/plat-s3c24xx/spi-bus0-gpe11_12_13.c + * + * Copyright (c) 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C24XX SPI - gpio configuration for bus 0 on gpe11,12,13 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. +*/ + +#include + +#include + +#include +#include + +void s3c24xx_spi_gpiocfg_bus0_gpe11_12_13(struct s3c2410_spi_info *spi, + int enable) +{ + if (enable) { + s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0); + s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0); + s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0); + s3c2410_gpio_pullup(S3C2410_GPE11, 0); + s3c2410_gpio_pullup(S3C2410_GPE13, 0); + } else { + s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPIO_INPUT); + s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPIO_INPUT); + s3c2410_gpio_pullup(S3C2410_GPE11, 1); + s3c2410_gpio_pullup(S3C2410_GPE12, 1); + s3c2410_gpio_pullup(S3C2410_GPE13, 1); + } +} diff --git a/arch/arm/plat-s3c24xx/spi-bus1-gpg5_6_7.c b/arch/arm/plat-s3c24xx/spi-bus1-gpg5_6_7.c new file mode 100644 index 00000000000..8fccd4e549f --- /dev/null +++ b/arch/arm/plat-s3c24xx/spi-bus1-gpg5_6_7.c @@ -0,0 +1,37 @@ +/* linux/arch/arm/plat-s3c24xx/spi-bus0-gpg5_6_7.c + * + * Copyright (c) 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C24XX SPI - gpio configuration for bus 1 on gpg5,6,7 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. +*/ + +#include + +#include + +#include +#include + +void s3c24xx_spi_gpiocfg_bus1_gpg5_6_7(struct s3c2410_spi_info *spi, + int enable) +{ + if (enable) { + s3c2410_gpio_cfgpin(S3C2410_GPG7, S3C2410_GPG7_SPICLK1); + s3c2410_gpio_cfgpin(S3C2410_GPG6, S3C2410_GPG6_SPIMOSI1); + s3c2410_gpio_cfgpin(S3C2410_GPG5, S3C2410_GPG5_SPIMISO1); + s3c2410_gpio_pullup(S3C2410_GPG5, 0); + s3c2410_gpio_pullup(S3C2410_GPG6, 0); + } else { + s3c2410_gpio_cfgpin(S3C2410_GPG7, S3C2410_GPIO_INPUT); + s3c2410_gpio_cfgpin(S3C2410_GPG5, S3C2410_GPIO_INPUT); + s3c2410_gpio_pullup(S3C2410_GPG5, 1); + s3c2410_gpio_pullup(S3C2410_GPG6, 1); + s3c2410_gpio_pullup(S3C2410_GPG7, 1); + } +} -- cgit v1.2.3 From 7f565ecc33c5536a76cd73847874df5f804a49aa Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:21 +0100 Subject: [ARM] S3C: Add for initial cpufreq definitions This is the header file that defines the basic cpu frequency scalling support for the Samsung S3C series of SoC. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/include/plat/cpu-freq.h | 94 +++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 arch/arm/plat-s3c/include/plat/cpu-freq.h (limited to 'arch/arm') diff --git a/arch/arm/plat-s3c/include/plat/cpu-freq.h b/arch/arm/plat-s3c/include/plat/cpu-freq.h new file mode 100644 index 00000000000..c86a13307e9 --- /dev/null +++ b/arch/arm/plat-s3c/include/plat/cpu-freq.h @@ -0,0 +1,94 @@ +/* arch/arm/plat-s3c/include/plat/cpu-freq.h + * + * Copyright (c) 2006,2007 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C CPU frequency scaling support - driver and board + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include + +struct s3c_cpufreq_info; +struct s3c_cpufreq_board; +struct s3c_iotimings; + +struct s3c_freq { + unsigned long fclk; + unsigned long armclk; + unsigned long hclk_tns; /* in 10ths of ns */ + unsigned long hclk; + unsigned long pclk; +}; + +/* wrapper 'struct cpufreq_freqs' so that any drivers receiving the + * notification can use this information that is not provided by just + * having the core frequency alone. + */ + +struct s3c_cpufreq_freqs { + struct cpufreq_freqs freqs; + struct s3c_freq old; + struct s3c_freq new; +}; + +#define to_s3c_cpufreq(_cf) container_of(_cf, struct s3c_cpufreq_freqs, freqs) + +struct s3c_clkdivs { + int p_divisor; /* fclk / pclk */ + int h_divisor; /* fclk / hclk */ + int arm_divisor; /* not all cpus have this. */ + unsigned char dvs; /* using dvs mode to arm. */ +}; + +#define PLLVAL(_m, _p, _s) (((_m) << 12) | ((_p) << 4) | (_s)) + +struct s3c_pllval { + unsigned long freq; + unsigned long pll_reg; +}; + +struct s3c_cpufreq_config { + struct s3c_freq freq; + struct s3c_pllval pll; + struct s3c_clkdivs divs; + struct s3c_cpufreq_info *info; /* for core, not drivers */ + struct s3c_cpufreq_board *board; +}; + +/* s3c_cpufreq_board + * + * per-board configuraton information, such as memory refresh and + * how to initialise IO timings. + */ +struct s3c_cpufreq_board { + unsigned int refresh; /* refresh period in ns */ + unsigned int auto_io:1; /* automatically init io timings. */ + unsigned int need_io:1; /* set if needs io timing support. */ + + /* any non-zero field in here is taken as an upper limit. */ + struct s3c_freq max; /* frequency limits */ +}; + +/* Things depending on frequency scaling. */ +#ifdef CONFIG_CPU_FREQ_S3C +#define __init_or_cpufreq +#else +#define __init_or_cpufreq __init +#endif + +/* Board functions */ + +#ifdef CONFIG_CPU_FREQ_S3C +extern int s3c_cpufreq_setboard(struct s3c_cpufreq_board *board); +#else + +static inline int s3c_cpufreq_setboard(struct s3c_cpufreq_board *board) +{ + return 0; +} +#endif /* CONFIG_CPU_FREQ_S3C */ -- cgit v1.2.3 From 1b3ba68884d192f42fcf4352e1ac7174facc96af Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:22 +0100 Subject: [ARM] S3C2410: Move base clock code to plat-s3c24xx Move the S3C2410 base clock list to arch/arm/plat-s3c24xx as this code is common to the S3C2410, S3C2440 and S3C2442 cpus. Signed-off-by: Ben Dooks --- arch/arm/mach-s3c2410/Kconfig | 5 - arch/arm/mach-s3c2410/Makefile | 1 - arch/arm/mach-s3c2410/clock.c | 276 ---------------------------------- arch/arm/plat-s3c24xx/Kconfig | 8 + arch/arm/plat-s3c24xx/Makefile | 1 + arch/arm/plat-s3c24xx/s3c2410-clock.c | 276 ++++++++++++++++++++++++++++++++++ 6 files changed, 285 insertions(+), 282 deletions(-) delete mode 100644 arch/arm/mach-s3c2410/clock.c create mode 100644 arch/arm/plat-s3c24xx/s3c2410-clock.c (limited to 'arch/arm') diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig index 7315569fbfd..5453aff6048 100644 --- a/arch/arm/mach-s3c2410/Kconfig +++ b/arch/arm/mach-s3c2410/Kconfig @@ -33,11 +33,6 @@ config S3C2410_GPIO help GPIO code for S3C2410 and similar processors -config S3C2410_CLOCK - bool - help - Clock code for the S3C2410, and similar processors - config SIMTEC_NOR bool help diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile index 00f31f8c4e7..fca02f82711 100644 --- a/arch/arm/mach-s3c2410/Makefile +++ b/arch/arm/mach-s3c2410/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_CPU_S3C2410_DMA) += dma.o obj-$(CONFIG_CPU_S3C2410_DMA) += dma.o obj-$(CONFIG_S3C2410_PM) += pm.o sleep.o obj-$(CONFIG_S3C2410_GPIO) += gpio.o -obj-$(CONFIG_S3C2410_CLOCK) += clock.o # Machine support diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c deleted file mode 100644 index 4e07943c1e2..00000000000 --- a/arch/arm/mach-s3c2410/clock.c +++ /dev/null @@ -1,276 +0,0 @@ -/* linux/arch/arm/mach-s3c2410/clock.c - * - * Copyright (c) 2006 Simtec Electronics - * Ben Dooks - * - * S3C2410,S3C2440,S3C2442 Clock control support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#include -#include -#include - -int s3c2410_clkcon_enable(struct clk *clk, int enable) -{ - unsigned int clocks = clk->ctrlbit; - unsigned long clkcon; - - clkcon = __raw_readl(S3C2410_CLKCON); - - if (enable) - clkcon |= clocks; - else - clkcon &= ~clocks; - - /* ensure none of the special function bits set */ - clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER); - - __raw_writel(clkcon, S3C2410_CLKCON); - - return 0; -} - -static int s3c2410_upll_enable(struct clk *clk, int enable) -{ - unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); - unsigned long orig = clkslow; - - if (enable) - clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF; - else - clkslow |= S3C2410_CLKSLOW_UCLK_OFF; - - __raw_writel(clkslow, S3C2410_CLKSLOW); - - /* if we started the UPLL, then allow to settle */ - - if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF)) - udelay(200); - - return 0; -} - -/* standard clock definitions */ - -static struct clk init_clocks_disable[] = { - { - .name = "nand", - .id = -1, - .parent = &clk_h, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_NAND, - }, { - .name = "sdi", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_SDI, - }, { - .name = "adc", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_ADC, - }, { - .name = "i2c", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_IIC, - }, { - .name = "iis", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_IIS, - }, { - .name = "spi", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_SPI, - } -}; - -static struct clk init_clocks[] = { - { - .name = "lcd", - .id = -1, - .parent = &clk_h, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_LCDC, - }, { - .name = "gpio", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_GPIO, - }, { - .name = "usb-host", - .id = -1, - .parent = &clk_h, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_USBH, - }, { - .name = "usb-device", - .id = -1, - .parent = &clk_h, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_USBD, - }, { - .name = "timers", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_PWMT, - }, { - .name = "uart", - .id = 0, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_UART0, - }, { - .name = "uart", - .id = 1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_UART1, - }, { - .name = "uart", - .id = 2, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_UART2, - }, { - .name = "rtc", - .id = -1, - .parent = &clk_p, - .enable = s3c2410_clkcon_enable, - .ctrlbit = S3C2410_CLKCON_RTC, - }, { - .name = "watchdog", - .id = -1, - .parent = &clk_p, - .ctrlbit = 0, - }, { - .name = "usb-bus-host", - .id = -1, - .parent = &clk_usb_bus, - }, { - .name = "usb-bus-gadget", - .id = -1, - .parent = &clk_usb_bus, - }, -}; - -/* s3c2410_baseclk_add() - * - * Add all the clocks used by the s3c2410 or compatible CPUs - * such as the S3C2440 and S3C2442. - * - * We cannot use a system device as we are needed before any - * of the init-calls that initialise the devices are actually - * done. -*/ - -int __init s3c2410_baseclk_add(void) -{ - unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); - unsigned long clkcon = __raw_readl(S3C2410_CLKCON); - struct clk *clkp; - struct clk *xtal; - int ret; - int ptr; - - clk_upll.enable = s3c2410_upll_enable; - - if (s3c24xx_register_clock(&clk_usb_bus) < 0) - printk(KERN_ERR "failed to register usb bus clock\n"); - - /* register clocks from clock array */ - - clkp = init_clocks; - for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { - /* ensure that we note the clock state */ - - clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0; - - ret = s3c24xx_register_clock(clkp); - if (ret < 0) { - printk(KERN_ERR "Failed to register clock %s (%d)\n", - clkp->name, ret); - } - } - - /* We must be careful disabling the clocks we are not intending to - * be using at boot time, as subsystems such as the LCD which do - * their own DMA requests to the bus can cause the system to lockup - * if they where in the middle of requesting bus access. - * - * Disabling the LCD clock if the LCD is active is very dangerous, - * and therefore the bootloader should be careful to not enable - * the LCD clock if it is not needed. - */ - - /* install (and disable) the clocks we do not need immediately */ - - clkp = init_clocks_disable; - for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) { - - ret = s3c24xx_register_clock(clkp); - if (ret < 0) { - printk(KERN_ERR "Failed to register clock %s (%d)\n", - clkp->name, ret); - } - - s3c2410_clkcon_enable(clkp, 0); - } - - /* show the clock-slow value */ - - xtal = clk_get(NULL, "xtal"); - - printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n", - print_mhz(clk_get_rate(xtal) / - ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))), - (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast", - (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on", - (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on"); - - return 0; -} diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig index 48ebf355a45..2bc4b10f522 100644 --- a/arch/arm/plat-s3c24xx/Kconfig +++ b/arch/arm/plat-s3c24xx/Kconfig @@ -15,6 +15,14 @@ config PLAT_S3C24XX if PLAT_S3C24XX +# code that is shared between a number of the s3c24xx implementations + +config S3C2410_CLOCK + bool + help + Clock code for the S3C2410, and similar processors which + is currently includes the S3C2410, S3C2440, S3C2442. + config CPU_S3C244X bool depends on ARCH_S3C2410 && (CPU_S3C2440 || CPU_S3C2442) diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile index 6b574016dc4..095a6c2324d 100644 --- a/arch/arm/plat-s3c24xx/Makefile +++ b/arch/arm/plat-s3c24xx/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_PM_SIMTEC) += pm-simtec.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_PM) += sleep.o obj-$(CONFIG_HAVE_PWM) += pwm.o +obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o obj-$(CONFIG_S3C2410_DMA) += dma.o # SPI gpio central GPIO functions diff --git a/arch/arm/plat-s3c24xx/s3c2410-clock.c b/arch/arm/plat-s3c24xx/s3c2410-clock.c new file mode 100644 index 00000000000..4e07943c1e2 --- /dev/null +++ b/arch/arm/plat-s3c24xx/s3c2410-clock.c @@ -0,0 +1,276 @@ +/* linux/arch/arm/mach-s3c2410/clock.c + * + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks + * + * S3C2410,S3C2440,S3C2442 Clock control support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +int s3c2410_clkcon_enable(struct clk *clk, int enable) +{ + unsigned int clocks = clk->ctrlbit; + unsigned long clkcon; + + clkcon = __raw_readl(S3C2410_CLKCON); + + if (enable) + clkcon |= clocks; + else + clkcon &= ~clocks; + + /* ensure none of the special function bits set */ + clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER); + + __raw_writel(clkcon, S3C2410_CLKCON); + + return 0; +} + +static int s3c2410_upll_enable(struct clk *clk, int enable) +{ + unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); + unsigned long orig = clkslow; + + if (enable) + clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF; + else + clkslow |= S3C2410_CLKSLOW_UCLK_OFF; + + __raw_writel(clkslow, S3C2410_CLKSLOW); + + /* if we started the UPLL, then allow to settle */ + + if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF)) + udelay(200); + + return 0; +} + +/* standard clock definitions */ + +static struct clk init_clocks_disable[] = { + { + .name = "nand", + .id = -1, + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_NAND, + }, { + .name = "sdi", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_SDI, + }, { + .name = "adc", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_ADC, + }, { + .name = "i2c", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_IIC, + }, { + .name = "iis", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_IIS, + }, { + .name = "spi", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_SPI, + } +}; + +static struct clk init_clocks[] = { + { + .name = "lcd", + .id = -1, + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_LCDC, + }, { + .name = "gpio", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_GPIO, + }, { + .name = "usb-host", + .id = -1, + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_USBH, + }, { + .name = "usb-device", + .id = -1, + .parent = &clk_h, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_USBD, + }, { + .name = "timers", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_PWMT, + }, { + .name = "uart", + .id = 0, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART0, + }, { + .name = "uart", + .id = 1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART1, + }, { + .name = "uart", + .id = 2, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_UART2, + }, { + .name = "rtc", + .id = -1, + .parent = &clk_p, + .enable = s3c2410_clkcon_enable, + .ctrlbit = S3C2410_CLKCON_RTC, + }, { + .name = "watchdog", + .id = -1, + .parent = &clk_p, + .ctrlbit = 0, + }, { + .name = "usb-bus-host", + .id = -1, + .parent = &clk_usb_bus, + }, { + .name = "usb-bus-gadget", + .id = -1, + .parent = &clk_usb_bus, + }, +}; + +/* s3c2410_baseclk_add() + * + * Add all the clocks used by the s3c2410 or compatible CPUs + * such as the S3C2440 and S3C2442. + * + * We cannot use a system device as we are needed before any + * of the init-calls that initialise the devices are actually + * done. +*/ + +int __init s3c2410_baseclk_add(void) +{ + unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); + unsigned long clkcon = __raw_readl(S3C2410_CLKCON); + struct clk *clkp; + struct clk *xtal; + int ret; + int ptr; + + clk_upll.enable = s3c2410_upll_enable; + + if (s3c24xx_register_clock(&clk_usb_bus) < 0) + printk(KERN_ERR "failed to register usb bus clock\n"); + + /* register clocks from clock array */ + + clkp = init_clocks; + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { + /* ensure that we note the clock state */ + + clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0; + + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } + + /* We must be careful disabling the clocks we are not intending to + * be using at boot time, as subsystems such as the LCD which do + * their own DMA requests to the bus can cause the system to lockup + * if they where in the middle of requesting bus access. + * + * Disabling the LCD clock if the LCD is active is very dangerous, + * and therefore the bootloader should be careful to not enable + * the LCD clock if it is not needed. + */ + + /* install (and disable) the clocks we do not need immediately */ + + clkp = init_clocks_disable; + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) { + + ret = s3c24xx_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + + s3c2410_clkcon_enable(clkp, 0); + } + + /* show the clock-slow value */ + + xtal = clk_get(NULL, "xtal"); + + printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n", + print_mhz(clk_get_rate(xtal) / + ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))), + (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast", + (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on", + (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on"); + + return 0; +} -- cgit v1.2.3 From 95f42b3911fc3af2a4d4d8af966583122a94e42c Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:27 +0100 Subject: [ARM] S3C: Make common Move to arch/arm/plat-s3c/include/mach/timex.h so it can be the default for all S3C based architectures. Signed-off-by: Ben Dooks --- arch/arm/mach-s3c2410/include/mach/timex.h | 26 -------------------------- arch/arm/plat-s3c/include/mach/timex.h | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 26 deletions(-) delete mode 100644 arch/arm/mach-s3c2410/include/mach/timex.h create mode 100644 arch/arm/plat-s3c/include/mach/timex.h (limited to 'arch/arm') diff --git a/arch/arm/mach-s3c2410/include/mach/timex.h b/arch/arm/mach-s3c2410/include/mach/timex.h deleted file mode 100644 index 2a425ed0a7e..00000000000 --- a/arch/arm/mach-s3c2410/include/mach/timex.h +++ /dev/null @@ -1,26 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/timex.h - * - * Copyright (c) 2003-2005 Simtec Electronics - * Ben Dooks - * - * S3C2410 - time parameters - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __ASM_ARCH_TIMEX_H -#define __ASM_ARCH_TIMEX_H - -/* CLOCK_TICK_RATE needs to be evaluatable by the cpp, so making it - * a variable is useless. It seems as long as we make our timers an - * exact multiple of HZ, any value that makes a 1->1 correspondence - * for the time conversion functions to/from jiffies is acceptable. -*/ - - -#define CLOCK_TICK_RATE 12000000 - - -#endif /* __ASM_ARCH_TIMEX_H */ diff --git a/arch/arm/plat-s3c/include/mach/timex.h b/arch/arm/plat-s3c/include/mach/timex.h new file mode 100644 index 00000000000..2a425ed0a7e --- /dev/null +++ b/arch/arm/plat-s3c/include/mach/timex.h @@ -0,0 +1,26 @@ +/* arch/arm/mach-s3c2410/include/mach/timex.h + * + * Copyright (c) 2003-2005 Simtec Electronics + * Ben Dooks + * + * S3C2410 - time parameters + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_TIMEX_H +#define __ASM_ARCH_TIMEX_H + +/* CLOCK_TICK_RATE needs to be evaluatable by the cpp, so making it + * a variable is useless. It seems as long as we make our timers an + * exact multiple of HZ, any value that makes a 1->1 correspondence + * for the time conversion functions to/from jiffies is acceptable. +*/ + + +#define CLOCK_TICK_RATE 12000000 + + +#endif /* __ASM_ARCH_TIMEX_H */ -- cgit v1.2.3 From 112f4a708d7bb84298d1b34c734f90d027e7ec01 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:28 +0100 Subject: [ARM] S3C24XX: Move vmalloc.h to plat-s3c vmalloc.h is common across all the current s3c platforms, so move it to arch/arm/plat-s3c/include/mach to be used for all the targets. Signed-off-by: Ben Dooks --- arch/arm/mach-s3c2410/include/mach/vmalloc.h | 20 -------------------- arch/arm/plat-s3c/include/mach/vmalloc.h | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 20 deletions(-) delete mode 100644 arch/arm/mach-s3c2410/include/mach/vmalloc.h create mode 100644 arch/arm/plat-s3c/include/mach/vmalloc.h (limited to 'arch/arm') diff --git a/arch/arm/mach-s3c2410/include/mach/vmalloc.h b/arch/arm/mach-s3c2410/include/mach/vmalloc.h deleted file mode 100644 index 315b0078a34..00000000000 --- a/arch/arm/mach-s3c2410/include/mach/vmalloc.h +++ /dev/null @@ -1,20 +0,0 @@ -/* arch/arm/mach-s3c2410/include/mach/vmalloc.h - * - * from arch/arm/mach-iop3xx/include/mach/vmalloc.h - * - * Copyright (c) 2003 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * S3C2410 vmalloc definition -*/ - -#ifndef __ASM_ARCH_VMALLOC_H -#define __ASM_ARCH_VMALLOC_H - -#define VMALLOC_END (0xE0000000) - -#endif /* __ASM_ARCH_VMALLOC_H */ diff --git a/arch/arm/plat-s3c/include/mach/vmalloc.h b/arch/arm/plat-s3c/include/mach/vmalloc.h new file mode 100644 index 00000000000..bfd2ca6e307 --- /dev/null +++ b/arch/arm/plat-s3c/include/mach/vmalloc.h @@ -0,0 +1,20 @@ +/* arch/arm/plat-s3c/include/mach/vmalloc.h + * + * from arch/arm/mach-iop3xx/include/mach/vmalloc.h + * + * Copyright (c) 2003 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * S3C2410 vmalloc definition +*/ + +#ifndef __ASM_ARCH_VMALLOC_H +#define __ASM_ARCH_VMALLOC_H + +#define VMALLOC_END (0xE0000000) + +#endif /* __ASM_ARCH_VMALLOC_H */ -- cgit v1.2.3 From 2cd493fc10a5ad628dce8471ae72cf0882506735 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:29 +0100 Subject: [ARM] S3C24XX: Add default header Add a default header for for systems such as the S3C24A0 which do not need any of the complex code that the S3C2410 uses. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/include/mach/io.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 arch/arm/plat-s3c/include/mach/io.h (limited to 'arch/arm') diff --git a/arch/arm/plat-s3c/include/mach/io.h b/arch/arm/plat-s3c/include/mach/io.h new file mode 100644 index 00000000000..10d28d66ace --- /dev/null +++ b/arch/arm/plat-s3c/include/mach/io.h @@ -0,0 +1,18 @@ +/* arch/arm/plat-s3c/include/mach/io.h + * + * Copyright 2008 Simtec Electronics + * Ben Dooks + * + * Default IO routines for plat-s3c based systems, such as S3C24A0 + */ + +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +/* No current ISA/PCI bus support. */ +#define __io(a) ((void __iomem *)(a)) +#define __mem_pci(a) (a) + +#define IO_SPACE_LIMIT (0xFFFFFFFF) + +#endif -- cgit v1.2.3 From 74b265d4e0555b9fc9cc75eb8876140ecf8c6b8a Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:31 +0100 Subject: [ARM] S3C24XX: Move initialisation code to arch/arm/plat-s3c We need to add plat-s3c to the build to get the headers that will go in here once moved from include/asm-arm so we may as well put some useful common s3c code in here to stop the errors generated form having nothing built. The cpu setup is now passed the cpu idcode and the table of supported cpus to s3c_init_cpu() to abstract the cpu identification out of the initial io setup. As well as moving the cpu initialisation code, we move the map of the board specific items up to the calling code as none of the map_io() functions actually do anything other than pass this to iotable_init(). This patch does not rename any of the init functions that will be common to s3c24xx and any other s3c architectures as this can be done at a later date as it will touch all the board support files which use functions such as s3c24xx_init_clocks() and s3c24xx_init_uarts(). Note, the header arch/arm/plat-s3c24xx/include/plat/cpu.h still has functions that are used by both the cpu and board initialisation functions. This means that each board has definitions specific to the cpu support included and the vice-versa. Signed-off-by: Ben Dooks --- arch/arm/mach-s3c2410/s3c2410.c | 5 +- arch/arm/mach-s3c2412/s3c2412.c | 3 +- arch/arm/mach-s3c2443/s3c2443.c | 3 +- arch/arm/plat-s3c/Makefile | 15 ++- arch/arm/plat-s3c/init.c | 161 +++++++++++++++++++++++++++ arch/arm/plat-s3c24xx/cpu.c | 141 +---------------------- arch/arm/plat-s3c24xx/include/plat/cpu.h | 15 +++ arch/arm/plat-s3c24xx/include/plat/s3c2400.h | 2 +- arch/arm/plat-s3c24xx/include/plat/s3c2410.h | 2 +- arch/arm/plat-s3c24xx/include/plat/s3c2412.h | 2 +- arch/arm/plat-s3c24xx/include/plat/s3c2443.h | 2 +- arch/arm/plat-s3c24xx/s3c244x.c | 3 +- arch/arm/plat-s3c24xx/s3c244x.h | 2 +- 13 files changed, 201 insertions(+), 155 deletions(-) create mode 100644 arch/arm/plat-s3c/init.c (limited to 'arch/arm') diff --git a/arch/arm/mach-s3c2410/s3c2410.c b/arch/arm/mach-s3c2410/s3c2410.c index ac79b536c4c..1db39c609d7 100644 --- a/arch/arm/mach-s3c2410/s3c2410.c +++ b/arch/arm/mach-s3c2410/s3c2410.c @@ -59,12 +59,9 @@ void __init s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no) * machine specific initialisation. */ -void __init s3c2410_map_io(struct map_desc *mach_desc, int mach_size) +void __init s3c2410_map_io(void) { - /* register our io-tables */ - iotable_init(s3c2410_iodesc, ARRAY_SIZE(s3c2410_iodesc)); - iotable_init(mach_desc, mach_size); } void __init s3c2410_init_clocks(int xtal) diff --git a/arch/arm/mach-s3c2412/s3c2412.c b/arch/arm/mach-s3c2412/s3c2412.c index a086818e117..22d22a03578 100644 --- a/arch/arm/mach-s3c2412/s3c2412.c +++ b/arch/arm/mach-s3c2412/s3c2412.c @@ -136,7 +136,7 @@ static void s3c2412_hard_reset(void) * machine specific initialisation. */ -void __init s3c2412_map_io(struct map_desc *mach_desc, int mach_size) +void __init s3c2412_map_io(void) { /* move base of IO */ @@ -153,7 +153,6 @@ void __init s3c2412_map_io(struct map_desc *mach_desc, int mach_size) /* register our io-tables */ iotable_init(s3c2412_iodesc, ARRAY_SIZE(s3c2412_iodesc)); - iotable_init(mach_desc, mach_size); } void __init s3c2412_init_clocks(int xtal) diff --git a/arch/arm/mach-s3c2443/s3c2443.c b/arch/arm/mach-s3c2443/s3c2443.c index bbeddf9ddcb..ce2ec329893 100644 --- a/arch/arm/mach-s3c2443/s3c2443.c +++ b/arch/arm/mach-s3c2443/s3c2443.c @@ -81,10 +81,9 @@ void __init s3c2443_init_uarts(struct s3c2410_uartcfg *cfg, int no) * machine specific initialisation. */ -void __init s3c2443_map_io(struct map_desc *mach_desc, int mach_size) +void __init s3c2443_map_io(void) { iotable_init(s3c2443_iodesc, ARRAY_SIZE(s3c2443_iodesc)); - iotable_init(mach_desc, mach_size); } /* need to register class before we actually register the device, and diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index f03d7b35ba3..f7c8824ef80 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -1,3 +1,14 @@ -# dummy makefile, currently just including asm/arm/plat-s3c/include/plat +# arch/arm/plat-s3c/Makefile +# +# Copyright 2008 Simtec Electronics +# +# Licensed under GPLv2 -obj-n := dummy.o +obj-y := +obj-m := +obj-n := +obj- := + +# Core support for all Samsung SoCs + +obj-y += init.o diff --git a/arch/arm/plat-s3c/init.c b/arch/arm/plat-s3c/init.c new file mode 100644 index 00000000000..85f086ee930 --- /dev/null +++ b/arch/arm/plat-s3c/init.c @@ -0,0 +1,161 @@ +/* linux/arch/arm/plat-s3c/init.c + * + * Copyright (c) 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C series CPU initialisation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +static struct cpu_table *cpu; + +static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode, + struct cpu_table *tab, + unsigned int count) +{ + for (; count != 0; count--, tab++) { + if ((idcode & tab->idmask) == tab->idcode) + return tab; + } + + return NULL; +} + +void __init s3c_init_cpu(unsigned long idcode, + struct cpu_table *cputab, unsigned int cputab_size) +{ + cpu = s3c_lookup_cpu(idcode, cputab, cputab_size); + + if (cpu == NULL) { + printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode); + panic("Unknown S3C24XX CPU"); + } + + printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode); + + if (cpu->map_io == NULL || cpu->init == NULL) { + printk(KERN_ERR "CPU %s support not enabled\n", cpu->name); + panic("Unsupported Samsung CPU"); + } + + cpu->map_io(); +} + +/* s3c24xx_init_clocks + * + * Initialise the clock subsystem and associated information from the + * given master crystal value. + * + * xtal = 0 -> use default PLL crystal value (normally 12MHz) + * != 0 -> PLL crystal value in Hz +*/ + +void __init s3c24xx_init_clocks(int xtal) +{ + if (xtal == 0) + xtal = 12*1000*1000; + + if (cpu == NULL) + panic("s3c24xx_init_clocks: no cpu setup?\n"); + + if (cpu->init_clocks == NULL) + panic("s3c24xx_init_clocks: cpu has no clock init\n"); + else + (cpu->init_clocks)(xtal); +} + +/* uart management */ + +static int nr_uarts __initdata = 0; + +static struct s3c2410_uartcfg uart_cfgs[3]; + +/* s3c24xx_init_uartdevs + * + * copy the specified platform data and configuration into our central + * set of devices, before the data is thrown away after the init process. + * + * This also fills in the array passed to the serial driver for the + * early initialisation of the console. +*/ + +void __init s3c24xx_init_uartdevs(char *name, + struct s3c24xx_uart_resources *res, + struct s3c2410_uartcfg *cfg, int no) +{ + struct platform_device *platdev; + struct s3c2410_uartcfg *cfgptr = uart_cfgs; + struct s3c24xx_uart_resources *resp; + int uart; + + memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no); + + for (uart = 0; uart < no; uart++, cfg++, cfgptr++) { + platdev = s3c24xx_uart_src[cfgptr->hwport]; + + resp = res + cfgptr->hwport; + + s3c24xx_uart_devs[uart] = platdev; + + platdev->name = name; + platdev->resource = resp->resources; + platdev->num_resources = resp->nr_resources; + + platdev->dev.platform_data = cfgptr; + } + + nr_uarts = no; +} + +void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) +{ + if (cpu == NULL) + return; + + if (cpu->init_uarts == NULL) { + printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n"); + } else + (cpu->init_uarts)(cfg, no); +} + +static int __init s3c_arch_init(void) +{ + int ret; + + // do the correct init for cpu + + if (cpu == NULL) + panic("s3c_arch_init: NULL cpu\n"); + + ret = (cpu->init)(); + if (ret != 0) + return ret; + + ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts); + return ret; +} + +arch_initcall(s3c_arch_init); diff --git a/arch/arm/plat-s3c24xx/cpu.c b/arch/arm/plat-s3c24xx/cpu.c index 22a329513c0..7a0f494c93b 100644 --- a/arch/arm/plat-s3c24xx/cpu.c +++ b/arch/arm/plat-s3c24xx/cpu.c @@ -55,16 +55,6 @@ #include #include -struct cpu_table { - unsigned long idcode; - unsigned long idmask; - void (*map_io)(struct map_desc *mach_desc, int size); - void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no); - void (*init_clocks)(int xtal); - int (*init)(void); - const char *name; -}; - /* table of supported CPUs */ static const char name_s3c2400[] = "S3C2400"; @@ -169,23 +159,7 @@ static struct map_desc s3c_iodesc[] __initdata = { IODESC_ENT(UART) }; -static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode) -{ - struct cpu_table *tab; - int count; - - tab = cpu_ids; - for (count = 0; count < ARRAY_SIZE(cpu_ids); count++, tab++) { - if ((idcode & tab->idmask) == tab->idcode) - return tab; - } - - return NULL; -} - -/* cpu information */ - -static struct cpu_table *cpu; +/* read cpu identificaiton code */ static unsigned long s3c24xx_read_idcode_v5(void) { @@ -231,6 +205,7 @@ void __init s3c24xx_init_io(struct map_desc *mach_desc, int size) unsigned long idcode = 0x0; /* initialise the io descriptors we need for initialisation */ + iotable_init(mach_desc, size); iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); if (cpu_architecture() >= CPU_ARCH_ARMv5) { @@ -239,117 +214,7 @@ void __init s3c24xx_init_io(struct map_desc *mach_desc, int size) idcode = s3c24xx_read_idcode_v4(); } - cpu = s3c_lookup_cpu(idcode); - - if (cpu == NULL) { - printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode); - panic("Unknown S3C24XX CPU"); - } - - printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode); - - if (cpu->map_io == NULL || cpu->init == NULL) { - printk(KERN_ERR "CPU %s support not enabled\n", cpu->name); - panic("Unsupported S3C24XX CPU"); - } - arm_pm_restart = s3c24xx_pm_restart; - (cpu->map_io)(mach_desc, size); -} - -/* s3c24xx_init_clocks - * - * Initialise the clock subsystem and associated information from the - * given master crystal value. - * - * xtal = 0 -> use default PLL crystal value (normally 12MHz) - * != 0 -> PLL crystal value in Hz -*/ - -void __init s3c24xx_init_clocks(int xtal) -{ - if (xtal == 0) - xtal = 12*1000*1000; - - if (cpu == NULL) - panic("s3c24xx_init_clocks: no cpu setup?\n"); - - if (cpu->init_clocks == NULL) - panic("s3c24xx_init_clocks: cpu has no clock init\n"); - else - (cpu->init_clocks)(xtal); -} - -/* uart management */ - -static int nr_uarts __initdata = 0; - -static struct s3c2410_uartcfg uart_cfgs[3]; - -/* s3c24xx_init_uartdevs - * - * copy the specified platform data and configuration into our central - * set of devices, before the data is thrown away after the init process. - * - * This also fills in the array passed to the serial driver for the - * early initialisation of the console. -*/ - -void __init s3c24xx_init_uartdevs(char *name, - struct s3c24xx_uart_resources *res, - struct s3c2410_uartcfg *cfg, int no) -{ - struct platform_device *platdev; - struct s3c2410_uartcfg *cfgptr = uart_cfgs; - struct s3c24xx_uart_resources *resp; - int uart; - - memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no); - - for (uart = 0; uart < no; uart++, cfg++, cfgptr++) { - platdev = s3c24xx_uart_src[cfgptr->hwport]; - - resp = res + cfgptr->hwport; - - s3c24xx_uart_devs[uart] = platdev; - - platdev->name = name; - platdev->resource = resp->resources; - platdev->num_resources = resp->nr_resources; - - platdev->dev.platform_data = cfgptr; - } - - nr_uarts = no; + s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids)); } - -void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) -{ - if (cpu == NULL) - return; - - if (cpu->init_uarts == NULL) { - printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n"); - } else - (cpu->init_uarts)(cfg, no); -} - -static int __init s3c_arch_init(void) -{ - int ret; - - // do the correct init for cpu - - if (cpu == NULL) - panic("s3c_arch_init: NULL cpu\n"); - - ret = (cpu->init)(); - if (ret != 0) - return ret; - - ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts); - return ret; -} - -arch_initcall(s3c_arch_init); diff --git a/arch/arm/plat-s3c24xx/include/plat/cpu.h b/arch/arm/plat-s3c24xx/include/plat/cpu.h index 23e420e8bd5..bd0a02963b4 100644 --- a/arch/arm/plat-s3c24xx/include/plat/cpu.h +++ b/arch/arm/plat-s3c24xx/include/plat/cpu.h @@ -26,6 +26,21 @@ struct platform_device; struct s3c2410_uartcfg; struct map_desc; +/* per-cpu initialisation function table. */ + +struct cpu_table { + unsigned long idcode; + unsigned long idmask; + void (*map_io)(void); + void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no); + void (*init_clocks)(int xtal); + int (*init)(void); + const char *name; +}; + +extern void s3c_init_cpu(unsigned long idcode, + struct cpu_table *cpus, unsigned int cputab_size); + /* core initialisation functions */ extern void s3c24xx_init_irq(void); diff --git a/arch/arm/plat-s3c24xx/include/plat/s3c2400.h b/arch/arm/plat-s3c24xx/include/plat/s3c2400.h index 3a5a16821af..b3feaea5c70 100644 --- a/arch/arm/plat-s3c24xx/include/plat/s3c2400.h +++ b/arch/arm/plat-s3c24xx/include/plat/s3c2400.h @@ -17,7 +17,7 @@ extern int s3c2400_init(void); -extern void s3c2400_map_io(struct map_desc *mach_desc, int size); +extern void s3c2400_map_io(void); extern void s3c2400_init_uarts(struct s3c2410_uartcfg *cfg, int no); diff --git a/arch/arm/plat-s3c24xx/include/plat/s3c2410.h b/arch/arm/plat-s3c24xx/include/plat/s3c2410.h index 3cd1ec677b3..a9ac9e29759 100644 --- a/arch/arm/plat-s3c24xx/include/plat/s3c2410.h +++ b/arch/arm/plat-s3c24xx/include/plat/s3c2410.h @@ -15,7 +15,7 @@ extern int s3c2410_init(void); -extern void s3c2410_map_io(struct map_desc *mach_desc, int size); +extern void s3c2410_map_io(void); extern void s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no); diff --git a/arch/arm/plat-s3c24xx/include/plat/s3c2412.h b/arch/arm/plat-s3c24xx/include/plat/s3c2412.h index 3ec97685e78..bb15d3b68be 100644 --- a/arch/arm/plat-s3c24xx/include/plat/s3c2412.h +++ b/arch/arm/plat-s3c24xx/include/plat/s3c2412.h @@ -14,7 +14,7 @@ extern int s3c2412_init(void); -extern void s3c2412_map_io(struct map_desc *mach_desc, int size); +extern void s3c2412_map_io(void); extern void s3c2412_init_uarts(struct s3c2410_uartcfg *cfg, int no); diff --git a/arch/arm/plat-s3c24xx/include/plat/s3c2443.h b/arch/arm/plat-s3c24xx/include/plat/s3c2443.h index 11d83b5c84e..815b107ed89 100644 --- a/arch/arm/plat-s3c24xx/include/plat/s3c2443.h +++ b/arch/arm/plat-s3c24xx/include/plat/s3c2443.h @@ -16,7 +16,7 @@ struct s3c2410_uartcfg; extern int s3c2443_init(void); -extern void s3c2443_map_io(struct map_desc *mach_desc, int size); +extern void s3c2443_map_io(void); extern void s3c2443_init_uarts(struct s3c2410_uartcfg *cfg, int no); diff --git a/arch/arm/plat-s3c24xx/s3c244x.c b/arch/arm/plat-s3c24xx/s3c244x.c index c0344fac4a9..d1152d1f9ba 100644 --- a/arch/arm/plat-s3c24xx/s3c244x.c +++ b/arch/arm/plat-s3c24xx/s3c244x.c @@ -56,12 +56,11 @@ void __init s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no) s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no); } -void __init s3c244x_map_io(struct map_desc *mach_desc, int size) +void __init s3c244x_map_io(void) { /* register our io-tables */ iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc)); - iotable_init(mach_desc, size); /* rename any peripherals used differing from the s3c2410 */ diff --git a/arch/arm/plat-s3c24xx/s3c244x.h b/arch/arm/plat-s3c24xx/s3c244x.h index f8ed17676a3..6aab5eaae2b 100644 --- a/arch/arm/plat-s3c24xx/s3c244x.h +++ b/arch/arm/plat-s3c24xx/s3c244x.h @@ -12,7 +12,7 @@ #if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2442) -extern void s3c244x_map_io(struct map_desc *mach_desc, int size); +extern void s3c244x_map_io(void); extern void s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no); -- cgit v1.2.3 From 93bc6b6371b6b7303ffdae0d69dcdc443b8b0d8a Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:33 +0100 Subject: [ARM] S3C24XX: Split DCLK/CLKOUT definitions out of clock.c Only certain boards need these clocks, and they are not available on some CPUs (such as the S3C24A0) so remove them from arch/arm/plat-s3c24xx/clock.c and into their own file with appropriate Kconfig entries. Signed-off-by: Ben Dooks --- arch/arm/mach-s3c2410/Kconfig | 2 + arch/arm/mach-s3c2440/Kconfig | 2 + arch/arm/plat-s3c24xx/Kconfig | 5 + arch/arm/plat-s3c24xx/Makefile | 1 + arch/arm/plat-s3c24xx/clock-dclk.c | 194 +++++++++++++++++++++++++++++++++++++ arch/arm/plat-s3c24xx/clock.c | 171 -------------------------------- 6 files changed, 204 insertions(+), 171 deletions(-) create mode 100644 arch/arm/plat-s3c24xx/clock-dclk.c (limited to 'arch/arm') diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig index 5453aff6048..63a30d1dd42 100644 --- a/arch/arm/mach-s3c2410/Kconfig +++ b/arch/arm/mach-s3c2410/Kconfig @@ -80,6 +80,7 @@ config ARCH_BAST select PM_SIMTEC if PM select SIMTEC_NOR select MACH_BAST_IDE + select S3C24XX_DCLK select ISA help Say Y here if you are using the Simtec Electronics EB2410ITX @@ -117,6 +118,7 @@ config MACH_TCT_HAMMER config MACH_VR1000 bool "Thorcom VR1000" select PM_SIMTEC if PM + select S3C24XX_DCLK select SIMTEC_NOR select MACH_BAST_IDE select CPU_S3C2410 diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig index 0429d255b0d..57b9c57ff2b 100644 --- a/arch/arm/mach-s3c2440/Kconfig +++ b/arch/arm/mach-s3c2440/Kconfig @@ -29,6 +29,7 @@ menu "S3C2440 Machines" config MACH_ANUBIS bool "Simtec Electronics ANUBIS" select CPU_S3C2440 + select S3C24XX_DCLK select PM_SIMTEC if PM select HAVE_PATA_PLATFORM help @@ -38,6 +39,7 @@ config MACH_ANUBIS config MACH_OSIRIS bool "Simtec IM2440D20 (OSIRIS) module" select CPU_S3C2440 + select S3C24XX_DCLK select PM_SIMTEC if PM help Say Y here if you are using the Simtec IM2440D20 module, also diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig index 2bc4b10f522..d3faf01bbfc 100644 --- a/arch/arm/plat-s3c24xx/Kconfig +++ b/arch/arm/plat-s3c24xx/Kconfig @@ -23,6 +23,11 @@ config S3C2410_CLOCK Clock code for the S3C2410, and similar processors which is currently includes the S3C2410, S3C2440, S3C2442. +config S3C24XX_DCLK + bool + help + Clock code for supporting DCLK/CLKOUT on S3C24XX architectures + config CPU_S3C244X bool depends on ARCH_S3C2410 && (CPU_S3C2440 || CPU_S3C2442) diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile index 095a6c2324d..80cf8eb211f 100644 --- a/arch/arm/plat-s3c24xx/Makefile +++ b/arch/arm/plat-s3c24xx/Makefile @@ -20,6 +20,7 @@ obj-y += gpiolib.o obj-y += time.o obj-y += clock.o obj-y += pwm-clock.o +obj-$(CONFIG_S3C24XX_DCLK) += clock-dclk.o # Architecture dependant builds diff --git a/arch/arm/plat-s3c24xx/clock-dclk.c b/arch/arm/plat-s3c24xx/clock-dclk.c new file mode 100644 index 00000000000..5b75a797b5a --- /dev/null +++ b/arch/arm/plat-s3c24xx/clock-dclk.c @@ -0,0 +1,194 @@ +/* linux/arch/arm/plat-s3c24xx/clock-dclk.c + * + * Copyright (c) 2004,2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * S3C24XX - definitions for DCLK and CLKOUT registers + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* clocks that could be registered by external code */ + +static int s3c24xx_dclk_enable(struct clk *clk, int enable) +{ + unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON); + + if (enable) + dclkcon |= clk->ctrlbit; + else + dclkcon &= ~clk->ctrlbit; + + __raw_writel(dclkcon, S3C24XX_DCLKCON); + + return 0; +} + +static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) +{ + unsigned long dclkcon; + unsigned int uclk; + + if (parent == &clk_upll) + uclk = 1; + else if (parent == &clk_p) + uclk = 0; + else + return -EINVAL; + + clk->parent = parent; + + dclkcon = __raw_readl(S3C24XX_DCLKCON); + + if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) { + if (uclk) + dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK; + else + dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK; + } else { + if (uclk) + dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK; + else + dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK; + } + + __raw_writel(dclkcon, S3C24XX_DCLKCON); + + return 0; +} +static unsigned long s3c24xx_calc_div(struct clk *clk, unsigned long rate) +{ + unsigned long div; + + if ((rate == 0) || !clk->parent) + return 0; + + div = clk_get_rate(clk->parent) / rate; + if (div < 2) + div = 2; + else if (div > 16) + div = 16; + + return div; +} + +static unsigned long s3c24xx_round_dclk_rate(struct clk *clk, + unsigned long rate) +{ + unsigned long div = s3c24xx_calc_div(clk, rate); + + if (div == 0) + return 0; + + return clk_get_rate(clk->parent) / div; +} + +static int s3c24xx_set_dclk_rate(struct clk *clk, unsigned long rate) +{ + unsigned long mask, data, div = s3c24xx_calc_div(clk, rate); + + if (div == 0) + return -EINVAL; + + if (clk == &s3c24xx_dclk0) { + mask = S3C2410_DCLKCON_DCLK0_DIV_MASK | + S3C2410_DCLKCON_DCLK0_CMP_MASK; + data = S3C2410_DCLKCON_DCLK0_DIV(div) | + S3C2410_DCLKCON_DCLK0_CMP((div + 1) / 2); + } else if (clk == &s3c24xx_dclk1) { + mask = S3C2410_DCLKCON_DCLK1_DIV_MASK | + S3C2410_DCLKCON_DCLK1_CMP_MASK; + data = S3C2410_DCLKCON_DCLK1_DIV(div) | + S3C2410_DCLKCON_DCLK1_CMP((div + 1) / 2); + } else + return -EINVAL; + + clk->rate = clk_get_rate(clk->parent) / div; + __raw_writel(((__raw_readl(S3C24XX_DCLKCON) & ~mask) | data), + S3C24XX_DCLKCON); + return clk->rate; +} +static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) +{ + unsigned long mask; + unsigned long source; + + /* calculate the MISCCR setting for the clock */ + + if (parent == &clk_xtal) + source = S3C2410_MISCCR_CLK0_MPLL; + else if (parent == &clk_upll) + source = S3C2410_MISCCR_CLK0_UPLL; + else if (parent == &clk_f) + source = S3C2410_MISCCR_CLK0_FCLK; + else if (parent == &clk_h) + source = S3C2410_MISCCR_CLK0_HCLK; + else if (parent == &clk_p) + source = S3C2410_MISCCR_CLK0_PCLK; + else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0) + source = S3C2410_MISCCR_CLK0_DCLK0; + else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1) + source = S3C2410_MISCCR_CLK0_DCLK0; + else + return -EINVAL; + + clk->parent = parent; + + if (clk == &s3c24xx_clkout0) + mask = S3C2410_MISCCR_CLK0_MASK; + else { + source <<= 4; + mask = S3C2410_MISCCR_CLK1_MASK; + } + + s3c2410_modify_misccr(mask, source); + return 0; +} + +/* external clock definitions */ + +struct clk s3c24xx_dclk0 = { + .name = "dclk0", + .id = -1, + .ctrlbit = S3C2410_DCLKCON_DCLK0EN, + .enable = s3c24xx_dclk_enable, + .set_parent = s3c24xx_dclk_setparent, + .set_rate = s3c24xx_set_dclk_rate, + .round_rate = s3c24xx_round_dclk_rate, +}; + +struct clk s3c24xx_dclk1 = { + .name = "dclk1", + .id = -1, + .ctrlbit = S3C2410_DCLKCON_DCLK1EN, + .enable = s3c24xx_dclk_enable, + .set_parent = s3c24xx_dclk_setparent, + .set_rate = s3c24xx_set_dclk_rate, + .round_rate = s3c24xx_round_dclk_rate, +}; + +struct clk s3c24xx_clkout0 = { + .name = "clkout0", + .id = -1, + .set_parent = s3c24xx_clkout_setparent, +}; + +struct clk s3c24xx_clkout1 = { + .name = "clkout1", + .id = -1, + .set_parent = s3c24xx_clkout_setparent, +}; diff --git a/arch/arm/plat-s3c24xx/clock.c b/arch/arm/plat-s3c24xx/clock.c index a005ddbd9ef..bf2633bd399 100644 --- a/arch/arm/plat-s3c24xx/clock.c +++ b/arch/arm/plat-s3c24xx/clock.c @@ -283,178 +283,7 @@ struct clk clk_usb_bus = { .parent = &clk_upll, }; -/* clocks that could be registered by external code */ -static int s3c24xx_dclk_enable(struct clk *clk, int enable) -{ - unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON); - - if (enable) - dclkcon |= clk->ctrlbit; - else - dclkcon &= ~clk->ctrlbit; - - __raw_writel(dclkcon, S3C24XX_DCLKCON); - - return 0; -} - -static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) -{ - unsigned long dclkcon; - unsigned int uclk; - - if (parent == &clk_upll) - uclk = 1; - else if (parent == &clk_p) - uclk = 0; - else - return -EINVAL; - - clk->parent = parent; - - dclkcon = __raw_readl(S3C24XX_DCLKCON); - - if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) { - if (uclk) - dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK; - else - dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK; - } else { - if (uclk) - dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK; - else - dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK; - } - - __raw_writel(dclkcon, S3C24XX_DCLKCON); - - return 0; -} - -static unsigned long s3c24xx_calc_div(struct clk *clk, unsigned long rate) -{ - unsigned long div; - - if ((rate == 0) || !clk->parent) - return 0; - - div = clk_get_rate(clk->parent) / rate; - if (div < 2) - div = 2; - else if (div > 16) - div = 16; - - return div; -} - -static unsigned long s3c24xx_round_dclk_rate(struct clk *clk, - unsigned long rate) -{ - unsigned long div = s3c24xx_calc_div(clk, rate); - - if (div == 0) - return 0; - - return clk_get_rate(clk->parent) / div; -} - -static int s3c24xx_set_dclk_rate(struct clk *clk, unsigned long rate) -{ - unsigned long mask, data, div = s3c24xx_calc_div(clk, rate); - - if (div == 0) - return -EINVAL; - - if (clk == &s3c24xx_dclk0) { - mask = S3C2410_DCLKCON_DCLK0_DIV_MASK | - S3C2410_DCLKCON_DCLK0_CMP_MASK; - data = S3C2410_DCLKCON_DCLK0_DIV(div) | - S3C2410_DCLKCON_DCLK0_CMP((div + 1) / 2); - } else if (clk == &s3c24xx_dclk1) { - mask = S3C2410_DCLKCON_DCLK1_DIV_MASK | - S3C2410_DCLKCON_DCLK1_CMP_MASK; - data = S3C2410_DCLKCON_DCLK1_DIV(div) | - S3C2410_DCLKCON_DCLK1_CMP((div + 1) / 2); - } else - return -EINVAL; - - clk->rate = clk_get_rate(clk->parent) / div; - __raw_writel(((__raw_readl(S3C24XX_DCLKCON) & ~mask) | data), - S3C24XX_DCLKCON); - return clk->rate; -} - -static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) -{ - unsigned long mask; - unsigned long source; - - /* calculate the MISCCR setting for the clock */ - - if (parent == &clk_xtal) - source = S3C2410_MISCCR_CLK0_MPLL; - else if (parent == &clk_upll) - source = S3C2410_MISCCR_CLK0_UPLL; - else if (parent == &clk_f) - source = S3C2410_MISCCR_CLK0_FCLK; - else if (parent == &clk_h) - source = S3C2410_MISCCR_CLK0_HCLK; - else if (parent == &clk_p) - source = S3C2410_MISCCR_CLK0_PCLK; - else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0) - source = S3C2410_MISCCR_CLK0_DCLK0; - else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1) - source = S3C2410_MISCCR_CLK0_DCLK0; - else - return -EINVAL; - - clk->parent = parent; - - if (clk == &s3c24xx_clkout0) - mask = S3C2410_MISCCR_CLK0_MASK; - else { - source <<= 4; - mask = S3C2410_MISCCR_CLK1_MASK; - } - - s3c2410_modify_misccr(mask, source); - return 0; -} - -/* external clock definitions */ - -struct clk s3c24xx_dclk0 = { - .name = "dclk0", - .id = -1, - .ctrlbit = S3C2410_DCLKCON_DCLK0EN, - .enable = s3c24xx_dclk_enable, - .set_parent = s3c24xx_dclk_setparent, - .set_rate = s3c24xx_set_dclk_rate, - .round_rate = s3c24xx_round_dclk_rate, -}; - -struct clk s3c24xx_dclk1 = { - .name = "dclk1", - .id = -1, - .ctrlbit = S3C2410_DCLKCON_DCLK1EN, - .enable = s3c24xx_dclk_enable, - .set_parent = s3c24xx_dclk_setparent, - .set_rate = s3c24xx_set_dclk_rate, - .round_rate = s3c24xx_round_dclk_rate, -}; - -struct clk s3c24xx_clkout0 = { - .name = "clkout0", - .id = -1, - .set_parent = s3c24xx_clkout_setparent, -}; - -struct clk s3c24xx_clkout1 = { - .name = "clkout1", - .id = -1, - .set_parent = s3c24xx_clkout_setparent, -}; struct clk s3c24xx_uclk = { .name = "uclk", -- cgit v1.2.3 From e24b864ab3e1a5916c87e13cfdc94c1d02f0578b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:34 +0100 Subject: [ARM] S3C24XX: Split pll code out of regs-clock.h Move the PLL calculation code into it's own header file for re-use with the other plat-s3c24xx based systems such as the S3C24A0. Note, we change the name of s3c2410_get_pll to the more generically named s3c24xx_get_pll as well as the related defintions. Signed-off-by: Ben Dooks --- arch/arm/mach-s3c2410/include/mach/regs-clock.h | 33 ---------------------- arch/arm/mach-s3c2410/mach-h1940.c | 8 +++--- arch/arm/mach-s3c2410/s3c2410.c | 3 +- arch/arm/mach-s3c2412/s3c2412.c | 3 +- arch/arm/plat-s3c24xx/clock.c | 3 +- arch/arm/plat-s3c24xx/include/plat/pll.h | 37 +++++++++++++++++++++++++ arch/arm/plat-s3c24xx/s3c244x.c | 3 +- 7 files changed, 49 insertions(+), 41 deletions(-) create mode 100644 arch/arm/plat-s3c24xx/include/plat/pll.h (limited to 'arch/arm') diff --git a/arch/arm/mach-s3c2410/include/mach/regs-clock.h b/arch/arm/mach-s3c2410/include/mach/regs-clock.h index b3f90aa7807..2a5d90e957f 100644 --- a/arch/arm/mach-s3c2410/include/mach/regs-clock.h +++ b/arch/arm/mach-s3c2410/include/mach/regs-clock.h @@ -42,13 +42,6 @@ #define S3C2410_CLKCON_IIS (1<<17) #define S3C2410_CLKCON_SPI (1<<18) -#define S3C2410_PLLCON_MDIVSHIFT 12 -#define S3C2410_PLLCON_PDIVSHIFT 4 -#define S3C2410_PLLCON_SDIVSHIFT 0 -#define S3C2410_PLLCON_MDIVMASK ((1<<(1+(19-12)))-1) -#define S3C2410_PLLCON_PDIVMASK ((1<<5)-1) -#define S3C2410_PLLCON_SDIVMASK 3 - /* DCLKCON register addresses in gpio.h */ #define S3C2410_DCLKCON_DCLK0EN (1<<0) @@ -76,32 +69,6 @@ #define S3C2410_CLKSLOW_SLOWVAL(x) (x) #define S3C2410_CLKSLOW_GET_SLOWVAL(x) ((x) & 7) -#ifndef __ASSEMBLY__ - -#include - -static inline unsigned int -s3c2410_get_pll(unsigned int pllval, unsigned int baseclk) -{ - unsigned int mdiv, pdiv, sdiv; - uint64_t fvco; - - mdiv = pllval >> S3C2410_PLLCON_MDIVSHIFT; - pdiv = pllval >> S3C2410_PLLCON_PDIVSHIFT; - sdiv = pllval >> S3C2410_PLLCON_SDIVSHIFT; - - mdiv &= S3C2410_PLLCON_MDIVMASK; - pdiv &= S3C2410_PLLCON_PDIVMASK; - sdiv &= S3C2410_PLLCON_SDIVMASK; - - fvco = (uint64_t)baseclk * (mdiv + 8); - do_div(fvco, (pdiv + 2) << sdiv); - - return (unsigned int)fvco; -} - -#endif /* __ASSEMBLY__ */ - #if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2442) /* extra registers */ diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c index 32d550fcff4..836508b829b 100644 --- a/arch/arm/mach-s3c2410/mach-h1940.c +++ b/arch/arm/mach-s3c2410/mach-h1940.c @@ -43,6 +43,7 @@ #include #include #include +#include #include static struct map_desc h1940_iodesc[] __initdata = { @@ -223,10 +224,9 @@ static void __init h1940_init(void) S3C2410_MISCCR_USBSUSPND0 | S3C2410_MISCCR_USBSUSPND1, 0x0); - tmp = ( - 0x78 << S3C2410_PLLCON_MDIVSHIFT) - | (0x02 << S3C2410_PLLCON_PDIVSHIFT) - | (0x03 << S3C2410_PLLCON_SDIVSHIFT); + tmp = (0x78 << S3C24XX_PLLCON_MDIVSHIFT) + | (0x02 << S3C24XX_PLLCON_PDIVSHIFT) + | (0x03 << S3C24XX_PLLCON_SDIVSHIFT); writel(tmp, S3C2410_UPLLCON); platform_add_devices(h1940_devices, ARRAY_SIZE(h1940_devices)); diff --git a/arch/arm/mach-s3c2410/s3c2410.c b/arch/arm/mach-s3c2410/s3c2410.c index 1db39c609d7..4e23bc05f4b 100644 --- a/arch/arm/mach-s3c2410/s3c2410.c +++ b/arch/arm/mach-s3c2410/s3c2410.c @@ -35,6 +35,7 @@ #include #include #include +#include /* Initial IO mappings */ @@ -74,7 +75,7 @@ void __init s3c2410_init_clocks(int xtal) /* now we've got our machine bits initialised, work out what * clocks we've got */ - fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal); + fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal); tmp = __raw_readl(S3C2410_CLKDIVN); diff --git a/arch/arm/mach-s3c2412/s3c2412.c b/arch/arm/mach-s3c2412/s3c2412.c index 22d22a03578..4bd2b5fb866 100644 --- a/arch/arm/mach-s3c2412/s3c2412.c +++ b/arch/arm/mach-s3c2412/s3c2412.c @@ -47,6 +47,7 @@ #include #include #include +#include #ifndef CONFIG_CPU_S3C2412_ONLY void __iomem *s3c24xx_va_gpio2 = S3C24XX_VA_GPIO; @@ -165,7 +166,7 @@ void __init s3c2412_init_clocks(int xtal) /* now we've got our machine bits initialised, work out what * clocks we've got */ - fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal*2); + fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal*2); clk_mpll.rate = fclk; diff --git a/arch/arm/plat-s3c24xx/clock.c b/arch/arm/plat-s3c24xx/clock.c index bf2633bd399..1ff1b983604 100644 --- a/arch/arm/plat-s3c24xx/clock.c +++ b/arch/arm/plat-s3c24xx/clock.c @@ -49,6 +49,7 @@ #include #include +#include /* clock information */ @@ -332,7 +333,7 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, /* initialise the main system clocks */ clk_xtal.rate = xtal; - clk_upll.rate = s3c2410_get_pll(__raw_readl(S3C2410_UPLLCON), xtal); + clk_upll.rate = s3c24xx_get_pll(__raw_readl(S3C2410_UPLLCON), xtal); clk_mpll.rate = fclk; clk_h.rate = hclk; diff --git a/arch/arm/plat-s3c24xx/include/plat/pll.h b/arch/arm/plat-s3c24xx/include/plat/pll.h new file mode 100644 index 00000000000..7ea8bffa7a9 --- /dev/null +++ b/arch/arm/plat-s3c24xx/include/plat/pll.h @@ -0,0 +1,37 @@ +/* linux/arch/arm/plat-s3c24xx/include/plat/pll.h + * + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C24xx - common pll registers and code + */ + +#define S3C24XX_PLLCON_MDIVSHIFT 12 +#define S3C24XX_PLLCON_PDIVSHIFT 4 +#define S3C24XX_PLLCON_SDIVSHIFT 0 +#define S3C24XX_PLLCON_MDIVMASK ((1<<(1+(19-12)))-1) +#define S3C24XX_PLLCON_PDIVMASK ((1<<5)-1) +#define S3C24XX_PLLCON_SDIVMASK 3 + +#include + +static inline unsigned int +s3c24xx_get_pll(unsigned int pllval, unsigned int baseclk) +{ + unsigned int mdiv, pdiv, sdiv; + uint64_t fvco; + + mdiv = pllval >> S3C24XX_PLLCON_MDIVSHIFT; + pdiv = pllval >> S3C24XX_PLLCON_PDIVSHIFT; + sdiv = pllval >> S3C24XX_PLLCON_SDIVSHIFT; + + mdiv &= S3C24XX_PLLCON_MDIVMASK; + pdiv &= S3C24XX_PLLCON_PDIVMASK; + sdiv &= S3C24XX_PLLCON_SDIVMASK; + + fvco = (uint64_t)baseclk * (mdiv + 8); + do_div(fvco, (pdiv + 2) << sdiv); + + return (unsigned int)fvco; +} diff --git a/arch/arm/plat-s3c24xx/s3c244x.c b/arch/arm/plat-s3c24xx/s3c244x.c index d1152d1f9ba..7f33cef20ba 100644 --- a/arch/arm/plat-s3c24xx/s3c244x.c +++ b/arch/arm/plat-s3c24xx/s3c244x.c @@ -42,6 +42,7 @@ #include #include #include +#include static struct map_desc s3c244x_iodesc[] __initdata = { IODESC_ENT(CLKPWR), @@ -80,7 +81,7 @@ void __init s3c244x_init_clocks(int xtal) /* now we've got our machine bits initialised, work out what * clocks we've got */ - fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2; + fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2; clkdiv = __raw_readl(S3C2410_CLKDIVN); camdiv = __raw_readl(S3C2440_CAMDIVN); -- cgit v1.2.3 From c3391e36d697c997b6afeb045071e0be95219a3e Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:37 +0100 Subject: [ARM] S3C24XX: Change clock locking to use spinlocks. We cannot sleep if we have cpufreq pm enabled during some of the clock operations, so change to use a spinlock to protect the clock system. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c24xx/clock.c | 32 +++++++++++++++++------------- arch/arm/plat-s3c24xx/include/plat/clock.h | 4 +++- arch/arm/plat-s3c24xx/s3c244x-clock.c | 5 ++--- 3 files changed, 23 insertions(+), 18 deletions(-) (limited to 'arch/arm') diff --git a/arch/arm/plat-s3c24xx/clock.c b/arch/arm/plat-s3c24xx/clock.c index 1ff1b983604..334e696200b 100644 --- a/arch/arm/plat-s3c24xx/clock.c +++ b/arch/arm/plat-s3c24xx/clock.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include @@ -55,7 +55,11 @@ static LIST_HEAD(clocks); -DEFINE_MUTEX(clocks_mutex); +/* We originally used an mutex here, but some contexts (see resume) + * are calling functions such as clk_set_parent() with IRQs disabled + * causing an BUG to be triggered. + */ +DEFINE_SPINLOCK(clocks_lock); /* enable and disable calls for use with the clk struct */ @@ -77,7 +81,7 @@ struct clk *clk_get(struct device *dev, const char *id) else idno = to_platform_device(dev)->id; - mutex_lock(&clocks_mutex); + spin_lock(&clocks_lock); list_for_each_entry(p, &clocks, list) { if (p->id == idno && @@ -101,7 +105,7 @@ struct clk *clk_get(struct device *dev, const char *id) } } - mutex_unlock(&clocks_mutex); + spin_unlock(&clocks_lock); return clk; } @@ -117,12 +121,12 @@ int clk_enable(struct clk *clk) clk_enable(clk->parent); - mutex_lock(&clocks_mutex); + spin_lock(&clocks_lock); if ((clk->usage++) == 0) (clk->enable)(clk, 1); - mutex_unlock(&clocks_mutex); + spin_unlock(&clocks_lock); return 0; } @@ -131,12 +135,12 @@ void clk_disable(struct clk *clk) if (IS_ERR(clk) || clk == NULL) return; - mutex_lock(&clocks_mutex); + spin_lock(&clocks_lock); if ((--clk->usage) == 0) (clk->enable)(clk, 0); - mutex_unlock(&clocks_mutex); + spin_unlock(&clocks_lock); clk_disable(clk->parent); } @@ -182,9 +186,9 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (clk->set_rate == NULL) return -EINVAL; - mutex_lock(&clocks_mutex); + spin_lock(&clocks_lock); ret = (clk->set_rate)(clk, rate); - mutex_unlock(&clocks_mutex); + spin_unlock(&clocks_lock); return ret; } @@ -201,12 +205,12 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (IS_ERR(clk)) return -EINVAL; - mutex_lock(&clocks_mutex); + spin_lock(&clocks_lock); if (clk->set_parent) ret = (clk->set_parent)(clk, parent); - mutex_unlock(&clocks_mutex); + spin_unlock(&clocks_lock); return ret; } @@ -302,9 +306,9 @@ int s3c24xx_register_clock(struct clk *clk) /* add to the list of available clocks */ - mutex_lock(&clocks_mutex); + spin_lock(&clocks_lock); list_add(&clk->list, &clocks); - mutex_unlock(&clocks_mutex); + spin_unlock(&clocks_lock); return 0; } diff --git a/arch/arm/plat-s3c24xx/include/plat/clock.h b/arch/arm/plat-s3c24xx/include/plat/clock.h index 235b753cd87..88a00c35b68 100644 --- a/arch/arm/plat-s3c24xx/include/plat/clock.h +++ b/arch/arm/plat-s3c24xx/include/plat/clock.h @@ -10,6 +10,8 @@ * published by the Free Software Foundation. */ +#include + struct clk { struct list_head list; struct module *owner; @@ -51,7 +53,7 @@ extern struct clk clk_xtal; * Please DO NOT use these outside of arch/arm/mach-s3c2410 */ -extern struct mutex clocks_mutex; +extern spinlock_t clocks_lock; extern int s3c2410_clkcon_enable(struct clk *clk, int enable); diff --git a/arch/arm/plat-s3c24xx/s3c244x-clock.c b/arch/arm/plat-s3c24xx/s3c244x-clock.c index 7c09773ff9f..dde41f171af 100644 --- a/arch/arm/plat-s3c24xx/s3c244x-clock.c +++ b/arch/arm/plat-s3c24xx/s3c244x-clock.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -102,13 +101,13 @@ static int s3c244x_clk_add(struct sys_device *sysdev) if (clk_get_rate(clock_upll) > (94 * MHZ)) { clk_usb_bus.rate = clk_get_rate(clock_upll) / 2; - mutex_lock(&clocks_mutex); + spin_lock(&clocks_lock); clkdivn = __raw_readl(S3C2410_CLKDIVN); clkdivn |= S3C2440_CLKDIVN_UCLK; __raw_writel(clkdivn, S3C2410_CLKDIVN); - mutex_unlock(&clocks_mutex); + spin_unlock(&clocks_lock); } return 0; -- cgit v1.2.3 From e425382ed90d221ef9031a1b2d97d9bfedcf90c3 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:38 +0100 Subject: [ARM] S3C24XX: Update clock data on resume Update the clock settings on resume for suspend/resume support so that if the boot loader changes anything or the system's PLL is reset then we return with the correct settings. Signed-off-by: Ben Dooks --- arch/arm/mach-s3c2410/s3c2410.c | 19 ++++++++++++++-- arch/arm/mach-s3c2412/s3c2412.c | 21 +++++++++++++++--- arch/arm/mach-s3c2443/clock.c | 35 +++++++++++++++++++++--------- arch/arm/plat-s3c24xx/clock.c | 25 +++++++++++---------- arch/arm/plat-s3c24xx/include/plat/clock.h | 15 +++++++++---- arch/arm/plat-s3c24xx/pm.c | 4 +++- arch/arm/plat-s3c24xx/s3c244x.c | 21 +++++++++++++----- 7 files changed, 104 insertions(+), 36 deletions(-) (limited to 'arch/arm') diff --git a/arch/arm/mach-s3c2410/s3c2410.c b/arch/arm/mach-s3c2410/s3c2410.c index 4e23bc05f4b..feb141b1f91 100644 --- a/arch/arm/mach-s3c2410/s3c2410.c +++ b/arch/arm/mach-s3c2410/s3c2410.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,8 @@ #include #include +#include + #include #include @@ -65,13 +68,19 @@ void __init s3c2410_map_io(void) iotable_init(s3c2410_iodesc, ARRAY_SIZE(s3c2410_iodesc)); } -void __init s3c2410_init_clocks(int xtal) +void __init_or_cpufreq s3c2410_setup_clocks(void) { + struct clk *xtal_clk; unsigned long tmp; + unsigned long xtal; unsigned long fclk; unsigned long hclk; unsigned long pclk; + xtal_clk = clk_get(NULL, "xtal"); + xtal = clk_get_rate(xtal_clk); + clk_put(xtal_clk); + /* now we've got our machine bits initialised, work out what * clocks we've got */ @@ -93,7 +102,13 @@ void __init s3c2410_init_clocks(int xtal) * console to use them */ - s3c24xx_setup_clocks(xtal, fclk, hclk, pclk); + s3c24xx_setup_clocks(fclk, hclk, pclk); +} + +void __init s3c2410_init_clocks(int xtal) +{ + s3c24xx_register_baseclocks(xtal); + s3c2410_setup_clocks(); s3c2410_baseclk_add(); } diff --git a/arch/arm/mach-s3c2412/s3c2412.c b/arch/arm/mach-s3c2412/s3c2412.c index 4bd2b5fb866..5b5aba69ec3 100644 --- a/arch/arm/mach-s3c2412/s3c2412.c +++ b/arch/arm/mach-s3c2412/s3c2412.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,8 @@ #include #include +#include + #include #include #include @@ -156,17 +159,23 @@ void __init s3c2412_map_io(void) iotable_init(s3c2412_iodesc, ARRAY_SIZE(s3c2412_iodesc)); } -void __init s3c2412_init_clocks(int xtal) +void __init_or_cpufreq s3c2412_setup_clocks(void) { + struct clk *xtal_clk; unsigned long tmp; + unsigned long xtal; unsigned long fclk; unsigned long hclk; unsigned long pclk; + xtal_clk = clk_get(NULL, "xtal"); + xtal = clk_get_rate(xtal_clk); + clk_put(xtal_clk); + /* now we've got our machine bits initialised, work out what * clocks we've got */ - fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal*2); + fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal * 2); clk_mpll.rate = fclk; @@ -183,11 +192,17 @@ void __init s3c2412_init_clocks(int xtal) printk("S3C2412: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n", print_mhz(fclk), print_mhz(hclk), print_mhz(pclk)); + s3c24xx_setup_clocks(fclk, hclk, pclk); +} + +void __init s3c2412_init_clocks(int xtal) +{ /* initialise the clocks here, to allow other things like the * console to use them */ - s3c24xx_setup_clocks(xtal, fclk, hclk, pclk); + s3c24xx_register_baseclocks(xtal); + s3c2412_setup_clocks(); s3c2412_baseclk_add(); } diff --git a/arch/arm/mach-s3c2443/clock.c b/arch/arm/mach-s3c2443/clock.c index f854e7385e3..1df8429242b 100644 --- a/arch/arm/mach-s3c2443/clock.c +++ b/arch/arm/mach-s3c2443/clock.c @@ -39,6 +39,8 @@ #include +#include + #include #include #include @@ -1011,22 +1013,20 @@ static struct clk *clks[] __initdata = { &clk_prediv, }; -void __init s3c2443_init_clocks(int xtal) +void __init_or_cpufreq s3c2443_setup_clocks(void) { - unsigned long epllcon = __raw_readl(S3C2443_EPLLCON); unsigned long mpllcon = __raw_readl(S3C2443_MPLLCON); unsigned long clkdiv0 = __raw_readl(S3C2443_CLKDIV0); + struct clk *xtal_clk; + unsigned long xtal; unsigned long pll; unsigned long fclk; unsigned long hclk; unsigned long pclk; - struct clk *clkp; - int ret; - int ptr; - /* s3c2443 parents h and p clocks from prediv */ - clk_h.parent = &clk_prediv; - clk_p.parent = &clk_prediv; + xtal_clk = clk_get(NULL, "xtal"); + xtal = clk_get_rate(xtal_clk); + clk_put(xtal_clk); pll = s3c2443_get_mpll(mpllcon, xtal); clk_msysclk.rate = pll; @@ -1036,13 +1036,29 @@ void __init s3c2443_init_clocks(int xtal) hclk /= s3c2443_get_hdiv(clkdiv0); pclk = hclk / ((clkdiv0 & S3C2443_CLKDIV0_HALF_PCLK) ? 2 : 1); - s3c24xx_setup_clocks(xtal, fclk, hclk, pclk); + s3c24xx_setup_clocks(fclk, hclk, pclk); printk("S3C2443: mpll %s %ld.%03ld MHz, cpu %ld.%03ld MHz, mem %ld.%03ld MHz, pclk %ld.%03ld MHz\n", (mpllcon & S3C2443_PLLCON_OFF) ? "off":"on", print_mhz(pll), print_mhz(fclk), print_mhz(hclk), print_mhz(pclk)); + s3c24xx_setup_clocks(fclk, hclk, pclk); +} + +void __init s3c2443_init_clocks(int xtal) +{ + struct clk *clkp; + unsigned long epllcon = __raw_readl(S3C2443_EPLLCON); + int ret; + int ptr; + + /* s3c2443 parents h and p clocks from prediv */ + clk_h.parent = &clk_prediv; + clk_p.parent = &clk_prediv; + + s3c24xx_register_baseclocks(xtal); + s3c2443_setup_clocks(); s3c2443_clk_initparents(); for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) { @@ -1056,7 +1072,6 @@ void __init s3c2443_init_clocks(int xtal) } clk_epll.rate = s3c2443_get_epll(epllcon, xtal); - clk_usb_bus.parent = &clk_usb_bus_host; /* ensure usb bus clock is within correct rate of 48MHz */ diff --git a/arch/arm/plat-s3c24xx/clock.c b/arch/arm/plat-s3c24xx/clock.c index 334e696200b..a4a0a67a307 100644 --- a/arch/arm/plat-s3c24xx/clock.c +++ b/arch/arm/plat-s3c24xx/clock.c @@ -47,6 +47,8 @@ #include #include +#include + #include #include #include @@ -327,24 +329,24 @@ int s3c24xx_register_clocks(struct clk **clks, int nr_clks) /* initalise all the clocks */ -int __init s3c24xx_setup_clocks(unsigned long xtal, - unsigned long fclk, - unsigned long hclk, - unsigned long pclk) +void __init_or_cpufreq s3c24xx_setup_clocks(unsigned long fclk, + unsigned long hclk, + unsigned long pclk) { - printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n"); - - /* initialise the main system clocks */ - - clk_xtal.rate = xtal; - clk_upll.rate = s3c24xx_get_pll(__raw_readl(S3C2410_UPLLCON), xtal); + clk_upll.rate = s3c24xx_get_pll(__raw_readl(S3C2410_UPLLCON), + clk_xtal.rate); clk_mpll.rate = fclk; clk_h.rate = hclk; clk_p.rate = pclk; clk_f.rate = fclk; +} - /* assume uart clocks are correctly setup */ +int __init s3c24xx_register_baseclocks(unsigned long xtal) +{ + printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n"); + + clk_xtal.rate = xtal; /* register our clocks */ @@ -368,3 +370,4 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, return 0; } + diff --git a/arch/arm/plat-s3c24xx/include/plat/clock.h b/arch/arm/plat-s3c24xx/include/plat/clock.h index 88a00c35b68..ba174aab76f 100644 --- a/arch/arm/plat-s3c24xx/include/plat/clock.h +++ b/arch/arm/plat-s3c24xx/include/plat/clock.h @@ -60,7 +60,14 @@ extern int s3c2410_clkcon_enable(struct clk *clk, int enable); extern int s3c24xx_register_clock(struct clk *clk); extern int s3c24xx_register_clocks(struct clk **clk, int nr_clks); -extern int s3c24xx_setup_clocks(unsigned long xtal, - unsigned long fclk, - unsigned long hclk, - unsigned long pclk); +extern int s3c24xx_register_baseclocks(unsigned long xtal); + +extern void s3c24xx_setup_clocks(unsigned long fclk, + unsigned long hclk, + unsigned long pclk); + +extern void s3c2410_setup_clocks(void); +extern void s3c2412_setup_clocks(void); +extern void s3c244x_setup_clocks(void); +extern void s3c2443_setup_clocks(void); + diff --git a/arch/arm/plat-s3c24xx/pm.c b/arch/arm/plat-s3c24xx/pm.c index 8efb57ad501..bc37cf49f97 100644 --- a/arch/arm/plat-s3c24xx/pm.c +++ b/arch/arm/plat-s3c24xx/pm.c @@ -76,11 +76,13 @@ static struct sleep_save core_save[] = { SAVE_ITEM(S3C2410_BANKCON4), SAVE_ITEM(S3C2410_BANKCON5), +#ifndef CONFIG_CPU_FREQ SAVE_ITEM(S3C2410_CLKDIVN), SAVE_ITEM(S3C2410_MPLLCON), + SAVE_ITEM(S3C2410_REFRESH), +#endif SAVE_ITEM(S3C2410_UPLLCON), SAVE_ITEM(S3C2410_CLKSLOW), - SAVE_ITEM(S3C2410_REFRESH), }; static struct gpio_sleep { diff --git a/arch/arm/plat-s3c24xx/s3c244x.c b/arch/arm/plat-s3c24xx/s3c244x.c index 7f33cef20ba..49436840305 100644 --- a/arch/arm/plat-s3c24xx/s3c244x.c +++ b/arch/arm/plat-s3c24xx/s3c244x.c @@ -29,6 +29,8 @@ #include #include +#include + #include #include #include @@ -71,15 +73,18 @@ void __init s3c244x_map_io(void) s3c_device_usbgadget.name = "s3c2440-usbgadget"; } -void __init s3c244x_init_clocks(int xtal) +void __init_or_cpufreq s3c244x_setup_clocks(void) { + struct clk *xtal_clk; unsigned long clkdiv; unsigned long camdiv; + unsigned long xtal; unsigned long hclk, fclk, pclk; int hdiv = 1; - /* now we've got our machine bits initialised, work out what - * clocks we've got */ + xtal_clk = clk_get(NULL, "xtal"); + xtal = clk_get_rate(xtal_clk); + clk_put(xtal_clk); fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2; @@ -107,18 +112,24 @@ void __init s3c244x_init_clocks(int xtal) } hclk = fclk / hdiv; - pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN)? 2:1); + pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN) ? 2 : 1); /* print brief summary of clocks, etc */ printk("S3C244X: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n", print_mhz(fclk), print_mhz(hclk), print_mhz(pclk)); + s3c24xx_setup_clocks(fclk, hclk, pclk); +} + +void __init s3c244x_init_clocks(int xtal) +{ /* initialise the clocks here, to allow other things like the * console to use them, and to add new ones after the initialisation */ - s3c24xx_setup_clocks(xtal, fclk, hclk, pclk); + s3c24xx_register_baseclocks(xtal); + s3c244x_setup_clocks(); s3c2410_baseclk_add(); } -- cgit v1.2.3 From a503059c14215ac4325e25d9061cad992a373e17 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:43 +0100 Subject: [ARM] S3C24XX: Move headers from plat-s3c24xx to plat-s3c Move clock.h, cpu.h and devs.h to plat-s3c for use with the s3c64xx support. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/include/plat/clock.h | 72 +++++++++++++++++++++++++++++ arch/arm/plat-s3c/include/plat/cpu.h | 69 ++++++++++++++++++++++++++++ arch/arm/plat-s3c/include/plat/devs.h | 49 ++++++++++++++++++++ arch/arm/plat-s3c24xx/include/plat/clock.h | 73 ------------------------------ arch/arm/plat-s3c24xx/include/plat/cpu.h | 69 ---------------------------- arch/arm/plat-s3c24xx/include/plat/devs.h | 49 -------------------- 6 files changed, 190 insertions(+), 191 deletions(-) create mode 100644 arch/arm/plat-s3c/include/plat/clock.h create mode 100644 arch/arm/plat-s3c/include/plat/cpu.h create mode 100644 arch/arm/plat-s3c/include/plat/devs.h delete mode 100644 arch/arm/plat-s3c24xx/include/plat/clock.h delete mode 100644 arch/arm/plat-s3c24xx/include/plat/cpu.h delete mode 100644 arch/arm/plat-s3c24xx/include/plat/devs.h (limited to 'arch/arm') diff --git a/arch/arm/plat-s3c/include/plat/clock.h b/arch/arm/plat-s3c/include/plat/clock.h new file mode 100644 index 00000000000..d871609738f --- /dev/null +++ b/arch/arm/plat-s3c/include/plat/clock.h @@ -0,0 +1,72 @@ +/* linux/arch/arm/plat-s3c/include/plat/clock.h + * + * Copyright (c) 2004-2005 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * Written by Ben Dooks, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include + +struct clk { + struct list_head list; + struct module *owner; + struct clk *parent; + const char *name; + int id; + int usage; + unsigned long rate; + unsigned long ctrlbit; + + int (*enable)(struct clk *, int enable); + int (*set_rate)(struct clk *c, unsigned long rate); + unsigned long (*get_rate)(struct clk *c); + unsigned long (*round_rate)(struct clk *c, unsigned long rate); + int (*set_parent)(struct clk *c, struct clk *parent); +}; + +/* other clocks which may be registered by board support */ + +extern struct clk s3c24xx_dclk0; +extern struct clk s3c24xx_dclk1; +extern struct clk s3c24xx_clkout0; +extern struct clk s3c24xx_clkout1; +extern struct clk s3c24xx_uclk; + +extern struct clk clk_usb_bus; + +/* core clock support */ + +extern struct clk clk_f; +extern struct clk clk_h; +extern struct clk clk_p; +extern struct clk clk_mpll; +extern struct clk clk_upll; +extern struct clk clk_xtal; + +/* exports for arch/arm/mach-s3c2410 + * + * Please DO NOT use these outside of arch/arm/mach-s3c2410 +*/ + +extern spinlock_t clocks_lock; + +extern int s3c2410_clkcon_enable(struct clk *clk, int enable); + +extern int s3c24xx_register_clock(struct clk *clk); +extern int s3c24xx_register_clocks(struct clk **clk, int nr_clks); + +extern int s3c24xx_register_baseclocks(unsigned long xtal); + +extern void s3c24xx_setup_clocks(unsigned long fclk, + unsigned long hclk, + unsigned long pclk); + +extern void s3c2410_setup_clocks(void); +extern void s3c2412_setup_clocks(void); +extern void s3c244x_setup_clocks(void); +extern void s3c2443_setup_clocks(void); + diff --git a/arch/arm/plat-s3c/include/plat/cpu.h b/arch/arm/plat-s3c/include/plat/cpu.h new file mode 100644 index 00000000000..011157ea871 --- /dev/null +++ b/arch/arm/plat-s3c/include/plat/cpu.h @@ -0,0 +1,69 @@ +/* linux/arch/arm/plat-s3c/include/plat/cpu.h + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks + * + * Header file for S3C24XX CPU support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +/* todo - fix when rmk changes iodescs to use `void __iomem *` */ + +#define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, __phys_to_pfn(S3C24XX_PA_##x), S3C24XX_SZ_##x, MT_DEVICE } + +#ifndef MHZ +#define MHZ (1000*1000) +#endif + +#define print_mhz(m) ((m) / MHZ), (((m) / 1000) % 1000) + +/* forward declaration */ +struct s3c24xx_uart_resources; +struct platform_device; +struct s3c2410_uartcfg; +struct map_desc; + +/* per-cpu initialisation function table. */ + +struct cpu_table { + unsigned long idcode; + unsigned long idmask; + void (*map_io)(void); + void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no); + void (*init_clocks)(int xtal); + int (*init)(void); + const char *name; +}; + +extern void s3c_init_cpu(unsigned long idcode, + struct cpu_table *cpus, unsigned int cputab_size); + +/* core initialisation functions */ + +extern void s3c24xx_init_irq(void); + +extern void s3c24xx_init_io(struct map_desc *mach_desc, int size); + +extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no); + +extern void s3c24xx_init_clocks(int xtal); + +extern void s3c24xx_init_uartdevs(char *name, + struct s3c24xx_uart_resources *res, + struct s3c2410_uartcfg *cfg, int no); + +/* timer for 2410/2440 */ + +struct sys_timer; +extern struct sys_timer s3c24xx_timer; + +/* system device classes */ + +extern struct sysdev_class s3c2410_sysclass; +extern struct sysdev_class s3c2412_sysclass; +extern struct sysdev_class s3c2440_sysclass; +extern struct sysdev_class s3c2442_sysclass; +extern struct sysdev_class s3c2443_sysclass; diff --git a/arch/arm/plat-s3c/include/plat/devs.h b/arch/arm/plat-s3c/include/plat/devs.h new file mode 100644 index 00000000000..badaac9d64a --- /dev/null +++ b/arch/arm/plat-s3c/include/plat/devs.h @@ -0,0 +1,49 @@ +/* linux/include/asm-arm/plat-s3c24xx/devs.h + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * Header file for s3c2410 standard platform devices + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ +#include + +struct s3c24xx_uart_resources { + struct resource *resources; + unsigned long nr_resources; +}; + +extern struct s3c24xx_uart_resources s3c2410_uart_resources[]; + +extern struct platform_device *s3c24xx_uart_devs[]; +extern struct platform_device *s3c24xx_uart_src[]; + +extern struct platform_device s3c_device_timer[]; + +extern struct platform_device s3c_device_usb; +extern struct platform_device s3c_device_lcd; +extern struct platform_device s3c_device_wdt; +extern struct platform_device s3c_device_i2c; +extern struct platform_device s3c_device_iis; +extern struct platform_device s3c_device_rtc; +extern struct platform_device s3c_device_adc; +extern struct platform_device s3c_device_sdi; +extern struct platform_device s3c_device_hsmmc; + +extern struct platform_device s3c_device_spi0; +extern struct platform_device s3c_device_spi1; + +extern struct platform_device s3c_device_nand; + +extern struct platform_device s3c_device_usbgadget; + +/* s3c2440 specific devices */ + +#ifdef CONFIG_CPU_S3C2440 + +extern struct platform_device s3c_device_camif; + +#endif diff --git a/arch/arm/plat-s3c24xx/include/plat/clock.h b/arch/arm/plat-s3c24xx/include/plat/clock.h deleted file mode 100644 index ba174aab76f..00000000000 --- a/arch/arm/plat-s3c24xx/include/plat/clock.h +++ /dev/null @@ -1,73 +0,0 @@ -/* linux/include/asm-arm/plat-s3c24xx/clock.h - * linux/arch/arm/mach-s3c2410/clock.h - * - * Copyright (c) 2004-2005 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ - * Written by Ben Dooks, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include - -struct clk { - struct list_head list; - struct module *owner; - struct clk *parent; - const char *name; - int id; - int usage; - unsigned long rate; - unsigned long ctrlbit; - - int (*enable)(struct clk *, int enable); - int (*set_rate)(struct clk *c, unsigned long rate); - unsigned long (*get_rate)(struct clk *c); - unsigned long (*round_rate)(struct clk *c, unsigned long rate); - int (*set_parent)(struct clk *c, struct clk *parent); -}; - -/* other clocks which may be registered by board support */ - -extern struct clk s3c24xx_dclk0; -extern struct clk s3c24xx_dclk1; -extern struct clk s3c24xx_clkout0; -extern struct clk s3c24xx_clkout1; -extern struct clk s3c24xx_uclk; - -extern struct clk clk_usb_bus; - -/* core clock support */ - -extern struct clk clk_f; -extern struct clk clk_h; -extern struct clk clk_p; -extern struct clk clk_mpll; -extern struct clk clk_upll; -extern struct clk clk_xtal; - -/* exports for arch/arm/mach-s3c2410 - * - * Please DO NOT use these outside of arch/arm/mach-s3c2410 -*/ - -extern spinlock_t clocks_lock; - -extern int s3c2410_clkcon_enable(struct clk *clk, int enable); - -extern int s3c24xx_register_clock(struct clk *clk); -extern int s3c24xx_register_clocks(struct clk **clk, int nr_clks); - -extern int s3c24xx_register_baseclocks(unsigned long xtal); - -extern void s3c24xx_setup_clocks(unsigned long fclk, - unsigned long hclk, - unsigned long pclk); - -extern void s3c2410_setup_clocks(void); -extern void s3c2412_setup_clocks(void); -extern void s3c244x_setup_clocks(void); -extern void s3c2443_setup_clocks(void); - diff --git a/arch/arm/plat-s3c24xx/include/plat/cpu.h b/arch/arm/plat-s3c24xx/include/plat/cpu.h deleted file mode 100644 index bd0a02963b4..00000000000 --- a/arch/arm/plat-s3c24xx/include/plat/cpu.h +++ /dev/null @@ -1,69 +0,0 @@ -/* linux/include/asm-arm/plat-s3c24xx/cpu.h - * - * Copyright (c) 2004-2005 Simtec Electronics - * Ben Dooks - * - * Header file for S3C24XX CPU support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -/* todo - fix when rmk changes iodescs to use `void __iomem *` */ - -#define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, __phys_to_pfn(S3C24XX_PA_##x), S3C24XX_SZ_##x, MT_DEVICE } - -#ifndef MHZ -#define MHZ (1000*1000) -#endif - -#define print_mhz(m) ((m) / MHZ), ((m / 1000) % 1000) - -/* forward declaration */ -struct s3c24xx_uart_resources; -struct platform_device; -struct s3c2410_uartcfg; -struct map_desc; - -/* per-cpu initialisation function table. */ - -struct cpu_table { - unsigned long idcode; - unsigned long idmask; - void (*map_io)(void); - void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no); - void (*init_clocks)(int xtal); - int (*init)(void); - const char *name; -}; - -extern void s3c_init_cpu(unsigned long idcode, - struct cpu_table *cpus, unsigned int cputab_size); - -/* core initialisation functions */ - -extern void s3c24xx_init_irq(void); - -extern void s3c24xx_init_io(struct map_desc *mach_desc, int size); - -extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no); - -extern void s3c24xx_init_clocks(int xtal); - -extern void s3c24xx_init_uartdevs(char *name, - struct s3c24xx_uart_resources *res, - struct s3c2410_uartcfg *cfg, int no); - -/* timer for 2410/2440 */ - -struct sys_timer; -extern struct sys_timer s3c24xx_timer; - -/* system device classes */ - -extern struct sysdev_class s3c2410_sysclass; -extern struct sysdev_class s3c2412_sysclass; -extern struct sysdev_class s3c2440_sysclass; -extern struct sysdev_class s3c2442_sysclass; -extern struct sysdev_class s3c2443_sysclass; diff --git a/arch/arm/plat-s3c24xx/include/plat/devs.h b/arch/arm/plat-s3c24xx/include/plat/devs.h deleted file mode 100644 index badaac9d64a..00000000000 --- a/arch/arm/plat-s3c24xx/include/plat/devs.h +++ /dev/null @@ -1,49 +0,0 @@ -/* linux/include/asm-arm/plat-s3c24xx/devs.h - * - * Copyright (c) 2004 Simtec Electronics - * Ben Dooks - * - * Header file for s3c2410 standard platform devices - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ -#include - -struct s3c24xx_uart_resources { - struct resource *resources; - unsigned long nr_resources; -}; - -extern struct s3c24xx_uart_resources s3c2410_uart_resources[]; - -extern struct platform_device *s3c24xx_uart_devs[]; -extern struct platform_device *s3c24xx_uart_src[]; - -extern struct platform_device s3c_device_timer[]; - -extern struct platform_device s3c_device_usb; -extern struct platform_device s3c_device_lcd; -extern struct platform_device s3c_device_wdt; -extern struct platform_device s3c_device_i2c; -extern struct platform_device s3c_device_iis; -extern struct platform_device s3c_device_rtc; -extern struct platform_device s3c_device_adc; -extern struct platform_device s3c_device_sdi; -extern struct platform_device s3c_device_hsmmc; - -extern struct platform_device s3c_device_spi0; -extern struct platform_device s3c_device_spi1; - -extern struct platform_device s3c_device_nand; - -extern struct platform_device s3c_device_usbgadget; - -/* s3c2440 specific devices */ - -#ifdef CONFIG_CPU_S3C2440 - -extern struct platform_device s3c_device_camif; - -#endif -- cgit v1.2.3 From 6af0e929e9fb60a8e2609555129713f310188f33 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:47 +0100 Subject: [ARM] S3C: Move S3C2410_EXTINT to common regs-irqtype.h header Add a common include file (regs-irqtype.h) for the IRQ trigger control register values S3C2410_EXTINT*. Signed-off-by: Ben Dooks --- arch/arm/mach-s3c2410/include/mach/regs-gpio.h | 7 ------- arch/arm/plat-s3c/include/plat/regs-irqtype.h | 21 +++++++++++++++++++++ arch/arm/plat-s3c24xx/irq.c | 1 + 3 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 arch/arm/plat-s3c/include/plat/regs-irqtype.h (limited to 'arch/arm') diff --git a/arch/arm/mach-s3c2410/include/mach/regs-gpio.h b/arch/arm/mach-s3c2410/include/mach/regs-gpio.h index 528080ceac4..32107761306 100644 --- a/arch/arm/mach-s3c2410/include/mach/regs-gpio.h +++ b/arch/arm/mach-s3c2410/include/mach/regs-gpio.h @@ -1053,13 +1053,6 @@ #define S3C24XX_EXTINT1 S3C24XX_GPIOREG2(0x8C) #define S3C24XX_EXTINT2 S3C24XX_GPIOREG2(0x90) -/* values for S3C2410_EXTINT0/1/2 */ -#define S3C2410_EXTINT_LOWLEV (0x00) -#define S3C2410_EXTINT_HILEV (0x01) -#define S3C2410_EXTINT_FALLEDGE (0x02) -#define S3C2410_EXTINT_RISEEDGE (0x04) -#define S3C2410_EXTINT_BOTHEDGE (0x06) - /* interrupt filtering conrrol for EINT16..EINT23 */ #define S3C2410_EINFLT0 S3C2410_GPIOREG(0x94) #define S3C2410_EINFLT1 S3C2410_GPIOREG(0x98) diff --git a/arch/arm/plat-s3c/include/plat/regs-irqtype.h b/arch/arm/plat-s3c/include/plat/regs-irqtype.h new file mode 100644 index 00000000000..c63cd3fc5ad --- /dev/null +++ b/arch/arm/plat-s3c/include/plat/regs-irqtype.h @@ -0,0 +1,21 @@ +/* arch/arm/plat-s3c/include/plat/regs-irqtype.h + * + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * S3C - IRQ detection types. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* values for S3C2410_EXTINT0/1/2 and other cpus in the series, including + * the S3C64XX +*/ +#define S3C2410_EXTINT_LOWLEV (0x00) +#define S3C2410_EXTINT_HILEV (0x01) +#define S3C2410_EXTINT_FALLEDGE (0x02) +#define S3C2410_EXTINT_RISEEDGE (0x04) +#define S3C2410_EXTINT_BOTHEDGE (0x06) diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c index 963f7a4f26f..0192ecdc144 100644 --- a/arch/arm/plat-s3c24xx/irq.c +++ b/arch/arm/plat-s3c24xx/irq.c @@ -62,6 +62,7 @@ #include +#include #include #include -- cgit v1.2.3 From b915a125112b2500d5c6fd752948afc170a49cef Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:53 +0100 Subject: [ARM] S3C: Move time.c to arch/arm/plat-s3c Move time.c to arch/arm/plat-s3c to be shared with the S3C64XX implementations. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/Makefile | 1 + arch/arm/plat-s3c/time.c | 260 +++++++++++++++++++++++++++++++++++++++++ arch/arm/plat-s3c24xx/Makefile | 1 - arch/arm/plat-s3c24xx/time.c | 260 ----------------------------------------- 4 files changed, 261 insertions(+), 261 deletions(-) create mode 100644 arch/arm/plat-s3c/time.c delete mode 100644 arch/arm/plat-s3c24xx/time.c (limited to 'arch/arm') diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index f7c8824ef80..3c543ed5fb9 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -12,3 +12,4 @@ obj- := # Core support for all Samsung SoCs obj-y += init.o +obj-y += time.o diff --git a/arch/arm/plat-s3c/time.c b/arch/arm/plat-s3c/time.c new file mode 100644 index 00000000000..c6861a05a29 --- /dev/null +++ b/arch/arm/plat-s3c/time.c @@ -0,0 +1,260 @@ +/* linux/arch/arm/plat-s3c24xx/time.c + * + * Copyright (C) 2003-2005 Simtec Electronics + * Ben Dooks, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +static unsigned long timer_startval; +static unsigned long timer_usec_ticks; + +#define TIMER_USEC_SHIFT 16 + +/* we use the shifted arithmetic to work out the ratio of timer ticks + * to usecs, as often the peripheral clock is not a nice even multiple + * of 1MHz. + * + * shift of 14 and 15 are too low for the 12MHz, 16 seems to be ok + * for the current HZ value of 200 without producing overflows. + * + * Original patch by Dimitry Andric, updated by Ben Dooks +*/ + + +/* timer_mask_usec_ticks + * + * given a clock and divisor, make the value to pass into timer_ticks_to_usec + * to scale the ticks into usecs +*/ + +static inline unsigned long +timer_mask_usec_ticks(unsigned long scaler, unsigned long pclk) +{ + unsigned long den = pclk / 1000; + + return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den; +} + +/* timer_ticks_to_usec + * + * convert timer ticks to usec. +*/ + +static inline unsigned long timer_ticks_to_usec(unsigned long ticks) +{ + unsigned long res; + + res = ticks * timer_usec_ticks; + res += 1 << (TIMER_USEC_SHIFT - 4); /* round up slightly */ + + return res >> TIMER_USEC_SHIFT; +} + +/*** + * Returns microsecond since last clock interrupt. Note that interrupts + * will have been disabled by do_gettimeoffset() + * IRQs are disabled before entering here from do_gettimeofday() + */ + +#define SRCPND_TIMER4 (1<<(IRQ_TIMER4 - IRQ_EINT0)) + +static unsigned long s3c2410_gettimeoffset (void) +{ + unsigned long tdone; + unsigned long irqpend; + unsigned long tval; + + /* work out how many ticks have gone since last timer interrupt */ + + tval = __raw_readl(S3C2410_TCNTO(4)); + tdone = timer_startval - tval; + + /* check to see if there is an interrupt pending */ + + irqpend = __raw_readl(S3C2410_SRCPND); + if (irqpend & SRCPND_TIMER4) { + /* re-read the timer, and try and fix up for the missed + * interrupt. Note, the interrupt may go off before the + * timer has re-loaded from wrapping. + */ + + tval = __raw_readl(S3C2410_TCNTO(4)); + tdone = timer_startval - tval; + + if (tval != 0) + tdone += timer_startval; + } + + return timer_ticks_to_usec(tdone); +} + + +/* + * IRQ handler for the timer + */ +static irqreturn_t +s3c2410_timer_interrupt(int irq, void *dev_id) +{ + timer_tick(); + return IRQ_HANDLED; +} + +static struct irqaction s3c2410_timer_irq = { + .name = "S3C2410 Timer Tick", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = s3c2410_timer_interrupt, +}; + +#define use_tclk1_12() ( \ + machine_is_bast() || \ + machine_is_vr1000() || \ + machine_is_anubis() || \ + machine_is_osiris()) + +/* + * Set up timer interrupt, and return the current time in seconds. + * + * Currently we only use timer4, as it is the only timer which has no + * other function that can be exploited externally + */ +static void s3c2410_timer_setup (void) +{ + unsigned long tcon; + unsigned long tcnt; + unsigned long tcfg1; + unsigned long tcfg0; + + tcnt = 0xffff; /* default value for tcnt */ + + /* read the current timer configuration bits */ + + tcon = __raw_readl(S3C2410_TCON); + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg0 = __raw_readl(S3C2410_TCFG0); + + /* configure the system for whichever machine is in use */ + + if (use_tclk1_12()) { + /* timer is at 12MHz, scaler is 1 */ + timer_usec_ticks = timer_mask_usec_ticks(1, 12000000); + tcnt = 12000000 / HZ; + + tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; + tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1; + } else { + unsigned long pclk; + struct clk *clk; + + /* for the h1940 (and others), we use the pclk from the core + * to generate the timer values. since values around 50 to + * 70MHz are not values we can directly generate the timer + * value from, we need to pre-scale and divide before using it. + * + * for instance, using 50.7MHz and dividing by 6 gives 8.45MHz + * (8.45 ticks per usec) + */ + + /* this is used as default if no other timer can be found */ + + clk = clk_get(NULL, "timers"); + if (IS_ERR(clk)) + panic("failed to get clock for system timer"); + + clk_enable(clk); + + pclk = clk_get_rate(clk); + + /* configure clock tick */ + + timer_usec_ticks = timer_mask_usec_ticks(6, pclk); + + tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; + tcfg1 |= S3C2410_TCFG1_MUX4_DIV2; + + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; + tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT; + + tcnt = (pclk / 6) / HZ; + } + + /* timers reload after counting zero, so reduce the count by 1 */ + + tcnt--; + + printk(KERN_DEBUG "timer tcon=%08lx, tcnt %04lx, tcfg %08lx,%08lx, usec %08lx\n", + tcon, tcnt, tcfg0, tcfg1, timer_usec_ticks); + + /* check to see if timer is within 16bit range... */ + if (tcnt > 0xffff) { + panic("setup_timer: HZ is too small, cannot configure timer!"); + return; + } + + __raw_writel(tcfg1, S3C2410_TCFG1); + __raw_writel(tcfg0, S3C2410_TCFG0); + + timer_startval = tcnt; + __raw_writel(tcnt, S3C2410_TCNTB(4)); + + /* ensure timer is stopped... */ + + tcon &= ~(7<<20); + tcon |= S3C2410_TCON_T4RELOAD; + tcon |= S3C2410_TCON_T4MANUALUPD; + + __raw_writel(tcon, S3C2410_TCON); + __raw_writel(tcnt, S3C2410_TCNTB(4)); + __raw_writel(tcnt, S3C2410_TCMPB(4)); + + /* start the timer running */ + tcon |= S3C2410_TCON_T4START; + tcon &= ~S3C2410_TCON_T4MANUALUPD; + __raw_writel(tcon, S3C2410_TCON); +} + +static void __init s3c2410_timer_init(void) +{ + s3c2410_timer_setup(); + setup_irq(IRQ_TIMER4, &s3c2410_timer_irq); +} + +struct sys_timer s3c24xx_timer = { + .init = s3c2410_timer_init, + .offset = s3c2410_gettimeoffset, + .resume = s3c2410_timer_setup +}; diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile index 80cf8eb211f..8da14beda77 100644 --- a/arch/arm/plat-s3c24xx/Makefile +++ b/arch/arm/plat-s3c24xx/Makefile @@ -17,7 +17,6 @@ obj-y += irq.o obj-y += devs.o obj-y += gpio.o obj-y += gpiolib.o -obj-y += time.o obj-y += clock.o obj-y += pwm-clock.o obj-$(CONFIG_S3C24XX_DCLK) += clock-dclk.o diff --git a/arch/arm/plat-s3c24xx/time.c b/arch/arm/plat-s3c24xx/time.c deleted file mode 100644 index c51916236ac..00000000000 --- a/arch/arm/plat-s3c24xx/time.c +++ /dev/null @@ -1,260 +0,0 @@ -/* linux/arch/arm/plat-s3c24xx/time.c - * - * Copyright (C) 2003-2005 Simtec Electronics - * Ben Dooks, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -static unsigned long timer_startval; -static unsigned long timer_usec_ticks; - -#define TIMER_USEC_SHIFT 16 - -/* we use the shifted arithmetic to work out the ratio of timer ticks - * to usecs, as often the peripheral clock is not a nice even multiple - * of 1MHz. - * - * shift of 14 and 15 are too low for the 12MHz, 16 seems to be ok - * for the current HZ value of 200 without producing overflows. - * - * Original patch by Dimitry Andric, updated by Ben Dooks -*/ - - -/* timer_mask_usec_ticks - * - * given a clock and divisor, make the value to pass into timer_ticks_to_usec - * to scale the ticks into usecs -*/ - -static inline unsigned long -timer_mask_usec_ticks(unsigned long scaler, unsigned long pclk) -{ - unsigned long den = pclk / 1000; - - return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den; -} - -/* timer_ticks_to_usec - * - * convert timer ticks to usec. -*/ - -static inline unsigned long timer_ticks_to_usec(unsigned long ticks) -{ - unsigned long res; - - res = ticks * timer_usec_ticks; - res += 1 << (TIMER_USEC_SHIFT - 4); /* round up slightly */ - - return res >> TIMER_USEC_SHIFT; -} - -/*** - * Returns microsecond since last clock interrupt. Note that interrupts - * will have been disabled by do_gettimeoffset() - * IRQs are disabled before entering here from do_gettimeofday() - */ - -#define SRCPND_TIMER4 (1<<(IRQ_TIMER4 - IRQ_EINT0)) - -static unsigned long s3c2410_gettimeoffset (void) -{ - unsigned long tdone; - unsigned long irqpend; - unsigned long tval; - - /* work out how many ticks have gone since last timer interrupt */ - - tval = __raw_readl(S3C2410_TCNTO(4)); - tdone = timer_startval - tval; - - /* check to see if there is an interrupt pending */ - - irqpend = __raw_readl(S3C2410_SRCPND); - if (irqpend & SRCPND_TIMER4) { - /* re-read the timer, and try and fix up for the missed - * interrupt. Note, the interrupt may go off before the - * timer has re-loaded from wrapping. - */ - - tval = __raw_readl(S3C2410_TCNTO(4)); - tdone = timer_startval - tval; - - if (tval != 0) - tdone += timer_startval; - } - - return timer_ticks_to_usec(tdone); -} - - -/* - * IRQ handler for the timer - */ -static irqreturn_t -s3c2410_timer_interrupt(int irq, void *dev_id) -{ - timer_tick(); - return IRQ_HANDLED; -} - -static struct irqaction s3c2410_timer_irq = { - .name = "S3C2410 Timer Tick", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, - .handler = s3c2410_timer_interrupt, -}; - -#define use_tclk1_12() ( \ - machine_is_bast() || \ - machine_is_vr1000() || \ - machine_is_anubis() || \ - machine_is_osiris() ) - -/* - * Set up timer interrupt, and return the current time in seconds. - * - * Currently we only use timer4, as it is the only timer which has no - * other function that can be exploited externally - */ -static void s3c2410_timer_setup (void) -{ - unsigned long tcon; - unsigned long tcnt; - unsigned long tcfg1; - unsigned long tcfg0; - - tcnt = 0xffff; /* default value for tcnt */ - - /* read the current timer configuration bits */ - - tcon = __raw_readl(S3C2410_TCON); - tcfg1 = __raw_readl(S3C2410_TCFG1); - tcfg0 = __raw_readl(S3C2410_TCFG0); - - /* configure the system for whichever machine is in use */ - - if (use_tclk1_12()) { - /* timer is at 12MHz, scaler is 1 */ - timer_usec_ticks = timer_mask_usec_ticks(1, 12000000); - tcnt = 12000000 / HZ; - - tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; - tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1; - } else { - unsigned long pclk; - struct clk *clk; - - /* for the h1940 (and others), we use the pclk from the core - * to generate the timer values. since values around 50 to - * 70MHz are not values we can directly generate the timer - * value from, we need to pre-scale and divide before using it. - * - * for instance, using 50.7MHz and dividing by 6 gives 8.45MHz - * (8.45 ticks per usec) - */ - - /* this is used as default if no other timer can be found */ - - clk = clk_get(NULL, "timers"); - if (IS_ERR(clk)) - panic("failed to get clock for system timer"); - - clk_enable(clk); - - pclk = clk_get_rate(clk); - - /* configure clock tick */ - - timer_usec_ticks = timer_mask_usec_ticks(6, pclk); - - tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; - tcfg1 |= S3C2410_TCFG1_MUX4_DIV2; - - tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; - tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT; - - tcnt = (pclk / 6) / HZ; - } - - /* timers reload after counting zero, so reduce the count by 1 */ - - tcnt--; - - printk("timer tcon=%08lx, tcnt %04lx, tcfg %08lx,%08lx, usec %08lx\n", - tcon, tcnt, tcfg0, tcfg1, timer_usec_ticks); - - /* check to see if timer is within 16bit range... */ - if (tcnt > 0xffff) { - panic("setup_timer: HZ is too small, cannot configure timer!"); - return; - } - - __raw_writel(tcfg1, S3C2410_TCFG1); - __raw_writel(tcfg0, S3C2410_TCFG0); - - timer_startval = tcnt; - __raw_writel(tcnt, S3C2410_TCNTB(4)); - - /* ensure timer is stopped... */ - - tcon &= ~(7<<20); - tcon |= S3C2410_TCON_T4RELOAD; - tcon |= S3C2410_TCON_T4MANUALUPD; - - __raw_writel(tcon, S3C2410_TCON); - __raw_writel(tcnt, S3C2410_TCNTB(4)); - __raw_writel(tcnt, S3C2410_TCMPB(4)); - - /* start the timer running */ - tcon |= S3C2410_TCON_T4START; - tcon &= ~S3C2410_TCON_T4MANUALUPD; - __raw_writel(tcon, S3C2410_TCON); -} - -static void __init s3c2410_timer_init (void) -{ - s3c2410_timer_setup(); - setup_irq(IRQ_TIMER4, &s3c2410_timer_irq); -} - -struct sys_timer s3c24xx_timer = { - .init = s3c2410_timer_init, - .offset = s3c2410_gettimeoffset, - .resume = s3c2410_timer_setup -}; -- cgit v1.2.3 From adbefaa5fdc6673bf389c00c3448b12d6125990b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:54 +0100 Subject: [ARM] S3C: Move core clock support to plat-s3c Move the core clock registration and definitions in arch/arm/plat-s3c24xx/clock.c to arch/arm/plat-s3c to be shared with the S3C64XX implementations. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/Makefile | 1 + arch/arm/plat-s3c/clock.c | 356 ++++++++++++++++++++++++++++++++++++++++++ arch/arm/plat-s3c24xx/clock.c | 314 ------------------------------------- 3 files changed, 357 insertions(+), 314 deletions(-) create mode 100644 arch/arm/plat-s3c/clock.c (limited to 'arch/arm') diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index 3c543ed5fb9..51e7ed5c5c9 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -13,3 +13,4 @@ obj- := obj-y += init.o obj-y += time.o +obj-y += clock.o \ No newline at end of file diff --git a/arch/arm/plat-s3c/clock.c b/arch/arm/plat-s3c/clock.c new file mode 100644 index 00000000000..35249b58f38 --- /dev/null +++ b/arch/arm/plat-s3c/clock.c @@ -0,0 +1,356 @@ +/* linux/arch/arm/plat-s3c24xx/clock.c + * + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks + * + * S3C24XX Core clock control support + * + * Based on, and code from linux/arch/arm/mach-versatile/clock.c + ** + ** Copyright (C) 2004 ARM Limited. + ** Written by Deep Blue Solutions Limited. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +/* clock information */ + +static LIST_HEAD(clocks); + +/* We originally used an mutex here, but some contexts (see resume) + * are calling functions such as clk_set_parent() with IRQs disabled + * causing an BUG to be triggered. + */ +DEFINE_SPINLOCK(clocks_lock); + +/* enable and disable calls for use with the clk struct */ + +static int clk_null_enable(struct clk *clk, int enable) +{ + return 0; +} + +/* Clock API calls */ + +struct clk *clk_get(struct device *dev, const char *id) +{ + struct clk *p; + struct clk *clk = ERR_PTR(-ENOENT); + int idno; + + if (dev == NULL || dev->bus != &platform_bus_type) + idno = -1; + else + idno = to_platform_device(dev)->id; + + spin_lock(&clocks_lock); + + list_for_each_entry(p, &clocks, list) { + if (p->id == idno && + strcmp(id, p->name) == 0 && + try_module_get(p->owner)) { + clk = p; + break; + } + } + + /* check for the case where a device was supplied, but the + * clock that was being searched for is not device specific */ + + if (IS_ERR(clk)) { + list_for_each_entry(p, &clocks, list) { + if (p->id == -1 && strcmp(id, p->name) == 0 && + try_module_get(p->owner)) { + clk = p; + break; + } + } + } + + spin_unlock(&clocks_lock); + return clk; +} + +void clk_put(struct clk *clk) +{ + module_put(clk->owner); +} + +int clk_enable(struct clk *clk) +{ + if (IS_ERR(clk) || clk == NULL) + return -EINVAL; + + clk_enable(clk->parent); + + spin_lock(&clocks_lock); + + if ((clk->usage++) == 0) + (clk->enable)(clk, 1); + + spin_unlock(&clocks_lock); + return 0; +} + +void clk_disable(struct clk *clk) +{ + if (IS_ERR(clk) || clk == NULL) + return; + + spin_lock(&clocks_lock); + + if ((--clk->usage) == 0) + (clk->enable)(clk, 0); + + spin_unlock(&clocks_lock); + clk_disable(clk->parent); +} + + +unsigned long clk_get_rate(struct clk *clk) +{ + if (IS_ERR(clk)) + return 0; + + if (clk->rate != 0) + return clk->rate; + + if (clk->get_rate != NULL) + return (clk->get_rate)(clk); + + if (clk->parent != NULL) + return clk_get_rate(clk->parent); + + return clk->rate; +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (!IS_ERR(clk) && clk->round_rate) + return (clk->round_rate)(clk, rate); + + return rate; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret; + + if (IS_ERR(clk)) + return -EINVAL; + + /* We do not default just do a clk->rate = rate as + * the clock may have been made this way by choice. + */ + + WARN_ON(clk->set_rate == NULL); + + if (clk->set_rate == NULL) + return -EINVAL; + + spin_lock(&clocks_lock); + ret = (clk->set_rate)(clk, rate); + spin_unlock(&clocks_lock); + + return ret; +} + +struct clk *clk_get_parent(struct clk *clk) +{ + return clk->parent; +} + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int ret = 0; + + if (IS_ERR(clk)) + return -EINVAL; + + spin_lock(&clocks_lock); + + if (clk->set_parent) + ret = (clk->set_parent)(clk, parent); + + spin_unlock(&clocks_lock); + + return ret; +} + +EXPORT_SYMBOL(clk_get); +EXPORT_SYMBOL(clk_put); +EXPORT_SYMBOL(clk_enable); +EXPORT_SYMBOL(clk_disable); +EXPORT_SYMBOL(clk_get_rate); +EXPORT_SYMBOL(clk_round_rate); +EXPORT_SYMBOL(clk_set_rate); +EXPORT_SYMBOL(clk_get_parent); +EXPORT_SYMBOL(clk_set_parent); + +/* base clocks */ + +static int clk_default_setrate(struct clk *clk, unsigned long rate) +{ + clk->rate = rate; + return 0; +} + +struct clk clk_xtal = { + .name = "xtal", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, +}; + +struct clk clk_mpll = { + .name = "mpll", + .id = -1, + .set_rate = clk_default_setrate, +}; + +struct clk clk_upll = { + .name = "upll", + .id = -1, + .parent = NULL, + .ctrlbit = 0, +}; + +struct clk clk_f = { + .name = "fclk", + .id = -1, + .rate = 0, + .parent = &clk_mpll, + .ctrlbit = 0, + .set_rate = clk_default_setrate, +}; + +struct clk clk_h = { + .name = "hclk", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, + .set_rate = clk_default_setrate, +}; + +struct clk clk_p = { + .name = "pclk", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, + .set_rate = clk_default_setrate, +}; + +struct clk clk_usb_bus = { + .name = "usb-bus", + .id = -1, + .rate = 0, + .parent = &clk_upll, +}; + + + +struct clk s3c24xx_uclk = { + .name = "uclk", + .id = -1, +}; + +/* initialise the clock system */ + +int s3c24xx_register_clock(struct clk *clk) +{ + clk->owner = THIS_MODULE; + + if (clk->enable == NULL) + clk->enable = clk_null_enable; + + /* add to the list of available clocks */ + + spin_lock(&clocks_lock); + list_add(&clk->list, &clocks); + spin_unlock(&clocks_lock); + + return 0; +} + +int s3c24xx_register_clocks(struct clk **clks, int nr_clks) +{ + int fails = 0; + + for (; nr_clks > 0; nr_clks--, clks++) { + if (s3c24xx_register_clock(*clks) < 0) + fails++; + } + + return fails; +} + +/* initalise all the clocks */ + +int __init s3c24xx_register_baseclocks(unsigned long xtal) +{ + printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n"); + + clk_xtal.rate = xtal; + + /* register our clocks */ + + if (s3c24xx_register_clock(&clk_xtal) < 0) + printk(KERN_ERR "failed to register master xtal\n"); + + if (s3c24xx_register_clock(&clk_mpll) < 0) + printk(KERN_ERR "failed to register mpll clock\n"); + + if (s3c24xx_register_clock(&clk_upll) < 0) + printk(KERN_ERR "failed to register upll clock\n"); + + if (s3c24xx_register_clock(&clk_f) < 0) + printk(KERN_ERR "failed to register cpu fclk\n"); + + if (s3c24xx_register_clock(&clk_h) < 0) + printk(KERN_ERR "failed to register cpu hclk\n"); + + if (s3c24xx_register_clock(&clk_p) < 0) + printk(KERN_ERR "failed to register cpu pclk\n"); + + return 0; +} + diff --git a/arch/arm/plat-s3c24xx/clock.c b/arch/arm/plat-s3c24xx/clock.c index a4a0a67a307..8474d05274b 100644 --- a/arch/arm/plat-s3c24xx/clock.c +++ b/arch/arm/plat-s3c24xx/clock.c @@ -27,18 +27,8 @@ */ #include -#include #include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include #include @@ -53,280 +43,6 @@ #include #include -/* clock information */ - -static LIST_HEAD(clocks); - -/* We originally used an mutex here, but some contexts (see resume) - * are calling functions such as clk_set_parent() with IRQs disabled - * causing an BUG to be triggered. - */ -DEFINE_SPINLOCK(clocks_lock); - -/* enable and disable calls for use with the clk struct */ - -static int clk_null_enable(struct clk *clk, int enable) -{ - return 0; -} - -/* Clock API calls */ - -struct clk *clk_get(struct device *dev, const char *id) -{ - struct clk *p; - struct clk *clk = ERR_PTR(-ENOENT); - int idno; - - if (dev == NULL || dev->bus != &platform_bus_type) - idno = -1; - else - idno = to_platform_device(dev)->id; - - spin_lock(&clocks_lock); - - list_for_each_entry(p, &clocks, list) { - if (p->id == idno && - strcmp(id, p->name) == 0 && - try_module_get(p->owner)) { - clk = p; - break; - } - } - - /* check for the case where a device was supplied, but the - * clock that was being searched for is not device specific */ - - if (IS_ERR(clk)) { - list_for_each_entry(p, &clocks, list) { - if (p->id == -1 && strcmp(id, p->name) == 0 && - try_module_get(p->owner)) { - clk = p; - break; - } - } - } - - spin_unlock(&clocks_lock); - return clk; -} - -void clk_put(struct clk *clk) -{ - module_put(clk->owner); -} - -int clk_enable(struct clk *clk) -{ - if (IS_ERR(clk) || clk == NULL) - return -EINVAL; - - clk_enable(clk->parent); - - spin_lock(&clocks_lock); - - if ((clk->usage++) == 0) - (clk->enable)(clk, 1); - - spin_unlock(&clocks_lock); - return 0; -} - -void clk_disable(struct clk *clk) -{ - if (IS_ERR(clk) || clk == NULL) - return; - - spin_lock(&clocks_lock); - - if ((--clk->usage) == 0) - (clk->enable)(clk, 0); - - spin_unlock(&clocks_lock); - clk_disable(clk->parent); -} - - -unsigned long clk_get_rate(struct clk *clk) -{ - if (IS_ERR(clk)) - return 0; - - if (clk->rate != 0) - return clk->rate; - - if (clk->get_rate != NULL) - return (clk->get_rate)(clk); - - if (clk->parent != NULL) - return clk_get_rate(clk->parent); - - return clk->rate; -} - -long clk_round_rate(struct clk *clk, unsigned long rate) -{ - if (!IS_ERR(clk) && clk->round_rate) - return (clk->round_rate)(clk, rate); - - return rate; -} - -int clk_set_rate(struct clk *clk, unsigned long rate) -{ - int ret; - - if (IS_ERR(clk)) - return -EINVAL; - - /* We do not default just do a clk->rate = rate as - * the clock may have been made this way by choice. - */ - - WARN_ON(clk->set_rate == NULL); - - if (clk->set_rate == NULL) - return -EINVAL; - - spin_lock(&clocks_lock); - ret = (clk->set_rate)(clk, rate); - spin_unlock(&clocks_lock); - - return ret; -} - -struct clk *clk_get_parent(struct clk *clk) -{ - return clk->parent; -} - -int clk_set_parent(struct clk *clk, struct clk *parent) -{ - int ret = 0; - - if (IS_ERR(clk)) - return -EINVAL; - - spin_lock(&clocks_lock); - - if (clk->set_parent) - ret = (clk->set_parent)(clk, parent); - - spin_unlock(&clocks_lock); - - return ret; -} - -EXPORT_SYMBOL(clk_get); -EXPORT_SYMBOL(clk_put); -EXPORT_SYMBOL(clk_enable); -EXPORT_SYMBOL(clk_disable); -EXPORT_SYMBOL(clk_get_rate); -EXPORT_SYMBOL(clk_round_rate); -EXPORT_SYMBOL(clk_set_rate); -EXPORT_SYMBOL(clk_get_parent); -EXPORT_SYMBOL(clk_set_parent); - -/* base clocks */ - -static int clk_default_setrate(struct clk *clk, unsigned long rate) -{ - clk->rate = rate; - return 0; -} - -struct clk clk_xtal = { - .name = "xtal", - .id = -1, - .rate = 0, - .parent = NULL, - .ctrlbit = 0, -}; - -struct clk clk_mpll = { - .name = "mpll", - .id = -1, - .set_rate = clk_default_setrate, -}; - -struct clk clk_upll = { - .name = "upll", - .id = -1, - .parent = NULL, - .ctrlbit = 0, -}; - -struct clk clk_f = { - .name = "fclk", - .id = -1, - .rate = 0, - .parent = &clk_mpll, - .ctrlbit = 0, - .set_rate = clk_default_setrate, -}; - -struct clk clk_h = { - .name = "hclk", - .id = -1, - .rate = 0, - .parent = NULL, - .ctrlbit = 0, - .set_rate = clk_default_setrate, -}; - -struct clk clk_p = { - .name = "pclk", - .id = -1, - .rate = 0, - .parent = NULL, - .ctrlbit = 0, - .set_rate = clk_default_setrate, -}; - -struct clk clk_usb_bus = { - .name = "usb-bus", - .id = -1, - .rate = 0, - .parent = &clk_upll, -}; - - - -struct clk s3c24xx_uclk = { - .name = "uclk", - .id = -1, -}; - -/* initialise the clock system */ - -int s3c24xx_register_clock(struct clk *clk) -{ - clk->owner = THIS_MODULE; - - if (clk->enable == NULL) - clk->enable = clk_null_enable; - - /* add to the list of available clocks */ - - spin_lock(&clocks_lock); - list_add(&clk->list, &clocks); - spin_unlock(&clocks_lock); - - return 0; -} - -int s3c24xx_register_clocks(struct clk **clks, int nr_clks) -{ - int fails = 0; - - for (; nr_clks > 0; nr_clks--, clks++) { - if (s3c24xx_register_clock(*clks) < 0) - fails++; - } - - return fails; -} - /* initalise all the clocks */ void __init_or_cpufreq s3c24xx_setup_clocks(unsigned long fclk, @@ -341,33 +57,3 @@ void __init_or_cpufreq s3c24xx_setup_clocks(unsigned long fclk, clk_p.rate = pclk; clk_f.rate = fclk; } - -int __init s3c24xx_register_baseclocks(unsigned long xtal) -{ - printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n"); - - clk_xtal.rate = xtal; - - /* register our clocks */ - - if (s3c24xx_register_clock(&clk_xtal) < 0) - printk(KERN_ERR "failed to register master xtal\n"); - - if (s3c24xx_register_clock(&clk_mpll) < 0) - printk(KERN_ERR "failed to register mpll clock\n"); - - if (s3c24xx_register_clock(&clk_upll) < 0) - printk(KERN_ERR "failed to register upll clock\n"); - - if (s3c24xx_register_clock(&clk_f) < 0) - printk(KERN_ERR "failed to register cpu fclk\n"); - - if (s3c24xx_register_clock(&clk_h) < 0) - printk(KERN_ERR "failed to register cpu hclk\n"); - - if (s3c24xx_register_clock(&clk_p) < 0) - printk(KERN_ERR "failed to register cpu pclk\n"); - - return 0; -} - -- cgit v1.2.3 From 93b458858f6d40650513f63feef0b350eff4845f Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:55 +0100 Subject: [ARM] S3C: Move pwm-clock.c to arch/arm/plat-s3c Move pwm-clock.c to arch/arm/plat-s3c to be shared with the S3C64XX implementations. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/Makefile | 3 +- arch/arm/plat-s3c/pwm-clock.c | 437 ++++++++++++++++++++++++++++++++++++++ arch/arm/plat-s3c24xx/Makefile | 1 - arch/arm/plat-s3c24xx/pwm-clock.c | 437 -------------------------------------- 4 files changed, 439 insertions(+), 439 deletions(-) create mode 100644 arch/arm/plat-s3c/pwm-clock.c delete mode 100644 arch/arm/plat-s3c24xx/pwm-clock.c (limited to 'arch/arm') diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index 51e7ed5c5c9..a2fe3c77564 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -13,4 +13,5 @@ obj- := obj-y += init.o obj-y += time.o -obj-y += clock.o \ No newline at end of file +obj-y += clock.o +obj-y += pwm-clock.o \ No newline at end of file diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c new file mode 100644 index 00000000000..3fad68a1e6b --- /dev/null +++ b/arch/arm/plat-s3c/pwm-clock.c @@ -0,0 +1,437 @@ +/* linux/arch/arm/plat-s3c24xx/pwm-clock.c + * + * Copyright (c) 2007 Simtec Electronics + * Copyright (c) 2007, 2008 Ben Dooks + * Ben Dooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +/* Each of the timers 0 through 5 go through the following + * clock tree, with the inputs depending on the timers. + * + * pclk ---- [ prescaler 0 ] -+---> timer 0 + * +---> timer 1 + * + * pclk ---- [ prescaler 1 ] -+---> timer 2 + * +---> timer 3 + * \---> timer 4 + * + * Which are fed into the timers as so: + * + * prescaled 0 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 0 + * tclk 0 ------------------------------/ + * + * prescaled 0 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 1 + * tclk 0 ------------------------------/ + * + * + * prescaled 1 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 2 + * tclk 1 ------------------------------/ + * + * prescaled 1 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 3 + * tclk 1 ------------------------------/ + * + * prescaled 1 ---- [ div 2,4,8, 16 ] --\ + * [mux] -> timer 4 + * tclk 1 ------------------------------/ + * + * Since the mux and the divider are tied together in the + * same register space, it is impossible to set the parent + * and the rate at the same time. To avoid this, we add an + * intermediate 'prescaled-and-divided' clock to select + * as the parent for the timer input clock called tdiv. + * + * prescaled clk --> pwm-tdiv ---\ + * [ mux ] --> timer X + * tclk -------------------------/ +*/ + +static unsigned long clk_pwm_scaler_getrate(struct clk *clk) +{ + unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); + + if (clk->id == 1) { + tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK; + tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT; + } else { + tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK; + } + + return clk_get_rate(clk->parent) / (tcfg0 + 1); +} + +/* TODO - add set rate calls. */ + +static struct clk clk_timer_scaler[] = { + [0] = { + .name = "pwm-scaler0", + .id = -1, + .get_rate = clk_pwm_scaler_getrate, + }, + [1] = { + .name = "pwm-scaler1", + .id = -1, + .get_rate = clk_pwm_scaler_getrate, + }, +}; + +static struct clk clk_timer_tclk[] = { + [0] = { + .name = "pwm-tclk0", + .id = -1, + }, + [1] = { + .name = "pwm-tclk1", + .id = -1, + }, +}; + +struct pwm_tdiv_clk { + struct clk clk; + unsigned int divisor; +}; + +static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk) +{ + return container_of(clk, struct pwm_tdiv_clk, clk); +} + +static inline unsigned long tcfg_to_divisor(unsigned long tcfg1) +{ + return 1 << (1 + tcfg1); +} + +static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) +{ + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned int divisor; + + tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) + divisor = to_tdiv(clk)->divisor; + else + divisor = tcfg_to_divisor(tcfg1); + + return clk_get_rate(clk->parent) / divisor; +} + +static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, + unsigned long rate) +{ + unsigned long parent_rate; + unsigned long divisor; + + parent_rate = clk_get_rate(clk->parent); + divisor = parent_rate / rate; + + if (divisor <= 2) + divisor = 2; + else if (divisor <= 4) + divisor = 4; + else if (divisor <= 8) + divisor = 8; + else + divisor = 16; + + return parent_rate / divisor; +} + +static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk) +{ + unsigned long bits; + + switch (divclk->divisor) { + case 2: + bits = S3C2410_TCFG1_MUX_DIV2; + break; + case 4: + bits = S3C2410_TCFG1_MUX_DIV4; + break; + case 8: + bits = S3C2410_TCFG1_MUX_DIV8; + break; + case 16: + default: + bits = S3C2410_TCFG1_MUX_DIV16; + break; + } + + return bits; +} + +static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk) +{ + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned long bits = clk_pwm_tdiv_bits(divclk); + unsigned long flags; + unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id); + + local_irq_save(flags); + + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); + tcfg1 |= bits << shift; + __raw_writel(tcfg1, S3C2410_TCFG1); + + local_irq_restore(flags); +} + +static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) +{ + struct pwm_tdiv_clk *divclk = to_tdiv(clk); + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long divisor; + + tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + rate = clk_round_rate(clk, rate); + divisor = parent_rate / rate; + + if (divisor > 16) + return -EINVAL; + + divclk->divisor = divisor; + + /* Update the current MUX settings if we are currently + * selected as the clock source for this clock. */ + + if (tcfg1 != S3C2410_TCFG1_MUX_TCLK) + clk_pwm_tdiv_update(divclk); + + return 0; +} + +static struct pwm_tdiv_clk clk_timer_tdiv[] = { + [0] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[0], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + }, + }, + [1] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[0], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + } + }, + [2] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[1], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + }, + }, + [3] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[1], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + }, + }, + [4] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[1], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + }, + }, +}; + +static int __init clk_pwm_tdiv_register(unsigned int id) +{ + struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id]; + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + + tcfg1 >>= S3C2410_TCFG1_SHIFT(id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + divclk->clk.id = id; + divclk->divisor = tcfg_to_divisor(tcfg1); + + return s3c24xx_register_clock(&divclk->clk); +} + +static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id) +{ + return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0]; +} + +static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id) +{ + return &clk_timer_tdiv[id].clk; +} + +static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) +{ + unsigned int id = clk->id; + unsigned long tcfg1; + unsigned long flags; + unsigned long bits; + unsigned long shift = S3C2410_TCFG1_SHIFT(id); + + if (parent == s3c24xx_pwmclk_tclk(id)) + bits = S3C2410_TCFG1_MUX_TCLK << shift; + else if (parent == s3c24xx_pwmclk_tdiv(id)) + bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift; + else + return -EINVAL; + + clk->parent = parent; + + local_irq_save(flags); + + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); + __raw_writel(tcfg1 | bits, S3C2410_TCFG1); + + local_irq_restore(flags); + + return 0; +} + +static struct clk clk_tin[] = { + [0] = { + .name = "pwm-tin", + .id = 0, + .set_parent = clk_pwm_tin_set_parent, + }, + [1] = { + .name = "pwm-tin", + .id = 1, + .set_parent = clk_pwm_tin_set_parent, + }, + [2] = { + .name = "pwm-tin", + .id = 2, + .set_parent = clk_pwm_tin_set_parent, + }, + [3] = { + .name = "pwm-tin", + .id = 3, + .set_parent = clk_pwm_tin_set_parent, + }, + [4] = { + .name = "pwm-tin", + .id = 4, + .set_parent = clk_pwm_tin_set_parent, + }, +}; + +static __init int clk_pwm_tin_register(struct clk *pwm) +{ + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned int id = pwm->id; + + struct clk *parent; + int ret; + + ret = s3c24xx_register_clock(pwm); + if (ret < 0) + return ret; + + tcfg1 >>= S3C2410_TCFG1_SHIFT(id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) + parent = s3c24xx_pwmclk_tclk(id); + else + parent = s3c24xx_pwmclk_tdiv(id); + + return clk_set_parent(pwm, parent); +} + +static __init int s3c24xx_pwmclk_init(void) +{ + struct clk *clk_timers; + unsigned int clk; + int ret; + + clk_timers = clk_get(NULL, "timers"); + if (IS_ERR(clk_timers)) { + printk(KERN_ERR "%s: no parent clock\n", __func__); + return -EINVAL; + } + + for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) { + clk_timer_scaler[clk].parent = clk_timers; + ret = s3c24xx_register_clock(&clk_timer_scaler[clk]); + if (ret < 0) { + printk(KERN_ERR "error adding pwm scaler%d clock\n", clk); + goto err; + } + } + + for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) { + ret = s3c24xx_register_clock(&clk_timer_tclk[clk]); + if (ret < 0) { + printk(KERN_ERR "error adding pww tclk%d\n", clk); + goto err; + } + } + + for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) { + ret = clk_pwm_tdiv_register(clk); + if (ret < 0) { + printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); + goto err; + } + } + + for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) { + ret = clk_pwm_tin_register(&clk_tin[clk]); + if (ret < 0) { + printk(KERN_ERR "error adding pwm%d tin clock\n", clk); + goto err; + } + } + + return 0; + + err: + return ret; +} + +arch_initcall(s3c24xx_pwmclk_init); diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile index 8da14beda77..2a65ba7eb34 100644 --- a/arch/arm/plat-s3c24xx/Makefile +++ b/arch/arm/plat-s3c24xx/Makefile @@ -18,7 +18,6 @@ obj-y += devs.o obj-y += gpio.o obj-y += gpiolib.o obj-y += clock.o -obj-y += pwm-clock.o obj-$(CONFIG_S3C24XX_DCLK) += clock-dclk.o # Architecture dependant builds diff --git a/arch/arm/plat-s3c24xx/pwm-clock.c b/arch/arm/plat-s3c24xx/pwm-clock.c deleted file mode 100644 index 3fad68a1e6b..00000000000 --- a/arch/arm/plat-s3c24xx/pwm-clock.c +++ /dev/null @@ -1,437 +0,0 @@ -/* linux/arch/arm/plat-s3c24xx/pwm-clock.c - * - * Copyright (c) 2007 Simtec Electronics - * Copyright (c) 2007, 2008 Ben Dooks - * Ben Dooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include - -#include - -/* Each of the timers 0 through 5 go through the following - * clock tree, with the inputs depending on the timers. - * - * pclk ---- [ prescaler 0 ] -+---> timer 0 - * +---> timer 1 - * - * pclk ---- [ prescaler 1 ] -+---> timer 2 - * +---> timer 3 - * \---> timer 4 - * - * Which are fed into the timers as so: - * - * prescaled 0 ---- [ div 2,4,8,16 ] ---\ - * [mux] -> timer 0 - * tclk 0 ------------------------------/ - * - * prescaled 0 ---- [ div 2,4,8,16 ] ---\ - * [mux] -> timer 1 - * tclk 0 ------------------------------/ - * - * - * prescaled 1 ---- [ div 2,4,8,16 ] ---\ - * [mux] -> timer 2 - * tclk 1 ------------------------------/ - * - * prescaled 1 ---- [ div 2,4,8,16 ] ---\ - * [mux] -> timer 3 - * tclk 1 ------------------------------/ - * - * prescaled 1 ---- [ div 2,4,8, 16 ] --\ - * [mux] -> timer 4 - * tclk 1 ------------------------------/ - * - * Since the mux and the divider are tied together in the - * same register space, it is impossible to set the parent - * and the rate at the same time. To avoid this, we add an - * intermediate 'prescaled-and-divided' clock to select - * as the parent for the timer input clock called tdiv. - * - * prescaled clk --> pwm-tdiv ---\ - * [ mux ] --> timer X - * tclk -------------------------/ -*/ - -static unsigned long clk_pwm_scaler_getrate(struct clk *clk) -{ - unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); - - if (clk->id == 1) { - tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK; - tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT; - } else { - tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK; - } - - return clk_get_rate(clk->parent) / (tcfg0 + 1); -} - -/* TODO - add set rate calls. */ - -static struct clk clk_timer_scaler[] = { - [0] = { - .name = "pwm-scaler0", - .id = -1, - .get_rate = clk_pwm_scaler_getrate, - }, - [1] = { - .name = "pwm-scaler1", - .id = -1, - .get_rate = clk_pwm_scaler_getrate, - }, -}; - -static struct clk clk_timer_tclk[] = { - [0] = { - .name = "pwm-tclk0", - .id = -1, - }, - [1] = { - .name = "pwm-tclk1", - .id = -1, - }, -}; - -struct pwm_tdiv_clk { - struct clk clk; - unsigned int divisor; -}; - -static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk) -{ - return container_of(clk, struct pwm_tdiv_clk, clk); -} - -static inline unsigned long tcfg_to_divisor(unsigned long tcfg1) -{ - return 1 << (1 + tcfg1); -} - -static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) -{ - unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); - unsigned int divisor; - - tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); - tcfg1 &= S3C2410_TCFG1_MUX_MASK; - - if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) - divisor = to_tdiv(clk)->divisor; - else - divisor = tcfg_to_divisor(tcfg1); - - return clk_get_rate(clk->parent) / divisor; -} - -static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, - unsigned long rate) -{ - unsigned long parent_rate; - unsigned long divisor; - - parent_rate = clk_get_rate(clk->parent); - divisor = parent_rate / rate; - - if (divisor <= 2) - divisor = 2; - else if (divisor <= 4) - divisor = 4; - else if (divisor <= 8) - divisor = 8; - else - divisor = 16; - - return parent_rate / divisor; -} - -static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk) -{ - unsigned long bits; - - switch (divclk->divisor) { - case 2: - bits = S3C2410_TCFG1_MUX_DIV2; - break; - case 4: - bits = S3C2410_TCFG1_MUX_DIV4; - break; - case 8: - bits = S3C2410_TCFG1_MUX_DIV8; - break; - case 16: - default: - bits = S3C2410_TCFG1_MUX_DIV16; - break; - } - - return bits; -} - -static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk) -{ - unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); - unsigned long bits = clk_pwm_tdiv_bits(divclk); - unsigned long flags; - unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id); - - local_irq_save(flags); - - tcfg1 = __raw_readl(S3C2410_TCFG1); - tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); - tcfg1 |= bits << shift; - __raw_writel(tcfg1, S3C2410_TCFG1); - - local_irq_restore(flags); -} - -static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) -{ - struct pwm_tdiv_clk *divclk = to_tdiv(clk); - unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); - unsigned long parent_rate = clk_get_rate(clk->parent); - unsigned long divisor; - - tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); - tcfg1 &= S3C2410_TCFG1_MUX_MASK; - - rate = clk_round_rate(clk, rate); - divisor = parent_rate / rate; - - if (divisor > 16) - return -EINVAL; - - divclk->divisor = divisor; - - /* Update the current MUX settings if we are currently - * selected as the clock source for this clock. */ - - if (tcfg1 != S3C2410_TCFG1_MUX_TCLK) - clk_pwm_tdiv_update(divclk); - - return 0; -} - -static struct pwm_tdiv_clk clk_timer_tdiv[] = { - [0] = { - .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[0], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, - }, - }, - [1] = { - .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[0], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, - } - }, - [2] = { - .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[1], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, - }, - }, - [3] = { - .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[1], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, - }, - }, - [4] = { - .clk = { - .name = "pwm-tdiv", - .parent = &clk_timer_scaler[1], - .get_rate = clk_pwm_tdiv_get_rate, - .set_rate = clk_pwm_tdiv_set_rate, - .round_rate = clk_pwm_tdiv_round_rate, - }, - }, -}; - -static int __init clk_pwm_tdiv_register(unsigned int id) -{ - struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id]; - unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); - - tcfg1 >>= S3C2410_TCFG1_SHIFT(id); - tcfg1 &= S3C2410_TCFG1_MUX_MASK; - - divclk->clk.id = id; - divclk->divisor = tcfg_to_divisor(tcfg1); - - return s3c24xx_register_clock(&divclk->clk); -} - -static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id) -{ - return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0]; -} - -static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id) -{ - return &clk_timer_tdiv[id].clk; -} - -static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) -{ - unsigned int id = clk->id; - unsigned long tcfg1; - unsigned long flags; - unsigned long bits; - unsigned long shift = S3C2410_TCFG1_SHIFT(id); - - if (parent == s3c24xx_pwmclk_tclk(id)) - bits = S3C2410_TCFG1_MUX_TCLK << shift; - else if (parent == s3c24xx_pwmclk_tdiv(id)) - bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift; - else - return -EINVAL; - - clk->parent = parent; - - local_irq_save(flags); - - tcfg1 = __raw_readl(S3C2410_TCFG1); - tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); - __raw_writel(tcfg1 | bits, S3C2410_TCFG1); - - local_irq_restore(flags); - - return 0; -} - -static struct clk clk_tin[] = { - [0] = { - .name = "pwm-tin", - .id = 0, - .set_parent = clk_pwm_tin_set_parent, - }, - [1] = { - .name = "pwm-tin", - .id = 1, - .set_parent = clk_pwm_tin_set_parent, - }, - [2] = { - .name = "pwm-tin", - .id = 2, - .set_parent = clk_pwm_tin_set_parent, - }, - [3] = { - .name = "pwm-tin", - .id = 3, - .set_parent = clk_pwm_tin_set_parent, - }, - [4] = { - .name = "pwm-tin", - .id = 4, - .set_parent = clk_pwm_tin_set_parent, - }, -}; - -static __init int clk_pwm_tin_register(struct clk *pwm) -{ - unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); - unsigned int id = pwm->id; - - struct clk *parent; - int ret; - - ret = s3c24xx_register_clock(pwm); - if (ret < 0) - return ret; - - tcfg1 >>= S3C2410_TCFG1_SHIFT(id); - tcfg1 &= S3C2410_TCFG1_MUX_MASK; - - if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) - parent = s3c24xx_pwmclk_tclk(id); - else - parent = s3c24xx_pwmclk_tdiv(id); - - return clk_set_parent(pwm, parent); -} - -static __init int s3c24xx_pwmclk_init(void) -{ - struct clk *clk_timers; - unsigned int clk; - int ret; - - clk_timers = clk_get(NULL, "timers"); - if (IS_ERR(clk_timers)) { - printk(KERN_ERR "%s: no parent clock\n", __func__); - return -EINVAL; - } - - for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) { - clk_timer_scaler[clk].parent = clk_timers; - ret = s3c24xx_register_clock(&clk_timer_scaler[clk]); - if (ret < 0) { - printk(KERN_ERR "error adding pwm scaler%d clock\n", clk); - goto err; - } - } - - for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) { - ret = s3c24xx_register_clock(&clk_timer_tclk[clk]); - if (ret < 0) { - printk(KERN_ERR "error adding pww tclk%d\n", clk); - goto err; - } - } - - for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) { - ret = clk_pwm_tdiv_register(clk); - if (ret < 0) { - printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); - goto err; - } - } - - for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) { - ret = clk_pwm_tin_register(&clk_tin[clk]); - if (ret < 0) { - printk(KERN_ERR "error adding pwm%d tin clock\n", clk); - goto err; - } - } - - return 0; - - err: - return ret; -} - -arch_initcall(s3c24xx_pwmclk_init); -- cgit v1.2.3 From cec444b79073e7d6ef35ced5ef63c3fd83d9b9a7 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:07:10 +0100 Subject: [ARM] S3C: BUG_ON() if clock has already been registered Add a simple check when registering a clock on whether the clock has already been added to the list. Any attempt to re-register a clock will cause the clock list to be come looped and thus produces silent failures when looking up clocks. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/clock.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'arch/arm') diff --git a/arch/arm/plat-s3c/clock.c b/arch/arm/plat-s3c/clock.c index 35249b58f38..da7ac07c7a0 100644 --- a/arch/arm/plat-s3c/clock.c +++ b/arch/arm/plat-s3c/clock.c @@ -304,6 +304,9 @@ int s3c24xx_register_clock(struct clk *clk) /* add to the list of available clocks */ + /* Quick check to see if this clock has already been registered. */ + BUG_ON(clk->list.prev != clk->list.next); + spin_lock(&clocks_lock); list_add(&clk->list, &clocks); spin_unlock(&clocks_lock); -- cgit v1.2.3 From f2edc7565a14aa82231c847abf33d02314cac9f9 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 31 Oct 2008 16:14:27 +0000 Subject: [ARM] S3C24XX: Reduce code lineage of gpiolib.c All the s3c24xx gpiolib chips share the same get/set calls and all but one bank shares the same calls for .direction_input and .direction_output methods. Change the initialisation process to use an new call to register the chips that fills in any blank calls with the default values to avoid having to fill them in the structure initialisers. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c24xx/gpiolib.c | 49 ++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 27 deletions(-) (limited to 'arch/arm') diff --git a/arch/arm/plat-s3c24xx/gpiolib.c b/arch/arm/plat-s3c24xx/gpiolib.c index 3caec6bad3e..b07c2d0dd53 100644 --- a/arch/arm/plat-s3c24xx/gpiolib.c +++ b/arch/arm/plat-s3c24xx/gpiolib.c @@ -161,8 +161,6 @@ static struct s3c24xx_gpio_chip gpios[] = { .ngpio = 24, .direction_input = s3c24xx_gpiolib_banka_input, .direction_output = s3c24xx_gpiolib_banka_output, - .set = s3c24xx_gpiolib_set, - .get = s3c24xx_gpiolib_get, }, }, [1] = { @@ -172,10 +170,6 @@ static struct s3c24xx_gpio_chip gpios[] = { .owner = THIS_MODULE, .label = "GPIOB", .ngpio = 16, - .direction_input = s3c24xx_gpiolib_input, - .direction_output = s3c24xx_gpiolib_output, - .set = s3c24xx_gpiolib_set, - .get = s3c24xx_gpiolib_get, }, }, [2] = { @@ -185,10 +179,6 @@ static struct s3c24xx_gpio_chip gpios[] = { .owner = THIS_MODULE, .label = "GPIOC", .ngpio = 16, - .direction_input = s3c24xx_gpiolib_input, - .direction_output = s3c24xx_gpiolib_output, - .set = s3c24xx_gpiolib_set, - .get = s3c24xx_gpiolib_get, }, }, [3] = { @@ -198,10 +188,6 @@ static struct s3c24xx_gpio_chip gpios[] = { .owner = THIS_MODULE, .label = "GPIOD", .ngpio = 16, - .direction_input = s3c24xx_gpiolib_input, - .direction_output = s3c24xx_gpiolib_output, - .set = s3c24xx_gpiolib_set, - .get = s3c24xx_gpiolib_get, }, }, [4] = { @@ -211,10 +197,6 @@ static struct s3c24xx_gpio_chip gpios[] = { .label = "GPIOE", .owner = THIS_MODULE, .ngpio = 16, - .direction_input = s3c24xx_gpiolib_input, - .direction_output = s3c24xx_gpiolib_output, - .set = s3c24xx_gpiolib_set, - .get = s3c24xx_gpiolib_get, }, }, [5] = { @@ -224,10 +206,6 @@ static struct s3c24xx_gpio_chip gpios[] = { .owner = THIS_MODULE, .label = "GPIOF", .ngpio = 8, - .direction_input = s3c24xx_gpiolib_input, - .direction_output = s3c24xx_gpiolib_output, - .set = s3c24xx_gpiolib_set, - .get = s3c24xx_gpiolib_get, }, }, [6] = { @@ -237,21 +215,38 @@ static struct s3c24xx_gpio_chip gpios[] = { .owner = THIS_MODULE, .label = "GPIOG", .ngpio = 10, - .direction_input = s3c24xx_gpiolib_input, - .direction_output = s3c24xx_gpiolib_output, - .set = s3c24xx_gpiolib_set, - .get = s3c24xx_gpiolib_get, }, }, }; +static __init void s3c24xx_gpiolib_add(struct s3c24xx_gpio_chip *chip) +{ + struct gpio_chip *gc = &chip->chip; + + BUG_ON(!chip->base); + BUG_ON(!gc->label); + BUG_ON(!gc->ngpio); + + if (!gc->direction_input) + gc->direction_input = s3c24xx_gpiolib_input; + if (!gc->direction_output) + gc->direction_output = s3c24xx_gpiolib_output; + if (!gc->set) + gc->set = s3c24xx_gpiolib_set; + if (!gc->get) + gc->get = s3c24xx_gpiolib_get; + + /* gpiochip_add() prints own failure message on error. */ + gpiochip_add(gc); +} + static __init int s3c24xx_gpiolib_init(void) { struct s3c24xx_gpio_chip *chip = gpios; int gpn; for (gpn = 0; gpn < ARRAY_SIZE(gpios); gpn++, chip++) - gpiochip_add(&chip->chip); + s3c24xx_gpiolib_add(chip); return 0; } -- cgit v1.2.3 From 82fd8e681d60a195ce6e9fc783d0ebe7a81b1ead Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 21 Nov 2008 10:36:04 +0000 Subject: [ARM] S3C: Add set_rate/round_rate methods for pwm-scaler clock Add the set_rate and round_rate methods for the pwm-scaler clock for use with the time code. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/pwm-clock.c | 51 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) (limited to 'arch/arm') diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c index 3fad68a1e6b..b0fcbc1fb64 100644 --- a/arch/arm/plat-s3c/pwm-clock.c +++ b/arch/arm/plat-s3c/pwm-clock.c @@ -73,7 +73,7 @@ * tclk -------------------------/ */ -static unsigned long clk_pwm_scaler_getrate(struct clk *clk) +static unsigned long clk_pwm_scaler_get_rate(struct clk *clk) { unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); @@ -87,18 +87,61 @@ static unsigned long clk_pwm_scaler_getrate(struct clk *clk) return clk_get_rate(clk->parent) / (tcfg0 + 1); } -/* TODO - add set rate calls. */ +static unsigned long clk_pwm_scaler_round_rate(struct clk *clk, + unsigned long rate) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long divisor = parent_rate / rate; + + if (divisor > 256) + divisor = 256; + else if (divisor < 2) + divisor = 2; + + return parent_rate / divisor; +} + +static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long round = clk_pwm_scaler_round_rate(clk, rate); + unsigned long tcfg0; + unsigned long divisor; + unsigned long flags; + + divisor = clk_get_rate(clk->parent) / round; + divisor--; + + local_irq_save(flags); + tcfg0 = __raw_readl(S3C2410_TCFG0); + + if (clk->id == 1) { + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; + tcfg0 |= divisor << S3C2410_TCFG_PRESCALER1_SHIFT; + } else { + tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; + tcfg0 |= divisor; + } + + __raw_writel(tcfg0, S3C2410_TCFG0); + local_irq_restore(flags); + + return 0; +} static struct clk clk_timer_scaler[] = { [0] = { .name = "pwm-scaler0", .id = -1, - .get_rate = clk_pwm_scaler_getrate, + .get_rate = clk_pwm_scaler_get_rate, + .set_rate = clk_pwm_scaler_set_rate, + .round_rate = clk_pwm_scaler_round_rate, }, [1] = { .name = "pwm-scaler1", .id = -1, - .get_rate = clk_pwm_scaler_getrate, + .get_rate = clk_pwm_scaler_get_rate, + .set_rate = clk_pwm_scaler_set_rate, + .round_rate = clk_pwm_scaler_round_rate, }, }; -- cgit v1.2.3 From 7d2dbcf9faad074c52a941d01fc21eea3c95ca33 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 21 Nov 2008 10:36:06 +0000 Subject: [ARM] S3C: Fix scaler1 clock rate information The pwm-scaler0 and pwm-scaler1 clocks have their .id field set to -1 as they are not referenced to any specific device. However, parts of the pwm-clock code used the .id field to identify which scaler clock was being used. Fix the problem by comparing against the pointer to the clock to identify the scalers. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/pwm-clock.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'arch/arm') diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c index b0fcbc1fb64..e07d82891a9 100644 --- a/arch/arm/plat-s3c/pwm-clock.c +++ b/arch/arm/plat-s3c/pwm-clock.c @@ -73,11 +73,13 @@ * tclk -------------------------/ */ +static struct clk clk_timer_scaler[]; + static unsigned long clk_pwm_scaler_get_rate(struct clk *clk) { unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); - if (clk->id == 1) { + if (clk == &clk_timer_scaler[1]) { tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK; tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT; } else { @@ -114,7 +116,7 @@ static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate) local_irq_save(flags); tcfg0 = __raw_readl(S3C2410_TCFG0); - if (clk->id == 1) { + if (clk == &clk_timer_scaler[1]) { tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; tcfg0 |= divisor << S3C2410_TCFG_PRESCALER1_SHIFT; } else { -- cgit v1.2.3 From e7aa6f46f6e4644d466e04f54b5750a91d188abf Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 2 Dec 2008 19:34:52 +0000 Subject: [ARM] S3C: Add UART FIFO selection during arch decompression Add a configuration option to start the UART FIFOs during the decompressions sequence to improve boot time when the bootloader fails to enable the UART FIFOs. For example, the SMDK6410 UBoot 1.1.6 leaves the FIFOs off. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/Kconfig | 8 ++++++++ arch/arm/plat-s3c/include/plat/uncompress.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'arch/arm') diff --git a/arch/arm/plat-s3c/Kconfig b/arch/arm/plat-s3c/Kconfig index 31656c33e05..10c1dd3cd46 100644 --- a/arch/arm/plat-s3c/Kconfig +++ b/arch/arm/plat-s3c/Kconfig @@ -57,6 +57,14 @@ config S3C_BOOT_ERROR_RESET Say y here to use the watchdog to reset the system if the kernel decompressor detects an error during decompression. +config S3C_BOOT_UART_FORCE_FIFO + bool "Force UART FIFO on during boot process" + depends on PLAT_S3C + default y + help + Say Y here to force the UART FIFOs on during the kernel + uncompressor + comment "Power management" config S3C2410_PM_DEBUG diff --git a/arch/arm/plat-s3c/include/plat/uncompress.h b/arch/arm/plat-s3c/include/plat/uncompress.h index 8a8a927292e..2c39a309aeb 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) static void error(char *err); +#ifdef CONFIG_S3C_BOOT_UART_FORCE_FIFO +static inline void arch_enable_uart_fifo(void) +{ + u32 fifocon = uart_rd(S3C2410_UFCON); + + if (!(fifocon & S3C2410_UFCON_FIFOMODE)) { + fifocon |= S3C2410_UFCON_RESETBOTH; + uart_wr(S3C2410_UFCON, fifocon); + + /* wait for fifo reset to complete */ + while (1) { + fifocon = uart_rd(S3C2410_UFCON); + if (!(fifocon & S3C2410_UFCON_RESETBOTH)) + break; + } + } +} +#else +#define arch_enable_uart_fifo() do { } while(0) +#endif + + static void arch_decomp_setup(void) { @@ -149,6 +171,12 @@ arch_decomp_setup(void) arch_detect_cpu(); arch_decomp_wdog_start(); + + /* Enable the UART FIFOs if they where not enabled and our + * configuration says we should turn them on. + */ + + arch_enable_uart_fifo(); } -- cgit v1.2.3 From 28ab44c5be60a9b91021a7cc7b4e202775c04764 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Thu, 18 Dec 2008 14:20:04 +0000 Subject: [ARM] S3C24XX: ADC driver core A common core driver for the S3C24XX ADC block so that the touchscreen, hwmon and any other drivers can share the resource. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/include/plat/adc.h | 29 +++ arch/arm/plat-s3c24xx/Kconfig | 7 + arch/arm/plat-s3c24xx/Makefile | 1 + arch/arm/plat-s3c24xx/adc.c | 372 +++++++++++++++++++++++++++++++++++ arch/arm/plat-s3c24xx/devs.c | 10 +- 5 files changed, 418 insertions(+), 1 deletion(-) create mode 100644 arch/arm/plat-s3c/include/plat/adc.h create mode 100644 arch/arm/plat-s3c24xx/adc.c (limited to 'arch/arm') 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 00000000000..43df2a404b0 --- /dev/null +++ b/arch/arm/plat-s3c/include/plat/adc.h @@ -0,0 +1,29 @@ +/* arch/arm/plat-s3c/include/plat/adc.h + * + * Copyright (c) 2008 Simtec Electronics + * http://armlinux.simnte.co.uk/ + * Ben Dooks + * + * S3C24XX ADC driver information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __ASM_PLAT_ADC_H +#define __ASM_PLAT_ADC_H __FILE__ + +struct s3c_adc_client; + +extern int s3c_adc_start(struct s3c_adc_client *client, + unsigned int channel, unsigned int nr_samples); + +extern struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev, + void (*select)(unsigned selected), + void (*conv)(unsigned d0, unsigned d1), + unsigned int is_ts); + +extern void s3c_adc_release(struct s3c_adc_client *client); + +#endif /* __ASM_PLAT_ADC_H */ diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig index d3faf01bbfc..7d3d6eb5205 100644 --- a/arch/arm/plat-s3c24xx/Kconfig +++ b/arch/arm/plat-s3c24xx/Kconfig @@ -62,6 +62,13 @@ config S3C2410_DMA_DEBUG Enable debugging output for the DMA code. This option sends info to the kernel log, at priority KERN_DEBUG. +config S3C24XX_ADC + bool "ADC common driver support" + help + Core support for the ADC block found in the S3C24XX SoC systems + for drivers such as the touchscreen and hwmon to use to share + this resource. + # SPI default pin configuration code config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13 diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile index 2a65ba7eb34..a8cfdefc29e 100644 --- a/arch/arm/plat-s3c24xx/Makefile +++ b/arch/arm/plat-s3c24xx/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_PM) += sleep.o obj-$(CONFIG_HAVE_PWM) += pwm.o obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o obj-$(CONFIG_S3C2410_DMA) += dma.o +obj-$(CONFIG_S3C24XX_ADC) += adc.o # SPI gpio central GPIO functions diff --git a/arch/arm/plat-s3c24xx/adc.c b/arch/arm/plat-s3c24xx/adc.c new file mode 100644 index 00000000000..9a5c767e0a4 --- /dev/null +++ b/arch/arm/plat-s3c24xx/adc.c @@ -0,0 +1,372 @@ +/* arch/arm/plat-s3c24xx/adc.c + * + * Copyright (c) 2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks , + * + * S3C24XX ADC device core + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* This driver is designed to control the usage of the ADC block between + * the touchscreen and any other drivers that may need to use it, such as + * the hwmon driver. + * + * Priority will be given to the touchscreen driver, but as this itself is + * rate limited it should not starve other requests which are processed in + * order that they are received. + * + * Each user registers to get a client block which uniquely identifies it + * and stores information such as the necessary functions to callback when + * action is required. + */ + +struct s3c_adc_client { + struct platform_device *pdev; + struct list_head pend; + + unsigned int nr_samples; + unsigned char is_ts; + unsigned char channel; + + void (*select_cb)(unsigned selected); + void (*convert_cb)(unsigned val1, unsigned val2); +}; + +struct adc_device { + struct platform_device *pdev; + struct platform_device *owner; + struct clk *clk; + struct s3c_adc_client *cur; + struct s3c_adc_client *ts_pend; + void __iomem *regs; + + unsigned int prescale; + + int irq; +}; + +static struct adc_device *adc_dev; + +static LIST_HEAD(adc_pending); + +#define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg) + +static inline void s3c_adc_convert(struct adc_device *adc) +{ + unsigned con = readl(adc->regs + S3C2410_ADCCON); + + con |= S3C2410_ADCCON_ENABLE_START; + writel(con, adc->regs + S3C2410_ADCCON); +} + +static inline void s3c_adc_select(struct adc_device *adc, + struct s3c_adc_client *client) +{ + unsigned con = readl(adc->regs + S3C2410_ADCCON); + + client->select_cb(1); + + con &= ~S3C2410_ADCCON_MUXMASK; + con &= ~S3C2410_ADCCON_STDBM; + con &= ~S3C2410_ADCCON_STARTMASK; + + if (!client->is_ts) + con |= S3C2410_ADCCON_SELMUX(client->channel); + + writel(con, adc->regs + S3C2410_ADCCON); +} + +static void s3c_adc_dbgshow(struct adc_device *adc) +{ + adc_dbg(adc, "CON=%08x, TSC=%08x, DLY=%08x\n", + readl(adc->regs + S3C2410_ADCCON), + readl(adc->regs + S3C2410_ADCTSC), + readl(adc->regs + S3C2410_ADCDLY)); +} + +void s3c_adc_try(struct adc_device *adc) +{ + struct s3c_adc_client *next = adc->ts_pend; + + if (!next && !list_empty(&adc_pending)) { + next = list_first_entry(&adc_pending, + struct s3c_adc_client, pend); + list_del(&next->pend); + } else + adc->ts_pend = NULL; + + if (next) { + adc_dbg(adc, "new client is %p\n", next); + adc->cur = next; + s3c_adc_select(adc, next); + s3c_adc_convert(adc); + s3c_adc_dbgshow(adc); + } +} + +int s3c_adc_start(struct s3c_adc_client *client, + unsigned int channel, unsigned int nr_samples) +{ + struct adc_device *adc = adc_dev; + unsigned long flags; + + if (!adc) { + printk(KERN_ERR "%s: failed to find adc\n", __func__); + return -EINVAL; + } + + if (client->is_ts && adc->ts_pend) + return -EAGAIN; + + local_irq_save(flags); + + client->channel = channel; + client->nr_samples = nr_samples; + + if (client->is_ts) + adc->ts_pend = client; + else + list_add_tail(&client->pend, &adc_pending); + + if (!adc->cur) + s3c_adc_try(adc); + local_irq_restore(flags); + + return 0; +} +EXPORT_SYMBOL_GPL(s3c_adc_start); + +static void s3c_adc_default_select(unsigned select) +{ +} + +struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev, + void (*select)(unsigned int selected), + void (*conv)(unsigned d0, unsigned d1), + unsigned int is_ts) +{ + struct s3c_adc_client *client; + + WARN_ON(!pdev); + WARN_ON(!conv); + + if (!select) + select = s3c_adc_default_select; + + if (!conv || !pdev) + return ERR_PTR(-EINVAL); + + client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL); + if (!client) { + dev_err(&pdev->dev, "no memory for adc client\n"); + return ERR_PTR(-ENOMEM); + } + + client->pdev = pdev; + client->is_ts = is_ts; + client->select_cb = select; + client->convert_cb = conv; + + return client; +} +EXPORT_SYMBOL_GPL(s3c_adc_register); + +void s3c_adc_release(struct s3c_adc_client *client) +{ + /* We should really check that nothing is in progress. */ + kfree(client); +} +EXPORT_SYMBOL_GPL(s3c_adc_release); + +static irqreturn_t s3c_adc_irq(int irq, void *pw) +{ + struct adc_device *adc = pw; + struct s3c_adc_client *client = adc->cur; + unsigned long flags; + unsigned data0, data1; + + if (!client) { + dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__); + return IRQ_HANDLED; + } + + data0 = readl(adc->regs + S3C2410_ADCDAT0); + data1 = readl(adc->regs + S3C2410_ADCDAT1); + adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1); + + (client->convert_cb)(data0 & 0x3ff, data1 & 0x3ff); + + if (--client->nr_samples > 0) { + /* fire another conversion for this */ + + client->select_cb(1); + s3c_adc_convert(adc); + } else { + local_irq_save(flags); + (client->select_cb)(0); + adc->cur = NULL; + + s3c_adc_try(adc); + local_irq_restore(flags); + } + + return IRQ_HANDLED; +} + +static int s3c_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adc_device *adc; + struct resource *regs; + int ret; + + adc = kzalloc(sizeof(struct adc_device), GFP_KERNEL); + if (adc == NULL) { + dev_err(dev, "failed to allocate adc_device\n"); + return -ENOMEM; + } + + adc->pdev = pdev; + adc->prescale = S3C2410_ADCCON_PRSCVL(49); + + adc->irq = platform_get_irq(pdev, 1); + if (adc->irq <= 0) { + dev_err(dev, "failed to get adc irq\n"); + ret = -ENOENT; + goto err_alloc; + } + + ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc); + if (ret < 0) { + dev_err(dev, "failed to attach adc irq\n"); + goto err_alloc; + } + + adc->clk = clk_get(dev, "adc"); + if (IS_ERR(adc->clk)) { + dev_err(dev, "failed to get adc clock\n"); + ret = PTR_ERR(adc->clk); + goto err_irq; + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(dev, "failed to find registers\n"); + ret = -ENXIO; + goto err_clk; + } + + adc->regs = ioremap(regs->start, resource_size(regs)); + if (!adc->regs) { + dev_err(dev, "failed to map registers\n"); + ret = -ENXIO; + goto err_clk; + } + + clk_enable(adc->clk); + + writel(adc->prescale | S3C2410_ADCCON_PRSCEN, + adc->regs + S3C2410_ADCCON); + + dev_info(dev, "attached adc driver\n"); + + platform_set_drvdata(pdev, adc); + adc_dev = adc; + + return 0; + + err_clk: + clk_put(adc->clk); + + err_irq: + free_irq(adc->irq, adc); + + err_alloc: + kfree(adc); + return ret; +} + +static int s3c_adc_remove(struct platform_device *pdev) +{ + struct adc_device *adc = platform_get_drvdata(pdev); + + iounmap(adc->regs); + free_irq(adc->irq, adc); + clk_disable(adc->clk); + clk_put(adc->clk); + kfree(adc); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct adc_device *adc = platform_get_drvdata(pdev); + u32 con; + + con = readl(adc->regs + S3C2410_ADCCON); + con |= S3C2410_ADCCON_STDBM; + writel(con, adc->regs + S3C2410_ADCCON); + + clk_disable(adc->clk); + + return 0; +} + +static int s3c_adc_resume(struct platform_device *pdev) +{ + struct adc_device *adc = platform_get_drvdata(pdev); + + clk_enable(adc->clk); + + writel(adc->prescale | S3C2410_ADCCON_PRSCEN, + adc->regs + S3C2410_ADCCON); + + return 0; +} + +#else +#define s3c_adc_suspend NULL +#define s3c_adc_resume NULL +#endif + +static struct platform_driver s3c_adc_driver = { + .driver = { + .name = "s3c24xx-adc", + .owner = THIS_MODULE, + }, + .probe = s3c_adc_probe, + .remove = __devexit_p(s3c_adc_remove), + .suspend = s3c_adc_suspend, + .resume = s3c_adc_resume, +}; + +static int __init adc_init(void) +{ + int ret; + + ret = platform_driver_register(&s3c_adc_driver); + if (ret) + printk(KERN_ERR "%s: failed to add adc driver\n", __func__); + + return ret; +} + +arch_initcall(adc_init); diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c index adf535aaf43..6a33dbc494f 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[] = { }; struct platform_device s3c_device_adc = { - .name = "s3c2410-adc", + .name = "s3c24xx-adc", .id = -1, .num_resources = ARRAY_SIZE(s3c_adc_resource), .resource = s3c_adc_resource, }; +/* HWMON */ + +struct platform_device s3c_device_hwmon = { + .name = "s3c24xx-hwmon", + .id = -1, + .dev.parent = &s3c_device_adc.dev, +}; + /* SDI */ static struct resource s3c_sdi_resource[] = { -- cgit v1.2.3