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
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
config MACH_VR1000
bool "Thorcom VR1000"
select PM_SIMTEC if PM
+ select S3C24XX_DCLK
select SIMTEC_NOR
select MACH_BAST_IDE
select CPU_S3C2410
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
+++ /dev/null
-/* linux/arch/arm/mach-s3c2410/clock.c
- *
- * Copyright (c) 2006 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * 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 <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/sysdev.h>
-#include <linux/clk.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-#include <linux/serial_core.h>
-#include <linux/io.h>
-
-#include <asm/mach/map.h>
-
-#include <mach/hardware.h>
-
-#include <plat/regs-serial.h>
-#include <mach/regs-clock.h>
-#include <mach/regs-gpio.h>
-
-#include <plat/s3c2410.h>
-#include <plat/clock.h>
-#include <plat/cpu.h>
-
-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;
-}
#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)
#define S3C2410_CLKSLOW_SLOWVAL(x) (x)
#define S3C2410_CLKSLOW_GET_SLOWVAL(x) ((x) & 7)
-#ifndef __ASSEMBLY__
-
-#include <asm/div64.h>
-
-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 */
#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)
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 */
+++ /dev/null
-/* arch/arm/mach-s3c2410/include/mach/timex.h
- *
- * Copyright (c) 2003-2005 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * 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 */
+++ /dev/null
-/* arch/arm/mach-s3c2410/include/mach/vmalloc.h
- *
- * from arch/arm/mach-iop3xx/include/mach/vmalloc.h
- *
- * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
- * 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 */
#include <plat/clock.h>
#include <plat/devs.h>
#include <plat/cpu.h>
+#include <plat/pll.h>
#include <plat/pm.h>
static struct map_desc h1940_iodesc[] __initdata = {
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));
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/sysdev.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <mach/hardware.h>
#include <asm/irq.h>
+#include <plat/cpu-freq.h>
+
#include <mach/regs-clock.h>
#include <plat/regs-serial.h>
#include <plat/cpu.h>
#include <plat/devs.h>
#include <plat/clock.h>
+#include <plat/pll.h>
/* Initial IO mappings */
* 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)
+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 */
- fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal);
+ fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal);
tmp = __raw_readl(S3C2410_CLKDIVN);
* 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();
}
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/sysdev.h>
#include <linux/serial_core.h>
#include <mach/reset.h>
#include <mach/idle.h>
+#include <plat/cpu-freq.h>
+
#include <mach/regs-clock.h>
#include <plat/regs-serial.h>
#include <mach/regs-power.h>
#include <plat/devs.h>
#include <plat/clock.h>
#include <plat/pm.h>
+#include <plat/pll.h>
#ifndef CONFIG_CPU_S3C2412_ONLY
void __iomem *s3c24xx_va_gpio2 = S3C24XX_VA_GPIO;
* 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 */
/* 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)
+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 = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal*2);
+ fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal * 2);
clk_mpll.rate = fclk;
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();
}
config MACH_ANUBIS
bool "Simtec Electronics ANUBIS"
select CPU_S3C2440
+ select S3C24XX_DCLK
select PM_SIMTEC if PM
select HAVE_PATA_PLATFORM
select S3C24XX_GPIO_EXTRA64
config MACH_OSIRIS
bool "Simtec IM2440D20 (OSIRIS) module"
select CPU_S3C2440
+ select S3C24XX_DCLK
select PM_SIMTEC if PM
select S3C24XX_GPIO_EXTRA128
help
#include <mach/regs-s3c2443-clock.h>
+#include <plat/cpu-freq.h>
+
#include <plat/s3c2443.h>
#include <plat/clock.h>
#include <plat/cpu.h>
&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;
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++) {
}
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 */
* 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
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
-# 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
+obj-y += time.o
+obj-y += clock.o
+obj-y += pwm-clock.o
\ No newline at end of file
--- /dev/null
+/* linux/arch/arm/plat-s3c24xx/clock.c
+ *
+ * Copyright (c) 2004-2005 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/sysdev.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+
+#include <plat/cpu-freq.h>
+
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+/* 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 */
+
+ /* 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);
+
+ 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;
+}
+
--- /dev/null
+/* arch/arm/plat-s3c/include/mach/io.h
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben-linux@fluff.org>
+ *
+ * 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
--- /dev/null
+/* arch/arm/mach-s3c2410/include/mach/timex.h
+ *
+ * Copyright (c) 2003-2005 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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 */
--- /dev/null
+/* arch/arm/plat-s3c/include/mach/vmalloc.h
+ *
+ * from arch/arm/mach-iop3xx/include/mach/vmalloc.h
+ *
+ * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
+ * 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 */
--- /dev/null
+/* arch/arm/plat-s3c/include/plat/adc.h
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * http://armlinux.simnte.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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 */
--- /dev/null
+/* 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, <ben@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.
+*/
+
+#include <linux/spinlock.h>
+
+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);
+
--- /dev/null
+/* arch/arm/plat-s3c/include/plat/cpu-freq.h
+ *
+ * Copyright (c) 2006,2007 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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 <linux/cpufreq.h>
+
+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 */
--- /dev/null
+/* linux/arch/arm/plat-s3c/include/plat/cpu.h
+ *
+ * Copyright (c) 2004-2005 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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;
--- /dev/null
+/* linux/include/asm-arm/plat-s3c24xx/devs.h
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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 <linux/platform_device.h>
+
+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
--- /dev/null
+/* arch/arm/plat-s3c/include/plat/regs-irqtype.h
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * 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)
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)
{
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();
}
--- /dev/null
+/* linux/arch/arm/plat-s3c/init.c
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+
+#include <mach/hardware.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/clock.h>
+
+#include <plat/regs-serial.h>
+
+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);
--- /dev/null
+/* linux/arch/arm/plat-s3c24xx/pwm-clock.c
+ *
+ * Copyright (c) 2007 Simtec Electronics
+ * Copyright (c) 2007, 2008 Ben Dooks
+ * Ben Dooks <ben-linux@fluff.org>
+ *
+ * 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+
+#include <mach/regs-clock.h>
+#include <mach/regs-gpio.h>
+
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+#include <plat/regs-timer.h>
+
+/* 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 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 == &clk_timer_scaler[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);
+}
+
+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 == &clk_timer_scaler[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_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_get_rate,
+ .set_rate = clk_pwm_scaler_set_rate,
+ .round_rate = clk_pwm_scaler_round_rate,
+ },
+};
+
+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);
--- /dev/null
+/* linux/arch/arm/plat-s3c24xx/time.c
+ *
+ * Copyright (C) 2003-2005 Simtec Electronics
+ * Ben Dooks, <ben@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 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 <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/leds.h>
+#include <asm/mach-types.h>
+
+#include <asm/irq.h>
+#include <mach/map.h>
+#include <plat/regs-timer.h>
+#include <mach/regs-irq.h>
+#include <asm/mach/time.h>
+
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+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
+};
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 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)
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
+ 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
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
# Architecture dependant builds
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
+obj-$(CONFIG_S3C24XX_ADC) += adc.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
--- /dev/null
+/* arch/arm/plat-s3c24xx/adc.c
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <plat/regs-adc.h>
+#include <plat/adc.h>
+
+/* 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);
--- /dev/null
+/* linux/arch/arm/plat-s3c24xx/clock-dclk.c
+ *
+ * Copyright (c) 2004,2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * 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 <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/regs-clock.h>
+#include <mach/regs-gpio.h>
+
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+/* 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,
+};
*/
#include <linux/init.h>
-#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/sysdev.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
#include <linux/clk.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
+#include <plat/cpu-freq.h>
+
#include <plat/clock.h>
#include <plat/cpu.h>
-
-/* clock information */
-
-static LIST_HEAD(clocks);
-
-DEFINE_MUTEX(clocks_mutex);
-
-/* 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;
-
- mutex_lock(&clocks_mutex);
-
- 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;
- }
- }
- }
-
- mutex_unlock(&clocks_mutex);
- 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);
-
- mutex_lock(&clocks_mutex);
-
- if ((clk->usage++) == 0)
- (clk->enable)(clk, 1);
-
- mutex_unlock(&clocks_mutex);
- return 0;
-}
-
-void clk_disable(struct clk *clk)
-{
- if (IS_ERR(clk) || clk == NULL)
- return;
-
- mutex_lock(&clocks_mutex);
-
- if ((--clk->usage) == 0)
- (clk->enable)(clk, 0);
-
- mutex_unlock(&clocks_mutex);
- 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;
-
- mutex_lock(&clocks_mutex);
- ret = (clk->set_rate)(clk, rate);
- mutex_unlock(&clocks_mutex);
-
- 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;
-
- mutex_lock(&clocks_mutex);
-
- if (clk->set_parent)
- ret = (clk->set_parent)(clk, parent);
-
- mutex_unlock(&clocks_mutex);
-
- 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,
-};
-
-/* 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",
- .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 */
-
- mutex_lock(&clocks_mutex);
- list_add(&clk->list, &clocks);
- mutex_unlock(&clocks_mutex);
-
- 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;
-}
+#include <plat/pll.h>
/* 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 = s3c2410_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 */
-
- /* 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;
}
#include <plat/s3c2442.h>
#include <plat/s3c2443.h>
-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";
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)
{
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) {
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);
};
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[] = {
.ngpio = 24,
.direction_input = s3c24xx_gpiolib_banka_input,
.direction_output = s3c24xx_gpiolib_banka_output,
- .set = s3c24xx_gpiolib_set,
- .get = s3c24xx_gpiolib_get,
},
},
[1] = {
.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] = {
.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] = {
.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] = {
.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] = {
.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] = {
.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;
}
+++ /dev/null
-/* 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, <ben@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.
-*/
-
-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 struct mutex clocks_mutex;
-
-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);
+++ /dev/null
-/* linux/include/asm-arm/plat-s3c24xx/cpu.h
- *
- * Copyright (c) 2004-2005 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * 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;
-
-/* 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;
+++ /dev/null
-/* linux/include/asm-arm/plat-s3c24xx/devs.h
- *
- * Copyright (c) 2004 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * 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 <linux/platform_device.h>
-
-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
--- /dev/null
+/* linux/arch/arm/plat-s3c24xx/include/plat/pll.h
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * 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 <asm/div64.h>
+
+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;
+}
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);
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);
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);
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);
#include <asm/mach/irq.h>
+#include <plat/regs-irqtype.h>
#include <mach/regs-irq.h>
#include <mach/regs-gpio.h>
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 {
+++ /dev/null
-/* linux/arch/arm/plat-s3c24xx/pwm-clock.c
- *
- * Copyright (c) 2007 Simtec Electronics
- * Copyright (c) 2007, 2008 Ben Dooks
- * Ben Dooks <ben-linux@fluff.org>
- *
- * 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 <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/errno.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/io.h>
-
-#include <mach/hardware.h>
-#include <asm/irq.h>
-
-#include <mach/regs-clock.h>
-#include <mach/regs-gpio.h>
-
-#include <plat/clock.h>
-#include <plat/cpu.h>
-
-#include <plat/regs-timer.h>
-
-/* 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);
--- /dev/null
+/* linux/arch/arm/mach-s3c2410/clock.c
+ *
+ * Copyright (c) 2006 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/sysdev.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/serial_core.h>
+#include <linux/io.h>
+
+#include <asm/mach/map.h>
+
+#include <mach/hardware.h>
+
+#include <plat/regs-serial.h>
+#include <mach/regs-clock.h>
+#include <mach/regs-gpio.h>
+
+#include <plat/s3c2410.h>
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+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;
+}
#include <linux/sysdev.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
-#include <linux/mutex.h>
#include <linux/clk.h>
#include <linux/io.h>
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;
#include <mach/hardware.h>
#include <asm/irq.h>
+#include <plat/cpu-freq.h>
+
#include <mach/regs-clock.h>
#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <plat/devs.h>
#include <plat/cpu.h>
#include <plat/pm.h>
+#include <plat/pll.h>
static struct map_desc s3c244x_iodesc[] __initdata = {
IODESC_ENT(CLKPWR),
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 */
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 = 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);
}
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();
}
#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);
--- /dev/null
+/* linux/arch/arm/plat-s3c24xx/spi-bus0-gpe11_12_13.c
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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 <linux/kernel.h>
+
+#include <mach/hardware.h>
+
+#include <mach/spi.h>
+#include <mach/regs-gpio.h>
+
+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);
+ }
+}
--- /dev/null
+/* linux/arch/arm/plat-s3c24xx/spi-bus0-gpg5_6_7.c
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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 <linux/kernel.h>
+
+#include <mach/hardware.h>
+
+#include <mach/spi.h>
+#include <mach/regs-gpio.h>
+
+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);
+ }
+}
+++ /dev/null
-/* linux/arch/arm/plat-s3c24xx/time.c
- *
- * Copyright (C) 2003-2005 Simtec Electronics
- * Ben Dooks, <ben@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 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 <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-
-#include <asm/system.h>
-#include <asm/leds.h>
-#include <asm/mach-types.h>
-
-#include <asm/irq.h>
-#include <mach/map.h>
-#include <plat/regs-timer.h>
-#include <mach/regs-irq.h>
-#include <asm/mach/time.h>
-
-#include <plat/clock.h>
-#include <plat/cpu.h>
-
-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
-};
#include <linux/serial.h>
#include <linux/delay.h>
#include <linux/clk.h>
+#include <linux/cpufreq.h>
#include <asm/irq.h>
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
+ ourport->pm_level = level;
+
switch (level) {
case 3:
if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
ourport->clksrc = clksrc;
ourport->baudclk = clk;
+ ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
}
switch (termios->c_cflag & CSIZE) {
return (info->reset_port)(port, cfg);
}
+
+#ifdef CONFIG_CPU_FREQ
+
+static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct s3c24xx_uart_port *port;
+ struct uart_port *uport;
+
+ port = container_of(nb, struct s3c24xx_uart_port, freq_transition);
+ uport = &port->port;
+
+ /* check to see if port is enabled */
+
+ if (port->pm_level != 0)
+ return 0;
+
+ /* try and work out if the baudrate is changing, we can detect
+ * a change in rate, but we do not have support for detecting
+ * a disturbance in the clock-rate over the change.
+ */
+
+ if (IS_ERR(port->clk))
+ goto exit;
+
+ if (port->baudclk_rate == clk_get_rate(port->clk))
+ goto exit;
+
+ if (val == CPUFREQ_PRECHANGE) {
+ /* we should really shut the port down whilst the
+ * frequency change is in progress. */
+
+ } else if (val == CPUFREQ_POSTCHANGE) {
+ struct ktermios *termios;
+ struct tty_struct *tty;
+
+ if (uport->info == NULL) {
+ printk(KERN_WARNING "%s: info NULL\n", __func__);
+ goto exit;
+ }
+
+ tty = uport->info->port.tty;
+
+ if (tty == NULL) {
+ printk(KERN_WARNING "%s: tty is NULL\n", __func__);
+ goto exit;
+ }
+
+ termios = tty->termios;
+
+ if (termios == NULL) {
+ printk(KERN_WARNING "%s: no termios?\n", __func__);
+ goto exit;
+ }
+
+ s3c24xx_serial_set_termios(uport, termios, NULL);
+ }
+
+ exit:
+ return 0;
+}
+
+static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
+{
+ port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition;
+
+ return cpufreq_register_notifier(&port->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
+{
+ cpufreq_unregister_notifier(&port->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
+{
+ return 0;
+}
+
+static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
+{
+}
+#endif
+
/* s3c24xx_serial_init_port
*
* initialise a single serial port from the platform device given
if (ret < 0)
printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);
+ ret = s3c24xx_serial_cpufreq_register(ourport);
+ if (ret < 0)
+ dev_err(&dev->dev, "failed to add cpufreq notifier\n");
+
return 0;
probe_err:
struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
if (port) {
+ s3c24xx_serial_cpufreq_deregister(to_ourport(port));
device_remove_file(&dev->dev, &dev_attr_clock_source);
uart_remove_one_port(&s3c24xx_uart_drv, port);
}
struct s3c24xx_uart_port {
unsigned char rx_claimed;
unsigned char tx_claimed;
+ unsigned int pm_level;
+ unsigned long baudclk_rate;
struct s3c24xx_uart_info *info;
struct s3c24xx_uart_clksrc *clksrc;
struct clk *clk;
struct clk *baudclk;
struct uart_port port;
+
+#ifdef CONFIG_CPU_FREQ
+ struct notifier_block freq_transition;
+#endif
};
/* conversion functions */