From: Kai Svahn Date: Fri, 26 Jan 2007 20:39:48 +0000 (-0800) Subject: ARM: OMAP: Merge board specific files from N800 tree X-Git-Tag: v2.6.23-omap1~285^2~2 X-Git-Url: http://git.openpandora.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7275ab4e5db3cface7068e8e58931b3077386877;p=pandora-kernel.git ARM: OMAP: Merge board specific files from N800 tree 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 Signed-off-by: Tony Lindgren --- diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index c7507adb5fb7..752dd6e75826 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -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 diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 036f4eee4b70..4cccb4c6e90b 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -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 index 000000000000..9033de583197 --- /dev/null +++ b/arch/arm/mach-omap2/board-n800-audio.c @@ -0,0 +1,366 @@ +/* + * linux/arch/arm/mach-omap2/board-n800-audio.c + * + * Copyright (C) 2006 Nokia Corporation + * Contact: Juha Yrjola + * Jarkko Nikula + * + * 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 +#include +#include +#include + +#include +#include + +#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 index 000000000000..4ea19cca83f9 --- /dev/null +++ b/arch/arm/mach-omap2/board-n800-bt.c @@ -0,0 +1,42 @@ +/* + * Nokia N800 platform-specific data for Bluetooth + * + * Copyright (C) 2005, 2006 Nokia Corporation + * Contact: Ville Tervo + * + * 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 +#include +#include + +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 index 000000000000..cb570243dc59 --- /dev/null +++ b/arch/arm/mach-omap2/board-n800-dsp.c @@ -0,0 +1,156 @@ +/* + * linux/arch/arm/mach-omap2/board-n800-dsp.c + * + * Copyright (C) 2006 Nokia Corporation. + * + * Contact: Hiroshi DOYU + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#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 index 000000000000..528ee52a3773 --- /dev/null +++ b/arch/arm/mach-omap2/board-n800-flash.c @@ -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 +#include +#include +#include + +#include +#include +#include +#include + +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 index 000000000000..b0bdd97ea535 --- /dev/null +++ b/arch/arm/mach-omap2/board-n800-mmc.c @@ -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 +#include +#include + +#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 Reading git-diff-tree failed