summaryrefslogtreecommitdiff
path: root/drivers/platform/sprd
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/platform/sprd
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/platform/sprd')
-rw-r--r--drivers/platform/sprd/Kconfig266
-rw-r--r--drivers/platform/sprd/Makefile63
-rw-r--r--drivers/platform/sprd/adc.c1046
-rw-r--r--drivers/platform/sprd/adi.c380
-rw-r--r--drivers/platform/sprd/adie_irq.c229
-rw-r--r--drivers/platform/sprd/arch_init.c42
-rw-r--r--drivers/platform/sprd/arch_lock.c101
-rw-r--r--drivers/platform/sprd/arch_misc.c525
-rwxr-xr-xdrivers/platform/sprd/aux_debug.c148
-rwxr-xr-xdrivers/platform/sprd/avs_debug.c139
-rw-r--r--drivers/platform/sprd/boot_mode.c46
-rw-r--r--drivers/platform/sprd/busmonitor.c2111
-rw-r--r--drivers/platform/sprd/clock-sc8830_dt.c29
-rw-r--r--drivers/platform/sprd/clock.h148
-rw-r--r--drivers/platform/sprd/clock_dt.h131
-rw-r--r--drivers/platform/sprd/common.c72
-rw-r--r--drivers/platform/sprd/cpufreq-scx35.c1272
-rw-r--r--drivers/platform/sprd/cpuidle-scx35.c445
-rw-r--r--drivers/platform/sprd/dcdc_cal.c417
-rw-r--r--drivers/platform/sprd/dcdc_debug.c126
-rw-r--r--drivers/platform/sprd/devices.h169
-rw-r--r--drivers/platform/sprd/dhd_adapter.c497
-rw-r--r--drivers/platform/sprd/dma_r4p0.c890
-rw-r--r--drivers/platform/sprd/dmc_dfs_test.c112
-rwxr-xr-xdrivers/platform/sprd/dmc_freq.c816
-rw-r--r--drivers/platform/sprd/dmc_freq_28nm.c657
-rw-r--r--drivers/platform/sprd/dmc_freq_ap.c838
-rwxr-xr-xdrivers/platform/sprd/dmc_freq_ap_28nm.c551
-rw-r--r--drivers/platform/sprd/fix_v7_tag_ram_bug.S183
-rw-r--r--drivers/platform/sprd/glb.c85
-rw-r--r--drivers/platform/sprd/headsmp.S84
-rw-r--r--drivers/platform/sprd/hotplug.c167
-rw-r--r--drivers/platform/sprd/io-sc8830.c67
-rw-r--r--drivers/platform/sprd/iomap.c111
-rw-r--r--drivers/platform/sprd/irq.c319
-rw-r--r--drivers/platform/sprd/mailbox.c205
-rwxr-xr-xdrivers/platform/sprd/pin_switch.c559
-rw-r--r--drivers/platform/sprd/platsmp.c382
-rw-r--r--drivers/platform/sprd/pm-scx35.c1829
-rw-r--r--drivers/platform/sprd/pm.c171
-rw-r--r--drivers/platform/sprd/pm_debug.c723
-rw-r--r--drivers/platform/sprd/pm_debug_scx35.c982
-rwxr-xr-xdrivers/platform/sprd/reserve.c182
-rwxr-xr-xdrivers/platform/sprd/sec_gps_s5n6420.c140
-rw-r--r--drivers/platform/sprd/sprd_debug.c249
-rw-r--r--drivers/platform/sprd/sprd_mem_pool.c470
-rw-r--r--drivers/platform/sprd/sprd_vibrator_2723.c141
-rw-r--r--drivers/platform/sprd/standby-scx35.S848
-rwxr-xr-xdrivers/platform/sprd/sys_reset.c75
-rw-r--r--drivers/platform/sprd/sysdump64.c741
-rw-r--r--drivers/platform/sprd/sysdump64.h21
-rwxr-xr-xdrivers/platform/sprd/timer_sc8830.c515
-rwxr-xr-xdrivers/platform/sprd/timer_sharkl64.c281
-rw-r--r--drivers/platform/sprd/vibrator.c250
54 files changed, 22046 insertions, 0 deletions
diff --git a/drivers/platform/sprd/Kconfig b/drivers/platform/sprd/Kconfig
new file mode 100644
index 00000000..67f52cd9
--- /dev/null
+++ b/drivers/platform/sprd/Kconfig
@@ -0,0 +1,266 @@
+#
+# SPREADTRUM Platform Specific Drivers
+#
+menu "Spreadtrum Platform Specific Drivers"
+
+menu "Spreadtrum Adie Type"
+config ADIE_SC2713
+ bool "Spredtrum A-die SC2713 Chip Support"
+ default n
+ help
+ Say Y here to support for Spreadtrum a-die sc2713 chip.
+
+config ADIE_SC2713S
+ bool "Spredtrum A-die SC2713S Chip Support"
+ default n
+ help
+ Say Y here to support for Spreadtrum a-die sc2713s chip.
+
+config ADIE_SC2711
+ bool "Spredtrum A-die SC2711 Chip Support"
+ default n
+ help
+ Say Y here to support for Spreadtrum a-die sc2711 chip.
+
+config ADIE_SC2723
+ bool "Spredtrum A-die SC2723 Chip Support"
+ default n
+ help
+ Say Y here to support for Spreadtrum a-die sc2723 chip.
+
+config ADIE_SC2723S
+ bool "Spredtrum A-die SC2723S Chip Support"
+ default n
+ help
+ Say Y here to support for Spreadtrum a-die sc2723S chip.
+endmenu
+
+config BUS_MONITOR_DEBUG
+ bool "Spreadtrum BM Support"
+ default y
+ help
+ Say Y here to support debug busmonitor
+
+config SC_FPGA
+ bool "Spreadtrum FPGA support"
+ default n
+ help
+ This is used for FPGA verification
+
+config SCX35L64BIT_FPGA
+ bool "Spreadtrum FPGA platform"
+ depends on ARCH_SCX35L64
+ select SC_FPGA
+ default n
+ help
+ SCX35L64BIT_FPGA fpga platform
+
+config SC_VIBRATOR
+ bool "vibrator for SC serials"
+ select ANDROID_TIMED_OUTPUT
+ default n
+
+config SPRD_VIBRATOR_2723
+ bool "vibrator for sprd_vibrator_2723"
+ select ANDROID_TIMED_OUTPUT
+ default n
+
+config SC_VIBRATOR_GPIO
+ bool "vibrator for SC serials by gpio"
+ default n
+ depends on SC_VIBRATOR
+
+config SC_VIBRATOR_POWER
+ bool "vibrator for SC serials by power"
+ default n
+ depends on SC_VIBRATOR
+
+config SPRD_ION_MM_SIZE
+ int "size for ion"
+ range 1 64
+ default 1
+ depends on ION
+
+config SPRD_ION_OVERLAY_SIZE
+ int "size for ion overlay"
+ range 1 64
+ default 1
+ depends on ION
+
+config SPRD_VIBRATOR_2723
+ bool "vibrator for sprd_vibrator_2723"
+ select ANDROID_TIMED_OUTPUT
+ default n
+
+config EIRQ_NUM
+ int "number for external irq region desc request"
+ range 0 1024
+ default 0
+
+config PIN_POWER_DOMAIN_SWITCH
+ bool "pin power domain switch for 28nm chip"
+ default n
+
+comment "SC8830 ADIE type"
+
+config SC_INTERNAL_ADI
+ bool "spreadtrum sc internal adi"
+ default y
+ depends on ARCH_SCX35
+
+config SC_INTERNAL_ADC
+ bool "spreadtrum sc internal adc"
+ default y
+ depends on ARCH_SCX35
+
+config SC_INTERNAL_WATCHDOG
+ bool "spreadtrum sc internal watchdog"
+ default y
+ depends on ARCH_SCX35
+
+config FIX_V7TAGRAM_BUG
+ bool "fix arm-v7 scu tag ram bug"
+ default n
+ depends on ARCH_SCX35
+ depends on SMP
+
+config DDR_VALIDITY_TEST
+ bool "test ddr validity before go into ddr"
+ default n
+
+config SPRD_CPU_DYNAMIC_HOTPLUG
+ bool "cpu dynamic hotplug with DVFS"
+ default n
+ depends on HOTPLUG_CPU
+ depends on ARCH_SCX35
+
+config SPRD_MEM_POOL
+ default y
+ bool "Using a memory pool to alloc 8k to 64k"
+ depends on !COMPACTION
+ help
+ This option enables memmory pool for allocating 8k to 64k.
+
+config SPRD_DCDC_DEBUG
+ tristate "Enable dcdc debug module"
+ default n
+ help
+ This option select dcdc debug module.
+
+config SPRD_AVS_DEBUG
+ tristate "Enable avs debug module"
+ default n
+ help
+ This option select avs debug module.
+
+config SPRD_AUX_DEBUG
+ tristate "Enable aux debug moduel"
+ default n
+ help
+ This option select aux debug module.
+
+config SPRD_SIMCTRL
+ bool "Enable simctrl module"
+ default n
+ help
+ This option select simctrl module.
+config SPRD_MODEM_TD
+ bool "SPRD TD modem"
+ default n
+ help
+ This option select modem type.
+
+config MODEM_W_MEMCUT
+ bool "SPRD w modem memory cut"
+ default n
+ help
+ This option select for w modem memory cut.
+
+config SIPC_TD
+ bool "SIPC instance of TD modem"
+ select SIPC
+ default n
+
+config SIPC_WCDMA
+ bool "SIPC instance of WCDMA modem"
+ select SIPC
+ default n
+
+config SIPC_WCN
+ bool "SIPC instance of Wireless Connection"
+ select SIPC
+ default n
+
+config SIPC_PMIC
+ bool "SIPC instance of PMIC arm7 modem"
+ select SIPC
+ default n
+
+
+config SIPC_GGE
+ bool "SIPC instance of GGE modem"
+ select SIPC
+ default n
+
+config SIPC_LTE
+ bool "SIPC instance of LTE modem"
+ select SIPC
+ default n
+
+config VOIP_CPT
+ bool "VOIP process in modem T,it is processed in modem W by default"
+ default n
+ depends on SIPC_TD
+
+config VOIP_LTE
+ bool "VOIP process in modem LTE,it is processed in modem W by default"
+ default n
+ depends on SIPC_LTE
+
+config CP_SWITCH
+ bool "cp switch"
+ default n
+ help
+ This option select cp switch
+
+config EAR_LOW_LEVEL_DETECT
+ bool "ear detecting trigger is low level"
+ default n
+config SS_FUNCTION
+ bool "only for samsung function"
+ default n
+config TSP_0_1_A
+ bool "For corsica_ve 3131 0.1A board"
+ default n
+
+config SPRD_MAILBOX
+ bool "Spreadtrum Mailbox IPI Arch"
+ default n
+
+config MUIC_CABLE_DETECT
+ bool "usb, jig-box and so on cables are detected by external MUIC chip"
+ default n
+ depends on MFD_SM5504 || MFD_RT8973
+
+config SPRD_SYSDUMP
+ bool "Enable SPRD sysdump handler"
+ depends on ARM64
+ help
+ This option enables Spreadtrum sysdump which prepare debug info
+ for u-boot to write into mmc storage after kernel panics.
+
+config SPRD_DEBUG
+ bool "Enable SPRD DEBUG"
+
+config SPRD_DEBUG_SCHED_LOG
+ bool "SPRD Scheduler Logging Feature"
+ depends on SPRD_DEBUG
+ default n
+ help
+ SPRD Scheduler Logging Feature for Debug use.
+
+config GPS_LSI_S5N6420
+bool "LSI_GPS_CHIPSET_HARRIER"
+default n
+
+endmenu
diff --git a/drivers/platform/sprd/Makefile b/drivers/platform/sprd/Makefile
new file mode 100644
index 00000000..a9ec1b88
--- /dev/null
+++ b/drivers/platform/sprd/Makefile
@@ -0,0 +1,63 @@
+obj-$(CONFIG_64BIT) += iomap.o
+obj-$(CONFIG_ARCH_SCX35) += arch_init.o arch_misc.o adi.o adc.o glb.o boot_mode.o pin_switch.o
+obj-$(CONFIG_SPRD_MAILBOX) += mailbox.o
+obj-$(CONFIG_SC_INTERNAL_WATCHDOG) += sys_reset.o
+obj-$(CONFIG_BUS_MONITOR_DEBUG) += busmonitor.o
+obj-$(CONFIG_SC_VIBRATOR) += vibrator.o
+obj-$(CONFIG_GPS_LSI_S5N6420) += sec_gps_s5n6420.o
+ifneq ($(CONFIG_64BIT),y)
+obj-y += timer_sc8830.o
+else
+obj-y += timer_sharkl64.o
+endif
+ifneq ($(CONFIG_DMA_SPRD),y)
+ obj-$(CONFIG_ARCH_SCX35) +=dma_r4p0.o
+endif
+obj-$(CONFIG_SC_INTERNAL_ADI) += adie_irq.o
+obj-$(CONFIG_SPRD_DEBUG) += sprd_debug.o
+obj-$(CONFIG_PM) += pm.o
+ifeq ($(CONFIG_PM),y)
+ obj-$(CONFIG_ARCH_SCX35) += pm-scx35.o standby-scx35.o pm_debug_scx35.o
+endif
+obj-$(CONFIG_CPU_FREQ) += cpufreq-scx35.o
+obj-$(CONFIG_SPRD_VIBRATOR_2723) += sprd_vibrator_2723.o
+ifeq ($(CONFIG_64BIT),y)
+obj-$(CONFIG_SPRD_SYSDUMP) += sysdump64.o
+endif
+
+
+ifneq ($(CONFIG_64BIT),y)
+
+ifeq ($(CONFIG_CPU_IDLE),y)
+ obj-$(CONFIG_ARCH_SCX35) += cpuidle-scx35.o
+endif
+obj-y += irq.o reserve.o common.o
+obj-$(CONFIG_ARCH_SCX35) += clock-sc8830_dt.o
+obj-$(CONFIG_ARCH_SCX35) += io-sc8830.o pin_switch.o
+
+ifeq ($(CONFIG_ARCH_SCX30G),y)
+obj-$(CONFIG_PM_DEVFREQ) += dmc_freq_28nm.o dmc_dfs_test.o
+obj-$(CONFIG_SCX35_DMC_FREQ_AP) += dmc_freq_ap_28nm.o
+ CFLAGS_dmc_freq_ap_28nm.o += -fPIC
+else
+ifeq ($(CONFIG_ARCH_SCX35L),y)
+obj-y += dmc_freq_28nm.o
+else
+obj-$(CONFIG_PM_DEVFREQ) += dmc_freq.o
+obj-$(CONFIG_SCX35_DMC_FREQ_AP) += dmc_freq_ap.o
+endif
+endif
+
+obj-$(CONFIG_FIX_V7TAGRAM_BUG) += fix_v7_tag_ram_bug.o
+obj-$(CONFIG_SPRD_DCDC_DEBUG) += dcdc_debug.o
+obj-$(CONFIG_SMP) += platsmp.o headsmp.o
+obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+ifeq ($(CONFIG_BCMDHD),m)
+obj-y += dhd_adapter.o
+endif
+
+obj-$(CONFIG_SPRD_MEM_POOL) += sprd_mem_pool.o
+obj-$(CONFIG_SPRD_AVS_DEBUG) += avs_debug.o
+obj-$(CONFIG_SPRD_AUX_DEBUG) += aux_debug.o
+
+endif
diff --git a/drivers/platform/sprd/adc.c b/drivers/platform/sprd/adc.c
new file mode 100644
index 00000000..f10bb8f7
--- /dev/null
+++ b/drivers/platform/sprd/adc.c
@@ -0,0 +1,1046 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ * Author: steve.zhan <steve.zhan@spreadtrum.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/irqflags.h>
+#include <linux/delay.h>
+#include <linux/hwspinlock.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/miscdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/arch_lock.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/adc.h>
+
+static void __iomem *io_base; /* Mapped base address */
+
+#define adc_write(val, reg) \
+do { \
+ sci_adi_raw_write((unsigned long)reg, val); \
+} while (0)
+
+static unsigned adc_read(unsigned long addr)
+{
+ return (unsigned)sci_adi_read(addr);
+}
+
+/*FIXME: If we have not hwspinlock , we need use spinlock to do it */
+#define sci_adc_lock() \
+do { \
+ WARN_ON(IS_ERR_VALUE(hwspin_lock_timeout_irqsave(arch_get_hwlock(HWLOCK_ADC), -1, &flags))); \
+} while(0)
+
+#define sci_adc_unlock() \
+do { \
+ hwspin_unlock_irqrestore(arch_get_hwlock(HWLOCK_ADC), &flags); \
+} while(0)
+
+
+#define ADC_CTL (0x00)
+#define ADC_SW_CH_CFG (0x04)
+#define ADC_FAST_HW_CHX_CFG(_X_) ((_X_) * 0x4 + 0x8)
+#define ADC_SLOW_HW_CHX_CFG(_X_) ((_X_) * 0x4 + 0x28)
+#define ADC_HW_CH_DELAY (0x48)
+
+#define ADC_DAT (0x4c)
+#define adc_get_data(_SAMPLE_BITS_) (adc_read((unsigned long)(io_base + ADC_DAT)) & (_SAMPLE_BITS_))
+
+#define ADC_IRQ_EN (0x50)
+#define adc_enable_irq(_X_) \
+do { \
+ adc_write(((_X_) & 0x1), (unsigned long)(io_base + ADC_IRQ_EN)); \
+} while(0)
+
+#define ADC_IRQ_CLR (0x54)
+#define adc_clear_irq() \
+do { \
+ adc_write(0x1, (unsigned long)(io_base + ADC_IRQ_CLR)); \
+} while (0)
+
+#define ADC_IRQ_STS (0x58)
+#define adc_mask_irqstatus() adc_read((unsigned long)(io_base + ADC_IRQ_STS))
+
+#define ADC_IRQ_RAW (0x5c)
+#define adc_raw_irqstatus() adc_read((unsigned long)(io_base + ADC_IRQ_RAW))
+
+#define ADC_DEBUG (0x60)
+
+/*ADC_CTL */
+#define ADC_MAX_SAMPLE_NUM (0x10)
+#define BIT_SW_CH_RUN_NUM(_X_) ((((_X_) - 1) & 0xf ) << 4)
+#define BIT_ADC_BIT_MODE(_X_) (((_X_) & 0x1) << 2) /*0: adc in 10bits mode, 1: adc in 12bits mode */
+#define BIT_ADC_BIT_MODE_MASK (BIT_ADC_BIT_MODE(1))
+#define BIT_SW_CH_ON ( BIT(1) ) /*WO*/
+#define BIT_ADC_EN ( BIT(0) )
+
+/*ADC_SW_CH_CFG && ADC_FAST(SLOW)_HW_CHX_CFG*/
+#define BIT_CH_IN_MODE(_X_) (((_X_) & 0x1) << 8) /*0: resistance path, 1: capacitance path */
+#define BIT_CH_SLOW(_X_) (((_X_) & 0x1) << 6) /*0: quick mode, 1: slow mode */
+#define BIT_CH_SCALE(_X_) (((_X_) & 0x1) << 5) /*0: little scale, 1: big scale */
+#define BIT_CH_ID(_X_) ((_X_) & 0x1f)
+
+/*ADC_FAST(SLOW)_HW_CHX_CFG*/
+#define BIT_CH_DLY_EN(_X_) (((_X_) & 0x1) << 7) /*0:disable, 1:enable */
+
+/*ADC_HW_CH_DELAY*/
+#define BIT_HW_CH_DELAY(_X_) ((_X_) & 0xff) /*its unit is ADC clock */
+
+#if defined(CONFIG_ARCH_SC8825)
+#define BIT_ADC_EB ( BIT(5) )
+#endif
+
+#define SPRDBAT_AUXADC_CAL_NO 0
+#define SPRDBAT_AUXADC_CAL_NV 1
+#define SPRDBAT_AUXADC_CAL_CHIP 2
+int cal_type = SPRDBAT_AUXADC_CAL_NO;
+static short adc_cali_data[2][2] = {
+ { 4200 , 3310 },
+ { 3600 , 2832 },
+};
+
+struct sprd_adc_data{
+ struct miscdevice misc_dev;
+ struct mutex lock;
+ unsigned short channel;
+ unsigned char scale;
+ unsigned int voltage_ratio;
+};
+
+#ifdef CONFIG_OTP_SPRD
+extern int sci_efuse_calibration_get(unsigned int *p_cal_data);
+#endif
+
+static int __init adc_cali_fuse_proc(void);
+
+static ssize_t sprd_adc_store(struct device *dev,
+ struct device_attribute *dev_attr,
+ const char *buf, size_t count);
+static ssize_t sprd_adc_show(struct device *dev,
+ struct device_attribute *dev_attr,
+ char *buf);
+static ssize_t sprd_cal_type_show(struct device *dev,
+ struct device_attribute *dev_attr,
+ char *buf);
+
+static struct device_attribute sprd_adc_attr[] = {
+ __ATTR(adc_channel, S_IRUGO | S_IWUSR | S_IWGRP, sprd_adc_show, sprd_adc_store),
+ __ATTR(adc_scale, S_IRUGO | S_IWUSR | S_IWGRP, sprd_adc_show, sprd_adc_store),
+ __ATTR(adc_voltage_ratio, S_IRUGO, sprd_adc_show, NULL),
+ __ATTR(adc_data_raw, S_IRUGO, sprd_adc_show, NULL),
+ __ATTR(adc_cal_type, S_IRUGO, sprd_cal_type_show, NULL),
+};
+
+#define SPRD_MODULE_NAME "sprd-adc"
+
+#define DIV_ROUND(n, d) (((n) + ((d)/2)) / (d))
+#define MEASURE_TIMES ( 15 )
+#define ADC_DROP_CNT ( DIV_ROUND(MEASURE_TIMES, 5) )
+
+static int average_int(int a[], int len)
+{
+ int i, sum = 0;
+ for (i = 0; i < len; i++)
+ sum += a[i];
+ return DIV_ROUND(sum, len);
+}
+
+static int compare_val(const void *a, const void *b)
+{
+ return *(int *)a - *(int *)b;
+}
+
+static void dump_adc_data(uint32_t* p_adc_val, int len)
+{
+ int i = 0;
+
+ printk("dump adc data: ");
+ for (i = 0; i < len; i++) {
+ printk("%d ", p_adc_val[i]);
+ }
+ printk("\n");
+}
+
+/*
+* Get the raw data of adc channel
+* @ channel: adc channel id
+* @ scale: adc scale
+*
+* return 0 if failed
+*
+*/
+int sci_get_adc_data(unsigned int channel, unsigned int scale)
+{
+ uint32_t adc_val[MEASURE_TIMES] = {0}, adc_res = 0;
+ struct adc_sample_data adc_sample = {
+ .channel_id = channel,
+ .channel_type = 0, /* sw */
+ .hw_channel_delay = 0,
+ .scale = scale, /* 1: big scale, 0: small scale */
+ .pbuf = &adc_val[0],
+ .sample_num = MEASURE_TIMES,
+ .sample_bits = 1, /* 12 bit*/
+ .sample_speed = 0, /* quick mode */
+ .signal_mode = 0, /* resistance path */
+ };
+
+ if (0 != sci_adc_get_values(&adc_sample)) {
+ pr_err("sprd_adc error occured in function %s\n", __func__);
+ sci_adc_dump_register();
+ //BUG_ON();
+ return 0;
+ }
+
+ dump_adc_data(adc_val, ARRAY_SIZE(adc_val));
+
+ sort(adc_val, ARRAY_SIZE(adc_val), sizeof(uint32_t), compare_val, 0);
+
+ adc_res = average_int(&adc_val[ADC_DROP_CNT], MEASURE_TIMES - ADC_DROP_CNT * 2);
+
+ return (int)adc_res;
+}
+
+EXPORT_SYMBOL(sci_get_adc_data);
+
+/*
+* The value of adc device node is set to kernel space
+*
+* Example:
+* $ adb root && adb shell
+* $ echo 5 > /sys/class/misc/adc_channel # set current adc channel to 5 (vbat channel)
+* $ echo 1 > /sys/class/misc/adc_scale #set adc to big scale
+*/
+static ssize_t sprd_adc_store(struct device *dev,
+ struct device_attribute *dev_attr,
+ const char *buf, size_t count)
+{
+ struct sprd_adc_data *adc_data = dev_get_drvdata(dev);
+ //const ptrdiff_t offset = dev_attr - sprd_adc_attr;
+ //unsigned long tmp = simple_strtoul(buf, NULL, 10);
+ unsigned int len = 0, tmp_data = 0;
+
+ if ((NULL == adc_data) || (NULL == buf))
+ return -EIO;
+
+ len = sscanf(buf, "%d", &tmp_data);
+
+ if (0 == len)
+ return -EIO;
+
+ pr_info("%s line: %d, attr name(%s), tmp_data %d, count %d, len %d\n", __func__, __LINE__,
+ dev_attr->attr.name, tmp_data, (int)count, len);
+
+ mutex_lock(&adc_data->lock);
+
+ if (0 == strcmp(dev_attr->attr.name, "adc_channel")) {
+ WARN_ON(tmp_data > BIT_CH_ID(-1));
+
+ adc_data->channel = (unsigned short)tmp_data;
+ } else if (0 == strcmp(dev_attr->attr.name, "adc_scale")) {
+ adc_data->scale = (unsigned char)tmp_data;
+ }
+
+ mutex_unlock(&adc_data->lock);
+
+ return count;
+}
+
+/*
+* The value of adc device node is showed to user space
+*
+* Example:
+* $ adb root && adb shell
+* $ cat /sys/class/misc/adc_channel # show current adc channel number
+* $ cat /sys/class/misc/adc_scale #show adc scale value
+* $ cat /sys/class/misc/adc_data_raw #show adc raw data
+*/
+static ssize_t sprd_adc_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ struct sprd_adc_data *adc_data = dev_get_drvdata(dev);
+ int len = 0, adc_value = 0;
+
+ if ((NULL == adc_data) || (NULL == buf))
+ return -EIO;
+
+ pr_info("%s line: %d, attr name(%s), channel(%d), scale(%d)\n", __func__, __LINE__,
+ dev_attr->attr.name, adc_data->channel, adc_data->scale);
+
+ mutex_lock(&adc_data->lock);
+
+ if (0 == strcmp(dev_attr->attr.name, "adc_channel")) {
+ len += sprintf(buf, "%d\n", adc_data->channel);
+ } else if (0 == strcmp(dev_attr->attr.name, "adc_scale")) {
+ len += sprintf(buf, "%d\n", adc_data->scale);
+ } else if (0 == strcmp(dev_attr->attr.name, "adc_voltage_ratio")) {
+ unsigned int div_num = 0, div_den = 0;
+
+ sci_adc_get_vol_ratio(adc_data->channel, adc_data->scale, &div_num, &div_den);
+ adc_data->voltage_ratio = (div_num << 16) | (div_den & 0xFFFF);
+ len += sprintf(buf, "%d\n", adc_data->voltage_ratio);
+ } else if (0 == strcmp(dev_attr->attr.name, "adc_data_raw")) {
+ adc_value = sci_get_adc_data(adc_data->channel, adc_data->scale);
+ if (adc_value < 0)
+ adc_value = 0;
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d\n", adc_value);
+
+ pr_info("value: %d, len: %d\n", adc_value, len);
+ }
+
+ mutex_unlock(&adc_data->lock);
+
+ return len;
+}
+static ssize_t sprd_cal_type_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buf)
+{
+ return sprintf(buf, "%d", cal_type);
+}
+
+static int sprd_device_delete_attributes(struct device *dev,
+ struct device_attribute *dev_attrs, int len)
+{
+ int i = 0;
+
+ for (i = 0; i < len; i++) {
+ device_remove_file(dev, &dev_attrs[i]);
+ }
+
+ return 0;
+}
+
+static int sprd_device_create_attributes(struct device *dev,
+ struct device_attribute *dev_attrs, int len)
+{
+ int i = 0, rc = 0;
+
+ for (i = 0; i < len; i++) {
+ rc = device_create_file(dev, &dev_attrs[i]);
+ if (rc)
+ break;
+ }
+
+ if (rc) {
+ for (; i >= 0 ; --i)
+ device_remove_file(dev, &dev_attrs[i]);
+ }
+
+ return rc;
+}
+
+#ifndef CONFIG_OF
+static int __init sprd_adc_dev_register(void)
+{
+ static struct platform_device sprdadc_device = {
+ .name = SPRD_MODULE_NAME,
+ .id = -1,
+ };
+
+ return platform_device_register(&sprdadc_device);
+}
+#endif
+
+static int sprd_adc_probe(struct platform_device *pdev)
+{
+ struct sprd_adc_data *adc_data = NULL;
+ int rc = 0;
+
+ pr_info("%s()->Line: %d\n", __func__, __LINE__);
+
+ adc_data = devm_kzalloc(&pdev->dev, sizeof(struct sprd_adc_data), GFP_KERNEL);
+ if (!adc_data) {
+ pr_err("%s failed to kzalloc sprd_adc_data!\n", __func__);
+ return -ENOMEM;
+ }
+
+ adc_data->channel = 5;
+ adc_data->misc_dev.minor = MISC_DYNAMIC_MINOR;
+ adc_data->misc_dev.name = SPRD_MODULE_NAME; //pdev->dev.driver->name
+ adc_data->misc_dev.fops = NULL;
+ adc_data->misc_dev.parent = NULL;
+
+ dev_set_drvdata(&pdev->dev, adc_data);
+
+ mutex_init(&adc_data->lock);
+
+ rc = misc_register(&adc_data->misc_dev);
+ if (rc) {
+ pr_err("%s failed to register misc device.\n", __func__);
+ return rc;
+ }
+
+ rc = sprd_device_create_attributes(adc_data->misc_dev.this_device, sprd_adc_attr, ARRAY_SIZE(sprd_adc_attr));
+ if (rc) {
+ pr_err("%s failed to create device attributes.\n", __func__);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int sprd_adc_remove(struct platform_device *pdev)
+{
+ struct sprd_adc_data *adc_data = platform_get_drvdata(pdev);
+ int rc = 0;
+
+ sprd_device_delete_attributes(adc_data->misc_dev.this_device, sprd_adc_attr, ARRAY_SIZE(sprd_adc_attr));
+
+ rc = misc_deregister(&adc_data->misc_dev);
+ if (rc) {
+ pr_err("%s failed to unregister misc device.\n", __func__);
+ }
+
+ kfree(adc_data);
+
+ return rc;
+}
+
+
+#ifdef CONFIG_OF
+static struct of_device_id sprd_adc_of_match[] = {
+ { .compatible = "sprd,sprd-adc", },
+ { }
+};
+#endif
+
+static struct platform_driver sprd_adc_driver = {
+ .probe = sprd_adc_probe,
+ .remove = sprd_adc_remove,
+ .driver = {
+ .name = SPRD_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(sprd_adc_of_match),
+ },
+};
+
+static int __init sprd_adc_driver_init(void)
+{
+ return platform_driver_register(&sprd_adc_driver);
+}
+
+static void __exit sprd_adc_driver_exit(void)
+{
+ platform_driver_unregister(&sprd_adc_driver);
+}
+
+module_init(sprd_adc_driver_init);
+module_exit(sprd_adc_driver_exit);
+
+
+static void sci_adc_enable(void)
+{
+#if defined(CONFIG_ARCH_SC8825)
+ /* enable adc */
+ sci_adi_set(ANA_REG_GLB_ANA_APB_CLK_EN,
+ BIT_ANA_ADC_EB | BIT_ANA_CLK_AUXADC_EN |
+ BIT_ANA_CLK_AUXAD_EN);
+#elif defined(CONFIG_ARCH_SCX35)
+ sci_adi_set(ANA_REG_GLB_ARM_MODULE_EN,
+ BIT_ANA_ADC_EN);
+ sci_adi_set(ANA_REG_GLB_ARM_CLK_EN,
+ BIT_CLK_AUXADC_EN | BIT_CLK_AUXAD_EN);
+ sci_adi_set(ANA_REG_GLB_XTL_WAIT_CTRL,
+ BIT_XTL_EN);
+ sci_glb_set(REG_AON_APB_SINDRV_CTRL, BIT_SINDRV_ENA); //maybe need to modify it in later
+#endif
+}
+
+void sci_adc_dump_register()
+{
+ unsigned long _base = (unsigned long)(io_base);
+ unsigned long _end = _base + 0x64;
+
+ printk("sci_adc_dump_register begin\n");
+ for (; _base < _end; _base += 4) {
+ printk("_base = 0x%lx, value = 0x%x\n", _base, adc_read(_base));
+ }
+ printk("sci_adc_dump_register end\n");
+}
+
+EXPORT_SYMBOL(sci_adc_dump_register);
+
+void sci_adc_init(void)
+{
+ io_base = (void __iomem *)REGS_ADC_BASE;
+
+#ifndef CONFIG_OF
+ sprd_adc_dev_register();
+#endif
+
+ adc_enable_irq(0);
+ adc_clear_irq();
+ sci_adc_enable();
+ if (cal_type == SPRDBAT_AUXADC_CAL_NO) {
+ if (!adc_cali_fuse_proc())
+ cal_type = SPRDBAT_AUXADC_CAL_CHIP;
+ }
+}
+EXPORT_SYMBOL(sci_adc_init);
+
+/*
+* Notes: for hw channel, config its hardware configuration register and using sw channel to read it.
+*/
+static int sci_adc_config(struct adc_sample_data *adc)
+{
+ unsigned long addr = 0;
+ unsigned val = 0;
+ int ret = 0;
+
+ BUG_ON(!adc);
+ BUG_ON(adc->channel_id > ADC_MAX);
+
+ val = BIT_CH_IN_MODE(adc->signal_mode);
+ val |= BIT_CH_SLOW(adc->sample_speed);
+ val |= BIT_CH_SCALE(adc->scale);
+ val |= BIT_CH_ID(adc->channel_id);
+ val |= BIT_CH_DLY_EN(adc->hw_channel_delay ? 1 : 0);
+
+ adc_write(val, (unsigned long)(io_base + ADC_SW_CH_CFG));
+
+ if (adc->channel_type > 0) { /*hardware */
+ adc_write(BIT_HW_CH_DELAY(adc->hw_channel_delay),
+ (unsigned long)(io_base + ADC_HW_CH_DELAY));
+
+ if (adc->channel_type == 1) { /*slow */
+ addr = (unsigned long)(io_base + ADC_SLOW_HW_CHX_CFG(adc->channel_id));
+ } else {
+ addr = (unsigned long)(io_base + ADC_FAST_HW_CHX_CFG(adc->channel_id));
+ }
+ adc_write(val, addr);
+ }
+
+ return ret;
+}
+
+#if defined(CONFIG_ADIE_SC2723) || defined(CONFIG_ADIE_SC2723S)
+#define RATIO(_n_, _d_) (_n_ << 16 | _d_)
+static int sci_adc_ratio(int channel, int scale, int mux)
+{
+ switch (channel) {
+ case ADC_CHANNEL_0:
+ return RATIO(1, 1);
+ case ADC_CHANNEL_1:
+ case ADC_CHANNEL_2:
+ case ADC_CHANNEL_3:
+ return (scale ? RATIO(400, 1025) : RATIO(1, 1));
+ case ADC_CHANNEL_VBAT: //Vbat
+ case ADC_CHANNEL_ISENSE:
+ return RATIO(7, 29);
+ case ADC_CHANNEL_VCHGSEN:
+ return RATIO(77, 1024);
+ case ADC_CHANNEL_DCDCCORE: //dcdc core/arm/mem/gen/rf/con/wpa
+ mux = mux >> 13;
+ switch (mux) {
+ case 1: //dcdcarm
+ case 2: //dcdccore
+ return (scale ? RATIO(36, 55) : RATIO(9, 11));
+ case 3: //dcdcmem
+ case 5: //dcdcrf
+ return (scale ? RATIO(12, 25) : RATIO(3, 5));
+ case 4: //dcdcgen
+ return (scale ? RATIO(3, 10) : RATIO(3, 8));
+ case 6: //dcdccon
+ return (scale ? RATIO(9, 20) : RATIO(9, 16));
+ case 7: //dcdwpa
+ return (scale ? RATIO(12, 55) : RATIO(3, 11));
+ default:
+ return RATIO(1, 1);
+ }
+ case 0xE: //LP dcxo
+ return (scale ? RATIO(4, 5) : RATIO(1, 1));
+ case 0x14: //headmic
+ return RATIO(1, 3);
+
+ case ADC_CHANNEL_LDO0: //dcdc supply LDO, vdd18/vddcamd/vddcamio/vddrf0/vddgen1/vddgen0
+ return RATIO(1, 2);
+ case ADC_CHANNEL_VBATBK: //VbatBK
+ case ADC_CHANNEL_LDO1: //VbatD Domain LDO, vdd25/vddcama/vddsim2/vddsim1/vddsim0
+ case ADC_CHANNEL_LDO2: //VbatA Domain LDO, vddwifipa/vddcammot/vddemmccore/vdddcxo/vddsdcore/vdd28
+ case ADC_CHANNEL_WHTLED: //kpled/vibr
+ case ADC_CHANNEL_WHTLED_VFB://vddsdio/vddusb/vddfgu
+ case ADC_CHANNEL_USBDP: //DP from terminal
+ case ADC_CHANNEL_USBDM: //DM from terminal
+ return RATIO(1, 3);
+
+ default:
+ return RATIO(1, 1);
+ }
+ return RATIO(1, 1);
+}
+
+void sci_adc_get_vol_ratio(unsigned int channel_id, int scale, unsigned int *div_numerators,
+ unsigned int *div_denominators)
+{
+ unsigned int ratio = sci_adc_ratio(channel_id, scale, 0);
+ *div_numerators = ratio >> 16;
+ *div_denominators = ratio << 16 >> 16;
+}
+
+unsigned int sci_adc_get_ratio(unsigned int channel_id, int scale, int mux)
+{
+ unsigned int ratio = (unsigned int)sci_adc_ratio(channel_id, scale, mux);
+
+ return ratio;
+}
+
+#elif (defined(CONFIG_ARCH_SCX15) || defined(CONFIG_ADIE_SC2711))
+#define RATIO(_n_, _d_) (_n_ << 16 | _d_)
+static int sci_adc_ratio(int channel, int scale, int mux)
+{
+ switch (channel) {
+ case ADC_CHANNEL_0:
+ case ADC_CHANNEL_1:
+ case ADC_CHANNEL_2:
+ case ADC_CHANNEL_3:
+ return (scale ? RATIO(400, 1025) : RATIO(1, 1));
+ case ADC_CHANNEL_VBAT: //vbat
+ case ADC_CHANNEL_ISENSE:
+ return RATIO(7, 29);
+ case ADC_CHANNEL_VCHGSEN:
+ return RATIO(77, 1024);
+ case ADC_CHANNEL_DCDCCORE: //dcdccore
+ case ADC_CHANNEL_DCDCARM: //dcdcarm
+ return (scale ? RATIO(4, 5) : RATIO(1, 1));
+ case ADC_CHANNEL_DCDCMEM: //dcdcmem
+ return (scale ? RATIO(3, 5) : RATIO(4, 5));
+ case ADC_CHANNEL_DCDCLDO: //dcdcgen
+ return RATIO(4, 9);
+ case 0x14 /* ADC_CHANNEL_HEADMIC */: //DCDCHEADMIC
+ return RATIO(1, 3);
+ case ADC_CHANNEL_LDO0: //DCDC Supply LDO, VDD18/CAMIO/CAMD/EMMCIO
+ return RATIO(1, 2);
+ case ADC_CHANNEL_VBATBK: //DCDCVBATBK
+ case ADC_CHANNEL_LDO1: //VBATD Domain LDO, VDD25/SD/USB/SIM0/SIM1/SIM2
+ case ADC_CHANNEL_LDO2: //VBATA Domain LDO, VDD28/CAMA/CAMMOT/EMMCCORE/CON/DCXO/RF0
+ case ADC_CHANNEL_USBDP: //DP from terminal
+ case ADC_CHANNEL_USBDM: //DM from terminal
+ return RATIO(1, 3);
+
+ default:
+ return RATIO(1, 1);
+ }
+ return RATIO(1, 1);
+}
+
+void sci_adc_get_vol_ratio(unsigned int channel_id, int scale, unsigned int *div_numerators,
+ unsigned int *div_denominators)
+{
+ unsigned int ratio = sci_adc_ratio(channel_id, scale, 0);
+ *div_numerators = ratio >> 16;
+ *div_denominators = ratio << 16 >> 16;
+}
+
+#else
+void sci_adc_get_vol_ratio(unsigned int channel_id, int scale, unsigned int *div_numerators,
+ unsigned int *div_denominators)
+{
+ unsigned int chip_id = 0;
+
+ switch (channel_id) {
+
+ case ADC_CHANNEL_0:
+ case ADC_CHANNEL_1:
+ case ADC_CHANNEL_2:
+ case ADC_CHANNEL_3:
+ if (scale) {
+ *div_numerators = 16;
+ *div_denominators = 41;
+ } else {
+ *div_numerators = 1;
+ *div_denominators = 1;
+ }
+ return;
+ case ADC_CHANNEL_PROG: //channel 4
+ case ADC_CHANNEL_VCHGBG: //channel 7
+ case ADC_CHANNEL_HEADMIC: //18
+ *div_numerators = 1;
+ *div_denominators = 1;
+ return;
+ case ADC_CHANNEL_VBAT: //channel 5
+ case ADC_CHANNEL_ISENSE: //channel 8
+#if defined(CONFIG_ARCH_SCX35)
+ *div_numerators = 7;
+ *div_denominators = 29;
+#else
+ chip_id = sci_adi_read(CHIP_ID_LOW_REG);
+#ifdef CHIP_ID_HIGH_REG
+ chip_id |= (sci_adi_read(CHIP_ID_HIGH_REG) << 16);
+#endif
+ if (chip_id == 0x8820A001) { //metalfix
+ *div_numerators = 247;
+ *div_denominators = 1024;
+ } else {
+ *div_numerators = 266;
+ *div_denominators = 1000;
+ }
+#endif
+ return;
+ case ADC_CHANNEL_VCHGSEN: //channel 6
+ *div_numerators = 77;
+ *div_denominators = 1024;
+ return;
+ case ADC_CHANNEL_TPYD: //channel 9
+ case ADC_CHANNEL_TPYU: //channel 10
+ case ADC_CHANNEL_TPXR: //channel 11
+ case ADC_CHANNEL_TPXL: //channel 12
+ if (scale) { //larger
+ *div_numerators = 2;
+ *div_denominators = 5;
+ } else {
+ *div_numerators = 3;
+ *div_denominators = 5;
+ }
+ return;
+ case ADC_CHANNEL_DCDCCORE: //channel 13
+ case ADC_CHANNEL_DCDCARM: //channel 14
+ if (scale) { //lager
+ *div_numerators = 4;
+ *div_denominators = 5;
+ } else {
+ *div_numerators = 1;
+ *div_denominators = 1;
+ }
+ return;
+ case ADC_CHANNEL_DCDCMEM: //channel 15
+ if (scale) { //lager
+ *div_numerators = 3;
+ *div_denominators = 5;
+ } else {
+ *div_numerators = 4;
+ *div_denominators = 5;
+ }
+ return;
+ case ADC_CHANNEL_DCDCLDO: //16
+ *div_numerators = 4;
+ *div_denominators = 9;
+ return;
+
+#if defined(CONFIG_ARCH_SCX35)
+ case ADC_CHANNEL_VBATBK:
+ case ADC_CHANNEL_LDO0:
+ case ADC_CHANNEL_LDO2:
+ case ADC_CHANNEL_USBDP:
+ case ADC_CHANNEL_USBDM:
+ *div_numerators = 1;
+ *div_denominators = 3;
+ return;
+ case ADC_CHANNEL_LDO1:
+ *div_numerators = 1;
+ *div_denominators = 2;
+ return;
+ case ADC_CHANNEL_DCDCGPU:
+ if (scale) {
+ *div_numerators = 4;
+ *div_denominators = 5;
+ } else {
+ *div_numerators = 1;
+ *div_denominators = 1;
+ }
+ return;
+ case ADC_CHANNEL_DCDCWRF:
+ if (scale) {
+ *div_numerators = 1;
+ *div_denominators = 3;
+ } else {
+ *div_numerators = 4;
+ *div_denominators = 5;
+ }
+ return;
+#else
+ case ADC_CHANNEL_VBATBK: //channel 17
+ case ADC_CHANNEL_LDO0: //channel 19,20
+ case ADC_CHANNEL_LDO1:
+ *div_numerators = 1;
+ *div_denominators = 3;
+ return;
+ case ADC_CHANNEL_LDO2: //channel 21
+ *div_numerators = 1;
+ *div_denominators = 2;
+ return;
+#endif
+ default:
+ *div_numerators = 1;
+ *div_denominators = 1;
+ break;
+ }
+}
+#endif
+
+int sci_adc_get_values(struct adc_sample_data *adc)
+{
+ unsigned long flags;
+ int cnt = 12;
+ unsigned long addr = 0;
+ unsigned val = 0;
+ int ret = 0;
+ int num = 0;
+ int sample_bits_msk = 0;
+ int *pbuf = 0;
+
+ if (!adc || adc->channel_id > ADC_MAX)
+ return -EINVAL;
+
+ pbuf = adc->pbuf;
+ if (!pbuf)
+ return -EINVAL;
+
+ num = adc->sample_num;
+ if (num > ADC_MAX_SAMPLE_NUM)
+ return -EINVAL;
+
+ sci_adc_lock();
+
+ sci_adc_config(adc); //configs adc sample.
+
+ addr = (unsigned long)(io_base + ADC_CTL);
+ val = adc_read((unsigned long)addr);
+ val &= ~(BIT_ADC_EN | BIT_SW_CH_ON | BIT_ADC_BIT_MODE_MASK);
+ adc_write(val, addr);
+
+ adc_clear_irq();
+
+ val = BIT_SW_CH_RUN_NUM(num);
+ val |= BIT_ADC_EN;
+ val |= BIT_ADC_BIT_MODE(adc->sample_bits);
+ val |= BIT_SW_CH_ON;
+
+ adc_write(val, addr);
+
+ while ((!adc_raw_irqstatus()) && cnt--) {
+ udelay(50);
+ }
+
+ if (!cnt) {
+ ret = -1;
+ WARN_ON(1);
+ goto Exit;
+ }
+
+ if (adc->sample_bits)
+ sample_bits_msk = ((1 << 12) - 1); //12
+ else
+ sample_bits_msk = ((1 << 10) - 1); //10
+ while (num--)
+ *pbuf++ = adc_get_data(sample_bits_msk);
+
+Exit:
+ val = adc_read((unsigned long)addr);
+ val &= ~BIT_ADC_EN;
+ adc_write(val, addr);
+
+ sci_adc_unlock();
+
+ return ret;
+}
+
+EXPORT_SYMBOL_GPL(sci_adc_get_values);
+
+static int __average(int a[], int N)
+{
+#define __DIV_ROUND(n, d) (((n) + ((d)/2)) / (d))
+ int i, sum = 0;
+
+ for (i = 0; i < N; i++)
+ sum += a[i];
+ return __DIV_ROUND(sum, N);
+}
+
+static int sci_adc_set_current(u8 enable, int isen)
+{
+ if(enable) {
+ /* BITS_AUXAD_CURRENT_IBS = (isen * 100 / 125 -1) */
+ isen = (isen * 100 / 125 -1);
+ if(isen > BITS_AUXAD_CURRENT_IBS(-1))
+ isen = BITS_AUXAD_CURRENT_IBS(-1);
+ sci_adi_write(ANA_REG_GLB_AUXAD_CTL, (BIT_AUXAD_CURRENTSEN_EN | BITS_AUXAD_CURRENT_IBS(isen)),
+ BIT_AUXAD_CURRENTSEN_EN | BITS_AUXAD_CURRENT_IBS(-1));
+ } else {
+ sci_adi_clr(ANA_REG_GLB_AUXAD_CTL, BIT_AUXAD_CURRENTSEN_EN);
+ }
+
+ return 0;
+}
+
+/*
+ * sci_adc_get_value_by_isen - read adc value by current sense
+ * @channel: adc software channel id;
+ * @scale: adc sample scale, 0:little scale, 1:big scale;
+ * @current: adc current isense(uA), 1.25uA/step, max 40uA;
+ *
+ * returns: adc value
+ */
+int sci_adc_get_value_by_isen(unsigned int channel, int scale, int isen)
+{
+#define ADC_MESURE_NUMBER 15
+
+ struct adc_sample_data adc;
+ int results[ADC_MESURE_NUMBER + 1] = {0};
+ int ret = 0, i = 0;
+
+ /* Fixme: only external adc channel used */
+ BUG_ON(channel > 3);
+
+ WARN_ON(isen > 40);
+
+ adc.channel_id = channel;
+ adc.channel_type = 0;
+ adc.hw_channel_delay = 0;
+ adc.pbuf = &results[0];
+ adc.sample_bits = 1;
+ adc.sample_num = ADC_MESURE_NUMBER;
+ adc.sample_speed = 0;
+ adc.scale = scale;
+ adc.signal_mode = 0;
+
+ sci_adc_set_current(1, isen);
+ if(0 == sci_adc_get_values(&adc)) {
+ ret = __average(&results[ADC_MESURE_NUMBER/5],
+ (ADC_MESURE_NUMBER - ADC_MESURE_NUMBER * 2 /5));
+ }
+ sci_adc_set_current(0, 0);
+
+ for(i = 0; i < ARRAY_SIZE(results); i++) {
+ printk("%d\t", results[i]);
+ }
+ printk("\n%s() adc[%d] value: %d\n", __func__, channel, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sci_adc_get_value_by_isen);
+
+static int is_valid_adc_cal(void)
+{
+ return !!(0 != adc_cali_data[0][0]);
+}
+
+static int __init adc_cali_fuse_proc(void)
+{
+ if (cal_type == SPRDBAT_AUXADC_CAL_NO) {
+ unsigned int efuse_cal_data[2] = { 0 };
+ #ifdef CONFIG_OTP_SPRD
+ if (sci_efuse_calibration_get(efuse_cal_data)) {
+ adc_cali_data[0][0] =
+ efuse_cal_data[0] & 0xffff;
+ adc_cali_data[0][1] =
+ (efuse_cal_data[0] >> 16) & 0xffff;
+ adc_cali_data[1][0] =
+ efuse_cal_data[1] & 0xffff;
+ adc_cali_data[1][1] =
+ (efuse_cal_data[1] >> 16) & 0xffff;
+ printk("auxadc cal from efuse ok!!!\n");
+ printk("efuse_cal_data[0]: 0x%x, efuse_cal_data[1]:0x%x\n",
+ efuse_cal_data[0], efuse_cal_data[1]);
+ return 0;
+ }
+ #endif
+ }
+ return -1;
+}
+
+static int __init adc_cali_proc(char *str)
+{
+ unsigned int adc_data[2] = { 0 };
+ memset(adc_cali_data, 0, ARRAY_SIZE(adc_cali_data));
+
+ if (str) {
+ char *cali_data = &str[1];
+ sscanf(cali_data, "%d,%d", &adc_data[0], &adc_data[1]);
+
+ adc_cali_data[0][0] = adc_data[0] & 0xFFFF;
+ adc_cali_data[0][1] = (adc_data[0] >> 16) & 0xFFFF;
+ adc_cali_data[1][0] = adc_data[1] & 0xFFFF;
+ adc_cali_data[1][1] = (adc_data[1] >> 16) & 0xFFFF;
+
+ pr_info("%s adc cali data (%d : %d -- %d : %d)\n", __func__,
+ (int)adc_cali_data[0][0], (int)adc_cali_data[0][1],
+ (int)adc_cali_data[1][0], (int)adc_cali_data[1][1]);
+ cal_type = SPRDBAT_AUXADC_CAL_NV;
+ }
+ return 1;
+}
+__setup("adc_cal", adc_cali_proc);
+
+
+static int adc2vbat(int adc_res, int scale)
+{
+ int t = 0;
+
+ if(!is_valid_adc_cal())
+ return 0;
+
+ if (1 || scale) {
+ t = adc_cali_data[0][0] - adc_cali_data[1][0];
+ t *= (adc_res - adc_cali_data[0][1]);
+ t /= (adc_cali_data[0][1] - adc_cali_data[1][1]);
+ t += adc_cali_data[0][0];
+ } else {
+ t = adc_cali_data[2][0] - adc_cali_data[3][0];
+ t *= (adc_res - adc_cali_data[2][1]);
+ t /= (adc_cali_data[2][1] - adc_cali_data[3][1]);
+ t += adc_cali_data[2][0];
+ }
+
+ return t;
+}
+
+#if defined(CONFIG_ADIE_SC2723) || defined(CONFIG_ADIE_SC2723S)
+/*
+* Get adc channel voltage
+* @ channel: adc channel id
+* @ scale: adc scale
+* @ mux: adc calibration
+* @ adc_data: adc channel raw data
+*
+* return 0 if failed
+*
+*/
+int sci_adc_vol_request(unsigned int channel, int scale, int mux, int* adc_data)
+{
+ unsigned int bat_numerators;
+ unsigned int bat_denominators;
+ unsigned int chan_numerators;
+ unsigned int chan_denominators;
+ u32 res;
+ int chan_adc = 0, chan_vol = 0;
+
+ res = (u32)sci_adc_get_ratio(ADC_CHANNEL_VBAT, 0, 0);
+ bat_numerators = (res >> 16) & 0xFFFF;
+ bat_denominators = res & 0xFFFF;
+
+ chan_adc = sci_get_adc_data(channel, scale);
+ if (chan_adc) {
+ if(adc_data)
+ *adc_data = chan_adc;
+
+ chan_vol = adc2vbat(chan_adc, scale);
+ if(scale) {
+ res = (u32) sci_adc_get_ratio(channel, scale, mux);
+ chan_numerators = (res >> 16) & 0xFFFF;
+ chan_denominators = res & 0xFFFF;
+
+ chan_vol *= (bat_numerators * chan_denominators);
+ chan_vol /= (bat_denominators * chan_numerators);
+ }
+ }
+
+ return chan_vol;
+}
+EXPORT_SYMBOL_GPL(sci_adc_vol_request);
+#endif
diff --git a/drivers/platform/sprd/adi.c b/drivers/platform/sprd/adi.c
new file mode 100644
index 00000000..9fc9f30e
--- /dev/null
+++ b/drivers/platform/sprd/adi.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ * Copyright (C) 2012 steve zhan
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+/**
+*For arm read ,delay about 1us when clk_adi runs at 76.8M
+*The interface timing is compatible with spi timing
+*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/irqflags.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/hwspinlock.h>
+#include <asm/delay.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/arch_lock.h>
+#include <soc/sprd/adi.h>
+
+unsigned long sprd_adi_base;
+unsigned long sprd_adi_phys;
+
+/* soc defined begin*/
+#define CTL_ADI_BASE (SPRD_ADI_BASE)
+
+/* registers definitions for controller CTL_ADI */
+#define REG_ADI_CTRL0 (CTL_ADI_BASE + 0x04)
+#ifdef CONFIG_ARCH_SCX35LT8
+#define REG_ADI_CHNL_PRIL (CTL_ADI_BASE + 0x08)
+#define REG_ADI_CHNL_PRIH (CTL_ADI_BASE + 0x0C)
+#define REG_ADI_INT_RAW (CTL_ADI_BASE + 0x14)
+#define REG_ADI_RD_CMD (CTL_ADI_BASE + 0x28)
+#define REG_ADI_RD_DATA (CTL_ADI_BASE + 0x2C)
+#define REG_ADI_FIFO_STS (CTL_ADI_BASE + 0x30)
+#define REG_ADI_GSSI_CFG0 (CTL_ADI_BASE + 0x20)
+#define REG_ADI_GSSI_CFG1 (CTL_ADI_BASE + 0x24)
+#else
+#define REG_ADI_CHNL_PRI (CTL_ADI_BASE + 0x08)
+#define REG_ADI_INT_RAW (CTL_ADI_BASE + 0x10)
+#define REG_ADI_RD_CMD (CTL_ADI_BASE + 0x24)
+#define REG_ADI_RD_DATA (CTL_ADI_BASE + 0x28)
+#define REG_ADI_FIFO_STS (CTL_ADI_BASE + 0x2c)
+#define REG_ADI_GSSI_CFG0 (CTL_ADI_BASE + 0x1C)
+#define REG_ADI_GSSI_CFG1 (CTL_ADI_BASE + 0x20)
+#endif
+/* bits definitions for register REG_ADI_CTRL0 */
+#define BIT_ARM_SCLK_EN ( BIT(1) )
+#define BITS_CMMB_WR_PRI ( (1) << 4 & (BIT(4)|BIT(5)) )
+
+/* bits definitions for register REG_ADI_CHNL_PRI */
+#define BITS_PD_WR_PRI ( (1) << 14 & (BIT(14)|BIT(15)) )
+#define BITS_RFT_WR_PRI ( (1) << 12 & (BIT(12)|BIT(13)) )
+#define BITS_DSP_RD_PRI ( (2) << 10 & (BIT(10)|BIT(11)) )
+#define BITS_DSP_WR_PRI ( (2) << 8 & (BIT(8)|BIT(9)) )
+#define BITS_ARM_RD_PRI ( (3) << 6 & (BIT(6)|BIT(7)) )
+#define BITS_ARM_WR_PRI ( (3) << 4 & (BIT(4)|BIT(5)) )
+#define BITS_STC_WR_PRI ( (1) << 2 & (BIT(2)|BIT(3)) )
+#define BITS_INT_STEAL_PRI ( (3) << 0 & (BIT(0)|BIT(1)) )
+
+/* bits definitions for register REG_ADI_RD_DATA */
+#define BIT_RD_CMD_BUSY ( BIT(31) )
+#define SHIFT_RD_ADDR ( 16 )
+
+#define SHIFT_RD_VALU ( 0 )
+#define MASK_RD_VALU ( 0xFFFF )
+
+/* bits definitions for register REG_ADI_FIFO_STS */
+#define BIT_FIFO_FULL ( BIT(11) )
+#define FIFO_IS_FULL() (__raw_readl((void __iomem *)REG_ADI_FIFO_STS) & BIT_FIFO_FULL)
+#define BIT_FIFO_EMPTY ( BIT(10) )
+
+/* special V1 (sc8830 soc) defined */
+/* bits definitions for register REG_ADI_CTRL0 */
+#define BIT_ADI_WR(_X_) ( (_X_) << 2 )
+#define BITS_ADDR_BYTE_SEL(_X_) ( (_X_) << 0 & (BIT(0)|BIT(1)) )
+
+/* bits definitions for register REG_ADI_CHNL_PRI */
+#define VALUE_CH_PRI (0x0)
+
+
+#if defined(CONFIG_ARCH_SCX35)
+#define ANA_VIRT_BASE ( REGS_ADISLAVE_BASE )
+#define ANA_PHYS_BASE ( REGS_ADISLAVE_PHYS )
+#else
+#define ANA_VIRT_BASE ( REGS_MISC_BASE )
+#define ANA_PHYS_BASE ( REGS_MISC_PHYS )
+#endif
+
+#define ANA_ADDR_SIZE (SZ_4K)
+
+#define TO_ADDR(_x_) ( ((_x_) >> SHIFT_RD_ADDR) & readback_addr_mak )
+/* soc defined end*/
+
+/*This value have a bit of depending on real hardware fifo size*/
+#define CACHE_SIZE (16)
+#define HEAD_ADD (1)
+#define TAIL_ADD (0)
+
+static struct __data {
+ unsigned long reg;
+ u16 val;
+} __data_array[CACHE_SIZE];
+
+static struct __data *head_p = &__data_array[0];
+static struct __data *tail_p = &__data_array[0];
+static u32 data_in_cache = 0;
+static u32 readback_addr_mak __read_mostly = 0;
+
+/*FIXME: Now define adi IP version, sc8825 is zero, sc8830 is one,
+* Adi need init early that than read soc id, now using this ARCH dependency.
+*/
+static inline int __adi_ver(void)
+{
+#ifdef CONFIG_ARCH_SC8825
+ return 0;
+#else
+ return 1;
+#endif
+}
+
+static inline int __adi_fifo_drain(void)
+{
+ int cnt = 1000;
+ while (!(__raw_readl((void __iomem *)REG_ADI_FIFO_STS) & BIT_FIFO_EMPTY) && cnt--) {
+ udelay(1);
+ }
+ WARN(cnt == 0, "ADI WAIT timeout!!!");
+ return 0;
+}
+
+int sci_is_adi_vaddr(unsigned long vaddr)
+{
+ return (vaddr >= ANA_VIRT_BASE && vaddr <= (ANA_VIRT_BASE + ANA_ADDR_SIZE));
+}
+EXPORT_SYMBOL(sci_is_adi_vaddr);
+
+int sci_adi_p2v(unsigned long paddr, unsigned long *vaddr)
+{
+ if(paddr < ANA_PHYS_BASE || paddr > (ANA_PHYS_BASE + ANA_ADDR_SIZE)) {
+ return -1;
+ } else {
+ *vaddr = paddr - ANA_PHYS_BASE + ANA_VIRT_BASE;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(sci_adi_p2v);
+
+static inline int __adi_addr_check(unsigned long vaddr)
+{
+ if(!sci_is_adi_vaddr(vaddr)) {
+ WARN_ONCE(1, "Maybe ADI vaddr is wrong?!!");
+ return -1;
+ }
+ return 0;
+}
+
+static inline unsigned long __adi_translate_addr(unsigned long regvddr)
+{
+ regvddr = regvddr - ANA_VIRT_BASE + ANA_PHYS_BASE;
+ return regvddr;
+}
+
+static inline int __adi_read(unsigned long regPddr, unsigned int *v)
+{
+ unsigned long val;
+ int cnt = 2000;
+ int retry_cnt = 0;
+
+ /*
+ * We don't wait write fifo empty in here,
+ * Because if normal write is SYNC, that will
+ * wait fifo empty at the end of the write.
+ */
+re_try:
+ __raw_writel((u32)regPddr,(void __iomem *)REG_ADI_RD_CMD);
+
+ /*
+ * wait read operation complete, RD_data[31]
+ * is set simulaneously when writing read command.
+ * RD_data[31] will be cleared after the read operation complete,
+ */
+ do {
+ val = __raw_readl((void __iomem *)REG_ADI_RD_DATA);
+ } while ((val & BIT_RD_CMD_BUSY) && cnt--);
+
+ WARN(cnt == 0, "ADI READ timeout!!!");
+ /* val high part should be the address of the last read operation */
+ if ((!v) || TO_ADDR(val) != (regPddr & readback_addr_mak)) {
+ printk("Warning:val = 0x%lx, regPaddr = 0x%lx, readback_addr_mak = 0x%x\n",
+ val,regPddr,readback_addr_mak);
+ if(retry_cnt++ < 5)
+ goto re_try;
+ else
+ return -1;
+ }
+
+ *v = val & MASK_RD_VALU;
+ return 0;
+}
+
+int sci_adi_read(unsigned long reg)
+{
+ int val = 0;
+ if (!__adi_addr_check(reg)) {
+ unsigned long flags;
+ int ret = 0;
+ reg = __adi_translate_addr(reg);
+ __arch_default_lock(HWLOCK_ADI, &flags);
+ ret = __adi_read(reg, &val);
+ __arch_default_unlock(HWLOCK_ADI, &flags);
+ if (ret) {
+ printk("read error: reg = 0x%lx\n",reg);
+ BUG_ON(1);
+ }
+ }
+ return val;
+}
+EXPORT_SYMBOL(sci_adi_read);
+
+static inline void __p_add(struct __data **p, u32 isHead)
+{
+ if (++(*p) > &__data_array[CACHE_SIZE - 1])
+ (*p) = &__data_array[0];
+ if (head_p == tail_p) {
+ if (isHead == HEAD_ADD) {
+ data_in_cache = 0;
+ } else {
+ data_in_cache = CACHE_SIZE;
+ }
+ } else {
+ data_in_cache = 2;
+ }
+}
+
+static inline int __adi_write(unsigned long reg, u16 val, u32 sync)
+{
+ tail_p->reg = reg;
+ tail_p->val = val;
+ __p_add(&tail_p, TAIL_ADD);
+ while (!FIFO_IS_FULL() && (data_in_cache != 0)) {
+ __raw_writel(head_p->val,(void __iomem *)head_p->reg);
+ __p_add(&head_p, HEAD_ADD);
+ }
+
+ if (sync || data_in_cache == CACHE_SIZE) {
+ __adi_fifo_drain();
+ while (data_in_cache != 0) {
+ while (FIFO_IS_FULL()) {
+ cpu_relax();
+ }
+ __raw_writel(head_p->val,(void __iomem *)head_p->reg);
+ __p_add(&head_p, HEAD_ADD);
+ }
+ __adi_fifo_drain();
+ }
+
+ return 0;
+}
+
+int sci_adi_write_fast(unsigned long reg, u16 val, u32 sync)
+{
+ if (!__adi_addr_check(reg)) {
+ unsigned long flags;
+ __arch_default_lock(HWLOCK_ADI, &flags);
+ __adi_write(reg, val, sync);
+ __arch_default_unlock(HWLOCK_ADI, &flags);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(sci_adi_write_fast);
+
+int sci_adi_write(unsigned long reg, u16 or_val, u16 clear_msk)
+{
+ if (!__adi_addr_check(reg)) {
+ unsigned long flags;
+ int ret = 0, val = 0;
+ __arch_default_lock(HWLOCK_ADI, &flags);
+ ret = __adi_read(__adi_translate_addr(reg), &val);
+ if (!ret)
+ __adi_write(reg, (val & ~clear_msk) | or_val, 1);
+ __arch_default_unlock(HWLOCK_ADI, &flags);
+ if (ret)
+ BUG_ON(1);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(sci_adi_write);
+
+static void __init __adi_init(void)
+{
+ uint32_t value;
+ value = __raw_readl((void __iomem *)REG_ADI_CTRL0);
+
+ if (__adi_ver() == 0) {
+ value &= ~BIT_ARM_SCLK_EN;
+ value |= BITS_CMMB_WR_PRI;
+ __raw_writel(value, (void __iomem *)REG_ADI_CTRL0);
+
+#ifdef CONFIG_ARCH_SCX35LT8
+ value = __raw_readl((void __iomem *)REG_ADI_CHNL_PRIL);
+#else
+ value = __raw_readl((void __iomem *)REG_ADI_CHNL_PRI);
+#endif
+ value |= BITS_PD_WR_PRI | BITS_RFT_WR_PRI |
+ BITS_DSP_RD_PRI | BITS_DSP_WR_PRI |
+ BITS_ARM_RD_PRI | BITS_ARM_WR_PRI |
+ BITS_STC_WR_PRI | BITS_INT_STEAL_PRI;
+#ifdef CONFIG_ARCH_SCX35LT8
+ __raw_writel(value, (void __iomem *)REG_ADI_CHNL_PRIL);
+#else
+ __raw_writel(value, (void __iomem *)REG_ADI_CHNL_PRI);
+#endif
+ readback_addr_mak = 0x7ff;
+ } else if (__adi_ver() == 1) {
+ if (value)
+ WARN_ON(1);
+
+ value = VALUE_CH_PRI;
+#ifdef CONFIG_ARCH_SCX35LT8
+ __raw_writel(value, (void __iomem *)REG_ADI_CHNL_PRIL);
+ __raw_writel(value, (void __iomem *)REG_ADI_CHNL_PRIH);
+#else
+ __raw_writel(value, (void __iomem *)REG_ADI_CHNL_PRI);
+#endif
+ value = __raw_readl((void __iomem *)REG_ADI_GSSI_CFG0);
+ readback_addr_mak = (value & 0x3f) - ((value >> 11) & 0x1f) - 1;
+ readback_addr_mak = (1<<(readback_addr_mak + 2)) - 1;
+ }
+}
+
+int __init sci_adi_init(void)
+{
+ struct resource res;
+ struct device_node *np;
+
+
+ np = of_find_compatible_node(NULL, NULL, "sprd,adi");
+ if (of_can_translate_address(np)) {
+ of_address_to_resource(np, 0, &res);
+ sprd_adi_phys = res.start;
+ sprd_adi_base = ioremap_nocache(res.start,
+ resource_size(&res));
+ if (!sprd_adi_base) {
+ printk("%s: no such memory!\n", __func__);
+ return -ENOMEM;
+ }
+ printk("%s: sprd_adi_base=%lx\n", __func__, sprd_adi_base);
+ }
+
+#if defined(CONFIG_ARCH_SC8825)
+ /* enable adi in global regs */
+ sci_glb_set((unsigned long )REG_GLB_GEN0, BIT_ADI_EB);
+ /* reset adi */
+ sci_glb_set((unsigned long )REG_GLB_SOFT_RST, BIT_ADI_RST);
+ udelay(2);
+ sci_glb_clr((unsigned long )REG_GLB_SOFT_RST, BIT_ADI_RST);
+
+#elif defined(CONFIG_ARCH_SCX35)
+ /*enable adi in global regs*/
+ sci_glb_set((unsigned long )REG_AON_APB_APB_EB0, BIT_ADI_EB);
+#endif
+ __adi_init();
+
+ return 0;
+}
+
diff --git a/drivers/platform/sprd/adie_irq.c b/drivers/platform/sprd/adie_irq.c
new file mode 100644
index 00000000..df4ac0ca
--- /dev/null
+++ b/drivers/platform/sprd/adie_irq.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ * Author: steve.zhan <steve.zhan@spreadtrum.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/irqflags.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#ifdef CONFIG_OF
+#include <linux/irqdomain.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#endif
+
+#include <asm/delay.h>
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/globalregs.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/irqs.h>
+#include <soc/sprd/sci_glb_regs.h>
+
+//#include <mach/irqs.h>
+
+/* registers definitions for controller ANA_CTL_INT */
+#define ANA_REG_INT_MASK_STATUS (ANA_CTL_INT_BASE + 0x0000)
+#define ANA_REG_INT_RAW_STATUS (ANA_CTL_INT_BASE + 0x0004)
+#define ANA_REG_INT_EN (ANA_CTL_INT_BASE + 0x0008)
+#define ANA_REG_INT_MASK_STATUS_SYNC (ANA_CTL_INT_BASE + 0x000c)
+
+/* bits definitions for register REG_INT_MASK_STATUS */
+#if defined(CONFIG_ARCH_SC8825)
+/* vars definitions for controller ANA_CTL_INT */
+#define MASK_ANA_INT ( 0xFF )
+#elif defined(CONFIG_ARCH_SCX15)
+/* vars definitions for controller ANA_CTL_INT */
+#define MASK_ANA_INT ( 0x3FF )
+#elif defined(CONFIG_ARCH_SCX35)
+/* vars definitions for controller ANA_CTL_INT */
+#define MASK_ANA_INT ( 0x7FF )
+#else
+#error "pls define interrupt number mask value"
+#endif
+
+/* used to define the start irq in xlate fun */
+#define ADIE_SUB_IRQ_START 32
+
+/* used to save vitual irq num */
+static int *virq_table = NULL;
+
+/* the total irq num of adie domain */
+static int irqnums = 0;
+
+static struct irq_domain *irq_domain;
+void sprd_ack_ana_irq(struct irq_data *data)
+{
+ /* nothing to do... */
+}
+
+static void sprd_mask_ana_irq(struct irq_data *data)
+{
+ int offset;
+#ifdef CONFIG_OF
+ int i;
+
+ for (i = 0; i < irqnums; i++) {
+ if (virq_table[i] == data->irq) {
+ offset = i;
+ break;
+ }
+ }
+ if (i == irqnums)
+ return;
+#else
+ offset = data->irq - IRQ_ANA_INT_START;
+#endif
+ pr_debug("%s %d\n", __FUNCTION__, data->irq);
+ sci_adi_write(ANA_REG_INT_EN, 0, BIT(offset) & MASK_ANA_INT);
+}
+
+static void sprd_unmask_ana_irq(struct irq_data *data)
+{
+ int offset;
+#ifdef CONFIG_OF
+ int i;
+
+ for (i = 0; i < irqnums; i++) {
+ if (virq_table[i] == data->irq) {
+ offset = i;
+ break;
+ }
+ }
+ if (i == irqnums)
+ return;
+#else
+ offset = data->irq - IRQ_ANA_INT_START;
+#endif
+ pr_debug("%s %d\n", __FUNCTION__, data->irq);
+ sci_adi_write(ANA_REG_INT_EN, BIT(offset) & MASK_ANA_INT, 0);
+}
+
+static struct irq_chip sprd_muxed_ana_chip = {
+ .name = "irq-ANA",
+ .irq_ack = sprd_ack_ana_irq,
+ .irq_mask = sprd_mask_ana_irq,
+ .irq_unmask = sprd_unmask_ana_irq,
+};
+
+static irqreturn_t sprd_muxed_ana_handler(int irq, void *dev_id)
+{
+ uint32_t irq_ana, status;
+ int i;
+
+ status = sci_adi_read(ANA_REG_INT_MASK_STATUS) & MASK_ANA_INT;
+ pr_debug("%s %d 0x%08x\n", __FUNCTION__, irq, status);
+ while (status) {
+ i = __ffs(status);
+ status &= ~(1 << i);
+#ifndef CONFIG_OF
+ irq_ana = IRQ_ANA_INT_START + i;
+#else
+ irq_ana = virq_table[i];
+#endif
+ pr_debug("%s generic_handle_irq %d\n", __FUNCTION__, irq_ana);
+ generic_handle_irq(irq_ana);
+ }
+ return IRQ_HANDLED;
+}
+
+static struct irqaction __adie_mux_irq = {
+ .name = "adie_mux",
+ .flags = IRQF_DISABLED | IRQF_NO_SUSPEND,
+ .handler = sprd_muxed_ana_handler,
+};
+
+void __init ana_init_irq(void)
+{
+ int n;
+
+ for (n = IRQ_ANA_INT_START; n < IRQ_ANA_INT_START + NR_ANA_IRQS; n++) {
+ irq_set_chip_and_handler(n, &sprd_muxed_ana_chip,
+ handle_level_irq);
+ set_irq_flags(n, IRQF_VALID);
+ }
+ setup_irq(IRQ_ANA_INT, &__adie_mux_irq);
+
+}
+
+#ifdef CONFIG_OF
+#define IRQCHIP_DECLARE(name,compstr,fn) \
+ static const struct of_device_id irqchip_of_match_##name \
+ __used __section(__irqchip_of_table) \
+ = { .compatible = compstr, .data = fn }
+
+static int adie_irq_domain_xlate(struct irq_domain *d,
+ struct device_node *ctrlr,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq, unsigned int *out_type)
+{
+ if (WARN_ON(intsize < 1))
+ return -EINVAL;
+ *out_hwirq = intspec[0] + ADIE_SUB_IRQ_START;
+ *out_type = (intsize > 1) ? intspec[1] : IRQ_TYPE_NONE;
+ return 0;
+}
+
+static int adie_irq_domain_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ virq_table[hw-ADIE_SUB_IRQ_START] = virq;
+ irq_set_chip_and_handler(virq, &sprd_muxed_ana_chip,
+ handle_level_irq);
+ set_irq_flags(virq, IRQF_VALID);
+
+ return 0;
+}
+
+const struct irq_domain_ops adie_irq_domain_ops = {
+ .xlate = adie_irq_domain_xlate,
+ .map = adie_irq_domain_map,
+};
+int __init adi_of_init(struct device_node *node, struct device_node *parent)
+{
+ struct resource res;
+ int irq;
+
+ if (WARN_ON(!node))
+ return -ENODEV;
+
+ if(of_address_to_resource(node, 0, &res)){
+ pr_err("no reg of property specified for adi\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(node, "sprd,irqnums", &irqnums)){
+ pr_err("no sprd,irqnums of property specified");
+ BUG();
+ }
+
+ irq_domain = irq_domain_add_linear(node, irqnums + ADIE_SUB_IRQ_START,
+ &adie_irq_domain_ops, NULL);
+ BUG_ON(!irq_domain);
+
+ virq_table = kmalloc(sizeof(int)*irqnums, GFP_KERNEL);
+ BUG_ON(!virq_table);
+
+ if (parent) {
+ irq = irq_of_parse_and_map(node, 0);
+ setup_irq(irq, &__adie_mux_irq);
+ }
+
+ return 0;
+}
+IRQCHIP_DECLARE(sprd_adi, "sprd,adi-bus", adi_of_init);
+
+#endif
diff --git a/drivers/platform/sprd/arch_init.c b/drivers/platform/sprd/arch_init.c
new file mode 100644
index 00000000..68674b2a
--- /dev/null
+++ b/drivers/platform/sprd/arch_init.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications Inc.
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/adc.h>
+#include <soc/sprd/arch_lock.h>
+#include <linux/of_platform.h>
+
+static int __init arch_init(void)
+{
+ early_hwspinlocks_init();
+ sci_adi_init();
+ sci_adc_init();
+
+ return 0;
+}
+
+postcore_initcall_sync(arch_init);
+
+//jian.chen temporary modification
+#ifdef CONFIG_64BIT
+const struct of_device_id of_sprd_late_bus_match_table[] = {
+ { .compatible = "sprd,sound", },
+ {}
+};
+static void __init sc8830_init_late(void)
+{
+ of_platform_populate(of_find_node_by_path("/sprd-audio-devices"),
+ of_sprd_late_bus_match_table, NULL, NULL);
+}
+core_initcall(sc8830_init_late);
+#endif
diff --git a/drivers/platform/sprd/arch_lock.c b/drivers/platform/sprd/arch_lock.c
new file mode 100644
index 00000000..b4a7d66a
--- /dev/null
+++ b/drivers/platform/sprd/arch_lock.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ * Author: steve.zhan <steve.zhan@spreadtrum.com>
+ * 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.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/hwspinlock.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <soc/sprd/sci.h>
+#include <soc/sprd/arch_lock.h>
+#include <linux/of_irq.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "devices.h"
+
+/**
+* Each physical lock must have a system-wide id number
+* that is agreed upon, otherwise remote processors can't possibly assume
+* they're using the same hardware lock.
+*/
+struct hwspinlock *hwlocks[HWSPINLOCK_ID_TOTAL_NUMS];
+unsigned char __initdata hwlocks_implemented[HWSPINLOCK_ID_TOTAL_NUMS];
+unsigned int hwspinlock_vid;
+unsigned char local_hwlocks_status[HWSPINLOCK_ID_TOTAL_NUMS];/*array[0] standfor lock0*/
+
+int __init early_init_hwlocks(void)
+{
+ int i;
+ struct hwspinlock **plock;
+ arch_hwlocks_implemented();
+
+ for (i = 0; i < HWSPINLOCK_ID_TOTAL_NUMS; ++i) {
+ if (hwlocks_implemented[i]) {
+ plock = &hwlocks[i];
+ *plock = hwspin_lock_request_specific(i);
+ if (WARN_ON(IS_ERR_OR_NULL(*plock)))
+ *plock = NULL;
+ else
+ pr_info("early alloc hwspinlock id %d\n",
+ hwspin_lock_get_id(*plock));
+ }
+ }
+ return 0;
+}
+
+static int remove_node(struct device_node *np)
+{
+ struct property *ps;
+
+ ps = kzalloc(sizeof(*ps), GFP_KERNEL);
+ if (!ps)
+ return -1;
+
+ ps->value = kstrdup("no", GFP_KERNEL);
+ ps->length = strlen(ps->value);
+ ps->name = kstrdup("status", GFP_KERNEL);
+ of_update_property(np, ps);
+ printk("After register,hwspinlock1 node available=%d\n",of_device_is_available(np));
+
+ return 0;
+}
+
+int __init hwspinlocks_init(void)
+{
+ struct device_node *np;
+ struct platform_device *pdev;
+ int ret;
+
+ np = of_find_node_by_name(NULL, "hwspinlock1");
+ if (!np) {
+ pr_warn("Can't get the hwspinlock1 node!\n");
+ return -ENODEV;
+ }
+
+ pdev = of_platform_device_create(np, 0, NULL);
+ if (!pdev) {
+ pr_warn("register hwspinlock1 failed!\n");
+ return -ENODEV;
+ }
+ pr_info("SPRD register hwspinlock1 ok,hwspinlock1's name is %s!\n",pdev->name);
+ ret = remove_node(np);
+
+ return ret;
+}
+
diff --git a/drivers/platform/sprd/arch_misc.c b/drivers/platform/sprd/arch_misc.c
new file mode 100644
index 00000000..4804d2b2
--- /dev/null
+++ b/drivers/platform/sprd/arch_misc.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2013 Spreadtrum Communications Inc.
+ * Copyright (C) 2013 steve zhan
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/irqflags.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/hwspinlock.h>
+#include <linux/mm.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+
+#include <asm/delay.h>
+#include <asm/memory.h>
+#ifndef CONFIG_64BIT
+#include <asm/highmem.h>
+#include <mach/irqs.h>
+#endif
+#include <asm/pgtable-hwdef.h>
+#include <asm/tlbflush.h>
+#include <asm/fixmap.h>
+#include <asm/pgalloc.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/arch_misc.h>
+
+#ifndef CONFIG_64BIT
+#include "../../../arch/arm/mm/mm.h"
+#else
+#include "../../../arch/arm64/mm/mm.h"
+#endif
+/*#define DEBUG */
+#ifdef DEBUG
+#define DEBUGSEE printk
+#else
+#define DEBUGSEE(...)
+#endif
+
+static u32 chip_id __read_mostly;
+u32 sci_get_chip_id(void)
+{
+ return chip_id;
+}
+
+u32 sci_get_ana_chip_id(void)
+{
+#if defined(CONFIG_ARCH_SCX35)
+ return (u32)sci_adi_read(ANA_REG_GLB_CHIP_ID_HIGH) << 16 |
+ (u32)(sci_adi_read(ANA_REG_GLB_CHIP_ID_LOW) & ~MASK_ANA_VER);
+#else
+ return 0;
+#endif
+}
+
+int sci_get_ana_chip_ver(void)
+{
+#if defined(CONFIG_ARCH_SCX35)
+ return ((u32)sci_adi_read(ANA_REG_GLB_CHIP_ID_LOW) & MASK_ANA_VER);
+#else
+ return 0;
+#endif
+}
+
+void set_section_ro(unsigned long virt, unsigned long numsections)
+{
+ pmd_t *pmd;
+ pgd_t *pgd;
+ pud_t *pud;
+ unsigned long start = virt;
+ unsigned long end = virt + (numsections << SECTION_SHIFT);
+ unsigned long pmd_end;
+
+ while (virt < end) {
+ //pmd = pmd_off_k(virt);
+ pgd = pgd_offset_k(virt);
+ BUG_ON(pgd_none(*pgd) || pgd_bad(*pgd));
+
+ pud = pud_offset(pgd, virt);
+ BUG_ON(pud_none(*pud) || pud_bad(*pud));
+ pmd = pmd_offset(pud,virt);
+ if ((pmd_val(*pmd) & PMD_TYPE_MASK) != PMD_TYPE_SECT) {
+ pr_err("%s: pmd %p=%08lx for %08lx not section map\n",
+ __func__, pmd, pmd_val(*pmd), virt);
+ return;
+ }
+
+ /*
+ * We set section page table entry APX:1 AP:01
+ * Privileged : READ, Unprivileged : No Access
+ */
+ *pmd |= PAGE_READONLY;
+ pr_debug("%s: pmd %p=%08lx for %08lx ok\n",
+ __func__, pmd, pmd_val(*pmd), virt);
+ virt += SECTION_SIZE;
+ }
+
+ flush_tlb_kernel_range(start, end);
+}
+
+void __iomap_page(unsigned long virt, unsigned long size, int enable)
+{
+ unsigned long addr = virt, end = virt + (size & ~(SZ_4K - 1));
+ u32 *pgd;
+ u32 *pte;
+
+ pgd = (u32 *)init_mm.pgd + (addr >> 20);
+ pte = (u32 *)__va(*pgd & 0xfffffc00);
+ pte += (addr >> 12) & 0xff;
+
+ pr_debug("%s (%d) pgd %p, pte %p, *pte %x\n",
+ __FUNCTION__, enable, pgd, pte, *pte);
+
+ if (enable)
+ *pte |= 0x3;
+ else
+ *pte &= ~0x3;
+
+ flush_tlb_kernel_range(virt, end);
+}
+#if 0
+#define sci_mm_glb_set(reg, bit) __raw_writel((bit), (void *)REG_GLB_SET(reg))
+#define sci_mm_glb_clr(reg, bit) __raw_writel((bit), (void *)REG_GLB_CLR(reg))
+#define sci_mm_reg_set(reg, bit) __raw_writel(__raw_readl((void *)(reg)) | (bit), (void *)(reg))
+#define sci_mm_reg_clr(reg, bit) __raw_writel((__raw_readl((void *)(reg)) & ~(bit)), (void *)(reg))
+#ifdef CONFIG_OF
+int local_mm_enable(void *c, int enable)
+#else
+int sci_mm_enable(void *c, int enable, unsigned long *pflags)
+#endif
+{
+ if (enable) {
+ int to = 2000;
+ /* FIXME: mm domain power on at first
+ */
+ sci_mm_glb_clr(REG_PMU_APB_PD_MM_TOP_CFG, BIT_PD_MM_TOP_FORCE_SHUTDOWN);
+
+ __iomap_page(REGS_MM_AHB_BASE, SZ_4K, enable);
+ __iomap_page(REGS_MM_CLK_BASE, SZ_4K, enable);
+ __iomap_page(SPRD_DCAM_BASE, SZ_4K, enable);
+ __iomap_page(SPRD_VSP_BASE, SZ_4K, enable);
+
+#if defined(CONFIG_ARCH_SCX15)
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT_MM_EB);
+#else
+ sci_mm_glb_clr(REG_PMU_APB_PD_MM_TOP_CFG, BIT_PD_MM_TOP_FORCE_SHUTDOWN);
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT_MM_EB);
+ /* FIXME: wait a moment for mm domain stable
+ */
+ while ((__raw_readl((void *)REG_PMU_APB_PWR_STATUS0_DBG) & BITS_PD_MM_TOP_STATE(-1)) && to--) {
+ udelay(50);
+ }
+
+ WARN(0 || 0 != sci_glb_read(REG_PMU_APB_PWR_STATUS0_DBG, BITS_PD_MM_TOP_STATE(-1)),
+ "MM TOP CFG 0x%08x, TIMEOUT %d\n", __raw_readl((void *)REG_PMU_APB_PD_MM_TOP_CFG),
+ (2000 - to) * 50);
+
+ WARN(0 == sci_glb_read(REG_AON_APB_APB_EB0, BIT_MM_EB),
+ "AON APB EB0 0x%08x\n", __raw_readl((void *)REG_AON_APB_APB_EB0));
+
+ /* FIXME: force mm clock enable
+ */
+ sci_mm_glb_set(REG_AON_APB_APB_EB0, BIT_MM_EB);
+ sci_mm_glb_set(REG_MM_AHB_AHB_EB, BIT_MM_CKG_EB);
+ sci_mm_reg_set(REG_MM_AHB_GEN_CKG_CFG, BIT_MM_MTX_AXI_CKG_EN | BIT_MM_AXI_CKG_EN);
+ sci_mm_reg_set(REG_MM_CLK_MM_AHB_CFG, 0x3);/* default set mm ahb 153.6MHz */
+#endif
+ } else {
+#if defined(CONFIG_ARCH_SCX15)
+ sci_glb_clr(REG_AON_APB_APB_EB0, BIT_MM_EB);
+#else
+ sci_mm_reg_clr(REG_MM_AHB_GEN_CKG_CFG, BIT_MM_MTX_AXI_CKG_EN | BIT_MM_AXI_CKG_EN);
+ sci_mm_glb_clr(REG_MM_AHB_AHB_EB, BIT_MM_CKG_EB);
+
+ sci_glb_clr(REG_AON_APB_APB_EB0, BIT_MM_EB);
+ /* FIXME: do not force mm domain power off before retention
+ */
+ sci_mm_glb_set(REG_PMU_APB_PD_MM_TOP_CFG, BIT_PD_MM_TOP_FORCE_SHUTDOWN);
+#endif
+ /* FIXME: clean mm io map,
+ * do not access any reg in mm domain after mm clock disabled
+ */
+ __iomap_page(REGS_MM_AHB_BASE, SZ_4K, enable);
+ __iomap_page(REGS_MM_CLK_BASE, SZ_4K, enable);
+ __iomap_page(SPRD_DCAM_BASE, SZ_4K, enable);
+ __iomap_page(SPRD_VSP_BASE, SZ_4K, enable);
+ }
+ return 0;
+}
+#endif
+void __init sc_init_chip_id(void)
+{
+#if defined(CONFIG_ARCH_SC8825)
+ chip_id = __raw_readl(REG_AHB_CHIP_ID);
+#elif defined(CONFIG_ARCH_SCX35LT8)
+ chip_id = __raw_readl((void *)REG_AON_APB_CHIP_ID0);
+ if (chip_id == 0x6B4C5438) { //kLT8
+ chip_id = __raw_readl((void *)REG_AON_APB_CHIP_ID1);
+ if (chip_id == 0x53686172) { //shar
+ chip_id = 0x9838;
+ } else {
+ chip_id = 0;
+ printk(KERN_WARNING"Chip id is wrong!\n");
+ }
+ } else {
+ chip_id = 0;
+ printk(KERN_WARNING"Chip id is wrong!\n");
+ }
+#elif defined(CONFIG_ARCH_SCX35)
+ chip_id = __raw_readl((void *)REG_AON_APB_CHIP_ID);
+ if (chip_id == 0)
+ chip_id = SCX35_ALPHA_TAPOUT; //alpha Tapout.
+#else
+ #error "Is chip_id ..?"
+#endif
+}
+
+static inline int __chip_raw_ddie_write(ulong vreg, ulong or_val, u32 clear_msk)
+{
+ ulong val = __raw_readl((void *)vreg);
+ writel((val & ~clear_msk) | or_val, (void *)vreg);
+ return 0;
+}
+
+int sci_read_va(ulong vreg, ulong * val)
+{
+ DEBUGSEE("sci_read_va vreg = 0x%x type:", vreg);
+ if (!val)
+ return -1;
+
+ if (unlikely(sci_is_adi_vaddr(vreg))) {
+ DEBUGSEE("adie\n");
+ *val = sci_adi_read(vreg);
+ } else {
+ DEBUGSEE("ddie\n");
+ *val = __raw_readl((void *)vreg);
+ }
+
+ return 0;
+}
+
+int sci_write_va(ulong vreg, const ulong or_val, const u32 clear_msk)
+{
+ DEBUGSEE("sci_write_va\n");
+
+ if (unlikely(sci_is_adi_vaddr(vreg))) {
+ DEBUGSEE("adie\n");
+ sci_adi_write(vreg, or_val, clear_msk);
+ } else { /*FIXME: is not safe */
+ DEBUGSEE("ddie\n");
+ __chip_raw_ddie_write(vreg, or_val, clear_msk);
+ }
+ return 0;
+}
+
+int sci_read_pa(ulong preg, ulong * val)
+{
+ void __iomem *addr;
+ int ret = 0;
+ ulong vaddr = 0;
+
+ if (!val)
+ return -1;
+
+ DEBUGSEE("sci_read_pa 0x%x\n", preg);
+ addr = __va(preg);
+ if (!virt_addr_valid(addr)) {
+ if (!sci_adi_p2v(preg, &vaddr)) {
+ *val = sci_adi_read((long) vaddr);
+ } else {
+ addr = ioremap_nocache(preg, PAGE_SIZE);
+ if (!addr) {
+ printk(KERN_WARNING
+ "unable to map i/o region\n");
+ ret = -ENOMEM;
+ goto Exit;
+ }
+ *val = __raw_readl((void *) addr);
+ iounmap(addr);
+ }
+ } else {
+ *val = __raw_readl((void *) addr);
+ }
+Exit:
+ return ret;
+}
+
+int sci_write_pa(ulong paddr, const ulong or_val, const u32 clear_msk)
+{
+ int ret = 0;
+ ulong vaddr = 0;
+
+ DEBUGSEE("sci_write_pa 0x%x, 0x%x, 0x%x\n", paddr, or_val, clear_msk);
+ vaddr = (ulong) __va(paddr);
+ if (!virt_addr_valid(vaddr)) {
+ if (!sci_adi_p2v(paddr, &vaddr)) {
+ sci_adi_write(vaddr, or_val, clear_msk);
+ } else {
+ vaddr = (ulong) ioremap_nocache(paddr, PAGE_SIZE);
+ if (!vaddr) {
+ printk(KERN_WARNING
+ "unable to map i/o region\n");
+ ret = -ENOMEM;
+ goto Exit;
+ }
+ DEBUGSEE("vaddr = 0x%x\n", vaddr);
+ __chip_raw_ddie_write((ulong) vaddr, or_val, clear_msk);
+ iounmap((void __iomem *)vaddr);
+ }
+ } else {
+ __chip_raw_ddie_write((ulong) vaddr, or_val, clear_msk);
+ }
+Exit:
+ return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+struct sci_lookat {
+ const char *name;
+
+#define _LOOKAT_RWPV_ (1<<0)
+#define _LOOKAT_INPUT_OUTPUT_DATA_ (1<<1)
+ int entry_type;
+};
+static struct sci_lookat __lookat_array[] = {
+ {"addr_rwpv", _LOOKAT_RWPV_},
+ {"data", _LOOKAT_INPUT_OUTPUT_DATA_},
+};
+
+struct lookat_request {
+#define __READ__ (0<<1)
+#define __WRITE__ (1<<1)
+#define __P__ (0<<0)
+#define __V__ (1<<0)
+ u32 cmd;
+ ulong addr;
+ ulong value; /*if is read, save result to value, if is write, value is need to write */
+};
+static struct lookat_request __request[5];
+static int index = 0;
+static int queue_add_element(u32 raw_cmd)
+{
+ __request[index].cmd = raw_cmd & (0x3);
+ __request[index].addr = raw_cmd & (~0x3);
+ if (index == ARRAY_SIZE(__request) - 1)
+ index = 0;
+ else
+ index++;
+ return 0;
+}
+
+static struct lookat_request *__return_last_eliment(void)
+{
+ if (!index)
+ return &__request[ARRAY_SIZE(__request) - 1];
+ else
+ return &__request[index - 1];
+}
+
+static inline int queue_modify_value(ulong value)
+{
+ struct lookat_request *p = __return_last_eliment();
+
+ if (!(p->cmd & __WRITE__)) {
+ return -1;
+ } else { /*write, save the write data that need to send */
+ p->value = value;
+ return 0;
+ }
+}
+
+static int do_request(void)
+{
+ struct lookat_request *p;
+ int ret = 0;
+
+ p = __return_last_eliment();
+ DEBUGSEE("p->cmd = 0x%x, p->addr = 0x%x ", p->cmd, p->addr);
+
+ if ((p->cmd & __WRITE__) == __WRITE__) {
+ if ((p->cmd & __V__) == __V__)
+ ret = sci_write_va(p->addr, p->value, ~0);
+ else
+ ret = sci_write_pa(p->addr, p->value, ~0);
+ } else {
+ if ((p->cmd & __V__) == __V__)
+ ret = sci_read_va(p->addr, &p->value);
+ else
+ ret = sci_read_pa(p->addr, &p->value);
+ }
+ DEBUGSEE("p->value = 0x%x\n", p->value);
+
+ return ret;
+}
+
+static int __debug_set(void *data, u64 val)
+{
+ struct sci_lookat *p = data;
+ int ret = 0;
+ struct lookat_request *r;
+
+ if (p->entry_type == _LOOKAT_INPUT_OUTPUT_DATA_) {
+ if (queue_modify_value((ulong) val) || do_request()) {
+ ret = -1;
+ goto Exit;
+ }
+ } else if (p->entry_type == _LOOKAT_RWPV_) {
+ ret = queue_add_element((ulong) val);
+ if (!ret) {
+ r = __return_last_eliment();
+ if (!((r->cmd & __WRITE__) == __WRITE__))
+ ret = do_request();
+ goto Exit;
+ }
+ } else {
+ BUG_ON(1);
+ }
+
+Exit:
+ return ret;
+}
+
+static int __debug_get(void *data, u64 * val)
+{
+ struct sci_lookat *p = data;
+
+ if (p->entry_type == _LOOKAT_INPUT_OUTPUT_DATA_) {
+ struct lookat_request *r = __return_last_eliment();
+ *val = r->value;
+ DEBUGSEE("__debug_get *val = 0x%08x ", r->value);
+ } else if (p->entry_type == _LOOKAT_RWPV_) {
+ printk("echo addr | b00 to read Paddr's value\n");
+ printk("echo addr | b01 to read Vaddr's value\n");
+ printk
+ ("echo addr | b10 to write Paddr's value,Pls echo value to data file\n");
+ printk
+ ("echo addr | b11 to write Vaddr's value,Pls echo value to data file\n");
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(lookat_fops, __debug_get, __debug_set, "0x%08llx");
+
+static struct dentry *lookat_base;
+static int __init __debug_add(struct sci_lookat *lookat)
+{
+ if (!debugfs_create_file(lookat->name, 0644, lookat_base,
+ lookat, &lookat_fops))
+ return -ENOMEM;
+
+ return 0;
+}
+
+int __init lookat_debug_init(void)
+{
+ int i;
+ lookat_base = debugfs_create_dir("lookat", NULL);
+ if (!lookat_base)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(__lookat_array); ++i) {
+ if (__debug_add(&__lookat_array[i]))
+ goto err;
+ }
+
+ return 0;
+err:
+ debugfs_remove(lookat_base);
+ return -1;
+}
+
+module_init(lookat_debug_init);
+#endif
+
+#if defined(CONFIG_ARCH_SCX35LT8)
+void __clock_init_early(void)
+{
+ sci_glb_set(REG_AON_APB_APB_EB1,
+ BIT_CODEC_EB
+ );
+
+ sci_glb_set(REG_AON_APB_APB_EB0,
+ BIT_GPU_EB
+ );
+}
+
+static void sprd_ap_machine_start(void)
+{
+ __clock_init_early();
+}
+arch_initcall(sprd_ap_machine_start);
+
+void __clock_init_after(void)
+{
+ sci_glb_clr(REG_AON_APB_APB_EB1,
+ BIT_CODEC_EB
+ );
+
+ sci_glb_clr(REG_AON_APB_APB_EB0,
+ BIT_GPU_EB
+ );
+}
+
+static void sprd_ap_machine_exit(void)
+{
+ __clock_init_after();
+}
+subsys_initcall(sprd_ap_machine_exit);
+#endif
diff --git a/drivers/platform/sprd/aux_debug.c b/drivers/platform/sprd/aux_debug.c
new file mode 100755
index 00000000..ac65a71e
--- /dev/null
+++ b/drivers/platform/sprd/aux_debug.c
@@ -0,0 +1,148 @@
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/kobject.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <mach/sci.h>
+#include <linux/earlysuspend.h>
+#include <mach/sci_glb_regs.h>
+#include <mach/adi.h>
+#include <linux/random.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/cacheflush.h>
+#include <asm/suspend.h>
+#include <linux/vmalloc.h>
+#include <linux/printk.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+
+enum aux_clk_src_e
+{
+ aux_clk_32k = 0,
+ aux_clk_26m_rf0 = 1,
+ aux_clk_26m_rf1 = 2,
+ aux_clk_48m = 3,
+ aux_clk_52m = 4,
+ aux_clk_51_2m = 5,
+ aux_clk_37_5m = 6,
+ aux_clk_40m_wifipll1 = 7,
+ aux_clk_66m = 8,
+ aux_clk_40m_wifipll2 = 9,
+ aux_clk_max = 10,
+};
+
+static char clk_menu[10][16]=
+{
+ "32k",
+ "26m rf0",
+ "26m rf1",
+ "48m tdpll",
+ "52m cpll",
+ "51_2m wpll",
+ "37_5m mpll",
+ "40m wifipll1",
+ "66m dpll",
+ "40m wifipll2"
+};
+
+struct aux_cfg_info
+{
+ struct dentry *debug_aux_dir;
+ char (*ptr_menu)[16];
+ volatile u32 aux_ena;
+ volatile u32 aux_sel;
+ volatile u32 aux_pin;
+ volatile u32 magic_header;
+ volatile u32 is_init_cfg;
+ volatile u32 cur_sel;
+ volatile u32 magic_ender;
+};
+
+static struct aux_cfg_info clk_cfg_info =
+{
+ 0x00000000, /*debug_aux_dir*/
+ clk_menu, /*clk_meum*/
+ 0x00000000, /*aux_ena*/
+ 0x00000000, /*aux_sel*/
+ 0x00000000, /*aux_pin*/
+ 0x5a5aa5a5, /*magic_header*/
+ 0x00000000, /*is_init_cfg*/
+ 0x6c657300, /*cur_sel*/
+ 0xa5a55a5a /*magic_ender*/
+};
+
+static void aux_clk_config(u32 clk_sel)
+{
+ u32 reg_val;
+
+ if(clk_sel < aux_clk_max){
+ /*select pin function to func 0*/
+ reg_val = sci_glb_read(clk_cfg_info.aux_pin,-1UL);
+ reg_val &= ~(0x3 << 4);
+ sci_glb_write(clk_cfg_info.aux_pin,reg_val,-1UL);
+ /*enable aux clock*/
+ sci_glb_set(clk_cfg_info.aux_ena,0x1 << 2);
+ /*select aux clock source*/
+ sci_glb_set(clk_cfg_info.aux_sel,clk_sel);
+ }
+}
+
+static int debugfs_aux_clk_sel_get(void *data, u64 * val)
+{
+ *val = clk_cfg_info.cur_sel & 0xf;
+
+ return 0;
+}
+static int debugfs_aux_clk_sel_set(void *data, u64 val)
+{
+ clk_cfg_info.cur_sel &= ~0xf;
+ clk_cfg_info.cur_sel |= val;
+
+ aux_clk_config(clk_cfg_info.cur_sel & 0xf);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_aux_clk_ops,
+ debugfs_aux_clk_sel_get, debugfs_aux_clk_sel_set, "%llu\n");
+
+static void aux_debugfs_create(void)
+{
+ clk_cfg_info.debug_aux_dir = debugfs_create_dir("aux_dir", NULL);
+ if (IS_ERR_OR_NULL(clk_cfg_info.debug_aux_dir)) {
+ pr_err("%s return %p\n", __FUNCTION__, clk_cfg_info.debug_aux_dir);
+ }
+ debugfs_create_file("aux", S_IRUGO | S_IWUGO,
+ clk_cfg_info.debug_aux_dir, &clk_cfg_info.cur_sel, &fops_aux_clk_ops);
+}
+
+static int __init aux_debug_init(void)
+{
+ clk_cfg_info.aux_sel = REG_AON_APB_AON_CGM_CFG;
+ clk_cfg_info.aux_ena = REG_AON_APB_APB_EB1;
+ clk_cfg_info.aux_pin = SPRD_PIN_BASE + 0x2ec;
+
+ aux_debugfs_create();
+
+ if(clk_cfg_info.is_init_cfg)
+ aux_clk_config(clk_cfg_info.cur_sel & 0xf);
+
+ return 0;
+}
+
+static void __exit aux_debug_exit(void)
+{
+ debugfs_remove_recursive(clk_cfg_info.debug_aux_dir);
+}
+module_init(aux_debug_init);
+module_exit(aux_debug_exit);
diff --git a/drivers/platform/sprd/avs_debug.c b/drivers/platform/sprd/avs_debug.c
new file mode 100755
index 00000000..01b1dbbb
--- /dev/null
+++ b/drivers/platform/sprd/avs_debug.c
@@ -0,0 +1,139 @@
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/kobject.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <mach/sci.h>
+#include <linux/earlysuspend.h>
+#include <mach/sci_glb_regs.h>
+#include <mach/adi.h>
+#include <linux/random.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/cacheflush.h>
+#include <asm/suspend.h>
+#include <linux/vmalloc.h>
+#include <linux/printk.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+
+struct avs_hw_ctrl
+{
+ volatile u32 avs_tune_lmt_cfg;
+ volatile u32 avs_tune_status;
+ volatile u32 avs_cfg;
+ volatile u32 avs_tune_step_cfg;
+ volatile u32 avs_wait_cfg;
+ volatile u32 avs_int_cfg;
+ volatile u32 avs_start_single_act;
+ volatile u32 avs_start_repeat_act;
+ volatile u32 avs_hpm_en;
+ volatile u32 avs_hpm_rpt_anls;
+ volatile u32 avs_hpm_rpt_vld_status[4];
+ volatile u32 avs_hpm_dly_wind_sel;
+ volatile u32 avs_fsm_sts;
+};
+
+struct sprd_avs_info
+{
+ struct avs_hw_ctrl *chip;
+ struct timer_list avs_timer;
+ struct dentry *debug_avs_dir;
+ volatile u32 printfreq;
+ u32 window;
+};
+
+static struct sprd_avs_info sai;
+
+static void avs_init(void)
+{
+ /* avs enable */
+ sci_glb_set(REG_AON_APB_APB_EB1,1 << 6);
+}
+
+static void avs_trigger(void)
+{
+ /* avs hpm enable */
+ sai.chip->avs_hpm_en |= (1 << 1);
+ /* avs tune limit cfg */
+ sai.chip->avs_tune_lmt_cfg = 0xff80;
+ /* avs hpm rpt anls */
+ sai.chip->avs_hpm_rpt_anls = 0x3FF94;
+ /* start avs */
+ sai.chip->avs_start_repeat_act = 0x1;
+ /* delay for something */
+ udelay(5);
+}
+
+static void avs_timer_func(unsigned long data)
+{
+ static u32 arm_voltage_int[]={1100,700,800,900,1000,650,1200,1300};
+ u32 reg_val = sci_adi_read(ANA_REG_GLB_DCDC_ARM_ADI);
+ u32 int_part,frc_part;
+ int_part = arm_voltage_int[(reg_val & 0x7) >> 5];
+ frc_part = (reg_val & 0x1F)*3;
+
+ avs_trigger();
+
+ printk("****************************avs debug**********************************\r\n");
+ sai.window = sai.chip->avs_hpm_rpt_vld_status[0];
+ printk("avs window 0x%x arm voltage is %d mv\r\n",sai.window,(int_part + frc_part));
+ printk("****************************avs debug**********************************\r\n");
+ mod_timer(&sai.avs_timer,jiffies + msecs_to_jiffies(sai.printfreq + 10000));
+}
+
+static int debugfs_avs_printfreq_get(void *data, u64 * val)
+{
+ *(u32 *) data = *val = sai.printfreq;
+ return 0;
+}
+static int debugfs_avs_printfreq__set(void *data, u64 val)
+{
+ sai.printfreq = *(u32 *) data = val;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fops_avs_printfreq,
+ debugfs_avs_printfreq_get, debugfs_avs_printfreq__set, "%llu\n");
+
+static void avs_debugfs_create(void)
+{
+ sai.debug_avs_dir = debugfs_create_dir("avs_dir", NULL);
+ if (IS_ERR_OR_NULL(sai.debug_avs_dir)) {
+ pr_err("%s return %p\n", __FUNCTION__, sai.debug_avs_dir);
+ }
+ debugfs_create_file("avs", S_IRUGO | S_IWUGO,
+ sai.debug_avs_dir, &sai.printfreq, &fops_avs_printfreq);
+}
+
+static int __init avs_debug_init(void)
+{
+ sai.chip = (struct avs_hw_ctrl*)SPRD_AVSCA7_BASE;
+
+ init_timer(&sai.avs_timer);
+ sai.avs_timer.function = avs_timer_func;
+ sai.avs_timer.expires = jiffies + msecs_to_jiffies(2000);
+ sai.avs_timer.data = (u32)&sai;
+
+ avs_init();
+
+ avs_debugfs_create();
+
+ add_timer(&sai.avs_timer);
+ return 0;
+}
+
+static void __exit avs_debug_exit(void)
+{
+ del_timer_sync(&sai.avs_timer);
+}
+module_init(avs_debug_init);
+module_exit(avs_debug_exit);
diff --git a/drivers/platform/sprd/boot_mode.c b/drivers/platform/sprd/boot_mode.c
new file mode 100644
index 00000000..b206dc4d
--- /dev/null
+++ b/drivers/platform/sprd/boot_mode.c
@@ -0,0 +1,46 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+
+
+static int calibration_mode = false;
+static int __init calibration_start(char *str)
+{
+ int calibration_device =0;
+ int mode=0,freq=0,device=0;
+ if(str){
+ pr_info("modem calibartion:%s\n", str);
+ sscanf(str, "%d,%d,%d", &mode,&freq,&device);
+ }
+ if(device & 0x80){
+ calibration_device = device & 0xf0;
+ calibration_mode = true;
+ pr_info("calibration device = 0x%x\n",calibration_device);
+ }
+ return 1;
+}
+__setup("calibration=", calibration_start);
+
+int in_calibration(void)
+{
+ return (int)(calibration_mode == true);
+}
+
+EXPORT_SYMBOL(in_calibration);
+
+static int autotest_mode = false;
+static int __init autotest_start(char *str)
+{
+ autotest_mode = true;
+ return 1;
+}
+__setup("autotest=", autotest_start);
+
+int in_autotest(void)
+{
+ return (int)(autotest_mode == true);
+}
+
+EXPORT_SYMBOL(in_autotest);
diff --git a/drivers/platform/sprd/busmonitor.c b/drivers/platform/sprd/busmonitor.c
new file mode 100644
index 00000000..10bd2148
--- /dev/null
+++ b/drivers/platform/sprd/busmonitor.c
@@ -0,0 +1,2111 @@
+/*copyright (C) 2014 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/semaphore.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/timer.h>
+#include <linux/timex.h>
+#include <linux/rtc.h>
+
+#include <soc/sprd/sci.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/irqs.h>
+#include <soc/sprd/busmonitor.h>
+#include <soc/sprd/iomap.h>
+#include <soc/sprd/pm_debug.h>
+#include <soc/sprd/sprd_persistent_clock.h>
+
+
+#ifdef CONFIG_OF
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#endif
+
+ulong axi_bm_base = 0;
+ulong ahb_bm_base = 0;
+
+#define SPRD_AXI_BM_OFFSET 0x10000
+#define SPRD_AHB_BM_OFFSET 0x100000
+#define SPRD_AXIBM0_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM0)
+#define SPRD_AXIBM1_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM1)
+#define SPRD_AXIBM2_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM2)
+#define SPRD_AXIBM3_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM3)
+#define SPRD_AXIBM4_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM4)
+#define SPRD_AXIBM5_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM5)
+#define SPRD_AXIBM6_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM6)
+#define SPRD_AXIBM7_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM7)
+#define SPRD_AXIBM8_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM8)
+#define SPRD_AXIBM9_BASE (axi_bm_base + SPRD_AXI_BM_OFFSET * AXI_BM9)
+
+#define SPRD_BM0_BASE (ahb_bm_base + CHN0 * SPRD_AHB_BM_OFFSET)
+#define SPRD_BM1_BASE (ahb_bm_base + CHN1 * SPRD_AHB_BM_OFFSET)
+#define SPRD_BM2_BASE (ahb_bm_base + CHN2 * SPRD_AHB_BM_OFFSET)
+
+#define PUB_APB_BM_CNT_START REG_PUB_APB_BUSMON_CNT_START
+#define PUB_APB_BM_CFG REG_PUB_APB_BUSMON_CFG
+
+#define AP_AHB_AHB_EB REG_AP_AHB_AHB_EB
+#define AP_AHB_AHB_RST REG_AP_AHB_AHB_RST
+#define AP_AHB_MISC_CFG REG_AP_AHB_MISC_CFG
+
+struct memory_layout{
+ u32 dram_start;
+ u32 dram_end;
+
+ u32 ap_start1;
+ u32 ap_end1;
+
+ u32 shm_start;
+ u32 shm_end;
+
+ u32 ap_start2;
+ u32 ap_end2;
+
+ u32 cpgge_start;
+ u32 cpgge_end;
+
+ u32 cpw_start;
+ u32 cpw_end;
+
+ u32 cpwcn_start;
+ u32 cpwcn_end;
+
+ u32 cptl_start;
+ u32 cptl_end;
+
+ u32 ap_start3;
+ u32 ap_end3;
+
+ u32 fb_start;
+ u32 fb_end;
+
+ u32 ion_start;
+ u32 ion_end;
+};
+
+static struct memory_layout memory_layout;
+
+static u32 bm_count = 0;
+
+static DEFINE_SPINLOCK(bm_lock);
+
+static ulong __sci_get_bm_base(u32 bm_index)
+{
+ switch (bm_index) {
+ case AXI_BM0:
+ return SPRD_AXIBM0_BASE;
+ case AXI_BM1:
+ return SPRD_AXIBM1_BASE;
+ case AXI_BM2:
+ return SPRD_AXIBM2_BASE;
+ case AXI_BM3:
+ return SPRD_AXIBM3_BASE;
+ case AXI_BM4:
+ return SPRD_AXIBM4_BASE;
+ case AXI_BM5:
+ return SPRD_AXIBM5_BASE;
+ case AXI_BM6:
+ return SPRD_AXIBM6_BASE;
+ case AXI_BM7:
+ return SPRD_AXIBM7_BASE;
+ case AXI_BM8:
+ return SPRD_AXIBM8_BASE;
+ case AXI_BM9:
+ return SPRD_AXIBM9_BASE;
+ case AHB_BM0:
+ return SPRD_BM0_BASE;
+ case AHB_BM1:
+ return SPRD_BM1_BASE;
+ case AHB_BM2:
+ return SPRD_BM2_BASE;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static void __sci_bm_init(void)
+{
+ u32 bm_index;
+ volatile struct sci_bm_reg *bm_reg;
+
+ for (bm_index = AXI_BM0; bm_index < BM_SIZE; bm_index++) {
+ bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_index);
+ memset((void *)bm_reg, 0x0, sizeof(struct sci_bm_reg));
+ }
+}
+
+static inline void __sci_axi_bm_chn_en(int chn)
+{
+ ulong val, base_reg;
+
+ base_reg = __sci_get_bm_base(chn);
+ val = __raw_readl((volatile void *)(base_reg + AXI_BM_INTC_REG));
+ val |= (BM_CHN_EN);
+ __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG));
+}
+
+static inline void __sci_axi_bm_chn_int_clr(int chn)
+{
+ ulong base_reg, val;
+
+ base_reg = __sci_get_bm_base(chn);
+ val = __raw_readl((volatile void *)(base_reg + AXI_BM_INTC_REG));
+ val |= (BM_INT_CLR);
+ __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG));
+}
+
+static inline void __sci_axi_bm_chn_int_disable(int chn)
+{
+ ulong base_reg, val;
+
+ base_reg = __sci_get_bm_base(chn);
+ val = __raw_readl((volatile void *)(base_reg + AXI_BM_INTC_REG));
+ val &= ~(BM_INT_EN);
+ val |= (BM_INT_CLR);
+ __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG));
+}
+
+static inline void __sci_axi_bm_chn_int_enable(int chn)
+{
+ ulong base_reg, val;
+
+ base_reg = __sci_get_bm_base(chn);
+ val = __raw_readl((volatile void *)(base_reg + AXI_BM_INTC_REG));
+ val |= (BM_INT_CLR | BM_INT_EN);
+ __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG));
+}
+
+static inline ulong __sci_axi_bm_chn_cnt_bw(int chn)
+{
+ ulong base_reg, rbw, wbw;
+
+ base_reg = __sci_get_bm_base(chn);
+ rbw = __raw_readl((volatile void *)(base_reg + AXI_BM_RBW_IN_WIN_REG));
+ wbw = __raw_readl((volatile void *)(base_reg + AXI_BM_WBW_IN_WIN_REG));
+ if(rbw || wbw)
+ BM_INFO(" chn:%d, rbw:%lu, wbw:%lu \n", chn, rbw, wbw );
+
+ return (rbw+wbw);
+}
+
+static void __sci_axi_bm_cnt_start(void)
+{
+ ulong bm_index, base_reg, val;
+
+ for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) {
+ base_reg = __sci_get_bm_base(bm_index);
+ val = __raw_readl((volatile void *)(base_reg + AXI_BM_INTC_REG));
+ val |= (BM_CHN_EN | BM_CNT_EN | BM_CNT_START | BM_INT_EN);
+ __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG));
+ }
+ return;
+}
+
+static void __sci_axi_bm_cnt_stop(void)
+{
+ ulong bm_index, base_reg, val;
+
+ for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) {
+ base_reg = __sci_get_bm_base(bm_index);
+ val = __raw_readl((volatile void *)(base_reg + AXI_BM_INTC_REG));
+ val &= ~(BM_CNT_START | BM_INT_EN);//disable cnt and disable int
+ __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG));
+ }
+ return;
+}
+
+static void __sci_bm_int_clr(void)
+{
+ int bm_index;
+
+ for (bm_index = AXI_BM0; bm_index < BM_SIZE; bm_index++) {
+ __sci_axi_bm_chn_int_clr(bm_index);
+ }
+}
+
+static void __sci_axi_bm_int_disable(void)
+{
+ int bm_index;
+
+ for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) {
+ __sci_axi_bm_chn_int_disable(bm_index);
+ }
+}
+
+static void __sci_axi_bm_int_enable(void)
+{
+ int bm_index;
+
+ for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) {
+ __sci_axi_bm_chn_int_enable(bm_index);
+ }
+}
+
+/* performance count clear */
+static void __sci_axi_bm_cnt_clr(void)
+{
+ ulong bm_index, base_reg, val;
+
+ for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) {
+ base_reg = __sci_get_bm_base(bm_index);
+ val = __raw_readl((volatile void *)base_reg);
+ val |= (BM_CHN_EN | BM_CNT_EN);
+ __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG));
+ val &= ~(BM_CNT_START);
+ __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG));
+ val |= (BM_CNT_CLR);
+ __raw_writel(val, (volatile void *)(base_reg + AXI_BM_INTC_REG));
+ }
+ return;
+}
+
+static void __sci_axi_bm_set_winlen(void)
+{
+ ulong bm_index, base_reg, axi_clk, win_len;
+ /*the win len is 10ms*/
+#ifdef CONFIG_ARCH_SCX30G
+ axi_clk = 640;//emc_clk_get();
+#else
+ //axi_clk = __raw_readl(REG_AON_APB_DPLL_CFG) & 0x7ff;
+ //axi_clk = axi_clk << 2;
+ axi_clk = 640;
+#endif
+ /*the win_len = (axi_clk / 1000) * 10 */
+ win_len = axi_clk * 10000;
+ for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++) {
+ base_reg = __sci_get_bm_base(bm_index);
+ __raw_writel(win_len, (volatile void *)(base_reg + AXI_BM_CNT_WIN_LEN_REG));
+ }
+}
+
+static int __sci_bm_glb_reset_and_enable(u32 bm_index, bool is_enable)
+{
+ ulong reg_en, reg_rst, bit_en, bit_rst;
+
+ switch (bm_index) {
+ case AXI_BM0:
+ case AXI_BM1:
+ case AXI_BM2:
+ case AXI_BM3:
+ case AXI_BM4:
+ case AXI_BM5:
+ case AXI_BM6:
+ case AXI_BM7:
+ case AXI_BM8:
+ case AXI_BM9:
+ reg_en = PUB_APB_BM_CFG;//REG_PUB_APB_BUSMON_CFG;
+ reg_rst = PUB_APB_BM_CFG;
+ bit_en = BIT(16 + bm_index);
+ bit_rst = BIT(bm_index);
+ break;
+ case AHB_BM0:
+ case AHB_BM1:
+ case AHB_BM2:
+ reg_en = AP_AHB_AHB_EB;// REG_AP_AHB_AHB_EB;
+ reg_rst = AP_AHB_AHB_RST; //REG_AP_AHB_AHB_RST;
+ bit_en = BIT(14 + bm_index - AHB_BM0);
+ bit_rst = BIT(17 + bm_index - AHB_BM0);
+ break;
+ default:
+ return -EINVAL;
+ }
+ sci_glb_set(reg_rst, bit_rst);
+ sci_glb_clr(reg_rst, bit_rst);
+
+ if (is_enable)
+ sci_glb_set(reg_en, bit_en);
+ else
+ sci_glb_clr(reg_en, bit_en);
+ return SPRD_BM_SUCCESS;
+}
+
+static void __sci_bm_glb_count_enable(bool is_enable)
+{
+ ulong reg_val = 0;
+
+ reg_val = sci_glb_read(PUB_APB_BM_CNT_START, 0x1);
+ if (is_enable) {
+ if (!reg_val)
+ sci_glb_set(PUB_APB_BM_CNT_START, BIT(0));
+ } else {
+ sci_glb_clr(PUB_APB_BM_CNT_START, BIT(0));
+ }
+}
+
+static void __sci_bm_glb_ahb_disable(void)
+{
+ int bm_index;
+
+ for (bm_index = AHB_BM0; bm_index < BM_SIZE; bm_index++)
+ __sci_bm_glb_reset_and_enable(bm_index, false);
+}
+
+static int __sci_bm_get_chn_sel(u32 bm_index)
+{
+ ulong reg_val, chn_sel;
+
+ reg_val = sci_glb_read(AP_AHB_MISC_CFG, 0xFFFFFFFF);
+
+ switch (bm_index) {
+ case AHB_BM0:
+ reg_val &= ((ulong)0x3 << 4);
+ chn_sel = (reg_val >> 4) + AHB_BM0_CHN0;
+ break;
+ case AHB_BM1:
+ reg_val &= ((ulong)0x3 << 8);
+ chn_sel = (reg_val >> 8) + AHB_BM1_CHN0;
+ break;
+ case AHB_BM2:
+ reg_val &= ((ulong)0x3 << 10);
+ chn_sel = (reg_val >> 10) + AHB_BM2_CHN0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return chn_sel;
+}
+
+static int __sci_bm_chn_sel(u32 bm_index, u32 chn_id)
+{
+ ulong reg_val;
+
+ reg_val = sci_glb_read(AP_AHB_MISC_CFG, 0xffffffff);
+
+ switch (bm_index) {
+ case AHB_BM0:
+ reg_val &= ~(0x3 << 4);
+ reg_val |= (chn_id & 0x3) << 4;
+ break;
+ case AHB_BM1:
+ reg_val &= ~(0x3 << 8);
+ reg_val |= (chn_id & 0x3) << 8;
+ break;
+ case AHB_BM2:
+ reg_val &= ~(0x3 << 10);
+ reg_val |= (chn_id & 0x3) << 10;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sci_glb_write(AP_AHB_MISC_CFG, reg_val, 0xff0);
+
+ return SPRD_BM_SUCCESS;
+}
+
+static void __sci_bm_store_int_info(u32 bm_index)
+{
+ ulong reg_addr, mask_id;
+
+ reg_addr = __sci_get_bm_base(bm_index);
+ if(bm_ctn_dbg.bm_continue_dbg == false){
+ debug_bm_int_info[bm_index].bm_index = bm_index;
+ debug_bm_int_info[bm_index].msk_addr =
+ __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_ADDR_REG));
+ debug_bm_int_info[bm_index].msk_cmd =
+ __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_CMD_REG));
+ debug_bm_int_info[bm_index].msk_data_l =
+ __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_DATA_L_REG));
+ debug_bm_int_info[bm_index].msk_data_h =
+ __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_DATA_H_REG));
+ if(bm_index <= AXI_BM9){
+ mask_id = __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_ID_REG));
+ debug_bm_int_info[bm_index].msk_id = mask_id >> 2 ? 0 : mask_id;
+ }else{
+ mask_id = sci_glb_read(REG_AP_AHB_MISC_CFG, 0xFFFFFFFF);
+ debug_bm_int_info[bm_index].msk_id = 0;
+ }
+ BM_ERR("bm int info:\nBM CHN: %d\nOverlap ADDR: 0x%X\nOverlap CMD: 0x%X\nOverlap DATA: 0x%X 0x%X\nOverlap ID: %s--%ld\n",
+ debug_bm_int_info[bm_index].bm_index,
+ debug_bm_int_info[bm_index].msk_addr,
+ debug_bm_int_info[bm_index].msk_cmd,
+ debug_bm_int_info[bm_index].msk_data_l,
+ debug_bm_int_info[bm_index].msk_data_h,
+ bm_chn_name[bm_index].chn_name,
+ mask_id);
+ }else{
+ bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].bm_index = bm_index;
+ bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_addr =
+ __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_ADDR_REG));
+ bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_cmd =
+ __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_CMD_REG));
+ bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_data_l =
+ __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_DATA_L_REG));
+ bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_data_h =
+ __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_DATA_H_REG));
+ if(bm_index <= AXI_BM9){
+ mask_id = __raw_readl((volatile void *)(reg_addr + AXI_BM_MATCH_ID_REG));
+ bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_id = mask_id >> 2 ? 0 : mask_id;
+ }else{
+ mask_id = sci_glb_read(REG_AP_AHB_MISC_CFG, 0xFFFFFFFF);
+ bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_id = 0;
+ }
+
+ BM_ERR("bm ctn info:\nBM CHN: %d\nOverlap ADDR: 0x%X\nOverlap CMD: 0x%X\nOverlap DATA: 0x%X 0x%X\nOverlap ID: %s--%ld\n",
+ bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].bm_index,
+ bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_addr,
+ bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_cmd,
+ bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_data_l,
+ bm_ctn_dbg.bm_ctn_info[bm_ctn_dbg.current_cnt].msk_data_h,
+ bm_chn_name[bm_index].chn_name,
+ mask_id);
+ bm_ctn_dbg.current_cnt++;
+ }
+}
+
+static irqreturn_t __sci_bm_isr(int irq_num, void *dev)
+{
+ ulong bm_index, rwbw_cnt, bm_chn, bm_reg, bm_int;
+ struct bm_per_info *bm_info;
+ struct timeval tv;
+ struct rtc_time tm;
+
+ bm_info = (struct bm_per_info *)per_buf;
+ spin_lock(&bm_lock);
+
+ /*only one precess handle the bm's irq is this case */
+ if (!bm_irq_in_process) {
+ bm_irq_in_process = true;
+ } else {
+ spin_unlock(&bm_lock);
+ return IRQ_NONE;
+ }
+
+ bm_reg = __sci_get_bm_base(AXI_BM0);
+ if(__raw_readl((volatile void *)(bm_reg + AXI_BM_INTC_REG)) & BM_CNT_EN)
+ {
+ if(bm_info == NULL){
+ BM_ERR("BM irq ERR, trigger info: int 0x%x, min addr 0x%x, max addr 0x%x, cnt len 0x%x\n",
+ __raw_readl((volatile void *)(bm_reg + AXI_BM_INTC_REG)),
+ __raw_readl((volatile void *)(bm_reg + AXI_BM_ADDR_MIN_REG)),
+ __raw_readl((volatile void *)(bm_reg + AXI_BM_ADDR_MAX_REG)),
+ __raw_readl((volatile void *)(bm_reg + AXI_BM_CNT_WIN_LEN_REG)));
+ return IRQ_NONE;
+ }
+ __sci_axi_bm_cnt_stop();
+
+ rwbw_cnt = 0x0;
+ /*count stop time stamp */
+ bm_info[buf_write_index].t_stop = get_sys_cnt();//__raw_readl((const volatile void *)(syscnt + 0xc));//((const volatile void *)(SPRD_SYSCNT_BASE + 0xc));
+ bm_info[buf_write_index].count = bm_count;
+
+ for (bm_chn = AXI_BM0; bm_chn <= AXI_BM9; bm_chn++) {
+ bm_reg = __sci_get_bm_base(bm_chn);
+ bm_info[buf_write_index].per_data[bm_chn][0] =
+ __raw_readl((volatile void *)(bm_reg + AXI_BM_RTRANS_IN_WIN_REG));
+ bm_info[buf_write_index].per_data[bm_chn][1] =
+ __raw_readl((volatile void *)(bm_reg + AXI_BM_RBW_IN_WIN_REG));
+ bm_info[buf_write_index].per_data[bm_chn][2] =
+ __raw_readl((volatile void *)(bm_reg + AXI_BM_RLATENCY_IN_WIN_REG));
+
+ bm_info[buf_write_index].per_data[bm_chn][3] =
+ __raw_readl((volatile void *)(bm_reg + AXI_BM_WTRANS_IN_WIN_REG));
+ bm_info[buf_write_index].per_data[bm_chn][4] =
+ __raw_readl((volatile void *)(bm_reg + AXI_BM_WBW_IN_WIN_REG));
+ bm_info[buf_write_index].per_data[bm_chn][5] =
+ __raw_readl((volatile void *)(bm_reg + AXI_BM_WLATENCY_IN_WIN_REG));
+
+ rwbw_cnt += bm_info[buf_write_index].per_data[bm_chn][1];
+ rwbw_cnt += bm_info[buf_write_index].per_data[bm_chn][4];
+ }
+ if (++buf_write_index == PER_COUNT_RECORD_SIZE) {
+ buf_write_index = 0;
+ buf_skip_cnt++;
+ }
+
+ /*wake up the thread to output log per 4 second*/
+ if ((buf_write_index == 0) ||
+ buf_write_index == (PER_COUNT_RECORD_SIZE >> 1) ) {
+ /*need to skip the star buf, because it includes a lot of usefull info,skip 8s buf*/
+ if(buf_skip_cnt > 1)
+ up(&bm_seam);
+ }
+ __sci_bm_int_clr();
+ __sci_axi_bm_cnt_clr();
+ __sci_axi_bm_set_winlen();
+
+ /*count start time stamp */
+ do_gettimeofday(&tv);
+ rtc_time_to_tm(tv.tv_sec,&tm);
+ tm.tm_hour = tm.tm_hour < 16 ? (tm.tm_hour + 8) : (tm.tm_hour - 16);
+ bm_info[buf_write_index].t_start = get_sys_cnt();//__raw_readl((const volatile void *)(syscnt + 0xc));//((const volatile void *)(SPRD_SYSCNT_BASE + 0xc));
+ bm_info[buf_write_index].tmp1 = (tm.tm_hour * 10000) + (tm.tm_min * 100) + tm.tm_sec;//emc_clk_get();
+ bm_info[buf_write_index].tmp2 = 640;//__raw_readl(REG_AON_APB_DPLL_CFG);
+
+ __sci_axi_bm_cnt_start();
+
+ bm_irq_in_process = false;
+ spin_unlock(&bm_lock);
+
+ return IRQ_HANDLED;
+ }
+
+ for(bm_index = AXI_BM0; bm_index < BM_SIZE; bm_index++)
+ {
+ bm_reg = __sci_get_bm_base(bm_index);
+ if(bm_index < AHB_BM0)
+ bm_int = __raw_readl((volatile void *)(bm_reg + AXI_BM_INTC_REG));
+ else
+ bm_int = __raw_readl((volatile void *)(bm_reg + AHB_BM_INTC_REG));
+ if (bm_int & BM_INT_MSK_STS) {
+ __sci_bm_store_int_info(bm_index);
+ if((bm_ctn_dbg.bm_continue_dbg == true) && (bm_ctn_dbg.current_cnt <= BM_CONTINUE_DEBUG_SIZE)){
+ bm_int &= ~BM_INT_EN;
+ bm_int |= (BM_INT_CLR | BM_INT_EN);
+ }else{
+ bm_int &= ~BM_INT_EN;
+ }
+ __raw_writel(bm_int, (volatile void *)bm_reg);
+ if (bm_callback_set[bm_index].fun)
+ bm_callback_set[bm_index].fun(bm_callback_set[bm_index].data);
+ if(bm_st_info.bm_stack_st == true){
+ BM_ERR("Bus Monitor output stack!\n");
+ dump_stack();
+ }
+ if(true == bm_st_info.bm_panic_st){
+ BM_ERR("Bus Monitor enter panic!\n");
+ BUG();
+ }
+ }
+ }
+ bm_irq_in_process = false;
+ spin_unlock(&bm_lock);
+
+ return IRQ_HANDLED;
+}
+
+unsigned int dmc_mon_cnt_bw(void)
+{
+ int chn;
+ ulong cnt = 0;
+
+ if(bm_st_info.bm_dbg_st != true){
+ if(true == bm_st_info.bm_dfs_off_st)
+ return 0xFFFFFFFF;
+ for (chn = AXI_BM0; chn <= AXI_BM9; chn++)
+ cnt += __sci_axi_bm_chn_cnt_bw(chn);
+ return cnt;
+ }else
+ return 0xFFFFFFFF;
+}
+
+void dmc_mon_cnt_clr(void)
+{
+ if(bm_st_info.bm_dbg_st != true)
+ __sci_axi_bm_cnt_clr();
+ return;
+}
+
+void dmc_mon_cnt_start(void)
+{
+ if(bm_st_info.bm_dbg_st != true){
+ __sci_axi_bm_cnt_start();
+ __sci_bm_glb_count_enable(true);
+ __sci_axi_bm_cnt_start();
+ }
+ return;
+}
+
+void dmc_mon_cnt_stop(void)
+{
+ if(bm_st_info.bm_dbg_st != true){
+ __sci_bm_glb_count_enable(false);
+ __sci_axi_bm_cnt_stop();
+ }
+ return;
+}
+
+void dmc_mon_resume(void)
+{
+ return;
+}
+
+int sci_bm_set_point(enum sci_bm_index bm_index, enum sci_bm_chn chn,
+ const struct sci_bm_cfg *cfg, void(*call_back)(void *), void *data)
+{
+ int ret;
+ u32 ddr_size = 0;
+ struct device_node *np = NULL;
+ struct resource res;
+ volatile struct sci_bm_reg *bm_reg;
+
+ bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_index);
+ /*clean the irq status*/
+ bm_reg->intc |= BM_INT_CLR;
+ bm_reg->intc = 0x0;
+ memset((void *)bm_reg, 0x0, sizeof(*bm_reg));
+
+#ifdef CONFIG_OF
+ np = of_find_node_by_name(NULL, "memory");
+ if(np){
+ ret = of_address_to_resource(np, 0, &res);
+ if(!ret)
+ ddr_size = res.end - res.start;
+ }
+ /* if monitor ddr addr, it needs addr wrap*/
+ if(cfg->addr_min >= 0x80000000 && "ERROR_NAME" != bm_chn_name[bm_index].chn_name){
+ if(ddr_size == 0x1fffffff)//512M
+ bm_store_vale[bm_index].bm_mask = 0xE0000000;
+ else if(ddr_size == 0x3fffffff)//1G
+ bm_store_vale[bm_index].bm_mask = 0xC0000000;
+ else// >= 4G, do not need mask
+ bm_store_vale[bm_index].bm_mask = 0x00000000;
+ }else
+ bm_store_vale[bm_index].bm_mask = 0x00000000;
+#endif
+
+ if ("ERROR_NAME" != bm_chn_name[bm_index].chn_name){
+ if (bm_index < AHB_BM0) {
+ bm_reg->addr_min = cfg->addr_min & (~bm_store_vale[bm_index].bm_mask);
+ bm_reg->addr_max = cfg->addr_max & (~bm_store_vale[bm_index].bm_mask);
+ bm_reg->addr_msk = bm_store_vale[bm_index].bm_mask;
+
+ /* the interrupt just trigger by addr range for axi
+ * busmonitor, so set the data range difficult to
+ * access.
+ */
+ if (bm_chn_name[bm_index].chn_type) {
+ bm_reg->data_min_l = 0x0fffffff;
+ bm_reg->data_min_h = 0x0fffffff;
+ bm_reg->data_max_l = 0x0;
+ bm_reg->data_max_h = 0x0;
+ bm_reg->data_msk_l = 0x0;
+ bm_reg->data_msk_h = 0x0;
+ } else {
+ bm_reg->data_min_l = 0x0;
+ bm_reg->data_min_h = 0x0;
+ bm_reg->data_max_l = 0xffffffff;
+ bm_reg->data_max_h = 0xffffffff;
+ bm_reg->data_msk_l = 0x0;
+ bm_reg->data_msk_h = 0x0;
+ }
+
+ switch (cfg->bm_mode) {
+ case R_MODE:
+ bm_reg->cfg = 0x1;
+ break;
+ case W_MODE:
+ bm_reg->cfg = 0x3;
+ break;
+ case RW_MODE:
+ bm_reg->cfg = 0x0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ bm_reg->intc |= BM_INT_EN | BM_CHN_EN;
+
+ } else {
+ bm_reg->addr_min = cfg->addr_min & (~bm_store_vale[bm_index].bm_mask);
+ bm_reg->addr_max = cfg->addr_max & (~bm_store_vale[bm_index].bm_mask);
+ bm_reg->addr_msk = bm_store_vale[bm_index].bm_mask;
+ bm_reg->data_min_l = cfg->data_min_l;
+ bm_reg->data_min_h = cfg->data_min_h;
+ bm_reg->data_max_l = cfg->data_max_l;
+ bm_reg->data_max_h = cfg->data_max_h;
+ bm_reg->data_msk_l = 0x0;
+ bm_reg->data_msk_h = 0x0;
+
+ switch (cfg->bm_mode) {
+ case R_MODE:
+ bm_reg->cfg = 0x1;
+ break;
+ case W_MODE:
+ bm_reg->cfg = 0x3;
+ break;
+ case RW_MODE:
+ bm_reg->cfg = 0x0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = __sci_bm_chn_sel(bm_index, chn);
+ if (ret < 0)
+ return ret;
+
+ bm_reg->intc |= BM_INT_EN | BM_CHN_EN;
+ }
+ }
+
+ bm_callback_set[bm_index].fun = call_back;
+ bm_callback_set[bm_index].data = data;
+
+ return SPRD_BM_SUCCESS;
+}
+
+void sci_bm_unset_point(enum sci_bm_index bm_index)
+{
+ ulong reg_addr;
+
+ reg_addr = __sci_get_bm_base(bm_index);
+
+ __raw_writel(BM_INT_CLR | BM_CHN_EN, (volatile void *)reg_addr);
+
+ __raw_writel(0x0, (volatile void *)reg_addr);
+
+ bm_callback_set[bm_index].fun = NULL;
+}
+
+void sci_bm_set_perform_point(void)
+{
+ __sci_bm_init();
+ __sci_axi_bm_cnt_clr();
+ __sci_bm_int_clr();
+
+ __sci_axi_bm_set_winlen();
+ __sci_bm_glb_count_enable(false);
+ __sci_axi_bm_cnt_start();
+ __sci_bm_glb_count_enable(true);
+}
+
+static void sci_bm_def_val_set_by_dts(void)
+{
+ u32 bm_chn, ret;
+ struct sci_bm_cfg bm_cfg;
+ //default set is monitor CP write AP memory.
+ for(bm_chn = AXI_BM0; bm_chn <= AXI_BM9; bm_chn++){
+ if(!strcmp(bm_chn_name[bm_chn].chn_name, "CPU") ||
+ !strcmp(bm_chn_name[bm_chn].chn_name, "DISP") ||
+ !strcmp(bm_chn_name[bm_chn].chn_name, "GPU") ||
+ !strcmp(bm_chn_name[bm_chn].chn_name, "AP/ZIP") ||
+ !strcmp(bm_chn_name[bm_chn].chn_name, "ZIP") ||
+ !strcmp(bm_chn_name[bm_chn].chn_name, "AP") ||
+ !strcmp(bm_chn_name[bm_chn].chn_name, "MM") ||
+ !strcmp(bm_chn_name[bm_chn].chn_name, "ERROR_NAME"))
+ continue;
+ bm_cfg.addr_min = memory_layout.ap_start3;
+ bm_cfg.addr_max = memory_layout.ap_end3;
+ bm_cfg.bm_mode = W_MODE;
+ ret = sci_bm_set_point(bm_chn, CHN0, &bm_cfg, NULL, NULL);
+ if(SPRD_BM_SUCCESS != ret)
+ return;
+ }
+}
+
+static void sci_bm_def_val_set(void)
+{
+ u32 bm_chn, ret;
+ struct sci_bm_cfg bm_cfg;
+
+ for (bm_chn = AXI_BM0; bm_chn < BM_SIZE; bm_chn++){
+ if((0x00000000 == bm_def_value[bm_chn].str_addr) && (0x00000000 == bm_def_value[bm_chn].end_addr))
+ continue;
+ bm_cfg.addr_min = bm_def_value[bm_chn].str_addr;
+ bm_cfg.addr_max = bm_def_value[bm_chn].end_addr;
+ bm_cfg.bm_mode = bm_def_value[bm_chn].mode;
+ ret = sci_bm_set_point(bm_chn, bm_def_value[bm_chn].chn_sel, &bm_cfg, NULL, NULL);
+ if(SPRD_BM_SUCCESS != ret)
+ return;
+ }
+}
+
+static int sci_bm_output_log(void *p)
+{
+ mm_segment_t old_fs;
+ int ret;
+
+ while (1) {
+ down(&bm_seam);
+
+ if (!log_file) {
+ log_file = filp_open(LOG_FILE_PATH, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (IS_ERR(log_file) || !log_file || !log_file->f_dentry) {
+ BM_ERR("file_open(%s) for create failed\n", LOG_FILE_PATH);
+ return -ENODEV;
+ }
+ }
+ switch (buf_write_index) {
+ case 0:
+ buf_read_index = PER_COUNT_RECORD_SIZE >> 1;
+ break;
+ case (PER_COUNT_RECORD_SIZE >> 1):
+ buf_read_index = 0x0;
+ break;
+ default:
+ BM_ERR("get buf_read_indiex failed!\n");
+ }
+ old_fs = get_fs();
+ set_fs(get_ds());
+ ret = vfs_write(log_file,
+ (const char *)(per_buf + buf_read_index * sizeof(struct bm_per_info)),
+ sizeof(struct bm_per_info) *(PER_COUNT_RECORD_SIZE >> 1),
+ &log_file->f_pos);
+
+ set_fs(old_fs);
+
+ /*raw back file write*/
+ if (log_file->f_pos >= (sizeof(struct bm_per_info) * LOG_FILE_MAX_RECORDS)) {
+ log_file->f_pos = 0x0;
+ }
+ }
+
+ filp_close(log_file, NULL);
+
+ return 0;
+}
+
+static ssize_t sci_bm_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if(bm_st_info.bm_dbg_st == true)
+ return sprintf(buf, "Bus Monitor in debug mode!\n");
+ else
+ return sprintf(buf, "Bus Monitor in bandwidth mode!\n");
+}
+
+static ssize_t sci_bm_chn_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 bm_index;
+ char occ_info[30] = {};
+ char chn_info[30*BM_CHANNEL_SIZE] = {};
+
+ for(bm_index = AXI_BM0; bm_index < BM_CHANNEL_SIZE; bm_index++){
+ sprintf(occ_info, "%s\n", bm_chn_name[bm_index].chn_name);
+ strcat(chn_info, occ_info);
+ }
+ return sprintf(buf, "%s\n", chn_info);
+}
+
+static ssize_t sci_bm_axi_dbg_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 bm_index;
+ ulong reg_addr, str_addr, end_addr;
+ char chn_info[58];
+ char info_buf[58*10] = {};
+
+ for(bm_index = AXI_BM0; bm_index < AHB_BM0; bm_index++){
+ reg_addr = __sci_get_bm_base(bm_index);
+ str_addr = __raw_readl((volatile void *)(reg_addr + AXI_BM_ADDR_MIN_REG)) + (0x80000000 & bm_store_vale[bm_index].bm_mask);
+ end_addr = __raw_readl((volatile void *)(reg_addr + AXI_BM_ADDR_MAX_REG)) + (0x80000000 & bm_store_vale[bm_index].bm_mask);
+ sprintf(chn_info, "%d 0x%08lX 0x%08lX %s\n", bm_index, str_addr, end_addr, bm_chn_name[bm_index].chn_name);
+ strcat(info_buf, chn_info);
+ }
+ BM_INFO("%s\n", info_buf);
+ return sprintf(buf, "%s\n", info_buf);
+}
+
+static ssize_t sci_bm_axi_dbg_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ unsigned char start[12], end[12], chn[6], mod[3];
+ unsigned long start_addr, end_addr, channel, bm_index;
+ struct sci_bm_cfg bm_cfg;
+ u32 rd_wt;
+ int ret,i;
+
+ sscanf(buf, "%s %s %s %s",chn, start, end, mod);
+
+ ret = strict_strtoul(start, 0, &start_addr);
+ if (ret)
+ BM_ERR("start %s is not in hex or decimal form.\n", buf);
+ ret = strict_strtoul(end, 0, &end_addr);
+ if (ret)
+ BM_ERR("end %s is not in hex or decimal form.\n", buf);
+ if((chn[0] >= '0') && (chn[0] <= '9'))
+ channel = chn[0] - '0';
+ else if(strcmp(chn, "all") == 0)
+ channel = BM_DEBUG_ALL_CHANNEL;
+ else{
+ BM_ERR("please input a legal channel number! e.g: 0-9,all.");
+ return EINVAL;
+ }
+ if((strcmp(mod, "r") == 0)||(strcmp(mod, "R") == 0))
+ rd_wt = R_MODE;
+ else if((strcmp(mod, "w") == 0)||(strcmp(mod, "W") == 0))
+ rd_wt = W_MODE;
+ else if((strcmp(mod, "rw") == 0)||(strcmp(mod, "RW") == 0))
+ rd_wt = RW_MODE;
+ else{
+ BM_ERR("please input a legal channel mode! e.g: r,w,rw");
+ return EINVAL;
+ }
+
+ BM_INFO("str addr 0x%lx end addr 0x%lx chn %ld rw %d\n", start_addr, end_addr, channel, rd_wt);
+ if(((channel > AXI_BM9) && (channel != BM_DEBUG_ALL_CHANNEL)) || (rd_wt > RW_MODE) || (start_addr > end_addr))
+ return -EINVAL;
+
+ /*for (bm_index = AXI_BM0; bm_index <= AXI_BM9; bm_index++)
+ __sci_bm_glb_reset_and_enable(bm_index, true);*/
+ bm_st_info.bm_dbg_st = true;
+ bm_st_info.bm_dfs_off_st = true;
+
+ if(channel != BM_DEBUG_ALL_CHANNEL){
+ bm_cfg.addr_min = (u32)start_addr;
+ bm_cfg.addr_max = (u32)end_addr;
+ bm_cfg.bm_mode = rd_wt;
+ ret = sci_bm_set_point(channel, CHN0, &bm_cfg, NULL, NULL);
+ if(SPRD_BM_SUCCESS != ret)
+ return ret;
+ }else{
+ bm_cfg.addr_min = (u32)start_addr;
+ bm_cfg.addr_max = (u32)end_addr;
+ bm_cfg.bm_mode = rd_wt;
+ for(i = AXI_BM0; i < AHB_BM0; i++){
+ ret = sci_bm_set_point(i, CHN0, &bm_cfg, NULL, NULL);
+ if(SPRD_BM_SUCCESS != ret)
+ return ret;
+ }
+ }
+ return strnlen(buf, count);
+}
+
+static ssize_t sci_bm_ahb_dbg_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 bm_index, chn_sel;
+ ulong reg_addr, str_addr, end_addr, str_data, end_data;
+ char chn_info[84];
+ char info_buf[84*3] = {};
+
+ for(bm_index = AHB_BM0; bm_index < BM_SIZE; bm_index++){
+ reg_addr = __sci_get_bm_base(bm_index);
+ str_addr = __raw_readl((volatile void *)(reg_addr + AXI_BM_ADDR_MIN_REG)) + (0x80000000 & bm_store_vale[bm_index].bm_mask);
+ end_addr = __raw_readl((volatile void *)(reg_addr + AXI_BM_ADDR_MAX_REG)) + (0x80000000 & bm_store_vale[bm_index].bm_mask);
+ str_data = __raw_readl((volatile void *)(reg_addr + AXI_BM_DATA_MIN_L_REG));
+ end_data = __raw_readl((volatile void *)(reg_addr + AXI_BM_DATA_MAX_L_REG));
+ chn_sel = __sci_bm_get_chn_sel(bm_index);
+ sprintf(chn_info, "%d 0x%08lX 0x%08lX 0x%08lX 0x%08lX %s\n", bm_index-10, str_addr, end_addr,
+ str_data, end_data, bm_chn_name[chn_sel].chn_name);
+ strcat(info_buf, chn_info);
+ }
+ BM_INFO("%s\n", info_buf);
+ return sprintf(buf, "%s\n", info_buf);
+}
+
+static ssize_t sci_bm_ahb_dbg_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ unsigned char addr_start[12], data_min[12];
+ unsigned char addr_end[12], data_max[12];
+ unsigned long start_addr, end_addr, min_data, max_data, channel, chn_sel, bm_index;
+ struct sci_bm_cfg bm_cfg;
+ int ret;
+
+ sscanf(buf, "%ld %ld %s %s %s %s", &channel, &chn_sel, addr_start, addr_end, data_min, data_max);
+
+ ret = strict_strtoul(addr_start, 0, &start_addr);
+ if (ret)
+ BM_ERR("start addr %s is not in hex or decimal form.\n", buf);
+ ret = strict_strtoul(addr_end, 0, &end_addr);
+ if (ret)
+ BM_ERR("end addr %s is not in hex or decimal form.\n", buf);
+ ret = strict_strtoul(data_min, 0, &min_data);
+ if (ret)
+ BM_ERR("start data %s is not in hex or decimal form.\n", buf);
+ ret = strict_strtoul(data_max, 0, &max_data);
+ if (ret)
+ BM_ERR("end data %s is not in hex or decimal form.\n", buf);
+
+ BM_INFO("str addr 0x%lX end addr 0x%lX min data 0x%lX max data 0x%lX\n", start_addr, end_addr, min_data, max_data);
+ if((channel > 3) || (chn_sel > 4) || (start_addr > end_addr) || (min_data > max_data))
+ return -EINVAL;
+
+ __sci_bm_glb_reset_and_enable(channel + AHB_BM0, true);
+ bm_st_info.bm_dbg_st = true;
+ bm_st_info.bm_dfs_off_st = true;
+
+ bm_cfg.addr_min = (u32)start_addr;
+ bm_cfg.addr_max = (u32)end_addr;
+ bm_cfg.data_min_l = (u32)min_data;
+ bm_cfg.data_min_h = 0x0;
+ bm_cfg.data_max_l = (u32)max_data;
+ bm_cfg.data_max_h = 0x0;
+ bm_cfg.bm_mode = W_MODE;
+ ret = sci_bm_set_point(channel + AHB_BM0, chn_sel, &bm_cfg, NULL, NULL);
+ if(SPRD_BM_SUCCESS != ret)
+ return ret;
+ return strnlen(buf, count);
+}
+
+static ssize_t sci_bm_bandwidth_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ u32 bw_en, bm_index;
+ struct task_struct *t;
+
+ bm_st_info.bm_dfs_off_st = false;
+ sscanf(buf, "%d", &bw_en);
+ if(bw_en){
+ BM_INFO("bm bandwidth mode enable!!!\n");
+ bm_st_info.bm_dbg_st = false;
+ if(per_buf == NULL){
+ per_buf = kmalloc(PER_COUNT_BUF_SIZE, GFP_KERNEL);
+ if (!per_buf)
+ BM_ERR("kmalloc failed!\n");
+ sema_init(&bm_seam, 0);
+ t = kthread_run(sci_bm_output_log, NULL, "%s", "bm_per_log");
+ if (IS_ERR(t)) {
+ BM_ERR("bm probe: Failed to run thread bm_per_log\n");
+ kthread_stop(t);
+ return 0;
+ }
+ }
+ for (bm_index = AXI_BM0; bm_index <= AHB_BM2; bm_index++) {
+ __sci_bm_glb_reset_and_enable(bm_index, true);
+ bm_store_vale[bm_index].bm_mask= 0;
+ }
+ sci_bm_set_perform_point();
+ msleep(100);
+ }else{
+ BM_INFO("bm bandwidth mode disable!!!\n");
+ bm_st_info.bm_dbg_st = true;
+ if(per_buf != NULL){
+ kfree(per_buf);
+ per_buf = NULL;
+ if(log_file != NULL){
+ filp_close(log_file, NULL);
+ log_file = NULL;
+ buf_write_index = 0;
+ bm_count = 0;
+ }
+ }
+ __sci_axi_bm_cnt_clr();
+ __sci_bm_int_clr();
+ __sci_bm_glb_count_enable(false);
+ for (bm_index = AXI_BM0; bm_index <= AHB_BM2; bm_index++) {
+ __sci_bm_glb_reset_and_enable(bm_index, false);
+ bm_store_vale[bm_index].bm_mask= 0;
+ }
+ __sci_bm_glb_count_enable(false);
+ }
+ return strnlen(buf, count);
+}
+
+static ssize_t sci_bm_occur_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 bm_index;
+ char occ_info[116] = {};
+ char chn_info[116*21] = {};
+
+ for(bm_index = AXI_BM0; bm_index < BM_SIZE; bm_index++){
+ if(debug_bm_int_info[bm_index].msk_addr != 0){
+ sprintf(occ_info, " %s\n addr: 0x%X\n CMD: 0x%X\n msk_data_l: 0x%X\n msk_data_h: 0x%X\n id 0x%X\n",
+ bm_chn_name[bm_index].chn_name,
+ debug_bm_int_info[bm_index].msk_addr,
+ debug_bm_int_info[bm_index].msk_cmd,
+ debug_bm_int_info[bm_index].msk_data_l,
+ debug_bm_int_info[bm_index].msk_data_h,
+ debug_bm_int_info[bm_index].msk_id);
+ strcat(chn_info, occ_info);
+ }
+ }
+ if(chn_info[1] == 0)
+ sprintf(chn_info, ":-) ! No action was monitored by BM!!!\n");
+
+ return sprintf(buf, "%s\n", chn_info);
+}
+
+static ssize_t sci_bm_continue_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 bm_index;
+ char occ_info[96] = {};
+ char chn_info[96*50] = {};
+
+ for(bm_index = 0; bm_index < bm_ctn_dbg.loop_cnt; bm_index++){
+ if(bm_ctn_dbg.bm_ctn_info[bm_index].msk_addr != 0){
+ sprintf(occ_info, " %d\n addr: 0x%X\n CMD: 0x%X\n msk_data_l: 0x%X\n msk_data_h: 0x%X\n id 0x%X\n",
+ bm_ctn_dbg.bm_ctn_info[bm_index].bm_index,
+ bm_ctn_dbg.bm_ctn_info[bm_index].msk_addr,
+ bm_ctn_dbg.bm_ctn_info[bm_index].msk_cmd,
+ bm_ctn_dbg.bm_ctn_info[bm_index].msk_data_l,
+ bm_ctn_dbg.bm_ctn_info[bm_index].msk_data_h,
+ bm_ctn_dbg.bm_ctn_info[bm_index].msk_id);
+ strcat(chn_info, occ_info);
+ }
+ }
+ if(chn_info[1] == 0)
+ sprintf(chn_info, ":-) ! No action was monitored by BM!!!\n");
+
+ return sprintf(buf, "%s\n", chn_info);
+}
+
+static ssize_t sci_bm_continue_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ u32 ctn_num;
+ sscanf(buf, "%d", &ctn_num);
+ if(ctn_num){
+ BM_INFO("Set BM support continue debug!!!\n");
+ bm_ctn_dbg.bm_continue_dbg = true;
+ if(ctn_num > BM_CONTINUE_DEBUG_SIZE)
+ bm_ctn_dbg.loop_cnt = BM_CONTINUE_DEBUG_SIZE;
+ else
+ bm_ctn_dbg.loop_cnt = ctn_num;
+ }else{
+ BM_INFO("Set BM do not support continue debug!!!\n");
+ bm_ctn_dbg.bm_continue_dbg = false;
+ bm_ctn_dbg.loop_cnt = 0;
+ }
+ return strnlen(buf, count);
+}
+
+static ssize_t sci_bm_dfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if(bm_st_info.bm_dfs_off_st == true)
+ return sprintf(buf, "The BM DFS is closed.\n");
+ else
+ return sprintf(buf, "The BM DFS is open.\n");
+}
+
+static ssize_t sci_bm_dfs_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ u32 dfs_flg;
+ sscanf(buf, "%d", &dfs_flg);
+ if(dfs_flg){
+ BM_INFO("Disable BM DFS.\n");
+ bm_st_info.bm_dfs_off_st = true;
+ }else{
+ BM_INFO("Reopen BM DFS.\n");
+ bm_st_info.bm_dfs_off_st = false;
+ }
+ return strnlen(buf, count);
+}
+
+static ssize_t sci_bm_panic_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if(bm_st_info.bm_panic_st == true)
+ return sprintf(buf, "The BM panic is open.\n");
+ else
+ return sprintf(buf, "The BM panic is close.\n");
+}
+
+static ssize_t sci_bm_panic_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ u32 panic_flg;
+ sscanf(buf, "%d", &panic_flg);
+ if(panic_flg){
+ BM_INFO("Reopen BM panic.\n");
+ bm_st_info.bm_panic_st = true;
+ }else{
+ BM_INFO("Disable BM panic.\n");
+ bm_st_info.bm_panic_st = false;
+ }
+ return strnlen(buf, count);
+}
+
+static ssize_t sci_bm_stack_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if(bm_st_info.bm_stack_st == true)
+ return sprintf(buf, "The BM stack is open.\n");
+ else
+ return sprintf(buf, "The BM stack is close.\n");
+}
+
+static ssize_t sci_bm_stack_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ u32 panic_flg;
+ sscanf(buf, "%d", &panic_flg);
+ if(panic_flg){
+ BM_INFO("Reopen BM DFS.\n");
+ bm_st_info.bm_stack_st = true;
+ }else{
+ BM_INFO("Disable BM DFS.\n");
+ bm_st_info.bm_stack_st = false;
+ }
+ return strnlen(buf, count);
+}
+
+static ssize_t sci_bm_disable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ u32 dis_val;
+ sscanf(buf, "%d", &dis_val);
+ if(dis_val){
+ BM_INFO("Set BM disable count %d\n", bm_count);
+ __raw_writel(0x0, (volatile void *)(REG_PUB_APB_BUSMON_CFG));
+ }else{
+ bm_count++;
+ BM_INFO("Set BM enable count %d\n", bm_count);
+ __raw_writel(BM_CHANNEL_ENABLE_FLAGE, (volatile void *)(REG_PUB_APB_BUSMON_CFG));
+ }
+ return strnlen(buf, count);
+}
+
+static ssize_t sci_bm_bus_status_show(struct device * dev,
+ struct device_attribute * attr,const char * buf)
+{
+ u32 bm_index;
+ ulong reg_addr, axi_val;
+ char axi_info[20] = {0}, axi_buf[10*20] = {0};
+
+ for(bm_index = AXI_BM0; bm_index < AHB_BM0; bm_index++){
+ reg_addr = __sci_get_bm_base(bm_index);
+ axi_val = __raw_readl((volatile void *)(reg_addr + AXI_BM_BUS_STATUS_REG));
+ sprintf(axi_info, "0x%08lX\n", axi_val);
+ strcat(axi_buf, axi_info);
+ }
+ BM_INFO("%s\n", axi_buf);
+ return sprintf(buf, "%s\n", axi_buf);
+}
+
+static DEVICE_ATTR(state, S_IRUGO | S_IWUSR,
+ sci_bm_state_show, NULL);
+
+static DEVICE_ATTR(chn, S_IRUGO | S_IWUSR,
+ sci_bm_chn_show, NULL);
+
+static DEVICE_ATTR(axi_dbg, S_IRUGO | S_IWUSR,
+ sci_bm_axi_dbg_show, sci_bm_axi_dbg_store);
+
+static DEVICE_ATTR(ahb_dbg, S_IRUGO | S_IWUSR,
+ sci_bm_ahb_dbg_show, sci_bm_ahb_dbg_store);
+
+static DEVICE_ATTR(bandwidth, S_IRUGO | S_IWUSR,
+ NULL, sci_bm_bandwidth_store);
+
+static DEVICE_ATTR(occur, S_IRUGO | S_IWUSR,
+ sci_bm_occur_show, NULL);
+
+static DEVICE_ATTR(continue, S_IRUGO | S_IWUSR,
+ sci_bm_continue_show, sci_bm_continue_store);
+
+static DEVICE_ATTR(dfs_off, S_IRUGO | S_IWUSR,
+ sci_bm_dfs_show, sci_bm_dfs_store);
+
+static DEVICE_ATTR(panic, S_IRUGO | S_IWUSR,
+ sci_bm_panic_show, sci_bm_panic_store);
+
+static DEVICE_ATTR(stack, S_IRUGO | S_IWUSR,
+ sci_bm_stack_show, sci_bm_stack_store);
+
+static DEVICE_ATTR(disable, S_IRUGO | S_IWUSR,
+ NULL, sci_bm_disable_store);
+
+static DEVICE_ATTR(bus_status, S_IRUGO | S_IWUSR,
+ sci_bm_bus_status_show, NULL);
+
+
+static struct attribute *bm_attrs[] = {
+ &dev_attr_state.attr,
+ &dev_attr_chn.attr,
+ &dev_attr_axi_dbg.attr,
+ &dev_attr_ahb_dbg.attr,
+ &dev_attr_bandwidth.attr,
+ &dev_attr_occur.attr,
+ &dev_attr_continue.attr,
+ &dev_attr_dfs_off.attr,
+ &dev_attr_panic.attr,
+ &dev_attr_stack.attr,
+ &dev_attr_disable.attr,
+ &dev_attr_bus_status.attr,
+ NULL,
+};
+
+static struct attribute_group bm_attr_group = {
+ .attrs = bm_attrs,
+};
+
+#define BM_MAX(x, y) (x > y ? x : y)
+static int sci_bm_get_mem_layout(void)
+{
+ struct device_node *np = NULL;
+ struct resource res;
+ int ret;
+ char *devname;
+
+ np = of_find_node_by_name(NULL, "memory");
+ if(np){
+ ret = of_address_to_resource(np, 0, &res);
+ if(!ret){
+ memory_layout.dram_start = res.start;
+ memory_layout.dram_end = res.end;
+ }
+ }
+
+ np = of_find_compatible_node(NULL, NULL, "sprd,sipc");
+ if(np){
+ ret = of_address_to_resource(np, 0, &res);
+ if(!ret){
+ memory_layout.shm_start = res.start;
+ memory_layout.shm_end = res.end;
+ }
+ }
+
+ for_each_compatible_node(np, NULL, "sprd,scproc"){
+ if(np){
+ of_property_read_string(np, "sprd,name", (const char **)&devname);
+ if(!strcmp(devname, "cpw")){
+ ret = of_address_to_resource(np, 0, &res);
+ if(!ret){
+ memory_layout.cpw_start = res.start;
+ memory_layout.cpw_end = res.end;
+ }
+ }else if(!strcmp(devname, "cpgge")){
+ ret = of_address_to_resource(np, 0, &res);
+ if(!ret){
+ memory_layout.cpgge_start = res.start;
+ memory_layout.cpgge_end = res.end;
+ }
+ }else if(!strcmp(devname, "cpwcn")){
+ ret = of_address_to_resource(np, 0, &res);
+ if(!ret){
+ memory_layout.cpwcn_start = res.start;
+ memory_layout.cpwcn_end = res.end;
+ }
+ }else if(!strcmp(devname, "cptl")){
+ ret = of_address_to_resource(np, 0, &res);
+ if(!ret){
+ memory_layout.cptl_start = res.start;
+ memory_layout.cptl_end = res.end;
+ }
+ }
+ }
+ }
+
+ np = of_find_compatible_node(NULL, NULL, "sprd,sprdfb");
+ if(np){
+ u32 val[2] = { 0 };
+ ret = of_property_read_u32_array(np, "sprd,fb_mem", val, 2);
+ if(!ret){
+ memory_layout.fb_start = val[0];
+ memory_layout.fb_end = val[0] + val[1] - 1;
+ }
+ }
+
+ np = of_find_compatible_node(NULL, NULL, "sprd,ion-sprd");
+ if(np){
+ struct device_node *child = NULL;
+ char *reg_name = NULL;
+ u32 val[2] = { 0 };
+
+ for_each_child_of_node(np, child){
+ of_property_read_string(child, "reg-names", (const char **)&reg_name);
+ if(!strcmp("ion_heap_carveout_overlay", reg_name)){
+ ret = of_property_read_u32_array(child, "sprd,ion-heap-mem", val, 2);
+ if(!ret){
+ memory_layout.ion_start = val[0];
+ memory_layout.ion_end= val[0] + val[1] - 1;
+ }
+ }
+ }
+ }
+ memory_layout.ap_start3 = BM_MAX(memory_layout.cpw_end, memory_layout.cpgge_end);
+ memory_layout.ap_start3 = BM_MAX(memory_layout.ap_start3, memory_layout.cpwcn_end);
+ memory_layout.ap_start3 = BM_MAX(memory_layout.ap_start3, memory_layout.cptl_end) + 1;
+
+ memory_layout.ap_start1 = memory_layout.dram_start;
+ memory_layout.ap_end1 = memory_layout.shm_start - 1;
+ memory_layout.ap_start2 = memory_layout.shm_end + 1;
+ memory_layout.ap_end2 = memory_layout.cpgge_start -1;
+ //memory_layout.ap_start3 = memory_layout.cptl_end + 1;
+ memory_layout.ap_end3 = memory_layout.fb_start -1;
+
+ BM_ERR("%s: dram: 0x%08X ~ 0x%08X\n", __func__, memory_layout.dram_start, memory_layout.dram_end);
+ BM_ERR("%s: ap_1: 0x%08X ~ 0x%08X\n", __func__, memory_layout.ap_start1, memory_layout.ap_end1);
+ BM_ERR("%s: shm : 0x%08X ~ 0x%08X\n", __func__, memory_layout.shm_start, memory_layout.shm_end);
+ BM_ERR("%s: ap_2: 0x%08X ~ 0x%08X\n", __func__, memory_layout.ap_start2, memory_layout.ap_end2);
+ BM_ERR("%s: cpge: 0x%08X ~ 0x%08X\n", __func__, memory_layout.cpgge_start, memory_layout.cpgge_end);
+ BM_ERR("%s: cpw : 0x%08X ~ 0x%08X\n", __func__, memory_layout.cpw_start, memory_layout.cpw_end);
+ BM_ERR("%s: cpwcn: 0x%08X ~ 0x%08X\n", __func__, memory_layout.cpwcn_start, memory_layout.cpwcn_end);
+ BM_ERR("%s: cptl: 0x%08X ~ 0x%08X\n", __func__, memory_layout.cptl_start, memory_layout.cptl_end);
+ BM_ERR("%s: ap_3: 0x%08X ~ 0x%08X\n", __func__, memory_layout.ap_start3, memory_layout.ap_end3);
+ BM_ERR("%s: fb : 0x%08X ~ 0x%08X\n", __func__, memory_layout.fb_start, memory_layout.fb_end);
+ BM_ERR("%s: ion : 0x%08X ~ 0x%08X\n", __func__, memory_layout.ion_start, memory_layout.ion_end);
+
+ return 0;
+}
+
+static void sci_bm_init_name_info(void)
+{
+ u32 bm_index, bm_chn, bm_no_name = 0;
+ u32 axi_chn_nr, axi_chn_type;
+ //init axi bm name info.
+ for(bm_chn = 0; bm_chn < bm_st_info.axi_bm_cnt; bm_chn++){
+ for(bm_index = 3; bm_index < BM_AXI_TOTAL_CHN_NAME + 3; bm_index++){
+ axi_chn_nr = (*(&bm_st_info.bm_def_mode + bm_index)) & 0xff;
+ axi_chn_type = ((*(&bm_st_info.bm_def_mode + bm_index)) >> 31) & 0x01;
+ if(bm_chn == axi_chn_nr){
+ bm_chn_name[bm_chn].chn_name = bm_name_list[bm_index - 3].chn_name;
+ bm_chn_name[bm_chn].chn_type = axi_chn_type;
+ break;
+ }
+ }
+ if (NULL == bm_chn_name[bm_chn].chn_name)
+ bm_chn_name[bm_chn].chn_name = "ERROR_NAME";
+ }
+ //init ahb bm name info.
+ for(bm_chn = bm_st_info.axi_bm_cnt; bm_chn < BM_AXI_TOTAL_CHN_NAME + BM_AHB_TOTAL_CHN_NAME + 3; bm_chn++){
+ for(bm_index = BM_AXI_TOTAL_CHN_NAME + 3; bm_index < BM_AXI_TOTAL_CHN_NAME + BM_AHB_TOTAL_CHN_NAME + 3; bm_index++){
+ if(bm_chn == ((*(&bm_st_info.bm_def_mode + bm_index)) + bm_st_info.axi_bm_cnt)){
+ bm_chn_name[bm_chn - bm_no_name].chn_name = bm_name_list[bm_index - 3].chn_name;
+ break;
+ }
+ }
+ if("ERROR_NAME" == bm_chn_name[bm_chn - bm_no_name].chn_name){
+ //del the no name chn. to get a continue name list.
+ bm_no_name++;
+ }
+ }
+}
+
+static void sci_bm_get_hw_info(void)
+{
+ struct device_node *np = NULL;
+ u32 val[2];
+
+ np = of_find_compatible_node(NULL, NULL, "sprd,sprd_bm");
+ if(np){
+ //Get the bus monitor default mode.
+ if(of_property_read_u32(np, "sprd,bm_status", &val[0])){
+ BM_WRN("it must be sure that the bus monitor is in pref mode!\n");
+ bm_st_info.bm_def_mode = 0;
+ }else{
+ bm_st_info.bm_def_mode = val[0];
+ BM_INFO("Bus Monitor mode: 0 - Pref; 1 - Debug. Current mode %d\n", bm_st_info.bm_def_mode);
+ }
+ if(of_property_read_u32_array(np, "sprd,bm_count", &val[0], 2)){
+ BM_WRN("it must be sure that the there is no count para!\n");
+ bm_st_info.axi_bm_cnt = 10;
+ bm_st_info.ahb_bm_chn = 11;
+ }else{
+ bm_st_info.axi_bm_cnt = val[0];
+ bm_st_info.ahb_bm_chn = val[1];
+ }
+ //Get AXI bus monitor arch.
+ if(of_property_read_u32_array(np, "sprd,cpu_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not CPU chn!\n");
+ bm_st_info.cpu_chn = 0xff;
+ }else{
+ bm_st_info.cpu_chn = val[0];
+ bm_st_info.cpu_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,disp_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not DISP chn!\n");
+ bm_st_info.disp_chn = 0xff;
+ }else{
+ bm_st_info.disp_chn = val[0];
+ bm_st_info.disp_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,gpu_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not GPU chn!\n");
+ bm_st_info.gpu_chn = 0xff;
+ }else{
+ bm_st_info.gpu_chn = val[0];
+ bm_st_info.gpu_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,ap_zip_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP/ZIP chn!\n");
+ bm_st_info.ap_zip_chn = 0xff;
+ }else{
+ bm_st_info.ap_zip_chn = val[0];
+ bm_st_info.ap_zip_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,zip_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not ZIP chn!\n");
+ bm_st_info.zip_chn = 0xff;
+ }else{
+ bm_st_info.zip_chn = val[0];
+ bm_st_info.zip_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,ap_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP chn!\n");
+ bm_st_info.ap_chn = 0xff;
+ }else{
+ bm_st_info.ap_chn = val[0];
+ bm_st_info.ap_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,mm_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not MM chn!\n");
+ bm_st_info.mm_chn = 0xff;
+ }else{
+ bm_st_info.mm_chn = val[0];
+ bm_st_info.mm_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,cp0_arm0_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not CP0 ARM0 chn!\n");
+ bm_st_info.cp0_arm0_chn = 0xff;
+ }else{
+ bm_st_info.cp0_arm0_chn = val[0];
+ bm_st_info.cp0_arm0_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,cp0_arm1_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not CP0 ARM1 chn!\n");
+ bm_st_info.cp0_arm1_chn = 0xff;
+ }else{
+ bm_st_info.cp0_arm1_chn = val[0];
+ bm_st_info.cp0_arm1_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,cp0_dsp_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not CP0 DSP chn!\n");
+ bm_st_info.cp0_dsp_chn = 0xff;
+ }else{
+ bm_st_info.cp0_dsp_chn = val[0];
+ bm_st_info.cp0_dsp_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,cp0_arm0_1_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not CP0 ARM0/1 chn!\n");
+ bm_st_info.cp0_arm0_1_chn = 0xff;
+ }else{
+ bm_st_info.cp0_arm0_1_chn = val[0];
+ bm_st_info.cp0_arm0_1_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,cp1_lte_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not CP1 LTE chn!\n");
+ bm_st_info.cp1_lte_chn = 0xff;
+ }else{
+ bm_st_info.cp1_lte_chn = val[0];
+ bm_st_info.cp1_lte_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,cp1_dsp_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not CP1 DSP chn!\n");
+ bm_st_info.cp1_dsp_chn = 0xff;
+ }else{
+ bm_st_info.cp1_dsp_chn = val[0];
+ bm_st_info.cp1_dsp_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,cp1_arm_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not CP1 ARM chn!\n");
+ bm_st_info.cp1_arm_chn = 0xff;
+ }else{
+ bm_st_info.cp1_arm_chn = val[0];
+ bm_st_info.cp1_arm_chn |= (val[1] << 31);
+ }
+ if(of_property_read_u32_array(np, "sprd,cp2_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not CP2 chn!\n");
+ bm_st_info.cp2_chn = 0xff;
+ }else{
+ bm_st_info.cp2_chn = val[0];
+ bm_st_info.cp2_chn |= (val[1] << 31);
+ }
+
+ //Get the AHB bus monitor arch
+ if(of_property_read_u32_array(np, "sprd,ap_dap_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP DAP chn!\n");
+ bm_st_info.ap_dap = 0xff;
+ }else
+ bm_st_info.ap_dap = val[1] + (val[0] << 2);
+ if(of_property_read_u32_array(np, "sprd,ap_cpu_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP CPU chn!\n");
+ bm_st_info.ap_cpu = 0xff;
+ }else
+ bm_st_info.ap_cpu = val[1] + (val[0] << 2);
+ if(of_property_read_u32_array(np, "sprd,ap_dma_r_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP DMA READ chn!\n");
+ bm_st_info.ap_dma_r = 0xff;
+ }else
+ bm_st_info.ap_dma_r = val[1] + (val[0] << 2);
+ if(of_property_read_u32_array(np, "sprd,ap_dma_w_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP DMA WRITE chn!\n");
+ bm_st_info.ap_dma_w = 0xff;
+ }else
+ bm_st_info.ap_dma_w = val[1] + (val[0] << 2);
+ if(of_property_read_u32_array(np, "sprd,ap_sdio_0_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP SDIO 0 chn!\n");
+ bm_st_info.ap_sdio_0 = 0xff;
+ }else
+ bm_st_info.ap_sdio_0 = val[1] + (val[0] << 2);
+ if(of_property_read_u32_array(np, "sprd,ap_sdio_1_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP SDIO 1 chn!\n");
+ bm_st_info.ap_sdio_1 = 0xff;
+ }else
+ bm_st_info.ap_sdio_1 = val[1] + (val[0] << 2);
+ if(of_property_read_u32_array(np, "sprd,ap_sdio_2_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP SDIO 2 chn!\n");
+ bm_st_info.ap_sdio_2 = 0xff;
+ }else
+ bm_st_info.ap_sdio_2 = val[1] + (val[0] << 2);
+ if(of_property_read_u32_array(np, "sprd,ap_emmc_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP EMMC chn!\n");
+ bm_st_info.ap_emmc = 0xff;
+ }else
+ bm_st_info.ap_emmc = val[1] + (val[0] << 2);
+ if(of_property_read_u32_array(np, "sprd,ap_nfc_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP NFC chn!\n");
+ bm_st_info.ap_nfc = 0xff;
+ }else
+ bm_st_info.ap_nfc = val[1] + (val[0] << 2);
+ if(of_property_read_u32_array(np, "sprd,ap_usb_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP USB chn!\n");
+ bm_st_info.ap_usb = 0xff;
+ }else
+ bm_st_info.ap_usb = val[1] + (val[0] << 2);
+ if(of_property_read_u32_array(np, "sprd,ap_hsic_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP HSIC chn!\n");
+ bm_st_info.ap_hsic = 0xff;
+ }else
+ bm_st_info.ap_hsic = val[1] + (val[0] << 2);
+ if(of_property_read_u32_array(np, "sprd,ap_otg_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP OTG chn!\n");
+ bm_st_info.ap_otg = 0xff;
+ }else
+ bm_st_info.ap_otg = val[1] + (val[0] << 2);
+ if(of_property_read_u32_array(np, "sprd,ap_nandc_chn", &val[0], 2)){
+ BM_WRN("it must be sure that there is not AP NANDC chn!\n");
+ bm_st_info.ap_nandc = 0xff;
+ }else
+ bm_st_info.ap_nandc = val[1] + (val[0] << 2);
+ }
+ sci_bm_init_name_info();
+}
+
+static int sci_bm_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ u32 bm_chn, bm_mode;
+ ulong reg_addr;
+
+ if(true == bm_st_info.bm_dbg_st){
+ for (bm_chn = AXI_BM0; bm_chn < BM_SIZE; bm_chn++){
+ reg_addr = __sci_get_bm_base(bm_chn);
+ bm_store_vale[bm_chn].str_addr = __raw_readl((volatile void *)(reg_addr + AXI_BM_ADDR_MIN_REG)) + (0x80000000 & bm_store_vale[bm_chn].bm_mask);
+ bm_store_vale[bm_chn].end_addr = __raw_readl((volatile void *)(reg_addr + AXI_BM_ADDR_MAX_REG)) + (0x80000000 & bm_store_vale[bm_chn].bm_mask);
+ bm_mode = __raw_readl((volatile void *)(reg_addr + AXI_BM_CFG_REG)) & 0x3;
+ if(0 == bm_mode)
+ bm_store_vale[bm_chn].mode = RW_MODE;
+ else if(1 == bm_mode)
+ bm_store_vale[bm_chn].mode = R_MODE;
+ else if(3 == bm_mode)
+ bm_store_vale[bm_chn].mode = W_MODE;
+ if(bm_chn >= AHB_BM0){
+ bm_store_vale[bm_chn].min_data = __raw_readl((volatile void *)(reg_addr + AXI_BM_DATA_MIN_L_REG));
+ bm_store_vale[bm_chn].max_data = __raw_readl((volatile void *)(reg_addr + AXI_BM_DATA_MAX_L_REG));
+ bm_mode = sci_glb_read(REG_AP_AHB_MISC_CFG, 0xFFFFFFFF);
+ switch(bm_chn){
+ case AHB_BM0:
+ bm_store_vale[bm_chn].chn_sel = (bm_mode >> 4) & 0x3;
+ break;
+ case AHB_BM1:
+ bm_store_vale[bm_chn].chn_sel = (bm_mode >> 8) & 0x3;
+ break;
+ case AHB_BM2:
+ bm_store_vale[bm_chn].chn_sel = (bm_mode >> 10) & 0x3;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int sci_bm_resume(struct platform_device *pdev)
+{
+ u32 bm_chn, ret;
+ struct sci_bm_cfg bm_cfg;
+
+ __sci_bm_init();
+ __sci_bm_int_clr();
+ if(true == bm_st_info.bm_dbg_st){
+ for (bm_chn = AXI_BM0; bm_chn < BM_SIZE; bm_chn++){
+ if((0x00000000 == bm_store_vale[bm_chn].str_addr) && (0x00000000 == bm_store_vale[bm_chn].end_addr))
+ continue;
+ bm_cfg.addr_min = bm_store_vale[bm_chn].str_addr;
+ bm_cfg.addr_max = bm_store_vale[bm_chn].end_addr;
+ bm_cfg.bm_mode = bm_store_vale[bm_chn].mode;
+ if(bm_chn >= AHB_BM0){
+ bm_cfg.data_min_l = bm_store_vale[bm_chn].min_data;
+ bm_cfg.data_min_h = 0;
+ bm_cfg.data_max_l = bm_store_vale[bm_chn].max_data;
+ bm_cfg.data_max_h = 0;
+ }
+ ret = sci_bm_set_point(bm_chn, bm_store_vale[bm_chn].chn_sel, &bm_cfg, NULL, NULL);
+ if(SPRD_BM_SUCCESS != ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int sci_bm_probe(struct platform_device *pdev)
+{
+ int ret;
+ u32 bm_index;
+ struct resource *res = NULL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "sprd_bm get io resource failed!\n");
+ return -ENODEV;
+ }
+ axi_bm_base = (unsigned long)devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res));
+ if (!axi_bm_base){
+ dev_err(&pdev->dev, "sprd_bm get base address failed!\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "sprd_ahb_bm get io resource failed!\n");
+ return -ENODEV;
+ }
+ ahb_bm_base = (unsigned long)devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res));
+ if (!ahb_bm_base){
+ dev_err(&pdev->dev, "sprd_ahb_bm get base address failed!\n");
+ return -ENODEV;
+ }
+
+ ret = request_irq(IRQ_AXI_BM_PUB_INT, __sci_bm_isr, IRQF_TRIGGER_NONE, pdev->name, pdev);
+ if (ret)
+ return ret;
+ ret = request_irq(IRQ_BM0_INT, __sci_bm_isr, IRQF_SHARED, pdev->name, pdev);
+ if (ret)
+ return ret;
+ ret = request_irq(IRQ_BM1_INT, __sci_bm_isr, IRQF_SHARED, pdev->name, pdev);
+ if (ret)
+ return ret;
+ ret = request_irq(IRQ_BM2_INT, __sci_bm_isr, IRQF_SHARED, pdev->name, pdev);
+ if (ret)
+ return ret;
+ sci_bm_get_hw_info();
+ for (bm_index = AXI_BM0; bm_index < BM_SIZE; bm_index++)
+ __sci_bm_glb_reset_and_enable(bm_index, true);
+ __sci_bm_glb_count_enable(true);
+ __sci_bm_int_clr();
+ __sci_bm_init();
+ __sci_bm_glb_ahb_disable(); /*for power consumption*/
+ ret = sysfs_create_group(&pdev->dev.kobj, &bm_attr_group);
+ if (ret) {
+ BM_ERR("Unable to export sysfs\n");
+ return ret;
+ }
+
+ if(1 == bm_st_info.bm_def_mode){
+ sci_bm_get_mem_layout();
+ sci_bm_def_val_set_by_dts();
+ bm_st_info.bm_dbg_st = true;
+ bm_st_info.bm_stack_st = true;
+ bm_st_info.bm_panic_st = true;
+ //bm_ctn_dbg.bm_continue_dbg= true;
+ //bm_ctn_dbg.loop_cnt = 20;
+ bm_st_info.bm_dfs_off_st = true;
+ BM_ERR("Bus Monitor default config set success!!!\n");
+ }
+ BM_INFO("sci_bm_probe done!\n");
+ return SPRD_BM_SUCCESS;
+}
+
+static int sci_bm_remove(struct platform_device *pdev)
+{
+ u32 bm_index;
+
+ for (bm_index = AXI_BM0; bm_index < BM_SIZE; bm_index++)
+ __sci_bm_glb_reset_and_enable(bm_index, false);
+ __sci_bm_glb_count_enable(false);
+
+ sysfs_remove_group(&pdev->dev.kobj, &bm_attr_group);
+ free_irq(IRQ_AXI_BM_PUB_INT, pdev);
+ free_irq(IRQ_BM0_INT, pdev);
+ free_irq(IRQ_BM1_INT, pdev);
+ free_irq(IRQ_BM2_INT, pdev);
+ return SPRD_BM_SUCCESS;
+}
+
+static int sci_bm_open(struct inode *inode, struct file *filp)
+{
+ BM_INFO("%s!\n", __func__);
+ return 0;
+}
+
+static int sci_bm_release(struct inode *inode, struct file *filp)
+{
+ BM_INFO("%s!\n", __func__);
+ return 0;
+}
+
+static long sci_bm_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
+{
+ struct sci_bm_cfg bm_cfg;
+ struct task_struct *t;
+ struct timeval tv;
+ struct rtc_time tm;
+ volatile struct sci_bm_reg *bm_reg;
+ unsigned long bm_user;
+ unsigned char bm_name[18] = {0};
+ u32 i, ret, bm_index;
+
+ if(cmd >= (BM_CHANNELS << BM_CHN_NAME_CMD_OFFSET)){
+ if(BM_CHANNELS == (cmd >> BM_CHN_NAME_CMD_OFFSET)){
+ bm_index = cmd & 0xf;
+ cmd = cmd >> BM_CHN_NAME_CMD_OFFSET;
+ }
+ }
+ if(_IOC_TYPE(cmd) >= BM_CMD_MAX)
+ return -EINVAL;
+
+ switch(cmd){
+ case BM_STATE:
+ if(bm_st_info.bm_dbg_st)
+ bm_user = 1;
+ if(put_user(bm_user, (unsigned long __user *)args))
+ return -EFAULT;
+ break;
+ case BM_CHANNELS:
+ for(i = 0; i < strlen(bm_chn_name[bm_index].chn_name); i++)
+ bm_name[i] = bm_chn_name[bm_index].chn_name[i];
+ bm_name[i] = '\0';
+ if(copy_to_user((unsigned char __user *)args,
+ &bm_name, strlen(bm_name)))
+ return -EFAULT;
+ break;
+ case BM_AXI_DEBUG_SET://set axi debug point
+ if(copy_from_user(&bm_cfg, (struct sci_bm_cfg __user *)args,
+ sizeof(struct sci_bm_cfg)))
+ return -EFAULT;
+ ret = sci_bm_set_point(bm_cfg.chn, CHN0, &bm_cfg, NULL, NULL);
+ if(SPRD_BM_SUCCESS != ret)
+ return ret;
+ bm_st_info.bm_dbg_st = true;
+ bm_st_info.bm_dfs_off_st = true;
+ break;
+ case BM_AHB_DEBUG_SET://set ahb debug point
+ if(copy_from_user(&bm_cfg, (struct sci_bm_cfg __user *)args,
+ sizeof(struct sci_bm_cfg)))
+ return -EFAULT;
+ ret = sci_bm_set_point(bm_cfg.chn, bm_cfg.data, &bm_cfg, NULL, NULL);
+ if(SPRD_BM_SUCCESS != ret)
+ return ret;
+ bm_st_info.bm_dbg_st = true;
+ bm_st_info.bm_dfs_off_st = true;
+ break;
+ case BM_PERFORM_SET://set performance point
+ if(per_buf == NULL){
+ per_buf = kmalloc(PER_COUNT_BUF_SIZE, GFP_KERNEL);
+ if (!per_buf)
+ BM_ERR("kmalloc failed!\n");
+ sema_init(&bm_seam, 0);
+ t = kthread_run(sci_bm_output_log, NULL, "%s", "bm_per_log");
+ if (IS_ERR(t)) {
+ BM_ERR("bm probe: Failed to run thread bm_per_log\n");
+ kthread_stop(t);
+ return -EFAULT;
+ }
+ }
+ for (bm_index = AXI_BM0; bm_index <= AHB_BM2; bm_index++)
+ __sci_bm_glb_reset_and_enable(bm_index, true);
+ sci_bm_set_perform_point();
+ msleep(100);
+ bm_st_info.bm_dbg_st = false;
+ bm_st_info.bm_dfs_off_st = false;
+ break;
+ case BM_PERFORM_UNSET://unset performance point
+ if(per_buf != NULL)
+ kfree(per_buf);
+ for (bm_index = AXI_BM0; bm_index <= AHB_BM2; bm_index++)
+ __sci_bm_glb_reset_and_enable(bm_index, false);
+ __sci_bm_glb_count_enable(false);
+ break;
+ case BM_OCCUR://read dbg info
+ if(copy_to_user((struct bm_debug_info __user *)args,
+ debug_bm_int_info, sizeof(struct bm_debug_info)))
+ return -EFAULT;
+ break;
+ case BM_CONTINUE_SET://set continue statue
+ if(copy_from_user(&bm_ctn_dbg, (struct bm_continue_debug __user *)args,
+ sizeof(struct bm_continue_debug)))
+ return -EFAULT;
+ break;
+ case BM_CONTINUE_UNSET:
+ bm_ctn_dbg.bm_continue_dbg = false;
+ break;
+ case BM_DFS_SET://set DFS statue
+ case BM_DFS_UNSET:
+ if(get_user(bm_user,(unsigned long __user *)(&args)))
+ return -EFAULT;
+ bm_st_info.bm_dfs_off_st = bm_user;
+ break;
+ case BM_PANIC_SET://set DFS statue
+ case BM_PANIC_UNSET:
+ if(get_user(bm_user,(unsigned long __user *)(&args)))
+ return -EFAULT;
+ bm_st_info.bm_panic_st = bm_user;
+ break;
+ case BM_BW_CNT_START:
+ dmc_mon_cnt_start();
+ break;
+ case BM_BW_CNT_STOP:
+ dmc_mon_cnt_stop();
+ break;
+ case BM_BW_CNT_RESUME:
+ break;
+ case BM_BW_CNT:
+ bm_user = dmc_mon_cnt_bw();
+ if(put_user(bm_user, (unsigned long __user *)args))
+ return -EFAULT;
+ case BM_BW_CNT_CLR:
+ dmc_mon_cnt_clr();
+ break;
+ case BM_DBG_INT_CLR:
+ __sci_axi_bm_int_disable();
+ break;
+ case BM_DBG_INT_SET:
+ __sci_axi_bm_int_enable();
+ break;
+ case BM_DISABLE:
+ __raw_writel(0x0, (volatile void *)(REG_PUB_APB_BUSMON_CFG));
+ break;
+ case BM_ENABLE:
+ __raw_writel(BM_CHANNEL_ENABLE_FLAGE, (volatile void *)(REG_PUB_APB_BUSMON_CFG));
+ break;
+ case BM_PROF_SET:
+ for (bm_index = AXI_BM0; bm_index <= AHB_BM2; bm_index++)
+ __sci_bm_glb_reset_and_enable(bm_index, true);
+ __sci_bm_init();
+ __sci_axi_bm_cnt_clr();
+ __sci_bm_int_clr();
+ __sci_bm_glb_count_enable(false);
+ __sci_axi_bm_cnt_start();
+ __sci_bm_glb_count_enable(true);
+ break;
+ case BM_PROF_CLR:
+ for (bm_index = AXI_BM0; bm_index <= AHB_BM2; bm_index++)
+ __sci_bm_glb_reset_and_enable(bm_index, false);
+ __sci_bm_glb_count_enable(false);
+ break;
+ case BM_CHN_CNT:
+ if(put_user(bm_st_info.axi_bm_cnt, (unsigned long __user *)args))
+ return -EFAULT;
+ break;
+ case BM_RD_CNT:
+ copy_from_user(&bm_user, (unsigned long __user *)args, sizeof(unsigned long));
+ bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_user);
+ bm_user = (unsigned long)(bm_reg->rtrans_in_win);
+ if(copy_to_user((unsigned long __user *)args, &bm_user, sizeof(unsigned long)))
+ return -EFAULT;
+ break;
+ case BM_WR_CNT:
+ copy_from_user(&bm_user, (unsigned long __user *)args, sizeof(unsigned long));
+ bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_user);
+ bm_user = (unsigned long)(bm_reg->wtrans_in_win);
+ if(copy_to_user((unsigned long __user *)args, &bm_user, sizeof(unsigned long)))
+ return -EFAULT;
+ break;
+ case BM_RD_BW:
+ copy_from_user(&bm_user, (unsigned long __user *)args, sizeof(unsigned long));
+ bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_user);
+ bm_user = (unsigned long)(bm_reg->rbw_in_win);
+ if(copy_to_user((unsigned long __user *)args, &bm_user, sizeof(unsigned long)))
+ return -EFAULT;
+ break;
+ case BM_WR_BW:
+ copy_from_user(&bm_user, (unsigned long __user *)args, sizeof(unsigned long));
+ bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_user);
+ bm_user = (unsigned long)(bm_reg->wbw_in_win);
+ if(copy_to_user((unsigned long __user *)args, &bm_user, sizeof(unsigned long)))
+ return -EFAULT;
+ break;
+ case BM_RD_LATENCY:
+ copy_from_user(&bm_user, (unsigned long __user *)args, sizeof(unsigned long));
+ bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_user);
+ bm_user = (unsigned long)(bm_reg->rlatency_in_win);
+ if(copy_to_user((unsigned long __user *)args, &bm_user, sizeof(unsigned long)))
+ return -EFAULT;
+ break;
+ case BM_WR_LATENCY:
+ copy_from_user(&bm_user, (unsigned long __user *)args, sizeof(unsigned long));
+ bm_reg = (struct sci_bm_reg *)__sci_get_bm_base(bm_user);
+ bm_user = (unsigned long)(bm_reg->wlatency_in_win);
+ if(copy_to_user((unsigned long __user *)args, &bm_user, sizeof(unsigned long)))
+ return -EFAULT;
+ break;
+ case BM_KERNEL_TIME:
+ do_gettimeofday(&tv);
+ //rtc_time_to_tm(tv.tv_sec,&tm);
+ //bm_user = (tm.tm_hour * 10000) + (tm.tm_min * 100) + tm.tm_sec;
+ if(put_user(tv.tv_sec, (unsigned long __user *)args))
+ return -EFAULT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct file_operations bm_fops = {
+ .owner = THIS_MODULE,
+ .open = sci_bm_open,
+ .release = sci_bm_release,
+ .unlocked_ioctl = sci_bm_ioctl,
+};
+
+static struct miscdevice bm_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "sprd_bm",
+ .fops = &bm_fops,
+};
+
+static const struct of_device_id bm_of_match[] = {
+ { .compatible = "sprd,sprd_bm", },
+ { }
+};
+
+static struct platform_driver sprd_bm_driver = {
+ .probe = sci_bm_probe,
+ .remove = sci_bm_remove,
+ .suspend = sci_bm_suspend,
+ .resume = sci_bm_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "sprd_bm",
+ .of_match_table = bm_of_match,
+ },
+};
+
+static int __init sci_bm_init(void)
+{
+ misc_register(&bm_misc);
+ return platform_driver_register(&sprd_bm_driver);
+}
+
+static void __exit sci_bm_exit(void)
+{
+ platform_driver_unregister(&sprd_bm_driver);
+ misc_deregister(&bm_misc);
+}
+
+module_init(sci_bm_init);
+module_exit(sci_bm_exit);
+
+EXPORT_SYMBOL_GPL(dmc_mon_cnt_bw);
+EXPORT_SYMBOL_GPL(dmc_mon_cnt_clr);
+EXPORT_SYMBOL_GPL(dmc_mon_cnt_start);
+EXPORT_SYMBOL_GPL(dmc_mon_cnt_stop);
+EXPORT_SYMBOL_GPL(dmc_mon_resume);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("eric.long<eric.long@spreadtrum.com>");
+MODULE_DESCRIPTION("spreadtrum platform busmonitor driver");
+
+
diff --git a/drivers/platform/sprd/clock-sc8830_dt.c b/drivers/platform/sprd/clock-sc8830_dt.c
new file mode 100644
index 00000000..72398373
--- /dev/null
+++ b/drivers/platform/sprd/clock-sc8830_dt.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * 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.
+ *
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+/* FIXME:
+ * called by sprd_init_time() in CONFIG_OF, but not load DT
+ * see board-sp8830gea.c
+ */
+#if defined(CONFIG_OF)
+int __init sci_clock_init(void)
+{
+ WARN_ON(1);
+ return 0;
+}
+#endif
diff --git a/drivers/platform/sprd/clock.h b/drivers/platform/sprd/clock.h
new file mode 100644
index 00000000..eb8586b8
--- /dev/null
+++ b/drivers/platform/sprd/clock.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * 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.
+ *
+ */
+
+#undef debug
+#define debug(format, arg...) pr_debug("clk: " "@@@%s: " format, __func__, ## arg)
+#define debug0(format, arg...)// pr_info("clk: " "@@@%s: " format, __func__, ## arg)
+#define debug2(format, arg...) pr_debug("clk: " "@@@%s: " format, __func__, ## arg)
+
+/**
+ * struct clk_sel - list of sources for a given clock (pll)
+ * @sources: array of pointers to clocks
+ * @nr_sources: The size of @sources
+ */
+struct clk_sel {
+ int nr_sources;
+ u32 sources[];
+};
+
+/**
+ * struct clk_reg - register definition for clock control bits
+ * @reg: pointer to the register in virtual memory.
+ * @shift: the shift in bits to where the bitfield is.
+ * @size: the size in bits of the bitfield.
+ * @mask: the mask of the bitfield.
+ *
+ * This specifies the size and position of the bits we are interested
+ * in within the register specified by @reg.
+ */
+struct clk_reg {
+ u32 reg;
+ //unsigned short shift, size;
+ u32 mask;
+};
+
+struct clk_regs {
+ int id;
+ const char *name;
+ struct clk_reg enb, div, sel;
+
+ //pll sources select
+ int nr_sources;
+ struct clk *sources[];
+};
+
+/**
+ * struct clk_ops - standard clock operations
+ * @set_rate: set the clock rate, see clk_set_rate().
+ * @get_rate: get the clock rate, see clk_get_rate().
+ * @round_rate: round a given clock rate, see clk_round_rate().
+ * @set_parent: set the clock's parent, see clk_set_parent().
+ *
+ * Group the common clock implementations together so that we
+ * don't have to keep setting the same fields again. We leave
+ * enable in struct clk.
+ *
+ * Adding an extra layer of indirection into the process should
+ * not be a problem as it is unlikely these operations are going
+ * to need to be called quickly.
+ */
+struct clk_ops {
+ 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);
+};
+
+/**
+ * struct clk - class of clock for newer style spreadtrum devices.
+ * @clk: the standard clock representation
+ * @sel: the parent sources select for this clock
+ * @reg_sel: the register definition for selecting the pll clock's source
+ * @reg_div: the register definition for the clock's output divisor
+ * @reg_enb: the register definition for enable or disable the clock's source
+ *
+ * This clock implements the features required by the newer SoCs where
+ * the standard clock block provides an input mux and a post-mux divisor
+ * to provide the periperhal's clock.
+ *
+ * The array of @sel provides the mapping of mux position to the
+ * clock, and @reg_sel shows the code where to modify to change the mux
+ * position. The @reg_div defines how to change the divider settings on
+ * the output.
+ */
+
+struct clk {
+ struct module *owner;
+ struct clk *parent;
+ int usage;
+ unsigned long rate;
+ struct clk_ops *ops;
+ int (*enable) (struct clk *, int enable, unsigned long *);
+
+ const struct clk_regs *regs;
+#if defined(CONFIG_DEBUG_FS)
+ struct dentry *dent; /* For visible tree hierarchy */
+#endif
+};
+
+#define MAX_DIV (1000)
+
+#define SCI_CLK_ADD(ID, RATE, ENB, ENB_BIT, DIV, DIV_MSK, SEL, SEL_MSK, NR_CLKS, ...) \
+static const struct clk_regs REGS_##ID = { \
+ .name = #ID, \
+ .id = 0, \
+ .enb = { \
+ .reg = (u32)ENB,.mask = ENB_BIT, \
+ }, \
+ .div = { \
+ .reg = (u32)DIV,.mask = DIV_MSK, \
+ }, \
+ .sel = { \
+ .reg = (u32)SEL,.mask = SEL_MSK, \
+ }, \
+ .nr_sources = NR_CLKS, \
+ .sources = {__VA_ARGS__}, \
+}; \
+static struct clk ID = { \
+ .owner = THIS_MODULE, \
+ .parent = 0, \
+ .usage = 0, \
+ .rate = RATE, \
+ .regs = &REGS_##ID, \
+ .ops = 0, \
+ .enable = 0, \
+}; \
+const struct clk_lookup __clkinit1 CLK_LK_##ID = { \
+ .dev_id = 0, \
+ .con_id = #ID, \
+ .clk = &ID, \
+}; \
+
+int __init sci_clk_register(struct clk_lookup *cl);
+
+#define __clkinit0 __section(.rodata.clkinit0)
+#define __clkinit1 __section(.rodata.clkinit1)
+#define __clkinit2 __section(.rodata.clkinit2)
diff --git a/drivers/platform/sprd/clock_dt.h b/drivers/platform/sprd/clock_dt.h
new file mode 100644
index 00000000..bc9b58fd
--- /dev/null
+++ b/drivers/platform/sprd/clock_dt.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * 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.
+ *
+ */
+
+#undef debug
+#define debug(format, arg...) pr_debug("clk: " "@@@%s: " format, __func__, ## arg)
+#define debug0(format, arg...)// pr_info("clk: " "@@@%s: " format, __func__, ## arg)
+#define debug2(format, arg...) pr_debug("clk: " "@@@%s: " format, __func__, ## arg)
+
+/**
+ * struct clk_sel - list of sources for a given clock (pll)
+ * @sources: array of pointers to clocks
+ * @nr_sources: The size of @sources
+ */
+struct clk_sel {
+ int nr_sources;
+ u32 sources[];
+};
+
+/**
+ * struct clk_reg - register definition for clock control bits
+ * @reg: pointer to the register in virtual memory.
+ * @shift: the shift in bits to where the bitfield is.
+ * @size: the size in bits of the bitfield.
+ * @mask: the mask of the bitfield.
+ *
+ * This specifies the size and position of the bits we are interested
+ * in within the register specified by @reg.
+ */
+struct clk_reg {
+ u32 reg;
+ //unsigned short shift, size;
+ u32 mask;
+};
+
+struct clk_regs {
+ int id;
+ const char *name;
+ struct clk_reg enb, div, sel;
+
+ //pll sources select
+ int nr_sources;
+ char *sources[];
+};
+
+/**
+ * struct clk_ops - standard clock operations
+ * @set_rate: set the clock rate, see clk_set_rate().
+ * @get_rate: get the clock rate, see clk_get_rate().
+ * @round_rate: round a given clock rate, see clk_round_rate().
+ * @set_parent: set the clock's parent, see clk_set_parent().
+ *
+ * Group the common clock implementations together so that we
+ * don't have to keep setting the same fields again. We leave
+ * enable in struct clk.
+ *
+ * Adding an extra layer of indirection into the process should
+ * not be a problem as it is unlikely these operations are going
+ * to need to be called quickly.
+ */
+
+/**
+ * struct clk - class of clock for newer style spreadtrum devices.
+ * @clk: the standard clock representation
+ * @sel: the parent sources select for this clock
+ * @reg_sel: the register definition for selecting the pll clock's source
+ * @reg_div: the register definition for the clock's output divisor
+ * @reg_enb: the register definition for enable or disable the clock's source
+ *
+ * This clock implements the features required by the newer SoCs where
+ * the standard clock block provides an input mux and a post-mux divisor
+ * to provide the periperhal's clock.
+ *
+ * The array of @sel provides the mapping of mux position to the
+ * clock, and @reg_sel shows the code where to modify to change the mux
+ * position. The @reg_div defines how to change the divider settings on
+ * the output.
+ */
+
+struct sci_clk {
+ struct clk_hw hw;
+ unsigned long rate;
+
+ const struct clk_regs *regs;
+};
+
+#define MAX_DIV (1000)
+
+#define SCI_CLK_ADD(ID, RATE, ENB, ENB_BIT, DIV, DIV_MSK, SEL, SEL_MSK, NR_CLKS, ...) \
+static const struct clk_regs REGS_##ID = { \
+ .name = #ID, \
+ .id = 0, \
+ .enb = { \
+ .reg = (u32)ENB,.mask = ENB_BIT, \
+ }, \
+ .div = { \
+ .reg = (u32)DIV,.mask = DIV_MSK, \
+ }, \
+ .sel = { \
+ .reg = (u32)SEL,.mask = SEL_MSK, \
+ }, \
+ .nr_sources = NR_CLKS, \
+ .sources = {__VA_ARGS__}, \
+}; \
+static struct sci_clk ID = { \
+ .hw = {0}, \
+ .rate = RATE, \
+ .regs = &REGS_##ID, \
+}; \
+const struct clk_lookup __clkinit1 CLK_LK_##ID = { \
+ .dev_id = 0, \
+ .con_id = #ID, \
+ .clk = (struct clk *)&ID, \
+}; \
+
+int __init sci_clk_register(struct clk_lookup *cl);
+
+#define __clkinit0 __section(.rodata.clkinit0)
+#define __clkinit1 __section(.rodata.clkinit1)
+#define __clkinit2 __section(.rodata.clkinit2)
diff --git a/drivers/platform/sprd/common.c b/drivers/platform/sprd/common.c
new file mode 100644
index 00000000..57b8f327
--- /dev/null
+++ b/drivers/platform/sprd/common.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/io.h>
+#include <linux/irqchip/arm-gic.h>
+#include <soc/sprd/hardware.h>
+
+static void __iomem *gic_dist_base_addr;
+static void __iomem *gic_cpu_base;
+
+void __iomem *sprd_get_gic_dist_base(void)
+{
+ gic_dist_base_addr = (void __iomem *)CORE_GIC_DIS_VA;
+ return gic_dist_base_addr;
+}
+
+void __iomem *sprd_get_gic_cpu_base(void)
+{
+ gic_cpu_base = (void __iomem *)CORE_GIC_CPU_VA;
+ return gic_cpu_base;
+}
+
+int sprd_map_gic(void){
+ gic_dist_base_addr = (void __iomem *)CORE_GIC_DIS_VA;
+ gic_cpu_base = (void __iomem *)CORE_GIC_CPU_VA;
+ return 0;
+}
+
+/*
+ * FIXME: Remove this GIC APIs once common GIG library starts
+ * supporting it.
+ */
+void gic_cpu_enable(unsigned int cpu)
+{
+ __raw_writel(0xf0, gic_cpu_base + GIC_CPU_PRIMASK);
+ __raw_writel(1, gic_cpu_base + GIC_CPU_CTRL);
+}
+
+void gic_cpu_disable(unsigned int cpu)
+{
+ __raw_writel(0, gic_cpu_base + GIC_CPU_CTRL);
+}
+
+
+bool gic_dist_disabled(void)
+{
+ return !(__raw_readl(gic_dist_base_addr + GIC_DIST_CTRL) & 0x1);
+}
+
+void gic_dist_enable(void)
+{
+ __raw_writel(0x1, gic_dist_base_addr + GIC_DIST_CTRL);
+}
+void gic_dist_disable(void)
+{
+ __raw_writel(0, gic_dist_base_addr + GIC_CPU_CTRL);
+}
+
+void __iomem *sprd_get_scu_base(void)
+{
+ return (void __iomem *)SPRD_CORE_BASE;
+}
diff --git a/drivers/platform/sprd/cpufreq-scx35.c b/drivers/platform/sprd/cpufreq-scx35.c
new file mode 100644
index 00000000..6ef6a5a4
--- /dev/null
+++ b/drivers/platform/sprd/cpufreq-scx35.c
@@ -0,0 +1,1272 @@
+/*
+ * Copyright (C) 2013 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/cpu.h>
+#include <linux/regulator/consumer.h>
+//#include <asm/system.h>
+#include <trace/events/power.h>
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/regulator.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/arch_misc.h>
+#include <linux/of_platform.h>
+
+#if defined(CONFIG_ARCH_SC8825)
+#define MHz (1000000)
+#define GR_MPLL_REFIN_2M (2 * MHz)
+#define GR_MPLL_REFIN_4M (4 * MHz)
+#define GR_MPLL_REFIN_13M (13 * MHz)
+#define GR_MPLL_REFIN_SHIFT 16
+#define GR_MPLL_REFIN_MASK (0x3)
+#define GR_MPLL_N_MASK (0x7ff)
+#define GR_MPLL_MN (REG_GLB_M_PLL_CTL0)
+#define GR_GEN1 (REG_GLB_GEN1)
+#endif
+
+#define FREQ_TABLE_SIZE 10
+#define DVFS_BOOT_TIME (30 * HZ)
+#define SHARK_TDPLL_FREQUENCY (768000)
+#define TRANSITION_LATENCY (50 * 1000) /* ns */
+
+static DEFINE_MUTEX(freq_lock);
+struct cpufreq_freqs global_freqs;
+unsigned int percpu_target[CONFIG_NR_CPUS] = {0};
+static unsigned long boot_done;
+static unsigned int sprd_top_frequency; /* khz */
+
+struct cpufreq_conf {
+ struct clk *clk;
+ struct clk *mpllclk;
+ struct clk *tdpllclk;
+ struct regulator *regulator;
+ struct cpufreq_frequency_table *freq_tbl;
+ unsigned int *vddarm_mv;
+ unsigned int max_axi_freq;
+};
+
+struct cpufreq_table_data {
+ struct cpufreq_frequency_table freq_tbl[FREQ_TABLE_SIZE];
+ unsigned int vddarm_mv[FREQ_TABLE_SIZE];
+};
+
+struct cpufreq_conf *sprd_cpufreq_conf = NULL;
+static struct mutex cpufreq_vddarm_lock;
+
+#if defined(CONFIG_ARCH_SC8825)
+static struct cpufreq_table_data sc8825_cpufreq_table_data = {
+ .freq_tbl = {
+ {0, 1000000},
+ {1, 500000},
+ {2, CPUFREQ_TABLE_END}
+ },
+ .vddarm_mv = {
+ 0
+ },
+};
+
+struct cpufreq_conf sc8825_cpufreq_conf = {
+ .clk = NULL,
+ .regulator = NULL,
+ .freq_tbl = sc8825_cpufreq_table_data.freq_tbl,
+ .vddarm_mv = sc8825_cpufreq_table_data.vddarm_mv,
+};
+
+static void set_mcu_clk_freq(u32 mcu_freq)
+{
+ u32 val, rate, arm_clk_div, gr_gen1;
+
+ rate = mcu_freq / MHz;
+ switch(1000 / rate)
+ {
+ case 1:
+ arm_clk_div = 0;
+ break;
+ case 2:
+ arm_clk_div = 1;
+ break;
+ default:
+ panic("set_mcu_clk_freq fault\n");
+ break;
+ }
+ pr_debug("%s --- before, AHB_ARM_CLK: %08x, rate = %d, div = %d\n",
+ __func__, __raw_readl(REG_AHB_ARM_CLK), rate, arm_clk_div);
+
+ gr_gen1 = __raw_readl(GR_GEN1);
+ gr_gen1 |= BIT(9);
+ __raw_writel(gr_gen1, GR_GEN1);
+
+ val = __raw_readl(REG_AHB_ARM_CLK);
+ val &= 0xfffffff8;
+ val |= arm_clk_div;
+ __raw_writel(val, REG_AHB_ARM_CLK);
+
+ gr_gen1 &= ~BIT(9);
+ __raw_writel(gr_gen1, GR_GEN1);
+
+ pr_debug("%s --- after, AHB_ARM_CLK: %08x, rate = %d, div = %d\n",
+ __func__, __raw_readl(REG_AHB_ARM_CLK), rate, arm_clk_div);
+
+ return;
+}
+
+static unsigned int get_mcu_clk_freq(void)
+{
+ u32 mpll_refin, mpll_n, mpll_cfg = 0, rate, val;
+
+ mpll_cfg = __raw_readl(GR_MPLL_MN);
+
+ mpll_refin = (mpll_cfg >> GR_MPLL_REFIN_SHIFT) & GR_MPLL_REFIN_MASK;
+ switch(mpll_refin){
+ case 0:
+ mpll_refin = GR_MPLL_REFIN_2M;
+ break;
+ case 1:
+ case 2:
+ mpll_refin = GR_MPLL_REFIN_4M;
+ break;
+ case 3:
+ mpll_refin = GR_MPLL_REFIN_13M;
+ break;
+ default:
+ pr_err("%s mpll_refin: %d\n", __FUNCTION__, mpll_refin);
+ }
+ mpll_n = mpll_cfg & GR_MPLL_N_MASK;
+ rate = mpll_refin * mpll_n;
+
+ /*find div */
+ val = __raw_readl(REG_AHB_ARM_CLK) & 0x7;
+ val += 1;
+ return rate / val;
+}
+#endif
+
+static struct cpufreq_table_data sc8830_cpufreq_table_data_cs = {
+ .freq_tbl = {
+ {0, 1200000},
+ {1, 1000000},
+ {2, SHARK_TDPLL_FREQUENCY},
+ {3, 600000},
+ {4, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1300000,
+ 1200000,
+ 1150000,
+ 1100000,
+ 1000000,
+ },
+};
+
+/*
+for 7715 test
+*/
+static struct cpufreq_table_data sc7715_cpufreq_table_data = {
+ .freq_tbl = {
+ {0, 1000000},
+ {1, SHARK_TDPLL_FREQUENCY},
+ {2, 600000},
+ {3, SHARK_TDPLL_FREQUENCY/2},
+ {4, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1200000,
+ 1150000,
+ 1100000,
+ 1100000,
+ 1000000,
+ },
+};
+
+
+static struct cpufreq_table_data sc8830_cpufreq_table_data_es = {
+ .freq_tbl = {
+ {0, 1000000},
+ {1, SHARK_TDPLL_FREQUENCY},
+ {2, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1250000,
+ 1200000,
+ 1000000,
+ },
+};
+
+static struct cpufreq_table_data sc8830t_cpufreq_table_data_es = {
+ .freq_tbl = {
+ {0, 1300000},
+ {1, 1200000},
+ {2, 1000000},
+ {3, SHARK_TDPLL_FREQUENCY},
+ {4, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1060000,
+ 1030000,
+ 960000,
+ 900000,
+ 900000,
+ },
+};
+
+static struct cpufreq_table_data sc8730c_cpufreq_table_data = {
+ .freq_tbl = {
+ {0, 1400000},
+ {1, 1300000},
+ {2, 1200000},
+ {3, 1000000},
+ {4, SHARK_TDPLL_FREQUENCY},
+ {5, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1060000,
+ 1060000,
+ 1030000,
+ 960000,
+ 900000,
+ 900000,
+ },
+};
+
+static struct cpufreq_table_data sc8830t_cpufreq_table_data_es_1300 = {
+ .freq_tbl = {
+ {0, 1300000},
+ {1, 1200000},
+ {2, 1000000},
+ {3, SHARK_TDPLL_FREQUENCY},
+ {4, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1060000,
+ 1030000,
+ 960000,
+ 900000,
+ 900000,
+ },
+};
+
+static struct cpufreq_table_data sc9630_cpufreq_table_data = {
+ .freq_tbl = {
+ {0, 1500000},
+ {1, 1350000},
+ {2, 900000},
+ {3, 768000},
+ {4, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1100000,
+ 1000000,
+ 900000,
+ 900000,
+ 900000,
+ },
+};
+
+static struct cpufreq_table_data sc7720_cpufreq_table_data = {
+ .freq_tbl = {
+ {0, 1200000},
+ {1, 813000},
+ {2, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1200000,
+ 1050000,
+ 1050000,
+ },
+};
+
+static struct cpufreq_table_data sc9631l64_cpufreq_table_data_es = {
+ .freq_tbl = {
+ {0, 1300000},
+ {1, 1000000},
+ {2, SHARK_TDPLL_FREQUENCY},
+ {3, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1000000,
+ 900000,
+ 900000,
+ 900000,
+ },
+};
+static struct cpufreq_table_data sc9820_cpufreq_table_data = {
+ .freq_tbl = {
+ {0, 1250000},
+ {1, 800000},
+ {2, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1200000,
+ 1050000,
+ 1050000,
+ },
+};
+struct cpufreq_conf sc8830_cpufreq_conf = {
+ .clk = NULL,
+ .mpllclk = NULL,
+ .tdpllclk = NULL,
+ .regulator = NULL,
+ .freq_tbl = NULL,
+ .vddarm_mv = NULL,
+};
+
+int cpufreq_table_thermal_update(unsigned int freq, unsigned int voltage)
+{
+ struct cpufreq_frequency_table *freq_tbl;
+ unsigned int *vddarm;
+ int i;
+
+ if (NULL == sprd_cpufreq_conf)
+ return -1;
+ freq_tbl = sprd_cpufreq_conf->freq_tbl;
+ vddarm = sprd_cpufreq_conf->vddarm_mv;
+ if (NULL == freq_tbl && NULL == vddarm)
+ return -1;
+
+ for (i = 0; freq_tbl[i].frequency != CPUFREQ_TABLE_END; ++i) {
+ if (freq_tbl[i].frequency == freq)
+ goto done;
+ }
+ pr_err(KERN_ERR "%s cpufreq %dMHz isn't find!\n", __func__, freq);
+ return -1;
+done:
+ printk(KERN_ERR "%s: %dMHz voltage is %dmV\n",
+ __func__, freq, voltage);
+ if (vddarm[i] == voltage)
+ return 0;
+
+ mutex_lock(&cpufreq_vddarm_lock);
+ vddarm[i] = voltage;
+ mutex_unlock(&cpufreq_vddarm_lock);
+
+ return 0;
+}
+
+static unsigned int sprd_raw_get_cpufreq(void)
+{
+#if defined(CONFIG_ARCH_SCX35)
+ return clk_get_rate(sprd_cpufreq_conf->clk) / 1000;
+#elif defined(CONFIG_ARCH_SC8825)
+ return get_mcu_clk_freq() / 1000;
+#endif
+}
+
+unsigned int last_freq = 0;
+static void dump_axi_cpu(unsigned int freq)
+{
+ u32 div, axi_freq;
+
+#ifndef CONFIG_ARCH_SCX35L
+ div = sci_glb_read(REG_AP_AHB_CA7_CKG_CFG, -1UL);
+ div &= (0x7<<8);
+ div >>= 8;
+ axi_freq = freq / (div + 1);
+#elif defined CONFIG_ARCH_SCX35LT8
+ div = sci_glb_read(REG_AP_AHB_CA7_CKG_DIV_CFG, -1UL);
+ div &= (0x7<<8);
+ div >>= 8;
+ axi_freq = freq / (div + 1);
+#endif
+ printk("%s(%d): cpu_freq %d, div %d, axi_freq %d\n", __func__, __LINE__,
+ freq, div, axi_freq);
+}
+
+static inline int get_axi_div(unsigned int freq)
+{
+ if (freq % sprd_cpufreq_conf->max_axi_freq)
+ return (freq / sprd_cpufreq_conf->max_axi_freq) + 1;
+ else
+ return freq / sprd_cpufreq_conf->max_axi_freq;
+}
+
+static void cpufreq_adjust_axi_clk(unsigned int freq)
+{
+ struct clk *clk_axi = NULL;
+ int div = 0, div_old = 0;
+ int need_adjust = 0;
+ char clk_name[50] = {0};
+
+ if (last_freq == 0) {
+ last_freq = freq;
+ need_adjust = 1;
+ }
+
+#ifndef CONFIG_ARCH_SCX35L
+ strcpy(clk_name, "clk_ca7_axi");
+#else
+ // for T8, in cpufreq-dt-sprd.c ?
+#endif
+ if (!clk_name[0]) {
+ last_freq = freq;
+ return;
+ }
+
+ div = get_axi_div(freq);
+ div_old = get_axi_div(last_freq);
+ if (!need_adjust && (div != div_old))
+ need_adjust = 1;
+
+ if (!need_adjust) {
+ last_freq = freq;
+ return;
+ }
+ last_freq = freq;
+
+ clk_axi = clk_get_sys(NULL, clk_name);
+ if (IS_ERR_OR_NULL(clk_axi)) {
+ pr_err("%s(%d) err: cannot find clock %s\n", __func__, __LINE__, clk_name);
+ return;
+ }
+
+ if (clk_set_rate(clk_axi, freq * 1000 / div))
+ pr_err("%s(%d) err: clk_set_rate failed\n", __func__, __LINE__);
+
+ clk_put(clk_axi);
+ //dump_axi_cpu(freq);
+}
+
+static void cpufreq_set_clock(unsigned int freq)
+{
+ int ret;
+
+ ret = clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->tdpllclk);
+ if (ret)
+ pr_err("Failed to set cpu parent to tdpll\n");
+ if (freq == SHARK_TDPLL_FREQUENCY/2) {
+ //ca7 clk div
+ #ifndef CONFIG_ARCH_SCX35L
+ sci_glb_set(REG_AP_AHB_CA7_CKG_CFG, BITS_CA7_MCU_CKG_DIV(1));
+ #else
+ #ifndef CONFIG_ARCH_SCX35LT8 //TODO
+ sci_glb_set(REG_AP_AHB_CA7_CKG_DIV_CFG, BITS_CA7_MCU_CKG_DIV(1));
+ #endif
+ #endif
+ } else if (freq == SHARK_TDPLL_FREQUENCY) {
+ #ifndef CONFIG_ARCH_SCX35L
+ sci_glb_clr(REG_AP_AHB_CA7_CKG_CFG, BITS_CA7_MCU_CKG_DIV(1));
+ #else
+ #ifndef CONFIG_ARCH_SCX35LT8 //TODO
+ sci_glb_clr(REG_AP_AHB_CA7_CKG_DIV_CFG, BITS_CA7_MCU_CKG_DIV(1));
+ #endif
+ #endif
+
+ } else {
+ /*
+ if (clk_get_parent(sprd_cpufreq_conf->clk) != sprd_cpufreq_conf->tdpllclk) {
+ ret = clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->tdpllclk);
+ if (ret)
+ pr_err("Failed to set cpu parent to tdpll\n");
+ }
+ */
+ if (!(sci_glb_read(REG_PMU_APB_MPLL_REL_CFG, -1) & BIT_MPLL_AP_SEL)) {
+ sci_glb_set(REG_PMU_APB_MPLL_REL_CFG, BIT_MPLL_AP_SEL);
+ udelay(500);
+ }
+ ret = clk_set_rate(sprd_cpufreq_conf->mpllclk, (freq * 1000));
+ if (ret)
+ pr_err("Failed to set mpll rate\n");
+ ret = clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->mpllclk);
+ if (ret)
+ pr_err("Failed to set cpu parent to mpll\n");
+ #ifndef CONFIG_ARCH_SCX35L
+ sci_glb_clr(REG_AP_AHB_CA7_CKG_CFG, BITS_CA7_MCU_CKG_DIV(1));
+ #else
+ #ifndef CONFIG_ARCH_SCX35LT8 //TODO
+ sci_glb_clr(REG_AP_AHB_CA7_CKG_DIV_CFG, BITS_CA7_MCU_CKG_DIV(1));
+ #endif
+ #endif
+ }
+
+ cpufreq_adjust_axi_clk(freq);
+}
+static void sprd_raw_set_cpufreq(int cpu, struct cpufreq_freqs *freq, int index)
+{
+#if defined(CONFIG_ARCH_SCX35)
+ int ret;
+
+#define CPUFREQ_SET_VOLTAGE() \
+ do { \
+ mutex_lock(&cpufreq_vddarm_lock); \
+ ret = regulator_set_voltage(sprd_cpufreq_conf->regulator, \
+ sprd_cpufreq_conf->vddarm_mv[index], \
+ sprd_cpufreq_conf->vddarm_mv[index]); \
+ mutex_unlock(&cpufreq_vddarm_lock); \
+ if (ret) \
+ pr_err("Failed to set vdd to %d mv\n", \
+ sprd_cpufreq_conf->vddarm_mv[index]); \
+ } while (0)
+#define CPUFREQ_SET_CLOCK() \
+ do { \
+ if (freq->new == SHARK_TDPLL_FREQUENCY) { \
+ ret = clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->tdpllclk); \
+ if (ret) \
+ pr_err("Failed to set cpu parent to tdpll\n"); \
+ } else { \
+ if (clk_get_parent(sprd_cpufreq_conf->clk) != sprd_cpufreq_conf->tdpllclk) { \
+ ret = clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->tdpllclk); \
+ if (ret) \
+ pr_err("Failed to set cpu parent to tdpll\n"); \
+ } \
+ ret = clk_set_rate(sprd_cpufreq_conf->mpllclk, (freq->new * 1000)); \
+ if (ret) \
+ pr_err("Failed to set mpll rate\n"); \
+ ret = clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->mpllclk); \
+ if (ret) \
+ pr_err("Failed to set cpu parent to mpll\n"); \
+ } \
+ } while (0)
+ trace_cpu_frequency(freq->new, cpu);
+
+ if (freq->new >= sprd_raw_get_cpufreq()) {
+ CPUFREQ_SET_VOLTAGE();
+ cpufreq_set_clock(freq->new);
+ } else {
+ cpufreq_set_clock(freq->new);
+ CPUFREQ_SET_VOLTAGE();
+ }
+
+ pr_info("%u --> %u, real=%u, index=%d\n",
+ freq->old, freq->new, sprd_raw_get_cpufreq(), index);
+
+#undef CPUFREQ_SET_VOLTAGE
+#undef CPUFREQ_SET_CLOCK
+
+#elif defined(CONFIG_ARCH_SC8825)
+ set_mcu_clk_freq(freq->new * 1000);
+#endif
+ return;
+}
+
+static void sprd_real_set_cpufreq(struct cpufreq_policy *policy, unsigned int new_speed, int index)
+{
+ mutex_lock(&freq_lock);
+
+ if (global_freqs.old == new_speed) {
+ pr_debug("do nothing for cpu%u, new=old=%u\n",
+ policy->cpu, new_speed);
+ mutex_unlock(&freq_lock);
+ return;
+ }
+ pr_info("--xing-- set %u khz for cpu%u\n",
+ new_speed, policy->cpu);
+ global_freqs.cpu = policy->cpu;
+ global_freqs.new = new_speed;
+
+ cpufreq_notify_transition(policy, &global_freqs, CPUFREQ_PRECHANGE);
+
+ sprd_raw_set_cpufreq(policy->cpu, &global_freqs, index);
+
+ cpufreq_notify_transition(policy, &global_freqs, CPUFREQ_POSTCHANGE);
+
+ global_freqs.old = global_freqs.new;
+
+ mutex_unlock(&freq_lock);
+ return;
+}
+
+static void sprd_find_real_index(unsigned int new_speed, int *index)
+{
+ int i;
+ struct cpufreq_frequency_table *pfreq = sprd_cpufreq_conf->freq_tbl;
+
+ *index = pfreq[0].index;
+ for (i = 0; (pfreq[i].frequency != CPUFREQ_TABLE_END); i++) {
+ if (new_speed == pfreq[i].frequency) {
+ *index = pfreq[i].index;
+ break;
+ }
+ }
+ return;
+}
+
+static int sprd_update_cpu_speed(struct cpufreq_policy *policy,
+ unsigned int target_speed, int index)
+{
+ int i, real_index = 0;
+ unsigned int new_speed = 0;
+
+ /*
+ * CONFIG_NR_CPUS cores are always in the same voltage, at the same
+ * frequency. But, cpu load is calculated individual in each cores,
+ * So we remeber the original target frequency and voltage of core0,
+ * and use the higher one
+ */
+
+ for_each_online_cpu(i) {
+ new_speed = max(new_speed, percpu_target[i]);
+ }
+
+ if (new_speed > sprd_top_frequency)
+ new_speed = sprd_top_frequency;
+
+ if (new_speed != sprd_cpufreq_conf->freq_tbl[index].frequency)
+ sprd_find_real_index(new_speed, &real_index);
+ else
+ real_index = index;
+ sprd_real_set_cpufreq(policy, new_speed, real_index);
+ return 0;
+}
+
+static int sprd_cpufreq_verify_speed(struct cpufreq_policy *policy)
+{
+ if (policy->cpu > CONFIG_NR_CPUS) {
+ pr_err("%s no such cpu id %d\n", __func__, policy->cpu);
+ return -EINVAL;
+ }
+
+ return cpufreq_frequency_table_verify(policy, sprd_cpufreq_conf->freq_tbl);
+}
+
+unsigned int cpufreq_min_limit = ULONG_MAX;
+unsigned int cpufreq_max_limit = 0;
+unsigned int dvfs_score_select = 5;
+unsigned int dvfs_unplug_select = 2;
+unsigned int dvfs_plug_select = 0;
+unsigned int dvfs_score_hi[4] = {0};
+unsigned int dvfs_score_mid[4] = {0};
+unsigned int dvfs_score_critical[4] = {0};
+extern unsigned int percpu_load[4];
+extern unsigned int cur_window_size[4];
+extern unsigned int cur_window_index[4];
+extern unsigned int ga_percpu_total_load[4][8];
+
+static DEFINE_SPINLOCK(cpufreq_state_lock);
+
+static int sprd_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ int ret = -EFAULT;
+ int index;
+ unsigned int new_speed;
+ struct cpufreq_frequency_table *table;
+ int max_freq = cpufreq_max_limit;
+ int min_freq = cpufreq_min_limit;
+ int cur_freq = 0;
+ unsigned long irq_flags;
+
+ /* delay 30s to enable dvfs&dynamic-hotplug,
+ * except requirment from termal-cooling device
+ */
+ if(time_before(jiffies, boot_done)){
+ return 0;
+ }
+
+ if((target_freq < min_freq) || (target_freq > max_freq))
+ {
+ pr_err("invalid target_freq: %d min_freq %d max_freq %d\n", target_freq,min_freq,max_freq);
+ return -EINVAL;
+ }
+ table = cpufreq_frequency_get_table(policy->cpu);
+
+ if (cpufreq_frequency_table_target(policy, table,
+ target_freq, relation, &index)) {
+ pr_err("invalid target_freq: %d\n", target_freq);
+ return -EINVAL;
+ }
+
+ pr_debug("CPU_%d target %d relation %d (%d-%d) selected %d\n",
+ policy->cpu, target_freq, relation,
+ policy->min, policy->max, table[index].frequency);
+
+ new_speed = table[index].frequency;
+
+ percpu_target[policy->cpu] = new_speed;
+ pr_debug("%s cpu:%d new_speed:%u on cpu%d\n",
+ __func__, policy->cpu, new_speed, smp_processor_id());
+
+ ret = sprd_update_cpu_speed(policy, new_speed, index);
+
+ return ret;
+
+}
+
+static unsigned int sprd_cpufreq_getspeed(unsigned int cpu)
+{
+ if (cpu > CONFIG_NR_CPUS) {
+ pr_err("%s no such cpu id %d\n", __func__, cpu);
+ return -EINVAL;
+ }
+
+ return sprd_raw_get_cpufreq();
+}
+
+static void sprd_set_cpureq_limit(void)
+{
+ int i;
+ struct cpufreq_frequency_table *tmp = sprd_cpufreq_conf->freq_tbl;
+ for (i = 0; (tmp[i].frequency != CPUFREQ_TABLE_END); i++) {
+ cpufreq_min_limit = min(tmp[i].frequency, cpufreq_min_limit);
+ cpufreq_max_limit = max(tmp[i].frequency, cpufreq_max_limit);
+ }
+ pr_info("--xing-- %s max=%u min=%u\n", __func__, cpufreq_max_limit, cpufreq_min_limit);
+}
+
+#if defined(CONFIG_ARCH_SCX35LT8)
+#define AON_APB_CHIP_ID REG_AON_APB_CHIP_ID0
+#else
+#define AON_APB_CHIP_ID REG_AON_APB_CHIP_ID
+#endif
+static int sprd_freq_table_init(void)
+{
+ /* we init freq table here depends on which chip being used */
+ if (soc_is_scx35_v0()) {
+ pr_info("%s es_chip\n", __func__);
+ sprd_cpufreq_conf->freq_tbl =
+ sc8830_cpufreq_table_data_es.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc8830_cpufreq_table_data_es.vddarm_mv;
+ } else if (soc_is_scx35_v1()) {
+ pr_info("%s cs_chip\n", __func__);
+ sprd_cpufreq_conf->freq_tbl =
+ sc8830_cpufreq_table_data_cs.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc8830_cpufreq_table_data_cs.vddarm_mv;
+ } else if (soc_is_sc7715()) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc7715_cpufreq_table_data.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc7715_cpufreq_table_data.vddarm_mv;
+ } else if (soc_is_scx35g_v0()) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc8830t_cpufreq_table_data_es.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc8830t_cpufreq_table_data_es.vddarm_mv;
+ sprd_cpufreq_conf->max_axi_freq = 500000;
+ } else if (soc_is_scx30g2_v0()) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc8730c_cpufreq_table_data.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc8730c_cpufreq_table_data.vddarm_mv;
+ sprd_cpufreq_conf->max_axi_freq = 500000;
+ } else if (soc_is_scx9630_v0() || soc_is_scx9830i_v0()) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc9630_cpufreq_table_data.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc9630_cpufreq_table_data.vddarm_mv;
+ } else if (soc_is_scx9820_v0()) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc9820_cpufreq_table_data.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc9820_cpufreq_table_data.vddarm_mv;
+ } else if (soc_is_scx7720_v0()) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc7720_cpufreq_table_data.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc7720_cpufreq_table_data.vddarm_mv;
+#if defined (CONFIG_ARCH_SCX35LT8) //TODO
+ } else if(__raw_readl(REG_AON_APB_CHIP_ID0) == 0x96310000){
+ sprd_cpufreq_conf->freq_tbl =
+ sc9631l64_cpufreq_table_data_es.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc9631l64_cpufreq_table_data_es.vddarm_mv;
+#else
+ } else if (__raw_readl(AON_APB_CHIP_ID) == 0x96310000) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc9631l64_cpufreq_table_data_es.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc9631l64_cpufreq_table_data_es.vddarm_mv;
+#endif
+ } else {
+#if defined(CONFIG_ARCH_SCX35LT8)
+ pr_info("D-die chip id = 0x%08X\n", __raw_readl(REG_AON_APB_CHIP_ID0));
+#endif
+ pr_err("%s error chip id\n", __func__);
+ return -EINVAL;
+ }
+ pr_info("sprd_freq_table_init \n");
+ sprd_set_cpureq_limit();
+ return 0;
+}
+
+static int sprd_cpufreq_init(struct cpufreq_policy *policy)
+{
+ int ret;
+
+ cpufreq_frequency_table_cpuinfo(policy, sprd_cpufreq_conf->freq_tbl);
+ policy->cur = sprd_raw_get_cpufreq(); /* current cpu frequency: KHz*/
+ /*
+ * transition_latency 5us is enough now
+ * but sampling too often, unbalance and irregular on each online cpu
+ * so we set 500us here.
+ */
+ policy->cpuinfo.transition_latency = TRANSITION_LATENCY;
+ policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
+
+ cpufreq_frequency_table_get_attr(sprd_cpufreq_conf->freq_tbl, policy->cpu);
+
+ percpu_target[policy->cpu] = policy->cur;
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, sprd_cpufreq_conf->freq_tbl);
+ if (ret != 0)
+ pr_err("%s Failed to config freq table: %d\n", __func__, ret);
+
+
+ pr_info("%s policy->cpu=%d, policy->cur=%u, ret=%d\n",
+ __func__, policy->cpu, policy->cur, ret);
+
+ cpumask_setall(policy->cpus);
+
+ return ret;
+}
+
+static int sprd_cpufreq_exit(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+
+static struct freq_attr *sprd_cpufreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver sprd_cpufreq_driver = {
+ .verify = sprd_cpufreq_verify_speed,
+ .target = sprd_cpufreq_target,
+ .get = sprd_cpufreq_getspeed,
+ .init = sprd_cpufreq_init,
+ .exit = sprd_cpufreq_exit,
+ .name = "sprd",
+ .attr = sprd_cpufreq_attr,
+#if defined(CONFIG_ARCH_SCX35)
+ .flags = CPUFREQ_SHARED
+#endif
+};
+
+static ssize_t cpufreq_min_limit_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ memcpy(buf,&cpufreq_min_limit,sizeof(int));
+ return sizeof(int);
+}
+
+static ssize_t cpufreq_max_limit_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ memcpy(buf,&cpufreq_max_limit,sizeof(int));
+ return sizeof(int);
+}
+
+static ssize_t cpufreq_min_limit_debug_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ snprintf(buf,10,"%d\n",cpufreq_min_limit);
+ return strlen(buf) + 1;
+}
+
+static ssize_t cpufreq_max_limit_debug_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ snprintf(buf,10,"%d\n",cpufreq_max_limit);
+ return strlen(buf) + 1;
+}
+
+static ssize_t cpufreq_max_axi_freq_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ snprintf(buf, 10, "%d\n", sprd_cpufreq_conf->max_axi_freq);
+ return strlen(buf) + 1;
+}
+
+static ssize_t cpufreq_max_axi_freq_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int value;
+ int temp,max_freq = 0;
+ int i,j;
+
+ strict_strtoul(buf, 16, (long unsigned int *)&value);
+ i = 0;
+ do{
+ temp = value & 0xf;
+ for(j = 0; j < i; j++)
+ temp = temp * 10;
+ max_freq += temp;
+ value = value >> 4;
+ i++;
+ } while(value);
+
+ sprd_cpufreq_conf->max_axi_freq = max_freq;
+ return count;
+}
+
+static ssize_t cpufreq_min_limit_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ spin_lock_irqsave(&cpufreq_state_lock, irq_flags);
+ /*
+ for debug use
+ echo 0xabcde258 > /sys/power/cpufreq_min_limit means set the minimum limit to 600Mhz
+ */
+ if((value & 0xfffff000) == 0xabcde000)
+ {
+ cpufreq_min_limit = value & 0x00000fff;
+ cpufreq_min_limit *= 1000;
+ printk(KERN_ERR"cpufreq_min_limit value %s %d\n",buf,cpufreq_min_limit);
+ }
+ else
+ {
+ cpufreq_min_limit = *(int *)buf;
+ }
+ spin_unlock_irqrestore(&cpufreq_state_lock, irq_flags);
+ return count;
+}
+
+static ssize_t cpufreq_max_limit_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ spin_lock_irqsave(&cpufreq_state_lock, irq_flags);
+
+ /*
+ for debug use
+ echo 0xabcde4b0 > /sys/power/cpufreq_max_limit means set the maximum limit to 1200Mhz
+ */
+ if((value & 0xfffff000) == 0xabcde000)
+ {
+ cpufreq_max_limit = value & 0x00000fff;
+ cpufreq_max_limit *= 1000;
+ printk(KERN_ERR"cpufreq_max_limit value %s %d\n",buf,cpufreq_max_limit);
+ }
+ else
+ {
+ cpufreq_max_limit = *(int *)buf;
+ }
+ spin_unlock_irqrestore(&cpufreq_state_lock, irq_flags);
+
+ return count;
+}
+
+#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SPRDEMAND
+static ssize_t dvfs_score_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ printk(KERN_ERR"dvfs_score_input %x\n",value);
+
+ dvfs_score_select = (value >> 24) & 0x0f;
+ if(dvfs_score_select < 4)
+ {
+ dvfs_score_critical[dvfs_score_select] = (value >> 16) & 0xff;
+ dvfs_score_hi[dvfs_score_select] = (value >> 8) & 0xff;
+ dvfs_score_mid[dvfs_score_select] = value & 0xff;
+ }
+
+
+ return count;
+}
+
+static ssize_t dvfs_score_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ int ret = 0;
+
+ ret = snprintf(buf + ret,50,"dvfs_score_select %d\n",dvfs_score_select);
+ ret += snprintf(buf + ret,200,"dvfs_score_critical[1] = %d dvfs_score_hi[1] = %d dvfs_score_mid[1] = %d\n",dvfs_score_critical[1],dvfs_score_hi[1],dvfs_score_mid[1]);
+ ret += snprintf(buf + ret,200,"dvfs_score_critical[2] = %d dvfs_score_hi[2] = %d dvfs_score_mid[2] = %d\n",dvfs_score_critical[2],dvfs_score_hi[2],dvfs_score_mid[2]);
+ ret += snprintf(buf + ret,200,"dvfs_score_critical[3] = %d dvfs_score_hi[3] = %d dvfs_score_mid[3] = %d\n",dvfs_score_critical[3],dvfs_score_hi[3],dvfs_score_mid[3]);
+
+ ret += snprintf(buf + ret,200,"percpu_total_load[0] = %d,%d->%d\n",
+ percpu_load[0],ga_percpu_total_load[0][(cur_window_index[0] - 1 + 10) % 10],ga_percpu_total_load[0][cur_window_index[0]]);
+ ret += snprintf(buf + ret,200,"percpu_total_load[1] = %d,%d->%d\n",
+ percpu_load[1],ga_percpu_total_load[1][(cur_window_index[1] - 1 + 10) % 10],ga_percpu_total_load[1][cur_window_index[1]]);
+ ret += snprintf(buf + ret,200,"percpu_total_load[2] = %d,%d->%d\n",
+ percpu_load[2],ga_percpu_total_load[2][(cur_window_index[2] - 1 + 10) % 10],ga_percpu_total_load[2][cur_window_index[2]]);
+ ret += snprintf(buf + ret,200,"percpu_total_load[3] = %d,%d->%d\n",
+ percpu_load[3],ga_percpu_total_load[3][(cur_window_index[3] - 1 + 10) % 10],ga_percpu_total_load[3][cur_window_index[3]]);
+
+ return strlen(buf) + 1;
+}
+
+static ssize_t dvfs_unplug_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ printk(KERN_ERR"dvfs_score_input %x\n",value);
+
+ dvfs_unplug_select = (value >> 24) & 0x0f;
+ if(dvfs_unplug_select > 7)
+ {
+ cur_window_size[0]= (value >> 8) & 0xff;
+ cur_window_size[1]= (value >> 8) & 0xff;
+ cur_window_size[2]= (value >> 8) & 0xff;
+ cur_window_size[3]= (value >> 8) & 0xff;
+ }
+ return count;
+}
+
+static ssize_t dvfs_unplug_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ int ret = 0;
+
+ ret = snprintf(buf + ret,50,"dvfs_unplug_select %d\n",dvfs_unplug_select);
+ ret += snprintf(buf + ret,100,"cur_window_size[0] = %d\n",cur_window_size[0]);
+ ret += snprintf(buf + ret,100,"cur_window_size[1] = %d\n",cur_window_size[1]);
+ ret += snprintf(buf + ret,100,"cur_window_size[2] = %d\n",cur_window_size[2]);
+ ret += snprintf(buf + ret,100,"cur_window_size[3] = %d\n",cur_window_size[3]);
+
+ return strlen(buf) + 1;
+}
+
+
+static ssize_t dvfs_plug_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ printk(KERN_ERR"dvfs_plug_select %x\n",value);
+
+ dvfs_plug_select = (value ) & 0x0f;
+ return count;
+}
+
+
+static ssize_t dvfs_plug_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ int ret = 0;
+
+ ret = snprintf(buf + ret,50,"dvfs_plug_select %d\n",dvfs_plug_select);
+
+ return strlen(buf) + 1;
+}
+#endif
+
+static ssize_t cpufreq_table_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ memcpy(buf,sprd_cpufreq_conf->freq_tbl,sizeof(* sprd_cpufreq_conf->freq_tbl));
+ return sizeof(* sprd_cpufreq_conf->freq_tbl);
+}
+
+static ssize_t dvfs_prop_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ printk(KERN_ERR"dvfs_status %s\n",buf);
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ printk(KERN_ERR"dvfs_plug_select %x\n",value);
+
+ dvfs_plug_select = (value ) & 0x0f;
+ return count;
+}
+
+static ssize_t dvfs_prop_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ int ret = 0;
+
+ ret = snprintf(buf + ret,50,"dvfs_plug_select %d\n",dvfs_plug_select);
+
+ return strlen(buf) + 1;
+}
+
+#ifdef CONFIG_SPRD_AVS_DEBUG
+extern unsigned int g_avs_log_flag;
+
+static ssize_t avs_log_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ printk(KERN_ERR"g_avs_log_flag %s\n",buf);
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ printk(KERN_ERR"g_avs_log_flag %x\n",value);
+
+ g_avs_log_flag = (value ) & 0x0f;
+ return count;
+}
+
+static ssize_t avs_log_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ int ret = 0;
+
+ ret = snprintf(buf + ret,50,"g_avs_log_flag %d\n",g_avs_log_flag);
+
+ return strlen(buf) + 1;
+}
+#endif
+static DEVICE_ATTR(cpufreq_min_limit, 0660, cpufreq_min_limit_show, cpufreq_min_limit_store);
+static DEVICE_ATTR(cpufreq_max_limit, 0660, cpufreq_max_limit_show, cpufreq_max_limit_store);
+static DEVICE_ATTR(cpufreq_min_limit_debug, 0440, cpufreq_min_limit_debug_show, NULL);
+static DEVICE_ATTR(cpufreq_max_limit_debug, 0440, cpufreq_max_limit_debug_show, NULL);
+static DEVICE_ATTR(cpufreq_table, 0440, cpufreq_table_show, NULL);
+static DEVICE_ATTR(cpufreq_max_axi_freq, 0660, cpufreq_max_axi_freq_show, cpufreq_max_axi_freq_store);
+
+#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SPRDEMAND
+static DEVICE_ATTR(dvfs_score, 0660, dvfs_score_show, dvfs_score_store);
+static DEVICE_ATTR(dvfs_unplug, 0660, dvfs_unplug_show, dvfs_unplug_store);
+static DEVICE_ATTR(dvfs_plug, 0660, dvfs_plug_show, dvfs_plug_store);
+#endif
+static DEVICE_ATTR(dvfs_prop, 0660, dvfs_prop_show, dvfs_prop_store);
+
+#ifdef CONFIG_SPRD_AVS_DEBUG
+static DEVICE_ATTR(avs_log, 0660, avs_log_show, avs_log_store);
+#endif
+static struct attribute *g[] = {
+ &dev_attr_cpufreq_min_limit.attr,
+ &dev_attr_cpufreq_max_limit.attr,
+ &dev_attr_cpufreq_min_limit_debug.attr,
+ &dev_attr_cpufreq_max_limit_debug.attr,
+ &dev_attr_cpufreq_table.attr,
+ &dev_attr_cpufreq_max_axi_freq.attr,
+#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SPRDEMAND
+ &dev_attr_dvfs_score.attr,
+ &dev_attr_dvfs_unplug.attr,
+ &dev_attr_dvfs_plug.attr,
+#endif
+ &dev_attr_dvfs_prop.attr,
+#ifdef CONFIG_SPRD_AVS_DEBUG
+ &dev_attr_avs_log.attr,
+#endif
+ NULL,
+};
+
+static struct attribute_group attr_group = {
+ .attrs = g,
+};
+
+static int sprd_cpufreq_policy_notifier(
+ struct notifier_block *nb, unsigned long event, void *data)
+{
+ return NOTIFY_OK;
+}
+
+static struct notifier_block sprd_cpufreq_policy_nb = {
+ .notifier_call = sprd_cpufreq_policy_notifier,
+};
+
+static int __init sprd_cpufreq_modinit(void)
+{
+ int ret;
+#if defined(CONFIG_SPRD_CPUFREQ_DT_DRIVER)
+ struct platform_device_info devinfo = { .name = "cpufreq-dt-sprd", };
+
+ platform_device_register_full(&devinfo);
+
+ return;
+#endif
+
+#if defined(CONFIG_ARCH_SCX35)
+ sprd_cpufreq_conf = &sc8830_cpufreq_conf;
+#elif defined(CONFIG_ARCH_SC8825)
+ sprd_cpufreq_conf = &sc8825_cpufreq_conf;
+#endif
+
+#if defined(CONFIG_ARCH_SCX35)
+ ret = sprd_freq_table_init();
+ if (ret)
+ return ret;
+
+ sprd_top_frequency = sprd_cpufreq_conf->freq_tbl[0].frequency;
+ /* TODO:need verify for the initialization of limited max freq */
+
+ sprd_cpufreq_conf->clk = clk_get_sys(NULL, "clk_mcu");
+ if (IS_ERR(sprd_cpufreq_conf->clk))
+ return PTR_ERR(sprd_cpufreq_conf->clk);
+
+ sprd_cpufreq_conf->mpllclk = clk_get_sys(NULL, "clk_mpll");
+ if (IS_ERR(sprd_cpufreq_conf->mpllclk))
+ return PTR_ERR(sprd_cpufreq_conf->mpllclk);
+
+#if !defined(CONFIG_ARCH_SCX35L) && !defined(CONFIG_ARCH_SCX20)
+ sprd_cpufreq_conf->tdpllclk = clk_get_sys(NULL, "clk_tdpll");
+ if (IS_ERR(sprd_cpufreq_conf->tdpllclk))
+ return PTR_ERR(sprd_cpufreq_conf->tdpllclk);
+#else
+// sprd_cpufreq_conf->tdpllclk = clk_get_sys(NULL, "clk_twpll");
+ sprd_cpufreq_conf->tdpllclk = clk_get_sys(NULL, "clk_768m");
+ if (IS_ERR(sprd_cpufreq_conf->tdpllclk))
+ return PTR_ERR(sprd_cpufreq_conf->tdpllclk);
+#endif
+ mutex_init(&cpufreq_vddarm_lock);
+
+ sprd_cpufreq_conf->regulator = regulator_get(NULL, "vddarm");
+
+ if (IS_ERR(sprd_cpufreq_conf->regulator))
+ return PTR_ERR(sprd_cpufreq_conf->regulator);
+
+ /* set max voltage first */
+ /*
+ regulator_set_voltage(sprd_cpufreq_conf->regulator,
+ sprd_cpufreq_conf->vddarm_mv[0],
+ sprd_cpufreq_conf->vddarm_mv[0]);
+ */
+ clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->tdpllclk);
+ /*
+ * clk_set_rate(sprd_cpufreq_conf->mpllclk, (sprd_top_frequency * 1000));
+ */
+ clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->mpllclk);
+ global_freqs.old = sprd_raw_get_cpufreq();
+
+#endif
+
+ boot_done = jiffies + DVFS_BOOT_TIME;
+ ret = cpufreq_register_notifier(
+ &sprd_cpufreq_policy_nb, CPUFREQ_POLICY_NOTIFIER);
+ if (ret)
+ return ret;
+
+ ret = cpufreq_register_driver(&sprd_cpufreq_driver);
+
+ ret = sysfs_create_group(power_kobj, &attr_group);
+ return ret;
+}
+
+static void __exit sprd_cpufreq_modexit(void)
+{
+#if defined(CONFIG_ARCH_SCX35)
+ if (!IS_ERR_OR_NULL(sprd_cpufreq_conf->regulator))
+ regulator_put(sprd_cpufreq_conf->regulator);
+#endif
+ cpufreq_unregister_driver(&sprd_cpufreq_driver);
+ cpufreq_unregister_notifier(
+ &sprd_cpufreq_policy_nb, CPUFREQ_POLICY_NOTIFIER);
+ return;
+}
+
+module_init(sprd_cpufreq_modinit);
+module_exit(sprd_cpufreq_modexit);
+
+MODULE_DESCRIPTION("cpufreq driver for Spreadtrum");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/sprd/cpuidle-scx35.c b/drivers/platform/sprd/cpuidle-scx35.c
new file mode 100644
index 00000000..fb452336
--- /dev/null
+++ b/drivers/platform/sprd/cpuidle-scx35.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2013 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/cpuidle.h>
+#include <linux/cpu_pm.h>
+#include <linux/export.h>
+#include <linux/clockchips.h>
+#include <linux/suspend.h>
+#include <asm/proc-fns.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/cpuidle.h>
+
+
+/*#define SC_IDLE_DEBUG 1*/
+extern u32 emc_clk_get(void);
+#ifdef SC_IDLE_DEBUG
+unsigned int idle_debug_state[NR_CPUS];
+#endif
+
+#define SC_CPUIDLE_STATE_NUM ARRAY_SIZE(cpuidle_params_table)
+#define WAIT_WFI_TIMEOUT (20)
+#define LIGHT_SLEEP_ENABLE (BIT_MCU_LIGHT_SLEEP_EN)
+#define IDLE_DEEP_DELAY_TIME (70 * HZ)
+static unsigned long delay_done;
+static unsigned int idle_disabled_by_suspend;
+#if defined(CONFIG_ARCH_SCX15)
+static unsigned int zipenc_status;
+static unsigned int zipdec_status;
+#endif
+
+ static int light_sleep_en = 1;
+
+
+static int idle_deep_en = 0;
+static int cpuidle_debug = 0;
+module_param_named(cpuidle_debug, cpuidle_debug, int, S_IRUGO | S_IWUSR);
+module_param_named(light_sleep_en, light_sleep_en, int, S_IRUGO | S_IWUSR);
+module_param_named(idle_deep_en, idle_deep_en, int, S_IRUGO | S_IWUSR);
+/* Machine specific information to be recorded in the C-state driver_data */
+struct sc_idle_statedata {
+ u32 cpu_state;
+ u32 mpu_logic_state;
+ u32 mpu_state;
+ u8 valid;
+};
+
+/*
+ * cpuidle mach specific parameters
+ * Can board code can override the default C-states definition???
+ */
+struct cpuidle_params {
+ u32 exit_latency; /* exit_latency = sleep + wake-up latencies */
+ u32 target_residency;
+ u32 power_usage;
+ u8 valid;
+};
+
+/*
+* TODO: chip parameters
+*/
+static struct cpuidle_params cpuidle_params_table[] = {
+ /* WFI */
+ {.exit_latency = 1 , .target_residency = 1, .valid = 1},
+ /* LIGHT SLEEP */
+ {.exit_latency = 100 , .target_residency = 1000, .valid = 1},
+ /* CPU CORE POWER DOWN */
+ {.exit_latency = 20000 , .target_residency = 50000, .valid = 1},
+};
+
+
+struct sc_idle_statedata sc8830_idle_data[SC_CPUIDLE_STATE_NUM];
+char* sc_cpuidle_desc[ SC_CPUIDLE_STATE_NUM] = {
+ "standby",
+ "l_sleep",
+ "core_pd",
+};
+
+enum {
+ STANDBY = 0, /* wfi */
+ L_SLEEP, /* light sleep */
+ CORE_PD, /* cpu core power down except cpu0 */
+};
+
+static BLOCKING_NOTIFIER_HEAD(sc_cpuidle_chain_head);
+int register_sc_cpuidle_notifier(struct notifier_block *nb)
+{
+ printk("*** %s, nb->notifier_call:%pf ***\n", __func__, nb->notifier_call );
+ return blocking_notifier_chain_register(&sc_cpuidle_chain_head, nb);
+}
+EXPORT_SYMBOL_GPL(register_sc_cpuidle_notifier);
+int unregister_sc_cpuidle_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&sc_cpuidle_chain_head, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_sc_cpuidle_notifier);
+int sc_cpuidle_notifier_call_chain(unsigned long val)
+{
+ int ret = blocking_notifier_call_chain(&sc_cpuidle_chain_head, val, NULL);
+ return notifier_to_errno(ret);
+}
+
+static void set_cpu_pd(void *data)
+{
+ int cpu_id = *((int*)data);
+ int on_cpu = smp_processor_id();
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(WAIT_WFI_TIMEOUT);
+
+ if(on_cpu != 0)
+ panic(" this function only can excute on cpu0 \n");
+ if(cpu_id == 0)
+ panic(" cpu0 can not power down \n");
+
+ /*
+ * TODO: set cpu power down
+ while (!(__raw_readl(REG_AP_AHB_CA7_STANDBY_STATUS) & (1<<cpu_id))) {
+ if (time_after(jiffies, timeout)) {
+ printk(" waiting for cpu%d wfi time out \n", cpu_id);
+ return;
+ }
+ }
+ printk(" set cpu%d power down\n", cpu_id);
+ */
+
+ return;
+}
+
+static void sc_cpuidle_debug(void)
+{
+ unsigned int val = sci_glb_read(REG_AP_AHB_MCU_PAUSE, -1UL);
+ if( val&BIT_MCU_LIGHT_SLEEP_EN ){
+ printk("*** %s, REG_AP_AHB_MCU_PAUSE:0x%x ***\n", __func__, val );
+ printk("*** %s, REG_AP_AHB_AHB_EB:0x%x ***\n",
+ __func__, sci_glb_read(REG_AP_AHB_AHB_EB, -1UL));
+ printk("*** %s, REG_AON_APB_APB_EB0:0x%x ***\n",
+ __func__, sci_glb_read(REG_AON_APB_APB_EB0, -1UL));
+ printk("*** %s, REG_AP_AHB_CA7_STANDBY_STATUS:0x%x ***\n",
+ __func__, sci_glb_read(REG_AP_AHB_CA7_STANDBY_STATUS, -1UL));
+ printk("*** %s, REG_PMU_APB_CP_SLP_STATUS_DBG0:0x%x ***\n",
+ __func__, sci_glb_read(REG_PMU_APB_CP_SLP_STATUS_DBG0, -1UL));
+#if !defined(CONFIG_ARCH_SCX35L)
+ printk("*** %s, REG_PMU_APB_CP_SLP_STATUS_DBG1:0x%x ***\n",
+ __func__, sci_glb_read(REG_PMU_APB_CP_SLP_STATUS_DBG1, -1UL));
+#endif
+ printk("*** %s, DDR_OP_MODE:0x%x ***\n",
+ __func__, __raw_readl(SPRD_LPDDR2_BASE + 0x03fc) );
+ }else
+ printk("*** %s, LIGHT_SLEEP_EN can not be set ***\n", __func__ );
+}
+
+static void sc_cpuidle_light_sleep_en(int cpu)
+{
+ int error;
+/*200M ddr clk is not necessary for SCX30G and SCX35L when light sleep status to be entered*/
+#if defined(CONFIG_PM_DEVFREQ) && !defined(CONFIG_ARCH_SCX30G) && !defined(CONFIG_ARCH_SCX35L)
+ if(emc_clk_get() > 200){
+ sci_glb_clr(REG_AP_AHB_MCU_PAUSE, LIGHT_SLEEP_ENABLE | BIT_MCU_SYS_SLEEP_EN);
+ return;
+ }
+#endif
+ /*
+ * if DAP clock is disabled, arm core can not be attached.
+ * it is no necessary only disable DAP in core 0, just for debug
+ */
+
+ if(light_sleep_en){
+ if (cpu == 0) {
+#if defined(CONFIG_ARCH_SCX15)
+ if (__raw_readl(REG_AP_AHB_AHB_EB) & BIT_ZIPDEC_EB) {
+ sci_glb_clr(REG_AP_AHB_AHB_EB, BIT_ZIPDEC_EB);
+ zipdec_status = 1;
+ }
+ if (__raw_readl(REG_AP_AHB_AHB_EB) & BIT_ZIPENC_EB) {
+ sci_glb_clr(REG_AP_AHB_AHB_EB, BIT_ZIPENC_EB);
+ zipenc_status = 1;
+ }
+#endif
+ sci_glb_clr(REG_AON_APB_APB_EB0, BIT_CA7_DAP_EB);
+ if (!(sci_glb_read(REG_AP_AHB_AHB_EB, -1UL) & BIT_DMA_EB) && (num_online_cpus() == 1)) {
+ error = sc_cpuidle_notifier_call_chain(SC_CPUIDLE_PREPARE);
+ if (error) {
+ sci_glb_clr(REG_AP_AHB_MCU_PAUSE, BIT_MCU_SYS_SLEEP_EN);
+ pr_debug("could not set %s ... \n", __func__);
+ }
+ else
+ sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_SYS_SLEEP_EN);
+ }
+ }
+ sci_glb_set(REG_AP_AHB_MCU_PAUSE, LIGHT_SLEEP_ENABLE);
+ }
+ if(cpuidle_debug){
+ sc_cpuidle_debug();
+ }
+}
+
+/*
+* light sleep must be disabled before deep-sleep
+*/
+static void sc_cpuidle_light_sleep_dis(void)
+{
+ if(light_sleep_en){
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT_CA7_DAP_EB);
+ sci_glb_clr(REG_AP_AHB_MCU_PAUSE, LIGHT_SLEEP_ENABLE | BIT_MCU_SYS_SLEEP_EN);
+#if defined(CONFIG_ARCH_SCX15)
+ if (zipdec_status)
+ sci_glb_set(REG_AP_AHB_AHB_EB, BIT_ZIPDEC_EB);
+ if (zipenc_status)
+ sci_glb_set(REG_AP_AHB_AHB_EB, BIT_ZIPENC_EB);
+ zipenc_status = 0;
+ zipdec_status = 0;
+#endif
+ }
+ return;
+}
+extern void deep_sleep(int);
+static void idle_into_deep(void)
+{
+ if (sci_glb_read(REG_AP_AHB_AHB_EB, -1UL) &
+ (BIT_DMA_EB | BIT_USB_EB | BIT_EMMC_EB | BIT_NFC_EB | BIT_SDIO0_EB |
+ BIT_SDIO1_EB | BIT_SDIO2_EB)) {
+ /* so we've already known ap can't go into deep now, we just do idle */
+ cpu_do_idle();
+ return;
+ }
+ /* ignore uart1_eb for uart output log now */
+ if (sci_glb_read(REG_AP_APB_APB_EB, -1UL) &
+ (BIT_UART0_EB |BIT_UART2_EB | BIT_UART3_EB | BIT_UART4_EB |
+ BIT_I2C0_EB | BIT_I2C1_EB |BIT_I2C2_EB | BIT_I2C3_EB | BIT_I2C4_EB |
+ BIT_IIS0_EB | BIT_IIS1_EB | BIT_IIS2_EB | BIT_IIS3_EB)) {
+ /* so we've already known ap can't go into deep now, we just do idle
+ * maybe we can go into light sleep or sys sleep
+ */
+ cpu_do_idle();
+ return;
+ }
+
+ if (sci_glb_read(REG_AON_APB_APB_EB0, -1UL) &
+ (BIT_MM_EB |BIT_GPU_EB)) {
+ cpu_do_idle();
+ return;
+ }
+
+ /* It is a chip design defect(MSPI CLOCK SELECTION)
+ * AP can not enter deep sleep mode when FM is working, because mspi clock is from CPLL,
+ * and it can not switch to 26MHz frequently in idle.
+ */
+ if (sci_glb_read(REG_AON_APB_APB_EB0, -1UL) & BIT_FM_EB) {
+ cpu_do_idle();
+ return;
+ }
+
+ sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_DEEP_SLEEP_EN /*| BIT_MCU_SLEEP_FOLLOW_CA7_EN*/);
+ deep_sleep(1);
+ sci_glb_clr(REG_AP_AHB_MCU_PAUSE, BIT_MCU_DEEP_SLEEP_EN /*| BIT_MCU_SLEEP_FOLLOW_CA7_EN*/);
+}
+
+/**
+ * sc_enter_idle - Programs arm core to enter the specified state
+ * @dev: cpuidle device
+ * @drv: cpuidle driver
+ * @index: the index of state to be entered
+ *
+ * Called from the CPUidle framework to program the device to the
+ * specified low power state selected by the governor.
+ * Returns the index of power state.
+ */
+static int sc_enter_idle(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ int cpu_id = smp_processor_id();
+ ktime_t enter, exit;
+ s64 us;
+
+ local_irq_disable();
+ local_fiq_disable();
+
+ enter = ktime_get();
+
+#ifdef SC_IDLE_DEBUG
+ idle_debug_state[cpu_id] = index;
+ if(index)
+ printk("cpu:%d, index:%d \n", cpu_id, index );
+#endif
+
+ switch(index){
+ case STANDBY:
+ cpu_do_idle();
+ break;
+ case L_SLEEP:
+ /*
+ * TODO: enter light sleep
+ */
+ sc_cpuidle_light_sleep_en(cpu_id);
+ cpu_do_idle();
+ sc_cpuidle_light_sleep_dis();
+ break;
+ case CORE_PD:
+ if(cpu_id != 0){
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
+ /*
+ * TODO:
+ * set irq affinity to cpu0, so irq can be handled on cpu0
+ gic_affinity_to_cpu0(cpu_id);
+ smp_call_function_single(0, set_cpu_pd, &cpu_id, 0);
+ */
+ cpu_do_idle();
+ /*
+ gic_affinity_restore(cpu_id);
+ */
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
+ }else{
+ pr_debug("sc_enter_idle go into core_pd state\n");
+ sc_cpuidle_light_sleep_en(cpu_id);
+#if defined(CONFIG_ARCH_SCX15)
+ if (idle_deep_en && time_after(jiffies, delay_done))
+ idle_into_deep();
+ else
+ cpu_do_idle();
+#else
+ cpu_do_idle();
+#endif
+ sc_cpuidle_light_sleep_dis();
+ }
+ break;
+ default:
+ cpu_do_idle();
+ WARN(1, "CPUIDLE: NO THIS LEVEL!!!");
+ }
+
+ exit = ktime_sub(ktime_get(), enter);
+ us = ktime_to_us(exit);
+
+ dev->last_residency = us;
+
+ local_fiq_enable();
+ local_irq_enable();
+
+ return index;
+}
+
+DEFINE_PER_CPU(struct cpuidle_device, sc8830_idle_dev);
+
+struct cpuidle_driver sc8830_idle_driver = {
+ .name = "sc_cpuidle",
+ .owner = THIS_MODULE,
+};
+
+
+/*
+* sc_fill_cstate: initialize cpu idle status
+* @drv: cpuidle_driver
+* @idx: cpuidle status index
+*/
+static inline void sc_fill_cstate(struct cpuidle_driver *drv, struct cpuidle_device *dev, int idx)
+{
+ char *descr = sc_cpuidle_desc[idx];
+ struct cpuidle_state *state = &drv->states[idx];
+ struct cpuidle_state_usage *state_usage = &dev->states_usage[idx];
+
+ sprintf(state->name, "C%d", idx + 1);
+ strncpy(state->desc, descr, CPUIDLE_DESC_LEN);
+ state->flags = CPUIDLE_FLAG_TIME_VALID;
+ state->exit_latency = cpuidle_params_table[idx].exit_latency;
+ /*TODO*/
+ /*state->power_usage = cpuidle_params_table[idx].power_usage;*/
+ state->target_residency = cpuidle_params_table[idx].target_residency;
+ state->enter = sc_enter_idle;
+
+ /*TODO*/
+ /*state_usage->driver_data = ;*/
+}
+
+static int sc_cpuidle_register_device(struct cpuidle_driver *drv, unsigned int cpu)
+{
+ struct cpuidle_device *dev;
+ int state_idx;
+
+ dev = &per_cpu(sc8830_idle_dev, cpu);
+ dev->cpu = cpu;
+ pr_info("%s, cpu:%d \n", __func__, cpu);
+ for(state_idx=0; state_idx<SC_CPUIDLE_STATE_NUM; state_idx++){
+ sc_fill_cstate(drv, dev, state_idx);
+ }
+ dev->state_count = state_idx;
+
+ if (cpuidle_register_device(dev)) {
+ pr_err("CPU%u: failed to register idle device\n", cpu);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sc_cpuidle_pm_notify(struct notifier_block *nb,
+ unsigned long event, void *dummy)
+{
+#ifdef CONFIG_PM_SLEEP
+ if (event == PM_SUSPEND_PREPARE)
+ idle_disabled_by_suspend = true;
+ else if (event == PM_POST_SUSPEND)
+ idle_disabled_by_suspend = false;
+#endif
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block sc_cpuidle_pm_notifier = {
+ .notifier_call = sc_cpuidle_pm_notify,
+};
+
+int __init sc_cpuidle_init(void)
+{
+ unsigned int cpu_id = 0;
+ struct cpuidle_driver *drv = &sc8830_idle_driver;
+ drv->safe_state_index = 0;
+ drv->state_count = SC_CPUIDLE_STATE_NUM;
+ pr_info("%s, enter, drv->state_count:%d \n", __func__, drv->state_count );
+
+ cpuidle_register_driver(drv);
+
+ for_each_possible_cpu(cpu_id) {
+ if (sc_cpuidle_register_device(drv, cpu_id))
+ pr_err("CPU%u: error initializing idle loop\n", cpu_id);
+ }
+ delay_done = jiffies + IDLE_DEEP_DELAY_TIME;
+ register_pm_notifier(&sc_cpuidle_pm_notifier);
+
+ return 0;
+
+}
diff --git a/drivers/platform/sprd/dcdc_cal.c b/drivers/platform/sprd/dcdc_cal.c
new file mode 100644
index 00000000..dd6e69e8
--- /dev/null
+++ b/drivers/platform/sprd/dcdc_cal.c
@@ -0,0 +1,417 @@
+#include <linux/bug.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+#include <linux/sort.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <mach/hardware.h>
+
+#include <linux/irqflags.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#include <mach/hardware.h>
+#include <mach/sci.h>
+#include <mach/sci_glb_regs.h>
+#include <mach/adi.h>
+#include <mach/adc.h>
+#include <mach/efuse.h>
+
+#define REG_SYST_VALUE (SPRD_SYSCNT_BASE + 0x0004)
+
+#define debug0(format, arg...) pr_debug("dcdc: " "@@@" format, ## arg)
+#define debug(format, arg...) pr_info("dcdc: " "@@@" format, ## arg)
+#define info(format, arg...) pr_info("dcdc: " "@@@" format, ## arg)
+
+extern int in_calibration(void);
+int sprd_get_adc_cal_type(void);
+uint16_t sprd_get_adc_to_vol(uint16_t data);
+int reguator_is_trimming(void *);
+int regulator_set_trimming(struct regulator *, int, int);
+int regulator_get_trimming_step(struct regulator *, int);
+
+static uint32_t bat_numerators, bat_denominators = 0;
+
+#define CALIBRATE_TO (60 * 1) /* one minute */
+#define MEASURE_TIMES (15)
+
+static int cmp_val(const void *a, const void *b)
+{
+ return *(int *)a - *(int *)b;
+}
+
+int dcdc_adc_get(int adc_chan)
+{
+ int ret;
+ u32 val[MEASURE_TIMES], adc_res = 0, adc_vol = 0;
+ u32 chan_numerators, chan_denominators;
+ uint32_t bat_numerators, bat_denominators;
+
+ struct adc_sample_data adc_data = {
+ .channel_id = adc_chan,
+ .channel_type = 0,
+ .hw_channel_delay = 0,
+ .scale = true,
+ .pbuf = &val[0],
+ .sample_num = MEASURE_TIMES,
+ .sample_bits = 1,
+ .sample_speed = 0,
+ .signal_mode = 0,
+ };
+
+ sci_adc_get_vol_ratio(ADC_CHANNEL_VBAT, 0, &bat_numerators,
+ &bat_denominators);
+
+ sci_adc_get_vol_ratio(adc_chan, true, &chan_numerators,
+ &chan_denominators);
+
+ ret = sci_adc_get_values(&adc_data);
+ if (0 != ret)
+ goto exit;
+
+ /*
+ for(i = 0; i < MEASURE_TIMES; i++) {
+ printk("%d\t", val[i]);
+ }
+ printk("\n");
+ */
+ sort(val, MEASURE_TIMES, sizeof(u32), cmp_val, 0);
+
+ adc_res = val[MEASURE_TIMES / 2];
+ /* info("adc chan %d, value %d\n", adc_chan, adc_res); */
+ adc_vol = sprd_get_adc_to_vol(adc_res) *
+ (bat_numerators * chan_denominators) /
+ (bat_denominators * chan_numerators);
+exit:
+ return adc_vol;
+}
+
+struct dcdc_cal_map {
+ const char *name; /* a-die DCDC or LDO name */
+ int def_on; /* 1: default ON, 0: default OFF */
+ u32 def_vol; /* default voltage (mV), could not read from a-die */
+ u32 cal_sel; /* only one ldo cal can be enable at the same time */
+ int adc_chan; /* multiplexed adc-channel id for LDOs */
+};
+
+struct dcdc_cal_map dcdc_cal_map[] = {
+ {"vddcore", 1, 1100, 0, ADC_CHANNEL_DCDCCORE},
+ {"vddarm", 1, 1200, 0, ADC_CHANNEL_DCDCARM},
+ {"vddmem", 1, 1200, 0, ADC_CHANNEL_DCDCMEM}, /*DDR2 */
+ {"vddmem1", 0, 1800, 0, ADC_CHANNEL_DCDCMEM}, /*DDR1 */
+ {"dcdcldo", 1, 2200, 0, ADC_CHANNEL_DCDCLDO},
+
+ {"vddrf", 1, 2850, BITS_LDO_RF_CAL_EN(-1), ADC_CHANNEL_LDO0},
+ {"avddbb", 1, 3000, BITS_LDO_ABB_CAL_EN(-1), ADC_CHANNEL_LDO0},
+ {"vddcama", 0, 2800, BITS_LDO_CAMA_CAL_EN(-1), ADC_CHANNEL_LDO0},
+
+ {"vdd3v", 0, 3000, BITS_LDO_VDD3V_CAL_EN(-1), ADC_CHANNEL_LDO1},
+ {"vdd28", 1, 2800, BITS_LDO_VDD28_CAL_EN(-1), ADC_CHANNEL_LDO1},
+ {"vddsim0", 0, 1800, BITS_LDO_VSIM_CAL_EN(-1), ADC_CHANNEL_LDO1},
+ {"vddsim1", 0, 1800, BITS_LDO_VSIM_CAL_EN(-1), ADC_CHANNEL_LDO1},
+ {"vddcammot", 0, 2800, BITS_LDO_CAMMOT_CAL_EN(-1), ADC_CHANNEL_LDO1},
+ {"vddsd0", 0, 2800, BITS_LDO_SD0_CAL_EN(-1), ADC_CHANNEL_LDO1},
+ {"vddusb", 0, 3300, BITS_LDO_USB_CAL_EN(-1), ADC_CHANNEL_LDO1},
+ {"vdd_a", 1, 1800, BITS_LDO_DVDD18_CAL_EN(-1), ADC_CHANNEL_LDO1}, /* alias dvdd18 */
+ {"vdd25", 1, 2500, BITS_LDO_VDD25_CAL_EN(-1), ADC_CHANNEL_LDO1},
+
+ {"vddcamio", 0, 1800, BITS_LDO_CAMIO_CAL_EN(-1), ADC_CHANNEL_LDO2},
+ {"vddcamcore", 0, 1500, BITS_LDO_CAMCORE_CAL_EN(-1), ADC_CHANNEL_LDO2},
+ {"vddcmmb1p2", 0, 1200, BITS_LDO_CMMB1P2_CAL_EN(-1), ADC_CHANNEL_LDO2},
+ {"vddcmmb1p8", 0, 1800, BITS_LDO_CMMB1P8_CAL_EN(-1), ADC_CHANNEL_LDO2},
+ {"vdd18", 1, 1800, BITS_LDO_VDD18_CAL_EN(-1), ADC_CHANNEL_LDO2},
+ {"vddsd1", 0, 1800, BITS_LDO_SD1_CAL_EN(-1), ADC_CHANNEL_LDO2},
+ {"vddsd3", 0, 1800, BITS_LDO_SD3_CAL_EN(-1), ADC_CHANNEL_LDO2},
+};
+
+int ldo_adc_get(const char *name)
+{
+ int i, adc_vol;
+ for (i = 0; i < ARRAY_SIZE(dcdc_cal_map); i++) {
+ if (0 == strcmp(name, dcdc_cal_map[i].name))
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(dcdc_cal_map))
+ return -EINVAL; /* not found */
+
+ /* enable ldo cal before adc sampling and ldo calibration */
+ if (0 != dcdc_cal_map[i].cal_sel) {
+ sci_adi_write(ANA_REG_GLB2_LDO_TRIM_SEL,
+ dcdc_cal_map[i].cal_sel, -1);
+ }
+
+ adc_vol = dcdc_adc_get(dcdc_cal_map[i].adc_chan);
+
+ /* close ldo cal */
+ if (0 != dcdc_cal_map[i].cal_sel) {
+ sci_adi_write(ANA_REG_GLB2_LDO_TRIM_SEL, 0, -1);
+ }
+
+ return adc_vol;
+}
+
+/* FIXME: sometime, adc vol is untrusted, not match the real voltage */
+static int __check_adc_validity_one(const char *name, int ctl_vol)
+{
+ int ret = 0;
+ int cal_vol, adc_vol = ldo_adc_get(name);
+ cal_vol = abs(adc_vol - ctl_vol);
+ info("check %s default %dmv, from %dmv, %c%d.%02d%%\n",
+ name, adc_vol, ctl_vol,
+ (adc_vol > ctl_vol) ? '+' : '-',
+ cal_vol * 100 / ctl_vol, cal_vol * 100 * 100 / ctl_vol % 100);
+ ret = cal_vol < ctl_vol / 50; /* margin 2% */
+ return ret;
+}
+
+static int __check_adc_validity(void)
+{
+ int ret = 0;
+ ret |= __check_adc_validity_one("vdd18", 1800);
+ ret |= __check_adc_validity_one("vdd25", 2500);
+ ret |= __check_adc_validity_one("vdd28", 2800);
+ ret |= __check_adc_validity_one("vdd_a", 1800);
+ ret |= __check_adc_validity_one("vddarm", 1200);
+ ret |= __check_adc_validity_one("vddcore", 1100);
+ return ret;
+}
+
+static int dcdc_calibrate(struct regulator *dcdc, int adc_chan,
+ const char *id, int def_vol, int to_vol, int is_cal)
+{
+ int ret = 0;
+ int acc_vol, adc_vol = 0, ctl_vol, cal_vol = 0;
+
+ if (0 != regulator_is_enabled(dcdc))
+ adc_vol = dcdc_adc_get(adc_chan);
+
+ cal_vol = abs(adc_vol - to_vol);
+
+ info("%s default %dmv, from %dmv to %dmv, %c%d.%02d%%\n", __FUNCTION__,
+ def_vol, adc_vol, to_vol,
+ (adc_vol > to_vol) ? '+' : '-',
+ cal_vol * 100 / to_vol, cal_vol * 100 * 100 / to_vol % 100);
+
+ if (!def_vol || !to_vol || !adc_vol)
+ goto exit;
+
+ if (cal_vol > to_vol / 10) /* adjust limit 10% */
+ goto exit;
+ else if (cal_vol < to_vol / 100 && !is_cal) { /* margin 1% */
+ info("%s %s is ok\n", __FUNCTION__, id);
+ return 0;
+ }
+
+ acc_vol = regulator_get_trimming_step(dcdc, to_vol);
+
+ /* always set valid vol ctrl bits */
+ ctl_vol = DIV_ROUND_UP(def_vol * to_vol, adc_vol) + acc_vol;
+ ret = regulator_set_trimming(dcdc, ctl_vol * 1000, to_vol * 1000);
+ if (IS_ERR_VALUE(ret))
+ goto exit;
+
+ WARN(!is_cal,
+ "warning: regulator (%s) voltage ctrl %dmv\n", id, ctl_vol);
+ return ctl_vol;
+
+exit:
+ info("%s %s failure\n", __FUNCTION__, id);
+ return -1;
+}
+
+int sci_dcdc_calibrate(const char *name, int def_vol, int to_vol)
+{
+ int i, ret, adc_chan, ctl_vol;
+ struct regulator *dcdc;
+ static int is_ddr2 = 0;
+ if (bat_denominators == 0) {
+ u32 chan_numerators, chan_denominators;
+ sci_adc_get_vol_ratio(ADC_CHANNEL_VBAT, 0, &bat_numerators,
+ &bat_denominators);
+ sci_adc_get_vol_ratio(ADC_CHANNEL_DCDCCORE, true,
+ &chan_numerators, &chan_denominators);
+ is_ddr2 = 0 == (sci_adi_read(ANA_REG_GLB_ANA_STATUS) & BIT(6));
+ debug
+ ("vbat cal type %d, chan scale ratio %u/%u, and dcdc %u/%u, %s\n",
+ sprd_get_adc_cal_type(), bat_numerators, bat_denominators,
+ chan_numerators, chan_denominators,
+ is_ddr2 ? "ddr2" : "ddr");
+
+ __check_adc_validity();
+ }
+
+ if (!is_ddr2 && 0 == strcmp(name, "vddmem"))
+ name = "vddmem1";
+
+ for (i = 0; i < ARRAY_SIZE(dcdc_cal_map); i++) {
+ if (0 == strcmp(name, dcdc_cal_map[i].name))
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(dcdc_cal_map))
+ return -EINVAL; /* not found */
+
+ dcdc = regulator_get(0, name);
+ if (IS_ERR(dcdc))
+ goto exit;
+
+ if (reguator_is_trimming(regulator_get_drvdata(dcdc)))
+ goto exit; /* already trimming, do nothing */
+
+ adc_chan = dcdc_cal_map[i].adc_chan;
+ ctl_vol = regulator_get_voltage(dcdc);
+ if (IS_ERR_VALUE(ctl_vol)) {
+ debug0("no valid %s vol ctrl bits\n", name);
+ } else /* dcdc maybe had been adjusted in uboot-spl */
+ ctl_vol /= 1000;
+
+ if (!def_vol)
+ def_vol =
+ (IS_ERR_VALUE(ctl_vol)) ? dcdc_cal_map[i].def_vol : ctl_vol;
+
+ if (!to_vol)
+ to_vol =
+ (IS_ERR_VALUE(ctl_vol)) ? dcdc_cal_map[i].def_vol : ctl_vol;
+
+ /* enable ldo cal before adc sampling and ldo calibration */
+ if (0 != dcdc_cal_map[i].cal_sel) {
+ sci_adi_write(ANA_REG_GLB2_LDO_TRIM_SEL,
+ dcdc_cal_map[i].cal_sel, -1);
+ }
+
+ ret = dcdc_calibrate(dcdc, adc_chan, name, def_vol, to_vol, 1);
+ debug0("dcdc_calibrate return %d\n", ret);
+ if (ret >= 0) {
+ msleep(10); /* wait a moment before cal verify */
+ ret = dcdc_calibrate(dcdc, adc_chan, name,
+ (0 == ret) ? def_vol : ret, to_vol, 0);
+ if (0 == ret) { /* cal is ok, do not cal any more */
+ dcdc_cal_map[i].def_on = 0;
+ }
+ }
+
+ /* close ldo cal */
+ if (0 != dcdc_cal_map[i].cal_sel) {
+ sci_adi_write(ANA_REG_GLB2_LDO_TRIM_SEL, 0, -1);
+ }
+
+exit:
+ regulator_put(dcdc);
+ return 0;
+}
+
+int mpll_calibrate(int cpu_freq)
+{
+ u32 val = 0;
+ unsigned long flags;
+ //BUG_ON(cpu_freq != 1200); /* only upgrade 1.2G */
+ cpu_freq /= 4;
+ local_irq_save(flags);
+ val = sci_glb_raw_read(REG_GLB_M_PLL_CTL0);
+ if ((val & MASK_MPLL_N) == cpu_freq)
+ goto exit;
+ val = (val & ~MASK_MPLL_N) | cpu_freq;
+ sci_glb_set(REG_GLB_GEN1, BIT_MPLL_CTL_WE); /* mpll unlock */
+ sci_glb_write(REG_GLB_M_PLL_CTL0, val, MASK_MPLL_N);
+ sci_glb_clr(REG_GLB_GEN1, BIT_MPLL_CTL_WE);
+exit:
+ local_irq_restore(flags);
+ debug("%s 0x%08x\n", __FUNCTION__, val);
+ return 0;
+}
+
+struct dcdc_delayed_work {
+ struct delayed_work work;
+ u32 uptime;
+ int cal_typ;
+};
+
+static struct dcdc_delayed_work dcdc_work = {
+ .work.work.func = NULL,
+ .uptime = 0,
+ .cal_typ = -1, /* Invalid */
+};
+
+static u32 sci_syst_read(void)
+{
+ u32 t = __raw_readl(REG_SYST_VALUE);
+ while (t != __raw_readl(REG_SYST_VALUE))
+ t = __raw_readl(REG_SYST_VALUE);
+ return t;
+}
+
+static void do_dcdc_work(struct work_struct *work)
+{
+ int i, cnt = CALIBRATE_TO;
+
+ /* debug("%s %d\n", __FUNCTION__, sprd_get_adc_cal_type()); */
+ if (dcdc_work.cal_typ == sprd_get_adc_cal_type())
+ goto exit; /* no change, set next delayed work */
+
+ dcdc_work.cal_typ = sprd_get_adc_cal_type();
+ debug0("%s %d %d\n", __FUNCTION__, dcdc_work.cal_typ, cnt);
+
+ /* four DCDCs and all LDOs Trimming if default ON */
+ for (i = 0; i < ARRAY_SIZE(dcdc_cal_map); i++) {
+ if (dcdc_cal_map[i].def_on)
+ sci_dcdc_calibrate(dcdc_cal_map[i].name, 0, 0);
+ }
+
+exit:
+ if (sci_syst_read() - dcdc_work.uptime < CALIBRATE_TO * 1000) {
+ schedule_delayed_work(&dcdc_work.work, msecs_to_jiffies(1000));
+ } else {
+ debug0("%s end.\n", __FUNCTION__);
+ }
+
+ return;
+}
+
+void dcdc_calibrate_callback(void *data)
+{
+ if (!dcdc_work.work.work.func) {
+ INIT_DELAYED_WORK(&dcdc_work.work, do_dcdc_work);
+ dcdc_work.uptime = sci_syst_read();
+ }
+
+ dcdc_work.cal_typ = (int)data;
+ schedule_delayed_work(&dcdc_work.work, msecs_to_jiffies(1000));
+}
+
+int ldo_trimming_callback(void *data)
+{
+ int i, ret = 0;
+ const char *name = data;
+ for (i = 0; i < ARRAY_SIZE(dcdc_cal_map); i++) {
+ if (0 == strcmp(name, dcdc_cal_map[i].name))
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(dcdc_cal_map))
+ return -EINVAL; /* not found */
+
+ if (!dcdc_cal_map[i].def_on) {
+ dcdc_cal_map[i].def_on = 1;
+ debug0("%s trimming ...\n", name);
+ }
+
+ if (dcdc_work.cal_typ >= 0)
+ dcdc_calibrate_callback(0);
+ return ret;
+}
+
+static int __init dcdc_init(void)
+{
+ if (!in_calibration()) /* bypass if in CFT */
+ dcdc_calibrate_callback(0);
+ return 0;
+}
+
+late_initcall(dcdc_init);
diff --git a/drivers/platform/sprd/dcdc_debug.c b/drivers/platform/sprd/dcdc_debug.c
new file mode 100644
index 00000000..4f0d492d
--- /dev/null
+++ b/drivers/platform/sprd/dcdc_debug.c
@@ -0,0 +1,126 @@
+#include <linux/bug.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <mach/hardware.h>
+#include <mach/regs_glb.h>
+#include <mach/regs_ana_glb.h>
+#include <mach/adi.h>
+#include <mach/adc.h>
+
+int dcdc_adc_get(int adc_chan);
+
+int mpll_calibrate(int cpu_freq);
+int sci_dcdc_calibrate(const char *name, int def_vol, int to_vol);
+
+static u32 dcdc_to_vol = 0, dcdcarm_to_vol = 0;
+static u32 dcdcldo_to_vol = 0, dcdcmem_to_vol = 0;
+
+static int debugfs_dcdc_get(void *data, u64 * val)
+{
+ *(u32 *) data = *val = dcdc_adc_get(ADC_CHANNEL_DCDCCORE);
+ return 0;
+}
+
+static int debugfs_dcdcarm_get(void *data, u64 * val)
+{
+ *(u32 *) data = *val = dcdc_adc_get(ADC_CHANNEL_DCDCARM);
+ return 0;
+}
+
+static int debugfs_dcdcldo_get(void *data, u64 * val)
+{
+ *(u32 *) data = *val = dcdc_adc_get(ADC_CHANNEL_DCDCLDO);
+ return 0;
+}
+
+static int debugfs_dcdcmem_get(void *data, u64 * val)
+{
+ *(u32 *) data = *val = dcdc_adc_get(ADC_CHANNEL_DCDCMEM);
+ return 0;
+}
+
+static int debugfs_dcdc_set(void *data, u64 val)
+{
+ int to_vol = *(u32 *) data = val;
+ sci_dcdc_calibrate("vddcore", 0, to_vol);
+ return 0;
+}
+
+static int debugfs_dcdcarm_set(void *data, u64 val)
+{
+ int to_vol = *(u32 *) data = val;
+ sci_dcdc_calibrate("vddarm", 0, to_vol);
+ return 0;
+}
+
+static int debugfs_dcdcldo_set(void *data, u64 val)
+{
+ int to_vol = *(u32 *) data = val;
+ sci_dcdc_calibrate("dcdcldo", 0, to_vol);
+ return 0;
+}
+
+static int debugfs_dcdcmem_set(void *data, u64 val)
+{
+ int to_vol = *(u32 *) data = val;
+ sci_dcdc_calibrate("vddmem", 0, to_vol);
+ return 0;
+}
+
+static u32 mpll_freq = 0;
+static int debugfs_mpll_get(void *data, u64 * val)
+{
+ if (0 == mpll_freq) {
+ *(u32 *) data = (__raw_readl(REG_GLB_M_PLL_CTL0) & MASK_MPLL_N) * 4; /* default refin */
+ }
+ *val = *(u32 *) data;
+ return 0;
+}
+
+static int debugfs_mpll_set(void *data, u64 val)
+{
+ *(u32 *) data = val;
+ mpll_calibrate(mpll_freq);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_dcdc,
+ debugfs_dcdc_get, debugfs_dcdc_set, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(fops_dcdcarm,
+ debugfs_dcdcarm_get, debugfs_dcdcarm_set, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(fops_dcdcldo,
+ debugfs_dcdcldo_get, debugfs_dcdcldo_set, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(fops_dcdcmem,
+ debugfs_dcdcmem_get, debugfs_dcdcmem_set, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(fops_mpll,
+ debugfs_mpll_get, debugfs_mpll_set, "%llu\n");
+
+static int __init dcdc_debugfs_init(void)
+{
+ static struct dentry *debug_root = NULL;
+ debug_root = debugfs_create_dir("vol", NULL);
+ if (IS_ERR_OR_NULL(debug_root)) {
+ printk("%s return %p\n", __FUNCTION__, debug_root);
+ return PTR_ERR(debug_root);
+ }
+ debugfs_create_file("dcdc", S_IRUGO | S_IWUGO,
+ debug_root, &dcdc_to_vol, &fops_dcdc);
+ debugfs_create_file("dcdcarm", S_IRUGO | S_IWUGO,
+ debug_root, &dcdcarm_to_vol, &fops_dcdcarm);
+ debugfs_create_file("dcdcldo", S_IRUGO | S_IWUGO,
+ debug_root, &dcdcldo_to_vol, &fops_dcdcldo);
+ debugfs_create_file("dcdcmem", S_IRUGO | S_IWUGO,
+ debug_root, &dcdcmem_to_vol, &fops_dcdcmem);
+ debugfs_create_file("mpll", S_IRUGO | S_IWUGO,
+ debug_root, &mpll_freq, &fops_mpll);
+ return 0;
+}
+
+module_init(dcdc_debugfs_init);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robot <zhulin.lian@spreadtrum.com>");
+MODULE_DESCRIPTION("dcdc debugfs");
diff --git a/drivers/platform/sprd/devices.h b/drivers/platform/sprd/devices.h
new file mode 100644
index 00000000..ff80c401
--- /dev/null
+++ b/drivers/platform/sprd/devices.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __ARCH_ARM_MACH_SPRD_DEVICES_H
+#define __ARCH_ARM_MACH_SPRD_DEVICES_H
+
+extern struct platform_device sprd_hwspinlock_device0;
+extern struct platform_device sprd_hwspinlock_device1;
+extern struct platform_device sprd_serial_device0;
+extern struct platform_device sprd_serial_device1;
+extern struct platform_device sprd_serial_device2;
+extern struct platform_device sprd_serial_device3;
+extern struct platform_device sprd_device_rtc;
+extern struct platform_device sprd_eic_gpio_device;
+extern struct platform_device sprd_nand_device;
+extern struct platform_device sprd_lcd_device0;
+extern struct platform_device sprd_lcd_device1;
+#ifdef CONFIG_PSTORE_RAM
+extern struct platform_device sprd_ramoops_device;
+#endif
+extern struct platform_device sprd_otg_device;
+extern struct platform_device sprd_backlight_device;
+extern struct platform_device sprd_i2c_device0;
+extern struct platform_device sprd_i2c_device1;
+extern struct platform_device sprd_i2c_device2;
+extern struct platform_device sprd_i2c_device3;
+#if defined(CONFIG_PIN_POWER_DOMAIN_SWITCH)
+extern struct platform_device sprd_pin_switch_device;
+#endif
+extern struct platform_device sprd_spi0_device;
+extern struct platform_device sprd_spi1_device;
+extern struct platform_device sprd_spi2_device;
+extern struct platform_device sprd_keypad_device;
+extern struct platform_device sprd_thm_device;
+extern struct platform_device sprd_thm_a_device;
+extern struct platform_device sprd_battery_device;
+extern struct platform_device sprd_headset_device;
+
+extern struct platform_device sprd_battery_device;
+extern struct platform_device sprd_vsp_device;
+extern struct platform_device sprd_jpg_device;
+#ifdef CONFIG_ION
+extern struct platform_device sprd_ion_dev;
+#endif
+extern struct platform_device sprd_iommu_gsp_device;
+extern struct platform_device sprd_iommu_mm_device;
+#ifdef CONFIG_MUX_SDIO_OPT1_HAL
+extern struct platform_device ipc_sdio_device;
+#endif
+extern struct platform_device sprd_sdio0_device;
+extern struct platform_device sprd_sdio1_device;
+extern struct platform_device sprd_sdio2_device;
+extern struct platform_device sprd_emmc_device;
+extern struct platform_device sprd_dcam_device;
+extern struct platform_device sprd_scale_device;
+extern struct platform_device sprd_gsp_device;
+extern struct platform_device sprd_rotation_device;
+extern struct platform_device sprd_sensor_device;
+extern struct platform_device sprd_isp_device;
+extern struct platform_device sprd_dma_copy_device;
+extern struct platform_device sprd_ahb_bm0_device;
+extern struct platform_device sprd_ahb_bm1_device;
+extern struct platform_device sprd_ahb_bm2_device;
+extern struct platform_device sprd_ahb_bm3_device;
+extern struct platform_device sprd_ahb_bm4_device;
+extern struct platform_device sprd_axi_bm0_device;
+extern struct platform_device sprd_axi_bm1_device;
+extern struct platform_device sprd_axi_bm2_device;
+#ifdef CONFIG_SIPC_TD
+extern struct platform_device sprd_spipe_td_device;
+extern struct platform_device sprd_slog_td_device;
+extern struct platform_device sprd_stty_td_device;
+extern struct platform_device sprd_spool_td_device;
+extern struct platform_device sprd_cproc_td_device;
+extern struct platform_device sprd_seth0_td_device;
+extern struct platform_device sprd_seth1_td_device;
+extern struct platform_device sprd_seth2_td_device;
+extern struct platform_device sprd_saudio_td_device;
+#endif
+#ifdef CONFIG_SIPC_WCDMA
+extern struct platform_device sprd_spipe_wcdma_device;
+extern struct platform_device sprd_slog_wcdma_device;
+extern struct platform_device sprd_stty_wcdma_device;
+extern struct platform_device sprd_spool_wcdma_device;
+extern struct platform_device sprd_cproc_wcdma_device;
+extern struct platform_device sprd_seth0_wcdma_device;
+extern struct platform_device sprd_seth1_wcdma_device;
+extern struct platform_device sprd_seth2_wcdma_device;
+extern struct platform_device sprd_saudio_wcdma_device;
+#endif
+#ifdef CONFIG_SIPC_WCN
+extern struct platform_device sprd_spipe_wcn_device;
+extern struct platform_device sprd_slog_wcn_device;
+extern struct platform_device sprd_cproc_wcn_device;
+extern struct platform_device sprd_sttybt_td_device;
+#endif
+#ifdef CONFIG_SIPC_GGE
+extern struct platform_device sprd_cproc_cp0_device;
+extern struct platform_device sprd_spipe_gge_device;
+extern struct platform_device sprd_slog_gge_device;
+extern struct platform_device sprd_stty_gge_device;
+extern struct platform_device sprd_seth0_gge_device;
+extern struct platform_device sprd_seth1_gge_device;
+extern struct platform_device sprd_seth2_gge_device;
+#endif
+#ifdef CONFIG_SIPC_LTE
+extern struct platform_device sprd_cproc_cp1_device;
+extern struct platform_device sprd_spipe_lte_device;
+extern struct platform_device sprd_slog_lte_device;
+extern struct platform_device sprd_stty_lte_device;
+extern struct platform_device sprd_seth0_lte_device;
+extern struct platform_device sprd_seth1_lte_device;
+extern struct platform_device sprd_seth2_lte_device;
+#endif
+
+extern struct platform_device sprd_saudio_voip_device;
+extern struct platform_device sprd_a7_pmu_device;
+extern struct platform_device sprd_memnand_system_device;
+extern struct platform_device sprd_memnand_userdata_device;
+extern struct platform_device sprd_memnand_cache_device;
+
+extern struct platform_device sprd_audio_vbc_r2p0_sprd_codec_v3_device;
+extern struct platform_device sprd_audio_i2s_null_codec_device;
+extern struct platform_device sprd_audio_platform_pcm_device;
+extern struct platform_device sprd_audio_vaudio_device;
+extern struct platform_device sprd_audio_vbc_r2p0_device;
+extern struct platform_device sprd_audio_i2s0_device;
+extern struct platform_device sprd_audio_i2s1_device;
+extern struct platform_device sprd_audio_i2s2_device;
+extern struct platform_device sprd_audio_i2s3_device;
+extern struct platform_device sprd_audio_sprd_codec_v3_device;
+extern struct platform_device sprd_audio_null_codec_device;
+
+#ifdef CONFIG_SPRD_VETH
+#ifdef CONFIG_MUX_SPI_HAL
+extern struct platform_device sprd_veth_spi0_device;
+extern struct platform_device sprd_veth_spi1_device;
+extern struct platform_device sprd_veth_spi2_device;
+extern struct platform_device sprd_veth_spi3_device;
+extern struct platform_device sprd_veth_spi4_device;
+#endif
+#ifdef CONFIG_MUX_SDIO_OPT1_HAL
+extern struct platform_device sprd_veth_sdio0_device;
+extern struct platform_device sprd_veth_sdio1_device;
+extern struct platform_device sprd_veth_sdio2_device;
+extern struct platform_device sprd_veth_sdio3_device;
+extern struct platform_device sprd_veth_sdio4_device;
+#endif
+#ifdef CONFIG_MODEM_INTF
+extern struct platform_device modem_interface_device;
+#endif
+#endif
+
+extern struct platform_device modem_interface_device;
+#endif
+#ifdef CONFIG_TS0710_MUX_ENABLE
+extern struct platform_device sprd_mux_spi_device;
+extern struct platform_device sprd_mux_sdio_device;
+#endif
diff --git a/drivers/platform/sprd/dhd_adapter.c b/drivers/platform/sprd/dhd_adapter.c
new file mode 100644
index 00000000..aa5637c0
--- /dev/null
+++ b/drivers/platform/sprd/dhd_adapter.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <asm/mach-types.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/wlan_plat.h>
+#include <soc/sprd/board.h>
+#include <soc/sprd/hardware.h>
+#include <linux/regulator/consumer.h>
+#include <soc/sprd/regulator.h>
+#include <linux/export.h>
+#include <linux/clk.h>
+#include <linux/fs.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdhci.h>
+#include <soc/sprd/pinmap.h>
+
+#define GET_WIFI_MAC_ADDR_FROM_NV_ITEM 1
+
+#define WLAN_STATIC_SCAN_BUF0 5
+#define WLAN_STATIC_SCAN_BUF1 6
+#define WLAN_STATIC_DHD_INFO_BUF 7
+#define WLAN_SCAN_BUF_SIZE (64 * 1024)
+#define WLAN_DHD_INFO_BUF_SIZE (16 * 1024)
+#define PREALLOC_WLAN_SEC_NUM 4
+#define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160
+#define PREALLOC_WLAN_SECTION_HEADER 24
+
+#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128)
+#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128)
+#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 512)
+#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 1024)
+
+#define DHD_SKB_HDRSIZE 336
+#define DHD_SKB_1PAGE_BUFSIZE ((PAGE_SIZE*1)-DHD_SKB_HDRSIZE)
+#define DHD_SKB_2PAGE_BUFSIZE ((PAGE_SIZE*2)-DHD_SKB_HDRSIZE)
+#define DHD_SKB_4PAGE_BUFSIZE ((PAGE_SIZE*4)-DHD_SKB_HDRSIZE)
+
+#define WLAN_SKB_BUF_NUM 17
+#define IFHWADDRLEN 6
+
+#define CUSTOMER_MAC_FILE "/data/misc/wifi/wifimac.txt"
+
+extern void * dhd_os_open_image(char *filename);
+extern int dhd_os_get_image_block(char *buf, int len, void *image);
+extern void dhd_os_close_image(void *image);
+extern int sdhci_wifi_detect_isbusy(void);
+
+static struct mmc_host * wlan_mmc =NULL;
+static int wlan_device_cd = 0; /* WIFI virtual 'card detect' status */
+static int wlan_device_power_state;
+static int wlan_device_reset_state;
+static void (*wifi_status_cb)(int card_present, void *dev_id);
+static void *wifi_status_cb_devid;
+static struct regulator *wlan_regulator_18 = NULL;
+
+int wlan_device_power(int on);
+int wlan_device_reset(int on);
+int wlan_device_set_carddetect(int on);
+
+static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM];
+
+typedef struct wifi_mem_prealloc_struct {
+ void *mem_ptr;
+ unsigned long size;
+} wifi_mem_prealloc_t;
+
+static wifi_mem_prealloc_t wlan_mem_array[PREALLOC_WLAN_SEC_NUM] = {
+ { NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER) },
+ { NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER) },
+ { NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER) },
+ { NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER) }
+};
+
+void *wlan_static_scan_buf0;
+void *wlan_static_scan_buf1;
+void *wlan_static_dhd_info_buf;
+
+static void * open_image(char *filename)
+{
+ struct file *fp;
+
+ fp = filp_open(filename, O_RDONLY, 0);
+ /*
+ * 2.6.11 (FC4) supports filp_open() but later revs don't?
+ * Alternative:
+ * fp = open_namei(AT_FDCWD, filename, O_RD, 0);
+ * ???
+ */
+ if (IS_ERR(fp))
+ fp = NULL;
+
+ return fp;
+}
+
+static int get_image_block(char *buf, int len, void *image)
+{
+ struct file *fp = (struct file *)image;
+ int rdlen;
+
+ if (!image)
+ return 0;
+
+ rdlen = kernel_read(fp, fp->f_pos, buf, len);
+ if (rdlen > 0)
+ fp->f_pos += rdlen;
+
+ return rdlen;
+}
+
+static void close_image(void *image)
+{
+ if (image)
+ filp_close((struct file *)image, NULL);
+}
+
+static void *wlan_device_mem_prealloc(int section, unsigned long size)
+{
+ if (section == PREALLOC_WLAN_SEC_NUM)
+ return wlan_static_skb;
+
+ if (section == WLAN_STATIC_SCAN_BUF0)
+ return wlan_static_scan_buf0;
+
+ if (section == WLAN_STATIC_SCAN_BUF1)
+ return wlan_static_scan_buf1;
+
+ if (section == WLAN_STATIC_DHD_INFO_BUF) {
+ if (size > WLAN_DHD_INFO_BUF_SIZE) {
+ pr_err("request DHD_INFO size(%lu) is bigger than static size(%d).\n", size, WLAN_DHD_INFO_BUF_SIZE);
+ return NULL;
+ }
+ return wlan_static_dhd_info_buf;
+ }
+
+ if ((section < 0) || (section > PREALLOC_WLAN_SEC_NUM))
+ return NULL;
+
+ if (wlan_mem_array[section].size < size)
+ return NULL;
+
+ return wlan_mem_array[section].mem_ptr;
+}
+
+int __init init_wifi_mem(void)
+{
+ int i;
+ int j;
+
+ for (i = 0; i < 8; i++) {
+ wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_1PAGE_BUFSIZE);
+ if (!wlan_static_skb[i])
+ goto err_skb_alloc;
+ }
+
+ for (; i < 16; i++) {
+ wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_2PAGE_BUFSIZE);
+ if (!wlan_static_skb[i])
+ goto err_skb_alloc;
+ }
+
+ wlan_static_skb[i] = dev_alloc_skb(DHD_SKB_4PAGE_BUFSIZE);
+ if (!wlan_static_skb[i])
+ goto err_skb_alloc;
+
+ for (i = 0 ; i < PREALLOC_WLAN_SEC_NUM ; i++) {
+ wlan_mem_array[i].mem_ptr =
+ kmalloc(wlan_mem_array[i].size, GFP_KERNEL);
+
+ if (!wlan_mem_array[i].mem_ptr)
+ goto err_mem_alloc;
+ }
+
+ wlan_static_scan_buf0 = kmalloc(WLAN_SCAN_BUF_SIZE, GFP_KERNEL);
+ if (!wlan_static_scan_buf0)
+ goto err_mem_alloc;
+
+ wlan_static_scan_buf1 = kmalloc(WLAN_SCAN_BUF_SIZE, GFP_KERNEL);
+ if (!wlan_static_scan_buf1)
+ goto err_mem_alloc;
+
+ wlan_static_dhd_info_buf = kmalloc(WLAN_DHD_INFO_BUF_SIZE, GFP_KERNEL);
+ if (!wlan_static_dhd_info_buf)
+ goto err_mem_alloc;
+
+ printk(KERN_INFO"%s: WIFI MEM Allocated\n", __func__);
+ return 0;
+
+ err_mem_alloc:
+ pr_err("Failed to mem_alloc for WLAN\n");
+ for (j = 0 ; j < i ; j++)
+ kfree(wlan_mem_array[j].mem_ptr);
+
+ i = WLAN_SKB_BUF_NUM;
+
+ err_skb_alloc:
+ pr_err("Failed to skb_alloc for WLAN\n");
+ for (j = 0 ; j < i ; j++)
+ dev_kfree_skb(wlan_static_skb[j]);
+
+ return -ENOMEM;
+}
+
+/* Control the BT_VDDIO and WLAN_VDDIO
+Always power on According to spec
+*/
+static int wlan_ldo_enable(void)
+{
+ int err;
+
+#ifdef CONFIG_ARCH_SCX35
+ /*temp config for clk_aux0, waiting for SC8830 pin config*/
+// pinmap_set(0x400, 0x0101);
+
+ wlan_regulator_18 = regulator_get(NULL, "vdd18");
+#else
+ wlan_regulator_18 = regulator_get(NULL, "vddsd1");
+#endif
+
+ if (IS_ERR(wlan_regulator_18)) {
+ pr_err("can't get wlan 1.8V regulator\n");
+ return -1;
+ }
+
+ err = regulator_set_voltage(wlan_regulator_18,1800000,1800000);
+ if (err){
+ pr_err("can't set wlan to 1.8V.\n");
+ return -1;
+ }
+
+ regulator_set_mode(wlan_regulator_18, REGULATOR_MODE_STANDBY);
+ regulator_enable(wlan_regulator_18);
+ return 0;
+}
+
+static void wlan_clk_init(void)
+{
+ struct clk *wlan_clk;
+ struct clk *clk_parent;
+/*8825ea/openphone use aux0 garda use aux1*/
+#ifdef CONFIG_MACH_GARDA
+ wlan_clk = clk_get(NULL, "clk_aux1");
+ if (IS_ERR(wlan_clk)) {
+ printk("clock: failed to get clk_aux1\n");
+ }
+#else
+ wlan_clk = clk_get(NULL, "clk_aux0");
+ if (IS_ERR(wlan_clk)) {
+ printk("clock: failed to get clk_aux0\n");
+ }
+#endif
+ clk_parent = clk_get(NULL, "ext_32k");
+ if (IS_ERR(clk_parent)) {
+ printk("failed to get parent ext_32k\n");
+ }
+
+ clk_set_parent(wlan_clk, clk_parent);
+ clk_set_rate(wlan_clk, 32000);
+ clk_prepare_enable(wlan_clk);
+}
+
+int wlan_device_power(int on)
+{
+ int i;
+ pr_info("%s:%d \n", __func__, on);
+
+ if(on) {
+ #if 0
+ for (i = 0; i <= 200; i++) {
+ //if(!sdhci_wifi_detect_isbusy())
+ break;
+ msleep(100);
+ }
+ #else
+// if(wlan_mmc)
+// flush_delayed_work(&wlan_mmc->detect);
+ #endif
+ printk("%s after delay %d times (100ms)\n", __func__, i);
+ /* enable SDIO clock */
+ #ifdef CONFIG_WLAN_SDIO
+ //sdhci_device_attach(1);
+ #endif
+ gpio_direction_output(GPIO_WIFI_SHUTDOWN, 0);
+ mdelay(10);
+ gpio_direction_output(GPIO_WIFI_SHUTDOWN, 1);
+// mdelay(200);
+ }
+ else {
+ /* disale SDIO clock */
+ #ifdef CONFIG_WLAN_SDIO
+ //sdhci_device_attach(0);
+ #endif
+ gpio_direction_output(GPIO_WIFI_SHUTDOWN, 0);
+
+ }
+ wlan_device_power_state = on;
+ return 0;
+}
+
+int wlan_device_reset(int on)
+{
+ pr_info("%s: do nothing\n", __func__);
+ wlan_device_reset_state = on;
+ return 0;
+}
+
+
+int wlan_device_status_register(
+ void (*callback)(int card_present, void *dev_id),
+ void *dev_id)
+{
+ if (wifi_status_cb)
+ return -EAGAIN;
+ wifi_status_cb = callback;
+ wifi_status_cb_devid = dev_id;
+ return 0;
+}
+
+EXPORT_SYMBOL(wlan_device_status_register);
+
+static __used unsigned int wlan_device_status(struct device *dev)
+{
+ return wlan_device_cd;
+}
+
+int wlan_device_set_carddetect(int val)
+{
+ pr_info("%s: %d\n", __func__, val);
+ mdelay(100);
+#if 0
+ wlan_device_cd = val;
+ if (wifi_status_cb) {
+ wifi_status_cb(val, wifi_status_cb_devid);
+ } else
+ pr_warning("%s: Nobody to notify\n", __func__);
+#endif
+
+#ifdef CONFIG_WLAN_SDIO
+// sdhci_bus_scan();
+ if(wlan_mmc) {
+ mmc_detect_change(wlan_mmc, 0);
+ } else {
+ pr_info("%s wlan_mmc is null,carddetect failed \n ",__func__);
+ }
+#endif
+ return 0;
+}
+
+
+
+#ifdef GET_WIFI_MAC_ADDR_FROM_NV_ITEM
+static unsigned char wlan_mac_addr[IFHWADDRLEN] = { 0x11,0x22,0x33,0x44,0x55,0x66 };
+
+static unsigned char char2bin( char m)
+{
+ if (( m >= 'a') && (m <= 'f')) {
+ return m - 'a' + 10;
+ }
+ if (( m >= 'A') && (m <= 'F')) {
+ return m - 'A' + 10;
+ }
+ if (( m >= '0') && (m <= '9')) {
+ return m - '0';
+ }
+ return 0;
+}
+
+static int wlan_device_get_mac_addr(unsigned char *buf)
+{
+
+ char macaddr[20];
+ int mac_len = 0;
+ void *fp;
+
+ if (!buf){
+ pr_err("%s, null parameter !!\n", __func__);
+ return -EFAULT;
+ }
+ fp = open_image(CUSTOMER_MAC_FILE);
+ if (fp == NULL)
+ return -EFAULT;
+ mac_len = get_image_block(macaddr, 17, fp);
+ if (mac_len < 17)
+ return -EFAULT;
+
+
+ wlan_mac_addr[0] = (unsigned char)((char2bin(macaddr[0]) << 4) | char2bin(macaddr[1]));
+ wlan_mac_addr[1] = (unsigned char)((char2bin(macaddr[3]) << 4) | char2bin(macaddr[4]));
+ wlan_mac_addr[2] = (unsigned char)((char2bin(macaddr[6]) << 4) | char2bin(macaddr[7]));
+ wlan_mac_addr[3] = (unsigned char)((char2bin(macaddr[9]) << 4) | char2bin(macaddr[10]));
+ wlan_mac_addr[4] = (unsigned char)((char2bin(macaddr[12]) << 4) | char2bin(macaddr[13]));
+ wlan_mac_addr[5] = (unsigned char)((char2bin(macaddr[15]) << 4) | char2bin(macaddr[16]));
+
+ memcpy(buf, wlan_mac_addr, IFHWADDRLEN);
+ pr_info("wifi mac: %x:%x:%x:%x:%x:%x\n", wlan_mac_addr[0], wlan_mac_addr[1], wlan_mac_addr[2], wlan_mac_addr[3], wlan_mac_addr[4], wlan_mac_addr[5]);
+
+ close_image(fp);
+
+ return 0;
+
+}
+#endif
+
+static struct wifi_platform_data wlan_device_control = {
+ .set_power = wlan_device_power,
+ .set_reset = wlan_device_reset,
+ .set_carddetect = wlan_device_set_carddetect,
+ .mem_prealloc = wlan_device_mem_prealloc,
+#ifdef GET_WIFI_MAC_ADDR_FROM_NV_ITEM
+ .get_mac_addr = wlan_device_get_mac_addr,
+#endif
+};
+
+static struct resource wlan_resources[] = {
+#ifdef CONFIG_WLAN_SDIO
+ [0] = {
+ .start = SPRD_SDIO1_PHYS,
+ .end = SPRD_SDIO1_PHYS + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+#else
+ [0] = {
+ .start = SPRD_SPI0_PHYS,
+ .end = SPRD_SPI0_PHYS + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+#endif
+ [1] = {
+ .name = "bcmdhd_wlan_irq",
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE,
+ },
+};
+
+static struct platform_device sprd_wlan_device = {
+ .name = "bcmdhd_wlan",
+ .id = 1,
+ .dev = {
+ .platform_data = &wlan_device_control,
+ },
+ .resource = wlan_resources,
+ .num_resources = ARRAY_SIZE(wlan_resources),
+};
+
+void wlan_host_get(void) {
+ struct sdhci_host *host;
+#ifndef CONFIG_OF
+ extern struct platform_device sprd_sdio1_device;
+ host = platform_get_drvdata(&sprd_sdio1_device);
+#else
+ struct platform_device * sdio1_device = to_platform_device(bus_find_device_by_name(&platform_bus_type, NULL, "sdio_wifi"));
+ host = platform_get_drvdata(sdio1_device);
+#endif
+// BUG_ON(!host->mmc);
+// wlan_mmc = host->mmc;
+ wlan_mmc = container_of(host, struct mmc_host, private);
+ BUG_ON(!wlan_mmc);
+}
+
+static int __init wlan_device_init(void)
+{
+ int ret;
+ wlan_host_get();
+ init_wifi_mem();
+// wlan_ldo_enable();//NOTE:check this function!If enble this, it cause V2.8 jump from 2.6v to 2.8v when sleep!
+ wlan_clk_init();
+ gpio_request(GPIO_WIFI_IRQ, "oob_irq");
+ gpio_direction_input(GPIO_WIFI_IRQ);
+
+ wlan_resources[1].start = gpio_to_irq(GPIO_WIFI_IRQ);
+ wlan_resources[1].end = gpio_to_irq(GPIO_WIFI_IRQ);
+
+ gpio_request(GPIO_WIFI_SHUTDOWN,"wifi_pwd");
+ gpio_direction_output(GPIO_WIFI_SHUTDOWN, 0);
+ ret = platform_device_register(&sprd_wlan_device);
+
+ return ret;
+}
+
+late_initcall(wlan_device_init);
+
+//MODULE_DESCRIPTION("Broadcomm wlan driver");
+//MODULE_LICENSE("GPL");
+
diff --git a/drivers/platform/sprd/dma_r4p0.c b/drivers/platform/sprd/dma_r4p0.c
new file mode 100644
index 00000000..fa8f4cdb
--- /dev/null
+++ b/drivers/platform/sprd/dma_r4p0.c
@@ -0,0 +1,890 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/dma-mapping.h>//test
+#include <linux/delay.h>
+
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/dma_r4p0.h>
+#include <soc/sprd/dma_reg.h>
+
+//#define DMA_DEBUG
+
+struct sci_dma_chn_desc;
+#define AP_DMA_CHN_NUM 32
+#define DMA_CHN_SRC_ADR(base,chn) (DMA_CHx_BASE(base,chn) + 0x0010)
+#define DMA_CHN_DES_ADR(base,chn) (DMA_CHx_BASE(base,chn) + 0x0014)
+
+/*depict a dma controller*/
+struct sci_dma_chip {
+ void __iomem *dma_glb_base;
+ spinlock_t chip_lock;
+ /*point to them chns*/
+ struct sci_dma_chn_desc *chn_desc;
+};
+
+struct sci_dma_chn_desc {
+ void __iomem *dma_chn_base;
+ struct sci_dma_chip *dma_chip;
+ spinlock_t chn_lock;
+ const char *dev_name;
+ void (*irq_handler) (int, void *);
+ void *data;
+ u32 dev_id;
+};
+
+#define get_chn_reg_point(dma_chn) \
+ ((struct sci_dma_chn_reg *)(dma_chns[(dma_chn)].dma_chn_base))
+
+#define get_dma_chip_point(dma_chn) (dma_chns[dma_chn].dma_chip)
+
+#define SCI_DMA_DEBUG
+
+/*support 5 dma controllers*/
+#define SCI_MAX_DMA_SIZE 5
+#define AP_DMA_INDEX 0
+#define AON_DMA_INDEX 1
+
+static struct sci_dma_chip dma_chips[SCI_MAX_DMA_SIZE];
+static struct sci_dma_chn_desc dma_chns[DMA_CHN_MAX + 1];
+static DEFINE_MUTEX(dma_mutex);
+
+#ifdef DMA_DEBUG
+static int __dma_cfg_check_register(void __iomem * dma_reg_addr)
+{
+ volatile struct sci_dma_chn_reg *dma_reg;
+ dma_reg = (struct sci_dma_chn_reg *)dma_reg_addr;
+
+ printk("DMA register:pause=0x%x,\n req=0x%x,\n cfg=0x%x,\n int=0x%x,\n src_addr=0x%x,\n des_addr=0x%x,\n frg_len=0x%x,\n blk_len=0x%x,\n trsc_len=0x%x,\n trsf_step=0x%x,\n wrap_ptr=0x%x,\n wrap_to=0x%x,\n llist_ptr=0x%x,\n frg_step=0x%x,\n src_blk_step=0x%x,\n des_blk_step=0x%x\n",dma_reg->pause,dma_reg->req,dma_reg->cfg,dma_reg->intc,dma_reg->src_addr,dma_reg->des_addr,dma_reg->frg_len,dma_reg->blk_len,dma_reg->trsc_len,dma_reg->trsf_step,dma_reg->wrap_ptr,dma_reg->wrap_to,dma_reg->llist_ptr,dma_reg->frg_step,dma_reg->src_blk_step,dma_reg->des_blk_step);
+ return 0;
+}
+#endif
+
+static void __inline __ap_dma_clk_enable(void)
+{
+ if (!sci_glb_read((unsigned long)REG_AP_AHB_AHB_EB, BIT_DMA_EB)) {
+ sci_glb_set((unsigned long)REG_AP_AHB_AHB_EB, BIT_DMA_EB);
+ }
+}
+
+static void __inline __ap_dma_clk_disable(void)
+{
+ sci_glb_clr((unsigned long)REG_AP_AHB_AHB_EB, BIT_DMA_EB);
+}
+
+static void __inline __dma_softreset(void)
+{
+ sci_glb_set(REG_AP_AHB_AHB_RST, BIT_DMA_SOFT_RST);
+ udelay(1);
+ sci_glb_clr(REG_AP_AHB_AHB_RST, BIT_DMA_SOFT_RST);
+}
+
+static int __dma_set_int_type(u32 dma_chn, dma_int_type int_type)
+{
+ volatile struct sci_dma_chn_reg *dma_reg = get_chn_reg_point(dma_chn);
+
+ dma_reg->intc &= ~0x1f;
+ dma_reg->intc |= 0x1 << 4;
+ switch (int_type) {
+ case NO_INT:
+ break;
+
+ case FRAG_DONE:
+ dma_reg->intc |= 0x1;
+ break;
+
+ case BLK_DONE:
+ dma_reg->intc |= 0x2;
+ break;
+
+ case TRANS_DONE:
+ dma_reg->intc |= 0x4;
+ break;
+
+ case LIST_DONE:
+ dma_reg->intc |= 0x8;
+ break;
+
+ case CONFIG_ERR:
+ dma_reg->intc |= 0x10;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*convert struct sci_dma_cfg to struct sci_dma_chn_reg*/
+static int __dma_cfg_check_and_convert(u32 dma_chn,
+ const struct sci_dma_cfg *cfg,
+ void __iomem * dma_reg_addr)
+{
+ volatile struct sci_dma_chn_reg *dma_reg;
+ u32 datawidth = 0, req_mode = 0, list_end = 0, fix_en = 0;
+ u32 fix_mode = 0, llist_en = 0, wrap_en = 0, wrap_mode = 0;
+
+ /* only full chn support following features:
+ * 1 transcation tranfer
+ * 2 linklist
+ * 3 copy data from fifo to fifo (not test yet, config the
+ * src_trsf_step = 0 and dst_trsf_step = 0)
+ * 4 wrap mode
+ * 5 src_frag_step or dst_frag_step
+ * 6 src_blk_step or dst_blk_step
+ */
+ if (cfg->transcation_len ||
+ cfg->linklist_ptr ||
+ ((cfg->src_step | cfg->des_step) == 0) ||
+ (cfg->wrap_ptr && cfg->wrap_to) ||
+ (cfg->src_frag_step | cfg->dst_frag_step) ||
+ (cfg->src_blk_step | cfg->dst_blk_step)) {
+ if (dma_chn < FULL_CHN_START) {
+ return -EINVAL;
+ }
+ }
+
+ switch (cfg->datawidth) {
+ case BYTE_WIDTH:
+ datawidth = 0;
+ break;
+ case SHORT_WIDTH:
+ datawidth = 1;
+ break;
+ case WORD_WIDTH:
+ datawidth = 2;
+ break;
+ default:
+ /*linklist config */
+ if (cfg->linklist_ptr)
+ break;
+ printk("DMA config datawidth error!\n");
+ return -EINVAL;
+ }
+
+ /*check step, the step must be Integer multiple of the data width */
+ if (!IS_ALIGNED(cfg->src_step, cfg->datawidth))
+ return -EINVAL;
+
+ if (!IS_ALIGNED(cfg->des_step, cfg->datawidth))
+ return -EINVAL;
+
+ req_mode = cfg->req_mode;
+#if 0
+ /*linklist ptr must aligned with 8 bytes */
+ if (cfg->linklist_ptr) {
+ if (!PTR_ALIGN(cfg->linklist_ptr, 8))
+ return -EINVAL;
+ }
+#endif
+
+ /*addr fix mode */
+ if (cfg->src_step != 0 && cfg->des_step != 0) {
+ fix_en = 0x0;
+ } else {
+ if ((cfg->src_step | cfg->des_step) == 0) {
+ /*only full chn support data copy from fifo to fifo ??? */
+ fix_en = 0x0;
+ } else {
+ fix_en = 0x1;
+ if (cfg->src_step) {
+ /*dest addr is fixed */
+ fix_mode = 0x1;
+ } else {
+ /*src addr is fixed */
+ fix_mode = 0x0;
+ }
+ }
+ }
+
+ /*wrap mode, if wrap_ptr point 0x0, there're some problems, Notices!!! */
+ if (cfg->wrap_ptr && cfg->wrap_to) {
+ wrap_en = 0x1;
+ if (cfg->wrap_to == cfg->src_addr) {
+ wrap_mode = 0x0;
+ } else {
+ if (cfg->wrap_to == cfg->des_addr) {
+ wrap_mode = 0x1;
+ } else {
+ /*error message */
+ return -EINVAL;
+ }
+ }
+ }
+
+ /*Notice!! if the linlklist point 0x0, thers're some porblems */
+ if (cfg->linklist_ptr) {
+ llist_en = 0x1;
+ if (cfg->is_end) {
+ list_end = 0x1;
+ }
+ }
+
+ dma_reg = (struct sci_dma_chn_reg *)dma_reg_addr;
+
+ dma_reg->pause = 0x0;
+ dma_reg->req = 0x0;
+ /*set default priority = 1 */
+ dma_reg->cfg = DMA_PRI_1 << CHN_PRIORITY_OFFSET |
+ llist_en << LLIST_EN_OFFSET;
+ /*src and des addr */
+ dma_reg->src_addr = cfg->src_addr;
+ dma_reg->des_addr = cfg->des_addr;
+ /*frag len */
+ dma_reg->frg_len =
+ (datawidth << SRC_DATAWIDTH_OFFSET) |
+ (datawidth << DES_DATAWIDTH_OFFSET) |
+ (0x0 << SWT_MODE_OFFSET) |
+ (req_mode << REQ_MODE_OFFSET) |
+ (wrap_mode << ADDR_WRAP_SEL_OFFSET) |
+ (wrap_en << ADDR_WRAP_EN_OFFSET) |
+ (fix_mode << ADDR_FIX_SEL_OFFSET) |
+ (fix_en << ADDR_FIX_SEL_EN) |
+ (list_end << LLIST_END_OFFSET) | (cfg->fragmens_len & FRG_LEN_MASK);
+ /*blk len */
+ dma_reg->blk_len = cfg->block_len & BLK_LEN_MASK;
+
+ if (dma_chn < FULL_CHN_START)
+ return 0;
+
+ /*trac len */
+ if (0x0 == cfg->transcation_len) {
+ dma_reg->trsc_len = cfg->block_len & TRSC_LEN_MASK;
+ } else {
+ dma_reg->trsc_len = cfg->transcation_len & TRSC_LEN_MASK;
+ }
+
+ /*trsf step */
+ dma_reg->trsf_step =
+ (cfg->des_step & TRSF_STEP_MASK) << DEST_TRSF_STEP_OFFSET |
+ (cfg->src_step & TRSF_STEP_MASK) << SRC_TRSF_STEP_OFFSET;
+
+ /*wrap ptr */
+ dma_reg->wrap_ptr = cfg->wrap_ptr;
+ dma_reg->wrap_to = cfg->wrap_to;
+
+ dma_reg->llist_ptr = cfg->linklist_ptr;
+ /*frag step */
+ dma_reg->frg_step =
+ (cfg->dst_frag_step & FRAG_STEP_MASK) << DEST_FRAG_STEP_OFFSET |
+ (cfg->src_frag_step & FRAG_STEP_MASK) << SRC_FRAG_STEP_OFFSET;
+
+ /*src and dst blk step */
+ dma_reg->src_blk_step = cfg->src_blk_step;
+ dma_reg->des_blk_step = cfg->dst_blk_step;
+
+ return 0;
+}
+
+/*just clean, not disable*/
+static void __inline __dma_int_clr(u32 dma_chn)
+{
+ volatile struct sci_dma_chn_reg *dma_reg = get_chn_reg_point(dma_chn);
+
+ dma_reg->intc |= 0x1f << 24;
+}
+
+static void __inline __dma_int_dis(u32 dma_chn)
+{
+ volatile struct sci_dma_chn_reg *dma_reg = get_chn_reg_point(dma_chn);
+
+ dma_reg->intc |= 0x1f << 24;
+ dma_reg->intc &= ~0x1f;
+}
+
+static irqreturn_t __dma_irq_handle(int irq, void *dev_id)
+{
+ u32 i;
+ u32 logic_dma_chn_offset, dma_chn;
+ u32 irq_status;
+ volatile struct sci_dma_chn_reg *dma_reg;
+ struct sci_dma_chip *dma_chip = (struct sci_dma_chip*)dev_id;
+ volatile struct sci_dma_glb_reg *dma_glb_reg =
+ (struct sci_dma_glb_reg*)dma_chip->dma_glb_base;
+
+ spin_lock(&dma_chip->chip_lock);
+
+ irq_status = dma_glb_reg->int_msk_sts;
+
+ if (unlikely(0 == irq_status)) {
+ spin_unlock(&dma_chip->chip_lock);
+ return IRQ_NONE;
+ }
+
+ /*AP dma,logic_dma_chn_offset = 1 */
+ /*AON dma,logic_dma_chn_offset = 33 */
+ logic_dma_chn_offset = dma_chip->chn_desc - dma_chns;
+
+ while (irq_status) {
+ i = __ffs(irq_status);
+ irq_status &= (irq_status - 1);
+
+ dma_chn = logic_dma_chn_offset + i;
+
+ dma_reg = get_chn_reg_point(dma_chn);
+
+ /*need to deal with DMA configuration error interrupt*/
+ dma_reg->intc |= 0x1f << 24;
+
+ if (dma_chns[dma_chn].irq_handler) {
+ /*audio driver need to get the dma chn */
+ dma_chns[dma_chn].irq_handler(dma_chn, dma_chns[dma_chn].data);
+ }
+ }
+
+ spin_unlock(&dma_chip->chip_lock);
+
+ return IRQ_HANDLED;
+}
+
+static void __inline __dma_set_uid(u32 dma_chn, u32 dev_id)
+{
+ struct sci_dma_chip *dma_chip = get_dma_chip_point(dma_chn);
+
+ if (DMA_UID_SOFTWARE != dev_id) {
+ __raw_writel(dma_chn,
+ (volatile void *)DMA_REQ_CID(dma_chip->dma_glb_base, dev_id));
+ }
+}
+
+static void __inline __dma_unset_uid(u32 dma_chn, u32 dev_id)
+{
+ struct sci_dma_chip *dma_chip = get_dma_chip_point(dma_chn);
+
+ if (DMA_UID_SOFTWARE != dev_id) {
+ __raw_writel(0x0,
+ (volatile void *)DMA_REQ_CID(dma_chip->dma_glb_base, dev_id));
+ }
+}
+
+static void __inline __dma_chn_enable(u32 dma_chn)
+{
+ volatile struct sci_dma_chn_reg *dma_reg = get_chn_reg_point(dma_chn);
+
+ dma_reg->cfg |= 0x1;
+}
+
+static void __inline __dma_soft_request(u32 dma_chn)
+{
+ volatile struct sci_dma_chn_reg *dma_reg = get_chn_reg_point(dma_chn);
+
+ dma_reg->req |= 0x1;
+}
+
+static void __dma_stop_and_disable(u32 dma_chn)
+{
+ u32 timeout = 0x2000;
+
+ volatile struct sci_dma_chn_reg *dma_reg = get_chn_reg_point(dma_chn);
+
+ /*if the chn has disable already, do nothing */
+ if (!(dma_reg->cfg & 0x1)) {
+ return;
+ }
+
+ dma_reg->pause |= 0x1;
+
+ /*fixme, need to deal with timeout*/
+ while (!(dma_reg->pause & (0x1 << 16))){
+ timeout--;
+ if(timeout == 0){
+ __dma_softreset();
+ break;
+ }
+ }
+
+ dma_reg->cfg &= ~0x1;
+
+ dma_reg->pause = 0x0;
+}
+
+/*HAL layer function*/
+int sci_dma_start(u32 dma_chn, u32 dev_id)
+{
+ if (dma_chn > DMA_CHN_MAX)
+ return -EINVAL;
+
+ dma_chns[dma_chn].dev_id = dev_id;
+ /*fixme, need to check dev_id */
+ __dma_set_uid(dma_chn, dev_id);
+
+ __dma_chn_enable(dma_chn);
+
+ if (DMA_UID_SOFTWARE == dev_id)
+ __dma_soft_request(dma_chn);
+
+ return 0;
+}
+
+int sci_dma_stop(u32 dma_chn, u32 dev_id)
+{
+ if (dma_chn > DMA_CHN_MAX)
+ return -EINVAL;
+
+ __dma_stop_and_disable(dma_chn);
+
+ __dma_int_clr(dma_chn);
+
+ return 0;
+}
+
+int sci_dma_register_irqhandle(u32 dma_chn, dma_int_type int_type,
+ void (*irq_handle) (int, void *), void *data)
+{
+ int ret;
+
+ if (dma_chn > DMA_CHN_MAX)
+ return -EINVAL;
+
+ if (NULL == irq_handle)
+ return -EINVAL;
+
+ ret = __dma_set_int_type(dma_chn, int_type);
+ if (ret < 0)
+ return ret;
+
+ dma_chns[dma_chn].irq_handler = irq_handle;
+ dma_chns[dma_chn].data = data;
+
+ return 0;
+}
+
+int sci_dma_config(u32 dma_chn, struct sci_dma_cfg *cfg_list,
+ u32 node_size, struct reg_cfg_addr *cfg_addr)
+{
+ int ret, i;
+ struct sci_dma_cfg list_cfg;
+ struct sci_dma_chn_reg *dma_reg_list;
+
+ if (dma_chn > DMA_CHN_MAX)
+ return -EINVAL;
+
+ __ap_dma_clk_enable();
+
+ if (node_size > 1)
+ goto linklist_config;
+
+ ret =
+ __dma_cfg_check_and_convert(dma_chn, cfg_list,
+ dma_chns[dma_chn].dma_chn_base);
+ if (ret < 0) {
+ printk("%s %d error\n", __func__, __LINE__);
+ }
+
+ return ret;
+
+ linklist_config:
+ if (NULL == cfg_addr)
+ return -EINVAL;
+
+ dma_reg_list = (struct sci_dma_chn_reg *)cfg_addr->virt_addr;
+
+ for (i = 0; i < node_size; i++) {
+ cfg_list[i].linklist_ptr = cfg_addr->phys_addr +
+ ((i + 1) % node_size) * sizeof(struct sci_dma_chn_reg) + 0x10;
+ ret =
+ __dma_cfg_check_and_convert(dma_chn, cfg_list + i,
+ dma_reg_list + i);
+ if (ret < 0) {
+ printk("%s %d error\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ }
+
+ memset((void *)&list_cfg, 0x0, sizeof(list_cfg));
+
+ list_cfg.linklist_ptr = cfg_addr->phys_addr + 0x10;
+ /*audio driver need get src and dest addr in first node */
+ list_cfg.src_addr = cfg_list[0].src_addr;
+ list_cfg.des_addr = cfg_list[0].des_addr;
+
+ ret =
+ __dma_cfg_check_and_convert(dma_chn, &list_cfg,
+ dma_chns[dma_chn].dma_chn_base);
+
+ return ret;
+}
+
+int sci_dma_request(const char *dev_name, dma_chn_type chn_type)
+{
+ int i;
+ int dma_chn;
+ int dma_chn_start, dma_chn_end;
+
+ if (!dev_name)
+ return -EINVAL;
+
+ dma_chn = -EBUSY;
+
+ switch (chn_type) {
+ case STD_DMA_CHN:
+ dma_chn_start = AP_DMA_CHN_START + STD_CHN_START;
+ dma_chn_end = AP_DMA_CHN_START + STD_CHN_END;
+ break;
+
+ case FULL_DMA_CHN:
+ dma_chn_start = AP_DMA_CHN_START + FULL_CHN_START;
+ dma_chn_end = AP_DMA_CHN_START + FULL_CHN_END;
+ break;
+
+#ifdef AON_DMA_SUPPORT
+ case AON_STD_DMA_CHN:
+ dma_chn_start = AON_DMA_CHN_START + STD_CHN_START;
+ dma_chn_end = AON_DMA_CHN_START + STD_CHN_END;
+ break;
+
+ case AON_FULL_DMA_CHN:
+ dma_chn_start = AON_DMA_CHN_START + FULL_CHN_START;
+ dma_chn_end = AON_DMA_CHN_START + FULL_CHN_END;
+ break;
+#endif
+
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&dma_mutex);
+
+ for (i = dma_chn_start; i <= dma_chn_end; i++) {
+ if (!dma_chns[i].dev_name) {
+ dma_chns[i].dev_name = dev_name;
+ dma_chn = i;
+ break;
+ }
+ }
+
+#ifdef SCI_DMA_DEBUG
+ if (dma_chn >= dma_chn_start) {
+ struct sci_dma_chip *dma_chip;
+ dma_chip = get_dma_chip_point(dma_chn);
+
+ printk("alloc dma chn is %d, and chn reg base is %p, dma chip base is %p\n",
+ dma_chn, dma_chns[i].dma_chn_base, dma_chip->dma_glb_base);
+ }
+#endif
+ mutex_unlock(&dma_mutex);
+
+ return dma_chn;
+}
+
+int sci_dma_free(u32 dma_chn)
+{
+ int i;
+
+ if (dma_chn > DMA_CHN_MAX)
+ return -EINVAL;
+
+ __dma_stop_and_disable(dma_chn);
+
+ __dma_int_dis(dma_chn);
+
+ /*set a valid dma chn for CID, the CID is start with 1 */
+ __dma_unset_uid(dma_chn, dma_chns[dma_chn].dev_id);
+
+ mutex_lock(&dma_mutex);
+
+ dma_chns[dma_chn].dev_name = NULL;
+ dma_chns[dma_chn].irq_handler = NULL;
+ dma_chns[dma_chn].data = NULL;
+ dma_chns[dma_chn].dev_id = 0;
+
+ /*no need to stop AON dma*/
+ if (dma_chn <= FULL_CHN_END) {
+ /*if all AP chn be free, disbale the dma's clk */
+ for (i = STD_CHN_START; i <= FULL_CHN_END; i++) {
+ if (dma_chns[i].dev_name)
+ break;
+ }
+
+ if (i >= DMA_CHN_MAX) {
+ __dma_softreset();
+ __ap_dma_clk_disable();
+ }
+ }
+
+ mutex_unlock(&dma_mutex);
+
+ return 0;
+}
+
+/*support for audio driver to get current src and dst addr*/
+u32 sci_dma_get_src_addr(u32 dma_chn)
+{
+ unsigned long dma_chn_base;
+ if (dma_chn > DMA_CHN_MAX) {
+ printk("dma chn %d is overflow!\n", dma_chn);
+ return 0;
+ }
+
+ if(dma_chn <= AP_DMA_CHN_NUM)
+ dma_chn_base = (unsigned long)dma_chips[AP_DMA_INDEX].dma_glb_base;
+ else
+ dma_chn_base = (unsigned long)dma_chips[AON_DMA_INDEX].dma_glb_base;
+
+ return __raw_readl((volatile void *)DMA_CHN_SRC_ADR(dma_chn_base,(dma_chn-1)));
+}
+
+u32 sci_dma_get_dst_addr(u32 dma_chn)
+{
+ unsigned long dma_chn_base;
+ if (dma_chn > DMA_CHN_MAX) {
+ printk("dma chn %d is overflow!\n", dma_chn);
+ return 0;
+ }
+ if(dma_chn <= AP_DMA_CHN_NUM)
+ dma_chn_base = (unsigned long)dma_chips[AP_DMA_INDEX].dma_glb_base;
+ else
+ dma_chn_base = (unsigned long)dma_chips[AON_DMA_INDEX].dma_glb_base;
+
+ return __raw_readl((volatile void *)DMA_CHN_DES_ADR(dma_chn_base,(dma_chn-1)));
+}
+
+int sci_dma_dump_reg(u32 dma_chn, u32 *reg_base)
+{
+ unsigned long dma_chn_base;
+ if (dma_chn >= DMA_CHN_MAX) {
+ return -EINVAL;
+ }
+ if(dma_chn <= AP_DMA_CHN_NUM)
+ dma_chn_base = (unsigned long)dma_chips[AP_DMA_INDEX].dma_glb_base;
+ else
+ dma_chn_base = (unsigned long)dma_chips[AON_DMA_INDEX].dma_glb_base;
+ if(reg_base) {
+ *reg_base = DMA_CHx_BASE(dma_chn_base, (dma_chn-1));
+ }
+
+ return DMA_CHx_OFFSET;
+}
+
+#define DMA_MEMCPY_MIN_SIZE 64
+#define DMA_MEMCPY_MAX_SIZE TRSC_LEN_MASK
+static void sci_dma_memcpy_irqhandle(int chn, void *data)
+{
+ struct semaphore *dma_sema = (struct semaphore *)data;
+
+ up(dma_sema);
+}
+
+int sci_dma_memcpy(u32 dest, u32 src, size_t size)
+{
+ int ret;
+ int dma_chn;
+ u32 data_width, src_step;
+ u32 irq_mode;
+ struct sci_dma_cfg cfg;
+ struct semaphore dma_seam;
+
+ if (size < DMA_MEMCPY_MIN_SIZE || size > DMA_MEMCPY_MAX_SIZE) {
+ return -EINVAL;
+ }
+
+ if (size <= BLK_LEN_MASK) {
+ dma_chn = sci_dma_request("dma memcpy", STD_DMA_CHN);
+ } else {
+ dma_chn = sci_dma_request("dma memcpy", FULL_DMA_CHN);
+ }
+ if (dma_chn < 0) {
+ printk("alloc dma chn fail\n");
+ return dma_chn;
+ }
+
+ sema_init(&dma_seam, 0);
+
+ memset(&cfg, 0x0, sizeof(cfg));
+
+ if ((size & 0x3) == 0) {
+ data_width = WORD_WIDTH;
+ src_step = 4;
+ } else {
+ if ((size & 0x1) == 0) {
+ data_width = SHORT_WIDTH;
+ src_step = 2;
+ } else {
+ data_width = BYTE_WIDTH;
+ src_step = 1;
+ }
+ }
+
+ cfg.src_addr = src;
+ cfg.des_addr = dest;
+ cfg.datawidth = data_width;
+ cfg.src_step = src_step;
+ cfg.des_step = src_step;
+ cfg.fragmens_len = DMA_MEMCPY_MIN_SIZE;
+ if (size <= BLK_LEN_MASK) {
+ cfg.block_len = size;
+ cfg.req_mode = BLOCK_REQ_MODE;
+ irq_mode = BLK_DONE;
+ } else {
+ cfg.block_len = DMA_MEMCPY_MIN_SIZE;
+ cfg.transcation_len = size;
+ cfg.req_mode = TRANS_REQ_MODE;
+ irq_mode = TRANS_DONE;
+ }
+
+ ret = sci_dma_config(dma_chn, &cfg, 1, NULL);
+ if (ret < 0) {
+ printk("dma memcpy config error!\n");
+ sci_dma_free(dma_chn);
+ return -EINVAL;
+ }
+
+ ret = sci_dma_register_irqhandle(dma_chn, irq_mode,
+ sci_dma_memcpy_irqhandle,
+ &dma_seam);
+ if (ret < 0) {
+ printk("dma register irqhandle failed!\n");
+ sci_dma_free(dma_chn);
+ return ret;
+ }
+
+ sci_dma_start(dma_chn, DMA_UID_SOFTWARE);
+ down(&dma_seam);
+
+ sci_dma_free(dma_chn);
+
+ return 0;
+}
+
+static int __init sci_init_dma(void)
+{
+ int ret,i;
+ u32 dma_irq;
+ void __iomem *dma_reg_base;
+ struct sci_dma_chip *dma_chip;
+ struct sci_dma_chn_desc *chns_desc;
+ struct device_node *dma_node;
+ dma_node = of_find_compatible_node(NULL, NULL, "sprd,sprd-dma");
+ if (!dma_node) {
+ pr_warn("Can't get the dmac node!\n");
+ return -ENODEV;
+ }
+
+ dma_irq = irq_of_parse_and_map(dma_node, 0);
+ if (dma_irq == 0) {
+ pr_warn("Can't get the dma irq number!\n");
+ return -EIO;
+ }
+ pr_info(" dma irq number is %d!\n", dma_irq);
+ dma_reg_base = (void __iomem *)SPRD_DMA0_BASE;
+
+#ifdef AON_DMA_SUPPORT
+ u32 aon_dma_irq;
+ void __iomem *aon_dma_reg_base;
+ struct device_node *aon_dma_node;
+ aon_dma_node = of_find_compatible_node(NULL, NULL, "sprd,aon_dma");
+ if (!aon_dma_node) {
+ pr_warn("Can't get the aon dmac node!\n");
+ return -ENODEV;
+ }
+
+ aon_dma_irq = irq_of_parse_and_map(aon_dma_node, 0);
+ if (dma_irq == 0) {
+ pr_warn("Can't get the aon dma irq number!\n");
+ return -EIO;
+ }
+ pr_info(" dma irq number is %d!\n", aon_dma_irq);
+ aon_dma_reg_base = (void __iomem *)REGS_AON_DMA_BASE;
+#endif
+
+ dma_chip = &dma_chips[AP_DMA_INDEX];
+ dma_chip->dma_glb_base = dma_reg_base;
+ spin_lock_init(&dma_chip->chip_lock);
+
+ chns_desc = dma_chns + AP_DMA_CHN_START;
+ for (i = STD_CHN_START; i <= FULL_CHN_END; i++) {
+ chns_desc[i].dma_chip = dma_chip;
+ chns_desc[i].dma_chn_base = DMA_CHx_BASE(dma_reg_base, i);
+ spin_lock_init(&chns_desc[i].chn_lock);
+ }
+
+ dma_chip->chn_desc = chns_desc;
+
+ ret = request_irq(dma_irq, __dma_irq_handle, 0, "sci-dma",
+ (void*)dma_chip);
+ if (ret) {
+ printk(KERN_ERR "request dma irq failed %d\n", ret);
+ return ret;
+ }
+
+#ifdef AON_DMA_SUPPORT
+ dma_chip = &dma_chips[AON_DMA_INDEX];
+ dma_chip->dma_glb_base = aon_dma_reg_base;
+ spin_lock_init(&dma_chip->chip_lock);
+
+ chns_desc = dma_chns + AON_DMA_CHN_START;
+ for (i = STD_CHN_START; i <= FULL_CHN_END; i++) {
+ chns_desc[i].dma_chip = dma_chip;
+ chns_desc[i].dma_chn_base = DMA_CHx_BASE(aon_dma_reg_base, i);
+ spin_lock_init(&chns_desc[i].chn_lock);
+ }
+
+ dma_chip->chn_desc = chns_desc;
+ ret = request_irq(aon_dma_irq, __dma_irq_handle, 0, "sci-aon-dma",
+ (void*)dma_chip);
+ if (ret) {
+ printk(KERN_ERR "request dma irq failed %d\n", ret);
+ return ret;
+ }
+#endif
+
+ return ret;
+}
+
+#ifdef DMA_DEBUG
+static int __init dma_test(void)
+{
+ sci_dma_memcpy(0x806178d0,0x80000000,256);
+ return 0;
+}
+#endif
+
+int __init dma_new_init(void)
+{
+ int ret;
+ ret = sci_init_dma();
+#ifdef DMA_DEBUG
+ dma_test();
+#endif
+ return ret;
+}
+
+void __exit dma_new_exit(void)
+{
+}
+
+module_init(dma_new_init);
+module_exit(dma_new_exit);
+
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL_GPL(sci_dma_request);
+EXPORT_SYMBOL_GPL(sci_dma_free);
+EXPORT_SYMBOL_GPL(sci_dma_config);
+EXPORT_SYMBOL_GPL(sci_dma_register_irqhandle);
+EXPORT_SYMBOL_GPL(sci_dma_start);
+EXPORT_SYMBOL_GPL(sci_dma_stop);
+EXPORT_SYMBOL_GPL(sci_dma_memcpy);
diff --git a/drivers/platform/sprd/dmc_dfs_test.c b/drivers/platform/sprd/dmc_dfs_test.c
new file mode 100644
index 00000000..3e186e32
--- /dev/null
+++ b/drivers/platform/sprd/dmc_dfs_test.c
@@ -0,0 +1,112 @@
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/kobject.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <soc/sprd/common.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/sci.h>
+#include <linux/earlysuspend.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <linux/random.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/cacheflush.h>
+#include <asm/suspend.h>
+#include <linux/vmalloc.h>
+#include <linux/cpu.h>
+#include <linux/printk.h>
+#include <soc/sprd/__sc8830_dmc_dfs.h>
+#include "soc/sprd/chip_x30g/dram_phy_28nm.h"
+
+#ifdef EMC_FREQ_AUTO_TEST
+static u32 dfs_freq_array[] = {
+ //192,
+ 200,
+ //384,
+ 400
+ //466
+ //533
+};
+
+static struct wake_lock dfs_test_lock;
+static DEFINE_SPINLOCK(emc_freq_spinlock);
+
+extern int scxx30_all_nonboot_cpus_died(void);
+extern u32 emc_clk_set(u32 new_clk, u32 sene);
+
+static int emc_freq_test_thread(void * data)
+{
+ u32 i = 0;
+ unsigned long spinlock_flags = 0;
+ msleep(17000);
+ wake_lock(&dfs_test_lock);
+
+ /* close cpu. */
+// cpu_down(1);
+// msleep(200);
+
+// cpu_down(2);
+// msleep(200);
+
+// cpu_down(3);
+// msleep(2000);
+
+ while(1){
+ spin_lock_irqsave(&emc_freq_spinlock, spinlock_flags);
+
+ /* check current cpu number is 1 or not. */
+ if (scxx30_all_nonboot_cpus_died() != 1){
+ printk("*** %s, num_online_cpus:%d ***\n", __func__, num_online_cpus() );
+ return 0;
+ }
+
+ //set_current_state(TASK_INTERRUPTIBLE);
+// i = get_random_int();
+// i = i % 2;//(sizeof(dfs_freq_array)/ sizeof(dfs_freq_array[0]));
+ printk("emc_freq_test_thread i = %x, clk=%d\n", i, dfs_freq_array[i]);
+
+ emc_clk_set(dfs_freq_array[i], 0);
+
+ printk("e\n");
+
+ i++;
+ if (i >= 2) {
+ i = 0;
+ }
+
+ /* resume irq, cpu and so on. */
+ spin_unlock_irqrestore(&emc_freq_spinlock, spinlock_flags);
+
+// schedule_timeout(10);
+ msleep(50);
+ }
+
+ /* open cpu */
+// cpu_up(1);
+// cpu_up(2);
+// cpu_up(3);
+ return 0;
+}
+
+/* used this test must make sure cpu is only one. */
+/* platsmp.c : sci_get_core_num() return 1 means signal core */
+void __emc_freq_test(void)
+{
+ struct task_struct * task;
+ wake_lock_init(&dfs_test_lock, WAKE_LOCK_SUSPEND,"emc_freq_test_wakelock");
+ task = kthread_create(emc_freq_test_thread, NULL, "emc freq test test");
+ if (task == 0) {
+ printk("Can't crate emc freq test thread!\n");
+ }else {
+ wake_up_process(task);
+ }
+}
+#endif
diff --git a/drivers/platform/sprd/dmc_freq.c b/drivers/platform/sprd/dmc_freq.c
new file mode 100755
index 00000000..8408fee8
--- /dev/null
+++ b/drivers/platform/sprd/dmc_freq.c
@@ -0,0 +1,816 @@
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/kobject.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <mach/sci.h>
+#include <linux/earlysuspend.h>
+#include <mach/sci_glb_regs.h>
+#include <linux/random.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/cacheflush.h>
+#include <mach/sc8830_emc_freq_data.h>
+#include <asm/suspend.h>
+#include <linux/vmalloc.h>
+#include <linux/printk.h>
+#include <mach/__sc8830_dmc_dfs.h>
+static u32 max_clk = 0;
+static volatile u32 cp_code_addr = 0x0;
+static DEFINE_MUTEX(emc_mutex);
+#define CP_DEBUG_ADDR (cp_code_addr + 0xFF8)
+#define CP_FLAGS_ADDR (cp_code_addr + 0xFFC)
+#define CP_PARAM_ADDR (cp_code_addr + 0xF00)
+#define DDR_TIMING_REG_VAL_ADDR (SPRD_IRAM0H_BASE + 0xc00)
+
+#define debug(format, arg...) pr_debug("emc_freq" "" format, ## arg)
+#define info(format, arg...) pr_info("emc_freq: " "" format, ## arg)
+
+static u32 emc_freq = 0;
+static u32 emc_delay = 20;
+static u32 chip_id = 0;
+static ddr_dfs_val_t __emc_param_configs[5];
+static void __timing_reg_dump(ddr_dfs_val_t * dfs_val_ptr);
+u32 emc_clk_get(void);
+static u32 get_dpll_clk(void);
+#ifdef CONFIG_SCX35_DMC_FREQ_AP
+static void emc_dfs_code_copy(u8 * dest);
+static int emc_dfs_call(unsigned long flag);
+void emc_dfs_main(unsigned long flag);
+#endif
+#define MAX_DLL_DISABLE_CLK 200
+#define MIN_DPLL_CLK 250
+enum{
+ CLK_EMC_SELECT_26M = 0,
+ CLK_EMC_SELECT_TDPLL = 1,
+ CLK_EMC_SELECT_DPLL = 2,
+};
+//#define EMC_FREQ_AUTO_TEST
+static ddr_dfs_val_t *__dmc_param_config(u32 clk)
+{
+ u32 i;
+ ddr_dfs_val_t * ret_timing = 0;
+ if(clk == 533) {
+ clk = 532;
+ }
+ if(clk == 333) {
+ clk = 332;
+ }
+ for(i = 0; i < (sizeof(__emc_param_configs) / sizeof(__emc_param_configs[0])); i++) {
+ if(__emc_param_configs[i].ddr_clk >= clk) {
+ ret_timing = &__emc_param_configs[i];
+ break;
+ }
+ }
+ //__timing_reg_dump(ret_timing);
+ if(ret_timing) {
+#ifndef CONFIG_SCX35_DMC_FREQ_AP
+ memcpy((void *)CP_PARAM_ADDR, ret_timing, sizeof(ddr_dfs_val_t));
+#endif
+ }
+ return ret_timing;
+}
+#ifndef CONFIG_SCX35_DMC_FREQ_AP
+static void cp_code_init(void)
+{
+ u32 *copy_data;
+ u32 copy_size;
+ u32 code_phy_addr;
+
+#ifdef CONFIG_SCX35_DMC_FREQ_CP0
+ copy_data = cp0_dfs_code_data;
+ copy_size = sizeof(cp0_dfs_code_data);
+ code_phy_addr = 0x50000000;
+#endif
+
+#ifdef CONFIG_SCX35_DMC_FREQ_CP1
+ copy_data = cp1_dfs_code_data;
+ copy_size = sizeof(cp1_dfs_code_data);
+ code_phy_addr = 0x50001800;
+#endif
+
+#ifdef CONFIG_SCX35_DMC_FREQ_CP2
+ copy_data = cp2_dfs_code_data;
+ copy_size = sizeof(cp2_dfs_code_data);
+ code_phy_addr = 0x50003000;
+#endif
+ cp_code_addr = (volatile u32)ioremap(code_phy_addr,0x1000);
+ memcpy((void *)cp_code_addr, (void *)copy_data, copy_size);
+}
+static void close_cp(void)
+{
+ u32 value;
+ u32 times;
+
+ value = __raw_readl(CP_FLAGS_ADDR);
+ times = 0;
+ while((value & EMC_FREQ_SWITCH_STATUS_MASK) != (EMC_FREQ_SWITCH_COMPLETE << EMC_FREQ_SWITCH_STATUS_OFFSET)) {
+ value = __raw_readl(CP_FLAGS_ADDR);
+ mdelay(2);
+ if(times >= 100) {
+ break;
+ }
+ times ++;
+ }
+ info("__emc_clk_set flag = 0x%08x , version flag = 0x%08x phy register = 0x%08x\n", __raw_readl(CP_FLAGS_ADDR),__raw_readl(CP_FLAGS_ADDR - 4), __raw_readl(SPRD_LPDDR2_PHY_BASE + 0x4));
+ udelay(200);
+}
+static void wait_cp_run(void)
+{
+ u32 val;
+ u32 times = 0;
+ val = __raw_readl(CP_FLAGS_ADDR - 4);
+ while(val != 0x11223344/*cp run flag*/) {
+ mdelay(2);
+ val = __raw_readl(CP_FLAGS_ADDR - 4);
+ times ++;
+ if(times >= 10) {
+ panic("wait_cp2_run timeout\n");
+ }
+ }
+ __raw_writel(0x44332211, CP_FLAGS_ADDR - 4)/*for watchdog reset, so clear it*/;
+}
+#endif
+#ifdef EMC_FREQ_AUTO_TEST
+static u32 get_sys_cnt(void)
+{
+ return __raw_readl(SPRD_GPTIMER_BASE + 0x44);
+}
+#endif
+
+static void __set_dpll_clk(u32 clk)
+{
+ u32 reg = 0;
+ u32 dpll = 0;
+ u32 dpll_lpf = 6;
+ u32 dpll_ibias = 1;
+ if(get_dpll_clk() == clk) {
+ return;
+ }
+ reg = sci_glb_read(REG_AON_APB_DPLL_CFG,-1);
+ reg &= ~0x7ff;
+ reg &= ~BITS_DPLL_LPF(0x7);
+ reg &= ~BITS_DPLL_IBIAS(0x3);
+
+ if((reg & 0x03000000) == 0x00000000){
+ dpll = clk >> 1;
+ }
+ if((reg & 0x03000000) == 0x01000000) {
+ dpll = clk >> 2;
+ }
+ if((reg & 0x03000000) == 0x02000000) {
+ dpll = clk / 13;
+ }
+ if((reg & 0x03000000) == 0x03000000) {
+ dpll = clk / 26;
+ }
+ if(dpll < MIN_DPLL_CLK) {
+ dpll_lpf = 0x2;
+ dpll_ibias = 0x1;
+ } else {
+ dpll_lpf = 0x6;
+ dpll_ibias = 0x1;
+ }
+ reg |= dpll | BITS_DPLL_LPF(dpll_lpf) | BITS_DPLL_IBIAS(dpll_ibias);
+ sci_glb_write(REG_AON_APB_DPLL_CFG, reg, -1);
+ udelay(100);
+}
+
+static u32 is_current_set = 0;
+static u32 __emc_clk_set(u32 clk, u32 sene, u32 dll_enable, u32 bps_200)
+{
+ u32 flag = 0;
+ ddr_dfs_val_t * ret_timing;
+ ret_timing = __dmc_param_config(clk);
+
+ if(!ret_timing) {
+ return -1;
+ }
+ clk = ret_timing->ddr_clk;
+ if(clk == 533) {
+ clk = 532;
+ }
+ if(clk == 333) {
+ clk = 332;
+ }
+
+
+#ifdef CONFIG_SCX35_DMC_FREQ_DDR3
+ if(clk == 400){
+ clk = 384;
+ }
+ if(clk == 466){
+ clk = 464;
+ }
+ flag = (EMC_DDR_TYPE_DDR3 << EMC_DDR_TYPE_OFFSET) | (clk << EMC_CLK_FREQ_OFFSET);
+ flag |= EMC_FREQ_NORMAL_SCENE << EMC_FREQ_SENE_OFFSET;
+ flag |= dll_enable;
+ flag |= 0x0 << EMC_CLK_DIV_OFFSET;
+#else
+ flag = (EMC_DDR_TYPE_LPDDR2 << EMC_DDR_TYPE_OFFSET) | (clk << EMC_CLK_FREQ_OFFSET);
+ flag |= sene << EMC_FREQ_SENE_OFFSET;
+ flag |= dll_enable;
+ flag |= bps_200 << EMC_BSP_BPS_200_OFFSET;
+#endif
+
+#ifdef CONFIG_SCX35_DMC_FREQ_AP
+ if((sene<2) || (sene>4))
+ {
+ panic("invalid scene for ap dfs, valid number is only 2,3,4!\n");
+ return -1;
+ }
+
+ if((sene!=EMC_FREQ_NORMAL_SWITCH_SENE) && (clk != 192))
+ {
+ panic("invalid scene clk combination, sleep and resume must be tdpll 192M!\n");
+ return -1;
+ }
+
+ if(sene == EMC_FREQ_DEEP_SLEEP_SENE)
+ {
+ __set_dpll_clk(192);
+ }
+
+ /*flush_cache_all();*/
+ cpu_suspend(flag, emc_dfs_call);
+
+ if(sene == EMC_FREQ_RESUME_SENE)
+ {
+ __set_dpll_clk(332);
+ }
+#else
+ __raw_writel(flag, CP_FLAGS_ADDR);
+
+ #ifdef CONFIG_SCX35_DMC_FREQ_CP0
+ sci_glb_set(SPRD_IPI_BASE,1 << 0);//send ipi interrupt to cp0
+ #endif
+
+ #ifdef CONFIG_SCX35_DMC_FREQ_CP1
+ sci_glb_set(SPRD_IPI_BASE,1 << 4);//send ipi interrupt to cp1
+ #endif
+
+ #ifdef CONFIG_SCX35_DMC_FREQ_CP2
+ sci_glb_set(SPRD_IPI_BASE,1 << 8);//send ipi interrupt to cp2
+ #endif
+ close_cp();
+ info("__emc_clk_set clk = %d REG_AON_APB_DPLL_CFG = %x, PUBL_DLLGCR = %x\n",
+ clk, sci_glb_read(REG_AON_APB_DPLL_CFG, -1), __raw_readl(SPRD_LPDDR2_PHY_BASE + 0x04));
+#endif
+ if(emc_clk_get() != clk) {
+ info("clk set error, set clk = %d, get clk = %d, sence = %d\n", clk, emc_clk_get(), sene);
+ }
+ else{
+ info("clk set success, set clk = %d, get clk = %d, sence = %d\n", clk, emc_clk_get(), sene);
+ }
+ return 0;
+}
+
+static u32 get_emc_clk_select(u32 clk)
+{
+ u32 sel;
+ u32 reg_val;
+ reg_val = sci_glb_read(REG_AON_CLK_EMC_CFG, -1);
+ sel = reg_val & 0x3;
+ switch(sel) {
+ case 0:
+ sel = CLK_EMC_SELECT_26M;
+ break;
+ case 1:
+ case 2:
+ sel = CLK_EMC_SELECT_TDPLL;
+ break;
+ case 3:
+ sel = CLK_EMC_SELECT_DPLL;
+ break;
+ default:
+ break;
+ }
+ return sel;
+}
+/*
+ * open or close dpll
+ * flag = 1, open dpll
+ * flag = 0, close dpll
+ */
+static u32 dpll_rel_cfg_bak = 0;
+static void __dpll_open(u32 flag)
+{
+ u32 reg;
+ u32 mask;
+ mask = BIT_DPLL_AP_SEL | BIT_DPLL_CP0_SEL | BIT_DPLL_CP1_SEL | BIT_DPLL_CP2_SEL;
+ reg = sci_glb_read(REG_PMU_APB_DPLL_REL_CFG, -1) & mask;
+ if(flag) {
+ if(reg == 0) {
+ sci_glb_set(REG_PMU_APB_DPLL_REL_CFG, mask & dpll_rel_cfg_bak);
+ udelay(500);
+ }
+ }
+ else {
+ sci_glb_clr(REG_PMU_APB_DPLL_REL_CFG, mask);
+ }
+}
+u32 emc_clk_set(u32 new_clk, u32 sene)
+{
+ u32 dll_enable = EMC_DLL_SWITCH_ENABLE_MODE;
+ u32 old_clk,old_select,new_select,div = 0x0;
+ u32 ret;
+#ifdef EMC_FREQ_AUTO_TEST
+ u32 start_t1, end_t1;
+ static u32 max_u_time = 0;
+ u32 current_u_time;
+
+ start_t1 = get_sys_cnt();
+#endif
+
+#if defined (EMC_FREQ_AUTO_TEST) || defined (CONFIG_SCX35_DMC_FREQ_AP)
+ unsigned long irq_flags;
+ if((new_clk > 200) || (sene != EMC_FREQ_NORMAL_SWITCH_SENE)) {
+ __dpll_open(1);
+ }
+ local_irq_save(irq_flags);
+#else
+ mutex_lock(&emc_mutex);
+#endif
+
+ /*info("emc clk going on %d #########################################################\n",new_clk);*/
+ //mutex_lock(&emc_mutex);
+ if(new_clk > max_clk) {
+ new_clk = max_clk;
+ }
+ if(emc_clk_get() == new_clk) {
+ // mutex_unlock(&emc_mutex);
+ goto out;
+ // return 0;
+ }
+ if(new_clk <= 200) {
+ dll_enable = 0;
+ }
+ old_clk = emc_clk_get();
+ if(is_current_set == 1) {
+ panic("now other thread set dmc clk\n");
+ goto out;
+ // return 0;
+ }
+ is_current_set ++;
+ //info("REG_AON_CLK_PUB_AHB_CFG = %x\n", __raw_readl(REG_AON_CLK_PUB_AHB_CFG));
+ //info("emc_clk_set old = %d, new = %d\n", old_clk, new_clk);
+ //info("emc_clk_set clk = %d, sene = %d, dll_enable = %d, emc_delay = %x\n", new_clk, sene,dll_enable, emc_delay);
+#ifdef CONFIG_SCX35_DMC_FREQ_AP
+ if(new_clk <= 200){
+ new_clk = 192;
+ }
+ if(new_clk > 200)
+ {
+ new_clk = 332;
+ }
+
+ if(new_clk == old_clk)
+ {
+ if(sene == EMC_FREQ_NORMAL_SWITCH_SENE)
+ {
+ is_current_set --;
+ goto out;
+ }
+ // return 0;
+ }
+ __emc_clk_set(new_clk,sene,EMC_DLL_SWITCH_DISABLE_MODE, EMC_BSP_BPS_200_NOT_CHANGE);
+#else
+ if( (new_clk >= 0) && (new_clk <= 200) )
+ {
+ new_clk = 200;
+ }
+ else if((new_clk > 200) && (new_clk <=400))
+ {
+ new_clk = 384;
+ }
+
+ if(emc_clk_get() == new_clk)
+ {
+ is_current_set--;
+ goto out;
+ //return 0;
+ }
+
+ if(new_clk <= MAX_DLL_DISABLE_CLK){
+ dll_enable = EMC_DLL_SWITCH_DISABLE_MODE;
+ }
+ old_select = get_emc_clk_select(old_clk);
+ if((new_clk == 384) || (new_clk == 256)) {
+ new_select = CLK_EMC_SELECT_TDPLL;
+ }
+ else {
+ new_select = CLK_EMC_SELECT_DPLL;
+ }
+
+
+ if(new_select == CLK_EMC_SELECT_TDPLL) {
+ ret = __emc_clk_set(new_clk, 0, dll_enable, 0);
+ }
+ else {
+ if(old_select == new_select) {
+ ret = __emc_clk_set(384, 0, EMC_DLL_SWITCH_ENABLE_MODE, 0);
+ if(ret != 0) {
+ is_current_set --;
+ goto out;
+ }
+ }
+ __set_dpll_clk(new_clk * (div + 1));
+ __emc_clk_set(new_clk, 0, dll_enable, 0);
+ }
+
+ printk("sys timer = 0x%08x, ap sys count = 0x%08x\n", __raw_readl(SPRD_SYSTIMER_CMP_BASE + 4), __raw_readl(SPRD_SYSCNT_BASE + 0xc));
+#endif
+ //mutex_unlock(&emc_mutex);
+ is_current_set --;
+
+out:
+#ifdef EMC_FREQ_AUTO_TEST
+ end_t1 = get_sys_cnt();
+
+ current_u_time = (start_t1 - end_t1)/128;
+ if(max_u_time < current_u_time) {
+ max_u_time = current_u_time;
+ }
+ info("**************emc dfs use current = %08u max %08u\n", current_u_time, max_u_time);
+#endif
+
+
+#if defined (EMC_FREQ_AUTO_TEST) || defined(CONFIG_SCX35_DMC_FREQ_AP)
+ local_irq_restore(irq_flags);
+ if(CLK_EMC_SELECT_DPLL != get_emc_clk_select(0)) {
+ __dpll_open(0);
+ }
+#else
+ mutex_unlock(&emc_mutex);
+#endif
+ info("__emc_clk_set REG_AON_APB_DPLL_CFG = %x, PUBL_DLLGCR = %x\n",sci_glb_read(REG_AON_APB_DPLL_CFG, -1), __raw_readl(SPRD_LPDDR2_PHY_BASE + 0x04));
+ return 0;
+}
+EXPORT_SYMBOL(emc_clk_set);
+
+static u32 get_dpll_clk(void)
+{
+ u32 clk;
+ u32 reg;
+ reg = sci_glb_read(REG_AON_APB_DPLL_CFG, -1);
+ clk = reg & 0x7ff;
+ if((reg & 0x03000000) == 0x00000000) {
+ clk *= 2;
+ }
+ if((reg & 0x03000000) == 0x01000000) {
+ clk *= 4;
+ }
+ if((reg & 0x03000000) == 0x02000000) {
+ clk *= 13;
+ }
+ if((reg & 0x03000000) == 0x03000000) {
+ clk *= 26;
+ }
+ return clk;
+}
+u32 emc_clk_get(void)
+{
+ u32 pll_clk;
+ u32 div;
+ u32 reg_val;
+ u32 sel;
+ u32 clk;
+ reg_val = sci_glb_read(REG_AON_CLK_EMC_CFG, -1);
+ sel = reg_val & 0x3;
+ div = (reg_val >> 8) & 0x3;
+ switch(sel) {
+ case 0:
+ pll_clk = 26;
+ break;
+ case 1:
+ pll_clk = 256;
+ break;
+ case 2:
+ pll_clk = 384;
+ break;
+ case 3:
+ pll_clk = get_dpll_clk();
+ break;
+ //default:
+ // break;
+ }
+ clk = pll_clk / (div + 1);
+ return clk;
+}
+EXPORT_SYMBOL(emc_clk_get);
+
+static int debugfs_emc_freq_get(void *data, u64 * val)
+{
+ u32 freq;
+ freq = emc_clk_get();
+ info("debugfs_emc_freq_get %d\n",freq);
+ *(u32 *) data = *val = freq;
+ return 0;
+}
+static int debugfs_emc_freq_set(void *data, u64 val)
+{
+ int new_freq = *(u32 *) data = val;
+ emc_clk_set(new_freq, 0);
+ return 0;
+}
+
+static int debugfs_emc_delay_get(void *data, u64 * val)
+{
+ *(u32 *) data = *val = emc_delay;
+ return 0;
+}
+static int debugfs_emc_delay_set(void *data, u64 val)
+{
+ emc_delay = *(u32 *) data = val;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fops_emc_freq,
+ debugfs_emc_freq_get, debugfs_emc_freq_set, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(fops_emc_delay,
+ debugfs_emc_delay_get, debugfs_emc_delay_set, "%llu\n");
+static u32 get_spl_emc_clk_set(void)
+{
+ return emc_clk_get();
+}
+static void emc_debugfs_creat(void)
+{
+ static struct dentry *debug_root = NULL;
+ debug_root = debugfs_create_dir("emc", NULL);
+ if (IS_ERR_OR_NULL(debug_root)) {
+ printk("%s return %p\n", __FUNCTION__, debug_root);
+ }
+ debugfs_create_file("freq", S_IRUGO | S_IWUGO,
+ debug_root, &emc_freq, &fops_emc_freq);
+ debugfs_create_file("delay", S_IRUGO | S_IWUGO,
+ debug_root, &emc_delay, &fops_emc_delay);
+}
+static void emc_earlysuspend(struct early_suspend *h)
+{
+#ifndef EMC_FREQ_AUTO_TEST
+ emc_clk_set(200, EMC_FREQ_NORMAL_SCENE);
+#endif
+}
+static void emc_late_resume(struct early_suspend *h)
+{
+#ifndef EMC_FREQ_AUTO_TEST
+ emc_clk_set(max_clk, EMC_FREQ_NORMAL_SCENE);
+#endif
+}
+static struct early_suspend emc_early_suspend_desc = {
+ .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 100,
+ .suspend = emc_earlysuspend,
+ .resume = emc_late_resume,
+};
+#ifdef EMC_FREQ_AUTO_TEST
+#ifdef CONFIG_SCX35_DMC_FREQ_DDR3
+static u32 emc_freq_valid_array[] = {
+ 200,
+ 384,
+ 532
+};
+#else
+static u32 emc_freq_valid_array[] = {
+ //100,
+ 200,
+ 332,
+ 384,
+ 532,
+};
+#endif //CONFIG_SCX35_DMC_FREQ_DDR3
+static struct wake_lock emc_freq_test_wakelock;
+static int emc_freq_test_thread(void * data)
+{
+ u32 i = 0;
+ wake_lock(&emc_freq_test_wakelock);
+ msleep(20000);
+ while(1){
+ set_current_state(TASK_INTERRUPTIBLE);
+ i = get_random_int();
+ i = i % (sizeof(emc_freq_valid_array)/ sizeof(emc_freq_valid_array[0]));
+ printk("emc_freq_test_thread i = %x\n", i);
+ emc_clk_set(emc_freq_valid_array[i], EMC_FREQ_NORMAL_SWITCH_SENE);
+ schedule_timeout(10);
+ }
+ return 0;
+}
+
+static void __emc_freq_test(void)
+{
+ struct task_struct * task;
+ wake_lock_init(&emc_freq_test_wakelock, WAKE_LOCK_SUSPEND,
+ "emc_freq_test_wakelock");
+ task = kthread_create(emc_freq_test_thread, NULL, "emc freq test test");
+ if (task == 0) {
+ printk("Can't crate emc freq test thread!\n");
+ }else {
+ wake_up_process(task);
+ }
+}
+#endif
+
+static void __timing_reg_dump(ddr_dfs_val_t * dfs_val_ptr)
+{
+ debug("umctl2_rfshtmg %x\n", dfs_val_ptr->ddr_clk);
+ debug("umctl2_rfshtmg %x\n", dfs_val_ptr->umctl2_rfshtmg);
+ debug("umctl2_dramtmg0 %x\n", dfs_val_ptr->umctl2_dramtmg0);
+ debug("umctl2_dramtmg1 %x\n", dfs_val_ptr->umctl2_dramtmg1);
+ debug("umctl2_dramtmg2 %x\n", dfs_val_ptr->umctl2_dramtmg2);
+ debug("umctl2_dramtmg3 %x\n", dfs_val_ptr->umctl2_dramtmg3);
+ debug("umctl2_dramtmg4 %x\n", dfs_val_ptr->umctl2_dramtmg4);
+ debug("umctl2_dramtmg5 %x\n", dfs_val_ptr->umctl2_dramtmg5);
+ debug("umctl2_dramtmg6 %x\n", dfs_val_ptr->umctl2_dramtmg6);
+ debug("umctl2_dramtmg7 %x\n", dfs_val_ptr->umctl2_dramtmg7);
+ debug("umctl2_dramtmg8 %x\n", dfs_val_ptr->umctl2_dramtmg8);
+ debug("publ_dx0gcr %x\n", dfs_val_ptr->publ_dx0gcr);
+ debug("publ_dx1gcr %x\n", dfs_val_ptr->publ_dx1gcr);
+ debug("publ_dx2gcr %x\n", dfs_val_ptr->publ_dx2gcr);
+ debug("publ_dx3gcr %x\n", dfs_val_ptr->publ_dx3gcr);
+ debug("publ_dx0dqstr %x\n", dfs_val_ptr->publ_dx0dqstr);
+ debug("publ_dx1dqstr %x\n", dfs_val_ptr->publ_dx1dqstr);
+ debug("publ_dx2dqstr %x\n", dfs_val_ptr->publ_dx2dqstr);
+ debug("publ_dx3dqstr %x\n", dfs_val_ptr->publ_dx3dqstr);
+}
+static void __emc_timing_reg_init(void)
+{
+ ddr_dfs_val_t * dfs_val_ptr;
+ ddr_dfs_val_t *dmc_timing_ptr;
+ u32 i;
+ memset(__emc_param_configs, 0, sizeof(__emc_param_configs));
+ for(i = 0; i < 5; i++) {
+ dfs_val_ptr = (ddr_dfs_val_t *)(DDR_TIMING_REG_VAL_ADDR + i * sizeof(ddr_dfs_val_t));
+ dmc_timing_ptr = 0;
+ if((dfs_val_ptr->ddr_clk >= 100) && (dfs_val_ptr->ddr_clk <= 533)) {
+ dmc_timing_ptr = &__emc_param_configs[i];
+ }
+ if(dfs_val_ptr->ddr_clk == 333) {
+ dfs_val_ptr->ddr_clk = 332;
+ }
+ if(dfs_val_ptr->ddr_clk == 533) {
+ dfs_val_ptr->ddr_clk = 532;
+ }
+ if(dfs_val_ptr->ddr_clk == 466) {
+ dfs_val_ptr->ddr_clk = 464;
+ }
+ if(dmc_timing_ptr) {
+ memcpy(dmc_timing_ptr, dfs_val_ptr, sizeof(*dfs_val_ptr));
+ }
+ }
+ for(i = 0; i < 5; i++) {
+ __timing_reg_dump(&__emc_param_configs[i]);
+ }
+}
+#ifdef CONFIG_SCX35_DMC_FREQ_AP
+static int emc_dfs_call(unsigned long flag)
+{
+ cpu_switch_mm(init_mm.pgd, &init_mm);
+ ((int (*)(unsigned long))SPRD_IRAM0H_PHYS)(flag); //iram0h must be the first function of dfs
+ return 0;
+}
+static void emc_dfs_code_copy(u8 * dest)
+{
+ memcpy_toio((void *)dest, (void *)emc_dfs_main, 0xc00);
+}
+
+#else
+static void cp_init(void)
+{
+ u32 val;
+#ifdef CONFIG_SCX35_DMC_FREQ_CP0 //dfs is at cp0
+ cp_code_init();
+ val = BIT_PD_CP0_ARM9_0_AUTO_SHUTDOWN_EN;
+ val |= BITS_PD_CP0_ARM9_0_PWR_ON_DLY(0x8);
+ val |= BITS_PD_CP0_ARM9_0_PWR_ON_SEQ_DLY(0x6);
+ val |= BITS_PD_CP0_ARM9_0_ISO_ON_DLY(0x2);
+ sci_glb_write(REG_PMU_APB_PD_CP0_ARM9_0_CFG, val, -1);
+ sci_glb_set(REG_PMU_APB_CP_SOFT_RST, 1 << 0);//reset cp0
+ udelay(200);
+ sci_glb_clr(REG_PMU_APB_PD_CP0_SYS_CFG, 1 << 25);//power on cp0
+ mdelay(4);
+ sci_glb_clr(REG_PMU_APB_PD_CP0_SYS_CFG, 1 << 28);//close cp0 force sleep
+ mdelay(2);
+ sci_glb_clr(REG_PMU_APB_CP_SOFT_RST, 1 << 0);//release cp0
+
+ wait_cp_run();
+#endif
+
+#ifdef CONFIG_SCX35_DMC_FREQ_CP1 //dfs is at cp1
+ cp_code_init();
+ val = BIT_PD_CP1_ARM9_AUTO_SHUTDOWN_EN;
+ val |= BITS_PD_CP1_ARM9_PWR_ON_DLY(0x8);
+ val |= BITS_PD_CP1_ARM9_PWR_ON_SEQ_DLY(0x6);
+ val |= BITS_PD_CP1_ARM9_ISO_ON_DLY(0x2);
+ sci_glb_write(REG_PMU_APB_PD_CP1_ARM9_CFG, val, -1);
+ sci_glb_set(REG_PMU_APB_CP_SOFT_RST, 1 << 1);//reset cp1
+ udelay(200);
+ sci_glb_clr(REG_PMU_APB_PD_CP1_SYS_CFG, 1 << 25);//power on cp1
+ mdelay(4);
+ sci_glb_clr(REG_PMU_APB_PD_CP1_SYS_CFG, 1 << 28);//close cp1 force sleep
+ mdelay(2);
+ sci_glb_clr(REG_PMU_APB_CP_SOFT_RST, 1 << 1);//release cp1
+
+ wait_cp_run();
+#endif
+
+#ifdef CONFIG_SCX35_DMC_FREQ_CP2 //dfs is at cp2
+ cp_code_init();
+ val = BIT_PD_CP2_ARM9_AUTO_SHUTDOWN_EN;
+ val |= BITS_PD_CP2_ARM9_PWR_ON_DLY(0x8);
+ val |= BITS_PD_CP2_ARM9_PWR_ON_SEQ_DLY(0x4);
+ val |= BITS_PD_CP2_ARM9_ISO_ON_DLY(0x2);
+ sci_glb_write(REG_PMU_APB_PD_CP2_ARM9_CFG, val, -1);
+
+ sci_glb_set(REG_PMU_APB_CP_SOFT_RST, 1 << 2);//reset cp2
+ udelay(200);
+ sci_glb_clr(REG_PMU_APB_PD_CP2_SYS_CFG, 1 << 25);//power on cp2
+ mdelay(4);
+ sci_glb_clr(REG_PMU_APB_PD_CP2_SYS_CFG, 1 << 28);//close cp2 force sleep
+ mdelay(2);
+ sci_glb_clr(REG_PMU_APB_CP_SOFT_RST, 1 << 2);//reset cp2
+
+ wait_cp_run();
+#endif
+}
+#endif
+
+
+static int dmcfreq_pm_notifier_do(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ unsigned long irq_flags;
+ printk("*** %s, event:0x%x ***\n", __func__, event );
+
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ /*
+ * DMC must be set 200MHz before deep sleep in ES chips
+ */
+ emc_clk_set(200, EMC_FREQ_NORMAL_SWITCH_SENE); //nomarl switch to tdpll 192
+ emc_clk_set(200, EMC_FREQ_DEEP_SLEEP_SENE); //deep sleep tdpll192 to dpll 192
+ return NOTIFY_OK;
+ case PM_POST_RESTORE:
+ case PM_POST_SUSPEND:
+ /* Reactivate */
+ emc_clk_set(200, EMC_FREQ_RESUME_SENE); //resume dpll192 -- tdpll192
+ emc_clk_set(max_clk, EMC_FREQ_NORMAL_SWITCH_SENE); //nomarl tdpll192 -- dpll332
+ return NOTIFY_OK;
+ }
+ printk("*** %s, event:0x%x done***\n", __func__, event );
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block dmcfreq_pm_notifier = {
+ .notifier_call = dmcfreq_pm_notifier_do,
+};
+
+
+static int __init emc_early_suspend_init(void)
+{
+ dpll_rel_cfg_bak = sci_glb_read(REG_PMU_APB_DPLL_REL_CFG, -1);
+ max_clk = get_spl_emc_clk_set();
+ chip_id = __raw_readl(REG_AON_APB_CHIP_ID);
+ __emc_timing_reg_init();
+#ifndef CONFIG_SCX35_DMC_FREQ_AP
+ cp_init();
+#else
+ int ret;
+ emc_dfs_code_copy((u8 *)SPRD_IRAM0H_BASE);
+ ret = ioremap_page_range(SPRD_IRAM0H_PHYS, SPRD_IRAM0H_PHYS+SZ_4K, SPRD_IRAM0H_PHYS, PAGE_KERNEL_EXEC);
+ if(ret){
+ printk("ioremap_page_range err %d\n", ret);
+ BUG();
+ }
+#endif
+ /*
+ * if DFS is not configurated, we keep ddr 200MHz when screen off
+ */
+#ifndef CONFIG_SPRD_SCX35_DMC_FREQ
+ register_pm_notifier(&dmcfreq_pm_notifier);
+ //register_early_suspend(&emc_early_suspend_desc);
+#endif
+ emc_debugfs_creat();
+#ifdef EMC_FREQ_AUTO_TEST
+ __emc_freq_test();
+#endif
+
+ return 0;
+}
+static void __exit emc_early_suspend_exit(void)
+{
+#ifndef CONFIG_SPRD_SCX35_DMC_FREQ
+ unregister_pm_notifier(&dmcfreq_pm_notifier);
+ //unregister_early_suspend(&emc_early_suspend_desc);
+#endif
+}
+
+module_init(emc_early_suspend_init);
+module_exit(emc_early_suspend_exit);
diff --git a/drivers/platform/sprd/dmc_freq_28nm.c b/drivers/platform/sprd/dmc_freq_28nm.c
new file mode 100644
index 00000000..3b26dc24
--- /dev/null
+++ b/drivers/platform/sprd/dmc_freq_28nm.c
@@ -0,0 +1,657 @@
+
+#ifdef CONFIG_ARCH_SCX30G
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/kobject.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <soc/sprd/common.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/sci.h>
+#include <linux/earlysuspend.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <linux/random.h>
+#include <asm/hardware/cache-l2x0.h>
+#include <asm/cacheflush.h>
+#include <asm/suspend.h>
+#include <linux/vmalloc.h>
+#include <linux/printk.h>
+#include <soc/sprd/__sc8830_dmc_dfs.h>
+#include "soc/sprd/chip_x30g/dram_phy_28nm.h"
+
+static volatile u32 mutex_flg = 0;
+
+/* other PLL clock srv, t-shark is used 384 for tdpll */
+#define PLAT_CLK 384
+
+#define DDR_TIMING_REG_VAL_ADDR (SPRD_IRAM0H_BASE + 0xc0c)
+#define DDR_TIMING_CALC_VAL_ADDR (SPRD_IRAM0H_BASE + 0xF80)
+//#define UMCTL_REG_BASE (0x30000000)
+//#define PUBL_REG_BASE (0x30010000)
+
+//SPRD_LPDDR2_BASE
+//SPRD_LPDDR2_PHY_BASE
+
+#define NINT(FREQ,REFIN) (FREQ/REFIN)
+#define KINT(FREQ,REFIN) ((FREQ-(FREQ/REFIN)*REFIN)*1048576/REFIN)
+
+#define REG32(x) (*((volatile u32 *)(x)))
+
+#ifdef CONFIG_SCX35_DMC_FREQ_AP
+static void emc_dfs_code_copy(u8 * dest);
+static int emc_dfs_call(unsigned long flag);
+void emc_dfs_main(unsigned long flag);
+#endif
+#ifdef EMC_FREQ_AUTO_TEST
+extern void __emc_freq_test(void);
+
+//static u32 get_sys_cnt(void)
+//{
+// return __raw_readl(SPRD_GPTIMER_BASE + 0x44);
+//}
+#endif
+static u32 dpll_clk_get(void)
+{
+ u32 clk = 0;
+ u32 nint, kint, refin, pnt = 0 ;
+ u32 reg, div_s, sdm_en, n;
+
+ reg = sci_glb_read(REG_AON_APB_DPLL_CFG1, -1);
+ kint = ((reg >> 12) & 0xFFFFF);
+ nint = (reg & 0x3F);
+ div_s = (reg >> 10) & 0x01;
+ sdm_en = (reg >> 6) & 0x01;
+
+ reg = sci_glb_read(REG_AON_APB_DPLL_CFG, -1);
+ n = reg & 0x3F;
+ if((reg & 0x03000000) == 0x00000000) {
+ refin = 2;
+ }
+ if((reg & 0x03000000) == 0x01000000) {
+ refin = 4;
+ }
+ if((reg & 0x03000000) == 0x02000000) {
+ refin = 13;
+ }
+ if((reg & 0x03000000) == 0x03000000) {
+ refin = 26;
+ }
+
+ if ((div_s != 0) && (sdm_en != 0)) {
+ if (((kint * refin ) & 0xFFFFF) >= 0x80000) {
+ pnt = 1;
+ }
+ else {
+ pnt = 0;
+ }
+ kint = ((kint * refin) >> 20) + pnt;
+ clk = nint * refin + kint;
+ }
+ else if ((div_s != 0) && (sdm_en == 0)) {
+ clk = nint * refin;
+ }
+ else/* if (div_s == 0) */ {
+ clk = refin * n;
+ }
+
+ return clk;
+}
+
+u32 emc_clk_get(void)
+{
+ u32 pll_clk;
+ u32 div;
+ u32 reg_val;
+ u32 sel;
+ u32 clk;
+ reg_val = sci_glb_read(REG_AON_CLK_EMC_CFG, -1);
+ sel = reg_val & 0x3;
+ div = (reg_val >> 8) & 0x3;
+ switch(sel) {
+ case 0:
+ pll_clk = 26;
+ break;
+ case 1:
+ pll_clk = 624;
+ break;
+ case 2:
+ pll_clk = 768;
+ break;
+ case 3:
+ pll_clk = dpll_clk_get();
+ break;
+ default:
+ break;
+ }
+
+ clk = (pll_clk / (div + 1)) >> 1;
+ return clk;
+}
+EXPORT_SYMBOL(emc_clk_get);
+
+static void dpll_clk_set(u32 clk)
+{
+ volatile u32 reg = 0;
+ u32 refin = 0;
+
+ if(dpll_clk_get() == clk) {
+ return;
+ }
+
+ /* get refin value */
+ refin = 26;
+
+ /* set kint, nint */
+ reg = sci_glb_read(REG_AON_APB_DPLL_CFG1, -1);
+ reg |= 1 << 10; /* set fractional divider */
+ reg &=~(0xfffff<<12 | 0x3f);
+ reg |= (KINT(clk, refin) & 0xfffff) << 12;
+ reg |= (NINT(clk, refin)) & 0x3f;
+
+ if (0 == (reg & 0xfffff000)) {
+ reg &= ~(1<<6); /* sdm_en disable */
+ } else {
+ reg |= 1<<6; /* sdm_en */
+ }
+
+ sci_glb_write(REG_AON_APB_DPLL_CFG1, reg, -1);
+
+ /* Set REFIN */
+ reg = sci_glb_read(REG_AON_APB_DPLL_CFG,-1);
+ reg &= ~(BITS_DPLL_REFIN(0x03));
+ if (refin == 26) {
+ reg |= BITS_DPLL_REFIN(0x03);
+ }
+ else if (refin == 13){
+ reg |= BITS_DPLL_REFIN(0x02);
+ }
+/*
+ else if (refin = 4){
+ reg |= BITS_DPLL_REFIN(0x02);
+ }
+ else if (refin = 2){
+ }
+*/
+ sci_glb_write(REG_AON_APB_DPLL_CFG, reg, -1);
+
+ udelay(100);
+}
+
+static u32 calc_tprd(u32 reg_addr, u32 new_clk, u32 cur_clk, u32 init_val)
+{
+ u32 tprd, reg;
+
+ reg = sci_glb_read(reg_addr ,-1);
+ tprd = (reg >> 8) & 0xFF;
+ tprd = tprd * cur_clk / new_clk;
+
+ reg = (init_val & (~0xFF00));
+ reg |= ((tprd & 0xFF) << 8 );
+
+ return reg;
+}
+
+static void emc_calc_phy_param(u32 new_clk)
+{
+ ddr_dfs_v2_t *init = (ddr_dfs_v2_t *)DDR_TIMING_REG_VAL_ADDR;
+ publ_calc_t *calc = (publ_calc_t *)DDR_TIMING_CALC_VAL_ADDR;
+ u32 cur_clk, i;
+ u32 cur_tprd, init_tprd;
+ u32 acd, r0wld, r1wld, wdqd, rdqsd, rdqsnd, r0dqsgd, r1dqsgd;
+
+ for (i = 0; i < 5; i++) {
+ if (init->ddr_clk == new_clk) {
+ break;
+ }
+ init++;
+ }
+ if (i == 5) {
+ return ;
+ }
+
+ cur_clk = emc_clk_get();
+ /* get register acmdlr.tprd [15:8]*/
+ calc->publ_acmdlr = calc_tprd((SPRD_LPDDR2_PHY_BASE +0x38),
+ new_clk, cur_clk, init->publ_acmdlr);
+ /* get register dx0mdlr.tprd [15:8]*/
+ calc->publ_dx0mdlr = calc_tprd((SPRD_LPDDR2_PHY_BASE +0x2C4),
+ new_clk, cur_clk, init->publ_dx0mdlr);
+ /* get register dx1mdlr.tprd [15:8]*/
+ calc->publ_dx1mdlr = calc_tprd((SPRD_LPDDR2_PHY_BASE +0x344),
+ new_clk, cur_clk, init->publ_dx1mdlr);
+ /* get register dx2mdlr.tprd [15:8]*/
+ calc->publ_dx2mdlr = calc_tprd((SPRD_LPDDR2_PHY_BASE +0x3C4),
+ new_clk, cur_clk, init->publ_dx2mdlr);
+ /* get register dx3mdlr.tprd [15:8]*/
+ calc->publ_dx3mdlr = calc_tprd((SPRD_LPDDR2_PHY_BASE +0x444),
+ new_clk, cur_clk, init->publ_dx3mdlr);
+
+ /* get alcdlr.acd [7:0] */
+ cur_tprd = (calc->publ_acmdlr >> 8 ) & 0xFF;
+ init_tprd = (init->publ_acmdlr >> 8) & 0xFF;
+ if (init_tprd == 0) {
+ printk("[emc_calc_phy_param] publ_acmdlr.init_tprd is 0x%d, error!\n\r", init_tprd);
+ return;
+ }
+ acd = (init->publ_aclcdlr & 0xFF) * cur_tprd / init_tprd;
+ calc->publ_aclcdlr = (init->publ_aclcdlr & (~0xFF)) | (acd & 0xFF);
+
+ /* DX0LCDLR0~3 */
+ cur_tprd = (calc->publ_dx0mdlr >> 8 ) & 0xFF;
+ init_tprd = (init->publ_dx0mdlr >> 8) & 0xFF;
+ if (init_tprd == 0) {
+ printk("[emc_calc_phy_param] publ_dx0mdlr.init_tprd is 0x%d, error!\n\r", init_tprd);
+ return;
+ }
+ r0wld = ((init->publ_dx0lcdlr0 & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ r1wld = (((init->publ_dx0lcdlr0 >> 8) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ calc->publ_dx0lcdlr0 = (init->publ_dx0lcdlr0 & (~0xFFFF)) | (r1wld << 8) | r0wld;
+
+ wdqd = ((init->publ_dx0lcdlr1 & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ rdqsd = (((init->publ_dx0lcdlr1 >> 8) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ rdqsnd = (((init->publ_dx0lcdlr1 >> 16) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ calc->publ_dx0lcdlr1 = (rdqsnd << 16) | (rdqsd << 8) | wdqd;
+
+ r0dqsgd = ((init->publ_dx0lcdlr2 & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ r1dqsgd = (((init->publ_dx0lcdlr2 >> 8) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ calc->publ_dx0lcdlr2 = (init->publ_dx0lcdlr2 & (~0xFFFF)) | (r1dqsgd << 8) | r0dqsgd;
+
+ /* DX1LCDLR0~3 */
+ cur_tprd = (calc->publ_dx1mdlr >> 8 ) & 0xFF;
+ init_tprd = (init->publ_dx1mdlr >> 8) & 0xFF;
+ if (init_tprd == 0) {
+ printk("[emc_calc_phy_param] publ_dx1mdlr.init_tprd is 0x%d, error!\n\r", init_tprd);
+ return;
+ }
+ r0wld = ((init->publ_dx1lcdlr0 & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ r1wld = (((init->publ_dx1lcdlr0 >> 8) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ calc->publ_dx1lcdlr0 = (init->publ_dx1lcdlr0 & (~0xFFFF)) | (r1wld << 8) | r0wld;
+
+ wdqd = ((init->publ_dx1lcdlr1 & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ rdqsd = (((init->publ_dx1lcdlr1 >> 8) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ rdqsnd = (((init->publ_dx1lcdlr1 >> 16) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ calc->publ_dx1lcdlr1 = (rdqsnd << 16) | (rdqsd << 8) | wdqd;
+
+ r0dqsgd = ((init->publ_dx1lcdlr2 & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ r1dqsgd = (((init->publ_dx1lcdlr2 >> 8) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ calc->publ_dx1lcdlr2 = (init->publ_dx1lcdlr2 & (~0xFFFF)) | (r1dqsgd << 8) | r0dqsgd;
+
+ /* DX2LCDLR0~3 */
+ cur_tprd = (calc->publ_dx2mdlr >> 8 ) & 0xFF;
+ init_tprd = (init->publ_dx2mdlr >> 8) & 0xFF;
+ if (init_tprd == 0) {
+ printk("[emc_calc_phy_param] publ_dx2mdlr.init_tprd is 0x%d, error!\n\r", init_tprd);
+ return;
+ }
+ r0wld = ((init->publ_dx2lcdlr0 & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ r1wld = (((init->publ_dx2lcdlr0 >> 8) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ calc->publ_dx2lcdlr0 = (init->publ_dx2lcdlr0 & (~0xFFFF)) | (r1wld << 8) | r0wld;
+
+ wdqd = ((init->publ_dx2lcdlr1 & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ rdqsd = (((init->publ_dx2lcdlr1 >> 8) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ rdqsnd = (((init->publ_dx2lcdlr1 >> 16) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ calc->publ_dx2lcdlr1 = (rdqsnd << 16) | (rdqsd << 8) | wdqd;
+
+ r0dqsgd = ((init->publ_dx2lcdlr2 & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ r1dqsgd = (((init->publ_dx2lcdlr2 >> 8) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ calc->publ_dx2lcdlr2 = (init->publ_dx2lcdlr2 & (~0xFFFF)) | (r1dqsgd << 8) | r0dqsgd;
+
+ /* DX3LCDLR0~3 */
+ cur_tprd = (calc->publ_dx3mdlr >> 8 ) & 0xFF;
+ init_tprd = (init->publ_dx3mdlr >> 8) & 0xFF;
+ if (init_tprd == 0) {
+ printk("[emc_calc_phy_param] publ_dx3mdlr.init_tprd is 0x%d, error!\n\r", init_tprd);
+ return;
+ }
+ r0wld = ((init->publ_dx3lcdlr0 & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ r1wld = (((init->publ_dx3lcdlr0 >> 8) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ calc->publ_dx3lcdlr0 = (init->publ_dx3lcdlr0 & (~0xFFFF)) | (r1wld << 8) | r0wld;
+
+ wdqd = ((init->publ_dx3lcdlr1 & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ rdqsd = (((init->publ_dx3lcdlr1 >> 8) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ rdqsnd = (((init->publ_dx3lcdlr1 >> 16) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ calc->publ_dx3lcdlr1 = (rdqsnd << 16) | (rdqsd << 8) | wdqd;
+
+ r0dqsgd = ((init->publ_dx3lcdlr2 & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ r1dqsgd = (((init->publ_dx3lcdlr2 >> 8) & 0xFF) * cur_tprd / init_tprd) & 0xFF;
+ calc->publ_dx3lcdlr2 = (init->publ_dx3lcdlr2 & (~0xFFFF)) | (r1dqsgd << 8) | r0dqsgd;
+
+ /* write new_clk in struct */
+ calc->ddr_clk = new_clk;
+}
+
+static u32 __emc_clk_set(u32 clk)
+{
+ u32 flag = 0;
+
+ /* check timing params */
+ flag = (clk << EMC_CLK_FREQ_OFFSET);
+
+ emc_calc_phy_param(clk);
+
+#ifdef CONFIG_SCX35_DMC_FREQ_AP
+ flush_cache_all();
+ cpu_suspend(flag, emc_dfs_call);
+#endif
+ if(emc_clk_get() != clk) {
+ printk("[__emc_clk_set] error, set clk = %d, get clk = %d\n\r", clk, emc_clk_get());
+ }
+ return 0;
+}
+
+static u32 get_dpll_from_clk (u32 new_clk)
+{
+ u32 dpll = 0;
+
+ switch (new_clk){
+ case 200:
+ case 400:
+ dpll = 800;
+ break;
+ case 384:
+ /* this clock from tdpll */
+ break;
+ case 466:
+ dpll = 932;
+ break;
+ case 533:
+ dpll = 1066;
+ break;
+ default :
+ break;
+ }
+
+ return dpll;
+}
+/* How To look PLL Setting
+ reg name val bit
+ MPLL_CFG reFin [25:24]
+ MPLL_CFG N [5:0]
+
+ MPLL_CFG1 div_s [10]
+ MPLL_CFG1 sdm_en [6]
+ MPLL_CFG1 Nint [5:0]
+ MPLL_CFG1 Kint [31:12]
+ div_s = 1 sdm_en = 1 Fout = Fin * ( Nint + Kint/1048576)
+ div_s = 1 sdm_en = 0 Fout = Fin * Nint
+ div_s = 0 sdm_en = x Fout = Fin * N */
+/* reg[0x402D0024] :*/
+/* [9:8] : clk_emc_div(clk_div= clk_src/(div+1)) */
+/* [1:0] : clk_emc_sel (0:pub 26m, 1: CPLL, 2:TDPLL, 3:DPLL)*/
+static u32 check_need_plat_clk(u32 new_clk)
+{
+ u32 sel, cur_dpll, nd_dpll = 0;
+
+ if (PLAT_CLK == new_clk) {
+ return 0;
+ }
+
+ sel = sci_glb_read(REG_AON_CLK_EMC_CFG, -1);
+ sel &= 0x3;
+ /* if current not used dpll return 0 don't need platform clock */
+ if (sel != 0x03) {
+ return 0;
+ }
+
+ /* get dpll clock to check */
+ cur_dpll = dpll_clk_get();
+ nd_dpll = get_dpll_from_clk(new_clk);
+ if (nd_dpll == 0){
+ /* must be not go to this, if running, new_clk error! */
+ return 1;
+ }
+
+ if (cur_dpll == nd_dpll) {
+ return 0;
+ }
+
+ return 1;
+}
+
+u32 emc_clk_set(u32 new_clk, u32 sene)
+{
+ u32 old_clk, nd_dpll;
+#if defined (EMC_FREQ_AUTO_TEST) || defined (CONFIG_SCX35_DMC_FREQ_AP)
+ unsigned long irq_flags = 0;
+#endif
+
+/* used to get dfs hold times for debug */
+#if 0
+ u32 start_t1, end_t1;
+ static u32 max_u_time = 0;
+ u32 current_u_time;
+
+ start_t1 = get_sys_cnt();
+#endif
+
+ /* check new_clk */
+ if ((new_clk != 200) && (new_clk != 384) && (new_clk != 400)
+ && (new_clk != 466) && (new_clk != 533)) {
+ printk("[emc_clk_set] : new_clk[%d] error!\n\r", new_clk);
+ return 0;
+ }
+
+ /* make sure no other thread running this code */
+ if(mutex_flg >= 1) {
+ panic("now other thread set dmc clk\n");
+ goto out;
+ }
+ mutex_flg ++;
+
+ /* step 1: save irq and lock it */
+#if defined (EMC_FREQ_AUTO_TEST) || defined (CONFIG_SCX35_DMC_FREQ_AP)
+ local_irq_save(irq_flags);
+#endif
+
+ /* check current clock */
+ old_clk = emc_clk_get();
+ if(old_clk == new_clk) {
+ goto out;
+ }
+
+ /* step 2: check new clock need alter dpll( paltform clock ) or not */
+ if (check_need_plat_clk(new_clk)) {
+ __emc_clk_set(PLAT_CLK);
+
+ /* set dpll to need */
+ nd_dpll = get_dpll_from_clk(new_clk);
+ if (nd_dpll == 0){
+ /* must be not go to this, if running, new_clk error! */
+ goto out;
+ }
+ dpll_clk_set(nd_dpll);
+ }
+
+#ifdef CONFIG_SCX35_DMC_FREQ_AP
+ __emc_clk_set(new_clk);
+#endif
+
+out:
+#if defined (EMC_FREQ_AUTO_TEST) || defined(CONFIG_SCX35_DMC_FREQ_AP)
+ local_irq_restore(irq_flags);
+#endif
+ mutex_flg --;
+#if 0
+ end_t1 = get_sys_cnt();
+
+ current_u_time = (start_t1 - end_t1)/128;
+ if(max_u_time < current_u_time) {
+ max_u_time = current_u_time;
+ }
+ printk("emc dfs use current = %08u max %08u\n", current_u_time, max_u_time);
+#endif
+ return 0;
+}
+EXPORT_SYMBOL(emc_clk_set);
+
+#ifdef CONFIG_SCX35_DMC_FREQ_AP
+static int emc_dfs_call(unsigned long flag)
+{
+ cpu_switch_mm(init_mm.pgd, &init_mm);
+ ((int (*)(unsigned long))SPRD_IRAM0H_PHYS)(flag); //iram0h must be the first function of dfs
+ return 0;
+}
+static void emc_dfs_code_copy(u8 * dest)
+{
+ memcpy_toio((void *)dest, (void *)emc_dfs_main, 0xc00);
+}
+#endif
+
+
+static int __init emc_early_suspend_init(void)
+{
+#ifdef CONFIG_SCX35_DMC_FREQ_AP
+ int ret;
+ emc_dfs_code_copy((u8 *)SPRD_IRAM0H_BASE);
+ ret = ioremap_page_range(SPRD_IRAM0H_PHYS,
+ SPRD_IRAM0H_PHYS+SZ_4K, SPRD_IRAM0H_PHYS, PAGE_KERNEL_EXEC);
+ if (ret) {
+ printk("ioremap_page_range err %d\n", ret);
+ BUG();
+ }
+#endif
+
+#ifdef EMC_FREQ_AUTO_TEST
+ __emc_freq_test();
+#endif
+
+ return 0;
+}
+
+static void __exit emc_early_suspend_exit(void)
+{
+}
+
+#ifndef CONFIG_ARCH_SCX20
+module_init(emc_early_suspend_init);
+module_exit(emc_early_suspend_exit);
+#endif
+
+#endif
+
+#ifdef CONFIG_ARCH_SCX35L
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/kobject.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <soc/sprd/common.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/sci.h>
+#include <linux/earlysuspend.h>
+#include <soc/sprd/sci_glb_regs.h>
+
+#define DDR_TIMING_REG_VAL_ADDR (SPRD_IRAM0H_BASE + 0xc00)
+#define DDR_TIMING_CALC_VAL_ADDR (SPRD_IRAM0H_BASE + 0xF80)
+
+static u32 dpll_clk_get(void)
+{
+ u32 clk = 0;
+ u32 nint, kint, refin, pnt = 0 ;
+ u32 reg1, reg2, div_s, sdm_en, n;
+
+ reg1 = sci_glb_read(REG_AON_APB_DPLL_CFG1, -1);
+ reg2 = sci_glb_read(REG_AON_APB_DPLL_CFG2, -1);
+ kint = reg2 & 0xFFFFF;
+ nint = (reg2>>24) & 0x3F;
+ div_s = (reg1 >> 26) & 0x01;
+ sdm_en = (reg1 >> 24) & 0x01;
+ n = reg1 & 0x7ff;
+
+ switch((reg1>>18)&0x3)
+ {
+ case 0:
+ refin = 2;
+ break;
+ case 1:
+ refin = 4;
+ break;
+ case 2:
+ refin = 13;
+ break;
+ case 3:
+ refin = 26;
+ break;
+ }
+
+
+ if ((div_s != 0) && (sdm_en != 0)) {
+ if (((kint * refin ) & 0xFFFFF) >= 0x80000) {
+ pnt = 1;
+ }
+ else {
+ pnt = 0;
+ }
+ kint = ((kint * refin) >> 20) + pnt;
+ clk = nint * refin + kint;
+ }
+ else if ((div_s != 0) && (sdm_en == 0)) {
+ clk = nint * refin;
+ }
+ else/* if (div_s == 0) */ {
+ clk = refin * n;
+ }
+
+ return clk;
+}
+
+u32 emc_clk_get(void)
+{
+ u32 pll_clk;
+ u32 div;
+ u32 reg_val;
+ u32 sel;
+ u32 clk;
+ reg_val = sci_glb_read(REG_AON_CLK_EMC_CFG, -1);
+ sel = reg_val & 0x7;
+ div = (reg_val >> 8) & 0x7;
+ switch(sel) {
+ case 0:
+ pll_clk = 26;
+ break;
+ case 1:
+ pll_clk = 192;
+ break;
+ case 2:
+ pll_clk = 307;
+ break;
+ case 3:
+ pll_clk = 384;
+ break;
+ case 4:
+ pll_clk = 512;
+ break;
+ case 5:
+ pll_clk = 614;
+ break;
+ case 6:
+ pll_clk = 768;
+ break;
+ case 7:
+ pll_clk = dpll_clk_get();
+ break;
+ default:
+ break;
+ }
+
+ clk = (pll_clk / (div + 1)) >> 1;
+ return clk;
+}
+EXPORT_SYMBOL(emc_clk_get);
+#endif
diff --git a/drivers/platform/sprd/dmc_freq_ap.c b/drivers/platform/sprd/dmc_freq_ap.c
new file mode 100644
index 00000000..ac8aff36
--- /dev/null
+++ b/drivers/platform/sprd/dmc_freq_ap.c
@@ -0,0 +1,838 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/input/matrix_keypad.h>
+#include <mach/hardware.h>
+#include <mach/__sc8830_dmc_dfs.h>
+#define REG32(x) (*((volatile uint32 *)(x)))
+#define REG16(x) (*((volatile uint16 *)(x)))
+#define UMCTL2_REG_GET(reg_addr) (*((volatile uint32 *)(reg_addr)))
+#define UMCTL2_REG_SET( reg_addr, value ) *(volatile uint32 *)(reg_addr) = value
+#define UMCTL_REG_BASE (0x30000000)
+#define PUBL_REG_BASE (0x30010000)
+
+#define UMCTL2_REG_(x) (UMCTL_REG_BASE+(x))
+
+#define SPRD_UART1_PHYS (0x70100000)
+/*
+ *uMCTL2 DDRC registers
+*/
+#define UMCTL_MSTR UMCTL2_REG_(0x0000) /*master*/
+#define UMCTL_STAT UMCTL2_REG_(0x0004) /*oeration mode status*/
+#define UMCTL_MRCTRL0 UMCTL2_REG_(0x0010)/*mode register read/werite control*/
+#define UMCTL_MRCTRL1 UMCTL2_REG_(0x0014)
+#define UMCTL_MRSTAT UMCTL2_REG_(0x0018)/*mode register read/weite status*/
+
+#define UMCTL_DERATEEN UMCTL2_REG_(0x0020)/*temperature derate enable*/
+#define UMCTL_DERATEINT UMCTL2_REG_(0x0024)/*temperature derate interval*/
+#define UMCTL_PWRCTL UMCTL2_REG_(0x0030)/*low power control*/
+#define UMCTL_PWRTMG UMCTL2_REG_(0x0034)/*low power timing*/
+
+#define UMCTL_RFSHCTL0 UMCTL2_REG_(0x0050)/*refresh control0*/
+#define UMCTL_RFSHCTL1 UMCTL2_REG_(0x0054)
+#define UMCTL_RFSHCTL2 UMCTL2_REG_(0x0058)
+#define UMCTL_RFSHCTL3 UMCTL2_REG_(0x0060)
+#define UMCTL_RFSHTMG UMCTL2_REG_(0x0064)/*refresh Timing*/
+#define UMCTL_ECCCFG0 UMCTL2_REG_(0x0070)/*ecc configuration*/
+#define UMCTL_ECCCFG1 UMCTL2_REG_(0x0074)
+#define UMCTL_ECCSTAT UMCTL2_REG_(0x0078)/*ecc status*/
+#define UMCTL_ECCCLR UMCTL2_REG_(0x007C)/*ecc clear*/
+#define UMCTL_ECCERRCNT UMCTL2_REG_(0x0080)/*ecc error counter*/
+
+#define UMCTL_ECCADDR0 UMCTL2_REG_(0x0084)/*ecc corrected error address reg0*/
+#define UMCTL_ECCADDR1 UMCTL2_REG_(0x0088)
+#define UMCTL_ECCSYN0 UMCTL2_REG_(0x008C)/*ecc corected syndrome reg0*/
+#define UMCTL_ECCSYN1 UMCTL2_REG_(0x0090)
+#define UMCTL_ECCSYN2 UMCTL2_REG_(0x0094)
+#define UMCTL_ECCBITMASK0 UMCTL2_REG_(0x0098)
+#define UMCTL_ECCBITMASK1 UMCTL2_REG_(0x009C)
+#define UMCTL_ECCBITMASK2 UMCTL2_REG_(0x00A0)
+#define UMCTL_ECCUADDR0 UMCTL2_REG_(0x00A4)/*ecc uncorrected error address reg0*/
+#define UMCTL_ECCUADDR1 UMCTL2_REG_(0x00A8)
+#define UMCTL_ECCUSYN0 UMCTL2_REG_(0x00AC)/*ecc UNcorected syndrome reg0*/
+#define UMCTL_ECCUSYN1 UMCTL2_REG_(0x00B0)
+#define UMCTL_ECCUSYN2 UMCTL2_REG_(0x00B4)
+#define UMCTL_ECCPOISONADDR0 UMCTL2_REG_(0x00B8)/*ecc data poisoning address reg0*/
+#define UMCTL_ECCPOISONADDR1 UMCTL2_REG_(0x00BC)
+
+
+#define UMCTL_PARCTL UMCTL2_REG_(0x00C0)/*parity control register*/
+#define UMCTL_PARSTAT UMCTL2_REG_(0x00C4)/*parity status register*/
+
+#define UMCTL_INIT0 UMCTL2_REG_(0x00D0)/*SDRAM initialization register0*/
+#define UMCTL_INIT1 UMCTL2_REG_(0x00D4)
+#define UMCTL_INIT2 UMCTL2_REG_(0x00D8)
+#define UMCTL_INIT3 UMCTL2_REG_(0x00DC)
+#define UMCTL_INIT4 UMCTL2_REG_(0x00E0)
+#define UMCTL_INIT5 UMCTL2_REG_(0x00E4)
+
+
+#define UMCTL_DIMMCTL UMCTL2_REG_(0x00F0)/*DIMM control register*/
+#define UMCTL_RANKCTL UMCTL2_REG_(0x00F4)
+
+#define UMCTL_DRAMTMG0 UMCTL2_REG_(0x0100)/*SDRAM timing register0*/
+#define UMCTL_DRAMTMG1 UMCTL2_REG_(0x0104)
+#define UMCTL_DRAMTMG2 UMCTL2_REG_(0x0108)
+#define UMCTL_DRAMTMG3 UMCTL2_REG_(0x010C)
+#define UMCTL_DRAMTMG4 UMCTL2_REG_(0x0110)
+#define UMCTL_DRAMTMG5 UMCTL2_REG_(0x0114)
+#define UMCTL_DRAMTMG6 UMCTL2_REG_(0x0118)
+#define UMCTL_DRAMTMG7 UMCTL2_REG_(0x011C)
+#define UMCTL_DRAMTMG8 UMCTL2_REG_(0x0120)
+
+#define UMCTL_ZQCTL0 UMCTL2_REG_(0x0180)/*ZQ control register0*/
+#define UMCTL_ZQCTL1 UMCTL2_REG_(0x0184)
+#define UMCTL_ZQCTL2 UMCTL2_REG_(0x0188)
+#define UMCTL_ZQSTAT UMCTL2_REG_(0x018C)
+
+
+#define UMCTL_DFITMG0 UMCTL2_REG_(0x0190)/*DFI timing register0*/
+#define UMCTL_DFITMG1 UMCTL2_REG_(0x0194)
+
+#define UMCTL_DFILPCFG0 UMCTL2_REG_(0x0198)/*DFI low power configuration*/
+#define UMCTL_DFIUPD0 UMCTL2_REG_(0x01A0)/*DFI update register0*/
+#define UMCTL_DFIUPD1 UMCTL2_REG_(0x01A4)
+#define UMCTL_DFIUPD2 UMCTL2_REG_(0x01A8)
+#define UMCTL_DFIUPD3 UMCTL2_REG_(0x01AC)
+
+#define UMCTL_DFIMISC UMCTL2_REG_(0x01B0)
+
+#define UMCTL_TRAINCTL0 UMCTL2_REG_(0x01D0)/*PHY eval training control reg0*/
+#define UMCTL_TRAINCTL1 UMCTL2_REG_(0x01D4)
+#define UMCTL_TRAINCTL2 UMCTL2_REG_(0x01D8)
+#define UMCTL_TRAINSTAT UMCTL2_REG_(0x01DC)
+
+#define UMCTL_ADDRMAP0 UMCTL2_REG_(0x0200)/*address map register0*/
+#define UMCTL_ADDRMAP1 UMCTL2_REG_(0x0204)
+#define UMCTL_ADDRMAP2 UMCTL2_REG_(0x0208)
+#define UMCTL_ADDRMAP3 UMCTL2_REG_(0x020C)
+#define UMCTL_ADDRMAP4 UMCTL2_REG_(0x0210)
+#define UMCTL_ADDRMAP5 UMCTL2_REG_(0x0214)
+#define UMCTL_ADDRMAP6 UMCTL2_REG_(0x0218)
+
+#define UMCTL_ODTCFG UMCTL2_REG_(0x0240)/*ODT configuration register*/
+#define UMCTL_ODTMAP UMCTL2_REG_(0x0244)
+
+#define UMCTL_SCHED UMCTL2_REG_(0x0250)/*scheduler control register*/
+#define UMCTL_PERFHPR0 UMCTL2_REG_(0x0258)/*high priority read CAM reg0*/
+#define UMCTL_PERFHPR1 UMCTL2_REG_(0x025C)
+#define UMCTL_PERFLPR0 UMCTL2_REG_(0x0260)
+#define UMCTL_PERFLPR1 UMCTL2_REG_(0x0264)
+
+#define UMCTL_PERFWR0 UMCTL2_REG_(0x0268)/*write CAM reg0*/
+#define UMCTL_PERFWR1 UMCTL2_REG_(0x026C)
+#define UMCTL_DBG0 UMCTL2_REG_(0x0300)/*debug register0*/
+#define UMCTL_DBG1 UMCTL2_REG_(0x0304)
+#define UMCTL_DBGCAM UMCTL2_REG_(0x0308)/*cam debug register*/
+
+
+/*
+ *uMCTL2 Multi-Port registers
+*/
+#define UMCTL_PCCFG UMCTL2_REG_(0x0400)/*port common configuration*/
+
+#define UMCTL_PCFGR_0 UMCTL2_REG_(0x0404+(0x00)*0xB0)/*Port n configuration read reg*/
+#define UMCTL_PCFGR_1 UMCTL2_REG_(0x0404+(0x01)*0xB0)
+#define UMCTL_PCFGR_2 UMCTL2_REG_(0x0404+(0x02)*0xB0)
+#define UMCTL_PCFGR_3 UMCTL2_REG_(0x0404+(0x03)*0xB0)
+#define UMCTL_PCFGR_4 UMCTL2_REG_(0x0404+(0x04)*0xB0)
+#define UMCTL_PCFGR_5 UMCTL2_REG_(0x0404+(0x05)*0xB0)
+#define UMCTL_PCFGR_6 UMCTL2_REG_(0x0404+(0x06)*0xB0)
+#define UMCTL_PCFGR_7 UMCTL2_REG_(0x0404+(0x07)*0xB0)
+#define UMCTL_PCFGR_8 UMCTL2_REG_(0x0404+(0x08)*0xB0)
+#define UMCTL_PCFGR_9 UMCTL2_REG_(0x0404+(0x09)*0xB0)
+#define UMCTL_PCFGR_10 UMCTL2_REG_(0x0404+(0x0A)*0xB0)
+#define UMCTL_PCFGR_11 UMCTL2_REG_(0x0404+(0x0B)*0xB0)
+#define UMCTL_PCFGR_12 UMCTL2_REG_(0x0404+(0x0C)*0xB0)
+#define UMCTL_PCFGR_13 UMCTL2_REG_(0x0404+(0x0D)*0xB0)
+#define UMCTL_PCFGR_14 UMCTL2_REG_(0x0404+(0x0E)*0xB0)
+#define UMCTL_PCFGR_15 UMCTL2_REG_(0x0404+(0x0F)*0xB0)
+
+
+#define UMCTL_PCFGW_0 UMCTL2_REG_(0x0408+(0x00)*0xB0)/*Port n configuration write reg*/
+#define UMCTL_PCFGW_1 UMCTL2_REG_(0x0408+(0x01)*0xB0)
+#define UMCTL_PCFGW_2 UMCTL2_REG_(0x0408+(0x02)*0xB0)
+#define UMCTL_PCFGW_3 UMCTL2_REG_(0x0408+(0x03)*0xB0)
+#define UMCTL_PCFGW_4 UMCTL2_REG_(0x0408+(0x04)*0xB0)
+#define UMCTL_PCFGW_5 UMCTL2_REG_(0x0408+(0x05)*0xB0)
+#define UMCTL_PCFGW_6 UMCTL2_REG_(0x0408+(0x06)*0xB0)
+#define UMCTL_PCFGW_7 UMCTL2_REG_(0x0408+(0x07)*0xB0)
+#define UMCTL_PCFGW_8 UMCTL2_REG_(0x0408+(0x08)*0xB0)
+#define UMCTL_PCFGW_9 UMCTL2_REG_(0x0408+(0x09)*0xB0)
+#define UMCTL_PCFGW_10 UMCTL2_REG_(0x0408+(0x0A)*0xB0)
+#define UMCTL_PCFGW_11 UMCTL2_REG_(0x0408+(0x0B)*0xB0)
+#define UMCTL_PCFGW_12 UMCTL2_REG_(0x0408+(0x0C)*0xB0)
+#define UMCTL_PCFGW_13 UMCTL2_REG_(0x0408+(0x0D)*0xB0)
+#define UMCTL_PCFGW_14 UMCTL2_REG_(0x0408+(0x0E)*0xB0)
+#define UMCTL_PCFGW_15 UMCTL2_REG_(0x0408+(0x0F)*0xB0)
+
+
+#define UMCTL_PORT_EN_0 0xFFFF0000//UMCTL2_REG_(0x0490+(0x00)*0xB0)/*Port n enable reg*/
+#define UMCTL_PORT_EN_1 0xFFFF0000//UMCTL2_REG_(0x0490+(0x01)*0xB0)/*Port n enable reg*/
+#define UMCTL_PORT_EN_2 0xFFFF0000//UMCTL2_REG_(0x0490+(0x02)*0xB0)/*Port n enable reg*/
+#define UMCTL_PORT_EN_3 0xFFFF0000//UMCTL2_REG_(0x0490+(0x03)*0xB0)/*Port n enable reg*/
+#define UMCTL_PORT_EN_4 0xFFFF0000//UMCTL2_REG_(0x0490+(0x04)*0xB0)/*Port n enable reg*/
+#define UMCTL_PORT_EN_5 0xFFFF0000//UMCTL2_REG_(0x0490+(0x05)*0xB0)/*Port n enable reg*/
+#define UMCTL_PORT_EN_6 0xFFFF0000//UMCTL2_REG_(0x0490+(0x06)*0xB0)/*Port n enable reg*/
+#define UMCTL_PORT_EN_7 0xFFFF0000//UMCTL2_REG_(0x0490+(0x07)*0xB0)/*Port n enable reg*/
+#define UMCTL_PORT_EN_8 0xFFFF0000//UMCTL2_REG_(0x0490+(0x08)*0xB0)/*Port n enable reg*/
+#define UMCTL_PORT_EN_9 0xFFFF0000//UMCTL2_REG_(0x0490+(0x09)*0xB0)/*Port n enable reg*/
+
+
+/*
+#define UMCTL2_PCFGIDMASKCH_m_N
+#define UMCTL2_PCFGIDVALUECH_m_N
+*/
+
+
+/*
+ *Refer to PUBL databook 1.44a for detail,Chapter3.3 Registers.
+*/
+#define PUBL_RIDR (PUBL_REG_BASE+0x00*4) // R - Revision Identification Register
+#define PUBL_PIR (PUBL_REG_BASE+0x01*4) // R/W - PHY Initialization Register
+#define PUBL_PGCR (PUBL_REG_BASE+0x02*4) // R/W - PHY General Configuration Register
+#define PUBL_PGSR (PUBL_REG_BASE+0x03*4) // R - PHY General Status Register
+#define PUBL_DLLGCR (PUBL_REG_BASE+0x04*4) // R/W - DLL General Control Register
+#define PUBL_ACDLLCR (PUBL_REG_BASE+0x05*4) // R/W - AC DLL Control Register
+#define PUBL_PTR0 (PUBL_REG_BASE+0x06*4) // R/W - PHY Timing Register 0
+#define PUBL_PTR1 (PUBL_REG_BASE+0x07*4) // R/W - PHY Timing Register 1
+#define PUBL_PTR2 (PUBL_REG_BASE+0x08*4) // R/W - PHY Timing Register 2
+#define PUBL_ACIOCR (PUBL_REG_BASE+0x09*4) // R/W - AC I/O Configuration Register
+#define PUBL_DXCCR (PUBL_REG_BASE+0x0A*4) // R/W - DATX8 I/O Configuration Register
+#define PUBL_DSGCR (PUBL_REG_BASE+0x0B*4) // R/W - DFI Configuration Register
+#define PUBL_DCR (PUBL_REG_BASE+0x0C*4) // R/W - DRAM Configuration Register
+#define PUBL_DTPR0 (PUBL_REG_BASE+0x0D*4) // R/W - SDRAM Timing Parameters Register 0
+#define PUBL_DTPR1 (PUBL_REG_BASE+0x0E*4) // R/W - SDRAM Timing Parameters Register 1
+#define PUBL_DTPR2 (PUBL_REG_BASE+0x0F*4) // R/W - SDRAM Timing Parameters Register 2
+#define PUBL_MR0 (PUBL_REG_BASE+0x10*4) // R/W - Mode Register
+#define PUBL_MR1 (PUBL_REG_BASE+0x11*4) // R/W - Ext}ed Mode Register
+#define PUBL_MR2 (PUBL_REG_BASE+0x12*4) // R/W - Ext}ed Mode Register 2
+#define PUBL_MR3 (PUBL_REG_BASE+0x13*4) // R/W - Ext}ed Mode Register 3
+#define PUBL_ODTCR (PUBL_REG_BASE+0x14*4) // R/W - ODT Configuration Register
+#define PUBL_DTAR (PUBL_REG_BASE+0x15*4) // R/W - Data Training Address Register
+#define PUBL_DTDR0 (PUBL_REG_BASE+0x16*4) // R/W - Data Training Data Register 0
+#define PUBL_DTDR1 (PUBL_REG_BASE+0x17*4) // R/W - Data Training Data Register 1
+#define PUBL_DCUAR (PUBL_REG_BASE+0X30*4) // R/W - DCU Address Resiter
+#define PUBL_DCUDR (PUBL_REG_BASE+0x31*4) // R/W - DCU Data Register
+#define PUBL_DCURR (PUBL_REG_BASE+0x32*4) // R/W - DCU Run Register
+#define PUBL_DCULR (PUBL_REG_BASE+0x33*4) // R/W - DCU Loop Register
+#define PUBL_DCUGCR (PUBL_REG_BASE+0x34*4) // R/W - DCU General Configuration Register
+#define PUBL_DCUTPR (PUBL_REG_BASE+0x35*4) // R/W - DCU Timing Parameters Registers
+#define PUBL_DCUSR0 (PUBL_REG_BASE+0x36*4) // R - DCU Status Register 0
+#define PUBL_DCUSR1 (PUBL_REG_BASE+0x37*4) // R - DCU Status Register 1
+#define PUBL_BISTRR (PUBL_REG_BASE+0x40*4) // R/W - BIST Run Register
+#define PUBL_BISTMSKR0 (PUBL_REG_BASE+0x41*4) // R/W - BIST Mask Register 0
+#define PUBL_BISTMSKR1 (PUBL_REG_BASE+0x42*4) // R/W - BIST Mask Register 1
+#define PUBL_BISTWCR (PUBL_REG_BASE+0x43*4) // R/W - BIST Word Count Register
+#define PUBL_BISTLSR (PUBL_REG_BASE+0x44*4) // R/W - BIST LFSR Seed Register
+#define PUBL_BISTAR0 (PUBL_REG_BASE+0x45*4) // R/W - BIST Address Register 0
+#define PUBL_BISTAR1 (PUBL_REG_BASE+0x46*4) // R/W - BIST Address Register 1
+#define PUBL_BISTAR2 (PUBL_REG_BASE+0x47*4) // R/W - BIST Address Register 2
+#define PUBL_BISTUDPR (PUBL_REG_BASE+0x48*4) // R/W - BIST User Data Pattern Register
+#define PUBL_BISTGSR (PUBL_REG_BASE+0x49*4) // R - BIST General Status Register
+#define PUBL_BISTWER (PUBL_REG_BASE+0x4A*4) // R - BIST Word Error Register
+#define PUBL_BISTBER0 (PUBL_REG_BASE+0x4B*4) // R - BIST Bit Error Register 0
+#define PUBL_BISTBER1 (PUBL_REG_BASE+0x4C*4) // R - BIST Bit Error Register 1
+#define PUBL_BISTBER2 (PUBL_REG_BASE+0x4D*4) // R - BIST Bit Error Register 2
+#define PUBL_BISTWCSR (PUBL_REG_BASE+0x4E*4) // R - BIST Word Count Status Register
+#define PUBL_BISTFWR0 (PUBL_REG_BASE+0x4F*4) // R - BIST Fail Word Register 0
+#define PUBL_BISTFWR1 (PUBL_REG_BASE+0x50*4) // R - BIST Fail Word Register 1
+#define PUBL_ZQ0CR0 (PUBL_REG_BASE+0x60*4) // R/W - ZQ 0 Impedance Control Register 0
+#define PUBL_ZQ0CR1 (PUBL_REG_BASE+0x61*4) // R/W - ZQ 0 Impedance Control Register 1
+#define PUBL_ZQ0SR0 (PUBL_REG_BASE+0x62*4) // R - ZQ 0 Impedance Status Register 0
+#define PUBL_ZQ0SR1 (PUBL_REG_BASE+0x63*4) // R - ZQ 0 Impedance Status Register 1
+#define PUBL_ZQ1CR0 (PUBL_REG_BASE+0x64*4) // R/W - ZQ 1 Impedance Control Register 0
+#define PUBL_ZQ1CR1 (PUBL_REG_BASE+0x65*4) // R/W - ZQ 1 Impedance Control Register 1
+#define PUBL_ZQ1SR0 (PUBL_REG_BASE+0x66*4) // R - ZQ 1 Impedance Status Register 0
+#define PUBL_ZQ1SR1 (PUBL_REG_BASE+0x67*4) // R - ZQ 1 Impedance Status Register 1
+#define PUBL_ZQ2CR0 (PUBL_REG_BASE+0x68*4) // R/W - ZQ 2 Impedance Control Register 0
+#define PUBL_ZQ2CR1 (PUBL_REG_BASE+0x69*4) // R/W - ZQ 2 Impedance Control Register 1
+#define PUBL_ZQ2SR0 (PUBL_REG_BASE+0x6A*4) // R - ZQ 2 Impedance Status Register 0
+#define PUBL_ZQ2SR1 (PUBL_REG_BASE+0x6B*4) // R - ZQ 2 Impedance Status Register 1
+#define PUBL_ZQ3CR0 (PUBL_REG_BASE+0x6C*4) // R/W - ZQ 3 Impedance Control Register 0
+#define PUBL_ZQ3CR1 (PUBL_REG_BASE+0x6D*4) // R/W - ZQ 3 Impedance Control Register 1
+#define PUBL_ZQ3SR0 (PUBL_REG_BASE+0x6E*4) // R - ZQ 3 Impedance Status Register 0
+#define PUBL_ZQ3SR1 (PUBL_REG_BASE+0x6F*4) // R - ZQ 3 Impedance Status Register 1
+#define PUBL_DX0GCR (PUBL_REG_BASE+0x70*4) // R/W - DATX8 0 General Configuration Register
+#define PUBL_DX0GSR0 (PUBL_REG_BASE+0x71*4) // R - DATX8 0 General Status Register
+#define PUBL_DX0GSR1 (PUBL_REG_BASE+0x72*4) // R - DATX8 0 General Status Register 1
+#define PUBL_DX0DLLCR (PUBL_REG_BASE+0x73*4) // R - DATX8 0 DLL Control Register
+#define PUBL_DX0DQTR (PUBL_REG_BASE+0x74*4) // R/W - DATX8 0 DQ Timing Register
+#define PUBL_DX0DQSTR (PUBL_REG_BASE+0x75*4) // R/W - DATX8 0 DQS Timing Register
+#define PUBL_DX1GCR (PUBL_REG_BASE+0x80*4) // R - DATX8 1 General Configration Register
+#define PUBL_DX1GSR0 (PUBL_REG_BASE+0x81*4) // R - DATX8 1 General Status Register
+#define PUBL_DX1GSR1 (PUBL_REG_BASE+0x82*4) // R - DATX8 1 General Status Register
+#define PUBL_DX1DLLCR (PUBL_REG_BASE+0x83*4) // R - DATX8 1 DLL Control Register
+#define PUBL_DX1DQTR (PUBL_REG_BASE+0x84*4) // R/W - DATX8 1 DQ Timing Register
+#define PUBL_DX1DQSTR (PUBL_REG_BASE+0x85*4) // R/W - DATX8 1 DQS Timing Register
+#define PUBL_DX2GCR (PUBL_REG_BASE+0x90*4) // R - DATX8 2 General Configration Register
+#define PUBL_DX2GSR0 (PUBL_REG_BASE+0x91*4) // R - DATX8 2 General Status Register
+#define PUBL_DX2GSR1 (PUBL_REG_BASE+0x92*4) // R - DATX8 2 General Status Register
+#define PUBL_DX2DLLCR (PUBL_REG_BASE+0x93*4) // R - DATX8 2 DLL Control Register
+#define PUBL_DX2DQTR (PUBL_REG_BASE+0x94*4) // R/W - DATX8 2 DQ Timing Register
+#define PUBL_DX2DQSTR (PUBL_REG_BASE+0x95*4) // R/W - DATX8 2 DQS Timing Register
+#define PUBL_DX3GCR (PUBL_REG_BASE+0xA0*4) // R - DATX8 3 General Configration Register
+#define PUBL_DX3GSR0 (PUBL_REG_BASE+0xA1*4) // R - DATX8 3 General Status Register
+#define PUBL_DX3GSR1 (PUBL_REG_BASE+0xA2*4) // R - DATX8 3 General Status Register
+#define PUBL_DX3DLLCR (PUBL_REG_BASE+0xA3*4) // R - DATX8 3 DLL Control Register
+#define PUBL_DX3DQTR (PUBL_REG_BASE+0xA4*4) // R/W - DATX8 3 DQ Timing Register
+#define PUBL_DX3DQSTR (PUBL_REG_BASE+0xA5*4) // R/W - DATX8 3 DQS Timing Register
+#define PUBL_DX4GCR (PUBL_REG_BASE+0xB0*4) // R - DATX8 4 General Configration Register
+#define PUBL_DX4GSR0 (PUBL_REG_BASE+0xB1*4) // R - DATX8 4 General Status Register
+#define PUBL_DX4GSR1 (PUBL_REG_BASE+0xB2*4) // R - DATX8 4 General Status Register
+#define PUBL_DX4DLLCR (PUBL_REG_BASE+0xB3*4) // R - DATX8 4 DLL Control Register
+#define PUBL_DX4DQTR (PUBL_REG_BASE+0xB4*4) // R/W - DATX8 4 DQ Timing Register
+#define PUBL_DX4DQSTR (PUBL_REG_BASE+0xB5*4) // R/W - DATX8 4 DQS Timing Register
+#define PUBL_DX5GCR (PUBL_REG_BASE+0xC0*4) // R - DATX8 5 General Configration Register
+#define PUBL_DX5GSR0 (PUBL_REG_BASE+0xC1*4) // R - DATX8 5 General Status Register
+#define PUBL_DX5GSR1 (PUBL_REG_BASE+0xC2*4) // R - DATX8 5 General Status Register
+#define PUBL_DX5DLLCR (PUBL_REG_BASE+0xC3*4) // R - DATX8 5 DLL Control Register
+#define PUBL_DX5DQTR (PUBL_REG_BASE+0xC4*4) // R/W - DATX8 5 DQ Timing Register
+#define PUBL_DX5DQSTR (PUBL_REG_BASE+0xC5*4) // R/W - DATX8 5 DQS Timing Register
+#define PUBL_DX6GCR (PUBL_REG_BASE+0xD0*4) // R - DATX8 6 General Configration Register
+#define PUBL_DX6GSR0 (PUBL_REG_BASE+0xD1*4) // R - DATX8 6 General Status Register
+#define PUBL_DX6GSR1 (PUBL_REG_BASE+0xD2*4) // R - DATX8 6 General Status Register
+#define PUBL_DX6DLLCR (PUBL_REG_BASE+0xD3*4) // R - DATX8 6 DLL Control Register
+#define PUBL_DX6DQTR (PUBL_REG_BASE+0xD4*4) // R/W - DATX8 6 DQ Timing Register
+#define PUBL_DX6DQSTR (PUBL_REG_BASE+0xD5*4) // R/W - DATX8 6 DQS Timing Register
+#define PUBL_DX7GCR (PUBL_REG_BASE+0xE0*4) // R - DATX8 7 General Configration Register
+#define PUBL_DX7GSR0 (PUBL_REG_BASE+0xE1*4) // R - DATX8 7 General Status Register
+#define PUBL_DX7GSR1 (PUBL_REG_BASE+0xE2*4) // R - DATX8 7 General Status Register
+#define PUBL_DX7DLLCR (PUBL_REG_BASE+0xE3*4) // R - DATX8 7 DLL Control Register
+#define PUBL_DX7DQTR (PUBL_REG_BASE+0xE4*4) // R/W - DATX8 7 DQ Timing Register
+#define PUBL_DX7DQSTR (PUBL_REG_BASE+0xE5*4) // R/W - DATX8 7 DQS Timing Register
+#define PUBL_DX8GCR (PUBL_REG_BASE+0xF0*4) // R - DATX8 8 General Configration Register
+#define PUBL_DX8GSR0 (PUBL_REG_BASE+0xF1*4) // R - DATX8 8 General Status Register
+#define PUBL_DX8GSR1 (PUBL_REG_BASE+0xF2*4) // R - DATX8 8 General Status Register
+#define PUBL_DX8DLLCR (PUBL_REG_BASE+0xF3*4) // R - DATX8 8 DLL Control Register
+#define PUBL_DX8DQTR (PUBL_REG_BASE+0xF4*4) // R/W - DATX8 8 DQ Timing Register
+#define PUBL_DX8DQSTR (PUBL_REG_BASE+0xF5*4) // R/W - DATX8 8 DQS Timing Register
+
+typedef unsigned long int uint32;
+
+
+static inline void reg_bits_set(uint32 addr,uint32 start_bitpos,uint32 bit_num,uint32 value)
+{
+ /*create bit mask according to input param*/
+ uint32 bit_mask = (1<<bit_num)-1;
+ uint32 reg_data = *((volatile uint32*)(addr));
+
+ reg_data &= ~(bit_mask<<start_bitpos);
+ reg_data |= ((value&bit_mask)<<start_bitpos);
+
+ *((volatile uint32*)(addr)) = reg_data;
+}__attribute__((always_inline))
+
+
+static inline void assert_reset_dll(void)
+{
+ REG32(PUBL_ACDLLCR) &= ~(0x1<<30);
+ REG32(PUBL_DX0DLLCR) &= ~(0x1<<30);
+ REG32(PUBL_DX1DLLCR) &= ~(0x1<<30);
+ REG32(PUBL_DX2DLLCR) &= ~(0x1<<30);
+ REG32(PUBL_DX3DLLCR) &= ~(0x1<<30);
+}__attribute__((always_inline))
+
+static inline void deassert_reset_dll(void)
+{
+ REG32(PUBL_ACDLLCR) |= (0x1<<30);
+ REG32(PUBL_DX0DLLCR) |= (0x1<<30);
+ REG32(PUBL_DX1DLLCR) |= (0x1<<30);
+ REG32(PUBL_DX2DLLCR) |= (0x1<<30);
+ REG32(PUBL_DX3DLLCR) |= (0x1<<30);
+}__attribute__((always_inline))
+
+static inline void disable_ddrphy_dll(void) {
+ REG32(PUBL_PIR) |= (1 << 17);
+}__attribute__((always_inline))
+
+static inline void enable_ddrphy_dll(void) {
+ REG32(PUBL_PIR) &= ~(1 << 17);
+}__attribute__((always_inline))
+
+static inline void move_upctl_state_to_self_refresh(void)
+{
+ volatile uint32 val;
+
+ REG32(UMCTL_PWRCTL) = 0;
+ val = REG32(UMCTL_STAT);
+ //wait umctl2 core is in self-refresh mode
+ while((val & 0x7) != 1) {
+ val = REG32(UMCTL_STAT);
+ }
+
+ REG32(UMCTL_DFILPCFG0) = 0x0700f000;
+// val = REG32(UMCTL_STAT);
+ //wait umctl2 core is in self-refresh mode
+// while((val & 0x7) != 3) {
+// val = REG32(UMCTL_STAT);
+// }
+ //check self refresh was due to software
+}__attribute__((always_inline))
+static inline void move_upctl_state_exit_self_refresh(u32 dll_enable)
+{
+ if(dll_enable == EMC_DLL_SWITCH_DISABLE_MODE) {
+ REG32(UMCTL_DFILPCFG0) = 0x0700f100;
+ REG32(UMCTL_PWRCTL) = 0x9;
+ }
+ else{
+// REG32(UMCTL_DFILPCFG0) = 0x0700f000;
+ REG32(UMCTL_PWRCTL) = 0xa;
+ }
+
+// val = REG32(UMCTL_STAT);
+ //wait umctl2 core is in self-refresh mode
+// while((val & 0x7) != 1) {
+// val = REG32(UMCTL_STAT);
+// }
+
+}__attribute__((always_inline))
+
+static inline void disable_cam_command_deque(void)
+{
+ REG32(UMCTL_DBG1) |= (1 << 0);
+}__attribute__((always_inline))
+static inline void enable_cam_command_deque(void)
+{
+ REG32(UMCTL_DBG1) &= ~(1 << 0);
+}__attribute__((always_inline))
+static inline void ddr_clk_set(uint32 new_clk, uint32 delay, uint32 sene, ddr_dfs_val_t *timing);
+static inline void ddr_timing_update(ddr_dfs_val_t *timing);
+static inline void manual_issue_autorefresh(void)
+{
+ uint32 val;
+ val = REG32(UMCTL_RFSHCTL3);
+ val |= (1 << 0) ;
+ val ^= (1 << 1);
+ REG32(UMCTL_RFSHCTL3) = val;
+
+ val = REG32(UMCTL_RFSHCTL3);
+ val &= ~(1 << 0) ;
+ val ^= (1 << 1);
+ REG32(UMCTL_RFSHCTL3) = val;
+}__attribute__((always_inline))
+static inline void wait_queue_complete(void)
+{
+ volatile u32 i = 0;
+ volatile u32 value_temp;
+ while(i < 10)
+ {
+ value_temp = REG32(UMCTL_DBG1);
+ if(value_temp == 1) {
+ i++;
+ }
+ }
+}__attribute__((always_inline))
+static inline void dll_bypass_switch(u32 dll_mode, ddr_dfs_val_t *timing)
+{
+ volatile uint32 i;
+ //for(i = 0; i < 0x80000000; i++);
+ move_upctl_state_to_self_refresh();
+ //timing->publ_mr1 = 0x1234556C;
+ disable_cam_command_deque();
+ REG32(UMCTL_DFIMISC) &= ~(1 << 0);
+ wait_queue_complete();
+ //for(i = 0; i < 20; i++);
+
+ //phy clock close
+// REG32(PMU_APB_BASE + 0xc8) &= ~(1 << 2);
+ REG32(SPRD_PMU_PHYS + 0xc8) &= ~(1 << 6);
+// for(i = 0 ; i < 2; i++);
+
+ if(dll_mode == EMC_DLL_SWITCH_DISABLE_MODE) {
+ disable_ddrphy_dll();
+ //phy clock open
+// REG32(PMU_APB_BASE + 0xc8) |= (1 << 2);
+ REG32(SPRD_PMU_PHYS + 0xc8) |= (1 << 6);
+ for(i = 0 ; i < 2; i++);
+// deassert_reset_dll();
+ REG32(PUBL_PIR) |= (1 << 4) | (1 << 0);
+ while((REG32(PUBL_PGSR) & 1) != 1);
+ while((REG32(PUBL_PGSR) & 1) != 1);
+ REG32(PUBL_PIR) |= (1 << 4);
+ }
+ else if(dll_mode == EMC_DLL_SWITCH_ENABLE_MODE){
+ assert_reset_dll();
+ for(i = 0 ; i < 2; i++);
+ enable_ddrphy_dll();
+ //phy clock open
+// REG32(PMU_APB_BASE + 0xc8) |= (1 << 2);
+ REG32(SPRD_PMU_PHYS + 0xc8) |= (1 << 6);
+ for(i = 0 ; i < 2; i++);
+ deassert_reset_dll();
+ for(i = 0 ; i < 40; i++);
+ }
+ REG32(UMCTL_DFIMISC) |= (1 << 0);
+ enable_cam_command_deque();
+ move_upctl_state_exit_self_refresh(dll_mode);
+}__attribute__((always_inline))
+static inline void umctl2_freq_set(uint32 clk, uint32 DDR_TYPE, uint32 dll_mode,uint32 sene,ddr_dfs_val_t *timing)
+{
+ #ifdef CONFIG_SCX35_DMC_FREQ_AP
+ ddr_clk_set(clk,0,sene,timing);
+ #else
+ if(EMC_DLL_SWITCH_ENABLE_MODE == dll_mode) {
+ dll_bypass_switch(dll_mode, timing);
+ ddr_clk_set(clk, 10, sene,timing);
+ }
+ else if(EMC_DLL_SWITCH_DISABLE_MODE == dll_mode) {
+ ddr_clk_set(clk, 10, sene,timing);
+ dll_bypass_switch(dll_mode, timing);
+ }
+ else if(EMC_DLL_NOT_SWITCH_MODE) {
+ ddr_clk_set(clk, 10, sene,timing);
+ }
+ #endif
+}__attribute__((always_inline))
+static inline void ddr_timing_update(ddr_dfs_val_t *timing)
+{
+ disable_cam_command_deque();
+ REG32(UMCTL_RFSHTMG) = timing->umctl2_rfshtmg;
+// REG32(UMCTL_RFSHTMG) = val;
+ REG32(UMCTL_RFSHCTL3) ^= (1 << 1);
+// manual_issue_autorefresh();
+ REG32(UMCTL_DRAMTMG0) = timing->umctl2_dramtmg0;
+ REG32(UMCTL_DRAMTMG1) = timing->umctl2_dramtmg1;
+ REG32(UMCTL_DRAMTMG2) = timing->umctl2_dramtmg2;
+ //REG32(UMCTL_DRAMTMG3) = timing->umctl2_dramtmg3;
+ REG32(UMCTL_DRAMTMG4) = timing->umctl2_dramtmg4;
+ //REG32(UMCTL_DRAMTMG5) = timing->umctl2_dramtmg5;
+ REG32(UMCTL_DRAMTMG6) = timing->umctl2_dramtmg6;
+ //REG32(UMCTL_DRAMTMG7) = timing->umctl2_dramtmg7;
+ //REG32(UMCTL_DRAMTMG8) = timing->umctl2_dramtmg8;
+ //REG32(UMCTL_DRAMTMG8) = 0x2;
+ REG32(PUBL_DX0DQSTR) = timing->publ_dx0dqstr;
+ REG32(PUBL_DX1DQSTR) = timing->publ_dx1dqstr;
+ REG32(PUBL_DX2DQSTR) = timing->publ_dx2dqstr;
+ REG32(PUBL_DX3DQSTR) = timing->publ_dx3dqstr;
+ REG32(PUBL_DX0GCR) = timing->publ_dx0gcr;
+ REG32(PUBL_DX1GCR) = timing->publ_dx1gcr;
+ REG32(PUBL_DX2GCR) = timing->publ_dx2gcr;
+ REG32(PUBL_DX3GCR) = timing->publ_dx3gcr;
+ enable_cam_command_deque();
+}__attribute__((always_inline))
+
+static int get_dpll_refin(void)
+{
+ return ((REG32(SPRD_AONAPB_PHYS + 0X18) >> 24) & 0x3);
+}__attribute__((always_inline))
+
+#ifdef CONFIG_SCX35_DMC_FREQ_AP
+static inline void exit_lowpower_mode(void)
+{
+ uint32 val;
+
+ *(volatile uint32*)(UMCTL_PWRCTL) = 0x8;
+ val = *(volatile uint32*)(UMCTL_STAT);
+ //wait umctl2 core is in self-refresh mode
+ while((val & 0x7) != 1) {
+ val = *(volatile uint32*)(UMCTL_STAT);
+ }
+ *(volatile uint32*)(UMCTL_DFILPCFG0) = 0x0700f000;
+}__attribute__((always_inline))
+
+static inline void ddr_cam_command_dequeue(uint32 isEnable)
+{
+ if(isEnable)
+ {
+ *(volatile uint32*)(UMCTL_DBG1) &= ~(1<<0);
+ }
+ else
+ {
+ *(volatile uint32*)(UMCTL_DBG1) |= (1<<0);
+ }
+}__attribute__((always_inline))
+
+
+static inline void ddr_timing_update_ex(ddr_dfs_val_t *timing_param)
+{
+ //minimum time from refresh to refresh or active
+ //reg_bits_set(UMCTL_RFSHTMG,0,9,timing_param->umctl2_rfshtmg);
+ //toggle this signel indicate refresh register has been update
+ *(volatile uint32*)(UMCTL_RFSHTMG) = timing_param->umctl2_rfshtmg;
+ *(volatile uint32*)(UMCTL_RFSHCTL3) ^= (1<<1);
+ //update umctl & publ timing
+ *(volatile uint32*)UMCTL_DRAMTMG0 = timing_param->umctl2_dramtmg0;
+ *(volatile uint32*)UMCTL_DRAMTMG1 = timing_param->umctl2_dramtmg1;
+ *(volatile uint32*)UMCTL_DRAMTMG2 = timing_param->umctl2_dramtmg2;
+ //*(volatile uint32*)UMCTL_DRAMTMG3 = timing_param->umctl2_dramtmg3;
+ *(volatile uint32*)UMCTL_DRAMTMG4 = timing_param->umctl2_dramtmg4;
+ //*(volatile uint32*)UMCTL_DRAMTMG5 = timing_param->umctl2_dramtmg5;
+ *(volatile uint32*)UMCTL_DRAMTMG6 = timing_param->umctl2_dramtmg6;
+ //*(volatile uint32*)UMCTL_DRAMTMG7 = timing_param->umctl2_dramtmg7;
+ //*(volatile uint32*)UMCTL_DRAMTMG8 = timing_param->umctl2_dramtmg8;
+ //*(volatile uint32*)UMCTL_DRAMTMG8 = 1;
+
+ *(volatile uint32*)PUBL_DX0DQSTR = timing_param->publ_dx0dqstr;
+ *(volatile uint32*)PUBL_DX1DQSTR = timing_param->publ_dx1dqstr;
+ *(volatile uint32*)PUBL_DX2DQSTR = timing_param->publ_dx2dqstr;
+ *(volatile uint32*)PUBL_DX3DQSTR = timing_param->publ_dx3dqstr;
+ *(volatile uint32*)PUBL_DX0GCR = timing_param->publ_dx0gcr;
+ *(volatile uint32*)PUBL_DX1GCR = timing_param->publ_dx1gcr;
+ *(volatile uint32*)PUBL_DX2GCR = timing_param->publ_dx2gcr;
+ *(volatile uint32*)PUBL_DX3GCR = timing_param->publ_dx3gcr;
+}__attribute__((always_inline))
+
+static inline void uart_putch(uint32 c)
+{
+ *(volatile uint32*)SPRD_UART1_PHYS = c;
+}__attribute__((always_inline))
+
+static inline void ddr_clk_set(uint32 new_clk, uint32 delay, uint32 sene,ddr_dfs_val_t *timing)
+{
+ volatile uint32 i;
+ uint32 reg;
+// uint32 time_1,time_2;
+ uart_putch('0');
+ uart_putch('\n');
+ exit_lowpower_mode();
+ uart_putch('1');
+ uart_putch('\n');
+ //hold bus
+ //*(volatile uint32*)(0X40050048) = 0xc0; //open AON AP TIMER2
+ //time_1 = *(volatile uint32*)(0X40050044);
+ ddr_cam_command_dequeue(0);
+ //uart_putch('2');
+ //uart_putch('\n');
+ //confirm hold bus success
+ //hold not trigger sdram initialization
+ *(volatile uint32*)UMCTL_DFIMISC &= ~0x1;
+ wait_queue_complete();
+ //uart_putch('3');
+ //uart_putch('\n');
+ //disable auto refresh
+ REG32(UMCTL_RFSHCTL3) |= (1<<0);
+ for(i=0;i<10;i++);
+ switch(new_clk)
+ {
+ case 192:
+ {
+ if((sene == EMC_FREQ_NORMAL_SWITCH_SENE) || (sene == EMC_FREQ_RESUME_SENE))
+ {
+ //set tdpll clock divider
+ reg_bits_set((SPRD_AONCKG_PHYS+0x0024),0x8,2,0x1);
+
+ for(i=0;i<2;i++);
+
+ //switch to tdpll source 384Mhz
+ reg_bits_set((SPRD_AONCKG_PHYS+0x0024),0x0,2,0x2);
+
+ for(i=0;i<10;i++);
+ }
+ if(sene == EMC_FREQ_DEEP_SLEEP_SENE)
+ {
+ //switch to dpll source
+ reg_bits_set((SPRD_AONCKG_PHYS+0x0024),0x8,2,0x0);
+
+ for(i=0;i<2;i++);
+ reg_bits_set((SPRD_AONCKG_PHYS+0x0024),0x0,2,0x3);
+ for(i=0;i<10;i++);
+ }
+
+ //phy clock close
+ *(volatile uint32*)(SPRD_PMU_PHYS+0x00c8) &= ~(1<<6);
+ for(i=0;i<10;i++);
+ //close dll
+ disable_ddrphy_dll();
+
+ //phy clock open
+ *(volatile uint32*)(SPRD_PMU_PHYS+0x00c8) |= (1<<6);
+// *(volatile uint32*)0x022b00c8 |= (1<<2);
+ for(i=0;i<10;i++);
+ //deassert_reset_dll();
+
+// wait DLL lock in memory side;
+// use MRS to close the DLL in memory side in 192MHz
+ REG32(PUBL_PIR) |= (1 << 4) | (1 << 0);
+ while((REG32(PUBL_PGSR) & 1) != 1);
+ while((REG32(PUBL_PGSR) & 1) != 1);
+// REG32(PUBL_PIR) |= (1 << 4);
+ REG32(UMCTL_PERFLPR1) &= ~0xFFFF;
+ REG32(UMCTL_PERFWR1) &= ~0xFFFF;
+
+ break;
+ }
+
+ case 332:
+ {
+ //config dpll divider
+ reg_bits_set((SPRD_AONCKG_PHYS+0x0024),0x8,2,0x0);
+
+ for(i=0;i<2;i++);
+
+ //switch to dpll source
+ reg_bits_set((SPRD_AONCKG_PHYS+0x0024),0x0,2,0x3);
+
+ for(i=0;i<10;i++);
+ //phy clock close
+ *(volatile uint32*)(SPRD_PMU_PHYS+0x00c8) &= ~(1<<6);
+ for(i=0;i<10;i++);
+
+ assert_reset_dll();
+ for(i=0;i<10;i++);
+ //open dll
+ enable_ddrphy_dll();
+
+ for(i=0;i<10;i++);
+
+ //phy clock open
+ *(volatile uint32*)(SPRD_PMU_PHYS+0x00c8) |= (1<<6);
+// *(volatile uint32*)0x022b00c8 |= (1<<2);
+ for(i=0;i<10;i++);
+ //release dll
+ deassert_reset_dll();
+ for(i=0;i<30;i++);
+
+ REG32(UMCTL_PERFLPR1) |= 0x100;
+ REG32(UMCTL_PERFWR1) |= 0x20;
+ break;
+ }
+ default:
+ break;
+ }
+
+ //enable auto refresh
+ REG32(UMCTL_RFSHCTL3) &= ~(1<<0);
+ //uart_putch('4');
+ //uart_putch('\n');
+ ddr_timing_update_ex(timing);
+ //uart_putch('5');
+ //uart_putch('\n');
+ *(volatile uint32*)UMCTL_DFIMISC |= (1<<0);
+ ddr_cam_command_dequeue(1);
+ //time_2 = *(volatile uint32*)(0X40050044);
+
+ //if(new_clk == 192)
+ //*(volatile uint32*)(0x1dc0) = time_1 - time_2;
+ //else
+ //*(volatile uint32*)(0x1dd0) = time_1 - time_2;
+ uart_putch('6');
+ uart_putch('\n');
+ if(new_clk == 192)
+ {
+ REG32(UMCTL_DFILPCFG0) = 0x0700f100;
+ REG32(UMCTL_PWRCTL) = 0x9;
+ }
+ else
+ {
+ REG32(UMCTL_PWRCTL) = 0xa;
+ }
+}__attribute__((always_inline))
+
+#else
+static inline void ddr_clk_set(uint32 new_clk, uint32 delay, uint32 sene,ddr_dfs_val_t *timing)
+{
+ uint32 reg_val;
+ uint32 old_clk;
+ uint32 steps;
+ uint32 i;
+ uint32 j;
+ reg_val = REG32(SPRD_AONAPB_PHYS + 0X18);
+ old_clk = reg_val & 0xfff;
+ old_clk *= 4;
+ if(old_clk > new_clk) {
+ steps = (old_clk - new_clk) / 4;
+// ddr_autorefresh_timing_update();
+ for(i = 0; i < steps; i++) {
+ REG32(SPRD_AONAPB_PHYS + 0X18) = REG32(SPRD_AONAPB_PHYS + 0X18) - 1;
+ for(j = 0; j < delay; j++);
+ }
+ ddr_timing_update(timing);
+ }
+ else if(old_clk < new_clk){
+ ddr_timing_update(timing);
+ steps = (new_clk - old_clk) / 4;
+ for(i = 0; i < steps; i++) {
+ REG32(SPRD_AONAPB_PHYS + 0X18) = REG32(SPRD_AONAPB_PHYS + 0X18) + 1;
+ for(j = 0; j < delay; j++);
+ }
+// ddr_autorefresh_timing_update();
+ }
+}__attribute__((always_inline))
+#endif
+
+
+#define DFS_PARAM_ADDR (0x1C00)
+#define DFS_AP_REQ_ARRAY_ADDR (SPRD_IRAM0_PHYS + 0x1BF0)
+inline void dev_freq_set(unsigned long req)
+{
+ u32 ddr_type;
+ u32 clk;
+ u32 dll_mode;
+ u32 sene;
+ ddr_dfs_val_t *timing;
+ REG32(PUBL_DSGCR) &= ~(0x10);
+ ddr_type = (req & EMC_DDR_TYPE_MASK) >> EMC_DDR_TYPE_OFFSET;
+ clk = (req & EMC_CLK_FREQ_MASK) >> EMC_CLK_FREQ_OFFSET;
+ dll_mode = (req & EMC_DLL_MODE_MASK);
+ sene = (req & EMC_FREQ_SENE_MASK) >> EMC_FREQ_SENE_OFFSET;
+ timing = (ddr_dfs_val_t *)(DFS_PARAM_ADDR);
+
+ if(clk == 332)
+ {
+ timing += 1;
+ }
+
+ if(timing->ddr_clk != clk)
+ {
+ uart_putch('d');
+ uart_putch('f');
+ uart_putch('s');
+ uart_putch('e');
+ uart_putch('r');
+ uart_putch('r');
+ uart_putch('o');
+ uart_putch('r');
+ uart_putch('\n');
+ while(1);
+ }
+ umctl2_freq_set(clk, ddr_type, dll_mode,sene, timing);
+
+
+} __attribute__((always_inline))
+void emc_dfs_main(unsigned long flag)
+{
+ uint32_t reg, val;
+ /* disable mmu, cache */
+ asm volatile (
+// "ldr %1, =10000 \n"
+// "1: nop \n"
+// "sub %1, %1, #1 \n"
+// "cmp %1, #0 \n"
+// "bne 1b \n"
+ "mrc p15, 0, %0, c1, c0, 0 \n"
+ "ldr %1, =0x1005 \n"
+ "bic %0, %0, %1 \n"
+ "mcr p15, 0, %0, c1, c0, 0\n"
+ "ldr sp, =0x1BF0 \n"
+ "ldr r11, =0x1F00 \n"
+ : "=r"(reg), "=r"(val)
+ );
+ //for(i = 0; i < 0x80000000; i++);
+ dev_freq_set(flag); /* call dfs freq set function */
+ /* "ldr r11, =0x1c00 \n" */
+ /* jump back */
+ asm volatile (
+ "ldr r0, =cpu_resume \n"
+ "sub r0, r0, #0xc0000000 \n"
+ "add r0, r0, #0x80000000 \n"
+ "mov pc, r0 \n"
+ );
+}
diff --git a/drivers/platform/sprd/dmc_freq_ap_28nm.c b/drivers/platform/sprd/dmc_freq_ap_28nm.c
new file mode 100755
index 00000000..f9b80a5c
--- /dev/null
+++ b/drivers/platform/sprd/dmc_freq_ap_28nm.c
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/input/matrix_keypad.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/__sc8830_dmc_dfs.h>
+
+#include "soc/sprd/chip_x30g/dram_phy_28nm.h"
+
+#define REG32(x) (*((volatile uint32 *)(x)))
+
+#define DFS_PARAM_ADDR (0x1C0C)
+#define DFS_CALC_PARAM_ADDR (0x1F80)
+#define UMCTL_REG_BASE (0x30000000)
+#define PUBL_REG_BASE (0x30010000)
+#define UART1_PHYS_ADDR (0x70100000)
+
+//#define DPLL_CLK 800
+//#define DPLL_REFIN 26
+//#define NINT(FREQ,REFIN) (FREQ/REFIN)
+//#define KINT(FREQ,REFIN) ((FREQ-(FREQ/REFIN)*REFIN)*1048576/REFIN)
+
+typedef unsigned long int uint32;
+
+static inline void uart_putch(uint32 c)
+{
+ REG32(UART1_PHYS_ADDR) = c;
+}__attribute__((always_inline))
+
+static inline uint32 reg_bits_set(uint32 addr,uint32 start_bitpos,uint32 bit_num,uint32 value)
+{
+ /*create bit mask according to input param*/
+ volatile uint32 bit_mask = (1 << bit_num) - 1;
+ volatile uint32 reg_data = REG32(addr);
+
+ reg_data &= ~(bit_mask<<start_bitpos);
+ reg_data |= ((value&bit_mask)<<start_bitpos);
+
+ REG32(addr) = reg_data;
+
+ return 0;
+}__attribute__((always_inline))
+
+static inline void move_upctl_state_to_self_refresh(void)
+{
+ volatile uint32 val;
+ DMC_UMCTL_REG_INFO_PTR_T p_umctl_reg = (DMC_UMCTL_REG_INFO_PTR_T)UMCTL_REG_BASE;
+
+ p_umctl_reg->umctl_pwrctl = BIT_PWRCTL_SELFREF_SW;
+
+ /* wait umctl2 core is in self-refresh mode */
+ val = p_umctl_reg->umctl_stat;
+ while ((val & (BIT_STAT_SELFREF_TYPE | BIT_STAT_OP_MODE)) != 0x23) {
+ val = p_umctl_reg->umctl_stat;
+ }
+}__attribute__((always_inline))
+
+static inline void move_upctl_state_exit_self_refresh(void )
+{
+ volatile uint32 val;
+ DMC_UMCTL_REG_INFO_PTR_T p_umctl_reg = (DMC_UMCTL_REG_INFO_PTR_T)UMCTL_REG_BASE;
+
+ p_umctl_reg->umctl_pwrctl = 0x00;
+
+ /* wait umctl2 core is in self-refresh mode */
+ val = p_umctl_reg->umctl_stat;
+ while ((val & BIT_STAT_OP_MODE) != 0x1) {
+ val = p_umctl_reg->umctl_stat;
+ }
+
+}__attribute__((always_inline))
+
+#if 0
+static inline void disable_ddrphy_pll(void) {
+ DMC_PUBL_REG_INFO_PTR_T p_publ_reg = (DMC_PUBL_REG_INFO_PTR_T) PUBL_REG_BASE;
+ p_publ_reg->publ_pir |= BIT_PIR_PLLBYP;
+}__attribute__((always_inline))
+
+static inline void enable_ddrphy_pll(void) {
+ DMC_PUBL_REG_INFO_PTR_T p_publ_reg = (DMC_PUBL_REG_INFO_PTR_T) PUBL_REG_BASE;
+ p_publ_reg->publ_pir &= ~BIT_PIR_PLLBYP;
+}__attribute__((always_inline))
+
+static inline void disable_cam_command_deque(void)
+{
+ DMC_UMCTL_REG_INFO_PTR_T p_umctl_reg = (DMC_UMCTL_REG_INFO_PTR_T)UMCTL_REG_BASE;
+ p_umctl_reg->umctl_dbg[1] |= BIT_DBG1_DIS_DQ;
+}__attribute__((always_inline))
+
+static inline void enable_cam_command_deque(void)
+{
+ DMC_UMCTL_REG_INFO_PTR_T p_umctl_reg = (DMC_UMCTL_REG_INFO_PTR_T)UMCTL_REG_BASE;
+ p_umctl_reg->umctl_dbg[1] &= ~BIT_DBG1_DIS_DQ;
+}__attribute__((always_inline))
+
+static inline uint32 dqs_gating_training(uint32 new_clk)
+{
+ volatile uint32 s_pgcr, s_dsgcr, i;
+ DMC_PUBL_REG_INFO_PTR_T p_publ_reg = (DMC_PUBL_REG_INFO_PTR_T) PUBL_REG_BASE;
+
+ /* phy rst */
+ p_publ_reg->publ_pgcr[1] &= ~(0x1 << 25);
+ for(i = 0; i < 2; i++);
+ p_publ_reg->publ_pgcr[1] |= (0x1 << 25);
+
+ /* ACBVT, [4:3]:RX FIFO Read Mode,00 asynchronous. */
+ s_pgcr = p_publ_reg->publ_pgcr[3];
+ p_publ_reg->publ_pgcr[3] &= ~((0x3 << 3) | (0x1 << 24));
+
+ /* DQSGX: DQS Gate extention, do not extend the gate. */
+ s_dsgcr = p_publ_reg->publ_dsgcr;
+ p_publ_reg->publ_dsgcr &= ~(0x03 << 6);
+
+ if( new_clk == 200 ) {
+ p_publ_reg->publ_dtpr[3] = 0x1;
+ }
+ else if (new_clk == 400){
+ p_publ_reg->publ_dtpr[3] = 0xa;
+ }
+
+ /*do training*/
+ p_publ_reg->publ_pir |= ((1 << 10)|(1 << 0));
+
+ while( (p_publ_reg->publ_pgsr[0] & (0x1 << 0)) != 0x1 );
+ /*check ddr training status*/
+ if( p_publ_reg->publ_pgsr[0] & (1<<22) ){
+ while(1);
+ }
+
+ p_publ_reg->publ_pgcr[3] = s_pgcr;
+ p_publ_reg->publ_dsgcr = s_dsgcr;
+}__attribute__((always_inline))
+#endif
+
+static inline void wait_queue_complete(void)
+{
+ volatile uint32 value_temp;
+ DMC_UMCTL_REG_INFO_PTR_T p_umctl_reg = (DMC_UMCTL_REG_INFO_PTR_T)UMCTL_REG_BASE;
+
+ while(1) {
+ value_temp = p_umctl_reg->umctl_dbgcam;
+ if (((value_temp & BIT_DBGCAM_WR_PP_EPTY) != 0) &&
+ ((value_temp & BIT_DBGCAM_RD_PP_EPTY) != 0)) {
+
+ return;
+ }
+ }
+}__attribute__((always_inline))
+
+static inline void exit_lowpower_mode(uint32 *reg_store)
+{
+ volatile uint32 val;
+ DMC_UMCTL_REG_INFO_PTR_T p_umctl_reg = (DMC_UMCTL_REG_INFO_PTR_T)UMCTL_REG_BASE;
+#if 0
+ p_umctl_reg->umctl_pwrctl = BIT_PWRCTL_DFICLK_DIS;
+ val = p_umctl_reg->umctl_stat;
+ /* wait umctl2 core is in self-refresh mode */
+ while((val & 0x7) != 1) {
+ val = p_umctl_reg->umctl_stat;
+ }
+#endif
+
+ /* disable_cgm */
+ REG32(SPRD_PMU_PHYS + 0xF8) &= ~((3 << 30) );
+
+ /* disable lp interface. */
+ reg_store[0] = p_umctl_reg->umctl_hwlpctl;
+ p_umctl_reg->umctl_hwlpctl &= ~0x03;
+ //p_umctl_reg->umctl_dfilpcfg[0] = 0x0700f000;
+
+ /* disable_powerdown */
+ reg_store[1] = p_umctl_reg->umctl_pwrctl;
+ p_umctl_reg->umctl_pwrctl = 0;
+ /* wait status to normal mode */
+ val = p_umctl_reg->umctl_stat;
+ while ((val & BIT_STAT_OP_MODE) != 0x1) {
+ val = p_umctl_reg->umctl_stat;
+ }
+
+ /* disable dfi low power interface */
+ p_umctl_reg->umctl_dfilpcfg[0] &= ~(1 << 8);
+
+}__attribute__((always_inline))
+
+static inline void enable_lowpower_mode(uint32 *reg_store)
+{
+ DMC_UMCTL_REG_INFO_PTR_T p_umctl_reg = (DMC_UMCTL_REG_INFO_PTR_T)UMCTL_REG_BASE;
+
+ /* enable_sleep */
+ p_umctl_reg->umctl_dfilpcfg[0] |= (1 << 8);
+
+ /* enable lp interface. */
+ p_umctl_reg->umctl_hwlpctl = reg_store[0];
+
+ /* enable_powerdown */
+ p_umctl_reg->umctl_pwrctl = reg_store[1];
+
+ /* enable_cgm */
+ REG32(SPRD_PMU_PHYS + 0xF8) |= ((3 << 30) );
+
+}__attribute__((always_inline))
+
+static inline void ddr_cam_command_dequeue(uint32 isEnable)
+{
+ DMC_UMCTL_REG_INFO_PTR_T p_umctl_reg = (DMC_UMCTL_REG_INFO_PTR_T)UMCTL_REG_BASE;
+
+ if(isEnable) {
+ p_umctl_reg->umctl_dbg[1] &= ~BIT_DBG1_DIS_DQ;
+ }
+ else {
+ p_umctl_reg->umctl_dbg[1] |= BIT_DBG1_DIS_DQ;
+ }
+}__attribute__((always_inline))
+
+
+static inline void ddr_timing_update(ddr_dfs_v2_t *timing_param)
+{
+ DMC_UMCTL_REG_INFO_PTR_T p_umctl_reg = (DMC_UMCTL_REG_INFO_PTR_T)UMCTL_REG_BASE;
+ DMC_PUBL_REG_INFO_PTR_T p_publ_reg = (DMC_PUBL_REG_INFO_PTR_T) PUBL_REG_BASE;
+ publ_calc_t *calc = (publ_calc_t *)DFS_CALC_PARAM_ADDR;
+
+ /* minimum time from refresh to refresh or active */
+ //toggle this signel indicate refresh register has been update
+ p_umctl_reg->umctl_rfshtmg = timing_param->umctl_rfshtmg;
+ p_umctl_reg->umctl_rfshctl3 ^= (1<<1);
+
+ //update umctl & publ timing
+ p_umctl_reg->umctl_dramtmg[0] = timing_param->umctl_dramtmg0;
+ p_umctl_reg->umctl_dramtmg[1] = timing_param->umctl_dramtmg1;
+ p_umctl_reg->umctl_dramtmg[2] = timing_param->umctl_dramtmg2;
+ p_umctl_reg->umctl_dramtmg[3] = timing_param->umctl_dramtmg3;
+ p_umctl_reg->umctl_dramtmg[4] = timing_param->umctl_dramtmg4;
+ p_umctl_reg->umctl_dramtmg[5] = timing_param->umctl_dramtmg5;
+ p_umctl_reg->umctl_dramtmg[7] = timing_param->umctl_dramtmg7;
+ p_umctl_reg->umctl_dramtmg[8] = timing_param->umctl_dramtmg8;
+
+ p_umctl_reg->umctl_dfitmg[0] = timing_param->umctl_dfitmg0;
+ p_umctl_reg->umctl_init[3] = timing_param->umctl_init3;
+ p_umctl_reg->umctl_zqctl[0] = timing_param->umctl_zqctl0;
+ p_umctl_reg->umctl_zqctl[1] = timing_param->umctl_zqctl1;
+
+ p_publ_reg->publ_mr[2] = timing_param->publ_mr2;
+
+ p_publ_reg->publ_dx0gtr = timing_param->publ_dx0gtr;
+ p_publ_reg->publ_dx1gtr = timing_param->publ_dx1gtr;
+ p_publ_reg->publ_dx2gtr = timing_param->publ_dx2gtr;
+ p_publ_reg->publ_dx3gtr = timing_param->publ_dx3gtr;
+
+ p_publ_reg->publ_pgcr[3] = timing_param->publ_pgcr3;
+ p_publ_reg->publ_acmdlr = calc->publ_acmdlr;
+ p_publ_reg->publ_dx0mdlr = calc->publ_dx0mdlr;
+ p_publ_reg->publ_dx1mdlr = calc->publ_dx1mdlr;
+ p_publ_reg->publ_dx2mdlr = calc->publ_dx2mdlr;
+ p_publ_reg->publ_dx3mdlr = calc->publ_dx3mdlr;
+ p_publ_reg->publ_aclcdlr = calc->publ_aclcdlr;
+ p_publ_reg->publ_dx0lcdlr[0] = calc->publ_dx0lcdlr0;
+ p_publ_reg->publ_dx0lcdlr[1] = calc->publ_dx0lcdlr1;
+ p_publ_reg->publ_dx0lcdlr[2] = calc->publ_dx0lcdlr2;
+ p_publ_reg->publ_dx1lcdlr[0] = calc->publ_dx1lcdlr0;
+ p_publ_reg->publ_dx1lcdlr[1] = calc->publ_dx1lcdlr1;
+ p_publ_reg->publ_dx1lcdlr[2] = calc->publ_dx1lcdlr2;
+ p_publ_reg->publ_dx2lcdlr[0] = calc->publ_dx2lcdlr0;
+ p_publ_reg->publ_dx2lcdlr[1] = calc->publ_dx2lcdlr1;
+ p_publ_reg->publ_dx2lcdlr[2] = calc->publ_dx2lcdlr2;
+ p_publ_reg->publ_dx3lcdlr[0] = calc->publ_dx3lcdlr0;
+ p_publ_reg->publ_dx3lcdlr[1] = calc->publ_dx3lcdlr1;
+ p_publ_reg->publ_dx3lcdlr[2] = calc->publ_dx3lcdlr2;
+
+ p_publ_reg->publ_dsgcr = timing_param->publ_dsgcr;
+ p_publ_reg->publ_dtpr[0] = timing_param->publ_dtpr0;
+ p_publ_reg->publ_dtpr[1] = timing_param->publ_dtpr1;
+ p_publ_reg->publ_dtpr[2] = timing_param->publ_dtpr2;
+ p_publ_reg->publ_dtpr[3] = timing_param->publ_dtpr3;
+
+}__attribute__((always_inline))
+
+static inline void ddr_clk_set(uint32 new_clk, ddr_dfs_v2_t *timing)
+{
+ volatile uint32 i;
+ uint32 reg_store[3];
+
+ DMC_UMCTL_REG_INFO_PTR_T p_umctl_reg = (DMC_UMCTL_REG_INFO_PTR_T)UMCTL_REG_BASE;
+ DMC_PUBL_REG_INFO_PTR_T p_publ_reg = (DMC_PUBL_REG_INFO_PTR_T) PUBL_REG_BASE;
+
+ uart_putch('d');
+ exit_lowpower_mode(&(reg_store[0]));
+
+#if 0
+REG32(0x8F001000)= 0x11223344;
+
+for (i = 0; i < 0x20; i ++)
+{
+ REG32(0x1F00 + (i << 2)) = 0xAAAAAAAA;
+}
+#endif
+ /* step a: move dram into self-refresh. */
+ move_upctl_state_to_self_refresh();
+
+ /* step b: changing the input clock period. */
+ /* hold bus : set DBG1.dis_dq = 1 */
+ ddr_cam_command_dequeue(0);
+
+ /* DFIMISC.dfi_init_complete_en =0.hold not trigger sdram initialization */
+ p_umctl_reg->umctl_dfimisc &= ~BIT_DFIMISC_DFI_COMP_EN;
+ wait_queue_complete();
+
+ /* step c: change the clock frequency to the DWC_ddr_umctl2 and ensure no glitchs. */
+ /* phy clock close : DDR_PHY_AUTO_GATE_EN */
+ reg_store[2] = REG32(SPRD_PMU_PHYS+0x00D0);
+ REG32(SPRD_PMU_PHYS+0x00D0) &= ~((1 << 6) | 0x07);
+ for(i = 0; i < 0x2; i++);
+
+ switch(new_clk) {
+ case 192:
+ {
+ reg_bits_set((SPRD_AONCKG_PHYS + 0x0024), 0x8, 2, 0x1);
+ for(i = 0; i < 0x2; i++);
+
+ reg_bits_set((SPRD_AONCKG_PHYS + 0x0024), 0x0, 2, 0x2);
+ for(i = 0; i < 0x2; i++);
+ break;
+ }
+
+ case 200:
+ {
+ /* switch to dpll source */
+ reg_bits_set((SPRD_AONCKG_PHYS + 0x0024), 0x8, 2, 0x1);
+ for(i = 0; i < 0x2; i++);
+
+ reg_bits_set((SPRD_AONCKG_PHYS + 0x0024), 0x0, 2, 0x3);
+ for(i = 0; i < 0x2; i++);
+ break;
+ }
+
+ case 384:
+ {
+ /* set dpll clock to 192M : set DPLL_REFIN to 26M * 26 = 676*/
+ //reg_bits_set((SPRD_AONAPB_PHYS + 0x3004), 24, 2, 0x03);
+ //reg_bits_set((SPRD_AONAPB_PHYS + 0x3074), 0, 6, 26);
+
+ /* switch to dpll source */
+ /* reg[0x402D0024] :*/
+ /* [9:8] : clk_emc_div(clk_div= clk_src/(div+1)) */
+ /* [1:0] : clk_emc_sel (0:pub 26m, 1: CPLL, 2:TDPLL, 3:DPLL)*/
+ reg_bits_set((SPRD_AONCKG_PHYS + 0x0024), 0x8, 2, 0x0);
+ for(i = 0; i < 0x2; i++);
+
+ reg_bits_set((SPRD_AONCKG_PHYS + 0x0024), 0x0, 2, 0x2);
+ for(i = 0; i < 0x2; i++);
+
+ break;
+ }
+
+ case 400:
+ case 466:
+ case 533:
+ {
+ /* set dpll clock to 400M : set DPLL_REFIN to 4M*/
+ /* reg[0x402E3004] :*/
+ /* [25:24] : DPLL_REFIN(00: 2M, 01 :4M, 10:13M, 11:26M) */
+ /* [10:0] : DPLL_N( dpll = dpll_refin * dpll_n)*/
+ /* reg[0x402E3074] :*/
+ /* [31:12] : DPLL_KINT */
+ /* [10] : DPLL_DIV_S */
+ /* [7] : DPLL_MOD_EN */
+ /* [6] : DPLL_SDM_EN */
+ /* [5:0] : DPLL_NINT */
+ #if 0
+ /* set dpll clock to 201M : set DPLL_REFIN to 26M * 31 = 806M*/
+ reg_val = REG32(REG_AON_APB_DPLL_CFG1);
+ reg_val |= 1 << 10; // fractional divider
+ reg_val &= ~(0xFFFFF << 12 | 0x3f);
+ reg_val |= (KINT(DPLL_CLK, DPLL_REFIN) & 0xFFFFF) << 12;
+ reg_val |= (NINT(DPLL_CLK, DPLL_REFIN)) & 0x3f;
+ REG32(REG_AON_APB_DPLL_CFG1)= reg_val;
+
+ reg_val = REG32(REG_AON_APB_DPLL_CFG);
+ reg_val &= ~(3 << 24);
+ reg_val |= (3 << 24);
+ REG32(REG_AON_APB_DPLL_CFG) = reg_val;
+ #endif
+
+ /* config dpll divider */
+ reg_bits_set((SPRD_AONCKG_PHYS+0x0024), 0x8, 2, 0x0);
+ for(i = 0; i < 0x2; i++);
+
+ /*switch to dpll source */
+ reg_bits_set((SPRD_AONCKG_PHYS+0x0024), 0x0, 2, 0x3);
+ for(i = 0; i < 0x2; i++);
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ /* phy clock open */
+ REG32(SPRD_PMU_PHYS+0x00D0) = reg_store[2];
+ for(i = 0; i < 0x2; i++);
+
+ /* step d: re-lock mode sequence. */
+ /* step e: set VT inhibit register pgcr1[26] and DCAL bypass PIR[29]. */
+ p_publ_reg->publ_pgcr[1] |= (0x1 << 26);
+ for(i = 0; i < 0x2; i++);
+ while((p_publ_reg->publ_pgsr[1] & (1<<30)) == 0);
+
+ p_publ_reg->publ_pir |= BIT_PIR_DCALBYP;
+ for(i = 0; i < 0x2; i++);
+ while ((p_publ_reg->publ_pgsr[0] & 0x01) != 1);
+
+ /* step f: update phy timing register. static and dynamic register */
+ ddr_timing_update(timing);
+
+ /* step g: trigger the initization PHY */
+ p_publ_reg->publ_pgcr[1] &= ~(0x1 << 26);
+ for(i = 0; i < 0x2; i++);
+ p_publ_reg->publ_pir &= ~BIT_PIR_DCALBYP;
+ for(i = 0; i < 0x2; i++);
+
+ /* dfi complete enable */
+ p_umctl_reg->umctl_dfimisc |= BIT_DFIMISC_DFI_COMP_EN;
+
+ uart_putch('f');
+ /* step i: require to exit sel_refresh by PWRCTL.selfref_sw. */
+ move_upctl_state_exit_self_refresh();
+
+ /* set ddr mr2 */
+ p_umctl_reg->umctl_mrctrl[1] = (2 << 8) | (timing->publ_mr2 & 0xFF);
+ p_umctl_reg->umctl_mrctrl[0] = (3 << 4) | (1 << 31);
+ while((p_umctl_reg->umctl_mrstat & 0x01) != 0);
+
+#if 0
+ /* step 9: do dqs traning. */
+ dqs_gating_training(new_clk);
+#endif
+
+ /* step j: FBG1.dis_dq = 0 */
+ ddr_cam_command_dequeue(1);
+
+ /* enable lowpower */
+ enable_lowpower_mode(&(reg_store[0]));
+#if 0
+for (i = 0; i < 0x20; i ++)
+{
+ REG32(0x1F00 + (i << 2)) = REG32(0x8F001000);
+}
+
+for (i = 0; i < 0x20; i ++)
+{
+ if (REG32(0x1F00 + (i << 2)) != 0x11223344) {
+
+ while(1);
+ }
+}
+#endif
+
+ uart_putch('s');
+}__attribute__((always_inline))
+
+static inline ddr_dfs_v2_t *get_clk_timing( uint32 clk )
+{
+ volatile uint32 i;
+ ddr_dfs_v2_t *timing;
+ publ_calc_t *calc;
+
+ timing = (ddr_dfs_v2_t *)(DFS_PARAM_ADDR);
+ calc = (publ_calc_t *)DFS_CALC_PARAM_ADDR;
+
+ for (i = 0; i < 5; i++) {
+ if (clk == timing->ddr_clk) {
+ if (clk == calc->ddr_clk) {
+ return timing;
+ }
+ }
+ timing++;
+ }
+
+ return (ddr_dfs_v2_t *)0;
+}__attribute__((always_inline))
+
+inline void dev_freq_set(unsigned long req)
+{
+ u32 clk;
+// u32 sene;
+ ddr_dfs_v2_t *timing;
+
+ //ddr_type = (req & EMC_DDR_TYPE_MASK) >> EMC_DDR_TYPE_OFFSET;
+ clk = (req & EMC_CLK_FREQ_MASK) >> EMC_CLK_FREQ_OFFSET;
+ //dll_mode = (req & EMC_DLL_MODE_MASK);
+ //sene = (req & EMC_FREQ_SENE_MASK) >> EMC_FREQ_SENE_OFFSET;
+
+ timing = get_clk_timing(clk);
+ if(timing->ddr_clk != clk) {
+ uart_putch('d');
+ uart_putch('f');
+ uart_putch('s');
+ uart_putch('e');
+ uart_putch('r');
+ uart_putch('r');
+ uart_putch('o');
+ uart_putch('r');
+ uart_putch('\n');
+ while(1);
+ }
+
+ ddr_clk_set(clk, timing);
+} __attribute__((always_inline))
+
+void emc_dfs_main(unsigned long flag)
+{
+ volatile uint32 reg, val;
+ /* disable mmu, cache */
+ /* SP : 0x1B00 ~ 0x1BFF */
+ /* ddr timing params : 0x1C00 ~ 0x1CFF */
+ asm volatile (
+ "mrc p15, 0, %0, c1, c0, 0 \n"
+ "ldr %1, =0x1005 \n"
+ "bic %0, %0, %1 \n"
+ "mcr p15, 0, %0, c1, c0, 0\n"
+ "ldr sp, =0x1B00 \n"
+ "ldr r11, =0x1F00 \n"
+ : "=r"(reg), "=r"(val)
+ );
+
+ dev_freq_set(flag); /* call dfs freq set function */
+ /* jump back */
+ asm volatile (
+ "ldr r0, =cpu_resume \n"
+ "sub r0, r0, #0xc0000000 \n"
+ "add r0, r0, #0x80000000 \n"
+ "mov pc, r0 \n"
+ );
+}
diff --git a/drivers/platform/sprd/fix_v7_tag_ram_bug.S b/drivers/platform/sprd/fix_v7_tag_ram_bug.S
new file mode 100644
index 00000000..255ac530
--- /dev/null
+++ b/drivers/platform/sprd/fix_v7_tag_ram_bug.S
@@ -0,0 +1,183 @@
+/*
+* fix arm-v7 tag ram bug
+*
+*/
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <mach/hardware.h>
+#include <mach/sci_glb_regs.h>
+
+#define REG_HOLDING_PEN_VADDR (SPRD_AHB_BASE + 0x4c)
+#define REG_CPU1_JUMP_VADDR (SPRD_AHB_BASE + 0x54)
+#define REG_CA7_STANDBY_STATUS_VADDR (SPRD_AHB_BASE + 0x48)
+
+ .data
+
+@other_cpu_fix function phys
+.globl other_cpu_fix_phys
+other_cpu_fix_phys:
+ .long 0x0
+
+@invalidation_flag for other 3 cpus
+invalidation_flag:
+ .long 0x0
+ .long 0x0
+ .long 0x0
+all_complete:
+ .long 0x0
+
+ .text
+
+ENTRY(fix_tag_ram_bug)
+ stmfd sp!, {r4-r12, lr}
+
+ mrc p15, 0, r1, c1, c0, 0
+ bic r0, r1, #(1 << 2) @disable d-cache
+ mcr p15, 0, r0, c1, c0, 0
+ bl v7_flush_dcache_all @flush d-cache
+ isb
+
+ mov r2, #0x0
+ ldr r1, =invalidation_flag
+ str r2, [r1] @clean all invalidation flag
+ str r2, [r1, #4]
+ str r2, [r1, #8]
+ ldr r1, =all_complete
+ str r2, [r1] @clean complete flag
+
+ ldr r1, =other_cpu_fix_phys @other_cpu_fix function virt_to_phys
+ ldr r3, [r1]
+ cmp r3, #0 @function phys shouldn't be zero
+ beq finished
+ ldr r1, =REG_CPU1_JUMP_VADDR @change other cpus jump address
+ str r3, [r1]
+ str r3, [r1, #4]
+ str r3, [r1, #8]
+ ldr r1, =REG_HOLDING_PEN_VADDR @write holding pen for other cpus
+ mov r2, #(0x02 + 0x04 + 0x08)
+ str r2, [r1]
+ dsb
+
+ mov r0, #0x01 @power on other cpus
+ bl poweron_cpus
+ mov r0, #0x02
+ bl poweron_cpus
+ mov r0, #0x03
+ bl poweron_cpus
+ ldr r0, =0x0000ffff
+ bl my_delay
+ sev
+
+loop_wfe:
+ wfe
+ ldr r1, =invalidation_flag
+ ldr r2, [r1]
+ cmp r2, #1
+ bne loop_wfe
+ ldr r2, [r1, #4]
+ cmp r2, #1
+ bne loop_wfe
+ ldr r2, [r1, #8]
+ cmp r2, #1
+ bne loop_wfe
+
+ mov r0, #0x1
+ ldr r1, =all_complete
+ str r0, [r1]
+ dsb
+ sev
+ wfe @just ignore sev before
+ nop
+/*
+ ldr r0, =0x00ffffff @make sure other cpus go into wfi
+ bl my_delay
+*/
+#if 0 //uart debug
+ adr r0, str_cpu0
+ bl printascii_phy
+#endif
+
+ ldr r2, =physical_from_idle @wether or not from physical_from_idle
+ ldr r5, [r2]
+ ldr r2, =REG_CA7_STANDBY_STATUS_VADDR
+wait_others_wfi:
+ ldr r3, [r2]
+ eor r3, r5
+ and r3, #0x0e
+ teq r3, #0x0e @check wfi? which not from physical_from_idle
+ bne wait_others_wfi
+
+ tst r5, #(1 << 1)
+ mov r0, #0x1
+ bleq powerdown_cpus
+ tst r5, #(1 << 2)
+ mov r0, #0x2
+ bleq powerdown_cpus
+ tst r5, #(1 << 3)
+ mov r0, #0x3
+ bleq powerdown_cpus
+
+finished:
+ mrc p15, 0, r1, c1, c0, 0
+ orr r0, r1, #(1 << 2) @enable d-cache
+ mcr p15, 0, r0, c1, c0, 0
+
+ ldmfd sp!, {r4-r12, pc}
+ENDPROC(fix_tag_ram_bug)
+
+
+ENTRY(other_cpu_fix)
+ mov r0, #0x0
+ mcr p15, 0, r0, c8, c3, 1
+ isb
+ dsb
+ nop
+ mrc p15, 0, r1, c0, c0, 5
+ and r1, r1, #3 @ r1=cpu mpid
+ cmp r1, #0x0
+ beq wfi_for_powerdown @ mpid shouldn't be zero
+ mov r9, r1 @ r9=saved cpu mpid
+ sub r1, #1
+ lsl r1, #2
+
+ ldr r6, =invalidation_flag
+ ldr r4, =other_cpu_fix
+ adr r5, other_cpu_fix
+ add r6, r6, r5
+ sub r6, r6, r4 @ r6=invalidation_flag phys
+ ldr r7, =all_complete
+ add r7, r7, r5
+ sub r7, r7, r4 @ r7=all_complete phys
+ ldr r8, =physical_from_idle
+ add r8, r8, r5
+ sub r8, r8, r4 @ r8=physical_from_idle phys
+
+ mov r2, #0x1
+ str r2, [r6, r1]
+ dsb
+ sev
+
+loop_wfe_other:
+ wfe
+ ldr r0, [r7]
+ cmp r0, #0x1
+ bne loop_wfe_other
+#if 0 //uart debug
+ adr r0, str_cpus
+ bl printascii_phy
+#endif
+ ldr r0, [r8]
+ mov r2, #0x1
+ lsl r2, r9
+ tst r0, r2
+ beq wfi_for_powerdown
+ mov pc, #0x0 @if from idle, cpu pc should goto 0x0
+
+wfi_for_powerdown:
+ wfi
+ b wfi_for_powerdown
+ENDPROC(other_cpu_fix)
+
+str_cpu0: .asciz "!! cpu0 !!\n"
+str_cpus: .asciz "-others-\n"
diff --git a/drivers/platform/sprd/glb.c b/drivers/platform/sprd/glb.c
new file mode 100644
index 00000000..2f9da396
--- /dev/null
+++ b/drivers/platform/sprd/glb.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * 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.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/hwspinlock.h>
+#include <linux/io.h>
+
+#include <soc/sprd/sci.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/arch_lock.h>
+
+u32 sci_glb_read(unsigned long reg, u32 msk)
+{
+ return __raw_readl((void *)reg) & msk;
+}
+
+int sci_glb_write(unsigned long reg, u32 val, u32 msk)
+{
+ unsigned long flags;
+ __arch_default_lock(HWLOCK_GLB, &flags);
+ __raw_writel((__raw_readl((void *)reg) & ~msk) | val, (void *)reg);
+ __arch_default_unlock(HWLOCK_GLB, &flags);
+ return 0;
+}
+
+static int __is_glb(unsigned long reg)
+{
+#if defined (CONFIG_ARCH_SCX35)
+ return 0;
+ return rounddown(reg, SZ_64K) == rounddown(REGS_GLB_BASE, SZ_64K) ||
+ rounddown(reg, SZ_64K) == rounddown(REGS_AHB_BASE, SZ_64K) ||
+ rounddown(reg, SZ_64K) == rounddown(REGS_AP_APB_BASE, SZ_64K) ||
+ rounddown(reg, SZ_64K) == rounddown(REGS_PMU_APB_BASE, SZ_64K);
+
+#else
+ return rounddown(reg, SZ_64K) == rounddown(REGS_GLB_BASE, SZ_64K) ||
+ rounddown(reg, SZ_64K) == rounddown(REGS_AHB_BASE, SZ_64K);
+#endif
+}
+
+int sci_glb_set(unsigned long reg, u32 bit)
+{
+ if (__is_glb(reg)) {
+ __raw_writel(bit, (void *)REG_GLB_SET(reg));
+ } else {
+ unsigned long flags;
+ __arch_default_lock(HWLOCK_GLB, &flags);
+ __raw_writel(__raw_readl((void *)reg) | bit, (void *)reg);
+ __arch_default_unlock(HWLOCK_GLB, &flags);
+ }
+ return 0;
+}
+
+int sci_glb_clr(unsigned long reg, u32 bit)
+{
+ if (__is_glb(reg)) {
+ __raw_writel(bit, (void *)REG_GLB_CLR(reg));
+ } else {
+ unsigned long flags;
+ __arch_default_lock(HWLOCK_GLB, &flags);
+ __raw_writel((__raw_readl((void *)reg) & ~bit), (void *)reg);
+ __arch_default_unlock(HWLOCK_GLB, &flags);
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(sci_glb_read);
+EXPORT_SYMBOL(sci_glb_write);
+EXPORT_SYMBOL(sci_glb_set);
+EXPORT_SYMBOL(sci_glb_clr);
+
diff --git a/drivers/platform/sprd/headsmp.S b/drivers/platform/sprd/headsmp.S
new file mode 100644
index 00000000..8a112000
--- /dev/null
+++ b/drivers/platform/sprd/headsmp.S
@@ -0,0 +1,84 @@
+/*
+ * linux/arch/arm/mach-sc/headsmp.S
+ *
+ *
+ * Copyright (c) 2003 ARM Limited
+ * All Rights Reserved
+ *
+ * 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/linkage.h>
+#include <linux/init.h>
+
+ __CPUINIT
+
+/*
+ * sci smp specific entry point for secondary CPUs.
+ * This provides a "holding pen" into which all secondary cores are held
+ * until we're ready for them to initialise.
+ */
+ENTRY(sci_secondary_startup)
+ mrc p15, 0, r0, c0, c0, 5
+#ifndef CONFIG_OF
+ and r0, r0, #15
+#else
+ /*and r0, r0, #0xFFFFFF*/ /* mpidr is 0xf00 0xf01, 0xf02, 0xf03 */
+ and r0, r0, #15 /* mpidr is 0xf00 0xf01, 0xf02, 0xf03 */
+#endif
+ adr r4, 1f
+ ldmia r4, {r5, r6}
+ sub r4, r4, r5
+ add r6, r6, r4
+pen: ldr r7, [r6]
+ and r7, r7, #15/* mpidr is 0xf00 0xf01, 0xf02, 0xf03 */
+ cmp r7, r0
+ bne pen
+
+ /*
+ * we've been released from the holding pen: secondary_stack
+ * should now contain the SVC stack for this core
+ */
+ b secondary_startup
+
+ .align
+1: .long .
+ .long pen_release
+
+ENTRY(sci_shark_enter_lowpower)
+ stmfd sp!, {r4-r12, lr}
+
+ mrc p15, 0, r1, c1, c0, 0
+ bic r0, r1, #(1 << 2) @disable d-cache
+ mcr p15, 0, r0, c1, c0, 0
+
+ bl v7_flush_dcache_louis
+
+ clrex
+
+ mrc p15, 0, r1, c1, c0, 1
+ bic r1, r1, #(1 << 6)
+ mcr p15, 0, r1, c1, c0, 1
+
+ isb
+ dsb
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+ wfi
+
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+ ldmfd sp!, {r4-r12, pc}
+ENDPROC(sci_shark_enter_lowpower)
diff --git a/drivers/platform/sprd/hotplug.c b/drivers/platform/sprd/hotplug.c
new file mode 100644
index 00000000..cf176183
--- /dev/null
+++ b/drivers/platform/sprd/hotplug.c
@@ -0,0 +1,167 @@
+/* linux arch/arm/mach-sc8830/hotplug.c
+ *
+ * Cloned from linux/arch/arm/mach-realview/hotplug.c
+ *
+ * Copyright (C) 2002 ARM Ltd.
+ * All Rights Reserved
+ *
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+
+#include <soc/sprd/hardware.h>
+#include <asm/cacheflush.h>
+#include <asm/cp15.h>
+#include <asm/smp_plat.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/sci_glb_regs.h>
+
+extern volatile int pen_release;
+#ifdef CONFIG_ARCH_SCX35
+int powerdown_cpus(int cpu);
+int sci_shark_enter_lowpower(void);
+#endif
+static inline void cpu_enter_lowpower(void)
+{
+ unsigned int v;
+
+ flush_cache_all();
+ asm volatile(
+ " mcr p15, 0, %1, c7, c5, 0\n"
+ " mcr p15, 0, %1, c7, c10, 4\n"
+ /*
+ * Turn off coherency
+ */
+ " mrc p15, 0, %0, c1, c0, 1\n"
+ " bic %0, %0, #0x20\n"
+ " mcr p15, 0, %0, c1, c0, 1\n"
+ " mrc p15, 0, %0, c1, c0, 0\n"
+ " bic %0, %0, %2\n"
+ " mcr p15, 0, %0, c1, c0, 0\n"
+ : "=&r" (v)
+ : "r" (0), "Ir" (CR_C)
+ : "cc");
+}
+
+static inline void cpu_leave_lowpower(void)
+{
+ unsigned int v;
+
+ asm volatile( "mrc p15, 0, %0, c1, c0, 0\n"
+ " orr %0, %0, %1\n"
+ " mcr p15, 0, %0, c1, c0, 0\n"
+ " mrc p15, 0, %0, c1, c0, 1\n"
+ " orr %0, %0, #0x20\n"
+ " mcr p15, 0, %0, c1, c0, 1\n"
+ : "=&r" (v)
+ : "Ir" (CR_C)
+ : "cc");
+}
+
+extern unsigned int sprd_boot_magnum;
+
+static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
+{
+ unsigned int val = sprd_boot_magnum;
+
+ /*
+ * there is no power-control hardware on this platform, so all
+ * we can do is put the core into WFI; this is safe as the calling
+ * code will have already disabled interrupts
+ */
+
+ val |= (cpu_logical_map(cpu) & 0x0000000f);
+ for (;;) {
+ /*
+ * here's the WFI
+ */
+ asm(".word 0xe320f003\n"
+ :
+ :
+ : "memory", "cc");
+
+ if (pen_release == val) {
+ /*
+ * OK, proper wakeup, we're done
+ */
+ pr_info("platform_do_lowpower %x %x\n",pen_release,val);
+ break;
+ }
+
+ /*
+ * Getting here, means that we have come out of WFI without
+ * having been woken up - this shouldn't happen
+ *
+ * Just note it happening - when we're woken, we can report
+ * its occurrence.
+ */
+ (*spurious)++;
+ }
+}
+
+int sprd_cpu_kill(unsigned int cpu)
+{
+#ifdef CONFIG_ARCH_SCX35
+ int i = 0;
+ printk("!! %d platform_cpu_kill %d !!\n", smp_processor_id(), cpu);
+ while (i < 20) {
+ //check wfi?
+ if (sci_glb_read(REG_AP_AHB_CA7_STANDBY_STATUS, -1UL) & (1 << cpu_logical_map(cpu)))
+ {
+ powerdown_cpus(cpu);
+ break;
+ }
+ udelay(100);
+ i++;
+ }
+ printk("platform_cpu_kill finished i=%d !!\n", i);
+ return (i >= 20? 0 : 1);
+#endif
+ return 1;
+}
+
+/*
+ * platform-specific code to shutdown a CPU
+ *
+ * Called with IRQs disabled
+ */
+void __cpuinit sprd_cpu_die(unsigned int cpu)
+{
+ int spurious = 0;
+
+#ifdef CONFIG_ARCH_SCX35
+ sci_shark_enter_lowpower();
+ panic("shouldn't be here \n");
+#endif
+ /*
+ * we're ready for shutdown now, so do it
+ */
+ cpu_enter_lowpower();
+ platform_do_lowpower(cpu, &spurious);
+
+ /*
+ * bring this CPU back into the world of cache
+ * coherency, and then restore interrupts
+ */
+ cpu_leave_lowpower();
+
+ if (spurious)
+ pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
+}
+
+int sprd_cpu_disable(unsigned int cpu)
+{
+ /*
+ * we don't allow CPU 0 to be shutdown (it is still too special
+ * e.g. clock tick interrupts)
+ */
+ return cpu == 0 ? -EPERM : 0;
+}
diff --git a/drivers/platform/sprd/io-sc8830.c b/drivers/platform/sprd/io-sc8830.c
new file mode 100644
index 00000000..a512c892
--- /dev/null
+++ b/drivers/platform/sprd/io-sc8830.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ * Author: steve.zhan <steve.zhan@spreadtrum.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/mach/map.h>
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/arch_misc.h>
+
+#define SPRD_DEVICE(name) { \
+ .virtual = SPRD_##name##_BASE, \
+ .pfn = __phys_to_pfn(SPRD_##name##_PHYS),\
+ .length = SPRD_##name##_SIZE, \
+ .type = MT_DEVICE_NONSHARED, \
+ }
+#define SPRD_IRAM(name) { \
+ .virtual = SPRD_##name##_BASE, \
+ .pfn = __phys_to_pfn(SPRD_##name##_PHYS),\
+ .length = SPRD_##name##_SIZE, \
+ .type = MT_MEMORY, \
+ }
+
+static struct map_desc sprd_io_desc[] __initdata = {
+ SPRD_DEVICE(UART0),
+ SPRD_DEVICE(UART1),
+ SPRD_DEVICE(UART2),
+ SPRD_DEVICE(UART3),
+ SPRD_DEVICE(UART4),
+#ifdef CONFIG_ARCH_SCX20L
+ SPRD_DEVICE(ARM7AHBRF),
+#endif
+
+ SPRD_IRAM(IRAM0),
+ SPRD_IRAM(IRAM0H),
+#if defined(CONFIG_ARCH_SCX30G)
+ SPRD_IRAM(IRAM1),
+ SPRD_DEVICE(LPDDR2_PHY),
+#else
+ SPRD_DEVICE(IRAM1),
+#endif
+#ifndef CONFIG_ARCH_SCX35L
+ SPRD_DEVICE(IRAM2),
+#endif
+ SPRD_DEVICE(PWM),
+ SPRD_DEVICE(LPDDR2),
+};
+
+void __init sci_map_io(void)
+{
+ iotable_init(sprd_io_desc, ARRAY_SIZE(sprd_io_desc));
+}
+
diff --git a/drivers/platform/sprd/iomap.c b/drivers/platform/sprd/iomap.c
new file mode 100644
index 00000000..7a912fea
--- /dev/null
+++ b/drivers/platform/sprd/iomap.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications Inc.
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <asm/io.h>
+#include <soc/sprd/sci_glb_regs.h>
+
+struct iotable_sprd io_addr_sprd;
+EXPORT_SYMBOL_GPL(io_addr_sprd);
+
+#define ADD_SPRD_DEVICE_BY_COMPAT(compat, id) \
+do { \
+ np = of_find_compatible_node(NULL, NULL, compat);\
+ if (of_can_translate_address(np)) { \
+ of_address_to_resource(np, 0, &res); \
+ io_addr_sprd.id.paddr = res.start; \
+ io_addr_sprd.id.length = \
+ resource_size(&res); \
+ io_addr_sprd.id.vaddr = base + res.start;\
+ pr_debug("sprd io map: phys=%16lx virt=%16lx size=%16lx\n", \
+ io_addr_sprd.id.paddr, io_addr_sprd.id.vaddr, io_addr_sprd.id.length);\
+ } \
+} while (0)
+
+#define ADD_SPRD_DEVICE_BY_NAME(name, id) \
+do { \
+ np = of_find_node_by_name(NULL, name); \
+ if (of_can_translate_address(np)) { \
+ of_address_to_resource(np, 0, &res); \
+ io_addr_sprd.id.paddr = res.start; \
+ io_addr_sprd.id.length = \
+ resource_size(&res); \
+ io_addr_sprd.id.vaddr = base + res.start;\
+ pr_debug("sprd io map: phys=%16lx virt=%16lx size=%16lx\n", \
+ io_addr_sprd.id.paddr, io_addr_sprd.id.vaddr, io_addr_sprd.id.length);\
+ } \
+} while (0)
+
+static int sprd_create_iomap(void)
+{
+ struct device_node *np;
+ struct resource res;
+ unsigned long base;
+
+ base = (unsigned long) ioremap_nocache(0, SZ_2G);
+ if (!base)
+ panic("%s: unable to alloc such memory!", __func__);
+ io_addr_sprd.base = base;
+ pr_debug("sprd io map base address: %016x\n", base);
+
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,ahb", AHB);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,apbckg", APBCKG);
+ //ADD_SPRD_DEVICE_BY_COMPAT("sprd,hwlock0", HWLOCK0);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,pub_apb", PUB);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,aon_apb", AONAPB);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,pmu_apb", PMU);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,mm_ahb", MMAHB);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,mm_clk", MMCKG);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,ap_apb", APBREG);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,dma0", DMA0);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,gpu_apb", GPUAPB);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,ap_ckg", APCKG);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,aon_dma", AONDMA);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,pwm", PWM);
+ ADD_SPRD_DEVICE_BY_NAME("hwspinlock0", HWSPINLOCK0);
+ ADD_SPRD_DEVICE_BY_NAME("hwspinlock1", HWSPINLOCK1);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,intc2", INTC2);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,aonckg", AONCKG);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,core", CORE);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,gpuckg", GPUCKG);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,int", INT);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,intc0", INTC0);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,intc1", INTC1);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,intc3", INTC3);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,uidefuse", UIDEFUSE);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,isp", ISP);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,csi2", CSI2);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,d-eic-gpio", EIC);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,ipi", IPI);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,dcam", DCAM);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,syscnt", SYSCNT);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,pin", PIN);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,d-gpio-gpio", GPIO);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,codecahb", CODECAHB);
+#endif
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,lpddr2", LPDDR2);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,uart0", UART0);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,uart1", UART1);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,uart2", UART2);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,uart3", UART3);
+ ADD_SPRD_DEVICE_BY_COMPAT("sprd,uart4", UART4);
+ return 0;
+}
+
+early_initcall(sprd_create_iomap);
diff --git a/drivers/platform/sprd/irq.c b/drivers/platform/sprd/irq.c
new file mode 100644
index 00000000..4a3db24c
--- /dev/null
+++ b/drivers/platform/sprd/irq.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * Contact: steve.zhan <steve.zhan@spreadtrum.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+
+#include <linux/irqchip/arm-gic.h>
+
+#include <soc/sprd/hardware.h>
+//#include <mach/irqs.h>
+#include <soc/sprd/irqs.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/arch_misc.h>
+//#include <asm/fiq.h>
+#include <soc/sprd/adi.h>
+#include <linux/of.h>
+#include <linux/irqchip.h>
+
+#include <soc/sprd/sci_glb_regs.h>
+
+/* general interrupt registers */
+#define INTC_IRQ_MSKSTS (0x0000)
+#define INTC_IRQ_RAW (0x0004)
+#define INTC_IRQ_EN (0x0008)
+#define INTC_IRQ_DIS (0x000C)
+#define INTC_IRQ_SOFT (0x0010)
+#define INTC_FIQ_STS (0x0020)
+#define INTC_FIQ_EN (0x0028)
+#define INTC_FIQ_DIS (0x002C)
+
+struct intc {
+ u32 intc_reg_base;
+ u32 min_int_num;/*global logical number max value in intc*/
+ u32 max_min_num;/*global logical number min value in intc */
+ u32 min_bit_offset;/*It is equal to "min_int_num - actual bit offset"*/
+};
+
+struct intc_mux_irq {/*multi irqs mapping to one bit*/
+ u32 irq_number;
+ u32 intc_base;
+ u32 bits;
+ u32 is_unmask;
+};
+
+#if defined(CONFIG_ARCH_SCX35)
+/*_intc and mux_irq is separately, we just simply enable/disable it at the same time
+ When system deepsleep, only mux_irq(AON) can wakeup system(intc0,1,2,3,gic,armcore)
+ intc0/1/2/3 will lose if system in deepsleep, _mux can wakeup. intc0/1/2/3 need SW retention.
+ gic clock will stop if all of core has been wfi.intc0/1/2/3 can wakeup.
+*/
+#define UNINITIALIZED 0xdeadbeaf
+static struct intc _intc[] = {
+ {UNINITIALIZED, 2, 31, 0},
+ {UNINITIALIZED, 34, 63, 32},
+ {UNINITIALIZED, 66, 95, 64},
+ {UNINITIALIZED, 98, 124, 96},
+};
+#if defined(CONFIG_ARCH_SCX35L)
+static struct intc_mux_irq _mux[] = {
+ {30,UNINITIALIZED,2,0},{31,UNINITIALIZED,2,0},{28,UNINITIALIZED,2,0},
+ {121,UNINITIALIZED,2,0},{120,UNINITIALIZED,2,0},{119,UNINITIALIZED,2,0},
+ {118,UNINITIALIZED,2,0},{29,UNINITIALIZED,2,0},
+ {21,UNINITIALIZED,3,0},{22,UNINITIALIZED,3,0},{23,UNINITIALIZED,3,0},{24,UNINITIALIZED,3,0},
+ {35,UNINITIALIZED,4,0},{36,UNINITIALIZED,4,0},{38,UNINITIALIZED,4,0},{25,UNINITIALIZED,4,0},
+ {20,UNINITIALIZED,4,0},{34,UNINITIALIZED,4,0},/* missing lvds_trx, mdar */
+ {41,UNINITIALIZED,5,0},{40,UNINITIALIZED,5,0},{42,UNINITIALIZED,5,0},{44,UNINITIALIZED,5,0},
+ {45,UNINITIALIZED,5,0},{43,UNINITIALIZED,5,0},
+ {39,UNINITIALIZED,6,0},
+ {84,UNINITIALIZED,7,0},{83,UNINITIALIZED,7,0},
+ {123,UNINITIALIZED,8,0},{124,UNINITIALIZED,8,0},
+ {122,UNINITIALIZED,9,0},{26,UNINITIALIZED,9,0},
+ {86,UNINITIALIZED,10,0},
+ /* bit11: missing mbox_tar_arm7 */
+ {69,UNINITIALIZED,12,0},
+ {37,UNINITIALIZED,31,0},
+};
+#else
+static struct intc_mux_irq _mux[] = {
+ {30, UNINITIALIZED, 2, 0},
+ {31, UNINITIALIZED, 2, 0},
+ {28, UNINITIALIZED, 2, 0},
+ {121,UNINITIALIZED, 2, 0},
+ {120,UNINITIALIZED, 2, 0},
+ {119,UNINITIALIZED, 2, 0},
+ {118,UNINITIALIZED, 2, 0},
+ {29, UNINITIALIZED, 2, 0},
+ {21, UNINITIALIZED, 3, 0},
+ {22, UNINITIALIZED, 3, 0},
+ {23, UNINITIALIZED, 3, 0},
+ {24, UNINITIALIZED, 3, 0},
+ {35, UNINITIALIZED, 4, 0},
+ {36, UNINITIALIZED, 4, 0},
+ {38, UNINITIALIZED, 4, 0},
+ {25, UNINITIALIZED, 4, 0},
+ {27, UNINITIALIZED, 4, 0},
+ {20, UNINITIALIZED, 4, 0},
+ {34, UNINITIALIZED, 4, 0},
+ {41, UNINITIALIZED, 5, 0},
+ {40, UNINITIALIZED, 5, 0},
+ {42, UNINITIALIZED, 5, 0},
+ {44, UNINITIALIZED, 5, 0},
+ {45, UNINITIALIZED, 5, 0},
+ {43, UNINITIALIZED, 5, 0},
+ {39, UNINITIALIZED, 6, 0},
+ {85, UNINITIALIZED, 7, 0},
+ {84, UNINITIALIZED, 7, 0},
+ {83, UNINITIALIZED, 7, 0},
+ {67, UNINITIALIZED, 8, 0},
+ {68, UNINITIALIZED, 8, 0},
+ {69, UNINITIALIZED, 8, 0},
+ {70, UNINITIALIZED, 9, 0},
+ {71, UNINITIALIZED, 9, 0},
+ {72, UNINITIALIZED, 9, 0},
+ {73, UNINITIALIZED,10, 0},
+ {74, UNINITIALIZED,10, 0},
+ {123,UNINITIALIZED,11, 0},
+ {124,UNINITIALIZED,11, 0},
+ {26, UNINITIALIZED,12, 0},
+ {122,UNINITIALIZED,12, 0},
+ {86, UNINITIALIZED,13, 0},
+ {37, UNINITIALIZED,14, 0},
+};
+#endif
+
+#define LEGACY_FIQ_BIT (32)
+#define LEGACY_IRQ_BIT (29)
+
+static __init void __irq_init(void)
+{
+ if (soc_is_scx35_v0())
+ sci_glb_clr(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG,BIT_CA7_CORE_AUTO_GATE_EN);
+
+ /*enable all intc*/
+ sci_glb_set(REG_AP_APB_APB_EB, BIT_INTC0_EB | BIT_INTC1_EB |
+ BIT_INTC2_EB | BIT_INTC3_EB);
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT_INTC_EB);
+
+ /*disable default for startup*/
+ __raw_writel(~0, (void *)(SPRD_INTC0_BASE + INTC_IRQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC0_BASE + INTC_FIQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC1_BASE + INTC_IRQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC1_BASE + INTC_FIQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC2_BASE + INTC_IRQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC2_BASE + INTC_FIQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC3_BASE + INTC_IRQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INTC3_BASE + INTC_FIQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INT_BASE + INTC_IRQ_DIS));
+ __raw_writel(~0, (void *)(SPRD_INT_BASE + INTC_FIQ_DIS));
+}
+
+#else
+
+static const struct intc _intc[] = {
+ {SPRD_INTC0_BASE, 0, 31, 0},
+ {SPRD_INTC1_BASE, 32, 61, 32},
+};
+
+static struct intc_mux_irq _mux[] = {};
+
+#define LEGACY_FIQ_BIT (31)
+#define LEGACY_IRQ_BIT (28)
+
+static __init void __irq_init(void)
+{
+ /*
+ * gic clock will be stopped after 2 cores enter standby in the same time,
+ * dsp assert if IRQ_DSP0_INT and IRQ_DSP1_INT are disabled. so enable IRQ_DSP0_INT
+ * and IRQ_DSP1_INT in INTC0 here.
+ */
+ u32 val = __raw_readl(SPRD_INTC0_BASE + INTC_IRQ_EN);
+ val |= (SCI_INTC_IRQ_BIT(IRQ_DSP0_INT) | SCI_INTC_IRQ_BIT(IRQ_DSP1_INT) | SCI_INTC_IRQ_BIT(IRQ_EPT_INT));
+ val |= (SCI_INTC_IRQ_BIT(IRQ_SIM0_INT) | SCI_INTC_IRQ_BIT(IRQ_SIM1_INT));
+ val |= (SCI_INTC_IRQ_BIT(IRQ_TIMER0_INT));
+ __raw_writel(val, SPRD_INTC0_BASE + INTC_IRQ_EN);
+}
+#endif
+
+static inline int __irq_mux_irq_find(u32 irq, u32 *base, u32 *bit, int *p_index)
+{
+ int s = ARRAY_SIZE(_mux);
+ while (s--)
+ if (_mux[s].irq_number == irq) {
+ *base = _mux[s].intc_base;
+ *bit = _mux[s].bits;
+ *p_index = s;
+ return 0;
+ }
+ return -ENXIO;
+}
+
+static inline int __irq_find_base(u32 irq, u32 *base, u32 *bit)
+{
+ int s = ARRAY_SIZE(_intc);
+ while (s--)
+ if (irq >= _intc[s].min_int_num && irq <= _intc[s].max_min_num) {
+ *base = _intc[s].intc_reg_base;
+ *bit = irq - _intc[s].min_bit_offset;
+ return 0;
+ }
+ return -ENXIO;
+}
+
+static inline void __mux_irq(u32 irq, u32 offset, u32 is_unmask)
+{
+ u32 base, bit, s;
+ int index = 0;
+ int dont_mask = 0;
+ if (!__irq_mux_irq_find(irq, &base, &bit, &index)) {
+ _mux[index].is_unmask = !!is_unmask;
+
+ if (is_unmask) {/*if any one need unmask(wakeup source), let the irq mux bit enable*/
+ __raw_writel(1 << bit, (void *)(base + offset));
+ } else {/*maybe mask*/
+ s = ARRAY_SIZE(_mux);
+ while (s--) {
+ if (_mux[s].is_unmask) {
+ dont_mask = 1;
+ break;
+ }
+ }
+ if (!dont_mask)
+ __raw_writel(1 << bit, (void *)(base + offset));
+ }
+ }
+}
+
+void sci_intc_mask(u32 __irq)
+{
+ unsigned int irq = SCI_GET_INTC_IRQ(__irq);
+ u32 base;
+ u32 bit;
+ u32 offset = INTC_IRQ_DIS;
+#ifdef CONFIG_SPRD_WATCHDOG_SYS_FIQ
+ if (unlikely(irq == (IRQ_CA7WDG_INT - IRQ_GIC_START)))
+ offset = INTC_FIQ_DIS;
+#endif
+ __mux_irq(irq, offset, 0);
+ if (!__irq_find_base(irq, &base, &bit))
+ __raw_writel(1 << bit, (void *)(base + offset));
+}
+EXPORT_SYMBOL(sci_intc_mask);
+
+void sci_intc_unmask(u32 __irq)
+{
+ unsigned int irq = SCI_GET_INTC_IRQ(__irq);
+ u32 base;
+ u32 bit;
+ u32 offset = INTC_IRQ_EN;
+#ifdef CONFIG_SPRD_WATCHDOG_SYS_FIQ
+ if (unlikely(irq == (IRQ_CA7WDG_INT - IRQ_GIC_START)))
+ offset = INTC_FIQ_EN;
+#endif
+ __mux_irq(irq, offset, 1);
+ if (!__irq_find_base(irq, &base, &bit))
+ __raw_writel(1 << bit, (void *)(base + offset));
+}
+
+EXPORT_SYMBOL(sci_intc_unmask);
+
+static void __irq_mask(struct irq_data *data)
+{
+ sci_intc_mask(data->irq);
+}
+
+static void __irq_unmask(struct irq_data *data)
+{
+ sci_intc_unmask(data->irq);
+}
+
+static int __set_wake(struct irq_data *d, unsigned int on)
+{
+ /*we don't add mask/unmask in this now, this is not mean mask interrupt*/
+ return 0;
+}
+
+extern int sprd_init_before_irq(void);
+void __init sci_init_irq(void)
+{
+ int i;
+ sprd_init_before_irq();
+ irqchip_init();
+
+ _intc[0].intc_reg_base = SPRD_INTC0_BASE;
+ _intc[1].intc_reg_base = SPRD_INTC1_BASE;
+ _intc[2].intc_reg_base = SPRD_INTC2_BASE;
+ _intc[3].intc_reg_base = SPRD_INTC3_BASE;
+
+ for (i = 0; i < ARRAY_SIZE(_mux); i++)
+ _mux[i].intc_base = SPRD_INT_BASE;
+
+ gic_arch_extn.irq_mask = __irq_mask;
+ gic_arch_extn.irq_unmask = __irq_unmask;
+ gic_arch_extn.irq_set_wake = __set_wake;
+
+
+ /*disable legacy interrupt*/
+#if (LEGACY_FIQ_BIT < 32)
+ __raw_writel(1<<LEGACY_FIQ_BIT, (void *)(CORE_GIC_DIS_VA + GIC_DIST_ENABLE_CLEAR));
+#endif
+ __raw_writel(1<<LEGACY_IRQ_BIT, (void *)(CORE_GIC_DIS_VA + GIC_DIST_ENABLE_CLEAR));
+
+ __irq_init();
+}
diff --git a/drivers/platform/sprd/mailbox.c b/drivers/platform/sprd/mailbox.c
new file mode 100644
index 00000000..c2b1445a
--- /dev/null
+++ b/drivers/platform/sprd/mailbox.c
@@ -0,0 +1,205 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/resource.h>
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/irqs.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/mailbox.h>
+
+unsigned long sprd_mailbox_base;
+#define REGS_SEND_MBOX_BASE sprd_mailbox_base
+#define REGS_RECV_MBOX_BASE (sprd_mailbox_base + 0x8000)
+
+/*reg offset*/
+#define MBOX_ID 0x00
+#define MBOX_MSG_L 0x04
+#define MBOX_MSG_H 0x08
+#define MBOX_TRI 0x0c
+#define MBOX_FIFO_RST 0x10
+#define MBOX_FIFO_STS 0x14
+#define MBOX_IRQ_STS 0x18
+#define MBOX_IRQ_MSK 0x1c
+#define MBOX_LOCK 0x20
+#define FIFO_BLOCK_STS (0x20)
+#define MBOX_UNLOCK_KEY 0x5a5a5a5a
+
+struct mbox_chn {
+ spinlock_t mbox_lock;
+ irq_handler_t mbox_recv_irq_handler;
+ void *mbox_priv_data;
+};
+
+static struct mbox_chn mbox_chns[MBOX_NR];
+static irqreturn_t mbox_src_irqhandle(int irq_num, void *dev)
+{
+ u8 target_id;
+ u32 irq_sts;
+ u32 reg_val;
+
+ reg_val = __raw_readl(REGS_SEND_MBOX_BASE + MBOX_ID);
+ irq_sts = __raw_readl(REGS_SEND_MBOX_BASE + MBOX_FIFO_STS);
+ irq_sts = irq_sts & 0x3f;
+ if(irq_sts & FIFO_BLOCK_STS )
+ panic("target 0x%x mailbox blocked", reg_val);
+}
+
+static irqreturn_t mbox_recv_irqhandle(int irq_num, void *dev)
+{
+ u8 target_id;
+ u32 irq_sts;
+ u32 reg_val;
+ void *priv_data;
+ unsigned long flags;
+
+ irq_sts = __raw_readl((void __iomem *)(REGS_RECV_MBOX_BASE + MBOX_FIFO_STS));
+ irq_sts = irq_sts & 0x0000ff00;
+ __raw_writel(irq_sts, (void __iomem *)(REGS_RECV_MBOX_BASE + MBOX_IRQ_STS));
+ irq_sts = (irq_sts) >> 8;
+
+ while (irq_sts) {
+ target_id = __ffs(irq_sts);
+ if (target_id >= MBOX_NR){
+ break;
+ }
+ irq_sts &= irq_sts - 1;
+ spin_lock_irqsave(&mbox_chns[target_id].mbox_lock,flags);
+ if (mbox_chns[target_id].mbox_recv_irq_handler) {
+ priv_data = mbox_chns[target_id].mbox_priv_data;
+ /*we will use tasklet later*/
+ mbox_chns[target_id].mbox_recv_irq_handler(irq_num, priv_data);
+ }
+ spin_unlock_irqrestore(&mbox_chns[target_id].mbox_lock,flags);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int mbox_register_irq_handle(u8 target_id, irq_handler_t irq_handler, void *priv_data)
+{
+ u32 reg_val;
+
+ if (target_id >= MBOX_NR || mbox_chns[target_id].mbox_recv_irq_handler) {
+ return -EINVAL;
+ }
+
+ mbox_chns[target_id].mbox_recv_irq_handler = irq_handler;
+ mbox_chns[target_id].mbox_priv_data = priv_data;
+
+ /*enable the irq*/
+ reg_val = __raw_readl((void __iomem *)(REGS_RECV_MBOX_BASE + MBOX_IRQ_MSK));
+ reg_val &= ~((0x1 << target_id) << 8);
+ __raw_writel(reg_val, (void __iomem *)(REGS_RECV_MBOX_BASE + MBOX_IRQ_MSK));
+
+ return 0;
+}
+
+int mbox_unregister_irq_handle(u8 target_id)
+{
+ u32 reg_val;
+
+ if (target_id >= MBOX_NR || !mbox_chns[target_id].mbox_recv_irq_handler) {
+ return -EINVAL;
+ }
+
+ spin_lock(&mbox_chns[target_id].mbox_lock);
+
+ mbox_chns[target_id].mbox_recv_irq_handler = NULL;
+
+ /*disable the irq*/
+ reg_val = __raw_readl((void __iomem *)(REGS_RECV_MBOX_BASE + MBOX_IRQ_MSK));
+ reg_val |= (0x1 << target_id) << 8;
+ __raw_writel(reg_val, (void __iomem *)(REGS_RECV_MBOX_BASE + MBOX_IRQ_MSK));
+ reg_val = __raw_readl((void __iomem *)(REGS_RECV_MBOX_BASE + MBOX_IRQ_STS));
+ reg_val |= (0x1 << target_id) << 8;
+ __raw_writel(reg_val, (void __iomem *)(REGS_RECV_MBOX_BASE + MBOX_IRQ_STS));
+
+ /*clean the irq status*/
+ spin_unlock(&mbox_chns[target_id].mbox_lock);
+
+ return 0;
+}
+
+int mbox_raw_sent(u8 target_id, u64 msg)
+{
+ /*lock the mbox, fix me*/
+ while (!(__raw_readl((void __iomem *)(REGS_SEND_MBOX_BASE + MBOX_LOCK)) & 0x1));
+#if 0
+ __raw_writeq(msg, REGS_SEND_MBOX_BASE + MBOX_MSG_L );
+#endif
+ __raw_writel(target_id, (void __iomem *)(REGS_SEND_MBOX_BASE + MBOX_ID));
+ __raw_writel(0x1, (void __iomem *)(REGS_SEND_MBOX_BASE + MBOX_TRI));
+ __raw_writel(MBOX_UNLOCK_KEY, (void __iomem *)(REGS_SEND_MBOX_BASE + MBOX_LOCK));
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(mbox_register_irq_handle);
+EXPORT_SYMBOL_GPL(mbox_unregister_irq_handle);
+EXPORT_SYMBOL_GPL(mbox_raw_sent);
+
+static int __init mbox_init(void)
+{
+ int i;
+ int ret;
+ struct resource res;
+ struct device_node *np;
+ int vmailbox_tag_irq = SCI_IRQ(69);
+ int vmailbox_src_irq = SCI_IRQ(68);
+
+ np = of_find_compatible_node(NULL, NULL, "sprd,mailbox");
+ if (of_can_translate_address(np)) {
+ of_address_to_resource(np, 0, &res);
+ sprd_mailbox_base = ioremap_nocache(res.start,
+ resource_size(&res));
+ }
+ if (!sprd_mailbox_base)
+ panic("can't create iomap for mailbox!");
+
+ /*glb enable and rst*/
+ sci_glb_set(REG_AON_APB_APB_EB1, BIT_MBOX_EB);
+
+ for (i = 0; i < 0x100; i++);
+
+ __raw_writel(0x1, (void __iomem *)(REGS_SEND_MBOX_BASE + MBOX_FIFO_RST));
+ __raw_writel(0x1, (void __iomem *)(REGS_RECV_MBOX_BASE + MBOX_FIFO_RST));
+
+ for (i = 0; i < 0x100; i++);
+
+ __raw_writel(0x0, (void __iomem *)(REGS_SEND_MBOX_BASE + MBOX_FIFO_RST));
+ __raw_writel(~FIFO_BLOCK_STS, REGS_SEND_MBOX_BASE + MBOX_IRQ_MSK);
+ ret = request_irq(vmailbox_src_irq, mbox_src_irqhandle, IRQF_NO_SUSPEND, "sprd-mailbox_source", NULL);
+ if (ret) {
+ pr_err("mbox request source irq:%d failed\n", vmailbox_src_irq);
+ return -1; /*fixme*/
+ }
+ enable_irq_wake(vmailbox_src_irq);
+ /*recv use the irq_type_1 default*/
+ __raw_writel(0x10000, (void __iomem *)(REGS_RECV_MBOX_BASE + MBOX_FIFO_RST));
+ /* FIXME: irq num */
+ ret = request_irq(vmailbox_tag_irq, mbox_recv_irqhandle, IRQF_NO_SUSPEND, "sprd-mailbox_target", NULL);
+ if (ret) {
+ pr_err("mbox request target irq:%d failed\n",vmailbox_tag_irq);
+ return -1; /*fixme*/
+ }
+ enable_irq_wake(vmailbox_tag_irq);
+ /*disable recv irq*/
+ __raw_writel(0xff00, (void __iomem *)(REGS_RECV_MBOX_BASE + MBOX_IRQ_MSK));
+
+ for (i = 0; i < MBOX_NR; i++) {
+ spin_lock_init(&mbox_chns[i].mbox_lock);
+ }
+
+ return 0;
+}
+
+arch_initcall(mbox_init);
diff --git a/drivers/platform/sprd/pin_switch.c b/drivers/platform/sprd/pin_switch.c
new file mode 100755
index 00000000..58591d3b
--- /dev/null
+++ b/drivers/platform/sprd/pin_switch.c
@@ -0,0 +1,559 @@
+// #define DEBUG
+// #define pr_fmt(fmt) "---sprd pinswitch---"fmt
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/uaccess.h>
+#include <linux/printk.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/arch_lock.h>
+#include <linux/of.h>
+#include <soc/sprd/pin_switch.h>
+#include <linux/regulator/consumer.h>
+
+#ifdef CONFIG_PROC_FS
+
+#undef SPRD_PIN_BASE
+unsigned long SPRD_PIN_BASE = 0;
+struct sci_pin_switch {
+ const char *dirname;
+ const char *filename;
+ u32 reg;
+ u32 bit_offset;
+ u32 bit_width;
+ u32 func;
+};
+
+static struct sci_pin_switch sci_pin_switch_array[] = {
+#if !(defined(CONFIG_ARCH_SCX15)||defined(CONFIG_ARCH_SCX35L))
+ {"", "vbc_dac_iislrck_pin_in_sel", 0x4, 24, 1, 0},
+ {"", "vbc_dac_iisclk_pin_in_sel", 0x4, 23, 1, 0},
+ {"", "vbc_adc_iislrck_pin_in_sel", 0x4, 22, 1, 0},
+ {"", "vbc_adc_iisdi_pin_in_sel", 0x4, 21, 1, 0},
+ {"", "vbc_adc_iisclk_pin_in_sel", 0x4, 20, 1, 0},
+ {"", "iis3mck_pin_in_sel", 0x4, 19, 1, 0},
+ {"", "iis3lrck_pin_in_sel", 0x4, 18, 1, 0},
+ {"", "iis3di_pin_in_sel", 0x4, 17, 1, 0},
+ {"", "iis3clk_pin_in_sel", 0x4, 16, 1, 0},
+ {"", "iis2mck_pin_in_sel", 0x4, 15, 1, 0},
+ {"", "iis2lrck_pin_in_sel", 0x4, 14, 1, 0},
+ {"", "iis2di_pin_in_sel", 0x4, 13, 1, 0},
+ {"", "iis2clk_pin_in_sel", 0x4, 12, 1, 0},
+#if defined(CONFIG_ARCH_SCX30G) /*tshark add */
+ {"", "bt_iis_con_eb", 0xc, 30, 1, 0},
+ {"", "pad_oe_iis3_mck", 0xc, 27, 1, 0},
+ {"", "pad_oe_iis2_mck", 0xc, 26, 1, 0},
+ {"", "pad_oe_iis1_mck", 0xc, 25, 1, 0},
+ {"", "pad_oe_iis0_mck", 0xc, 24, 1, 0},
+#endif
+#endif
+ {"", "iis23_loop_sel", 0xc, 5, 1, 0},
+ {"", "iis13_loop_sel", 0xc, 4, 1, 0},
+ {"", "iis12_loop_sel", 0xc, 3, 1, 0},
+ {"", "iis03_loop_sel", 0xc, 2, 1, 0},
+ {"", "iis02_loop_sel", 0xc, 1, 1, 0},
+ {"", "iis01_loop_sel", 0xc, 0, 1, 0},
+};
+
+
+#if defined(CONFIG_ARCH_SCX35L)
+static struct sci_pin_switch iis_0_array[] = {
+ {"iis0_sys_sel", "ap_iis0", 0xc, 6, 3, 0},
+ {"iis0_sys_sel", "cp0_iis0", 0xc, 6, 3, 1},
+ {"iis0_sys_sel", "cp1_iis0", 0xc, 6, 3, 2},
+ {"iis0_sys_sel", "vbc_iis0", 0xc, 6, 3, 3},
+ {"iis0_sys_sel", "vbc_iis1", 0xc, 6, 3, 4},
+};
+
+static struct sci_pin_switch iis_1_array[] = {
+ {"iis1_sys_sel", "ap_iis1", 0xc, 9, 3, 0},
+ {"iis1_sys_sel", "cp0_iis1", 0xc, 9, 3, 1},
+ {"iis1_sys_sel", "cp1_iis1", 0xc, 9, 3, 2},
+ {"iis1_sys_sel", "vbc_iis0", 0xc, 9, 3, 3},
+ {"iis1_sys_sel", "vbc_iis1", 0xc, 9, 3, 4},
+};
+
+static struct sci_pin_switch iis_2_array[] = {
+ {"iis2_sys_sel", "ap_iis2", 0xc, 12, 3, 0},
+ {"iis2_sys_sel", "cp0_iis2", 0xc, 12, 3, 1},
+ {"iis2_sys_sel", "cp1_iis2", 0xc, 12, 3, 2},
+ {"iis2_sys_sel", "vbc_iis0", 0xc, 12, 3, 3},
+ {"iis2_sys_sel", "vbc_iis1", 0xc, 12, 3, 4},
+};
+
+static struct sci_pin_switch iis_3_array[] = {
+ {"iis3_sys_sel", "ap_iis3", 0xc, 15, 3, 0},
+ {"iis3_sys_sel", "cp0_iis3", 0xc, 15, 3, 1},
+ {"iis3_sys_sel", "cp1_iis3", 0xc, 15, 3, 2},
+ {"iis3_sys_sel", "vbc_iis0", 0xc, 15, 3, 3},
+ {"iis3_sys_sel", "vbc_iis1", 0xc, 15, 3, 4},
+};
+#else
+static struct sci_pin_switch bt_iis_sys_sel_array[] = {
+ {"bt_iis_sys_sel", "cp0_iis0", 0x8, 28, 4, 0},
+ {"bt_iis_sys_sel", "cp0_iis1", 0x8, 28, 4, 1},
+ {"bt_iis_sys_sel", "cp0_iis2", 0x8, 28, 4, 2},
+ {"bt_iis_sys_sel", "cp0_iis3", 0x8, 28, 4, 3},
+ {"bt_iis_sys_sel", "cp1_iis0", 0x8, 28, 4, 4},
+ {"bt_iis_sys_sel", "cp1_iis1", 0x8, 28, 4, 5},
+ {"bt_iis_sys_sel", "cp1_iis2", 0x8, 28, 4, 6},
+ {"bt_iis_sys_sel", "cp1_iis3", 0x8, 28, 4, 7},
+ {"bt_iis_sys_sel", "ap_iis0", 0x8, 28, 4, 8},
+ {"bt_iis_sys_sel", "ap_iis1", 0x8, 28, 4, 9},
+ {"bt_iis_sys_sel", "ap_iis2", 0x8, 28, 4, 10},
+ {"bt_iis_sys_sel", "ap_iis3", 0x8, 28, 4, 11},
+ {"bt_iis_sys_sel", "vbc_iis", 0x8, 28, 4, 12},
+ {"bt_iis_sys_sel", "rsv_d", 0x8, 28, 4, 13},
+ {"bt_iis_sys_sel", "rsv_e", 0x8, 28, 4, 14},
+ {"bt_iis_sys_sel", "rsv_f", 0x8, 28, 4, 15},
+};
+
+static struct sci_pin_switch iis_0_array[] = {
+ {"iis0_sys_sel", "ap_iis0", 0xc, 6, 3, 0},
+ {"iis0_sys_sel", "cp0_iis0", 0xc, 6, 3, 1},
+ {"iis0_sys_sel", "cp1_iis0", 0xc, 6, 3, 2},
+ {"iis0_sys_sel", "cp2_iis0", 0xc, 6, 3, 3},
+ {"iis0_sys_sel", "vbc_iis0", 0xc, 6, 3, 4},
+};
+
+static struct sci_pin_switch iis_1_array[] = {
+ {"iis1_sys_sel", "ap_iis1", 0xc, 9, 3, 0},
+ {"iis1_sys_sel", "cp0_iis1", 0xc, 9, 3, 1},
+ {"iis1_sys_sel", "cp1_iis1", 0xc, 9, 3, 2},
+ {"iis1_sys_sel", "cp2_iis1", 0xc, 9, 3, 3},
+};
+
+#if !defined(CONFIG_ARCH_SCX15)
+static struct sci_pin_switch iis_2_array[] = {
+ {"iis2_sys_sel", "ap_iis2", 0xc, 12, 3, 0},
+ {"iis2_sys_sel", "cp0_iis2", 0xc, 12, 3, 1},
+ {"iis2_sys_sel", "cp1_iis2", 0xc, 12, 3, 2},
+ {"iis2_sys_sel", "cp2_iis2", 0xc, 12, 3, 3},
+};
+
+static struct sci_pin_switch iis_3_array[] = {
+ {"iis3_sys_sel", "ap_iis3", 0xc, 15, 3, 0},
+ {"iis3_sys_sel", "cp0_iis3", 0xc, 15, 3, 1},
+ {"iis3_sys_sel", "cp1_iis3", 0xc, 15, 3, 2},
+ {"iis3_sys_sel", "cp2_iis3", 0xc, 15, 3, 3},
+};
+#endif
+#endif
+
+#if defined(CONFIG_PIN_POWER_DOMAIN_SWITCH)
+struct pin_reg_desc {
+ struct sprd_pin_switch_platform_data *platform_data;
+ struct notifier_block nb;
+ struct regulator_bulk_data supplies;
+} pin_reg_desc_array[PD_CNT];
+#endif
+
+static struct sci_pin_switch_dir {
+ struct sci_pin_switch *sci_pin_switch;
+ u32 array_size;
+} sci_pin_switch_dir_array[] = {
+#if !defined(CONFIG_ARCH_SCX35L)
+ {
+ bt_iis_sys_sel_array, ARRAY_SIZE(bt_iis_sys_sel_array)},
+#endif
+ {
+ iis_0_array, ARRAY_SIZE(iis_0_array)}, {
+ iis_1_array, ARRAY_SIZE(iis_1_array)},
+#if !defined(CONFIG_ARCH_SCX15)
+ {
+ iis_2_array, ARRAY_SIZE(iis_2_array)}, {
+ iis_3_array, ARRAY_SIZE(iis_3_array)},
+#endif
+};
+
+u32 pinmap_get(u32 offset)
+{
+ return readl(SPRD_PIN_BASE + offset);
+}
+
+EXPORT_SYMBOL(pinmap_get);
+int pinmap_set(u32 offset, u32 value)
+{
+ unsigned long flags;
+ __arch_default_lock(HWLOCK_GLB, &flags);
+ writel(value, SPRD_PIN_BASE + offset);
+ __arch_default_unlock(HWLOCK_GLB, &flags);
+ return 0;
+}
+
+EXPORT_SYMBOL(pinmap_set);
+
+/*
+* #define IIS_TO_AP (0)
+* #define IIS_TO_CP0 (1)
+* #define IIS_TO_CP1 (2)
+* #define IIS_TO_CP2 (3)
+* #define PIN_CTL_REG3 (SPRD_PIN_BASE + 0xc)
+*/
+static int read_write_pin_switch(int is_read, int v, struct sci_pin_switch *p)
+{
+ int val = 0;
+ u32 shift = p->bit_offset;
+ u32 mask = (1 << (p->bit_width)) - 1;
+ if ((shift > 31))
+ BUG_ON(1);
+ if (v > mask)
+ printk("v:0x%x overflow bitwidth:%ud, mask:0x%x it\n",
+ v, p->bit_width, mask);
+ val = pinmap_get(p->reg);
+ if (is_read) {
+ val >>= shift;
+ val &= mask;
+ return val;
+ } else {
+ val &= ~(mask << shift);
+ val |= (v & mask) << shift;
+ pinmap_set(p->reg, val);
+ }
+ return val;
+}
+
+static int pin_switch_proc_show(struct seq_file *m, void *v)
+{
+ int val = 0;
+ struct sci_pin_switch *p = (struct sci_pin_switch *)(m->private);
+ val = read_write_pin_switch(1, (int)val, p);
+ seq_printf(m, "%d\n", val);
+ return 0;
+}
+
+static int pin_switch_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pin_switch_proc_show, PDE_DATA(inode));
+}
+
+static ssize_t pin_switch_proc_write(struct file *file,
+ const char __user * buffer,
+ size_t count, loff_t * pos)
+{
+ long val = 0;
+ int ret = 0;
+ struct sci_pin_switch *p =
+ (struct sci_pin_switch *)(PDE_DATA(file_inode(file)));
+ ret = kstrtol_from_user(buffer, count, 0, &val);
+ if (ret) {
+ pr_err("input err\n");
+ return -EINVAL;
+ }
+ read_write_pin_switch(0, val, p);
+ return count;
+}
+
+static const struct file_operations pin_switch_fops = {
+ .open = pin_switch_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = pin_switch_proc_write,
+};
+
+static int pin_switch_dir_proc_show(struct seq_file *m, void *v)
+{
+ int val = 0;
+ int func_tmp = 0;
+ struct sci_pin_switch *p = (struct sci_pin_switch *)m->private;
+ func_tmp = read_write_pin_switch(1, val, p);
+ if (p->func == func_tmp)
+ val = 1;
+ else
+ val = 0;
+ seq_printf(m, "%d\n", val);
+ return 0;
+}
+
+static int pin_switch_dir_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pin_switch_dir_proc_show, PDE_DATA(inode));
+}
+
+static ssize_t pin_switch_dir_proc_write(struct file *file,
+ const char __user * buffer,
+ size_t count, loff_t * pos)
+{
+ int val = 0;
+ int ret = 0;
+ struct sci_pin_switch *p =
+ (struct sci_pin_switch *)(PDE_DATA(file_inode(file)));
+ ret = kstrtol_from_user(buffer, count, 0, &val);
+ if (ret) {
+ pr_err("input err\n");
+ return -EINVAL;
+ }
+ if (val == 1)
+ val = p->func;
+ else /*if val = 0 or other value, just ignore it */
+ return 0;
+ read_write_pin_switch(0, val, p);
+ return count;
+}
+
+static const struct file_operations pin_switch_dir_fops = {
+ .open = pin_switch_dir_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = pin_switch_dir_proc_write,
+};
+
+static struct proc_dir_entry *pin_switch_proc_base;
+static int __init pin_switch_proc_add(struct sci_pin_switch *pin_switch)
+{
+ struct proc_dir_entry *tmp_proc;
+ tmp_proc =
+ proc_create_data(pin_switch->filename, S_IRWXUGO,
+ pin_switch_proc_base, &pin_switch_fops,
+ pin_switch);
+ if (!tmp_proc)
+ return -ENOMEM;
+ return 0;
+}
+
+static int __init pin_switch_proc_add_dir(struct sci_pin_switch_dir
+ *pin_switch_dir)
+{
+ struct proc_dir_entry *tmp_proc_dir;
+ struct proc_dir_entry *tmp_proc;
+ int i;
+ if (pin_switch_dir->sci_pin_switch->dirname == NULL)
+ return EINVAL;
+ /* has dir name, first create parent dir */
+ tmp_proc_dir =
+ proc_mkdir(pin_switch_dir->sci_pin_switch->dirname,
+ pin_switch_proc_base);
+ if (!tmp_proc_dir)
+ return -ENOMEM;
+
+ for (i = 0; i < pin_switch_dir->array_size; ++i) {
+ if (pin_switch_dir->sci_pin_switch[i].filename == NULL)
+ return EINVAL;
+ tmp_proc =
+ proc_create_data(pin_switch_dir->sci_pin_switch[i].filename,
+ S_IRWXUGO, tmp_proc_dir,
+ &pin_switch_dir_fops,
+ &pin_switch_dir->sci_pin_switch[i]);
+ if (!tmp_proc)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#if defined(CONFIG_PIN_POWER_DOMAIN_SWITCH)
+static int sc271x_regulator_event(struct notifier_block *regu_nb,
+ unsigned long event, void *data)
+{
+ unsigned long flags;
+ struct pin_reg_desc *p_pin_reg_desc =
+ container_of(regu_nb, struct pin_reg_desc, nb);
+ unsigned long best_data = (unsigned long *)data;
+ const char *regu_name;
+ u32 bit;
+ u32 reg;
+
+ if (!p_pin_reg_desc) {
+ return -EINVAL;
+ }
+ regu_name = p_pin_reg_desc->supplies.supply;
+ bit = p_pin_reg_desc->platform_data->bit_offset;
+ reg = p_pin_reg_desc->platform_data->reg;
+ pr_info("event:0x%x, best_data:0x%x\n", event, best_data);
+
+ if (event & REGULATOR_EVENT_VOLTAGE_CHANGE) {
+ if (p_pin_reg_desc) {
+ if (best_data > VOL_THRESHOLD) {
+ #if (defined(CONFIG_ARCH_SCX20L)||defined(CONFIG_ARCH_SCX20))
+ pinmap_set(reg, (pinmap_get(reg) | (1 << bit)));
+ #else
+ pinmap_set(reg, (pinmap_get(reg) & ~(1 << bit)));
+ #endif
+ pr_info
+ ("%s()->Line:%d, --REGULATOR_EVENT_VOLTAGE_CHANGE--> %s event %ld, data %ld\n",
+ __func__, __LINE__, regu_name, event,
+ best_data);
+ } else {
+ #if (defined(CONFIG_ARCH_SCX20L)||defined(CONFIG_ARCH_SCX20))
+ pinmap_set(reg, (pinmap_get(reg) & ~(1 << bit)));
+ #else
+ pinmap_set(reg, (pinmap_get(reg) | (1 << bit)));
+ #endif
+ pr_info
+ ("%s()->Line:%d, --REGULATOR_EVENT_VOLTAGE_CHANGE--> %s event %ld, data %ld\n",
+ __func__, __LINE__, regu_name, event,
+ best_data);
+ }
+ }
+ } else if (event & REGULATOR_EVENT_DISABLE) {
+ pr_info
+ ("%s()->Line:%d, --REGULATOR_EVENT_DISABLE--> %s event %ld\n",
+ __func__, __LINE__, regu_name, event);
+ }
+
+ return NOTIFY_OK;
+}
+
+static int pin_regulator_notifier_register(struct pin_reg_desc *table)
+{
+ int i, ret;
+ struct regulator *regu;
+
+ for (i = 0; i < PD_CNT; i++) {
+ if (IS_ERR_OR_NULL(table[i].platform_data->power_domain)) {
+ pr_err("invalid platform data: 0x%p\n",
+ table[i].platform_data->power_domain);
+ return -EINVAL;
+ }
+ regu =
+ regulator_get(NULL, table[i].platform_data->power_domain);
+ pr_info("%s, %d, regu_name:%s\n", __FUNCTION__, __LINE__,
+ table[i].platform_data->power_domain);
+ if (IS_ERR_OR_NULL(regu)) {
+ pr_err("no regu %s !\n",
+ table[i].platform_data->power_domain);
+ return -ENXIO;;
+ }
+ table[i].supplies.consumer = regu;
+ table[i].supplies.supply = table[i].platform_data->power_domain;
+ table[i].nb.notifier_call = sc271x_regulator_event;
+ ret = regulator_register_notifier(regu, &(table[i].nb));
+ if (ret) {
+ pr_err("alert: regu %s reg notifier failed\n",
+ table[i].platform_data->power_domain);
+ }
+ }
+ return 0;
+}
+
+static int sprd_pin_switch_probe(struct platform_device *pdev)
+{
+ int i, ret;
+ struct sprd_pin_switch_platform_data *p_pin_switch_data;
+
+#ifdef CONFIG_OF
+ struct resource *res;
+ struct device_node *np = pdev->dev.of_node;
+
+ p_pin_switch_data =
+ devm_kzalloc(&pdev->dev, sizeof(*p_pin_switch_data) * PD_CNT,
+ GFP_KERNEL);
+ if (!p_pin_switch_data) {
+ pr_err("pin_switch alloc err\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "No reg of property specified\n");
+ return -ENODEV;
+ }
+
+ SPRD_PIN_BASE = (unsigned long)ioremap_nocache(res->start,res->end - res->start);
+ if (!SPRD_PIN_BASE)
+ panic("pin_switch get address failed!\n");
+
+
+ pr_info("-----SPRD_PIN_BASE:0x%x-----\n", SPRD_PIN_BASE);
+
+ for (i = 0; i < PD_CNT; i++) {
+ /*1. get pin power domain vddname */
+ ret =
+ of_property_read_string_index(np, "pwr_domain", i,
+ &p_pin_switch_data[i].
+ power_domain);
+ if (ret) {
+ pr_err("fail to get pwr_domain\n");
+ return ret;
+ }
+ pr_info("pwr_domain[%d]:%s\n", i,
+ p_pin_switch_data[i].power_domain);
+ /*2. get pin power domain reg ctrl description info */
+ ret =
+ of_property_read_u32_index(np, "ctrl_desc", i * 3,
+ &p_pin_switch_data[i].reg);
+ if (ret) {
+ pr_err("fail to get ctrl_desc reg\n");
+ return ret;
+ }
+ pr_info("reg[%d]:0x%x\n", i, p_pin_switch_data[i].reg);
+
+ ret =
+ of_property_read_u32_index(np, "ctrl_desc", i * 3 + 1,
+ &p_pin_switch_data[i].
+ bit_offset);
+ if (ret) {
+ pr_err("fail to get ctrl_desc bit_offset\n");
+ return ret;
+ }
+ pr_info("bit_offset[%d]:0x%x\n", i,
+ p_pin_switch_data[i].bit_offset);
+ ret =
+ of_property_read_u32_index(np, "ctrl_desc", i * 3 + 2,
+ &p_pin_switch_data[i].bit_width);
+ if (ret) {
+ pr_err("fail to get ctrl_desc bit_width\n");
+ return ret;
+ }
+ pr_info("bit_width[%d]:0x%x\n", i,
+ p_pin_switch_data[i].bit_width);
+ }
+#else
+ p_pin_switch_data = dev_get_platdata(&pdev->dev);
+ if (IS_ERR_OR_NULL(p_pin_switch_data)) {
+ pr_err("invalid platform data: 0x%p\n", p_pin_switch_data);
+ return -EINVAL;
+ }
+#endif
+ for (i = 0; i < PD_CNT; i++) {
+ pin_reg_desc_array[i].platform_data = &p_pin_switch_data[i]; // + i*sizeof(struct sprd_pin_switch_platform_data);
+ }
+ ret = pin_regulator_notifier_register(pin_reg_desc_array);
+ if (ret) {
+ pr_err("register notifier err\n");
+ }
+ return ret;
+}
+
+static struct of_device_id sprd_pinctrl_match_table[] = {
+ {.compatible = "sprd,pinctrl",},
+ {},
+};
+
+static struct platform_driver pin_switch_driver = {
+ .probe = sprd_pin_switch_probe,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "pin_switch",
+ .of_match_table = of_match_ptr(sprd_pinctrl_match_table),
+ },
+};
+#endif
+
+int __init pin_switch_proc_init(void)
+{
+ int i;
+ int ret = 0;
+ pin_switch_proc_base = proc_mkdir("pin_switch", NULL);
+ if (!pin_switch_proc_base)
+ return -ENOMEM;
+ for (i = 0; i < ARRAY_SIZE(sci_pin_switch_array); ++i) {
+ pin_switch_proc_add(&sci_pin_switch_array[i]);
+ }
+ for (i = 0; i < ARRAY_SIZE(sci_pin_switch_dir_array); ++i) {
+ pin_switch_proc_add_dir(&sci_pin_switch_dir_array[i]);
+ }
+#if defined(CONFIG_PIN_POWER_DOMAIN_SWITCH)
+ ret = platform_driver_register(&pin_switch_driver);
+#endif
+ return ret;
+}
+
+subsys_initcall_sync(pin_switch_proc_init);
+#else
+#error "CONFIG_PROC_FS needed by mach-sc/pin_switch"
+#endif /* CONFIG_PROC_FS */
diff --git a/drivers/platform/sprd/platsmp.c b/drivers/platform/sprd/platsmp.c
new file mode 100644
index 00000000..dcccde1a
--- /dev/null
+++ b/drivers/platform/sprd/platsmp.c
@@ -0,0 +1,382 @@
+/* linux/arch/arm/mach-sc8830/platsmp.c
+ *
+ * Copyright (c) 2010-2012 Spreadtrum Co., Ltd.
+ * http://www.spreadtrum.com
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Cloned from linux/arch/arm/mach-vexpress/platsmp.c
+ *
+ * Copyright (C) 2002 ARM Ltd.
+ * All Rights Reserved
+ *
+ * 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/errno.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp_plat.h>
+#include <linux/irqchip/arm-gic.h>
+#include <asm/mach-types.h>
+#include <asm/smp_scu.h>
+#include <asm/unified.h>
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/sci.h>
+#include <asm/atomic.h>
+
+extern void sci_secondary_startup(void);
+
+#if (defined CONFIG_ARCH_SC8825)
+static int __cpuinit boot_secondary_cpus(int cpu_id, u32 paddr)
+{
+ if (cpu_id != 1)
+ return -1;
+ writel(paddr,CPU_JUMP_VADDR);
+ writel(1 << (cpu_id * 4),HOLDING_PEN_VADDR);
+ return 0;
+}
+
+#elif (defined CONFIG_ARCH_SCX35)
+
+#define CA7_CORE1_PD (BIT(8)|BIT(9)|BIT(10))
+#define CA7_CORE2_PD (BIT(12)|BIT(13)|BIT(14))
+#define CA7_CORE3_PD (BIT(16)|BIT(17)|BIT(18))
+int scxx30_all_nonboot_cpus_died(void)
+{
+ u32 val;
+ u32 core1_pd , core2_pd , core3_pd;
+
+ val = sci_glb_read(REG_PMU_APB_PWR_STATUS0_DBG, -1UL);
+
+ core1_pd = val & CA7_CORE1_PD;
+ core2_pd = val & CA7_CORE2_PD;
+ core3_pd = val & CA7_CORE3_PD;
+
+ if(core1_pd && core2_pd && core3_pd){
+ pr_debug("*** %s, REG_PMU_APB_PWR_STATUS0_DBG:0x%x ***\n", __func__, val);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+int poweron_cpus(int cpu)
+{
+ u32 poweron, val;
+
+ if (cpu == 1)
+ poweron = REG_PMU_APB_PD_CA7_C1_CFG;
+ else if (cpu == 2)
+ poweron = REG_PMU_APB_PD_CA7_C2_CFG;
+ else if (cpu == 3)
+ poweron = REG_PMU_APB_PD_CA7_C3_CFG;
+ else
+ return -1;
+
+
+ val = sci_glb_read(REG_AP_AHB_CA7_RST_SET, -1UL);
+ val |= (1 << cpu);
+ sci_glb_write(REG_AP_AHB_CA7_RST_SET, val, -1UL);
+
+ val = BITS_PD_CA7_C3_PWR_ON_DLY(4) | BITS_PD_CA7_C3_PWR_ON_SEQ_DLY(4) | BITS_PD_CA7_C3_ISO_ON_DLY(4);
+ sci_glb_write(poweron, val, -1UL);
+
+ val = (BIT_PD_CA7_C3_AUTO_SHUTDOWN_EN | __raw_readl(poweron)) &~(BIT_PD_CA7_C3_FORCE_SHUTDOWN);
+ sci_glb_write(poweron, val, -1UL);
+ dmb();
+ udelay(1000);
+
+ while((__raw_readl(poweron)&BIT_PD_CA7_C3_FORCE_SHUTDOWN) ||
+ !(__raw_readl(REG_AP_AHB_CA7_RST_SET)&(1 << cpu)) ){
+ pr_debug("??? %s set regs failed ???\n", __func__ );
+ writel((__raw_readl(REG_AP_AHB_CA7_RST_SET) | (1 << cpu)), REG_AP_AHB_CA7_RST_SET);
+ val = (BIT_PD_CA7_C3_AUTO_SHUTDOWN_EN | __raw_readl(poweron)) &~(BIT_PD_CA7_C3_FORCE_SHUTDOWN);
+ writel(val,poweron);
+ dmb();
+ udelay(500);
+ }
+ writel((__raw_readl(REG_AP_AHB_CA7_RST_SET) & ~(1 << cpu)), REG_AP_AHB_CA7_RST_SET);
+ return 0;
+}
+
+int powerdown_cpus(int cpu)
+{
+ u32 poweron, val, i = 0;
+ if (cpu == 1)
+ poweron = REG_PMU_APB_PD_CA7_C1_CFG;
+ else if (cpu == 2)
+ poweron = REG_PMU_APB_PD_CA7_C2_CFG;
+ else if (cpu == 3)
+ poweron = REG_PMU_APB_PD_CA7_C3_CFG;
+ else
+ return -1;
+
+ val = (BIT_PD_CA7_C3_FORCE_SHUTDOWN | __raw_readl(poweron)) &~(BIT_PD_CA7_C3_AUTO_SHUTDOWN_EN);
+ writel(val, poweron);
+
+
+ while (i < 20) {
+ //check power down?
+ if (((__raw_readl(REG_PMU_APB_PWR_STATUS0_DBG) >> (4 * (cpu_logical_map(cpu) + 1))) & 0x0f) == 0x07) {
+ break;
+ }
+ udelay(60);
+ i++;
+ }
+ printk("powerdown_cpus i=%d !!\n", i);
+ BUG_ON(i >= 20);
+ udelay(60);
+
+ return 0;
+}
+
+static int __cpuinit boot_secondary_cpus(int cpu_id, u32 paddr)
+{
+ if (cpu_id < 1 || cpu_id > 3)
+ return -1;
+
+ writel(paddr,(CPU_JUMP_VADDR + (cpu_id << 2)));
+ writel(readl(HOLDING_PEN_VADDR) | (1 << cpu_id),HOLDING_PEN_VADDR);
+ poweron_cpus(cpu_id);
+
+ return 0;
+}
+
+#endif
+
+/*
+ * control for which core is the next to come out of the secondary
+ * boot "holding pen"
+ */
+extern volatile int pen_release;
+
+/*
+ * Write pen_release in a way that is guaranteed to be visible to all
+ * observers, irrespective of whether they're taking part in coherency
+ * or not. This is necessary for the hotplug code to work reliably.
+ */
+static void __cpuinit write_pen_release(int val)
+{
+ pen_release = val;
+ smp_wmb();
+ __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
+ outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+}
+
+static DEFINE_SPINLOCK(boot_lock);
+
+
+unsigned int sprd_boot_magnum = 0xbadf1a90;
+
+#define SPRD_UP_FLAG0 (0x63507530)
+#define SPRD_UP_FLAG1 (0x63507531)
+#define SPRD_UP_FLAG2 (0x63507532)
+#define SPRD_UP_FLAG3 (0x63507533)
+
+unsigned int g_sprd_boot_flag_1 = 0;
+unsigned int g_sprd_boot_flag_2 = 0;
+char * ga_boot_flag1[4] =
+ {
+ "bf10",
+ "bf11",
+ "bf12",
+ "bf13"
+ };
+char * ga_boot_flag2[4] =
+ {
+ "bf20",
+ "bf21",
+ "bf22",
+ "bf23"
+ };
+
+unsigned int g_sprd_up_flag[4] =
+ {
+ SPRD_UP_FLAG0,
+ SPRD_UP_FLAG1,
+ SPRD_UP_FLAG2,
+ SPRD_UP_FLAG3
+ };
+
+atomic_t boot_lock_cnt = ATOMIC_INIT(0);
+atomic_t boot_unlock_cnt = ATOMIC_INIT(0);
+
+void __cpuinit sprd_secondary_init(unsigned int cpu)
+{
+ /*
+ * if any interrupts are already enabled for the primary
+ * core (e.g. timer irq), then they will not have been enabled
+ * for us: do so
+ */
+ //gic_secondary_init(0);
+
+ /*
+ * let the primary processor know we're out of the
+ * pen, then head off into the C entry point
+ */
+
+ write_pen_release(g_sprd_up_flag[cpu]);
+
+ /*
+ * Synchronise with the boot thread.
+ */
+ spin_lock(&boot_lock);
+ spin_unlock(&boot_lock);
+ atomic_inc(&boot_unlock_cnt);
+ g_sprd_boot_flag_1 = 0;
+ g_sprd_boot_flag_2 = 0;
+
+}
+
+
+
+int __cpuinit sprd_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long timeout;
+ int ret;
+ unsigned int val = sprd_boot_magnum;
+
+ /*
+ * Set synchronisation state between this boot processor
+ * and the secondary one
+ */
+ spin_lock(&boot_lock);
+ atomic_inc(&boot_lock_cnt);
+
+ /*
+ * The secondary processor is waiting to be released from
+ * the holding pen - release it, then wait for it to flag
+ * that it has been released by resetting pen_release.
+ *
+ */
+
+ memcpy(&g_sprd_boot_flag_1,ga_boot_flag1[cpu],4);
+
+ val |= (cpu_logical_map(cpu) & 0x0000000f);
+
+ write_pen_release((int)val);
+
+ memcpy(&g_sprd_boot_flag_2,ga_boot_flag2[cpu],4);
+
+ ret = boot_secondary_cpus(cpu, virt_to_phys(sci_secondary_startup));
+ if (ret < 0)
+ pr_warn("SMP: boot_secondary(%u) error\n", cpu);
+
+ dsb_sev();
+
+
+ timeout = jiffies + (1 * HZ);
+
+ while (time_before(jiffies, timeout)) {
+ smp_rmb();
+ if (pen_release == g_sprd_up_flag[cpu])
+ break;
+
+ udelay(10);
+ dsb_sev();
+ }
+
+ spin_unlock(&boot_lock);
+
+ /*
+ * now the secondary core is starting up let it run its
+ * calibrations, then wait for it to finish
+ */
+ if(pen_release != g_sprd_up_flag[cpu]){
+ printk("*** %s, pen_release:%x ***\n ", __func__, pen_release);
+ printk("*** %s, REG_PMU_APB_PWR_STATUS0_DBG:0x%x \n", __func__, sci_glb_read(REG_PMU_APB_PWR_STATUS0_DBG, -1UL));
+ printk("*** %s, REG_AP_AHB_CA7_RST_SET:0x%x \n", __func__, sci_glb_read(REG_AP_AHB_CA7_RST_SET, -1UL));
+ printk("*** %s, REG_AP_AHB_CA7_STANDBY_STATUS:0x%x \n", __func__, sci_glb_read(REG_AP_AHB_CA7_STANDBY_STATUS, -1UL));
+#ifndef CONFIG_ARCH_SCX35L
+ printk("*** %s, REG_AON_APB_MPLL_CFG:0x%x \n", __func__, sci_glb_read(REG_AON_APB_MPLL_CFG, -1UL));
+#endif
+ }
+
+ return pen_release != g_sprd_up_flag[cpu] ? -ENOSYS : 0;
+}
+
+#ifdef CONFIG_HAVE_ARM_SCU
+static unsigned int __get_core_num(void)
+{
+ void __iomem *scu_base = (void __iomem *)(SPRD_CORE_BASE);
+ return (scu_base ? scu_get_core_count(scu_base) : 1);
+}
+#else
+static unsigned int __get_core_num(void)
+{
+ return 4;
+}
+#endif
+
+static unsigned int sci_get_core_num(void)
+{
+ return __get_core_num();
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system.
+ */
+void __init sprd_smp_init_cpus(void)
+{
+ unsigned int i, ncores;
+
+ ncores = sci_get_core_num();
+
+ /* sanity check */
+ if (ncores > nr_cpu_ids) {
+ pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
+ ncores, nr_cpu_ids);
+ ncores = nr_cpu_ids;
+ }
+
+ for (i = 0; i < ncores; i++)
+ set_cpu_possible(i, true);
+
+}
+#ifdef CONFIG_FIX_V7TAGRAM_BUG
+unsigned long physical_from_idle = 0;
+#endif
+void __init sprd_smp_prepare_cpus(unsigned int max_cpus)
+{
+#ifdef CONFIG_HAVE_ARM_SCU
+ scu_enable((void __iomem *)(SPRD_CORE_BASE));
+#endif
+#ifdef CONFIG_FIX_V7TAGRAM_BUG
+ extern unsigned long other_cpu_fix_phys;
+ extern void fix_tag_ram_bug(void);
+ extern void other_cpu_fix(void);
+
+ if(!other_cpu_fix_phys)
+ other_cpu_fix_phys = virt_to_phys(other_cpu_fix);
+ local_irq_disable();
+ fix_tag_ram_bug();
+ local_irq_enable();
+#endif
+}
+
+extern int sprd_cpu_kill(unsigned int cpu);
+extern void sprd_cpu_die(unsigned int cpu);
+extern int sprd_cpu_disable(unsigned int cpu);
+
+struct smp_operations sprd_smp_ops __initdata = {
+ .smp_init_cpus = sprd_smp_init_cpus,
+ .smp_prepare_cpus = sprd_smp_prepare_cpus,
+ .smp_secondary_init = sprd_secondary_init,
+ .smp_boot_secondary = sprd_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_kill = sprd_cpu_kill,
+ .cpu_disable = sprd_cpu_disable,
+ .cpu_die = sprd_cpu_die,
+#endif
+};
diff --git a/drivers/platform/sprd/pm-scx35.c b/drivers/platform/sprd/pm-scx35.c
new file mode 100644
index 00000000..7ec92fa0
--- /dev/null
+++ b/drivers/platform/sprd/pm-scx35.c
@@ -0,0 +1,1829 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#include <linux/io.h>
+#include <linux/kthread.h>
+#include <asm/irqflags.h>
+//#include <asm/hardware/cache-l2x0.h>
+#include <asm/cacheflush.h>
+#include <soc/sprd/system.h>
+#include <soc/sprd/pm_debug.h>
+#include <soc/sprd/common.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/sci_glb_regs.h>
+//#include <mach/irqs.h>
+#include <soc/sprd/sci.h>
+//#include "emc_repower.h"
+#include <linux/clockchips.h>
+#include <linux/wakelock.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/arch_misc.h>
+#include <asm/suspend.h>
+#include <asm/barrier.h>
+#include <linux/cpu_pm.h>
+#if defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8)
+#include <soc/sprd/sprd_persistent_clock.h>
+#endif
+#if defined(CONFIG_SPRD_DEBUG)
+/* For saving Fault status */
+#include <soc/sprd/sprd_debug.h>
+#endif
+
+extern void (*arm_pm_restart)(char str, const char *cmd);
+extern int sc8830_get_clock_status(void);
+extern void secondary_startup(void);
+extern int sp_pm_collapse(unsigned int cpu, unsigned int save_state);
+extern void sp_pm_collapse_exit(void);
+extern void sc8830_standby_iram(void);
+extern void sc8830_standby_iram_end(void);
+extern void sc8830_standby_exit_iram(void);
+extern int sc_cpuidle_init(void);
+void pm_ana_ldo_config(void);
+struct ap_ahb_reg_bak {
+ u32 ahb_eb;
+ u32 ca7_ckg_cfg;
+ u32 ca7_div_cfg;
+ u32 misc_ckg_en;
+ u32 misc_cfg;
+ u32 usb_phy_tune;
+};
+struct ap_clk_reg_bak {
+ u32 ap_ahb_cfg;
+ u32 ap_apb_cfg;
+ u32 clk_gsp_cfg;
+ u32 dispc0_cfg;
+ u32 dispc0_dbi_cfg;
+ u32 dispc0_dpi_cfg;
+ u32 dispc1_cfg;
+ u32 dispc1_dbi_cfg;
+ u32 dispc1_dpi_cfg;
+ u32 nfc_cfg;
+ u32 sdio0_cfg;
+ u32 sdio1_cfg;
+ u32 sdio2_cfg;
+ u32 emmc_cfg;
+ u32 gps_cfg;
+ u32 gps_tcxo_cfg;
+ u32 usb_ref_cfg;
+ u32 uart0_cfg;
+ u32 uart1_cfg;
+ u32 uart2_cfg;
+ u32 uart3_cfg;
+ u32 uart4_cfg;
+ u32 i2c0_cfg;
+ u32 i2c1_cfg;
+ u32 i2c2_cfg;
+ u32 i2c3_cfg;
+ u32 i2c4_cfg;
+ u32 spi0_cfg;
+ u32 spi1_cfg;
+ u32 spi2_cfg;
+ u32 iis0_cfg;
+ u32 iis1_cfg;
+ u32 iis2_cfg;
+ u32 iis3_cfg;
+};
+struct ap_apb_reg_bak {
+ u32 apb_eb;
+ u32 usb_phy_tune;
+ u32 usb_phy_ctrl;
+ u32 apb_misc_ctrl;
+};
+struct pub_reg_bak {
+ u32 ddr_qos_cfg1;
+ u32 ddr_qos_cfg2;
+ u32 ddr_qos_cfg3;
+};
+struct mm_reg_bak {
+ u32 mm_cfg1;
+ u32 mm_cfg2;
+};
+
+struct auto_pd_en {
+ volatile u32 magic_header;
+ volatile u32 bits;
+ volatile u32 magic_end;
+ char pd_config_menu[6][16];
+};
+
+struct dcdc_core_ds_step_info{
+ u32 ctl_reg;
+ u32 ctl_sht;
+ u32 cal_reg;
+ u32 cal_sht;
+};
+
+static struct ap_ahb_reg_bak ap_ahb_reg_saved;
+static struct ap_clk_reg_bak ap_clk_reg_saved;
+static struct ap_apb_reg_bak ap_apb_reg_saved;
+static struct pub_reg_bak pub_reg_saved;
+
+#if defined(CONFIG_ARCH_SCX15)
+static struct mm_reg_bak mm_reg_saved;
+#endif
+
+#if defined(CONFIG_ARCH_SCX30G)
+static unsigned long ana_chip_id;
+#endif
+
+/* bits definition
+ * bit_0 : ca7 top
+ * bit_1 : ca7 c0
+ * bit_2 : pub
+ * bit_3 : mm
+ * bit_4 : ap sys
+ * bit_5 : dcdc arm
+*/
+static struct auto_pd_en pd_config = {
+ 0x6a6aa6a6, 0x3f,0xa6a66a6a,
+ .pd_config_menu = {
+ "ca7_top",
+ "ca7_c0",
+ "pub",
+ "mm",
+ "ap_sys",
+ "dcdc_arm",
+ },
+};
+
+/*
+ * we just conigure the auto_pd_en property for parts of power domain, ldo here
+ * clr = 0 means called when kernel init and after real deep sleep
+ * clr = 1 means just called before real deep sleep
+ */
+static void configure_for_deepsleep(int clr)
+{
+#if defined(CONFIG_ARCH_SCX15)
+ if (clr) {
+ sci_glb_clr(REG_PMU_APB_PD_MM_TOP_CFG,BIT_PD_MM_TOP_AUTO_SHUTDOWN_EN);
+ sci_glb_clr(REG_PMU_APB_PD_AP_SYS_CFG, BIT_PD_AP_SYS_AUTO_SHUTDOWN_EN);
+ sci_glb_clr(REG_PMU_APB_PD_PUB_SYS_CFG, BIT_PD_PUB_SYS_AUTO_SHUTDOWN_EN);
+ /* FIXME: we can't powerdown ca7 because of stability problem when idle deep sleep */
+ //sci_glb_clr(REG_PMU_APB_PD_CA7_TOP_CFG, BIT_PD_CA7_TOP_AUTO_SHUTDOWN_EN);
+ //sci_glb_clr(REG_PMU_APB_PD_CA7_C0_CFG, BIT_PD_CA7_C0_AUTO_SHUTDOWN_EN);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_clr(REG_PMU_APB_PD_APCPU_TOP_CFG, BIT_PD_CA53_TOP_AUTO_SHUTDOWN_EN);
+ sci_glb_clr(REG_PMU_APB_PD_APCPU_C0_CFG, BIT_PD_CA53_LIT_C0_AUTO_SHUTDOWN_EN);
+#else
+ sci_glb_clr(REG_PMU_APB_PD_CA7_TOP_CFG, BIT_PD_CA7_TOP_AUTO_SHUTDOWN_EN);
+ sci_glb_clr(REG_PMU_APB_PD_CA7_C0_CFG, BIT_PD_CA7_C0_AUTO_SHUTDOWN_EN);
+#endif
+ sci_adi_clr(ANA_REG_GLB_PWR_SLP_CTRL0, BIT_SLP_DCDCARM_PD_EN);
+ /*
+ * because we can power down rf0 and vdd25 in idle deep now
+ * so we can let it be configured in u-boot
+ */
+ /* sci_adi_set(ANA_REG_GLB_PWR_SLP_CTRL0, BIT_SLP_LDORF0_PD_EN); */
+ /* sci_adi_set(ANA_REG_GLB_PWR_SLP_CTRL0, BIT_SLP_LDOVDD25_PD_EN); */
+#if defined(CONFIG_MACH_CORSICA_VE)
+ sci_adi_clr(ANA_REG_GLB_PWR_SLP_CTRL1, BIT_SLP_LDOSIM2_PD_EN);
+ sci_adi_clr(ANA_REG_GLB_PWR_SLP_CTRL1, BIT_SLP_LDOCAMMOT_PD_EN);
+#endif
+ } else {
+ if (pd_config.bits & (0x1 << 0))
+ //sci_glb_set(REG_PMU_APB_PD_CA7_TOP_CFG, BIT_PD_CA7_TOP_AUTO_SHUTDOWN_EN);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_set(REG_PMU_APB_PD_APCPU_TOP_CFG, BIT_PD_CA53_TOP_AUTO_SHUTDOWN_EN);
+#else
+ sci_glb_set(REG_PMU_APB_PD_CA7_TOP_CFG, BIT_PD_CA7_TOP_AUTO_SHUTDOWN_EN);
+#endif
+ if (pd_config.bits & (0x1 << 1))
+ //sci_glb_set(REG_PMU_APB_PD_CA7_C0_CFG, BIT_PD_CA7_C0_AUTO_SHUTDOWN_EN);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_set(REG_PMU_APB_PD_APCPU_C0_CFG, BIT_PD_CA53_LIT_C0_AUTO_SHUTDOWN_EN);
+#else
+ sci_glb_set(REG_PMU_APB_PD_CA7_C0_CFG, BIT_PD_CA7_C0_AUTO_SHUTDOWN_EN);
+#endif
+ if (pd_config.bits & (0x1 << 2))
+ sci_glb_set(REG_PMU_APB_PD_PUB_SYS_CFG, BIT_PD_PUB_SYS_AUTO_SHUTDOWN_EN);
+ if (pd_config.bits & (0x1 << 3))
+ sci_glb_set(REG_PMU_APB_PD_MM_TOP_CFG,BIT_PD_MM_TOP_AUTO_SHUTDOWN_EN);
+ if (pd_config.bits & (0x1 << 4))
+ sci_glb_set(REG_PMU_APB_PD_AP_SYS_CFG, BIT_PD_AP_SYS_AUTO_SHUTDOWN_EN);
+ if (pd_config.bits & (0x1 << 5))
+ sci_adi_set(ANA_REG_GLB_PWR_SLP_CTRL0, BIT_SLP_DCDCARM_PD_EN);
+#if defined(CONFIG_MACH_CORSICA_VE)
+ sci_adi_set(ANA_REG_GLB_PWR_SLP_CTRL1, BIT_SLP_LDOSIM2_PD_EN);
+ sci_adi_set(ANA_REG_GLB_PWR_SLP_CTRL1, BIT_SLP_LDOCAMMOT_PD_EN);
+#endif
+ #if defined(CONFIG_SS_FUNCTION)
+ sci_glb_set(REG_PMU_APB_PD_PUB_SYS_CFG,BIT_PD_PUB_SYS_AUTO_SHUTDOWN_EN);
+ #endif
+ }
+#endif
+}
+
+static void setup_autopd_mode(void)
+{
+ if (soc_is_scx35_v0())
+ sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, 0x3a);
+ else
+ sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, 0x3B);
+ sci_glb_write(REG_PMU_APB_AP_WAKEUP_POR_CFG, 0x1, -1UL);//AP_WAKEUP_POR_CFG
+
+ //sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_AP_EMC_AUTO_GATE_EN | BIT_CA7_EMC_AUTO_GATE_EN | BIT_CA7_CORE_AUTO_GATE_EN);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_AP_EMC_AUTO_GATE_EN | BIT_CA53_EMC_AUTO_GATE_EN | BIT_CA53_CORE_AUTO_GATE_EN);
+#else
+ sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, BIT_AP_EMC_AUTO_GATE_EN | BIT_CA7_EMC_AUTO_GATE_EN | BIT_CA7_CORE_AUTO_GATE_EN);
+#endif
+
+ // INTC0_EB, INTC1_EB, INTC2_EB, INTC3_EB
+ sci_glb_set(REG_AP_APB_APB_EB, 0xf<<19);
+
+ //set PD_CA7_C0_AUTO_SHUTDOWN_EN
+ //sci_glb_set(REG_PMU_APB_PD_CA7_TOP_CFG, BIT_PD_CA7_TOP_AUTO_SHUTDOWN_EN);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_set(REG_PMU_APB_PD_APCPU_TOP_CFG, BIT_PD_CA53_TOP_AUTO_SHUTDOWN_EN);
+#else
+ sci_glb_set(REG_PMU_APB_PD_CA7_TOP_CFG, BIT_PD_CA7_TOP_AUTO_SHUTDOWN_EN);
+#endif
+ //set PD_CA7_C0_AUTO_SHUTDOWN_EN
+ //sci_glb_set(REG_PMU_APB_PD_CA7_C0_CFG, BIT_PD_CA7_C0_AUTO_SHUTDOWN_EN);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_set(REG_PMU_APB_PD_CA53_LIT_C0_CFG, BIT_PD_CA53_LIT_C0_AUTO_SHUTDOWN_EN);
+#else
+ sci_glb_set(REG_PMU_APB_PD_CA7_C0_CFG, BIT_PD_CA7_C0_AUTO_SHUTDOWN_EN);
+#endif
+ sci_glb_set(SPRD_INT_BASE + 0x8, BIT(14) | BIT(4)); //ana & eic
+ /* Temporarily put it in here for AON Timer can wakeup cluster gating */
+ sci_glb_set(SPRD_INT_BASE + 0x8, BIT(2));
+
+ sci_glb_set(SPRD_INTC1_BASE + 0x8, BIT(6) | BIT(4)); //ana & kpd
+ //sci_glb_set(SPRD_INT_BASE + 0x8, BIT(2)); //ana & eic
+ //sci_glb_set(SPRD_INTC0_BASE + 0x8, BIT(30) | BIT(31)); //syst & aon_syst
+
+ //sci_adi_set(ANA_REG_GLB_LDO_SLP_CTRL0, BIT_SLP_IO_EN | BIT_SLP_LDORF0_PD_EN | BIT_SLP_LDORF1_PD_EN | BIT_SLP_LDORF2_PD_EN);
+ //sci_adi_clr(ANA_REG_GLB_LDO_SLP_CTRL0, BIT_SLP_IO_EN | BIT_SLP_LDORF0_PD_EN | BIT_SLP_LDORF1_PD_EN | BIT_SLP_LDORF2_PD_EN);
+
+ //sci_adi_set(ANA_REG_GLB_LDO_SLP_CTRL1, BIT_SLP_LDO_PD_EN);
+ sci_adi_clr(ANA_REG_GLB_XTL_WAIT_CTRL, BIT_SLP_XTLBUF_PD_EN);
+ //sci_adi_set(ANA_REG_GLB_XTL_WAIT_CTRL, BIT_SLP_XTLBUF_PD_EN);
+ //sci_adi_set(ANA_REG_GLB_LDO_SLP_CTRL2, BIT_SLP_LDORF2_LP_EN | BIT_SLP_LDORF1_LP_EN | BIT_SLP_LDORF0_LP_EN);
+
+ //sci_adi_clr(ANA_REG_GLB_LDO_SLP_CTRL2, BIT_SLP_LDORF2_LP_EN | BIT_SLP_LDORF1_LP_EN | BIT_SLP_LDORF0_LP_EN);
+
+ //sci_glb_set(REG_PMU_APB_DDR_SLEEP_CTRL, 7<<4);
+ //sci_glb_set(REG_PMU_APB_DDR_SLEEP_CTRL, 5<<4);
+#if defined(CONFIG_ARCH_SCX15)
+ sci_glb_set(REG_PMU_APB_PD_PUB_SYS_CFG, BIT_PD_PUB_SYS_AUTO_SHUTDOWN_EN);
+#else
+ if (soc_is_scx35_v1()) {
+ sci_glb_set(REG_PMU_APB_PD_PUB_SYS_CFG, BIT_PD_PUB_SYS_AUTO_SHUTDOWN_EN);
+ }
+#if !(defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L))
+ else {
+ sci_glb_clr(REG_PMU_APB_PD_PUB_SYS_CFG, BIT_PD_PUB_SYS_AUTO_SHUTDOWN_EN);
+ }
+#endif
+
+#if defined(CONFIG_SCX35_DMC_FREQ_DDR3)
+ sci_glb_clr(REG_PMU_APB_PD_PUB_SYS_CFG, BIT_PD_PUB_SYS_AUTO_SHUTDOWN_EN);
+#endif
+
+#endif
+
+#if defined(CONFIG_ARCH_SCX15)
+ sci_glb_set(REG_PMU_APB_PD_MM_TOP_CFG,BIT_PD_MM_TOP_AUTO_SHUTDOWN_EN);
+ sci_glb_clr(REG_PMU_APB_PD_PUB_SYS_CFG,BIT_PD_PUB_SYS_AUTO_SHUTDOWN_EN);
+#else
+ sci_glb_clr(REG_PMU_APB_PD_MM_TOP_CFG,BIT_PD_MM_TOP_AUTO_SHUTDOWN_EN);
+#endif
+ sci_glb_write(REG_PMU_APB_AP_WAKEUP_POR_CFG, 0x1, -1UL);
+ /* KEEP eMMC/SD power */
+#if defined(CONFIG_ADIE_SC2723)
+ sci_adi_clr(ANA_REG_GLB_PWR_SLP_CTRL0, BIT_SLP_LDOEMMCCORE_PD_EN);
+ sci_adi_clr(ANA_REG_GLB_PWR_SLP_CTRL1, BIT_SLP_LDOSDCORE_PD_EN | BIT_SLP_LDOSDIO_PD_EN);
+#elif defined(CONFIG_ADIE_SC2723S)
+ sci_adi_clr(ANA_REG_GLB_PWR_SLP_CTRL0, BIT_SLP_LDOEMMCCORE_PD_EN); /* | BIT_SLP_LDOEMMCIO_PD_EN);*/
+ sci_adi_clr(ANA_REG_GLB_PWR_SLP_CTRL1, BIT_SLP_LDOSDIO_PD_EN );
+#else
+ sci_adi_clr(ANA_REG_GLB_LDO_SLP_CTRL0, BIT_SLP_LDOEMMCCORE_PD_EN | BIT_SLP_LDOEMMCIO_PD_EN);
+ sci_adi_clr(ANA_REG_GLB_LDO_SLP_CTRL1, BIT_SLP_LDOSD_PD_EN );
+#endif
+ /***************** for idle to deep ****************/
+ configure_for_deepsleep(1);
+ return;
+}
+void disable_mm(void)
+{
+ sci_glb_clr(REG_MM_AHB_GEN_CKG_CFG, BIT_JPG_AXI_CKG_EN | BIT_VSP_AXI_CKG_EN |\
+ BIT_ISP_AXI_CKG_EN | BIT_DCAM_AXI_CKG_EN | BIT_SENSOR_CKG_EN | BIT_MIPI_CSI_CKG_EN | BIT_CPHY_CFG_CKG_EN);
+ sci_glb_clr(REG_MM_AHB_AHB_EB, BIT_JPG_EB | BIT_CSI_EB | BIT_VSP_EB | BIT_ISP_EB | BIT_CCIR_EB | BIT_DCAM_EB);
+ sci_glb_clr(REG_MM_AHB_GEN_CKG_CFG, BIT_MM_AXI_CKG_EN);
+ sci_glb_clr(REG_MM_AHB_GEN_CKG_CFG, BIT_MM_MTX_AXI_CKG_EN);
+ sci_glb_clr(REG_MM_AHB_AHB_EB, BIT_MM_CKG_EB);
+ sci_glb_clr(REG_PMU_APB_PD_MM_TOP_CFG,BIT_PD_MM_TOP_AUTO_SHUTDOWN_EN);
+ sci_glb_clr(REG_AON_APB_APB_EB0, BIT_MM_EB);
+ sci_glb_set(REG_PMU_APB_PD_MM_TOP_CFG,BIT_PD_MM_TOP_AUTO_SHUTDOWN_EN);
+ return;
+}
+void bak_restore_mm(int bak)
+{
+ static uint32_t gen_ckg, ahb_eb, mm_top, aon_eb0;
+ static uint32_t gen_ckg_en_mask = BIT_JPG_AXI_CKG_EN | BIT_VSP_AXI_CKG_EN |\
+ BIT_ISP_AXI_CKG_EN | BIT_DCAM_AXI_CKG_EN | BIT_SENSOR_CKG_EN | BIT_MIPI_CSI_CKG_EN | BIT_CPHY_CFG_CKG_EN;
+ static uint32_t ahb_eb_mask = BIT_JPG_EB | BIT_CSI_EB | BIT_VSP_EB | BIT_ISP_EB | BIT_CCIR_EB | BIT_DCAM_EB;
+ if(bak){
+ gen_ckg = sci_glb_read(REG_MM_AHB_GEN_CKG_CFG, -1UL);
+ ahb_eb = sci_glb_read(REG_MM_AHB_AHB_EB, -1UL);
+ mm_top = sci_glb_read(REG_PMU_APB_PD_MM_TOP_CFG, -1UL);
+ aon_eb0 = sci_glb_read(REG_AON_APB_APB_EB0, -1UL);
+ }else{
+ sci_glb_clr(REG_PMU_APB_PD_MM_TOP_CFG,BIT_PD_MM_TOP_AUTO_SHUTDOWN_EN);
+ if(aon_eb0 & BIT_MM_EB)
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT_MM_EB);
+ if(ahb_eb & BIT_MM_CKG_EB)
+ sci_glb_set(REG_MM_AHB_AHB_EB, BIT_MM_CKG_EB);
+ if(gen_ckg & BIT_MM_MTX_AXI_CKG_EN)
+ sci_glb_set(REG_MM_AHB_GEN_CKG_CFG, BIT_MM_MTX_AXI_CKG_EN);
+ if(gen_ckg & BIT_MM_AXI_CKG_EN)
+ sci_glb_set(REG_MM_AHB_GEN_CKG_CFG, BIT_MM_AXI_CKG_EN);
+ if(ahb_eb & ahb_eb_mask)
+ sci_glb_write(REG_MM_AHB_AHB_EB, ahb_eb, ahb_eb_mask);
+ if(gen_ckg & gen_ckg_en_mask)
+ sci_glb_write(REG_MM_AHB_GEN_CKG_CFG, gen_ckg, gen_ckg_en_mask);
+ }
+ return;
+}
+void disable_dma(void)
+{
+ int reg = sci_glb_read(SPRD_DMA0_BASE, BIT(16));
+ int cnt = 30;
+ if(reg){
+ sci_glb_set((SPRD_DMA0_BASE), BIT(0)); // pause dma
+ while(cnt-- && reg){
+ sci_glb_read(SPRD_DMA0_BASE, BIT(16));
+ udelay(100);
+ reg = sci_glb_read(SPRD_DMA0_BASE, BIT(16));
+ }
+ if(reg)
+ printk("disable dma failed\n");
+ }
+ sci_glb_clr(REG_AP_AHB_AHB_EB, BIT_DMA_EB);
+}
+void disable_ahb_module(void)
+{
+ //sci_glb_write(REG_AP_AHB_AHB_EB, 0, -1UL);
+ sci_glb_clr(REG_AP_AHB_AHB_EB, 0x1fff);
+
+ // AP_PERI_FORCE_SLP
+
+ sci_glb_set(REG_AP_AHB_AP_SYS_FORCE_SLEEP_CFG, BIT_AP_PERI_FORCE_SLP);
+ //sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_SLEEP_FOLLOW_CA7_EN);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_SLEEP_FOLLOW_CA53_EN);
+#else
+ sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_SLEEP_FOLLOW_CA7_EN);
+#endif
+ sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_DEEP_SLEEP_EN);
+
+ //AP_SYS_AUTO_SLEEP_CFG
+ sci_glb_set(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, 0x3B);
+
+ return;
+}
+void bak_restore_ahb(int bak)
+{
+ volatile u32 i;
+ if(bak){
+ ap_ahb_reg_saved.ahb_eb = sci_glb_read(REG_AP_AHB_AHB_EB, -1UL);
+#if defined(CONFIG_ARCH_SCX35L)
+ //ap_ahb_reg_saved.ca7_ckg_cfg = sci_glb_read(REG_AP_AHB_CA7_CKG_SEL_CFG, -1UL);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ ap_ahb_reg_saved.ca7_ckg_cfg = sci_glb_read(REG_AP_AHB_APCPU_CKG_SEL_CFG, -1UL);
+#else
+ ap_ahb_reg_saved.ca7_ckg_cfg = sci_glb_read(REG_AP_AHB_CA7_CKG_SEL_CFG, -1UL);
+#endif
+ ap_ahb_reg_saved.usb_phy_tune = sci_glb_read(REG_AP_AHB_OTG_PHY_TUNE, -1UL);
+ //ap_ahb_reg_saved.ca7_div_cfg = sci_glb_read(REG_AP_AHB_CA7_CKG_DIV_CFG, -1UL);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ ap_ahb_reg_saved.ca7_div_cfg = sci_glb_read(REG_AP_AHB_APCPU_CKG_DIV_CFG, -1UL);
+#else
+ ap_ahb_reg_saved.ca7_div_cfg = sci_glb_read(REG_AP_AHB_CA7_CKG_DIV_CFG, -1UL);
+#endif
+#else
+ //ap_ahb_reg_saved.ca7_ckg_cfg = sci_glb_read(REG_AP_AHB_CA7_CKG_CFG, -1UL);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ ap_ahb_reg_saved.ca7_ckg_cfg = sci_glb_read(REG_AP_AHB_APCPU_CKG_CFG, -1UL);
+#else
+ ap_ahb_reg_saved.ca7_ckg_cfg = sci_glb_read(REG_AP_AHB_CA7_CKG_CFG, -1UL);
+#endif
+#endif
+ ap_ahb_reg_saved.misc_ckg_en = sci_glb_read(REG_AP_AHB_MISC_CKG_EN, -1UL);
+ ap_ahb_reg_saved.misc_cfg = sci_glb_read(REG_AP_AHB_MISC_CFG, -1UL);
+ } else{
+ sci_glb_write(REG_AP_AHB_AHB_EB, ap_ahb_reg_saved.ahb_eb, -1UL);
+#if defined(CONFIG_ARCH_SCX35L)
+ //sci_glb_write(REG_AP_AHB_CA7_CKG_DIV_CFG, ap_ahb_reg_saved.ca7_div_cfg & (~0x7), -1UL);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_write(REG_AP_AHB_APCPU_CKG_DIV_CFG, ap_ahb_reg_saved.ca7_div_cfg & (~0x7), -1UL);
+#else
+ sci_glb_write(REG_AP_AHB_CA7_CKG_DIV_CFG, ap_ahb_reg_saved.ca7_div_cfg & (~0x7), -1UL);
+#endif
+ for(i = 0; i < 20; i++);
+ //sci_glb_write(REG_AP_AHB_CA7_CKG_DIV_CFG, ap_ahb_reg_saved.ca7_div_cfg, -1UL);
+ //sci_glb_write(REG_AP_AHB_CA7_CKG_SEL_CFG, ap_ahb_reg_saved.ca7_ckg_cfg & (~0x7), -1UL);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_write(REG_AP_AHB_APCPU_CKG_DIV_CFG, ap_ahb_reg_saved.ca7_div_cfg, -1UL);
+ sci_glb_write(REG_AP_AHB_APCPU_CKG_SEL_CFG, ap_ahb_reg_saved.ca7_ckg_cfg & (~0x7), -1UL);
+#else
+ sci_glb_write(REG_AP_AHB_CA7_CKG_DIV_CFG, ap_ahb_reg_saved.ca7_div_cfg, -1UL);
+ sci_glb_write(REG_AP_AHB_CA7_CKG_SEL_CFG, ap_ahb_reg_saved.ca7_ckg_cfg & (~0x7), -1UL);
+#endif
+ for(i = 0; i < 20; i++);
+ //sci_glb_write(REG_AP_AHB_CA7_CKG_SEL_CFG, ap_ahb_reg_saved.ca7_ckg_cfg, -1UL);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_write(REG_AP_AHB_APCPU_CKG_SEL_CFG, ap_ahb_reg_saved.ca7_ckg_cfg, -1UL);
+#else
+ sci_glb_write(REG_AP_AHB_CA7_CKG_SEL_CFG, ap_ahb_reg_saved.ca7_ckg_cfg, -1UL);
+#endif
+ sci_glb_write(REG_AP_AHB_OTG_PHY_TUNE, ap_ahb_reg_saved.usb_phy_tune, -1UL);
+#else
+ //sci_glb_write(REG_AP_AHB_CA7_CKG_CFG, ap_ahb_reg_saved.ca7_ckg_cfg & (~0x7), -1UL);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_write(REG_AP_AHB_APCPU_CKG_CFG, ap_ahb_reg_saved.ca7_ckg_cfg & (~0x7), -1UL);
+#else
+ sci_glb_write(REG_AP_AHB_CA7_CKG_CFG, ap_ahb_reg_saved.ca7_ckg_cfg & (~0x7), -1UL);
+#endif
+ for(i = 0; i < 20; i++);
+ //sci_glb_write(REG_AP_AHB_CA7_CKG_CFG, ap_ahb_reg_saved.ca7_ckg_cfg, -1UL);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_write(REG_AP_AHB_APCPU_CKG_CFG, ap_ahb_reg_saved.ca7_ckg_cfg, -1UL);
+#else
+ sci_glb_write(REG_AP_AHB_CA7_CKG_CFG, ap_ahb_reg_saved.ca7_ckg_cfg, -1UL);
+#endif
+#endif
+ sci_glb_write(REG_AP_AHB_MISC_CKG_EN, ap_ahb_reg_saved.misc_ckg_en, -1UL);
+ sci_glb_write(REG_AP_AHB_MISC_CFG, ap_ahb_reg_saved.misc_cfg, -1UL);
+ }
+ return;
+}
+
+void disable_apb_module(void)
+{
+ int stat;
+ stat = sci_glb_read(REG_AP_APB_APB_EB, -1UL);
+ stat &=(0xf<<19 | BIT_UART1_EB | BIT_UART0_EB); // leave intc on
+ sci_glb_write(REG_AP_APB_APB_EB, stat, -1UL);
+ return;
+}
+void bak_restore_apb(int bak)
+{
+ if(bak){
+ ap_apb_reg_saved.apb_eb = sci_glb_read(REG_AP_APB_APB_EB, -1UL);
+ ap_apb_reg_saved.apb_misc_ctrl = sci_glb_read(REG_AP_APB_APB_MISC_CTRL, -1UL);
+#if !defined(CONFIG_ARCH_SCX35L)
+#if !defined(CONFIG_ARCH_SCX20)
+ ap_apb_reg_saved.usb_phy_tune = sci_glb_read(REG_AP_APB_USB_PHY_TUNE, -1UL);
+#endif //#if !defined(CONFIG_ARCH_SCX20)
+#endif
+ }else{
+ sci_glb_write(REG_AP_APB_APB_EB, ap_apb_reg_saved.apb_eb, -1UL);
+ sci_glb_write(REG_AP_APB_APB_MISC_CTRL, ap_apb_reg_saved.apb_misc_ctrl, -1UL);
+#if !defined(CONFIG_ARCH_SCX35L)
+#if !defined(CONFIG_ARCH_SCX20)
+ sci_glb_write(REG_AP_APB_USB_PHY_TUNE, ap_apb_reg_saved.usb_phy_tune, -1UL);
+#endif //#if !defined(CONFIG_ARCH_SCX20)
+#endif
+ }
+ return;
+}
+#if defined(CONFIG_ARCH_SCX15)
+extern void __iomap_page(unsigned long virt, unsigned long size, int enable);
+void bak_restore_mm_scx15(int bak)
+{
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT_MM_EB);
+ __iomap_page(REGS_MM_AHB_BASE, SZ_4K, 1);
+ if(bak){
+ mm_reg_saved.mm_cfg1= sci_glb_read(SPRD_MMAHB_BASE, -1UL);
+ mm_reg_saved.mm_cfg2 = sci_glb_read(SPRD_MMAHB_BASE + 0x8, -1UL);
+ }else{
+ sci_glb_write(SPRD_MMAHB_BASE, mm_reg_saved.mm_cfg1, -1UL);
+ sci_glb_write(SPRD_MMAHB_BASE + 0x8, mm_reg_saved.mm_cfg2, -1UL);
+ }
+ __iomap_page(REGS_MM_AHB_BASE, SZ_4K, 0);
+ sci_glb_clr(REG_AON_APB_APB_EB0, BIT_MM_EB);
+ return;
+}
+#endif
+static void bak_ap_clk_reg(int bak)
+{
+ volatile u32 i;
+ if(bak) {
+ ap_clk_reg_saved.ap_ahb_cfg = sci_glb_read(REG_AP_CLK_AP_AHB_CFG , -1UL);
+ ap_clk_reg_saved.ap_apb_cfg = sci_glb_read(REG_AP_CLK_AP_APB_CFG , -1UL);
+ ap_clk_reg_saved.clk_gsp_cfg = sci_glb_read(REG_AP_CLK_GSP_CFG , -1UL);
+ ap_clk_reg_saved.dispc0_cfg = sci_glb_read(REG_AP_CLK_DISPC0_CFG , -1UL);
+ ap_clk_reg_saved.dispc0_dbi_cfg = sci_glb_read(REG_AP_CLK_DISPC0_DBI_CFG , -1UL);
+ ap_clk_reg_saved.dispc0_dpi_cfg = sci_glb_read(REG_AP_CLK_DISPC0_DPI_CFG , -1UL);
+#if defined(CONFIG_ARCH_SCX35L)
+ ap_clk_reg_saved.nfc_cfg = sci_glb_read(REG_AP_CLK_NANDC_ECC_CFG , -1UL);
+#else
+ ap_clk_reg_saved.dispc1_cfg = sci_glb_read(REG_AP_CLK_DISPC1_CFG , -1UL);
+ ap_clk_reg_saved.dispc1_dbi_cfg = sci_glb_read(REG_AP_CLK_DISPC1_DBI_CFG , -1UL);
+ ap_clk_reg_saved.dispc1_dpi_cfg = sci_glb_read(REG_AP_CLK_DISPC1_DPI_CFG , -1UL);
+ ap_clk_reg_saved.nfc_cfg = sci_glb_read(REG_AP_CLK_NFC_CFG , -1UL);
+#endif
+ ap_clk_reg_saved.sdio0_cfg = sci_glb_read(REG_AP_CLK_SDIO0_CFG , -1UL);
+ ap_clk_reg_saved.sdio1_cfg = sci_glb_read(REG_AP_CLK_SDIO1_CFG , -1UL);
+ ap_clk_reg_saved.sdio2_cfg = sci_glb_read(REG_AP_CLK_SDIO2_CFG , -1UL);
+ ap_clk_reg_saved.emmc_cfg = sci_glb_read(REG_AP_CLK_EMMC_CFG , -1UL);
+#if defined(CONFIG_ARCH_SCX35L)
+ ap_clk_reg_saved.usb_ref_cfg = sci_glb_read(REG_AP_CLK_OTG_REF_CFG , -1UL);
+#else
+ ap_clk_reg_saved.gps_cfg = sci_glb_read(REG_AP_CLK_GPS_CFG , -1UL);
+ ap_clk_reg_saved.gps_tcxo_cfg = sci_glb_read(REG_AP_CLK_GPS_TCXO_CFG , -1UL);
+ ap_clk_reg_saved.usb_ref_cfg = sci_glb_read(REG_AP_CLK_USB_REF_CFG , -1UL);
+#endif
+ ap_clk_reg_saved.uart0_cfg = sci_glb_read(REG_AP_CLK_UART0_CFG , -1UL);
+ ap_clk_reg_saved.uart1_cfg = sci_glb_read(REG_AP_CLK_UART1_CFG , -1UL);
+ ap_clk_reg_saved.uart2_cfg = sci_glb_read(REG_AP_CLK_UART2_CFG , -1UL);
+ ap_clk_reg_saved.uart3_cfg = sci_glb_read(REG_AP_CLK_UART3_CFG , -1UL);
+ ap_clk_reg_saved.uart4_cfg = sci_glb_read(REG_AP_CLK_UART4_CFG , -1UL);
+ ap_clk_reg_saved.i2c0_cfg = sci_glb_read(REG_AP_CLK_I2C0_CFG , -1UL);
+ ap_clk_reg_saved.i2c1_cfg = sci_glb_read(REG_AP_CLK_I2C1_CFG , -1UL);
+ ap_clk_reg_saved.i2c2_cfg = sci_glb_read(REG_AP_CLK_I2C2_CFG , -1UL);
+ ap_clk_reg_saved.i2c3_cfg = sci_glb_read(REG_AP_CLK_I2C3_CFG , -1UL);
+ ap_clk_reg_saved.i2c4_cfg = sci_glb_read(REG_AP_CLK_I2C4_CFG , -1UL);
+ ap_clk_reg_saved.spi0_cfg = sci_glb_read(REG_AP_CLK_SPI0_CFG , -1UL);
+ ap_clk_reg_saved.spi1_cfg = sci_glb_read(REG_AP_CLK_SPI1_CFG , -1UL);
+ ap_clk_reg_saved.spi2_cfg = sci_glb_read(REG_AP_CLK_SPI2_CFG , -1UL);
+ ap_clk_reg_saved.iis0_cfg = sci_glb_read(REG_AP_CLK_IIS0_CFG , -1UL);
+ ap_clk_reg_saved.iis1_cfg = sci_glb_read(REG_AP_CLK_IIS1_CFG , -1UL);
+ ap_clk_reg_saved.iis2_cfg = sci_glb_read(REG_AP_CLK_IIS2_CFG , -1UL);
+ ap_clk_reg_saved.iis3_cfg = sci_glb_read(REG_AP_CLK_IIS3_CFG , -1UL);
+ }
+ else {
+ sci_glb_write(REG_AP_CLK_AP_AHB_CFG , ap_clk_reg_saved.ap_ahb_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_AP_APB_CFG , ap_clk_reg_saved.ap_apb_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_GSP_CFG , ap_clk_reg_saved.clk_gsp_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_DISPC0_CFG , ap_clk_reg_saved.dispc0_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_DISPC0_CFG , ap_clk_reg_saved.dispc0_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_DISPC0_DBI_CFG , ap_clk_reg_saved.dispc0_dbi_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_DISPC0_DBI_CFG , ap_clk_reg_saved.dispc0_dbi_cfg ,-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_DISPC0_DPI_CFG , ap_clk_reg_saved.dispc0_dpi_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_DISPC0_DPI_CFG , ap_clk_reg_saved.dispc0_dpi_cfg ,-1UL);
+#if defined(CONFIG_ARCH_SCX35L)
+ sci_glb_write(REG_AP_CLK_NANDC_ECC_CFG , ap_clk_reg_saved.nfc_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_NANDC_ECC_CFG , ap_clk_reg_saved.nfc_cfg ,-1UL);
+#else
+ sci_glb_write(REG_AP_CLK_DISPC1_CFG , ap_clk_reg_saved.dispc1_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_DISPC1_CFG , ap_clk_reg_saved.dispc1_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_DISPC1_DBI_CFG , ap_clk_reg_saved.dispc1_dbi_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_DISPC1_DBI_CFG , ap_clk_reg_saved.dispc1_dbi_cfg ,-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_DISPC1_DPI_CFG , ap_clk_reg_saved.dispc1_dpi_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_DISPC1_DPI_CFG , ap_clk_reg_saved.dispc1_dpi_cfg ,-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_NFC_CFG , ap_clk_reg_saved.nfc_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_NFC_CFG , ap_clk_reg_saved.nfc_cfg ,-1UL);
+#endif
+ sci_glb_write(REG_AP_CLK_SDIO0_CFG , ap_clk_reg_saved.sdio0_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_SDIO0_CFG , ap_clk_reg_saved.sdio0_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_SDIO1_CFG , ap_clk_reg_saved.sdio1_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_SDIO2_CFG , ap_clk_reg_saved.sdio2_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_EMMC_CFG , ap_clk_reg_saved.emmc_cfg ,-1UL);
+#if defined(CONFIG_ARCH_SCX35L)
+ sci_glb_write(REG_AP_CLK_OTG_REF_CFG , ap_clk_reg_saved.usb_ref_cfg ,-1UL);
+#else
+ sci_glb_write(REG_AP_CLK_GPS_CFG , ap_clk_reg_saved.gps_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_GPS_TCXO_CFG , ap_clk_reg_saved.gps_tcxo_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_USB_REF_CFG , ap_clk_reg_saved.usb_ref_cfg ,-1UL);
+#endif
+ sci_glb_write(REG_AP_CLK_UART0_CFG , ap_clk_reg_saved.uart0_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_UART0_CFG , ap_clk_reg_saved.uart0_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_UART1_CFG , ap_clk_reg_saved.uart1_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_UART1_CFG , ap_clk_reg_saved.uart1_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_UART2_CFG , ap_clk_reg_saved.uart2_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_UART2_CFG , ap_clk_reg_saved.uart2_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_UART3_CFG , ap_clk_reg_saved.uart3_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_UART3_CFG , ap_clk_reg_saved.uart3_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_UART4_CFG , ap_clk_reg_saved.uart4_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_UART4_CFG , ap_clk_reg_saved.uart4_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_I2C0_CFG , ap_clk_reg_saved.i2c0_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_I2C0_CFG , ap_clk_reg_saved.i2c0_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_I2C1_CFG , ap_clk_reg_saved.i2c1_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_I2C1_CFG , ap_clk_reg_saved.i2c1_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_I2C2_CFG , ap_clk_reg_saved.i2c2_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_I2C2_CFG , ap_clk_reg_saved.i2c2_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_I2C3_CFG , ap_clk_reg_saved.i2c3_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_I2C3_CFG , ap_clk_reg_saved.i2c3_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_I2C4_CFG , ap_clk_reg_saved.i2c4_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_I2C4_CFG , ap_clk_reg_saved.i2c4_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_SPI0_CFG , ap_clk_reg_saved.spi0_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_SPI0_CFG , ap_clk_reg_saved.spi0_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_SPI1_CFG , ap_clk_reg_saved.spi1_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_SPI1_CFG , ap_clk_reg_saved.spi1_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_SPI2_CFG , ap_clk_reg_saved.spi2_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_SPI2_CFG , ap_clk_reg_saved.spi2_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_IIS0_CFG , ap_clk_reg_saved.iis0_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_IIS0_CFG , ap_clk_reg_saved.iis0_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_IIS1_CFG , ap_clk_reg_saved.iis1_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_IIS1_CFG , ap_clk_reg_saved.iis1_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_IIS2_CFG , ap_clk_reg_saved.iis2_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_IIS2_CFG , ap_clk_reg_saved.iis2_cfg ,-1UL);
+ sci_glb_write(REG_AP_CLK_IIS3_CFG , ap_clk_reg_saved.iis3_cfg & (~0x3),-1UL);
+ for(i = 0; i < 10; i++);
+ sci_glb_write(REG_AP_CLK_IIS3_CFG , ap_clk_reg_saved.iis3_cfg ,-1UL);
+ }
+}
+void disable_aon_module(void)
+{
+ //sci_glb_clr(REG_AON_APB_APB_EB0, BIT_CA7_DAP_EB | BIT_GPU_EB);
+ //sci_glb_clr(REG_AON_APB_APB_EB0, BIT_CA7_DAP_EB);
+
+ sci_glb_clr(REG_AON_APB_APB_EB0, BIT_AON_TMR_EB | BIT_AP_TMR0_EB);
+ sci_glb_clr(REG_AON_APB_APB_EB1, BIT_AP_TMR2_EB | BIT_AP_TMR1_EB);
+ sci_glb_clr(REG_AON_APB_APB_EB1, BIT_DISP_EMC_EB);
+}
+void bak_restore_aon(int bak)
+{
+ static uint32_t apb_eb1, pwr_ctl, apb_eb0;
+ if(bak){
+ apb_eb0 = sci_glb_read(REG_AON_APB_APB_EB0, -1UL);
+ apb_eb1 = sci_glb_read(REG_AON_APB_APB_EB1, -1UL);
+ pwr_ctl = sci_glb_read(REG_AON_APB_PWR_CTRL, -1UL);
+ }else{
+ if(apb_eb1 & BIT_DISP_EMC_EB)
+ sci_glb_set(REG_AON_APB_APB_EB1, BIT_DISP_EMC_EB);
+ sci_glb_write(REG_AON_APB_APB_EB0, apb_eb0, -1UL);
+ sci_glb_write(REG_AON_APB_APB_EB1, apb_eb1, -1UL);
+ }
+ return;
+}
+
+void disable_ana_module(void)
+{
+#if defined(CONFIG_ADIE_SC2713S) || defined(CONFIG_ADIE_SC2713)
+ sci_adi_set(ANA_REG_GLB_LDO_PD_CTRL, BIT_LDO_SD_PD | BIT_LDO_SIM0_PD | BIT_LDO_SIM1_PD | BIT_LDO_SIM2_PD | BIT_LDO_CAMA_PD |\
+ BIT_LDO_CAMD_PD | BIT_LDO_CAMIO_PD | BIT_LDO_CAMMOT_PD | BIT_DCDC_WPA_PD);
+#endif
+#if defined(CONFIG_ADIE_SC2723S)
+ sci_adi_set(ANA_REG_GLB_LDO_PD_CTRL, BIT_LDO_SDIO_PD | BIT_LDO_SIM0_PD | BIT_LDO_SIM1_PD | BIT_LDO_SIM2_PD | BIT_LDO_CAMA_PD |\
+ BIT_LDO_CAMD_PD | BIT_LDO_CAMIO_PD | BIT_LDO_CAMMOT_PD | BIT_DCDC_WPA_PD);
+#endif
+#if defined(CONFIG_ADIE_SC2723)
+ sci_adi_set(ANA_REG_GLB_LDO_PD_CTRL, BIT_LDO_SDIO_PD | BIT_LDO_SIM0_PD | BIT_LDO_SIM1_PD | BIT_LDO_SIM2_PD | BIT_LDO_CAMA_PD |\
+ BIT_LDO_CAMD_PD | BIT_LDO_CAMIO_PD | BIT_LDO_CAMMOT_PD | BIT_LDO_SDCORE_PD | BIT_LDO_WIFIPA_PD | BIT_DCDC_CON_PD | BIT_DCDC_WPA_PD);
+#endif
+}
+void bak_restore_ana(int bak)
+{
+ static uint32_t ldo_pd_ctrl;
+ static uint32_t mask;
+#if defined(CONFIG_ADIE_SC2713S) || defined(CONFIG_ADIE_SC2713)
+ mask = BIT_LDO_SD_PD | BIT_LDO_SIM0_PD | BIT_LDO_SIM1_PD | BIT_LDO_SIM2_PD | BIT_LDO_CAMA_PD |\
+ BIT_LDO_CAMD_PD | BIT_LDO_CAMIO_PD | BIT_LDO_CAMMOT_PD | BIT_DCDC_WPA_PD;
+#endif
+#if defined(CONFIG_ADIE_SC2723S)
+ mask = BIT_LDO_SDIO_PD | BIT_LDO_SIM0_PD | BIT_LDO_SIM1_PD | BIT_LDO_SIM2_PD | BIT_LDO_CAMA_PD |\
+ BIT_LDO_CAMD_PD | BIT_LDO_CAMIO_PD | BIT_LDO_CAMMOT_PD | BIT_DCDC_WPA_PD;
+#endif
+
+ if(bak){
+ ldo_pd_ctrl = sci_adi_read(ANA_REG_GLB_LDO_PD_CTRL);
+ }else{
+ sci_adi_write(ANA_REG_GLB_LDO_PD_CTRL, ldo_pd_ctrl, mask);
+ }
+ return;
+}
+static void bak_restore_pub(int bak)
+{
+ /*v0 not set auto power down*/
+ if (soc_is_scx35_v0()) {
+ return ;
+ }
+ if(bak) {
+ pub_reg_saved.ddr_qos_cfg1 = sci_glb_read(REG_PUB_APB_DDR_QOS_CFG1, -1UL);
+ pub_reg_saved.ddr_qos_cfg2 = sci_glb_read(REG_PUB_APB_DDR_QOS_CFG2, -1UL);
+ pub_reg_saved.ddr_qos_cfg3 = sci_glb_read(REG_PUB_APB_DDR_QOS_CFG3, -1UL);
+ } else {
+ sci_glb_write(REG_PUB_APB_DDR_QOS_CFG1, pub_reg_saved.ddr_qos_cfg1, -1UL);
+ sci_glb_write(REG_PUB_APB_DDR_QOS_CFG2, pub_reg_saved.ddr_qos_cfg2, -1UL);
+ sci_glb_write(REG_PUB_APB_DDR_QOS_CFG3, pub_reg_saved.ddr_qos_cfg3, -1UL);
+ }
+}
+
+static void bak_restore_ap_intc(int bak)
+{
+ static u32 intc0_eb, intc1_eb, intc2_eb,intc3_eb;
+ if(bak) {
+ intc0_eb = sci_glb_read(SPRD_INTC0_BASE + 0x8, -1UL);
+ intc1_eb = sci_glb_read(SPRD_INTC1_BASE + 0x8, -1UL);
+ intc2_eb = sci_glb_read(SPRD_INTC2_BASE + 0x8, -1UL);
+ intc3_eb = sci_glb_read(SPRD_INTC3_BASE + 0x8, -1UL);
+ } else {
+ sci_glb_write(SPRD_INTC0_BASE + 0x8, intc0_eb, -1UL);
+ sci_glb_write(SPRD_INTC1_BASE + 0x8, intc1_eb, -1UL);
+ sci_glb_write(SPRD_INTC2_BASE + 0x8, intc2_eb, -1UL);
+ sci_glb_write(SPRD_INTC3_BASE + 0x8, intc3_eb, -1UL);
+ }
+}
+
+#define REG_PIN_XTLEN ( SPRD_PIN_BASE + 0x0138 )
+#define REG_PIN_CHIP_SLEEP ( SPRD_PIN_BASE + 0x0208 )
+#define REG_PIN_XTL_BUF_EN0 ( SPRD_PIN_BASE + 0x020C )
+#define REG_PIN_XTL_BUF_EN1 ( SPRD_PIN_BASE + 0x0210 )
+#define REG_PIN_XTL_BUF_EN2 ( SPRD_PIN_BASE + 0x0214 )
+void show_pin_reg(void)
+{
+ sci_glb_set(SPRD_INT_BASE + 0x8, BIT(14) | BIT(4)); //ana & eic
+ printk("REG_PIN_XTLEN 0x%08x\n", sci_glb_read(REG_PIN_XTLEN, -1UL));
+ printk("REG_PIN_XTL_BUF_EN0 0x%08x\n", sci_glb_read(REG_PIN_XTL_BUF_EN0, -1UL));
+ printk("REG_PIN_XTL_BUF_EN1 0x%08x\n", sci_glb_read(REG_PIN_XTL_BUF_EN1, -1UL));
+ printk("REG_PIN_XTL_BUF_EN2 0x%08x\n", sci_glb_read(REG_PIN_XTL_BUF_EN2, -1UL));
+ printk("REG_PIN_CHIP_SLEEP 0x%08x\n", sci_glb_read(REG_PIN_CHIP_SLEEP, -1UL));
+ printk("### uart1 ckd 0x%08x\n", sci_glb_read(SPRD_UART1_BASE + 0X24, -1UL));
+ printk("### uart1 ctl 0x%08x\n", sci_glb_read(SPRD_UART1_BASE + 0X18, -1UL));
+#if defined(CONFIG_ARCH_SCX15)
+ sci_glb_set(REG_PMU_APB_XTL0_REL_CFG, 0x4);
+ sci_glb_set(REG_PMU_APB_XTLBUF0_REL_CFG, 0x4);
+#if !defined(CONFIG_ARCH_SCX35L)
+ sci_glb_clr(REG_PMU_APB_PD_CP1_TD_CFG, BIT(24));
+#endif /* CONFIG_ARCH_SCX35L */
+#endif
+ printk("REG_PMU_APB_XTL0_REL_CFG 0x%08x\n", sci_glb_read(REG_PMU_APB_XTL0_REL_CFG, -1UL));
+ printk("REG_PMU_APB_XTL1_REL_CFG 0x%08x\n", sci_glb_read(REG_PMU_APB_XTL1_REL_CFG, -1UL));
+#if !defined(CONFIG_ARCH_SCX35L)
+ printk("REG_PMU_APB_XTL2_REL_CFG 0x%08x\n", sci_glb_read(REG_PMU_APB_XTL2_REL_CFG, -1UL));
+#endif
+ printk("REG_PMU_APB_XTLBUF0_REL_CFG 0x%08x\n", sci_glb_read(REG_PMU_APB_XTLBUF0_REL_CFG, -1UL));
+ printk("REG_PMU_APB_XTLBUF1_REL_CFG 0x%08x\n", sci_glb_read(REG_PMU_APB_XTLBUF1_REL_CFG, -1UL));
+#if !defined(CONFIG_ARCH_SCX35L)
+ printk("REG_PMU_APB_PD_CP1_TD_CFG 0x%08x\n", sci_glb_read(REG_PMU_APB_PD_CP1_TD_CFG, -1Ul));
+#endif
+}
+void force_mcu_core_sleep(void)
+{
+ sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_CORE_SLEEP);
+}
+void enable_mcu_deep_sleep(void)
+{
+ sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_DEEP_SLEEP_EN | BIT_MCU_LIGHT_SLEEP_EN);
+}
+void disable_mcu_deep_sleep(void)
+{
+ sci_glb_clr(REG_AP_AHB_MCU_PAUSE, BIT_MCU_DEEP_SLEEP_EN | BIT_MCU_LIGHT_SLEEP_EN);
+}
+#define BITS_SET_CHECK(__reg, __bits, __string) do{\
+ uint32_t mid =sci_glb_read(__reg, __bits); \
+ if(mid != __bits) \
+ printk(__string " not 1" "\n"); \
+ }while(0)
+#define BITS_CLEAR_CHECK(__reg, __bits, __string) do{\
+ uint32_t mid = sci_glb_read(__reg, __bits); \
+ if(mid & __bits) \
+ printk(__string " not 0" "\n"); \
+ }while(0)
+#define PRINT_REG(__reg) do{\
+ uint32_t mid = sci_glb_read(__reg, -1UL); \
+ printk(#__reg "value 0x%x\n", mid); \
+ }while(0)
+void show_reg_status(void)
+{
+ //BITS_CLEAR_CHECK(REG_AON_APB_APB_EB0, BIT_CA7_DAP_EB, "ca7_dap_eb");
+#if defined(CONFIG_ARCH_SCX35LT8)
+ /*BITS_CLEAR_CHECK(REG_AON_APB_APB_EB0, BIT_DAP_CA53_CCI_EN, "ca7_dap_eb");*/
+#else
+ BITS_CLEAR_CHECK(REG_AON_APB_APB_EB0, BIT_CA7_DAP_EB, "ca7_dap_eb");
+#endif
+ BITS_CLEAR_CHECK(REG_AP_AHB_AHB_EB, BIT_DMA_EB, "dma_eb");
+ BITS_CLEAR_CHECK((SPRD_DMA0_BASE + 0x1c), BIT(20), "dma_busy");
+ BITS_CLEAR_CHECK(REG_AP_AHB_AHB_EB, BIT_USB_EB, "usb_eb");
+ BITS_CLEAR_CHECK(REG_AP_AHB_AHB_EB, BIT_SDIO0_EB, "sdio0_eb");
+ BITS_CLEAR_CHECK(REG_AP_AHB_AHB_EB, BIT_SDIO1_EB, "sdio1_eb");
+ BITS_CLEAR_CHECK(REG_AP_AHB_AHB_EB, BIT_SDIO2_EB, "sdio2_eb");
+ BITS_CLEAR_CHECK(REG_AP_AHB_AHB_EB, BIT_EMMC_EB, "emmc_eb");
+ BITS_CLEAR_CHECK(REG_AP_AHB_AHB_EB, BIT_NFC_EB, "nfc_eb");
+ PRINT_REG(REG_PMU_APB_SLEEP_STATUS);
+}
+uint32_t pwr_stat0=0, pwr_stat1=0, pwr_stat2=0, pwr_stat3=0, apb_eb0=0, apb_eb1=0, ahb_eb=0, apb_eb=0, mcu_pause=0, sys_force_sleep=0, sys_auto_sleep_cfg=0;
+uint32_t ldo_slp_ctrl0, ldo_slp_ctrl1, ldo_slp_ctrl2, xtl_wait_ctrl, ldo_slp_ctrl3, ldo_slp_ctrl4, ldo_aud_ctrl4;
+uint32_t mm_apb, ldo_pd_ctrl, arm_module_en;
+uint32_t cp_slp_status_dbg0, cp_slp_status_dbg1, sleep_ctrl, ddr_sleep_ctrl, sleep_status, pwr_ctrl, ca7_standby_status;
+uint32_t pd_pub_sys;
+#if defined(CONFIG_ARCH_SCX15)
+uint32_t ldo_dcdc_pd_ctrl;
+uint32_t xtl0_rel_cfg, xtl1_rel_cfg, xtl2_rel_cfg, xtlbuf0_rel_cfg, xtlbuf1_rel_cfg;
+uint32_t mpll_rel_cfg, dpll_rel_cfg, tdpll_rel_cfg, wpll_rel_cfg, cpll_rel_cfg, wifipll1_rel_cfg, wifipll2_rel_cfg;
+uint32_t ddr_chn_sleep_ctrl0, ddr_chn_sleep_ctrl1;
+#endif
+#if defined(CONFIG_ARCH_SCX35L)
+extern void cp0_power_domain_debug(void);
+extern void cp1_power_domain_debug(void);
+#endif
+
+void bak_last_reg(void)
+{
+#if defined(CONFIG_ARCH_SCX35L)
+ cp0_power_domain_debug();
+ cp1_power_domain_debug();
+#endif
+ pd_pub_sys = __raw_readl(REG_PMU_APB_PD_PUB_SYS_CFG);
+ cp_slp_status_dbg0 = __raw_readl(REG_PMU_APB_CP_SLP_STATUS_DBG0);
+#if !defined(CONFIG_ARCH_SCX35L)
+ cp_slp_status_dbg1 = __raw_readl(REG_PMU_APB_CP_SLP_STATUS_DBG1);
+#endif
+ pwr_stat0 = __raw_readl(REG_PMU_APB_PWR_STATUS0_DBG);
+ pwr_stat1 = __raw_readl(REG_PMU_APB_PWR_STATUS1_DBG);
+ pwr_stat2 = __raw_readl(REG_PMU_APB_PWR_STATUS2_DBG);
+#if !defined(CONFIG_ARCH_SCX35L)
+ pwr_stat3 = __raw_readl(REG_PMU_APB_PWR_STATUS3_DBG);
+#endif
+ sleep_ctrl = __raw_readl(REG_PMU_APB_SLEEP_CTRL);
+ ddr_sleep_ctrl = __raw_readl(REG_PMU_APB_DDR_SLEEP_CTRL);
+ sleep_status = __raw_readl(REG_PMU_APB_SLEEP_STATUS);
+
+#if defined(CONFIG_ARCH_SCX15)
+ xtl0_rel_cfg = __raw_readl(REG_PMU_APB_XTL0_REL_CFG);
+ xtl1_rel_cfg = __raw_readl(REG_PMU_APB_XTL1_REL_CFG);
+ xtl2_rel_cfg = __raw_readl(REG_PMU_APB_XTL2_REL_CFG);
+ xtlbuf0_rel_cfg = __raw_readl(REG_PMU_APB_XTLBUF0_REL_CFG);
+ xtlbuf1_rel_cfg = __raw_readl(REG_PMU_APB_XTLBUF1_REL_CFG);
+ mpll_rel_cfg = __raw_readl(REG_PMU_APB_MPLL_REL_CFG);
+ dpll_rel_cfg = __raw_readl(REG_PMU_APB_DPLL_REL_CFG);
+ tdpll_rel_cfg = __raw_readl(REG_PMU_APB_TDPLL_REL_CFG);
+ wpll_rel_cfg = __raw_readl(REG_PMU_APB_WPLL_REL_CFG);
+ cpll_rel_cfg = __raw_readl(REG_PMU_APB_CPLL_REL_CFG);
+ wifipll1_rel_cfg = __raw_readl(REG_PMU_APB_WIFIPLL1_REL_CFG);
+ wifipll2_rel_cfg = __raw_readl(REG_PMU_APB_WIFIPLL2_REL_CFG);
+ ddr_chn_sleep_ctrl0 = __raw_readl(REG_PMU_APB_DDR_CHN_SLEEP_CTRL0);
+ ddr_chn_sleep_ctrl1 = __raw_readl(REG_PMU_APB_DDR_CHN_SLEEP_CTRL1);
+#endif
+
+ apb_eb0 = __raw_readl(REG_AON_APB_APB_EB0);
+ apb_eb1 = __raw_readl(REG_AON_APB_APB_EB1);
+ pwr_ctrl = __raw_readl(REG_AON_APB_PWR_CTRL);
+
+ ahb_eb = __raw_readl(REG_AP_AHB_AHB_EB);
+
+ apb_eb = __raw_readl(REG_AP_APB_APB_EB);
+ mcu_pause = __raw_readl(REG_AP_AHB_MCU_PAUSE);
+ sys_force_sleep = __raw_readl(REG_AP_AHB_AP_SYS_FORCE_SLEEP_CFG);
+ sys_auto_sleep_cfg = __raw_readl(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG);
+ //ca7_standby_status = __raw_readl(REG_AP_AHB_CA7_STANDBY_STATUS);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ ca7_standby_status = __raw_readl(REG_AP_AHB_APCPU_STANDBY_STATUS);
+#else
+ ca7_standby_status = __raw_readl(REG_AP_AHB_CA7_STANDBY_STATUS);
+#endif
+
+#if defined(CONFIG_ADIE_SC2713S) || defined(CONFIG_ADIE_SC2713)
+ ldo_slp_ctrl0 = sci_adi_read(ANA_REG_GLB_LDO_SLP_CTRL0);
+ ldo_slp_ctrl1 = sci_adi_read(ANA_REG_GLB_LDO_SLP_CTRL1);
+ ldo_slp_ctrl2 = sci_adi_read(ANA_REG_GLB_LDO_SLP_CTRL2);
+ ldo_slp_ctrl3 = sci_adi_read(ANA_REG_GLB_LDO_SLP_CTRL3);
+#endif
+#if defined(CONFIG_ADIE_SC2723S)
+ ldo_slp_ctrl0 = sci_adi_read(ANA_REG_GLB_PWR_SLP_CTRL0);
+ ldo_slp_ctrl1 = sci_adi_read(ANA_REG_GLB_PWR_SLP_CTRL1);
+ ldo_slp_ctrl2 = sci_adi_read(ANA_REG_GLB_PWR_SLP_CTRL2);
+ ldo_slp_ctrl3 = sci_adi_read(ANA_REG_GLB_PWR_SLP_CTRL3);
+#endif
+#if defined(CONFIG_ADIE_SC2723)
+ ldo_slp_ctrl0 = sci_adi_read(ANA_REG_GLB_PWR_SLP_CTRL0);
+ ldo_slp_ctrl1 = sci_adi_read(ANA_REG_GLB_PWR_SLP_CTRL1);
+ ldo_slp_ctrl2 = sci_adi_read(ANA_REG_GLB_PWR_SLP_CTRL2);
+ ldo_slp_ctrl3 = sci_adi_read(ANA_REG_GLB_PWR_SLP_CTRL3);
+ ldo_slp_ctrl4 = sci_adi_read(ANA_REG_GLB_PWR_SLP_CTRL4);
+#endif
+
+#if defined(CONFIG_ARCH_SCX15)
+#else
+#if defined(CONFIG_ADIE_SC2713S) || defined(CONFIG_ADIE_SC2713)
+ ldo_aud_ctrl4 = sci_adi_read(ANA_REG_GLB_AUD_SLP_CTRL4);
+#endif
+#if defined(CONFIG_ADIE_SC2723S)
+ ldo_aud_ctrl4 = sci_adi_read(ANA_REG_GLB_AUD_SLP_CTRL);
+#endif
+#if defined(CONFIG_ADIE_SC2723)
+ ldo_aud_ctrl4 = sci_adi_read(ANA_REG_GLB_AUD_SLP_CTRL);
+#endif
+#endif
+ xtl_wait_ctrl = sci_adi_read(ANA_REG_GLB_XTL_WAIT_CTRL);
+#if defined(CONFIG_ARCH_SCX15)
+ ldo_dcdc_pd_ctrl = sci_adi_read(ANA_REG_GLB_LDO_DCDC_PD);
+#endif
+ ldo_pd_ctrl = sci_adi_read(ANA_REG_GLB_LDO_PD_CTRL);
+ arm_module_en = sci_adi_read(ANA_REG_GLB_ARM_MODULE_EN);
+}
+void print_last_reg(void)
+{
+ printk("aon pmu status reg\n");
+ printk("REG_PMU_APB_PD_PUB_SYS_CFG ------ 0x%08x\n", pd_pub_sys);
+ printk("REG_PMU_APB_PD_MM_TOP_CFG ------ 0x%08x\n",
+ sci_glb_read(REG_PMU_APB_PD_MM_TOP_CFG, -1UL) );
+ printk("REG_PMU_APB_PD_AP_SYS_CFG ------ 0x%08x\n",
+ sci_glb_read(REG_PMU_APB_PD_AP_SYS_CFG, -1UL) );
+#ifdef REG_PMU_APB_PD_AP_DISP_CFG
+ printk("REG_PMU_APB_PD_AP_DISP_CFG ------ 0x%08x\n",
+ sci_glb_read(REG_PMU_APB_PD_AP_DISP_CFG, -1UL) );
+#endif
+
+#if defined(CONFIG_ARCH_SCX30G)
+ printk("REG_PMU_APB_PD_DDR_PUBL_CFG ------ 0x%08x\n",
+ sci_glb_read(REG_PMU_APB_PD_DDR_PUBL_CFG, -1UL) );
+ printk("REG_PMU_APB_PD_DDR_PHY_CFG ------ 0x%08x\n",
+ sci_glb_read(REG_PMU_APB_PD_DDR_PHY_CFG, -1UL) );
+#endif
+ printk("REG_PMU_APB_CP_SLP_STATUS_DBG0 ----- 0x%08x\n", cp_slp_status_dbg0);
+#if !defined(CONFIG_ARCH_SCX35L)
+ printk("REG_PMU_APB_CP_SLP_STATUS_DBG1 ----- 0x%08x\n", cp_slp_status_dbg1);
+#endif
+ printk("REG_PMU_APB_PWR_STATUS0_DBG ----- 0x%08x\n", pwr_stat0);
+ printk("REG_PMU_APB_PWR_STATUS1_DBG ----- 0x%08x\n", pwr_stat1);
+ printk("REG_PMU_APB_PWR_STATUS2_DBG ----- 0x%08x\n", pwr_stat2);
+#if !defined(CONFIG_ARCH_SCX35L)
+ printk("REG_PMU_APB_PWR_STATUS3_DBG ----- 0x%08x\n", pwr_stat3);
+#endif
+ printk("REG_PMU_APB_SLEEP_CTRL ----- 0x%08x\n", sleep_ctrl);
+ printk("REG_PMU_APB_DDR_SLEEP_CTRL ----- 0x%08x\n", ddr_sleep_ctrl);
+ printk("REG_PMU_APB_SLEEP_STATUS ----- 0x%08x\n", sleep_status);
+#if defined(CONFIG_ARCH_SCX15)
+ printk("REG_PMU_APB_XTL0_REL_CFG ----- 0x%08x\n", xtl0_rel_cfg);
+ printk("REG_PMU_APB_XTL1_REL_CFG ----- 0x%08x\n", xtl1_rel_cfg);
+ printk("REG_PMU_APB_XTL2_REL_CFG ----- 0x%08x\n", xtl2_rel_cfg);
+ printk("REG_PMU_APB_XTLBUF0_REL_CFG ----- 0x%08x\n", xtlbuf0_rel_cfg);
+ printk("REG_PMU_APB_XTLBUF1_REL_CFG ----- 0x%08x\n", xtlbuf1_rel_cfg);
+ printk("REG_PMU_APB_MPLL_REL_CFG ----- 0x%08x\n", mpll_rel_cfg);
+ printk("REG_PMU_APB_DPLL_REL_CFG ----- 0x%08x\n", dpll_rel_cfg);
+ printk("REG_PMU_APB_TDPLL_REL_CFG ----- 0x%08x\n", tdpll_rel_cfg);
+ printk("REG_PMU_APB_WPLL_REL_CFG ----- 0x%08x\n", wpll_rel_cfg);
+ printk("REG_PMU_APB_CPLL_REL_CFG ----- 0x%08x\n", cpll_rel_cfg);
+ printk("REG_PMU_APB_WIFIPLL1_REL_CFG ----- 0x%08x\n", wifipll1_rel_cfg);
+ printk("REG_PMU_APB_WIFIPLL2_REL_CFG ----- 0x%08x\n", wifipll2_rel_cfg);
+ printk("REG_PMU_APB_DDR_CHN_SLEEP_CTRL0 ----- 0x%08x\n", ddr_chn_sleep_ctrl0);
+ printk("REG_PMU_APB_DDR_CHN_SLEEP_CTRL1 ----- 0x%08x\n", ddr_chn_sleep_ctrl1);
+#endif
+
+ printk("aon apb reg\n");
+ printk("REG_AON_APB_APB_EB0 ----- 0x%08x\n", apb_eb0);
+ printk("REG_AON_APB_APB_EB1 ----- 0x%08x\n", apb_eb1);
+ printk("REG_AON_APB_PWR_CTRL ----- 0x%08x\n", pwr_ctrl);
+ printk("REG_AON_APB_BB_BG_CTRL ------ 0x%08x\n",
+ sci_glb_read(REG_AON_APB_BB_BG_CTRL, -1UL) );
+
+ printk("ap ahb reg \n");
+ printk("REG_AP_AHB_AHB_EB ----- 0x%08x\n", ahb_eb);
+
+ printk("ap apb reg\n");
+ printk("REG_AP_APB_APB_EB ---- 0x%08x\n", apb_eb);
+ printk("REG_AP_AHB_MCU_PAUSE --- 0x%08x\n", mcu_pause);
+ printk("REG_AP_AHB_AP_SYS_FORCE_SLEEP_CFG --- 0x%08x\n", sys_force_sleep);
+ printk("REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG ---- 0x%08x\n", sys_auto_sleep_cfg);
+ printk("REG_AP_AHB_CA7_STANDBY_STATUS ---- 0x%08x\n", ca7_standby_status);
+
+ printk("ana reg \n");
+ printk("ANA_REG_GLB_LDO_SLP_CTRL0 --- 0x%08x\n", ldo_slp_ctrl0);
+ printk("ANA_REG_GLB_LDO_SLP_CTRL1 --- 0x%08x\n", ldo_slp_ctrl1);
+ printk("ANA_REG_GLB_LDO_SLP_CTRL2 --- 0x%08x\n", ldo_slp_ctrl2);
+ printk("ANA_REG_GLB_LDO_SLP_CTRL3 --- 0x%08x\n", ldo_slp_ctrl3);
+ printk("ANA_REG_GLB_AUD_SLP_CTRL4 --- 0x%08x\n", ldo_aud_ctrl4);
+ printk("ANA_REG_GLB_XTL_WAIT_CTRL --- 0x%08x\n", xtl_wait_ctrl);
+
+ printk("ANA_REG_GLB_PWR_XTL_EN0 -- 0x%08x\n", sci_adi_read(ANA_REG_GLB_PWR_XTL_EN0));
+ printk("ANA_REG_GLB_PWR_XTL_EN1 -- 0x%08x\n", sci_adi_read(ANA_REG_GLB_PWR_XTL_EN1));
+ printk("ANA_REG_GLB_PWR_XTL_EN2 -- 0x%08x\n", sci_adi_read(ANA_REG_GLB_PWR_XTL_EN2));
+ printk("ANA_REG_GLB_PWR_XTL_EN3 -- 0x%08x\n", sci_adi_read(ANA_REG_GLB_PWR_XTL_EN3));
+#if !defined(CONFIG_ADIE_SC2723) && !defined(CONFIG_ADIE_SC2723S)
+ printk("ANA_REG_GLB_PWR_XTL_EN4 -- 0x%08x\n", sci_adi_read(ANA_REG_GLB_PWR_XTL_EN4));
+#endif
+#if !defined(CONFIG_ARCH_SCX15) && !defined(CONFIG_ADIE_SC2723) && !defined(CONFIG_ADIE_SC2723S)
+ printk("ANA_REG_GLB_PWR_XTL_EN5 -- 0x%08x\n", sci_adi_read(ANA_REG_GLB_PWR_XTL_EN5));
+#endif
+ printk("mm reg\n");
+ printk("REG_MM_AHB_AHB_EB ---- 0x%08x\n", mm_apb);
+
+ printk("ana reg\n");
+#if defined(CONFIG_ARCH_SCX15)
+ printk("ANA_REG_GLB_LDO_DCDC_PD --- 0x%08x\n", ldo_dcdc_pd_ctrl);
+#endif
+ printk("ANA_REG_GLB_LDO_PD_CTRL --- 0x%08x\n", ldo_pd_ctrl);
+ printk("ANA_REG_GLB_ARM_MODULE_EN --- 0x%08x\n", arm_module_en);
+}
+#if 0
+static void test_setup_timer(int load)
+{
+ //AP_TMR0_EB
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT(12));
+
+ //AP_TMR0_RTC_EB
+ sci_glb_set(REG_AON_APB_APB_RTC_EB, BIT(5));
+
+ //load value
+ sci_glb_write(SPRD_APTIMER0_BASE, load, -1UL);
+
+ //int en
+ sci_glb_set(SPRD_APTIMER0_BASE + 0xc, BIT(0));
+
+
+ //AP_TMR0_RTC_EB --- TIMER_INT_EN timer0 INTC0 en
+ sci_glb_set(SPRD_INT_BASE + 0x8, BIT(2) | BIT(29));
+
+ //int en
+ sci_glb_set(SPRD_APTIMER0_BASE + 0x8, BIT(7));
+
+ sci_glb_write(SPRD_INT_BASE + 0x8, 0xffffffff, -1UL);
+ return;
+}
+#endif
+void check_ldo(void)
+{
+}
+
+void check_pd(void)
+{
+}
+
+unsigned int sprd_irq_pending(void)
+{
+ uint32_t bits = 0;
+ bits = sci_glb_read(SPRD_INT_BASE, -1UL);
+ if(bits)
+ return 1;
+ else
+ return 0;
+}
+
+/*copy code for deepsleep return */
+#define SAVED_VECTOR_SIZE 64
+static uint32_t *sp_pm_reset_vector = NULL;
+static uint32_t saved_vector[SAVED_VECTOR_SIZE];
+void __iomem *iram_start;
+struct emc_repower_param *repower_param;
+
+
+#define SPRD_RESET_VECTORS 0X00000000
+#define SLEEP_RESUME_CODE_PHYS 0X400
+#define SLEEP_CODE_SIZE 0x800
+#define SPRD_IRAM0_PHYS 0x0
+
+extern unsigned long reg_aon_apb_eb0_vir;
+extern unsigned long reg_aon_apb_eb0_phy;
+extern unsigned long reg_uart1_enable_vir;
+extern unsigned long reg_uart1_enable_phy;
+
+static int init_reset_vector(void)
+{
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ sp_pm_reset_vector = (u32 *)SPRD_IRAM0_BASE;
+
+ reg_aon_apb_eb0_vir = SPRD_AONAPB_BASE;
+ reg_aon_apb_eb0_phy = SPRD_AONAPB_PHYS;
+ reg_uart1_enable_vir = SPRD_APBREG_BASE;
+ reg_uart1_enable_phy = SPRD_APBREG_PHYS;
+
+ iram_start = (void __iomem *)(SPRD_IRAM0_BASE + SLEEP_RESUME_CODE_PHYS);
+ /* copy sleep code to (IRAM). */
+ if ((sc8830_standby_iram_end - sc8830_standby_iram) > SLEEP_CODE_SIZE) {
+ panic("##: code size is larger than expected, need more memory!\n");
+ }
+ memcpy_toio(iram_start, sc8830_standby_iram, SLEEP_CODE_SIZE);
+
+
+ /* just make sure*/
+ flush_cache_all();
+ outer_flush_all();
+ sp_pm_reset_vector[65] = virt_to_phys(sp_pm_collapse_exit);
+#endif
+ return 0;
+}
+void save_reset_vector(void)
+{
+ int i = 0;
+ for (i = 0; i < SAVED_VECTOR_SIZE; i++)
+ saved_vector[i] = sp_pm_reset_vector[i];
+}
+
+void set_reset_vector(void)
+{
+ int i = 0;
+ printk("SAVED_VECTOR_SIZE 0x%x\n", SAVED_VECTOR_SIZE);
+ for (i = 0; i < SAVED_VECTOR_SIZE; i++)
+ sp_pm_reset_vector[i] = 0xe320f000; /* nop*/
+
+ sp_pm_reset_vector[SAVED_VECTOR_SIZE - 2] = 0xE51FF004; /* ldr pc, 4 */
+
+ sp_pm_reset_vector[SAVED_VECTOR_SIZE - 1] = (sc8830_standby_exit_iram -
+ sc8830_standby_iram + SLEEP_RESUME_CODE_PHYS); /* place v7_standby_iram here */
+}
+
+#ifdef CONFIG_DDR_VALIDITY_TEST
+#include <linux/slab.h>
+static unsigned long *vtest = NULL;
+static unsigned long *ptest = NULL;
+static void test_memory(void)
+{
+ int i;
+#ifndef CONFIG_ARCH_SCX30G
+ vtest = kmalloc(64*1024, GFP_KERNEL);
+ if (vtest) {
+ for (i = 0; i < (64*1024/4); i++) {
+ vtest[i] = 0x12345678;
+ }
+#else
+ vtest = kmalloc(2 * sizeof(int), GFP_KERNEL);
+ if (vtest) {
+ vtest[0] = 0x12345678;
+#endif
+ printk("%s %p %p\n", __func__, vtest, virt_to_phys(vtest));
+ ptest = virt_to_phys(vtest);
+ } else {
+ printk("error kmalloc\n");
+ }
+ sp_pm_reset_vector[64] = ptest;
+}
+#endif
+
+void restore_reset_vector(void)
+{
+ int i;
+ for (i = 0; i < SAVED_VECTOR_SIZE; i++)
+ sp_pm_reset_vector[i] = saved_vector[i];
+}
+/* irq functions */
+#define hw_raw_irqs_disabled_flags(flags) \
+({ \
+ (int)((flags) & PSR_I_BIT); \
+})
+
+#define hw_irqs_disabled() \
+({ \
+ unsigned long _flags; \
+ local_irq_save(_flags); \
+ hw_raw_irqs_disabled_flags(_flags); \
+})
+
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+u32 __attribute__ ((naked)) read_cpsr(void)
+{
+ __asm__ __volatile__("mrs r0, cpsr\nbx lr");
+}
+#endif
+/* make sure printk is end, if not maybe some messy code in SERIAL1 output */
+#define UART_TRANSFER_REALLY_OVER (0x1UL << 15)
+#define UART_STS0 (SPRD_UART1_BASE + 0x08)
+#define UART_STS1 (SPRD_UART1_BASE + 0x0c)
+
+static __used void wait_until_uart1_tx_done(void)
+{
+ u32 tx_fifo_val;
+ u32 really_done = 0;
+ u32 timeout = 2000;
+
+ tx_fifo_val = __raw_readl(UART_STS1);
+ tx_fifo_val >>= 8;
+ tx_fifo_val &= 0xff;
+ while(tx_fifo_val != 0) {
+ if (timeout <= 0) break;
+ udelay(100);
+ tx_fifo_val = __raw_readl(UART_STS1);
+ tx_fifo_val >>= 8;
+ tx_fifo_val &= 0xff;
+ timeout--;
+ }
+
+ timeout = 30;
+ really_done = __raw_readl(UART_STS0);
+ while(!(really_done & UART_TRANSFER_REALLY_OVER)) {
+ if (timeout <= 0) break;
+ udelay(100);
+ really_done = __raw_readl(UART_STS0);
+ timeout--;
+ }
+}
+#define SAVE_GLOBAL_REG do{ \
+ bak_restore_apb(1); \
+ bak_ap_clk_reg(1); \
+ bak_restore_ahb(1); \
+ bak_restore_aon(1); \
+ bak_restore_pub(1); \
+ bak_restore_ap_intc(1);\
+ }while(0)
+#define RESTORE_GLOBAL_REG do{ \
+ bak_restore_apb(0); \
+ bak_ap_clk_reg(0); \
+ bak_restore_aon(0); \
+ bak_restore_ahb(0); \
+ bak_restore_pub(0); \
+ bak_restore_ap_intc(0);\
+ }while(0)
+
+/* arm core sleep*/
+static void arm_sleep(void)
+{
+ cpu_do_idle();
+ hard_irq_set();
+}
+
+/* arm core + arm sys */
+static void mcu_sleep(void)
+{
+ SAVE_GLOBAL_REG;
+ sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_SYS_SLEEP_EN);
+ cpu_do_idle();
+ sci_glb_clr(REG_AP_AHB_MCU_PAUSE, BIT_MCU_SYS_SLEEP_EN);
+ hard_irq_set();
+ RESTORE_GLOBAL_REG;
+}
+
+static void light_sleep(void)
+{
+ sci_glb_set(REG_AP_AHB_MCU_PAUSE, BIT_MCU_LIGHT_SLEEP_EN);
+ cpu_do_idle();
+ sci_glb_clr(REG_AP_AHB_MCU_PAUSE, BIT_MCU_LIGHT_SLEEP_EN);
+}
+void int_work_round(void)
+{
+ //sci_glb_set(SPRD_AONAPB_BASE + 0x8, (0x1<<30 | 1<<8 /*| 1<<1*/));
+ //sci_glb_clr(SPRD_LPDDR2_PHY_BASE + 0x2c, BIT(4));
+ //sci_glb_set(SPRD_LPDDR2_PHY_BASE + 0x2c, BIT(4));
+ //sci_glb_write(SPRD_LPDDR2_BASE + 0x30, 0, -1UL);
+ //sci_glb_set(SPRD_PMU_BASE + 0xa8, 1<<1);
+ //sci_adi_clr(ANA_EIC_BASE + 0x18, 0x20);
+}
+void show_deep_reg_status(void)
+{
+ printk("PWR_STATUS0_DBG 0x%08x\n", sci_glb_read(REG_PMU_APB_PWR_STATUS0_DBG, -1UL));
+ printk("PWR_STATUS1_DBG 0x%08x\n", sci_glb_read(REG_PMU_APB_PWR_STATUS1_DBG, -1UL));
+ printk("PWR_STATUS2_DBG 0x%08x\n", sci_glb_read(REG_PMU_APB_PWR_STATUS2_DBG, -1UL));
+#if !defined(CONFIG_ARCH_SCX35L)
+ printk("PWR_STATUS3_DBG 0x%08x\n", sci_glb_read(REG_PMU_APB_PWR_STATUS3_DBG, -1UL));
+#endif /* CONFIG_ARCH_SCX35L */
+}
+
+
+#if defined(CONFIG_MACH_SC9620OPENPHONE)
+void cp0_sys_power_domain_close(void)
+{
+ sci_glb_set(REG_PMU_APB_PD_CP0_SYS_CFG, BIT_CP0_FORCE_DEEP_SLEEP);//enable cp0 force sleep
+}
+
+void cp0_sys_power_domain_open(void)
+{
+ sci_glb_set(REG_PMU_APB_CP_SOFT_RST, BIT_CP0_SOFT_RST);//reset cp0
+ sci_glb_clr(REG_PMU_APB_PD_CP0_SYS_CFG, BIT_CP0_FORCE_DEEP_SLEEP);//close cp0 force sleep
+ mdelay(2);
+ sci_glb_clr(REG_PMU_APB_CP_SOFT_RST, BIT_CP0_SOFT_RST);//release cp0
+}
+#endif
+
+extern void pm_debug_set_wakeup_timer(void);
+extern void pm_debug_set_apwdt(void);
+typedef u32 (*iram_standby_entry_ptr)(u32,u32);
+static int sc_sleep_call(unsigned long flag)
+{
+ u32 ret = 0;
+ struct mm_struct *mm = current->active_mm;
+
+ iram_standby_entry_ptr func_ptr;
+ cpu_switch_mm(init_mm.pgd, &init_mm);
+ func_ptr = (iram_standby_entry_ptr)(SLEEP_RESUME_CODE_PHYS + (u32)sp_pm_collapse - (u32)sc8830_standby_iram);
+ ret = (func_ptr)(0, flag);
+ cpu_switch_mm(mm->pgd, mm);
+
+ return ret;
+}
+
+int deep_sleep(int from_idle)
+{
+ int ret = 0;
+ static unsigned int cnt = 0;
+
+ if(!from_idle){
+ SAVE_GLOBAL_REG;
+ /* some prepare here */
+#if defined(CONFIG_ARCH_SCX15)
+ bak_restore_mm_scx15(1);
+#endif
+ show_pin_reg();
+ enable_mcu_deep_sleep();
+ configure_for_deepsleep(0);
+ disable_ahb_module();
+ //disable_pmu_ddr_module();
+
+ //sci_glb_set(SPRD_PMU_BASE+0x00F4, 0x3FF);
+
+ disable_dma();
+ //disable_mm();
+ //disable_ana_module();
+ disable_aon_module();
+ show_reg_status();
+ bak_last_reg();
+ print_last_reg();
+ print_int_status();
+ //wait_until_uart1_tx_done();
+ int_work_round();
+ //pm_debug_set_apwdt();
+ disable_apb_module();
+ //pm_debug_set_wakeup_timer();
+ //force_mcu_core_sleep();
+ //bak_last_reg();
+
+#if defined(CONFIG_ARCH_SCX20L)
+ __raw_writel(0x20, REG_ARM7_AHB_RF_AP_COMM_CTL);
+ /*for pikeL, AP can't read/write ARM7 register when ARM7 sleep.
+ * so, disable ARM7 BUS sleep when AP wake, enable when AP sleep*/
+ sci_glb_set(REG_AON_APB_ARM7_CFG_BUS, BIT_ARM7_CFG_BUS_SLEEP);
+#else
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ __raw_writel(0x0, REG_PMU_APB_CA7_C0_CFG);
+#endif
+#endif
+ show_deep_reg_status();
+ } else {
+ /* __raw_writel(0x0, REG_PMU_APB_CA7_C0_CFG); */
+ }
+ /*
+ * sp_pm_collapse(param0, param1)
+ * param0, param1 are not used before
+ * so we use second param to distinguish idle deep or real deep
+ */
+#define dmc_retention_func (SPRD_IRAM1_BASE + 0x1800)
+#define dmc_retention_para_vaddr (SPRD_IRAM1_BASE + 0x2400)
+
+#ifdef CONFIG_ARCH_SCX20
+ /* set auto-self refresh mode */
+ sci_glb_clr(SPRD_LPDDR2_BASE+0x124, BIT(0)| BIT(1));
+ sci_glb_set(SPRD_LPDDR2_BASE+0X124, BIT(2));
+#else
+#if defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L) || defined(CONFIG_ARCH_SCX35L64) || defined(CONFIG_ARCH_SCX35LT8)
+ /* set auto-self refresh mode */
+ sci_glb_clr(SPRD_LPDDR2_BASE+0x30, BIT(1));
+ sci_glb_set(SPRD_LPDDR2_BASE+0x30, BIT(0));
+#if defined(CONFIG_ARCH_SCX30G)
+ *(volatile unsigned int*)(SPRD_IRAM1_BASE+0x1800 + 0xc00 + 0x0c) = SPRD_PUB_BASE;
+ *(volatile unsigned int*)(SPRD_IRAM1_BASE+0x1800 + 0xc00 + 0x10) = SPRD_LPDDR2_BASE;
+ *(volatile unsigned int*)(SPRD_IRAM1_BASE+0x1800 + 0xc00 + 0x14) = SPRD_LPDDR2_PHY_BASE;
+ ((void (*)(unsigned long,unsigned long))(dmc_retention_func))(1,dmc_retention_para_vaddr);
+#endif
+#endif
+#endif
+#if defined(CONFIG_MACH_SC9620OPENPHONE)
+ cp0_sys_power_domain_close();
+#endif
+#if defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8)
+ ret = cpu_suspend(5);
+ ret = (ret ? 0 : 1);
+#else
+ ret = sc_sleep_call(from_idle);
+#endif
+#if defined(CONFIG_MACH_SC9620OPENPHONE)
+ cp0_sys_power_domain_open();
+#endif
+
+#if defined(CONFIG_ARCH_SCX20L)
+ sci_glb_clr(REG_AON_APB_ARM7_CFG_BUS, BIT_ARM7_CFG_BUS_SLEEP);
+#endif
+
+#ifdef CONFIG_ARCH_SCX20
+ /* set auto powerdown mode */
+ sci_glb_set(SPRD_LPDDR2_BASE+0x124, BIT(0)| BIT(1));
+ sci_glb_clr(SPRD_LPDDR2_BASE+0x124, BIT(2));
+#else
+#if defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L) || defined(CONFIG_ARCH_SCX35L64) || defined(CONFIG_ARCH_SCX35LT8)
+ /* set auto powerdown mode */
+ sci_glb_set(SPRD_LPDDR2_BASE+0x30, BIT(1));
+ sci_glb_clr(SPRD_LPDDR2_BASE+0x30, BIT(0));
+#endif
+#endif
+
+ udelay(50);
+
+
+ if(!from_idle){
+#if defined(CONFIG_ARCH_SCX20L)
+ __raw_writel(0x30, REG_ARM7_AHB_RF_AP_COMM_CTL);
+#else
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ __raw_writel(0x1, REG_PMU_APB_CA7_C0_CFG);
+#endif
+#endif
+ printk("ret %d not from idle\n", ret);
+ if(ret){
+#if defined(CONFIG_DDR_VALIDITY_TEST) && defined(CONFIG_ARCH_SCX30G)
+ printk("DDR test: read times: %ld\n", *(vtest + 1));
+#endif
+ printk("deep sleep %u times\n", cnt);
+ cnt++;
+ }
+ sci_glb_set(REG_AP_APB_APB_EB, 0xf<<19);
+ hard_irq_set();
+ sci_glb_clr(REG_AP_APB_APB_EB, 0xf<<19);
+ disable_mcu_deep_sleep();
+ configure_for_deepsleep(1);
+ //sci_glb_clr(SPRD_PMU_BASE+0x00F4, 0x3FF);
+ RESTORE_GLOBAL_REG;
+#if defined(CONFIG_ARCH_SCX15)
+ bak_restore_mm_scx15(0);
+#endif
+ } else {
+ /* __raw_writel(0x1, REG_PMU_APB_CA7_C0_CFG); */
+ pr_debug("ret %d from idle\n", ret);
+ }
+
+ udelay(5);
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ if (ret) cpu_init();
+#endif
+
+ if (soc_is_scx35_v0())
+ //sci_glb_clr(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG,BIT_CA7_CORE_AUTO_GATE_EN);
+#if defined(CONFIG_ARCH_SCX35LT8)
+ sci_glb_clr(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG,BIT_CA53_CORE_AUTO_GATE_EN);
+#else
+ sci_glb_clr(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG,BIT_CA7_CORE_AUTO_GATE_EN);
+#endif
+
+ return ret;
+}
+
+#define DEVICE_AHB (0x1UL << 20)
+#define DEVICE_APB (0x1UL << 21)
+#define DEVICE_VIR (0x1UL << 22)
+#define DEVICE_AWAKE (0x1UL << 23)
+#define LIGHT_SLEEP (0x1UL << 24)
+#define DEVICE_TEYP_MASK (DEVICE_AHB | DEVICE_APB | DEVICE_VIR | DEVICE_AWAKE | LIGHT_SLEEP)
+
+int sprd_cpu_deep_sleep(unsigned int cpu)
+{
+ int status, ret = 0;
+ unsigned long flags, time;
+
+#ifdef CONFIG_SPRD_PM_DEBUG
+ __raw_writel(0xfdffbfff, SPRD_INTC0_BASE + 0xc);//intc0
+ __raw_writel(0x02004000, SPRD_INTC0_BASE + 0x8);//intc0
+ __raw_writel(0xffffffff, SPRD_INTC0_BASE + 0x100c);//intc1
+#endif
+
+ time = get_sys_cnt();
+ if (!hw_irqs_disabled()) {
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ flags = read_cpsr();
+#endif
+ printk("##: Error(%s): IRQ is enabled(%08lx)!\n",
+ "wakelock_suspend", flags);
+ }
+ /*TODO:
+ * we need to known clock status in modem side
+ */
+#ifdef FORCE_DISABLE_DSP
+ status = 0;
+#else
+ /*
+ * TODO: get clock status in native version, force deep sleep now
+ */
+ status = 0;
+#endif
+ if (status & DEVICE_AHB) {
+ printk("###### %s, DEVICE_AHB ###\n", __func__ );
+ set_sleep_mode(SLP_MODE_ARM);
+ arm_sleep();
+ } else if (status & DEVICE_APB) {
+ printk("###### %s, DEVICE_APB ###\n", __func__ );
+ set_sleep_mode(SLP_MODE_MCU);
+ mcu_sleep();
+ } else if (status & LIGHT_SLEEP) {
+ printk("###### %s, DEVICE_APB ###\n", __func__ );
+ set_sleep_mode(SLP_MODE_LIT);
+ light_sleep();
+ } else {
+ /*printk("###### %s, DEEP ###\n", __func__ );*/
+ set_sleep_mode(SLP_MODE_DEP);
+#if 1
+
+ ret = deep_sleep(0);
+#else
+ //pm_debug_set_wakeup_timer();
+ ret = 0;
+ arm_sleep();
+#endif
+ flush_cache_all();
+ }
+
+ time_add(get_sys_cnt() - time, ret);
+ print_hard_irq_inloop(ret);
+
+ return ret;
+}
+static void init_gr(void){}
+void sc_default_idle(void)
+{
+ cpu_do_idle();
+ local_irq_enable();
+ return;
+}
+
+/*config dcdc core deep sleep voltage*/
+static void dcdc_core_ds_config(void)
+{
+ u32 dcdc_core_ctl_adi = 0;
+ u32 val = 0;
+ u32 dcdc_core_ctl_ds = -1;
+#if defined(CONFIG_ADIE_SC2713S) || defined(CONFIG_ADIE_SC2713)
+#ifdef CONFIG_ARCH_SCX30G
+ static struct dcdc_core_ds_step_info step_info[5]={
+ {0, 0,0, 0},
+ {0, 3,0, 5},
+ {0, 6,0,10},
+ {0, 9,0, 0},
+ {0,12,0, 5}
+ };
+ step_info[0].ctl_reg = ANA_REG_GLB_MP_PWR_CTRL1;
+ step_info[1].ctl_reg = ANA_REG_GLB_MP_PWR_CTRL1;
+ step_info[2].ctl_reg = ANA_REG_GLB_MP_PWR_CTRL1;
+ step_info[3].ctl_reg = ANA_REG_GLB_MP_PWR_CTRL1;
+ step_info[4].ctl_reg = ANA_REG_GLB_MP_PWR_CTRL1;
+ step_info[0].cal_reg = ANA_REG_GLB_MP_PWR_CTRL2;
+ step_info[1].cal_reg = ANA_REG_GLB_MP_PWR_CTRL2;
+ step_info[2].cal_reg = ANA_REG_GLB_MP_PWR_CTRL2;
+ step_info[3].cal_reg = ANA_REG_GLB_MP_PWR_CTRL3;
+ step_info[4].cal_reg = ANA_REG_GLB_MP_PWR_CTRL3;
+
+ static u8 dcdc_core_down_volt[]={4,1,1,2,3,5,0,6};
+ static u8 dcdc_core_up_volt[]={6,2,3,4,0,1,7,7};
+ u32 dcdc_core_cal_adi,i;
+ /*1100,700,800,900,1000,650,1200,1300*/
+ static u32 step_ratio[]={10,10,6,3,3};
+ dcdc_core_ctl_adi = (sci_adi_read(ANA_REG_GLB_MP_MISC_CTRL) >> 3) & 0x7;
+ if((ana_chip_id == 0x2713ca00)||(ana_chip_id == 0x2723a000)) {
+ dcdc_core_ctl_ds = dcdc_core_ctl_adi;
+ } else {
+ dcdc_core_ctl_ds = dcdc_core_down_volt[dcdc_core_ctl_adi];
+ }
+ printk("ana_chip_id:%x, dcdc_core_ctl_adi = %d, dcdc_core_ctl_ds = %d\n"
+ ,ana_chip_id, dcdc_core_ctl_adi, dcdc_core_ctl_ds);
+#if defined(CONFIG_ADIE_SC2713S) || defined(CONFIG_ADIE_SC2713)
+ val = sci_adi_read(ANA_REG_GLB_DCDC_SLP_CTRL);
+#endif
+#if defined(CONFIG_ADIE_SC2723S)
+ val = sci_adi_read(ANA_REG_GLB_DCDC_SLP_CTRL0);
+#endif
+ val &= ~0x7;
+ val |= dcdc_core_ctl_ds;
+#if defined(CONFIG_ADIE_SC2713S) || defined(CONFIG_ADIE_SC2713)
+ sci_adi_write(ANA_REG_GLB_DCDC_SLP_CTRL, val, 0xffff);
+#endif
+#if defined(CONFIG_ADIE_SC2723S)
+ sci_adi_write(ANA_REG_GLB_DCDC_SLP_CTRL0, val, 0xffff);
+#endif
+
+ if(dcdc_core_ctl_ds < dcdc_core_ctl_adi){
+ dcdc_core_cal_adi = sci_adi_read(ANA_REG_GLB_DCDC_CORE_ADI) & 0x1F;
+ /*last step must equel function mode */
+ sci_adi_write(step_info[4].ctl_reg,dcdc_core_ctl_adi<<step_info[4].ctl_sht,0x07 << step_info[4].ctl_sht);
+ sci_adi_write(step_info[4].cal_reg,dcdc_core_cal_adi<<step_info[4].cal_sht,0x1F << step_info[4].cal_sht);
+
+ for(i=0;i<4;i++) {
+ val = dcdc_core_cal_adi + step_ratio[i];
+ if(val <= 0x1F) {
+ sci_adi_write(step_info[i].ctl_reg,dcdc_core_ctl_ds<<step_info[i].ctl_sht,0x07<<step_info[i].ctl_sht);
+ sci_adi_write(step_info[i].cal_reg,val<<step_info[i].cal_sht,0x1F << step_info[i].cal_sht);
+ dcdc_core_cal_adi = val;
+ } else {
+ sci_adi_write(step_info[i].ctl_reg,dcdc_core_up_volt[dcdc_core_ctl_ds]<<step_info[i].ctl_sht,
+ 0x07 << step_info[i].ctl_sht);
+ sci_adi_write(step_info[i].cal_reg,(val-0x1F)<<step_info[i].cal_sht,0x1F << step_info[i].cal_sht);
+ dcdc_core_ctl_ds = dcdc_core_up_volt[dcdc_core_ctl_ds];
+ dcdc_core_cal_adi = val - 0x1F;
+ }
+ }
+ } else {
+ dcdc_core_cal_adi = sci_adi_read(ANA_REG_GLB_DCDC_CORE_ADI) & 0x1F;
+ for(i=0;i<5;i++){
+ sci_adi_write(step_info[i].ctl_reg,dcdc_core_ctl_adi<<step_info[i].ctl_sht,0x07<<step_info[i].ctl_sht);
+ sci_adi_write(step_info[i].cal_reg,dcdc_core_cal_adi<<step_info[i].cal_sht,0x1F<<step_info[i].cal_sht);
+ }
+ }
+#else
+ dcdc_core_ctl_adi = sci_adi_read(ANA_REG_GLB_DCDC_CORE_ADI);
+ dcdc_core_ctl_adi = dcdc_core_ctl_adi >> 0x5;
+ dcdc_core_ctl_adi = dcdc_core_ctl_adi & 0x7;
+ /*only support dcdc_core_ctl_adi 0.9v,1.0v, 1.1v, 1.2v, 1.3v*/
+ switch(dcdc_core_ctl_adi) {
+ case 0://1.1v
+ dcdc_core_ctl_ds = 0x4; //1.0v
+ break;
+ case 4: //1.0v
+ dcdc_core_ctl_ds = 0x3;//0.9v
+ break;
+ case 6: //1.2v
+ dcdc_core_ctl_ds = 0x0;//1.1v
+ break;
+ case 7: //1.3v
+ dcdc_core_ctl_ds = 0x6;//1.2v
+ break;
+ case 3: //0.9v
+ dcdc_core_ctl_ds = 0x2;//0.8v
+ break;
+ case 1: //0.7v
+ case 2: //0.8v
+ case 5: //0.65v
+ //unvalid value
+ break;
+ }
+ printk("dcdc_core_ctl_adi = %d, dcdc_core_ctl_ds = %d\n", dcdc_core_ctl_adi, dcdc_core_ctl_ds);
+ /*valid value*/
+ if(dcdc_core_ctl_ds != -1) {
+#ifndef CONFIG_ARCH_SCX15
+#if defined(CONFIG_ADIE_SC2713S) || defined(CONFIG_ADIE_SC2713)
+ val = sci_adi_read(ANA_REG_GLB_DCDC_SLP_CTRL);
+ val &= ~(0x7);
+ val |= dcdc_core_ctl_ds;
+ sci_adi_write(ANA_REG_GLB_DCDC_SLP_CTRL, val, 0xffff);
+#endif
+#if defined(CONFIG_ADIE_SC2723S)
+ val = sci_adi_read(ANA_REG_GLB_DCDC_SLP_CTRL0);
+ val &= ~(0x7);
+ val |= dcdc_core_ctl_ds;
+ sci_adi_write(ANA_REG_GLB_DCDC_SLP_CTRL0, val, 0xffff);
+#endif
+#if defined(CONFIG_ADIE_SC2723)
+ val = sci_adi_read(ANA_REG_GLB_DCDC_SLP_CTRL1);
+ val &= ~(0xF);
+ val |= dcdc_core_ctl_ds;
+ sci_adi_write(ANA_REG_GLB_DCDC_SLP_CTRL1, val, 0xffff);
+#endif
+#else
+ val = sci_adi_read(ANA_REG_GLB_DCDC_SLP_CTRL0);
+ val &= ~(0x7 << 4);
+ val |= (dcdc_core_ctl_ds << 4);
+ sci_adi_write(ANA_REG_GLB_DCDC_SLP_CTRL0, val, 0xffff);
+#endif
+ }
+#endif
+#endif
+}
+
+void pm_ana_ldo_config(void)
+{
+#ifdef CONFIG_ARCH_SCX30G
+ ana_chip_id = (sci_adi_read(ANA_REG_GLB_CHIP_ID_HIGH) & 0xFFFF) << 16;
+ ana_chip_id |= (sci_adi_read(ANA_REG_GLB_CHIP_ID_LOW) & 0xFFFF);
+#endif
+
+#if defined(CONFIG_ARCH_SCX15) || defined(CONFIG_ADIE_SC2723)
+ dcdc_core_ds_config();
+#else
+ /*set vddcore deep sleep voltage to 0.9v*/
+ //sci_adi_set(ANA_REG_GLB_DCDC_SLP_CTRL, BITS_DCDC_CORE_CTL_DS(3));
+ dcdc_core_ds_config();
+ /*open vddcore lp VDDMEM, DCDCGEN mode*/
+ //sci_adi_set(ANA_REG_GLB_LDO_SLP_CTRL2, BIT_SLP_DCDCCORE_LP_EN | BIT_SLP_DCDCMEM_LP_EN | BIT_SLP_DCDCGEN_LP_EN);
+ /*open vdd28, vdd18 lp mode*/
+ //sci_adi_set(ANA_REG_GLB_LDO_SLP_CTRL3, BIT_SLP_LDOVDD28_LP_EN | BIT_SLP_LDOVDD18_LP_EN);
+ /*ddr2_buf quiesent curretn set to 4-5uA in deep sleep mode*/
+#if defined(CONFIG_ADIE_SC2713S) || defined(CONFIG_ADIE_SC2713)
+ sci_adi_clr(ANA_REG_GLB_DDR2_CTRL, BITS_DDR2_BUF_S_DS(0x3));
+#endif
+#endif
+}
+static void init_led(void){}
+
+void sprd_pbint_7s_reset_disable(void)
+{
+ sci_adi_set(ANA_REG_GLB_POR_7S_CTRL, BIT_PBINT_7S_RST_DISABLE);
+}
+
+#ifdef CONFIG_SPRD_EXT_IC_POWER
+// EXT IC should disable otg mode when power off
+ extern void sprd_extic_otg_power(int enable);
+#endif
+
+static void sc8830_power_off(void)
+{
+ u32 reg_val;
+ /*turn off all modules's ldo*/
+
+#ifdef CONFIG_SPRD_EXT_IC_POWER
+ sprd_extic_otg_power(0);
+#endif
+ sci_adi_raw_write(ANA_REG_GLB_LDO_PD_CTRL, 0x1fff);
+
+#if defined(CONFIG_ARCH_SCX15) || defined(CONFIG_ADIE_SC2723) || defined(CONFIG_ADIE_SC2723S)
+ sci_adi_raw_write(ANA_REG_GLB_PWR_WR_PROT_VALUE,BITS_PWR_WR_PROT_VALUE(0x6e7f));
+ do{
+ reg_val = (sci_adi_read(ANA_REG_GLB_PWR_WR_PROT_VALUE) & BIT_PWR_WR_PROT);
+ }while(reg_val == 0);
+ sci_adi_raw_write(ANA_REG_GLB_LDO_PD_CTRL,0xfff);
+ sci_adi_raw_write(ANA_REG_GLB_LDO_DCDC_PD,0x7fff);
+#endif
+
+#if !defined(CONFIG_64BIT)
+#if defined(CONFIG_ADIE_SC2713S) || defined(CONFIG_ADIE_SC2713)
+ /*turn off system core's ldo*/
+ sci_adi_raw_write(ANA_REG_GLB_LDO_DCDC_PD_RTCCLR, 0x0);
+ sci_adi_raw_write(ANA_REG_GLB_LDO_DCDC_PD_RTCSET, 0X7fff);
+#endif
+#endif
+
+}
+
+
+static void sc8830_machine_restart(char mode, const char *cmd)
+{
+ local_irq_disable();
+ local_fiq_disable();
+
+ arch_reset(mode, cmd);
+
+ mdelay(1000);
+
+ printk("reboot failed!\n");
+
+ while (1);
+}
+void resume_code_remap(void)
+{
+ int ret;
+ ret = ioremap_page_range(SPRD_IRAM0_PHYS, SPRD_IRAM0_PHYS+SZ_8K, SPRD_IRAM0_PHYS, PAGE_KERNEL_EXEC);
+ if(ret){
+ printk("resume_code_remap err %d\n", ret);
+ BUG();
+ }
+}
+void __init sc_pm_init(void)
+{
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ init_reset_vector();
+ resume_code_remap();
+#endif
+ pm_power_off = sc8830_power_off;
+ arm_pm_restart = sc8830_machine_restart;
+ pr_info("power off %pf, restart %pf\n", pm_power_off, arm_pm_restart);
+ init_gr();
+ setup_autopd_mode();
+ pm_ana_ldo_config();
+ init_led();
+ /* disable all sleep mode */
+ sci_glb_clr(REG_AP_AHB_MCU_PAUSE, BIT_MCU_DEEP_SLEEP_EN | BIT_MCU_LIGHT_SLEEP_EN | \
+ BIT_MCU_SYS_SLEEP_EN | BIT_MCU_CORE_SLEEP);
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ set_reset_vector();
+#endif
+#ifdef CONFIG_DDR_VALIDITY_TEST
+ test_memory();
+#endif
+#ifndef CONFIG_SPRD_PM_DEBUG
+ pm_debug_init();
+#endif
+
+ /* enable arm clock auto gating*/
+ //sci_glb_set(REG_AHB_AHB_CTL1, BIT_ARM_AUTO_GATE_EN);
+#ifdef CONFIG_CPU_IDLE
+#ifndef CONFIG_64BIT
+ /* Used to armv7 */
+ sc_cpuidle_init();
+#endif
+#endif
+/*
+ wake_lock_init(&pm_debug_lock, WAKE_LOCK_SUSPEND, "pm_not_ready");
+ wake_lock(&pm_debug_lock);
+*/
+#if defined(CONFIG_SPRD_DEBUG)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ sprd_debug_init();
+#endif
+#endif
+}
diff --git a/drivers/platform/sprd/pm.c b/drivers/platform/sprd/pm.c
new file mode 100644
index 00000000..97bb899a
--- /dev/null
+++ b/drivers/platform/sprd/pm.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+extern void sc_pm_init(void);
+
+#include <asm/irqflags.h>
+#include <soc/sprd/pm_debug.h>
+#include <soc/sprd/globalregs.h>
+#if defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8)
+#include <soc/sprd/sprd_persistent_clock.h>
+#endif
+
+extern unsigned int sprd_irq_pending(void);
+extern int sprd_cpu_deep_sleep(unsigned int cpu);
+extern void sc_pm_init(void);
+extern void check_ldo(void);
+extern void check_pd(void);
+
+#if 0 /*comment for bug 179358 */
+/*for battery*/
+#define BATTERY_CHECK_INTERVAL 30000
+extern int battery_updata(void);
+
+static int __used sprd_check_battery(void)
+{
+ int ret_val = 0;
+ if (battery_updata()) {
+ ret_val = 1;
+ }
+ return ret_val;
+}
+#endif
+
+static void sprd_pm_standby(void)
+{
+ cpu_do_idle();
+}
+
+static int sprd_pm_deepsleep(suspend_state_t state)
+{
+ int ret_val = 0;
+ unsigned long flags;
+ unsigned int cpu = smp_processor_id();
+ u32 battery_time, cur_time;
+ battery_time = cur_time = get_sys_cnt();
+
+ /* add for debug & statisic*/
+ clr_sleep_mode();
+ time_statisic_begin();
+
+ /*
+ * when we get here, only boot cpu still alive
+ */
+ if( cpu ){
+ __WARN();
+ goto enter_exit;
+ }
+ printk("cpu%d, enter %s\n", cpu, __func__ );
+
+ while(1){
+ local_fiq_disable();
+ local_irq_save(flags);
+ WARN_ONCE(!irqs_disabled(), "#####: Interrupts enabled in pm_enter()!\n");
+
+ sprd_cpu_deep_sleep(cpu);
+
+ if( sprd_irq_pending() ){
+ irq_wakeup_set();
+ local_irq_restore(flags);
+ local_fiq_enable();
+ break;
+ }
+
+ /*TODO: charging in power down
+ cur_time = get_sys_cnt();
+ if ((cur_time - battery_time) > BATTERY_CHECK_INTERVAL) {
+ battery_time = cur_time;
+ if (sprd_check_battery()) {
+ printk("###: battery low!\n");
+ break;
+ }
+ }
+ */
+
+ }/*end while*/
+
+ time_statisic_end();
+
+enter_exit:
+ return ret_val;
+}
+
+
+static int sprd_pm_enter(suspend_state_t state)
+{
+ int rval = 0;
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ sprd_pm_standby( );
+ break;
+ case PM_SUSPEND_MEM:
+ rval = sprd_pm_deepsleep(state);
+ break;
+ default:
+ break;
+ }
+
+ return rval;
+}
+
+static int sprd_pm_valid(suspend_state_t state)
+{
+ pr_debug("pm_valid: %d\n", state);
+ switch (state) {
+ case PM_SUSPEND_ON:
+ case PM_SUSPEND_STANDBY:
+ case PM_SUSPEND_MEM:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int sprd_pm_prepare(void)
+{
+ pr_debug("enter %s\n", __func__);
+ check_ldo();
+ check_pd();
+ return 0;
+}
+
+static void sprd_pm_finish(void)
+{
+ pr_debug("enter %s\n", __func__);
+ print_statisic();
+}
+
+static struct platform_suspend_ops sprd_pm_ops = {
+ .valid = sprd_pm_valid,
+ .enter = sprd_pm_enter,
+ .prepare = sprd_pm_prepare,
+ .prepare_late = NULL,
+ .finish = sprd_pm_finish,
+};
+
+static int __init sprd_pm_init(void)
+{
+ sc_pm_init();
+
+#ifdef CONFIG_SUSPEND
+ suspend_set_ops(&sprd_pm_ops);
+#endif
+
+ return 0;
+}
+
+subsys_initcall(sprd_pm_init);
diff --git a/drivers/platform/sprd/pm_debug.c b/drivers/platform/sprd/pm_debug.c
new file mode 100644
index 00000000..8037ced4
--- /dev/null
+++ b/drivers/platform/sprd/pm_debug.c
@@ -0,0 +1,723 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#if 0
+#include <asm/irqflags.h>
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#include <linux/kthread.h>
+#include <linux/debugfs.h>
+#include <mach/pm_debug.h>
+#include <mach/globalregs.h>
+#include <mach/regs_glb.h>
+#include <mach/regs_ahb.h>
+#include <mach/regs_ana_glb.h>
+#include <mach/sci.h>
+#include <mach/adi.h>
+
+struct dentry * dentry_debug_root = NULL;
+static int is_print_sleep_mode = 0;
+int is_print_linux_clock = 1;
+int is_print_modem_clock = 1;
+static int is_print_irq = 1;
+static int is_print_wakeup = 1;
+static int is_print_irq_runtime = 0;
+static int is_print_time = 1;
+static unsigned int core_time = 0;
+static unsigned int mcu_time = 0;
+static unsigned int deep_time_successed = 0;
+static unsigned int deep_time_failed = 0;
+static unsigned int sleep_time = 0;
+#define SPRD_INTC_NUM 2
+#define SPRD_HARD_INTERRUPT_NUM 64
+#define SPRD_HARD_INT_NUM_EACH_INTC 32
+#define SPRD_IRQ_NUM 1024
+static u32 sprd_hard_irq[SPRD_HARD_INTERRUPT_NUM]= {0, };
+static u32 sprd_irqs[SPRD_IRQ_NUM] = {0, };
+static u32 sprd_irqs_sts[SPRD_INTC_NUM] = {0, };
+static int is_wakeup = 0;
+static int irq_status = 0;
+static int hard_irq_status[SPRD_INTC_NUM] = {0, };
+static int sleep_mode = SLP_MODE_NON;
+static char * sleep_mode_str[] = {
+ "[ARM]",
+ "[MCU]",
+ "[DEP]",
+ "[NON]"
+};
+#define INT_REG(off) (SPRD_INTC0_BASE + (off))
+#define INTC1_REG(off) (SPRD_INTC0_BASE + 0x1000 + (off))
+#define INT_IRQ_STS INT_REG(0x0000)
+#define INT_IRQ_RAW INT_REG(0x0004)
+#define INT_IRQ_ENB INT_REG(0x0008)
+#define INT_IRQ_DIS INT_REG(0x000c)
+#define INT_FIQ_STS INT_REG(0x0020)
+#define INTCV1_IRQ_MSKSTS INTC1_REG(0x0000)
+#define INTCV1_IRQ_RAW INTC1_REG(0x0004)
+#define INTCV1_IRQ_EN INTC1_REG(0x0008)
+#define INTCV1_IRQ_DIS INTC1_REG(0x000C)
+#define INTCV1_FIQ_STS INTC1_REG(0x0020)
+#define INT_IRQ_MASK (1<<3)
+
+void pm_debug_dump_ahb_glb_regs(void);
+
+static void hard_irq_reset(void)
+{
+ int i = SPRD_HARD_INTERRUPT_NUM - 1;
+ do{
+ sprd_hard_irq[i] = 0;
+ }while(--i >= 0);
+}
+
+static void parse_hard_irq(unsigned long val, unsigned long intc)
+{
+ int i;
+ if(intc == 0){
+ for (i = 0; i < SPRD_HARD_INT_NUM_EACH_INTC; i++) {
+ if (test_and_clear_bit(i, &val)) sprd_hard_irq[i]++;
+ }
+ }
+ if(intc == 1){
+ for (i = 32; i < SPRD_HARD_INT_NUM_EACH_INTC; i++) {
+ if (test_and_clear_bit(i, &val)) sprd_hard_irq[i]++;
+ }
+ }
+}
+
+void hard_irq_set(void)
+{
+ sprd_irqs_sts[0] = __raw_readl(INT_IRQ_STS);
+ sprd_irqs_sts[1] = __raw_readl(INT_FIQ_STS);
+ irq_status = __raw_readl(INT_IRQ_STS);
+ parse_hard_irq(irq_status, 0);
+ irq_status = __raw_readl(INTCV1_IRQ_MSKSTS);
+ parse_hard_irq(irq_status, 1);
+}
+
+#define GPIO_GROUP_NUM 16
+#define IRQ_GPIO (1<<10)
+#define IRQ_ADIE (1<<25)
+#define IRQ_DSP0 (1<<26)
+#define IRQ_DSP1 (1<<27)
+#define IRQ_TIMER0 (1<<6)
+#define IRQ_SIM0 (1<<15)
+#define IRQ_SIM1 (1<<16)
+
+#define REG_GPIO_MIS (0x0020)
+#define ANA_REG_INT_MASK_STATUS (SPRD_MISC_BASE + 0x380 +0x0000)
+void print_hard_irq_inloop(int ret)
+{
+ unsigned int i, j, val;
+ unsigned int gpio_irq[GPIO_GROUP_NUM];
+
+ if(!((sprd_irqs_sts[0]&IRQ_DSP0) ||
+ (sprd_irqs_sts[0]&IRQ_DSP1) ||
+ (sprd_irqs_sts[0]&IRQ_TIMER0) ||
+ (sprd_irqs_sts[0]&IRQ_SIM0) ||
+ (sprd_irqs_sts[0]&IRQ_SIM1) ) ){
+
+ if(sprd_irqs_sts[0] != 0)
+ printk("%c#:INTC0: %08x\n", ret?'S':'F', sprd_irqs_sts[0]);
+ if(sprd_irqs_sts[1] != 0)
+ printk("%c#:INTC0 FIQ: %08x\n", ret?'S':'F', sprd_irqs_sts[1]);
+ }
+
+ if(sprd_irqs_sts[0] & IRQ_GPIO){
+ for(i=0; i<(GPIO_GROUP_NUM/2); i++){
+ j = 2*i;
+ gpio_irq[j]= __raw_readl(SPRD_GPIO_BASE + 0x100*i + REG_GPIO_MIS);
+ gpio_irq[j+1]= __raw_readl(SPRD_GPIO_BASE + 0x100*i + 0x80 + REG_GPIO_MIS);
+ printk("gpio_irq[%d]:0x%x, gpio_irq[%d]:0x%x \n", j, gpio_irq[j], j+1, gpio_irq[j+1]);
+ }
+ for(i=0; i<GPIO_GROUP_NUM; i++){
+ if(gpio_irq[i] != 0){
+ val = gpio_irq[i];
+ while(val){
+ j = __ffs(val);
+ printk("gpio irq number : %d \n", (j+16*(i+1)) );
+ val &= ~(1<<j);
+ }
+ }
+ }
+ }
+ if(sprd_irqs_sts[0] & IRQ_ADIE){
+ printk("adie, irq status:0x%x \n", sci_adi_read(ANA_REG_INT_MASK_STATUS));
+ }
+
+}
+
+static void print_hard_irq(void)
+{
+ int i = SPRD_HARD_INTERRUPT_NUM -1;
+ if(!is_print_irq)
+ return;
+ do{
+ if(0 != sprd_hard_irq[i])
+ printk("##: sprd_hard_irq[%d] = %d.\n",
+ i, sprd_hard_irq[i]);
+ }while(--i >= 0);
+}
+
+static void irq_reset(void)
+{
+ int i = SPRD_IRQ_NUM - 1;
+ do{
+ sprd_irqs[i] = 0;
+ }while(--i >= 0);
+}
+
+
+void inc_irq(int irq)
+{
+ if(is_wakeup){
+ if (irq >= SPRD_IRQ_NUM) {
+ printk("## bad irq number %d.\n", irq);
+ return;
+ }
+ sprd_irqs[irq]++;
+ if(is_print_wakeup)
+ printk("\n#####: wakeup irq = %d.\n", irq);
+ is_wakeup = 0;
+ }
+}
+EXPORT_SYMBOL(inc_irq);
+
+void irq_wakeup_set(void)
+{
+ is_wakeup = 1;
+}
+
+static void print_irq(void)
+{
+ int i = SPRD_IRQ_NUM - 1;
+ if(!is_print_irq)
+ return;
+ do{
+ if(0 != sprd_irqs[i])
+ printk("##: sprd_irqs[%d] = %d.\n",
+ i, sprd_irqs[i]);
+ }while(--i >= 0);
+}
+
+void time_add(unsigned int time, int ret)
+{
+ switch(sleep_mode){
+ case SLP_MODE_ARM:
+ core_time += time;
+ break;
+ case SLP_MODE_MCU:
+ mcu_time += time;
+ break;
+ case SLP_MODE_DEP:
+ if(ret)
+ deep_time_successed += time;
+ else
+ deep_time_failed += time;
+ break;
+ default:
+ break;
+ }
+}
+
+void time_statisic_begin(void)
+{
+ core_time = 0;
+ mcu_time = 0;
+ deep_time_successed = 0;
+ deep_time_failed = 0;
+ sleep_time = get_sys_cnt();
+ hard_irq_reset();
+}
+
+void time_statisic_end(void)
+{
+ sleep_time = get_sys_cnt() - sleep_time;
+}
+
+void print_time(void)
+{
+ if(!is_print_time)
+ return;
+ printk("time statisics : sleep_time=%d, core_time=%d, mcu_time=%d, deep_sus=%d, dep_fail=%d\n",
+ sleep_time, core_time, mcu_time, deep_time_successed, deep_time_failed);
+}
+
+void set_sleep_mode(int sm){
+ int is_print = (sm == sleep_mode);
+ sleep_mode = sm;
+ if(is_print_sleep_mode == 0 || is_print )
+ return;
+ switch(sm){
+ case SLP_MODE_ARM:
+ printk("\n[ARM]\n");
+ break;
+ case SLP_MODE_MCU:
+ printk("\n[MCU]\n");
+ break;
+ case SLP_MODE_DEP:
+ printk("\n[DEP]\n");
+ break;
+ default:
+ printk("\nNONE\n");
+ }
+}
+void clr_sleep_mode(void)
+{
+ sleep_mode = SLP_MODE_NON;
+}
+void print_statisic(void)
+{
+ print_time();
+ print_hard_irq();
+ print_irq();
+ pm_debug_dump_ahb_glb_regs();
+ if(is_print_wakeup){
+ printk("###wake up form %s : %08x\n", sleep_mode_str[sleep_mode], sprd_irqs_sts[0]);
+ printk("###wake up form %s : %08x\n", sleep_mode_str[sleep_mode], sprd_irqs_sts[1]);
+ }
+}
+#ifdef PM_PRINT_ENABLE
+static struct wake_lock messages_wakelock;
+#endif
+#ifdef PM_PRINT_ENABLE
+/* save pm message for debug when enter deep sleep*/
+unsigned int debug_status[10];
+void pm_debug_save_ahb_glb_regs(void)
+{
+ debug_status[0] = sci_glb_read(REG_AHB_AHB_STATUS, -1UL);
+ debug_status[1] = sci_glb_read(REG_AHB_AHB_CTL0, -1UL);
+ debug_status[2] = sci_glb_read(REG_AHB_AHB_CTL1, -1UL);
+ debug_status[3] = sci_glb_read(REG_AHB_AHB_STATUS, -1UL);
+ debug_status[4] = sci_glb_read(REG_GLB_GEN0, -1UL);
+ debug_status[5] = sci_glb_read(REG_GLB_GEN1, -1UL);
+ debug_status[6] = sci_glb_read(REG_GLB_STC_DSP_ST, -1UL);
+ debug_status[7] = sci_glb_read(REG_GLB_BUSCLK, -1UL);
+ debug_status[8] = sci_glb_read(REG_GLB_CLKDLY, -1UL);
+}
+void pm_debug_dump_ahb_glb_regs(void)
+{
+ printk("***** ahb and globle registers before last deep sleep **********\n");
+
+ printk("*** AHB_CTL0: 0x%x ***\n", debug_status[1] );
+ printk("*** AHB_CTL1: 0x%x ***\n", debug_status[2] );
+ printk("*** GR_GEN0: 0x%x ***\n", debug_status[4] );
+ printk("*** GR_GEN1: 0x%x ***\n", debug_status[5] );
+ printk("*** GR_BUSCLK: 0x%x ***\n", debug_status[7] );
+
+ printk("*** AHB_STS: 0x%x ***\n", debug_status[3] );
+ printk("*** GR_STC_STATE: 0x%x ***\n", debug_status[6] );
+ printk("*** GR_CLK_DLY: 0x%x ***\n", debug_status[8] );
+}
+
+
+static void print_ahb(void)
+{
+ u32 val = sci_glb_read(REG_AHB_AHB_CTL0, -1UL);
+ printk("##: REG_AHB_AHB_CTL0 = %08x.\n", val);
+ if (val & AHB_CTL0_DCAM_EN) printk("AHB_CTL0_DCAM_EN =1.\n");
+ if (val & AHB_CTL0_CCIR_IN_EN) printk("AHB_CTL0_CCIR_IN_EN =1. CCIR enable\n");
+ if (val & AHB_CTL0_LCDC_EN) printk("AHB_CTL0_LCDC_EN =1.\n");
+ if (val & AHB_CTL0_SDIO0_EN) printk("AHB_CTL0_SDIO0_EN =1.\n");
+ if (val & AHB_CTL0_USBD_EN) printk("AHB_CTL0_USBD_EN =1.\n");
+ if (val & AHB_CTL0_DMA_EN) printk("AHB_CTL0_DMA_EN =1.\n");
+ if (val & AHB_CTL0_BM0_EN) printk("AHB_CTL0_BM0_EN =1.\n");
+ if (val & AHB_CTL0_NFC_EN) printk("AHB_CTL0_NFC_EN =1.\n");
+ if (val & AHB_CTL0_CCIR_EN) printk("AHB_CTL0_CCIR_EN =1. CCIR clock enable\n");
+ if (val & AHB_CTL0_DCAM_MIPI_EN) printk("AHB_CTL0_DCAM_MIPI_EN =1.\n");
+ if (val & AHB_CTL0_BM1_EN) printk("AHB_CTL0_BM1_EN =1.\n");
+ if (val & AHB_CTL0_ISP_EN) printk("AHB_CTL0_ISP_EN =1.\n");
+ if (val & AHB_CTL0_VSP_EN) printk("AHB_CTL0_VSP_EN =1.\n");
+ if (val & AHB_CTL0_ROT_EN) printk("AHB_CTL0_ROT_EN =1.\n");
+ if (val & AHB_CTL0_BM2_EN) printk("AHB_CTL0_BM2_EN =1.\n");
+ if (val & AHB_CTL0_BM3_EN) printk("AHB_CTL0_BM3_EN =1.\n");
+ if (val & AHB_CTL0_BM4_EN) printk("AHB_CTL0_BM4_EN =1.\n");
+ if (val & AHB_CTL0_SDIO1_EN) printk("AHB_CTL0_SDIO1_EN =1.\n");
+ if (val & AHB_CTL0_G2D_EN) printk("AHB_CTL0_G2D_EN =1.\n");
+ if (val & AHB_CTL0_G3D_EN) printk("AHB_CTL0_G3D_EN =1.\n");
+ if (val & AHB_CTL0_DISPC_EN) printk("AHB_CTL0_DISPC_EN =1.\n");
+ if (val & AHB_CTL0_EMMC_EN) printk("AHB_CTL0_EMMC_EN =1.\n");
+ if (val & AHB_CTL0_SDIO2_EN) printk("AHB_CTL0_SDIO2_EN =1.\n");
+ if (val & AHB_CTL0_SPINLOCK_EN) printk("AHB_CTL0_SPINLOCK_EN =1.\n");
+ if (val & AHB_CTL0_AHB_ARCH_EB) printk("AHB_CTL0_AHB_ARCH_EB =1. AHB bus HCLK enable\n");
+ if (val & AHB_CTL0_EMC_EN) printk("AHB_CTL0_EMC_EN =1.\n");
+ if (val & AHB_CTL0_AXIBUSMON0_EN) printk("AHB_CTL0_AXIBUSMON0_EN =1.\n");
+ if (val & AHB_CTL0_AXIBUSMON1_EN) printk("AHB_CTL0_AXIBUSMON1_EN =1.\n");
+ if (val & AHB_CTL0_AXIBUSMON2_EN) printk("AHB_CTL0_AXIBUSMON2_EN =1.\n");
+
+ val = sci_glb_read(REG_AHB_AHB_CTL1, -1UL);
+ printk("##: REG_AHB_AHB_CTL1 = %08x.\n", val);
+
+ val = sci_glb_read(REG_AHB_AHB_CTL2, -1UL);
+ printk("##: REG_AHB_AHB_CTL2 = %08x.\n", val);
+ if (val & AHB_CTL2_DISPMTX_CLK_EN ) printk("AHB_CTL2_DISPMTX_CLK_EN =1.\n");
+ if (val & AHB_CTL2_MMMTX_CLK_EN ) printk("AHB_CTL2_MMMTX_CLK_EN =1.\n");
+ if (val & AHB_CTL2_DISPC_CORE_CLK_EN) printk("AHB_CTL2_DISPC_CORE_CLK_EN=1.\n");
+ if (val & AHB_CTL2_LCDC_CORE_CLK_EN) printk("AHB_CTL2_LCDC_CORE_CLK_EN=1.\n");
+ if (val & AHB_CTL2_ISP_CORE_CLK_EN) printk("AHB_CTL2_ISP_CORE_CLK_EN=1.\n");
+ if (val & AHB_CTL2_VSP_CORE_CLK_EN) printk("AHB_CTL2_VSP_CORE_CLK_EN=1.\n");
+ if (val & AHB_CTL2_DCAM_CORE_CLK_EN) printk("AHB_CTL2_DCAM_CORE_CLK_EN=1.\n");
+
+ val = sci_glb_read(REG_AHB_AHB_CTL3, -1UL);
+ printk("##: REG_AHB_AHB_CTL3 = %08x.\n", val);
+
+ val = sci_glb_read(REG_AHB_MIPI_PHY_CTRL, -1UL);
+ printk("##: REG_AHB_MIPI_PHY_CTRL= %08x.\n", val);
+
+ val = sci_glb_read(REG_AHB_AHB_STATUS, -1UL);
+ printk("##:REG_AHB_AHB_STATUS = %08x.\n", val);
+}
+static void print_gr(void)
+{
+ u32 val = sci_glb_read(REG_GLB_GEN0, -1UL);
+ printk("##: GR_GEN0 = %08x.\n", val);
+ if (val & GEN0_UART3_EN) printk("GEN0_UART3_EN =1.\n");
+ if (val & GEN0_SPI2_EN) printk("GEN0_SPI2_EN =1.\n");
+ if (val & GEN0_TIMER_EN) printk("GEN0_TIMER_EN =1.\n");
+ if (val & GEN0_SIM0_EN) printk("GEN0_SIM0_EN =1.\n");
+ if (val & GEN0_I2C_EN) printk("GEN0_I2C_EN =1.\n");
+ if (val & GEN0_GPIO_EN) printk("GEN0_GPIO_EN =1.\n");
+ if (val & GEN0_ADI_EN) printk("GEN0_ADI_EN =1.\n");
+ if (val & GEN0_EFUSE_EN) printk("GEN0_EFUSE_EN =1.\n");
+ if (val & GEN0_KPD_EN) printk("GEN0_KPD_EN =1.\n");
+ if (val & GEN0_EIC_EN) printk("GEN0_EIC_EN =1.\n");
+ if (val & GEN0_I2S0_EN) printk("GEN0_I2S0_EN =1.\n");
+ if (val & GEN0_PIN_EN) printk("GEN0_PIN_EN =1.\n");
+ if (val & GEN0_CCIR_MCLK_EN) printk("GEN0_CCIR_MCLK_EN =1.\n");
+ if (val & GEN0_EPT_EN) printk("GEN0_EPT_EN =1.\n");
+ if (val & GEN0_SIM1_EN) printk("GEN0_SIM1_EN =1.\n");
+ if (val & GEN0_SPI0_EN) printk("GEN0_SPI0_EN =1.\n");
+ if (val & GEN0_SPI1_EN) printk("GEN0_SPI1_EN =1.\n");
+ if (val & GEN0_SYST_EN) printk("GEN0_SYST_EN =1.\n");
+ if (val & GEN0_UART0_EN) printk("GEN0_UART0_EN =1.\n");
+ if (val & GEN0_UART1_EN) printk("GEN0_UART1_EN =1.\n");
+ if (val & GEN0_UART2_EN) printk("GEN0_UART2_EN =1.\n");
+ if (val & GEN0_VB_EN) printk("GEN0_VB_EN =1.\n");
+ if (val & GEN0_EIC_RTC_EN) printk("GEN0_EIC_RTC_EN =1.\n");
+ if (val & GEN0_I2S1_EN) printk("GEN0_I2S1_EN =1.\n");
+ if (val & GEN0_KPD_RTC_EN) printk("GEN0_KPD_RTC_EN =1.\n");
+ if (val & GEN0_SYST_RTC_EN) printk("GEN0_SYST_RTC_EN =1.\n");
+ if (val & GEN0_TMR_RTC_EN) printk("GEN0_TMR_RTC_EN =1.\n");
+ if (val & GEN0_I2C1_EN) printk("GEN0_I2C1_EN =1.\n");
+ if (val & GEN0_I2C2_EN) printk("GEN0_I2C2_EN =1.\n");
+ if (val & GEN0_I2C3_EN) printk("GEN0_I2C3_EN =1.\n");
+
+ val = sci_glb_read(REG_GLB_GEN1, -1UL);
+ printk("##: REG_GLB_GEN1 = %08x.\n", val);
+ if (val & BIT_CLK_AUX0_EN ) printk(" CLK_AUX0_EN =1.\n");
+ if (val & BIT_CLK_AUX1_EN ) printk(" CLK_AUX1_EN =1.\n");
+ if (val & BIT_AUD_IF_EB ) printk(" AUD_IF_EB =1.\n");
+ if (val & BIT_AUD_TOP_EB ) printk(" AUD_TOP_EB =1.\n");
+ if (val & BIT_VBC_EN ) printk(" VBC_EN =1.\n");
+ if (val & BIT_AUDIF_AUTO_EN ) printk(" AUDIF_AUTO_EN =1.\n");
+
+ val = sci_glb_read(REG_GLB_CLK_EN, -1UL);
+ printk("##: GR_CLK_EN = %08x.\n", val);
+ if (val & CLK_PWM0_EN) printk("CLK_PWM0_EN =1.\n");
+ if (val & CLK_PWM1_EN) printk("CLK_PWM1_EN = 1.\n");
+ if (val & CLK_PWM2_EN) printk("CLK_PWM2_EN = 1.\n");
+ if (val & CLK_PWM3_EN) printk("CLK_PWM3_EN = 1.\n");
+
+ val = sci_glb_read(REG_GLB_BUSCLK, -1UL);
+ printk("##: GR_BUSCLK_ALM = %08x.\n", val);
+ if (val & ARM_VB_MCLKON) printk("ARM_VB_MCLKON =1.\n");
+ if (val & ARM_VB_DA0ON) printk("ARM_VB_DA0ON = 1.\n");
+ if (val & ARM_VB_DA1ON) printk("ARM_VB_DA1ON = 1.\n");
+ if (val & ARM_VB_ADCON) printk("ARM_VB_ADCON = 1.\n");
+ if (val & ARM_VB_ANAON) printk("ARM_VB_ANAON = 1.\n");
+ if (val & ARM_VB_ACC) printk("ARM_VB_ACC = 1.\n");
+
+ val = sci_glb_read(REG_GLB_CLK_GEN5, -1UL);
+ printk("##: GR_CLK_GEN5 = %08x.\n", val);
+ if (val & BIT(9)) printk("LDO_USB_PD =1.\n");
+
+ val = sci_glb_raw_read(REG_GLB_PCTRL);
+ printk("##: GLB_PCTL = %08x.\n", val);
+ if (val & BIT_MCU_MPLL_EN) printk("MCU_MPLL = 1.\n");
+ if (val & BIT_MCU_GPLL_EN) printk("MCU_GPLL = 1.\n");
+ if (val & BIT_MCU_DPLL_EN) printk("MCU_DPLL = 1.\n");
+ if (val & BIT_MCU_TDPLL_EN) printk("MCU_TDPLL = 1.\n");
+
+ val = sci_glb_raw_read(REG_GLB_TD_PLL_CTL);
+ printk("##: GLB_TD_PLL_CTL = %08x.\n", val);
+ if (!(val & BIT_TDPLL_DIV2OUT_FORCE_PD)) printk("clk_384m = 1.\n");
+ if (!(val & BIT_TDPLL_DIV3OUT_FORCE_PD)) printk("clk_256m = 1.\n");
+ if (!(val & BIT_TDPLL_DIV4OUT_FORCE_PD)) printk("clk_192m = 1.\n");
+ if (!(val & BIT_TDPLL_DIV5OUT_FORCE_PD)) printk("clk_153p6m = 1.\n");
+
+ val = sci_glb_read(REG_GLB_POWCTL1, -1UL);
+ printk("##: GR_POWCTL1 = %08x.\n", val);
+
+ val = sci_glb_read(REG_GLB_M_PLL_CTL0, -1UL);
+ printk("##: REG_GLB_M_PLL_CTL0 = %08x.\n", val);
+
+ val = sci_glb_read(REG_GLB_MM_PWR_CTL, -1UL);
+ printk("##: REG_GLB_MM_PWR_CTL = %08x.\n", val);
+ val = sci_glb_read(REG_GLB_CEVA_L1RAM_PWR_CTL, -1UL);
+ printk("##: REG_GLB_CEVA_L1RAM_PWR_CTL = %08x.\n", val);
+ val = sci_glb_read(REG_GLB_GSM_PWR_CTL, -1UL);
+ printk("##: REG_GLB_GSM_PWR_CTL = %08x.\n", val);
+ val = sci_glb_read(REG_GLB_TD_PWR_CTL, -1UL);
+ printk("##: GR_TD_PWR_CTRL = %08x.\n", val);
+ val = sci_glb_read(REG_GLB_PERI_PWR_CTL, -1UL);
+ printk("##: GR_PERI_PWR_CTRL = %08x.\n", val);
+ val = sci_glb_read(REG_GLB_ARM_SYS_PWR_CTL, -1UL);
+ printk("##: GR_ARM_SYS_PWR_CTRL = %08x.\n", val);
+ val = sci_glb_read(REG_GLB_G3D_PWR_CTL, -1UL);
+ printk("##: GR_G3D_PWR_CTRL = %08x.\n", val);
+}
+#define LDO_USB_CTL 0x2
+#define LDO_SIM0_CTL 0x20
+#define LDO_SIM1_CTL 0x80
+#define LDO_BPCAMCORE_CTL 0x200
+#define LDO_BPCAMIO_CTL 0x800
+#define LDO_BPCAMA_CTL 0x2000
+#define LDO_BPCAMMOT_CTL 0x8000
+#define LDO_SDIO0_CTL 0x2
+#define LDO_SDIO1_CTL 0x8
+#define LDO_SDIO3_CTL 0x20
+#define LDO_VDD3V_CTL 0x80
+#define LDO_CMMB1P8_CTL 0x200
+#define LDO_CMMB1V2_CTL 0x800
+#define AUDIO_PA_ENABLE 0x1
+#define AUDIO_PA_ENABLE_RST 0x2
+#define AUDIO_PA_LDO_ENABLE 0x100
+#define AUDIO_PA_LDO_ENABLE_RST 0x200
+#define VIBR_PD_RST 0x8
+#define VIBR_PD 0x4
+#define LDO_REG_BASE (SPRD_MISC_BASE + 0x600)
+#define ANA_LDO_PD_CTL0 (LDO_REG_BASE + 0x10)
+#define ANA_LDO_PD_CTL1 (LDO_REG_BASE + 0x14)
+#define ANA_LED_CTL (LDO_REG_BASE + 0x70)
+#define ANA_VIBRATOR_CTRL0 (LDO_REG_BASE + 0x74)
+#define ANA_AUD_CLK_RST (LDO_REG_BASE + 0x7C)
+
+static int check_ana(void)
+{
+ static u16 tag = 0x555;
+ int ret = 0;
+ u32 val, reg = SCI_ADDR(SPRD_MISC_BASE, 0x0144);
+
+ /* check adi fifo r/w address */
+ val = __raw_readl(SCI_ADDR(SPRD_ADI_BASE, 0x002c));
+ ret |= (val >> 4 & 3) == (val >> 8 & 3);
+ ret <<= 1;
+
+ /* check ana reg w/r consistency */
+ sci_adi_raw_write(reg, tag);
+ ret |= sci_adi_read(reg) == tag;
+ tag = ~tag & 0xfff;
+
+ return ret;
+}
+
+static void print_ana(void)
+{
+ u32 val;
+ val = sci_adi_read(ANA_REG_GLB_ANA_APB_CLK_EN);
+ printk("##%d: ANA_REG_GLB_ANA_APB_CLK_EN = %08x.\n", check_ana(), val);
+
+ val = sci_adi_read(ANA_REG_GLB_LDO_PD_CTRL0);
+ printk("##: ANA_REG_GLB_LDO_PD_CTRL0 = %04x.\n", val);
+ if ((val & BIT_LDO_BP_CAMMOT_RST)) printk("##:LDO_CAMMOT is on.\n");
+ else if((val & BIT_LDO_BPCAMMOT)) printk("##: LDO_CAMMOT is off.\n");
+ if ((val & BIT_LDO_BPCAMA_RST)) printk("## LDO_BPCAMA:is on.\n");
+ else if((val & BIT_LDO_BPCAMA)) printk("##: LDO_BPCAMA is off.\n");
+ if ((val & BIT_LDO_BPCAMIO_RST)) printk("## LDO_BPCAMIO :is on.\n");
+ else if((val & BIT_LDO_BPCAMIO)) printk("## LDO_BPCAMIO : is off.\n");
+ if ((val & BIT_LDO_BPCAMCORE_RST)) printk("## LDO_BPCAMCORE :is on.\n");
+ else if((val & BIT_LDO_BPCAMCORE)) printk("## LDO_BPCAMCORE : is off.\n");
+ if ((val & BIT_LDO_BPSIM1_RST)) printk("## LDO_BPSIM1 :is on.\n");
+ else if((val & BIT_LDO_BPSIM1)) printk("## LDO_BPSIM1 : is off.\n");
+ if ((val & BIT_LDO_BPSIM0_RST)) printk("## LDO_BPSIM0 :is on.\n");
+ else if((val & BIT_LDO_BPSIM0)) printk("## LDO_BPSIM0 : is off.\n");
+ if ((val & BIT_LDO_BPUSB_RST)) printk("## LDO_BPUSB :is on.\n");
+ else if((val & BIT_LDO_BPUSB)) printk("## LDO_BPUSB : is off.\n");
+
+ val = sci_adi_read(ANA_REG_GLB_LDO_PD_CTRL1);
+ printk("##:ANA_REG_GLB_LDO_PD_CTRL1 = %04x.\n", val);
+ if ((val & BIT_LDO_BPCMMB1V2_RST)) printk("## LDO_BPCMMB1V2: is on.\n");
+ else if((val & BIT_LDO_BPCMMB1V2)) printk("## LDO_BPCMMB1V2: is off.\n");
+ if ((val & BIT_LDO_BPCMMB1P8_RST)) printk("## LDO_BPCMMB1P8: is on.\n");
+ else if((val & BIT_LDO_BPCMMB1P8)) printk("## LDO_BPCMMB1P8: is off.\n");
+ if ((val & BIT_LDO_BPVDD3V_RST)) printk("## LDO_BPVDD3V: is on.\n");
+ else if((val & BIT_LDO_BPVDD3V)) printk("## LDO_BPVDD3V: is off.\n");
+ if ((val & BIT_LDO_BPSD3_RST)) printk("## LDO_BPSD3: is on.\n");
+ else if((val & BIT_LDO_BPSD3)) printk("## LDO_BPSD3: is off.\n");
+ if ((val & BIT_LDO_BPSD1_RST)) printk("## LDO_BPSD1: is on.\n");
+ else if((val & BIT_LDO_BPSD1)) printk("## LDO_BPSD1: is off.\n");
+ if ((val & BIT_LDO_BPSD0_RST)) printk("## LDO_BPSD0: is on.\n");
+ else if((val & BIT_LDO_BPSD0)) printk("## LDO_BPSD0: is off.\n");
+
+ val = sci_adi_read(ANA_LED_CTL);
+ printk("##: ANA_LED_CTL = 0x%x.\n", val);
+
+ val = sci_adi_read(ANA_VIBRATOR_CTRL0);
+ printk("##: ANA_VIBRATOR_CTRL0 = 0x%x.\n", val);
+ if (val & VIBR_PD_RST) printk("##: vibrator is power on.\n");
+ else if (val & VIBR_PD) printk("##: vibrator is power off.\n");
+
+ val = sci_adi_read(ANA_AUD_CLK_RST);
+ printk("##: ANA_AUD_CLK_RST = 0x%x.\n", val);
+
+ val = sci_adi_read(ANA_REG_GLB_DCDCARM_CTRL0);
+ printk("##: ANA_REG_GLB_DCDCARM_CTRL0 = 0x%x.\n", val);
+
+}
+static int is_dsp_sleep(void)
+{
+ u32 val;
+ val = sci_glb_read(REG_GLB_STC_DSP_ST, -1UL);
+ if (GR_EMC_STOP & val)
+ printk("#####: REG_GLB_STC_DSP_ST[GR_EMC_STOP] is set!\n");
+ else
+ printk("#####: REG_GLB_STC_DSP_ST[GR_EMC_STOP] is NOT set!\n");
+ if (GR_MCU_STOP & val)
+ printk("#####: REG_GLB_STC_DSP_ST[GR_MCU_STOP] is set!\n");
+ else
+ printk("#####: REG_GLB_STC_DSP_ST[GR_MCU_STOP] is NOT set!\n");
+ if (GR_DSP_STOP & val)
+ printk("#####: REG_GLB_STC_DSP_ST[DSP_STOP] is set!\n");
+ else
+ printk("#####: REG_GLB_STC_DSP_ST[DSP_STOP] is NOT set!\n");
+ return 0;
+}
+extern void sci_clock_dump_active(void);
+static int print_thread(void * data)
+{
+ while(1){
+ wake_lock(&messages_wakelock);
+ print_ahb();
+ print_gr();
+ print_ana();
+ is_dsp_sleep();
+ has_wake_lock(WAKE_LOCK_SUSPEND);
+ msleep(100);
+ wake_unlock(&messages_wakelock);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(30 * HZ);
+ }
+ return 0;
+}
+void pm_debug_set_wakeup_timer()
+{
+ u32 val = get_sys_cnt();
+ val = val + 2000;
+ __raw_writel(val, SYSCNT_REG(0) );
+ __raw_writel(1, SYSCNT_REG(0X8) );
+}
+static void debugfs_init(void)
+{
+ dentry_debug_root = debugfs_create_dir("power", NULL);
+ if (IS_ERR(dentry_debug_root) || !dentry_debug_root) {
+ printk("!!!powermanager Failed to create debugfs directory\n");
+ dentry_debug_root = NULL;
+ return;
+ }
+ debugfs_create_u32("print_sleep_mode", 0644, dentry_debug_root,
+ &is_print_sleep_mode);
+ debugfs_create_u32("print_linux_clock", 0644, dentry_debug_root,
+ &is_print_linux_clock);
+ debugfs_create_u32("print_modem_clock", 0644, dentry_debug_root,
+ &is_print_modem_clock);
+ debugfs_create_u32("print_irq", 0644, dentry_debug_root,
+ &is_print_irq);
+ debugfs_create_u32("print_wakeup", 0644, dentry_debug_root,
+ &is_print_wakeup);
+ debugfs_create_u32("print_irq_runtime", 0644, dentry_debug_root,
+ &is_print_irq_runtime);
+ debugfs_create_u32("print_time", 0644, dentry_debug_root,
+ &is_print_time);
+}
+void pm_debug_init(void)
+{
+ struct task_struct * task;
+#ifdef PM_PRINT_ENABLE
+ wake_lock_init(&messages_wakelock, WAKE_LOCK_SUSPEND,
+ "pm_message_wakelock");
+ task = kthread_create(print_thread, NULL, "pm_print");
+ if (task == 0) {
+ printk("Can't crate power manager print thread!\n");
+ }else
+ wake_up_process(task);
+#endif
+ debugfs_init();
+}
+void pm_debug_clr(void)
+{
+ if(dentry_debug_root != NULL)
+ debugfs_remove_recursive(dentry_debug_root);
+}
+#endif
+#else
+void pm_debug_init(void)
+{
+}
+void pm_debug_clr(void)
+{
+}
+void clr_sleep_mode(void)
+{
+}
+void time_statisic_begin(void){}
+void irq_wakeup_set(void){}
+void time_statisic_end(void){}
+void pm_debug_save_ahb_glb_regs(void){}
+void time_add(unsigned int time, int ret){}
+void print_hard_irq_inloop(int ret){}
+void print_statisic(void){}
+void set_sleep_mode(int sm){}
+#endif
+#if 0
+void inc_irq(int irq)
+{
+
+}
+
+#include <linux/regulator/consumer.h>
+int __init sc_ldo_slp_init(void)
+{
+ int i;
+
+ static struct {
+ const char *vdd_name;
+ /*
+ * 0: slp pd disable, very light loads when in STANDBY mode
+ * 1: slp pd enable, this is chip default config for most of LDOs
+ */
+ bool pd_en;
+ } ldo_slp_config[] __initdata = {
+ {"vddsim0", 0},
+ {"vddsim1", 0},
+ {"avddvb", 0},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(ldo_slp_config); i++) {
+ if (0 == ldo_slp_config[i].pd_en) {
+ struct regulator *ldo =
+ regulator_get(NULL, ldo_slp_config[i].vdd_name);
+ if (!WARN_ON(IS_ERR(ldo))) {
+ regulator_set_mode(ldo, REGULATOR_MODE_STANDBY);
+ regulator_put(ldo);
+ }
+ pr_info("%s slp pd disable\n", ldo_slp_config[i].vdd_name);
+ }
+ }
+ return 0;
+}
+
+late_initcall(sc_ldo_slp_init);
+#endif
diff --git a/drivers/platform/sprd/pm_debug_scx35.c b/drivers/platform/sprd/pm_debug_scx35.c
new file mode 100644
index 00000000..280b34bc
--- /dev/null
+++ b/drivers/platform/sprd/pm_debug_scx35.c
@@ -0,0 +1,982 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <asm/irqflags.h>
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+#include <linux/kthread.h>
+#include <linux/debugfs.h>
+#include <soc/sprd/pm_debug.h>
+#if defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8)
+#include <soc/sprd/sprd_persistent_clock.h>
+#endif
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/adi.h>
+#include <linux/interrupt.h>
+#include <soc/sprd/irqs.h>
+#include <soc/sprd/gpio.h>
+#include <soc/sprd/iomap.h>
+
+struct dentry * dentry_debug_root = NULL;
+static int is_print_sleep_mode = 0;
+int is_print_linux_clock = 1;
+int is_print_modem_clock = 1;
+static int is_print_irq = 1;
+static int is_print_wakeup = 1;
+static int is_print_irq_runtime = 0;
+static int is_print_time = 1;
+static int print_thread_enable = 1;
+static int print_thread_interval = 30;
+static unsigned int core_time = 0;
+static unsigned int mcu_time = 0;
+static unsigned int lit_time = 0;
+static unsigned int deep_time_successed = 0;
+static unsigned int deep_time_failed = 0;
+static unsigned int sleep_time = 0;
+#define SPRD_INTC_NUM 4
+#define SPRD_HARD_INTERRUPT_NUM 128
+#define SPRD_HARD_INT_NUM_EACH_INTC 32
+#define SPRD_IRQ_NUM 1024
+static u32 sprd_hard_irq[SPRD_HARD_INTERRUPT_NUM]= {0, };
+//static u32 sprd_irqs[SPRD_IRQ_NUM] = {0, };
+static u32 sprd_irqs_sts[SPRD_INTC_NUM] = {0, };
+static int is_wakeup = 0;
+static int irq_status = 0;
+//static int hard_irq_status[SPRD_INTC_NUM] = {0, };
+static int sleep_mode = SLP_MODE_NON;
+static char * sleep_mode_str[] = {
+ "[ARM]",
+ "[MCU]",
+ "[DEP]",
+ "[LIT]",
+ "[NON]"
+};
+#define INT_REG(off) (SPRD_INT_BASE + (off))
+#define INTC0_REG(off) (SPRD_INTC0_BASE + (off))
+#define INTC1_REG(off) (SPRD_INTC1_BASE + (off))
+#define INTC2_REG(off) (SPRD_INTC2_BASE + (off))
+#define INTC3_REG(off) (SPRD_INTC3_BASE + (off))
+#define INT_IRQ_STS INT_REG(0x0000)
+#define INT_IRQ_RAW INT_REG(0x0004)
+#define INT_IRQ_ENB INT_REG(0x0008)
+#define INT_IRQ_DIS INT_REG(0x000c)
+#define INT_FIQ_STS INT_REG(0x0020)
+#define INTCV0_IRQ_MSKSTS INTC0_REG(0x0000)
+#define INTCV0_IRQ_RAW INTC0_REG(0x0004)
+#define INTCV0_IRQ_EN INTC0_REG(0x0008)
+#define INTCV0_IRQ_DIS INTC0_REG(0x000C)
+#define INTCV0_FIQ_STS INTC0_REG(0x0020)
+#define INTCV1_IRQ_MSKSTS INTC1_REG(0x0000)
+#define INTCV1_IRQ_RAW INTC1_REG(0x0004)
+#define INTCV1_IRQ_EN INTC1_REG(0x0008)
+#define INTCV1_IRQ_DIS INTC1_REG(0x000C)
+#define INTCV1_FIQ_STS INTC1_REG(0x0020)
+#define INTCV2_IRQ_MSKSTS INTC2_REG(0x0000)
+#define INTCV2_IRQ_RAW INTC2_REG(0x0004)
+#define INTCV2_IRQ_EN INTC2_REG(0x0008)
+#define INTCV2_IRQ_DIS INTC2_REG(0x000C)
+#define INTCV2_FIQ_STS INTC2_REG(0x0020)
+#define INTCV3_IRQ_MSKSTS INTC3_REG(0x0000)
+#define INTCV3_IRQ_RAW INTC3_REG(0x0004)
+#define INTCV3_IRQ_EN INTC3_REG(0x0008)
+#define INTCV3_IRQ_DIS INTC3_REG(0x000C)
+#define INTCV3_FIQ_STS INTC3_REG(0x0020)
+#define INT_IRQ_MASK (1<<3)
+
+#define ANA_REG_INT_MASK_STATUS (ANA_CTL_INT_BASE + 0x0000)
+#define ANA_REG_INT_RAW_STATUS (ANA_CTL_INT_BASE + 0x0004)
+#define ANA_REG_INT_EN (ANA_CTL_INT_BASE + 0x0008)
+#define ANA_REG_INT_MASK_STATUS_SYNC (ANA_CTL_INT_BASE + 0x000c)
+
+void pm_debug_dump_ahb_glb_regs(void);
+
+static void hard_irq_reset(void)
+{
+ int i = SPRD_HARD_INTERRUPT_NUM - 1;
+ do{
+ sprd_hard_irq[i] = 0;
+ }while(--i >= 0);
+}
+
+static void parse_hard_irq(unsigned long val, unsigned long intc)
+{
+ int i;
+ if(intc == 0){
+ for (i = 0; i < SPRD_HARD_INT_NUM_EACH_INTC; i++) {
+ if (test_and_clear_bit(i, &val)) sprd_hard_irq[i]++;
+ }
+ }
+ if(intc == 1){
+ for (i = 0; i < SPRD_HARD_INT_NUM_EACH_INTC; i++) {
+ if (test_and_clear_bit(i, &val)) sprd_hard_irq[32+i]++;
+ }
+ }
+ if(intc == 2){
+ for (i = 0; i < SPRD_HARD_INT_NUM_EACH_INTC; i++) {
+ if (test_and_clear_bit(i, &val)) sprd_hard_irq[64+i]++;
+ }
+ }
+ if(intc == 3){
+ for (i = 0; i < SPRD_HARD_INT_NUM_EACH_INTC; i++) {
+ if (test_and_clear_bit(i, &val)) sprd_hard_irq[96+i]++;
+ }
+ }
+}
+
+void hard_irq_set(void)
+{
+ sprd_irqs_sts[0] = __raw_readl(INT_IRQ_STS);
+ sprd_irqs_sts[1] = __raw_readl(INT_FIQ_STS);
+ irq_status = __raw_readl(INTCV0_IRQ_RAW);
+ parse_hard_irq(irq_status, 0);
+ irq_status = __raw_readl(INTCV1_IRQ_RAW);
+ parse_hard_irq(irq_status, 1);
+ irq_status = __raw_readl(INTCV2_IRQ_RAW);
+ parse_hard_irq(irq_status, 2);
+ irq_status = __raw_readl(INTCV3_IRQ_RAW);
+ parse_hard_irq(irq_status, 3);
+}
+void print_int_status(void)
+{
+ printk("APB_EB 0x%08x\n", __raw_readl(REG_AP_APB_APB_EB));
+ printk("INTC0 mask:0x%08x raw:0x%08x en:0x%08x\n", __raw_readl(INTCV0_IRQ_MSKSTS),__raw_readl(INTCV0_IRQ_RAW), __raw_readl(INTCV0_IRQ_EN));
+ printk("INTC1 mask:0x%08x raw:0x%08x en:0x%08x\n", __raw_readl(INTCV1_IRQ_MSKSTS),__raw_readl(INTCV1_IRQ_RAW), __raw_readl(INTCV1_IRQ_EN));
+ printk("INTC2 mask:0x%08x raw:0x%08x en:0x%08x\n", __raw_readl(INTCV2_IRQ_MSKSTS),__raw_readl(INTCV2_IRQ_RAW), __raw_readl(INTCV2_IRQ_EN));
+ printk("INTC3 mask:0x%08x raw:0x%08x en:0x%08x\n", __raw_readl(INTCV3_IRQ_MSKSTS),__raw_readl(INTCV3_IRQ_RAW), __raw_readl(INTCV3_IRQ_EN));
+ printk("INT mask:0x%08x raw:0x%08x en:0x%08x\n", __raw_readl(INT_IRQ_STS),__raw_readl(INT_IRQ_RAW), __raw_readl(INT_IRQ_ENB));
+ printk("ANA INT mask:0x%08x raw:0x%08x en:0x%08x\n", sci_adi_read(ANA_REG_INT_MASK_STATUS), sci_adi_read(ANA_REG_INT_RAW_STATUS), sci_adi_read(ANA_REG_INT_EN));
+ printk("ANA EIC MODULE_EN 0x%08x eic bit(3)\n", sci_adi_read(ANA_REG_GLB_ARM_MODULE_EN));
+ printk("ANA EIC int en 0x%08x\n", sci_adi_read(ANA_CTL_EIC_BASE + 0x18));
+ printk("ANA EIC int status 0x%08x, 0x%08x\n", sci_adi_read(ANA_CTL_EIC_BASE + 0x20),sci_adi_read(ANA_CTL_EIC_BASE + 0x14));
+}
+
+#define GPIO_GROUP_NUM 16
+#define IRQ_EIC (1<<14)
+#define IRQ_BUSMON (1<<13)
+#define IRQ_WDG (1<<11)
+#define IRQ_CP2 (1<<10)
+#define IRQ_CP1 (1<<9)
+#define IRQ_CP0 (1<<8)
+#define IRQ_CP_WDG (1<<7)
+#define IRQ_GPU (1<<6)
+#define IRQ_MM (1<<5)
+#define IRQ_4 (1<<4)
+#define IRQ_3 (1<<3)
+#define IRQ_2 (1<<2)
+
+#define REG_GPIO_MIS (0x0020)
+void print_hard_irq_inloop(int ret)
+{
+ unsigned int i, j, val;
+ unsigned int ana_sts;
+ unsigned int gpio_irq[GPIO_GROUP_NUM];
+
+ if(sprd_irqs_sts[0] != 0)
+ printk("%c#:INTC0: %08x\n", ret?'S':'F', sprd_irqs_sts[0]);
+ if(sprd_irqs_sts[1] != 0)
+ printk("%c#:INTC0 FIQ: %08x\n", ret?'S':'F', sprd_irqs_sts[1]);
+ if(sprd_irqs_sts[0]&IRQ_EIC){
+ printk("wake up by eic\n");
+ }
+ if(sprd_irqs_sts[0]&IRQ_BUSMON){
+ printk("wake up by busmoniter\n");
+ }
+ if(sprd_irqs_sts[0]&IRQ_WDG){
+ printk("wake up by ca7_wdg or ap_wdg\n");
+ }
+ if(sprd_irqs_sts[0]&IRQ_CP2){
+ printk("wake up by cp2\n");
+ }
+ if(sprd_irqs_sts[0]&IRQ_CP1){
+ printk("wake up by cp1\n");
+ }
+ if(sprd_irqs_sts[0]&IRQ_CP0){
+ printk("wake up by cp0\n");
+ }
+ if(sprd_irqs_sts[0]&IRQ_GPU){
+ printk("wake up by gpu\n");
+ }
+ if(sprd_irqs_sts[0]&IRQ_MM){
+ printk("wake up by mm\n");
+ }
+ if(sprd_irqs_sts[0]&IRQ_4){
+ if(sprd_hard_irq[34])
+ printk("wake up by i2c\n");
+ if(sprd_hard_irq[20])
+ printk("wake up by audio\n");
+ if(sprd_hard_irq[27])
+ printk("wake up by fm\n");
+ if(sprd_hard_irq[25]){
+ printk("wake up by adi\n");
+ }
+ if(sprd_hard_irq[38]){
+ printk("wake up by ana ");
+ ana_sts = sci_adi_read(ANA_REG_INT_RAW_STATUS);
+ if(ana_sts & BIT(0))
+ printk("adc\n");
+ if(ana_sts & BIT(1)){
+ printk("gpio\n");
+ printk("gpio 0 ~ 16 0x%08x\n", sci_adi_read(SPRD_MISC_BASE + 0x0480 + 0x1c));
+ printk("gpio 17 ~ 31 0x%08x\n", sci_adi_read(SPRD_MISC_BASE + 0x0480 + 0x40 + 0x1c));
+ }
+ if(ana_sts & BIT(2))
+ printk("rtc\n");
+ if(ana_sts & BIT(3))
+ printk("wdg\n");
+ if(ana_sts & BIT(4))
+ printk("fgu\n");
+ if(ana_sts & BIT(5)){
+ printk("eic\n");
+ printk("ana eic 0x%08x\n", sci_adi_read(ANA_EIC_BASE + 0x1c));
+ }
+ if(ana_sts & BIT(6))
+ printk("aud head button\n");
+ if(ana_sts & BIT(7))
+ printk("aud protect\n");
+ if(ana_sts & BIT(8))
+ printk("thermal\n");
+ if(ana_sts & BIT(10))
+ printk("dcdc otp\n");
+ printk("ANA INT 0x%08x\n", ana_sts);
+ }
+ if(sprd_hard_irq[36])
+ printk("wake up by kpd\n");
+ if(sprd_hard_irq[35]){
+ printk("wake up by gpio\n");
+ }
+ }
+ if(sprd_irqs_sts[0]&IRQ_3){
+ if(sprd_hard_irq[23])
+ printk("wake up by vbc_ad01\n");
+ if(sprd_hard_irq[24])
+ printk("wake up by vbc_ad23\n");
+ if(sprd_hard_irq[22])
+ printk("wake up by vbc_da\n");
+ if(sprd_hard_irq[21])
+ printk("wake up by afifi_error\n");
+ }
+ if(sprd_irqs_sts[0]&IRQ_2){
+ if(sprd_hard_irq[29]){
+ printk("wake up by ap_tmr0\n");
+ }
+ if(sprd_hard_irq[118]){
+ printk("wake up by ap_tmr1\n");
+ }
+ if(sprd_hard_irq[119]){
+ printk("wake up by ap_tmr2\n");
+ }
+ if(sprd_hard_irq[120]){
+ printk("wake up by ap_tmr3\n");
+ }
+ if(sprd_hard_irq[121]){
+ printk("wake up by ap_tmr4\n");
+ }
+ if(sprd_hard_irq[31]){
+ printk("wake up by ap_syst\n");
+ }
+ if(sprd_hard_irq[30]){
+ printk("wake up by aon_syst\n");
+ }
+ if(sprd_hard_irq[28]){
+ printk("wake up by aon_tmr\n");
+ }
+ }
+
+ if (sprd_hard_irq[35]) {
+ for(i=0; i<(GPIO_GROUP_NUM/2); i++){
+ j = 2*i;
+ gpio_irq[j]= __raw_readl(SPRD_GPIO_BASE + 0x100*i + REG_GPIO_MIS);
+ gpio_irq[j+1]= __raw_readl(SPRD_GPIO_BASE + 0x100*i + 0x80 + REG_GPIO_MIS);
+ // printk("gpio_irq[%d]:0x%x, gpio_irq[%d]:0x%x \n", j, gpio_irq[j], j+1, gpio_irq[j+1]);
+ }
+ for(i=0; i<GPIO_GROUP_NUM; i++){
+ if(gpio_irq[i] != 0){
+ val = gpio_irq[i];
+ while(val){
+ j = __ffs(val);
+ printk("irq from gpio%d\n", j + 16*i);
+ val &= ~(1<<j);
+ }
+ }
+ }
+ }
+}
+
+static void print_hard_irq(void)
+{
+ int i = SPRD_HARD_INTERRUPT_NUM -1;
+ if(!is_print_irq)
+ return;
+ do{
+ if(0 != sprd_hard_irq[i])
+ printk("##: sprd_hard_irq[%d] = %d.\n",
+ i, sprd_hard_irq[i]);
+ }while(--i >= 0);
+}
+
+#if 0
+static void irq_reset(void)
+{
+ int i = SPRD_IRQ_NUM - 1;
+ do{
+ sprd_irqs[i] = 0;
+ }while(--i >= 0);
+}
+
+
+void inc_irq(int irq)
+{
+ if(is_wakeup){
+ if (irq >= SPRD_IRQ_NUM) {
+ printk("## bad irq number %d.\n", irq);
+ return;
+ }
+ sprd_irqs[irq]++;
+ if(is_print_wakeup)
+ printk("\n#####: wakeup irq = %d.\n", irq);
+ is_wakeup = 0;
+ }
+}
+EXPORT_SYMBOL(inc_irq);
+
+
+static void print_irq(void)
+{
+ int i = SPRD_IRQ_NUM - 1;
+ if(!is_print_irq)
+ return;
+ do{
+ if(0 != sprd_irqs[i])
+ printk("##: sprd_irqs[%d] = %d.\n",
+ i, sprd_irqs[i]);
+ }while(--i >= 0);
+}
+#else
+static void print_irq(void){}
+#endif
+void irq_wakeup_set(void)
+{
+ is_wakeup = 1;
+}
+
+void time_add(unsigned int time, int ret)
+{
+ switch(sleep_mode){
+ case SLP_MODE_ARM:
+ core_time += time;
+ break;
+ case SLP_MODE_MCU:
+ mcu_time += time;
+ break;
+ case SLP_MODE_LIT:
+ lit_time += time;
+ break;
+ case SLP_MODE_DEP:
+ if(ret)
+ deep_time_successed += time;
+ else
+ deep_time_failed += time;
+ break;
+ default:
+ break;
+ }
+}
+
+void time_statisic_begin(void)
+{
+ core_time = 0;
+ mcu_time = 0;
+ lit_time = 0;
+ deep_time_successed = 0;
+ deep_time_failed = 0;
+ sleep_time = get_sys_cnt();
+ hard_irq_reset();
+}
+
+void time_statisic_end(void)
+{
+ sleep_time = get_sys_cnt() - sleep_time;
+}
+
+void print_time(void)
+{
+ if(!is_print_time)
+ return;
+ printk("time statisics : sleep_time=%d, core_time=%d, mcu_time=%d, lit_time=%d, deep_sus=%d, dep_fail=%d\n",
+ sleep_time, core_time, mcu_time, lit_time, deep_time_successed, deep_time_failed);
+}
+
+void set_sleep_mode(int sm){
+ int is_print = (sm == sleep_mode);
+ sleep_mode = sm;
+ if(is_print_sleep_mode == 0 || is_print )
+ return;
+ switch(sm){
+ case SLP_MODE_ARM:
+ printk("\n[ARM]\n");
+ break;
+ case SLP_MODE_MCU:
+ printk("\n[MCU]\n");
+ break;
+ case SLP_MODE_LIT:
+ printk("\n[LIT]\n");
+ break;
+ case SLP_MODE_DEP:
+ printk("\n[DEP]\n");
+ break;
+ default:
+ printk("\nNONE\n");
+ }
+}
+void clr_sleep_mode(void)
+{
+ sleep_mode = SLP_MODE_NON;
+}
+void print_statisic(void)
+{
+ print_time();
+ print_hard_irq();
+ print_irq();
+ pm_debug_dump_ahb_glb_regs();
+ if(is_print_wakeup){
+ printk("###wake up form %s : %08x\n", sleep_mode_str[sleep_mode], sprd_irqs_sts[0]);
+ printk("###wake up form %s : %08x\n", sleep_mode_str[sleep_mode], sprd_irqs_sts[1]);
+ }
+}
+#ifdef PM_PRINT_ENABLE
+static struct wake_lock messages_wakelock;
+#endif
+
+#define PM_PRINT_ENABLE
+#if defined(CONFIG_ARCH_SCX35L)
+void cp0_power_domain_debug(void)
+{
+ unsigned long cp0_arm9_0_cfg = __raw_readl(REG_PMU_APB_PD_CP0_ARM9_0_CFG);
+ unsigned long cp0_arm9_1_cfg = __raw_readl(REG_PMU_APB_PD_CP0_ARM9_1_CFG);
+ unsigned long cp0_hu3ge_cfg = __raw_readl(REG_PMU_APB_PD_CP0_HU3GE_CFG);
+ unsigned long cp0_gsm_0_cfg = __raw_readl(REG_PMU_APB_PD_CP0_GSM_0_CFG);
+ unsigned long cp0_gsm_1_cfg = __raw_readl(REG_PMU_APB_PD_CP0_GSM_1_CFG);
+ unsigned long cp0_td_cfg = __raw_readl(REG_PMU_APB_PD_CP0_TD_CFG);
+ unsigned long cp0_ceva_0_cfg = __raw_readl(REG_PMU_APB_PD_CP0_CEVA_0_CFG);
+ unsigned long cp0_ceva_1_cfg = __raw_readl(REG_PMU_APB_PD_CP0_CEVA_1_CFG);
+
+ printk("###---- REG_PMU_APB_PD_CP0_ARM9_0_CFG : 0x%08x\n", cp0_arm9_0_cfg);
+ printk("###---- REG_PMU_APB_PD_CP0_ARM9_1_CFG : 0x%08x\n", cp0_arm9_1_cfg);
+ printk("###---- REG_PMU_APB_PD_CP0_HU3GE_CFG : 0x%08x\n", cp0_hu3ge_cfg);
+ printk("###---- REG_PMU_APB_PD_CP0_GSM_0_CFG : 0x%08x\n", cp0_gsm_0_cfg);
+ printk("###---- REG_PMU_APB_PD_CP0_GSM_1_CFG : 0x%08x\n", cp0_gsm_1_cfg);
+ printk("###---- REG_PMU_APB_PD_CP0_TD_CFG : 0x%08x\n", cp0_td_cfg);
+ printk("###---- REG_PMU_APB_PD_CP0_CEVA_0_CFG : 0x%08x\n", cp0_ceva_0_cfg);
+ printk("###---- REG_PMU_APB_PD_CP0_CEVA_1_CFG : 0x%08x\n", cp0_ceva_1_cfg);
+}
+
+void cp1_power_domain_debug(void)
+{
+ unsigned long cp1_ca5_cfg = __raw_readl(REG_PMU_APB_PD_CP1_CA5_CFG);
+ unsigned long cp1_lte_p1_cfg = __raw_readl(REG_PMU_APB_PD_CP1_LTE_P1_CFG);
+ unsigned long cp1_lte_p2_cfg = __raw_readl(REG_PMU_APB_PD_CP1_LTE_P2_CFG);
+ unsigned long cp1_ceva_cfg = __raw_readl(REG_PMU_APB_PD_CP1_CEVA_CFG);
+ /*unsigned long cp1_comwrap_cfg = __raw_readl(REG_PMU_APB_PD_COMWRAP_CFG);*/
+
+ printk("###---- REG_PMU_APB_PD_CP1_CA5_CFG : 0x%08x\n", cp1_ca5_cfg);
+ printk("###---- REG_PMU_APB_PD_CP1_LTE_P1_CFG : 0x%08x\n", cp1_lte_p1_cfg);
+ printk("###---- REG_PMU_APB_PD_CP1_LTE_P2_CFG : 0x%08x\n", cp1_lte_p2_cfg);
+ printk("###---- REG_PMU_APB_PD_CP1_CEVA_CFG : 0x%08x\n", cp1_ceva_cfg);
+ /*printk("REG_PMU_APB_PD_COMWRAP_CFG ------ 0x%08x\n", cp1_comwrap_cfg);*/
+}
+#endif
+
+static void print_debug_info(void)
+{
+ unsigned int ahb_eb, apb_eb0, cp_slp_status0, cp_slp_status1, ldo_pd_ctrl,
+ ap_apb_eb, apb_pwrstatus0, apb_pwrstatus1, apb_pwrstatus2,
+ apb_pwrstatus3, mpll_cfg, dpll_cfg, emc_clk_cfg,
+ apb_slp_status, ap_sys_auto_sleep_cfg, ca5_lte_status;
+#if defined(CONFIG_ARCH_SCX15)
+ unsigned int ldo_dcdc_pd_ctrl;
+#endif
+#if defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L)
+ unsigned int mpll_cfg1, dpll_cfg1, aon_apb_eb1;
+#endif
+#if defined(CONFIG_ARCH_SCX35L)
+ unsigned int mpll_cfg2, dpll_cfg2;
+#endif
+ ahb_eb = sci_glb_read(REG_AP_AHB_AHB_EB, -1UL);
+ ap_sys_auto_sleep_cfg = sci_glb_read(REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG, -1UL);
+ ap_apb_eb = sci_glb_read(REG_AP_APB_APB_EB, -1UL);
+ apb_eb0 = sci_glb_read(REG_AON_APB_APB_EB0, -1UL);
+ cp_slp_status0 = sci_glb_read(REG_PMU_APB_CP_SLP_STATUS_DBG0, -1UL);
+#if !defined(CONFIG_ARCH_SCX35L)
+ cp_slp_status1 = sci_glb_read(REG_PMU_APB_CP_SLP_STATUS_DBG1, -1UL);
+#endif
+ ca5_lte_status = sci_glb_read(REG_PUB_APB_DDR_ID2QOS_RCFG9, -1UL);
+ apb_pwrstatus0 = sci_glb_read(REG_PMU_APB_PWR_STATUS0_DBG, -1UL);
+ apb_pwrstatus1 = sci_glb_read(REG_PMU_APB_PWR_STATUS1_DBG, -1UL);
+ apb_pwrstatus2 = sci_glb_read(REG_PMU_APB_PWR_STATUS2_DBG, -1UL);
+#if !defined(CONFIG_ARCH_SCX35L)
+ apb_pwrstatus3 = sci_glb_read(REG_PMU_APB_PWR_STATUS3_DBG, -1UL);
+#endif
+#if defined(CONFIG_ARCH_SCX35LT8)
+ apb_pwrstatus3 = sci_glb_read(REG_PMU_APB_PWR_STATUS3_DBG, -1UL);
+#endif
+ apb_slp_status = __raw_readl(REG_PMU_APB_SLEEP_STATUS);
+#if defined(CONFIG_ARCH_SCX15)
+ mpll_cfg = sci_glb_read(REG_AON_APB_MPLL_CFG, -1UL);
+ dpll_cfg = sci_glb_read(REG_AON_APB_DPLL_CFG, -1UL);
+#endif
+ emc_clk_cfg = sci_glb_read(REG_AON_CLK_EMC_CFG, -1UL);
+ ldo_pd_ctrl = sci_adi_read(ANA_REG_GLB_LDO_PD_CTRL);
+#if defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L)
+ mpll_cfg1 = sci_glb_read(REG_AON_APB_MPLL_CFG1, -1UL);
+ dpll_cfg1 = sci_glb_read(REG_AON_APB_DPLL_CFG1, -1UL);
+ aon_apb_eb1 = sci_glb_read(REG_AON_APB_APB_EB1, -1UL);
+#endif
+#if defined(CONFIG_ARCH_SCX35L)
+ mpll_cfg2 = sci_glb_read(REG_AON_APB_MPLL_CFG2, -1UL);
+ dpll_cfg2 = sci_glb_read(REG_AON_APB_DPLL_CFG2, -1UL);
+#endif
+#if defined(CONFIG_ARCH_SCX15)
+ ldo_dcdc_pd_ctrl = sci_adi_read(ANA_REG_GLB_LDO_DCDC_PD);
+#endif
+ unsigned long sleep_ctrl = __raw_readl(REG_PMU_APB_SLEEP_CTRL);
+ printk("###---- REG_PMU_APB_SLEEP_CTRL : 0x%08x\n", sleep_ctrl);
+ printk("###---- REG_AP_AHB_AHB_EB : 0x%08x\n", ahb_eb);
+ printk("###---- REG_AP_AHB_AP_SYS_AUTO_SLEEP_CFG : 0x%08x\n", ap_sys_auto_sleep_cfg);
+ printk("###---- REG_AP_APB_APB_EB : 0x%08x\n", ap_apb_eb);
+ printk("###---- REG_AON_APB_APB_EB0 : 0x%08x\n", apb_eb0);
+#if defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L)
+ printk("###---- REG_AON_APB_APB_EB1 : 0x%08x\n", aon_apb_eb1);
+#endif
+ printk("###---- REG_PMU_APB_CP_SLP_STATUS_DBG0 : 0x%08x\n", cp_slp_status0);
+#if !defined(CONFIG_ARCH_SCX35L)
+ printk("###---- REG_PMU_APB_CP_SLP_STATUS_DBG1 : 0x%08x\n", cp_slp_status1);
+#endif
+ printk("###---- REG_PMU_APB_PWR_STATUS0_DBG : 0x%08x\n", apb_pwrstatus0);
+ printk("###---- REG_PMU_APB_PWR_STATUS1_DBG : 0x%08x\n", apb_pwrstatus1);
+ printk("###---- REG_PMU_APB_PWR_STATUS2_DBG : 0x%08x\n", apb_pwrstatus2);
+ printk("###---- REG_PMU_APB_PWR_STATUS3_DBG : 0x%08x\n", apb_pwrstatus3);
+ printk("###---- REG_PMU_APB_SLEEP_STATUS : 0x%08x\n", apb_slp_status);
+ printk("###---- REG_PUB_APB_DDR_ID2QOS_RCFG9 : 0x%08x\n", ca5_lte_status);
+#if defined(CONFIG_ARCH_SCX15) || defined(CONFIG_ARCH_SCX35) || defined(CONFIG_ARCH_SCX30G)
+ printk("###---- REG_AON_APB_MPLL_CFG : 0x%08x\n", mpll_cfg);
+ printk("###---- REG_AON_APB_DPLL_CFG : 0x%08x\n", dpll_cfg);
+#endif
+ printk("###---- REG_AON_CLK_EMC_CFG : 0x%08x\n", emc_clk_cfg);
+ printk("###---- ANA_REG_GLB_LDO_PD_CTRL : 0x%08x\n", ldo_pd_ctrl);
+#if defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L)
+ printk("###---- REG_AON_APB_MPLL_CFG1 : 0x%08x\n", mpll_cfg1);
+ printk("###---- REG_AON_APB_DPLL_CFG1 : 0x%08x\n", dpll_cfg1);
+ printk("###---- REG_PMU_APB_DDR_SLEEP_CTRL : 0x%08x\n",
+ sci_glb_read(REG_PMU_APB_DDR_SLEEP_CTRL, -1UL) );
+#endif
+#if defined(CONFIG_ARCH_SCX35L)
+ printk("###---- REG_AON_APB_MPLL_CFG2 : 0x%08x\n", mpll_cfg2);
+ printk("###---- REG_AON_APB_DPLL_CFG2 : 0x%08x\n", dpll_cfg2);
+#endif
+#if defined(CONFIG_ARCH_SCX15)
+ printk("###---- ANA_REG_GLB_LDO_DCDC_PD_CTRL : 0x%08x\n", ldo_dcdc_pd_ctrl);
+#endif
+ if (apb_eb0 & BIT_GPU_EB)
+ printk("###---- BIT_GPU_EB still set ----###\n");
+ if (apb_eb0 & BIT_MM_EB)
+ printk("###---- BIT_MM_EB still set ----###\n");
+ if (apb_eb0 & BIT_CA7_DAP_EB)
+ printk("###---- BIT_CA7_DAP_EB still set ----###\n");
+
+ if (ahb_eb & BIT_GSP_EB)
+ printk("###---- BIT_GSP_EB still set ----###\n");
+#if defined(CONFIG_ARCH_SCX15) || defined(CONFIG_ARCH_SCX30G)
+ if (ahb_eb & BIT_DISPC1_EB)
+ printk("###---- BIT_DISPC1_EB still set ----###\n");
+#endif
+#if defined(CONFIG_ARCH_SCX35L)
+ if (ahb_eb & BIT_HSIC_EB)
+ printk("###---- BIT_HSIC_EB still set ----###\n");
+ if (ahb_eb & BIT_DISPC_EB)
+ printk("###---- BIT_DISPC_EB still set ----###\n");
+#else
+ if (ahb_eb & BIT_DISPC0_EB)
+ printk("###---- BIT_DISPC0_EB still set ----###\n");
+#endif
+ if (ahb_eb & BIT_SDIO0_EB)
+ printk("###---- SDIO0_EB still set ----###\n");
+ if (ahb_eb & BIT_SDIO1_EB)
+ printk("###---- SDIO1_EB still set ----###\n");
+ if (ahb_eb & BIT_SDIO2_EB)
+ printk("###---- BIT_SDIO2_EB still set ----###\n");
+#if defined(CONFIG_ARCH_SCX35L)
+ if (ahb_eb & BIT_OTG_EB)
+ printk("###---- BIT_OTG_EB still set ----###\n");
+#else
+ if (ahb_eb & BIT_USB_EB)
+ printk("###---- BIT_USB_EB still set ----###\n");
+#endif
+ if (ahb_eb & BIT_DMA_EB)
+ printk("###---- BIT_DMA_EB still set ----###\n");
+ if (ahb_eb & BIT_NFC_EB)
+ printk("###---- BIT_NFC_EB still set ----###\n");
+ if (ahb_eb & BIT_EMMC_EB)
+ printk("###---- BIT_EMMC_EB still set ----###\n");
+#if defined(CONFIG_ARCH_SCX15) || defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_64BIT)
+ if (ahb_eb & BIT_ZIPDEC_EB)
+ printk("###---- BIT_ZIPDEC_EB still set ----###\n");
+ if (ahb_eb & BIT_ZIPENC_EB)
+ printk("###---- BIT_ZIPENC_EB still set ----###\n");
+ if (ahb_eb & BIT_NANDC_ECC_EB)
+ printk("###---- BIT_NANDC_ECC_EB still set ----###\n");
+ if (ahb_eb & BIT_NANDC_2X_EB)
+ printk("###---- BIT_NANDC_2X_EB still set ----###\n");
+ if (ahb_eb & BIT_NANDC_EB)
+ printk("###---- BIT_NANDC_EB still set ----###\n");
+#endif
+#if defined(CONFIG_ARCH_SCX30G)
+ if (aon_apb_eb1 & BIT_GSP_EMC_EB )
+ printk("###---- BIT_GSP_EMC_EB set ----###\n");
+ if (aon_apb_eb1 & BIT_ZIP_EMC_EB )
+ printk("###---- BIT_ZIP_EMC_EB set ----###\n");
+ if (aon_apb_eb1 & BIT_DISP_EMC_EB )
+ printk("###---- BIT_DISP_EMC_EB set ----###\n");
+#endif
+
+ /*A-die*/
+ printk("---------- A-die power status -----------\n");
+ if (!(ldo_pd_ctrl & BIT_DCDC_WPA_PD))
+ printk("###---- BIT_DCDC_WPA_PD power on! ----###\n");
+#if defined(CONFIG_ADIE_SC2713S)
+ if (!(ldo_pd_ctrl & BIT_LDO_CLSG_PD))
+ printk("###---- BIT_LDO_CLSG_PD power on! ----###\n");
+#endif
+ if (!(ldo_pd_ctrl & BIT_LDO_USB_PD))
+ printk("###---- BIT_LDO_USB_PD power on! ----###\n");
+ if (!(ldo_pd_ctrl & BIT_LDO_CAMMOT_PD))
+ printk("###---- BIT_LDO_CAMMOT_PD power on! ----###\n");
+ if (!(ldo_pd_ctrl & BIT_LDO_CAMIO_PD))
+ printk("###---- BIT_LDO_CAMIO_PD power on! ----###\n");
+ if (!(ldo_pd_ctrl & BIT_LDO_CAMD_PD))
+ printk("###---- BIT_LDO_CAMD_PD power on! ----###\n");
+ if (!(ldo_pd_ctrl & BIT_LDO_CAMA_PD))
+ printk("###---- BIT_LDO_CAMA_PD power on! ----###\n");
+ if (!(ldo_pd_ctrl & BIT_LDO_SIM2_PD))
+ printk("###---- BIT_LDO_SIM2_PD power on! ----###\n");
+ if (!(ldo_pd_ctrl & BIT_LDO_SIM1_PD))
+ printk("###---- BIT_LDO_SIM1_PD power on! ----###\n");
+ if (!(ldo_pd_ctrl & BIT_LDO_SIM0_PD))
+ printk("###---- BIT_LDO_SIM0_PD power on! ----###\n");
+#if defined(CONFIG_ADIE_SC2713S)
+ if (!(ldo_pd_ctrl & BIT_LDO_SD_PD))
+ printk("###---- BIT_LDO_SD_PD power on! ----###\n");
+#endif
+#if defined(CONFIG_ADIE_SC2723S)
+ if (!(ldo_pd_ctrl & BIT_LDO_SDIO_PD))
+ printk("###---- BIT_LDO_SD_PD power on! ----###\n");
+#endif
+#if defined(CONFIG_ADIE_SC2723)
+ if (!(ldo_pd_ctrl & BIT_LDO_SDIO_PD))
+ printk("###---- BIT_LDO_SD_PD power on! ----###\n");
+#endif
+#if defined(CONFIG_ARCH_SCX15)
+ if (!(ldo_dcdc_pd_ctrl & BIT_BG_PD))
+ printk("###---- BIT_BG_PD power on! ----###\n");
+ if (!(ldo_dcdc_pd_ctrl & BIT_LDO_CON_PD))
+ printk("###---- BIT_LDO_CON_PD power on! ----###\n");
+ if (!(ldo_dcdc_pd_ctrl & BIT_LDO_DCXO_PD))
+ printk("###---- BIT_LDO_DCXO_PD power on! ----###\n");
+ if (!(ldo_dcdc_pd_ctrl & BIT_LDO_EMMCIO_PD))
+ printk("###---- BIT_LDO_EMMCIO_PD power on! ----###\n");
+ if (!(ldo_dcdc_pd_ctrl & BIT_LDO_EMMCCORE_PD))
+ printk("###---- BIT_LDO_EMMCCORE_PD power on! ----###\n");
+#endif
+
+#if defined(CONFIG_ARCH_SCX15)
+ if (ap_apb_eb & BIT_UART4_EB)
+ printk("###---- BIT_UART4_EB set! ----###\n");
+ if (ap_apb_eb & BIT_UART3_EB)
+ printk("###---- BIT_UART3_EB set! ----###\n");
+ if (ap_apb_eb & BIT_UART2_EB)
+ printk("###---- BIT_UART2_EB set! ----###\n");
+ if (ap_apb_eb & BIT_UART1_EB)
+ printk("###---- BIT_UART1_EB set! ----###\n");
+ if (ap_apb_eb & BIT_UART0_EB)
+ printk("###---- BIT_UART0_EB set! ----###\n");
+ if (ap_apb_eb & BIT_I2C4_EB)
+ printk("###---- BIT_I2C4_EB set! ----###\n");
+ if (ap_apb_eb & BIT_I2C3_EB)
+ printk("###---- BIT_I2C3_EB set! ----###\n");
+ if (ap_apb_eb & BIT_I2C2_EB)
+ printk("###---- BIT_I2C2_EB set! ----###\n");
+ if (ap_apb_eb & BIT_I2C1_EB)
+ printk("###---- BIT_I2C1_EB set! ----###\n");
+ if (ap_apb_eb & BIT_I2C0_EB)
+ printk("###---- BIT_I2C0_EB set! ----###\n");
+ if (ap_apb_eb & BIT_IIS3_EB)
+ printk("###---- BIT_IIS3_EB set! ----###\n");
+ if (ap_apb_eb & BIT_IIS2_EB)
+ printk("###---- BIT_IIS2_EB set! ----###\n");
+ if (ap_apb_eb & BIT_IIS1_EB)
+ printk("###---- BIT_IIS1_EB set! ----###\n");
+ if (ap_apb_eb & BIT_IIS0_EB)
+ printk("###---- BIT_IIS0_EB set! ----###\n");
+#endif
+
+#if defined(CONFIG_ARCH_SCX35LT8)
+ if (sleep_ctrl & BIT_VCP1_FORCE_LIGHT_SLEEP)
+ printk("###---- force VCP1 in light sleep ----###\n");
+ if (sleep_ctrl & BIT_VCP0_FORCE_LIGHT_SLEEP)
+ printk("###---- force VCP0 in light sleep ----###\n");
+ if (sleep_ctrl & BIT_CP1_FORCE_LIGHT_SLEEP)
+ printk("###---- force CP1 in light sleep ----###\n");
+ if (sleep_ctrl & BIT_CP0_FORCE_LIGHT_SLEEP)
+ printk("###---- force CP0 in light sleep ----###\n");
+ if (sleep_ctrl & BIT_AP_FORCE_LIGHT_SLEEP)
+ printk("###---- force AP in light sleep ----###\n");
+ if (sleep_ctrl & BIT_VCP1_LIGHT_SLEEP)
+ printk("###---- VCP1 is in light sleep status ----###\n");
+ if (sleep_ctrl & BIT_VCP0_LIGHT_SLEEP)
+ printk("###--- VCP0 is in light sleep status ----###\n");
+ if (sleep_ctrl & BIT_CP1_LIGHT_SLEEP)
+ printk("###---- CP1 is in light sleep status ----###\n");
+ if (sleep_ctrl & BIT_CP0_LIGHT_SLEEP)
+ printk("###---- CP0 is in light sleep status ----###\n");
+ if (sleep_ctrl & BIT_AP_LIGHT_SLEEP)
+ printk("###---- AP is in light sleep status ----###\n");
+
+ if (apb_pwrstatus0){
+ printk("status of power domain 'MM_TOP' 0x%08x\n", (apb_pwrstatus0 & BITS_PD_MM_TOP_STATE(15)) >> 28);
+ printk("status of power domain 'GPU_TOP' 0x%08x\n", (apb_pwrstatus0 & BITS_PD_GPU_TOP_STATE(15)) >> 24);
+ printk("status of power domain 'AP_SYS' 0x%08x\n", (apb_pwrstatus0 & BITS_PD_AP_SYS_STATE(15)) >> 20);
+ printk("status of power domain 'CA53_LIT_C3' 0x%08x\n", (apb_pwrstatus0 & BITS_PD_CA53_LIT_C3_STATE(15)) >> 16);
+ printk("status of power domain 'CA53_LIT_C2' 0x%08x\n", (apb_pwrstatus0 & BITS_PD_CA53_LIT_C2_STATE(15)) >> 12);
+ printk("status of power domain 'CA53_LIT_C1' 0x%08x\n", (apb_pwrstatus0 & BITS_PD_CA53_LIT_C1_STATE(15)) >> 8);
+ printk("status of power domain 'CA53_LIT_C0' 0x%08x\n", (apb_pwrstatus0 & BITS_PD_CA53_LIT_C0_STATE(15)) >> 4);
+ printk("status of power domain 'CA53_TOP' 0x%08x\n", apb_pwrstatus0 & BITS_PD_CA53_TOP_STATE(15));
+ }
+ if (apb_pwrstatus1){
+ printk("status of power domain 'CP0_CEVA_1' 0x%08x\n", (apb_pwrstatus1 & BITS_PD_CP0_CEVA_1_STATE(15)) >> 28);
+ printk("status of power domain 'CP_CEVA_0' 0x%08x\n", (apb_pwrstatus1 & BITS_PD_CP0_CEVA_0_STATE(15)) >> 24);
+ printk("status of power domain 'CP0_GSM_0' 0x%08x\n", (apb_pwrstatus1 & BITS_PD_CP0_GSM_0_STATE(15)) >> 20);
+ printk("status of power domain 'CP0_GSM_1' 0x%08x\n", (apb_pwrstatus1 & BITS_PD_CP0_GSM_1_STATE(15)) >> 16);
+ printk("status of power domain 'CP0_HU3GE' 0x%08x\n", (apb_pwrstatus1 & BITS_PD_CP0_HU3GE_STATE(15)) >> 12);
+ printk("status of power domain 'CP0_ARM9_1' 0x%08x\n", (apb_pwrstatus1 & BITS_PD_CP0_ARM9_1_STATE(15)) >> 8);
+ printk("status of power domain 'CP0_ARM9_0' 0x%08x\n", (apb_pwrstatus1 & BITS_PD_CP0_ARM9_0_STATE(15)) >> 4);
+ printk("status of power domain 'CP0_TD' 0x%08x\n", apb_pwrstatus1 & BITS_PD_CP0_TD_STATE(15));
+ }
+ if (apb_pwrstatus2){
+ printk("status of power domain 'CP0_PUB_SYS' 0x%08x\n", (apb_pwrstatus2 & BITS_PD_PUB_SYS_STATE(15)) >> 24);
+ printk("status of power domain 'CP1_COMWRAP' 0x%08x\n", (apb_pwrstatus2 & BITS_PD_CP1_COMWRAP_STATE(15)) >> 20);
+ printk("status of power domain 'CP1_LTE_P2' 0x%08x\n", (apb_pwrstatus2 & BITS_PD_CP1_LTE_P2_STATE(15)) >> 16);
+ printk("status of power domain 'CP1_LTE_P1' 0x%08x\n", (apb_pwrstatus2 & BITS_PD_CP1_LTE_P1_STATE(15)) >> 12);
+ printk("status of power domain 'CP1_CEVA' 0x%08x\n", (apb_pwrstatus2 & BITS_PD_CP1_CEVA_STATE(15)) >> 8);
+ printk("status of power domain 'CP1_CA5' 0x%08x\n", (apb_pwrstatus2 & BITS_PD_CP1_CA5_STATE(15)) >> 4);
+ printk("status of power domain 'CA53_LIT_MP4' 0x%08x\n", apb_pwrstatus2 & BITS_PD_CA53_LIT_MP4_STATE(15));
+ }
+ if (apb_pwrstatus3){
+ printk("###---- status of power domain 'MM_CODEC' 0x%08x----###\n", (apb_pwrstatus3 & BITS_PD_MM_CODEC_STATE(15)) >> 28);
+ printk("###---- status of power domain 'CA53_BIG_C3' 0x%08x----###\n", (apb_pwrstatus3 & BITS_PD_CA53_BIG_C3_STATE(15)) >> 24);
+ printk("###---- status of power domain 'CA53_BIG_C2' 0x%08x----###\n", (apb_pwrstatus3 & BITS_PD_CA53_BIG_C2_STATE(15)) >> 20);
+ printk("###---- status of power domain 'CA53_BIG_C1' 0x%08x----###\n", (apb_pwrstatus3 & BITS_PD_CA53_BIG_C1_STATE(15)) >> 16);
+ printk("###---- status of power domain 'CA53_BIG_C0' 0x%08x----###\n", (apb_pwrstatus3 & BITS_PD_CA53_BIG_C0_STATE(15)) >> 12);
+ printk("###---- status of power domain 'CA53_BIG_MP4' 0x%08x----###\n", (apb_pwrstatus3 & BITS_PD_CA53_BIG_MP4_STATE(15)) >> 8);
+ printk("###---- status of power domain 'GPU_C1' 0x%08x----###\n", (apb_pwrstatus3 & BITS_PD_GPU_C1_STATE(15)) >> 4);
+ printk("###---- status of power domain 'GPU_C0' 0x%08x----###\n", apb_pwrstatus3 & BITS_PD_GPU_C0_STATE(15));
+ }
+
+ if (mpll_cfg1 & BIT_MPLL_LOCK_DONE)
+ printk("###---- MPLL lock_none bit is set ----###\n");
+ if (mpll_cfg1 & BIT_MPLL_DIV_S)
+ printk("###---- MPLL feedback is fractional divider ----###");
+ if (mpll_cfg1 & BIT_MPLL_MOD_EN)
+ printk("###---- MPLL modulator MOD enable ----###\n");
+ if (mpll_cfg1 & BIT_MPLL_SDM_EN)
+ printk("###---- MPLL modulator SDM enable ----###\n");
+
+ if (dpll_cfg1 & BIT_DPLL_LOCK_DONE)
+ printk("###---- DPLL lock_node bit is set ----###\n");
+ if (dpll_cfg1 & BIT_DPLL_DIV_S)
+ printk("###---- DPLL feedback is fractional divider ----###\n");
+ if (dpll_cfg1 & BIT_DPLL_MOD_EN)
+ printk("###---- DPLL modulator MOD enable ----###\n");
+ if (dpll_cfg1 & BIT_DPLL_SDM_EN)
+ printk("###---- DPLL modulator SDM enable ----###\n");
+#endif
+
+
+}
+
+static int print_thread(void * data)
+{
+ while(1){
+ wake_lock(&messages_wakelock);
+ if (print_thread_enable)
+ print_debug_info();
+ has_wake_lock(WAKE_LOCK_SUSPEND);
+ msleep(100);
+ wake_unlock(&messages_wakelock);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(print_thread_interval * HZ);
+ }
+ return 0;
+}
+static void debugfs_init(void)
+{
+ dentry_debug_root = debugfs_create_dir("power", NULL);
+ if (IS_ERR(dentry_debug_root) || !dentry_debug_root) {
+ printk("!!!powermanager Failed to create debugfs directory\n");
+ dentry_debug_root = NULL;
+ return;
+ }
+ debugfs_create_u32("print_sleep_mode", 0644, dentry_debug_root,
+ &is_print_sleep_mode);
+ debugfs_create_u32("print_linux_clock", 0644, dentry_debug_root,
+ &is_print_linux_clock);
+ debugfs_create_u32("print_modem_clock", 0644, dentry_debug_root,
+ &is_print_modem_clock);
+ debugfs_create_u32("print_irq", 0644, dentry_debug_root,
+ &is_print_irq);
+ debugfs_create_u32("print_wakeup", 0644, dentry_debug_root,
+ &is_print_wakeup);
+ debugfs_create_u32("print_irq_runtime", 0644, dentry_debug_root,
+ &is_print_irq_runtime);
+ debugfs_create_u32("print_time", 0644, dentry_debug_root,
+ &is_print_time);
+ debugfs_create_u32("print_thread_enable", 0644, dentry_debug_root,
+ &print_thread_enable);
+ debugfs_create_u32("print_thread_interval", 0644, dentry_debug_root,
+ &print_thread_interval);
+}
+void pm_debug_init(void)
+{
+#ifdef PM_PRINT_ENABLE
+ struct task_struct * task;
+ wake_lock_init(&messages_wakelock, WAKE_LOCK_SUSPEND,
+ "pm_message_wakelock");
+ task = kthread_create(print_thread, NULL, "pm_print");
+ if (task == 0) {
+ printk("Can't crate power manager print thread!\n");
+ }else
+ wake_up_process(task);
+#endif
+ debugfs_init();
+}
+void pm_debug_clr(void)
+{
+ if(dentry_debug_root != NULL)
+ debugfs_remove_recursive(dentry_debug_root);
+}
+void pm_debug_dump_ahb_glb_regs(void){}
+void pm_debug_save_ahb_glb_regs(void){}
+#define WDG_BASE (ANA_WDG_BASE)
+#define SPRD_ANA_BASE (ANA_CTL_GLB_BASE)
+
+#define WDG_LOAD_LOW (WDG_BASE + 0x00)
+#define WDG_LOAD_HIGH (WDG_BASE + 0x04)
+#define WDG_CTRL (WDG_BASE + 0x08)
+#define WDG_INT_CLR (WDG_BASE + 0x0C)
+#define WDG_INT_RAW (WDG_BASE + 0x10)
+#define WDG_INT_MSK (WDG_BASE + 0x14)
+#define WDG_CNT_LOW (WDG_BASE + 0x18)
+#define WDG_CNT_HIGH (WDG_BASE + 0x1C)
+#define WDG_LOCK (WDG_BASE + 0x20)
+#define WDG_IRQ_LOW (WDG_BASE + 0x2c)
+#define WDG_IRQ_HIGH (WDG_BASE + 0x30)
+
+#define WDG_INT_EN_BIT BIT(0)
+#define WDG_CNT_EN_BIT BIT(1)
+#define WDG_NEW_VER_EN BIT(2)
+#define WDG_INT_CLEAR_BIT BIT(0)
+#define WDG_RST_CLEAR_BIT BIT(3)
+#define WDG_LD_BUSY_BIT BIT(4)
+#define WDG_RST_EN_BIT BIT(3)
+
+
+#define ANA_REG_BASE SPRD_ANA_BASE /* 0x82000600 */
+
+
+#define WDG_NEW_VERSION
+#define ANA_RST_STATUS (ANA_REG_BASE + 0xE8)
+#define ANA_AGEN (ANA_REG_BASE + 0x00)
+#define ANA_RTC_CLK_EN (ANA_REG_BASE + 0x08)
+
+#define AGEN_WDG_EN BIT(2)
+#define AGEN_RTC_WDG_EN BIT(2)
+
+#define WDG_CLK 32768
+#define WDG_UNLOCK_KEY 0xE551
+
+#define ANA_WDG_LOAD_TIMEOUT_NUM (10000)
+
+#ifdef WDG_NEW_VERSION
+#define WDG_LOAD_TIMER_VALUE(value) \
+ do{\
+ sci_adi_raw_write( WDG_LOAD_HIGH, (uint16_t)(((value) >> 16 ) & 0xffff));\
+ sci_adi_raw_write( WDG_LOAD_LOW , (uint16_t)((value) & 0xffff) );\
+ }while(0)
+#define WDG_LOAD_TIMER_INT_VALUE(value) \
+ do{\
+ sci_adi_raw_write( WDG_IRQ_HIGH, (uint16_t)(((value) >> 16 ) & 0xffff));\
+ sci_adi_raw_write( WDG_IRQ_LOW , (uint16_t)((value) & 0xffff) );\
+ }while(0)
+
+#else
+
+#define WDG_LOAD_TIMER_VALUE(value) \
+ do{\
+ uint32_t cnt = 0;\
+ sci_adi_raw_write( WDG_LOAD_HIGH, (uint16_t)(((value) >> 16 ) & 0xffff));\
+ sci_adi_raw_write( WDG_LOAD_LOW , (uint16_t)((value) & 0xffff) );\
+ while((sci_adi_read(WDG_INT_RAW) & WDG_LD_BUSY_BIT) && ( cnt < ANA_WDG_LOAD_TIMEOUT_NUM )) cnt++;\
+ }while(0)
+#endif
+/* use ana watchdog to wake up */
+void pm_debug_set_apwdt(void)
+{
+ uint32_t cnt = 0;
+ uint32_t ms = 5000;
+ cnt = (ms * WDG_CLK) / 1000;
+ sci_glb_set(INT_IRQ_ENB, BIT(11));
+ /*enable interface clk*/
+ sci_adi_set(ANA_AGEN, AGEN_WDG_EN);
+ /*enable work clk*/
+ sci_adi_set(ANA_RTC_CLK_EN, AGEN_RTC_WDG_EN);
+ sci_adi_raw_write(WDG_LOCK, WDG_UNLOCK_KEY);
+ sci_adi_set(WDG_CTRL, WDG_NEW_VER_EN);
+ WDG_LOAD_TIMER_VALUE(0x80000);
+ WDG_LOAD_TIMER_INT_VALUE(0x40000);
+ sci_adi_set(WDG_CTRL, WDG_CNT_EN_BIT | WDG_INT_EN_BIT| WDG_RST_EN_BIT);
+ sci_adi_raw_write(WDG_LOCK, (uint16_t) (~WDG_UNLOCK_KEY));
+ sci_adi_set(ANA_REG_INT_EN, BIT(3));
+#if 0
+ do{
+ udelay(1000);
+ printk("INTC1 mask:0x%08x raw:0x%08x en:0x%08x\n", __raw_readl(INTCV1_IRQ_MSKSTS),__raw_readl(INTCV1_IRQ_RAW), __raw_readl(INTCV1_IRQ_EN));
+ printk("INT mask:0x%08x raw:0x%08x en:0x%08x ana 0x%08x\n", __raw_readl(INT_IRQ_STS),__raw_readl(INT_IRQ_RAW), __raw_readl(INT_IRQ_ENB), sci_adi_read(ANA_REG_INT_MASK_STATUS));
+ printk("ANA mask:0x%08x raw:0x%08x en:0x%08x\n", sci_adi_read(ANA_REG_INT_MASK_STATUS), sci_adi_read(ANA_REG_INT_RAW_STATUS), sci_adi_read(ANA_REG_INT_EN));
+ printk("wdg cnt low 0x%08x high 0x%08x\n", sci_adi_read(WDG_CNT_LOW), sci_adi_read(WDG_CNT_HIGH));
+ }while(0);
+#endif
+}
+void pm_debug_clr_apwdt(void)
+{
+ //sci_adi_raw_write(WDG_LOCK, WDG_UNLOCK_KEY);
+ printk("watchdog int raw status: 0x%x\n", sci_adi_read(WDG_INT_RAW));
+ printk("watchdog int msk status: 0x%x\n", sci_adi_read(WDG_INT_MSK));
+ sci_adi_set(WDG_INT_CLR, WDG_INT_CLEAR_BIT | WDG_RST_CLEAR_BIT);
+ printk("watchdog int raw status: 0x%x\n", sci_adi_read(WDG_INT_RAW));
+ printk("watchdog int msk status: 0x%x\n", sci_adi_read(WDG_INT_MSK));
+ sci_adi_clr(WDG_CTRL, WDG_CNT_EN_BIT | WDG_RST_EN_BIT);
+ //sci_adi_raw_write(WDG_LOCK, (uint16_t) (~WDG_UNLOCK_KEY));
+}
diff --git a/drivers/platform/sprd/reserve.c b/drivers/platform/sprd/reserve.c
new file mode 100755
index 00000000..14412be5
--- /dev/null
+++ b/drivers/platform/sprd/reserve.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/board.h>
+#include <asm/memory.h>
+#include <linux/memblock.h>
+#include <linux/dma-contiguous.h>
+#include <linux/platform_device.h>
+//#include "devices.h"
+
+#include <asm/setup.h>
+
+static int __init __iomem_reserve_memblock(void)
+{
+ int ret;
+
+#ifndef CONFIG_CMA
+ if (memblock_is_region_reserved(SPRD_ION_MEM_BASE, SPRD_ION_MEM_SIZE))
+ return -EBUSY;
+ if (memblock_reserve(SPRD_ION_MEM_BASE, SPRD_ION_MEM_SIZE))
+ return -ENOMEM;
+#else
+#ifndef CONFIG_OF
+ ret = dma_declare_contiguous_reserved(&sprd_ion_dev.dev, SPRD_ION_MEM_SIZE, SPRD_ION_MEM_BASE, 0, CMA_RESERVE, CMA_THRESHOLD);
+ if (unlikely(ret))
+ {
+ pr_err("reserve CMA area(base:%x size:%x) for ION failed!!!\n", SPRD_ION_MEM_BASE, SPRD_ION_MEM_SIZE);
+ return -ENOMEM;
+ }
+ pr_info("reserve CMA area(base:%x size:%x) for ION\n", SPRD_ION_MEM_BASE, SPRD_ION_MEM_SIZE);
+#endif
+#endif
+ return 0;
+}
+
+#ifdef CONFIG_PSTORE_RAM
+int __init __ramconsole_reserve_memblock(void)
+{
+ if (memblock_is_region_reserved(SPRD_RAM_CONSOLE_START, SPRD_RAM_CONSOLE_SIZE))
+ return -EBUSY;
+ if (memblock_reserve(SPRD_RAM_CONSOLE_START, SPRD_RAM_CONSOLE_SIZE))
+ return -ENOMEM;
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_FB_LCD_RESERVE_MEM
+static int __init __fbmem_reserve_memblock(void)
+{
+ pr_err("__fbmem_reserve_memblock,SPRD_FB_MEM_BASE:%x,SPRD_FB_MEM_SIZE:%x\n",SPRD_FB_MEM_BASE,SPRD_FB_MEM_SIZE);
+ if (memblock_is_region_reserved(SPRD_FB_MEM_BASE, SPRD_FB_MEM_SIZE))
+ return -EBUSY;
+ if (memblock_reserve(SPRD_FB_MEM_BASE, SPRD_FB_MEM_SIZE))
+ return -ENOMEM;
+ pr_err("__fbmem_reserve_memblock-end,\n");
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_SIPC) && !defined(CONFIG_ARCH_SC8825)
+int __init __sipc_reserve_memblock(void)
+{
+ uint32_t smem_size = 0;
+
+#ifdef CONFIG_SIPC_TD
+ if (memblock_reserve(CPT_START_ADDR, CPT_TOTAL_SIZE))
+ return -ENOMEM;
+ smem_size += CPT_SMEM_SIZE;
+#endif
+
+#ifdef CONFIG_SIPC_WCDMA
+ if (memblock_reserve(CPW_START_ADDR, CPW_TOTAL_SIZE))
+ return -ENOMEM;
+ smem_size += CPW_SMEM_SIZE;
+#endif
+
+#ifdef CONFIG_SIPC_WCN
+ if (memblock_reserve(WCN_START_ADDR, WCN_TOTAL_SIZE))
+ return -ENOMEM;
+ smem_size += WCN_SMEM_SIZE;
+#endif
+
+#ifdef CONFIG_SIPC_GGE
+ if (memblock_reserve(GGE_START_ADDR, GGE_TOTAL_SIZE))
+ return -ENOMEM;
+ smem_size += GGE_SMEM_SIZE;
+#endif
+
+#ifdef CONFIG_SIPC_LTE
+ if (memblock_reserve(LTE_START_ADDR, LTE_TOTAL_SIZE))
+ return -ENOMEM;
+ smem_size += LTE_SMEM_SIZE;
+#endif
+
+ if (memblock_reserve(SIPC_SMEM_ADDR, smem_size))
+ return -ENOMEM;
+
+ return 0;
+}
+#endif
+
+#ifdef SPRD_ION_BASE_USE_VARIABLE
+phys_addr_t sprd_reserve_limit;
+extern phys_addr_t arm_lowmem_limit;
+#endif
+static int dram_cs_num = 0;
+static u32 dram_cs0_size = 0x0;
+static int __init mem_cs_num_get(char *str)
+{
+ dram_cs_num=simple_strtoul(str,0, 16);
+ printk("mem_cs_get dram_cs_num=%d\n",dram_cs_num);
+ return 0;
+}
+static int __init mem_cs0_size_get(char *str)
+{
+ dram_cs0_size=simple_strtoul(str,0, 16);
+ printk("mem_cs0_size_get dram_cs0_size =0x%08x\n",dram_cs0_size);
+ return 0;
+}
+early_param("mem_cs", mem_cs_num_get);
+early_param("mem_cs0_sz", mem_cs0_size_get);
+static int __ddr_training_memblock(void)
+{
+ memblock_reserve(CONFIG_PHYS_OFFSET, PAGE_SIZE);
+ if(2 == dram_cs_num) {
+ if(0==dram_cs0_size){
+ pr_err("dram_cs0_size = 0, error, please check the dram size\n");
+ return -ENOMEM;
+ }
+ memblock_reserve(CONFIG_PHYS_OFFSET+dram_cs0_size, PAGE_SIZE);
+ }
+ return 0;
+}
+void __init sci_reserve(void)
+{
+ int ret;
+#if defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L)
+ __ddr_training_memblock();
+#endif
+#ifdef SPRD_ION_BASE_USE_VARIABLE
+ /*sprd_reserve_limit is used save arm_lowmem_limit,will be use by ION*/
+ sprd_reserve_limit = arm_lowmem_limit;
+#endif
+
+#ifndef CONFIG_OF
+ ret = __iomem_reserve_memblock();
+ if (ret != 0)
+ pr_err("Fail to reserve mem for iomem. errno=%d\n", ret);
+
+#if defined(CONFIG_SIPC) && !defined(CONFIG_ARCH_SC8825)
+ ret = __sipc_reserve_memblock();
+ if (ret != 0)
+ pr_err("Fail to reserve mem for sipc. errno=%d\n", ret);
+#endif
+
+#ifdef CONFIG_PSTORE_RAM
+ ret = __ramconsole_reserve_memblock();
+ if (ret != 0)
+ pr_err("Fail to reserve mem for ram_console. errno=%d\n", ret);
+#endif
+
+#ifdef CONFIG_FB_LCD_RESERVE_MEM
+ ret = __fbmem_reserve_memblock();
+ if (ret != 0)
+ pr_err("Fail to reserve mem for framebuffer . errno=%d\n", ret);
+#endif
+#endif
+}
diff --git a/drivers/platform/sprd/sec_gps_s5n6420.c b/drivers/platform/sprd/sec_gps_s5n6420.c
new file mode 100755
index 00000000..61d91ef9
--- /dev/null
+++ b/drivers/platform/sprd/sec_gps_s5n6420.c
@@ -0,0 +1,140 @@
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <soc/sprd/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <soc/sprd/regulator.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+
+static struct device *gps_dev;
+extern struct class *sec_class;
+
+static void gps_clk_init(void)
+{
+ struct clk *gps_clk;
+ struct clk *clk_parent;
+ const char *clk_32k= "clk_aux0";
+
+ gps_clk = clk_get(NULL, clk_32k);
+ if (IS_ERR(gps_clk)) {
+ printk("clock: failed to get clk_aux0\n");
+ }
+ clk_parent = clk_get(NULL, "ext_32k");
+ if (IS_ERR(clk_parent)) {
+ printk("failed to get parent ext_32k\n");
+ }
+
+ clk_set_parent(gps_clk, clk_parent);
+ clk_set_rate(gps_clk, 32000);
+ if(!clk_prepare_enable(gps_clk))
+ printk("%s: %s enabled!!\n", __func__, clk_32k);
+ //clk_enable(gps_clk);
+}
+
+static int gps_regulator_on(void)
+{
+ int ret = 0;
+ const char *gps_node = "lsi,s5n6420";
+ struct device_node *root_node = NULL;
+ static struct regulator *gps_regulator = NULL;
+ const char *reg = NULL;
+
+ root_node = of_find_compatible_node(NULL, NULL, gps_node);
+ if(!root_node) {
+ WARN(1, "[GPS] failed to get device node of %s\n", gps_node);
+ return -ENODEV;
+ }
+ /* regulator enable from dts */
+ if(of_property_read_string(root_node, "gps-regulator", &reg)) {
+ pr_err("%s: main(1.8v) regulator field is not exsit\n", __func__);
+ ret = -ENODEV;
+ goto err_find_node;
+ } else {
+ gps_regulator = regulator_get(NULL, reg);
+ if (IS_ERR(gps_regulator)){
+ gps_regulator = NULL;
+ return -EIO;
+ } else {
+ regulator_set_voltage(gps_regulator, 1800000, 1800000);
+ if(!regulator_enable(gps_regulator))
+ printk("%s: (%s) main(1.8v) regulator turned ON!!\n", __func__, reg);
+ }
+ }
+ return 0;
+
+err_find_node:
+ of_node_put(root_node);
+ return ret;
+}
+
+static unsigned int gps_pwr_on = 0;
+static unsigned int gps_reset = 0;
+
+static int __init gps_s5n6420_init(void)
+{
+ pr_info("%s\n", __func__);
+ gps_regulator_on();
+ gps_clk_init();
+
+ int ret = 0;
+ const char *gps_node = "lsi,s5n6420";
+ const char *gps_pwr_en = "gps-pwr-en";
+ const char *gps_nRst = "gps-reset";
+ struct device_node *root_node = NULL;
+
+ BUG_ON(!sec_class);
+ gps_dev = device_create(sec_class, NULL, 0, NULL, "gps");
+ BUG_ON(!gps_dev);
+
+ root_node = of_find_compatible_node(NULL, NULL, gps_node);
+ if(!root_node) {
+ WARN(1, "failed to get device node of bcm4752\n");
+ ret = -ENODEV;
+ goto err_sec_device_create;
+ }
+
+ gps_pwr_on = of_get_named_gpio(root_node, gps_pwr_en, 0);
+ if(!gpio_is_valid(gps_pwr_on)) {
+ WARN(1, "Invalid gpio pin : %d\n", gps_pwr_on);
+ ret = -ENODEV;
+ goto err_find_node;
+ }
+ if (gpio_request(gps_pwr_on, "GPS_PWR_EN")) {
+ WARN(1, "fail to request gpio(GPS_PWR_EN)\n");
+ ret = -ENODEV;
+ goto err_find_node;
+ }
+ gps_reset= of_get_named_gpio(root_node, gps_nRst, 0);
+ if(!gpio_is_valid(gps_reset)) {
+ WARN(1, "Invalid gpio pin : %d\n", gps_reset);
+ ret = -ENODEV;
+ goto err_find_node;
+ }
+ if (gpio_request(gps_reset, "GPS_RESET")) {
+ WARN(1, "fail to request gpio(GPS_RESET)\n");
+ ret = -ENODEV;
+ goto err_find_node;
+ }
+
+ gpio_direction_output(gps_pwr_on, 0);
+ gpio_export(gps_pwr_on, 1);
+ gpio_export_link(gps_dev, "GPS_PWR_EN", gps_pwr_on);
+
+ gpio_direction_output(gps_reset, 1);
+ gpio_export(gps_reset, 1);
+ gpio_export_link(gps_dev, "GPS_RESET", gps_reset);
+
+ return 0;
+
+err_find_node:
+ of_node_put(root_node);
+err_sec_device_create:
+ device_destroy(gps_dev, gps_dev->devt);
+ return ret;
+}
+
+device_initcall(gps_s5n6420_init);
diff --git a/drivers/platform/sprd/sprd_debug.c b/drivers/platform/sprd/sprd_debug.c
new file mode 100644
index 00000000..1025c20e
--- /dev/null
+++ b/drivers/platform/sprd/sprd_debug.c
@@ -0,0 +1,249 @@
+/*
+ * sprd_debug.c
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/sysrq.h>
+//#include <mach/regs-pmu.h>
+
+/* FIXME: This is temporary solution come from Midas */
+#ifndef CONFIG_64BIT
+#include <asm/outercache.h>
+#endif
+#include <asm/cacheflush.h>
+#include <asm/io.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/swiotlb.h>
+#include <soc/sprd/pm_debug.h>
+#include <soc/sprd/sprd_persistent_clock.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/system.h>
+#include <soc/sprd/sprd_debug.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/regs_debug.h>
+
+#define MY_DEBUG 0
+
+struct sprd_debug_regs_access *sprd_debug_last_regs_access;
+EXPORT_SYMBOL(sprd_debug_last_regs_access);
+
+/* enable/disable sprd_debug feature
+ * level = 0 when enable = 0 && enable_user = 0
+ * level = 1 when enable = 1 && enable_user = 0
+ * level = 0x10001 when enable = 1 && enable_user = 1
+ * The other cases are not considered
+ */
+union sprd_debug_level_t sprd_debug_level = { .en.kernel_fault = 1, };
+
+module_param_named(enable, sprd_debug_level.en.kernel_fault, ushort, 0644);
+module_param_named(enable_user, sprd_debug_level.en.user_fault, ushort, 0644);
+module_param_named(level, sprd_debug_level.uint_val, uint, 0644);
+
+
+static const char *gkernel_sprd_build_info_date_time[] = {
+ __DATE__,
+ __TIME__
+};
+
+static char gkernel_sprd_build_info[100];
+
+/* klaatu - schedule log */
+#ifdef CONFIG_SPRD_DEBUG_SCHED_LOG
+static struct sched_log sprd_debug_log __cacheline_aligned;
+static atomic_t task_log_idx[NR_CPUS] = { ATOMIC_INIT(-1), ATOMIC_INIT(-1) };
+static atomic_t irq_log_idx[NR_CPUS] = { ATOMIC_INIT(-1), ATOMIC_INIT(-1) };
+static atomic_t work_log_idx[NR_CPUS] = { ATOMIC_INIT(-1), ATOMIC_INIT(-1) };
+static atomic_t hrtimer_log_idx[NR_CPUS] = { ATOMIC_INIT(-1), ATOMIC_INIT(-1) };
+struct sched_log (*psprd_debug_log) = (&sprd_debug_log);
+#endif
+
+
+
+void sprd_debug_save_pte(void *pte, int task_addr)
+{
+
+// memcpy(&per_cpu(sprd_debug_fault_status,smp_processor_id()), pte,sizeof(struct sprd_debug_fault_status_t));
+// per_cpu(sprd_debug_fault_status,smp_processor_id()).cur_process_magic =task_addr;
+}
+void sprd_debug_check_crash_key(unsigned int code, int value)
+{
+ static bool volup_p;
+ static bool voldown_p;
+ static int loopcount;
+
+ if (!sprd_debug_level.en.kernel_fault)
+ return;
+
+#if 0
+ /* Must be deleted later */
+ pr_info("Test %s:key code(%d) value(%d),(up:%d,down:%d)\n", __func__, code, value, volup_p, voldown_p);
+#endif
+
+ /* Enter Force Upload
+ * Hold volume down key first
+ * and then press power key twice
+ * and volume up key should not be pressed
+ */
+ if (value) {
+ if (code == KEY_VOLUMEUP)
+ volup_p = true;
+ if (code == KEY_VOLUMEDOWN)
+ voldown_p = true;
+ if (volup_p && voldown_p) {
+ static unsigned long stack_dump_jiffies = 0;
+
+ if (!stack_dump_jiffies || jiffies > stack_dump_jiffies + HZ * 10) {
+ pr_info("trigger sysrq w & m\n");
+
+ /* show blocked thread */
+ handle_sysrq('w');
+
+ /* show memory */
+ handle_sysrq('m');
+ stack_dump_jiffies = jiffies;
+ }
+ if (code == KEY_POWER) {
+ pr_info("%s: Crash key count : %d\n", __func__, ++loopcount);
+ if (loopcount == 2)
+ panic("Crash Key");
+ }
+ }
+ } else {
+ if (code == KEY_VOLUMEUP)
+ volup_p = false;
+ if (code == KEY_VOLUMEDOWN) {
+ loopcount = 0;
+ voldown_p = false;
+ }
+ }
+}
+
+static void sprd_debug_set_build_info(void)
+{
+ char *p = gkernel_sprd_build_info;
+ sprintf(p, "Kernel Build Info : ");
+ strcat(p, " Date:");
+ strncat(p, gkernel_sprd_build_info_date_time[0], 12);
+ strcat(p, " Time:");
+ strncat(p, gkernel_sprd_build_info_date_time[1], 9);
+}
+
+__init int sprd_debug_init(void)
+{
+ dma_addr_t addr;
+
+ if (!sprd_debug_level.en.kernel_fault)
+ return -1;
+
+ sprd_debug_set_build_info();
+#ifndef CONFIG_64BIT
+ sprd_debug_last_regs_access = (struct sprd_debug_regs_access*)dma_alloc_coherent(
+ NULL, sizeof(struct sprd_debug_regs_access)*NR_CPUS, &addr, GFP_KERNEL);
+#else
+ sprd_debug_last_regs_access = (struct sprd_debug_regs_access*)swiotlb_alloc_coherent(
+ NULL, sizeof(struct sprd_debug_regs_access)*NR_CPUS, &addr, GFP_KERNEL);
+#endif
+ printk("*** %s, size:%u, sprd_debug_last_regs_access:%p *** \n",
+ __func__, sizeof(struct sprd_debug_regs_access)*NR_CPUS, sprd_debug_last_regs_access);
+
+ return 0;
+}
+subsys_initcall(sprd_debug_init);
+
+int get_sprd_debug_level(void)
+{
+ return sprd_debug_level.uint_val;
+}
+
+/* klaatu - schedule log */
+#ifdef CONFIG_SPRD_DEBUG_SCHED_LOG
+void __sprd_debug_task_log(int cpu, struct task_struct *task)
+{
+ unsigned i;
+
+ i = atomic_inc_return(&task_log_idx[cpu]) &
+ (ARRAY_SIZE(psprd_debug_log->task[0]) - 1);
+ psprd_debug_log->task[cpu][i].time = cpu_clock(cpu);
+#ifdef CP_DEBUG
+ psprd_debug_log->task[cpu][i].sys_cnt = get_sys_cnt();
+#endif
+ strcpy(psprd_debug_log->task[cpu][i].comm, task->comm);
+ psprd_debug_log->task[cpu][i].pid = task->pid;
+}
+
+void __sprd_debug_irq_log(unsigned int irq, void *fn, int en)
+{
+ int cpu = raw_smp_processor_id();
+ unsigned i;
+
+ i = atomic_inc_return(&irq_log_idx[cpu]) &
+ (ARRAY_SIZE(psprd_debug_log->irq[0]) - 1);
+ psprd_debug_log->irq[cpu][i].time = cpu_clock(cpu);
+#ifdef CP_DEBUG
+ psprd_debug_log->irq[cpu][i].sys_cnt = get_sys_cnt();
+#endif
+ psprd_debug_log->irq[cpu][i].irq = irq;
+ psprd_debug_log->irq[cpu][i].fn = (void *)fn;
+ psprd_debug_log->irq[cpu][i].en = en;
+}
+
+void __sprd_debug_work_log(struct worker *worker,
+ struct work_struct *work, work_func_t f)
+{
+ int cpu = raw_smp_processor_id();
+ unsigned i;
+
+ i = atomic_inc_return(&work_log_idx[cpu]) &
+ (ARRAY_SIZE(psprd_debug_log->work[0]) - 1);
+ psprd_debug_log->work[cpu][i].time = cpu_clock(cpu);
+#ifdef CP_DEBUG
+ psprd_debug_log->work[cpu][i].sys_cnt = get_sys_cnt();
+#endif
+ psprd_debug_log->work[cpu][i].worker = worker;
+ psprd_debug_log->work[cpu][i].work = work;
+ psprd_debug_log->work[cpu][i].f = f;
+}
+
+void __sprd_debug_hrtimer_log(struct hrtimer *timer,
+ enum hrtimer_restart (*fn) (struct hrtimer *), int en)
+{
+ int cpu = raw_smp_processor_id();
+ unsigned i;
+
+ i = atomic_inc_return(&hrtimer_log_idx[cpu]) &
+ (ARRAY_SIZE(psprd_debug_log->hrtimers[0]) - 1);
+ psprd_debug_log->hrtimers[cpu][i].time = cpu_clock(cpu);
+#ifdef CP_DEBUG
+ psprd_debug_log->hrtimers[cpu][i].sys_cnt = get_sys_cnt();
+#endif
+ psprd_debug_log->hrtimers[cpu][i].timer = timer;
+ psprd_debug_log->hrtimers[cpu][i].fn = fn;
+ psprd_debug_log->hrtimers[cpu][i].en = en;
+}
+
+int get_task_log_idx(int cpu)
+{
+ return atomic_inc_return(&task_log_idx[cpu]) & (ARRAY_SIZE(psprd_debug_log->task[0]) - 1);
+}
+
+int get_irq_log_idx(int cpu)
+{
+ return atomic_inc_return(&irq_log_idx[cpu]) & (ARRAY_SIZE(psprd_debug_log->irq[0]) - 1);
+}
+
+#endif /* CONFIG_SPRD_DEBUG_SCHED_LOG */
+
diff --git a/drivers/platform/sprd/sprd_mem_pool.c b/drivers/platform/sprd/sprd_mem_pool.c
new file mode 100644
index 00000000..dcfa658d
--- /dev/null
+++ b/drivers/platform/sprd/sprd_mem_pool.c
@@ -0,0 +1,470 @@
+/* linux/arch/arm/mach-sc8810/sprd_mem_pool.c
+ *
+ * Copyright (C) 2010 Spreadtrum
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/mm.h>
+#include <linux/mmzone.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/pid.h>
+#include <linux/mutex.h>
+#include <asm/current.h>
+#include <asm/uaccess.h>
+#include <linux/pagemap.h>
+#include <linux/pfn.h>
+#include <linux/memory.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+
+/**
+ * add another order pool, plz modify:
+ * 1.PAGE_POOL_MAX_COUNT: +1
+ * 2.page_pool struct: +node
+ * */
+
+/*Magic Numbers*/
+#define DEBUG_PRINT 1 /*used for debug*/
+#define UNSIGNED_LONG_MAX 0xffffffff
+#define PAGE_POOL_MAX_COUNT 5
+
+/*status record struct declare*/
+struct status {
+ unsigned long used;
+ unsigned long free;
+ unsigned long peak;
+ unsigned long total;
+};
+
+/*page pool struct definition*/
+static struct {
+ unsigned long order;
+ unsigned long threshold;
+ unsigned long node_count;
+ unsigned long *buffer;
+ struct mutex lock;
+ struct status stat;
+ struct workqueue_struct *sprd_alloc_wq;
+ struct work_struct sprd_alloc_work;
+} page_pool[PAGE_POOL_MAX_COUNT] = {
+ {
+ .order = 0,
+ .buffer = NULL,
+ .sprd_alloc_wq = NULL,
+ }, {
+ .order = 1,
+ .threshold = 2,
+ .node_count = 15,
+ .buffer = NULL,
+ .lock = __MUTEX_INITIALIZER(page_pool[1].lock),
+ .stat.used = 0,
+ .stat.free = 0,
+ .stat.peak = 0,
+ .stat.total = 0,
+ .sprd_alloc_wq = NULL,
+ }, {
+ .order = 2,
+ .threshold = 2,
+ .node_count = 10,
+ .buffer = NULL,
+ .lock = __MUTEX_INITIALIZER(page_pool[2].lock),
+ .stat.used = 0,
+ .stat.free = 0,
+ .stat.peak = 0,
+ .stat.total = 0,
+ .sprd_alloc_wq = NULL,
+ }, {
+ .order = 3,
+ .threshold = 1,
+ .node_count = 3,
+ .buffer = NULL,
+ .lock = __MUTEX_INITIALIZER(page_pool[3].lock),
+ .stat.used = 0,
+ .stat.free = 0,
+ .stat.peak = 0,
+ .stat.total = 0,
+ .sprd_alloc_wq = NULL,
+ }, {
+ .order = 4,
+ .threshold = 1,
+ .node_count = 2,
+ .buffer = NULL,
+ .lock = __MUTEX_INITIALIZER(page_pool[4].lock),
+ .stat.used = 0,
+ .stat.free = 0,
+ .stat.peak = 0,
+ .stat.total = 0,
+ .sprd_alloc_wq = NULL,
+ }
+};
+
+/*global variables*/
+static unsigned long sys_buddy_info[MAX_ORDER];
+static unsigned long alloc_flag = 1;
+static int sprd_alloc_workqueue_pid = -1;
+
+/*extern functions*/
+extern void msleep(unsigned int msecs);
+extern void dump_stack(void);
+struct mutex sprd_mem_stat_lock;
+static int sprd_mem_pool_stat = 0;
+static unsigned long long sprd_mem_alloc_count = 0;
+
+/*internal functions*/
+static unsigned long pow2(unsigned long x, unsigned long y)
+{
+ unsigned long result = 0;
+
+ /*x == 0*/
+ if(!x) return 0;
+
+ /*y == 0*/
+ if(!y) return 1;
+
+ /*x > 0, y > 0*/
+ result = x * pow2(x, (y - 1));
+
+ if(result > UNSIGNED_LONG_MAX) {
+ result = UNSIGNED_LONG_MAX;
+ printk("__SPRD__INIT__: call pow2(%lu, %lu) error!!!\n", x, y);
+ }
+
+ return result;
+}
+
+static void dumpstack(void)
+{
+ printk("Process Name: %s, Process Pid: %d, Parent Name: %s, Parent Pid: %d\n",
+ current->comm, current->pid, current->parent->comm, current->parent->pid);
+ dump_stack();
+}
+
+static struct page *address_to_pages(unsigned long address)
+{
+ if(!address) return NULL;
+
+#if defined(WANT_PAGE_VIRTUAL)
+ return container_of((void *)(address), struct page, virtual);
+#else
+ return virt_to_page(address);
+#endif
+}
+
+static void get_buddy_info(void)
+{
+ struct zone *zone;
+ unsigned int order = 0;
+
+ /*normal zone only*/
+ for_each_zone(zone) {
+ if(is_normal(zone)) break;
+ }
+
+ /*global buddyinfo*/
+ for(order = 0; order < MAX_ORDER; order++) {
+ sys_buddy_info[order] = zone->free_area[order].nr_free;
+ }
+}
+
+static unsigned long get_page_count(unsigned int entry_order)
+{
+ struct zone *zone;
+ unsigned int order = 0;
+ unsigned long page_count = 0;
+
+ /*normal zone only*/
+ for_each_zone(zone) {
+ if(is_normal(zone)) break;
+ }
+
+ /*forall buddy pages*/
+ for(order = 0; order < MAX_ORDER; order++) {
+ /*get page counts*/
+ if(order < entry_order) continue;
+
+ page_count += (zone->free_area[order].nr_free << (order - entry_order));
+ }
+
+ return page_count;
+}
+
+static unsigned long sprd_alloc_one(unsigned long order)
+{
+ unsigned long sub = 0;
+ unsigned long address = 0;
+
+ /*lock*/
+ mutex_lock(&(page_pool[order].lock));
+
+ /*for order page pool*/
+ for(sub = 0; sub < page_pool[order].node_count; sub++) {
+
+ /*jump over null page*/
+ if(!page_pool[order].buffer[sub]) continue;
+
+ /*get first !null page*/
+ address = page_pool[order].buffer[sub];
+ page_pool[order].buffer[sub] = 0;
+
+ /*status record*/
+ page_pool[order].stat.free--;
+ page_pool[order].stat.used++;
+
+ mutex_lock(&sprd_mem_stat_lock);
+ sprd_mem_alloc_count++;
+ sprd_mem_pool_stat = 1;
+ mutex_unlock(&sprd_mem_stat_lock);
+
+ if(strncmp(current->comm, "sprd-page-alloc", 13)) {
+ if(page_pool[order].stat.peak < page_pool[order].stat.used)
+ page_pool[order].stat.peak = page_pool[order].stat.used;
+ if(page_pool[order].stat.total < UNSIGNED_LONG_MAX)
+ page_pool[order].stat.total++;
+ }
+ goto end;
+ }
+
+end:
+ /*unlock*/
+ mutex_unlock(&(page_pool[order].lock));
+
+#if DEBUG_PRINT
+ printk("__SPRD__ALLOC__ONE__: buffer for order = %lu empty; free = %lu, used = %lu, sys = %lu; peak = %lu, total = %lu; Call Stack:\n",
+ order, page_pool[order].stat.free, page_pool[order].stat.used,
+ get_page_count(order), page_pool[order].stat.peak, page_pool[order].stat.total);
+ dumpstack();
+#endif
+ return address;
+}
+
+int sprd_page_mask_check(int pid)
+{
+ if(pid == sprd_alloc_workqueue_pid) return -1;
+
+ return 0;
+}
+
+struct page *sprd_page_alloc(gfp_t gfp_mask, unsigned int order, unsigned long zoneidx)
+{
+ unsigned long address = 0;
+ struct page *page = NULL;
+
+ /*check some flags*/
+ if((!alloc_flag) || (GFP_KERNEL != gfp_mask) || (ZONE_NORMAL != zoneidx) || (order > PAGE_POOL_MAX_COUNT - 1) || (order < 1))
+ goto Failed;
+
+ /*alloc*/
+ address = sprd_alloc_one((unsigned long)order);
+ if(!address) goto Failed;
+
+ /*convert address to page*/
+ page = address_to_pages(address);
+
+ return page;
+
+Failed:
+ return NULL;
+}
+
+static int sprd_show_pages_info(struct seq_file *m, void *v)
+{
+ unsigned long i = 0;
+ unsigned long start = 0;
+
+ seq_printf(m,
+ "sprd pool summery:\n"
+ " status: %s\n",
+ (alloc_flag ? "open" : "closed"));
+ for(start = 1; start < PAGE_POOL_MAX_COUNT; start++) {
+ seq_printf(m,
+ " free node (order=%lu) = %lu, used node (order=%lu) = %lu\n",
+ start, page_pool[start].stat.free, start, page_pool[start].stat.used);
+ }
+ for(start = 1; start < PAGE_POOL_MAX_COUNT; start++) {
+ seq_printf(m,
+ " system memory count (order=%lu) = %lu\n",
+ start, get_page_count(start));
+ }
+ for(start = 1; start < PAGE_POOL_MAX_COUNT; start++) {
+ seq_printf(m,
+ " alloc peak (order=%lu) = %lu, alloc total (order=%lu) = %lu\n",
+ start, page_pool[start].stat.peak, start, page_pool[start].stat.total);
+ }
+
+ seq_printf(m, "buddy info:\n");
+ get_buddy_info();
+ for(i = 0; i < MAX_ORDER; i++) {
+ seq_printf(m, " orders = %lu, number = %lu\n", i, sys_buddy_info[i]);
+ }
+
+ for(start = 1; start < PAGE_POOL_MAX_COUNT; start++) {
+ seq_printf(m, "pool node (order=%lu) details:\n", start);
+ for(i = 0; i < page_pool[start].node_count; i++) {
+ struct page *page = NULL;
+ page = address_to_pages(page_pool[start].buffer[i]);
+ seq_printf(m, " subscript = %lu, value = %p, page = %p, page->flags %lx\n",
+ i, (void *)(page_pool[start].buffer[i]), page, (page ? page->flags : 0x00));
+ }
+ }
+
+ seq_printf(m, "sprd_mem_alloc_count %d\n", sprd_mem_alloc_count);
+ seq_printf(m, "sprd_mem_pool_stat %d\n", sprd_mem_pool_stat);
+ sprd_alloc_one(3);
+ return 0;
+}
+
+static int sprd_pages_info_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, sprd_show_pages_info, NULL);
+}
+
+static int sprd_pages_info_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos)
+{
+ char flag;
+
+ if (copy_from_user(&flag, buf, 1)) return -EFAULT;
+
+ switch (flag) {
+ case '0': alloc_flag = 0; break;
+ case '1': alloc_flag = 1; break;
+ default: break;
+ }
+
+ return 1;
+}
+
+static const struct file_operations sprd_page_info_fops = {
+ .open = sprd_pages_info_open,
+ .read = seq_read,
+ .write = sprd_pages_info_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int sprd_mem_pool_daemon(void *arg)
+{
+ while (1) {
+ int start = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(1000));
+ set_current_state(TASK_RUNNING);
+
+ if (!sprd_mem_pool_stat)
+ continue;
+
+ mutex_lock(&sprd_mem_stat_lock);
+ sprd_mem_pool_stat = 0;
+ mutex_unlock(&sprd_mem_stat_lock);
+
+ for(start = PAGE_POOL_MAX_COUNT - 1; start > 0; start--) {
+ unsigned long sub = 0;
+
+ if (page_pool[start].node_count == page_pool[start].stat.free)
+ continue;
+ else {
+ mutex_lock(&sprd_mem_stat_lock);
+ if (!sprd_mem_pool_stat)
+ sprd_mem_pool_stat = 1;
+ mutex_unlock(&sprd_mem_stat_lock);
+ }
+
+ /*for single page buffer*/
+ for(sub = 0; sub < page_pool[start].node_count; sub++) {
+ mutex_lock(&(page_pool[start].lock));
+ gfp_t gfp_mask = GFP_KERNEL;
+
+ /*jump !null node*/
+ if(page_pool[start].buffer[sub]) {
+ mutex_unlock(&(page_pool[start].lock));
+ continue;
+ }
+
+ /*check if sys pages enough*/
+ if(page_pool[start].threshold > get_page_count(page_pool[start].order)) {
+ mutex_unlock(&(page_pool[start].lock));
+ break;
+ }
+
+ /*alloc page from sys for null node*/
+ page_pool[start].buffer[sub] = __get_free_pages(gfp_mask, page_pool[start].order);
+ if(!page_pool[start].buffer[sub]) {
+ mutex_unlock(&(page_pool[start].lock));
+ break;
+ }
+
+ /*stats record*/
+ page_pool[start].stat.free++;
+ page_pool[start].stat.used--;
+ mutex_unlock(&(page_pool[start].lock));
+ }
+ }
+
+ mutex_lock(&sprd_mem_stat_lock);
+ if(!sprd_mem_pool_stat)
+ sprd_mem_alloc_count = 0;
+ mutex_unlock(&sprd_mem_stat_lock);
+ }
+}
+
+static int __init sprd_pages_init(void)
+{
+ int start = 0, mem_sum = 0;
+ gfp_t gfp_mask = GFP_KERNEL;
+ struct workqueue_struct *sprd_alloc_wq = NULL;
+ struct task_struct *p = NULL;
+
+ /*init global*/
+ memset(sys_buddy_info, 0, sizeof(sys_buddy_info));
+ mutex_init(&sprd_mem_stat_lock);
+
+ /*init page pool*/
+ for(start = 1; start < PAGE_POOL_MAX_COUNT; start++) {
+
+ /*node buffer*/
+ unsigned long sub = 0;
+ page_pool[start].buffer = kmalloc((page_pool[start].node_count * sizeof(unsigned long)), gfp_mask);
+ memset(page_pool[start].buffer, 0, (page_pool[start].node_count * sizeof(unsigned long)));
+
+ for(sub = 0; sub < page_pool[start].node_count; sub++) {
+ /*alloc page from sys*/
+ page_pool[start].buffer[sub] = __get_free_pages(gfp_mask, page_pool[start].order);
+ /*stats record*/
+ page_pool[start].buffer[sub] ? page_pool[start].stat.free++ : page_pool[start].stat.used++;
+ }
+
+ /*alloc total size*/
+ mem_sum += page_pool[start].node_count * 4 * pow2(2, page_pool[start].order);
+
+#if DEBUG_PRINT
+ printk("__SPRD__INIT__: page_pool[%d].node_count = %lu, page_pool[%d].order = %lu, mem_sum = %d KiB\n",
+ start, page_pool[start].node_count, start, page_pool[start].order, mem_sum);
+#endif
+ }
+ p = kthread_create(sprd_mem_pool_daemon, NULL, "%s", "sprdmempool");
+ wake_up_process(p);
+ sprd_alloc_workqueue_pid = p->pid;
+
+ printk("__SPRD__INIT__: Sprd memory pool initialized, %d KiB memory allocated.\n", mem_sum);
+
+ /*create proc file*/
+ proc_create("sprd_pages", 0, NULL, &sprd_page_info_fops);
+ return 0;
+}
+
+module_init(sprd_pages_init);
+
diff --git a/drivers/platform/sprd/sprd_vibrator_2723.c b/drivers/platform/sprd/sprd_vibrator_2723.c
new file mode 100644
index 00000000..e44db15c
--- /dev/null
+++ b/drivers/platform/sprd/sprd_vibrator_2723.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <../../../drivers/staging/android/timed_output.h>
+#include <linux/sched.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/adc.h>
+
+#ifdef CONFIG_SC_VIBRATOR_GPIO
+#include <linux/gpio.h>
+#include <soc/sprd/board.h>
+#elif defined(CONFIG_SC_VIBRATOR_POWER)
+#include <linux/regulator/consumer.h>
+#endif
+
+#include <soc/sprd/sci_glb_regs.h>
+#define ANA_VIBRATOR_CTRL0 (ANA_REGS_GLB_BASE + 0xf8)
+
+#define CUR_DRV_CAL_SEL (0x0 << 12)
+#define SLP_LDOVIBR_PD_EN (0x1 << 9)
+#define LDO_VIBR_PD (0x1 << 8)
+#define LDO_VIBR_V (0xB4)
+
+static struct work_struct vibrator_work;
+static struct hrtimer vibe_timer;
+static int vibe_state = 0;
+
+static inline uint32_t vibrator_read(unsigned long reg)
+{
+ return sci_adi_read(reg);
+}
+
+static void set_vibrator(int on)
+{
+ if (on){
+ sci_adi_clr(ANA_VIBRATOR_CTRL0, LDO_VIBR_PD);
+ sci_adi_clr(ANA_VIBRATOR_CTRL0, SLP_LDOVIBR_PD_EN);
+ printk("v_reg:0x%08x\n",ANA_VIBRATOR_CTRL0);
+ printk("v_reg_value:0x%08x\n",vibrator_read(ANA_VIBRATOR_CTRL0));
+
+ }
+ else{
+ sci_adi_write(ANA_VIBRATOR_CTRL0, LDO_VIBR_PD, LDO_VIBR_PD);
+ sci_adi_write(ANA_VIBRATOR_CTRL0, SLP_LDOVIBR_PD_EN, SLP_LDOVIBR_PD_EN);
+ }
+}
+
+static void vibrator_hw_init(void)
+{
+ sci_adi_write(ANA_REG_GLB_RTC_CLK_EN, BIT_RTC_VIBR_EN, BIT_RTC_VIBR_EN);
+ sci_adi_write(ANA_VIBRATOR_CTRL0,CUR_DRV_CAL_SEL,CUR_DRV_CAL_SEL);
+ sci_adi_clr(ANA_VIBRATOR_CTRL0, 0xFF);
+ sci_adi_write(ANA_VIBRATOR_CTRL0,LDO_VIBR_V,LDO_VIBR_V);
+}
+
+static void update_vibrator(struct work_struct *work)
+{
+ set_vibrator(vibe_state);
+}
+
+static void vibrator_enable(struct timed_output_dev *dev, int value)
+{
+
+ hrtimer_cancel(&vibe_timer);
+
+ if (value == 0)
+ vibe_state = 0;
+ else {
+ value = (value > 15000) ? 15000 : value;
+ vibe_state = 1;
+ hrtimer_start(&vibe_timer,
+ ktime_set(value / 1000, (value % 1000) * 1000000),
+ HRTIMER_MODE_REL);
+ printk("vibrator_enable:value = %d\n",value);
+ printk("vibrator_enable:vibe_state = %d\n",vibe_state);
+
+ }
+
+ schedule_work(&vibrator_work);
+}
+
+static int vibrator_get_time(struct timed_output_dev *dev)
+{
+ ktime_t re;
+
+ if (hrtimer_active(&vibe_timer)) {
+ re = hrtimer_get_remaining(&vibe_timer);
+ return ktime_to_ns(re);
+ } else
+ return 0;
+}
+
+static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
+{
+ vibe_state = 0;
+ schedule_work(&vibrator_work);
+
+ return HRTIMER_NORESTART;
+}
+
+static struct timed_output_dev sprd_vibrator = {
+ .name = "vibrator",
+ .get_time = vibrator_get_time,
+ .enable = vibrator_enable,
+};
+
+static int __init sprd_init_vibrator(void)
+{
+ vibrator_hw_init();
+
+ printk("locate in sprd_init_vibrator!\n");
+ INIT_WORK(&vibrator_work, update_vibrator);
+ vibe_state = 0;
+ hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ vibe_timer.function = vibrator_timer_func;
+
+ timed_output_dev_register(&sprd_vibrator);
+
+ return 0;
+}
+
+module_init(sprd_init_vibrator);
+MODULE_DESCRIPTION("vibrator driver for spreadtrum Processors");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/platform/sprd/standby-scx35.S b/drivers/platform/sprd/standby-scx35.S
new file mode 100644
index 00000000..1b014171
--- /dev/null
+++ b/drivers/platform/sprd/standby-scx35.S
@@ -0,0 +1,848 @@
+/*
+ * sc8830 standby mode
+ *
+ * Author: Wang Liwei
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/sci_glb_regs.h>
+
+#ifndef CONFIG_NR_CPUS
+#define CONFIG_NR_CPUS 1
+#endif
+
+#define CPU_SAVED_STATE_SIZE (4 * 11 + 4 * 11)
+#define REG_AON_APB_EB0_VIR reg_aon_apb_eb0_vir
+/* #define REG_AON_APB_EB0_PHY reg_aon_apb_eb0_phy */
+#define REG_AON_APB_EB0_PHY 0x402e0000
+#define REG_UART1_ENABLE_VIR reg_uart1_enable_vir
+#define REG_UART1_ENABLE_PHY reg_uart1_enable_phy
+#define REG_UART1_ENABLE_BIT (0x1 << 14)
+#define REG_GLB_GEN0_PHY_ADDR (SPRD_APBREG_PHYS + 0x8)
+#define REG_GLB_GEN0_VIR_ADDR (SPRD_APBREG_BASE + 0x8)
+#define UART1_EN (0X1 << 14)
+#define JTAG_DAP_EN (0X1 << 30)
+#define UART1_CKD_PHY (SPRD_UART1_PHYS + 0x24)
+#define UART1_CTR0_PHY (SPRD_UART1_PHYS + 0x18)
+#define UART1_CKD_VIR (SPRD_UART1_BASE + 0x24)
+#define UART1_CTR0_VIR (SPRD_UART1_BASE + 0x18)
+#define SPRD_LPDDR2_PHY_PHYS 0X30010000
+#define SPRD_PUB_PHYS 0X30020000
+#define LPDDR2_PHY_STATUS (SPRD_LPDDR2_PHY_PHYS+0x0184)
+#define PUB_ACCESS_SET (SPRD_PUB_PHYS+0x1064)
+#define PUB_ACCESS_CLR (SPRD_PUB_PHYS+0x2064)
+#define AON_APB_RES_REG1 (SPRD_AONAPB_PHYS+0x3048)
+#define SLEEP_RESUME_CODE_PHYS 0X400
+
+ .macro addruart,rx
+ @ see if the MMU is enabled and select appropriate base address
+ mrc p15, 0, \rx, c1, c0
+ tst \rx, #1
+ ldreq \rx, =SPRD_UART1_PHYS
+ ldrne \rx, =SPRD_UART1_BASE
+ .endm
+
+ .macro senduart,rd,rx
+ and \rd,\rd,#0xFF
+ str \rd, [\rx, #0x00]
+ .endm
+
+ .macro waituart,rd,rx
+ @ wait for TX_READY
+1: ldr \rd, [\rx, #0x0C]
+ mov \rd,\rd,lsr #8
+ and \rd,\rd,#0xFF
+ teq \rd, #0x00
+ bne 1b
+ .endm
+
+ .macro busyuart,rd,rx
+ @ wait for TX_READY
+2: ldr \rd, [\rx, #0x0C]
+ mov \rd,\rd,lsr #8
+ and \rd,\rd,#0xFF
+ teq \rd, #0x00
+ bne 2b
+ .endm
+
+ .text
+ .align 12
+ENTRY(sc8830_standby_iram)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+sprd_pm_mapped_va:
+ /* disable uart1 for idle deep sleep */
+ adr r0, REG_UART1_ENABLE_PHY
+ ldr r0, [r0]
+ ldr r1, [r0]
+ bic r1, #REG_UART1_ENABLE_BIT
+ str r1, [r0]
+/*
+ ldr r0, =sprd_pm_va_to_iram
+ ldr r1, =sc8830_standby_iram
+ ldr r2, =iram_start
+ ldr r3, [r2, #0]
+ sub r0, r0, r1
+ add r0, r0, r3
+ mov pc, r0
+sprd_pm_va_to_iram:
+*/
+ nop
+ nop
+ nop
+ dsb
+ isb
+#if 0
+4: b 4b
+#endif
+ nop
+ nop
+ nop
+ dsb
+ isb
+#if !(defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L))
+ wfi
+ mov r0, #0x400
+ /*wait 60us for clk is safely closed*/
+5: sub r0, r0, #0x1
+ cmp r0, #0x0
+ bne 5b
+#else
+#ifndef CONFIG_ARCH_SCX20
+// Pike doesn't have this register
+ ldr r0, =PUB_ACCESS_SET
+ mov r1, #0x1
+ str r1, [r0]
+ dsb
+ isb
+ nop
+ nop
+ nop
+#endif
+
+ /*enable icache*/
+ mrc p15, 0, r0, c1, c0, 0
+ orr r0, r0, #0x1000
+ mcr p15, 0, r0, c1, c0, 0
+
+#ifdef CONFIG_ARCH_SCX35L
+check_pub_fsm_wfi0:
+ ldr r0,=0x402b00c4
+check_pub_fsm_wfi:
+ ldr r2,[r0]
+ ldr r1,[r0]
+ and r2,r2,#0x0f000000
+ and r1,r1,#0x0f000000
+ cmp r2,#0x0
+ bne check_pub_fsm_wfi
+ cmp r1,#0x0
+ bne check_pub_fsm_wfi
+
+ ldr r0,=LPDDR2_PHY_STATUS
+ ldr r1,[r0]
+ tst r1,#0x1
+ beq check_pub_fsm_wfi0
+ dsb
+ isb
+#endif
+
+ /* NOTE: here is some trick code, please pay more attention.
+ * Put some code which before "wfi" executed into I-cache to avoid
+ * some hang-up problems caused by FSM of chip-sleep.
+ * When code after "wfi" is executing, it means that cpu can not be
+ * powerred off. So we delay some time(must longer than 6*RTC_cycle,
+ * wait for FSM) in I-cache to avoid some access out of cpu.
+ */
+ mov r1, #0x0
+9: cmp r1, #0x1
+ wfieq
+ movne r0, #0x1
+ moveq r0, #0x30000
+ /*wait 250us for clk is safely closed*/
+5: sub r0, r0, #0x1
+ cmp r0, #0x0
+ bne 5b
+
+ add r1, r1, #0x1
+ cmp r1, #0x1
+ beq 9b
+ dsb
+ isb
+
+#ifdef CONFIG_ARCH_SCX35L
+check_pub_fsm_wake0:
+ ldr r0,=0x402b00c4
+check_pub_fsm_wake:
+ ldr r2,[r0]
+ ldr r1,[r0]
+ and r2,r2,#0x0f000000
+ and r1,r1,#0x0f000000
+ cmp r2,#0x0
+ bne check_pub_fsm_wake
+ cmp r1,#0x0
+ bne check_pub_fsm_wake
+ ldr r0,=LPDDR2_PHY_STATUS
+ ldr r1,[r0]
+ tst r1,#0x1
+ beq check_pub_fsm_wake0
+
+ ldr r0, =PUB_ACCESS_CLR
+ mov r1, #0x1
+ str r1, [r0]
+#endif
+
+ /*disable icache*/
+ mrc p15, 0, r0, c1, c0, 0
+ bic r0, r0, #0x1000
+ mcr p15, 0, r0, c1, c0, 0
+#endif
+#if 0
+6: b 6b
+#endif
+ ldr r0, =REG_AON_APB_EB0_PHY
+ ldr r1, [r0]
+ orr r1, #JTAG_DAP_EN
+ str r1, [r0]
+ adr r0, REG_UART1_ENABLE_PHY
+ ldr r0, [r0]
+ ldr r1, [r0]
+ orr r1, #REG_UART1_ENABLE_BIT
+ str r1, [r0]
+#if 0
+ adr r0, REG_UART1_ENABLE_VIR
+ ldr r0, [r0]
+ ldr r1, [r0]
+ orr r1, r1, #REG_UART1_ENABLE_BIT
+ str r1, [r0]
+ ldr r0, =UART1_CKD_VIR
+ ldr r1, =0xe2
+ str r1, [r0]
+ ldr r0, =UART1_CTR0_VIR
+ ldr r1, =0x1c
+ str r1, [r0]
+loopq:
+ mov r0, #'B'
+ bl printch
+ b loopq
+
+#endif
+ mov r0, #0
+ mov pc, lr
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+#endif
+ENDPROC(sc8830_standby_iram)
+
+ENTRY(sc8830_standby_exit_iram)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ /*delay on 26MHZ in IRAM*/
+ mov r0, #0x100
+delay_after_reset:
+ sub r0, r0, #0x1
+ cmp r0, #0x0
+ bne delay_after_reset
+
+ bl uart1_en
+
+ /*enable DAP for debug*/
+ ldr r0, =REG_AON_APB_EB0_PHY
+ ldr r1, [r0]
+ orr r1, #JTAG_DAP_EN
+ str r1, [r0]
+
+ mrs r0, cpsr
+ and r0, r0, #0xff
+ mov r6, r0
+ ldr r1, =0x00d3
+ cmp r0, r1
+ msr cpsr_c, r1
+
+ ldr r0, =0x20
+ bl my_delay
+#ifdef CONFIG_ARCH_SCX20
+/* pub_reinit_info */
+ ldr r0, =0x50002400
+ ldr r7, [r0, #0x4] // ap_access_reg_addr
+ ldr r1, [r0, #0x18] // sw_reinit_enable
+ ldr r6, [r0, #0x14] // complete_flag
+
+/* PWR_STATUS3_DBG */
+ ldr r2, =0x402b00c8
+ ldr r3, =0x000000f0
+
+ cmp r1, #1
+ bne pub_reinit_complete // This is hw_reinit and we directly go to pub_reinit_complete
+
+/* sw_reinit */
+1:
+ ldr r4, [r2]
+ and r4, r3
+ ldr r5, [r2]
+ and r5, r3
+ cmp r4, r5
+ bne 1b
+
+ cmp r4, #0
+ bne send_ipi
+ ldr r5, [r7]
+ cmp r5, r6
+ beq next
+
+send_ipi:
+ /*send ipi irq1 to cp0*/
+ ldr r4,=0x402c0000
+ mov r5,#0x4
+ str r5,[r4]
+
+/* Polling if pub reinit completed */
+pub_reinit_complete:
+1:
+ ldr r4, [r2]
+ and r4, r3
+ ldr r5, [r2]
+ and r5, r3
+ cmp r4, r5
+ bne 1b
+
+ cmp r4, #0
+ bne 1b
+ ldr r5, [r7]
+ cmp r5, r6
+ bne 1b
+
+next:
+#else
+#ifdef CONFIG_ARCH_SCX30G
+ mov r2, #0x3
+ ldr r0,=0x402b00c8
+check_phy_fsm:
+ ldr r1,[r0]
+ and r1,r1,#0xf000
+ cmp r1,#0x0
+ bne check_phy_fsm
+check_pub_fsm:
+ ldr r1,[r0]
+ and r1,r1,#0x00f0
+ cmp r1,#0x0
+ bne check_pub_fsm
+ sub r2,r2,#0x1
+ cmp r2,#0x0
+ bne check_phy_fsm
+ ldr r0,=0x50002404
+ ldr r0,[r0]
+ ldr r1,[r0]
+ tst r1,#0x1
+ bne next
+ /*send ipi irq1 to cp0*/
+ ldr r0,=0x402c0000
+ mov r1,#0x4
+ str r1,[r0]
+
+1: ldr r0,=0x50002404
+ ldr r0,[r0]
+ ldr r1,[r0]
+ tst r1,#0x1
+ beq 1b
+next:
+#endif
+#endif
+
+#ifdef CONFIG_ARCH_SCX35L
+check_pub_fsm0:
+ ldr r0,=0x402b00c4
+check_pub_fsm:
+ ldr r2,[r0]
+ ldr r1,[r0]
+ and r2,r2,#0x0f000000
+ and r1,r1,#0x0f000000
+ cmp r2,#0x0
+ bne check_pub_fsm
+ cmp r1,#0x0
+ bne check_pub_fsm
+
+ ldr r0,=LPDDR2_PHY_STATUS
+ ldr r1,[r0]
+ tst r1,#0x1
+ beq check_pub_fsm0
+
+ ldr r0, =PUB_ACCESS_CLR
+ mov r1, #0x1
+ str r1, [r0]
+#endif
+
+/* DLL calibration */
+#if 0
+ mov r0,#0x30000004
+ ldr r1,[r0]
+ and r1,r1,#0x7
+ cmp r1,#0x3
+ bne recover_context
+
+ ldr r0,=0x300001b0
+ ldr r1,[r0]
+ bic r1,r1,#0x1
+ str r1,[r0]
+
+ ldr r0,=0x30010004
+ mov r1,#0x21
+ str r1,[r0]
+
+ ldr r0,=0x30010018
+1: ldr r1,[r0]
+ tst r1,#0x1
+ beq 1b
+
+ ldr r0,=0x300001b0
+ ldr r1,[r0]
+ orr r1,r1,#0x1
+ str r1,[r0]
+
+ mov r0,#0x100
+2: sub r0,r0,#0x1
+ cmp r0,#0x0
+ bne 2b
+recover_context:
+#endif
+/* DLL calibration end */
+
+#ifdef CONFIG_DDR_VALIDITY_TEST
+ bl ddr_test
+#endif
+#if 0
+5: b 5b
+#endif
+ #blne dead
+/*
+ adr r0, sleep_asm_counter
+ ldr r1, [r0]
+ add r1, r1, #1
+ str r1, [r0]
+ cmp r1, #32
+ bleq dead
+*/
+
+loopp:
+ adr r0, str_deep
+ bl printascii_phy
+ /*
+ b loopp
+ */
+
+ /*
+ * convert to physical address,
+ * need to find a more elegent way.
+ */
+context_restore:
+ /*
+ ldr r0, =sp_pm_collapse_exit
+ ldr r1, =0xc0000000
+ sub r0, r0, r1
+ ldr r1, =(CONFIG_PHYS_OFFSET)
+ add r0, r0, r1
+ */
+ mov r0, #0x104 /*sp_pm_collapse_exit phy address pointer*/
+ ldr r0, [r0] /*sp_pm_collapse_exit phy address*/
+
+ mov pc, r0
+#endif
+ENDPROC(sc8830_standby_exit_iram)
+
+#ifdef CONFIG_DDR_VALIDITY_TEST
+#ifndef CONFIG_ARCH_SCX30G
+ENTRY(ddr_test)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ddr_start:
+ /* we will test 64kB ddr */
+ mov r8, lr
+ ldr r4, =0x10000
+ ldr r3, =0x12345678
+ ldr r5, =0x87654321
+ mov r0, #0x100
+ ldr r1, [r0]
+ cmp r1, #0x0
+ beq finish
+ add r4, r1
+loop:
+ ldr r2, [r1]
+ teq r2, r3
+ bne bad
+ str r5, [r1]
+ ldr r2, [r1]
+ teq r2, r5
+ str r3, [r1]
+ bne bad
+ add r1, #0x4
+ cmp r1, r4
+ blt loop
+ adr r0, str_y
+ bl printascii_phy
+ b finish
+bad:
+ str r1, [r0]
+ str r2, [r0, #0x4]
+ adr r0, str_n
+ bl printascii_phy
+#b ddr_start
+ b dead
+finish:
+ mov pc, r8
+#endif
+ENDPROC(ddr_test)
+#else
+ENTRY(ddr_test)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+start:
+ /*ddr test, always read till it's right*/
+ ldr r7, =0x12345678
+ mov r8, lr
+ mov r5, #0x100
+ ldr r0, [r5]
+ cmp r0, #0x0
+ beq finish
+ add r6, r0
+ mov r2, #0x0
+loop:
+ ldr r1, [r0]
+ teq r7, r1
+ bne bad
+ mov r6, r2
+ mov r7, r0
+ adr r0, str_y
+ bl printascii_phy
+ b finish
+bad:
+ add r2, #0x1
+ ldr r1, [r0]
+ teq r7, r1
+ bne bad
+ mov r6, r2
+ mov r7, r0
+ adr r0, str_n
+ bl printascii_phy
+finish:
+ str r6, [r7, #0x4]
+ mov pc, r8
+#endif
+ENDPROC(ddr_test)
+#endif
+#endif //CONFIG_DDR_VALIDITY_TEST
+
+ENTRY(uart1_en)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ adr r0, REG_UART1_ENABLE_PHY
+ ldr r0, [r0]
+ ldr r1, [r0]
+ orr r1, r1, #REG_UART1_ENABLE_BIT
+ str r1, [r0]
+ ldr r0, =UART1_CKD_PHY
+ ldr r1, =0xe2
+ str r1, [r0]
+ ldr r0, =UART1_CTR0_PHY
+ ldr r1, =0x1c
+ str r1, [r0]
+ mov pc, lr
+#endif
+ENDPROC(uart1_en)
+
+ENTRY(printascii_phy)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ addruart r3
+ b 2f
+1: waituart r2, r3
+ senduart r1, r3
+ busyuart r2, r3
+ teq r1, #'\n'
+ moveq r1, #'\r'
+ beq 1b
+2: teq r0, #0
+ ldrneb r1, [r0], #1
+ teqne r1, #0
+ bne 1b
+ mov pc, lr
+#endif
+ENDPROC(printascii_phy)
+
+printch:
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ addruart r3
+ mov r1, r0
+ mov r0, #0
+ b 1b
+#endif
+ENTRY(my_delay)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+1: sub r0, r0, #0x1
+ cmp r0, #0x0
+ bne 1b
+ mov pc, lr
+#endif
+ENDPROC(my_delay)
+
+ENTRY(dead)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+1: adr r0, str_err
+ bl printascii_phy
+ ldr r0, =0x000fffff
+ bl my_delay
+
+ b 1b
+#endif
+ENDPROC(dead)
+
+ .globl sleep_asm_counter
+sleep_asm_counter:
+ .long 0x0
+
+str_y: .asciz "Y!\n"
+str_n: .asciz "N!\n"
+str_deep: .asciz "from deep!"
+str_err: .asciz "DDR ERR!"
+
+reg_aon_apb_eb0_vir: .long 0
+reg_aon_apb_eb0_phy: .long 0
+reg_uart1_enable_vir: .long 0
+reg_uart1_enable_phy: .long 0
+
+.globl reg_aon_apb_eb0_vir
+.globl reg_aon_apb_eb0_phy
+.globl reg_uart1_enable_vir
+.globl reg_uart1_enable_phy
+
+#if 0
+ENTRY(sc8830_standby_iram_end)
+
+ENTRY(sp_arch_idle)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ wfi
+ nop
+ nop
+ nop
+ bx lr
+#endif
+ENDPROC(sp_arch_idle)
+#endif
+
+ENTRY(sp_pm_collapse)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ mov r3, r1
+ ldr r0, =sp_saved_state /* address of sp_saved_state ptr */
+#if (CONFIG_NR_CPUS >= 2)
+ mrc p15, 0, r1, c0, c0, 5 /* MPIDR */
+ ands r1, r1, #15 /* What CPU am I */
+ mov r2, #CPU_SAVED_STATE_SIZE
+ mul r1, r1, r2
+ add r0, r0, r1
+#endif
+
+ stmia r0!, {r4-r14}
+#if 0
+ mov r5, r0
+ cmp r3, #1
+ bne real_deep
+ adr r0, str_a6
+ bl printascii_phy
+ b go_on
+real_deep:
+ adr r0, str_a7
+ bl printascii_phy
+go_on:
+ mov r0, r5
+#endif
+#if 0
+2: b 2b
+#endif
+ mrc p15, 0, r1, c1, c0, 0 /* MMU control */
+ mrc p15, 0, r2, c2, c0, 0 /* TTBR0 */
+ mrc p15, 0, r3, c3, c0, 0 /* dacr */
+ mrc p15, 0, r5, c10, c2, 0 /* PRRR */
+ mrc p15, 0, r6, c10, c2, 1 /* NMRR */
+ mrc p15, 0, r7, c1, c0, 1 /* ACTLR */
+ mrc p15, 0, r8, c2, c0, 1 /* TTBR1 */
+ mrc p15, 0, r9, c13, c0, 3 /* TPIDRURO */
+ mrc p15, 0, r10, c13, c0, 4 /* TPIDRURW */
+ mrc p15, 0, ip, c13, c0, 1 /* context ID */
+ stmia r0!, {r1-r10, ip}
+
+ ldr r0, =v7_flush_dcache_all
+ blx r0
+#if 0
+ blx outer_flush_all
+ blx outer_disable
+#endif
+ dmb
+
+ mrc p15, 0, r0, c0, c0, 5 /* MPIDR */
+ and r0, r0, #15 /* what CPU am I */
+
+ mrc p15, 0, r4, c1, c0, 0 /* read current CR */
+ bic r0, r4, #(1 << 2) /* clear dcache bit */
+ bic r0, r0, #(1 << 12) /* clear icache bit */
+ mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */
+ isb
+
+#if (defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L))
+ ldr r1, =v7_flush_icache_all
+ blx r1
+#endif
+ mrc p15, 0, r4, c1, c0, 0 /* read current CR */
+ bic r0, r4, #(1 << 0) /* disable MMU */
+ mcr p15, 0, r0, c1, c0, 0 /* disable MMU */
+
+ ldr r1, =SLEEP_RESUME_CODE_PHYS /*jump to sc8830_standby_iram*/
+ blx r1
+
+ dsb
+ isb
+ mrc p15, 0, r1, c1, c0, 0 /* read current CR */
+ orr r0, r1, #(1 << 0) /* enable MMU */
+ mcr p15, 0, r0, c1, c0, 0 /* enable MMU */
+ isb
+
+ mcr p15, 0, r4, c1, c0, 0 /* restore d/i cache */
+ isb
+
+ mrc p15, 0, r0, c0, c0, 5 /* MPIDR */
+ and r0, r0, #15 /* what CPU am I */
+
+#if 0
+ blx outer_resume
+#endif
+ dmb
+
+ ldr r0, =sp_saved_state /* address of sp_saved_state ptr */
+#if (CONFIG_NR_CPUS >= 2)
+ mrc p15, 0, r1, c0, c0, 5 /* MPIDR */
+ ands r1, r1, #15 /* What CPU am I */
+ mov r2, #CPU_SAVED_STATE_SIZE
+ mul r2, r2, r1
+ add r0, r0, r2
+#endif
+ ldmfd r0, {r4-r14} /* restore registers */
+ mov r0, #0 /* return power collapse failed */
+ bx lr
+#endif
+ENDPROC(sp_pm_collapse)
+str_a6: .asciz "idle deep\n"
+str_a7: .asciz "real deep\n"
+ENTRY(sp_pm_collapse_exit)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ mov r0, #'Z'
+ bl printch
+ ldr r1, =sp_saved_state
+ ldr r2, =sp_pm_collapse_exit
+ adr r3, sp_pm_collapse_exit
+ add r1, r1, r3
+ sub r1, r1, r2
+ add r1, r1, #CPU_SAVED_STATE_SIZE
+#if (CONFIG_NR_CPUS >= 2)
+ mrc p15, 0, r2, c0, c0, 5 /* MPIDR */
+ ands r2, r2, #15 /* What CPU am I */
+ mov r3, #CPU_SAVED_STATE_SIZE
+ mul r2, r2, r3
+ add r1, r1, r2
+#endif
+
+ ldmdb r1!, {r2-r12}
+ mcr p15, 0, r4, c3, c0, 0 /* dacr */
+ mcr p15, 0, r3, c2, c0, 0 /* TTBR0 */
+ mcr p15, 0, r6, c10, c2, 0 /* PRRR */
+ mcr p15, 0, r7, c10, c2, 1 /* NMRR */
+ mcr p15, 0, r8, c1, c0, 1 /* ACTLR */
+ mcr p15, 0, r9, c2, c0, 1 /* TTBR1 */
+ mcr p15, 0, r10, c13, c0, 3 /* TPIDRURO */
+ mcr p15, 0, r11, c13, c0, 4 /* TPIDRURW */
+ mcr p15, 0, r12, c13, c0, 1 /* context ID */
+ isb
+ ldmdb r1!, {r4-r14}
+
+#if 0
+3: b 3b
+ mov r0, #'Y'
+ bl printch
+#endif
+
+ /* Add 1:1 map in the PMD to allow smooth switch when turning on MMU */
+ and r3, r3, #~0x7F /* mask off lower 7 bits of TTB */
+ adr r0, sp_pm_mapped_pa /* get address of the mapped instr */
+ lsr r1, r0, #20 /* get the addr range of addr in MB */
+ lsl r1, r1, #2 /* multiply by 4 to get to the pg index */
+ add r3, r3, r1 /* pgd + pgd_index(addr) */
+ ldr r1, [r3] /* save current entry to r1 */
+ lsr r0, #20 /* align current addr to 1MB boundary */
+ lsl r0, #20
+ /* Create new entry for this 1MB page */
+ orr r0, r0, #0x4 /* PMD_SECT_BUFFERED */
+ orr r0, r0, #0x400 /* PMD_SECT_AP_WRITE */
+ orr r0, r0, #0x2 /* PMD_TYPE_SECT|PMD_DOMAIN(DOMAIN_KERNEL) */
+ str r0, [r3] /* put new entry into the MMU table */
+ mcr p15, 0, r3, c7, c10, 1 /* flush_pmd */
+ dsb
+ isb
+ mcr p15, 0, r2, c1, c0, 0 /* MMU control */
+ isb
+sp_pm_mapped_pa:
+ /* Switch to virtual */
+ adr r2, sp_pm_pa_to_va
+ ldr r0, =sp_pm_pa_to_va
+ mov pc, r0
+sp_pm_pa_to_va:
+ sub r0, r0, r2
+ /* Restore r1 in MMU table */
+ add r3, r3, r0
+ str r1, [r3]
+ mcr p15, 0, r3, c7, c10, 1 /* flush_pmd */
+ dsb
+ isb
+ mcr p15, 0, r3, c8, c7, 0 /* UTLBIALL */
+ mcr p15, 0, r3, c7, c5, 6 /* BPIALL */
+ dsb
+ isb
+#ifdef CONFIG_FIX_V7TAGRAM_BUG
+ stmfd sp!, {lr}
+ ldr r0, =physical_from_idle
+ mov r1, #0
+ str r1, [r0]
+ bl fix_tag_ram_bug
+ ldmfd sp!, {lr}
+#endif
+
+#if 0
+ stmfd sp!, {lr}
+ blx outer_resume
+ dmb
+ ldmfd sp!, {lr}
+#endif
+#if 0
+2: b 2b
+#endif
+ mov r0, #1
+ bx lr
+ nop
+ nop
+ nop
+ nop
+ nop
+1: b 1b
+#endif
+ENDPROC(sp_pm_collapse_exit)
+ENTRY(sc8830_standby_iram_end)
+
+ .data
+sp_saved_state:
+ .space CPU_SAVED_STATE_SIZE * CONFIG_NR_CPUS
+
+ENTRY(sp_arch_idle)
+#if !(defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+ wfi
+ nop
+ nop
+ nop
+ bx lr
+#endif
+ENDPROC(sp_arch_idle)
diff --git a/drivers/platform/sprd/sys_reset.c b/drivers/platform/sprd/sys_reset.c
new file mode 100755
index 00000000..1f6bc437
--- /dev/null
+++ b/drivers/platform/sprd/sys_reset.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <soc/sprd/sys_reset.h>
+#include <linux/string.h>
+#include <asm/bitops.h>
+
+void sprd_set_reboot_mode(const char *cmd)
+{
+ if(cmd)
+ printk("sprd_set_reboot_mode:cmd=%s\n",cmd);
+ if (cmd && !(strncmp(cmd, "recovery", 8))) {
+ sci_adi_raw_write(ANA_RST_STATUS, HWRST_STATUS_RECOVERY);
+ } else if (cmd && !strncmp(cmd, "alarm", 5)) {
+ sci_adi_raw_write(ANA_RST_STATUS, HWRST_STATUS_ALARM);
+ } else if (cmd && !strncmp(cmd, "fastsleep", 9)) {
+ sci_adi_raw_write(ANA_RST_STATUS, HWRST_STATUS_SLEEP);
+ } else if (cmd && !strncmp(cmd, "bootloader", 10)) {
+ sci_adi_raw_write(ANA_RST_STATUS, HWRST_STATUS_FASTBOOT);
+ } else if (cmd && !strncmp(cmd, "panic", 5)) {
+ sci_adi_raw_write(ANA_RST_STATUS, HWRST_STATUS_PANIC);
+ } else if (cmd && !strncmp(cmd, "special", 7)) {
+ sci_adi_raw_write(ANA_RST_STATUS, HWRST_STATUS_SPECIAL);
+ } else if (cmd && !strncmp(cmd, "cftreboot", 9)) {
+ sci_adi_raw_write(ANA_RST_STATUS, HWRST_STATUS_CFTREBOOT);
+ } else if (cmd && !strncmp(cmd, "autodloader", 11)) {
+ sci_adi_raw_write(ANA_RST_STATUS, HWRST_STATUS_AUTODLOADER);
+ } else if (cmd && !strncmp(cmd, "iqmode", 6)) {
+ sci_adi_raw_write(ANA_RST_STATUS, HWRST_STATUS_IQMODE);
+ } else if(cmd){
+ sci_adi_raw_write(ANA_RST_STATUS, HWRST_STATUS_NORMAL);
+ }else{
+ sci_adi_raw_write(ANA_RST_STATUS, HWRST_STATUS_SPECIAL);
+ }
+}
+
+void sprd_turnon_watchdog(unsigned int ms)
+{
+ uint32_t cnt;
+
+ cnt = (ms * 1000) / WDG_CLK;
+
+ /*enable interface clk*/
+ sci_adi_set(ANA_AGEN, AGEN_WDG_EN);
+ /*enable work clk*/
+ sci_adi_set(ANA_RTC_CLK_EN, AGEN_RTC_WDG_EN);
+ sci_adi_raw_write(WDG_LOCK, WDG_UNLOCK_KEY);
+ sci_adi_set(WDG_CTRL, WDG_NEW_VER_EN);
+ WDG_LOAD_TIMER_VALUE(cnt);
+ sci_adi_set(WDG_CTRL, WDG_CNT_EN_BIT | WDG_RST_EN_BIT);
+ sci_adi_raw_write(WDG_LOCK, (uint16_t) (~WDG_UNLOCK_KEY));
+}
+
+void sprd_turnoff_watchdog(void)
+{
+ sci_adi_raw_write(WDG_LOCK, WDG_UNLOCK_KEY);
+ /*wdg counter stop*/
+ sci_adi_clr(WDG_CTRL, WDG_CNT_EN_BIT);
+ /*disable the reset mode*/
+ sci_adi_clr(WDG_CTRL, WDG_RST_EN_BIT);
+ sci_adi_raw_write(WDG_LOCK, (uint16_t) (~WDG_UNLOCK_KEY));
+ /*stop the interface and work clk*/
+ sci_adi_clr(ANA_AGEN, AGEN_WDG_EN);
+ sci_adi_clr(ANA_RTC_CLK_EN, AGEN_RTC_WDG_EN);
+}
diff --git a/drivers/platform/sprd/sysdump64.c b/drivers/platform/sprd/sysdump64.c
new file mode 100644
index 00000000..fdb207e7
--- /dev/null
+++ b/drivers/platform/sprd/sysdump64.c
@@ -0,0 +1,741 @@
+/*
+ * Copyright (C) 2013 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/miscdevice.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/elf.h>
+#include <linux/elfcore.h>
+#include <linux/init.h>
+#include <linux/highuid.h>
+#include <linux/sched.h>
+#include <linux/sysctl.h>
+#include <asm/memory.h>
+#include <asm/cacheflush.h>
+
+#include <soc/sprd/sci_glb_regs.h>
+#include "sysdump64.h"
+
+#define CORE_STR "CORE"
+#ifndef ELF_CORE_EFLAGS
+#define ELF_CORE_EFLAGS 0
+#endif
+
+#define SYSDUMP_MAGIC "SPRD_SYSDUMP_119"
+/* FIXME: come from u-boot */
+#define SPRD_SYSDUMP_MAGIC (0x80000000 + 0x20000000 - (1024<<10))
+
+#define SYSDUMP_NOTE_BYTES (ALIGN(sizeof(struct elf_note), 4) + \
+ ALIGN(sizeof(CORE_STR), 4) + \
+ ALIGN(sizeof(struct elf_prstatus), 4))
+
+typedef char note_buf_t[SYSDUMP_NOTE_BYTES];
+
+note_buf_t __percpu *crash_notes;
+
+/* An ELF note in memory */
+struct memelfnote
+{
+ const char *name;
+ int type;
+ unsigned int datasz;
+ void *data;
+};
+
+struct sysdump_info {
+ char magic[16];
+ char time[32];
+ char dump_path[128];
+ int elfhdr_size;
+ int mem_num;
+ unsigned long dump_mem_paddr;
+ int crash_key;
+};
+
+struct sysdump_extra {
+ int enter_id;
+ int enter_cpu;
+ char reason[256];
+ struct pt_regs cpu_context[CONFIG_NR_CPUS];
+};
+
+struct sysdump_config {
+ int enable;
+ int crashkey_only;
+ int dump_modem;
+ int reboot;
+ char dump_path[128];
+};
+
+static struct sysdump_info *sprd_sysdump_info = NULL;
+
+/* must be global to let gdb know */
+struct sysdump_extra sprd_sysdump_extra = {
+ .enter_id = -1,
+ .enter_cpu = -1,
+ .reason = {0},
+};
+
+static struct sysdump_config sysdump_conf = {
+ .enable = 1,
+ .crashkey_only = 0,
+ .dump_modem = 1,
+ .reboot = 1,
+ .dump_path = "",
+};
+
+#ifdef CONFIG_SPRD_SYSDUMP
+/*FIXME:
+ the file nodes related to resered memory from DT has not been created,
+ so we just use hard code here temporarily.
+*/
+struct sysdump_mem sprd_dump_mem[] = {
+ {
+ .paddr = 0x80000000,
+ .soff = 0xffffffff,
+ .size = 128*1024*1024,
+ .type = SYSDUMP_RAM,
+ },
+ { /* GGE modem */
+ .paddr = 0x80000000 + 128*1024*1024,
+ .soff = 0xffffffff,
+ .size = 22*1024*1024,
+ .type = SYSDUMP_RAM,
+ },
+ { /* LTE modem */
+ .paddr = 0x80000000 + 150*1024*1024,
+ .soff = 0xffffffff,
+ .size = 80*1024*1024,
+ .type = SYSDUMP_RAM,
+ },
+ { /* LEFT mem */
+ .paddr = 0x80000000 + 230*1024*1024,
+ .soff = 0xffffffff,
+ .size = 0,
+ .type = SYSDUMP_RAM,
+ },
+};
+int sprd_dump_mem_num = ARRAY_SIZE(sprd_dump_mem);
+#endif /* CONFIG_SPRD_SYSDUMP */
+
+static size_t get_elfhdr_size(int nphdr)
+{
+ size_t elfhdr_len;
+
+ elfhdr_len = sizeof(struct elfhdr) +
+ (nphdr + 1)*sizeof(struct elf_phdr) +
+ ((sizeof(struct elf_note)) * 3 +
+ roundup(sizeof(CORE_STR), 4)) +
+ roundup(sizeof(struct elf_prstatus), 4) +
+ roundup(sizeof(struct elf_prpsinfo), 4) +
+ roundup(sizeof(struct task_struct), 4);
+ elfhdr_len = PAGE_ALIGN(elfhdr_len);
+
+ return elfhdr_len;
+}
+
+static int notesize(struct memelfnote *en)
+{
+ int sz;
+
+ sz = sizeof(struct elf_note);
+ sz += roundup((strlen(en->name) + 1), 4);
+ sz += roundup(en->datasz, 4);
+
+ return sz;
+}
+
+static char *storenote(struct memelfnote *men, char *bufp)
+{
+ struct elf_note en;
+
+#define DUMP_WRITE(addr,nr) do { memcpy(bufp,addr,nr); bufp += nr; } while(0)
+
+ en.n_namesz = strlen(men->name) + 1;
+ en.n_descsz = men->datasz;
+ en.n_type = men->type;
+
+ DUMP_WRITE(&en, sizeof(en));
+ DUMP_WRITE(men->name, en.n_namesz);
+
+ /* XXX - cast from long long to long to avoid need for libgcc.a */
+ bufp = (char*) roundup((unsigned long)bufp,4);
+ DUMP_WRITE(men->data, men->datasz);
+ bufp = (char*) roundup((unsigned long)bufp,4);
+
+#undef DUMP_WRITE
+
+ return bufp;
+}
+
+/*
+ * fill up all the fields in prstatus from the given task struct, except
+ * registers which need to be filled up separately.
+ */
+static void fill_prstatus(struct elf_prstatus *prstatus,
+ struct task_struct *p, long signr)
+{
+ prstatus->pr_info.si_signo = prstatus->pr_cursig = signr;
+ prstatus->pr_sigpend = p->pending.signal.sig[0];
+ prstatus->pr_sighold = p->blocked.sig[0];
+ rcu_read_lock();
+ prstatus->pr_ppid = task_pid_vnr(rcu_dereference(p->real_parent));
+ rcu_read_unlock();
+ prstatus->pr_pid = task_pid_vnr(p);
+ prstatus->pr_pgrp = task_pgrp_vnr(p);
+ prstatus->pr_sid = task_session_vnr(p);
+ if ( 0 /* thread_group_leader(p) */) {
+ struct task_cputime cputime;
+
+ /*
+ * This is the record for the group leader. It shows the
+ * group-wide total, not its individual thread total.
+ */
+ /* thread_group_cputime(p, &cputime); */
+ cputime_to_timeval(cputime.utime, &prstatus->pr_utime);
+ cputime_to_timeval(cputime.stime, &prstatus->pr_stime);
+ } else {
+ cputime_to_timeval(p->utime, &prstatus->pr_utime);
+ cputime_to_timeval(p->stime, &prstatus->pr_stime);
+ }
+ cputime_to_timeval(p->signal->cutime, &prstatus->pr_cutime);
+ cputime_to_timeval(p->signal->cstime, &prstatus->pr_cstime);
+}
+
+static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
+ struct mm_struct *mm)
+{
+ const struct cred *cred;
+ unsigned int i, len;
+
+ /* first copy the parameters from user space */
+ memset(psinfo, 0, sizeof(struct elf_prpsinfo));
+
+ if (mm) {
+ len = mm->arg_end - mm->arg_start;
+ if (len >= ELF_PRARGSZ)
+ len = ELF_PRARGSZ-1;
+ if (copy_from_user(&psinfo->pr_psargs,
+ (const char __user *)mm->arg_start, len))
+ return -EFAULT;
+ for(i = 0; i < len; i++)
+ if (psinfo->pr_psargs[i] == 0)
+ psinfo->pr_psargs[i] = ' ';
+ psinfo->pr_psargs[len] = 0;
+ }
+
+ rcu_read_lock();
+ psinfo->pr_ppid = task_pid_vnr(rcu_dereference(p->real_parent));
+ rcu_read_unlock();
+ psinfo->pr_pid = task_pid_vnr(p);
+ psinfo->pr_pgrp = task_pgrp_vnr(p);
+ psinfo->pr_sid = task_session_vnr(p);
+
+ i = p->state ? ffz(~p->state) + 1 : 0;
+ psinfo->pr_state = i;
+ psinfo->pr_sname = (i > 5) ? '.' : "RSDTZW"[i];
+ psinfo->pr_zomb = psinfo->pr_sname == 'Z';
+ psinfo->pr_nice = task_nice(p);
+ psinfo->pr_flag = p->flags;
+
+ rcu_read_lock();
+ cred = __task_cred(p);
+ SET_UID(psinfo->pr_uid, cred->uid);
+ SET_GID(psinfo->pr_gid, cred->gid);
+ rcu_read_unlock();
+ strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
+
+ return 0;
+}
+
+/**
+ * crash_setup_regs() - save registers for the panic kernel
+ * @newregs: registers are saved here
+ * @oldregs: registers to be saved (may be %NULL)
+ *
+ * Function copies machine registers from @oldregs to @newregs. If @oldregs is
+ * %NULL then current registers are stored there.
+ */
+static inline void crash_setup_regs(struct pt_regs *newregs,
+ struct pt_regs *oldregs)
+{
+ unsigned long tmp = 0;
+ if (oldregs) {
+ memcpy(newregs, oldregs, sizeof(*newregs));
+ } else {
+ __asm__ __volatile__ (
+ "stp x0, x1, [%[regs_base], #0x00]\n\t"
+ "stp x2, x3, [%[regs_base], #0x10]\n\t"
+ "stp x4, x5, [%[regs_base], #0x20]\n\t"
+ "stp x6, x7, [%[regs_base], #0x30]\n\t"
+ "stp x8, x9, [%[regs_base], #0x40]\n\t"
+ "stp x10, x11, [%[regs_base], #0x50]\n\t"
+ "stp x12, x13, [%[regs_base], #0x60]\n\t"
+ "stp x14, x15, [%[regs_base], #0x70]\n\t"
+ "stp x16, x17, [%[regs_base], #0x80]\n\t"
+ "stp x18, x19, [%[regs_base], #0x90]\n\t"
+ "stp x20, x21, [%[regs_base], #0x100]\n\t"
+ "stp x22, x23, [%[regs_base], #0x110]\n\t"
+ "stp x24, x25, [%[regs_base], #0x120]\n\t"
+ "stp x26, x27, [%[regs_base], #0x130]\n\t"
+ "stp x28, x29, [%[regs_base], #0x140]\n\t"
+ "mov %[tmp], sp\n\t"
+ "stp x30, %[tmp], [%[regs_base], #0x150]\n\t"
+ "adr %[pc], 1f\n\t"
+ "mrs %[cpsr], nzcv\n\t"
+ "mrs %[tmp], daif\n\t"
+ "orr %[cpsr], %[cpsr], %[tmp]\n\t"
+ "mrs %[tmp], CurrentEL\n\t"
+ "orr %[cpsr], %[cpsr], %[tmp]\n\t"
+ "mrs %[tmp], SPSel\n\t"
+ "orr %[cpsr], %[cpsr], %[tmp]\n\t"
+ "1:"
+ : [pc] "=r" (newregs->pc),
+ [cpsr] "=r" (newregs->pstate),
+ [tmp] "=r" (tmp)
+ : [regs_base] "r" (&newregs->regs)
+ : "memory"
+ );
+ }
+}
+
+void crash_note_save_cpu(struct pt_regs *regs, int cpu)
+{
+ struct elf_prstatus prstatus;
+ struct memelfnote notes;
+
+ notes.name = CORE_STR;
+ notes.type = NT_PRSTATUS;
+ notes.datasz = sizeof(struct elf_prstatus);
+ notes.data = &prstatus;
+
+ memset(&prstatus, 0, sizeof(struct elf_prstatus));
+ fill_prstatus(&prstatus, current, 0);
+ memcpy(&prstatus.pr_reg, regs, sizeof(struct pt_regs));
+ storenote(&notes, per_cpu_ptr(crash_notes, cpu));
+}
+
+static void sysdump_fill_core_hdr(struct pt_regs *regs,
+ struct sysdump_mem *sysmem, int mem_num,
+ char *bufp, int nphdr, int dataoff)
+{
+ struct elf_prstatus prstatus; /* NT_PRSTATUS */
+ struct elf_prpsinfo prpsinfo; /* NT_PRPSINFO */
+ struct elf_phdr *nhdr, *phdr;
+ struct elfhdr *elf;
+ struct memelfnote notes;
+ off_t offset = 0;
+ int i;
+
+ /* setup ELF header */
+ elf = (struct elfhdr *) bufp;
+ bufp += sizeof(struct elfhdr);
+ offset += sizeof(struct elfhdr);
+ memcpy(elf->e_ident, ELFMAG, SELFMAG);
+ elf->e_ident[EI_CLASS] = ELF_CLASS;
+ elf->e_ident[EI_DATA] = ELF_DATA;
+ elf->e_ident[EI_VERSION]= EV_CURRENT;
+ elf->e_ident[EI_OSABI] = ELF_OSABI;
+ memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+ elf->e_type = ET_CORE;
+ elf->e_machine = ELF_ARCH;
+ elf->e_version = EV_CURRENT;
+ elf->e_entry = 0;
+ elf->e_phoff = sizeof(struct elfhdr);
+ elf->e_shoff = 0;
+ elf->e_flags = ELF_CORE_EFLAGS;
+ elf->e_ehsize = sizeof(struct elfhdr);
+ elf->e_phentsize= sizeof(struct elf_phdr);
+ elf->e_phnum = nphdr;
+ elf->e_shentsize= 0;
+ elf->e_shnum = 0;
+ elf->e_shstrndx = 0;
+
+ /* setup ELF PT_NOTE program header */
+ nhdr = (struct elf_phdr *) bufp;
+ bufp += sizeof(struct elf_phdr);
+ offset += sizeof(struct elf_phdr);
+ nhdr->p_type = PT_NOTE;
+ nhdr->p_offset = 0;
+ nhdr->p_vaddr = 0;
+ nhdr->p_paddr = 0;
+ nhdr->p_filesz = 0;
+ nhdr->p_memsz = SYSDUMP_NOTE_BYTES*NR_CPUS;
+ nhdr->p_flags = 0;
+ nhdr->p_align = 0;
+
+ /* setup ELF PT_LOAD program header for every area */
+ for (i = 0; i < mem_num; i++) {
+ phdr = (struct elf_phdr *) bufp;
+ bufp += sizeof(struct elf_phdr);
+ offset += sizeof(struct elf_phdr);
+
+ phdr->p_type = PT_LOAD;
+ phdr->p_flags = PF_R|PF_W|PF_X;
+ phdr->p_offset = dataoff;
+ phdr->p_vaddr = sysmem[i].vaddr;
+ phdr->p_paddr = sysmem[i].paddr;
+ phdr->p_filesz = phdr->p_memsz = sysmem[i].size;
+ phdr->p_align = 0;//PAGE_SIZE;
+ dataoff += sysmem[i].size;
+ }
+
+ /*
+ * Set up the notes in similar form to SVR4 core dumps made
+ * with info from their /proc.
+ */
+ nhdr->p_offset = offset;
+ nhdr->p_filesz = 0;
+
+ return;
+} /* end elf_kcore_store_hdr() */
+
+static void sysdump_prepare_info(int enter_id, const char *reason,
+ struct pt_regs *regs)
+{
+ unsigned long long t;
+ unsigned long nanosec_rem;
+ int iocnt, i;
+ char *iomem;
+
+ strncpy(sprd_sysdump_extra.reason,
+ reason, sizeof(sprd_sysdump_extra.reason));
+ sprd_sysdump_extra.enter_id = enter_id;
+
+ sprd_sysdump_info = (struct sysdump_info *)phys_to_virt(SPRD_SYSDUMP_MAGIC);
+
+ printk("vaddr is %p,paddr is %p\n",sprd_sysdump_info, (void *)SPRD_SYSDUMP_MAGIC);
+ memcpy(sprd_sysdump_info->magic, SYSDUMP_MAGIC,
+ sizeof(sprd_sysdump_info->magic));
+
+ if (reason != NULL && !strcmp(reason, "Crash Key")) {
+ sprd_sysdump_info->crash_key = 1;
+ } else {
+ sprd_sysdump_info->crash_key = 0;
+ }
+
+ printk("reason: %s, sprd_sysdump_info->crash_key: %d\n", reason, sprd_sysdump_info->crash_key);
+
+ t = cpu_clock(smp_processor_id());
+ nanosec_rem = do_div(t, 1000000000);
+ sprintf(sprd_sysdump_info->time, "%lu.%06lu", (unsigned long)t,
+ nanosec_rem / 1000);
+
+ memcpy(sprd_sysdump_info->dump_path, sysdump_conf.dump_path,
+ sizeof(sprd_sysdump_info->dump_path));
+
+ sprd_sysdump_info->dump_mem_paddr = virt_to_phys(sprd_dump_mem);
+ sprd_sysdump_info->mem_num = sprd_dump_mem_num;
+ sprd_sysdump_info->elfhdr_size = get_elfhdr_size(sprd_sysdump_info->mem_num);
+
+ /* this must before sysdump_fill_core_hdr, it needs size */
+ for (i = 0; i < sprd_dump_mem_num; i++) {
+ if (SYSDUMP_RAM == sprd_dump_mem[i].type && 0 == sprd_dump_mem[i].size)
+ sprd_dump_mem[i].size = PHYS_OFFSET + (num_physpages << PAGE_SHIFT) - sprd_dump_mem[i].paddr;
+
+ if (!sysdump_conf.dump_modem && SYSDUMP_MODEM == sprd_dump_mem[i].type)
+ sprd_dump_mem[i].size = 0;
+ }
+
+ sysdump_fill_core_hdr(regs,
+ sprd_dump_mem,
+ sprd_dump_mem_num,
+ (char *)sprd_sysdump_info + sizeof(*sprd_sysdump_info),
+ sprd_sysdump_info->mem_num + 1,
+ sprd_sysdump_info->elfhdr_size);
+
+ iocnt = 0;
+ for (i = 0; i < sprd_dump_mem_num; i++) {
+ /* save iomem(regiters) to ram, cause they will change while rebooting */
+ if (0xffffffff != sprd_dump_mem[i].soff) {
+ sprd_dump_mem[i].soff = iocnt;
+ iomem = (char *)sprd_sysdump_info + sizeof(*sprd_sysdump_info) +
+ sprd_sysdump_info->elfhdr_size + iocnt;
+ memcpy(iomem,
+ (void const *)(sprd_dump_mem[i].vaddr),
+ sprd_dump_mem[i].size);
+ iocnt += sprd_dump_mem[i].size;
+ }
+ }
+
+ return;
+}
+
+
+struct sprd_debug_mmu_reg_t {
+ unsigned long sctlr_el1;
+ unsigned long ttbr0_el1;
+ unsigned long ttbr1_el1;
+ unsigned long tcr_el1;
+ unsigned long mair_el1;
+ unsigned long amair_el1;
+ unsigned long contextidr_el1;
+};
+
+/* ARM CORE regs mapping structure */
+struct sprd_debug_core_t {
+ /* COMMON */
+ unsigned long x0, x1, x2, x3, x4, x5, x6, x7, x8, x9;
+ unsigned long x10, x11, x12, x13, x14, x15, x16, x17, x18, x19;
+ unsigned long x20, x21, x22, x23, x24, x25, x26, x27, x28, x29;
+ unsigned long x30;
+
+ /* PC & CPSR */
+ unsigned long pc;
+ unsigned long pstate;
+};
+
+DEFINE_PER_CPU(struct sprd_debug_core_t, sprd_debug_core_reg);
+DEFINE_PER_CPU(struct sprd_debug_mmu_reg_t, sprd_debug_mmu_reg);
+
+
+/* core reg dump function*/
+static inline void sprd_debug_save_core_reg(struct sprd_debug_core_t *core_reg)
+{
+ unsigned long tmp;
+ __asm__ __volatile__ (
+ "stp x0, x1, [%[regs_base], #0x00]\n\t"
+ "stp x2, x3, [%[regs_base], #0x10]\n\t"
+ "stp x4, x5, [%[regs_base], #0x20]\n\t"
+ "stp x6, x7, [%[regs_base], #0x30]\n\t"
+ "stp x8, x9, [%[regs_base], #0x40]\n\t"
+ "stp x10, x11, [%[regs_base], #0x50]\n\t"
+ "stp x12, x13, [%[regs_base], #0x60]\n\t"
+ "stp x14, x15, [%[regs_base], #0x70]\n\t"
+ "stp x16, x17, [%[regs_base], #0x80]\n\t"
+ "stp x18, x19, [%[regs_base], #0x90]\n\t"
+ "stp x20, x21, [%[regs_base], #0x100]\n\t"
+ "stp x22, x23, [%[regs_base], #0x110]\n\t"
+ "stp x24, x25, [%[regs_base], #0x120]\n\t"
+ "stp x26, x27, [%[regs_base], #0x130]\n\t"
+ "stp x28, x29, [%[regs_base], #0x140]\n\t"
+ "mov %[tmp], sp\n\t"
+ "stp x30, %[tmp], [%[regs_base], #0x150]\n\t"
+ "adr %[pc], 1f\n\t"
+ "mrs %[cpsr], nzcv\n\t"
+ "mrs %[tmp], daif\n\t"
+ "orr %[cpsr], %[cpsr], %[tmp]\n\t"
+ "mrs %[tmp], CurrentEL\n\t"
+ "orr %[cpsr], %[cpsr], %[tmp]\n\t"
+ "mrs %[tmp], SPSel\n\t"
+ "orr %[cpsr], %[cpsr], %[tmp]\n\t"
+ "stp %[pc], %[cpsr], [%[regs_base], #0x160]\n\t"
+ "1:"
+ : [pc] "=r" (core_reg->pc),
+ [cpsr] "=r" (core_reg->pstate),
+ [tmp] "=r" (tmp)
+ : [regs_base] "r" (core_reg)
+ : "memory"
+ );
+
+ return;
+}
+
+static inline void sprd_debug_save_mmu_reg(struct sprd_debug_mmu_reg_t *mmu_reg)
+{
+ unsigned long tmp = 0;
+
+ asm volatile ("mrs %1, sctlr_el1\n\t"
+ "str %1, [%0]\n\t"
+ "mrs %1, ttbr0_el1\n\t"
+ "str %1, [%0, #8]\n\t"
+ "mrs %1, ttbr1_el1\n\t"
+ "str %1, [%0, #0x10]\n\t"
+ "mrs %1, tcr_el1\n\t"
+ "str %1, [%0, #0x18]\n\t"
+ "mrs %1, mair_el1\n\t"
+ "str %1, [%0, #0x20]\n\t"
+ "mrs %1, amair_el1\n\t"
+ "str %1, [%0, #0x28]\n\t"
+ "mrs %1, contextidr_el1\n\t"
+ "str %1, [%0, #0x30]\n\t"
+ :
+ : "r"(mmu_reg), "r"(tmp)
+ : "%0", "%1"
+ );
+}
+
+static inline void sprd_debug_save_context(void)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ sprd_debug_save_mmu_reg(&per_cpu(sprd_debug_mmu_reg, smp_processor_id()));
+ sprd_debug_save_core_reg(&per_cpu
+ (sprd_debug_core_reg, smp_processor_id()));
+
+ pr_emerg("(%s) context saved(CPU:%d)\n", __func__, smp_processor_id());
+ local_irq_restore(flags);
+
+ flush_cache_all();
+}
+
+
+extern void emergency_restart(void);
+void sysdump_enter(int enter_id, const char *reason, struct pt_regs *regs)
+{
+ struct pt_regs * pregs;
+ if (!sysdump_conf.enable)
+ return;
+
+ if ((sysdump_conf.crashkey_only) && !(reason != NULL && !strcmp(reason, "Crash Key")))
+ return;
+
+ /* this should before smp_send_stop() to make sysdump_ipi enable */
+ sprd_sysdump_extra.enter_cpu = smp_processor_id();
+
+ pregs = &sprd_sysdump_extra.cpu_context[sprd_sysdump_extra.enter_cpu];
+ if (regs)
+ memcpy(pregs, regs, sizeof(*regs));
+ else
+ crash_setup_regs((struct pt_regs *)pregs, NULL);
+
+ crash_note_save_cpu(pregs, sprd_sysdump_extra.enter_cpu);
+ sprd_debug_save_context();
+
+ smp_send_stop();
+ mdelay(1000);
+
+ printk("\n");
+ printk("*****************************************************\n");
+ printk("* *\n");
+ printk("* Sysdump enter, preparing debug info to dump ... *\n");
+ printk("* *\n");
+ printk("*****************************************************\n");
+ printk("\n");
+
+ flush_cache_all();
+ mdelay(1000);
+
+ sysdump_prepare_info(enter_id, reason, regs);
+
+ flush_cache_all();
+ mdelay(1000);
+
+ printk("\n");
+ printk("*****************************************************\n");
+ printk("* *\n");
+ printk("* Try to reboot system ... *\n");
+ printk("* *\n");
+ printk("*****************************************************\n");
+ printk("\n");
+
+ emergency_restart();
+
+
+ return;
+}
+
+void sysdump_ipi(struct pt_regs *regs)
+{
+ int cpu = smp_processor_id();
+
+ if (sprd_sysdump_extra.enter_cpu != -1)
+ {
+ memcpy((void *)&(sprd_sysdump_extra.cpu_context[cpu]),
+ (void *)regs, sizeof(struct pt_regs));
+ crash_note_save_cpu(regs, cpu);
+ sprd_debug_save_context();
+ }
+ return;
+}
+
+
+static ctl_table sysdump_sysctl_table[] = {
+ {
+ .procname = "sysdump_enable",
+ .data = &sysdump_conf.enable,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sysdump_crashkey_only",
+ .data = &sysdump_conf.crashkey_only,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sysdump_dump_modem",
+ .data = &sysdump_conf.dump_modem,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sysdump_reboot",
+ .data = &sysdump_conf.reboot,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "sysdump_dump_path",
+ .data = sysdump_conf.dump_path,
+ .maxlen = sizeof(sysdump_conf.dump_path),
+ .mode = 0644,
+ .proc_handler = proc_dostring,
+ },
+ {}
+};
+
+static ctl_table sysdump_sysctl_root[] = {
+ {
+ .procname = "kernel",
+ .mode = 0555,
+ .child = sysdump_sysctl_table,
+ },
+ {}
+};
+
+
+static struct ctl_table_header *sysdump_sysctl_hdr = NULL;
+
+int sysdump_sysctl_init(void)
+{
+ char *phy_sprd_sysdump_magic;
+ int i;
+
+ for (i = 0; i < sprd_dump_mem_num; i++)
+ sprd_dump_mem[i].vaddr = __va(sprd_dump_mem[i].paddr);
+
+ sysdump_sysctl_hdr = register_sysctl_table(sysdump_sysctl_root);
+ if (!sysdump_sysctl_hdr)
+ return -ENOMEM;
+
+ crash_notes = alloc_percpu(note_buf_t);
+ if (crash_notes == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void sysdump_sysctl_exit(void)
+{
+ if (sysdump_sysctl_hdr)
+ unregister_sysctl_table(sysdump_sysctl_hdr);
+}
+
+module_init(sysdump_sysctl_init);
+module_exit(sysdump_sysctl_exit);
+
+MODULE_AUTHOR("Jianjun.He <jianjun.he@spreadtrum.com>");
+MODULE_DESCRIPTION("kernel core dump for Spreadtrum");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/sprd/sysdump64.h b/drivers/platform/sprd/sysdump64.h
new file mode 100644
index 00000000..bb78d413
--- /dev/null
+++ b/drivers/platform/sprd/sysdump64.h
@@ -0,0 +1,21 @@
+#ifndef CONFIG_SYSDUMP64_H
+#define CONFIG_SYSDUMP64_H
+
+struct sysdump_mem {
+ unsigned long paddr;
+ unsigned long vaddr;
+ unsigned long soff;
+ size_t size;
+ int type;
+};
+
+enum sysdump_type {
+ SYSDUMP_RAM,
+ SYSDUMP_MODEM,
+ SYSDUMP_IOMEM,
+};
+
+extern int sprd_dump_mem_num;
+extern struct sysdump_mem sprd_dump_mem[];
+
+#endif
diff --git a/drivers/platform/sprd/timer_sc8830.c b/drivers/platform/sprd/timer_sc8830.c
new file mode 100755
index 00000000..fa8b9213
--- /dev/null
+++ b/drivers/platform/sprd/timer_sc8830.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/clockchips.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/clocksource.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <asm/sched_clock.h>
+#include <asm/localtimer.h>
+#include <asm/mach/time.h>
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <mach/irqs.h>
+#include <asm/mach/time.h>
+#include <soc/sprd/sprd_persistent_clock.h>
+
+static __iomem void *base_gptimer[4] = {
+ 0,
+};
+
+static int irq_nr[4] = {
+ 0,
+};
+static struct clock_event_device *local_evt[4] = { 0 };
+
+static int e_cpu = 0;
+#define TIMER_LOAD(ind, id) (base_gptimer[ind] + 0x20 * (id) + 0x0000)
+#define TIMER_VALUE(ind, id) (base_gptimer[ind] + 0x20 * (id) + 0x0004)
+#define TIMER_CTL(ind, id) (base_gptimer[ind] + 0x20 * (id) + 0x0008)
+#define TIMER_INT(ind, id) (base_gptimer[ind] + 0x20 * (id) + 0x000C)
+#define TIMER_CNT_RD(ind, id) (base_gptimer[ind] + 0x20 * (id) + 0x0010)
+
+#define ONETIME_MODE (0 << 6)
+#define PERIOD_MODE (1 << 6)
+
+#define TIMER_DISABLE (0 << 7)
+#define TIMER_ENABLE (1 << 7)
+
+#define TIMER_INT_EN (1 << 0)
+#define TIMER_INT_CLR (1 << 3)
+#define TIMER_INT_BUSY (1 << 4)
+#define TIMER_NEW (1 << 8)
+/**
+ * timer0 is used as clockevent,
+ * timer1 of aptimer0 is used as broadcast timer,
+ * timer1 is used as clocksource
+ */
+#define EVENT_TIMER 0
+#define BC_TIMER 1
+#define SOURCE_TIMER 1
+
+#define BC_CPU 1
+static int BC_IRQ = 0;
+
+static __iomem void *base_syscnt = (__iomem void *)NULL;
+
+#define SYSCNT_ALARM (base_syscnt + 0x0000)
+#define SYSCNT_COUNT (base_syscnt + 0x0004)
+#define SYSCNT_CTL (base_syscnt + 0x0008)
+#define SYSCNT_SHADOW_CNT (base_syscnt + 0x000C)
+
+static int sched_clock_source_freq;
+static int gptimer_clock_source_freq;
+
+/*only for 2713 charger*/
+int sprd_request_timer(int timer_id, int sub_id, unsigned long *base)
+{
+ if (1 == timer_id && 1 == sub_id) {
+ *base = (unsigned long)(base_gptimer[2] + 0x20);
+ return 0;
+ } else
+ return -1;
+}
+
+static inline void __gptimer_ctl(int cpu, int timer_id, int enable, int mode)
+{
+ __raw_writel(enable | mode, TIMER_CTL(cpu, timer_id));
+}
+
+static int __gptimer_set_next_event(unsigned long cycles,
+ struct clock_event_device *c)
+{
+ int cpu = smp_processor_id();
+
+ while (TIMER_INT_BUSY & __raw_readl(TIMER_INT(cpu, EVENT_TIMER))) ;
+ __gptimer_ctl(cpu, EVENT_TIMER, TIMER_DISABLE, ONETIME_MODE);
+ __raw_writel(cycles, TIMER_LOAD(cpu, EVENT_TIMER));
+ __gptimer_ctl(cpu, EVENT_TIMER, TIMER_ENABLE, ONETIME_MODE);
+ return 0;
+}
+
+static void __gptimer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *c)
+{
+ unsigned int saved;
+ int cpu = smp_processor_id();
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ __gptimer_ctl(cpu, EVENT_TIMER, TIMER_DISABLE, PERIOD_MODE);
+ __raw_writel(LATCH, TIMER_LOAD(cpu, EVENT_TIMER));
+ __gptimer_ctl(cpu, EVENT_TIMER, TIMER_ENABLE, PERIOD_MODE);
+ __raw_writel(TIMER_INT_EN, TIMER_INT(cpu, EVENT_TIMER));
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ __raw_writel(LATCH, TIMER_LOAD(cpu, EVENT_TIMER));
+ __gptimer_ctl(cpu, EVENT_TIMER, TIMER_ENABLE, ONETIME_MODE);
+ __raw_writel(TIMER_INT_EN, TIMER_INT(cpu, EVENT_TIMER));
+ break;
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ __raw_writel(TIMER_INT_CLR, TIMER_INT(cpu, EVENT_TIMER));
+ saved = __raw_readl(TIMER_CTL(cpu, EVENT_TIMER)) & PERIOD_MODE;
+ __gptimer_ctl(cpu, EVENT_TIMER, TIMER_DISABLE, saved);
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ saved = __raw_readl(TIMER_CTL(cpu, EVENT_TIMER)) & PERIOD_MODE;
+ __gptimer_ctl(cpu, EVENT_TIMER, TIMER_ENABLE, saved);
+ break;
+ }
+}
+
+static int __bctimer_set_next_event(unsigned long cycles,
+ struct clock_event_device *c)
+{
+ while (TIMER_INT_BUSY & __raw_readl(TIMER_INT(BC_CPU, BC_TIMER))) ;
+ __gptimer_ctl(BC_CPU, BC_TIMER, TIMER_DISABLE, ONETIME_MODE);
+ __raw_writel(cycles, TIMER_LOAD(BC_CPU, BC_TIMER));
+ __gptimer_ctl(BC_CPU, BC_TIMER, TIMER_ENABLE, ONETIME_MODE);
+ return 0;
+}
+
+static void __bctimer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *c)
+{
+ unsigned int saved;
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ __gptimer_ctl(BC_CPU, BC_TIMER, TIMER_DISABLE, PERIOD_MODE);
+ __raw_writel(LATCH, TIMER_LOAD(BC_CPU, BC_TIMER));
+ __gptimer_ctl(BC_CPU, BC_TIMER, TIMER_ENABLE, PERIOD_MODE);
+ __raw_writel(TIMER_INT_EN, TIMER_INT(BC_CPU, BC_TIMER));
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ __raw_writel(LATCH, TIMER_LOAD(BC_CPU, BC_TIMER));
+ __gptimer_ctl(BC_CPU, BC_TIMER, TIMER_ENABLE, ONETIME_MODE);
+ __raw_writel(TIMER_INT_EN, TIMER_INT(BC_CPU, BC_TIMER));
+ break;
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ __raw_writel(TIMER_INT_CLR, TIMER_INT(BC_CPU, BC_TIMER));
+ saved = __raw_readl(TIMER_CTL(BC_CPU, BC_TIMER)) & PERIOD_MODE;
+ __gptimer_ctl(BC_CPU, BC_TIMER, TIMER_DISABLE, saved);
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ saved = __raw_readl(TIMER_CTL(BC_CPU, BC_TIMER)) & PERIOD_MODE;
+ __gptimer_ctl(BC_CPU, BC_TIMER, TIMER_ENABLE, saved);
+ break;
+ }
+}
+
+static struct clock_event_device bctimer_event = {
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .shift = 32,
+ .rating = 150,
+ .set_next_event = __bctimer_set_next_event,
+ .set_mode = __bctimer_set_mode,
+};
+
+static irqreturn_t __gptimer_interrupt(int irq, void *dev_id);
+#ifdef CONFIG_LOCAL_TIMERS
+#if !defined (CONFIG_HAVE_ARM_ARCH_TIMER)
+static int __cpuinit sprd_local_timer_setup(struct clock_event_device *evt)
+{
+ int cpu = smp_processor_id();
+
+ evt->irq = irq_nr[cpu];
+ evt->name = "local_timer";
+ evt->features = CLOCK_EVT_FEAT_ONESHOT;
+ evt->rating = 200;
+ evt->set_mode = __gptimer_set_mode;
+ evt->set_next_event = __gptimer_set_next_event;
+ evt->shift = 32;
+ evt->mult = div_sc(32768, NSEC_PER_SEC, evt->shift);
+ evt->max_delta_ns = clockevent_delta2ns(0xf0000000, evt);
+ evt->min_delta_ns = clockevent_delta2ns(4, evt);
+
+ local_evt[cpu] = evt;
+ irq_set_affinity(evt->irq, cpumask_of(cpu));
+
+ clockevents_register_device(evt);
+ return 0;
+}
+
+static void sprd_local_timer_stop(struct clock_event_device *evt)
+{
+ evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
+}
+
+static struct local_timer_ops sprd_local_timer_ops __cpuinitdata = {
+ .setup = sprd_local_timer_setup,
+ .stop = sprd_local_timer_stop,
+};
+#endif
+#endif /* CONFIG_LOCAL_TIMERS */
+
+static irqreturn_t __gptimer_interrupt(int irq, void *dev_id)
+{
+ unsigned int value;
+ int cpu = smp_processor_id();
+ struct clock_event_device **evt = dev_id;
+
+ value = __raw_readl(TIMER_INT(cpu, EVENT_TIMER));
+ value |= TIMER_INT_CLR;
+ __raw_writel(value, TIMER_INT(cpu, EVENT_TIMER));
+
+ if (evt[cpu]->event_handler)
+ evt[cpu]->event_handler(evt[cpu]);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t __bctimer_interrupt(int irq, void *dev_id)
+{
+ unsigned int value;
+ struct clock_event_device *evt = dev_id;
+
+ value = __raw_readl(TIMER_INT(BC_CPU, BC_TIMER));
+ value |= TIMER_INT_CLR;
+ __raw_writel(value, TIMER_INT(BC_CPU, BC_TIMER));
+
+ if (evt->event_handler)
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction bctimer_irq = {
+ .name = "bctimer",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = __bctimer_interrupt,
+ .dev_id = &bctimer_event,
+};
+
+static void sprd_gptimer_clockevent_init(unsigned int irq, const char *name,
+ unsigned long hz)
+{
+ struct clock_event_device *evt = &bctimer_event;
+ int ret = 0;
+
+ __raw_writel(TIMER_DISABLE, TIMER_CTL(BC_CPU, BC_TIMER));
+ __raw_writel(TIMER_INT_CLR, TIMER_INT(BC_CPU, BC_TIMER));
+
+ evt->name = name;
+ evt->irq = irq;
+ evt->mult = div_sc(hz, NSEC_PER_SEC, evt->shift);
+ evt->max_delta_ns = clockevent_delta2ns(ULONG_MAX, evt);
+ evt->min_delta_ns = clockevent_delta2ns(2, evt);
+ evt->cpumask = cpu_all_mask;
+
+ ret = setup_irq(irq, &bctimer_irq);
+ if (ret)
+ BUG_ON(1);
+ clockevents_register_device(evt);
+}
+
+/* ****************************************************************** */
+void __gptimer_clocksource_resume(struct clocksource *cs)
+{
+ __gptimer_ctl(e_cpu, SOURCE_TIMER, TIMER_ENABLE | TIMER_NEW,
+ PERIOD_MODE);
+ pr_debug("%s: timer_val=0x%x\n", __FUNCTION__,
+ __raw_readl(TIMER_CNT_RD(e_cpu, SOURCE_TIMER)));
+}
+
+void __gptimer_clocksource_suspend(struct clocksource *cs)
+{
+ __gptimer_ctl(e_cpu, SOURCE_TIMER, TIMER_DISABLE, PERIOD_MODE);
+ pr_debug("%s: timer_val=0x%x\n", __FUNCTION__,
+ __raw_readl(TIMER_CNT_RD(e_cpu, SOURCE_TIMER)));
+}
+
+cycle_t __gptimer_clocksource_read(struct clocksource *cs)
+{
+ return ~readl_relaxed(TIMER_CNT_RD(e_cpu, SOURCE_TIMER));
+}
+
+struct clocksource clocksource_sprd = {
+ .name = "aon_timer_timer1",
+ .rating = 300,
+ .read = __gptimer_clocksource_read,
+ .mask = CLOCKSOURCE_MASK(32),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ .resume = __gptimer_clocksource_resume,
+ .suspend = __gptimer_clocksource_suspend,
+};
+
+static void __gptimer_clocksource_init(void)
+{
+ /* disalbe irq since it's just a read source */
+ __raw_writel(0, TIMER_INT(e_cpu, SOURCE_TIMER));
+
+ __gptimer_ctl(e_cpu, SOURCE_TIMER, TIMER_DISABLE | TIMER_NEW,
+ PERIOD_MODE);
+ __raw_writel(ULONG_MAX, TIMER_LOAD(e_cpu, SOURCE_TIMER));
+ __gptimer_ctl(e_cpu, SOURCE_TIMER, TIMER_NEW, PERIOD_MODE);
+ __gptimer_ctl(e_cpu, SOURCE_TIMER, TIMER_ENABLE | TIMER_NEW,
+ PERIOD_MODE);
+
+ if (clocksource_register_hz
+ (&clocksource_sprd, gptimer_clock_source_freq))
+ printk("%s: can't register clocksource\n",
+ clocksource_sprd.name);
+}
+
+static void __syscnt_clocksource_init(const char *name, unsigned long hz)
+{
+ /* disable irq for syscnt */
+ __raw_writel(0, SYSCNT_CTL);
+
+ clocksource_mmio_init(SYSCNT_SHADOW_CNT, name,
+ hz, 200, 32, clocksource_mmio_readw_up);
+}
+
+/* ****************************************************************** */
+static u32 notrace __update_sched_clock(void)
+{
+ return ~(readl_relaxed(TIMER_CNT_RD(0, SOURCE_TIMER)));
+}
+
+static void __init __sched_clock_init(unsigned long rate)
+{
+ setup_sched_clock(__update_sched_clock, 32, rate);
+}
+
+void __init sci_enable_timer_early(void)
+{
+ /* enable timer & syscnt in global regs */
+ int i = 0, j = 0;
+ sci_glb_set(REG_AON_APB_APB_EB0,
+ BIT_AON_TMR_EB | BIT_AP_SYST_EB | BIT_AP_TMR0_EB);
+#if defined CONFIG_LOCAL_TIMERS && !defined CONFIG_HAVE_ARM_ARCH_TIMER
+ sci_glb_set(REG_AON_APB_APB_EB1, BIT_AP_TMR2_EB | BIT_AP_TMR1_EB);
+ for (i = 0; i < 4; i++) {
+#else
+ sci_glb_clr(REG_AON_APB_APB_EB1, BIT_AP_TMR2_EB | BIT_AP_TMR1_EB);
+ for (i = 0; i < 2; i++) {
+#endif
+ for (j = 0; j < 3; j++) {
+ __gptimer_ctl(i, j, TIMER_DISABLE, 0);
+ __raw_writel(TIMER_INT_CLR, TIMER_INT(i, j));
+ }
+ }
+
+#if defined(CONFIG_ARCH_SCX30G) || defined(CONFIG_ARCH_SCX35L)
+ /*timer1 fixed 32768 clk */
+ sched_clock_source_freq = 32768;
+#else /*timer2 clk source is from apb clk */
+ val = sci_glb_read(REG_AON_CLK_AON_APB_CFG, -1) & 0x3;
+ if (val == 0x1)
+ sched_clock_source_freq = 76800000;
+ else if (val == 0x2)
+ sched_clock_source_freq = 96000000;
+ else if (val == 0x3)
+ sched_clock_source_freq = 128000000;
+ else
+ sched_clock_source_freq = 26000000; /*default setting */
+#endif
+
+ gptimer_clock_source_freq = sched_clock_source_freq;
+#if !defined (CONFIG_HAVE_ARM_ARCH_TIMER)
+ __sched_clock_init(sched_clock_source_freq);
+#endif
+}
+
+u32 get_sys_cnt(void)
+{
+ u32 val = 0;
+ val = __raw_readl(SYSCNT_SHADOW_CNT);
+ return val;
+}
+
+void set_ap_system_timer_expires(u32 expires_ms)
+{
+ u32 val = get_sys_cnt();
+ val = val + expires_ms;
+ __raw_writel(val, SYSCNT_ALARM);
+ __raw_writel(1, SYSCNT_CTL);
+}
+
+static irqreturn_t sys_cnt_isr(int irq, void *dev_id)
+{
+ __raw_writel(8, SYSCNT_CTL);
+ return IRQ_HANDLED;
+}
+
+static struct timespec persistent_ts;
+static u64 persistent_ms, last_persistent_ms;
+static void sprd_read_persistent_clock(struct timespec *ts)
+{
+ u64 delta;
+ struct timespec *tsp = &persistent_ts;
+
+ last_persistent_ms = persistent_ms;
+ persistent_ms = get_sys_cnt();
+ delta = persistent_ms - last_persistent_ms;
+
+ timespec_add_ns(tsp, delta * NSEC_PER_MSEC);
+ *ts = *tsp;
+}
+
+void __init sci_timer_init(void)
+{
+#ifdef CONFIG_LOCAL_TIMERS
+#if !defined (CONFIG_HAVE_ARM_ARCH_TIMER)
+ int i = 0, ret = 0;
+ local_timer_register(&sprd_local_timer_ops);
+ for (i = 0; i < CONFIG_NR_CPUS; i++) {
+ ret = request_irq(irq_nr[i], __gptimer_interrupt,
+ IRQF_TIMER | IRQF_NOBALANCING | IRQF_DISABLED
+ | IRQF_PERCPU, "local_timer", local_evt);
+ if (ret) {
+ printk(KERN_ERR "request local timer irq %d failed\n",
+ irq_nr[i]);
+ }
+ }
+#endif
+#endif
+ /* setup aon timer timer1 and syscnt as clocksource */
+ __gptimer_clocksource_init();
+ __syscnt_clocksource_init("syscnt", 1000);
+ /* setup timer1 of aon timer as clockevent. */
+ sprd_gptimer_clockevent_init(BC_IRQ, "bctimer", 32768);
+ register_persistent_clock(NULL, sprd_read_persistent_clock);
+
+ printk(KERN_INFO "sci_timer_init\n");
+}
+
+#define LOCAL_TIMER_CNT 4
+static void __init sprd_init_timer(struct device_node *np)
+{
+ struct resource res;
+ int i, ret;
+ int syscnt_irqnr = 0;
+
+ printk("%s \n", __func__);
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret < 0)
+ panic("Can't get syscnt registers!\n");
+
+ base_syscnt = ioremap_nocache(res.start, res.end - res.start);
+ if (!base_syscnt)
+ panic("ioremap_nocache failed!");
+
+ syscnt_irqnr = irq_of_parse_and_map(np, 5);
+ if (syscnt_irqnr < 0)
+ panic("Can't map ap system timer irq!\n");
+
+ ret = request_irq(syscnt_irqnr,
+ sys_cnt_isr, IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND,
+ "sys_cnt", NULL);
+ if (ret)
+ panic("sys cnt isr register failed\n");
+
+ for (i = 0; i < LOCAL_TIMER_CNT; i++) {
+ void *vaddr;
+ ret = of_address_to_resource(np, i + 1, &res);
+ if (ret < 0)
+ panic("Can't get timer %d registers for local timer",
+ i + 1);
+
+ vaddr = ioremap_nocache(res.start, resource_size(&res));
+ if (vaddr)
+ base_gptimer[i] = vaddr;
+ else
+ panic("ioremap_nocache failed!");
+ }
+ BC_IRQ = irq_of_parse_and_map(np, 0);
+ if (BC_IRQ < 0)
+ panic("Can't map bc irq");
+
+ for (i = 0; i < LOCAL_TIMER_CNT; i++) {
+ *(irq_nr + i) = irq_of_parse_and_map(np, i + 1);
+ if (*(irq_nr + i) < 0)
+ panic("Can't map bc irq");
+ }
+ of_node_put(np);
+ sci_enable_timer_early();
+ sci_timer_init();
+}
+
+CLOCKSOURCE_OF_DECLARE(scx35_timer, "sprd,scx35-timer", sprd_init_timer);
diff --git a/drivers/platform/sprd/timer_sharkl64.c b/drivers/platform/sprd/timer_sharkl64.c
new file mode 100755
index 00000000..e27ed793
--- /dev/null
+++ b/drivers/platform/sprd/timer_sharkl64.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/clockchips.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/clocksource.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/sprd_persistent_clock.h>
+struct sprd_ap_system_timer sprd_apsystimer;
+static __iomem void *base_bctimer = NULL;
+static int bc_irqnr = 0;
+
+#define TIMER_LOAD (base_bctimer + 0x0000)
+#define TIMER_VALUE (base_bctimer + 0x0004)
+#define TIMER_CTL (base_bctimer + 0x0008)
+#define TIMER_INT (base_bctimer + 0x000C)
+#define TIMER_CNT_RD (base_bctimer + 0x0010)
+
+#define ONETIME_MODE (0 << 6)
+#define PERIOD_MODE (1 << 6)
+#define TIMER_DISABLE (0 << 7)
+#define TIMER_ENABLE (1 << 7)
+#define TIMER_NEW (1 << 8)
+
+#define TIMER_INT_EN (1 << 0)
+#define TIMER_INT_CLR (1 << 3)
+#define TIMER_INT_BUSY (1 << 4)
+
+#define SYST_ALARM (sprd_apsystimer.base + 0x0000)
+#define SYST_VALUE (sprd_apsystimer.base + 0x0004)
+#define SYST_INT (sprd_apsystimer.base + 0x0008)
+#define SYST_VALUE_SHDW (sprd_apsystimer.base + 0x000C)
+
+u32 get_sys_cnt(void)
+{
+ u32 val = 0;
+ val = __raw_readl(SYST_VALUE_SHDW);
+ return val;
+}
+
+void set_ap_system_timer_expires(u32 expires_ms)
+{
+ u32 val = get_sys_cnt();
+ val = val + expires_ms;
+ __raw_writel(val, SYST_ALARM);
+ __raw_writel(1, SYST_INT);
+}
+
+static irqreturn_t sys_cnt_isr(int irq, void *dev_id)
+{
+ __raw_writel(8, SYST_INT);
+ return IRQ_HANDLED;
+}
+/*
+static struct timespec persistent_ts;
+static u64 persistent_ms, last_persistent_ms;
+static void sprd_read_persistent_clock(struct timespec *ts)
+{
+ u64 delta;
+ struct timespec *tsp = &persistent_ts;
+
+ last_persistent_ms = persistent_ms;
+ persistent_ms = get_sys_cnt();
+ delta = persistent_ms - last_persistent_ms;
+
+ timespec_add_ns(tsp, delta * NSEC_PER_MSEC);
+ *ts = *tsp;
+}
+*/
+static int sprd_ap_system_timer_init(void)
+{
+ struct resource res;
+ int ret = 0;
+ struct device_node *np = NULL;
+
+ np = of_find_node_by_name(NULL, "sprd_ap_system_timer");
+ if (!np) {
+ panic("Can't get the sprd_ap_system_timer node!\n");
+ }
+
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret < 0) {
+ panic("Can't get syscnt registers!\n");
+ }
+ sprd_apsystimer.base = ioremap_nocache(res.start, resource_size(&res));
+ if (!sprd_apsystimer.base) {
+ panic("ioremap_nocache failed!\n");
+ }
+
+ sprd_apsystimer.irqnr = irq_of_parse_and_map(np, 0);
+ if (sprd_apsystimer.irqnr < 0) {
+ panic("Can't map ap system timer irq!\n");
+ }
+
+ ret = request_irq(sprd_apsystimer.irqnr,
+ sys_cnt_isr,IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND,
+ "sys_cnt", NULL);
+ if(ret){
+ panic("sys cnt isr register failed\n");
+ }
+
+ of_node_put(np);
+
+ sci_glb_set(REG_AON_APB_APB_EB0, BIT_AP_SYST_EB);
+
+ /* disable irq for syscnt */
+ __raw_writel(0, SYST_INT);
+ /*register_persistent_clock(NULL, sprd_read_persistent_clock); */
+ printk("sprd ap system timer init ok!\n");
+ return 0;
+}
+
+arch_initcall(sprd_ap_system_timer_init);
+
+static inline void bctimer_ctl(int enable, int mode)
+{
+ __raw_writel(enable | mode, TIMER_CTL);
+}
+
+static int bctimer_set_next_event(unsigned long cycles,
+ struct clock_event_device *c)
+{
+ while (TIMER_INT_BUSY & __raw_readl(TIMER_INT)) ;
+ bctimer_ctl(TIMER_DISABLE, ONETIME_MODE);
+ __raw_writel(cycles, TIMER_LOAD);
+ bctimer_ctl(TIMER_ENABLE, ONETIME_MODE);
+ return 0;
+}
+
+static void bctimer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *c)
+{
+ unsigned int saved;
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ bctimer_ctl(TIMER_DISABLE, PERIOD_MODE);
+ __raw_writel(TIMER_INT_EN, TIMER_INT);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ bctimer_ctl(TIMER_DISABLE, ONETIME_MODE);
+ __raw_writel(TIMER_INT_EN, TIMER_INT);
+ break;
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ __raw_writel(TIMER_INT_CLR, TIMER_INT);
+ saved = __raw_readl(TIMER_CTL) & PERIOD_MODE;
+ bctimer_ctl(TIMER_DISABLE, saved);
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ saved = __raw_readl(TIMER_CTL) & PERIOD_MODE;
+ bctimer_ctl(TIMER_ENABLE, saved);
+ break;
+ }
+}
+
+static struct clock_event_device bctimer_event = {
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .shift = 32,
+ .rating = 150,
+ .set_next_event = bctimer_set_next_event,
+ .set_mode = bctimer_set_mode,
+};
+
+static irqreturn_t bctimer_interrupt(int irq, void *dev_id)
+{
+ unsigned int value;
+ struct clock_event_device *evt = dev_id;
+
+ value = __raw_readl(TIMER_INT);
+ value |= TIMER_INT_CLR;
+ __raw_writel(value, TIMER_INT);
+ if (evt->event_handler)
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction bctimer_irq = {
+ .name = "bctimer",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = bctimer_interrupt,
+ .dev_id = &bctimer_event,
+};
+
+static void sprd_gptimer_clockevent_init(unsigned int irq, const char *name,
+ unsigned long hz)
+{
+ struct clock_event_device *evt = &bctimer_event;
+ int ret = 0;
+
+ __raw_writel(TIMER_DISABLE, TIMER_CTL);
+ __raw_writel(TIMER_INT_CLR, TIMER_INT);
+
+ evt->name = name;
+ evt->irq = irq;
+ evt->mult = div_sc(hz, NSEC_PER_SEC, evt->shift);
+ evt->max_delta_ns = clockevent_delta2ns(ULONG_MAX, evt);
+ evt->min_delta_ns = clockevent_delta2ns(2, evt);
+ evt->cpumask = cpu_all_mask;
+
+ ret = setup_irq(irq, &bctimer_irq);
+ if (ret)
+ BUG_ON(1);
+ clockevents_register_device(evt);
+}
+
+static void sci_enable_timer_early(void)
+{
+ /* enable timer, need modify */
+ sci_glb_set(REG_AON_APB_APB_EB0,
+ BIT_AON_TMR_EB | BIT_AP_SYST_EB | BIT_AP_TMR0_EB);
+ sci_glb_set(REG_AON_APB_APB_EB1, BIT_AP_TMR2_EB | BIT_AP_TMR1_EB);
+}
+
+static int sprd_init_timer_sharkl64(void)
+{
+ struct resource res;
+ int ret = 0;
+ struct device_node *np = NULL;
+ unsigned int bc_timer_rate = 0;
+
+ np = of_find_node_by_name(NULL, "sprd_timer");
+ if (!np) {
+ printk("Can't get the sprd_sharkl64_timer node!\n");
+ return -ENODEV;
+ }
+
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret < 0) {
+ panic("Can't get syscnt registers!\n");
+ }
+ base_bctimer = ioremap_nocache(res.start, resource_size(&res));
+ if (!base_bctimer) {
+ panic("ioremap_nocache failed!");
+ }
+
+ bc_irqnr = irq_of_parse_and_map(np, 0);
+ if (bc_irqnr < 0) {
+ panic("Can't map bc irq");
+ }
+
+ of_property_read_u32(np, "clock-frequency", &bc_timer_rate);
+ if (bc_timer_rate == 0) {
+ printk("bc timer frequency not available\n");
+ of_node_put(np);
+ return -ENODEV;
+ }
+
+ of_node_put(np);
+ printk("%s,bc_irqnr = %d,clock-frequency is %d\n", __func__, bc_irqnr,
+ bc_timer_rate);
+ sci_enable_timer_early();
+ sprd_gptimer_clockevent_init(bc_irqnr, "bctimer", bc_timer_rate);
+ return 0;
+}
+
+subsys_initcall(sprd_init_timer_sharkl64);
diff --git a/drivers/platform/sprd/vibrator.c b/drivers/platform/sprd/vibrator.c
new file mode 100644
index 00000000..680d72ea
--- /dev/null
+++ b/drivers/platform/sprd/vibrator.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <../../../drivers/staging/android/timed_output.h>
+#include <linux/sched.h>
+#if (defined(CONFIG_ARCH_SCX35L64)||defined(CONFIG_ARCH_SCX35LT8))
+#include <soc/sprd/adi.h>
+#include <soc/sprd/adc.h>
+#else
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/adc.h>
+#endif
+
+#ifdef CONFIG_SC_VIBRATOR_GPIO
+#include <linux/gpio.h>
+#include <soc/sprd/board.h>
+#elif defined(CONFIG_SC_VIBRATOR_POWER)
+#include <linux/regulator/consumer.h>
+#endif
+
+#ifdef CONFIG_ARCH_SCX35
+#include <soc/sprd/sci_glb_regs.h>
+
+#define ANA_VIBRATOR_CTRL0 (ANA_REG_GLB_VIBR_CTRL0)
+#if defined(CONFIG_ADIE_SC2723)
+#define BIT_VIBRATOR_PD BIT_LDO_VIBR_PD
+#else
+#define BIT_VIBRATOR_PD BIT_VIBR_PON
+#define ANA_VIBRATOR_CTRL1 (ANA_REG_GLB_VIBR_CTRL1)
+#define ANA_VIBRATOR_CTRL2 (ANA_REG_GLB_VIBR_CTRL2)
+#define ANA_VIBR_WR_PROT (ANA_REG_GLB_VIBR_WR_PROT_VALUE)
+#endif
+#define VIBRATOR_REG_UNLOCK (0x1A2B)
+#else
+#define SPRD_ANA_BASE (SPRD_MISC_BASE + 0x600)
+#define ANA_REG_BASE (SPRD_ANA_BASE)
+#define ANA_VIBR_WR_PROT (ANA_REG_BASE + 0x90)
+#define ANA_VIBRATOR_CTRL0 (ANA_REG_BASE + 0x74)
+#define ANA_VIBRATOR_CTRL1 (ANA_REG_BASE + 0x78)
+#define VIBRATOR_REG_UNLOCK (0xA1B2)
+#endif
+#define VIBRATOR_REG_LOCK ((~VIBRATOR_REG_UNLOCK) & 0xffff)
+#define VIBRATOR_STABLE_LEVEL (4)
+#ifdef CONFIG_ARCH_SCX15
+#define VIBRATOR_INIT_LEVEL (0x0f)
+#define VIBRATOR_INIT_STATE_CNT (0x600)
+#else
+#define VIBRATOR_INIT_LEVEL (4)
+#define VIBRATOR_INIT_STATE_CNT (10)
+#endif
+
+#define VIBR_STABLE_V_SHIFT (12)
+#define VIBR_STABLE_V_MSK (0x0f << VIBR_STABLE_V_SHIFT)
+#define VIBR_INIT_V_SHIFT (8)
+#define VIBR_INIT_V_MSK (0x0f << VIBR_INIT_V_SHIFT)
+#define VIBR_V_BP_SHIFT (4)
+#define VIBR_V_BP_MSK (0x0f << VIBR_V_BP_SHIFT)
+#define VIBR_PD_RST (1 << 3)
+#define VIBR_PD_SET (1 << 2)
+#define VIBR_BP_EN (1 << 1)
+#define VIBR_RTC_EN (1 << 0)
+
+#ifdef CONFIG_SS_FUNCTION
+#define VIBR_PULLDOWN_EN (1 << 1)
+#define LDO_VIBR_PD (1 << 8)
+#endif
+
+#ifdef CONFIG_SC_VIBRATOR_POWER
+static struct regulator *vibrator_regulator = NULL;
+#endif
+
+static struct work_struct vibrator_work;
+static struct hrtimer vibe_timer;
+static int vibe_state = 0;
+
+#ifdef CONFIG_SS_FUNCTION
+static void set_vibrator(int on)
+{
+ if (on)
+ {
+ printk("###############vibrator on##################\n");
+ sci_adi_set((unsigned long)ANA_REG_GLB_BA_CTRL3, VIBR_PULLDOWN_EN);
+ sci_adi_clr((unsigned long)ANA_REG_GLB_BA_CTRL2, LDO_VIBR_PD);
+ }
+ else
+ {
+ printk("###############vibrator off##################\n");
+ sci_adi_set((unsigned long)ANA_REG_GLB_BA_CTRL2, LDO_VIBR_PD);
+ sci_adi_clr((unsigned long)ANA_REG_GLB_BA_CTRL3, VIBR_PULLDOWN_EN);
+ }
+}
+static void vibrator_hw_init(void)
+{
+ return;
+}
+#else
+static void set_vibrator(int on)
+{
+#ifdef CONFIG_SC_VIBRATOR_GPIO
+ gpio_set_value(GPIO_VIBRATOR_INT, on);
+#elif defined(CONFIG_SC_VIBRATOR_POWER)
+ if (on) {
+ regulator_enable(vibrator_regulator);
+ }
+ else {
+ regulator_disable(vibrator_regulator);
+ }
+#else
+#if !defined(CONFIG_ADIE_SC2723)
+ /* unlock vibrator registor */
+ sci_adi_write((unsigned long)ANA_VIBR_WR_PROT, VIBRATOR_REG_UNLOCK, 0xffff);
+#endif
+#ifdef CONFIG_ARCH_SCX35
+ if (on)
+ sci_adi_write((unsigned long)ANA_VIBRATOR_CTRL0, BIT_VIBRATOR_PD, BIT_VIBRATOR_PD);
+ else
+ sci_adi_write((unsigned long)ANA_VIBRATOR_CTRL0, 0, BIT_VIBRATOR_PD);
+#else
+ sci_adi_clr((unsigned long)ANA_VIBRATOR_CTRL0, VIBR_PD_SET | VIBR_PD_RST);
+ if (on)
+ sci_adi_set((unsigned long)ANA_VIBRATOR_CTRL0, VIBR_PD_RST);
+ else
+ sci_adi_set((unsigned long)ANA_VIBRATOR_CTRL0, VIBR_PD_SET);
+#endif
+#if !defined(CONFIG_ADIE_SC2723)
+ /* lock vibrator registor */
+ sci_adi_write((unsigned long)ANA_VIBR_WR_PROT, VIBRATOR_REG_LOCK, 0xffff);
+#endif
+#endif
+}
+
+static void vibrator_hw_init(void)
+{
+#ifdef CONFIG_SC_VIBRATOR_GPIO
+ gpio_request(GPIO_VIBRATOR_INT, "vibrator");
+ gpio_direction_output(GPIO_VIBRATOR_INT, 0);
+#elif defined(CONFIG_SC_VIBRATOR_POWER)
+ vibrator_regulator = regulator_get(NULL, "vddcammot");
+ regulator_set_voltage(vibrator_regulator, 3300000, 3300000);
+#else
+#if !defined(CONFIG_ADIE_SC2723)
+ sci_adi_write((unsigned long)ANA_VIBR_WR_PROT, VIBRATOR_REG_UNLOCK, 0xffff);
+#endif
+#ifdef CONFIG_ARCH_SCX35
+ sci_adi_write((unsigned long)ANA_REG_GLB_RTC_CLK_EN, BIT_RTC_VIBR_EN, BIT_RTC_VIBR_EN);
+#else
+ sci_adi_set((unsigned long)ANA_VIBRATOR_CTRL0, VIBR_RTC_EN);
+ sci_adi_clr((unsigned long)ANA_VIBRATOR_CTRL0, VIBR_BP_EN);
+#endif
+ /* set init current level */
+ sci_adi_write((unsigned long)ANA_VIBRATOR_CTRL0,
+ (VIBRATOR_INIT_LEVEL << VIBR_INIT_V_SHIFT),
+ VIBR_INIT_V_MSK);
+ sci_adi_write((unsigned long)ANA_VIBRATOR_CTRL0,
+ (VIBRATOR_STABLE_LEVEL << VIBR_STABLE_V_SHIFT),
+ VIBR_STABLE_V_MSK);
+#if !defined(CONFIG_ADIE_SC2723)
+ /* set stable current level */
+ sci_adi_write((unsigned long)ANA_VIBRATOR_CTRL1, VIBRATOR_INIT_STATE_CNT, 0xffff);
+ sci_adi_write((unsigned long)ANA_VIBR_WR_PROT, VIBRATOR_REG_LOCK, 0xffff);
+#endif
+#endif
+}
+
+#endif
+
+static void update_vibrator(struct work_struct *work)
+{
+ set_vibrator(vibe_state);
+}
+
+static void vibrator_enable(struct timed_output_dev *dev, int value)
+{
+
+ hrtimer_cancel(&vibe_timer);
+
+ if (value == 0)
+ vibe_state = 0;
+ else {
+ value = (value > 15000) ? 15000 : value;
+ vibe_state = 1;
+ hrtimer_start(&vibe_timer,
+ ktime_set(value / 1000, (value % 1000) * 1000000),
+ HRTIMER_MODE_REL);
+ }
+
+ schedule_work(&vibrator_work);
+}
+
+static int vibrator_get_time(struct timed_output_dev *dev)
+{
+ ktime_t re;
+
+ if (hrtimer_active(&vibe_timer)) {
+ re = hrtimer_get_remaining(&vibe_timer);
+ return ktime_to_ns(re);
+ } else
+ return 0;
+}
+
+static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
+{
+ vibe_state = 0;
+ schedule_work(&vibrator_work);
+
+ return HRTIMER_NORESTART;
+}
+
+static struct timed_output_dev sprd_vibrator = {
+ .name = "vibrator",
+ .get_time = vibrator_get_time,
+ .enable = vibrator_enable,
+};
+
+static int __init sprd_init_vibrator(void)
+{
+ vibrator_hw_init();
+
+ INIT_WORK(&vibrator_work, update_vibrator);
+ vibe_state = 0;
+ hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ vibe_timer.function = vibrator_timer_func;
+
+ timed_output_dev_register(&sprd_vibrator);
+
+ return 0;
+}
+#ifdef CONFIG_OF
+late_initcall(sprd_init_vibrator);
+#else
+module_init(sprd_init_vibrator);
+#endif
+MODULE_DESCRIPTION("vibrator driver for spreadtrum Processors");
+MODULE_LICENSE("GPL");