From d3048278388b0908e179882587a8b6fcd95f2f50 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Sun, 4 Oct 2009 13:47:44 +0200
Subject: [PATCH 15/69] Add glamo driver.

---
 drivers/gpio/Kconfig           |    6 +
 drivers/gpio/Makefile          |    1 +
 drivers/gpio/glamo-gpio.c      |  250 ++++++++
 drivers/mfd/Kconfig            |    8 +
 drivers/mfd/Makefile           |    2 +
 drivers/mfd/glamo-core.c       | 1274 ++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/Kconfig       |   11 +
 drivers/mmc/host/Makefile      |    1 +
 drivers/mmc/host/glamo-mci.c   |  939 +++++++++++++++++++++++++++++
 drivers/video/Kconfig          |   17 +
 drivers/video/Makefile         |    1 +
 drivers/video/glamo-fb.c       |  943 +++++++++++++++++++++++++++++
 include/linux/fb.h             |    1 +
 include/linux/glamofb.h        |   21 +
 include/linux/mfd/glamo-core.h |   58 ++
 include/linux/mfd/glamo-regs.h |  630 ++++++++++++++++++++
 include/linux/mfd/glamo.h      |   53 ++
 17 files changed, 4216 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpio/glamo-gpio.c
 create mode 100644 drivers/mfd/glamo-core.c
 create mode 100644 drivers/mmc/host/glamo-mci.c
 create mode 100644 drivers/video/glamo-fb.c
 create mode 100644 include/linux/glamofb.h
 create mode 100644 include/linux/mfd/glamo-core.h
 create mode 100644 include/linux/mfd/glamo-regs.h
 create mode 100644 include/linux/mfd/glamo.h

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index d3b2953..2265ee6 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -75,6 +75,12 @@ config GPIO_BASIC_MMIO
 	help
 	  Say yes here to support basic memory-mapped GPIO controllers.
 
+config GPIO_GLAMO
+	tristate "Glamo GPIO support"
+	depends on MFD_GLAMO
+	help
+	  Say yes here to support GPIO functionality of the Smedia Glamo.
+
 config GPIO_IT8761E
 	tristate "IT8761E GPIO support"
 	depends on GPIOLIB
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index becef59..8b748db 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_GPIOLIB)		+= gpiolib.o
 obj-$(CONFIG_GPIO_ADP5520)	+= adp5520-gpio.o
 obj-$(CONFIG_GPIO_ADP5588)	+= adp5588-gpio.o
 obj-$(CONFIG_GPIO_BASIC_MMIO)	+= basic_mmio_gpio.o
+obj-$(CONFIG_GPIO_GLAMO)	+= glamo-gpio.o
 obj-$(CONFIG_GPIO_LANGWELL)	+= langwell_gpio.o
 obj-$(CONFIG_GPIO_MAX730X)	+= max730x.o
 obj-$(CONFIG_GPIO_MAX7300)	+= max7300.o
