2010-07-07 13:21:07

by Thibaut Girka

[permalink] [raw]
Subject: [PATCH 0/4] Smedia Glamo 3362 drivers

Those drivers provide MMC and display functionalities of the Smedia Glamo 3362,
a multi-function device used in the Neo FreeRunner.

First patch is for the glamo core driver, needed for every other driver.
The second is for MMC support.
The third is for the GPIO interface.
The last one is for the framebuffer driver. In the case of the FreeRunner,
it is only useful with the glamo-gpio driver.


2010-07-07 13:15:09

by Thibaut Girka

[permalink] [raw]
Subject: [PATCH 1/4] Smedia Glamo 3362 core/resource driver

This driver provides base support for the Smedia Glamo 3362 multi-function device.

Signed-off-by: Thibaut Girka <[email protected]>
---
drivers/mfd/Kconfig | 8 +
drivers/mfd/Makefile | 1 +
drivers/mfd/glamo-core.c | 1299 ++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/glamo-core.h | 58 ++
include/linux/mfd/glamo-regs.h | 630 +++++++++++++++++++
include/linux/mfd/glamo.h | 54 ++
6 files changed, 2050 insertions(+), 0 deletions(-)
create mode 100644 drivers/mfd/glamo-core.c
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/mfd/Kconfig b/drivers/mfd/Kconfig
index 9da0e50..7937883 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -505,4 +505,12 @@ config MCP_UCB1200_TS
tristate "Touchscreen interface support"
depends on MCP_UCB1200 && INPUT

