ARM: OMAP: Merge board specific files from N800 tree
authorKai Svahn <kai.svahn@nokia.com>
Fri, 26 Jan 2007 20:39:48 +0000 (12:39 -0800)
committerTony Lindgren <tony@atomide.com>
Mon, 6 Aug 2007 09:22:28 +0000 (02:22 -0700)
This patch merges board specific files from N800 tree.
Nokia has published the files at:

http://repository.maemo.org/pool/maemo3.0/free/source/
kernel-source-rx-34_2.6.18.orig.tar.gz
kernel-source-rx-34_2.6.18-osso29.diff.gz

Signed-off-by: Kai Svahn <kai.svahn@nokia.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
13 files changed:
arch/arm/mach-omap2/Kconfig
arch/arm/mach-omap2/Makefile
arch/arm/mach-omap2/board-n800-audio.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800-bt.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800-dsp.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800-flash.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800-mmc.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800-pm.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800-usb.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800.c [new file with mode: 0644]
include/asm-arm/arch-omap/board-nokia.h
include/asm-arm/arch-omap/board.h
include/asm-arm/arch-omap/onenand.h

index c7507ad..752dd6e 100644 (file)
@@ -54,4 +54,13 @@ config MACH_OMAP_APOLLON
 
 config MACH_OMAP_2430SDP
        bool "OMAP 2430 SDP board"
-       depends on ARCH_OMAP2 && ARCH_OMAP24XX
\ No newline at end of file
+       depends on ARCH_OMAP2 && ARCH_OMAP24XX
+
+config MACH_NOKIA_N800
+       bool "Nokia N800"
+       depends on ARCH_OMAP24XX
+
+config MACH_OMAP2_TUSB6010
+       bool
+       depends on ARCH_OMAP2 && ARCH_OMAP2420
+       default y if MACH_NOKIA_N800
\ No newline at end of file
index 036f4ee..4cccb4c 100644 (file)
@@ -16,4 +16,8 @@ obj-$(CONFIG_MACH_OMAP_GENERIC)               += board-generic.o
 obj-$(CONFIG_MACH_OMAP_H4)             += board-h4.o
 obj-$(CONFIG_MACH_OMAP_2430SDP)                += board-2430sdp.o
 obj-$(CONFIG_MACH_OMAP_APOLLON)                += board-apollon.o
+obj-$(CONFIG_MACH_NOKIA_N800)          += board-n800.o board-n800-flash.o \
+                                          board-n800-mmc.o board-n800-bt.o \
+                                          board-n800-audio.o board-n800-usb.o \
+                                          board-n800-dsp.o board-n800-pm.o
 