diff --git a/drivers/gpio/glamo-gpio.c b/drivers/gpio/glamo-gpio.c
new file mode 100644
index 0000000..1e9986b
--- /dev/null
+++ b/drivers/gpio/glamo-gpio.c
@@ -0,0 +1,250 @@
+/* Smedia Glamo 336x/337x gpio driver
+ *
+ * (C) 2009,2011 Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <linux/gpio.h>
+#include <linux/mfd/glamo.h>
+#include <linux/mfd/glamo-core.h>
+#include <linux/mfd/glamo-regs.h>
+
+#define GLAMO_NR_GPIO 21
+#define GLAMO_NR_GPIO_REGS DIV_ROUND_UP(GLAMO_NR_GPIO, 4)
+
+#define GLAMO_GPIO_REG(x) (((x) * 2) + GLAMO_REG_GPIO_GEN1)
+
+struct glamo_gpio {
+	struct glamo_core *glamo;
+	struct gpio_chip chip;
+	uint16_t saved_regs[GLAMO_NR_GPIO_REGS];
+};
+
+#define GLAMO_GPIO_REG_GPIO(gpio)	GLAMO_GPIO_REG((gpio) >> 2)
+#define GLAMO_GPIO_BIT(gpio, offset)	BIT(((gpio) & 0x3) + (offset))
+#define GLAMO_GPIO_DIRECTION_BIT(gpio)	GLAMO_GPIO_BIT((gpio), 0)
+#define GLAMO_GPIO_OUTPUT_BIT(gpio)	GLAMO_GPIO_BIT((gpio), 4)
+#define GLAMO_GPIO_INPUT_BIT(gpio)	GLAMO_GPIO_BIT((gpio), 8)
+#define GLAMO_GPIO_FUNC_BIT(gpio)	GLAMO_GPIO_BIT((gpio), 12)
+
+
+static inline struct glamo_core *chip_to_glamo(struct gpio_chip *chip)
+{
+	return container_of(chip, struct glamo_gpio, chip)->glamo;
+}
+
+static void glamo_gpio_modify_reg(struct gpio_chip *chip, unsigned offset,
+    uint16_t set, uint16_t clear)
+{
+	struct glamo_core *glamo = chip_to_glamo(chip);
+	void __iomem *reg = glamo->base + GLAMO_GPIO_REG_GPIO(offset);
+	uint16_t tmp;
+
+	spin_lock(&glamo->lock);
+	tmp = readw(reg);
+	tmp |= set;
+	tmp &= ~clear;
+	writew(tmp, reg);
+	spin_unlock(&glamo->lock);
+}
+
+static void glamo_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	uint16_t mask = GLAMO_GPIO_OUTPUT_BIT(offset);
+	glamo_gpio_modify_reg(chip, offset, value ? mask : 0, value ? 0 : mask);
+}
+
+static int glamo_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct glamo_core *glamo = chip_to_glamo(chip);
+	void __iomem *reg = glamo->base + GLAMO_GPIO_REG_GPIO(offset);
+
+	return !!(readw(reg) & GLAMO_GPIO_INPUT_BIT(offset));
+}
+
+static int glamo_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	glamo_gpio_modify_reg(chip, offset, GLAMO_GPIO_FUNC_BIT(offset), 0);
+
+	return 0;
+}
+
+static void glamo_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	glamo_gpio_modify_reg(chip, offset, 0, GLAMO_GPIO_FUNC_BIT(offset));
+}
+
+static int glamo_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+				       int value)
+{
+	uint16_t output_mask = GLAMO_GPIO_OUTPUT_BIT(offset);
+	uint16_t set, clear;
+
+	clear = GLAMO_GPIO_DIRECTION_BIT(offset);
+
+	if (value) {
+		set = output_mask;
+	} else {
+		set = 0;
+		clear |= output_mask;
+	}
+
+	glamo_gpio_modify_reg(chip, offset, set, clear);
+
+	return 0;
+}
+
+static int glamo_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	glamo_gpio_modify_reg(chip, offset, GLAMO_GPIO_DIRECTION_BIT(offset), 0);
+
+	return 0;
+}
+
+static const struct __devinit gpio_chip glamo_gpio_chip = {
+	.label			= "glamo",
+	.request		= glamo_gpio_request,
+	.free			= glamo_gpio_free,
+	.direction_input	= glamo_gpio_direction_input,
+	.direction_output	= glamo_gpio_direction_output,
+	.get			= glamo_gpio_get,
+	.set			= glamo_gpio_set,
+	.base			= -1,
+	.ngpio			= GLAMO_NR_GPIO,
+	.owner			= THIS_MODULE,
+};
+
+static int __devinit glamo_gpio_probe(struct platform_device *pdev)
+{
+	struct glamo_platform_data *pdata = pdev->dev.parent->platform_data;
+	struct glamo_gpio *glamo_gpio;
+	int ret;
+
+	glamo_gpio = kzalloc(sizeof(*glamo_gpio), GFP_KERNEL);
+	if (!glamo_gpio)
+		return -ENOMEM;
+
+	glamo_gpio->glamo = dev_get_drvdata(pdev->dev.parent);
+	glamo_gpio->chip = glamo_gpio_chip;
+	glamo_gpio->chip.dev = &pdev->dev;
+	if (pdata && pdata->gpio_data)
+		glamo_gpio->chip.base = pdata->gpio_data->base;
+
+	ret = gpiochip_add(&glamo_gpio->chip);
+
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register gpio chip: %d\n", ret);
+		goto err;
+	}
+
+	platform_set_drvdata(pdev, glamo_gpio);
+
+	return 0;
+err:
+	kfree(glamo_gpio);
+	return ret;
+}
+
+static int __devexit glamo_gpio_remove(struct platform_device *pdev)
+{
+	struct glamo_gpio *glamo_gpio = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = gpiochip_remove(&glamo_gpio->chip);
+	if (!ret)
+		goto done;
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(glamo_gpio);
+
+done:
+	return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int glamo_gpio_suspend(struct device *dev)
+{
+	struct glamo_gpio *glamo_gpio = dev_get_drvdata(dev);
+	struct glamo_core *glamo = glamo_gpio->glamo;
+	uint16_t *saved_regs = glamo_gpio->saved_regs;
+	int i;
+
+	spin_lock(&glamo->lock);
+	for (i = 0; i < GLAMO_NR_GPIO_REGS; ++i)
+		saved_regs[i] = readw(glamo->base + GLAMO_GPIO_REG(i));
+	spin_unlock(&glamo->lock);
+
+	return 0;
+}
+
+static int glamo_gpio_resume(struct device *dev)
+{
+	struct glamo_gpio *glamo_gpio = dev_get_drvdata(dev);
+	struct glamo_core *glamo = glamo_gpio->glamo;
+	uint16_t *saved_regs = glamo_gpio->saved_regs;
+	int i;
+
+	spin_lock(&glamo->lock);
+	for (i = 0; i < GLAMO_NR_GPIO_REGS; ++i)
+		writew(saved_regs[i], glamo->base + GLAMO_GPIO_REG(i));
+	spin_unlock(&glamo->lock);
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(glamo_pm_ops, glamo_gpio_suspend, glamo_gpio_resume);
+
+#define GLAMO_GPIO_PM_OPS (&glamo_pm_ops)
+
+#else
+#define GLAMO_GPIO_PM_OPS NULL
+#endif
+
+static struct platform_driver glamo_gpio_driver = {
+	.probe = glamo_gpio_probe,
+	.remove = __devexit_p(glamo_gpio_remove),
+	.driver = {
+		.name  = "glamo-gpio",
+		.owner = THIS_MODULE,
+		.pm    = GLAMO_GPIO_PM_OPS,
+	},
+};
+
+static int __init glamo_gpio_init(void)
+{
+	return platform_driver_register(&glamo_gpio_driver);
+}
+module_init(glamo_gpio_init);
+
+static void __exit glamo_gpio_exit(void)
+{
+	platform_driver_unregister(&glamo_gpio_driver);
+}
+module_exit(glamo_gpio_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("GPIO interface for the Glamo multimedia device");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:glamo-gpio");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3ed3ff0..a04f607 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -602,6 +602,14 @@ config LPC_SCH
 	  LPC bridge function of the Intel SCH provides support for
 	  System Management Bus and General Purpose I/O.
 
+config MFD_GLAMO
+	bool "Smedia Glamo 336x/337x support"
+	select MFD_CORE
+	help
+	  This enables the core driver for the Smedia Glamo 336x/337x
+	  multi-function device.  It includes irq_chip demultiplex as
+	  well as clock / power management and GPIO support.
+
 config MFD_RDC321X
 	tristate "Support for RDC-R321x southbridge"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 419caa9..26d7b45 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -6,6 +6,8 @@
 obj-$(CONFIG_MFD_88PM860X)	+= 88pm860x.o
 obj-$(CONFIG_MFD_SM501)		+= sm501.o
 obj-$(CONFIG_MFD_ASIC3)		+= asic3.o tmio_core.o
+obj-$(CONFIG_MFD_SH_MOBILE_SDHI)		+= sh_mobile_sdhi.o
+obj-$(CONFIG_MFD_GLAMO)		+= glamo-core.o
 
 obj-$(CONFIG_HTC_EGPIO)		+= htc-egpio.o
 obj-$(CONFIG_HTC_PASIC3)	+= htc-pasic3.o
diff --git a/drivers/mfd/glamo-core.c b/drivers/mfd/glamo-core.c
new file mode 100644
index 0000000..c9b2ae5
--- /dev/null
+++ b/drivers/mfd/glamo-core.c
@@ -0,0 +1,1274 @@
+/* Smedia Glamo 3362 driver
+ *
+ * (C) 2007 by Openmoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ * (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/kernel_stat.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/glamo.h>
+#include <linux/mfd/glamo-regs.h>
+#include <linux/mfd/glamo-core.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+#include <linux/pm.h>
+
+#define GLAMO_MEM_REFRESH_COUNT 0x100
+
+#define GLAMO_NR_IRQS 9
+
+#define GLAMO_IRQ_HOSTBUS	0
+#define GLAMO_IRQ_JPEG		1
+#define GLAMO_IRQ_MPEG		2
+#define GLAMO_IRQ_MPROC1	3
+#define GLAMO_IRQ_MPROC0	4
+#define GLAMO_IRQ_CMDQUEUE	5
+#define GLAMO_IRQ_2D		6
+#define GLAMO_IRQ_MMC		7
+#define GLAMO_IRQ_RISC		8
+
+/*
+ * Glamo internal settings
+ *
+ * We run the memory interface from the faster PLLB on 2.6.28 kernels and
+ * above.  Couple of GTA02 users report trouble with memory bus when they
+ * upgraded from 2.6.24.  So this parameter allows reversion to 2.6.24
+ * scheme if their Glamo chip needs it.
+ *
+ * you can override the faster default on kernel commandline using
+ *
+ *   glamo3362.slow_memory=1
+ *
+ * for example
+ */
+
+static int slow_memory;
+module_param(slow_memory, int, 0644);
+
+struct reg_range {
+	int start;
+	int count;
+	char *name;
+	unsigned dump:1;
+};
+
+static const struct reg_range reg_range[] = {
+	{ 0x0000, 0x76,		"General",	1 },
+	{ 0x0200, 0x18,		"Host Bus",	1 },
+	{ 0x0300, 0x38,		"Memory",	1 },
+/*	{ 0x0400, 0x100,	"Sensor",	0 }, */
+/*	{ 0x0500, 0x300,	"ISP",		0 }, */
+/*	{ 0x0800, 0x400,	"JPEG",		0 }, */
+/*	{ 0x0c00, 0xcc,		"MPEG",		0 }, */
+	{ 0x1100, 0xb2,		"LCD 1",	0 },
+	{ 0x1200, 0x64,		"LCD 2",	0 },
+	{ 0x1400, 0x42,		"MMC",		0 },
+/*	{ 0x1500, 0x080,	"MPU 0",	0 },
+	{ 0x1580, 0x080,	"MPU 1",	0 },
+	{ 0x1600, 0x080,	"Cmd Queue",	0 },
+	{ 0x1680, 0x080,	"RISC CPU",	0 },*/
+	{ 0x1700, 0x400,	"2D Unit",	0 },
+/*	{ 0x1b00, 0x900,	"3D Unit",	0 }, */
+};
+
+static inline void __reg_write(struct glamo_core *glamo,
+				uint16_t reg, uint16_t val)
+{
+	writew(val, glamo->base + reg);
+}
+
+void glamo_reg_write(struct glamo_core *glamo,
+				uint16_t reg, uint16_t val)
+{
+	spin_lock(&glamo->lock);
+	__reg_write(glamo, reg, val);
+	spin_unlock(&glamo->lock);
+}
+EXPORT_SYMBOL_GPL(glamo_reg_write);
+
+
+static inline uint16_t __reg_read(struct glamo_core *glamo,
+					uint16_t reg)
+{
+	return readw(glamo->base + reg);
+}
+
+uint16_t glamo_reg_read(struct glamo_core *glamo, uint16_t reg)
+{
+	uint16_t val;
+	spin_lock(&glamo->lock);
+	val = __reg_read(glamo, reg);
+	spin_unlock(&glamo->lock);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(glamo_reg_read);
+
+static void __reg_set_bit_mask(struct glamo_core *glamo,
+				uint16_t reg, uint16_t mask,
+				uint16_t val)
+{
+	uint16_t tmp;
+
+	val &= mask;
+
+	tmp = __reg_read(glamo, reg);
+	tmp &= ~mask;
+	tmp |= val;
+	__reg_write(glamo, reg, tmp);
+}
+
+static void reg_set_bit_mask(struct glamo_core *glamo,
+				uint16_t reg, uint16_t mask,
+				uint16_t val)
+{
+	spin_lock(&glamo->lock);
+	__reg_set_bit_mask(glamo, reg, mask, val);
+	spin_unlock(&glamo->lock);
+}
+
+static inline void __reg_set_bit(struct glamo_core *glamo,
+				 uint16_t reg, uint16_t bit)
+{
+	uint16_t tmp;
+	tmp = __reg_read(glamo, reg);
+	tmp |= bit;
+	__reg_write(glamo, reg, tmp);
+}
+
+static inline void __reg_clear_bit(struct glamo_core *glamo,
+					uint16_t reg, uint16_t bit)
+{
+	uint16_t tmp;
+	tmp = __reg_read(glamo, reg);
+	tmp &= ~bit;
+	__reg_write(glamo, reg, tmp);
+}
+
+/***********************************************************************
+ * resources of sibling devices
+ ***********************************************************************/
+
+static struct resource glamo_fb_resources[] = {
+	{
+		.name	= "glamo-fb-regs",
+		.start	= GLAMO_REGOFS_LCD,
+		.end	= GLAMO_REGOFS_MMC - 1,
+		.flags	= IORESOURCE_MEM,
+	}, {
+		.name	= "glamo-fb-mem",
+		.start	= GLAMO_OFFSET_FB,
+		.end	= GLAMO_OFFSET_FB + GLAMO_FB_SIZE - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct resource glamo_mmc_resources[] = {
+	{
+		.name	= "glamo-mmc-regs",
+		.start	= GLAMO_REGOFS_MMC,
+		.end	= GLAMO_REGOFS_MPROC0 - 1,
+		.flags	= IORESOURCE_MEM
+	}, {
+		.name	= "glamo-mmc-mem",
+		.start	= GLAMO_OFFSET_FB + GLAMO_FB_SIZE,
+		.end	= GLAMO_OFFSET_FB + GLAMO_FB_SIZE +
+				  GLAMO_MMC_BUFFER_SIZE - 1,
+		.flags	= IORESOURCE_MEM
+	}, {
+		.start	= GLAMO_IRQ_MMC,
+		.end	= GLAMO_IRQ_MMC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+enum glamo_cells {
+	GLAMO_CELL_FB,
+	GLAMO_CELL_MMC,
+	GLAMO_CELL_GPIO,
+};
+
+static const struct mfd_cell glamo_cells[] = {
+	[GLAMO_CELL_FB] = {
+		.name = "glamo-fb",
+		.num_resources = ARRAY_SIZE(glamo_fb_resources),
+		.resources = glamo_fb_resources,
+	},
+	[GLAMO_CELL_MMC] = {
+		.name = "glamo-mci",
+		.num_resources = ARRAY_SIZE(glamo_mmc_resources),
+		.resources = glamo_mmc_resources,
+	},
+	[GLAMO_CELL_GPIO] = {
+		.name = "glamo-gpio",
+	},
+};
+
+/***********************************************************************
+ * IRQ demultiplexer
+ ***********************************************************************/
+static inline unsigned int glamo_irq_bit(struct glamo_core *glamo,
+	struct irq_data *data)
+{
+	return BIT(data->irq - glamo->irq_base);
+}
+
+static void glamo_ack_irq(struct irq_data *data)
+{
+	struct glamo_core *glamo = irq_data_get_irq_chip_data(data);
+	/* clear interrupt source */
+	__reg_write(glamo, GLAMO_REG_IRQ_CLEAR, glamo_irq_bit(glamo, data));
+}
+
+static void glamo_mask_irq(struct irq_data *data)
+{
+	struct glamo_core *glamo = irq_data_get_irq_chip_data(data);
+
+	/* clear bit in enable register */
+	__reg_clear_bit(glamo, GLAMO_REG_IRQ_ENABLE, glamo_irq_bit(glamo, data));
+}
+
+static void glamo_unmask_irq(struct irq_data *data)
+{
+	struct glamo_core *glamo = irq_data_get_irq_chip_data(data);
+
+	/* set bit in enable register */
+	__reg_set_bit(glamo, GLAMO_REG_IRQ_ENABLE, glamo_irq_bit(glamo, data));
+}
+
+static struct irq_chip glamo_irq_chip = {
+	.name		= "glamo",
+	.irq_ack	= glamo_ack_irq,
+	.irq_mask	= glamo_mask_irq,
+	.irq_unmask	= glamo_unmask_irq,
+};
+
+static irqreturn_t glamo_irq_handler(int irq, void *devid)
+{
+	struct glamo_core *glamo = devid;
+	uint16_t irqstatus;
+	int i;
+
+	irqstatus = __reg_read(glamo, GLAMO_REG_IRQ_STATUS);
+	for (i = 0; i < 9; ++i) {
+		if (irqstatus & BIT(i))
+			generic_handle_irq(glamo->irq_base + i);
+	}
+
+	return IRQ_HANDLED;
+}
+
+struct glamo_engine_reg_set {
+	uint16_t reg;
+	uint16_t mask_suspended;
+	uint16_t mask_enabled;
+};
+
+/*
+debugfs
+*/
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t debugfs_regs_write(struct file *file,
+				  const char __user *user_buf,
+				  size_t count, loff_t *ppos)
+{
+	struct glamo_core *glamo = ((struct seq_file *)file->private_data)->private;
+	char buf[14];
+	unsigned int reg;
+	unsigned int val;
+	int buf_size;
+
+	buf_size = min(count, sizeof(buf) - 1);
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+	if (sscanf(buf, "%x %x", &reg, &val) != 2)
+		return -EFAULT;
+
+	dev_info(&glamo->pdev->dev, "reg %#02x <-- %#04x\n", reg, val);
+
+	glamo_reg_write(glamo, reg, val);
+
+	return count;
+}
+
+static int glamo_show_regs(struct seq_file *s, void *pos)
+{
+	struct glamo_core *glamo = s->private;
+	const struct reg_range *rr = reg_range;
+	int i, n;
+
+	spin_lock(&glamo->lock);
+	for (i = 0; i < ARRAY_SIZE(reg_range); ++i, ++rr) {
+		if (!rr->dump)
+			continue;
+		seq_printf(s, "\n%s\n", rr->name);
+		for (n = rr->start; n < rr->start + rr->count; n += 2) {
+			if ((n & 15) == 0)
+				seq_printf(s, "\n%04X:  ", n);
+			seq_printf(s, "%04x ", __reg_read(glamo, n));
+		}
+		seq_printf(s, "\n");
+	}
+	spin_unlock(&glamo->lock);
+
+	return 0;
+}
+
+static int debugfs_open_file(struct inode *inode, struct file *file)
+{
+	return single_open(file, glamo_show_regs, inode->i_private);
+}
+
+static const struct file_operations debugfs_regs_ops = {
+	.open		= debugfs_open_file,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.write		= debugfs_regs_write,
+	.release	= single_release,
+};
+
+static void glamo_init_debugfs(struct glamo_core *glamo)
+{
+	glamo->debugfs_dir = debugfs_create_dir("glamo3362", NULL);
+	if (glamo->debugfs_dir)
+		debugfs_create_file("regs", S_IRUGO | S_IWUSR,
+				    glamo->debugfs_dir,
+				    glamo, &debugfs_regs_ops);
+	else
+		dev_warn(&glamo->pdev->dev, "Failed to set up debugfs.\n");
+}
+
+static void glamo_exit_debugfs(struct glamo_core *glamo)
+{
+	if (glamo->debugfs_dir)
+		debugfs_remove_recursive(glamo->debugfs_dir);
+}
+#else
+static void glamo_init_debugfs(struct glamo_core *glamo)
+{
+}
+
+static void glamo_exit_debugfs(struct glamo_core *glamo)
+{
+}
+#endif
+
+struct glamo_engine_desc {
+	const char *name;
+	uint16_t hostbus;
+	const struct glamo_engine_reg_set *regs;
+	size_t num_regs;
+};
+
+static const struct glamo_engine_reg_set glamo_lcd_regs[] = {
+	{ GLAMO_REG_CLOCK_LCD,
+	GLAMO_CLOCK_LCD_EN_M5CLK |
+	GLAMO_CLOCK_LCD_DG_M5CLK |
+	GLAMO_CLOCK_LCD_EN_DMCLK,
+
+	GLAMO_CLOCK_LCD_EN_DHCLK |
+	GLAMO_CLOCK_LCD_EN_DCLK
+	},
+	{ GLAMO_REG_CLOCK_GEN5_1,
+	GLAMO_CLOCK_GEN51_EN_DIV_DMCLK,
+
+	GLAMO_CLOCK_GEN51_EN_DIV_DHCLK |
+	GLAMO_CLOCK_GEN51_EN_DIV_DCLK
+	}
+};
+
+static const struct glamo_engine_reg_set glamo_mmc_regs[] = {
+	{ GLAMO_REG_CLOCK_MMC,
+	GLAMO_CLOCK_MMC_EN_M9CLK |
+	GLAMO_CLOCK_MMC_DG_M9CLK,
+
+	GLAMO_CLOCK_MMC_EN_TCLK |
+	GLAMO_CLOCK_MMC_DG_TCLK
+	},
+	{ GLAMO_REG_CLOCK_GEN5_1,
+	0,
+	GLAMO_CLOCK_GEN51_EN_DIV_TCLK
+	}
+};
+
+static const struct glamo_engine_reg_set glamo_2d_regs[] = {
+	{ GLAMO_REG_CLOCK_2D,
+	GLAMO_CLOCK_2D_EN_M7CLK |
+	GLAMO_CLOCK_2D_DG_M7CLK,
+
+	GLAMO_CLOCK_2D_EN_GCLK |
+	GLAMO_CLOCK_2D_DG_GCLK
+	},
+	{ GLAMO_REG_CLOCK_GEN5_1,
+	0,
+	GLAMO_CLOCK_GEN51_EN_DIV_GCLK,
+	}
+};
+
+static const struct glamo_engine_reg_set glamo_cmdq_regs[] = {
+	{ GLAMO_REG_CLOCK_2D,
+	GLAMO_CLOCK_2D_EN_M6CLK,
+	0
+	},
+};
+
+#define GLAMO_ENGINE(xname, xhostbus, xregs) { \
+	.name = xname, \
+	.hostbus = xhostbus, \
+	.num_regs = ARRAY_SIZE(xregs), \
+	.regs = xregs, \
+}
+
+static const struct glamo_engine_desc glamo_engines[] = {
+	[GLAMO_ENGINE_LCD] = GLAMO_ENGINE("LCD", GLAMO_HOSTBUS2_MMIO_EN_LCD,
+					glamo_lcd_regs),
+	[GLAMO_ENGINE_MMC] = GLAMO_ENGINE("MMC", GLAMO_HOSTBUS2_MMIO_EN_MMC,
+					glamo_mmc_regs),
+	[GLAMO_ENGINE_2D] = GLAMO_ENGINE("2D", GLAMO_HOSTBUS2_MMIO_EN_2D,
+					glamo_2d_regs),
+	[GLAMO_ENGINE_CMDQ] = GLAMO_ENGINE("CMDQ", GLAMO_HOSTBUS2_MMIO_EN_CQ,
+					glamo_cmdq_regs),
+};
+
+static inline const char *glamo_engine_name(enum glamo_engine engine)
+{
+	return glamo_engines[engine].name;
+}
+
+/***********************************************************************
+ * 'engine' support
+ ***********************************************************************/
+
+int __glamo_engine_enable(struct glamo_core *glamo, enum glamo_engine engine)
+{
+	int i;
+	const struct glamo_engine_desc *engine_desc = &glamo_engines[engine];
+	const struct glamo_engine_reg_set *reg;
+
+	switch (engine) {
+	case GLAMO_ENGINE_LCD:
+	case GLAMO_ENGINE_MMC:
+	case GLAMO_ENGINE_2D:
+	case GLAMO_ENGINE_CMDQ:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	reg = engine_desc->regs;
+
+	__reg_set_bit(glamo, GLAMO_REG_HOSTBUS(2),
+			engine_desc->hostbus);
+	for (i = engine_desc->num_regs; i; --i, ++reg)
+		__reg_set_bit(glamo, reg->reg,
+				reg->mask_suspended | reg->mask_enabled);
+
+	return 0;
+}
+
+int glamo_engine_enable(struct glamo_core *glamo, enum glamo_engine engine)
+{
+	int ret = 0;
+
+	spin_lock(&glamo->lock);
+
+	if (glamo->engine_state[engine] != GLAMO_ENGINE_ENABLED) {
+		ret = __glamo_engine_enable(glamo, engine);
+		if (!ret)
+			glamo->engine_state[engine] = GLAMO_ENGINE_ENABLED;
+	}
+
+	spin_unlock(&glamo->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(glamo_engine_enable);
+
+int __glamo_engine_disable(struct glamo_core *glamo, enum glamo_engine engine)
+{
+	int i;
+	const struct glamo_engine_desc *engine_desc = &glamo_engines[engine];
+	const struct glamo_engine_reg_set *reg;
+
+	switch (engine) {
+	case GLAMO_ENGINE_LCD:
+	case GLAMO_ENGINE_MMC:
+	case GLAMO_ENGINE_2D:
+	case GLAMO_ENGINE_CMDQ:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	reg = engine_desc->regs;
+
+	__reg_clear_bit(glamo, GLAMO_REG_HOSTBUS(2),
+			engine_desc->hostbus);
+	for (i = engine_desc->num_regs; i; --i, ++reg)
+		__reg_clear_bit(glamo, reg->reg,
+				reg->mask_suspended | reg->mask_enabled);
+
+	return 0;
+}
+int glamo_engine_disable(struct glamo_core *glamo, enum glamo_engine engine)
+{
+	int ret = 0;
+
+	spin_lock(&glamo->lock);
+
+	if (glamo->engine_state[engine] != GLAMO_ENGINE_DISABLED) {
+		ret = __glamo_engine_disable(glamo, engine);
+		if (!ret)
+			glamo->engine_state[engine] = GLAMO_ENGINE_DISABLED;
+	}
+
+	spin_unlock(&glamo->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(glamo_engine_disable);
+
+int __glamo_engine_suspend(struct glamo_core *glamo, enum glamo_engine engine)
+{
+	int i;
+	const struct glamo_engine_desc *engine_desc = &glamo_engines[engine];
+	const struct glamo_engine_reg_set *reg;
+
+	switch (engine) {
+	case GLAMO_ENGINE_LCD:
+	case GLAMO_ENGINE_MMC:
+	case GLAMO_ENGINE_2D:
+	case GLAMO_ENGINE_CMDQ:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	reg = engine_desc->regs;
+
+	__reg_set_bit(glamo, GLAMO_REG_HOSTBUS(2),
+			engine_desc->hostbus);
+	for (i = engine_desc->num_regs; i; --i, ++reg) {
+		__reg_set_bit(glamo, reg->reg, reg->mask_suspended);
+		__reg_clear_bit(glamo, reg->reg, reg->mask_enabled);
+	}
+
+	return 0;
+}
+
+int glamo_engine_suspend(struct glamo_core *glamo, enum glamo_engine engine)
+{
+	int ret = 0;
+
+	spin_lock(&glamo->lock);
+
+	if (glamo->engine_state[engine] != GLAMO_ENGINE_SUSPENDED) {
+		ret = __glamo_engine_suspend(glamo, engine);
+		if (!ret)
+			glamo->engine_state[engine] = GLAMO_ENGINE_SUSPENDED;
+	}
+
+	spin_unlock(&glamo->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(glamo_engine_suspend);
+
+static const struct glamo_script reset_regs[] = {
+	[GLAMO_ENGINE_LCD] = {
+		GLAMO_REG_CLOCK_LCD, GLAMO_CLOCK_LCD_RESET
+	},
+	[GLAMO_ENGINE_MMC] = {
+		GLAMO_REG_CLOCK_MMC, GLAMO_CLOCK_MMC_RESET
+	},
+	[GLAMO_ENGINE_CMDQ] = {
+		GLAMO_REG_CLOCK_2D, GLAMO_CLOCK_2D_CQ_RESET
+	},
+	[GLAMO_ENGINE_2D] = {
+		GLAMO_REG_CLOCK_2D, GLAMO_CLOCK_2D_RESET
+	},
+	[GLAMO_ENGINE_JPEG] = {
+		GLAMO_REG_CLOCK_JPEG, GLAMO_CLOCK_JPEG_RESET
+	},
+};
+
+void glamo_engine_reset(struct glamo_core *glamo, enum glamo_engine engine)
+{
+	uint16_t reg = reset_regs[engine].reg;
+	uint16_t val = reset_regs[engine].val;
+
+	if (engine >= ARRAY_SIZE(reset_regs)) {
+		dev_warn(&glamo->pdev->dev, "unknown engine %u ", engine);
+		return;
+	}
+
+	spin_lock(&glamo->lock);
+	__reg_set_bit(glamo, reg, val);
+	__reg_clear_bit(glamo, reg, val);
+	spin_unlock(&glamo->lock);
+}
+EXPORT_SYMBOL_GPL(glamo_engine_reset);
+
+int glamo_pll_rate(struct glamo_core *glamo,
+			  enum glamo_pll pll)
+{
+	uint16_t reg;
+	unsigned int osci = glamo->pdata->osci_clock_rate;
+
+	switch (pll) {
+	case GLAMO_PLL1:
+		reg = __reg_read(glamo, GLAMO_REG_PLL_GEN1);
+		break;
+	case GLAMO_PLL2:
+		reg = __reg_read(glamo, GLAMO_REG_PLL_GEN3);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return (int)osci * (int)reg;
+}
+EXPORT_SYMBOL_GPL(glamo_pll_rate);
+
+int glamo_engine_reclock(struct glamo_core *glamo,
+			 enum glamo_engine engine,
+			 int hz)
+{
+	int pll;
+	uint16_t reg, mask, div;
+
+	if (!hz)
+		return -EINVAL;
+
+	switch (engine) {
+	case GLAMO_ENGINE_LCD:
+		pll = GLAMO_PLL1;
+		reg = GLAMO_REG_CLOCK_GEN7;
+		mask = 0xff;
+		break;
+	case GLAMO_ENGINE_MMC:
+		pll = GLAMO_PLL1;
+		reg = GLAMO_REG_CLOCK_GEN8;
+		mask = 0xff;
+		break;
+	default:
+		dev_warn(&glamo->pdev->dev,
+			 "reclock of engine 0x%x not supported\n", engine);
+		return -EINVAL;
+		break;
+	}
+
+	pll = glamo_pll_rate(glamo, pll);
+
+	div = pll / hz;
+
+	if (div != 0 && pll / div <= hz)
+		--div;
+
+	if (div > mask)
+		div = mask;
+
+	dev_dbg(&glamo->pdev->dev,
+			"PLL %d, kHZ %d, div %d\n", pll, hz / 1000, div);
+
+	reg_set_bit_mask(glamo, reg, mask, div);
+	mdelay(5); /* wait some time to stabilize */
+
+	return pll / (div + 1);
+}
+EXPORT_SYMBOL_GPL(glamo_engine_reclock);
+
+/***********************************************************************
+ * script support
+ ***********************************************************************/
+
+#define GLAMO_SCRIPT_END	0xffff
+#define GLAMO_SCRIPT_WAIT	0xfffe
+#define GLAMO_SCRIPT_LOCK_PLL	0xfffd
+
+/*
+ * couple of people reported artefacts with 2.6.28 changes, this
+ * allows reversion to 2.6.24 settings
+*/
+static const uint16_t reg_0x200[] = {
+	0xe03, /* 0 waits on Async BB R & W, Use PLL 2 for mem bus */
+	0xef0, /* 3 waits on Async BB R & W, Use PLL 1 for mem bus */
+	0xea0, /* 2 waits on Async BB R & W, Use PLL 1 for mem bus */
+	0xe50, /* 1 waits on Async BB R & W, Use PLL 1 for mem bus */
+	0xe00, /* 0 waits on Async BB R & W, Use PLL 1 for mem bus */
+	0xef3, /* 3 waits on Async BB R & W, Use PLL 2 for mem bus */
+	0xea3, /* 2 waits on Async BB R & W, Use PLL 2 for mem bus */
+	0xe53, /* 1 waits on Async BB R & W, Use PLL 2 for mem bus */
+};
+
+static int glamo_run_script(struct glamo_core *glamo,
+				const struct glamo_script *script, int len,
+				int may_sleep)
+{
+	int i;
+	uint16_t status;
+	const struct glamo_script *line = script;
+
+	for (i = 0; i < len; ++i, ++line) {
+		switch (line->reg) {
+		case GLAMO_SCRIPT_END:
+			return 0;
+		case GLAMO_SCRIPT_WAIT:
+			if (may_sleep)
+				msleep(line->val);
+			else
+				mdelay(line->val * 4);
+			break;
+		case GLAMO_SCRIPT_LOCK_PLL:
+			/* spin until PLLs lock */
+			do {
+				status = __reg_read(glamo, GLAMO_REG_PLL_GEN5);
+			} while ((status & 3) != 3);
+			break;
+		case 0x200:
+			__reg_write(glamo, line->reg,
+					reg_0x200[slow_memory & 0x7]);
+			break;
+		default:
+			__reg_write(glamo, line->reg, line->val);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static const struct glamo_script glamo_init_script[] = {
+	{ GLAMO_REG_CLOCK_HOST,		0x1000 },
+	{ GLAMO_SCRIPT_WAIT,		     2 },
+	{ GLAMO_REG_CLOCK_MEMORY,	0x1000 },
+	{ GLAMO_REG_CLOCK_MEMORY,	0x2000 },
+	{ GLAMO_REG_CLOCK_LCD,		0x1000 },
+	{ GLAMO_REG_CLOCK_MMC,		0x1000 },
+	{ GLAMO_REG_CLOCK_ISP,		0x1000 },
+	{ GLAMO_REG_CLOCK_ISP,		0x3000 },
+	{ GLAMO_REG_CLOCK_JPEG,		0x1000 },
+	{ GLAMO_REG_CLOCK_3D,		0x1000 },
+	{ GLAMO_REG_CLOCK_3D,		0x3000 },
+	{ GLAMO_REG_CLOCK_2D,		0x1000 },
+	{ GLAMO_REG_CLOCK_2D,		0x3000 },
+	{ GLAMO_REG_CLOCK_RISC1,	0x1000 },
+	{ GLAMO_REG_CLOCK_MPEG,		0x1000 },
+	{ GLAMO_REG_CLOCK_MPEG,		0x3000 },
+	{ GLAMO_REG_CLOCK_MPROC,	0x1000 /*0x100f*/ },
+	{ GLAMO_SCRIPT_WAIT,		     2 },
+	{ GLAMO_REG_CLOCK_HOST,		0x0000 },
+	{ GLAMO_REG_CLOCK_MEMORY,	0x0000 },
+	{ GLAMO_REG_CLOCK_LCD,		0x0000 },
+	{ GLAMO_REG_CLOCK_MMC,		0x0000 },
+	{ GLAMO_REG_PLL_GEN1,		0x05db },	/* 48MHz */
+	{ GLAMO_REG_PLL_GEN3,		0x0aba },	/* 90MHz */
+	{ GLAMO_SCRIPT_LOCK_PLL, 0 },
+	/*
+	 * b9 of this register MUST be zero to get any interrupts on INT#
+	 * the other set bits enable all the engine interrupt sources
+	 */
+	{ GLAMO_REG_IRQ_ENABLE,		0x0100 },
+	{ GLAMO_REG_CLOCK_GEN6,		0x2000 },
+	{ GLAMO_REG_CLOCK_GEN7,		0x0101 },
+	{ GLAMO_REG_CLOCK_GEN8,		0x0100 },
+	{ GLAMO_REG_CLOCK_HOST,		0x000d },
+	/*
+	 * b7..b4 = 0 = no wait states on read or write
+	 * b0 = 1 select PLL2 for Host interface, b1 = enable it
+	 */
+	{ GLAMO_REG_HOSTBUS(1),		0x0e03 /* this is replaced by script parser */ },
+	{ GLAMO_REG_HOSTBUS(2),		0x07ff }, /* TODO: Disable all */
+	{ GLAMO_REG_HOSTBUS(10),	0x0000 },
+	{ GLAMO_REG_HOSTBUS(11),	0x4000 },
+	{ GLAMO_REG_HOSTBUS(12),	0xf00e },
+
+	/* S-Media recommended "set tiling mode to 512 mode for memory access
+	 * more efficiency when 640x480" */
+	{ GLAMO_REG_MEM_TYPE,		0x0c74 }, /* 8MB, 16 word pg wr+rd */
+	{ GLAMO_REG_MEM_GEN,		0xafaf }, /* 63 grants min + max */
+
+	{ GLAMO_REG_MEM_TIMING1,	0x0108 },
+	{ GLAMO_REG_MEM_TIMING2,	0x0010 }, /* Taa = 3 MCLK */
+	{ GLAMO_REG_MEM_TIMING3,	0x0000 },
+	{ GLAMO_REG_MEM_TIMING4,	0x0000 }, /* CE1# delay fall/rise */
+	{ GLAMO_REG_MEM_TIMING5,	0x0000 }, /* UB# LB# */
+	{ GLAMO_REG_MEM_TIMING6,	0x0000 }, /* OE# */
+	{ GLAMO_REG_MEM_TIMING7,	0x0000 }, /* WE# */
+	{ GLAMO_REG_MEM_TIMING8,	0x1002 }, /* MCLK delay, was 0x1000 */
+	{ GLAMO_REG_MEM_TIMING9,	0x6006 },
+	{ GLAMO_REG_MEM_TIMING10,	0x00ff },
+	{ GLAMO_REG_MEM_TIMING11,	0x0001 },
+	{ GLAMO_REG_MEM_POWER1,		0x0020 },
+	{ GLAMO_REG_MEM_POWER2,		0x0000 },
+	{ GLAMO_REG_MEM_DRAM1,		0x0000 },
+	{ GLAMO_SCRIPT_WAIT,		     1 },
+	{ GLAMO_REG_MEM_DRAM1,		0xc100 },
+	{ GLAMO_SCRIPT_WAIT,		     1 },
+	{ GLAMO_REG_MEM_DRAM1,		0xe100 },
+	{ GLAMO_REG_MEM_DRAM2,		0x01d6 },
+	{ GLAMO_REG_CLOCK_MEMORY,	0x000b },
+};
+
+/* Find out if we can support this version of the Glamo chip */
+static int __devinit glamo_supported(struct glamo_core *glamo)
+{
+	uint16_t dev_id, rev_id;
+
+	dev_id = __reg_read(glamo, GLAMO_REG_DEVICE_ID);
+	rev_id = __reg_read(glamo, GLAMO_REG_REVISION_ID);
+
+	switch (dev_id) {
+	case 0x3650:
+		switch (rev_id) {
+		case GLAMO_CORE_REV_A2:
+			break;
+		case GLAMO_CORE_REV_A0:
+		case GLAMO_CORE_REV_A1:
+		case GLAMO_CORE_REV_A3:
+			dev_warn(&glamo->pdev->dev, "untested core revision "
+				 "%04x, your mileage may vary\n", rev_id);
+			break;
+		default:
+			dev_warn(&glamo->pdev->dev, "unknown glamo revision "
+				 "%04x, your mileage may vary\n", rev_id);
+		}
+		break;
+	default:
+		dev_err(&glamo->pdev->dev, "unsupported Glamo device %04x\n",
+			dev_id);
+		return 0;
+	}
+
+	dev_dbg(&glamo->pdev->dev, "Detected Glamo core %04x Revision %04x "
+		 "(%uHz CPU / %uHz Memory)\n", dev_id, rev_id,
+		 glamo_pll_rate(glamo, GLAMO_PLL1),
+		 glamo_pll_rate(glamo, GLAMO_PLL2));
+
+	return 1;
+}
+
+static int __devinit glamo_probe(struct platform_device *pdev)
+{
+	int ret = 0, n, irq, irq_base;
+	struct glamo_core *glamo;
+	struct resource *mem;
+
+	glamo = kmalloc(GFP_KERNEL, sizeof(*glamo));
+	if (!glamo)
+		return -ENOMEM;
+
+	for (n = 0; n < __NUM_GLAMO_ENGINES; n++)
+		glamo->engine_state[n] = GLAMO_ENGINE_DISABLED;
+
+	spin_lock_init(&glamo->lock);
+
+	glamo->pdev = pdev;
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	glamo->irq = platform_get_irq(pdev, 0);
+	glamo->pdata = pdev->dev.platform_data;
+
+	if (glamo->irq < 0) {
+		ret = glamo->irq;
+		dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
+		goto err_free;
+	}
+
+	if (!mem) {
+		dev_err(&pdev->dev, "Failed to get platform memory\n");
+		ret = -ENOENT;
+		goto err_free;
+	}
+
+	if (!glamo->pdata) {
+		dev_err(&pdev->dev, "Missing platform data\n");
+		ret = -ENOENT;
+		goto err_free;
+	}
+
+	irq_base = irq_alloc_descs(-1, 0, GLAMO_NR_IRQS, 0);
+	if (irq_base < 0) {
+		dev_err(&pdev->dev, "Failed to allocate irqs: %d\n", irq_base);
+		goto err_free;
+	}
+	glamo->irq_base = irq_base;
+
+
+	/* only request the generic, hostbus and memory controller registers */
+	glamo->mem = request_mem_region(mem->start, GLAMO_REGOFS_VIDCAP,
+					pdev->name);
+
+	if (!glamo->mem) {
+		dev_err(&pdev->dev, "Failed to request io memory region\n");
+		ret = -ENOENT;
+		goto err_irq_free_descs;
+	}
+
+	glamo->base = ioremap(glamo->mem->start, resource_size(glamo->mem));
+	if (!glamo->base) {
+		dev_err(&pdev->dev, "Failed to ioremap() memory region\n");
+		goto err_release_mem_region;
+	}
+
+	/* confirm it isn't insane version */
+	if (!glamo_supported(glamo)) {
+		dev_err(&pdev->dev,
+			"This version of the Glamo is not supported\n");
+		goto err_iounmap;
+	}
+
+	platform_set_drvdata(pdev, glamo);
+
+	/* debugfs */
+	glamo_init_debugfs(glamo);
+
+	/* init the chip with canned register set */
+	glamo_run_script(glamo, glamo_init_script,
+			 ARRAY_SIZE(glamo_init_script), 1);
+
+	/*
+	 * finally set the mfd interrupts up
+	 */
+	for (irq = irq_base; irq < irq_base + GLAMO_NR_IRQS; ++irq) {
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, IRQF_VALID);
+#else
+		irq_set_noprobe(irq);
+#endif
+		irq_set_chip_data(irq, glamo);
+		irq_set_chip_and_handler(irq, &glamo_irq_chip,
+					handle_level_irq);
+	}
+
+	ret = request_any_context_irq(glamo->irq, glamo_irq_handler,
+				IRQF_TRIGGER_FALLING, "glamo irq demux", glamo);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
+		goto err_free_irqs;
+	}
+
+	ret = mfd_add_devices(&pdev->dev, pdev->id, glamo_cells,
+				ARRAY_SIZE(glamo_cells), mem, glamo->irq_base);
+
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to add child devices: %d\n", ret);
+		goto err_free_irq;
+	}
+
+	dev_info(&glamo->pdev->dev, "Glamo core PLL1: %uHz, PLL2: %uHz\n",
+		 glamo_pll_rate(glamo, GLAMO_PLL1),
+		 glamo_pll_rate(glamo, GLAMO_PLL2));
+
+	return 0;
+
+err_free_irq:
+	free_irq(glamo->irq, glamo);
+err_free_irqs:
+	for (irq = irq_base; irq < irq_base + GLAMO_NR_IRQS; ++irq) {
+		irq_set_chip(irq, NULL);
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, 0);
+#else
+		irq_set_probe(irq);
+#endif
+		irq_set_chip_data(irq, NULL);
+	}
+err_iounmap:
+	iounmap(glamo->base);
+err_release_mem_region:
+	release_mem_region(glamo->mem->start, resource_size(glamo->mem));
+err_irq_free_descs:
+	irq_free_descs(irq_base, GLAMO_NR_IRQS);
+err_free:
+	platform_set_drvdata(pdev, NULL);
+	kfree(glamo);
+
+	return ret;
+}
+
+static int __devexit glamo_remove(struct platform_device *pdev)
+{
+	struct glamo_core *glamo = platform_get_drvdata(pdev);
+	int irq;
+	int irq_base = glamo->irq_base;
+
+	glamo_exit_debugfs(glamo);
+
+	mfd_remove_devices(&pdev->dev);
+
+	free_irq(glamo->irq, glamo);
+
+	for (irq = irq_base; irq < irq_base + GLAMO_NR_IRQS; ++irq) {
+#ifdef CONFIG_ARM
+		set_irq_flags(irq, 0);
+#else
+		irq_set_noprobe();
+#endif
+		irq_set_chip(irq, NULL);
+		irq_set_chip_data(irq, NULL);
+	}
+
+	platform_set_drvdata(pdev, NULL);
+	iounmap(glamo->base);
+	release_mem_region(glamo->mem->start, resource_size(glamo->mem));
+	irq_free_descs(irq_base, GLAMO_NR_IRQS);
+	kfree(glamo);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+#if 0
+static struct glamo_script glamo_resume_script[] = {
+
+	{ GLAMO_REG_PLL_GEN1,		0x05db },	/* 48MHz */
+	{ GLAMO_REG_PLL_GEN3,		0x0aba },	/* 90MHz */
+	{ GLAMO_REG_DFT_GEN6, 1 },
+		{ 0xfffe, 100 },
+		{ 0xfffd, 0 },
+	{ 0x200,	0x0e03 },
+
+	/*
+	 * b9 of this register MUST be zero to get any interrupts on INT#
+	 * the other set bits enable all the engine interrupt sources
+	 */
+	{ GLAMO_REG_IRQ_ENABLE,		0x01ff },
+	{ GLAMO_REG_CLOCK_HOST,		0x0018 },
+	{ GLAMO_REG_CLOCK_GEN5_1, 0x18b1 },
+
+	{ GLAMO_REG_MEM_DRAM1,		0x0000 },
+		{ 0xfffe, 1 },
+	{ GLAMO_REG_MEM_DRAM1,		0xc100 },
+		{ 0xfffe, 1 },
+	{ GLAMO_REG_MEM_DRAM1,		0xe100 },
+	{ GLAMO_REG_MEM_DRAM2,		0x01d6 },
+	{ GLAMO_REG_CLOCK_MEMORY,	0x000b },
+};
+#endif
+
+#if 0
+static void glamo_power(struct glamo_core *glamo)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&glamo->lock, flags);
+
+	/*
+Power management
+static const REG_VALUE_MASK_TYPE reg_powerOn[] =
+{
+	{ REG_GEN_DFT6,	    REG_BIT_ALL,    REG_DATA(1u << 0)		},
+	{ REG_GEN_PLL3,	    0u,		    REG_DATA(1u << 13)		},
+	{ REG_GEN_MEM_CLK,  REG_BIT_ALL,    REG_BIT_EN_MOCACLK		},
+	{ REG_MEM_DRAM2,    0u,		    REG_BIT_EN_DEEP_POWER_DOWN	},
+	{ REG_MEM_DRAM1,    0u,		    REG_BIT_SELF_REFRESH	}
+};
+
+static const REG_VALUE_MASK_TYPE reg_powerStandby[] =
+{
+	{ REG_MEM_DRAM1,    REG_BIT_ALL,    REG_BIT_SELF_REFRESH    },
+	{ REG_GEN_MEM_CLK,  0u,		    REG_BIT_EN_MOCACLK	    },
+	{ REG_GEN_PLL3,	    REG_BIT_ALL,    REG_DATA(1u << 13)	    },
+	{ REG_GEN_DFT5,	    REG_BIT_ALL,    REG_DATA(1u << 0)	    }
+};
+
+static const REG_VALUE_MASK_TYPE reg_powerSuspend[] =
+{
+	{ REG_MEM_DRAM2,    REG_BIT_ALL,    REG_BIT_EN_DEEP_POWER_DOWN  },
+	{ REG_GEN_MEM_CLK,  0u,		    REG_BIT_EN_MOCACLK		},
+	{ REG_GEN_PLL3,	    REG_BIT_ALL,    REG_DATA(1u << 13)		},
+	{ REG_GEN_DFT5,	    REG_BIT_ALL,    REG_DATA(1u << 0)		}
+};
+*/
+	switch (new_state) {
+	case GLAMO_POWER_ON:
+
+		/*
+		 * glamo state on resume is nondeterministic in some
+		 * fundamental way, it has also been observed that the
+		 * Glamo reset pin can get asserted by, eg, touching it with
+		 * a scope probe.  So the only answer is to roll with it and
+		 * force an external reset on the Glamo during resume.
+		 */
+
+
+		break;
+
+	case GLAMO_POWER_SUSPEND:
+
+		break;
+	}
+	spin_unlock_irqrestore(&glamo->lock, flags);
+}
+#endif
+
+static int glamo_suspend(struct device *dev)
+{
+	struct glamo_core *glamo = dev_get_drvdata(dev);
+	int n;
+
+	spin_lock(&glamo->lock);
+
+	glamo->saved_irq_mask = __reg_read(glamo, GLAMO_REG_IRQ_ENABLE);
+
+	/* nuke interrupts */
+	__reg_write(glamo, GLAMO_REG_IRQ_ENABLE, 0x200);
+
+	/* take down each engine before we kill mem and pll */
+	for (n = 0; n < __NUM_GLAMO_ENGINES; n++) {
+		if (glamo->engine_state[n] != GLAMO_ENGINE_DISABLED)
+			__glamo_engine_disable(glamo, n);
+	}
+
+	/* enable self-refresh */
+
+	__reg_write(glamo, GLAMO_REG_MEM_DRAM1,
+				GLAMO_MEM_DRAM1_EN_DRAM_REFRESH |
+				GLAMO_MEM_DRAM1_EN_GATE_CKE |
+				GLAMO_MEM_DRAM1_SELF_REFRESH |
+				GLAMO_MEM_REFRESH_COUNT);
+	__reg_write(glamo, GLAMO_REG_MEM_DRAM1,
+				GLAMO_MEM_DRAM1_EN_MODEREG_SET |
+				GLAMO_MEM_DRAM1_EN_DRAM_REFRESH |
+				GLAMO_MEM_DRAM1_EN_GATE_CKE |
+				GLAMO_MEM_DRAM1_SELF_REFRESH |
+				GLAMO_MEM_REFRESH_COUNT);
+
+	/* force RAM into deep powerdown */
+	__reg_write(glamo, GLAMO_REG_MEM_DRAM2,
+				GLAMO_MEM_DRAM2_DEEP_PWRDOWN |
+				(7 << 6) | /* tRC */
+				(1 << 4) | /* tRP */
+				(1 << 2) | /* tRCD */
+				2); /* CAS latency */
+
+	/* disable clocks to memory */
+	__reg_write(glamo, GLAMO_REG_CLOCK_MEMORY, 0);
+
+	/* all dividers from OSCI */
+	__reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_GEN5_1, 0x400, 0x400);
+
+	/* PLL2 into bypass */
+	__reg_set_bit_mask(glamo, GLAMO_REG_PLL_GEN3, 1 << 12, 1 << 12);
+
+	__reg_write(glamo, GLAMO_BASIC_MMC_EN_TCLK_DLYA1, 0x0e00);
+
+	/* kill PLLS 1 then 2 */
+	__reg_write(glamo, GLAMO_REG_DFT_GEN5, 0x0001);
+	__reg_set_bit_mask(glamo, GLAMO_REG_PLL_GEN3, 1 << 13, 1 << 13);
+
+	spin_unlock(&glamo->lock);
+
+	return 0;
+}
+
+static int glamo_resume(struct device *dev)
+{
+	struct glamo_core *glamo = dev_get_drvdata(dev);
+	int n;
+
+	(glamo->pdata->glamo_external_reset)(0);
+	udelay(10);
+	(glamo->pdata->glamo_external_reset)(1);
+	mdelay(5);
+
+	spin_lock(&glamo->lock);
+
+	glamo_run_script(glamo, glamo_init_script,
+			 ARRAY_SIZE(glamo_init_script), 0);
+
+
+	for (n = 0; n < __NUM_GLAMO_ENGINES; n++) {
+		switch (glamo->engine_state[n]) {
+		case GLAMO_ENGINE_SUSPENDED:
+			__glamo_engine_suspend(glamo, n);
+			break;
+		case GLAMO_ENGINE_ENABLED:
+			__glamo_engine_enable(glamo, n);
+			break;
+		default:
+			break;
+		}
+	}
+
+	__reg_write(glamo, GLAMO_REG_IRQ_ENABLE, glamo->saved_irq_mask);
+
+	spin_unlock(&glamo->lock);
+
+	return 0;
+}
+
+static const struct dev_pm_ops glamo_pm_ops = {
+	.suspend    = glamo_suspend,
+	.resume     = glamo_resume,
+	.poweroff   = glamo_suspend,
+	.restore    = glamo_resume,
+};
+
+#define GLAMO_PM_OPS (&glamo_pm_ops)
+
+#else
+#define GLAMO_PM_OPS NULL
+#endif
+
+static struct platform_driver glamo_driver = {
+	.probe		= glamo_probe,
+	.remove		= __devexit_p(glamo_remove),
+	.driver		= {
+		.name	= "glamo3362",
+		.owner	= THIS_MODULE,
+		.pm	= GLAMO_PM_OPS,
+	},
+};
+
+static int __init glamo_init(void)
+{
+	return platform_driver_register(&glamo_driver);
+}
+module_init(glamo_init);
+
+static void __exit glamo_exit(void)
+{
+	platform_driver_unregister(&glamo_driver);
+}
+module_exit(glamo_exit);
+
+MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Smedia Glamo 3362 core/resource driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:glamo3362");
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 94df405..872ef4a 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -429,6 +429,17 @@ config MMC_S3C_PIODMA
 
 endchoice
 
+config MMC_GLAMO
+	tristate "Glamo S3C SD/MMC Card Interface support"
+	depends on MFD_GLAMO && MMC && REGULATOR
+	select CRC7
+	help
+	  This selects a driver for the MCI interface found in
+	  the S-Media GLAMO chip, as used in Openmoko
+	  neo1973 GTA-02.
+
+	  If unsure, say N.
+
 config MMC_SDRICOH_CS
 	tristate "MMC/SD driver for Ricoh Bay1Controllers (EXPERIMENTAL)"
 	depends on EXPERIMENTAL && PCI && PCMCIA
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 4f1df0a..7133627 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -27,6 +27,7 @@ ifeq ($(CONFIG_OF),y)
 obj-$(CONFIG_MMC_SPI)		+= of_mmc_spi.o
 endif
 obj-$(CONFIG_MMC_S3C)   	+= s3cmci.o
+obj-$(CONFIG_MMC_GLAMO)		+= glamo-mci.o
 obj-$(CONFIG_MMC_SDRICOH_CS)	+= sdricoh_cs.o
 obj-$(CONFIG_MMC_TMIO)		+= tmio_mmc.o
 obj-$(CONFIG_MMC_TMIO_CORE)	+= tmio_mmc_core.o
diff --git a/drivers/mmc/host/glamo-mci.c b/drivers/mmc/host/glamo-mci.c
new file mode 100644
index 0000000..02c4b69
--- /dev/null
+++ b/drivers/mmc/host/glamo-mci.c
@@ -0,0 +1,939 @@
+/*
+ *  linux/drivers/mmc/host/glamo-mmc.c - Glamo MMC driver
+ *
+ *  Copyright (C) 2007 Openmoko, Inc,  Andy Green <andy@openmoko.com>
+ *  Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
+ *  Based on S3C MMC driver that was:
+ *  Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de>
+ *
+ * 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/module.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/host.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/crc7.h>
+#include <linux/scatterlist.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/mfd/glamo.h>
+#include <linux/mfd/glamo-core.h>
+#include <linux/mfd/glamo-regs.h>
+
+struct glamo_mci_host {
+	struct glamo_mmc_platform_data *pdata;
+	struct platform_device	*pdev;
+	struct glamo_core	*core;
+	struct mmc_host		*mmc;
+	struct resource		*mmio_mem;
+	struct resource		*data_mem;
+	void __iomem		*mmio_base;
+	uint16_t __iomem	*data_base;
+
+	unsigned int irq;
+
+	struct regulator *regulator;
+	struct mmc_request *mrq;
+
+	unsigned int clk_rate;
+
+	unsigned short vdd;
+	char power_mode;
+
+	unsigned long transfer_start;
+	unsigned long request_start;
+
+	unsigned char request_counter;
+};
+
+static void glamo_mci_send_request(struct mmc_host *mmc,
+				   struct mmc_request *mrq);
+static void glamo_mci_send_command(struct glamo_mci_host *host,
+				   struct mmc_command *cmd);
+
+/*
+ * Max SD clock rate
+ *
+ * held at /(3 + 1) due to concerns of 100R recommended series resistor
+ * allows 16MHz @ 4-bit --> 8MBytes/sec raw
+ *
+ * you can override this on kernel commandline using
+ *
+ *   glamo_mci.sd_max_clk=10000000
+ *
+ * for example
+ */
+
+static int sd_max_clk = 17000000;
+module_param(sd_max_clk, int, 0644);
+
+/*
+ * Slow SD clock rate
+ *
+ * you can override this on kernel commandline using
+ *
+ *   glamo_mci.sd_slow_ratio=8
+ *
+ * for example
+ *
+ * platform callback is used to decide effective clock rate, if not
+ * defined then max is used, if defined and returns nonzero, rate is
+ * divided by this factor
+ */
+
+static int sd_slow_ratio = 8;
+module_param(sd_slow_ratio, int, 0644);
+
+/*
+ * Post-power SD clock rate
+ *
+ * you can override this on kernel commandline using
+ *
+ *   glamo_mci.sd_post_power_clock=1000000
+ *
+ * for example
+ *
+ * After changing power to card, clock is held at this rate until first bulk
+ * transfer completes
+ */
+
+static int sd_post_power_clock = 1000000;
+module_param(sd_post_power_clock, int, 0644);
+
+
+static inline void glamomci_reg_write(struct glamo_mci_host *glamo,
+		uint16_t reg, uint16_t val)
+{
+	writew(val, glamo->mmio_base + reg);
+}
+
+static inline uint16_t glamomci_reg_read(struct glamo_mci_host *glamo,
+		uint16_t reg)
+{
+	return readw(glamo->mmio_base + reg);
+}
+
+static void glamomci_reg_set_bit_mask(struct glamo_mci_host *glamo,
+		uint16_t reg, uint16_t mask, uint16_t val)
+{
+	uint16_t tmp;
+
+	val &= mask;
+
+	tmp = glamomci_reg_read(glamo, reg);
+	tmp &= ~mask;
+	tmp |= val;
+	glamomci_reg_write(glamo, reg, tmp);
+}
+
+static void glamo_mci_reset(struct glamo_mci_host *host)
+{
+	glamo_engine_reset(host->core, GLAMO_ENGINE_MMC);
+
+	glamomci_reg_write(host, GLAMO_REG_MMC_WDATADS1,
+			(uint16_t)(host->data_mem->start));
+	glamomci_reg_write(host, GLAMO_REG_MMC_WDATADS2,
+			(uint16_t)(host->data_mem->start >> 16));
+
+	glamomci_reg_write(host, GLAMO_REG_MMC_RDATADS1,
+			(uint16_t)(host->data_mem->start));
+	glamomci_reg_write(host, GLAMO_REG_MMC_RDATADS2,
+			(uint16_t)(host->data_mem->start >> 16));
+
+}
+
+static int glamo_mci_clock_disable(struct mmc_host *mmc, int lazy)
+{
+	struct glamo_mci_host *host = mmc_priv(mmc);
+	glamo_engine_suspend(host->core, GLAMO_ENGINE_MMC);
+	return 0;
+}
+
+static int glamo_mci_clock_enable(struct mmc_host *mmc)
+{
+	struct glamo_mci_host *host = mmc_priv(mmc);
+	glamo_engine_enable(host->core, GLAMO_ENGINE_MMC);
+	return 0;
+}
+
+static void __iomem *glamo_mci_get_data_addr(struct glamo_mci_host *host,
+	struct mmc_data *data)
+{
+	void __iomem *addr = host->data_base;
+
+	if (data->host_cookie & 1)
+		addr += resource_size(host->data_mem) / 2;
+
+	return addr;
+}
+
+static void do_pio_read(struct glamo_mci_host *host, struct mmc_data *data)
+{
+	void __iomem *from_ptr = glamo_mci_get_data_addr(host, data);
+	struct sg_mapping_iter miter;
+
+	dev_dbg(&host->pdev->dev, "pio_read():\n");
+
+	sg_miter_start(&miter, data->sg, data->sg_len, SG_MITER_TO_SG);
+
+	while (sg_miter_next(&miter)) {
+		memcpy(miter.addr, from_ptr, miter.length);
+		from_ptr += miter.length;
+	}
+
+	sg_miter_stop(&miter);
+
+	dev_dbg(&host->pdev->dev, "pio_read(): "
+			"complete (no more data)\n");
+}
+
+static void do_pio_write(struct glamo_mci_host *host, struct mmc_data *data)
+{
+	void __iomem *to_ptr = glamo_mci_get_data_addr(host, data);
+	struct sg_mapping_iter miter;
+
+	dev_dbg(&host->pdev->dev, "pio_write():\n");
+	sg_miter_start(&miter, data->sg, data->sg_len, SG_MITER_FROM_SG);
+
+	while (sg_miter_next(&miter)) {
+		memcpy(to_ptr, miter.addr, miter.length);
+		to_ptr += miter.length;
+
+		data->bytes_xfered += miter.length;
+	}
+	sg_miter_stop(&miter);
+
+	dev_dbg(&host->pdev->dev, "pio_write(): complete\n");
+}
+
+static int glamo_mci_set_card_clock(struct glamo_mci_host *host, int freq)
+{
+	int real_rate = 0;
+
+	if (freq)
+		real_rate = glamo_engine_reclock(host->core, GLAMO_ENGINE_MMC,
+						 freq);
+
+	return real_rate;
+}
+
+static int glamo_mci_wait_idle(struct glamo_mci_host *host,
+			       unsigned long timeout)
+{
+	uint16_t status;
+
+	do {
+		status = glamomci_reg_read(host, GLAMO_REG_MMC_RB_STAT1);
+	} while (!(status & GLAMO_STAT1_MMC_IDLE) &&
+		  time_is_after_jiffies(timeout));
+
+	if (time_is_before_eq_jiffies(timeout)) {
+		glamo_mci_reset(host);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void glamo_mci_request_done(struct glamo_mci_host *host,
+				   struct mmc_request *mrq)
+{
+	mmc_request_done(host->mmc, mrq);
+}
+
+static irqreturn_t glamo_mci_irq(int irq, void *data)
+{
+	struct glamo_mci_host *host = data;
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	uint16_t status;
+
+	if (!host->mrq || !host->mrq->cmd)
+		return IRQ_HANDLED;
+
+	mrq = host->mrq;
+	cmd = mrq->cmd;
+
+	status = glamomci_reg_read(host, GLAMO_REG_MMC_RB_STAT1);
+	dev_dbg(&host->pdev->dev, "status = 0x%04x\n", status);
+
+	/* we ignore a data timeout report if we are also told the data came */
+	if (status & GLAMO_STAT1_MMC_RB_DRDY)
+		status &= ~GLAMO_STAT1_MMC_DTOUT;
+
+	if (status & (GLAMO_STAT1_MMC_RTOUT | GLAMO_STAT1_MMC_DTOUT))
+		cmd->error = -ETIMEDOUT;
+	else if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR))
+		cmd->error = -EILSEQ;
+
+	if (cmd->error) {
+		dev_info(&host->pdev->dev, "Error after cmd: 0x%x\n", status);
+		goto done;
+	}
+
+	/* issue STOP if we have been given one to use */
+	if (mrq->stop)
+		glamo_mci_send_command(host, mrq->stop);
+
+	if (mrq->data && (mrq->data->flags & MMC_DATA_READ)) {
+		mrq->data->bytes_xfered = mrq->data->blocks * mrq->data->blksz;
+		if (!mrq->data->host_cookie)
+			do_pio_read(host, mrq->data);
+	}
+
+	if (mrq->stop)
+		mrq->stop->error = glamo_mci_wait_idle(host, jiffies + HZ);
+
+done:
+	host->mrq = NULL;
+	glamo_mci_request_done(host, cmd->mrq);
+
+	return IRQ_HANDLED;
+}
+
+static void glamo_mci_send_command(struct glamo_mci_host *host,
+		struct mmc_command *cmd)
+{
+	uint8_t u8a[6];
+	uint16_t fire = 0;
+	unsigned int timeout = 1000000;
+	uint16_t *reg_resp = (uint16_t *)(host->mmio_base + GLAMO_REG_MMC_CMD_RSP1);
+	uint16_t status;
+	int triggers_int = 1;
+
+	/* if we can't do it, reject as busy */
+	if (!(glamomci_reg_read(host, GLAMO_REG_MMC_RB_STAT1) &
+		 GLAMO_STAT1_MMC_IDLE)) {
+		cmd->error = -EBUSY;
+		return;
+	}
+
+	/* create an array in wire order for CRC computation */
+	u8a[0] = 0x40 | (cmd->opcode & 0x3f);
+	u8a[1] = (uint8_t)(cmd->arg >> 24);
+	u8a[2] = (uint8_t)(cmd->arg >> 16);
+	u8a[3] = (uint8_t)(cmd->arg >> 8);
+	u8a[4] = (uint8_t)cmd->arg;
+	u8a[5] = (crc7(0, u8a, 5) << 1) | 0x01;
+
+	/* issue the wire-order array including CRC in register order */
+	glamomci_reg_write(host, GLAMO_REG_MMC_CMD_REG1, ((u8a[4] << 8) | u8a[5]));
+	glamomci_reg_write(host, GLAMO_REG_MMC_CMD_REG2, ((u8a[2] << 8) | u8a[3]));
+	glamomci_reg_write(host, GLAMO_REG_MMC_CMD_REG3, ((u8a[0] << 8) | u8a[1]));
+
+	/* command index toggle */
+	fire |= (host->request_counter & 1) << 12;
+
+	/* set type of command */
+	switch (mmc_cmd_type(cmd)) {
+	case MMC_CMD_BC:
+		fire |= GLAMO_FIRE_MMC_CMDT_BNR;
+		break;
+	case MMC_CMD_BCR:
+		fire |= GLAMO_FIRE_MMC_CMDT_BR;
+		break;
+	case MMC_CMD_AC:
+		fire |= GLAMO_FIRE_MMC_CMDT_AND;
+		break;
+	case MMC_CMD_ADTC:
+		fire |= GLAMO_FIRE_MMC_CMDT_AD;
+		break;
+	}
+	/*
+	 * if it expects a response, set the type expected
+	 *
+	 * R1, Length  : 48bit, Normal response
+	 * R1b, Length : 48bit, same R1, but added card busy status
+	 * R2, Length  : 136bit (really 128 bits with CRC snipped)
+	 * R3, Length  : 48bit (OCR register value)
+	 * R4, Length  : 48bit, SDIO_OP_CONDITION, Reverse SDIO Card
+	 * R5, Length  : 48bit, IO_RW_DIRECTION, Reverse SDIO Card
+	 * R6, Length  : 48bit (RCA register)
+	 * R7, Length  : 48bit (interface condition, VHS(voltage supplied),
+	 *                     check pattern, CRC7)
+	 */
+	switch (mmc_resp_type(cmd)) {
+	case MMC_RSP_R1: /* same index as R6 and R7 */
+		fire |= GLAMO_FIRE_MMC_RSPT_R1;
+		break;
+	case MMC_RSP_R1B:
+		fire |= GLAMO_FIRE_MMC_RSPT_R1b;
+		break;
+	case MMC_RSP_R2:
+		fire |= GLAMO_FIRE_MMC_RSPT_R2;
+		break;
+	case MMC_RSP_R3:
+		fire |= GLAMO_FIRE_MMC_RSPT_R3;
+		break;
+	/* R4 and R5 supported by chip not defined in linux/mmc/core.h (sdio) */
+	}
+	/*
+	 * From the command index, set up the command class in the host ctrllr
+	 *
+	 * missing guys present on chip but couldn't figure out how to use yet:
+	 *     0x0 "stream read"
+	 *     0x9 "cancel running command"
+	 */
+	switch (cmd->opcode) {
+	case MMC_READ_SINGLE_BLOCK:
+		fire |= GLAMO_FIRE_MMC_CC_SBR; /* single block read */
+		break;
+	case MMC_SWITCH: /* 64 byte payload */
+	case SD_APP_SD_STATUS:
+	case SD_APP_SEND_SCR:
+	case MMC_READ_MULTIPLE_BLOCK:
+		/* we will get an interrupt off this */
+		if (!cmd->mrq->stop) {
+			/* multiblock no stop */
+			fire |= GLAMO_FIRE_MMC_CC_MBRNS;
+		} else {
+			/* multiblock with stop */
+			fire |= GLAMO_FIRE_MMC_CC_MBRS;
+		}
+		break;
+	case MMC_WRITE_BLOCK:
+		fire |= GLAMO_FIRE_MMC_CC_SBW; /* single block write */
+		break;
+	case MMC_WRITE_MULTIPLE_BLOCK:
+		if (cmd->mrq->stop) {
+			/* multiblock with stop */
+			fire |= GLAMO_FIRE_MMC_CC_MBWS;
+		} else {
+			/* multiblock NO stop-- 'RESERVED'? */
+			fire |= GLAMO_FIRE_MMC_CC_MBWNS;
+		}
+		break;
+	case MMC_STOP_TRANSMISSION:
+		fire |= GLAMO_FIRE_MMC_CC_STOP; /* STOP */
+		triggers_int = 0;
+		break;
+	default:
+		fire |= GLAMO_FIRE_MMC_CC_BASIC; /* "basic command" */
+		triggers_int = 0;
+		break;
+	}
+
+	if (cmd->data)
+		host->mrq = cmd->mrq;
+
+	/* always largest timeout */
+	glamomci_reg_write(host, GLAMO_REG_MMC_TIMEOUT, 0xfff);
+
+	/* Generate interrupt on txfer */
+	glamomci_reg_set_bit_mask(host, GLAMO_REG_MMC_BASIC, 0xff36,
+			0x0800 |
+			GLAMO_BASIC_MMC_NO_CLK_RD_WAIT |
+			GLAMO_BASIC_MMC_EN_COMPL_INT |
+			GLAMO_BASIC_MMC_EN_DATA_PUPS |
+			GLAMO_BASIC_MMC_EN_CMD_PUP);
+
+	/* send the command out on the wire */
+	/* dev_info(&host->pdev->dev, "Using FIRE %04X\n", fire); */
+	glamomci_reg_write(host, GLAMO_REG_MMC_CMD_FIRE, fire);
+
+	/* we are deselecting card?  because it isn't going to ack then... */
+	if ((cmd->opcode == 7) && (cmd->arg == 0))
+		return;
+
+	/*
+	 * we must spin until response is ready or timed out
+	 * -- we don't get interrupts unless there is a bulk rx
+	 */
+	do
+		status = glamomci_reg_read(host, GLAMO_REG_MMC_RB_STAT1);
+	while (((((status >> 15) & 1) != (host->request_counter & 1)) ||
+		(!(status & (GLAMO_STAT1_MMC_RB_RRDY |
+			     GLAMO_STAT1_MMC_RTOUT |
+			     GLAMO_STAT1_MMC_DTOUT |
+			     GLAMO_STAT1_MMC_BWERR |
+			     GLAMO_STAT1_MMC_BRERR)))) && (timeout--));
+
+	if ((status & (GLAMO_STAT1_MMC_RTOUT | GLAMO_STAT1_MMC_DTOUT)) ||
+	    (timeout == 0)) {
+		cmd->error = -ETIMEDOUT;
+	} else if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR)) {
+		cmd->error = -EILSEQ;
+	}
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136) {
+			cmd->resp[3] = readw(&reg_resp[0]) |
+						   (readw(&reg_resp[1]) << 16);
+			cmd->resp[2] = readw(&reg_resp[2]) |
+						   (readw(&reg_resp[3]) << 16);
+			cmd->resp[1] = readw(&reg_resp[4]) |
+						   (readw(&reg_resp[5]) << 16);
+			cmd->resp[0] = readw(&reg_resp[6]) |
+						   (readw(&reg_resp[7]) << 16);
+		} else {
+			cmd->resp[0] = (readw(&reg_resp[0]) >> 8) |
+					   (readw(&reg_resp[1]) << 8) |
+					   (readw(&reg_resp[2]) << 24);
+		}
+	}
+}
+
+static int glamo_mci_prepare_pio(struct glamo_mci_host *host,
+		struct mmc_data *data)
+{
+	unsigned long addr = host->data_mem->start;
+
+	if (data->host_cookie & 1)
+		addr += resource_size(host->data_mem) / 2;
+
+	/* set up the block info */
+	glamomci_reg_write(host, GLAMO_REG_MMC_DATBLKLEN, data->blksz);
+	glamomci_reg_write(host, GLAMO_REG_MMC_DATBLKCNT, data->blocks);
+
+	if (data->flags & MMC_DATA_WRITE) {
+		glamomci_reg_write(host, GLAMO_REG_MMC_WDATADS1, addr);
+		glamomci_reg_write(host, GLAMO_REG_MMC_WDATADS2, addr >> 16);
+	} else {
+		glamomci_reg_write(host, GLAMO_REG_MMC_RDATADS1, addr);
+		glamomci_reg_write(host, GLAMO_REG_MMC_RDATADS2, addr >> 16);
+	}
+
+	if ((data->flags & MMC_DATA_WRITE) && !data->host_cookie)
+		do_pio_write(host, data);
+
+	dev_dbg(&host->pdev->dev, "(blksz=%d, count=%d)\n",
+				   data->blksz, data->blocks);
+	return 0;
+}
+
+static void glamo_mci_send_request(struct mmc_host *mmc,
+		struct mmc_request *mrq)
+{
+	struct glamo_mci_host *host = mmc_priv(mmc);
+	struct mmc_command *cmd = mrq->cmd;
+
+	host->request_counter++;
+	host->request_start = jiffies;
+
+	if (cmd->data) {
+		if (glamo_mci_prepare_pio(host, cmd->data)) {
+			cmd->error = -EIO;
+			cmd->data->error = -EIO;
+			goto done;
+		}
+	}
+
+	dev_dbg(&host->pdev->dev, "cmd 0x%x, "
+		 "arg 0x%x data=%p mrq->stop=%p flags 0x%x\n",
+		 cmd->opcode, cmd->arg, cmd->data, cmd->mrq->stop,
+		 cmd->flags);
+
+	glamo_mci_send_command(host, cmd);
+
+	/*
+	 * if we don't have bulk data to take care of, we're done
+	 */
+	if (!cmd->data || cmd->error)
+		goto done;
+
+	/*
+	 * Otherwise can can use the interrupt as async completion --
+	 * if there is read data coming, or we wait for write data to complete,
+	 * exit without mmc_request_done() as the payload interrupt
+	 * will service it
+	 */
+	dev_dbg(&host->pdev->dev, "Waiting for payload data\n");
+	return;
+done:
+	if (!cmd->error)
+		cmd->error = glamo_mci_wait_idle(host, jiffies + 2 * HZ);
+	glamo_mci_request_done(host, mrq);
+}
+
+static void glamo_mci_set_power_mode(struct glamo_mci_host *host,
+				     unsigned char power_mode)
+{
+	int ret;
+
+	if (power_mode == host->power_mode)
+		return;
+
+	switch (power_mode) {
+	case MMC_POWER_UP:
+		if (host->power_mode == MMC_POWER_OFF) {
+			ret = regulator_enable(host->regulator);
+			if (ret)
+				dev_err(&host->pdev->dev,
+					"Failed to enable regulator: %d\n",
+					ret);
+		}
+		break;
+	case MMC_POWER_ON:
+		break;
+	case MMC_POWER_OFF:
+	default:
+		ret = regulator_disable(host->regulator);
+		if (ret)
+			dev_warn(&host->pdev->dev,
+				"Failed to disable regulator: %d\n",
+				ret);
+		break;
+	}
+	host->power_mode = power_mode;
+}
+
+static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct glamo_mci_host *host = mmc_priv(mmc);
+	int bus_width = 0;
+	int rate;
+	int sd_drive;
+	int ret;
+
+	mmc_host_enable(mmc);
+
+	/* Set power */
+	glamo_mci_set_power_mode(host, ios->power_mode);
+
+	if (host->vdd != ios->vdd) {
+		ret = mmc_regulator_set_ocr(mmc, host->regulator, ios->vdd);
+		if (ret)
+			dev_err(&host->pdev->dev,
+				"Failed to set regulator voltage: %d\n", ret);
+		else
+			host->vdd = ios->vdd;
+	}
+
+	rate = glamo_mci_set_card_clock(host, ios->clock);
+
+	if ((ios->power_mode == MMC_POWER_ON) ||
+		(ios->power_mode == MMC_POWER_UP)) {
+		dev_info(&host->pdev->dev,
+			"powered (vdd = %hu) clk: %dkHz div=%hu (req: %ukHz). "
+			"Bus width=%d\n", ios->vdd,
+			rate / 1000, 0,
+			ios->clock / 1000, (int)ios->bus_width);
+	} else {
+		dev_info(&host->pdev->dev, "glamo_mci_set_ios: power down.\n");
+	}
+
+	/* set bus width */
+	if (ios->bus_width == MMC_BUS_WIDTH_4)
+		bus_width = GLAMO_BASIC_MMC_EN_4BIT_DATA;
+
+	sd_drive = (rate * 4) / host->clk_rate;
+	if (sd_drive > 3)
+		sd_drive = 3;
+
+	glamomci_reg_set_bit_mask(host, GLAMO_REG_MMC_BASIC,
+				GLAMO_BASIC_MMC_EN_4BIT_DATA | 0xc0,
+						   bus_width | sd_drive << 6);
+
+	if (host->power_mode == MMC_POWER_OFF)
+		mmc_host_disable(host->mmc);
+	else
+		mmc_host_lazy_disable(host->mmc);
+}
+
+static void glamo_mci_pre_request(struct mmc_host *mmc,
+		struct mmc_request *mrq, bool is_first_req)
+{
+	struct glamo_mci_host *host = mmc_priv(mmc);
+
+	mrq->data->host_cookie = (host->request_counter & 1) | 2;
+
+	/* if write, prep the write into the shared RAM before the command */
+	if (mrq->data->flags & MMC_DATA_WRITE)
+		do_pio_write(host, mrq->data);
+}
+
+static void glamo_mci_post_request(struct mmc_host *mmc,
+		struct mmc_request *mrq, int err)
+{
+	struct glamo_mci_host *host = mmc_priv(mmc);
+
+	if (!mrq->data->host_cookie)
+		return;
+
+	if (err)
+		return;
+
+	if (mrq->data->flags & MMC_DATA_READ)
+		do_pio_read(host, mrq->data);
+
+	mrq->data->host_cookie = 0;
+}
+
+static struct mmc_host_ops glamo_mci_ops = {
+	.enable		= glamo_mci_clock_enable,
+	.disable	= glamo_mci_clock_disable,
+	.request	= glamo_mci_send_request,
+	.post_req	= glamo_mci_post_request,
+	.pre_req	= glamo_mci_pre_request,
+	.set_ios	= glamo_mci_set_ios,
+};
+
+static int __devinit glamo_mci_probe(struct platform_device *pdev)
+{
+	struct mmc_host *mmc;
+	struct glamo_mci_host *host;
+	struct glamo_core *core = dev_get_drvdata(pdev->dev.parent);
+	int ret;
+
+	dev_info(&pdev->dev, "glamo_mci driver (C)2007 Openmoko, Inc\n");
+
+	mmc = mmc_alloc_host(sizeof(struct glamo_mci_host), &pdev->dev);
+	if (!mmc) {
+		ret = -ENOMEM;
+		goto probe_out;
+	}
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->pdev = pdev;
+	if (core->pdata)
+		host->pdata = core->pdata->mmc_data;
+	host->power_mode = MMC_POWER_OFF;
+	host->core = core;
+
+	host->irq = platform_get_irq(pdev, 0);
+
+	host->regulator = regulator_get(pdev->dev.parent, "SD_3V3");
+	if (IS_ERR(host->regulator)) {
+		dev_err(&pdev->dev, "Cannot proceed without regulator.\n");
+		ret = PTR_ERR(host->regulator);
+		goto probe_free_host;
+	}
+
+	host->mmio_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!host->mmio_mem) {
+		dev_err(&pdev->dev,
+			"failed to get io memory region resouce.\n");
+		ret = -ENOENT;
+		goto probe_regulator_put;
+	}
+
+	host->mmio_mem = request_mem_region(host->mmio_mem->start,
+					    resource_size(host->mmio_mem),
+					    pdev->name);
+
+	if (!host->mmio_mem) {
+		dev_err(&pdev->dev, "failed to request io memory region.\n");
+		ret = -ENOENT;
+		goto probe_regulator_put;
+	}
+
+	host->mmio_base = ioremap(host->mmio_mem->start,
+				  resource_size(host->mmio_mem));
+	if (!host->mmio_base) {
+		dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
+		ret = -EINVAL;
+		goto probe_free_mem_region_mmio;
+	}
+
+
+	/* Get ahold of our data buffer we use for data in and out on MMC */
+	host->data_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!host->data_mem) {
+		dev_err(&pdev->dev,
+			"failed to get io memory region resource.\n");
+		ret = -ENOENT;
+		goto probe_iounmap_mmio;
+	}
+
+	host->data_mem = request_mem_region(host->data_mem->start,
+					    resource_size(host->data_mem),
+					    pdev->name);
+
+	if (!host->data_mem) {
+		dev_err(&pdev->dev, "failed to request io memory region.\n");
+		ret = -ENOENT;
+		goto probe_iounmap_mmio;
+	}
+	host->data_base = ioremap(host->data_mem->start,
+				  resource_size(host->data_mem));
+
+	if (host->data_base == 0) {
+		dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
+		ret = -EINVAL;
+		goto probe_free_mem_region_data;
+	}
+
+	ret = request_threaded_irq(host->irq, NULL, glamo_mci_irq, IRQF_SHARED,
+				   pdev->name, host);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register irq.\n");
+		goto probe_iounmap_data;
+	}
+
+
+	host->vdd = 0;
+	host->clk_rate = glamo_pll_rate(host->core, GLAMO_PLL1);
+
+	/* explain our host controller capabilities */
+	mmc->ops	= &glamo_mci_ops;
+	mmc->ocr_avail	= mmc_regulator_get_ocrmask(host->regulator);
+	mmc->caps	= MMC_CAP_4_BIT_DATA |
+			    MMC_CAP_MMC_HIGHSPEED |
+			    MMC_CAP_SD_HIGHSPEED;
+
+	if (host->pdata->nonremovable)
+		mmc->caps |= MMC_CAP_NONREMOVABLE;
+
+	mmc->f_min	= host->clk_rate / 256;
+	mmc->f_max	= sd_max_clk;
+
+	mmc->max_blk_count = (1 << 16) - 1; /* GLAMO_REG_MMC_RB_BLKCNT */
+	mmc->max_blk_size  = (1 << 12) - 1; /* GLAMO_REG_MMC_RB_BLKLEN */
+	mmc->max_req_size  = resource_size(host->data_mem) / 2;
+	mmc->max_seg_size  = mmc->max_req_size;
+	mmc->max_segs = 128;
+
+	if (mmc->ocr_avail < 0) {
+		dev_warn(&pdev->dev,
+			"Failed to get ocr list for regulator: %d.\n",
+			mmc->ocr_avail);
+		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+	}
+
+	platform_set_drvdata(pdev, mmc);
+
+	mmc->caps |= MMC_CAP_DISABLE;
+	mmc_set_disable_delay(mmc, 1000 / 16);
+
+	mmc_host_enable(mmc);
+	glamo_mci_reset(host);
+
+	ret = mmc_add_host(mmc);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to add mmc host.\n");
+		goto probe_mmc_host_disable;
+	}
+
+	mmc_host_lazy_disable(mmc);
+
+	return 0;
+
+probe_mmc_host_disable:
+	mmc_host_disable(mmc);
+	free_irq(host->irq, host);
+probe_iounmap_data:
+	iounmap(host->data_base);
+probe_free_mem_region_data:
+	release_mem_region(host->data_mem->start,
+			   resource_size(host->data_mem));
+probe_iounmap_mmio:
+	iounmap(host->mmio_base);
+probe_free_mem_region_mmio:
+	release_mem_region(host->mmio_mem->start,
+			   resource_size(host->mmio_mem));
+probe_regulator_put:
+	regulator_put(host->regulator);
+probe_free_host:
+	mmc_free_host(mmc);
+probe_out:
+	return ret;
+}
+
+static int __devexit glamo_mci_remove(struct platform_device *pdev)
+{
+	struct mmc_host	*mmc = platform_get_drvdata(pdev);
+	struct glamo_mci_host *host = mmc_priv(mmc);
+
+	mmc_host_enable(mmc);
+	mmc_remove_host(mmc);
+	mmc_host_disable(mmc);
+
+	synchronize_irq(host->irq);
+	free_irq(host->irq, host);
+
+	iounmap(host->mmio_base);
+	iounmap(host->data_base);
+	release_mem_region(host->mmio_mem->start,
+				resource_size(host->mmio_mem));
+	release_mem_region(host->data_mem->start,
+				resource_size(host->data_mem));
+
+	regulator_put(host->regulator);
+
+	mmc_free_host(mmc);
+
+	return 0;
+}
+
+
+#ifdef CONFIG_PM
+
+static int glamo_mci_suspend(struct device *dev)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct glamo_mci_host *host = mmc_priv(mmc);
+	int ret;
+
+	disable_irq(host->irq);
+
+	mmc_host_enable(mmc);
+	ret = mmc_suspend_host(mmc);
+	mmc_host_disable(mmc);
+
+	return ret;
+}
+
+static int glamo_mci_resume(struct device *dev)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct glamo_mci_host *host = mmc_priv(mmc);
+	int ret;
+
+	mmc_host_enable(mmc);
+	glamo_mci_reset(host);
+	mdelay(10);
+
+	enable_irq(host->irq);
+
+	ret = mmc_resume_host(host->mmc);
+
+	mmc_host_lazy_disable(host->mmc);
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(glamo_mci_pm_ops, glamo_mci_suspend, glamo_mci_resume);
+#define GLAMO_MCI_PM_OPS (&glamo_mci_pm_ops)
+
+#else /* CONFIG_PM */
+#define GLAMO_MCI_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+
+static struct platform_driver glamo_mci_driver = {
+	.probe  = glamo_mci_probe,
+	.remove = __devexit_p(glamo_mci_remove),
+	.driver = {
+		.name	= "glamo-mci",
+		.owner	= THIS_MODULE,
+		.pm	= GLAMO_MCI_PM_OPS,
+	},
+};
+
+static int __init glamo_mci_init(void)
+{
+	platform_driver_register(&glamo_mci_driver);
+	return 0;
+}
+module_init(glamo_mci_init);
+
+static void __exit glamo_mci_exit(void)
+{
+	platform_driver_unregister(&glamo_mci_driver);
+}
+module_exit(glamo_mci_exit);
+
+MODULE_DESCRIPTION("Glamo MMC/SD Card Interface driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andy Green <andy@openmoko.com>");
+MODULE_ALIAS("platform:glamo-mci");
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index e6a8d8c..03ff799 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2341,6 +2341,23 @@ config FB_PUV3_UNIGFX
 	  Choose this option if you want to use the Unigfx device as a
 	  framebuffer device. Without the support of PCI & AGP.
 
+config FB_GLAMO
+	tristate "Smedia Glamo 336x/337x framebuffer support"
+	depends on FB && MFD_GLAMO
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	help
+	  Frame buffer driver for the LCD controller in the Smedia Glamo
+	  336x/337x.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted and removed from the running kernel whenever you want). The
+	  module will be called glamofb. If you want to compile it as a module,
+	  say M here and read <file:Documentation/modules.txt>.
+
+	  If unsure, say N.
+
 source "drivers/video/omap/Kconfig"
 source "drivers/video/omap2/Kconfig"
 
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 2ea44b6..c92cacf 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -140,6 +140,7 @@ obj-$(CONFIG_FB_MSM)              += msm/
 obj-$(CONFIG_FB_NUC900)           += nuc900fb.o
 obj-$(CONFIG_FB_JZ4740)		  += jz4740_fb.o
 obj-$(CONFIG_FB_PUV3_UNIGFX)      += fb-puv3.o