+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.
+
endmenu
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index fb503e7..54ae9b3 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -7,6 +7,7 @@ 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..8220906
--- /dev/null
+++ b/drivers/mfd/glamo-core.c
@@ -0,0 +1,1299 @@
+/* Smedia Glamo 3362 driver
+ *
+ * (C) 2007 by Openmoko, Inc.
+ * Author: Harald Welte <[email protected]>
+ * (C) 2009, Lars-Peter Clausen <[email protected]>
+ * 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
+ ***********************************************************************/
+#define glamo_irq_bit(glamo, x) BIT(x - glamo->irq_base)
+
+static inline struct glamo_core *irq_to_glamo(unsigned int irq)
+{
+ return (struct glamo_core *)get_irq_chip_data(irq);
+}
+
+static void glamo_ack_irq(unsigned int irq)
+{
+ struct glamo_core *glamo = irq_to_glamo(irq);
+ /* clear interrupt source */
+ __reg_write(glamo, GLAMO_REG_IRQ_CLEAR, glamo_irq_bit(glamo, irq));
+}
+
+static void glamo_mask_irq(unsigned int irq)
+{
+ struct glamo_core *glamo = irq_to_glamo(irq);
+
+ /* clear bit in enable register */
+ __reg_clear_bit(glamo, GLAMO_REG_IRQ_ENABLE, glamo_irq_bit(glamo, irq));
+}
+
+static void glamo_unmask_irq(unsigned int irq)
+{
+ struct glamo_core *glamo = irq_to_glamo(irq);
+
+ /* set bit in enable register */
+ __reg_set_bit(glamo, GLAMO_REG_IRQ_ENABLE, glamo_irq_bit(glamo, irq));
+}
+
+static struct irq_chip glamo_irq_chip = {
+ .name = "glamo",
+ .ack = glamo_ack_irq,
+ .mask = glamo_mask_irq,
+ .unmask = glamo_unmask_irq,
+};
+
+static void glamo_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct glamo_core *glamo = get_irq_desc_data(desc);
+ desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
+
+ if (unlikely(desc->status & IRQ_INPROGRESS)) {
+ desc->status |= (IRQ_PENDING | IRQ_MASKED);
+ desc->chip->mask(irq);
+ desc->chip->ack(irq);
+ return;
+ }
+ kstat_incr_irqs_this_cpu(irq, desc);
+
+ desc->chip->ack(irq);
+ desc->status |= IRQ_INPROGRESS;
+
+ do {
+ uint16_t irqstatus;
+ int i;
+
+ if (unlikely((desc->status &
+ (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
+ (IRQ_PENDING | IRQ_MASKED))) {
+ /* dealing with pending IRQ, unmasking */
+ desc->chip->unmask(irq);
+ desc->status &= ~IRQ_MASKED;
+ }
+
+ desc->status &= ~IRQ_PENDING;
+
+ /* read IRQ status register */
+ 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);
+ }
+
+ } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
+
+ desc->status &= ~IRQ_INPROGRESS;
+}
+
+/*
+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;
+ int i, n;
+ const struct reg_range *rr = reg_range;
+
+ 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,
+};
+
+struct glamo_engine_reg_set {
+ uint16_t reg;
+ uint16_t mask_suspended;
+ uint16_t mask_enabled;
+};
+
+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;
+ int 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->irq_base = irq_base = platform_get_irq(pdev, 1);
+ 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 (irq_base < 0) {
+ ret = glamo->irq;
+ dev_err(&pdev->dev, "Failed to get glamo irq base: %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;
+ }
+
+ /* 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_free;
+ }
+
+ 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
+ set_irq_noprobe(irq);
+#endif
+ set_irq_chip_data(irq, glamo);
+ set_irq_chip_and_handler(irq, &glamo_irq_chip,
+ handle_level_irq);
+ }
+
+ set_irq_type(glamo->irq, IRQ_TYPE_EDGE_FALLING);
+ set_irq_data(glamo->irq, glamo);
+ set_irq_chained_handler(glamo->irq, glamo_irq_demux_handler);
+
+ 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_irqs;
+ }
+
+ 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_irqs:
+ disable_irq(glamo->irq);
+ set_irq_chained_handler(glamo->irq, NULL);
+ set_irq_chip_data(glamo->irq, NULL);
+
+ for (irq = irq_base; irq < irq_base + GLAMO_NR_IRQS; ++irq) {
+ set_irq_chip(irq, NULL);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#else
+ set_irq_probe(irq);
+#endif
+ set_irq_chip_data(irq, NULL);
+ }
+err_iounmap:
+ iounmap(glamo->base);
+err_release_mem_region:
+ release_mem_region(glamo->mem->start, resource_size(glamo->mem));
+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);
+
+ disable_irq(glamo->irq);
+ set_irq_chained_handler(glamo->irq, NULL);
+ set_irq_chip_data(glamo->irq, NULL);
+
+ for (irq = irq_base; irq < irq_base + GLAMO_NR_IRQS; ++irq) {
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#else
+ set_irq_noprobe();
+#endif
+ set_irq_chip(irq, NULL);
+ set_irq_chip_data(irq, NULL);
+ }
+
+ platform_set_drvdata(pdev, NULL);
+ iounmap(glamo->base);
+ release_mem_region(glamo->mem->start, resource_size(glamo->mem));
+ 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 __devinit 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 <[email protected]>");
+MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
+MODULE_DESCRIPTION("Smedia Glamo 3362 core/resource driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:glamo3362");
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 <[email protected]>
+ * 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..ae52f3d
--- /dev/null
+++ b/include/linux/mfd/glamo.h
@@ -0,0 +1,54 @@
+#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;
+ void (*registered)(struct device *dev);
+};
+
+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.1

2010-07-07 13:15:21

by Thibaut Girka

[permalink] [raw]
Subject: [PATCH 2/4] Smedia Glamo 3362 MMC/SD Card Interface driver

Signed-off-by: Thibaut Girka <[email protected]>
---
drivers/mmc/host/Kconfig | 11 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/glamo-mci.c | 985 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 997 insertions(+), 0 deletions(-)
create mode 100644 drivers/mmc/host/glamo-mci.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index f06d06e..b28bf7c 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -365,6 +365,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 e30c2ee..9d03382 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -30,6 +30,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_CB710) += cb710-mmc.o
diff --git a/drivers/mmc/host/glamo-mci.c b/drivers/mmc/host/glamo-mci.c
new file mode 100644
index 0000000..f07ad09
--- /dev/null
+++ b/drivers/mmc/host/glamo-mci.c
@@ -0,0 +1,985 @@
+/*
+ * linux/drivers/mmc/host/glamo-mmc.c - Glamo MMC driver
+ *
+ * Copyright (C) 2007 Openmoko, Inc, Andy Green <[email protected]>
+ * Copyright (C) 2009, Lars-Peter Clausen <[email protected]>
+ * Based on S3C MMC driver that was:
+ * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <[email protected]>
+ *
+ * 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 char request_counter;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct read_work;
+};
+
+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 = 21000000;
+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;
+}
+
+
+#ifndef GLAMO_MCI_WORKER
+static void do_pio_read(struct glamo_mci_host *host, struct mmc_data *data)
+{
+ struct sg_mapping_iter miter;
+ uint16_t __iomem *from_ptr = host->data_base;
+
+ 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 >> 1;
+
+ data->bytes_xfered += miter.length;
+ }
+
+ sg_miter_stop(&miter);
+
+ dev_dbg(&host->pdev->dev, "pio_read(): "
+ "complete (no more data).\n");
+}
+#endif
+
+static void do_pio_write(struct glamo_mci_host *host, struct mmc_data *data)
+{
+ struct sg_mapping_iter miter;
+ uint16_t __iomem *to_ptr = host->data_base;
+
+ 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 >> 1;
+
+ 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 (cmd->data->flags & MMC_DATA_READ)
+#ifndef GLAMO_MCI_WORKER
+ do_pio_read(host, cmd->data);
+#else
+ flush_workqueue(host->workqueue);
+#endif
+
+ 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;
+}
+
+#ifdef GLAMO_MCI_WORKER
+static void glamo_mci_read_worker(struct work_struct *work)
+{
+ struct glamo_mci_host *host = container_of(work, struct glamo_mci_host,
+ read_work);
+ struct mmc_command *cmd;
+ uint16_t status;
+ uint16_t blocks_ready;
+ size_t data_read = 0;
+ size_t data_ready;
+ struct scatterlist *sg;
+ uint16_t __iomem *from_ptr = host->data_base;
+ void *sg_pointer;
+
+
+ cmd = host->mrq->cmd;
+ sg = cmd->data->sg;
+ do {
+ /*
+ * TODO: How to get rid of that?
+ * Maybe just drop it... In fact, it is already handled in
+ * the IRQ handler, maybe we should only check cmd->error.
+ * But the question is: what happens between the moment
+ * the error occurs, and the moment the IRQ handler handles it?
+ */
+ status = glamomci_reg_read(host, GLAMO_REG_MMC_RB_STAT1);
+
+ if (status & (GLAMO_STAT1_MMC_RTOUT | GLAMO_STAT1_MMC_DTOUT))
+ cmd->error = -ETIMEDOUT;
+ 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);
+ return;
+ }
+
+ blocks_ready = glamomci_reg_read(host, GLAMO_REG_MMC_RB_BLKCNT);
+ data_ready = blocks_ready * cmd->data->blksz;
+
+ if (data_ready == data_read)
+ yield();
+
+ while (sg && data_read + sg->length <= data_ready) {
+ sg_pointer = page_address(sg_page(sg)) + sg->offset;
+ memcpy(sg_pointer, from_ptr, sg->length);
+ from_ptr += sg->length >> 1;
+
+ data_read += sg->length;
+
+ sg = sg_next(sg);
+ }
+
+ } while (sg);
+ cmd->data->bytes_xfered = data_read;
+}
+#endif
+
+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_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);
+ }
+ }
+
+#ifdef GLAMO_MCI_WORKER
+ /* We'll only get an interrupt when all data has been transfered.
+ By starting to copy data when it's avaiable we can increase
+ throughput by up to 30%. */
+ if (cmd->data && (cmd->data->flags & MMC_DATA_READ))
+ queue_work(host->workqueue, &host->read_work);
+#endif
+
+}
+
+static int glamo_mci_prepare_pio(struct glamo_mci_host *host,
+ struct mmc_data *data)
+{
+ /* 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);
+
+ data->bytes_xfered = 0;
+
+ /* if write, prep the write into the shared RAM before the command */
+ if (data->flags & MMC_DATA_WRITE)
+ 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++;
+ 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(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);
+}
+
+
+/*
+ * no physical write protect supported by us
+ */
+static int glamo_mci_get_ro(struct mmc_host *mmc)
+{
+ return 0;
+}
+
+static struct mmc_host_ops glamo_mci_ops = {
+ .enable = glamo_mci_clock_enable,
+ .disable = glamo_mci_clock_disable,
+ .request = glamo_mci_send_request,
+ .set_ios = glamo_mci_set_ios,
+ .get_ro = glamo_mci_get_ro,
+};
+
+static int 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);
+
+#ifdef GLAMO_MCI_WORKER
+ INIT_WORK(&host->read_work, glamo_mci_read_worker);
+ host->workqueue = create_singlethread_workqueue("glamo-mci-read");
+#endif
+
+ 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);
+ mmc->max_seg_size = mmc->max_req_size;
+ mmc->max_phys_segs = 128;
+ mmc->max_hw_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:
+#ifdef GLAMO_MCI_WORKER
+ destroy_workqueue(host->workqueue);
+#endif
+ mmc_free_host(mmc);
+probe_out:
+ return ret;
+}
+
+static int glamo_mci_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct glamo_mci_host *host = mmc_priv(mmc);
+
+#ifdef GLAMO_MCI_WORKER
+ flush_workqueue(host->workqueue);
+ destroy_workqueue(host->workqueue);
+#endif
+
+ 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, PMSG_SUSPEND);
+ 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 const struct dev_pm_ops glamo_mci_pm_ops = {
+ .suspend = glamo_mci_suspend,
+ .resume = glamo_mci_resume,
+ .freeze = glamo_mci_suspend,
+ .thaw = 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 = 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 <[email protected]>");
+MODULE_ALIAS("platform:glamo-mci");
--
1.7.1

2010-07-07 13:15:41

by Thibaut Girka

[permalink] [raw]
Subject: [PATCH 4/4] Smedia Glamo 3362 Framebuffer driver

Signed-off-by: Thibaut Girka <[email protected]>
---
drivers/video/Kconfig | 17 +
drivers/video/Makefile | 1 +
drivers/video/glamo-fb.c | 943 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/fb.h | 1 +
include/linux/glamofb.h | 21 +
5 files changed, 983 insertions(+), 0 deletions(-)
create mode 100644 drivers/video/glamo-fb.c
create mode 100644 include/linux/glamofb.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3d94a14..8f9e4e4 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2229,6 +2229,23 @@ config FB_BROADSHEET
and could also have been called by other names when coupled with
a bridge adapter.

+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 ddc2af2..90d949b 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -131,6 +131,7 @@ obj-$(CONFIG_FB_CARMINE) += carminefb.o
obj-$(CONFIG_FB_MB862XX) += mb862xx/
obj-$(CONFIG_FB_MSM) += msm/
obj-$(CONFIG_FB_NUC900) += nuc900fb.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..3f8ec8d
--- /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 <[email protected]>
+ * 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);
+
+ acquire_console_sem();
+ fb_set_suspend(gfb->fb, 1);
+ release_console_sem();
+
+ /* 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
+
+ acquire_console_sem();
+ fb_set_suspend(gfb->fb, 0);
+ release_console_sem();
+
+ 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 <[email protected]>");
+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 907ace3..d6a03c2 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -132,6 +132,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
--
1.7.1

2010-07-07 13:21:08

by Thibaut Girka

[permalink] [raw]
Subject: [PATCH 3/4] Smedia Glamo 3362 GPIO interface driver

Signed-off-by: Thibaut Girka <[email protected]>
---
drivers/gpio/Kconfig | 6 +
drivers/gpio/Makefile | 1 +
drivers/gpio/glamo-gpio.c | 284 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 291 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpio/glamo-gpio.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 724038d..cc82f42 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -70,6 +70,12 @@ config GPIO_MAX730X

comment "Memory mapped GPIO expanders:"

+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 51c3cdd..0de1cf9 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib.o

obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o
obj-$(CONFIG_GPIO_ADP5588) += adp5588-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..dff60eb
--- /dev/null
+++ b/drivers/gpio/glamo-gpio.c
@@ -0,0 +1,284 @@
+/* Smedia Glamo 336x/337x gpio driver
+ *
+ * (C) 2009 Lars-Peter Clausen
+ *
+ * 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_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct glamo_core *glamo = chip_to_glamo(chip);
+ unsigned int reg = GLAMO_GPIO_REG_GPIO(offset);
+ uint16_t tmp;
+
+ spin_lock(&glamo->lock);
+ tmp = readw(glamo->base + reg);
+ if (value)
+ tmp |= GLAMO_GPIO_OUTPUT_BIT(offset);
+ else
+ tmp &= ~GLAMO_GPIO_OUTPUT_BIT(offset);
+ writew(tmp, glamo->base + reg);
+ spin_unlock(&glamo->lock);
+}
+
+static int glamo_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct glamo_core *glamo = chip_to_glamo(chip);
+ return !!(readw(glamo->base + GLAMO_GPIO_REG_GPIO(offset)) & GLAMO_GPIO_INPUT_BIT(offset));
+}
+
+static int glamo_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct glamo_core *glamo = chip_to_glamo(chip);
+ unsigned int reg = GLAMO_GPIO_REG_GPIO(offset);
+ uint16_t tmp;
+
+ spin_lock(&glamo->lock);
+ tmp = readw(glamo->base + reg);
+ if ((tmp & GLAMO_GPIO_FUNC_BIT(offset)) == 0) {
+ tmp |= GLAMO_GPIO_FUNC_BIT(offset);
+ writew(tmp, glamo->base + reg);
+ }
+ spin_unlock(&glamo->lock);
+
+ return 0;
+}
+
+static void glamo_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct glamo_core *glamo = chip_to_glamo(chip);
+ unsigned int reg = GLAMO_GPIO_REG_GPIO(offset);
+ uint16_t tmp;
+
+ spin_lock(&glamo->lock);
+ tmp = readw(glamo->base + reg);
+ if ((tmp & GLAMO_GPIO_FUNC_BIT(offset)) == 1) {
+ tmp &= ~GLAMO_GPIO_FUNC_BIT(offset);
+ writew(tmp, glamo->base + reg);
+ }
+ spin_unlock(&glamo->lock);
+}
+
+static int glamo_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct glamo_core *glamo = chip_to_glamo(chip);
+ unsigned int reg = GLAMO_GPIO_REG_GPIO(offset);
+ uint16_t tmp;
+
+ spin_lock(&glamo->lock);
+ tmp = readw(glamo->base + reg);
+ tmp &= ~GLAMO_GPIO_DIRECTION_BIT(offset);
+
+ if (value)
+ tmp |= GLAMO_GPIO_OUTPUT_BIT(offset);
+ else
+ tmp &= ~GLAMO_GPIO_OUTPUT_BIT(offset);
+
+ writew(tmp, glamo->base + reg);
+ spin_unlock(&glamo->lock);
+
+ return 0;
+}
+
+static int glamo_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct glamo_core *glamo = chip_to_glamo(chip);
+ unsigned int reg = GLAMO_GPIO_REG_GPIO(offset);
+ uint16_t tmp;
+
+ spin_lock(&glamo->lock);
+ tmp = readw(glamo->base + reg);
+ if ((tmp & GLAMO_GPIO_DIRECTION_BIT(offset)) == 0) {
+ tmp |= GLAMO_GPIO_DIRECTION_BIT(offset);
+ writew(tmp, glamo->base + reg);
+ }
+ spin_unlock(&glamo->lock);
+
+ 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,
+ .get = glamo_gpio_get,
+ .direction_output = glamo_gpio_direction_output,
+ .set = glamo_gpio_set,
+ .base = -1,
+ .ngpio = GLAMO_NR_GPIO,
+ .can_sleep = 0,
+ .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);
+
+ if (pdata && pdata->gpio_data && pdata->gpio_data->registered)
+ pdata->gpio_data->registered(&pdev->dev);
+
+ 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
+
+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 / 4; ++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 const struct dev_pm_ops glamo_pm_ops = {
+ .suspend = glamo_gpio_suspend,
+ .resume = glamo_gpio_resume,
+ .freeze = glamo_gpio_suspend,
+ .thaw = 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 = {
+ .driver = {
+ .name = "glamo-gpio",
+ .owner = THIS_MODULE,
+ .pm = GLAMO_GPIO_PM_OPS,
+ },
+ .probe = glamo_gpio_probe,
+ .remove = __devexit_p(glamo_gpio_remove),
+};
+
+static int __devinit 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 <[email protected]>");
+MODULE_DESCRIPTION("GPIO interface for the Glamo multimedia device");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:glamo-gpio");
--
1.7.1

2010-08-18 13:39:17

by Samuel Ortiz

[permalink] [raw]
Subject: Re: [PATCH 1/4] Smedia Glamo 3362 core/resource driver

Hi Thibaut,

On Wed, Jul 07, 2010 at 03:12:03PM +0200, Thibaut Girka wrote:
> +#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
This is conflicting with enum glamo_irq, please fix your namespaces.


> +
> +/*
> + * 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.
So you're saying that a couple users reported a bug on a specific kernel
revision, with the same HW, but all other users haven't ?
Couldnt that be related to a specific HW revision ?
Also, this code should be scheduled for 2.6.37, and we're not going to
carry code to allow it to run on 2.6.28.


> +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 }, */
> +};
Please remove the commented code from this array.


