diff options
author | lars <lars@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2009-05-18 17:55:41 +0000 |
---|---|---|
committer | lars <lars@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2009-05-18 17:55:41 +0000 |
commit | fb189822fcab111e55d1c7e83c482dc2c144500a (patch) | |
tree | dd10e61f382ace3afbe40af3af8238757cf5e3d0 /target/linux/s3c24xx/files-2.6.30/drivers/mfd | |
parent | c5f7c391b56bb88e21895f0fc9310cd0919e8d0c (diff) |
[s3c24xx] bump to 2.6.30-rc6
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@15918 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/s3c24xx/files-2.6.30/drivers/mfd')
11 files changed, 5165 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/Kconfig b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/Kconfig new file mode 100644 index 0000000000..efa16990d5 --- /dev/null +++ b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/Kconfig @@ -0,0 +1,59 @@ +config MFD_GLAMO + bool "Smedia Glamo 336x/337x support" + help + This enables the core driver for the Smedia Glamo 336x/337x + multi-function device. It includes irq_chip demultiplex as + well as clock / power management and GPIO support. + +config MFD_GLAMO_FB + 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. + +config MFD_GLAMO_FB_XGLAMO_WORKAROUND + bool "Smedia Glamo 336x/337x Xglamo rotation workaround" + depends on MFD_GLAMO_FB + help + This is a workaround for a Xglamo bug. This should be fixed + in Xglamo and not in kernel space. + + If unsure, say N. + + +config MFD_GLAMO_SPI_GPIO + tristate "Glamo GPIO SPI bitbang support" + depends on MFD_GLAMO + select SPI_BITBANG + + help + Enable a bitbanging SPI adapter driver for the Smedia Glamo. + +config MFD_GLAMO_SPI_FB + tristate "Glamo LCM control channel SPI support" + depends on MFD_GLAMO_FB + help + Enable a bitbanging SPI adapter driver for the Smedia Glamo LCM + control channel. This SPI interface is frequently used to + interconnect the LCM control interface. + +config MFD_GLAMO_MCI + tristate "Glamo S3C SD/MMC Card Interface support" + depends on MFD_GLAMO && MMC + 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. diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/Makefile b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/Makefile new file mode 100644 index 0000000000..dc64d50fa1 --- /dev/null +++ b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the Smedia Glamo framebuffer driver +# + +obj-$(CONFIG_MFD_GLAMO) += glamo-core.o glamo-gpio.o +obj-$(CONFIG_MFD_GLAMO_SPI) += glamo-spi.o +obj-$(CONFIG_MFD_GLAMO_SPI_GPIO) += glamo-spi-gpio.o + +obj-$(CONFIG_MFD_GLAMO_FB) += glamo-fb.o +obj-$(CONFIG_MFD_GLAMO_SPI_FB) += glamo-lcm-spi.o +obj-$(CONFIG_MFD_GLAMO_MCI) += glamo-mci.o + diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-core.c b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-core.c new file mode 100644 index 0000000000..a2332a7df4 --- /dev/null +++ b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-core.c @@ -0,0 +1,1455 @@ +/* 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> + +//#include <mach/regs-irq.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) + +#define GLAMO_MEM_REFRESH_COUNT 0x100 + + +/* + * 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 = 0; +module_param(slow_memory, int, 0644); + +struct reg_range { + int start; + int count; + char *name; + char dump; +}; +struct reg_range reg_range[] = { + { 0x0000, 0x76, "General", 1 }, + { 0x0200, 0x16, "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", 1 }, + { 0x1200, 0x64, "LCD 2", 1 }, + { 0x1400, 0x40, "MMC", 1 }, +/* { 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 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 + }, +}; + +struct glamo_mci_pdata glamo_mci_def_pdata = { + .gpio_detect = 0, + .glamo_can_set_mci_power = NULL, /* filled in from MFD platform data */ + .ocr_avail = MMC_VDD_20_21 | + MMC_VDD_21_22 | + MMC_VDD_22_23 | + MMC_VDD_23_24 | + MMC_VDD_24_25 | + MMC_VDD_25_26 | + MMC_VDD_26_27 | + MMC_VDD_27_28 | + MMC_VDD_28_29 | + MMC_VDD_29_30 | + MMC_VDD_30_31 | + MMC_VDD_32_33, + .glamo_irq_is_wired = NULL, /* filled in from MFD platform data */ + .mci_suspending = NULL, /* filled in from MFD platform data */ + .mci_all_dependencies_resumed = 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) +{ + 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 { + 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; +} + + +static ssize_t regs_write(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long reg = simple_strtoul(buf, NULL, 10); + struct glamo_core *glamo = dev_get_drvdata(dev); + + while (*buf && (*buf != ' ')) + buf++; + if (*buf != ' ') + return -EINVAL; + while (*buf && (*buf == ' ')) + buf++; + if (!*buf) + return -EINVAL; + + printk(KERN_INFO"reg 0x%02lX <-- 0x%04lX\n", + reg, simple_strtoul(buf, NULL, 10)); + + __reg_write(glamo, reg, simple_strtoul(buf, NULL, 10)); + + return count; +} + +static ssize_t regs_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct glamo_core *glamo = dev_get_drvdata(dev); + int n, n1 = 0, r; + char * end = buf; + + spin_lock(&glamo->lock); + + for (r = 0; r < ARRAY_SIZE(reg_range); r++) { + if (!reg_range[r].dump) + continue; + n1 = 0; + end += sprintf(end, "\n%s\n", reg_range[r].name); + for (n = reg_range[r].start; + n < reg_range[r].start + reg_range[r].count; n += 2) { + if (((n1++) & 7) == 0) + end += sprintf(end, "\n%04X: ", n); + end += sprintf(end, "%04x ", __reg_read(glamo, n)); + } + end += sprintf(end, "\n"); + if (!attr) { + printk("%s", buf); + end = buf; + } + } + spin_unlock(&glamo->lock); + + return end - buf; +} + +static DEVICE_ATTR(regs, 0644, regs_read, regs_write); +static struct attribute *glamo_sysfs_entries[] = { + &dev_attr_regs.attr, + NULL +}; +static struct attribute_group glamo_attr_group = { + .name = NULL, + .attrs = glamo_sysfs_entries, +}; + + + +/*********************************************************************** + * 'engine' support + ***********************************************************************/ + +int __glamo_engine_enable(struct glamo_core *glamo, enum glamo_engine engine) +{ + switch (engine) { + case GLAMO_ENGINE_LCD: + __reg_set_bit_mask(glamo, GLAMO_REG_HOSTBUS(2), + GLAMO_HOSTBUS2_MMIO_EN_LCD, + GLAMO_HOSTBUS2_MMIO_EN_LCD); + __reg_write(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); + __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); + break; + case GLAMO_ENGINE_MMC: + __reg_set_bit_mask(glamo, GLAMO_REG_HOSTBUS(2), + GLAMO_HOSTBUS2_MMIO_EN_MMC, + GLAMO_HOSTBUS2_MMIO_EN_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); + /* enable the TCLK divider clk input */ + __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_GEN5_1, + GLAMO_CLOCK_GEN51_EN_DIV_TCLK, + GLAMO_CLOCK_GEN51_EN_DIV_TCLK); + 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; + } + + glamo->engine_enabled_bitfield |= 1 << engine; + + return 0; +} + +int glamo_engine_enable(struct glamo_core *glamo, enum glamo_engine engine) +{ + int ret; + + spin_lock(&glamo->lock); + + ret = __glamo_engine_enable(glamo, engine); + + spin_unlock(&glamo->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(glamo_engine_enable); + +int __glamo_engine_disable(struct glamo_core *glamo, enum glamo_engine engine) +{ + switch (engine) { + case GLAMO_ENGINE_LCD: + /* remove pixel clock to LCM */ + __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_LCD, + GLAMO_CLOCK_LCD_EN_DCLK, 0); + __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_LCD, + GLAMO_CLOCK_LCD_EN_DHCLK | + GLAMO_CLOCK_LCD_EN_DMCLK, 0); + /* kill memory clock */ + __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_LCD, + GLAMO_CLOCK_LCD_EN_M5CLK, 0); + /* stop dividing the clocks */ + __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, 0); + 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, 0); + /* disable the TCLK divider clk input */ +// __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_GEN5_1, +// GLAMO_CLOCK_GEN51_EN_DIV_TCLK, 0); + + default: + break; + } + + glamo->engine_enabled_bitfield &= ~(1 << engine); + + return 0; +} +int glamo_engine_disable(struct glamo_core *glamo, enum glamo_engine engine) +{ + int ret; + + spin_lock(&glamo->lock); + + ret = __glamo_engine_disable(glamo, engine); + + spin_unlock(&glamo->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(glamo_engine_disable); + +static const u_int16_t engine_clock_regs[__NUM_GLAMO_ENGINES] = { + [GLAMO_ENGINE_LCD] = GLAMO_REG_CLOCK_LCD, + [GLAMO_ENGINE_MMC] = GLAMO_REG_CLOCK_MMC, + [GLAMO_ENGINE_ISP] = GLAMO_REG_CLOCK_ISP, + [GLAMO_ENGINE_JPEG] = GLAMO_REG_CLOCK_JPEG, + [GLAMO_ENGINE_3D] = GLAMO_REG_CLOCK_3D, + [GLAMO_ENGINE_2D] = GLAMO_REG_CLOCK_2D, + [GLAMO_ENGINE_MPEG_ENC] = GLAMO_REG_CLOCK_MPEG, + [GLAMO_ENGINE_MPEG_DEC] = GLAMO_REG_CLOCK_MPEG, +}; + +void glamo_engine_clkreg_set(struct glamo_core *glamo, + enum glamo_engine engine, + u_int16_t mask, u_int16_t val) +{ + reg_set_bit_mask(glamo, engine_clock_regs[engine], mask, val); +} +EXPORT_SYMBOL_GPL(glamo_engine_clkreg_set); + +u_int16_t glamo_engine_clkreg_get(struct glamo_core *glamo, + enum glamo_engine engine) +{ + u_int16_t val; + + spin_lock(&glamo->lock); + val = __reg_read(glamo, engine_clock_regs[engine]); + spin_unlock(&glamo->lock); + + return val; +} +EXPORT_SYMBOL_GPL(glamo_engine_clkreg_get); + +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); + __reg_clear_bit(glamo, rst->reg, rst->val); + spin_unlock(&glamo->lock); +} +EXPORT_SYMBOL_GPL(glamo_engine_reset); + +void glamo_lcm_reset(int level) +{ + if (!glamo_handle) + return; + + glamo_gpio_setpin(glamo_handle, GLAMO_GPIO4, level); + glamo_gpio_cfgpin(glamo_handle, GLAMO_GPIO4_OUTPUT); + +} +EXPORT_SYMBOL_GPL(glamo_lcm_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); + mdelay(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 * 4); + break; + case 0xfffd: + /* spin until PLLs lock */ + while ((__reg_read(glamo, GLAMO_REG_PLL_GEN5) & 3) != 3) + ; + break; + + /* + * couple of people reported artefacts with 2.6.28 changes, this + * allows reversion to 2.6.24 settings + */ + + case 0x200: + switch (slow_memory) { + /* choice 1 is the most conservative */ + case 1: /* 3 waits on Async BB R & W, Use PLL 1 for mem bus */ + __reg_write(glamo, script[i].reg, 0xef0); + break; + case 2: /* 2 waits on Async BB R & W, Use PLL 1 for mem bus */ + __reg_write(glamo, script[i].reg, 0xea0); + break; + case 3: /* 1 waits on Async BB R & W, Use PLL 1 for mem bus */ + __reg_write(glamo, script[i].reg, 0xe50); + break; + case 4: /* 0 waits on Async BB R & W, Use PLL 1 for mem bus */ + __reg_write(glamo, script[i].reg, 0xe00); + break; + + /* using PLL2 for memory bus increases CPU bandwidth significantly */ + case 5: /* 3 waits on Async BB R & W, Use PLL 2 for mem bus */ + __reg_write(glamo, script[i].reg, 0xef3); + break; + case 6: /* 2 waits on Async BB R & W, Use PLL 2 for mem bus */ + __reg_write(glamo, script[i].reg, 0xea3); + break; + case 7: /* 1 waits on Async BB R & W, Use PLL 2 for mem bus */ + __reg_write(glamo, script[i].reg, 0xe53); + break; + /* default of 0 or >7 is fastest */ + default: /* 0 waits on Async BB R & W, Use PLL 2 for mem bus */ + __reg_write(glamo, script[i].reg, 0xe03); + break; + } + 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, 0x0aba }, /* 90MHz */ + { 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 }, + /* + * b7..b4 = 0 = no wait states on read or write + * b0 = 1 select PLL2 for Host interface, b1 = enable it + */ + { 0x200, 0x0e03 /* this is replaced by script parser */ }, + { 0x202, 0x07ff }, + { 0x212, 0x0000 }, + { 0x214, 0x4000 }, + { 0x216, 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_REGOFS_HOSTBUS + 2, 0xffff }, /* enable on MMIO*/ + + { 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 }, + { GLAMO_REG_GPIO_GEN1, 0x000f }, + { GLAMO_REG_GPIO_GEN2, 0x111e }, + { GLAMO_REG_GPIO_GEN3, 0xccc3 }, + { GLAMO_REG_GPIO_GEN4, 0x111e }, + { GLAMO_REG_GPIO_GEN5, 0x000f }, +}; +#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 + +enum glamo_power { + GLAMO_POWER_ON, + GLAMO_POWER_SUSPEND, +}; + +static void glamo_power(struct glamo_core *glamo, + enum glamo_power new_state) +{ + int n; + unsigned long flags; + + spin_lock_irqsave(&glamo->lock, flags); + + dev_info(&glamo->pdev->dev, "***** glamo_power -> %d\n", new_state); + + /* +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. + */ + + (glamo->pdata->glamo_external_reset)(0); + udelay(10); + (glamo->pdata->glamo_external_reset)(1); + mdelay(5); + + glamo_run_script(glamo, glamo_init_script, + ARRAY_SIZE(glamo_init_script), 0); + + break; + + case GLAMO_POWER_SUSPEND: + + /* nuke interrupts */ + __reg_write(glamo, GLAMO_REG_IRQ_ENABLE, 0x200); + + /* stash a copy of which engines were running */ + glamo->engine_enabled_bitfield_suspend = + glamo->engine_enabled_bitfield; + + /* take down each engine before we kill mem and pll */ + for (n = 0; n < __NUM_GLAMO_ENGINES; n++) + if (glamo->engine_enabled_bitfield & (1 << n)) + __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, 0x200, 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); + + break; + } + + spin_unlock_irqrestore(&glamo->lock, flags); +} + +#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_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 __init glamo_probe(struct platform_device *pdev) +{ + int rc = 0, irq; + struct glamo_core *glamo; + struct platform_device *glamo_mmc_dev; + + 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 bail_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, 0x4000 /*GLAMO_REGOFS_VIDCAP*/); + if (!glamo->base) { + dev_err(&pdev->dev, "failed to ioremap() memory region\n"); + goto bail_free; + } + + platform_set_drvdata(pdev, glamo); + + (glamo->pdata->glamo_external_reset)(0); + udelay(10); + (glamo->pdata->glamo_external_reset)(1); + mdelay(10); + + /* + * finally set the mfd interrupts up + * can't do them earlier or sibling probes blow up + */ + + 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, IRQ_TYPE_EDGE_FALLING); + dev_info(&pdev->dev, "Glamo interrupt registered\n"); + glamo->irq_works = 1; + } else { + dev_err(&pdev->dev, "Glamo interrupt not used\n"); + glamo->irq_works = 0; + } + + + /* confirm it isn't insane version */ + if (!glamo_supported(glamo)) { + dev_err(&pdev->dev, "This Glamo is not supported\n"); + goto bail_irq; + } + + /* sysfs */ + rc = sysfs_create_group(&pdev->dev.kobj, &glamo_attr_group); + if (rc < 0) { + dev_err(&pdev->dev, "cannot create sysfs group\n"); + goto bail_irq; + } + + /* init the chip with canned register set */ + + 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 PLL1: %uHz, PLL2: %uHz\n", + glamo_pll_rate(glamo, GLAMO_PLL1), + glamo_pll_rate(glamo, GLAMO_PLL2)); + + /* bring MCI specific stuff over from our MFD platform data */ + glamo_mci_def_pdata.glamo_can_set_mci_power = + glamo->pdata->glamo_can_set_mci_power; + glamo_mci_def_pdata.glamo_mci_use_slow = + glamo->pdata->glamo_mci_use_slow; + glamo_mci_def_pdata.glamo_irq_is_wired = + glamo->pdata->glamo_irq_is_wired; + + /* start creating the siblings */ + + 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); + + glamo_mmc_dev = glamo->pdata->mmc_dev; + glamo_mmc_dev->name = "glamo-mci"; + glamo_mmc_dev->dev.parent = &pdev->dev; + glamo_mmc_dev->resource = glamo_mmc_resources; + glamo_mmc_dev->num_resources = ARRAY_SIZE(glamo_mmc_resources); + + /* 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); + + /* 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 bail_irq; + } + + return 0; + +bail_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); + } + + iounmap(glamo->base); +bail_free: + platform_set_drvdata(pdev, NULL); + 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->pdata->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) +{ + glamo_handle->suspending = 1; + glamo_power(glamo_handle, GLAMO_POWER_SUSPEND); + + return 0; +} + +static int glamo_resume(struct platform_device *pdev) +{ + glamo_power(glamo_handle, GLAMO_POWER_ON); + glamo_handle->suspending = 0; + + 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"); diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-core.h b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-core.h new file mode 100644 index 0000000000..8e09564bc3 --- /dev/null +++ b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-core.h @@ -0,0 +1,92 @@ +#ifndef __GLAMO_CORE_H +#define __GLAMO_CORE_H + +#include <asm/system.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) + +struct glamo_core { + int irq; + int irq_works; /* 0 means PCB does not support Glamo IRQ */ + struct resource *mem; + struct resource *mem_core; + void __iomem *base; + struct platform_device *pdev; + struct glamofb_platform_data *pdata; + u_int16_t type; + u_int16_t revision; + spinlock_t lock; + u32 engine_enabled_bitfield; + u32 engine_enabled_bitfield_suspend; + int suspending; +}; + +struct glamo_script { + u_int16_t reg; + u_int16_t val; +}; + +int glamo_run_script(struct glamo_core *glamo, + struct glamo_script *script, int len, int may_sleep); + +enum glamo_engine { + GLAMO_ENGINE_CAPTURE, + GLAMO_ENGINE_ISP, + GLAMO_ENGINE_JPEG, + GLAMO_ENGINE_MPEG_ENC, + GLAMO_ENGINE_MPEG_DEC, + GLAMO_ENGINE_LCD, + GLAMO_ENGINE_CMDQ, + GLAMO_ENGINE_2D, + GLAMO_ENGINE_3D, + GLAMO_ENGINE_MMC, + GLAMO_ENGINE_MICROP0, + GLAMO_ENGINE_RISC, + GLAMO_ENGINE_MICROP1_MPEG_ENC, + GLAMO_ENGINE_MICROP1_MPEG_DEC, +#if 0 + GLAMO_ENGINE_H264_DEC, + GLAMO_ENGINE_RISC1, + GLAMO_ENGINE_SPI, +#endif + __NUM_GLAMO_ENGINES +}; + +struct glamo_mci_pdata { + struct glamo_core * pglamo; + unsigned int gpio_detect; + unsigned int gpio_wprotect; + unsigned long ocr_avail; + int (*glamo_can_set_mci_power)(void); + /* glamo-mci asking if it should use the slow clock to card */ + int (*glamo_mci_use_slow)(void); + int (*glamo_irq_is_wired)(void); + void (*mci_suspending)(struct platform_device *dev); + int (*mci_all_dependencies_resumed)(struct platform_device *dev); + +}; + +int glamo_engine_enable(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); + +void glamo_engine_clkreg_set(struct glamo_core *glamo, + enum glamo_engine engine, + u_int16_t mask, u_int16_t val); + +u_int16_t glamo_engine_clkreg_get(struct glamo_core *glamo, + enum glamo_engine engine); +#endif /* __GLAMO_CORE_H */ diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-fb.c b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-fb.c new file mode 100644 index 0000000000..8d1a5ed13d --- /dev/null +++ b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-fb.c @@ -0,0 +1,1084 @@ +/* Smedia Glamo 336x/337x driver + * + * (C) 2007-2008 by Openmoko, Inc. + * Author: Harald Welte <laforge@openmoko.org> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/vmalloc.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/wait.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/uaccess.h> + +#include <asm/div64.h> + +#ifdef CONFIG_PM +#include <linux/pm.h> +#endif + +#include <linux/glamofb.h> + +#include "glamo-regs.h" +#include "glamo-core.h" + +#ifndef DEBUG +#define GLAMO_LOG(...) +#else +#define GLAMO_LOG(...) \ +do { \ + printk(KERN_DEBUG "in %s:%s:%d", __FILE__, __func__, __LINE__); \ + printk(KERN_DEBUG __VA_ARGS__); \ +} while (0); +#endif + + +#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1) + +struct glamofb_handle { + struct fb_info *fb; + struct device *dev; + struct resource *reg; + struct resource *fb_res; + char __iomem *base; + struct glamofb_platform_data *mach_info; + char __iomem *cursor_addr; + int cursor_on; + u_int32_t pseudo_pal[16]; + spinlock_t lock_cmd; + int angle; /* Current rotation angle */ + int blank_mode; +}; + +/* 'sibling' spi device for lcm init */ +static struct platform_device glamo_spi_dev = { + .name = "glamo-lcm-spi", +}; + + +static int reg_read(struct glamofb_handle *glamo, + u_int16_t reg) +{ + int i = 0; + + for (i = 0; i != 2; i++) + nop(); + + return readw(glamo->base + reg); +} + +static void reg_write(struct glamofb_handle *glamo, + u_int16_t reg, u_int16_t val) +{ + int i = 0; + + for (i = 0; i != 2; i++) + nop(); + + writew(val, glamo->base + reg); +} + +static 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, 0x0000 }, /* display A base address 22:16 */ + { GLAMO_REG_LCD_B_BASE1, 0x6000 }, /* display B base address 15:0 */ + { GLAMO_REG_LCD_B_BASE2, 0x0009 }, /* display B 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, + struct glamo_script *script, int len) +{ + int i; + + if (glamo->mach_info->glamo->suspending) { + dev_err(&glamo->mach_info->glamo->pdev->dev, + "IGNORING glamofb_run_script while " + "suspended\n"); + return -EBUSY; + } + + for (i = 0; i < len; i++) { + struct glamo_script *line = &script[i]; + + if (line->reg == 0xffff) + return 0; + else if (line->reg == 0xfffe) + msleep(line->val); + else + 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 (glamo->mach_info->glamo->suspending) { + dev_err(&glamo->mach_info->glamo->pdev->dev, + "IGNORING glamofb_check_var while " + "suspended\n"); + return -EBUSY; + } + + if (var->yres > glamo->mach_info->yres.max) + var->yres = glamo->mach_info->yres.max; + else if (var->yres < glamo->mach_info->yres.min) + var->yres = glamo->mach_info->yres.min; + + if (var->xres > glamo->mach_info->xres.max) + var->xres = glamo->mach_info->xres.max; + else if (var->xres < glamo->mach_info->xres.min) + var->xres = glamo->mach_info->xres.min; + + if (var->bits_per_pixel > glamo->mach_info->bpp.max) + var->bits_per_pixel = glamo->mach_info->bpp.max; + else if (var->bits_per_pixel < glamo->mach_info->bpp.min) + var->bits_per_pixel = glamo->mach_info->bpp.min; + + /* FIXME: set rgb positions */ + switch (var->bits_per_pixel) { + case 16: + switch (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 */ + printk(KERN_ERR + "Smedia driver does not [yet?] support 24/32bpp\n"); + return -EINVAL; + } + + return 0; +} + +static void reg_set_bit_mask(struct glamofb_handle *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); +} + +#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 + +enum orientation {ORIENTATION_PORTRAIT, ORIENTATION_LANDSCAPE}; + +/* the caller has to enxure lock_cmd is held and we are in cmd mode */ +static void __rotate_lcd(struct glamofb_handle *glamo, __u32 rotation) +{ + int glamo_rot; + + if (glamo->mach_info->glamo->suspending) { + dev_err(&glamo->mach_info->glamo->pdev->dev, + "IGNORING rotate_lcd while " + "suspended\n"); + return; + } + + switch (rotation) { + case FB_ROTATE_UR: + glamo_rot = GLAMO_LCD_ROT_MODE_0; + glamo->angle = 0; + break; + case FB_ROTATE_CW: + glamo_rot = GLAMO_LCD_ROT_MODE_90; + glamo->angle = 90; + break; + case FB_ROTATE_UD: + glamo_rot = GLAMO_LCD_ROT_MODE_180; + glamo->angle = 180; + break; + case FB_ROTATE_CCW: + glamo_rot = GLAMO_LCD_ROT_MODE_270; + glamo->angle = 270; + break; + default: + glamo->angle = 0; + glamo_rot = GLAMO_LCD_ROT_MODE_0; + break; + } + + reg_set_bit_mask(glamo, + GLAMO_REG_LCD_WIDTH, + GLAMO_LCD_ROT_MODE_MASK, + glamo_rot); + 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 enum orientation get_orientation(struct fb_var_screeninfo *var) +{ + if (var->xres <= var->yres) + return ORIENTATION_PORTRAIT; + + return ORIENTATION_LANDSCAPE; +} + +static int will_orientation_change(struct fb_var_screeninfo *var) +{ + enum orientation orient = get_orientation(var); + + switch (orient) { + case ORIENTATION_LANDSCAPE: + if (var->rotate == FB_ROTATE_UR || var->rotate == FB_ROTATE_UD) + return 1; + break; + case ORIENTATION_PORTRAIT: + if (var->rotate == FB_ROTATE_CW || var->rotate == FB_ROTATE_CCW) + return 1; + break; + } + return 0; +} + +static void glamofb_update_lcd_controller(struct glamofb_handle *glamo, + struct fb_var_screeninfo *var) +{ + int sync, bp, disp, fp, total, xres, yres, pitch; + int uninitialized_var(orientation_changing); + unsigned long flags; + + if (!glamo || !var) + return; + + if (glamo->mach_info->glamo->suspending) { + dev_err(&glamo->mach_info->glamo->pdev->dev, + "IGNORING glamofb_update_lcd_controller while " + "suspended\n"); + return; + } + + dev_dbg(&glamo->mach_info->glamo->pdev->dev, + "glamofb_update_lcd_controller spin_lock_irqsave\n"); + spin_lock_irqsave(&glamo->lock_cmd, flags); + + if (glamofb_cmd_mode(glamo, 1)) + goto out_unlock; + +/* if (var->pixclock) + glamo_engine_reclock(glamo->mach_info->glamo, + GLAMO_ENGINE_LCD, + var->pixclock);*/ + + xres = var->xres; + yres = var->yres; + + orientation_changing = will_orientation_change(var); + /* Adjust the pitch according to new orientation to come. */ + if (orientation_changing) + pitch = var->yres * var->bits_per_pixel / 8; + else + pitch = var->xres * var->bits_per_pixel / 8; + + reg_set_bit_mask(glamo, + GLAMO_REG_LCD_WIDTH, + GLAMO_LCD_WIDTH_MASK, + xres); + reg_set_bit_mask(glamo, + GLAMO_REG_LCD_HEIGHT, + GLAMO_LCD_HEIGHT_MASK, + yres); + reg_set_bit_mask(glamo, + GLAMO_REG_LCD_PITCH, + GLAMO_LCD_PITCH_MASK, + pitch); + + /* honour the rotation request */ + __rotate_lcd(glamo, var->rotate); + + if (orientation_changing) { + var->xres_virtual = yres; + var->xres = yres; + var->xres_virtual *= 2; + var->yres_virtual = xres; + var->yres = xres; + } else { + var->xres_virtual = xres; + var->yres_virtual = yres; + var->yres_virtual *= 2; + } + + /* update scannout timings */ + sync = 0; + bp = sync + var->hsync_len; + disp = bp + var->left_margin; + fp = disp + xres; + total = fp + var->right_margin; + + reg_set_bit_mask(glamo, GLAMO_REG_LCD_HORIZ_TOTAL, + GLAMO_LCD_HV_TOTAL_MASK, total); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_HORIZ_RETR_START, + GLAMO_LCD_HV_RETR_START_MASK, sync); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_HORIZ_RETR_END, + GLAMO_LCD_HV_RETR_END_MASK, bp); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_HORIZ_DISP_START, + GLAMO_LCD_HV_RETR_DISP_START_MASK, disp); + reg_set_bit_mask(glamo, 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 + yres; + total = fp + var->lower_margin; + + reg_set_bit_mask(glamo, GLAMO_REG_LCD_VERT_TOTAL, + GLAMO_LCD_HV_TOTAL_MASK, total); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_VERT_RETR_START, + GLAMO_LCD_HV_RETR_START_MASK, sync); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_VERT_RETR_END, + GLAMO_LCD_HV_RETR_END_MASK, bp); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_VERT_DISP_START, + GLAMO_LCD_HV_RETR_DISP_START_MASK, disp); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_VERT_DISP_END, + GLAMO_LCD_HV_RETR_DISP_END_MASK, fp); + + glamofb_cmd_mode(glamo, 0); + +out_unlock: + dev_dbg(&glamo->mach_info->glamo->pdev->dev, + "glamofb_update_lcd_controller spin_unlock_irqrestore\n"); + spin_unlock_irqrestore(&glamo->lock_cmd, flags); +} + +static int glamofb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct glamofb_handle *glamo = info->par; + u_int16_t page = var->yoffset / glamo->mach_info->yres.defval; + reg_write(glamo, GLAMO_REG_LCD_COMMAND2, page); + + return 0; +} + +static int glamofb_set_par(struct fb_info *info) +{ + struct glamofb_handle *glamo = info->par; + struct fb_var_screeninfo *var = &info->var; + + if (glamo->mach_info->glamo->suspending) { + dev_err(&glamo->mach_info->glamo->pdev->dev, + "IGNORING glamofb_set_par while " + "suspended\n"); + return -EBUSY; + } + + switch (var->bits_per_pixel) { + case 16: + info->fix.visual = FB_VISUAL_TRUECOLOR; + break; + default: + printk("Smedia driver doesn't support != 16bpp\n"); + return -EINVAL; + } + + info->fix.line_length = (var->xres * var->bits_per_pixel) / 8; + + glamofb_update_lcd_controller(glamo, var); + + return 0; +} + + +static void notify_blank(struct fb_info *info, int blank_mode) +{ + struct fb_event event; + + event.info = info; + event.data = &blank_mode; + fb_notifier_call_chain(FB_EVENT_CONBLANK, &event); +} + + +static int glamofb_blank(int blank_mode, struct fb_info *info) +{ + struct glamofb_handle *gfb = info->par; + struct glamo_core *gcore = gfb->mach_info->glamo; + + 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: + /* Simulating FB_BLANK_NORMAL allow turning off backlight */ + if (gfb->blank_mode != FB_BLANK_NORMAL) + notify_blank(info, FB_BLANK_NORMAL); + + /* LCM need notification before pixel clock is stopped */ + notify_blank(info, blank_mode); + + /* disable the pixel clock */ + glamo_engine_clkreg_set(gcore, GLAMO_ENGINE_LCD, + GLAMO_CLOCK_LCD_EN_DCLK, 0); + gfb->blank_mode = blank_mode; + break; + case FB_BLANK_UNBLANK: + case FB_BLANK_NORMAL: + /* enable the pixel clock if off */ + if (gfb->blank_mode == FB_BLANK_POWERDOWN) + glamo_engine_clkreg_set(gcore, + GLAMO_ENGINE_LCD, + GLAMO_CLOCK_LCD_EN_DCLK, + GLAMO_CLOCK_LCD_EN_DCLK); + + notify_blank(info, blank_mode); + 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; + + if (glamo->mach_info->glamo->suspending) { + dev_err(&glamo->mach_info->glamo->pdev->dev, + "IGNORING glamofb_set_par while " + "suspended\n"); + return -EBUSY; + } + + 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; +} + +#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] = reg_read(glamo, GLAMO_REG_LCD_STATUS2) & 0x3ff; + count[1] = 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 = reg_read(glamo, GLAMO_REG_LCD_CURSOR_Y_POS); + size = reg_read(glamo, GLAMO_REG_LCD_CURSOR_Y_SIZE); + + glamofb_vsync_wait(glamo, y, size, 30); + } + + 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 */ + 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); + + reg_write(glamo, GLAMO_REG_LCD_CURSOR_X_SIZE, + cursor->enable ? cursor->image.width : 0); + + if (cursor->set & FB_CUR_SETPOS) { + reg_write(glamo, GLAMO_REG_LCD_CURSOR_X_POS, + cursor->image.dx); + 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]; + + reg_write(glamo, GLAMO_REG_LCD_CURSOR_FG_COLOR, fg); + reg_write(glamo, GLAMO_REG_LCD_CURSOR_BG_COLOR, bg); + reg_write(glamo, GLAMO_REG_LCD_CURSOR_DST_COLOR, fg); + } + + if (cursor->set & FB_CUR_SETHOT) + 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; + reg_write(glamo, GLAMO_REG_LCD_CURSOR_PITCH, + pitch); + 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 inline int glamofb_cmdq_empty(struct glamofb_handle *gfb) +{ + /* DGCMdQempty -- 1 == command queue is empty */ + return reg_read(gfb, GLAMO_REG_LCD_STATUS1) & (1 << 15); +} + +/* call holding gfb->lock_cmd when locking, until you unlock */ +int glamofb_cmd_mode(struct glamofb_handle *gfb, int on) +{ + int timeout = 2000000; + + if (gfb->mach_info->glamo->suspending) { + dev_err(&gfb->mach_info->glamo->pdev->dev, + "IGNORING glamofb_cmd_mode while " + "suspended\n"); + return -EBUSY; + } + + 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--)) + /* yield() */; + 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 */ + 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 ((!reg_read(gfb, GLAMO_REG_LCD_STATUS2) & (1 << 12)) && + (timeout--)) + /* yield() */; + 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 (reg_read(gfb, GLAMO_REG_LCD_MODE3) & GLAMO_LCD_MODE3_RGB) + reg_write(gfb, GLAMO_REG_LCD_COMMAND1, + GLAMO_LCD_CMD_TYPE_DISP | + GLAMO_LCD_CMD_DATA_DISP_SYNC); + + reg_write(gfb, GLAMO_REG_LCD_COMMAND1, + GLAMO_LCD_CMD_TYPE_DISP | + GLAMO_LCD_CMD_DATA_DISP_FIRE); + } + + return 0; +} +EXPORT_SYMBOL_GPL(glamofb_cmd_mode); + + +int glamofb_cmd_write(struct glamofb_handle *gfb, u_int16_t val) +{ + int timeout = 200000; + + if (gfb->mach_info->glamo->suspending) { + dev_err(&gfb->mach_info->glamo->pdev->dev, + "IGNORING glamofb_cmd_write while " + "suspended\n"); + return -EBUSY; + } + + dev_dbg(gfb->dev, "%s: waiting for cmdq empty\n", __func__); + while ((!glamofb_cmdq_empty(gfb)) && (timeout--)) + yield(); + if (timeout < 0) { + printk(KERN_ERR"*************" + "glamofb cmd_queue never got empty" + "*************\n"); + return 1; + } + dev_dbg(gfb->dev, "idle, writing 0x%04x\n", val); + + reg_write(gfb, GLAMO_REG_LCD_COMMAND1, val); + + return 0; +} +EXPORT_SYMBOL_GPL(glamofb_cmd_write); + +static struct fb_ops glamofb_ops = { + .owner = THIS_MODULE, + .fb_check_var = glamofb_check_var, + .fb_pan_display = glamofb_pan_display, + .fb_set_par = glamofb_set_par, + .fb_blank = glamofb_blank, + .fb_setcolreg = glamofb_setcolreg, +#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 __init glamofb_probe(struct platform_device *pdev) +{ + int rc = -EIO; + struct fb_info *fbinfo; + struct glamofb_handle *glamofb; + struct glamofb_platform_data *mach_info = pdev->dev.platform_data; + + printk(KERN_INFO "SMEDIA Glamo frame buffer driver (C) 2007 " + "Openmoko, Inc.\n"); + + fbinfo = framebuffer_alloc(sizeof(struct glamofb_handle), &pdev->dev); + if (!fbinfo) + return -ENOMEM; + + glamofb = fbinfo->par; + glamofb->fb = fbinfo; + glamofb->dev = &pdev->dev; + + glamofb->angle = 0; + 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, + RESSIZE(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, + mach_info->fb_mem_size, + 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(glamofb->reg->start, RESSIZE(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 = mach_info->fb_mem_size; + + fbinfo->screen_base = ioremap(glamofb->fb_res->start, + RESSIZE(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); + + glamofb->mach_info = pdev->dev.platform_data; + + 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 = mach_info->yres.defval; + fbinfo->fix.ywrapstep = 0; + fbinfo->fix.accel = FB_ACCEL_GLAMO; + + 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; /* FIXME */ + fbinfo->var.vmode = FB_VMODE_NONINTERLACED; + + fbinfo->fbops = &glamofb_ops; + fbinfo->flags = FBINFO_FLAG_DEFAULT; + fbinfo->pseudo_palette = &glamofb->pseudo_pal; + + fbinfo->var.xres = mach_info->xres.defval; + fbinfo->var.xres_virtual = mach_info->xres.defval; + fbinfo->var.yres = mach_info->yres.defval; + fbinfo->var.yres_virtual = mach_info->yres.defval * 2; + fbinfo->var.bits_per_pixel = mach_info->bpp.defval; + + fbinfo->var.pixclock = mach_info->pixclock; + fbinfo->var.left_margin = mach_info->left_margin; + fbinfo->var.right_margin = mach_info->right_margin; + fbinfo->var.upper_margin = mach_info->upper_margin; + fbinfo->var.lower_margin = mach_info->lower_margin; + fbinfo->var.hsync_len = mach_info->hsync_len; + fbinfo->var.vsync_len = mach_info->vsync_len; + + memset(fbinfo->screen_base, 0, + mach_info->xres.max * + mach_info->yres.max * + mach_info->bpp.max / 8); + + glamo_engine_enable(mach_info->glamo, GLAMO_ENGINE_LCD); + glamo_engine_reset(mach_info->glamo, GLAMO_ENGINE_LCD); + + 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 + +#ifdef CONFIG_MFD_GLAMO_FB_XGLAMO_WORKAROUND + /* sysfs */ + rc = sysfs_create_group(&pdev->dev.kobj, &glamo_fb_attr_group); + if (rc < 0) { + dev_err(&pdev->dev, "cannot create sysfs group\n"); + goto out_unmap_fb; + } +#endif + + rc = register_framebuffer(fbinfo); + if (rc < 0) { + dev_err(&pdev->dev, "failed to register framebuffer\n"); + goto out_unmap_fb; + } + + if (mach_info->spi_info) { + /* register the sibling spi device */ + mach_info->spi_info->glamofb_handle = glamofb; + glamo_spi_dev.dev.parent = &pdev->dev; + glamo_spi_dev.dev.platform_data = mach_info->spi_info; + platform_device_register(&glamo_spi_dev); + } + + 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, RESSIZE(glamofb->fb_res)); +out_release_reg: + release_mem_region(glamofb->reg->start, RESSIZE(glamofb->reg)); +out_free: + framebuffer_release(fbinfo); + return rc; +} + +static int glamofb_remove(struct platform_device *pdev) +{ + struct glamofb_handle *glamofb = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + iounmap(glamofb->base); + release_mem_region(glamofb->reg->start, RESSIZE(glamofb->reg)); + kfree(glamofb); + + return 0; +} + +#ifdef CONFIG_PM + +static int glamofb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct glamofb_handle *gfb = platform_get_drvdata(pdev); + + /* we need to stop anything touching our framebuffer */ + fb_set_suspend(gfb->fb, 1); + + /* 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 platform_device *pdev) +{ + struct glamofb_handle *gfb = platform_get_drvdata(pdev); + struct glamofb_platform_data *mach_info = pdev->dev.platform_data; + + /* OK let's allow framebuffer ops again */ + /* gfb->fb->screen_base = ioremap(gfb->fb_res->start, + RESSIZE(gfb->fb_res)); */ + glamo_engine_enable(mach_info->glamo, GLAMO_ENGINE_LCD); + glamo_engine_reset(mach_info->glamo, GLAMO_ENGINE_LCD); + + printk(KERN_ERR"spin_lock_init\n"); + spin_lock_init(&gfb->lock_cmd); + glamofb_init_regs(gfb); +#ifdef CONFIG_MFD_GLAMO_HWACCEL + glamofb_cursor_onoff(gfb, 1); +#endif + + fb_set_suspend(gfb->fb, 0); + + return 0; +} +#else +#define glamofb_suspend NULL +#define glamofb_resume NULL +#endif + +static struct platform_driver glamofb_driver = { + .probe = glamofb_probe, + .remove = glamofb_remove, + .suspend = glamofb_suspend, + .resume = glamofb_resume, + .driver = { + .name = "glamo-fb", + .owner = THIS_MODULE, + }, +}; + +static int __devinit glamofb_init(void) +{ + return platform_driver_register(&glamofb_driver); +} + +static void __exit glamofb_cleanup(void) +{ + platform_driver_unregister(&glamofb_driver); +} + +module_init(glamofb_init); +module_exit(glamofb_cleanup); + +MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>"); +MODULE_DESCRIPTION("Smedia Glamo 336x/337x framebuffer driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-gpio.c b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-gpio.c new file mode 100644 index 0000000000..45d0bf9126 --- /dev/null +++ b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-gpio.c @@ -0,0 +1,62 @@ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/io.h> + +#include <linux/glamo-gpio.h> + +#include "glamo-core.h" +#include "glamo-regs.h" + +void glamo_gpio_setpin(struct glamo_core *glamo, unsigned int pin, + unsigned int value) +{ + unsigned int reg = REG_OF_GPIO(pin); + u_int16_t tmp; + + spin_lock(&glamo->lock); + tmp = readw(glamo->base + reg); + if (value) + tmp |= OUTPUT_BIT(pin); + else + tmp &= ~OUTPUT_BIT(pin); + writew(tmp, glamo->base + reg); + spin_unlock(&glamo->lock); +} +EXPORT_SYMBOL(glamo_gpio_setpin); + +int glamo_gpio_getpin(struct glamo_core *glamo, unsigned int pin) +{ + return readw(REG_OF_GPIO(pin)) & INPUT_BIT(pin) ? 1 : 0; +} +EXPORT_SYMBOL(glamo_gpio_getpin); + +void glamo_gpio_cfgpin(struct glamo_core *glamo, unsigned int pinfunc) +{ + unsigned int reg = REG_OF_GPIO(pinfunc); + u_int16_t tmp; + + spin_lock(&glamo->lock); + tmp = readw(glamo->base + reg); + + if ((pinfunc & 0x00f0) == GLAMO_GPIO_F_FUNC) { + /* pin is a function pin: clear gpio bit */ + tmp &= ~FUNC_BIT(pinfunc); + } else { + /* pin is gpio: set gpio bit */ + tmp |= FUNC_BIT(pinfunc); + + if (pinfunc & GLAMO_GPIO_F_IN) { + /* gpio input: set bit to disable output mode */ + tmp |= GPIO_OUT_BIT(pinfunc); + } else if (pinfunc & GLAMO_GPIO_F_OUT) { + /* gpio output: clear bit to enable output mode */ + tmp &= ~GPIO_OUT_BIT(pinfunc); + } + } + writew(tmp, glamo->base + reg); + spin_unlock(&glamo->lock); +} +EXPORT_SYMBOL(glamo_gpio_cfgpin); + diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-lcm-spi.c b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-lcm-spi.c new file mode 100644 index 0000000000..a7129fe798 --- /dev/null +++ b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-lcm-spi.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2007 Openmoko, Inc. + * Author: Harald Welte <laforge@openmoko.org> + * + * Smedia Glamo GPIO based SPI driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This driver currently only implements a minimum subset of the hardware + * features, esp. those features that are required to drive the jbt6k74 + * LCM controller asic in the TD028TTEC1 LCM. + * +*/ + +#define DEBUG + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> + +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> +#include <linux/spi/glamo.h> + +#include <linux/glamofb.h> + +#include <mach/hardware.h> + +#include "glamo-core.h" +#include "glamo-regs.h" + +struct glamo_spi { + struct spi_bitbang bitbang; + struct spi_master *master; + struct glamo_spi_info *info; + struct device *dev; +}; + +static inline struct glamo_spi *to_gs(struct spi_device *spi) +{ + return spi->controller_data; +} + +static int glamo_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) +{ + unsigned int bpw; + + bpw = t ? t->bits_per_word : spi->bits_per_word; + + if (bpw != 9 && bpw != 8) { + dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw); + return -EINVAL; + } + + return 0; +} + +static void glamo_spi_chipsel(struct spi_device *spi, int value) +{ +#if 0 + struct glamo_spi *gs = to_gs(spi); + + dev_dbg(&spi->dev, "chipsel %d: spi=%p, gs=%p, info=%p, handle=%p\n", + value, spi, gs, gs->info, gs->info->glamofb_handle); + + glamofb_cmd_mode(gs->info->glamofb_handle, value); +#endif +} + +static int glamo_spi_txrx(struct spi_device *spi, struct spi_transfer *t) +{ + struct glamo_spi *gs = to_gs(spi); + const u_int16_t *ui16 = (const u_int16_t *) t->tx_buf; + u_int16_t nine_bits; + int i; + + dev_dbg(&spi->dev, "txrx: tx %p, rx %p, bpw %d, len %d\n", + t->tx_buf, t->rx_buf, t->bits_per_word, t->len); + + if (spi->bits_per_word == 9) + nine_bits = (1 << 9); + else + nine_bits = 0; + + if (t->len > 3 * sizeof(u_int16_t)) { + dev_err(&spi->dev, "this driver doesn't support " + "%u sized xfers\n", t->len); + return -EINVAL; + } + + for (i = 0; i < t->len/sizeof(u_int16_t); i++) { + /* actually transfer the data */ +#if 1 + glamofb_cmd_write(gs->info->glamofb_handle, + GLAMO_LCD_CMD_TYPE_SERIAL | nine_bits | + (1 << 10) | (1 << 11) | (ui16[i] & 0x1ff)); +#endif + /* FIXME: fire ?!? */ + if (i == 0 && (ui16[i] & 0x1ff) == 0x29) { + dev_dbg(&spi->dev, "leaving command mode\n"); + glamofb_cmd_mode(gs->info->glamofb_handle, 0); + } + } + + return t->len; +} + +static int glamo_spi_setup(struct spi_device *spi) +{ + int ret; + + if (!spi->bits_per_word) + spi->bits_per_word = 9; + + /* FIXME: hardware can do this */ + if (spi->mode & SPI_LSB_FIRST) + return -EINVAL; + + ret = glamo_spi_setupxfer(spi, NULL); + if (ret < 0) { + dev_err(&spi->dev, "setupxfer returned %d\n", ret); + return ret; + } + + dev_dbg(&spi->dev, "%s: mode %d, %u bpw\n", + __FUNCTION__, spi->mode, spi->bits_per_word); + + return 0; +} + +static int glamo_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct glamo_spi *sp; + int ret; + int i; + + master = spi_alloc_master(&pdev->dev, sizeof(struct glamo_spi)); + if (master == NULL) { + dev_err(&pdev->dev, "failed to allocate spi master\n"); + ret = -ENOMEM; + goto err; + } + + sp = spi_master_get_devdata(master); + memset(sp, 0, sizeof(struct glamo_spi)); + + sp->master = spi_master_get(master); + sp->info = pdev->dev.platform_data; + if (!sp->info) { + dev_err(&pdev->dev, "can't operate without platform data\n"); + ret = -EIO; + goto err_no_pdev; + } + dev_dbg(&pdev->dev, "sp->info(pdata) = %p\n", sp->info); + + sp->dev = &pdev->dev; + + platform_set_drvdata(pdev, sp); + + sp->bitbang.master = sp->master; + sp->bitbang.setup_transfer = glamo_spi_setupxfer; + sp->bitbang.chipselect = glamo_spi_chipsel; + sp->bitbang.txrx_bufs = glamo_spi_txrx; + sp->bitbang.master->setup = glamo_spi_setup; + + ret = spi_bitbang_start(&sp->bitbang); + if (ret) + goto err_no_bitbang; + + /* register the chips to go with the board */ + + glamofb_cmd_mode(sp->info->glamofb_handle, 1); + + for (i = 0; i < sp->info->board_size; i++) { + dev_info(&pdev->dev, "registering %p: %s\n", + &sp->info->board_info[i], + sp->info->board_info[i].modalias); + + sp->info->board_info[i].controller_data = sp; + spi_new_device(master, sp->info->board_info + i); + } + + return 0; + +err_no_bitbang: + platform_set_drvdata(pdev, NULL); +err_no_pdev: + spi_master_put(sp->bitbang.master); +err: + return ret; + +} + +static int glamo_spi_remove(struct platform_device *pdev) +{ + struct glamo_spi *sp = platform_get_drvdata(pdev); + + spi_bitbang_stop(&sp->bitbang); + spi_master_put(sp->bitbang.master); + + return 0; +} + +#define glamo_spi_suspend NULL +#define glamo_spi_resume NULL + +static struct platform_driver glamo_spi_drv = { + .probe = glamo_spi_probe, + .remove = glamo_spi_remove, + .suspend = glamo_spi_suspend, + .resume = glamo_spi_resume, + .driver = { + .name = "glamo-lcm-spi", + .owner = THIS_MODULE, + }, +}; + +static int __init glamo_spi_init(void) +{ + return platform_driver_register(&glamo_spi_drv); +} + +static void __exit glamo_spi_exit(void) +{ + platform_driver_unregister(&glamo_spi_drv); +} + +module_init(glamo_spi_init); +module_exit(glamo_spi_exit); + +MODULE_DESCRIPTION("Smedia Glamo 336x/337x LCM serial command SPI Driver"); +MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>") +MODULE_LICENSE("GPL"); diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-mci.c b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-mci.c new file mode 100644 index 0000000000..956be57177 --- /dev/null +++ b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-mci.c @@ -0,0 +1,1167 @@ +/* + * linux/drivers/mmc/host/glamo-mmc.c - Glamo MMC driver + * + * Copyright (C) 2007 Openmoko, Inc, Andy Green <andy@openmoko.com> + * Based on S3C MMC driver that was: + * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/mmc/mmc.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/spinlock.h> +#include <linux/workqueue.h> + +#include <asm/dma.h> +#include <asm/dma-mapping.h> +#include <asm/io.h> + +#include "glamo-mci.h" +#include "glamo-core.h" +#include "glamo-regs.h" + +/* from glamo-core.c */ +extern struct glamo_mci_pdata glamo_mci_def_pdata; + +static spinlock_t clock_lock; + +#define DRIVER_NAME "glamo-mci" +#define RESSIZE(ressource) (((ressource)->end - (ressource)->start) + 1) + +static void glamo_mci_send_request(struct mmc_host *mmc); + +/* + * 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 = 50000000 / 3; +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); + + +/* + * SD Signal drive strength + * + * you can override this on kernel commandline using + * + * glamo_mci.sd_drive=0 + * + * for example + */ + +static int sd_drive; +module_param(sd_drive, int, 0644); + +/* + * SD allow SD clock to run while idle + * + * you can override this on kernel commandline using + * + * glamo_mci.sd_idleclk=0 + * + * for example + */ + +static int sd_idleclk = 0; /* disallow idle clock by default */ +module_param(sd_idleclk, int, 0644); + +/* used to stash real idleclk state in suspend: we force it to run in there */ +static int suspend_sd_idleclk; + + +unsigned char CRC7(u8 * pu8, int cnt) +{ + u8 crc = 0; + + while (cnt--) { + int n; + u8 d = *pu8++; + for (n = 0; n < 8; n++) { + crc <<= 1; + if ((d & 0x80) ^ (crc & 0x80)) + crc ^= 0x09; + d <<= 1; + } + } + return (crc << 1) | 1; +} + +static int get_data_buffer(struct glamo_mci_host *host, + volatile u32 *words, volatile u16 **pointer) +{ + struct scatterlist *sg; + + *words = 0; + *pointer = NULL; + + if (host->pio_active == XFER_NONE) + return -EINVAL; + + if ((!host->mrq) || (!host->mrq->data)) + return -EINVAL; + + if (host->pio_sgptr >= host->mrq->data->sg_len) { + dev_dbg(&host->pdev->dev, "no more buffers (%i/%i)\n", + host->pio_sgptr, host->mrq->data->sg_len); + return -EBUSY; + } + sg = &host->mrq->data->sg[host->pio_sgptr]; + + *words = sg->length >> 1; /* we are working with a 16-bit data bus */ + *pointer = page_address(sg_page(sg)) + sg->offset; + + BUG_ON(((long)(*pointer)) & 1); + + host->pio_sgptr++; + + /* dev_info(&host->pdev->dev, "new buffer (%i/%i)\n", + host->pio_sgptr, host->mrq->data->sg_len); */ + return 0; +} + +static void do_pio_read(struct glamo_mci_host *host) +{ + int res; + u16 __iomem *from_ptr = host->base_data + (RESSIZE(host->mem_data) / + sizeof(u16) / 2); +#ifdef DEBUG + u16 * block; +#endif + + while (1) { + res = get_data_buffer(host, &host->pio_words, &host->pio_ptr); + if (res) { + host->pio_active = XFER_NONE; + host->complete_what = COMPLETION_FINALIZE; + + dev_dbg(&host->pdev->dev, "pio_read(): " + "complete (no more data).\n"); + return; + } + + dev_dbg(&host->pdev->dev, "pio_read(): host->pio_words: %d\n", + host->pio_words); + + host->pio_count += host->pio_words << 1; + +#ifdef DEBUG + block = (u16 *)host->pio_ptr; + res = host->pio_words << 1; +#endif +#if 0 + /* u16-centric memcpy */ + while (host->pio_words--) + *host->pio_ptr++ = *from_ptr++; +#else + /* memcpy can be faster? */ + memcpy((void *)host->pio_ptr, from_ptr, host->pio_words << 1); + host->pio_ptr += host->pio_words; +#endif + +#ifdef DEBUG + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1, + (void *)block, res, 1); +#endif + } +} + +static int do_pio_write(struct glamo_mci_host *host) +{ + int res = 0; + volatile u16 __iomem *to_ptr = host->base_data; + int err = 0; + + dev_dbg(&host->pdev->dev, "pio_write():\n"); + while (!res) { + res = get_data_buffer(host, &host->pio_words, &host->pio_ptr); + if (res) + continue; + + dev_dbg(&host->pdev->dev, "pio_write():new source: [%i]@[%p]\n", + host->pio_words, host->pio_ptr); + + host->pio_count += host->pio_words << 1; + while (host->pio_words--) + writew(*host->pio_ptr++, to_ptr++); + } + + dev_dbg(&host->pdev->dev, "pio_write(): complete\n"); + host->pio_active = XFER_NONE; + return err; +} + +static void __glamo_mci_fix_card_div(struct glamo_mci_host *host, int div) +{ + unsigned long flags; + + spin_lock_irqsave(&clock_lock, flags); + + if (div < 0) { + /* stop clock - remove clock from divider input */ + writew(readw(glamo_mci_def_pdata.pglamo->base + + GLAMO_REG_CLOCK_GEN5_1) & (~GLAMO_CLOCK_GEN51_EN_DIV_TCLK), + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1); + + goto done; + } else { + /* set the nearest prescaler factor + * + * register shared with SCLK divisor -- no chance of race because + * we don't use sensor interface + */ + writew((readw(glamo_mci_def_pdata.pglamo->base + + GLAMO_REG_CLOCK_GEN8) & 0xff00) | div, + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN8); + /* enable clock to divider input */ + writew(readw(glamo_mci_def_pdata.pglamo->base + + GLAMO_REG_CLOCK_GEN5_1) | GLAMO_CLOCK_GEN51_EN_DIV_TCLK, + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1); + } + + if (host->force_slow_during_powerup) + div = host->clk_rate / sd_post_power_clock; + else + if (host->pdata->glamo_mci_use_slow) + if ((host->pdata->glamo_mci_use_slow)()) + div = div * sd_slow_ratio; + + if (div > 255) + div = 255; + + /* + * set the nearest prescaler factor + * + * register shared with SCLK divisor -- no chance of race because + * we don't use sensor interface + */ + writew((readw(glamo_mci_def_pdata.pglamo->base + + GLAMO_REG_CLOCK_GEN8) & 0xff00) | div, + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN8); + /* enable clock to divider input */ + writew(readw(glamo_mci_def_pdata.pglamo->base + + GLAMO_REG_CLOCK_GEN5_1) | GLAMO_CLOCK_GEN51_EN_DIV_TCLK, + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1); + +done: + spin_unlock_irqrestore(&clock_lock, flags); +} + +static int __glamo_mci_set_card_clock(struct glamo_mci_host *host, int freq, + int *division) +{ + int div = 0; + int real_rate = 0; + + if (freq) { + /* Set clock */ + for (div = 0; div < 256; div++) { + real_rate = host->clk_rate / (div + 1); + if (real_rate <= freq) + break; + } + if (div > 255) + div = 255; + + if (division) + *division = div; + + __glamo_mci_fix_card_div(host, div); + + } else { + /* stop clock */ + if (division) + *division = 0xff; + + if (!sd_idleclk && !host->force_slow_during_powerup) + /* clock off */ + __glamo_mci_fix_card_div(host, -1); + } + + return real_rate; +} + + +static void glamo_mci_irq_worker(struct work_struct *work) +{ + struct glamo_mci_host *host = + container_of(work, struct glamo_mci_host, irq_work); + struct mmc_command *cmd = host->mrq->cmd; + + if (host->pio_active == XFER_READ) + do_pio_read(host); + + host->mrq->data->bytes_xfered = host->pio_count; + dev_dbg(&host->pdev->dev, "count=%d\n", host->pio_count); + + /* issue STOP if we have been given one to use */ + if (host->mrq->stop) { + host->cmd_is_stop = 1; + glamo_mci_send_request(host->mmc); + host->cmd_is_stop = 0; + } + + if (!sd_idleclk && !host->force_slow_during_powerup) + /* clock off */ + __glamo_mci_fix_card_div(host, -1); + + host->complete_what = COMPLETION_NONE; + host->mrq = NULL; + mmc_request_done(host->mmc, cmd->mrq); +} + +static void glamo_mci_irq_host(struct glamo_mci_host *host) +{ + u16 status; + struct mmc_command *cmd; + unsigned long iflags; + + if (host->suspending) { /* bad news, dangerous time */ + dev_err(&host->pdev->dev, "****glamo_mci_irq before resumed\n"); + return; + } + + if (!host->mrq) + return; + cmd = host->mrq->cmd; + if (!cmd) + return; + + spin_lock_irqsave(&host->complete_lock, iflags); + + status = readw(host->base + GLAMO_REG_MMC_RB_STAT1); + dev_dbg(&host->pdev->dev, "status = 0x%04x\n", status); + + /* ack this interrupt source */ + writew(GLAMO_IRQ_MMC, + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_IRQ_CLEAR); + + /* 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; + 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; + } + + /* + * disable the initial slow start after first bulk transfer + */ + if (host->force_slow_during_powerup) + host->force_slow_during_powerup--; + + /* + * we perform the memcpy out of Glamo memory outside of IRQ context + * so we don't block other interrupts + */ + schedule_work(&host->irq_work); + + goto leave; + +done: + host->complete_what = COMPLETION_NONE; + host->mrq = NULL; + mmc_request_done(host->mmc, cmd->mrq); +leave: + spin_unlock_irqrestore(&host->complete_lock, iflags); +} + +static void glamo_mci_irq(unsigned int irq, struct irq_desc *desc) +{ + struct glamo_mci_host *host = (struct glamo_mci_host *) + desc->handler_data; + + if (host) + glamo_mci_irq_host(host); + +} + +static int glamo_mci_send_command(struct glamo_mci_host *host, + struct mmc_command *cmd) +{ + u8 u8a[6]; + u16 fire = 0; + + /* if we can't do it, reject as busy */ + if (!readw(host->base + GLAMO_REG_MMC_RB_STAT1) & + GLAMO_STAT1_MMC_IDLE) { + host->mrq = NULL; + cmd->error = -EBUSY; + mmc_request_done(host->mmc, host->mrq); + return -EBUSY; + } + + /* create an array in wire order for CRC computation */ + u8a[0] = 0x40 | (cmd->opcode & 0x3f); + u8a[1] = (u8)(cmd->arg >> 24); + u8a[2] = (u8)(cmd->arg >> 16); + u8a[3] = (u8)(cmd->arg >> 8); + u8a[4] = (u8)cmd->arg; + u8a[5] = CRC7(&u8a[0], 5); /* CRC7 on first 5 bytes of packet */ + + /* issue the wire-order array including CRC in register order */ + writew((u8a[4] << 8) | u8a[5], host->base + GLAMO_REG_MMC_CMD_REG1); + writew((u8a[2] << 8) | u8a[3], host->base + GLAMO_REG_MMC_CMD_REG2); + writew((u8a[0] << 8) | u8a[1], host->base + GLAMO_REG_MMC_CMD_REG3); + + /* command index toggle */ + fire |= (host->ccnt & 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_R6: /* same index as R7 and R1 */ + 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 0x33: /* observed issued by MCI */ + 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 */ + break; + default: + fire |= GLAMO_FIRE_MMC_CC_BASIC; /* "basic command" */ + break; + } + + /* always largest timeout */ + writew(0xfff, host->base + GLAMO_REG_MMC_TIMEOUT); + + /* Generate interrupt on txfer */ + writew((readw(host->base + GLAMO_REG_MMC_BASIC) & 0x3e) | + 0x0800 | GLAMO_BASIC_MMC_NO_CLK_RD_WAIT | + GLAMO_BASIC_MMC_EN_COMPL_INT | (sd_drive << 6), + host->base + GLAMO_REG_MMC_BASIC); + + /* send the command out on the wire */ + /* dev_info(&host->pdev->dev, "Using FIRE %04X\n", fire); */ + writew(fire, host->base + GLAMO_REG_MMC_CMD_FIRE); + cmd->error = 0; + return 0; +} + +static int glamo_mci_prepare_pio(struct glamo_mci_host *host, + struct mmc_data *data) +{ + /* + * the S-Media-internal RAM offset for our MMC buffer + * Read is halfway up the buffer and write is at the start + */ + if (data->flags & MMC_DATA_READ) { + writew((u16)(GLAMO_FB_SIZE + (RESSIZE(host->mem_data) / 2)), + host->base + GLAMO_REG_MMC_WDATADS1); + writew((u16)((GLAMO_FB_SIZE + + (RESSIZE(host->mem_data) / 2)) >> 16), + host->base + GLAMO_REG_MMC_WDATADS2); + } else { + writew((u16)GLAMO_FB_SIZE, host->base + + GLAMO_REG_MMC_RDATADS1); + writew((u16)(GLAMO_FB_SIZE >> 16), host->base + + GLAMO_REG_MMC_RDATADS2); + } + + /* set up the block info */ + writew(data->blksz, host->base + GLAMO_REG_MMC_DATBLKLEN); + writew(data->blocks, host->base + GLAMO_REG_MMC_DATBLKCNT); + dev_dbg(&host->pdev->dev, "(blksz=%d, count=%d)\n", + data->blksz, data->blocks); + host->pio_sgptr = 0; + host->pio_words = 0; + host->pio_count = 0; + host->pio_active = 0; + /* if write, prep the write into the shared RAM before the command */ + if (data->flags & MMC_DATA_WRITE) { + host->pio_active = XFER_WRITE; + return do_pio_write(host); + } + host->pio_active = XFER_READ; + return 0; +} + +static void glamo_mci_send_request(struct mmc_host *mmc) +{ + struct glamo_mci_host *host = mmc_priv(mmc); + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; + u16 * pu16 = (u16 *)&cmd->resp[0]; + u16 * reg_resp = (u16 *)(host->base + GLAMO_REG_MMC_CMD_RSP1); + u16 status; + int n; + int timeout = 1000000; + int insanity_timeout = 1000000; + + if (host->suspending) { + dev_err(&host->pdev->dev, "IGNORING glamo_mci_send_request while " + "suspended\n"); + cmd->error = -EIO; + if (cmd->data) + cmd->data->error = -EIO; + mmc_request_done(mmc, mrq); + return; + } + + host->ccnt++; + /* + * somehow 2.6.24 MCI manages to issue MMC_WRITE_BLOCK *without* the + * MMC_DATA_WRITE flag, WTF? Work around the madness. + */ + if (cmd->opcode == MMC_WRITE_BLOCK) + if (mrq->data) + mrq->data->flags |= MMC_DATA_WRITE; + + /* this guy has data to read/write? */ + if ((!host->cmd_is_stop) && cmd->data) { + int res; + host->dcnt++; + res = glamo_mci_prepare_pio(host, cmd->data); + if (res) { + cmd->error = -EIO; + cmd->data->error = -EIO; + mmc_request_done(mmc, mrq); + return; + } + } + + 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); + + /* resume requested clock rate + * scale it down by sd_slow_ratio if platform requests it + */ + __glamo_mci_fix_card_div(host, host->clk_div); + + if (glamo_mci_send_command(host, cmd)) + goto bail; + + /* we are deselecting card? because it isn't going to ack then... */ + if ((cmd->opcode == 7) && (cmd->arg == 0)) + goto done; + + /* + * we must spin until response is ready or timed out + * -- we don't get interrupts unless there is a bulk rx + */ + do + status = readw(host->base + GLAMO_REG_MMC_RB_STAT1); + while (((((status >> 15) & 1) != (host->ccnt & 1)) || + (!(status & (GLAMO_STAT1_MMC_RB_RRDY | + GLAMO_STAT1_MMC_RTOUT | + GLAMO_STAT1_MMC_DTOUT | + GLAMO_STAT1_MMC_BWERR | + GLAMO_STAT1_MMC_BRERR)))) && (insanity_timeout--)); + + if (insanity_timeout < 0) + dev_info(&host->pdev->dev, "command timeout, continuing\n"); + + 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 (host->cmd_is_stop) + goto bail; + + if (cmd->error) { + dev_info(&host->pdev->dev, "Error after cmd: 0x%x\n", status); + goto done; + } + /* + * mangle the response registers in two different exciting + * undocumented ways discovered by trial and error + */ + if (mmc_resp_type(cmd) == MMC_RSP_R2) + /* grab the response */ + for (n = 0; n < 8; n++) /* super mangle power 1 */ + pu16[n ^ 6] = readw(®_resp[n]); + else + for (n = 0; n < 3; n++) /* super mangle power 2 */ + pu16[n] = (readw(®_resp[n]) >> 8) | + (readw(®_resp[n + 1]) << 8); + /* + * if we don't have bulk data to take care of, we're done + */ + if (!cmd->data) + goto done; + if (!(cmd->data->flags & (MMC_DATA_READ | MMC_DATA_WRITE))) + 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"); + /* + * if the glamo INT# line isn't wired (*cough* it can happen) + * I'm afraid we have to spin on the IRQ status bit and "be + * our own INT# line" + */ + if (!glamo_mci_def_pdata.pglamo->irq_works) { + /* + * we have faith we will get an "interrupt"... + * but something insane like suspend problems can mean + * we spin here forever, so we timeout after a LONG time + */ + while ((!(readw(glamo_mci_def_pdata.pglamo->base + + GLAMO_REG_IRQ_STATUS) & GLAMO_IRQ_MMC)) && + (timeout--)) + ; + + if (timeout < 0) { + if (cmd->data->error) + cmd->data->error = -ETIMEDOUT; + dev_err(&host->pdev->dev, "Payload timeout\n"); + goto bail; + } + + /* yay we are an interrupt controller! -- call the ISR + * it will stop clock to card + */ + glamo_mci_irq_host(host); + } + return; + +done: + host->complete_what = COMPLETION_NONE; + host->mrq = NULL; + mmc_request_done(host->mmc, cmd->mrq); +bail: + if (!sd_idleclk && !host->force_slow_during_powerup) + /* stop the clock to card */ + __glamo_mci_fix_card_div(host, -1); +} + +static void glamo_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct glamo_mci_host *host = mmc_priv(mmc); + + host->cmd_is_stop = 0; + host->mrq = mrq; + glamo_mci_send_request(mmc); +} + +#if 1 +static void glamo_mci_reset(struct glamo_mci_host *host) +{ + if (host->suspending) { + dev_err(&host->pdev->dev, "IGNORING glamo_mci_reset while " + "suspended\n"); + return; + } + dev_dbg(&host->pdev->dev, "******* glamo_mci_reset\n"); + /* reset MMC controller */ + writew(GLAMO_CLOCK_MMC_RESET | GLAMO_CLOCK_MMC_DG_TCLK | + GLAMO_CLOCK_MMC_EN_TCLK | GLAMO_CLOCK_MMC_DG_M9CLK | + GLAMO_CLOCK_MMC_EN_M9CLK, + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_MMC); + udelay(10); + /* and disable reset */ + writew(GLAMO_CLOCK_MMC_DG_TCLK | + GLAMO_CLOCK_MMC_EN_TCLK | GLAMO_CLOCK_MMC_DG_M9CLK | + GLAMO_CLOCK_MMC_EN_M9CLK, + glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_MMC); +} +#endif +static inline int glamo_mci_get_mv(int vdd) +{ + int mv = 1650; + + if (vdd > 7) + mv += 350 + 100 * (vdd - 8); + + return mv; +} + +static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct glamo_mci_host *host = mmc_priv(mmc); + struct regulator *regulator; + int n = 0; + int div; + int powering = 0; + int mv; + + if (host->suspending) { + dev_err(&host->pdev->dev, "IGNORING glamo_mci_set_ios while " + "suspended\n"); + return; + } + + regulator = host->regulator; + + /* Set power */ + switch(ios->power_mode) { + case MMC_POWER_UP: + if (host->pdata->glamo_can_set_mci_power()) { + mv = glamo_mci_get_mv(ios->vdd); + regulator_set_voltage(regulator, mv * 1000, mv * 1000); + regulator_enable(regulator); + } + break; + case MMC_POWER_ON: + /* + * we should use very slow clock until first bulk + * transfer completes OK + */ + host->force_slow_during_powerup = 1; + + if (host->vdd_current != ios->vdd) { + if (host->pdata->glamo_can_set_mci_power()) { + mv = glamo_mci_get_mv(ios->vdd); + regulator_set_voltage(regulator, mv * 1000, mv * 1000); + printk(KERN_INFO "SD power -> %dmV\n", mv); + } + host->vdd_current = ios->vdd; + } + if (host->power_mode_current == MMC_POWER_OFF) { + glamo_engine_enable(glamo_mci_def_pdata.pglamo, + GLAMO_ENGINE_MMC); + powering = 1; + } + break; + + case MMC_POWER_OFF: + default: + if (host->power_mode_current == MMC_POWER_OFF) + break; + /* never want clocking with dead card */ + __glamo_mci_fix_card_div(host, -1); + + glamo_engine_disable(glamo_mci_def_pdata.pglamo, + GLAMO_ENGINE_MMC); + regulator_disable(regulator); + host->vdd_current = -1; + break; + } + host->power_mode_current = ios->power_mode; + + host->real_rate = __glamo_mci_set_card_clock(host, ios->clock, &div); + host->clk_div = div; + + /* after power-up, we are meant to give it >= 74 clocks so it can + * initialize itself. Doubt any modern cards need it but anyway... + */ + if (powering) + mdelay(1); + + if (!sd_idleclk && !host->force_slow_during_powerup) + /* stop the clock to card, because we are idle until transfer */ + __glamo_mci_fix_card_div(host, -1); + + if ((ios->power_mode == MMC_POWER_ON) || + (ios->power_mode == MMC_POWER_UP)) { + dev_info(&host->pdev->dev, + "powered (vdd = %d) clk: %lukHz div=%d (req: %ukHz). " + "Bus width=%d\n",(int)ios->vdd, + host->real_rate / 1000, (int)host->clk_div, + ios->clock / 1000, (int)ios->bus_width); + } else + dev_info(&host->pdev->dev, "glamo_mci_set_ios: power down.\n"); + + /* set bus width */ + host->bus_width = ios->bus_width; + if (host->bus_width == MMC_BUS_WIDTH_4) + n = GLAMO_BASIC_MMC_EN_4BIT_DATA; + writew((readw(host->base + GLAMO_REG_MMC_BASIC) & + (~(GLAMO_BASIC_MMC_EN_4BIT_DATA | + GLAMO_BASIC_MMC_EN_DR_STR0 | + GLAMO_BASIC_MMC_EN_DR_STR1))) | n | + sd_drive << 6, host->base + GLAMO_REG_MMC_BASIC); +} + + +/* + * 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 = { + .request = glamo_mci_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; + 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; + host->pdata = &glamo_mci_def_pdata; + host->power_mode_current = MMC_POWER_OFF; + + host->complete_what = COMPLETION_NONE; + host->pio_active = XFER_NONE; + + spin_lock_init(&host->complete_lock); + INIT_WORK(&host->irq_work, glamo_mci_irq_worker); + + host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!host->mem) { + dev_err(&pdev->dev, + "failed to get io memory region resouce.\n"); + + ret = -ENOENT; + goto probe_free_host; + } + + host->mem = request_mem_region(host->mem->start, + RESSIZE(host->mem), pdev->name); + + if (!host->mem) { + dev_err(&pdev->dev, "failed to request io memory region.\n"); + ret = -ENOENT; + goto probe_free_host; + } + + host->base = ioremap(host->mem->start, RESSIZE(host->mem)); + if (!host->base) { + dev_err(&pdev->dev, "failed to ioremap() io memory region.\n"); + ret = -EINVAL; + goto probe_free_mem_region; + } + + host->regulator = regulator_get(&pdev->dev, "SD_3V3"); + if (!host->regulator) { + dev_err(&pdev->dev, "Cannot proceed without regulator.\n"); + return -ENODEV; + } + + /* set the handler for our bit of the shared chip irq register */ + set_irq_handler(IRQ_GLAMO(GLAMO_IRQIDX_MMC), glamo_mci_irq); + /* stash host as our handler's private data */ + set_irq_data(IRQ_GLAMO(GLAMO_IRQIDX_MMC), host); + + /* Get ahold of our data buffer we use for data in and out on MMC */ + host->mem_data = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!host->mem_data) { + dev_err(&pdev->dev, + "failed to get io memory region resource.\n"); + ret = -ENOENT; + goto probe_iounmap; + } + + host->mem_data = request_mem_region(host->mem_data->start, + RESSIZE(host->mem_data), pdev->name); + + if (!host->mem_data) { + dev_err(&pdev->dev, "failed to request io memory region.\n"); + ret = -ENOENT; + goto probe_iounmap; + } + host->base_data = ioremap(host->mem_data->start, + RESSIZE(host->mem_data)); + host->data_max_size = RESSIZE(host->mem_data); + + if (host->base_data == 0) { + dev_err(&pdev->dev, "failed to ioremap() io memory region.\n"); + ret = -EINVAL; + goto probe_free_mem_region_data; + } + + host->vdd_current = 0; + host->clk_rate = 50000000; /* really it's 49152000 */ + host->clk_div = 16; + + /* explain our host controller capabilities */ + mmc->ops = &glamo_mci_ops; + mmc->ocr_avail = host->pdata->ocr_avail; + mmc->caps = MMC_CAP_4_BIT_DATA | + MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_SD_HIGHSPEED; + 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 = RESSIZE(host->mem_data) / 2; + mmc->max_seg_size = mmc->max_req_size; + mmc->max_phys_segs = 1; /* hw doesn't talk about segs??? */ + mmc->max_hw_segs = 1; + + dev_info(&host->pdev->dev, "probe: mapped mci_base:%p irq:%u.\n", + host->base, host->irq); + + platform_set_drvdata(pdev, mmc); + + glamo_engine_enable(glamo_mci_def_pdata.pglamo, GLAMO_ENGINE_MMC); + glamo_mci_reset(host); + + if ((ret = mmc_add_host(mmc))) { + dev_err(&pdev->dev, "failed to add mmc host.\n"); + goto probe_free_mem_region_data; + } + + dev_info(&pdev->dev,"initialisation done.\n"); + return 0; + + probe_free_mem_region_data: + release_mem_region(host->mem_data->start, RESSIZE(host->mem_data)); + + probe_iounmap: + iounmap(host->base); + + probe_free_mem_region: + release_mem_region(host->mem->start, RESSIZE(host->mem)); + + probe_free_host: + 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); + struct regulator *regulator; + + mmc_remove_host(mmc); + /* stop using our handler, revert it to default */ + set_irq_handler(IRQ_GLAMO(GLAMO_IRQIDX_MMC), handle_level_irq); + iounmap(host->base); + iounmap(host->base_data); + release_mem_region(host->mem->start, RESSIZE(host->mem)); + release_mem_region(host->mem_data->start, RESSIZE(host->mem_data)); + + regulator = host->regulator; + regulator_put(regulator); + + mmc_free_host(mmc); + + glamo_engine_disable(glamo_mci_def_pdata.pglamo, GLAMO_ENGINE_MMC); + return 0; +} + + +#ifdef CONFIG_PM + +static int glamo_mci_suspend(struct platform_device *dev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + struct glamo_mci_host *host = mmc_priv(mmc); + int ret; + + cancel_work_sync(&host->irq_work); + + /* + * possible workaround for SD corruption during suspend - resume + * make sure the clock was running during suspend and consequently + * resume + */ + __glamo_mci_fix_card_div(host, host->clk_div); + + /* we are going to do more commands to override this in + * mmc_suspend_host(), so we need to change sd_idleclk for the + * duration as well + */ + suspend_sd_idleclk = sd_idleclk; + sd_idleclk = 1; + + ret = mmc_suspend_host(mmc, state); + + host->suspending++; + /* so that when we resume, we use any modified max rate */ + mmc->f_max = sd_max_clk; + + return ret; +} + +int glamo_mci_resume(struct platform_device *dev) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + struct glamo_mci_host *host = mmc_priv(mmc); + int ret; + + sd_idleclk = 1; + + glamo_engine_enable(host->pdata->pglamo, GLAMO_ENGINE_MMC); + glamo_mci_reset(host); + + host->suspending--; + + ret = mmc_resume_host(mmc); + + /* put sd_idleclk back to pre-suspend state */ + sd_idleclk = suspend_sd_idleclk; + + return ret; +} +EXPORT_SYMBOL_GPL(glamo_mci_resume); + +#else /* CONFIG_PM */ +#define glamo_mci_suspend NULL +#define glamo_mci_resume NULL +#endif /* CONFIG_PM */ + + +static struct platform_driver glamo_mci_driver = +{ + .driver.name = "glamo-mci", + .probe = glamo_mci_probe, + .remove = glamo_mci_remove, + .suspend = glamo_mci_suspend, + .resume = glamo_mci_resume, +}; + +static int __init glamo_mci_init(void) +{ + spin_lock_init(&clock_lock); + platform_driver_register(&glamo_mci_driver); + return 0; +} + +static void __exit glamo_mci_exit(void) +{ + platform_driver_unregister(&glamo_mci_driver); +} + +module_init(glamo_mci_init); +module_exit(glamo_mci_exit); + +MODULE_DESCRIPTION("Glamo MMC/SD Card Interface driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andy Green <andy@openmoko.com>"); diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-mci.h b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-mci.h new file mode 100644 index 0000000000..daae7a3bcc --- /dev/null +++ b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-mci.h @@ -0,0 +1,84 @@ +/* + * linux/drivers/mmc/host/glamo-mmc.h - GLAMO MCI driver + * + * Copyright (C) 2007-2008 Openmoko, Inc, Andy Green <andy@openmoko.com> + * based on S3C MMC driver --> + * Copyright (C) 2004-2006 Thomas Kleffel, 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 version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/regulator/consumer.h> + +enum glamo_mci_waitfor { + COMPLETION_NONE, + COMPLETION_FINALIZE, + COMPLETION_CMDSENT, + COMPLETION_RSPFIN, + COMPLETION_XFERFINISH, + COMPLETION_XFERFINISH_RSPFIN, +}; + +struct glamo_mci_host { + struct platform_device *pdev; + struct glamo_mci_pdata *pdata; + struct mmc_host *mmc; + struct resource *mem; + struct resource *mem_data; + struct clk *clk; + void __iomem *base; + u16 __iomem *base_data; + int irq; + int irq_cd; + int dma; + int data_max_size; + + int suspending; + + int power_mode_current; + unsigned int vdd_current; + + unsigned long clk_rate; + unsigned long clk_div; + unsigned long real_rate; + u8 prescaler; + + int force_slow_during_powerup; + + unsigned sdiimsk; + int dodma; + + volatile int dmatogo; + + struct mmc_request *mrq; + int cmd_is_stop; + struct work_struct irq_work; + + spinlock_t complete_lock; + volatile enum glamo_mci_waitfor + complete_what; + + volatile int dma_complete; + + volatile u32 pio_sgptr; + volatile u32 pio_words; + volatile u32 pio_count; + volatile u16 *pio_ptr; +#define XFER_NONE 0 +#define XFER_READ 1 +#define XFER_WRITE 2 + volatile u32 pio_active; + + int bus_width; + + char dbgmsg_cmd[301]; + char dbgmsg_dat[301]; + volatile char *status; + + unsigned int ccnt, dcnt; + struct tasklet_struct pio_tasklet; + + struct regulator *regulator; +}; diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-regs.h b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-regs.h new file mode 100644 index 0000000000..2328b8ac98 --- /dev/null +++ b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-regs.h @@ -0,0 +1,632 @@ +#ifndef _GLAMO_REGS_H +#define _GLAMO_REGS_H + +/* Smedia Glamo 336x/337x driver + * + * (C) 2007 by Openmoko, Inc. + * Author: Harald Welte <laforge@openmoko.org> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +enum glamo_regster_offsets { + GLAMO_REGOFS_GENERIC = 0x0000, + GLAMO_REGOFS_HOSTBUS = 0x0200, + GLAMO_REGOFS_MEMORY = 0x0300, + GLAMO_REGOFS_VIDCAP = 0x0400, + GLAMO_REGOFS_ISP = 0x0500, + GLAMO_REGOFS_JPEG = 0x0800, + GLAMO_REGOFS_MPEG = 0x0c00, + GLAMO_REGOFS_LCD = 0x1100, + GLAMO_REGOFS_MMC = 0x1400, + GLAMO_REGOFS_MPROC0 = 0x1500, + GLAMO_REGOFS_MPROC1 = 0x1580, + GLAMO_REGOFS_CMDQUEUE = 0x1600, + GLAMO_REGOFS_RISC = 0x1680, + GLAMO_REGOFS_2D = 0x1700, + GLAMO_REGOFS_3D = 0x1b00, + GLAMO_REGOFS_END = 0x2400, +}; + + +enum glamo_register_generic { + GLAMO_REG_GCONF1 = 0x0000, + GLAMO_REG_GCONF2 = 0x0002, +#define GLAMO_REG_DEVICE_ID GLAMO_REG_GCONF2 + GLAMO_REG_GCONF3 = 0x0004, +#define GLAMO_REG_REVISION_ID GLAMO_REG_GCONF3 + GLAMO_REG_IRQ_GEN1 = 0x0006, +#define GLAMO_REG_IRQ_ENABLE GLAMO_REG_IRQ_GEN1 + GLAMO_REG_IRQ_GEN2 = 0x0008, +#define GLAMO_REG_IRQ_SET GLAMO_REG_IRQ_GEN2 + GLAMO_REG_IRQ_GEN3 = 0x000a, +#define GLAMO_REG_IRQ_CLEAR GLAMO_REG_IRQ_GEN3 + GLAMO_REG_IRQ_GEN4 = 0x000c, +#define GLAMO_REG_IRQ_STATUS GLAMO_REG_IRQ_GEN4 + GLAMO_REG_CLOCK_HOST = 0x0010, + GLAMO_REG_CLOCK_MEMORY = 0x0012, + GLAMO_REG_CLOCK_LCD = 0x0014, + GLAMO_REG_CLOCK_MMC = 0x0016, + GLAMO_REG_CLOCK_ISP = 0x0018, + GLAMO_REG_CLOCK_JPEG = 0x001a, + GLAMO_REG_CLOCK_3D = 0x001c, + GLAMO_REG_CLOCK_2D = 0x001e, + GLAMO_REG_CLOCK_RISC1 = 0x0020, /* 3365 only? */ + GLAMO_REG_CLOCK_RISC2 = 0x0022, /* 3365 only? */ + GLAMO_REG_CLOCK_MPEG = 0x0024, + GLAMO_REG_CLOCK_MPROC = 0x0026, + + GLAMO_REG_CLOCK_GEN5_1 = 0x0030, + GLAMO_REG_CLOCK_GEN5_2 = 0x0032, + GLAMO_REG_CLOCK_GEN6 = 0x0034, + GLAMO_REG_CLOCK_GEN7 = 0x0036, + GLAMO_REG_CLOCK_GEN8 = 0x0038, + GLAMO_REG_CLOCK_GEN9 = 0x003a, + GLAMO_REG_CLOCK_GEN10 = 0x003c, + GLAMO_REG_CLOCK_GEN11 = 0x003e, + GLAMO_REG_PLL_GEN1 = 0x0040, + GLAMO_REG_PLL_GEN2 = 0x0042, + GLAMO_REG_PLL_GEN3 = 0x0044, + GLAMO_REG_PLL_GEN4 = 0x0046, + GLAMO_REG_PLL_GEN5 = 0x0048, + GLAMO_REG_GPIO_GEN1 = 0x0050, + GLAMO_REG_GPIO_GEN2 = 0x0052, + GLAMO_REG_GPIO_GEN3 = 0x0054, + GLAMO_REG_GPIO_GEN4 = 0x0056, + GLAMO_REG_GPIO_GEN5 = 0x0058, + GLAMO_REG_GPIO_GEN6 = 0x005a, + GLAMO_REG_GPIO_GEN7 = 0x005c, + GLAMO_REG_GPIO_GEN8 = 0x005e, + GLAMO_REG_GPIO_GEN9 = 0x0060, + GLAMO_REG_GPIO_GEN10 = 0x0062, + GLAMO_REG_DFT_GEN1 = 0x0070, + GLAMO_REG_DFT_GEN2 = 0x0072, + GLAMO_REG_DFT_GEN3 = 0x0074, + GLAMO_REG_DFT_GEN4 = 0x0076, + + GLAMO_REG_DFT_GEN5 = 0x01e0, + GLAMO_REG_DFT_GEN6 = 0x01f0, +}; + +#define GLAMO_REG_HOSTBUS(x) (GLAMO_REGOFS_HOSTBUS-2+(x*2)) + +#define REG_MEM(x) (GLAMO_REGOFS_MEMORY+(x)) +#define GLAMO_REG_MEM_TIMING(x) (GLAMO_REG_MEM_TIMING1-2+(x*2)) + +enum glamo_register_mem { + GLAMO_REG_MEM_TYPE = REG_MEM(0x00), + GLAMO_REG_MEM_GEN = REG_MEM(0x02), + GLAMO_REG_MEM_TIMING1 = REG_MEM(0x04), + GLAMO_REG_MEM_TIMING2 = REG_MEM(0x06), + GLAMO_REG_MEM_TIMING3 = REG_MEM(0x08), + GLAMO_REG_MEM_TIMING4 = REG_MEM(0x0a), + GLAMO_REG_MEM_TIMING5 = REG_MEM(0x0c), + GLAMO_REG_MEM_TIMING6 = REG_MEM(0x0e), + GLAMO_REG_MEM_TIMING7 = REG_MEM(0x10), + GLAMO_REG_MEM_TIMING8 = REG_MEM(0x12), + GLAMO_REG_MEM_TIMING9 = REG_MEM(0x14), + GLAMO_REG_MEM_TIMING10 = REG_MEM(0x16), + GLAMO_REG_MEM_TIMING11 = REG_MEM(0x18), + GLAMO_REG_MEM_POWER1 = REG_MEM(0x1a), + GLAMO_REG_MEM_POWER2 = REG_MEM(0x1c), + GLAMO_REG_MEM_LCD_BUF1 = REG_MEM(0x1e), + GLAMO_REG_MEM_LCD_BUF2 = REG_MEM(0x20), + GLAMO_REG_MEM_LCD_BUF3 = REG_MEM(0x22), + GLAMO_REG_MEM_LCD_BUF4 = REG_MEM(0x24), + GLAMO_REG_MEM_BIST1 = REG_MEM(0x26), + GLAMO_REG_MEM_BIST2 = REG_MEM(0x28), + GLAMO_REG_MEM_BIST3 = REG_MEM(0x2a), + GLAMO_REG_MEM_BIST4 = REG_MEM(0x2c), + GLAMO_REG_MEM_BIST5 = REG_MEM(0x2e), + GLAMO_REG_MEM_MAH1 = REG_MEM(0x30), + GLAMO_REG_MEM_MAH2 = REG_MEM(0x32), + GLAMO_REG_MEM_DRAM1 = REG_MEM(0x34), + GLAMO_REG_MEM_DRAM2 = REG_MEM(0x36), + GLAMO_REG_MEM_CRC = REG_MEM(0x38), +}; + +#define GLAMO_MEM_TYPE_MASK 0x03 + +enum glamo_reg_mem_dram1 { + /* b0 - b10 == refresh period, 1 -> 2048 clocks */ + GLAMO_MEM_DRAM1_EN_GATE_CLK = (1 << 11), + GLAMO_MEM_DRAM1_SELF_REFRESH = (1 << 12), + GLAMO_MEM_DRAM1_EN_GATE_CKE = (1 << 13), + GLAMO_MEM_DRAM1_EN_DRAM_REFRESH = (1 << 14), + GLAMO_MEM_DRAM1_EN_MODEREG_SET = (1 << 15), +}; + +enum glamo_reg_mem_dram2 { + GLAMO_MEM_DRAM2_DEEP_PWRDOWN = (1 << 12), +}; + +enum glamo_irq_index { + GLAMO_IRQIDX_HOSTBUS = 0, + GLAMO_IRQIDX_JPEG = 1, + GLAMO_IRQIDX_MPEG = 2, + GLAMO_IRQIDX_MPROC1 = 3, + GLAMO_IRQIDX_MPROC0 = 4, + GLAMO_IRQIDX_CMDQUEUE = 5, + GLAMO_IRQIDX_2D = 6, + GLAMO_IRQIDX_MMC = 7, + GLAMO_IRQIDX_RISC = 8, +}; + +enum glamo_irq { + GLAMO_IRQ_HOSTBUS = (1 << GLAMO_IRQIDX_HOSTBUS), + GLAMO_IRQ_JPEG = (1 << GLAMO_IRQIDX_JPEG), + GLAMO_IRQ_MPEG = (1 << GLAMO_IRQIDX_MPEG), + GLAMO_IRQ_MPROC1 = (1 << GLAMO_IRQIDX_MPROC1), + GLAMO_IRQ_MPROC0 = (1 << GLAMO_IRQIDX_MPROC0), + GLAMO_IRQ_CMDQUEUE = (1 << GLAMO_IRQIDX_CMDQUEUE), + GLAMO_IRQ_2D = (1 << GLAMO_IRQIDX_2D), + GLAMO_IRQ_MMC = (1 << GLAMO_IRQIDX_MMC), + GLAMO_IRQ_RISC = (1 << GLAMO_IRQIDX_RISC), +}; + +enum glamo_reg_clock_host { + GLAMO_CLOCK_HOST_DG_BCLK = 0x0001, + GLAMO_CLOCK_HOST_DG_M0CLK = 0x0004, + GLAMO_CLOCK_HOST_RESET = 0x1000, +}; + +enum glamo_reg_clock_mem { + GLAMO_CLOCK_MEM_DG_M1CLK = 0x0001, + GLAMO_CLOCK_MEM_EN_M1CLK = 0x0002, + GLAMO_CLOCK_MEM_DG_MOCACLK = 0x0004, + GLAMO_CLOCK_MEM_EN_MOCACLK = 0x0008, + GLAMO_CLOCK_MEM_RESET = 0x1000, + GLAMO_CLOCK_MOCA_RESET = 0x2000, +}; + +enum glamo_reg_clock_lcd { + GLAMO_CLOCK_LCD_DG_DCLK = 0x0001, + GLAMO_CLOCK_LCD_EN_DCLK = 0x0002, + GLAMO_CLOCK_LCD_DG_DMCLK = 0x0004, + GLAMO_CLOCK_LCD_EN_DMCLK = 0x0008, + // + GLAMO_CLOCK_LCD_EN_DHCLK = 0x0020, + GLAMO_CLOCK_LCD_DG_M5CLK = 0x0040, + GLAMO_CLOCK_LCD_EN_M5CLK = 0x0080, + GLAMO_CLOCK_LCD_RESET = 0x1000, +}; + +enum glamo_reg_clock_mmc { + GLAMO_CLOCK_MMC_DG_TCLK = 0x0001, + GLAMO_CLOCK_MMC_EN_TCLK = 0x0002, + GLAMO_CLOCK_MMC_DG_M9CLK = 0x0004, + GLAMO_CLOCK_MMC_EN_M9CLK = 0x0008, + GLAMO_CLOCK_MMC_RESET = 0x1000, +}; + +enum glamo_reg_basic_mmc { + /* set to disable CRC error rejection */ + GLAMO_BASIC_MMC_DISABLE_CRC = 0x0001, + /* enable completion interrupt */ + GLAMO_BASIC_MMC_EN_COMPL_INT = 0x0002, + /* stop MMC clock while enforced idle waiting for data from card */ + GLAMO_BASIC_MMC_NO_CLK_RD_WAIT = 0x0004, + /* 0 = 1-bit bus to card, 1 = use 4-bit bus (has to be negotiated) */ + GLAMO_BASIC_MMC_EN_4BIT_DATA = 0x0008, + /* enable 75K pullups on D3..D0 */ + GLAMO_BASIC_MMC_EN_DATA_PUPS = 0x0010, + /* enable 75K pullup on CMD */ + GLAMO_BASIC_MMC_EN_CMD_PUP = 0x0020, + /* IO drive strength 00=weak -> 11=strongest */ + GLAMO_BASIC_MMC_EN_DR_STR0 = 0x0040, + GLAMO_BASIC_MMC_EN_DR_STR1 = 0x0080, + /* TCLK delay stage A, 0000 = 500ps --> 1111 = 8ns */ + GLAMO_BASIC_MMC_EN_TCLK_DLYA0 = 0x0100, + GLAMO_BASIC_MMC_EN_TCLK_DLYA1 = 0x0200, + GLAMO_BASIC_MMC_EN_TCLK_DLYA2 = 0x0400, + GLAMO_BASIC_MMC_EN_TCLK_DLYA3 = 0x0800, + /* TCLK delay stage B (cumulative), 0000 = 500ps --> 1111 = 8ns */ + GLAMO_BASIC_MMC_EN_TCLK_DLYB0 = 0x1000, + GLAMO_BASIC_MMC_EN_TCLK_DLYB1 = 0x2000, + GLAMO_BASIC_MMC_EN_TCLK_DLYB2 = 0x4000, + GLAMO_BASIC_MMC_EN_TCLK_DLYB3 = 0x8000, +}; + +enum glamo_reg_stat1_mmc { + /* command "counter" (really: toggle) */ + GLAMO_STAT1_MMC_CMD_CTR = 0x8000, + /* engine is idle */ + GLAMO_STAT1_MMC_IDLE = 0x4000, + /* readback response is ready */ + GLAMO_STAT1_MMC_RB_RRDY = 0x0200, + /* readback data is ready */ + GLAMO_STAT1_MMC_RB_DRDY = 0x0100, + /* no response timeout */ + GLAMO_STAT1_MMC_RTOUT = 0x0020, + /* no data timeout */ + GLAMO_STAT1_MMC_DTOUT = 0x0010, + /* CRC error on block write */ + GLAMO_STAT1_MMC_BWERR = 0x0004, + /* CRC error on block read */ + GLAMO_STAT1_MMC_BRERR = 0x0002 +}; + +enum glamo_reg_fire_mmc { + /* command "counter" (really: toggle) + * the STAT1 register reflects this so you can ensure you don't look + * at status for previous command + */ + GLAMO_FIRE_MMC_CMD_CTR = 0x8000, + /* sets kind of response expected */ + GLAMO_FIRE_MMC_RES_MASK = 0x0700, + /* sets command type */ + GLAMO_FIRE_MMC_TYP_MASK = 0x00C0, + /* sets command class */ + GLAMO_FIRE_MMC_CLS_MASK = 0x000F, +}; + +enum glamo_fire_mmc_response_types { + GLAMO_FIRE_MMC_RSPT_R1 = 0x0000, + GLAMO_FIRE_MMC_RSPT_R1b = 0x0100, + GLAMO_FIRE_MMC_RSPT_R2 = 0x0200, + GLAMO_FIRE_MMC_RSPT_R3 = 0x0300, + GLAMO_FIRE_MMC_RSPT_R4 = 0x0400, + GLAMO_FIRE_MMC_RSPT_R5 = 0x0500, +}; + +enum glamo_fire_mmc_command_types { + /* broadcast, no response */ + GLAMO_FIRE_MMC_CMDT_BNR = 0x0000, + /* broadcast, with response */ + GLAMO_FIRE_MMC_CMDT_BR = 0x0040, + /* addressed, no data */ + GLAMO_FIRE_MMC_CMDT_AND = 0x0080, + /* addressed, with data */ + GLAMO_FIRE_MMC_CMDT_AD = 0x00C0, +}; + +enum glamo_fire_mmc_command_class { + /* "Stream Read" */ + GLAMO_FIRE_MMC_CC_STRR = 0x0000, + /* Single Block Read */ + GLAMO_FIRE_MMC_CC_SBR = 0x0001, + /* Multiple Block Read With Stop */ + GLAMO_FIRE_MMC_CC_MBRS = 0x0002, + /* Multiple Block Read No Stop */ + GLAMO_FIRE_MMC_CC_MBRNS = 0x0003, + /* RESERVED for "Stream Write" */ + GLAMO_FIRE_MMC_CC_STRW = 0x0004, + /* "Stream Write" */ + GLAMO_FIRE_MMC_CC_SBW = 0x0005, + /* RESERVED for Multiple Block Write With Stop */ + GLAMO_FIRE_MMC_CC_MBWS = 0x0006, + /* Multiple Block Write No Stop */ + GLAMO_FIRE_MMC_CC_MBWNS = 0x0007, + /* STOP command */ + GLAMO_FIRE_MMC_CC_STOP = 0x0008, + /* Cancel on Running Command */ + GLAMO_FIRE_MMC_CC_CANCL = 0x0009, + /* "Basic Command" */ + GLAMO_FIRE_MMC_CC_BASIC = 0x000a, +}; + +/* these are offsets from the start of the MMC register region */ +enum glamo_register_mmc { + /* MMC command, b15..8 = cmd arg b7..0; b7..1 = CRC; b0 = end bit */ + GLAMO_REG_MMC_CMD_REG1 = 0x00, + /* MMC command, b15..0 = cmd arg b23 .. 8 */ + GLAMO_REG_MMC_CMD_REG2 = 0x02, + /* MMC command, b15=start, b14=transmission, + * b13..8=cmd idx, b7..0=cmd arg b31..24 + */ + GLAMO_REG_MMC_CMD_REG3 = 0x04, + GLAMO_REG_MMC_CMD_FIRE = 0x06, + GLAMO_REG_MMC_CMD_RSP1 = 0x10, + GLAMO_REG_MMC_CMD_RSP2 = 0x12, + GLAMO_REG_MMC_CMD_RSP3 = 0x14, + GLAMO_REG_MMC_CMD_RSP4 = 0x16, + GLAMO_REG_MMC_CMD_RSP5 = 0x18, + GLAMO_REG_MMC_CMD_RSP6 = 0x1a, + GLAMO_REG_MMC_CMD_RSP7 = 0x1c, + GLAMO_REG_MMC_CMD_RSP8 = 0x1e, + GLAMO_REG_MMC_RB_STAT1 = 0x20, + GLAMO_REG_MMC_RB_BLKCNT = 0x22, + GLAMO_REG_MMC_RB_BLKLEN = 0x24, + GLAMO_REG_MMC_BASIC = 0x30, + GLAMO_REG_MMC_RDATADS1 = 0x34, + GLAMO_REG_MMC_RDATADS2 = 0x36, + GLAMO_REG_MMC_WDATADS1 = 0x38, + GLAMO_REG_MMC_WDATADS2 = 0x3a, + GLAMO_REG_MMC_DATBLKCNT = 0x3c, + GLAMO_REG_MMC_DATBLKLEN = 0x3e, + GLAMO_REG_MMC_TIMEOUT = 0x40, + +}; + +enum glamo_reg_clock_isp { + GLAMO_CLOCK_ISP_DG_I1CLK = 0x0001, + GLAMO_CLOCK_ISP_EN_I1CLK = 0x0002, + GLAMO_CLOCK_ISP_DG_CCLK = 0x0004, + GLAMO_CLOCK_ISP_EN_CCLK = 0x0008, + // + GLAMO_CLOCK_ISP_EN_SCLK = 0x0020, + GLAMO_CLOCK_ISP_DG_M2CLK = 0x0040, + GLAMO_CLOCK_ISP_EN_M2CLK = 0x0080, + GLAMO_CLOCK_ISP_DG_M15CLK = 0x0100, + GLAMO_CLOCK_ISP_EN_M15CLK = 0x0200, + GLAMO_CLOCK_ISP1_RESET = 0x1000, + GLAMO_CLOCK_ISP2_RESET = 0x2000, +}; + +enum glamo_reg_clock_jpeg { + GLAMO_CLOCK_JPEG_DG_JCLK = 0x0001, + GLAMO_CLOCK_JPEG_EN_JCLK = 0x0002, + GLAMO_CLOCK_JPEG_DG_M3CLK = 0x0004, + GLAMO_CLOCK_JPEG_EN_M3CLK = 0x0008, + GLAMO_CLOCK_JPEG_RESET = 0x1000, +}; + +enum glamo_reg_clock_2d { + GLAMO_CLOCK_2D_DG_GCLK = 0x0001, + GLAMO_CLOCK_2D_EN_GCLK = 0x0002, + GLAMO_CLOCK_2D_DG_M7CLK = 0x0004, + GLAMO_CLOCK_2D_EN_M7CLK = 0x0008, + GLAMO_CLOCK_2D_DG_M6CLK = 0x0010, + GLAMO_CLOCK_2D_EN_M6CLK = 0x0020, + GLAMO_CLOCK_2D_RESET = 0x1000, + GLAMO_CLOCK_2D_CQ_RESET = 0x2000, +}; + +enum glamo_reg_clock_3d { + GLAMO_CLOCK_3D_DG_ECLK = 0x0001, + GLAMO_CLOCK_3D_EN_ECLK = 0x0002, + GLAMO_CLOCK_3D_DG_RCLK = 0x0004, + GLAMO_CLOCK_3D_EN_RCLK = 0x0008, + GLAMO_CLOCK_3D_DG_M8CLK = 0x0010, + GLAMO_CLOCK_3D_EN_M8CLK = 0x0020, + GLAMO_CLOCK_3D_BACK_RESET = 0x1000, + GLAMO_CLOCK_3D_FRONT_RESET = 0x2000, +}; + +enum glamo_reg_clock_mpeg { + GLAMO_CLOCK_MPEG_DG_X0CLK = 0x0001, + GLAMO_CLOCK_MPEG_EN_X0CLK = 0x0002, + GLAMO_CLOCK_MPEG_DG_X1CLK = 0x0004, + GLAMO_CLOCK_MPEG_EN_X1CLK = 0x0008, + GLAMO_CLOCK_MPEG_DG_X2CLK = 0x0010, + GLAMO_CLOCK_MPEG_EN_X2CLK = 0x0020, + GLAMO_CLOCK_MPEG_DG_X3CLK = 0x0040, + GLAMO_CLOCK_MPEG_EN_X3CLK = 0x0080, + GLAMO_CLOCK_MPEG_DG_X4CLK = 0x0100, + GLAMO_CLOCK_MPEG_EN_X4CLK = 0x0200, + GLAMO_CLOCK_MPEG_DG_X6CLK = 0x0400, + GLAMO_CLOCK_MPEG_EN_X6CLK = 0x0800, + GLAMO_CLOCK_MPEG_ENC_RESET = 0x1000, + GLAMO_CLOCK_MPEG_DEC_RESET = 0x2000, +}; + +enum glamo_reg_clock51 { + GLAMO_CLOCK_GEN51_EN_DIV_MCLK = 0x0001, + GLAMO_CLOCK_GEN51_EN_DIV_SCLK = 0x0002, + GLAMO_CLOCK_GEN51_EN_DIV_JCLK = 0x0004, + GLAMO_CLOCK_GEN51_EN_DIV_DCLK = 0x0008, + GLAMO_CLOCK_GEN51_EN_DIV_DMCLK = 0x0010, + GLAMO_CLOCK_GEN51_EN_DIV_DHCLK = 0x0020, + GLAMO_CLOCK_GEN51_EN_DIV_GCLK = 0x0040, + GLAMO_CLOCK_GEN51_EN_DIV_TCLK = 0x0080, + /* FIXME: higher bits */ +}; + +enum glamo_reg_hostbus2 { + GLAMO_HOSTBUS2_MMIO_EN_ISP = 0x0001, + GLAMO_HOSTBUS2_MMIO_EN_JPEG = 0x0002, + GLAMO_HOSTBUS2_MMIO_EN_MPEG = 0x0004, + GLAMO_HOSTBUS2_MMIO_EN_LCD = 0x0008, + GLAMO_HOSTBUS2_MMIO_EN_MMC = 0x0010, + GLAMO_HOSTBUS2_MMIO_EN_MICROP0 = 0x0020, + GLAMO_HOSTBUS2_MMIO_EN_MICROP1 = 0x0040, + GLAMO_HOSTBUS2_MMIO_EN_CQ = 0x0080, + GLAMO_HOSTBUS2_MMIO_EN_RISC = 0x0100, + GLAMO_HOSTBUS2_MMIO_EN_2D = 0x0200, + GLAMO_HOSTBUS2_MMIO_EN_3D = 0x0400, +}; + +/* LCD Controller */ + +#define REG_LCD(x) (x) +enum glamo_reg_lcd { + GLAMO_REG_LCD_MODE1 = REG_LCD(0x00), + GLAMO_REG_LCD_MODE2 = REG_LCD(0x02), + GLAMO_REG_LCD_MODE3 = REG_LCD(0x04), + GLAMO_REG_LCD_WIDTH = REG_LCD(0x06), + GLAMO_REG_LCD_HEIGHT = REG_LCD(0x08), + GLAMO_REG_LCD_POLARITY = REG_LCD(0x0a), + GLAMO_REG_LCD_A_BASE1 = REG_LCD(0x0c), + GLAMO_REG_LCD_A_BASE2 = REG_LCD(0x0e), + GLAMO_REG_LCD_B_BASE1 = REG_LCD(0x10), + GLAMO_REG_LCD_B_BASE2 = REG_LCD(0x12), + GLAMO_REG_LCD_C_BASE1 = REG_LCD(0x14), + GLAMO_REG_LCD_C_BASE2 = REG_LCD(0x16), + GLAMO_REG_LCD_PITCH = REG_LCD(0x18), + /* RES */ + GLAMO_REG_LCD_HORIZ_TOTAL = REG_LCD(0x1c), + /* RES */ + GLAMO_REG_LCD_HORIZ_RETR_START = REG_LCD(0x20), + /* RES */ + GLAMO_REG_LCD_HORIZ_RETR_END = REG_LCD(0x24), + /* RES */ + GLAMO_REG_LCD_HORIZ_DISP_START = REG_LCD(0x28), + /* RES */ + GLAMO_REG_LCD_HORIZ_DISP_END = REG_LCD(0x2c), + /* RES */ + GLAMO_REG_LCD_VERT_TOTAL = REG_LCD(0x30), + /* RES */ + GLAMO_REG_LCD_VERT_RETR_START = REG_LCD(0x34), + /* RES */ + GLAMO_REG_LCD_VERT_RETR_END = REG_LCD(0x38), + /* RES */ + GLAMO_REG_LCD_VERT_DISP_START = REG_LCD(0x3c), + /* RES */ + GLAMO_REG_LCD_VERT_DISP_END = REG_LCD(0x40), + /* RES */ + GLAMO_REG_LCD_POL = REG_LCD(0x44), + GLAMO_REG_LCD_DATA_START = REG_LCD(0x46), + GLAMO_REG_LCD_FRATE_CONTRO = REG_LCD(0x48), + GLAMO_REG_LCD_DATA_CMD_HDR = REG_LCD(0x4a), + GLAMO_REG_LCD_SP_START = REG_LCD(0x4c), + GLAMO_REG_LCD_SP_END = REG_LCD(0x4e), + GLAMO_REG_LCD_CURSOR_BASE1 = REG_LCD(0x50), + GLAMO_REG_LCD_CURSOR_BASE2 = REG_LCD(0x52), + GLAMO_REG_LCD_CURSOR_PITCH = REG_LCD(0x54), + GLAMO_REG_LCD_CURSOR_X_SIZE = REG_LCD(0x56), + GLAMO_REG_LCD_CURSOR_Y_SIZE = REG_LCD(0x58), + GLAMO_REG_LCD_CURSOR_X_POS = REG_LCD(0x5a), + GLAMO_REG_LCD_CURSOR_Y_POS = REG_LCD(0x5c), + GLAMO_REG_LCD_CURSOR_PRESET = REG_LCD(0x5e), + GLAMO_REG_LCD_CURSOR_FG_COLOR = REG_LCD(0x60), + /* RES */ + GLAMO_REG_LCD_CURSOR_BG_COLOR = REG_LCD(0x64), + /* RES */ + GLAMO_REG_LCD_CURSOR_DST_COLOR = REG_LCD(0x68), + /* RES */ + GLAMO_REG_LCD_STATUS1 = REG_LCD(0x80), + GLAMO_REG_LCD_STATUS2 = REG_LCD(0x82), + GLAMO_REG_LCD_STATUS3 = REG_LCD(0x84), + GLAMO_REG_LCD_STATUS4 = REG_LCD(0x86), + /* RES */ + GLAMO_REG_LCD_COMMAND1 = REG_LCD(0xa0), + GLAMO_REG_LCD_COMMAND2 = REG_LCD(0xa2), + /* RES */ + GLAMO_REG_LCD_WFORM_DELAY1 = REG_LCD(0xb0), + GLAMO_REG_LCD_WFORM_DELAY2 = REG_LCD(0xb2), + /* RES */ + GLAMO_REG_LCD_GAMMA_CORR = REG_LCD(0x100), + /* RES */ + GLAMO_REG_LCD_GAMMA_R_ENTRY01 = REG_LCD(0x110), + GLAMO_REG_LCD_GAMMA_R_ENTRY23 = REG_LCD(0x112), + GLAMO_REG_LCD_GAMMA_R_ENTRY45 = REG_LCD(0x114), + GLAMO_REG_LCD_GAMMA_R_ENTRY67 = REG_LCD(0x116), + GLAMO_REG_LCD_GAMMA_R_ENTRY8 = REG_LCD(0x118), + /* RES */ + GLAMO_REG_LCD_GAMMA_G_ENTRY01 = REG_LCD(0x130), + GLAMO_REG_LCD_GAMMA_G_ENTRY23 = REG_LCD(0x132), + GLAMO_REG_LCD_GAMMA_G_ENTRY45 = REG_LCD(0x134), + GLAMO_REG_LCD_GAMMA_G_ENTRY67 = REG_LCD(0x136), + GLAMO_REG_LCD_GAMMA_G_ENTRY8 = REG_LCD(0x138), + /* RES */ + GLAMO_REG_LCD_GAMMA_B_ENTRY01 = REG_LCD(0x150), + GLAMO_REG_LCD_GAMMA_B_ENTRY23 = REG_LCD(0x152), + GLAMO_REG_LCD_GAMMA_B_ENTRY45 = REG_LCD(0x154), + GLAMO_REG_LCD_GAMMA_B_ENTRY67 = REG_LCD(0x156), + GLAMO_REG_LCD_GAMMA_B_ENTRY8 = REG_LCD(0x158), + /* RES */ + GLAMO_REG_LCD_SRAM_DRIVING1 = REG_LCD(0x160), + GLAMO_REG_LCD_SRAM_DRIVING2 = REG_LCD(0x162), + GLAMO_REG_LCD_SRAM_DRIVING3 = REG_LCD(0x164), +}; + +enum glamo_reg_lcd_mode1 { + GLAMO_LCD_MODE1_PWRSAVE = 0x0001, + GLAMO_LCD_MODE1_PARTIAL_PRT = 0x0002, + GLAMO_LCD_MODE1_HWFLIP = 0x0004, + GLAMO_LCD_MODE1_LCD2 = 0x0008, + /* RES */ + GLAMO_LCD_MODE1_PARTIAL_MODE = 0x0020, + GLAMO_LCD_MODE1_CURSOR_DSTCOLOR = 0x0040, + GLAMO_LCD_MODE1_PARTIAL_ENABLE = 0x0080, + GLAMO_LCD_MODE1_TVCLK_IN_ENABLE = 0x0100, + GLAMO_LCD_MODE1_HSYNC_HIGH_ACT = 0x0200, + GLAMO_LCD_MODE1_VSYNC_HIGH_ACT = 0x0400, + GLAMO_LCD_MODE1_HSYNC_FLIP = 0x0800, + GLAMO_LCD_MODE1_GAMMA_COR_EN = 0x1000, + GLAMO_LCD_MODE1_DITHER_EN = 0x2000, + GLAMO_LCD_MODE1_CURSOR_EN = 0x4000, + GLAMO_LCD_MODE1_ROTATE_EN = 0x8000, +}; + +enum glamo_reg_lcd_mode2 { + GLAMO_LCD_MODE2_CRC_CHECK_EN = 0x0001, + GLAMO_LCD_MODE2_DCMD_PER_LINE = 0x0002, + GLAMO_LCD_MODE2_NOUSE_BDEF = 0x0004, + GLAMO_LCD_MODE2_OUT_POS_MODE = 0x0008, + GLAMO_LCD_MODE2_FRATE_CTRL_EN = 0x0010, + GLAMO_LCD_MODE2_SINGLE_BUFFER = 0x0020, + GLAMO_LCD_MODE2_SER_LSB_TO_MSB = 0x0040, + /* FIXME */ +}; + +enum glamo_reg_lcd_mode3 { + /* LCD color source data format */ + GLAMO_LCD_SRC_RGB565 = 0x0000, + GLAMO_LCD_SRC_ARGB1555 = 0x4000, + GLAMO_LCD_SRC_ARGB4444 = 0x8000, + /* interface type */ + GLAMO_LCD_MODE3_LCD = 0x1000, + GLAMO_LCD_MODE3_RGB = 0x0800, + GLAMO_LCD_MODE3_CPU = 0x0000, + /* mode */ + GLAMO_LCD_MODE3_RGB332 = 0x0000, + GLAMO_LCD_MODE3_RGB444 = 0x0100, + GLAMO_LCD_MODE3_RGB565 = 0x0200, + GLAMO_LCD_MODE3_RGB666 = 0x0300, + /* depth */ + GLAMO_LCD_MODE3_6BITS = 0x0000, + GLAMO_LCD_MODE3_8BITS = 0x0010, + GLAMO_LCD_MODE3_9BITS = 0x0020, + GLAMO_LCD_MODE3_16BITS = 0x0030, + GLAMO_LCD_MODE3_18BITS = 0x0040, +}; + +enum glamo_lcd_rot_mode { + GLAMO_LCD_ROT_MODE_0 = 0x0000, + GLAMO_LCD_ROT_MODE_180 = 0x2000, + GLAMO_LCD_ROT_MODE_MIRROR = 0x4000, + GLAMO_LCD_ROT_MODE_FLIP = 0x6000, + GLAMO_LCD_ROT_MODE_90 = 0x8000, + GLAMO_LCD_ROT_MODE_270 = 0xa000, +}; +#define GLAMO_LCD_ROT_MODE_MASK 0xe000 + +enum glamo_lcd_cmd_type { + GLAMO_LCD_CMD_TYPE_DISP = 0x0000, + GLAMO_LCD_CMD_TYPE_PARALLEL = 0x4000, + GLAMO_LCD_CMD_TYPE_SERIAL = 0x8000, + GLAMO_LCD_CMD_TYPE_SERIAL_DIRECT= 0xc000, +}; +#define GLAMO_LCD_CMD_TYPE_MASK 0xc000 + +enum glamo_lcd_cmds { + GLAMO_LCD_CMD_DATA_DISP_FIRE = 0x00, + GLAMO_LCD_CMD_DATA_DISP_SYNC = 0x01, /* RGB only */ + /* switch to command mode, no display */ + GLAMO_LCD_CMD_DATA_FIRE_NO_DISP = 0x02, + /* display until VSYNC, switch to command */ + GLAMO_LCD_CMD_DATA_FIRE_VSYNC = 0x11, + /* display until HSYNC, switch to command */ + GLAMO_LCD_CMD_DATA_FIRE_HSYNC = 0x12, + /* display until VSYNC, 1 black frame, VSYNC, switch to command */ + GLAMO_LCD_CMD_DATA_FIRE_VSYNC_B = 0x13, + /* don't care about display and switch to command */ + GLAMO_LCD_CMD_DATA_FIRE_FREE = 0x14, /* RGB only */ + /* don't care about display, keep data display but disable data, + * and switch to command */ + GLAMO_LCD_CMD_DATA_FIRE_FREE_D = 0x15, /* RGB only */ +}; + +enum glamo_core_revisions { + GLAMO_CORE_REV_A0 = 0x0000, + GLAMO_CORE_REV_A1 = 0x0001, + GLAMO_CORE_REV_A2 = 0x0002, + GLAMO_CORE_REV_A3 = 0x0003, +}; + +#endif /* _GLAMO_REGS_H */ diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-spi-gpio.c b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-spi-gpio.c new file mode 100644 index 0000000000..b92e48a17b --- /dev/null +++ b/target/linux/s3c24xx/files-2.6.30/drivers/mfd/glamo/glamo-spi-gpio.c @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2007 Openmoko, Inc. + * Author: Harald Welte <laforge@openmoko.org> + * + * Smedia Glamo GPIO based SPI driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This driver currently only implements a minimum subset of the hardware + * features, esp. those features that are required to drive the jbt6k74 + * LCM controller asic in the TD028TTEC1 LCM. + * +*/ + +#define DEBUG + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> + +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> +#include <linux/spi/glamo.h> + +#include <linux/glamofb.h> + +#include <mach/hardware.h> + +#include "glamo-core.h" +#include "glamo-regs.h" + +struct glamo_spigpio { + struct spi_bitbang bitbang; + struct spi_master *master; + struct glamo_spigpio_info *info; + struct glamo_core *glamo; +}; + +static inline struct glamo_spigpio *to_sg(struct spi_device *spi) +{ + return dev_get_drvdata(&spi->master->dev); +} + +static inline void setsck(struct spi_device *dev, int on) +{ + struct glamo_spigpio *sg = to_sg(dev); + glamo_gpio_setpin(sg->glamo, sg->info->pin_clk, on ? 1 : 0); +} + +static inline void setmosi(struct spi_device *dev, int on) +{ + struct glamo_spigpio *sg = to_sg(dev); + glamo_gpio_setpin(sg->glamo, sg->info->pin_mosi, on ? 1 : 0); +} + +static inline u32 getmiso(struct spi_device *dev) +{ + struct glamo_spigpio *sg = to_sg(dev); + if (sg->info->pin_miso) + return glamo_gpio_getpin(sg->glamo, sg->info->pin_miso) ? 1 : 0; + else + return 0; +} + +#define spidelay(x) ndelay(x) + +#define EXPAND_BITBANG_TXRX +#include <linux/spi/spi_bitbang.h> + +static u32 glamo_spigpio_txrx_mode0(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits); +} + +static u32 glamo_spigpio_txrx_mode1(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits); +} + +static u32 glamo_spigpio_txrx_mode2(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits); +} + +static u32 glamo_spigpio_txrx_mode3(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits); +} + + +#if 0 +static int glamo_spigpio_setupxfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct glamo_spi *gs = to_sg(spi); + unsigned int bpw; + + bpw = t ? t->bits_per_word : spi->bits_per_word; + + if (bpw != 9 && bpw != 8) { + dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw); + return -EINVAL; + } + + return 0; +} +#endif + +static void glamo_spigpio_chipsel(struct spi_device *spi, int value) +{ + struct glamo_spigpio *gs = to_sg(spi); +#if 0 + dev_dbg(&spi->dev, "chipsel %d: spi=%p, gs=%p, info=%p, handle=%p\n", + value, spi, gs, gs->info, gs->info->glamo); +#endif + glamo_gpio_setpin(gs->glamo, gs->info->pin_cs, value ? 0 : 1); +} + + +static int glamo_spigpio_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct glamo_spigpio *sp; + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof(struct glamo_spigpio)); + if (master == NULL) { + dev_err(&pdev->dev, "failed to allocate spi master\n"); + ret = -ENOMEM; + goto err; + } + + sp = spi_master_get_devdata(master); + platform_set_drvdata(pdev, sp); + sp->info = pdev->dev.platform_data; + if (!sp->info) { + dev_err(&pdev->dev, "can't operate without platform data\n"); + ret = -EIO; + goto err_no_pdev; + } + + master->num_chipselect = 1; + master->bus_num = 2; /* FIXME: use dynamic number */ + + sp->master = spi_master_get(master); + sp->glamo = sp->info->glamo; + + sp->bitbang.master = sp->master; + sp->bitbang.chipselect = glamo_spigpio_chipsel; + sp->bitbang.txrx_word[SPI_MODE_0] = glamo_spigpio_txrx_mode0; + sp->bitbang.txrx_word[SPI_MODE_1] = glamo_spigpio_txrx_mode1; + sp->bitbang.txrx_word[SPI_MODE_2] = glamo_spigpio_txrx_mode2; + sp->bitbang.txrx_word[SPI_MODE_3] = glamo_spigpio_txrx_mode3; + + /* set state of spi pins */ + glamo_gpio_setpin(sp->glamo, sp->info->pin_clk, 0); + glamo_gpio_setpin(sp->glamo, sp->info->pin_mosi, 0); + glamo_gpio_setpin(sp->glamo, sp->info->pin_cs, 1); + + glamo_gpio_cfgpin(sp->glamo, sp->info->pin_clk); + glamo_gpio_cfgpin(sp->glamo, sp->info->pin_mosi); + glamo_gpio_cfgpin(sp->glamo, sp->info->pin_cs); + if (sp->info->pin_miso) + glamo_gpio_cfgpin(sp->glamo, sp->info->pin_miso); + + /* bring the LCM panel out of reset if it isn't already */ + + glamo_gpio_setpin(sp->glamo, GLAMO_GPIO4, 1); + glamo_gpio_cfgpin(sp->glamo, GLAMO_GPIO4_OUTPUT); + msleep(90); + +#if 0 + sp->dev = &pdev->dev; + + sp->bitbang.setup_transfer = glamo_spi_setupxfer; + sp->bitbang.txrx_bufs = glamo_spi_txrx; + sp->bitbang.master->setup = glamo_spi_setup; +#endif + + dev_set_drvdata(&sp->master->dev, sp); + + ret = spi_bitbang_start(&sp->bitbang); + if (ret) + goto err_no_bitbang; + + return 0; + +err_no_bitbang: + platform_set_drvdata(pdev, NULL); +err_no_pdev: + spi_master_put(sp->bitbang.master); +err: + return ret; + +} + +static int glamo_spigpio_remove(struct platform_device *pdev) +{ + struct glamo_spigpio *sp = platform_get_drvdata(pdev); + + spi_bitbang_stop(&sp->bitbang); + spi_master_put(sp->bitbang.master); + + return 0; +} + +/*#define glamo_spigpio_suspend NULL +#define glamo_spigpio_resume NULL +*/ + + +#ifdef CONFIG_PM +static int glamo_spigpio_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int glamo_spigpio_resume(struct platform_device *pdev) +{ + struct glamo_spigpio *sp = platform_get_drvdata(pdev); + + if (!sp) + return 0; + + /* set state of spi pins */ + glamo_gpio_setpin(sp->glamo, sp->info->pin_clk, 0); + glamo_gpio_setpin(sp->glamo, sp->info->pin_mosi, 0); + glamo_gpio_setpin(sp->glamo, sp->info->pin_cs, 1); + + glamo_gpio_cfgpin(sp->glamo, sp->info->pin_clk); + glamo_gpio_cfgpin(sp->glamo, sp->info->pin_mosi); + glamo_gpio_cfgpin(sp->glamo, sp->info->pin_cs); + if (sp->info->pin_miso) + glamo_gpio_cfgpin(sp->glamo, sp->info->pin_miso); + + return 0; +} +#endif + +static struct platform_driver glamo_spi_drv = { + .probe = glamo_spigpio_probe, + .remove = glamo_spigpio_remove, +#ifdef CONFIG_PM + .suspend_late = glamo_spigpio_suspend, + .resume_early = glamo_spigpio_resume, +#endif + .driver = { + .name = "glamo-spi-gpio", + .owner = THIS_MODULE, + }, +}; + +static int __init glamo_spi_init(void) +{ + return platform_driver_register(&glamo_spi_drv); +} + +static void __exit glamo_spi_exit(void) +{ + platform_driver_unregister(&glamo_spi_drv); +} + +module_init(glamo_spi_init); +module_exit(glamo_spi_exit); + +MODULE_DESCRIPTION("Smedia Glamo 336x/337x LCM serial command SPI Driver"); +MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>") +MODULE_LICENSE("GPL"); |