diff options
Diffstat (limited to 'drivers/platform/sprd/arch_misc.c')
| -rw-r--r-- | drivers/platform/sprd/arch_misc.c | 525 |
1 files changed, 525 insertions, 0 deletions
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 |