> +static void glamo_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
> +{
> + struct glamo_core *glamo = get_irq_desc_data(desc);
> + desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
> +
> + if (unlikely(desc->status & IRQ_INPROGRESS)) {
> + desc->status |= (IRQ_PENDING | IRQ_MASKED);
> + desc->chip->mask(irq);
> + desc->chip->ack(irq);
> + return;
> + }
> + kstat_incr_irqs_this_cpu(irq, desc);
> +
> + desc->chip->ack(irq);
> + desc->status |= IRQ_INPROGRESS;
> +
> + do {
> + uint16_t irqstatus;
> + int i;
> +
> + if (unlikely((desc->status &
> + (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
> + (IRQ_PENDING | IRQ_MASKED))) {
> + /* dealing with pending IRQ, unmasking */
> + desc->chip->unmask(irq);
> + desc->status &= ~IRQ_MASKED;
> + }
> +
> + desc->status &= ~IRQ_PENDING;
> +
> + /* read IRQ status register */
> + 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);
> + }
> +
> + } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
So here you're busy looping in an interrupt handler, and that's not
recommended.


> +#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;
> + int i, n;
> + const struct reg_range *rr = reg_range;
> +
> + 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,
> +};
> +
> +struct glamo_engine_reg_set {
> + uint16_t reg;
> + uint16_t mask_suspended;
> + uint16_t mask_enabled;
> +};
I think you want this definition outside of the DEBUGFS if.else.endif
scope. Have you tried building this driver without DEBUGFS enabled ?


