/* * arch/arm/mach-spc300/spc300.c * * (C) Copyright 2009 SPiDCOM Technologies * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_CHIP_FEATURE_SRAM #include #endif #include #include #include "spc300.h" #include "spc300-reset_cause.h" #define SYSTEMTICK (1000000/HZ) static void spc300_mask_irq(unsigned int irq); static void spc300_unmask_irq(unsigned int irq); static void set_irq_priority(uint32_t line, uint32_t priority); static irqreturn_t spc300_timer_interrupt(int irq, void *dev_id); static unsigned long spc300_gettimeoffset(void); static void __init spc300_init_time(void); #ifdef CONFIG_PM static int spc300_set_wake(unsigned int irq, unsigned int on); #endif static struct irq_chip spc300_irq_chip = { .name = "SPC300 GIC", .ack = spc300_mask_irq, .mask = spc300_mask_irq, .unmask = spc300_unmask_irq, #ifdef CONFIG_PM .set_wake = spc300_set_wake, #endif }; static struct irqaction spc300_timer_irq = { .name = "SPC300 Tick Timer", .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, .handler = spc300_timer_interrupt }; struct sys_timer spc300_timer = { .init = spc300_init_time, .offset = spc300_gettimeoffset }; static struct map_desc spc300_io_desc[] __initdata = { /* virtual physical length domain */ { IO_ADDRESS(ARM_ICTL_BASE) , __phys_to_pfn(ARM_ICTL_BASE) , SZ_1K , MT_DEVICE }, { IO_ADDRESS(ARM_TIMER1_BASE) , __phys_to_pfn(ARM_TIMER1_BASE) , SZ_1K , MT_DEVICE }, { IO_ADDRESS(ARM_UART1_BASE) , __phys_to_pfn(ARM_UART1_BASE) , SZ_1K , MT_DEVICE }, { IO_ADDRESS(ARM_UART2_BASE) , __phys_to_pfn(ARM_UART2_BASE) , SZ_1K , MT_DEVICE }, { IO_ADDRESS(MARIA_REGBANK_BASE) , __phys_to_pfn(MARIA_REGBANK_BASE) , SZ_4K , MT_DEVICE }, #ifdef CONFIG_CHIP_FEATURE_MIU_CTRL { IO_ADDRESS(AHB2MIU_BASE) , __phys_to_pfn(AHB2MIU_BASE) , SZ_1K , MT_DEVICE }, { IO_ADDRESS(MIU_BASE) , __phys_to_pfn(MIU_BASE) , SZ_8K , MT_DEVICE }, #endif { IO_ADDRESS(ARM_GPIO_BASE) , __phys_to_pfn(ARM_GPIO_BASE) , SZ_1K , MT_DEVICE }, { IO_ADDRESS(ARM_WDT_BASE) , __phys_to_pfn(ARM_WDT_BASE) , SZ_1K , MT_DEVICE }, #ifdef CONFIG_CHIP_FEATURE_SRAM { IO_ADDRESS(SRAM_BASE) , __phys_to_pfn(SRAM_BASE) , SRAM_SIZE , MT_DEVICE }, #endif }; static struct map_desc spc300_nvram_io_desc; static struct map_desc spc300_plccode_io_desc; uint32_t nvram_offset = NVRAM_OFFSET_INVALID; /** Global NVRAM address */ spidcom_nvram_t spidcom_nvram; EXPORT_SYMBOL(spidcom_nvram); /** Global DSP mode */ spc300_atag_dsp_mode_t spc300_dsp_mode; EXPORT_SYMBOL(spc300_dsp_mode); /** Physical address of the start of the memory dedicated to PLC. */ uint32_t spc300_plc_mem_start = 0; EXPORT_SYMBOL(spc300_plc_mem_start); /** Size of the memory dedicated to PLC. */ uint32_t spc300_plc_mem_size = 0; EXPORT_SYMBOL(spc300_plc_mem_size); /** Current image slot number. */ static int spc300_cur_img_slot = -1; /** Autoswitch enable. */ int spc300_autoswitch_en = 0; /** Wakeup interrupts mask. */ static uint32_t spc300_wakeup_irq_mask; /** Backup of interrupts mask at suspend. */ static uint32_t spc300_backup_irq_mask; /** Spidimg proc dir pointer. */ struct proc_dir_entry *spc300_spidimg_proc_dir; EXPORT_SYMBOL(spc300_spidimg_proc_dir); /** * Mask IRQ line. * * \param irq irq number. */ static void spc300_mask_irq(unsigned int irq) { IRQ_DISABLE (irq); } /** * Unmask IRQ line. * * \param irq irq number. */ static void spc300_unmask_irq(unsigned int irq) { IRQ_ENABLE (irq); } #ifdef CONFIG_PM /** * Enable/disable the use of an interrupt to wake system up. * \param irq irq number. * \param on enable/disable. * \return error code. */ static int spc300_set_wake(unsigned int irq, unsigned int on) { if (unlikely(irq >= NR_IRQS)) return -EINVAL; if (on) spc300_wakeup_irq_mask |= (1 << irq); else spc300_wakeup_irq_mask &= ~(1 << irq); return 0; } /** * Set interrupts mask for suspend mode, only enable interrupts which should * wake system up. */ void spc300_irq_suspend(void) { spc300_backup_irq_mask = IRQ_INTEN_VA; IRQ_INTEN_VA = spc300_wakeup_irq_mask; } /** * Restore interrupts after spc300_irq_suspend has been called. */ void spc300_irq_resume(void) { IRQ_INTEN_VA = spc300_backup_irq_mask; } #endif /* CONFIG_PM */ /** * Set spc300 GIC priority level for each interrupt line. * * \param line irq number. * \param priority priority level (0 min, 15 max). */ static void set_irq_priority(uint32_t line, uint32_t priority) { volatile uint32_t *gic_base_prio_reg = IRQ_PRIO_ADDR_VA; if ((line < NR_IRQS) && (priority <= 15)) *(gic_base_prio_reg + line) = priority; } /** * Initialize spc300 GIC. */ void spc300_init_irq(void) { unsigned int i; //Mask all IRQs and FIQs IRQ_INTEN_VA = 0; FIQ_INTEN_VA = 0; //WARNING: All the priorities are hard-coded //the other are left to default for (i=0; ihdr.size ; tags = tag_next(tags)) { if(tags->hdr.tag == ATAG_SPC300) { nvram_offset = tags->u.spc300.nvram_offset; spc300_plc_mem_size = tags->u.spc300.plc_mem_size; /* We assume that any element added to ATAG will never ever be * removed. */ if (tags->hdr.size >= tag_size(tag_spc300)) { spc300_cur_img_slot = tags->u.spc300.cur_img_slot; spc300_autoswitch_en = tags->u.spc300.autoswitch_en; spc300_dsp_mode = tags->u.spc300.dsp_mode; } tags->hdr.tag = ATAG_NONE; tags->hdr.size = 0; } if(tags->hdr.tag == ATAG_MEM) { spc300_plc_mem_start = tags->u.mem.start + tags->u.mem.size; } } //Set NVRAM mapping spc300_nvram_io_desc.virtual = IO_ADDRESS(SPI_BASE_DIR) + nvram_offset; spc300_nvram_io_desc.pfn = __phys_to_pfn(SPI_BASE_DIR + nvram_offset); spc300_nvram_io_desc.length = sizeof(spidcom_nvram_t); spc300_nvram_io_desc.type = MT_DEVICE; //Set PLC code mapping spc300_plccode_io_desc.virtual = VIRT_PLCCODE_BASE; spc300_plccode_io_desc.pfn = __phys_to_pfn(spc300_plc_mem_start); spc300_plccode_io_desc.length = spc300_plc_mem_size; spc300_plccode_io_desc.type = MT_DEVICE; } /** * Read the current image slot by /proc. * * \param file file structure. * \param buffer string pointer given by user. * \param start string pointer begin. * \param offset offset value. * \param count count parameter. * \param eof end of file. * \param data current image slot. * \return new pointer position. */ static int spc300_readproc_img_slot (char *buf, char **start, off_t offset, int count, int *eof, void *data) { int img_slot = *(int *)data; char *p; p = buf; p += sprintf(p, "%d\n", img_slot); *eof = 1; return p-buf+1; } static void spc300_spidimg_proc_init (void) { struct proc_dir_entry *entry; spc300_spidimg_proc_dir = proc_mkdir("spidimg", &proc_root); /* This entry is read only, so there is no write function and permissions * are set to 0444. */ entry = create_proc_entry("current_img_slot", 0, spc300_spidimg_proc_dir); entry->read_proc = spc300_readproc_img_slot; entry->mode = S_IRUGO; entry->data = (void *)(&spc300_cur_img_slot); } /** * Initialize spc300. */ void spc300_init(void) { spc300_reset_cause_init(); spc300_spidimg_proc_init(); }