+obj-$(CONFIG_FB_GLAMO)            += glamo-fb.o
 
 # Platform or fallback drivers go here
 obj-$(CONFIG_FB_UVESA)            += uvesafb.o
diff --git a/drivers/video/glamo-fb.c b/drivers/video/glamo-fb.c
new file mode 100644
index 0000000..45f5a2b
--- /dev/null
+++ b/drivers/video/glamo-fb.c
@@ -0,0 +1,943 @@
+/* Smedia Glamo 336x/337x driver
+ *
+ * (C) 2007-2008 by Openmoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/mfd/glamo.h>
+#include <linux/mfd/glamo-core.h>
+#include <linux/mfd/glamo-regs.h>
+
+#include <asm/div64.h>
+
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
+#include <linux/glamofb.h>
+
+struct glamofb_handle {
+	struct glamo_core *core;
+	struct fb_info *fb;
+	struct device *dev;
+
+	struct resource *reg;
+	struct resource *fb_res;
+	void __iomem *base;
+	void __iomem *cursor_addr;
+
+	struct glamo_fb_platform_data *mach_info;
+
+	int cursor_on;
+	int blank_mode;
+	int mode_set; /* 0 if the current display mode hasn't been set on the glamo */
+	int output_enabled; /* 0 if the video output is disabled */
+
+	spinlock_t lock_cmd;
+	uint32_t pseudo_pal[16];
+};
+
+static void glamofb_program_mode(struct glamofb_handle *glamo);
+
+static void glamo_output_enable(struct glamofb_handle *gfb)
+{
+	struct glamo_core *gcore = gfb->core;
+
+	if (gfb->output_enabled)
+		return;
+
+	/* enable the pixel clock if off */
+	glamo_engine_enable(gcore, GLAMO_ENGINE_LCD);
+
+	gfb->output_enabled = 1;
+	if (!gfb->mode_set)
+		glamofb_program_mode(gfb);
+}
+
+static void glamo_output_disable(struct glamofb_handle *gfb)
+{
+	struct glamo_core *gcore = gfb->core;
+
+	if (!gfb->output_enabled)
+		return;
+
+	/* enable the pixel clock if off */
+	glamo_engine_suspend(gcore, GLAMO_ENGINE_LCD);
+
+	gfb->output_enabled = 0;
+}
+
+
+static inline int glamofb_reg_read(struct glamofb_handle *glamo, uint16_t reg)
+{
+	return readw(glamo->base + reg);
+}
+
+static inline void glamofb_reg_write(struct glamofb_handle *glamo, uint16_t reg,
+	uint16_t val)
+{
+	writew(val, glamo->base + reg);
+}
+
+static const struct glamo_script glamo_regs[] = {
+	{ GLAMO_REG_LCD_MODE1, 0x0020 },
+	/* no display rotation, no hardware cursor, no dither, no gamma,
+	 * no retrace flip, vsync low-active, hsync low active,
+	 * no TVCLK, no partial display, hw dest color from fb,
+	 * no partial display mode, LCD1, software flip,  */
+	{ GLAMO_REG_LCD_MODE2, 0x9020 },
+	  /* video flip, no ptr, no ptr, dhclk off,
+	   * normal mode,  no cpuif,
+	   * res, serial msb first, single fb, no fr ctrl,
+	   * cpu if bits all zero, no crc
+	   * 0000 0000 0010  0000 */
+	{ GLAMO_REG_LCD_MODE3, 0x0b40 },
+	  /* src data rgb565, res, 18bit rgb666
+	   * 000 01 011 0100 0000 */
+	{ GLAMO_REG_LCD_POLARITY, 0x440c },
+	  /* DE high active, no cpu/lcd if, cs0 force low, a0 low active,
+	   * np cpu if, 9bit serial data, sclk rising edge latch data
+	   * 01 00 0 100 0 000 01 0 0 */
+	/* The following values assume 640*480@16bpp */
+	{ GLAMO_REG_LCD_A_BASE1, 0x0000 }, /* display A base address 15:0 */
+	{ GLAMO_REG_LCD_A_BASE2, 0x4000 }, /* display A base address 22:16 */
+	{ GLAMO_REG_LCD_CURSOR_BASE1, 0xC000 }, /* cursor base address 15:0 */
+	{ GLAMO_REG_LCD_CURSOR_BASE2, 0x0012 }, /* cursor base address 22:16 */
+	{ GLAMO_REG_LCD_COMMAND2, 0x0000 }, /* display page A */
+};
+
+static int glamofb_run_script(struct glamofb_handle *glamo,
+	const struct glamo_script *script, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		const struct glamo_script *line = &script[i];
+
+		if (line->reg == 0xffff)
+			return 0;
+		else if (line->reg == 0xfffe)
+			msleep(line->val);
+		else
+			glamofb_reg_write(glamo, script[i].reg, script[i].val);
+	}
+
+	return 0;
+}
+
+static int glamofb_check_var(struct fb_var_screeninfo *var,
+	struct fb_info *info)
+{
+	struct glamofb_handle *glamo = info->par;
+
+	if (var->bits_per_pixel != 16)
+		var->bits_per_pixel = 16;
+
+	var->height = glamo->mach_info->height;
+	var->width = glamo->mach_info->width;
+
+	/* FIXME: set rgb positions */
+	switch (var->bits_per_pixel) {
+	case 16:
+		switch (glamofb_reg_read(glamo, GLAMO_REG_LCD_MODE3) & 0xc000) {
+		case GLAMO_LCD_SRC_RGB565:
+			var->red.offset		= 11;
+			var->green.offset	= 5;
+			var->blue.offset	= 0;
+			var->red.length		= 5;
+			var->green.length	= 6;
+			var->blue.length	= 5;
+			var->transp.length	= 0;
+			break;
+		case GLAMO_LCD_SRC_ARGB1555:
+			var->transp.offset	= 15;
+			var->red.offset		= 10;
+			var->green.offset	= 5;
+			var->blue.offset	= 0;
+			var->transp.length	= 1;
+			var->red.length		= 5;
+			var->green.length	= 5;
+			var->blue.length	= 5;
+			break;
+		case GLAMO_LCD_SRC_ARGB4444:
+			var->transp.offset	= 12;
+			var->red.offset		= 8;
+			var->green.offset	= 4;
+			var->blue.offset	= 0;
+			var->transp.length	= 4;
+			var->red.length		= 4;
+			var->green.length	= 4;
+			var->blue.length	= 4;
+			break;
+		}
+		break;
+	case 24:
+	case 32:
+	default:
+		/* The Smedia Glamo doesn't support anything but 16bit color */
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void glamofb_reg_set_bit_mask(struct glamofb_handle *glamo, uint16_t reg,
+	uint16_t mask, uint16_t val)
+{
+	uint16_t tmp;
+
+	val &= mask;
+
+	tmp = glamofb_reg_read(glamo, reg);
+	tmp &= ~mask;
+	tmp |= val;
+	glamofb_reg_write(glamo, reg, tmp);
+}
+
+#define GLAMO_LCD_WIDTH_MASK 0x03FF
+#define GLAMO_LCD_HEIGHT_MASK 0x03FF
+#define GLAMO_LCD_PITCH_MASK 0x07FE
+#define GLAMO_LCD_HV_TOTAL_MASK 0x03FF
+#define GLAMO_LCD_HV_RETR_START_MASK 0x03FF
+#define GLAMO_LCD_HV_RETR_END_MASK 0x03FF
+#define GLAMO_LCD_HV_RETR_DISP_START_MASK 0x03FF
+#define GLAMO_LCD_HV_RETR_DISP_END_MASK 0x03FF
+
+/* the caller has to ensure lock_cmd is held and we are in cmd mode */
+static void __rotate_lcd(struct glamofb_handle *glamo, uint32_t rotation)
+{
+	uint32_t glamo_rot;
+
+	switch (rotation) {
+	case FB_ROTATE_CW:
+		glamo_rot = GLAMO_LCD_ROT_MODE_90;
+		break;
+	case FB_ROTATE_UD:
+		glamo_rot = GLAMO_LCD_ROT_MODE_180;
+		break;
+	case FB_ROTATE_CCW:
+		glamo_rot = GLAMO_LCD_ROT_MODE_270;
+		break;
+	default:
+		glamo_rot = GLAMO_LCD_ROT_MODE_0;
+		break;
+	}
+
+	glamofb_reg_set_bit_mask(glamo,
+			 GLAMO_REG_LCD_WIDTH,
+			 GLAMO_LCD_ROT_MODE_MASK,
+			 glamo_rot);
+	glamofb_reg_set_bit_mask(glamo,
+			 GLAMO_REG_LCD_MODE1,
+			 GLAMO_LCD_MODE1_ROTATE_EN,
+			 (glamo_rot != GLAMO_LCD_ROT_MODE_0) ?
+				 GLAMO_LCD_MODE1_ROTATE_EN : 0);
+}
+
+static inline int glamofb_cmdq_empty(struct glamofb_handle *gfb)
+{
+	/* DGCMdQempty -- 1 == command queue is empty */
+	return glamofb_reg_read(gfb, GLAMO_REG_LCD_STATUS1) & (1 << 15);
+}
+
+/* call holding gfb->lock_cmd  when locking, until you unlock */
+static int glamofb_cmd_mode(struct glamofb_handle *gfb, int on)
+{
+	int timeout = 2000000;
+
+	dev_dbg(gfb->dev, "glamofb_cmd_mode(gfb=%p, on=%d)\n", gfb, on);
+	if (on) {
+		dev_dbg(gfb->dev, "%s: waiting for cmdq empty: ",
+			__func__);
+		while (!glamofb_cmdq_empty(gfb) && (timeout--))
+			cpu_relax();
+		if (timeout < 0) {
+			printk(KERN_ERR "glamofb cmd_queue never got empty\n");
+			return -EIO;
+		}
+		dev_dbg(gfb->dev, "empty!\n");
+
+		/* display the entire frame then switch to command */
+		glamofb_reg_write(gfb, GLAMO_REG_LCD_COMMAND1,
+			  GLAMO_LCD_CMD_TYPE_DISP |
+			  GLAMO_LCD_CMD_DATA_FIRE_VSYNC);
+
+		/* wait until lcd idle */
+		dev_dbg(gfb->dev, "waiting for lcd idle: ");
+		timeout = 2000000;
+		while (!(glamofb_reg_read(gfb, GLAMO_REG_LCD_STATUS2) & (1 << 12)) &&
+		      (timeout--))
+			cpu_relax();
+		if (timeout < 0) {
+			printk(KERN_ERR"*************"
+				       "glamofb lcd never idle"
+				       "*************\n");
+			return -EIO;
+		}
+
+		mdelay(100);
+
+		dev_dbg(gfb->dev, "cmd mode entered\n");
+
+	} else {
+		/* RGB interface needs vsync/hsync */
+		if (glamofb_reg_read(gfb, GLAMO_REG_LCD_MODE3) & GLAMO_LCD_MODE3_RGB)
+			glamofb_reg_write(gfb, GLAMO_REG_LCD_COMMAND1,
+				  GLAMO_LCD_CMD_TYPE_DISP |
+				  GLAMO_LCD_CMD_DATA_DISP_SYNC);
+
+		glamofb_reg_write(gfb, GLAMO_REG_LCD_COMMAND1,
+			  GLAMO_LCD_CMD_TYPE_DISP |
+			  GLAMO_LCD_CMD_DATA_DISP_FIRE);
+	}
+
+	return 0;
+}
+
+static void glamofb_program_mode(struct glamofb_handle *gfb)
+{
+	unsigned int sync, bp, disp, fp, total;
+	unsigned long flags;
+	struct glamo_core *gcore = gfb->core;
+	struct fb_var_screeninfo *var = &gfb->fb->var;
+
+	dev_dbg(&gcore->pdev->dev,
+			  "glamofb_program_mode spin_lock_irqsave\n");
+	spin_lock_irqsave(&gfb->lock_cmd, flags);
+
+	if (glamofb_cmd_mode(gfb, 1))
+		goto out_unlock;
+
+	if (var->pixclock)
+		glamo_engine_reclock(gcore, GLAMO_ENGINE_LCD,
+				 (1000000000UL / gfb->fb->var.pixclock) * 1000);
+
+	glamofb_reg_set_bit_mask(gfb,
+			 GLAMO_REG_LCD_WIDTH,
+			 GLAMO_LCD_WIDTH_MASK,
+			 var->xres);
+	glamofb_reg_set_bit_mask(gfb,
+			 GLAMO_REG_LCD_HEIGHT,
+			 GLAMO_LCD_HEIGHT_MASK,
+			 var->yres);
+	glamofb_reg_set_bit_mask(gfb,
+			 GLAMO_REG_LCD_PITCH,
+			 GLAMO_LCD_PITCH_MASK,
+			 gfb->fb->fix.line_length);
+
+	/* honour the rotation request */
+	__rotate_lcd(gfb, var->rotate);
+
+	/* update scannout timings */
+	sync = 0;
+	bp = sync + var->hsync_len;
+	disp = bp + var->left_margin;
+	fp = disp + var->xres;
+	total = fp + var->right_margin;
+
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_HORIZ_TOTAL,
+			 GLAMO_LCD_HV_TOTAL_MASK, total);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_HORIZ_RETR_START,
+			 GLAMO_LCD_HV_RETR_START_MASK, sync);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_HORIZ_RETR_END,
+			 GLAMO_LCD_HV_RETR_END_MASK, bp);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_HORIZ_DISP_START,
+			  GLAMO_LCD_HV_RETR_DISP_START_MASK, disp);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_HORIZ_DISP_END,
+			 GLAMO_LCD_HV_RETR_DISP_END_MASK, fp);
+
+	sync = 0;
+	bp = sync + var->vsync_len;
+	disp = bp + var->upper_margin;
+	fp = disp + var->yres;
+	total = fp + var->lower_margin;
+
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_VERT_TOTAL,
+			 GLAMO_LCD_HV_TOTAL_MASK, total);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_VERT_RETR_START,
+			  GLAMO_LCD_HV_RETR_START_MASK, sync);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_VERT_RETR_END,
+			 GLAMO_LCD_HV_RETR_END_MASK, bp);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_VERT_DISP_START,
+			 GLAMO_LCD_HV_RETR_DISP_START_MASK, disp);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_VERT_DISP_END,
+			 GLAMO_LCD_HV_RETR_DISP_END_MASK, fp);
+
+	glamofb_cmd_mode(gfb, 0);
+
+	gfb->mode_set = 1;
+
+out_unlock:
+	dev_dbg(&gcore->pdev->dev,
+		  "glamofb_program_mode spin_unlock_irqrestore\n");
+	spin_unlock_irqrestore(&gfb->lock_cmd, flags);
+}
+
+static struct fb_videomode *glamofb_find_mode(struct fb_info *info,
+	struct fb_var_screeninfo *var)
+{
+	struct glamofb_handle *glamo = info->par;
+	struct glamo_fb_platform_data *pdata = glamo->mach_info;
+	struct fb_videomode *mode;
+	int i;
+
+	for (i = pdata->num_modes, mode = pdata->modes; i; --i, ++mode) {
+		if (mode->xres == var->xres &&
+			mode->yres == var->yres)
+			return mode;
+	}
+
+	return NULL;
+}
+
+static int glamofb_set_par(struct fb_info *info)
+{
+	struct glamofb_handle *glamo = info->par;
+	struct fb_var_screeninfo *var = &info->var;
+	struct fb_videomode *mode;
+
+	mode = glamofb_find_mode(info, var);
+	if (!mode)
+		return -EINVAL;
+
+	fb_videomode_to_var(var, mode);
+
+	info->mode = mode;
+
+	glamo->mode_set = 0;
+
+	switch (var->rotate) {
+	case FB_ROTATE_CW:
+	case FB_ROTATE_CCW:
+		info->fix.line_length = (var->yres * var->bits_per_pixel) / 8;
+		/* FIXME: Limit pixelclock */
+		var->pixclock *= 2;
+		break;
+	default:
+		info->fix.line_length = (var->xres * var->bits_per_pixel) / 8;
+		break;
+	}
+
+	if (glamo->output_enabled)
+		glamofb_program_mode(glamo);
+
+	return 0;
+}
+
+static int glamofb_blank(int blank_mode, struct fb_info *info)
+{
+	struct glamofb_handle *gfb = info->par;
+
+	dev_dbg(gfb->dev, "glamofb_blank(%u)\n", blank_mode);
+
+	switch (blank_mode) {
+	case FB_BLANK_VSYNC_SUSPEND:
+	case FB_BLANK_HSYNC_SUSPEND:
+		/* FIXME: add pdata hook/flag to indicate whether
+		 * we should already switch off pixel clock here */
+		break;
+	case FB_BLANK_POWERDOWN:
+		/* disable the pixel clock */
+		glamo_output_disable(gfb);
+		gfb->blank_mode = blank_mode;
+		break;
+	case FB_BLANK_UNBLANK:
+	case FB_BLANK_NORMAL:
+		glamo_output_enable(gfb);
+		gfb->blank_mode = blank_mode;
+		break;
+	}
+
+	/* FIXME: once we have proper clock management in glamo-core,
+	 * we can determine if other units need MCLK1 or the PLL, and
+	 * disable it if not used. */
+	return 0;
+}
+
+static inline unsigned int chan_to_field(unsigned int chan,
+	struct fb_bitfield *bf)
+{
+	chan &= 0xffff;
+	chan >>= 16 - bf->length;
+	return chan << bf->offset;
+}
+
+static int glamofb_setcolreg(unsigned regno, unsigned red, unsigned green,
+	unsigned blue, unsigned transp, struct fb_info *info)
+{
+	struct glamofb_handle *glamo = info->par;
+	unsigned int val;
+
+	switch (glamo->fb->fix.visual) {
+	case FB_VISUAL_TRUECOLOR:
+	case FB_VISUAL_DIRECTCOLOR:
+		/* true-colour, use pseuo-palette */
+
+		if (regno < 16) {
+			u32 *pal = glamo->fb->pseudo_palette;
+
+			val  = chan_to_field(red, &glamo->fb->var.red);
+			val |= chan_to_field(green, &glamo->fb->var.green);
+			val |= chan_to_field(blue, &glamo->fb->var.blue);
+
+			pal[regno] = val;
+		};
+		break;
+	default:
+		return 1; /* unknown type */
+	}
+
+	return 0;
+}
+
+static int glamofb_ioctl(struct fb_info *info, unsigned int cmd,
+	unsigned long arg)
+{
+	struct glamofb_handle *gfb = (struct glamofb_handle *)info->par;
+	struct glamo_core *gcore = gfb->core;
+	int retval = -ENOTTY;
+
+	switch (cmd) {
+	case GLAMOFB_ENGINE_ENABLE:
+		retval = glamo_engine_enable(gcore, arg);
+		break;
+	case GLAMOFB_ENGINE_DISABLE:
+		retval = glamo_engine_disable(gcore, arg);
+		break;
+	case GLAMOFB_ENGINE_RESET:
+		glamo_engine_reset(gcore, arg);
+		retval = 0;
+		break;
+	default:
+		break;
+	}
+
+	return retval;
+}
+
+
+#ifdef CONFIG_MFD_GLAMO_HWACCEL
+static inline void glamofb_vsync_wait(struct glamofb_handle *glamo, int line,
+	int size, int range)
+{
+	int count[2];
+
+	do {
+		count[0] = glamofb_reg_read(glamo, GLAMO_REG_LCD_STATUS2) & 0x3ff;
+		count[1] = glamofb_reg_read(glamo, GLAMO_REG_LCD_STATUS2) & 0x3ff;
+	} while (count[0] != count[1] ||
+			(line < count[0] + range &&
+			 size > count[0] - range) ||
+			count[0] < range * 2);
+}
+
+/*
+ * Enable/disable the hardware cursor mode altogether
+ * (for blinking and such, use glamofb_cursor()).
+ */
+static void glamofb_cursor_onoff(struct glamofb_handle *glamo, int on)
+{
+	int y, size;
+
+	if (glamo->cursor_on) {
+		y = glamofb_reg_read(glamo, GLAMO_REG_LCD_CURSOR_Y_POS);
+		size = glamofb_reg_read(glamo, GLAMO_REG_LCD_CURSOR_Y_SIZE);
+
+		glamofb_vsync_wait(glamo, y, size, 30);
+	}
+
+	glamofb_reg_set_bit_mask(glamo, GLAMO_REG_LCD_MODE1,
+			GLAMO_LCD_MODE1_CURSOR_EN,
+			on ? GLAMO_LCD_MODE1_CURSOR_EN : 0);
+	glamo->cursor_on = on;
+
+	/* Hide the cursor by default */
+	glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_X_SIZE, 0);
+}
+
+static int glamofb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+	struct glamofb_handle *glamo = info->par;
+	unsigned long flags;
+
+	spin_lock_irqsave(&glamo->lock_cmd, flags);
+
+	glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_X_SIZE,
+			cursor->enable ? cursor->image.width : 0);
+
+	if (cursor->set & FB_CUR_SETPOS) {
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_X_POS,
+			  cursor->image.dx);
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_Y_POS,
+			  cursor->image.dy);
+	}
+
+	if (cursor->set & FB_CUR_SETCMAP) {
+		uint16_t fg = glamo->pseudo_pal[cursor->image.fg_color];
+		uint16_t bg = glamo->pseudo_pal[cursor->image.bg_color];
+
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_FG_COLOR, fg);
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_BG_COLOR, bg);
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_DST_COLOR, fg);
+	}
+
+	if (cursor->set & FB_CUR_SETHOT)
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_PRESET,
+				(cursor->hot.x << 8) | cursor->hot.y);
+
+	if ((cursor->set & FB_CUR_SETSIZE) ||
+	    (cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE))) {
+		int x, y, pitch, op;
+		const uint8_t *pcol = cursor->image.data;
+		const uint8_t *pmsk = cursor->mask;
+		uint8_t __iomem *dst = glamo->cursor_addr;
+		uint8_t dcol = 0;
+		uint8_t dmsk = 0;
+		uint8_t byte = 0;
+
+		if (cursor->image.depth > 1) {
+			spin_unlock_irqrestore(&glamo->lock_cmd, flags);
+			return -EINVAL;
+		}
+
+		pitch = ((cursor->image.width + 7) >> 2) & ~1;
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_PITCH,
+			pitch);
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_Y_SIZE,
+			cursor->image.height);
+
+		for (y = 0; y < cursor->image.height; y++) {
+			byte = 0;
+			for (x = 0; x < cursor->image.width; x++) {
+				if ((x % 8) == 0) {
+					dcol = *pcol++;
+					dmsk = *pmsk++;
+				} else {
+					dcol >>= 1;
+					dmsk >>= 1;
+				}
+
+				if (cursor->rop == ROP_COPY)
+					op = (dmsk & 1) ?
+						(dcol & 1) ? 1 : 3 : 0;
+				else
+					op = ((dmsk & 1) << 1) |
+						((dcol & 1) << 0);
+				byte |= op << ((x & 3) << 1);
+
+				if (x % 4 == 3) {
+					writeb(byte, dst + x / 4);
+					byte = 0;
+				}
+			}
+			if (x % 4) {
+				writeb(byte, dst + x / 4);
+				byte = 0;
+			}
+
+			dst += pitch;
+		}
+	}
+
+	spin_unlock_irqrestore(&glamo->lock_cmd, flags);
+
+	return 0;
+}
+#endif
+
+static struct fb_ops glamofb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_check_var	= glamofb_check_var,
+	.fb_set_par	= glamofb_set_par,
+	.fb_blank	= glamofb_blank,
+	.fb_setcolreg	= glamofb_setcolreg,
+	.fb_ioctl = glamofb_ioctl,
+#ifdef CONFIG_MFD_GLAMO_HWACCEL
+	.fb_cursor	= glamofb_cursor,
+#endif
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+};
+
+static int glamofb_init_regs(struct glamofb_handle *glamo)
+{
+	struct fb_info *info = glamo->fb;
+
+	glamofb_check_var(&info->var, info);
+	glamofb_run_script(glamo, glamo_regs, ARRAY_SIZE(glamo_regs));
+	glamofb_set_par(info);
+
+	return 0;
+}
+
+static int __devinit glamofb_probe(struct platform_device *pdev)
+{
+	int rc = -EIO;
+	struct fb_info *fbinfo;
+	struct glamofb_handle *glamofb;
+	struct glamo_core *core = dev_get_drvdata(pdev->dev.parent);
+	struct glamo_fb_platform_data *mach_info;
+
+	printk(KERN_INFO "SMEDIA Glamo frame buffer driver (C) 2007 "
+		"Openmoko, Inc.\n");
+
+	if (!core->pdata || !core->pdata->fb_data)
+		return -ENOENT;
+
+
+	fbinfo = framebuffer_alloc(sizeof(struct glamofb_handle), &pdev->dev);
+	if (!fbinfo)
+		return -ENOMEM;
+
+
+	glamofb = fbinfo->par;
+	glamofb->fb = fbinfo;
+	glamofb->dev = &pdev->dev;
+
+	glamofb->blank_mode = FB_BLANK_POWERDOWN;
+
+	strcpy(fbinfo->fix.id, "SMedia Glamo");
+
+	glamofb->reg = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						    "glamo-fb-regs");
+	if (!glamofb->reg) {
+		dev_err(&pdev->dev, "platform device with no registers?\n");
+		rc = -ENOENT;
+		goto out_free;
+	}
+
+	glamofb->fb_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+							"glamo-fb-mem");
+	if (!glamofb->fb_res) {
+		dev_err(&pdev->dev, "platform device with no memory ?\n");
+		rc = -ENOENT;
+		goto out_free;
+	}
+
+	glamofb->reg = request_mem_region(glamofb->reg->start,
+					      resource_size(glamofb->reg),
+					      pdev->name);
+	if (!glamofb->reg) {
+		dev_err(&pdev->dev, "failed to request mmio region\n");
+		goto out_free;
+	}
+
+	glamofb->fb_res = request_mem_region(glamofb->fb_res->start,
+						 resource_size(glamofb->fb_res),
+						 pdev->name);
+	if (!glamofb->fb_res) {
+		dev_err(&pdev->dev, "failed to request vram region\n");
+		goto out_release_reg;
+	}
+
+	/* we want to remap only the registers required for this core
+	 * driver. */
+	glamofb->base = ioremap_nocache(glamofb->reg->start,
+					resource_size(glamofb->reg));
+	if (!glamofb->base) {
+		dev_err(&pdev->dev, "failed to ioremap() mmio memory\n");
+		goto out_release_fb;
+	}
+
+	fbinfo->fix.smem_start = (unsigned long)glamofb->fb_res->start;
+	fbinfo->fix.smem_len = (__u32)resource_size(glamofb->fb_res);
+
+	fbinfo->screen_base = ioremap(glamofb->fb_res->start,
+					   resource_size(glamofb->fb_res));
+	if (!fbinfo->screen_base) {
+		dev_err(&pdev->dev, "failed to ioremap() vram memory\n");
+		goto out_release_fb;
+	}
+	glamofb->cursor_addr = fbinfo->screen_base + 0x12C000;
+
+	platform_set_drvdata(pdev, glamofb);
+
+	mach_info = core->pdata->fb_data;
+	glamofb->core = core;
+	glamofb->mach_info = mach_info;
+
+	fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
+	fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
+	fbinfo->fix.type_aux = 0;
+	fbinfo->fix.xpanstep = 0;
+	fbinfo->fix.ypanstep = 0;
+	fbinfo->fix.ywrapstep = 0;
+	fbinfo->fix.accel = FB_ACCEL_GLAMO;
+
+
+	fbinfo->fbops = &glamofb_ops;
+	fbinfo->flags = FBINFO_FLAG_DEFAULT;
+	fbinfo->pseudo_palette = &glamofb->pseudo_pal;
+
+	fbinfo->mode = mach_info->modes;
+	fb_videomode_to_var(&fbinfo->var, fbinfo->mode);
+	fbinfo->var.bits_per_pixel = 16;
+	fbinfo->var.nonstd = 0;
+	fbinfo->var.activate = FB_ACTIVATE_NOW;
+	fbinfo->var.height = mach_info->height;
+	fbinfo->var.width = mach_info->width;
+	fbinfo->var.accel_flags = 0;
+	fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
+
+	glamo_engine_enable(core, GLAMO_ENGINE_LCD);
+	glamo_engine_reset(core, GLAMO_ENGINE_LCD);
+	glamofb->output_enabled = 1;
+	glamofb->mode_set = 1;
+
+	dev_info(&pdev->dev, "spin_lock_init\n");
+	spin_lock_init(&glamofb->lock_cmd);
+	glamofb_init_regs(glamofb);
+#ifdef CONFIG_MFD_GLAMO_HWACCEL
+	glamofb_cursor_onoff(glamofb, 1);
+#endif
+
+	fb_videomode_to_modelist(mach_info->modes, mach_info->num_modes,
+				 &fbinfo->modelist);
+
+	rc = register_framebuffer(fbinfo);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "failed to register framebuffer\n");
+		goto out_unmap_fb;
+	}
+
+	printk(KERN_INFO "fb%d: %s frame buffer device\n",
+		fbinfo->node, fbinfo->fix.id);
+
+	return 0;
+
+out_unmap_fb:
+	iounmap(fbinfo->screen_base);
+	iounmap(glamofb->base);
+out_release_fb:
+	release_mem_region(glamofb->fb_res->start,
+				resource_size(glamofb->fb_res));
+out_release_reg:
+	release_mem_region(glamofb->reg->start,
+				resource_size(glamofb->reg));
+out_free:
+	framebuffer_release(fbinfo);
+	return rc;
+}
+
+static int __devexit glamofb_remove(struct platform_device *pdev)
+{
+	struct glamofb_handle *glamofb = platform_get_drvdata(pdev);
+
+	iounmap(glamofb->fb->screen_base);
+	iounmap(glamofb->base);
+
+	release_mem_region(glamofb->fb_res->start,
+				resource_size(glamofb->fb_res));
+	release_mem_region(glamofb->reg->start, resource_size(glamofb->reg));
+
+	platform_set_drvdata(pdev, NULL);
+	framebuffer_release(glamofb->fb);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int glamofb_suspend(struct device *dev)
+{
+	struct glamofb_handle *gfb = dev_get_drvdata(dev);
+
+	console_lock();
+	fb_set_suspend(gfb->fb, 1);
+	console_unlock();
+
+	/* seriously -- nobody is allowed to touch glamo memory when we
+	 * are suspended or we lock on nWAIT
+	 */
+	/* iounmap(gfb->fb->screen_base); */
+
+	return 0;
+}
+
+static int glamofb_resume(struct device *dev)
+{
+	struct glamofb_handle *gfb = dev_get_drvdata(dev);
+
+	/* OK let's allow framebuffer ops again */
+	/* gfb->fb->screen_base = ioremap(gfb->fb_res->start,
+					   resource_size(gfb->fb_res)); */
+	glamo_engine_enable(gfb->core, GLAMO_ENGINE_LCD);
+	glamo_engine_reset(gfb->core, GLAMO_ENGINE_LCD);
+
+	glamofb_init_regs(gfb);
+#ifdef CONFIG_MFD_GLAMO_HWACCEL
+	glamofb_cursor_onoff(gfb, 1);
+#endif
+
+	console_lock();
+	fb_set_suspend(gfb->fb, 0);
+	console_unlock();
+
+	return 0;
+}
+
+static const struct dev_pm_ops glamofb_pm_ops = {
+	.suspend = glamofb_suspend,
+	.resume = glamofb_resume,
+};
+
+#define GLAMOFB_PM_OPS (&glamofb_pm_ops)
+
+#else
+#define GLAMOFB_PM_OPS NULL
+#endif
+
+static struct platform_driver glamofb_driver = {
+	.probe		= glamofb_probe,
+	.remove		= __devexit_p(glamofb_remove),
+	.driver		= {
+		.name	= "glamo-fb",
+		.owner	= THIS_MODULE,
+		.pm	= GLAMOFB_PM_OPS
+	},
+};
+
+static int __init glamofb_init(void)
+{
+	return platform_driver_register(&glamofb_driver);
+}
+module_init(glamofb_init);
+
+static void __exit glamofb_cleanup(void)
+{
+	platform_driver_unregister(&glamofb_driver);
+}
+module_exit(glamofb_cleanup);
+
+MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
+MODULE_DESCRIPTION("Smedia Glamo 336x/337x framebuffer driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:glamo-fb");
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 6a82748..6965285 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -135,6 +135,7 @@
 #define FB_ACCEL_NEOMAGIC_NM2360 97	/* NeoMagic NM2360              */
 #define FB_ACCEL_NEOMAGIC_NM2380 98	/* NeoMagic NM2380              */
 #define FB_ACCEL_PXA3XX		 99	/* PXA3xx			*/
+#define FB_ACCEL_GLAMO		100	/* Smedia Glamo 3362            */
 
 #define FB_ACCEL_SAVAGE4        0x80	/* S3 Savage4                   */
 #define FB_ACCEL_SAVAGE3D       0x81	/* S3 Savage3D                  */
diff --git a/include/linux/glamofb.h b/include/linux/glamofb.h
new file mode 100644
index 0000000..896579b
--- /dev/null
+++ b/include/linux/glamofb.h
@@ -0,0 +1,21 @@
+#ifndef _LINUX_GLAMOFB_H
+#define _LINUX_GLAMOFB_H
+
+#ifdef __KERNEL__
+
+#include <linux/fb.h>
+
+struct glamo_fb_platform_data {
+    int width, height;
+
+    int num_modes;
+    struct fb_videomode *modes;
+};
+
+#endif
+
+#define GLAMOFB_ENGINE_ENABLE _IOW('F', 0x1, __u32)
+#define GLAMOFB_ENGINE_DISABLE _IOW('F', 0x2, __u32)
+#define GLAMOFB_ENGINE_RESET _IOW('F', 0x3, __u32)
+
+#endif
diff --git a/include/linux/mfd/glamo-core.h b/include/linux/mfd/glamo-core.h
new file mode 100644
index 0000000..8275a2f
--- /dev/null
+++ b/include/linux/mfd/glamo-core.h
@@ -0,0 +1,58 @@
+#ifndef __GLAMO_CORE_H
+#define __GLAMO_CORE_H
+
+#include <linux/mfd/glamo.h>
+
+/* for the time being, we put the on-screen framebuffer into the lowest
+ * VRAM space.  This should make the code easily compatible with the various
+ * 2MB/4MB/8MB variants of the Smedia chips */
+#define GLAMO_OFFSET_VRAM	0x800000
+#define GLAMO_OFFSET_FB	(GLAMO_OFFSET_VRAM)
+
+/* we only allocate the minimum possible size for the framebuffer to make
+ * sure we have sufficient memory for other functions of the chip */
+/*#define GLAMO_FB_SIZE	(640*480*4)	*//* == 0x12c000 */
+#define GLAMO_INTERNAL_RAM_SIZE 0x800000
+#define GLAMO_MMC_BUFFER_SIZE (64 * 1024)
+#define GLAMO_FB_SIZE	(GLAMO_INTERNAL_RAM_SIZE - GLAMO_MMC_BUFFER_SIZE)
+
+enum glamo_pll {
+	GLAMO_PLL1,
+	GLAMO_PLL2,
+};
+
+enum glamo_engine_state {
+	GLAMO_ENGINE_DISABLED,
+	GLAMO_ENGINE_SUSPENDED,
+	GLAMO_ENGINE_ENABLED,
+};
+
+struct glamo_core {
+	int irq;
+	int irq_base;
+	struct resource *mem;
+	void __iomem *base;
+	struct platform_device *pdev;
+	struct glamo_platform_data *pdata;
+	enum glamo_engine_state engine_state[__NUM_GLAMO_ENGINES];
+	spinlock_t lock;
+	uint16_t saved_irq_mask;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_dir;
+#endif
+};
+
+struct glamo_script {
+	uint16_t reg;
+	uint16_t val;
+};
+
+int glamo_pll_rate(struct glamo_core *glamo, enum glamo_pll pll);
+
+int glamo_engine_enable(struct glamo_core *glamo, enum glamo_engine engine);
+int glamo_engine_suspend(struct glamo_core *glamo, enum glamo_engine engine);
+int glamo_engine_disable(struct glamo_core *glamo, enum glamo_engine engine);
+void glamo_engine_reset(struct glamo_core *glamo, enum glamo_engine engine);
+int glamo_engine_reclock(struct glamo_core *glamo,
+			 enum glamo_engine engine, int ps);
+#endif /* __GLAMO_CORE_H */
diff --git a/include/linux/mfd/glamo-regs.h b/include/linux/mfd/glamo-regs.h
new file mode 100644
index 0000000..59848e1
--- /dev/null
+++ b/include/linux/mfd/glamo-regs.h
@@ -0,0 +1,630 @@
+#ifndef _GLAMO_REGS_H
+#define _GLAMO_REGS_H
+
+/* Smedia Glamo 336x/337x driver
+ *
+ * (C) 2007 by Openmoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+enum glamo_regster_offsets {
+	GLAMO_REGOFS_GENERIC	= 0x0000,
+	GLAMO_REGOFS_HOSTBUS	= 0x0200,
+	GLAMO_REGOFS_MEMORY	= 0x0300,
+	GLAMO_REGOFS_VIDCAP	= 0x0400,
+	GLAMO_REGOFS_ISP	= 0x0500,
+	GLAMO_REGOFS_JPEG	= 0x0800,
+	GLAMO_REGOFS_MPEG	= 0x0c00,
+	GLAMO_REGOFS_LCD	= 0x1100,
+	GLAMO_REGOFS_MMC	= 0x1400,
+	GLAMO_REGOFS_MPROC0	= 0x1500,
+	GLAMO_REGOFS_MPROC1	= 0x1580,
+	GLAMO_REGOFS_CMDQUEUE	= 0x1600,
+	GLAMO_REGOFS_RISC	= 0x1680,
+	GLAMO_REGOFS_2D		= 0x1700,
+	GLAMO_REGOFS_3D		= 0x1b00,
+	GLAMO_REGOFS_END	= 0x2400,
+};
+
+
+enum glamo_register_generic {
+	GLAMO_REG_GCONF1	= 0x0000,
+	GLAMO_REG_GCONF2	= 0x0002,
+#define	GLAMO_REG_DEVICE_ID	GLAMO_REG_GCONF2
+	GLAMO_REG_GCONF3	= 0x0004,
+#define	GLAMO_REG_REVISION_ID	GLAMO_REG_GCONF3
+	GLAMO_REG_IRQ_GEN1	= 0x0006,
+#define GLAMO_REG_IRQ_ENABLE	GLAMO_REG_IRQ_GEN1
+	GLAMO_REG_IRQ_GEN2	= 0x0008,
+#define GLAMO_REG_IRQ_SET	GLAMO_REG_IRQ_GEN2
+	GLAMO_REG_IRQ_GEN3	= 0x000a,
+#define GLAMO_REG_IRQ_CLEAR	GLAMO_REG_IRQ_GEN3
+	GLAMO_REG_IRQ_GEN4	= 0x000c,
+#define GLAMO_REG_IRQ_STATUS	GLAMO_REG_IRQ_GEN4
+	GLAMO_REG_CLOCK_HOST	= 0x0010,
+	GLAMO_REG_CLOCK_MEMORY	= 0x0012,
+	GLAMO_REG_CLOCK_LCD	= 0x0014,
+	GLAMO_REG_CLOCK_MMC	= 0x0016,
+	GLAMO_REG_CLOCK_ISP	= 0x0018,
+	GLAMO_REG_CLOCK_JPEG	= 0x001a,
+	GLAMO_REG_CLOCK_3D	= 0x001c,
+	GLAMO_REG_CLOCK_2D	= 0x001e,
+	GLAMO_REG_CLOCK_RISC1	= 0x0020,	/* 3365 only? */
+	GLAMO_REG_CLOCK_RISC2	= 0x0022,	/* 3365 only? */
+	GLAMO_REG_CLOCK_MPEG	= 0x0024,
+	GLAMO_REG_CLOCK_MPROC	= 0x0026,
+
+	GLAMO_REG_CLOCK_GEN5_1	= 0x0030,
+	GLAMO_REG_CLOCK_GEN5_2	= 0x0032,
+	GLAMO_REG_CLOCK_GEN6	= 0x0034,
+	GLAMO_REG_CLOCK_GEN7	= 0x0036,
+	GLAMO_REG_CLOCK_GEN8	= 0x0038,
+	GLAMO_REG_CLOCK_GEN9	= 0x003a,
+	GLAMO_REG_CLOCK_GEN10	= 0x003c,
+	GLAMO_REG_CLOCK_GEN11	= 0x003e,
+	GLAMO_REG_PLL_GEN1	= 0x0040,
+	GLAMO_REG_PLL_GEN2	= 0x0042,
+	GLAMO_REG_PLL_GEN3	= 0x0044,
+	GLAMO_REG_PLL_GEN4	= 0x0046,
+	GLAMO_REG_PLL_GEN5	= 0x0048,
+	GLAMO_REG_GPIO_GEN1	= 0x0050,
+	GLAMO_REG_GPIO_GEN2	= 0x0052,
+	GLAMO_REG_GPIO_GEN3	= 0x0054,
+	GLAMO_REG_GPIO_GEN4	= 0x0056,
+	GLAMO_REG_GPIO_GEN5	= 0x0058,
+	GLAMO_REG_GPIO_GEN6	= 0x005a,
+	GLAMO_REG_GPIO_GEN7	= 0x005c,
+	GLAMO_REG_GPIO_GEN8	= 0x005e,
+	GLAMO_REG_GPIO_GEN9	= 0x0060,
+	GLAMO_REG_GPIO_GEN10	= 0x0062,
+	GLAMO_REG_DFT_GEN1	= 0x0070,
+	GLAMO_REG_DFT_GEN2	= 0x0072,
+	GLAMO_REG_DFT_GEN3	= 0x0074,
+	GLAMO_REG_DFT_GEN4	= 0x0076,
+
+	GLAMO_REG_DFT_GEN5	= 0x01e0,
+	GLAMO_REG_DFT_GEN6	= 0x01f0,
+};
+
+#define GLAMO_REG_HOSTBUS(x)	(GLAMO_REGOFS_HOSTBUS-2+(x*2))
+
+#define REG_MEM(x)		(GLAMO_REGOFS_MEMORY+(x))
+#define GLAMO_REG_MEM_TIMING(x)	(GLAMO_REG_MEM_TIMING1-2+(x*2))
+
+enum glamo_register_mem {
+	GLAMO_REG_MEM_TYPE	= REG_MEM(0x00),
+	GLAMO_REG_MEM_GEN	= REG_MEM(0x02),
+	GLAMO_REG_MEM_TIMING1	= REG_MEM(0x04),
+	GLAMO_REG_MEM_TIMING2	= REG_MEM(0x06),
+	GLAMO_REG_MEM_TIMING3	= REG_MEM(0x08),
+	GLAMO_REG_MEM_TIMING4	= REG_MEM(0x0a),
+	GLAMO_REG_MEM_TIMING5	= REG_MEM(0x0c),
+	GLAMO_REG_MEM_TIMING6	= REG_MEM(0x0e),
+	GLAMO_REG_MEM_TIMING7	= REG_MEM(0x10),
+	GLAMO_REG_MEM_TIMING8	= REG_MEM(0x12),
+	GLAMO_REG_MEM_TIMING9	= REG_MEM(0x14),
+	GLAMO_REG_MEM_TIMING10	= REG_MEM(0x16),
+	GLAMO_REG_MEM_TIMING11	= REG_MEM(0x18),
+	GLAMO_REG_MEM_POWER1	= REG_MEM(0x1a),
+	GLAMO_REG_MEM_POWER2	= REG_MEM(0x1c),
+	GLAMO_REG_MEM_LCD_BUF1	= REG_MEM(0x1e),
+	GLAMO_REG_MEM_LCD_BUF2	= REG_MEM(0x20),
+	GLAMO_REG_MEM_LCD_BUF3	= REG_MEM(0x22),
+	GLAMO_REG_MEM_LCD_BUF4	= REG_MEM(0x24),
+	GLAMO_REG_MEM_BIST1	= REG_MEM(0x26),
+	GLAMO_REG_MEM_BIST2	= REG_MEM(0x28),
+	GLAMO_REG_MEM_BIST3	= REG_MEM(0x2a),
+	GLAMO_REG_MEM_BIST4	= REG_MEM(0x2c),
+	GLAMO_REG_MEM_BIST5	= REG_MEM(0x2e),
+	GLAMO_REG_MEM_MAH1	= REG_MEM(0x30),
+	GLAMO_REG_MEM_MAH2	= REG_MEM(0x32),
+	GLAMO_REG_MEM_DRAM1	= REG_MEM(0x34),
+	GLAMO_REG_MEM_DRAM2	= REG_MEM(0x36),
+	GLAMO_REG_MEM_CRC	= REG_MEM(0x38),
+};
+
+#define GLAMO_MEM_TYPE_MASK	0x03
+
+enum glamo_reg_mem_dram1 {
+	/* b0 - b10 == refresh period, 1 -> 2048 clocks */
+	GLAMO_MEM_DRAM1_EN_GATE_CLK	= (1 << 11),
+	GLAMO_MEM_DRAM1_SELF_REFRESH	= (1 << 12),
+	GLAMO_MEM_DRAM1_EN_GATE_CKE	= (1 << 13),
+	GLAMO_MEM_DRAM1_EN_DRAM_REFRESH	= (1 << 14),
+	GLAMO_MEM_DRAM1_EN_MODEREG_SET	= (1 << 15),
+};
+
+enum glamo_reg_mem_dram2 {
+	GLAMO_MEM_DRAM2_DEEP_PWRDOWN	= (1 << 12),
+};
+
+enum glamo_irq_index {
+	GLAMO_IRQIDX_HOSTBUS	= 0,
+	GLAMO_IRQIDX_JPEG	= 1,
+	GLAMO_IRQIDX_MPEG	= 2,
+	GLAMO_IRQIDX_MPROC1	= 3,
+	GLAMO_IRQIDX_MPROC0	= 4,
+	GLAMO_IRQIDX_CMDQUEUE	= 5,
+	GLAMO_IRQIDX_2D		= 6,
+	GLAMO_IRQIDX_MMC	= 7,
+	GLAMO_IRQIDX_RISC	= 8,
+};
+
+enum glamo_irq {
+	GLAMO_IRQ_HOSTBUS	= (1 << GLAMO_IRQIDX_HOSTBUS),
+	GLAMO_IRQ_JPEG		= (1 << GLAMO_IRQIDX_JPEG),
+	GLAMO_IRQ_MPEG		= (1 << GLAMO_IRQIDX_MPEG),
+	GLAMO_IRQ_MPROC1	= (1 << GLAMO_IRQIDX_MPROC1),
+	GLAMO_IRQ_MPROC0	= (1 << GLAMO_IRQIDX_MPROC0),
+	GLAMO_IRQ_CMDQUEUE	= (1 << GLAMO_IRQIDX_CMDQUEUE),
+	GLAMO_IRQ_2D		= (1 << GLAMO_IRQIDX_2D),
+	GLAMO_IRQ_MMC		= (1 << GLAMO_IRQIDX_MMC),
+	GLAMO_IRQ_RISC		= (1 << GLAMO_IRQIDX_RISC),
+};
+
+enum glamo_reg_clock_host {
+	GLAMO_CLOCK_HOST_DG_BCLK	= 0x0001,
+	GLAMO_CLOCK_HOST_DG_M0CLK	= 0x0004,
+	GLAMO_CLOCK_HOST_RESET		= 0x1000,
+};
+
+enum glamo_reg_clock_mem {
+	GLAMO_CLOCK_MEM_DG_M1CLK	= 0x0001,
+	GLAMO_CLOCK_MEM_EN_M1CLK	= 0x0002,
+	GLAMO_CLOCK_MEM_DG_MOCACLK	= 0x0004,
+	GLAMO_CLOCK_MEM_EN_MOCACLK	= 0x0008,
+	GLAMO_CLOCK_MEM_RESET		= 0x1000,
+	GLAMO_CLOCK_MOCA_RESET		= 0x2000,
+};
+
+enum glamo_reg_clock_lcd {
+	GLAMO_CLOCK_LCD_DG_DCLK		= 0x0001,
+	GLAMO_CLOCK_LCD_EN_DCLK		= 0x0002,
+	GLAMO_CLOCK_LCD_DG_DMCLK	= 0x0004,
+	GLAMO_CLOCK_LCD_EN_DMCLK	= 0x0008,
+	GLAMO_CLOCK_LCD_EN_DHCLK	= 0x0020,
+	GLAMO_CLOCK_LCD_DG_M5CLK	= 0x0040,
+	GLAMO_CLOCK_LCD_EN_M5CLK	= 0x0080,
+	GLAMO_CLOCK_LCD_RESET		= 0x1000,
+};
+
+enum glamo_reg_clock_mmc {
+	GLAMO_CLOCK_MMC_DG_TCLK		= 0x0001,
+	GLAMO_CLOCK_MMC_EN_TCLK		= 0x0002,
+	GLAMO_CLOCK_MMC_DG_M9CLK	= 0x0004,
+	GLAMO_CLOCK_MMC_EN_M9CLK	= 0x0008,
+	GLAMO_CLOCK_MMC_RESET		= 0x1000,
+};
+
+enum glamo_reg_basic_mmc {
+	/* set to disable CRC error rejection */
+	GLAMO_BASIC_MMC_DISABLE_CRC	= 0x0001,
+	/* enable completion interrupt */
+	GLAMO_BASIC_MMC_EN_COMPL_INT	= 0x0002,
+	/* stop MMC clock while enforced idle waiting for data from card */
+	GLAMO_BASIC_MMC_NO_CLK_RD_WAIT	= 0x0004,
+	/* 0 = 1-bit bus to card, 1 = use 4-bit bus (has to be negotiated) */
+	GLAMO_BASIC_MMC_EN_4BIT_DATA	= 0x0008,
+	/* enable 75K pullups on D3..D0 */
+	GLAMO_BASIC_MMC_EN_DATA_PUPS	= 0x0010,
+	/* enable 75K pullup on CMD */
+	GLAMO_BASIC_MMC_EN_CMD_PUP	= 0x0020,
+	/* IO drive strength 00=weak -> 11=strongest */
+	GLAMO_BASIC_MMC_EN_DR_STR0	= 0x0040,
+	GLAMO_BASIC_MMC_EN_DR_STR1	= 0x0080,
+	/* TCLK delay stage A, 0000 = 500ps --> 1111 = 8ns */
+	GLAMO_BASIC_MMC_EN_TCLK_DLYA0	= 0x0100,
+	GLAMO_BASIC_MMC_EN_TCLK_DLYA1	= 0x0200,
+	GLAMO_BASIC_MMC_EN_TCLK_DLYA2	= 0x0400,
+	GLAMO_BASIC_MMC_EN_TCLK_DLYA3	= 0x0800,
+	/* TCLK delay stage B (cumulative), 0000 = 500ps --> 1111 = 8ns */
+	GLAMO_BASIC_MMC_EN_TCLK_DLYB0	= 0x1000,
+	GLAMO_BASIC_MMC_EN_TCLK_DLYB1	= 0x2000,
+	GLAMO_BASIC_MMC_EN_TCLK_DLYB2	= 0x4000,
+	GLAMO_BASIC_MMC_EN_TCLK_DLYB3	= 0x8000,
+};
+
+enum glamo_reg_stat1_mmc {
+	/* command "counter" (really: toggle) */
+	GLAMO_STAT1_MMC_CMD_CTR	= 0x8000,
+	/* engine is idle */
+	GLAMO_STAT1_MMC_IDLE	= 0x4000,
+	/* readback response is ready */
+	GLAMO_STAT1_MMC_RB_RRDY	= 0x0200,
+	/* readback data is ready */
+	GLAMO_STAT1_MMC_RB_DRDY	= 0x0100,
+	/* no response timeout */
+	GLAMO_STAT1_MMC_RTOUT	= 0x0020,
+	/* no data timeout */
+	GLAMO_STAT1_MMC_DTOUT	= 0x0010,
+	/* CRC error on block write */
+	GLAMO_STAT1_MMC_BWERR	= 0x0004,
+	/* CRC error on block read */
+	GLAMO_STAT1_MMC_BRERR	= 0x0002
+};
+
+enum glamo_reg_fire_mmc {
+	/* command "counter" (really: toggle)
+	 * the STAT1 register reflects this so you can ensure you don't look
+	 * at status for previous command
+	 */
+	GLAMO_FIRE_MMC_CMD_CTR	= 0x8000,
+	/* sets kind of response expected */
+	GLAMO_FIRE_MMC_RES_MASK	= 0x0700,
+	/* sets command type */
+	GLAMO_FIRE_MMC_TYP_MASK	= 0x00C0,
+	/* sets command class */
+	GLAMO_FIRE_MMC_CLS_MASK	= 0x000F,
+};
+
+enum glamo_fire_mmc_response_types {
+	GLAMO_FIRE_MMC_RSPT_R1	= 0x0000,
+	GLAMO_FIRE_MMC_RSPT_R1b	= 0x0100,
+	GLAMO_FIRE_MMC_RSPT_R2	= 0x0200,
+	GLAMO_FIRE_MMC_RSPT_R3	= 0x0300,
+	GLAMO_FIRE_MMC_RSPT_R4	= 0x0400,
+	GLAMO_FIRE_MMC_RSPT_R5	= 0x0500,
+};
+
+enum glamo_fire_mmc_command_types {
+	/* broadcast, no response */
+	GLAMO_FIRE_MMC_CMDT_BNR	= 0x0000,
+	/* broadcast, with response */
+	GLAMO_FIRE_MMC_CMDT_BR	= 0x0040,
+	/* addressed, no data */
+	GLAMO_FIRE_MMC_CMDT_AND	= 0x0080,
+	/* addressed, with data */
+	GLAMO_FIRE_MMC_CMDT_AD	= 0x00C0,
+};
+
+enum glamo_fire_mmc_command_class {
+	/* "Stream Read" */
+	GLAMO_FIRE_MMC_CC_STRR	= 0x0000,
+	/* Single Block Read */
+	GLAMO_FIRE_MMC_CC_SBR	= 0x0001,
+	/* Multiple Block Read With Stop */
+	GLAMO_FIRE_MMC_CC_MBRS	= 0x0002,
+	/* Multiple Block Read No Stop */
+	GLAMO_FIRE_MMC_CC_MBRNS	= 0x0003,
+	/* RESERVED for "Stream Write" */
+	GLAMO_FIRE_MMC_CC_STRW	= 0x0004,
+	/* "Stream Write" */
+	GLAMO_FIRE_MMC_CC_SBW	= 0x0005,
+	/* RESERVED for Multiple Block Write With Stop */
+	GLAMO_FIRE_MMC_CC_MBWS	= 0x0006,
+	/* Multiple Block Write No Stop */
+	GLAMO_FIRE_MMC_CC_MBWNS	= 0x0007,
+	/* STOP command */
+	GLAMO_FIRE_MMC_CC_STOP	= 0x0008,
+	/* Cancel on Running Command */
+	GLAMO_FIRE_MMC_CC_CANCL	= 0x0009,
+	/* "Basic Command" */
+	GLAMO_FIRE_MMC_CC_BASIC	= 0x000a,
+};
+
+/* these are offsets from the start of the MMC register region */
+enum glamo_register_mmc {
+	/* MMC command, b15..8 = cmd arg b7..0; b7..1 = CRC; b0 = end bit */
+	GLAMO_REG_MMC_CMD_REG1	= 0x00,
+	/* MMC command, b15..0 = cmd arg b23 .. 8 */
+	GLAMO_REG_MMC_CMD_REG2	= 0x02,
+	/* MMC command, b15=start, b14=transmission,
+	 * b13..8=cmd idx, b7..0=cmd arg b31..24
+	 */
+	GLAMO_REG_MMC_CMD_REG3	= 0x04,
+	GLAMO_REG_MMC_CMD_FIRE	= 0x06,
+	GLAMO_REG_MMC_CMD_RSP1	= 0x10,
+	GLAMO_REG_MMC_CMD_RSP2	= 0x12,
+	GLAMO_REG_MMC_CMD_RSP3	= 0x14,
+	GLAMO_REG_MMC_CMD_RSP4	= 0x16,
+	GLAMO_REG_MMC_CMD_RSP5	= 0x18,
+	GLAMO_REG_MMC_CMD_RSP6	= 0x1a,
+	GLAMO_REG_MMC_CMD_RSP7	= 0x1c,
+	GLAMO_REG_MMC_CMD_RSP8	= 0x1e,
+	GLAMO_REG_MMC_RB_STAT1	= 0x20,
+	GLAMO_REG_MMC_RB_BLKCNT	= 0x22,
+	GLAMO_REG_MMC_RB_BLKLEN	= 0x24,
+	GLAMO_REG_MMC_BASIC	= 0x30,
+	GLAMO_REG_MMC_RDATADS1	= 0x34,
+	GLAMO_REG_MMC_RDATADS2	= 0x36,
+	GLAMO_REG_MMC_WDATADS1	= 0x38,
+	GLAMO_REG_MMC_WDATADS2	= 0x3a,
+	GLAMO_REG_MMC_DATBLKCNT	= 0x3c,
+	GLAMO_REG_MMC_DATBLKLEN	= 0x3e,
+	GLAMO_REG_MMC_TIMEOUT	= 0x40,
+
+};
+
+enum glamo_reg_clock_isp {
+	GLAMO_CLOCK_ISP_DG_I1CLK	= 0x0001,
+	GLAMO_CLOCK_ISP_EN_I1CLK	= 0x0002,
+	GLAMO_CLOCK_ISP_DG_CCLK		= 0x0004,
+	GLAMO_CLOCK_ISP_EN_CCLK		= 0x0008,
+	GLAMO_CLOCK_ISP_EN_SCLK		= 0x0020,
+	GLAMO_CLOCK_ISP_DG_M2CLK	= 0x0040,
+	GLAMO_CLOCK_ISP_EN_M2CLK	= 0x0080,
+	GLAMO_CLOCK_ISP_DG_M15CLK	= 0x0100,
+	GLAMO_CLOCK_ISP_EN_M15CLK	= 0x0200,
+	GLAMO_CLOCK_ISP1_RESET		= 0x1000,
+	GLAMO_CLOCK_ISP2_RESET		= 0x2000,
+};
+
+enum glamo_reg_clock_jpeg {
+	GLAMO_CLOCK_JPEG_DG_JCLK	= 0x0001,
+	GLAMO_CLOCK_JPEG_EN_JCLK	= 0x0002,
+	GLAMO_CLOCK_JPEG_DG_M3CLK	= 0x0004,
+	GLAMO_CLOCK_JPEG_EN_M3CLK	= 0x0008,
+	GLAMO_CLOCK_JPEG_RESET		= 0x1000,
+};
+
+enum glamo_reg_clock_2d {
+	GLAMO_CLOCK_2D_DG_GCLK		= 0x0001,
+	GLAMO_CLOCK_2D_EN_GCLK		= 0x0002,
+	GLAMO_CLOCK_2D_DG_M7CLK		= 0x0004,
+	GLAMO_CLOCK_2D_EN_M7CLK		= 0x0008,
+	GLAMO_CLOCK_2D_DG_M6CLK		= 0x0010,
+	GLAMO_CLOCK_2D_EN_M6CLK		= 0x0020,
+	GLAMO_CLOCK_2D_RESET		= 0x1000,
+	GLAMO_CLOCK_2D_CQ_RESET		= 0x2000,
+};
+
+enum glamo_reg_clock_3d {
+	GLAMO_CLOCK_3D_DG_ECLK		= 0x0001,
+	GLAMO_CLOCK_3D_EN_ECLK		= 0x0002,
+	GLAMO_CLOCK_3D_DG_RCLK		= 0x0004,
+	GLAMO_CLOCK_3D_EN_RCLK		= 0x0008,
+	GLAMO_CLOCK_3D_DG_M8CLK		= 0x0010,
+	GLAMO_CLOCK_3D_EN_M8CLK		= 0x0020,
+	GLAMO_CLOCK_3D_BACK_RESET	= 0x1000,
+	GLAMO_CLOCK_3D_FRONT_RESET	= 0x2000,
+};
+
+enum glamo_reg_clock_mpeg {
+	GLAMO_CLOCK_MPEG_DG_X0CLK	= 0x0001,
+	GLAMO_CLOCK_MPEG_EN_X0CLK	= 0x0002,
+	GLAMO_CLOCK_MPEG_DG_X1CLK	= 0x0004,
+	GLAMO_CLOCK_MPEG_EN_X1CLK	= 0x0008,
+	GLAMO_CLOCK_MPEG_DG_X2CLK	= 0x0010,
+	GLAMO_CLOCK_MPEG_EN_X2CLK	= 0x0020,
+	GLAMO_CLOCK_MPEG_DG_X3CLK	= 0x0040,
+	GLAMO_CLOCK_MPEG_EN_X3CLK	= 0x0080,
+	GLAMO_CLOCK_MPEG_DG_X4CLK	= 0x0100,
+	GLAMO_CLOCK_MPEG_EN_X4CLK	= 0x0200,
+	GLAMO_CLOCK_MPEG_DG_X6CLK	= 0x0400,
+	GLAMO_CLOCK_MPEG_EN_X6CLK	= 0x0800,
+	GLAMO_CLOCK_MPEG_ENC_RESET	= 0x1000,
+	GLAMO_CLOCK_MPEG_DEC_RESET	= 0x2000,
+};
+
+enum glamo_reg_clock51 {
+	GLAMO_CLOCK_GEN51_EN_DIV_MCLK	= 0x0001,
+	GLAMO_CLOCK_GEN51_EN_DIV_SCLK	= 0x0002,
+	GLAMO_CLOCK_GEN51_EN_DIV_JCLK	= 0x0004,
+	GLAMO_CLOCK_GEN51_EN_DIV_DCLK	= 0x0008,
+	GLAMO_CLOCK_GEN51_EN_DIV_DMCLK	= 0x0010,
+	GLAMO_CLOCK_GEN51_EN_DIV_DHCLK	= 0x0020,
+	GLAMO_CLOCK_GEN51_EN_DIV_GCLK	= 0x0040,
+	GLAMO_CLOCK_GEN51_EN_DIV_TCLK	= 0x0080,
+	/* FIXME: higher bits */
+};
+
+enum glamo_reg_hostbus2 {
+	GLAMO_HOSTBUS2_MMIO_EN_ISP	= 0x0001,
+	GLAMO_HOSTBUS2_MMIO_EN_JPEG	= 0x0002,
+	GLAMO_HOSTBUS2_MMIO_EN_MPEG	= 0x0004,
+	GLAMO_HOSTBUS2_MMIO_EN_LCD	= 0x0008,
+	GLAMO_HOSTBUS2_MMIO_EN_MMC	= 0x0010,
+	GLAMO_HOSTBUS2_MMIO_EN_MICROP0	= 0x0020,
+	GLAMO_HOSTBUS2_MMIO_EN_MICROP1	= 0x0040,
+	GLAMO_HOSTBUS2_MMIO_EN_CQ	= 0x0080,
+	GLAMO_HOSTBUS2_MMIO_EN_RISC	= 0x0100,
+	GLAMO_HOSTBUS2_MMIO_EN_2D	= 0x0200,
+	GLAMO_HOSTBUS2_MMIO_EN_3D	= 0x0400,
+};
+
+/* LCD Controller */
+
+#define REG_LCD(x)	(x)
+enum glamo_reg_lcd {
+	GLAMO_REG_LCD_MODE1		= REG_LCD(0x00),
+	GLAMO_REG_LCD_MODE2		= REG_LCD(0x02),
+	GLAMO_REG_LCD_MODE3		= REG_LCD(0x04),
+	GLAMO_REG_LCD_WIDTH		= REG_LCD(0x06),
+	GLAMO_REG_LCD_HEIGHT		= REG_LCD(0x08),
+	GLAMO_REG_LCD_POLARITY		= REG_LCD(0x0a),
+	GLAMO_REG_LCD_A_BASE1		= REG_LCD(0x0c),
+	GLAMO_REG_LCD_A_BASE2		= REG_LCD(0x0e),
+	GLAMO_REG_LCD_B_BASE1		= REG_LCD(0x10),
+	GLAMO_REG_LCD_B_BASE2		= REG_LCD(0x12),
+	GLAMO_REG_LCD_C_BASE1		= REG_LCD(0x14),
+	GLAMO_REG_LCD_C_BASE2		= REG_LCD(0x16),
+	GLAMO_REG_LCD_PITCH		= REG_LCD(0x18),
+	/* RES */
+	GLAMO_REG_LCD_HORIZ_TOTAL	= REG_LCD(0x1c),
+	/* RES */
+	GLAMO_REG_LCD_HORIZ_RETR_START	= REG_LCD(0x20),
+	/* RES */
+	GLAMO_REG_LCD_HORIZ_RETR_END	= REG_LCD(0x24),
+	/* RES */
+	GLAMO_REG_LCD_HORIZ_DISP_START	= REG_LCD(0x28),
+	/* RES */
+	GLAMO_REG_LCD_HORIZ_DISP_END	= REG_LCD(0x2c),
+	/* RES */
+	GLAMO_REG_LCD_VERT_TOTAL	= REG_LCD(0x30),
+	/* RES */
+	GLAMO_REG_LCD_VERT_RETR_START	= REG_LCD(0x34),
+	/* RES */
+	GLAMO_REG_LCD_VERT_RETR_END	= REG_LCD(0x38),
+	/* RES */
+	GLAMO_REG_LCD_VERT_DISP_START	= REG_LCD(0x3c),
+	/* RES */
+	GLAMO_REG_LCD_VERT_DISP_END	= REG_LCD(0x40),
+	/* RES */
+	GLAMO_REG_LCD_POL		= REG_LCD(0x44),
+	GLAMO_REG_LCD_DATA_START	= REG_LCD(0x46),
+	GLAMO_REG_LCD_FRATE_CONTRO	= REG_LCD(0x48),
+	GLAMO_REG_LCD_DATA_CMD_HDR	= REG_LCD(0x4a),
+	GLAMO_REG_LCD_SP_START		= REG_LCD(0x4c),
+	GLAMO_REG_LCD_SP_END		= REG_LCD(0x4e),
+	GLAMO_REG_LCD_CURSOR_BASE1	= REG_LCD(0x50),
+	GLAMO_REG_LCD_CURSOR_BASE2	= REG_LCD(0x52),
+	GLAMO_REG_LCD_CURSOR_PITCH	= REG_LCD(0x54),
+	GLAMO_REG_LCD_CURSOR_X_SIZE	= REG_LCD(0x56),
+	GLAMO_REG_LCD_CURSOR_Y_SIZE	= REG_LCD(0x58),
+	GLAMO_REG_LCD_CURSOR_X_POS	= REG_LCD(0x5a),
+	GLAMO_REG_LCD_CURSOR_Y_POS	= REG_LCD(0x5c),
+	GLAMO_REG_LCD_CURSOR_PRESET	= REG_LCD(0x5e),
+	GLAMO_REG_LCD_CURSOR_FG_COLOR	= REG_LCD(0x60),
+	/* RES */
+	GLAMO_REG_LCD_CURSOR_BG_COLOR	= REG_LCD(0x64),
+	/* RES */
+	GLAMO_REG_LCD_CURSOR_DST_COLOR	= REG_LCD(0x68),
+	/* RES */
+	GLAMO_REG_LCD_STATUS1		= REG_LCD(0x80),
+	GLAMO_REG_LCD_STATUS2		= REG_LCD(0x82),
+	GLAMO_REG_LCD_STATUS3		= REG_LCD(0x84),
+	GLAMO_REG_LCD_STATUS4		= REG_LCD(0x86),
+	/* RES */
+	GLAMO_REG_LCD_COMMAND1		= REG_LCD(0xa0),
+	GLAMO_REG_LCD_COMMAND2		= REG_LCD(0xa2),
+	/* RES */
+	GLAMO_REG_LCD_WFORM_DELAY1	= REG_LCD(0xb0),
+	GLAMO_REG_LCD_WFORM_DELAY2	= REG_LCD(0xb2),
+	/* RES */
+	GLAMO_REG_LCD_GAMMA_CORR	= REG_LCD(0x100),
+	/* RES */
+	GLAMO_REG_LCD_GAMMA_R_ENTRY01	= REG_LCD(0x110),
+	GLAMO_REG_LCD_GAMMA_R_ENTRY23	= REG_LCD(0x112),
+	GLAMO_REG_LCD_GAMMA_R_ENTRY45	= REG_LCD(0x114),
+	GLAMO_REG_LCD_GAMMA_R_ENTRY67	= REG_LCD(0x116),
+	GLAMO_REG_LCD_GAMMA_R_ENTRY8	= REG_LCD(0x118),
+	/* RES */
+	GLAMO_REG_LCD_GAMMA_G_ENTRY01	= REG_LCD(0x130),
+	GLAMO_REG_LCD_GAMMA_G_ENTRY23	= REG_LCD(0x132),
+	GLAMO_REG_LCD_GAMMA_G_ENTRY45	= REG_LCD(0x134),
+	GLAMO_REG_LCD_GAMMA_G_ENTRY67	= REG_LCD(0x136),
+	GLAMO_REG_LCD_GAMMA_G_ENTRY8	= REG_LCD(0x138),
+	/* RES */
+	GLAMO_REG_LCD_GAMMA_B_ENTRY01	= REG_LCD(0x150),
+	GLAMO_REG_LCD_GAMMA_B_ENTRY23	= REG_LCD(0x152),
+	GLAMO_REG_LCD_GAMMA_B_ENTRY45	= REG_LCD(0x154),
+	GLAMO_REG_LCD_GAMMA_B_ENTRY67	= REG_LCD(0x156),
+	GLAMO_REG_LCD_GAMMA_B_ENTRY8	= REG_LCD(0x158),
+	/* RES */
+	GLAMO_REG_LCD_SRAM_DRIVING1	= REG_LCD(0x160),
+	GLAMO_REG_LCD_SRAM_DRIVING2	= REG_LCD(0x162),
+	GLAMO_REG_LCD_SRAM_DRIVING3	= REG_LCD(0x164),
+};
+
+enum glamo_reg_lcd_mode1 {
+	GLAMO_LCD_MODE1_PWRSAVE		= 0x0001,
+	GLAMO_LCD_MODE1_PARTIAL_PRT	= 0x0002,
+	GLAMO_LCD_MODE1_HWFLIP		= 0x0004,
+	GLAMO_LCD_MODE1_LCD2		= 0x0008,
+	/* RES */
+	GLAMO_LCD_MODE1_PARTIAL_MODE	= 0x0020,
+	GLAMO_LCD_MODE1_CURSOR_DSTCOLOR	= 0x0040,
+	GLAMO_LCD_MODE1_PARTIAL_ENABLE	= 0x0080,
+	GLAMO_LCD_MODE1_TVCLK_IN_ENABLE	= 0x0100,
+	GLAMO_LCD_MODE1_HSYNC_HIGH_ACT	= 0x0200,
+	GLAMO_LCD_MODE1_VSYNC_HIGH_ACT	= 0x0400,
+	GLAMO_LCD_MODE1_HSYNC_FLIP	= 0x0800,
+	GLAMO_LCD_MODE1_GAMMA_COR_EN	= 0x1000,
+	GLAMO_LCD_MODE1_DITHER_EN	= 0x2000,
+	GLAMO_LCD_MODE1_CURSOR_EN	= 0x4000,
+	GLAMO_LCD_MODE1_ROTATE_EN	= 0x8000,
+};
+
+enum glamo_reg_lcd_mode2 {
+	GLAMO_LCD_MODE2_CRC_CHECK_EN	= 0x0001,
+	GLAMO_LCD_MODE2_DCMD_PER_LINE	= 0x0002,
+	GLAMO_LCD_MODE2_NOUSE_BDEF	= 0x0004,
+	GLAMO_LCD_MODE2_OUT_POS_MODE	= 0x0008,
+	GLAMO_LCD_MODE2_FRATE_CTRL_EN	= 0x0010,
+	GLAMO_LCD_MODE2_SINGLE_BUFFER	= 0x0020,
+	GLAMO_LCD_MODE2_SER_LSB_TO_MSB	= 0x0040,
+	/* FIXME */
+};
+
+enum glamo_reg_lcd_mode3 {
+	/* LCD color source data format */
+	GLAMO_LCD_SRC_RGB565		= 0x0000,
+	GLAMO_LCD_SRC_ARGB1555		= 0x4000,
+	GLAMO_LCD_SRC_ARGB4444		= 0x8000,
+	/* interface type */
+	GLAMO_LCD_MODE3_LCD		= 0x1000,
+	GLAMO_LCD_MODE3_RGB		= 0x0800,
+	GLAMO_LCD_MODE3_CPU		= 0x0000,
+	/* mode */
+	GLAMO_LCD_MODE3_RGB332		= 0x0000,
+	GLAMO_LCD_MODE3_RGB444		= 0x0100,
+	GLAMO_LCD_MODE3_RGB565		= 0x0200,
+	GLAMO_LCD_MODE3_RGB666		= 0x0300,
+	/* depth */
+	GLAMO_LCD_MODE3_6BITS		= 0x0000,
+	GLAMO_LCD_MODE3_8BITS		= 0x0010,
+	GLAMO_LCD_MODE3_9BITS		= 0x0020,
+	GLAMO_LCD_MODE3_16BITS		= 0x0030,
+	GLAMO_LCD_MODE3_18BITS		= 0x0040,
+};
+
+enum glamo_lcd_rot_mode {
+	GLAMO_LCD_ROT_MODE_0            = 0x0000,
+	GLAMO_LCD_ROT_MODE_180          = 0x2000,
+	GLAMO_LCD_ROT_MODE_MIRROR       = 0x4000,
+	GLAMO_LCD_ROT_MODE_FLIP         = 0x6000,
+	GLAMO_LCD_ROT_MODE_90           = 0x8000,
+	GLAMO_LCD_ROT_MODE_270          = 0xa000,
+};
+#define GLAMO_LCD_ROT_MODE_MASK         0xe000
+
+enum glamo_lcd_cmd_type {
+	GLAMO_LCD_CMD_TYPE_DISP		 = 0x0000,
+	GLAMO_LCD_CMD_TYPE_PARALLEL	 = 0x4000,
+	GLAMO_LCD_CMD_TYPE_SERIAL	 = 0x8000,
+	GLAMO_LCD_CMD_TYPE_SERIAL_DIRECT = 0xc000,
+};
+#define GLAMO_LCD_CMD_TYPE_MASK		0xc000
+
+enum glamo_lcd_cmds {
+	GLAMO_LCD_CMD_DATA_DISP_FIRE	= 0x00,
+	GLAMO_LCD_CMD_DATA_DISP_SYNC	= 0x01,		/* RGB only */
+	/* switch to command mode, no display */
+	GLAMO_LCD_CMD_DATA_FIRE_NO_DISP	= 0x02,
+	/* display until VSYNC, switch to command */
+	GLAMO_LCD_CMD_DATA_FIRE_VSYNC	= 0x11,
+	/* display until HSYNC, switch to command */
+	GLAMO_LCD_CMD_DATA_FIRE_HSYNC	= 0x12,
+	/* display until VSYNC, 1 black frame, VSYNC, switch to command */
+	GLAMO_LCD_CMD_DATA_FIRE_VSYNC_B	= 0x13,
+	/* don't care about display and switch to command */
+	GLAMO_LCD_CMD_DATA_FIRE_FREE	= 0x14,		/* RGB only */
+	/* don't care about display, keep data display but disable data,
+	 * and switch to command */
+	GLAMO_LCD_CMD_DATA_FIRE_FREE_D	= 0x15,		/* RGB only */
+};
+
+enum glamo_core_revisions {
+	GLAMO_CORE_REV_A0		= 0x0000,
+	GLAMO_CORE_REV_A1		= 0x0001,
+	GLAMO_CORE_REV_A2		= 0x0002,
+	GLAMO_CORE_REV_A3		= 0x0003,
+};
+
+#endif /* _GLAMO_REGS_H */
diff --git a/include/linux/mfd/glamo.h b/include/linux/mfd/glamo.h
new file mode 100644
index 0000000..b8dfaef
--- /dev/null
+++ b/include/linux/mfd/glamo.h
@@ -0,0 +1,53 @@
+#ifndef __GLAMO_MFD_H
+#define __GLAMO_MFD_H
+
+struct glamo_core;
+struct glamo_spigpio_platform_data;
+struct glamo_fb_platform_data;
+
+struct glamo_mmc_platform_data {
+	int	(*glamo_mmc_use_slow)(void);
+
+	unsigned nonremovable:1;
+
+	struct glamo_core *core;
+};
+
+struct glamo_gpio_platform_data {
+	int base;
+};
+
+struct glamo_platform_data {
+	struct glamo_fb_platform_data	*fb_data;
+	struct glamo_mmc_platform_data	*mmc_data;
+	struct glamo_gpio_platform_data	*gpio_data;
+
+	unsigned int osci_clock_rate;
+
+	void (*glamo_external_reset)(int);
+};
+
+enum glamo_engine {
+	GLAMO_ENGINE_CAPTURE = 0,
+	GLAMO_ENGINE_ISP = 1,
+	GLAMO_ENGINE_JPEG = 2,
+	GLAMO_ENGINE_MPEG_ENC = 3,
+	GLAMO_ENGINE_MPEG_DEC = 4,
+	GLAMO_ENGINE_LCD = 5,
+	GLAMO_ENGINE_CMDQ = 6,
+	GLAMO_ENGINE_2D = 7,
+	GLAMO_ENGINE_3D = 8,
+	GLAMO_ENGINE_MMC = 9,
+	GLAMO_ENGINE_MICROP0 = 10,
+	GLAMO_ENGINE_RISC = 11,
+	GLAMO_ENGINE_MICROP1_MPEG_ENC = 12,
+	GLAMO_ENGINE_MICROP1_MPEG_DEC = 13,
+#if 0
+	GLAMO_ENGINE_H264_DEC = 14,
+	GLAMO_ENGINE_RISC1 = 15,
+	GLAMO_ENGINE_SPI = 16,
+#endif
+	__NUM_GLAMO_ENGINES
+};
+
+#endif
-- 
1.7.2.5

