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>
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
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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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();
+}
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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