diff --git a/arch/arm/mach-omap2/board-n800-audio.c b/arch/arm/mach-omap2/board-n800-audio.c
new file mode 100644 (file)
index 0000000..9033de5
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * linux/arch/arm/mach-omap2/board-n800-audio.c
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Contact: Juha Yrjola
+ *          Jarkko Nikula <jarkko.nikula@nokia.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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/spi/tsc2301.h>
+
+#include <asm/io.h>
+#include <asm/arch/eac.h>
+
+#include "../plat-omap/dsp/dsp_common.h"
+
+#if defined(CONFIG_SPI_TSC2301_AUDIO) && defined(CONFIG_SND_OMAP24XX_EAC)
+#define AUDIO_ENABLED
+
+static struct clk *sys_clkout2;
+static struct clk *func96m_clk;
+static struct device *eac_device;
+static struct device *tsc2301_device;
+
+static int enable_audio;
+static int audio_ok;
+static spinlock_t audio_lock;
+
+/*
+ * Leaving EAC and sys_clkout2 pins multiplexed to those subsystems results
+ * in about 2 mA extra current leak when audios are powered down. The
+ * workaround is to multiplex them to protected mode (with pull-ups enabled)
+ * whenever audio is not being used.
+ */
+static int eac_mux_disabled = 0;
+static int clkout2_mux_disabled = 0;
+static u32 saved_mux[2];
+
+static void n800_enable_eac_mux(void)
+{
+       if (!eac_mux_disabled)
+               return;
+       __raw_writel(saved_mux[1], IO_ADDRESS(0x48000124));
+       eac_mux_disabled = 0;
+}
+
+static void n800_disable_eac_mux(void)
+{
+       if (eac_mux_disabled) {
+               WARN_ON(eac_mux_disabled);
+               return;
+       }
+       saved_mux[1] = __raw_readl(IO_ADDRESS(0x48000124));
+       __raw_writel(0x1f1f1f1f, IO_ADDRESS(0x48000124));
+       eac_mux_disabled = 1;
+}
+
+static void n800_enable_clkout2_mux(void)
+{
+       if (!clkout2_mux_disabled)
+               return;
+       __raw_writel(saved_mux[0], IO_ADDRESS(0x480000e8));
+       clkout2_mux_disabled = 0;
+}
+
+static void n800_disable_clkout2_mux(void)
+{
+       u32 l;
+
+       if (clkout2_mux_disabled) {
+               WARN_ON(clkout2_mux_disabled);
+               return;
+       }
+       saved_mux[0] = __raw_readl(IO_ADDRESS(0x480000e8));
+       l = saved_mux[0] & ~0xff;
+       l |= 0x1f;
+       __raw_writel(l, IO_ADDRESS(0x480000e8));
+       clkout2_mux_disabled = 1;
+}
+
+static int n800_eac_enable_ext_clocks(struct device *dev)
+{
+       BUG_ON(tsc2301_device == NULL);
+       n800_enable_eac_mux();
+       tsc2301_mixer_enable_mclk(tsc2301_device);
+
+       return 0;
+}
+
+static void n800_eac_disable_ext_clocks(struct device *dev)
+{
+       BUG_ON(tsc2301_device == NULL);
+       tsc2301_mixer_disable_mclk(tsc2301_device);
+       n800_disable_eac_mux();
+}
+
+static int n800_audio_set_power(void *pdata, int dac, int adc)
+{
+       BUG_ON(pdata != tsc2301_device);
+       tsc2301_mixer_set_power(tsc2301_device, dac, adc);
+
+       return 0;
+}
+
+static int n800_audio_register_controls(void *pdata, struct snd_card *card)
+{
+       BUG_ON(pdata != tsc2301_device);
+       return tsc2301_mixer_register_controls(tsc2301_device, card);
+}
+
+static struct eac_codec n800_eac_codec = {
+       .mclk_src = EAC_MCLK_EXT_2x12288000,
+       .codec_mode = EAC_CODEC_I2S,
+       .codec_conf.i2s.polarity_changed_mode = 0,
+       .codec_conf.i2s.sync_delay_enable = 0,
+       .default_rate = 48000,
+       .set_power = n800_audio_set_power,
+       .register_controls = n800_audio_register_controls,
+       .short_name = "TSC2301",
+};
+
+static int n800_register_codec(void)
+{
+       int r, do_enable = 0;
+       unsigned long flags;
+
+       n800_eac_codec.private_data = tsc2301_device;
+       r = eac_register_codec(eac_device, &n800_eac_codec);
+       if (r < 0)
+               return r;
+       spin_lock_irqsave(&audio_lock, flags);
+       audio_ok = 1;
+       if (enable_audio)
+               do_enable = 1;
+       spin_unlock_irqrestore(&audio_lock, flags);
+       if (do_enable)
+               eac_set_mode(eac_device, 1, 1);
+       return 0;
+}
+
+static void n800_unregister_codec(void)
+{
+       audio_ok = 0;
+       eac_unregister_codec(eac_device);
+       eac_set_mode(eac_device, 0, 0);
+}
+
+static int n800_eac_init(struct device *dev)
+{
+       int r;
+
+       BUG_ON(eac_device != NULL);
+       eac_device = dev;
+       if (tsc2301_device != NULL) {
+               r = n800_register_codec();
+               if (r < 0)
+                       return r;
+       }
+
+       return 0;
+}
+
+static void n800_eac_cleanup(struct device *dev)
+{
+       eac_device = NULL;
+       if (tsc2301_device != NULL)
+               n800_unregister_codec();
+}
+
+static int n800_codec_get_clocks(struct device *dev)
+{
+       sys_clkout2 = clk_get(dev, "sys_clkout2");
+       if (IS_ERR(sys_clkout2)) {
+               dev_err(dev, "Could not get sys_clkout2\n");
+               return -ENODEV;
+       }
+       /* configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
+        * 96 MHz as its parent in order to get 12 MHz */
+       func96m_clk = clk_get(dev, "func_96m_ck");
+       if (IS_ERR(func96m_clk)) {
+               dev_err(dev, "Could not get func 96M clock\n");
+               clk_put(sys_clkout2);
+               return -ENODEV;
+       }
+
+       clk_set_parent(sys_clkout2, func96m_clk);
+       clk_set_rate(sys_clkout2, 12000000);
+
+       return 0;
+}
+
+static void n800_codec_put_clocks(struct device *dev)
+{
+       clk_put(func96m_clk);
+       clk_put(sys_clkout2);
+}
+
+static int n800_codec_enable_clock(struct device *dev)
+{
+       n800_enable_clkout2_mux();
+       return clk_enable(sys_clkout2);
+}
+
+static void n800_codec_disable_clock(struct device *dev)
+{
+       clk_disable(sys_clkout2);
+       n800_disable_clkout2_mux();
+}
+
+static int n800_codec_init(struct device *dev)
+{
+       int r;
+
+       BUG_ON(tsc2301_device != NULL);
+       tsc2301_device = dev;
+       if ((r = n800_codec_get_clocks(dev)) < 0)
+               return r;
+       if (eac_device != NULL) {
+               r = n800_register_codec();
+               if (r < 0) {
+                       n800_codec_put_clocks(dev);
+                       return r;
+               }
+       }
+       return 0;
+}
+
+static void n800_codec_cleanup(struct device *dev)
+{
+       tsc2301_device = NULL;
+       if (eac_device != NULL)
+               n800_unregister_codec();
+       n800_codec_put_clocks(dev);
+}
+
+static struct eac_platform_data n800_eac_data = {
+       .init = n800_eac_init,
+       .cleanup = n800_eac_cleanup,
+       .enable_ext_clocks = n800_eac_enable_ext_clocks,
+       .disable_ext_clocks = n800_eac_disable_ext_clocks,
+};
+
+static const struct tsc2301_mixer_gpio n800_mixer_gpios[] = {
+       {
+               .name                   = "Headset Amplifier",
+               .gpio                   = 1,
+               .deactivate_on_pd       = 1,
+       }, {
+               .name                   = "Speaker Amplifier",
+               .gpio                   = 2,
+               .def_enable             = 1,
+               .deactivate_on_pd       = 1,
+       }, {
+               .name                   = "Headset Mic Select",
+               .gpio                   = 3,
+       }
+};
+
+static struct platform_device retu_headset_device = {
+       .name           = "retu-headset",
+       .id             = -1,
+       .dev            = {
+               .release        = NULL,
+       },
+};
+
+void __init n800_audio_init(struct tsc2301_platform_data *tc)
+{
+       spin_lock_init(&audio_lock);
+
+       if (platform_device_register(&retu_headset_device) < 0)
+               return;
+       omap_init_eac(&n800_eac_data);
+
+       tc->pll_pdc = 7;
+       tc->pll_a = 7;
+       tc->pll_n = 9;
+       tc->pll_output = 1;
+       tc->mclk_ratio = TSC2301_MCLK_256xFS;
+       tc->i2s_sample_rate = TSC2301_I2S_SR_48000;
+       tc->i2s_format = TSC2301_I2S_FORMAT0;
+       tc->power_down_blocks = TSC2301_REG_PD_MISC_MOPD;
+       tc->mixer_gpios = n800_mixer_gpios;
+       tc->n_mixer_gpios = ARRAY_SIZE(n800_mixer_gpios);
+       tc->codec_init = n800_codec_init;
+       tc->codec_cleanup = n800_codec_cleanup;
+       tc->enable_clock = n800_codec_enable_clock;
+       tc->disable_clock = n800_codec_disable_clock;
+}
+
+#else
+
+void __init n800_audio_init(void)
+{
+}
+
+#endif
+
+#ifdef CONFIG_OMAP_DSP
+
+int n800_audio_enable(struct dsp_kfunc_device *kdev, int stage)
+{
+#ifdef AUDIO_ENABLED
+       unsigned long flags;
+       int do_enable = 0;
+
+       spin_lock_irqsave(&audio_lock, flags);
+
+       pr_debug("DSP power up request (audio codec %sinitialized)\n",
+                audio_ok ? "" : "not ");
+
+       if (enable_audio)
+               goto out;
+       enable_audio = 1;
+       if (audio_ok)
+               do_enable = 1;
+out:
+       spin_unlock_irqrestore(&audio_lock, flags);
+       if (do_enable)
+               eac_set_mode(eac_device, 1, 1);
+#endif
+       return 0;
+}
+
+int n800_audio_disable(struct dsp_kfunc_device *kdev, int stage)
+{
+#ifdef AUDIO_ENABLED
+       unsigned long flags;
+       int do_disable = 0;
+
+       spin_lock_irqsave(&audio_lock, flags);
+
+       pr_debug("DSP power down request (audio codec %sinitialized)\n",
+               audio_ok ? "" : "not ");
+
+       if (!enable_audio)
+               goto out;
+       enable_audio = 0;
+       if (audio_ok)
+               do_disable = 1;
+out:
+       spin_unlock_irqrestore(&audio_lock, flags);
+       if (do_disable)
+               eac_set_mode(eac_device, 0, 0);
+#endif
+       return 0;
+}
+
+#endif /* CONFIG_OMAP_DSP */
diff --git a/arch/arm/mach-omap2/board-n800-bt.c b/arch/arm/mach-omap2/board-n800-bt.c
new file mode 100644 (file)
index 0000000..4ea19cc
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Nokia N800 platform-specific data for Bluetooth
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ * Contact: Ville Tervo <ville.tervo@nokia.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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <asm/arch/board.h>
+
+static struct platform_device n800_bt_device = {
+       .name           = "hci_h4p",
+       .id             = -1,
+       .num_resources  = 0,
+};
+
+void __init n800_bt_init(void)
+{
+       const struct omap_bluetooth_config *bt_config;
+
+       bt_config = (void *) omap_get_config(OMAP_TAG_NOKIA_BT,
+                                            struct omap_bluetooth_config);
+       n800_bt_device.dev.platform_data = (void *) bt_config;
+       if (platform_device_register(&n800_bt_device) < 0)
+               BUG();
+}
+
diff --git a/arch/arm/mach-omap2/board-n800-dsp.c b/arch/arm/mach-omap2/board-n800-dsp.c
new file mode 100644 (file)
index 0000000..cb57024
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * linux/arch/arm/mach-omap2/board-n800-dsp.c
+ *
+ * Copyright (C) 2006 Nokia Corporation.
+ *
+ * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/board.h>
+
+#include "../plat-omap/dsp/dsp_common.h"
+
+#if    defined(CONFIG_OMAP_DSP)
+
+/*
+ * dsp peripheral device: AUDIO
+ */
+static struct dsp_kfunc_device n800_audio_device = {
+       .name    = "audio",
+       .type    = DSP_KFUNC_DEV_TYPE_AUDIO,
+       .enable  = n800_audio_enable,
+       .disable = n800_audio_disable,
+};
+
+/*
+ * dsp peripheral device: TIMER
+ */
+static int dsp_timer_probe(struct dsp_kfunc_device *kdev)
+{
+       char clockname[20];
+
+       strcpy(clockname, kdev->name);
+       strcat(clockname, "_fck");
+
+       kdev->fck = clk_get(NULL, clockname);
+       if (IS_ERR(kdev->fck)) {
+               printk(KERN_ERR "couldn't acquire %s\n", clockname);
+               return PTR_ERR(kdev->fck);
+       }
+       pr_debug("%s probed successfully\n", clockname);
+
+       strcpy(clockname, kdev->name);
+       strcat(clockname, "_ick");
+       kdev->ick = clk_get(NULL, clockname);
+       if (IS_ERR(kdev->ick)) {
+               printk(KERN_ERR "couldn't acquire %s\n", clockname);
+               goto fail;
+       }
+       pr_debug("%s probed successfully\n", clockname);
+
+       return 0;
+ fail:
+       clk_put(kdev->fck);
+
+       return PTR_ERR(kdev->ick);
+}
+
+static int dsp_timer_remove(struct dsp_kfunc_device *kdev)
+{
+       clk_put(kdev->ick);
+       clk_put(kdev->fck);
+       pr_debug("%s removed successfully\n", kdev->name);
+       return 0;
+}
+
+static int dsp_timer_enable(struct dsp_kfunc_device *kdev, int stage)
+{
+       pr_debug("%s enabled(%d)\n", kdev->name, stage);
+
+       mutex_lock(&kdev->lock);
+
+       if (kdev->enabled)
+               goto out;
+       kdev->enabled = 1;
+
+       clk_enable(kdev->fck);
+       clk_enable(kdev->ick);
+ out:
+       mutex_unlock(&kdev->lock);
+
+       return 0;
+}
+
+static int dsp_timer_disable(struct dsp_kfunc_device *kdev, int stage)
+{
+       pr_debug("%s disabled(%d)\n", kdev->name, stage);
+
+       mutex_lock(&kdev->lock);
+
+       if (kdev->enabled == 0)
+               goto out;
+       kdev->enabled = 0;
+
+       clk_disable(kdev->ick);
+       clk_disable(kdev->fck);
+ out:
+       mutex_unlock(&kdev->lock);
+
+       return 0;
+}
+
+static struct dsp_kfunc_device n800_timer_device = {
+       .name    = "gpt5",
+       .type    = DSP_KFUNC_DEV_TYPE_COMMON,
+       .probe   = dsp_timer_probe,
+       .remove  = dsp_timer_remove,
+       .enable  = dsp_timer_enable,
+       .disable = dsp_timer_disable,
+};
+
+static struct dsp_kfunc_device *n800_kfunc_dev[] = {
+       &n800_audio_device,
+       &n800_timer_device,
+};
+
+void __init n800_dsp_init(void)
+{
+       int i, ret;
+       struct dsp_kfunc_device **p = n800_kfunc_dev;
+
+       for (i = 0; i < ARRAY_SIZE(n800_kfunc_dev); i++) {
+               ret = dsp_kfunc_device_register(p[i]);
+               if (ret) {
+                       printk(KERN_ERR
+                              "KFUNC device registration failed: %s\n",
+                              p[i]->name);
+               }
+       }
+}
+
+#else
+void __init n800_dsp_init(void) { }
+#endif /* CONFIG_OMAP_DSP */
diff --git a/arch/arm/mach-omap2/board-n800-flash.c b/arch/arm/mach-omap2/board-n800-flash.c
new file mode 100644 (file)
index 0000000..528ee52
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * linux/arch/arm/mach-omap2/board-n800-flash.c
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Juha Yrjola
+ *
+ * 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/platform_device.h>
+#include <asm/mach/flash.h>
+#include <linux/mtd/onenand_regs.h>
+
+#include <asm/io.h>
+#include <asm/arch/onenand.h>
+#include <asm/arch/board.h>
+#include <asm/arch/gpmc.h>
+
+static struct mtd_partition n800_partitions[8];
+
+static int n800_onenand_setup(void __iomem *);
+
+static struct omap_onenand_platform_data n800_onenand_data = {
+       .cs = 0,
+       .gpio_irq = 26,
+       .parts = n800_partitions,
+       .nr_parts = 0, /* filled later */
+       .onenand_setup = n800_onenand_setup,
+};
+
+static struct platform_device n800_onenand_device = {
+       .name           = "omap2-onenand",
+       .id             = -1,
+       .dev = {
+               .platform_data = &n800_onenand_data,
+       },
+};
+
+static unsigned short omap2_onenand_readw(void __iomem *addr)
+{
+       return readw(addr);
+}
+
+static void omap2_onenand_writew(unsigned short value, void __iomem *addr)
+{
+       writew(value, addr);
+}
+
+static int omap2_onenand_set_sync_mode(int cs, void __iomem *onenand_base)
+{
+       const int min_gpmc_clk_period = 18;
+       struct gpmc_timings t;
+       int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency;
+       u32 reg;
+
+       tick_ns = gpmc_round_ns_to_ticks(1);
+       div = gpmc_cs_calc_divider(cs, min_gpmc_clk_period);
+       gpmc_clk_ns = div * tick_ns;
+       if (gpmc_clk_ns >= 24)
+               latency = 3;
+       else
+               latency = 4;
+
+       /* Configure OneNAND for sync read */
+       reg = omap2_onenand_readw(onenand_base + ONENAND_REG_SYS_CFG1);
+       reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9));
+       reg |=  (latency << ONENAND_SYS_CFG1_BRL_SHIFT) |
+               ONENAND_SYS_CFG1_SYNC_READ |
+               ONENAND_SYS_CFG1_BL_16;
+       omap2_onenand_writew(reg, onenand_base + ONENAND_REG_SYS_CFG1);
+
+       /* FIXME: Get timings from platform data */
+       /* Set syncronous read timings */
+       memset(&t, 0, sizeof(t));
+       t.sync_clk = min_gpmc_clk_period;
+       t.cs_on = 0;
+       t.adv_on = gpmc_round_ns_to_ticks(7);
+       fclk_offset_ns = t.adv_on + gpmc_round_ns_to_ticks(7);
+       fclk_offset = fclk_offset_ns / gpmc_round_ns_to_ticks(1);
+       t.page_burst_access = gpmc_clk_ns;
+
+       /* Read */
+       t.adv_rd_off = fclk_offset_ns + gpmc_round_ns_to_ticks(7);
+       t.oe_on = t.adv_rd_off;
+       t.access = fclk_offset_ns + (latency + 1) * gpmc_clk_ns;
+       t.oe_off = t.access + gpmc_round_ns_to_ticks(1);
+       t.cs_rd_off = t.oe_off;
+       t.rd_cycle = t.cs_rd_off + gpmc_round_ns_to_ticks(17);
+
+       /* Write */
+       t.adv_wr_off = t.adv_on + gpmc_round_ns_to_ticks(12);
+       t.we_on = t.adv_wr_off + gpmc_round_ns_to_ticks(1);
+       t.we_off = t.we_on + gpmc_round_ns_to_ticks(40);
+       t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(1);
+       t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(1);
+
+       /* Configure GPMC for synchronous read */
+       fclk_offset %= div;
+       gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1,
+                         GPMC_CONFIG1_WRAPBURST_SUPP |
+                         GPMC_CONFIG1_READMULTIPLE_SUPP |
+                         GPMC_CONFIG1_READTYPE_SYNC |
+                         GPMC_CONFIG1_CLKACTIVATIONTIME(fclk_offset) |
+                         GPMC_CONFIG1_PAGE_LEN(2) |
+                         GPMC_CONFIG1_WAIT_READ_MON |
+                         GPMC_CONFIG1_WAIT_PIN_SEL(0) |
+                         GPMC_CONFIG1_DEVICESIZE_16 |
+                         GPMC_CONFIG1_DEVICETYPE_NOR |
+                         GPMC_CONFIG1_MUXADDDATA);
+
+       return gpmc_cs_set_timings(cs, &t);
+}
+
+static int n800_onenand_setup(void __iomem *onenand_base)
+{
+       struct omap_onenand_platform_data *datap = &n800_onenand_data;
+       struct device *dev = &n800_onenand_device.dev;
+
+       /* Set sync timings in GPMC */
+       if (omap2_onenand_set_sync_mode(datap->cs, onenand_base) < 0) {
+               dev_err(dev, "Unable to set synchronous mode\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+void __init n800_flash_init(void)
+{
+       const struct omap_partition_config *part;
+       int i = 0;
+
+       while ((part = omap_get_nr_config(OMAP_TAG_PARTITION,
+                               struct omap_partition_config, i)) != NULL) {
+               struct mtd_partition *mpart;
+
+               mpart = n800_partitions + i;
+               mpart->name = (char *) part->name;
+               mpart->size = part->size;
+               mpart->offset = part->offset;
+               mpart->mask_flags = part->mask_flags;
+               i++;
+               if (i == ARRAY_SIZE(n800_partitions)) {
+                       printk(KERN_ERR "Too many partitions supplied\n");
+                       return;
+               }
+       }
+       n800_onenand_data.nr_parts = i;
+       if (platform_device_register(&n800_onenand_device) < 0) {
+               printk(KERN_ERR "Unable to register OneNAND device\n");
+               return;
+       }
+}
diff --git a/arch/arm/mach-omap2/board-n800-mmc.c b/arch/arm/mach-omap2/board-n800-mmc.c
new file mode 100644 (file)
index 0000000..b0bdd97
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * linux/arch/arm/mach-omap2/board-n800-mmc.c
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Juha Yrjola
+ *
+ * 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 <asm/arch/mmc.h>
+#include <asm/arch/menelaus.h>
+#include <asm/arch/gpio.h>
+
+#ifdef CONFIG_MMC_OMAP
+
+static const int slot_switch_gpio = 96;
+static const int slot1_wp_gpio = 23;
+static const int slot2_wp_gpio = 8;
+static int slot1_cover_closed;
+static int slot2_cover_closed;
+static struct device *mmc_device;
+
+/*
+ * VMMC --> slot 1
+ * VDCDC3_APE, VMCS2_APE --> slot 2
+ * GPIO96 --> Menelaus GPIO2
+ */
+
+static int n800_mmc_switch_slot(struct device *dev, int slot)
+{
+#ifdef CONFIG_MMC_DEBUG
+       dev_dbg(dev, "Choose slot %d\n", slot + 1);
+#endif
+       if (slot == 0)
+               omap_set_gpio_dataout(slot_switch_gpio, 0);
+       else
+               omap_set_gpio_dataout(slot_switch_gpio, 1);
+       return 0;
+}
+
+static int n800_mmc_set_power(struct device *dev, int slot, int power_on,
+                               int vdd)
+{
+       int mV;
+
+#ifdef CONFIG_MMC_DEBUG
+       dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1,
+               power_on ? "on" : "off", vdd);
+#endif
+       if (slot == 0) {
+               if (!power_on)
+                       return menelaus_set_vmmc(0);
+               switch (1 << vdd) {
+               case MMC_VDD_33_34:
+               case MMC_VDD_32_33:
+               case MMC_VDD_31_32:
+                       mV = 3100;
+                       break;
+               case MMC_VDD_30_31:
+                       mV = 3000;
+                       break;
+               case MMC_VDD_28_29:
+                       mV = 2800;
+                       break;
+               case MMC_VDD_18_19:
+                       mV = 1850;
+                       break;
+               default:
+                       BUG();
+               }
+               return menelaus_set_vmmc(mV);
+       } else {
+               if (!power_on)
+                       return menelaus_set_vdcdc(3, 0);
+               switch (1 << vdd) {
+               case MMC_VDD_33_34:
+               case MMC_VDD_32_33:
+                       mV = 3300;
+                       break;
+               case MMC_VDD_30_31:
+               case MMC_VDD_29_30:
+                       mV = 3000;
+                       break;
+               case MMC_VDD_28_29:
+               case MMC_VDD_27_28:
+                       mV = 2800;
+                       break;
+               case MMC_VDD_24_25:
+               case MMC_VDD_23_24:
+                       mV = 2400;
+                       break;
+               case MMC_VDD_22_23:
+               case MMC_VDD_21_22:
+                       mV = 2200;
+                       break;
+               case MMC_VDD_20_21:
+               case MMC_VDD_19_20:
+                       mV = 2000;
+                       break;
+               case MMC_VDD_18_19:
+               case MMC_VDD_17_18:
+                       mV = 1800;
+                       break;
+               case MMC_VDD_150_155:
+               case MMC_VDD_145_150:
+                       mV = 1500;
+                       break;
+               default:
+                       BUG();
+               }
+               return menelaus_set_vdcdc(3, mV);
+       }
+       return 0;
+}
+
+static int n800_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode)
+{
+       int r;
+
+#ifdef CONFIG_MMC_DEBUG
+       dev_dbg(dev, "Set slot %d bus mode %s\n", slot + 1,
+               bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull");
+#endif
+       BUG_ON(slot != 0 && slot != 1);
+       slot++;
+       switch (bus_mode) {
+       case MMC_BUSMODE_OPENDRAIN:
+               r = menelaus_set_mmc_opendrain(slot, 1);
+               break;
+       case MMC_BUSMODE_PUSHPULL:
+               r = menelaus_set_mmc_opendrain(slot, 0);
+               break;
+       default:
+               BUG();
+       }
+       if (r != 0 && printk_ratelimit())
+               dev_err(dev, "MMC: unable to set bus mode for slot %d\n",
+                       slot);
+       return r;
+}
+
+#if 0
+static int n800_mmc_get_ro(struct device *dev, int slot)
+{
+       int ro;
+
+       slot++;
+       if (slot == 1)
+               ro = omap_get_gpio_datain(slot1_wp_gpio);
+       else
+               ro = omap_get_gpio_datain(slot2_wp_gpio);
+#ifdef CONFIG_MMC_DEBUG
+       dev_dbg(dev, "Get RO slot %d: %s\n",
+               slot, ro ? "read-only" : "read-write");
+#endif
+       return ro;
+}
+#endif
+
+static int n800_mmc_get_cover_state(struct device *dev, int slot)
+{
+       slot++;
+       BUG_ON(slot != 1 && slot != 2);
+       if (slot == 1)
+               return slot1_cover_closed;
+       else
+               return slot2_cover_closed;
+}
+
+static void n800_mmc_callback(void *data, u8 card_mask)
+{
+       if (card_mask & (1 << 1))
+               slot2_cover_closed = 0;
+       else
+               slot2_cover_closed = 1;
+        omap_mmc_notify_cover_event(mmc_device, 1, slot2_cover_closed);
+}
+
+void n800_mmc_slot1_cover_handler(void *arg, int state)
+{
+       if (mmc_device == NULL)
+               return;
+
+       slot1_cover_closed = state;
+       omap_mmc_notify_cover_event(mmc_device, 0, state);
+}
+
+static int n800_mmc_late_init(struct device *dev)
+{
+       int r;
+
+       mmc_device = dev;
+
+       r = menelaus_set_slot_sel(1);
+       if (r < 0)
+               return r;
+
+       r = menelaus_set_mmc_slot(1, 1, 0, 1);
+       if (r < 0)
+               return r;
+       r = menelaus_set_mmc_slot(2, 1, 0, 1);
+       if (r < 0)
+               return r;
+
+       r = menelaus_get_slot_pin_states();
+       if (r < 0)
+               return r;
+
+       if (r & (1 << 1))
+               slot2_cover_closed = 1;
+       else
+               slot2_cover_closed = 0;
+
+       r = menelaus_register_mmc_callback(n800_mmc_callback, NULL);
+
+       return r;
+}
+
+static void n800_mmc_cleanup(struct device *dev)
+{
+       menelaus_unregister_mmc_callback();
+}
+
+static struct omap_mmc_platform_data n800_mmc_data = {
+       .enabled                = 1,
+       .nr_slots               = 2,
+       .wire4                  = 1,
+       .switch_slot            = n800_mmc_switch_slot,
+       .init                   = n800_mmc_late_init,
+       .cleanup                = n800_mmc_cleanup,
+       .slots[0] = {
+               .set_power      = n800_mmc_set_power,
+               .set_bus_mode   = n800_mmc_set_bus_mode,
+               .get_ro         = NULL,
+               .get_cover_state= n800_mmc_get_cover_state,
+               .ocr_mask       = MMC_VDD_18_19 | MMC_VDD_28_29 | MMC_VDD_30_31 |
+                                 MMC_VDD_32_33 | MMC_VDD_33_34,
+               .name           = "internal",
+       },
+       .slots[1] = {
+               .set_power      = n800_mmc_set_power,
+               .set_bus_mode   = n800_mmc_set_bus_mode,
+               .get_ro         = NULL,
+               .get_cover_state= n800_mmc_get_cover_state,
+               .ocr_mask       = MMC_VDD_150_155 | MMC_VDD_145_150 | MMC_VDD_17_18 |
+                                 MMC_VDD_18_19 | MMC_VDD_19_20 | MMC_VDD_20_21 |
+                                 MMC_VDD_21_22 | MMC_VDD_22_23 | MMC_VDD_23_24 |
+                                 MMC_VDD_24_25 | MMC_VDD_27_28 | MMC_VDD_28_29 |
+                                 MMC_VDD_29_30 | MMC_VDD_30_31 | MMC_VDD_32_33 |
+                                 MMC_VDD_33_34,
+               .name           = "external",
+       },
+};
+
+void __init n800_mmc_init(void)
+{
+       omap_set_mmc_info(1, &n800_mmc_data);
+       if (omap_request_gpio(slot_switch_gpio) < 0)
+               BUG();
+       omap_set_gpio_dataout(slot_switch_gpio, 0);
+       omap_set_gpio_direction(slot_switch_gpio, 0);
+       if (omap_request_gpio(slot1_wp_gpio) < 0)
+               BUG();
+       if (omap_request_gpio(slot2_wp_gpio) < 0)
+               BUG();
+       omap_set_gpio_direction(slot1_wp_gpio, 1);
+       omap_set_gpio_direction(slot2_wp_gpio, 1);
+}
+
+#else
+
+void __init n800_mmc_init(void)
+{
+}
+
+void n800_mmc_slot1_cover_handler(void *arg, int state)
+{
+}
+
+#endif
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge