162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/arm/mach-sa1100/cpu-sa1110.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001 Russell King 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Note: there are two erratas that apply to the SA1110 here: 862306a36Sopenharmony_ci * 7 - SDRAM auto-power-up failure (rev A0) 962306a36Sopenharmony_ci * 13 - Corruption of internal register reads/writes following 1062306a36Sopenharmony_ci * SDRAM reads (rev A0, B0, B1) 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * We ignore rev. A0 and B0 devices; I don't think they're worth supporting. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * The SDRAM type can be passed on the command line as cpu_sa1110.sdram=type 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci#include <linux/cpufreq.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/io.h> 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/moduleparam.h> 2262306a36Sopenharmony_ci#include <linux/types.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <asm/cputype.h> 2562306a36Sopenharmony_ci#include <asm/mach-types.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <mach/generic.h> 2862306a36Sopenharmony_ci#include <mach/hardware.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#undef DEBUG 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct sdram_params { 3362306a36Sopenharmony_ci const char name[20]; 3462306a36Sopenharmony_ci u_char rows; /* bits */ 3562306a36Sopenharmony_ci u_char cas_latency; /* cycles */ 3662306a36Sopenharmony_ci u_char tck; /* clock cycle time (ns) */ 3762306a36Sopenharmony_ci u_char trcd; /* activate to r/w (ns) */ 3862306a36Sopenharmony_ci u_char trp; /* precharge to activate (ns) */ 3962306a36Sopenharmony_ci u_char twr; /* write recovery time (ns) */ 4062306a36Sopenharmony_ci u_short refresh; /* refresh time for array (us) */ 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct sdram_info { 4462306a36Sopenharmony_ci u_int mdcnfg; 4562306a36Sopenharmony_ci u_int mdrefr; 4662306a36Sopenharmony_ci u_int mdcas[3]; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic struct sdram_params sdram_tbl[] __initdata = { 5062306a36Sopenharmony_ci { /* Toshiba TC59SM716 CL2 */ 5162306a36Sopenharmony_ci .name = "TC59SM716-CL2", 5262306a36Sopenharmony_ci .rows = 12, 5362306a36Sopenharmony_ci .tck = 10, 5462306a36Sopenharmony_ci .trcd = 20, 5562306a36Sopenharmony_ci .trp = 20, 5662306a36Sopenharmony_ci .twr = 10, 5762306a36Sopenharmony_ci .refresh = 64000, 5862306a36Sopenharmony_ci .cas_latency = 2, 5962306a36Sopenharmony_ci }, { /* Toshiba TC59SM716 CL3 */ 6062306a36Sopenharmony_ci .name = "TC59SM716-CL3", 6162306a36Sopenharmony_ci .rows = 12, 6262306a36Sopenharmony_ci .tck = 8, 6362306a36Sopenharmony_ci .trcd = 20, 6462306a36Sopenharmony_ci .trp = 20, 6562306a36Sopenharmony_ci .twr = 8, 6662306a36Sopenharmony_ci .refresh = 64000, 6762306a36Sopenharmony_ci .cas_latency = 3, 6862306a36Sopenharmony_ci }, { /* Samsung K4S641632D TC75 */ 6962306a36Sopenharmony_ci .name = "K4S641632D", 7062306a36Sopenharmony_ci .rows = 14, 7162306a36Sopenharmony_ci .tck = 9, 7262306a36Sopenharmony_ci .trcd = 27, 7362306a36Sopenharmony_ci .trp = 20, 7462306a36Sopenharmony_ci .twr = 9, 7562306a36Sopenharmony_ci .refresh = 64000, 7662306a36Sopenharmony_ci .cas_latency = 3, 7762306a36Sopenharmony_ci }, { /* Samsung K4S281632B-1H */ 7862306a36Sopenharmony_ci .name = "K4S281632B-1H", 7962306a36Sopenharmony_ci .rows = 12, 8062306a36Sopenharmony_ci .tck = 10, 8162306a36Sopenharmony_ci .trp = 20, 8262306a36Sopenharmony_ci .twr = 10, 8362306a36Sopenharmony_ci .refresh = 64000, 8462306a36Sopenharmony_ci .cas_latency = 3, 8562306a36Sopenharmony_ci }, { /* Samsung KM416S4030CT */ 8662306a36Sopenharmony_ci .name = "KM416S4030CT", 8762306a36Sopenharmony_ci .rows = 13, 8862306a36Sopenharmony_ci .tck = 8, 8962306a36Sopenharmony_ci .trcd = 24, /* 3 CLKs */ 9062306a36Sopenharmony_ci .trp = 24, /* 3 CLKs */ 9162306a36Sopenharmony_ci .twr = 16, /* Trdl: 2 CLKs */ 9262306a36Sopenharmony_ci .refresh = 64000, 9362306a36Sopenharmony_ci .cas_latency = 3, 9462306a36Sopenharmony_ci }, { /* Winbond W982516AH75L CL3 */ 9562306a36Sopenharmony_ci .name = "W982516AH75L", 9662306a36Sopenharmony_ci .rows = 16, 9762306a36Sopenharmony_ci .tck = 8, 9862306a36Sopenharmony_ci .trcd = 20, 9962306a36Sopenharmony_ci .trp = 20, 10062306a36Sopenharmony_ci .twr = 8, 10162306a36Sopenharmony_ci .refresh = 64000, 10262306a36Sopenharmony_ci .cas_latency = 3, 10362306a36Sopenharmony_ci }, { /* Micron MT48LC8M16A2TG-75 */ 10462306a36Sopenharmony_ci .name = "MT48LC8M16A2TG-75", 10562306a36Sopenharmony_ci .rows = 12, 10662306a36Sopenharmony_ci .tck = 8, 10762306a36Sopenharmony_ci .trcd = 20, 10862306a36Sopenharmony_ci .trp = 20, 10962306a36Sopenharmony_ci .twr = 8, 11062306a36Sopenharmony_ci .refresh = 64000, 11162306a36Sopenharmony_ci .cas_latency = 3, 11262306a36Sopenharmony_ci }, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic struct sdram_params sdram_params; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 11862306a36Sopenharmony_ci * Given a period in ns and frequency in khz, calculate the number of 11962306a36Sopenharmony_ci * cycles of frequency in period. Note that we round up to the next 12062306a36Sopenharmony_ci * cycle, even if we are only slightly over. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistatic inline u_int ns_to_cycles(u_int ns, u_int khz) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci return (ns * khz + 999999) / 1000000; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* 12862306a36Sopenharmony_ci * Create the MDCAS register bit pattern. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic inline void set_mdcas(u_int *mdcas, int delayed, u_int rcd) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci u_int shift; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci rcd = 2 * rcd - 1; 13562306a36Sopenharmony_ci shift = delayed + 1 + rcd; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci mdcas[0] = (1 << rcd) - 1; 13862306a36Sopenharmony_ci mdcas[0] |= 0x55555555 << shift; 13962306a36Sopenharmony_ci mdcas[1] = mdcas[2] = 0x55555555 << (shift & 1); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void 14362306a36Sopenharmony_cisdram_calculate_timing(struct sdram_info *sd, u_int cpu_khz, 14462306a36Sopenharmony_ci struct sdram_params *sdram) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci u_int mem_khz, sd_khz, trp, twr; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci mem_khz = cpu_khz / 2; 14962306a36Sopenharmony_ci sd_khz = mem_khz; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* 15262306a36Sopenharmony_ci * If SDCLK would invalidate the SDRAM timings, 15362306a36Sopenharmony_ci * run SDCLK at half speed. 15462306a36Sopenharmony_ci * 15562306a36Sopenharmony_ci * CPU steppings prior to B2 must either run the memory at 15662306a36Sopenharmony_ci * half speed or use delayed read latching (errata 13). 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci if ((ns_to_cycles(sdram->tck, sd_khz) > 1) || 15962306a36Sopenharmony_ci (read_cpuid_revision() < ARM_CPU_REV_SA1110_B2 && sd_khz < 62000)) 16062306a36Sopenharmony_ci sd_khz /= 2; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci sd->mdcnfg = MDCNFG & 0x007f007f; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci twr = ns_to_cycles(sdram->twr, mem_khz); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* trp should always be >1 */ 16762306a36Sopenharmony_ci trp = ns_to_cycles(sdram->trp, mem_khz) - 1; 16862306a36Sopenharmony_ci if (trp < 1) 16962306a36Sopenharmony_ci trp = 1; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci sd->mdcnfg |= trp << 8; 17262306a36Sopenharmony_ci sd->mdcnfg |= trp << 24; 17362306a36Sopenharmony_ci sd->mdcnfg |= sdram->cas_latency << 12; 17462306a36Sopenharmony_ci sd->mdcnfg |= sdram->cas_latency << 28; 17562306a36Sopenharmony_ci sd->mdcnfg |= twr << 14; 17662306a36Sopenharmony_ci sd->mdcnfg |= twr << 30; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci sd->mdrefr = MDREFR & 0xffbffff0; 17962306a36Sopenharmony_ci sd->mdrefr |= 7; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (sd_khz != mem_khz) 18262306a36Sopenharmony_ci sd->mdrefr |= MDREFR_K1DB2; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* initial number of '1's in MDCAS + 1 */ 18562306a36Sopenharmony_ci set_mdcas(sd->mdcas, sd_khz >= 62000, 18662306a36Sopenharmony_ci ns_to_cycles(sdram->trcd, mem_khz)); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci#ifdef DEBUG 18962306a36Sopenharmony_ci printk(KERN_DEBUG "MDCNFG: %08x MDREFR: %08x MDCAS0: %08x MDCAS1: %08x MDCAS2: %08x\n", 19062306a36Sopenharmony_ci sd->mdcnfg, sd->mdrefr, sd->mdcas[0], sd->mdcas[1], 19162306a36Sopenharmony_ci sd->mdcas[2]); 19262306a36Sopenharmony_ci#endif 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* 19662306a36Sopenharmony_ci * Set the SDRAM refresh rate. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_cistatic inline void sdram_set_refresh(u_int dri) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci MDREFR = (MDREFR & 0xffff000f) | (dri << 4); 20162306a36Sopenharmony_ci (void) MDREFR; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* 20562306a36Sopenharmony_ci * Update the refresh period. We do this such that we always refresh 20662306a36Sopenharmony_ci * the SDRAMs within their permissible period. The refresh period is 20762306a36Sopenharmony_ci * always a multiple of the memory clock (fixed at cpu_clock / 2). 20862306a36Sopenharmony_ci * 20962306a36Sopenharmony_ci * FIXME: we don't currently take account of burst accesses here, 21062306a36Sopenharmony_ci * but neither do Intels DM nor Angel. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_cistatic void 21362306a36Sopenharmony_cisdram_update_refresh(u_int cpu_khz, struct sdram_params *sdram) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci u_int ns_row = (sdram->refresh * 1000) >> sdram->rows; 21662306a36Sopenharmony_ci u_int dri = ns_to_cycles(ns_row, cpu_khz / 2) / 32; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci#ifdef DEBUG 21962306a36Sopenharmony_ci mdelay(250); 22062306a36Sopenharmony_ci printk(KERN_DEBUG "new dri value = %d\n", dri); 22162306a36Sopenharmony_ci#endif 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci sdram_set_refresh(dri); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* 22762306a36Sopenharmony_ci * Ok, set the CPU frequency. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_cistatic int sa1110_target(struct cpufreq_policy *policy, unsigned int ppcr) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct sdram_params *sdram = &sdram_params; 23262306a36Sopenharmony_ci struct sdram_info sd; 23362306a36Sopenharmony_ci unsigned long flags; 23462306a36Sopenharmony_ci unsigned int unused; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci sdram_calculate_timing(&sd, sa11x0_freq_table[ppcr].frequency, sdram); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci#if 0 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * These values are wrong according to the SA1110 documentation 24162306a36Sopenharmony_ci * and errata, but they seem to work. Need to get a storage 24262306a36Sopenharmony_ci * scope on to the SDRAM signals to work out why. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci if (policy->max < 147500) { 24562306a36Sopenharmony_ci sd.mdrefr |= MDREFR_K1DB2; 24662306a36Sopenharmony_ci sd.mdcas[0] = 0xaaaaaa7f; 24762306a36Sopenharmony_ci } else { 24862306a36Sopenharmony_ci sd.mdrefr &= ~MDREFR_K1DB2; 24962306a36Sopenharmony_ci sd.mdcas[0] = 0xaaaaaa9f; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci sd.mdcas[1] = 0xaaaaaaaa; 25262306a36Sopenharmony_ci sd.mdcas[2] = 0xaaaaaaaa; 25362306a36Sopenharmony_ci#endif 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* 25662306a36Sopenharmony_ci * The clock could be going away for some time. Set the SDRAMs 25762306a36Sopenharmony_ci * to refresh rapidly (every 64 memory clock cycles). To get 25862306a36Sopenharmony_ci * through the whole array, we need to wait 262144 mclk cycles. 25962306a36Sopenharmony_ci * We wait 20ms to be safe. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci sdram_set_refresh(2); 26262306a36Sopenharmony_ci if (!irqs_disabled()) 26362306a36Sopenharmony_ci msleep(20); 26462306a36Sopenharmony_ci else 26562306a36Sopenharmony_ci mdelay(20); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* 26862306a36Sopenharmony_ci * Reprogram the DRAM timings with interrupts disabled, and 26962306a36Sopenharmony_ci * ensure that we are doing this within a complete cache line. 27062306a36Sopenharmony_ci * This means that we won't access SDRAM for the duration of 27162306a36Sopenharmony_ci * the programming. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci local_irq_save(flags); 27462306a36Sopenharmony_ci asm("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)); 27562306a36Sopenharmony_ci udelay(10); 27662306a36Sopenharmony_ci __asm__ __volatile__("\n\ 27762306a36Sopenharmony_ci b 2f \n\ 27862306a36Sopenharmony_ci .align 5 \n\ 27962306a36Sopenharmony_ci1: str %3, [%1, #0] @ MDCNFG \n\ 28062306a36Sopenharmony_ci str %4, [%1, #28] @ MDREFR \n\ 28162306a36Sopenharmony_ci str %5, [%1, #4] @ MDCAS0 \n\ 28262306a36Sopenharmony_ci str %6, [%1, #8] @ MDCAS1 \n\ 28362306a36Sopenharmony_ci str %7, [%1, #12] @ MDCAS2 \n\ 28462306a36Sopenharmony_ci str %8, [%2, #0] @ PPCR \n\ 28562306a36Sopenharmony_ci ldr %0, [%1, #0] \n\ 28662306a36Sopenharmony_ci b 3f \n\ 28762306a36Sopenharmony_ci2: b 1b \n\ 28862306a36Sopenharmony_ci3: nop \n\ 28962306a36Sopenharmony_ci nop" 29062306a36Sopenharmony_ci : "=&r" (unused) 29162306a36Sopenharmony_ci : "r" (&MDCNFG), "r" (&PPCR), "0" (sd.mdcnfg), 29262306a36Sopenharmony_ci "r" (sd.mdrefr), "r" (sd.mdcas[0]), 29362306a36Sopenharmony_ci "r" (sd.mdcas[1]), "r" (sd.mdcas[2]), "r" (ppcr)); 29462306a36Sopenharmony_ci local_irq_restore(flags); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* 29762306a36Sopenharmony_ci * Now, return the SDRAM refresh back to normal. 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci sdram_update_refresh(sa11x0_freq_table[ppcr].frequency, sdram); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int __init sa1110_cpu_init(struct cpufreq_policy *policy) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci cpufreq_generic_init(policy, sa11x0_freq_table, 0); 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* sa1110_driver needs __refdata because it must remain after init registers 31162306a36Sopenharmony_ci * it with cpufreq_register_driver() */ 31262306a36Sopenharmony_cistatic struct cpufreq_driver sa1110_driver __refdata = { 31362306a36Sopenharmony_ci .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK | 31462306a36Sopenharmony_ci CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, 31562306a36Sopenharmony_ci .verify = cpufreq_generic_frequency_table_verify, 31662306a36Sopenharmony_ci .target_index = sa1110_target, 31762306a36Sopenharmony_ci .get = sa11x0_getspeed, 31862306a36Sopenharmony_ci .init = sa1110_cpu_init, 31962306a36Sopenharmony_ci .name = "sa1110", 32062306a36Sopenharmony_ci}; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic struct sdram_params *sa1110_find_sdram(const char *name) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct sdram_params *sdram; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci for (sdram = sdram_tbl; sdram < sdram_tbl + ARRAY_SIZE(sdram_tbl); 32762306a36Sopenharmony_ci sdram++) 32862306a36Sopenharmony_ci if (strcmp(name, sdram->name) == 0) 32962306a36Sopenharmony_ci return sdram; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return NULL; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic char sdram_name[16]; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int __init sa1110_clk_init(void) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct sdram_params *sdram; 33962306a36Sopenharmony_ci const char *name = sdram_name; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (!cpu_is_sa1110()) 34262306a36Sopenharmony_ci return -ENODEV; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (!name[0]) { 34562306a36Sopenharmony_ci if (machine_is_assabet()) 34662306a36Sopenharmony_ci name = "TC59SM716-CL3"; 34762306a36Sopenharmony_ci if (machine_is_jornada720() || machine_is_h3600()) 34862306a36Sopenharmony_ci name = "K4S281632B-1H"; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci sdram = sa1110_find_sdram(name); 35262306a36Sopenharmony_ci if (sdram) { 35362306a36Sopenharmony_ci printk(KERN_DEBUG "SDRAM: tck: %d trcd: %d trp: %d" 35462306a36Sopenharmony_ci " twr: %d refresh: %d cas_latency: %d\n", 35562306a36Sopenharmony_ci sdram->tck, sdram->trcd, sdram->trp, 35662306a36Sopenharmony_ci sdram->twr, sdram->refresh, sdram->cas_latency); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci memcpy(&sdram_params, sdram, sizeof(sdram_params)); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return cpufreq_register_driver(&sa1110_driver); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cimodule_param_string(sdram, sdram_name, sizeof(sdram_name), 0); 36762306a36Sopenharmony_ciarch_initcall(sa1110_clk_init); 368