> +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
> + }
> +};
Identation could be clearer here:
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
},
[...]



> +/***********************************************************************
> + * 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
> +*/
Same remark as with the module option: This is going to be a 2.6.37
driver, and I'd like you to check if you're still seeing those issues
before carrying these kind of hacks.


> +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 },
> +};
Shouldnt those values be set by your platform bootloader ? They're obviously
not, but I find it weird.


> +#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
Please remove all #if 0 code from this driver.

Cheers,
Samuel.

--
Intel Open Source Technology Centre
http://oss.intel.com/

2010-08-18 13:40:52

by Samuel Ortiz

[permalink] [raw]
Subject: Re: [PATCH 0/4] Smedia Glamo 3362 drivers

Hi Thibaut,

On Wed, Jul 07, 2010 at 03:12:02PM +0200, Thibaut Girka wrote:
> Those drivers provide MMC and display functionalities of the Smedia Glamo 3362,
> a multi-function device used in the Neo FreeRunner.
>
> First patch is for the glamo core driver, needed for every other driver.
> The second is for MMC support.
> The third is for the GPIO interface.
> The last one is for the framebuffer driver. In the case of the FreeRunner,
> it is only useful with the glamo-gpio driver.
I finally reviewed the MFD driver. Sorry for being so lazy.
Could you please re-send the other ones with the corresponding maintainers
Cc'ed ? I can have a look at the GPIO one, but the MMC and the framebuffer
ones are out of my league.

