summaryrefslogtreecommitdiff
path: root/arch/arm/plat-nomadik
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-nomadik')
-rw-r--r--arch/arm/plat-nomadik/Kconfig5
-rw-r--r--arch/arm/plat-nomadik/Makefile1
-rw-r--r--arch/arm/plat-nomadik/gpio.c435
-rw-r--r--arch/arm/plat-nomadik/include/plat/gpio.h70
-rw-r--r--arch/arm/plat-nomadik/include/plat/ste_dma40.h239
-rw-r--r--arch/arm/plat-nomadik/timer.c145
6 files changed, 836 insertions, 59 deletions
diff --git a/arch/arm/plat-nomadik/Kconfig b/arch/arm/plat-nomadik/Kconfig
index 159daf583f85..5da3f97c537b 100644
--- a/arch/arm/plat-nomadik/Kconfig
+++ b/arch/arm/plat-nomadik/Kconfig
@@ -19,4 +19,9 @@ config HAS_MTU
to multiple interrupt generating programmable
32-bit free running decrementing counters.
+config NOMADIK_GPIO
+ bool
+ help
+ Support for the Nomadik GPIO controller.
+
endif
diff --git a/arch/arm/plat-nomadik/Makefile b/arch/arm/plat-nomadik/Makefile
index 37c7cdd0f8f0..c33547361bd7 100644
--- a/arch/arm/plat-nomadik/Makefile
+++ b/arch/arm/plat-nomadik/Makefile
@@ -3,3 +3,4 @@
# Licensed under GPLv2
obj-$(CONFIG_HAS_MTU) += timer.o
+obj-$(CONFIG_NOMADIK_GPIO) += gpio.o
diff --git a/arch/arm/plat-nomadik/gpio.c b/arch/arm/plat-nomadik/gpio.c
new file mode 100644
index 000000000000..5a6ef252c38b
--- /dev/null
+++ b/arch/arm/plat-nomadik/gpio.c
@@ -0,0 +1,435 @@
+/*
+ * Generic GPIO driver for logic cells found in the Nomadik SoC
+ *
+ * Copyright (C) 2008,2009 STMicroelectronics
+ * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it>
+ * Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
+/*
+ * The GPIO module in the Nomadik family of Systems-on-Chip is an
+ * AMBA device, managing 32 pins and alternate functions. The logic block
+ * is currently only used in the Nomadik.
+ *
+ * Symbols in this file are called "nmk_gpio" for "nomadik gpio"
+ */
+
+#define NMK_GPIO_PER_CHIP 32
+struct nmk_gpio_chip {
+ struct gpio_chip chip;
+ void __iomem *addr;
+ struct clk *clk;
+ unsigned int parent_irq;
+ spinlock_t lock;
+ /* Keep track of configured edges */
+ u32 edge_rising;
+ u32 edge_falling;
+};
+
+/* Mode functions */
+int nmk_gpio_set_mode(int gpio, int gpio_mode)
+{
+ struct nmk_gpio_chip *nmk_chip;
+ unsigned long flags;
+ u32 afunc, bfunc, bit;
+
+ nmk_chip = get_irq_chip_data(NOMADIK_GPIO_TO_IRQ(gpio));
+ if (!nmk_chip)
+ return -EINVAL;
+
+ bit = 1 << (gpio - nmk_chip->chip.base);
+
+ spin_lock_irqsave(&nmk_chip->lock, flags);
+ afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & ~bit;
+ bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & ~bit;
+ if (gpio_mode & NMK_GPIO_ALT_A)
+ afunc |= bit;
+ if (gpio_mode & NMK_GPIO_ALT_B)
+ bfunc |= bit;
+ writel(afunc, nmk_chip->addr + NMK_GPIO_AFSLA);
+ writel(bfunc, nmk_chip->addr + NMK_GPIO_AFSLB);
+ spin_unlock_irqrestore(&nmk_chip->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(nmk_gpio_set_mode);
+
+int nmk_gpio_get_mode(int gpio)
+{
+ struct nmk_gpio_chip *nmk_chip;
+ u32 afunc, bfunc, bit;
+
+ nmk_chip = get_irq_chip_data(NOMADIK_GPIO_TO_IRQ(gpio));
+ if (!nmk_chip)
+ return -EINVAL;
+
+ bit = 1 << (gpio - nmk_chip->chip.base);
+
+ afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & bit;
+ bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & bit;
+
+ return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0);
+}
+EXPORT_SYMBOL(nmk_gpio_get_mode);
+
+
+/* IRQ functions */
+static inline int nmk_gpio_get_bitmask(int gpio)
+{
+ return 1 << (gpio % 32);
+}
+
+static void nmk_gpio_irq_ack(unsigned int irq)
+{
+ int gpio;
+ struct nmk_gpio_chip *nmk_chip;
+
+ gpio = NOMADIK_IRQ_TO_GPIO(irq);
+ nmk_chip = get_irq_chip_data(irq);
+ if (!nmk_chip)
+ return;
+ writel(nmk_gpio_get_bitmask(gpio), nmk_chip->addr + NMK_GPIO_IC);
+}
+
+static void __nmk_gpio_irq_modify(struct nmk_gpio_chip *nmk_chip,
+ int gpio, bool enable)
+{
+ u32 bitmask = nmk_gpio_get_bitmask(gpio);
+ u32 reg;
+
+ /* we must individually set/clear the two edges */
+ if (nmk_chip->edge_rising & bitmask) {
+ reg = readl(nmk_chip->addr + NMK_GPIO_RIMSC);
+ if (enable)
+ reg |= bitmask;
+ else
+ reg &= ~bitmask;
+ writel(reg, nmk_chip->addr + NMK_GPIO_RIMSC);
+ }
+ if (nmk_chip->edge_falling & bitmask) {
+ reg = readl(nmk_chip->addr + NMK_GPIO_FIMSC);
+ if (enable)
+ reg |= bitmask;
+ else
+ reg &= ~bitmask;
+ writel(reg, nmk_chip->addr + NMK_GPIO_FIMSC);
+ }
+}
+
+static void nmk_gpio_irq_modify(unsigned int irq, bool enable)
+{
+ int gpio;
+ struct nmk_gpio_chip *nmk_chip;
+ unsigned long flags;
+ u32 bitmask;
+
+ gpio = NOMADIK_IRQ_TO_GPIO(irq);
+ nmk_chip = get_irq_chip_data(irq);
+ bitmask = nmk_gpio_get_bitmask(gpio);
+ if (!nmk_chip)
+ return;
+
+ spin_lock_irqsave(&nmk_chip->lock, flags);
+ __nmk_gpio_irq_modify(nmk_chip, gpio, enable);
+ spin_unlock_irqrestore(&nmk_chip->lock, flags);
+}
+
+static void nmk_gpio_irq_mask(unsigned int irq)
+{
+ nmk_gpio_irq_modify(irq, false);
+};
+
+static void nmk_gpio_irq_unmask(unsigned int irq)
+{
+ nmk_gpio_irq_modify(irq, true);
+}
+
+static int nmk_gpio_irq_set_type(unsigned int irq, unsigned int type)
+{
+ bool enabled = !(irq_to_desc(irq)->status & IRQ_DISABLED);
+ int gpio;
+ struct nmk_gpio_chip *nmk_chip;
+ unsigned long flags;
+ u32 bitmask;
+
+ gpio = NOMADIK_IRQ_TO_GPIO(irq);
+ nmk_chip = get_irq_chip_data(irq);
+ bitmask = nmk_gpio_get_bitmask(gpio);
+ if (!nmk_chip)
+ return -EINVAL;
+
+ if (type & IRQ_TYPE_LEVEL_HIGH)
+ return -EINVAL;
+ if (type & IRQ_TYPE_LEVEL_LOW)
+ return -EINVAL;
+
+ spin_lock_irqsave(&nmk_chip->lock, flags);
+
+ if (enabled)
+ __nmk_gpio_irq_modify(nmk_chip, gpio, false);
+
+ nmk_chip->edge_rising &= ~bitmask;
+ if (type & IRQ_TYPE_EDGE_RISING)
+ nmk_chip->edge_rising |= bitmask;
+
+ nmk_chip->edge_falling &= ~bitmask;
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ nmk_chip->edge_falling |= bitmask;
+
+ if (enabled)
+ __nmk_gpio_irq_modify(nmk_chip, gpio, true);
+
+ spin_unlock_irqrestore(&nmk_chip->lock, flags);
+
+ return 0;
+}
+
+static struct irq_chip nmk_gpio_irq_chip = {
+ .name = "Nomadik-GPIO",
+ .ack = nmk_gpio_irq_ack,
+ .mask = nmk_gpio_irq_mask,
+ .unmask = nmk_gpio_irq_unmask,
+ .set_type = nmk_gpio_irq_set_type,
+};
+
+static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct nmk_gpio_chip *nmk_chip;
+ struct irq_chip *host_chip = get_irq_chip(irq);
+ unsigned int gpio_irq;
+ u32 pending;
+ unsigned int first_irq;
+
+ if (host_chip->mask_ack)
+ host_chip->mask_ack(irq);
+ else {
+ host_chip->mask(irq);
+ if (host_chip->ack)
+ host_chip->ack(irq);
+ }
+
+ nmk_chip = get_irq_data(irq);
+ first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base);
+ while ( (pending = readl(nmk_chip->addr + NMK_GPIO_IS)) ) {
+ gpio_irq = first_irq + __ffs(pending);
+ generic_handle_irq(gpio_irq);
+ }
+
+ host_chip->unmask(irq);
+}
+
+static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip)
+{
+ unsigned int first_irq;
+ int i;
+
+ first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base);
+ for (i = first_irq; i < first_irq + NMK_GPIO_PER_CHIP; i++) {
+ set_irq_chip(i, &nmk_gpio_irq_chip);
+ set_irq_handler(i, handle_edge_irq);
+ set_irq_flags(i, IRQF_VALID);
+ set_irq_chip_data(i, nmk_chip);
+ set_irq_type(i, IRQ_TYPE_EDGE_FALLING);
+ }
+ set_irq_chained_handler(nmk_chip->parent_irq, nmk_gpio_irq_handler);
+ set_irq_data(nmk_chip->parent_irq, nmk_chip);
+ return 0;
+}
+
+/* I/O Functions */
+static int nmk_gpio_make_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct nmk_gpio_chip *nmk_chip =
+ container_of(chip, struct nmk_gpio_chip, chip);
+
+ writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC);
+ return 0;
+}
+
+static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset,
+ int val)
+{
+ struct nmk_gpio_chip *nmk_chip =
+ container_of(chip, struct nmk_gpio_chip, chip);
+
+ writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRS);
+ return 0;
+}
+
+static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct nmk_gpio_chip *nmk_chip =
+ container_of(chip, struct nmk_gpio_chip, chip);
+ u32 bit = 1 << offset;
+
+ return (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0;
+}
+
+static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset,
+ int val)
+{
+ struct nmk_gpio_chip *nmk_chip =
+ container_of(chip, struct nmk_gpio_chip, chip);
+ u32 bit = 1 << offset;
+
+ if (val)
+ writel(bit, nmk_chip->addr + NMK_GPIO_DATS);
+ else
+ writel(bit, nmk_chip->addr + NMK_GPIO_DATC);
+}
+
+/* This structure is replicated for each GPIO block allocated at probe time */
+static struct gpio_chip nmk_gpio_template = {
+ .direction_input = nmk_gpio_make_input,
+ .get = nmk_gpio_get_input,
+ .direction_output = nmk_gpio_make_output,
+ .set = nmk_gpio_set_output,
+ .ngpio = NMK_GPIO_PER_CHIP,
+ .can_sleep = 0,
+};
+
+static int __init nmk_gpio_probe(struct platform_device *dev)
+{
+ struct nmk_gpio_platform_data *pdata = dev->dev.platform_data;
+ struct nmk_gpio_chip *nmk_chip;
+ struct gpio_chip *chip;
+ struct resource *res;
+ struct clk *clk;
+ int irq;
+ int ret;
+
+ if (!pdata)
+ return -ENODEV;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto out;
+ }
+
+ if (request_mem_region(res->start, resource_size(res),
+ dev_name(&dev->dev)) == NULL) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ clk = clk_get(&dev->dev, NULL);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto out_release;
+ }
+
+ clk_enable(clk);
+
+ nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL);
+ if (!nmk_chip) {
+ ret = -ENOMEM;
+ goto out_clk;
+ }
+ /*
+ * The virt address in nmk_chip->addr is in the nomadik register space,
+ * so we can simply convert the resource address, without remapping
+ */
+ nmk_chip->clk = clk;
+ nmk_chip->addr = io_p2v(res->start);
+ nmk_chip->chip = nmk_gpio_template;
+ nmk_chip->parent_irq = irq;
+ spin_lock_init(&nmk_chip->lock);
+
+ chip = &nmk_chip->chip;
+ chip->base = pdata->first_gpio;
+ chip->label = pdata->name;
+ chip->dev = &dev->dev;
+ chip->owner = THIS_MODULE;
+
+ ret = gpiochip_add(&nmk_chip->chip);
+ if (ret)
+ goto out_free;
+
+ platform_set_drvdata(dev, nmk_chip);
+
+ nmk_gpio_init_irq(nmk_chip);
+
+ dev_info(&dev->dev, "Bits %i-%i at address %p\n",
+ nmk_chip->chip.base, nmk_chip->chip.base+31, nmk_chip->addr);
+ return 0;
+
+out_free:
+ kfree(nmk_chip);
+out_clk:
+ clk_disable(clk);
+ clk_put(clk);
+out_release:
+ release_mem_region(res->start, resource_size(res));
+out:
+ dev_err(&dev->dev, "Failure %i for GPIO %i-%i\n", ret,
+ pdata->first_gpio, pdata->first_gpio+31);
+ return ret;
+}
+
+static int __exit nmk_gpio_remove(struct platform_device *dev)
+{
+ struct nmk_gpio_chip *nmk_chip;
+ struct resource *res;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+
+ nmk_chip = platform_get_drvdata(dev);
+ gpiochip_remove(&nmk_chip->chip);
+ clk_disable(nmk_chip->clk);
+ clk_put(nmk_chip->clk);
+ kfree(nmk_chip);
+ release_mem_region(res->start, resource_size(res));
+ return 0;
+}
+
+
+static struct platform_driver nmk_gpio_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "gpio",
+ },
+ .probe = nmk_gpio_probe,
+ .remove = __exit_p(nmk_gpio_remove),
+ .suspend = NULL, /* to be done */
+ .resume = NULL,
+};
+
+static int __init nmk_gpio_init(void)
+{
+ return platform_driver_register(&nmk_gpio_driver);
+}
+
+arch_initcall(nmk_gpio_init);
+
+MODULE_AUTHOR("Prafulla WADASKAR and Alessandro Rubini");
+MODULE_DESCRIPTION("Nomadik GPIO Driver");
+MODULE_LICENSE("GPL");
+
+
diff --git a/arch/arm/plat-nomadik/include/plat/gpio.h b/arch/arm/plat-nomadik/include/plat/gpio.h
new file mode 100644
index 000000000000..4200811249ca
--- /dev/null
+++ b/arch/arm/plat-nomadik/include/plat/gpio.h
@@ -0,0 +1,70 @@
+/*
+ * Structures and registers for GPIO access in the Nomadik SoC
+ *
+ * Copyright (C) 2008 STMicroelectronics
+ * Author: Prafulla WADASKAR <prafulla.wadaskar@st.com>
+ * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __ASM_PLAT_GPIO_H
+#define __ASM_PLAT_GPIO_H
+
+#include <asm-generic/gpio.h>
+
+/*
+ * These currently cause a function call to happen, they may be optimized
+ * if needed by adding cpu-specific defines to identify blocks
+ * (see mach-pxa/include/mach/gpio.h as an example using GPLR etc)
+ */
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
+#define gpio_to_irq __gpio_to_irq
+
+/*
+ * "nmk_gpio" and "NMK_GPIO" stand for "Nomadik GPIO", leaving
+ * the "gpio" namespace for generic and cross-machine functions
+ */
+
+/* Register in the logic block */
+#define NMK_GPIO_DAT 0x00
+#define NMK_GPIO_DATS 0x04
+#define NMK_GPIO_DATC 0x08
+#define NMK_GPIO_PDIS 0x0c
+#define NMK_GPIO_DIR 0x10
+#define NMK_GPIO_DIRS 0x14
+#define NMK_GPIO_DIRC 0x18
+#define NMK_GPIO_SLPC 0x1c
+#define NMK_GPIO_AFSLA 0x20
+#define NMK_GPIO_AFSLB 0x24
+
+#define NMK_GPIO_RIMSC 0x40
+#define NMK_GPIO_FIMSC 0x44
+#define NMK_GPIO_IS 0x48
+#define NMK_GPIO_IC 0x4c
+#define NMK_GPIO_RWIMSC 0x50
+#define NMK_GPIO_FWIMSC 0x54
+#define NMK_GPIO_WKS 0x58
+
+/* Alternate functions: function C is set in hw by setting both A and B */
+#define NMK_GPIO_ALT_GPIO 0
+#define NMK_GPIO_ALT_A 1
+#define NMK_GPIO_ALT_B 2
+#define NMK_GPIO_ALT_C (NMK_GPIO_ALT_A | NMK_GPIO_ALT_B)
+
+extern int nmk_gpio_set_mode(int gpio, int gpio_mode);
+extern int nmk_gpio_get_mode(int gpio);
+
+/*
+ * Platform data to register a block: only the initial gpio/irq number.
+ */
+struct nmk_gpio_platform_data {
+ char *name;
+ int first_gpio;
+ int first_irq;
+};
+
+#endif /* __ASM_PLAT_GPIO_H */
diff --git a/arch/arm/plat-nomadik/include/plat/ste_dma40.h b/arch/arm/plat-nomadik/include/plat/ste_dma40.h
new file mode 100644
index 000000000000..4d12ea4ca361
--- /dev/null
+++ b/arch/arm/plat-nomadik/include/plat/ste_dma40.h
@@ -0,0 +1,239 @@
+/*
+ * arch/arm/plat-nomadik/include/plat/ste_dma40.h
+ *
+ * Copyright (C) ST-Ericsson 2007-2010
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Per Friden <per.friden@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ */
+
+
+#ifndef STE_DMA40_H
+#define STE_DMA40_H
+
+#include <linux/dmaengine.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/dmaengine.h>
+
+/* dev types for memcpy */
+#define STEDMA40_DEV_DST_MEMORY (-1)
+#define STEDMA40_DEV_SRC_MEMORY (-1)
+
+/*
+ * Description of bitfields of channel_type variable is available in
+ * the info structure.
+ */
+
+/* Priority */
+#define STEDMA40_INFO_PRIO_TYPE_POS 2
+#define STEDMA40_HIGH_PRIORITY_CHANNEL (0x1 << STEDMA40_INFO_PRIO_TYPE_POS)
+#define STEDMA40_LOW_PRIORITY_CHANNEL (0x2 << STEDMA40_INFO_PRIO_TYPE_POS)
+
+/* Mode */
+#define STEDMA40_INFO_CH_MODE_TYPE_POS 6
+#define STEDMA40_CHANNEL_IN_PHY_MODE (0x1 << STEDMA40_INFO_CH_MODE_TYPE_POS)
+#define STEDMA40_CHANNEL_IN_LOG_MODE (0x2 << STEDMA40_INFO_CH_MODE_TYPE_POS)
+#define STEDMA40_CHANNEL_IN_OPER_MODE (0x3 << STEDMA40_INFO_CH_MODE_TYPE_POS)
+
+/* Mode options */
+#define STEDMA40_INFO_CH_MODE_OPT_POS 8
+#define STEDMA40_PCHAN_BASIC_MODE (0x1 << STEDMA40_INFO_CH_MODE_OPT_POS)
+#define STEDMA40_PCHAN_MODULO_MODE (0x2 << STEDMA40_INFO_CH_MODE_OPT_POS)
+#define STEDMA40_PCHAN_DOUBLE_DST_MODE (0x3 << STEDMA40_INFO_CH_MODE_OPT_POS)
+#define STEDMA40_LCHAN_SRC_PHY_DST_LOG (0x1 << STEDMA40_INFO_CH_MODE_OPT_POS)
+#define STEDMA40_LCHAN_SRC_LOG_DST_PHS (0x2 << STEDMA40_INFO_CH_MODE_OPT_POS)
+#define STEDMA40_LCHAN_SRC_LOG_DST_LOG (0x3 << STEDMA40_INFO_CH_MODE_OPT_POS)
+
+/* Interrupt */
+#define STEDMA40_INFO_TIM_POS 10
+#define STEDMA40_NO_TIM_FOR_LINK (0x0 << STEDMA40_INFO_TIM_POS)
+#define STEDMA40_TIM_FOR_LINK (0x1 << STEDMA40_INFO_TIM_POS)
+
+/* End of channel_type configuration */
+
+#define STEDMA40_ESIZE_8_BIT 0x0
+#define STEDMA40_ESIZE_16_BIT 0x1
+#define STEDMA40_ESIZE_32_BIT 0x2
+#define STEDMA40_ESIZE_64_BIT 0x3
+
+/* The value 4 indicates that PEN-reg shall be set to 0 */
+#define STEDMA40_PSIZE_PHY_1 0x4
+#define STEDMA40_PSIZE_PHY_2 0x0
+#define STEDMA40_PSIZE_PHY_4 0x1
+#define STEDMA40_PSIZE_PHY_8 0x2
+#define STEDMA40_PSIZE_PHY_16 0x3
+
+/*
+ * The number of elements differ in logical and
+ * physical mode
+ */
+#define STEDMA40_PSIZE_LOG_1 STEDMA40_PSIZE_PHY_2
+#define STEDMA40_PSIZE_LOG_4 STEDMA40_PSIZE_PHY_4
+#define STEDMA40_PSIZE_LOG_8 STEDMA40_PSIZE_PHY_8
+#define STEDMA40_PSIZE_LOG_16 STEDMA40_PSIZE_PHY_16
+
+enum stedma40_flow_ctrl {
+ STEDMA40_NO_FLOW_CTRL,
+ STEDMA40_FLOW_CTRL,
+};
+
+enum stedma40_endianess {
+ STEDMA40_LITTLE_ENDIAN,
+ STEDMA40_BIG_ENDIAN
+};
+
+enum stedma40_periph_data_width {
+ STEDMA40_BYTE_WIDTH = STEDMA40_ESIZE_8_BIT,
+ STEDMA40_HALFWORD_WIDTH = STEDMA40_ESIZE_16_BIT,
+ STEDMA40_WORD_WIDTH = STEDMA40_ESIZE_32_BIT,
+ STEDMA40_DOUBLEWORD_WIDTH = STEDMA40_ESIZE_64_BIT
+};
+
+struct stedma40_half_channel_info {
+ enum stedma40_endianess endianess;
+ enum stedma40_periph_data_width data_width;
+ int psize;
+ enum stedma40_flow_ctrl flow_ctrl;
+};
+
+enum stedma40_xfer_dir {
+ STEDMA40_MEM_TO_MEM,
+ STEDMA40_MEM_TO_PERIPH,
+ STEDMA40_PERIPH_TO_MEM,
+ STEDMA40_PERIPH_TO_PERIPH
+};
+
+
+/**
+ * struct stedma40_chan_cfg - Structure to be filled by client drivers.
+ *
+ * @dir: MEM 2 MEM, PERIPH 2 MEM , MEM 2 PERIPH, PERIPH 2 PERIPH
+ * @channel_type: priority, mode, mode options and interrupt configuration.
+ * @src_dev_type: Src device type
+ * @dst_dev_type: Dst device type
+ * @src_info: Parameters for dst half channel
+ * @dst_info: Parameters for dst half channel
+ * @pre_transfer_data: Data to be passed on to the pre_transfer() function.
+ * @pre_transfer: Callback used if needed before preparation of transfer.
+ * Only called if device is set. size of bytes to transfer
+ * (in case of multiple element transfer size is size of the first element).
+ *
+ *
+ * This structure has to be filled by the client drivers.
+ * It is recommended to do all dma configurations for clients in the machine.
+ *
+ */
+struct stedma40_chan_cfg {
+ enum stedma40_xfer_dir dir;
+ unsigned int channel_type;
+ int src_dev_type;
+ int dst_dev_type;
+ struct stedma40_half_channel_info src_info;
+ struct stedma40_half_channel_info dst_info;
+ void *pre_transfer_data;
+ int (*pre_transfer) (struct dma_chan *chan,
+ void *data,
+ int size);
+};
+
+/**
+ * struct stedma40_platform_data - Configuration struct for the dma device.
+ *
+ * @dev_len: length of dev_tx and dev_rx
+ * @dev_tx: mapping between destination event line and io address
+ * @dev_rx: mapping between source event line and io address
+ * @memcpy: list of memcpy event lines
+ * @memcpy_len: length of memcpy
+ * @memcpy_conf_phy: default configuration of physical channel memcpy
+ * @memcpy_conf_log: default configuration of logical channel memcpy
+ * @llis_per_log: number of max linked list items per logical channel
+ *
+ */
+struct stedma40_platform_data {
+ u32 dev_len;
+ const dma_addr_t *dev_tx;
+ const dma_addr_t *dev_rx;
+ int *memcpy;
+ u32 memcpy_len;
+ struct stedma40_chan_cfg *memcpy_conf_phy;
+ struct stedma40_chan_cfg *memcpy_conf_log;
+ unsigned int llis_per_log;
+};
+
+/**
+ * setdma40_set_psize() - Used for changing the package size of an
+ * already configured dma channel.
+ *
+ * @chan: dmaengine handle
+ * @src_psize: new package side for src. (STEDMA40_PSIZE*)
+ * @src_psize: new package side for dst. (STEDMA40_PSIZE*)
+ *
+ * returns 0 on ok, otherwise negative error number.
+ */
+int stedma40_set_psize(struct dma_chan *chan,
+ int src_psize,
+ int dst_psize);
+
+/**
+ * stedma40_filter() - Provides stedma40_chan_cfg to the
+ * ste_dma40 dma driver via the dmaengine framework.
+ * does some checking of what's provided.
+ *
+ * Never directly called by client. It used by dmaengine.
+ * @chan: dmaengine handle.
+ * @data: Must be of type: struct stedma40_chan_cfg and is
+ * the configuration of the framework.
+ *
+ *
+ */
+
+bool stedma40_filter(struct dma_chan *chan, void *data);
+
+/**
+ * stedma40_memcpy_sg() - extension of the dma framework, memcpy to/from
+ * scattergatter lists.
+ *
+ * @chan: dmaengine handle
+ * @sgl_dst: Destination scatter list
+ * @sgl_src: Source scatter list
+ * @sgl_len: The length of each scatterlist. Both lists must be of equal length
+ * and each element must match the corresponding element in the other scatter
+ * list.
+ * @flags: is actually enum dma_ctrl_flags. See dmaengine.h
+ */
+
+struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan,
+ struct scatterlist *sgl_dst,
+ struct scatterlist *sgl_src,
+ unsigned int sgl_len,
+ unsigned long flags);
+
+/**
+ * stedma40_slave_mem() - Transfers a raw data buffer to or from a slave
+ * (=device)
+ *
+ * @chan: dmaengine handle
+ * @addr: source or destination physicall address.
+ * @size: bytes to transfer
+ * @direction: direction of transfer
+ * @flags: is actually enum dma_ctrl_flags. See dmaengine.h
+ */
+
+static inline struct
+dma_async_tx_descriptor *stedma40_slave_mem(struct dma_chan *chan,
+ dma_addr_t addr,
+ unsigned int size,
+ enum dma_data_direction direction,
+ unsigned long flags)
+{
+ struct scatterlist sg;
+ sg_init_table(&sg, 1);
+ sg.dma_address = addr;
+ sg.length = size;
+
+ return chan->device->device_prep_slave_sg(chan, &sg, 1,
+ direction, flags);
+}
+
+#endif
diff --git a/arch/arm/plat-nomadik/timer.c b/arch/arm/plat-nomadik/timer.c
index fa7cb3a57cbf..0ff3798769ab 100644
--- a/arch/arm/plat-nomadik/timer.c
+++ b/arch/arm/plat-nomadik/timer.c
@@ -2,7 +2,7 @@
* linux/arch/arm/mach-nomadik/timer.c
*
* Copyright (C) 2008 STMicroelectronics
- * Copyright (C) 2009 Alessandro Rubini, somewhat based on at91sam926x
+ * Copyright (C) 2010 Alessandro Rubini
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
@@ -18,123 +18,150 @@
#include <plat/mtu.h>
-static u32 nmdk_count; /* accumulated count */
-static u32 nmdk_cycle; /* write-once */
-
-/* setup by the platform code */
-void __iomem *mtu_base;
+void __iomem *mtu_base; /* ssigned by machine code */
/*
- * clocksource: the MTU device is a decrementing counters, so we negate
- * the value being read.
+ * Kernel assumes that sched_clock can be called early
+ * but the MTU may not yet be initialized.
*/
-static cycle_t nmdk_read_timer(struct clocksource *cs)
+static cycle_t nmdk_read_timer_dummy(struct clocksource *cs)
{
- u32 count = readl(mtu_base + MTU_VAL(0));
- return nmdk_count + nmdk_cycle - count;
+ return 0;
+}
+/* clocksource: MTU decrements, so we negate the value being read. */
+static cycle_t nmdk_read_timer(struct clocksource *cs)
+{
+ return -readl(mtu_base + MTU_VAL(0));
}
static struct clocksource nmdk_clksrc = {
.name = "mtu_0",
- .rating = 120,
- .read = nmdk_read_timer,
+ .rating = 200,
+ .read = nmdk_read_timer_dummy,
+ .mask = CLOCKSOURCE_MASK(32),
.shift = 20,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
/*
- * Clockevent device: currently only periodic mode is supported
+ * Override the global weak sched_clock symbol with this
+ * local implementation which uses the clocksource to get some
+ * better resolution when scheduling the kernel. We accept that
+ * this wraps around for now, since it is just a relative time
+ * stamp. (Inspired by OMAP implementation.)
*/
+unsigned long long notrace sched_clock(void)
+{
+ return clocksource_cyc2ns(nmdk_clksrc.read(
+ &nmdk_clksrc),
+ nmdk_clksrc.mult,
+ nmdk_clksrc.shift);
+}
+
+/* Clockevent device: use one-shot mode */
static void nmdk_clkevt_mode(enum clock_event_mode mode,
struct clock_event_device *dev)
{
+ u32 cr;
+
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
- /* count current value? */
- writel(readl(mtu_base + MTU_IMSC) | 1, mtu_base + MTU_IMSC);
+ pr_err("%s: periodic mode not supported\n", __func__);
break;
case CLOCK_EVT_MODE_ONESHOT:
- BUG(); /* Not supported, yet */
- /* FALLTHROUGH */
+ /* Load highest value, enable device, enable interrupts */
+ cr = readl(mtu_base + MTU_CR(1));
+ writel(0, mtu_base + MTU_LR(1));
+ writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(1));
+ writel(0x2, mtu_base + MTU_IMSC);
+ break;
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED:
- writel(readl(mtu_base + MTU_IMSC) & ~1, mtu_base + MTU_IMSC);
+ /* disable irq */
+ writel(0, mtu_base + MTU_IMSC);
break;
case CLOCK_EVT_MODE_RESUME:
break;
}
}
+static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev)
+{
+ /* writing the value has immediate effect */
+ writel(evt, mtu_base + MTU_LR(1));
+ return 0;
+}
+
static struct clock_event_device nmdk_clkevt = {
- .name = "mtu_0",
- .features = CLOCK_EVT_FEAT_PERIODIC,
+ .name = "mtu_1",
+ .features = CLOCK_EVT_FEAT_ONESHOT,
.shift = 32,
- .rating = 100,
+ .rating = 200,
.set_mode = nmdk_clkevt_mode,
+ .set_next_event = nmdk_clkevt_next,
};
/*
- * IRQ Handler for the timer 0 of the MTU block. The irq is not shared
- * as we are the only users of mtu0 by now.
+ * IRQ Handler for timer 1 of the MTU block.
*/
static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
{
- /* ack: "interrupt clear register" */
- writel(1 << 0, mtu_base + MTU_ICR);
-
- /* we can't count lost ticks, unfortunately */
- nmdk_count += nmdk_cycle;
- nmdk_clkevt.event_handler(&nmdk_clkevt);
+ struct clock_event_device *evdev = dev_id;
+ writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */
+ evdev->event_handler(evdev);
return IRQ_HANDLED;
}
-/*
- * Set up timer interrupt, and return the current time in seconds.
- */
static struct irqaction nmdk_timer_irq = {
.name = "Nomadik Timer Tick",
.flags = IRQF_DISABLED | IRQF_TIMER,
.handler = nmdk_timer_interrupt,
+ .dev_id = &nmdk_clkevt,
};
-static void nmdk_timer_reset(void)
-{
- u32 cr;
-
- writel(0, mtu_base + MTU_CR(0)); /* off */
-
- /* configure load and background-load, and fire it up */
- writel(nmdk_cycle, mtu_base + MTU_LR(0));
- writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
- cr = MTU_CRn_PERIODIC | MTU_CRn_PRESCALE_1 | MTU_CRn_32BITS;
- writel(cr, mtu_base + MTU_CR(0));
- writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0));
-}
-
void __init nmdk_timer_init(void)
{
unsigned long rate;
- int bits;
-
- rate = CLOCK_TICK_RATE; /* 2.4MHz */
- nmdk_cycle = (rate + HZ/2) / HZ;
+ u32 cr = MTU_CRn_32BITS;;
+
+ /*
+ * Tick rate is 2.4MHz for Nomadik and 110MHz for ux500:
+ * use a divide-by-16 counter if it's more than 16MHz
+ */
+ rate = CLOCK_TICK_RATE;
+ if (rate > 16 << 20) {
+ rate /= 16;
+ cr |= MTU_CRn_PRESCALE_16;
+ } else {
+ cr |= MTU_CRn_PRESCALE_1;
+ }
- /* Init the timer and register clocksource */
- nmdk_timer_reset();
+ /* Timer 0 is the free running clocksource */
+ writel(cr, mtu_base + MTU_CR(0));
+ writel(0, mtu_base + MTU_LR(0));
+ writel(0, mtu_base + MTU_BGLR(0));
+ writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0));
nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift);
- bits = 8*sizeof(nmdk_count);
- nmdk_clksrc.mask = CLOCKSOURCE_MASK(bits);
+ /* Now the scheduling clock is ready */
+ nmdk_clksrc.read = nmdk_read_timer;
if (clocksource_register(&nmdk_clksrc))
- printk(KERN_ERR "timer: failed to initialize clock "
- "source %s\n", nmdk_clksrc.name);
+ pr_err("timer: failed to initialize clock source %s\n",
+ nmdk_clksrc.name);
+
+ /* Timer 1 is used for events, fix according to rate */
+ writel(cr | MTU_CRn_ONESHOT, mtu_base + MTU_CR(1)); /* off, currently */
+ nmdk_clkevt.mult = div_sc(rate, NSEC_PER_SEC, nmdk_clkevt.shift);
+ nmdk_clkevt.max_delta_ns =
+ clockevent_delta2ns(0xffffffff, &nmdk_clkevt);
+ nmdk_clkevt.min_delta_ns =
+ clockevent_delta2ns(0x00000002, &nmdk_clkevt);
+ nmdk_clkevt.cpumask = cpumask_of(0);
/* Register irq and clockevents */
setup_irq(IRQ_MTU0, &nmdk_timer_irq);
- nmdk_clkevt.mult = div_sc(rate, NSEC_PER_SEC, nmdk_clkevt.shift);
- nmdk_clkevt.cpumask = cpumask_of(0);
clockevents_register_device(&nmdk_clkevt);
}