diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/platform/sprd | |
Diffstat (limited to 'drivers/platform/sprd')
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 **)®_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 = ®S_##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 = ®S_##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", ®)) { + 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(¬es, 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"); |
