aboutsummaryrefslogtreecommitdiff
path: root/drivers/mfd/glamo/glamo-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/glamo/glamo-core.c')
-rw-r--r--drivers/mfd/glamo/glamo-core.c1151
1 files changed, 1151 insertions, 0 deletions
diff --git a/drivers/mfd/glamo/glamo-core.c b/drivers/mfd/glamo/glamo-core.c
new file mode 100644
index 00000000000..acf545fbdb6
--- /dev/null
+++ b/drivers/mfd/glamo/glamo-core.c
@@ -0,0 +1,1151 @@
+/* Smedia Glamo 336x/337x driver
+ *
+ * (C) 2007 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/kernel_stat.h>
+#include <linux/spinlock.h>
+#include <linux/glamofb.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/div64.h>
+
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
+#include "glamo-regs.h"
+#include "glamo-core.h"
+
+#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
+
+static struct glamo_core *glamo_handle;
+
+static inline void __reg_write(struct glamo_core *glamo,
+ u_int16_t reg, u_int16_t val)
+{
+ writew(val, glamo->base + reg);
+}
+
+static inline u_int16_t __reg_read(struct glamo_core *glamo,
+ u_int16_t reg)
+{
+ return readw(glamo->base + reg);
+}
+
+static void __reg_set_bit_mask(struct glamo_core *glamo,
+ u_int16_t reg, u_int16_t mask,
+ u_int16_t val)
+{
+ u_int16_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,
+ u_int16_t reg, u_int16_t mask,
+ u_int16_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,
+ u_int16_t reg, u_int16_t bit)
+{
+ __reg_set_bit_mask(glamo, reg, bit, 0xffff);
+}
+
+static inline void __reg_clear_bit(struct glamo_core *glamo,
+ u_int16_t reg, u_int16_t bit)
+{
+ __reg_set_bit_mask(glamo, reg, bit, 0);
+}
+
+static inline void glamo_vmem_write(struct glamo_core *glamo, u_int32_t addr,
+ u_int16_t *src, int len)
+{
+ if (addr & 0x0001 || (unsigned long)src & 0x0001 || len & 0x0001) {
+ dev_err(&glamo->pdev->dev, "unaligned write(0x%08x, 0x%p, "
+ "0x%x)!!\n", addr, src, len);
+ }
+
+}
+
+static inline void glamo_vmem_read(struct glamo_core *glamo, u_int16_t *buf,
+ u_int32_t addr, int len)
+{
+ if (addr & 0x0001 || (unsigned long) buf & 0x0001 || len & 0x0001) {
+ dev_err(&glamo->pdev->dev, "unaligned read(0x%p, 0x08%x, "
+ "0x%x)!!\n", buf, addr, len);
+ }
+
+
+}
+
+/***********************************************************************
+ * resources of sibling devices
+ ***********************************************************************/
+
+#if 0
+static struct resource glamo_core_resources[] = {
+ {
+ .start = GLAMO_REGOFS_GENERIC,
+ .end = GLAMO_REGOFS_GENERIC + 0x400,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device glamo_core_dev = {
+ .name = "glamo-core",
+ .resource = &glamo_core_resources,
+ .num_resources = ARRAY_SIZE(glamo_core_resources),
+};
+#endif
+
+static struct resource glamo_jpeg_resources[] = {
+ {
+ .start = GLAMO_REGOFS_JPEG,
+ .end = GLAMO_REGOFS_MPEG - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_GLAMO_JPEG,
+ .end = IRQ_GLAMO_JPEG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device glamo_jpeg_dev = {
+ .name = "glamo-jpeg",
+ .resource = glamo_jpeg_resources,
+ .num_resources = ARRAY_SIZE(glamo_jpeg_resources),
+};
+
+static struct resource glamo_mpeg_resources[] = {
+ {
+ .start = GLAMO_REGOFS_MPEG,
+ .end = GLAMO_REGOFS_LCD - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_GLAMO_MPEG,
+ .end = IRQ_GLAMO_MPEG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device glamo_mpeg_dev = {
+ .name = "glamo-mpeg",
+ .resource = glamo_mpeg_resources,
+ .num_resources = ARRAY_SIZE(glamo_mpeg_resources),
+};
+
+static struct resource glamo_2d_resources[] = {
+ {
+ .start = GLAMO_REGOFS_2D,
+ .end = GLAMO_REGOFS_3D - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_GLAMO_2D,
+ .end = IRQ_GLAMO_2D,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device glamo_2d_dev = {
+ .name = "glamo-2d",
+ .resource = glamo_2d_resources,
+ .num_resources = ARRAY_SIZE(glamo_2d_resources),
+};
+
+static struct resource glamo_3d_resources[] = {
+ {
+ .start = GLAMO_REGOFS_3D,
+ .end = GLAMO_REGOFS_END - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device glamo_3d_dev = {
+ .name = "glamo-3d",
+ .resource = glamo_3d_resources,
+ .num_resources = ARRAY_SIZE(glamo_3d_resources),
+};
+
+static struct platform_device glamo_spigpio_dev = {
+ .name = "glamo-spi-gpio",
+};
+
+static struct resource glamo_fb_resources[] = {
+ /* FIXME: those need to be incremented by parent base */
+ {
+ .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 platform_device glamo_fb_dev = {
+ .name = "glamo-fb",
+ .resource = glamo_fb_resources,
+ .num_resources = ARRAY_SIZE(glamo_fb_resources),
+};
+
+static struct resource glamo_mmc_resources[] = {
+ {
+ /* FIXME: those need to be incremented by parent base */
+ .start = GLAMO_REGOFS_MMC,
+ .end = GLAMO_REGOFS_MPROC0 - 1,
+ .flags = IORESOURCE_MEM
+ }, {
+ .start = IRQ_GLAMO_MMC,
+ .end = IRQ_GLAMO_MMC,
+ .flags = IORESOURCE_IRQ,
+ }, { /* our data buffer for MMC transfers */
+ .start = GLAMO_OFFSET_FB + GLAMO_FB_SIZE,
+ .end = GLAMO_OFFSET_FB + GLAMO_FB_SIZE +
+ GLAMO_MMC_BUFFER_SIZE - 1,
+ .flags = IORESOURCE_MEM
+ },
+};
+
+static struct platform_device glamo_mmc_dev = {
+ .name = "glamo-mci",
+ .resource = glamo_mmc_resources,
+ .num_resources = ARRAY_SIZE(glamo_mmc_resources),
+};
+
+struct glamo_mci_pdata glamo_mci_def_pdata = {
+ .gpio_detect = 0,
+ .glamo_set_mci_power = NULL, /* filled in from MFD platform data */
+ .ocr_avail = MMC_VDD_32_33,
+ .glamo_irq_is_wired = NULL, /* filled in from MFD platform data */
+};
+EXPORT_SYMBOL_GPL(glamo_mci_def_pdata);
+
+
+
+static void mangle_mem_resources(struct resource *res, int num_res,
+ struct resource *parent)
+{
+ int i;
+
+ for (i = 0; i < num_res; i++) {
+ if (res[i].flags != IORESOURCE_MEM)
+ continue;
+ res[i].start += parent->start;
+ res[i].end += parent->start;
+ res[i].parent = parent;
+ }
+}
+
+/***********************************************************************
+ * IRQ demultiplexer
+ ***********************************************************************/
+#define irq2glamo(x) (x - IRQ_GLAMO(0))
+
+static void glamo_ack_irq(unsigned int irq)
+{
+ /* clear interrupt source */
+ __reg_write(glamo_handle, GLAMO_REG_IRQ_CLEAR,
+ 1 << irq2glamo(irq));
+}
+
+static void glamo_mask_irq(unsigned int irq)
+{
+ u_int16_t tmp;
+
+ /* clear bit in enable register */
+ tmp = __reg_read(glamo_handle, GLAMO_REG_IRQ_ENABLE);
+ tmp &= ~(1 << irq2glamo(irq));
+ __reg_write(glamo_handle, GLAMO_REG_IRQ_ENABLE, tmp);
+}
+
+static void glamo_unmask_irq(unsigned int irq)
+{
+ u_int16_t tmp;
+
+ /* set bit in enable register */
+ tmp = __reg_read(glamo_handle, GLAMO_REG_IRQ_ENABLE);
+ tmp |= (1 << irq2glamo(irq));
+ __reg_write(glamo_handle, GLAMO_REG_IRQ_ENABLE, tmp);
+}
+
+static struct irq_chip glamo_irq_chip = {
+ .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)
+{
+ const unsigned int cpu = smp_processor_id();
+
+ spin_lock(&desc->lock);
+
+ 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);
+ goto out_unlock;
+ }
+
+ kstat_cpu(cpu).irqs[irq]++;
+ desc->chip->ack(irq);
+ desc->status |= IRQ_INPROGRESS;
+
+ do {
+ u_int16_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_handle, GLAMO_REG_IRQ_STATUS);
+ for (i = 0; i < 9; i++)
+ if (irqstatus & (1 << i))
+ desc_handle_irq(IRQ_GLAMO(i),
+ irq_desc+IRQ_GLAMO(i));
+
+ } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
+
+ desc->status &= ~IRQ_INPROGRESS;
+
+out_unlock:
+ spin_unlock(&desc->lock);
+}
+
+/***********************************************************************
+ * 'engine' support
+ ***********************************************************************/
+
+int glamo_engine_enable(struct glamo_core *glamo, enum glamo_engine engine)
+{
+ spin_lock(&glamo->lock);
+ switch (engine) {
+ case GLAMO_ENGINE_LCD:
+ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_LCD,
+ GLAMO_CLOCK_LCD_EN_M5CLK |
+ GLAMO_CLOCK_LCD_EN_DHCLK |
+ GLAMO_CLOCK_LCD_EN_DMCLK |
+ GLAMO_CLOCK_LCD_EN_DCLK |
+ GLAMO_CLOCK_LCD_DG_M5CLK |
+ GLAMO_CLOCK_LCD_DG_DMCLK, 0xffff);
+ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_GEN5_1,
+ GLAMO_CLOCK_GEN51_EN_DIV_DHCLK |
+ GLAMO_CLOCK_GEN51_EN_DIV_DMCLK |
+ GLAMO_CLOCK_GEN51_EN_DIV_DCLK, 0xffff);
+ __reg_set_bit_mask(glamo, GLAMO_REG_HOSTBUS(2),
+ GLAMO_HOSTBUS2_MMIO_EN_LCD,
+ 0xffff);
+ break;
+ case GLAMO_ENGINE_MMC:
+ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_MMC,
+ GLAMO_CLOCK_MMC_EN_M9CLK |
+ GLAMO_CLOCK_MMC_EN_TCLK |
+ GLAMO_CLOCK_MMC_DG_M9CLK |
+ GLAMO_CLOCK_MMC_DG_TCLK, 0xffff);
+ __reg_set_bit_mask(glamo, GLAMO_REG_HOSTBUS(2),
+ GLAMO_HOSTBUS2_MMIO_EN_MMC,
+ GLAMO_HOSTBUS2_MMIO_EN_MMC);
+ break;
+ case GLAMO_ENGINE_2D:
+ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_2D,
+ GLAMO_CLOCK_2D_EN_M7CLK |
+ GLAMO_CLOCK_2D_EN_GCLK |
+ GLAMO_CLOCK_2D_DG_M7CLK |
+ GLAMO_CLOCK_2D_DG_GCLK, 0xffff);
+ __reg_set_bit_mask(glamo, GLAMO_REG_HOSTBUS(2),
+ GLAMO_HOSTBUS2_MMIO_EN_2D,
+ GLAMO_HOSTBUS2_MMIO_EN_2D);
+ break;
+ case GLAMO_ENGINE_CMDQ:
+ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_2D,
+ GLAMO_CLOCK_2D_EN_M6CLK, 0xffff);
+ __reg_set_bit_mask(glamo, GLAMO_REG_HOSTBUS(2),
+ GLAMO_HOSTBUS2_MMIO_EN_CQ,
+ GLAMO_HOSTBUS2_MMIO_EN_CQ);
+ break;
+ /* FIXME: Implementation */
+ default:
+ break;
+ }
+ spin_unlock(&glamo->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(glamo_engine_enable);
+
+int glamo_engine_disable(struct glamo_core *glamo, enum glamo_engine engine)
+{
+ spin_lock(&glamo->lock);
+ switch (engine) {
+ /* FIXME: Implementation */
+ default:
+ break;
+ }
+ spin_unlock(&glamo->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(glamo_engine_disable);
+
+struct glamo_script reset_regs[] = {
+ [GLAMO_ENGINE_LCD] = {
+ GLAMO_REG_CLOCK_LCD, GLAMO_CLOCK_LCD_RESET
+ },
+#if 0
+ [GLAMO_ENGINE_HOST] = {
+ GLAMO_REG_CLOCK_HOST, GLAMO_CLOCK_HOST_RESET
+ },
+ [GLAMO_ENGINE_MEM] = {
+ GLAMO_REG_CLOCK_MEM, GLAMO_CLOCK_MEM_RESET
+ },
+#endif
+ [GLAMO_ENGINE_MMC] = {
+ GLAMO_REG_CLOCK_MMC, GLAMO_CLOCK_MMC_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)
+{
+ struct glamo_script *rst;
+
+ if (engine >= ARRAY_SIZE(reset_regs)) {
+ dev_warn(&glamo->pdev->dev, "unknown engine %u ", engine);
+ return;
+ }
+
+ rst = &reset_regs[engine];
+
+ spin_lock(&glamo->lock);
+ __reg_set_bit(glamo, rst->reg, rst->val);
+ spin_unlock(&glamo->lock);
+
+ msleep(1);
+
+ spin_lock(&glamo->lock);
+ __reg_clear_bit(glamo, rst->reg, rst->val);
+ spin_unlock(&glamo->lock);
+
+ msleep(1);
+}
+EXPORT_SYMBOL_GPL(glamo_engine_reset);
+
+enum glamo_pll {
+ GLAMO_PLL1,
+ GLAMO_PLL2,
+};
+
+static int glamo_pll_rate(struct glamo_core *glamo,
+ enum glamo_pll pll)
+{
+ u_int16_t reg;
+ unsigned int div = 512;
+ /* FIXME: move osci into platform_data */
+ unsigned int osci = 32768;
+
+ if (osci == 32768)
+ div = 1;
+
+ 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 (osci/div)*reg;
+}
+
+int glamo_engine_reclock(struct glamo_core *glamo,
+ enum glamo_engine engine,
+ int ps)
+{
+ int pll, khz;
+ u_int16_t reg, mask, val = 0;
+
+ if (!ps)
+ return 0;
+
+ switch (engine) {
+ case GLAMO_ENGINE_LCD:
+ pll = GLAMO_PLL1;
+ reg = GLAMO_REG_CLOCK_GEN7;
+ 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);
+ khz = 1000000000UL / ps;
+
+ if (khz)
+ val = (pll / khz) / 1000;
+
+ dev_dbg(&glamo->pdev->dev,
+ "PLL %d, kHZ %d, div %d\n", pll, khz, val);
+
+ if (val) {
+ val--;
+ reg_set_bit_mask(glamo, reg, mask, val);
+ msleep(5); /* wait some time to stabilize */
+
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(glamo_engine_reclock);
+
+/***********************************************************************
+ * script support
+ ***********************************************************************/
+
+int glamo_run_script(struct glamo_core *glamo, struct glamo_script *script,
+ int len, int may_sleep)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ struct glamo_script *line = &script[i];
+
+ switch (line->reg) {
+ case 0xffff:
+ return 0;
+ case 0xfffe:
+ if (may_sleep)
+ msleep(line->val);
+ else
+ mdelay(line->val);
+ break;
+ case 0xfffd:
+ /* spin until PLLs lock */
+ while ((__reg_read(glamo, GLAMO_REG_PLL_GEN5) & 3) != 3)
+ ;
+ break;
+ default:
+ __reg_write(glamo, script[i].reg, script[i].val);
+ break;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(glamo_run_script);
+
+static struct glamo_script glamo_init_script[] = {
+ { GLAMO_REG_CLOCK_HOST, 0x1000 },
+ { 0xfffe, 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, 0x3000 },
+ { GLAMO_REG_CLOCK_MPEG, 0x3000 },
+ { GLAMO_REG_CLOCK_MPROC, 0x1000 /*0x100f*/ },
+ { 0xfffe, 2 },
+ { GLAMO_REG_CLOCK_HOST, 0x0000 },
+ { GLAMO_REG_CLOCK_MEMORY, 0x0000 },
+ { GLAMO_REG_CLOCK_LCD, 0x0000 },
+ { GLAMO_REG_CLOCK_MMC, 0x0000 },
+#if 0
+/* unused engines must be left in reset to stop MMC block read "blackouts" */
+ { GLAMO_REG_CLOCK_ISP, 0x0000 },
+ { GLAMO_REG_CLOCK_ISP, 0x0000 },
+ { GLAMO_REG_CLOCK_JPEG, 0x0000 },
+ { GLAMO_REG_CLOCK_3D, 0x0000 },
+ { GLAMO_REG_CLOCK_3D, 0x0000 },
+ { GLAMO_REG_CLOCK_2D, 0x0000 },
+ { GLAMO_REG_CLOCK_2D, 0x0000 },
+ { GLAMO_REG_CLOCK_RISC1, 0x0000 },
+ { GLAMO_REG_CLOCK_MPEG, 0x0000 },
+ { GLAMO_REG_CLOCK_MPEG, 0x0000 },
+#endif
+ { GLAMO_REG_PLL_GEN1, 0x05db }, /* 48MHz */
+ { GLAMO_REG_PLL_GEN3, 0x09c3 }, /* 80MHz */
+ { 0xfffd, 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, 0x01ff },
+ { GLAMO_REG_CLOCK_GEN6, 0x2000 },
+ { GLAMO_REG_CLOCK_GEN7, 0x0101 },
+ { GLAMO_REG_CLOCK_GEN8, 0x0100 },
+ { GLAMO_REG_CLOCK_HOST, 0x000d },
+ { 0x200, 0x0ef0 },
+ { 0x202, 0x07ff },
+ { 0x212, 0x0000 },
+ { 0x214, 0x4000 },
+ { 0x216, 0xf00e },
+ { GLAMO_REG_MEM_TYPE, 0x0874 }, /* 8MB, 16 word pg wr+rd */
+ { GLAMO_REG_MEM_GEN, 0xafaf }, /* 63 grants min + max */
+ /*
+ * the register below originally 0x0108 makes unreliable Glamo MMC
+ * write operations. Cranked to 0x05ad to add a wait state, the
+ * unreliability is not seen after 4GB of write / read testing
+ */
+ { 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 },
+ { 0xfffe, 1 },
+ { GLAMO_REG_MEM_DRAM1, 0xc100 },
+ { 0xfffe, 1 },
+ { GLAMO_REG_MEM_DRAM1, 0xe100 },
+ { GLAMO_REG_MEM_DRAM2, 0x01d6 },
+ { GLAMO_REG_CLOCK_MEMORY, 0x000b },
+};
+
+static struct glamo_script glamo_resume_script[] = {
+ { GLAMO_REG_IRQ_ENABLE, 0x01ff },
+ { GLAMO_REG_CLOCK_GEN6, 0x2000 },
+ { GLAMO_REG_CLOCK_GEN7, 0x0001 }, /* 0101 */
+ { GLAMO_REG_CLOCK_GEN8, 0x0100 },
+ { GLAMO_REG_CLOCK_HOST, 0x000d },
+ { 0x200, 0x0ef0 },
+ { 0x202, 0x07ff },
+ { 0x212, 0x0000 },
+ { 0x214, 0x4000 },
+ { 0x216, 0xf00e },
+ { GLAMO_REG_MEM_TYPE, 0x0874 }, /* 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 },
+ { 0xfffe, 1 },
+ { GLAMO_REG_MEM_DRAM1, 0xc100 },
+ { 0xfffe, 1 },
+ { GLAMO_REG_MEM_DRAM1, 0xe100 },
+ { GLAMO_REG_MEM_DRAM2, 0x01d6 },
+ { GLAMO_REG_CLOCK_MEMORY, 0x000b },
+};
+
+#if 0 /* MM370 */
+static const struct glamo_script regs_vram_2mb = {
+ { GLAMO_REG_CLOCK_MEMORY, 0x3aaa },
+ { 0xfffe, 50 },
+ { GLAMO_REG_CLOCK_MEMORY, 0x0aaa },
+ { 0xfffe, 3 },
+ { GLAMO_REG_MEM_POWER1, 0x0020 },
+ { 0x033a, 0x0000 },
+ { 0x033c, 0x0000 },
+ { 0x033e, 0x0000 },
+ { 0x0340, 0x0000 },
+ { 0x0342, 0x0000 },
+ { 0x0344, 0x0000 },
+ { 0x0346, 0x0240 },
+ { GLAMO_REG_MEM_TIMING8, 0x1016 },
+ { GLAMO_REG_MEM_TIMING9, 0x6067 },
+ { GLAMO_REG_MEM_TIMING10, 0x00ff },
+ { GLAMO_REG_MEM_TIMING11, 0x0030 },
+ { GLAMO_REG_MEM_GEN, 0x3fff },
+ { GLAMO_REG_MEM_GEN, 0xafaf },
+ { GLAMO_REG_MEM_TIMING1, 0x0108 },
+ { GLAMO_REG_MEM_TIMING2, 0x0010 },
+ { GLAMO_REG_MEM_DRAM1, 0x0a00 },
+ { 0xfffe, 3 },
+ { GLAMO_REG_MEM_DRAM1, 0xe200 },
+ { 0xfffe, 1 },
+};
+
+static const struct glamo_script regs_vram_8mb = {
+ { GLAMO_REG_CLOCK_MEMORY, 0x3aaa },
+ { 0xfffe, 50 },
+ { GLAMO_REG_CLOCK_MEMORY, 0x0aaa },
+ { 0xfffe, 3 },
+ { GLAMO_REG_MEM_POWER1, 0x0020 },
+ { 0x033a, 0x45cf },
+ { 0x033c, 0x4240 },
+ { 0x033e, 0x53e0 },
+ { 0x0340, 0x1401 },
+ { 0x0342, 0x0c44 },
+ { 0x0344, 0x1d0b },
+ { 0x0346, 0x25ac },
+ { 0x0348, 0x1953 },
+ { 0xfffe, 1 },
+ { GLAMO_REG_MEM_TYPE, 0x087a },
+ { GLAMO_REG_MEM_DRAM2, 0x01d6 },
+ { GLAMO_REG_MEM_TIMING8, 0x1060 },
+ { GLAMO_REG_MEM_TIMING9, 0x6067 },
+ { GLAMO_REG_MEM_TIMING10, 0x00ff },
+ { GLAMO_REG_MEM_TIMING11, 0x0030 },
+ { GLAMO_REG_MEM_GEN, 0x3fff },
+ { GLAMO_REG_MEM_GEN, 0xafaf },
+ { GLAMO_REG_MEM_TIMING1, 0x3108 },
+ { GLAMO_REG_MEM_TIMING2, 0x0010 },
+ { GLAMO_REG_MEM_DRAM1, 0x0a00 },
+ { 0xfffe, 3 },
+ { GLAMO_REG_MEM_DRAM1, 0xe200 },
+ { 0xfffe, 1 },
+};
+#endif
+
+enum glamo_power {
+ GLAMO_POWER_ON,
+ GLAMO_POWER_STANDBY,
+ GLAMO_POWER_SUSPEND,
+};
+
+static void glamo_power(struct glamo_core *glamo,
+ enum glamo_power new_state)
+{
+ spin_lock(&glamo->lock);
+
+ switch (new_state) {
+ case GLAMO_POWER_ON:
+ /* power up PLL1 and PLL2 */
+ __reg_set_bit_mask(glamo, GLAMO_REG_DFT_GEN6, 0x0001, 0xffff);
+ __reg_set_bit_mask(glamo, GLAMO_REG_PLL_GEN3, 0x2000, 0x0000);
+
+ /* spin until PLL1 and PLL2 lock */
+ while ((__reg_read(glamo, GLAMO_REG_PLL_GEN5) & 3) != 3)
+ ;
+
+ /* enable memory clock and get it out of deep pwrdown */
+ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_MEMORY,
+ GLAMO_CLOCK_MEM_EN_MOCACLK, 0xffff);
+ __reg_set_bit_mask(glamo, GLAMO_REG_MEM_DRAM2,
+ GLAMO_MEM_DRAM2_DEEP_PWRDOWN, 0x0000);
+ __reg_set_bit_mask(glamo, GLAMO_REG_MEM_DRAM1,
+ GLAMO_MEM_DRAM1_SELF_REFRESH, 0x0000);
+
+ glamo_run_script(glamo, glamo_resume_script,
+ ARRAY_SIZE(glamo_resume_script), 0);
+
+ break;
+ case GLAMO_POWER_STANDBY:
+ /* enable memory self-refresh */
+ __reg_set_bit_mask(glamo, GLAMO_REG_MEM_DRAM1,
+ GLAMO_MEM_DRAM1_SELF_REFRESH, 0xffff);
+ /* stop memory clock */
+ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_MEMORY,
+ GLAMO_CLOCK_MEM_EN_MOCACLK, 0x0000);
+ /* power down PLL2 and then PLL1 */
+ __reg_set_bit_mask(glamo, GLAMO_REG_PLL_GEN3, 0x2000, 0xffff);
+ __reg_set_bit_mask(glamo, GLAMO_REG_DFT_GEN5, 0x0001, 0xffff);
+ break;
+ case GLAMO_POWER_SUSPEND:
+ __reg_set_bit_mask(glamo, GLAMO_REG_MEM_DRAM2,
+ GLAMO_MEM_DRAM2_DEEP_PWRDOWN, 0xffff);
+ /* stop memory clock */
+ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_MEMORY,
+ GLAMO_CLOCK_MEM_EN_MOCACLK, 0x0000);
+ /* power down PLL2 and then PLL1 */
+ __reg_set_bit_mask(glamo, GLAMO_REG_PLL_GEN3, 0x2000, 0xffff);
+ __reg_set_bit_mask(glamo, GLAMO_REG_DFT_GEN5, 0x0001, 0xffff);
+ break;
+ }
+
+ spin_unlock(&glamo->lock);
+}
+
+#if 0
+#define MEMDETECT_RETRY 6
+static unsigned int detect_memsize(struct glamo_core *glamo)
+{
+ int i;
+
+ /*static const u_int16_t pattern[] = {
+ 0x1111, 0x8a8a, 0x2222, 0x7a7a,
+ 0x3333, 0x6a6a, 0x4444, 0x5a5a,
+ 0x5555, 0x4a4a, 0x6666, 0x3a3a,
+ 0x7777, 0x2a2a, 0x8888, 0x1a1a
+ }; */
+
+ for (i = 0; i < MEMDETECT_RETRY; i++) {
+ switch (glamo->type) {
+ case 3600:
+ __reg_write(glamo, GLAMO_REG_MEM_TYPE, 0x0072);
+ __reg_write(glamo, GLAMO_REG_MEM_DRAM1, 0xc100);
+ break;
+ case 3650:
+ switch (glamo->revision) {
+ case GLAMO_CORE_REV_A0:
+ if (i & 1)
+ __reg_write(glamo, GLAMO_REG_MEM_TYPE,
+ 0x097a);
+ else
+ __reg_write(glamo, GLAMO_REG_MEM_TYPE,
+ 0x0173);
+
+ __reg_write(glamo, GLAMO_REG_MEM_DRAM1, 0x0000);
+ msleep(1);
+ __reg_write(glamo, GLAMO_REG_MEM_DRAM1, 0xc100);
+ break;
+ default:
+ if (i & 1)
+ __reg_write(glamo, GLAMO_REG_MEM_TYPE,
+ 0x0972);
+ else
+ __reg_write(glamo, GLAMO_REG_MEM_TYPE,
+ 0x0872);
+
+ __reg_write(glamo, GLAMO_REG_MEM_DRAM1, 0x0000);
+ msleep(1);
+ __reg_write(glamo, GLAMO_REG_MEM_DRAM1, 0xe100);
+ break;
+ }
+ break;
+ case 3700:
+ /* FIXME */
+ default:
+ break;
+ }
+
+#if 0
+ /* FIXME: finish implementation */
+ for (j = 0; j < 8; j++) {
+ __
+#endif
+ }
+
+ return 0;
+}
+#endif
+
+/* Find out if we can support this version of the Glamo chip */
+static int glamo_supported(struct glamo_core *glamo)
+{
+ u_int16_t dev_id, rev_id; /*, memsize; */
+
+ 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);
+ /* maybe should abort ? */
+ }
+ break;
+ case 0x3600:
+ case 0x3700:
+ default:
+ dev_err(&glamo->pdev->dev, "unsupported Glamo device %04x\n",
+ dev_id);
+ return 0;
+ }
+
+ dev_info(&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 __init glamo_probe(struct platform_device *pdev)
+{
+ int rc, irq;
+ struct glamo_core *glamo;
+
+ if (glamo_handle) {
+ dev_err(&pdev->dev,
+ "This driver supports only one instance\n");
+ return -EBUSY;
+ }
+
+ glamo = kmalloc(GFP_KERNEL, sizeof(*glamo));
+ if (!glamo)
+ return -ENOMEM;
+
+ spin_lock_init(&glamo->lock);
+ glamo_handle = glamo;
+ glamo->pdev = pdev;
+ glamo->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ glamo->irq = platform_get_irq(pdev, 0);
+ glamo->pdata = pdev->dev.platform_data;
+ if (!glamo->mem || !glamo->pdata) {
+ dev_err(&pdev->dev, "platform device with no MEM/PDATA ?\n");
+ rc = -ENOENT;
+ goto out_free;
+ }
+
+ /* register a number of sibling devices whoise IOMEM resources
+ * are siblings of pdev's IOMEM resource */
+#if 0
+ glamo_core_dev.dev.parent = &pdev.dev;
+ mangle_mem_resources(glamo_core_dev.resources,
+ glamo_core_dev.num_resources, glamo->mem);
+ glamo_core_dev.resources[1].start = glamo->irq;
+ glamo_core_dev.resources[1].end = glamo->irq;
+ platform_device_register(&glamo_core_dev);
+#endif
+ /* only remap the generic, hostbus and memory controller registers */
+ glamo->base = ioremap(glamo->mem->start, GLAMO_REGOFS_VIDCAP);
+ if (!glamo->base) {
+ dev_err(&pdev->dev, "failed to ioremap() memory region\n");
+ goto out_free;
+ }
+
+ /* bring MCI specific stuff over from our MFD platform data */
+ glamo_mci_def_pdata.glamo_set_mci_power =
+ glamo->pdata->glamo_set_mci_power;
+ glamo_mci_def_pdata.glamo_irq_is_wired =
+ glamo->pdata->glamo_irq_is_wired;
+
+ glamo_mmc_dev.dev.parent = &pdev->dev;
+ /* we need it later to give to the engine enable and disable */
+ glamo_mci_def_pdata.pglamo = glamo;
+ mangle_mem_resources(glamo_mmc_dev.resource,
+ glamo_mmc_dev.num_resources, glamo->mem);
+ platform_device_register(&glamo_mmc_dev);
+
+ glamo_2d_dev.dev.parent = &pdev->dev;
+ mangle_mem_resources(glamo_2d_dev.resource,
+ glamo_2d_dev.num_resources, glamo->mem);
+ platform_device_register(&glamo_2d_dev);
+
+ glamo_3d_dev.dev.parent = &pdev->dev;
+ mangle_mem_resources(glamo_3d_dev.resource,
+ glamo_3d_dev.num_resources, glamo->mem);
+ platform_device_register(&glamo_3d_dev);
+
+ glamo_jpeg_dev.dev.parent = &pdev->dev;
+ mangle_mem_resources(glamo_jpeg_dev.resource,
+ glamo_jpeg_dev.num_resources, glamo->mem);
+ platform_device_register(&glamo_jpeg_dev);
+
+ glamo_mpeg_dev.dev.parent = &pdev->dev;
+ mangle_mem_resources(glamo_mpeg_dev.resource,
+ glamo_mpeg_dev.num_resources, glamo->mem);
+ platform_device_register(&glamo_mpeg_dev);
+
+ glamo->pdata->glamo = glamo;
+ glamo_fb_dev.dev.parent = &pdev->dev;
+ glamo_fb_dev.dev.platform_data = glamo->pdata;
+ mangle_mem_resources(glamo_fb_dev.resource,
+ glamo_fb_dev.num_resources, glamo->mem);
+ platform_device_register(&glamo_fb_dev);
+
+ glamo->pdata->spigpio_info->glamo = glamo;
+ glamo_spigpio_dev.dev.parent = &pdev->dev;
+ glamo_spigpio_dev.dev.platform_data = glamo->pdata->spigpio_info;
+ platform_device_register(&glamo_spigpio_dev);
+
+ /* only request the generic, hostbus and memory controller MMIO */
+ glamo->mem = request_mem_region(glamo->mem->start,
+ GLAMO_REGOFS_VIDCAP, "glamo-core");
+ if (!glamo->mem) {
+ dev_err(&pdev->dev, "failed to request memory region\n");
+ goto out_free;
+ }
+
+ if (!glamo_supported(glamo)) {
+ dev_err(&pdev->dev, "This Glamo is not supported\n");
+ goto out_free;
+ }
+
+ platform_set_drvdata(pdev, glamo);
+
+ dev_dbg(&glamo->pdev->dev, "running init script\n");
+ glamo_run_script(glamo, glamo_init_script,
+ ARRAY_SIZE(glamo_init_script), 1);
+
+ dev_info(&glamo->pdev->dev, "Glamo core now %uHz CPU / %uHz Memory)\n",
+ glamo_pll_rate(glamo, GLAMO_PLL1),
+ glamo_pll_rate(glamo, GLAMO_PLL2));
+
+ for (irq = IRQ_GLAMO(0); irq <= IRQ_GLAMO(8); irq++) {
+ set_irq_chip(irq, &glamo_irq_chip);
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ if (glamo->pdata->glamo_irq_is_wired &&
+ !glamo->pdata->glamo_irq_is_wired()) {
+ set_irq_chained_handler(glamo->irq, glamo_irq_demux_handler);
+ set_irq_type(glamo->irq, IRQT_FALLING);
+ glamo->irq_works = 1;
+ } else
+ glamo->irq_works = 0;
+ return 0;
+
+out_free:
+ glamo_handle = NULL;
+ kfree(glamo);
+ return rc;
+}
+
+static int glamo_remove(struct platform_device *pdev)
+{
+ struct glamo_core *glamo = platform_get_drvdata(pdev);
+ int irq;
+
+ disable_irq(glamo->irq);
+ set_irq_chained_handler(glamo->irq, NULL);
+
+ for (irq = IRQ_GLAMO(0); irq <= IRQ_GLAMO(8); irq++) {
+ set_irq_flags(irq, 0);
+ set_irq_chip(irq, NULL);
+ }
+
+ platform_set_drvdata(pdev, NULL);
+ platform_device_unregister(&glamo_fb_dev);
+ platform_device_unregister(&glamo_mmc_dev);
+ iounmap(glamo->base);
+ release_mem_region(glamo->mem->start, GLAMO_REGOFS_VIDCAP);
+ glamo_handle = NULL;
+ kfree(glamo);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int glamo_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+static int glamo_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define glamo_suspend NULL
+#define glamo_resume NULL
+#endif
+
+static struct platform_driver glamo_driver = {
+ .probe = glamo_probe,
+ .remove = glamo_remove,
+ .suspend = glamo_suspend,
+ .resume = glamo_resume,
+ .driver = {
+ .name = "glamo3362",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit glamo_init(void)
+{
+ return platform_driver_register(&glamo_driver);
+}
+
+static void __exit glamo_cleanup(void)
+{
+ platform_driver_unregister(&glamo_driver);
+}
+
+module_init(glamo_init);
+module_exit(glamo_cleanup);
+
+MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
+MODULE_DESCRIPTION("Smedia Glamo 336x/337x core/resource driver");
+MODULE_LICENSE("GPL");