Cheers,
Samuel.


--
Intel Open Source Technology Centre
http://oss.intel.com/

2010-08-20 15:06:43

by Thibaut Girka

[permalink] [raw]
Subject: Re: [PATCH 1/4] Smedia Glamo 3362 core/resource driver

Le mercredi 18 août 2010 à 15:39 +0200, Samuel Ortiz a écrit :
> Hi Thibaut,

Hi!

> On Wed, Jul 07, 2010 at 03:12:03PM +0200, Thibaut Girka wrote:
> > +#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
> This is conflicting with enum glamo_irq, please fix your namespaces.

Fixed.

> > +
> > +/*
> > + * 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.
> So you're saying that a couple users reported a bug on a specific kernel
> revision, with the same HW, but all other users haven't ?
> Couldnt that be related to a specific HW revision ?
> Also, this code should be scheduled for 2.6.37, and we're not going to
> carry code to allow it to run on 2.6.28.

Well, OpenMoko, the project behind this driver, has been carrying its
own linux tree for years, now, and "2.6.28" and "2.6.24" are referring
to the whole branch, glamo-core included.
So, it hasn't really something to do with the kernel version, but with
the version of the driver itself.
It is not here to allow the driver to run on a specific kernel version.

> > +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 }, */
> > +};
> Please remove the commented code from this array.

