diff options
Diffstat (limited to 'arch/arm/plat-mxc')
45 files changed, 14280 insertions, 3 deletions
diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig index 03a65c0dfb60..9b9e0e9d82b6 100644 --- a/arch/arm/plat-mxc/Kconfig +++ b/arch/arm/plat-mxc/Kconfig @@ -6,15 +6,117 @@ choice prompt "MXC/iMX System Type" default 0 +config ARCH_MXC91321 + bool "MXC91321-based" + select CPU_V6 + select CACHE_L2X0 + select MXC_DSP_BRINGUP + select ARCH_HAS_EVTMON + config ARCH_MX3 bool "MX3-based" + select CPU_V6 + select CACHE_L2X0 + select USB_ARCH_HAS_EHCI + select ARCH_HAS_EVTMON + help + This enables support for systems based on the Freescale i.MX31 and i.MX32 + +config ARCH_MX27 + bool "MX27-based" + select CPU_ARM926T + select MXC_EMMA + select USB_ARCH_HAS_EHCI + help + This enables support for systems based on the Freescale i.MX27 + +config ARCH_MX21 + bool "MX21-based" + select CPU_ARM926T + select MXC_EMMA help - This enables support for systems based on the Freescale i.MX3 family + This enables support for systems based on the Freescale i.MX21 endchoice +source "arch/arm/mach-mx27/Kconfig" + +source "arch/arm/mach-mx21/Kconfig" + source "arch/arm/mach-mx3/Kconfig" +source "arch/arm/mach-mxc91321/Kconfig" + + + + + + endmenu +config MXC_DSP_BRINGUP + bool + depends on ARCH_MXC + +config ARCH_HAS_EVTMON + bool + depends on ARCH_MXC + +config MXC_EMMA + bool + depends on ARCH_MXC + +config MXC_FB_IRAM + bool + depends on ARCH_MXC + +config ISP1504_MXC + bool "ISP1504 transceiver support" + depends on USB_ARCH_HAS_EHCI + help + Support for the ISP1504 USB transceiver on MXC platforms. + +choice + prompt "Select serial USB transceiver" + depends on USB_EHCI_ARC_OTGFS || USB_GADGET_ARC_OTGFS + +config ISP1301_MXC + bool "ISP1301 transceiver support" + select I2C_MXC + help + Support for the ISP1301 USB transceiver on MXC platforms. + +config MC13783_MXC + bool "MC13783 transceiver support" + select SPI_MXC + help + Support for the MC13783 USB transceiver on MXC platforms. +endchoice + +choice + prompt "Select serial USB mode" + depends on ISP1301_MXC || MC13783_MXC + +config MXC_USB_SB3 + bool "USB Single End Bidirection Mode" + help + If you say yes to this option, the serial tranceiver is on SB3 mode + +config MXC_USB_SU6 + bool "USB Single End Unidirection Mode" + help + If you say yes to this option, the serial tranceiver is on SU6 mode + +config MXC_USB_DB4 + bool "USB Differential Bidirection Mode" + help + If you say yes to this option, the serial tranceiver is on DB4 mode + +config MXC_USB_DU6 + bool "USB Differential Unidirection Mode" + help + If you say yes to this option, the serial tranceiver is on DU6 mode + +endchoice + endif diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile index 66ad9c2b6d64..1a03faec06ca 100644 --- a/arch/arm/plat-mxc/Makefile +++ b/arch/arm/plat-mxc/Makefile @@ -3,8 +3,36 @@ # # Common support -obj-y := irq.o +obj-y := gpio.o clock.o wdog.o snoop.o io.o + +ifneq ($(CONFIG_ARCH_MX21)$(CONFIG_ARCH_MX27),y) +obj-y += time.o cpu_common.o spba.o sdma/ +endif + +ifneq ($(CONFIG_ARCH_MX33),y) +obj-y += irq.o +endif + +obj-$(CONFIG_ARCH_MX21) += dma_mx2.o +obj-$(CONFIG_ARCH_MX27) += cpu_common.o dma_mx2.o usb_common.o +obj-$(CONFIG_ARCH_MX3) += dptc.o usb_common.o obj-m := obj-n := obj- := + +obj-$(CONFIG_MXC_DSP_BRINGUP) += dsp_bringup.o + +# LEDs support +obj-$(CONFIG_LEDS) += leds.o + +# USB support +obj-$(CONFIG_ISP1504_MXC) += isp1504xc.o + +obj-$(CONFIG_ISP1301_MXC) += isp1301xc.o + +obj-$(CONFIG_MC13783_MXC) += mc13783_xc.o + +ifneq ($(CONFIG_USB_EHCI_ARC_H1),) +obj-y += serialxc.o +endif diff --git a/arch/arm/plat-mxc/clock.c b/arch/arm/plat-mxc/clock.c new file mode 100644 index 000000000000..0b228091d228 --- /dev/null +++ b/arch/arm/plat-mxc/clock.c @@ -0,0 +1,517 @@ +/* + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +/* + * Based on arch/arm/plat-omap/clock.c + * + * Copyright (C) 2004 - 2005 Nokia corporation + * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> + * Modified for omap shared clock framework by Tony Lindgren <tony@atomide.com> + */ +/*! + * @file plat-mxc/clock.c + * + * @brief clk API implementation for MXC clocks. + * + * This file contains API defined in include/linux/clk.h for setting up and + * retrieving clocks. + * + * @ingroup CLOCKS + */ +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/string.h> +#include <linux/clk.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/proc_fs.h> + +#include <asm/io.h> +#include <asm/semaphore.h> + +#include <asm/arch/clock.h> + +static LIST_HEAD(clocks); +static DEFINE_MUTEX(clocks_mutex); +static DEFINE_SPINLOCK(clockfw_lock); + +/*------------------------------------------------------------------------- + * Standard clock functions defined in include/linux/clk.h + *-------------------------------------------------------------------------*/ + +/*! + * @brief Function to retrieve a clock by name. + * + * Note that we first try to use device id on the bus + * and clock name. If this fails, we try to use "<name>.<id>". If this fails, + * we try to use clock name only. + * + * The reference count to the clock's module owner ref count is incremented. + * + * @param dev Device structure to get bus device id. + * @param id Clock name string + * + * @return Returns handle to clock on success or -ENOENT on failure. + */ +struct clk *clk_get(struct device *dev, const char *id) +{ + struct clk *p, *clk = ERR_PTR(-ENOENT); + int idno; + char *str; + + if (id == NULL) + return clk; + + if (dev == NULL || dev->bus != &platform_bus_type) + idno = -1; + else + idno = to_platform_device(dev)->id; + + mutex_lock(&clocks_mutex); + + list_for_each_entry(p, &clocks, node) { + if (p->id == idno && + strcmp(id, p->name) == 0 && try_module_get(p->owner)) { + clk = p; + goto found; + } + } + + str = strrchr(id, '.'); + if (str) { + str[0] = '\0'; + str++; + idno = simple_strtol(str, NULL, 10); + list_for_each_entry(p, &clocks, node) { + if (p->id == idno && + strcmp(id, p->name) == 0 && + try_module_get(p->owner)) { + clk = p; + goto found; + } + } + } + + list_for_each_entry(p, &clocks, node) { + if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { + clk = p; + goto found; + } + } + + printk(KERN_WARNING "clk: Unable to get requested clock: %s\n", id); + + found: + mutex_unlock(&clocks_mutex); + + return clk; +} + +EXPORT_SYMBOL(clk_get); + +static void __clk_disable(struct clk *clk) +{ + if (clk->usecount > 0 && !(--clk->usecount)) { + if (clk->disable != NULL) { + clk->disable(clk); + } + if (likely((u32) clk->parent)) + __clk_disable(clk->parent); + if (unlikely((u32) clk->secondary)) + __clk_disable(clk->secondary); + } +} + +static int __clk_enable(struct clk *clk) +{ + int ret = 0; + + if (clk->usecount++ == 0) { + if (likely((u32) clk->parent)) { + ret = __clk_enable(clk->parent); + + if (unlikely(ret != 0)) { + goto err1; + } + } + + if (unlikely((u32) clk->secondary)) { + ret = __clk_enable(clk->secondary); + + if (unlikely(ret != 0)) { + goto err2; + } + } + + if (clk->enable) { + ret = clk->enable(clk); + } + + if (unlikely(ret != 0)) { + goto err3; + } + } + return 0; + + err3: + if (clk->secondary) + __clk_disable(clk->secondary); + err2: + if (clk->parent) + __clk_disable(clk->parent); + err1: + clk->usecount--; + return ret; +} + +/*! + * @brief Function to enable a clock. + * + * This function increments the reference count on the clock and enables the + * clock if not already enabled. The parent clock tree is recursively enabled. + * + * @param clk Handle to clock to enable. + * + * @return Returns 0 on success or error code on failure. + */ +int clk_enable(struct clk *clk) +{ + unsigned long flags; + int ret = 0; + + if (clk == NULL || IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clockfw_lock, flags); + + ret = __clk_enable(clk); + + spin_unlock_irqrestore(&clockfw_lock, flags); + + return ret; +} + +EXPORT_SYMBOL(clk_enable); + +/*! + * @brief Function to disable a clock. + * + * This function decrements the reference count on the clock and disables the + * clock when reference count is 0. The parent clock tree is recursively + * disabled. + * + * @param clk Handle to clock to disable. + * + */ +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + if (clk == NULL || IS_ERR(clk)) + return; + + spin_lock_irqsave(&clockfw_lock, flags); + + __clk_disable(clk); + + spin_unlock_irqrestore(&clockfw_lock, flags); +} + +EXPORT_SYMBOL(clk_disable); + +/*! + * @brief Function to get the usage count for the requested clock. + * + * This function returns the reference count for the clock. + * + * @param clk Handle to clock to disable. + * + * @return Returns the usage count for the requested clock. + */ +int clk_get_usecount(struct clk *clk) +{ + if (clk == NULL || IS_ERR(clk)) + return 0; + + return clk->usecount; +} + +EXPORT_SYMBOL(clk_get_usecount); + +/*! + * @brief Function to retrieve the clock rate for a clock. + * + * @param clk Handle to clock to retrieve. + * + * @return Returns the clock's rate in Hz. + * + */ +unsigned long clk_get_rate(struct clk *clk) +{ + if (clk == NULL || IS_ERR(clk)) + return 0; + + return clk->rate; +} + +EXPORT_SYMBOL(clk_get_rate); + +/*! + * @brief Function to decrement the clock's module reference count. + * + * @param clk Handle to clock to put. + * + */ +void clk_put(struct clk *clk) +{ + if (clk && !IS_ERR(clk)) + module_put(clk->owner); +} + +EXPORT_SYMBOL(clk_put); + +/*! + * @brief Function to round the requested clock rate to the nearest supported + * rate that is less than or equal to the requested rate. This is dependent on + * the clock's current parent. + * + * @param clk Handle to clock to retrieve. + * @param rate Desired clock rate in Hz. + * + * @return Returns the nearest supported rate in Hz. + * + */ +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (clk == NULL || IS_ERR(clk) || !clk->round_rate) + return 0; + + return clk->round_rate(clk, rate); +} + +EXPORT_SYMBOL(clk_round_rate); + +/* Propagate rate to children */ +void propagate_rate(struct clk *tclk) +{ + struct clk *clkp; + + if (tclk == NULL || IS_ERR(tclk)) + return; + + pr_debug("mxc clock: finding children of %s-%d\n", tclk->name, + tclk->id); + list_for_each_entry(clkp, &clocks, node) { + if (likely(clkp->parent != tclk)) + continue; + pr_debug("mxc clock: %s-%d: recalculating rate: old = %lu, ", + clkp->name, clkp->id, clkp->rate); + if (likely((u32) clkp->recalc)) + clkp->recalc(clkp); + else + clkp->rate = tclk->rate; + pr_debug("new = %lu\n", clkp->rate); + propagate_rate(clkp); + } +} + +/*! + * @brief Function to set the clock to the requested clock rate. The rate must + * match a supported rate exactly based on what clk_round_rate returns. + * + * @param clk Handle to clock to retrieve. + * @param rate Desired clock rate in Hz. + * + * @return Returns 0 on success, negative error code on failure. + * + */ +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long flags; + int ret = -EINVAL; + + if (clk == NULL || IS_ERR(clk) || clk->set_rate == NULL || rate == 0) + return ret; + + spin_lock_irqsave(&clockfw_lock, flags); + + ret = clk->set_rate(clk, rate); + if (unlikely((ret == 0) && (clk->flags & RATE_PROPAGATES))) + propagate_rate(clk); + + spin_unlock_irqrestore(&clockfw_lock, flags); + + return ret; +} + +EXPORT_SYMBOL(clk_set_rate); + +/*! + * @brief Function to set the clock's parent to another clock source. + * + * @param clk Handle to clock to retrieve. + * @param parent Desired parent clock input for the clock. + * + * @return Returns 0 on success, negative error code on failure. + * + */ +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + unsigned long flags; + int ret = -EINVAL; + + if (clk == NULL || IS_ERR(clk) || parent == NULL || + IS_ERR(parent) || clk->set_parent == NULL) + return ret; + + spin_lock_irqsave(&clockfw_lock, flags); + ret = clk->set_parent(clk, parent); + if (ret == 0) { + clk->parent = parent; + if (clk->recalc) { + clk->recalc(clk); + } else { + clk->rate = parent->rate; + } + if (unlikely(clk->flags & RATE_PROPAGATES)) + propagate_rate(clk); + } + spin_unlock_irqrestore(&clockfw_lock, flags); + + return ret; +} + +EXPORT_SYMBOL(clk_set_parent); + +/*! + * @brief Function to retrieve the clock's parent clock source. + * + * @param clk Handle to clock to retrieve. + * + * @return Returns parent clk on success, NULL on failure. + * + */ +struct clk *clk_get_parent(struct clk *clk) +{ + struct clk *ret = NULL; + + if (clk == NULL || IS_ERR(clk)) + return ret; + + return clk->parent; +} + +EXPORT_SYMBOL(clk_get_parent); + +/*! + * @brief Function to add a new clock to the clock tree. + * + * @param clk Handle to clock to add. + * + * @return Returns 0 on success, negative error code on failure. + * + */ +int clk_register(struct clk *clk) +{ + if (clk == NULL || IS_ERR(clk)) + return -EINVAL; + + mutex_lock(&clocks_mutex); + list_add(&clk->node, &clocks); + mutex_unlock(&clocks_mutex); + + return 0; +} + +EXPORT_SYMBOL(clk_register); + +/*! + * @brief Function to remove a clock from the clock tree. + * + * @param clk Handle to clock to add. + * + */ +void clk_unregister(struct clk *clk) +{ + if (clk == NULL || IS_ERR(clk)) + return; + + mutex_lock(&clocks_mutex); + list_del(&clk->node); + mutex_unlock(&clocks_mutex); +} + +EXPORT_SYMBOL(clk_unregister); + +void mxc_dump_clocks(void) +{ + struct clk *clkp; + list_for_each_entry(clkp, &clocks, node) { + printk("name:\t%s-%d\n", clkp->name, clkp->id); + printk("count:\t%d\n", clkp->usecount); + printk("rate:\t%lu\n", clkp->rate); + if (clkp->parent) + printk("parent:\t%s-%d\n\n", clkp->parent->name, + clkp->parent->id); + else + printk("parent:\tnone\n\n"); + } +} + +#ifdef CONFIG_PROC_FS +static int mxc_clock_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct clk *clkp; + char *p = page; + int len; + + list_for_each_entry(clkp, &clocks, node) { + p += sprintf(p, "%s-%d:\t\t%lu, %d", + clkp->name, clkp->id, clkp->rate, clkp->usecount); + if (clkp->parent) + p += sprintf(p, ", %s-%d\n", clkp->parent->name, + clkp->parent->id); + else + p += sprintf(p, "\n"); + } + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static int __init mxc_setup_proc_entry(void) +{ + struct proc_dir_entry *res; + + res = create_proc_read_entry("cpu/clocks", 0, NULL, + mxc_clock_read_proc, NULL); + if (!res) { + printk(KERN_ERR "Failed to create proc/cpu/clocks\n"); + return -ENOMEM; + } + return 0; +} + +late_initcall(mxc_setup_proc_entry); +#endif diff --git a/arch/arm/plat-mxc/cpu_common.c b/arch/arm/plat-mxc/cpu_common.c new file mode 100644 index 000000000000..f9edd6b04404 --- /dev/null +++ b/arch/arm/plat-mxc/cpu_common.c @@ -0,0 +1,85 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <asm/setup.h> +extern int mxc_early_serial_console_init(char *); + +/*! + * @file plat-mxc/cpu_common.c + * + * @brief This file contains the common CPU initialization code. + * + * @ingroup MSL_MX31 MSL_MXC91321 + */ + +static void __init system_rev_setup(char **p) +{ + system_rev = simple_strtoul(*p, NULL, 16); +} + +__early_param("system_rev=", system_rev_setup); + +int mxc_jtag_enabled; /* OFF: 0 (default), ON: 1 */ + +/* + * Here are the JTAG options from the command line. By default JTAG + * is OFF which means JTAG is not connected and WFI is enabled + * + * "on" -- JTAG is connected, so WFI is disabled + * "off" -- JTAG is disconnected, so WFI is enabled + */ + +static void __init jtag_wfi_setup(char **p) +{ + if (memcmp(*p, "on", 2) == 0) { + mxc_jtag_enabled = 1; + *p += 2; + } else if (memcmp(*p, "off", 3) == 0) { + mxc_jtag_enabled = 0; + *p += 3; + } +} + +__early_param("jtag=", jtag_wfi_setup); + +void __init mxc_cpu_common_init(void) +{ + pr_info("CPU is %s%x Revision %u.%u\n", + (mxc_cpu() < 0x100) ? "i.MX" : "MXC", + mxc_cpu(), mxc_cpu_rev_major(), mxc_cpu_rev_minor()); +} + +/** + * early_console_setup - setup debugging console + * + * Consoles started here require little enough setup that we can start using + * them very early in the boot process, either right after the machine + * vector initialization, or even before if the drivers can detect their hw. + * + * Returns non-zero if a console couldn't be setup. + * This function is developed based on + * early_console_setup function as defined in arch/ia64/kernel/setup.c + */ +int __init early_console_setup(char *cmdline) +{ + int earlycons = 0; + +#ifdef CONFIG_SERIAL_MXC_CONSOLE + if (!mxc_early_serial_console_init(cmdline)) + earlycons++; +#endif + + return (earlycons) ? 0 : -1; +} diff --git a/arch/arm/plat-mxc/dma_mx2.c b/arch/arm/plat-mxc/dma_mx2.c new file mode 100644 index 000000000000..76e64ede4ae7 --- /dev/null +++ b/arch/arm/plat-mxc/dma_mx2.c @@ -0,0 +1,1315 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* Front-end to the DMA handling. This handles the allocation/freeing + * of DMA channels, and provides a unified interface to the machines + * DMA facilities. + */ + +/*! + * @file plat-mxc/dma_mx2.c + * @brief This file contains functions for DMA API + * + * @ingroup DMA_MX27 + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/mman.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/clk.h> + +#include <linux/proc_fs.h> +#include <asm/io.h> + +#include <asm/hardware.h> +#include <asm/arch/dma.h> +#include <asm/delay.h> + +#include <asm/atomic.h> + +/* commented temperily for mx27 compilation +#define DMA_PM +*/ +#ifdef DMA_PM +#include <linux/pm.h> +#include <asm/arch/apmc.h> +struct apmc_user *dma_apmc_user; +struct pm_dev *dma_pm; +#define DMA_PMST_RESUME 0 +#define DMA_PMST_STANDBY 1 +#define DMA_PMST_SUSPEND 2 +static unsigned int dma_pm_status = DMA_PMST_RESUME; +#endif + +/*! + * This variable is used to controll the clock of DMA. + * It counts the number of actived channels + */ +static atomic_t g_dma_actived = ATOMIC_INIT(0); + +/*! + * This variable point a proc file which contains the information + * of DMA channels + */ +static struct proc_dir_entry *g_proc_dir; + +/*! + * The dma channels + */ +static mxc_dma_channel_t g_dma_channels[MAX_DMA_CHANNELS]; +static mx2_dma_priv_t g_dma_privates[MXC_DMA_CHANNELS]; +static mx2_dma_bd_t g_dma_bd_table[MXC_DMA_CHANNELS][MAX_BD_SIZE]; + +static DEFINE_SPINLOCK(dma_list_lock); + +static struct clk *dma_clk; + +/*!@brief flush buffer descriptor ring*/ +#define flush_dma_bd(private) \ + { \ + atomic_set(&(private->bd_used), 0); \ + private->bd_rd = private->bd_wr;\ + } + +/*!@brief get next buffer discriptor */ +#define next_dma_bd(private) \ + ({ \ + int bd_next = (private->bd_rd+1)%MAX_BD_SIZE; \ + (bd_next == private->bd_wr) ? NULL: private->bd_ring+bd_next;\ + }) + +static inline int consume_dma_bd(mxc_dma_channel_t * dma, int error); +/*! + *@brief allocate a dma channel. + * + *@param idx Requested channel NO. + * @li MXC_INVLAID_CHANNEL System allocates a free channel which is not statically allocated. + * @li Others User requests a specific channel + *@return @li MXC_INVLAID_CHANNEL Failure + * @li Others Success + */ +static inline int get_dma_channel(int idx) +{ + int i; + mxc_dma_channel_t *p; + + if ((idx >= MAX_DMA_CHANNELS) && (idx != MXC_DMA_DYNAMIC_CHANNEL)) { + return -1; + } + if (idx != MXC_DMA_DYNAMIC_CHANNEL) { + p = g_dma_channels + idx; + BUG_ON(p->dynamic != 0); + if (xchg(&p->lock, 1) != 0) { + return -1; + } + return idx; + } + + p = g_dma_channels; + for (i = 0; (i < MAX_DMA_CHANNELS); i++, p++) { + if (p->dynamic && (xchg(&p->lock, 1) == 0)) { + return i; + } + } + return -1; +} + +/*! + *@brief release a dma channel. + * + *@param idx channel number + *@return none; + */ +static inline void put_dma_channel(int idx) +{ + mxc_dma_channel_t *p; + + if ((idx < MAX_DMA_CHANNELS) && (idx >= 0)) { + p = g_dma_channels + idx; + (void)xchg(&p->lock, 0); + } +} + +/*! + *@brief Get dma list for /proc/dma + */ +static int mxc_get_dma_list(char *buf) +{ + mxc_dma_channel_t *dma; + char *p = buf; + int i; + + for (i = 0, dma = g_dma_channels; i < MAX_DMA_CHANNELS; i++, dma++) { + if (dma->lock) { + p += sprintf(p, "dma channel %2d: %s\n", i, + dma->dev_name ? dma->dev_name : "unknown"); + } else { + p += sprintf(p, "dma channel %2d: unused\n", i); + } + } + + return p - buf; +} + +/*!@brief save the mask of dma interrupts*/ +#define save_dma_interrupt(flags) \ + flags = __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DIMR) + +/*!@brief restore the mask of dma interrupts*/ +#define restore_dma_interrupt(flags) \ + __raw_writel(flags, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DIMR) + +/*!@brief disable interrupt of dma channel*/ +static inline void mask_dma_interrupt(int channel) +{ + unsigned long reg; + save_dma_interrupt(reg); + reg |= 1 << channel; /*mask interrupt; */ + restore_dma_interrupt(reg); +} + +/*!@brief enable interrupt of dma channel */ +static inline void unmask_dma_interrupt(int channel) +{ + unsigned long reg; + save_dma_interrupt(reg); + reg &= ~(1 << channel); /*unmask interrupt; */ + restore_dma_interrupt(reg); +} + +/*!@brief get interrupt event of dma channel */ +static unsigned long inline __get_dma_interrupt(int channel) +{ + unsigned long mode; + mode = 0; + if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DISR) & (1 << channel)) + mode |= DMA_DONE; + + if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBTOSR) & + (1 << channel)) + mode |= DMA_BURST_TIMEOUT; + if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DSESR) & (1 << channel)) + mode |= DMA_TRANSFER_ERROR; + + if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBOSR) & (1 << channel)) + mode |= DMA_BUFFER_OVERFLOW; + if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DRTOSR) & + (1 << channel)) + mode |= DMA_REQUEST_TIMEOUT; + return mode; +} + +/*! + *@brief clean all event of dma interrupt and return the valid event. + */ +static unsigned long inline __clear_dma_interrupt(int channel) +{ + unsigned long mode; + mode = __get_dma_interrupt(channel); + __raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DISR); + __raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBTOSR); + __raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DRTOSR); + __raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DSESR); + __raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBOSR); + + return mode; +} + +/*!@brief This function enables dma clocks without lock */ +static void inline __enable_dma_clk(void) +{ + unsigned long reg; + clk_enable(dma_clk); + reg = __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR); + reg |= 0x1; + __raw_writel(reg, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR); +} + +/*!@brief This function disables dma clocks without lock */ +static void inline __disable_dma_clk(void) +{ + unsigned long reg; + reg = __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR); + reg &= ~0x1; + __raw_writel(reg, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR); + clk_disable(dma_clk); +} + +/*!@brief This function enables dma clocks with lock */ +static void inline enable_dma_clk(void) +{ + unsigned long flags; + spin_lock_irqsave(&dma_list_lock, flags); + if (atomic_read(&g_dma_actived) == 0) { + __enable_dma_clk(); + } + spin_unlock_irqrestore(&dma_list_lock, flags); + return; +} + +/*!@brief This function disables dma clocks without locked */ +static void inline disable_dma_clk(void) +{ + unsigned long flags; + spin_lock_irqsave(&dma_list_lock, flags); + if (atomic_read(&g_dma_actived) == 0) { + __disable_dma_clk(); + } + spin_unlock_irqrestore(&dma_list_lock, flags); + return; +} + +/*!@brief select a buffer to transfer and + * setup dma channel for current transfer + */ +static void setup_dmac(mxc_dma_channel_t * dma) +{ + mx2_dma_priv_t *priv = (mx2_dma_priv_t *) dma->private; + dma_regs_t *dma_base = (dma_regs_t *) (priv->dma_base); + mx2_dma_bd_t *p, *q; + unsigned long ctrl_val; + + if (dma->active == 0) { + printk(KERN_ERR + "dma channel %d is not enabled, when receiving this channel 's interrupt\n", + dma->channel); + return; + } + if (atomic_read(&(priv->bd_used)) <= 0) { + printk(KERN_ERR "dma channel %d is empty\n", dma->channel); + dma->active = 0; + atomic_dec(&g_dma_actived); + return; + } + /* BUSY: transfering + * PEND: Wait for set to DMAC. + * s1: no transfering: + * set first(one BUSY). if there are more than one tranfer. set second &repeat is enabled(two BUSY). + * + * s2: transfering & just on transfer + * one BUSY. set the tranesfer and set repeat bit(two BUSY) + * s3: transfering & repeat has set + * has two BUSY. + */ + p = priv->bd_ring + priv->bd_rd; + q = next_dma_bd(priv); + if (!(p->state & DMA_BD_ST_BUSY)) { + /*NOTICE:: This is first buffer or dma chain does not support chain-buffer. So CEN must clear & set again */ + ctrl_val = + __raw_readl(&(dma_base->Ctl)) & + (~(DMA_CTL_ACRPT | DMA_CTL_RPT | DMA_CTL_CEN)); + __raw_writel(ctrl_val, &(dma_base->Ctl)); + if (p->mode != dma->mode) { + dma->mode = p->mode; /* bi-dir channel do mode change */ + if (dma->mode == MXC_DMA_MODE_READ) { + DMA_CTL_SET_SMOD(ctrl_val, + priv->dma_info->sourceType); + DMA_CTL_SET_SSIZ(ctrl_val, + priv->dma_info->sourcePort); + DMA_CTL_SET_DMOD(ctrl_val, + priv->dma_info->destType); + DMA_CTL_SET_DSIZ(ctrl_val, + priv->dma_info->destPort); + } else { + DMA_CTL_SET_SMOD(ctrl_val, + priv->dma_info->destType); + DMA_CTL_SET_SSIZ(ctrl_val, + priv->dma_info->destPort); + DMA_CTL_SET_DMOD(ctrl_val, + priv->dma_info->sourceType); + DMA_CTL_SET_DSIZ(ctrl_val, + priv->dma_info->sourcePort); + } + } + __raw_writel(p->src_addr, &(dma_base->SourceAddr)); + __raw_writel(p->dst_addr, &(dma_base->DestAddr)); + __raw_writel(p->count, &(dma_base->Count)); + p->state |= DMA_BD_ST_BUSY; + p->state &= ~(DMA_BD_ST_PEND); + ctrl_val |= DMA_CTL_CEN; + __raw_writel(ctrl_val, &(dma_base->Ctl)); + if (q && priv->dma_chaining) { /*DO chain-buffer */ + __raw_writel(q->src_addr, &(dma_base->SourceAddr)); + __raw_writel(q->dst_addr, &(dma_base->DestAddr)); + __raw_writel(q->count, &(dma_base->Count)); + q->state |= DMA_BD_ST_BUSY; + q->state &= ~(DMA_BD_ST_PEND); + ctrl_val |= DMA_CTL_ACRPT | DMA_CTL_RPT | DMA_CTL_CEN; + __raw_writel(ctrl_val, &(dma_base->Ctl)); + } + } else { /* Just dma channel which supports dma buffer can run to there */ + BUG_ON(!priv->dma_chaining); + if (q) { /* p is tranfering, then q must be set into dma controller */ + /*WARNING:: [1] dangerous area begin. + * If the p is completed during MCU run in this erea, the dma channel is crashed. + */ + __raw_writel(q->src_addr, &(dma_base->SourceAddr)); + __raw_writel(q->dst_addr, &(dma_base->DestAddr)); + __raw_writel(q->count, &(dma_base->Count)); + /*WARNING:: [2] dangerous area end */ + ctrl_val = + __raw_readl(&(dma_base->Ctl)) | (DMA_CTL_ACRPT | + DMA_CTL_RPT | + DMA_CTL_CEN); + __raw_writel(ctrl_val, &(dma_base->Ctl)); + + /* WARNING:: This is workaround and it is dangerous: + * the judgement is not safety. + */ + if (!__get_dma_interrupt(dma->channel)) { + q->state |= DMA_BD_ST_BUSY; + q->state &= ~(DMA_BD_ST_PEND); + } else { + /*Waiting re-enable is in ISR */ + printk(KERN_ERR + "Warning:: The privous transfer is completed. Maybe the chain buffer is stopped."); + } + } else { /* Last buffer is transfering: just clear RPT bit */ + ctrl_val = + __raw_readl(&(dma_base->Ctl)) & + (~(DMA_CTL_ACRPT | DMA_CTL_RPT)); + __raw_writel(ctrl_val, &(dma_base->Ctl)); + } + } +} + +/*! + * @brief interrupt handler of dma channel + */ +static irqreturn_t dma_irq_handler(int irq, void *dev_id) +{ + mxc_dma_channel_t *dma = (mxc_dma_channel_t *) dev_id; + mx2_dma_priv_t *priv = (mx2_dma_priv_t *) (dma ? dma->private : NULL); + dma_regs_t *dma_base; + int state, error = MXC_DMA_DONE; + + BUG_ON(priv == NULL); + + dma_base = (dma_regs_t *) priv->dma_base; + + state = __clear_dma_interrupt(dma->channel); + + priv->trans_bytes += dma_base->transferd; + if (state != DMA_DONE) { + if (state & DMA_REQUEST_TIMEOUT) { + error = MXC_DMA_REQUEST_TIMEOUT; + } else { + error = MXC_DMA_TRANSFER_ERROR; + } + } + if (consume_dma_bd(dma, error)) { + disable_dma_clk(); + if (dma->cb_fn) { + dma->cb_fn(dma->cb_args, error, priv->trans_bytes); + } + priv->trans_bytes = 0; + } else { + disable_dma_clk(); + } + return IRQ_HANDLED; +} + +/*! + *@brief Set DMA channel parameters + * + *@param dma Requested channel NO. + *@param dma_info Channel configuration + *@return @li 0 Success + * @li others Failure + */ +static int setup_dma_channel(mxc_dma_channel_t * dma, mx2_dma_info_t * dma_info) +{ + mx2_dma_priv_t *priv = (mx2_dma_priv_t *) (dma ? dma->private : NULL); + dma_regs_t *dma_base; + unsigned long reg; + + if (!dma_info || !priv) { + return -1; + } + + if (dma_info->sourceType > 3) { + return -1; + } + if (dma_info->destType > 3) { + return -1; + } + if (dma_info->destPort > 3) { + return -1; + } + if (dma_info->sourcePort > 3) { + return -1; + } + if (dma_info->M2D_Valid) { + /*add for second dma */ + if (dma_info->W < dma_info->X) { + return -1; + } + } + + priv->dma_chaining = dma_info->dma_chaining; + priv->ren = dma_info->ren; + + if (dma_info->sourceType != DMA_TYPE_FIFO + && dma_info->destType != DMA_TYPE_FIFO) { + if (dma_info->ren) { + printk(KERN_INFO + "Warning:request enable just affect source or destination port is FIFO !\n"); + priv->ren = 0; + } + } + + if (dma_info->M2D_Valid) { + if (dma_info->msel) { + __raw_writel(dma_info->W, + IO_ADDRESS(DMA_BASE_ADDR) + DMA_WSRB); + __raw_writel(dma_info->X, + IO_ADDRESS(DMA_BASE_ADDR) + DMA_XSRB); + __raw_writel(dma_info->Y, + IO_ADDRESS(DMA_BASE_ADDR) + DMA_YSRB); + + } else { + __raw_writel(dma_info->W, + IO_ADDRESS(DMA_BASE_ADDR) + DMA_WSRA); + __raw_writel(dma_info->X, + IO_ADDRESS(DMA_BASE_ADDR) + DMA_XSRA); + __raw_writel(dma_info->Y, + IO_ADDRESS(DMA_BASE_ADDR) + DMA_YSRA); + } + } + + dma_base = (dma_regs_t *) (priv->dma_base); + + __raw_writel(dma_info->burstLength, &(dma_base->BurstLength)); + __raw_writel(dma_info->request, &(dma_base->RequestSource)); + + if (dma_info->ren) { + reg = dma_info->busuntils & 0x1FFFF; + if (dma_info->rto_en) { + reg |= 0xE000; + } + __raw_writel(reg, &(dma_base->BusUtilt)); + } else { + __raw_writel(dma_info->busuntils, &(dma_base->BusUtilt)); + } + + reg = __raw_readl(&(dma_base->Ctl)) & (~(DMA_CTL_ACRPT | DMA_CTL_RPT)); + + if (dma_info->dir) { + reg |= DMA_CTL_MDIR; + } else { + reg &= ~DMA_CTL_MDIR; + } + + if (priv->ren) { + reg |= DMA_CTL_REN; + } else { + reg &= ~DMA_CTL_REN; + } + + if ((dma_info->M2D_Valid) && (dma_info->msel)) { + reg |= DMA_CTL_MSEL; + } else { + reg &= ~DMA_CTL_MSEL; + } + + if (dma_info->mode) { + DMA_CTL_SET_SMOD(reg, dma_info->destType); + DMA_CTL_SET_SSIZ(reg, dma_info->destPort); + DMA_CTL_SET_DMOD(reg, dma_info->sourceType); + DMA_CTL_SET_DSIZ(reg, dma_info->sourcePort); + } else { + DMA_CTL_SET_SMOD(reg, dma_info->sourceType); + DMA_CTL_SET_SSIZ(reg, dma_info->sourcePort); + DMA_CTL_SET_DMOD(reg, dma_info->destType); + DMA_CTL_SET_DSIZ(reg, dma_info->destPort); + } + + __raw_writel(reg, &(dma_base->Ctl)); + + __clear_dma_interrupt(dma->channel); + unmask_dma_interrupt(dma->channel); + + disable_dma_clk(); + return 0; +} + +/*!@brief setup interrupt and setup dma channel by dma parameter */ +static inline int __init_dma_channel(mxc_dma_channel_t * chan, + mx2_dma_info_t * dma_info) +{ + mx2_dma_priv_t *dma_private = (mx2_dma_priv_t *) chan->private; + dma_regs_t *dma_base; + int ret; + + mask_dma_interrupt(chan->channel); + ret = + request_irq(dma_private->dma_irq, dma_irq_handler, + IRQF_DISABLED | IRQF_SHARED, chan->dev_name, + (void *)chan); + if (ret) { + printk(KERN_ERR + "%s: unable to request IRQ %d for DMA channel\n", + chan->dev_name, dma_private->dma_irq); + return ret; + } + + enable_dma_clk(); + + dma_base = (dma_regs_t *) (dma_private->dma_base); + __raw_writel(0, &(dma_base->Ctl)); + + ret = 0; + if ((ret = setup_dma_channel(chan, dma_info))) { + free_irq(dma_private->dma_irq, (void *)chan); + } + disable_dma_clk(); + return 0; +} + +/*!@brief initialize buffer descriptor ring.*/ +static inline void init_dma_bd(mx2_dma_priv_t * private) +{ + int i; + mx2_dma_bd_t *pbd; + private->bd_rd = private->bd_wr = 0; + atomic_set(&(private->bd_used), 0); + for (i = 0, pbd = private->bd_ring; i < MAX_BD_SIZE; i++, pbd++) { + pbd->state = 0; + } +} + +/*!@brief add dma buffer into buffer descriptor ring */ +static inline int fill_dma_bd(mxc_dma_channel_t * dma, + mxc_dma_requestbuf_t * buf, int num, + mxc_dma_mode_t mode) +{ + int i, wr; + unsigned long flags, mask; + mx2_dma_priv_t *priv = dma->private; + mx2_dma_bd_t *p, *q; + + if ((atomic_read(&(priv->bd_used)) + num) > MAX_BD_SIZE) { + return -EBUSY; + } + + for (i = 0; i < num; i++) { + wr = priv->bd_wr; + p = priv->bd_ring + wr; + p->mode = mode; + p->count = buf[i].num_of_bytes; + p->src_addr = buf[i].src_addr; + p->dst_addr = buf[i].dst_addr; + if (i == num - 1) { + p->state = DMA_BD_ST_LAST | DMA_BD_ST_PEND; + } else { + p->state = DMA_BD_ST_PEND; + } + priv->bd_wr = (wr + 1) % MAX_BD_SIZE; + atomic_inc(&(priv->bd_used)); + + if (atomic_read(&(priv->bd_used)) != 2) + continue; + /* Disable interrupt of this channel */ + local_irq_save(flags); + local_irq_disable(); + save_dma_interrupt(mask); + mask_dma_interrupt(dma->channel); + local_irq_restore(flags); + /*TODO :: + * If channel is transfering and supports chain_buffer, + * when the new buffer is 2st buffer , repeat must be enabled + */ + if (priv->dma_chaining && dma->active) { + q = priv->bd_ring + priv->bd_rd; + if (q && (q->state & DMA_BD_ST_BUSY)) { + if (atomic_read(&(priv->bd_used)) == 2) { + setup_dmac(dma); + } + } + } + restore_dma_interrupt(mask); + } + return 0; +} + +/*!@brief add sg-list into buffer descriptor ring */ +static inline int fill_dma_bd_by_sg(mxc_dma_channel_t * dma, + struct scatterlist *sg, int num, + int real_bytes, mxc_dma_mode_t mode) +{ + int i, wr, total_bytes = real_bytes; + unsigned long flags, mask; + mx2_dma_priv_t *priv = dma->private; + mx2_dma_bd_t *p, *q; + if ((atomic_read(&(priv->bd_used)) + num) > MAX_BD_SIZE) { + return -EBUSY; + } + + for (i = 0; i < num && ((real_bytes <= 0) || (total_bytes > 0)); i++) { + wr = priv->bd_wr; + p = priv->bd_ring + wr; + p->mode = mode; + if (real_bytes > 0) { + if (sg[i].length >= total_bytes) { + p->count = total_bytes; + } else { + p->count = sg[i].length; + } + total_bytes -= p->count; + } else { + p->count = sg[i].length; + } + if (mode == MXC_DMA_MODE_READ) { + p->src_addr = priv->dma_info->per_address; + p->dst_addr = sg[i].dma_address; + } else { + p->dst_addr = priv->dma_info->per_address; + p->src_addr = sg[i].dma_address; + } + if ((i == num - 1) || ((real_bytes > 0) && (total_bytes == 0))) { + p->state = DMA_BD_ST_LAST | DMA_BD_ST_PEND; + } else { + p->state = DMA_BD_ST_PEND; + } + priv->bd_wr = (wr + 1) % MAX_BD_SIZE; + atomic_inc(&(priv->bd_used)); + + if (atomic_read(&(priv->bd_used)) != 2) + continue; + /* Disable interrupt of this channel */ + local_irq_save(flags); + local_irq_disable(); + save_dma_interrupt(mask); + mask_dma_interrupt(dma->channel); + local_irq_restore(flags); + /*TODO :: + * If channel is transfering and supports chain_buffer, + * when the new buffer is 2st buffer , repeat must be enabled + */ + if (priv->dma_chaining && dma->active) { + q = next_dma_bd(priv); + if (q && (q->state & DMA_BD_ST_BUSY)) { + if ((atomic_read(&(priv->bd_used))) == 2) { + setup_dmac(dma); + } + } + } + restore_dma_interrupt(mask); + } + return 0; +} + +/*!@brief select next buffer descripter to transfer. + * return 1: need call call-back function. 0: Not need call call-back. + * it just is called in ISR + */ +static inline int consume_dma_bd(mxc_dma_channel_t * dma, int error) +{ + mx2_dma_priv_t *priv = dma->private; + mx2_dma_bd_t *p; + int notify = 0; + if (priv == NULL) { + printk(KERN_ERR + "request dma channel %d which is not initialize completed.!\n", + dma->channel); + return 1; + } + if (error != MXC_DMA_DONE) { + for (p = priv->bd_ring + priv->bd_rd; + atomic_read(&(priv->bd_used)) > 0;) { + priv->bd_rd = (priv->bd_rd + 1) % MAX_BD_SIZE; + atomic_dec(&(priv->bd_used)); + if (p->state & DMA_BD_ST_LAST) { + p->state = 0; + break; + } + p->state = 0; + } + notify = 1; + } else { + p = priv->bd_ring + priv->bd_rd; + priv->bd_rd = (priv->bd_rd + 1) % MAX_BD_SIZE; + atomic_dec(&(priv->bd_used)); + notify = (p->state & DMA_BD_ST_LAST) == DMA_BD_ST_LAST; + } + if (atomic_read(&(priv->bd_used)) <= 0) { + dma->active = 0; + atomic_dec(&g_dma_actived); + } else { + setup_dmac(dma); + } + return notify; +} + +/*! + * This function is generally called by the driver at open time. + * The DMA driver would do any initialization steps that is required + * to get the channel ready for data transfer. + * + * @param channel_id a pre-defined id. The peripheral driver would specify + * the id associated with its peripheral. This would be + * used by the DMA driver to identify the peripheral + * requesting DMA and do the necessary setup on the + * channel associated with the particular peripheral. + * The DMA driver could use static or dynamic DMA channel + * allocation. + * @param dev_name module name or device name + * @return returns a negative number on error if request for a DMA channel did not + * succeed, returns the channel number to be used on success. + */ +int mxc_dma_request(mxc_dma_device_t channel_id, char *dev_name) +{ + mxc_dma_channel_t *dma; + mx2_dma_priv_t *dma_private = NULL; + mx2_dma_info_t *dma_info = mxc_dma_get_info(channel_id); + int index; + int ret; + + if (dma_info == NULL) { + return -EINVAL; + } + + if ((index = get_dma_channel(dma_info->dma_chan)) < 0) { + return -ENODEV; + } + + dma = g_dma_channels + index; + dma_private = (mx2_dma_priv_t *) dma->private; + if (dma_private == NULL) { + printk(KERN_ERR + "request dma channel %d which is not initialize completed.!\n", + index); + ret = -EFAULT; + goto exit; + } + + dma->active = 0; + dma_private->dma_info = NULL; + dma->cb_fn = NULL; + dma->cb_args = NULL; + dma->dev_name = dev_name; + dma->mode = dma_info->mode ? MXC_DMA_MODE_WRITE : MXC_DMA_MODE_READ; + init_dma_bd(dma_private); + + if (!(ret = __init_dma_channel(dma, dma_info))) { + dma_private->dma_info = dma_info; + return index; + } + exit: + put_dma_channel(index); + return ret; +} + +/*! + * This function is generally called by the driver at close time. The DMA + * driver would do any cleanup associated with this channel. + * + * @param channel_num the channel number returned at request time. This + * would be used by the DMA driver to identify the calling + * driver and do the necessary cleanup on the channel + * associated with the particular peripheral + * @return returns a negative number on error or 0 on success + */ +int mxc_dma_free(int channel_num) +{ + mxc_dma_channel_t *dma; + mx2_dma_priv_t *dma_private; + + if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) { + return -EINVAL; + } + + dma = g_dma_channels + channel_num; + dma_private = (mx2_dma_priv_t *) dma->private; + if (dma_private == NULL) { + printk(KERN_ERR + "Free dma %d which is not completed initialization \n", + channel_num); + return -EFAULT; + } + if (dma->lock) { + if (dma->active) { /*Channel is busy */ + mxc_dma_disable(channel_num); + } + + dma_private = (mx2_dma_priv_t *) dma->private; + + enable_dma_clk(); + mask_dma_interrupt(channel_num); + disable_dma_clk(); + + free_irq(dma_private->dma_irq, (void *)dma); + put_dma_channel(channel_num); + } + return 0; +} + +/*! + * This function would just configure the buffers specified by the user into + * dma channel. The caller must call mxc_dma_enable to start this transfer. + * + * @param channel_num the channel number returned at request time. This + * would be used by the DMA driver to identify the calling + * driver and do the necessary cleanup on the channel + * associated with the particular peripheral + * @param dma_buf an array of physical addresses to the user defined + * buffers. The caller must guarantee the dma_buf is + * available until the transfer is completed. + * @param num_buf number of buffers in the array + * @param mode specifies whether this is READ or WRITE operation + * @return This function returns a negative number on error if buffer could not be + * added with DMA for transfer. On Success, it returns 0 + */ +int mxc_dma_config(int channel_num, mxc_dma_requestbuf_t * dma_buf, int num_buf, + mxc_dma_mode_t mode) +{ + mxc_dma_channel_t *dma; + mx2_dma_priv_t *dma_private; + + if ((dma_buf == NULL) || (num_buf < 1)) { + return -EINVAL; + } + + if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) { + return -EINVAL; + } + + dma = g_dma_channels + channel_num; + dma_private = (mx2_dma_priv_t *) dma->private; + if (dma_private == NULL) { + printk(KERN_ERR + "config dma %d which is not completed initialization \n", + channel_num); + return -EFAULT; + } + + if (dma->lock == 0) { + return -ENODEV; + } + + /*TODO: dma chainning can not support on bi-dir channel */ + if (dma_private->dma_chaining && (dma->mode != mode)) { + return -EINVAL; + } + + /*TODO: fill dma buffer into driver . + * If driver is no enought buffer to save them , it will return -EBUSY + */ + if (fill_dma_bd(dma, dma_buf, num_buf, mode)) { + return -EBUSY; + } + + return 0; +} + +/*! + * This function would just configure the scatterlist specified by the + * user into dma channel. This is a slight variation of mxc_dma_config(), + * it is provided for the convenience of drivers that have a scatterlist + * passed into them. It is the calling driver's responsibility to have the + * correct physical address filled in the "dma_address" field of the + * scatterlist. + * + * @param channel_num the channel number returned at request time. This + * would be used by the DMA driver to identify the calling + * driver and do the necessary cleanup on the channel + * associated with the particular peripheral + * @param sg a scatterlist of buffers. The caller must guarantee + * the dma_buf is available until the transfer is + * completed. + * @param num_buf number of buffers in the array + * @param num_of_bytes total number of bytes to transfer. If set to 0, this + * would imply to use the length field of the scatterlist + * for each DMA transfer. Else it would calculate the size + * for each DMA transfer. + * @param mode specifies whether this is READ or WRITE operation + * @return This function returns a negative number on error if buffer could not + * be added with DMA for transfer. On Success, it returns 0 + */ +int mxc_dma_sg_config(int channel_num, struct scatterlist *sg, + int num_buf, int num_of_bytes, mxc_dma_mode_t mode) +{ + mxc_dma_channel_t *dma; + mx2_dma_priv_t *dma_private; + + if ((sg == NULL) || (num_buf < 1) || (num_of_bytes < 0)) { + return -EINVAL; + } + + if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) { + return -EINVAL; + } + + dma = g_dma_channels + channel_num; + dma_private = (mx2_dma_priv_t *) dma->private; + if (dma_private == NULL) { + printk(KERN_ERR + "config_sg dma %d which is not completed initialization \n", + channel_num); + return -EFAULT; + } + + if (dma->lock == 0) { + return -ENODEV; + } + + /*TODO: dma chainning can not support on bi-dir channel */ + if (dma_private->dma_chaining && (dma->mode != mode)) { + return -EINVAL; + } + + /*TODO: fill dma buffer into driver . + * If driver is no enought buffer to save them , it will return -EBUSY + */ + if (fill_dma_bd_by_sg(dma, sg, num_buf, num_of_bytes, mode)) { + return -EBUSY; + } + return 0; +} + +/*! + * This function is provided if the driver would like to set/change its + * callback function. + * + * @param channel_num the channel number returned at request time. This + * would be used by the DMA driver to identify the calling + * driver and do the necessary cleanup on the channel + * associated with the particular peripheral + * @param callback a callback function to provide notification on transfer + * completion, user could specify NULL if he does not wish + * to be notified + * @param arg an argument that gets passed in to the callback + * function, used by the user to do any driver specific + * operations. + * @return this function returns an error if the callback could not be set + * for the channel + */ +int mxc_dma_callback_set(int channel_num, mxc_dma_callback_t callback, + void *arg) +{ + mxc_dma_channel_t *dma; + + if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) { + return -EINVAL; + } + dma = g_dma_channels + channel_num; + + if (!dma->lock) { + return -ENODEV; + } + + if (dma->active) { + return -EBUSY; + } + dma->cb_fn = callback; + dma->cb_args = arg; + return 0; + +} + +/*! + * This stops the DMA channel and any ongoing transfers. Subsequent use of + * mxc_dma_enable() will restart the channel and restart the transfer. + * + * @param channel_num the channel number returned at request time. This + * would be used by the DMA driver to identify the calling + * driver and do the necessary cleanup on the channel + * associated with the particular peripheral + * @return returns a negative number on error or 0 on success + */ +int mxc_dma_disable(int channel_num) +{ + mxc_dma_channel_t *dma; + mx2_dma_priv_t *priv; + unsigned long ctrl_val; + + if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) { + return -EINVAL; + } + + dma = g_dma_channels + channel_num; + + if (dma->lock == 0) { + return -EINVAL; + } + + if (!dma->active) { + return -EINVAL; + } + + priv = (mx2_dma_priv_t *) dma->private; + if (priv == NULL) { + printk(KERN_ERR "disable a uncompleted dma channel %d\n", + channel_num); + return -EFAULT; + } + + dma->active = 0; + enable_dma_clk(); + + __clear_dma_interrupt(channel_num); + ctrl_val = + __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_CCR(channel_num)); + ctrl_val &= ~DMA_CTL_CEN; /* clear CEN bit */ + __raw_writel(ctrl_val, + IO_ADDRESS(DMA_BASE_ADDR) + DMA_CCR(channel_num)); + disable_dma_clk(); + atomic_dec(&g_dma_actived); + + /*TODO: Clear all request buffers */ + flush_dma_bd(priv); + return 0; +} + +/*! + * This starts DMA transfer. Or it restarts DMA on a stopped channel + * previously stopped with mxc_dma_disable(). + * + * @param channel_num the channel number returned at request time. This + * would be used by the DMA driver to identify the calling + * driver and do the necessary cleanup on the channel + * associated with the particular peripheral + * @return returns a negative number on error or 0 on success + */ +int mxc_dma_enable(int channel_num) +{ + mxc_dma_channel_t *dma; + mx2_dma_priv_t *priv; + + if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) { + return -EINVAL; + } + + dma = g_dma_channels + channel_num; + + if (dma->lock == 0) { + return -EINVAL; + } + + priv = (mx2_dma_priv_t *) dma->private; + if (priv == NULL) { + printk(KERN_ERR "enable a uncompleted dma channel %d\n", + channel_num); + return -EFAULT; + } + + if (dma->active) { + return 0; + } + dma->active = 1; + priv->trans_bytes = 0; + + enable_dma_clk(); + + atomic_inc(&g_dma_actived); + __clear_dma_interrupt(channel_num); + + setup_dmac(dma); + disable_dma_clk(); + return 0; +} + +/*! +*@brief Dump DMA registers +* +*@param channel Requested channel NO. +*@return none +*/ + +void mxc_dump_dma_register(int channel) +{ + mxc_dma_channel_t *dma = &g_dma_channels[channel]; + mx2_dma_priv_t *priv = (mx2_dma_priv_t *) dma->private; + dma_regs_t *dma_base; + + printk(KERN_INFO "======== Dump dma channel %d \n", channel); + if ((unsigned)channel >= MXC_DMA_CHANNELS) { + printk(KERN_INFO "Channel number is invalid \n"); + return; + } + if (!dma->lock) { + printk(KERN_INFO "Channel is not allocated \n"); + return; + } + + printk(KERN_INFO "g_dma_actived = %d\n", atomic_read(&g_dma_actived)); + + enable_dma_clk(); + dma_base = (dma_regs_t *) (priv->dma_base); + printk(KERN_INFO "DMA COMMON REGISTER\n"); + printk(KERN_INFO "DMA CONTROL DMA_DCR: %08x\n", + __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR)); + printk(KERN_INFO "DMA Interrupt status DMA_DISR: %08x\n", + __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DISR)); + printk(KERN_INFO "DMA Interrupt Mask DMA_DIMR: %08x\n", + __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DIMR)); + printk(KERN_INFO "DMA Burst Time Out DMA_DBTOSR: %08x\n", + __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBTOSR)); + printk(KERN_INFO "DMA request Time Out DMA_DRTOSR: %08x\n", + __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DRTOSR)); + printk(KERN_INFO "DMA Transfer Error DMA_DSESR: %08x\n", + __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DSESR)); + printk(KERN_INFO "DMA DMA_Overflow DMA_DBOSR: %08x\n", + __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBOSR)); + printk(KERN_INFO "DMA Burst Time OutCtl DMA_BurstTOCtl: %08x\n", + __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBTOCR)); + + printk(KERN_INFO "DMA 2D X size: %08x\n", + __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_XSRA)); + printk(KERN_INFO "DMA 2D Y size: %08x\n", + __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_YSRA)); + printk(KERN_INFO "DMA 2D Z size: %08x\n", + __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_WSRA)); + + printk(KERN_INFO "DMA Chan %2d Sourc SourceAddr: %08x\n", channel, + __raw_readl(&(dma_base->SourceAddr))); + printk(KERN_INFO "DMA Chan %2d dest DestAddr: %08x\n", channel, + __raw_readl(&(dma_base->DestAddr))); + printk(KERN_INFO "DMA Chan %2d count Count: %08x\n", channel, + __raw_readl(&(dma_base->Count))); + printk(KERN_INFO "DMA Chan %2d Ctl Ctl: %08x\n", channel, + __raw_readl(&(dma_base->Ctl))); + printk(KERN_INFO "DMA Chan %2d request RequestSource: %08x\n", + channel, __raw_readl(&(dma_base->RequestSource))); + printk(KERN_INFO "DMA Chan %2d burstL BurstLength: %08x\n", channel, + __raw_readl(&(dma_base->BurstLength))); + printk(KERN_INFO "DMA Chan %2d requestTO ReqTimeout: %08x\n", channel, + __raw_readl(&(dma_base->ReqTimeout))); + printk(KERN_INFO "DMA Chan %2d BusUtilt BusUtilt: %08x\n", channel, + __raw_readl(&(dma_base->BusUtilt))); + + disable_dma_clk(); +} + +#ifdef DMA_PM + +static int channel_in_use(void) +{ + int i; + for (i = 0; i < MXC_DMA_CHANNELS; i++) { + if (dma_chan[i].lock) + return 1; + } + return 0; +} + +int mxc_dma_pm_standby(void) +{ + unsigned long reg; + if (dma_pm_status == DMA_PMST_STANDBY) + return 0; + + if (!channel_in_use()) { + /*Disable DMA */ + __disable_dma_clk(); + dma_pm_status = DMA_PMST_STANDBY; + return 0; + } + return -1; +} + +int mxc_dma_pm_resume(void) +{ + unsigned long reg; + if (dma_pm_status == DMA_PMST_RESUME) + return 0; + + /*Enable HCLK_DMA and DMA(ipg clock) */ + dma_pm_status = DMA_PMST_RESUME; + return 0; +} + +int mxc_dma_pm_suspend(void) +{ + unsigned long reg; + if (dma_pm_status == DMA_PMST_SUSPEND) + return 0; + + if (!channel_in_use()) { + /*Disable DMA */ + __disable_dma_clk(); + dma_pm_status = DMA_PMST_SUSPEND; + return 0; + } + return -1; +} + +int mxc_dma_pm_handler(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + int ret = 0; + switch (rqst) { + /*APM doesn't send PM_STANDBY and PM_STANDBY_RESUME request now. */ + case PM_SUSPEND: + ret = dma_pm_suspend(); + break; + case PM_RESUME: + ret = dma_pm_resume(); + break; + } + return ret; +} + +#endif /*DMA_PM */ + +int __init mxc_dma_init(void) +{ + int i; + mxc_dma_channel_t *dma = g_dma_channels; + mx2_dma_priv_t *private = g_dma_privates; + + memset(dma, 0, sizeof(mxc_dma_channel_t) * MXC_DMA_CHANNELS); + for (i = 0; i < MXC_DMA_CHANNELS; i++, dma++, private++) { + dma->channel = i; + dma->private = private; + private->dma_base = + (unsigned int)(IO_ADDRESS(DMA_BASE_ADDR + DMA_CH_BASE(i))); + private->dma_irq = i + MXC_DMA_INTR_0; /*Dma channel interrupt number */ + private->bd_ring = &g_dma_bd_table[i][0]; + } + + mxc_dma_load_info(g_dma_channels); + + dma_clk = clk_get(NULL, "dma_clk"); + clk_enable(dma_clk); + + __raw_writel(0x2, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR); /*reset DMA; */ + + disable_dma_clk(); + + /*use module init because create_proc after init_dma */ + g_proc_dir = create_proc_entry("dma", 0, NULL); + g_proc_dir->read_proc = (read_proc_t *) mxc_get_dma_list; + g_proc_dir->data = NULL; + +#ifdef DMA_PM + /* Register the device with power management. */ + dma_pm = pm_register(PM_DMA_DEV, PM_SYS_UNKNOWN, dma_pm_handler); +#endif + + return 0; +} + +arch_initcall(mxc_dma_init); + +EXPORT_SYMBOL(mxc_dma_request); +EXPORT_SYMBOL(mxc_dma_free); +EXPORT_SYMBOL(mxc_dma_callback_set); +EXPORT_SYMBOL(mxc_dma_enable); +EXPORT_SYMBOL(mxc_dma_disable); +EXPORT_SYMBOL(mxc_dma_config); +EXPORT_SYMBOL(mxc_dma_sg_config); +EXPORT_SYMBOL(mxc_dump_dma_register); diff --git a/arch/arm/plat-mxc/dptc.c b/arch/arm/plat-mxc/dptc.c new file mode 100644 index 000000000000..e35e3a2440a8 --- /dev/null +++ b/arch/arm/plat-mxc/dptc.c @@ -0,0 +1,421 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file dptc.c + * + * @brief Driver for the Freescale Semiconductor MXC DPTC module. + * + * The DPTC driver is designed to control the MXC DPTC hardware. + * hardware. Upon initialization, the DPTC driver initializes the DPTC hardware + * sets up driver nodes attaches to the DPTC interrupt and initializes internal + * data structures. When the DPTC interrupt occurs the driver checks the cause + * of the interrupt (lower frequency, increase frequency or emergency) and changes + * the CPU voltage according to translation table that is loaded into the driver. + * The driver read method is used to read the log buffer. + * Driver ioctls are used to change driver parameters and enable/disable the + * DVFS operation. + * + * @ingroup PM + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> + +#include <asm/arch/clock.h> +#include <asm/arch/gpio.h> +#include <asm/arch/pmic_external.h> +#include <asm/arch/pmic_power.h> +#include <asm/arch/hardware.h> + +#if defined(CONFIG_ARCH_MX3) +#include "../mach-mx3/crm_regs.h" +#elif defined(CONFIG_ARCH_MXC91321) +#include "../mach-mxc91321/crm_regs.h" +#else +#include "../mach-mx27/crm_regs.h" +#endif + +int dptc_is_active; +int turbo_mode_active; + +static int curr_wp; +static u32 ptvai; +static struct delayed_work dptc_work; +static struct dptc_wp *dptc_wp_allfreq; +static struct device *dptc_dev; +static struct clk *cpu_clk; + +DEFINE_SPINLOCK(mxc_dptc_lock); + +enum { + DPTC_PTVAI_NOCHANGE = 0x0, + DPTC_PTVAI_DECREASE, + DPTC_PTVAI_INCREASE, + DPTC_PTVAI_EMERG, +}; + +static void update_dptc_wp(u32 wp) +{ + t_pmic_regulator regulator; + t_regulator_voltage voltage; + + regulator = dptc_wp_allfreq[wp].regulator; + voltage = (t_regulator_voltage) dptc_wp_allfreq[wp].voltage; + + __raw_writel(dptc_wp_allfreq[wp].dcvr0, MXC_CCM_DCVR0); + __raw_writel(dptc_wp_allfreq[wp].dcvr1, MXC_CCM_DCVR1); + __raw_writel(dptc_wp_allfreq[wp].dcvr2, MXC_CCM_DCVR2); + __raw_writel(dptc_wp_allfreq[wp].dcvr3, MXC_CCM_DCVR3); + + if (cpu_is_mx31()) { + pmic_power_regulator_set_voltage(SW_SW1A, voltage); + } else if (cpu_is_mxc91321()) { + pmic_power_switcher_set_dvs(SW_SW1A, voltage); + } else { + pmic_power_regulator_set_voltage(SW_SW1A, voltage); + } + + pr_debug("dcvr0-3: 0x%x, 0x%x, 0x%x, 0x%x; vol: %d\n", + dptc_wp_allfreq[wp].dcvr0, + dptc_wp_allfreq[wp].dcvr1, + dptc_wp_allfreq[wp].dcvr2, + dptc_wp_allfreq[wp].dcvr3, dptc_wp_allfreq[wp].voltage); +} + +static irqreturn_t dptc_irq(int irq, void *dev_id) +{ + u32 pmcr0 = __raw_readl(MXC_CCM_PMCR0); + + ptvai = (pmcr0 & MXC_CCM_PMCR0_PTVAI_MASK) >> + MXC_CCM_PMCR0_PTVAI_OFFSET; + + pr_debug("dptc_irq: ptvai = 0x%x (0x%x)!!!!!!!\n", ptvai, pmcr0); + + /* disable DPTC and mask its interrupt */ + pmcr0 = (pmcr0 & ~(MXC_CCM_PMCR0_DPTEN)) | MXC_CCM_PMCR0_PTVAIM; + __raw_writel(pmcr0, MXC_CCM_PMCR0); + + schedule_delayed_work(&dptc_work, 0); + + return IRQ_RETVAL(1); +} + +static void dptc_workqueue_handler(struct work_struct *work) +{ + u32 pmcr0 = __raw_readl(MXC_CCM_PMCR0); + + if (!cpu_is_mxc91321() || (turbo_mode_active == 1)) { + switch (ptvai) { + case DPTC_PTVAI_DECREASE: + curr_wp++; + break; + case DPTC_PTVAI_INCREASE: + case DPTC_PTVAI_EMERG: + curr_wp--; + if (curr_wp < 0) { + /* already max voltage */ + curr_wp = 0; + printk(KERN_WARNING + "dptc: already maximum voltage\n"); + } + break; + + /* Unknown interrupt cause */ + default: + BUG(); + } + + if (curr_wp > DPTC_WP_SUPPORTED || curr_wp < 0) { + panic("Can't support this working point: %d\n", + curr_wp); + } + update_dptc_wp(curr_wp); + + /* enable DPTC and unmask its interrupt */ + pmcr0 = (pmcr0 & ~(MXC_CCM_PMCR0_PTVAIM)) | MXC_CCM_PMCR0_DPTEN; + __raw_writel(pmcr0, MXC_CCM_PMCR0); + } +} + +static int start_dptc(void) +{ + u32 pmcr0, flags; + unsigned long cpu_rate; + + if (dptc_is_active) { + return 0; + } + + spin_lock_irqsave(&mxc_dptc_lock, flags); + + cpu_rate = clk_get_rate(cpu_clk); + + if (cpu_is_mx27() && cpu_rate < 399000000) { + goto err; + } else if (cpu_rate < 532000000) { + goto err; + } + + pmcr0 = __raw_readl(MXC_CCM_PMCR0); + + /* enable DPTC and unmask its interrupt */ + pmcr0 = ((pmcr0 & ~(MXC_CCM_PMCR0_PTVAIM)) | MXC_CCM_PMCR0_DPTEN) | + (MXC_CCM_PMCR0_DPVCR | MXC_CCM_PMCR0_DPVV); + + __raw_writel(pmcr0, MXC_CCM_PMCR0); + + dptc_is_active = 1; + + spin_unlock_irqrestore(&mxc_dptc_lock, flags); + + pr_info("DPTC has been started \n"); + + return 0; + + err: + spin_unlock_irqrestore(&mxc_dptc_lock, flags); + pr_info("Core is not running in Turbo mode\n"); + pr_info("DPTC is not enabled\n"); + return -1; +} + +static void stop_dptc(void) +{ + u32 pmcr0; + + if (!dptc_is_active) { + return; + } + + pmcr0 = __raw_readl(MXC_CCM_PMCR0); + + /* disable DPTC and mask its interrupt */ + pmcr0 = ((pmcr0 & ~(MXC_CCM_PMCR0_DPTEN)) | MXC_CCM_PMCR0_PTVAIM) & + (~MXC_CCM_PMCR0_DPVCR); + + __raw_writel(pmcr0, MXC_CCM_PMCR0); + + dptc_is_active = 0; + + /* Restore Turbo Mode voltage to highest wp */ + update_dptc_wp(0); + curr_wp = 0; + + pr_info("DPTC has been stopped\n"); +} + +/*! + * This function is called to put the DPTC in a low power state. + * + */ +void dptc_disable(void) +{ + turbo_mode_active = 0; + stop_dptc(); +} + +/*! + * This function is called to resume the DPTC from a low power state. + * + */ +void dptc_enable(void) +{ + turbo_mode_active = 1; + start_dptc(); +} + +static ssize_t dptc_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + if (dptc_is_active) + return sprintf(buf, "DPTC is enabled\n"); + else + return sprintf(buf, "DPTC is disabled\n"); +} + +static ssize_t dptc_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + if (strstr(buf, "0") != NULL) { + dptc_disable(); + } else if (strstr(buf, "1") != NULL) { + dptc_enable(); + } + + return size; +} + +static DEVICE_ATTR(enable, 0644, dptc_show, dptc_store); + +/*! + * This is the probe routine for the DPTC driver. + * + * @param pdev The platform device structure + * + * @return The function returns 0 on success + * + */ +static int __devinit mxc_dptc_probe(struct platform_device *pdev) +{ + int res = 0; + u32 pmcr0 = __raw_readl(MXC_CCM_PMCR0); + + INIT_DELAYED_WORK(&dptc_work, dptc_workqueue_handler); + + /* request the DPTC interrupt */ + res = request_irq(INT_CCM, dptc_irq, IRQF_DISABLED, "mxc-dptc", NULL); + if (res) { + printk(KERN_ERR "DPTC: Unable to attach to DPTC interrupt"); + return res; + } + + if (cpu_is_mx31()) { + /* 256 system clock count; ARM interrupt; enable all 2 rf circuits */ + pmcr0 = (pmcr0 & ~(MXC_CCM_PMCR0_DCR)) | MXC_CCM_PMCR0_PTVIS | + MXC_CCM_PMCR0_DRCE3 | MXC_CCM_PMCR0_DRCE1; + + } else if (cpu_is_mxc91321()) { + pmcr0 = (pmcr0 | MXC_CCM_PMCR0_DRCE3) | MXC_CCM_PMCR0_DRCE2 | + MXC_CCM_PMCR0_DRCE1; + } else if (cpu_is_mx27()) { + pmcr0 = + (pmcr0 & ~(MXC_CCM_PMCR0_DCR)) | MXC_CCM_PMCR0_DRCE3 | + MXC_CCM_PMCR0_DRCE1; + } + + __raw_writel(pmcr0, MXC_CCM_PMCR0); + + dptc_dev = &pdev->dev; + res = sysfs_create_file(&dptc_dev->kobj, &dev_attr_enable.attr); + if (res) { + printk(KERN_ERR + "DPTC: Unable to register sysdev entry for dptc"); + return res; + } + + if (res != 0) { + printk(KERN_ERR "DPTC: Unable to start"); + return res; + } + + dptc_wp_allfreq = pdev->dev.platform_data; + + curr_wp = 0; + + update_dptc_wp(curr_wp); + + cpu_clk = clk_get(NULL, "cpu_clk"); + + pmcr0 = __raw_readl(MXC_CCM_PMCR0); + + return 0; +} + +/*! + * This function is called to put DPTC in a low power state. + * + * @param pdev the device structure + * @param state the power state the device is entering + * + * @return The function always returns 0. + */ +static int mxc_dptc_suspend(struct platform_device *pdev, pm_message_t state) +{ + dptc_disable(); + + return 0; +} + +/*! + * This function is called to resume the MU from a low power state. + * + * @param dev the device structure + * @param level the stage in device suspension process that we want the + * device to be put in + * + * @return The function always returns 0. + */ +static int mxc_dptc_resume(struct platform_device *pdev) +{ + dptc_enable(); + + return 0; +} + +static struct platform_driver mxc_dptc_v2_driver = { + .driver = { + .name = "mxc_dptc", + }, + .probe = mxc_dptc_probe, + .suspend = mxc_dptc_suspend, + .resume = mxc_dptc_resume, +}; + +/*! + * This function is called to resume the MU from a low power state. + * + * @param dev the device structure used to give information on which MU + * device (0 through 3 channels) to suspend + * @param level the stage in device suspension process that we want the + * device to be put in + * + * @return The function always returns 0. + */ + +static int __init dptc_init(void) +{ + if (platform_driver_register(&mxc_dptc_v2_driver) != 0) { + printk(KERN_ERR + "Driver register failed for mxc_dptc_v2_driver\n"); + return -ENODEV; + } + + printk("DPTC driver module loaded\n"); + + return 0; +} + +static void __exit dptc_cleanup(void) +{ + stop_dptc(); + + /* release the DPTC interrupt */ + free_irq(INT_CCM, NULL); + + sysfs_remove_file(&dptc_dev->kobj, &dev_attr_enable.attr); + + /* Unregister the device structure */ + platform_driver_unregister(&mxc_dptc_v2_driver); + + printk("DPTC driver module unloaded\n"); +} + +module_init(dptc_init); +module_exit(dptc_cleanup); + +EXPORT_SYMBOL(dptc_disable); +EXPORT_SYMBOL(dptc_enable); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("DPTC driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-mxc/dsp_bringup.c b/arch/arm/plat-mxc/dsp_bringup.c new file mode 100644 index 000000000000..0e33265e973a --- /dev/null +++ b/arch/arm/plat-mxc/dsp_bringup.c @@ -0,0 +1,242 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file plat-mxc/dsp_bringup.c + * + * @brief This files contains functions for DSP operations. + * + * @ingroup MSL_MXC91321 + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/moduleparam.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <asm/io.h> +#include <asm/setup.h> +#include <asm/arch/hardware.h> + +#include <asm/arch/dsp_bringup.h> + +#define MCU_MTR0_REGISTER IO_ADDRESS(MU_BASE_ADDR + 0x0) +#define MCU_MTR1_REGISTER IO_ADDRESS(MU_BASE_ADDR + 0x4) +#define MCU_MTR2_REGISTER IO_ADDRESS(MU_BASE_ADDR + 0x8) +#define MCU_MRR0_REGISTER IO_ADDRESS(MU_BASE_ADDR + 0x10) +#define MCU_MSR_REGISTER IO_ADDRESS(MU_BASE_ADDR + 0x20) +#define MCU_MCR_REGISTER IO_ADDRESS(MU_BASE_ADDR + 0x24) + +/* + * Control register to reset and to disable security mode on DSP core. + */ +#define REG_SPBA_INIT_IIM IO_ADDRESS(SPBA_CTRL_BASE_ADDR + 0x1C) +#define REG_DSP_HAB_TYPE IO_ADDRESS(IIM_BASE_ADDR + 0x814) + +/* + * Definition of parameter passed to kernel to decide + * if an image should be started on StarCore + */ +#define DSP_BOOT "dspboot" +#define DSP_BOOT_ON "on" +#define DSP_BOOT_OFF "off" + +#define WRITE_REQUEST 1 +#define WRITE_RESPONSE 1 + +#define READ_REQUEST 2 +#define READ_RESPONSE 2 + +#define MEM_BLOCK_REQUEST 3 +#define MEM_BLOCK_RESPONSE 3 + +#define STARTAPP_REQUEST 4 +#define STARTAPP_RESPONSE 4 + +#define BAD_OPCODE_RESPONSE 5 + +/* + * This variable holds address of MSR register. We need this register to + * issue a DSP reset and to disable security mode. + */ +volatile unsigned long *mcu_msr_register = + (volatile unsigned long *)MCU_MSR_REGISTER; +volatile unsigned long *mcu_mcr_register = + (volatile unsigned long *)MCU_MCR_REGISTER; +volatile unsigned long *mcu_mtr0_register = + (volatile unsigned long *)MCU_MTR0_REGISTER; +volatile unsigned long *mcu_mtr1_register = + (volatile unsigned long *)MCU_MTR1_REGISTER; +volatile unsigned long *mcu_mtr2_register = + (volatile unsigned long *)MCU_MTR2_REGISTER; +volatile unsigned long *mcu_mrr0_register = + (volatile unsigned long *)MCU_MRR0_REGISTER; + +volatile unsigned long *spba_iim_register = + (volatile unsigned long *)REG_SPBA_INIT_IIM; +volatile unsigned long *iim_hab_register = + (volatile unsigned long *)REG_DSP_HAB_TYPE; + +/* + * This variable holds whether we have received a "dspboot=on" or not + */ +static int dsp_boot_on = 0; + + /*! + * This function issues a DSP hardware reset. It executes + * three steps: + * + * 1) Sets DSP Hardware Reset bit into MU module - MCR register + * 2) De-asserts DSP Hardware Reset bit + * 3) Executes an active wait until DSP is out of reset state. + * + * @return No return value + */ +void reset_dsp(void) +{ + *mcu_mcr_register |= 0x10; + *mcu_mcr_register &= 0xFFFFFFEF; + + while (*mcu_msr_register & 0x80) ; +} + +/*! + * Disables security mode for DSP + * + * @return No return value + */ +void disable_security_mode(void) +{ + /*Disable DSP_HAB_TYPE security mode */ + *spba_iim_register = 0x00000007; + *iim_hab_register = 0x00000004; +} + +/*! + * parse_args system function passes kernel's parsed arguments + * to do_dsp_params in order to verify whether dspboot parameter + * has been passed or not. + * + * @return 0 if parameter is not "dspboot" + * 0 if "dspboot" parameter has been found and + * its value is equal to "on" + * 0 if "dspboot" parameter has been found and + * its value is equal to "off" + */ +static int __init do_dsp_params(char *param, char *val) +{ + + if (strcmp(param, DSP_BOOT) == 0) { + printk + ("STARCORE: dspboot parameter present. Go to try to start DSP!\n"); + if (strcmp(val, DSP_BOOT_ON) == 0) + dsp_boot_on = 1; + else + printk + ("STARCORE: dspboot parameter present, but option is set to \"off\"\n"); + } + + return 0; +} + +/*! + * This function search for 'dspboot' in kernel's command line + * options and checks its value. if value is 'on', a startapp_request + * will be sent to DSP core in order to start an image. Otherwise + * none request is sent + * + * @return Returns 1 if dspboot option = 'on' + * Returns 0 if dspboot option = 'off' + * Returns 0 if dspboot option does not exist + */ +int __init dsp_parse_cmdline(const char *cmdline) +{ + char tmp_cmdline[COMMAND_LINE_SIZE]; + + dsp_boot_on = 0; + + strlcpy(tmp_cmdline, cmdline, COMMAND_LINE_SIZE); + parse_args("DSP bringup arguments", tmp_cmdline, NULL, 0, + do_dsp_params); + return dsp_boot_on; +} + +/*! + * this function sends a jump vector to DSP + * in order to resume execution of a previously + * downloaded executable image. + * + * @return Returns -1 if it is not possible to allocate + * channel 0 or channel 1, both required by boot + * protocol to issue a start_application request. + * Returns request's result otherwise. + */ +void dsp_startapp_request(void) +{ + unsigned long *virtual_addr = NULL; + unsigned long physical_addr = 0; + + /* disable security mode */ + disable_security_mode(); + + /*execute a dsp hardware reset */ + reset_dsp(); + + /* Get a chunk of virtual memory to hold DSP image start address */ + virtual_addr = (unsigned long *)kmalloc(sizeof(unsigned long), + GFP_KERNEL); + *virtual_addr = IMAGE_START_ADDRESS; + + physical_addr = __pa((volatile void *)virtual_addr); + + /* Send a start application request to the DSP */ + *mcu_mtr0_register = STARTAPP_REQUEST; + + /* Send the start physical address to the DSP */ + *mcu_mtr1_register = physical_addr; + + /* Wait for the DSP response */ + while (!(*mcu_msr_register & 0x08000000)) ; + + /*check if DSP has acknowledged our request */ + if ((*mcu_mrr0_register & 0x000000FF) != STARTAPP_RESPONSE) + printk + ("STARCORE: DSP core DOES NOT acknowledged STARTAPP request!!\n"); + else + printk("STARCORE: DSP core acknowledged STARTAPP request\n"); + + kfree(virtual_addr); +} + +int dsp_memwrite_request(unsigned long addr, unsigned long word) +{ + int res = 0; + + /* Send a write application request to the DSP */ + *mcu_mtr0_register = 0x00010001; + + /* Send the write address to the DSP */ + *mcu_mtr1_register = addr; + + /* Send data to write to StarCore */ + *mcu_mtr2_register = word; + + /* Wait for the DSP response */ + while (!(*mcu_msr_register & 0x08000000)) ; + + /*return response of DSP */ + res = (int)(*mcu_mrr0_register & 0x000000FF); + return res; +} diff --git a/arch/arm/plat-mxc/gpio.c b/arch/arm/plat-mxc/gpio.c new file mode 100644 index 000000000000..a1c85dbaab0a --- /dev/null +++ b/arch/arm/plat-mxc/gpio.c @@ -0,0 +1,897 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * Implementation based on omap gpio.c + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/sysdev.h> +#include <linux/ptrace.h> +#include <asm/hardware.h> +#include <asm/irq.h> +#include <asm/arch/irqs.h> +#include <asm/mach/irq.h> +#include <asm/io.h> +#include <asm/arch/gpio.h> + +/*! + * @file plat-mxc/gpio.c + * + * @brief This file contains the GPIO implementation details. + * + * @ingroup GPIO + */ + +/* GPIO related defines */ +#if defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MX21) +enum gpio_reg { + GPIO_DR = 0x1C, + GPIO_GDIR = 0x00, + GPIO_PSR = 0x24, + GPIO_ICR1 = 0x028, + GPIO_ICR2 = 0x2C, + GPIO_IMR = 0x30, + GPIO_ISR = 0x34, +}; +#else +enum gpio_reg { + GPIO_DR = 0x00, + GPIO_GDIR = 0x04, + GPIO_PSR = 0x08, + GPIO_ICR1 = 0x0C, + GPIO_ICR2 = 0x10, + GPIO_IMR = 0x14, + GPIO_ISR = 0x18, +}; +#endif + +extern struct mxc_gpio_port mxc_gpio_ports[]; + +struct gpio_port { + u32 num; /*!< gpio port number */ + u32 base; /*!< gpio port base VA */ +#ifdef MXC_GPIO_SPLIT_IRQ_2 + u16 irq_0_15, irq_16_31; +#else + u16 irq; /*!< irq number to the core */ +#endif + u16 virtual_irq_start; /*!< virtual irq start number */ + u32 reserved_map; /*!< keep track of which pins are in use */ + u32 irq_is_level_map; /*!< if a pin's irq is level sensitive. default is edge */ + u32 suspend_wakeup; + u32 saved_wakeup; + spinlock_t lock; /*!< lock when operating on the port */ +}; +static struct gpio_port gpio_port[GPIO_PORT_NUM]; + +/* + * Find the pointer to the gpio_port for a given pin. + * @param gpio a gpio pin number + * @return pointer to \b struc \b gpio_port + */ +static inline struct gpio_port *get_gpio_port(u32 gpio) +{ + return &gpio_port[GPIO_TO_PORT(gpio)]; +} + +/* + * Check if a gpio pin is within [0, MXC_MAX_GPIO_LINES -1]. + * @param gpio a gpio pin number + * @return 0 if the pin number is valid; -1 otherwise + */ +static int check_gpio(u32 gpio) +{ + if (gpio >= MXC_MAX_GPIO_LINES) { + printk(KERN_ERR "mxc-gpio: invalid GPIO %d\n", gpio); + dump_stack(); + return -1; + } + return 0; +} + +/* + * Set a GPIO pin's direction + * @param port pointer to a gpio_port + * @param index gpio pin index value (0~31) + * @param is_input 0 for output; non-zero for input + */ +static void _set_gpio_direction(struct gpio_port *port, u32 index, int is_input) +{ + u32 reg = port->base + GPIO_GDIR; + u32 l; + + l = __raw_readl(reg); + if (is_input) + l &= ~(1 << index); + else + l |= 1 << index; + __raw_writel(l, reg); +} + +/*! + * Exported function to set a GPIO pin's direction + * @param pin a name defined by \b iomux_pin_name_t + * @param is_input 1 (or non-zero) for input; 0 for output + */ +void mxc_set_gpio_direction(iomux_pin_name_t pin, int is_input) +{ + struct gpio_port *port; + u32 gpio = IOMUX_TO_GPIO(pin); + + if (check_gpio(gpio) < 0) + return; + port = get_gpio_port(gpio); + spin_lock(&port->lock); + _set_gpio_direction(port, GPIO_TO_INDEX(gpio), is_input); + spin_unlock(&port->lock); +} + +/* + * Set a GPIO pin's data output + * @param port pointer to a gpio_port + * @param index gpio pin index value (0~31) + * @param data value to be set (only 0 or 1 is valid) + */ +static void _set_gpio_dataout(struct gpio_port *port, u32 index, u32 data) +{ + u32 reg = port->base + GPIO_DR; + u32 l = 0; + + l = (__raw_readl(reg) & (~(1 << index))) | (data << index); + __raw_writel(l, reg); +} + +/*! + * Exported function to set a GPIO pin's data output + * @param pin a name defined by \b iomux_pin_name_t + * @param data value to be set (only 0 or 1 is valid) + */ +void mxc_set_gpio_dataout(iomux_pin_name_t pin, u32 data) +{ + struct gpio_port *port; + u32 gpio = IOMUX_TO_GPIO(pin); + + if (check_gpio(gpio) < 0) + return; + + port = get_gpio_port(gpio); + spin_lock(&port->lock); + _set_gpio_dataout(port, GPIO_TO_INDEX(gpio), (data == 0) ? 0 : 1); + spin_unlock(&port->lock); +} + +/*! + * Return the data value of a GPIO signal. + * @param pin a name defined by \b iomux_pin_name_t + * + * @return value (0 or 1) of the GPIO signal; -1 if pass in invalid pin + */ +int mxc_get_gpio_datain(iomux_pin_name_t pin) +{ + struct gpio_port *port; + u32 gpio = IOMUX_TO_GPIO(pin); + + if (check_gpio(gpio) < 0) + return -1; + + port = get_gpio_port(gpio); + + return (__raw_readl(port->base + GPIO_DR) >> GPIO_TO_INDEX(gpio)) & 1; +} + +/* + * Clear a GPIO signal's interrupt status + * + * @param port pointer to a gpio_port + * @param index gpio pin index value (0~31) + */ +static inline void _clear_gpio_irqstatus(struct gpio_port *port, u32 index) +{ + __raw_writel(1 << index, port->base + GPIO_ISR); +} + +/* + * Set a GPIO pin's interrupt edge + * @param port pointer to a gpio_port + * @param index gpio pin index value (0~31) + * @param icr one of the values defined in \b gpio_edge_t + * to indicate how to generate an interrupt + */ +static void _set_gpio_edge_ctrl(struct gpio_port *port, u32 index, + gpio_edge_t edge) +{ + u32 reg = port->base; + u32 l, sig; + + reg += (index <= 15) ? GPIO_ICR1 : GPIO_ICR2; + sig = (index <= 15) ? index : (index - 16); + l = __raw_readl(reg); + l = (l & (~(0x3 << (sig * 2)))) | (edge << (sig * 2)); + __raw_writel(l, reg); + _clear_gpio_irqstatus(port, index); +} + +/* + * Enable/disable a GPIO signal's interrupt. + * + * @param port pointer to a gpio_port + * @param index gpio pin index value (0~31) + * @param enable \b #true for enabling the interrupt; \b #false otherwise + */ +static inline void _set_gpio_irqenable(struct gpio_port *port, u32 index, + bool enable) +{ + u32 reg = port->base + GPIO_IMR; + u32 mask = (!enable) ? 0 : 1; + u32 l; + + l = __raw_readl(reg); + l = (l & (~(1 << index))) | (mask << index); + __raw_writel(l, reg); +} + +static inline int _request_gpio(struct gpio_port *port, u32 index) +{ + spin_lock(&port->lock); + if (port->reserved_map & (1 << index)) { + printk(KERN_ERR + "GPIO port %d (0-based), pin %d is already reserved!\n", + port->num, index); + dump_stack(); + spin_unlock(&port->lock); + return -1; + } + port->reserved_map |= (1 << index); + spin_unlock(&port->lock); + return 0; +} + +/*! + * Request ownership for a GPIO pin. The caller has to check the return value + * of this function to make sure it returns 0 before make use of that pin. + * @param pin a name defined by \b iomux_pin_name_t + * @return 0 if successful; Non-zero otherwise + */ +int mxc_request_gpio(iomux_pin_name_t pin) +{ + struct gpio_port *port; + u32 index, gpio = IOMUX_TO_GPIO(pin); + + if (check_gpio(gpio) < 0) + return -EINVAL; + + port = get_gpio_port(gpio); + index = GPIO_TO_INDEX(gpio); + + return _request_gpio(port, index); +} + +/*! + * Release ownership for a GPIO pin + * @param pin a name defined by \b iomux_pin_name_t + */ +void mxc_free_gpio(iomux_pin_name_t pin) +{ + struct gpio_port *port; + u32 index, gpio = IOMUX_TO_GPIO(pin); + + if (check_gpio(gpio) < 0) + return; + + port = get_gpio_port(gpio); + index = GPIO_TO_INDEX(gpio); + + spin_lock(&port->lock); + if ((!(port->reserved_map & (1 << index)))) { + printk(KERN_ERR "GPIO port %d, pin %d wasn't reserved!\n", + port->num, index); + dump_stack(); + spin_unlock(&port->lock); + return; + } + port->reserved_map &= ~(1 << index); + port->irq_is_level_map &= ~(1 << index); + _set_gpio_direction(port, index, 1); + _set_gpio_irqenable(port, index, 0); + _clear_gpio_irqstatus(port, index); + spin_unlock(&port->lock); +} + +/* + * We need to unmask the GPIO port interrupt as soon as possible to + * avoid missing GPIO interrupts for other lines in the port. + * Then we need to mask-read-clear-unmask the triggered GPIO lines + * in the port to avoid missing nested interrupts for a GPIO line. + * If we wait to unmask individual GPIO lines in the port after the + * line's interrupt handler has been run, we may miss some nested + * interrupts. + */ +static void mxc_gpio_irq_handler(u32 irq, struct irq_desc *desc) +{ + u32 isr_reg = 0, imr_reg = 0, imr_val; + u32 int_valid; + u32 gpio_irq, mask = 0xFFFFFFFF; + struct gpio_port *port; + + port = (struct gpio_port *)get_irq_data(irq); + isr_reg = port->base + GPIO_ISR; + imr_reg = port->base + GPIO_IMR; + +#ifdef MXC_GPIO_SPLIT_IRQ_2 + if (irq == port->irq_0_15) { + mask = 0x0000FFFF; + } else { + mask = 0xFFFF0000; + } +#endif + + imr_val = __raw_readl(imr_reg) & mask; + int_valid = __raw_readl(isr_reg) & imr_val; + + if (unlikely(!int_valid)) { + printk(KERN_ERR "\nGPIO port: %d Spurious interrupt:0x%0x\n\n", + port->num, int_valid); + BUG(); /* oops */ + } + + gpio_irq = port->virtual_irq_start; + for (; int_valid != 0; int_valid >>= 1, gpio_irq++) { + struct irq_desc *d; + + if ((int_valid & 1) == 0) + continue; + d = irq_desc + gpio_irq; + if (unlikely(!(d->handle_irq))) { + printk(KERN_ERR "\nGPIO port: %d irq: %d unhandeled\n", + port->num, gpio_irq); + BUG(); /* oops */ + } + d->handle_irq(gpio_irq, d); + } +} + +#ifdef MXC_MUX_GPIO_INTERRUPTS +static void mxc_gpio_mux_irq_handler(u32 irq, struct irq_desc *desc) +{ + int i; + u32 isr_reg = 0, imr_reg = 0, imr_val; + u32 int_valid; + struct gpio_port *port; + + for (i = 0; i < GPIO_PORT_NUM; i++) { + port = &gpio_port[i]; + isr_reg = port->base + GPIO_ISR; + imr_reg = port->base + GPIO_IMR; + + imr_val = __raw_readl(imr_reg); + int_valid = __raw_readl(isr_reg) & imr_val; + + if (int_valid) { + set_irq_data(irq, (void *)port); + mxc_gpio_irq_handler(irq, desc); + } + } +} +#endif + +/* + * Disable a gpio pin's interrupt by setting the bit in the imr. + * @param irq a gpio virtual irq number + */ +static void gpio_mask_irq(u32 irq) +{ + u32 gpio = MXC_IRQ_TO_GPIO(irq); + struct gpio_port *port = get_gpio_port(gpio); + + _set_gpio_irqenable(port, GPIO_TO_INDEX(gpio), 0); +} + +/* + * Acknowledge a gpio pin's interrupt by clearing the bit in the isr. + * If the GPIO interrupt is level triggered, it also disables the interrupt. + * @param irq a gpio virtual irq number + */ +static void gpio_ack_irq(u32 irq) +{ + u32 gpio = MXC_IRQ_TO_GPIO(irq); + u32 index = GPIO_TO_INDEX(gpio); + struct gpio_port *port = get_gpio_port(gpio); + + _clear_gpio_irqstatus(port, GPIO_TO_INDEX(gpio)); + if (port->irq_is_level_map & (1 << index)) { + gpio_mask_irq(irq); + } +} + +/* + * Enable a gpio pin's interrupt by clearing the bit in the imr. + * @param irq a gpio virtual irq number + */ +static void gpio_unmask_irq(u32 irq) +{ + u32 gpio = MXC_IRQ_TO_GPIO(irq); + struct gpio_port *port = get_gpio_port(gpio); + + _set_gpio_irqenable(port, GPIO_TO_INDEX(gpio), 1); +} + +/* + * Enable a gpio pin's interrupt by clearing the bit in the imr. + * @param irq a gpio virtual irq number + */ +static int gpio_set_irq_type(u32 irq, u32 type) +{ + u32 gpio = MXC_IRQ_TO_GPIO(irq); + struct gpio_port *port = get_gpio_port(gpio); + + switch (type) { + case IRQT_RISING: + _set_gpio_edge_ctrl(port, GPIO_TO_INDEX(gpio), + GPIO_INT_RISE_EDGE); + set_irq_handler(irq, handle_edge_irq); + port->irq_is_level_map &= ~(1 << GPIO_TO_INDEX(gpio)); + break; + case IRQT_FALLING: + _set_gpio_edge_ctrl(port, GPIO_TO_INDEX(gpio), + GPIO_INT_FALL_EDGE); + set_irq_handler(irq, handle_edge_irq); + port->irq_is_level_map &= ~(1 << GPIO_TO_INDEX(gpio)); + break; + case IRQT_LOW: + _set_gpio_edge_ctrl(port, GPIO_TO_INDEX(gpio), + GPIO_INT_LOW_LEV); + set_irq_handler(irq, handle_level_irq); + port->irq_is_level_map |= 1 << GPIO_TO_INDEX(gpio); + break; + case IRQT_HIGH: + _set_gpio_edge_ctrl(port, GPIO_TO_INDEX(gpio), + GPIO_INT_HIGH_LEV); + set_irq_handler(irq, handle_level_irq); + port->irq_is_level_map |= 1 << GPIO_TO_INDEX(gpio); + break; + case IRQT_BOTHEDGE: + default: + return -EINVAL; + break; + } + return 0; +} + +/* + * Set interrupt number "irq" in the GPIO as a wake-up source. + * While system is running all registered GPIO interrupts need to have + * wake-up enabled. When system is suspended, only selected GPIO interrupts + * need to have wake-up enabled. + * @param irq interrupt source number + * @param enable enable as wake-up if equal to non-zero + * @return This function returns 0 on success. + */ +static int gpio_set_wake_irq(u32 irq, u32 enable) +{ + u32 gpio = MXC_IRQ_TO_GPIO(irq); + u32 gpio_idx = GPIO_TO_INDEX(gpio); + struct gpio_port *port = get_gpio_port(gpio); + + if (check_gpio(gpio) < 0) + return -ENODEV; + + if (enable) + port->suspend_wakeup |= (1 << gpio_idx); + else + port->suspend_wakeup &= ~(1 << gpio_idx); + return 0; +} + +static struct irq_chip gpio_irq_chip = { + .name = "MXC_GPIO", + .ack = gpio_ack_irq, + .mask = gpio_mask_irq, + .unmask = gpio_unmask_irq, + .set_type = gpio_set_irq_type, + .set_wake = gpio_set_wake_irq, +}; + +/*! + * This function initializes the GPIO hardware and disables all the + * interrupts. It registers functions for core interrupt handling code, + * for irq-chip based architectures for each interrupt source. + */ +static int __init _mxc_gpio_init(void) +{ + int i; + struct gpio_port *port; + + printk(KERN_INFO "MXC GPIO hardware\n"); + + for (i = 0; i < GPIO_PORT_NUM; i++) { + int j, gpio_count = GPIO_NUM_PIN; + + port = &gpio_port[i]; + port->base = mxc_gpio_ports[i].base; + port->num = mxc_gpio_ports[i].num; +#ifdef MXC_GPIO_SPLIT_IRQ_2 + port->irq_0_15 = mxc_gpio_ports[i].irq_0_15; + port->irq_16_31 = mxc_gpio_ports[i].irq_16_31; +#else + port->irq = mxc_gpio_ports[i].irq; +#endif + port->virtual_irq_start = mxc_gpio_ports[i].virtual_irq_start; + + port->reserved_map = 0; + spin_lock_init(&port->lock); + + /* disable the interrupt and clear the status */ + __raw_writel(0, port->base + GPIO_IMR); + __raw_writel(0xFFFFFFFF, port->base + GPIO_ISR); + for (j = port->virtual_irq_start; + j < port->virtual_irq_start + gpio_count; j++) { + set_irq_chip(j, &gpio_irq_chip); + set_irq_handler(j, handle_edge_irq); + set_irq_flags(j, IRQF_VALID); + } +#ifndef MXC_MUX_GPIO_INTERRUPTS +#ifdef MXC_GPIO_SPLIT_IRQ_2 + set_irq_chained_handler(port->irq_0_15, mxc_gpio_irq_handler); + set_irq_data(port->irq_0_15, port); + set_irq_chained_handler(port->irq_16_31, mxc_gpio_irq_handler); + set_irq_data(port->irq_16_31, port); +#else + set_irq_chained_handler(port->irq, mxc_gpio_irq_handler); + set_irq_data(port->irq, port); +#endif +#endif + } + +#ifdef MXC_MUX_GPIO_INTERRUPTS + set_irq_chained_handler(port->irq, mxc_gpio_mux_irq_handler); + set_irq_data(mxc_gpio_ports[0].irq, gpio_port); +#endif + + return 0; +} + +#ifdef CONFIG_PM +/*! + * This function puts the GPIO in low-power mode/state. + * All the interrupts that are enabled are first saved. + * Only those interrupts which registers as a wake source by calling + * enable_irq_wake are enabled. All other interrupts are disabled. + * + * @param dev the system device structure used to give information + * on GPIO to suspend + * @param mesg the power state the device is entering + * + * @return The function always returns 0. + */ +static int mxc_gpio_suspend(struct sys_device *dev, pm_message_t mesg) +{ + int i; + + for (i = 0; i < GPIO_PORT_NUM; i++) { + struct gpio_port *port = &gpio_port[i]; + u32 isr_reg; + u32 imr_reg; + + isr_reg = port->base + GPIO_ISR; + imr_reg = port->base + GPIO_IMR; + + if (__raw_readl(isr_reg) & port->suspend_wakeup) { + return -EPERM; + } + port->saved_wakeup = __raw_readl(imr_reg); + __raw_writel(port->suspend_wakeup, imr_reg); +#ifdef MXC_GPIO_SPLIT_IRQ_2 + if (port->suspend_wakeup & 0xFFFF) + enable_irq_wake(port->irq_0_15); + if (port->suspend_wakeup & 0xFFFF0000) + enable_irq_wake(port->irq_16_31); +#else + if (port->suspend_wakeup) + enable_irq_wake(port->irq); +#endif + } + + return 0; +} + +/*! + * This function brings the GPIO back from low-power state. + * All the interrupts enabled before suspension are re-enabled from + * the saved information. + * + * @param dev the system device structure used to give information + * on GPIO to resume + * + * @return The function always returns 0. + */ +static int mxc_gpio_resume(struct sys_device *dev) +{ + int i; + + for (i = 0; i < GPIO_PORT_NUM; i++) { + struct gpio_port *port = &gpio_port[i]; + u32 isr_reg; + u32 imr_reg; + + isr_reg = port->base + GPIO_ISR; + imr_reg = port->base + GPIO_IMR; + + __raw_writel(port->saved_wakeup, imr_reg); +#ifdef MXC_GPIO_SPLIT_IRQ_2 + if (port->suspend_wakeup & 0xFFFF) + disable_irq_wake(port->irq_0_15); + if (port->suspend_wakeup & 0xFFFF0000) + disable_irq_wake(port->irq_16_31); +#else + if (port->suspend_wakeup) + disable_irq_wake(port->irq); +#endif + } + + return 0; +} +#else +#define mxc_gpio_suspend NULL +#define mxc_gpio_resume NULL +#endif /* CONFIG_PM */ + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct sysdev_class mxc_gpio_sysclass = { + set_kset_name("mxc_gpio"), + .suspend = mxc_gpio_suspend, + .resume = mxc_gpio_resume, +}; + +/*! + * This structure represents GPIO as a system device. + * System devices follow a slightly different driver model. + * They don't need to do dynammic driver binding, can't be probed, + * and don't reside on any type of peripheral bus. + * So, it is represented and treated a little differently. + */ +static struct sys_device mxc_gpio_device = { + .id = 0, + .cls = &mxc_gpio_sysclass, +}; + +/*! + * This function registers GPIO hardware as a system device and + * intializes all the GPIO ports if not already done. + * System devices will only be suspended with interrupts disabled, and + * after all other devices have been suspended. On resume, they will be + * resumed before any other devices, and also with interrupts disabled. + * This may get called early from board specific init + * + * @return This function returns 0 on success. + */ +int __init mxc_gpio_init(void) +{ + int ret = 0; + + ret = _mxc_gpio_init(); + + if (ret == 0) { + ret = sysdev_class_register(&mxc_gpio_sysclass); + if (ret == 0) + ret = sysdev_register(&mxc_gpio_device); + } + + return ret; +} + +/* + * FIXME: The following functions are for backward-compatible. + * They will be removed at a future release. + */ +#define PORT_SIG_TO_GPIO(p, s) (p * 32 + s) + +/*! + * This function configures the GPIO signal to be either input or output. For + * input signals used for generating interrupts for the ARM core, how the + * interrupts being triggered is also passed in via \a icr. For output signals, + * the \a icr value doesn't matter. + * FIXED ME: for backward compatible. to be removed! + * + * @param port specified port with 0-GPIO port 1; 1-GPIO port 2 + * @param sig_no specified GPIO signal (0 based) + * @param out #true for output, #false for input + * @param icr value defined in \b #gpio_edge_t + */ +void gpio_config(u32 port, u32 sig_no, bool out, gpio_edge_t icr) +{ + u32 gpio = PORT_SIG_TO_GPIO(port, sig_no); + struct gpio_port *port_p; + + if (check_gpio(gpio) < 0) + return; + + port_p = get_gpio_port(gpio); + + if (!(port_p->reserved_map & (1 << sig_no))) + _request_gpio(port_p, sig_no); + + if (!out) { + /* input */ + _set_gpio_direction(port_p, sig_no, 1); + _set_gpio_edge_ctrl(port_p, sig_no, icr); + } else { /* output */ + _set_gpio_direction(port_p, sig_no, 0); + } +} + +/*! + * This function sets a GPIO signal value. + * FIXED ME: for backward compatible. to be removed! + * + * @param port specified port with 0 for GPIO port 1 and 1 for GPIO port 2 + * @param sig_no specified GPIO signal (0 based) + * @param data value to be set (only 0 or 1 is valid) + */ +void gpio_set_data(u32 port, u32 sig_no, u32 data) +{ + u32 gpio = PORT_SIG_TO_GPIO(port, sig_no); + struct gpio_port *port_p; + + if (check_gpio(gpio) < 0) + return; + + port_p = get_gpio_port(gpio); + + if (!(port_p->reserved_map & (1 << sig_no))) + _request_gpio(port_p, sig_no); + + spin_lock(&port_p->lock); + _set_gpio_dataout(port_p, sig_no, (data == 0) ? 0 : 1); + spin_unlock(&port_p->lock); +} + +/*! + * This function returns the value of the GPIO signal. + * FIXED ME: for backward compatible. to be removed! + * + * @param port specified port with 0 for GPIO port 1 and 1 for GPIO port 2 + * @param sig_no specified GPIO signal (0 based) + * + * @return Value of the GPIO signal + */ +u32 gpio_get_data(u32 port, u32 sig_no) +{ + u32 gpio = PORT_SIG_TO_GPIO(port, sig_no); + struct gpio_port *port_p; + + if (check_gpio(gpio) < 0) + BUG(); + + port_p = get_gpio_port(gpio); + + if (!(port_p->reserved_map & (1 << sig_no))) + _request_gpio(port_p, sig_no); + + return (__raw_readl(port_p->base + GPIO_DR) >> sig_no) & 1; +} + +/*! + * This function clears the GPIO interrupt for a signal on a port. + * FIXED ME: for backward compatible. to be removed! + * + * @param port specified port with 0 for GPIO port 1 and 1 for GPIO port 2 + * @param sig_no specified GPIO signal (0 based) to clear + */ +void gpio_clear_int(u32 port, u32 sig_no) +{ + u32 gpio = PORT_SIG_TO_GPIO(port, sig_no); + struct gpio_port *port_p; + + if (check_gpio(gpio) < 0) + return; + + port_p = get_gpio_port(gpio); + + if (!(port_p->reserved_map & (1 << sig_no))) + _request_gpio(port_p, sig_no); + + _clear_gpio_irqstatus(port_p, sig_no); +} + +/*! + * This function is responsible for registering a GPIO signal's ISR. + * FIXED ME: for backward compatible. to be removed! + * + * @param port specified port with 0 for GPIO port 1 and 1 for GPIO port 2 + * @param sig_no specified GPIO signal (0 based) + * @param prio priority as defined in \b enum \b #gpio_prio + * @param handler GPIO ISR function pointer for the GPIO signal + * @param irq_flags irq flags (not used) + * @param devname device name associated with the interrupt + * @param dev_id some unique information for the ISR + * + * @return 0 if successful; non-zero otherwise. + */ +int gpio_request_irq(u32 port, u32 sig_no, enum gpio_prio prio, + gpio_irq_handler handler, u32 irq_flags, + const char *devname, void *dev_id) +{ + u32 gpio = PORT_SIG_TO_GPIO(port, sig_no), ret; + struct gpio_port *port_p; + + if (check_gpio(gpio) < 0) + return -1; + + port_p = get_gpio_port(gpio); + + if (!(port_p->reserved_map & (1 << sig_no))) + _request_gpio(port_p, sig_no); + + ret = request_irq(MXC_GPIO_TO_IRQ(gpio), handler, 0, "gpio", dev_id); + if (ret) { + printk(KERN_ERR "gpio: IRQ%d already in use.\n", + MXC_GPIO_TO_IRQ(gpio)); + return ret; + } + return 0; +} + +/*! + * This function un-registers an ISR with the GPIO interrupt module. + * FIXED ME: for backward compatible. to be removed! + * + * @param port specified port with 0 for GPIO port 1 and 1 for GPIO port 2 + * @param sig_no specified GPIO signal (0 based) + * @param prio priority as defined in \b enum \b #gpio_prio + */ +void gpio_free_irq(u32 port, u32 sig_no, enum gpio_prio prio) +{ + u32 gpio = PORT_SIG_TO_GPIO(port, sig_no); + struct gpio_port *port_p; + + if (check_gpio(gpio) < 0) + return; + + port_p = get_gpio_port(gpio); + + free_irq(MXC_GPIO_TO_IRQ(gpio), NULL); + + spin_lock(&port_p->lock); + if ((!(port_p->reserved_map & (1 << sig_no)))) { + printk(KERN_ERR "GPIO port %d, pin %d wasn't reserved!\n", + port_p->num, sig_no); + dump_stack(); + spin_unlock(&port_p->lock); + return; + } + port_p->reserved_map &= ~(1 << sig_no); + _set_gpio_direction(port_p, sig_no, 1); + _set_gpio_irqenable(port_p, sig_no, 0); + _clear_gpio_irqstatus(port_p, sig_no); + spin_unlock(&port_p->lock); +} + +EXPORT_SYMBOL(mxc_request_gpio); +EXPORT_SYMBOL(mxc_free_gpio); +EXPORT_SYMBOL(mxc_set_gpio_direction); +EXPORT_SYMBOL(mxc_set_gpio_dataout); +EXPORT_SYMBOL(mxc_get_gpio_datain); + +/* For backward compatible */ +EXPORT_SYMBOL(gpio_config); +EXPORT_SYMBOL(gpio_set_data); +EXPORT_SYMBOL(gpio_get_data); +EXPORT_SYMBOL(gpio_clear_int); +EXPORT_SYMBOL(gpio_request_irq); +EXPORT_SYMBOL(gpio_free_irq); diff --git a/arch/arm/plat-mxc/io.c b/arch/arm/plat-mxc/io.c new file mode 100644 index 000000000000..ee42c048d72d --- /dev/null +++ b/arch/arm/plat-mxc/io.c @@ -0,0 +1,41 @@ +/* + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * mxc custom ioremap implementation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/hardware.h> +#include <asm/io.h> + +void *__iomem __mxc_ioremap(unsigned long cookie, size_t size, + unsigned int mtype) +{ + if (mtype == MT_DEVICE && IS_MEM_DEVICE_NONSHARED(cookie)) { + mtype = MT_DEVICE_NONSHARED; + } + return __arm_ioremap(cookie, size, mtype); +} + +EXPORT_SYMBOL(__mxc_ioremap); + +void __mxc_iounmap(void __iomem * addr) +{ + extern void __iounmap(volatile void __iomem * addr); + + __iounmap(addr); +} + +EXPORT_SYMBOL(__mxc_iounmap); diff --git a/arch/arm/plat-mxc/irq.c b/arch/arm/plat-mxc/irq.c index 87d253bc3d3c..13a5cc1fd95e 100644 --- a/arch/arm/plat-mxc/irq.c +++ b/arch/arm/plat-mxc/irq.c @@ -12,6 +12,7 @@ #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/device.h> +#include <linux/sysdev.h> #include <linux/errno.h> #include <asm/hardware.h> #include <asm/io.h> @@ -20,6 +21,111 @@ #include <asm/arch/common.h> /*! + * @file plat-mxc/irq.c + * + * @brief This file contains the AVIC implementation details. + * + * @ingroup Interrupt + */ + +#define IRQ_BIT(irq) (1 << (irq)) + +static uint32_t saved_wakeup_low, saved_wakeup_high; +static uint32_t suspend_wakeup_low, suspend_wakeup_high; + +/* + ***************************************** + * EDIO Registers * + ***************************************** + */ +#ifdef EDIO_BASE_ADDR + +static const int mxc_edio_irq_map[] = { + INT_EXT_INT0, + INT_EXT_INT1, + INT_EXT_INT2, + INT_EXT_INT3, + INT_EXT_INT4, + INT_EXT_INT5, + INT_EXT_INT6, + INT_EXT_INT7, +}; + +static u32 edio_irq_type[MXC_MAX_EXT_LINES] = { + IRQT_LOW, + IRQT_LOW, + IRQT_LOW, + IRQT_LOW, + IRQT_LOW, + IRQT_LOW, + IRQT_LOW, + IRQT_LOW, +}; + +static int irq_to_edio(int irq) +{ + int i; + int edio = -1; + + for (i = 0; i < MXC_MAX_EXT_LINES; i++) { + if (mxc_edio_irq_map[i] == irq) { + edio = i; + break; + } + } + return edio; +} + +static void mxc_irq_set_edio_level(int irq, int trigger) +{ + int edio; + unsigned short rval; + + edio = irq_to_edio(irq); + rval = __raw_readw(EDIO_EPPAR); + rval = (rval & (~(0x3 << (edio * 2)))) | (trigger << (edio * 2)); + __raw_writew(rval, EDIO_EPPAR); +} + +static void mxc_irq_set_edio_dir(int irq, int dir) +{ + int edio; + unsigned short rval; + + edio = irq_to_edio(irq); + + rval = __raw_readw(EDIO_EPDR); + rval &= ~(1 << edio); + rval |= (0 << edio); + __raw_writew(rval, (EDIO_EPDR)); + + /* set direction */ + rval = __raw_readw(EDIO_EPDDR); + + if (dir) + /* out */ + rval |= (1 << edio); + else + /* in */ + rval &= ~(1 << edio); + + __raw_writew(rval, EDIO_EPDDR); + +} + +/* + * Allows tuning the IRQ type , trigger and priority + */ +static void mxc_irq_set_edio(int irq, int fiq, int priority, int trigger) +{ + + mxc_irq_set_edio_dir(irq, 0); + /* set level */ + mxc_irq_set_edio_level(irq, trigger); +} +#endif + +/*! * Disable interrupt number "irq" in the AVIC * * @param irq interrupt source number @@ -39,12 +145,190 @@ static void mxc_unmask_irq(unsigned int irq) __raw_writel(irq, AVIC_INTENNUM); } +/*! + * Set interrupt number "irq" in the AVIC as a wake-up source. + * + * @param irq interrupt source number + * @param enable enable as wake-up if equal to non-zero + * disble as wake-up if equal to zero + * + * @return This function returns 0 on success. + */ +static int mxc_set_wake_irq(unsigned int irq, unsigned int enable) +{ + uint32_t *wakeup_intr; + uint32_t irq_bit; + + if (irq < 32) { + wakeup_intr = &suspend_wakeup_low; + irq_bit = IRQ_BIT(irq); + } else { + wakeup_intr = &suspend_wakeup_high; + irq_bit = IRQ_BIT(irq - 32); + } + + if (enable) { + *wakeup_intr |= irq_bit; + } else { + *wakeup_intr &= ~irq_bit; + } + + return 0; +} + static struct irq_chip mxc_avic_chip = { .mask_ack = mxc_mask_irq, .mask = mxc_mask_irq, .unmask = mxc_unmask_irq, + .set_wake = mxc_set_wake_irq, }; +#ifdef EDIO_BASE_ADDR +static void mxc_ack_edio(u32 irq) +{ + u16 edio = (u16) irq_to_edio(irq); + if (edio_irq_type[edio] == IRQT_LOW) { + /* Mask interrupt for level sensitive */ + mxc_mask_irq(irq); + } else if (edio_irq_type[edio] == IRQT_HIGH) { + /* clear edio interrupt */ + __raw_writew((1 << edio), EDIO_EPFR); + /* dummy read for edio workaround */ + __raw_readw(EDIO_EPFR); + /* Mask interrupt for level sensitive */ + mxc_mask_irq(irq); + } else { + /* clear edio interrupt */ + __raw_writew((1 << edio), EDIO_EPFR); + /* dummy read for edio workaround */ + __raw_readw(EDIO_EPFR); + } +} + +static int mxc_edio_set_type(u32 irq, u32 type) +{ + edio_irq_type[irq_to_edio(irq)] = type; + + switch (type) { + case IRQT_RISING: + mxc_irq_set_edio_level(irq, 1); + set_irq_handler(irq, handle_edge_irq); + break; + case IRQT_FALLING: + mxc_irq_set_edio_level(irq, 2); + set_irq_handler(irq, handle_edge_irq); + break; + case IRQT_BOTHEDGE: + mxc_irq_set_edio_level(irq, 3); + set_irq_handler(irq, handle_edge_irq); + break; + case IRQT_LOW: + mxc_irq_set_edio_level(irq, 0); + set_irq_handler(irq, handle_level_irq); + break; + case IRQT_HIGH: + /* EDIO doesn't really support high-level interrupts, + * so we're faking it + */ + mxc_irq_set_edio_level(irq, 1); + set_irq_handler(irq, handle_level_irq); + break; + default: + return -EINVAL; + break; + } + return 0; +} + +static struct irq_chip mxc_edio_chip = { + .name = "MXC_EDIO", + .ack = mxc_ack_edio, + .mask = mxc_mask_irq, + .unmask = mxc_unmask_irq, + .set_type = mxc_edio_set_type, + .set_wake = mxc_set_wake_irq, +}; + +#endif + +#ifdef CONFIG_PM +/*! + * This function puts the AVIC in low-power mode/state. + * All the interrupts that are enabled are first saved. + * Only those interrupts which registers as a wake source by calling + * enable_irq_wake are enabled. All other interrupts are disabled. + * + * @param dev the system device structure used to give information + * on AVIC to suspend + * @param mesg the power state the device is entering + * + * @return The function always returns 0. + */ +static int mxc_avic_suspend(struct sys_device *dev, pm_message_t mesg) +{ + saved_wakeup_high = __raw_readl(AVIC_INTENABLEH); + saved_wakeup_low = __raw_readl(AVIC_INTENABLEL); + + __raw_writel(suspend_wakeup_high, AVIC_INTENABLEH); + __raw_writel(suspend_wakeup_low, AVIC_INTENABLEL); + + return 0; +} + +/*! + * This function brings the AVIC back from low-power state. + * All the interrupts enabled before suspension are re-enabled from + * the saved information. + * + * @param dev the system device structure used to give information + * on AVIC to resume + * + * @return The function always returns 0. + */ +static int mxc_avic_resume(struct sys_device *dev) +{ + __raw_writel(saved_wakeup_high, AVIC_INTENABLEH); + __raw_writel(saved_wakeup_low, AVIC_INTENABLEL); + + return 0; +} + +#else +#define mxc_avic_suspend NULL +#define mxc_avic_resume NULL +#endif /* CONFIG_PM */ + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct sysdev_class mxc_avic_sysclass = { + set_kset_name("mxc_irq"), + .suspend = mxc_avic_suspend, + .resume = mxc_avic_resume, +}; + +/*! + * This structure represents AVIC as a system device. + * System devices follow a slightly different driver model. + * They don't need to do dynammic driver binding, can't be probed, + * and don't reside on any type of peripheral bus. + * So, it is represented and treated a little differently. + */ +static struct sys_device mxc_avic_device = { + .id = 0, + .cls = &mxc_avic_sysclass, +}; + +/* + * This function is used to get the AVIC Lo and Hi interrupts + * that are enabled as wake up sources to wake up the core from suspend + */ +void mxc_get_wake_irq(u32 * wake_src[]) +{ + *wake_src[0] = __raw_readl(AVIC_INTENABLEL); + *wake_src[1] = __raw_readl(AVIC_INTENABLEH); +} + /*! * This function initializes the AVIC hardware and disables all the * interrupts. It registers the interrupt enable and disable functions @@ -69,7 +353,15 @@ void __init mxc_init_irq(void) __raw_writel(0, AVIC_INTTYPEH); __raw_writel(0, AVIC_INTTYPEL); for (i = 0; i < MXC_MAX_INT_LINES; i++) { - set_irq_chip(i, &mxc_avic_chip); +#ifdef EDIO_BASE_ADDR + if (irq_to_edio(i) != -1) { + mxc_irq_set_edio(i, 0, 0, 0); + set_irq_chip(i, &mxc_edio_chip); + } else +#endif + { + set_irq_chip(i, &mxc_avic_chip); + } set_irq_handler(i, handle_level_irq); set_irq_flags(i, IRQF_VALID); } @@ -81,3 +373,25 @@ void __init mxc_init_irq(void) printk(KERN_INFO "MXC IRQ initialized\n"); } + +/*! + * This function registers AVIC hardware as a system device. + * System devices will only be suspended with interrupts disabled, and + * after all other devices have been suspended. On resume, they will be + * resumed before any other devices, and also with interrupts disabled. + * + * @return This function returns 0 on success. + */ +static int __init mxc_avic_sysinit(void) +{ + int ret = 0; + + ret = sysdev_class_register(&mxc_avic_sysclass); + if (ret == 0) { + ret = sysdev_register(&mxc_avic_device); + } + + return ret; +} + +arch_initcall(mxc_avic_sysinit); diff --git a/arch/arm/plat-mxc/isp1301xc.c b/arch/arm/plat-mxc/isp1301xc.c new file mode 100644 index 000000000000..e27e73d7f439 --- /dev/null +++ b/arch/arm/plat-mxc/isp1301xc.c @@ -0,0 +1,275 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/usb/fsl_xcvr.h> +#include <linux/i2c.h> + +#include <asm/arch/arc_otg.h> + +/* + * ISP1301 register addresses,all register of ISP1301 + * is one-byte length register + */ + +/* ISP1301: I2C device address */ +#define ISP1301_DEV_ADDR 0x2D + +/* ISP 1301 register set*/ +#define ISP1301_MODE_REG1_SET 0x04 +#define ISP1301_MODE_REG1_CLR 0x05 + +#define ISP1301_CTRL_REG1_SET 0x06 +#define ISP1301_CTRL_REG1_CLR 0x07 + +#define ISP1301_INT_SRC_REG 0x08 +#define ISP1301_INT_LAT_REG_SET 0x0a +#define ISP1301_INT_LAT_REG_CLR 0x0b +#define ISP1301_INT_FALSE_REG_SET 0x0c +#define ISP1301_INT_FALSE_REG_CLR 0x0d +#define ISP1301_INT_TRUE_REG_SET 0x0e +#define ISP1301_INT_TRUE_REG_CLR 0x0f + +#define ISP1301_CTRL_REG2_SET 0x10 +#define ISP1301_CTRL_REG2_CLR 0x11 + +#define ISP1301_MODE_REG2_SET 0x12 +#define ISP1301_MODE_REG2_CLR 0x13 + +#define ISP1301_BCD_DEV_REG0 0x14 +#define ISP1301_BCD_DEV_REG1 0x15 + +/* OTG Control register bit description */ +#define DP_PULLUP 0x01 +#define DM_PULLUP 0x02 +#define DP_PULLDOWN 0x04 +#define DM_PULLDOWN 0x08 +#define ID_PULLDOWN 0x10 +#define VBUS_DRV 0x20 +#define VBUS_DISCHRG 0x40 +#define VBUS_CHRG 0x80 + +/* Mode Control 1 register bit description */ +#define SPEED_REG 0x01 +#define SUSPEND_REG 0x02 +#define DAT_SE0 0x04 +#define TRANSP_EN 0x08 +#define BDIS_ACON_EN 0x10 +#define OE_INT_EN 0x20 +#define UART_EN 0x40 + +/* Mode Control 2 register bit description */ +#define SPD_SUSP_CTRL 0x02 +#define BI_DI 0x04 + +static int isp1301_attach(struct i2c_adapter *adapter); +static int isp1301_detach(struct i2c_client *client); + +static struct i2c_driver isp1301_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "isp1301 Client", + }, + .attach_adapter = isp1301_attach, + .detach_client = isp1301_detach, +}; + +static struct i2c_client isp1301_i2c_client = { + .name = "isp1301 I2C dev", + .addr = ISP1301_DEV_ADDR, + .driver = &isp1301_i2c_driver, +}; + +static unsigned short normal_i2c[] = { ISP1301_DEV_ADDR, I2C_CLIENT_END }; + +/* Magic definition of all other variables and things */ +I2C_CLIENT_INSMOD; + +static int isp1301_detect_client(struct i2c_adapter *adapter, int address, + int kind) +{ + isp1301_i2c_client.adapter = adapter; + if (i2c_attach_client(&isp1301_i2c_client)) { + isp1301_i2c_client.adapter = NULL; + printk(KERN_ERR "isp1301_attach: i2c_attach_client failed\n"); + return -1; + } + + printk(KERN_INFO "isp1301 Detected\n"); + return 0; +} + +/*! + * isp1301 I2C attach function + * + * @param adapter struct i2c_adapter * + * @return Error code indicating success or failure + */ +static int isp1301_attach(struct i2c_adapter *adapter) +{ + return i2c_probe(adapter, &addr_data, &isp1301_detect_client); +} + +/*! + * isp1301 I2C detach function + * + * @param client struct i2c_client * + * @return Error code indicating success or failure + */ +static int isp1301_detach(struct i2c_client *client) +{ + int err; + + if (!isp1301_i2c_client.adapter) + return -1; + + err = i2c_detach_client(&isp1301_i2c_client); + isp1301_i2c_client.adapter = NULL; + + return err; +} + +static void isp1301_init(struct fsl_xcvr_ops *this) +{ + pr_debug("%s\n", __FUNCTION__); + + i2c_add_driver(&isp1301_i2c_driver); +} + +static void isp1301_uninit(struct fsl_xcvr_ops *this) +{ + // DDD do this for host only: + /* disable OTG VBUS */ + i2c_del_driver(&isp1301_i2c_driver); +} + +/* Write ISP1301 register*/ +static inline void isp1301_write_reg(char reg, char data) +{ + i2c_smbus_write_byte_data(&isp1301_i2c_client, reg, data); +} + +/* read ISP1301 register*/ +static inline char isp1301_read_reg(char reg) +{ + return i2c_smbus_read_byte_data(&isp1301_i2c_client, reg); +} + +/* set ISP1301 as USB host*/ +static inline void isp1301_set_serial_host(void) +{ + pr_debug("%s\n", __FUNCTION__); + + isp1301_write_reg(ISP1301_MODE_REG2_CLR, 0xFF); +#if defined(CONFIG_MXC_USB_SB3) || defined(CONFIG_MXC_USB_DB4) + isp1301_write_reg(ISP1301_MODE_REG2_SET, SPD_SUSP_CTRL | BI_DI); +#else + isp1301_write_reg(ISP1301_MODE_REG2_SET, SPD_SUSP_CTRL); +#endif + + isp1301_write_reg(ISP1301_MODE_REG1_CLR, 0xFF); +#if defined(CONFIG_MXC_USB_SB3) || defined(CONFIG_MXC_USB_SU6) + isp1301_write_reg(ISP1301_MODE_REG1_SET, DAT_SE0 | SPEED_REG); +#else + isp1301_write_reg(ISP1301_MODE_REG1_SET, SPEED_REG); +#endif + + /* configure transceiver for host mode */ + isp1301_write_reg(ISP1301_CTRL_REG1_SET, + (VBUS_DRV | DP_PULLDOWN | DM_PULLDOWN)); +} + +/* set ISP1301 as USB device*/ +static inline void isp1301_set_serial_dev(void) +{ + pr_debug("%s\n", __FUNCTION__); + + isp1301_write_reg(ISP1301_MODE_REG2_CLR, 0xFF); +#if defined(CONFIG_MXC_USB_SB3) || defined(CONFIG_MXC_USB_DB4) + isp1301_write_reg(ISP1301_MODE_REG2_SET, SPD_SUSP_CTRL | BI_DI); +#else + isp1301_write_reg(ISP1301_MODE_REG2_SET, SPD_SUSP_CTRL); +#endif + + isp1301_write_reg(ISP1301_MODE_REG1_CLR, 0xFF); +#if defined(CONFIG_MXC_USB_SB3) || defined(CONFIG_MXC_USB_SU6) + isp1301_write_reg(ISP1301_MODE_REG1_SET, DAT_SE0 | SPEED_REG); +#else + isp1301_write_reg(ISP1301_MODE_REG1_SET, SPEED_REG); +#endif + + /* FS mode, DP pull down, DM pull down */ + isp1301_write_reg(ISP1301_CTRL_REG1_SET, + (DP_PULLDOWN | DM_PULLDOWN | DP_PULLUP)); +} + +static void isp1301_set_vbus_power(u32 * view, int on) +{ + pr_debug("%s(on=%d)\n", __FUNCTION__, on); + if (on) { + /* disable D+ pull-up */ + isp1301_write_reg(ISP1301_CTRL_REG1_CLR, DP_PULLUP); + /* enable D+ pull-down */ + isp1301_write_reg(ISP1301_CTRL_REG1_SET, DP_PULLDOWN); + /* turn on Vbus */ + isp1301_write_reg(ISP1301_CTRL_REG1_SET, VBUS_DRV); + } else { + /* D+ pull up, D- pull down */ + isp1301_write_reg(ISP1301_CTRL_REG1_SET, + (DP_PULLUP | DM_PULLDOWN)); + /* disable D- pull up, disable D+ pull down */ + isp1301_write_reg(ISP1301_CTRL_REG1_CLR, + (DM_PULLUP | DP_PULLDOWN)); + } +} + +static struct fsl_xcvr_ops isp1301_ops_otg = { + .name = "isp1301", + .xcvr_type = PORTSC_PTS_SERIAL, + .init = isp1301_init, + .uninit = isp1301_uninit, + .set_host = isp1301_set_serial_host, + .set_device = isp1301_set_serial_dev, + .set_vbus_power = isp1301_set_vbus_power, +}; + +extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops); + +static int __init isp1301xc_init(void) +{ + pr_debug("%s\n", __FUNCTION__); + + fsl_usb_xcvr_register(&isp1301_ops_otg); + + return 0; +} + +extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops); + +static void __exit isp1301xc_exit(void) +{ + fsl_usb_xcvr_unregister(&isp1301_ops_otg); +} + +module_init(isp1301xc_init); +module_exit(isp1301xc_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("isp1301"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-mxc/isp1504xc.c b/arch/arm/plat-mxc/isp1504xc.c new file mode 100644 index 000000000000..bd975824aee4 --- /dev/null +++ b/arch/arm/plat-mxc/isp1504xc.c @@ -0,0 +1,236 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/usb/fsl_xcvr.h> + +#include <asm/hardware.h> +#include <asm/arch/arc_otg.h> + +/* ISP 1504 register addresses */ +#define ISP1504_VID_LOW 0x00 /* Vendor ID low */ +#define ISP1504_VID_HIGH 0x01 /* Vendor ID high */ +#define ISP1504_PID_LOW 0x02 /* Product ID low */ +#define ISP1504_PID_HIGH 0x03 /* Product ID high */ +#define ISP1504_ITFCTL 0x07 /* Interface Control */ +#define ISP1504_OTGCTL 0x0A /* OTG Control */ + +/* add to above register address to access Set/Clear functions */ +#define ISP1504_REG_SET 0x01 +#define ISP1504_REG_CLEAR 0x02 + +/* 1504 OTG Control Register bits */ +#define USE_EXT_VBUS_IND (1 << 7) /* Use ext. Vbus indicator */ +#define DRV_VBUS_EXT (1 << 6) /* Drive Vbus external */ +#define DRV_VBUS (1 << 5) /* Drive Vbus */ +#define CHRG_VBUS (1 << 4) /* Charge Vbus */ +#define DISCHRG_VBUS (1 << 3) /* Discharge Vbus */ +#define DM_PULL_DOWN (1 << 2) /* enable DM Pull Down */ +#define DP_PULL_DOWN (1 << 1) /* enable DP Pull Down */ +#define ID_PULL_UP (1 << 0) /* enable ID Pull Up */ + +/*! + * read ULPI register 'reg' thru VIEWPORT register 'view' + * + * @param reg register to read + * @param view the ULPI VIEWPORT register address + * @return return isp1504 register value + */ +static u8 isp1504_read(int reg, volatile u32 * view) +{ + u32 data; + + /* make sure interface is running */ + if (!(__raw_readl(view) && ULPIVW_SS)) { + __raw_writel(ULPIVW_WU, view); + do { /* wait for wakeup */ + data = __raw_readl(view); + } while (data & ULPIVW_WU); + } + + /* read the register */ + __raw_writel((ULPIVW_RUN | (reg << ULPIVW_ADDR_SHIFT)), view); + + do { /* wait for completion */ + data = __raw_readl(view); + } while (data & ULPIVW_RUN); + + return (u8) (data >> ULPIVW_RDATA_SHIFT) & ULPIVW_RDATA_MASK; +} + +/*! + * set bits into OTG ISP1504 register 'reg' thru VIEWPORT register 'view' + * + * @param bits set value + * @param reg which register + * @param view the ULPI VIEWPORT register address + */ +static void isp1504_set(u8 bits, int reg, volatile u32 * view) +{ + u32 data; + + /* make sure interface is running */ + if (!(__raw_readl(view) && ULPIVW_SS)) { + __raw_writel(ULPIVW_WU, view); + do { /* wait for wakeup */ + data = __raw_readl(view); + } while (data & ULPIVW_WU); + } + + __raw_writel((ULPIVW_RUN | ULPIVW_WRITE | + ((reg + ISP1504_REG_SET) << ULPIVW_ADDR_SHIFT) | + ((bits & ULPIVW_WDATA_MASK) << ULPIVW_WDATA_SHIFT)), + view); + + while (__raw_readl(view) & ULPIVW_RUN) /* wait for completion */ + continue; +} + +/*! + * clear bits in OTG ISP1504 register 'reg' thru VIEWPORT register 'view' + * + * @param bits bits to clear + * @param reg in this register + * @param view the ULPI VIEWPORT register address + */ +static void isp1504_clear(u8 bits, int reg, volatile u32 * view) +{ + __raw_writel((ULPIVW_RUN | ULPIVW_WRITE | + ((reg + ISP1504_REG_CLEAR) << ULPIVW_ADDR_SHIFT) | + ((bits & ULPIVW_WDATA_MASK) << ULPIVW_WDATA_SHIFT)), + view); + + while (__raw_readl(view) & ULPIVW_RUN) /* wait for completion */ + continue; +} + +extern int gpio_usbotg_hs_active(void); + +static void isp1508_fix(u32 * view) +{ + gpio_usbotg_hs_active(); + + /* Set bits IND_PASS_THRU and IND_COMPL */ + isp1504_set(0x60, ISP1504_ITFCTL, view); + + /* Set bit USE_EXT_VBUS_IND */ + isp1504_set(USE_EXT_VBUS_IND, ISP1504_OTGCTL, view); +} + +/*! + * set vbus power + * + * @param view viewport register + * @param on power on or off + */ +static void isp1504_set_vbus_power(u32 * view, int on) +{ + pr_debug("real %s(on=%d) view=0x%p\n", __FUNCTION__, on, view); + + pr_debug("ULPI Vendor ID 0x%x Product ID 0x%x\n", + (isp1504_read(ISP1504_VID_HIGH, view) << 8) | + isp1504_read(ISP1504_VID_LOW, view), + (isp1504_read(ISP1504_PID_HIGH, view) << 8) | + isp1504_read(ISP1504_PID_LOW, view)); + + pr_debug("OTG Control before=0x%x\n", + isp1504_read(ISP1504_OTGCTL, view)); + + if (on) { + isp1504_set(DRV_VBUS_EXT | /* enable external Vbus */ + DRV_VBUS | /* enable internal Vbus */ + USE_EXT_VBUS_IND | /* use external indicator */ + CHRG_VBUS, /* charge Vbus */ + ISP1504_OTGCTL, view); + + } else { + isp1508_fix(view); + + isp1504_clear(DRV_VBUS_EXT | /* disable external Vbus */ + DRV_VBUS, /* disable internal Vbus */ + ISP1504_OTGCTL, view); + + isp1504_set(USE_EXT_VBUS_IND | /* use external indicator */ + DISCHRG_VBUS, /* discharge Vbus */ + ISP1504_OTGCTL, view); + } + + pr_debug("OTG Control after = 0x%x\n", + isp1504_read(ISP1504_OTGCTL, view)); +} + +/*! + * set remote wakeup + * + * @param view viewport register + */ +static void isp1504_set_remote_wakeup(u32 * view) +{ + __raw_writel(~ULPIVW_WRITE & __raw_readl(view), view); + __raw_writel((1 << ULPIVW_PORT_SHIFT) | __raw_readl(view), view); + __raw_writel(ULPIVW_RUN | __raw_readl(view), view); + + while (__raw_readl(view) & ULPIVW_RUN) /* wait for completion */ + continue; +} + +static void isp1504_init(struct fsl_xcvr_ops *this) +{ + pr_debug("%s:\n", __FUNCTION__); +} + +static void isp1504_uninit(struct fsl_xcvr_ops *this) +{ + pr_debug("%s:\n", __FUNCTION__); +} + +static struct fsl_xcvr_ops isp1504_ops = { + .name = "isp1504", + .xcvr_type = PORTSC_PTS_ULPI, + .init = isp1504_init, + .uninit = isp1504_uninit, + .set_host = NULL, + .set_device = NULL, + .set_vbus_power = isp1504_set_vbus_power, + .set_remote_wakeup = isp1504_set_remote_wakeup, +}; + +extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops); + +static int __init isp1504xc_init(void) +{ + pr_debug("%s\n", __FUNCTION__); + + fsl_usb_xcvr_register(&isp1504_ops); + + return 0; +} + +extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops); + +static void __exit isp1504xc_exit(void) +{ + fsl_usb_xcvr_unregister(&isp1504_ops); +} + +module_init(isp1504xc_init); +module_exit(isp1504xc_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("isp1504 xcvr driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-mxc/leds.c b/arch/arm/plat-mxc/leds.c new file mode 100644 index 000000000000..09d1ee3f4459 --- /dev/null +++ b/arch/arm/plat-mxc/leds.c @@ -0,0 +1,111 @@ +/* + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * The LED can be used for debugging purpose. To enalbe the LEDs, in the + * config file, select: + * CONFIG_LEDS + * CONFIG_LEDS_TIMER --- enable the OS tick LED once every 50 ticks (.5sec) + * CONFIG_LEDS_CPU --- enable the cpu idle in/out LED (blink fast) + * + * The two LEDs can be disabled through user space by issuing: + * echo "claim" > /sys/devices/system/leds/leds0/event + * To release the LEDs back to the normal operation, do: + * echo "release" > /sys/devices/system/leds/leds0/event + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/ioport.h> +#include <asm/mach-types.h> +#include <asm/hardware.h> +#include <asm/leds.h> + +#define LED_STATE_ENABLED (1 << 0) +#define LED_STATE_CLAIMED (1 << 1) + +static unsigned int led_state; +static unsigned int hw_led_state; + +static void mxc_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch (evt) { + case led_start: + hw_led_state = MXC_BD_LED1 | MXC_BD_LED2; + led_state = LED_STATE_ENABLED; + break; + + case led_stop: + case led_halted: + hw_led_state = 0; + led_state &= ~LED_STATE_ENABLED; + MXC_BD_LED_OFF(MXC_BD_LED1 | MXC_BD_LED2); + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = 0; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = 0; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= MXC_BD_LED1; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~MXC_BD_LED2; + break; + + case led_idle_end: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= MXC_BD_LED2; + break; +#endif + + default: + break; + } + + if (led_state & LED_STATE_ENABLED) { + MXC_BD_LED_OFF(~hw_led_state); + MXC_BD_LED_ON(hw_led_state); + } + + local_irq_restore(flags); +} + +static int __init mxc_leds_init(void) +{ + led_state = LED_STATE_ENABLED; + leds_event = mxc_leds_event; + return 0; +} + +core_initcall(mxc_leds_init); diff --git a/arch/arm/plat-mxc/mc13783_xc.c b/arch/arm/plat-mxc/mc13783_xc.c new file mode 100644 index 000000000000..e230e8b936bb --- /dev/null +++ b/arch/arm/plat-mxc/mc13783_xc.c @@ -0,0 +1,319 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/kernel.h> +#include <asm/arch/hardware.h> +#include <asm/arch/pmic_external.h> +#include <asm/arch/pmic_convity.h> +#include <asm/arch/arc_otg.h> +#include <linux/platform_device.h> +#include <linux/usb/fsl_xcvr.h> +#include <linux/delay.h> +#include <linux/kthread.h> + +#define TRUE 1 +#define FALSE 0 + +/* Events to be passed to the thread */ +#define MC13783_USB_VBUS_ON 0x0001 +#define MC13783_USB_VBUS_OFF 0x0002 +#define MC13783_USB_DETECT_MINI_A 0x0004 +#define MC13783_USB_DETECT_MINI_B 0x0008 + +static inline void mc13783_set_host(void); +static inline void mc13783_set_peripheral(void); + +static int pmic_event_thread(void *arg); + +extern void otg_set_serial_peripheral(void); +extern void otg_set_serial_host(void); + +static unsigned int p_event; +PMIC_CONVITY_EVENTS g_event; +PMIC_CONVITY_HANDLE pmic_handle = (PMIC_CONVITY_HANDLE) NULL; + +DECLARE_MUTEX_LOCKED(pmic_mx); +static struct task_struct *pmic_task; + +static void pmic_event_handler(const PMIC_CONVITY_EVENTS event) +{ + if (event & USB_DETECT_4V4_RISE) { + pr_debug("%s: USB_DETECT_4V4_RISE\n", __FUNCTION__); + } + if (event & USB_DETECT_4V4_FALL) { + pr_debug("%s: USB_DETECT_4V4_FALL\n", __FUNCTION__); + } + if (event & USB_DETECT_2V0_RISE) { + pr_debug("%s: USB_DETECT_2V0_RISE\n", __FUNCTION__); + } + if (event & USB_DETECT_2V0_FALL) { + pr_debug("%s: USB_DETECT_2V0_FALL\n", __FUNCTION__); + } + if (event & USB_DETECT_0V8_RISE) { + pr_debug("%s: USB_DETECT_0V8_RISE\n", __FUNCTION__); + } + if (event & USB_DETECT_0V8_FALL) { + pr_debug("%s: USB_DETECT_0V8_FALL\n", __FUNCTION__); + } + if (event & USB_DETECT_MINI_B) { + pr_debug("%s: USB_DETECT_MINI_B\n", __FUNCTION__); + otg_set_serial_peripheral(); + g_event = USB_DETECT_MINI_B; + p_event = MC13783_USB_DETECT_MINI_B; + up(&pmic_mx); + } + if (event & USB_DETECT_MINI_A) { + pr_debug("%s: USB_DETECT_MINI_A\n", __FUNCTION__); + otg_set_serial_host(); + g_event = USB_DETECT_MINI_A; + p_event = MC13783_USB_DETECT_MINI_A; + up(&pmic_mx); + } + + /* + * Mini-B cable insertion/removal does not generate cable-detect + * event, so we rely on the VBUS changes to identify a mini-b cable + * connect. This logic is only used if mini-b is the first cable that + * is connected after bootup. At all other times, removal of mini-a + * cable is used to initialize peripheral. + */ + if (g_event != USB_DETECT_MINI_A && g_event != USB_DETECT_MINI_B) { + if ((event & USB_DETECT_0V8_RISE) && + (event & USB_DETECT_2V0_RISE) && + (event & USB_DETECT_4V4_RISE)) { + otg_set_serial_peripheral(); + g_event = USB_DETECT_MINI_B; + p_event = MC13783_USB_DETECT_MINI_B; + up(&pmic_mx); + } + } +} + +static int usb_pmic_mod_init(void) +{ + PMIC_STATUS rs = PMIC_ERROR; + + rs = pmic_convity_open(&pmic_handle, USB); + if (rs != PMIC_SUCCESS) { + printk(KERN_ERR "pmic_convity_open returned error %d\n", rs); + return rs; + } + + rs = pmic_convity_set_callback(pmic_handle, pmic_event_handler, + USB_DETECT_4V4_RISE | USB_DETECT_4V4_FALL + | USB_DETECT_2V0_RISE | + USB_DETECT_2V0_FALL | USB_DETECT_0V8_RISE + | USB_DETECT_0V8_FALL | USB_DETECT_MINI_A + | USB_DETECT_MINI_B); + + if (rs != PMIC_SUCCESS) { + printk(KERN_ERR + "pmic_convity_set_callback returned error %d\n", rs); + return rs; + } + + return rs; +} + +static void usb_pmic_mod_exit(void) +{ + PMIC_STATUS rs; + + pmic_convity_set_mode(pmic_handle, RS232_1); + pmic_convity_clear_callback(pmic_handle); + + if (pmic_handle != (PMIC_CONVITY_HANDLE) NULL) { + rs = pmic_convity_close(pmic_handle); + if (rs != PMIC_SUCCESS) { + printk(KERN_ERR + "pmic_convity_close() returned error %d", rs); + } else { + pmic_handle = (PMIC_CONVITY_HANDLE) NULL; + } + } +} + +static inline void mc13783_set_host(void) +{ + PMIC_STATUS rs = PMIC_ERROR; + + rs = pmic_convity_usb_otg_clear_config(pmic_handle, USB_OTG_SE0CONN); + rs |= pmic_convity_usb_otg_clear_config(pmic_handle, USB_PU); + + rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_UDM_PD); + rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_UDP_PD); + + if (rs != PMIC_SUCCESS) { + printk(KERN_ERR "mc13783_set_host failed\n"); + } +} + +static inline void mc13783_set_peripheral(void) +{ + PMIC_STATUS rs = PMIC_ERROR; + + rs = pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDM_PD); + rs |= pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDP_PD); + + rs |= pmic_convity_usb_set_speed(pmic_handle, USB_FULL_SPEED); + rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_OTG_SE0CONN); + rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_PU); + + if (rs != PMIC_SUCCESS) { + printk(KERN_ERR "mc13783_set_peripheral failed\n"); + } +} + +void mc13783_set_vbus_power(u32 * view, int on) +{ + if (on) { + p_event = MC13783_USB_VBUS_ON; + up(&pmic_mx); + } +} + +static struct fsl_xcvr_ops mc13783_ops_otg = { + .name = "mc13783", + .xcvr_type = PORTSC_PTS_SERIAL, + .set_host = mc13783_set_host, + .set_device = mc13783_set_peripheral, + .set_vbus_power = mc13783_set_vbus_power, +}; + +extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops); + +int mc13783xc_init(void) +{ + PMIC_STATUS rs = PMIC_ERROR; + +#if defined(CONFIG_MXC_USB_SB3) + int xc_mode = USB_SINGLE_ENDED_BIDIR; +#elif defined(CONFIG_MXC_USB_SU6) + int xc_mode = USB_SINGLE_ENDED_UNIDIR; +#elif defined(CONFIG_MXC_USB_DB4) + int xc_mode = USB_DIFFERENTIAL_BIDIR; +#else + int xc_mode = USB_DIFFERENTIAL_UNIDIR; +#endif + + rs = usb_pmic_mod_init(); + if (rs != PMIC_SUCCESS) { + usb_pmic_mod_exit(); + printk(KERN_ERR "usb_pmic_mod_init failed\n"); + return rs; + } + + rs = pmic_convity_usb_set_xcvr(pmic_handle, xc_mode); + rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_OTG_SE0CONN); + rs |= pmic_convity_usb_otg_set_config(pmic_handle, USBXCVREN); + rs |= pmic_convity_set_output(pmic_handle, FALSE, TRUE); + + rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_PULL_OVERRIDE); + rs |= pmic_convity_usb_otg_clear_config(pmic_handle, USB_USBCNTRL); + rs |= pmic_convity_usb_otg_clear_config(pmic_handle, USB_DP150K_PU); + + if (rs != PMIC_SUCCESS) { + printk(KERN_ERR "pmic configuration failed\n"); + } + + fsl_usb_xcvr_register(&mc13783_ops_otg); + + mc13783_set_peripheral(); + + pmic_task = kthread_run(pmic_event_thread, NULL, "pmic_usb_xc"); + if (pmic_task == NULL) { + printk(KERN_ERR "pmic task creation failed\n"); + } + + return rs; +} + +extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops); + +void mc13783xc_uninit(void) +{ + int res = 0; + + /* Clear stuff from init */ + pmic_convity_usb_otg_clear_config(pmic_handle, USB_OTG_SE0CONN); + pmic_convity_usb_otg_clear_config(pmic_handle, USBXCVREN); + pmic_convity_set_output(pmic_handle, FALSE, FALSE); + pmic_convity_usb_otg_clear_config(pmic_handle, USB_PULL_OVERRIDE); + + /* Clear host mode */ + pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDP_PD); + pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDM_PD); + + /* Clear peripheral mode */ + pmic_convity_usb_otg_clear_config(pmic_handle, USB_PU); + + /* Vbus off */ + pmic_convity_set_output(pmic_handle, TRUE, FALSE); + pmic_convity_usb_otg_clear_config(pmic_handle, + USB_VBUS_CURRENT_LIMIT_LOW_30MS); + + usb_pmic_mod_exit(); + + fsl_usb_xcvr_unregister(&mc13783_ops_otg); + + res = kthread_stop(pmic_task); + if (res < 0) { + printk(KERN_ERR "pmic task stop failed\n"); + } +} + +static int pmic_event_thread(void *arg) +{ + PMIC_STATUS rs = PMIC_ERROR; + + while (1) { + down(&pmic_mx); + + if (p_event == MC13783_USB_VBUS_OFF) { + mc13783_set_peripheral(); + } + if (p_event == MC13783_USB_VBUS_ON) { + mc13783_set_host(); + } + if (p_event == MC13783_USB_DETECT_MINI_B) { + rs = pmic_convity_set_output(pmic_handle, TRUE, FALSE); + rs |= + pmic_convity_usb_otg_clear_config(pmic_handle, + USB_VBUS_CURRENT_LIMIT_LOW_30MS); + + if (rs != PMIC_SUCCESS) { + printk(KERN_ERR + "MC13783_USB_VBUS_OFF failed\n"); + } + } + if (p_event == MC13783_USB_DETECT_MINI_A) { + rs = pmic_convity_set_output(pmic_handle, TRUE, TRUE); + rs |= + pmic_convity_usb_otg_set_config(pmic_handle, + USB_VBUS_CURRENT_LIMIT_LOW_30MS); + + if (rs != PMIC_SUCCESS) { + printk(KERN_ERR "MC13783_USB_VBUS_ON failed\n"); + } + } + } + + return 0; +} + +module_init(mc13783xc_init); +module_exit(mc13783xc_uninit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("mc13783xc"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-mxc/sdma/Makefile b/arch/arm/plat-mxc/sdma/Makefile new file mode 100644 index 000000000000..4a36719aad92 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/Makefile @@ -0,0 +1,18 @@ +ifneq ($(KBUILD_SRC),) +CFLAGS += -I$(KBUILD_SRC)/arch/arm/plat-mxc/sdma/iapi/include \ + -I$(KBUILD_SRC)/include/linux \ + -DMCU -DOS=LINUX \ + -DL_I_T_T_L_E_ENDIAN=0 -DB_I_G_ENDIAN=1 \ + -DENDIANNESS=L_I_T_T_L_E_ENDIAN +else +CFLAGS += -Iarch/arm/plat-mxc/sdma/iapi/include \ + -Iinclude/linux \ + -DMCU -DOS=LINUX \ + -DL_I_T_T_L_E_ENDIAN=0 -DB_I_G_ENDIAN=1 \ + -DENDIANNESS=L_I_T_T_L_E_ENDIAN +endif + +obj-y += dma_sdma.o +obj-$(CONFIG_MXC_SDMA_API) += sdma.o +obj-$(CONFIG_MXC_SDMA_API) += iapi/ +obj-$(CONFIG_MXC_SDMA_API) += sdma_malloc.o diff --git a/arch/arm/plat-mxc/sdma/dma_sdma.c b/arch/arm/plat-mxc/sdma/dma_sdma.c new file mode 100644 index 000000000000..89fea1411f6d --- /dev/null +++ b/arch/arm/plat-mxc/sdma/dma_sdma.c @@ -0,0 +1,771 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file plat-mxc/sdma/dma_sdma.c + * @brief Front-end to the DMA handling. This handles the allocation/freeing + * of DMA channels, and provides a unified interface to the machines + * DMA facilities. This file contains functions for Smart DMA. + * + * @ingroup SDMA + */ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <asm/arch/dma.h> +#include <asm/arch/hardware.h> + +#include <asm/semaphore.h> +#include <linux/spinlock.h> + +#include <asm/uaccess.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> + +#include "iapi.h" + +#ifdef CONFIG_MXC_SDMA_API + +static mxc_dma_channel_t mxc_sdma_channels[MAX_DMA_CHANNELS]; +static mxc_dma_channel_private_t mxc_sdma_private[MAX_DMA_CHANNELS]; + +#define SDMA_SLEEP_STATE (0x6 << 12) +#define SDMA_STATE_MASK (0xF << 12) + +/*! + * To indicate whether SDMA engine is suspending + */ +static int suspend_flag = 0; + +extern struct clk *mxc_sdma_ahb_clk, *mxc_sdma_ipg_clk; + +/*! + * Tasket to handle processing the channel buffers + * + * @param arg channel id + */ +static void mxc_sdma_channeltasklet(unsigned long arg) +{ + dma_request_t request_t; + dma_channel_params chnl_param; + mxc_dma_channel_t *chnl_info; + mxc_dma_channel_private_t *data_priv; + int bd_intr = 0, error = MXC_DMA_DONE; + + chnl_info = &mxc_sdma_channels[arg]; + data_priv = chnl_info->private; + chnl_param = + mxc_sdma_get_channel_params(chnl_info->channel)->chnl_params; + + mxc_dma_get_config(arg, &request_t, data_priv->buf_tail); + + while (request_t.bd_done == 0) { + bd_intr = mxc_dma_get_bd_intr(arg, data_priv->buf_tail); + if ((data_priv->buf_tail += 1) >= chnl_param.bd_number) { + data_priv->buf_tail = 0; + } + chnl_info->active = 0; + if (request_t.bd_error) { + error = MXC_DMA_TRANSFER_ERROR; + } + + if (bd_intr != 0) { + chnl_info->cb_fn(chnl_info->cb_args, error, + request_t.count); + error = MXC_DMA_DONE; + } + + if (data_priv->buf_tail == chnl_info->curr_buf) { + break; + } + memset(&request_t, 0, sizeof(dma_request_t)); + mxc_dma_get_config(arg, &request_t, data_priv->buf_tail); + } +} + +/*! + * This function is generally called by the driver at open time. + * The DMA driver would do any initialization steps that is required + * to get the channel ready for data transfer. + * + * @param channel_id a pre-defined id. The peripheral driver would specify + * the id associated with its peripheral. This would be + * used by the DMA driver to identify the peripheral + * requesting DMA and do the necessary setup on the + * channel associated with the particular peripheral. + * The DMA driver could use static or dynamic DMA channel + * allocation. + * @param dev_name module name or device name + * @return returns a negative number on error if request for a DMA channel did not + * succeed, returns the channel number to be used on success. + */ +int mxc_dma_request(mxc_dma_device_t channel_id, char *dev_name) +{ + mxc_sdma_channel_params_t *chnl; + mxc_dma_channel_private_t *data_priv; + int ret = 0, i = 0, channel_num = 0; + + if (suspend_flag == 1) { + return -EBUSY; + } + + chnl = mxc_sdma_get_channel_params(channel_id); + if (chnl == NULL) { + return -EINVAL; + } + + /* Enable the SDMA clock */ + clk_enable(mxc_sdma_ahb_clk); + clk_enable(mxc_sdma_ipg_clk); + + channel_num = chnl->channel_num; + if (chnl->channel_num == MXC_DMA_DYNAMIC_CHANNEL) { + /* Get the first free channel */ + for (i = (MAX_DMA_CHANNELS - 1); i > 0; i--) { + /* See if channel is available */ + if ((mxc_sdma_channels[i].dynamic != 1) + || (mxc_sdma_channels[i].lock != 0)) { + continue; + } + channel_num = i; + /* Check to see if we can get this channel */ + ret = mxc_request_dma(&channel_num, dev_name); + if (ret == 0) { + break; + } else { + continue; + } + } + if (ret != 0) { + /* No free channel */ + goto err_ret; + } + } else { + if (mxc_sdma_channels[chnl->channel_num].lock == 1) { + ret = -ENODEV; + goto err_ret; + } + ret = mxc_request_dma(&channel_num, dev_name); + if (ret != 0) { + goto err_ret; + } + } + + ret = mxc_dma_setup_channel(channel_num, &chnl->chnl_params); + + if (ret == 0) { + if (chnl->chnl_priority != MXC_SDMA_DEFAULT_PRIORITY) { + ret = + mxc_dma_set_channel_priority(channel_num, + chnl->chnl_priority); + if (ret != 0) { + pr_info("Failed to set channel prority,\ + continue with the existing \ + priority\n"); + goto err_ret; + } + } + mxc_sdma_channels[channel_num].lock = 1; + if ((chnl->chnl_params.transfer_type == per_2_emi) + || (chnl->chnl_params.transfer_type == dsp_2_emi)) { + mxc_sdma_channels[channel_num].mode = MXC_DMA_MODE_READ; + } else { + mxc_sdma_channels[channel_num].mode = + MXC_DMA_MODE_WRITE; + } + mxc_sdma_channels[channel_num].channel = channel_id; + data_priv = mxc_sdma_channels[channel_num].private; + tasklet_init(&data_priv->chnl_tasklet, + mxc_sdma_channeltasklet, channel_num); + if ((channel_id == MXC_DMA_ATA_RX) + || (channel_id == MXC_DMA_ATA_TX)) { + data_priv->intr_after_every_bd = 0; + } else { + data_priv->intr_after_every_bd = 1; + } + } + err_ret: + if (ret != 0) { + clk_disable(mxc_sdma_ahb_clk); + clk_disable(mxc_sdma_ipg_clk); + channel_num = -ENODEV; + } + + return channel_num; +} + +/*! + * This function is generally called by the driver at close time. The DMA + * driver would do any cleanup associated with this channel. + * + * @param channel_num the channel number returned at request time. This + * would be used by the DMA driver to identify the calling + * driver and do the necessary cleanup on the channel + * associated with the particular peripheral + * @return returns a negative number on error or 0 on success + */ +int mxc_dma_free(int channel_num) +{ + mxc_dma_channel_private_t *data_priv; + + if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) { + return -EINVAL; + } + + if (mxc_sdma_channels[channel_num].lock != 1) { + return -ENODEV; + } + + mxc_free_dma(channel_num); + + /* Disable the SDMA clock */ + clk_disable(mxc_sdma_ahb_clk); + clk_disable(mxc_sdma_ipg_clk); + + mxc_sdma_channels[channel_num].lock = 0; + mxc_sdma_channels[channel_num].active = 0; + mxc_sdma_channels[channel_num].curr_buf = 0; + data_priv = mxc_sdma_channels[channel_num].private; + data_priv->buf_tail = 0; + tasklet_kill(&data_priv->chnl_tasklet); + + return 0; +} + +/*! + * Callback function called from the SDMA Interrupt routine + * + * @param arg driver specific argument that was registered + */ +static void mxc_dma_chnl_callback(void *arg) +{ + int priv; + mxc_dma_channel_private_t *data_priv; + + priv = (int)arg; + data_priv = mxc_sdma_channels[priv].private; + /* Process the buffers in a tasklet */ + tasklet_schedule(&data_priv->chnl_tasklet); +} + +/*! + * This function would just configure the buffers specified by the user into + * dma channel. The caller must call mxc_dma_enable to start this transfer. + * + * @param channel_num the channel number returned at request time. This + * would be used by the DMA driver to identify the calling + * driver and do the necessary cleanup on the channel + * associated with the particular peripheral + * @param dma_buf an array of physical addresses to the user defined + * buffers. The caller must guarantee the dma_buf is + * available until the transfer is completed. + * @param num_buf number of buffers in the array + * @param mode specifies whether this is READ or WRITE operation + * @return This function returns a negative number on error if buffer could not be + * added with DMA for transfer. On Success, it returns 0 + */ +int mxc_dma_config(int channel_num, mxc_dma_requestbuf_t * dma_buf, + int num_buf, mxc_dma_mode_t mode) +{ + int ret = 0, i = 0, prev_buf; + mxc_dma_channel_t *chnl_info; + mxc_dma_channel_private_t *data_priv; + mxc_sdma_channel_params_t *chnl; + dma_channel_params chnl_param; + dma_request_t request_t; + + if (suspend_flag == 1) { + return -EBUSY; + } + + if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) { + return -EINVAL; + } + + if (num_buf <= 0) { + return -EINVAL; + } + + chnl_info = &mxc_sdma_channels[channel_num]; + data_priv = chnl_info->private; + if (chnl_info->lock != 1) { + return -ENODEV; + } + + /* Check to see if all buffers are taken */ + if (chnl_info->active == 1) { + return -EBUSY; + } + + chnl = mxc_sdma_get_channel_params(chnl_info->channel); + chnl_param = chnl->chnl_params; + + /* Re-setup the SDMA channel if the transfer direction is changed */ + if ((chnl_param.peripheral_type != MEMORY) && (mode != chnl_info->mode)) { + if (chnl_param.peripheral_type == DSP) { + if (mode == MXC_DMA_MODE_READ) { + chnl_param.transfer_type = dsp_2_emi; + } else { + chnl_param.transfer_type = emi_2_dsp; + } + } else { + if (mode == MXC_DMA_MODE_READ) { + chnl_param.transfer_type = per_2_emi; + } else { + chnl_param.transfer_type = emi_2_per; + } + } + chnl_param.callback = mxc_dma_chnl_callback; + chnl_param.arg = (void *)channel_num; + ret = mxc_dma_setup_channel(channel_num, &chnl_param); + if (ret != 0) { + return ret; + } + if (chnl->chnl_priority != MXC_SDMA_DEFAULT_PRIORITY) { + ret = + mxc_dma_set_channel_priority(channel_num, + chnl->chnl_priority); + if (ret != 0) { + pr_info("Failed to set channel prority,\ + continue with the existing \ + priority\n"); + } + } + chnl_info->mode = mode; + } + + for (i = 0; i < num_buf; i++, dma_buf++) { + /* Check to see if all buffers are taken */ + if (chnl_info->active == 1) { + break; + } + request_t.destAddr = (__u8 *) dma_buf->dst_addr; + request_t.sourceAddr = (__u8 *) dma_buf->src_addr; + request_t.count = dma_buf->num_of_bytes; + request_t.bd_cont = 1; + ret = mxc_dma_set_config(channel_num, &request_t, + chnl_info->curr_buf); + if (ret != 0) { + break; + } + if (data_priv->intr_after_every_bd == 0) { + if (i == num_buf - 1) { + mxc_dma_set_bd_intr(channel_num, + chnl_info->curr_buf, 1); + } else { + mxc_dma_set_bd_intr(channel_num, + chnl_info->curr_buf, 0); + } + } + + prev_buf = chnl_info->curr_buf; + if ((chnl_info->curr_buf += 1) >= chnl_param.bd_number) { + chnl_info->curr_buf = 0; + } + if (chnl_info->curr_buf == data_priv->buf_tail) { + if ((data_priv->intr_after_every_bd == 0) + && (i != num_buf - 1)) { + /* + * Set the BD_INTR flag on the last BD that + * was queued + */ + mxc_dma_set_bd_intr(channel_num, prev_buf, 1); + } + chnl_info->active = 1; + } + } + + if (i == 0) { + return -EBUSY; + } + return 0; +} + +/*! + * This function would just configure the scatterlist specified by the + * user into dma channel. This is a slight variation of mxc_dma_config(), + * it is provided for the convenience of drivers that have a scatterlist + * passed into them. It is the calling driver's responsibility to have the + * correct physical address filled in the "dma_address" field of the + * scatterlist. + * + * @param channel_num the channel number returned at request time. This + * would be used by the DMA driver to identify the calling + * driver and do the necessary cleanup on the channel + * associated with the particular peripheral + * @param sg a scatterlist of buffers. The caller must guarantee + * the dma_buf is available until the transfer is + * completed. + * @param num_buf number of buffers in the array + * @param num_of_bytes total number of bytes to transfer. If set to 0, this + * would imply to use the length field of the scatterlist + * for each DMA transfer. Else it would calculate the size + * for each DMA transfer. + * @param mode specifies whether this is READ or WRITE operation + * @return This function returns a negative number on error if buffer could not + * be added with DMA for transfer. On Success, it returns 0 + */ +int mxc_dma_sg_config(int channel_num, struct scatterlist *sg, + int num_buf, int num_of_bytes, mxc_dma_mode_t mode) +{ + int ret = 0, i = 0; + mxc_dma_requestbuf_t *dma_buf; + + if (suspend_flag == 1) { + return -EBUSY; + } + + if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) { + return -EINVAL; + } + + if (mxc_sdma_channels[channel_num].lock != 1) { + return -ENODEV; + } + + dma_buf = + (mxc_dma_requestbuf_t *) kmalloc(num_buf * + sizeof(mxc_dma_requestbuf_t), + GFP_KERNEL); + + if (dma_buf == NULL) { + return -EFAULT; + } + + for (i = 0; i < num_buf; i++) { + if (mode == MXC_DMA_MODE_READ) { + (dma_buf + i)->dst_addr = sg->dma_address; + } else { + (dma_buf + i)->src_addr = sg->dma_address; + } + + if ((num_of_bytes > sg->length) || (num_of_bytes == 0)) { + (dma_buf + i)->num_of_bytes = sg->length; + } else { + (dma_buf + i)->num_of_bytes = num_of_bytes; + } + sg++; + num_of_bytes -= (dma_buf + i)->num_of_bytes; + } + + ret = mxc_dma_config(channel_num, dma_buf, num_buf, mode); + kfree(dma_buf); + return ret; +} + +/*! + * This function is provided if the driver would like to set/change its + * callback function. + * + * @param channel_num the channel number returned at request time. This + * would be used by the DMA driver to identify the calling + * driver and do the necessary cleanup on the channel + * associated with the particular peripheral + * @param callback a callback function to provide notification on transfer + * completion, user could specify NULL if he does not wish + * to be notified + * @param arg an argument that gets passed in to the callback + * function, used by the user to do any driver specific + * operations. + * @return this function returns a negative number on error if the callback + * could not be set for the channel or 0 on success + */ +int mxc_dma_callback_set(int channel_num, + mxc_dma_callback_t callback, void *arg) +{ + if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) { + return -EINVAL; + } + + if (mxc_sdma_channels[channel_num].lock != 1) { + return -ENODEV; + } + + mxc_sdma_channels[channel_num].cb_fn = callback; + mxc_sdma_channels[channel_num].cb_args = arg; + + mxc_dma_set_callback(channel_num, mxc_dma_chnl_callback, + (void *)channel_num); + + return 0; +} + +/*! + * This stops the DMA channel and any ongoing transfers. Subsequent use of + * mxc_dma_enable() will restart the channel and restart the transfer. + * + * @param channel_num the channel number returned at request time. This + * would be used by the DMA driver to identify the calling + * driver and do the necessary cleanup on the channel + * associated with the particular peripheral + * @return returns a negative number on error or 0 on success + */ +int mxc_dma_disable(int channel_num) +{ + if (suspend_flag == 1) { + return -EBUSY; + } + + if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) { + return -EINVAL; + } + + if (mxc_sdma_channels[channel_num].lock != 1) { + return -ENODEV; + } + + mxc_dma_stop(channel_num); + return 0; +} + +/*! + * This starts DMA transfer. Or it restarts DMA on a stopped channel + * previously stopped with mxc_dma_disable(). + * + * @param channel_num the channel number returned at request time. This + * would be used by the DMA driver to identify the calling + * driver and do the necessary cleanup on the channel + * associated with the particular peripheral + * @return returns a negative number on error or 0 on success + */ +int mxc_dma_enable(int channel_num) +{ + if (suspend_flag == 1) { + return -EBUSY; + } + + if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) { + return -EINVAL; + } + + if (mxc_sdma_channels[channel_num].lock != 1) { + return -ENODEV; + } + + mxc_dma_start(channel_num); + return 0; +} + +/*! + * This function is called to put the SDMA in a low power state. + * + * @param pdev the device structure + * @param state the power state the device is entering + * + * @return The function always returns 0. + */ +static int mxc_dma_suspend(struct platform_device *pdev, pm_message_t state) +{ + if ((SDMA_ONCE_STAT & SDMA_STATE_MASK) == SDMA_SLEEP_STATE) { + suspend_flag = 1; + clk_disable(mxc_sdma_ahb_clk); + clk_disable(mxc_sdma_ipg_clk); + + return 0; + } else { + return -EAGAIN; + } +} + +/*! + * This function is called to resume the MU from a low power state. + * + * @param dev the device structure + * + * @return The function always returns 0. + */ +static int mxc_dma_resume(struct platform_device *pdev) +{ + clk_enable(mxc_sdma_ahb_clk); + clk_enable(mxc_sdma_ipg_clk); + suspend_flag = 0; + + return 0; +} + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxc_dma_driver = { + .driver = { + .name = "mxc_dma", + }, + .suspend = mxc_dma_suspend, + .resume = mxc_dma_resume, +}; + +/*! + * Initializes dma structure with dma_operations + * + * @param dma dma structure + * @return returns 0 on success + */ +static int __init mxc_dma_init(void) +{ + int i; + for (i = 0; i < MAX_DMA_CHANNELS; i++) { + mxc_sdma_channels[i].active = 0; + mxc_sdma_channels[i].lock = 0; + mxc_sdma_channels[i].curr_buf = 0; + mxc_sdma_channels[i].dynamic = 1; + mxc_sdma_private[i].buf_tail = 0; + mxc_sdma_channels[i].private = &mxc_sdma_private[i]; + } + /* + * Make statically allocated channels unavailable for dynamic channel + * requests + */ + mxc_get_static_channels(mxc_sdma_channels); + + if (platform_driver_register(&mxc_dma_driver) != 0) { + printk(KERN_ERR "Driver register failed for mxc_dma_driver\n"); + return -ENODEV; + } + + return 0; +} + +arch_initcall(mxc_dma_init); + +#else +int mxc_request_dma(int *channel, const char *devicename) +{ + return -ENODEV; +} + +int mxc_dma_setup_channel(int channel, dma_channel_params * p) +{ + return -ENODEV; +} + +int mxc_dma_set_channel_priority(unsigned int channel, unsigned int priority) +{ + return -ENODEV; +} + +int mxc_dma_set_config(int channel, dma_request_t * p, int bd_index) +{ + return -ENODEV; +} + +int mxc_dma_get_config(int channel, dma_request_t * p, int bd_index) +{ + return -ENODEV; +} + +int mxc_dma_start(int channel) +{ + return -ENODEV; +} + +int mxc_dma_stop(int channel) +{ + return -ENODEV; +} + +void mxc_free_dma(int channel) +{ +} + +void mxc_dma_set_callback(int channel, dma_callback_t callback, void *arg) +{ +} + +void *sdma_malloc(size_t size) +{ + return 0; +} + +void sdma_free(void *buf) +{ +} + +void *sdma_phys_to_virt(unsigned long buf) +{ + return 0; +} + +unsigned long sdma_virt_to_phys(void *buf) +{ + return 0; +} + +int mxc_dma_request(mxc_dma_device_t channel_id, char *dev_name) +{ + return -ENODEV; +} + +int mxc_dma_free(int channel_num) +{ + return -ENODEV; +} + +int mxc_dma_config(int channel_num, mxc_dma_requestbuf_t * dma_buf, + int num_buf, mxc_dma_mode_t mode) +{ + return -ENODEV; +} + +int mxc_dma_sg_config(int channel_num, struct scatterlist *sg, + int num_buf, int num_of_bytes, mxc_dma_mode_t mode) +{ + return -ENODEV; +} + +int mxc_dma_callback_set(int channel_num, mxc_dma_callback_t callback, + void *arg) +{ + return -ENODEV; +} + +int mxc_dma_disable(int channel_num) +{ + return -ENODEV; +} + +int mxc_dma_enable(int channel_num) +{ + return -ENODEV; +} + +EXPORT_SYMBOL(mxc_request_dma); +EXPORT_SYMBOL(mxc_dma_setup_channel); +EXPORT_SYMBOL(mxc_dma_set_channel_priority); +EXPORT_SYMBOL(mxc_dma_set_config); +EXPORT_SYMBOL(mxc_dma_get_config); +EXPORT_SYMBOL(mxc_dma_start); +EXPORT_SYMBOL(mxc_dma_stop); +EXPORT_SYMBOL(mxc_free_dma); +EXPORT_SYMBOL(mxc_dma_set_callback); +EXPORT_SYMBOL(sdma_malloc); +EXPORT_SYMBOL(sdma_free); +EXPORT_SYMBOL(sdma_phys_to_virt); +EXPORT_SYMBOL(sdma_virt_to_phys); + +#endif + +EXPORT_SYMBOL(mxc_dma_request); +EXPORT_SYMBOL(mxc_dma_free); +EXPORT_SYMBOL(mxc_dma_config); +EXPORT_SYMBOL(mxc_dma_sg_config); +EXPORT_SYMBOL(mxc_dma_callback_set); +EXPORT_SYMBOL(mxc_dma_disable); +EXPORT_SYMBOL(mxc_dma_enable); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC Linux SDMA API"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-mxc/sdma/iapi/Makefile b/arch/arm/plat-mxc/sdma/iapi/Makefile new file mode 100644 index 000000000000..b6a5d6aebda0 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for I.API sources. +# + +obj-y := src/
\ No newline at end of file diff --git a/arch/arm/plat-mxc/sdma/iapi/include/epm.h b/arch/arm/plat-mxc/sdma/iapi/include/epm.h new file mode 100644 index 000000000000..ad6ea9573a36 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/include/epm.h @@ -0,0 +1,202 @@ +/* + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef _mcuEpm_h +#define _mcuEpm_h + +#include <asm/arch/hardware.h> + +/*MCU_sdma (sdma.0.) regs definition */ +#define SDMA_BASE_IO_ADDR IO_ADDRESS(SDMA_BASE_ADDR) + +#define SDMA_H_C0PTR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x000)) +#define SDMA_H_INTR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x004)) +#define SDMA_H_STATSTOP *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x008)) +#define SDMA_H_START *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x00C)) +#define SDMA_H_EVTOVR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x010)) +#define SDMA_H_DSPOVR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x014)) +#define SDMA_H_HOSTOVR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x018)) +#define SDMA_H_EVTPEND *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x01C)) +#define SDMA_H_DSPENBL *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x020)) +#define SDMA_H_RESET *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x024)) +#define SDMA_H_EVTERR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x028)) +#define SDMA_H_INTRMSK *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x02C)) +#define SDMA_H_PSW *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x030)) +#define SDMA_H_EVTERRDBG *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x034)) +#define SDMA_H_CONFIG *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x038)) +#define SDMA_ONCE_ENB *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x040)) +#define SDMA_ONCE_DATA *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x044)) +#define SDMA_ONCE_INSTR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x048)) +#define SDMA_ONCE_STAT *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x04C)) +#define SDMA_ONCE_CMD *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x050)) +#define SDMA_EVT_MIRROR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x054)) +#define SDMA_ILLINSTADDR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x058)) +#define SDMA_CHN0ADDR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x05C)) +#define SDMA_ONCE_RTB *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x060)) +#define SDMA_XTRIG_CONF1 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x070)) +#define SDMA_XTRIG_CONF2 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x074)) + +#if defined(CONFIG_ARCH_MXC91221) || defined(CONFIG_ARCH_MXC92323) +#define SDMA_CHNENBL_0 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x200)) +#define SDMA_CHNENBL_1 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x204)) +#define SDMA_CHNENBL_2 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x208)) +#define SDMA_CHNENBL_3 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x20C)) +#define SDMA_CHNENBL_4 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x210)) +#define SDMA_CHNENBL_5 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x214)) +#define SDMA_CHNENBL_6 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x218)) +#define SDMA_CHNENBL_7 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x21C)) +#define SDMA_CHNENBL_8 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x220)) +#define SDMA_CHNENBL_9 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x224)) +#define SDMA_CHNENBL_10 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x228)) +#define SDMA_CHNENBL_11 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x22C)) +#define SDMA_CHNENBL_12 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x230)) +#define SDMA_CHNENBL_13 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x234)) +#define SDMA_CHNENBL_14 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x238)) +#define SDMA_CHNENBL_15 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x23C)) +#define SDMA_CHNENBL_16 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x240)) +#define SDMA_CHNENBL_17 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x244)) +#define SDMA_CHNENBL_18 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x248)) +#define SDMA_CHNENBL_19 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x24C)) +#define SDMA_CHNENBL_20 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x250)) +#define SDMA_CHNENBL_21 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x254)) +#define SDMA_CHNENBL_22 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x258)) +#define SDMA_CHNENBL_23 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x25C)) +#define SDMA_CHNENBL_24 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x260)) +#define SDMA_CHNENBL_25 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x264)) +#define SDMA_CHNENBL_26 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x268)) +#define SDMA_CHNENBL_27 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x26C)) +#define SDMA_CHNENBL_28 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x270)) +#define SDMA_CHNENBL_29 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x274)) +#define SDMA_CHNENBL_30 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x278)) +#define SDMA_CHNENBL_31 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x27C)) +#define SDMA_CHNENBL_32 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x280)) +#define SDMA_CHNENBL_33 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x284)) +#define SDMA_CHNENBL_34 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x288)) +#define SDMA_CHNENBL_35 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x28C)) +#define SDMA_CHNENBL_36 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x290)) +#define SDMA_CHNENBL_37 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x294)) +#define SDMA_CHNENBL_38 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x298)) +#define SDMA_CHNENBL_39 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x29C)) +#define SDMA_CHNENBL_40 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2A0)) +#define SDMA_CHNENBL_41 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2A4)) +#define SDMA_CHNENBL_42 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2A8)) +#define SDMA_CHNENBL_43 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2AC)) +#define SDMA_CHNENBL_44 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2B0)) +#define SDMA_CHNENBL_45 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2B4)) +#define SDMA_CHNENBL_46 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2B8)) +#define SDMA_CHNENBL_47 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2BC)) + +#define SDMA_ONCE_COUNT *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x300)) +#define SDMA_ONCE_ECTL *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x304)) +#define SDMA_ONCE_EAA *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x308)) +#define SDMA_ONCE_EAB *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x30C)) +#define SDMA_ONCE_EAM *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x310)) +#define SDMA_ONCE_ED *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x314)) +#define SDMA_ONCE_EDM *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x318)) +#define SDMA_ONCE_PCMATCH *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x31C)) + +#else +#define SDMA_CHNENBL_0 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x080)) +#define SDMA_CHNENBL_1 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x084)) +#define SDMA_CHNENBL_2 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x088)) +#define SDMA_CHNENBL_3 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x08C)) +#define SDMA_CHNENBL_4 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x090)) +#define SDMA_CHNENBL_5 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x094)) +#define SDMA_CHNENBL_6 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x098)) +#define SDMA_CHNENBL_7 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x09C)) +#define SDMA_CHNENBL_8 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0A0)) +#define SDMA_CHNENBL_9 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0A4)) +#define SDMA_CHNENBL_10 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0A8)) +#define SDMA_CHNENBL_11 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0AC)) +#define SDMA_CHNENBL_12 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0B0)) +#define SDMA_CHNENBL_13 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0B4)) +#define SDMA_CHNENBL_14 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0B8)) +#define SDMA_CHNENBL_15 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0BC)) +#define SDMA_CHNENBL_16 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0C0)) +#define SDMA_CHNENBL_17 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0C4)) +#define SDMA_CHNENBL_18 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0C8)) +#define SDMA_CHNENBL_19 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0CC)) +#define SDMA_CHNENBL_20 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0D0)) +#define SDMA_CHNENBL_21 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0D4)) +#define SDMA_CHNENBL_22 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0D8)) +#define SDMA_CHNENBL_23 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0DC)) +#define SDMA_CHNENBL_24 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0E0)) +#define SDMA_CHNENBL_25 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0E4)) +#define SDMA_CHNENBL_26 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0E8)) +#define SDMA_CHNENBL_27 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0EC)) +#define SDMA_CHNENBL_28 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0F0)) +#define SDMA_CHNENBL_29 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0F4)) +#define SDMA_CHNENBL_30 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0F8)) +#define SDMA_CHNENBL_31 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0FC)) + +#define SDMA_ONCE_COUNT *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x200)) +#define SDMA_ONCE_ECTL *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x204)) +#define SDMA_ONCE_EAA *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x208)) +#define SDMA_ONCE_EAB *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x20C)) +#define SDMA_ONCE_EAM *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x210)) +#define SDMA_ONCE_ED *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x214)) +#define SDMA_ONCE_EDM *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x218)) +#define SDMA_ONCE_PCMATCH *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x21C)) + +#endif + +#define SDMA_CHNPRI_0 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x100)) +#define SDMA_CHNPRI_1 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x104)) +#define SDMA_CHNPRI_2 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x108)) +#define SDMA_CHNPRI_3 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x10C)) +#define SDMA_CHNPRI_4 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x110)) +#define SDMA_CHNPRI_5 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x114)) +#define SDMA_CHNPRI_6 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x118)) +#define SDMA_CHNPRI_7 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x11C)) +#define SDMA_CHNPRI_8 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x120)) +#define SDMA_CHNPRI_9 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x124)) +#define SDMA_CHNPRI_10 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x128)) +#define SDMA_CHNPRI_11 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x12C)) +#define SDMA_CHNPRI_12 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x130)) +#define SDMA_CHNPRI_13 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x134)) +#define SDMA_CHNPRI_14 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x138)) +#define SDMA_CHNPRI_15 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x13C)) +#define SDMA_CHNPRI_16 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x140)) +#define SDMA_CHNPRI_17 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x144)) +#define SDMA_CHNPRI_18 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x148)) +#define SDMA_CHNPRI_19 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x14C)) +#define SDMA_CHNPRI_20 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x150)) +#define SDMA_CHNPRI_21 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x154)) +#define SDMA_CHNPRI_22 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x158)) +#define SDMA_CHNPRI_23 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x15C)) +#define SDMA_CHNPRI_24 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x160)) +#define SDMA_CHNPRI_25 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x164)) +#define SDMA_CHNPRI_26 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x168)) +#define SDMA_CHNPRI_27 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x16C)) +#define SDMA_CHNPRI_28 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x170)) +#define SDMA_CHNPRI_29 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x174)) +#define SDMA_CHNPRI_30 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x178)) +#define SDMA_CHNPRI_31 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x17C)) +#define SDMA_CHNPRI_32 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x180)) +#define SDMA_CHNPRI_33 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x184)) +#define SDMA_CHNPRI_34 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x188)) +#define SDMA_CHNPRI_35 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x18C)) +#define SDMA_CHNPRI_36 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x190)) +#define SDMA_CHNPRI_37 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x194)) +#define SDMA_CHNPRI_38 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x198)) +#define SDMA_CHNPRI_39 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x19C)) +#define SDMA_CHNPRI_40 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x1A0)) +#define SDMA_CHNPRI_41 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x1A4)) +#define SDMA_CHNPRI_42 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x1A8)) +#define SDMA_CHNPRI_43 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x1AC)) +#define SDMA_CHNPRI_44 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x1B0)) +#define SDMA_CHNPRI_45 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x1B4)) +#define SDMA_CHNPRI_46 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x1B8)) +#define SDMA_CHNPRI_47 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x1BC)) + +#endif /* _mcuEpm_h */ diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapi.h b/arch/arm/plat-mxc/sdma/iapi/include/iapi.h new file mode 100644 index 000000000000..d7300218057b --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/include/iapi.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapi.h + * + * $Id iapi.h $ + * + * Description: + * Unique include for the whole IAPI library. + * + * + * http//compass.mot.com/go/115342679 + * + * $Log iapi.h $ + * + * ***************************************************************************/ + +#ifndef _iapi_h +#define _iapi_h + +/* **************************************************************************** + * Include File Section + * ***************************************************************************/ + +#include "sdmaStruct.h" +#include "iapiDefaults.h" +#include "iapiLow.h" +#include "iapiMiddle.h" +#include "iapiHigh.h" + +#ifdef MCU +#include "iapiLowMcu.h" +#include "iapiMiddleMcu.h" +#endif /* MCU */ + + + +#endif /* _iapi_h */ diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiDefaults.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiDefaults.h new file mode 100644 index 000000000000..b03a53ae1893 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiDefaults.h @@ -0,0 +1,128 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiDefaults.h + * + * $Id iapiDefaults.h $ + * + * Description: + * This library is written in C to guarantee functionality and integrity in + * the usage of SDMA virtual DMA channels. This API (Application Programming + * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE + * fashion. + * + * + * + * + * $Log iapiDefaults.h $ + * + *****************************************************************************/ + + +#ifndef _iapi_defaults_h +#define _iapi_defaults_h + +/****************************************************************************** + * Include File Section + *****************************************************************************/ +#include "epm.h" +#include "sdmaStruct.h" + +/* **************************************************************************** + * Macro-command Section + * ***************************************************************************/ + +/** + * Error codes + * lower 5 bits free to include channel number when available + * and bit number 6 must be set when channel number is available + * + * Note : + * 1) Abbreviations / naming convention : + * - BD : Buffer Descriptor + * - CC : Channel Context + * - CCB : Channel Control Block + * - CD : Channel Descriptor + * - B : Buffer + * - CH : Channel + * + */ +#define IAPI_SUCCESS 0 +#define IAPI_FAILURE -1 +#define IAPI_ERR_CH_AVAILABLE 0x00020 +#define IAPI_ERR_NO_ERROR 0x00000 +#define IAPI_ERR_NO_CCB_DEFINED 0x01000 +#define IAPI_ERR_BD_UNINITIALIZED 0x02000 +#define IAPI_ERR_BD_ALLOCATED 0x03000 +#define IAPI_ERR_BD_ALLOCATION 0x04000 +#define IAPI_ERR_CCB_ALLOC_FAILED 0x05000 +#define IAPI_ERR_CCB_UNINITIALIZED 0x06000 +#define IAPI_ERR_CC_ALREADY_DEFINED 0x07000 +#define IAPI_ERR_CC_ALLOC_FAILED 0x08000 +#define IAPI_ERR_CD_ALREADY_DEFINED 0x09000 +#define IAPI_ERR_CD_ALLOC_FAILED 0x0A000 +#define IAPI_ERR_CD_CHANGE_CH_NUMBER 0x0B000 +#define IAPI_ERR_CD_CHANGE_CCB_PTR 0x0C000 +#define IAPI_ERR_CD_CHANGE_UNKNOWN 0x0D000 +#define IAPI_ERR_CD_CHANGE 0x0E000 +#define IAPI_ERR_CD_UNINITIALIZED 0x0F000 +#define IAPI_ERR_CLOSE 0x10000 +#define IAPI_ERR_B_ALLOC_FAILED 0x11000 +#define IAPI_ERR_CONFIG_OVERRIDE 0x12000 +#define IAPI_ERR_CH_IN_USE 0x13000 +#define IAPI_ERR_CALLBACKSYNCH_UNKNOWN 0x14000 +#define IAPI_ERR_INVALID_PARAMETER 0x15000 +#define IAPI_ERR_TRUST 0x16000 +#define IAPI_ERR_CHANNEL_UNINITIALIZED 0x17000 +#define IAPI_ERR_RROR_BIT_READ 0x18000 +#define IAPI_ERR_RROR_BIT_WRITE 0x19000 +#define IAPI_ERR_NOT_ALLOWED 0x1A000 +#define IAPI_ERR_NO_OS_FN 0x1B000 + + +/* + * Global Variable Section + */ + +/* + * Table to hold pointers to the callback functions registered by the users of + *I.API + */ +extern void (* callbackIsrTable[CH_NUM])(channelDescriptor * cd_p, void * arg); + +/* + * Table to hold user registered data pointers, to be privided in the callback + *function + */ +extern void * userArgTable[CH_NUM]; + +/* channelDescriptor data structure filled with default data*/ +extern channelDescriptor iapi_ChannelDefaults; + +/* Global variable to hold the last error encountered in I.API operations*/ +extern unsigned int iapi_errno; + +/* Used in synchronization, to mark started channels*/ +extern volatile unsigned long iapi_SDMAIntr; + +/* Hold a pointer to the start of the CCB array, to be used in the IRQ routine + *to find the channel descriptor for the channed sending the interrupt to the + *core. + */ +extern channelControlBlock * iapi_CCBHead; + +/* configs_data structure filled with default data*/ +extern configs_data iapi_ConfigDefaults; + +#endif /* iapiDefaults_h */ diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiHigh.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiHigh.h new file mode 100644 index 000000000000..14cfae539eb8 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiHigh.h @@ -0,0 +1,136 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiHigh.h + * + * $Id iapiHigh.h $ + * + * Description: + * prototypes for high level function of I.API + * + * + * http://venerque.sps.mot.com/pjt/sfs/www/iapi/softsim_api.pdf + * + * $Log iapiHigh.h + * + * ***************************************************************************/ + +#ifndef _iapiHigh_h +#define _iapiHigh_h + +/* **************************************************************************** + * Include File Section + *****************************************************************************/ +#include "sdmaStruct.h" + +/* **************************************************************************** + * Macro-command Section + *****************************************************************************/ +enum { + IAPI_CHANGE_CHANDESC, + IAPI_CHANGE_BDNUM, + IAPI_CHANGE_BUFFSIZE, + IAPI_CHANGE_CHANBLOCK, + IAPI_CHANGE_INSTANCE, + IAPI_CHANGE_OWNERSHIP, + IAPI_CHANGE_SYNCH, + IAPI_CHANGE_TRUST, + IAPI_CHANGE_CALLBACKFUNC, + IAPI_CHANGE_CHANCCB, + IAPI_CHANGE_PRIORITY, + IAPI_CHANGE_BDWRAP, + IAPI_CHANGE_WATERMARK, + IAPI_CHANGE_SET_BDCONT, + IAPI_CHANGE_UNSET_BDCONT, + IAPI_CHANGE_SET_BDEXTD, + IAPI_CHANGE_UNSET_BDEXTD, + IAPI_CHANGE_EVTMASK1, + IAPI_CHANGE_EVTMASK2, + IAPI_CHANGE_PERIPHADDR, + IAPI_CHANGE_SET_BDINTR, + IAPI_CHANGE_UNSET_BDINTR, + IAPI_CHANGE_SET_TRANSFER_CD, + IAPI_CHANGE_FORCE_CLOSE, + IAPI_CHANGE_SET_TRANSFER, + IAPI_CHANGE_USER_ARG, + IAPI_CHANGE_SET_BUFFERADDR, + IAPI_CHANGE_SET_EXTDBUFFERADDR, + IAPI_CHANGE_SET_COMMAND, + IAPI_CHANGE_SET_COUNT, + IAPI_CHANGE_SET_STATUS, + IAPI_CHANGE_GET_BUFFERADDR, + IAPI_CHANGE_GET_EXTDBUFFERADDR, + IAPI_CHANGE_GET_COMMAND, + IAPI_CHANGE_GET_COUNT, + IAPI_CHANGE_GET_STATUS, + IAPI_CHANGE_SET_ENDIANNESS +}; + + +/* + * Public Function Prototype Section + */ +int iapi_Open ( channelDescriptor * cd_p, unsigned char channelNumber ); +int iapi_Close( channelDescriptor * cd_p ); +int iapi_Read ( channelDescriptor * cd_p, void * buf, unsigned short nbyte ); +int iapi_Write( channelDescriptor * cd_p, void * buf, unsigned short nbyte ); +int iapi_MemCopy(channelDescriptor * cd_p, void* dest, void* src, + unsigned long size); +int iapi_IoCtl( channelDescriptor * cd_p, unsigned long ctlRequest, + unsigned long param); + + +int iapi_Read_ipcv2( channelDescriptor * cd_p, void * data_control_struct_ipcv2); + +int iapi_Write_ipcv2( channelDescriptor * cd_p, void * data_control_struct_ipcv2); + +#ifdef MCU +int iapi_Init(channelDescriptor * cd_p, configs_data * config_p, + unsigned short* ram_image, unsigned short code_size, + unsigned long start_addr); +#endif /* MCU */ +#ifdef DSP +int iapi_Init(channelDescriptor * cd_p); +#endif /* DSP */ + +int iapi_StartChannel(unsigned char channel); +int iapi_StopChannel(unsigned char channel); +int iapi_SynchChannel(unsigned char channel); + +int iapi_GetChannelNumber(channelDescriptor * cd_p); +unsigned long iapi_GetError(channelDescriptor * cd_p); +int iapi_GetCount(channelDescriptor * cd_p); +int iapi_GetCountAll(channelDescriptor * cd_p); + +#ifndef IRQ_KEYWORD +#define IRQ_KEYWORD +#endif /* IRQ_KEYWORD */ + +IRQ_KEYWORD void IRQ_Handler(void); + +#ifdef MCU +int iapi_GetScript(channelDescriptor * cd_p, void * buf, unsigned short size, + unsigned long address); +int iapi_GetContext(channelDescriptor * cd_p, void * buf, + unsigned char channel); +int iapi_SetScript(channelDescriptor * cd_p, void * buf, unsigned short nbyte, + unsigned long destAddr); +int iapi_SetContext(channelDescriptor * cd_p, void * buf, + unsigned char channel); +int iapi_AssignScript(channelDescriptor * cd_p, script_data * data_p); + +int iapi_SetChannelEventMapping(unsigned char event, unsigned long channel_map); +#endif /* MCU */ + +#endif /* _iapiHigh_h */ diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiLow.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiLow.h new file mode 100644 index 000000000000..2fb54ae141af --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiLow.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiLow.h + * + * $Id iapiLow.h $ + * + * Description: + * prototypes for low level function of I.API + * + * + * + * + * $Log iapiLow.h + * + * ***************************************************************************/ + +#ifndef _iapiLow_h +#define _iapiLow_h + +/* **************************************************************************** + * Boolean identifiers + *****************************************************************************/ +#define NO_OS 0 +#define LINUX 1 +#define SYMBIAN 2 +#define WINCE 3 + +/* **************************************************************************** + * Include File Section + *****************************************************************************/ +#include "sdmaStruct.h" +#include "iapiDefaults.h" +#include "iapiOS.h" +#ifdef MCU +#include "iapiLowMcu.h" +#endif /*MCU*/ +#if OS == NO_OS +#include <stdlib.h> +#elif OS == LINUX +#include <linux/types.h> +#endif + + +/* **************************************************************************** + * Macro-command Section + *****************************************************************************/ +#define MALLOC(x) (* iapi_Malloc)(x) +#define FREE(x) if (x!=NULL) (* iapi_Free)(x) + +#define GOTO_SLEEP(x) (iapi_GotoSleep)(x) +#define INIT_SLEEP(x) (iapi_InitSleep)(x) + +/* **************************************************************************** + * Public Function Prototype Section + *****************************************************************************/ +void iapi_lowStartChannel ( unsigned char channel ); +void iapi_lowStopChannel ( unsigned char channel ); +void iapi_AttachCallbackISR (channelDescriptor * cd_p, + void (* func_p)(channelDescriptor * cd_p, void * arg)); +void iapi_DetachCallbackISR (channelDescriptor * cd_p); +void iapi_ChangeCallbackISR (channelDescriptor * cd_p, + void (* func_p)(channelDescriptor * cd_p, void * arg)); +void iapi_lowSynchChannel ( unsigned char channel ); +void iapi_SetBufferDescriptor(bufferDescriptor *bd_p, unsigned char command, + unsigned char status, unsigned short count, + void * buffAddr, void * extBufferAddr); + +#endif /* _iapiLow_h */ diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiLowDsp.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiLowDsp.h new file mode 100644 index 000000000000..0adaa2548b9e --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiLowDsp.h @@ -0,0 +1,50 @@ +/* + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* **************************************************************************** + * + * File: iapiLowDsp.h + * + * $Id iapiLowDsp.h $ + * + * Description: + * prototypes for low level function of I.API for DSP side only + * + * + * + * + * $Log iapiLowDsp.h + * + * ***************************************************************************/ + +#ifndef _iapiLowDsp_h +#define _iapiLowDsp_h + +/* **************************************************************************** + * Include File Section + *****************************************************************************/ + + +/* **************************************************************************** + * Public Function Prototype Section + *****************************************************************************/ +/* WARNING !!!!! + * This file is empty and it is normal, because there is no low level functions + * dedicated to the DSP but the file (iapi_LowDsp.h) must still exist because + * some project directly links the file. Previously, there were function + * iapi_EnableInterrupts,iapi_DisableInterrupts,iapi_WaitCore,iapi_StartChannel + * iapi_StopChannel but they are common to both MCU and DSP, so they have been + * moved to iapi_Low.h file. + */ + +#endif /* _iapiLowDsp_h */ diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiLowMcu.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiLowMcu.h new file mode 100644 index 000000000000..12bea564c116 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiLowMcu.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiLowMcu.h + * + * $Id iapiLowMcu.h $ + * + * Description: + * prototypes for low level function of I.API of MCU side only + * + * + * + * + * $Log iapiLowMcu.h $ + * + * ***************************************************************************/ + +#ifndef _iapiLowMcu_h +#define _iapiLowMcu_h + +/****************************************************************************** + * Include File Section + *****************************************************************************/ +#include "sdmaStruct.h" +#include "iapiDefaults.h" + +/* **************************************************************************** + * Public Function Prototype Section + * ***************************************************************************/ + + +void iapi_InitChannelTables ( void ); +int iapi_ChannelConfig(unsigned char channel, unsigned eventOverride, + unsigned mcuOverride, unsigned dspOverride); +int iapi_Channel0Command(channelDescriptor * cd_p, void * buf, + unsigned short nbyte, unsigned char command); +void iapi_lowGetScript(channelDescriptor * cd_p, void * buf, unsigned short size, + unsigned long address); +void iapi_lowGetContext(channelDescriptor * cd_p, void * buf, + unsigned char channel); +void iapi_lowSetScript(channelDescriptor * cd_p, void * buf, unsigned short nbyte, + unsigned long destAddr); +void iapi_lowSetContext(channelDescriptor * cd_p, void * buf, + unsigned char channel); +int iapi_lowAssignScript(channelDescriptor * cd_p, script_data * data_p); + +int iapi_lowSetChannelEventMapping(unsigned char event, unsigned long channel_map); + +#endif /* _iapiLowMcu_h */ diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiMiddle.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiMiddle.h new file mode 100644 index 000000000000..2470ffcaabf1 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiMiddle.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiMiddle.h + * + * $Id iapiMiddle.h $ + * + * Description: + * prototypes for middle level function of I.API + * + * + * + * + * $Log iapiMiddle.h + * + * ***************************************************************************/ + +#ifndef _iapiMiddle_h +#define _iapiMiddle_h + +/* **************************************************************************** + * Include File Section + ******************************************************************************/ +#include "sdmaStruct.h" +#ifdef MCU +#include "iapiMiddleMcu.h" +#endif /* MCU */ + +/* **************************************************************************** + * Public Function Prototype Section + ******************************************************************************/ +bufferDescriptor * iapi_AllocBD (channelControlBlock * ccb_p); +int iapi_AllocContext(contextData ** ctxd_p, unsigned char channel); +int iapi_AllocChannelDesc(channelDescriptor ** cd_p, unsigned char channel); +int iapi_ChangeChannelDesc (channelDescriptor * cd_p, + unsigned char whatToChange, unsigned long newval); +void iapi_InitializeCallbackISR (void (* func_p)(channelDescriptor * cd_p, + void * arg)); +int iapi_InitializeMemory (channelControlBlock * ccb_p); + +#endif /* iapiMiddle_h */ diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiMiddleMcu.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiMiddleMcu.h new file mode 100644 index 000000000000..a47c02d4440b --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiMiddleMcu.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiMiddleMcu.h + * + * $Id iapiMiddleMcu.h $ + * + * Description: + * prototypes for middle level function of I.API + * + * + * + * + * $Log iapiMiddleMcu.h + * + * ***************************************************************************/ + +#ifndef _iapiMiddleMcu_h +#define _iapiMiddleMcu_h + +/* **************************************************************************** + * Include File Section + ******************************************************************************/ +#include "sdmaStruct.h" + +/* **************************************************************************** + * Public Function Prototype Section + ******************************************************************************/ + +#endif /* iapiMiddleMcu_h */ diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiOS.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiOS.h new file mode 100644 index 000000000000..82b6349dbe57 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiOS.h @@ -0,0 +1,86 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiOS.h + * + * $Id iapiOS.h $ + * + * Description: + * prototypes for OS level function of I.API + * + * + * + * + * $Log iapiOS.h + * + * ***************************************************************************/ + +#ifndef _iapiOS_h +#define _iapiOS_h + +/* **************************************************************************** + * Boolean identifiers + *****************************************************************************/ +#define NO_OS 0 +#define LINUX 1 +#define SYMBIAN 2 +#define WINCE 3 + +/* **************************************************************************** + * Include File Section + *****************************************************************************/ +#include "sdmaStruct.h" +#include "iapiDefaults.h" +#ifdef MCU +#include "iapiLowMcu.h" +#endif /*MCU*/ +#if OS == NO_OS +#include <stdlib.h> +#elif OS == LINUX +#include <linux/types.h> +#endif + +/* **************************************************************************** + * Macro-command Section + *****************************************************************************/ +#define MALLOC(x) (* iapi_Malloc)(x) +#define FREE(x) if (x!=NULL) (* iapi_Free)(x) + +#define GOTO_SLEEP(x) (iapi_GotoSleep)(x) +#define INIT_SLEEP(x) (iapi_InitSleep)(x) + +/* **************************************************************************** + * Public Function Prototype Section + *****************************************************************************/ + +extern void*(* iapi_Malloc) (size_t size); +extern void (* iapi_Free) (void * ptr); + +extern void*(* iapi_Virt2Phys) (void * ptr); +extern void*(* iapi_Phys2Virt) (void * ptr); + +extern void (* iapi_WakeUp)(int); +extern void (* iapi_GotoSleep)(int); +extern void (* iapi_InitSleep)(int); + +extern void*(* iapi_memcpy)(void *dest, const void *src, size_t count); +extern void*(* iapi_memset)(void *dest, int c, size_t count); + +extern void (* iapi_EnableInterrupts)(void); +extern void (* iapi_DisableInterrupts)(void); + +extern int (* iapi_GetChannel)(int); +extern int (* iapi_ReleaseChannel)(int); + +#endif /* _iapiOS_h */ diff --git a/arch/arm/plat-mxc/sdma/iapi/include/sdmaStruct.h b/arch/arm/plat-mxc/sdma/iapi/include/sdmaStruct.h new file mode 100644 index 000000000000..72e92d9308e6 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/include/sdmaStruct.h @@ -0,0 +1,417 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: sdmaStruct.h + * + * $Id sdmaStruct.h $ + * + * Description: provides necessary definitions and inclusion for ipcmStruct.c + * + * $Log $ + * + *****************************************************************************/ +#ifndef _sdmaStruct_h +#define _sdmaStruct_h + +/* **************************************************************************** + * Include File Section + ******************************************************************************/ + +/* **************************************************************************** + * Macro-command Section + ******************************************************************************/ + +/** + * Identifier NULL + */ +#ifndef NULL +#define NULL 0 +#endif + +/** + * Boolean identifiers + */ +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +/** + * Number of channel + */ +#define CH_NUM 32 +/** + * Channel configuration + */ +#define DONT_OWN_CHANNEL 0 +#define OWN_CHANNEL 1 + +/** + * Ownership (value defined to computed decimal value) + */ +#define CH_OWNSHP_OFFSET_EVT 0 +#define CH_OWNSHP_OFFSET_MCU 1 +#define CH_OWNSHP_OFFSET_DSP 2 +/** + * Indexof the greg which holds address to start a script from when channel + * becomes current. + */ +#define SDMA_NUMBER_GREGS 8 + +/** + * Channel contexts management + */ + +#define CHANNEL_CONTEXT_BASE_ADDRESS 0x800 +/** + * Buffer descriptor status values. + */ +#define BD_DONE 0x01 +#define BD_WRAP 0x02 +#define BD_CONT 0x04 +#define BD_INTR 0x08 +#define BD_RROR 0x10 +#define BD_LAST 0x20 +#define BD_EXTD 0x80 + + +/** + * Data Node descriptor status values. + */ +#define DND_END_OF_FRAME 0x80 +#define DND_END_OF_XFER 0x40 +#define DND_DONE 0x20 +#define DND_UNUSED 0x01 + +/** + * IPCV2 descriptor status values. + */ +#define BD_IPCV2_END_OF_FRAME 0x40 + + +#define IPCV2_MAX_NODES 50 +/** + * Error bit set in the CCB status field by the SDMA, + * in setbd routine, in case of a transfer error + */ +#define DATA_ERROR 0x10000000 + +/** + * Buffer descriptor commands. + */ +#define C0_ADDR 0x01 +#define C0_LOAD 0x02 +#define C0_DUMP 0x03 +#define C0_SETCTX 0x07 +#define C0_GETCTX 0x03 +#define C0_SETDM 0x01 +#define C0_SETPM 0x04 +#define C0_GETDM 0x02 +#define C0_GETPM 0x08 +/** + * Transfer types, encoded in the BD command field + */ +#define TRANSFER_32BIT 0x00 +#define TRANSFER_8BIT 0x01 +#define TRANSFER_16BIT 0x02 +#define TRANSFER_24BIT 0x03 +/** + * Change endianness indicator in the BD command field + */ +#define CHANGE_ENDIANNESS 0x80 +/** + * Size in bytes + */ +#define SDMA_BD_SIZE 8 +#define SDMA_EXTENDED_BD_SIZE 12 +#define BD_NUMBER 4 +/** + * Channel interrupt policy + */ +#define DEFAULT_POLL 0 +#define CALLBACK_ISR 1 +/** + * Channel status + */ +#define UNINITIALIZED 0 +#define INITIALIZED 1 + +/** + * IoCtl particular values + */ +#define SET_BIT_ALL 0xFFFFFFFF +#define BD_NUM_OFFSET 16 +#define BD_NUM_MASK 0xFFFF0000 + +/** + * Maximum values for IoCtl calls, used in high or middle level calls + */ +#define MAX_BD_NUM 256 +#define MAX_BD_SIZE 65536 +#define MAX_BLOCKING 2 +#define MAX_SYNCH 2 +#define MAX_OWNERSHIP 8 +#define MAX_CH_PRIORITY 8 +#define MAX_TRUST 2 +#define MAX_WML 256 + + +/** + * Access to channelDescriptor fields + */ +enum { + IAPI_CHANNELNUMBER, + IAPI_BUFFERDESCNUMBER, + IAPI_BUFFERSIZE, + IAPI_BLOCKING, + IAPI_CALLBACKSYNCH, + IAPI_OWNERSHIP, + IAPI_PRIORITY, + IAPI_TRUST, + IAPI_UNUSED, + IAPI_CALLBACKISR_PTR, + IAPI_CCB_PTR, + IAPI_BDWRAP, + IAPI_WML +}; + +/** + * Default values for channel descriptor - nobody ownes the channel + */ +#define CD_DEFAULT_OWNERSHIP 7 + + +/** + * User Type Section + */ + +/** + * Command/Mode/Count of buffer descriptors + */ + +#if (ENDIANNESS==B_I_G_ENDIAN) +typedef struct iapi_modeCount_ipcv2 { + unsigned long status : 8; /**< L, E , D bits stored here */ + unsigned long reserved : 8; + unsigned long count : 16; /**< <size of the buffer pointed by this BD */ +} modeCount_ipcv2; +#else +typedef struct iapi_modeCount_ipcv2 { + unsigned long count : 16; /**<size of the buffer pointed by this BD */ + unsigned long reserved : 8; /**Reserved*/ + unsigned long status : 8; /**< L, E , D bits stored here */ +} modeCount_ipcv2; +#endif +/** + * Data Node descriptor - IPCv2 + * (differentiated between evolutions of SDMA) + */ +typedef struct iapi_dataNodeDescriptor { + modeCount_ipcv2 mode; /**<command, status and count */ + void * bufferAddr; /**<address of the buffer described */ +} dataNodeDescriptor; + +#if (ENDIANNESS==B_I_G_ENDIAN) +typedef struct iapi_modeCount_ipcv1_v2 { + unsigned long endianness: 1; + unsigned long reserved: 7; + unsigned long status : 8; /**< E,R,I,C,W,D status bits stored here */ + unsigned long count : 16; /**< size of the buffer pointed by this BD */ +} modeCount_ipcv1_v2; +#else +typedef struct iapi_modeCount_ipcv1_v2 { + unsigned long count : 16; /**<size of the buffer pointed by this BD */ + unsigned long status : 8; /**< E,R,I,C,W,D status bits stored here */ + unsigned long reserved: 7; + unsigned long endianness: 1; +} modeCount_ipcv1_v2; +#endif +/** + * Buffer descriptor + * (differentiated between evolutions of SDMA) + */ +typedef struct iapi_bufferDescriptor_ipcv1_v2 { + modeCount_ipcv1_v2 mode; /**<command, status and count */ + void * bufferAddr; /**<address of the buffer described */ + void * extBufferAddr; /**<extended buffer address */ +} bufferDescriptor_ipcv1_v2; + + +/** + * Mode/Count of data node descriptors - IPCv2 + */ + +#if (ENDIANNESS==B_I_G_ENDIAN) +typedef struct iapi_modeCount { + unsigned long command : 8; /**< command mostlky used for channel 0 */ + unsigned long status : 8; /**< E,R,I,C,W,D status bits stored here */ + unsigned long count : 16; /**< size of the buffer pointed by this BD */ +} modeCount; +#else +typedef struct iapi_modeCount { + unsigned long count : 16; /**<size of the buffer pointed by this BD */ + unsigned long status : 8; /**< E,R,I,C,W,D status bits stored here */ + unsigned long command : 8; /**< command mostlky used for channel 0 */ +} modeCount; +#endif + + +/** + * Buffer descriptor + * (differentiated between evolutions of SDMA) + */ +typedef struct iapi_bufferDescriptor { + modeCount mode; /**<command, status and count */ + void * bufferAddr; /**<address of the buffer described */ + void * extBufferAddr; /**<extended buffer address */ +} bufferDescriptor; + + + +struct iapi_channelControlBlock; +struct iapi_channelDescriptor; +/** + * Channel Descriptor + */ +typedef struct iapi_channelDescriptor { + unsigned char channelNumber ;/**<stores the channel number */ + unsigned char bufferDescNumber;/**<number of BD's automatically allocated for this channel */ + unsigned short bufferSize ;/**<size (in bytes) of buffer descriptors */ + unsigned long blocking :3;/**<blocking / non blocking feature selection */ + unsigned long callbackSynch :1;/**<polling/ callback method selection */ + unsigned long ownership :3;/**<ownership of the channel (host+dedicated+event)*/ + unsigned long priority :3;/**<reflects the SDMA channel priority register */ + unsigned long trust :1;/**<trusted buffers or kernel allocated */ + unsigned long useDataSize :1;/**<indicates if the dataSize field is meaningfull */ + unsigned long dataSize :2;/**<data transfer size - 8,16,24 or 32 bits*/ + unsigned long forceClose :1;/**<if TRUE, close channel even with BD owned by SDMA*/ + unsigned long scriptId :7;/**<number of the script */ + unsigned long watermarkLevel:10;/**<Watermark level for the peripheral access*/ + unsigned long eventMask1; /**<First Event mask */ + unsigned long eventMask2; /**<Second Event mask */ + unsigned long peripheralAddr; /**<Address of the peripheral or its fifo when needed */ + void (* callbackISR_ptr)(struct iapi_channelDescriptor*, void*); /**<pointer to the callback function (or NULL) */ + struct iapi_channelControlBlock * ccb_ptr; /**<pointer to the channel control block associated to this channel */ +} channelDescriptor; + +/** + * Channel Status + */ +typedef struct iapi_channelStatus { + unsigned long unused :29;/**<*/ + unsigned long openedInit : 1;/**<channel is initialized or not */ + unsigned long stateDirection: 1;/**<sdma is reading/writing (as seen from channel owner core) */ + unsigned long execute : 1;/**<channel is being processed (started) or not */ +} channelStatus; + +/** + * Channel control Block + */ +typedef struct iapi_channelControlBlock { + bufferDescriptor * currentBDptr; /**<current buffer descriptor processed */ + bufferDescriptor * baseBDptr; /**<first element of buffer descriptor array */ + channelDescriptor * channelDescriptor; /**<pointer to the channel descriptor */ + channelStatus status; /**<open/close ; started/stopped ; read/write */ +} channelControlBlock; + +/** + * Context structure. + */ +#if (ENDIANNESS==B_I_G_ENDIAN) +typedef struct iapi_stateRegisters { + unsigned long sf : 1;/**<source falut while loading data */ + unsigned long unused0: 1;/**<*/ + unsigned long rpc :14;/**<return program counter */ + unsigned long t : 1;/**<test bit:status of arithmetic & test instruction*/ + unsigned long unused1: 1;/**<*/ + unsigned long pc :14;/**<program counter */ + unsigned long lm : 2;/**<loop mode */ + unsigned long epc :14;/**<loop end program counter */ + unsigned long df : 1;/**<destiantion falut while storing data */ + unsigned long unused2: 1;/**<*/ + unsigned long spc :14;/**<loop start program counter */ +} stateRegiters; +#else +typedef struct iapi_stateRegisters { + unsigned long pc :14;/**<program counter */ + unsigned long unused1: 1;/**<*/ + unsigned long t : 1;/**<test bit: status of arithmetic & test instruction*/ + unsigned long rpc :14;/**<return program counter */ + unsigned long unused0: 1;/**<*/ + unsigned long sf : 1;/**<source falut while loading data */ + unsigned long spc :14;/**<loop start program counter */ + unsigned long unused2: 1;/**<*/ + unsigned long df : 1;/**<destiantion falut while storing data */ + unsigned long epc :14;/**<loop end program counter */ + unsigned long lm : 2;/**<loop mode */ +} stateRegiters; +#endif + +/** + * This is SDMA version of SDMA + */ +typedef struct iapi_contextData { + stateRegiters channelState; /**<channel state bits */ + unsigned long gReg[ SDMA_NUMBER_GREGS ]; /**<general registers */ + unsigned long mda; /**<burst dma destination address register */ + unsigned long msa; /**<burst dma source address register */ + unsigned long ms; /**<burst dma status register */ + unsigned long md; /**<burst dma data register */ + unsigned long pda; /**<peripheral dma destination address register */ + unsigned long psa; /**<peripheral dma source address register */ + unsigned long ps; /**<peripheral dma status register */ + unsigned long pd; /**<peripheral dma data register */ + unsigned long ca; /**<CRC polynomial register */ + unsigned long cs; /**<CRC accumulator register */ + unsigned long dda; /**<dedicated core destination address register */ + unsigned long dsa; /**<dedicated core source address register */ + unsigned long ds; /**<dedicated core status register */ + unsigned long dd; /**<dedicated core data register */ + unsigned long scratch0; /**<scratch */ + unsigned long scratch1; /**<scratch */ + unsigned long scratch2; /**<scratch */ + unsigned long scratch3; /**<scratch */ + unsigned long scratch4; /**<scratch */ + unsigned long scratch5; /**<scratch */ + unsigned long scratch6; /**<scratch */ + unsigned long scratch7; /**<scratch */ + +} contextData; + +/** + *This structure holds the necessary data for the assignment in the + * dynamic channel-script association + */ +typedef struct iapi_script_data { + unsigned short load_address;/**<start address of the script*/ + unsigned long wml; /**<parameters for the channel descriptor*/ + unsigned long shp_addr; /**<shared peripheral base address*/ + unsigned long event_mask1; /**<First Event mask */ + unsigned long event_mask2; /**<Second Event mask */ +} script_data; + +/** + *This structure holds the the useful bits of the CONFIG register + */ +typedef struct iapi_configs_data { + unsigned long dspdma :1; /*indicates if the DSPDMA is used */ + unsigned long rtdobs :1; /*indicates if Real-Time Debug pins are enabled */ + unsigned long acr :1; /**indicates if AHB freq /core freq = 2 or 1 */ + unsigned long csm :2; /**indicates which context switch mode is selected*/ +} configs_data; + +#endif /* _sdmaStruct_h */ diff --git a/arch/arm/plat-mxc/sdma/iapi/src/Makefile b/arch/arm/plat-mxc/sdma/iapi/src/Makefile new file mode 100644 index 000000000000..0bb3a14edffc --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/src/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for I.API sources. +# + +obj-y += iapiLow.o iapiLowMcu.o iapiMiddle.o iapiMiddleMcu.o iapiHigh.o iapiDefaults.o iapiOS.o diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiDefaults.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiDefaults.c new file mode 100644 index 000000000000..345c240cb15c --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiDefaults.c @@ -0,0 +1,110 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiDefaults.c + * + * $Id iapiDefaults.c $ + * + * Description: + * This library is written in C to guarantee functionality and integrity in + * the usage of SDMA virtual DMA channels. This API (Application Programming + * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE + * fashion. + * + * Usage: + * + * Files: + * + * +* / + * + * $Log iapiDefaults.c $ + * + *****************************************************************************/ + +/* **************************************************************************** + * Include File Section + ******************************************************************************/ +#include "iapiDefaults.h" + +/* **************************************************************************** + * Global Variable Section + ******************************************************************************/ + +/** + * @brief System Call-back ISRs Table + */ +void (* callbackIsrTable[CH_NUM])(channelDescriptor* cd_p, void* arg); + +/** + * @brief User registered pointers table + */ +void * userArgTable[CH_NUM]; + +/** + * @brief Pointer to the first CCB in the CCB array + */ +channelControlBlock * iapi_CCBHead = NULL; + + +/**Default channel description. + * + * Initialization values are:\n + * - channelNumber = 0 + * - bufferDescNumber = 1 + * - bufferSize = 8 + * - blocking = 0 + * - callbackSynch = DEFAULT_POLL + * - ownership = CD_DEFAULT_OWNERSHIP + * - priority = 1 + * - trust = TRUE + * - useDataSize = 0 + * - dataSize = 0 + * - forceClose = 0 + * - scriptId = 0 + * - watermarkLevel = 0 + * - eventMask1 = 0 + * - eventMask2 = 0 + * - peripheralAddr = NULL + * - callbackISR_ptr = NULL + * - iapi_channelControlBlock = NULL + */ +channelDescriptor iapi_ChannelDefaults = {0, 1, 8, 0, DEFAULT_POLL, + CD_DEFAULT_OWNERSHIP, 1, TRUE, 0, 0, 0, 0, + 0, 0x00, 0x00, 0x00, NULL, NULL}; + +/** + * Integrated error management + */ +unsigned int iapi_errno = 0; +volatile unsigned long iapi_SDMAIntr = 0; + +/* Default config register. + * Initialization values are: + * dspdma used + * Real-Time Debug pins disabled + * AHB freq / core freq = 2 + * dynamic context switch +*/ +configs_data iapi_ConfigDefaults = {1, 0, 0, 3}; + +#ifdef SDMA_SKYE +/* Default sdma State : UNDEF + *possible value are UNDEF, OPEN, LOCK, CLOSED, CLOSE_LOCK + */ + +sdmaState iapi_SdmaState= UNDEF; +#endif + +/* ***************************************************************************/ diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiHigh.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiHigh.c new file mode 100644 index 000000000000..426497ee9b3b --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiHigh.c @@ -0,0 +1,2796 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiHigh.c + * + * $Id iapiHigh.c $ + * + * Description: + * This library is written in C to guarantee functionality and integrity in + * the usage of SDMA virtual DMA channels. This API (Application Programming + * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE + * fashion. + * These are the HIGH level functions of the I.API. + * + * + * / + * + * $Log iapiHigh.c $ + * + *****************************************************************************/ + +/* **************************************************************************** + * Include File Section + *****************************************************************************/ +#include <stdarg.h> +#include <string.h> + +#include "epm.h" +#include "iapi.h" + +/* **************************************************************************** + * External Reference Section (for compatibility with already developed code) + *****************************************************************************/ +static void iapi_read_ipcv2_callback(struct iapi_channelDescriptor* cd_p, void* data); + +/* **************************************************************************** + * Global Variable Section + *****************************************************************************/ +#define MAX_CHANNEL 32 + +static dataNodeDescriptor* dnd_read_control_struct[MAX_CHANNEL]; + +/* MASK to get Nullify all the bits of Status in Data Node descriptor apart from L, E and D*/ + +#define GET_LED_MASK 0xE0 + +/*Table defines mapping of Data Node Descriptor to Buffer Descriptor status*/ + +static unsigned char dnd_2_bd_status[]= +{ +0x85, /*00 L = 0, E = 0, D = 0*/ +0x00, /*01*/ +0x00, /*02*/ +0x00, /*03*/ +0x00, /*04*/ +0x00, /*05*/ +0x00, /*06*/ +0x00, /*07*/ +0x00, /*08*/ +0x00, /*09*/ +0x00, /*0A*/ +0x00, /*0B*/ +0x00, /*0C*/ +0x00, /*0D*/ +0x00, /*0E*/ +0x00, /*0F*/ +0x00, /*10*/ +0x00,/*11*/ +0x00,/*12*/ +0x00,/*13*/ +0x00,/*14*/ +0x00,/*15*/ +0x00,/*16*/ +0x00,/*17*/ +0x00,/*18*/ +0x00,/*19*/ +0x00,/*1A*/ +0x00,/*1B*/ +0x00,/*1C*/ +0x00,/*1D*/ +0x00,/*1E*/ +0x00,/*1F*/ +0x84,/*20 L = 0, E = 0, D = 1 */ +0x00,/*21*/ +0x00,/*22*/ +0x00,/*23*/ +0x00,/*24*/ +0x00,/*25*/ +0x00,/*26*/ +0x00,/*27*/ +0x00,/*28*/ +0x00,/*29*/ +0x00,/*2A*/ +0x00,/*2B*/ +0x00,/*2C*/ +0x00,/*2D*/ +0x00,/*2E*/ +0x00,/*2F*/ +0x00,/*30*/ +0x00,/*31*/ +0x00,/*32*/ +0x00,/*33*/ +0x00,/*34*/ +0x00,/*35*/ +0x00,/*36*/ +0x00,/*37*/ +0x00,/*38*/ +0x00,/*39*/ +0x00,/*3A*/ +0x00,/*3B*/ +0x00,/*3C*/ +0x00,/*3D*/ +0x00,/*3E*/ +0x00,/*3F*/ +0xAB,/*40 L = 0, E = 1, D = 0*/ +0x00,/*41*/ +0x00,/*42*/ +0x00,/*43*/ +0x00,/*44*/ +0x00,/*45*/ +0x00,/*46*/ +0x00,/*47*/ +0x00,/*48*/ +0x00,/*49*/ +0x00,/*4A*/ +0x00,/*4B*/ +0x00,/*4C*/ +0x00,/*4D*/ +0x00,/*4E*/ +0x00,/*4F*/ +0x00,/*50*/ +0x00,/*51*/ +0x00,/*52*/ +0x00,/*53*/ +0x00,/*54*/ +0x00,/*55*/ +0x00,/*56*/ +0x00,/*57*/ +0x00,/*58*/ +0x00,/*59*/ +0x00,/*5A*/ +0x00,/*5B*/ +0x00,/*5C*/ +0x00,/*5D*/ +0x00,/*5E*/ +0x00,/*5F*/ +0xAA,/*60 L = 0, E = 1, D = 1*/ +0x00,/*61*/ +0x00,/*62*/ +0x00,/*63*/ +0x00,/*64*/ +0x00,/*65*/ +0x00,/*66*/ +0x00,/*67*/ +0x00,/*68*/ +0x00,/*69*/ +0x00,/*6A*/ +0x00,/*6B*/ +0x00,/*6C*/ +0x00,/*6D*/ +0x00,/*6E*/ +0x00,/*6F*/ +0x00,/*70*/ +0x00,/*71*/ +0x00,/*72*/ +0x00,/*73*/ +0x00,/*74*/ +0x00,/*75*/ +0x00,/*76*/ +0x00,/*77*/ +0x00,/*78*/ +0x00,/*79*/ +0x00,/*7A*/ +0x00,/*7B*/ +0x00,/*7C*/ +0x00,/*7D*/ +0x00,/*7E*/ +0x00,/*7F*/ +0xC5,/*80 L = 1, E = 0, D = 0*/ +0x00,/*81*/ +0x00,/*82*/ +0x00,/*83*/ +0x00,/*84*/ +0x00,/*85*/ +0x00,/*86*/ +0x00,/*87*/ +0x00,/*88*/ +0x00,/*89*/ +0x00,/*8A*/ +0x00,/*8B*/ +0x00,/*8C*/ +0x00,/*8D*/ +0x00,/*8E*/ +0x00,/*8F*/ +0x00,/*90*/ +0x00,/*91*/ +0x00,/*92*/ +0x00,/*93*/ +0x00,/*94*/ +0x00,/*95*/ +0x00,/*96*/ +0x00,/*97*/ +0x00,/*98*/ +0x00,/*99*/ +0x00,/*9A*/ +0x00,/*9B*/ +0x00,/*9C*/ +0x00,/*9D*/ +0x00,/*9E*/ +0x00,/*9F*/ +0xC4,/*A0 L = 1, E = 0, D = 1*/ +0x00,/*A1*/ +0x00,/*A2*/ +0x00,/*A3*/ +0x00,/*A4*/ +0x00,/*A5*/ +0x00,/*A6*/ +0x00,/*A7*/ +0x00,/*A8*/ +0x00,/*A9*/ +0x00,/*AA*/ +0x00,/*AB*/ +0x00,/*AC*/ +0x00,/*AD*/ +0x00,/*AE*/ +0x00,/*AF*/ +0x00,/*B0*/ +0x00,/*B1*/ +0x00,/*B2*/ +0x00,/*B3*/ +0x00,/*B4*/ +0x00,/*B5*/ +0x00,/*B6*/ +0x00,/*B7*/ +0x00,/*B8*/ +0x00,/*B9*/ +0x00,/*BA*/ +0x00,/*BB*/ +0x00,/*BC*/ +0x00,/*BD*/ +0x00,/*BE*/ +0x00,/*BF*/ +0xEB,/*C0 L = 1, E = 1, D = 0*/ +0x00,/*C1*/ +0x00,/*C2*/ +0x00,/*C3*/ +0x00,/*C4*/ +0x00,/*C5*/ +0x00,/*C6*/ +0x00,/*C7*/ +0x00,/*C8*/ +0x00,/*C9*/ +0x00,/*CA*/ +0x00,/*CB*/ +0x00,/*CC*/ +0x00,/*CD*/ +0x00,/*CE*/ +0x00,/*CF*/ +0x00,/*D0*/ +0x00,/*D1*/ +0x00,/*D2*/ +0x00,/*D3*/ +0x00,/*D4*/ +0x00,/*D5*/ +0x00,/*D6*/ +0x00,/*D7*/ +0x00,/*D8*/ +0x00,/*D9*/ +0x00,/*DA*/ +0x00,/*DB*/ +0x00,/*DC*/ +0x00,/*DD*/ +0x00,/*DE*/ +0x00,/*DF*/ +0xEA,/*E0 L = 1, E = 1, D = 1*/ +0x00,/*E1*/ +0x00,/*E2*/ +0x00,/*E3*/ +0x00,/*E4*/ +0x00,/*E5*/ +0x00,/*E6*/ +0x00,/*E7*/ +0x00,/*E8*/ +0x00,/*E9*/ +0x00,/*EA*/ +0x00,/*EB*/ +0x00,/*EC*/ +0x00,/*ED*/ +0x00,/*EE*/ +0x00,/*EF*/ +0x00,/*F0*/ +0x00,/*F1*/ +0x00,/*F2*/ +0x00,/*F3*/ +0x00,/*F4*/ +0x00,/*F5*/ +0x00,/*F6*/ +0x00,/*F7*/ +0x00,/*F8*/ +0x00,/*F9*/ +0x00,/*FA*/ +0x00,/*FB*/ +0x00,/*FC*/ +0x00,/*FD*/ +0x00,/*FE*/ +0x00/*FF*/ +}; +/* **************************************************************************** + * Function Section + *****************************************************************************/ + +/* ***************************************************************************/ +/**Opens an SDMA channel to be used by the library. + * + * <b>Algorithm:</b>\n + * + * - Check if initialization is necessary. + * - Check that user initialized OS dependant functions. + * - Test validity of input parameters + * - Check whole channel control block data structure + * - Finish initializations (tables with default values) + * - Initialize channel 0 is dedicated to communications with SDMA + * - Check channel control block definition + * - if the channel descriptor is not initialized, initialize it with + * the default value + * - If buffer descriptor already allocated, exit with iapi_errno filled + * complete the lowest bits with the number of 'D' bits set + * - Buffer Descriptors allocation + * - Channel's configuration properties (mcu side only) + * - read/write direction => enable/disable channel setting + * + * @param *cd_p -If channelNumber is 0, it is pointer to channel descriptor for the channnel 0 to be opened and + has default values. + * For other channels,this function should be called after channel 0 has been opened, and it is channel descriptor for + channel 0.Must be allocated. + * @param channelNumber channel to be opened + * + * @return + * - IAPI_SUCCESS : OK + * - -iapi_errno : close failed, return negated value of iapi_errno + */ +int +iapi_Open (channelDescriptor * cd_p, unsigned char channelNumber) +{ + channelControlBlock * ccb_p; + channelControlBlock * local_ccb_p; + channelDescriptor * local_cd_p; + bufferDescriptor * bd_p; + unsigned char index = 0; + int result = IAPI_SUCCESS; +#ifdef MCU + volatile unsigned long * channelPriorityMatx; +#endif /* MCU */ + + + /* + * 1. Check if initialization is necessary + */ + if (cd_p == NULL){ + result = IAPI_ERR_CD_UNINITIALIZED | + IAPI_ERR_CH_AVAILABLE | channelNumber; + iapi_errno = result; + return -result; + } + + /* Verify these functions every time*/ + if((iapi_GetChannel == NULL)||(iapi_ReleaseChannel == NULL)) + { + result = IAPI_ERR_NO_OS_FN | channelNumber; + iapi_errno = result; + return -result; + } + + /* Try to aquire channel*/ + if(iapi_GetChannel(channelNumber) != 0) + { + result = IAPI_ERR_CH_IN_USE | channelNumber; + iapi_errno = result; + return -result; + } + + if (channelNumber == 0 && cd_p->ccb_ptr == NULL){ + /* Verify that the user initialized all OS dependant functions required + * by the library. + */ + if((iapi_Malloc == NULL)||(iapi_Free == NULL)||(iapi_Virt2Phys == NULL)|| + (iapi_Phys2Virt == NULL)||(iapi_GotoSleep == NULL)|| + (iapi_WakeUp == NULL)||(iapi_InitSleep == NULL)||(iapi_memset == NULL)|| + (iapi_memcpy == NULL)) + { + result = IAPI_ERR_NO_OS_FN | channelNumber; + iapi_errno = result; + iapi_ReleaseChannel(channelNumber); + return -result; + } + /* Whole channel control block data structure */ + ccb_p = (channelControlBlock *) + MALLOC(CH_NUM*sizeof(channelControlBlock)); + if (ccb_p == NULL){ + result = IAPI_ERR_CCB_ALLOC_FAILED | + IAPI_ERR_CH_AVAILABLE | channelNumber; + iapi_errno = result; + iapi_ReleaseChannel(channelNumber); + return -result; + } + /* Zero-out the CCB structures array just allocated*/ + iapi_memset(ccb_p, 0x00, CH_NUM*sizeof(channelControlBlock)); + /* Save the address of the CCB structures array*/ + iapi_CCBHead = ccb_p; + + cd_p->ccb_ptr = (struct iapi_channelControlBlock *)ccb_p; + ccb_p->channelDescriptor = cd_p; +#ifdef MCU + /* finish initializations */ + iapi_InitChannelTables(); +#endif /* MCU */ + /* Channel 0 is dedicated to communications with SDMA */ + cd_p->ownership = ((DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT ) | + ( OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU ) | + (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP )); + cd_p->bufferDescNumber = 1; + } + + /* + * 2. Check channel control block + */ + ccb_p = cd_p->ccb_ptr; + if (ccb_p == NULL){ + result = IAPI_ERR_NO_CCB_DEFINED | IAPI_ERR_CH_AVAILABLE|channelNumber; + iapi_errno = result; + iapi_ReleaseChannel(channelNumber); + return -result; + } + + /* Control block & Descriptor associated with the channel being worked on */ + local_ccb_p = &ccb_p[channelNumber]; + local_cd_p = ccb_p[channelNumber].channelDescriptor; + + /* If the channel is not initialized, initialize it with the default value */ + if (local_cd_p == NULL){ + result = iapi_AllocChannelDesc (&local_cd_p, channelNumber); + if ( result!= IAPI_SUCCESS) + { + iapi_ReleaseChannel(channelNumber); + return result; //is allready negated from iapi_AllocChannelDesc + } + + local_cd_p->ccb_ptr = (struct iapi_channelControlBlock *)local_ccb_p; + local_ccb_p->channelDescriptor = local_cd_p; + } + + /* + * 3. If buffer descriptor already allocated, exit with iapi_errno filled + */ + if ( local_ccb_p->baseBDptr != NULL ){ + bd_p = (bufferDescriptor *)iapi_Phys2Virt(local_ccb_p->baseBDptr); + result = IAPI_ERR_BD_ALLOCATED; + for (index=1 ; index< local_cd_p->bufferDescNumber ; index++){ + if ((bd_p->mode.status & BD_DONE) == BD_DONE){ + /* complete the lowest bits with the number of 'D' bits set */ + result++; + } + bd_p++; + } + iapi_errno = result; + iapi_ReleaseChannel(channelNumber); + return -result; + } + + /* + * 4. Buffer Descriptors allocation + */ + iapi_InitializeMemory(local_ccb_p); + +#ifdef MCU + /* + * 5. Channel's configuration properties (mcu side only) + */ + iapi_ChannelConfig( channelNumber, + ( local_cd_p->ownership >> CH_OWNSHP_OFFSET_EVT ) & 1UL, + ( local_cd_p->ownership >> CH_OWNSHP_OFFSET_MCU ) & 1UL, + ( local_cd_p->ownership >> CH_OWNSHP_OFFSET_DSP ) & 1UL); +#endif /* MCU */ + + /* Setting interrupt handling */ + iapi_ChangeCallbackISR(local_cd_p, local_cd_p->callbackISR_ptr); + + /* Call initialization fn for polling synch on this channel*/ + INIT_SLEEP(channelNumber); + + /* No user arg pointer yet*/ + userArgTable[cd_p->channelNumber]= NULL; + + /* + * 6. read/write direction => enable/disable channel + */ +#ifdef MCU + channelPriorityMatx = &SDMA_CHNPRI_0; + channelPriorityMatx[channelNumber] = 1; +#endif /* MCU */ + + local_ccb_p->status.openedInit = TRUE; + iapi_ReleaseChannel(channelNumber); + return IAPI_SUCCESS; +} + +/* ***************************************************************************/ +/** Attempts to read nbyte from the data buffer descriptor associated with the + * channel channelNumber, into the user's data buffer pointed to by buf. + * + * <b>Algorithm:</b>\n + * - Check data structures are properly initialized: + * - Channel descriptor validity + * - Control block & Descriptor associated with the channel being worked on + * - Check initialization has been done for trusted channels + * - If transfer data size is used, check validity of combination transfer + * size/requested bytes + * - Set the 'D' done bits on all buffer descriptors + * - Starting of the channel + * - Synchronization mechanism handling: + * - for callback: just exit function + * - for polling: call the synchronization function then read data from + * buffer until either nbyte parameter is reached or all buffer descriptors + * have been processed. + * + * <b>Notes:</b>\n + * 1) Virtual DMA SDMA channels are unidirectional, an iapi_Read authorized + * on a channel means that we are expecting to receive from the SDMA. The + * meaning of an interrupt received from the SDMA is therefore that the + * data has been copied from the SDMA to the host's data buffers and is + * already passed on upper layers of the application.\n + * + * @param *cd_p chanenl descriptor for the channel to read from + * @param *buf buffer to receive the data + * @param nbyte number of bytes to read from channel + * + * @return + * - number of bytes read + * - -iapi_errno : in case of failure return negated value of iapi_errno + */ +int +iapi_Read (channelDescriptor * cd_p, void * buf, unsigned short nbyte) +{ + int index = 0; + int readBytes; + int toRead; + int result = IAPI_SUCCESS; + unsigned int copyFinished; + int bufsize; + bufferDescriptor * bd_p; + channelControlBlock * ccb_p; + unsigned char * local_buf; + unsigned char chNum; + unsigned char div; + + iapi_errno = IAPI_ERR_NO_ERROR; + + /* + * 1. Check data structures are properly initialized + */ + /* Channel descriptor validity */ + if (cd_p == NULL){ + result = IAPI_ERR_CD_UNINITIALIZED; + iapi_errno = result; + return -result; + } + + /* Channel control block validity */ + if (cd_p->ccb_ptr == NULL){ + result = IAPI_ERR_CCB_UNINITIALIZED; + iapi_errno = result; + return -result; + } + + /* Control block & Descriptor associated with the channel being worked on */ + chNum = cd_p->channelNumber; + ccb_p = cd_p->ccb_ptr; + + /* Try to aquire channel*/ + if(iapi_GetChannel(chNum) != 0) + { + result = IAPI_ERR_CH_IN_USE | chNum; + iapi_errno = result; + return -result; + } + + /* Check if channel is already opened/initialized */ + if (ccb_p->status.openedInit == FALSE) { + result = IAPI_ERR_CHANNEL_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + + /* Buffer descriptor validity */ + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + if (bd_p == NULL ){ + result = IAPI_ERR_BD_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + + + /* Check initialization has been done for trusted channels */ + if (cd_p->trust == TRUE) { + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + for(index=0 ; index < cd_p->bufferDescNumber ; index++){ + if ((bd_p->bufferAddr == NULL) || (bd_p->mode.count == 0)){ + result = IAPI_ERR_BD_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + bd_p++; + } + } + + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + /*If transfer data size is used, check that the required read length is + * divisible by transfer data size expressed in bytes + */ + if(cd_p->useDataSize) + { + /*Check for divisibility only if data size different then 8bit*/ + if(cd_p->dataSize != TRANSFER_8BIT) + { + switch(cd_p->dataSize) + { + case TRANSFER_32BIT: + div = 4; + break; + case TRANSFER_16BIT: + div = 2; + break; + case TRANSFER_24BIT: + div = 3; + break; + /*we should not get to default*/ + default: + result = IAPI_ERR_INVALID_PARAMETER | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + /*check the total number of bytes requested*/ + if((nbyte % div) != 0) + { + result = IAPI_ERR_INVALID_PARAMETER | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + /*now check the length of every BD*/ + for(index=0 ; index < cd_p->bufferDescNumber ; index++) + { + if((bd_p->mode.count % div) != 0) + { + result = IAPI_ERR_INVALID_PARAMETER | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + bd_p++; + } + } + } + + /* + * 2. Set the 'D' done bits on all buffer descriptors + */ + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + for (index=0 ; index < cd_p->bufferDescNumber ; index++) { + bd_p->mode.status |= BD_DONE; + bd_p++; + } + + /* + * 3. Starting of the channel + */ + iapi_lowStartChannel (chNum); + ccb_p->status.execute = TRUE; + readBytes = 0; + + /* + * 4. Synchronization mechanism handling + */ + if( cd_p->callbackSynch == DEFAULT_POLL){ + iapi_SynchChannel(chNum); + + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + toRead = nbyte; + copyFinished = FALSE; + local_buf = (unsigned char *)buf; + + /* + * Check the 'RROR' bit on all buffer descriptors, set error number + * and return IAPI_FAILURE if set. + */ + for (index=0 ; index < cd_p->bufferDescNumber ; index++) + { + if(bd_p->mode.status & BD_RROR) + { + result = IAPI_ERR_RROR_BIT_READ | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + bd_p++; + } + + + /* + * 5. Read loop + */ + + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + while (!copyFinished) + { + if (!(bd_p->mode.status & BD_DONE)) + { + if (cd_p->trust == FALSE) { + bufsize = cd_p->bufferSize; + } else { + bufsize = bd_p->mode.count; + } + /*if L bit is set, read only "count" bytes and exit the loop*/ + if(bd_p->mode.status & BD_LAST) + { + bufsize = bd_p->mode.count; + copyFinished = TRUE; + } + if (toRead > bufsize) + { + if (cd_p->trust == FALSE) + { + iapi_memcpy(local_buf, iapi_Phys2Virt(bd_p->bufferAddr), bufsize); + local_buf += bufsize; + } + readBytes += bufsize; + toRead -= bufsize; + /*advance bd_p only if bit L is not set. The loop will exit anyway.*/ + if(!(bd_p->mode.status & BD_LAST)) + { + if (bd_p->mode.status & BD_WRAP) + { + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + } + else if(((bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr) + + (cd_p->bufferDescNumber - 1)*sizeof(bufferDescriptor)) != bd_p) + { + bd_p++; + } + else + { + /* finished here : end of buffer descriptors */ + copyFinished = TRUE; + } + } + } + else + { + if (cd_p->trust == FALSE) + { + iapi_memcpy(local_buf, iapi_Phys2Virt(bd_p->bufferAddr), toRead); + local_buf += toRead; + } + readBytes += toRead; + toRead = 0; + /* finished successfully : readBytes = nbytes */ + copyFinished = TRUE; + } + } + else + { + /* finished here : buffer not already done*/ + copyFinished = TRUE; + } + } + iapi_ReleaseChannel(chNum); + } + + /* + *If synchronization type is callback, the user of I.API must + *release the channel + */ + return readBytes; +} + +/* ***************************************************************************/ +/*Attempts to write nbyte from the buffer pointed to by buf to the channel + * data buffers associated with the opened channel number channelNumber + * + * <b>Algorithm:</b>\n + * + * - Check data structures are properly initialized: + * - Channel descriptor validity + * - Channel control block validity + * - Buffer descriptor validity + * - If transfer data size is used, check validity of combination transfer + * size/requested bytes + * - Write loop\n + * Write occurs in the buffer acceded form buffer descriptor and continues + * to the "next" buffer which can be:\n + * -# the last BD of the ring so re-start from beginning\n + * -# the last BD of the BD array but no ring so finish\n + * -# (general case) the next BD in the BD array\n + * And copy continues until data fit in the current buffer or the nbyte + * parameter is reached. + * - Starting of the channel + * + * <b>Notes:</b>\n + * 1) Virtual DMA SDMA channels are unidirectionnal, an iapi_Write authorized + * on a channel means that we are expecting to send to the SDMA. The + * meaning of an interrupt received from the SDMA is therfore that the + * data has been delivered to the SDMA. + * + * @param *cd_p chanenl descriptor for the channel to write to + * @param *buf buffer with data to be written + * @param nbyte number of bytes to write to channel + * + * @return + * - number of bytes written + * - -iapi_errno if failure + */ +int +iapi_Write (channelDescriptor * cd_p, void * buf, unsigned short nbyte) +{ + unsigned int writtenBytes = 0; + unsigned int toWrite; + int result = IAPI_SUCCESS; + unsigned int copyFinished; + unsigned int buffsize; + unsigned int index = 0; + bufferDescriptor * bd_p; + channelControlBlock * ccb_p; + unsigned char * local_buf; + unsigned char chNum; + unsigned char div; + + iapi_errno = IAPI_ERR_NO_ERROR; + + /* + * 1. Check data structures are properly initialized + */ + /* Channel descriptor validity */ + if (cd_p == NULL){ + result = IAPI_ERR_CD_UNINITIALIZED; + iapi_errno = result; + return -result; + } + + /* Channel control block validity */ + if (cd_p->ccb_ptr == NULL){ + result = IAPI_ERR_CCB_UNINITIALIZED; + iapi_errno = result; + return -result; + } + + /* Control block & Descriptpor associated with the channel being worked on */ + chNum = cd_p->channelNumber; + ccb_p = cd_p->ccb_ptr; + + /* Try to aquire channel*/ + if(iapi_GetChannel(chNum) != 0) + { + result = IAPI_ERR_CH_IN_USE | chNum; + iapi_errno = result; + return -result; + } + + /* Buffer descriptor validity */ + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + if (bd_p == NULL ){ + result = IAPI_ERR_BD_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + + /* Check initialization has been done for trusted channels */ + if (cd_p->trust == TRUE) { + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + for(index=0 ; index < cd_p->bufferDescNumber ; index++){ + if ((bd_p->bufferAddr == NULL) || (bd_p->mode.count == 0)){ + result = IAPI_ERR_BD_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + bd_p++; + } + } + + + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + /*If transfer data size is used, check that the required write length is + * divisible by transfer data size expressed in bytes + */ + if(cd_p->useDataSize) + { + /*Check for divisibility only if data size different then 8bit*/ + if(cd_p->dataSize != TRANSFER_8BIT) + { + switch(cd_p->dataSize) + { + case TRANSFER_32BIT: + div = 4; + break; + case TRANSFER_16BIT: + div = 2; + break; + case TRANSFER_24BIT: + div = 3; + break; + /*we should not get to default*/ + default: + result = IAPI_ERR_INVALID_PARAMETER | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + /*check the total number of bytes requested*/ + if((nbyte % div) != 0) + { + result = IAPI_ERR_INVALID_PARAMETER | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + /*now check the length of every BD*/ + for(index=0 ; index < cd_p->bufferDescNumber ; index++) + { + if((bd_p->mode.count % div) != 0) + { + result = IAPI_ERR_INVALID_PARAMETER | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + bd_p++; + } + } + } + + /* + * 2. Write loop + */ + + local_buf = (unsigned char *)buf; + toWrite = nbyte; + copyFinished = FALSE; + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + + while (!copyFinished){ + + /* variable buffsize contains the nb of bytes that the SDMA will transfer at each pass of the while loop*/ + + /* in NON trusted mode, buffsize is copied from Channel descriptor bufferSize (same size for all transfers) */ + if (cd_p->trust == FALSE) { + buffsize = cd_p->bufferSize; + } + /* in TRUSTED mode, it's up to the user to specify the size of each buffer thru an IoCtl call */ + /* This IoCtl has directly modified the bd_p->mode.count */ + /* therefore, buffersize is copied from the bd_p->mode.count */ + else { + buffsize = bd_p->mode.count; + } + + /* in any mode (trusted or non trusted), the transfer size must be overridden by */ + /* "toWrite" when there is less remaining bytes to transfer than the current buffer size */ + if (toWrite < buffsize) { + buffsize = toWrite; + } + + + if (!(bd_p->mode.status & BD_DONE)){ + /* More data to write than a single buffer can contain */ + if (cd_p->trust == FALSE ){ + iapi_memcpy(iapi_Phys2Virt(bd_p->bufferAddr), local_buf, buffsize); + local_buf += buffsize; + } + + /* update the BD count that will be used by the SDMA to transfer the proper nb of bytes */ + bd_p->mode.count = buffsize; + + bd_p->mode.status |= BD_DONE; + writtenBytes += buffsize; + toWrite -= buffsize; + /* Prepares access to the "next" buffer */ + /* - case 1 - finished successfully : writtenBytes = nbytes */ + if (toWrite == 0) { + copyFinished = TRUE; + } + /* - case 2 - Last BD and WRAP bit set so re-start from beginning */ + /*else if ((bd_p->mode.status & BD_WRAP)){ + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + }*/ + /* - case 3 - Last BD of the BD but nor ring*/ + else if (((bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr) + + (cd_p->bufferDescNumber - 1) * sizeof(bufferDescriptor)) == bd_p){ + copyFinished = TRUE; + } + /* - case 4 - general : next BD in the BD array */ + else { + bd_p++; + } + + } else { + /* finished here : buffer not already done */ + copyFinished = TRUE; + } + } + + ccb_p->currentBDptr = ccb_p->baseBDptr; + + /* + * 3. Starting of the channel + */ + iapi_lowStartChannel(chNum); + ccb_p->status.execute = TRUE; + + if( cd_p->callbackSynch == DEFAULT_POLL) + { + iapi_SynchChannel(chNum); + /* + * Check the 'RROR' bit on all buffer descriptors, set error number + * and return IAPI_FAILURE if set. + */ + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + for (index=0 ; index < cd_p->bufferDescNumber ; index++) + { + if(bd_p->mode.status & BD_RROR) + { + result = IAPI_ERR_RROR_BIT_WRITE | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + bd_p++; + } + iapi_ReleaseChannel(chNum); + } + + /* + *If synchronization type is callback, the user of I.API must + *release the channel + */ + return writtenBytes; +} + + + + +/* ***************************************************************************/ +/* This function is used to receive data from the SDMA. + * + * <b>Algorithm:</b>\n + * + * The data control structure would be copied to IPCv1 complied Buffer + * Descriptor Array. This array shall be allocated from non cacheable memory. + * It would then provide this buffer descriptor array as an input to SDMA using + * channel control block and then configure the Host Enable (HE) or + * DSP enable (DE) bit of SDMA for the channel used for this transfer depending + * on the source. + * + * <b>Notes:</b>\n + * Virtual DMA channels are unidirectional, an iapi_Write_ipcv2 authorized + * on a channel means that source processor is expecting to send to the destination + * processor. The meaning of an interrupt received from the SDMA notifies that the + * data has been delivered to the destination processor. + * + * @param *cd_p chanenl descriptor for the channel to receive from + * @param *data_control_struct_ipcv2 + + * Data Control structure: + * ------------------------- + * | Data Node Descriptor 1| + * ------------------------- + * | Data Node Descriptor 2| + * ------------------------- + * | : | + * | : | + * ------------------------- + * |Data Node Descriptor n | + * ------------------------- + * + * Data Node Descriptor (Buffer Descriptor): + *------------------------------------------------------------------------------ + *| 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 … 0| + *------------------------------------------------------------------------------ + *| L E D R R R R R |<---- Reserved ----> |<- Length-> | + *------------------------------------------------------------------------------ + *| <---------------------------- Data Ptr ----------------------------------->| + *------------------------------------------------------------------------------ + * + * L bit (LAST): If set, means that this buffer of data is the last buffer of the frame + * E bit (END): If set, we reached the end of the buffers passed to the function + * D bit (DONE): Only valid on the read callback. When set, means that the buffer has been + * filled by the SDMA. + * Length: Length of data pointed by this node in bytes + * Data Ptr: Pointer to the data pointed to by this node. + * The Function Shall not be called for the same channel unless the Read callback has been + * received for channel for which it has been called already. + * + * @return + * - IAPI_SUCCESS on success, IAPI_ERROR otherwise + * + *- -iapi_errno if failure + */ + +int iapi_Read_ipcv2( channelDescriptor * cd_p, void * data_control_struct_ipcv2) +{ + channelControlBlock * ccb_p; + + +/* The Parameters passed are considered to be validated by the upper layers*/ + + bufferDescriptor_ipcv1_v2 *bd_ipcv2_p; + dataNodeDescriptor *dnd_p = (dataNodeDescriptor*)data_control_struct_ipcv2; + + ccb_p = cd_p->ccb_ptr; + iapi_errno = IAPI_ERR_NO_ERROR; + + if(ccb_p->baseBDptr == NULL) +{ + iapi_errno = IAPI_ERR_BD_UNINITIALIZED; + return -(IAPI_ERR_BD_UNINITIALIZED); +} + + ccb_p->currentBDptr = ccb_p->baseBDptr; + + /* Copy the data Node descriptor information to new BDs */ + bd_ipcv2_p = (bufferDescriptor_ipcv1_v2*)iapi_Phys2Virt(ccb_p->baseBDptr); + + while(1) + { + bd_ipcv2_p->bufferAddr = dnd_p->bufferAddr; + bd_ipcv2_p->mode.count = dnd_p->mode.count; +#ifdef MCU + bd_ipcv2_p->mode.endianness = 1; +#endif +#ifdef DSP + bd_ipcv2_p->mode.endianness = 0; +#endif + + bd_ipcv2_p->mode.status = dnd_2_bd_status[dnd_p->mode.status & GET_LED_MASK]; + + if((dnd_p->mode.status & DND_END_OF_XFER) != 0) + { + /* Break the loop at End of Transfer */ + break; + + } + bd_ipcv2_p++; + dnd_p++; + + } + /* + * Store the buffer address + */ + dnd_read_control_struct[cd_p->channelNumber] = (dataNodeDescriptor*)data_control_struct_ipcv2; + /* + * Register the Call Back + */ + + iapi_AttachCallbackISR(cd_p, iapi_read_ipcv2_callback); + + /* + * Starting of the channel + */ + iapi_lowStartChannel(cd_p->channelNumber); + ccb_p->status.execute = TRUE; + + return IAPI_SUCCESS; + +} + + +/* ***************************************************************************/ +/* + * The function is used send a group of buffers to SDMA. + * <b>Algorithm:</b>\n + * + * The data control structure would be copied to IPCv1 complied Buffer + * Descriptor Array. This array shall be allocated from non cacheable memory. + * It would then provide this buffer descriptor array as an input to SDMA using + * channel control block and then configure the Host Enable (HE) or + * DSP enable (DE) bit of SDMA for the channel used for this transfer depending + * on the source. + * The Function Shall not be called for the same channel unless the Read callback has been + * received for channel for which it has been called already. + * + * <b>Notes:</b>\n + * Virtual DMA channels are unidirectional, an iapi_Write_ipcv2 authorized + * on a channel means that source processor is expecting to send to the destination + * processor. The meaning of an interrupt received from the SDMA notifies that the + * data has been delivered to the destination processor. + * + * @param *cd_p chanenl descriptor for the channel to write to + * @param *data_control_struct_ipcv2 + + * Data Control structure: + * ------------------------- + * | Data Node Descriptor 1| + * ------------------------- + * | Data Node Descriptor 2| + * ------------------------- + * | : | + * | : | + * ------------------------- + * |Data Node Descriptor n | + * ------------------------- + * + * Data Node Descriptor (Buffer Descriptor): + *------------------------------------------------------------------------------ + *| 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 … 0| + *------------------------------------------------------------------------------ + *| L E D R R R R R |<---- Reserved ----> |<- Length-> | + *------------------------------------------------------------------------------ + *| <---------------------------- Data Ptr ----------------------------------->| + *------------------------------------------------------------------------------ + * + * L bit (LAST): If set, means that this buffer of data is the last buffer of the frame + * E bit (END): If set, we reached the end of the buffers passed to the function + * D bit (DONE): Only valid on the read callback. When set, means that the buffer has been + * filled by the SDMA. + * Length: Length of data pointed by this node in bytes + * Data Ptr: Pointer to the data pointed to by this node. + * + * + * @return + * - iapi sucess on success. + * - -iapi_errno if failure + */ + +int iapi_Write_ipcv2( channelDescriptor * cd_p, void * data_control_struct_ipcv2) +{ + + channelControlBlock * ccb_p; + +/* The Parameters passed are considered to be validated by the upper layers*/ + + bufferDescriptor_ipcv1_v2 *bd_ipcv2_p; + dataNodeDescriptor *dnd_p = (dataNodeDescriptor*)data_control_struct_ipcv2; + ccb_p = cd_p->ccb_ptr; + iapi_errno = IAPI_ERR_NO_ERROR; + + if(ccb_p->baseBDptr == NULL) +{ + iapi_errno = IAPI_ERR_BD_UNINITIALIZED; + return -(IAPI_ERR_BD_UNINITIALIZED); +} + + + ccb_p->currentBDptr = ccb_p->baseBDptr; + + bd_ipcv2_p = (bufferDescriptor_ipcv1_v2*)iapi_Phys2Virt(ccb_p->currentBDptr); + /* Copy the data Node descriptor information to new BDs */ + while(1) + { + bd_ipcv2_p->bufferAddr = dnd_p->bufferAddr; + bd_ipcv2_p->mode.count = dnd_p->mode.count; + +#ifdef MCU + bd_ipcv2_p->mode.endianness = 1; +#endif +#ifdef DSP + bd_ipcv2_p->mode.endianness = 0; +#endif + + bd_ipcv2_p->mode.status = dnd_2_bd_status[dnd_p->mode.status & GET_LED_MASK]; + + if((dnd_p->mode.status & DND_END_OF_XFER) != 0) + { + /* Break the loop at End of Transfer */ + break; + } + bd_ipcv2_p++; + dnd_p++; + + } + + /* + * Starting of the channel + */ + iapi_lowStartChannel(cd_p->channelNumber); + ccb_p->status.execute = TRUE; + + return IAPI_SUCCESS; + +} + +/* ***************************************************************************/ +/** Call back ISR for the IPCv2 Receive. + * + * <b>Algorithm:</b>\n + * - This would copy back the informationfrom IPCv1 BD to IPCv2 BD on + * the receiving processor + * + * @return + * - void + */ + +void iapi_read_ipcv2_callback(struct iapi_channelDescriptor* cd_p, void* data) +{ + dataNodeDescriptor *dnd_p = dnd_read_control_struct[cd_p->channelNumber];//cd_p->ccb_ptr->channelDNDBuffer; + bufferDescriptor_ipcv1_v2 *bd_ipcv2_p = (bufferDescriptor_ipcv1_v2*)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + int index = MAX_BD_NUM - 1; + + + do + { + dnd_p->mode.status = 0; + dnd_p->mode.count = bd_ipcv2_p->mode.count; + + dnd_p->mode.status |= bd_ipcv2_p->mode.status & BD_DONE ? 0x00 : DND_DONE ; + dnd_p->mode.status |= bd_ipcv2_p->mode.status & BD_IPCV2_END_OF_FRAME ? DND_END_OF_FRAME : 0x00; + dnd_p->mode.status |= bd_ipcv2_p->mode.status & BD_LAST ? DND_END_OF_XFER : 0x00; + cd_p->ccb_ptr->currentBDptr = (bufferDescriptor*)iapi_Virt2Phys(bd_ipcv2_p); + + if((bd_ipcv2_p->mode.status & BD_LAST) != 0 || + (bd_ipcv2_p->mode.status & BD_CONT) == 0 + ) + break; + dnd_p++; + bd_ipcv2_p++; + + }while(index--); + + /*Call back the Original ISR */ + cd_p->callbackISR_ptr(cd_p, data); +} + +/* ***************************************************************************/ +/**Terminates a channel. + * + * <b>Algorithm:</b>\n + * - Check input parameters ans data structures + * - Check that all buffes have been processed (test all 'D' bits) + * - Stop the channel execution + * - Free alocated memory structures + * - Re-instantiate default interrupt handling + * + * @param *cd_p chanenl descriptor for the channel to close + * + * @return + * - IAPI_SUCCESS : OK + * - -iapi_errno : close failed + */ +int +iapi_Close (channelDescriptor * cd_p) +{ + int index = 0; + int result = IAPI_SUCCESS; + unsigned char chNum; + bufferDescriptor * bd_p; + channelControlBlock * ccb_p; + + /* + * 1. Check input parameters ans data structures + */ + if (cd_p != NULL){ + if (cd_p->ccb_ptr != NULL) { + chNum = cd_p->channelNumber; + ccb_p = cd_p->ccb_ptr; + } else { + result = IAPI_ERR_NO_CCB_DEFINED | IAPI_ERR_CH_AVAILABLE; + iapi_errno = result; + return -result; + } + } else { + result = IAPI_ERR_CD_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE; + iapi_errno = result; + return -result; + } + /* Try to aquire channel*/ + if(iapi_GetChannel(chNum) != 0) + { + result = IAPI_ERR_CH_IN_USE | chNum; + iapi_errno = result; + return -result; + } + + /* + * 2. Check that all buffes have been processed (test all 'D' bits), + * only if the forceClose bit in channel descriptor is set to FALSE + */ + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + if (bd_p == NULL){ + result = IAPI_ERR_BD_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | chNum; + iapi_errno = result; + return -result; + } + if(cd_p->forceClose == FALSE) + { + for (index=cd_p->bufferDescNumber ; index>0 ; index--){ + if (bd_p->mode.status & BD_DONE){ + result = IAPI_ERR_CLOSE | IAPI_ERR_CH_AVAILABLE | chNum; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } + bd_p++; + } + } + /*if the closing is forced,mark channel unused,set BD ownership to processor*/ + else + { + ccb_p->status.execute = FALSE; + for (index=cd_p->bufferDescNumber ; index>0 ; index--) + { + bd_p->mode.status &= ~BD_DONE; + bd_p++; + } + } + + /* + * 3. Stop the channel execution + */ + iapi_lowStopChannel(chNum); + + /* + * 4. Free alocated memory structures + */ + if (cd_p->trust == FALSE ){ + bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr); + for (index=cd_p->bufferDescNumber ; index>0 ; index--){ + FREE (iapi_Phys2Virt(bd_p->bufferAddr)); + bd_p++; + } + } + + /* + * 5. Re-instantiate default interrupt handling + */ + iapi_DetachCallbackISR (cd_p); + FREE ((bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr)); + FREE (cd_p); + ccb_p->baseBDptr = NULL; + ccb_p->currentBDptr = NULL; + ccb_p->channelDescriptor = NULL; + ccb_p->status.openedInit = FALSE; + + iapi_ReleaseChannel(chNum); + + return IAPI_SUCCESS; +} + +/* ***************************************************************************/ +/**The request argument selects the control function to be performed. + * + * <b>Algorithm:</b>\n + * + * - Check data structures are properly initialized: + * - Channel descriptor validity + * - Channel control block validity + * - The ctlRequest parameter contains in the lower 16 bits the control code of + * the change to be performed, and in the upper 16 bits, the BD to be + * modified if the change affects a BD od the channel. + * - Selection of the parameter to change and appropriate sanity checks: + * - Channel Descriptor: changes the pointer to the channel descriptor + * structure, the pointer to the new channel descriptor is given in the third + * argument call + * - Buffer Descriptor Number: changes the number of buffer descriptor for the + * channel + * - Buffer size: changes the size of the data buffers pointed to by the + * buffer descriptor; note that all buffer descriptors are assumed to have the + * same size for a given buffer descripotr chain + * - Blocking policy: changes the blocking policy for the read and write calls + * - Ownership: changes direction: turnaround + * - Synchronization method: changes the callback type, default or user. The* + * callback function table is set accordingly + * - Trust property: trust can only be changed through ChangeChannelDesc first + * request, this guarantees the close/open sequence for the channel + * - Callback Interrupt service routine pointer: changes the callback function + * pointer, when this method is used, to replace it with a new one + * - Channel control block pointer: not available + * - Priority: changes the channel priority directly in SDMA register + * - Watermark level: changes the value of the peripheral watermark level that + * passed to the script. The new value is passed in the third parameter call. + * - Wrap bit: changes to set to 1 the Wrap bit of the last buffer descriptor + * + * @param *cd_p channel descriptor for the channel to modify + * @param ctlRequest request control code and, if tha case, number of BD to be + * changed + * @param param parameter for the modification + * + * @return + * - IAPI_SUCCESS : OK + * - -iapi_errno : operation failed + */ +int +iapi_IoCtl (channelDescriptor * cd_p, unsigned long ctlRequest, + unsigned long param) +{ + int retvalue; + int result = IAPI_SUCCESS; + unsigned char chNum; + unsigned long clean_ctlRequest; /* lower 16 bits of the ctlRequest*/ + unsigned long bd_num; /* upper 16 bits of the ctlRequest*/ + + /* + * 1. Check data structures are properly initialized + */ + /* Channel descriptor validity */ + if (cd_p == NULL){ + result = IAPI_ERR_CD_UNINITIALIZED; + iapi_errno = result; + return -result; + } + + /* Channel control block validity */ + if (cd_p->ccb_ptr == NULL){ + result = IAPI_ERR_CCB_UNINITIALIZED; + iapi_errno = result; + return -result; + } + + /* Control block & Descriptor associated with the channel being worked on */ + chNum = cd_p->channelNumber; + + /* Remove, if exists, BD number specified in upper bits of ctlRequest*/ + clean_ctlRequest = ctlRequest & (~BD_NUM_MASK); + + /* Extract, if exists, BD number specified in upper bits of ctlRequest*/ + bd_num = (ctlRequest & BD_NUM_MASK) >> BD_NUM_OFFSET; + + /* Check that the bd_num is valid*/ + if(bd_num > cd_p->bufferDescNumber) + { + result = IAPI_ERR_INVALID_PARAMETER | chNum; + iapi_errno = result; + return -result; + } + + /*All checks OK, try to aquire channel*/ + if(iapi_GetChannel(chNum) != 0) + { + result = IAPI_ERR_CH_IN_USE | chNum; + iapi_errno = result; + return -result; + } + + /* + * 2. Selection of the parameter to change and appropriate sanity checks + */ + switch (clean_ctlRequest){ + + /* + * Channel Descriptor + * --- Changes the pointer to the channel descriptor structure: the pointer + * to the new channel descriptor is given in the third argument call. + */ + case IAPI_CHANGE_CHANDESC: + if ((void *) param == NULL) { + result = IAPI_ERR_INVALID_PARAMETER; + iapi_errno = result; + iapi_ReleaseChannel(chNum); + return -result; + } else { + channelDescriptor * chParam = (channelDescriptor *)param; + if (chParam->channelNumber != chNum){ + /* Release ch so it can be aquired by the Close fn*/ + iapi_ReleaseChannel(chNum); + result = iapi_Close(cd_p); + if (result == IAPI_SUCCESS){ + FREE((void*)cd_p); + iapi_AllocChannelDesc (&cd_p, chParam->channelNumber); + iapi_memcpy((void*)cd_p, (void*)chParam, sizeof (channelDescriptor)); + /* Channel is released allready, so Open can get the channel*/ + result = iapi_Open(cd_p, chParam->channelNumber); + if(result != IAPI_SUCCESS) + { + return result; /* error code already set in iapi_Open*/ + } + } else { + return result; /* error code already set in iapi_Close*/ + } + } else { + result = IAPI_ERR_CD_CHANGE | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_ReleaseChannel(chNum); + iapi_errno = result; + return -result; + } + return IAPI_SUCCESS; + } + + /* + * Buffer Descriptor Number + * --- Changes the number of buffer descriptor for the channel. + */ + case IAPI_CHANGE_BDNUM: + result = iapi_ChangeChannelDesc(cd_p, IAPI_BUFFERDESCNUMBER, param); + iapi_ReleaseChannel(chNum); + return result; + + /* + * Buffer size + * --- Changes the size of the data buffers pointed to by the buffer + * descriptor; note that all buffer descriptors are assumed to have the + * same size for a given buffer descripotr chain. + */ + case IAPI_CHANGE_BUFFSIZE: + result = iapi_ChangeChannelDesc(cd_p, IAPI_BUFFERSIZE, param); + iapi_ReleaseChannel(chNum); + return result; + + /* + * Blocking policy + * --- Changes the blocking policy for the read and write calls. + */ + case IAPI_CHANGE_CHANBLOCK: + result = iapi_ChangeChannelDesc(cd_p, IAPI_BLOCKING, param); + iapi_ReleaseChannel(chNum); + return result; + + /* + * Ownership + * --- Changes direction: turnaround + */ + case IAPI_CHANGE_OWNERSHIP: + result = iapi_ChangeChannelDesc(cd_p, IAPI_OWNERSHIP, param); + iapi_ReleaseChannel(chNum); + return result; + + /* + * Synchronization method + * --- Changes the callback type, default or user. The callback function + * table is set accordingly. + */ + case IAPI_CHANGE_SYNCH: + result = iapi_ChangeChannelDesc(cd_p, IAPI_CALLBACKSYNCH, param); + iapi_ReleaseChannel(chNum); + return result; + + /* + * Trust property + * --- trust can only be changed through ChangeChannelDesc first request, + * this guarantees the close/open sequence for the channel. + */ + case IAPI_CHANGE_TRUST: + result = iapi_ChangeChannelDesc(cd_p, IAPI_TRUST, param); + iapi_ReleaseChannel(chNum); + return result; + + /* + * Callback Interrupt service routine pointer + * --- Cahnges the callback function pointer, when this method is used, to + * replace it with a new one. + */ + case IAPI_CHANGE_CALLBACKFUNC: + result = iapi_ChangeChannelDesc(cd_p, IAPI_CALLBACKISR_PTR, param); + iapi_ReleaseChannel(chNum); + return result; + + /* + * Channel control block pointer + * --- NA + */ + case IAPI_CHANGE_CHANCCB: + result = iapi_ChangeChannelDesc(cd_p, IAPI_CCB_PTR, param); + iapi_ReleaseChannel(chNum); + return result; + +#ifdef MCU + /* + * Priority + * --- Changes the channel priority directly in SDMA register + */ + case IAPI_CHANGE_PRIORITY: + { + volatile unsigned long * ChannelPriorities = &SDMA_CHNPRI_0; + if(param < MAX_CH_PRIORITY) + { + ChannelPriorities[ cd_p->channelNumber ] = param; + } + else + { + iapi_ReleaseChannel(chNum); + return IAPI_FAILURE; + } + } + break; +#endif /* MCU */ + + /* + * Wrap + * --- Set to 1 the wrap bit of the last buffer descriptor of the array. + * it provides the possibility to have a circular buffer structure. + */ + case IAPI_CHANGE_BDWRAP: + { + result = iapi_ChangeChannelDesc(cd_p,IAPI_BDWRAP , param); + iapi_ReleaseChannel(chNum); + return result; + } + + /* + * Watermark + * --- Changes the value of the peripheral watermark level that triggers + * a DMA request. It impacts context of the channel, therefore channel 0 + * must be started to update the context with this new value. + */ + case IAPI_CHANGE_WATERMARK: + { + result = iapi_ChangeChannelDesc(cd_p,IAPI_WML , param); + iapi_ReleaseChannel(chNum); + return result; + } + /* + * INTR + * --- Set the INTR bit on specified BD or on all BD's if SET_BIT_ALL + * is passed as parameter. + */ + case IAPI_CHANGE_SET_BDINTR: + { + bufferDescriptor * bde_p; + int j = 0; + + retvalue = IAPI_SUCCESS; + if(param == SET_BIT_ALL) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + for(j = 0; j < cd_p->bufferDescNumber; j++) + { + bde_p->mode.status |= BD_INTR; + bde_p++; + } + } + else if(param < cd_p->bufferDescNumber) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) + + param; + bde_p->mode.status |= BD_INTR; + } + else + { + retvalue = IAPI_FAILURE; + } + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * INTR + * --- Unset the INTR bit on specified BD or on all BD's if SET_BIT_ALL + * is passed as parameter. + */ + case IAPI_CHANGE_UNSET_BDINTR: + { + bufferDescriptor * bde_p; + + int j = 0; + + retvalue = IAPI_SUCCESS; + if(param == SET_BIT_ALL) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + for(j = 0; j < cd_p->bufferDescNumber; j++) + { + bde_p->mode.status &= ~BD_INTR; + bde_p++; + } + } + else if(param < cd_p->bufferDescNumber) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) + + param; + bde_p->mode.status &= ~BD_INTR; + } + else + { + retvalue = IAPI_FAILURE; + } + iapi_ReleaseChannel(chNum); + return retvalue; + } +/* + * EventMask1 + * --- Changes the value of the eventMask1 + */ + case IAPI_CHANGE_EVTMASK1: + { + cd_p->eventMask1 = param; + } + break; + /* + * EventMask2 + * --- Changes the value of the eventMask2 + */ + case IAPI_CHANGE_EVTMASK2: + { + cd_p->eventMask2 = param; + } + break; + /* + * Peripheral Address + * --- Changes the value of the peripheralAddr + */ + case IAPI_CHANGE_PERIPHADDR: + { + cd_p->peripheralAddr = param; + } + break; + /* + * Cont + * --- Set the CONT bit on specified BD on all BD's if SET_BIT_ALL + * is passed as parameter. + */ + case IAPI_CHANGE_SET_BDCONT: + { + bufferDescriptor * bde_p; + int j = 0; + + retvalue = IAPI_SUCCESS; + if(param == SET_BIT_ALL) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + for(j = 0; j < cd_p->bufferDescNumber; j++) + { + bde_p->mode.status |= BD_CONT; + bde_p++; + } + } + else if(param < cd_p->bufferDescNumber) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) + + param; + bde_p->mode.status |= BD_CONT; + } + else + { + retvalue = IAPI_FAILURE; + } + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * Cont + * --- Unset the CONT bit on specified BD or on all BD's if SET_BIT_ALL + * is passed as parameter. + */ + case IAPI_CHANGE_UNSET_BDCONT: + { + bufferDescriptor * bde_p; + + int j = 0; + + retvalue = IAPI_SUCCESS; + if(param == SET_BIT_ALL) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + for(j = 0; j < cd_p->bufferDescNumber; j++) + { + bde_p->mode.status &= ~BD_CONT; + bde_p++; + } + } + else if(param < cd_p->bufferDescNumber) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) + + param; + bde_p->mode.status &= ~BD_CONT; + } + else + { + retvalue = IAPI_FAILURE; + } + iapi_ReleaseChannel(chNum); + return retvalue; + } + + /* + * EXTD + * --- Set the EXTD bit on specified BD or on all BD's if SET_BIT_ALL + * is passed as parameter. + */ + case IAPI_CHANGE_SET_BDEXTD: + { + bufferDescriptor * bde_p; + int j = 0; + + retvalue = IAPI_SUCCESS; + if(param == SET_BIT_ALL) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + for(j = 0; j < cd_p->bufferDescNumber; j++) + { + bde_p->mode.status |= BD_EXTD; + bde_p++; + } + } + else if(param < cd_p->bufferDescNumber) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) + + param; + bde_p->mode.status |= BD_EXTD; + } + else + { + retvalue = IAPI_FAILURE; + } + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * EXTD + * --- Unset the EXTD bit on specified BD or on all BD's if SET_BIT_ALL + * is passed as parameter. + */ + case IAPI_CHANGE_UNSET_BDEXTD: + { + bufferDescriptor * bde_p; + + int j = 0; + + retvalue = IAPI_SUCCESS; + if(param == SET_BIT_ALL) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + for(j = 0; j < cd_p->bufferDescNumber; j++) + { + bde_p->mode.status &= ~BD_EXTD; + bde_p++; + } + } + else if(param < cd_p->bufferDescNumber) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) + + param; + bde_p->mode.status &= ~BD_EXTD; + } + else + { + retvalue = IAPI_FAILURE; + } + iapi_ReleaseChannel(chNum); + return retvalue; + } + + /* + * TRANSFER SIZE to be used for this channel + * --- Set the transfer size used indicator and code for transfer size in + * the CD + */ +case IAPI_CHANGE_SET_TRANSFER_CD: + { + bufferDescriptor * bde_p; + int j = 0; + retvalue = IAPI_SUCCESS; + if((param == TRANSFER_8BIT) || (param == TRANSFER_16BIT) || + (param == TRANSFER_24BIT) || (param == TRANSFER_32BIT)) + { + cd_p->useDataSize = TRUE; + cd_p->dataSize = param; + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + for(j = 0; j < cd_p->bufferDescNumber; j++) + { + bde_p->mode.command = param; + bde_p++; + } + } + else + { + retvalue = IAPI_FAILURE; + } + iapi_ReleaseChannel(chNum); + return retvalue; + } + + + /* + * USER_ARG + * --- Set the user selectable pointer to be received by the callback + * function, if IRQ synch is used + */ + case IAPI_CHANGE_USER_ARG: + { + userArgTable[cd_p->channelNumber]= (void*)param; + iapi_ReleaseChannel(chNum); + return IAPI_SUCCESS; + } + /* + * FORCE_CLOSE + * --- Set the forceClose bit in channelDescriptor to value passed in param. + * If this bit is TRUE, the channel in closed even if some BD are still + * owned by the SDMA. + */ + case IAPI_CHANGE_FORCE_CLOSE: + { + retvalue = IAPI_SUCCESS; + if((param == TRUE) || (param == FALSE)) + { + cd_p->forceClose = param; + } + else + { + iapi_errno = IAPI_ERR_INVALID_PARAMETER | cd_p->channelNumber; + retvalue = -iapi_errno; + } + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * TRANSFER type + * --- Set the last 2 bits in the command field of the BD to specify the + * transfer type 8, 16, 24, or 32 bits on all BD's, allready set in the CD + */ + case IAPI_CHANGE_SET_TRANSFER: + { + bufferDescriptor * bde_p; + int j = 0; + + retvalue = IAPI_SUCCESS; + if((param == TRANSFER_8BIT)||(param == TRANSFER_16BIT)|| + (param == TRANSFER_24BIT)||(param == TRANSFER_32BIT)) + { + bde_p = cd_p->ccb_ptr->baseBDptr; + for(j = 0; j < cd_p->bufferDescNumber; j++) + { + bde_p->mode.command = param; + bde_p++; + } + } + else + { + retvalue = IAPI_FAILURE; + } + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * BUFFER address + * --- Change buffer address in BD specified in the upper 16 bits of the + * ctlRequest. + */ + case IAPI_CHANGE_SET_BUFFERADDR: + { + bufferDescriptor * bde_p; + retvalue = IAPI_SUCCESS; + + /* Get pointer to the BD structure to change*/ + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + bde_p+= bd_num; + + /* DO NOT translate address to physical */ + bde_p->bufferAddr = (void*)param; + + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * BUFFER address + * --- Get the buffer address from the BD specified in the upper 16 bits of the + * ctlRequest. + */ + case IAPI_CHANGE_GET_BUFFERADDR: + { + bufferDescriptor * bde_p; + retvalue = IAPI_SUCCESS; + + /* Get pointer to the BD structure to change*/ + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + bde_p+= bd_num; + /* Translate to virtual*/ + *((unsigned long*)param) = (unsigned long)bde_p->bufferAddr; + + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * EXTENDED BUFFER address + * --- Change extended buffer address in BD specified in the upper 16 bits + * of the ctlRequest. + */ + case IAPI_CHANGE_SET_EXTDBUFFERADDR: + { + bufferDescriptor * bde_p; + retvalue = IAPI_SUCCESS; + + /* Get pointer to the BD structure to change*/ + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + bde_p+= bd_num; + + /* DO NOT translate address to physical. The user might want something else + *here + */ + bde_p->extBufferAddr = (void*)param; + + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * EXTENDED BUFFER address + * --- Get extended buffer address from the BD specified in the upper 16 bits + * of the ctlRequest. + */ + case IAPI_CHANGE_GET_EXTDBUFFERADDR: + { + bufferDescriptor * bde_p; + retvalue = IAPI_SUCCESS; + + /* Get pointer to the BD structure to change*/ + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + bde_p+= bd_num; + + /* DO NOT translate address to vitual - user knows what is here. + */ + *((unsigned long*)param) = (unsigned long)bde_p->extBufferAddr; + + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * COMMAND field + * --- Change command field in BD specified in the upper 16 bits of the + * ctlRequest. + */ + case IAPI_CHANGE_SET_COMMAND: + { + bufferDescriptor * bde_p; + retvalue = IAPI_SUCCESS; + + /* Get pointer to the BD structure to change*/ + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + bde_p+= bd_num; + /* Update command field*/ + bde_p->mode.command = param; + + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * COMMAND field + * --- Get the command field from the BD specified in the upper 16 bits + * of the ctlRequest. + */ + case IAPI_CHANGE_GET_COMMAND: + { + bufferDescriptor * bde_p; + retvalue = IAPI_SUCCESS; + + /* Get pointer to the BD structure to change*/ + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + bde_p+= bd_num; + /* Get the command field*/ + *((unsigned long*)param) = bde_p->mode.command; + + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * COUNT field + * --- Change count field in BD specified in the upper 16 bits of the + * ctlRequest. + */ + case IAPI_CHANGE_SET_COUNT: + { + bufferDescriptor * bde_p; + retvalue = IAPI_SUCCESS; + + /* Get pointer to the BD structure to change*/ + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + bde_p+= bd_num; + /* Update count field*/ + bde_p->mode.count = param; + + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * COUNT field + * --- Get the count field of the BD specified in the upper 16 bits of the + * ctlRequest. + */ + case IAPI_CHANGE_GET_COUNT: + { + bufferDescriptor * bde_p; + retvalue = IAPI_SUCCESS; + + /* Get pointer to the BD structure to change*/ + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + bde_p+= bd_num; + /* Update count field*/ + *((unsigned long*)param) = bde_p->mode.count; + + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * STATUS field + * --- Change status field in BD specified in the upper 16 bits of the + * ctlRequest. + */ + case IAPI_CHANGE_SET_STATUS: + { + bufferDescriptor * bde_p; + retvalue = IAPI_SUCCESS; + + /* Get pointer to the BD structure to change*/ + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + bde_p+= bd_num; + /* Update status field*/ + bde_p->mode.status = param; + + iapi_ReleaseChannel(chNum); + return retvalue; + } + /* + * STATUS field + * --- Get the status field of the BD specified in the upper 16 bits + * of the ctlRequest. + */ + case IAPI_CHANGE_GET_STATUS: + { + bufferDescriptor * bde_p; + retvalue = IAPI_SUCCESS; + + /* Get pointer to the BD structure to change*/ + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + bde_p+= bd_num; + /* Update status field*/ + *((unsigned long*)param) = bde_p->mode.status; + + iapi_ReleaseChannel(chNum); + return retvalue; + } + +#ifdef MCU + /* + * Endianness + * --- Set the ENDIANNESS indicator in the command filed of the specified BD + * or on all BD's if SET_BIT_ALL is passed as parameter. + */ + case IAPI_CHANGE_SET_ENDIANNESS: + { + bufferDescriptor * bde_p; + int j = 0; + + retvalue = IAPI_SUCCESS; + if(param == SET_BIT_ALL) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + for(j = 0; j < cd_p->bufferDescNumber; j++) + { + bde_p->mode.command = CHANGE_ENDIANNESS; + bde_p++; + } + } + else if(param < cd_p->bufferDescNumber) + { + bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) + + param; + bde_p->mode.command = CHANGE_ENDIANNESS; + } + else + { + retvalue = IAPI_FAILURE; + } + iapi_ReleaseChannel(chNum); + return retvalue; + } +#endif + +#ifdef SDMA_SKYE +#ifdef MCU + + /* + * SDMA State + * --- Enter the SDMA into LOCK Mode. No RAM updation allowed except same Context + * update with same PC Value. + */ + case IAPI_ENTER_LOCK_MODE: + { + if(param == RESET_CLEAR_LOCK) + { + SDMA_SDMA_LOCK = (1 << RESET_CLR_BIT_OFFSET); + SDMA_SDMA_LOCK = (1 << LOCK_BIT_OFFSET); + iapi_SdmaState = LOCK; + } + else if(param == RESET_NOCLEAR_LOCK) + { + SDMA_SDMA_LOCK = (1 << LOCK_BIT_OFFSET); + iapi_SdmaState = LOCK; + } + + } + break; + +#endif +#endif + default: + retvalue = IAPI_ERR_CD_CHANGE_UNKNOWN | IAPI_ERR_CH_AVAILABLE | chNum; + iapi_errno = retvalue; + iapi_ReleaseChannel(chNum); + return -retvalue; + } + + + iapi_ReleaseChannel(chNum); + return IAPI_SUCCESS; +} +/* ***************************************************************************/ +/**Initialization of the SDMA - opening of channel 0, download RAM image. + * + * <b>Algorithm:</b>\n + * - open channel 0 + * - if ram_image pointer passed is not NULL, download RAM image to SDMA + * + * @param + * - cd_p channel descriptor pointer for channel 0 + * - ram_image pointer to RAM image to download, or NULL if this operation + * is not required + * - code_size size of the RAM image, in bytes + * - start_addr start address for the RAM image + * + * @return + * - IAPI_SUCCESS if all operations were successful + * - negated I.API error code if any operation failed + */ +#ifdef MCU +int +iapi_Init(channelDescriptor * cd_p, configs_data * config_p, unsigned short* ram_image, + unsigned short code_size, unsigned long start_addr) +{ +#endif +#ifdef DSP +int +iapi_Init(channelDescriptor * cd_p) +{ +#endif + +int retvalue = IAPI_SUCCESS; /* Variable to store the results from I.API calls */ + + /* Check initialization not allredy done*/ + if(iapi_CCBHead != NULL) + { + retvalue = IAPI_ERR_NOT_ALLOWED; + iapi_errno = retvalue; + return -retvalue; + } + /* Be sure SDMA has not started yet */ +#ifdef MCU + SDMA_H_C0PTR = 0x0; +#endif +#ifdef DSP + SDMA_D_C0PTR = 0x0; +#endif + + /*Try to open channel 0*/ + retvalue = iapi_Open(cd_p, 0); + if(retvalue != IAPI_SUCCESS) + { + return retvalue; + } + +#ifdef MCU + /* Set Command Channel (Channel Zero) */ + SDMA_CHN0ADDR = 0x4050; + + /* Set bits of CONFIG register but with static context switching */ + SDMA_H_CONFIG = (config_p->dspdma << 12) | (config_p->rtdobs << 11) | + (config_p->acr << 4) | (0); + + /* Send the address for the host channel table to the SDMA*/ + SDMA_H_C0PTR = (unsigned long)iapi_Virt2Phys(iapi_CCBHead); + /* If required, download the RAM image for SDMA*/ + if(ram_image != NULL) + { + retvalue = iapi_SetScript(cd_p, (void*)ram_image, code_size, + start_addr); + } + + /* Set bits of CONFIG register with given context switching mode */ + SDMA_H_CONFIG = (config_p->dspdma << 12) | (config_p->rtdobs << 11) | + (config_p->acr << 4) | (config_p->csm); + +#endif +#ifdef DSP + /* Send the address for the host channel table to the SDMA*/ + SDMA_D_C0PTR = (unsigned long)iapi_Virt2Phys(iapi_CCBHead); +#endif + +#ifdef SDMA_SKYE + iapi_SdmaState = OPEN; +#endif + + return retvalue; +} + + +/* ***************************************************************************/ +/**High layer interface for starting a channel + * + * <b>Algorithm:</b>\n + * - call low layer function for starting a channel + * + * @return + * - IAPI_SUCCESS + */ +int +iapi_StartChannel(unsigned char channel) +{ + iapi_lowStartChannel(channel); + return IAPI_SUCCESS; +} +/* ***************************************************************************/ +/**High layer interface for stopping a channel + * + * <b>Algorithm:</b>\n + * - call low layer function for stopping a channel + * + * @return + * - IAPI_SUCCESS + */ +int +iapi_StopChannel(unsigned char channel) +{ + iapi_lowStopChannel(channel); + return IAPI_SUCCESS; +} + +/* ***************************************************************************/ +/**High layer interface for synchronising a channel + * + * <b>Algorithm:</b>\n + * - call low layer function for stopping a channel + * + * @return + * - IAPI_SUCCESS + */ +int iapi_SynchChannel(unsigned char channel) +{ + iapi_lowSynchChannel(channel); + return IAPI_SUCCESS; +} + +#ifdef MCU +/* ***************************************************************************/ +/**High layer interface for getting program memory data from SDMA + * + * <b>Algorithm:</b>\n + * - call coresponding low layer function + * + * @return + * - IAPI_SUCCESS + */ +int +iapi_GetScript(channelDescriptor * cd_p, void * buf, unsigned short size, + unsigned long address) +{ + iapi_lowGetScript(cd_p, buf, size, address); + return IAPI_SUCCESS; +} + +/* ***************************************************************************/ +/**High layer interface for getting data memory from SDMA + * + * <b>Algorithm:</b>\n + * - call coresponding low layer function + * + * @return + * - IAPI_SUCCESS + */ +int +iapi_GetContext(channelDescriptor * cd_p, void * buf, + unsigned char channel) +{ + iapi_lowGetContext(cd_p, buf, channel); + return IAPI_SUCCESS; +} + +/* ***************************************************************************/ +/**High layer interface for set program memory data to SDMA - e.g. scripts + * + * <b>Algorithm:</b>\n + * - call coresponding low layer function + * + * @return + * - IAPI_SUCCESS + */ +int +iapi_SetScript(channelDescriptor * cd_p, void * buf, unsigned short nbyte, + unsigned long destAddr) +{ + iapi_lowSetScript(cd_p, buf, nbyte, destAddr); + return IAPI_SUCCESS; +} + +/* ***************************************************************************/ +/**High layer interface for set data memory to SDMA - e.g. contexts. + * + * <b>Algorithm:</b>\n + * - call coresponding low layer function + * + * @return + * - IAPI_SUCCESS + */ +int +iapi_SetContext(channelDescriptor * cd_p, void * buf, + unsigned char channel) +{ + iapi_lowSetContext(cd_p, buf, channel); + return IAPI_SUCCESS; +} + +/* ***************************************************************************/ +/**High layer interface used to associate specified channel with a script. + * + * <b>Algorithm:</b>\n + * - call coresponding low layer function + * + * @return + * - IAPI_SUCCESS + */ +int +iapi_AssignScript(channelDescriptor * cd_p, script_data * data_p) +{ + /* VERIFY THAT THE CHANNEL IT IS OPENED !!!!*/ + return iapi_lowAssignScript(cd_p, data_p); +} + +/* ***************************************************************************/ +/**High layer interface used to associate specified channel with a script. + * + * <b>Algorithm:</b>\n + * - call coresponding low layer function + * + * @return + * - IAPI_SUCCESS + */ +int +iapi_SetChannelEventMapping(unsigned char event, unsigned long channel_map) +{ + return iapi_lowSetChannelEventMapping(event, channel_map); +} +#endif + + + +#ifdef DSP +#define SDMA_DI SDMA_D_INTR +void IRQ_Handler(); +#pragma interrupt IRQ_Handler +#endif + +#ifdef MCU +#define SDMA_DI SDMA_H_INTR +#endif + +#ifndef IRQ_KEYWORD +#define IRQ_KEYWORD +#endif /* IRQ_KEYWORD */ + +/* ***************************************************************************/ +/** + *@brief Find the first set bit in data parameter. + * + * Find the first set bit in unsigned integer parameter data. Data is scanned + * from MSB to LSB, searching for the set bit. The value returned is the + * offset from the most significant bit of data. If bit 31 is set, the value + * returned is zero. If no bits are set, a value of 32 is returned. This is compliant + * with the MCore FF1 instruction. + * + * + * + * @param + * - data: variable to check + * + * @return + * - the offset of the most significant bit set from the MSB + */ +unsigned int +quartz_FF1( unsigned int data ) +{ + register unsigned int result = 0; + while ( (result <= 31 ) && !( data & 0x80000000U) ) + { + data <<= 1U; + result++; + } + + return result; +} + +IRQ_KEYWORD +void +IRQ_Handler(void) +{ + unsigned int intrReg;/* interrupt register mask for clearing the interrupt bit */ + unsigned char chNum; /* SDMA channel number generating the a IRQ*/ + + /* Disable interrupts */ + iapi_DisableInterrupts(); + /* + * Clear interrupt in SDMA DI register => ACK to the SDMA the IT request. + * Get each interrupt number, clear them one after the other. + */ + if(SDMA_DI != 0) + { + chNum = (CH_NUM - 1 - quartz_FF1(SDMA_DI)); + } + else + { + chNum = 32; + } + intrReg = 1 << chNum; + while (intrReg != 0) + { + SDMA_DI &= intrReg; + iapi_SDMAIntr |= intrReg; + iapi_WakeUp(chNum); + if (callbackIsrTable[chNum] != NULL) + { + /* release channel before callback, so IoCtl's are available*/ + iapi_ReleaseChannel(chNum); + callbackIsrTable[chNum](iapi_CCBHead[chNum].channelDescriptor, + userArgTable[chNum]); + } + + chNum = (CH_NUM - 1 - quartz_FF1(SDMA_DI)); + intrReg = 1 << chNum; + } + + /* Enable interrupts */ + iapi_EnableInterrupts(); +} + +/* ***************************************************************************/ +/** + *@brief Perform a memory copy operation, in the memory of the same processor + * + * Size bytes are copied from the src address to dest address. It is used + * the channel pointed by cd_p, which must be configured prior to this call: + * opened, associated with the script to perform the operation - DSP_2_DSP, + * or MCU_2_MCU - and have the synchronization option set. + * + * + * + * @param + * - cd_p: channel configured to perform DSP_2_DSP or MCU_2_MCU transfers + * - dest: destination memory address + * - src : source memory address + * - size: number of bytes to copy from src to dest + * + * @return + * - the offset of the most significant bit set from the MSB + */ + +int iapi_MemCopy(channelDescriptor * cd_p, void* dest, void* src, unsigned long size) +{ + int result = IAPI_SUCCESS; + bufferDescriptor * bd_p; + + /* Channel descriptor validity */ + if (cd_p == NULL) + { + result = IAPI_ERR_CD_UNINITIALIZED; + iapi_errno = result; + return -result; + } + + /* Check and set correct parameter */ + if(cd_p->trust != TRUE) + { + result = iapi_ChangeChannelDesc(cd_p, IAPI_TRUST, TRUE); + } + + if(cd_p->bufferDescNumber != 1) + { + result = iapi_ChangeChannelDesc(cd_p, IAPI_BUFFERDESCNUMBER, 1); + if(result != IAPI_SUCCESS) + { + return result; + } + } + + if(cd_p->bufferSize != size) + { + result = iapi_ChangeChannelDesc(cd_p, IAPI_BUFFERSIZE, size); + if(result != IAPI_SUCCESS) + { + return result; + } + } + /* Set addresses*/ + bd_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + bd_p->bufferAddr = iapi_Virt2Phys(src); + bd_p->extBufferAddr = iapi_Virt2Phys(dest); + + /* Set mode*/ + bd_p->mode.count = size; + bd_p->mode.command = 0x00; + bd_p->mode.status = BD_INTR|BD_EXTD|BD_DONE|BD_WRAP; + + /*Decide if we sleep or not*/ + if(cd_p->callbackSynch == DEFAULT_POLL) + { + iapi_StartChannel(cd_p->channelNumber); + /* Call synchronization routine*/ + iapi_SynchChannel(cd_p->channelNumber); + } + else + { + /* Just start the channel*/ + iapi_StartChannel(cd_p->channelNumber); + } + + return result; +} + +/* ***************************************************************************/ +/**Return the channel number from the channel descriptor + * + * @param cd_p pointer to channel descriptor to obtain the channel number + * + * @return + * - the channel number + * + */ +int iapi_GetChannelNumber(channelDescriptor * cd_p) +{ + return cd_p->channelNumber; +} + +/* ***************************************************************************/ +/**Return the error bit from the current BD of the channel + * + * + * @param cd_p pointer to channel descriptor + * + * @return + * - 0 if no error detected + * - BD_RROR | DATA_ERROR if error detected + * + */ +unsigned long iapi_GetError(channelDescriptor * cd_p) +{ + return ((cd_p->ccb_ptr->currentBDptr->mode.status & BD_RROR) | + (*(unsigned long*)&cd_p->ccb_ptr->status & DATA_ERROR)); +} + +/* ***************************************************************************/ +/**Return the count from the current BD of the channel + * + * + * @param cd_p pointer to channel descriptor + * + * @return + * - count field of the current BD for the channel + * + */ +int iapi_GetCount(channelDescriptor * cd_p) +{ + return (cd_p->ccb_ptr->currentBDptr->mode.count); +} + +/* ***************************************************************************/ +/**Return the sum of counts for all the BD's owned by the processor for + * the channel specified by the received parameter. + * + * + * @param cd_p pointer to channel descriptor + * + * @return + * - sum of count fields + * + */ +int iapi_GetCountAll(channelDescriptor * cd_p) +{ + int retval = 0; + int i = 0; + bufferDescriptor* bd_p; + + bd_p = cd_p->ccb_ptr->baseBDptr; + + while((i < cd_p->bufferDescNumber) && ((bd_p->mode.status & BD_DONE) == 0)) + { + retval += bd_p->mode.count; + i++; + bd_p++; + } + return retval; +} diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiLow.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiLow.c new file mode 100644 index 000000000000..5ba7849aa3b5 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiLow.c @@ -0,0 +1,150 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiLow.c + * + * $Id iapiLow.c $ + * + * Description: + * This library is written in C to guarantee functionality and integrity in + * the usage of SDMA virtual DMA channels. This API (Application Programming + * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE + * fashion. + * These are the LOW level functions of the I.API. + * + * + * / + * + * $Log iapiLow.c $ + * + *****************************************************************************/ + +/* **************************************************************************** + * Include File Section + *****************************************************************************/ +#include "epm.h" +#include "iapiLow.h" + +/** + * Function Section + */ + + +/* ***************************************************************************/ +/**Records an ISR callback function pointer into the ISR callback + * function table + * + * @param cd_p channel descriptor to attach callback to + * @param func_p pointer to the callback function to be registered + * + * @return none + */ +void +iapi_AttachCallbackISR (channelDescriptor * cd_p, + void (* func_p)(channelDescriptor * cd_p, void * arg)) + +{ + if (cd_p->callbackSynch == CALLBACK_ISR) { + iapi_DisableInterrupts(); + callbackIsrTable[cd_p->channelNumber] = func_p; + iapi_EnableInterrupts(); + } else if (cd_p->callbackSynch == DEFAULT_POLL) { + callbackIsrTable[cd_p->channelNumber] = NULL; + } else { + iapi_errno = IAPI_ERR_CALLBACKSYNCH_UNKNOWN | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + } +} + + +/* ***************************************************************************/ +/**Detaches (removes) an ISR callback function pointer from the ISR callback + * function table + * + * <b>Algorithm:</b>\n + * - Attach a null function to replace the original one. + * + * @param cd_p channel descriptor to detach callback from + * + * @return none + */ +void +iapi_DetachCallbackISR (channelDescriptor * cd_p) + +{ + iapi_AttachCallbackISR (cd_p, NULL); +} + +/* ***************************************************************************/ +/**Updates an ISR callback function pointer into the ISR callback function + * table + * + * <b>Algorithm:</b>\n + * - Detach the old function pointer (if any) and attach the new one + * + * @param cd_p channel descriptor to attach callback to + * @param func_p pointer to the callback function to be registered + * + * @return none + */ +void +iapi_ChangeCallbackISR (channelDescriptor * cd_p, + void (* func_p)(channelDescriptor * cd_p, void * arg)) +{ + iapi_DetachCallbackISR(cd_p); + iapi_AttachCallbackISR(cd_p, func_p); +} + +/* ***************************************************************************/ +/**Loop while the channel is not done on the SDMA + * + * <b>Algorithm:</b>\n + * - Loop doing nothing but checking the I.API global variable to indicate + * that the channel has been completed (interrupt from SDMA) + * + * <b>Notes:</b>\n + * - The ISR must update the I.API global variable iapi_SDMAIntr. + * + * @param channel channel number to poll on + * + * @return none + */ +void +iapi_lowSynchChannel (unsigned char channel) +{ + //while (! ( (1UL << channel) & iapi_SDMAIntr ) ) ; + GOTO_SLEEP(channel); + iapi_SDMAIntr &= ~(1UL << channel); +} + +/* ***************************************************************************/ +/**Fill the buffer descriptor with the values given in parameter. + * + * @return none + */ +void +iapi_SetBufferDescriptor( bufferDescriptor * bd_p, unsigned char command, + unsigned char status, unsigned short count, + void * buffAddr, void * extBufferAddr) +{ + bd_p->mode.command = command; + bd_p->mode.status = status; + bd_p->mode.count = count; + if (buffAddr != NULL) { + bd_p->bufferAddr = iapi_Virt2Phys(buffAddr); + } else { + bd_p->bufferAddr = buffAddr; + } + bd_p->extBufferAddr = extBufferAddr; +} diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiLowDsp.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiLowDsp.c new file mode 100644 index 000000000000..df7d44a9bd46 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiLowDsp.c @@ -0,0 +1,79 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiLowDsp.c + * + * $Id iapiLowDsp.c $ + * + * Description: + * This library is written in C to guarantee functionality and integrity in + * the usage of SDMA virtual DMA channels. This API (Application Programming + * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE + * fashion. + * These are the LOW level functions of the I.API specific to MCU. + * + * + * + * + * $Log iapiLowDsp.c $ + * + *****************************************************************************/ + +/* **************************************************************************** + * Include File Section + *****************************************************************************/ +#include "epm.h" +#include "iapiLow.h" + +/* **************************************************************************** + * Function Section + *****************************************************************************/ +#ifdef DSP + +/* ***************************************************************************/ +/**Starts the channel (core specific register) + * + * <b>Algorithm:</b>\n + * - Bit numbered "channel" of DspEnStartReg register is set + * + * @param channel channel to start + * + * @return none + */ +void +iapi_lowStartChannel (unsigned char channel) +{ + SDMA_D_START |= (1 << channel); +} + +/* ***************************************************************************/ +/**Stops the channel (core specific register) + * + * <b>Algorithm:</b> + * - Bit numbered "channel" of DspEnStopReg register is cleared + * + * <b>Notes:</b>\n + * - This is a write one to clear register + * + * @param channel channel to stop + * + * @return none + */ +void +iapi_lowStopChannel (unsigned char channel) +{ + SDMA_D_STATSTOP &= (1 << channel); +} + +#endif /* DSP */ diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiLowMcu.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiLowMcu.c new file mode 100644 index 000000000000..4e0ff7cac507 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiLowMcu.c @@ -0,0 +1,516 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiLowMcu.c + * + * $Id iapiLowMcu.c $ + * + * Description: + * This library is written in C to guarantee functionality and integrity in + * the usage of SDMA virtual DMA channels. This API (Application Programming + * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE + * fashion. + * These are the LOW level functions of the I.API specific to MCU. + * + * + * http://compass/mot.com/go/115342679 + * + * $Log iapiLowMcu.c $ + * + *****************************************************************************/ + +/* **************************************************************************** + * Include File Section + *****************************************************************************/ +#include <string.h> + +#include "epm.h" +#include "iapiLow.h" + +/* **************************************************************************** + * Function Section + *****************************************************************************/ +#ifdef MCU + +/* ***************************************************************************/ +/**Send a command on SDMA's channel zero. + * Check if buffer descriptor is already used by the sdma, if yes return + * an error as c0BDNum is wrong. + * + * <b>Notes</b>\n + * There is an upgrade in the script on the Context load command and + * the fact that the context structure has a fixed length of 20 or 24 + * depending on SDMA versions. + * + * @return + * - IAPI_SUCCESS + * - -iapi_errno if failure + */ +int +iapi_Channel0Command( channelDescriptor * cd_p, void * buf, + unsigned short nbyte, unsigned char command) +{ + channelControlBlock * ccb_p; + bufferDescriptor * bd_p; + int result = IAPI_SUCCESS; + unsigned char chNum; + + + /* + * Check data structures are properly initialized + */ + /* Channel descriptor validity */ + if (cd_p == NULL){ + result = IAPI_ERR_CD_UNINITIALIZED; + iapi_errno = result; + return -result; + } + + /* Channel control block validity */ + if (cd_p->ccb_ptr == NULL){ + result = IAPI_ERR_CCB_UNINITIALIZED; + iapi_errno = result; + return -result; + } + + /* Control block & Descriptpor associated with the channel being worked on */ + chNum = cd_p->channelNumber; + ccb_p = cd_p->ccb_ptr; + + /* Is channel already in use ? */ + if (ccb_p->baseBDptr != NULL ) { + result = IAPI_ERR_BD_ALLOCATED | IAPI_ERR_CH_AVAILABLE | chNum; + iapi_errno = result; + return -result; + } + + /* Allocation of buffer descriptors */ + bd_p = (bufferDescriptor *)MALLOC(sizeof( bufferDescriptor )); + if (bd_p != NULL) { + ccb_p->baseBDptr = (bufferDescriptor *)iapi_Virt2Phys(bd_p); + } else { + result = IAPI_ERR_BD_ALLOCATION | IAPI_ERR_CH_AVAILABLE | chNum; + iapi_errno = result; + return -result; + } + + /* Buffer descriptor setting */ + iapi_SetBufferDescriptor( bd_p, command , BD_WRAP|BD_DONE|BD_INTR , nbyte, + buf,NULL); + + /* Actually the transfer */ + iapi_lowStartChannel( cd_p->channelNumber ); + iapi_lowSynchChannel( cd_p->channelNumber ); + + /* Cleaning of allocation */ + FREE( bd_p ); + ccb_p->baseBDptr = NULL; + + return IAPI_SUCCESS; + +} + +/* ***************************************************************************/ +/**Starts the channel (core specific register) + * + * <b>Algorithm:</b>\n + * - Bit numbered "channel" of HostEnStartReg register is set + * + * @param channel channel to start + * + * @return none + */ +void +iapi_lowStartChannel (unsigned char channel) +{ + SDMA_H_START |= 1 << channel; +} + +/* ***************************************************************************/ +/**Stops the channel (core specific register) + * + * <b>Algorithm:</b> + * - Bit numbered "channel" of HostEnStopReg register is cleared + * + * <b>Notes:</b>\n + * - This is a write one to clear register + * + * @param channel channel to stop + * + * @return none + */ +void +iapi_lowStopChannel (unsigned char channel) +{ + SDMA_H_STATSTOP &= 1 << channel; +} + +/* ***************************************************************************/ +/**Initialize the initial priority of registers and channel enable + * RAM from the MCU side. No channels are enabled, all priorities are set to 0. + * + * @return none + */ +void +iapi_InitChannelTables(void) +{ + + /* No channel is enabled*/ + iapi_memset((void *)&SDMA_CHNENBL_0, 0x00, sizeof(unsigned long)*CH_NUM); + /* All channels have priority 0*/ + iapi_memset((void *)&SDMA_CHNPRI_0, 0x00, sizeof(unsigned long)*CH_NUM); +} + +/* ***************************************************************************/ +/** The host enable (HE), hosts override (HO), dsp enable (DE), dsp override + * (DO) registers are involved here. + * Host and Dsp enable registers are here to signify that the MCU or DSP side + * have prepared the appropriate buffers and are now ready. If the channel is + * owned by the MCU the override bit for that channel needs to be cleared : + * the host allows the channel to be used.\n + * + * Then the override bits can define (mcuOverride dspOverride):\n + * - 0 0 channel is public: transfer to/from MCU to DSP + * - 0 1 channel if owned by DSP + * - 1 0 channel if owned by MCU + * - 1 1 channel zero config + * + * See also :\n + * IAPI Table 1.1 "Channel configuration properties" + * + * @param channel channel to configure + * @param eventOverride event ownership + * @param mcuOverride ARM ownership + * @param dspOverride DSP ownership + * + * @return + * - -iapi_errno if the 3 override parameters are all set + * - IAPI_SUCCESS in other cases (valid cases) + */ +int +iapi_ChannelConfig (unsigned char channel, unsigned eventOverride, + unsigned mcuOverride, unsigned dspOverride) +{ + int result = IAPI_SUCCESS; + + if ( ( eventOverride == 1 ) && + ( mcuOverride == 1 ) && + ( dspOverride == 1 ) ){ + result = IAPI_ERR_CONFIG_OVERRIDE ; + iapi_errno = result; + return -result; + } else { + /* + * DSP side + */ + if ( dspOverride ){ + SDMA_H_DSPOVR &= ~( 1 << channel ); + } else { + SDMA_H_DSPOVR |= ( 1 << channel ); + } + /* + * Event + */ + if ( eventOverride ){ + SDMA_H_EVTOVR &= ~( 1 << channel ); + } else { + SDMA_H_EVTOVR |= ( 1 << channel ); + } + /* + * MCU side + */ + if ( mcuOverride ) { + SDMA_H_HOSTOVR &= ~( 1 << channel ); + } else { + SDMA_H_HOSTOVR |= ( 1 << channel ); + } + } + return IAPI_SUCCESS; +} +/* ***************************************************************************/ +/**Load the context data of a channel from SDMA + * + * <b>Algorithm:</b>\n + * - Setup BD with appropiate parameters + * - Start channel + * - Poll for answer + * + * @param *cd_p channel descriptor for channel 0 + * @param *buf pointer to receive context data + * @param channel channel for which the context data is requested + * + * @return none + */ +void +iapi_lowGetContext(channelDescriptor * cd_p, void * buf, unsigned char channel) +{ + bufferDescriptor * bd_p; + + bd_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + + /*Setup buffer descriptor with channel 0 command*/ + iapi_SetBufferDescriptor(&bd_p[0], + C0_GETDM, + (unsigned char)(BD_DONE | BD_INTR | BD_WRAP | BD_EXTD), + (unsigned short)sizeof(contextData)/4, + buf, + (void *)(CHANNEL_CONTEXT_BASE_ADDRESS + (sizeof(contextData)*channel/4))); + /* Receive, polling method*/ + iapi_lowStartChannel(cd_p->channelNumber); + iapi_lowSynchChannel(cd_p->channelNumber); +} +/* ***************************************************************************/ +/**Read "size" byte /2 at SDMA address (address) and write them in buf + * + * <b>Algorithm:</b>\n + * - Setup BD with appropiate parameters (C0_GETPM) + * - Start channel + * - Poll for answer + * + * <b>Notes</b>\n + * - Parameter "size" is in bytes, it represents the size of "buf", e.g. + * the size in bytes of the script to be loaded. + * - Parameter "address" denotes the RAM address for the script in SDMA + * + * @param *cd_p channel descriptor for channel 0 + * @param *buf pointer to receive the data + * @param size number of bytes to read + * @param address address in SDMA RAM to start reading from + * + * @return none + */ +void +iapi_lowGetScript(channelDescriptor * cd_p, void * buf, unsigned short size, + unsigned long address) +{ + bufferDescriptor * bd_p; + + bd_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + + /*Setup buffer descriptor with channel 0 command*/ + iapi_SetBufferDescriptor(&bd_p[0], + C0_GETPM, + (unsigned char)(BD_DONE | BD_INTR | BD_WRAP | BD_EXTD), + (unsigned short)size/2,/*count in shorts*/ + buf, + (void *)address); + /* Receive, polling method*/ + iapi_lowStartChannel(cd_p->channelNumber); + iapi_lowSynchChannel(cd_p->channelNumber); +} + +/* ***************************************************************************/ +/**Load a SDMA script to SDMA + * + * <b>Algorithm:</b>\n + * - Setup BD with appropiate parameters (C0_SETPM) + * - Start channel + * - Poll for answer + * + * <b>Notes</b>\b + * - Parameter "size" is in bytes, it represents the size of "buf", e.g. + * the size in bytes of the script to be uploaded. + * - Parameter "address" denotes the RAM address for the script in SDMA + * + * @param *cd_p channel descriptor for channel 0 + * @param *buf pointer to the script + * @param size size of the script, in bytes + * @param address address in SDMA RAM to place the script + * + * @return none + */ +void +iapi_lowSetScript(channelDescriptor * cd_p, void * buf, unsigned short size, + unsigned long address) +{ + bufferDescriptor * bd_p; + + bd_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + + /*Setup buffer descriptor with channel 0 command*/ + iapi_SetBufferDescriptor(&bd_p[0], + C0_SETPM, + (unsigned char)(BD_DONE | BD_INTR | BD_WRAP | BD_EXTD), + (unsigned short)size/2,/*count in shorts*/ + buf, + (void *)(address)); + /* Receive, polling method*/ + iapi_lowStartChannel(cd_p->channelNumber); + iapi_lowSynchChannel(cd_p->channelNumber); +} + + +/* ***************************************************************************/ +/**Load the context for a channel to SDMA + * + * <b>Algorithm:</b>\n + * - Send context and poll for answer. + * + * @param *cd_p channel descriptor for channel 0 + * @param *buf pointer to context data + * @param channel channel to place the context for + * + * @return none + */ +void +iapi_lowSetContext(channelDescriptor * cd_p, void * buf, unsigned char channel) +{ + + bufferDescriptor * local_bd_p; +#ifdef SDMA_SKYE + + unsigned char command =0; + + local_bd_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + + + command = channel <<3 ; + command = command | C0_SETCTX; + iapi_SetBufferDescriptor( &local_bd_p[0], + command, + (unsigned char)(BD_DONE | BD_INTR | BD_WRAP), + (unsigned short)(sizeof(contextData)/4), + buf, + NULL); +#else + + local_bd_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + + + + + iapi_SetBufferDescriptor( &local_bd_p[0], + C0_SETDM, + (unsigned char)(BD_DONE | BD_INTR | BD_WRAP|BD_EXTD), + (unsigned short)(sizeof(contextData)/4), + buf, + (void *)(2048+(sizeof(contextData)/4)*channel)); +#endif + /* Send */ + iapi_lowStartChannel( cd_p->channelNumber ); + iapi_lowSynchChannel( cd_p->channelNumber ); + +} + +/* ***************************************************************************/ +/**Associate specified channel with the script starting at the + * specified address. Channel 0 command is used to load the set-up context + * for the channel. The address used must be generated by the GUI tool + * used to create RAM images for SDMA. + * + * <b>Algorithm:</b>\n + * - Set-up and load the context. + * + * @param *cd_p pointer to the channel descriptor of the channel + * @param *data_p: pointer to the data identifying the script to be associated + * with the channel + * + * @return + * - IAPI_SUCCESS : OK + * - -iapi_errno : operation failed, return negated value of iapi_errno + */ + +int +iapi_lowAssignScript(channelDescriptor * cd_p, script_data * data_p) +{ + contextData * chContext; /* context to be loaded for the channel */ + channelDescriptor * cd0_p; /* pointer to channel descriptor of channel 0*/ + int result = IAPI_SUCCESS; + + /*Verify passed data*/ + if(cd_p == NULL || data_p == NULL) + { + result = IAPI_ERR_INVALID_PARAMETER; + iapi_errno = result; + return -result; + } + + /* Allocate context and initialize PC to required script start adress*/ + chContext = (contextData *) MALLOC(sizeof(contextData)); + if (chContext == NULL) + { + result = IAPI_ERR_B_ALLOC_FAILED | cd_p->channelNumber; + iapi_errno = result; + return -result; + } + + iapi_memset(chContext, 0x00, sizeof(contextData)); + chContext->channelState.pc = data_p->load_address; + + /* Send by context the event mask,base address for peripheral + * and watermark level + */ + chContext->gReg[0] = data_p->event_mask2; + chContext->gReg[1] = data_p->event_mask1; + chContext->gReg[6] = data_p->shp_addr; + chContext->gReg[7] = data_p->wml; + + /* Set transmited data to the CD*/ + cd_p->watermarkLevel = data_p->wml; + cd_p->eventMask1 = data_p->event_mask1; + cd_p->eventMask2 = data_p->event_mask2; + + /* Get the cd0_p*/ + cd0_p = (cd_p->ccb_ptr - cd_p->channelNumber)->channelDescriptor; + + /*load the context*/ + iapi_lowSetContext(cd0_p, chContext, cd_p->channelNumber); + + /* release allocated memory*/ + FREE(chContext); + + return IAPI_SUCCESS; +} + +/* ***************************************************************************/ +/** Set the channels to be triggered by an event. The for every channel that + *must be triggered by the event, the corresponding bit from channel_map + *parameter must be set to 1. (e.g. for the event to trigger channels 31 and + *0 one must pass 0x80000001) + * + * + * <b>Algorithm:</b>\n + * - Update the register from Channel Enable RAM with the channel_map + * + * @param event event for which to set the channel association + * @param channel_map channels to be triggered by event. Put the corresponding + * bit from this 32-bit value to 1 for every channel that should be + * triggered by the event. + * + * @return + * - IAPI_SUCCESS : OK + * - -iapi_errno : operation failed, return negated value of iapi_errno + */ +int +iapi_lowSetChannelEventMapping(unsigned char event, unsigned long channel_map) +{ + volatile unsigned long * channelEnableMatx; + int result = IAPI_SUCCESS; + + /* Check validity of event*/ + if(event < CH_NUM) + { + channelEnableMatx = &SDMA_CHNENBL_0; + channelEnableMatx[event] |= channel_map; + return result; + } + else + { + result = IAPI_ERR_INVALID_PARAMETER | event; + iapi_errno = result; + return -result; + } +} + +#endif /* MCU */ diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiMiddle.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiMiddle.c new file mode 100644 index 000000000000..dc255703626c --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiMiddle.c @@ -0,0 +1,613 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiMiddle.c + * + * $Id iapiMiddle.c $ + * + * Description: + * This library is written in C to guarantee functionality and integrity in + * the usage of SDMA virtual DMA channels. This API (Application Programming + * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE + * fashion. + * These are the MIDDLE level functions of the I.API. + * + * + * + * + * $Log iapiMiddle.c $ + * + *****************************************************************************/ + + +/* **************************************************************************** + * Include File Section + *****************************************************************************/ +#include "epm.h" +#include <string.h> + +#include "iapiLow.h" +#include "iapiMiddle.h" + +/* **************************************************************************** + * Global Variable Section + *****************************************************************************/ + + +/* **************************************************************************** + * Function Section + *****************************************************************************/ + +/* ***************************************************************************/ +/**Allocates one Buffer Descriptor structure using information present in the + * channel descriptor. + * + * @param *ccb_p channel control block used to get the channel descriptor + * + * @return + * - pointer on the new Buffer Descriptor + * - NULL if allocation failed + * + */ +bufferDescriptor * +iapi_AllocBD (channelControlBlock * ccb_p) + +{ + bufferDescriptor * ptrBD = NULL; + + if (ccb_p->channelDescriptor->bufferDescNumber != 0){ + ptrBD = (bufferDescriptor *) + MALLOC( ccb_p->channelDescriptor->bufferDescNumber * + sizeof(bufferDescriptor)); + } + if (ptrBD != NULL) { + ptrBD->mode.command = 0; + ptrBD->mode.status = 0; + ptrBD->mode.count = 0; + ptrBD->bufferAddr = NULL; + } + + return ptrBD; +} + +/* ***************************************************************************/ +/**Allocate one channel context data structure. + * + * @param **ctxd_p pointer to context data to be allocated + * @param channel channel number of context data structure + * + * @return + * - IAPI_SUCCESS + * - -iapi_errno if allocation failed + */ +int +iapi_AllocContext(contextData ** ctxd_p, unsigned char channel) +{ + contextData * ctxData; + int result = IAPI_SUCCESS; + + if (*ctxd_p != NULL){ + result = IAPI_ERR_CC_ALREADY_DEFINED | IAPI_ERR_CH_AVAILABLE | channel; + iapi_errno = result; + return -result; + } + + ctxData = (contextData *)MALLOC(sizeof(contextData)); + + if (ctxData !=NULL) { + *ctxd_p = ctxData; + return IAPI_SUCCESS; + + } else { + *ctxd_p = NULL; + result = IAPI_ERR_CC_ALLOC_FAILED | IAPI_ERR_CH_AVAILABLE | channel; + iapi_errno = result; + return -result; + } +} + +/* ***************************************************************************/ +/**Allocates channel description and fill in with default values. + * + * <b>Algorithm:</b>\n + * - Check channel properties. + * - Then modifies the properties of the channel description with default + * + * @param **cd_p pointer to channel descriptor to be allocated + * @param channel channel number of channel descriptor + * + * @return + * - IAPI_SUCCESS + * - -iapi_errno if allocation failed + * + */ +int +iapi_AllocChannelDesc (channelDescriptor ** cd_p, unsigned char channel) +{ +#ifdef MCU + volatile unsigned long * chPriorities = &SDMA_CHNPRI_0; +#endif /* MCU */ + channelDescriptor * tmpCDptr; + int result = IAPI_SUCCESS; + + + if (*cd_p != NULL){ + result = IAPI_ERR_CD_ALREADY_DEFINED | IAPI_ERR_CH_AVAILABLE | channel; + iapi_errno = result; + return -result; + } + + tmpCDptr = (channelDescriptor *)MALLOC(sizeof(channelDescriptor)); + + if (tmpCDptr != NULL){ + iapi_memcpy(tmpCDptr, &iapi_ChannelDefaults, sizeof (channelDescriptor)); + tmpCDptr->channelNumber = channel; +#ifdef MCU + if (chPriorities[channel] != 0) { + tmpCDptr->priority = chPriorities[channel]; + } else { + chPriorities[channel] = tmpCDptr->priority; + } +#endif + * cd_p = tmpCDptr ; + return IAPI_SUCCESS; + } else { + * cd_p = NULL; + result = IAPI_ERR_CD_ALLOC_FAILED | IAPI_ERR_CH_AVAILABLE | channel; + iapi_errno = result; + return -result; + } +} + +/* ***************************************************************************/ +/**Changes channel description information after performing sanity checks. + * + * <b>Algorithm:</b>\n + * - Check channel properties. + * - Then modifies the properties of the channel description. + * + * @param *cd_p channel descriptor of the channel to change + * @param whatToChange control code indicating the desired change + * @param newval new value + * + * @return + * - IAPI_SUCCESS + * - IAPI_FAILURE if change failed + * + */ +int +iapi_ChangeChannelDesc (channelDescriptor * cd_p, unsigned char whatToChange, + unsigned long newval) +{ + bufferDescriptor * tmpBDptr; + unsigned char index = 0; + int result = IAPI_SUCCESS; + + /* verify parameter validity */ + if (cd_p == NULL) { + result = IAPI_ERR_CD_UNINITIALIZED; + iapi_errno = result; + return -result; + } + + /* verify channel descriptor initialization */ + if (cd_p->ccb_ptr == NULL){ + result = IAPI_ERR_CCB_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } + + /* verify channel is not in use */ + tmpBDptr = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + for (index=cd_p->bufferDescNumber ; index>0 ; index--){ + if (tmpBDptr->mode.status & BD_DONE){ + result = IAPI_ERR_CH_IN_USE | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } + tmpBDptr++; + } + + /* Select the change accorded to the selector given in parameter */ + switch (whatToChange){ + + /* + * Channel Number + */ + case IAPI_CHANNELNUMBER: + /* Channel number can not be changed (description remains attached) */ + result = IAPI_ERR_CD_CHANGE_CH_NUMBER | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + + /* + * Buffer Descriptor Number + */ + case IAPI_BUFFERDESCNUMBER: + if (newval < MAX_BD_NUM){ + if (newval != cd_p->bufferDescNumber){ + /* Free memory used for previous old data */ + if (cd_p->ccb_ptr->baseBDptr != NULL){ + tmpBDptr = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + for (index=0 ; index < cd_p->bufferDescNumber ; index++){ + if (tmpBDptr->bufferAddr != NULL){ + if (cd_p->trust == FALSE) { + FREE(iapi_Phys2Virt(tmpBDptr->bufferAddr)); + } + } + tmpBDptr ++ ; + } + FREE((bufferDescriptor *)iapi_Phys2Virt((cd_p->ccb_ptr)->baseBDptr)); + } + (cd_p->ccb_ptr)->baseBDptr = NULL; + (cd_p->ccb_ptr)->currentBDptr = NULL; + /* Allocate and initialize structures */ + cd_p->bufferDescNumber = (unsigned char)newval; + cd_p->ccb_ptr->status.openedInit = FALSE; + if (IAPI_SUCCESS !=iapi_InitializeMemory (cd_p->ccb_ptr)) + { + result = IAPI_ERR_BD_ALLOCATION | cd_p->channelNumber; + iapi_errno = result; + return -result; + } + cd_p->ccb_ptr->status.openedInit = TRUE; + } + break; + } else { + result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } + + /* + * Buffer size + */ + case IAPI_BUFFERSIZE: + if (newval < MAX_BD_SIZE) + { + if (newval != cd_p->bufferSize) + { + /* Free memory used for previous old data */ + if (cd_p->ccb_ptr->baseBDptr != NULL) + { + tmpBDptr = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + for (index=0 ; index < cd_p->bufferDescNumber ; index++) + { + if (cd_p->trust == FALSE) + { + FREE(iapi_Phys2Virt(tmpBDptr->bufferAddr)); + } + tmpBDptr ++ ; + } + FREE((bufferDescriptor *)iapi_Phys2Virt((cd_p->ccb_ptr)->baseBDptr)); + } + (cd_p->ccb_ptr)->baseBDptr = NULL; + (cd_p->ccb_ptr)->currentBDptr = NULL; + /* Allocate and initialize structures */ + cd_p->bufferSize = (unsigned short)newval; + cd_p->ccb_ptr->status.openedInit = FALSE; + if (IAPI_SUCCESS !=iapi_InitializeMemory (cd_p->ccb_ptr)) + { + result = IAPI_ERR_BD_ALLOCATION | cd_p->channelNumber; + iapi_errno = result; + return -result; + } + cd_p->ccb_ptr->status.openedInit = TRUE; + } + break; + } + else + { + result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } + + + /* + * Blocking / non blocking feature + */ + case IAPI_BLOCKING: + if (newval < MAX_BLOCKING){ + cd_p->blocking = newval; + break; + } else { + result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } + + /* + * Synchronization method + */ + case IAPI_CALLBACKSYNCH: + if (newval < MAX_SYNCH){ + cd_p->callbackSynch = newval; + iapi_ChangeCallbackISR( cd_p, cd_p->callbackISR_ptr); + break; + } else { + result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } + + + /* + * Ownership of the channel + */ + case IAPI_OWNERSHIP: +#ifdef DSP + result = IAPI_ERR_NOT_ALLOWED | cd_p->channelNumber; + iapi_errno = result; + return -result; +#endif /* DSP */ +#ifdef MCU + if (newval < MAX_OWNERSHIP){ + cd_p->ownership = newval; + iapi_ChannelConfig( cd_p->channelNumber, + ( newval >> CH_OWNSHP_OFFSET_EVT ) & 1, + ( newval >> CH_OWNSHP_OFFSET_MCU ) & 1, + ( newval >> CH_OWNSHP_OFFSET_DSP ) & 1); + break; + } else { + result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } + +#endif /* MCU */ + + /* + * Priority + */ + case IAPI_PRIORITY: +#ifdef DSP + result = IAPI_ERR_NOT_ALLOWED | cd_p->channelNumber; + iapi_errno = result; + return -result; +#endif /* DSP */ + +#ifdef MCU + if (newval < MAX_CH_PRIORITY){ + volatile unsigned long * ChannelPriorities = &SDMA_CHNPRI_0; + ChannelPriorities[ cd_p->channelNumber ] = newval; + break; + } else { + result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } +#endif /* MCU */ + + + /* + * "Trust" property + */ + case IAPI_TRUST: + if (newval < MAX_TRUST){ + if (cd_p->trust != newval){ + cd_p->trust = newval; + if (newval == FALSE) { + if (IAPI_SUCCESS !=iapi_InitializeMemory (cd_p->ccb_ptr)) + { + result = IAPI_ERR_BD_ALLOCATION | cd_p->channelNumber; + iapi_errno = result; + return -result; + } + } + } + break; + } else { + result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } + + /* + * Callback function pointer + */ + case IAPI_CALLBACKISR_PTR: + if ( (void *)newval != NULL){ + { + union { + void * voidstar; + void (* funcptr)(channelDescriptor * cd_p, void * arg); + } value; + value.voidstar = (void*) newval; + cd_p->callbackISR_ptr = value.funcptr; + } + iapi_ChangeCallbackISR( cd_p, cd_p->callbackISR_ptr); + break; + } else { + result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } + + + /* + * Channel Control Block pointer + */ + case IAPI_CCB_PTR: + cd_p->ccb_ptr = (channelControlBlock *)newval; + cd_p->ccb_ptr->channelDescriptor = cd_p; + break; + + /* + * WRAP/UNWRAP + */ + case IAPI_BDWRAP: + /* point to first BD */ + tmpBDptr = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr); + /* to point to last BD */ + tmpBDptr += cd_p->bufferDescNumber - 1; + if (newval == TRUE){ + /* wrap last BD */ + tmpBDptr->mode.status |= BD_WRAP; + break; + } + else if (newval == FALSE){ + /* unwrap last BD */ + tmpBDptr->mode.status &= ~BD_WRAP; + break; + } + else { + result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } + + + /* + * Watermark level + */ + case IAPI_WML: +#ifdef DSP + result = IAPI_ERR_NOT_ALLOWED | cd_p->channelNumber; + iapi_errno = result; + return -result; +#endif /* DSP */ +#ifdef MCU + if (newval < MAX_WML){ + if (cd_p->watermarkLevel != newval){ + cd_p->watermarkLevel = newval; + } + break; + } + else { + result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } +#endif /* MCU */ + + /* + * Detect errors + */ + default: + result = IAPI_ERR_CD_CHANGE_UNKNOWN | IAPI_ERR_CH_AVAILABLE | + cd_p->channelNumber; + iapi_errno = result; + return -result; + } + + return IAPI_SUCCESS; +} + + +/* ***************************************************************************/ +/**Initialize a table of function pointers that contain the interrupt Service + * Routine callback pointers for the SDMA channels with a default value + * + * <b>Algorithm:</b>\n + * - Loop on each element of the global IAPI variable callbackIsrTable + * + * @param *func_p default callback functon for all SDMA channels + * + * @return none + */ +void +iapi_InitializeCallbackISR(void(* func_p)(channelDescriptor * cd_p, void * arg)) +{ + unsigned long chCnt; + + for (chCnt = 0 ; chCnt < CH_NUM ; chCnt++){ + callbackIsrTable[chCnt] = func_p; + } +} + +/* ***************************************************************************/ +/**For the specified channel control block, attach the array of buffer + * descriptors, the channel description structure and initialize channel's + * status using information in the channel descriptor. + * + * @param *ccb_p pointer to channel control block + * + * @return none + * + */ +int +iapi_InitializeMemory (channelControlBlock * ccb_p) +{ + bufferDescriptor * bd_p; + unsigned char index; + int result = IAPI_SUCCESS; + + /* Attach the array of Buffer descriptors */ + bd_p = iapi_AllocBD( ccb_p ); + if (bd_p != NULL) + { + ccb_p->baseBDptr = (bufferDescriptor *)iapi_Virt2Phys(bd_p); + ccb_p->currentBDptr = ccb_p->baseBDptr; + for(index=0 ;index < ccb_p->channelDescriptor->bufferDescNumber-1 ; index++) + { + if (ccb_p->channelDescriptor->trust == TRUE) + { + iapi_SetBufferDescriptor(bd_p, + (unsigned char)ccb_p->channelDescriptor->dataSize, + BD_CONT|BD_EXTD, ccb_p->channelDescriptor->bufferSize, + NULL, NULL); + } + else + { + if (ccb_p->channelDescriptor->bufferSize != 0) + { + iapi_SetBufferDescriptor(bd_p, + (unsigned char)ccb_p->channelDescriptor->dataSize, + BD_CONT|BD_EXTD, ccb_p->channelDescriptor->bufferSize, + MALLOC(ccb_p->channelDescriptor->bufferSize), NULL); + } + } + bd_p++; + } + + if (ccb_p->channelDescriptor->trust == TRUE) + { + iapi_SetBufferDescriptor(bd_p, + (unsigned char)ccb_p->channelDescriptor->dataSize, + BD_EXTD|BD_WRAP|BD_INTR, ccb_p->channelDescriptor->bufferSize, + NULL, NULL); + } + else + { + if (ccb_p->channelDescriptor->bufferSize != 0) + { + iapi_SetBufferDescriptor(bd_p, + (unsigned char)ccb_p->channelDescriptor->dataSize, + BD_EXTD|BD_WRAP|BD_INTR, + ccb_p->channelDescriptor->bufferSize, + MALLOC(ccb_p->channelDescriptor->bufferSize), NULL); + } + } + } + else + { + result = IAPI_ERR_BD_ALLOCATION; + return -result; + } + return result; +} diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiMiddleMcu.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiMiddleMcu.c new file mode 100644 index 000000000000..8dc814418e4c --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiMiddleMcu.c @@ -0,0 +1,52 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiMiddleMcu.c + * + * $Id iapiMiddleMcu.c $ + * + * Description: + * This library is written in C to guarantee functionality and integrity in + * the usage of SDMA virtual DMA channels. This API (Application Programming + * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE + * fashion. + * These are the MIDDLE level functions of the I.API specific to MCU. + * + * + * + * + * $Log iapiMiddleMcu.c $ + * + *****************************************************************************/ + +/* **************************************************************************** + * Include File Section + *****************************************************************************/ +#include "epm.h" +#include <string.h> + +#include "iapiLow.h" +#include "iapiMiddle.h" + +/* **************************************************************************** + * Global Variable Section + *****************************************************************************/ + +/*extern void * __HEAP_START; +extern void * __HEAP_END; +*/ + +/* **************************************************************************** + * Function Section + *****************************************************************************/ diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiOS.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiOS.c new file mode 100644 index 000000000000..3fe50621f5f6 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiOS.c @@ -0,0 +1,61 @@ +/****************************************************************************** + * + * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + ****************************************************************************** + * + * File: iapiOS.c + * + * $Id iapiOS.c $ + * + * Description: + * This library is written in C to guarantee functionality and integrity in + * the usage of SDMA virtual DMA channels. This API (Application Programming + * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE + * fashion. + * These are the OS level functions of the I.API - are OS dependant and must + * be provided by the user of I.API. + * + * + * / + * + * $Log iapiOS.c $ + * + *****************************************************************************/ + +/* **************************************************************************** + * Include File Section + *****************************************************************************/ +#include "epm.h" +#include "iapiLow.h" + +/** + * Function Section + */ + +void*(* iapi_Malloc) (size_t size); +void (* iapi_Free) (void * ptr); + +void*(* iapi_Virt2Phys) (void * ptr); +void*(* iapi_Phys2Virt) (void * ptr); + +void (* iapi_WakeUp)(int); +void (* iapi_GotoSleep)(int); +void (* iapi_InitSleep)(int); + +void*(* iapi_memcpy)(void *dest, const void *src, size_t count); +void*(* iapi_memset)(void *dest, int c, size_t count); + +void (* iapi_EnableInterrupts)(void); +void (* iapi_DisableInterrupts)(void); + +int (* iapi_GetChannel)(int); +int (* iapi_ReleaseChannel)(int); diff --git a/arch/arm/plat-mxc/sdma/sdma.c b/arch/arm/plat-mxc/sdma/sdma.c new file mode 100644 index 000000000000..0adf526df713 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/sdma.c @@ -0,0 +1,1327 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file plat-mxc/sdma/sdma.c + * @brief This file contains functions for Smart DMA API + * + * SDMA (Smart DMA) is used for transferring data between MCU and peripherals + * + * @ingroup SDMA + */ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <asm/irq.h> +#include <asm/arch/dma.h> +#include <asm/arch/hardware.h> + +#include <asm/semaphore.h> +#include <linux/spinlock.h> + +#include <asm/uaccess.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> + +#include <linux/proc_fs.h> + +#include "iapi.h" + +#define M3_BASE_ADDRESS 0x80000000 +#define CHAD(ch) sdma_data[0].cd->ccb_ptr[ch].channelDescriptor + +/*! + * SDMA status mutex + */ +static struct semaphore sdma_status_mutex; + +/*! + * SDMA channel sleep queues + */ +//static struct semaphore sdma_sleep_mutex[MAX_DMA_CHANNELS]; +static wait_queue_head_t sdma_sleep_queue[MAX_DMA_CHANNELS]; + +/*! + * SDMA channel synchronization + */ +static struct semaphore sdma_synch_mutex[MAX_DMA_CHANNELS]; + +/*! + * SDMA buffers pool initialization function + */ +extern void init_sdma_pool(void); + +/*! + * Flags are save and restored during interrupt handler + */ +unsigned long flags; + +struct clk *mxc_sdma_ahb_clk, *mxc_sdma_ipg_clk; + +/*! + * Structure containing sdma channels information. + */ +typedef struct { + /*! Channel number */ + int channel; + /*! Channel usage name */ + int in_use; + /*! Name of device using the channel */ + char devicename[MAX_DEVNAME_LENGTH]; + /*! Transfer type. Needed for setting SDMA script */ + sdma_transferT transfer_type; + /*! Peripheral type. Needed for setting SDMA script */ + sdma_periphT peripheral_type; + /*! Watermark level of device's fifo */ + __u32 watermark_level; + /*! Peripheral event id */ + int event_id; + /*! Peripheral event id2 (for ATA only) */ + int event_id2; + /*! Running status (boolean) */ + int running; + /*! buffer descriptors number */ + int bd_number; + /*! callback function */ + dma_callback_t callback; + /*! callback argument */ + void *arg; + /*! SDMA data access word size */ + unsigned long word_size:8; + /*! channel descriptor pointer */ + channelDescriptor *cd; +} sdma_struct; + +/*! + * Used to save the status of channels. + */ +static sdma_struct sdma_data[MAX_DMA_CHANNELS]; + +/*! + * Stores the start address of the SDMA scripts + */ +static sdma_script_start_addrs sdma_script_addrs; + +extern void mxc_sdma_get_script_info(sdma_script_start_addrs * sdma_script_add); + +/*! + * Init sleep mutex of the channel + * + * @param channel channel number + */ +static void sdma_init_sleep(int channel) +{ + init_waitqueue_head(&sdma_sleep_queue[channel]); +} + +/*! + * Puts channel to sleep + * + * @param channel channel number + */ +static void sdma_sleep_channel(int channel) +{ + while ((iapi_SDMAIntr & (1 << channel)) == 0) { + wait_event_interruptible(sdma_sleep_queue[channel], + ((iapi_SDMAIntr & (1 << channel)) != + 0)); + } +} + +/*! + * Wake up channel from sleep + * + * @param channel channel number + */ +static void sdma_wakeup_channel(int channel) +{ + wake_up_interruptible(&sdma_sleep_queue[channel]); +} + +/*! + * Sdma interrupt handler routine. + * Calls channels callback function + * + * @param irq the interrupt number + * @param dev_id driver private data + * @return the function returns \b IRQ_RETVAL(1) - interrupt was handled + */ +static irqreturn_t sdma_int_handler(int irq, void *dev_id) +{ + IRQ_Handler(); + return IRQ_RETVAL(1); +} + +/*! + * I.API channel callback function + * + * @param cd channel descriptor structure + * @param channel_data SDMA struct of the current channel + */ +static void iapi_interrupt_callback(channelDescriptor * cd, + sdma_struct * channel_data) +{ + int channel; + dma_callback_t callback; + void *arg; + + channel = channel_data->channel; + + channel_data->running = 0; + + arg = channel_data->arg; + + if (arg == 0) { + arg = (void *)&channel; + } + + callback = channel_data->callback; + + if (callback != 0) { + callback(arg); + } +} + +/*! + * Returns pc of SDMA script according to peripheral and transfer type + * + * @param peripheral_type peripheral type + * @param transfer_type transfer type + * + * @return PC of SDMA script +*/ +static unsigned short sdma_get_pc(sdma_periphT peripheral_type, + sdma_transferT transfer_type) +{ + int res = 0; + + if (peripheral_type == MEMORY) { + switch (transfer_type) { + case emi_2_int: + res = sdma_script_addrs.mxc_sdma_ap_2_ap_addr; + break; + case emi_2_emi: + res = sdma_script_addrs.mxc_sdma_ap_2_ap_addr; + break; + case int_2_emi: + res = sdma_script_addrs.mxc_sdma_ap_2_ap_addr; + break; + default: + res = -EINVAL; + } + } else if (peripheral_type == DSP) { + switch (transfer_type) { + case emi_2_dsp: + res = sdma_script_addrs.mxc_sdma_ap_2_bp_addr; + break; + case dsp_2_emi: + res = sdma_script_addrs.mxc_sdma_bp_2_ap_addr; + break; + case dsp_2_emi_loop: + res = + sdma_script_addrs. + mxc_sdma_loopback_on_dsp_side_addr; + break; + case emi_2_dsp_loop: + res = + sdma_script_addrs.mxc_sdma_mcu_interrupt_only_addr; + break; + default: + res = -EINVAL; + } + } else if (peripheral_type == FIRI) { + switch (transfer_type) { + case per_2_int: + res = sdma_script_addrs.mxc_sdma_firi_2_per_addr; + break; + case per_2_emi: + res = sdma_script_addrs.mxc_sdma_firi_2_mcu_addr; + break; + case int_2_per: + res = sdma_script_addrs.mxc_sdma_per_2_firi_addr; + break; + case emi_2_per: + res = sdma_script_addrs.mxc_sdma_mcu_2_firi_addr; + break; + default: + res = -EINVAL; + } + } else if (peripheral_type == UART) { + switch (transfer_type) { + case per_2_int: + res = sdma_script_addrs.mxc_sdma_uart_2_per_addr; + break; + case per_2_emi: + res = sdma_script_addrs.mxc_sdma_uart_2_mcu_addr; + break; + case int_2_per: + res = sdma_script_addrs.mxc_sdma_per_2_app_addr; + break; + case emi_2_per: + res = sdma_script_addrs.mxc_sdma_mcu_2_app_addr; + break; + default: + res = -EINVAL; + } + } else if (peripheral_type == UART_SP) { + switch (transfer_type) { + case per_2_int: + res = sdma_script_addrs.mxc_sdma_uartsh_2_per_addr; + break; + case per_2_emi: + res = sdma_script_addrs.mxc_sdma_uartsh_2_mcu_addr; + break; + case int_2_per: + res = sdma_script_addrs.mxc_sdma_per_2_shp_addr; + break; + case emi_2_per: + res = sdma_script_addrs.mxc_sdma_mcu_2_shp_addr; + break; + default: + res = -EINVAL; + } + } else if (peripheral_type == ATA) { + switch (transfer_type) { + case per_2_emi: + res = sdma_script_addrs.mxc_sdma_ata_2_mcu_addr; + break; + case emi_2_per: + res = sdma_script_addrs.mxc_sdma_mcu_2_ata_addr; + break; + default: + res = -EINVAL; + } + } else if (peripheral_type == CSPI || peripheral_type == EXT || + peripheral_type == SSI) { + switch (transfer_type) { + case per_2_int: + res = sdma_script_addrs.mxc_sdma_app_2_per_addr; + break; + case per_2_emi: + res = sdma_script_addrs.mxc_sdma_app_2_mcu_addr; + break; + case int_2_per: + res = sdma_script_addrs.mxc_sdma_per_2_app_addr; + break; + case emi_2_per: + res = sdma_script_addrs.mxc_sdma_mcu_2_app_addr; + break; + default: + res = -EINVAL; + } + } else if (peripheral_type == SSI_SP || peripheral_type == MMC || + peripheral_type == SDHC || peripheral_type == CSPI_SP) { + switch (transfer_type) { + case per_2_int: + res = sdma_script_addrs.mxc_sdma_shp_2_per_addr; + break; + case per_2_emi: + res = sdma_script_addrs.mxc_sdma_shp_2_mcu_addr; + break; + case int_2_per: + res = sdma_script_addrs.mxc_sdma_per_2_shp_addr; + break; + case emi_2_per: + res = sdma_script_addrs.mxc_sdma_mcu_2_shp_addr; + break; + default: + res = -EINVAL; + } + } else if (peripheral_type == MSHC) { + switch (transfer_type) { + case per_2_emi: + res = sdma_script_addrs.mxc_sdma_mshc_2_mcu_addr; + break; + case emi_2_per: + res = sdma_script_addrs.mxc_sdma_mcu_2_mshc_addr; + break; + default: + res = -EINVAL; + } + } else if (peripheral_type == CCM) { + switch (transfer_type) { + case per_2_emi: + res = sdma_script_addrs.mxc_sdma_dptc_dvfs_addr; + break; + default: + res = -EINVAL; + } + } + + if (res < 0) { + printk(KERN_ERR "SDMA script not found\n"); + } + + return res; + +} + +/*! + * Downloads channel context according to channel parameters + * + * @param channel channel number + * @param p channel parameters + */ +static int sdma_load_context(int channel, dma_channel_params * p) +{ + script_data context; + int res; + + res = 0; + + memset(&context, 0, sizeof(script_data)); + context.load_address = sdma_get_pc(p->peripheral_type, + p->transfer_type); + + if (context.load_address > 0) { + if ((p->peripheral_type != MEMORY) + && (p->peripheral_type != DSP)) { + /* Watermark Level */ + context.wml = p->watermark_level; + /* Event mask */ + if (p->event_id) { + context.event_mask1 = 0x1 << p->event_id; + } + /* Event mask2 (ATA) */ + if (p->event_id2) { + context.event_mask2 = 0x1 << p->event_id2; + } + /* Address */ + context.shp_addr = (unsigned long)(p->per_address); + iapi_IoCtl(sdma_data[channel].cd, + IAPI_CHANGE_PERIPHADDR, p->per_address); + } else { + context.wml = M3_BASE_ADDRESS; + } + + sdma_data[channel].transfer_type = p->transfer_type; + sdma_data[channel].peripheral_type = p->peripheral_type; + sdma_data[channel].watermark_level = p->watermark_level; + iapi_AssignScript(sdma_data[channel].cd, &context); + } else { + res = context.load_address; + } + + return res; +} + +/*! + * Setup channel according to parameters. Must be called once after mxc_request_dma() + * + * @param channel channel number + * @param p channel parameters pointer + * @return 0 on success, error code on fail + */ +int mxc_dma_setup_channel(int channel, dma_channel_params * p) +{ + int err = 0; + int i; + + mxc_dma_stop(channel); + + for (i = 0; i < sdma_data[channel].bd_number; i++) { + iapi_IoCtl(sdma_data[channel].cd, + (i << BD_NUM_OFFSET) | + IAPI_CHANGE_SET_STATUS, (unsigned long)0); + } + + sdma_data[channel].bd_number = (p->bd_number <= 0) ? 1 : p->bd_number; + + sdma_data[channel].word_size = p->word_size; + + sdma_data[channel].event_id = p->event_id; + sdma_data[channel].event_id2 = p->event_id2; + + sdma_data[channel].callback = p->callback; + + sdma_data[channel].arg = p->arg; + + err = iapi_IoCtl(sdma_data[channel].cd, + IAPI_CHANGE_BDNUM, sdma_data[channel].bd_number); + + if (err < 0) { + printk(KERN_ERR "Failed allocating buffer \ +descriptors (0x%x)\n", err); + err = -ENOMEM; + goto setup_channel_fail; + } + + if (channel != 0) { + switch (p->transfer_type) { + case dsp_2_per: + break; + case emi_2_per: + case int_2_per: + case per_2_int: + case per_2_emi: + /* + * Peripheral <------> Memory + * evtOvr = 0 dspOvr = 1 + */ + iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_OWNERSHIP, + (OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT) | + (OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU) | + (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP)); + if (p->event_id) { + err = iapi_SetChannelEventMapping(p->event_id, + 0x1 << + channel); + } + if (!err && p->peripheral_type == ATA && p->event_id2) { + err = iapi_SetChannelEventMapping(p->event_id2, + 0x1 << + channel); + } + break; + case emi_2_dsp: + case int_2_dsp: + case dsp_2_int: + case dsp_2_emi: + case dsp_2_dsp: + /* + * DSP <-----------> Memory + * evtOvr = 1 dspOvr = 0 + */ + iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_OWNERSHIP, + (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT) | + (OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU) | + (OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP)); + break; + case emi_2_int: + case emi_2_emi: + case int_2_int: + case int_2_emi: + case emi_2_dsp_loop: + case dsp_2_emi_loop: + /* evtOvr = 1 dspOvr = 1 */ + iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_OWNERSHIP, + (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT) | + (OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU) | + (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP)); + break; + case per_2_dsp: + /* evtOvr = 0 dspOvr = 0 */ + iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_OWNERSHIP, + (OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT) | + (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU) | + (OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP)); + err = iapi_SetChannelEventMapping(p->event_id, + 0x1 << channel); + break; + default: + break; + printk(KERN_ERR "Wrong SDMA transfer type\n"); + err = -EINVAL; + } + if (err == 0) { + err = sdma_load_context(channel, p); + iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_PRIORITY, + MXC_SDMA_DEFAULT_PRIORITY); + } + } + setup_channel_fail: + return err; +} + +/*! + * Setup the channel priority. This can be used to change the default priority + * for the channel. + * + * @param channel channel number + * @param priority priority to be set for the channel + * + * @return 0 on success, error code on failure + */ +int mxc_dma_set_channel_priority(unsigned int channel, unsigned int priority) +{ + if (priority < MXC_SDMA_MIN_PRIORITY + || priority > MXC_SDMA_MAX_PRIORITY) { + return -EINVAL; + } + return iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_PRIORITY, + priority); +} + +/*! + * Allocates dma channel. + * If channel's value is 0, then the function allocates a free channel + * dynamically and sets its value to channel. + * Else allocates requested channel if it is free. + * If the channel is busy or no free channels (in dynamic allocation) -EBUSY returned. + * + * @param channel pointer to channel number + * @param devicename device name + * @return 0 on success, error code on fail + */ +int mxc_request_dma(int *channel, const char *devicename) +{ + int i, res; + + res = 0; + + down(&sdma_status_mutex); + + /* Dynamic allocation */ + if (*channel == 0) { + for (i = MAX_DMA_CHANNELS - 1; i > 0; i--) { + if (!sdma_data[i].in_use) { + *channel = i; + break; + } + } + } + + if (*channel > 0 && *channel < MAX_DMA_CHANNELS && + sdma_data[*channel].in_use == 0) { + res = iapi_Open(sdma_data[0].cd, *channel); + + if (res < 0) { + printk(KERN_ERR "Failed iapi_Open channel %d, 0x%x\n", + *channel, res); + } else { + sdma_data[*channel].in_use = 1; + strcpy(sdma_data[*channel].devicename, devicename); + sdma_data[*channel].cd = CHAD(*channel); + + iapi_IoCtl(sdma_data[*channel].cd, IAPI_CHANGE_SYNCH, + CALLBACK_ISR); + iapi_IoCtl(sdma_data[*channel].cd, + IAPI_CHANGE_CALLBACKFUNC, + (unsigned long)iapi_interrupt_callback); + iapi_IoCtl(sdma_data[*channel].cd, + IAPI_CHANGE_USER_ARG, + (unsigned long)&(sdma_data[*channel])); + } + } else { + res = -EBUSY; + } + + up(&sdma_status_mutex); + + return res; +} + +/*! + * Configures request parameters. Can be called multiple times after + * mxc_request_dma() and mxc_dma_setup_channel(). + * + * + * @param channel channel number + * @param p request parameters pointer + * @param bd_index index of buffer descriptor to set + * @return 0 on success, error code on fail + */ +int mxc_dma_set_config(int channel, dma_request_t * p, int bd_index) +{ + unsigned char param; + + if (!sdma_data[channel].in_use) { + return -EINVAL; + } + + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | + IAPI_CHANGE_SET_TRANSFER_CD, sdma_data[channel].word_size); + + param = BD_DONE | BD_INTR | BD_EXTD; + + if (sdma_data[channel].bd_number > 1 && p->bd_cont == 1) { + param |= BD_CONT; + } + + if (bd_index == sdma_data[channel].bd_number - 1) { + param |= BD_WRAP; + } + + switch (sdma_data[channel].transfer_type) { + case emi_2_per: + case dsp_2_per: + case int_2_per: + case emi_2_dsp: + case int_2_dsp: + case emi_2_dsp_loop: + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | + IAPI_CHANGE_SET_BUFFERADDR, + (unsigned long)p->sourceAddr); + break; + case per_2_int: + case per_2_emi: + case per_2_dsp: + case dsp_2_int: + case dsp_2_emi: + case dsp_2_dsp: + case dsp_2_emi_loop: + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | + IAPI_CHANGE_SET_BUFFERADDR, + (unsigned long)p->destAddr); + break; + case emi_2_int: + case emi_2_emi: + case int_2_int: + case int_2_emi: + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | + IAPI_CHANGE_SET_BUFFERADDR, + (unsigned long)p->sourceAddr); + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | + IAPI_CHANGE_SET_EXTDBUFFERADDR, + (unsigned long)p->destAddr); + break; + default: + break; + } + + /* Change the endianness for DSP to MCU Data transfers */ + if (sdma_data[channel].transfer_type == dsp_2_emi || + sdma_data[channel].transfer_type == emi_2_dsp) { + iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_SET_ENDIANNESS, + SET_BIT_ALL); + } + + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | + IAPI_CHANGE_SET_COUNT, p->count); + + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | IAPI_CHANGE_SET_STATUS, param); + + return 0; +} + +/*! + * Configures the BD_INTR bit on a buffer descriptor parameters. + * + * + * @param channel channel number + * @param bd_index index of buffer descriptor to set + * @param bd_intr flag to set or clear the BD_INTR bit + * @return 0 on success, error code on fail + */ +void mxc_dma_set_bd_intr(int channel, int bd_index, int bd_intr) +{ + unsigned long param; + + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | + IAPI_CHANGE_GET_STATUS, (unsigned long)¶m); + + if (bd_intr) { + param |= BD_INTR; + } else { + param &= ~BD_INTR; + } + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | IAPI_CHANGE_SET_STATUS, param); + +} + +/*! + * Gets the BD_INTR bit on a buffer descriptor. + * + * + * @param channel channel number + * @param bd_index index of buffer descriptor to set + * + * @return returns the BD_INTR bit status + */ +int mxc_dma_get_bd_intr(int channel, int bd_index) +{ + unsigned long bd_status = 0; + + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | + IAPI_CHANGE_GET_STATUS, (unsigned long)&bd_status); + + return (bd_status & BD_INTR); +} + +/*! + * Stop the current transfer + * + * @param channel channel number + * @param buffer_number number of buffers (beginning with 0), + * whose done bits should be reset to 0 + */ +int mxc_dma_reset(int channel, int buffer_number) +{ + unsigned char param = 0; + int i = 0; + + if (!sdma_data[channel].in_use) { + return -EINVAL; + } + + /* clear the BD_DONE bits for all the necessary buffers */ + for (i = 0; i < buffer_number; i++) { + + iapi_IoCtl(sdma_data[channel].cd, (i << BD_NUM_OFFSET) | + IAPI_CHANGE_GET_STATUS, (unsigned long)¶m); + + /* clear the BD_DONE bit of the buffer */ + param = param & (~BD_DONE); + + iapi_IoCtl(sdma_data[channel].cd, (i << BD_NUM_OFFSET) | + IAPI_CHANGE_SET_STATUS, param); + } + + return 0; +} + +/*! + * Returns request parameters. + * + * @param channel channel number + * @param p request parameters pointer + * @param bd_index index of buffer descriptor to get + * @return 0 on success, error code on fail + */ +int mxc_dma_get_config(int channel, dma_request_t * p, int bd_index) +{ + int err = 0; + unsigned long bd_status; + unsigned long bd_count; + __u8 *sourceAddr; + __u8 *destAddr; + + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | + IAPI_CHANGE_GET_STATUS, (unsigned long)&bd_status); + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | + IAPI_CHANGE_GET_COUNT, (unsigned long)&bd_count); + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | + IAPI_CHANGE_GET_BUFFERADDR, (unsigned long)&sourceAddr); + + switch (sdma_data[channel].transfer_type) { + case emi_2_per: + case dsp_2_per: + case int_2_per: + case emi_2_dsp: + case int_2_dsp: + case emi_2_dsp_loop: + p->sourceAddr = sourceAddr; + break; + case per_2_int: + case per_2_emi: + case per_2_dsp: + case dsp_2_int: + case dsp_2_emi: + case dsp_2_dsp: + case dsp_2_emi_loop: + p->destAddr = sourceAddr; + break; + case emi_2_int: + case emi_2_emi: + case int_2_int: + case int_2_emi: + p->sourceAddr = sourceAddr; + iapi_IoCtl(sdma_data[channel].cd, + (bd_index << BD_NUM_OFFSET) | + IAPI_CHANGE_GET_EXTDBUFFERADDR, + (unsigned long)&destAddr); + p->destAddr = destAddr; + break; + default: + break; + } + + p->count = bd_count; + p->bd_done = bd_status & BD_DONE; + p->bd_cont = bd_status & BD_CONT; + p->bd_error = bd_status & BD_RROR; + + return err; +} + +/*! + * This function is used by MXC IPC's write_ex2. It passes the pointer to the + * data control structure to iapi_write_ipcv2() + * + * @param channel SDMA channel number + * @param ctrl_ptr Data Control structure pointer + */ +int mxc_sdma_write_ipcv2(int channel, void *ctrl_ptr) +{ + return iapi_Write_ipcv2(sdma_data[channel].cd, ctrl_ptr); +} + +/*! + * This function is used by MXC IPC's read_ex2. It passes the pointer to the + * data control structure to iapi_read_ipcv2() + * + * @param channel SDMA channel number + * @param ctrl_ptr Data Control structure pointer + */ +int mxc_sdma_read_ipcv2(int channel, void *ctrl_ptr) +{ + return iapi_Read_ipcv2(sdma_data[channel].cd, ctrl_ptr); +} + +/*! + * Starts dma channel. + * + * @param channel channel number + */ +int mxc_dma_start(int channel) +{ + if (sdma_data[channel].running == 0) { + sdma_data[channel].running = 1; + iapi_StartChannel(channel); + } + + return 0; +} + +/*! + * Stops dma channel. + * + * @param channel channel number + */ +int mxc_dma_stop(int channel) +{ + iapi_StopChannel(channel); + sdma_data[channel].running = 0; + + return 0; +} + +/*! + * Frees dma channel. + * + * @param channel channel number + */ +void mxc_free_dma(int channel) +{ + int i; + + mxc_dma_stop(channel); + + if (sdma_data[channel].event_id != 0) { + iapi_SetChannelEventMapping(sdma_data[channel].event_id, 0x0); + } + if (sdma_data[channel].event_id2 != 0) { + iapi_SetChannelEventMapping(sdma_data[channel].event_id2, 0x0); + } + + sdma_data[channel].event_id = 0; + + iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_PRIORITY, 0x0); + iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_OWNERSHIP, + (OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT) | + (OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU) | + (OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP)); + + for (i = 0; i < sdma_data[channel].bd_number; i++) { + iapi_IoCtl(sdma_data[channel].cd, + (i << BD_NUM_OFFSET) | + IAPI_CHANGE_SET_STATUS, (unsigned long)0); + } + + iapi_Close(sdma_data[channel].cd); + + strcpy(sdma_data[channel].devicename, "not used"); + + sdma_data[channel].in_use = 0; +} + +/*! + * Initializes channel's priorities + * + */ +static void __init init_priorities(void) +{ + iapi_IoCtl(sdma_data[0].cd, IAPI_CHANGE_PRIORITY, 0x7); +} + +/*! + * Initializes events table + */ +static void __init init_event_table(void) +{ + int channel; + + for (channel = 0; channel < MAX_DMA_CHANNELS; channel++) { + iapi_SetChannelEventMapping(channel, 0); + } +} + +/*! + * Sets callback function. Used with standard dma api + * for supporting interrupts + * + * @param channel channel number + * @param callback callback function pointer + * @param arg argument for callback function + */ +void mxc_dma_set_callback(int channel, dma_callback_t callback, void *arg) +{ + sdma_data[channel].callback = callback; + sdma_data[channel].arg = arg; +} + +/*! + * Synchronization function used by I.API + * + * @param channel channel number + */ +static int getChannel(int channel) +{ + if (irqs_disabled() || in_atomic()) { + if (down_trylock(&sdma_synch_mutex[channel])) { + return -EBUSY; + } + } else { + if (down_interruptible(&sdma_synch_mutex[channel])) { + return -EBUSY; + } + } + + return 0; +} + +/*! + * Synchronization function used by I.API + * + * @param channel channel number + */ +static int releaseChannel(int channel) +{ + up(&sdma_synch_mutex[channel]); + return 0; +} + +/*! + * Unmask interrupt function. Used by I.API + * + */ +static void unmask_sdma_interrupt(void) +{ + /* Commented out tp take care of the PREEMPT_RT option + * local_irq_restore(flags); + */ +} + +/*! + * Mask interrupt function. Used by I.API + * + */ +static void mask_sdma_interrupt(void) +{ + /* Commented to take of the PREEMPT_RT option + * local_irq_save(flags); + */ +} + +/*! + * Initializes I.API + */ +static void __init init_iapi_struct(void) +{ + channelDescriptor *cd; + + printk(KERN_INFO "Using SDMA I.API\n"); + + iapi_Malloc = &sdma_malloc; + + iapi_Free = &sdma_free; + iapi_Virt2Phys = (void *(*)(void *))&sdma_virt_to_phys; + iapi_Phys2Virt = (void *(*)(void *))&sdma_phys_to_virt; + iapi_memset = &memset; + iapi_memcpy = &memcpy; + + iapi_GotoSleep = &sdma_sleep_channel; + iapi_WakeUp = &sdma_wakeup_channel; + iapi_InitSleep = &sdma_init_sleep; + iapi_ReleaseChannel = &releaseChannel; + iapi_GetChannel = &getChannel; + + iapi_EnableInterrupts = &unmask_sdma_interrupt; + iapi_DisableInterrupts = &mask_sdma_interrupt; + + cd = kmalloc(sizeof(channelDescriptor), GFP_KERNEL); + + memset(cd, 0, sizeof(channelDescriptor)); + + sdma_data[0].cd = cd; +} + +/*! + * Initializes channel synchronization mutexes + */ +static void __init init_mutexes(void) +{ + int i; + + for (i = 0; i < MAX_DMA_CHANNELS; i++) { + init_MUTEX(&sdma_synch_mutex[i]); + } + + init_MUTEX(&sdma_status_mutex); +} + +/*! + * Channels status read proc file system function + * + * @param buf pointer to the buffer the data shuld be written to. + * @param start pointer to the pointer where the new data is + * written to. + * procedure should update the start pointer to point to + * where in the buffer the data was written. + * @param offset offset from start of the file + * @param count number of bytes to read. + * @param eof pointer to eof flag. sould be set to 1 when + * reaching eof. + * @param data driver specific data pointer. + * + * @return number byte read from the log buffer. + */ +static int proc_read_channels(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + char *log; + char *log_ptr; + char tmp[48]; + int i; + + log = kmalloc(4096, GFP_KERNEL); + memset(log, 0, 4096); + log_ptr = log; + + for (i = 0; i < MAX_DMA_CHANNELS; i++) { + if (sdma_data[i].in_use == 0) { + continue; + } + + memset(tmp, 0, 48); + sprintf(tmp, "Channel %d: %s\n", i, sdma_data[i].devicename); + + strcpy(log_ptr, tmp); + log_ptr += strlen(tmp); + } + + if (offset > strlen(log)) { + *eof = 1; + count = 0; + } else { + if (offset + count > strlen(log)) { + count = strlen(log) - offset; + *eof = 1; + } else { + *eof = 0; + } + + memcpy(buf, log, count); + *start = buf; + kfree(log); + } + + return count; +} + +/*! + * SDMA proc file system read function + */ +static int __init init_proc_fs(void) +{ + struct proc_dir_entry *sdma_proc_dir; + int res; + + res = 0; + + sdma_proc_dir = proc_mkdir("sdma", NULL); + create_proc_read_entry("channels", 0, sdma_proc_dir, + proc_read_channels, NULL); + + if (res < 0) { + printk(KERN_WARNING "Failed create SDMA proc entry\n"); + } + + return res; +} + +/*! + * Initializes SDMA private data + */ +static void __init init_sdma_data(void) +{ + int i; + + memset(sdma_data, 0, sizeof(sdma_struct) * MAX_DMA_CHANNELS); + sdma_data[0].in_use = 1; + strcpy(sdma_data[0].devicename, "MCU"); + + for (i = 0; i < MAX_DMA_CHANNELS; i++) { + sdma_data[i].channel = i; + } +} + +#if defined(CONFIG_MXC_SUPER_GEM) +/*! + * Initialize the Super GEM SDMA channel + * + * @return returns -1 on error, 0 on success. + */ +static int __init init_super_gem(void) +{ + channelDescriptor *cd; + script_data context; + int res = 0; + + res = iapi_Open(sdma_data[0].cd, MXC_DMA_CHANNEL_GEM); + if (res < 0) { + return -1; + } + sdma_data[MXC_DMA_CHANNEL_GEM].in_use = 1; + cd = CHAD(MXC_DMA_CHANNEL_GEM); + memset(&context, 0, sizeof(script_data)); + context.load_address = sdma_script_addrs.mxc_sdma_utra_addr; + context.wml = M3_BASE_ADDRESS; + res = iapi_AssignScript(cd, &context); + if (res < 0) { + iapi_Close(cd); + sdma_data[MXC_DMA_CHANNEL_GEM].in_use = 0; + return -1; + } + res = + iapi_IoCtl(cd, IAPI_CHANGE_OWNERSHIP, + (OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT) | + (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU) | + (OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP)); + if (res < 0) { + iapi_Close(cd); + sdma_data[MXC_DMA_CHANNEL_GEM].in_use = 0; + return -1; + } + /* Set EP=1, which is required to start SuperGem script the first time */ + /* This can be done only on the AP side */ + SDMA_H_EVTPEND |= 1 << MXC_DMA_CHANNEL_GEM; + + res = + iapi_SetChannelEventMapping(DMA_REQ_GEM, 1 << MXC_DMA_CHANNEL_GEM); + if (res < 0) { + iapi_Close(cd); + sdma_data[MXC_DMA_CHANNEL_GEM].in_use = 0; + return -1; + } + + return 0; +} +#endif + +/*! + * Initializes dma + */ +int __init sdma_init(void) +{ + int res = 0; + configs_data confreg_data; + + /* Initialize to the default values */ + confreg_data = iapi_ConfigDefaults; + + confreg_data.dspdma = MXC_SDMA_DSPDMA; + /* Set ACR bit */ + mxc_sdma_ahb_clk = clk_get(NULL, "sdma_ahb_clk"); + mxc_sdma_ipg_clk = clk_get(NULL, "sdma_ipg_clk"); + clk_enable(mxc_sdma_ahb_clk); + clk_enable(mxc_sdma_ipg_clk); + if (clk_get_rate(mxc_sdma_ahb_clk) / clk_get_rate(mxc_sdma_ipg_clk) < 2) { + printk(KERN_INFO "Setting SDMA ACR\n"); + confreg_data.acr = 1; + } + + init_sdma_data(); + + init_sdma_pool(); + + res = request_irq(INT_SDMA, sdma_int_handler, 0, "mxcsdma", 0); + + if (res < 0) { + goto sdma_init_fail; + } + + init_mutexes(); + + init_iapi_struct(); + + mxc_sdma_get_script_info(&sdma_script_addrs); + + res = iapi_Init(sdma_data[0].cd, &confreg_data, + sdma_script_addrs.mxc_sdma_start_addr, + sdma_script_addrs.mxc_sdma_ram_code_size * 2, + sdma_script_addrs.mxc_sdma_ram_code_start_addr); + + if (res < 0) { + free_irq(INT_SDMA, 0); + goto sdma_init_fail; + } + + init_priorities(); + + init_event_table(); + +#if defined(CONFIG_MXC_SUPER_GEM) + res = init_super_gem(); + if (res < 0) { + free_irq(INT_SDMA, 0); + goto sdma_init_fail; + } +#endif + + init_proc_fs(); + + printk(KERN_INFO "MXC DMA API initialized\n"); + + clk_disable(mxc_sdma_ahb_clk); + clk_disable(mxc_sdma_ipg_clk); + return res; + + sdma_init_fail: + printk(KERN_ERR "Error 0x%x in sdma_init\n", res); + clk_disable(mxc_sdma_ahb_clk); + clk_disable(mxc_sdma_ipg_clk); + return res; + +} + +arch_initcall(sdma_init); + +EXPORT_SYMBOL(mxc_request_dma); +EXPORT_SYMBOL(mxc_free_dma); +EXPORT_SYMBOL(mxc_dma_setup_channel); +EXPORT_SYMBOL(mxc_dma_set_channel_priority); +EXPORT_SYMBOL(mxc_dma_set_config); +EXPORT_SYMBOL(mxc_dma_get_config); +EXPORT_SYMBOL(mxc_dma_set_bd_intr); +EXPORT_SYMBOL(mxc_dma_get_bd_intr); +EXPORT_SYMBOL(mxc_dma_reset); +EXPORT_SYMBOL(mxc_sdma_write_ipcv2); +EXPORT_SYMBOL(mxc_sdma_read_ipcv2); +EXPORT_SYMBOL(mxc_dma_start); +EXPORT_SYMBOL(mxc_dma_stop); +EXPORT_SYMBOL(sdma_malloc); +EXPORT_SYMBOL(sdma_free); +EXPORT_SYMBOL(mxc_dma_set_callback); +EXPORT_SYMBOL(sdma_virt_to_phys); +EXPORT_SYMBOL(sdma_phys_to_virt); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC Linux SDMA API"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-mxc/sdma/sdma_malloc.c b/arch/arm/plat-mxc/sdma/sdma_malloc.c new file mode 100644 index 000000000000..408b74903868 --- /dev/null +++ b/arch/arm/plat-mxc/sdma/sdma_malloc.c @@ -0,0 +1,240 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file plat-mxc/sdma/sdma_malloc.c + * @brief This file contains functions for SDMA non-cacheable buffers allocation + * + * SDMA (Smart DMA) is used for transferring data between MCU and peripherals + * + * @ingroup SDMA + */ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/mm.h> +#include <asm/dma.h> +#include <asm/mach/dma.h> +#include <asm/arch/hardware.h> + +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> + +#define DEBUG 0 + +#if DEBUG +#define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +/*! + * Defines SDMA non-cacheable buffers pool + */ +static struct dma_pool *pool; + +/*! + * SDMA memory conversion hashing structure + */ +typedef struct { + struct list_head node; + int use_count; + /*! Virtual address */ + void *virt; + /*! Physical address */ + unsigned long phys; +} virt_phys_struct; + +static struct list_head buf_map; + +/*! + * Defines the size of each buffer in SDMA pool. + * The size must be at least 512 bytes, because + * sdma channel control blocks array size is 512 bytes + */ +#define SDMA_POOL_SIZE 1024 + +/*! + * Adds new buffer structure into conversion hash tables + * + * @param vf SDMA memory conversion hashing structure + * + * @return 1 on success, 0 on fail + */ +static int add_entry(virt_phys_struct * vf) +{ + virt_phys_struct *p; + + vf->phys &= PAGE_MASK; + vf->virt = (void *)((u32) vf->virt & PAGE_MASK); + + list_for_each_entry(p, &buf_map, node) { + if (p->virt == vf->virt) { + p->use_count++; + return 0; + } + } + + p = kmalloc(sizeof(virt_phys_struct), GFP_KERNEL); + if (p == 0) { + return -ENOMEM; + } + + *p = *vf; + p->use_count = 1; + list_add_tail(&p->node, &buf_map); + + DPRINTK("added vaddr 0x%p, paddr 0x%08X to list\n", p->virt, p->phys); + + return 0; +} + +/*! + * Deletes buffer stracture from conversion hash tables + * + * @param buf SDMA memory buffer virtual addr + * + * @return 0 on success, -1 on fail + */ +static int delete_entry(void *buf) +{ + virt_phys_struct *p; + + buf = (void *)((u32) buf & PAGE_MASK); + + list_for_each_entry(p, &buf_map, node) { + if (p->virt == buf) { + p->use_count--; + break; + } + } + + if (p->use_count == 0) { + list_del(&p->node); + kfree(p); + } + + return 0; +} + +/*! + * Virtual to physical address conversion functio + * + * @param buf pointer to virtual address + * + * @return physical address + */ +unsigned long sdma_virt_to_phys(void *buf) +{ + u32 offset = (u32) buf & (~PAGE_MASK); + virt_phys_struct *p; + + DPRINTK("searching for vaddr 0x%p\n", buf); + + list_for_each_entry(p, &buf_map, node) { + if ((u32) p->virt == ((u32) buf & PAGE_MASK)) { + return p->phys | offset; + } + } + + if (virt_addr_valid(buf)) { + return virt_to_phys(buf); + } + + printk(KERN_WARNING + "SDMA malloc: could not translate virt address 0x%p\n", buf); + return 0; +} + +/*! + * Physical to virtual address conversion functio + * + * @param buf pointer to physical address + * + * @return virtual address + */ +void *sdma_phys_to_virt(unsigned long buf) +{ + u32 offset = buf & (~PAGE_MASK); + virt_phys_struct *p; + + list_for_each_entry(p, &buf_map, node) { + if (p->phys == (buf & PAGE_MASK)) { + return (void *)((u32) p->virt | offset); + } + } + + printk(KERN_WARNING + "SDMA malloc: could not translate phys address 0x%lx\n", buf); + return 0; +} + +/*! + * Allocates uncacheable buffer + * + * @param size size of allocated buffer + * @return pointer to buffer + */ +void *sdma_malloc(size_t size) +{ + void *buf; + dma_addr_t dma_addr; + virt_phys_struct vf; + + if (size > SDMA_POOL_SIZE) { + printk(KERN_WARNING + "size in sdma_malloc is more than %d bytes\n", + SDMA_POOL_SIZE); + buf = 0; + } else { + buf = dma_pool_alloc(pool, GFP_KERNEL, &dma_addr); + if (buf > 0) { + vf.virt = buf; + vf.phys = dma_addr; + + if (add_entry(&vf) < 0) { + dma_pool_free(pool, buf, dma_addr); + buf = 0; + } + } + } + + DPRINTK("allocated vaddr 0x%p\n", buf); + return buf; +} + +/*! + * Frees uncacheable buffer + * + * @param buf buffer pointer for deletion + */ +void sdma_free(void *buf) +{ + dma_pool_free(pool, buf, sdma_virt_to_phys(buf)); + delete_entry(buf); +} + +/*! + * SDMA buffers pool initialization function + */ +void __init init_sdma_pool(void) +{ + pool = dma_pool_create("SDMA", NULL, SDMA_POOL_SIZE, 0, 0); + + INIT_LIST_HEAD(&buf_map); +} + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC Linux SDMA API"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-mxc/serialxc.c b/arch/arm/plat-mxc/serialxc.c new file mode 100644 index 000000000000..6115df212ec0 --- /dev/null +++ b/arch/arm/plat-mxc/serialxc.c @@ -0,0 +1,64 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/usb/fsl_xcvr.h> + +#include <asm/hardware.h> +#include <asm/arch/arc_otg.h> + +static void usb_serial_init(struct fsl_xcvr_ops *this) +{ +} + +static void usb_serial_uninit(struct fsl_xcvr_ops *this) +{ +} + +static struct fsl_xcvr_ops serial_ops = { + .name = "serial", + .xcvr_type = PORTSC_PTS_SERIAL, + .init = usb_serial_init, + .uninit = usb_serial_uninit, +}; + +extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops); + +static int __init serialxc_init(void) +{ + pr_debug("%s\n", __FUNCTION__); + + fsl_usb_xcvr_register(&serial_ops); + + return 0; +} + +extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops); + +static void __exit serialxc_exit(void) +{ + fsl_usb_xcvr_unregister(&serial_ops); +} + +module_init(serialxc_init); +module_exit(serialxc_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("serial xcvr driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-mxc/snoop.c b/arch/arm/plat-mxc/snoop.c new file mode 100644 index 000000000000..2b724af0a932 --- /dev/null +++ b/arch/arm/plat-mxc/snoop.c @@ -0,0 +1,133 @@ +/* + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <asm/hardware.h> +#include <asm/io.h> + +#ifdef M4IF_BASE_ADDR +#define SNOOP_V2 +#define MAX_SNOOP 2 +#define g_snoop_base (IO_ADDRESS(M4IF_BASE_ADDR) + 0x4C) +#elif defined(M3IF_BASE_ADDR) +#define MAX_SNOOP 1 +#define g_snoop_base (IO_ADDRESS(M3IF_BASE_ADDR) + 0x28) +#else +#define MAX_SNOOP 0 +#define g_snoop_base 0 +#endif + +/* M3IF Snooping Configuration Register 0 (M3IFSCFG0) READ/WRITE*/ +#define SBAR(x) (x * 0x14) +/* M3IF Snooping Configuration Register 1 (M3IFSCFG1) READ/WRITE*/ +#define SERL(x) ((x * 0x14) + 0x4) +/* M3IF Snooping Configuration Register 2 (M3IFSCFG2) READ/WRITE*/ +#define SERH(x) ((x * 0x14) + 0x8) +/* M3IF Snooping Status Register 0 (M3IFSSR0) READ/WRITE */ +#define SSRL(x) ((x * 0x14) + 0xC) +/* M3IF Snooping Status Register 1 (M3IFSSR1) */ +#define SSRH(x) ((x * 0x14) + 0x10) + +#if MAX_SNOOP + +int mxc_snoop_set_config(u32 num, unsigned long base, int size) +{ + u32 reg; + uint32_t msb; + uint32_t seg_size; + uint32_t window_size = 0; + int i; + + if (num >= MAX_SNOOP) { + return -EINVAL; + } + + /* Setup M3IF for snooping */ + if (size) { + + if (base == 0) { + return -EINVAL; + } + + msb = fls(size); + if (!(size & ((1UL << msb) - 1))) + msb--; /* Already aligned to power 2 */ + if (msb < 11) + msb = 11; + + window_size = (1UL << msb); + seg_size = window_size / 64; + + msb -= 11; + + reg = base & ~((1UL << msb) - 1); + reg |= msb << 1; + reg |= 1; /* enable snooping */ + reg |= 0x80; /* Set pulse width to default (M4IF only) */ + __raw_writel(reg, g_snoop_base + SBAR(num)); + + reg = 0; + for (i = 0; i < 32; i++) { + if (i * seg_size >= size) + break; + reg |= 1UL << i; + } + __raw_writel(reg, g_snoop_base + SERL(num)); + + reg = 0; + for (i = 32; i < 64; i++) { + if (i * seg_size >= size) + break; + reg |= 1UL << (i - 32); + } + __raw_writel(reg, g_snoop_base + SERH(num)); + + pr_debug + ("Snooping unit # %d enabled: window size = 0x%X, M3IFSCFG0=0x%08X, M3IFSCFG1=0x%08X, M3IFSCFG2=0x%08X\n", + num, window_size, __raw_readl(g_snoop_base + SBAR(num)), + __raw_readl(g_snoop_base + SERL(num)), + __raw_readl(g_snoop_base + SERH(num))); + } else { + __raw_writel(0, g_snoop_base + SBAR(num)); + } + + return window_size; +} + +EXPORT_SYMBOL(mxc_snoop_set_config); + +int mxc_snoop_get_status(u32 num, u32 * statl, u32 * stath) +{ + if (num >= MAX_SNOOP) { + return -EINVAL; + } + + *statl = __raw_readl(g_snoop_base + SSRL(num)); + *stath = __raw_readl(g_snoop_base + SSRH(num)); + /* DPRINTK("status = 0x%08X%08X\n", stat[1], stat[0]); */ + +#ifdef SNOOP_V2 + __raw_writel(*statl, g_snoop_base + SSRL(num)); + __raw_writel(*stath, g_snoop_base + SSRH(num)); +#else + __raw_writel(0x0, g_snoop_base + SSRL(num)); + __raw_writel(0x0, g_snoop_base + SSRH(num)); +#endif + return 0; +} + +EXPORT_SYMBOL(mxc_snoop_get_status); + +#endif /* MAX_SNOOP */ diff --git a/arch/arm/plat-mxc/spba.c b/arch/arm/plat-mxc/spba.c new file mode 100644 index 000000000000..82be83fcfa1b --- /dev/null +++ b/arch/arm/plat-mxc/spba.c @@ -0,0 +1,112 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <asm/io.h> +#include <asm/arch/hardware.h> +#include <asm/arch/spba.h> + +/*! + * @file plat-mxc/spba.c + * + * @brief This file contains the SPBA API implementation details. + * + * @ingroup SPBA + */ + +static DEFINE_SPINLOCK(spba_lock); + +#define SPBA_MASTER_MIN 1 +#define SPBA_MASTER_MAX 7 + +/*! + * the base addresses for the SPBA modules + */ +static unsigned long spba_base = (unsigned long)IO_ADDRESS(SPBA_CTRL_BASE_ADDR); + +/*! + * This function allows the three masters (A, B, C) to take ownership of a + * shared peripheral. + * + * @param mod specified module as defined in \b enum \b #spba_module + * @param master one of more (or-ed together) masters as defined in \b enum \b #spba_masters + * + * @return 0 if successful; -1 otherwise. + */ +int spba_take_ownership(int mod, int master) +{ + unsigned long spba_flags; + __u32 rtn_val = -1; + + if (master < SPBA_MASTER_MIN || master > SPBA_MASTER_MAX) { + printk("spba_take_ownership() invalide master= %d\n", master); + BUG(); /* oops */ + } + + spin_lock_irqsave(&spba_lock, spba_flags); + __raw_writel(master, spba_base + mod); + + if ((__raw_readl(spba_base + mod) & MXC_SPBA_RAR_MASK) == master) { + rtn_val = 0; + } + + spin_unlock_irqrestore(&spba_lock, spba_flags); + + return rtn_val; +} + +/*! + * This function releases the ownership for a shared peripheral. + * + * @param mod specified module as defined in \b enum \b #spba_module + * @param master one of more (or-ed together) masters as defined in \b enum \b #spba_masters + * + * @return 0 if successful; -1 otherwise. + */ +int spba_rel_ownership(int mod, int master) +{ + unsigned long spba_flags; + volatile unsigned long rar; + + if (master < SPBA_MASTER_MIN || master > SPBA_MASTER_MAX) { + printk("spba_take_ownership() invalide master= %d\n", master); + BUG(); /* oops */ + } + + if ((__raw_readl(spba_base + mod) & master) == 0) { + return 0; /* does not own it */ + } + + spin_lock_irqsave(&spba_lock, spba_flags); + + /* Since only the last 3 bits are writeable, doesn't need to mask off + bits 31-3 */ + rar = __raw_readl(spba_base + mod) & (~master); + __raw_writel(rar, spba_base + mod); + + if ((__raw_readl(spba_base + mod) & master) != 0) { + spin_unlock_irqrestore(&spba_lock, spba_flags); + return -1; + } + + spin_unlock_irqrestore(&spba_lock, spba_flags); + return 0; +} + +EXPORT_SYMBOL(spba_take_ownership); +EXPORT_SYMBOL(spba_rel_ownership); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("SPBA"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-mxc/time.c b/arch/arm/plat-mxc/time.c new file mode 100644 index 000000000000..5f83111c08db --- /dev/null +++ b/arch/arm/plat-mxc/time.c @@ -0,0 +1,265 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* System Timer Interrupt reconfigured to run in free-run mode. + * Author: Vitaly Wool + * Copyright 2004 MontaVista Software Inc. + */ + +/*! + * @defgroup Timers OS Tick Timer + */ +/*! + * @file plat-mxc/time.c + * @brief This file contains OS tick timer implementation. + * + * This file contains OS tick timer implementation. + * + * @ingroup Timers + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> +#include <linux/mtd/xip.h> +#include <asm/hardware.h> +#include <asm/mach/time.h> + +/*! + * GPT register address definitions + */ +#define GPT_BASE_ADDR (IO_ADDRESS(GPT1_BASE_ADDR)) +#define MXC_GPT_GPTCR (GPT_BASE_ADDR + 0x00) +#define MXC_GPT_GPTPR (GPT_BASE_ADDR + 0x04) +#define MXC_GPT_GPTSR (GPT_BASE_ADDR + 0x08) +#define MXC_GPT_GPTIR (GPT_BASE_ADDR + 0x0C) +#define MXC_GPT_GPTOCR1 (GPT_BASE_ADDR + 0x10) +#define MXC_GPT_GPTOCR2 (GPT_BASE_ADDR + 0x14) +#define MXC_GPT_GPTOCR3 (GPT_BASE_ADDR + 0x18) +#define MXC_GPT_GPTICR1 (GPT_BASE_ADDR + 0x1C) +#define MXC_GPT_GPTICR2 (GPT_BASE_ADDR + 0x20) +#define MXC_GPT_GPTCNT (GPT_BASE_ADDR + 0x24) + +/*! + * GPT Control register bit definitions + */ +#define GPTCR_FO3 (1 << 31) +#define GPTCR_FO2 (1 << 30) +#define GPTCR_FO1 (1 << 29) + +#define GPTCR_SWR (1 << 15) +#define GPTCR_FRR (1 << 9) + +#define GPTCR_CLKSRC_SHIFT 6 +#define GPTCR_CLKSRC_MASK (7 << GPTCR_CLKSRC_SHIFT) +#define GPTCR_CLKSRC_NOCLOCK (0 << GPTCR_CLKSRC_SHIFT) +#define GPTCR_CLKSRC_HIGHFREQ (2 << GPTCR_CLKSRC_SHIFT) +#define GPTCR_CLKSRC_CLKIN (3 << GPTCR_CLKSRC_SHIFT) +#define GPTCR_CLKSRC_CLK32K (4 << GPTCR_CLKSRC_SHIFT) + +#define GPTCR_STOPEN (1 << 5) +#define GPTCR_DOZEN (1 << 4) +#define GPTCR_WAITEN (1 << 3) +#define GPTCR_DBGEN (1 << 2) +#define GPTCR_ENMOD (1 << 1) +#define GPTCR_ENABLE (1 << 0) + +#define GPTSR_OF1 (1 << 0) +#define GPTSR_OF2 (1 << 1) +#define GPTSR_OF3 (1 << 2) +#define GPTSR_IF1 (1 << 3) +#define GPTSR_IF2 (1 << 4) +#define GPTSR_ROV (1 << 5) + +#define GPTIR_OF1IE GPTSR_OF1 +#define GPTIR_OF2IE GPTSR_OF2 +#define GPTIR_OF3IE GPTSR_OF3 +#define GPTIR_IF1IE GPTSR_IF1 +#define GPTIR_IF2IE GPTSR_IF2 +#define GPTIR_ROVIE GPTSR_ROV + +extern unsigned long clk_early_get_timer_rate(void); + +static int mxc_gpt_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + unsigned long now, expires; + u32 reg; + + now = __raw_readl(MXC_GPT_GPTCNT); + expires = now + cycles; + __raw_writel(expires, MXC_GPT_GPTOCR1); + __raw_writel(GPTSR_OF1, MXC_GPT_GPTSR); + + /* enable interrupt */ + reg = __raw_readl(MXC_GPT_GPTIR); + reg |= GPTIR_OF1IE; + __raw_writel(reg, MXC_GPT_GPTIR); + + return 0; +} + +static void mxc_gpt_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + u32 reg; + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + panic("MXC GPT: CLOCK_EVT_MODE_PERIODIC not supported\n"); + break; + case CLOCK_EVT_MODE_ONESHOT: + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + /* Disable interrupts */ + reg = __raw_readl(MXC_GPT_GPTIR); + reg &= ~GPTIR_OF1IE; + __raw_writel(reg, MXC_GPT_GPTIR); + break; + } +} + +static struct clock_event_device gpt_clockevent = { + .name = "mxc_gpt", + .features = CLOCK_EVT_FEAT_ONESHOT, + .rating = 300, + .shift = 32, + .set_next_event = mxc_gpt_set_next_event, + .set_mode = mxc_gpt_set_mode, +}; + +/*! + * This is the timer interrupt service routine to do required tasks. + * It also services the WDOG timer at the frequency of twice per WDOG + * timeout value. For example, if the WDOG's timeout value is 4 (2 + * seconds since the WDOG runs at 0.5Hz), it will be serviced once + * every 2/2=1 second. + * + * @param irq GPT interrupt source number (not used) + * @param dev_id this parameter is not used + * @return always returns \b IRQ_HANDLED as defined in + * include/linux/interrupt.h. + */ +static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id) +{ + unsigned int gptsr; + u32 reg; + + gptsr = __raw_readl(MXC_GPT_GPTSR); + if (gptsr & GPTSR_OF1) { + /* Disable interrupt */ + reg = __raw_readl(MXC_GPT_GPTIR); + reg &= ~GPTIR_OF1IE; + __raw_writel(reg, MXC_GPT_GPTIR); + /* Clear interrupt */ + __raw_writel(GPTSR_OF1, MXC_GPT_GPTSR); + + gpt_clockevent.event_handler(&gpt_clockevent); + } + + mxc_kick_wd(); + + return IRQ_HANDLED; +} + +/*! + * The clockevents timer interrupt structure. + */ +static struct irqaction timer_irq = { + .name = "gpt-irq", + .flags = IRQF_DISABLED, + .handler = mxc_timer_interrupt, +}; + +static cycle_t __xipram mxc_gpt_read(void) +{ + return __raw_readl(MXC_GPT_GPTCNT); +} + +static struct clocksource gpt_clocksrc = { + .name = "mxc_gpt", + .rating = 300, + .read = mxc_gpt_read, + .mask = CLOCKSOURCE_MASK(32), + .shift = 24, + .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_VALID_FOR_HRES, +}; + +/*! + * This function is used to initialize the GPT as a clocksource and clockevent. + * It is called by the start_kernel() during system startup. + */ +void __init mxc_init_time(void) +{ + int ret; + unsigned long rate; + u32 reg, div; + + /* Reset GPT */ + __raw_writel(GPTCR_SWR, MXC_GPT_GPTCR); + while ((__raw_readl(MXC_GPT_GPTCR) & GPTCR_SWR) != 0) + mb(); + + /* Normal clk api are not yet initialized, so use early verion */ + rate = clk_early_get_timer_rate(); + if (rate == 0) + panic("MXC GPT: Can't get timer clock rate\n"); + +#ifdef CLOCK_TICK_RATE + div = rate / CLOCK_TICK_RATE; + WARN_ON((div * CLOCK_TICK_RATE) != rate); +#else /* Hopefully CLOCK_TICK_RATE will go away soon */ + div = 1; + while ((rate / div) > 20000000) { + div++; + } +#endif + rate /= div; + __raw_writel(div - 1, MXC_GPT_GPTPR); + + reg = GPTCR_FRR | GPTCR_CLKSRC_HIGHFREQ | GPTCR_STOPEN | + GPTCR_DOZEN | GPTCR_WAITEN | GPTCR_ENMOD | GPTCR_ENABLE; + __raw_writel(reg, MXC_GPT_GPTCR); + + gpt_clocksrc.mult = clocksource_hz2mult(rate, gpt_clocksrc.shift); + ret = clocksource_register(&gpt_clocksrc); + if (ret < 0) { + goto err; + } + + gpt_clockevent.mult = div_sc(rate, NSEC_PER_SEC, gpt_clockevent.shift); + gpt_clockevent.max_delta_ns = clockevent_delta2ns(-1, &gpt_clockevent); + gpt_clockevent.min_delta_ns = clockevent_delta2ns(1, &gpt_clockevent); + + gpt_clockevent.cpumask = cpumask_of_cpu(0); + clockevents_register_device(&gpt_clockevent); + + ret = setup_irq(INT_GPT, &timer_irq); + if (ret < 0) { + goto err; + } + + pr_info("MXC GPT timer initialized, rate = %lu\n", rate); + return; + err: + panic("Unable to initialize timer\n"); +} + +struct sys_timer mxc_timer = { + .init = mxc_init_time, +}; diff --git a/arch/arm/plat-mxc/usb_common.c b/arch/arm/plat-mxc/usb_common.c new file mode 100644 index 000000000000..578a3dcee394 --- /dev/null +++ b/arch/arm/plat-mxc/usb_common.c @@ -0,0 +1,575 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * otg_{get,set}_transceiver() are from arm/plat-omap/usb.c. + * which is Copyright (C) 2004 Texas Instruments, Inc. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + *@defgroup USB ARC OTG USB Driver + */ + +/*! + * @file usb_common.c + * + * @brief platform related part of usb driver. + * @ingroup USB + */ + +/*! + *Include files + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/fsl_devices.h> +#include <linux/usb/otg.h> +#include <linux/usb/fsl_xcvr.h> +#include <asm/arch/arc_otg.h> + +#define MXC_NUMBER_USB_TRANSCEIVER 6 +struct fsl_xcvr_ops *g_xc_ops[MXC_NUMBER_USB_TRANSCEIVER] = { NULL }; + +static struct clk *usb_clk; +static struct clk *usb_ahb_clk; + +/* + * make sure USB_CLK is running at 60 MHz +/- 1000 Hz + */ +static int fsl_check_usbclk(void) +{ + unsigned long freq; + + usb_ahb_clk = clk_get(NULL, "usb_ahb_clk"); + clk_enable(usb_ahb_clk); + clk_put(usb_ahb_clk); + + usb_clk = clk_get(NULL, "usb_clk"); + freq = clk_get_rate(usb_clk); + clk_put(usb_clk); + if ((freq < 59999000) || (freq > 60001000)) { + printk(KERN_ERR "USB_CLK=%lu, should be 60MHz\n", freq); + return -1; + } + + return 0; +} + +void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops) +{ + int i; + + pr_debug("%s\n", __FUNCTION__); + for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) { + if (g_xc_ops[i] == NULL) { + g_xc_ops[i] = xcvr_ops; + return; + } + } + + pr_debug("Failed %s\n", __FUNCTION__); +} + +EXPORT_SYMBOL(fsl_usb_xcvr_register); + +void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops) +{ + int i; + + pr_debug("%s\n", __FUNCTION__); + for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) { + if (g_xc_ops[i] == xcvr_ops) { + g_xc_ops[i] = NULL; + return; + } + } + + pr_debug("Failed %s\n", __FUNCTION__); +} + +EXPORT_SYMBOL(fsl_usb_xcvr_unregister); + +static struct fsl_xcvr_ops *fsl_usb_get_xcvr(char *name) +{ + int i; + + pr_debug("%s\n", __FUNCTION__); + if (name == NULL) { + printk(KERN_ERR "None tranceiver name be passed\n"); + return NULL; + } + + for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) { + if (strcmp(g_xc_ops[i]->name, name) == 0) { + return g_xc_ops[i]; + } + } + pr_debug("Failed %s\n", __FUNCTION__); + return NULL; +} + +/* The dmamask must be set for EHCI to work */ +static u64 ehci_dmamask = ~(u32) 0; + +/*! + * Register an instance of a USB host platform device. + * + * @param res: resource pointer + * @param n_res: number of resources + * @param config: config pointer + * + * @return newly-registered platform_device + * + * The USB controller supports 3 host interfaces, and the + * kernel can be configured to support some number of them. + * Each supported host interface is registered as an instance + * of the "fsl-ehci" device. Call this function multiple times + * to register each host interface. + */ +static int instance_id = 0; +struct platform_device *host_pdev_register(struct resource *res, int n_res, struct fsl_usb2_platform_data + *config) +{ + struct platform_device *pdev; + int rc; + + pr_debug("register host res=0x%p, size=%d\n", res, n_res); + + pdev = platform_device_register_simple("fsl-ehci", + instance_id, res, n_res); + if (IS_ERR(pdev)) { + pr_debug("can't register %s Host, %ld\n", + config->name, PTR_ERR(pdev)); + return NULL; + } + + pdev->dev.coherent_dma_mask = 0xffffffff; + pdev->dev.dma_mask = &ehci_dmamask; + + /* + * platform_device_add_data() makes a copy of + * the platform_data passed in. That makes it + * impossible to share the same config struct for + * all OTG devices (host,gadget,otg). So, just + * set the platorm_data pointer ourselves. + */ + rc = platform_device_add_data(pdev, config, + sizeof(struct fsl_usb2_platform_data)); + if (rc) { + platform_device_unregister(pdev); + return NULL; + } + + printk(KERN_INFO "usb: %s registered\n", config->name); + pr_debug("pdev=0x%p dev=0x%p resources=0x%p pdata=0x%p\n", + pdev, &pdev->dev, pdev->resource, pdev->dev.platform_data); + + instance_id++; + + return pdev; +} + +static int fsl_usb_mem_init(struct platform_device *pdev) +{ + struct resource *res; + struct fsl_usb2_platform_data *pdata; + + pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data; + + pr_debug("%s: pdev=0x%p pdata=0x%p\n", __FUNCTION__, pdev, pdata); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no MEM resource.\n"); + return -ENODEV; + } + + pdata->r_start = res->start; + pdata->r_len = res->end - res->start + 1; + pr_debug("%s: MEM resource start=0x%x len=0x%x\n", pdata->name, + res->start, pdata->r_len); + + if (!request_mem_region(pdata->r_start, pdata->r_len, "OTG")) { + dev_err(&pdev->dev, "request_mem_region failed\n"); + return -EBUSY; + } + pdata->regs = ioremap(pdata->r_start, pdata->r_len); + pr_debug("ioremapped to 0x%p\n", pdata->regs); + + if (pdata->regs == NULL) { + dev_err(&pdev->dev, "ioremap failed\n"); + release_mem_region(pdata->r_start, pdata->r_len); + return -EFAULT; + } + + pr_debug("%s: success\n", __FUNCTION__); + return 0; +} + +static int fsl_usb_mem_map(struct platform_device *pdev) +{ + struct resource *res; + struct fsl_usb2_platform_data *pdata; + + pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data; + + pr_debug("%s: pdev=0x%p pdata=0x%p\n", __FUNCTION__, pdev, pdata); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no MEM resource.\n"); + return -ENODEV; + } + + pdata->r_start = res->start; + pdata->r_len = res->end - res->start + 1; + pr_debug("%s: MEM resource start=0x%x len=0x%x\n", pdata->name, + res->start, pdata->r_len); + + pdata->regs = ioremap(pdata->r_start, pdata->r_len); + pr_debug("ioremapped to 0x%p\n", pdata->regs); + + if (pdata->regs == NULL) { + dev_err(&pdev->dev, "ioremap failed\n"); + return -EFAULT; + } + + pr_debug("%s: success\n", __FUNCTION__); + return 0; +} + +void fsl_platform_set_vbus_power(struct fsl_usb2_platform_data *pdata, int on) +{ + if (pdata->xcvr_ops && pdata->xcvr_ops->set_vbus_power) + pdata->xcvr_ops->set_vbus_power((u32 *) pdata->viewport, on); +} + +EXPORT_SYMBOL(fsl_platform_set_vbus_power); + +void fsl_platform_perform_remote_wakeup(struct fsl_usb2_platform_data *pdata) +{ + if (pdata->xcvr_ops && pdata->xcvr_ops->set_remote_wakeup) + pdata->xcvr_ops->set_remote_wakeup((u32 *) pdata->viewport); +} + +EXPORT_SYMBOL(fsl_platform_perform_remote_wakeup); + +#if defined(CONFIG_USB_OTG) +static struct otg_transceiver *xceiv; + +/** + * otg_get_transceiver - find the (single) OTG transceiver driver + * + * Returns the transceiver driver, after getting a refcount to it; or + * null if there is no such transceiver. The caller is responsible for + * releasing that count. + */ +struct otg_transceiver *otg_get_transceiver(void) +{ + pr_debug("%s xceiv=0x%p\n", __FUNCTION__, xceiv); + if (xceiv) + get_device(xceiv->dev); + return xceiv; +} + +EXPORT_SYMBOL(otg_get_transceiver); + +int otg_set_transceiver(struct otg_transceiver *x) +{ + pr_debug("%s xceiv=0x%p x=0x%p\n", __FUNCTION__, xceiv, x); + if (xceiv && x) + return -EBUSY; + xceiv = x; + return 0; +} + +EXPORT_SYMBOL(otg_set_transceiver); +#endif + +static void usbh1_set_serial_xcvr(void) +{ + pr_debug("%s: \n", __FUNCTION__); + USBCTRL &= ~(UCTRL_H1SIC_MASK | UCTRL_BPE); /* disable bypass mode */ + USBCTRL |= UCTRL_H1SIC_SU6 | /* single-ended / unidir. */ + UCTRL_H1WIE | UCTRL_H1DT | /* disable H1 TLL */ + UCTRL_H1PM; /* power mask */ + UH1_PORTSC1 &= ~PORTSC_PTS_MASK; + UH1_PORTSC1 |= PORTSC_PTS_SERIAL; +} + +static void usbh2_set_ulpi_xcvr(void) +{ + USBCTRL &= ~(UCTRL_H2SIC_MASK | UCTRL_BPE); /* disable bypass mode */ + USBCTRL |= UCTRL_H2WIE | /* wakeup intr enable */ + UCTRL_H2UIE | /* ULPI intr enable */ + UCTRL_H2DT | /* disable H2 TLL */ + UCTRL_H2PM; /* power mask */ + + UH2_PORTSC1 &= ~PORTSC_PTS_MASK; /* set ULPI xcvr */ + UH2_PORTSC1 |= PORTSC_PTS_ULPI; + + /* Turn off the usbpll for ulpi tranceivers */ + clk_disable(usb_clk); +} + +int fsl_usb_host_init(struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata; + struct fsl_xcvr_ops *xops; + int rc; + + pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data; + + pr_debug("%s: pdev=0x%p pdata=0x%p\n", __FUNCTION__, pdev, + pdata->name); + + xops = fsl_usb_get_xcvr(pdata->transceiver); + if (!xops) { + printk(KERN_ERR "%s transceiver ops missing\n", pdata->name); + return -EINVAL; + } + pdata->xcvr_ops = xops; + pdata->xcvr_type = xops->xcvr_type; + + if (fsl_check_usbclk() != 0) + return -EINVAL; + + pr_debug("%s: grab pins\n", __FUNCTION__); + if (pdata->gpio_usb_active()) + return -EINVAL; + + /* request_mem_region and ioremap registers */ + if ((rc = fsl_usb_mem_init(pdev))) { + pdata->gpio_usb_inactive(); /* release our pins */ + return rc; + } + + clk_enable(usb_clk); + + if (xops->init) + xops->init(xops); + + if (xops->xcvr_type == PORTSC_PTS_SERIAL) { + usbh1_set_serial_xcvr(); + } else if (xops->xcvr_type == PORTSC_PTS_ULPI) { + usbh2_set_ulpi_xcvr(); + } + + pr_debug("%s: %s success\n", __FUNCTION__, pdata->name); + return 0; +} + +EXPORT_SYMBOL(fsl_usb_host_init); + +void fsl_usb_host_uninit(struct fsl_usb2_platform_data *pdata) +{ + pr_debug("%s\n", __FUNCTION__); + + if (pdata->xcvr_ops && pdata->xcvr_ops->uninit) + pdata->xcvr_ops->uninit(pdata->xcvr_ops); + + iounmap(pdata->regs); + release_mem_region(pdata->r_start, pdata->r_len); + + pdata->regs = NULL; + pdata->r_start = pdata->r_len = 0; + + pdata->gpio_usb_inactive(); + if (pdata->xcvr_type == PORTSC_PTS_SERIAL) + clk_disable(usb_clk); +} + +EXPORT_SYMBOL(fsl_usb_host_uninit); + +static void otg_set_serial_xcvr(void) +{ + u32 tmp; + + tmp = UOG_PORTSC1 & ~PORTSC_PTS_MASK; + tmp |= PORTSC_PTS_SERIAL; + UOG_PORTSC1 = tmp; +} + +void otg_set_serial_host(void) +{ + /* set USBCTRL for host operation + * disable: bypass mode, + * set: single-ended/unidir/6 wire, OTG wakeup intr enable, + * power mask + */ + USBCTRL &= ~UCTRL_OSIC_MASK; +#if defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MX3) + USBCTRL &= ~UCTRL_BPE; +#endif + +#if defined(CONFIG_MXC_USB_SB3) + USBCTRL |= UCTRL_OSIC_SB3 | UCTRL_OWIE | UCTRL_OPM; +#elif defined(CONFIG_MXC_USB_SU6) + USBCTRL |= UCTRL_OSIC_SU6 | UCTRL_OWIE | UCTRL_OPM; +#elif defined(CONFIG_MXC_USB_DB4) + USBCTRL |= UCTRL_OSIC_DB4 | UCTRL_OWIE | UCTRL_OPM; +#else + USBCTRL |= UCTRL_OSIC_DU6 | UCTRL_OWIE | UCTRL_OPM; +#endif + + USB_OTG_MIRROR = OTGM_VBUSVAL | OTGM_ASESVLD; /* 0xa */ +} + +EXPORT_SYMBOL(otg_set_serial_host); + +void otg_set_serial_peripheral(void) +{ + /* set USBCTRL for device operation + * disable: bypass mode + * set: differential/unidir/6 wire, OTG wakeup intr enable, + * power mask + */ + USBCTRL &= ~UCTRL_OSIC_MASK; +#if defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MX3) + USBCTRL &= ~UCTRL_BPE; +#endif + +#if defined(CONFIG_MXC_USB_SB3) + USBCTRL |= UCTRL_OSIC_SB3 | UCTRL_OWIE | UCTRL_OPM; +#elif defined(CONFIG_MXC_USB_SU6) + USBCTRL |= UCTRL_OSIC_SU6 | UCTRL_OWIE | UCTRL_OPM; +#elif defined(CONFIG_MXC_USB_DB4) + USBCTRL |= UCTRL_OSIC_DB4 | UCTRL_OWIE | UCTRL_OPM; +#else + USBCTRL |= UCTRL_OSIC_DU6 | UCTRL_OWIE | UCTRL_OPM; +#endif + + USB_OTG_MIRROR = OTGM_VBUSVAL | OTGM_BSESVLD | OTGM_IDIDG; /* oxd */ +} + +EXPORT_SYMBOL(otg_set_serial_peripheral); + +static void otg_set_ulpi_xcvr(void) +{ + u32 tmp; + + USBCTRL &= ~UCTRL_OSIC_MASK; +#if defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MX3) + USBCTRL &= ~UCTRL_BPE; +#endif + USBCTRL |= UCTRL_OUIE | /* ULPI intr enable */ + UCTRL_OWIE | /* OTG wakeup intr enable */ + UCTRL_OPM; /* power mask */ + + /* set ULPI xcvr */ + tmp = UOG_PORTSC1 & ~PORTSC_PTS_MASK; + tmp |= PORTSC_PTS_ULPI; + UOG_PORTSC1 = tmp; + + /* need to reset the controller here so that the ID pin + * is correctly detected. + */ + UOG_USBCMD |= UCMD_RESET; + + /* allow controller to reset, and leave time for + * the ULPI transceiver to reset too. + */ + msleep(100); + + /* Turn off the usbpll for ulpi tranceivers */ + clk_disable(usb_clk); +} + +static int otg_used = 0; + +int usbotg_init(struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata; + struct fsl_xcvr_ops *xops; + int rc; + + pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data; + + pr_debug("%s: pdev=0x%p pdata=0x%p\n", __FUNCTION__, pdev, pdata); + + xops = fsl_usb_get_xcvr(pdata->transceiver); + if (!xops) { + printk(KERN_ERR "OTG transceiver ops missing\n"); + return -EINVAL; + } + pdata->xcvr_ops = xops; + pdata->xcvr_type = xops->xcvr_type; + + if (!otg_used) { + if (fsl_check_usbclk() != 0) + return -EINVAL; + + pr_debug("%s: grab pins\n", __FUNCTION__); + if (pdata->gpio_usb_active()) + return -EINVAL; + + clk_enable(usb_clk); + + /* request_mem_region and ioremap registers */ + if ((rc = fsl_usb_mem_init(pdev))) { + return rc; + } + + if (xops->init) + xops->init(xops); + + if (xops->xcvr_type == PORTSC_PTS_SERIAL) { + if (pdata->operating_mode == FSL_USB2_DR_HOST) { + otg_set_serial_host(); + /* need reset */ + UOG_USBCMD |= UCMD_RESET; + msleep(100); + } else if (pdata->operating_mode == FSL_USB2_DR_DEVICE) + otg_set_serial_peripheral(); + otg_set_serial_xcvr(); + } else if (xops->xcvr_type == PORTSC_PTS_ULPI) { + otg_set_ulpi_xcvr(); + } + } else { + fsl_usb_mem_map(pdev); + } + + otg_used++; + pr_debug("%s: success\n", __FUNCTION__); + return 0; +} + +EXPORT_SYMBOL(usbotg_init); + +void usbotg_uninit(struct fsl_usb2_platform_data *pdata) +{ + pr_debug("%s\n", __FUNCTION__); + + otg_used--; + if (!otg_used) { + if (pdata->xcvr_ops && pdata->xcvr_ops->uninit) + pdata->xcvr_ops->uninit(pdata->xcvr_ops); + + iounmap(pdata->regs); + release_mem_region(pdata->r_start, pdata->r_len); + + pdata->regs = NULL; + pdata->r_start = pdata->r_len = 0; + + pdata->gpio_usb_inactive(); + if (pdata->xcvr_type == PORTSC_PTS_SERIAL) + clk_disable(usb_clk); + } +} + +EXPORT_SYMBOL(usbotg_uninit); diff --git a/arch/arm/plat-mxc/wdog.c b/arch/arm/plat-mxc/wdog.c new file mode 100644 index 000000000000..35821e8168c5 --- /dev/null +++ b/arch/arm/plat-mxc/wdog.c @@ -0,0 +1,181 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file plat-mxc/wdog.c + * @brief This file contains watchdog timer implementations. + * + * This file contains watchdog timer implementations for timer tick. + * + * @ingroup WDOG + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/clk.h> +#include <asm/io.h> + +/* Watchdog timers are not enabled by default */ +#undef WDOG1_ENABLE /* not defined by default */ +#undef WDOG2_ENABLE /* not defined by default */ + +#define WDOG1_TIMEOUT 4000 /* WDOG1 timeout in ms */ +#define WDOG2_TIMEOUT (WDOG1_TIMEOUT / 2) /* WDOG2 timeout in ms */ +#define WDOG_SERVICE_PERIOD (WDOG2_TIMEOUT / 2) /* time interval in ms to service WDOGs */ + +#if (WDOG1_TIMEOUT < WDOG2_TIMEOUT) +#error WDOG1_TIMEOUT must be greater than WDOG2_TIMEOUT! +#endif + +#if (WDOG_SERVICE_PERIOD >= (WDOG2_TIMEOUT - 1000 / HZ)) +#error WDOG_SERVICE_PERIOD is too large! +#endif + +/* maximum timeout is 128s based on 2Hz clock */ +#if ((WDOG1_TIMEOUT/1000) > 128) +#error WDOG time out has to be less than 128 seconds! +#endif + +#define WDOG_WT 0x8 /* WDOG WT starting bit inside WCR */ +#define WCR_WOE_BIT (1 << 6) +#define WCR_WDA_BIT (1 << 5) +#define WCR_SRS_BIT (1 << 4) +#define WCR_WRE_BIT (1 << 3) +#define WCR_WDE_BIT (1 << 2) +#define WCR_WDBG_BIT (1 << 1) +#define WCR_WDZST_BIT (1 << 0) + +/* + * WatchDog + */ +#define WDOG_WCR 0 /* 16bit watchdog control reg */ +#define WDOG_WSR 2 /* 16bit watchdog service reg */ +#define WDOG_WRSR 4 /* 16bit watchdog reset status reg */ + +/*! + * The base addresses for the WDOG modules + */ +static unsigned long wdog_base[2] = { + IO_ADDRESS(WDOG1_BASE_ADDR), +#ifdef WDOG2_BASE_ADDR + IO_ADDRESS(WDOG2_BASE_ADDR), +#endif +}; + +/*! + * The corresponding WDOG won't be serviced unless the corresponding global + * variable is set to a non-zero value. + */ +volatile unsigned short g_wdog1_enabled, g_wdog2_enabled; + +/* WDOG WCR register's WT value */ +static int wdog_tmout[2] = { WDOG1_TIMEOUT, WDOG2_TIMEOUT }; + +static int g_wdog_count = 0; + +/*! + * This function provides the required service for the watchdog to avoid + * the timeout. + */ +void mxc_kick_wd(void) +{ + if (++g_wdog_count >= ((WDOG_SERVICE_PERIOD / 1000) * HZ)) { + g_wdog_count = 0; /* reset */ + if (g_wdog1_enabled) { + /* issue the service sequence instructions */ + __raw_writew(0x5555, wdog_base[0] + WDOG_WSR); + __raw_writew(0xAAAA, wdog_base[0] + WDOG_WSR); + } + if (g_wdog2_enabled) { + /* issue the service sequence instructions */ + __raw_writew(0x5555, wdog_base[1] + WDOG_WSR); + __raw_writew(0xAAAA, wdog_base[1] + WDOG_WSR); + } + } +} + +/*! + * This is the watchdog initialization routine to setup the timeout + * value and enable it. + */ +void mxc_wd_init(int port) +{ + u32 reg; + unsigned short timeout = ((wdog_tmout[port] / 1000) * 2) << WDOG_WT; + + if (port == 0) { + /* enable WD, suspend WD in DEBUG mode */ + reg = timeout | WCR_WOE_BIT | + WCR_SRS_BIT | WCR_WDA_BIT | WCR_WDE_BIT | WCR_WDBG_BIT; + } else { + /* enable WD, suspend WD in DEBUG, low power modes and WRE=1 */ + reg = timeout | WCR_WOE_BIT | + WCR_SRS_BIT | WCR_WRE_BIT | WCR_WDE_BIT | WCR_WDBG_BIT; + } + __raw_writew(reg, wdog_base[port] + WDOG_WCR); +} + +void mxc_wd_reset(void) +{ + u16 reg; + struct clk *clk; + + clk = clk_get(NULL, "wdog_clk"); + clk_enable(clk); + + reg = __raw_readw(wdog_base[0] + WDOG_WCR) & ~WCR_SRS_BIT; + reg |= WCR_WDE_BIT; + __raw_writew(reg, wdog_base[0] + WDOG_WCR); +} + +/*! + * This function is used to initialize the GPT to produce an interrupt + * every 10 msec. It is called by the start_kernel() during system startup. + */ +static int __init wdog_init(void) +{ + +#ifdef WDOG1_BASE_ADDR +#ifdef WDOG1_ENABLE + mxc_wd_init(0); + g_wdog1_enabled = 1; +#else + g_wdog1_enabled = __raw_readw(wdog_base[0] + WDOG_WCR) & WCR_WDE_BIT; +#endif +#endif + +#ifdef WDOG2_BASE_ADDR +#ifdef WDOG2_ENABLE + mxc_wd_init(1); + g_wdog2_enabled = 1; +#else + g_wdog2_enabled = __raw_readw(wdog_base[1] + WDOG_WCR) & WCR_WDE_BIT; +#endif +#endif + + mxc_kick_wd(); + + if (g_wdog1_enabled) + pr_info("MXC WDOG1 Enabled\n"); + if (g_wdog2_enabled) + pr_info("MXC WDOG2 Enabled\n"); + return 0; +} + +postcore_initcall(wdog_init); + +EXPORT_SYMBOL(g_wdog1_enabled); +EXPORT_SYMBOL(g_wdog2_enabled); +EXPORT_SYMBOL(mxc_wd_init); |