Hm, well, that may be useful, for "documentation" and future work, but
we can remove it.

> > +static void glamo_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
> > +{
> > + struct glamo_core *glamo = get_irq_desc_data(desc);
> > + desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
> > +
> > + if (unlikely(desc->status & IRQ_INPROGRESS)) {
> > + desc->status |= (IRQ_PENDING | IRQ_MASKED);
> > + desc->chip->mask(irq);
> > + desc->chip->ack(irq);
> > + return;
> > + }
> > + kstat_incr_irqs_this_cpu(irq, desc);
> > +
> > + desc->chip->ack(irq);
> > + desc->status |= IRQ_INPROGRESS;
> > +
> > + do {
> > + uint16_t irqstatus;
> > + int i;
> > +
> > + if (unlikely((desc->status &
> > + (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
> > + (IRQ_PENDING | IRQ_MASKED))) {
> > + /* dealing with pending IRQ, unmasking */
> > + desc->chip->unmask(irq);
> > + desc->status &= ~IRQ_MASKED;
> > + }
> > +
> > + desc->status &= ~IRQ_PENDING;
> > +
> > + /* read IRQ status register */
> > + 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);
> > + }
> > +
> > + } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
> So here you're busy looping in an interrupt handler, and that's not
> recommended.

Hm, ok... Not sure how to do that otherwise, but I'll have a look.

> > +#ifdef CONFIG_DEBUG_FS
> > [...]
> > +struct glamo_engine_reg_set {
> > + uint16_t reg;
> > + uint16_t mask_suspended;
> > + uint16_t mask_enabled;
> > +};
> I think you want this definition outside of the DEBUGFS if.else.endif
> scope. Have you tried building this driver without DEBUGFS enabled ?

Yes, indeed, it should be outside of the DEBUGFS scope.
Fixed.


> > +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
> > + }
> > +};
> Identation could be clearer here:
> 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
> },
> [...]

Done.

>
> > +/***********************************************************************
> > + * 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
> > +*/
> Same remark as with the module option: This is going to be a 2.6.37
> driver, and I'd like you to check if you're still seeing those issues
> before carrying these kind of hacks.

As stated before, it hasn't anything to do with kernel version.
Though, I'm not sure this module parameter is still being used.
I'll either get rid of that, or make it clear in the comments.

> > +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 },
> > +};
> Shouldnt those values be set by your platform bootloader ? They're obviously
> not, but I find it weird.

I'm not sure, I have to check.
There are at least two bootloader for the FreeRunner, and one of them is
meant to do as little as possible.
Furthermore, the code in the default bootloader is mostly based on a
copy of an old version of this driver.


> Please remove all #if 0 code from this driver.

Hm, ok, checking that, but again, it can be of help for future work
(some sort of documentation).

> Cheers,
> Samuel.


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part