machine-$(CONFIG_ARCH_IXP23XX) := ixp23xx
machine-$(CONFIG_ARCH_OMAP1) := omap1
machine-$(CONFIG_ARCH_OMAP2) := omap2
- machine-$(CONFIG_ARCH_OMAP3) := omap2
- incdir-$(CONFIG_ARCH_OMAP) := omap
- machine-$(CONFIG_ARCH_S3C2410) := s3c2410
++ machine-$(CONFIG_ARCH_OMAP3) := omap2
+ plat-$(CONFIG_ARCH_OMAP) := omap
+ machine-$(CONFIG_ARCH_S3C2410) := s3c2410 s3c2400 s3c2412 s3c2440 s3c2442 s3c2443
+ plat-$(CONFIG_PLAT_S3C24XX) := s3c24xx
machine-$(CONFIG_ARCH_LH7A40X) := lh7a40x
machine-$(CONFIG_ARCH_VERSATILE) := versatile
machine-$(CONFIG_ARCH_IMX) := imx
#include <linux/mtd/partitions.h>
#include <linux/input.h>
#include <linux/i2c/tps65010.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc2101.h>
+#include <linux/clk.h>
- #include <asm/hardware.h>
+ #include <mach/hardware.h>
#include <asm/gpio.h>
#include <asm/mach-types.h>
#include <asm/mach/flash.h>
#include <asm/mach/map.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/gpio-switch.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/tc.h>
- #include <asm/arch/nand.h>
- #include <asm/arch/irda.h>
- #include <asm/arch/usb.h>
- #include <asm/arch/keypad.h>
- #include <asm/arch/common.h>
- #include <asm/arch/mcbsp.h>
- #include <asm/arch/omap-alsa.h>
++#include <mach/gpio.h>
+ #include <mach/gpio-switch.h>
+ #include <mach/mux.h>
+ #include <mach/tc.h>
+ #include <mach/nand.h>
+ #include <mach/irda.h>
+ #include <mach/usb.h>
+ #include <mach/keypad.h>
+ #include <mach/common.h>
+ #include <mach/mcbsp.h>
+ #include <mach/omap-alsa.h>
static int h2_keymap[] = {
KEY(0, 0, KEY_LEFT),
#include <asm/mach/flash.h>
#include <asm/mach/map.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/gpio-switch.h>
- #include <asm/arch/gpioexpander.h>
- #include <asm/arch/irqs.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/tc.h>
- #include <asm/arch/nand.h>
- #include <asm/arch/irda.h>
- #include <asm/arch/usb.h>
- #include <asm/arch/keypad.h>
- #include <asm/arch/dma.h>
- #include <asm/arch/common.h>
- #include <asm/arch/mcbsp.h>
- #include <asm/arch/omap-alsa.h>
+#include <media/v4l2-int-device.h>
+
++#include <mach/gpio.h>
++#include <mach/gpio-switch.h>
+ #include <mach/gpioexpander.h>
+ #include <mach/irqs.h>
+ #include <mach/mux.h>
+ #include <mach/tc.h>
+ #include <mach/nand.h>
+ #include <mach/irda.h>
+ #include <mach/usb.h>
+ #include <mach/keypad.h>
+ #include <mach/dma.h>
+ #include <mach/common.h>
+ #include <mach/mcbsp.h>
+ #include <mach/omap-alsa.h>
+#include <../drivers/media/video/ov9640.h>
+
#define H3_TS_GPIO 48
static int h3_keymap[] = {
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/usb.h>
- #include <asm/arch/board.h>
- #include <asm/arch/keypad.h>
- #include <asm/arch/common.h>
- #include <asm/arch/dsp_common.h>
- #include <asm/arch/aic23.h>
- #include <asm/arch/omapfb.h>
- #include <asm/arch/hwa742.h>
- #include <asm/arch/lcd_mipid.h>
+ #include <mach/gpio.h>
+ #include <mach/mux.h>
+ #include <mach/usb.h>
+ #include <mach/board.h>
+ #include <mach/keypad.h>
+ #include <mach/common.h>
+ #include <mach/dsp_common.h>
+ #include <mach/aic23.h>
+ #include <mach/omapfb.h>
++#include <mach/hwa742.h>
+ #include <mach/lcd_mipid.h>
#define ADS7846_PENDOWN_GPIO 15
#include <asm/mach/map.h>
#include <asm/mach/flash.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/usb.h>
- #include <asm/arch/tc.h>
- #include <asm/arch/dma.h>
- #include <asm/arch/board.h>
- #include <asm/arch/irda.h>
- #include <asm/arch/keypad.h>
- #include <asm/arch/common.h>
- #include <asm/arch/mcbsp.h>
- #include <asm/arch/omap-alsa.h>
- #include <asm/arch/gpio-switch.h>
+ #include <mach/gpio.h>
+ #include <mach/mux.h>
+ #include <mach/usb.h>
+ #include <mach/tc.h>
+ #include <mach/dma.h>
+ #include <mach/board.h>
+ #include <mach/irda.h>
+ #include <mach/keypad.h>
+ #include <mach/common.h>
+ #include <mach/mcbsp.h>
+ #include <mach/omap-alsa.h>
++#include <mach/gpio-switch.h>
static void __init omap_palmte_init_irq(void)
{
#include <asm/mach/flash.h>
#include <asm/mach/map.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/gpio-switch.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/irda.h>
- #include <asm/arch/usb.h>
- #include <asm/arch/tc.h>
- #include <asm/arch/board.h>
- #include <asm/arch/common.h>
- #include <asm/arch/mcbsp.h>
- #include <asm/arch/omap-alsa.h>
- #include <asm/arch/keypad.h>
+ #include <mach/gpio.h>
++#include <mach/gpio-switch.h>
+ #include <mach/mux.h>
+ #include <mach/irda.h>
+ #include <mach/usb.h>
+ #include <mach/tc.h>
+ #include <mach/board.h>
+ #include <mach/common.h>
+ #include <mach/mcbsp.h>
+ #include <mach/omap-alsa.h>
+ #include <mach/keypad.h>
/* Write to I2C device */
int sx1_i2c_write_byte(u8 devaddr, u8 regoffset, u8 value)
--- /dev/null
- #include <asm/arch/dsp_common.h>
+/*
+ * linux/arch/arm/mach-omap1/mmu.c
+ *
+ * Support for non-MPU OMAP1 MMUs.
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ * and Paul Mundt <paul.mundt@nokia.com>
+ *
+ * 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/types.h>
+#include <linux/init.h>
+#include <linux/rwsem.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include "mmu.h"
+#include <asm/tlbflush.h>
++#include <mach/dsp_common.h>
+
+static void *dspvect_page;
+#define DSP_INIT_PAGE 0xfff000
+
+#define MMUFAULT_MASK (OMAP_MMU_FAULT_ST_PERM |\
+ OMAP_MMU_FAULT_ST_TLB_MISS |\
+ OMAP_MMU_FAULT_ST_TRANS)
+
+static unsigned int get_cam_l_va_mask(u16 pgsz)
+{
+ switch (pgsz) {
+ case OMAP_MMU_CAM_PAGESIZE_1MB:
+ return OMAP_MMU_CAM_L_VA_TAG_L1_MASK |
+ OMAP_MMU_CAM_L_VA_TAG_L2_MASK_1MB;
+ case OMAP_MMU_CAM_PAGESIZE_64KB:
+ return OMAP_MMU_CAM_L_VA_TAG_L1_MASK |
+ OMAP_MMU_CAM_L_VA_TAG_L2_MASK_64KB;
+ case OMAP_MMU_CAM_PAGESIZE_4KB:
+ return OMAP_MMU_CAM_L_VA_TAG_L1_MASK |
+ OMAP_MMU_CAM_L_VA_TAG_L2_MASK_4KB;
+ case OMAP_MMU_CAM_PAGESIZE_1KB:
+ return OMAP_MMU_CAM_L_VA_TAG_L1_MASK |
+ OMAP_MMU_CAM_L_VA_TAG_L2_MASK_1KB;
+ }
+ return 0;
+}
+
+#define get_cam_va_mask(pgsz) \
+ ((u32)OMAP_MMU_CAM_H_VA_TAG_H_MASK << 22 | \
+ (u32)get_cam_l_va_mask(pgsz) << 6)
+
+static int intmem_usecount;
+
+/* for safety */
+void dsp_mem_usecount_clear(void)
+{
+ if (intmem_usecount != 0) {
+ printk(KERN_WARNING
+ "MMU: unbalanced memory request/release detected.\n"
+ " intmem_usecount is not zero at where "
+ "it should be! ... fixed to be zero.\n");
+ intmem_usecount = 0;
+ omap_dsp_release_mem();
+ }
+}
+EXPORT_SYMBOL_GPL(dsp_mem_usecount_clear);
+
+void omap_mmu_itack(struct omap_mmu *mmu)
+{
+ omap_mmu_write_reg(mmu, OMAP_MMU_IT_ACK_IT_ACK, OMAP_MMU_IT_ACK);
+}
+EXPORT_SYMBOL(omap_mmu_itack);
+
+static int omap1_mmu_mem_enable(struct omap_mmu *mmu, void *addr)
+{
+ int ret = 0;
+
+ if (omap_mmu_internal_memory(mmu, addr)) {
+ if (intmem_usecount++ == 0)
+ ret = omap_dsp_request_mem();
+ }
+
+ return ret;
+}
+
+static int omap1_mmu_mem_disable(struct omap_mmu *mmu, void *addr)
+{
+ int ret = 0;
+
+ if (omap_mmu_internal_memory(mmu, addr)) {
+ if (--intmem_usecount == 0)
+ omap_dsp_release_mem();
+ } else
+ ret = -EIO;
+
+ return ret;
+}
+
+static inline void
+omap1_mmu_read_tlb(struct omap_mmu *mmu, struct cam_ram_regset *cr)
+{
+ /* read a TLB entry */
+ omap_mmu_write_reg(mmu, OMAP_MMU_LD_TLB_RD, OMAP_MMU_LD_TLB);
+
+ cr->cam_h = omap_mmu_read_reg(mmu, OMAP_MMU_READ_CAM_H);
+ cr->cam_l = omap_mmu_read_reg(mmu, OMAP_MMU_READ_CAM_L);
+ cr->ram_h = omap_mmu_read_reg(mmu, OMAP_MMU_READ_RAM_H);
+ cr->ram_l = omap_mmu_read_reg(mmu, OMAP_MMU_READ_RAM_L);
+}
+
+static inline void
+omap1_mmu_load_tlb(struct omap_mmu *mmu, struct cam_ram_regset *cr)
+{
+ /* Set the CAM and RAM entries */
+ omap_mmu_write_reg(mmu, cr->cam_h, OMAP_MMU_CAM_H);
+ omap_mmu_write_reg(mmu, cr->cam_l, OMAP_MMU_CAM_L);
+ omap_mmu_write_reg(mmu, cr->ram_h, OMAP_MMU_RAM_H);
+ omap_mmu_write_reg(mmu, cr->ram_l, OMAP_MMU_RAM_L);
+}
+
+static ssize_t omap1_mmu_show(struct omap_mmu *mmu, char *buf,
+ struct omap_mmu_tlb_lock *tlb_lock)
+{
+ int i, len;
+
+ len = sprintf(buf, "P: preserved, V: valid\n"
+ "ety P V size cam_va ram_pa ap\n");
+ /* 00: P V 4KB 0x300000 0x10171800 FA */
+
+ for (i = 0; i < mmu->nr_tlb_entries; i++) {
+ struct omap_mmu_tlb_entry ent;
+ struct cam_ram_regset cr;
+ struct omap_mmu_tlb_lock entry_lock;
+ char *pgsz_str, *ap_str;
+
+ /* read a TLB entry */
+ entry_lock.base = tlb_lock->base;
+ entry_lock.victim = i;
+ omap_mmu_read_tlb(mmu, &entry_lock, &cr);
+
+ ent.pgsz = cr.cam_l & OMAP_MMU_CAM_PAGESIZE_MASK;
+ ent.prsvd = cr.cam_l & OMAP_MMU_CAM_P;
+ ent.valid = cr.cam_l & OMAP_MMU_CAM_V;
+ ent.ap = cr.ram_l & OMAP_MMU_RAM_L_AP_MASK;
+ ent.va = (u32)(cr.cam_h & OMAP_MMU_CAM_H_VA_TAG_H_MASK) << 22 |
+ (u32)(cr.cam_l & get_cam_l_va_mask(ent.pgsz)) << 6;
+ ent.pa = (unsigned long)cr.ram_h << 16 |
+ (cr.ram_l & OMAP_MMU_RAM_L_RAM_LSB_MASK);
+
+ pgsz_str = (ent.pgsz == OMAP_MMU_CAM_PAGESIZE_1MB) ? " 1MB":
+ (ent.pgsz == OMAP_MMU_CAM_PAGESIZE_64KB) ? "64KB":
+ (ent.pgsz == OMAP_MMU_CAM_PAGESIZE_4KB) ? " 4KB":
+ (ent.pgsz == OMAP_MMU_CAM_PAGESIZE_1KB) ? " 1KB":
+ " ???";
+ ap_str = (ent.ap == OMAP_MMU_RAM_L_AP_RO) ? "RO":
+ (ent.ap == OMAP_MMU_RAM_L_AP_FA) ? "FA":
+ (ent.ap == OMAP_MMU_RAM_L_AP_NA) ? "NA":
+ "??";
+
+ if (i == tlb_lock->base)
+ len += sprintf(buf + len, "lock base = %d\n",
+ tlb_lock->base);
+ if (i == tlb_lock->victim)
+ len += sprintf(buf + len, "victim = %d\n",
+ tlb_lock->victim);
+ len += sprintf(buf + len,
+ /* 00: P V 4KB 0x300000 0x10171800 FA */
+ "%02d: %c %c %s 0x%06lx 0x%08lx %s\n",
+ i,
+ ent.prsvd ? 'P' : ' ',
+ ent.valid ? 'V' : ' ',
+ pgsz_str, ent.va, ent.pa, ap_str);
+ }
+
+ return len;
+}
+
+static int exmap_setup_preserved_entries(struct omap_mmu *mmu)
+{
+ int n = 0;
+
+ exmap_setup_preserved_mem_page(mmu, dspvect_page, DSP_INIT_PAGE, n++);
+
+ return n;
+}
+
+static void exmap_clear_preserved_entries(struct omap_mmu *mmu)
+{
+ exmap_clear_mem_page(mmu, DSP_INIT_PAGE);
+}
+
+static int omap1_mmu_startup(struct omap_mmu *mmu)
+{
+ dspvect_page = (void *)__get_dma_pages(GFP_KERNEL, 0);
+ if (dspvect_page == NULL) {
+ dev_err(mmu->dev, "MMU %s: failed to allocate memory "
+ "for vector table\n", mmu->name);
+ return -ENOMEM;
+ }
+
+ mmu->nr_exmap_preserved = exmap_setup_preserved_entries(mmu);
+
+ return 0;
+}
+
+static void omap1_mmu_shutdown(struct omap_mmu *mmu)
+{
+ exmap_clear_preserved_entries(mmu);
+
+ if (dspvect_page != NULL) {
+ unsigned long virt;
+
+ down_read(&mmu->exmap_sem);
+
+ virt = (unsigned long)omap_mmu_to_virt(mmu, DSP_INIT_PAGE);
+ flush_tlb_kernel_range(virt, virt + PAGE_SIZE);
+ free_page((unsigned long)dspvect_page);
+ dspvect_page = NULL;
+
+ up_read(&mmu->exmap_sem);
+ }
+}
+
+static inline unsigned long omap1_mmu_cam_va(struct cam_ram_regset *cr)
+{
+ unsigned int page_size = cr->cam_l & OMAP_MMU_CAM_PAGESIZE_MASK;
+
+ return (u32)(cr->cam_h & OMAP_MMU_CAM_H_VA_TAG_H_MASK) << 22 |
+ (u32)(cr->cam_l & get_cam_l_va_mask(page_size)) << 6;
+}
+
+static struct cam_ram_regset *
+omap1_mmu_cam_ram_alloc(struct omap_mmu *mmu, struct omap_mmu_tlb_entry *entry)
+{
+ struct cam_ram_regset *cr;
+
+ if (entry->va & ~(get_cam_va_mask(entry->pgsz))) {
+ dev_err(mmu->dev, "MMU %s: mapping vadr (0x%06lx) is not on"
+ " an aligned boundary\n", mmu->name, entry->va);
+ return ERR_PTR(-EINVAL);
+ }
+
+ cr = kmalloc(sizeof(struct cam_ram_regset), GFP_KERNEL);
+
+ cr->cam_h = entry->va >> 22;
+ cr->cam_l = (entry->va >> 6 & get_cam_l_va_mask(entry->pgsz)) |
+ entry->prsvd | entry->pgsz;
+ cr->ram_h = entry->pa >> 16;
+ cr->ram_l = (entry->pa & OMAP_MMU_RAM_L_RAM_LSB_MASK) | entry->ap;
+
+ return cr;
+}
+
+static inline int omap1_mmu_cam_ram_valid(struct cam_ram_regset *cr)
+{
+ return cr->cam_l & OMAP_MMU_CAM_V;
+}
+
+static void omap1_mmu_interrupt(struct omap_mmu *mmu)
+{
+ unsigned long status;
+ unsigned long adh, adl;
+ unsigned long dp;
+ unsigned long va;
+
+ status = omap_mmu_read_reg(mmu, OMAP_MMU_FAULT_ST);
+ adh = omap_mmu_read_reg(mmu, OMAP_MMU_FAULT_AD_H);
+ adl = omap_mmu_read_reg(mmu, OMAP_MMU_FAULT_AD_L);
+ dp = adh & OMAP_MMU_FAULT_AD_H_DP;
+ va = (((adh & OMAP_MMU_FAULT_AD_H_ADR_MASK) << 16) | adl);
+
+ /* if the fault is masked, nothing to do */
+ if ((status & MMUFAULT_MASK) == 0) {
+ pr_debug("MMU interrupt, but ignoring.\n");
+ /*
+ * note: in OMAP1710,
+ * when CACHE + DMA domain gets out of idle in DSP,
+ * MMU interrupt occurs but MMU_FAULT_ST is not set.
+ * in this case, we just ignore the interrupt.
+ */
+ if (status) {
+ pr_debug("%s%s%s%s\n",
+ (status & OMAP_MMU_FAULT_ST_PREF)?
+ " (prefetch err)" : "",
+ (status & OMAP_MMU_FAULT_ST_PERM)?
+ " (permission fault)" : "",
+ (status & OMAP_MMU_FAULT_ST_TLB_MISS)?
+ " (TLB miss)" : "",
+ (status & OMAP_MMU_FAULT_ST_TRANS) ?
+ " (translation fault)": "");
+ pr_debug("fault address = %#08lx\n", va);
+ }
+ enable_irq(mmu->irq);
+ return;
+ }
+
+ pr_info("%s%s%s%s\n",
+ (status & OMAP_MMU_FAULT_ST_PREF)?
+ (MMUFAULT_MASK & OMAP_MMU_FAULT_ST_PREF)?
+ " prefetch err":
+ " (prefetch err)":
+ "",
+ (status & OMAP_MMU_FAULT_ST_PERM)?
+ (MMUFAULT_MASK & OMAP_MMU_FAULT_ST_PERM)?
+ " permission fault":
+ " (permission fault)":
+ "",
+ (status & OMAP_MMU_FAULT_ST_TLB_MISS)?
+ (MMUFAULT_MASK & OMAP_MMU_FAULT_ST_TLB_MISS)?
+ " TLB miss":
+ " (TLB miss)":
+ "",
+ (status & OMAP_MMU_FAULT_ST_TRANS)?
+ (MMUFAULT_MASK & OMAP_MMU_FAULT_ST_TRANS)?
+ " translation fault":
+ " (translation fault)":
+ "");
+ pr_info("fault address = %#08lx\n", va);
+
+ mmu->fault_address = va;
+ schedule_work(&mmu->irq_work);
+}
+
+static pgprot_t omap1_mmu_pte_get_attr(struct omap_mmu_tlb_entry *entry)
+{
+ /* 4KB AP position as default */
+ u32 attr = entry->ap >> 4;
+ attr <<= ((entry->pgsz == OMAP_MMU_CAM_PAGESIZE_1MB) ? 6:0);
+ return attr;
+}
+
+struct omap_mmu_ops omap1_mmu_ops = {
+ .startup = omap1_mmu_startup,
+ .shutdown = omap1_mmu_shutdown,
+ .mem_enable = omap1_mmu_mem_enable,
+ .mem_disable = omap1_mmu_mem_disable,
+ .read_tlb = omap1_mmu_read_tlb,
+ .load_tlb = omap1_mmu_load_tlb,
+ .show = omap1_mmu_show,
+ .cam_va = omap1_mmu_cam_va,
+ .cam_ram_alloc = omap1_mmu_cam_ram_alloc,
+ .cam_ram_valid = omap1_mmu_cam_ram_valid,
+ .interrupt = omap1_mmu_interrupt,
+ .pte_get_attr = omap1_mmu_pte_get_attr,
+};
+EXPORT_SYMBOL_GPL(omap1_mmu_ops);
--- /dev/null
- #include <asm/arch/mmu.h>
+#ifndef __MACH_OMAP1_MMU_H
+#define __MACH_OMAP1_MMU_H
+
+#include <linux/io.h>
++#include <mach/mmu.h>
+
+#define MMU_LOCK_BASE_MASK (0x3f << 10)
+#define MMU_LOCK_VICTIM_MASK (0x3f << 4)
+
+#define OMAP_MMU_PREFETCH 0x00
+#define OMAP_MMU_WALKING_ST 0x04
+#define OMAP_MMU_CNTL 0x08
+#define OMAP_MMU_FAULT_AD_H 0x0c
+#define OMAP_MMU_FAULT_AD_L 0x10
+#define OMAP_MMU_FAULT_ST 0x14
+#define OMAP_MMU_IT_ACK 0x18
+#define OMAP_MMU_TTB_H 0x1c
+#define OMAP_MMU_TTB_L 0x20
+#define OMAP_MMU_LOCK 0x24
+#define OMAP_MMU_LD_TLB 0x28
+#define OMAP_MMU_CAM_H 0x2c
+#define OMAP_MMU_CAM_L 0x30
+#define OMAP_MMU_RAM_H 0x34
+#define OMAP_MMU_RAM_L 0x38
+#define OMAP_MMU_GFLUSH 0x3c
+#define OMAP_MMU_FLUSH_ENTRY 0x40
+#define OMAP_MMU_READ_CAM_H 0x44
+#define OMAP_MMU_READ_CAM_L 0x48
+#define OMAP_MMU_READ_RAM_H 0x4c
+#define OMAP_MMU_READ_RAM_L 0x50
+
+#define OMAP_MMU_CNTL_BURST_16MNGT_EN 0x0020
+#define OMAP_MMU_CNTL_WTL_EN 0x0004
+#define OMAP_MMU_CNTL_MMU_EN 0x0002
+#define OMAP_MMU_CNTL_RESET_SW 0x0001
+
+#define OMAP_MMU_FAULT_AD_H_DP 0x0100
+#define OMAP_MMU_FAULT_AD_H_ADR_MASK 0x00ff
+
+#define OMAP_MMU_FAULT_ST_PREF 0x0008
+#define OMAP_MMU_FAULT_ST_PERM 0x0004
+#define OMAP_MMU_FAULT_ST_TLB_MISS 0x0002
+#define OMAP_MMU_FAULT_ST_TRANS 0x0001
+
+#define OMAP_MMU_IT_ACK_IT_ACK 0x0001
+
+#define OMAP_MMU_CAM_H_VA_TAG_H_MASK 0x0003
+
+#define OMAP_MMU_CAM_L_VA_TAG_L1_MASK 0xc000
+#define OMAP_MMU_CAM_L_VA_TAG_L2_MASK_1MB 0x0000
+#define OMAP_MMU_CAM_L_VA_TAG_L2_MASK_64KB 0x3c00
+#define OMAP_MMU_CAM_L_VA_TAG_L2_MASK_4KB 0x3fc0
+#define OMAP_MMU_CAM_L_VA_TAG_L2_MASK_1KB 0x3ff0
+#define OMAP_MMU_CAM_L_P 0x0008
+#define OMAP_MMU_CAM_L_V 0x0004
+#define OMAP_MMU_CAM_L_PAGESIZE_MASK 0x0003
+#define OMAP_MMU_CAM_L_PAGESIZE_1MB 0x0000
+#define OMAP_MMU_CAM_L_PAGESIZE_64KB 0x0001
+#define OMAP_MMU_CAM_L_PAGESIZE_4KB 0x0002
+#define OMAP_MMU_CAM_L_PAGESIZE_1KB 0x0003
+
+#define OMAP_MMU_CAM_P OMAP_MMU_CAM_L_P
+#define OMAP_MMU_CAM_V OMAP_MMU_CAM_L_V
+#define OMAP_MMU_CAM_PAGESIZE_MASK OMAP_MMU_CAM_L_PAGESIZE_MASK
+#define OMAP_MMU_CAM_PAGESIZE_1MB OMAP_MMU_CAM_L_PAGESIZE_1MB
+#define OMAP_MMU_CAM_PAGESIZE_64KB OMAP_MMU_CAM_L_PAGESIZE_64KB
+#define OMAP_MMU_CAM_PAGESIZE_4KB OMAP_MMU_CAM_L_PAGESIZE_4KB
+#define OMAP_MMU_CAM_PAGESIZE_1KB OMAP_MMU_CAM_L_PAGESIZE_1KB
+#define OMAP_MMU_CAM_PAGESIZE_16MB -1 /* unused in omap1 */
+
+#define OMAP_MMU_RAM_L_RAM_LSB_MASK 0xfc00
+#define OMAP_MMU_RAM_L_AP_MASK 0x0300
+#define OMAP_MMU_RAM_L_AP_NA 0x0000
+#define OMAP_MMU_RAM_L_AP_RO 0x0200
+#define OMAP_MMU_RAM_L_AP_FA 0x0300
+
+#define OMAP_MMU_LD_TLB_RD 0x0002
+
+#define INIT_TLB_ENTRY(ent, v, p, ps) \
+do { \
+ (ent)->va = (v); \
+ (ent)->pa = (p); \
+ (ent)->pgsz = (ps); \
+ (ent)->prsvd = 0; \
+ (ent)->ap = OMAP_MMU_RAM_L_AP_FA; \
+ (ent)->tlb = 1; \
+} while (0)
+
+#define INIT_TLB_ENTRY_4KB_PRESERVED(ent, v, p) \
+do { \
+ (ent)->va = (v); \
+ (ent)->pa = (p); \
+ (ent)->pgsz = OMAP_MMU_CAM_PAGESIZE_4KB; \
+ (ent)->prsvd = OMAP_MMU_CAM_P; \
+ (ent)->ap = OMAP_MMU_RAM_L_AP_FA; \
+} while (0)
+
+struct omap_mmu_tlb_entry {
+ unsigned long va;
+ unsigned long pa;
+ unsigned int pgsz, prsvd, valid;
+
+ u16 ap;
+ unsigned int tlb;
+};
+
+static inline unsigned short
+omap_mmu_read_reg(struct omap_mmu *mmu, unsigned long reg)
+{
+ return __raw_readw(mmu->base + reg);
+}
+
+static inline void omap_mmu_write_reg(struct omap_mmu *mmu,
+ unsigned short val, unsigned long reg)
+{
+ __raw_writew(val, mmu->base + reg);
+}
+
+#endif /* __MACH_OMAP1_MMU_H */
--- /dev/null
- #include <asm/arch/bci.h>
+/*
+ * linux/arch/arm/mach-omap2/bci.c
+ *
+ * TWL4030 BCI platform device setup/initialization
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
++#include <mach/bci.h>
+
+#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
+ defined(CONFIG_TWL4030_BCI_BATTERY_MODULE)
+/*
+ * Thermistor Calibration for Current Source and MADC
+ * Tolerance (for THS05-3H103F)
+ */
+static int sdp3430_batt_table[] = {
+/* 0 C*/
+30800, 29500, 28300, 27100,
+26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900,
+17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100,
+11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310,
+8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830,
+5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170,
+4040, 3910, 3790, 3670, 3550
+};
+
+static struct twl4030_bci_platform_data sdp3430_bci_data = {
+ .battery_tmp_tbl = sdp3430_batt_table,
+ .tblsize = ARRAY_SIZE(sdp3430_batt_table),
+};
+
+static struct platform_device twl4030_bci_battery_device = {
+ .name = "twl4030-bci-battery",
+ .id = -1,
+ .dev = {
+ .platform_data = &sdp3430_bci_data,
+ },
+ .num_resources = 0,
+};
+
+void __init twl4030_bci_battery_init(void)
+{
+ (void) platform_device_register(&twl4030_bci_battery_device);
+}
+#else
+void __init twl4030_bci_battery_init(void)
+{
+}
+#endif
--- /dev/null
- #include <asm/arch/onenand.h>
- #include <asm/arch/board.h>
- #include <asm/arch/gpmc.h>
- #include <asm/arch/nand.h>
+/*
+ * linux/arch/arm/mach-omap2/board-2430sdp-flash.c
+ *
+ * Copyright (C) 2007 MontaVista Software, Inc. <source@mvista.com>
+ * Author: Kevin Hilman
+ *
+ * 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/kernel.h>
+#include <linux/platform_device.h>
+#include <asm/mach/flash.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/onenand_regs.h>
+
+#include <asm/io.h>
++#include <mach/onenand.h>
++#include <mach/board.h>
++#include <mach/gpmc.h>
++#include <mach/nand.h>
+
+#define ONENAND_MAP 0x20000000
+#define GPMC_OFF_CONFIG1_0 0x60
+
+enum fstype {
+ NAND = 0,
+ NOR,
+ ONENAND,
+ UNKNOWN = -1
+};
+
+static enum fstype flash_type = NAND;
+
+static struct mtd_partition nand_partitions[] = {
+ {
+ .name = "X-Loader",
+ .offset = 0,
+ .size = 4*(64*2048), /* 0-3 blks reserved.
+ Mandated by ROM code */
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "U-Boot",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 4*(64*2048),
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "U-Boot Environment",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 2*(64*2048),
+ },
+ {
+ .name = "Kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 32*(64*2048), /* 4*1M */
+ },
+ {
+ .name = "File System",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+static struct omap_nand_platform_data sdp_nand_data = {
+ .parts = nand_partitions,
+ .nr_parts = ARRAY_SIZE(nand_partitions),
+ .dma_channel = -1, /* disable DMA in OMAP OneNAND driver */
+};
+
+static struct platform_device sdp_nand_device = {
+ .name = "omap2-nand",
+ .id = -1,
+ .dev = {
+ .platform_data = &sdp_nand_data,
+ },
+};
+
+static struct mtd_partition onenand_partitions[] = {
+ {
+ .name = "(OneNAND)X-Loader",
+ .offset = 0,
+ .size = 4*(64*2048), /* 0-3 blks reserved.
+ Mandated by ROM code */
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "(OneNAND)U-Boot",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 2*(64*2048),
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "(OneNAND)U-Boot Environment",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 1*(64*2048),
+ },
+ {
+ .name = "(OneNAND)Kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 4*(64*2048),
+ },
+ {
+ .name = "(OneNAND)File System",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct omap_onenand_platform_data sdp_onenand_data = {
+ .parts = onenand_partitions,
+ .nr_parts = ARRAY_SIZE(onenand_partitions),
+ .dma_channel = -1, /* disable DMA in OMAP OneNAND driver */
+};
+
+static struct platform_device sdp_onenand_device = {
+ .name = "omap2-onenand",
+ .id = -1,
+ .dev = {
+ .platform_data = &sdp_onenand_data,
+ },
+};
+
+void __init sdp2430_flash_init(void)
+{
+ void __iomem *gpmc_base_add, *gpmc_cs_base_add;
+ unsigned char cs = 0;
+
+ gpmc_base_add = (__force void __iomem *)OMAP243X_GPMC_VIRT;
+ while (cs < GPMC_CS_NUM) {
+ int ret = 0;
+
+ /* Each GPMC set for a single CS is at offset 0x30 */
+ gpmc_cs_base_add = (gpmc_base_add + GPMC_OFF_CONFIG1_0 +
+ (cs*0x30));
+
+ /* xloader/Uboot would have programmed the NAND/oneNAND
+ * base address for us This is a ugly hack. The proper
+ * way of doing this is to pass the setup of u-boot up
+ * to kernel using kernel params - something on the
+ * lines of machineID. Check if Nand/oneNAND is
+ * configured */
+ ret = __raw_readl(gpmc_cs_base_add + GPMC_CS_CONFIG1);
+ if ((ret & 0xC00) == (0x800)) {
+ /* Found it!! */
+ printk(KERN_INFO "NAND: Found NAND on CS %d \n", cs);
+ flash_type = NAND;
+ break;
+ }
+ ret = __raw_readl(gpmc_cs_base_add + GPMC_CS_CONFIG7);
+ if ((ret & 0x3F) == (ONENAND_MAP >> 24)) {
+ /* Found it!! */
+ flash_type = ONENAND;
+ break;
+ }
+ cs++;
+ }
+ if (cs >= GPMC_CS_NUM) {
+ printk(KERN_INFO "MTD: Unable to find MTD configuration in "
+ "GPMC - not registering.\n");
+ return;
+ }
+
+ if (flash_type == NAND) {
+ sdp_nand_data.cs = cs;
+ sdp_nand_data.gpmc_cs_baseaddr = gpmc_cs_base_add;
+ sdp_nand_data.gpmc_baseaddr = gpmc_base_add;
+
+ if (platform_device_register(&sdp_nand_device) < 0) {
+ printk(KERN_ERR "Unable to register NAND device\n");
+ return;
+ }
+ }
+
+ if (flash_type == ONENAND) {
+ sdp_onenand_data.cs = cs;
+
+ if (platform_device_register(&sdp_onenand_device) < 0) {
+ printk(KERN_ERR "Unable to register OneNAND device\n");
+ return;
+ }
+ }
+}
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
+#include <linux/input.h>
#include <linux/err.h>
#include <linux/clk.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/i2c/twl4030-rtc.h>
- #include <asm/hardware.h>
+ #include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/flash.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/board.h>
- #include <asm/arch/usb-musb.h>
- #include <asm/arch/hsmmc.h>
- #include <asm/arch/common.h>
- #include <asm/arch/keypad.h>
- #include <asm/arch/gpmc.h>
- #include <asm/arch/mcspi.h>
+ #include <mach/gpio.h>
+ #include <mach/mux.h>
+ #include <mach/board.h>
++#include <mach/usb-musb.h>
++#include <mach/hsmmc.h>
+ #include <mach/common.h>
++#include <mach/keypad.h>
+ #include <mach/gpmc.h>
++#include <mach/mcspi.h>
#include <asm/io.h>
--- /dev/null
- #include <asm/arch/onenand.h>
- #include <asm/arch/board.h>
- #include <asm/arch/gpmc.h>
+/*
+ * linux/arch/arm/mach-omap2/board-3430sdp-flash.c
+ *
+ * Copyright (c) 2007 Texas Instruments
+ *
+ * Modified from mach-omap2/board-2430sdp-flash.c
+ * Author: Rohit Choraria <rohitkc@ti.com>
+ *
+ * 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/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/onenand_regs.h>
+#include <linux/types.h>
+#include <linux/io.h>
+
+#include <asm/mach/flash.h>
++#include <mach/onenand.h>
++#include <mach/board.h>
++#include <mach/gpmc.h>
+
+static struct mtd_partition sdp_nor_partitions[] = {
+ /* bootloader (U-Boot, etc) in first sector */
+ {
+ .name = "Bootloader-NOR",
+ .offset = 0,
+ .size = SZ_256K,
+ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ /* bootloader params in the next sector */
+ {
+ .name = "Params-NOR",
+ .offset = MTDPART_OFS_APPEND,
+ .size = SZ_256K,
+ .mask_flags = 0,
+ },
+ /* kernel */
+ {
+ .name = "Kernel-NOR",
+ .offset = MTDPART_OFS_APPEND,
+ .size = SZ_2M,
+ .mask_flags = 0
+ },
+ /* file system */
+ {
+ .name = "Filesystem-NOR",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ .mask_flags = 0
+ }
+};
+
+static struct flash_platform_data sdp_nor_data = {
+ .map_name = "cfi_probe",
+ .width = 2,
+ .parts = sdp_nor_partitions,
+ .nr_parts = ARRAY_SIZE(sdp_nor_partitions),
+};
+
+static struct resource sdp_nor_resource = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device sdp_nor_device = {
+ .name = "omapflash",
+ .id = 0,
+ .dev = {
+ .platform_data = &sdp_nor_data,
+ },
+ .num_resources = 1,
+ .resource = &sdp_nor_resource,
+};
+
+static int sdp_onenand_setup(void __iomem *, int freq);
+
+static struct mtd_partition sdp_onenand_partitions[] = {
+ {
+ .name = "X-Loader-OneNAND",
+ .offset = 0,
+ .size = 4 * (64 * 2048),
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "U-Boot-OneNAND",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 2 * (64 * 2048),
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "U-Boot Environment-OneNAND",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 1 * (64 * 2048),
+ },
+ {
+ .name = "Kernel-OneNAND",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 16 * (64 * 2048),
+ },
+ {
+ .name = "File System-OneNAND",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct omap_onenand_platform_data sdp_onenand_data = {
+ .parts = sdp_onenand_partitions,
+ .nr_parts = ARRAY_SIZE(sdp_onenand_partitions),
+ .onenand_setup = sdp_onenand_setup,
+ .dma_channel = -1, /* disable DMA in OMAP OneNAND driver */
+};
+
+static struct platform_device sdp_onenand_device = {
+ .name = "omap2-onenand",
+ .id = -1,
+ .dev = {
+ .platform_data = &sdp_onenand_data,
+ },
+};
+
+/*
+ * sdp_onenand_setup - The function configures the onenand flash.
+ * @onenand_base: Onenand base address
+ *
+ * @return int: Currently always returning zero.
+ */
+static int sdp_onenand_setup(void __iomem *onenand_base, int freq)
+{
+ /* Onenand setup does nothing at present */
+ return 0;
+}
+/**
+ * sdp3430_flash_init - Identify devices connected to GPMC and register.
+ *
+ * @return - void.
+ */
+void __init sdp3430_flash_init(void)
+{
+ u8 cs = 0;
+ u8 onenandcs = GPMC_CS_NUM + 1;
+
+ /* Configure start address and size of NOR device */
+ if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0)) {
+ sdp_nor_resource.start = FLASH_BASE_SDPV2;
+ sdp_nor_resource.end = FLASH_BASE_SDPV2
+ + FLASH_SIZE_SDPV2 - 1;
+ } else {
+ sdp_nor_resource.start = FLASH_BASE_SDPV1;
+ sdp_nor_resource.end = FLASH_BASE_SDPV1
+ + FLASH_SIZE_SDPV1 - 1;
+ }
+
+ if (platform_device_register(&sdp_nor_device) < 0)
+ printk(KERN_ERR "Unable to register NOR device\n");
+
+ while (cs < GPMC_CS_NUM) {
+ u32 ret = 0;
+ ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
+
+ /*
+ * xloader/Uboot would have programmed the oneNAND
+ * base address for us This is a ugly hack. The proper
+ * way of doing this is to pass the setup of u-boot up
+ * to kernel using kernel params - something on the
+ * lines of machineID. Check if oneNAND is configured
+ */
+ if ((ret & 0x3F) == (ONENAND_MAP >> 24))
+ onenandcs = cs;
+ cs++;
+ }
+ if (onenandcs > GPMC_CS_NUM) {
+ printk(KERN_INFO "OneNAND: Unable to find configuration "
+ " in GPMC\n ");
+ return;
+ }
+
+ if (onenandcs < GPMC_CS_NUM) {
+ sdp_onenand_data.cs = onenandcs;
+ if (platform_device_register(&sdp_onenand_device) < 0)
+ printk(KERN_ERR "Unable to register OneNAND device\n");
+ }
+}
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * linux/arch/arm/mach-omap2/board-3430sdp.c
+ *
+ * Copyright (C) 2007 Texas Instruments
+ *
+ * Modified from mach-omap2/board-generic.c
+ *
+ * Initial code: Syed Mohammed Khasim
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+
- #include <asm/arch/mcspi.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/board.h>
- #include <asm/arch/usb-musb.h>
- #include <asm/arch/usb-ehci.h>
- #include <asm/arch/hsmmc.h>
- #include <asm/arch/common.h>
- #include <asm/arch/keypad.h>
- #include <asm/arch/dma.h>
- #include <asm/arch/gpmc.h>
++#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
- #include <asm/arch/control.h>
++#include <mach/mcspi.h>
++#include <mach/gpio.h>
++#include <mach/mux.h>
++#include <mach/board.h>
++#include <mach/usb-musb.h>
++#include <mach/usb-ehci.h>
++#include <mach/hsmmc.h>
++#include <mach/common.h>
++#include <mach/keypad.h>
++#include <mach/dma.h>
++#include <mach/gpmc.h>
+#include <linux/i2c/twl4030-rtc.h>
+
+#include <asm/io.h>
+#include <asm/delay.h>
++#include <mach/control.h>
+
+#include "sdram-qimonda-hyb18m512160af-6.h"
+
+#define SDP3430_SMC91X_CS 3
+
+#define ENABLE_VAUX3_DEDICATED 0x03
+#define ENABLE_VAUX3_DEV_GRP 0x20
+
+
+#define TWL4030_MSECURE_GPIO 22
+
+static struct resource sdp3430_smc91x_resources[] = {
+ [0] = {
+ .start = OMAP34XX_ETHR_START,
+ .end = OMAP34XX_ETHR_START + SZ_4K,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
+ },
+};
+
+static struct platform_device sdp3430_smc91x_device = {
+ .name = "smc91x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(sdp3430_smc91x_resources),
+ .resource = sdp3430_smc91x_resources,
+};
+
+static int sdp3430_keymap[] = {
+ KEY(0, 0, KEY_LEFT),
+ KEY(0, 1, KEY_RIGHT),
+ KEY(0, 2, KEY_A),
+ KEY(0, 3, KEY_B),
+ KEY(0, 4, KEY_C),
+ KEY(1, 0, KEY_DOWN),
+ KEY(1, 1, KEY_UP),
+ KEY(1, 2, KEY_E),
+ KEY(1, 3, KEY_F),
+ KEY(1, 4, KEY_G),
+ KEY(2, 0, KEY_ENTER),
+ KEY(2, 1, KEY_I),
+ KEY(2, 2, KEY_J),
+ KEY(2, 3, KEY_K),
+ KEY(2, 4, KEY_3),
+ KEY(3, 0, KEY_M),
+ KEY(3, 1, KEY_N),
+ KEY(3, 2, KEY_O),
+ KEY(3, 3, KEY_P),
+ KEY(3, 4, KEY_Q),
+ KEY(4, 0, KEY_R),
+ KEY(4, 1, KEY_4),
+ KEY(4, 2, KEY_T),
+ KEY(4, 3, KEY_U),
+ KEY(4, 4, KEY_D),
+ KEY(5, 0, KEY_V),
+ KEY(5, 1, KEY_W),
+ KEY(5, 2, KEY_L),
+ KEY(5, 3, KEY_S),
+ KEY(5, 4, KEY_H),
+ 0
+};
+
+static struct omap_kp_platform_data sdp3430_kp_data = {
+ .rows = 5,
+ .cols = 6,
+ .keymap = sdp3430_keymap,
+ .keymapsize = ARRAY_SIZE(sdp3430_keymap),
+ .rep = 1,
+};
+
+static struct platform_device sdp3430_kp_device = {
+ .name = "omap_twl4030keypad",
+ .id = -1,
+ .dev = {
+ .platform_data = &sdp3430_kp_data,
+ },
+};
+
+static int ts_gpio;
+
+#ifdef CONFIG_RTC_DRV_TWL4030
+static int twl4030_rtc_init(void)
+{
+ int ret = 0;
+
+ /* 3430ES2.0 doesn't have msecure/gpio-22 line connected to T2 */
+ if (is_device_type_gp() && is_sil_rev_less_than(OMAP3430_REV_ES2_0)) {
+ u32 msecure_pad_config_reg = omap_ctrl_base_get() + 0xA3C;
+ int mux_mask = 0x04;
+ u16 tmp;
+
+ ret = omap_request_gpio(TWL4030_MSECURE_GPIO);
+ if (ret < 0) {
+ printk(KERN_ERR "twl4030_rtc_init: can't"
+ "reserve GPIO:%d !\n", TWL4030_MSECURE_GPIO);
+ goto out;
+ }
+ /*
+ * TWL4030 will be in secure mode if msecure line from OMAP
+ * is low. Make msecure line high in order to change the
+ * TWL4030 RTC time and calender registers.
+ */
+ omap_set_gpio_direction(TWL4030_MSECURE_GPIO, 0);
+
+ tmp = omap_readw(msecure_pad_config_reg);
+ tmp &= 0xF8; /* To enable mux mode 03/04 = GPIO_RTC */
+ tmp |= mux_mask;/* To enable mux mode 03/04 = GPIO_RTC */
+ omap_writew(tmp, msecure_pad_config_reg);
+
+ omap_set_gpio_dataout(TWL4030_MSECURE_GPIO, 1);
+ }
+out:
+ return ret;
+}
+
+static void twl4030_rtc_exit(void)
+{
+ if (is_device_type_gp() &&
+ is_sil_rev_less_than(OMAP3430_REV_ES2_0)) {
+ omap_free_gpio(TWL4030_MSECURE_GPIO);
+ }
+}
+
+static struct twl4030rtc_platform_data sdp3430_twl4030rtc_data = {
+ .init = &twl4030_rtc_init,
+ .exit = &twl4030_rtc_exit,
+};
+
+static struct platform_device sdp3430_twl4030rtc_device = {
+ .name = "twl4030_rtc",
+ .id = -1,
+ .dev = {
+ .platform_data = &sdp3430_twl4030rtc_data,
+ },
+};
+#endif
+
+/**
+ * @brief ads7846_dev_init : Requests & sets GPIO line for pen-irq
+ *
+ * @return - void. If request gpio fails then Flag KERN_ERR.
+ */
+static void ads7846_dev_init(void)
+{
+ if (omap_request_gpio(ts_gpio) < 0) {
+ printk(KERN_ERR "can't get ads746 pen down GPIO\n");
+ return;
+ }
+
+ omap_set_gpio_direction(ts_gpio, 1);
+
+ omap_set_gpio_debounce(ts_gpio, 1);
+ omap_set_gpio_debounce_time(ts_gpio, 0xa);
+}
+
+static int ads7846_get_pendown_state(void)
+{
+ return !omap_get_gpio_datain(ts_gpio);
+}
+
+/*
+ * This enable(1)/disable(0) the voltage for TS: uses twl4030 calls
+ */
+static int ads7846_vaux_control(int vaux_cntrl)
+{
+ int ret = 0;
+
+#ifdef CONFIG_TWL4030_CORE
+ /* check for return value of ldo_use: if success it returns 0 */
+ if (vaux_cntrl == VAUX_ENABLE) {
+ if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ ENABLE_VAUX3_DEDICATED, TWL4030_VAUX3_DEDICATED))
+ return -EIO;
+ if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ ENABLE_VAUX3_DEV_GRP, TWL4030_VAUX3_DEV_GRP))
+ return -EIO;
+ } else if (vaux_cntrl == VAUX_DISABLE) {
+ if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ 0x00, TWL4030_VAUX3_DEDICATED))
+ return -EIO;
+ if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ 0x00, TWL4030_VAUX3_DEV_GRP))
+ return -EIO;
+ }
+#else
+ ret = -EIO;
+#endif
+ return ret;
+}
+
+static struct ads7846_platform_data tsc2046_config __initdata = {
+ .get_pendown_state = ads7846_get_pendown_state,
+ .keep_vref_on = 1,
+ .vaux_control = ads7846_vaux_control,
+};
+
+
+static struct omap2_mcspi_device_config tsc2046_mcspi_config = {
+ .turbo_mode = 0,
+ .single_channel = 1, /* 0: slave, 1: master */
+};
+
+static struct spi_board_info sdp3430_spi_board_info[] __initdata = {
+ [0] = {
+ /*
+ * TSC2046 operates at a max freqency of 2MHz, so
+ * operate slightly below at 1.5MHz
+ */
+ .modalias = "ads7846",
+ .bus_num = 1,
+ .chip_select = 0,
+ .max_speed_hz = 1500000,
+ .controller_data = &tsc2046_mcspi_config,
+ .irq = 0,
+ .platform_data = &tsc2046_config,
+ },
+};
+
+static struct platform_device sdp3430_lcd_device = {
+ .name = "sdp2430_lcd",
+ .id = -1,
+};
+
+static struct platform_device *sdp3430_devices[] __initdata = {
+ &sdp3430_smc91x_device,
+ &sdp3430_kp_device,
+ &sdp3430_lcd_device,
+#ifdef CONFIG_RTC_DRV_TWL4030
+ &sdp3430_twl4030rtc_device,
+#endif
+};
+
+static inline void __init sdp3430_init_smc91x(void)
+{
+ int eth_cs;
+ unsigned long cs_mem_base;
+ int eth_gpio = 0;
+
+ eth_cs = SDP3430_SMC91X_CS;
+
+ if (gpmc_cs_request(eth_cs, SZ_16M, &cs_mem_base) < 0) {
+ printk(KERN_ERR "Failed to request GPMC mem for smc91x\n");
+ return;
+ }
+
+ sdp3430_smc91x_resources[0].start = cs_mem_base + 0x0;
+ sdp3430_smc91x_resources[0].end = cs_mem_base + 0xf;
+ udelay(100);
+
+ if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0))
+ eth_gpio = OMAP34XX_ETHR_GPIO_IRQ_SDPV2;
+ else
+ eth_gpio = OMAP34XX_ETHR_GPIO_IRQ_SDPV1;
+
+ sdp3430_smc91x_resources[1].start = OMAP_GPIO_IRQ(eth_gpio);
+
+ if (omap_request_gpio(eth_gpio) < 0) {
+ printk(KERN_ERR "Failed to request GPIO%d for smc91x IRQ\n",
+ eth_gpio);
+ return;
+ }
+ omap_set_gpio_direction(eth_gpio, 1);
+}
+
+static void __init omap_3430sdp_init_irq(void)
+{
+ omap2_init_common_hw(hyb18m512160af6_sdrc_params);
+ omap_init_irq();
+ omap_gpio_init();
+ sdp3430_init_smc91x();
+}
+
+static struct omap_uart_config sdp3430_uart_config __initdata = {
+ .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
+};
+
+static struct omap_lcd_config sdp3430_lcd_config __initdata = {
+ .ctrl_name = "internal",
+};
+
+static struct omap_mmc_config sdp3430_mmc_config __initdata = {
+ .mmc [0] = {
+ .enabled = 1,
+ .wire4 = 1,
+ },
+};
+
+static struct omap_board_config_kernel sdp3430_config[] __initdata = {
+ { OMAP_TAG_UART, &sdp3430_uart_config },
+ {OMAP_TAG_LCD, &sdp3430_lcd_config},
+ {OMAP_TAG_MMC, &sdp3430_mmc_config },
+};
+
+static int __init omap3430_i2c_init(void)
+{
+ omap_register_i2c_bus(1, 2600, NULL, 0);
+ omap_register_i2c_bus(2, 400, NULL, 0);
+ omap_register_i2c_bus(3, 400, NULL, 0);
+ return 0;
+}
+
+extern void __init sdp3430_flash_init(void);
+
+static void __init omap_3430sdp_init(void)
+{
+ platform_add_devices(sdp3430_devices, ARRAY_SIZE(sdp3430_devices));
+ omap_board_config = sdp3430_config;
+ omap_board_config_size = ARRAY_SIZE(sdp3430_config);
+ if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0))
+ ts_gpio = OMAP34XX_TS_GPIO_IRQ_SDPV2;
+ else
+ ts_gpio = OMAP34XX_TS_GPIO_IRQ_SDPV1;
+ sdp3430_spi_board_info[0].irq = OMAP_GPIO_IRQ(ts_gpio);
+ spi_register_board_info(sdp3430_spi_board_info,
+ ARRAY_SIZE(sdp3430_spi_board_info));
+ ads7846_dev_init();
+ sdp3430_flash_init();
+ twl4030_bci_battery_init();
+ omap_serial_init();
+ usb_musb_init();
+ usb_ehci_init();
+ hsmmc_init();
+}
+
+static void __init omap_3430sdp_map_io(void)
+{
+ omap2_set_globals_343x();
+ omap2_map_common_io();
+}
+arch_initcall(omap3430_i2c_init);
+
+MACHINE_START(OMAP_3430SDP, "OMAP3430 3430SDP board")
+ /* Maintainer: Syed Khasim - Texas Instruments Inc */
+ .phys_io = 0x48000000,
+ .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc,
+ .boot_params = 0x80000100,
+ .map_io = omap_3430sdp_map_io,
+ .init_irq = omap_3430sdp_init_irq,
+ .init_machine = omap_3430sdp_init,
+ .timer = &omap_timer,
+MACHINE_END
--- /dev/null
- #include <asm/arch/gpio.h>
- #include <asm/arch/mux.h>
+/*
+ * linux/arch/arm/mach-omap2/board-apollon-keys.c
+ *
+ * Copyright (C) 2007 Samsung Electronics
+ * Author: Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/gpio_keys.h>
+
++#include <mach/gpio.h>
++#include <mach/mux.h>
+
+#define SW_ENTER_GPIO16 16
+#define SW_UP_GPIO17 17
+#define SW_DOWN_GPIO58 58
+#define SW_LEFT_GPIO95 95
+#define SW_RIGHT_GPIO96 96
+#define SW_ESC_GPIO97 97
+
+static struct gpio_keys_button apollon_gpio_keys_buttons[] = {
+ [0] = {
+ .code = KEY_ENTER,
+ .gpio = SW_ENTER_GPIO16,
+ .desc = "enter sw",
+ },
+ [1] = {
+ .code = KEY_UP,
+ .gpio = SW_UP_GPIO17,
+ .desc = "up sw",
+ },
+ [2] = {
+ .code = KEY_DOWN,
+ .gpio = SW_DOWN_GPIO58,
+ .desc = "down sw",
+ },
+ [3] = {
+ .code = KEY_LEFT,
+ .gpio = SW_LEFT_GPIO95,
+ .desc = "left sw",
+ },
+ [4] = {
+ .code = KEY_RIGHT,
+ .gpio = SW_RIGHT_GPIO96,
+ .desc = "right sw",
+ },
+ [5] = {
+ .code = KEY_ESC,
+ .gpio = SW_ESC_GPIO97,
+ .desc = "esc sw",
+ },
+};
+
+static struct gpio_keys_platform_data apollon_gpio_keys = {
+ .buttons = apollon_gpio_keys_buttons,
+ .nbuttons = ARRAY_SIZE(apollon_gpio_keys_buttons),
+};
+
+static struct platform_device apollon_gpio_keys_device = {
+ .name = "gpio-keys",
+ .id = -1,
+ .dev = {
+ .platform_data = &apollon_gpio_keys,
+ },
+};
+
+static void __init apollon_sw_init(void)
+{
+ /* Enter SW - Y11 */
+ omap_cfg_reg(Y11_242X_GPIO16);
+ /* Up SW - AA12 */
+ omap_cfg_reg(AA12_242X_GPIO17);
+ /* Down SW - AA8 */
+ omap_cfg_reg(AA8_242X_GPIO58);
+
+ if (apollon_plus()) {
+ /* Left SW - P18 */
+ omap_cfg_reg(P18_24XX_GPIO95);
+ /* Right SW - M18 */
+ omap_cfg_reg(M18_24XX_GPIO96);
+ /* Esc SW - L14 */
+ omap_cfg_reg(L14_24XX_GPIO97);
+ } else
+ apollon_gpio_keys.nbuttons = 3;
+}
+
+static int __init omap_apollon_keys_init(void)
+{
+ apollon_sw_init();
+
+ return platform_device_register(&apollon_gpio_keys_device);
+}
+
+arch_initcall(omap_apollon_keys_init);
--- /dev/null
- #include <asm/arch/gpio.h>
- #include <asm/arch/mmc.h>
+/*
+ * linux/arch/arm/mach-omap2/board-apollon-mmc.c
+ *
+ * Copyright (C) 2005-2007 Samsung Electronics
+ * Author: Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * 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/kernel.h>
+
++#include <mach/gpio.h>
++#include <mach/mmc.h>
+
+#ifdef CONFIG_MMC_OMAP
+
+static struct device *mmc_device;
+
+static int apollon_mmc_set_power(struct device *dev, int slot, int power_on,
+ int vdd)
+{
+#ifdef CONFIG_MMC_DEBUG
+ dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1,
+ power_on ? "on" : "off", vdd);
+#endif
+ if (slot != 0) {
+ dev_err(dev, "No such slot %d\n", slot + 1);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int apollon_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode)
+{
+#ifdef CONFIG_MMC_DEBUG
+ dev_dbg(dev, "Set slot %d bus_mode %s\n", slot + 1,
+ bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull");
+#endif
+ if (slot != 0) {
+ dev_err(dev, "No such slot %d\n", slot + 1);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int apollon_mmc_late_init(struct device *dev)
+{
+ mmc_device = dev;
+
+ return 0;
+}
+
+static void apollon_mmc_cleanup(struct device *dev)
+{
+}
+
+static struct omap_mmc_platform_data apollon_mmc_data = {
+ .nr_slots = 1,
+ .switch_slot = NULL,
+ .init = apollon_mmc_late_init,
+ .cleanup = apollon_mmc_cleanup,
+ .slots[0] = {
+ .set_power = apollon_mmc_set_power,
+ .set_bus_mode = apollon_mmc_set_bus_mode,
+ .get_ro = NULL,
+ .get_cover_state = NULL,
+ .ocr_mask = MMC_VDD_30_31 | MMC_VDD_31_32 |
+ MMC_VDD_32_33 | MMC_VDD_33_34,
+ .name = "mmcblk",
+ },
+};
+
+void __init apollon_mmc_init(void)
+{
+ omap_set_mmc_info(1, &apollon_mmc_data);
+}
+
+#else /* !CONFIG_MMC_OMAP */
+
+void __init apollon_mmc_init(void)
+{
+}
+
+#endif /* CONFIG_MMC_OMAP */
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/clk.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc210x.h>
- #include <asm/hardware.h>
+#include <asm/io.h>
+ #include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/led.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/usb.h>
- #include <asm/arch/board.h>
- #include <asm/arch/common.h>
- #include <asm/arch/gpmc.h>
+ #include <mach/gpio.h>
+ #include <mach/led.h>
+ #include <mach/mux.h>
+ #include <mach/usb.h>
+ #include <mach/board.h>
+ #include <mach/common.h>
+ #include <mach/gpmc.h>
-#include <mach/control.h>
/* LED & Switch macros */
#define LED0_GPIO13 13
--- /dev/null
- #include <asm/arch/mmc.h>
+/*
+ * linux/arch/arm/mach-omap2/board-h4-mmc.c
+ *
+ * Copyright (C) 2007 Instituto Nokia de Tecnologia - INdT
+ * Authors: David Cohen <david.cohen@indt.org.br>
+ * Carlos Eduardo Aguiar <carlos.aguiar@indt.org.br>
+ *
+ * This code is based on linux/arch/arm/mach-omap2/board-n800-mmc.c, which is:
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Juha Yrjola
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
++#include <mach/mmc.h>
+
+#include <asm/mach-types.h>
+#include <linux/delay.h>
+#include <linux/i2c/menelaus.h>
+
+#ifdef CONFIG_MMC_OMAP
+
+/* Bit mask for slots detection interrupts */
+#define SD1_CD_ST (1 << 0)
+#define SD2_CD_ST (1 << 1)
+
+static int slot1_cover_open;
+static int slot2_cover_open;
+static struct device *mmc_device;
+
+/*
+ * VMMC --> slot 1
+ * VDCDC3_APE, VMCS2_APE --> slot 2
+ */
+
+static int h4_mmc_switch_slot(struct device *dev, int slot)
+{
+ int r = 0;
+
+#ifdef CONFIG_MMC_DEBUG
+ dev_dbg(dev, "Choose slot %d\n", slot + 1);
+#endif
+ if (slot == 0) {
+ r = menelaus_enable_slot(2, 0);
+ r |= menelaus_enable_slot(1, 1);
+ } else {
+ r = menelaus_enable_slot(1, 0);
+ r |= menelaus_enable_slot(2, 1);
+ }
+
+ return r ? -ENODEV : 0;
+}
+
+static int h4_mmc_set_power(struct device *dev, int slot, int power_on,
+ int vdd)
+{
+ int mV = 0;
+
+#ifdef CONFIG_MMC_DEBUG
+ dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1,
+ power_on ? "on" : "off", vdd);
+#endif
+ if (slot == 0) {
+ if (!power_on)
+ return menelaus_set_vmmc(3000);
+ switch (1 << vdd) {
+ case MMC_VDD_33_34:
+ case MMC_VDD_32_33:
+ case MMC_VDD_31_32:
+ mV = 3100;
+ break;
+ case MMC_VDD_30_31:
+ mV = 3000;
+ break;
+ case MMC_VDD_28_29:
+ mV = 2800;
+ break;
+ case MMC_VDD_165_195:
+ mV = 1850;
+ break;
+ default:
+ BUG();
+ }
+ return menelaus_set_vmmc(mV);
+ } else {
+ if (!power_on)
+ return menelaus_set_vdcdc(3, 3000);
+ switch (1 << vdd) {
+ case MMC_VDD_33_34:
+ case MMC_VDD_32_33:
+ mV = 3300;
+ break;
+ case MMC_VDD_30_31:
+ case MMC_VDD_29_30:
+ mV = 3000;
+ break;
+ case MMC_VDD_28_29:
+ case MMC_VDD_27_28:
+ mV = 2800;
+ break;
+ case MMC_VDD_24_25:
+ case MMC_VDD_23_24:
+ mV = 2400;
+ break;
+ case MMC_VDD_22_23:
+ case MMC_VDD_21_22:
+ mV = 2200;
+ break;
+ case MMC_VDD_20_21:
+ mV = 2000;
+ break;
+ case MMC_VDD_165_195:
+ mV = 1800;
+ break;
+ default:
+ BUG();
+ }
+ return menelaus_set_vdcdc(3, mV);
+ }
+ return 0;
+}
+
+static int h4_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode)
+{
+ int r = 0;
+
+#ifdef CONFIG_MMC_DEBUG
+ dev_dbg(dev, "Set slot %d bus mode %s\n", slot + 1,
+ bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull");
+#endif
+ BUG_ON(slot != 0 && slot != 1);
+ slot++;
+ switch (bus_mode) {
+ case MMC_BUSMODE_OPENDRAIN:
+ r = menelaus_set_mmc_opendrain(slot, 1);
+ break;
+ case MMC_BUSMODE_PUSHPULL:
+ r = menelaus_set_mmc_opendrain(slot, 0);
+ break;
+ default:
+ BUG();
+ }
+ if (r != 0 && printk_ratelimit()) {
+ dev_err(dev, "MMC: unable to set bus mode for slot %d\n",
+ slot);
+ }
+ return r;
+}
+
+static int h4_mmc_slot1_cover_state(struct device *dev, int slot)
+{
+ BUG_ON(slot != 0);
+ return slot1_cover_open;
+}
+
+static int h4_mmc_slot2_cover_state(struct device *dev, int slot)
+{
+ BUG_ON(slot != 1);
+ return slot2_cover_open;
+}
+
+static void h4_mmc_slot_callback(void *data, u8 card_mask)
+{
+ int cover_open;
+
+ cover_open = (card_mask & SD1_CD_ST) ? 0 : 1;
+ if (cover_open != slot1_cover_open) {
+ slot1_cover_open = cover_open;
+ omap_mmc_notify_cover_event(mmc_device, 0, slot1_cover_open);
+ }
+
+ cover_open = (card_mask & SD2_CD_ST) ? 0 : 1;
+ if (cover_open != slot2_cover_open) {
+ slot2_cover_open = cover_open;
+ omap_mmc_notify_cover_event(mmc_device, 1, slot2_cover_open);
+ }
+}
+
+static int h4_mmc_late_init(struct device *dev)
+{
+ int r;
+
+ mmc_device = dev;
+
+ r = menelaus_set_mmc_slot(1, 0, 0, 1);
+ if (r < 0)
+ goto out;
+ r = menelaus_set_mmc_slot(2, 0, 0, 1);
+ if (r < 0)
+ goto out;
+
+ r = menelaus_get_slot_pin_states();
+ if (r < 0)
+ goto out;
+
+ if (r & SD1_CD_ST)
+ slot1_cover_open = 1;
+ else
+ slot1_cover_open = 0;
+
+ /* Slot pin bits seem to be inversed until first swith change,
+ * but just for slot 2
+ */
+ if ((r == 0xf) || (r == (0xf & ~SD2_CD_ST)))
+ r = ~r;
+
+ if (r & SD2_CD_ST)
+ slot2_cover_open = 1;
+ else
+ slot2_cover_open = 0;
+
+ r = menelaus_register_mmc_callback(h4_mmc_slot_callback, NULL);
+
+out:
+ return r;
+}
+
+static void h4_mmc_cleanup(struct device *dev)
+{
+ menelaus_unregister_mmc_callback();
+}
+
+static struct omap_mmc_platform_data h4_mmc_data = {
+ .nr_slots = 2,
+ .switch_slot = h4_mmc_switch_slot,
+ .init = h4_mmc_late_init,
+ .cleanup = h4_mmc_cleanup,
+ .slots[0] = {
+ .set_power = h4_mmc_set_power,
+ .set_bus_mode = h4_mmc_set_bus_mode,
+ .get_ro = NULL,
+ .get_cover_state= h4_mmc_slot1_cover_state,
+ .ocr_mask = MMC_VDD_165_195 |
+ MMC_VDD_28_29 | MMC_VDD_30_31 |
+ MMC_VDD_32_33 | MMC_VDD_33_34,
+ .name = "slot1",
+ },
+ .slots[1] = {
+ .set_power = h4_mmc_set_power,
+ .set_bus_mode = h4_mmc_set_bus_mode,
+ .get_ro = NULL,
+ .get_cover_state= h4_mmc_slot2_cover_state,
+ .ocr_mask = MMC_VDD_165_195 | MMC_VDD_20_21 |
+ MMC_VDD_21_22 | MMC_VDD_22_23 | MMC_VDD_23_24 |
+ MMC_VDD_24_25 | MMC_VDD_27_28 | MMC_VDD_28_29 |
+ MMC_VDD_29_30 | MMC_VDD_30_31 | MMC_VDD_32_33 |
+ MMC_VDD_33_34,
+ .name = "slot2",
+ },
+};
+
+void __init h4_mmc_init(void)
+{
+ omap_set_mmc_info(1, &h4_mmc_data);
+}
+
+#else
+
+void __init h4_mmc_init(void)
+{
+}
+
+#endif
+
#include <linux/input.h>
#include <linux/err.h>
#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/i2c/menelaus.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc210x.h>
+
+#include <media/v4l2-int-device.h>
- #include <asm/hardware.h>
+ #include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/flash.h>
- #include <asm/arch/control.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/gpioexpander.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/usb.h>
- #include <asm/arch/irda.h>
- #include <asm/arch/board.h>
- #include <asm/arch/common.h>
- #include <asm/arch/keypad.h>
- #include <asm/arch/dma.h>
- #include <asm/arch/gpmc.h>
+ #include <mach/control.h>
+ #include <mach/gpio.h>
+ #include <mach/gpioexpander.h>
+ #include <mach/mux.h>
+ #include <mach/usb.h>
+ #include <mach/irda.h>
+ #include <mach/board.h>
+ #include <mach/common.h>
+ #include <mach/keypad.h>
-#include <mach/menelaus.h>
+ #include <mach/dma.h>
+ #include <mach/gpmc.h>
#include <asm/io.h>
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * linux/arch/arm/mach-omap2/board-ldp.c
+ *
+ * Copyright (C) 2008 Texas Instruments Inc.
+ * Nishant Kamat <nskamat@ti.com>
+ *
+ * Modified from mach-omap2/board-3430sdp.c
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl4030-rtc.h>
+
- #include <asm/arch/mcspi.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/board.h>
- #include <asm/arch/common.h>
- #include <asm/arch/gpmc.h>
- #include <asm/arch/hsmmc.h>
- #include <asm/arch/usb-musb.h>
++#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
- #include <asm/arch/control.h>
++#include <mach/mcspi.h>
++#include <mach/gpio.h>
++#include <mach/board.h>
++#include <mach/common.h>
++#include <mach/gpmc.h>
++#include <mach/hsmmc.h>
++#include <mach/usb-musb.h>
+
+#include <asm/io.h>
+#include <asm/delay.h>
++#include <mach/control.h>
+
+#define ENABLE_VAUX1_DEDICATED 0x03
+#define ENABLE_VAUX1_DEV_GRP 0x20
+
+#define TWL4030_MSECURE_GPIO 22
+
+static int ts_gpio;
+
+#ifdef CONFIG_RTC_DRV_TWL4030
+static int twl4030_rtc_init(void)
+{
+ int ret = 0;
+
+ /* 3430ES2.0 doesn't have msecure/gpio-22 line connected to T2 */
+ if (is_device_type_gp() && is_sil_rev_less_than(OMAP3430_REV_ES2_0)) {
+ u32 msecure_pad_config_reg = omap_ctrl_base_get() + 0xA3C;
+ int mux_mask = 0x04;
+ u16 tmp;
+
+ ret = omap_request_gpio(TWL4030_MSECURE_GPIO);
+ if (ret < 0) {
+ printk(KERN_ERR "twl4030_rtc_init: can't"
+ "reserve GPIO:%d !\n", TWL4030_MSECURE_GPIO);
+ goto out;
+ }
+ /*
+ * TWL4030 will be in secure mode if msecure line from OMAP
+ * is low. Make msecure line high in order to change the
+ * TWL4030 RTC time and calender registers.
+ */
+ omap_set_gpio_direction(TWL4030_MSECURE_GPIO, 0);
+
+ tmp = omap_readw(msecure_pad_config_reg);
+ tmp &= 0xF8; /* To enable mux mode 03/04 = GPIO_RTC */
+ tmp |= mux_mask;/* To enable mux mode 03/04 = GPIO_RTC */
+ omap_writew(tmp, msecure_pad_config_reg);
+
+ omap_set_gpio_dataout(TWL4030_MSECURE_GPIO, 1);
+ }
+out:
+ return ret;
+}
+
+static void twl4030_rtc_exit(void)
+{
+ omap_free_gpio(TWL4030_MSECURE_GPIO);
+}
+
+static struct twl4030rtc_platform_data ldp_twl4030rtc_data = {
+ .init = &twl4030_rtc_init,
+ .exit = &twl4030_rtc_exit,
+};
+
+static struct platform_device ldp_twl4030rtc_device = {
+ .name = "twl4030_rtc",
+ .id = -1,
+ .dev = {
+ .platform_data = &ldp_twl4030rtc_data,
+ },
+};
+#endif
+
+/**
+ * @brief ads7846_dev_init : Requests & sets GPIO line for pen-irq
+ *
+ * @return - void. If request gpio fails then Flag KERN_ERR.
+ */
+static void ads7846_dev_init(void)
+{
+ if (omap_request_gpio(ts_gpio) < 0) {
+ printk(KERN_ERR "can't get ads746 pen down GPIO\n");
+ return;
+ }
+
+ omap_set_gpio_direction(ts_gpio, 1);
+
+ omap_set_gpio_debounce(ts_gpio, 1);
+ omap_set_gpio_debounce_time(ts_gpio, 0xa);
+}
+
+static int ads7846_get_pendown_state(void)
+{
+ return !omap_get_gpio_datain(ts_gpio);
+}
+
+/*
+ * This enable(1)/disable(0) the voltage for TS: uses twl4030 calls
+ */
+static int ads7846_vaux_control(int vaux_cntrl)
+{
+ int ret = 0;
+
+#ifdef CONFIG_TWL4030_CORE
+ /* check for return value of ldo_use: if success it returns 0 */
+ if (vaux_cntrl == VAUX_ENABLE) {
+ if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ ENABLE_VAUX1_DEDICATED, TWL4030_VAUX1_DEDICATED))
+ return -EIO;
+ if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ ENABLE_VAUX1_DEV_GRP, TWL4030_VAUX1_DEV_GRP))
+ return -EIO;
+ } else if (vaux_cntrl == VAUX_DISABLE) {
+ if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ 0x00, TWL4030_VAUX1_DEDICATED))
+ return -EIO;
+ if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ 0x00, TWL4030_VAUX1_DEV_GRP))
+ return -EIO;
+ }
+#else
+ ret = -EIO;
+#endif
+ return ret;
+}
+
+static struct ads7846_platform_data tsc2046_config __initdata = {
+ .get_pendown_state = ads7846_get_pendown_state,
+ .keep_vref_on = 1,
+ .vaux_control = ads7846_vaux_control,
+};
+
+
+static struct omap2_mcspi_device_config tsc2046_mcspi_config = {
+ .turbo_mode = 0,
+ .single_channel = 1, /* 0: slave, 1: master */
+};
+
+static struct spi_board_info ldp_spi_board_info[] __initdata = {
+ [0] = {
+ /*
+ * TSC2046 operates at a max freqency of 2MHz, so
+ * operate slightly below at 1.5MHz
+ */
+ .modalias = "ads7846",
+ .bus_num = 1,
+ .chip_select = 0,
+ .max_speed_hz = 1500000,
+ .controller_data = &tsc2046_mcspi_config,
+ .irq = 0,
+ .platform_data = &tsc2046_config,
+ },
+};
+
+static struct platform_device *ldp_devices[] __initdata = {
+#ifdef CONFIG_RTC_DRV_TWL4030
+ &ldp_twl4030rtc_device,
+#endif
+};
+
+static void __init omap_ldp_init_irq(void)
+{
+ omap2_init_common_hw(NULL);
+ omap_init_irq();
+ omap_gpio_init();
+}
+
+static struct omap_uart_config ldp_uart_config __initdata = {
+ .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
+};
+
+static struct omap_mmc_config ldp_mmc_config __initdata = {
+ .mmc [0] = {
+ .enabled = 1,
+ .wire4 = 1,
+ },
+};
+
+static struct omap_board_config_kernel ldp_config[] __initdata = {
+ { OMAP_TAG_UART, &ldp_uart_config },
+ { OMAP_TAG_MMC, &ldp_mmc_config },
+};
+
+static int __init omap_i2c_init(void)
+{
+ omap_register_i2c_bus(1, 2600, NULL, 0);
+ omap_register_i2c_bus(2, 400, NULL, 0);
+ omap_register_i2c_bus(3, 400, NULL, 0);
+ return 0;
+}
+
+static void __init omap_ldp_init(void)
+{
+ platform_add_devices(ldp_devices, ARRAY_SIZE(ldp_devices));
+ omap_board_config = ldp_config;
+ omap_board_config_size = ARRAY_SIZE(ldp_config);
+ ts_gpio = 54;
+ ldp_spi_board_info[0].irq = OMAP_GPIO_IRQ(ts_gpio);
+ spi_register_board_info(ldp_spi_board_info,
+ ARRAY_SIZE(ldp_spi_board_info));
+ ads7846_dev_init();
+ omap_serial_init();
+ usb_musb_init();
+ hsmmc_init();
+}
+
+static void __init omap_ldp_map_io(void)
+{
+ omap2_set_globals_343x();
+ omap2_map_common_io();
+}
+arch_initcall(omap_i2c_init);
+
+MACHINE_START(OMAP_LDP, "OMAP LDP board")
+ .phys_io = 0x48000000,
+ .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc,
+ .boot_params = 0x80000100,
+ .map_io = omap_ldp_map_io,
+ .init_irq = omap_ldp_init_irq,
+ .init_machine = omap_ldp_init,
+ .timer = &omap_timer,
+MACHINE_END
--- /dev/null
- #include <asm/arch/eac.h>
+/*
+ * linux/arch/arm/mach-omap2/board-n800-audio.c
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Contact: Juha Yrjola
+ * Jarkko Nikula <jarkko.nikula@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/spi/tsc2301.h>
+
+#include <asm/io.h>
- #include <asm/arch/dsp_common.h>
++#include <mach/eac.h>
+
++#include <mach/dsp_common.h>
+
+#if defined(CONFIG_SPI_TSC2301_AUDIO) && defined(CONFIG_SND_OMAP24XX_EAC)
+#define AUDIO_ENABLED
+
+static struct clk *sys_clkout2;
+static struct clk *sys_clkout2_src;
+static struct clk *func96m_clk;
+static struct device *eac_device;
+static struct device *tsc2301_device;
+
+static int enable_audio;
+static int audio_ok;
+static spinlock_t audio_lock;
+
+/*
+ * Leaving EAC and sys_clkout2 pins multiplexed to those subsystems results
+ * in about 2 mA extra current leak when audios are powered down. The
+ * workaround is to multiplex them to protected mode (with pull-ups enabled)
+ * whenever audio is not being used.
+ */
+static int eac_mux_disabled = 0;
+static int clkout2_mux_disabled = 0;
+static u32 saved_mux[2];
+
+#define MUX_EAC_IOP2V(x) (__force void __iomem *)io_p2v(x)
+
+static void n800_enable_eac_mux(void)
+{
+ if (!eac_mux_disabled)
+ return;
+ __raw_writel(saved_mux[1], MUX_EAC_IOP2V(0x48000124));
+ eac_mux_disabled = 0;
+}
+
+static void n800_disable_eac_mux(void)
+{
+ if (eac_mux_disabled) {
+ WARN_ON(eac_mux_disabled);
+ return;
+ }
+ saved_mux[1] = __raw_readl(MUX_EAC_IOP2V(0x48000124));
+ __raw_writel(0x1f1f1f1f, MUX_EAC_IOP2V(0x48000124));
+ eac_mux_disabled = 1;
+}
+
+static void n800_enable_clkout2_mux(void)
+{
+ if (!clkout2_mux_disabled)
+ return;
+ __raw_writel(saved_mux[0], MUX_EAC_IOP2V(0x480000e8));
+ clkout2_mux_disabled = 0;
+}
+
+static void n800_disable_clkout2_mux(void)
+{
+ u32 l;
+
+ if (clkout2_mux_disabled) {
+ WARN_ON(clkout2_mux_disabled);
+ return;
+ }
+ saved_mux[0] = __raw_readl(MUX_EAC_IOP2V(0x480000e8));
+ l = saved_mux[0] & ~0xff;
+ l |= 0x1f;
+ __raw_writel(l, MUX_EAC_IOP2V(0x480000e8));
+ clkout2_mux_disabled = 1;
+}
+
+static int n800_eac_enable_ext_clocks(struct device *dev)
+{
+ BUG_ON(tsc2301_device == NULL);
+ n800_enable_eac_mux();
+ tsc2301_mixer_enable_mclk(tsc2301_device);
+
+ return 0;
+}
+
+static void n800_eac_disable_ext_clocks(struct device *dev)
+{
+ BUG_ON(tsc2301_device == NULL);
+ tsc2301_mixer_disable_mclk(tsc2301_device);
+ n800_disable_eac_mux();
+}
+
+static int n800_audio_set_power(void *pdata, int dac, int adc)
+{
+ BUG_ON(pdata != tsc2301_device);
+ tsc2301_mixer_set_power(tsc2301_device, dac, adc);
+
+ return 0;
+}
+
+static int n800_audio_register_controls(void *pdata, struct snd_card *card)
+{
+ BUG_ON(pdata != tsc2301_device);
+ return tsc2301_mixer_register_controls(tsc2301_device, card);
+}
+
+static struct eac_codec n800_eac_codec = {
+ .mclk_src = EAC_MCLK_EXT_2x12288000,
+ .codec_mode = EAC_CODEC_I2S_MASTER,
+ .codec_conf.i2s.polarity_changed_mode = 0,
+ .codec_conf.i2s.sync_delay_enable = 0,
+ .default_rate = 48000,
+ .set_power = n800_audio_set_power,
+ .register_controls = n800_audio_register_controls,
+ .short_name = "TSC2301",
+};
+
+static int n800_register_codec(void)
+{
+ int r, do_enable = 0;
+ unsigned long flags;
+
+ n800_eac_codec.private_data = tsc2301_device;
+ r = eac_register_codec(eac_device, &n800_eac_codec);
+ if (r < 0)
+ return r;
+ spin_lock_irqsave(&audio_lock, flags);
+ audio_ok = 1;
+ if (enable_audio)
+ do_enable = 1;
+ spin_unlock_irqrestore(&audio_lock, flags);
+ if (do_enable)
+ eac_set_mode(eac_device, 1, 1);
+ return 0;
+}
+
+static void n800_unregister_codec(void)
+{
+ audio_ok = 0;
+ eac_unregister_codec(eac_device);
+ eac_set_mode(eac_device, 0, 0);
+}
+
+static int n800_eac_init(struct device *dev)
+{
+ int r;
+
+ BUG_ON(eac_device != NULL);
+ eac_device = dev;
+ if (tsc2301_device != NULL) {
+ r = n800_register_codec();
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static void n800_eac_cleanup(struct device *dev)
+{
+ eac_device = NULL;
+ if (tsc2301_device != NULL)
+ n800_unregister_codec();
+}
+
+static int n800_codec_get_clocks(struct device *dev)
+{
+ sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
+ if (IS_ERR(sys_clkout2_src)) {
+ dev_err(dev, "Could not get sys_clkout2_src clock\n");
+ return -ENODEV;
+ }
+ sys_clkout2 = clk_get(dev, "sys_clkout2");
+ if (IS_ERR(sys_clkout2)) {
+ dev_err(dev, "Could not get sys_clkout2 clock\n");
+ clk_put(sys_clkout2_src);
+ return -ENODEV;
+ }
+ /* configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
+ * 96 MHz as its parent in order to get 12 MHz */
+ func96m_clk = clk_get(dev, "func_96m_ck");
+ if (IS_ERR(func96m_clk)) {
+ dev_err(dev, "Could not get func 96M clock\n");
+ clk_put(sys_clkout2);
+ clk_put(sys_clkout2_src);
+ return -ENODEV;
+ }
+
+ clk_set_parent(sys_clkout2_src, func96m_clk);
+ clk_set_rate(sys_clkout2, 12000000);
+
+ return 0;
+}
+
+static void n800_codec_put_clocks(struct device *dev)
+{
+ clk_put(func96m_clk);
+ clk_put(sys_clkout2);
+ clk_put(sys_clkout2_src);
+}
+
+static int n800_codec_enable_clock(struct device *dev)
+{
+ n800_enable_clkout2_mux();
+ return clk_enable(sys_clkout2);
+}
+
+static void n800_codec_disable_clock(struct device *dev)
+{
+ clk_disable(sys_clkout2);
+ n800_disable_clkout2_mux();
+}
+
+static int n800_codec_init(struct device *dev)
+{
+ int r;
+
+ BUG_ON(tsc2301_device != NULL);
+ tsc2301_device = dev;
+ if ((r = n800_codec_get_clocks(dev)) < 0)
+ return r;
+ if (eac_device != NULL) {
+ r = n800_register_codec();
+ if (r < 0) {
+ n800_codec_put_clocks(dev);
+ return r;
+ }
+ }
+ return 0;
+}
+
+static void n800_codec_cleanup(struct device *dev)
+{
+ tsc2301_device = NULL;
+ if (eac_device != NULL)
+ n800_unregister_codec();
+ n800_codec_put_clocks(dev);
+}
+
+static struct eac_platform_data n800_eac_data = {
+ .init = n800_eac_init,
+ .cleanup = n800_eac_cleanup,
+ .enable_ext_clocks = n800_eac_enable_ext_clocks,
+ .disable_ext_clocks = n800_eac_disable_ext_clocks,
+};
+
+static const struct tsc2301_mixer_gpio n800_mixer_gpios[] = {
+ {
+ .name = "Headset Amplifier",
+ .gpio = 1,
+ .deactivate_on_pd = 1,
+ }, {
+ .name = "Speaker Amplifier",
+ .gpio = 2,
+ .def_enable = 1,
+ .deactivate_on_pd = 1,
+ }, {
+ .name = "Headset Mic Select",
+ .gpio = 3,
+ }
+};
+
+static struct platform_device retu_headset_device = {
+ .name = "retu-headset",
+ .id = -1,
+ .dev = {
+ .release = NULL,
+ },
+};
+
+void __init n800_audio_init(struct tsc2301_platform_data *tc)
+{
+ spin_lock_init(&audio_lock);
+
+ if (platform_device_register(&retu_headset_device) < 0)
+ return;
+ omap_init_eac(&n800_eac_data);
+
+ tc->pll_pdc = 7;
+ tc->pll_a = 7;
+ tc->pll_n = 9;
+ tc->pll_output = 1;
+ tc->mclk_ratio = TSC2301_MCLK_256xFS;
+ tc->i2s_sample_rate = TSC2301_I2S_SR_48000;
+ tc->i2s_format = TSC2301_I2S_FORMAT0;
+ tc->power_down_blocks = TSC2301_REG_PD_MISC_MOPD;
+ tc->mixer_gpios = n800_mixer_gpios;
+ tc->n_mixer_gpios = ARRAY_SIZE(n800_mixer_gpios);
+ tc->codec_init = n800_codec_init;
+ tc->codec_cleanup = n800_codec_cleanup;
+ tc->enable_clock = n800_codec_enable_clock;
+ tc->disable_clock = n800_codec_disable_clock;
+}
+
+#else
+
+void __init n800_audio_init(struct tsc2301_platform_data *tc)
+{
+}
+
+#endif
+
+#ifdef CONFIG_OMAP_DSP
+
+int n800_audio_enable(struct dsp_kfunc_device *kdev, int stage)
+{
+#ifdef AUDIO_ENABLED
+ unsigned long flags;
+ int do_enable = 0;
+
+ spin_lock_irqsave(&audio_lock, flags);
+
+ pr_debug("DSP power up request (audio codec %sinitialized)\n",
+ audio_ok ? "" : "not ");
+
+ if (enable_audio)
+ goto out;
+ enable_audio = 1;
+ if (audio_ok)
+ do_enable = 1;
+out:
+ spin_unlock_irqrestore(&audio_lock, flags);
+ if (do_enable)
+ eac_set_mode(eac_device, 1, 1);
+#endif
+ return 0;
+}
+
+int n800_audio_disable(struct dsp_kfunc_device *kdev, int stage)
+{
+#ifdef AUDIO_ENABLED
+ unsigned long flags;
+ int do_disable = 0;
+
+ spin_lock_irqsave(&audio_lock, flags);
+
+ pr_debug("DSP power down request (audio codec %sinitialized)\n",
+ audio_ok ? "" : "not ");
+
+ if (!enable_audio)
+ goto out;
+ enable_audio = 0;
+ if (audio_ok)
+ do_disable = 1;
+out:
+ spin_unlock_irqrestore(&audio_lock, flags);
+ if (do_disable)
+ eac_set_mode(eac_device, 0, 0);
+#endif
+ return 0;
+}
+
+#endif /* CONFIG_OMAP_DSP */
--- /dev/null
- #include <asm/arch/board.h>
+/*
+ * Nokia N800 platform-specific data for Bluetooth
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
++#include <mach/board.h>
+
+static struct platform_device n800_bt_device = {
+ .name = "hci_h4p",
+ .id = -1,
+ .num_resources = 0,
+};
+
+void __init n800_bt_init(void)
+{
+ const struct omap_bluetooth_config *bt_config;
+
+ bt_config = (void *) omap_get_config(OMAP_TAG_NOKIA_BT,
+ struct omap_bluetooth_config);
+ n800_bt_device.dev.platform_data = (void *) bt_config;
+ if (platform_device_register(&n800_bt_device) < 0)
+ BUG();
+}
+
--- /dev/null
- #include <asm/arch/gpio.h>
- #include <asm/arch/board.h>
+/*
+ * arch/arm/mach-omap2/board-n800-camera.c
+ *
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/i2c/menelaus.h>
+
+#include <media/v4l2-int-device.h>
+
+#include <asm/mach-types.h>
+
++#include <mach/gpio.h>
++#include <mach/board.h>
+
+#include <../drivers/cbus/retu.h>
+#include <../drivers/media/video/tcm825x.h>
+
+#include "board-n800.h"
+
+#if defined (CONFIG_VIDEO_TCM825X) || defined (CONFIG_VIDEO_TCM825X_MODULE)
+
+#define OMAP24XX_CAMERA_JAM_HACK
+
+#ifdef OMAP24XX_CAMERA_JAM_HACK
+/*
+ * We don't need to check every pixel to assume that the frame is
+ * corrupt and the sensor is jammed. CHECK_X and CHECK_Y are the
+ * number of u32s to check per line / row, plus there are two lines in
+ * the bottom of the frame.
+ */
+#define CHECK_X 8
+#define CHECK_Y 6
+/*
+ * Start checking after this many frames since resetting the sensor.
+ * Sometimes the first frame(s) is(/are) black which could trigger
+ * unwanted reset(s).
+ */
+#define JAM_CHECK_AFTER 3
+/*
+ * If the sensor is quickly brought into bright conditions from dark,
+ * it may temporarily be saturated, leaving out the normal background
+ * noise. This many saturated frames may go through before the sensor
+ * is considered jammed.
+ */
+#define SATURATED_MAX 30
+#endif
+
+#define N800_CAM_SENSOR_RESET_GPIO 53
+
+static int sensor_okay;
+#ifdef OMAP24XX_CAMERA_JAM_HACK
+static int frames_after_reset;
+static int saturated_count;
+#endif
+
+const static struct tcm825x_reg tcm825x_regs_n800[] = {
+ /* initial settings for 2.5 V */
+ {0x00, 0x03}, {0x03, 0x29}, {0xaa, 0x2a}, {0xc0, 0x2b},
+ {0x10, 0x2c}, {0x4c, 0x2d}, {0x9c, 0x3f},
+
+ /* main settings */
+ {0x00, 0x00}, {0x30, 0x01}, {0x0e, 0x02}, /* initial */
+ {0x0f, 0x04}, {0x02, 0x05}, {0x0d, 0x06}, {0xc0, 0x07},
+ {0x38, 0x08}, {0x50, 0x09}, {0x80, 0x0a}, {0x40, 0x0b},
+ {0x40, 0x0c}, {0x00, 0x0d}, {0x04, 0x0e}, {0x04, 0x0f},
+ {0x22, 0x10}, {0x96, 0x11}, {0xf0, 0x12}, {0x08, 0x13},
+ {0x08, 0x14}, {0x30, 0x15}, {0x30, 0x16}, {0x01, 0x17},
+ {0x40, 0x18}, {0x87, 0x19}, {0x2b, 0x1a}, {0x84, 0x1b},
+ {0x52, 0x1c}, {0x44, 0x1d}, {0x68, 0x1e}, {0x00, 0x1f},
+ {0x00, 0x20}, {0x01, 0x21}, {0x27, 0x22}, {0x40, 0x23},
+ {0x27, 0x24}, {0x5f, 0x25}, {0x00, 0x26}, {0x16, 0x27},
+ {0x23, 0x28}, /* initial */ /* initial */ /* initial */
+ /* initial */ /* initial */ {0x00, 0x2e}, {0x00, 0x2f},
+ {0x00, 0x30}, {0x00, 0x31}, {0x00, 0x32}, {0x00, 0x33},
+ {0x00, 0x34}, {0x00, 0x35}, {0x00, 0x36}, {0x00, 0x37},
+ {0x00, 0x38}, {0x8c, 0x39}, {0xc8, 0x3A}, {0x80, 0x3b},
+ {0x00, 0x3c}, {0x17, 0x3d}, {0x85, 0x3e}, /* initial */
+ {0xa0, 0x40}, {0x00, 0x41}, {0x00, 0x42}, {0x00, 0x43},
+ {0x08, 0x44}, {0x12, 0x45}, {0x00, 0x46}, {0x20, 0x47},
+ {0x30, 0x48}, {0x18, 0x49}, {0x20, 0x4a}, {0x4d, 0x4b},
+ {0x0c, 0x4c}, {0xe0, 0x4d}, {0x20, 0x4e}, {0x89, 0x4f},
+ {0x21, 0x50}, {0x80, 0x51}, {0x02, 0x52}, {0x00, 0x53},
+ {0x30, 0x54}, {0x90, 0x55}, {0x40, 0x56}, {0x06, 0x57},
+ {0x0f, 0x58}, {0x23, 0x59}, {0x08, 0x5A}, {0x04, 0x5b},
+ {0x08, 0x5c}, {0x08, 0x5d}, {0x08, 0x5e}, {0x08, 0x5f},
+ {TCM825X_VAL_TERM, TCM825X_REG_TERM}
+};
+
+const static struct tcm825x_reg tcm825x_regs_n810[] = {
+ /* initial settings for 2.5 V */
+ {0x00, 0x03}, {0x03, 0x29}, {0xaa, 0x2a}, {0xc0, 0x2b},
+ {0x10, 0x2c}, {0x4c, 0x2d}, {0x9c, 0x3f},
+
+ /* main settings */
+ {0x00, 0x00}, {0x30, 0x01}, {0x0e, 0x02}, /* initial */
+ {0xcf, 0x04}, {0x02, 0x05}, {0x0d, 0x06}, {0xc0, 0x07},
+ {0x38, 0x08}, {0x50, 0x09}, {0x80, 0x0a}, {0x40, 0x0b},
+ {0x40, 0x0c}, {0x00, 0x0d}, {0x04, 0x0e}, {0x04, 0x0f},
+ {0x22, 0x10}, {0x96, 0x11}, {0xf0, 0x12}, {0x08, 0x13},
+ {0x08, 0x14}, {0x30, 0x15}, {0x30, 0x16}, {0x01, 0x17},
+ {0x40, 0x18}, {0x87, 0x19}, {0x2b, 0x1a}, {0x84, 0x1b},
+ {0x52, 0x1c}, {0x44, 0x1d}, {0x68, 0x1e}, {0x00, 0x1f},
+ {0x00, 0x20}, {0x01, 0x21}, {0x27, 0x22}, {0x40, 0x23},
+ {0x27, 0x24}, {0x5f, 0x25}, {0x00, 0x26}, {0x16, 0x27},
+ {0x23, 0x28}, /* initial */ /* initial */ /* initial */
+ /* initial */ /* initial */ {0x00, 0x2e}, {0x00, 0x2f},
+ {0x00, 0x30}, {0x00, 0x31}, {0x00, 0x32}, {0x00, 0x33},
+ {0x00, 0x34}, {0x00, 0x35}, {0x00, 0x36}, {0x00, 0x37},
+ {0x00, 0x38}, {0x8c, 0x39}, {0xc8, 0x3A}, {0x80, 0x3b},
+ {0x00, 0x3c}, {0x17, 0x3d}, {0x85, 0x3e}, /* initial */
+ {0xa0, 0x40}, {0x00, 0x41}, {0x00, 0x42}, {0x00, 0x43},
+ {0x08, 0x44}, {0x12, 0x45}, {0x00, 0x46}, {0x20, 0x47},
+ {0x30, 0x48}, {0x18, 0x49}, {0x20, 0x4a}, {0x4d, 0x4b},
+ {0x0c, 0x4c}, {0xe0, 0x4d}, {0x20, 0x4e}, {0x89, 0x4f},
+ {0x21, 0x50}, {0x80, 0x51}, {0x02, 0x52}, {0x00, 0x53},
+ {0x30, 0x54}, {0x90, 0x55}, {0x40, 0x56}, {0x06, 0x57},
+ {0x0f, 0x58}, {0x23, 0x59}, {0x08, 0x5A}, {0x04, 0x5b},
+ {0x08, 0x5c}, {0x08, 0x5d}, {0x08, 0x5e}, {0x08, 0x5f},
+ {TCM825X_VAL_TERM, TCM825X_REG_TERM}
+};
+
+static int tcm825x_is_okay(void)
+{
+ return sensor_okay;
+}
+
+/*
+ * VSIM1 --> CAM_IOVDD --> IOVDD (1.8 V)
+ */
+static int tcm825x_power_on(void)
+{
+ int ret;
+
+ /* Set VMEM to 1.5V and VIO to 2.5V */
+ ret = menelaus_set_vmem(1500);
+ if (ret < 0) {
+ /* Try once more, it seems the sensor power up causes
+ * some problems on the I2C bus. */
+ ret = menelaus_set_vmem(1500);
+ if (ret < 0)
+ return ret;
+ }
+ msleep(1);
+
+ ret = menelaus_set_vio(2500);
+ if (ret < 0)
+ return ret;
+
+ /* Set VSim1 on */
+ retu_write_reg(RETU_REG_CTRL_SET, 0x0080);
+ msleep(1);
+
+ omap_set_gpio_dataout(N800_CAM_SENSOR_RESET_GPIO, 1);
+ msleep(1);
+
+ saturated_count = 0;
+ frames_after_reset = 0;
+
+ return 0;
+}
+
+static int tcm825x_power_off(void)
+{
+ int ret;
+
+ omap_set_gpio_dataout(N800_CAM_SENSOR_RESET_GPIO, 0);
+ msleep(1);
+
+ /* Set VSim1 off */
+ retu_write_reg(RETU_REG_CTRL_CLR, 0x0080);
+ msleep(1);
+
+ /* Set VIO_MODE to off */
+ ret = menelaus_set_vio(0);
+ if (ret < 0)
+ return ret;
+ msleep(1);
+
+ /* Set VMEM_MODE to off */
+ ret = menelaus_set_vmem(0);
+ if (ret < 0)
+ return ret;
+ msleep(1);
+
+ return 0;
+}
+
+static int tcm825x_power_set(int power)
+{
+ BUG_ON(!sensor_okay);
+
+ if (power)
+ return tcm825x_power_on();
+ else
+ return tcm825x_power_off();
+}
+
+static const struct tcm825x_reg *tcm825x_default_regs(void)
+{
+ if (machine_is_nokia_n810())
+ return tcm825x_regs_n810;
+
+ return tcm825x_regs_n800;
+}
+
+#ifdef OMAP24XX_CAMERA_JAM_HACK
+/*
+ * Check for jammed sensor, in which case all horizontal lines are
+ * equal. Handle also case where sensor could be saturated awhile in
+ * case of rapid increase of brightness.
+ */
+static int tcm825x_needs_reset(struct v4l2_int_device *s, void *buf,
+ struct v4l2_pix_format *pix)
+{
+ int i, j;
+ uint32_t xor, xor2;
+ uint32_t offset;
+ uint32_t dx_offset;
+ uint32_t saturated_pattern;
+ int is_saturated = 1;
+
+ switch (pix->pixelformat) {
+ default:
+ case V4L2_PIX_FMT_RGB565:
+ saturated_pattern = 0xffffffff; /* guess */
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ saturated_pattern = 0xe080e080;
+ break;
+ }
+
+ /* This won't work for height under 2 at all. */
+ if (pix->height < 2)
+ return 0;
+ /* Check that there is enough image data. */
+ if (pix->width * TCM825X_BYTES_PER_PIXEL < sizeof(uint32_t))
+ return 0;
+ /*
+ * Don't check for jamming immediately. Sometimes frames
+ * immediately after reset are black.
+ */
+ if (frames_after_reset < JAM_CHECK_AFTER) {
+ frames_after_reset++;
+ return 0;
+ }
+
+ dx_offset = ((pix->width - sizeof(uint32_t) / TCM825X_BYTES_PER_PIXEL)
+ * TCM825X_BYTES_PER_PIXEL) / (CHECK_X - 1);
+ dx_offset = dx_offset - dx_offset % TCM825X_BYTES_PER_PIXEL;
+ /*
+ * Check two lines in the bottom first. They're unlikely to be
+ * saturated and quick to check.
+ */
+ offset = (pix->height - 2) * pix->bytesperline;
+ xor = xor2 = 0;
+ for (j = 0; j < CHECK_X; j++) {
+ uint32_t *val = buf + offset;
+ uint32_t *val2 = buf + offset + pix->bytesperline;
+ xor ^= *val;
+ if (*val != saturated_pattern)
+ is_saturated = 0;
+ xor2 ^= *val2;
+ if (xor2 != xor) {
+ saturated_count = 0;
+ return 0;
+ }
+ offset += dx_offset;
+ }
+ /* Check the rest of the picture. */
+ offset = 0;
+ for (i = 0; i < CHECK_Y; i++) {
+ uint32_t offset2 = offset;
+ xor2 = 0;
+ for (j = 0; j < CHECK_X; j++) {
+ uint32_t *val = buf + offset2;
+ xor2 ^= *val;
+ offset2 += dx_offset;
+ }
+ if (xor2 != xor) {
+ saturated_count = 0;
+ return 0;
+ }
+ offset += pix->bytesperline * ((pix->height - 2) / CHECK_Y);
+ }
+
+ if (is_saturated && saturated_count++ < SATURATED_MAX)
+ return 0;
+
+ return -EIO;
+}
+#else
+static int tcm825x_needs_reset(struct v4l2_int_device *s, void *buf,
+ struct v4l2_pix_format *pix)
+{
+ return 0;
+}
+#endif
+
+static const struct v4l2_ifparm ifparm = {
+ .if_type = V4L2_IF_TYPE_BT656,
+ .u = {
+ .bt656 = {
+ .frame_start_on_rising_vs = 1,
+ .latch_clk_inv = 1,
+ .mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT,
+ .clock_min = TCM825X_XCLK_MIN,
+ .clock_max = TCM825X_XCLK_MAX,
+ },
+ },
+};
+
+static int tcm825x_ifparm(struct v4l2_ifparm *p)
+{
+ *p = ifparm;
+
+ return 0;
+}
+
+static int tcm825x_is_upside_down(void)
+{
+ return machine_is_nokia_n810();
+}
+
+const struct tcm825x_platform_data n800_tcm825x_platform_data = {
+ .is_okay = tcm825x_is_okay,
+ .power_set = tcm825x_power_set,
+ .default_regs = tcm825x_default_regs,
+ .needs_reset = tcm825x_needs_reset,
+ .ifparm = tcm825x_ifparm,
+ .is_upside_down = tcm825x_is_upside_down,
+};
+
+void __init n800_cam_init(void)
+{
+ int r;
+
+ r = omap_request_gpio(N800_CAM_SENSOR_RESET_GPIO);
+ if (r < 0) {
+ printk(KERN_WARNING "%s: failed to request gpio\n",
+ __func__);
+ return;
+ }
+
+ omap_set_gpio_dataout(N800_CAM_SENSOR_RESET_GPIO, 0);
+ omap_set_gpio_direction(N800_CAM_SENSOR_RESET_GPIO, 0);
+
+ sensor_okay = 1;
+}
+
+#else
+void __init n800_cam_init(void)
+{
+}
+
+#endif
--- /dev/null
- #include <asm/arch/clock.h>
- #include <asm/arch/board.h>
- #include <asm/arch/dsp_common.h>
+/*
+ * linux/arch/arm/mach-omap2/board-n800-dsp.c
+ *
+ * Copyright (C) 2006 Nokia Corporation.
+ *
+ * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
++#include <mach/clock.h>
++#include <mach/board.h>
++#include <mach/dsp_common.h>
+
+#if defined(CONFIG_OMAP_DSP)
+
+/*
+ * dsp peripheral device: AUDIO
+ */
+static struct dsp_kfunc_device n800_audio_device = {
+ .name = "audio",
+ .type = DSP_KFUNC_DEV_TYPE_AUDIO,
+ .enable = n800_audio_enable,
+ .disable = n800_audio_disable,
+};
+
+/*
+ * dsp peripheral device: TIMER
+ */
+static int dsp_timer_probe(struct dsp_kfunc_device *kdev, int stage)
+{
+ char clockname[20];
+
+ strcpy(clockname, kdev->name);
+ strcat(clockname, "_fck");
+
+ kdev->fck = clk_get(NULL, clockname);
+ if (IS_ERR(kdev->fck)) {
+ printk(KERN_ERR "couldn't acquire %s\n", clockname);
+ return PTR_ERR(kdev->fck);
+ }
+ pr_debug("%s probed successfully\n", clockname);
+
+ strcpy(clockname, kdev->name);
+ strcat(clockname, "_ick");
+ kdev->ick = clk_get(NULL, clockname);
+ if (IS_ERR(kdev->ick)) {
+ printk(KERN_ERR "couldn't acquire %s\n", clockname);
+ goto fail;
+ }
+ pr_debug("%s probed successfully\n", clockname);
+
+ return 0;
+ fail:
+ clk_put(kdev->fck);
+
+ return PTR_ERR(kdev->ick);
+}
+
+static int dsp_timer_remove(struct dsp_kfunc_device *kdev, int stage)
+{
+ clk_put(kdev->ick);
+ clk_put(kdev->fck);
+ pr_debug("%s removed successfully\n", kdev->name);
+ return 0;
+}
+
+static int dsp_timer_enable(struct dsp_kfunc_device *kdev, int stage)
+{
+ pr_debug("%s enabled(%d)\n", kdev->name, stage);
+
+ spin_lock(&kdev->lock);
+
+ if (kdev->enabled)
+ goto out;
+ kdev->enabled = 1;
+
+ clk_enable(kdev->fck);
+ clk_enable(kdev->ick);
+ out:
+ spin_unlock(&kdev->lock);
+
+ return 0;
+}
+
+static int dsp_timer_disable(struct dsp_kfunc_device *kdev, int stage)
+{
+ pr_debug("%s disabled(%d)\n", kdev->name, stage);
+
+ spin_lock(&kdev->lock);
+
+ if (kdev->enabled == 0)
+ goto out;
+ kdev->enabled = 0;
+
+ clk_disable(kdev->ick);
+ clk_disable(kdev->fck);
+ out:
+ spin_unlock(&kdev->lock);
+
+ return 0;
+}
+
+static struct dsp_kfunc_device n800_timer_device = {
+ .name = "gpt5",
+ .type = DSP_KFUNC_DEV_TYPE_COMMON,
+ .probe = dsp_timer_probe,
+ .remove = dsp_timer_remove,
+ .enable = dsp_timer_enable,
+ .disable = dsp_timer_disable,
+};
+
+static struct dsp_kfunc_device *n800_kfunc_dev[] = {
+ &n800_audio_device,
+ &n800_timer_device,
+};
+
+void __init n800_dsp_init(void)
+{
+ int i, ret;
+ struct dsp_kfunc_device **p = n800_kfunc_dev;
+
+ for (i = 0; i < ARRAY_SIZE(n800_kfunc_dev); i++) {
+ ret = dsp_kfunc_device_register(p[i]);
+ if (ret) {
+ printk(KERN_ERR
+ "KFUNC device registration failed: %s\n",
+ p[i]->name);
+ }
+ }
+}
+
+#else
+void __init n800_dsp_init(void) { }
+#endif /* CONFIG_OMAP_DSP */
--- /dev/null
- #include <asm/arch/onenand.h>
- #include <asm/arch/board.h>
- #include <asm/arch/gpmc.h>
+/*
+ * linux/arch/arm/mach-omap2/board-n800-flash.c
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Juha Yrjola
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <asm/mach/flash.h>
+#include <linux/mtd/onenand_regs.h>
+
+#include <asm/io.h>
++#include <mach/onenand.h>
++#include <mach/board.h>
++#include <mach/gpmc.h>
+
+struct mtd_partition n800_partitions[ONENAND_MAX_PARTITIONS];
+
+int n800_onenand_setup(void __iomem *, int freq);
+
+static struct omap_onenand_platform_data n800_onenand_data = {
+ .cs = 0,
+ .parts = n800_partitions,
+ .nr_parts = 0, /* filled later */
+ .onenand_setup = n800_onenand_setup,
+};
+
+static struct platform_device n800_onenand_device = {
+ .name = "omap2-onenand",
+ .id = -1,
+ .dev = {
+ .platform_data = &n800_onenand_data,
+ },
+};
+
+static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base)
+{
+ struct gpmc_timings t;
+
+ const int t_cer = 15;
+ const int t_avdp = 12;
+ const int t_aavdh = 7;
+ const int t_ce = 76;
+ const int t_aa = 76;
+ const int t_oe = 20;
+ const int t_cez = 20; /* max of t_cez, t_oez */
+ const int t_ds = 30;
+ const int t_wpl = 40;
+ const int t_wph = 30;
+
+ memset(&t, 0, sizeof(t));
+ t.sync_clk = 0;
+ t.cs_on = 0;
+ t.adv_on = 0;
+
+ /* Read */
+ t.adv_rd_off = gpmc_round_ns_to_ticks(max_t(int, t_avdp, t_cer));
+ t.oe_on = t.adv_rd_off + gpmc_round_ns_to_ticks(t_aavdh);
+ t.access = t.adv_on + gpmc_round_ns_to_ticks(t_aa);
+ t.access = max_t(int, t.access, t.cs_on + gpmc_round_ns_to_ticks(t_ce));
+ t.access = max_t(int, t.access, t.oe_on + gpmc_round_ns_to_ticks(t_oe));
+ t.oe_off = t.access + gpmc_round_ns_to_ticks(1);
+ t.cs_rd_off = t.oe_off;
+ t.rd_cycle = t.cs_rd_off + gpmc_round_ns_to_ticks(t_cez);
+
+ /* Write */
+ t.adv_wr_off = t.adv_rd_off;
+ t.we_on = t.oe_on;
+ if (cpu_is_omap34xx()) {
+ t.wr_data_mux_bus = t.we_on;
+ t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds);
+ }
+ t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl);
+ t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph);
+ t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez);
+
+ /* Configure GPMC for asynchronous read */
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1,
+ GPMC_CONFIG1_DEVICESIZE_16 |
+ GPMC_CONFIG1_MUXADDDATA);
+
+ return gpmc_cs_set_timings(cs, &t);
+}
+
+static unsigned short omap2_onenand_readw(void __iomem *addr)
+{
+ return readw(addr);
+}
+
+static void omap2_onenand_writew(unsigned short value, void __iomem *addr)
+{
+ writew(value, addr);
+}
+
+static void set_onenand_cfg(void __iomem *onenand_base, int latency,
+ int sync_write, int hf)
+{
+ u32 reg;
+
+ reg = omap2_onenand_readw(onenand_base + ONENAND_REG_SYS_CFG1);
+ reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9));
+ reg |= (latency << ONENAND_SYS_CFG1_BRL_SHIFT) |
+ ONENAND_SYS_CFG1_SYNC_READ |
+ ONENAND_SYS_CFG1_BL_16;
+ if (sync_write)
+ reg |= ONENAND_SYS_CFG1_SYNC_WRITE;
+ else
+ reg &= ~ONENAND_SYS_CFG1_SYNC_WRITE;
+ if (hf)
+ reg |= ONENAND_SYS_CFG1_HF;
+ else
+ reg &= ~ONENAND_SYS_CFG1_HF;
+ omap2_onenand_writew(reg, onenand_base + ONENAND_REG_SYS_CFG1);
+}
+
+static int omap2_onenand_set_sync_mode(int cs, void __iomem *onenand_base,
+ int freq)
+{
+ struct gpmc_timings t;
+ const int t_cer = 15;
+ const int t_avdp = 12;
+ const int t_cez = 20; /* max of t_cez, t_oez */
+ const int t_ds = 30;
+ const int t_wpl = 40;
+ const int t_wph = 30;
+ int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo;
+ int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency;
+ int err, ticks_cez, sync_write = 0, first_time = 0, hf = 0;
+ u32 reg;
+
+ if (!freq) {
+ /* Very first call freq is not known */
+ err = omap2_onenand_set_async_mode(cs, onenand_base);
+ if (err)
+ return err;
+ reg = omap2_onenand_readw(onenand_base +
+ ONENAND_REG_VERSION_ID);
+ switch ((reg >> 4) & 0xf) {
+ case 0:
+ freq = 40;
+ break;
+ case 1:
+ freq = 54;
+ break;
+ case 2:
+ freq = 66;
+ break;
+ case 3:
+ freq = 83;
+ break;
+ case 4:
+ freq = 104;
+ break;
+ default:
+ freq = 54;
+ break;
+ }
+ first_time = 1;
+ }
+
+ switch (freq) {
+ case 83:
+ min_gpmc_clk_period = 12; /* 83 MHz */
+ t_ces = 5;
+ t_avds = 4;
+ t_avdh = 2;
+ t_ach = 6;
+ t_aavdh = 6;
+ t_rdyo = 9;
+ if (cpu_is_omap34xx())
+ sync_write = 1;
+ break;
+ case 66:
+ min_gpmc_clk_period = 15; /* 66 MHz */
+ t_ces = 6;
+ t_avds = 5;
+ t_avdh = 2;
+ t_ach = 6;
+ t_aavdh = 6;
+ t_rdyo = 11;
+ if (cpu_is_omap34xx())
+ sync_write = 1;
+ break;
+ default:
+ min_gpmc_clk_period = 18; /* 54 MHz */
+ t_ces = 7;
+ t_avds = 7;
+ t_avdh = 7;
+ t_ach = 9;
+ t_aavdh = 7;
+ t_rdyo = 15;
+ break;
+ }
+
+ tick_ns = gpmc_ticks_to_ns(1);
+ div = gpmc_cs_calc_divider(cs, min_gpmc_clk_period);
+ gpmc_clk_ns = gpmc_ticks_to_ns(div);
+ if (gpmc_clk_ns < 15) /* >66Mhz */
+ hf = 1;
+ if (hf)
+ latency = 6;
+ else if (gpmc_clk_ns >= 25) /* 40 MHz*/
+ latency = 3;
+ else
+ latency = 4;
+
+ if (first_time)
+ set_onenand_cfg(onenand_base, latency, sync_write, hf);
+
+ if (div == 1) {
+ reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2);
+ reg |= (1 << 7);
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, reg);
+ reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG3);
+ reg |= (1 << 7);
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, reg);
+ reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG4);
+ reg |= (1 << 7);
+ reg |= (1 << 23);
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, reg);
+ } else {
+ reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2);
+ reg &= ~(1 << 7);
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, reg);
+ reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG3);
+ reg &= ~(1 << 7);
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, reg);
+ reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG4);
+ reg &= ~(1 << 7);
+ reg &= ~(1 << 23);
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, reg);
+ }
+
+ /* Set synchronous read timings */
+ memset(&t, 0, sizeof(t));
+ t.sync_clk = min_gpmc_clk_period;
+ t.cs_on = 0;
+ t.adv_on = 0;
+ fclk_offset_ns = gpmc_round_ns_to_ticks(max_t(int, t_ces, t_avds));
+ fclk_offset = gpmc_ns_to_ticks(fclk_offset_ns);
+ t.page_burst_access = gpmc_clk_ns;
+
+ /* Read */
+ t.adv_rd_off = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_avdh));
+ t.oe_on = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_ach));
+ t.access = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div);
+ t.oe_off = t.access + gpmc_round_ns_to_ticks(1);
+ t.cs_rd_off = t.oe_off;
+ ticks_cez = ((gpmc_ns_to_ticks(t_cez) + div - 1) / div) * div;
+ t.rd_cycle = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div +
+ ticks_cez);
+
+ /* Write */
+ if (sync_write) {
+ t.adv_wr_off = t.adv_rd_off;
+ t.we_on = 0;
+ t.we_off = t.cs_rd_off;
+ t.cs_wr_off = t.cs_rd_off;
+ t.wr_cycle = t.rd_cycle;
+ if (cpu_is_omap34xx()) {
+ t.wr_data_mux_bus = gpmc_ticks_to_ns(fclk_offset +
+ gpmc_ns_to_ticks(min_gpmc_clk_period +
+ t_rdyo));
+ t.wr_access = t.access;
+ }
+ } else {
+ t.adv_wr_off = gpmc_round_ns_to_ticks(max_t(int, t_avdp, t_cer));
+ t.we_on = t.adv_wr_off + gpmc_round_ns_to_ticks(t_aavdh);
+ t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl);
+ t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph);
+ t.wr_cycle = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez);
+ if (cpu_is_omap34xx()) {
+ t.wr_data_mux_bus = t.we_on;
+ t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds);
+ }
+ }
+
+ /* Configure GPMC for synchronous read */
+ gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1,
+ GPMC_CONFIG1_WRAPBURST_SUPP |
+ GPMC_CONFIG1_READMULTIPLE_SUPP |
+ GPMC_CONFIG1_READTYPE_SYNC |
+ (sync_write ? GPMC_CONFIG1_WRITEMULTIPLE_SUPP : 0) |
+ (sync_write ? GPMC_CONFIG1_WRITETYPE_SYNC : 0) |
+ GPMC_CONFIG1_CLKACTIVATIONTIME(fclk_offset) |
+ GPMC_CONFIG1_PAGE_LEN(2) |
+ (cpu_is_omap34xx() ? 0 :
+ (GPMC_CONFIG1_WAIT_READ_MON |
+ GPMC_CONFIG1_WAIT_PIN_SEL(0))) |
+ GPMC_CONFIG1_DEVICESIZE_16 |
+ GPMC_CONFIG1_DEVICETYPE_NOR |
+ GPMC_CONFIG1_MUXADDDATA);
+
+ err = gpmc_cs_set_timings(cs, &t);
+ if (err)
+ return err;
+
+ set_onenand_cfg(onenand_base, latency, sync_write, hf);
+
+ return 0;
+}
+
+int n800_onenand_setup(void __iomem *onenand_base, int freq)
+{
+ struct omap_onenand_platform_data *datap = &n800_onenand_data;
+ struct device *dev = &n800_onenand_device.dev;
+
+ /* Set sync timings in GPMC */
+ if (omap2_onenand_set_sync_mode(datap->cs, onenand_base, freq) < 0) {
+ dev_err(dev, "Unable to set synchronous mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void __init n800_flash_init(void)
+{
+ const struct omap_partition_config *part;
+ int i = 0;
+
+ n800_onenand_data.gpio_irq = cpu_is_omap34xx() ? 65 : 26;
+
+ while ((part = omap_get_nr_config(OMAP_TAG_PARTITION,
+ struct omap_partition_config, i)) != NULL) {
+ struct mtd_partition *mpart;
+
+ mpart = n800_partitions + i;
+ mpart->name = (char *) part->name;
+ mpart->size = part->size;
+ mpart->offset = part->offset;
+ mpart->mask_flags = part->mask_flags;
+ i++;
+ if (i == ARRAY_SIZE(n800_partitions)) {
+ printk(KERN_ERR "Too many partitions supplied\n");
+ return;
+ }
+ }
+ n800_onenand_data.nr_parts = i;
+ if (platform_device_register(&n800_onenand_device) < 0) {
+ printk(KERN_ERR "Unable to register OneNAND device\n");
+ return;
+ }
+}
--- /dev/null
- #include <asm/arch/mmc.h>
- #include <asm/arch/gpio.h>
+/*
+ * linux/arch/arm/mach-omap2/board-n800-mmc.c
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Juha Yrjola
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
++#include <mach/mmc.h>
++#include <mach/gpio.h>
+
+#include <asm/mach-types.h>
+#include <linux/delay.h>
+#include <linux/i2c/menelaus.h>
+
+#ifdef CONFIG_MMC_OMAP
+
+static const int slot_switch_gpio = 96;
+
+static const int n810_slot2_pw_vddf = 23;
+static const int n810_slot2_pw_vdd = 9;
+
+static int slot1_cover_open;
+static int slot2_cover_open;
+static struct device *mmc_device;
+
+/*
+ * VMMC --> slot 1 (N800 & N810)
+ * VDCDC3_APE, VMCS2_APE --> slot 2 on N800
+ * GPIO96 --> Menelaus GPIO2
+ * GPIO23 --> controls slot2 VSD (N810 only)
+ * GPIO9 --> controls slot2 VIO_SD (N810 only)
+ */
+
+static int n800_mmc_switch_slot(struct device *dev, int slot)
+{
+#ifdef CONFIG_MMC_DEBUG
+ dev_dbg(dev, "Choose slot %d\n", slot + 1);
+#endif
+ if (slot == 0)
+ omap_set_gpio_dataout(slot_switch_gpio, 0);
+ else
+ omap_set_gpio_dataout(slot_switch_gpio, 1);
+ return 0;
+}
+
+static int n800_mmc_set_power_menelaus(struct device *dev, int slot,
+ int power_on, int vdd)
+{
+ int mV;
+
+#ifdef CONFIG_MMC_DEBUG
+ dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1,
+ power_on ? "on" : "off", vdd);
+#endif
+ if (slot == 0) {
+ if (!power_on)
+ return menelaus_set_vmmc(0);
+ switch (1 << vdd) {
+ case MMC_VDD_33_34:
+ case MMC_VDD_32_33:
+ case MMC_VDD_31_32:
+ mV = 3100;
+ break;
+ case MMC_VDD_30_31:
+ mV = 3000;
+ break;
+ case MMC_VDD_28_29:
+ mV = 2800;
+ break;
+ case MMC_VDD_165_195:
+ mV = 1850;
+ break;
+ default:
+ BUG();
+ }
+ return menelaus_set_vmmc(mV);
+ } else {
+ if (!power_on)
+ return menelaus_set_vdcdc(3, 0);
+ switch (1 << vdd) {
+ case MMC_VDD_33_34:
+ case MMC_VDD_32_33:
+ mV = 3300;
+ break;
+ case MMC_VDD_30_31:
+ case MMC_VDD_29_30:
+ mV = 3000;
+ break;
+ case MMC_VDD_28_29:
+ case MMC_VDD_27_28:
+ mV = 2800;
+ break;
+ case MMC_VDD_24_25:
+ case MMC_VDD_23_24:
+ mV = 2400;
+ break;
+ case MMC_VDD_22_23:
+ case MMC_VDD_21_22:
+ mV = 2200;
+ break;
+ case MMC_VDD_20_21:
+ mV = 2000;
+ break;
+ case MMC_VDD_165_195:
+ mV = 1800;
+ break;
+ default:
+ BUG();
+ }
+ return menelaus_set_vdcdc(3, mV);
+ }
+ return 0;
+}
+
+static void nokia_mmc_set_power_internal(struct device *dev,
+ int power_on)
+{
+ dev_dbg(dev, "Set internal slot power %s\n",
+ power_on ? "on" : "off");
+
+ if (power_on) {
+ omap_set_gpio_dataout(n810_slot2_pw_vddf, 1);
+ udelay(30);
+ omap_set_gpio_dataout(n810_slot2_pw_vdd, 1);
+ udelay(100);
+ } else {
+ omap_set_gpio_dataout(n810_slot2_pw_vdd, 0);
+ msleep(50);
+ omap_set_gpio_dataout(n810_slot2_pw_vddf, 0);
+ msleep(50);
+ }
+}
+
+static int n800_mmc_set_power(struct device *dev, int slot, int power_on,
+ int vdd)
+{
+ if (machine_is_nokia_n800() || slot == 0)
+ return n800_mmc_set_power_menelaus(dev, slot, power_on, vdd);
+
+ nokia_mmc_set_power_internal(dev, power_on);
+
+ return 0;
+}
+
+static int n800_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode)
+{
+ int r;
+
+ dev_dbg(dev, "Set slot %d bus mode %s\n", slot + 1,
+ bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull");
+ BUG_ON(slot != 0 && slot != 1);
+ slot++;
+ switch (bus_mode) {
+ case MMC_BUSMODE_OPENDRAIN:
+ r = menelaus_set_mmc_opendrain(slot, 1);
+ break;
+ case MMC_BUSMODE_PUSHPULL:
+ r = menelaus_set_mmc_opendrain(slot, 0);
+ break;
+ default:
+ BUG();
+ }
+ if (r != 0 && printk_ratelimit())
+ dev_err(dev, "MMC: unable to set bus mode for slot %d\n",
+ slot);
+ return r;
+}
+
+static int n800_mmc_get_cover_state(struct device *dev, int slot)
+{
+ slot++;
+ BUG_ON(slot != 1 && slot != 2);
+ if (slot == 1)
+ return slot1_cover_open;
+ else
+ return slot2_cover_open;
+}
+
+static void n800_mmc_callback(void *data, u8 card_mask)
+{
+ int bit, *openp, index;
+
+ if (machine_is_nokia_n800()) {
+ bit = 1 << 1;
+ openp = &slot2_cover_open;
+ index = 1;
+ } else {
+ bit = 1;
+ openp = &slot1_cover_open;
+ index = 0;
+ }
+
+ if (card_mask & bit)
+ *openp = 1;
+ else
+ *openp = 0;
+
+ omap_mmc_notify_cover_event(mmc_device, index, *openp);
+}
+
+void n800_mmc_slot1_cover_handler(void *arg, int closed_state)
+{
+ if (mmc_device == NULL)
+ return;
+
+ slot1_cover_open = !closed_state;
+ omap_mmc_notify_cover_event(mmc_device, 0, closed_state);
+}
+
+static int n800_mmc_late_init(struct device *dev)
+{
+ int r, bit, *openp;
+ int vs2sel;
+
+ mmc_device = dev;
+
+ r = menelaus_set_slot_sel(1);
+ if (r < 0)
+ return r;
+
+ if (machine_is_nokia_n800())
+ vs2sel = 0;
+ else
+ vs2sel = 2;
+
+ r = menelaus_set_mmc_slot(2, 0, vs2sel, 1);
+ if (r < 0)
+ return r;
+
+ n800_mmc_set_power(dev, 0, MMC_POWER_ON, 16); /* MMC_VDD_28_29 */
+ n800_mmc_set_power(dev, 1, MMC_POWER_ON, 16);
+
+ r = menelaus_set_mmc_slot(1, 1, 0, 1);
+ if (r < 0)
+ return r;
+ r = menelaus_set_mmc_slot(2, 1, vs2sel, 1);
+ if (r < 0)
+ return r;
+
+ r = menelaus_get_slot_pin_states();
+ if (r < 0)
+ return r;
+
+ if (machine_is_nokia_n800()) {
+ bit = 1 << 1;
+ openp = &slot2_cover_open;
+ } else {
+ bit = 1;
+ openp = &slot1_cover_open;
+ slot2_cover_open = 0;
+ }
+
+ /* All slot pin bits seem to be inversed until first swith change */
+ if (r == 0xf || r == (0xf & ~bit))
+ r = ~r;
+
+ if (r & bit)
+ *openp = 1;
+ else
+ *openp = 0;
+
+ r = menelaus_register_mmc_callback(n800_mmc_callback, NULL);
+
+ return r;
+}
+
+static void n800_mmc_shutdown(struct device *dev)
+{
+ int vs2sel;
+
+ if (machine_is_nokia_n800())
+ vs2sel = 0;
+ else
+ vs2sel = 2;
+
+ menelaus_set_mmc_slot(1, 0, 0, 0);
+ menelaus_set_mmc_slot(2, 0, vs2sel, 0);
+}
+
+static void n800_mmc_cleanup(struct device *dev)
+{
+ menelaus_unregister_mmc_callback();
+
+ omap_free_gpio(slot_switch_gpio);
+
+ if (machine_is_nokia_n810()) {
+ omap_free_gpio(n810_slot2_pw_vddf);
+ omap_free_gpio(n810_slot2_pw_vdd);
+ }
+}
+
+static struct omap_mmc_platform_data n800_mmc_data = {
+ .nr_slots = 2,
+ .switch_slot = n800_mmc_switch_slot,
+ .init = n800_mmc_late_init,
+ .cleanup = n800_mmc_cleanup,
+ .shutdown = n800_mmc_shutdown,
+ .max_freq = 24000000,
+ .slots[0] = {
+ .set_power = n800_mmc_set_power,
+ .set_bus_mode = n800_mmc_set_bus_mode,
+ .get_ro = NULL,
+ .get_cover_state= n800_mmc_get_cover_state,
+ .ocr_mask = MMC_VDD_165_195 | MMC_VDD_30_31 |
+ MMC_VDD_32_33 | MMC_VDD_33_34,
+ .name = "internal",
+ },
+ .slots[1] = {
+ .set_power = n800_mmc_set_power,
+ .set_bus_mode = n800_mmc_set_bus_mode,
+ .get_ro = NULL,
+ .get_cover_state= n800_mmc_get_cover_state,
+ .ocr_mask = MMC_VDD_165_195 | MMC_VDD_20_21 |
+ MMC_VDD_21_22 | MMC_VDD_22_23 | MMC_VDD_23_24 |
+ MMC_VDD_24_25 | MMC_VDD_27_28 | MMC_VDD_28_29 |
+ MMC_VDD_29_30 | MMC_VDD_30_31 | MMC_VDD_32_33 |
+ MMC_VDD_33_34,
+ .name = "external",
+ },
+};
+
+void __init n800_mmc_init(void)
+
+{
+ if (machine_is_nokia_n810()) {
+ n800_mmc_data.slots[0].name = "external";
+
+ /*
+ * Some Samsung Movinand chips do not like open-ended
+ * multi-block reads and fall to braind-dead state
+ * while doing so. Reducing the number of blocks in
+ * the transfer or delays in clock disable do not help
+ */
+ n800_mmc_data.slots[1].name = "internal";
+ n800_mmc_data.slots[1].ban_openended = 1;
+ }
+
+ omap_set_mmc_info(1, &n800_mmc_data);
+ if (omap_request_gpio(slot_switch_gpio) < 0)
+ BUG();
+ omap_set_gpio_dataout(slot_switch_gpio, 0);
+ omap_set_gpio_direction(slot_switch_gpio, 0);
+
+ if (machine_is_nokia_n810()) {
+ if (omap_request_gpio(n810_slot2_pw_vddf) < 0)
+ BUG();
+ omap_set_gpio_dataout(n810_slot2_pw_vddf, 0);
+ omap_set_gpio_direction(n810_slot2_pw_vddf, 0);
+
+ if (omap_request_gpio(n810_slot2_pw_vdd) < 0)
+ BUG();
+ omap_set_gpio_dataout(n810_slot2_pw_vdd, 0);
+ omap_set_gpio_direction(n810_slot2_pw_vdd, 0);
+ }
+}
+#else
+
+void __init n800_mmc_init(void)
+{
+}
+
+void n800_mmc_slot1_cover_handler(void *arg, int state)
+{
+}
+
+#endif
--- /dev/null
- #include <asm/arch/gpmc.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/pm.h>
+/*
+ * linux/arch/arm/mach-omap2/board-n800-usb.c
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Juha Yrjola
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/usb/musb.h>
++#include <mach/gpmc.h>
++#include <mach/gpio.h>
++#include <mach/pm.h>
+
+#define TUSB_ASYNC_CS 1
+#define TUSB_SYNC_CS 4
+#define GPIO_TUSB_INT 58
+#define GPIO_TUSB_ENABLE 0
+
+static int tusb_set_power(int state);
+static int tusb_set_clock(struct clk *osc_ck, int state);
+
+#if defined(CONFIG_USB_MUSB_OTG)
+# define BOARD_MODE MUSB_OTG
+#elif defined(CONFIG_USB_MUSB_PERIPHERAL)
+# define BOARD_MODE MUSB_PERIPHERAL
+#else /* defined(CONFIG_USB_MUSB_HOST) */
+# define BOARD_MODE MUSB_HOST
+#endif
+
+static struct musb_hdrc_platform_data tusb_data = {
+ .mode = BOARD_MODE,
+ .multipoint = 1,
+ .set_power = tusb_set_power,
+ .set_clock = tusb_set_clock,
+ .min_power = 25, /* x2 = 50 mA drawn from VBUS as peripheral */
+ .power = 100, /* Max 100 mA VBUS for host mode */
+ .clock = "osc_ck",
+};
+
+/*
+ * Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and
+ * 1.5 V voltage regulators of PM companion chip. Companion chip will then
+ * provide then PGOOD signal to TUSB6010 which will release it from reset.
+ */
+static int tusb_set_power(int state)
+{
+ int i, retval = 0;
+
+ if (state) {
+ omap_set_gpio_dataout(GPIO_TUSB_ENABLE, 1);
+ msleep(1);
+
+ /* Wait until TUSB6010 pulls INT pin down */
+ i = 100;
+ while (i && omap_get_gpio_datain(GPIO_TUSB_INT)) {
+ msleep(1);
+ i--;
+ }
+
+ if (!i) {
+ printk(KERN_ERR "tusb: powerup failed\n");
+ retval = -ENODEV;
+ }
+ } else {
+ omap_set_gpio_dataout(GPIO_TUSB_ENABLE, 0);
+ msleep(10);
+ }
+
+ return retval;
+}
+
+static int osc_ck_on;
+
+static int tusb_set_clock(struct clk *osc_ck, int state)
+{
+ if (state) {
+ if (osc_ck_on > 0)
+ return -ENODEV;
+
+ omap2_block_sleep();
+ clk_enable(osc_ck);
+ osc_ck_on = 1;
+ } else {
+ if (osc_ck_on == 0)
+ return -ENODEV;
+
+ clk_disable(osc_ck);
+ osc_ck_on = 0;
+ omap2_allow_sleep();
+ }
+
+ return 0;
+}
+
+void __init n800_usb_init(void)
+{
+ int ret = 0;
+ static char announce[] __initdata = KERN_INFO "TUSB 6010\n";
+
+ /* PM companion chip power control pin */
+ ret = omap_request_gpio(GPIO_TUSB_ENABLE);
+ if (ret != 0) {
+ printk(KERN_ERR "Could not get TUSB power GPIO%i\n",
+ GPIO_TUSB_ENABLE);
+ return;
+ }
+ omap_set_gpio_direction(GPIO_TUSB_ENABLE, 0);
+
+ tusb_set_power(0);
+
+ ret = tusb6010_setup_interface(&tusb_data, TUSB6010_REFCLK_19, 2,
+ TUSB_ASYNC_CS, TUSB_SYNC_CS,
+ GPIO_TUSB_INT, 0x3f);
+ if (ret != 0)
+ goto err;
+
+ printk(announce);
+
+ return;
+
+err:
+ omap_free_gpio(GPIO_TUSB_ENABLE);
+}
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * linux/arch/arm/mach-omap2/board-n800.c
+ *
+ * Copyright (C) 2005-2007 Nokia Corporation
+ * Author: Juha Yrjola <juha.yrjola@nokia.com>
+ *
+ * Modified from mach-omap2/board-generic.c
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc2301.h>
+#include <linux/spi/tsc2005.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
+#include <linux/i2c/lm8323.h>
+#include <linux/i2c/menelaus.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/usb.h>
- #include <asm/arch/board.h>
- #include <asm/arch/common.h>
- #include <asm/arch/mcspi.h>
- #include <asm/arch/lcd_mipid.h>
- #include <asm/arch/clock.h>
- #include <asm/arch/gpio-switch.h>
- #include <asm/arch/omapfb.h>
- #include <asm/arch/blizzard.h>
++#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
++#include <mach/gpio.h>
++#include <mach/usb.h>
++#include <mach/board.h>
++#include <mach/common.h>
++#include <mach/mcspi.h>
++#include <mach/lcd_mipid.h>
++#include <mach/clock.h>
++#include <mach/gpio-switch.h>
++#include <mach/omapfb.h>
++#include <mach/blizzard.h>
+
+#include <../drivers/cbus/tahvo.h>
+#include <../drivers/media/video/tcm825x.h>
+
+#define N800_BLIZZARD_POWERDOWN_GPIO 15
+#define N800_STI_GPIO 62
+#define N800_KEYB_IRQ_GPIO 109
+#define N800_DAV_IRQ_GPIO 103
+#define N800_TSC2301_RESET_GPIO 118
+
+#ifdef CONFIG_MACH_NOKIA_N810
+static s16 rx44_keymap[LM8323_KEYMAP_SIZE] = {
+ [0x01] = KEY_Q,
+ [0x02] = KEY_K,
+ [0x03] = KEY_O,
+ [0x04] = KEY_P,
+ [0x05] = KEY_BACKSPACE,
+ [0x06] = KEY_A,
+ [0x07] = KEY_S,
+ [0x08] = KEY_D,
+ [0x09] = KEY_F,
+ [0x0a] = KEY_G,
+ [0x0b] = KEY_H,
+ [0x0c] = KEY_J,
+
+ [0x11] = KEY_W,
+ [0x12] = KEY_F4,
+ [0x13] = KEY_L,
+ [0x14] = KEY_APOSTROPHE,
+ [0x16] = KEY_Z,
+ [0x17] = KEY_X,
+ [0x18] = KEY_C,
+ [0x19] = KEY_V,
+ [0x1a] = KEY_B,
+ [0x1b] = KEY_N,
+ [0x1c] = KEY_LEFTSHIFT, /* Actually, this is both shift keys */
+ [0x1f] = KEY_F7,
+
+ [0x21] = KEY_E,
+ [0x22] = KEY_SEMICOLON,
+ [0x23] = KEY_MINUS,
+ [0x24] = KEY_EQUAL,
+ [0x2b] = KEY_FN,
+ [0x2c] = KEY_M,
+ [0x2f] = KEY_F8,
+
+ [0x31] = KEY_R,
+ [0x32] = KEY_RIGHTCTRL,
+ [0x34] = KEY_SPACE,
+ [0x35] = KEY_COMMA,
+ [0x37] = KEY_UP,
+ [0x3c] = KEY_COMPOSE,
+ [0x3f] = KEY_F6,
+
+ [0x41] = KEY_T,
+ [0x44] = KEY_DOT,
+ [0x46] = KEY_RIGHT,
+ [0x4f] = KEY_F5,
+ [0x51] = KEY_Y,
+ [0x53] = KEY_DOWN,
+ [0x55] = KEY_ENTER,
+ [0x5f] = KEY_ESC,
+
+ [0x61] = KEY_U,
+ [0x64] = KEY_LEFT,
+
+ [0x71] = KEY_I,
+ [0x75] = KEY_KPENTER,
+};
+
+static struct lm8323_platform_data lm8323_pdata = {
+ .repeat = 0, /* Repeat is handled in userspace for now. */
+ .keymap = rx44_keymap,
+
+ .name = "Internal keyboard",
+ .pwm1_name = "keyboard",
+ .pwm2_name = "cover",
+};
+#endif
+
+void __init nokia_n800_init_irq(void)
+{
+ omap2_init_common_hw(NULL);
+ omap_init_irq();
+ omap_gpio_init();
+
+#ifdef CONFIG_OMAP_STI
+ if (omap_request_gpio(N800_STI_GPIO) < 0) {
+ printk(KERN_ERR "Failed to request GPIO %d for STI\n",
+ N800_STI_GPIO);
+ return;
+ }
+
+ omap_set_gpio_direction(N800_STI_GPIO, 0);
+ omap_set_gpio_dataout(N800_STI_GPIO, 0);
+#endif
+}
+
+#if defined(CONFIG_MENELAUS) && defined(CONFIG_SENSORS_TMP105)
+
+static int n800_tmp105_set_power(int enable)
+{
+ return menelaus_set_vaux(enable ? 2800 : 0);
+}
+
+#else
+
+#define n800_tmp105_set_power NULL
+
+#endif
+
+static struct omap_uart_config n800_uart_config __initdata = {
+ .enabled_uarts = (1 << 0) | (1 << 2),
+};
+
+#include "../../../drivers/cbus/retu.h"
+
+static struct omap_fbmem_config n800_fbmem0_config __initdata = {
+ .size = 752 * 1024,
+};
+
+static struct omap_fbmem_config n800_fbmem1_config __initdata = {
+ .size = 752 * 1024,
+};
+
+static struct omap_fbmem_config n800_fbmem2_config __initdata = {
+ .size = 752 * 1024,
+};
+
+static struct omap_tmp105_config n800_tmp105_config __initdata = {
+ .tmp105_irq_pin = 125,
+ .set_power = n800_tmp105_set_power,
+};
+
+static void mipid_shutdown(struct mipid_platform_data *pdata)
+{
+ if (pdata->nreset_gpio != -1) {
+ pr_info("shutdown LCD\n");
+ omap_set_gpio_dataout(pdata->nreset_gpio, 0);
+ msleep(120);
+ }
+}
+
+static struct mipid_platform_data n800_mipid_platform_data = {
+ .shutdown = mipid_shutdown,
+};
+
+static void __init mipid_dev_init(void)
+{
+ const struct omap_lcd_config *conf;
+
+ conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
+ if (conf != NULL) {
+ n800_mipid_platform_data.nreset_gpio = conf->nreset_gpio;
+ n800_mipid_platform_data.data_lines = conf->data_lines;
+ }
+}
+
+static struct {
+ struct clk *sys_ck;
+} blizzard;
+
+static int blizzard_get_clocks(void)
+{
+ blizzard.sys_ck = clk_get(0, "osc_ck");
+ if (IS_ERR(blizzard.sys_ck)) {
+ printk(KERN_ERR "can't get Blizzard clock\n");
+ return PTR_ERR(blizzard.sys_ck);
+ }
+ return 0;
+}
+
+static unsigned long blizzard_get_clock_rate(struct device *dev)
+{
+ return clk_get_rate(blizzard.sys_ck);
+}
+
+static void blizzard_enable_clocks(int enable)
+{
+ if (enable)
+ clk_enable(blizzard.sys_ck);
+ else
+ clk_disable(blizzard.sys_ck);
+}
+
+static void blizzard_power_up(struct device *dev)
+{
+ /* Vcore to 1.475V */
+ tahvo_set_clear_reg_bits(0x07, 0, 0xf);
+ msleep(10);
+
+ blizzard_enable_clocks(1);
+ omap_set_gpio_dataout(N800_BLIZZARD_POWERDOWN_GPIO, 1);
+}
+
+static void blizzard_power_down(struct device *dev)
+{
+ omap_set_gpio_dataout(N800_BLIZZARD_POWERDOWN_GPIO, 0);
+ blizzard_enable_clocks(0);
+
+ /* Vcore to 1.005V */
+ tahvo_set_clear_reg_bits(0x07, 0xf, 0);
+}
+
+static struct blizzard_platform_data n800_blizzard_data = {
+ .power_up = blizzard_power_up,
+ .power_down = blizzard_power_down,
+ .get_clock_rate = blizzard_get_clock_rate,
+ .te_connected = 1,
+};
+
+static void __init blizzard_dev_init(void)
+{
+ int r;
+
+ r = omap_request_gpio(N800_BLIZZARD_POWERDOWN_GPIO);
+ if (r < 0)
+ return;
+ omap_set_gpio_direction(N800_BLIZZARD_POWERDOWN_GPIO, 0);
+ omap_set_gpio_dataout(N800_BLIZZARD_POWERDOWN_GPIO, 1);
+
+ blizzard_get_clocks();
+ omapfb_set_ctrl_platform_data(&n800_blizzard_data);
+}
+
+static struct omap_mmc_config n800_mmc_config __initdata = {
+ .mmc [0] = {
+ .enabled = 1,
+ .wire4 = 1,
+ },
+};
+
+extern struct omap_mmc_platform_data n800_mmc_data;
+
+static struct omap_board_config_kernel n800_config[] __initdata = {
+ { OMAP_TAG_UART, &n800_uart_config },
+ { OMAP_TAG_FBMEM, &n800_fbmem0_config },
+ { OMAP_TAG_FBMEM, &n800_fbmem1_config },
+ { OMAP_TAG_FBMEM, &n800_fbmem2_config },
+ { OMAP_TAG_TMP105, &n800_tmp105_config },
+ { OMAP_TAG_MMC, &n800_mmc_config },
+};
+
+static struct tsc2301_platform_data tsc2301_config = {
+ .reset_gpio = N800_TSC2301_RESET_GPIO,
+ .keymap = {
+ -1, /* Event for bit 0 */
+ KEY_UP, /* Event for bit 1 (up) */
+ KEY_F5, /* Event for bit 2 (home) */
+ -1, /* Event for bit 3 */
+ KEY_LEFT, /* Event for bit 4 (left) */
+ KEY_ENTER, /* Event for bit 5 (enter) */
+ KEY_RIGHT, /* Event for bit 6 (right) */
+ -1, /* Event for bit 7 */
+ KEY_ESC, /* Event for bit 8 (cycle) */
+ KEY_DOWN, /* Event for bit 9 (down) */
+ KEY_F4, /* Event for bit 10 (menu) */
+ -1, /* Event for bit 11 */
+ KEY_F8, /* Event for bit 12 (Zoom-) */
+ KEY_F6, /* Event for bit 13 (FS) */
+ KEY_F7, /* Event for bit 14 (Zoom+) */
+ -1, /* Event for bit 15 */
+ },
+ .kp_rep = 0,
+ .keyb_name = "Internal keypad",
+};
+
+static void tsc2301_dev_init(void)
+{
+ int r;
+ int gpio = N800_KEYB_IRQ_GPIO;
+
+ r = gpio_request(gpio, "tsc2301 KBD IRQ");
+ if (r >= 0) {
+ gpio_direction_input(gpio);
+ tsc2301_config.keyb_int = OMAP_GPIO_IRQ(gpio);
+ } else {
+ printk(KERN_ERR "unable to get KBD GPIO");
+ }
+
+ gpio = N800_DAV_IRQ_GPIO;
+ r = gpio_request(gpio, "tsc2301 DAV IRQ");
+ if (r >= 0) {
+ gpio_direction_input(gpio);
+ tsc2301_config.dav_int = OMAP_GPIO_IRQ(gpio);
+ } else {
+ printk(KERN_ERR "unable to get DAV GPIO");
+ }
+}
+
+static int __init tea5761_dev_init(void)
+{
+ const struct omap_tea5761_config *info;
+ int enable_gpio = 0;
+
+ info = omap_get_config(OMAP_TAG_TEA5761, struct omap_tea5761_config);
+ if (info)
+ enable_gpio = info->enable_gpio;
+
+ if (enable_gpio) {
+ pr_debug("Enabling tea5761 at GPIO %d\n",
+ enable_gpio);
+
+ if (omap_request_gpio(enable_gpio) < 0) {
+ printk(KERN_ERR "Can't request GPIO %d\n",
+ enable_gpio);
+ return -ENODEV;
+ }
+
+ omap_set_gpio_direction(enable_gpio, 0);
+ udelay(50);
+ omap_set_gpio_dataout(enable_gpio, 1);
+ }
+
+ return 0;
+}
+
+static struct omap2_mcspi_device_config tsc2301_mcspi_config = {
+ .turbo_mode = 0,
+ .single_channel = 1,
+};
+
+static struct omap2_mcspi_device_config mipid_mcspi_config = {
+ .turbo_mode = 0,
+ .single_channel = 1,
+};
+
+static struct omap2_mcspi_device_config cx3110x_mcspi_config = {
+ .turbo_mode = 0,
+ .single_channel = 1,
+};
+
+#ifdef CONFIG_TOUCHSCREEN_TSC2005
+static struct tsc2005_platform_data tsc2005_config = {
+ .reset_gpio = 94,
+ .dav_gpio = 106
+};
+
+static struct omap2_mcspi_device_config tsc2005_mcspi_config = {
+ .turbo_mode = 0,
+ .single_channel = 1,
+};
+#endif
+
+static struct spi_board_info n800_spi_board_info[] __initdata = {
+ {
+ .modalias = "lcd_mipid",
+ .bus_num = 1,
+ .chip_select = 1,
+ .max_speed_hz = 4000000,
+ .controller_data= &mipid_mcspi_config,
+ .platform_data = &n800_mipid_platform_data,
+ }, {
+ .modalias = "cx3110x",
+ .bus_num = 2,
+ .chip_select = 0,
+ .max_speed_hz = 48000000,
+ .controller_data= &cx3110x_mcspi_config,
+ },
+ {
+ .modalias = "tsc2301",
+ .bus_num = 1,
+ .chip_select = 0,
+ .max_speed_hz = 6000000,
+ .controller_data= &tsc2301_mcspi_config,
+ .platform_data = &tsc2301_config,
+ },
+};
+
+static struct spi_board_info n810_spi_board_info[] __initdata = {
+ {
+ .modalias = "lcd_mipid",
+ .bus_num = 1,
+ .chip_select = 1,
+ .max_speed_hz = 4000000,
+ .controller_data = &mipid_mcspi_config,
+ .platform_data = &n800_mipid_platform_data,
+ },
+ {
+ .modalias = "cx3110x",
+ .bus_num = 2,
+ .chip_select = 0,
+ .max_speed_hz = 48000000,
+ .controller_data = &cx3110x_mcspi_config,
+ },
+ {
+ .modalias = "tsc2005",
+ .bus_num = 1,
+ .chip_select = 0,
+ .max_speed_hz = 6000000,
+ .controller_data = &tsc2005_mcspi_config,
+ .platform_data = &tsc2005_config,
+ },
+};
+
+static void __init tsc2005_set_config(void)
+{
+ const struct omap_lcd_config *conf;
+
+ conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
+ if (conf != NULL) {
+#ifdef CONFIG_TOUCHSCREEN_TSC2005
+ if (strcmp(conf->panel_name, "lph8923") == 0) {
+ tsc2005_config.ts_x_plate_ohm = 180;
+ tsc2005_config.ts_hw_avg = 0;
+ tsc2005_config.ts_ignore_last = 0;
+ tsc2005_config.ts_touch_pressure = 1500;
+ tsc2005_config.ts_stab_time = 100;
+ tsc2005_config.ts_pressure_max = 2048;
+ tsc2005_config.ts_pressure_fudge = 2;
+ tsc2005_config.ts_x_max = 4096;
+ tsc2005_config.ts_x_fudge = 4;
+ tsc2005_config.ts_y_max = 4096;
+ tsc2005_config.ts_y_fudge = 7;
+ } else if (strcmp(conf->panel_name, "ls041y3") == 0) {
+ tsc2005_config.ts_x_plate_ohm = 280;
+ tsc2005_config.ts_hw_avg = 0;
+ tsc2005_config.ts_ignore_last = 0;
+ tsc2005_config.ts_touch_pressure = 1500;
+ tsc2005_config.ts_stab_time = 1000;
+ tsc2005_config.ts_pressure_max = 2048;
+ tsc2005_config.ts_pressure_fudge = 2;
+ tsc2005_config.ts_x_max = 4096;
+ tsc2005_config.ts_x_fudge = 4;
+ tsc2005_config.ts_y_max = 4096;
+ tsc2005_config.ts_y_fudge = 7;
+ } else {
+ printk(KERN_ERR "Unknown panel type, set default "
+ "touchscreen configuration\n");
+ tsc2005_config.ts_x_plate_ohm = 200;
+ tsc2005_config.ts_stab_time = 100;
+ }
+#endif
+ }
+}
+
+#if defined(CONFIG_CBUS_RETU) && defined(CONFIG_LEDS_OMAP_PWM)
+
+void retu_keypad_led_set_power(struct omap_pwm_led_platform_data *self,
+ int on_off)
+{
+ if (on_off) {
+ retu_write_reg(RETU_REG_CTRL_SET, 1 << 6);
+ msleep(2);
+ retu_write_reg(RETU_REG_CTRL_SET, 1 << 3);
+ } else {
+ retu_write_reg(RETU_REG_CTRL_CLR, (1 << 6) | (1 << 3));
+ }
+}
+
+static struct omap_pwm_led_platform_data n800_keypad_led_data = {
+ .name = "keypad",
+ .intensity_timer = 10,
+ .blink_timer = 9,
+ .set_power = retu_keypad_led_set_power,
+};
+
+static struct platform_device n800_keypad_led_device = {
+ .name = "omap_pwm_led",
+ .id = -1,
+ .dev = {
+ .platform_data = &n800_keypad_led_data,
+ },
+};
+#endif
+
+#if defined(CONFIG_TOUCHSCREEN_TSC2301)
+static void __init n800_ts_set_config(void)
+{
+ const struct omap_lcd_config *conf;
+
+ conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
+ if (conf != NULL) {
+ if (strcmp(conf->panel_name, "lph8923") == 0) {
+ tsc2301_config.ts_x_plate_ohm = 180;
+ tsc2301_config.ts_hw_avg = 8;
+ tsc2301_config.ts_max_pressure = 2048;
+ tsc2301_config.ts_touch_pressure = 400;
+ tsc2301_config.ts_stab_time = 100;
+ tsc2301_config.ts_pressure_fudge = 2;
+ tsc2301_config.ts_x_max = 4096;
+ tsc2301_config.ts_x_fudge = 4;
+ tsc2301_config.ts_y_max = 4096;
+ tsc2301_config.ts_y_fudge = 7;
+ } else if (strcmp(conf->panel_name, "ls041y3") == 0) {
+ tsc2301_config.ts_x_plate_ohm = 280;
+ tsc2301_config.ts_hw_avg = 8;
+ tsc2301_config.ts_touch_pressure = 400;
+ tsc2301_config.ts_max_pressure = 2048;
+ tsc2301_config.ts_stab_time = 1000;
+ tsc2301_config.ts_pressure_fudge = 2;
+ tsc2301_config.ts_x_max = 4096;
+ tsc2301_config.ts_x_fudge = 4;
+ tsc2301_config.ts_y_max = 4096;
+ tsc2301_config.ts_y_fudge = 7;
+ } else {
+ printk(KERN_ERR "Unknown panel type, set default "
+ "touchscreen configuration\n");
+ tsc2301_config.ts_x_plate_ohm = 200;
+ tsc2301_config.ts_stab_time = 100;
+ }
+ }
+}
+#else
+static inline void n800_ts_set_config(void)
+{
+}
+#endif
+
+static struct omap_gpio_switch n800_gpio_switches[] __initdata = {
+ {
+ .name = "bat_cover",
+ .gpio = -1,
+ .debounce_rising = 100,
+ .debounce_falling = 0,
+ .notify = n800_mmc_slot1_cover_handler,
+ .notify_data = NULL,
+ }, {
+ .name = "headphone",
+ .gpio = -1,
+ .debounce_rising = 200,
+ .debounce_falling = 200,
+ }, {
+ .name = "cam_act",
+ .gpio = -1,
+ .debounce_rising = 200,
+ .debounce_falling = 200,
+ }, {
+ .name = "cam_turn",
+ .gpio = -1,
+ .debounce_rising = 100,
+ .debounce_falling = 100,
+ },
+};
+
+static struct platform_device *n800_devices[] __initdata = {
+#if defined(CONFIG_CBUS_RETU) && defined(CONFIG_LEDS_OMAP_PWM)
+ &n800_keypad_led_device,
+#endif
+};
+
+#ifdef CONFIG_MENELAUS
+static int n800_auto_sleep_regulators(void)
+{
+ u32 val;
+ int ret;
+
+ val = EN_VPLL_SLEEP | EN_VMMC_SLEEP \
+ | EN_VAUX_SLEEP | EN_VIO_SLEEP \
+ | EN_VMEM_SLEEP | EN_DC3_SLEEP \
+ | EN_VC_SLEEP | EN_DC2_SLEEP;
+
+ ret = menelaus_set_regulator_sleep(1, val);
+ if (ret < 0) {
+ printk(KERN_ERR "Could not set regulators to sleep on "
+ "menelaus: %u\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int n800_auto_voltage_scale(void)
+{
+ int ret;
+
+ ret = menelaus_set_vcore_hw(1400, 1050);
+ if (ret < 0) {
+ printk(KERN_ERR "Could not set VCORE voltage on "
+ "menelaus: %u\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int n800_menelaus_init(struct device *dev)
+{
+ int ret;
+
+ ret = n800_auto_voltage_scale();
+ if (ret < 0)
+ return ret;
+ ret = n800_auto_sleep_regulators();
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static struct menelaus_platform_data n800_menelaus_platform_data = {
+ .late_init = n800_menelaus_init,
+};
+#endif
+
+static struct i2c_board_info __initdata n800_i2c_board_info_1[] = {
+ {
+ I2C_BOARD_INFO("menelaus", 0x72),
+ .irq = INT_24XX_SYS_NIRQ,
+ .platform_data = &n800_menelaus_platform_data,
+ },
+};
+
+extern struct tcm825x_platform_data n800_tcm825x_platform_data;
+
+static struct i2c_board_info __initdata_or_module n8x0_i2c_board_info_2[] = {
+ {
+ I2C_BOARD_INFO(TCM825X_NAME, TCM825X_I2C_ADDR),
+#if defined (CONFIG_VIDEO_TCM825X) || defined (CONFIG_VIDEO_TCM825X_MODULE)
+ .platform_data = &n800_tcm825x_platform_data,
+#endif
+ },
+ {
+ I2C_BOARD_INFO("tsl2563", 0x29),
+ },
+ {
+ I2C_BOARD_INFO("lp5521", 0x32),
+ },
+};
+
+
+static struct i2c_board_info __initdata_or_module n800_i2c_board_info_2[] = {
+ {
+ I2C_BOARD_INFO("tea5761", 0x10),
+ },
+};
+
+static struct i2c_board_info __initdata_or_module n810_i2c_board_info_2[] = {
+ {
+ I2C_BOARD_INFO("lm8323", 0x45),
+ .irq = OMAP_GPIO_IRQ(109),
+ .platform_data = &lm8323_pdata,
+ },
+};
+
+void __init nokia_n800_common_init(void)
+{
+ platform_add_devices(n800_devices, ARRAY_SIZE(n800_devices));
+
+ n800_flash_init();
+ n800_mmc_init();
+ n800_bt_init();
+ n800_dsp_init();
+ n800_usb_init();
+ n800_cam_init();
+ if (machine_is_nokia_n800())
+ spi_register_board_info(n800_spi_board_info,
+ ARRAY_SIZE(n800_spi_board_info));
+ if (machine_is_nokia_n810()) {
+ tsc2005_set_config();
+ spi_register_board_info(n810_spi_board_info,
+ ARRAY_SIZE(n810_spi_board_info));
+ }
+ omap_serial_init();
+ omap_register_i2c_bus(1, 400, n800_i2c_board_info_1,
+ ARRAY_SIZE(n800_i2c_board_info_1));
+ omap_register_i2c_bus(2, 400, n8x0_i2c_board_info_2,
+ ARRAY_SIZE(n800_i2c_board_info_2));
+ if (machine_is_nokia_n800())
+ i2c_register_board_info(2, n800_i2c_board_info_2,
+ ARRAY_SIZE(n800_i2c_board_info_2));
+ if (machine_is_nokia_n810())
+ i2c_register_board_info(2, n810_i2c_board_info_2,
+ ARRAY_SIZE(n810_i2c_board_info_2));
+
+ mipid_dev_init();
+ blizzard_dev_init();
+}
+
+static void __init nokia_n800_init(void)
+{
+ nokia_n800_common_init();
+
+ n800_audio_init(&tsc2301_config);
+ n800_ts_set_config();
+ tsc2301_dev_init();
+ tea5761_dev_init();
+ omap_register_gpio_switches(n800_gpio_switches,
+ ARRAY_SIZE(n800_gpio_switches));
+}
+
+void __init nokia_n800_map_io(void)
+{
+ omap_board_config = n800_config;
+ omap_board_config_size = ARRAY_SIZE(n800_config);
+
+ omap2_set_globals_242x();
+ omap2_map_common_io();
+}
+
+MACHINE_START(NOKIA_N800, "Nokia N800")
+ .phys_io = 0x48000000,
+ .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc,
+ .boot_params = 0x80000100,
+ .map_io = nokia_n800_map_io,
+ .init_irq = nokia_n800_init_irq,
+ .init_machine = nokia_n800_init,
+ .timer = &omap_timer,
+MACHINE_END
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * linux/arch/arm/mach-omap2/board-n810.c
+ *
+ * Copyright (C) 2007 Nokia
+ * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ *
+ * 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/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c/lm8323.h>
+
- #include <asm/arch/board.h>
- #include <asm/arch/common.h>
++#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
++#include <mach/board.h>
++#include <mach/common.h>
+
+#include "board-n800.h"
+
+static void __init nokia_n810_init(void)
+{
+ nokia_n800_common_init();
+}
+
+MACHINE_START(NOKIA_N810, "Nokia N810")
+ .phys_io = 0x48000000,
+ .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc,
+ .boot_params = 0x80000100,
+ .map_io = nokia_n800_map_io,
+ .init_irq = nokia_n800_init_irq,
+ .init_machine = nokia_n810_init,
+ .timer = &omap_timer,
+MACHINE_END
+
+MACHINE_START(NOKIA_N810_WIMAX, "Nokia N810 WiMAX")
+ .phys_io = 0x48000000,
+ .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc,
+ .boot_params = 0x80000100,
+ .map_io = nokia_n800_map_io,
+ .init_irq = nokia_n800_init_irq,
+ .init_machine = nokia_n810_init,
+ .timer = &omap_timer,
+MACHINE_END
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * linux/arch/arm/mach-omap2/board-omap2evm.c
+ *
+ * Copyright (C) 2008 Mistral Solutions Pvt Ltd
+ *
+ * Modified from mach-omap2/board-generic.c
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/input.h>
+
- #include <asm/arch/gpio.h>
- #include <asm/arch/board.h>
- #include <asm/arch/common.h>
- #include <asm/arch/hsmmc.h>
- #include <asm/arch/keypad.h>
++#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
++#include <mach/gpio.h>
++#include <mach/board.h>
++#include <mach/common.h>
++#include <mach/hsmmc.h>
++#include <mach/keypad.h>
+
+static struct resource omap2evm_smc911x_resources[] = {
+ [0] = {
+ .start = OMAP2EVM_ETHR_START,
+ .end = (OMAP2EVM_ETHR_START + OMAP2EVM_ETHR_SIZE - 1),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = OMAP_GPIO_IRQ(OMAP2EVM_ETHR_GPIO_IRQ),
+ .end = OMAP_GPIO_IRQ(OMAP2EVM_ETHR_GPIO_IRQ),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device omap2evm_smc911x_device = {
+ .name = "smc911x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(omap2evm_smc911x_resources),
+ .resource = &omap2evm_smc911x_resources [0],
+};
+
+static inline void __init omap2evm_init_smc911x(void)
+{
+ int gpio = OMAP2EVM_ETHR_GPIO_IRQ;
+ int ret;
+
+ ret = gpio_request(gpio, "smc911x IRQ");
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to request GPIO %d for smc911x IRQ\n",
+ gpio);
+ return;
+ }
+ gpio_direction_input(gpio);
+
+}
+
+static struct platform_device omap2_evm_lcd_device = {
+ .name = "omap2evm_lcd",
+ .id = -1,
+};
+
+static struct omap_lcd_config omap2_evm_lcd_config __initdata = {
+ .ctrl_name = "internal",
+};
+
+static int omap2evm_keymap[] = {
+ KEY(0, 0, KEY_LEFT),
+ KEY(0, 1, KEY_RIGHT),
+ KEY(0, 2, KEY_A),
+ KEY(0, 3, KEY_B),
+ KEY(1, 0, KEY_DOWN),
+ KEY(1, 1, KEY_UP),
+ KEY(1, 2, KEY_E),
+ KEY(1, 3, KEY_F),
+ KEY(2, 0, KEY_ENTER),
+ KEY(2, 1, KEY_I),
+ KEY(2, 2, KEY_J),
+ KEY(2, 3, KEY_K),
+ KEY(3, 0, KEY_M),
+ KEY(3, 1, KEY_N),
+ KEY(3, 2, KEY_O),
+ KEY(3, 3, KEY_P)
+};
+
+static struct omap_kp_platform_data omap2evm_kp_data = {
+ .rows = 4,
+ .cols = 4,
+ .keymap = omap2evm_keymap,
+ .keymapsize = ARRAY_SIZE(omap2evm_keymap),
+ .rep = 1,
+};
+
+static struct platform_device omap2evm_kp_device = {
+ .name = "omap_twl4030keypad",
+ .id = -1,
+ .dev = {
+ .platform_data = &omap2evm_kp_data,
+ },
+};
+
+static void __init omap2_evm_init_irq(void)
+{
+ omap2_init_common_hw(NULL);
+ omap_init_irq();
+ omap_gpio_init();
+ omap2evm_init_smc911x();
+}
+
+static struct omap_uart_config omap2_evm_uart_config __initdata = {
+ .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
+};
+
+static struct omap_mmc_config omap2_evm_mmc_config __initdata = {
+ .mmc [0] = {
+ .enabled = 1,
+ .wire4 = 1,
+ },
+};
+
+static struct omap_board_config_kernel omap2_evm_config[] __initdata = {
+ { OMAP_TAG_UART, &omap2_evm_uart_config },
+ { OMAP_TAG_LCD, &omap2_evm_lcd_config },
+ { OMAP_TAG_MMC, &omap2_evm_mmc_config },
+};
+
+static int __init omap2_evm_i2c_init(void)
+{
+ /*
+ * Registering bus 2 first to avoid twl4030 misbehaving as OMAP2EVM
+ * has twl4030 on bus 2
+ */
+ omap_register_i2c_bus(2, 2600, NULL, 0);
+ omap_register_i2c_bus(1, 400, NULL, 0);
+ return 0;
+}
+
+static struct platform_device *omap2_evm_devices[] __initdata = {
+ &omap2_evm_lcd_device,
+ &omap2evm_smc911x_device,
+ &omap2evm_kp_device,
+};
+
+static void __init omap2_evm_init(void)
+{
+ platform_add_devices(omap2_evm_devices, ARRAY_SIZE(omap2_evm_devices));
+ omap_board_config = omap2_evm_config;
+ omap_board_config_size = ARRAY_SIZE(omap2_evm_config);
+ omap_serial_init();
+ hsmmc_init();
+}
+
+static void __init omap2_evm_map_io(void)
+{
+ omap2_set_globals_243x();
+ omap2_map_common_io();
+}
+
+arch_initcall(omap2_evm_i2c_init);
+
+MACHINE_START(OMAP2EVM, "OMAP2EVM Board")
+ /* Maintainer: Arun KS <arunks@mistralsolutions.com> */
+ .phys_io = 0x48000000,
+ .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc,
+ .boot_params = 0x80000100,
+ .map_io = omap2_evm_map_io,
+ .init_irq = omap2_evm_init_irq,
+ .init_machine = omap2_evm_init,
+ .timer = &omap_timer,
+MACHINE_END
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * linux/arch/arm/mach-omap2/board-omap3beagle.c
+ *
+ * Copyright (C) 2008 Texas Instruments
+ *
+ * Modified from mach-omap2/board-3430sdp.c
+ *
+ * Initial code: Syed Mohammed Khasim
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/leds.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand.h>
+
- #include <asm/arch/gpio.h>
- #include <asm/arch/board.h>
- #include <asm/arch/usb-musb.h>
- #include <asm/arch/usb-ehci.h>
- #include <asm/arch/hsmmc.h>
- #include <asm/arch/common.h>
- #include <asm/arch/gpmc.h>
- #include <asm/arch/nand.h>
++#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/flash.h>
+
++#include <mach/gpio.h>
++#include <mach/board.h>
++#include <mach/usb-musb.h>
++#include <mach/usb-ehci.h>
++#include <mach/hsmmc.h>
++#include <mach/common.h>
++#include <mach/gpmc.h>
++#include <mach/nand.h>
+
+#define GPMC_CS0_BASE 0x60
+#define GPMC_CS_SIZE 0x30
+
+static struct mtd_partition omap3beagle_nand_partitions[] = {
+ /* All the partition sizes are listed in terms of NAND block size */
+ {
+ .name = "X-Loader",
+ .offset = 0,
+ .size = 4*(64 * 2048),
+ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+ .name = "U-Boot",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x80000 */
+ .size = 15*(64 * 2048),
+ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+ .name = "U-Boot Env",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x260000 */
+ .size = 1*(64 * 2048),
+ },
+ {
+ .name = "Kernel",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x280000 */
+ .size = 32*(64 * 2048),
+ },
+ {
+ .name = "File System",
+ .offset = MTDPART_OFS_APPEND, /* Offset = 0x680000 */
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct omap_nand_platform_data omap3beagle_nand_data = {
+ .parts = omap3beagle_nand_partitions,
+ .nr_parts = ARRAY_SIZE(omap3beagle_nand_partitions),
+ .dma_channel = -1, /* disable DMA in OMAP NAND driver */
+ .nand_setup = NULL,
+ .dev_ready = NULL,
+};
+
+static struct resource omap3beagle_nand_resource = {
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device omap3beagle_nand_device = {
+ .name = "omap2-nand",
+ .id = -1,
+ .dev = {
+ .platform_data = &omap3beagle_nand_data,
+ },
+ .num_resources = 1,
+ .resource = &omap3beagle_nand_resource,
+};
+
+#include "sdram-micron-mt46h32m32lf-6.h"
+
+static struct omap_uart_config omap3_beagle_uart_config __initdata = {
+ .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
+};
+
+static int __init omap3_beagle_i2c_init(void)
+{
+ omap_register_i2c_bus(1, 2600, NULL, 0);
+#ifdef CONFIG_I2C2_OMAP_BEAGLE
+ omap_register_i2c_bus(2, 400, NULL, 0);
+#endif
+ omap_register_i2c_bus(3, 400, NULL, 0);
+ return 0;
+}
+
+static void __init omap3_beagle_init_irq(void)
+{
+ omap2_init_common_hw(mt46h32m32lf6_sdrc_params);
+ omap_init_irq();
+ omap_gpio_init();
+}
+
+static struct omap_mmc_config omap3beagle_mmc_config __initdata = {
+ .mmc [0] = {
+ .enabled = 1,
+ .wire4 = 1,
+ },
+};
+
+static struct platform_device omap3_beagle_twl4030rtc_device = {
+ .name = "twl4030_rtc",
+ .id = -1,
+};
+
+static struct platform_device omap3_beagle_lcd_device = {
+ .name = "omap3beagle_lcd",
+ .id = -1,
+};
+
+static struct omap_lcd_config omap3_beagle_lcd_config __initdata = {
+ .ctrl_name = "internal",
+};
+
+struct gpio_led gpio_leds[] = {
+ {
+ .name = "beagleboard::usr0",
+ .default_trigger = "none",
+ .gpio = 150,
+ },
+ {
+ .name = "beagleboard::usr1",
+ .default_trigger = "none",
+ .gpio = 149,
+ },
+};
+
+static struct gpio_led_platform_data gpio_led_info = {
+ .leds = gpio_leds,
+ .num_leds = ARRAY_SIZE(gpio_leds),
+};
+
+static struct platform_device leds_gpio = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &gpio_led_info,
+ },
+};
+
+static struct omap_board_config_kernel omap3_beagle_config[] __initdata = {
+ { OMAP_TAG_UART, &omap3_beagle_uart_config },
+ { OMAP_TAG_MMC, &omap3beagle_mmc_config },
+ { OMAP_TAG_LCD, &omap3_beagle_lcd_config },
+};
+
+static struct platform_device *omap3_beagle_devices[] __initdata = {
+ &omap3_beagle_lcd_device,
+#ifdef CONFIG_RTC_DRV_TWL4030
+ &omap3_beagle_twl4030rtc_device,
+#endif
+ &leds_gpio,
+};
+
+void __init omap3beagle_flash_init(void)
+{
+ u8 cs = 0;
+ u8 nandcs = GPMC_CS_NUM + 1;
+
+ u32 gpmc_base_add = OMAP34XX_GPMC_VIRT;
+
+ /* find out the chip-select on which NAND exists */
+ while (cs < GPMC_CS_NUM) {
+ u32 ret = 0;
+ ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
+
+ if ((ret & 0xC00) == 0x800) {
+ printk(KERN_INFO "Found NAND on CS%d\n", cs);
+ if (nandcs > GPMC_CS_NUM)
+ nandcs = cs;
+ }
+ cs++;
+ }
+
+ if (nandcs > GPMC_CS_NUM) {
+ printk(KERN_INFO "NAND: Unable to find configuration "
+ "in GPMC\n ");
+ return;
+ }
+
+ if (nandcs < GPMC_CS_NUM) {
+ omap3beagle_nand_data.cs = nandcs;
+ omap3beagle_nand_data.gpmc_cs_baseaddr = (void *)
+ (gpmc_base_add + GPMC_CS0_BASE + nandcs * GPMC_CS_SIZE);
+ omap3beagle_nand_data.gpmc_baseaddr = (void *) (gpmc_base_add);
+
+ printk(KERN_INFO "Registering NAND on CS%d\n", nandcs);
+ if (platform_device_register(&omap3beagle_nand_device) < 0)
+ printk(KERN_ERR "Unable to register NAND device\n");
+ }
+}
+
+static void __init omap3_beagle_init(void)
+{
+ platform_add_devices(omap3_beagle_devices, ARRAY_SIZE(omap3_beagle_devices));
+ omap_board_config = omap3_beagle_config;
+ omap_board_config_size = ARRAY_SIZE(omap3_beagle_config);
+ omap_serial_init();
+ hsmmc_init();
+ usb_musb_init();
+ usb_ehci_init();
+ omap3beagle_flash_init();
+}
+
+arch_initcall(omap3_beagle_i2c_init);
+
+static void __init omap3_beagle_map_io(void)
+{
+ omap2_set_globals_343x();
+ omap2_map_common_io();
+}
+
+MACHINE_START(OMAP3_BEAGLE, "OMAP3 Beagle Board")
+ /* Maintainer: Syed Mohammed Khasim - http://beagleboard.org */
+ .phys_io = 0x48000000,
+ .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc,
+ .boot_params = 0x80000100,
+ .map_io = omap3_beagle_map_io,
+ .init_irq = omap3_beagle_init_irq,
+ .init_machine = omap3_beagle_init,
+ .timer = &omap_timer,
+MACHINE_END
--- /dev/null
- #include <asm/arch/onenand.h>
- #include <asm/arch/board.h>
- #include <asm/arch/gpmc.h>
- #include <asm/arch/nand.h>
+/*
+ * board-omap3evm-flash.c
+ *
+ * Copyright (c) 2008 Texas Instruments,
+ *
+ * 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/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/onenand_regs.h>
+#include <linux/types.h>
+#include <linux/io.h>
+
+#include <asm/mach/flash.h>
++#include <mach/onenand.h>
++#include <mach/board.h>
++#include <mach/gpmc.h>
++#include <mach/nand.h>
+
+static int omap3evm_onenand_setup(void __iomem *);
+
+static struct mtd_partition omap3evm_onenand_partitions[] = {
+ {
+ .name = "xloader",
+ .offset = 0,
+ .size = 4*(64*2048),
+ .mask_flags = MTD_WRITEABLE
+ },
+ {
+ .name = "uboot",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 15*(64*2048),
+ .mask_flags = MTD_WRITEABLE
+ },
+ {
+ .name = "params",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 1*(64*2048),
+ },
+ {
+ .name = "linux",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 40*(64*2048),
+ },
+ {
+ .name = "jffs2",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct omap_onenand_platform_data omap3evm_onenand_data = {
+ .parts = omap3evm_onenand_partitions,
+ .nr_parts = ARRAY_SIZE(omap3evm_onenand_partitions),
+ .onenand_setup = omap3evm_onenand_setup,
+ .dma_channel = -1, /* disable DMA in OMAP OneNAND driver */
+};
+
+static struct platform_device omap3evm_onenand_device = {
+ .name = "omap2-onenand",
+ .id = -1,
+ .dev = {
+ .platform_data = &omap3evm_onenand_data,
+ },
+};
+
+/*
+ * omap3evm_onenand_setup - Set the onenand sync mode
+ * @onenand_base: The onenand base address in GPMC memory map
+ *
+ */
+
+static int omap3evm_onenand_setup(void __iomem *onenand_base)
+ {
+ /* nothing is required to be setup for onenand as of now */
+ return 0;
+}
+
+void __init omap3evm_flash_init(void)
+{
+ u8 cs = 0;
+ u8 onenandcs = GPMC_CS_NUM + 1;
+
+ while (cs < GPMC_CS_NUM) {
+ u32 ret = 0;
+ ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
+
+ /*
+ * xloader/Uboot would have programmed the oneNAND
+ * base address for us This is a ugly hack. The proper
+ * way of doing this is to pass the setup of u-boot up
+ * to kernel using kernel params - something on the
+ * lines of machineID. Check if oneNAND is configured
+ */
+ if ((ret & 0x3F) == (ONENAND_MAP >> 24))
+ onenandcs = cs;
+ cs++;
+ }
+ if (onenandcs > GPMC_CS_NUM) {
+ printk(KERN_INFO "OneNAND: Unable to find configuration "
+ " in GPMC\n ");
+ return;
+ }
+
+ if (onenandcs < GPMC_CS_NUM) {
+ omap3evm_onenand_data.cs = onenandcs;
+ if (platform_device_register(&omap3evm_onenand_device) < 0)
+ printk(KERN_ERR "Unable to register OneNAND device\n");
+ }
+}
+
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * linux/arch/arm/mach-omap2/board-omap3evm.c
+ *
+ * Copyright (C) 2008 Texas Instruments
+ *
+ * Modified from mach-omap2/board-3430sdp.c
+ *
+ * Initial code: Syed Mohammed Khasim
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+
- #include <asm/arch/gpio.h>
- #include <asm/arch/keypad.h>
- #include <asm/arch/board.h>
- #include <asm/arch/hsmmc.h>
- #include <asm/arch/usb-musb.h>
- #include <asm/arch/usb-ehci.h>
- #include <asm/arch/common.h>
- #include <asm/arch/mcspi.h>
++#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
++#include <mach/gpio.h>
++#include <mach/keypad.h>
++#include <mach/board.h>
++#include <mach/hsmmc.h>
++#include <mach/usb-musb.h>
++#include <mach/usb-ehci.h>
++#include <mach/common.h>
++#include <mach/mcspi.h>
+
+#include "sdram-micron-mt46h32m32lf-6.h"
+
+static struct resource omap3evm_smc911x_resources[] = {
+ [0] = {
+ .start = OMAP3EVM_ETHR_START,
+ .end = (OMAP3EVM_ETHR_START + OMAP3EVM_ETHR_SIZE - 1),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = OMAP_GPIO_IRQ(OMAP3EVM_ETHR_GPIO_IRQ),
+ .end = OMAP_GPIO_IRQ(OMAP3EVM_ETHR_GPIO_IRQ),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device omap3evm_smc911x_device = {
+ .name = "smc911x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(omap3evm_smc911x_resources),
+ .resource = &omap3evm_smc911x_resources [0],
+};
+
+static inline void __init omap3evm_init_smc911x(void)
+{
+ int eth_cs;
+ struct clk *l3ck;
+ unsigned int rate;
+
+ eth_cs = OMAP3EVM_SMC911X_CS;
+
+ l3ck = clk_get(NULL, "l3_ck");
+ if (IS_ERR(l3ck))
+ rate = 100000000;
+ else
+ rate = clk_get_rate(l3ck);
+
+ if (omap_request_gpio(OMAP3EVM_ETHR_GPIO_IRQ) < 0) {
+ printk(KERN_ERR "Failed to request GPIO%d for smc911x IRQ\n",
+ OMAP3EVM_ETHR_GPIO_IRQ);
+ return;
+ }
+
+ omap_set_gpio_direction(OMAP3EVM_ETHR_GPIO_IRQ, 1);
+}
+
+static struct omap_uart_config omap3_evm_uart_config __initdata = {
+ .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
+};
+
+static int __init omap3_evm_i2c_init(void)
+{
+ omap_register_i2c_bus(1, 2600, NULL, 0);
+ omap_register_i2c_bus(2, 400, NULL, 0);
+ omap_register_i2c_bus(3, 400, NULL, 0);
+ return 0;
+}
+
+static struct omap_mmc_config omap3_evm_mmc_config __initdata = {
+ .mmc [0] = {
+ .enabled = 1,
+ .wire4 = 1,
+ },
+};
+
+static struct platform_device omap3_evm_lcd_device = {
+ .name = "omap3evm_lcd",
+ .id = -1,
+};
+
+static struct omap_lcd_config omap3_evm_lcd_config __initdata = {
+ .ctrl_name = "internal",
+};
+
+static struct platform_device omap3_evm_twl4030rtc_device = {
+ .name = "twl4030_rtc",
+ .id = -1,
+};
+
+static void ads7846_dev_init(void)
+{
+ if (omap_request_gpio(OMAP3_EVM_TS_GPIO) < 0)
+ printk(KERN_ERR "can't get ads7846 pen down GPIO\n");
+
+ omap_set_gpio_direction(OMAP3_EVM_TS_GPIO, 1);
+
+ omap_set_gpio_debounce(OMAP3_EVM_TS_GPIO, 1);
+ omap_set_gpio_debounce_time(OMAP3_EVM_TS_GPIO, 0xa);
+}
+
+static int ads7846_get_pendown_state(void)
+{
+ return !omap_get_gpio_datain(OMAP3_EVM_TS_GPIO);
+}
+
+struct ads7846_platform_data ads7846_config = {
+ .get_pendown_state = ads7846_get_pendown_state,
+ .keep_vref_on = 1,
+};
+
+static struct omap2_mcspi_device_config ads7846_mcspi_config = {
+ .turbo_mode = 0,
+ .single_channel = 1, /* 0: slave, 1: master */
+};
+
+struct spi_board_info omap3evm_spi_board_info[] = {
+ [0] = {
+ .modalias = "ads7846",
+ .bus_num = 1,
+ .chip_select = 0,
+ .max_speed_hz = 1500000,
+ .controller_data = &ads7846_mcspi_config,
+ .irq = OMAP_GPIO_IRQ(OMAP3_EVM_TS_GPIO),
+ .platform_data = &ads7846_config,
+ },
+};
+
+static int omap3evm_keymap[] = {
+ KEY(0, 0, KEY_LEFT),
+ KEY(0, 1, KEY_RIGHT),
+ KEY(0, 2, KEY_A),
+ KEY(0, 3, KEY_B),
+ KEY(1, 0, KEY_DOWN),
+ KEY(1, 1, KEY_UP),
+ KEY(1, 2, KEY_E),
+ KEY(1, 3, KEY_F),
+ KEY(2, 0, KEY_ENTER),
+ KEY(2, 1, KEY_I),
+ KEY(2, 2, KEY_J),
+ KEY(2, 3, KEY_K),
+ KEY(3, 0, KEY_M),
+ KEY(3, 1, KEY_N),
+ KEY(3, 2, KEY_O),
+ KEY(3, 3, KEY_P)
+};
+
+static struct omap_kp_platform_data omap3evm_kp_data = {
+ .rows = 4,
+ .cols = 4,
+ .keymap = omap3evm_keymap,
+ .keymapsize = ARRAY_SIZE(omap3evm_keymap),
+ .rep = 1,
+};
+
+static struct platform_device omap3evm_kp_device = {
+ .name = "omap_twl4030keypad",
+ .id = -1,
+ .dev = {
+ .platform_data = &omap3evm_kp_data,
+ },
+};
+
+static void __init omap3_evm_init_irq(void)
+{
+ omap2_init_common_hw(mt46h32m32lf6_sdrc_params);
+ omap_init_irq();
+ omap_gpio_init();
+ omap3evm_init_smc911x();
+}
+
+static struct omap_board_config_kernel omap3_evm_config[] __initdata = {
+ { OMAP_TAG_UART, &omap3_evm_uart_config },
+ { OMAP_TAG_MMC, &omap3_evm_mmc_config },
+ { OMAP_TAG_LCD, &omap3_evm_lcd_config },
+};
+
+static struct platform_device *omap3_evm_devices[] __initdata = {
+ &omap3_evm_lcd_device,
+ &omap3evm_kp_device,
+#ifdef CONFIG_RTC_DRV_TWL4030
+ &omap3_evm_twl4030rtc_device,
+#endif
+ &omap3evm_smc911x_device,
+};
+
+static void __init omap3_evm_init(void)
+{
+ platform_add_devices(omap3_evm_devices, ARRAY_SIZE(omap3_evm_devices));
+ omap_board_config = omap3_evm_config;
+ omap_board_config_size = ARRAY_SIZE(omap3_evm_config);
+
+ spi_register_board_info(omap3evm_spi_board_info,
+ ARRAY_SIZE(omap3evm_spi_board_info));
+
+ omap_serial_init();
+ hsmmc_init();
+ usb_musb_init();
+ usb_ehci_init();
+ omap3evm_flash_init();
+ ads7846_dev_init();
+}
+
+arch_initcall(omap3_evm_i2c_init);
+
+static void __init omap3_evm_map_io(void)
+{
+ omap2_set_globals_343x();
+ omap2_map_common_io();
+}
+
+MACHINE_START(OMAP3EVM, "OMAP3 EVM")
+ /* Maintainer: Syed Mohammed Khasim - Texas Instruments */
+ .phys_io = 0x48000000,
+ .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc,
+ .boot_params = 0x80000100,
+ .map_io = omap3_evm_map_io,
+ .init_irq = omap3_evm_init_irq,
+ .init_machine = omap3_evm_init,
+ .timer = &omap_timer,
+MACHINE_END
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/clk.h>
-#include <asm/bitops.h>
-
-#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
- #include <asm/arch/clock.h>
- #include <asm/arch/clockdomain.h>
- #include <asm/arch/sram.h>
- #include <asm/arch/cpu.h>
- #include <asm/arch/prcm.h>
+ #include <mach/clock.h>
++#include <mach/clockdomain.h>
+ #include <mach/sram.h>
+ #include <mach/cpu.h>
++#include <mach/prcm.h>
#include <asm/div64.h>
- #include <asm/arch/sdrc.h>
-#include "memory.h"
++#include <mach/sdrc.h>
#include "sdrc.h"
#include "clock.h"
#include "prm.h"
#include <linux/io.h>
#include <linux/cpufreq.h>
- #include <asm/arch/common.h>
- #include <asm/arch/clock.h>
- #include <asm/arch/sram.h>
++#include <mach/common.h>
+ #include <mach/clock.h>
+ #include <mach/sram.h>
#include <asm/div64.h>
-#include <asm/bitops.h>
- #include <asm/arch/sdrc.h>
-#include "memory.h"
++#include <mach/sdrc.h>
#include "clock.h"
#include "clock24xx.h"
#include "prm.h"
#include <asm/div64.h>
#include <asm/bitops.h>
- #include <asm/arch/sdrc.h>
-#include "memory.h"
++#include <mach/sdrc.h>
#include "clock.h"
#include "clock34xx.h"
#include "prm.h"
--- /dev/null
- #include <asm/arch/clock.h>
+/*
+ * OMAP2/3 clockdomain framework functions
+ *
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Written by Paul Walmsley and Jouni Högander
+ *
+ * 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.
+ */
+#ifdef CONFIG_OMAP_DEBUG_CLOCKDOMAIN
+# define DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/limits.h>
+
+#include <linux/io.h>
+
+#include <linux/bitops.h>
+
- #include <asm/arch/powerdomain.h>
- #include <asm/arch/clockdomain.h>
++#include <mach/clock.h>
+
+#include "prm.h"
+#include "prm-regbits-24xx.h"
+#include "cm.h"
+
++#include <mach/powerdomain.h>
++#include <mach/clockdomain.h>
+
+/* clkdm_list contains all registered struct clockdomains */
+static LIST_HEAD(clkdm_list);
+
+/* clkdm_mutex protects clkdm_list add and del ops */
+static DEFINE_MUTEX(clkdm_mutex);
+
+/* array of powerdomain deps to be added/removed when clkdm in hwsup mode */
+static struct clkdm_pwrdm_autodep *autodeps;
+
+
+/* Private functions */
+
+/*
+ * _autodep_lookup - resolve autodep pwrdm names to pwrdm pointers; store
+ * @autodep: struct clkdm_pwrdm_autodep * to resolve
+ *
+ * Resolve autodep powerdomain names to powerdomain pointers via
+ * pwrdm_lookup() and store the pointers in the autodep structure. An
+ * "autodep" is a powerdomain sleep/wakeup dependency that is
+ * automatically added and removed whenever clocks in the associated
+ * clockdomain are enabled or disabled (respectively) when the
+ * clockdomain is in hardware-supervised mode. Meant to be called
+ * once at clockdomain layer initialization, since these should remain
+ * fixed for a particular architecture. No return value.
+ */
+static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep)
+{
+ struct powerdomain *pwrdm;
+
+ if (!autodep)
+ return;
+
+ if (!omap_chip_is(autodep->omap_chip))
+ return;
+
+ pwrdm = pwrdm_lookup(autodep->pwrdm.name);
+ if (!pwrdm) {
+ pr_debug("clockdomain: _autodep_lookup: powerdomain %s "
+ "does not exist\n", autodep->pwrdm.name);
+ WARN_ON(1);
+ return;
+ }
+ autodep->pwrdm.ptr = pwrdm;
+
+ return;
+}
+
+/*
+ * _clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable
+ * @clkdm: struct clockdomain *
+ *
+ * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm'
+ * in hardware-supervised mode. Meant to be called from clock framework
+ * when a clock inside clockdomain 'clkdm' is enabled. No return value.
+ */
+static void _clkdm_add_autodeps(struct clockdomain *clkdm)
+{
+ struct clkdm_pwrdm_autodep *autodep;
+
+ for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) {
+ if (!omap_chip_is(autodep->omap_chip))
+ continue;
+
+ pr_debug("clockdomain: adding %s sleepdep/wkdep for "
+ "pwrdm %s\n", autodep->pwrdm.ptr->name,
+ clkdm->pwrdm.ptr->name);
+
+ pwrdm_add_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
+ pwrdm_add_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
+ }
+}
+
+/*
+ * _clkdm_add_autodeps - remove auto sleepdeps/wkdeps from clkdm
+ * @clkdm: struct clockdomain *
+ *
+ * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm'
+ * in hardware-supervised mode. Meant to be called from clock framework
+ * when a clock inside clockdomain 'clkdm' is disabled. No return value.
+ */
+static void _clkdm_del_autodeps(struct clockdomain *clkdm)
+{
+ struct clkdm_pwrdm_autodep *autodep;
+
+ for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) {
+ if (!omap_chip_is(autodep->omap_chip))
+ continue;
+
+ pr_debug("clockdomain: removing %s sleepdep/wkdep for "
+ "pwrdm %s\n", autodep->pwrdm.ptr->name,
+ clkdm->pwrdm.ptr->name);
+
+ pwrdm_del_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
+ pwrdm_del_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
+ }
+}
+
+
+static struct clockdomain *_clkdm_lookup(const char *name)
+{
+ struct clockdomain *clkdm, *temp_clkdm;
+
+ if (!name)
+ return NULL;
+
+ clkdm = NULL;
+
+ list_for_each_entry(temp_clkdm, &clkdm_list, node) {
+ if (!strcmp(name, temp_clkdm->name)) {
+ clkdm = temp_clkdm;
+ break;
+ }
+ }
+
+ return clkdm;
+}
+
+
+/* Public functions */
+
+/**
+ * clkdm_init - set up the clockdomain layer
+ * @clkdms: optional pointer to an array of clockdomains to register
+ * @init_autodeps: optional pointer to an array of autodeps to register
+ *
+ * Set up internal state. If a pointer to an array of clockdomains
+ * was supplied, loop through the list of clockdomains, register all
+ * that are available on the current platform. Similarly, if a
+ * pointer to an array of clockdomain-powerdomain autodependencies was
+ * provided, register those. No return value.
+ */
+void clkdm_init(struct clockdomain **clkdms,
+ struct clkdm_pwrdm_autodep *init_autodeps)
+{
+ struct clockdomain **c = NULL;
+ struct clkdm_pwrdm_autodep *autodep = NULL;
+
+ if (clkdms)
+ for (c = clkdms; *c; c++)
+ clkdm_register(*c);
+
+ autodeps = init_autodeps;
+ if (autodeps)
+ for (autodep = autodeps; autodep->pwrdm.ptr; autodep++)
+ _autodep_lookup(autodep);
+}
+
+/**
+ * clkdm_register - register a clockdomain
+ * @clkdm: struct clockdomain * to register
+ *
+ * Adds a clockdomain to the internal clockdomain list.
+ * Returns -EINVAL if given a null pointer, -EEXIST if a clockdomain is
+ * already registered by the provided name, or 0 upon success.
+ */
+int clkdm_register(struct clockdomain *clkdm)
+{
+ int ret = -EINVAL;
+ struct powerdomain *pwrdm;
+
+ if (!clkdm || !clkdm->name)
+ return -EINVAL;
+
+ if (!omap_chip_is(clkdm->omap_chip))
+ return -EINVAL;
+
+ pwrdm = pwrdm_lookup(clkdm->pwrdm.name);
+ if (!pwrdm) {
+ pr_debug("clockdomain: clkdm_register %s: powerdomain %s "
+ "does not exist\n", clkdm->name, clkdm->pwrdm.name);
+ return -EINVAL;
+ }
+ clkdm->pwrdm.ptr = pwrdm;
+
+ mutex_lock(&clkdm_mutex);
+ /* Verify that the clockdomain is not already registered */
+ if (_clkdm_lookup(clkdm->name)) {
+ ret = -EEXIST;
+ goto cr_unlock;
+ };
+
+ list_add(&clkdm->node, &clkdm_list);
+
+ pwrdm_add_clkdm(pwrdm, clkdm);
+
+ pr_debug("clockdomain: registered %s\n", clkdm->name);
+ ret = 0;
+
+cr_unlock:
+ mutex_unlock(&clkdm_mutex);
+
+ return ret;
+}
+
+/**
+ * clkdm_unregister - unregister a clockdomain
+ * @clkdm: struct clockdomain * to unregister
+ *
+ * Removes a clockdomain from the internal clockdomain list. Returns
+ * -EINVAL if clkdm argument is NULL.
+ */
+int clkdm_unregister(struct clockdomain *clkdm)
+{
+ if (!clkdm)
+ return -EINVAL;
+
+ pwrdm_del_clkdm(clkdm->pwrdm.ptr, clkdm);
+
+ mutex_lock(&clkdm_mutex);
+ list_del(&clkdm->node);
+ mutex_unlock(&clkdm_mutex);
+
+ pr_debug("clockdomain: unregistered %s\n", clkdm->name);
+
+ return 0;
+}
+
+/**
+ * clkdm_lookup - look up a clockdomain by name, return a pointer
+ * @name: name of clockdomain
+ *
+ * Find a registered clockdomain by its name. Returns a pointer to the
+ * struct clockdomain if found, or NULL otherwise.
+ */
+struct clockdomain *clkdm_lookup(const char *name)
+{
+ struct clockdomain *clkdm, *temp_clkdm;
+
+ if (!name)
+ return NULL;
+
+ clkdm = NULL;
+
+ mutex_lock(&clkdm_mutex);
+ list_for_each_entry(temp_clkdm, &clkdm_list, node) {
+ if (!strcmp(name, temp_clkdm->name)) {
+ clkdm = temp_clkdm;
+ break;
+ }
+ }
+ mutex_unlock(&clkdm_mutex);
+
+ return clkdm;
+}
+
+/**
+ * clkdm_for_each - call function on each registered clockdomain
+ * @fn: callback function *
+ *
+ * Call the supplied function for each registered clockdomain.
+ * The callback function can return anything but 0 to bail
+ * out early from the iterator. The callback function is called with
+ * the clkdm_mutex held, so no clockdomain structure manipulation
+ * functions should be called from the callback, although hardware
+ * clockdomain control functions are fine. Returns the last return
+ * value of the callback function, which should be 0 for success or
+ * anything else to indicate failure; or -EINVAL if the function pointer
+ * is null.
+ */
+int clkdm_for_each(int (*fn)(struct clockdomain *clkdm))
+{
+ struct clockdomain *clkdm;
+ int ret = 0;
+
+ if (!fn)
+ return -EINVAL;
+
+ mutex_lock(&clkdm_mutex);
+ list_for_each_entry(clkdm, &clkdm_list, node) {
+ ret = (*fn)(clkdm);
+ if (ret)
+ break;
+ }
+ mutex_unlock(&clkdm_mutex);
+
+ return ret;
+}
+
+
+/**
+ * clkdm_get_pwrdm - return a ptr to the pwrdm that this clkdm resides in
+ * @clkdm: struct clockdomain *
+ *
+ * Return a pointer to the struct powerdomain that the specified clockdomain
+ * 'clkdm' exists in, or returns NULL if clkdm argument is NULL.
+ */
+struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm)
+{
+ if (!clkdm)
+ return NULL;
+
+ return clkdm->pwrdm.ptr;
+}
+
+
+/* Hardware clockdomain control */
+
+/**
+ * omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode
+ * @clk: struct clk * of a clockdomain
+ *
+ * Return the clockdomain's current state transition mode from the
+ * corresponding domain CM_CLKSTCTRL register. Returns -EINVAL if clk
+ * is NULL or the current mode upon success.
+ */
+static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm)
+{
+ u32 v;
+
+ if (!clkdm)
+ return -EINVAL;
+
+ v = cm_read_mod_reg(clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL);
+ v &= clkdm->clktrctrl_mask;
+ v >>= __ffs(clkdm->clktrctrl_mask);
+
+ return v;
+}
+
+/**
+ * omap2_clkdm_sleep - force clockdomain sleep transition
+ * @clkdm: struct clockdomain *
+ *
+ * Instruct the CM to force a sleep transition on the specified
+ * clockdomain 'clkdm'. Returns -EINVAL if clk is NULL or if
+ * clockdomain does not support software-initiated sleep; 0 upon
+ * success.
+ */
+int omap2_clkdm_sleep(struct clockdomain *clkdm)
+{
+ if (!clkdm)
+ return -EINVAL;
+
+ if (!(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
+ pr_debug("clockdomain: %s does not support forcing "
+ "sleep via software\n", clkdm->name);
+ return -EINVAL;
+ }
+
+ pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
+
+ if (cpu_is_omap24xx()) {
+
+ cm_set_mod_reg_bits(OMAP24XX_FORCESTATE,
+ clkdm->pwrdm.ptr->prcm_offs, PM_PWSTCTRL);
+
+ } else if (cpu_is_omap34xx()) {
+
+ u32 v = (OMAP34XX_CLKSTCTRL_FORCE_SLEEP <<
+ __ffs(clkdm->clktrctrl_mask));
+
+ cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, v,
+ clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL);
+
+ } else {
+ BUG();
+ };
+
+ return 0;
+}
+
+/**
+ * omap2_clkdm_wakeup - force clockdomain wakeup transition
+ * @clkdm: struct clockdomain *
+ *
+ * Instruct the CM to force a wakeup transition on the specified
+ * clockdomain 'clkdm'. Returns -EINVAL if clkdm is NULL or if the
+ * clockdomain does not support software-controlled wakeup; 0 upon
+ * success.
+ */
+int omap2_clkdm_wakeup(struct clockdomain *clkdm)
+{
+ if (!clkdm)
+ return -EINVAL;
+
+ if (!(clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) {
+ pr_debug("clockdomain: %s does not support forcing "
+ "wakeup via software\n", clkdm->name);
+ return -EINVAL;
+ }
+
+ pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
+
+ if (cpu_is_omap24xx()) {
+
+ cm_clear_mod_reg_bits(OMAP24XX_FORCESTATE,
+ clkdm->pwrdm.ptr->prcm_offs, PM_PWSTCTRL);
+
+ } else if (cpu_is_omap34xx()) {
+
+ u32 v = (OMAP34XX_CLKSTCTRL_FORCE_WAKEUP <<
+ __ffs(clkdm->clktrctrl_mask));
+
+ cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, v,
+ clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL);
+
+ } else {
+ BUG();
+ };
+
+ return 0;
+}
+
+/**
+ * omap2_clkdm_allow_idle - enable hwsup idle transitions for clkdm
+ * @clkdm: struct clockdomain *
+ *
+ * Allow the hardware to automatically switch the clockdomain into
+ * active or idle states, as needed by downstream clocks. If the
+ * clockdomain has any downstream clocks enabled in the clock
+ * framework, wkdep/sleepdep autodependencies are added; this is so
+ * device drivers can read and write to the device. No return value.
+ */
+void omap2_clkdm_allow_idle(struct clockdomain *clkdm)
+{
+ u32 v;
+
+ if (!clkdm)
+ return;
+
+ if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO)) {
+ pr_debug("clock: automatic idle transitions cannot be enabled "
+ "on clockdomain %s\n", clkdm->name);
+ return;
+ }
+
+ pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
+ clkdm->name);
+
+ if (atomic_read(&clkdm->usecount) > 0)
+ _clkdm_add_autodeps(clkdm);
+
+ if (cpu_is_omap24xx())
+ v = OMAP24XX_CLKSTCTRL_ENABLE_AUTO;
+ else if (cpu_is_omap34xx())
+ v = OMAP34XX_CLKSTCTRL_ENABLE_AUTO;
+ else
+ BUG();
+
+
+ cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask,
+ v << __ffs(clkdm->clktrctrl_mask),
+ clkdm->pwrdm.ptr->prcm_offs,
+ CM_CLKSTCTRL);
+}
+
+/**
+ * omap2_clkdm_deny_idle - disable hwsup idle transitions for clkdm
+ * @clkdm: struct clockdomain *
+ *
+ * Prevent the hardware from automatically switching the clockdomain
+ * into inactive or idle states. If the clockdomain has downstream
+ * clocks enabled in the clock framework, wkdep/sleepdep
+ * autodependencies are removed. No return value.
+ */
+void omap2_clkdm_deny_idle(struct clockdomain *clkdm)
+{
+ u32 v;
+
+ if (!clkdm)
+ return;
+
+ if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO)) {
+ pr_debug("clockdomain: automatic idle transitions cannot be "
+ "disabled on %s\n", clkdm->name);
+ return;
+ }
+
+ pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
+ clkdm->name);
+
+ if (cpu_is_omap24xx())
+ v = OMAP24XX_CLKSTCTRL_DISABLE_AUTO;
+ else if (cpu_is_omap34xx())
+ v = OMAP34XX_CLKSTCTRL_DISABLE_AUTO;
+ else
+ BUG();
+
+ cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask,
+ v << __ffs(clkdm->clktrctrl_mask),
+ clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL);
+
+ if (atomic_read(&clkdm->usecount) > 0)
+ _clkdm_del_autodeps(clkdm);
+}
+
+
+/* Clockdomain-to-clock framework interface code */
+
+/**
+ * omap2_clkdm_clk_enable - add an enabled downstream clock to this clkdm
+ * @clkdm: struct clockdomain *
+ * @clk: struct clk * of the enabled downstream clock
+ *
+ * Increment the usecount of this clockdomain 'clkdm' and ensure that
+ * it is awake. Intended to be called by clk_enable() code. If the
+ * clockdomain is in software-supervised idle mode, force the
+ * clockdomain to wake. If the clockdomain is in hardware-supervised
+ * idle mode, add clkdm-pwrdm autodependencies, to ensure that devices
+ * in the clockdomain can be read from/written to by on-chip processors.
+ * Returns -EINVAL if passed null pointers; returns 0 upon success or
+ * if the clockdomain is in hwsup idle mode.
+ */
+int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
+{
+ int v;
+
+ /*
+ * XXX Rewrite this code to maintain a list of enabled
+ * downstream clocks for debugging purposes?
+ */
+
+ if (!clkdm || !clk)
+ return -EINVAL;
+
+ if (atomic_inc_return(&clkdm->usecount) > 1)
+ return 0;
+
+ /* Clockdomain now has one enabled downstream clock */
+
+ pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name,
+ clk->name);
+
+ v = omap2_clkdm_clktrctrl_read(clkdm);
+
+ if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ||
+ (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO))
+ _clkdm_add_autodeps(clkdm);
+ else
+ omap2_clkdm_wakeup(clkdm);
+
+ return 0;
+}
+
+/**
+ * omap2_clkdm_clk_disable - remove an enabled downstream clock from this clkdm
+ * @clkdm: struct clockdomain *
+ * @clk: struct clk * of the disabled downstream clock
+ *
+ * Decrement the usecount of this clockdomain 'clkdm'. Intended to be
+ * called by clk_disable() code. If the usecount goes to 0, put the
+ * clockdomain to sleep (software-supervised mode) or remove the
+ * clkdm-pwrdm autodependencies (hardware-supervised mode). Returns
+ * -EINVAL if passed null pointers; -ERANGE if the clkdm usecount
+ * underflows and debugging is enabled; or returns 0 upon success or
+ * if the clockdomain is in hwsup idle mode.
+ */
+int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
+{
+ int v;
+
+ /*
+ * XXX Rewrite this code to maintain a list of enabled
+ * downstream clocks for debugging purposes?
+ */
+
+ if (!clkdm || !clk)
+ return -EINVAL;
+
+#ifdef DEBUG
+ if (atomic_read(&clkdm->usecount) == 0) {
+ WARN_ON(1); /* underflow */
+ return -ERANGE;
+ }
+#endif
+
+ if (atomic_dec_return(&clkdm->usecount) > 0)
+ return 0;
+
+ /* All downstream clocks of this clockdomain are now disabled */
+
+ pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name,
+ clk->name);
+
+ v = omap2_clkdm_clktrctrl_read(clkdm);
+
+ if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ||
+ (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO))
+ _clkdm_del_autodeps(clkdm);
+ else
+ omap2_clkdm_sleep(clkdm);
+
+ return 0;
+}
+
--- /dev/null
- #include <asm/arch/clockdomain.h>
+/*
+ * OMAP2/3 clockdomains
+ *
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Written by Paul Walmsley
+ */
+
+#ifndef __ARCH_ARM_MACH_OMAP2_CLOCKDOMAINS_H
+#define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAINS_H
+
++#include <mach/clockdomain.h>
+
+/*
+ * OMAP2/3-common clockdomains
+ */
+
+/* This is an implicit clockdomain - it is never defined as such in TRM */
+static struct clockdomain wkup_clkdm = {
+ .name = "wkup_clkdm",
+ .pwrdm = { .name = "wkup_pwrdm" },
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX | CHIP_IS_OMAP3430),
+};
+
+/*
+ * 2420-only clockdomains
+ */
+
+#if defined(CONFIG_ARCH_OMAP2420)
+
+static struct clockdomain mpu_2420_clkdm = {
+ .name = "mpu_clkdm",
+ .pwrdm = { .name = "mpu_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP,
+ .clktrctrl_mask = OMAP24XX_AUTOSTATE_MPU_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420),
+};
+
+static struct clockdomain iva1_2420_clkdm = {
+ .name = "iva1_clkdm",
+ .pwrdm = { .name = "dsp_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP2420_AUTOSTATE_IVA_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2420),
+};
+
+#endif /* CONFIG_ARCH_OMAP2420 */
+
+
+/*
+ * 2430-only clockdomains
+ */
+
+#if defined(CONFIG_ARCH_OMAP2430)
+
+static struct clockdomain mpu_2430_clkdm = {
+ .name = "mpu_clkdm",
+ .pwrdm = { .name = "mpu_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP24XX_AUTOSTATE_MPU_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430),
+};
+
+static struct clockdomain mdm_clkdm = {
+ .name = "mdm_clkdm",
+ .pwrdm = { .name = "mdm_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP2430_AUTOSTATE_MDM_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430),
+};
+
+#endif /* CONFIG_ARCH_OMAP2430 */
+
+
+/*
+ * 24XX-only clockdomains
+ */
+
+#if defined(CONFIG_ARCH_OMAP24XX)
+
+static struct clockdomain dsp_clkdm = {
+ .name = "dsp_clkdm",
+ .pwrdm = { .name = "dsp_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP24XX_AUTOSTATE_DSP_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX),
+};
+
+static struct clockdomain gfx_24xx_clkdm = {
+ .name = "gfx_clkdm",
+ .pwrdm = { .name = "gfx_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP24XX_AUTOSTATE_GFX_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX),
+};
+
+static struct clockdomain core_l3_24xx_clkdm = {
+ .name = "core_l3_clkdm",
+ .pwrdm = { .name = "core_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP,
+ .clktrctrl_mask = OMAP24XX_AUTOSTATE_L3_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX),
+};
+
+static struct clockdomain core_l4_24xx_clkdm = {
+ .name = "core_l4_clkdm",
+ .pwrdm = { .name = "core_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP,
+ .clktrctrl_mask = OMAP24XX_AUTOSTATE_L4_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX),
+};
+
+static struct clockdomain dss_24xx_clkdm = {
+ .name = "dss_clkdm",
+ .pwrdm = { .name = "core_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP,
+ .clktrctrl_mask = OMAP24XX_AUTOSTATE_DSS_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX),
+};
+
+#endif /* CONFIG_ARCH_OMAP24XX */
+
+
+/*
+ * 34xx clockdomains
+ */
+
+#if defined(CONFIG_ARCH_OMAP34XX)
+
+static struct clockdomain mpu_34xx_clkdm = {
+ .name = "mpu_clkdm",
+ .pwrdm = { .name = "mpu_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP | CLKDM_CAN_FORCE_WAKEUP,
+ .clktrctrl_mask = OMAP3430_CLKTRCTRL_MPU_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
+static struct clockdomain neon_clkdm = {
+ .name = "neon_clkdm",
+ .pwrdm = { .name = "neon_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP3430_CLKTRCTRL_NEON_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
+static struct clockdomain iva2_clkdm = {
+ .name = "iva2_clkdm",
+ .pwrdm = { .name = "iva2_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP3430_CLKTRCTRL_IVA2_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
+static struct clockdomain gfx_3430es1_clkdm = {
+ .name = "gfx_clkdm",
+ .pwrdm = { .name = "gfx_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP3430ES1_CLKTRCTRL_GFX_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
+};
+
+static struct clockdomain sgx_clkdm = {
+ .name = "sgx_clkdm",
+ .pwrdm = { .name = "sgx_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP3430ES2_CLKTRCTRL_SGX_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES2),
+};
+
+/*
+ * The die-to-die clockdomain was documented in the 34xx ES1 TRM, but
+ * then that information was removed from the 34xx ES2+ TRM. It is
+ * unclear whether the core is still there, but the clockdomain logic
+ * is there, and must be programmed to an appropriate state if the
+ * CORE clockdomain is to become inactive.
+ */
+static struct clockdomain d2d_clkdm = {
+ .name = "d2d_clkdm",
+ .pwrdm = { .name = "core_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP,
+ .clktrctrl_mask = OMAP3430ES1_CLKTRCTRL_D2D_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
+static struct clockdomain core_l3_34xx_clkdm = {
+ .name = "core_l3_clkdm",
+ .pwrdm = { .name = "core_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP,
+ .clktrctrl_mask = OMAP3430_CLKTRCTRL_L3_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
+static struct clockdomain core_l4_34xx_clkdm = {
+ .name = "core_l4_clkdm",
+ .pwrdm = { .name = "core_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP,
+ .clktrctrl_mask = OMAP3430_CLKTRCTRL_L4_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
+static struct clockdomain dss_34xx_clkdm = {
+ .name = "dss_clkdm",
+ .pwrdm = { .name = "dss_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP3430_CLKTRCTRL_DSS_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
+static struct clockdomain cam_clkdm = {
+ .name = "cam_clkdm",
+ .pwrdm = { .name = "cam_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP3430_CLKTRCTRL_CAM_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
+static struct clockdomain usbhost_clkdm = {
+ .name = "usbhost_clkdm",
+ .pwrdm = { .name = "usbhost_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP3430ES2_CLKTRCTRL_USBHOST_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES2),
+};
+
+static struct clockdomain per_clkdm = {
+ .name = "per_clkdm",
+ .pwrdm = { .name = "per_pwrdm" },
+ .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .clktrctrl_mask = OMAP3430_CLKTRCTRL_PER_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
+static struct clockdomain emu_clkdm = {
+ .name = "emu_clkdm",
+ .pwrdm = { .name = "emu_pwrdm" },
+ .flags = CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_SWSUP,
+ .clktrctrl_mask = OMAP3430_CLKTRCTRL_EMU_MASK,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
+#endif /* CONFIG_ARCH_OMAP34XX */
+
+/*
+ * Clockdomain-powerdomain hwsup dependencies (34XX only)
+ */
+
+static struct clkdm_pwrdm_autodep clkdm_pwrdm_autodeps[] = {
+ {
+ .pwrdm = { .name = "mpu_pwrdm" },
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm = { .name = "iva2_pwrdm" },
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm = { .name = NULL },
+ }
+};
+
+/*
+ *
+ */
+
+static struct clockdomain *clockdomains_omap[] = {
+
+ &wkup_clkdm,
+
+#ifdef CONFIG_ARCH_OMAP2420
+ &mpu_2420_clkdm,
+ &iva1_2420_clkdm,
+#endif
+
+#ifdef CONFIG_ARCH_OMAP2430
+ &mpu_2430_clkdm,
+ &mdm_clkdm,
+#endif
+
+#ifdef CONFIG_ARCH_OMAP24XX
+ &dsp_clkdm,
+ &gfx_24xx_clkdm,
+ &core_l3_24xx_clkdm,
+ &core_l4_24xx_clkdm,
+ &dss_24xx_clkdm,
+#endif
+
+#ifdef CONFIG_ARCH_OMAP34XX
+ &mpu_34xx_clkdm,
+ &neon_clkdm,
+ &iva2_clkdm,
+ &gfx_3430es1_clkdm,
+ &sgx_clkdm,
+ &d2d_clkdm,
+ &core_l3_34xx_clkdm,
+ &core_l4_34xx_clkdm,
+ &dss_34xx_clkdm,
+ &cam_clkdm,
+ &usbhost_clkdm,
+ &per_clkdm,
+ &emu_clkdm,
+#endif
+
+ NULL,
+};
+
+#endif
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/io.h>
- #include <asm/hardware.h>
+ #include <mach/hardware.h>
-#include <asm/io.h>
#include <asm/mach-types.h>
#include <asm/mach/map.h>
- #include <asm/arch/tc.h>
- #include <asm/arch/board.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/eac.h>
+ #include <mach/tc.h>
+ #include <mach/board.h>
+ #include <mach/mux.h>
+ #include <mach/gpio.h>
++#include <mach/eac.h>
-#if defined(CONFIG_I2C_OMAP) || defined(CONFIG_I2C_OMAP_MODULE)
+#if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE)
-#define OMAP2_I2C_BASE2 0x48072000
-#define OMAP2_I2C_INT2 57
-
-static struct resource i2c_resources2[] = {
+static struct resource cam_resources[] = {
{
- .start = OMAP2_I2C_BASE2,
- .end = OMAP2_I2C_BASE2 + 0x3f,
+ .start = OMAP24XX_CAMERA_BASE,
+ .end = OMAP24XX_CAMERA_BASE + 0xfff,
.flags = IORESOURCE_MEM,
},
{
static inline void omap_init_sti(void) {}
#endif
-#if defined(CONFIG_SPI_OMAP24XX)
+#if defined(CONFIG_SPI_OMAP24XX) || defined(CONFIG_SPI_OMAP24XX_MODULE)
- #include <asm/arch/mcspi.h>
+ #include <mach/mcspi.h>
#define OMAP2_MCSPI1_BASE 0x48098000
#define OMAP2_MCSPI2_BASE 0x4809a000
#include <linux/clk.h>
#include <linux/ioport.h>
#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/io.h>
-#include <asm/io.h>
#include <asm/mach-types.h>
- #include <asm/arch/gpmc.h>
+ #include <mach/gpmc.h>
- #include <asm/arch/sdrc.h>
-#undef DEBUG
-
-#ifdef CONFIG_ARCH_OMAP2420
-#define GPMC_BASE 0x6800a000
-#endif
-
-#ifdef CONFIG_ARCH_OMAP2430
-#define GPMC_BASE 0x6E000000
-#endif
++#include <mach/sdrc.h>
+/* GPMC register offsets */
#define GPMC_REVISION 0x00
#define GPMC_SYSCONFIG 0x10
#define GPMC_SYSSTATUS 0x14
--- /dev/null
- #include <asm/hardware.h>
- #include <asm/arch/mmc.h>
- #include <asm/arch/board.h>
+/*
+ * linux/arch/arm/mach-omap2/board-sdp-hsmmc.c
+ *
+ * Copyright (C) 2007-2008 Texas Instruments
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Texas Instruments
+ *
+ * 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/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/i2c/twl4030.h>
++#include <mach/hardware.h>
++#include <mach/mmc.h>
++#include <mach/board.h>
+
+#if defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
+
+#define VMMC1_DEV_GRP 0x27
+#define P1_DEV_GRP 0x20
+#define VMMC1_DEDICATED 0x2A
+#define VSEL_3V 0x02
+#define VSEL_18V 0x00
+#define TWL_GPIO_PUPDCTR1 0x13
+#define TWL_GPIO_IMR1A 0x1C
+#define TWL_GPIO_ISR1A 0x19
+#define LDO_CLR 0x00
+#define VSEL_S2_CLR 0x40
+#define GPIO_0_BIT_POS (1 << 0)
+#define MMC1_CD_IRQ 0
+#define MMC2_CD_IRQ 1
+
+#define OMAP2_CONTROL_DEVCONF0 0x48002274
+#define OMAP2_CONTROL_DEVCONF1 0x490022E8
+
+#define OMAP2_CONTROL_DEVCONF0_LBCLK (1 << 24)
+#define OMAP2_CONTROL_DEVCONF1_ACTOV (1 << 31)
+
+#define OMAP2_CONTROL_PBIAS_VMODE (1 << 0)
+#define OMAP2_CONTROL_PBIAS_PWRDNZ (1 << 1)
+#define OMAP2_CONTROL_PBIAS_SCTRL (1 << 2)
+
+static int hsmmc_card_detect(int irq)
+{
+ return twl4030_get_gpio_datain(irq - TWL4030_GPIO_IRQ_BASE);
+}
+
+/*
+ * MMC Slot Initialization.
+ */
+static int hsmmc_late_init(struct device *dev)
+{
+ int ret = 0;
+
+ /*
+ * Configure TWL4030 GPIO parameters for MMC hotplug irq
+ */
+ ret = twl4030_request_gpio(MMC1_CD_IRQ);
+ if (ret)
+ goto err;
+
+ ret = twl4030_set_gpio_edge_ctrl(MMC1_CD_IRQ,
+ TWL4030_GPIO_EDGE_RISING | TWL4030_GPIO_EDGE_FALLING);
+ if (ret)
+ goto err;
+
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0x02,
+ TWL_GPIO_PUPDCTR1);
+ if (ret)
+ goto err;
+
+ ret = twl4030_set_gpio_debounce(MMC1_CD_IRQ, TWL4030_GPIO_IS_ENABLE);
+ if (ret)
+ goto err;
+
+ return ret;
+
+err:
+ dev_err(dev, "Failed to configure TWL4030 GPIO IRQ\n");
+ return ret;
+}
+
+static void hsmmc_cleanup(struct device *dev)
+{
+ int ret = 0;
+
+ ret = twl4030_free_gpio(MMC1_CD_IRQ);
+ if (ret)
+ dev_err(dev, "Failed to configure TWL4030 GPIO IRQ\n");
+}
+
+#ifdef CONFIG_PM
+
+/*
+ * To mask and unmask MMC Card Detect Interrupt
+ * mask : 1
+ * unmask : 0
+ */
+static int mask_cd_interrupt(int mask)
+{
+ u8 reg = 0, ret = 0;
+
+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, ®, TWL_GPIO_IMR1A);
+ if (ret)
+ goto err;
+
+ reg = (mask == 1) ? (reg | GPIO_0_BIT_POS) : (reg & ~GPIO_0_BIT_POS);
+
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, reg, TWL_GPIO_IMR1A);
+ if (ret)
+ goto err;
+
+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, ®, TWL_GPIO_ISR1A);
+ if (ret)
+ goto err;
+
+ reg = (mask == 1) ? (reg | GPIO_0_BIT_POS) : (reg & ~GPIO_0_BIT_POS);
+
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, reg, TWL_GPIO_ISR1A);
+ if (ret)
+ goto err;
+
+err:
+ return ret;
+}
+
+static int hsmmc_suspend(struct device *dev, int slot)
+{
+ int ret = 0;
+
+ disable_irq(TWL4030_GPIO_IRQ_NO(MMC1_CD_IRQ));
+ ret = mask_cd_interrupt(1);
+
+ return ret;
+}
+
+static int hsmmc_resume(struct device *dev, int slot)
+{
+ int ret = 0;
+
+ enable_irq(TWL4030_GPIO_IRQ_NO(MMC1_CD_IRQ));
+ ret = mask_cd_interrupt(0);
+
+ return ret;
+}
+
+#endif
+
+static int hsmmc_set_power(struct device *dev, int slot, int power_on,
+ int vdd)
+{
+ u32 vdd_sel = 0, devconf = 0, reg = 0;
+ int ret = 0;
+
+ /* REVISIT: Using address directly till the control.h defines
+ * are settled.
+ */
+#if defined(CONFIG_ARCH_OMAP2430)
+ #define OMAP2_CONTROL_PBIAS 0x490024A0
+#else
+ #define OMAP2_CONTROL_PBIAS 0x48002520
+#endif
+
+ if (power_on) {
+ if (cpu_is_omap24xx())
+ devconf = omap_readl(OMAP2_CONTROL_DEVCONF1);
+ else
+ devconf = omap_readl(OMAP2_CONTROL_DEVCONF0);
+
+ switch (1 << vdd) {
+ case MMC_VDD_33_34:
+ case MMC_VDD_32_33:
+ vdd_sel = VSEL_3V;
+ if (cpu_is_omap24xx())
+ devconf |= OMAP2_CONTROL_DEVCONF1_ACTOV;
+ break;
+ case MMC_VDD_165_195:
+ vdd_sel = VSEL_18V;
+ if (cpu_is_omap24xx())
+ devconf &= ~OMAP2_CONTROL_DEVCONF1_ACTOV;
+ }
+
+ if (cpu_is_omap24xx())
+ omap_writel(devconf, OMAP2_CONTROL_DEVCONF1);
+ else
+ omap_writel(devconf | OMAP2_CONTROL_DEVCONF0_LBCLK,
+ OMAP2_CONTROL_DEVCONF0);
+
+ reg = omap_readl(OMAP2_CONTROL_PBIAS);
+ reg |= OMAP2_CONTROL_PBIAS_SCTRL;
+ omap_writel(reg, OMAP2_CONTROL_PBIAS);
+
+ reg = omap_readl(OMAP2_CONTROL_PBIAS);
+ reg &= ~OMAP2_CONTROL_PBIAS_PWRDNZ;
+ omap_writel(reg, OMAP2_CONTROL_PBIAS);
+
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ P1_DEV_GRP, VMMC1_DEV_GRP);
+ if (ret)
+ goto err;
+
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ vdd_sel, VMMC1_DEDICATED);
+ if (ret)
+ goto err;
+
+ msleep(100);
+ reg = omap_readl(OMAP2_CONTROL_PBIAS);
+ reg |= (OMAP2_CONTROL_PBIAS_SCTRL |
+ OMAP2_CONTROL_PBIAS_PWRDNZ);
+ if (vdd_sel == VSEL_18V)
+ reg &= ~OMAP2_CONTROL_PBIAS_VMODE;
+ else
+ reg |= OMAP2_CONTROL_PBIAS_VMODE;
+ omap_writel(reg, OMAP2_CONTROL_PBIAS);
+
+ return ret;
+
+ } else {
+ /* Power OFF */
+
+ /* For MMC1, Toggle PBIAS before every power up sequence */
+ reg = omap_readl(OMAP2_CONTROL_PBIAS);
+ reg &= ~OMAP2_CONTROL_PBIAS_PWRDNZ;
+ omap_writel(reg, OMAP2_CONTROL_PBIAS);
+
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ LDO_CLR, VMMC1_DEV_GRP);
+ if (ret)
+ goto err;
+
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ VSEL_S2_CLR, VMMC1_DEDICATED);
+ if (ret)
+ goto err;
+
+ /* 100ms delay required for PBIAS configuration */
+ msleep(100);
+ reg = omap_readl(OMAP2_CONTROL_PBIAS);
+ reg |= (OMAP2_CONTROL_PBIAS_VMODE |
+ OMAP2_CONTROL_PBIAS_PWRDNZ |
+ OMAP2_CONTROL_PBIAS_SCTRL);
+ omap_writel(reg, OMAP2_CONTROL_PBIAS);
+ }
+
+ return 0;
+
+err:
+ return 1;
+}
+
+static struct omap_mmc_platform_data hsmmc_data = {
+ .nr_slots = 1,
+ .switch_slot = NULL,
+ .init = hsmmc_late_init,
+ .cleanup = hsmmc_cleanup,
+#ifdef CONFIG_PM
+ .suspend = hsmmc_suspend,
+ .resume = hsmmc_resume,
+#endif
+ .slots[0] = {
+ .set_power = hsmmc_set_power,
+ .set_bus_mode = NULL,
+ .get_ro = NULL,
+ .get_cover_state = NULL,
+ .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34 |
+ MMC_VDD_165_195,
+ .name = "first slot",
+
+ .card_detect_irq = TWL4030_GPIO_IRQ_NO(MMC1_CD_IRQ),
+ .card_detect = hsmmc_card_detect,
+ },
+};
+
+void __init hsmmc_init(void)
+{
+ omap_set_mmc_info(1, &hsmmc_data);
+}
+
+#else
+
+void __init hsmmc_init(void)
+{
+
+}
+
+#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/io.h>
- #include <asm/arch/common.h>
- #include <asm/arch/control.h>
- #include <asm/arch/cpu.h>
-#include <asm/io.h>
-
++#include <mach/common.h>
+ #include <mach/control.h>
+ #include <mach/cpu.h>
-#if defined(CONFIG_ARCH_OMAP2420)
-#define TAP_BASE io_p2v(0x48014000)
-#elif defined(CONFIG_ARCH_OMAP2430)
-#define TAP_BASE io_p2v(0x4900A000)
-#elif defined(CONFIG_ARCH_OMAP34XX)
-#define TAP_BASE io_p2v(0x4830A000)
-#endif
+static u32 class;
+static void __iomem *tap_base;
+static u16 tap_prod_id;
#define OMAP_TAP_IDCODE 0x0204
-#if defined(CONFIG_ARCH_OMAP34XX)
-#define OMAP_TAP_PROD_ID 0x0210
-#else
-#define OMAP_TAP_PROD_ID 0x0208
-#endif
-
#define OMAP_TAP_DIE_ID_0 0x0218
#define OMAP_TAP_DIE_ID_1 0x021C
#define OMAP_TAP_DIE_ID_2 0x0220
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/io.h>
#include <asm/tlb.h>
-#include <asm/io.h>
#include <asm/mach/map.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/omapfb.h>
- #include <asm/arch/sram.h>
- #include <asm/arch/sdrc.h>
- #include <asm/arch/gpmc.h>
-
+ #include <mach/mux.h>
+ #include <mach/omapfb.h>
++#include <mach/sram.h>
++#include <mach/sdrc.h>
++#include <mach/gpmc.h>
+
+#include "clock.h"
+
- #include <asm/arch/powerdomain.h>
++#include <mach/powerdomain.h>
+
+#include "powerdomains.h"
- #include <asm/arch/clockdomain.h>
-extern void omap_sram_init(void);
-extern int omap2_clk_init(void);
-extern void omap2_check_revision(void);
-extern void omap2_init_memory(void);
-extern void gpmc_init(void);
-extern void omapfb_reserve_sdram(void);
++#include <mach/clockdomain.h>
+#include "clockdomains.h"
/*
* The machine specific code may provide the extra mapping besides the
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
- #include <asm/hardware.h>
+ #include <mach/hardware.h>
#include <asm/mach/irq.h>
-#include <asm/irq.h>
-#include <asm/io.h>
+#include <linux/irq.h>
+#include <linux/io.h>
-#define INTC_REVISION 0x0000
-#define INTC_SYSCONFIG 0x0010
-#define INTC_SYSSTATUS 0x0014
-#define INTC_CONTROL 0x0048
-#define INTC_MIR_CLEAR0 0x0088
-#define INTC_MIR_SET0 0x008c
+/* selected INTC register offsets */
+
+#define INTC_REVISION 0x0000
+#define INTC_SYSCONFIG 0x0010
+#define INTC_SYSSTATUS 0x0014
+#define INTC_CONTROL 0x0048
+#define INTC_MIR_CLEAR0 0x0088
+#define INTC_MIR_SET0 0x008c
+#define INTC_PENDING_IRQ0 0x0098
+
+/* Number of IRQ state bits in each MIR register */
+#define IRQ_BITS_PER_REG 32
/*
* OMAP2 has a number of different interrupt controllers, each interrupt
--- /dev/null
- #include <asm/arch/mmu.h>
+/*
+ * linux/arch/arm/mach-omap2/mmu.c
+ *
+ * Support for non-MPU OMAP2 MMUs.
+ *
+ * Copyright (C) 2002-2007 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ * and Paul Mundt <paul.mundt@nokia.com>
+ *
+ * TWL support: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
+ *
+ * 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/types.h>
+#include <linux/init.h>
+#include <linux/rwsem.h>
+#include <linux/device.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include "mmu.h"
++#include <mach/mmu.h>
+#include <asm/tlbflush.h>
+#include <asm/sizes.h>
+
+static void *dspvect_page;
+#define DSP_INIT_PAGE 0xfff000
+
+static inline void
+omap2_mmu_read_tlb(struct omap_mmu *mmu, struct cam_ram_regset *cr)
+{
+ cr->cam = omap_mmu_read_reg(mmu, OMAP_MMU_READ_CAM);
+ cr->ram = omap_mmu_read_reg(mmu, OMAP_MMU_READ_RAM);
+}
+
+static inline void
+omap2_mmu_load_tlb(struct omap_mmu *mmu, struct cam_ram_regset *cr)
+{
+ /* Set the CAM and RAM entries */
+ omap_mmu_write_reg(mmu, cr->cam | OMAP_MMU_CAM_V, OMAP_MMU_CAM);
+ omap_mmu_write_reg(mmu, cr->ram, OMAP_MMU_RAM);
+}
+
+static void exmap_setup_iomap_page(struct omap_mmu *mmu, unsigned long phys,
+ unsigned long dsp_io_adr, int index)
+{
+ unsigned long dspadr;
+ void *virt;
+ struct omap_mmu_tlb_entry tlb_ent;
+
+ dspadr = (IOMAP_VAL << 18) + (dsp_io_adr << 1);
+ virt = omap_mmu_to_virt(mmu, dspadr);
+ exmap_set_armmmu(mmu, (unsigned long)virt, phys, PAGE_SIZE);
+ INIT_EXMAP_TBL_ENTRY_4KB_PRESERVED(mmu->exmap_tbl + index, NULL, virt);
+ INIT_TLB_ENTRY_4KB_ES32_PRESERVED(&tlb_ent, dspadr, phys);
+ omap_mmu_load_pte_entry(mmu, &tlb_ent);
+}
+
+static void exmap_clear_iomap_page(struct omap_mmu *mmu,
+ unsigned long dsp_io_adr)
+{
+ unsigned long dspadr;
+ void *virt;
+
+ dspadr = (IOMAP_VAL << 18) + (dsp_io_adr << 1);
+ virt = omap_mmu_to_virt(mmu, dspadr);
+ exmap_clear_armmmu(mmu, (unsigned long)virt, PAGE_SIZE);
+ /* DSP MMU is shutting down. not handled here. */
+}
+
+#define OMAP24XX_MAILBOX_BASE (L4_24XX_BASE + 0x94000)
+#define OMAP2420_GPT5_BASE (L4_24XX_BASE + 0x7c000)
+#define OMAP2420_GPT6_BASE (L4_24XX_BASE + 0x7e000)
+#define OMAP2420_GPT7_BASE (L4_24XX_BASE + 0x80000)
+#define OMAP2420_GPT8_BASE (L4_24XX_BASE + 0x82000)
+#define OMAP24XX_EAC_BASE (L4_24XX_BASE + 0x90000)
+#define OMAP24XX_STI_BASE (L4_24XX_BASE + 0x68000)
+#define OMAP24XX_STI_CH_BASE (L4_24XX_BASE + 0x0c000000)
+
+static int exmap_setup_preserved_entries(struct omap_mmu *mmu)
+{
+ int i, n = 0;
+
+ exmap_setup_preserved_mem_page(mmu, dspvect_page, DSP_INIT_PAGE, n++);
+
+ /* REVISIT: This will need to be revisited for 3430 */
+ exmap_setup_iomap_page(mmu, OMAP2_PRCM_BASE, 0x7000, n++);
+ exmap_setup_iomap_page(mmu, OMAP24XX_MAILBOX_BASE, 0x11000, n++);
+
+ if (cpu_is_omap2420()) {
+ exmap_setup_iomap_page(mmu, OMAP2420_GPT5_BASE, 0xe000, n++);
+ exmap_setup_iomap_page(mmu, OMAP2420_GPT6_BASE, 0xe800, n++);
+ exmap_setup_iomap_page(mmu, OMAP2420_GPT7_BASE, 0xf000, n++);
+ exmap_setup_iomap_page(mmu, OMAP2420_GPT8_BASE, 0xf800, n++);
+ exmap_setup_iomap_page(mmu, OMAP24XX_EAC_BASE, 0x10000, n++);
+ exmap_setup_iomap_page(mmu, OMAP24XX_STI_BASE, 0xc800, n++);
+ for (i = 0; i < 5; i++)
+ exmap_setup_preserved_mem_page(mmu,
+ __va(OMAP24XX_STI_CH_BASE + i*SZ_4K),
+ 0xfb0000 + i*SZ_4K, n++);
+ }
+
+ return n;
+}
+
+static void exmap_clear_preserved_entries(struct omap_mmu *mmu)
+{
+ int i;
+
+ exmap_clear_iomap_page(mmu, 0x7000); /* PRCM registers */
+ exmap_clear_iomap_page(mmu, 0x11000); /* MAILBOX registers */
+
+ if (cpu_is_omap2420()) {
+ exmap_clear_iomap_page(mmu, 0xe000); /* GPT5 */
+ exmap_clear_iomap_page(mmu, 0xe800); /* GPT6 */
+ exmap_clear_iomap_page(mmu, 0xf000); /* GPT7 */
+ exmap_clear_iomap_page(mmu, 0xf800); /* GPT8 */
+ exmap_clear_iomap_page(mmu, 0x10000); /* EAC */
+ exmap_clear_iomap_page(mmu, 0xc800); /* STI */
+ for (i = 0; i < 5; i++) /* STI CH */
+ exmap_clear_mem_page(mmu, 0xfb0000 + i*SZ_4K);
+ }
+
+ exmap_clear_mem_page(mmu, DSP_INIT_PAGE);
+}
+
+#define MMU_IRQ_MASK \
+ (OMAP_MMU_IRQ_MULTIHITFAULT | \
+ OMAP_MMU_IRQ_TABLEWALKFAULT | \
+ OMAP_MMU_IRQ_EMUMISS | \
+ OMAP_MMU_IRQ_TRANSLATIONFAULT)
+
+static int omap2_mmu_startup(struct omap_mmu *mmu)
+{
+ u32 rev = omap_mmu_read_reg(mmu, OMAP_MMU_REVISION);
+
+ pr_info("MMU: OMAP %s MMU initialized (HW v%d.%d)\n", mmu->name,
+ (rev >> 4) & 0xf, rev & 0xf);
+
+ dspvect_page = (void *)__get_dma_pages(GFP_KERNEL, 0);
+ if (dspvect_page == NULL) {
+ dev_err(mmu->dev, "MMU %s: failed to allocate memory "
+ "for vector table\n", mmu->name);
+ return -ENOMEM;
+ }
+
+ mmu->nr_exmap_preserved = exmap_setup_preserved_entries(mmu);
+
+ omap_mmu_write_reg(mmu, MMU_IRQ_MASK, OMAP_MMU_IRQENABLE);
+
+ return 0;
+}
+
+static void omap2_mmu_shutdown(struct omap_mmu *mmu)
+{
+ exmap_clear_preserved_entries(mmu);
+
+ if (dspvect_page != NULL) {
+ unsigned long virt;
+
+ down_read(&mmu->exmap_sem);
+
+ virt = (unsigned long)omap_mmu_to_virt(mmu, DSP_INIT_PAGE);
+ flush_tlb_kernel_range(virt, virt + PAGE_SIZE);
+ free_page((unsigned long)dspvect_page);
+ dspvect_page = NULL;
+
+ up_read(&mmu->exmap_sem);
+ }
+}
+
+static ssize_t omap2_mmu_show(struct omap_mmu *mmu, char *buf,
+ struct omap_mmu_tlb_lock *tlb_lock)
+{
+ int i, len;
+
+ len = sprintf(buf, "P: preserved, V: valid\n"
+ "B: big endian, L:little endian, "
+ "M: mixed page attribute\n"
+ "ety P V size cam_va ram_pa E ES M\n");
+ /* 00: P V 4KB 0x300000 0x10171800 B 16 M */
+
+ for (i = 0; i < mmu->nr_tlb_entries; i++) {
+ struct omap_mmu_tlb_entry ent;
+ struct cam_ram_regset cr;
+ struct omap_mmu_tlb_lock entry_lock;
+ char *pgsz_str, *elsz_str;
+
+ /* read a TLB entry */
+ entry_lock.base = tlb_lock->base;
+ entry_lock.victim = i;
+ omap_mmu_read_tlb(mmu, &entry_lock, &cr);
+
+ ent.pgsz = cr.cam & OMAP_MMU_CAM_PAGESIZE_MASK;
+ ent.prsvd = cr.cam & OMAP_MMU_CAM_P;
+ ent.valid = cr.cam & OMAP_MMU_CAM_V;
+ ent.va = cr.cam & OMAP_MMU_CAM_VATAG_MASK;
+ ent.endian = cr.ram & OMAP_MMU_RAM_ENDIANNESS;
+ ent.elsz = cr.ram & OMAP_MMU_RAM_ELEMENTSIZE_MASK;
+ ent.pa = cr.ram & OMAP_MMU_RAM_PADDR_MASK;
+ ent.mixed = cr.ram & OMAP_MMU_RAM_MIXED;
+
+ pgsz_str = (ent.pgsz == OMAP_MMU_CAM_PAGESIZE_16MB) ? "64MB":
+ (ent.pgsz == OMAP_MMU_CAM_PAGESIZE_1MB) ? " 1MB":
+ (ent.pgsz == OMAP_MMU_CAM_PAGESIZE_64KB) ? "64KB":
+ (ent.pgsz == OMAP_MMU_CAM_PAGESIZE_4KB) ? " 4KB":
+ " ???";
+ elsz_str = (ent.elsz == OMAP_MMU_RAM_ELEMENTSIZE_8) ? " 8":
+ (ent.elsz == OMAP_MMU_RAM_ELEMENTSIZE_16) ? "16":
+ (ent.elsz == OMAP_MMU_RAM_ELEMENTSIZE_32) ? "32":
+ "??";
+
+ if (i == tlb_lock->base)
+ len += sprintf(buf + len, "lock base = %d\n",
+ tlb_lock->base);
+ if (i == tlb_lock->victim)
+ len += sprintf(buf + len, "victim = %d\n",
+ tlb_lock->victim);
+
+ len += sprintf(buf + len,
+ /* 00: P V 4KB 0x300000 0x10171800 B 16 M */
+ "%02d: %c %c %s 0x%06lx 0x%08lx %c %s %c\n",
+ i,
+ ent.prsvd ? 'P' : ' ',
+ ent.valid ? 'V' : ' ',
+ pgsz_str, ent.va, ent.pa,
+ ent.endian ? 'B' : 'L',
+ elsz_str,
+ ent.mixed ? 'M' : ' ');
+ }
+
+ return len;
+}
+
+#define get_cam_va_mask(pgsz) \
+ (((pgsz) == OMAP_MMU_CAM_PAGESIZE_16MB) ? 0xff000000 : \
+ ((pgsz) == OMAP_MMU_CAM_PAGESIZE_1MB) ? 0xfff00000 : \
+ ((pgsz) == OMAP_MMU_CAM_PAGESIZE_64KB) ? 0xffff0000 : \
+ ((pgsz) == OMAP_MMU_CAM_PAGESIZE_4KB) ? 0xfffff000 : 0)
+
+static inline unsigned long omap2_mmu_cam_va(struct cam_ram_regset *cr)
+{
+ unsigned int page_size = cr->cam & OMAP_MMU_CAM_PAGESIZE_MASK;
+ unsigned int mask = get_cam_va_mask(cr->cam & page_size);
+
+ return cr->cam & mask;
+}
+
+static struct cam_ram_regset *
+omap2_mmu_cam_ram_alloc(struct omap_mmu *mmu, struct omap_mmu_tlb_entry *entry)
+{
+ struct cam_ram_regset *cr;
+
+ if (entry->va & ~(get_cam_va_mask(entry->pgsz))) {
+ dev_err(mmu->dev, "MMU %s: mapping vadr (0x%06lx) is not on"
+ " an aligned boundary\n", mmu->name, entry->va);
+ return ERR_PTR(-EINVAL);
+ }
+
+ cr = kmalloc(sizeof(struct cam_ram_regset), GFP_KERNEL);
+
+ cr->cam = (entry->va & OMAP_MMU_CAM_VATAG_MASK) |
+ entry->prsvd | entry->pgsz;
+ cr->ram = entry->pa | entry->endian | entry->elsz;
+
+ return cr;
+}
+
+static inline int omap2_mmu_cam_ram_valid(struct cam_ram_regset *cr)
+{
+ return cr->cam & OMAP_MMU_CAM_V;
+}
+
+static void omap2_mmu_interrupt(struct omap_mmu *mmu)
+{
+ unsigned long status, va;
+
+ status = MMU_IRQ_MASK & omap_mmu_read_reg(mmu, OMAP_MMU_IRQSTATUS);
+ va = omap_mmu_read_reg(mmu, OMAP_MMU_FAULT_AD);
+
+ pr_info("%s\n", (status & OMAP_MMU_IRQ_MULTIHITFAULT)?
+ "multi hit":"");
+ pr_info("%s\n", (status & OMAP_MMU_IRQ_TABLEWALKFAULT)?
+ "table walk fault":"");
+ pr_info("%s\n", (status & OMAP_MMU_IRQ_EMUMISS)?
+ "EMU miss":"");
+ pr_info("%s\n", (status & OMAP_MMU_IRQ_TRANSLATIONFAULT)?
+ "translation fault":"");
+ pr_info("%s\n", (status & OMAP_MMU_IRQ_TLBMISS)?
+ "TLB miss":"");
+ pr_info("fault address = %#08lx\n", va);
+
+ omap_mmu_disable(mmu);
+ omap_mmu_write_reg(mmu, status, OMAP_MMU_IRQSTATUS);
+
+ mmu->fault_address = va;
+ schedule_work(&mmu->irq_work);
+}
+
+static pgprot_t omap2_mmu_pte_get_attr(struct omap_mmu_tlb_entry *entry)
+{
+ u32 attr;
+
+ attr = entry->mixed << 5;
+ attr |= entry->endian;
+ attr |= entry->elsz >> 3;
+ attr <<= ((entry->pgsz & OMAP_MMU_CAM_PAGESIZE_4KB) ? 0:6);
+
+ return attr;
+}
+
+struct omap_mmu_ops omap2_mmu_ops = {
+ .startup = omap2_mmu_startup,
+ .shutdown = omap2_mmu_shutdown,
+ .read_tlb = omap2_mmu_read_tlb,
+ .load_tlb = omap2_mmu_load_tlb,
+ .show = omap2_mmu_show,
+ .cam_va = omap2_mmu_cam_va,
+ .cam_ram_alloc = omap2_mmu_cam_ram_alloc,
+ .cam_ram_valid = omap2_mmu_cam_ram_valid,
+ .interrupt = omap2_mmu_interrupt,
+ .pte_get_attr = omap2_mmu_pte_get_attr,
+};
+EXPORT_SYMBOL_GPL(omap2_mmu_ops);
+
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/mmu.h>
+#ifndef __MACH_OMAP2_MMU_H
+#define __MACH_OMAP2_MMU_H
+
+#include <linux/io.h>
++#include <mach/mmu.h>
+
+#define MMU_LOCK_BASE_MASK (0x1f << 10)
+#define MMU_LOCK_VICTIM_MASK (0x1f << 4)
+
+#define OMAP_MMU_REVISION 0x00
+#define OMAP_MMU_SYSCONFIG 0x10
+#define OMAP_MMU_SYSSTATUS 0x14
+#define OMAP_MMU_IRQSTATUS 0x18
+#define OMAP_MMU_IRQENABLE 0x1c
+#define OMAP_MMU_WALKING_ST 0x40
+#define OMAP_MMU_CNTL 0x44
+#define OMAP_MMU_FAULT_AD 0x48
+#define OMAP_MMU_TTB 0x4c
+#define OMAP_MMU_LOCK 0x50
+#define OMAP_MMU_LD_TLB 0x54
+#define OMAP_MMU_CAM 0x58
+#define OMAP_MMU_RAM 0x5c
+#define OMAP_MMU_GFLUSH 0x60
+#define OMAP_MMU_FLUSH_ENTRY 0x64
+#define OMAP_MMU_READ_CAM 0x68
+#define OMAP_MMU_READ_RAM 0x6c
+#define OMAP_MMU_EMU_FAULT_AD 0x70
+
+#define OMAP_MMU_CNTL_BURST_16MNGT_EN 0x0020
+#define OMAP_MMU_CNTL_WTL_EN 0x0004
+#define OMAP_MMU_CNTL_MMU_EN 0x0002
+#define OMAP_MMU_CNTL_RESET_SW 0x0001
+
+#define OMAP_MMU_IRQ_MULTIHITFAULT 0x00000010
+#define OMAP_MMU_IRQ_TABLEWALKFAULT 0x00000008
+#define OMAP_MMU_IRQ_EMUMISS 0x00000004
+#define OMAP_MMU_IRQ_TRANSLATIONFAULT 0x00000002
+#define OMAP_MMU_IRQ_TLBMISS 0x00000001
+
+#define OMAP_MMU_CAM_VATAG_MASK 0xfffff000
+#define OMAP_MMU_CAM_P 0x00000008
+#define OMAP_MMU_CAM_V 0x00000004
+#define OMAP_MMU_CAM_PAGESIZE_MASK 0x00000003
+#define OMAP_MMU_CAM_PAGESIZE_1MB 0x00000000
+#define OMAP_MMU_CAM_PAGESIZE_64KB 0x00000001
+#define OMAP_MMU_CAM_PAGESIZE_4KB 0x00000002
+#define OMAP_MMU_CAM_PAGESIZE_16MB 0x00000003
+
+#define OMAP_MMU_RAM_PADDR_MASK 0xfffff000
+#define OMAP_MMU_RAM_ENDIANNESS 0x00000200
+#define OMAP_MMU_RAM_ENDIANNESS_BIG 0x00000200
+#define OMAP_MMU_RAM_ENDIANNESS_LITTLE 0x00000000
+#define OMAP_MMU_RAM_ELEMENTSIZE_MASK 0x00000180
+#define OMAP_MMU_RAM_ELEMENTSIZE_8 0x00000000
+#define OMAP_MMU_RAM_ELEMENTSIZE_16 0x00000080
+#define OMAP_MMU_RAM_ELEMENTSIZE_32 0x00000100
+#define OMAP_MMU_RAM_ELEMENTSIZE_NONE 0x00000180
+#define OMAP_MMU_RAM_MIXED 0x00000040
+
+#define IOMAP_VAL 0x3f
+
+#define INIT_TLB_ENTRY(ent, v, p, ps) \
+do { \
+ (ent)->va = (v); \
+ (ent)->pa = (p); \
+ (ent)->pgsz = (ps); \
+ (ent)->prsvd = 0; \
+ (ent)->endian = OMAP_MMU_RAM_ENDIANNESS_LITTLE; \
+ (ent)->elsz = OMAP_MMU_RAM_ELEMENTSIZE_16; \
+ (ent)->mixed = 0; \
+ (ent)->tlb = 1; \
+} while (0)
+
+#define INIT_TLB_ENTRY_4KB_PRESERVED(ent, v, p) \
+do { \
+ (ent)->va = (v); \
+ (ent)->pa = (p); \
+ (ent)->pgsz = OMAP_MMU_CAM_PAGESIZE_4KB; \
+ (ent)->prsvd = OMAP_MMU_CAM_P; \
+ (ent)->endian = OMAP_MMU_RAM_ENDIANNESS_LITTLE; \
+ (ent)->elsz = OMAP_MMU_RAM_ELEMENTSIZE_16; \
+ (ent)->mixed = 0; \
+} while (0)
+
+#define INIT_TLB_ENTRY_4KB_ES32_PRESERVED(ent, v, p) \
+do { \
+ (ent)->va = (v); \
+ (ent)->pa = (p); \
+ (ent)->pgsz = OMAP_MMU_CAM_PAGESIZE_4KB; \
+ (ent)->prsvd = OMAP_MMU_CAM_P; \
+ (ent)->endian = OMAP_MMU_RAM_ENDIANNESS_LITTLE; \
+ (ent)->elsz = OMAP_MMU_RAM_ELEMENTSIZE_32; \
+ (ent)->mixed = 0; \
+} while (0)
+
+struct omap_mmu_tlb_entry {
+ unsigned long va;
+ unsigned long pa;
+ unsigned int pgsz, prsvd, valid;
+
+ u32 endian, elsz, mixed;
+ unsigned int tlb;
+};
+
+static inline unsigned long
+omap_mmu_read_reg(struct omap_mmu *mmu, unsigned long reg)
+{
+ return __raw_readl((void __iomem *)(mmu->base + reg));
+}
+
+static inline void omap_mmu_write_reg(struct omap_mmu *mmu,
+ unsigned long val, unsigned long reg)
+{
+ __raw_writel(val, (void __iomem *)(mmu->base + reg));
+}
+
+#endif /* __MACH_OMAP2_MMU_H */
#include <linux/module.h>
#include <linux/init.h>
#include <asm/system.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/spinlock.h>
- #include <asm/arch/control.h>
- #include <asm/arch/mux.h>
+ #include <mach/control.h>
+ #include <mach/mux.h>
#ifdef CONFIG_OMAP_MUX
--- /dev/null
- #include <asm/arch/clock.h>
- #include <asm/arch/board.h>
+/*
+ * linux/arch/arm/mach-omap2/pm_debug.c
+ *
+ * OMAP Power Management debug routines
+ *
+ * Copyright (C) 2005 Texas Instruments, Inc.
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Written by:
+ * Richard Woodruff <r-woodruff2@ti.com>
+ * Tony Lindgren
+ * Juha Yrjola
+ * Amit Kucheria <amit.kucheria@nokia.com>
+ * Igor Stoppa <igor.stoppa@nokia.com>
+ * Jouni Hogander
+ *
+ * Based on pm.c for omap2
+ *
+ * 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/kernel.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
++#include <mach/clock.h>
++#include <mach/board.h>
+
+#include "prm.h"
+#include "cm.h"
+#include "pm.h"
+
+#ifdef CONFIG_PM_DEBUG
+int omap2_pm_debug = 0;
+
+static int serial_console_clock_disabled;
+static int serial_console_uart;
+static unsigned int serial_console_next_disable;
+
+static struct clk *console_iclk, *console_fclk;
+
+static void serial_console_kick(void)
+{
+ serial_console_next_disable = omap2_read_32k_sync_counter();
+ /* Keep the clocks on for 4 secs */
+ serial_console_next_disable += 4 * 32768;
+}
+
+static void serial_wait_tx(void)
+{
+ static const unsigned long uart_bases[3] = {
+ 0x4806a000, 0x4806c000, 0x4806e000
+ };
+ unsigned long lsr_reg;
+ int looped = 0;
+
+ /* Wait for TX FIFO and THR to get empty */
+ lsr_reg = IO_ADDRESS(uart_bases[serial_console_uart - 1] + (5 << 2));
+ while ((__raw_readb(lsr_reg) & 0x60) != 0x60)
+ looped = 1;
+ if (looped)
+ serial_console_kick();
+}
+
+u32 omap2_read_32k_sync_counter(void)
+{
+ return omap_readl(OMAP2_32KSYNCT_BASE + 0x0010);
+}
+
+void serial_console_fclk_mask(u32 *f1, u32 *f2)
+{
+ switch (serial_console_uart) {
+ case 1:
+ *f1 &= ~(1 << 21);
+ break;
+ case 2:
+ *f1 &= ~(1 << 22);
+ break;
+ case 3:
+ *f2 &= ~(1 << 2);
+ break;
+ }
+}
+
+void serial_console_sleep(int enable)
+{
+ if (console_iclk == NULL || console_fclk == NULL)
+ return;
+
+ if (enable) {
+ BUG_ON(serial_console_clock_disabled);
+ if (clk_get_usecount(console_fclk) == 0)
+ return;
+ if ((int) serial_console_next_disable - (int) omap2_read_32k_sync_counter() >= 0)
+ return;
+ serial_wait_tx();
+ clk_disable(console_iclk);
+ clk_disable(console_fclk);
+ serial_console_clock_disabled = 1;
+ } else {
+ int serial_wakeup = 0;
+ u32 l;
+
+ switch (serial_console_uart) {
+ case 1:
+ l = prm_read_mod_reg(CORE_MOD, PM_WKST1);
+ if (l & OMAP24XX_ST_UART1)
+ serial_wakeup = 1;
+ break;
+ case 2:
+ l = prm_read_mod_reg(CORE_MOD, PM_WKST1);
+ if (l & OMAP24XX_ST_UART2)
+ serial_wakeup = 1;
+ break;
+ case 3:
+ l = prm_read_mod_reg(CORE_MOD, OMAP24XX_PM_WKST2);
+ if (l & OMAP24XX_ST_UART3)
+ serial_wakeup = 1;
+ break;
+ }
+ if (serial_wakeup)
+ serial_console_kick();
+ if (!serial_console_clock_disabled)
+ return;
+ clk_enable(console_iclk);
+ clk_enable(console_fclk);
+ serial_console_clock_disabled = 0;
+ }
+}
+
+void pm_init_serial_console(void)
+{
+ const struct omap_serial_console_config *conf;
+ char name[16];
+
+ conf = omap_get_config(OMAP_TAG_SERIAL_CONSOLE,
+ struct omap_serial_console_config);
+ if (conf == NULL)
+ return;
+ if (conf->console_uart > 3 || conf->console_uart < 1)
+ return;
+ serial_console_uart = conf->console_uart;
+ sprintf(name, "uart%d_fck", conf->console_uart);
+ console_fclk = clk_get(NULL, name);
+ if (IS_ERR(console_fclk))
+ console_fclk = NULL;
+ name[6] = 'i';
+ console_iclk = clk_get(NULL, name);
+ if (IS_ERR(console_fclk))
+ console_iclk = NULL;
+ if (console_fclk == NULL || console_iclk == NULL) {
+ serial_console_uart = 0;
+ return;
+ }
+ switch (serial_console_uart) {
+ case 1:
+ prm_set_mod_reg_bits(OMAP24XX_ST_UART1, CORE_MOD, PM_WKEN1);
+ break;
+ case 2:
+ prm_set_mod_reg_bits(OMAP24XX_ST_UART2, CORE_MOD, PM_WKEN1);
+ break;
+ case 3:
+ prm_set_mod_reg_bits(OMAP24XX_ST_UART3, CORE_MOD, OMAP24XX_PM_WKEN2);
+ break;
+ }
+}
+
+#define DUMP_PRM_MOD_REG(mod, reg) \
+ regs[reg_count].name = #mod "." #reg; \
+ regs[reg_count++].val = prm_read_mod_reg(mod, reg)
+#define DUMP_CM_MOD_REG(mod, reg) \
+ regs[reg_count].name = #mod "." #reg; \
+ regs[reg_count++].val = cm_read_mod_reg(mod, reg)
+#define DUMP_PRM_REG(reg) \
+ regs[reg_count].name = #reg; \
+ regs[reg_count++].val = __raw_readl(reg)
+#define DUMP_CM_REG(reg) \
+ regs[reg_count].name = #reg; \
+ regs[reg_count++].val = __raw_readl(reg)
+#define DUMP_INTC_REG(reg, off) \
+ regs[reg_count].name = #reg; \
+ regs[reg_count++].val = __raw_readl(IO_ADDRESS(0x480fe000 + (off)))
+
+void omap2_pm_dump(int mode, int resume, unsigned int us)
+{
+ struct reg {
+ const char *name;
+ u32 val;
+ } regs[32];
+ int reg_count = 0, i;
+ const char *s1 = NULL, *s2 = NULL;
+
+ if (!resume) {
+#if 0
+ /* MPU */
+ DUMP_PRM_MOD_REG(OCP_MOD, OMAP2_PRM_IRQENABLE_MPU_OFFSET);
+ DUMP_CM_MOD_REG(MPU_MOD, CM_CLKSTCTRL);
+ DUMP_PRM_MOD_REG(MPU_MOD, PM_PWSTCTRL);
+ DUMP_PRM_MOD_REG(MPU_MOD, PM_PWSTST);
+ DUMP_PRM_MOD_REG(MPU_MOD, PM_WKDEP);
+#endif
+#if 0
+ /* INTC */
+ DUMP_INTC_REG(INTC_MIR0, 0x0084);
+ DUMP_INTC_REG(INTC_MIR1, 0x00a4);
+ DUMP_INTC_REG(INTC_MIR2, 0x00c4);
+#endif
+#if 0
+ DUMP_CM_MOD_REG(CORE_MOD, CM_FCLKEN1);
+ if (cpu_is_omap24xx()) {
+ DUMP_CM_MOD_REG(CORE_MOD, OMAP24XX_CM_FCLKEN2);
+ DUMP_PRM_MOD_REG(OMAP24XX_GR_MOD,
+ OMAP24XX_PRCM_CLKEMUL_CTRL_OFFSET);
+ DUMP_PRM_MOD_REG(OMAP24XX_GR_MOD,
+ OMAP24XX_PRCM_CLKSRC_CTRL_OFFSET);
+ }
+ DUMP_CM_MOD_REG(WKUP_MOD, CM_FCLKEN);
+ DUMP_CM_MOD_REG(CORE_MOD, CM_ICLKEN1);
+ DUMP_CM_MOD_REG(CORE_MOD, CM_ICLKEN2);
+ DUMP_CM_MOD_REG(WKUP_MOD, CM_ICLKEN);
+ DUMP_CM_MOD_REG(PLL_MOD, CM_CLKEN);
+ DUMP_CM_MOD_REG(PLL_MOD, CM_AUTOIDLE);
+ DUMP_PRM_MOD_REG(CORE_MOD, PM_PWSTST);
+#endif
+#if 0
+ /* DSP */
+ if (cpu_is_omap24xx()) {
+ DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_FCLKEN);
+ DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_ICLKEN);
+ DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_IDLEST);
+ DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_AUTOIDLE);
+ DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_CLKSEL);
+ DUMP_CM_MOD_REG(OMAP24XX_DSP_MOD, CM_CLKSTCTRL);
+ DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, RM_RSTCTRL);
+ DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, RM_RSTST);
+ DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, PM_PWSTCTRL);
+ DUMP_PRM_MOD_REG(OMAP24XX_DSP_MOD, PM_PWSTST);
+ }
+#endif
+ } else {
+ DUMP_PRM_MOD_REG(CORE_MOD, PM_WKST1);
+ if (cpu_is_omap24xx())
+ DUMP_PRM_MOD_REG(CORE_MOD, OMAP24XX_PM_WKST2);
+ DUMP_PRM_MOD_REG(WKUP_MOD, PM_WKST);
+ DUMP_PRM_MOD_REG(OCP_MOD, OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
+#if 1
+ DUMP_INTC_REG(INTC_PENDING_IRQ0, 0x0098);
+ DUMP_INTC_REG(INTC_PENDING_IRQ1, 0x00b8);
+ DUMP_INTC_REG(INTC_PENDING_IRQ2, 0x00d8);
+#endif
+ }
+
+ switch (mode) {
+ case 0:
+ s1 = "full";
+ s2 = "retention";
+ break;
+ case 1:
+ s1 = "MPU";
+ s2 = "retention";
+ break;
+ case 2:
+ s1 = "MPU";
+ s2 = "idle";
+ break;
+ }
+
+ if (!resume)
+#if defined(CONFIG_NO_IDLE_HZ) || defined(CONFIG_NO_HZ)
+ printk("--- Going to %s %s (next timer after %u ms)\n", s1, s2,
+ jiffies_to_msecs(get_next_timer_interrupt(jiffies) -
+ jiffies));
+#else
+ printk("--- Going to %s %s\n", s1, s2);
+#endif
+ else
+ printk("--- Woke up (slept for %u.%03u ms)\n",
+ us / 1000, us % 1000);
+
+ for (i = 0; i < reg_count; i++)
+ printk("%-20s: 0x%08x\n", regs[i].name, regs[i].val);
+}
+
+#endif
*/
#include <linux/suspend.h>
-#include <linux/sched.h>
-#include <linux/proc_fs.h>
-#include <linux/interrupt.h>
-#include <linux/sysfs.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/atomic.h>
+#include <linux/time.h>
+
- #include <asm/arch/cpu.h>
++#include <mach/cpu.h>
#include <asm/mach/time.h>
-#include <asm/mach/irq.h>
+#include <asm/atomic.h>
- #include <asm/arch/pm.h>
-#include <mach/irqs.h>
-#include <mach/clock.h>
-#include <mach/sram.h>
+ #include <mach/pm.h>
+#include "pm.h"
-static struct clk *vclk;
-static void (*omap2_sram_idle)(void);
-static void (*omap2_sram_suspend)(int dllctrl, int cpu_rev);
-static void (*saved_idle)(void);
+unsigned short enable_dyn_sleep;
+unsigned short clocks_off_while_idle;
+atomic_t sleep_block = ATOMIC_INIT(0);
-extern void __init pmdomain_init(void);
-extern void pmdomain_set_autoidle(void);
+static ssize_t idle_show(struct kobject *, struct kobj_attribute *, char *);
+static ssize_t idle_store(struct kobject *k, struct kobj_attribute *,
+ const char *buf, size_t n);
-static unsigned int omap24xx_sleep_save[OMAP24XX_SLEEP_SAVE_SIZE];
+static struct kobj_attribute sleep_while_idle_attr =
+ __ATTR(sleep_while_idle, 0644, idle_show, idle_store);
-void omap2_pm_idle(void)
-{
- local_irq_disable();
- local_fiq_disable();
- if (need_resched()) {
- local_fiq_enable();
- local_irq_enable();
- return;
- }
-
- omap2_sram_idle();
- local_fiq_enable();
- local_irq_enable();
-}
+static struct kobj_attribute clocks_off_while_idle_attr =
+ __ATTR(clocks_off_while_idle, 0644, idle_show, idle_store);
-static int omap2_pm_prepare(void)
+static ssize_t idle_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
{
- /* We cannot sleep in idle until we have resumed */
- saved_idle = pm_idle;
- pm_idle = NULL;
- return 0;
+ if (attr == &sleep_while_idle_attr)
+ return sprintf(buf, "%hu\n", enable_dyn_sleep);
+ else if (attr == &clocks_off_while_idle_attr)
+ return sprintf(buf, "%hu\n", clocks_off_while_idle);
+ else
+ return -EINVAL;
}
-static int omap2_pm_suspend(void)
+static ssize_t idle_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t n)
{
- return 0;
-}
+ unsigned short value;
-static int omap2_pm_enter(suspend_state_t state)
-{
- int ret = 0;
-
- switch (state)
- {
- case PM_SUSPEND_STANDBY:
- case PM_SUSPEND_MEM:
- ret = omap2_pm_suspend();
- break;
- default:
- ret = -EINVAL;
+ if (sscanf(buf, "%hu", &value) != 1 ||
+ (value != 0 && value != 1)) {
+ printk(KERN_ERR "idle_store: Invalid value\n");
+ return -EINVAL;
}
- return ret;
+ if (attr == &sleep_while_idle_attr)
+ enable_dyn_sleep = value;
+ else if (attr == &clocks_off_while_idle_attr)
+ clocks_off_while_idle = value;
+ else
+ return -EINVAL;
+
+ return n;
}
-static void omap2_pm_finish(void)
+void omap2_block_sleep(void)
{
- pm_idle = saved_idle;
+ atomic_inc(&sleep_block);
}
-static struct platform_suspend_ops omap_pm_ops = {
- .prepare = omap2_pm_prepare,
- .enter = omap2_pm_enter,
- .finish = omap2_pm_finish,
- .valid = suspend_valid_only_mem,
-};
+void omap2_allow_sleep(void)
+{
+ int i;
+
+ i = atomic_dec_return(&sleep_block);
+ BUG_ON(i < 0);
+}
-int __init omap2_pm_init(void)
+static int __init omap_pm_init(void)
{
- return 0;
+ int error = -1;
+
+ if (cpu_is_omap24xx())
+ error = omap2_pm_init();
+ if (cpu_is_omap34xx())
+ error = omap3_pm_init();
+ if (error) {
+ printk(KERN_ERR "omap2|3_pm_init failed: %d\n", error);
+ return error;
+ }
+
+ /* disabled till drivers are fixed */
+ enable_dyn_sleep = 0;
+ error = sysfs_create_file(power_kobj, &sleep_while_idle_attr.attr);
+ if (error)
+ printk(KERN_ERR "sysfs_create_file failed: %d\n", error);
+ error = sysfs_create_file(power_kobj,
+ &clocks_off_while_idle_attr.attr);
+ if (error)
+ printk(KERN_ERR "sysfs_create_file failed: %d\n", error);
+
+ return error;
}
-__initcall(omap2_pm_init);
+late_initcall(omap_pm_init);
--- /dev/null
- #include <asm/arch/irqs.h>
- #include <asm/arch/clock.h>
- #include <asm/arch/sram.h>
- #include <asm/arch/control.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/pm.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/dma.h>
- #include <asm/arch/board.h>
+/*
+ * linux/arch/arm/mach-omap2/pm.c
+ *
+ * OMAP2 Power Management Routines
+ *
+ * Copyright (C) 2005 Texas Instruments, Inc.
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Written by:
+ * Richard Woodruff <r-woodruff2@ti.com>
+ * Tony Lindgren
+ * Juha Yrjola
+ * Amit Kucheria <amit.kucheria@nokia.com>
+ * Igor Stoppa <igor.stoppa@nokia.com>
+ *
+ * Based on pm.c for omap1
+ *
+ * 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/suspend.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include <asm/mach/time.h>
+#include <asm/mach/irq.h>
+#include <asm/mach-types.h>
+
- #include <asm/arch/powerdomain.h>
- #include <asm/arch/clockdomain.h>
++#include <mach/irqs.h>
++#include <mach/clock.h>
++#include <mach/sram.h>
++#include <mach/control.h>
++#include <mach/gpio.h>
++#include <mach/pm.h>
++#include <mach/mux.h>
++#include <mach/dma.h>
++#include <mach/board.h>
+
+#include "prm.h"
+#include "prm-regbits-24xx.h"
+#include "cm.h"
+#include "cm-regbits-24xx.h"
+#include "sdrc.h"
+#include "pm.h"
+
++#include <mach/powerdomain.h>
++#include <mach/clockdomain.h>
+
+static void (*omap2_sram_idle)(void);
+static void (*omap2_sram_suspend)(void __iomem *dllctrl);
+static void (*saved_idle)(void);
+
+static struct powerdomain *mpu_pwrdm;
+static struct powerdomain *core_pwrdm;
+
+static struct clockdomain *dsp_clkdm;
+static struct clockdomain *gfx_clkdm;
+
+static struct clk *osc_ck, *emul_ck;
+
+static int omap2_fclks_active(void)
+{
+ u32 f1, f2;
+
+ f1 = cm_read_mod_reg(CORE_MOD, CM_FCLKEN1);
+ f2 = cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2);
+ serial_console_fclk_mask(&f1, &f2);
+ if (f1 | f2)
+ return 1;
+ return 0;
+}
+
+static void omap2_enter_full_retention(void)
+{
+ u32 l, sleep_time = 0;
+
+ /* There is 1 reference hold for all children of the oscillator
+ * clock, the following will remove it. If no one else uses the
+ * oscillator itself it will be disabled if/when we enter retention
+ * mode.
+ */
+ clk_disable(osc_ck);
+
+ /* Clear old wake-up events */
+ /* REVISIT: These write to reserved bits? */
+ prm_write_mod_reg(0xffffffff, CORE_MOD, PM_WKST1);
+ prm_write_mod_reg(0xffffffff, CORE_MOD, OMAP24XX_PM_WKST2);
+ prm_write_mod_reg(0xffffffff, WKUP_MOD, PM_WKST);
+
+ /*
+ * Set MPU powerdomain's next power state to RETENTION;
+ * preserve logic state during retention
+ */
+ pwrdm_set_logic_retst(mpu_pwrdm, PWRDM_POWER_RET);
+ pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_RET);
+
+ /* Workaround to kill USB */
+ l = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0) | OMAP24XX_USBSTANDBYCTRL;
+ omap_ctrl_writel(l, OMAP2_CONTROL_DEVCONF0);
+
+ omap2_gpio_prepare_for_retention();
+
+ if (omap2_pm_debug) {
+ omap2_pm_dump(0, 0, 0);
+ sleep_time = omap2_read_32k_sync_counter();
+ }
+
+ /* One last check for pending IRQs to avoid extra latency due
+ * to sleeping unnecessarily. */
+ if (omap_irq_pending())
+ goto no_sleep;
+
+ serial_console_sleep(1);
+ /* Jump to SRAM suspend code */
+ omap2_sram_suspend(OMAP_SDRC_REGADDR(SDRC_DLLA_CTRL));
+no_sleep:
+ serial_console_sleep(0);
+
+ if (omap2_pm_debug) {
+ unsigned long long tmp;
+ u32 resume_time;
+
+ resume_time = omap2_read_32k_sync_counter();
+ tmp = resume_time - sleep_time;
+ tmp *= 1000000;
+ omap2_pm_dump(0, 1, tmp / 32768);
+ }
+ omap2_gpio_resume_after_retention();
+
+ clk_enable(osc_ck);
+
+ /* clear CORE wake-up events */
+ prm_write_mod_reg(0xffffffff, CORE_MOD, PM_WKST1);
+ prm_write_mod_reg(0xffffffff, CORE_MOD, OMAP24XX_PM_WKST2);
+
+ /* wakeup domain events - bit 1: GPT1, bit5 GPIO */
+ prm_clear_mod_reg_bits(0x4 | 0x1, WKUP_MOD, PM_WKST);
+
+ /* MPU domain wake events */
+ l = prm_read_mod_reg(OCP_MOD, OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
+ if (l & 0x01)
+ prm_write_mod_reg(0x01, OCP_MOD,
+ OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
+ if (l & 0x20)
+ prm_write_mod_reg(0x20, OCP_MOD,
+ OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
+
+ /* Mask future PRCM-to-MPU interrupts */
+ prm_write_mod_reg(0x0, OCP_MOD, OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
+}
+
+static int omap2_i2c_active(void)
+{
+ u32 l;
+
+ l = cm_read_mod_reg(CORE_MOD, CM_FCLKEN1);
+ return l & (OMAP2420_EN_I2C2 | OMAP2420_EN_I2C1);
+}
+
+static int sti_console_enabled;
+
+static int omap2_allow_mpu_retention(void)
+{
+ u32 l;
+
+ if (atomic_read(&sleep_block))
+ return 0;
+
+ /* Check for MMC, UART2, UART1, McSPI2, McSPI1 and DSS1. */
+ l = cm_read_mod_reg(CORE_MOD, CM_FCLKEN1);
+ if (l & (OMAP2420_EN_MMC | OMAP24XX_EN_UART2 |
+ OMAP24XX_EN_UART1 | OMAP24XX_EN_MCSPI2 |
+ OMAP24XX_EN_MCSPI1 | OMAP24XX_EN_DSS1))
+ return 0;
+ /* Check for UART3. */
+ l = cm_read_mod_reg(CORE_MOD, OMAP24XX_CM_FCLKEN2);
+ if (l & OMAP24XX_EN_UART3)
+ return 0;
+ if (sti_console_enabled)
+ return 0;
+
+ return 1;
+}
+
+static void omap2_enter_mpu_retention(void)
+{
+ u32 sleep_time = 0;
+ int only_idle = 0;
+
+ /* Putting MPU into the WFI state while a transfer is active
+ * seems to cause the I2C block to timeout. Why? Good question. */
+ if (omap2_i2c_active())
+ return;
+
+ /* The peripherals seem not to be able to wake up the MPU when
+ * it is in retention mode. */
+ if (omap2_allow_mpu_retention()) {
+ /* REVISIT: These write to reserved bits? */
+ prm_write_mod_reg(0xffffffff, CORE_MOD, PM_WKST1);
+ prm_write_mod_reg(0xffffffff, CORE_MOD, OMAP24XX_PM_WKST2);
+ prm_write_mod_reg(0xffffffff, WKUP_MOD, PM_WKST);
+
+ /* Try to enter MPU retention */
+ prm_write_mod_reg((0x01 << OMAP_POWERSTATE_SHIFT) |
+ OMAP_LOGICRETSTATE,
+ MPU_MOD, PM_PWSTCTRL);
+ } else {
+ /* Block MPU retention */
+
+ prm_write_mod_reg(OMAP_LOGICRETSTATE, MPU_MOD, PM_PWSTCTRL);
+ only_idle = 1;
+ }
+
+ if (omap2_pm_debug) {
+ omap2_pm_dump(only_idle ? 2 : 1, 0, 0);
+ sleep_time = omap2_read_32k_sync_counter();
+ }
+
+ omap2_sram_idle();
+
+ if (omap2_pm_debug) {
+ unsigned long long tmp;
+ u32 resume_time;
+
+ resume_time = omap2_read_32k_sync_counter();
+ tmp = resume_time - sleep_time;
+ tmp *= 1000000;
+ omap2_pm_dump(only_idle ? 2 : 1, 1, tmp / 32768);
+ }
+}
+
+static int omap2_can_sleep(void)
+{
+ if (!enable_dyn_sleep)
+ return 0;
+ if (omap2_fclks_active())
+ return 0;
+ if (atomic_read(&sleep_block) > 0)
+ return 0;
+ if (clk_get_usecount(osc_ck) > 1)
+ return 0;
+ if (omap_dma_running())
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Note that you can use clock_event_device->min_delta_ns if you want to
+ * avoid reprogramming timer too often when using CONFIG_NO_HZ.
+ */
+static void omap2_pm_idle(void)
+{
+ local_irq_disable();
+ local_fiq_disable();
+
+ if (!omap2_can_sleep()) {
+ if (!atomic_read(&sleep_block) && omap_irq_pending())
+ goto out;
+ omap2_enter_mpu_retention();
+ goto out;
+ }
+
+ if (omap_irq_pending())
+ goto out;
+
+ omap2_enter_full_retention();
+
+out:
+ local_fiq_enable();
+ local_irq_enable();
+}
+
+static int omap2_pm_prepare(void)
+{
+ /* We cannot sleep in idle until we have resumed */
+ saved_idle = pm_idle;
+ pm_idle = NULL;
+
+ return 0;
+}
+
+static int omap2_pm_suspend(void)
+{
+ u32 wken_wkup, mir1;
+
+ wken_wkup = prm_read_mod_reg(WKUP_MOD, PM_WKEN);
+ prm_write_mod_reg(wken_wkup & ~OMAP24XX_EN_GPT1, WKUP_MOD, PM_WKEN);
+
+ /* Mask GPT1 */
+ mir1 = omap_readl(0x480fe0a4);
+ omap_writel(1 << 5, 0x480fe0ac);
+
+ omap2_enter_full_retention();
+
+ omap_writel(mir1, 0x480fe0a4);
+ prm_write_mod_reg(wken_wkup, WKUP_MOD, PM_WKEN);
+
+ return 0;
+}
+
+static int omap2_pm_enter(suspend_state_t state)
+{
+ int ret = 0;
+
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ case PM_SUSPEND_MEM:
+ ret = omap2_pm_suspend();
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void omap2_pm_finish(void)
+{
+ pm_idle = saved_idle;
+}
+
+static struct platform_suspend_ops omap_pm_ops = {
+ .prepare = omap2_pm_prepare,
+ .enter = omap2_pm_enter,
+ .finish = omap2_pm_finish,
+ .valid = suspend_valid_only_mem,
+};
+
+static int _pm_clkdm_enable_hwsup(struct clockdomain *clkdm)
+{
+ omap2_clkdm_allow_idle(clkdm);
+ return 0;
+}
+
+static void __init prcm_setup_regs(void)
+{
+ int i, num_mem_banks;
+ struct powerdomain *pwrdm;
+
+ /* Enable autoidle */
+ prm_write_mod_reg(OMAP24XX_AUTOIDLE, OCP_MOD,
+ OMAP24XX_PRM_SYSCONFIG_OFFSET);
+
+ /* Set all domain wakeup dependencies */
+ prm_write_mod_reg(OMAP_EN_WKUP_MASK, MPU_MOD, PM_WKDEP);
+ prm_write_mod_reg(0, OMAP24XX_DSP_MOD, PM_WKDEP);
+ prm_write_mod_reg(0, GFX_MOD, PM_WKDEP);
+ prm_write_mod_reg(0, CORE_MOD, PM_WKDEP);
+ if (cpu_is_omap2430())
+ prm_write_mod_reg(0, OMAP2430_MDM_MOD, PM_WKDEP);
+
+ /*
+ * Set CORE powerdomain memory banks to retain their contents
+ * during RETENTION
+ */
+ num_mem_banks = pwrdm_get_mem_bank_count(core_pwrdm);
+ for (i = 0; i < num_mem_banks; i++)
+ pwrdm_set_mem_retst(core_pwrdm, i, PWRDM_POWER_RET);
+
+ /* Set CORE powerdomain's next power state to RETENTION */
+ pwrdm_set_next_pwrst(core_pwrdm, PWRDM_POWER_RET);
+
+ /*
+ * Set MPU powerdomain's next power state to RETENTION;
+ * preserve logic state during retention
+ */
+ pwrdm_set_logic_retst(mpu_pwrdm, PWRDM_POWER_RET);
+ pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_RET);
+
+ /* Force-power down DSP, GFX powerdomains */
+
+ pwrdm = clkdm_get_pwrdm(dsp_clkdm);
+ pwrdm_set_next_pwrst(pwrdm, PWRDM_POWER_OFF);
+ omap2_clkdm_sleep(dsp_clkdm);
+
+ pwrdm = clkdm_get_pwrdm(gfx_clkdm);
+ pwrdm_set_next_pwrst(pwrdm, PWRDM_POWER_OFF);
+ omap2_clkdm_sleep(gfx_clkdm);
+
+ /* Enable clockdomain hardware-supervised control for all clkdms */
+ clkdm_for_each(_pm_clkdm_enable_hwsup);
+
+ /* Enable clock autoidle for all domains */
+ cm_write_mod_reg(OMAP24XX_AUTO_CAM |
+ OMAP24XX_AUTO_MAILBOXES |
+ OMAP24XX_AUTO_WDT4 |
+ OMAP2420_AUTO_WDT3 |
+ OMAP24XX_AUTO_MSPRO |
+ OMAP2420_AUTO_MMC |
+ OMAP24XX_AUTO_FAC |
+ OMAP2420_AUTO_EAC |
+ OMAP24XX_AUTO_HDQ |
+ OMAP24XX_AUTO_UART2 |
+ OMAP24XX_AUTO_UART1 |
+ OMAP24XX_AUTO_I2C2 |
+ OMAP24XX_AUTO_I2C1 |
+ OMAP24XX_AUTO_MCSPI2 |
+ OMAP24XX_AUTO_MCSPI1 |
+ OMAP24XX_AUTO_MCBSP2 |
+ OMAP24XX_AUTO_MCBSP1 |
+ OMAP24XX_AUTO_GPT12 |
+ OMAP24XX_AUTO_GPT11 |
+ OMAP24XX_AUTO_GPT10 |
+ OMAP24XX_AUTO_GPT9 |
+ OMAP24XX_AUTO_GPT8 |
+ OMAP24XX_AUTO_GPT7 |
+ OMAP24XX_AUTO_GPT6 |
+ OMAP24XX_AUTO_GPT5 |
+ OMAP24XX_AUTO_GPT4 |
+ OMAP24XX_AUTO_GPT3 |
+ OMAP24XX_AUTO_GPT2 |
+ OMAP2420_AUTO_VLYNQ |
+ OMAP24XX_AUTO_DSS,
+ CORE_MOD, CM_AUTOIDLE1);
+ cm_write_mod_reg(OMAP24XX_AUTO_UART3 |
+ OMAP24XX_AUTO_SSI |
+ OMAP24XX_AUTO_USB,
+ CORE_MOD, CM_AUTOIDLE2);
+ cm_write_mod_reg(OMAP24XX_AUTO_SDRC |
+ OMAP24XX_AUTO_GPMC |
+ OMAP24XX_AUTO_SDMA,
+ CORE_MOD, CM_AUTOIDLE3);
+ cm_write_mod_reg(OMAP24XX_AUTO_PKA |
+ OMAP24XX_AUTO_AES |
+ OMAP24XX_AUTO_RNG |
+ OMAP24XX_AUTO_SHA |
+ OMAP24XX_AUTO_DES,
+ CORE_MOD, OMAP24XX_CM_AUTOIDLE4);
+
+ cm_write_mod_reg(OMAP2420_AUTO_DSP_IPI, OMAP24XX_DSP_MOD, CM_AUTOIDLE);
+
+ /* Put DPLL and both APLLs into autoidle mode */
+ cm_write_mod_reg((0x03 << OMAP24XX_AUTO_DPLL_SHIFT) |
+ (0x03 << OMAP24XX_AUTO_96M_SHIFT) |
+ (0x03 << OMAP24XX_AUTO_54M_SHIFT),
+ PLL_MOD, CM_AUTOIDLE);
+
+ cm_write_mod_reg(OMAP24XX_AUTO_OMAPCTRL |
+ OMAP24XX_AUTO_WDT1 |
+ OMAP24XX_AUTO_MPU_WDT |
+ OMAP24XX_AUTO_GPIOS |
+ OMAP24XX_AUTO_32KSYNC |
+ OMAP24XX_AUTO_GPT1,
+ WKUP_MOD, CM_AUTOIDLE);
+
+ /* REVISIT: Configure number of 32 kHz clock cycles for sys_clk
+ * stabilisation */
+ prm_write_mod_reg(15 << OMAP_SETUP_TIME_SHIFT, OMAP24XX_GR_MOD,
+ OMAP24XX_PRCM_CLKSSETUP_OFFSET);
+
+ /* Configure automatic voltage transition */
+ prm_write_mod_reg(2 << OMAP_SETUP_TIME_SHIFT, OMAP24XX_GR_MOD,
+ OMAP24XX_PRCM_VOLTSETUP_OFFSET);
+ prm_write_mod_reg(OMAP24XX_AUTO_EXTVOLT |
+ (0x1 << OMAP24XX_SETOFF_LEVEL_SHIFT) |
+ OMAP24XX_MEMRETCTRL |
+ (0x1 << OMAP24XX_SETRET_LEVEL_SHIFT) |
+ (0x0 << OMAP24XX_VOLT_LEVEL_SHIFT),
+ OMAP24XX_GR_MOD, OMAP24XX_PRCM_VOLTCTRL_OFFSET);
+
+ /* Enable wake-up events */
+ prm_write_mod_reg(OMAP24XX_EN_GPIOS | OMAP24XX_EN_GPT1,
+ WKUP_MOD, PM_WKEN);
+}
+
+int __init omap2_pm_init(void)
+{
+ u32 l;
+
+ printk(KERN_INFO "Power Management for OMAP2 initializing\n");
+ l = prm_read_mod_reg(OCP_MOD, OMAP24XX_PRM_REVISION_OFFSET);
+ printk(KERN_INFO "PRCM revision %d.%d\n", (l >> 4) & 0x0f, l & 0x0f);
+
+ /* Look up important powerdomains, clockdomains */
+
+ mpu_pwrdm = pwrdm_lookup("mpu_pwrdm");
+ if (!mpu_pwrdm)
+ pr_err("PM: mpu_pwrdm not found\n");
+
+ core_pwrdm = pwrdm_lookup("core_pwrdm");
+ if (!core_pwrdm)
+ pr_err("PM: core_pwrdm not found\n");
+
+ dsp_clkdm = clkdm_lookup("dsp_clkdm");
+ if (!dsp_clkdm)
+ pr_err("PM: mpu_clkdm not found\n");
+
+ gfx_clkdm = clkdm_lookup("gfx_clkdm");
+ if (!gfx_clkdm)
+ pr_err("PM: gfx_clkdm not found\n");
+
+
+ osc_ck = clk_get(NULL, "osc_ck");
+ if (IS_ERR(osc_ck)) {
+ printk(KERN_ERR "could not get osc_ck\n");
+ return -ENODEV;
+ }
+
+ if (cpu_is_omap242x()) {
+ emul_ck = clk_get(NULL, "emul_ck");
+ if (IS_ERR(emul_ck)) {
+ printk(KERN_ERR "could not get emul_ck\n");
+ clk_put(osc_ck);
+ return -ENODEV;
+ }
+ }
+
+ prcm_setup_regs();
+
+ pm_init_serial_console();
+
+ /* Hack to prevent MPU retention when STI console is enabled. */
+ {
+ const struct omap_sti_console_config *sti;
+
+ sti = omap_get_config(OMAP_TAG_STI_CONSOLE,
+ struct omap_sti_console_config);
+ if (sti != NULL && sti->enable)
+ sti_console_enabled = 1;
+ }
+
+ /*
+ * We copy the assembler sleep/wakeup routines to SRAM.
+ * These routines need to be in SRAM as that's the only
+ * memory the MPU can see when it wakes up.
+ */
+ if (cpu_is_omap242x()) {
+ omap2_sram_idle = omap_sram_push(omap242x_idle_loop_suspend,
+ omap242x_idle_loop_suspend_sz);
+
+ omap2_sram_suspend = omap_sram_push(omap242x_cpu_suspend,
+ omap242x_cpu_suspend_sz);
+ } else {
+ omap2_sram_idle = omap_sram_push(omap243x_idle_loop_suspend,
+ omap243x_idle_loop_suspend_sz);
+
+ omap2_sram_suspend = omap_sram_push(omap243x_cpu_suspend,
+ omap243x_cpu_suspend_sz);
+ }
+
+ suspend_set_ops(&omap_pm_ops);
+ pm_idle = omap2_pm_idle;
+
+ return 0;
+}
--- /dev/null
- #include <asm/arch/gpio.h>
- #include <asm/arch/sram.h>
- #include <asm/arch/pm.h>
- #include <asm/arch/clockdomain.h>
- #include <asm/arch/powerdomain.h>
+/*
+ * linux/arch/arm/mach-omap2/pm34xx.c
+ *
+ * OMAP3 Power Management Routines
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ * Tony Lindgren <tony@atomide.com>
+ * Jouni Hogander
+ *
+ * Copyright (C) 2005 Texas Instruments, Inc.
+ * Richard Woodruff <r-woodruff2@ti.com>
+ *
+ * Based on pm.c for omap1
+ *
+ * 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/pm.h>
+#include <linux/suspend.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/err.h>
+
++#include <mach/gpio.h>
++#include <mach/sram.h>
++#include <mach/pm.h>
++#include <mach/clockdomain.h>
++#include <mach/powerdomain.h>
+
+#include "cm.h"
+#include "cm-regbits-34xx.h"
+#include "prm-regbits-34xx.h"
+
+#include "prm.h"
+#include "pm.h"
+#include "smartreflex.h"
+
+struct power_state {
+ struct powerdomain *pwrdm;
+ u32 next_state;
+ u32 saved_state;
+ struct list_head node;
+};
+
+static LIST_HEAD(pwrst_list);
+
+static void (*_omap_sram_idle)(u32 *addr, int save_state);
+
+static void (*saved_idle)(void);
+
+static struct powerdomain *mpu_pwrdm;
+
+/* PRCM Interrupt Handler for wakeups */
+static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
+{
+ u32 wkst, irqstatus_mpu;
+ u32 fclk, iclk;
+
+ /* WKUP */
+ wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST);
+ if (wkst) {
+ iclk = cm_read_mod_reg(WKUP_MOD, CM_ICLKEN);
+ fclk = cm_read_mod_reg(WKUP_MOD, CM_FCLKEN);
+ cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_ICLKEN);
+ cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_FCLKEN);
+ prm_write_mod_reg(wkst, WKUP_MOD, PM_WKST);
+ while (prm_read_mod_reg(WKUP_MOD, PM_WKST));
+ cm_write_mod_reg(iclk, WKUP_MOD, CM_ICLKEN);
+ cm_write_mod_reg(fclk, WKUP_MOD, CM_FCLKEN);
+ }
+
+ /* CORE */
+ wkst = prm_read_mod_reg(CORE_MOD, PM_WKST1);
+ if (wkst) {
+ iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN1);
+ fclk = cm_read_mod_reg(CORE_MOD, CM_FCLKEN1);
+ cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN1);
+ cm_set_mod_reg_bits(wkst, CORE_MOD, CM_FCLKEN1);
+ prm_write_mod_reg(wkst, CORE_MOD, PM_WKST1);
+ while (prm_read_mod_reg(CORE_MOD, PM_WKST1));
+ cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN1);
+ cm_write_mod_reg(fclk, CORE_MOD, CM_FCLKEN1);
+ }
+ wkst = prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3);
+ if (wkst) {
+ iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN3);
+ fclk = cm_read_mod_reg(CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
+ cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN3);
+ cm_set_mod_reg_bits(wkst, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
+ prm_write_mod_reg(wkst, CORE_MOD, OMAP3430ES2_PM_WKST3);
+ while (prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3));
+ cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN3);
+ cm_write_mod_reg(fclk, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
+ }
+
+ /* PER */
+ wkst = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST);
+ if (wkst) {
+ iclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_ICLKEN);
+ fclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_FCLKEN);
+ cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_ICLKEN);
+ cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_FCLKEN);
+ prm_write_mod_reg(wkst, OMAP3430_PER_MOD, PM_WKST);
+ while (prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST));
+ cm_write_mod_reg(iclk, OMAP3430_PER_MOD, CM_ICLKEN);
+ cm_write_mod_reg(fclk, OMAP3430_PER_MOD, CM_FCLKEN);
+ }
+
+ if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0)) {
+ /* USBHOST */
+ wkst = prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST);
+ if (wkst) {
+ iclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
+ CM_ICLKEN);
+ fclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
+ CM_FCLKEN);
+ cm_set_mod_reg_bits(wkst, OMAP3430ES2_USBHOST_MOD,
+ CM_ICLKEN);
+ cm_set_mod_reg_bits(wkst, OMAP3430ES2_USBHOST_MOD,
+ CM_FCLKEN);
+ prm_write_mod_reg(wkst, OMAP3430ES2_USBHOST_MOD,
+ PM_WKST);
+ while (prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
+ PM_WKST));
+ cm_write_mod_reg(iclk, OMAP3430ES2_USBHOST_MOD,
+ CM_ICLKEN);
+ cm_write_mod_reg(fclk, OMAP3430ES2_USBHOST_MOD,
+ CM_FCLKEN);
+ }
+ }
+
+ irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
+ OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
+ prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
+ OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
+
+ while (prm_read_mod_reg(OCP_MOD, OMAP2_PRM_IRQSTATUS_MPU_OFFSET));
+
+ return IRQ_HANDLED;
+}
+
+static void omap_sram_idle(void)
+{
+ /* Variable to tell what needs to be saved and restored
+ * in omap_sram_idle*/
+ /* save_state = 0 => Nothing to save and restored */
+ /* save_state = 1 => Only L1 and logic lost */
+ /* save_state = 2 => Only L2 lost */
+ /* save_state = 3 => L1, L2 and logic lost */
+ int save_state = 0, mpu_next_state;
+
+ if (!_omap_sram_idle)
+ return;
+
+ mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm);
+ switch (mpu_next_state) {
+ case PWRDM_POWER_RET:
+ /* No need to save context */
+ save_state = 0;
+ break;
+ default:
+ /* Invalid state */
+ printk(KERN_ERR "Invalid mpu state in sram_idle\n");
+ return;
+ }
+
+ omap2_gpio_prepare_for_retention();
+
+ _omap_sram_idle(NULL, save_state);
+
+ omap2_gpio_resume_after_retention();
+}
+
+static int omap3_can_sleep(void)
+{
+ if (!enable_dyn_sleep)
+ return 0;
+ if (atomic_read(&sleep_block) > 0)
+ return 0;
+ return 1;
+}
+
+/* _clkdm_deny_idle - private callback function used by set_pwrdm_state() */
+static int _clkdm_deny_idle(struct powerdomain *pwrdm,
+ struct clockdomain *clkdm)
+{
+ omap2_clkdm_deny_idle(clkdm);
+ return 0;
+}
+
+/* _clkdm_allow_idle - private callback function used by set_pwrdm_state() */
+static int _clkdm_allow_idle(struct powerdomain *pwrdm,
+ struct clockdomain *clkdm)
+{
+ omap2_clkdm_allow_idle(clkdm);
+ return 0;
+}
+
+/* This sets pwrdm state (other than mpu & core. Currently only ON &
+ * RET are supported. Function is assuming that clkdm doesn't have
+ * hw_sup mode enabled. */
+static int set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
+{
+ u32 cur_state;
+ int ret = 0;
+
+ if (pwrdm == NULL || IS_ERR(pwrdm))
+ return -EINVAL;
+
+ cur_state = pwrdm_read_next_pwrst(pwrdm);
+
+ if (cur_state == state)
+ return ret;
+
+ pwrdm_for_each_clkdm(pwrdm, _clkdm_deny_idle);
+
+ ret = pwrdm_set_next_pwrst(pwrdm, state);
+ if (ret) {
+ printk(KERN_ERR "Unable to set state of powerdomain: %s\n",
+ pwrdm->name);
+ goto err;
+ }
+
+ pwrdm_for_each_clkdm(pwrdm, _clkdm_allow_idle);
+
+err:
+ return ret;
+}
+
+static void omap3_pm_idle(void)
+{
+ local_irq_disable();
+ local_fiq_disable();
+
+ if (!omap3_can_sleep())
+ goto out;
+
+ if (omap_irq_pending())
+ goto out;
+
+ omap_sram_idle();
+
+out:
+ local_fiq_enable();
+ local_irq_enable();
+}
+
+static int omap3_pm_prepare(void)
+{
+ saved_idle = pm_idle;
+ pm_idle = NULL;
+ return 0;
+}
+
+static int omap3_pm_suspend(void)
+{
+ struct power_state *pwrst;
+ int state, ret = 0;
+
+ /* XXX Disable smartreflex before entering suspend */
+ disable_smartreflex(SR1);
+ disable_smartreflex(SR2);
+
+ /* Read current next_pwrsts */
+ list_for_each_entry(pwrst, &pwrst_list, node)
+ pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
+ /* Set ones wanted by suspend */
+ list_for_each_entry(pwrst, &pwrst_list, node) {
+ if (set_pwrdm_state(pwrst->pwrdm, pwrst->next_state))
+ goto restore;
+ if (pwrdm_clear_all_prev_pwrst(pwrst->pwrdm))
+ goto restore;
+ }
+
+ omap_sram_idle();
+
+restore:
+ /* Restore next_pwrsts */
+ list_for_each_entry(pwrst, &pwrst_list, node) {
+ set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
+ state = pwrdm_read_prev_pwrst(pwrst->pwrdm);
+ if (state != pwrst->next_state) {
+ printk(KERN_INFO "Powerdomain (%s) didn't enter "
+ "target state %d\n",
+ pwrst->pwrdm->name, pwrst->next_state);
+ ret = -1;
+ }
+ }
+ if (ret)
+ printk(KERN_ERR "Could not enter target state in pm_suspend\n");
+ else
+ printk(KERN_INFO "Successfully put all powerdomains "
+ "to target state\n");
+
+ /* XXX Enable smartreflex after suspend */
+ enable_smartreflex(SR1);
+ enable_smartreflex(SR2);
+
+ return ret;
+}
+
+static int omap3_pm_enter(suspend_state_t state)
+{
+ int ret = 0;
+
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ case PM_SUSPEND_MEM:
+ ret = omap3_pm_suspend();
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void omap3_pm_finish(void)
+{
+ pm_idle = saved_idle;
+}
+
+static struct platform_suspend_ops omap_pm_ops = {
+ .prepare = omap3_pm_prepare,
+ .enter = omap3_pm_enter,
+ .finish = omap3_pm_finish,
+ .valid = suspend_valid_only_mem,
+};
+
+static void __init prcm_setup_regs(void)
+{
+ /* XXX Reset all wkdeps. This should be done when initializing
+ * powerdomains */
+ prm_write_mod_reg(0, OMAP3430_IVA2_MOD, PM_WKDEP);
+ prm_write_mod_reg(0, MPU_MOD, PM_WKDEP);
+ prm_write_mod_reg(0, OMAP3430_DSS_MOD, PM_WKDEP);
+ prm_write_mod_reg(0, OMAP3430_NEON_MOD, PM_WKDEP);
+ prm_write_mod_reg(0, OMAP3430_CAM_MOD, PM_WKDEP);
+ prm_write_mod_reg(0, OMAP3430_PER_MOD, PM_WKDEP);
+ if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0)) {
+ prm_write_mod_reg(0, OMAP3430ES2_SGX_MOD, PM_WKDEP);
+ prm_write_mod_reg(0, OMAP3430ES2_USBHOST_MOD, PM_WKDEP);
+ } else
+ prm_write_mod_reg(0, GFX_MOD, PM_WKDEP);
+
+ /* setup wakup source */
+ prm_write_mod_reg(OMAP3430_EN_IO | OMAP3430_EN_GPIO1 | OMAP3430_EN_GPT1,
+ WKUP_MOD, PM_WKEN);
+ /* No need to write EN_IO, that is always enabled */
+ prm_write_mod_reg(OMAP3430_EN_GPIO1 | OMAP3430_EN_GPT1,
+ WKUP_MOD, OMAP3430_PM_MPUGRPSEL);
+ /* For some reason IO doesn't generate wakeup event even if
+ * it is selected to mpu wakeup goup */
+ prm_write_mod_reg(OMAP3430_IO_EN | OMAP3430_WKUP_EN,
+ OCP_MOD, OMAP2_PRM_IRQENABLE_MPU_OFFSET);
+}
+
+static int __init pwrdms_setup(struct powerdomain *pwrdm)
+{
+ struct power_state *pwrst;
+
+ if (!pwrdm->pwrsts)
+ return 0;
+
+ pwrst = kmalloc(sizeof(struct power_state), GFP_KERNEL);
+ if (!pwrst)
+ return -ENOMEM;
+ pwrst->pwrdm = pwrdm;
+ pwrst->next_state = PWRDM_POWER_RET;
+ list_add(&pwrst->node, &pwrst_list);
+
+ if (pwrdm_has_hdwr_sar(pwrdm))
+ pwrdm_enable_hdwr_sar(pwrdm);
+
+ return set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
+}
+
+int __init omap3_pm_init(void)
+{
+ struct power_state *pwrst;
+ int ret;
+
+ printk(KERN_ERR "Power Management for TI OMAP3.\n");
+
+ /* XXX prcm_setup_regs needs to be before enabling hw
+ * supervised mode for powerdomains */
+ prcm_setup_regs();
+
+ ret = request_irq(INT_34XX_PRCM_MPU_IRQ,
+ (irq_handler_t)prcm_interrupt_handler,
+ IRQF_DISABLED, "prcm", NULL);
+ if (ret) {
+ printk(KERN_ERR "request_irq failed to register for 0x%x\n",
+ INT_34XX_PRCM_MPU_IRQ);
+ goto err1;
+ }
+
+ ret = pwrdm_for_each(pwrdms_setup);
+ if (ret) {
+ printk(KERN_ERR "Failed to setup powerdomains\n");
+ goto err2;
+ }
+
+ mpu_pwrdm = pwrdm_lookup("mpu_pwrdm");
+ if (mpu_pwrdm == NULL) {
+ printk(KERN_ERR "Failed to get mpu_pwrdm\n");
+ goto err2;
+ }
+
+ _omap_sram_idle = omap_sram_push(omap34xx_cpu_suspend,
+ omap34xx_cpu_suspend_sz);
+
+ suspend_set_ops(&omap_pm_ops);
+
+ pm_idle = omap3_pm_idle;
+
+err1:
+ return ret;
+err2:
+ free_irq(INT_34XX_PRCM_MPU_IRQ, NULL);
+ list_for_each_entry(pwrst, &pwrst_list, node) {
+ list_del(&pwrst->node);
+ kfree(pwrst);
+ }
+ return ret;
+}
+
+static void __init configure_vc(void)
+{
+ prm_write_mod_reg((R_SRI2C_SLAVE_ADDR << OMAP3430_SMPS_SA1_SHIFT) |
+ (R_SRI2C_SLAVE_ADDR << OMAP3430_SMPS_SA0_SHIFT),
+ OMAP3430_GR_MOD, OMAP3_PRM_VC_SMPS_SA_OFFSET);
+ prm_write_mod_reg((R_VDD2_SR_CONTROL << OMAP3430_VOLRA1_SHIFT) |
+ (R_VDD1_SR_CONTROL << OMAP3430_VOLRA0_SHIFT),
+ OMAP3430_GR_MOD, OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET);
+
+ prm_write_mod_reg((OMAP3430_VC_CMD_VAL0_ON <<
+ OMAP3430_VC_CMD_ON_SHIFT) |
+ (OMAP3430_VC_CMD_VAL0_ONLP << OMAP3430_VC_CMD_ONLP_SHIFT) |
+ (OMAP3430_VC_CMD_VAL0_RET << OMAP3430_VC_CMD_RET_SHIFT) |
+ (OMAP3430_VC_CMD_VAL0_OFF << OMAP3430_VC_CMD_OFF_SHIFT),
+ OMAP3430_GR_MOD, OMAP3_PRM_VC_CMD_VAL_0_OFFSET);
+
+ prm_write_mod_reg((OMAP3430_VC_CMD_VAL1_ON <<
+ OMAP3430_VC_CMD_ON_SHIFT) |
+ (OMAP3430_VC_CMD_VAL1_ONLP << OMAP3430_VC_CMD_ONLP_SHIFT) |
+ (OMAP3430_VC_CMD_VAL1_RET << OMAP3430_VC_CMD_RET_SHIFT) |
+ (OMAP3430_VC_CMD_VAL1_OFF << OMAP3430_VC_CMD_OFF_SHIFT),
+ OMAP3430_GR_MOD, OMAP3_PRM_VC_CMD_VAL_1_OFFSET);
+
+ prm_write_mod_reg(OMAP3430_CMD1 | OMAP3430_RAV1,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VC_CH_CONF_OFFSET);
+
+ prm_write_mod_reg(OMAP3430_MCODE_SHIFT | OMAP3430_HSEN | OMAP3430_SREN,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VC_I2C_CFG_OFFSET);
+
+ /* Setup voltctrl and other setup times */
+
+#ifdef CONFIG_OMAP_SYSOFFMODE
+ prm_write_mod_reg(OMAP3430_AUTO_OFF | OMAP3430_AUTO_RET |
+ OMAP3430_SEL_OFF, OMAP3430_GR_MOD,
+ OMAP3_PRM_VOLTCTRL_OFFSET);
+
+ prm_write_mod_reg(OMAP3430_CLKSETUP_DURATION, OMAP3430_GR_MOD,
+ OMAP3_PRM_CLKSETUP_OFFSET);
+ prm_write_mod_reg((OMAP3430_VOLTSETUP_TIME2 <<
+ OMAP3430_SETUP_TIME2_SHIFT) |
+ (OMAP3430_VOLTSETUP_TIME1 <<
+ OMAP3430_SETUP_TIME1_SHIFT),
+ OMAP3430_GR_MOD, OMAP3_PRM_VOLTSETUP1_OFFSET);
+
+ prm_write_mod_reg(OMAP3430_VOLTOFFSET_DURATION, OMAP3430_GR_MOD,
+ OMAP3_PRM_VOLTOFFSET_OFFSET);
+ prm_write_mod_reg(OMAP3430_VOLTSETUP2_DURATION, OMAP3430_GR_MOD,
+ OMAP3_PRM_VOLTSETUP2_OFFSET);
+#else
+ prm_set_mod_reg_bits(OMAP3430_AUTO_RET, OMAP3430_GR_MOD,
+ OMAP3_PRM_VOLTCTRL_OFFSET);
+#endif
+
+}
+
+static int __init omap3_pm_early_init(void)
+{
+ prm_clear_mod_reg_bits(OMAP3430_OFFMODE_POL, OMAP3430_GR_MOD,
+ OMAP3_PRM_POLCTRL_OFFSET);
+
+ configure_vc();
+
+ return 0;
+}
+
+arch_initcall(omap3_pm_early_init);
--- /dev/null
- #include <asm/arch/cpu.h>
- #include <asm/arch/powerdomain.h>
- #include <asm/arch/clockdomain.h>
+/*
+ * OMAP powerdomain control
+ *
+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
+ * Copyright (C) 2007-2008 Nokia Corporation
+ *
+ * Written by Paul Walmsley
+ *
+ * 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.
+ */
+#ifdef CONFIG_OMAP_DEBUG_POWERDOMAIN
+# define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <asm/atomic.h>
+
+#include "cm.h"
+#include "cm-regbits-34xx.h"
+#include "prm.h"
+#include "prm-regbits-34xx.h"
+
++#include <mach/cpu.h>
++#include <mach/powerdomain.h>
++#include <mach/clockdomain.h>
+
+/* pwrdm_list contains all registered struct powerdomains */
+static LIST_HEAD(pwrdm_list);
+
+/*
+ * pwrdm_rwlock protects pwrdm_list add and del ops - also reused to
+ * protect pwrdm_clkdms[] during clkdm add/del ops
+ */
+static DEFINE_RWLOCK(pwrdm_rwlock);
+
+
+/* Private functions */
+
+static u32 prm_read_mod_bits_shift(s16 domain, s16 idx, u32 mask)
+{
+ u32 v;
+
+ v = prm_read_mod_reg(domain, idx);
+ v &= mask;
+ v >>= __ffs(mask);
+
+ return v;
+}
+
+static struct powerdomain *_pwrdm_lookup(const char *name)
+{
+ struct powerdomain *pwrdm, *temp_pwrdm;
+
+ pwrdm = NULL;
+
+ list_for_each_entry(temp_pwrdm, &pwrdm_list, node) {
+ if (!strcmp(name, temp_pwrdm->name)) {
+ pwrdm = temp_pwrdm;
+ break;
+ }
+ }
+
+ return pwrdm;
+}
+
+/* _pwrdm_deps_lookup - look up the specified powerdomain in a pwrdm list */
+static struct powerdomain *_pwrdm_deps_lookup(struct powerdomain *pwrdm,
+ struct pwrdm_dep *deps)
+{
+ struct pwrdm_dep *pd;
+
+ if (!pwrdm || !deps || !omap_chip_is(pwrdm->omap_chip))
+ return ERR_PTR(-EINVAL);
+
+ for (pd = deps; pd; pd++) {
+
+ if (!omap_chip_is(pd->omap_chip))
+ continue;
+
+ if (!pd->pwrdm && pd->pwrdm_name)
+ pd->pwrdm = pwrdm_lookup(pd->pwrdm_name);
+
+ if (pd->pwrdm == pwrdm)
+ break;
+
+ }
+
+ if (!pd)
+ return ERR_PTR(-ENOENT);
+
+ return pd->pwrdm;
+}
+
+
+/* Public functions */
+
+/**
+ * pwrdm_init - set up the powerdomain layer
+ *
+ * Loop through the list of powerdomains, registering all that are
+ * available on the current CPU. If pwrdm_list is supplied and not
+ * null, all of the referenced powerdomains will be registered. No
+ * return value.
+ */
+void pwrdm_init(struct powerdomain **pwrdm_list)
+{
+ struct powerdomain **p = NULL;
+
+ if (pwrdm_list)
+ for (p = pwrdm_list; *p; p++)
+ pwrdm_register(*p);
+}
+
+/**
+ * pwrdm_register - register a powerdomain
+ * @pwrdm: struct powerdomain * to register
+ *
+ * Adds a powerdomain to the internal powerdomain list. Returns
+ * -EINVAL if given a null pointer, -EEXIST if a powerdomain is
+ * already registered by the provided name, or 0 upon success.
+ */
+int pwrdm_register(struct powerdomain *pwrdm)
+{
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ if (!pwrdm)
+ return -EINVAL;
+
+ if (!omap_chip_is(pwrdm->omap_chip))
+ return -EINVAL;
+
+ write_lock_irqsave(&pwrdm_rwlock, flags);
+ if (_pwrdm_lookup(pwrdm->name)) {
+ ret = -EEXIST;
+ goto pr_unlock;
+ }
+
+ list_add(&pwrdm->node, &pwrdm_list);
+
+ pr_debug("powerdomain: registered %s\n", pwrdm->name);
+ ret = 0;
+
+pr_unlock:
+ write_unlock_irqrestore(&pwrdm_rwlock, flags);
+
+ return ret;
+}
+
+/**
+ * pwrdm_unregister - unregister a powerdomain
+ * @pwrdm: struct powerdomain * to unregister
+ *
+ * Removes a powerdomain from the internal powerdomain list. Returns
+ * -EINVAL if pwrdm argument is NULL.
+ */
+int pwrdm_unregister(struct powerdomain *pwrdm)
+{
+ unsigned long flags;
+
+ if (!pwrdm)
+ return -EINVAL;
+
+ write_lock_irqsave(&pwrdm_rwlock, flags);
+ list_del(&pwrdm->node);
+ write_unlock_irqrestore(&pwrdm_rwlock, flags);
+
+ pr_debug("powerdomain: unregistered %s\n", pwrdm->name);
+
+ return 0;
+}
+
+/**
+ * pwrdm_lookup - look up a powerdomain by name, return a pointer
+ * @name: name of powerdomain
+ *
+ * Find a registered powerdomain by its name. Returns a pointer to the
+ * struct powerdomain if found, or NULL otherwise.
+ */
+struct powerdomain *pwrdm_lookup(const char *name)
+{
+ struct powerdomain *pwrdm;
+ unsigned long flags;
+
+ if (!name)
+ return NULL;
+
+ read_lock_irqsave(&pwrdm_rwlock, flags);
+ pwrdm = _pwrdm_lookup(name);
+ read_unlock_irqrestore(&pwrdm_rwlock, flags);
+
+ return pwrdm;
+}
+
+/**
+ * pwrdm_for_each - call function on each registered clockdomain
+ * @fn: callback function *
+ *
+ * Call the supplied function for each registered powerdomain. The
+ * callback function can return anything but 0 to bail out early from
+ * the iterator. The callback function is called with the pwrdm_rwlock
+ * held for reading, so no powerdomain structure manipulation
+ * functions should be called from the callback, although hardware
+ * powerdomain control functions are fine. Returns the last return
+ * value of the callback function, which should be 0 for success or
+ * anything else to indicate failure; or -EINVAL if the function
+ * pointer is null.
+ */
+int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm))
+{
+ struct powerdomain *temp_pwrdm;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!fn)
+ return -EINVAL;
+
+ read_lock_irqsave(&pwrdm_rwlock, flags);
+ list_for_each_entry(temp_pwrdm, &pwrdm_list, node) {
+ ret = (*fn)(temp_pwrdm);
+ if (ret)
+ break;
+ }
+ read_unlock_irqrestore(&pwrdm_rwlock, flags);
+
+ return ret;
+}
+
+/**
+ * pwrdm_add_clkdm - add a clockdomain to a powerdomain
+ * @pwrdm: struct powerdomain * to add the clockdomain to
+ * @clkdm: struct clockdomain * to associate with a powerdomain
+ *
+ * Associate the clockdomain 'clkdm' with a powerdomain 'pwrdm'. This
+ * enables the use of pwrdm_for_each_clkdm(). Returns -EINVAL if
+ * presented with invalid pointers; -ENOMEM if memory could not be allocated;
+ * or 0 upon success.
+ */
+int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm)
+{
+ unsigned long flags;
+ int i;
+ int ret = -EINVAL;
+
+ if (!pwrdm || !clkdm)
+ return -EINVAL;
+
+ pr_debug("powerdomain: associating clockdomain %s with powerdomain "
+ "%s\n", clkdm->name, pwrdm->name);
+
+ write_lock_irqsave(&pwrdm_rwlock, flags);
+
+ for (i = 0; i < PWRDM_MAX_CLKDMS; i++) {
+ if (!pwrdm->pwrdm_clkdms[i])
+ break;
+#ifdef DEBUG
+ if (pwrdm->pwrdm_clkdms[i] == clkdm) {
+ ret = -EINVAL;
+ goto pac_exit;
+ }
+#endif
+ }
+
+ if (i == PWRDM_MAX_CLKDMS) {
+ pr_debug("powerdomain: increase PWRDM_MAX_CLKDMS for "
+ "pwrdm %s clkdm %s\n", pwrdm->name, clkdm->name);
+ WARN_ON(1);
+ ret = -ENOMEM;
+ goto pac_exit;
+ }
+
+ pwrdm->pwrdm_clkdms[i] = clkdm;
+
+ ret = 0;
+
+pac_exit:
+ write_unlock_irqrestore(&pwrdm_rwlock, flags);
+
+ return ret;
+}
+
+/**
+ * pwrdm_del_clkdm - remove a clockdomain from a powerdomain
+ * @pwrdm: struct powerdomain * to add the clockdomain to
+ * @clkdm: struct clockdomain * to associate with a powerdomain
+ *
+ * Dissociate the clockdomain 'clkdm' from the powerdomain
+ * 'pwrdm'. Returns -EINVAL if presented with invalid pointers;
+ * -ENOENT if the clkdm was not associated with the powerdomain, or 0
+ * upon success.
+ */
+int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm)
+{
+ unsigned long flags;
+ int ret = -EINVAL;
+ int i;
+
+ if (!pwrdm || !clkdm)
+ return -EINVAL;
+
+ pr_debug("powerdomain: dissociating clockdomain %s from powerdomain "
+ "%s\n", clkdm->name, pwrdm->name);
+
+ write_lock_irqsave(&pwrdm_rwlock, flags);
+
+ for (i = 0; i < PWRDM_MAX_CLKDMS; i++)
+ if (pwrdm->pwrdm_clkdms[i] == clkdm)
+ break;
+
+ if (i == PWRDM_MAX_CLKDMS) {
+ pr_debug("powerdomain: clkdm %s not associated with pwrdm "
+ "%s ?!\n", clkdm->name, pwrdm->name);
+ ret = -ENOENT;
+ goto pdc_exit;
+ }
+
+ pwrdm->pwrdm_clkdms[i] = NULL;
+
+ ret = 0;
+
+pdc_exit:
+ write_unlock_irqrestore(&pwrdm_rwlock, flags);
+
+ return ret;
+}
+
+/**
+ * pwrdm_for_each_clkdm - call function on each clkdm in a pwrdm
+ * @pwrdm: struct powerdomain * to iterate over
+ * @fn: callback function *
+ *
+ * Call the supplied function for each clockdomain in the powerdomain
+ * 'pwrdm'. The callback function can return anything but 0 to bail
+ * out early from the iterator. The callback function is called with
+ * the pwrdm_rwlock held for reading, so no powerdomain structure
+ * manipulation functions should be called from the callback, although
+ * hardware powerdomain control functions are fine. Returns -EINVAL
+ * if presented with invalid pointers; or passes along the last return
+ * value of the callback function, which should be 0 for success or
+ * anything else to indicate failure.
+ */
+int pwrdm_for_each_clkdm(struct powerdomain *pwrdm,
+ int (*fn)(struct powerdomain *pwrdm,
+ struct clockdomain *clkdm))
+{
+ unsigned long flags;
+ int ret = 0;
+ int i;
+
+ if (!fn)
+ return -EINVAL;
+
+ read_lock_irqsave(&pwrdm_rwlock, flags);
+
+ for (i = 0; i < PWRDM_MAX_CLKDMS && !ret; i++)
+ ret = (*fn)(pwrdm, pwrdm->pwrdm_clkdms[i]);
+
+ read_unlock_irqrestore(&pwrdm_rwlock, flags);
+
+ return ret;
+}
+
+
+/**
+ * pwrdm_add_wkdep - add a wakeup dependency from pwrdm2 to pwrdm1
+ * @pwrdm1: wake this struct powerdomain * up (dependent)
+ * @pwrdm2: when this struct powerdomain * wakes up (source)
+ *
+ * When the powerdomain represented by pwrdm2 wakes up (due to an
+ * interrupt), wake up pwrdm1. Implemented in hardware on the OMAP,
+ * this feature is designed to reduce wakeup latency of the dependent
+ * powerdomain. Returns -EINVAL if presented with invalid powerdomain
+ * pointers, -ENOENT if pwrdm2 cannot wake up pwrdm1 in hardware, or
+ * 0 upon success.
+ */
+int pwrdm_add_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2)
+{
+ struct powerdomain *p;
+
+ if (!pwrdm1)
+ return -EINVAL;
+
+ p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->wkdep_srcs);
+ if (IS_ERR(p)) {
+ pr_debug("powerdomain: hardware cannot set/clear wake up of "
+ "%s when %s wakes up\n", pwrdm1->name, pwrdm2->name);
+ return IS_ERR(p);
+ }
+
+ pr_debug("powerdomain: hardware will wake up %s when %s wakes up\n",
+ pwrdm1->name, pwrdm2->name);
+
+ prm_set_mod_reg_bits((1 << pwrdm2->dep_bit),
+ pwrdm1->prcm_offs, PM_WKDEP);
+
+ return 0;
+}
+
+/**
+ * pwrdm_del_wkdep - remove a wakeup dependency from pwrdm2 to pwrdm1
+ * @pwrdm1: wake this struct powerdomain * up (dependent)
+ * @pwrdm2: when this struct powerdomain * wakes up (source)
+ *
+ * Remove a wakeup dependency that causes pwrdm1 to wake up when pwrdm2
+ * wakes up. Returns -EINVAL if presented with invalid powerdomain
+ * pointers, -ENOENT if pwrdm2 cannot wake up pwrdm1 in hardware, or
+ * 0 upon success.
+ */
+int pwrdm_del_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2)
+{
+ struct powerdomain *p;
+
+ if (!pwrdm1)
+ return -EINVAL;
+
+ p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->wkdep_srcs);
+ if (IS_ERR(p)) {
+ pr_debug("powerdomain: hardware cannot set/clear wake up of "
+ "%s when %s wakes up\n", pwrdm1->name, pwrdm2->name);
+ return IS_ERR(p);
+ }
+
+ pr_debug("powerdomain: hardware will no longer wake up %s after %s "
+ "wakes up\n", pwrdm1->name, pwrdm2->name);
+
+ prm_clear_mod_reg_bits((1 << pwrdm2->dep_bit),
+ pwrdm1->prcm_offs, PM_WKDEP);
+
+ return 0;
+}
+
+/**
+ * pwrdm_read_wkdep - read wakeup dependency state from pwrdm2 to pwrdm1
+ * @pwrdm1: wake this struct powerdomain * up (dependent)
+ * @pwrdm2: when this struct powerdomain * wakes up (source)
+ *
+ * Return 1 if a hardware wakeup dependency exists wherein pwrdm1 will be
+ * awoken when pwrdm2 wakes up; 0 if dependency is not set; -EINVAL
+ * if either powerdomain pointer is invalid; or -ENOENT if the hardware
+ * is incapable.
+ *
+ * REVISIT: Currently this function only represents software-controllable
+ * wakeup dependencies. Wakeup dependencies fixed in hardware are not
+ * yet handled here.
+ */
+int pwrdm_read_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2)
+{
+ struct powerdomain *p;
+
+ if (!pwrdm1)
+ return -EINVAL;
+
+ p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->wkdep_srcs);
+ if (IS_ERR(p)) {
+ pr_debug("powerdomain: hardware cannot set/clear wake up of "
+ "%s when %s wakes up\n", pwrdm1->name, pwrdm2->name);
+ return IS_ERR(p);
+ }
+
+ return prm_read_mod_bits_shift(pwrdm1->prcm_offs, PM_WKDEP,
+ (1 << pwrdm2->dep_bit));
+}
+
+/**
+ * pwrdm_add_sleepdep - add a sleep dependency from pwrdm2 to pwrdm1
+ * @pwrdm1: prevent this struct powerdomain * from sleeping (dependent)
+ * @pwrdm2: when this struct powerdomain * is active (source)
+ *
+ * Prevent pwrdm1 from automatically going inactive (and then to
+ * retention or off) if pwrdm2 is still active. Returns -EINVAL if
+ * presented with invalid powerdomain pointers or called on a machine
+ * that does not support software-configurable hardware sleep dependencies,
+ * -ENOENT if the specified dependency cannot be set in hardware, or
+ * 0 upon success.
+ */
+int pwrdm_add_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2)
+{
+ struct powerdomain *p;
+
+ if (!pwrdm1)
+ return -EINVAL;
+
+ if (!cpu_is_omap34xx())
+ return -EINVAL;
+
+ p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->sleepdep_srcs);
+ if (IS_ERR(p)) {
+ pr_debug("powerdomain: hardware cannot set/clear sleep "
+ "dependency affecting %s from %s\n", pwrdm1->name,
+ pwrdm2->name);
+ return IS_ERR(p);
+ }
+
+ pr_debug("powerdomain: will prevent %s from sleeping if %s is active\n",
+ pwrdm1->name, pwrdm2->name);
+
+ cm_set_mod_reg_bits((1 << pwrdm2->dep_bit),
+ pwrdm1->prcm_offs, OMAP3430_CM_SLEEPDEP);
+
+ return 0;
+}
+
+/**
+ * pwrdm_del_sleepdep - remove a sleep dependency from pwrdm2 to pwrdm1
+ * @pwrdm1: prevent this struct powerdomain * from sleeping (dependent)
+ * @pwrdm2: when this struct powerdomain * is active (source)
+ *
+ * Allow pwrdm1 to automatically go inactive (and then to retention or
+ * off), independent of the activity state of pwrdm2. Returns -EINVAL
+ * if presented with invalid powerdomain pointers or called on a machine
+ * that does not support software-configurable hardware sleep dependencies,
+ * -ENOENT if the specified dependency cannot be cleared in hardware, or
+ * 0 upon success.
+ */
+int pwrdm_del_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2)
+{
+ struct powerdomain *p;
+
+ if (!pwrdm1)
+ return -EINVAL;
+
+ if (!cpu_is_omap34xx())
+ return -EINVAL;
+
+ p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->sleepdep_srcs);
+ if (IS_ERR(p)) {
+ pr_debug("powerdomain: hardware cannot set/clear sleep "
+ "dependency affecting %s from %s\n", pwrdm1->name,
+ pwrdm2->name);
+ return IS_ERR(p);
+ }
+
+ pr_debug("powerdomain: will no longer prevent %s from sleeping if "
+ "%s is active\n", pwrdm1->name, pwrdm2->name);
+
+ cm_clear_mod_reg_bits((1 << pwrdm2->dep_bit),
+ pwrdm1->prcm_offs, OMAP3430_CM_SLEEPDEP);
+
+ return 0;
+}
+
+/**
+ * pwrdm_read_sleepdep - read sleep dependency state from pwrdm2 to pwrdm1
+ * @pwrdm1: prevent this struct powerdomain * from sleeping (dependent)
+ * @pwrdm2: when this struct powerdomain * is active (source)
+ *
+ * Return 1 if a hardware sleep dependency exists wherein pwrdm1 will
+ * not be allowed to automatically go inactive if pwrdm2 is active;
+ * 0 if pwrdm1's automatic power state inactivity transition is independent
+ * of pwrdm2's; -EINVAL if either powerdomain pointer is invalid or called
+ * on a machine that does not support software-configurable hardware sleep
+ * dependencies; or -ENOENT if the hardware is incapable.
+ *
+ * REVISIT: Currently this function only represents software-controllable
+ * sleep dependencies. Sleep dependencies fixed in hardware are not
+ * yet handled here.
+ */
+int pwrdm_read_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2)
+{
+ struct powerdomain *p;
+
+ if (!pwrdm1)
+ return -EINVAL;
+
+ if (!cpu_is_omap34xx())
+ return -EINVAL;
+
+ p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->sleepdep_srcs);
+ if (IS_ERR(p)) {
+ pr_debug("powerdomain: hardware cannot set/clear sleep "
+ "dependency affecting %s from %s\n", pwrdm1->name,
+ pwrdm2->name);
+ return IS_ERR(p);
+ }
+
+ return prm_read_mod_bits_shift(pwrdm1->prcm_offs, OMAP3430_CM_SLEEPDEP,
+ (1 << pwrdm2->dep_bit));
+}
+
+/**
+ * pwrdm_get_mem_bank_count - get number of memory banks in this powerdomain
+ * @pwrdm: struct powerdomain *
+ *
+ * Return the number of controllable memory banks in powerdomain pwrdm,
+ * starting with 1. Returns -EINVAL if the powerdomain pointer is null.
+ */
+int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm)
+{
+ if (!pwrdm)
+ return -EINVAL;
+
+ return pwrdm->banks;
+}
+
+/**
+ * pwrdm_set_next_pwrst - set next powerdomain power state
+ * @pwrdm: struct powerdomain * to set
+ * @pwrst: one of the PWRDM_POWER_* macros
+ *
+ * Set the powerdomain pwrdm's next power state to pwrst. The powerdomain
+ * may not enter this state immediately if the preconditions for this state
+ * have not been satisfied. Returns -EINVAL if the powerdomain pointer is
+ * null or if the power state is invalid for the powerdomin, or returns 0
+ * upon success.
+ */
+int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst)
+{
+ if (!pwrdm)
+ return -EINVAL;
+
+ if (!(pwrdm->pwrsts & (1 << pwrst)))
+ return -EINVAL;
+
+ pr_debug("powerdomain: setting next powerstate for %s to %0x\n",
+ pwrdm->name, pwrst);
+
+ prm_rmw_mod_reg_bits(OMAP_POWERSTATE_MASK,
+ (pwrst << OMAP_POWERSTATE_SHIFT),
+ pwrdm->prcm_offs, PM_PWSTCTRL);
+
+ return 0;
+}
+
+/**
+ * pwrdm_read_next_pwrst - get next powerdomain power state
+ * @pwrdm: struct powerdomain * to get power state
+ *
+ * Return the powerdomain pwrdm's next power state. Returns -EINVAL
+ * if the powerdomain pointer is null or returns the next power state
+ * upon success.
+ */
+int pwrdm_read_next_pwrst(struct powerdomain *pwrdm)
+{
+ if (!pwrdm)
+ return -EINVAL;
+
+ return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTCTRL,
+ OMAP_POWERSTATE_MASK);
+}
+
+/**
+ * pwrdm_read_pwrst - get current powerdomain power state
+ * @pwrdm: struct powerdomain * to get power state
+ *
+ * Return the powerdomain pwrdm's current power state. Returns -EINVAL
+ * if the powerdomain pointer is null or returns the current power state
+ * upon success.
+ */
+int pwrdm_read_pwrst(struct powerdomain *pwrdm)
+{
+ if (!pwrdm)
+ return -EINVAL;
+
+ return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTST,
+ OMAP_POWERSTATEST_MASK);
+}
+
+/**
+ * pwrdm_read_prev_pwrst - get previous powerdomain power state
+ * @pwrdm: struct powerdomain * to get previous power state
+ *
+ * Return the powerdomain pwrdm's previous power state. Returns -EINVAL
+ * if the powerdomain pointer is null or returns the previous power state
+ * upon success.
+ */
+int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm)
+{
+ if (!pwrdm)
+ return -EINVAL;
+
+ return prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST,
+ OMAP3430_LASTPOWERSTATEENTERED_MASK);
+}
+
+/**
+ * pwrdm_set_logic_retst - set powerdomain logic power state upon retention
+ * @pwrdm: struct powerdomain * to set
+ * @pwrst: one of the PWRDM_POWER_* macros
+ *
+ * Set the next power state that the logic portion of the powerdomain
+ * pwrdm will enter when the powerdomain enters retention. This will
+ * be either RETENTION or OFF, if supported. Returns -EINVAL if the
+ * powerdomain pointer is null or the target power state is not not
+ * supported, or returns 0 upon success.
+ */
+int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst)
+{
+ if (!pwrdm)
+ return -EINVAL;
+
+ if (!(pwrdm->pwrsts_logic_ret & (1 << pwrst)))
+ return -EINVAL;
+
+ pr_debug("powerdomain: setting next logic powerstate for %s to %0x\n",
+ pwrdm->name, pwrst);
+
+ /*
+ * The register bit names below may not correspond to the
+ * actual names of the bits in each powerdomain's register,
+ * but the type of value returned is the same for each
+ * powerdomain.
+ */
+ prm_rmw_mod_reg_bits(OMAP3430_LOGICL1CACHERETSTATE,
+ (pwrst << __ffs(OMAP3430_LOGICL1CACHERETSTATE)),
+ pwrdm->prcm_offs, PM_PWSTCTRL);
+
+ return 0;
+}
+
+/**
+ * pwrdm_set_mem_onst - set memory power state while powerdomain ON
+ * @pwrdm: struct powerdomain * to set
+ * @bank: memory bank number to set (0-3)
+ * @pwrst: one of the PWRDM_POWER_* macros
+ *
+ * Set the next power state that memory bank x of the powerdomain
+ * pwrdm will enter when the powerdomain enters the ON state. Bank
+ * will be a number from 0 to 3, and represents different types of
+ * memory, depending on the powerdomain. Returns -EINVAL if the
+ * powerdomain pointer is null or the target power state is not not
+ * supported for this memory bank, -EEXIST if the target memory bank
+ * does not exist or is not controllable, or returns 0 upon success.
+ */
+int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
+{
+ u32 m;
+
+ if (!pwrdm)
+ return -EINVAL;
+
+ if (pwrdm->banks < (bank + 1))
+ return -EEXIST;
+
+ if (!(pwrdm->pwrsts_mem_on[bank] & (1 << pwrst)))
+ return -EINVAL;
+
+ pr_debug("powerdomain: setting next memory powerstate for domain %s "
+ "bank %0x while pwrdm-ON to %0x\n", pwrdm->name, bank, pwrst);
+
+ /*
+ * The register bit names below may not correspond to the
+ * actual names of the bits in each powerdomain's register,
+ * but the type of value returned is the same for each
+ * powerdomain.
+ */
+ switch (bank) {
+ case 0:
+ m = OMAP3430_SHAREDL1CACHEFLATONSTATE_MASK;
+ break;
+ case 1:
+ m = OMAP3430_L1FLATMEMONSTATE_MASK;
+ break;
+ case 2:
+ m = OMAP3430_SHAREDL2CACHEFLATONSTATE_MASK;
+ break;
+ case 3:
+ m = OMAP3430_L2FLATMEMONSTATE_MASK;
+ break;
+ default:
+ WARN_ON(1); /* should never happen */
+ return -EEXIST;
+ }
+
+ prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)),
+ pwrdm->prcm_offs, PM_PWSTCTRL);
+
+ return 0;
+}
+
+/**
+ * pwrdm_set_mem_retst - set memory power state while powerdomain in RET
+ * @pwrdm: struct powerdomain * to set
+ * @bank: memory bank number to set (0-3)
+ * @pwrst: one of the PWRDM_POWER_* macros
+ *
+ * Set the next power state that memory bank x of the powerdomain
+ * pwrdm will enter when the powerdomain enters the RETENTION state.
+ * Bank will be a number from 0 to 3, and represents different types
+ * of memory, depending on the powerdomain. pwrst will be either
+ * RETENTION or OFF, if supported. Returns -EINVAL if the powerdomain
+ * pointer is null or the target power state is not not supported for
+ * this memory bank, -EEXIST if the target memory bank does not exist
+ * or is not controllable, or returns 0 upon success.
+ */
+int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
+{
+ u32 m;
+
+ if (!pwrdm)
+ return -EINVAL;
+
+ if (pwrdm->banks < (bank + 1))
+ return -EEXIST;
+
+ if (!(pwrdm->pwrsts_mem_ret[bank] & (1 << pwrst)))
+ return -EINVAL;
+
+ pr_debug("powerdomain: setting next memory powerstate for domain %s "
+ "bank %0x while pwrdm-RET to %0x\n", pwrdm->name, bank, pwrst);
+
+ /*
+ * The register bit names below may not correspond to the
+ * actual names of the bits in each powerdomain's register,
+ * but the type of value returned is the same for each
+ * powerdomain.
+ */
+ switch (bank) {
+ case 0:
+ m = OMAP3430_SHAREDL1CACHEFLATRETSTATE;
+ break;
+ case 1:
+ m = OMAP3430_L1FLATMEMRETSTATE;
+ break;
+ case 2:
+ m = OMAP3430_SHAREDL2CACHEFLATRETSTATE;
+ break;
+ case 3:
+ m = OMAP3430_L2FLATMEMRETSTATE;
+ break;
+ default:
+ WARN_ON(1); /* should never happen */
+ return -EEXIST;
+ }
+
+ prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)), pwrdm->prcm_offs,
+ PM_PWSTCTRL);
+
+ return 0;
+}
+
+/**
+ * pwrdm_read_logic_pwrst - get current powerdomain logic retention power state
+ * @pwrdm: struct powerdomain * to get current logic retention power state
+ *
+ * Return the current power state that the logic portion of
+ * powerdomain pwrdm will enter
+ * Returns -EINVAL if the powerdomain pointer is null or returns the
+ * current logic retention power state upon success.
+ */
+int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm)
+{
+ if (!pwrdm)
+ return -EINVAL;
+
+ return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTST,
+ OMAP3430_LOGICSTATEST);
+}
+
+/**
+ * pwrdm_read_prev_logic_pwrst - get previous powerdomain logic power state
+ * @pwrdm: struct powerdomain * to get previous logic power state
+ *
+ * Return the powerdomain pwrdm's logic power state. Returns -EINVAL
+ * if the powerdomain pointer is null or returns the previous logic
+ * power state upon success.
+ */
+int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm)
+{
+ if (!pwrdm)
+ return -EINVAL;
+
+ /*
+ * The register bit names below may not correspond to the
+ * actual names of the bits in each powerdomain's register,
+ * but the type of value returned is the same for each
+ * powerdomain.
+ */
+ return prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST,
+ OMAP3430_LASTLOGICSTATEENTERED);
+}
+
+/**
+ * pwrdm_read_mem_pwrst - get current memory bank power state
+ * @pwrdm: struct powerdomain * to get current memory bank power state
+ * @bank: memory bank number (0-3)
+ *
+ * Return the powerdomain pwrdm's current memory power state for bank
+ * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if
+ * the target memory bank does not exist or is not controllable, or
+ * returns the current memory power state upon success.
+ */
+int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
+{
+ u32 m;
+
+ if (!pwrdm)
+ return -EINVAL;
+
+ if (pwrdm->banks < (bank + 1))
+ return -EEXIST;
+
+ /*
+ * The register bit names below may not correspond to the
+ * actual names of the bits in each powerdomain's register,
+ * but the type of value returned is the same for each
+ * powerdomain.
+ */
+ switch (bank) {
+ case 0:
+ m = OMAP3430_SHAREDL1CACHEFLATSTATEST_MASK;
+ break;
+ case 1:
+ m = OMAP3430_L1FLATMEMSTATEST_MASK;
+ break;
+ case 2:
+ m = OMAP3430_SHAREDL2CACHEFLATSTATEST_MASK;
+ break;
+ case 3:
+ m = OMAP3430_L2FLATMEMSTATEST_MASK;
+ break;
+ default:
+ WARN_ON(1); /* should never happen */
+ return -EEXIST;
+ }
+
+ return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTST, m);
+}
+
+/**
+ * pwrdm_read_prev_mem_pwrst - get previous memory bank power state
+ * @pwrdm: struct powerdomain * to get previous memory bank power state
+ * @bank: memory bank number (0-3)
+ *
+ * Return the powerdomain pwrdm's previous memory power state for bank
+ * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if
+ * the target memory bank does not exist or is not controllable, or
+ * returns the previous memory power state upon success.
+ */
+int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
+{
+ u32 m;
+
+ if (!pwrdm)
+ return -EINVAL;
+
+ if (pwrdm->banks < (bank + 1))
+ return -EEXIST;
+
+ /*
+ * The register bit names below may not correspond to the
+ * actual names of the bits in each powerdomain's register,
+ * but the type of value returned is the same for each
+ * powerdomain.
+ */
+ switch (bank) {
+ case 0:
+ m = OMAP3430_LASTMEM1STATEENTERED_MASK;
+ break;
+ case 1:
+ m = OMAP3430_LASTMEM2STATEENTERED_MASK;
+ break;
+ case 2:
+ m = OMAP3430_LASTSHAREDL2CACHEFLATSTATEENTERED_MASK;
+ break;
+ case 3:
+ m = OMAP3430_LASTL2FLATMEMSTATEENTERED_MASK;
+ break;
+ default:
+ WARN_ON(1); /* should never happen */
+ return -EEXIST;
+ }
+
+ return prm_read_mod_bits_shift(pwrdm->prcm_offs,
+ OMAP3430_PM_PREPWSTST, m);
+}
+
+/**
+ * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm
+ * @pwrdm: struct powerdomain * to clear
+ *
+ * Clear the powerdomain's previous power state register. Clears the
+ * entire register, including logic and memory bank previous power states.
+ * Returns -EINVAL if the powerdomain pointer is null, or returns 0 upon
+ * success.
+ */
+int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm)
+{
+ if (!pwrdm)
+ return -EINVAL;
+
+ /*
+ * XXX should get the powerdomain's current state here;
+ * warn & fail if it is not ON.
+ */
+
+ pr_debug("powerdomain: clearing previous power state reg for %s\n",
+ pwrdm->name);
+
+ prm_write_mod_reg(0, pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST);
+
+ return 0;
+}
+
+/**
+ * pwrdm_enable_hdwr_sar - enable automatic hardware SAR for a pwrdm
+ * @pwrdm: struct powerdomain *
+ *
+ * Enable automatic context save-and-restore upon power state change
+ * for some devices in a powerdomain. Warning: this only affects a
+ * subset of devices in a powerdomain; check the TRM closely. Returns
+ * -EINVAL if the powerdomain pointer is null or if the powerdomain
+ * does not support automatic save-and-restore, or returns 0 upon
+ * success.
+ */
+int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm)
+{
+ if (!pwrdm)
+ return -EINVAL;
+
+ if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR))
+ return -EINVAL;
+
+ pr_debug("powerdomain: %s: setting SAVEANDRESTORE bit\n",
+ pwrdm->name);
+
+ prm_rmw_mod_reg_bits(0, 1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT,
+ pwrdm->prcm_offs, PM_PWSTCTRL);
+
+ return 0;
+}
+
+/**
+ * pwrdm_disable_hdwr_sar - disable automatic hardware SAR for a pwrdm
+ * @pwrdm: struct powerdomain *
+ *
+ * Disable automatic context save-and-restore upon power state change
+ * for some devices in a powerdomain. Warning: this only affects a
+ * subset of devices in a powerdomain; check the TRM closely. Returns
+ * -EINVAL if the powerdomain pointer is null or if the powerdomain
+ * does not support automatic save-and-restore, or returns 0 upon
+ * success.
+ */
+int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm)
+{
+ if (!pwrdm)
+ return -EINVAL;
+
+ if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR))
+ return -EINVAL;
+
+ pr_debug("powerdomain: %s: clearing SAVEANDRESTORE bit\n",
+ pwrdm->name);
+
+ prm_rmw_mod_reg_bits(1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, 0,
+ pwrdm->prcm_offs, PM_PWSTCTRL);
+
+ return 0;
+}
+
+/**
+ * pwrdm_has_hdwr_sar - test whether powerdomain supports hardware SAR
+ * @pwrdm: struct powerdomain *
+ *
+ * Returns 1 if powerdomain 'pwrdm' supports hardware save-and-restore
+ * for some devices, or 0 if it does not.
+ */
+bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm)
+{
+ return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0;
+}
+
+/**
+ * pwrdm_wait_transition - wait for powerdomain power transition to finish
+ * @pwrdm: struct powerdomain * to wait for
+ *
+ * If the powerdomain pwrdm is in the process of a state transition,
+ * spin until it completes the power transition, or until an iteration
+ * bailout value is reached. Returns -EINVAL if the powerdomain
+ * pointer is null, -EAGAIN if the bailout value was reached, or
+ * returns 0 upon success.
+ */
+int pwrdm_wait_transition(struct powerdomain *pwrdm)
+{
+ u32 c = 0;
+
+ if (!pwrdm)
+ return -EINVAL;
+
+ /*
+ * REVISIT: pwrdm_wait_transition() may be better implemented
+ * via a callback and a periodic timer check -- how long do we expect
+ * powerdomain transitions to take?
+ */
+
+ /* XXX Is this udelay() value meaningful? */
+ while ((prm_read_mod_reg(pwrdm->prcm_offs, PM_PWSTST) &
+ OMAP_INTRANSITION) &&
+ (c++ < PWRDM_TRANSITION_BAILOUT))
+ udelay(1);
+
+ if (c >= PWRDM_TRANSITION_BAILOUT) {
+ printk(KERN_ERR "powerdomain: waited too long for "
+ "powerdomain %s to complete transition\n", pwrdm->name);
+ return -EAGAIN;
+ }
+
+ pr_debug("powerdomain: completed transition in %d loops\n", c);
+
+ return 0;
+}
+
+
--- /dev/null
- #include <asm/arch/powerdomain.h>
+/*
+ * OMAP2/3 common powerdomain definitions
+ *
+ * Copyright (C) 2007-8 Texas Instruments, Inc.
+ * Copyright (C) 2007-8 Nokia Corporation
+ *
+ * Written by Paul Walmsley
+ * Debugging and integration fixes by Jouni Högander
+ *
+ * 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.
+ */
+
+#ifndef ARCH_ARM_MACH_OMAP2_POWERDOMAINS
+#define ARCH_ARM_MACH_OMAP2_POWERDOMAINS
+
+/*
+ * This file contains all of the powerdomains that have some element
+ * of software control for the OMAP24xx and OMAP34XX chips.
+ *
+ * A few notes:
+ *
+ * This is not an exhaustive listing of powerdomains on the chips; only
+ * powerdomains that can be controlled in software.
+ *
+ * A useful validation rule for struct powerdomain:
+ * Any powerdomain referenced by a wkdep_srcs or sleepdep_srcs array
+ * must have a dep_bit assigned. So wkdep_srcs/sleepdep_srcs are really
+ * just software-controllable dependencies. Non-software-controllable
+ * dependencies do exist, but they are not encoded below (yet).
+ *
+ * 24xx does not support programmable sleep dependencies (SLEEPDEP)
+ *
+ */
+
+/*
+ * The names for the DSP/IVA2 powerdomains are confusing.
+ *
+ * Most OMAP chips have an on-board DSP.
+ *
+ * On the 2420, this is a 'C55 DSP called, simply, the DSP. Its
+ * powerdomain is called the "DSP power domain." On the 2430, the
+ * on-board DSP is a 'C64 DSP, now called the IVA2 or IVA2.1. Its
+ * powerdomain is still called the "DSP power domain." On the 3430,
+ * the DSP is a 'C64 DSP like the 2430, also known as the IVA2; but
+ * its powerdomain is now called the "IVA2 power domain."
+ *
+ * The 2420 also has something called the IVA, which is a separate ARM
+ * core, and has nothing to do with the DSP/IVA2.
+ *
+ * Ideally the DSP/IVA2 could just be the same powerdomain, but the PRCM
+ * address offset is different between the C55 and C64 DSPs.
+ *
+ * The overly-specific dep_bit names are due to a bit name collision
+ * with CM_FCLKEN_{DSP,IVA2}. The DSP/IVA2 PM_WKDEP and CM_SLEEPDEP shift
+ * value are the same for all powerdomains: 2
+ */
+
+/*
+ * XXX should dep_bit be a mask, so we can test to see if it is 0 as a
+ * sanity check?
+ * XXX encode hardware fixed wakeup dependencies -- esp. for 3430 CORE
+ */
+
++#include <mach/powerdomain.h>
+
+#include "prcm-common.h"
+#include "prm.h"
+#include "cm.h"
+
+/* OMAP2/3-common powerdomains and wakeup dependencies */
+
+/*
+ * 2420/2430 PM_WKDEP_GFX: CORE, MPU, WKUP
+ * 3430ES1 PM_WKDEP_GFX: adds IVA2, removes CORE
+ * 3430ES2 PM_WKDEP_SGX: adds IVA2, removes CORE
+ */
+static struct pwrdm_dep gfx_sgx_wkdeps[] = {
+ {
+ .pwrdm_name = "core_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX)
+ },
+ {
+ .pwrdm_name = "iva2_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "mpu_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX |
+ CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "wkup_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX |
+ CHIP_IS_OMAP3430)
+ },
+ { NULL },
+};
+
+/*
+ * 3430: CM_SLEEPDEP_CAM: MPU
+ * 3430ES1: CM_SLEEPDEP_GFX: MPU
+ * 3430ES2: CM_SLEEPDEP_SGX: MPU
+ */
+static struct pwrdm_dep cam_gfx_sleepdeps[] = {
+ {
+ .pwrdm_name = "mpu_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ { NULL },
+};
+
+
+#include "powerdomains24xx.h"
+#include "powerdomains34xx.h"
+
+
+/*
+ * OMAP2/3 common powerdomains
+ */
+
+/*
+ * The GFX powerdomain is not present on 3430ES2, but currently we do not
+ * have a macro to filter it out at compile-time.
+ */
+static struct powerdomain gfx_pwrdm = {
+ .name = "gfx_pwrdm",
+ .prcm_offs = GFX_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX |
+ CHIP_IS_OMAP3430ES1),
+ .wkdep_srcs = gfx_sgx_wkdeps,
+ .sleepdep_srcs = cam_gfx_sleepdeps,
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .pwrsts_logic_ret = PWRDM_POWER_RET,
+ .banks = 1,
+ .pwrsts_mem_ret = {
+ [0] = PWRDM_POWER_RET, /* MEMRETSTATE */
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRDM_POWER_ON, /* MEMONSTATE */
+ },
+};
+
+static struct powerdomain wkup_pwrdm = {
+ .name = "wkup_pwrdm",
+ .prcm_offs = WKUP_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX | CHIP_IS_OMAP3430),
+ .dep_bit = OMAP_EN_WKUP_SHIFT,
+};
+
+
+
+/* As powerdomains are added or removed above, this list must also be changed */
+static struct powerdomain *powerdomains_omap[] __initdata = {
+
+ &gfx_pwrdm,
+ &wkup_pwrdm,
+
+#ifdef CONFIG_ARCH_OMAP24XX
+ &dsp_pwrdm,
+ &mpu_24xx_pwrdm,
+ &core_24xx_pwrdm,
+#endif
+
+#ifdef CONFIG_ARCH_OMAP2430
+ &mdm_pwrdm,
+#endif
+
+#ifdef CONFIG_ARCH_OMAP34XX
+ &iva2_pwrdm,
+ &mpu_34xx_pwrdm,
+ &neon_pwrdm,
+ &core_34xx_es1_pwrdm,
+ &core_34xx_es2_pwrdm,
+ &cam_pwrdm,
+ &dss_pwrdm,
+ &per_pwrdm,
+ &emu_pwrdm,
+ &sgx_pwrdm,
+ &usbhost_pwrdm,
+#endif
+
+ NULL
+};
+
+
+#endif
--- /dev/null
- #include <asm/arch/powerdomain.h>
+/*
+ * OMAP24XX powerdomain definitions
+ *
+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
+ * Copyright (C) 2007-2008 Nokia Corporation
+ *
+ * Written by Paul Walmsley
+ * Debugging and integration fixes by Jouni Högander
+ *
+ * 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.
+ */
+
+#ifndef ARCH_ARM_MACH_OMAP2_POWERDOMAINS24XX
+#define ARCH_ARM_MACH_OMAP2_POWERDOMAINS24XX
+
+/*
+ * N.B. If powerdomains are added or removed from this file, update
+ * the array in mach-omap2/powerdomains.h.
+ */
+
++#include <mach/powerdomain.h>
+
+#include "prcm-common.h"
+#include "prm.h"
+#include "prm-regbits-24xx.h"
+#include "cm.h"
+#include "cm-regbits-24xx.h"
+
+/* 24XX powerdomains and dependencies */
+
+#ifdef CONFIG_ARCH_OMAP24XX
+
+
+/* Wakeup dependency source arrays */
+
+/*
+ * 2420/2430 PM_WKDEP_DSP: CORE, MPU, WKUP
+ * 2430 PM_WKDEP_MDM: same as above
+ */
+static struct pwrdm_dep dsp_mdm_24xx_wkdeps[] = {
+ {
+ .pwrdm_name = "core_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX)
+ },
+ {
+ .pwrdm_name = "mpu_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX)
+ },
+ {
+ .pwrdm_name = "wkup_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX)
+ },
+ { NULL },
+};
+
+/*
+ * 2420 PM_WKDEP_MPU: CORE, DSP, WKUP
+ * 2430 adds MDM
+ */
+static struct pwrdm_dep mpu_24xx_wkdeps[] = {
+ {
+ .pwrdm_name = "core_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX)
+ },
+ {
+ .pwrdm_name = "dsp_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX)
+ },
+ {
+ .pwrdm_name = "wkup_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX)
+ },
+ {
+ .pwrdm_name = "mdm_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+ },
+ { NULL },
+};
+
+/*
+ * 2420 PM_WKDEP_CORE: DSP, GFX, MPU, WKUP
+ * 2430 adds MDM
+ */
+static struct pwrdm_dep core_24xx_wkdeps[] = {
+ {
+ .pwrdm_name = "dsp_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX)
+ },
+ {
+ .pwrdm_name = "gfx_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX)
+ },
+ {
+ .pwrdm_name = "mpu_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX)
+ },
+ {
+ .pwrdm_name = "wkup_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX)
+ },
+ {
+ .pwrdm_name = "mdm_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430)
+ },
+ { NULL },
+};
+
+
+/* Powerdomains */
+
+static struct powerdomain dsp_pwrdm = {
+ .name = "dsp_pwrdm",
+ .prcm_offs = OMAP24XX_DSP_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX),
+ .dep_bit = OMAP24XX_PM_WKDEP_MPU_EN_DSP_SHIFT,
+ .wkdep_srcs = dsp_mdm_24xx_wkdeps,
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .pwrsts_logic_ret = PWRDM_POWER_RET,
+ .banks = 1,
+ .pwrsts_mem_ret = {
+ [0] = PWRDM_POWER_RET,
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRDM_POWER_ON,
+ },
+};
+
+static struct powerdomain mpu_24xx_pwrdm = {
+ .name = "mpu_pwrdm",
+ .prcm_offs = MPU_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX),
+ .dep_bit = OMAP24XX_EN_MPU_SHIFT,
+ .wkdep_srcs = mpu_24xx_wkdeps,
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .pwrsts_logic_ret = PWRSTS_OFF_RET,
+ .banks = 1,
+ .pwrsts_mem_ret = {
+ [0] = PWRDM_POWER_RET,
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRDM_POWER_ON,
+ },
+};
+
+static struct powerdomain core_24xx_pwrdm = {
+ .name = "core_pwrdm",
+ .prcm_offs = CORE_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP24XX),
+ .wkdep_srcs = core_24xx_wkdeps,
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .dep_bit = OMAP24XX_EN_CORE_SHIFT,
+ .banks = 3,
+ .pwrsts_mem_ret = {
+ [0] = PWRSTS_OFF_RET, /* MEM1RETSTATE */
+ [1] = PWRSTS_OFF_RET, /* MEM2RETSTATE */
+ [2] = PWRSTS_OFF_RET, /* MEM3RETSTATE */
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
+ [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
+ [2] = PWRSTS_OFF_RET_ON, /* MEM3ONSTATE */
+ },
+};
+
+#endif /* CONFIG_ARCH_OMAP24XX */
+
+
+
+/*
+ * 2430-specific powerdomains
+ */
+
+#ifdef CONFIG_ARCH_OMAP2430
+
+/* XXX 2430 KILLDOMAINWKUP bit? No current users apparently */
+
+/* Another case of bit name collisions between several registers: EN_MDM */
+static struct powerdomain mdm_pwrdm = {
+ .name = "mdm_pwrdm",
+ .prcm_offs = OMAP2430_MDM_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP2430),
+ .dep_bit = OMAP2430_PM_WKDEP_MPU_EN_MDM_SHIFT,
+ .wkdep_srcs = dsp_mdm_24xx_wkdeps,
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .pwrsts_logic_ret = PWRDM_POWER_RET,
+ .banks = 1,
+ .pwrsts_mem_ret = {
+ [0] = PWRDM_POWER_RET, /* MEMRETSTATE */
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRDM_POWER_ON, /* MEMONSTATE */
+ },
+};
+
+#endif /* CONFIG_ARCH_OMAP2430 */
+
+
+#endif
--- /dev/null
- #include <asm/arch/powerdomain.h>
+/*
+ * OMAP34XX powerdomain definitions
+ *
+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
+ * Copyright (C) 2007-2008 Nokia Corporation
+ *
+ * Written by Paul Walmsley
+ * Debugging and integration fixes by Jouni Högander
+ *
+ * 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.
+ */
+
+#ifndef ARCH_ARM_MACH_OMAP2_POWERDOMAINS34XX
+#define ARCH_ARM_MACH_OMAP2_POWERDOMAINS34XX
+
+/*
+ * N.B. If powerdomains are added or removed from this file, update
+ * the array in mach-omap2/powerdomains.h.
+ */
+
++#include <mach/powerdomain.h>
+
+#include "prcm-common.h"
+#include "prm.h"
+#include "prm-regbits-34xx.h"
+#include "cm.h"
+#include "cm-regbits-34xx.h"
+
+/*
+ * 34XX-specific powerdomains, dependencies
+ */
+
+#ifdef CONFIG_ARCH_OMAP34XX
+
+/*
+ * 3430: PM_WKDEP_{PER,USBHOST}: CORE, IVA2, MPU, WKUP
+ * (USBHOST is ES2 only)
+ */
+static struct pwrdm_dep per_usbhost_wkdeps[] = {
+ {
+ .pwrdm_name = "core_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "iva2_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "mpu_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "wkup_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ { NULL },
+};
+
+/*
+ * 3430 PM_WKDEP_MPU: CORE, IVA2, DSS, PER
+ */
+static struct pwrdm_dep mpu_34xx_wkdeps[] = {
+ {
+ .pwrdm_name = "core_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "iva2_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "dss_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "per_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ { NULL },
+};
+
+/*
+ * 3430 PM_WKDEP_IVA2: CORE, MPU, WKUP, DSS, PER
+ */
+static struct pwrdm_dep iva2_wkdeps[] = {
+ {
+ .pwrdm_name = "core_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "mpu_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "wkup_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "dss_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "per_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ { NULL },
+};
+
+
+/* 3430 PM_WKDEP_{CAM,DSS}: IVA2, MPU, WKUP */
+static struct pwrdm_dep cam_dss_wkdeps[] = {
+ {
+ .pwrdm_name = "iva2_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "mpu_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "wkup_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ { NULL },
+};
+
+/* 3430: PM_WKDEP_NEON: MPU */
+static struct pwrdm_dep neon_wkdeps[] = {
+ {
+ .pwrdm_name = "mpu_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ { NULL },
+};
+
+
+/* Sleep dependency source arrays for 34xx-specific pwrdms - 34XX only */
+
+/*
+ * 3430: CM_SLEEPDEP_{DSS,PER}: MPU, IVA
+ * 3430ES2: CM_SLEEPDEP_USBHOST: MPU, IVA
+ */
+static struct pwrdm_dep dss_per_usbhost_sleepdeps[] = {
+ {
+ .pwrdm_name = "mpu_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ {
+ .pwrdm_name = "iva2_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430)
+ },
+ { NULL },
+};
+
+
+/*
+ * Powerdomains
+ */
+
+static struct powerdomain iva2_pwrdm = {
+ .name = "iva2_pwrdm",
+ .prcm_offs = OMAP3430_IVA2_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+ .dep_bit = OMAP3430_PM_WKDEP_MPU_EN_IVA2_SHIFT,
+ .wkdep_srcs = iva2_wkdeps,
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .pwrsts_logic_ret = PWRSTS_OFF_RET,
+ .banks = 4,
+ .pwrsts_mem_ret = {
+ [0] = PWRSTS_OFF_RET,
+ [1] = PWRSTS_OFF_RET,
+ [2] = PWRSTS_OFF_RET,
+ [3] = PWRSTS_OFF_RET,
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRDM_POWER_ON,
+ [1] = PWRDM_POWER_ON,
+ [2] = PWRSTS_OFF_ON,
+ [3] = PWRDM_POWER_ON,
+ },
+};
+
+static struct powerdomain mpu_34xx_pwrdm = {
+ .name = "mpu_pwrdm",
+ .prcm_offs = MPU_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+ .dep_bit = OMAP3430_EN_MPU_SHIFT,
+ .wkdep_srcs = mpu_34xx_wkdeps,
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .pwrsts_logic_ret = PWRSTS_OFF_RET,
+ .banks = 1,
+ .pwrsts_mem_ret = {
+ [0] = PWRSTS_OFF_RET,
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRSTS_OFF_ON,
+ },
+};
+
+/* No wkdeps or sleepdeps for 34xx core apparently */
+static struct powerdomain core_34xx_es1_pwrdm = {
+ .name = "core_pwrdm",
+ .prcm_offs = CORE_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .dep_bit = OMAP3430_EN_CORE_SHIFT,
+ .banks = 2,
+ .pwrsts_mem_ret = {
+ [0] = PWRSTS_OFF_RET, /* MEM1RETSTATE */
+ [1] = PWRSTS_OFF_RET, /* MEM2RETSTATE */
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
+ [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
+ },
+};
+
+/* No wkdeps or sleepdeps for 34xx core apparently */
+static struct powerdomain core_34xx_es2_pwrdm = {
+ .name = "core_pwrdm",
+ .prcm_offs = CORE_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES2),
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .dep_bit = OMAP3430_EN_CORE_SHIFT,
+ .flags = PWRDM_HAS_HDWR_SAR, /* for USBTLL only */
+ .banks = 2,
+ .pwrsts_mem_ret = {
+ [0] = PWRSTS_OFF_RET, /* MEM1RETSTATE */
+ [1] = PWRSTS_OFF_RET, /* MEM2RETSTATE */
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
+ [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
+ },
+};
+
+/* Another case of bit name collisions between several registers: EN_DSS */
+static struct powerdomain dss_pwrdm = {
+ .name = "dss_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+ .prcm_offs = OMAP3430_DSS_MOD,
+ .dep_bit = OMAP3430_PM_WKDEP_MPU_EN_DSS_SHIFT,
+ .wkdep_srcs = cam_dss_wkdeps,
+ .sleepdep_srcs = dss_per_usbhost_sleepdeps,
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .pwrsts_logic_ret = PWRDM_POWER_RET,
+ .banks = 1,
+ .pwrsts_mem_ret = {
+ [0] = PWRDM_POWER_RET, /* MEMRETSTATE */
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRDM_POWER_ON, /* MEMONSTATE */
+ },
+};
+
+static struct powerdomain sgx_pwrdm = {
+ .name = "sgx_pwrdm",
+ .prcm_offs = OMAP3430ES2_SGX_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES2),
+ .wkdep_srcs = gfx_sgx_wkdeps,
+ .sleepdep_srcs = cam_gfx_sleepdeps,
+ /* XXX This is accurate for 3430 SGX, but what about GFX? */
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .pwrsts_logic_ret = PWRDM_POWER_RET,
+ .banks = 1,
+ .pwrsts_mem_ret = {
+ [0] = PWRDM_POWER_RET, /* MEMRETSTATE */
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRDM_POWER_ON, /* MEMONSTATE */
+ },
+};
+
+static struct powerdomain cam_pwrdm = {
+ .name = "cam_pwrdm",
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+ .prcm_offs = OMAP3430_CAM_MOD,
+ .wkdep_srcs = cam_dss_wkdeps,
+ .sleepdep_srcs = cam_gfx_sleepdeps,
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .pwrsts_logic_ret = PWRDM_POWER_RET,
+ .banks = 1,
+ .pwrsts_mem_ret = {
+ [0] = PWRDM_POWER_RET, /* MEMRETSTATE */
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRDM_POWER_ON, /* MEMONSTATE */
+ },
+};
+
+static struct powerdomain per_pwrdm = {
+ .name = "per_pwrdm",
+ .prcm_offs = OMAP3430_PER_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+ .dep_bit = OMAP3430_EN_PER_SHIFT,
+ .wkdep_srcs = per_usbhost_wkdeps,
+ .sleepdep_srcs = dss_per_usbhost_sleepdeps,
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .pwrsts_logic_ret = PWRSTS_OFF_RET,
+ .banks = 1,
+ .pwrsts_mem_ret = {
+ [0] = PWRDM_POWER_RET, /* MEMRETSTATE */
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRDM_POWER_ON, /* MEMONSTATE */
+ },
+};
+
+static struct powerdomain emu_pwrdm = {
+ .name = "emu_pwrdm",
+ .prcm_offs = OMAP3430_EMU_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+};
+
+static struct powerdomain neon_pwrdm = {
+ .name = "neon_pwrdm",
+ .prcm_offs = OMAP3430_NEON_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+ .wkdep_srcs = neon_wkdeps,
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .pwrsts_logic_ret = PWRDM_POWER_RET,
+};
+
+static struct powerdomain usbhost_pwrdm = {
+ .name = "usbhost_pwrdm",
+ .prcm_offs = OMAP3430ES2_USBHOST_MOD,
+ .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES2),
+ .wkdep_srcs = per_usbhost_wkdeps,
+ .sleepdep_srcs = dss_per_usbhost_sleepdeps,
+ .pwrsts = PWRSTS_OFF_RET_ON,
+ .pwrsts_logic_ret = PWRDM_POWER_RET,
+ .flags = PWRDM_HAS_HDWR_SAR, /* for USBHOST ctrlr only */
+ .banks = 1,
+ .pwrsts_mem_ret = {
+ [0] = PWRDM_POWER_RET, /* MEMRETSTATE */
+ },
+ .pwrsts_mem_on = {
+ [0] = PWRDM_POWER_ON, /* MEMONSTATE */
+ },
+};
+
+#endif /* CONFIG_ARCH_OMAP34XX */
+
+
+#endif
--- /dev/null
- #include <asm/arch/sdrc.h>
+/*
+ * SDRC register values for the Micron MT46H32M32LF-6
+ *
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Paul Walmsley
+ *
+ * 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.
+ */
+
+#ifndef ARCH_ARM_MACH_OMAP2_SDRAM_MICRON_MT46H32M32LF
+#define ARCH_ARM_MACH_OMAP2_SDRAM_MICRON_MT46H32M32LF
+
++#include <mach/sdrc.h>
+
+/* Micron MT46H32M32LF-6 */
+/* XXX Using ARE = 0x1 (no autorefresh burst) -- can this be changed? */
+static struct omap_sdrc_params mt46h32m32lf6_sdrc_params[] = {
+ [0] = {
+ .rate = 165941176,
+ .actim_ctrla = 0x9a9db4c6,
+ .actim_ctrlb = 0x00011217,
+ .rfr_ctrl = 0x0004dc01,
+ .mr = 0x00000032,
+ },
+ [1] = {
+ .rate = 133333333,
+ .actim_ctrla = 0x7a19b485,
+ .actim_ctrlb = 0x00011213,
+ .rfr_ctrl = 0x0003de01,
+ .mr = 0x00000032,
+ },
+ [2] = {
+ .rate = 82970588,
+ .actim_ctrla = 0x51512283,
+ .actim_ctrlb = 0x0001120c,
+ .rfr_ctrl = 0x00025501,
+ .mr = 0x00000032,
+ },
+ [3] = {
+ .rate = 66666666,
+ .actim_ctrla = 0x410d2243,
+ .actim_ctrlb = 0x0001120a,
+ .rfr_ctrl = 0x0001d601,
+ .mr = 0x00000032,
+ },
+ [4] = {
+ .rate = 0
+ },
+};
+
+#endif
--- /dev/null
- #include <asm/arch/sdrc.h>
+/*
+ * SDRC register values for the Qimonda HYB18M512160AF-6
+ *
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Paul Walmsley
+ *
+ * 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.
+ */
+
+#ifndef ARCH_ARM_MACH_OMAP2_SDRAM_QIMONDA_HYB18M512160AF6
+#define ARCH_ARM_MACH_OMAP2_SDRAM_QIMONDA_HYB18M512160AF6
+
++#include <mach/sdrc.h>
+
+/* Qimonda HYB18M512160AF-6 */
+/* XXX Using ARE = 0x1 (no autorefresh burst) -- can this be changed? */
+static struct omap_sdrc_params hyb18m512160af6_sdrc_params[] = {
+ [0] = {
+ .rate = 165941176,
+ .actim_ctrla = 0x629db4c6,
+ .actim_ctrlb = 0x00012214,
+ .rfr_ctrl = 0x0004dc01,
+ .mr = 0x00000032,
+ },
+ [1] = {
+ .rate = 133333333,
+ .actim_ctrla = 0x5219b485,
+ .actim_ctrlb = 0x00012210,
+ .rfr_ctrl = 0x0003de01,
+ .mr = 0x00000032,
+ },
+ [2] = {
+ .rate = 82970588,
+ .actim_ctrla = 0x31512283,
+ .actim_ctrlb = 0x0001220a,
+ .rfr_ctrl = 0x00025501,
+ .mr = 0x00000022,
+ },
+ [3] = {
+ .rate = 66666666,
+ .actim_ctrla = 0x290d2243,
+ .actim_ctrlb = 0x00012208,
+ .rfr_ctrl = 0x0001d601,
+ .mr = 0x00000022,
+ },
+ [4] = {
+ .rate = 0
+ },
+};
+
+#endif
--- /dev/null
- #include <asm/arch/common.h>
- #include <asm/arch/clock.h>
- #include <asm/arch/sram.h>
+/*
+ * SMS/SDRC (SDRAM controller) common code for OMAP2/3
+ *
+ * Copyright (C) 2005, 2008 Texas Instruments Inc.
+ * Copyright (C) 2005, 2008 Nokia Corporation
+ *
+ * Tony Lindgren <tony@atomide.com>
+ * Paul Walmsley
+ * Richard Woodruff <r-woodruff2@ti.com>
+ *
+ * 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.
+ */
+#undef DEBUG
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
- #include <asm/arch/sdrc.h>
++#include <mach/common.h>
++#include <mach/clock.h>
++#include <mach/sram.h>
+
+#include "prm.h"
+
++#include <mach/sdrc.h>
+#include "sdrc.h"
+
+static struct omap_sdrc_params *sdrc_init_params;
+
+void __iomem *omap2_sdrc_base;
+void __iomem *omap2_sms_base;
+
+
+/**
+ * omap2_sdrc_get_params - return SDRC register values for a given clock rate
+ * @r: SDRC clock rate (in Hz)
+ *
+ * Return pre-calculated values for the SDRC_ACTIM_CTRLA,
+ * SDRC_ACTIM_CTRLB, SDRC_RFR_CTRL, and SDRC_MR registers, for a given
+ * SDRC clock rate 'r'. These parameters control various timing
+ * delays in the SDRAM controller that are expressed in terms of the
+ * number of SDRC clock cycles to wait; hence the clock rate
+ * dependency. Note that sdrc_init_params must be sorted rate
+ * descending. Also assumes that both chip-selects use the same
+ * timing parameters. Returns a struct omap_sdrc_params * upon
+ * success, or NULL upon failure.
+ */
+struct omap_sdrc_params *omap2_sdrc_get_params(unsigned long r)
+{
+ struct omap_sdrc_params *sp;
+
+ sp = sdrc_init_params;
+
+ while (sp->rate != r)
+ sp++;
+
+ if (!sp->rate)
+ return NULL;
+
+ return sp;
+}
+
+
+void __init omap2_set_globals_sdrc(struct omap_globals *omap2_globals)
+{
+ omap2_sdrc_base = omap2_globals->sdrc;
+ omap2_sms_base = omap2_globals->sms;
+}
+
+/* turn on smart idle modes for SDRAM scheduler and controller */
+void __init omap2_sdrc_init(struct omap_sdrc_params *sp)
+{
+ u32 l;
+
+ l = sms_read_reg(SMS_SYSCONFIG);
+ l &= ~(0x3 << 3);
+ l |= (0x2 << 3);
+ sms_write_reg(l, SMS_SYSCONFIG);
+
+ l = sdrc_read_reg(SDRC_SYSCONFIG);
+ l &= ~(0x3 << 3);
+ l |= (0x2 << 3);
+ sdrc_write_reg(l, SDRC_SYSCONFIG);
+
+ sdrc_init_params = sp;
+}
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/clk.h>
-
-#include <asm/io.h>
+#include <linux/io.h>
- #include <asm/arch/common.h>
- #include <asm/arch/clock.h>
- #include <asm/arch/sram.h>
+ #include <mach/common.h>
+ #include <mach/clock.h>
+ #include <mach/sram.h>
#include "prm.h"
- #include <asm/arch/sdrc.h>
-#include "memory.h"
++#include <mach/sdrc.h>
#include "sdrc.h"
-void __iomem *omap2_sdrc_base;
-void __iomem *omap2_sms_base;
+/* Memory timing, DLL mode flags */
+#define M_DDR 1
+#define M_LOCK_CTRL (1 << 2)
+#define M_UNLOCK 0
+#define M_LOCK 1
+
static struct memory_timings mem_timings;
static u32 curr_perf_level = CORE_CLK_SRC_DPLL_X2;
#include <linux/serial_reg.h>
#include <linux/clk.h>
-#include <asm/io.h>
+#include <linux/io.h>
- #include <asm/arch/common.h>
- #include <asm/arch/board.h>
+ #include <mach/common.h>
+ #include <mach/board.h>
-static struct clk * uart1_ick = NULL;
-static struct clk * uart1_fck = NULL;
-static struct clk * uart2_ick = NULL;
-static struct clk * uart2_fck = NULL;
-static struct clk * uart3_ick = NULL;
-static struct clk * uart3_fck = NULL;
+static struct clk *uart_ick[OMAP_MAX_NR_PORTS];
+static struct clk *uart_fck[OMAP_MAX_NR_PORTS];
static struct plat_serial8250_port serial_platform_data[] = {
{
#include <linux/linkage.h>
#include <asm/assembler.h>
- #include <asm/arch/io.h>
- #include <asm/arch/pm.h>
+ #include <mach/io.h>
+ #include <mach/pm.h>
- #include <asm/arch/omap24xx.h>
++#include <mach/omap24xx.h>
+
#include "sdrc.h"
/* First address of reserved address space? apparently valid for OMAP2 & 3 */
--- /dev/null
- #include <asm/arch/io.h>
- #include <asm/arch/pm.h>
+/*
+ * linux/arch/arm/mach-omap2/sleep.S
+ *
+ * (C) Copyright 2004
+ * Texas Instruments, <www.ti.com>
+ * Richard Woodruff <r-woodruff2@ti.com>
+ *
+ * (C) Copyright 2006 Nokia Corporation
+ * Fixed idle loop sleep
+ * Igor Stoppa <igor.stoppa@nokia.com>
+ *
+ * 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/linkage.h>
+#include <asm/assembler.h>
- #include <asm/arch/omap24xx.h>
++#include <mach/io.h>
++#include <mach/pm.h>
+
++#include <mach/omap24xx.h>
+
+#include "sdrc.h"
+
+/* First address of reserved address space? apparently valid for OMAP2 & 3 */
+#define A_SDRC0_V (0xC0000000)
+
+ .text
+
+/*
+ * Forces OMAP into idle state
+ *
+ * omap243x_idle_loop_suspend() - This bit of code just executes the WFI
+ * for normal idles.
+ *
+ * Note: This code get's copied to internal SRAM at boot. When the OMAP
+ * wakes up it continues execution at the point it went to sleep.
+ */
+ENTRY(omap243x_idle_loop_suspend)
+ stmfd sp!, {r0, lr} @ save registers on stack
+ mov r0, #0x0 @ clear for mrc call
+ mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt
+ ldmfd sp!, {r0, pc} @ restore regs and return
+
+ENTRY(omap243x_idle_loop_suspend_sz)
+ .word . - omap243x_idle_loop_suspend
+
+/*
+ * omap243x_cpu_suspend() - Forces OMAP into deep sleep state by completing
+ * SDRC shutdown then ARM shutdown. Upon wake MPU is back on so just restore
+ * SDRC.
+ *
+ * Input:
+ * R0 : DLL ctrl value pre-Sleep
+ *
+ * The if the DPLL is going to AutoIdle. It seems like the DPLL may be back on
+ * when we get called, but the DLL probably isn't. We will wait a bit more in
+ * case the DPLL isn't quite there yet. The code will wait on DLL for DDR even
+ * if in unlocked mode.
+ *
+ * For less than 242x-ES2.2 upon wake from a sleep mode where the external
+ * oscillator was stopped, a timing bug exists where a non-stabilized 12MHz
+ * clock can pass into the PRCM can cause problems at DSP and IVA.
+ * To work around this the code will switch to the 32kHz source prior to sleep.
+ * Post sleep we will shift back to using the DPLL. Apparently,
+ * CM_IDLEST_CLKGEN does not reflect the full clock change so you need to wait
+ * 3x12MHz + 3x32kHz clocks for a full switch.
+ *
+ * The DLL load value is not kept in RETENTION or OFF. It needs to be restored
+ * at wake
+ */
+ENTRY(omap243x_cpu_suspend)
+ stmfd sp!, {r0 - r12, lr} @ save registers on stack
+ mov r3, #0x0 @ clear for mrc call
+ mcr p15, 0, r3, c7, c10, 4 @ memory barrier, hope SDR/DDR finished
+ nop
+ nop
+ ldr r3, omap2_ocs_sdrc_power @ addr of sdrc power
+ ldr r4, [r3] @ value of sdrc power
+ orr r4, r4, #0x40 @ enable self refresh on idle req
+ mov r5, #0x2000 @ set delay (DPLL relock + DLL relock)
+ str r4, [r3] @ make it so
+ mov r2, #0
+ nop
+ mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt
+ nop
+loop:
+ subs r5, r5, #0x1 @ awake, wait just a bit
+ bne loop
+
+ /* The DPLL has on before we take the DDR out of self refresh */
+ bic r4, r4, #0x40 @ now clear self refresh bit.
+ str r4, [r3] @ put vlaue back.
+ ldr r4, A_SDRC0 @ make a clock happen
+ ldr r4, [r4]
+ nop @ start auto refresh only after clk ok
+ movs r0, r0 @ see if DDR or SDR
+ ldrne r1, omap2_ocs_sdrc_dlla_ctrl @ get addr of DLL ctrl
+ strne r0, [r1] @ rewrite DLLA to force DLL reload
+ addne r1, r1, #0x8 @ move to DLLB
+ strne r0, [r1] @ rewrite DLLB to force DLL reload
+
+ mov r5, #0x1000
+loop2:
+ subs r5, r5, #0x1
+ bne loop2
+ /* resume*/
+ ldmfd sp!, {r0 - r12, pc} @ restore regs and return
+
+omap2_ocs_sdrc_power:
+ .word OMAP243X_SDRC_REGADDR(SDRC_POWER)
+A_SDRC0:
+ .word A_SDRC0_V
+omap2_ocs_sdrc_dlla_ctrl:
+ .word OMAP243X_SDRC_REGADDR(SDRC_DLLA_CTRL)
+
+ENTRY(omap243x_cpu_suspend_sz)
+ .word . - omap243x_cpu_suspend
+
--- /dev/null
- #include <asm/arch/io.h>
- #include <asm/arch/pm.h>
- #include <asm/arch/control.h>
+/*
+ * linux/arch/arm/mach-omap2/sleep.S
+ *
+ * (C) Copyright 2007
+ * Texas Instruments
+ * Karthik Dasu <karthik-dp@ti.com>
+ *
+ * (C) Copyright 2004
+ * Texas Instruments, <www.ti.com>
+ * Richard Woodruff <r-woodruff2@ti.com>
+ *
+ * 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/linkage.h>
+#include <asm/assembler.h>
++#include <mach/io.h>
++#include <mach/pm.h>
++#include <mach/control.h>
+
+#include "prm.h"
+#include "sdrc.h"
+
+#define PM_PREPWSTST_CORE_V OMAP34XX_PRM_REGADDR(CORE_MOD, \
+ OMAP3430_PM_PREPWSTST)
+#define PM_PREPWSTST_MPU_V OMAP34XX_PRM_REGADDR(MPU_MOD, \
+ OMAP3430_PM_PREPWSTST)
+#define PM_PWSTCTRL_MPU_P OMAP34XX_PRM_REGADDR(MPU_MOD, PM_PWSTCTRL)
+#define SCRATCHPAD_MEM_OFFS 0x310 /* Move this as correct place is
+ * available */
+#define SCRATCHPAD_BASE_P OMAP343X_CTRL_REGADDR(\
+ OMAP343X_CONTROL_MEM_WKUP +\
+ SCRATCHPAD_MEM_OFFS)
+#define SDRC_POWER_V OMAP34XX_SDRC_REGADDR(SDRC_POWER)
+
+ .text
+/* Function call to get the restore pointer for resume from OFF */
+ENTRY(get_restore_pointer)
+ stmfd sp!, {lr} @ save registers on stack
+ adr r0, restore
+ ldmfd sp!, {pc} @ restore regs and return
+ENTRY(get_restore_pointer_sz)
+ .word . - get_restore_pointer_sz
+/*
+ * Forces OMAP into idle state
+ *
+ * omap34xx_suspend() - This bit of code just executes the WFI
+ * for normal idles.
+ *
+ * Note: This code get's copied to internal SRAM at boot. When the OMAP
+ * wakes up it continues execution at the point it went to sleep.
+ */
+ENTRY(omap34xx_cpu_suspend)
+ stmfd sp!, {r0-r12, lr} @ save registers on stack
+loop:
+ /*b loop*/ @Enable to debug by stepping through code
+ /* r0 contains restore pointer in sdram */
+ /* r1 contains information about saving context */
+ ldr r4, sdrc_power @ read the SDRC_POWER register
+ ldr r5, [r4] @ read the contents of SDRC_POWER
+ orr r5, r5, #0x40 @ enable self refresh on idle req
+ str r5, [r4] @ write back to SDRC_POWER register
+
+ cmp r1, #0x0
+ /* If context save is required, do that and execute wfi */
+ bne save_context_wfi
+ /* Data memory barrier and Data sync barrier */
+ mov r1, #0
+ mcr p15, 0, r1, c7, c10, 4
+ mcr p15, 0, r1, c7, c10, 5
+
+ wfi @ wait for interrupt
+
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ bl i_dll_wait
+
+ ldmfd sp!, {r0-r12, pc} @ restore regs and return
+restore:
+ /* b restore*/ @ Enable to debug restore code
+ /* Check what was the reason for mpu reset and store the reason in r9*/
+ /* 1 - Only L1 and logic lost */
+ /* 2 - Only L2 lost - In this case, we wont be here */
+ /* 3 - Both L1 and L2 lost */
+ ldr r1, pm_pwstctrl_mpu
+ ldr r2, [r1]
+ and r2, r2, #0x3
+ cmp r2, #0x0 @ Check if target power state was OFF or RET
+ moveq r9, #0x3 @ MPU OFF => L1 and L2 lost
+ movne r9, #0x1 @ Only L1 and L2 lost => avoid L2 invalidation
+ bne logic_l1_restore
+ /* Execute smi to invalidate L2 cache */
+ mov r12, #0x1 @ set up to invalide L2
+smi: .word 0xE1600070 @ Call SMI monitor (smieq)
+logic_l1_restore:
+ mov r1, #0
+ /* Invalidate all instruction caches to PoU
+ * and flush branch target cache */
+ mcr p15, 0, r1, c7, c5, 0
+
+ ldr r4, scratchpad_base
+ ldr r3, [r4,#0xBC]
+ ldmia r3!, {r4-r6}
+ mov sp, r4
+ msr spsr_cxsf, r5
+ mov lr, r6
+
+ ldmia r3!, {r4-r9}
+ /* Coprocessor access Control Register */
+ mcr p15, 0, r4, c1, c0, 2
+
+ /* TTBR0 */
+ MCR p15, 0, r5, c2, c0, 0
+ /* TTBR1 */
+ MCR p15, 0, r6, c2, c0, 1
+ /* Translation table base control register */
+ MCR p15, 0, r7, c2, c0, 2
+ /*domain access Control Register */
+ MCR p15, 0, r8, c3, c0, 0
+ /* data fault status Register */
+ MCR p15, 0, r9, c5, c0, 0
+
+ ldmia r3!,{r4-r8}
+ /* instruction fault status Register */
+ MCR p15, 0, r4, c5, c0, 1
+ /*Data Auxiliary Fault Status Register */
+ MCR p15, 0, r5, c5, c1, 0
+ /*Instruction Auxiliary Fault Status Register*/
+ MCR p15, 0, r6, c5, c1, 1
+ /*Data Fault Address Register */
+ MCR p15, 0, r7, c6, c0, 0
+ /*Instruction Fault Address Register*/
+ MCR p15, 0, r8, c6, c0, 2
+ ldmia r3!,{r4-r7}
+
+ /* user r/w thread and process ID */
+ MCR p15, 0, r4, c13, c0, 2
+ /* user ro thread and process ID */
+ MCR p15, 0, r5, c13, c0, 3
+ /*Privileged only thread and process ID */
+ MCR p15, 0, r6, c13, c0, 4
+ /* cache size selection */
+ MCR p15, 2, r7, c0, c0, 0
+ ldmia r3!,{r4-r8}
+ /* Data TLB lockdown registers */
+ MCR p15, 0, r4, c10, c0, 0
+ /* Instruction TLB lockdown registers */
+ MCR p15, 0, r5, c10, c0, 1
+ /* Secure or Nonsecure Vector Base Address */
+ MCR p15, 0, r6, c12, c0, 0
+ /* FCSE PID */
+ MCR p15, 0, r7, c13, c0, 0
+ /* Context PID */
+ MCR p15, 0, r8, c13, c0, 1
+
+ ldmia r3!,{r4-r5}
+ /* primary memory remap register */
+ MCR p15, 0, r4, c10, c2, 0
+ /*normal memory remap register */
+ MCR p15, 0, r5, c10, c2, 1
+
+ /* Restore registers for other modes from SDRAM */
+ /* Save current mode */
+ mrs r7, cpsr
+
+ /* FIQ mode */
+ bic r0, r7, #0x1F
+ orr r0, r0, #0x11
+ msr cpsr, r0
+ ldmia r3!, {r8-r12}
+ /* load the SP and LR from SDRAM */
+ ldmia r3!,{r4-r6}
+ mov sp, r4 /*update the SP */
+ mov lr, r5 /*update the LR */
+ msr spsr, r6 /*update the SPSR*/
+
+ /* IRQ mode */
+ bic r0, r7, #0x1F
+ orr r0, r0, #0x12
+ msr cpsr, r0 /*go into IRQ mode*/
+ ldmia r3!,{r4-r6} /*load the SP and LR from SDRAM*/
+ mov sp, r4 /*update the SP */
+ mov lr, r5 /*update the LR */
+ msr spsr, r6 /*update the SPSR */
+
+ /* ABORT mode */
+ bic r0, r7, #0x1F
+ orr r0, r0, #0x17
+ msr cpsr, r0 /* go into ABORT mode */
+ ldmia r3!,{r4-r6} /*load the SP and LR from SDRAM */
+ mov sp, r4 /*update the SP */
+ mov lr, r5 /*update the LR */
+ msr spsr, r6 /*update the SPSR */
+
+ /* UNDEEF mode */
+ bic r0, r7, #0x1F
+ orr r0, r0, #0x1B
+ msr cpsr, r0 /*go into UNDEF mode */
+ ldmia r3!,{r4-r6} /*load the SP and LR from SDRAM */
+ mov sp, r4 /*update the SP*/
+ mov lr, r5 /*update the LR*/
+ msr spsr, r6 /*update the SPSR*/
+
+ /* SYSTEM (USER) mode */
+ bic r0, r7, #0x1F
+ orr r0, r0, #0x1F
+ msr cpsr, r0 /*go into USR mode */
+ ldmia r3!,{r4-r6} /*load the SP and LR from SDRAM*/
+ mov sp, r4 /*update the SP */
+ mov lr, r5 /*update the LR */
+ msr spsr, r6 /*update the SPSR */
+ msr cpsr, r7 /*back to original mode*/
+
+ /* Restore cpsr */
+ ldmia r3!,{r4} /*load CPSR from SDRAM*/
+ msr cpsr, r4 /*store cpsr */
+
+ /* Enabling MMU here */
+ mrc p15, 0, r7, c2, c0, 2 /* Read TTBRControl */
+ /* Extract N (0:2) bits and decide whether to use TTBR0 or TTBR1*/
+ and r7, #0x7
+ cmp r7, #0x0
+ beq usettbr0
+ttbr_error:
+ /* More work needs to be done to support N[0:2] value other than 0
+ * So looping here so that the error can be detected
+ */
+ b ttbr_error
+usettbr0:
+ mrc p15, 0, r2, c2, c0, 0
+ ldr r5, ttbrbit_mask
+ and r2, r5
+ mov r4, pc
+ ldr r5, table_index_mask
+ and r4, r5 /* r4 = 31 to 20 bits of pc */
+ /* Extract the value to be written to table entry */
+ ldr r1, table_entry
+ add r1, r1, r4 /* r1 has value to be written to table entry*/
+ /* Getting the address of table entry to modify */
+ lsr r4, #18
+ add r2, r4 /* r2 has the location which needs to be modified */
+ /* Storing previous entry of location being modified */
+ ldr r5, scratchpad_base
+ ldr r4, [r2]
+ str r4, [r5, #0xC0]
+ /* Modify the table entry */
+ str r1, [r2]
+ /* Storing address of entry being modified
+ * - will be restored after enabling MMU */
+ ldr r5, scratchpad_base
+ str r2, [r5, #0xC4]
+
+ mov r0, #0
+ mcr p15, 0, r0, c7, c5, 4 @ Flush prefetch buffer
+ mcr p15, 0, r0, c7, c5, 6 @ Invalidate branch predictor array
+ mcr p15, 0, r0, c8, c5, 0 @ Invalidate instruction TLB
+ mcr p15, 0, r0, c8, c6, 0 @ Invalidate data TLB
+ /* Restore control register but dont enable caches here*/
+ /* Caches will be enabled after restoring MMU table entry */
+ ldmia r3!, {r4}
+ /* Store previous value of control register in scratchpad */
+ str r4, [r5, #0xC8]
+ ldr r2, cache_pred_disable_mask
+ and r4, r2
+ mcr p15, 0, r4, c1, c0, 0
+
+ ldmfd sp!, {r0-r12, pc} @ restore regs and return
+save_context_wfi:
+ /*b save_context_wfi*/ @ enable to debug save code
+ mov r8, r0 /* Store SDRAM address in r8 */
+ /* Check what that target sleep state is:stored in r1*/
+ /* 1 - Only L1 and logic lost */
+ /* 2 - Only L2 lost */
+ /* 3 - Both L1 and L2 lost */
+ cmp r1, #0x2 /* Only L2 lost */
+ beq clean_l2
+ cmp r1, #0x1 /* L2 retained */
+ /* r9 stores whether to clean L2 or not*/
+ moveq r9, #0x0 /* Dont Clean L2 */
+ movne r9, #0x1 /* Clean L2 */
+l1_logic_lost:
+ /* Store sp and spsr to SDRAM */
+ mov r4, sp
+ mrs r5, spsr
+ mov r6, lr
+ stmia r8!, {r4-r6}
+ /* Save all ARM registers */
+ /* Coprocessor access control register */
+ mrc p15, 0, r6, c1, c0, 2
+ stmia r8!, {r6}
+ /* TTBR0, TTBR1 and Translation table base control */
+ mrc p15, 0, r4, c2, c0, 0
+ mrc p15, 0, r5, c2, c0, 1
+ mrc p15, 0, r6, c2, c0, 2
+ stmia r8!, {r4-r6}
+ /* Domain access control register, data fault status register,
+ and instruction fault status register */
+ mrc p15, 0, r4, c3, c0, 0
+ mrc p15, 0, r5, c5, c0, 0
+ mrc p15, 0, r6, c5, c0, 1
+ stmia r8!, {r4-r6}
+ /* Data aux fault status register, instruction aux fault status,
+ datat fault address register and instruction fault address register*/
+ mrc p15, 0, r4, c5, c1, 0
+ mrc p15, 0, r5, c5, c1, 1
+ mrc p15, 0, r6, c6, c0, 0
+ mrc p15, 0, r7, c6, c0, 2
+ stmia r8!, {r4-r7}
+ /* user r/w thread and process ID, user r/o thread and process ID,
+ priv only thread and process ID, cache size selection */
+ mrc p15, 0, r4, c13, c0, 2
+ mrc p15, 0, r5, c13, c0, 3
+ mrc p15, 0, r6, c13, c0, 4
+ mrc p15, 2, r7, c0, c0, 0
+ stmia r8!, {r4-r7}
+ /* Data TLB lockdown, instruction TLB lockdown registers */
+ mrc p15, 0, r5, c10, c0, 0
+ mrc p15, 0, r6, c10, c0, 1
+ stmia r8!, {r5-r6}
+ /* Secure or non secure vector base address, FCSE PID, Context PID*/
+ mrc p15, 0, r4, c12, c0, 0
+ mrc p15, 0, r5, c13, c0, 0
+ mrc p15, 0, r6, c13, c0, 1
+ stmia r8!, {r4-r6}
+ /* Primary remap, normal remap registers */
+ mrc p15, 0, r4, c10, c2, 0
+ mrc p15, 0, r5, c10, c2, 1
+ stmia r8!,{r4-r5}
+ /* Store SP, LR, SPSR registers for SUP, FIQ, IRQ, ABORT and USER
+ modes into SDRAM */
+
+ /* move SDRAM address to r7 as r8 is banked in FIQ*/
+ mov r7, r8
+
+ /* Save current mode */
+ mrs r2, cpsr
+ /* FIQ mode */
+ bic r0, r2, #0x1F
+ orr r0, r0, #0x11
+ msr cpsr, r0 /* go to FIQ mode */
+ stmia r7!, {r8-r12}
+ mov r4, r13 /* move SP into r4*/
+ mov r5, r14
+ mrs r6, spsr
+ stmia r7!, {r4-r6}
+
+ /* IRQ mode */
+ bic r0, r2, #0x1F
+ orr r0, r0, #0x12
+ msr cpsr, r0
+ mov r4, r13
+ mov r5, r14
+ mrs r6, spsr
+ stmia r7!, {r4-r6}
+
+ /* Abort mode */
+ bic r0, r2, #0x1F
+ orr r0, r0, #0x17
+ msr cpsr, r0
+ mov r4, r13
+ mov r5, r14
+ mrs r6, spsr
+ stmia r7!, {r4-r6}
+
+ /* UNDEF mode */
+ bic r0, r2, #0x1F
+ orr r0, r0, #0x1B
+ msr cpsr, r0
+ mov r4, r13
+ mov r5, r14
+ mrs r6, spsr
+ stmia r7!, {r4-r6}
+
+ /* System (USER mode) */
+ bic r0, r2, #0x1F
+ orr r0, r0, #0x1F
+ msr cpsr, r0
+ mov r4, r13
+ mov r5, r14
+ mrs r6, spsr
+ stmia r7!, {r4-r6}
+
+ /* Back to original mode */
+ msr cpsr, r2
+
+ /* Store current cpsr*/
+ stmia r7!, {r2}
+
+ mrc p15, 0, r4, c1, c0, 0
+ /* save control register */
+ stmia r7!, {r4}
+clean_caches:
+ /* Clean Data or unified cache to POU*/
+ /* How to invalidate only L1 cache???? - #FIX_ME# */
+ /* mcr p15, 0, r11, c7, c11, 1 */
+ cmp r9, #1 /* Check whether L2 inval is required or not*/
+ bne skip_l2_inval
+clean_l2:
+ /* read clidr */
+ mrc p15, 1, r0, c0, c0, 1
+ /* extract loc from clidr */
+ ands r3, r0, #0x7000000
+ /* left align loc bit field */
+ mov r3, r3, lsr #23
+ /* if loc is 0, then no need to clean */
+ beq finished
+ /* start clean at cache level 0 */
+ mov r10, #0
+loop1:
+ /* work out 3x current cache level */
+ add r2, r10, r10, lsr #1
+ /* extract cache type bits from clidr*/
+ mov r1, r0, lsr r2
+ /* mask of the bits for current cache only */
+ and r1, r1, #7
+ /* see what cache we have at this level */
+ cmp r1, #2
+ /* skip if no cache, or just i-cache */
+ blt skip
+ /* select current cache level in cssr */
+ mcr p15, 2, r10, c0, c0, 0
+ /* isb to sych the new cssr&csidr */
+ isb
+ /* read the new csidr */
+ mrc p15, 1, r1, c0, c0, 0
+ /* extract the length of the cache lines */
+ and r2, r1, #7
+ /* add 4 (line length offset) */
+ add r2, r2, #4
+ ldr r4, assoc_mask
+ /* find maximum number on the way size */
+ ands r4, r4, r1, lsr #3
+ /* find bit position of way size increment */
+ clz r5, r4
+ ldr r7, numset_mask
+ /* extract max number of the index size*/
+ ands r7, r7, r1, lsr #13
+loop2:
+ mov r9, r4
+ /* create working copy of max way size*/
+loop3:
+ /* factor way and cache number into r11 */
+ orr r11, r10, r9, lsl r5
+ /* factor index number into r11 */
+ orr r11, r11, r7, lsl r2
+ /*clean & invalidate by set/way */
+ mcr p15, 0, r11, c7, c10, 2
+ /* decrement the way*/
+ subs r9, r9, #1
+ bge loop3
+ /*decrement the index */
+ subs r7, r7, #1
+ bge loop2
+skip:
+ add r10, r10, #2
+ /* increment cache number */
+ cmp r3, r10
+ bgt loop1
+finished:
+ /*swith back to cache level 0 */
+ mov r10, #0
+ /* select current cache level in cssr */
+ mcr p15, 2, r10, c0, c0, 0
+ isb
+skip_l2_inval:
+ /* Data memory barrier and Data sync barrier */
+ mov r1, #0
+ mcr p15, 0, r1, c7, c10, 4
+ mcr p15, 0, r1, c7, c10, 5
+
+ wfi @ wait for interrupt
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ bl i_dll_wait
+ /* restore regs and return */
+ ldmfd sp!, {r0-r12, pc}
+
+i_dll_wait:
+ ldr r4, clk_stabilize_delay
+
+i_dll_delay:
+ subs r4, r4, #0x1
+ bne i_dll_delay
+ ldr r4, sdrc_power
+ ldr r5, [r4]
+ bic r5, r5, #0x40
+ str r5, [r4]
+ bx lr
+pm_prepwstst_core:
+ .word PM_PREPWSTST_CORE_V
+pm_prepwstst_mpu:
+ .word PM_PREPWSTST_MPU_V
+pm_pwstctrl_mpu:
+ .word PM_PWSTCTRL_MPU_P
+scratchpad_base:
+ .word SCRATCHPAD_BASE_P
+sdrc_power:
+ .word SDRC_POWER_V
+context_mem:
+ .word 0x803E3E14
+clk_stabilize_delay:
+ .word 0x000001FF
+assoc_mask:
+ .word 0x3ff
+numset_mask:
+ .word 0x7fff
+ttbrbit_mask:
+ .word 0xFFFFC000
+table_index_mask:
+ .word 0xFFF00000
+table_entry:
+ .word 0x00000C02
+cache_pred_disable_mask:
+ .word 0xFFFFE7FB
+ENTRY(omap34xx_cpu_suspend_sz)
+ .word . - omap34xx_cpu_suspend
--- /dev/null
- #include <asm/arch/omap34xx.h>
- #include <asm/arch/control.h>
- #include <asm/arch/clock.h>
+/*
+ * linux/arch/arm/mach-omap3/smartreflex.c
+ *
+ * OMAP34XX SmartReflex Voltage Control
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Kalle Jokiniemi
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ * Lesly A M <x0080970@ti.com>
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/io.h>
+
++#include <mach/omap34xx.h>
++#include <mach/control.h>
++#include <mach/clock.h>
+
+#include "prm.h"
+#include "smartreflex.h"
+#include "prm-regbits-34xx.h"
+
+/* XXX: These should be relocated where-ever the OPP implementation will be */
+u32 current_vdd1_opp;
+u32 current_vdd2_opp;
+
+struct omap_sr {
+ int srid;
+ int is_sr_reset;
+ int is_autocomp_active;
+ struct clk *clk;
+ u32 clk_length;
+ u32 req_opp_no;
+ u32 opp1_nvalue, opp2_nvalue, opp3_nvalue, opp4_nvalue;
+ u32 opp5_nvalue;
+ u32 senp_mod, senn_mod;
+ u32 srbase_addr;
+ u32 vpbase_addr;
+};
+
+/* Custom clocks to enable SR specific enable/disable functions. */
+struct sr_custom_clk {
+ struct clk clk; /* meta-clock with custom enable/disable calls */
+ struct clk *fck; /* actual functional clock */
+ struct omap_sr *sr;
+};
+
+#define SR_REGADDR(offs) (__force void __iomem *)(sr->srbase_addr + offset)
+
+static inline void sr_write_reg(struct omap_sr *sr, int offset, u32 value)
+{
+ __raw_writel(value, SR_REGADDR(offset));
+}
+
+static inline void sr_modify_reg(struct omap_sr *sr, int offset, u32 mask,
+ u32 value)
+{
+ u32 reg_val;
+
+ reg_val = __raw_readl(SR_REGADDR(offset));
+ reg_val &= ~mask;
+ reg_val |= value;
+
+ __raw_writel(reg_val, SR_REGADDR(offset));
+}
+
+static inline u32 sr_read_reg(struct omap_sr *sr, int offset)
+{
+ return __raw_readl(SR_REGADDR(offset));
+}
+
+/* Custom clock handling functions */
+static int sr_clk_enable(struct clk *clk)
+{
+ struct sr_custom_clk *sr_clk = container_of(clk, struct sr_custom_clk,
+ clk);
+
+ if (clk_enable(sr_clk->fck) != 0) {
+ printk(KERN_ERR "Could not enable %s\n", sr_clk->fck->name);
+ goto clk_enable_err;
+ }
+
+ /* set fclk- active , iclk- idle */
+ sr_modify_reg(sr_clk->sr, ERRCONFIG, SR_CLKACTIVITY_MASK,
+ SR_CLKACTIVITY_IOFF_FON);
+
+ return 0;
+
+clk_enable_err:
+ return -1;
+}
+
+static void sr_clk_disable(struct clk *clk)
+{
+ struct sr_custom_clk *sr_clk = container_of(clk, struct sr_custom_clk,
+ clk);
+
+ /* set fclk, iclk- idle */
+ sr_modify_reg(sr_clk->sr, ERRCONFIG, SR_CLKACTIVITY_MASK,
+ SR_CLKACTIVITY_IOFF_FOFF);
+
+ clk_disable(sr_clk->fck);
+ sr_clk->sr->is_sr_reset = 1;
+}
+
+static struct omap_sr sr1 = {
+ .srid = SR1,
+ .is_sr_reset = 1,
+ .is_autocomp_active = 0,
+ .clk_length = 0,
+ .srbase_addr = OMAP2_IO_ADDRESS(OMAP34XX_SR1_BASE),
+};
+
+static struct omap_sr sr2 = {
+ .srid = SR2,
+ .is_sr_reset = 1,
+ .is_autocomp_active = 0,
+ .clk_length = 0,
+ .srbase_addr = OMAP2_IO_ADDRESS(OMAP34XX_SR2_BASE),
+};
+
+static struct sr_custom_clk sr1_custom_clk = {
+ .clk = {
+ .name = "sr1_custom_clk",
+ .enable = sr_clk_enable,
+ .disable = sr_clk_disable,
+ },
+ .sr = &sr1,
+};
+
+static struct sr_custom_clk sr2_custom_clk = {
+ .clk = {
+ .name = "sr2_custom_clk",
+ .enable = sr_clk_enable,
+ .disable = sr_clk_disable,
+ },
+ .sr = &sr2,
+};
+
+static void cal_reciprocal(u32 sensor, u32 *sengain, u32 *rnsen)
+{
+ u32 gn, rn, mul;
+
+ for (gn = 0; gn < GAIN_MAXLIMIT; gn++) {
+ mul = 1 << (gn + 8);
+ rn = mul / sensor;
+ if (rn < R_MAXLIMIT) {
+ *sengain = gn;
+ *rnsen = rn;
+ }
+ }
+}
+
+static u32 cal_test_nvalue(u32 sennval, u32 senpval)
+{
+ u32 senpgain, senngain;
+ u32 rnsenp, rnsenn;
+
+ /* Calculating the gain and reciprocal of the SenN and SenP values */
+ cal_reciprocal(senpval, &senpgain, &rnsenp);
+ cal_reciprocal(sennval, &senngain, &rnsenn);
+
+ return ((senpgain << NVALUERECIPROCAL_SENPGAIN_SHIFT) |
+ (senngain << NVALUERECIPROCAL_SENNGAIN_SHIFT) |
+ (rnsenp << NVALUERECIPROCAL_RNSENP_SHIFT) |
+ (rnsenn << NVALUERECIPROCAL_RNSENN_SHIFT));
+}
+
+static void sr_clk_init(struct sr_custom_clk *sr_clk)
+{
+ if (sr_clk->sr->srid == SR1) {
+ sr_clk->fck = clk_get(NULL, "sr1_fck");
+ if (IS_ERR(sr_clk->fck))
+ printk(KERN_ERR "Could not get sr1_fck\n");
+ } else if (sr_clk->sr->srid == SR2) {
+ sr_clk->fck = clk_get(NULL, "sr2_fck");
+ if (IS_ERR(sr_clk->fck))
+ printk(KERN_ERR "Could not get sr2_fck\n");
+ }
+ clk_register(&sr_clk->clk);
+}
+
+static void sr_set_clk_length(struct omap_sr *sr)
+{
+ struct clk *osc_sys_ck;
+ u32 sys_clk = 0;
+
+ osc_sys_ck = clk_get(NULL, "osc_sys_ck");
+ sys_clk = clk_get_rate(osc_sys_ck);
+ clk_put(osc_sys_ck);
+
+ switch (sys_clk) {
+ case 12000000:
+ sr->clk_length = SRCLKLENGTH_12MHZ_SYSCLK;
+ break;
+ case 13000000:
+ sr->clk_length = SRCLKLENGTH_13MHZ_SYSCLK;
+ break;
+ case 19200000:
+ sr->clk_length = SRCLKLENGTH_19MHZ_SYSCLK;
+ break;
+ case 26000000:
+ sr->clk_length = SRCLKLENGTH_26MHZ_SYSCLK;
+ break;
+ case 38400000:
+ sr->clk_length = SRCLKLENGTH_38MHZ_SYSCLK;
+ break;
+ default :
+ printk(KERN_ERR "Invalid sysclk value: %d\n", sys_clk);
+ break;
+ }
+}
+
+static void sr_set_efuse_nvalues(struct omap_sr *sr)
+{
+ if (sr->srid == SR1) {
+ sr->senn_mod = (omap_ctrl_readl(OMAP343X_CONTROL_FUSE_SR) &
+ OMAP343X_SR1_SENNENABLE_MASK) >>
+ OMAP343X_SR1_SENNENABLE_SHIFT;
+
+ sr->senp_mod = (omap_ctrl_readl(OMAP343X_CONTROL_FUSE_SR) &
+ OMAP343X_SR1_SENPENABLE_MASK) >>
+ OMAP343X_SR1_SENPENABLE_SHIFT;
+
+ sr->opp5_nvalue = omap_ctrl_readl(
+ OMAP343X_CONTROL_FUSE_OPP5_VDD1);
+ sr->opp4_nvalue = omap_ctrl_readl(
+ OMAP343X_CONTROL_FUSE_OPP4_VDD1);
+ sr->opp3_nvalue = omap_ctrl_readl(
+ OMAP343X_CONTROL_FUSE_OPP3_VDD1);
+ sr->opp2_nvalue = omap_ctrl_readl(
+ OMAP343X_CONTROL_FUSE_OPP2_VDD1);
+ sr->opp1_nvalue = omap_ctrl_readl(
+ OMAP343X_CONTROL_FUSE_OPP1_VDD1);
+ } else if (sr->srid == SR2) {
+ sr->senn_mod = (omap_ctrl_readl(OMAP343X_CONTROL_FUSE_SR) &
+ OMAP343X_SR2_SENNENABLE_MASK) >>
+ OMAP343X_SR2_SENNENABLE_SHIFT;
+
+ sr->senp_mod = (omap_ctrl_readl(OMAP343X_CONTROL_FUSE_SR) &
+ OMAP343X_SR2_SENPENABLE_MASK) >>
+ OMAP343X_SR2_SENPENABLE_SHIFT;
+
+ sr->opp3_nvalue = omap_ctrl_readl(
+ OMAP343X_CONTROL_FUSE_OPP3_VDD2);
+ sr->opp2_nvalue = omap_ctrl_readl(
+ OMAP343X_CONTROL_FUSE_OPP2_VDD2);
+ sr->opp1_nvalue = omap_ctrl_readl(
+ OMAP343X_CONTROL_FUSE_OPP1_VDD2);
+ }
+}
+
+/* Hard coded nvalues for testing purposes, may cause device to hang! */
+static void sr_set_testing_nvalues(struct omap_sr *sr)
+{
+ if (sr->srid == SR1) {
+ sr->senp_mod = 0x03; /* SenN-M5 enabled */
+ sr->senn_mod = 0x03;
+
+ /* calculate nvalues for each opp */
+ sr->opp5_nvalue = cal_test_nvalue(0xacd + 0x330, 0x848 + 0x330);
+ sr->opp4_nvalue = cal_test_nvalue(0x964 + 0x2a0, 0x727 + 0x2a0);
+ sr->opp3_nvalue = cal_test_nvalue(0x85b + 0x200, 0x655 + 0x200);
+ sr->opp2_nvalue = cal_test_nvalue(0x506 + 0x1a0, 0x3be + 0x1a0);
+ sr->opp1_nvalue = cal_test_nvalue(0x373 + 0x100, 0x28c + 0x100);
+ } else if (sr->srid == SR2) {
+ sr->senp_mod = 0x03;
+ sr->senn_mod = 0x03;
+
+ sr->opp3_nvalue = cal_test_nvalue(0x76f + 0x200, 0x579 + 0x200);
+ sr->opp2_nvalue = cal_test_nvalue(0x4f5 + 0x1c0, 0x390 + 0x1c0);
+ sr->opp1_nvalue = cal_test_nvalue(0x359, 0x25d);
+ }
+
+}
+
+static void sr_set_nvalues(struct omap_sr *sr)
+{
+ if (SR_TESTING_NVALUES)
+ sr_set_testing_nvalues(sr);
+ else
+ sr_set_efuse_nvalues(sr);
+}
+
+static void sr_configure_vp(int srid)
+{
+ u32 vpconfig;
+
+ if (srid == SR1) {
+ vpconfig = PRM_VP1_CONFIG_ERROROFFSET | PRM_VP1_CONFIG_ERRORGAIN
+ | PRM_VP1_CONFIG_INITVOLTAGE
+ | PRM_VP1_CONFIG_TIMEOUTEN;
+
+ prm_write_mod_reg(vpconfig, OMAP3430_GR_MOD,
+ OMAP3_PRM_VP1_CONFIG_OFFSET);
+ prm_write_mod_reg(PRM_VP1_VSTEPMIN_SMPSWAITTIMEMIN |
+ PRM_VP1_VSTEPMIN_VSTEPMIN,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VP1_VSTEPMIN_OFFSET);
+
+ prm_write_mod_reg(PRM_VP1_VSTEPMAX_SMPSWAITTIMEMAX |
+ PRM_VP1_VSTEPMAX_VSTEPMAX,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VP1_VSTEPMAX_OFFSET);
+
+ prm_write_mod_reg(PRM_VP1_VLIMITTO_VDDMAX |
+ PRM_VP1_VLIMITTO_VDDMIN |
+ PRM_VP1_VLIMITTO_TIMEOUT,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VP1_VLIMITTO_OFFSET);
+
+ /* Trigger initVDD value copy to voltage processor */
+ prm_set_mod_reg_bits(PRM_VP1_CONFIG_INITVDD, OMAP3430_GR_MOD,
+ OMAP3_PRM_VP1_CONFIG_OFFSET);
+ /* Clear initVDD copy trigger bit */
+ prm_clear_mod_reg_bits(PRM_VP1_CONFIG_INITVDD, OMAP3430_GR_MOD,
+ OMAP3_PRM_VP1_CONFIG_OFFSET);
+
+ } else if (srid == SR2) {
+ vpconfig = PRM_VP2_CONFIG_ERROROFFSET | PRM_VP2_CONFIG_ERRORGAIN
+ | PRM_VP2_CONFIG_INITVOLTAGE
+ | PRM_VP2_CONFIG_TIMEOUTEN;
+
+ prm_write_mod_reg(vpconfig, OMAP3430_GR_MOD,
+ OMAP3_PRM_VP2_CONFIG_OFFSET);
+ prm_write_mod_reg(PRM_VP2_VSTEPMIN_SMPSWAITTIMEMIN |
+ PRM_VP2_VSTEPMIN_VSTEPMIN,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VP2_VSTEPMIN_OFFSET);
+
+ prm_write_mod_reg(PRM_VP2_VSTEPMAX_SMPSWAITTIMEMAX |
+ PRM_VP2_VSTEPMAX_VSTEPMAX,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VP2_VSTEPMAX_OFFSET);
+
+ prm_write_mod_reg(PRM_VP2_VLIMITTO_VDDMAX |
+ PRM_VP2_VLIMITTO_VDDMIN |
+ PRM_VP2_VLIMITTO_TIMEOUT,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VP2_VLIMITTO_OFFSET);
+
+ /* Trigger initVDD value copy to voltage processor */
+ prm_set_mod_reg_bits(PRM_VP2_CONFIG_INITVDD, OMAP3430_GR_MOD,
+ OMAP3_PRM_VP2_CONFIG_OFFSET);
+ /* Reset initVDD copy trigger bit */
+ prm_clear_mod_reg_bits(PRM_VP2_CONFIG_INITVDD, OMAP3430_GR_MOD,
+ OMAP3_PRM_VP2_CONFIG_OFFSET);
+
+ }
+}
+
+static void sr_configure(struct omap_sr *sr)
+{
+ u32 sr_config;
+ u32 senp_en , senn_en;
+
+ if (sr->clk_length == 0)
+ sr_set_clk_length(sr);
+
+ senp_en = sr->senp_mod;
+ senn_en = sr->senn_mod;
+ if (sr->srid == SR1) {
+ sr_config = SR1_SRCONFIG_ACCUMDATA |
+ (sr->clk_length << SRCONFIG_SRCLKLENGTH_SHIFT) |
+ SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN |
+ SRCONFIG_MINMAXAVG_EN |
+ (senn_en << SRCONFIG_SENNENABLE_SHIFT) |
+ (senp_en << SRCONFIG_SENPENABLE_SHIFT) |
+ SRCONFIG_DELAYCTRL;
+
+ sr_write_reg(sr, SRCONFIG, sr_config);
+ sr_write_reg(sr, AVGWEIGHT, SR1_AVGWEIGHT_SENPAVGWEIGHT |
+ SR1_AVGWEIGHT_SENNAVGWEIGHT);
+
+ sr_modify_reg(sr, ERRCONFIG, (SR_ERRWEIGHT_MASK |
+ SR_ERRMAXLIMIT_MASK | SR_ERRMINLIMIT_MASK),
+ (SR1_ERRWEIGHT | SR1_ERRMAXLIMIT | SR1_ERRMINLIMIT));
+
+ } else if (sr->srid == SR2) {
+ sr_config = SR2_SRCONFIG_ACCUMDATA |
+ (sr->clk_length << SRCONFIG_SRCLKLENGTH_SHIFT) |
+ SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN |
+ SRCONFIG_MINMAXAVG_EN |
+ (senn_en << SRCONFIG_SENNENABLE_SHIFT) |
+ (senp_en << SRCONFIG_SENPENABLE_SHIFT) |
+ SRCONFIG_DELAYCTRL;
+
+ sr_write_reg(sr, SRCONFIG, sr_config);
+ sr_write_reg(sr, AVGWEIGHT, SR2_AVGWEIGHT_SENPAVGWEIGHT |
+ SR2_AVGWEIGHT_SENNAVGWEIGHT);
+ sr_modify_reg(sr, ERRCONFIG, (SR_ERRWEIGHT_MASK |
+ SR_ERRMAXLIMIT_MASK | SR_ERRMINLIMIT_MASK),
+ (SR2_ERRWEIGHT | SR2_ERRMAXLIMIT | SR2_ERRMINLIMIT));
+
+ }
+ sr->is_sr_reset = 0;
+}
+
+static int sr_enable(struct omap_sr *sr, u32 target_opp_no)
+{
+ u32 nvalue_reciprocal;
+
+ sr->req_opp_no = target_opp_no;
+
+ if (sr->srid == SR1) {
+ switch (target_opp_no) {
+ case 5:
+ nvalue_reciprocal = sr->opp5_nvalue;
+ break;
+ case 4:
+ nvalue_reciprocal = sr->opp4_nvalue;
+ break;
+ case 3:
+ nvalue_reciprocal = sr->opp3_nvalue;
+ break;
+ case 2:
+ nvalue_reciprocal = sr->opp2_nvalue;
+ break;
+ case 1:
+ nvalue_reciprocal = sr->opp1_nvalue;
+ break;
+ default:
+ nvalue_reciprocal = sr->opp3_nvalue;
+ break;
+ }
+ } else {
+ switch (target_opp_no) {
+ case 3:
+ nvalue_reciprocal = sr->opp3_nvalue;
+ break;
+ case 2:
+ nvalue_reciprocal = sr->opp2_nvalue;
+ break;
+ case 1:
+ nvalue_reciprocal = sr->opp1_nvalue;
+ break;
+ default:
+ nvalue_reciprocal = sr->opp3_nvalue;
+ break;
+ }
+ }
+
+ if (nvalue_reciprocal == 0) {
+ printk(KERN_NOTICE "OPP%d doesn't support SmartReflex\n",
+ target_opp_no);
+ return SR_FALSE;
+ }
+
+ sr_write_reg(sr, NVALUERECIPROCAL, nvalue_reciprocal);
+
+ /* Enable the interrupt */
+ sr_modify_reg(sr, ERRCONFIG,
+ (ERRCONFIG_VPBOUNDINTEN | ERRCONFIG_VPBOUNDINTST),
+ (ERRCONFIG_VPBOUNDINTEN | ERRCONFIG_VPBOUNDINTST));
+ if (sr->srid == SR1) {
+ /* Enable VP1 */
+ prm_set_mod_reg_bits(PRM_VP1_CONFIG_VPENABLE, OMAP3430_GR_MOD,
+ OMAP3_PRM_VP1_CONFIG_OFFSET);
+ } else if (sr->srid == SR2) {
+ /* Enable VP2 */
+ prm_set_mod_reg_bits(PRM_VP2_CONFIG_VPENABLE, OMAP3430_GR_MOD,
+ OMAP3_PRM_VP2_CONFIG_OFFSET);
+ }
+
+ /* SRCONFIG - enable SR */
+ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, SRCONFIG_SRENABLE);
+ return SR_TRUE;
+}
+
+static void sr_disable(struct omap_sr *sr)
+{
+ sr->is_sr_reset = 1;
+
+ /* SRCONFIG - disable SR */
+ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, ~SRCONFIG_SRENABLE);
+
+ if (sr->srid == SR1) {
+ /* Disable VP1 */
+ prm_clear_mod_reg_bits(PRM_VP1_CONFIG_VPENABLE, OMAP3430_GR_MOD,
+ OMAP3_PRM_VP1_CONFIG_OFFSET);
+ } else if (sr->srid == SR2) {
+ /* Disable VP2 */
+ prm_clear_mod_reg_bits(PRM_VP2_CONFIG_VPENABLE, OMAP3430_GR_MOD,
+ OMAP3_PRM_VP2_CONFIG_OFFSET);
+ }
+}
+
+
+void sr_start_vddautocomap(int srid, u32 target_opp_no)
+{
+ struct omap_sr *sr = NULL;
+
+ if (srid == SR1)
+ sr = &sr1;
+ else if (srid == SR2)
+ sr = &sr2;
+
+ if (sr->is_sr_reset == 1) {
+ clk_enable(sr->clk);
+ sr_configure(sr);
+ }
+
+ if (sr->is_autocomp_active == 1)
+ printk(KERN_WARNING "SR%d: VDD autocomp is already active\n",
+ srid);
+
+ sr->is_autocomp_active = 1;
+ if (!sr_enable(sr, target_opp_no)) {
+ printk(KERN_WARNING "SR%d: VDD autocomp not activated\n", srid);
+ sr->is_autocomp_active = 0;
+ if (sr->is_sr_reset == 1)
+ clk_disable(sr->clk);
+ }
+}
+EXPORT_SYMBOL(sr_start_vddautocomap);
+
+int sr_stop_vddautocomap(int srid)
+{
+ struct omap_sr *sr = NULL;
+
+ if (srid == SR1)
+ sr = &sr1;
+ else if (srid == SR2)
+ sr = &sr2;
+
+ if (sr->is_autocomp_active == 1) {
+ sr_disable(sr);
+ clk_disable(sr->clk);
+ sr->is_autocomp_active = 0;
+ return SR_TRUE;
+ } else {
+ printk(KERN_WARNING "SR%d: VDD autocomp is not active\n",
+ srid);
+ return SR_FALSE;
+ }
+
+}
+EXPORT_SYMBOL(sr_stop_vddautocomap);
+
+void enable_smartreflex(int srid)
+{
+ u32 target_opp_no = 0;
+ struct omap_sr *sr = NULL;
+
+ if (srid == SR1)
+ sr = &sr1;
+ else if (srid == SR2)
+ sr = &sr2;
+
+ if (sr->is_autocomp_active == 1) {
+ if (sr->is_sr_reset == 1) {
+ /* Enable SR clks */
+ clk_enable(sr->clk);
+
+ if (srid == SR1)
+ target_opp_no = get_opp_no(current_vdd1_opp);
+ else if (srid == SR2)
+ target_opp_no = get_opp_no(current_vdd2_opp);
+
+ sr_configure(sr);
+
+ if (!sr_enable(sr, target_opp_no))
+ clk_disable(sr->clk);
+ }
+ }
+}
+
+void disable_smartreflex(int srid)
+{
+ struct omap_sr *sr = NULL;
+
+ if (srid == SR1)
+ sr = &sr1;
+ else if (srid == SR2)
+ sr = &sr2;
+
+ if (sr->is_autocomp_active == 1) {
+ if (sr->is_sr_reset == 0) {
+
+ sr->is_sr_reset = 1;
+ /* SRCONFIG - disable SR */
+ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE,
+ ~SRCONFIG_SRENABLE);
+
+ /* Disable SR clk */
+ clk_disable(sr->clk);
+ if (sr->srid == SR1) {
+ /* Disable VP1 */
+ prm_clear_mod_reg_bits(PRM_VP1_CONFIG_VPENABLE,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VP1_CONFIG_OFFSET);
+ } else if (sr->srid == SR2) {
+ /* Disable VP2 */
+ prm_clear_mod_reg_bits(PRM_VP2_CONFIG_VPENABLE,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VP2_CONFIG_OFFSET);
+ }
+ }
+ }
+}
+
+/* Voltage Scaling using SR VCBYPASS */
+int sr_voltagescale_vcbypass(u32 target_opp, u8 vsel)
+{
+ int sr_status = 0;
+ u32 vdd, target_opp_no;
+ u32 vc_bypass_value;
+ u32 reg_addr = 0;
+ u32 loop_cnt = 0, retries_cnt = 0;
+
+ vdd = get_vdd(target_opp);
+ target_opp_no = get_opp_no(target_opp);
+
+ if (vdd == PRCM_VDD1) {
+ sr_status = sr_stop_vddautocomap(SR1);
+
+ prm_rmw_mod_reg_bits(OMAP3430_VC_CMD_ON_MASK,
+ (vsel << OMAP3430_VC_CMD_ON_SHIFT),
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VC_CMD_VAL_0_OFFSET);
+ reg_addr = R_VDD1_SR_CONTROL;
+
+ } else if (vdd == PRCM_VDD2) {
+ sr_status = sr_stop_vddautocomap(SR2);
+
+ prm_rmw_mod_reg_bits(OMAP3430_VC_CMD_ON_MASK,
+ (vsel << OMAP3430_VC_CMD_ON_SHIFT),
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_VC_CMD_VAL_1_OFFSET);
+ reg_addr = R_VDD2_SR_CONTROL;
+ }
+
+ vc_bypass_value = (vsel << OMAP3430_DATA_SHIFT) |
+ (reg_addr << OMAP3430_REGADDR_SHIFT) |
+ (R_SRI2C_SLAVE_ADDR << OMAP3430_SLAVEADDR_SHIFT);
+
+ prm_write_mod_reg(vc_bypass_value, OMAP3430_GR_MOD,
+ OMAP3_PRM_VC_BYPASS_VAL_OFFSET);
+
+ vc_bypass_value = prm_set_mod_reg_bits(OMAP3430_VALID, OMAP3430_GR_MOD,
+ OMAP3_PRM_VC_BYPASS_VAL_OFFSET);
+
+ while ((vc_bypass_value & OMAP3430_VALID) != 0x0) {
+ loop_cnt++;
+ if (retries_cnt > 10) {
+ printk(KERN_INFO "Loop count exceeded in check SR I2C"
+ "write\n");
+ return SR_FAIL;
+ }
+ if (loop_cnt > 50) {
+ retries_cnt++;
+ loop_cnt = 0;
+ udelay(10);
+ }
+ vc_bypass_value = prm_read_mod_reg(OMAP3430_GR_MOD,
+ OMAP3_PRM_VC_BYPASS_VAL_OFFSET);
+ }
+
+ udelay(T2_SMPS_UPDATE_DELAY);
+
+ if (sr_status) {
+ if (vdd == PRCM_VDD1)
+ sr_start_vddautocomap(SR1, target_opp_no);
+ else if (vdd == PRCM_VDD2)
+ sr_start_vddautocomap(SR2, target_opp_no);
+ }
+
+ return SR_PASS;
+}
+
+/* Sysfs interface to select SR VDD1 auto compensation */
+static ssize_t omap_sr_vdd1_autocomp_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", sr1.is_autocomp_active);
+}
+
+static ssize_t omap_sr_vdd1_autocomp_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ u32 current_vdd1opp_no;
+ unsigned short value;
+
+ if (sscanf(buf, "%hu", &value) != 1 || (value > 1)) {
+ printk(KERN_ERR "sr_vdd1_autocomp: Invalid value\n");
+ return -EINVAL;
+ }
+
+ current_vdd1opp_no = get_opp_no(current_vdd1_opp);
+
+ if (value == 0)
+ sr_stop_vddautocomap(SR1);
+ else
+ sr_start_vddautocomap(SR1, current_vdd1opp_no);
+
+ return n;
+}
+
+static struct kobj_attribute sr_vdd1_autocomp = {
+ .attr = {
+ .name = __stringify(sr_vdd1_autocomp),
+ .mode = 0644,
+ },
+ .show = omap_sr_vdd1_autocomp_show,
+ .store = omap_sr_vdd1_autocomp_store,
+};
+
+/* Sysfs interface to select SR VDD2 auto compensation */
+static ssize_t omap_sr_vdd2_autocomp_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", sr2.is_autocomp_active);
+}
+
+static ssize_t omap_sr_vdd2_autocomp_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t n)
+{
+ u32 current_vdd2opp_no;
+ unsigned short value;
+
+ if (sscanf(buf, "%hu", &value) != 1 || (value > 1)) {
+ printk(KERN_ERR "sr_vdd2_autocomp: Invalid value\n");
+ return -EINVAL;
+ }
+
+ current_vdd2opp_no = get_opp_no(current_vdd2_opp);
+
+ if (value == 0)
+ sr_stop_vddautocomap(SR2);
+ else
+ sr_start_vddautocomap(SR2, current_vdd2opp_no);
+
+ return n;
+}
+
+static struct kobj_attribute sr_vdd2_autocomp = {
+ .attr = {
+ .name = __stringify(sr_vdd2_autocomp),
+ .mode = 0644,
+ },
+ .show = omap_sr_vdd2_autocomp_show,
+ .store = omap_sr_vdd2_autocomp_store,
+};
+
+
+
+static int __init omap3_sr_init(void)
+{
+ int ret = 0;
+ u8 RdReg;
+
+ if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0)) {
+ current_vdd1_opp = PRCM_VDD1_OPP3;
+ current_vdd2_opp = PRCM_VDD2_OPP3;
+ } else {
+ current_vdd1_opp = PRCM_VDD1_OPP1;
+ current_vdd2_opp = PRCM_VDD1_OPP1;
+ }
+ if (cpu_is_omap34xx()) {
+ sr_clk_init(&sr1_custom_clk);
+ sr_clk_init(&sr2_custom_clk);
+ sr1.clk = clk_get(NULL, "sr1_custom_clk");
+ sr2.clk = clk_get(NULL, "sr2_custom_clk");
+ }
+ sr_set_clk_length(&sr1);
+ sr_set_clk_length(&sr2);
+
+ /* Call the VPConfig, VCConfig, set N Values. */
+ sr_set_nvalues(&sr1);
+ sr_configure_vp(SR1);
+
+ sr_set_nvalues(&sr2);
+ sr_configure_vp(SR2);
+
+ /* Enable SR on T2 */
+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &RdReg,
+ R_DCDC_GLOBAL_CFG);
+
+ RdReg |= DCDC_GLOBAL_CFG_ENABLE_SRFLX;
+ ret |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, RdReg,
+ R_DCDC_GLOBAL_CFG);
+
+ printk(KERN_INFO "SmartReflex driver initialized\n");
+
+ ret = sysfs_create_file(power_kobj, &sr_vdd1_autocomp.attr);
+ if (ret)
+ printk(KERN_ERR "sysfs_create_file failed: %d\n", ret);
+
+ ret = sysfs_create_file(power_kobj, &sr_vdd2_autocomp.attr);
+ if (ret)
+ printk(KERN_ERR "sysfs_create_file failed: %d\n", ret);
+
+ return 0;
+}
+
+late_initcall(omap3_sr_init);
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * linux/arch/arm/mach-omap3/sram.S
+ *
+ * Omap3 specific functions that need to be run in internal SRAM
+ *
+ * (C) Copyright 2007
+ * Texas Instruments Inc.
+ * Rajendra Nayak <rnayak@ti.com>
+ *
+ * (C) Copyright 2004
+ * Texas Instruments, <www.ti.com>
+ * Richard Woodruff <r-woodruff2@ti.com>
+ *
+ * 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/linkage.h>
+#include <asm/assembler.h>
- #include <asm/arch/io.h>
++#include <mach/hardware.h>
+
++#include <mach/io.h>
+
+#include "sdrc.h"
+#include "cm.h"
+
+ .text
+
+/*
+ * Change frequency of core dpll
+ * r0 = sdrc_rfr_ctrl r1 = sdrc_actim_ctrla r2 = sdrc_actim_ctrlb r3 = M2
+ */
+ENTRY(omap3_sram_configure_core_dpll)
+ stmfd sp!, {r1-r12, lr} @ store regs to stack
+ cmp r3, #0x2
+ blne configure_sdrc
+ cmp r3, #0x2
+ blne lock_dll
+ cmp r3, #0x1
+ blne unlock_dll
+ bl sdram_in_selfrefresh @ put the SDRAM in self refresh
+ bl configure_core_dpll
+ bl enable_sdrc
+ cmp r3, #0x1
+ blne wait_dll_unlock
+ cmp r3, #0x2
+ blne wait_dll_lock
+ cmp r3, #0x1
+ blne configure_sdrc
+ mov r0, #0 @ return value
+ ldmfd sp!, {r1-r12, pc} @ restore regs and return
+unlock_dll:
+ ldr r4, omap3_sdrc_dlla_ctrl
+ ldr r5, [r4]
+ orr r5, r5, #0x4
+ str r5, [r4]
+ bx lr
+lock_dll:
+ ldr r4, omap3_sdrc_dlla_ctrl
+ ldr r5, [r4]
+ bic r5, r5, #0x4
+ str r5, [r4]
+ bx lr
+sdram_in_selfrefresh:
+ mov r5, #0x0 @ Move 0 to R5
+ mcr p15, 0, r5, c7, c10, 5 @ memory barrier
+ ldr r4, omap3_sdrc_power @ read the SDRC_POWER register
+ ldr r5, [r4] @ read the contents of SDRC_POWER
+ orr r5, r5, #0x40 @ enable self refresh on idle req
+ str r5, [r4] @ write back to SDRC_POWER register
+ ldr r4, omap3_cm_iclken1_core @ read the CM_ICLKEN1_CORE reg
+ ldr r5, [r4]
+ bic r5, r5, #0x2 @ disable iclk bit for SRDC
+ str r5, [r4]
+wait_sdrc_idle:
+ ldr r4, omap3_cm_idlest1_core
+ ldr r5, [r4]
+ and r5, r5, #0x2 @ check for SDRC idle
+ cmp r5, #2
+ bne wait_sdrc_idle
+ bx lr
+configure_core_dpll:
+ ldr r4, omap3_cm_clksel1_pll
+ ldr r5, [r4]
+ ldr r6, core_m2_mask_val @ modify m2 for core dpll
+ and r5, r5, r6
+ orr r5, r5, r3, lsl #0x1B @ r3 contains the M2 val
+ str r5, [r4]
+ mov r5, #0x800 @ wait for the clock to stabilise
+ cmp r3, #2
+ bne wait_clk_stable
+ bx lr
+wait_clk_stable:
+ subs r5, r5, #1
+ bne wait_clk_stable
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ bx lr
+enable_sdrc:
+ ldr r4, omap3_cm_iclken1_core
+ ldr r5, [r4]
+ orr r5, r5, #0x2 @ enable iclk bit for SDRC
+ str r5, [r4]
+wait_sdrc_idle1:
+ ldr r4, omap3_cm_idlest1_core
+ ldr r5, [r4]
+ and r5, r5, #0x2
+ cmp r5, #0
+ bne wait_sdrc_idle1
+ ldr r4, omap3_sdrc_power
+ ldr r5, [r4]
+ bic r5, r5, #0x40
+ str r5, [r4]
+ bx lr
+wait_dll_lock:
+ ldr r4, omap3_sdrc_dlla_status
+ ldr r5, [r4]
+ and r5, r5, #0x4
+ cmp r5, #0x4
+ bne wait_dll_lock
+ bx lr
+wait_dll_unlock:
+ ldr r4, omap3_sdrc_dlla_status
+ ldr r5, [r4]
+ and r5, r5, #0x4
+ cmp r5, #0x0
+ bne wait_dll_unlock
+ bx lr
+configure_sdrc:
+ ldr r4, omap3_sdrc_rfr_ctrl
+ str r0, [r4]
+ ldr r4, omap3_sdrc_actim_ctrla
+ str r1, [r4]
+ ldr r4, omap3_sdrc_actim_ctrlb
+ str r2, [r4]
+ bx lr
+
+omap3_sdrc_power:
+ .word OMAP34XX_SDRC_REGADDR(SDRC_POWER)
+omap3_cm_clksel1_pll:
+ .word OMAP34XX_CM_REGADDR(PLL_MOD, CM_CLKSEL1)
+omap3_cm_idlest1_core:
+ .word OMAP34XX_CM_REGADDR(CORE_MOD, CM_IDLEST)
+omap3_cm_iclken1_core:
+ .word OMAP34XX_CM_REGADDR(CORE_MOD, CM_ICLKEN1)
+omap3_sdrc_rfr_ctrl:
+ .word OMAP34XX_SDRC_REGADDR(SDRC_RFR_CTRL_0)
+omap3_sdrc_actim_ctrla:
+ .word OMAP34XX_SDRC_REGADDR(SDRC_ACTIM_CTRL_A)
+omap3_sdrc_actim_ctrlb:
+ .word OMAP34XX_SDRC_REGADDR(SDRC_ACTIM_CTRL_B)
+omap3_sdrc_dlla_status:
+ .word OMAP34XX_SDRC_REGADDR(SDRC_DLLA_STATUS)
+omap3_sdrc_dlla_ctrl:
+ .word OMAP34XX_SDRC_REGADDR(SDRC_DLLA_CTRL)
+core_m2_mask_val:
+ .word 0x07FFFFFF
+
+ENTRY(omap3_sram_configure_core_dpll_sz)
+ .word . - omap3_sram_configure_core_dpll
--- /dev/null
- #include <asm/arch/mux.h>
+/*
+ * linux/arch/arm/mach-omap2/usb-ehci.c
+ *
+ * This file will contain the board specific details for the
+ * Synopsys EHCI host controller on OMAP3430
+ *
+ * Copyright (C) 2007 Texas Instruments
+ * Author: Vikram Pandita <vikram.pandita@ti.com>
+ *
+ * Generalization by:
+ * Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * 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/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <asm/io.h>
- #include <asm/arch/hardware.h>
- #include <asm/arch/pm.h>
- #include <asm/arch/usb.h>
++#include <mach/mux.h>
+#include <linux/usb/musb.h>
+
++#include <mach/hardware.h>
++#include <mach/pm.h>
++#include <mach/usb.h>
+
+#if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_EHCI_HCD_MODULE)
+static struct resource ehci_resources[] = {
+ [0] = {
+ .start = OMAP34XX_HSUSB_HOST_BASE + 0x800,
+ .end = OMAP34XX_HSUSB_HOST_BASE + 0x800 + SZ_1K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = { /* general IRQ */
+ .start = INT_34XX_EHCI_IRQ,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 ehci_dmamask = ~(u32)0;
+static struct platform_device ehci_device = {
+ .name = "ehci-omap",
+ .id = 0,
+ .dev = {
+ .dma_mask = &ehci_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ .platform_data = NULL,
+ },
+ .num_resources = ARRAY_SIZE(ehci_resources),
+ .resource = ehci_resources,
+};
+
+
+/* MUX settings for EHCI pins */
+/*
+ * setup_ehci_io_mux - initialize IO pad mux for USBHOST
+ */
+static void setup_ehci_io_mux(void)
+{
+#ifdef CONFIG_OMAP_EHCI_PHY_MODE
+ /* PHY mode of operation for board: 750-2083-001
+ * ISP1504 connected to Port1 and Port2
+ * Do Func Mux setting for 12-pin ULPI PHY mode
+ */
+
+ /* Port1 */
+ omap_cfg_reg(Y9_3430_USB1HS_PHY_STP);
+ omap_cfg_reg(Y8_3430_USB1HS_PHY_CLK);
+ omap_cfg_reg(AA14_3430_USB1HS_PHY_DIR);
+ omap_cfg_reg(AA11_3430_USB1HS_PHY_NXT);
+ omap_cfg_reg(W13_3430_USB1HS_PHY_DATA0);
+ omap_cfg_reg(W12_3430_USB1HS_PHY_DATA1);
+ omap_cfg_reg(W11_3430_USB1HS_PHY_DATA2);
+ omap_cfg_reg(Y11_3430_USB1HS_PHY_DATA3);
+ omap_cfg_reg(W9_3430_USB1HS_PHY_DATA4);
+ omap_cfg_reg(Y12_3430_USB1HS_PHY_DATA5);
+ omap_cfg_reg(W8_3430_USB1HS_PHY_DATA6);
+ omap_cfg_reg(Y13_3430_USB1HS_PHY_DATA7);
+
+ /* Port2 */
+ omap_cfg_reg(AA10_3430_USB2HS_PHY_STP);
+ omap_cfg_reg(AA8_3430_USB2HS_PHY_CLK);
+ omap_cfg_reg(AA9_3430_USB2HS_PHY_DIR);
+ omap_cfg_reg(AB11_3430_USB2HS_PHY_NXT);
+ omap_cfg_reg(AB10_3430_USB2HS_PHY_DATA0);
+ omap_cfg_reg(AB9_3430_USB2HS_PHY_DATA1);
+ omap_cfg_reg(W3_3430_USB2HS_PHY_DATA2);
+ omap_cfg_reg(T4_3430_USB2HS_PHY_DATA3);
+ omap_cfg_reg(T3_3430_USB2HS_PHY_DATA4);
+ omap_cfg_reg(R3_3430_USB2HS_PHY_DATA5);
+ omap_cfg_reg(R4_3430_USB2HS_PHY_DATA6);
+ omap_cfg_reg(T2_3430_USB2HS_PHY_DATA7);
+
+#else
+ /* Set Func mux for :
+ * TLL mode of operation
+ * 12-pin ULPI SDR TLL mode for Port1/2/3
+ */
+
+ /* Port1 */
+ omap_cfg_reg(Y9_3430_USB1HS_TLL_STP);
+ omap_cfg_reg(Y8_3430_USB1HS_TLL_CLK);
+ omap_cfg_reg(AA14_3430_USB1HS_TLL_DIR);
+ omap_cfg_reg(AA11_3430_USB1HS_TLL_NXT);
+ omap_cfg_reg(W13_3430_USB1HS_TLL_DATA0);
+ omap_cfg_reg(W12_3430_USB1HS_TLL_DATA1);
+ omap_cfg_reg(W11_3430_USB1HS_TLL_DATA2);
+ omap_cfg_reg(Y11_3430_USB1HS_TLL_DATA3);
+ omap_cfg_reg(W9_3430_USB1HS_TLL_DATA4);
+ omap_cfg_reg(Y12_3430_USB1HS_TLL_DATA5);
+ omap_cfg_reg(W8_3430_USB1HS_TLL_DATA6);
+ omap_cfg_reg(Y13_3430_USB1HS_TLL_DATA7);
+
+ /* Port2 */
+ omap_cfg_reg(AA10_3430_USB2HS_TLL_STP);
+ omap_cfg_reg(AA8_3430_USB2HS_TLL_CLK);
+ omap_cfg_reg(AA9_3430_USB2HS_TLL_DIR);
+ omap_cfg_reg(AB11_3430_USB2HS_TLL_NXT);
+ omap_cfg_reg(AB10_3430_USB2HS_TLL_DATA0);
+ omap_cfg_reg(AB9_3430_USB2HS_TLL_DATA1);
+ omap_cfg_reg(W3_3430_USB2HS_TLL_DATA2);
+ omap_cfg_reg(T4_3430_USB2HS_TLL_DATA3);
+ omap_cfg_reg(T3_3430_USB2HS_TLL_DATA4);
+ omap_cfg_reg(R3_3430_USB2HS_TLL_DATA5);
+ omap_cfg_reg(R4_3430_USB2HS_TLL_DATA6);
+ omap_cfg_reg(T2_3430_USB2HS_TLL_DATA7);
+
+ /* Port3 */
+ omap_cfg_reg(AB3_3430_USB3HS_TLL_STP);
+ omap_cfg_reg(AA6_3430_USB3HS_TLL_CLK);
+ omap_cfg_reg(AA3_3430_USB3HS_TLL_DIR);
+ omap_cfg_reg(Y3_3430_USB3HS_TLL_NXT);
+ omap_cfg_reg(AA5_3430_USB3HS_TLL_DATA0);
+ omap_cfg_reg(Y4_3430_USB3HS_TLL_DATA1);
+ omap_cfg_reg(Y5_3430_USB3HS_TLL_DATA2);
+ omap_cfg_reg(W5_3430_USB3HS_TLL_DATA3);
+ omap_cfg_reg(AB12_3430_USB3HS_TLL_DATA4);
+ omap_cfg_reg(AB13_3430_USB3HS_TLL_DATA5);
+ omap_cfg_reg(AA13_3430_USB3HS_TLL_DATA6);
+ omap_cfg_reg(AA12_3430_USB3HS_TLL_DATA7);
+#endif /* CONFIG_OMAP_EHCI_PHY_MODE */
+
+ return;
+}
+
+#endif /* EHCI specific data */
+
+void __init usb_ehci_init(void)
+{
+#if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_EHCI_HCD_MODULE)
+ /* Setup Pin IO MUX for EHCI */
+ if (cpu_is_omap34xx())
+ setup_ehci_io_mux();
+
+ if (platform_device_register(&ehci_device) < 0) {
+ printk(KERN_ERR "Unable to register HS-USB (EHCI) device\n");
+ return;
+ }
+#endif
+}
+
--- /dev/null
- #include <asm/arch/mux.h>
+/*
+ * linux/arch/arm/mach-omap2/usb-musb.c
+ *
+ * This file will contain the board specific details for the
+ * MENTOR USB OTG controller on OMAP3430
+ *
+ * Copyright (C) 2007-2008 Texas Instruments
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Vikram Pandita
+ *
+ * Generalization by:
+ * Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * 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/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <asm/io.h>
- #include <asm/arch/hardware.h>
- #include <asm/arch/pm.h>
- #include <asm/arch/usb.h>
++#include <mach/mux.h>
+#include <linux/usb/musb.h>
+
++#include <mach/hardware.h>
++#include <mach/pm.h>
++#include <mach/usb.h>
+
+#ifdef CONFIG_USB_MUSB_SOC
+static struct resource musb_resources[] = {
+ [0] = {
+ .start = cpu_is_omap34xx()
+ ? OMAP34XX_HSUSB_OTG_BASE
+ : OMAP243X_HS_BASE,
+ .end = cpu_is_omap34xx()
+ ? OMAP34XX_HSUSB_OTG_BASE + SZ_8K - 1
+ : OMAP243X_HS_BASE + SZ_8K -1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = { /* general IRQ */
+ .start = INT_243X_HS_USB_MC,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = { /* DMA IRQ */
+ .start = INT_243X_HS_USB_DMA,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int clk_on;
+
+static int musb_set_clock(struct clk *clk, int state)
+{
+ if (state) {
+ if (clk_on > 0)
+ return -ENODEV;
+
+ omap2_block_sleep();
+ clk_enable(clk);
+ clk_on = 1;
+ } else {
+ if (clk_on == 0)
+ return -ENODEV;
+
+ clk_disable(clk);
+ clk_on = 0;
+ omap2_allow_sleep();
+ }
+
+ return 0;
+}
+
+static struct musb_hdrc_platform_data musb_plat = {
+#ifdef CONFIG_USB_MUSB_OTG
+ .mode = MUSB_OTG,
+#elif defined(CONFIG_USB_MUSB_HDRC_HCD)
+ .mode = MUSB_HOST,
+#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
+ .mode = MUSB_PERIPHERAL,
+#endif
+ .multipoint = 1,
+ .clock = cpu_is_omap34xx()
+ ? "hsotgusb_ick"
+ : "usbhs_ick",
+ .set_clock = musb_set_clock,
+};
+
+static u64 musb_dmamask = ~(u32)0;
+
+static struct platform_device musb_device = {
+ .name = "musb_hdrc",
+ .id = 0,
+ .dev = {
+ .dma_mask = &musb_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ .platform_data = &musb_plat,
+ },
+ .num_resources = ARRAY_SIZE(musb_resources),
+ .resource = musb_resources,
+};
+#endif
+
+
+void __init usb_musb_init(void)
+{
+#ifdef CONFIG_USB_MUSB_SOC
+ if (platform_device_register(&musb_device) < 0) {
+ printk(KERN_ERR "Unable to register HS-USB (MUSB) device\n");
+ return;
+ }
+#endif
+}
+
--- /dev/null
- #include <asm/arch/board.h>
+/*
+ * linux/arch/arm/plat-omap/bootreason.c
+ *
+ * OMAP Bootreason passing
+ *
+ * Copyright (c) 2004 Nokia
+ *
+ * Written by David Weinehall <david.weinehall@nokia.com>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
++#include <mach/board.h>
+
+static char boot_reason[16];
+
+static int omap_bootreason_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += sprintf(page + len, "%s\n", boot_reason);
+
+ *start = page + off;
+
+ if (len > off)
+ len -= off;
+ else
+ len = 0;
+
+ return len < count ? len : count;
+}
+
+static int __init bootreason_init(void)
+{
+ const struct omap_boot_reason_config *cfg;
+ int reason_valid = 0;
+
+ cfg = omap_get_config(OMAP_TAG_BOOT_REASON, struct omap_boot_reason_config);
+ if (cfg != NULL) {
+ strncpy(boot_reason, cfg->reason_str, sizeof(cfg->reason_str));
+ boot_reason[sizeof(cfg->reason_str)] = 0;
+ reason_valid = 1;
+ } else {
+ /* Read the boot reason from the OMAP registers */
+ }
+
+ if (!reason_valid)
+ return -ENOENT;
+
+ printk(KERN_INFO "Bootup reason: %s\n", boot_reason);
+
+ if (!create_proc_read_entry("bootreason", S_IRUGO, NULL,
+ omap_bootreason_read_proc, NULL))
+ return -ENOMEM;
+
+ return 0;
+}
+
+late_initcall(bootreason_init);
--- /dev/null
- #include <asm/arch/board.h>
- #include <asm/arch/board-nokia.h>
+/*
+ * linux/arch/arm/plat-omap/component-version.c
+ *
+ * Copyright (C) 2005 Nokia Corporation
+ * Written by Juha Yrjölä <juha.yrjola@nokia.com>
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/proc_fs.h>
++#include <mach/board.h>
++#include <mach/board-nokia.h>
+
+static int component_version_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len, i;
+ const struct omap_version_config *ver;
+ char *p;
+
+ i = 0;
+ p = page;
+ while ((ver = omap_get_nr_config(OMAP_TAG_VERSION_STR,
+ struct omap_version_config, i)) != NULL) {
+ p += sprintf(p, "%-12s%s\n", ver->component, ver->version);
+ i++;
+ }
+
+ len = (p - page) - off;
+ if (len < 0)
+ len = 0;
+
+ *eof = (len <= count) ? 1 : 0;
+ *start = page + off;
+
+ return len;
+}
+
+static int __init component_version_init(void)
+{
+ if (omap_get_config(OMAP_TAG_VERSION_STR, struct omap_version_config) == NULL)
+ return -ENODEV;
+ if (!create_proc_read_entry("component_version", S_IRUGO, NULL,
+ component_version_read_proc, NULL))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void __exit component_version_exit(void)
+{
+ remove_proc_entry("component_version", NULL);
+}
+
+late_initcall(component_version_init);
+module_exit(component_version_exit);
+
+MODULE_AUTHOR("Juha Yrjölä <juha.yrjola@nokia.com>");
+MODULE_DESCRIPTION("Component version driver");
+MODULE_LICENSE("GPL");
#include <linux/err.h>
#include <linux/clk.h>
- #include <asm/hardware.h>
+ #include <mach/hardware.h>
#include <asm/io.h>
#include <asm/system.h>
- #include <asm/arch/clock.h>
++#include <mach/clock.h>
#define VERY_HI_RATE 900000000
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/i2c/menelaus.h>
- #include <asm/hardware.h>
+ #include <mach/hardware.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <asm/mach/map.h>
- #include <asm/arch/tc.h>
- #include <asm/arch/control.h>
- #include <asm/arch/board.h>
- #include <asm/arch/mmc.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/dsp_common.h>
- #include <asm/arch/mcbsp.h>
+ #include <mach/tc.h>
++#include <mach/control.h>
+ #include <mach/board.h>
++#include <mach/mmc.h>
+ #include <mach/mux.h>
+ #include <mach/gpio.h>
-#include <mach/menelaus.h>
++#include <mach/dsp_common.h>
+ #include <mach/mcbsp.h>
#if defined(CONFIG_OMAP_DSP) || defined(CONFIG_OMAP_DSP_MODULE)
--- /dev/null
- #include <asm/arch/hardware.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/irqs.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/board.h>
- #include <asm/arch/gpio-switch.h>
+/*
+ * linux/arch/arm/plat-omap/gpio-switch.c
+ *
+ * Copyright (C) 2004-2006 Nokia Corporation
+ * Written by Juha Yrjölä <juha.yrjola@nokia.com>
+ * and Paul Mundt <paul.mundt@nokia.com>
+ *
+ * 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/sched.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/timer.h>
+#include <linux/err.h>
++#include <mach/hardware.h>
++#include <mach/gpio.h>
++#include <mach/irqs.h>
++#include <mach/mux.h>
++#include <mach/board.h>
++#include <mach/gpio-switch.h>
+
+struct gpio_switch {
+ char name[14];
+ u16 gpio;
+ unsigned flags:4;
+ unsigned type:4;
+ unsigned state:1;
+ unsigned both_edges:1;
+
+ u16 debounce_rising;
+ u16 debounce_falling;
+
+ void (* notify)(void *data, int state);
+ void *notify_data;
+
+ struct work_struct work;
+ struct timer_list timer;
+ struct platform_device pdev;
+
+ struct list_head node;
+};
+
+static LIST_HEAD(gpio_switches);
+static struct platform_device *gpio_sw_platform_dev;
+static struct platform_driver gpio_sw_driver;
+
+static const struct omap_gpio_switch *board_gpio_sw_table;
+static int board_gpio_sw_count;
+
+static const char *cover_str[2] = { "open", "closed" };
+static const char *connection_str[2] = { "disconnected", "connected" };
+static const char *activity_str[2] = { "inactive", "active" };
+
+/*
+ * GPIO switch state default debounce delay in ms
+ */
+#define OMAP_GPIO_SW_DEFAULT_DEBOUNCE 10
+
+static const char **get_sw_str(struct gpio_switch *sw)
+{
+ switch (sw->type) {
+ case OMAP_GPIO_SWITCH_TYPE_COVER:
+ return cover_str;
+ case OMAP_GPIO_SWITCH_TYPE_CONNECTION:
+ return connection_str;
+ case OMAP_GPIO_SWITCH_TYPE_ACTIVITY:
+ return activity_str;
+ default:
+ BUG();
+ return NULL;
+ }
+}
+
+static const char *get_sw_type(struct gpio_switch *sw)
+{
+ switch (sw->type) {
+ case OMAP_GPIO_SWITCH_TYPE_COVER:
+ return "cover";
+ case OMAP_GPIO_SWITCH_TYPE_CONNECTION:
+ return "connection";
+ case OMAP_GPIO_SWITCH_TYPE_ACTIVITY:
+ return "activity";
+ default:
+ BUG();
+ return NULL;
+ }
+}
+
+static void print_sw_state(struct gpio_switch *sw, int state)
+{
+ const char **str;
+
+ str = get_sw_str(sw);
+ if (str != NULL)
+ printk(KERN_INFO "%s (GPIO %d) is now %s\n", sw->name, sw->gpio, str[state]);
+}
+
+static int gpio_sw_get_state(struct gpio_switch *sw)
+{
+ int state;
+
+ state = omap_get_gpio_datain(sw->gpio);
+ if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
+ state = !state;
+
+ return state;
+}
+
+static ssize_t gpio_sw_state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct gpio_switch *sw = dev_get_drvdata(dev);
+ const char **str;
+ char state[16];
+ int enable;
+
+ if (!(sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT))
+ return -EPERM;
+
+ if (sscanf(buf, "%15s", state) != 1)
+ return -EINVAL;
+
+ str = get_sw_str(sw);
+ if (strcmp(state, str[0]) == 0)
+ sw->state = enable = 0;
+ else if (strcmp(state, str[1]) == 0)
+ sw->state = enable = 1;
+ else
+ return -EINVAL;
+
+ if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
+ enable = !enable;
+ omap_set_gpio_dataout(sw->gpio, enable);
+
+ return count;
+}
+
+static ssize_t gpio_sw_state_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct gpio_switch *sw = dev_get_drvdata(dev);
+ const char **str;
+
+ str = get_sw_str(sw);
+ return sprintf(buf, "%s\n", str[sw->state]);
+}
+
+static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, gpio_sw_state_show,
+ gpio_sw_state_store);
+
+static ssize_t gpio_sw_type_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct gpio_switch *sw = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", get_sw_type(sw));
+}
+
+static DEVICE_ATTR(type, S_IRUGO, gpio_sw_type_show, NULL);
+
+static ssize_t gpio_sw_direction_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct gpio_switch *sw = dev_get_drvdata(dev);
+ int is_output;
+
+ is_output = sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT;
+ return sprintf(buf, "%s\n", is_output ? "output" : "input");
+}
+
+static DEVICE_ATTR(direction, S_IRUGO, gpio_sw_direction_show, NULL);
+
+
+static irqreturn_t gpio_sw_irq_handler(int irq, void *arg)
+{
+ struct gpio_switch *sw = arg;
+ unsigned long timeout;
+ int state;
+
+ if (!sw->both_edges) {
+ if (omap_get_gpio_datain(sw->gpio))
+ set_irq_type(OMAP_GPIO_IRQ(sw->gpio), IRQ_TYPE_EDGE_FALLING);
+ else
+ set_irq_type(OMAP_GPIO_IRQ(sw->gpio), IRQ_TYPE_EDGE_RISING);
+ }
+
+ state = gpio_sw_get_state(sw);
+ if (sw->state == state)
+ return IRQ_HANDLED;
+
+ if (state)
+ timeout = sw->debounce_rising;
+ else
+ timeout = sw->debounce_falling;
+ if (!timeout)
+ schedule_work(&sw->work);
+ else
+ mod_timer(&sw->timer, jiffies + msecs_to_jiffies(timeout));
+
+ return IRQ_HANDLED;
+}
+
+static void gpio_sw_timer(unsigned long arg)
+{
+ struct gpio_switch *sw = (struct gpio_switch *) arg;
+
+ schedule_work(&sw->work);
+}
+
+static void gpio_sw_handler(struct work_struct *work)
+{
+ struct gpio_switch *sw = container_of(work, struct gpio_switch, work);
+ int state;
+
+ state = gpio_sw_get_state(sw);
+ if (sw->state == state)
+ return;
+
+ sw->state = state;
+ if (sw->notify != NULL)
+ sw->notify(sw->notify_data, state);
+ sysfs_notify(&sw->pdev.dev.kobj, NULL, "state");
+ print_sw_state(sw, state);
+}
+
+static int __init can_do_both_edges(struct gpio_switch *sw)
+{
+ if (!cpu_class_is_omap1())
+ return 1;
+ if (OMAP_GPIO_IS_MPUIO(sw->gpio))
+ return 0;
+ else
+ return 1;
+}
+
+static void gpio_sw_release(struct device *dev)
+{
+}
+
+static int __init new_switch(struct gpio_switch *sw)
+{
+ int r, direction, trigger;
+
+ switch (sw->type) {
+ case OMAP_GPIO_SWITCH_TYPE_COVER:
+ case OMAP_GPIO_SWITCH_TYPE_CONNECTION:
+ case OMAP_GPIO_SWITCH_TYPE_ACTIVITY:
+ break;
+ default:
+ printk(KERN_ERR "invalid GPIO switch type: %d\n", sw->type);
+ return -EINVAL;
+ }
+
+ sw->pdev.name = sw->name;
+ sw->pdev.id = -1;
+
+ sw->pdev.dev.parent = &gpio_sw_platform_dev->dev;
+ sw->pdev.dev.driver = &gpio_sw_driver.driver;
+ sw->pdev.dev.release = gpio_sw_release;
+
+ r = platform_device_register(&sw->pdev);
+ if (r) {
+ printk(KERN_ERR "gpio-switch: platform device registration "
+ "failed for %s", sw->name);
+ return r;
+ }
+ dev_set_drvdata(&sw->pdev.dev, sw);
+
+ r = omap_request_gpio(sw->gpio);
+ if (r < 0) {
+ platform_device_unregister(&sw->pdev);
+ return r;
+ }
+
+ /* input: 1, output: 0 */
+ direction = !(sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT);
+ omap_set_gpio_direction(sw->gpio, direction);
+
+ sw->state = gpio_sw_get_state(sw);
+
+ r = 0;
+ r |= device_create_file(&sw->pdev.dev, &dev_attr_state);
+ r |= device_create_file(&sw->pdev.dev, &dev_attr_type);
+ r |= device_create_file(&sw->pdev.dev, &dev_attr_direction);
+ if (r)
+ printk(KERN_ERR "gpio-switch: attribute file creation "
+ "failed for %s\n", sw->name);
+
+ if (!direction)
+ return 0;
+
+ if (can_do_both_edges(sw)) {
+ trigger = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
+ sw->both_edges = 1;
+ } else {
+ if (omap_get_gpio_datain(sw->gpio))
+ trigger = IRQF_TRIGGER_FALLING;
+ else
+ trigger = IRQF_TRIGGER_RISING;
+ }
+ r = request_irq(OMAP_GPIO_IRQ(sw->gpio), gpio_sw_irq_handler,
+ IRQF_SHARED | trigger, sw->name, sw);
+ if (r < 0) {
+ printk(KERN_ERR "gpio-switch: request_irq() failed "
+ "for GPIO %d\n", sw->gpio);
+ platform_device_unregister(&sw->pdev);
+ omap_free_gpio(sw->gpio);
+ return r;
+ }
+
+ INIT_WORK(&sw->work, gpio_sw_handler);
+ init_timer(&sw->timer);
+
+ sw->timer.function = gpio_sw_timer;
+ sw->timer.data = (unsigned long)sw;
+
+ list_add(&sw->node, &gpio_switches);
+
+ return 0;
+}
+
+static int __init add_atag_switches(void)
+{
+ const struct omap_gpio_switch_config *cfg;
+ struct gpio_switch *sw;
+ int i, r;
+
+ for (i = 0; ; i++) {
+ cfg = omap_get_nr_config(OMAP_TAG_GPIO_SWITCH,
+ struct omap_gpio_switch_config, i);
+ if (cfg == NULL)
+ break;
+ sw = kzalloc(sizeof(*sw), GFP_KERNEL);
+ if (sw == NULL) {
+ printk(KERN_ERR "gpio-switch: kmalloc failed\n");
+ return -ENOMEM;
+ }
+ strncpy(sw->name, cfg->name, sizeof(cfg->name));
+ sw->gpio = cfg->gpio;
+ sw->flags = cfg->flags;
+ sw->type = cfg->type;
+ sw->debounce_rising = OMAP_GPIO_SW_DEFAULT_DEBOUNCE;
+ sw->debounce_falling = OMAP_GPIO_SW_DEFAULT_DEBOUNCE;
+ if ((r = new_switch(sw)) < 0) {
+ kfree(sw);
+ return r;
+ }
+ }
+ return 0;
+}
+
+static struct gpio_switch * __init find_switch(int gpio, const char *name)
+{
+ struct gpio_switch *sw;
+
+ list_for_each_entry(sw, &gpio_switches, node) {
+ if ((gpio < 0 || sw->gpio != gpio) &&
+ (name == NULL || strcmp(sw->name, name) != 0))
+ continue;
+
+ if (gpio < 0 || name == NULL)
+ goto no_check;
+
+ if (strcmp(sw->name, name) != 0)
+ printk("gpio-switch: name mismatch for %d (%s, %s)\n",
+ gpio, name, sw->name);
+ else if (sw->gpio != gpio)
+ printk("gpio-switch: GPIO mismatch for %s (%d, %d)\n",
+ name, gpio, sw->gpio);
+no_check:
+ return sw;
+ }
+ return NULL;
+}
+
+static int __init add_board_switches(void)
+{
+ int i;
+
+ for (i = 0; i < board_gpio_sw_count; i++) {
+ const struct omap_gpio_switch *cfg;
+ struct gpio_switch *sw;
+ int r;
+
+ cfg = board_gpio_sw_table + i;
+ if (strlen(cfg->name) > sizeof(sw->name) - 1)
+ return -EINVAL;
+ /* Check whether we only update an existing switch
+ * or add a new switch. */
+ sw = find_switch(cfg->gpio, cfg->name);
+ if (sw != NULL) {
+ sw->debounce_rising = cfg->debounce_rising;
+ sw->debounce_falling = cfg->debounce_falling;
+ sw->notify = cfg->notify;
+ sw->notify_data = cfg->notify_data;
+ continue;
+ } else {
+ if (cfg->gpio < 0 || cfg->name == NULL) {
+ printk("gpio-switch: required switch not "
+ "found (%d, %s)\n", cfg->gpio,
+ cfg->name);
+ continue;
+ }
+ }
+ sw = kzalloc(sizeof(*sw), GFP_KERNEL);
+ if (sw == NULL) {
+ printk(KERN_ERR "gpio-switch: kmalloc failed\n");
+ return -ENOMEM;
+ }
+ strlcpy(sw->name, cfg->name, sizeof(sw->name));
+ sw->gpio = cfg->gpio;
+ sw->flags = cfg->flags;
+ sw->type = cfg->type;
+ sw->debounce_rising = cfg->debounce_rising;
+ sw->debounce_falling = cfg->debounce_falling;
+ sw->notify = cfg->notify;
+ sw->notify_data = cfg->notify_data;
+ if ((r = new_switch(sw)) < 0) {
+ kfree(sw);
+ return r;
+ }
+ }
+ return 0;
+}
+
+static void gpio_sw_cleanup(void)
+{
+ struct gpio_switch *sw = NULL, *old = NULL;
+
+ list_for_each_entry(sw, &gpio_switches, node) {
+ if (old != NULL)
+ kfree(old);
+ flush_scheduled_work();
+ del_timer_sync(&sw->timer);
+
+ free_irq(OMAP_GPIO_IRQ(sw->gpio), sw);
+
+ device_remove_file(&sw->pdev.dev, &dev_attr_state);
+ device_remove_file(&sw->pdev.dev, &dev_attr_type);
+ device_remove_file(&sw->pdev.dev, &dev_attr_direction);
+
+ platform_device_unregister(&sw->pdev);
+ omap_free_gpio(sw->gpio);
+ old = sw;
+ }
+ kfree(old);
+}
+
+static void __init report_initial_state(void)
+{
+ struct gpio_switch *sw;
+
+ list_for_each_entry(sw, &gpio_switches, node) {
+ int state;
+
+ state = omap_get_gpio_datain(sw->gpio);
+ if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
+ state = !state;
+ if (sw->notify != NULL)
+ sw->notify(sw->notify_data, state);
+ print_sw_state(sw, state);
+ }
+}
+
+static int gpio_sw_remove(struct platform_device *dev)
+{
+ return 0;
+}
+
+static struct platform_driver gpio_sw_driver = {
+ .remove = gpio_sw_remove,
+ .driver = {
+ .name = "gpio-switch",
+ },
+};
+
+void __init omap_register_gpio_switches(const struct omap_gpio_switch *tbl,
+ int count)
+{
+ BUG_ON(board_gpio_sw_table != NULL);
+
+ board_gpio_sw_table = tbl;
+ board_gpio_sw_count = count;
+}
+
+static int __init gpio_sw_init(void)
+{
+ int r;
+
+ printk(KERN_INFO "OMAP GPIO switch handler initializing\n");
+
+ r = platform_driver_register(&gpio_sw_driver);
+ if (r)
+ return r;
+
+ gpio_sw_platform_dev = platform_device_register_simple("gpio-switch",
+ -1, NULL, 0);
+ if (IS_ERR(gpio_sw_platform_dev)) {
+ r = PTR_ERR(gpio_sw_platform_dev);
+ goto err1;
+ }
+
+ r = add_atag_switches();
+ if (r < 0)
+ goto err2;
+
+ r = add_board_switches();
+ if (r < 0)
+ goto err2;
+
+ report_initial_state();
+
+ return 0;
+err2:
+ gpio_sw_cleanup();
+ platform_device_unregister(gpio_sw_platform_dev);
+err1:
+ platform_driver_unregister(&gpio_sw_driver);
+ return r;
+}
+
+static void __exit gpio_sw_exit(void)
+{
+ gpio_sw_cleanup();
+ platform_device_unregister(gpio_sw_platform_dev);
+ platform_driver_unregister(&gpio_sw_driver);
+}
+
+#ifndef MODULE
+late_initcall(gpio_sw_init);
+#else
+module_init(gpio_sw_init);
+#endif
+module_exit(gpio_sw_exit);
+
+MODULE_AUTHOR("Juha Yrjölä <juha.yrjola@nokia.com>, Paul Mundt <paul.mundt@nokia.com");
+MODULE_DESCRIPTION("GPIO switch driver");
+MODULE_LICENSE("GPL");
#define TLV320AIC23ID1 (0x1a) // cs low
#define TLV320AIC23ID2 (0x1b) // cs high
--void aic23_power_up(void);
--void aic23_power_down(void);
++#ifdef CONFIG_SENSORS_TLV320AIC23
++extern void aic23_power_up(void);
++extern void aic23_power_down(void);
++#else
++static inline void aic23_power_up(void)
++{
++}
++
++static inline void aic23_power_down(void)
++{
++}
++#endif
#endif /* __ASM_ARCH_AIC23_H */
--- /dev/null
- * linux/include/asm-arm/arch-omap/bci.h
+/*
++ * arch/arm/plat-omap/include/mach/bci.h
+ *
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ *
+ * 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.
+ */
+#ifndef ASMARM_ARCH_BCI_H
+#define ASMARM_ARCH_BCI_H
+struct twl4030_bci_platform_data {
+ int *battery_tmp_tbl;
+ unsigned int tblsize;
+};
+#endif
+
--- /dev/null
- * linux/include/asm-arm/arch-omap/board-3430sdp.h
+/*
++ * arch/arm/plat-omap/include/mach/board-3430sdp.h
+ *
+ * Hardware definitions for TI OMAP3430 SDP board.
+ *
+ * Initial creation by Syed Mohammed Khasim
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_ARCH_OMAP_3430SDP_H
+#define __ASM_ARCH_OMAP_3430SDP_H
+
+extern void sdp3430_usb_init(void);
+extern void sdp3430_flash_init(void);
+extern void twl4030_bci_battery_init(void);
+
+#define DEBUG_BASE 0x08000000 /* debug board */
+
+/* Placeholder for 3430SDP specific defines */
+
+#define OMAP34XX_ETHR_START DEBUG_BASE
+#define OMAP34XX_ETHR_GPIO_IRQ_SDPV1 29
+#define OMAP34XX_ETHR_GPIO_IRQ_SDPV2 6
+
+/*
+ * GPIO used for TSC2046, TI's Touchscreen controller
+ */
+#define OMAP34XX_TS_GPIO_IRQ_SDPV1 3
+#define OMAP34XX_TS_GPIO_IRQ_SDPV2 2
+
+/* NAND */
+/* IMPORTANT NOTE ON MAPPING
+ * 3430SDP - 34XX
+ * ----------
+ * NOR always on 0x04000000 for SDPV1
+ * NOR always on 0x10000000 for SDPV2
+ * MPDB always on 0x08000000
+ * NAND always on 0x0C000000
+ * OneNand Mapped to 0x20000000
+ * Boot Mode(NAND/NOR). The other on CS1
+ */
+#define FLASH_BASE_SDPV1 0x04000000 /* NOR flash (64 Meg aligned) */
+#define FLASH_BASE_SDPV2 0x10000000 /* NOR flash (256 Meg aligned) */
+#define DEBUG_BASE 0x08000000 /* debug board */
+#define NAND_BASE 0x0C000000 /* NAND flash */
+#define ONENAND_MAP 0x20000000 /* OneNand flash */
+
+/* various memory sizes */
+#define FLASH_SIZE_SDPV1 SZ_64M
+#define FLASH_SIZE_SDPV2 SZ_128M
+
+#endif /* __ASM_ARCH_OMAP_3430SDP_H */
+
/*
- * linux/include/asm-arm/arch-omap/board-h4.h
+ * arch/arm/plat-omap/include/mach/board-h4.h
*
- * Hardware definitions for TI OMAP1610 H4 board.
+ * Hardware definitions for TI OMAP2420 H4 board.
*
* Initial creation by Dirk Behme <dirk.behme@de.bosch.com>
*
--- /dev/null
- * linux/include/asm-arm/arch-omap/board-ldp.h
+/*
++ * arch/arm/plat-omap/include/mach/board-ldp.h
+ *
+ * Hardware definitions for TI OMAP3 LDP.
+ *
+ * Copyright (C) 2008 Texas Instruments Inc.
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_ARCH_OMAP_LDP_H
+#define __ASM_ARCH_OMAP_LDP_H
+
+#define TWL4030_IRQNUM INT_34XX_SYS_NIRQ
+
+#endif /* __ASM_ARCH_OMAP_LDP_H */
--- /dev/null
- * linux/include/asm-arm/arch-omap/board-omap2evm.h
+/*
++ * arch/arm/plat-omap/include/mach/board-omap2evm.h
+ *
+ * Hardware definitions for Mistral's OMAP2EVM board.
+ *
+ * Based on board-2430sdp.h
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_ARCH_OMAP2_EVM_H
+#define __ASM_ARCH_OMAP2_EVM_H
+
+/* Placeholder for OMAP2EVM specific defines */
+#define OMAP2EVM_ETHR_START 0x2c000000
+#define OMAP2EVM_ETHR_SIZE 1024
+#define OMAP2EVM_ETHR_GPIO_IRQ 149
+
+#endif /* __ASM_ARCH_OMAP2_EVM_H */
--- /dev/null
- * linux/include/asm-arm/arch-omap/board-omap3beagle.h
+/*
++ * arch/arm/plat-omap/include/mach/board-omap3beagle.h
+ *
+ * Hardware definitions for TI OMAP3 BEAGLE.
+ *
+ * Initial creation by Syed Mohammed Khasim <khasim@ti.com>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_ARCH_OMAP3_BEAGLE_H
+#define __ASM_ARCH_OMAP3_BEAGLE_H
+
+#endif /* __ASM_ARCH_OMAP3_BEAGLE_H */
+
--- /dev/null
- * linux/include/asm-arm/arch-omap/board-omap3evm.h
+/*
++ * arch/arm/plat-omap/include/mach/board-omap3evm.h
+ *
+ * Hardware definitions for TI OMAP3 EVM.
+ *
+ * Initial creation by Syed Mohammed Khasim <khasim@ti.com>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_ARCH_OMAP3_EVM_H
+#define __ASM_ARCH_OMAP3_EVM_H
+
+extern void omap3evm_flash_init(void);
+
+#define OMAP3_EVM_TS_GPIO 175
+
+#define ONENAND_MAP 0x20000000
+
+#define OMAP3EVM_ETHR_START 0x2c000000
+#define OMAP3EVM_ETHR_SIZE 1024
+#define OMAP3EVM_ETHR_GPIO_IRQ 176
+#define OMAP3EVM_SMC911X_CS 5
+
+#endif /* __ASM_ARCH_OMAP3_EVM_H */
+
--- /dev/null
- * linux/include/asm-arm/arch-omap/clockdomain.h
+/*
- #include <asm/arch/powerdomain.h>
- #include <asm/arch/clock.h>
- #include <asm/arch/cpu.h>
++ * arch/arm/plat-omap/include/mach/clockdomain.h
+ *
+ * OMAP2/3 clockdomain framework functions
+ *
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Written by Paul Walmsley
+ *
+ * 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.
+ */
+
+#ifndef __ASM_ARM_ARCH_OMAP_CLOCKDOMAIN_H
+#define __ASM_ARM_ARCH_OMAP_CLOCKDOMAIN_H
+
++#include <mach/powerdomain.h>
++#include <mach/clock.h>
++#include <mach/cpu.h>
+
+/* Clockdomain capability flags */
+#define CLKDM_CAN_FORCE_SLEEP (1 << 0)
+#define CLKDM_CAN_FORCE_WAKEUP (1 << 1)
+#define CLKDM_CAN_ENABLE_AUTO (1 << 2)
+#define CLKDM_CAN_DISABLE_AUTO (1 << 3)
+
+#define CLKDM_CAN_HWSUP (CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_DISABLE_AUTO)
+#define CLKDM_CAN_SWSUP (CLKDM_CAN_FORCE_SLEEP | CLKDM_CAN_FORCE_WAKEUP)
+#define CLKDM_CAN_HWSUP_SWSUP (CLKDM_CAN_SWSUP | CLKDM_CAN_HWSUP)
+
+/* OMAP24XX CM_CLKSTCTRL_*.AUTOSTATE_* register bit values */
+#define OMAP24XX_CLKSTCTRL_DISABLE_AUTO 0x0
+#define OMAP24XX_CLKSTCTRL_ENABLE_AUTO 0x1
+
+/* OMAP3XXX CM_CLKSTCTRL_*.CLKTRCTRL_* register bit values */
+#define OMAP34XX_CLKSTCTRL_DISABLE_AUTO 0x0
+#define OMAP34XX_CLKSTCTRL_FORCE_SLEEP 0x1
+#define OMAP34XX_CLKSTCTRL_FORCE_WAKEUP 0x2
+#define OMAP34XX_CLKSTCTRL_ENABLE_AUTO 0x3
+
+/*
+ * struct clkdm_pwrdm_autodep - a powerdomain that should have wkdeps
+ * and sleepdeps added when a powerdomain should stay active in hwsup mode;
+ * and conversely, removed when the powerdomain should be allowed to go
+ * inactive in hwsup mode.
+ */
+struct clkdm_pwrdm_autodep {
+
+ union {
+ /* Name of the powerdomain to add a wkdep/sleepdep on */
+ const char *name;
+
+ /* Powerdomain pointer (looked up at clkdm_init() time) */
+ struct powerdomain *ptr;
+ } pwrdm;
+
+ /* OMAP chip types that this clockdomain dep is valid on */
+ const struct omap_chip_id omap_chip;
+
+};
+
+struct clockdomain {
+
+ /* Clockdomain name */
+ const char *name;
+
+ union {
+ /* Powerdomain enclosing this clockdomain */
+ const char *name;
+
+ /* Powerdomain pointer assigned at clkdm_register() */
+ struct powerdomain *ptr;
+ } pwrdm;
+
+ /* CLKTRCTRL/AUTOSTATE field mask in CM_CLKSTCTRL reg */
+ const u16 clktrctrl_mask;
+
+ /* Clockdomain capability flags */
+ const u8 flags;
+
+ /* OMAP chip types that this clockdomain is valid on */
+ const struct omap_chip_id omap_chip;
+
+ /* Usecount tracking */
+ atomic_t usecount;
+
+ struct list_head node;
+
+};
+
+void clkdm_init(struct clockdomain **clkdms, struct clkdm_pwrdm_autodep *autodeps);
+int clkdm_register(struct clockdomain *clkdm);
+int clkdm_unregister(struct clockdomain *clkdm);
+struct clockdomain *clkdm_lookup(const char *name);
+
+int clkdm_for_each(int (*fn)(struct clockdomain *clkdm));
+struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm);
+
+void omap2_clkdm_allow_idle(struct clockdomain *clkdm);
+void omap2_clkdm_deny_idle(struct clockdomain *clkdm);
+
+int omap2_clkdm_wakeup(struct clockdomain *clkdm);
+int omap2_clkdm_sleep(struct clockdomain *clkdm);
+
+int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk);
+int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk);
+
+#endif
* the Free Software Foundation.
*/
- #include <asm/arch/io.h>
+#ifndef __ASM_ARCH_CONTROL_H
+#define __ASM_ARCH_CONTROL_H
+
+ #include <mach/io.h>
+#ifndef __ASSEMBLY__
#define OMAP242X_CTRL_REGADDR(reg) \
- (void __iomem *)IO_ADDRESS(OMAP242X_CTRL_BASE + (reg))
+ (__force void __iomem *)IO_ADDRESS(OMAP242X_CTRL_BASE + (reg))
#define OMAP243X_CTRL_REGADDR(reg) \
- (void __iomem *)IO_ADDRESS(OMAP243X_CTRL_BASE + (reg))
+ (__force void __iomem *)IO_ADDRESS(OMAP243X_CTRL_BASE + (reg))
#define OMAP343X_CTRL_REGADDR(reg) \
- (void __iomem *)IO_ADDRESS(OMAP343X_CTRL_BASE + (reg))
+ (__force void __iomem *)IO_ADDRESS(OMAP343X_CTRL_BASE + (reg))
+#else
+#define OMAP242X_CTRL_REGADDR(reg) IO_ADDRESS(OMAP242X_CTRL_BASE + (reg))
+#define OMAP243X_CTRL_REGADDR(reg) IO_ADDRESS(OMAP243X_CTRL_BASE + (reg))
+#define OMAP343X_CTRL_REGADDR(reg) IO_ADDRESS(OMAP343X_CTRL_BASE + (reg))
+#endif /* __ASSEMBLY__ */
/*
* As elsewhere, the "OMAP2_" prefix indicates that the macro is valid for
1510:
.endm
-#elif defined(CONFIG_ARCH_OMAP24XX)
+#endif
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
- #include <asm/arch/omap24xx.h>
+#if defined(CONFIG_ARCH_OMAP24XX)
- #include <asm/arch/omap34xx.h>
+ #include <mach/omap24xx.h>
+#endif
+#if defined(CONFIG_ARCH_OMAP34XX)
++#include <mach/omap34xx.h>
+#endif
+
+#define INTCPS_SIR_IRQ_OFFSET 0x0040 /* Active interrupt number */
.macro disable_fiq
.endm
#ifndef __ASSEMBLY__
extern void omap_init_irq(void);
+extern int omap_irq_pending(void);
#endif
- #include <asm/hardware.h>
+ #include <mach/hardware.h>
#endif
#ifndef __OMAP_ALSA_H
#define __OMAP_ALSA_H
- #include <asm/arch/dma.h>
+ #include <mach/dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
- #include <asm/arch/mcbsp.h>
+ #include <mach/mcbsp.h>
#include <linux/platform_device.h>
+/*
+ * Debug functions
+ */
+#undef DEBUG
+/* #define DEBUG */
+
+#define ERR(ARGS...) \
+ do { \
+ printk(KERN_ERR "{%s}-ERROR: ", __func__); \
+ printk(ARGS); \
+ } while (0)
+
+#ifdef DEBUG
+#define DPRINTK(ARGS...) \
+ do { \
+ printk(KERN_INFO "<%s>: ", __func__); \
+ printk(ARGS); \
+ } while (0)
+#define ADEBUG() printk("XXX Alsa debug f:%s, l:%d\n", __func__, __LINE__)
+#define FN_IN printk(KERN_INFO "[%s]: start\n", __func__)
+#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n", __func__, n)
+#else
+#define DPRINTK(ARGS...) /* nop */
+#define ADEBUG() /* nop */
+#define FN_IN /* nop */
+#define FN_OUT(n) /* nop */
+#endif
#define DMA_BUF_SIZE (1024 * 8)
/*
- * linux/include/asm/arch-omap/pm.h
- * arch/arm/plat-omap/include/mach/pm.h
++ * linux/include/mach-omap/pm.h
*
* Header file for OMAP Power Management Routines
*
--- /dev/null
- #include <asm/arch/cpu.h>
+/*
+ * OMAP2/3 powerdomain control
+ *
+ * Copyright (C) 2007-8 Texas Instruments, Inc.
+ * Copyright (C) 2007-8 Nokia Corporation
+ *
+ * Written by Paul Walmsley
+ *
+ * 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.
+ */
+
+#ifndef ASM_ARM_ARCH_OMAP_POWERDOMAIN
+#define ASM_ARM_ARCH_OMAP_POWERDOMAIN
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+#include <asm/atomic.h>
+
++#include <mach/cpu.h>
+
+
+/* Powerdomain basic power states */
+#define PWRDM_POWER_OFF 0x0
+#define PWRDM_POWER_RET 0x1
+#define PWRDM_POWER_INACTIVE 0x2
+#define PWRDM_POWER_ON 0x3
+
+/* Powerdomain allowable state bitfields */
+#define PWRSTS_OFF_ON ((1 << PWRDM_POWER_OFF) | \
+ (1 << PWRDM_POWER_ON))
+
+#define PWRSTS_OFF_RET ((1 << PWRDM_POWER_OFF) | \
+ (1 << PWRDM_POWER_RET))
+
+#define PWRSTS_OFF_RET_ON (PWRSTS_OFF_RET | (1 << PWRDM_POWER_ON))
+
+
+/* Powerdomain flags */
+#define PWRDM_HAS_HDWR_SAR (1 << 0) /* hardware save-and-restore support */
+
+
+/*
+ * Number of memory banks that are power-controllable. On OMAP3430, the
+ * maximum is 4.
+ */
+#define PWRDM_MAX_MEM_BANKS 4
+
+/*
+ * Maximum number of clockdomains that can be associated with a powerdomain.
+ * CORE powerdomain is probably the worst case.
+ */
+#define PWRDM_MAX_CLKDMS 3
+
+/* XXX A completely arbitrary number. What is reasonable here? */
+#define PWRDM_TRANSITION_BAILOUT 100000
+
+struct clockdomain;
+struct powerdomain;
+
+/* Encodes dependencies between powerdomains - statically defined */
+struct pwrdm_dep {
+
+ /* Powerdomain name */
+ const char *pwrdm_name;
+
+ /* Powerdomain pointer - resolved by the powerdomain code */
+ struct powerdomain *pwrdm;
+
+ /* Flags to mark OMAP chip restrictions, etc. */
+ const struct omap_chip_id omap_chip;
+
+};
+
+struct powerdomain {
+
+ /* Powerdomain name */
+ const char *name;
+
+ /* the address offset from CM_BASE/PRM_BASE */
+ const s16 prcm_offs;
+
+ /* Used to represent the OMAP chip types containing this pwrdm */
+ const struct omap_chip_id omap_chip;
+
+ /* Bit shift of this powerdomain's PM_WKDEP/CM_SLEEPDEP bit */
+ const u8 dep_bit;
+
+ /* Powerdomains that can be told to wake this powerdomain up */
+ struct pwrdm_dep *wkdep_srcs;
+
+ /* Powerdomains that can be told to keep this pwrdm from inactivity */
+ struct pwrdm_dep *sleepdep_srcs;
+
+ /* Possible powerdomain power states */
+ const u8 pwrsts;
+
+ /* Possible logic power states when pwrdm in RETENTION */
+ const u8 pwrsts_logic_ret;
+
+ /* Powerdomain flags */
+ const u8 flags;
+
+ /* Number of software-controllable memory banks in this powerdomain */
+ const u8 banks;
+
+ /* Possible memory bank pwrstates when pwrdm in RETENTION */
+ const u8 pwrsts_mem_ret[PWRDM_MAX_MEM_BANKS];
+
+ /* Possible memory bank pwrstates when pwrdm is ON */
+ const u8 pwrsts_mem_on[PWRDM_MAX_MEM_BANKS];
+
+ /* Clockdomains in this powerdomain */
+ struct clockdomain *pwrdm_clkdms[PWRDM_MAX_CLKDMS];
+
+ struct list_head node;
+
+};
+
+
+void pwrdm_init(struct powerdomain **pwrdm_list);
+
+int pwrdm_register(struct powerdomain *pwrdm);
+int pwrdm_unregister(struct powerdomain *pwrdm);
+struct powerdomain *pwrdm_lookup(const char *name);
+
+int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm));
+
+int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm);
+int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm);
+int pwrdm_for_each_clkdm(struct powerdomain *pwrdm,
+ int (*fn)(struct powerdomain *pwrdm,
+ struct clockdomain *clkdm));
+
+int pwrdm_add_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2);
+int pwrdm_del_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2);
+int pwrdm_read_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2);
+int pwrdm_add_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2);
+int pwrdm_del_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2);
+int pwrdm_read_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2);
+
+int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm);
+
+int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst);
+int pwrdm_read_next_pwrst(struct powerdomain *pwrdm);
+int pwrdm_read_pwrst(struct powerdomain *pwrdm);
+int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm);
+int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm);
+
+int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst);
+int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst);
+int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst);
+
+int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm);
+int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm);
+int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank);
+int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank);
+
+int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm);
+int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm);
+bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm);
+
+int pwrdm_wait_transition(struct powerdomain *pwrdm);
+
+#endif
#include <linux/clk.h>
#include <asm/mach-types.h>
- #include <asm/hardware.h>
+ #include <mach/hardware.h>
- #include <asm/arch/prcm.h>
++#include <mach/prcm.h>
+
#ifndef CONFIG_MACH_VOICEBLUE
#define voiceblue_reset() do {} while (0)
#endif
--- /dev/null
- * linux/include/asm-arm/arch-omap/usb-ehci.h
+/*
++ * arch/arm/plat-omap/include/mach/usb-ehci.h
+ *
+ * Hardware definitions for Synopsys EHCI host controller.
+ *
+ * Initial creation by Felipe Balbi.
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_ARCH_OMAP_USB_EHCI_H
+#define __ASM_ARCH_OMAP_USB_EHCI_H
+
+extern void usb_ehci_init(void);
+
+#endif /* __ASM_ARCH_OMAP_USB_EHCI_H */
+
--- /dev/null
- * linux/include/asm-arm/arch-omap/usb-musb.h
+/*
++ * arch/arm/plat-omap/include/mach/usb-musb.h
+ *
+ * Hardware definitions for Mentor Graphics MUSBMHDRC.
+ *
+ * Initial creation by Felipe Balbi.
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_ARCH_OMAP_USB_MUSB_H
+#define __ASM_ARCH_OMAP_USB_MUSB_H
+
+extern void usb_musb_init(void);
+
+#endif /* __ASM_ARCH_OMAP_USB_MUSB_H */
+
#include <linux/delay.h>
#include <linux/io.h>
- #include <asm/arch/dma.h>
- #include <asm/arch/mcbsp.h>
+ #include <mach/dma.h>
+ #include <mach/mcbsp.h>
-static struct omap_mcbsp mcbsp[OMAP_MAX_MCBSP_COUNT];
+struct omap_mcbsp **mcbsp_ptr;
+int omap_mcbsp_count;
-#define omap_mcbsp_check_valid_id(id) (mcbsp[id].pdata && \
- mcbsp[id].pdata->ops && \
- mcbsp[id].pdata->ops->check && \
- (mcbsp[id].pdata->ops->check(id) == 0))
+void omap_mcbsp_write(u32 io_base, u16 reg, u32 val)
+{
+ if (cpu_class_is_omap1() || cpu_is_omap2420())
+ __raw_writew((u16)val, io_base + reg);
+ else
+ __raw_writel(val, io_base + reg);
+}
+
+int omap_mcbsp_read(u32 io_base, u16 reg)
+{
+ if (cpu_class_is_omap1() || cpu_is_omap2420())
+ return __raw_readw(io_base + reg);
+ else
+ return __raw_readl(io_base + reg);
+}
+
+#define OMAP_MCBSP_READ(base, reg) \
+ omap_mcbsp_read(base, OMAP_MCBSP_REG_##reg)
+#define OMAP_MCBSP_WRITE(base, reg, val) \
+ omap_mcbsp_write(base, OMAP_MCBSP_REG_##reg, val)
+
+#define omap_mcbsp_check_valid_id(id) (id < omap_mcbsp_count)
+#define id_to_mcbsp_ptr(id) mcbsp_ptr[id];
static void omap_mcbsp_dump_reg(u8 id)
{
--- /dev/null
- #include <asm/arch/mmu.h>
+/*
+ * linux/arch/arm/plat-omap/mmu.c
+ *
+ * OMAP MMU management framework
+ *
+ * Copyright (C) 2002-2006 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ * and Paul Mundt <lethal@linux-sh.org>
+ *
+ * TWL support: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
+ *
+ * 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/mempool.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
- #include <asm/arch/dsp_common.h>
++#include <mach/mmu.h>
+#include <asm/sizes.h>
++#include <mach/dsp_common.h>
+
+#if defined(CONFIG_ARCH_OMAP1)
+#include "../mach-omap1/mmu.h"
+#elif defined(CONFIG_ARCH_OMAP2)
+#include "../mach-omap2/mmu.h"
+#endif
+
+/*
+ * On OMAP2 MMU_LOCK_xxx_MASK only applies to the IVA and DSP, the camera
+ * MMU has base and victim implemented in different bits in the LOCK
+ * register (shifts are still the same), all of the other registers are
+ * the same on all of the MMUs..
+ */
+#define MMU_LOCK_BASE_SHIFT 10
+#define MMU_LOCK_VICTIM_SHIFT 4
+
+#define CAMERA_MMU_LOCK_BASE_MASK (0x7 << MMU_LOCK_BASE_SHIFT)
+#define CAMERA_MMU_LOCK_VICTIM_MASK (0x7 << MMU_LOCK_VICTIM_SHIFT)
+
+#define is_aligned(adr, align) (!((adr)&((align)-1)))
+#define ORDER_1MB (20 - PAGE_SHIFT)
+#define ORDER_64KB (16 - PAGE_SHIFT)
+#define ORDER_4KB (12 - PAGE_SHIFT)
+
+#define MMU_CNTL_EMUTLBUPDATE (1<<3)
+#define MMU_CNTL_TWLENABLE (1<<2)
+#define MMU_CNTL_MMUENABLE (1<<1)
+
+static mempool_t *mempool_1M;
+static mempool_t *mempool_64K;
+
+#define omap_mmu_for_each_tlb_entry(mmu, entry) \
+ for (entry = mmu->exmap_tbl; prefetch(entry + 1), \
+ entry < (mmu->exmap_tbl + mmu->nr_tlb_entries); \
+ entry++)
+
+#define to_dev(obj) container_of(obj, struct device, kobj)
+
+static void *mempool_alloc_from_pool(mempool_t *pool,
+ unsigned int __nocast gfp_mask)
+{
+ spin_lock_irq(&pool->lock);
+ if (likely(pool->curr_nr)) {
+ void *element = pool->elements[--pool->curr_nr];
+ spin_unlock_irq(&pool->lock);
+ return element;
+ }
+
+ spin_unlock_irq(&pool->lock);
+ return mempool_alloc(pool, gfp_mask);
+}
+
+/*
+ * kmem_reserve(), kmem_release():
+ * reserve or release kernel memory for exmap().
+ *
+ * exmap() might request consecutive 1MB or 64kB,
+ * but it will be difficult after memory pages are fragmented.
+ * So, user can reserve such memory blocks in the early phase
+ * through kmem_reserve().
+ */
+static void *omap_mmu_pool_alloc(unsigned int __nocast gfp, void *order)
+{
+ return (void *)__get_dma_pages(gfp, (unsigned int)order);
+}
+
+static void omap_mmu_pool_free(void *buf, void *order)
+{
+ free_pages((unsigned long)buf, (unsigned int)order);
+}
+
+int omap_mmu_kmem_reserve(struct omap_mmu *mmu, unsigned long size)
+{
+ unsigned long len = size;
+
+ /* alignment check */
+ if (!is_aligned(size, SZ_64K)) {
+ dev_err(mmu->dev,
+ "MMU %s: size(0x%lx) is not multiple of 64KB.\n",
+ mmu->name, size);
+ return -EINVAL;
+ }
+
+ if (size > (1 << mmu->addrspace)) {
+ dev_err(mmu->dev,
+ "MMU %s: size(0x%lx) is larger than external device "
+ " memory space size (0x%x.\n", mmu->name, size,
+ (1 << mmu->addrspace));
+ return -EINVAL;
+ }
+
+ if (size >= SZ_1M) {
+ int nr = size >> 20;
+
+ if (likely(!mempool_1M))
+ mempool_1M = mempool_create(nr, omap_mmu_pool_alloc,
+ omap_mmu_pool_free,
+ (void *)ORDER_1MB);
+ else
+ mempool_resize(mempool_1M, mempool_1M->min_nr + nr,
+ GFP_KERNEL);
+
+ size &= ~(0xf << 20);
+ }
+
+ if (size >= SZ_64K) {
+ int nr = size >> 16;
+
+ if (likely(!mempool_64K))
+ mempool_64K = mempool_create(nr, omap_mmu_pool_alloc,
+ omap_mmu_pool_free,
+ (void *)ORDER_64KB);
+ else
+ mempool_resize(mempool_64K, mempool_64K->min_nr + nr,
+ GFP_KERNEL);
+
+ size &= ~(0xf << 16);
+ }
+
+ if (size)
+ len -= size;
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_kmem_reserve);
+
+void omap_mmu_kmem_release(void)
+{
+ if (mempool_64K) {
+ mempool_destroy(mempool_64K);
+ mempool_64K = NULL;
+ }
+
+ if (mempool_1M) {
+ mempool_destroy(mempool_1M);
+ mempool_1M = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(omap_mmu_kmem_release);
+
+static void omap_mmu_free_pages(unsigned long buf, unsigned int order)
+{
+ struct page *page, *ps, *pe;
+
+ ps = virt_to_page(buf);
+ pe = virt_to_page(buf + (1 << (PAGE_SHIFT + order)));
+
+ for (page = ps; page < pe; page++)
+ ClearPageReserved(page);
+
+ if ((order == ORDER_64KB) && likely(mempool_64K))
+ mempool_free((void *)buf, mempool_64K);
+ else if ((order == ORDER_1MB) && likely(mempool_1M))
+ mempool_free((void *)buf, mempool_1M);
+ else
+ free_pages(buf, order);
+}
+
+/*
+ * ARM MMU operations
+ */
+int exmap_set_armmmu(struct omap_mmu *mmu, unsigned long virt,
+ unsigned long phys, unsigned long size)
+{
+ long off;
+ unsigned long sz_left;
+ pmd_t *pmdp;
+ pte_t *ptep;
+ int prot_pmd, prot_pte;
+
+ dev_dbg(mmu->dev,
+ "MMU %s: mapping in ARM MMU, v=0x%08lx, p=0x%08lx, sz=0x%lx\n",
+ mmu->name, virt, phys, size);
+
+ prot_pmd = PMD_TYPE_TABLE | PMD_DOMAIN(DOMAIN_IO);
+ prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE;
+
+ pmdp = pmd_offset(pgd_offset_k(virt), virt);
+ if (pmd_none(*pmdp)) {
+ ptep = pte_alloc_one_kernel(&init_mm, 0);
+ if (ptep == NULL)
+ return -ENOMEM;
+ /* note: two PMDs will be set */
+ pmd_populate_kernel(&init_mm, pmdp, ptep);
+ }
+
+ off = phys - virt;
+ for (sz_left = size;
+ sz_left >= PAGE_SIZE;
+ sz_left -= PAGE_SIZE, virt += PAGE_SIZE) {
+ ptep = pte_offset_kernel(pmdp, virt);
+ set_pte_ext(ptep, __pte((virt + off) | prot_pte), 0);
+ }
+ if (sz_left)
+ BUG();
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(exmap_set_armmmu);
+
+void exmap_clear_armmmu(struct omap_mmu *mmu, unsigned long virt,
+ unsigned long size)
+{
+ unsigned long sz_left;
+ pmd_t *pmdp;
+ pte_t *ptep;
+
+ dev_dbg(mmu->dev,
+ "MMU %s: unmapping in ARM MMU, v=0x%08lx, sz=0x%lx\n",
+ mmu->name, virt, size);
+
+ for (sz_left = size;
+ sz_left >= PAGE_SIZE;
+ sz_left -= PAGE_SIZE, virt += PAGE_SIZE) {
+ pmdp = pmd_offset(pgd_offset_k(virt), virt);
+ ptep = pte_offset_kernel(pmdp, virt);
+ pte_clear(&init_mm, virt, ptep);
+ }
+ if (sz_left)
+ BUG();
+}
+EXPORT_SYMBOL_GPL(exmap_clear_armmmu);
+
+int exmap_valid(struct omap_mmu *mmu, void *vadr, size_t len)
+{
+ /* exmap_sem should be held before calling this function */
+ struct exmap_tbl *ent;
+
+start:
+ omap_mmu_for_each_tlb_entry(mmu, ent) {
+ void *mapadr;
+ unsigned long mapsize;
+
+ if (!ent->valid)
+ continue;
+ mapadr = (void *)ent->vadr;
+ mapsize = 1 << (ent->order + PAGE_SHIFT);
+ if ((vadr >= mapadr) && (vadr < mapadr + mapsize)) {
+ if (vadr + len <= mapadr + mapsize) {
+ /* this map covers whole address. */
+ return 1;
+ } else {
+ /*
+ * this map covers partially.
+ * check rest portion.
+ */
+ len -= mapadr + mapsize - vadr;
+ vadr = mapadr + mapsize;
+ goto start;
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(exmap_valid);
+
+/*
+ * omap_mmu_exmap_use(), unuse():
+ * when the mapped area is exported to user space with mmap,
+ * the usecount is incremented.
+ * while the usecount > 0, that area can't be released.
+ */
+void omap_mmu_exmap_use(struct omap_mmu *mmu, void *vadr, size_t len)
+{
+ struct exmap_tbl *ent;
+
+ down_write(&mmu->exmap_sem);
+ omap_mmu_for_each_tlb_entry(mmu, ent) {
+ void *mapadr;
+ unsigned long mapsize;
+
+ if (!ent->valid)
+ continue;
+ mapadr = (void *)ent->vadr;
+ mapsize = 1 << (ent->order + PAGE_SHIFT);
+ if ((vadr + len > mapadr) && (vadr < mapadr + mapsize))
+ ent->usecount++;
+ }
+ up_write(&mmu->exmap_sem);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_exmap_use);
+
+void omap_mmu_exmap_unuse(struct omap_mmu *mmu, void *vadr, size_t len)
+{
+ struct exmap_tbl *ent;
+
+ down_write(&mmu->exmap_sem);
+ omap_mmu_for_each_tlb_entry(mmu, ent) {
+ void *mapadr;
+ unsigned long mapsize;
+
+ if (!ent->valid)
+ continue;
+ mapadr = (void *)ent->vadr;
+ mapsize = 1 << (ent->order + PAGE_SHIFT);
+ if ((vadr + len > mapadr) && (vadr < mapadr + mapsize))
+ ent->usecount--;
+ }
+ up_write(&mmu->exmap_sem);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_exmap_unuse);
+
+/*
+ * omap_mmu_virt_to_phys()
+ * returns physical address, and sets len to valid length
+ */
+unsigned long
+omap_mmu_virt_to_phys(struct omap_mmu *mmu, void *vadr, size_t *len)
+{
+ struct exmap_tbl *ent;
+
+ if (omap_mmu_internal_memory(mmu, vadr)) {
+ unsigned long addr = (unsigned long)vadr;
+ *len = mmu->membase + mmu->memsize - addr;
+ return addr;
+ }
+
+ /* EXRAM */
+ omap_mmu_for_each_tlb_entry(mmu, ent) {
+ void *mapadr;
+ unsigned long mapsize;
+
+ if (!ent->valid)
+ continue;
+ mapadr = (void *)ent->vadr;
+ mapsize = 1 << (ent->order + PAGE_SHIFT);
+ if ((vadr >= mapadr) && (vadr < mapadr + mapsize)) {
+ *len = mapadr + mapsize - vadr;
+ return __pa(ent->buf) + vadr - mapadr;
+ }
+ }
+
+ /* valid mapping not found */
+ return 0;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_virt_to_phys);
+
+/*
+ * PTE operations
+ */
+static inline void
+omap_mmu_alloc_section(struct mm_struct *mm, unsigned long virt,
+ unsigned long phys, int prot)
+{
+ pmd_t *pmdp = pmd_offset(pgd_offset(mm, virt), virt);
+ if (virt & (1 << SECTION_SHIFT))
+ pmdp++;
+ *pmdp = __pmd((phys & SECTION_MASK) | prot | PMD_TYPE_SECT);
+ flush_pmd_entry(pmdp);
+}
+
+static inline void
+omap_mmu_alloc_supersection(struct mm_struct *mm, unsigned long virt,
+ unsigned long phys, int prot)
+{
+ int i;
+ for (i = 0; i < 16; i += 1) {
+ omap_mmu_alloc_section(mm, virt, phys, prot | PMD_SECT_SUPER);
+ virt += (PGDIR_SIZE / 2);
+ }
+}
+
+static inline int
+omap_mmu_alloc_page(struct mm_struct *mm, unsigned long virt,
+ unsigned long phys, pgprot_t prot)
+{
+ pte_t *ptep;
+ pmd_t *pmdp = pmd_offset(pgd_offset(mm, virt), virt);
+
+ if (!(prot & PTE_TYPE_MASK))
+ prot |= PTE_TYPE_SMALL;
+
+ if (pmd_none(*pmdp)) {
+ ptep = pte_alloc_one_kernel(mm, virt);
+ if (ptep == NULL)
+ return -ENOMEM;
+ pmd_populate_kernel(mm, pmdp, ptep);
+ }
+ ptep = pte_offset_kernel(pmdp, virt);
+ ptep -= PTRS_PER_PTE;
+ *ptep = pfn_pte(phys >> PAGE_SHIFT, prot);
+ flush_pmd_entry((pmd_t *)ptep);
+ return 0;
+}
+
+static inline int
+omap_mmu_alloc_largepage(struct mm_struct *mm, unsigned long virt,
+ unsigned long phys, pgprot_t prot)
+{
+ int i, ret;
+ for (i = 0; i < 16; i += 1) {
+ ret = omap_mmu_alloc_page(mm, virt, phys,
+ prot | PTE_TYPE_LARGE);
+ if (ret)
+ return -ENOMEM; /* only 1st time */
+ virt += PAGE_SIZE;
+ }
+ return 0;
+}
+
+static int omap_mmu_load_pte(struct omap_mmu *mmu,
+ struct omap_mmu_tlb_entry *e)
+{
+ int ret = 0;
+ struct mm_struct *mm = mmu->twl_mm;
+ const unsigned long va = e->va;
+ const unsigned long pa = e->pa;
+ const pgprot_t prot = mmu->ops->pte_get_attr(e);
+
+ spin_lock(&mm->page_table_lock);
+
+ switch (e->pgsz) {
+ case OMAP_MMU_CAM_PAGESIZE_16MB:
+ omap_mmu_alloc_supersection(mm, va, pa, prot);
+ break;
+ case OMAP_MMU_CAM_PAGESIZE_1MB:
+ omap_mmu_alloc_section(mm, va, pa, prot);
+ break;
+ case OMAP_MMU_CAM_PAGESIZE_64KB:
+ ret = omap_mmu_alloc_largepage(mm, va, pa, prot);
+ break;
+ case OMAP_MMU_CAM_PAGESIZE_4KB:
+ ret = omap_mmu_alloc_page(mm, va, pa, prot);
+ break;
+ default:
+ BUG();
+ break;
+ }
+
+ spin_unlock(&mm->page_table_lock);
+
+ return ret;
+}
+
+static void omap_mmu_clear_pte(struct omap_mmu *mmu, unsigned long virt)
+{
+ pte_t *ptep, *end;
+ pmd_t *pmdp;
+ struct mm_struct *mm = mmu->twl_mm;
+
+ spin_lock(&mm->page_table_lock);
+
+ pmdp = pmd_offset(pgd_offset(mm, virt), virt);
+
+ if (pmd_none(*pmdp))
+ goto out;
+
+ if (!pmd_table(*pmdp))
+ goto invalidate_pmd;
+
+ ptep = pte_offset_kernel(pmdp, virt);
+ pte_clear(mm, virt, ptep);
+ flush_pmd_entry((pmd_t *)ptep);
+
+ /* zap pte */
+ end = pmd_page_vaddr(*pmdp);
+ ptep = end - PTRS_PER_PTE;
+ while (ptep < end) {
+ if (!pte_none(*ptep))
+ goto out;
+ ptep++;
+ }
+ pte_free_kernel(mm, pmd_page_vaddr(*pmdp));
+
+ invalidate_pmd:
+ pmd_clear(pmdp);
+ flush_pmd_entry(pmdp);
+ out:
+ spin_unlock(&mm->page_table_lock);
+}
+
+/*
+ * TLB operations
+ */
+static struct cam_ram_regset *
+omap_mmu_cam_ram_alloc(struct omap_mmu *mmu, struct omap_mmu_tlb_entry *entry)
+{
+ return mmu->ops->cam_ram_alloc(mmu, entry);
+}
+
+static int omap_mmu_cam_ram_valid(struct omap_mmu *mmu,
+ struct cam_ram_regset *cr)
+{
+ return mmu->ops->cam_ram_valid(cr);
+}
+
+static inline void
+omap_mmu_get_tlb_lock(struct omap_mmu *mmu, struct omap_mmu_tlb_lock *tlb_lock)
+{
+ unsigned long lock = omap_mmu_read_reg(mmu, OMAP_MMU_LOCK);
+ int mask;
+
+ mask = (mmu->type == OMAP_MMU_CAMERA) ?
+ CAMERA_MMU_LOCK_BASE_MASK : MMU_LOCK_BASE_MASK;
+ tlb_lock->base = (lock & mask) >> MMU_LOCK_BASE_SHIFT;
+
+ mask = (mmu->type == OMAP_MMU_CAMERA) ?
+ CAMERA_MMU_LOCK_VICTIM_MASK : MMU_LOCK_VICTIM_MASK;
+ tlb_lock->victim = (lock & mask) >> MMU_LOCK_VICTIM_SHIFT;
+}
+
+static inline void
+omap_mmu_set_tlb_lock(struct omap_mmu *mmu, struct omap_mmu_tlb_lock *lock)
+{
+ omap_mmu_write_reg(mmu,
+ (lock->base << MMU_LOCK_BASE_SHIFT) |
+ (lock->victim << MMU_LOCK_VICTIM_SHIFT),
+ OMAP_MMU_LOCK);
+}
+
+static inline void omap_mmu_flush(struct omap_mmu *mmu)
+{
+ omap_mmu_write_reg(mmu, 0x1, OMAP_MMU_FLUSH_ENTRY);
+}
+
+static inline void omap_mmu_ldtlb(struct omap_mmu *mmu)
+{
+ omap_mmu_write_reg(mmu, 0x1, OMAP_MMU_LD_TLB);
+}
+
+void omap_mmu_read_tlb(struct omap_mmu *mmu, struct omap_mmu_tlb_lock *lock,
+ struct cam_ram_regset *cr)
+{
+ /* set victim */
+ omap_mmu_set_tlb_lock(mmu, lock);
+
+ if (likely(mmu->ops->read_tlb))
+ mmu->ops->read_tlb(mmu, cr);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_read_tlb);
+
+void omap_mmu_load_tlb(struct omap_mmu *mmu, struct cam_ram_regset *cr)
+{
+ if (likely(mmu->ops->load_tlb))
+ mmu->ops->load_tlb(mmu, cr);
+
+ /* flush the entry */
+ omap_mmu_flush(mmu);
+
+ /* load a TLB entry */
+ omap_mmu_ldtlb(mmu);
+}
+
+int omap_mmu_load_tlb_entry(struct omap_mmu *mmu,
+ struct omap_mmu_tlb_entry *entry)
+{
+ struct omap_mmu_tlb_lock lock;
+ struct cam_ram_regset *cr;
+ int ret;
+
+ clk_enable(mmu->clk);
+ ret = omap_dsp_request_mem();
+ if (ret < 0)
+ goto out;
+
+ omap_mmu_get_tlb_lock(mmu, &lock);
+ for (lock.victim = 0; lock.victim < lock.base; lock.victim++) {
+ struct cam_ram_regset tmp;
+
+ /* read a TLB entry */
+ omap_mmu_read_tlb(mmu, &lock, &tmp);
+ if (!omap_mmu_cam_ram_valid(mmu, &tmp))
+ goto found_victim;
+ }
+ omap_mmu_set_tlb_lock(mmu, &lock);
+
+found_victim:
+ /* The last entry cannot be locked? */
+ if (lock.victim == (mmu->nr_tlb_entries - 1)) {
+ dev_err(mmu->dev, "MMU %s: TLB is full.\n", mmu->name);
+ return -EBUSY;
+ }
+
+ cr = omap_mmu_cam_ram_alloc(mmu, entry);
+ if (IS_ERR(cr))
+ return PTR_ERR(cr);
+
+ omap_mmu_load_tlb(mmu, cr);
+ kfree(cr);
+
+ /* update lock base */
+ if (lock.victim == lock.base)
+ lock.base++;
+
+ omap_mmu_set_tlb_lock(mmu, &lock);
+
+ omap_dsp_release_mem();
+out:
+ clk_disable(mmu->clk);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_load_tlb_entry);
+
+static inline unsigned long
+omap_mmu_cam_va(struct omap_mmu *mmu, struct cam_ram_regset *cr)
+{
+ return mmu->ops->cam_va(cr);
+}
+
+int omap_mmu_clear_tlb_entry(struct omap_mmu *mmu, unsigned long vadr)
+{
+ struct omap_mmu_tlb_lock lock;
+ int i, ret = 0;
+ int max_valid = 0;
+
+ clk_enable(mmu->clk);
+ ret = omap_dsp_request_mem();
+ if (ret < 0)
+ goto out;
+
+ omap_mmu_get_tlb_lock(mmu, &lock);
+ for (i = 0; i < lock.base; i++) {
+ struct cam_ram_regset cr;
+
+ /* read a TLB entry */
+ lock.victim = i;
+ omap_mmu_read_tlb(mmu, &lock, &cr);
+ if (!omap_mmu_cam_ram_valid(mmu, &cr))
+ continue;
+
+ if (omap_mmu_cam_va(mmu, &cr) == vadr)
+ /* flush the entry */
+ omap_mmu_flush(mmu);
+ else
+ max_valid = i;
+ }
+
+ /* set new lock base */
+ lock.base = lock.victim = max_valid + 1;
+ omap_mmu_set_tlb_lock(mmu, &lock);
+
+ omap_dsp_release_mem();
+out:
+ clk_disable(mmu->clk);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_clear_tlb_entry);
+
+static void omap_mmu_gflush(struct omap_mmu *mmu)
+{
+ struct omap_mmu_tlb_lock lock;
+ int ret;
+
+ clk_enable(mmu->clk);
+ ret = omap_dsp_request_mem();
+ if (ret < 0)
+ goto out;
+
+ omap_mmu_write_reg(mmu, 0x1, OMAP_MMU_GFLUSH);
+ lock.base = lock.victim = mmu->nr_exmap_preserved;
+ omap_mmu_set_tlb_lock(mmu, &lock);
+
+ omap_dsp_release_mem();
+out:
+ clk_disable(mmu->clk);
+}
+
+int omap_mmu_load_pte_entry(struct omap_mmu *mmu,
+ struct omap_mmu_tlb_entry *entry)
+{
+ int ret = -1;
+ /*XXX use PG_flag for prsvd */
+ ret = omap_mmu_load_pte(mmu, entry);
+ if (ret)
+ return ret;
+ if (entry->tlb)
+ ret = omap_mmu_load_tlb_entry(mmu, entry);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_load_pte_entry);
+
+int omap_mmu_clear_pte_entry(struct omap_mmu *mmu, unsigned long vadr)
+{
+ int ret = omap_mmu_clear_tlb_entry(mmu, vadr);
+ if (ret)
+ return ret;
+ omap_mmu_clear_pte(mmu, vadr);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_clear_pte_entry);
+
+/*
+ * omap_mmu_exmap()
+ *
+ * MEM_IOCTL_EXMAP ioctl calls this function with padr=0.
+ * In this case, the buffer for external device is allocated in this routine,
+ * then it is mapped.
+ * On the other hand, for example - frame buffer sharing, calls
+ * this function with padr set. It means some known address space
+ * pointed with padr is going to be shared with external device.
+ */
+int omap_mmu_exmap(struct omap_mmu *mmu, unsigned long devadr,
+ unsigned long padr, unsigned long size,
+ enum exmap_type type)
+{
+ unsigned long pgsz;
+ void *buf;
+ unsigned int order = 0;
+ unsigned long unit;
+ int prev = -1;
+ unsigned long _devadr = devadr;
+ unsigned long _padr = padr;
+ void *_vadr = omap_mmu_to_virt(mmu, devadr);
+ unsigned long _size = size;
+ struct omap_mmu_tlb_entry tlb_ent;
+ struct exmap_tbl *exmap_ent, *tmp_ent;
+ int status;
+ int idx;
+
+#define MINIMUM_PAGESZ SZ_4K
+ /*
+ * alignment check
+ */
+ if (!is_aligned(size, MINIMUM_PAGESZ)) {
+ dev_err(mmu->dev,
+ "MMU %s: size(0x%lx) is not multiple of 4KB.\n",
+ mmu->name, size);
+ return -EINVAL;
+ }
+ if (!is_aligned(devadr, MINIMUM_PAGESZ)) {
+ dev_err(mmu->dev,
+ "MMU %s: external device address(0x%lx) is not"
+ " aligned.\n", mmu->name, devadr);
+ return -EINVAL;
+ }
+ if (!is_aligned(padr, MINIMUM_PAGESZ)) {
+ dev_err(mmu->dev,
+ "MMU %s: physical address(0x%lx) is not aligned.\n",
+ mmu->name, padr);
+ return -EINVAL;
+ }
+
+ /* address validity check */
+ if ((devadr < mmu->memsize) ||
+ (devadr >= (1 << mmu->addrspace))) {
+ dev_err(mmu->dev,
+ "MMU %s: illegal address/size for %s().\n",
+ mmu->name, __func__);
+ return -EINVAL;
+ }
+
+ down_write(&mmu->exmap_sem);
+
+ /* overlap check */
+ omap_mmu_for_each_tlb_entry(mmu, tmp_ent) {
+ unsigned long mapsize;
+
+ if (!tmp_ent->valid)
+ continue;
+ mapsize = 1 << (tmp_ent->order + PAGE_SHIFT);
+ if ((_vadr + size > tmp_ent->vadr) &&
+ (_vadr < tmp_ent->vadr + mapsize)) {
+ dev_err(mmu->dev, "MMU %s: exmap page overlap!\n",
+ mmu->name);
+ up_write(&mmu->exmap_sem);
+ return -EINVAL;
+ }
+ }
+
+start:
+ buf = NULL;
+ /* Are there any free TLB lines? */
+ for (idx = 0; idx < mmu->nr_tlb_entries; idx++)
+ if (!mmu->exmap_tbl[idx].valid)
+ goto found_free;
+
+ dev_err(mmu->dev, "MMU %s: TLB is full.\n", mmu->name);
+ status = -EBUSY;
+ goto fail;
+
+found_free:
+ exmap_ent = mmu->exmap_tbl + idx;
+
+ if ((_size >= SZ_1M) &&
+ (is_aligned(_padr, SZ_1M) || (padr == 0)) &&
+ is_aligned(_devadr, SZ_1M)) {
+ unit = SZ_1M;
+ pgsz = OMAP_MMU_CAM_PAGESIZE_1MB;
+ } else if ((_size >= SZ_64K) &&
+ (is_aligned(_padr, SZ_64K) || (padr == 0)) &&
+ is_aligned(_devadr, SZ_64K)) {
+ unit = SZ_64K;
+ pgsz = OMAP_MMU_CAM_PAGESIZE_64KB;
+ } else {
+ unit = SZ_4K;
+ pgsz = OMAP_MMU_CAM_PAGESIZE_4KB;
+ }
+
+ order = get_order(unit);
+
+ /* buffer allocation */
+ if (type == EXMAP_TYPE_MEM) {
+ struct page *page, *ps, *pe;
+
+ if ((order == ORDER_1MB) && likely(mempool_1M))
+ buf = mempool_alloc_from_pool(mempool_1M, GFP_KERNEL);
+ else if ((order == ORDER_64KB) && likely(mempool_64K))
+ buf = mempool_alloc_from_pool(mempool_64K, GFP_KERNEL);
+ else {
+ buf = (void *)__get_dma_pages(GFP_KERNEL, order);
+ if (buf == NULL) {
+ status = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ /* mark the pages as reserved; this is needed for mmap */
+ ps = virt_to_page(buf);
+ pe = virt_to_page(buf + unit);
+
+ for (page = ps; page < pe; page++)
+ SetPageReserved(page);
+
+ _padr = __pa(buf);
+ }
+
+ /*
+ * mapping for ARM MMU:
+ * we should not access to the allocated memory through 'buf'
+ * since this area should not be cached.
+ */
+ status = exmap_set_armmmu(mmu, (unsigned long)_vadr, _padr, unit);
+ if (status < 0)
+ goto fail;
+
+ /* loading external device PTE entry */
+ INIT_TLB_ENTRY(&tlb_ent, _devadr, _padr, pgsz);
+ status = omap_mmu_load_pte_entry(mmu, &tlb_ent);
+ if (status < 0) {
+ exmap_clear_armmmu(mmu, (unsigned long)_vadr, unit);
+ goto fail;
+ }
+
+ INIT_EXMAP_TBL_ENTRY(exmap_ent, buf, _vadr, type, order);
+ exmap_ent->link.prev = prev;
+ if (prev >= 0)
+ mmu->exmap_tbl[prev].link.next = idx;
+
+ if ((_size -= unit) == 0) { /* normal completion */
+ up_write(&mmu->exmap_sem);
+ return size;
+ }
+
+ _devadr += unit;
+ _vadr += unit;
+ _padr = padr ? _padr + unit : 0;
+ prev = idx;
+ goto start;
+
+fail:
+ up_write(&mmu->exmap_sem);
+ if (buf)
+ omap_mmu_free_pages((unsigned long)buf, order);
+ omap_mmu_exunmap(mmu, devadr);
+ return status;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_exmap);
+
+static unsigned long unmap_free_arm(struct omap_mmu *mmu,
+ struct exmap_tbl *ent)
+{
+ unsigned long size;
+
+ /* clearing ARM MMU */
+ size = 1 << (ent->order + PAGE_SHIFT);
+ exmap_clear_armmmu(mmu, (unsigned long)ent->vadr, size);
+
+ /* freeing allocated memory */
+ if (ent->type == EXMAP_TYPE_MEM) {
+ omap_mmu_free_pages((unsigned long)ent->buf, ent->order);
+ dev_dbg(mmu->dev, "MMU %s: freeing 0x%lx bytes @ adr 0x%8p\n",
+ mmu->name, size, ent->buf);
+ }
+
+ ent->valid = 0;
+ return size;
+}
+
+int omap_mmu_exunmap(struct omap_mmu *mmu, unsigned long devadr)
+{
+ void *vadr;
+ unsigned long size;
+ int total = 0;
+ struct exmap_tbl *ent;
+ int idx;
+
+ vadr = omap_mmu_to_virt(mmu, devadr);
+ down_write(&mmu->exmap_sem);
+ for (idx = 0; idx < mmu->nr_tlb_entries; idx++) {
+ ent = mmu->exmap_tbl + idx;
+ if (!ent->valid || ent->prsvd)
+ continue;
+ if (ent->vadr == vadr)
+ goto found_map;
+ }
+ up_write(&mmu->exmap_sem);
+ dev_warn(mmu->dev, "MMU %s: address %06lx not found in exmap_tbl.\n",
+ mmu->name, devadr);
+ return -EINVAL;
+
+found_map:
+ if (ent->usecount > 0) {
+ dev_err(mmu->dev, "MMU %s: exmap reference count is not 0.\n"
+ " idx=%d, vadr=%p, order=%d, usecount=%d\n",
+ mmu->name, idx, ent->vadr, ent->order, ent->usecount);
+ up_write(&mmu->exmap_sem);
+ return -EINVAL;
+ }
+ /* clearing external device PTE entry */
+ omap_mmu_clear_pte_entry(mmu, devadr);
+
+ /* clear ARM MMU and free buffer */
+ size = unmap_free_arm(mmu, ent);
+ total += size;
+
+ /* we don't free PTEs */
+
+ /* flush TLB */
+ flush_tlb_kernel_range((unsigned long)vadr, (unsigned long)vadr + size);
+
+ /* check if next mapping is in same group */
+ idx = ent->link.next;
+ if (idx < 0)
+ goto up_out; /* normal completion */
+ ent = mmu->exmap_tbl + idx;
+ devadr += size;
+ vadr += size;
+ if (ent->vadr == vadr)
+ goto found_map; /* continue */
+
+ dev_err(mmu->dev, "MMU %s: illegal exmap_tbl grouping!\n"
+ "expected vadr = %p, exmap_tbl[%d].vadr = %p\n",
+ mmu->name, vadr, idx, ent->vadr);
+ up_write(&mmu->exmap_sem);
+ return -EINVAL;
+
+up_out:
+ up_write(&mmu->exmap_sem);
+ return total;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_exunmap);
+
+void omap_mmu_exmap_flush(struct omap_mmu *mmu)
+{
+ struct exmap_tbl *ent;
+
+ down_write(&mmu->exmap_sem);
+
+ /* clearing TLB entry */
+ omap_mmu_gflush(mmu);
+
+ omap_mmu_for_each_tlb_entry(mmu, ent)
+ if (ent->valid && !ent->prsvd)
+ unmap_free_arm(mmu, ent);
+
+ /* flush TLB */
+ if (likely(mmu->membase))
+ flush_tlb_kernel_range(mmu->membase + mmu->memsize,
+ mmu->membase + (1 << mmu->addrspace));
+
+ up_write(&mmu->exmap_sem);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_exmap_flush);
+
+void exmap_setup_preserved_mem_page(struct omap_mmu *mmu, void *buf,
+ unsigned long devadr, int index)
+{
+ unsigned long phys;
+ void *virt;
+ struct omap_mmu_tlb_entry tlb_ent;
+
+ phys = __pa(buf);
+ virt = omap_mmu_to_virt(mmu, devadr);
+ exmap_set_armmmu(mmu, (unsigned long)virt, phys, PAGE_SIZE);
+ INIT_EXMAP_TBL_ENTRY_4KB_PRESERVED(mmu->exmap_tbl + index, buf, virt);
+ INIT_TLB_ENTRY_4KB_PRESERVED(&tlb_ent, devadr, phys);
+ omap_mmu_load_pte_entry(mmu, &tlb_ent);
+}
+EXPORT_SYMBOL_GPL(exmap_setup_preserved_mem_page);
+
+void exmap_clear_mem_page(struct omap_mmu *mmu, unsigned long devadr)
+{
+ void *virt = omap_mmu_to_virt(mmu, devadr);
+
+ exmap_clear_armmmu(mmu, (unsigned long)virt, PAGE_SIZE);
+ /* DSP MMU is shutting down. not handled here. */
+}
+EXPORT_SYMBOL_GPL(exmap_clear_mem_page);
+
+static void omap_mmu_reset(struct omap_mmu *mmu)
+{
+#if defined(CONFIG_ARCH_OMAP2) /* FIXME */
+ int i;
+
+ omap_mmu_write_reg(mmu, 0x2, OMAP_MMU_SYSCONFIG);
+
+ for (i = 0; i < 10000; i++)
+ if (likely(omap_mmu_read_reg(mmu, OMAP_MMU_SYSSTATUS) & 0x1))
+ break;
+#endif
+}
+
+void omap_mmu_disable(struct omap_mmu *mmu)
+{
+ omap_mmu_write_reg(mmu, 0x00, OMAP_MMU_CNTL);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_disable);
+
+void omap_mmu_enable(struct omap_mmu *mmu, int reset)
+{
+ u32 val = OMAP_MMU_CNTL_MMU_EN | MMU_CNTL_TWLENABLE;
+
+ if (likely(reset))
+ omap_mmu_reset(mmu);
+#if defined(CONFIG_ARCH_OMAP2) /* FIXME */
+ omap_mmu_write_reg(mmu, (u32)virt_to_phys(mmu->twl_mm->pgd),
+ OMAP_MMU_TTB);
+#else
+ omap_mmu_write_reg(mmu, (u32)virt_to_phys(mmu->twl_mm->pgd) & 0xffff,
+ OMAP_MMU_TTB_L);
+ omap_mmu_write_reg(mmu, (u32)virt_to_phys(mmu->twl_mm->pgd) >> 16,
+ OMAP_MMU_TTB_H);
+ val |= OMAP_MMU_CNTL_RESET_SW;
+#endif
+ omap_mmu_write_reg(mmu, val, OMAP_MMU_CNTL);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_enable);
+
+static irqreturn_t omap_mmu_interrupt(int irq, void *dev_id)
+{
+ struct omap_mmu *mmu = dev_id;
+
+ if (likely(mmu->ops->interrupt))
+ mmu->ops->interrupt(mmu);
+
+ return IRQ_HANDLED;
+}
+
+static int omap_mmu_init(struct omap_mmu *mmu)
+{
+ struct omap_mmu_tlb_lock tlb_lock;
+ int ret = 0;
+
+ clk_enable(mmu->clk);
+ ret = omap_dsp_request_mem();
+ if (ret < 0)
+ goto out;
+
+ down_write(&mmu->exmap_sem);
+
+ ret = request_irq(mmu->irq, omap_mmu_interrupt, IRQF_DISABLED,
+ mmu->name, mmu);
+ if (ret < 0) {
+ dev_err(mmu->dev, "MMU %s: failed to register MMU interrupt:"
+ " %d\n", mmu->name, ret);
+ goto fail;
+ }
+
+ omap_mmu_disable(mmu); /* clear all */
+ udelay(100);
+ omap_mmu_enable(mmu, 1);
+
+ memset(&tlb_lock, 0, sizeof(struct omap_mmu_tlb_lock));
+ omap_mmu_set_tlb_lock(mmu, &tlb_lock);
+
+ if (unlikely(mmu->ops->startup))
+ ret = mmu->ops->startup(mmu);
+fail:
+ up_write(&mmu->exmap_sem);
+ omap_dsp_release_mem();
+out:
+ clk_disable(mmu->clk);
+
+ return ret;
+}
+
+static void omap_mmu_shutdown(struct omap_mmu *mmu)
+{
+ free_irq(mmu->irq, mmu);
+
+ if (unlikely(mmu->ops->shutdown))
+ mmu->ops->shutdown(mmu);
+
+ omap_mmu_exmap_flush(mmu);
+ omap_mmu_disable(mmu); /* clear all */
+}
+
+/*
+ * omap_mmu_mem_enable() / disable()
+ */
+int omap_mmu_mem_enable(struct omap_mmu *mmu, void *addr)
+{
+ if (unlikely(mmu->ops->mem_enable))
+ return mmu->ops->mem_enable(mmu, addr);
+
+ down_read(&mmu->exmap_sem);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_mem_enable);
+
+void omap_mmu_mem_disable(struct omap_mmu *mmu, void *addr)
+{
+ if (unlikely(mmu->ops->mem_disable)) {
+ mmu->ops->mem_disable(mmu, addr);
+ return;
+ }
+
+ up_read(&mmu->exmap_sem);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_mem_disable);
+
+/*
+ * dsp_mem file operations
+ */
+static ssize_t intmem_read(struct omap_mmu *mmu, char *buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned long p = *ppos;
+ void *vadr = omap_mmu_to_virt(mmu, p);
+ ssize_t size = mmu->memsize;
+ ssize_t read;
+
+ if (p >= size)
+ return 0;
+ clk_enable(mmu->memclk);
+ read = count;
+ if (count > size - p)
+ read = size - p;
+ if (copy_to_user(buf, vadr, read)) {
+ read = -EFAULT;
+ goto out;
+ }
+ *ppos += read;
+out:
+ clk_disable(mmu->memclk);
+ return read;
+}
+
+static ssize_t exmem_read(struct omap_mmu *mmu, char *buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned long p = *ppos;
+ void *vadr = omap_mmu_to_virt(mmu, p);
+
+ if (!exmap_valid(mmu, vadr, count)) {
+ dev_err(mmu->dev, "MMU %s: external device address %08lx / "
+ "size %08x is not valid!\n", mmu->name, p, count);
+ return -EFAULT;
+ }
+ if (count > (1 << mmu->addrspace) - p)
+ count = (1 << mmu->addrspace) - p;
+ if (copy_to_user(buf, vadr, count))
+ return -EFAULT;
+ *ppos += count;
+
+ return count;
+}
+
+static ssize_t omap_mmu_mem_read(struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t count)
+{
+ struct device *dev = to_dev(kobj);
+ struct omap_mmu *mmu = dev_get_drvdata(dev);
+ unsigned long p = (unsigned long)offset;
+ void *vadr = omap_mmu_to_virt(mmu, p);
+ int ret;
+
+ if (omap_mmu_mem_enable(mmu, vadr) < 0)
+ return -EBUSY;
+
+ if (p < mmu->memsize)
+ ret = intmem_read(mmu, buf, count, &offset);
+ else
+ ret = exmem_read(mmu, buf, count, &offset);
+
+ omap_mmu_mem_disable(mmu, vadr);
+
+ return ret;
+}
+
+static ssize_t intmem_write(struct omap_mmu *mmu, const char *buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned long p = *ppos;
+ void *vadr = omap_mmu_to_virt(mmu, p);
+ ssize_t size = mmu->memsize;
+ ssize_t written;
+
+ if (p >= size)
+ return 0;
+ clk_enable(mmu->memclk);
+ written = count;
+ if (count > size - p)
+ written = size - p;
+ if (copy_from_user(vadr, buf, written)) {
+ written = -EFAULT;
+ goto out;
+ }
+ *ppos += written;
+out:
+ clk_disable(mmu->memclk);
+ return written;
+}
+
+static ssize_t exmem_write(struct omap_mmu *mmu, char *buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned long p = *ppos;
+ void *vadr = omap_mmu_to_virt(mmu, p);
+
+ if (!exmap_valid(mmu, vadr, count)) {
+ dev_err(mmu->dev, "MMU %s: external device address %08lx "
+ "/ size %08x is not valid!\n", mmu->name, p, count);
+ return -EFAULT;
+ }
+ if (count > (1 << mmu->addrspace) - p)
+ count = (1 << mmu->addrspace) - p;
+ if (copy_from_user(vadr, buf, count))
+ return -EFAULT;
+ *ppos += count;
+
+ return count;
+}
+
+static ssize_t omap_mmu_mem_write(struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t count)
+{
+ struct device *dev = to_dev(kobj);
+ struct omap_mmu *mmu = dev_get_drvdata(dev);
+ unsigned long p = (unsigned long)offset;
+ void *vadr = omap_mmu_to_virt(mmu, p);
+ int ret;
+
+ if (omap_mmu_mem_enable(mmu, vadr) < 0)
+ return -EBUSY;
+
+ if (p < mmu->memsize)
+ ret = intmem_write(mmu, buf, count, &offset);
+ else
+ ret = exmem_write(mmu, buf, count, &offset);
+
+ omap_mmu_mem_disable(mmu, vadr);
+
+ return ret;
+}
+
+static struct bin_attribute dev_attr_mem = {
+ .attr = {
+ .name = "mem",
+ .owner = THIS_MODULE,
+ .mode = S_IRUSR | S_IWUSR | S_IRGRP,
+ },
+
+ .read = omap_mmu_mem_read,
+ .write = omap_mmu_mem_write,
+};
+
+/* To be obsolete for backward compatibility */
+ssize_t __omap_mmu_mem_read(struct omap_mmu *mmu,
+ struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t count)
+{
+ return omap_mmu_mem_read(&mmu->dev->kobj, attr, buf, offset, count);
+}
+EXPORT_SYMBOL_GPL(__omap_mmu_mem_read);
+
+ssize_t __omap_mmu_mem_write(struct omap_mmu *mmu,
+ struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t count)
+{
+ return omap_mmu_mem_write(&mmu->dev->kobj, attr, buf, offset, count);
+}
+EXPORT_SYMBOL_GPL(__omap_mmu_mem_write);
+
+/*
+ * sysfs files
+ */
+static ssize_t omap_mmu_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct omap_mmu *mmu = dev_get_drvdata(dev);
+ struct omap_mmu_tlb_lock tlb_lock;
+ int ret;
+
+ clk_enable(mmu->clk);
+ ret = omap_dsp_request_mem();
+ if (ret < 0)
+ goto out;
+
+ down_read(&mmu->exmap_sem);
+
+ omap_mmu_get_tlb_lock(mmu, &tlb_lock);
+
+ ret = -EIO;
+ if (likely(mmu->ops->show))
+ ret = mmu->ops->show(mmu, buf, &tlb_lock);
+
+ /* restore victim entry */
+ omap_mmu_set_tlb_lock(mmu, &tlb_lock);
+
+ up_read(&mmu->exmap_sem);
+ omap_dsp_release_mem();
+out:
+ clk_disable(mmu->clk);
+
+ return ret;
+}
+
+static DEVICE_ATTR(mmu, S_IRUGO, omap_mmu_show, NULL);
+
+static ssize_t exmap_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct omap_mmu *mmu = dev_get_drvdata(dev);
+ struct exmap_tbl *ent;
+ int len;
+ int i = 0;
+
+ down_read(&mmu->exmap_sem);
+ len = sprintf(buf, " devadr size buf size uc\n");
+ /* 0x300000 0x123000 0xc0171000 0x100000 0*/
+
+ omap_mmu_for_each_tlb_entry(mmu, ent) {
+ void *vadr;
+ unsigned long size;
+ enum exmap_type type;
+ int idx;
+
+ /* find a top of link */
+ if (!ent->valid || (ent->link.prev >= 0))
+ continue;
+
+ vadr = ent->vadr;
+ type = ent->type;
+ size = 0;
+ idx = i;
+ do {
+ ent = mmu->exmap_tbl + idx;
+ size += PAGE_SIZE << ent->order;
+ } while ((idx = ent->link.next) >= 0);
+
+ len += sprintf(buf + len, "0x%06lx %#8lx",
+ virt_to_omap_mmu(mmu, vadr), size);
+
+ if (type == EXMAP_TYPE_FB) {
+ len += sprintf(buf + len, " framebuf\n");
+ } else {
+ len += sprintf(buf + len, "\n");
+ idx = i;
+ do {
+ ent = mmu->exmap_tbl + idx;
+ len += sprintf(buf + len,
+ /* 0xc0171000 0x100000 0*/
+ "%19s0x%8p %#8lx %2d\n",
+ "", ent->buf,
+ PAGE_SIZE << ent->order,
+ ent->usecount);
+ } while ((idx = ent->link.next) >= 0);
+ }
+
+ i++;
+ }
+
+ up_read(&mmu->exmap_sem);
+ return len;
+}
+
+static ssize_t exmap_store(struct device *dev, struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct omap_mmu *mmu = dev_get_drvdata(dev);
+ unsigned long base = 0, len = 0;
+ int ret;
+
+ sscanf(buf, "%lx %lx", &base, &len);
+
+ if (!base)
+ return -EINVAL;
+
+ if (len) {
+ /* Add the mapping */
+ ret = omap_mmu_exmap(mmu, base, 0, len, EXMAP_TYPE_MEM);
+ if (ret < 0)
+ return ret;
+ } else {
+ /* Remove the mapping */
+ ret = omap_mmu_exunmap(mmu, base);
+ if (ret < 0)
+ return ret;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(exmap, S_IRUGO | S_IWUSR, exmap_show, exmap_store);
+
+static ssize_t mempool_show(struct class *class, char *buf)
+{
+ int min_nr_1M = 0, curr_nr_1M = 0;
+ int min_nr_64K = 0, curr_nr_64K = 0;
+ int total = 0;
+
+ if (likely(mempool_1M)) {
+ min_nr_1M = mempool_1M->min_nr;
+ curr_nr_1M = mempool_1M->curr_nr;
+ total += min_nr_1M * SZ_1M;
+ }
+ if (likely(mempool_64K)) {
+ min_nr_64K = mempool_64K->min_nr;
+ curr_nr_64K = mempool_64K->curr_nr;
+ total += min_nr_64K * SZ_64K;
+ }
+
+ return sprintf(buf,
+ "0x%x\n"
+ "1M buffer: %d (%d free)\n"
+ "64K buffer: %d (%d free)\n",
+ total, min_nr_1M, curr_nr_1M, min_nr_64K, curr_nr_64K);
+}
+
+
+static CLASS_ATTR(mempool, S_IRUGO, mempool_show, NULL);
+
+static struct class omap_mmu_class = {
+ .name = "mmu",
+};
+
+int omap_mmu_register(struct omap_mmu *mmu)
+{
+ int ret;
+
+ mmu->dev = device_create(&omap_mmu_class, NULL, 0, "%s", mmu->name);
+ if (unlikely(IS_ERR(mmu->dev)))
+ return PTR_ERR(mmu->dev);
+ dev_set_drvdata(mmu->dev, mmu);
+
+ mmu->exmap_tbl = kcalloc(mmu->nr_tlb_entries, sizeof(struct exmap_tbl),
+ GFP_KERNEL);
+ if (!mmu->exmap_tbl)
+ return -ENOMEM;
+
+ mmu->twl_mm = mm_alloc();
+ if (!mmu->twl_mm) {
+ ret = -ENOMEM;
+ goto err_mm_alloc;
+ }
+
+ init_rwsem(&mmu->exmap_sem);
+
+ ret = omap_mmu_init(mmu);
+ if (unlikely(ret))
+ goto err_mmu_init;
+
+ ret = device_create_file(mmu->dev, &dev_attr_mmu);
+ if (unlikely(ret))
+ goto err_dev_create_mmu;
+ ret = device_create_file(mmu->dev, &dev_attr_exmap);
+ if (unlikely(ret))
+ goto err_dev_create_exmap;
+
+ if (likely(mmu->membase)) {
+ dev_attr_mem.size = mmu->memsize;
+ ret = device_create_bin_file(mmu->dev,
+ &dev_attr_mem);
+ if (unlikely(ret))
+ goto err_bin_create_mem;
+ }
+ return 0;
+
+err_bin_create_mem:
+ device_remove_file(mmu->dev, &dev_attr_exmap);
+err_dev_create_exmap:
+ device_remove_file(mmu->dev, &dev_attr_mmu);
+err_dev_create_mmu:
+ omap_mmu_shutdown(mmu);
+err_mmu_init:
+ kfree(mmu->twl_mm);
+ mmu->twl_mm = NULL;
+err_mm_alloc:
+ kfree(mmu->exmap_tbl);
+ mmu->exmap_tbl = NULL;
+ device_unregister(mmu->dev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_register);
+
+void omap_mmu_unregister(struct omap_mmu *mmu)
+{
+ omap_mmu_shutdown(mmu);
+ omap_mmu_kmem_release();
+
+ device_remove_file(mmu->dev, &dev_attr_mmu);
+ device_remove_file(mmu->dev, &dev_attr_exmap);
+
+ if (likely(mmu->membase))
+ device_remove_bin_file(mmu->dev, &dev_attr_mem);
+
+ device_unregister(mmu->dev);
+
+ kfree(mmu->exmap_tbl);
+ mmu->exmap_tbl = NULL;
+
+ if (mmu->twl_mm) {
+ __mmdrop(mmu->twl_mm);
+ mmu->twl_mm = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(omap_mmu_unregister);
+
+static int __init omap_mmu_class_init(void)
+{
+ int ret = class_register(&omap_mmu_class);
+ if (!ret)
+ ret = class_create_file(&omap_mmu_class, &class_attr_mempool);
+
+ return ret;
+}
+
+static void __exit omap_mmu_class_exit(void)
+{
+ class_remove_file(&omap_mmu_class, &class_attr_mempool);
+ class_unregister(&omap_mmu_class);
+}
+
+subsys_initcall(omap_mmu_class_init);
+module_exit(omap_mmu_class_exit);
+
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/hardware.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/board.h>
- #include <asm/arch/irqs.h>
+/*
+ * linux/drivers/bluetooth/brf6150/brf6150.c
+ *
+ * Copyright (C) 2005 Nokia Corporation
+ * Written by Ville Tervo <ville.tervo@nokia.com>
+ *
+ * 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/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/serial_reg.h>
+#include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include <linux/irq.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
++#include <mach/hardware.h>
++#include <mach/gpio.h>
++#include <mach/board.h>
++#include <mach/irqs.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#include "brf6150.h"
+
+#if 0
+#define NBT_DBG(fmt, arg...) printk("%s: " fmt "" , __FUNCTION__ , ## arg)
+#else
+#define NBT_DBG(...)
+#endif
+
+#if 0
+#define NBT_DBG_FW(fmt, arg...) printk("%s: " fmt "" , __FUNCTION__ , ## arg)
+#else
+#define NBT_DBG_FW(...)
+#endif
+
+#if 0
+#define NBT_DBG_POWER(fmt, arg...) printk("%s: " fmt "" , __FUNCTION__ , ## arg)
+#else
+#define NBT_DBG_POWER(...)
+#endif
+
+#if 0
+#define NBT_DBG_TRANSFER(fmt, arg...) printk("%s: " fmt "" , __FUNCTION__ , ## arg)
+#else
+#define NBT_DBG_TRANSFER(...)
+#endif
+
+#if 0
+#define NBT_DBG_TRANSFER_NF(fmt, arg...) printk(fmt "" , ## arg)
+#else
+#define NBT_DBG_TRANSFER_NF(...)
+#endif
+
+#define PM_TIMEOUT (2000)
+
+static void brf6150_device_release(struct device *dev);
+static struct brf6150_info *exit_info;
+
+static struct platform_device brf6150_device = {
+ .name = BT_DEVICE,
+ .id = -1,
+ .num_resources = 0,
+ .dev = {
+ .release = brf6150_device_release,
+ }
+};
+
+static struct device_driver brf6150_driver = {
+ .name = BT_DRIVER,
+ .bus = &platform_bus_type,
+};
+
+static inline void brf6150_outb(struct brf6150_info *info, unsigned int offset, u8 val)
+{
+ outb(val, info->uart_base + (offset << 2));
+}
+
+static inline u8 brf6150_inb(struct brf6150_info *info, unsigned int offset)
+{
+ return inb(info->uart_base + (offset << 2));
+}
+
+static void brf6150_set_rts(struct brf6150_info *info, int active)
+{
+ u8 b;
+
+ b = brf6150_inb(info, UART_MCR);
+ if (active)
+ b |= UART_MCR_RTS;
+ else
+ b &= ~UART_MCR_RTS;
+ brf6150_outb(info, UART_MCR, b);
+}
+
+static void brf6150_wait_for_cts(struct brf6150_info *info, int active,
+ int timeout_ms)
+{
+ int okay;
+ unsigned long timeout;
+
+ okay = 0;
+ timeout = jiffies + msecs_to_jiffies(timeout_ms);
+ for (;;) {
+ int state;
+
+ state = brf6150_inb(info, UART_MSR) & UART_MSR_CTS;
+ if (active) {
+ if (state)
+ break;
+ } else {
+ if (!state)
+ break;
+ }
+ if (jiffies > timeout)
+ break;
+ }
+}
+
+static inline void brf6150_set_auto_ctsrts(struct brf6150_info *info, int on)
+{
+ u8 lcr, b;
+
+ lcr = brf6150_inb(info, UART_LCR);
+ brf6150_outb(info, UART_LCR, 0xbf);
+ b = brf6150_inb(info, UART_EFR);
+ if (on)
+ b |= UART_EFR_CTS | UART_EFR_RTS;
+ else
+ b &= ~(UART_EFR_CTS | UART_EFR_RTS);
+ brf6150_outb(info, UART_EFR, b);
+ brf6150_outb(info, UART_LCR, lcr);
+}
+
+static inline void brf6150_enable_pm_rx(struct brf6150_info *info)
+{
+ if (info->pm_enabled) {
+ info->rx_pm_enabled = 1;
+ }
+}
+
+static inline void brf6150_disable_pm_rx(struct brf6150_info *info)
+{
+ if (info->pm_enabled) {
+ info->rx_pm_enabled = 0;
+ }
+}
+
+static void brf6150_enable_pm_tx(struct brf6150_info *info)
+{
+ if (info->pm_enabled) {
+ mod_timer(&info->pm_timer, jiffies + msecs_to_jiffies(PM_TIMEOUT));
+ info->tx_pm_enabled = 1;
+ }
+}
+
+static void brf6150_disable_pm_tx(struct brf6150_info *info)
+{
+ if (info->pm_enabled) {
+ info->tx_pm_enabled = 0;
+ omap_set_gpio_dataout(info->btinfo->bt_wakeup_gpio, 1);
+ }
+ if (omap_get_gpio_datain(info->btinfo->host_wakeup_gpio))
+ tasklet_schedule(&info->tx_task);
+}
+
+static void brf6150_pm_timer(unsigned long data)
+{
+ struct brf6150_info *info;
+
+ info = (struct brf6150_info *)data;
+ if (info->tx_pm_enabled && info->rx_pm_enabled && !test_bit(HCI_INQUIRY, &info->hdev->flags))
+ omap_set_gpio_dataout(info->btinfo->bt_wakeup_gpio, 0);
+ else
+ mod_timer(&info->pm_timer, jiffies + msecs_to_jiffies(PM_TIMEOUT));
+}
+
+static int brf6150_change_speed(struct brf6150_info *info, unsigned long speed)
+{
+ unsigned int divisor;
+ u8 lcr, mdr1;
+
+ NBT_DBG("Setting speed %lu\n", speed);
+
+ if (speed >= 460800) {
+ divisor = UART_CLOCK / 13 / speed;
+ mdr1 = 3;
+ } else {
+ divisor = UART_CLOCK / 16 / speed;
+ mdr1 = 0;
+ }
+
+ brf6150_outb(info, UART_OMAP_MDR1, 7); /* Make sure UART mode is disabled */
+ lcr = brf6150_inb(info, UART_LCR);
+ brf6150_outb(info, UART_LCR, UART_LCR_DLAB); /* Set DLAB */
+ brf6150_outb(info, UART_DLL, divisor & 0xff); /* Set speed */
+ brf6150_outb(info, UART_DLM, divisor >> 8);
+ brf6150_outb(info, UART_LCR, lcr);
+ brf6150_outb(info, UART_OMAP_MDR1, mdr1); /* Make sure UART mode is enabled */
+
+ return 0;
+}
+
+/* Firmware handling */
+static int brf6150_open_firmware(struct brf6150_info *info)
+{
+ int err;
+
+ info->fw_pos = 0;
+ err = request_firmware(&info->fw_entry, "brf6150fw.bin", &brf6150_device.dev);
+
+ return err;
+}
+
+static struct sk_buff *brf6150_read_fw_cmd(struct brf6150_info *info, int how)
+{
+ struct sk_buff *skb;
+ unsigned int cmd_len;
+
+ if (info->fw_pos >= info->fw_entry->size) {
+ return NULL;
+ }
+
+ cmd_len = info->fw_entry->data[info->fw_pos++];
+ if (!cmd_len)
+ return NULL;
+
+ if (info->fw_pos + cmd_len > info->fw_entry->size) {
+ printk(KERN_WARNING "Corrupted firmware image\n");
+ return NULL;
+ }
+
+ skb = bt_skb_alloc(cmd_len, how);
+ if (!skb) {
+ printk(KERN_WARNING "Cannot reserve memory for buffer\n");
+ return NULL;
+ }
+ memcpy(skb_put(skb, cmd_len), &info->fw_entry->data[info->fw_pos], cmd_len);
+
+ info->fw_pos += cmd_len;
+
+ return skb;
+}
+
+static int brf6150_close_firmware(struct brf6150_info *info)
+{
+ release_firmware(info->fw_entry);
+ return 0;
+}
+
+static int brf6150_send_alive_packet(struct brf6150_info *info)
+{
+ struct sk_buff *skb;
+
+ NBT_DBG("Sending alive packet\n");
+ skb = brf6150_read_fw_cmd(info, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_WARNING "Cannot read alive command");
+ return -1;
+ }
+
+ clk_enable(info->uart_ck);
+ skb_queue_tail(&info->txq, skb);
+ tasklet_schedule(&info->tx_task);
+
+ NBT_DBG("Alive packet sent\n");
+ return 0;
+}
+
+static void brf6150_alive_packet(struct brf6150_info *info, struct sk_buff *skb)
+{
+ NBT_DBG("Received alive packet\n");
+ if (skb->data[1] == 0xCC) {
+ complete(&info->init_completion);
+ }
+
+ kfree_skb(skb);
+}
+
+static int brf6150_send_negotiation(struct brf6150_info *info)
+{
+ struct sk_buff *skb;
+ NBT_DBG("Sending negotiation..\n");
+
+ brf6150_change_speed(info, INIT_SPEED);
+
+ skb = brf6150_read_fw_cmd(info, GFP_KERNEL);
+
+ if (!skb) {
+ printk(KERN_WARNING "Cannot read negoatiation message");
+ return -1;
+ }
+
+ clk_enable(info->uart_ck);
+ skb_queue_tail(&info->txq, skb);
+ tasklet_schedule(&info->tx_task);
+
+
+ NBT_DBG("Negotiation sent\n");
+ return 0;
+}
+
+static void brf6150_negotiation_packet(struct brf6150_info *info,
+ struct sk_buff *skb)
+{
+ if (skb->data[1] == 0x20) {
+ /* Change to operational settings */
+ brf6150_set_rts(info, 0);
+ brf6150_wait_for_cts(info, 0, 100);
+ brf6150_change_speed(info, MAX_BAUD_RATE);
+ brf6150_set_rts(info, 1);
+ brf6150_wait_for_cts(info, 1, 100);
+ brf6150_set_auto_ctsrts(info, 1);
+ brf6150_send_alive_packet(info);
+ } else {
+ printk(KERN_WARNING "Could not negotiate brf6150 settings\n");
+ }
+ kfree_skb(skb);
+}
+
+static int brf6150_get_hdr_len(u8 pkt_type)
+{
+ long retval;
+
+ switch (pkt_type) {
+ case H4_EVT_PKT:
+ retval = HCI_EVENT_HDR_SIZE;
+ break;
+ case H4_ACL_PKT:
+ retval = HCI_ACL_HDR_SIZE;
+ break;
+ case H4_SCO_PKT:
+ retval = HCI_SCO_HDR_SIZE;
+ break;
+ case H4_NEG_PKT:
+ retval = 9;
+ break;
+ case H4_ALIVE_PKT:
+ retval = 3;
+ break;
+ default:
+ printk(KERN_ERR "brf6150: Unknown H4 packet");
+ retval = -1;
+ break;
+ }
+
+ return retval;
+}
+
+static unsigned int brf6150_get_data_len(struct brf6150_info *info,
+ struct sk_buff *skb)
+{
+ long retval = -1;
+ struct hci_event_hdr *evt_hdr;
+ struct hci_acl_hdr *acl_hdr;
+ struct hci_sco_hdr *sco_hdr;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case H4_EVT_PKT:
+ evt_hdr = (struct hci_event_hdr *)skb->data;
+ retval = evt_hdr->plen;
+ break;
+ case H4_ACL_PKT:
+ acl_hdr = (struct hci_acl_hdr *)skb->data;
+ retval = le16_to_cpu(acl_hdr->dlen);
+ break;
+ case H4_SCO_PKT:
+ sco_hdr = (struct hci_sco_hdr *)skb->data;
+ retval = sco_hdr->dlen;
+ break;
+ case H4_NEG_PKT:
+ retval = 0;
+ break;
+ case H4_ALIVE_PKT:
+ retval = 0;
+ break;
+ }
+
+ return retval;
+}
+
+static void brf6150_parse_fw_event(struct brf6150_info *info)
+{
+ struct hci_fw_event *ev;
+
+ if (bt_cb(info->rx_skb)->pkt_type != H4_EVT_PKT) {
+ printk(KERN_WARNING "Got non event fw packet.\n");
+ info->fw_error = 1;
+ return;
+ }
+
+ ev = (struct hci_fw_event *)info->rx_skb->data;
+ if (ev->hev.evt != HCI_EV_CMD_COMPLETE) {
+ printk(KERN_WARNING "Got non cmd complete fw event\n");
+ info->fw_error = 1;
+ return;
+ }
+
+ if (ev->status != 0) {
+ printk(KERN_WARNING "Got error status from fw command\n");
+ info->fw_error = 1;
+ return;
+ }
+
+ complete(&info->fw_completion);
+}
+
+static inline void brf6150_recv_frame(struct brf6150_info *info,
+ struct sk_buff *skb)
+{
+ if (unlikely(!test_bit(HCI_RUNNING, &info->hdev->flags))) {
+ NBT_DBG("fw_event\n");
+ brf6150_parse_fw_event(info);
+ kfree_skb(skb);
+ } else {
+ hci_recv_frame(skb);
+ if (!(brf6150_inb(info, UART_LSR) & UART_LSR_DR))
+ brf6150_enable_pm_rx(info);
+ NBT_DBG("Frame sent to upper layer\n");
+ }
+
+}
+
+static inline void brf6150_rx(struct brf6150_info *info)
+{
+ u8 byte;
+
+ NBT_DBG_TRANSFER("rx_tasklet woke up\ndata ");
+
+ while (brf6150_inb(info, UART_LSR) & UART_LSR_DR) {
+ if (info->rx_skb == NULL) {
+ info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+ if (!info->rx_skb) {
+ printk(KERN_WARNING "brf6150: Can't allocate memory for new packet\n");
+ return;
+ }
+ info->rx_state = WAIT_FOR_PKT_TYPE;
+ info->rx_skb->dev = (void *)info->hdev;
+ brf6150_disable_pm_rx(info);
+ clk_enable(info->uart_ck);
+ }
+
+ byte = brf6150_inb(info, UART_RX);
+ if (info->garbage_bytes) {
+ info->garbage_bytes--;
+ info->hdev->stat.err_rx++;
+ continue;
+ }
+ info->hdev->stat.byte_rx++;
+ NBT_DBG_TRANSFER_NF("0x%.2x ", byte);
+ switch (info->rx_state) {
+ case WAIT_FOR_PKT_TYPE:
+ bt_cb(info->rx_skb)->pkt_type = byte;
+ info->rx_count = brf6150_get_hdr_len(byte);
+ if (info->rx_count >= 0) {
+ info->rx_state = WAIT_FOR_HEADER;
+ } else {
+ info->hdev->stat.err_rx++;
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ clk_disable(info->uart_ck);
+ }
+ break;
+ case WAIT_FOR_HEADER:
+ info->rx_count--;
+ *skb_put(info->rx_skb, 1) = byte;
+ if (info->rx_count == 0) {
+ info->rx_count = brf6150_get_data_len(info, info->rx_skb);
+ if (info->rx_count > skb_tailroom(info->rx_skb)) {
+ printk(KERN_WARNING "brf6150: Frame is %ld bytes too long.\n",
+ info->rx_count - skb_tailroom(info->rx_skb));
+ info->rx_skb = NULL;
+ info->garbage_bytes = info->rx_count - skb_tailroom(info->rx_skb);
+ clk_disable(info->uart_ck);
+ break;
+ }
+ info->rx_state = WAIT_FOR_DATA;
+ if (bt_cb(info->rx_skb)->pkt_type == H4_NEG_PKT) {
+ brf6150_negotiation_packet(info, info->rx_skb);
+ info->rx_skb = NULL;
+ clk_disable(info->uart_ck);
+ return;
+ }
+ if (bt_cb(info->rx_skb)->pkt_type == H4_ALIVE_PKT) {
+ brf6150_alive_packet(info, info->rx_skb);
+ info->rx_skb = NULL;
+ clk_disable(info->uart_ck);
+ return;
+ }
+ }
+ break;
+ case WAIT_FOR_DATA:
+ info->rx_count--;
+ *skb_put(info->rx_skb, 1) = byte;
+ if (info->rx_count == 0) {
+ brf6150_recv_frame(info, info->rx_skb);
+ info->rx_skb = NULL;
+ clk_disable(info->uart_ck);
+ }
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+ }
+
+ NBT_DBG_TRANSFER_NF("\n");
+}
+
+static void brf6150_tx_tasklet(unsigned long data)
+{
+ unsigned int sent = 0;
+ unsigned long flags;
+ struct sk_buff *skb;
+ struct brf6150_info *info = (struct brf6150_info *)data;
+
+ NBT_DBG_TRANSFER("tx_tasklet woke up\n data ");
+
+ skb = skb_dequeue(&info->txq);
+ if (!skb) {
+ /* No data in buffer */
+ brf6150_enable_pm_tx(info);
+ return;
+ }
+
+ /* Copy data to tx fifo */
+ while (!(brf6150_inb(info, UART_OMAP_SSR) & UART_OMAP_SSR_TXFULL) &&
+ (sent < skb->len)) {
+ NBT_DBG_TRANSFER_NF("0x%.2x ", skb->data[sent]);
+ brf6150_outb(info, UART_TX, skb->data[sent]);
+ sent++;
+ }
+
+ info->hdev->stat.byte_tx += sent;
+ NBT_DBG_TRANSFER_NF("\n");
+ if (skb->len == sent) {
+ kfree_skb(skb);
+ clk_disable(info->uart_ck);
+ } else {
+ skb_pull(skb, sent);
+ skb_queue_head(&info->txq, skb);
+ }
+
+ spin_lock_irqsave(&info->lock, flags);
+ brf6150_outb(info, UART_IER, brf6150_inb(info, UART_IER) | UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static irqreturn_t brf6150_interrupt(int irq, void *data)
+{
+ struct brf6150_info *info = (struct brf6150_info *)data;
+ u8 iir, msr;
+ int ret;
+ unsigned long flags;
+
+ ret = IRQ_NONE;
+
+ clk_enable(info->uart_ck);
+ iir = brf6150_inb(info, UART_IIR);
+ if (iir & UART_IIR_NO_INT) {
+ printk("Interrupt but no reason irq 0x%.2x\n", iir);
+ clk_disable(info->uart_ck);
+ return IRQ_HANDLED;
+ }
+
+ NBT_DBG("In interrupt handler iir 0x%.2x\n", iir);
+
+ iir &= UART_IIR_ID;
+
+ if (iir == UART_IIR_MSI) {
+ msr = brf6150_inb(info, UART_MSR);
+ ret = IRQ_HANDLED;
+ }
+ if (iir == UART_IIR_RLSI) {
+ brf6150_inb(info, UART_RX);
+ brf6150_inb(info, UART_LSR);
+ ret = IRQ_HANDLED;
+ }
+
+ if (iir == UART_IIR_RDI) {
+ brf6150_rx(info);
+ ret = IRQ_HANDLED;
+ }
+
+ if (iir == UART_IIR_THRI) {
+ spin_lock_irqsave(&info->lock, flags);
+ brf6150_outb(info, UART_IER, brf6150_inb(info, UART_IER) & ~UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+ tasklet_schedule(&info->tx_task);
+ ret = IRQ_HANDLED;
+ }
+
+ clk_disable(info->uart_ck);
+ return ret;
+}
+
+static irqreturn_t brf6150_wakeup_interrupt(int irq, void *dev_inst)
+{
+ struct brf6150_info *info = dev_inst;
+ int should_wakeup;
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->lock, flags);
+ should_wakeup = omap_get_gpio_datain(info->btinfo->host_wakeup_gpio);
+ NBT_DBG_POWER("gpio interrupt %d\n", should_wakeup);
+ if (should_wakeup) {
+ clk_enable(info->uart_ck);
+ brf6150_set_auto_ctsrts(info, 1);
+ brf6150_rx(info);
+ tasklet_schedule(&info->tx_task);
+ } else {
+ brf6150_set_auto_ctsrts(info, 0);
+ brf6150_set_rts(info, 0);
+ clk_disable(info->uart_ck);
+ }
+
+ spin_unlock_irqrestore(&info->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int brf6150_init_uart(struct brf6150_info *info)
+{
+ int count = 0;
+
+ /* Reset the UART */
+ brf6150_outb(info, UART_OMAP_SYSC, UART_SYSC_OMAP_RESET);
+ while (!(brf6150_inb(info, UART_OMAP_SYSS) & UART_SYSS_RESETDONE)) {
+ if (count++ > 100) {
+ printk(KERN_ERR "brf6150: UART reset timeout\n");
+ return -1;
+ }
+ udelay(1);
+ }
+
+ /* Enable and setup FIFO */
+ brf6150_outb(info, UART_LCR, UART_LCR_WLEN8);
+ brf6150_outb(info, UART_OMAP_MDR1, 0x00); /* Make sure UART mode is enabled */
+ brf6150_outb(info, UART_OMAP_SCR, 0x00);
+ brf6150_outb(info, UART_EFR, brf6150_inb(info, UART_EFR) | UART_EFR_ECB);
+ brf6150_outb(info, UART_MCR, brf6150_inb(info, UART_MCR) | UART_MCR_TCRTLR);
+ brf6150_outb(info, UART_TI752_TLR, 0xff);
+ brf6150_outb(info, UART_TI752_TCR, 0x1f);
+ brf6150_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ brf6150_outb(info, UART_IER, UART_IER_RDI);
+
+ return 0;
+}
+
+static int brf6150_reset(struct brf6150_info *info)
+{
+ omap_set_gpio_dataout(info->btinfo->bt_wakeup_gpio, 0);
+ omap_set_gpio_dataout(info->btinfo->reset_gpio, 0);
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(msecs_to_jiffies(10));
+ omap_set_gpio_dataout(info->btinfo->bt_wakeup_gpio, 1);
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(msecs_to_jiffies(100));
+ omap_set_gpio_dataout(info->btinfo->reset_gpio, 1);
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(msecs_to_jiffies(100));
+
+ return 0;
+}
+
+static int brf6150_send_firmware(struct brf6150_info *info)
+{
+ struct sk_buff *skb;
+
+ init_completion(&info->fw_completion);
+ info->fw_error = 0;
+
+ while ((skb = brf6150_read_fw_cmd(info, GFP_KERNEL)) != NULL) {
+ clk_enable(info->uart_ck);
+ skb_queue_tail(&info->txq, skb);
+ tasklet_schedule(&info->tx_task);
+
+ if (!wait_for_completion_timeout(&info->fw_completion, HZ)) {
+ return -1;
+ }
+
+ if (info->fw_error) {
+ return -1;
+ }
+ }
+ NBT_DBG_FW("Firmware sent\n");
+
+ return 0;
+
+}
+
+/* hci callback functions */
+static int brf6150_hci_flush(struct hci_dev *hdev)
+{
+ struct brf6150_info *info;
+ info = hdev->driver_data;
+
+ skb_queue_purge(&info->txq);
+
+ return 0;
+}
+
+static int brf6150_hci_open(struct hci_dev *hdev)
+{
+ struct brf6150_info *info;
+ int err;
+
+ info = hdev->driver_data;
+
+ if (test_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ if (brf6150_open_firmware(info) < 0) {
+ printk("Cannot open firmware\n");
+ return -1;
+ }
+
+ info->rx_state = WAIT_FOR_PKT_TYPE;
+ info->rx_count = 0;
+ info->garbage_bytes = 0;
+ info->rx_skb = NULL;
+ info->pm_enabled = 0;
+ set_irq_type(OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio), IRQ_TYPE_NONE);
+ init_completion(&info->fw_completion);
+
+ clk_enable(info->uart_ck);
+
+ brf6150_init_uart(info);
+ brf6150_set_auto_ctsrts(info, 0);
+ brf6150_set_rts(info, 0);
+ brf6150_reset(info);
+ brf6150_wait_for_cts(info, 1, 10);
+ brf6150_set_rts(info, 1);
+ if (brf6150_send_negotiation(info)) {
+ brf6150_close_firmware(info);
+ return -1;
+ }
+
+ if (!wait_for_completion_interruptible_timeout(&info->init_completion, HZ)) {
+ brf6150_close_firmware(info);
+ clk_disable(info->uart_ck);
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ return -1;
+ }
+ brf6150_set_auto_ctsrts(info, 1);
+
+ err = brf6150_send_firmware(info);
+ brf6150_close_firmware(info);
+ if (err < 0)
+ printk(KERN_ERR "brf6150: Sending firmware failed. Bluetooth won't work properly\n");
+
+ set_irq_type(OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio), IRQ_TYPE_EDGE_BOTH);
+ info->pm_enabled = 1;
+ set_bit(HCI_RUNNING, &hdev->flags);
+ return 0;
+}
+
+static int brf6150_hci_close(struct hci_dev *hdev)
+{
+ struct brf6150_info *info = hdev->driver_data;
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ brf6150_hci_flush(hdev);
+ clk_disable(info->uart_ck);
+ del_timer_sync(&info->pm_timer);
+ omap_set_gpio_dataout(info->btinfo->bt_wakeup_gpio, 0);
+ set_irq_type(OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio), IRQ_TYPE_NONE);
+
+ return 0;
+}
+
+static void brf6150_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+static int brf6150_hci_send_frame(struct sk_buff *skb)
+{
+ struct brf6150_info *info;
+ struct hci_dev *hdev = (struct hci_dev *)skb->dev;
+
+ if (!hdev) {
+ printk(KERN_WARNING "brf6150: Frame for unknown device\n");
+ return -ENODEV;
+ }
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+ printk(KERN_WARNING "brf6150: Frame for non-running device\n");
+ return -EIO;
+ }
+
+ info = hdev->driver_data;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ };
+
+ /* Push frame type to skb */
+ clk_enable(info->uart_ck);
+ *skb_push(skb, 1) = bt_cb(skb)->pkt_type;
+ skb_queue_tail(&info->txq, skb);
+
+ brf6150_disable_pm_tx(info);
+
+ return 0;
+}
+
+static int brf6150_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+static void brf6150_device_release(struct device *dev)
+{
+}
+
+static int brf6150_register_hdev(struct brf6150_info *info)
+{
+ struct hci_dev *hdev;
+
+ /* Initialize and register HCI device */
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ printk(KERN_WARNING "brf6150: Can't allocate memory for device\n");
+ return -ENOMEM;
+ }
+ info->hdev = hdev;
+
+ hdev->type = HCI_UART;
+ hdev->driver_data = info;
+
+ hdev->open = brf6150_hci_open;
+ hdev->close = brf6150_hci_close;
+ hdev->destruct = brf6150_hci_destruct;
+ hdev->flush = brf6150_hci_flush;
+ hdev->send = brf6150_hci_send_frame;
+ hdev->destruct = brf6150_hci_destruct;
+ hdev->ioctl = brf6150_hci_ioctl;
+
+ hdev->owner = THIS_MODULE;
+
+ if (hci_register_dev(hdev) < 0) {
+ printk(KERN_WARNING "brf6150: Can't register HCI device %s.\n", hdev->name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int __init brf6150_init(void)
+{
+ struct brf6150_info *info;
+ int irq, err;
+
+ info = kmalloc(sizeof(struct brf6150_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ memset(info, 0, sizeof(struct brf6150_info));
+
+ brf6150_device.dev.driver_data = info;
+ init_completion(&info->init_completion);
+ init_completion(&info->fw_completion);
+ info->pm_enabled = 0;
+ info->rx_pm_enabled = 0;
+ info->tx_pm_enabled = 0;
+ info->garbage_bytes = 0;
+ tasklet_init(&info->tx_task, brf6150_tx_tasklet, (unsigned long)info);
+ spin_lock_init(&info->lock);
+ skb_queue_head_init(&info->txq);
+ init_timer(&info->pm_timer);
+ info->pm_timer.function = brf6150_pm_timer;
+ info->pm_timer.data = (unsigned long)info;
+ exit_info = NULL;
+
+ info->btinfo = omap_get_config(OMAP_TAG_NOKIA_BT, struct omap_bluetooth_config);
+ if (info->btinfo == NULL)
+ return -1;
+
+ NBT_DBG("RESET gpio: %d\n", info->btinfo->reset_gpio);
+ NBT_DBG("BTWU gpio: %d\n", info->btinfo->bt_wakeup_gpio);
+ NBT_DBG("HOSTWU gpio: %d\n", info->btinfo->host_wakeup_gpio);
+ NBT_DBG("Uart: %d\n", info->btinfo->bt_uart);
+ NBT_DBG("sysclk: %d\n", info->btinfo->bt_sysclk);
+
+ err = omap_request_gpio(info->btinfo->reset_gpio);
+ if (err < 0)
+ {
+ printk(KERN_WARNING "Cannot get GPIO line %d",
+ info->btinfo->reset_gpio);
+ kfree(info);
+ return err;
+ }
+
+ err = omap_request_gpio(info->btinfo->bt_wakeup_gpio);
+ if (err < 0)
+ {
+ printk(KERN_WARNING "Cannot get GPIO line 0x%d",
+ info->btinfo->bt_wakeup_gpio);
+ omap_free_gpio(info->btinfo->reset_gpio);
+ kfree(info);
+ return err;
+ }
+
+ err = omap_request_gpio(info->btinfo->host_wakeup_gpio);
+ if (err < 0)
+ {
+ printk(KERN_WARNING "Cannot get GPIO line %d",
+ info->btinfo->host_wakeup_gpio);
+ omap_free_gpio(info->btinfo->reset_gpio);
+ omap_free_gpio(info->btinfo->bt_wakeup_gpio);
+ kfree(info);
+ return err;
+ }
+
+ omap_set_gpio_direction(info->btinfo->reset_gpio, 0);
+ omap_set_gpio_direction(info->btinfo->bt_wakeup_gpio, 0);
+ omap_set_gpio_direction(info->btinfo->host_wakeup_gpio, 1);
+ set_irq_type(OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio), IRQ_TYPE_NONE);
+
+ switch (info->btinfo->bt_uart) {
+ case 1:
+ irq = INT_UART1;
+ info->uart_ck = clk_get(NULL, "uart1_ck");
+ info->uart_base = io_p2v((unsigned long)OMAP_UART1_BASE);
+ break;
+ case 2:
+ irq = INT_UART2;
+ info->uart_ck = clk_get(NULL, "uart2_ck");
+ info->uart_base = io_p2v((unsigned long)OMAP_UART2_BASE);
+ break;
+ case 3:
+ irq = INT_UART3;
+ info->uart_ck = clk_get(NULL, "uart3_ck");
+ info->uart_base = io_p2v((unsigned long)OMAP_UART3_BASE);
+ break;
+ default:
+ printk(KERN_ERR "No uart defined\n");
+ goto cleanup;
+ }
+
+ info->irq = irq;
+ err = request_irq(irq, brf6150_interrupt, 0, "brf6150", (void *)info);
+ if (err < 0) {
+ printk(KERN_ERR "brf6150: unable to get IRQ %d\n", irq);
+ goto cleanup;
+ }
+
+ err = request_irq(OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio),
+ brf6150_wakeup_interrupt, 0, "brf6150_wkup", (void *)info);
+ if (err < 0) {
+ printk(KERN_ERR "brf6150: unable to get wakeup IRQ %d\n",
+ OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio));
+ free_irq(irq, (void *)info);
+ goto cleanup;
+ }
+
+ /* Register with LDM */
+ if (platform_device_register(&brf6150_device)) {
+ printk(KERN_ERR "failed to register brf6150 device\n");
+ err = -ENODEV;
+ goto cleanup_irq;
+ }
+ /* Register the driver with LDM */
+ if (driver_register(&brf6150_driver)) {
+ printk(KERN_WARNING "failed to register brf6150 driver\n");
+ platform_device_unregister(&brf6150_device);
+ err = -ENODEV;
+ goto cleanup_irq;
+ }
+
+ if (brf6150_register_hdev(info) < 0) {
+ printk(KERN_WARNING "failed to register brf6150 hci device\n");
+ platform_device_unregister(&brf6150_device);
+ driver_unregister(&brf6150_driver);
+ goto cleanup_irq;
+ }
+
+ exit_info = info;
+ return 0;
+
+cleanup_irq:
+ free_irq(irq, (void *)info);
+ free_irq(OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio), (void *)info);
+cleanup:
+ omap_free_gpio(info->btinfo->reset_gpio);
+ omap_free_gpio(info->btinfo->bt_wakeup_gpio);
+ omap_free_gpio(info->btinfo->host_wakeup_gpio);
+ kfree(info);
+
+ return err;
+}
+
+static void __exit brf6150_exit(void)
+{
+ brf6150_hci_close(exit_info->hdev);
+ hci_free_dev(exit_info->hdev);
+ omap_free_gpio(exit_info->btinfo->reset_gpio);
+ omap_free_gpio(exit_info->btinfo->bt_wakeup_gpio);
+ omap_free_gpio(exit_info->btinfo->host_wakeup_gpio);
+ free_irq(exit_info->irq, (void *)exit_info);
+ free_irq(OMAP_GPIO_IRQ(exit_info->btinfo->host_wakeup_gpio), (void *)exit_info);
+ kfree(exit_info);
+}
+
+module_init(brf6150_init);
+module_exit(brf6150_exit);
+
+MODULE_DESCRIPTION("brf6150 hci driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ville Tervo <ville.tervo@nokia.com>");
--- /dev/null
- #include <asm/arch/board.h>
+/*
+ * linux/drivers/bluetooth/brf6150/brf6150.h
+ *
+ * Copyright (C) 2005 Nokia Corporation
+ * Written by Ville Tervo <ville.tervo@nokia.com>
+ *
+ * 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 <mach/board.h>
+
+#ifndef __DRIVERS_BLUETOOTH_BRF6150_H
+#define __DRIVERS_BLUETOOTH_BRF6150_H
+
+#define UART_SYSC_OMAP_RESET 0x02
+#define UART_SYSS_RESETDONE 0x01
+#define UART_OMAP_SCR_EMPTY_THR 0x08
+#define UART_OMAP_SCR_WAKEUP 0x10
+#define UART_OMAP_SSR_WAKEUP 0x02
+#define UART_OMAP_SSR_TXFULL 0x01
+
+struct brf6150_info {
+ struct hci_dev *hdev;
+ spinlock_t lock;
+
+ struct clk *uart_ck;
+ unsigned long uart_base;
+ unsigned int irq;
+
+ struct sk_buff_head txq;
+ struct sk_buff *rx_skb;
+ const struct omap_bluetooth_config *btinfo;
+ const struct firmware *fw_entry;
+ int fw_pos;
+ int fw_error;
+ struct completion fw_completion;
+ struct completion init_completion;
+ struct tasklet_struct tx_task;
+ long rx_count;
+ unsigned long garbage_bytes;
+ unsigned long rx_state;
+ int pm_enabled;
+ int rx_pm_enabled;
+ int tx_pm_enabled;
+ struct timer_list pm_timer;
+};
+
+#define BT_DEVICE "nokia_btuart"
+#define BT_DRIVER "nokia_btuart"
+
+#define MAX_BAUD_RATE 921600
+#define UART_CLOCK 48000000
+#define BT_INIT_DIVIDER 320
+#define BT_BAUDRATE_DIVIDER 384000000
+#define BT_SYSCLK_DIV 1000
+#define INIT_SPEED 120000
+
+#define H4_TYPE_SIZE 1
+
+/* H4+ packet types */
+#define H4_CMD_PKT 0x01
+#define H4_ACL_PKT 0x02
+#define H4_SCO_PKT 0x03
+#define H4_EVT_PKT 0x04
+#define H4_NEG_PKT 0x06
+#define H4_ALIVE_PKT 0x07
+
+/* TX states */
+#define WAIT_FOR_PKT_TYPE 1
+#define WAIT_FOR_HEADER 2
+#define WAIT_FOR_DATA 3
+
+struct hci_fw_event {
+ struct hci_event_hdr hev;
+ struct hci_ev_cmd_complete cmd;
+ __u8 status;
+} __attribute__ ((packed));
+
+#endif /* __DRIVERS_BLUETOOTH_BRF6150_H */
--- /dev/null
- #include <asm/arch/gpio.h>
- #include <asm/arch/board.h>
+/*
+ * drivers/cbus/cbus.c
+ *
+ * Support functions for CBUS serial protocol
+ *
+ * Copyright (C) 2004, 2005 Nokia Corporation
+ *
+ * Written by Juha Yrjölä <juha.yrjola@nokia.com>,
+ * David Weinehall <david.weinehall@nokia.com>, and
+ * Mikko Ylinen <mikko.k.ylinen@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+
++#include <mach/gpio.h>
++#include <mach/board.h>
+
+#include <asm/io.h>
+
+#include "cbus.h"
+
+struct cbus_host *cbus_host = NULL;
+
+#ifdef CONFIG_ARCH_OMAP1
+/* We use our own MPUIO functions to get closer to 1MHz bus speed */
+
+static inline void cbus_set_gpio_direction(u32 base, int mpuio, int is_input)
+{
+ u16 w;
+
+ mpuio &= 0x0f;
+ w = __raw_readw(base + OMAP_MPUIO_IO_CNTL);
+ if (is_input)
+ w |= 1 << mpuio;
+ else
+ w &= ~(1 << mpuio);
+ __raw_writew(w, base + OMAP_MPUIO_IO_CNTL);
+
+}
+
+static inline void cbus_set_gpio_dataout(u32 base, int mpuio, int enable)
+{
+ u16 w;
+
+ mpuio &= 0x0f;
+ w = __raw_readw(base + OMAP_MPUIO_OUTPUT);
+ if (enable)
+ w |= 1 << mpuio;
+ else
+ w &= ~(1 << mpuio);
+ __raw_writew(w, base + OMAP_MPUIO_OUTPUT);
+}
+
+static inline int cbus_get_gpio_datain(u32 base, int mpuio)
+{
+ mpuio &= 0x0f;
+
+ return (__raw_readw(base + OMAP_MPUIO_INPUT_LATCH) & (1 << mpuio)) != 0;
+}
+
+static void cbus_send_bit(struct cbus_host *host, u32 base, int bit,
+ int set_to_input)
+{
+ cbus_set_gpio_dataout(base, host->dat_gpio, bit ? 1 : 0);
+ cbus_set_gpio_dataout(base, host->clk_gpio, 1);
+
+ /* The data bit is read on the rising edge of CLK */
+ if (set_to_input)
+ cbus_set_gpio_direction(base, host->dat_gpio, 1);
+
+ cbus_set_gpio_dataout(base, host->clk_gpio, 0);
+}
+
+static u8 cbus_receive_bit(struct cbus_host *host, u32 base)
+{
+ u8 ret;
+
+ cbus_set_gpio_dataout(base, host->clk_gpio, 1);
+ ret = cbus_get_gpio_datain(base, host->dat_gpio);
+ cbus_set_gpio_dataout(base, host->clk_gpio, 0);
+
+ return ret;
+}
+
+#else
+
+#define cbus_set_gpio_direction(base, gpio, is_input) omap_set_gpio_direction(gpio, is_input)
+#define cbus_set_gpio_dataout(base, gpio, enable) omap_set_gpio_dataout(gpio, enable)
+#define cbus_get_gpio_datain(base, int, gpio) omap_get_gpio_datain(gpio)
+
+static void _cbus_send_bit(struct cbus_host *host, int bit, int set_to_input)
+{
+ omap_set_gpio_dataout(host->dat_gpio, bit ? 1 : 0);
+ omap_set_gpio_dataout(host->clk_gpio, 1);
+
+ /* The data bit is read on the rising edge of CLK */
+ if (set_to_input)
+ omap_set_gpio_direction(host->dat_gpio, 1);
+
+ omap_set_gpio_dataout(host->clk_gpio, 0);
+}
+
+static u8 _cbus_receive_bit(struct cbus_host *host)
+{
+ u8 ret;
+
+ omap_set_gpio_dataout(host->clk_gpio, 1);
+ ret = omap_get_gpio_datain(host->dat_gpio);
+ omap_set_gpio_dataout(host->clk_gpio, 0);
+
+ return ret;
+}
+
+#define cbus_send_bit(host, base, bit, set_to_input) _cbus_send_bit(host, bit, set_to_input)
+#define cbus_receive_bit(host, base) _cbus_receive_bit(host)
+
+#endif
+
+static int cbus_transfer(struct cbus_host *host, int dev, int reg, int data)
+{
+ int i;
+ int is_read = 0;
+ unsigned long flags;
+ u32 base;
+
+#ifdef CONFIG_ARCH_OMAP1
+ base = (u32) io_p2v(OMAP_MPUIO_BASE);
+#else
+ base = 0;
+#endif
+
+ if (data < 0)
+ is_read = 1;
+
+ /* We don't want interrupts disturbing our transfer */
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Reset state and start of transfer, SEL stays down during transfer */
+ cbus_set_gpio_dataout(base, host->sel_gpio, 0);
+
+ /* Set the DAT pin to output */
+ cbus_set_gpio_direction(base, host->dat_gpio, 0);
+
+ /* Send the device address */
+ for (i = 3; i > 0; i--)
+ cbus_send_bit(host, base, dev & (1 << (i - 1)), 0);
+
+ /* Send the rw flag */
+ cbus_send_bit(host, base, is_read, 0);
+
+ /* Send the register address */
+ for (i = 5; i > 0; i--) {
+ int set_to_input = 0;
+
+ if (is_read && i == 1)
+ set_to_input = 1;
+
+ cbus_send_bit(host, base, reg & (1 << (i - 1)), set_to_input);
+ }
+
+ if (!is_read) {
+ for (i = 16; i > 0; i--)
+ cbus_send_bit(host, base, data & (1 << (i - 1)), 0);
+ } else {
+ cbus_set_gpio_dataout(base, host->clk_gpio, 1);
+ data = 0;
+
+ for (i = 16; i > 0; i--) {
+ u8 bit = cbus_receive_bit(host, base);
+
+ if (bit)
+ data |= 1 << (i - 1);
+ }
+ }
+
+ /* Indicate end of transfer, SEL goes up until next transfer */
+ cbus_set_gpio_dataout(base, host->sel_gpio, 1);
+ cbus_set_gpio_dataout(base, host->clk_gpio, 1);
+ cbus_set_gpio_dataout(base, host->clk_gpio, 0);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return is_read ? data : 0;
+}
+
+/*
+ * Read a given register from the device
+ */
+int cbus_read_reg(struct cbus_host *host, int dev, int reg)
+{
+ return cbus_host ? cbus_transfer(host, dev, reg, -1) : -ENODEV;
+}
+
+/*
+ * Write to a given register of the device
+ */
+int cbus_write_reg(struct cbus_host *host, int dev, int reg, u16 val)
+{
+ return cbus_host ? cbus_transfer(host, dev, reg, (int)val) : -ENODEV;
+}
+
+int __init cbus_bus_init(void)
+{
+ const struct omap_cbus_config * cbus_config;
+ struct cbus_host *chost;
+ int ret;
+
+ chost = kmalloc(sizeof (*chost), GFP_KERNEL);
+ if (chost == NULL)
+ return -ENOMEM;
+
+ memset(chost, 0, sizeof (*chost));
+
+ spin_lock_init(&chost->lock);
+
+ cbus_config = omap_get_config(OMAP_TAG_CBUS, struct omap_cbus_config);
+
+ if (cbus_config == NULL) {
+ printk(KERN_ERR "cbus: Unable to retrieve config data\n");
+ return -ENODATA;
+ }
+
+ chost->clk_gpio = cbus_config->clk_gpio;
+ chost->dat_gpio = cbus_config->dat_gpio;
+ chost->sel_gpio = cbus_config->sel_gpio;
+
+#ifdef CONFIG_ARCH_OMAP1
+ if (!OMAP_GPIO_IS_MPUIO(chost->clk_gpio) ||
+ !OMAP_GPIO_IS_MPUIO(chost->dat_gpio) ||
+ !OMAP_GPIO_IS_MPUIO(chost->sel_gpio)) {
+ printk(KERN_ERR "cbus: Only MPUIO pins supported\n");
+ ret = -ENODEV;
+ goto exit1;
+ }
+#endif
+
+ if ((ret = omap_request_gpio(chost->clk_gpio)) < 0)
+ goto exit1;
+
+ if ((ret = omap_request_gpio(chost->dat_gpio)) < 0)
+ goto exit2;
+
+ if ((ret = omap_request_gpio(chost->sel_gpio)) < 0)
+ goto exit3;
+
+ omap_set_gpio_dataout(chost->clk_gpio, 0);
+ omap_set_gpio_dataout(chost->sel_gpio, 1);
+
+ omap_set_gpio_direction(chost->clk_gpio, 0);
+ omap_set_gpio_direction(chost->dat_gpio, 1);
+ omap_set_gpio_direction(chost->sel_gpio, 0);
+
+ omap_set_gpio_dataout(chost->clk_gpio, 1);
+ omap_set_gpio_dataout(chost->clk_gpio, 0);
+
+ cbus_host = chost;
+
+ return 0;
+exit3:
+ omap_free_gpio(chost->dat_gpio);
+exit2:
+ omap_free_gpio(chost->clk_gpio);
+exit1:
+ kfree(chost);
+ return ret;
+}
+
+subsys_initcall(cbus_bus_init);
+
+EXPORT_SYMBOL(cbus_host);
+EXPORT_SYMBOL(cbus_read_reg);
+EXPORT_SYMBOL(cbus_write_reg);
+
+MODULE_DESCRIPTION("CBUS serial protocol");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Juha Yrjölä, David Weinehall, and Mikko Ylinen");
--- /dev/null
- #include <asm/arch/mux.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/board.h>
+/**
+ * drivers/cbus/retu.c
+ *
+ * Support functions for Retu ASIC
+ *
+ * Copyright (C) 2004, 2005 Nokia Corporation
+ *
+ * Written by Juha Yrjölä <juha.yrjola@nokia.com>,
+ * David Weinehall <david.weinehall@nokia.com>, and
+ * Mikko Ylinen <mikko.k.ylinen@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/init.h>
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <asm/uaccess.h>
+
++#include <mach/mux.h>
++#include <mach/gpio.h>
++#include <mach/board.h>
+
+#include "cbus.h"
+#include "retu.h"
+
+#define RETU_ID 0x01
+#define PFX "retu: "
+
+static int retu_initialized;
+static int retu_irq_pin;
+static int retu_is_vilma;
+
+static struct tasklet_struct retu_tasklet;
+spinlock_t retu_lock = SPIN_LOCK_UNLOCKED;
+
+static struct completion device_release;
+
+struct retu_irq_handler_desc {
+ int (*func)(unsigned long);
+ unsigned long arg;
+ char name[8];
+};
+
+static struct retu_irq_handler_desc retu_irq_handlers[MAX_RETU_IRQ_HANDLERS];
+
+/**
+ * retu_read_reg - Read a value from a register in Retu
+ * @reg: the register to read from
+ *
+ * This function returns the contents of the specified register
+ */
+int retu_read_reg(int reg)
+{
+ BUG_ON(!retu_initialized);
+ return cbus_read_reg(cbus_host, RETU_ID, reg);
+}
+
+/**
+ * retu_write_reg - Write a value to a register in Retu
+ * @reg: the register to write to
+ * @reg: the value to write to the register
+ *
+ * This function writes a value to the specified register
+ */
+void retu_write_reg(int reg, u16 val)
+{
+ BUG_ON(!retu_initialized);
+ cbus_write_reg(cbus_host, RETU_ID, reg, val);
+}
+
+void retu_set_clear_reg_bits(int reg, u16 set, u16 clear)
+{
+ unsigned long flags;
+ u16 w;
+
+ spin_lock_irqsave(&retu_lock, flags);
+ w = retu_read_reg(reg);
+ w &= ~clear;
+ w |= set;
+ retu_write_reg(reg, w);
+ spin_unlock_irqrestore(&retu_lock, flags);
+}
+
+#define ADC_MAX_CHAN_NUMBER 13
+
+int retu_read_adc(int channel)
+{
+ unsigned long flags;
+ int res;
+
+ if (channel < 0 || channel > ADC_MAX_CHAN_NUMBER)
+ return -EINVAL;
+
+ spin_lock_irqsave(&retu_lock, flags);
+
+ if ((channel == 8) && retu_is_vilma) {
+ int scr = retu_read_reg(RETU_REG_ADCSCR);
+ int ch = (retu_read_reg(RETU_REG_ADCR) >> 10) & 0xf;
+ if (((scr & 0xff) != 0) && (ch != 8))
+ retu_write_reg (RETU_REG_ADCSCR, (scr & ~0xff));
+ }
+
+ /* Select the channel and read result */
+ retu_write_reg(RETU_REG_ADCR, channel << 10);
+ res = retu_read_reg(RETU_REG_ADCR) & 0x3ff;
+
+ if (retu_is_vilma)
+ retu_write_reg(RETU_REG_ADCR, (1 << 13));
+
+ /* Unlock retu */
+ spin_unlock_irqrestore(&retu_lock, flags);
+
+ return res;
+}
+
+
+static u16 retu_disable_bogus_irqs(u16 mask)
+{
+ int i;
+
+ for (i = 0; i < MAX_RETU_IRQ_HANDLERS; i++) {
+ if (mask & (1 << i))
+ continue;
+ if (retu_irq_handlers[i].func != NULL)
+ continue;
+ /* an IRQ was enabled but we don't have a handler for it */
+ printk(KERN_INFO PFX "disabling bogus IRQ %d\n", i);
+ mask |= (1 << i);
+ }
+ return mask;
+}
+
+/*
+ * Disable given RETU interrupt
+ */
+void retu_disable_irq(int id)
+{
+ unsigned long flags;
+ u16 mask;
+
+ spin_lock_irqsave(&retu_lock, flags);
+ mask = retu_read_reg(RETU_REG_IMR);
+ mask |= 1 << id;
+ mask = retu_disable_bogus_irqs(mask);
+ retu_write_reg(RETU_REG_IMR, mask);
+ spin_unlock_irqrestore(&retu_lock, flags);
+}
+
+/*
+ * Enable given RETU interrupt
+ */
+void retu_enable_irq(int id)
+{
+ unsigned long flags;
+ u16 mask;
+
+ if (id == 3) {
+ printk("Enabling Retu IRQ %d\n", id);
+ dump_stack();
+ }
+ spin_lock_irqsave(&retu_lock, flags);
+ mask = retu_read_reg(RETU_REG_IMR);
+ mask &= ~(1 << id);
+ mask = retu_disable_bogus_irqs(mask);
+ retu_write_reg(RETU_REG_IMR, mask);
+ spin_unlock_irqrestore(&retu_lock, flags);
+}
+
+/*
+ * Acknowledge given RETU interrupt
+ */
+void retu_ack_irq(int id)
+{
+ retu_write_reg(RETU_REG_IDR, 1 << id);
+}
+
+/*
+ * RETU interrupt handler. Only schedules the tasklet.
+ */
+static irqreturn_t retu_irq_handler(int irq, void *dev_id)
+{
+ tasklet_schedule(&retu_tasklet);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Tasklet handler
+ */
+static void retu_tasklet_handler(unsigned long data)
+{
+ struct retu_irq_handler_desc *hnd;
+ u16 id;
+ u16 im;
+ int i;
+
+ for (;;) {
+ id = retu_read_reg(RETU_REG_IDR);
+ im = ~retu_read_reg(RETU_REG_IMR);
+ id &= im;
+
+ if (!id)
+ break;
+
+ for (i = 0; id != 0; i++, id >>= 1) {
+ if (!(id & 1))
+ continue;
+ hnd = &retu_irq_handlers[i];
+ if (hnd->func == NULL) {
+ /* Spurious retu interrupt - disable and ack it */
+ printk(KERN_INFO "Spurious Retu interrupt "
+ "(id %d)\n", i);
+ retu_disable_irq(i);
+ retu_ack_irq(i);
+ continue;
+ }
+ hnd->func(hnd->arg);
+ /*
+ * Don't acknowledge the interrupt here
+ * It must be done explicitly
+ */
+ }
+ }
+}
+
+/*
+ * Register the handler for a given RETU interrupt source.
+ */
+int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name)
+{
+ struct retu_irq_handler_desc *hnd;
+
+ if (irq_handler == NULL || id >= MAX_RETU_IRQ_HANDLERS ||
+ name == NULL) {
+ printk(KERN_ERR PFX "Invalid arguments to %s\n",
+ __FUNCTION__);
+ return -EINVAL;
+ }
+ hnd = &retu_irq_handlers[id];
+ if (hnd->func != NULL) {
+ printk(KERN_ERR PFX "IRQ %d already reserved\n", id);
+ return -EBUSY;
+ }
+ printk(KERN_INFO PFX "Registering interrupt %d for device %s\n",
+ id, name);
+ hnd->func = irq_handler;
+ hnd->arg = arg;
+ strlcpy(hnd->name, name, sizeof(hnd->name));
+
+ retu_ack_irq(id);
+ retu_enable_irq(id);
+
+ return 0;
+}
+
+/*
+ * Unregister the handler for a given RETU interrupt source.
+ */
+void retu_free_irq(int id)
+{
+ struct retu_irq_handler_desc *hnd;
+
+ if (id >= MAX_RETU_IRQ_HANDLERS) {
+ printk(KERN_ERR PFX "Invalid argument to %s\n",
+ __FUNCTION__);
+ return;
+ }
+ hnd = &retu_irq_handlers[id];
+ if (hnd->func == NULL) {
+ printk(KERN_ERR PFX "IRQ %d already freed\n", id);
+ return;
+ }
+
+ retu_disable_irq(id);
+ hnd->func = NULL;
+}
+
+/**
+ * retu_power_off - Shut down power to system
+ *
+ * This function puts the system in power off state
+ */
+static void retu_power_off(void)
+{
+ /* Ignore power button state */
+ retu_write_reg(RETU_REG_CC1, retu_read_reg(RETU_REG_CC1) | 2);
+ /* Expire watchdog immediately */
+ retu_write_reg(RETU_REG_WATCHDOG, 0);
+ /* Wait for poweroff*/
+ for (;;);
+}
+
+/**
+ * retu_probe - Probe for Retu ASIC
+ * @dev: the Retu device
+ *
+ * Probe for the Retu ASIC and allocate memory
+ * for its device-struct if found
+ */
+static int __devinit retu_probe(struct device *dev)
+{
+ const struct omap_em_asic_bb5_config * em_asic_config;
+ int rev, ret;
+
+ /* Prepare tasklet */
+ tasklet_init(&retu_tasklet, retu_tasklet_handler, 0);
+
+ em_asic_config = omap_get_config(OMAP_TAG_EM_ASIC_BB5,
+ struct omap_em_asic_bb5_config);
+ if (em_asic_config == NULL) {
+ printk(KERN_ERR PFX "Unable to retrieve config data\n");
+ return -ENODATA;
+ }
+
+ retu_irq_pin = em_asic_config->retu_irq_gpio;
+
+ if ((ret = omap_request_gpio(retu_irq_pin)) < 0) {
+ printk(KERN_ERR PFX "Unable to reserve IRQ GPIO\n");
+ return ret;
+ }
+
+ /* Set the pin as input */
+ omap_set_gpio_direction(retu_irq_pin, 1);
+
+ /* Rising edge triggers the IRQ */
+ set_irq_type(OMAP_GPIO_IRQ(retu_irq_pin), IRQ_TYPE_EDGE_RISING);
+
+ retu_initialized = 1;
+
+ rev = retu_read_reg(RETU_REG_ASICR) & 0xff;
+ if (rev & (1 << 7))
+ retu_is_vilma = 1;
+
+ printk(KERN_INFO "%s v%d.%d found\n", retu_is_vilma ? "Vilma" : "Retu",
+ (rev >> 4) & 0x07, rev & 0x0f);
+
+ /* Mask all RETU interrupts */
+ retu_write_reg(RETU_REG_IMR, 0xffff);
+
+ ret = request_irq(OMAP_GPIO_IRQ(retu_irq_pin), retu_irq_handler, 0,
+ "retu", 0);
+ if (ret < 0) {
+ printk(KERN_ERR PFX "Unable to register IRQ handler\n");
+ omap_free_gpio(retu_irq_pin);
+ return ret;
+ }
+ set_irq_wake(OMAP_GPIO_IRQ(retu_irq_pin), 1);
+
+ /* Register power off function */
+ pm_power_off = retu_power_off;
+
+#ifdef CONFIG_CBUS_RETU_USER
+ /* Initialize user-space interface */
+ if (retu_user_init() < 0) {
+ printk(KERN_ERR "Unable to initialize driver\n");
+ free_irq(OMAP_GPIO_IRQ(retu_irq_pin), 0);
+ omap_free_gpio(retu_irq_pin);
+ return ret;
+ }
+#endif
+
+ return 0;
+}
+
+static int retu_remove(struct device *dev)
+{
+#ifdef CONFIG_CBUS_RETU_USER
+ retu_user_cleanup();
+#endif
+ /* Mask all RETU interrupts */
+ retu_write_reg(RETU_REG_IMR, 0xffff);
+ free_irq(OMAP_GPIO_IRQ(retu_irq_pin), 0);
+ omap_free_gpio(retu_irq_pin);
+ tasklet_kill(&retu_tasklet);
+
+ return 0;
+}
+
+static void retu_device_release(struct device *dev)
+{
+ complete(&device_release);
+}
+
+static struct device_driver retu_driver = {
+ .name = "retu",
+ .bus = &platform_bus_type,
+ .probe = retu_probe,
+ .remove = retu_remove,
+};
+
+static struct platform_device retu_device = {
+ .name = "retu",
+ .id = -1,
+ .dev = {
+ .release = retu_device_release,
+ }
+};
+
+/**
+ * retu_init - initialise Retu driver
+ *
+ * Initialise the Retu driver and return 0 if everything worked ok
+ */
+static int __init retu_init(void)
+{
+ int ret = 0;
+
+ printk(KERN_INFO "Retu/Vilma driver initialising\n");
+
+ init_completion(&device_release);
+
+ if ((ret = driver_register(&retu_driver)) < 0)
+ return ret;
+
+ if ((ret = platform_device_register(&retu_device)) < 0) {
+ driver_unregister(&retu_driver);
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * Cleanup
+ */
+static void __exit retu_exit(void)
+{
+ platform_device_unregister(&retu_device);
+ driver_unregister(&retu_driver);
+ wait_for_completion(&device_release);
+}
+
+EXPORT_SYMBOL(retu_request_irq);
+EXPORT_SYMBOL(retu_free_irq);
+EXPORT_SYMBOL(retu_enable_irq);
+EXPORT_SYMBOL(retu_disable_irq);
+EXPORT_SYMBOL(retu_ack_irq);
+EXPORT_SYMBOL(retu_read_reg);
+EXPORT_SYMBOL(retu_write_reg);
+
+subsys_initcall(retu_init);
+module_exit(retu_exit);
+
+MODULE_DESCRIPTION("Retu ASIC control");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Juha Yrjölä, David Weinehall, and Mikko Ylinen");
--- /dev/null
- #include <asm/arch/usb.h>
+/**
+ * drivers/cbus/tahvo-usb.c
+ *
+ * Tahvo USB transeiver
+ *
+ * Copyright (C) 2005-2006 Nokia Corporation
+ *
+ * Parts copied from drivers/i2c/chips/isp1301_omap.c
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2004 David Brownell
+ *
+ * Written by Juha Yrjölä <juha.yrjola@nokia.com>,
+ * Tony Lindgren <tony@atomide.com>, and
+ * Timo Teräs <timo.teras@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+
+#include <asm/irq.h>
++#include <mach/usb.h>
+
+#include "cbus.h"
+#include "tahvo.h"
+
+#define DRIVER_NAME "tahvo-usb"
+
+#define USBR_SLAVE_CONTROL (1 << 8)
+#define USBR_VPPVIO_SW (1 << 7)
+#define USBR_SPEED (1 << 6)
+#define USBR_REGOUT (1 << 5)
+#define USBR_MASTER_SW2 (1 << 4)
+#define USBR_MASTER_SW1 (1 << 3)
+#define USBR_SLAVE_SW (1 << 2)
+#define USBR_NSUSPEND (1 << 1)
+#define USBR_SEMODE (1 << 0)
+
+/* bits in OTG_CTRL */
+
+/* Bits that are controlled by OMAP OTG and are read-only */
+#define OTG_CTRL_OMAP_MASK (OTG_PULLDOWN|OTG_PULLUP|OTG_DRV_VBUS|\
+ OTG_PD_VBUS|OTG_PU_VBUS|OTG_PU_ID)
+/* Bits that are controlled by transceiver */
+#define OTG_CTRL_XCVR_MASK (OTG_ASESSVLD|OTG_BSESSEND|\
+ OTG_BSESSVLD|OTG_VBUSVLD|OTG_ID)
+/* Bits that are controlled by system */
+#define OTG_CTRL_SYS_MASK (OTG_A_BUSREQ|OTG_A_SETB_HNPEN|OTG_B_BUSREQ|\
+ OTG_B_HNPEN|OTG_BUSDROP)
+
+#if defined(CONFIG_USB_OHCI_HCD) && !defined(CONFIG_USB_OTG)
+#error tahvo-otg.c does not work with OCHI yet!
+#endif
+
+#define TAHVO_MODE_HOST 0
+#define TAHVO_MODE_PERIPHERAL 1
+
+#ifdef CONFIG_USB_OTG
+#define TAHVO_MODE(tu) (tu)->tahvo_mode
+#elif defined(CONFIG_USB_GADGET_OMAP)
+#define TAHVO_MODE(tu) TAHVO_MODE_PERIPHERAL
+#else
+#define TAHVO_MODE(tu) TAHVO_MODE_HOST
+#endif
+
+struct tahvo_usb {
+ struct platform_device *pt_dev;
+ struct otg_transceiver otg;
+ int vbus_state;
+ struct work_struct irq_work;
+ struct mutex serialize;
+#ifdef CONFIG_USB_OTG
+ int tahvo_mode;
+#endif
+};
+static struct platform_device tahvo_usb_device;
+
+/*
+ * ---------------------------------------------------------------------------
+ * OTG related functions
+ *
+ * These shoud be separated into omap-otg.c driver module, as they are used
+ * by various transceivers. These functions are needed in the UDC-only case
+ * as well. These functions are copied from GPL isp1301_omap.c
+ * ---------------------------------------------------------------------------
+ */
+static struct platform_device *tahvo_otg_dev;
+
+static irqreturn_t omap_otg_irq(int irq, void *arg)
+{
+ struct platform_device *otg_dev = (struct platform_device *) arg;
+ struct tahvo_usb *tu = (struct tahvo_usb *) otg_dev->dev.driver_data;
+ u16 otg_irq;
+
+ otg_irq = omap_readw(OTG_IRQ_SRC);
+ if (otg_irq & OPRT_CHG) {
+ omap_writew(OPRT_CHG, OTG_IRQ_SRC);
+ } else if (otg_irq & B_SRP_TMROUT) {
+ omap_writew(B_SRP_TMROUT, OTG_IRQ_SRC);
+ } else if (otg_irq & B_HNP_FAIL) {
+ omap_writew(B_HNP_FAIL, OTG_IRQ_SRC);
+ } else if (otg_irq & A_SRP_DETECT) {
+ omap_writew(A_SRP_DETECT, OTG_IRQ_SRC);
+ } else if (otg_irq & A_REQ_TMROUT) {
+ omap_writew(A_REQ_TMROUT, OTG_IRQ_SRC);
+ } else if (otg_irq & A_VBUS_ERR) {
+ omap_writew(A_VBUS_ERR, OTG_IRQ_SRC);
+ } else if (otg_irq & DRIVER_SWITCH) {
+ if ((!(omap_readl(OTG_CTRL) & OTG_DRIVER_SEL)) &&
+ tu->otg.host && tu->otg.state == OTG_STATE_A_HOST) {
+ /* role is host */
+ usb_bus_start_enum(tu->otg.host,
+ tu->otg.host->otg_port);
+ }
+ omap_writew(DRIVER_SWITCH, OTG_IRQ_SRC);
+ } else
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+
+}
+
+static int omap_otg_init(void)
+{
+ u32 l;
+
+#ifdef CONFIG_USB_OTG
+ if (!tahvo_otg_dev) {
+ printk("tahvo-usb: no tahvo_otg_dev\n");
+ return -ENODEV;
+ }
+#endif
+
+ l = omap_readl(OTG_SYSCON_1);
+ l &= ~OTG_IDLE_EN;
+ omap_writel(l, OTG_SYSCON_1);
+ udelay(100);
+
+ /* some of these values are board-specific... */
+ l = omap_readl(OTG_SYSCON_2);
+ l |= OTG_EN
+ /* for B-device: */
+ | SRP_GPDATA /* 9msec Bdev D+ pulse */
+ | SRP_GPDVBUS /* discharge after VBUS pulse */
+ // | (3 << 24) /* 2msec VBUS pulse */
+ /* for A-device: */
+ | (0 << 20) /* 200ms nominal A_WAIT_VRISE timer */
+ | SRP_DPW /* detect 167+ns SRP pulses */
+ | SRP_DATA | SRP_VBUS; /* accept both kinds of SRP pulse */
+ omap_writel(l, OTG_SYSCON_2);
+
+ omap_writew(DRIVER_SWITCH | OPRT_CHG
+ | B_SRP_TMROUT | B_HNP_FAIL
+ | A_VBUS_ERR | A_SRP_DETECT | A_REQ_TMROUT,
+ OTG_IRQ_EN);
+ l = omap_readl(OTG_SYSCON_2);
+ l |= OTG_EN;
+ omap_writel(l, OTG_SYSCON_2);
+
+ return 0;
+}
+
+static int omap_otg_probe(struct device *dev)
+{
+ int ret;
+
+ tahvo_otg_dev = to_platform_device(dev);
+ ret = omap_otg_init();
+ if (ret != 0) {
+ printk(KERN_ERR "tahvo-usb: omap_otg_init failed\n");
+ return ret;
+ }
+
+ return request_irq(tahvo_otg_dev->resource[1].start,
+ omap_otg_irq, IRQF_DISABLED, DRIVER_NAME,
+ &tahvo_usb_device);
+}
+
+static int omap_otg_remove(struct device *dev)
+{
+ free_irq(tahvo_otg_dev->resource[1].start, &tahvo_usb_device);
+ tahvo_otg_dev = NULL;
+
+ return 0;
+}
+
+struct device_driver omap_otg_driver = {
+ .name = "omap_otg",
+ .bus = &platform_bus_type,
+ .probe = omap_otg_probe,
+ .remove = omap_otg_remove,
+};
+
+/*
+ * ---------------------------------------------------------------------------
+ * Tahvo related functions
+ * These are Nokia proprietary code, except for the OTG register settings,
+ * which are copied from isp1301.c
+ * ---------------------------------------------------------------------------
+ */
+static ssize_t vbus_state_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tahvo_usb *tu = (struct tahvo_usb*) device->driver_data;
+ return sprintf(buf, "%d\n", tu->vbus_state);
+}
+static DEVICE_ATTR(vbus_state, 0444, vbus_state_show, NULL);
+
+int vbus_active = 0;
+
+#if 0
+
+static int host_suspend(struct tahvo_usb *tu)
+{
+ struct device *dev;
+
+ if (!tu->otg.host)
+ return -ENODEV;
+
+ /* Currently ASSUMES only the OTG port matters;
+ * other ports could be active...
+ */
+ dev = tu->otg.host->controller;
+ return dev->driver->suspend(dev, PMSG_SUSPEND);
+}
+
+static int host_resume(struct tahvo_usb *tu)
+{
+ struct device *dev;
+
+ if (!tu->otg.host)
+ return -ENODEV;
+
+ dev = tu->otg.host->controller;
+ return dev->driver->resume(dev);
+}
+
+#else
+
+static int host_suspend(struct tahvo_usb *tu)
+{
+ return 0;
+}
+
+static int host_resume(struct tahvo_usb *tu)
+{
+ return 0;
+}
+
+#endif
+
+static void check_vbus_state(struct tahvo_usb *tu)
+{
+ int reg, prev_state;
+
+ reg = tahvo_read_reg(TAHVO_REG_IDSR);
+ if (reg & 0x01) {
+ u32 l;
+
+ vbus_active = 1;
+ switch (tu->otg.state) {
+ case OTG_STATE_B_IDLE:
+ /* Enable the gadget driver */
+ if (tu->otg.gadget)
+ usb_gadget_vbus_connect(tu->otg.gadget);
+ /* Set B-session valid and not B-sessio ended to indicate
+ * Vbus to be ok. */
+ l = omap_readl(OTG_CTRL);
+ l &= ~OTG_BSESSEND;
+ l |= OTG_BSESSVLD;
+ omap_writel(l, OTG_CTRL);
+
+ tu->otg.state = OTG_STATE_B_PERIPHERAL;
+ break;
+ case OTG_STATE_A_IDLE:
+ /* Session is now valid assuming the USB hub is driving Vbus */
+ tu->otg.state = OTG_STATE_A_HOST;
+ host_resume(tu);
+ break;
+ default:
+ break;
+ }
+ printk("USB cable connected\n");
+ } else {
+ switch (tu->otg.state) {
+ case OTG_STATE_B_PERIPHERAL:
+ if (tu->otg.gadget)
+ usb_gadget_vbus_disconnect(tu->otg.gadget);
+ tu->otg.state = OTG_STATE_B_IDLE;
+ break;
+ case OTG_STATE_A_HOST:
+ tu->otg.state = OTG_STATE_A_IDLE;
+ break;
+ default:
+ break;
+ }
+ printk("USB cable disconnected\n");
+ vbus_active = 0;
+ }
+
+ prev_state = tu->vbus_state;
+ tu->vbus_state = reg & 0x01;
+ if (prev_state != tu->vbus_state)
+ sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state");
+}
+
+static void tahvo_usb_become_host(struct tahvo_usb *tu)
+{
+ u32 l;
+
+ /* Clear system and transceiver controlled bits
+ * also mark the A-session is always valid */
+ omap_otg_init();
+
+ l = omap_readl(OTG_CTRL);
+ l &= ~(OTG_CTRL_XCVR_MASK | OTG_CTRL_SYS_MASK);
+ l |= OTG_ASESSVLD;
+ omap_writel(l, OTG_CTRL);
+
+ /* Power up the transceiver in USB host mode */
+ tahvo_write_reg(TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND |
+ USBR_MASTER_SW2 | USBR_MASTER_SW1);
+ tu->otg.state = OTG_STATE_A_IDLE;
+
+ check_vbus_state(tu);
+}
+
+static void tahvo_usb_stop_host(struct tahvo_usb *tu)
+{
+ host_suspend(tu);
+ tu->otg.state = OTG_STATE_A_IDLE;
+}
+
+static void tahvo_usb_become_peripheral(struct tahvo_usb *tu)
+{
+ u32 l;
+
+ /* Clear system and transceiver controlled bits
+ * and enable ID to mark peripheral mode and
+ * BSESSEND to mark no Vbus */
+ omap_otg_init();
+ l = omap_readl(OTG_CTRL);
+ l &= ~(OTG_CTRL_XCVR_MASK | OTG_CTRL_SYS_MASK | OTG_BSESSVLD);
+ l |= OTG_ID | OTG_BSESSEND;
+ omap_writel(l, OTG_CTRL);
+
+ /* Power up transceiver and set it in USB perhiperal mode */
+ tahvo_write_reg(TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT | USBR_NSUSPEND | USBR_SLAVE_SW);
+ tu->otg.state = OTG_STATE_B_IDLE;
+
+ check_vbus_state(tu);
+}
+
+static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu)
+{
+ u32 l;
+
+ l = omap_readl(OTG_CTRL);
+ l &= ~OTG_BSESSVLD;
+ l |= OTG_BSESSEND;
+ omap_writel(l, OTG_CTRL);
+
+ if (tu->otg.gadget)
+ usb_gadget_vbus_disconnect(tu->otg.gadget);
+ tu->otg.state = OTG_STATE_B_IDLE;
+
+}
+
+static void tahvo_usb_power_off(struct tahvo_usb *tu)
+{
+ u32 l;
+ int id;
+
+ /* Disable gadget controller if any */
+ if (tu->otg.gadget)
+ usb_gadget_vbus_disconnect(tu->otg.gadget);
+
+ host_suspend(tu);
+
+ /* Disable OTG and interrupts */
+ if (TAHVO_MODE(tu) == TAHVO_MODE_PERIPHERAL)
+ id = OTG_ID;
+ else
+ id = 0;
+ l = omap_readl(OTG_CTRL);
+ l &= ~(OTG_CTRL_XCVR_MASK | OTG_CTRL_SYS_MASK | OTG_BSESSVLD);
+ l |= id | OTG_BSESSEND;
+ omap_writel(l, OTG_CTRL);
+ omap_writew(0, OTG_IRQ_EN);
+
+ l = omap_readl(OTG_SYSCON_2);
+ l &= ~OTG_EN;
+ omap_writel(l, OTG_SYSCON_2);
+
+ l = omap_readl(OTG_SYSCON_1);
+ l |= OTG_IDLE_EN;
+ omap_writel(l, OTG_SYSCON_1);
+
+ /* Power off transceiver */
+ tahvo_write_reg(TAHVO_REG_USBR, 0);
+ tu->otg.state = OTG_STATE_UNDEFINED;
+}
+
+
+static int tahvo_usb_set_power(struct otg_transceiver *dev, unsigned mA)
+{
+ struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, otg);
+
+ dev_dbg(&tu->pt_dev->dev, "set_power %d mA\n", mA);
+
+ if (dev->state == OTG_STATE_B_PERIPHERAL) {
+ /* REVISIT: Can Tahvo charge battery from VBUS? */
+ }
+ return 0;
+}
+
+static int tahvo_usb_set_suspend(struct otg_transceiver *dev, int suspend)
+{
+ struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, otg);
+ u16 w;
+
+ dev_dbg(&tu->pt_dev->dev, "set_suspend\n");
+
+ w = tahvo_read_reg(TAHVO_REG_USBR);
+ if (suspend)
+ w &= ~USBR_NSUSPEND;
+ else
+ w |= USBR_NSUSPEND;
+ tahvo_write_reg(TAHVO_REG_USBR, w);
+
+ return 0;
+}
+
+static int tahvo_usb_start_srp(struct otg_transceiver *dev)
+{
+ struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, otg);
+ u32 otg_ctrl;
+
+ dev_dbg(&tu->pt_dev->dev, "start_srp\n");
+
+ if (!dev || tu->otg.state != OTG_STATE_B_IDLE)
+ return -ENODEV;
+
+ otg_ctrl = omap_readl(OTG_CTRL);
+ if (!(otg_ctrl & OTG_BSESSEND))
+ return -EINVAL;
+
+ otg_ctrl |= OTG_B_BUSREQ;
+ otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_SYS_MASK;
+ omap_writel(otg_ctrl, OTG_CTRL);
+ tu->otg.state = OTG_STATE_B_SRP_INIT;
+
+ return 0;
+}
+
+static int tahvo_usb_start_hnp(struct otg_transceiver *otg)
+{
+ struct tahvo_usb *tu = container_of(otg, struct tahvo_usb, otg);
+
+ dev_dbg(&tu->pt_dev->dev, "start_hnp\n");
+#ifdef CONFIG_USB_OTG
+ /* REVISIT: Add this for OTG */
+#endif
+ return -EINVAL;
+}
+
+static int tahvo_usb_set_host(struct otg_transceiver *otg, struct usb_bus *host)
+{
+ struct tahvo_usb *tu = container_of(otg, struct tahvo_usb, otg);
+ u32 l;
+
+ dev_dbg(&tu->pt_dev->dev, "set_host %p\n", host);
+
+ if (otg == NULL)
+ return -ENODEV;
+
+#if defined(CONFIG_USB_OTG) || !defined(CONFIG_USB_GADGET_OMAP)
+
+ mutex_lock(&tu->serialize);
+
+ if (host == NULL) {
+ if (TAHVO_MODE(tu) == TAHVO_MODE_HOST)
+ tahvo_usb_power_off(tu);
+ tu->otg.host = NULL;
+ mutex_unlock(&tu->serialize);
+ return 0;
+ }
+
+ l = omap_readl(OTG_SYSCON_1);
+ l &= ~(OTG_IDLE_EN | HST_IDLE_EN | DEV_IDLE_EN);
+ omap_writel(l, OTG_SYSCON_1);
+
+ if (TAHVO_MODE(tu) == TAHVO_MODE_HOST) {
+ tu->otg.host = NULL;
+ tahvo_usb_become_host(tu);
+ } else
+ host_suspend(tu);
+
+ tu->otg.host = host;
+
+ mutex_unlock(&tu->serialize);
+#else
+ /* No host mode configured, so do not allow host controlled to be set */
+ return -EINVAL;
+#endif
+
+ return 0;
+}
+
+static int tahvo_usb_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget)
+{
+ struct tahvo_usb *tu = container_of(otg, struct tahvo_usb, otg);
+
+ dev_dbg(&tu->pt_dev->dev, "set_peripheral %p\n", gadget);
+
+ if (!otg)
+ return -ENODEV;
+
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_GADGET_OMAP)
+
+ mutex_lock(&tu->serialize);
+
+ if (!gadget) {
+ if (TAHVO_MODE(tu) == TAHVO_MODE_PERIPHERAL)
+ tahvo_usb_power_off(tu);
+ tu->otg.gadget = NULL;
+ mutex_unlock(&tu->serialize);
+ return 0;
+ }
+
+ tu->otg.gadget = gadget;
+ if (TAHVO_MODE(tu) == TAHVO_MODE_PERIPHERAL)
+ tahvo_usb_become_peripheral(tu);
+
+ mutex_unlock(&tu->serialize);
+#else
+ /* No gadget mode configured, so do not allow host controlled to be set */
+ return -EINVAL;
+#endif
+
+ return 0;
+}
+
+static void tahvo_usb_irq_work(struct work_struct *work)
+{
+ struct tahvo_usb *tu = container_of(work, struct tahvo_usb, irq_work);
+
+ mutex_lock(&tu->serialize);
+ check_vbus_state(tu);
+ mutex_unlock(&tu->serialize);
+}
+
+static void tahvo_usb_vbus_interrupt(unsigned long arg)
+{
+ struct tahvo_usb *tu = (struct tahvo_usb *) arg;
+
+ tahvo_ack_irq(TAHVO_INT_VBUSON);
+ /* Seems we need this to acknowledge the interrupt */
+ tahvo_read_reg(TAHVO_REG_IDSR);
+ schedule_work(&tu->irq_work);
+}
+
+#ifdef CONFIG_USB_OTG
+static ssize_t otg_mode_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tahvo_usb *tu = (struct tahvo_usb*) device->driver_data;
+ switch (tu->tahvo_mode) {
+ case TAHVO_MODE_HOST:
+ return sprintf(buf, "host\n");
+ case TAHVO_MODE_PERIPHERAL:
+ return sprintf(buf, "peripheral\n");
+ }
+ return sprintf(buf, "unknown\n");
+}
+
+static ssize_t otg_mode_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct tahvo_usb *tu = (struct tahvo_usb*) device->driver_data;
+ int r;
+
+ r = strlen(buf);
+ mutex_lock(&tu->serialize);
+ if (strncmp(buf, "host", 4) == 0) {
+ if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
+ tahvo_usb_stop_peripheral(tu);
+ tu->tahvo_mode = TAHVO_MODE_HOST;
+ if (tu->otg.host) {
+ printk(KERN_INFO "Selected HOST mode: host controller present.\n");
+ tahvo_usb_become_host(tu);
+ } else {
+ printk(KERN_INFO "Selected HOST mode: no host controller, powering off.\n");
+ tahvo_usb_power_off(tu);
+ }
+ } else if (strncmp(buf, "peripheral", 10) == 0) {
+ if (tu->tahvo_mode == TAHVO_MODE_HOST)
+ tahvo_usb_stop_host(tu);
+ tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
+ if (tu->otg.gadget) {
+ printk(KERN_INFO "Selected PERIPHERAL mode: gadget driver present.\n");
+ tahvo_usb_become_peripheral(tu);
+ } else {
+ printk(KERN_INFO "Selected PERIPHERAL mode: no gadget driver, powering off.\n");
+ tahvo_usb_power_off(tu);
+ }
+ } else
+ r = -EINVAL;
+
+ mutex_unlock(&tu->serialize);
+ return r;
+}
+
+static DEVICE_ATTR(otg_mode, 0644, otg_mode_show, otg_mode_store);
+#endif
+
+static int tahvo_usb_probe(struct device *dev)
+{
+ struct tahvo_usb *tu;
+ int ret;
+
+ dev_dbg(dev, "probe\n");
+
+ /* Create driver data */
+ tu = kmalloc(sizeof(*tu), GFP_KERNEL);
+ if (!tu)
+ return -ENOMEM;
+ memset(tu, 0, sizeof(*tu));
+ tu->pt_dev = container_of(dev, struct platform_device, dev);
+#ifdef CONFIG_USB_OTG
+ /* Default mode */
+#ifdef CONFIG_CBUS_TAHVO_USB_HOST_BY_DEFAULT
+ tu->tahvo_mode = TAHVO_MODE_HOST;
+#else
+ tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
+#endif
+#endif
+
+ INIT_WORK(&tu->irq_work, tahvo_usb_irq_work);
+ mutex_init(&tu->serialize);
+
+ /* Set initial state, so that we generate kevents only on
+ * state changes */
+ tu->vbus_state = tahvo_read_reg(TAHVO_REG_IDSR) & 0x01;
+
+ /* We cannot enable interrupt until omap_udc is initialized */
+ ret = tahvo_request_irq(TAHVO_INT_VBUSON, tahvo_usb_vbus_interrupt,
+ (unsigned long) tu, "vbus_interrupt");
+ if (ret != 0) {
+ kfree(tu);
+ printk(KERN_ERR "Could not register Tahvo interrupt for VBUS\n");
+ return ret;
+ }
+
+ /* Attributes */
+ ret = device_create_file(dev, &dev_attr_vbus_state);
+#ifdef CONFIG_USB_OTG
+ ret |= device_create_file(dev, &dev_attr_otg_mode);
+#endif
+ if (ret)
+ printk(KERN_ERR "attribute creation failed: %d\n", ret);
+
+ /* Create OTG interface */
+ tahvo_usb_power_off(tu);
+ tu->otg.state = OTG_STATE_UNDEFINED;
+ tu->otg.label = DRIVER_NAME;
+ tu->otg.set_host = tahvo_usb_set_host;
+ tu->otg.set_peripheral = tahvo_usb_set_peripheral;
+ tu->otg.set_power = tahvo_usb_set_power;
+ tu->otg.set_suspend = tahvo_usb_set_suspend;
+ tu->otg.start_srp = tahvo_usb_start_srp;
+ tu->otg.start_hnp = tahvo_usb_start_hnp;
+
+ ret = otg_set_transceiver(&tu->otg);
+ if (ret < 0) {
+ printk(KERN_ERR "Cannot register USB transceiver\n");
+ kfree(tu);
+ tahvo_free_irq(TAHVO_INT_VBUSON);
+ return ret;
+ }
+
+ dev->driver_data = tu;
+
+ /* Act upon current vbus state once at startup. A vbus state irq may or
+ * may not be generated in addition to this. */
+ schedule_work(&tu->irq_work);
+ return 0;
+}
+
+static int tahvo_usb_remove(struct device *dev)
+{
+ dev_dbg(dev, "remove\n");
+
+ tahvo_free_irq(TAHVO_INT_VBUSON);
+ flush_scheduled_work();
+ otg_set_transceiver(0);
+ device_remove_file(dev, &dev_attr_vbus_state);
+#ifdef CONFIG_USB_OTG
+ device_remove_file(dev, &dev_attr_otg_mode);
+#endif
+ return 0;
+}
+
+static struct device_driver tahvo_usb_driver = {
+ .name = "tahvo-usb",
+ .bus = &platform_bus_type,
+ .probe = tahvo_usb_probe,
+ .remove = tahvo_usb_remove,
+};
+
+static struct platform_device tahvo_usb_device = {
+ .name = "tahvo-usb",
+ .id = -1,
+};
+
+static int __init tahvo_usb_init(void)
+{
+ int ret = 0;
+
+ printk(KERN_INFO "Tahvo USB transceiver driver initializing\n");
+ ret = driver_register(&tahvo_usb_driver);
+ if (ret)
+ return ret;
+ ret = platform_device_register(&tahvo_usb_device);
+ if (ret < 0) {
+ driver_unregister(&tahvo_usb_driver);
+ return ret;
+ }
+ ret = driver_register(&omap_otg_driver);
+ if (ret) {
+ platform_device_unregister(&tahvo_usb_device);
+ driver_unregister(&tahvo_usb_driver);
+ return ret;
+ }
+ return 0;
+}
+
+subsys_initcall(tahvo_usb_init);
+
+static void __exit tahvo_usb_exit(void)
+{
+ driver_unregister(&omap_otg_driver);
+ platform_device_unregister(&tahvo_usb_device);
+ driver_unregister(&tahvo_usb_driver);
+}
+module_exit(tahvo_usb_exit);
+
+MODULE_DESCRIPTION("Tahvo USB OTG Transceiver Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs");
--- /dev/null
- #include <asm/arch/mux.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/board.h>
+/**
+ * drivers/cbus/tahvo.c
+ *
+ * Support functions for Tahvo ASIC
+ *
+ * Copyright (C) 2004, 2005 Nokia Corporation
+ *
+ * Written by Juha Yrjölä <juha.yrjola@nokia.com>,
+ * David Weinehall <david.weinehall@nokia.com>, and
+ * Mikko Ylinen <mikko.k.ylinen@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/init.h>
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <asm/uaccess.h>
+
++#include <mach/mux.h>
++#include <mach/gpio.h>
++#include <mach/board.h>
+
+#include "cbus.h"
+#include "tahvo.h"
+
+#define TAHVO_ID 0x02
+#define PFX "tahvo: "
+
+static int tahvo_initialized;
+static int tahvo_irq_pin;
+static int tahvo_is_betty;
+
+static struct tasklet_struct tahvo_tasklet;
+spinlock_t tahvo_lock = SPIN_LOCK_UNLOCKED;
+
+static struct completion device_release;
+
+struct tahvo_irq_handler_desc {
+ int (*func)(unsigned long);
+ unsigned long arg;
+ char name[8];
+};
+
+static struct tahvo_irq_handler_desc tahvo_irq_handlers[MAX_TAHVO_IRQ_HANDLERS];
+
+/**
+ * tahvo_read_reg - Read a value from a register in Tahvo
+ * @reg: the register to read from
+ *
+ * This function returns the contents of the specified register
+ */
+int tahvo_read_reg(int reg)
+{
+ BUG_ON(!tahvo_initialized);
+ return cbus_read_reg(cbus_host, TAHVO_ID, reg);
+}
+
+/**
+ * tahvo_write_reg - Write a value to a register in Tahvo
+ * @reg: the register to write to
+ * @reg: the value to write to the register
+ *
+ * This function writes a value to the specified register
+ */
+void tahvo_write_reg(int reg, u16 val)
+{
+ BUG_ON(!tahvo_initialized);
+ cbus_write_reg(cbus_host, TAHVO_ID, reg, val);
+}
+
+/**
+ * tahvo_set_clear_reg_bits - set and clear register bits atomically
+ * @reg: the register to write to
+ * @bits: the bits to set
+ *
+ * This function sets and clears the specified Tahvo register bits atomically
+ */
+void tahvo_set_clear_reg_bits(int reg, u16 set, u16 clear)
+{
+ unsigned long flags;
+ u16 w;
+
+ spin_lock_irqsave(&tahvo_lock, flags);
+ w = tahvo_read_reg(reg);
+ w &= ~clear;
+ w |= set;
+ tahvo_write_reg(reg, w);
+ spin_unlock_irqrestore(&tahvo_lock, flags);
+}
+
+/*
+ * Disable given TAHVO interrupt
+ */
+void tahvo_disable_irq(int id)
+{
+ unsigned long flags;
+ u16 mask;
+
+ spin_lock_irqsave(&tahvo_lock, flags);
+ mask = tahvo_read_reg(TAHVO_REG_IMR);
+ mask |= 1 << id;
+ tahvo_write_reg(TAHVO_REG_IMR, mask);
+ spin_unlock_irqrestore(&tahvo_lock, flags);
+}
+
+/*
+ * Enable given TAHVO interrupt
+ */
+void tahvo_enable_irq(int id)
+{
+ unsigned long flags;
+ u16 mask;
+
+ spin_lock_irqsave(&tahvo_lock, flags);
+ mask = tahvo_read_reg(TAHVO_REG_IMR);
+ mask &= ~(1 << id);
+ tahvo_write_reg(TAHVO_REG_IMR, mask);
+ spin_unlock_irqrestore(&tahvo_lock, flags);
+}
+
+/*
+ * Acknowledge given TAHVO interrupt
+ */
+void tahvo_ack_irq(int id)
+{
+ tahvo_write_reg(TAHVO_REG_IDR, 1 << id);
+}
+
+static int tahvo_7bit_backlight;
+
+int tahvo_get_backlight_level(void)
+{
+ int mask;
+
+ if (tahvo_7bit_backlight)
+ mask = 0x7f;
+ else
+ mask = 0x0f;
+ return tahvo_read_reg(TAHVO_REG_LEDPWMR) & mask;
+}
+
+int tahvo_get_max_backlight_level(void)
+{
+ if (tahvo_7bit_backlight)
+ return 0x7f;
+ else
+ return 0x0f;
+}
+
+void tahvo_set_backlight_level(int level)
+{
+ int max_level;
+
+ max_level = tahvo_get_max_backlight_level();
+ if (level > max_level)
+ level = max_level;
+ tahvo_write_reg(TAHVO_REG_LEDPWMR, level);
+}
+
+/*
+ * TAHVO interrupt handler. Only schedules the tasklet.
+ */
+static irqreturn_t tahvo_irq_handler(int irq, void *dev_id)
+{
+ tasklet_schedule(&tahvo_tasklet);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Tasklet handler
+ */
+static void tahvo_tasklet_handler(unsigned long data)
+{
+ struct tahvo_irq_handler_desc *hnd;
+ u16 id;
+ u16 im;
+ int i;
+
+ for (;;) {
+ id = tahvo_read_reg(TAHVO_REG_IDR);
+ im = ~tahvo_read_reg(TAHVO_REG_IMR);
+ id &= im;
+
+ if (!id)
+ break;
+
+ for (i = 0; id != 0; i++, id >>= 1) {
+ if (!(id & 1))
+ continue;
+ hnd = &tahvo_irq_handlers[i];
+ if (hnd->func == NULL) {
+ /* Spurious tahvo interrupt - just ack it */
+ printk(KERN_INFO "Spurious Tahvo interrupt "
+ "(id %d)\n", i);
+ tahvo_disable_irq(i);
+ tahvo_ack_irq(i);
+ continue;
+ }
+ hnd->func(hnd->arg);
+ /*
+ * Don't acknowledge the interrupt here
+ * It must be done explicitly
+ */
+ }
+ }
+}
+
+/*
+ * Register the handler for a given TAHVO interrupt source.
+ */
+int tahvo_request_irq(int id, void *irq_handler, unsigned long arg, char *name)
+{
+ struct tahvo_irq_handler_desc *hnd;
+
+ if (irq_handler == NULL || id >= MAX_TAHVO_IRQ_HANDLERS ||
+ name == NULL) {
+ printk(KERN_ERR PFX "Invalid arguments to %s\n",
+ __FUNCTION__);
+ return -EINVAL;
+ }
+ hnd = &tahvo_irq_handlers[id];
+ if (hnd->func != NULL) {
+ printk(KERN_ERR PFX "IRQ %d already reserved\n", id);
+ return -EBUSY;
+ }
+ printk(KERN_INFO PFX "Registering interrupt %d for device %s\n",
+ id, name);
+ hnd->func = irq_handler;
+ hnd->arg = arg;
+ strlcpy(hnd->name, name, sizeof(hnd->name));
+
+ tahvo_ack_irq(id);
+ tahvo_enable_irq(id);
+
+ return 0;
+}
+
+/*
+ * Unregister the handler for a given TAHVO interrupt source.
+ */
+void tahvo_free_irq(int id)
+{
+ struct tahvo_irq_handler_desc *hnd;
+
+ if (id >= MAX_TAHVO_IRQ_HANDLERS) {
+ printk(KERN_ERR PFX "Invalid argument to %s\n",
+ __FUNCTION__);
+ return;
+ }
+ hnd = &tahvo_irq_handlers[id];
+ if (hnd->func == NULL) {
+ printk(KERN_ERR PFX "IRQ %d already freed\n", id);
+ return;
+ }
+
+ tahvo_disable_irq(id);
+ hnd->func = NULL;
+}
+
+/**
+ * tahvo_probe - Probe for Tahvo ASIC
+ * @dev: the Tahvo device
+ *
+ * Probe for the Tahvo ASIC and allocate memory
+ * for its device-struct if found
+ */
+static int __devinit tahvo_probe(struct device *dev)
+{
+ const struct omap_em_asic_bb5_config * em_asic_config;
+ int rev, id, ret;
+
+ /* Prepare tasklet */
+ tasklet_init(&tahvo_tasklet, tahvo_tasklet_handler, 0);
+
+ em_asic_config = omap_get_config(OMAP_TAG_EM_ASIC_BB5,
+ struct omap_em_asic_bb5_config);
+ if (em_asic_config == NULL) {
+ printk(KERN_ERR PFX "Unable to retrieve config data\n");
+ return -ENODATA;
+ }
+
+ tahvo_initialized = 1;
+
+ rev = tahvo_read_reg(TAHVO_REG_ASICR);
+
+ id = (rev >> 8) & 0xff;
+ if (id == 0x03) {
+ if ((rev & 0xff) >= 0x50)
+ tahvo_7bit_backlight = 1;
+ } else if (id == 0x0b) {
+ tahvo_is_betty = 1;
+ tahvo_7bit_backlight = 1;
+ } else {
+ printk(KERN_ERR "Tahvo/Betty chip not found");
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "%s v%d.%d found\n", tahvo_is_betty ? "Betty" : "Tahvo",
+ (rev >> 4) & 0x0f, rev & 0x0f);
+
+ tahvo_irq_pin = em_asic_config->tahvo_irq_gpio;
+
+ if ((ret = omap_request_gpio(tahvo_irq_pin)) < 0) {
+ printk(KERN_ERR PFX "Unable to reserve IRQ GPIO\n");
+ return ret;
+ }
+
+ /* Set the pin as input */
+ omap_set_gpio_direction(tahvo_irq_pin, 1);
+
+ /* Rising edge triggers the IRQ */
+ set_irq_type(OMAP_GPIO_IRQ(tahvo_irq_pin), IRQ_TYPE_EDGE_RISING);
+
+ /* Mask all TAHVO interrupts */
+ tahvo_write_reg(TAHVO_REG_IMR, 0xffff);
+
+ ret = request_irq(OMAP_GPIO_IRQ(tahvo_irq_pin), tahvo_irq_handler, 0,
+ "tahvo", 0);
+ if (ret < 0) {
+ printk(KERN_ERR PFX "Unable to register IRQ handler\n");
+ omap_free_gpio(tahvo_irq_pin);
+ return ret;
+ }
+#ifdef CONFIG_CBUS_TAHVO_USER
+ /* Initialize user-space interface */
+ if (tahvo_user_init() < 0) {
+ printk(KERN_ERR "Unable to initialize driver\n");
+ free_irq(OMAP_GPIO_IRQ(tahvo_irq_pin), 0);
+ omap_free_gpio(tahvo_irq_pin);
+ return ret;
+ }
+#endif
+ return 0;
+}
+
+static int tahvo_remove(struct device *dev)
+{
+#ifdef CONFIG_CBUS_TAHVO_USER
+ tahvo_user_cleanup();
+#endif
+ /* Mask all TAHVO interrupts */
+ tahvo_write_reg(TAHVO_REG_IMR, 0xffff);
+ free_irq(OMAP_GPIO_IRQ(tahvo_irq_pin), 0);
+ omap_free_gpio(tahvo_irq_pin);
+ tasklet_kill(&tahvo_tasklet);
+
+ return 0;
+}
+
+static void tahvo_device_release(struct device *dev)
+{
+ complete(&device_release);
+}
+
+static struct device_driver tahvo_driver = {
+ .name = "tahvo",
+ .bus = &platform_bus_type,
+ .probe = tahvo_probe,
+ .remove = tahvo_remove,
+};
+
+static struct platform_device tahvo_device = {
+ .name = "tahvo",
+ .id = -1,
+ .dev = {
+ .release = tahvo_device_release,
+ }
+};
+
+/**
+ * tahvo_init - initialise Tahvo driver
+ *
+ * Initialise the Tahvo driver and return 0 if everything worked ok
+ */
+static int __init tahvo_init(void)
+{
+ int ret = 0;
+
+ printk(KERN_INFO "Tahvo/Betty driver initialising\n");
+
+ init_completion(&device_release);
+
+ if ((ret = driver_register(&tahvo_driver)) < 0)
+ return ret;
+
+ if ((ret = platform_device_register(&tahvo_device)) < 0) {
+ driver_unregister(&tahvo_driver);
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * Cleanup
+ */
+static void __exit tahvo_exit(void)
+{
+ platform_device_unregister(&tahvo_device);
+ driver_unregister(&tahvo_driver);
+ wait_for_completion(&device_release);
+}
+
+EXPORT_SYMBOL(tahvo_request_irq);
+EXPORT_SYMBOL(tahvo_free_irq);
+EXPORT_SYMBOL(tahvo_enable_irq);
+EXPORT_SYMBOL(tahvo_disable_irq);
+EXPORT_SYMBOL(tahvo_ack_irq);
+EXPORT_SYMBOL(tahvo_read_reg);
+EXPORT_SYMBOL(tahvo_write_reg);
+EXPORT_SYMBOL(tahvo_get_backlight_level);
+EXPORT_SYMBOL(tahvo_get_max_backlight_level);
+EXPORT_SYMBOL(tahvo_set_backlight_level);
+
+subsys_initcall(tahvo_init);
+module_exit(tahvo_exit);
+
+MODULE_DESCRIPTION("Tahvo ASIC control");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Juha Yrjölä, David Weinehall, and Mikko Ylinen");
--- /dev/null
- #include <asm/arch/dsp_common.h>
- #include <asm/arch/mmu.h>
+/*
+ * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
+ *
+ * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __PLAT_OMAP_DSP_DSP_H
+#define __PLAT_OMAP_DSP_DSP_H
+
+#include "hardware_dsp.h"
++#include <mach/dsp_common.h>
++#include <mach/mmu.h>
+
+
+#ifdef CONFIG_ARCH_OMAP2
+#include "../../../arch/arm/mach-omap2/prm.h"
+#include "../../../arch/arm/mach-omap2/prm-regbits-24xx.h"
+#include "../../../arch/arm/mach-omap2/cm.h"
+#include "../../../arch/arm/mach-omap2/cm-regbits-24xx.h"
+#endif
+
+/*
+ * MAJOR device number: !! allocated arbitrary !!
+ */
+#define OMAP_DSP_CTL_MAJOR 96
+#define OMAP_DSP_TASK_MAJOR 97
+
+#define OLD_BINARY_SUPPORT y
+
+#ifdef OLD_BINARY_SUPPORT
+#define MBREV_3_0 0x0017
+#define MBREV_3_2 0x0018
+#endif
+
+#define DSP_INIT_PAGE 0xfff000
+
+#ifdef CONFIG_ARCH_OMAP1
+/* idle program will be placed at IDLEPG_BASE. */
+#define IDLEPG_BASE 0xfffe00
+#define IDLEPG_SIZE 0x100
+#endif /* CONFIG_ARCH_OMAP1 */
+
+/* timeout value for DSP response */
+#define DSP_TIMEOUT (10 * HZ)
+
+enum dsp_mem_type_e {
+ MEM_TYPE_CROSSING = -1,
+ MEM_TYPE_NONE = 0,
+ MEM_TYPE_DARAM,
+ MEM_TYPE_SARAM,
+ MEM_TYPE_EXTERN,
+};
+
+
+typedef int __bitwise arm_dsp_dir_t;
+#define DIR_A2D ((__force arm_dsp_dir_t) 1)
+#define DIR_D2A ((__force arm_dsp_dir_t) 2)
+
+enum cfgstat_e {
+ CFGSTAT_CLEAN = 0,
+ CFGSTAT_READY,
+ CFGSTAT_SUSPEND,
+ CFGSTAT_RESUME, /* request only */
+ CFGSTAT_MAX
+};
+
+enum errcode_e {
+ ERRCODE_WDT = 0,
+ ERRCODE_MMU,
+ ERRCODE_MAX
+};
+
+/* keep 2 entries for TID_FREE and TID_ANON */
+#define TASKDEV_MAX 254
+
+#define MK32(uw,lw) (((u32)(uw)) << 16 | (lw))
+#define MKLONG(uw,lw) (((unsigned long)(uw)) << 16 | (lw))
+#define MKVIRT(uw,lw) dspword_to_virt(MKLONG((uw), (lw)));
+
+struct sync_seq {
+ u16 da_dsp;
+ u16 da_arm;
+ u16 ad_dsp;
+ u16 ad_arm;
+};
+
+struct mem_sync_struct {
+ struct sync_seq *DARAM;
+ struct sync_seq *SARAM;
+ struct sync_seq *SDRAM;
+};
+
+/* struct mbcmd and union mbcmd_hw must be compatible */
+struct mbcmd {
+ u32 data:16;
+ u32 cmd_l:8;
+ u32 cmd_h:7;
+ u32 seq:1;
+};
+
+#define MBCMD_INIT(h, l, d) { \
+ .cmd_h = (h), \
+ .cmd_l = (l), \
+ .data = (d), \
+ }
+
+struct mb_exarg {
+ u8 tid;
+ int argc;
+ u16 *argv;
+};
+
+typedef u32 dsp_long_t; /* must have ability to carry TADD_ABORTADR */
+
+extern void dsp_mbox_start(void);
+extern void dsp_mbox_stop(void);
+extern int dsp_mbox_config(void *p);
+extern int sync_with_dsp(u16 *syncwd, u16 tid, int try_cnt);
+extern int __dsp_mbcmd_send_exarg(struct mbcmd *mb, struct mb_exarg *arg,
+ int recovery_flag);
+#define dsp_mbcmd_send(mb) __dsp_mbcmd_send_exarg((mb), NULL, 0)
+#define dsp_mbcmd_send_exarg(mb, arg) __dsp_mbcmd_send_exarg((mb), (arg), 0)
+extern int dsp_mbcmd_send_and_wait_exarg(struct mbcmd *mb, struct mb_exarg *arg,
+ wait_queue_head_t *q);
+#define dsp_mbcmd_send_and_wait(mb, q) \
+ dsp_mbcmd_send_and_wait_exarg((mb), NULL, (q))
+
+static inline int __mbcompose_send_exarg(u8 cmd_h, u8 cmd_l, u16 data,
+ struct mb_exarg *arg,
+ int recovery_flag)
+{
+ struct mbcmd mb = MBCMD_INIT(cmd_h, cmd_l, data);
+ return __dsp_mbcmd_send_exarg(&mb, arg, recovery_flag);
+}
+#define mbcompose_send(cmd_h, cmd_l, data) \
+ __mbcompose_send_exarg(MBOX_CMD_DSP_##cmd_h, (cmd_l), (data), NULL, 0)
+#define mbcompose_send_exarg(cmd_h, cmd_l, data, arg) \
+ __mbcompose_send_exarg(MBOX_CMD_DSP_##cmd_h, (cmd_l), (data), arg, 0)
+#define mbcompose_send_recovery(cmd_h, cmd_l, data) \
+ __mbcompose_send_exarg(MBOX_CMD_DSP_##cmd_h, (cmd_l), (data), NULL, 1)
+
+static inline int __mbcompose_send_and_wait_exarg(u8 cmd_h, u8 cmd_l,
+ u16 data,
+ struct mb_exarg *arg,
+ wait_queue_head_t *q)
+{
+ struct mbcmd mb = MBCMD_INIT(cmd_h, cmd_l, data);
+ return dsp_mbcmd_send_and_wait_exarg(&mb, arg, q);
+}
+#define mbcompose_send_and_wait(cmd_h, cmd_l, data, q) \
+ __mbcompose_send_and_wait_exarg(MBOX_CMD_DSP_##cmd_h, (cmd_l), (data), \
+ NULL, (q))
+#define mbcompose_send_and_wait_exarg(cmd_h, cmd_l, data, arg, q) \
+ __mbcompose_send_and_wait_exarg(MBOX_CMD_DSP_##cmd_h, (cmd_l), (data), \
+ (arg), (q))
+
+extern struct ipbuf_head *bid_to_ipbuf(u16 bid);
+extern void ipbuf_start(void);
+extern void ipbuf_stop(void);
+extern int ipbuf_config(u16 ln, u16 lsz, void *base);
+extern int ipbuf_sys_config(void *p, arm_dsp_dir_t dir);
+extern int ipbuf_p_validate(void *p, arm_dsp_dir_t dir);
+extern struct ipbuf_head *get_free_ipbuf(u8 tid);
+extern void release_ipbuf(struct ipbuf_head *ipb_h);
+extern void balance_ipbuf(void);
+extern void unuse_ipbuf(struct ipbuf_head *ipb_h);
+extern void unuse_ipbuf_nowait(struct ipbuf_head *ipb_h);
+
+#define release_ipbuf_pvt(ipbuf_pvt) \
+ do { \
+ (ipbuf_pvt)->s = TID_FREE; \
+ } while(0)
+
+extern int mbox_revision;
+
+extern int dsp_cfgstat_request(enum cfgstat_e st);
+extern enum cfgstat_e dsp_cfgstat_get_stat(void);
+extern int dsp_set_runlevel(u8 level);
+
+extern int dsp_task_config_all(u8 n);
+extern void dsp_task_unconfig_all(void);
+extern u8 dsp_task_count(void);
+extern int dsp_taskmod_busy(void);
+extern int dsp_mkdev(char *name);
+extern int dsp_rmdev(char *name);
+extern int dsp_tadd_minor(unsigned char minor, dsp_long_t adr);
+extern int dsp_tdel_minor(unsigned char minor);
+extern int dsp_tkill_minor(unsigned char minor);
+extern long taskdev_state_stale(unsigned char minor);
+extern int dsp_dbg_config(u16 *buf, u16 sz, u16 lsz);
+extern void dsp_dbg_stop(void);
+
+extern int ipbuf_is_held(u8 tid, u16 bid);
+
+extern int dsp_mem_sync_inc(void);
+extern int dsp_mem_sync_config(struct mem_sync_struct *sync);
+extern enum dsp_mem_type_e dsp_mem_type(void *vadr, size_t len);
+extern int dsp_address_validate(void *p, size_t len, char *fmt, ...);
+#ifdef CONFIG_ARCH_OMAP1
+extern void dsp_mem_usecount_clear(void);
+#endif
+extern void exmap_use(void *vadr, size_t len);
+extern void exmap_unuse(void *vadr, size_t len);
+extern unsigned long dsp_virt_to_phys(void *vadr, size_t *len);
+extern void dsp_mem_start(void);
+extern void dsp_mem_stop(void);
+
+extern void dsp_twch_start(void);
+extern void dsp_twch_stop(void);
+extern void dsp_twch_touch(void);
+
+extern void dsp_err_start(void);
+extern void dsp_err_stop(void);
+extern void dsp_err_set(enum errcode_e code, unsigned long arg);
+extern void dsp_err_clear(enum errcode_e code);
+extern int dsp_err_isset(enum errcode_e code);
+
+enum cmd_l_type_e {
+ CMD_L_TYPE_NULL,
+ CMD_L_TYPE_TID,
+ CMD_L_TYPE_SUBCMD,
+};
+
+struct cmdinfo {
+ char *name;
+ enum cmd_l_type_e cmd_l_type;
+ void (*handler)(struct mbcmd *mb);
+};
+
+extern const struct cmdinfo *cmdinfo[];
+
+#define cmd_name(mb) (cmdinfo[(mb).cmd_h]->name)
+extern char *subcmd_name(struct mbcmd *mb);
+
+extern void mblog_add(struct mbcmd *mb, arm_dsp_dir_t dir);
+
+extern struct omap_mmu dsp_mmu;
+
+#define dsp_mem_enable(addr) omap_mmu_mem_enable(&dsp_mmu, (addr))
+#define dsp_mem_disable(addr) omap_mmu_mem_disable(&dsp_mmu, (addr))
+
+#define DSPSPACE_SIZE 0x1000000
+
+#define omap_set_bit_regw(b,r) \
+ do { omap_writew(omap_readw(r) | (b), (r)); } while(0)
+#define omap_clr_bit_regw(b,r) \
+ do { omap_writew(omap_readw(r) & ~(b), (r)); } while(0)
+#define omap_set_bit_regl(b,r) \
+ do { omap_writel(omap_readl(r) | (b), (r)); } while(0)
+#define omap_clr_bit_regl(b,r) \
+ do { omap_writel(omap_readl(r) & ~(b), (r)); } while(0)
+#define omap_set_bits_regl(val,mask,r) \
+ do { omap_writel((omap_readl(r) & ~(mask)) | (val), (r)); } while(0)
+
+#define dspword_to_virt(dw) ((void *)(dspmem_base + ((dw) << 1)))
+#define dspbyte_to_virt(db) ((void *)(dspmem_base + (db)))
+#define virt_to_dspword(va) \
+ ((dsp_long_t)(((unsigned long)(va) - dspmem_base) >> 1))
+#define virt_to_dspbyte(va) \
+ ((dsp_long_t)((unsigned long)(va) - dspmem_base))
+#define is_dsp_internal_mem(va) \
+ (((unsigned long)(va) >= dspmem_base) && \
+ ((unsigned long)(va) < dspmem_base + dspmem_size))
+#define is_dspbyte_internal_mem(db) ((db) < dspmem_size)
+#define is_dspword_internal_mem(dw) (((dw) << 1) < dspmem_size)
+
+#ifdef CONFIG_ARCH_OMAP1
+/*
+ * MPUI byteswap/wordswap on/off
+ * default setting: wordswap = all, byteswap = APIMEM only
+ */
+#define mpui_wordswap_on() \
+ omap_set_bits_regl(MPUI_CTRL_WORDSWAP_ALL, MPUI_CTRL_WORDSWAP_MASK, \
+ MPUI_CTRL)
+
+#define mpui_wordswap_off() \
+ omap_set_bits_regl(MPUI_CTRL_WORDSWAP_NONE, MPUI_CTRL_WORDSWAP_MASK, \
+ MPUI_CTRL)
+
+#define mpui_byteswap_on() \
+ omap_set_bits_regl(MPUI_CTRL_BYTESWAP_API, MPUI_CTRL_BYTESWAP_MASK, \
+ MPUI_CTRL)
+
+#define mpui_byteswap_off() \
+ omap_set_bits_regl(MPUI_CTRL_BYTESWAP_NONE, MPUI_CTRL_BYTESWAP_MASK, \
+ MPUI_CTRL)
+
+/*
+ * TC wordswap on / off
+ */
+#define tc_wordswap() \
+ do { \
+ omap_writel(TC_ENDIANISM_SWAP_WORD | TC_ENDIANISM_EN, \
+ TC_ENDIANISM); \
+ } while(0)
+
+#define tc_noswap() omap_clr_bit_regl(TC_ENDIANISM_EN, TC_ENDIANISM)
+
+/*
+ * enable priority registers, EMIF, MPUI control logic
+ */
+#define __dsp_enable() omap_set_bit_regw(ARM_RSTCT1_DSP_RST, ARM_RSTCT1)
+#define __dsp_disable() omap_clr_bit_regw(ARM_RSTCT1_DSP_RST, ARM_RSTCT1)
+#define __dsp_run() omap_set_bit_regw(ARM_RSTCT1_DSP_EN, ARM_RSTCT1)
+#define __dsp_reset() omap_clr_bit_regw(ARM_RSTCT1_DSP_EN, ARM_RSTCT1)
+#endif /* CONFIG_ARCH_OMAP1 */
+
+#ifdef CONFIG_ARCH_OMAP2
+/*
+ * PRCM / IPI control logic
+ *
+ * REVISIT: these macros should probably be static inline functions
+ */
+#define __dsp_core_enable() \
+ do { prm_write_mod_reg(prm_read_mod_reg(OMAP24XX_DSP_MOD, RM_RSTCTRL) \
+ & ~OMAP24XX_RST1_DSP, OMAP24XX_DSP_MOD, RM_RSTCTRL); } while (0)
+#define __dsp_core_disable() \
+ do { prm_write_mod_reg(prm_read_mod_reg(OMAP24XX_DSP_MOD, RM_RSTCTRL) \
+ | OMAP24XX_RST1_DSP, OMAP24XX_DSP_MOD, RM_RSTCTRL); } while (0)
+#define __dsp_per_enable() \
+ do { prm_write_mod_reg(prm_read_mod_reg(OMAP24XX_DSP_MOD, RM_RSTCTRL) \
+ & ~OMAP24XX_RST2_DSP, OMAP24XX_DSP_MOD, RM_RSTCTRL); } while (0)
+#define __dsp_per_disable() \
+ do { prm_write_mod_reg(prm_read_mod_reg(OMAP24XX_DSP_MOD, RM_RSTCTRL) \
+ | OMAP24XX_RST2_DSP, OMAP24XX_DSP_MOD, RM_RSTCTRL); } while (0)
+#endif /* CONFIG_ARCH_OMAP2 */
+
+#if defined(CONFIG_ARCH_OMAP1)
+extern struct clk *dsp_ck_handle;
+extern struct clk *api_ck_handle;
+#elif defined(CONFIG_ARCH_OMAP2)
+extern struct clk *dsp_fck_handle;
+extern struct clk *dsp_ick_handle;
+#endif
+extern dsp_long_t dspmem_base, dspmem_size,
+ daram_base, daram_size,
+ saram_base, saram_size;
+
+enum cpustat_e {
+ CPUSTAT_RESET = 0,
+#ifdef CONFIG_ARCH_OMAP1
+ CPUSTAT_GBL_IDLE,
+ CPUSTAT_CPU_IDLE,
+#endif
+ CPUSTAT_RUN,
+ CPUSTAT_MAX
+};
+
+int dsp_set_rstvect(dsp_long_t adr);
+dsp_long_t dsp_get_rstvect(void);
+void dsp_set_idle_boot_base(dsp_long_t adr, size_t size);
+void dsp_reset_idle_boot_base(void);
+void dsp_cpustat_request(enum cpustat_e req);
+enum cpustat_e dsp_cpustat_get_stat(void);
+u16 dsp_cpustat_get_icrmask(void);
+void dsp_cpustat_set_icrmask(u16 mask);
+void dsp_register_mem_cb(int (*req_cb)(void), void (*rel_cb)(void));
+void dsp_unregister_mem_cb(void);
+
+#if defined(CONFIG_ARCH_OMAP1)
+#define command_dvfs_stop(m) (0)
+#define command_dvfs_start(m) (0)
+#elif defined(CONFIG_ARCH_OMAP2)
+#define command_dvfs_stop(m) \
+ (((m)->cmd_l == KFUNC_POWER) && ((m)->data == DVFS_STOP))
+#define command_dvfs_start(m) \
+ (((m)->cmd_l == KFUNC_POWER) && ((m)->data == DVFS_START))
+#endif
+
+extern struct omap_dsp *omap_dsp;
+
+extern int dsp_late_init(void);
+
+#endif /* __PLAT_OMAP_DSP_DSP_H */
--- /dev/null
- #include <asm/arch/dsp_common.h>
+/*
+ * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
+ *
+ * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/tlbflush.h>
+#include <asm/irq.h>
- #include <asm/arch/tc.h>
++#include <mach/dsp_common.h>
+#include "dsp.h"
+
+#ifdef CONFIG_ARCH_OMAP1
++#include <mach/tc.h>
+#endif
+
+#if defined(CONFIG_ARCH_OMAP1)
+#define dsp_boot_config(mode) omap_writew((mode), MPUI_DSP_BOOT_CONFIG)
+#endif
+#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
+#define dsp_boot_config(mode) writel((mode), DSP_IPI_DSPBOOTCONFIG)
+#endif
+
+struct omap_dsp *omap_dsp;
+
+#if defined(CONFIG_ARCH_OMAP1)
+struct clk *dsp_ck_handle;
+struct clk *api_ck_handle;
+#elif defined(CONFIG_ARCH_OMAP2)
+struct clk *dsp_fck_handle;
+struct clk *dsp_ick_handle;
+#endif
+dsp_long_t dspmem_base, dspmem_size,
+ daram_base, daram_size,
+ saram_base, saram_size;
+
+static struct cpustat {
+ struct mutex lock;
+ enum cpustat_e stat;
+ enum cpustat_e req;
+ u16 icrmask;
+#ifdef CONFIG_ARCH_OMAP1
+ struct {
+ int mpui;
+ int mem;
+ int mem_delayed;
+ } usecount;
+ int (*mem_req_cb)(void);
+ void (*mem_rel_cb)(void);
+#endif
+} cpustat = {
+ .stat = CPUSTAT_RESET,
+ .icrmask = 0xffff,
+};
+
+int dsp_set_rstvect(dsp_long_t adr)
+{
+ unsigned long *dst_adr;
+
+ if (adr >= DSPSPACE_SIZE)
+ return -EINVAL;
+
+ dst_adr = dspbyte_to_virt(DSP_BOOT_ADR_DIRECT);
+ /* word swap */
+ *dst_adr = ((adr & 0xffff) << 16) | (adr >> 16);
+ /* fill 8 bytes! */
+ *(dst_adr + 1) = 0;
+ /* direct boot */
+ dsp_boot_config(DSP_BOOT_CONFIG_DIRECT);
+
+ return 0;
+}
+
+dsp_long_t dsp_get_rstvect(void)
+{
+ unsigned long *dst_adr;
+
+ dst_adr = dspbyte_to_virt(DSP_BOOT_ADR_DIRECT);
+ return ((*dst_adr & 0xffff) << 16) | (*dst_adr >> 16);
+}
+
+#ifdef CONFIG_ARCH_OMAP1
+static void simple_load_code(unsigned char *src_c, u16 *dst, int len)
+{
+ int i;
+ u16 *src = (u16 *)src_c;
+ int len_w;
+
+ /* len must be multiple of 2. */
+ if (len & 1)
+ BUG();
+
+ len_w = len / 2;
+ for (i = 0; i < len_w; i++) {
+ /* byte swap copy */
+ *dst = ((*src & 0x00ff) << 8) |
+ ((*src & 0xff00) >> 8);
+ src++;
+ dst++;
+ }
+}
+
+/* program size must be multiple of 2 */
+#define GBL_IDLE_TEXT_SIZE 52
+#define GBL_IDLE_TEXT_INIT { \
+ /* SAM */ \
+ 0x3c, 0x4a, /* 0x3c4a: MOV 0x4, AR2 */ \
+ 0xf4, 0x41, 0xfc, 0xff, /* 0xf441fcff: AND 0xfcff, *AR2 */ \
+ /* disable WDT */ \
+ 0x76, 0x34, 0x04, 0xb8, /* 0x763404b8: MOV 0x3404, AR3 */ \
+ 0xfb, 0x61, 0x00, 0xf5, /* 0xfb6100f5: MOV 0x00f5, *AR3 */ \
+ 0x9a, /* 0x9a: PORT */ \
+ 0xfb, 0x61, 0x00, 0xa0, /* 0xfb6100a0: MOV 0x00a0, *AR3 */ \
+ 0x9a, /* 0x9a: PORT */ \
+ /* *IER0 = 0, *IER1 = 0 */ \
+ 0x3c, 0x0b, /* 0x3c0b: MOV 0x0, AR3 */ \
+ 0xe6, 0x61, 0x00, /* 0xe66100: MOV 0, *AR3 */ \
+ 0x76, 0x00, 0x45, 0xb8, /* 0x76004508: MOV 0x45, AR3 */ \
+ 0xe6, 0x61, 0x00, /* 0xe66100: MOV 0, *AR3 */ \
+ /* *ICR = 0xffff */ \
+ 0x3c, 0x1b, /* 0x3c1b: MOV 0x1, AR3 */ \
+ 0xfb, 0x61, 0xff, 0xff, /* 0xfb61ffff: MOV 0xffff, *AR3 */ \
+ 0x9a, /* 0x9a: PORT */ \
+ /* HOM */ \
+ 0xf5, 0x41, 0x03, 0x00, /* 0xf5410300: OR 0x0300, *AR2 */ \
+ /* idle and loop forever */ \
+ 0x7a, 0x00, 0x00, 0x0c, /* 0x7a00000c: IDLE */ \
+ 0x4a, 0x7a, /* 0x4a7a: B -6 (infinite loop) */ \
+ 0x20, 0x20, 0x20, /* 0x20: NOP */ \
+}
+
+/* program size must be multiple of 2 */
+#define CPU_IDLE_TEXT_SIZE 48
+#define CPU_IDLE_TEXT_INIT(icrh, icrl) { \
+ /* SAM */ \
+ 0x3c, 0x4b, /* 0x3c4b: MOV 0x4, AR3 */ \
+ 0xf4, 0x61, 0xfc, 0xff, /* 0xf461fcff: AND 0xfcff, *AR3 */ \
+ /* disable WDT */ \
+ 0x76, 0x34, 0x04, 0xb8, /* 0x763404b8: MOV 0x3404, AR3 */ \
+ 0xfb, 0x61, 0x00, 0xf5, /* 0xfb6100f5: MOV 0x00f5, *AR3 */ \
+ 0x9a, /* 0x9a: PORT */ \
+ 0xfb, 0x61, 0x00, 0xa0, /* 0xfb6100a0: MOV 0x00a0, *AR3 */ \
+ 0x9a, /* 0x9a: PORT */ \
+ /* *IER0 = 0, *IER1 = 0 */ \
+ 0x3c, 0x0b, /* 0x3c0b: MOV 0x0, AR3 */ \
+ 0xe6, 0x61, 0x00, /* 0xe66100: MOV 0, *AR3 */ \
+ 0x76, 0x00, 0x45, 0xb8, /* 0x76004508: MOV 0x45, AR3 */ \
+ 0xe6, 0x61, 0x00, /* 0xe66100: MOV 0, *AR3 */ \
+ /* set ICR = icr */ \
+ 0x3c, 0x1b, /* 0x3c1b: MOV AR3 0x1 */ \
+ 0xfb, 0x61, (icrh), (icrl), /* 0xfb61****: MOV *AR3, icr */ \
+ 0x9a, /* 0x9a: PORT */ \
+ /* idle and loop forever */ \
+ 0x7a, 0x00, 0x00, 0x0c, /* 0x7a00000c: IDLE */ \
+ 0x4a, 0x7a, /* 0x4a7a: B -6 (infinite loop) */ \
+ 0x20, 0x20, 0x20 /* 0x20: nop */ \
+}
+
+/*
+ * idle_boot base:
+ * Initialized with DSP_BOOT_ADR_MPUI (=0x010000).
+ * This value is used before DSP Gateway driver is initialized.
+ * DSP Gateway driver will overwrite this value with other value,
+ * to avoid confliction with the user program.
+ */
+static dsp_long_t idle_boot_base = DSP_BOOT_ADR_MPUI;
+
+static void dsp_gbl_idle(void)
+{
+ unsigned char idle_text[GBL_IDLE_TEXT_SIZE] = GBL_IDLE_TEXT_INIT;
+
+ __dsp_reset();
+ clk_enable(api_ck_handle);
+
+#if 0
+ dsp_boot_config(DSP_BOOT_CONFIG_IDLE);
+#endif
+ simple_load_code(idle_text, dspbyte_to_virt(idle_boot_base),
+ GBL_IDLE_TEXT_SIZE);
+ if (idle_boot_base == DSP_BOOT_ADR_MPUI)
+ dsp_boot_config(DSP_BOOT_CONFIG_MPUI);
+ else
+ dsp_set_rstvect(idle_boot_base);
+
+ __dsp_run();
+ udelay(100); /* to make things stable */
+ clk_disable(api_ck_handle);
+}
+
+static void dsp_cpu_idle(void)
+{
+ u16 icr_tmp;
+ unsigned char icrh, icrl;
+
+ __dsp_reset();
+ clk_enable(api_ck_handle);
+
+ /*
+ * icr settings:
+ * DMA should not sleep for DARAM/SARAM access
+ * DPLL should not sleep while any other domain is active
+ */
+ icr_tmp = cpustat.icrmask & ~(DSPREG_ICR_DMA | DSPREG_ICR_DPLL);
+ icrh = icr_tmp >> 8;
+ icrl = icr_tmp & 0xff;
+ {
+ unsigned char idle_text[CPU_IDLE_TEXT_SIZE] = CPU_IDLE_TEXT_INIT(icrh, icrl);
+ simple_load_code(idle_text, dspbyte_to_virt(idle_boot_base),
+ CPU_IDLE_TEXT_SIZE);
+ }
+ if (idle_boot_base == DSP_BOOT_ADR_MPUI)
+ dsp_boot_config(DSP_BOOT_CONFIG_MPUI);
+ else
+ dsp_set_rstvect(idle_boot_base);
+ __dsp_run();
+ udelay(100); /* to make things stable */
+ clk_disable(api_ck_handle);
+}
+
+void dsp_set_idle_boot_base(dsp_long_t adr, size_t size)
+{
+ if (adr == idle_boot_base)
+ return;
+ idle_boot_base = adr;
+ if ((size < GBL_IDLE_TEXT_SIZE) ||
+ (size < CPU_IDLE_TEXT_SIZE)) {
+ printk(KERN_ERR
+ "omapdsp: size for idle program is not enough!\n");
+ BUG();
+ }
+
+ /* restart idle program with new base address */
+ if (cpustat.stat == CPUSTAT_GBL_IDLE)
+ dsp_gbl_idle();
+ if (cpustat.stat == CPUSTAT_CPU_IDLE)
+ dsp_cpu_idle();
+}
+
+void dsp_reset_idle_boot_base(void)
+{
+ idle_boot_base = DSP_BOOT_ADR_MPUI;
+}
+#else
+void dsp_reset_idle_boot_base(void) { }
+#endif /* CONFIG_ARCH_OMAP1 */
+
+static int init_done;
+
+static int omap_dsp_init(void)
+{
+ mutex_init(&cpustat.lock);
+
+ dspmem_size = 0;
+#ifdef CONFIG_ARCH_OMAP15XX
+ if (cpu_is_omap15xx()) {
+ dspmem_base = OMAP1510_DSP_BASE;
+ dspmem_size = OMAP1510_DSP_SIZE;
+ daram_base = OMAP1510_DARAM_BASE;
+ daram_size = OMAP1510_DARAM_SIZE;
+ saram_base = OMAP1510_SARAM_BASE;
+ saram_size = OMAP1510_SARAM_SIZE;
+ }
+#endif
+#ifdef CONFIG_ARCH_OMAP16XX
+ if (cpu_is_omap16xx()) {
+ dspmem_base = OMAP16XX_DSP_BASE;
+ dspmem_size = OMAP16XX_DSP_SIZE;
+ daram_base = OMAP16XX_DARAM_BASE;
+ daram_size = OMAP16XX_DARAM_SIZE;
+ saram_base = OMAP16XX_SARAM_BASE;
+ saram_size = OMAP16XX_SARAM_SIZE;
+ }
+#endif
+#ifdef CONFIG_ARCH_OMAP24XX
+ if (cpu_is_omap24xx()) {
+ dspmem_base = DSP_MEM_24XX_VIRT;
+ dspmem_size = DSP_MEM_24XX_SIZE;
+ daram_base = OMAP24XX_DARAM_BASE;
+ daram_size = OMAP24XX_DARAM_SIZE;
+ saram_base = OMAP24XX_SARAM_BASE;
+ saram_size = OMAP24XX_SARAM_SIZE;
+ }
+#endif
+#ifdef CONFIG_ARCH_OMAP34XX
+ /* To be Revisited for 3430 */
+ if (cpu_is_omap34xx()) {
+ return -ENODEV;
+ }
+#endif
+ if (dspmem_size == 0) {
+ printk(KERN_ERR "omapdsp: unsupported omap architecture.\n");
+ return -ENODEV;
+ }
+
+#if defined(CONFIG_ARCH_OMAP1)
+ dsp_ck_handle = clk_get(NULL, "dsp_ck");
+ if (IS_ERR(dsp_ck_handle)) {
+ printk(KERN_ERR "omapdsp: could not acquire dsp_ck handle.\n");
+ return PTR_ERR(dsp_ck_handle);
+ }
+
+ api_ck_handle = clk_get(NULL, "api_ck");
+ if (IS_ERR(api_ck_handle)) {
+ printk(KERN_ERR "omapdsp: could not acquire api_ck handle.\n");
+ if (dsp_ck_handle != NULL)
+ clk_put(dsp_ck_handle);
+ return PTR_ERR(api_ck_handle);
+ }
+
+ /* This is needed for McBSP init, released in late_initcall */
+ clk_enable(api_ck_handle);
+
+ __dsp_enable();
+ mpui_byteswap_off();
+ mpui_wordswap_on();
+ tc_wordswap();
+#elif defined(CONFIG_ARCH_OMAP2)
+ dsp_fck_handle = clk_get(NULL, "dsp_fck");
+ if (IS_ERR(dsp_fck_handle)) {
+ printk(KERN_ERR "omapdsp: could not acquire dsp_fck handle.\n");
+ return PTR_ERR(dsp_fck_handle);
+ }
+
+# if defined(CONFIG_ARCH_OMAP2420)
+ dsp_ick_handle = clk_get(NULL, "dsp_ick");
+# elif defined(CONFIG_ARCH_OMAP2430)
+ /*
+ * 2430 has no separate switch for DSP ICLK, but this at least
+ * involves the minimal change to the rest of the code.
+ */
+ dsp_ick_handle = clk_get(NULL, "iva2_1_ick");
+# endif
+ if (IS_ERR(dsp_ick_handle)) {
+ printk(KERN_ERR "omapdsp: could not acquire dsp_ick handle.\n");
+ if (dsp_fck_handle != NULL)
+ clk_put(dsp_fck_handle);
+ return PTR_ERR(dsp_ick_handle);
+ }
+#endif
+
+ init_done = 1;
+ pr_info("omap_dsp_init() done\n");
+ return 0;
+}
+
+#if defined(CONFIG_ARCH_OMAP1)
+static int __dsp_late_init(void)
+{
+ clk_disable(api_ck_handle);
+ return 0;
+}
+late_initcall(__dsp_late_init);
+#endif
+
+static void dsp_cpustat_update(void)
+{
+ if (!init_done)
+ omap_dsp_init();
+
+ if (cpustat.req == CPUSTAT_RUN) {
+ if (cpustat.stat < CPUSTAT_RUN) {
+#if defined(CONFIG_ARCH_OMAP1)
+ __dsp_reset();
+ clk_enable(api_ck_handle);
+ udelay(10);
+ __dsp_run();
+#elif defined(CONFIG_ARCH_OMAP2)
+ __dsp_core_disable();
+ udelay(10);
+ __dsp_core_enable();
+#endif
+ cpustat.stat = CPUSTAT_RUN;
+ }
+ return;
+ }
+
+ /* cpustat.req < CPUSTAT_RUN */
+
+ if (cpustat.stat == CPUSTAT_RUN) {
+#ifdef CONFIG_ARCH_OMAP1
+ clk_disable(api_ck_handle);
+#endif
+ }
+
+#ifdef CONFIG_ARCH_OMAP1
+ /*
+ * (1) when ARM wants DARAM access, MPUI should be SAM and
+ * DSP needs to be on.
+ * (2) if any bits of icr is masked, we can not enter global idle.
+ */
+ if ((cpustat.req == CPUSTAT_CPU_IDLE) ||
+ (cpustat.usecount.mem > 0) ||
+ (cpustat.usecount.mem_delayed > 0) ||
+ ((cpustat.usecount.mpui > 0) && (cpustat.icrmask != 0xffff))) {
+ if (cpustat.stat != CPUSTAT_CPU_IDLE) {
+ dsp_cpu_idle();
+ cpustat.stat = CPUSTAT_CPU_IDLE;
+ }
+ return;
+ }
+
+ /*
+ * when ARM only needs MPUI access, MPUI can be HOM and
+ * DSP can be idling.
+ */
+ if ((cpustat.req == CPUSTAT_GBL_IDLE) ||
+ (cpustat.usecount.mpui > 0)) {
+ if (cpustat.stat != CPUSTAT_GBL_IDLE) {
+ dsp_gbl_idle();
+ cpustat.stat = CPUSTAT_GBL_IDLE;
+ }
+ return;
+ }
+#endif /* CONFIG_ARCH_OMAP1 */
+
+ /*
+ * no user, no request
+ */
+ if (cpustat.stat != CPUSTAT_RESET) {
+#if defined(CONFIG_ARCH_OMAP1)
+ __dsp_reset();
+#elif defined(CONFIG_ARCH_OMAP2)
+ __dsp_core_disable();
+#endif
+ cpustat.stat = CPUSTAT_RESET;
+ }
+}
+
+void dsp_cpustat_request(enum cpustat_e req)
+{
+ mutex_lock(&cpustat.lock);
+ cpustat.req = req;
+ dsp_cpustat_update();
+ mutex_unlock(&cpustat.lock);
+}
+
+enum cpustat_e dsp_cpustat_get_stat(void)
+{
+ return cpustat.stat;
+}
+
+u16 dsp_cpustat_get_icrmask(void)
+{
+ return cpustat.icrmask;
+}
+
+void dsp_cpustat_set_icrmask(u16 mask)
+{
+ mutex_lock(&cpustat.lock);
+ cpustat.icrmask = mask;
+ dsp_cpustat_update();
+ mutex_unlock(&cpustat.lock);
+}
+
+#ifdef CONFIG_ARCH_OMAP1
+void omap_dsp_request_mpui(void)
+{
+ mutex_lock(&cpustat.lock);
+ if (cpustat.usecount.mpui++ == 0)
+ dsp_cpustat_update();
+ mutex_unlock(&cpustat.lock);
+}
+
+void omap_dsp_release_mpui(void)
+{
+ mutex_lock(&cpustat.lock);
+ if (cpustat.usecount.mpui-- == 0) {
+ printk(KERN_ERR
+ "omapdsp: unbalanced mpui request/release detected.\n"
+ " cpustat.usecount.mpui is going to be "
+ "less than zero! ... fixed to be zero.\n");
+ cpustat.usecount.mpui = 0;
+ }
+ if (cpustat.usecount.mpui == 0)
+ dsp_cpustat_update();
+ mutex_unlock(&cpustat.lock);
+}
+
+#if defined(CONFIG_ARCH_OMAP1) && defined(CONFIG_OMAP_MMU_FWK)
+int omap_dsp_request_mem(void)
+{
+ int ret = 0;
+
+ mutex_lock(&cpustat.lock);
+ if ((cpustat.usecount.mem++ == 0) &&
+ (cpustat.usecount.mem_delayed == 0)) {
+ if (cpustat.mem_req_cb) {
+ if ((ret = cpustat.mem_req_cb()) < 0) {
+ cpustat.usecount.mem--;
+ goto out;
+ }
+ }
+ dsp_cpustat_update();
+ }
+out:
+ mutex_unlock(&cpustat.lock);
+
+ return ret;
+}
+
+/*
+ * release_mem will be delayed.
+ */
+static void do_release_mem(struct work_struct *dummy)
+{
+ mutex_lock(&cpustat.lock);
+ cpustat.usecount.mem_delayed = 0;
+ if (cpustat.usecount.mem == 0) {
+ dsp_cpustat_update();
+ if (cpustat.mem_rel_cb)
+ cpustat.mem_rel_cb();
+ }
+ mutex_unlock(&cpustat.lock);
+}
+
+static DECLARE_DELAYED_WORK(mem_rel_work, do_release_mem);
+
+int omap_dsp_release_mem(void)
+{
+ mutex_lock(&cpustat.lock);
+
+ /* cancel previous release work */
+ cancel_delayed_work(&mem_rel_work);
+ cpustat.usecount.mem_delayed = 0;
+
+ if (cpustat.usecount.mem-- == 0) {
+ printk(KERN_ERR
+ "omapdsp: unbalanced memory request/release detected.\n"
+ " cpustat.usecount.mem is going to be "
+ "less than zero! ... fixed to be zero.\n");
+ cpustat.usecount.mem = 0;
+ }
+ if (cpustat.usecount.mem == 0) {
+ cpustat.usecount.mem_delayed = 1;
+ schedule_delayed_work(&mem_rel_work, HZ);
+ }
+
+ mutex_unlock(&cpustat.lock);
+
+ return 0;
+}
+#endif
+
+void dsp_register_mem_cb(int (*req_cb)(void), void (*rel_cb)(void))
+{
+ mutex_lock(&cpustat.lock);
+
+ cpustat.mem_req_cb = req_cb;
+ cpustat.mem_rel_cb = rel_cb;
+
+ /*
+ * This function must be called while mem is enabled!
+ */
+ BUG_ON(cpustat.usecount.mem == 0);
+
+ mutex_unlock(&cpustat.lock);
+}
+
+void dsp_unregister_mem_cb(void)
+{
+ mutex_lock(&cpustat.lock);
+ cpustat.mem_req_cb = NULL;
+ cpustat.mem_rel_cb = NULL;
+ mutex_unlock(&cpustat.lock);
+}
+#else
+void dsp_register_mem_cb(int (*req_cb)(void), void (*rel_cb)(void)) { }
+void dsp_unregister_mem_cb(void) { }
+#endif /* CONFIG_ARCH_OMAP1 */
+
+arch_initcall(omap_dsp_init);
+
+#ifdef CONFIG_ARCH_OMAP1
+EXPORT_SYMBOL(omap_dsp_request_mpui);
+EXPORT_SYMBOL(omap_dsp_release_mpui);
+#if defined(CONFIG_ARCH_OMAP1) && defined(CONFIG_OMAP_MMU_FWK)
+EXPORT_SYMBOL(omap_dsp_request_mem);
+EXPORT_SYMBOL(omap_dsp_release_mem);
+#endif
+#endif /* CONFIG_ARCH_OMAP1 */
+
+#ifdef CONFIG_OMAP_DSP_MODULE
+#if defined(CONFIG_ARCH_OMAP1)
+EXPORT_SYMBOL(dsp_ck_handle);
+EXPORT_SYMBOL(api_ck_handle);
+#elif defined(CONFIG_ARCH_OMAP2)
+EXPORT_SYMBOL(dsp_fck_handle);
+EXPORT_SYMBOL(dsp_ick_handle);
+#endif
+EXPORT_SYMBOL(omap_dsp);
+EXPORT_SYMBOL(dspmem_base);
+EXPORT_SYMBOL(dspmem_size);
+EXPORT_SYMBOL(daram_base);
+EXPORT_SYMBOL(daram_size);
+EXPORT_SYMBOL(saram_base);
+EXPORT_SYMBOL(saram_size);
+EXPORT_SYMBOL(dsp_set_rstvect);
+EXPORT_SYMBOL(dsp_get_rstvect);
+#ifdef CONFIG_ARCH_OMAP1
+EXPORT_SYMBOL(dsp_set_idle_boot_base);
+EXPORT_SYMBOL(dsp_reset_idle_boot_base);
+#endif /* CONFIG_ARCH_OMAP1 */
+EXPORT_SYMBOL(dsp_cpustat_request);
+EXPORT_SYMBOL(dsp_cpustat_get_stat);
+EXPORT_SYMBOL(dsp_cpustat_get_icrmask);
+EXPORT_SYMBOL(dsp_cpustat_set_icrmask);
+EXPORT_SYMBOL(dsp_register_mem_cb);
+EXPORT_SYMBOL(dsp_unregister_mem_cb);
+
+EXPORT_SYMBOL(__cpu_flush_kern_tlb_range);
+EXPORT_SYMBOL(cpu_architecture);
+EXPORT_SYMBOL(pmd_clear_bad);
+#endif
--- /dev/null
- #include <asm/arch/mailbox.h>
- #include <asm/arch/dsp.h>
- #include <asm/arch/dsp_common.h>
+/*
+ * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
+ *
+ * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <asm/delay.h>
++#include <mach/mailbox.h>
++#include <mach/dsp.h>
++#include <mach/dsp_common.h>
+#include "dsp_mbcmd.h"
+#include "dsp.h"
+#include "ipbuf.h"
+
+MODULE_AUTHOR("Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>");
+MODULE_DESCRIPTION("OMAP DSP driver module");
+MODULE_LICENSE("GPL");
+
+static struct sync_seq *mbseq;
+static u16 mbseq_expect_tmp;
+static u16 *mbseq_expect = &mbseq_expect_tmp;
+
+extern int dsp_mem_late_init(void);
+
+/*
+ * mailbox commands
+ */
+extern void mbox_wdsnd(struct mbcmd *mb);
+extern void mbox_wdreq(struct mbcmd *mb);
+extern void mbox_bksnd(struct mbcmd *mb);
+extern void mbox_bkreq(struct mbcmd *mb);
+extern void mbox_bkyld(struct mbcmd *mb);
+extern void mbox_bksndp(struct mbcmd *mb);
+extern void mbox_bkreqp(struct mbcmd *mb);
+extern void mbox_tctl(struct mbcmd *mb);
+extern void mbox_poll(struct mbcmd *mb);
+#ifdef OLD_BINARY_SUPPORT
+/* v3.3 obsolete */
+extern void mbox_wdt(struct mbcmd *mb);
+#endif
+extern void mbox_suspend(struct mbcmd *mb);
+static void mbox_kfunc(struct mbcmd *mb);
+extern void mbox_tcfg(struct mbcmd *mb);
+extern void mbox_tadd(struct mbcmd *mb);
+extern void mbox_tdel(struct mbcmd *mb);
+extern void mbox_dspcfg(struct mbcmd *mb);
+extern void mbox_regrw(struct mbcmd *mb);
+extern void mbox_getvar(struct mbcmd *mb);
+extern void mbox_err(struct mbcmd *mb);
+extern void mbox_dbg(struct mbcmd *mb);
+
+static const struct cmdinfo
+ cif_wdsnd = { "WDSND", CMD_L_TYPE_TID, mbox_wdsnd },
+ cif_wdreq = { "WDREQ", CMD_L_TYPE_TID, mbox_wdreq },
+ cif_bksnd = { "BKSND", CMD_L_TYPE_TID, mbox_bksnd },
+ cif_bkreq = { "BKREQ", CMD_L_TYPE_TID, mbox_bkreq },
+ cif_bkyld = { "BKYLD", CMD_L_TYPE_NULL, mbox_bkyld },
+ cif_bksndp = { "BKSNDP", CMD_L_TYPE_TID, mbox_bksndp },
+ cif_bkreqp = { "BKREQP", CMD_L_TYPE_TID, mbox_bkreqp },
+ cif_tctl = { "TCTL", CMD_L_TYPE_TID, mbox_tctl },
+ cif_poll = { "POLL", CMD_L_TYPE_NULL, mbox_poll },
+#ifdef OLD_BINARY_SUPPORT
+ /* v3.3 obsolete */
+ cif_wdt = { "WDT", CMD_L_TYPE_NULL, mbox_wdt },
+#endif
+ cif_runlevel = { "RUNLEVEL", CMD_L_TYPE_SUBCMD, NULL },
+ cif_pm = { "PM", CMD_L_TYPE_SUBCMD, NULL },
+ cif_suspend = { "SUSPEND", CMD_L_TYPE_NULL, mbox_suspend },
+ cif_kfunc = { "KFUNC", CMD_L_TYPE_SUBCMD, mbox_kfunc },
+ cif_tcfg = { "TCFG", CMD_L_TYPE_TID, mbox_tcfg },
+ cif_tadd = { "TADD", CMD_L_TYPE_TID, mbox_tadd },
+ cif_tdel = { "TDEL", CMD_L_TYPE_TID, mbox_tdel },
+ cif_tstop = { "TSTOP", CMD_L_TYPE_TID, NULL },
+ cif_dspcfg = { "DSPCFG", CMD_L_TYPE_SUBCMD, mbox_dspcfg },
+ cif_regrw = { "REGRW", CMD_L_TYPE_SUBCMD, mbox_regrw },
+ cif_getvar = { "GETVAR", CMD_L_TYPE_SUBCMD, mbox_getvar },
+ cif_setvar = { "SETVAR", CMD_L_TYPE_SUBCMD, NULL },
+ cif_err = { "ERR", CMD_L_TYPE_SUBCMD, mbox_err },
+ cif_dbg = { "DBG", CMD_L_TYPE_NULL, mbox_dbg };
+
+#define MBOX_CMD_MAX 0x80
+const struct cmdinfo *cmdinfo[MBOX_CMD_MAX] = {
+ [MBOX_CMD_DSP_WDSND] = &cif_wdsnd,
+ [MBOX_CMD_DSP_WDREQ] = &cif_wdreq,
+ [MBOX_CMD_DSP_BKSND] = &cif_bksnd,
+ [MBOX_CMD_DSP_BKREQ] = &cif_bkreq,
+ [MBOX_CMD_DSP_BKYLD] = &cif_bkyld,
+ [MBOX_CMD_DSP_BKSNDP] = &cif_bksndp,
+ [MBOX_CMD_DSP_BKREQP] = &cif_bkreqp,
+ [MBOX_CMD_DSP_TCTL] = &cif_tctl,
+ [MBOX_CMD_DSP_POLL] = &cif_poll,
+#ifdef OLD_BINARY_SUPPORT
+ [MBOX_CMD_DSP_WDT] = &cif_wdt, /* v3.3 obsolete */
+#endif
+ [MBOX_CMD_DSP_RUNLEVEL] = &cif_runlevel,
+ [MBOX_CMD_DSP_PM] = &cif_pm,
+ [MBOX_CMD_DSP_SUSPEND] = &cif_suspend,
+ [MBOX_CMD_DSP_KFUNC] = &cif_kfunc,
+ [MBOX_CMD_DSP_TCFG] = &cif_tcfg,
+ [MBOX_CMD_DSP_TADD] = &cif_tadd,
+ [MBOX_CMD_DSP_TDEL] = &cif_tdel,
+ [MBOX_CMD_DSP_TSTOP] = &cif_tstop,
+ [MBOX_CMD_DSP_DSPCFG] = &cif_dspcfg,
+ [MBOX_CMD_DSP_REGRW] = &cif_regrw,
+ [MBOX_CMD_DSP_GETVAR] = &cif_getvar,
+ [MBOX_CMD_DSP_SETVAR] = &cif_setvar,
+ [MBOX_CMD_DSP_ERR] = &cif_err,
+ [MBOX_CMD_DSP_DBG] = &cif_dbg,
+};
+
+#define list_for_each_entry_safe_natural(p,n,h,m) \
+ list_for_each_entry_safe(p,n,h,m)
+#define __BUILD_KFUNC(fn, dir) \
+static int __dsp_kfunc_##fn##_devices(struct omap_dsp *dsp, int type, int stage)\
+{ \
+ struct dsp_kfunc_device *p, *tmp; \
+ int ret, fail = 0; \
+ \
+ list_for_each_entry_safe_##dir(p, tmp, dsp->kdev_list, entry) { \
+ if (type && (p->type != type)) \
+ continue; \
+ if (p->fn == NULL) \
+ continue; \
+ ret = p->fn(p, stage); \
+ if (ret) { \
+ printk(KERN_ERR "%s %s failed\n", #fn, p->name); \
+ fail++; \
+ } \
+ } \
+ return fail; \
+}
+#define BUILD_KFUNC(fn, dir) \
+__BUILD_KFUNC(fn, dir) \
+static inline int dsp_kfunc_##fn##_devices(struct omap_dsp *dsp) \
+{ \
+ return __dsp_kfunc_##fn##_devices(dsp, 0, 0); \
+}
+#define BUILD_KFUNC_CTL(fn, dir) \
+__BUILD_KFUNC(fn, dir) \
+static inline int dsp_kfunc_##fn##_devices(struct omap_dsp *dsp, int type, int stage) \
+{ \
+ return __dsp_kfunc_##fn##_devices(dsp, type, stage); \
+}
+
+BUILD_KFUNC(probe, natural)
+BUILD_KFUNC(remove, reverse)
+BUILD_KFUNC_CTL(enable, natural)
+BUILD_KFUNC_CTL(disable, reverse)
+
+int sync_with_dsp(u16 *adr, u16 val, int try_cnt)
+{
+ int try;
+
+ if (*(volatile u16 *)adr == val)
+ return 0;
+
+ for (try = 0; try < try_cnt; try++) {
+ udelay(1);
+ if (*(volatile u16 *)adr == val) {
+ /* success! */
+ pr_info("omapdsp: sync_with_dsp(): try = %d\n", try);
+ return 0;
+ }
+ }
+
+ /* fail! */
+ return -1;
+}
+
+static int mbcmd_sender_prepare(void *data)
+{
+ struct mb_exarg *arg = data;
+ int i, ret = 0;
+ /*
+ * even if ipbuf_sys_ad is in DSP internal memory,
+ * dsp_mem_enable() never cause to call PM mailbox command
+ * because in that case DSP memory should be always enabled.
+ * (see ipbuf_sys_hold_mem_active in ipbuf.c)
+ *
+ * Therefore, we can call this function here safely.
+ */
+ dsp_mem_enable(ipbuf_sys_ad);
+ if (sync_with_dsp(&ipbuf_sys_ad->s, TID_FREE, 10) < 0) {
+ printk(KERN_ERR "omapdsp: ipbuf_sys_ad is busy.\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ for (i = 0; i < arg->argc; i++) {
+ ipbuf_sys_ad->d[i] = arg->argv[i];
+ }
+ ipbuf_sys_ad->s = arg->tid;
+ out:
+ dsp_mem_disable(ipbuf_sys_ad);
+ return ret;
+}
+
+/*
+ * __dsp_mbcmd_send_exarg(): mailbox dispatcher
+ */
+int __dsp_mbcmd_send_exarg(struct mbcmd *mb, struct mb_exarg *arg,
+ int recovery_flag)
+{
+ int ret = 0;
+
+ if (unlikely(omap_dsp->enabled == 0)) {
+ ret = dsp_kfunc_enable_devices(omap_dsp,
+ DSP_KFUNC_DEV_TYPE_COMMON, 0);
+ if (ret == 0)
+ omap_dsp->enabled = 1;
+ }
+
+ /*
+ * while MMU fault is set,
+ * only recovery command can be executed
+ */
+ if (dsp_err_isset(ERRCODE_MMU) && !recovery_flag) {
+ printk(KERN_ERR
+ "mbox: mmu interrupt is set. %s is aborting.\n",
+ cmd_name(*mb));
+ goto out;
+ }
+
+ ret = omap_mbox_msg_send(omap_dsp->mbox,
+ *(mbox_msg_t *)mb, (void*)arg);
+ if (ret)
+ goto out;
+
+ if (mbseq)
+ mbseq->ad_arm++;
+
+ mblog_add(mb, DIR_A2D);
+ out:
+ return ret;
+}
+
+int dsp_mbcmd_send_and_wait_exarg(struct mbcmd *mb, struct mb_exarg *arg,
+ wait_queue_head_t *q)
+{
+ int ret;
+
+ DEFINE_WAIT(wait);
+ prepare_to_wait(q, &wait, TASK_INTERRUPTIBLE);
+ ret = dsp_mbcmd_send_exarg(mb, arg);
+ if (ret < 0)
+ goto out;
+ schedule_timeout(DSP_TIMEOUT);
+ out:
+ finish_wait(q, &wait);
+ return ret;
+}
+
+/*
+ * mbcmd receiver
+ */
+static int mbcmd_receiver(void* msg)
+{
+ struct mbcmd *mb = (struct mbcmd *)&msg;
+
+ if (cmdinfo[mb->cmd_h] == NULL) {
+ printk(KERN_ERR
+ "invalid message (%08x) for mbcmd_receiver().\n",
+ (mbox_msg_t)msg);
+ return -1;
+ }
+
+ (*mbseq_expect)++;
+
+ mblog_add(mb, DIR_D2A);
+
+ /* call handler for the command */
+ if (cmdinfo[mb->cmd_h]->handler)
+ cmdinfo[mb->cmd_h]->handler(mb);
+ else
+ printk(KERN_ERR "mbox: %s is not allowed from DSP.\n",
+ cmd_name(*mb));
+ return 0;
+}
+
+static int mbsync_hold_mem_active;
+
+void dsp_mbox_start(void)
+{
+ omap_mbox_init_seq(omap_dsp->mbox);
+ mbseq_expect_tmp = 0;
+}
+
+void dsp_mbox_stop(void)
+{
+ mbseq = NULL;
+ mbseq_expect = &mbseq_expect_tmp;
+}
+
+int dsp_mbox_config(void *p)
+{
+ unsigned long flags;
+
+ if (dsp_address_validate(p, sizeof(struct sync_seq), "mbseq") < 0)
+ return -1;
+ if (dsp_mem_type(p, sizeof(struct sync_seq)) != MEM_TYPE_EXTERN) {
+ printk(KERN_WARNING
+ "omapdsp: mbseq is placed in DSP internal memory.\n"
+ " It will prevent DSP from idling.\n");
+ mbsync_hold_mem_active = 1;
+ /*
+ * dsp_mem_enable() never fails because
+ * it has been already enabled in dspcfg process and
+ * this will just increment the usecount.
+ */
+ dsp_mem_enable((void *)daram_base);
+ }
+
+ local_irq_save(flags);
+ mbseq = p;
+ mbseq->da_arm = mbseq_expect_tmp;
+ mbseq_expect = &mbseq->da_arm;
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static int __init dsp_mbox_init(void)
+{
+ omap_dsp->mbox = omap_mbox_get("dsp");
+ if (IS_ERR(omap_dsp->mbox)) {
+ printk(KERN_ERR "failed to get mailbox handler for DSP.\n");
+ return -ENODEV;
+ }
+
+ omap_dsp->mbox->rxq->callback = mbcmd_receiver;
+ omap_dsp->mbox->txq->callback = mbcmd_sender_prepare;
+
+ return 0;
+}
+
+static void dsp_mbox_exit(void)
+{
+ omap_dsp->mbox->txq->callback = NULL;
+ omap_dsp->mbox->rxq->callback = NULL;
+
+ omap_mbox_put(omap_dsp->mbox);
+
+ if (mbsync_hold_mem_active) {
+ dsp_mem_disable((void *)daram_base);
+ mbsync_hold_mem_active = 0;
+ }
+}
+
+/*
+ * kernel function dispatcher
+ */
+extern void mbox_fbctl_upd(void);
+extern void mbox_fbctl_disable(struct mbcmd *mb);
+
+static void mbox_kfunc_fbctl(struct mbcmd *mb)
+{
+ switch (mb->data) {
+ case FBCTL_UPD:
+ mbox_fbctl_upd();
+ break;
+ case FBCTL_DISABLE:
+ mbox_fbctl_disable(mb);
+ break;
+ default:
+ printk(KERN_ERR
+ "mbox: Unknown FBCTL from DSP: 0x%04x\n", mb->data);
+ }
+}
+
+/*
+ * dspgw: KFUNC message handler
+ */
+static void mbox_kfunc_power(unsigned short data)
+{
+ int ret = -1;
+
+ switch (data) {
+ case DVFS_START: /* ACK from DSP */
+ /* TBD */
+ break;
+ case AUDIO_PWR_UP:
+ ret = dsp_kfunc_enable_devices(omap_dsp,
+ DSP_KFUNC_DEV_TYPE_AUDIO, 0);
+ if (ret == 0)
+ ret++;
+ break;
+ case AUDIO_PWR_DOWN: /* == AUDIO_PWR_DOWN1 */
+ ret = dsp_kfunc_disable_devices(omap_dsp,
+ DSP_KFUNC_DEV_TYPE_AUDIO, 1);
+ break;
+ case AUDIO_PWR_DOWN2:
+ ret = dsp_kfunc_disable_devices(omap_dsp,
+ DSP_KFUNC_DEV_TYPE_AUDIO, 2);
+ break;
+ case DSP_PWR_DOWN:
+ ret = dsp_kfunc_disable_devices(omap_dsp,
+ DSP_KFUNC_DEV_TYPE_COMMON, 0);
+ if (ret == 0)
+ omap_dsp->enabled = 0;
+ break;
+ default:
+ printk(KERN_ERR
+ "mailbox: Unknown PWR from DSP: 0x%04x\n", data);
+ break;
+ }
+
+ if (unlikely(ret < 0)) {
+ printk(KERN_ERR "mailbox: PWR(0x%04x) failed\n", data);
+ return;
+ }
+
+ if (likely(ret == 0))
+ return;
+
+ mbcompose_send(KFUNC, KFUNC_POWER, data);
+}
+
+static void mbox_kfunc(struct mbcmd *mb)
+{
+ switch (mb->cmd_l) {
+ case KFUNC_FBCTL:
+ mbox_kfunc_fbctl(mb);
+ break;
+ case KFUNC_POWER:
+ mbox_kfunc_power(mb->data);
+ break;
+ default:
+ printk(KERN_ERR
+ "mbox: Unknown KFUNC from DSP: 0x%02x\n", mb->cmd_l);
+ }
+}
+
+#if defined(CONFIG_ARCH_OMAP1)
+static inline void dsp_clk_enable(void) {}
+static inline void dsp_clk_disable(void) {}
+#elif defined(CONFIG_ARCH_OMAP2)
+static inline void dsp_clk_enable(void)
+{
+ clk_enable(dsp_fck_handle);
+ clk_enable(dsp_ick_handle);
+ __dsp_per_enable();
+}
+static inline void dsp_clk_disable(void)
+{
+ __dsp_per_disable();
+ clk_disable(dsp_ick_handle);
+ clk_disable(dsp_fck_handle);
+}
+#endif
+
+int dsp_late_init(void)
+{
+ int ret;
+
+ dsp_clk_enable();
+ ret = dsp_mem_late_init();
+ if (ret)
+ return ret;
+ ret = dsp_mbox_init();
+ if (ret)
+ goto fail_mbox;
+#ifdef CONFIG_ARCH_OMAP1
+ dsp_set_idle_boot_base(IDLEPG_BASE, IDLEPG_SIZE);
+#endif
+ ret = dsp_kfunc_enable_devices(omap_dsp,
+ DSP_KFUNC_DEV_TYPE_COMMON, 0);
+ if (ret)
+ goto fail_kfunc;
+ omap_dsp->enabled = 1;
+
+ return 0;
+
+ fail_kfunc:
+ dsp_mbox_exit();
+ fail_mbox:
+ dsp_clk_disable();
+
+ return ret;
+}
+
+extern int dsp_ctl_core_init(void);
+extern void dsp_ctl_core_exit(void);
+extern int dsp_ctl_init(void);
+extern void dsp_ctl_exit(void);
+extern int dsp_mem_init(void);
+extern void dsp_mem_exit(void);
+extern void mblog_init(void);
+extern void mblog_exit(void);
+extern int dsp_taskmod_init(void);
+extern void dsp_taskmod_exit(void);
+
+/*
+ * driver functions
+ */
+static int __init dsp_drv_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct omap_dsp *info;
+ struct dsp_platform_data *pdata = pdev->dev.platform_data;
+
+ dev_info(&pdev->dev, "OMAP DSP driver initialization\n");
+
+ info = kzalloc(sizeof(struct omap_dsp), GFP_KERNEL);
+ if (unlikely(info == NULL)) {
+ dev_dbg(&pdev->dev, "no memory for info\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, info);
+ omap_dsp = info;
+
+ mutex_init(&info->lock);
+ info->dev = &pdev->dev;
+ info->kdev_list = &pdata->kdev_list;
+
+ ret = dsp_kfunc_probe_devices(info);
+ if (ret) {
+ ret = -ENXIO;
+ goto fail_kfunc;
+ }
+
+ ret = dsp_ctl_core_init();
+ if (ret)
+ goto fail_ctl_core;
+ ret = dsp_mem_init();
+ if (ret)
+ goto fail_mem;
+ ret = dsp_ctl_init();
+ if (unlikely(ret))
+ goto fail_ctl_init;
+ mblog_init();
+ ret = dsp_taskmod_init();
+ if (ret)
+ goto fail_taskmod;
+
+ return 0;
+
+ fail_taskmod:
+ mblog_exit();
+ dsp_ctl_exit();
+ fail_ctl_init:
+ dsp_mem_exit();
+ fail_mem:
+ dsp_ctl_core_exit();
+ fail_ctl_core:
+ dsp_kfunc_remove_devices(info);
+ fail_kfunc:
+ kfree(info);
+
+ return ret;
+}
+
+static int dsp_drv_remove(struct platform_device *pdev)
+{
+ struct omap_dsp *info = platform_get_drvdata(pdev);
+
+ dsp_cpustat_request(CPUSTAT_RESET);
+
+ dsp_cfgstat_request(CFGSTAT_CLEAN);
+ dsp_mbox_exit();
+ dsp_taskmod_exit();
+ mblog_exit();
+ dsp_ctl_exit();
+ dsp_mem_exit();
+
+ dsp_ctl_core_exit();
+
+#ifdef CONFIG_ARCH_OMAP2
+ __dsp_per_disable();
+ clk_disable(dsp_ick_handle);
+ clk_disable(dsp_fck_handle);
+#endif
+ dsp_kfunc_remove_devices(info);
+ kfree(info);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM) && defined(CONFIG_ARCH_OMAP1)
+static int dsp_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ dsp_cfgstat_request(CFGSTAT_SUSPEND);
+
+ return 0;
+}
+
+static int dsp_drv_resume(struct platform_device *pdev)
+{
+ dsp_cfgstat_request(CFGSTAT_RESUME);
+
+ return 0;
+}
+#else
+#define dsp_drv_suspend NULL
+#define dsp_drv_resume NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver dsp_driver = {
+ .probe = dsp_drv_probe,
+ .remove = dsp_drv_remove,
+ .suspend = dsp_drv_suspend,
+ .resume = dsp_drv_resume,
+ .driver = {
+ .name = "dsp",
+ },
+};
+
+static int __init omap_dsp_mod_init(void)
+{
+ return platform_driver_register(&dsp_driver);
+}
+
+static void __exit omap_dsp_mod_exit(void)
+{
+ platform_driver_unregister(&dsp_driver);
+}
+
+/* module dependency: need mailbox module that have mbox_dsp_info */
+extern struct omap_mbox mbox_dsp_info;
+struct omap_mbox *mbox_dep = &mbox_dsp_info;
+
+module_init(omap_dsp_mod_init);
+module_exit(omap_dsp_mod_exit);
--- /dev/null
- #include <asm/arch/mailbox.h>
- #include <asm/arch/dsp.h>
+/*
+ * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
+ *
+ * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/ioctls.h>
++#include <mach/mailbox.h>
++#include <mach/dsp.h>
+#include "hardware_dsp.h"
+#include "dsp_mbcmd.h"
+#include "dsp.h"
+#include "ipbuf.h"
+
+enum dsp_space_e {
+ SPACE_MEM,
+ SPACE_IO,
+};
+
+#ifdef CONFIG_OMAP_DSP_FBEXPORT
+static enum fbstat_e {
+ FBSTAT_DISABLED = 0,
+ FBSTAT_ENABLED,
+ FBSTAT_MAX,
+} fbstat = FBSTAT_ENABLED;
+#endif
+
+static enum cfgstat_e cfgstat;
+int mbox_revision;
+static u8 n_stask;
+
+static ssize_t ifver_show(struct device *dev, struct device_attribute *attr,
+ char *buf);
+static ssize_t cpustat_show(struct device *dev, struct device_attribute *attr,
+ char *buf);
+static ssize_t icrmask_show(struct device *dev, struct device_attribute *attr,
+ char *buf);
+static ssize_t icrmask_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t loadinfo_show(struct device *dev, struct device_attribute *attr,
+ char *buf);
+
+#define __ATTR_RW(_name, _mode) { \
+ .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \
+ .show = _name##_show, \
+ .store = _name##_store, \
+}
+
+static struct device_attribute dev_attr_ifver = __ATTR_RO(ifver);
+static struct device_attribute dev_attr_cpustat = __ATTR_RO(cpustat);
+static struct device_attribute dev_attr_icrmask = __ATTR_RW(icrmask, 0644);
+static struct device_attribute dev_attr_loadinfo = __ATTR_RO(loadinfo);
+
+/*
+ * misc interactive mailbox command operations
+ */
+static struct misc_mb_wait_struct {
+ struct mutex lock;
+ wait_queue_head_t wait_q;
+ u8 cmd_h;
+ u8 cmd_l;
+ u16 *retvp;
+} misc_mb_wait = {
+ .lock = __MUTEX_INITIALIZER(misc_mb_wait.lock),
+ .wait_q = __WAIT_QUEUE_HEAD_INITIALIZER(misc_mb_wait.wait_q),
+};
+
+static int __misc_mbcompose_send_and_wait(u8 cmd_h, u8 cmd_l, u16 data,
+ u16 *retvp)
+{
+ struct mbcmd mb = MBCMD_INIT(cmd_h, cmd_l, data);
+ int ret = 0;
+
+ if (mutex_lock_interruptible(&misc_mb_wait.lock))
+ return -EINTR;
+
+ misc_mb_wait.cmd_h = mb.cmd_h;
+ misc_mb_wait.cmd_l = mb.cmd_l;
+ misc_mb_wait.retvp = retvp;
+ dsp_mbcmd_send_and_wait(&mb, &misc_mb_wait.wait_q);
+
+ if (misc_mb_wait.cmd_h != 0)
+ ret = -EINVAL;
+
+ mutex_unlock(&misc_mb_wait.lock);
+ return ret;
+}
+
+#define misc_mbcompose_send_and_wait(cmd_h, cmd_l, data, retvp) \
+ __misc_mbcompose_send_and_wait(MBOX_CMD_DSP_##cmd_h, (cmd_l), \
+ (data), (retvp));
+
+static int misc_mbcmd_response(struct mbcmd *mb, int argc, int match_cmd_l_flag)
+{
+ volatile u16 *buf;
+ int i;
+
+ /* if match_cmd_l_v flag is set, cmd_l needs to be matched as well. */
+ if (!waitqueue_active(&misc_mb_wait.wait_q) ||
+ (misc_mb_wait.cmd_h != mb->cmd_h) ||
+ (match_cmd_l_flag && (misc_mb_wait.cmd_l != mb->cmd_l))) {
+ const struct cmdinfo *ci = cmdinfo[mb->cmd_h];
+ char cmdstr[32];
+
+ if (ci->cmd_l_type == CMD_L_TYPE_SUBCMD)
+ sprintf(cmdstr, "%s:%s", ci->name, subcmd_name(mb));
+ else
+ strcpy(cmdstr, ci->name);
+ printk(KERN_WARNING
+ "mbox: unexpected command %s received!\n", cmdstr);
+ return -1;
+ }
+
+ /*
+ * if argc == 1, receive data through mbox:data register.
+ * if argc > 1, receive through ipbuf_sys.
+ */
+ if (argc == 1)
+ misc_mb_wait.retvp[0] = mb->data;
+ else if (argc > 1) {
+ if (dsp_mem_enable(ipbuf_sys_da) < 0) {
+ printk(KERN_ERR "mbox: %s - ipbuf_sys_da read failed!\n",
+ cmdinfo[mb->cmd_h]->name);
+ return -1;
+ }
+ if (sync_with_dsp(&ipbuf_sys_da->s, TID_ANON, 10) < 0) {
+ printk(KERN_ERR "mbox: %s - IPBUF sync failed!\n",
+ cmdinfo[mb->cmd_h]->name);
+ dsp_mem_disable(ipbuf_sys_da);
+ return -1;
+ }
+ /* need word access. do not use memcpy. */
+ buf = ipbuf_sys_da->d;
+ for (i = 0; i < argc; i++)
+ misc_mb_wait.retvp[i] = buf[i];
+ release_ipbuf_pvt(ipbuf_sys_da);
+ dsp_mem_disable(ipbuf_sys_da);
+ }
+
+ misc_mb_wait.cmd_h = 0;
+ wake_up_interruptible(&misc_mb_wait.wait_q);
+ return 0;
+}
+
+static int dsp_regread(enum dsp_space_e space, u16 adr, u16 *val)
+{
+ u8 cmd_l = (space == SPACE_MEM) ? REGRW_MEMR : REGRW_IOR;
+ int ret;
+
+ ret = misc_mbcompose_send_and_wait(REGRW, cmd_l, adr, val);
+ if ((ret < 0) && (ret != -EINTR))
+ printk(KERN_ERR "omapdsp: register read error!\n");
+
+ return ret;
+}
+
+static int dsp_regwrite(enum dsp_space_e space, u16 adr, u16 val)
+{
+ u8 cmd_l = (space == SPACE_MEM) ? REGRW_MEMW : REGRW_IOW;
+ struct mb_exarg arg = {
+ .tid = TID_ANON,
+ .argc = 1,
+ .argv = &val,
+ };
+
+ mbcompose_send_exarg(REGRW, cmd_l, adr, &arg);
+ return 0;
+}
+
+static int dsp_getvar(u8 varid, u16 *val)
+{
+ int ret;
+
+ ret = misc_mbcompose_send_and_wait(GETVAR, varid, 0, val);
+ if ((ret < 0) && (ret != -EINTR))
+ printk(KERN_ERR "omapdsp: variable read error!\n");
+
+ return ret;
+}
+
+static int dsp_setvar(u8 varid, u16 val)
+{
+ mbcompose_send(SETVAR, varid, val);
+ return 0;
+}
+
+/*
+ * dsp_cfg() return value
+ * = 0: OK
+ * = 1: failed, but state is clear. (DSPCFG command failed)
+ * < 0: failed. need cleanup.
+ */
+static int dsp_cfg(void)
+{
+ int ret = 0;
+
+#ifdef CONFIG_ARCH_OMAP1
+ /* for safety */
+ dsp_mem_usecount_clear();
+#endif
+
+ /*
+ * DSPCFG command and dsp_mem_start() must be called
+ * while internal mem is on.
+ */
+ dsp_mem_enable((void *)dspmem_base);
+
+ dsp_mbox_start();
+ dsp_twch_start();
+ dsp_mem_start();
+ dsp_err_start();
+
+ mbox_revision = -1;
+
+ ret = misc_mbcompose_send_and_wait(DSPCFG, DSPCFG_REQ, 0, NULL);
+ if (ret < 0) {
+ if (ret != -EINTR)
+ printk(KERN_ERR "omapdsp: configuration error!\n");
+ ret = 1;
+ goto out;
+ }
+
+#if defined(CONFIG_ARCH_OMAP1) && defined(OLD_BINARY_SUPPORT)
+ /*
+ * MBREV 3.2 or earlier doesn't assume DMA domain is on
+ * when DSPCFG command is sent
+ */
+ if ((mbox_revision == MBREV_3_0) ||
+ (mbox_revision == MBREV_3_2)) {
+ if ((ret = mbcompose_send(PM, PM_ENABLE, DSPREG_ICR_DMA)) < 0)
+ goto out;
+ }
+#endif
+
+ if ((ret = dsp_task_config_all(n_stask)) < 0)
+ goto out;
+
+ /* initialization */
+#ifdef CONFIG_OMAP_DSP_FBEXPORT
+ fbstat = FBSTAT_ENABLED;
+#endif
+
+ /* send parameter */
+ ret = dsp_setvar(VARID_ICRMASK, dsp_cpustat_get_icrmask());
+ if (ret < 0)
+ goto out;
+
+ /* create runtime sysfs entries */
+ ret = device_create_file(omap_dsp->dev, &dev_attr_loadinfo);
+ if (ret)
+ printk(KERN_ERR "device_create_file failed: %d\n", ret);
+ out:
+ dsp_mem_disable((void *)dspmem_base);
+ return ret;
+}
+
+static int dsp_uncfg(void)
+{
+ if (dsp_taskmod_busy()) {
+ printk(KERN_WARNING "omapdsp: tasks are busy.\n");
+ return -EBUSY;
+ }
+
+ /* FIXME: lock task module */
+
+ /* remove runtime sysfs entries */
+ device_remove_file(omap_dsp->dev, &dev_attr_loadinfo);
+
+ dsp_mbox_stop();
+ dsp_twch_stop();
+ dsp_mem_stop();
+ dsp_err_stop();
+ dsp_dbg_stop();
+ dsp_task_unconfig_all();
+ ipbuf_stop();
+
+ return 0;
+}
+
+static int dsp_suspend(void)
+{
+ int ret;
+
+ ret = misc_mbcompose_send_and_wait(SUSPEND, 0, 0, NULL);
+ if (ret < 0) {
+ if (ret != -EINVAL)
+ printk(KERN_ERR "omapdsp: DSP suspend error!\n");
+ return ret;
+ }
+
+ udelay(100); /* wait for DSP-side execution */
+ return 0;
+}
+
+int dsp_cfgstat_request(enum cfgstat_e st_req)
+{
+ static DEFINE_MUTEX(cfgstat_lock);
+ int ret = 0, ret_override = 0;
+
+ if (mutex_lock_interruptible(&cfgstat_lock))
+ return -EINTR;
+
+again:
+ switch (st_req) {
+
+ /* cfgstat takes CLEAN, READY or SUSPEND,
+ while st_req can take SUSPEND in addition. */
+
+ case CFGSTAT_CLEAN:
+ if (cfgstat == CFGSTAT_CLEAN)
+ goto up_out;
+ if ((ret = dsp_uncfg()) < 0)
+ goto up_out;
+ break;
+
+ case CFGSTAT_READY:
+ if (cfgstat != CFGSTAT_CLEAN) {
+ printk(KERN_ERR "omapdsp: DSP is ready already!\n");
+ ret = -EINVAL;
+ goto up_out;
+ }
+
+ ret = dsp_cfg();
+ if (ret > 0) { /* failed, but state is clear. */
+ ret = -EINVAL;
+ goto up_out;
+ } else if (ret < 0) { /* failed, need cleanup. */
+ st_req = CFGSTAT_CLEAN;
+ ret_override = ret;
+ goto again;
+ }
+ break;
+
+ /*
+ * suspend / resume
+ * DSP is not reset within this code, but done in omap_pm_suspend.
+ * so if these functions are called from sysfs,
+ * DSP should be reset / unreset out of these functions.
+ */
+ case CFGSTAT_SUSPEND:
+ switch (cfgstat) {
+
+ case CFGSTAT_CLEAN:
+ if (dsp_cpustat_get_stat() == CPUSTAT_RUN) {
+ printk(KERN_WARNING
+ "omapdsp: illegal operation -- trying "
+ "suspend DSP while it is running but "
+ "not configured.\n"
+ " Resetting DSP.\n");
+ dsp_cpustat_request(CPUSTAT_RESET);
+ ret = -EINVAL;
+ }
+ goto up_out;
+
+ case CFGSTAT_READY:
+ if ((ret = dsp_suspend()) < 0)
+ goto up_out;
+ break;
+
+ case CFGSTAT_SUSPEND:
+ goto up_out;
+
+ default:
+ BUG();
+
+ }
+
+ break;
+
+ case CFGSTAT_RESUME:
+ if (cfgstat != CFGSTAT_SUSPEND) {
+ printk(KERN_WARNING
+ "omapdsp: DSP resume request, but DSP is not in "
+ "suspend state.\n");
+ ret = -EINVAL;
+ goto up_out;
+ }
+ st_req = CFGSTAT_READY;
+ break;
+
+ default:
+ BUG();
+
+ }
+
+ cfgstat = st_req;
+up_out:
+ mutex_unlock(&cfgstat_lock);
+ return ret_override ? ret_override : ret;
+}
+
+enum cfgstat_e dsp_cfgstat_get_stat(void)
+{
+ return cfgstat;
+}
+
+/*
+ * polls all tasks
+ */
+static int dsp_poll(void)
+{
+ int ret;
+
+ ret = misc_mbcompose_send_and_wait(POLL, 0, 0, NULL);
+ if ((ret < 0) && (ret != -EINTR))
+ printk(KERN_ERR "omapdsp: poll error!\n");
+
+ return ret;
+}
+
+int dsp_set_runlevel(u8 level)
+{
+ if (level == RUNLEVEL_RECOVERY) {
+ if (mbcompose_send_recovery(RUNLEVEL, level, 0) < 0)
+ return -EINVAL;
+ } else {
+ if ((level < RUNLEVEL_USER) ||
+ (level > RUNLEVEL_SUPER))
+ return -EINVAL;
+ if (mbcompose_send(RUNLEVEL, level, 0) < 0)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OMAP_DSP_FBEXPORT
+static void dsp_fbctl_enable(void)
+{
+ mbcompose_send(KFUNC, KFUNC_FBCTL, FBCTL_ENABLE);
+}
+
+static int dsp_fbctl_disable(void)
+{
+ int ret;
+
+ ret = misc_mbcompose_send_and_wait(KFUNC, KFUNC_FBCTL, FBCTL_DISABLE,
+ NULL);
+ if ((ret < 0) && (ret != -EINTR))
+ printk(KERN_ERR "omapdsp: fb disable error!\n");
+
+ return 0;
+}
+
+static int dsp_fbstat_request(enum fbstat_e st)
+{
+ static DEFINE_MUTEX(fbstat_lock);
+ int ret = 0;
+
+ if (mutex_lock_interruptible(&fbstat_lock))
+ return -EINTR;
+
+ if (st == fbstat)
+ goto up_out;
+
+ switch (st) {
+ case FBSTAT_ENABLED:
+ dsp_fbctl_enable();
+ break;
+ case FBSTAT_DISABLED:
+ if ((ret = dsp_fbctl_disable()) < 0)
+ goto up_out;
+ break;
+ default:
+ BUG();
+ }
+
+ fbstat = st;
+up_out:
+ mutex_unlock(&fbstat_lock);
+ return 0;
+}
+#endif /* CONFIG_OMAP_DSP_FBEXPORT */
+
+/*
+ * DSP control device file operations
+ */
+static int dsp_ctl_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ /*
+ * command level 1: commands which don't need lock
+ */
+ case DSPCTL_IOCTL_RUN:
+ dsp_cpustat_request(CPUSTAT_RUN);
+ break;
+
+ case DSPCTL_IOCTL_RESET:
+ dsp_cpustat_request(CPUSTAT_RESET);
+ break;
+
+ case DSPCTL_IOCTL_SETRSTVECT:
+ ret = dsp_set_rstvect((dsp_long_t)arg);
+ break;
+
+#ifdef CONFIG_ARCH_OMAP1
+ case DSPCTL_IOCTL_CPU_IDLE:
+ dsp_cpustat_request(CPUSTAT_CPU_IDLE);
+ break;
+
+ case DSPCTL_IOCTL_GBL_IDLE:
+ dsp_cpustat_request(CPUSTAT_GBL_IDLE);
+ break;
+
+ case DSPCTL_IOCTL_MPUI_WORDSWAP_ON:
+ mpui_wordswap_on();
+ break;
+
+ case DSPCTL_IOCTL_MPUI_WORDSWAP_OFF:
+ mpui_wordswap_off();
+ break;
+
+ case DSPCTL_IOCTL_MPUI_BYTESWAP_ON:
+ mpui_byteswap_on();
+ break;
+
+ case DSPCTL_IOCTL_MPUI_BYTESWAP_OFF:
+ mpui_byteswap_off();
+ break;
+#endif /* CONFIG_ARCH_OMAP1 */
+
+ case DSPCTL_IOCTL_TASKCNT:
+ ret = dsp_task_count();
+ break;
+
+ case DSPCTL_IOCTL_MBSEND:
+ {
+ struct omap_dsp_mailbox_cmd u_cmd;
+ mbox_msg_t msg;
+ if (copy_from_user(&u_cmd, (void *)arg, sizeof(u_cmd)))
+ return -EFAULT;
+ msg = (u_cmd.cmd << 16) | u_cmd.data;
+ ret = dsp_mbcmd_send((struct mbcmd *)&msg);
+ break;
+ }
+
+ case DSPCTL_IOCTL_SETVAR:
+ {
+ struct omap_dsp_varinfo var;
+ if (copy_from_user(&var, (void *)arg, sizeof(var)))
+ return -EFAULT;
+ ret = dsp_setvar(var.varid, var.val[0]);
+ break;
+ }
+
+ case DSPCTL_IOCTL_RUNLEVEL:
+ ret = dsp_set_runlevel(arg);
+ break;
+
+#ifdef CONFIG_OMAP_DSP_FBEXPORT
+ case DSPCTL_IOCTL_FBEN:
+ ret = dsp_fbstat_request(FBSTAT_ENABLED);
+ break;
+#endif
+
+ /*
+ * command level 2: commands which need lock
+ */
+ case DSPCTL_IOCTL_DSPCFG:
+ ret = dsp_cfgstat_request(CFGSTAT_READY);
+ break;
+
+ case DSPCTL_IOCTL_DSPUNCFG:
+ ret = dsp_cfgstat_request(CFGSTAT_CLEAN);
+ break;
+
+ case DSPCTL_IOCTL_POLL:
+ ret = dsp_poll();
+ break;
+
+#ifdef CONFIG_OMAP_DSP_FBEXPORT
+ case DSPCTL_IOCTL_FBDIS:
+ ret = dsp_fbstat_request(FBSTAT_DISABLED);
+ break;
+#endif
+
+ case DSPCTL_IOCTL_SUSPEND:
+ if ((ret = dsp_cfgstat_request(CFGSTAT_SUSPEND)) < 0)
+ break;
+ dsp_cpustat_request(CPUSTAT_RESET);
+ break;
+
+ case DSPCTL_IOCTL_RESUME:
+ if ((ret = dsp_cfgstat_request(CFGSTAT_RESUME)) < 0)
+ break;
+ dsp_cpustat_request(CPUSTAT_RUN);
+ break;
+
+ case DSPCTL_IOCTL_REGMEMR:
+ {
+ struct omap_dsp_reginfo *u_reg = (void *)arg;
+ u16 adr, val;
+
+ if (copy_from_user(&adr, &u_reg->adr, sizeof(u16)))
+ return -EFAULT;
+ if ((ret = dsp_regread(SPACE_MEM, adr, &val)) < 0)
+ return ret;
+ if (copy_to_user(&u_reg->val, &val, sizeof(u16)))
+ return -EFAULT;
+ break;
+ }
+
+ case DSPCTL_IOCTL_REGMEMW:
+ {
+ struct omap_dsp_reginfo reg;
+
+ if (copy_from_user(®, (void *)arg, sizeof(reg)))
+ return -EFAULT;
+ ret = dsp_regwrite(SPACE_MEM, reg.adr, reg.val);
+ break;
+ }
+
+ case DSPCTL_IOCTL_REGIOR:
+ {
+ struct omap_dsp_reginfo *u_reg = (void *)arg;
+ u16 adr, val;
+
+ if (copy_from_user(&adr, &u_reg->adr, sizeof(u16)))
+ return -EFAULT;
+ if ((ret = dsp_regread(SPACE_IO, adr, &val)) < 0)
+ return ret;
+ if (copy_to_user(&u_reg->val, &val, sizeof(u16)))
+ return -EFAULT;
+ break;
+ }
+
+ case DSPCTL_IOCTL_REGIOW:
+ {
+ struct omap_dsp_reginfo reg;
+
+ if (copy_from_user(®, (void *)arg, sizeof(reg)))
+ return -EFAULT;
+ ret = dsp_regwrite(SPACE_IO, reg.adr, reg.val);
+ break;
+ }
+
+ case DSPCTL_IOCTL_GETVAR:
+ {
+ struct omap_dsp_varinfo *u_var = (void *)arg;
+ u8 varid;
+ u16 val[5]; /* maximum */
+ int argc;
+
+ if (copy_from_user(&varid, &u_var->varid, sizeof(u8)))
+ return -EFAULT;
+ switch (varid) {
+ case VARID_ICRMASK:
+ argc = 1;
+ break;
+ case VARID_LOADINFO:
+ argc = 5;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if ((ret = dsp_getvar(varid, val)) < 0)
+ return ret;
+ if (copy_to_user(&u_var->val, val, sizeof(u16) * argc))
+ return -EFAULT;
+ break;
+ }
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return ret;
+}
+
+/*
+ * functions called from mailbox interrupt routine
+ */
+void mbox_suspend(struct mbcmd *mb)
+{
+ misc_mbcmd_response(mb, 0, 0);
+}
+
+void mbox_dspcfg(struct mbcmd *mb)
+{
+ u8 last = mb->cmd_l & 0x80;
+ u8 cfgcmd = mb->cmd_l & 0x7f;
+ static dsp_long_t tmp_ipb_adr;
+
+ if (!waitqueue_active(&misc_mb_wait.wait_q) ||
+ (misc_mb_wait.cmd_h != MBOX_CMD_DSP_DSPCFG)) {
+ printk(KERN_WARNING
+ "mbox: DSPCFG command received, "
+ "but nobody is waiting for it...\n");
+ return;
+ }
+
+ /* mailbox protocol check */
+ if (cfgcmd == DSPCFG_PROTREV) {
+ mbox_revision = mb->data;
+ if (mbox_revision == MBPROT_REVISION)
+ return;
+#ifdef OLD_BINARY_SUPPORT
+ else if ((mbox_revision == MBREV_3_0) ||
+ (mbox_revision == MBREV_3_2)) {
+ printk(KERN_WARNING
+ "mbox: ***** old DSP binary *****\n"
+ " Please update your DSP application.\n");
+ return;
+ }
+#endif
+ else {
+ printk(KERN_ERR
+ "mbox: protocol revision check error!\n"
+ " expected=0x%04x, received=0x%04x\n",
+ MBPROT_REVISION, mb->data);
+ mbox_revision = -1;
+ goto abort1;
+ }
+ }
+
+ /*
+ * following commands are accepted only after
+ * revision check has been passed.
+ */
+ if (!mbox_revision < 0) {
+ pr_info("mbox: DSPCFG command received, "
+ "but revision check has not been passed.\n");
+ return;
+ }
+
+ switch (cfgcmd) {
+ case DSPCFG_SYSADRH:
+ tmp_ipb_adr = (u32)mb->data << 16;
+ break;
+
+ case DSPCFG_SYSADRL:
+ tmp_ipb_adr |= mb->data;
+ break;
+
+ case DSPCFG_ABORT:
+ goto abort1;
+
+ default:
+ printk(KERN_ERR
+ "mbox: Unknown CFG command: cmd_l=0x%02x, data=0x%04x\n",
+ mb->cmd_l, mb->data);
+ return;
+ }
+
+ if (last) {
+ void *badr;
+ u16 bln;
+ u16 bsz;
+ volatile u16 *buf;
+ void *ipb_sys_da, *ipb_sys_ad;
+ void *mbseq; /* FIXME: 3.4 obsolete */
+ short *dbg_buf;
+ u16 dbg_buf_sz, dbg_line_sz;
+ struct mem_sync_struct mem_sync, *mem_syncp;
+
+ ipb_sys_da = dspword_to_virt(tmp_ipb_adr);
+ if (ipbuf_sys_config(ipb_sys_da, DIR_D2A) < 0)
+ goto abort1;
+
+ if (dsp_mem_enable(ipbuf_sys_da) < 0) {
+ printk(KERN_ERR "mbox: DSPCFG - ipbuf_sys_da read failed!\n");
+ goto abort1;
+ }
+ if (sync_with_dsp(&ipbuf_sys_da->s, TID_ANON, 10) < 0) {
+ printk(KERN_ERR "mbox: DSPCFG - IPBUF sync failed!\n");
+ dsp_mem_disable(ipbuf_sys_da);
+ goto abort1;
+ }
+ /*
+ * read configuration data on system IPBUF
+ * we must read with 16bit-access
+ */
+#ifdef OLD_BINARY_SUPPORT
+ if (mbox_revision == MBPROT_REVISION) {
+#endif
+ buf = ipbuf_sys_da->d;
+ n_stask = buf[0];
+ bln = buf[1];
+ bsz = buf[2];
+ badr = MKVIRT(buf[3], buf[4]);
+ /* ipb_sys_da = MKVIRT(buf[5], buf[6]); */
+ ipb_sys_ad = MKVIRT(buf[7], buf[8]);
+ mbseq = MKVIRT(buf[9], buf[10]);
+ dbg_buf = MKVIRT(buf[11], buf[12]);
+ dbg_buf_sz = buf[13];
+ dbg_line_sz = buf[14];
+ mem_sync.DARAM = MKVIRT(buf[15], buf[16]);
+ mem_sync.SARAM = MKVIRT(buf[17], buf[18]);
+ mem_sync.SDRAM = MKVIRT(buf[19], buf[20]);
+ mem_syncp = &mem_sync;
+#ifdef OLD_BINARY_SUPPORT
+ } else if (mbox_revision == MBREV_3_2) {
+ buf = ipbuf_sys_da->d;
+ n_stask = buf[0];
+ bln = buf[1];
+ bsz = buf[2];
+ badr = MKVIRT(buf[3], buf[4]);
+ /* ipb_sys_da = MKVIRT(buf[5], buf[6]); */
+ ipb_sys_ad = MKVIRT(buf[7], buf[8]);
+ mbseq = MKVIRT(buf[9], buf[10]);
+ dbg_buf = NULL;
+ dbg_buf_sz = 0;
+ dbg_line_sz = 0;
+ mem_syncp = NULL;
+ } else if (mbox_revision == MBREV_3_0) {
+ buf = ipbuf_sys_da->d;
+ n_stask = buf[0];
+ bln = buf[1];
+ bsz = buf[2];
+ badr = MKVIRT(buf[3], buf[4]);
+ /* bkeep = buf[5]; */
+ /* ipb_sys_da = MKVIRT(buf[6], buf[7]); */
+ ipb_sys_ad = MKVIRT(buf[8], buf[9]);
+ mbseq = MKVIRT(buf[10], buf[11]);
+ dbg_buf = NULL;
+ dbg_buf_sz = 0;
+ dbg_line_sz = 0;
+ mem_syncp = NULL;
+ } else { /* should not occur */
+ dsp_mem_disable(ipbuf_sys_da);
+ goto abort1;
+ }
+#endif /* OLD_BINARY_SUPPORT */
+
+ release_ipbuf_pvt(ipbuf_sys_da);
+ dsp_mem_disable(ipbuf_sys_da);
+
+ /*
+ * following configurations need to be done before
+ * waking up the dspcfg initiator process.
+ */
+ if (ipbuf_sys_config(ipb_sys_ad, DIR_A2D) < 0)
+ goto abort1;
+ if (ipbuf_config(bln, bsz, badr) < 0)
+ goto abort1;
+ if (dsp_mbox_config(mbseq) < 0)
+ goto abort2;
+ if (dsp_dbg_config(dbg_buf, dbg_buf_sz, dbg_line_sz) < 0)
+ goto abort2;
+ if (dsp_mem_sync_config(mem_syncp) < 0)
+ goto abort2;
+
+ misc_mb_wait.cmd_h = 0;
+ wake_up_interruptible(&misc_mb_wait.wait_q);
+ }
+ return;
+
+abort2:
+ ipbuf_stop();
+abort1:
+ wake_up_interruptible(&misc_mb_wait.wait_q);
+ return;
+}
+
+void mbox_poll(struct mbcmd *mb)
+{
+ misc_mbcmd_response(mb, 0, 0);
+}
+
+void mbox_regrw(struct mbcmd *mb)
+{
+ switch (mb->cmd_l) {
+ case REGRW_DATA:
+ misc_mbcmd_response(mb, 1, 0);
+ break;
+ default:
+ printk(KERN_ERR
+ "mbox: Illegal REGRW command: "
+ "cmd_l=0x%02x, data=0x%04x\n", mb->cmd_l, mb->data);
+ return;
+ }
+}
+
+void mbox_getvar(struct mbcmd *mb)
+{
+ switch (mb->cmd_l) {
+ case VARID_ICRMASK:
+ misc_mbcmd_response(mb, 1, 1);
+ break;
+ case VARID_LOADINFO:
+ misc_mbcmd_response(mb, 5, 1);
+ break;
+ default:
+ printk(KERN_ERR
+ "mbox: Illegal GETVAR command: "
+ "cmd_l=0x%02x, data=0x%04x\n", mb->cmd_l, mb->data);
+ return;
+ }
+}
+
+void mbox_fbctl_disable(struct mbcmd *mb)
+{
+ misc_mbcmd_response(mb, 0, 0);
+}
+
+struct file_operations dsp_ctl_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = dsp_ctl_ioctl,
+};
+
+/*
+ * sysfs files
+ */
+
+/* ifver */
+static ssize_t ifver_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int len = 0;
+
+ /*
+ * I/F VERSION descriptions:
+ *
+ * 3.2: sysfs / udev support
+ * KMEM_RESERVE / KMEM_RELEASE ioctls for mem device
+ * 3.3: added following ioctls
+ * DSPCTL_IOCTL_GBL_IDLE
+ * DSPCTL_IOCTL_CPU_IDLE (instead of DSPCTL_IOCTL_IDLE)
+ * DSPCTL_IOCTL_POLL
+ */
+
+ /*
+ * print all supporting I/F VERSIONs, like followings.
+ *
+ * len += sprintf(buf, "3.2\n");
+ * len += sprintf(buf, "3.3\n");
+ */
+ len += sprintf(buf + len, "3.2\n");
+ len += sprintf(buf + len, "3.3\n");
+
+ return len;
+}
+
+/* cpustat */
+static char *cpustat_name[CPUSTAT_MAX] = {
+ [CPUSTAT_RESET] = "reset",
+#ifdef CONFIG_ARCH_OMAP1
+ [CPUSTAT_GBL_IDLE] = "gbl_idle",
+ [CPUSTAT_CPU_IDLE] = "cpu_idle",
+#endif
+ [CPUSTAT_RUN] = "run",
+};
+
+static ssize_t cpustat_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", cpustat_name[dsp_cpustat_get_stat()]);
+}
+
+/* icrmask */
+static ssize_t icrmask_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "0x%04x\n", dsp_cpustat_get_icrmask());
+}
+
+static ssize_t icrmask_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u16 mask;
+ int ret;
+
+ mask = simple_strtol(buf, NULL, 16);
+ dsp_cpustat_set_icrmask(mask);
+
+ if (dsp_cfgstat_get_stat() == CFGSTAT_READY) {
+ ret = dsp_setvar(VARID_ICRMASK, mask);
+ if (ret < 0)
+ return ret;
+ }
+
+ return count;
+}
+
+/* loadinfo */
+static ssize_t loadinfo_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int len;
+ int ret;
+ u16 val[5];
+
+ if ((ret = dsp_getvar(VARID_LOADINFO, val)) < 0)
+ return ret;
+
+ /*
+ * load info value range is 0(free) - 10000(busy):
+ * if CPU load is not measured on DSP, it sets 0xffff at val[0].
+ */
+
+ if (val[0] == 0xffff) {
+ len = sprintf(buf,
+ "currently DSP load info is not available.\n");
+ goto out;
+ }
+
+ len = sprintf(buf,
+ "DSP load info:\n"
+ " 10ms average = %3d.%02d%%\n"
+ " 1sec average = %3d.%02d%% busiest 10ms = %3d.%02d%%\n"
+ " 1min average = %3d.%02d%% busiest 1s = %3d.%02d%%\n",
+ val[0]/100, val[0]%100,
+ val[1]/100, val[1]%100, val[2]/100, val[2]%100,
+ val[3]/100, val[3]%100, val[4]/100, val[4]%100);
+out:
+ return len;
+}
+
+int __init dsp_ctl_init(void)
+{
+ int ret;
+
+ ret = device_create_file(omap_dsp->dev, &dev_attr_ifver);
+ if (unlikely(ret))
+ return ret;
+ ret = device_create_file(omap_dsp->dev, &dev_attr_cpustat);
+ if (unlikely(ret))
+ goto fail_create_cpustat;
+ ret = device_create_file(omap_dsp->dev, &dev_attr_icrmask);
+ if (unlikely(ret))
+ goto fail_create_icrmask;
+
+ return 0;
+
+fail_create_icrmask:
+ device_remove_file(omap_dsp->dev, &dev_attr_cpustat);
+fail_create_cpustat:
+ device_remove_file(omap_dsp->dev, &dev_attr_ifver);
+
+ return ret;
+}
+
+void dsp_ctl_exit(void)
+{
+ device_remove_file(omap_dsp->dev, &dev_attr_ifver);
+ device_remove_file(omap_dsp->dev, &dev_attr_cpustat);
+ device_remove_file(omap_dsp->dev, &dev_attr_icrmask);
+}
--- /dev/null
- #include <asm/arch/tc.h>
- #include <asm/arch/omapfb.h>
- #include <asm/arch/dsp.h>
- #include <asm/arch/mailbox.h>
- #include <asm/arch/mmu.h>
+/*
+ * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
+ *
+ * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * Conversion to mempool API and ARM MMU section mapping
+ * by Paul Mundt <paul.mundt@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mempool.h>
+#include <linux/clk.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
++#include <mach/tc.h>
++#include <mach/omapfb.h>
++#include <mach/dsp.h>
++#include <mach/mailbox.h>
++#include <mach/mmu.h>
+#include "dsp_mbcmd.h"
+#include "dsp.h"
+#include "ipbuf.h"
+
+#if 0
+#if defined(CONFIG_ARCH_OMAP1)
+#include "../../mach-omap1/mmu.h"
+#elif defined(CONFIG_ARCH_OMAP2)
+#include "../../mach-omap2/mmu.h"
+#endif
+#endif
+
+#include "mmu.h"
+
+static struct mem_sync_struct mem_sync;
+
+int dsp_mem_sync_inc(void)
+{
+ if (dsp_mem_enable((void *)dspmem_base) < 0)
+ return -1;
+ if (mem_sync.DARAM)
+ mem_sync.DARAM->ad_arm++;
+ if (mem_sync.SARAM)
+ mem_sync.SARAM->ad_arm++;
+ if (mem_sync.SDRAM)
+ mem_sync.SDRAM->ad_arm++;
+ dsp_mem_disable((void *)dspmem_base);
+
+ return 0;
+}
+
+/*
+ * dsp_mem_sync_config() is called from mbox1 workqueue
+ */
+int dsp_mem_sync_config(struct mem_sync_struct *sync)
+{
+ size_t sync_seq_sz = sizeof(struct sync_seq);
+
+#ifdef OLD_BINARY_SUPPORT
+ if (sync == NULL) {
+ memset(&mem_sync, 0, sizeof(struct mem_sync_struct));
+ return 0;
+ }
+#endif
+ if ((dsp_mem_type(sync->DARAM, sync_seq_sz) != MEM_TYPE_DARAM) ||
+ (dsp_mem_type(sync->SARAM, sync_seq_sz) != MEM_TYPE_SARAM) ||
+ (dsp_mem_type(sync->SDRAM, sync_seq_sz) != MEM_TYPE_EXTERN)) {
+ printk(KERN_ERR
+ "omapdsp: mem_sync address validation failure!\n"
+ " mem_sync.DARAM = 0x%p,\n"
+ " mem_sync.SARAM = 0x%p,\n"
+ " mem_sync.SDRAM = 0x%p,\n",
+ sync->DARAM, sync->SARAM, sync->SDRAM);
+ return -1;
+ }
+
+ memcpy(&mem_sync, sync, sizeof(struct mem_sync_struct));
+
+ return 0;
+}
+
+
+enum dsp_mem_type_e dsp_mem_type(void *vadr, size_t len)
+{
+ void *ds = (void *)daram_base;
+ void *de = (void *)daram_base + daram_size;
+ void *ss = (void *)saram_base;
+ void *se = (void *)saram_base + saram_size;
+ int ret;
+
+ if ((vadr >= ds) && (vadr < de)) {
+ if (vadr + len > de)
+ return MEM_TYPE_CROSSING;
+ else
+ return MEM_TYPE_DARAM;
+ } else if ((vadr >= ss) && (vadr < se)) {
+ if (vadr + len > se)
+ return MEM_TYPE_CROSSING;
+ else
+ return MEM_TYPE_SARAM;
+ } else {
+ down_read(&dsp_mmu.exmap_sem);
+ if (exmap_valid(&dsp_mmu, vadr, len))
+ ret = MEM_TYPE_EXTERN;
+ else
+ ret = MEM_TYPE_NONE;
+ up_read(&dsp_mmu.exmap_sem);
+ return ret;
+ }
+}
+
+int dsp_address_validate(void *p, size_t len, char *fmt, ...)
+{
+ char s[64];
+ va_list args;
+
+ if (dsp_mem_type(p, len) > 0)
+ return 0;
+
+ if (fmt == NULL)
+ goto out;
+
+ va_start(args, fmt);
+ vsprintf(s, fmt, args);
+ va_end(args);
+ printk(KERN_ERR
+ "omapdsp: %s address(0x%p) and size(0x%x) is not valid!\n"
+ "(crossing different type of memories, or external memory\n"
+ "space where no actual memory is mapped)\n", s, p, len);
+ out:
+ return -1;
+}
+
+#ifdef CONFIG_OMAP_DSP_FBEXPORT
+
+static inline unsigned long lineup_offset(unsigned long adr,
+ unsigned long ref,
+ unsigned long mask)
+{
+ unsigned long newadr;
+
+ newadr = (adr & ~mask) | (ref & mask);
+ if (newadr < adr)
+ newadr += mask + 1;
+ return newadr;
+}
+
+/*
+ * fb update functions:
+ * fbupd_response() is executed by the workqueue.
+ * fbupd_cb() is called when fb update is done, in interrupt context.
+ * mbox_fbupd() is called when KFUNC:FBCTL:UPD is received from DSP.
+ */
+static void fbupd_response(struct work_struct *unused)
+{
+ int status;
+
+ status = mbcompose_send(KFUNC, KFUNC_FBCTL, FBCTL_UPD);
+ if (status == 0)
+ return;
+
+ /* FIXME: DSP is busy !! */
+ printk(KERN_ERR
+ "omapdsp:"
+ "DSP is busy when trying to send FBCTL:UPD response!\n");
+}
+
+static DECLARE_WORK(fbupd_response_work, fbupd_response);
+
+static void fbupd_cb(void *arg)
+{
+ schedule_work(&fbupd_response_work);
+}
+
+void mbox_fbctl_upd(void)
+{
+ struct omapfb_update_window win;
+ volatile unsigned short *buf = ipbuf_sys_da->d;
+
+ if (sync_with_dsp(&ipbuf_sys_da->s, TID_ANON, 5000) < 0) {
+ printk(KERN_ERR "mbox: FBCTL:UPD - IPBUF sync failed!\n");
+ return;
+ }
+ win.x = buf[0];
+ win.y = buf[1];
+ win.width = buf[2];
+ win.height = buf[3];
+ win.format = buf[4];
+ release_ipbuf_pvt(ipbuf_sys_da);
+
+#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
+ if (!omapfb_ready) {
+ printk(KERN_WARNING
+ "omapdsp: fbupd() called while HWA742 is not ready!\n");
+ return;
+ }
+#endif
+ omapfb_update_window_async(registered_fb[0], &win, fbupd_cb, NULL);
+}
+
+#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
+static int omapfb_notifier_cb(struct notifier_block *omapfb_nb,
+ unsigned long event, void *fbi)
+{
+ pr_info("omapfb_notifier_cb(): event = %s\n",
+ (event == OMAPFB_EVENT_READY) ? "READY" :
+ (event == OMAPFB_EVENT_DISABLED) ? "DISABLED" : "Unknown");
+ if (event == OMAPFB_EVENT_READY)
+ omapfb_ready = 1;
+ else if (event == OMAPFB_EVENT_DISABLED)
+ omapfb_ready = 0;
+ return 0;
+}
+#endif
+
+static int dsp_fbexport(dsp_long_t *dspadr)
+{
+ dsp_long_t dspadr_actual;
+ unsigned long padr_sys, padr, fbsz_sys, fbsz;
+ int cnt;
+#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
+ int status;
+#endif
+
+ pr_debug( "omapdsp: frame buffer export\n");
+
+#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
+ if (omapfb_nb) {
+ printk(KERN_WARNING
+ "omapdsp: frame buffer has been exported already!\n");
+ return -EBUSY;
+ }
+#endif
+
+ if (num_registered_fb == 0) {
+ pr_info("omapdsp: frame buffer not registered.\n");
+ return -EINVAL;
+ }
+ if (num_registered_fb != 1) {
+ pr_info("omapdsp: %d frame buffers found. we use first one.\n",
+ num_registered_fb);
+ }
+ padr_sys = registered_fb[0]->fix.smem_start;
+ fbsz_sys = registered_fb[0]->fix.smem_len;
+ if (fbsz_sys == 0) {
+ printk(KERN_ERR
+ "omapdsp: framebuffer doesn't seem to be configured "
+ "correctly! (size=0)\n");
+ return -EINVAL;
+ }
+
+ /*
+ * align padr and fbsz to 4kB boundary
+ * (should be noted to the user afterwards!)
+ */
+ padr = padr_sys & ~(SZ_4K-1);
+ fbsz = (fbsz_sys + padr_sys - padr + SZ_4K-1) & ~(SZ_4K-1);
+
+ /* line up dspadr offset with padr */
+ dspadr_actual =
+ (fbsz > SZ_1M) ? lineup_offset(*dspadr, padr, SZ_1M-1) :
+ (fbsz > SZ_64K) ? lineup_offset(*dspadr, padr, SZ_64K-1) :
+ /* (fbsz > SZ_4KB) ? */ *dspadr;
+ if (dspadr_actual != *dspadr)
+ pr_debug(
+ "omapdsp: actual dspadr for FBEXPORT = %08x\n",
+ dspadr_actual);
+ *dspadr = dspadr_actual;
+
+ cnt = omap_mmu_exmap(&dsp_mmu, dspadr_actual, padr, fbsz,
+ EXMAP_TYPE_FB);
+ if (cnt < 0) {
+ printk(KERN_ERR "omapdsp: exmap failure.\n");
+ return cnt;
+ }
+
+ if ((padr != padr_sys) || (fbsz != fbsz_sys)) {
+ printk(KERN_WARNING
+" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
+" !! screen base address or size is not aligned in 4kB: !!\n"
+" !! actual screen adr = %08lx, size = %08lx !!\n"
+" !! exporting adr = %08lx, size = %08lx !!\n"
+" !! Make sure that the framebuffer is allocated with 4kB-order! !!\n"
+" !! Otherwise DSP can corrupt the kernel memory. !!\n"
+" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",
+ padr_sys, fbsz_sys, padr, fbsz);
+ }
+
+ /* increase the DMA priority */
+ set_emiff_dma_prio(15);
+
+#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
+ omapfb_nb = kzalloc(sizeof(struct omapfb_notifier_block), GFP_KERNEL);
+ if (omapfb_nb == NULL) {
+ printk(KERN_ERR
+ "omapdsp: failed to allocate memory for omapfb_nb!\n");
+ omap_mmu_exunmap(&dsp_mmu, (unsigned long)dspadr);
+ return -ENOMEM;
+ }
+
+ status = omapfb_register_client(omapfb_nb, omapfb_notifier_cb, NULL);
+ if (status)
+ pr_info("omapfb_register_client(): failure(%d)\n", status);
+#endif
+
+ return cnt;
+}
+#else
+void mbox_fbctl_upd(void) { }
+#endif
+
+/* dsp/mem fops: backward compatibility */
+static ssize_t dsp_mem_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct bin_attribute attr;
+
+ return __omap_mmu_mem_read(&dsp_mmu, &attr,
+ (char __user *)buf, *ppos, count);
+}
+
+static ssize_t dsp_mem_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct bin_attribute attr;
+
+ return __omap_mmu_mem_write(&dsp_mmu, &attr,
+ (char __user *)buf, *ppos, count);
+}
+
+static int dsp_mem_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct omap_dsp_mapinfo mapinfo;
+ __u32 size;
+
+ switch (cmd) {
+ case MEM_IOCTL_MMUINIT:
+ if (dsp_mmu.exmap_tbl)
+ omap_mmu_unregister(&dsp_mmu);
+ dsp_mem_ipi_init();
+ return omap_mmu_register(&dsp_mmu);
+
+ case MEM_IOCTL_EXMAP:
+ if (copy_from_user(&mapinfo, (void __user *)arg,
+ sizeof(mapinfo)))
+ return -EFAULT;
+ return omap_mmu_exmap(&dsp_mmu, mapinfo.dspadr,
+ 0, mapinfo.size, EXMAP_TYPE_MEM);
+
+ case MEM_IOCTL_EXUNMAP:
+ return omap_mmu_exunmap(&dsp_mmu, (unsigned long)arg);
+
+ case MEM_IOCTL_EXMAP_FLUSH:
+ omap_mmu_exmap_flush(&dsp_mmu);
+ return 0;
+#ifdef CONFIG_OMAP_DSP_FBEXPORT
+ case MEM_IOCTL_FBEXPORT:
+ {
+ dsp_long_t dspadr;
+ int ret;
+ if (copy_from_user(&dspadr, (void __user *)arg,
+ sizeof(dsp_long_t)))
+ return -EFAULT;
+ ret = dsp_fbexport(&dspadr);
+ if (copy_to_user((void __user *)arg, &dspadr,
+ sizeof(dsp_long_t)))
+ return -EFAULT;
+ return ret;
+ }
+#endif
+ case MEM_IOCTL_MMUITACK:
+ return dsp_mmu_itack();
+
+ case MEM_IOCTL_KMEM_RESERVE:
+
+ if (copy_from_user(&size, (void __user *)arg,
+ sizeof(__u32)))
+ return -EFAULT;
+ return omap_mmu_kmem_reserve(&dsp_mmu, size);
+
+
+ case MEM_IOCTL_KMEM_RELEASE:
+ omap_mmu_kmem_release();
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+struct file_operations dsp_mem_fops = {
+ .owner = THIS_MODULE,
+ .read = dsp_mem_read,
+ .write = dsp_mem_write,
+ .ioctl = dsp_mem_ioctl,
+};
+
+void dsp_mem_start(void)
+{
+ dsp_register_mem_cb(intmem_enable, intmem_disable);
+}
+
+void dsp_mem_stop(void)
+{
+ memset(&mem_sync, 0, sizeof(struct mem_sync_struct));
+ dsp_unregister_mem_cb();
+}
+
+static void dsp_mmu_irq_work(struct work_struct *work)
+{
+ struct omap_mmu *mmu = container_of(work, struct omap_mmu, irq_work);
+
+ if (dsp_cfgstat_get_stat() == CFGSTAT_READY) {
+ dsp_err_set(ERRCODE_MMU, mmu->fault_address);
+ return;
+ }
+ omap_mmu_itack(mmu);
+ pr_info("Resetting DSP...\n");
+ dsp_cpustat_request(CPUSTAT_RESET);
+ omap_mmu_enable(mmu, 0);
+}
+
+/*
+ * later half of dsp memory initialization
+ */
+int dsp_mem_late_init(void)
+{
+ int ret;
+
+ dsp_mem_ipi_init();
+
+ INIT_WORK(&dsp_mmu.irq_work, dsp_mmu_irq_work);
+ ret = omap_mmu_register(&dsp_mmu);
+ if (ret) {
+ dsp_reset_idle_boot_base();
+ goto out;
+ }
+ omap_dsp->mmu = &dsp_mmu;
+ out:
+ return ret;
+}
+
+int __init dsp_mem_init(void)
+{
+#ifdef CONFIG_ARCH_OMAP2
+ dsp_mmu.clk = dsp_fck_handle;
+ dsp_mmu.memclk = dsp_ick_handle;
+#elif defined(CONFIG_ARCH_OMAP1)
+ dsp_mmu.clk = dsp_ck_handle;
+ dsp_mmu.memclk = api_ck_handle;
+#endif
+ return 0;
+}
+
+void dsp_mem_exit(void)
+{
+ dsp_reset_idle_boot_base();
+ omap_mmu_unregister(&dsp_mmu);
+}
--- /dev/null
- #include <asm/arch/mailbox.h>
+/*
+ * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
+ *
+ * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
++#include <mach/mailbox.h>
+#include <asm/uaccess.h>
+#include "dsp_mbcmd.h"
+#include "dsp.h"
+
+/*
+ * value seen through read()
+ */
+#define DSP_ERR_WDT 0x00000001
+#define DSP_ERR_MMU 0x00000002
+static unsigned long errval;
+
+static DECLARE_WAIT_QUEUE_HEAD(err_wait_q);
+static int errcnt;
+static u16 wdtval; /* FIXME: read through ioctl */
+static u32 mmu_fadr; /* FIXME: read through ioctl */
+
+/*
+ * DSP error detection device file operations
+ */
+static ssize_t dsp_err_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned long flags;
+ int status;
+ DEFINE_WAIT(wait);
+
+ if (count < 4)
+ return 0;
+
+ prepare_to_wait(&err_wait_q, &wait, TASK_INTERRUPTIBLE);
+ if (errcnt == 0)
+ schedule();
+ finish_wait(&err_wait_q, &wait);
+ if (signal_pending(current))
+ return -EINTR;
+
+ local_irq_save(flags);
+ status = copy_to_user(buf, &errval, 4);
+ if (status) {
+ local_irq_restore(flags);
+ return -EFAULT;
+ }
+ errcnt = 0;
+ local_irq_restore(flags);
+
+ return 4;
+}
+
+static unsigned int dsp_err_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask = 0;
+
+ poll_wait(file, &err_wait_q, wait);
+ if (errcnt != 0)
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+struct file_operations dsp_err_fops = {
+ .owner = THIS_MODULE,
+ .poll = dsp_err_poll,
+ .read = dsp_err_read,
+};
+
+/*
+ * set / clear functions
+ */
+
+/* DSP MMU */
+static void dsp_err_mmu_set(unsigned long arg)
+{
+ disable_irq(omap_dsp->mmu->irq);
+ mmu_fadr = (u32)arg;
+}
+
+static void dsp_err_mmu_clr(void)
+{
+ enable_irq(omap_dsp->mmu->irq);
+}
+
+/* WDT */
+static void dsp_err_wdt_set(unsigned long arg)
+{
+ wdtval = (u16)arg;
+}
+
+/*
+ * error code handler
+ */
+static struct {
+ unsigned long val;
+ void (*set)(unsigned long arg);
+ void (*clr)(void);
+} dsp_err_desc[ERRCODE_MAX] = {
+ [ERRCODE_MMU] = { DSP_ERR_MMU, dsp_err_mmu_set, dsp_err_mmu_clr },
+ [ERRCODE_WDT] = { DSP_ERR_WDT, dsp_err_wdt_set, NULL },
+};
+
+void dsp_err_set(enum errcode_e code, unsigned long arg)
+{
+ if (dsp_err_desc[code].set != NULL)
+ dsp_err_desc[code].set(arg);
+
+ errval |= dsp_err_desc[code].val;
+ errcnt++;
+ wake_up_interruptible(&err_wait_q);
+}
+
+void dsp_err_clear(enum errcode_e code)
+{
+ errval &= ~dsp_err_desc[code].val;
+
+ if (dsp_err_desc[code].clr != NULL)
+ dsp_err_desc[code].clr();
+}
+
+int dsp_err_isset(enum errcode_e code)
+{
+ return (errval & dsp_err_desc[code].val) ? 1 : 0;
+}
+
+void dsp_err_notify(void)
+{
+ /* new error code should be assigned */
+ dsp_err_set(DSP_ERR_WDT, 0);
+}
+
+/*
+ * functions called from mailbox interrupt routine
+ */
+static void mbox_err_wdt(u16 data)
+{
+ dsp_err_set(DSP_ERR_WDT, (unsigned long)data);
+}
+
+#ifdef OLD_BINARY_SUPPORT
+/* v3.3 obsolete */
+void mbox_wdt(struct mbcmd *mb)
+{
+ mbox_err_wdt(mb->data);
+}
+#endif
+
+extern void mbox_err_ipbfull(void);
+extern void mbox_err_fatal(u8 tid);
+
+void mbox_err(struct mbcmd *mb)
+{
+ u8 eid = mb->cmd_l;
+ char *eidnm = subcmd_name(mb);
+ u8 tid;
+
+ if (eidnm) {
+ printk(KERN_WARNING
+ "mbox: ERR from DSP (%s): 0x%04x\n", eidnm, mb->data);
+ } else {
+ printk(KERN_WARNING
+ "mbox: ERR from DSP (unknown EID=%02x): %04x\n",
+ eid, mb->data);
+ }
+
+ switch (eid) {
+ case EID_IPBFULL:
+ mbox_err_ipbfull();
+ break;
+
+ case EID_FATAL:
+ tid = mb->data & 0x00ff;
+ mbox_err_fatal(tid);
+ break;
+
+ case EID_WDT:
+ mbox_err_wdt(mb->data);
+ break;
+ }
+}
+
+/*
+ *
+ */
+void dsp_err_start(void)
+{
+ enum errcode_e i;
+
+ for (i = 0; i < ERRCODE_MAX; i++) {
+ if (dsp_err_isset(i))
+ dsp_err_clear(i);
+ }
+ omap_dsp->mbox->err_notify = dsp_err_notify;
+ errcnt = 0;
+}
+
+void dsp_err_stop(void)
+{
+ wake_up_interruptible(&err_wait_q);
+ omap_dsp->mbox->err_notify = NULL;
+}
--- /dev/null
- #include <asm/arch/mailbox.h>
+/*
+ * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
+ *
+ * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
++#include <mach/mailbox.h>
+#include "dsp_mbcmd.h"
+#include "dsp.h"
+#include "ipbuf.h"
+
+static struct ipbuf_head *g_ipbuf;
+struct ipbcfg ipbcfg;
+struct ipbuf_sys *ipbuf_sys_da, *ipbuf_sys_ad;
+static struct ipblink ipb_free = IPBLINK_INIT;
+static int ipbuf_sys_hold_mem_active;
+
+static ssize_t ipbuf_show(struct device *dev, struct device_attribute *attr,
+ char *buf);
+static struct device_attribute dev_attr_ipbuf = __ATTR_RO(ipbuf);
+
+void ipbuf_stop(void)
+{
+ int i;
+
+ device_remove_file(omap_dsp->dev, &dev_attr_ipbuf);
+
+ spin_lock(&ipb_free.lock);
+ RESET_IPBLINK(&ipb_free);
+ spin_unlock(&ipb_free.lock);
+
+ ipbcfg.ln = 0;
+ if (g_ipbuf) {
+ kfree(g_ipbuf);
+ g_ipbuf = NULL;
+ }
+ for (i = 0; i < ipbuf_sys_hold_mem_active; i++) {
+ dsp_mem_disable((void *)daram_base);
+ }
+ ipbuf_sys_hold_mem_active = 0;
+}
+
+int ipbuf_config(u16 ln, u16 lsz, void *base)
+{
+ size_t lsz_byte = ((size_t)lsz) << 1;
+ size_t size;
+ int ret = 0;
+ int i;
+
+ /*
+ * global IPBUF
+ */
+ if (((unsigned long)base) & 0x3) {
+ printk(KERN_ERR
+ "omapdsp: global ipbuf address(0x%p) is not "
+ "32-bit aligned!\n", base);
+ return -EINVAL;
+ }
+ size = lsz_byte * ln;
+ if (dsp_address_validate(base, size, "global ipbuf") < 0)
+ return -EINVAL;
+
+ g_ipbuf = kmalloc(sizeof(struct ipbuf_head) * ln, GFP_KERNEL);
+ if (g_ipbuf == NULL) {
+ printk(KERN_ERR
+ "omapdsp: memory allocation for ipbuf failed.\n");
+ return -ENOMEM;
+ }
+ for (i = 0; i < ln; i++) {
+ void *top, *btm;
+
+ top = base + (sizeof(struct ipbuf) + lsz_byte) * i;
+ btm = base + (sizeof(struct ipbuf) + lsz_byte) * (i+1) - 1;
+ g_ipbuf[i].p = (struct ipbuf *)top;
+ g_ipbuf[i].bid = i;
+ if (((unsigned long)top & 0xfffe0000) !=
+ ((unsigned long)btm & 0xfffe0000)) {
+ /*
+ * an ipbuf line should not cross
+ * 64k-word boundary.
+ */
+ printk(KERN_ERR
+ "omapdsp: ipbuf[%d] crosses 64k-word boundary!\n"
+ " @0x%p, size=0x%08x\n", i, top, lsz_byte);
+ ret = -EINVAL;
+ goto free_out;
+ }
+ }
+ ipbcfg.ln = ln;
+ ipbcfg.lsz = lsz;
+ ipbcfg.base = base;
+ ipbcfg.bsycnt = ln; /* DSP holds all ipbufs initially. */
+ ipbcfg.cnt_full = 0;
+
+ pr_info("omapdsp: IPBUF configuration\n"
+ " %d words * %d lines at 0x%p.\n",
+ ipbcfg.lsz, ipbcfg.ln, ipbcfg.base);
+
+ ret = device_create_file(omap_dsp->dev, &dev_attr_ipbuf);
+ if (ret)
+ printk(KERN_ERR "device_create_file failed: %d\n", ret);
+
+ return ret;
+
+ free_out:
+ kfree(g_ipbuf);
+ g_ipbuf = NULL;
+ return ret;
+}
+
+int ipbuf_sys_config(void *p, arm_dsp_dir_t dir)
+{
+ char *dir_str = (dir == DIR_D2A) ? "D2A" : "A2D";
+
+ if (((unsigned long)p) & 0x3) {
+ printk(KERN_ERR
+ "omapdsp: system ipbuf(%s) address(0x%p) is "
+ "not 32-bit aligned!\n", dir_str, p);
+ return -1;
+ }
+ if (dsp_address_validate(p, sizeof(struct ipbuf_sys),
+ "system ipbuf(%s)", dir_str) < 0)
+ return -1;
+ if (dsp_mem_type(p, sizeof(struct ipbuf_sys)) != MEM_TYPE_EXTERN) {
+ printk(KERN_WARNING
+ "omapdsp: system ipbuf(%s) is placed in"
+ " DSP internal memory.\n"
+ " It will prevent DSP from idling.\n", dir_str);
+ ipbuf_sys_hold_mem_active++;
+ /*
+ * dsp_mem_enable() never fails because
+ * it has been already enabled in dspcfg process and
+ * this will just increment the usecount.
+ */
+ dsp_mem_enable((void *)daram_base);
+ }
+
+ if (dir == DIR_D2A)
+ ipbuf_sys_da = p;
+ else
+ ipbuf_sys_ad = p;
+
+ return 0;
+}
+
+int ipbuf_p_validate(void *p, arm_dsp_dir_t dir)
+{
+ char *dir_str = (dir == DIR_D2A) ? "D2A" : "A2D";
+
+ if (((unsigned long)p) & 0x3) {
+ printk(KERN_ERR
+ "omapdsp: private ipbuf(%s) address(0x%p) is "
+ "not 32-bit aligned!\n", dir_str, p);
+ return -1;
+ }
+ return dsp_address_validate(p, sizeof(struct ipbuf_p),
+ "private ipbuf(%s)", dir_str);
+}
+
+/*
+ * Global IPBUF operations
+ */
+struct ipbuf_head *bid_to_ipbuf(u16 bid)
+{
+ return &g_ipbuf[bid];
+}
+
+struct ipbuf_head *get_free_ipbuf(u8 tid)
+{
+ struct ipbuf_head *ipb_h;
+
+ if (dsp_mem_enable_ipbuf() < 0)
+ return NULL;
+
+ spin_lock(&ipb_free.lock);
+
+ if (ipblink_empty(&ipb_free)) {
+ /* FIXME: wait on queue when not available. */
+ ipb_h = NULL;
+ goto out;
+ }
+ ipb_h = &g_ipbuf[ipb_free.top];
+ ipb_h->p->la = tid; /* lock */
+ __ipblink_del_top(&ipb_free);
+out:
+ spin_unlock(&ipb_free.lock);
+ dsp_mem_disable_ipbuf();
+
+ return ipb_h;
+}
+
+void release_ipbuf(struct ipbuf_head *ipb_h)
+{
+ if (ipb_h->p->la == TID_FREE) {
+ printk(KERN_WARNING
+ "omapdsp: attempt to release unlocked IPBUF[%d].\n",
+ ipb_h->bid);
+ /*
+ * FIXME: re-calc bsycnt
+ */
+ return;
+ }
+ ipb_h->p->la = TID_FREE;
+ ipb_h->p->sa = TID_FREE;
+ ipblink_add_tail(&ipb_free, ipb_h->bid);
+}
+
+static int try_yld(struct ipbuf_head *ipb_h)
+{
+ int status;
+
+ ipb_h->p->sa = TID_ANON;
+ status = mbcompose_send(BKYLD, 0, ipb_h->bid);
+ if (status < 0) {
+ /* DSP is busy and ARM keeps this line. */
+ release_ipbuf(ipb_h);
+ return status;
+ }
+
+ ipb_bsycnt_inc(&ipbcfg);
+ return 0;
+}
+
+/*
+ * balancing ipbuf lines with DSP
+ */
+static void do_balance_ipbuf(struct work_struct *unused)
+{
+ while (ipbcfg.bsycnt <= ipbcfg.ln / 4) {
+ struct ipbuf_head *ipb_h;
+
+ if ((ipb_h = get_free_ipbuf(TID_ANON)) == NULL)
+ return;
+ if (try_yld(ipb_h) < 0)
+ return;
+ }
+}
+
+static DECLARE_WORK(balance_ipbuf_work, do_balance_ipbuf);
+
+void balance_ipbuf(void)
+{
+ schedule_work(&balance_ipbuf_work);
+}
+
+/* for process context */
+void unuse_ipbuf(struct ipbuf_head *ipb_h)
+{
+ if (ipbcfg.bsycnt > ipbcfg.ln / 4) {
+ /* we don't have enough IPBUF lines. let's keep it. */
+ release_ipbuf(ipb_h);
+ } else {
+ /* we have enough IPBUF lines. let's return this line to DSP. */
+ ipb_h->p->la = TID_ANON;
+ try_yld(ipb_h);
+ balance_ipbuf();
+ }
+}
+
+/* for interrupt context */
+void unuse_ipbuf_nowait(struct ipbuf_head *ipb_h)
+{
+ release_ipbuf(ipb_h);
+ balance_ipbuf();
+}
+
+/*
+ * functions called from mailbox interrupt routine
+ */
+
+void mbox_err_ipbfull(void)
+{
+ ipbcfg.cnt_full++;
+}
+
+/*
+ * sysfs files
+ */
+static ssize_t ipbuf_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int len = 0;
+ u16 bid;
+
+ for (bid = 0; bid < ipbcfg.ln; bid++) {
+ struct ipbuf_head *ipb_h = &g_ipbuf[bid];
+ u16 la = ipb_h->p->la;
+ u16 ld = ipb_h->p->ld;
+ u16 c = ipb_h->p->c;
+
+ if (len > PAGE_SIZE - 100) {
+ len += sprintf(buf + len, "out of buffer.\n");
+ goto finish;
+ }
+
+ len += sprintf(buf + len, "ipbuf[%d]: adr = 0x%p\n",
+ bid, ipb_h->p);
+ if (la == TID_FREE) {
+ len += sprintf(buf + len,
+ " DSPtask[%d]->Linux "
+ "(already read and now free for Linux)\n",
+ ld);
+ } else if (ld == TID_FREE) {
+ len += sprintf(buf + len,
+ " Linux->DSPtask[%d] "
+ "(already read and now free for DSP)\n",
+ la);
+ } else if (ipbuf_is_held(ld, bid)) {
+ len += sprintf(buf + len,
+ " DSPtask[%d]->Linux "
+ "(waiting to be read)\n"
+ " count = %d\n", ld, c);
+ } else {
+ len += sprintf(buf + len,
+ " Linux->DSPtask[%d] "
+ "(waiting to be read)\n"
+ " count = %d\n", la, c);
+ }
+ }
+
+ len += sprintf(buf + len, "\nFree IPBUF link: ");
+ spin_lock(&ipb_free.lock);
+ ipblink_for_each(bid, &ipb_free) {
+ len += sprintf(buf + len, "%d ", bid);
+ }
+ spin_unlock(&ipb_free.lock);
+ len += sprintf(buf + len, "\n");
+ len += sprintf(buf + len, "IPBFULL error count: %ld\n",
+ ipbcfg.cnt_full);
+
+finish:
+ return len;
+}
--- /dev/null
- #include <asm/arch/mailbox.h>
+/*
+ * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
+ *
+ * Copyright (C) 2003-2006 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
++#include <mach/mailbox.h>
+#include "dsp_mbcmd.h"
+#include "dsp.h"
+
+char *subcmd_name(struct mbcmd *mb)
+{
+ u8 cmd_h = mb->cmd_h;
+ u8 cmd_l = mb->cmd_l;
+ char *s;
+
+ switch (cmd_h) {
+ case MBOX_CMD_DSP_RUNLEVEL:
+ s = (cmd_l == RUNLEVEL_USER) ? "USER":
+ (cmd_l == RUNLEVEL_SUPER) ? "SUPER":
+ (cmd_l == RUNLEVEL_RECOVERY) ? "RECOVERY":
+ NULL;
+ break;
+ case MBOX_CMD_DSP_PM:
+ s = (cmd_l == PM_DISABLE) ? "DISABLE":
+ (cmd_l == PM_ENABLE) ? "ENABLE":
+ NULL;
+ break;
+ case MBOX_CMD_DSP_KFUNC:
+ s = (cmd_l == KFUNC_FBCTL) ? "FBCTL":
+ (cmd_l == KFUNC_POWER) ?
+ ((mb->data == AUDIO_PWR_UP) ? "PWR AUD /UP":
+ (mb->data == AUDIO_PWR_DOWN) ? "PWR AUD /DOWN":
+ (mb->data == AUDIO_PWR_DOWN2) ? "PWR AUD /DOWN(2)":
+ (mb->data == DSP_PWR_UP) ? "PWR DSP /UP":
+ (mb->data == DSP_PWR_DOWN) ? "PWR DSP /DOWN":
+ (mb->data == DVFS_START) ? "PWR DVFS/START":
+ (mb->data == DVFS_STOP) ? "PWR DVFS/STOP":
+ NULL):
+
+ NULL;
+ break;
+ case MBOX_CMD_DSP_DSPCFG:
+ {
+ u8 cfgc = cmd_l & 0x7f;
+ s = (cfgc == DSPCFG_REQ) ? "REQ":
+ (cfgc == DSPCFG_SYSADRH) ? "SYSADRH":
+ (cfgc == DSPCFG_SYSADRL) ? "SYSADRL":
+ (cfgc == DSPCFG_ABORT) ? "ABORT":
+ (cfgc == DSPCFG_PROTREV) ? "PROTREV":
+ NULL;
+ break;
+ }
+ case MBOX_CMD_DSP_REGRW:
+ s = (cmd_l == REGRW_MEMR) ? "MEMR":
+ (cmd_l == REGRW_MEMW) ? "MEMW":
+ (cmd_l == REGRW_IOR) ? "IOR":
+ (cmd_l == REGRW_IOW) ? "IOW":
+ (cmd_l == REGRW_DATA) ? "DATA":
+ NULL;
+ break;
+ case MBOX_CMD_DSP_GETVAR:
+ case MBOX_CMD_DSP_SETVAR:
+ s = (cmd_l == VARID_ICRMASK) ? "ICRMASK":
+ (cmd_l == VARID_LOADINFO) ? "LOADINFO":
+ NULL;
+ break;
+ case MBOX_CMD_DSP_ERR:
+ s = (cmd_l == EID_BADTID) ? "BADTID":
+ (cmd_l == EID_BADTCN) ? "BADTCN":
+ (cmd_l == EID_BADBID) ? "BADBID":
+ (cmd_l == EID_BADCNT) ? "BADCNT":
+ (cmd_l == EID_NOTLOCKED) ? "NOTLOCKED":
+ (cmd_l == EID_STVBUF) ? "STVBUF":
+ (cmd_l == EID_BADADR) ? "BADADR":
+ (cmd_l == EID_BADTCTL) ? "BADTCTL":
+ (cmd_l == EID_BADPARAM) ? "BADPARAM":
+ (cmd_l == EID_FATAL) ? "FATAL":
+ (cmd_l == EID_WDT) ? "WDT":
+ (cmd_l == EID_NOMEM) ? "NOMEM":
+ (cmd_l == EID_NORES) ? "NORES":
+ (cmd_l == EID_IPBFULL) ? "IPBFULL":
+ (cmd_l == EID_TASKNOTRDY) ? "TASKNOTRDY":
+ (cmd_l == EID_TASKBSY) ? "TASKBSY":
+ (cmd_l == EID_TASKERR) ? "TASKERR":
+ (cmd_l == EID_BADCFGTYP) ? "BADCFGTYP":
+ (cmd_l == EID_DEBUG) ? "DEBUG":
+ (cmd_l == EID_BADSEQ) ? "BADSEQ":
+ (cmd_l == EID_BADCMD) ? "BADCMD":
+ NULL;
+ break;
+ default:
+ s = NULL;
+ }
+
+ return s;
+}
+
+/* output of show() method should fit to PAGE_SIZE */
+#define MBLOG_DEPTH 64
+
+struct mblogent {
+ unsigned long jiffies;
+ mbox_msg_t msg;
+ arm_dsp_dir_t dir;
+};
+
+static struct {
+ spinlock_t lock;
+ int wp;
+ unsigned long cnt, cnt_ad, cnt_da;
+ struct mblogent ent[MBLOG_DEPTH];
+} mblog = {
+ .lock = SPIN_LOCK_UNLOCKED,
+};
+
+#ifdef CONFIG_OMAP_DSP_MBCMD_VERBOSE
+static inline void mblog_print_cmd(struct mbcmd *mb, arm_dsp_dir_t dir)
+{
+ const struct cmdinfo *ci = cmdinfo[mb->cmd_h];
+ char *dir_str;
+ char *subname;
+
+ dir_str = (dir == DIR_A2D) ? "sending " : "receiving";
+ switch (ci->cmd_l_type) {
+ case CMD_L_TYPE_SUBCMD:
+ subname = subcmd_name(mb);
+ if (unlikely(!subname))
+ subname = "Unknown";
+ pr_debug("mbox: %s seq=%d, cmd=%02x:%02x(%s:%s), data=%04x\n",
+ dir_str, mb->seq, mb->cmd_h, mb->cmd_l,
+ ci->name, subname, mb->data);
+ break;
+ case CMD_L_TYPE_TID:
+ pr_debug("mbox: %s seq=%d, cmd=%02x:%02x(%s:task %d), data=%04x\n",
+ dir_str, mb->seq, mb->cmd_h, mb->cmd_l,
+ ci->name, mb->cmd_l, mb->data);
+ break;
+ case CMD_L_TYPE_NULL:
+ pr_debug("mbox: %s seq=%d, cmd=%02x:%02x(%s), data=%04x\n",
+ dir_str, mb->seq, mb->cmd_h, mb->cmd_l,
+ ci->name, mb->data);
+ break;
+ }
+}
+#else
+static inline void mblog_print_cmd(struct mbcmd *mb, arm_dsp_dir_t dir) { }
+#endif
+
+void mblog_add(struct mbcmd *mb, arm_dsp_dir_t dir)
+{
+ struct mblogent *ent;
+
+ spin_lock(&mblog.lock);
+ ent = &mblog.ent[mblog.wp];
+ ent->jiffies = jiffies;
+ ent->msg = *(mbox_msg_t *)mb;
+ ent->dir = dir;
+ if (mblog.cnt < 0xffffffff)
+ mblog.cnt++;
+ switch (dir) {
+ case DIR_A2D:
+ if (mblog.cnt_ad < 0xffffffff)
+ mblog.cnt_ad++;
+ break;
+ case DIR_D2A:
+ if (mblog.cnt_da < 0xffffffff)
+ mblog.cnt_da++;
+ break;
+ }
+ if (++mblog.wp == MBLOG_DEPTH)
+ mblog.wp = 0;
+ spin_unlock(&mblog.lock);
+
+ mblog_print_cmd(mb, dir);
+}
+
+/*
+ * sysfs file
+ */
+static ssize_t mblog_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int len = 0;
+ int wp;
+ int i;
+
+ spin_lock(&mblog.lock);
+
+ wp = mblog.wp;
+ len += sprintf(buf + len,
+ "log count:%ld / ARM->DSP:%ld, DSP->ARM:%ld\n",
+ mblog.cnt, mblog.cnt_ad, mblog.cnt_da);
+ if (mblog.cnt == 0)
+ goto done;
+
+ len += sprintf(buf + len, " ARM->DSP ARM<-DSP\n");
+ len += sprintf(buf + len, " jiffies cmd data cmd data\n");
+ i = (mblog.cnt >= MBLOG_DEPTH) ? wp : 0;
+ do {
+ struct mblogent *ent = &mblog.ent[i];
+ struct mbcmd *mb = (struct mbcmd *)&ent->msg;
+ char *subname;
+ struct cmdinfo ci_null = {
+ .name = "Unknown",
+ .cmd_l_type = CMD_L_TYPE_NULL,
+ };
+ const struct cmdinfo *ci;
+
+ len += sprintf(buf + len,
+ (ent->dir == DIR_A2D) ?
+ "%08lx %04x %04x ":
+ "%08lx %04x %04x ",
+ ent->jiffies,
+ (ent->msg >> 16) & 0x7fff, ent->msg & 0xffff);
+
+ if ((ci = cmdinfo[mb->cmd_h]) == NULL)
+ ci = &ci_null;
+
+ switch (ci->cmd_l_type) {
+ case CMD_L_TYPE_SUBCMD:
+ if ((subname = subcmd_name(mb)) == NULL)
+ subname = "Unknown";
+ len += sprintf(buf + len, "%s:%s\n",
+ ci->name, subname);
+ break;
+ case CMD_L_TYPE_TID:
+ len += sprintf(buf + len, "%s:task %d\n",
+ ci->name, mb->cmd_l);
+ break;
+ case CMD_L_TYPE_NULL:
+ len += sprintf(buf + len, "%s\n", ci->name);
+ break;
+ }
+
+ if (++i == MBLOG_DEPTH)
+ i = 0;
+ } while (i != wp);
+
+done:
+ spin_unlock(&mblog.lock);
+
+ return len;
+}
+
+static struct device_attribute dev_attr_mblog = __ATTR_RO(mblog);
+
+void __init mblog_init(void)
+{
+ int ret;
+
+ ret = device_create_file(omap_dsp->dev, &dev_attr_mblog);
+ if (ret)
+ printk(KERN_ERR "device_create_file failed: %d\n", ret);
+}
+
+void mblog_exit(void)
+{
+ device_remove_file(omap_dsp->dev, &dev_attr_mblog);
+}
--- /dev/null
- #include <asm/arch/hardware.h>
+/*
+ * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
+ *
+ * Copyright (C) 2006 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __OMAP_DSP_OMAP2_DSP_H
+#define __OMAP_DSP_OMAP2_DSP_H
+
+#ifdef CONFIG_ARCH_OMAP24XX
+#define OMAP24XX_DARAM_BASE (DSP_MEM_24XX_VIRT + 0x0)
+#define OMAP24XX_DARAM_SIZE 0x10000
+#define OMAP24XX_SARAM_BASE (DSP_MEM_24XX_VIRT + 0x10000)
+#define OMAP24XX_SARAM_SIZE 0x18000
+#endif
+
++#include <mach/hardware.h>
+
+/*
+ * DSP IPI registers: mapped to 0xe1000000 -- use readX(), writeX()
+ */
+#ifdef CONFIG_ARCH_OMAP24XX
+#define DSP_IPI_BASE DSP_IPI_24XX_VIRT
+#endif
+
+#ifdef CONFIG_ARCH_OMAP34XX
+#define DSP_IPI_BASE DSP_IPI_34XX_VIRT
+#endif
+
+#define DSP_IPI_REVISION (DSP_IPI_BASE + 0x00)
+#define DSP_IPI_SYSCONFIG (DSP_IPI_BASE + 0x10)
+#define DSP_IPI_INDEX (DSP_IPI_BASE + 0x40)
+#define DSP_IPI_ENTRY (DSP_IPI_BASE + 0x44)
+#define DSP_IPI_ENABLE (DSP_IPI_BASE + 0x48)
+#define DSP_IPI_IOMAP (DSP_IPI_BASE + 0x4c)
+#define DSP_IPI_DSPBOOTCONFIG (DSP_IPI_BASE + 0x50)
+
+#define DSP_IPI_ENTRY_ELMSIZEVALUE_MASK 0x00000003
+#define DSP_IPI_ENTRY_ELMSIZEVALUE_8 0x00000000
+#define DSP_IPI_ENTRY_ELMSIZEVALUE_16 0x00000001
+#define DSP_IPI_ENTRY_ELMSIZEVALUE_32 0x00000002
+
+#define DSP_BOOT_CONFIG_DIRECT 0x00000000
+#define DSP_BOOT_CONFIG_PSD_DIRECT 0x00000001
+#define DSP_BOOT_CONFIG_IDLE 0x00000002
+#define DSP_BOOT_CONFIG_DL16 0x00000003
+#define DSP_BOOT_CONFIG_DL32 0x00000004
+#define DSP_BOOT_CONFIG_API 0x00000005
+#define DSP_BOOT_CONFIG_INTERNAL 0x00000006
+
+/*
+ * DSP boot mode
+ * direct: 0xffff00
+ * pseudo direct: 0x080000
+ * API: branch 0x010000
+ * internel: branch 0x024000
+ */
+#define DSP_BOOT_ADR_DIRECT 0xffff00
+#define DSP_BOOT_ADR_PSD_DIRECT 0x080000
+#define DSP_BOOT_ADR_API 0x010000
+#define DSP_BOOT_ADR_INTERNAL 0x024000
+
+/*
+ * DSP ICR
+ */
+#define DSPREG_ICR_RESERVED_BITS 0xfc00
+#define DSPREG_ICR_HWA 0x0200
+#define DSPREG_ICR_IPORT 0x0100
+#define DSPREG_ICR_MPORT 0x0080
+#define DSPREG_ICR_XPORT 0x0040
+#define DSPREG_ICR_DPORT 0x0020
+#define DSPREG_ICR_DPLL 0x0010
+#define DSPREG_ICR_PER 0x0008
+#define DSPREG_ICR_CACHE 0x0004
+#define DSPREG_ICR_DMA 0x0002
+#define DSPREG_ICR_CPU 0x0001
+
+#endif /* __OMAP_DSP_OMAP2_DSP_H */
--- /dev/null
- #include <asm/arch/mailbox.h>
- #include <asm/arch/dsp.h>
+/*
+ * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
+ *
+ * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
++#include <mach/mailbox.h>
++#include <mach/dsp.h>
+#include "uaccess_dsp.h"
+#include "dsp_mbcmd.h"
+#include "dsp.h"
+#include "ipbuf.h"
+#include "proclist.h"
+
+/*
+ * devstate: task device state machine
+ * NOTASK: task is not attached.
+ * ATTACHED: task is attached.
+ * GARBAGE: task is detached. waiting for all processes to close this device.
+ * ADDREQ: requesting for tadd
+ * DELREQ: requesting for tdel. no process is opening this device.
+ * FREEZED: task is attached, but reserved to be killed.
+ * ADDFAIL: tadd failed.
+ * ADDING: tadd in process.
+ * DELING: tdel in process.
+ * KILLING: tkill in process.
+ */
+#define TASKDEV_ST_NOTASK 0x00000001
+#define TASKDEV_ST_ATTACHED 0x00000002
+#define TASKDEV_ST_GARBAGE 0x00000004
+#define TASKDEV_ST_INVALID 0x00000008
+#define TASKDEV_ST_ADDREQ 0x00000100
+#define TASKDEV_ST_DELREQ 0x00000200
+#define TASKDEV_ST_FREEZED 0x00000400
+#define TASKDEV_ST_ADDFAIL 0x00001000
+#define TASKDEV_ST_ADDING 0x00010000
+#define TASKDEV_ST_DELING 0x00020000
+#define TASKDEV_ST_KILLING 0x00040000
+#define TASKDEV_ST_STATE_MASK 0x7fffffff
+#define TASKDEV_ST_STALE 0x80000000
+
+static struct {
+ long state;
+ char *name;
+} devstate_desc[] = {
+ { TASKDEV_ST_NOTASK, "notask" },
+ { TASKDEV_ST_ATTACHED, "attached" },
+ { TASKDEV_ST_GARBAGE, "garbage" },
+ { TASKDEV_ST_INVALID, "invalid" },
+ { TASKDEV_ST_ADDREQ, "addreq" },
+ { TASKDEV_ST_DELREQ, "delreq" },
+ { TASKDEV_ST_FREEZED, "freezed" },
+ { TASKDEV_ST_ADDFAIL, "addfail" },
+ { TASKDEV_ST_ADDING, "adding" },
+ { TASKDEV_ST_DELING, "deling" },
+ { TASKDEV_ST_KILLING, "killing" },
+};
+
+static char *devstate_name(long state)
+{
+ int i;
+ int max = ARRAY_SIZE(devstate_desc);
+
+ for (i = 0; i < max; i++) {
+ if (state & devstate_desc[i].state)
+ return devstate_desc[i].name;
+ }
+ return "unknown";
+}
+
+struct rcvdt_bk_struct {
+ struct ipblink link;
+ unsigned int rp;
+};
+
+struct taskdev {
+ struct bus_type *bus;
+ struct device dev; /* Generic device interface */
+
+ long state;
+ struct rw_semaphore state_sem;
+ wait_queue_head_t state_wait_q;
+ struct mutex usecount_lock;
+ unsigned int usecount;
+ char name[TNM_LEN];
+ struct file_operations fops;
+ spinlock_t proc_list_lock;
+ struct list_head proc_list;
+ struct dsptask *task;
+
+ /* read stuff */
+ wait_queue_head_t read_wait_q;
+ struct mutex read_mutex;
+ spinlock_t read_lock;
+ union {
+ struct kfifo *fifo; /* for active word */
+ struct rcvdt_bk_struct bk;
+ } rcvdt;
+
+ /* write stuff */
+ wait_queue_head_t write_wait_q;
+ struct mutex write_mutex;
+ spinlock_t wsz_lock;
+ size_t wsz;
+
+ /* tctl stuff */
+ wait_queue_head_t tctl_wait_q;
+ struct mutex tctl_mutex;
+ int tctl_stat;
+ int tctl_ret; /* return value for tctl_show() */
+
+ /* device lock */
+ struct mutex lock;
+ pid_t lock_pid;
+};
+
+#define to_taskdev(n) container_of(n, struct taskdev, dev)
+
+struct dsptask {
+ enum {
+ TASK_ST_ERR = 0,
+ TASK_ST_READY,
+ TASK_ST_CFGREQ
+ } state;
+ u8 tid;
+ char name[TNM_LEN];
+ u16 ttyp;
+ struct taskdev *dev;
+
+ /* read stuff */
+ struct ipbuf_p *ipbuf_pvt_r;
+
+ /* write stuff */
+ struct ipbuf_p *ipbuf_pvt_w;
+
+ /* mmap stuff */
+ void *map_base;
+ size_t map_length;
+};
+
+#define sndtyp_acv(ttyp) ((ttyp) & TTYP_ASND)
+#define sndtyp_psv(ttyp) (!((ttyp) & TTYP_ASND))
+#define sndtyp_bk(ttyp) ((ttyp) & TTYP_BKDM)
+#define sndtyp_wd(ttyp) (!((ttyp) & TTYP_BKDM))
+#define sndtyp_pvt(ttyp) ((ttyp) & TTYP_PVDM)
+#define sndtyp_gbl(ttyp) (!((ttyp) & TTYP_PVDM))
+#define rcvtyp_acv(ttyp) ((ttyp) & TTYP_ARCV)
+#define rcvtyp_psv(ttyp) (!((ttyp) & TTYP_ARCV))
+#define rcvtyp_bk(ttyp) ((ttyp) & TTYP_BKMD)
+#define rcvtyp_wd(ttyp) (!((ttyp) & TTYP_BKMD))
+#define rcvtyp_pvt(ttyp) ((ttyp) & TTYP_PVMD)
+#define rcvtyp_gbl(ttyp) (!((ttyp) & TTYP_PVMD))
+
+static inline int has_taskdev_lock(struct taskdev *dev);
+static int dsp_rmdev_minor(unsigned char minor);
+static int taskdev_init(struct taskdev *dev, char *name, unsigned char minor);
+static void taskdev_delete(unsigned char minor);
+static int taskdev_attach_task(struct taskdev *dev, struct dsptask *task);
+static int dsp_tdel_bh(struct taskdev *dev, u16 type);
+
+static struct bus_type dsptask_bus = {
+ .name = "dsptask",
+};
+
+static struct class *dsp_task_class;
+static DEFINE_MUTEX(devmgr_lock);
+static struct taskdev *taskdev[TASKDEV_MAX];
+static struct dsptask *dsptask[TASKDEV_MAX];
+static DEFINE_MUTEX(cfg_lock);
+static u16 cfg_cmd;
+static u8 cfg_tid;
+static DECLARE_WAIT_QUEUE_HEAD(cfg_wait_q);
+static u8 n_task; /* static task count */
+static void *heap;
+
+#define is_dynamic_task(tid) ((tid) >= n_task)
+
+#define devstate_read_lock(dev, devstate) \
+ devstate_read_lock_timeout(dev, devstate, 0)
+#define devstate_read_unlock(dev) up_read(&(dev)->state_sem)
+#define devstate_write_lock(dev, devstate) \
+ devstate_write_lock_timeout(dev, devstate, 0)
+#define devstate_write_unlock(dev) up_write(&(dev)->state_sem)
+
+static ssize_t devname_show(struct device *d, struct device_attribute *attr,
+ char *buf);
+static ssize_t devstate_show(struct device *d, struct device_attribute *attr,
+ char *buf);
+static ssize_t proc_list_show(struct device *d, struct device_attribute *attr,
+ char *buf);
+static ssize_t taskname_show(struct device *d, struct device_attribute *attr,
+ char *buf);
+static ssize_t ttyp_show(struct device *d, struct device_attribute *attr,
+ char *buf);
+static ssize_t fifosz_show(struct device *d, struct device_attribute *attr,
+ char *buf);
+static int fifosz_store(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t fifocnt_show(struct device *d, struct device_attribute *attr,
+ char *buf);
+static ssize_t ipblink_show(struct device *d, struct device_attribute *attr,
+ char *buf);
+static ssize_t wsz_show(struct device *d, struct device_attribute *attr,
+ char *buf);
+static ssize_t mmap_show(struct device *d, struct device_attribute *attr,
+ char *buf);
+
+#define __ATTR_RW(_name,_mode) { \
+ .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \
+ .show = _name##_show, \
+ .store = _name##_store, \
+}
+
+static struct device_attribute dev_attr_devname = __ATTR_RO(devname);
+static struct device_attribute dev_attr_devstate = __ATTR_RO(devstate);
+static struct device_attribute dev_attr_proc_list = __ATTR_RO(proc_list);
+static struct device_attribute dev_attr_taskname = __ATTR_RO(taskname);
+static struct device_attribute dev_attr_ttyp = __ATTR_RO(ttyp);
+static struct device_attribute dev_attr_fifosz = __ATTR_RW(fifosz, 0666);
+static struct device_attribute dev_attr_fifocnt = __ATTR_RO(fifocnt);
+static struct device_attribute dev_attr_ipblink = __ATTR_RO(ipblink);
+static struct device_attribute dev_attr_wsz = __ATTR_RO(wsz);
+static struct device_attribute dev_attr_mmap = __ATTR_RO(mmap);
+
+static inline void set_taskdev_state(struct taskdev *dev, int state)
+{
+ pr_debug("omapdsp: devstate: CHANGE %s[%d]:\"%s\"->\"%s\"\n",
+ dev->name,
+ (dev->task ? dev->task->tid : -1),
+ devstate_name(dev->state),
+ devstate_name(state));
+ dev->state = state;
+}
+
+/*
+ * devstate_read_lock_timeout()
+ * devstate_write_lock_timeout():
+ * timeout != 0: dev->state can be diffeent from what you want.
+ * timeout == 0: no timeout
+ */
+#define BUILD_DEVSTATE_LOCK_TIMEOUT(rw) \
+static int devstate_##rw##_lock_timeout(struct taskdev *dev, long devstate, \
+ int timeout) \
+{ \
+ DEFINE_WAIT(wait); \
+ down_##rw(&dev->state_sem); \
+ while (!(dev->state & devstate)) { \
+ up_##rw(&dev->state_sem); \
+ prepare_to_wait(&dev->state_wait_q, &wait, TASK_INTERRUPTIBLE); \
+ if (!timeout) \
+ timeout = MAX_SCHEDULE_TIMEOUT; \
+ timeout = schedule_timeout(timeout); \
+ finish_wait(&dev->state_wait_q, &wait); \
+ if (timeout == 0) \
+ return -ETIME; \
+ if (signal_pending(current)) \
+ return -EINTR; \
+ down_##rw(&dev->state_sem); \
+ } \
+ return 0; \
+}
+BUILD_DEVSTATE_LOCK_TIMEOUT(read)
+BUILD_DEVSTATE_LOCK_TIMEOUT(write)
+
+#define BUILD_DEVSTATE_LOCK_AND_TEST(rw) \
+static int devstate_##rw##_lock_and_test(struct taskdev *dev, long devstate) \
+{ \
+ down_##rw(&dev->state_sem); \
+ if (dev->state & devstate) \
+ return 1; /* success */ \
+ /* failure */ \
+ up_##rw(&dev->state_sem); \
+ return 0; \
+}
+BUILD_DEVSTATE_LOCK_AND_TEST(read)
+BUILD_DEVSTATE_LOCK_AND_TEST(write)
+
+static int taskdev_lock_interruptible(struct taskdev *dev,
+ struct mutex *lock)
+{
+ int ret;
+
+ if (has_taskdev_lock(dev))
+ ret = mutex_lock_interruptible(lock);
+ else {
+ if ((ret = mutex_lock_interruptible(&dev->lock)) != 0)
+ return ret;
+ ret = mutex_lock_interruptible(lock);
+ mutex_unlock(&dev->lock);
+ }
+
+ return ret;
+}
+
+static int taskdev_lock_and_statelock_attached(struct taskdev *dev,
+ struct mutex *lock)
+{
+ int ret;
+
+ if (!devstate_read_lock_and_test(dev, TASKDEV_ST_ATTACHED))
+ return -ENODEV;
+
+ if ((ret = taskdev_lock_interruptible(dev, lock)) != 0)
+ devstate_read_unlock(dev);
+
+ return ret;
+}
+
+static inline void taskdev_unlock_and_stateunlock(struct taskdev *dev,
+ struct mutex *lock)
+{
+ mutex_unlock(lock);
+ devstate_read_unlock(dev);
+}
+
+/*
+ * taskdev_flush_buf()
+ * must be called under state_lock(ATTACHED) and dev->read_mutex.
+ */
+static int taskdev_flush_buf(struct taskdev *dev)
+{
+ u16 ttyp = dev->task->ttyp;
+
+ if (sndtyp_wd(ttyp)) {
+ /* word receiving */
+ kfifo_reset(dev->rcvdt.fifo);
+ } else {
+ /* block receiving */
+ struct rcvdt_bk_struct *rcvdt = &dev->rcvdt.bk;
+
+ if (sndtyp_gbl(ttyp))
+ ipblink_flush(&rcvdt->link);
+ else {
+ ipblink_flush_pvt(&rcvdt->link);
+ release_ipbuf_pvt(dev->task->ipbuf_pvt_r);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * taskdev_set_fifosz()
+ * must be called under dev->read_mutex.
+ */
+static int taskdev_set_fifosz(struct taskdev *dev, unsigned long sz)
+{
+ u16 ttyp = dev->task->ttyp;
+
+ if (!(sndtyp_wd(ttyp) && sndtyp_acv(ttyp))) {
+ printk(KERN_ERR
+ "omapdsp: buffer size can be changed only for "
+ "active word sending task.\n");
+ return -EINVAL;
+ }
+ if ((sz == 0) || (sz & 1)) {
+ printk(KERN_ERR "omapdsp: illegal buffer size! (%ld)\n"
+ "it must be even and non-zero value.\n", sz);
+ return -EINVAL;
+ }
+
+ if (kfifo_len(dev->rcvdt.fifo)) {
+ printk(KERN_ERR "omapdsp: buffer is not empty!\n");
+ return -EIO;
+ }
+
+ kfifo_free(dev->rcvdt.fifo);
+ dev->rcvdt.fifo = kfifo_alloc(sz, GFP_KERNEL, &dev->read_lock);
+ if (IS_ERR(dev->rcvdt.fifo)) {
+ printk(KERN_ERR
+ "omapdsp: unable to change receive buffer size. "
+ "(%ld bytes for %s)\n", sz, dev->name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static inline int has_taskdev_lock(struct taskdev *dev)
+{
+ return (dev->lock_pid == current->pid);
+}
+
+static int taskdev_lock(struct taskdev *dev)
+{
+ if (mutex_lock_interruptible(&dev->lock))
+ return -EINTR;
+ dev->lock_pid = current->pid;
+ return 0;
+}
+
+static int taskdev_unlock(struct taskdev *dev)
+{
+ if (!has_taskdev_lock(dev)) {
+ printk(KERN_ERR
+ "omapdsp: an illegal process attempted to "
+ "unlock the dsptask lock!\n");
+ return -EINVAL;
+ }
+ dev->lock_pid = 0;
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int dsp_task_config(struct dsptask *task, u8 tid)
+{
+ u16 ttyp;
+ int ret;
+
+ task->tid = tid;
+ dsptask[tid] = task;
+
+ /* TCFG request */
+ task->state = TASK_ST_CFGREQ;
+ if (mutex_lock_interruptible(&cfg_lock)) {
+ ret = -EINTR;
+ goto fail_out;
+ }
+ cfg_cmd = MBOX_CMD_DSP_TCFG;
+ mbcompose_send_and_wait(TCFG, tid, 0, &cfg_wait_q);
+ cfg_cmd = 0;
+ mutex_unlock(&cfg_lock);
+
+ if (task->state != TASK_ST_READY) {
+ printk(KERN_ERR "omapdsp: task %d configuration error!\n", tid);
+ ret = -EINVAL;
+ goto fail_out;
+ }
+
+ if (strlen(task->name) <= 1)
+ sprintf(task->name, "%d", tid);
+ pr_info("omapdsp: task %d: name %s\n", tid, task->name);
+
+ ttyp = task->ttyp;
+
+ /*
+ * task info sanity check
+ */
+
+ /* task type check */
+ if (rcvtyp_psv(ttyp) && rcvtyp_pvt(ttyp)) {
+ printk(KERN_ERR "omapdsp: illegal task type(0x%04x), tid=%d\n",
+ tid, ttyp);
+ ret = -EINVAL;
+ goto fail_out;
+ }
+
+ /* private buffer address check */
+ if (sndtyp_pvt(ttyp) &&
+ (ipbuf_p_validate(task->ipbuf_pvt_r, DIR_D2A) < 0)) {
+ ret = -EINVAL;
+ goto fail_out;
+ }
+ if (rcvtyp_pvt(ttyp) &&
+ (ipbuf_p_validate(task->ipbuf_pvt_w, DIR_A2D) < 0)) {
+ ret = -EINVAL;
+ goto fail_out;
+ }
+
+ /* mmap buffer configuration check */
+ if ((task->map_length > 0) &&
+ ((!ALIGN((unsigned long)task->map_base, PAGE_SIZE)) ||
+ (!ALIGN(task->map_length, PAGE_SIZE)) ||
+ (dsp_mem_type(task->map_base, task->map_length) != MEM_TYPE_EXTERN))) {
+ printk(KERN_ERR
+ "omapdsp: illegal mmap buffer address(0x%p) or "
+ "length(0x%x).\n"
+ " It needs to be page-aligned and located at "
+ "external memory.\n",
+ task->map_base, task->map_length);
+ ret = -EINVAL;
+ goto fail_out;
+ }
+
+ return 0;
+
+fail_out:
+ dsptask[tid] = NULL;
+ return ret;
+}
+
+static void dsp_task_init(struct dsptask *task)
+{
+ mbcompose_send(TCTL, task->tid, TCTL_TINIT);
+}
+
+int dsp_task_config_all(u8 n)
+{
+ int i, ret;
+ struct taskdev *devheap;
+ struct dsptask *taskheap;
+ size_t devheapsz, taskheapsz;
+
+ pr_info("omapdsp: found %d task(s)\n", n);
+ if (n == 0)
+ return 0;
+
+ /*
+ * reducing kmalloc!
+ */
+ devheapsz = sizeof(struct taskdev) * n;
+ taskheapsz = sizeof(struct dsptask) * n;
+ heap = kzalloc(devheapsz + taskheapsz, GFP_KERNEL);
+ if (heap == NULL)
+ return -ENOMEM;
+ devheap = heap;
+ taskheap = heap + devheapsz;
+
+ n_task = n;
+ for (i = 0; i < n; i++) {
+ struct taskdev *dev = &devheap[i];
+ struct dsptask *task = &taskheap[i];
+
+ if ((ret = dsp_task_config(task, i)) < 0)
+ return ret;
+ if ((ret = taskdev_init(dev, task->name, i)) < 0)
+ return ret;
+ if ((ret = taskdev_attach_task(dev, task)) < 0)
+ return ret;
+ dsp_task_init(task);
+ pr_info("omapdsp: taskdev %s enabled.\n", dev->name);
+ }
+
+ return 0;
+}
+
+static void dsp_task_unconfig(struct dsptask *task)
+{
+ dsptask[task->tid] = NULL;
+}
+
+void dsp_task_unconfig_all(void)
+{
+ unsigned char minor;
+ u8 tid;
+ struct dsptask *task;
+
+ for (minor = 0; minor < n_task; minor++) {
+ /*
+ * taskdev[minor] can be NULL in case of
+ * configuration failure
+ */
+ if (taskdev[minor])
+ taskdev_delete(minor);
+ }
+ for (; minor < TASKDEV_MAX; minor++) {
+ if (taskdev[minor])
+ dsp_rmdev_minor(minor);
+ }
+
+ for (tid = 0; tid < n_task; tid++) {
+ /*
+ * dsptask[tid] can be NULL in case of
+ * configuration failure
+ */
+ task = dsptask[tid];
+ if (task)
+ dsp_task_unconfig(task);
+ }
+ for (; tid < TASKDEV_MAX; tid++) {
+ task = dsptask[tid];
+ if (task) {
+ /*
+ * on-demand tasks should be deleted in
+ * rmdev_minor(), but just in case.
+ */
+ dsp_task_unconfig(task);
+ kfree(task);
+ }
+ }
+
+ if (heap) {
+ kfree(heap);
+ heap = NULL;
+ }
+
+ n_task = 0;
+}
+
+static struct device_driver dsptask_driver = {
+ .name = "dsptask",
+ .bus = &dsptask_bus,
+};
+
+u8 dsp_task_count(void)
+{
+ return n_task;
+}
+
+int dsp_taskmod_busy(void)
+{
+ struct taskdev *dev;
+ unsigned char minor;
+ unsigned int usecount;
+
+ for (minor = 0; minor < TASKDEV_MAX; minor++) {
+ dev = taskdev[minor];
+ if (dev == NULL)
+ continue;
+ if ((usecount = dev->usecount) > 0) {
+ printk("dsp_taskmod_busy(): %s: usecount=%d\n",
+ dev->name, usecount);
+ return 1;
+ }
+/*
+ if ((dev->state & (TASKDEV_ST_ADDREQ |
+ TASKDEV_ST_DELREQ)) {
+*/
+ if (dev->state & TASKDEV_ST_ADDREQ) {
+ printk("dsp_taskmod_busy(): %s is in %s\n",
+ dev->name, devstate_name(dev->state));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * DSP task device file operations
+ */
+static ssize_t dsp_task_read_wd_acv(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ struct taskdev *dev = taskdev[minor];
+ int ret = 0;
+ DEFINE_WAIT(wait);
+
+ if (count == 0) {
+ return 0;
+ } else if (count & 0x1) {
+ printk(KERN_ERR
+ "omapdsp: odd count is illegal for DSP task device.\n");
+ return -EINVAL;
+ }
+
+ if (taskdev_lock_and_statelock_attached(dev, &dev->read_mutex))
+ return -ENODEV;
+
+
+ prepare_to_wait(&dev->read_wait_q, &wait, TASK_INTERRUPTIBLE);
+ if (kfifo_len(dev->rcvdt.fifo) == 0)
+ schedule();
+ finish_wait(&dev->read_wait_q, &wait);
+ if (kfifo_len(dev->rcvdt.fifo) == 0) {
+ /* failure */
+ if (signal_pending(current))
+ ret = -EINTR;
+ goto up_out;
+ }
+
+
+ ret = kfifo_get_to_user(dev->rcvdt.fifo, buf, count);
+
+ up_out:
+ taskdev_unlock_and_stateunlock(dev, &dev->read_mutex);
+ return ret;
+}
+
+static ssize_t dsp_task_read_bk_acv(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ struct taskdev *dev = taskdev[minor];
+ struct rcvdt_bk_struct *rcvdt = &dev->rcvdt.bk;
+ ssize_t ret = 0;
+ DEFINE_WAIT(wait);
+
+ if (count == 0) {
+ return 0;
+ } else if (count & 0x1) {
+ printk(KERN_ERR
+ "omapdsp: odd count is illegal for DSP task device.\n");
+ return -EINVAL;
+ } else if ((int)buf & 0x1) {
+ printk(KERN_ERR
+ "omapdsp: buf should be word aligned for "
+ "dsp_task_read().\n");
+ return -EINVAL;
+ }
+
+ if (taskdev_lock_and_statelock_attached(dev, &dev->read_mutex))
+ return -ENODEV;
+
+ prepare_to_wait(&dev->read_wait_q, &wait, TASK_INTERRUPTIBLE);
+ if (ipblink_empty(&rcvdt->link))
+ schedule();
+ finish_wait(&dev->read_wait_q, &wait);
+ if (ipblink_empty(&rcvdt->link)) {
+ /* failure */
+ if (signal_pending(current))
+ ret = -EINTR;
+ goto up_out;
+ }
+
+ /* copy from delayed IPBUF */
+ if (sndtyp_pvt(dev->task->ttyp)) {
+ /* private */
+ if (!ipblink_empty(&rcvdt->link)) {
+ struct ipbuf_p *ipbp = dev->task->ipbuf_pvt_r;
+ unsigned char *base, *src;
+ size_t bkcnt;
+
+ if (dsp_mem_enable(ipbp) < 0) {
+ ret = -EBUSY;
+ goto up_out;
+ }
+ base = MKVIRT(ipbp->ah, ipbp->al);
+ bkcnt = ((unsigned long)ipbp->c) * 2 - rcvdt->rp;
+ if (dsp_address_validate(base, bkcnt,
+ "task %s read buffer",
+ dev->task->name) < 0) {
+ ret = -EINVAL;
+ goto pv_out1;
+ }
+ if (dsp_mem_enable(base) < 0) {
+ ret = -EBUSY;
+ goto pv_out1;
+ }
+ src = base + rcvdt->rp;
+ if (bkcnt > count) {
+ if (copy_to_user_dsp(buf, src, count)) {
+ ret = -EFAULT;
+ goto pv_out2;
+ }
+ ret = count;
+ rcvdt->rp += count;
+ } else {
+ if (copy_to_user_dsp(buf, src, bkcnt)) {
+ ret = -EFAULT;
+ goto pv_out2;
+ }
+ ret = bkcnt;
+ ipblink_del_pvt(&rcvdt->link);
+ release_ipbuf_pvt(ipbp);
+ rcvdt->rp = 0;
+ }
+ pv_out2:
+ dsp_mem_disable(src);
+ pv_out1:
+ dsp_mem_disable(ipbp);
+ }
+ } else {
+ /* global */
+ if (dsp_mem_enable_ipbuf() < 0) {
+ ret = -EBUSY;
+ goto up_out;
+ }
+ while (!ipblink_empty(&rcvdt->link)) {
+ unsigned char *src;
+ size_t bkcnt;
+ struct ipbuf_head *ipb_h = bid_to_ipbuf(rcvdt->link.top);
+
+ src = ipb_h->p->d + rcvdt->rp;
+ bkcnt = ((unsigned long)ipb_h->p->c) * 2 - rcvdt->rp;
+ if (bkcnt > count) {
+ if (copy_to_user_dsp(buf, src, count)) {
+ ret = -EFAULT;
+ goto gb_out;
+ }
+ ret += count;
+ rcvdt->rp += count;
+ break;
+ } else {
+ if (copy_to_user_dsp(buf, src, bkcnt)) {
+ ret = -EFAULT;
+ goto gb_out;
+ }
+ ret += bkcnt;
+ buf += bkcnt;
+ count -= bkcnt;
+ ipblink_del_top(&rcvdt->link);
+ unuse_ipbuf(ipb_h);
+ rcvdt->rp = 0;
+ }
+ }
+ gb_out:
+ dsp_mem_disable_ipbuf();
+ }
+
+ up_out:
+ taskdev_unlock_and_stateunlock(dev, &dev->read_mutex);
+ return ret;
+}
+
+static ssize_t dsp_task_read_wd_psv(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ struct taskdev *dev = taskdev[minor];
+ int ret = 0;
+
+ if (count == 0) {
+ return 0;
+ } else if (count & 0x1) {
+ printk(KERN_ERR
+ "omapdsp: odd count is illegal for DSP task device.\n");
+ return -EINVAL;
+ } else {
+ /* force! */
+ count = 2;
+ }
+
+ if (taskdev_lock_and_statelock_attached(dev, &dev->read_mutex))
+ return -ENODEV;
+
+ mbcompose_send_and_wait(WDREQ, dev->task->tid, 0, &dev->read_wait_q);
+
+ if (kfifo_len(dev->rcvdt.fifo) == 0) {
+ /* failure */
+ if (signal_pending(current))
+ ret = -EINTR;
+ goto up_out;
+ }
+
+ ret = kfifo_get_to_user(dev->rcvdt.fifo, buf, count);
+
+up_out:
+ taskdev_unlock_and_stateunlock(dev, &dev->read_mutex);
+ return ret;
+}
+
+static ssize_t dsp_task_read_bk_psv(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ struct taskdev *dev = taskdev[minor];
+ struct rcvdt_bk_struct *rcvdt = &dev->rcvdt.bk;
+ int ret = 0;
+
+ if (count == 0) {
+ return 0;
+ } else if (count & 0x1) {
+ printk(KERN_ERR
+ "omapdsp: odd count is illegal for DSP task device.\n");
+ return -EINVAL;
+ } else if ((int)buf & 0x1) {
+ printk(KERN_ERR
+ "omapdsp: buf should be word aligned for "
+ "dsp_task_read().\n");
+ return -EINVAL;
+ }
+
+ if (taskdev_lock_and_statelock_attached(dev, &dev->read_mutex))
+ return -ENODEV;
+
+ mbcompose_send_and_wait(BKREQ, dev->task->tid, count/2,
+ &dev->read_wait_q);
+
+ if (ipblink_empty(&rcvdt->link)) {
+ /* failure */
+ if (signal_pending(current))
+ ret = -EINTR;
+ goto up_out;
+ }
+
+ /*
+ * We will not receive more than requested count.
+ */
+ if (sndtyp_pvt(dev->task->ttyp)) {
+ /* private */
+ struct ipbuf_p *ipbp = dev->task->ipbuf_pvt_r;
+ size_t rcvcnt;
+ void *src;
+
+ if (dsp_mem_enable(ipbp) < 0) {
+ ret = -EBUSY;
+ goto up_out;
+ }
+ src = MKVIRT(ipbp->ah, ipbp->al);
+ rcvcnt = ((unsigned long)ipbp->c) * 2;
+ if (dsp_address_validate(src, rcvcnt, "task %s read buffer",
+ dev->task->name) < 0) {
+ ret = -EINVAL;
+ goto pv_out1;
+ }
+ if (dsp_mem_enable(src) < 0) {
+ ret = -EBUSY;
+ goto pv_out1;
+ }
+ if (count > rcvcnt)
+ count = rcvcnt;
+ if (copy_to_user_dsp(buf, src, count)) {
+ ret = -EFAULT;
+ goto pv_out2;
+ }
+ ipblink_del_pvt(&rcvdt->link);
+ release_ipbuf_pvt(ipbp);
+ ret = count;
+pv_out2:
+ dsp_mem_disable(src);
+pv_out1:
+ dsp_mem_disable(ipbp);
+ } else {
+ /* global */
+ struct ipbuf_head *ipb_h = bid_to_ipbuf(rcvdt->link.top);
+ size_t rcvcnt;
+
+ if (dsp_mem_enable_ipbuf() < 0) {
+ ret = -EBUSY;
+ goto up_out;
+ }
+ rcvcnt = ((unsigned long)ipb_h->p->c) * 2;
+ if (count > rcvcnt)
+ count = rcvcnt;
+ if (copy_to_user_dsp(buf, ipb_h->p->d, count)) {
+ ret = -EFAULT;
+ goto gb_out;
+ }
+ ipblink_del_top(&rcvdt->link);
+ unuse_ipbuf(ipb_h);
+ ret = count;
+gb_out:
+ dsp_mem_disable_ipbuf();
+ }
+
+up_out:
+ taskdev_unlock_and_stateunlock(dev, &dev->read_mutex);
+ return ret;
+}
+
+static ssize_t dsp_task_write_wd(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ struct taskdev *dev = taskdev[minor];
+ u16 wd;
+ int ret = 0;
+ DEFINE_WAIT(wait);
+
+ if (count == 0) {
+ return 0;
+ } else if (count & 0x1) {
+ printk(KERN_ERR
+ "omapdsp: odd count is illegal for DSP task device.\n");
+ return -EINVAL;
+ } else {
+ /* force! */
+ count = 2;
+ }
+
+ if (taskdev_lock_and_statelock_attached(dev, &dev->write_mutex))
+ return -ENODEV;
+
+ prepare_to_wait(&dev->write_wait_q, &wait, TASK_INTERRUPTIBLE);
+ if (dev->wsz == 0)
+ schedule();
+ finish_wait(&dev->write_wait_q, &wait);
+ if (dev->wsz == 0) {
+ /* failure */
+ if (signal_pending(current))
+ ret = -EINTR;
+ goto up_out;
+ }
+
+ if (copy_from_user(&wd, buf, count)) {
+ ret = -EFAULT;
+ goto up_out;
+ }
+
+ spin_lock(&dev->wsz_lock);
+ if (mbcompose_send(WDSND, dev->task->tid, wd) < 0) {
+ spin_unlock(&dev->wsz_lock);
+ goto up_out;
+ }
+ ret = count;
+ if (rcvtyp_acv(dev->task->ttyp))
+ dev->wsz = 0;
+ spin_unlock(&dev->wsz_lock);
+
+ up_out:
+ taskdev_unlock_and_stateunlock(dev, &dev->write_mutex);
+ return ret;
+}
+
+static ssize_t dsp_task_write_bk(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ struct taskdev *dev = taskdev[minor];
+ int ret = 0;
+ DEFINE_WAIT(wait);
+
+ if (count == 0) {
+ return 0;
+ } else if (count & 0x1) {
+ printk(KERN_ERR
+ "omapdsp: odd count is illegal for DSP task device.\n");
+ return -EINVAL;
+ } else if ((int)buf & 0x1) {
+ printk(KERN_ERR
+ "omapdsp: buf should be word aligned for "
+ "dsp_task_write().\n");
+ return -EINVAL;
+ }
+
+ if (taskdev_lock_and_statelock_attached(dev, &dev->write_mutex))
+ return -ENODEV;
+
+ prepare_to_wait(&dev->write_wait_q, &wait, TASK_INTERRUPTIBLE);
+ if (dev->wsz == 0)
+ schedule();
+ finish_wait(&dev->write_wait_q, &wait);
+ if (dev->wsz == 0) {
+ /* failure */
+ if (signal_pending(current))
+ ret = -EINTR;
+ goto up_out;
+ }
+
+ if (count > dev->wsz)
+ count = dev->wsz;
+
+ if (rcvtyp_pvt(dev->task->ttyp)) {
+ /* private */
+ struct ipbuf_p *ipbp = dev->task->ipbuf_pvt_w;
+ unsigned char *dst;
+
+ if (dsp_mem_enable(ipbp) < 0) {
+ ret = -EBUSY;
+ goto up_out;
+ }
+ dst = MKVIRT(ipbp->ah, ipbp->al);
+ if (dsp_address_validate(dst, count, "task %s write buffer",
+ dev->task->name) < 0) {
+ ret = -EINVAL;
+ goto pv_out1;
+ }
+ if (dsp_mem_enable(dst) < 0) {
+ ret = -EBUSY;
+ goto pv_out1;
+ }
+ if (copy_from_user_dsp(dst, buf, count)) {
+ ret = -EFAULT;
+ goto pv_out2;
+ }
+ ipbp->c = count/2;
+ ipbp->s = dev->task->tid;
+ spin_lock(&dev->wsz_lock);
+ if (mbcompose_send(BKSNDP, dev->task->tid, 0) == 0) {
+ if (rcvtyp_acv(dev->task->ttyp))
+ dev->wsz = 0;
+ ret = count;
+ }
+ spin_unlock(&dev->wsz_lock);
+ pv_out2:
+ dsp_mem_disable(dst);
+ pv_out1:
+ dsp_mem_disable(ipbp);
+ } else {
+ /* global */
+ struct ipbuf_head *ipb_h;
+
+ if (dsp_mem_enable_ipbuf() < 0) {
+ ret = -EBUSY;
+ goto up_out;
+ }
+ if ((ipb_h = get_free_ipbuf(dev->task->tid)) == NULL)
+ goto gb_out;
+ if (copy_from_user_dsp(ipb_h->p->d, buf, count)) {
+ release_ipbuf(ipb_h);
+ ret = -EFAULT;
+ goto gb_out;
+ }
+ ipb_h->p->c = count/2;
+ ipb_h->p->sa = dev->task->tid;
+ spin_lock(&dev->wsz_lock);
+ if (mbcompose_send(BKSND, dev->task->tid, ipb_h->bid) == 0) {
+ if (rcvtyp_acv(dev->task->ttyp))
+ dev->wsz = 0;
+ ret = count;
+ ipb_bsycnt_inc(&ipbcfg);
+ } else
+ release_ipbuf(ipb_h);
+ spin_unlock(&dev->wsz_lock);
+ gb_out:
+ dsp_mem_disable_ipbuf();
+ }
+
+ up_out:
+ taskdev_unlock_and_stateunlock(dev, &dev->write_mutex);
+ return ret;
+}
+
+static unsigned int dsp_task_poll(struct file * file, poll_table * wait)
+{
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ struct taskdev *dev = taskdev[minor];
+ struct dsptask *task = dev->task;
+ unsigned int mask = 0;
+
+ if (!devstate_read_lock_and_test(dev, TASKDEV_ST_ATTACHED))
+ return 0;
+ poll_wait(file, &dev->read_wait_q, wait);
+ poll_wait(file, &dev->write_wait_q, wait);
+ if (sndtyp_psv(task->ttyp) ||
+ (sndtyp_wd(task->ttyp) && kfifo_len(dev->rcvdt.fifo)) ||
+ (sndtyp_bk(task->ttyp) && !ipblink_empty(&dev->rcvdt.bk.link)))
+ mask |= POLLIN | POLLRDNORM;
+ if (dev->wsz)
+ mask |= POLLOUT | POLLWRNORM;
+ devstate_read_unlock(dev);
+
+ return mask;
+}
+
+static int dsp_tctl_issue(struct taskdev *dev, u16 cmd, int argc, u16 argv[])
+{
+ int tctl_argc;
+ struct mb_exarg mbarg, *mbargp;
+ int interactive;
+ u8 tid;
+ int ret = 0;
+
+ if (cmd < 0x8000) {
+ /*
+ * 0x0000 - 0x7fff
+ * system reserved TCTL commands
+ */
+ switch (cmd) {
+ case TCTL_TEN:
+ case TCTL_TDIS:
+ tctl_argc = 0;
+ interactive = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ /*
+ * 0x8000 - 0xffff
+ * user-defined TCTL commands
+ */
+ else if (cmd < 0x8100) {
+ /* 0x8000-0x80ff: no arg, non-interactive */
+ tctl_argc = 0;
+ interactive = 0;
+ } else if (cmd < 0x8200) {
+ /* 0x8100-0x81ff: 1 arg, non-interactive */
+ tctl_argc = 1;
+ interactive = 0;
+ } else if (cmd < 0x9000) {
+ /* 0x8200-0x8fff: reserved */
+ return -EINVAL;
+ } else if (cmd < 0x9100) {
+ /* 0x9000-0x90ff: no arg, interactive */
+ tctl_argc = 0;
+ interactive = 1;
+ } else if (cmd < 0x9200) {
+ /* 0x9100-0x91ff: 1 arg, interactive */
+ tctl_argc = 1;
+ interactive = 1;
+ } else {
+ /* 0x9200-0xffff: reserved */
+ return -EINVAL;
+ }
+
+ /*
+ * if argc < 0, use tctl_argc as is.
+ * if argc >= 0, check arg count.
+ */
+ if ((argc >= 0) && (argc != tctl_argc))
+ return -EINVAL;
+
+ /*
+ * issue TCTL
+ */
+ if (taskdev_lock_interruptible(dev, &dev->tctl_mutex))
+ return -EINTR;
+
+ tid = dev->task->tid;
+ if (tctl_argc > 0) {
+ mbarg.argc = tctl_argc;
+ mbarg.tid = tid;
+ mbarg.argv = argv;
+ mbargp = &mbarg;
+ } else
+ mbargp = NULL;
+
+ if (interactive) {
+ dev->tctl_stat = -EINVAL;
+
+ mbcompose_send_and_wait_exarg(TCTL, tid, cmd, mbargp,
+ &dev->tctl_wait_q);
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ goto up_out;
+ }
+ if ((ret = dev->tctl_stat) < 0) {
+ printk(KERN_ERR "omapdsp: TCTL not responding.\n");
+ goto up_out;
+ }
+ } else
+ mbcompose_send_exarg(TCTL, tid, cmd, mbargp);
+
+up_out:
+ mutex_unlock(&dev->tctl_mutex);
+ return ret;
+}
+
+static int dsp_task_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct taskdev *dev = taskdev[minor];
+ int ret;
+
+ if (cmd < 0x10000) {
+ /* issue TCTL */
+ u16 mbargv[1];
+
+ mbargv[0] = arg & 0xffff;
+ return dsp_tctl_issue(dev, cmd, -1, mbargv);
+ }
+
+ /* non TCTL ioctls */
+ switch (cmd) {
+
+ case TASK_IOCTL_LOCK:
+ ret = taskdev_lock(dev);
+ break;
+
+ case TASK_IOCTL_UNLOCK:
+ ret = taskdev_unlock(dev);
+ break;
+
+ case TASK_IOCTL_BFLSH:
+ if (taskdev_lock_and_statelock_attached(dev, &dev->read_mutex))
+ return -ENODEV;
+ ret = taskdev_flush_buf(dev);
+ taskdev_unlock_and_stateunlock(dev, &dev->read_mutex);
+ break;
+
+ case TASK_IOCTL_SETBSZ:
+ if (taskdev_lock_and_statelock_attached(dev, &dev->read_mutex))
+ return -ENODEV;
+ ret = taskdev_set_fifosz(dev, arg);
+ taskdev_unlock_and_stateunlock(dev, &dev->read_mutex);
+ break;
+
+ case TASK_IOCTL_GETNAME:
+ ret = 0;
+ if (copy_to_user((void __user *)arg, dev->name,
+ strlen(dev->name) + 1))
+ ret = -EFAULT;
+ break;
+
+ default:
+ ret = -ENOIOCTLCMD;
+
+ }
+
+ return ret;
+}
+
+static void dsp_task_mmap_open(struct vm_area_struct *vma)
+{
+ struct taskdev *dev = (struct taskdev *)vma->vm_private_data;
+ struct dsptask *task;
+ size_t len = vma->vm_end - vma->vm_start;
+
+ BUG_ON(!(dev->state & TASKDEV_ST_ATTACHED));
+ task = dev->task;
+ omap_mmu_exmap_use(&dsp_mmu, task->map_base, len);
+}
+
+static void dsp_task_mmap_close(struct vm_area_struct *vma)
+{
+ struct taskdev *dev = (struct taskdev *)vma->vm_private_data;
+ struct dsptask *task;
+ size_t len = vma->vm_end - vma->vm_start;
+
+ BUG_ON(!(dev->state & TASKDEV_ST_ATTACHED));
+ task = dev->task;
+ omap_mmu_exmap_unuse(&dsp_mmu, task->map_base, len);
+}
+
+/**
+ * On demand page allocation is not allowed. The mapping area is defined by
+ * corresponding DSP tasks.
+ */
+static int dsp_task_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ return VM_FAULT_NOPAGE;
+}
+
+static struct vm_operations_struct dsp_task_vm_ops = {
+ .open = dsp_task_mmap_open,
+ .close = dsp_task_mmap_close,
+ .fault = dsp_task_mmap_fault,
+};
+
+static int dsp_task_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ void *tmp_vadr;
+ unsigned long tmp_padr, tmp_vmadr, off;
+ size_t req_len, tmp_len;
+ unsigned int minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+ struct taskdev *dev = taskdev[minor];
+ struct dsptask *task;
+ int ret = 0;
+
+ if (!devstate_read_lock_and_test(dev, TASKDEV_ST_ATTACHED))
+ return -ENODEV;
+ task = dev->task;
+
+ /*
+ * Don't swap this area out
+ * Don't dump this area to a core file
+ */
+ vma->vm_flags |= VM_RESERVED | VM_IO;
+
+ /* Do not cache this area */
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ req_len = vma->vm_end - vma->vm_start;
+ off = vma->vm_pgoff << PAGE_SHIFT;
+ tmp_vmadr = vma->vm_start;
+ tmp_vadr = task->map_base + off;
+ do {
+ tmp_padr = omap_mmu_virt_to_phys(&dsp_mmu, tmp_vadr, &tmp_len);
+ if (tmp_padr == 0) {
+ printk(KERN_ERR
+ "omapdsp: task %s: illegal address "
+ "for mmap: %p", task->name, tmp_vadr);
+ /* partial mapping will be cleared in upper layer */
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+ if (tmp_len > req_len)
+ tmp_len = req_len;
+
+ pr_debug("omapdsp: mmap info: "
+ "vmadr = %08lx, padr = %08lx, len = %x\n",
+ tmp_vmadr, tmp_padr, tmp_len);
+ if (remap_pfn_range(vma, tmp_vmadr, tmp_padr >> PAGE_SHIFT,
+ tmp_len, vma->vm_page_prot) != 0) {
+ printk(KERN_ERR
+ "omapdsp: task %s: remap_page_range() failed.\n",
+ task->name);
+ /* partial mapping will be cleared in upper layer */
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+
+ req_len -= tmp_len;
+ tmp_vmadr += tmp_len;
+ tmp_vadr += tmp_len;
+ } while (req_len);
+
+ vma->vm_ops = &dsp_task_vm_ops;
+ vma->vm_private_data = dev;
+ omap_mmu_exmap_use(&dsp_mmu, task->map_base, vma->vm_end - vma->vm_start);
+
+unlock_out:
+ devstate_read_unlock(dev);
+ return ret;
+}
+
+static int dsp_task_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct taskdev *dev;
+ int ret = 0;
+
+ if ((minor >= TASKDEV_MAX) || ((dev = taskdev[minor]) == NULL))
+ return -ENODEV;
+
+ restart:
+ mutex_lock(&dev->usecount_lock);
+ down_write(&dev->state_sem);
+
+ /* state can be NOTASK, ATTACHED/FREEZED, KILLING, GARBAGE or INVALID here. */
+ switch (dev->state & TASKDEV_ST_STATE_MASK) {
+ case TASKDEV_ST_NOTASK:
+ break;
+ case TASKDEV_ST_ATTACHED:
+ goto attached;
+
+ case TASKDEV_ST_INVALID:
+ up_write(&dev->state_sem);
+ mutex_unlock(&dev->usecount_lock);
+ return -ENODEV;
+
+ case TASKDEV_ST_FREEZED:
+ case TASKDEV_ST_KILLING:
+ case TASKDEV_ST_GARBAGE:
+ case TASKDEV_ST_DELREQ:
+ /* on the kill process. wait until it becomes NOTASK. */
+ up_write(&dev->state_sem);
+ mutex_unlock(&dev->usecount_lock);
+ if (devstate_write_lock(dev, TASKDEV_ST_NOTASK) < 0)
+ return -EINTR;
+ devstate_write_unlock(dev);
+ goto restart;
+ }
+
+ /* NOTASK */
+ set_taskdev_state(dev, TASKDEV_ST_ADDREQ);
+ /* wake up twch daemon for tadd */
+ dsp_twch_touch();
+ up_write(&dev->state_sem);
+ if (devstate_write_lock(dev, TASKDEV_ST_ATTACHED |
+ TASKDEV_ST_ADDFAIL) < 0) {
+ /* cancelled */
+ if (!devstate_write_lock_and_test(dev, TASKDEV_ST_ADDREQ)) {
+ mutex_unlock(&dev->usecount_lock);
+ /* out of control ??? */
+ return -EINTR;
+ }
+ set_taskdev_state(dev, TASKDEV_ST_NOTASK);
+ ret = -EINTR;
+ goto change_out;
+ }
+ if (dev->state & TASKDEV_ST_ADDFAIL) {
+ printk(KERN_ERR "omapdsp: task attach failed for %s!\n",
+ dev->name);
+ ret = -EBUSY;
+ set_taskdev_state(dev, TASKDEV_ST_NOTASK);
+ goto change_out;
+ }
+
+ attached:
+ ret = proc_list_add(&dev->proc_list_lock,
+ &dev->proc_list, current, file);
+ if (ret)
+ goto out;
+
+ dev->usecount++;
+ file->f_op = &dev->fops;
+ up_write(&dev->state_sem);
+ mutex_unlock(&dev->usecount_lock);
+
+#ifdef DSP_PTE_FREE /* not used currently. */
+ dsp_map_update(current);
+ dsp_cur_users_add(current);
+#endif /* DSP_PTE_FREE */
+ return 0;
+
+ change_out:
+ wake_up_interruptible_all(&dev->state_wait_q);
+ out:
+ up_write(&dev->state_sem);
+ mutex_unlock(&dev->usecount_lock);
+ return ret;
+}
+
+static int dsp_task_release(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct taskdev *dev = taskdev[minor];
+
+#ifdef DSP_PTE_FREE /* not used currently. */
+ dsp_cur_users_del(current);
+#endif /* DSP_PTE_FREE */
+
+ if (has_taskdev_lock(dev))
+ taskdev_unlock(dev);
+
+ proc_list_del(&dev->proc_list_lock, &dev->proc_list, current, file);
+ mutex_lock(&dev->usecount_lock);
+ if (--dev->usecount > 0) {
+ /* other processes are using this device. no state change. */
+ mutex_unlock(&dev->usecount_lock);
+ return 0;
+ }
+
+ /* usecount == 0 */
+ down_write(&dev->state_sem);
+
+ /* state can be ATTACHED/FREEZED, KILLING or GARBAGE here. */
+ switch (dev->state & TASKDEV_ST_STATE_MASK) {
+
+ case TASKDEV_ST_KILLING:
+ break;
+
+ case TASKDEV_ST_GARBAGE:
+ set_taskdev_state(dev, TASKDEV_ST_NOTASK);
+ wake_up_interruptible_all(&dev->state_wait_q);
+ break;
+
+ case TASKDEV_ST_ATTACHED:
+ case TASKDEV_ST_FREEZED:
+ if (is_dynamic_task(minor)) {
+ set_taskdev_state(dev, TASKDEV_ST_DELREQ);
+ /* wake up twch daemon for tdel */
+ dsp_twch_touch();
+ }
+ break;
+
+ }
+
+ up_write(&dev->state_sem);
+ mutex_unlock(&dev->usecount_lock);
+ return 0;
+}
+
+/*
+ * mkdev / rmdev
+ */
+int dsp_mkdev(char *name)
+{
+ struct taskdev *dev;
+ int status;
+ unsigned char minor;
+ int ret;
+
+ if (dsp_cfgstat_get_stat() != CFGSTAT_READY) {
+ printk(KERN_ERR "omapdsp: dsp has not been configured.\n");
+ return -EINVAL;
+ }
+
+ if (mutex_lock_interruptible(&devmgr_lock))
+ return -EINTR;
+
+ /* naming check */
+ for (minor = 0; minor < TASKDEV_MAX; minor++) {
+ if (taskdev[minor] && !strcmp(taskdev[minor]->name, name)) {
+ printk(KERN_ERR
+ "omapdsp: task device name %s is already "
+ "in use.\n", name);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ /* find free minor number */
+ for (minor = n_task; minor < TASKDEV_MAX; minor++) {
+ if (taskdev[minor] == NULL)
+ goto do_make;
+ }
+ printk(KERN_ERR "omapdsp: Too many task devices.\n");
+ ret = -EBUSY;
+ goto out;
+
+do_make:
+ if ((dev = kzalloc(sizeof(struct taskdev), GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if ((status = taskdev_init(dev, name, minor)) < 0) {
+ kfree(dev);
+ ret = status;
+ goto out;
+ }
+ ret = minor;
+
+out:
+ mutex_unlock(&devmgr_lock);
+ return ret;
+}
+
+int dsp_rmdev(char *name)
+{
+ unsigned char minor;
+ int status;
+ int ret;
+
+ if (dsp_cfgstat_get_stat() != CFGSTAT_READY) {
+ printk(KERN_ERR "omapdsp: dsp has not been configured.\n");
+ return -EINVAL;
+ }
+
+ if (mutex_lock_interruptible(&devmgr_lock))
+ return -EINTR;
+
+ /* find in dynamic devices */
+ for (minor = n_task; minor < TASKDEV_MAX; minor++) {
+ if (taskdev[minor] && !strcmp(taskdev[minor]->name, name))
+ goto do_remove;
+ }
+
+ /* find in static devices */
+ for (minor = 0; minor < n_task; minor++) {
+ if (taskdev[minor] && !strcmp(taskdev[minor]->name, name)) {
+ printk(KERN_ERR
+ "omapdsp: task device %s is static.\n", name);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ printk(KERN_ERR "omapdsp: task device %s not found.\n", name);
+ return -EINVAL;
+
+do_remove:
+ ret = minor;
+ if ((status = dsp_rmdev_minor(minor)) < 0)
+ ret = status;
+out:
+ mutex_unlock(&devmgr_lock);
+ return ret;
+}
+
+static int dsp_rmdev_minor(unsigned char minor)
+{
+ struct taskdev *dev = taskdev[minor];
+
+ while (!down_write_trylock(&dev->state_sem)) {
+ down_read(&dev->state_sem);
+ if (dev->state & (TASKDEV_ST_ATTACHED |
+ TASKDEV_ST_FREEZED)) {
+ /*
+ * task is working. kill it.
+ * ATTACHED -> FREEZED can be changed under
+ * down_read of state_sem..
+ */
+ set_taskdev_state(dev, TASKDEV_ST_FREEZED);
+ wake_up_interruptible_all(&dev->read_wait_q);
+ wake_up_interruptible_all(&dev->write_wait_q);
+ wake_up_interruptible_all(&dev->tctl_wait_q);
+ }
+ up_read(&dev->state_sem);
+ schedule();
+ }
+
+ switch (dev->state & TASKDEV_ST_STATE_MASK) {
+
+ case TASKDEV_ST_NOTASK:
+ case TASKDEV_ST_INVALID:
+ /* fine */
+ goto notask;
+
+ case TASKDEV_ST_ATTACHED:
+ case TASKDEV_ST_FREEZED:
+ /* task is working. kill it. */
+ set_taskdev_state(dev, TASKDEV_ST_KILLING);
+ up_write(&dev->state_sem);
+ dsp_tdel_bh(dev, TDEL_KILL);
+ goto invalidate;
+
+ case TASKDEV_ST_ADDREQ:
+ /* open() is waiting. drain it. */
+ set_taskdev_state(dev, TASKDEV_ST_ADDFAIL);
+ wake_up_interruptible_all(&dev->state_wait_q);
+ break;
+
+ case TASKDEV_ST_DELREQ:
+ /* nobody is waiting. */
+ set_taskdev_state(dev, TASKDEV_ST_NOTASK);
+ wake_up_interruptible_all(&dev->state_wait_q);
+ break;
+
+ case TASKDEV_ST_ADDING:
+ case TASKDEV_ST_DELING:
+ case TASKDEV_ST_KILLING:
+ case TASKDEV_ST_GARBAGE:
+ case TASKDEV_ST_ADDFAIL:
+ /* transient state. wait for a moment. */
+ break;
+
+ }
+
+ up_write(&dev->state_sem);
+
+invalidate:
+ /* wait for some time and hope the state is settled */
+ devstate_read_lock_timeout(dev, TASKDEV_ST_NOTASK, 5 * HZ);
+ if (!(dev->state & TASKDEV_ST_NOTASK)) {
+ printk(KERN_WARNING
+ "omapdsp: illegal device state (%s) on rmdev %s.\n",
+ devstate_name(dev->state), dev->name);
+ }
+notask:
+ set_taskdev_state(dev, TASKDEV_ST_INVALID);
+ devstate_read_unlock(dev);
+
+ taskdev_delete(minor);
+ kfree(dev);
+
+ return 0;
+}
+
+static struct file_operations dsp_task_fops = {
+ .owner = THIS_MODULE,
+ .poll = dsp_task_poll,
+ .ioctl = dsp_task_ioctl,
+ .open = dsp_task_open,
+ .release = dsp_task_release,
+};
+
+static void dsptask_dev_release(struct device *dev)
+{
+}
+
+static int taskdev_init(struct taskdev *dev, char *name, unsigned char minor)
+{
+ int ret;
+ struct device *task_dev;
+
+ taskdev[minor] = dev;
+
+ spin_lock_init(&dev->proc_list_lock);
+ INIT_LIST_HEAD(&dev->proc_list);
+ init_waitqueue_head(&dev->read_wait_q);
+ init_waitqueue_head(&dev->write_wait_q);
+ init_waitqueue_head(&dev->tctl_wait_q);
+ mutex_init(&dev->read_mutex);
+ mutex_init(&dev->write_mutex);
+ mutex_init(&dev->tctl_mutex);
+ mutex_init(&dev->lock);
+ spin_lock_init(&dev->wsz_lock);
+ dev->tctl_ret = -EINVAL;
+ dev->lock_pid = 0;
+
+ strncpy(dev->name, name, TNM_LEN);
+ dev->name[TNM_LEN-1] = '\0';
+ set_taskdev_state(dev, (minor < n_task) ? TASKDEV_ST_ATTACHED : TASKDEV_ST_NOTASK);
+ dev->usecount = 0;
+ mutex_init(&dev->usecount_lock);
+ memcpy(&dev->fops, &dsp_task_fops, sizeof(struct file_operations));
+
+ dev->dev.parent = omap_dsp->dev;
+ dev->dev.bus = &dsptask_bus;
+ sprintf(dev->dev.bus_id, "dsptask%d", minor);
+ dev->dev.release = dsptask_dev_release;
+ ret = device_register(&dev->dev);
+ if (ret) {
+ printk(KERN_ERR "device_register failed: %d\n", ret);
+ return ret;
+ }
+ ret = device_create_file(&dev->dev, &dev_attr_devname);
+ if (ret)
+ goto fail_create_devname;
+ ret = device_create_file(&dev->dev, &dev_attr_devstate);
+ if (ret)
+ goto fail_create_devstate;
+ ret = device_create_file(&dev->dev, &dev_attr_proc_list);
+ if (ret)
+ goto fail_create_proclist;
+
+ task_dev = device_create(dsp_task_class, NULL,
+ MKDEV(OMAP_DSP_TASK_MAJOR, minor), NULL,
+ "dsptask%d", (int)minor);
+
+ if (unlikely(IS_ERR(task_dev))) {
+ ret = -EINVAL;
+ goto fail_create_taskclass;
+ }
+
+ init_waitqueue_head(&dev->state_wait_q);
+ init_rwsem(&dev->state_sem);
+
+ return 0;
+
+ fail_create_taskclass:
+ device_remove_file(&dev->dev, &dev_attr_proc_list);
+ fail_create_proclist:
+ device_remove_file(&dev->dev, &dev_attr_devstate);
+ fail_create_devstate:
+ device_remove_file(&dev->dev, &dev_attr_devname);
+ fail_create_devname:
+ device_unregister(&dev->dev);
+ return ret;
+}
+
+static void taskdev_delete(unsigned char minor)
+{
+ struct taskdev *dev = taskdev[minor];
+
+ if (!dev)
+ return;
+ device_remove_file(&dev->dev, &dev_attr_devname);
+ device_remove_file(&dev->dev, &dev_attr_devstate);
+ device_remove_file(&dev->dev, &dev_attr_proc_list);
+ device_destroy(dsp_task_class, MKDEV(OMAP_DSP_TASK_MAJOR, minor));
+ device_unregister(&dev->dev);
+ proc_list_flush(&dev->proc_list_lock, &dev->proc_list);
+ taskdev[minor] = NULL;
+}
+
+static int taskdev_attach_task(struct taskdev *dev, struct dsptask *task)
+{
+ u16 ttyp = task->ttyp;
+ int ret;
+
+ dev->fops.read =
+ sndtyp_acv(ttyp) ?
+ sndtyp_wd(ttyp) ? dsp_task_read_wd_acv:
+ /* sndtyp_bk */ dsp_task_read_bk_acv:
+ /* sndtyp_psv */
+ sndtyp_wd(ttyp) ? dsp_task_read_wd_psv:
+ /* sndtyp_bk */ dsp_task_read_bk_psv;
+ if (sndtyp_wd(ttyp)) {
+ /* word */
+ size_t fifosz = sndtyp_psv(ttyp) ? 2:32; /* passive:active */
+
+ dev->rcvdt.fifo = kfifo_alloc(fifosz, GFP_KERNEL,
+ &dev->read_lock);
+ if (IS_ERR(dev->rcvdt.fifo)) {
+ printk(KERN_ERR
+ "omapdsp: unable to allocate receive buffer. "
+ "(%d bytes for %s)\n", fifosz, dev->name);
+ return -ENOMEM;
+ }
+ } else {
+ /* block */
+ INIT_IPBLINK(&dev->rcvdt.bk.link);
+ dev->rcvdt.bk.rp = 0;
+ }
+
+ dev->fops.write =
+ rcvtyp_wd(ttyp) ? dsp_task_write_wd:
+ /* rcvbyp_bk */ dsp_task_write_bk;
+ dev->wsz = rcvtyp_acv(ttyp) ? 0 : /* active */
+ rcvtyp_wd(ttyp) ? 2 : /* passive word */
+ ipbcfg.lsz*2; /* passive block */
+
+ if (task->map_length)
+ dev->fops.mmap = dsp_task_mmap;
+
+ ret = device_create_file(&dev->dev, &dev_attr_taskname);
+ if (unlikely(ret))
+ goto fail_create_taskname;
+ ret = device_create_file(&dev->dev, &dev_attr_ttyp);
+ if (unlikely(ret))
+ goto fail_create_ttyp;
+ ret = device_create_file(&dev->dev, &dev_attr_wsz);
+ if (unlikely(ret))
+ goto fail_create_wsz;
+ if (task->map_length) {
+ ret = device_create_file(&dev->dev, &dev_attr_mmap);
+ if (unlikely(ret))
+ goto fail_create_mmap;
+ }
+ if (sndtyp_wd(ttyp)) {
+ ret = device_create_file(&dev->dev, &dev_attr_fifosz);
+ if (unlikely(ret))
+ goto fail_create_fifosz;
+ ret = device_create_file(&dev->dev, &dev_attr_fifocnt);
+ if (unlikely(ret))
+ goto fail_create_fifocnt;
+ } else {
+ ret = device_create_file(&dev->dev, &dev_attr_ipblink);
+ if (unlikely(ret))
+ goto fail_create_ipblink;
+ }
+
+ dev->task = task;
+ task->dev = dev;
+
+ return 0;
+
+ fail_create_fifocnt:
+ device_remove_file(&dev->dev, &dev_attr_fifosz);
+ fail_create_ipblink:
+ fail_create_fifosz:
+ if (task->map_length)
+ device_remove_file(&dev->dev, &dev_attr_mmap);
+ fail_create_mmap:
+ device_remove_file(&dev->dev, &dev_attr_wsz);
+ fail_create_wsz:
+ device_remove_file(&dev->dev, &dev_attr_ttyp);
+ fail_create_ttyp:
+ device_remove_file(&dev->dev, &dev_attr_taskname);
+ fail_create_taskname:
+ if (task->map_length)
+ dev->fops.mmap = NULL;
+
+ dev->fops.write = NULL;
+ dev->wsz = 0;
+
+ dev->fops.read = NULL;
+ taskdev_flush_buf(dev);
+
+ if (sndtyp_wd(ttyp))
+ kfifo_free(dev->rcvdt.fifo);
+
+ dev->task = NULL;
+
+ return ret;
+}
+
+static void taskdev_detach_task(struct taskdev *dev)
+{
+ u16 ttyp = dev->task->ttyp;
+
+ device_remove_file(&dev->dev, &dev_attr_taskname);
+ device_remove_file(&dev->dev, &dev_attr_ttyp);
+ if (sndtyp_wd(ttyp)) {
+ device_remove_file(&dev->dev, &dev_attr_fifosz);
+ device_remove_file(&dev->dev, &dev_attr_fifocnt);
+ } else
+ device_remove_file(&dev->dev, &dev_attr_ipblink);
+ device_remove_file(&dev->dev, &dev_attr_wsz);
+ if (dev->task->map_length) {
+ device_remove_file(&dev->dev, &dev_attr_mmap);
+ dev->fops.mmap = NULL;
+ }
+
+ dev->fops.read = NULL;
+ taskdev_flush_buf(dev);
+ if (sndtyp_wd(ttyp))
+ kfifo_free(dev->rcvdt.fifo);
+
+ dev->fops.write = NULL;
+ dev->wsz = 0;
+
+ pr_info("omapdsp: taskdev %s disabled.\n", dev->name);
+ dev->task = NULL;
+}
+
+/*
+ * tadd / tdel / tkill
+ */
+static int dsp_tadd(struct taskdev *dev, dsp_long_t adr)
+{
+ struct dsptask *task;
+ struct mb_exarg arg;
+ u8 tid, tid_response;
+ u16 argv[2];
+ int ret = 0;
+
+ if (!devstate_write_lock_and_test(dev, TASKDEV_ST_ADDREQ)) {
+ printk(KERN_ERR
+ "omapdsp: taskdev %s is not requesting for tadd. "
+ "(state is %s)\n", dev->name, devstate_name(dev->state));
+ return -EINVAL;
+ }
+ set_taskdev_state(dev, TASKDEV_ST_ADDING);
+ devstate_write_unlock(dev);
+
+ if (adr == TADD_ABORTADR) {
+ /* aborting tadd intentionally */
+ pr_info("omapdsp: tadd address is ABORTADR.\n");
+ goto fail_out;
+ }
+ if (adr >= DSPSPACE_SIZE) {
+ printk(KERN_ERR
+ "omapdsp: illegal address 0x%08x for tadd\n", adr);
+ ret = -EINVAL;
+ goto fail_out;
+ }
+
+ adr >>= 1; /* word address */
+ argv[0] = adr >> 16; /* addrh */
+ argv[1] = adr & 0xffff; /* addrl */
+
+ if (mutex_lock_interruptible(&cfg_lock)) {
+ ret = -EINTR;
+ goto fail_out;
+ }
+ cfg_tid = TID_ANON;
+ cfg_cmd = MBOX_CMD_DSP_TADD;
+ arg.tid = TID_ANON;
+ arg.argc = 2;
+ arg.argv = argv;
+
+ if (dsp_mem_sync_inc() < 0) {
+ printk(KERN_ERR "omapdsp: memory sync failed!\n");
+ ret = -EBUSY;
+ goto fail_out;
+ }
+ mbcompose_send_and_wait_exarg(TADD, 0, 0, &arg, &cfg_wait_q);
+
+ tid = cfg_tid;
+ cfg_tid = TID_ANON;
+ cfg_cmd = 0;
+ mutex_unlock(&cfg_lock);
+
+ if (tid == TID_ANON) {
+ printk(KERN_ERR "omapdsp: tadd failed!\n");
+ ret = -EINVAL;
+ goto fail_out;
+ }
+ if ((tid < n_task) || dsptask[tid]) {
+ printk(KERN_ERR "omapdsp: illegal tid (%d)!\n", tid);
+ ret = -EINVAL;
+ goto fail_out;
+ }
+ if ((task = kzalloc(sizeof(struct dsptask), GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ goto del_out;
+ }
+
+ if ((ret = dsp_task_config(task, tid)) < 0)
+ goto free_out;
+
+ if (strcmp(dev->name, task->name)) {
+ printk(KERN_ERR
+ "omapdsp: task name (%s) doesn't match with "
+ "device name (%s).\n", task->name, dev->name);
+ ret = -EINVAL;
+ goto free_out;
+ }
+
+ if ((ret = taskdev_attach_task(dev, task)) < 0)
+ goto free_out;
+
+ dsp_task_init(task);
+ pr_info("omapdsp: taskdev %s enabled.\n", dev->name);
+ set_taskdev_state(dev, TASKDEV_ST_ATTACHED);
+ wake_up_interruptible_all(&dev->state_wait_q);
+ return 0;
+
+free_out:
+ kfree(task);
+
+del_out:
+ printk(KERN_ERR "omapdsp: deleting the task...\n");
+
+ set_taskdev_state(dev, TASKDEV_ST_DELING);
+
+ if (mutex_lock_interruptible(&cfg_lock)) {
+ printk(KERN_ERR "omapdsp: aborting tdel process. "
+ "DSP side could be corrupted.\n");
+ goto fail_out;
+ }
+ cfg_tid = TID_ANON;
+ cfg_cmd = MBOX_CMD_DSP_TDEL;
+ mbcompose_send_and_wait(TDEL, tid, TDEL_KILL, &cfg_wait_q);
+ tid_response = cfg_tid;
+ cfg_tid = TID_ANON;
+ cfg_cmd = 0;
+ mutex_unlock(&cfg_lock);
+
+ if (tid_response != tid)
+ printk(KERN_ERR "omapdsp: tdel failed. "
+ "DSP side could be corrupted.\n");
+
+fail_out:
+ set_taskdev_state(dev, TASKDEV_ST_ADDFAIL);
+ wake_up_interruptible_all(&dev->state_wait_q);
+ return ret;
+}
+
+int dsp_tadd_minor(unsigned char minor, dsp_long_t adr)
+{
+ struct taskdev *dev;
+ int status;
+ int ret;
+
+ if (mutex_lock_interruptible(&devmgr_lock))
+ return -EINTR;
+
+ if ((minor >= TASKDEV_MAX) || ((dev = taskdev[minor]) == NULL)) {
+ printk(KERN_ERR
+ "omapdsp: no task device with minor %d\n", minor);
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = minor;
+ if ((status = dsp_tadd(dev, adr)) < 0)
+ ret = status;
+
+out:
+ mutex_unlock(&devmgr_lock);
+ return ret;
+}
+
+static int dsp_tdel(struct taskdev *dev)
+{
+ if (!devstate_write_lock_and_test(dev, TASKDEV_ST_DELREQ)) {
+ printk(KERN_ERR
+ "omapdsp: taskdev %s is not requesting for tdel. "
+ "(state is %s)\n", dev->name, devstate_name(dev->state));
+ return -EINVAL;
+ }
+ set_taskdev_state(dev, TASKDEV_ST_DELING);
+ devstate_write_unlock(dev);
+
+ return dsp_tdel_bh(dev, TDEL_SAFE);
+}
+
+int dsp_tdel_minor(unsigned char minor)
+{
+ struct taskdev *dev;
+ int status;
+ int ret;
+
+ if (mutex_lock_interruptible(&devmgr_lock))
+ return -EINTR;
+
+ if ((minor >= TASKDEV_MAX) || ((dev = taskdev[minor]) == NULL)) {
+ printk(KERN_ERR
+ "omapdsp: no task device with minor %d\n", minor);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = minor;
+ if ((status = dsp_tdel(dev)) < 0)
+ ret = status;
+
+out:
+ mutex_unlock(&devmgr_lock);
+ return ret;
+}
+
+static int dsp_tkill(struct taskdev *dev)
+{
+ while (!down_write_trylock(&dev->state_sem)) {
+ if (!devstate_read_lock_and_test(dev, (TASKDEV_ST_ATTACHED |
+ TASKDEV_ST_FREEZED))) {
+ printk(KERN_ERR
+ "omapdsp: task has not been attached for "
+ "taskdev %s\n", dev->name);
+ return -EINVAL;
+ }
+ /* ATTACHED -> FREEZED can be changed under read semaphore. */
+ set_taskdev_state(dev, TASKDEV_ST_FREEZED);
+ wake_up_interruptible_all(&dev->read_wait_q);
+ wake_up_interruptible_all(&dev->write_wait_q);
+ wake_up_interruptible_all(&dev->tctl_wait_q);
+ devstate_read_unlock(dev);
+ schedule();
+ }
+
+ if (!(dev->state & (TASKDEV_ST_ATTACHED |
+ TASKDEV_ST_FREEZED))) {
+ printk(KERN_ERR
+ "omapdsp: task has not been attached for taskdev %s\n",
+ dev->name);
+ devstate_write_unlock(dev);
+ return -EINVAL;
+ }
+ if (!is_dynamic_task(dev->task->tid)) {
+ printk(KERN_ERR "omapdsp: task %s is not a dynamic task.\n",
+ dev->name);
+ devstate_write_unlock(dev);
+ return -EINVAL;
+ }
+ set_taskdev_state(dev, TASKDEV_ST_KILLING);
+ devstate_write_unlock(dev);
+
+ return dsp_tdel_bh(dev, TDEL_KILL);
+}
+
+int dsp_tkill_minor(unsigned char minor)
+{
+ struct taskdev *dev;
+ int status;
+ int ret;
+
+ if (mutex_lock_interruptible(&devmgr_lock))
+ return -EINTR;
+
+ if ((minor >= TASKDEV_MAX) || ((dev = taskdev[minor]) == NULL)) {
+ printk(KERN_ERR
+ "omapdsp: no task device with minor %d\n", minor);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = minor;
+ if ((status = dsp_tkill(dev)) < 0)
+ ret = status;
+
+out:
+ mutex_unlock(&devmgr_lock);
+ return ret;
+}
+
+static int dsp_tdel_bh(struct taskdev *dev, u16 type)
+{
+ struct dsptask *task;
+ u8 tid, tid_response;
+ int ret = 0;
+
+ task = dev->task;
+ tid = task->tid;
+ if (mutex_lock_interruptible(&cfg_lock)) {
+ if (type == TDEL_SAFE) {
+ set_taskdev_state(dev, TASKDEV_ST_DELREQ);
+ return -EINTR;
+ } else {
+ tid_response = TID_ANON;
+ ret = -EINTR;
+ goto detach_out;
+ }
+ }
+ cfg_tid = TID_ANON;
+ cfg_cmd = MBOX_CMD_DSP_TDEL;
+ mbcompose_send_and_wait(TDEL, tid, type, &cfg_wait_q);
+ tid_response = cfg_tid;
+ cfg_tid = TID_ANON;
+ cfg_cmd = 0;
+ mutex_unlock(&cfg_lock);
+
+detach_out:
+ taskdev_detach_task(dev);
+ dsp_task_unconfig(task);
+ kfree(task);
+
+ if (tid_response != tid) {
+ printk(KERN_ERR "omapdsp: %s failed!\n",
+ (type == TDEL_SAFE) ? "tdel" : "tkill");
+ ret = -EINVAL;
+ }
+ down_write(&dev->state_sem);
+ set_taskdev_state(dev, (dev->usecount > 0) ? TASKDEV_ST_GARBAGE :
+ TASKDEV_ST_NOTASK);
+ wake_up_interruptible_all(&dev->state_wait_q);
+ up_write(&dev->state_sem);
+
+ return ret;
+}
+
+/*
+ * state inquiry
+ */
+long taskdev_state_stale(unsigned char minor)
+{
+ if (taskdev[minor]) {
+ long state = taskdev[minor]->state;
+ taskdev[minor]->state |= TASKDEV_ST_STALE;
+ return state;
+ } else
+ return TASKDEV_ST_NOTASK;
+}
+
+/*
+ * functions called from mailbox interrupt routine
+ */
+void mbox_wdsnd(struct mbcmd *mb)
+{
+ unsigned int n;
+ u8 tid = mb->cmd_l;
+ u16 data = mb->data;
+ struct dsptask *task = dsptask[tid];
+
+ if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+ printk(KERN_ERR "mbox: WDSND with illegal tid! %d\n", tid);
+ return;
+ }
+ if (sndtyp_bk(task->ttyp)) {
+ printk(KERN_ERR
+ "mbox: WDSND from block sending task! (task%d)\n", tid);
+ return;
+ }
+ if (sndtyp_psv(task->ttyp) &&
+ !waitqueue_active(&task->dev->read_wait_q)) {
+ printk(KERN_WARNING
+ "mbox: WDSND from passive sending task (task%d) "
+ "without request!\n", tid);
+ return;
+ }
+
+ n = kfifo_put(task->dev->rcvdt.fifo, (unsigned char *)&data,
+ sizeof(data));
+ if (n != sizeof(data))
+ printk(KERN_WARNING "Receive FIFO(%d) is full\n", tid);
+
+ wake_up_interruptible(&task->dev->read_wait_q);
+}
+
+void mbox_wdreq(struct mbcmd *mb)
+{
+ u8 tid = mb->cmd_l;
+ struct dsptask *task = dsptask[tid];
+ struct taskdev *dev;
+
+ if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+ printk(KERN_ERR "mbox: WDREQ with illegal tid! %d\n", tid);
+ return;
+ }
+ if (rcvtyp_psv(task->ttyp)) {
+ printk(KERN_ERR
+ "mbox: WDREQ from passive receiving task! (task%d)\n",
+ tid);
+ return;
+ }
+
+ dev = task->dev;
+ spin_lock(&dev->wsz_lock);
+ dev->wsz = 2;
+ spin_unlock(&dev->wsz_lock);
+ wake_up_interruptible(&dev->write_wait_q);
+}
+
+void mbox_bksnd(struct mbcmd *mb)
+{
+ u8 tid = mb->cmd_l;
+ u16 bid = mb->data;
+ struct dsptask *task = dsptask[tid];
+ struct ipbuf_head *ipb_h;
+ u16 cnt;
+
+ if (bid >= ipbcfg.ln) {
+ printk(KERN_ERR "mbox: BKSND with illegal bid! %d\n", bid);
+ return;
+ }
+ ipb_h = bid_to_ipbuf(bid);
+ ipb_bsycnt_dec(&ipbcfg);
+ if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+ printk(KERN_ERR "mbox: BKSND with illegal tid! %d\n", tid);
+ goto unuse_ipbuf_out;
+ }
+ if (sndtyp_wd(task->ttyp)) {
+ printk(KERN_ERR
+ "mbox: BKSND from word sending task! (task%d)\n", tid);
+ goto unuse_ipbuf_out;
+ }
+ if (sndtyp_pvt(task->ttyp)) {
+ printk(KERN_ERR
+ "mbox: BKSND from private sending task! (task%d)\n", tid);
+ goto unuse_ipbuf_out;
+ }
+ if (sync_with_dsp(&ipb_h->p->sd, tid, 10) < 0) {
+ printk(KERN_ERR "mbox: BKSND - IPBUF sync failed!\n");
+ return;
+ }
+
+ /* should be done in DSP, but just in case. */
+ ipb_h->p->next = BID_NULL;
+
+ cnt = ipb_h->p->c;
+ if (cnt > ipbcfg.lsz) {
+ printk(KERN_ERR "mbox: BKSND cnt(%d) > ipbuf line size(%d)!\n",
+ cnt, ipbcfg.lsz);
+ goto unuse_ipbuf_out;
+ }
+
+ if (cnt == 0) {
+ /* 0-byte send from DSP */
+ unuse_ipbuf_nowait(ipb_h);
+ goto done;
+ }
+ ipblink_add_tail(&task->dev->rcvdt.bk.link, bid);
+ /* we keep coming bid and return alternative line to DSP. */
+ balance_ipbuf();
+
+done:
+ wake_up_interruptible(&task->dev->read_wait_q);
+ return;
+
+unuse_ipbuf_out:
+ unuse_ipbuf_nowait(ipb_h);
+ return;
+}
+
+void mbox_bkreq(struct mbcmd *mb)
+{
+ u8 tid = mb->cmd_l;
+ u16 cnt = mb->data;
+ struct dsptask *task = dsptask[tid];
+ struct taskdev *dev;
+
+ if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+ printk(KERN_ERR "mbox: BKREQ with illegal tid! %d\n", tid);
+ return;
+ }
+ if (rcvtyp_wd(task->ttyp)) {
+ printk(KERN_ERR
+ "mbox: BKREQ from word receiving task! (task%d)\n", tid);
+ return;
+ }
+ if (rcvtyp_pvt(task->ttyp)) {
+ printk(KERN_ERR
+ "mbox: BKREQ from private receiving task! (task%d)\n",
+ tid);
+ return;
+ }
+ if (rcvtyp_psv(task->ttyp)) {
+ printk(KERN_ERR
+ "mbox: BKREQ from passive receiving task! (task%d)\n",
+ tid);
+ return;
+ }
+
+ dev = task->dev;
+ spin_lock(&dev->wsz_lock);
+ dev->wsz = cnt*2;
+ spin_unlock(&dev->wsz_lock);
+ wake_up_interruptible(&dev->write_wait_q);
+}
+
+void mbox_bkyld(struct mbcmd *mb)
+{
+ u16 bid = mb->data;
+ struct ipbuf_head *ipb_h;
+
+ if (bid >= ipbcfg.ln) {
+ printk(KERN_ERR "mbox: BKYLD with illegal bid! %d\n", bid);
+ return;
+ }
+ ipb_h = bid_to_ipbuf(bid);
+
+ /* should be done in DSP, but just in case. */
+ ipb_h->p->next = BID_NULL;
+
+ /* we don't need to sync with DSP */
+ ipb_bsycnt_dec(&ipbcfg);
+ release_ipbuf(ipb_h);
+}
+
+void mbox_bksndp(struct mbcmd *mb)
+{
+ u8 tid = mb->cmd_l;
+ struct dsptask *task = dsptask[tid];
+ struct ipbuf_p *ipbp;
+
+ if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+ printk(KERN_ERR "mbox: BKSNDP with illegal tid! %d\n", tid);
+ return;
+ }
+ if (sndtyp_wd(task->ttyp)) {
+ printk(KERN_ERR
+ "mbox: BKSNDP from word sending task! (task%d)\n", tid);
+ return;
+ }
+ if (sndtyp_gbl(task->ttyp)) {
+ printk(KERN_ERR
+ "mbox: BKSNDP from non-private sending task! (task%d)\n",
+ tid);
+ return;
+ }
+
+ /*
+ * we should not have delayed block at this point
+ * because read() routine releases the lock of the buffer and
+ * until then DSP can't send next data.
+ */
+
+ ipbp = task->ipbuf_pvt_r;
+ if (sync_with_dsp(&ipbp->s, tid, 10) < 0) {
+ printk(KERN_ERR "mbox: BKSNDP - IPBUF sync failed!\n");
+ return;
+ }
+ pr_debug("mbox: ipbuf_pvt_r->a = 0x%08lx\n",
+ MKLONG(ipbp->ah, ipbp->al));
+ ipblink_add_pvt(&task->dev->rcvdt.bk.link);
+ wake_up_interruptible(&task->dev->read_wait_q);
+}
+
+void mbox_bkreqp(struct mbcmd *mb)
+{
+ u8 tid = mb->cmd_l;
+ struct dsptask *task = dsptask[tid];
+ struct taskdev *dev;
+ struct ipbuf_p *ipbp;
+
+ if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+ printk(KERN_ERR "mbox: BKREQP with illegal tid! %d\n", tid);
+ return;
+ }
+ if (rcvtyp_wd(task->ttyp)) {
+ printk(KERN_ERR
+ "mbox: BKREQP from word receiving task! (task%d)\n", tid);
+ return;
+ }
+ if (rcvtyp_gbl(task->ttyp)) {
+ printk(KERN_ERR
+ "mbox: BKREQP from non-private receiving task! (task%d)\n", tid);
+ return;
+ }
+ if (rcvtyp_psv(task->ttyp)) {
+ printk(KERN_ERR
+ "mbox: BKREQP from passive receiving task! (task%d)\n", tid);
+ return;
+ }
+
+ ipbp = task->ipbuf_pvt_w;
+ if (sync_with_dsp(&ipbp->s, TID_FREE, 10) < 0) {
+ printk(KERN_ERR "mbox: BKREQP - IPBUF sync failed!\n");
+ return;
+ }
+ pr_debug("mbox: ipbuf_pvt_w->a = 0x%08lx\n",
+ MKLONG(ipbp->ah, ipbp->al));
+ dev = task->dev;
+ spin_lock(&dev->wsz_lock);
+ dev->wsz = ipbp->c*2;
+ spin_unlock(&dev->wsz_lock);
+ wake_up_interruptible(&dev->write_wait_q);
+}
+
+void mbox_tctl(struct mbcmd *mb)
+{
+ u8 tid = mb->cmd_l;
+ struct dsptask *task = dsptask[tid];
+
+ if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+ printk(KERN_ERR "mbox: TCTL with illegal tid! %d\n", tid);
+ return;
+ }
+
+ if (!waitqueue_active(&task->dev->tctl_wait_q)) {
+ printk(KERN_WARNING "mbox: unexpected TCTL from DSP!\n");
+ return;
+ }
+
+ task->dev->tctl_stat = mb->data;
+ wake_up_interruptible(&task->dev->tctl_wait_q);
+}
+
+void mbox_tcfg(struct mbcmd *mb)
+{
+ u8 tid = mb->cmd_l;
+ struct dsptask *task = dsptask[tid];
+ u16 *tnm;
+ volatile u16 *buf;
+ int i;
+
+ if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+ printk(KERN_ERR "mbox: TCFG with illegal tid! %d\n", tid);
+ return;
+ }
+ if ((task->state != TASK_ST_CFGREQ) || (cfg_cmd != MBOX_CMD_DSP_TCFG)) {
+ printk(KERN_WARNING "mbox: unexpected TCFG from DSP!\n");
+ return;
+ }
+
+ if (dsp_mem_enable(ipbuf_sys_da) < 0) {
+ printk(KERN_ERR "mbox: TCFG - ipbuf_sys_da read failed!\n");
+ dsp_mem_disable(ipbuf_sys_da);
+ goto out;
+ }
+ if (sync_with_dsp(&ipbuf_sys_da->s, tid, 10) < 0) {
+ printk(KERN_ERR "mbox: TCFG - IPBUF sync failed!\n");
+ dsp_mem_disable(ipbuf_sys_da);
+ goto out;
+ }
+
+ /*
+ * read configuration data on system IPBUF
+ */
+ buf = ipbuf_sys_da->d;
+ task->ttyp = buf[0];
+ task->ipbuf_pvt_r = MKVIRT(buf[1], buf[2]);
+ task->ipbuf_pvt_w = MKVIRT(buf[3], buf[4]);
+ task->map_base = MKVIRT(buf[5], buf[6]);
+ task->map_length = MKLONG(buf[7], buf[8]) << 1; /* word -> byte */
+ tnm = MKVIRT(buf[9], buf[10]);
+ release_ipbuf_pvt(ipbuf_sys_da);
+ dsp_mem_disable(ipbuf_sys_da);
+
+ /*
+ * copy task name string
+ */
+ if (dsp_address_validate(tnm, TNM_LEN, "task name buffer") < 0) {
+ task->name[0] = '\0';
+ goto out;
+ }
+
+ for (i = 0; i < TNM_LEN-1; i++) {
+ /* avoiding byte access */
+ u16 tmp = tnm[i];
+ task->name[i] = tmp & 0x00ff;
+ if (!tmp)
+ break;
+ }
+ task->name[TNM_LEN-1] = '\0';
+
+ task->state = TASK_ST_READY;
+out:
+ wake_up_interruptible(&cfg_wait_q);
+}
+
+void mbox_tadd(struct mbcmd *mb)
+{
+ u8 tid = mb->cmd_l;
+
+ if ((!waitqueue_active(&cfg_wait_q)) || (cfg_cmd != MBOX_CMD_DSP_TADD)) {
+ printk(KERN_WARNING "mbox: unexpected TADD from DSP!\n");
+ return;
+ }
+ cfg_tid = tid;
+ wake_up_interruptible(&cfg_wait_q);
+}
+
+void mbox_tdel(struct mbcmd *mb)
+{
+ u8 tid = mb->cmd_l;
+
+ if ((!waitqueue_active(&cfg_wait_q)) || (cfg_cmd != MBOX_CMD_DSP_TDEL)) {
+ printk(KERN_WARNING "mbox: unexpected TDEL from DSP!\n");
+ return;
+ }
+ cfg_tid = tid;
+ wake_up_interruptible(&cfg_wait_q);
+}
+
+void mbox_err_fatal(u8 tid)
+{
+ struct dsptask *task = dsptask[tid];
+ struct taskdev *dev;
+
+ if ((tid >= TASKDEV_MAX) || (task == NULL)) {
+ printk(KERN_ERR "mbox: FATAL ERR with illegal tid! %d\n", tid);
+ return;
+ }
+
+ /* wake up waiting processes */
+ dev = task->dev;
+ wake_up_interruptible_all(&dev->read_wait_q);
+ wake_up_interruptible_all(&dev->write_wait_q);
+ wake_up_interruptible_all(&dev->tctl_wait_q);
+}
+
+static u16 *dbg_buf;
+static u16 dbg_buf_sz, dbg_line_sz;
+static int dbg_rp;
+
+int dsp_dbg_config(u16 *buf, u16 sz, u16 lsz)
+{
+#ifdef OLD_BINARY_SUPPORT
+ if ((mbox_revision == MBREV_3_0) || (mbox_revision == MBREV_3_2)) {
+ dbg_buf = NULL;
+ dbg_buf_sz = 0;
+ dbg_line_sz = 0;
+ dbg_rp = 0;
+ return 0;
+ }
+#endif
+
+ if (dsp_address_validate(buf, sz, "debug buffer") < 0)
+ return -1;
+
+ if (lsz > sz) {
+ printk(KERN_ERR
+ "omapdsp: dbg_buf lsz (%d) is greater than its "
+ "buffer size (%d)\n", lsz, sz);
+ return -1;
+ }
+
+ dbg_buf = buf;
+ dbg_buf_sz = sz;
+ dbg_line_sz = lsz;
+ dbg_rp = 0;
+
+ return 0;
+}
+
+void dsp_dbg_stop(void)
+{
+ dbg_buf = NULL;
+}
+
+#ifdef OLD_BINARY_SUPPORT
+static void mbox_dbg_old(struct mbcmd *mb);
+#endif
+
+void mbox_dbg(struct mbcmd *mb)
+{
+ u8 tid = mb->cmd_l;
+ int cnt = mb->data;
+ char s[80], *s_end = &s[79], *p;
+ u16 *src;
+ int i;
+
+#ifdef OLD_BINARY_SUPPORT
+ if ((mbox_revision == MBREV_3_0) || (mbox_revision == MBREV_3_2)) {
+ mbox_dbg_old(mb);
+ return;
+ }
+#endif
+
+ if (((tid >= TASKDEV_MAX) || (dsptask[tid] == NULL)) &&
+ (tid != TID_ANON)) {
+ printk(KERN_ERR "mbox: DBG with illegal tid! %d\n", tid);
+ return;
+ }
+ if (dbg_buf == NULL) {
+ printk(KERN_ERR "mbox: DBG command received, but "
+ "dbg_buf has not been configured yet.\n");
+ return;
+ }
+
+ if (dsp_mem_enable(dbg_buf) < 0)
+ return;
+
+ src = &dbg_buf[dbg_rp];
+ p = s;
+ for (i = 0; i < cnt; i++) {
+ u16 tmp;
+ /*
+ * Be carefull that dbg_buf should not be read with
+ * 1-byte access since it might be placed in DARAM/SARAM
+ * and it can cause unexpected byteswap.
+ * For example,
+ * *(p++) = *(src++) & 0xff;
+ * causes 1-byte access!
+ */
+ tmp = *src++;
+ *(p++) = tmp & 0xff;
+ if (*(p-1) == '\n') {
+ *p = '\0';
+ pr_info("%s", s);
+ p = s;
+ continue;
+ }
+ if (p == s_end) {
+ *p = '\0';
+ pr_info("%s\n", s);
+ p = s;
+ continue;
+ }
+ }
+ if (p > s) {
+ *p = '\0';
+ pr_info("%s\n", s);
+ }
+ if ((dbg_rp += cnt + 1) > dbg_buf_sz - dbg_line_sz)
+ dbg_rp = 0;
+
+ dsp_mem_disable(dbg_buf);
+}
+
+#ifdef OLD_BINARY_SUPPORT
+static void mbox_dbg_old(struct mbcmd *mb)
+{
+ u8 tid = mb->cmd_l;
+ char s[80], *s_end = &s[79], *p;
+ u16 *src;
+ volatile u16 *buf;
+ int cnt;
+ int i;
+
+ if (((tid >= TASKDEV_MAX) || (dsptask[tid] == NULL)) &&
+ (tid != TID_ANON)) {
+ printk(KERN_ERR "mbox: DBG with illegal tid! %d\n", tid);
+ return;
+ }
+ if (dsp_mem_enable(ipbuf_sys_da) < 0) {
+ printk(KERN_ERR "mbox: DBG - ipbuf_sys_da read failed!\n");
+ return;
+ }
+ if (sync_with_dsp(&ipbuf_sys_da->s, tid, 10) < 0) {
+ printk(KERN_ERR "mbox: DBG - IPBUF sync failed!\n");
+ goto out1;
+ }
+ buf = ipbuf_sys_da->d;
+ cnt = buf[0];
+ src = MKVIRT(buf[1], buf[2]);
+ if (dsp_address_validate(src, cnt, "dbg buffer") < 0)
+ goto out2;
+
+ if (dsp_mem_enable(src) < 0)
+ goto out2;
+
+ p = s;
+ for (i = 0; i < cnt; i++) {
+ u16 tmp;
+ /*
+ * Be carefull that ipbuf should not be read with
+ * 1-byte access since it might be placed in DARAM/SARAM
+ * and it can cause unexpected byteswap.
+ * For example,
+ * *(p++) = *(src++) & 0xff;
+ * causes 1-byte access!
+ */
+ tmp = *src++;
+ *(p++) = tmp & 0xff;
+ if (*(p-1) == '\n') {
+ *p = '\0';
+ pr_info("%s", s);
+ p = s;
+ continue;
+ }
+ if (p == s_end) {
+ *p = '\0';
+ pr_info("%s\n", s);
+ p = s;
+ continue;
+ }
+ }
+ if (p > s) {
+ *p = '\0';
+ pr_info("%s\n", s);
+ }
+
+ dsp_mem_disable(src);
+out2:
+ release_ipbuf_pvt(ipbuf_sys_da);
+out1:
+ dsp_mem_disable(ipbuf_sys_da);
+}
+#endif /* OLD_BINARY_SUPPORT */
+
+/*
+ * sysfs files: for each device
+ */
+
+/* devname */
+static ssize_t devname_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", to_taskdev(d)->name);
+}
+
+/* devstate */
+static ssize_t devstate_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", devstate_name(to_taskdev(d)->state));
+}
+
+/* proc_list */
+static ssize_t proc_list_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct taskdev *dev;
+ struct proc_list *pl;
+ int len = 0;
+
+ dev = to_taskdev(d);
+ spin_lock(&dev->proc_list_lock);
+ list_for_each_entry(pl, &dev->proc_list, list_head) {
+ /* need to lock tasklist_lock before calling
+ * find_task_by_pid_type. */
+ if (find_task_by_pid_type_ns(PIDTYPE_PID, pl->pid, &init_pid_ns) != NULL)
+ len += sprintf(buf + len, "%d\n", pl->pid);
+ read_unlock(&tasklist_lock);
+ }
+ spin_unlock(&dev->proc_list_lock);
+
+ return len;
+}
+
+/* taskname */
+static ssize_t taskname_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct taskdev *dev = to_taskdev(d);
+ int len;
+
+ if (!devstate_read_lock_and_test(dev, TASKDEV_ST_ATTACHED))
+ return -ENODEV;
+
+ len = sprintf(buf, "%s\n", dev->task->name);
+
+ devstate_read_unlock(dev);
+ return len;
+}
+
+/* ttyp */
+static ssize_t ttyp_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct taskdev *dev = to_taskdev(d);
+ u16 ttyp;
+ int len = 0;
+
+ if (!devstate_read_lock_and_test(dev, TASKDEV_ST_ATTACHED))
+ return -ENODEV;
+
+ ttyp = dev->task->ttyp;
+ len += sprintf(buf + len, "0x%04x\n", ttyp);
+ len += sprintf(buf + len, "%s %s send\n",
+ (sndtyp_acv(ttyp)) ? "active" :
+ "passive",
+ (sndtyp_wd(ttyp)) ? "word" :
+ (sndtyp_pvt(ttyp)) ? "private block" :
+ "global block");
+ len += sprintf(buf + len, "%s %s receive\n",
+ (rcvtyp_acv(ttyp)) ? "active" :
+ "passive",
+ (rcvtyp_wd(ttyp)) ? "word" :
+ (rcvtyp_pvt(ttyp)) ? "private block" :
+ "global block");
+
+ devstate_read_unlock(dev);
+ return len;
+}
+
+/* fifosz */
+static ssize_t fifosz_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct kfifo *fifo = to_taskdev(d)->rcvdt.fifo;
+ return sprintf(buf, "%d\n", fifo->size);
+}
+
+static int fifosz_store(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct taskdev *dev = to_taskdev(d);
+ unsigned long fifosz;
+ int ret;
+
+ fifosz = simple_strtol(buf, NULL, 10);
+ if (taskdev_lock_and_statelock_attached(dev, &dev->read_mutex))
+ return -ENODEV;
+ ret = taskdev_set_fifosz(dev, fifosz);
+ taskdev_unlock_and_stateunlock(dev, &dev->read_mutex);
+
+ return (ret < 0) ? ret : strlen(buf);
+}
+
+/* fifocnt */
+static ssize_t fifocnt_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct kfifo *fifo = to_taskdev(d)->rcvdt.fifo;
+ return sprintf(buf, "%d\n", fifo->size);
+}
+
+/* ipblink */
+static inline char *bid_name(u16 bid)
+{
+ static char s[6];
+
+ switch (bid) {
+ case BID_NULL:
+ return "NULL";
+ case BID_PVT:
+ return "PRIVATE";
+ default:
+ sprintf(s, "%d", bid);
+ return s;
+ }
+}
+
+static ssize_t ipblink_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct rcvdt_bk_struct *rcvdt = &to_taskdev(d)->rcvdt.bk;
+ int len;
+
+ spin_lock(&rcvdt->link.lock);
+ len = sprintf(buf, "top %s\ntail %s\n",
+ bid_name(rcvdt->link.top), bid_name(rcvdt->link.tail));
+ spin_unlock(&rcvdt->link.lock);
+
+ return len;
+}
+
+/* wsz */
+static ssize_t wsz_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", to_taskdev(d)->wsz);
+}
+
+/* mmap */
+static ssize_t mmap_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct dsptask *task = to_taskdev(d)->task;
+ return sprintf(buf, "0x%p 0x%x\n", task->map_base, task->map_length);
+}
+
+/*
+ * called from ipbuf_show()
+ */
+int ipbuf_is_held(u8 tid, u16 bid)
+{
+ struct dsptask *task = dsptask[tid];
+ struct ipblink *link;
+ u16 b;
+ int ret = 0;
+
+ if (task == NULL)
+ return 0;
+
+ link = &task->dev->rcvdt.bk.link;
+ spin_lock(&link->lock);
+ ipblink_for_each(b, link) {
+ if (b == bid) { /* found */
+ ret = 1;
+ break;
+ }
+ }
+ spin_unlock(&link->lock);
+
+ return ret;
+}
+
+int __init dsp_taskmod_init(void)
+{
+ int retval;
+
+ memset(taskdev, 0, sizeof(void *) * TASKDEV_MAX);
+ memset(dsptask, 0, sizeof(void *) * TASKDEV_MAX);
+
+ retval = register_chrdev(OMAP_DSP_TASK_MAJOR, "dsptask",
+ &dsp_task_fops);
+ if (retval < 0) {
+ printk(KERN_ERR
+ "omapdsp: failed to register task device: %d\n", retval);
+ return retval;
+ }
+
+ retval = bus_register(&dsptask_bus);
+ if (retval) {
+ printk(KERN_ERR
+ "omapdsp: failed to register DSP task bus: %d\n",
+ retval);
+ unregister_chrdev(OMAP_DSP_TASK_MAJOR, "dsptask");
+ return -EINVAL;
+ }
+ retval = driver_register(&dsptask_driver);
+ if (retval) {
+ printk(KERN_ERR
+ "omapdsp: failed to register DSP task driver: %d\n",
+ retval);
+ bus_unregister(&dsptask_bus);
+ unregister_chrdev(OMAP_DSP_TASK_MAJOR, "dsptask");
+ return -EINVAL;
+ }
+ dsp_task_class = class_create(THIS_MODULE, "dsptask");
+ if (IS_ERR(dsp_task_class)) {
+ printk(KERN_ERR "omapdsp: failed to create DSP task class\n");
+ driver_unregister(&dsptask_driver);
+ bus_unregister(&dsptask_bus);
+ unregister_chrdev(OMAP_DSP_TASK_MAJOR, "dsptask");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void dsp_taskmod_exit(void)
+{
+ class_destroy(dsp_task_class);
+ driver_unregister(&dsptask_driver);
+ bus_unregister(&dsptask_bus);
+ unregister_chrdev(OMAP_DSP_TASK_MAJOR, "dsptask");
+}
--- /dev/null
- #include <asm/arch/dsp.h>
+/*
+ * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
+ *
+ * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
++#include <mach/dsp.h>
+#include <asm/io.h>
+#include "dsp_mbcmd.h"
+#include "dsp.h"
+
+static DECLARE_WAIT_QUEUE_HEAD(read_wait_q);
+static unsigned int change_cnt;
+
+void dsp_twch_touch(void)
+{
+ change_cnt++;
+ wake_up_interruptible(&read_wait_q);
+}
+
+/*
+ * @count: represents the device counts of the user's interst
+ */
+static ssize_t dsp_twch_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ long taskstat[TASKDEV_MAX];
+ int devcount = count / sizeof(long);
+ int i;
+ DEFINE_WAIT(wait);
+
+ if (dsp_cfgstat_get_stat() != CFGSTAT_READY) {
+ printk(KERN_ERR "omapdsp: dsp has not been configured.\n");
+ return -EINVAL;
+ }
+
+ prepare_to_wait(&read_wait_q, &wait, TASK_INTERRUPTIBLE);
+ if (change_cnt == 0) /* last check */
+ schedule();
+ finish_wait(&read_wait_q, &wait);
+
+ /* unconfigured while waiting ;-( */
+ if ((change_cnt == 0) && (dsp_cfgstat_get_stat() != CFGSTAT_READY))
+ return -EINVAL;
+
+ if (devcount > TASKDEV_MAX)
+ devcount = TASKDEV_MAX;
+
+ count = devcount * sizeof(long);
+ change_cnt = 0;
+ for (i = 0; i < devcount; i++) {
+ /*
+ * once the device state is read, the 'STALE' bit will be set
+ * so that the Dynamic Loader can distinguish the new request
+ * from the old one.
+ */
+ taskstat[i] = taskdev_state_stale(i);
+ }
+
+ if (copy_to_user(buf, taskstat, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static unsigned int dsp_twch_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask = 0;
+
+ poll_wait(file, &read_wait_q, wait);
+ if (change_cnt)
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+static int dsp_twch_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret;
+
+ switch (cmd) {
+ case TWCH_IOCTL_MKDEV:
+ {
+ char name[TNM_LEN];
+ if (copy_from_user(name, (void __user *)arg, TNM_LEN))
+ return -EFAULT;
+ name[TNM_LEN-1] = '\0';
+ ret = dsp_mkdev(name);
+ break;
+ }
+
+ case TWCH_IOCTL_RMDEV:
+ {
+ char name[TNM_LEN];
+ if (copy_from_user(name, (void __user *)arg, TNM_LEN))
+ return -EFAULT;
+ name[TNM_LEN-1] = '\0';
+ ret = dsp_rmdev(name);
+ break;
+ }
+
+ case TWCH_IOCTL_TADD:
+ {
+ struct omap_dsp_taddinfo ti;
+ if (copy_from_user(&ti, (void __user *)arg, sizeof(ti)))
+ return -EFAULT;
+ ret = dsp_tadd_minor(ti.minor, ti.taskadr);
+ break;
+ }
+
+ case TWCH_IOCTL_TDEL:
+ ret = dsp_tdel_minor(arg);
+ break;
+
+ case TWCH_IOCTL_TKILL:
+ ret = dsp_tkill_minor(arg);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return ret;
+}
+
+struct file_operations dsp_twch_fops = {
+ .owner = THIS_MODULE,
+ .read = dsp_twch_read,
+ .poll = dsp_twch_poll,
+ .ioctl = dsp_twch_ioctl,
+};
+
+void dsp_twch_start(void)
+{
+ change_cnt = 1; /* first read will not wait */
+}
+
+void dsp_twch_stop(void)
+{
+ wake_up_interruptible(&read_wait_q);
+}
--- /dev/null
- #include <asm/arch/dsp_common.h>
+/*
+ * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
+ *
+ * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _OMAP_DSP_UACCESS_DSP_H
+#define _OMAP_DSP_UACCESS_DSP_H
+
+#include <asm/uaccess.h>
++#include <mach/dsp_common.h>
+#include "dsp.h"
+
+#define HAVE_ASM_COPY_FROM_USER_DSP_2B
+
+#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B
+extern unsigned long __copy_from_user_dsp_2b(void *to,
+ const void __user *from);
+extern unsigned long __copy_to_user_dsp_2b(void __user *to,
+ const void *from);
+#endif
+
+#ifndef HAVE_ASM_COPY_FROM_USER_DSP_2B
+static inline unsigned long copy_from_user_dsp_2b(void *to,
+ const void *from)
+{
+ unsigned short tmp;
+
+ if (__copy_from_user(&tmp, from, 2))
+ return 2;
+ /* expecting compiler to generate "strh" instruction */
+ *((unsigned short *)to) = tmp;
+ return 0;
+}
+#endif
+
+/*
+ * @n must be multiple of 2
+ */
+static inline unsigned long copy_from_user_dsp(void *to, const void *from,
+ unsigned long n)
+{
+ if (access_ok(VERIFY_READ, from, n)) {
+ if ((is_dsp_internal_mem(to)) &&
+ (((unsigned long)to & 2) || (n & 2))) {
+ /*
+ * DARAM/SARAM with odd word alignment
+ */
+ unsigned long n4;
+ unsigned long last_n;
+
+ /* dest not aligned -- copy 2 bytes */
+ if (((unsigned long)to & 2) && (n >= 2)) {
+#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B
+ if (__copy_from_user_dsp_2b(to, from))
+#else
+ if (copy_from_user_dsp_2b(to, from))
+#endif
+ return n;
+ to += 2;
+ from += 2;
+ n -= 2;
+ }
+ /* middle 4*n bytes */
+ last_n = n & 2;
+ n4 = n - last_n;
+ if ((n = __copy_from_user(to, from, n4)) != 0)
+ return n + last_n;
+ /* last 2 bytes */
+ if (last_n) {
+ to += n4;
+ from += n4;
+#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B
+ if (__copy_from_user_dsp_2b(to, from))
+#else
+ if (copy_from_user_dsp_2b(to, from))
+#endif
+ return 2;
+ n = 0;
+ }
+ } else {
+ /*
+ * DARAM/SARAM with 4-byte alignment or
+ * external memory
+ */
+ n = __copy_from_user(to, from, n);
+ }
+ }
+ else /* security hole - plug it */
+ memzero(to, n);
+ return n;
+}
+
+#ifndef HAVE_ASM_COPY_FROM_USER_DSP_2B
+static inline unsigned long copy_to_user_dsp_2b(void *to, const void *from)
+{
+ /* expecting compiler to generate "strh" instruction */
+ unsigned short tmp = *(unsigned short *)from;
+
+ return __copy_to_user(to, &tmp, 2);
+}
+#endif
+
+/*
+ * @n must be multiple of 2
+ */
+static inline unsigned long copy_to_user_dsp(void *to, const void *from,
+ unsigned long n)
+{
+ if (access_ok(VERIFY_WRITE, to, n)) {
+ if ((is_dsp_internal_mem(from)) &&
+ (((unsigned long)to & 2) || (n & 2))) {
+ /*
+ * DARAM/SARAM with odd word alignment
+ */
+ unsigned long n4;
+ unsigned long last_n;
+
+ /* dest not aligned -- copy 2 bytes */
+ if (((unsigned long)to & 2) && (n >= 2)) {
+#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B
+ if (__copy_to_user_dsp_2b(to, from))
+#else
+ if (copy_to_user_dsp_2b(to, from))
+#endif
+ return n;
+ to += 2;
+ from += 2;
+ n -= 2;
+ }
+ /* middle 4*n bytes */
+ last_n = n & 2;
+ n4 = n - last_n;
+ if ((n = __copy_to_user(to, from, n4)) != 0)
+ return n + last_n;
+ /* last 2 bytes */
+ if (last_n) {
+ to += n4;
+ from += n4;
+#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B
+ if (__copy_to_user_dsp_2b(to, from))
+#else
+ if (copy_to_user_dsp_2b(to, from))
+#endif
+ return 2;
+ n = 0;
+ }
+ } else {
+ /*
+ * DARAM/SARAM with 4-byte alignment or
+ * external memory
+ */
+ n = __copy_to_user(to, from, n);
+ }
+ }
+ return n;
+}
+
+#endif /* _OMAP_DSP_UACCESS_DSP_H */
--- /dev/null
- #include <asm/arch/omap34xx.h>
- #include <asm/arch/control.h>
+/*
+ * omap34xx_temp.c - Linux kernel module for OMAP34xx hardware monitoring
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Written by Peter De Schrijver <peter.de-schrijver@nokia.com>
+ *
+ * Inspired by k8temp.c
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/clk.h>
+#include <linux/hrtimer.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
++#include <mach/omap34xx.h>
++#include <mach/control.h>
+
+#define TEMP_SENSOR_SOC BIT(8)
+#define TEMP_SENSOR_EOCZ BIT(7)
+
+/* minimum delay for EOCZ rise after SOC rise is
+ * 11 cycles of the 32.768Khz clock */
+#define EOCZ_MIN_RISING_DELAY (11 * 30518)
+
+/* maximum delay for EOCZ rise after SOC rise is
+ * 14 cycles of the 32.768Khz clock */
+#define EOCZ_MAX_RISING_DELAY (14 * 30518)
+
+/* minimum delay for EOCZ falling is
+ * 36 cycles of the 32.768Khz clock */
+#define EOCZ_MIN_FALLING_DELAY (36 * 30518)
+
+/* maximum delay for EOCZ falling is
+ * 40 cycles of the 32.768Khz clock */
+#define EOCZ_MAX_FALLING_DELAY (40 * 30518)
+
+struct omap34xx_data {
+ struct device *hwmon_dev;
+ struct clk *clk_32k;
+ struct mutex update_lock;
+ const char *name;
+ char valid;
+ unsigned long last_updated;
+ u32 temp;
+};
+
+static struct platform_device omap34xx_temp_device = {
+ .name = "omap34xx_temp",
+ .id = -1,
+};
+
+static int adc_to_temp[] = {
+ -40, -40, -40, -40, -40, -39, -38, -36, -34, -32, -31, -29, -28, -26,
+ -25, -24, -22, -21, -19, -18, -17, -15, -14, -12, -11, -9, -8, -7, -5,
+ -4, -2, -1, 0, 1, 3, 4, 5, 7, 8, 10, 11, 13, 14, 15, 17, 18, 20, 21,
+ 22, 24, 25, 27, 28, 30, 31, 32, 34, 35, 37, 38, 39, 41, 42, 44, 45,
+ 47, 48, 49, 51, 52, 53, 55, 56, 58, 59, 60, 62, 63, 65, 66, 67, 69,
+ 70, 72, 73, 74, 76, 77, 79, 80, 81, 83, 84, 85, 87, 88, 89, 91, 92,
+ 94, 95, 96, 98, 99, 100, 102, 103, 105, 106, 107, 109, 110, 111, 113,
+ 114, 116, 117, 118, 120, 121, 122, 124, 124, 125, 125, 125, 125, 125};
+
+static inline u32 wait_for_eocz(int min_delay, int max_delay, u32 level)
+{
+ struct timespec timeout;
+ ktime_t expire;
+ u32 temp_sensor_reg;
+
+ level &= 1;
+ level *= TEMP_SENSOR_EOCZ;
+
+ expire = ktime_add_ns(ktime_get(), max_delay);
+ timeout = ns_to_timespec(min_delay);
+ hrtimer_nanosleep(&timeout, NULL, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
+ do {
+ temp_sensor_reg = omap_ctrl_readl(OMAP343X_CONTROL_TEMP_SENSOR);
+ if ((temp_sensor_reg & TEMP_SENSOR_EOCZ) == level)
+ break;
+ } while (ktime_us_delta(expire, ktime_get()) > 0);
+
+ return (temp_sensor_reg & TEMP_SENSOR_EOCZ) == level;
+}
+
+static void omap34xx_update(struct omap34xx_data *data)
+{
+ u32 temp_sensor_reg;
+
+ mutex_lock(&data->update_lock);
+
+ if (!data->valid
+ || time_after(jiffies, data->last_updated + HZ)) {
+
+ clk_enable(data->clk_32k);
+
+ temp_sensor_reg = omap_ctrl_readl(OMAP343X_CONTROL_TEMP_SENSOR);
+ temp_sensor_reg |= TEMP_SENSOR_SOC;
+ omap_ctrl_writel(temp_sensor_reg, OMAP343X_CONTROL_TEMP_SENSOR);
+
+ if (!wait_for_eocz(EOCZ_MIN_RISING_DELAY,
+ EOCZ_MAX_RISING_DELAY, 1))
+ goto err;
+
+ temp_sensor_reg = omap_ctrl_readl(OMAP343X_CONTROL_TEMP_SENSOR);
+ temp_sensor_reg &= ~TEMP_SENSOR_SOC;
+ omap_ctrl_writel(temp_sensor_reg, OMAP343X_CONTROL_TEMP_SENSOR);
+
+ if (!wait_for_eocz(EOCZ_MIN_FALLING_DELAY,
+ EOCZ_MAX_FALLING_DELAY, 0))
+ goto err;
+
+ data->temp = omap_ctrl_readl(OMAP343X_CONTROL_TEMP_SENSOR) &
+ ((1<<7) - 1);
+ data->last_updated = jiffies;
+ data->valid = 1;
+
+err:
+ clk_disable(data->clk_32k);
+ }
+
+ mutex_unlock(&data->update_lock);
+}
+
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct omap34xx_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+
+static ssize_t show_temp_raw(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct omap34xx_data *data = dev_get_drvdata(dev);
+
+ omap34xx_update(data);
+
+ return sprintf(buf, "%d\n", data->temp);
+}
+
+static ssize_t show_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct omap34xx_data *data = dev_get_drvdata(dev);
+
+ omap34xx_update(data);
+
+ return sprintf(buf, "%d\n", adc_to_temp[data->temp]);
+}
+
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_input_raw, S_IRUGO, show_temp_raw,
+ NULL, 0, 0);
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static int __devinit omap34xx_temp_probe(void)
+{
+ int err;
+ struct omap34xx_data *data;
+
+ err = platform_device_register(&omap34xx_temp_device);
+ if (err) {
+ printk(KERN_ERR
+ "Unable to register omap34xx temperature device\n");
+ goto exit;
+ }
+
+ data = kzalloc(sizeof(struct omap34xx_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit_platform;
+ }
+
+ dev_set_drvdata(&omap34xx_temp_device.dev, data);
+ mutex_init(&data->update_lock);
+ data->name = "omap34xx_temp";
+
+ data->clk_32k = clk_get(&omap34xx_temp_device.dev, "ts_fck");
+ if (IS_ERR(data->clk_32k)) {
+ err = PTR_ERR(data->clk_32k);
+ goto exit_free;
+ }
+
+ err = device_create_file(&omap34xx_temp_device.dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ if (err)
+ goto clock_free;
+
+ err = device_create_file(&omap34xx_temp_device.dev,
+ &sensor_dev_attr_temp1_input_raw.dev_attr);
+ if (err)
+ goto exit_remove;
+
+ err = device_create_file(&omap34xx_temp_device.dev, &dev_attr_name);
+ if (err)
+ goto exit_remove_raw;
+
+ data->hwmon_dev = hwmon_device_register(&omap34xx_temp_device.dev);
+
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_all;
+ }
+
+ return 0;
+
+exit_remove_all:
+ device_remove_file(&omap34xx_temp_device.dev,
+ &dev_attr_name);
+exit_remove_raw:
+ device_remove_file(&omap34xx_temp_device.dev,
+ &sensor_dev_attr_temp1_input_raw.dev_attr);
+exit_remove:
+ device_remove_file(&omap34xx_temp_device.dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+clock_free:
+ clk_put(data->clk_32k);
+
+exit_free:
+ kfree(data);
+exit_platform:
+ platform_device_unregister(&omap34xx_temp_device);
+exit:
+ return err;
+}
+
+static int __init omap34xx_temp_init(void)
+{
+ return omap34xx_temp_probe();
+}
+
+static void __exit omap34xx_temp_exit(void)
+{
+ struct omap34xx_data *data =
+ dev_get_drvdata(&omap34xx_temp_device.dev);
+
+ clk_put(data->clk_32k);
+ hwmon_device_unregister(data->hwmon_dev);
+ device_remove_file(&omap34xx_temp_device.dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ device_remove_file(&omap34xx_temp_device.dev, &dev_attr_name);
+ kfree(data);
+ platform_device_unregister(&omap34xx_temp_device);
+}
+
+MODULE_AUTHOR("Peter De Schrijver");
+MODULE_DESCRIPTION("Omap34xx temperature sensor");
+MODULE_LICENSE("GPL");
+
+module_init(omap34xx_temp_init)
+module_exit(omap34xx_temp_exit)
+
--- /dev/null
- #include <asm/arch/gpio.h>
+/*
+ * drivers/i2c/chips/lp5521.c
+ *
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Written by Mathias Nyman <mathias.nyman@nokia.com>
+ *
+ * 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/init.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
++#include <mach/gpio.h>
+
+#define LP5521_DRIVER_NAME "lp5521"
+
+#ifdef LED_CONNECTED_WRONG
+#define LP5521_REG_R_PWM 0x04
+#define LP5521_REG_B_PWM 0x02
+#else
+#define LP5521_REG_R_PWM 0x02
+#define LP5521_REG_B_PWM 0x04
+#endif
+#define LP5521_REG_ENABLE 0x00
+#define LP5521_REG_OP_MODE 0x01
+#define LP5521_REG_G_PWM 0x03
+#define LP5521_REG_R_CNTRL 0x05
+#define LP5521_REG_G_CNTRL 0x06
+#define LP5521_REG_B_CNTRL 0x07
+#define LP5521_REG_MISC 0x08
+#define LP5521_REG_R_CHANNEL_PC 0x09
+#define LP5521_REG_G_CHANNEL_PC 0x0a
+#define LP5521_REG_B_CHANNEL_PC 0x0b
+#define LP5521_REG_STATUS 0x0c
+#define LP5521_REG_RESET 0x0d
+#define LP5521_REG_GPO 0x0e
+#define LP5521_REG_R_PROG_MEM 0x10
+#define LP5521_REG_G_PROG_MEM 0x30
+#define LP5521_REG_B_PROG_MEM 0x50
+
+#define LP5521_MODE_LOAD "load"
+#define LP5521_MODE_RUN "run"
+#define LP5521_MODE_DIRECT_CONTROL "direct"
+
+#define LP5521_CURRENT_1m5 0x0f
+#define LP5521_CURRENT_3m1 0x1f
+#define LP5521_CURRENT_4m7 0x2f
+#define LP5521_CURRENT_6m3 0x3f
+#define LP5521_CURRENT_7m9 0x4f
+#define LP5521_CURRENT_9m5 0x5f
+#define LP5521_CURRENT_11m1 0x6f
+#define LP5521_CURRENT_12m7 0x7f
+#define LP5521_CURRENT_14m3 0x8f
+#define LP5521_CURRENT_15m9 0x9f
+#define LP5521_CURRENT_17m5 0xaf
+#define LP5521_CURRENT_19m1 0xbf
+#define LP5521_CURRENT_20m7 0xcf
+#define LP5521_CURRENT_22m3 0xdf
+#define LP5521_CURRENT_23m9 0xef
+#define LP5521_CURRENT_25m5 0xff
+
+#define LP5521_PROGRAM_LENGTH 32 /* in bytes */
+
+struct lp5521_chip {
+ struct mutex lock;
+ struct i2c_client *client;
+ char *mode;
+ int red;
+ int green;
+ int blue;
+};
+
+static int lp5521_set_mode(struct lp5521_chip *chip, char *mode);
+
+static int lp5521_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf)
+{
+ s32 ret = i2c_smbus_read_byte_data(client, reg);
+
+ if (ret < 0)
+ return -EIO;
+
+ *buf = ret;
+ return 0;
+}
+
+static int lp5521_configure(struct i2c_client *client)
+{
+ int ret = 0;
+
+ /* Enable chip and set light to logarithmic mode*/
+ ret |= lp5521_write(client, LP5521_REG_ENABLE, 0xc0);
+
+ /* setting all color pwms to direct control mode */
+ ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x3f);
+
+ /* setting current to 4.7 mA for all channels */
+ ret |= lp5521_write(client, LP5521_REG_R_CNTRL, LP5521_CURRENT_4m7);
+ ret |= lp5521_write(client, LP5521_REG_G_CNTRL, LP5521_CURRENT_4m7);
+ ret |= lp5521_write(client, LP5521_REG_B_CNTRL, LP5521_CURRENT_4m7);
+
+ /* Enable auto-powersave, set charge pump to auto, red to battery */
+ ret |= lp5521_write(client, LP5521_REG_MISC, 0x3c);
+
+ /* initialize all channels pwm to zero */
+ ret |= lp5521_write(client, LP5521_REG_R_PWM, 0);
+ ret |= lp5521_write(client, LP5521_REG_G_PWM, 0);
+ ret |= lp5521_write(client, LP5521_REG_B_PWM, 0);
+
+ /* Not much can be done about errors at this point */
+ return ret;
+}
+
+static int lp5521_load_program(struct lp5521_chip *chip, u8 *pattern)
+{
+ struct i2c_client *client = chip->client;
+ int ret = 0;
+
+ /* Enter load program mode for all led channels */
+ ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x15); /* 0001 0101 */
+ if (ret)
+ return ret;
+
+ if (chip->red)
+ ret |= i2c_smbus_write_i2c_block_data(client,
+ LP5521_REG_R_PROG_MEM,
+ LP5521_PROGRAM_LENGTH,
+ pattern);
+ if (chip->green)
+ ret |= i2c_smbus_write_i2c_block_data(client,
+ LP5521_REG_G_PROG_MEM,
+ LP5521_PROGRAM_LENGTH,
+ pattern);
+ if (chip->blue)
+ ret |= i2c_smbus_write_i2c_block_data(client,
+ LP5521_REG_B_PROG_MEM,
+ LP5521_PROGRAM_LENGTH,
+ pattern);
+
+ return ret;
+}
+
+static int lp5521_run_program(struct lp5521_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ int ret;
+ u8 mask = 0xc0;
+ u8 exec_state = 0;
+ u8 enable_reg;
+
+ ret = lp5521_read(client, LP5521_REG_ENABLE, &enable_reg);
+ if (ret)
+ goto fail;
+
+ enable_reg &= mask;
+
+ /* set all active channels exec state to countinous run*/
+ exec_state |= (chip->red << 5);
+ exec_state |= (chip->green << 3);
+ exec_state |= (chip->blue << 1);
+
+ enable_reg |= exec_state;
+
+ ret |= lp5521_write(client, LP5521_REG_ENABLE, enable_reg);
+
+ /* set op-mode to run for active channels, disabled for others */
+ ret |= lp5521_write(client, LP5521_REG_OP_MODE, exec_state);
+
+fail:
+ return ret;
+}
+
+/*--------------------------------------------------------------*/
+/* Sysfs interface */
+/*--------------------------------------------------------------*/
+
+static ssize_t show_active_channels(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct lp5521_chip *chip = dev_get_drvdata(dev);
+ char channels[4];
+ int pos = 0;
+
+#ifdef LED_CONNECTED_WRONG
+ if (chip->blue)
+ pos += sprintf(channels + pos, "r");
+ if (chip->green)
+ pos += sprintf(channels + pos, "g");
+ if (chip->red)
+ pos += sprintf(channels + pos, "b");
+
+#else
+ if (chip->red)
+ pos += sprintf(channels + pos, "r");
+ if (chip->green)
+ pos += sprintf(channels + pos, "g");
+ if (chip->blue)
+ pos += sprintf(channels + pos, "b");
+#endif
+
+ channels[pos] = '\0';
+
+ return sprintf(buf, "%s\n", channels);
+}
+
+static ssize_t store_active_channels(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp5521_chip *chip = dev_get_drvdata(dev);
+
+ chip->red = 0;
+ chip->green = 0;
+ chip->blue = 0;
+
+#ifdef LED_CONNECTED_WRONG
+ if (strchr(buf, 'r') != NULL)
+ chip->blue = 1;
+ if (strchr(buf, 'b') != NULL)
+ chip->red = 1;
+#else
+ if (strchr(buf, 'r') != NULL)
+ chip->red = 1;
+ if (strchr(buf, 'b') != NULL)
+ chip->blue = 1;
+#endif
+ if (strchr(buf, 'g') != NULL)
+ chip->green = 1;
+
+ return len;
+}
+
+static ssize_t show_color(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret = 0;
+ u8 r, g, b;
+
+ ret |= lp5521_read(client, LP5521_REG_R_PWM, &r);
+ ret |= lp5521_read(client, LP5521_REG_G_PWM, &g);
+ ret |= lp5521_read(client, LP5521_REG_B_PWM, &b);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%.2x:%.2x:%.2x\n", r, g, b);
+}
+
+static ssize_t store_color(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lp5521_chip *chip = i2c_get_clientdata(client);
+ int ret;
+ unsigned r, g, b;
+
+
+ ret = sscanf(buf, "%2x:%2x:%2x", &r, &g, &b);
+ if (ret != 3)
+ return -EINVAL;
+
+ mutex_lock(&chip->lock);
+
+ ret = lp5521_write(client, LP5521_REG_R_PWM, (u8)r);
+ ret = lp5521_write(client, LP5521_REG_G_PWM, (u8)g);
+ ret = lp5521_write(client, LP5521_REG_B_PWM, (u8)b);
+
+ mutex_unlock(&chip->lock);
+
+ return len;
+}
+
+static ssize_t store_load(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp5521_chip *chip = dev_get_drvdata(dev);
+ int ret, nrchars, offset = 0, i = 0;
+ char c[3];
+ unsigned cmd;
+ u8 pattern[LP5521_PROGRAM_LENGTH] = {0};
+
+ while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) {
+
+ /* separate sscanfs because length is working only for %s */
+ ret = sscanf(buf + offset, "%2s%n ", c, &nrchars);
+ ret = sscanf(c, "%2x", &cmd);
+ if (ret != 1)
+ goto fail;
+ pattern[i] = (u8)cmd;
+
+ offset += nrchars;
+ i++;
+ }
+
+ /* pattern commands are always two bytes long */
+ if (i % 2)
+ goto fail;
+
+ mutex_lock(&chip->lock);
+
+ ret = lp5521_load_program(chip, pattern);
+ mutex_unlock(&chip->lock);
+
+ if (ret) {
+ dev_err(dev, "lp5521 failed loading pattern\n");
+ return ret;
+ }
+
+ return len;
+fail:
+ dev_err(dev, "lp5521 wrong pattern format\n");
+ return -EINVAL;
+}
+
+static ssize_t show_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct lp5521_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", chip->mode);
+}
+
+static ssize_t store_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp5521_chip *chip = dev_get_drvdata(dev);
+
+ mutex_lock(&chip->lock);
+
+ if (!strncmp(buf, "run", 3))
+ lp5521_set_mode(chip, LP5521_MODE_RUN);
+ else if (!strncmp(buf, "load", 4))
+ lp5521_set_mode(chip, LP5521_MODE_LOAD);
+ else if (!strncmp(buf, "direct", 6))
+ lp5521_set_mode(chip, LP5521_MODE_DIRECT_CONTROL);
+
+ mutex_unlock(&chip->lock);
+
+ return len;
+}
+
+static ssize_t show_current(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret = 0;
+ u8 r_curr, g_curr, b_curr;
+
+ ret |= lp5521_read(client, LP5521_REG_R_CNTRL, &r_curr);
+ ret |= lp5521_read(client, LP5521_REG_G_CNTRL, &g_curr);
+ ret |= lp5521_read(client, LP5521_REG_B_CNTRL, &b_curr);
+
+ if (ret)
+ return ret;
+
+ r_curr = r_curr >> 4;
+ g_curr = g_curr >> 4;
+ b_curr = b_curr >> 4;
+
+ if (r_curr == g_curr && g_curr == b_curr)
+ return sprintf(buf, "%x\n", r_curr);
+ else
+ return sprintf(buf, "%x %x %x\n", r_curr, g_curr, b_curr);
+}
+
+static ssize_t store_current(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp5521_chip *chip = dev_get_drvdata(dev);
+ struct i2c_client *client = chip->client;
+ int ret;
+ unsigned curr;
+
+ ret = sscanf(buf, "%1x", &curr);
+ if (ret != 1)
+ return -EINVAL;
+
+ /* current level is determined by the 4 upper bits, rest is ones */
+ curr = (curr << 4) | 0x0f;
+
+ mutex_lock(&chip->lock);
+
+ ret |= lp5521_write(client, LP5521_REG_R_CNTRL, (u8)curr);
+ ret |= lp5521_write(client, LP5521_REG_G_CNTRL, (u8)curr);
+ ret |= lp5521_write(client, LP5521_REG_B_CNTRL, (u8)curr);
+
+ mutex_unlock(&chip->lock);
+
+ return len;
+}
+
+static DEVICE_ATTR(color, S_IRUGO | S_IWUGO, show_color, store_color);
+static DEVICE_ATTR(load, S_IWUGO, NULL, store_load);
+static DEVICE_ATTR(mode, S_IRUGO | S_IWUGO, show_mode, store_mode);
+static DEVICE_ATTR(active_channels, S_IRUGO | S_IWUGO,
+ show_active_channels, store_active_channels);
+static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current);
+
+static int lp5521_register_sysfs(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ int ret;
+
+ ret = device_create_file(dev, &dev_attr_color);
+ if (ret)
+ goto fail1;
+ ret = device_create_file(dev, &dev_attr_load);
+ if (ret)
+ goto fail2;
+ ret = device_create_file(dev, &dev_attr_active_channels);
+ if (ret)
+ goto fail3;
+ ret = device_create_file(dev, &dev_attr_mode);
+ if (ret)
+ goto fail4;
+ ret = device_create_file(dev, &dev_attr_led_current);
+ if (ret)
+ goto fail5;
+ return 0;
+
+fail5:
+ device_remove_file(dev, &dev_attr_mode);
+fail4:
+ device_remove_file(dev, &dev_attr_active_channels);
+fail3:
+ device_remove_file(dev, &dev_attr_load);
+fail2:
+ device_remove_file(dev, &dev_attr_color);
+fail1:
+ return ret;
+}
+
+static void lp5521_unregister_sysfs(struct i2c_client *client)
+{
+ struct lp5521_chip *chip = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+
+ device_remove_file(dev, &dev_attr_led_current);
+ device_remove_file(dev, &dev_attr_mode);
+ device_remove_file(dev, &dev_attr_active_channels);
+ device_remove_file(dev, &dev_attr_color);
+
+ if (!strcmp(chip->mode, LP5521_MODE_LOAD))
+ device_remove_file(dev, &dev_attr_load);
+}
+
+/*--------------------------------------------------------------*/
+/* Set chip operating mode */
+/*--------------------------------------------------------------*/
+
+static int lp5521_set_mode(struct lp5521_chip *chip, char *mode)
+{
+ struct i2c_client *client = chip->client ;
+ int ret = 0;
+
+ /* if in that mode already do nothing, except for run */
+ if (!strcmp(mode, chip->mode) && strcmp(mode, LP5521_MODE_RUN))
+ return 0;
+
+ if (!strcmp(mode, LP5521_MODE_RUN))
+ ret = lp5521_run_program(chip);
+
+ if (!strcmp(mode, LP5521_MODE_LOAD))
+ ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x15);
+
+ if (!strcmp(mode, LP5521_MODE_DIRECT_CONTROL))
+ ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x3F);
+
+ chip->mode = mode;
+
+ return ret;
+}
+
+/*--------------------------------------------------------------*/
+/* Probe, Attach, Remove */
+/*--------------------------------------------------------------*/
+static struct i2c_driver lp5521_driver;
+
+static int lp5521_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lp5521_chip *chip;
+ int ret = 0;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->client = client;
+ strncpy(client->name, LP5521_DRIVER_NAME, I2C_NAME_SIZE);
+ i2c_set_clientdata(client, chip);
+
+ mutex_init(&chip->lock);
+
+ ret = lp5521_configure(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "lp5521 error configuring chip \n");
+ goto fail1;
+ }
+
+ /* Set default values */
+ chip->mode = LP5521_MODE_DIRECT_CONTROL;
+ chip->red = 1;
+ chip->green = 1;
+ chip->blue = 1;
+
+ ret = lp5521_register_sysfs(client);
+ if (ret)
+ dev_err(&client->dev, "lp5521 registering sysfs failed \n");
+
+ return ret;
+
+fail1:
+ kfree(chip);
+ return ret;
+}
+
+static int lp5521_remove(struct i2c_client *client)
+{
+ struct lp5521_chip *chip = i2c_get_clientdata(client);
+
+ lp5521_unregister_sysfs(client);
+ kfree(chip);
+
+ return 0;
+}
+
+static const struct i2c_device_id lp5521_id[] = {
+ { LP5521_DRIVER_NAME, 0},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, lp5521_id);
+
+static struct i2c_driver lp5521_driver = {
+ .driver = {
+ .name = LP5521_DRIVER_NAME,
+ },
+ .probe = lp5521_probe,
+ .remove = __devexit_p(lp5521_remove),
+ .id_table = lp5521_id,
+};
+
+static int __init lp5521_init(void)
+{
+ return i2c_add_driver(&lp5521_driver);
+}
+
+static void __exit lp5521_exit(void)
+{
+ i2c_del_driver(&lp5521_driver);
+}
+
+MODULE_AUTHOR("Mathias Nyman <mathias.nyman@nokia.com>");
+MODULE_DESCRIPTION("lp5521 LED driver");
+MODULE_LICENSE("GPL");
+
+module_init(lp5521_init);
+module_exit(lp5521_exit);
#include <linux/delay.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
+#include <linux/i2c/menelaus.h>
+#include <asm/mach-types.h>
#include <asm/mach/irq.h>
- #include <asm/arch/gpio.h>
+ #include <mach/gpio.h>
-#include <mach/menelaus.h>
#define DRIVER_NAME "menelaus"
--- /dev/null
- #include <asm/arch/aic23.h>
- #include <asm/arch/mcbsp.h>
+/*
+ * Texas Instrumens TLV320AIC23 audio codec's i2c interface.
+ *
+ * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
+ * Copyright (c) by Jussi Laako <jussi.laako@nokia.com>
+ *
+ * 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/init.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <asm/io.h>
++#include <mach/aic23.h>
++#include <mach/mcbsp.h>
+
+#define TLV320AIC23_VERSION "1.8"
+#define TLV320AIC23_DATE "10-Feb-2006"
+#define MAX_VOL 100
+#define MIN_VOL 0
+#define MAX_GAIN 100
+#define MIN_GAIN 0
+#define OUTPUT_VOLUME_MIN LHV_MIN
+#define OUTPUT_VOLUME_MAX LHV_MAX
+#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)
+#define INPUT_VOLUME_MIN LIV_MIN
+#define INPUT_VOLUME_MAX LIV_MAX
+#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
+
+/* I2C Addresses to scan */
+static unsigned short normal_i2c[] = { TLV320AIC23ID1, TLV320AIC23ID2, \
+ I2C_CLIENT_END };
+/*static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };*/
+
+/* This makes all addr_data:s */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver aic23_driver;
+static struct i2c_client *new_client;
+static int selftest;
+static struct platform_device audio_i2c_device;
+
+static struct aic23_info {
+ u16 volume_reg_left;
+ u16 volume_reg_right;
+ u16 input_gain_reg_left;
+ u16 input_gain_reg_right;
+ u16 power; /* For POWER_DOWN_CONTROL_ADDR */
+ u16 mask; /* For ANALOG_AUDIO_CONTROL_ADDR */
+ int mic_loopback;
+ int mic_enable;
+ int sta;
+ int power_down;
+ int initialized;
+} aic23_info_l;
+
+static int _aic23_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+ u8 val, wreg;
+
+ /* TLV320AIC23 has 7 bit address and 9 bits of data
+ * so we need to switch one data bit into reg and rest
+ * of data into val
+ */
+
+ wreg = (reg << 1);
+ val = (0x01 & (value >> 8));
+ wreg = (wreg | val);
+ val = (0x00ff & value);
+
+ return i2c_smbus_write_byte_data(client, wreg, val);
+}
+
+int aic23_write_value(u8 reg, u16 value)
+{
+ static struct i2c_client *client;
+ client = new_client;
+ _aic23_write_value(client, reg, value);
+
+ return 0;
+}
+
+/*
+ * Configures the McBSP3 which is used to send clock to the AIC23 codec.
+ * The input clock rate from DSP is 12MHz.
+ * The DSP clock must be on before this is called.
+ */
+static int omap_mcbsp3_aic23_clock_init(void)
+{
+ u16 w;
+
+ /* enable 12MHz clock to mcbsp 1 & 3 */
+ __raw_writew(__raw_readw(DSP_IDLECT2) | (1<<1), DSP_IDLECT2);
+ __raw_writew(__raw_readw(DSP_RSTCT2) | 1 | 1<<1, DSP_RSTCT2);
+
+ /* disable sample rate generator */
+ OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SPCR1, 0x0000);
+ OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SPCR2, 0x0000);
+
+ /* pin control register */
+ OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, PCR0,(CLKXM | CLKXP | CLKRP));
+
+ /* configure srg to send 12MHz pulse from dsp peripheral clock */
+ OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SRGR1, 0x0000);
+ OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SRGR2, CLKSM);
+
+ /* enable sample rate generator */
+ w = OMAP_MCBSP_READ(OMAP1610_MCBSP3_BASE, SPCR2);
+ OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SPCR2, (w | FREE | GRST));
+ printk("Clock enabled to MCBSP1 & 3 \n");
+
+ return 0;
+}
+
+static int aic23_detect_client(struct i2c_adapter *adapter, int address,
+ int kind)
+{
+ int err = 0;
+ const char *client_name = "TLV320AIC23 Audio Codec";
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_WRITE_BYTE)) {
+ printk(KERN_WARNING "%s functionality check failed\n",
+ client_name);
+ return err;
+ }
+
+ if (!(new_client = kmalloc(sizeof(struct i2c_client),
+ GFP_KERNEL))) {
+ err = -ENOMEM;
+ printk(KERN_WARNING "Couldn't allocate memory for %s\n",
+ client_name);
+ return err;
+ }
+
+ memset(new_client, 0x00, sizeof(struct i2c_client));
+ new_client->addr = address;
+ new_client->adapter = adapter;
+ new_client->driver = &aic23_driver;
+ new_client->flags = 0;
+ strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
+
+ if ((err = i2c_attach_client(new_client))) {
+ printk(KERN_WARNING "Couldn't attach %s\n", client_name);
+ kfree(new_client);
+ return err;
+ }
+
+ if (platform_device_register(&audio_i2c_device)) {
+ printk(KERN_WARNING "Failed to register audio i2c device\n");
+ selftest = -ENODEV;
+ return selftest;
+ }
+ /* FIXME: Do in board-specific file */
+ omap_mcbsp3_aic23_clock_init();
+
+ if (!aic23_info_l.power_down)
+ aic23_power_up();
+ aic23_info_l.initialized = 1;
+
+ return 0;
+}
+
+static int aic23_detach_client(struct i2c_client *client)
+{
+ int err;
+
+ platform_device_unregister(&audio_i2c_device);
+
+ if ((err = i2c_detach_client(client))) {
+ printk("aic23.o: Client deregistration failed, \
+ client not detached.\n");
+ return err;
+ }
+ kfree(client);
+ return 0;
+}
+
+static int aic23_attach_adapter(struct i2c_adapter *adapter)
+{
+ int res;
+
+ res = i2c_probe(adapter, &addr_data, &aic23_detect_client);
+ return res;
+}
+
+static struct i2c_driver aic23_driver = {
+ .driver = {
+ .name = "OMAP+TLV320AIC23 codec",
+ /*.flags = I2C_DF_NOTIFY,*/
+ },
+ .id = I2C_DRIVERID_MISC, /* Experimental ID */
+ .attach_adapter = aic23_attach_adapter,
+ .detach_client = aic23_detach_client,
+};
+
+static void update_volume_left(int volume)
+{
+ u16 val = 0;
+ val = ((volume * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN;
+ aic23_write_value(LEFT_CHANNEL_VOLUME_ADDR, val);
+ aic23_info_l.volume_reg_left = volume;
+}
+
+static void update_volume_right(int volume)
+{
+ u16 val = 0;
+ val = ((volume * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN;
+ aic23_write_value(RIGHT_CHANNEL_VOLUME_ADDR, val);
+ aic23_info_l.volume_reg_right = volume;
+}
+
+static void set_mic(int mic_en)
+{
+ u16 dg_ctrl;
+
+ if (mic_en) {
+ aic23_info_l.power = OSC_OFF | LINE_OFF;
+ dg_ctrl = ADCHP_ON;
+ aic23_info_l.mask &= ~MICM_MUTED;
+ aic23_info_l.mask |= MICB_20DB; /* STE_ENABLED */
+ } else {
+ aic23_info_l.power =
+ OSC_OFF | ADC_OFF | MIC_OFF | LINE_OFF;
+ dg_ctrl = 0x00;
+ aic23_info_l.mask =
+ DAC_SELECTED | INSEL_MIC | MICM_MUTED;
+ }
+ aic23_write_value(POWER_DOWN_CONTROL_ADDR,
+ aic23_info_l.power);
+ aic23_write_value(DIGITAL_AUDIO_CONTROL_ADDR, dg_ctrl);
+ aic23_write_value(ANALOG_AUDIO_CONTROL_ADDR,
+ aic23_info_l.mask);
+ aic23_info_l.mic_enable = mic_en;
+
+ printk(KERN_INFO "aic23 mic state: %i\n", mic_en);
+}
+
+static void aic23_init_power(void)
+{
+ aic23_write_value(RESET_CONTROL_ADDR, 0x00);
+
+ if (aic23_info_l.initialized == 0) {
+ aic23_write_value(LEFT_CHANNEL_VOLUME_ADDR, LHV_MIN);
+ aic23_write_value(RIGHT_CHANNEL_VOLUME_ADDR, LHV_MIN);
+ }
+ else {
+ update_volume_left(aic23_info_l.volume_reg_left);
+ update_volume_right(aic23_info_l.volume_reg_right);
+ }
+
+ aic23_info_l.mask = DAC_SELECTED | INSEL_MIC | MICM_MUTED;
+ aic23_write_value(ANALOG_AUDIO_CONTROL_ADDR,
+ aic23_info_l.mask);
+ aic23_write_value(DIGITAL_AUDIO_CONTROL_ADDR, 0x00);
+ aic23_write_value(DIGITAL_AUDIO_FORMAT_ADDR, LRP_ON | FOR_DSP);
+ aic23_write_value(SAMPLE_RATE_CONTROL_ADDR, USB_CLK_ON);
+ aic23_write_value(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON);
+ aic23_info_l.power = OSC_OFF | ADC_OFF | MIC_OFF | LINE_OFF;
+ aic23_write_value(POWER_DOWN_CONTROL_ADDR,
+ aic23_info_l.power);
+
+ /* enable mic input */
+ if (aic23_info_l.mic_enable)
+ set_mic(aic23_info_l.mic_enable);
+
+ printk(KERN_INFO "aic23_init_power() done\n");
+}
+
+void aic23_power_down(void)
+{
+ if (aic23_info_l.initialized) {
+ printk("aic23 powering down\n");
+ aic23_write_value(POWER_DOWN_CONTROL_ADDR, 0xff);
+ }
+ aic23_info_l.power_down = 1;
+}
+
+void aic23_power_up(void)
+{
+ if (aic23_info_l.initialized) {
+ printk("aic23 powering up\n");
+ aic23_init_power();
+ }
+ aic23_info_l.power_down = 0;
+}
+
+/*----------------------------------------------------------------------*/
+/* sysfs initializations */
+/*----------------------------------------------------------------------*/
+
+static ssize_t store_volume_left(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ signed volume;
+
+ sscanf(buf, "%i", &volume);
+
+ if (volume < MIN_VOL) {
+ aic23_power_down();
+ return count;
+ } else if (volume > MIN_VOL && aic23_info_l.power_down) {
+ aic23_info_l.volume_reg_left = volume;
+ aic23_power_up();
+ return count;
+ }
+ if (volume > MAX_VOL)
+ volume = MAX_VOL;
+
+ update_volume_left(volume);
+ return count;
+}
+
+static ssize_t show_volume_left(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", aic23_info_l.volume_reg_left);
+}
+
+static DEVICE_ATTR(volume_left, S_IRUGO | S_IWUGO,
+ show_volume_left, store_volume_left);
+
+static ssize_t store_volume_right(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ signed volume;
+
+ sscanf(buf, "%i", &volume);
+ if (volume < MIN_VOL) {
+ aic23_power_down();
+ return count;
+ } else if (volume > MIN_VOL && aic23_info_l.power_down) {
+ aic23_info_l.volume_reg_right = volume;
+ aic23_power_up();
+ return count;
+ }
+ if (volume > MAX_VOL)
+ volume = MAX_VOL;
+
+ update_volume_right(volume);
+ return count;
+}
+
+static ssize_t show_volume_right(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", aic23_info_l.volume_reg_right);
+}
+
+static DEVICE_ATTR(volume_right, S_IRUGO | S_IWUGO,
+ show_volume_right, store_volume_right);
+
+static ssize_t store_gain_left(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u16 val = 0;
+ unsigned gain;
+
+ sscanf(buf, "%u", &gain);
+ if (gain > MAX_VOL)
+ gain = MAX_VOL;
+
+ val = ((gain * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;
+ aic23_write_value(LEFT_LINE_VOLUME_ADDR, val);
+ aic23_info_l.input_gain_reg_left = gain;
+
+ return count;
+}
+
+static ssize_t show_gain_left(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", aic23_info_l.input_gain_reg_left);
+}
+
+static DEVICE_ATTR(gain_left, S_IRUGO | S_IWUSR, show_gain_left,
+ store_gain_left);
+
+static ssize_t store_gain_right(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u16 val = 0;
+ unsigned gain;
+
+ sscanf(buf, "%u", &gain);
+ if (gain > MAX_VOL)
+ gain = MAX_VOL;
+
+ val = ((gain * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;
+ aic23_write_value(RIGHT_LINE_VOLUME_ADDR, val);
+ aic23_info_l.input_gain_reg_right = gain;
+
+ return count;
+}
+
+static ssize_t show_gain_right(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", aic23_info_l.input_gain_reg_right);
+}
+
+static DEVICE_ATTR(gain_right, S_IRUGO | S_IWUSR, show_gain_right,
+ store_gain_right);
+
+static ssize_t store_mic_loopback(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int mic;
+
+ sscanf(buf, "%i", &mic);
+ if (mic > 0) {
+ aic23_write_value(POWER_DOWN_CONTROL_ADDR, \
+ OSC_OFF | ADC_OFF | LINE_OFF);
+ aic23_info_l.mask = STE_ENABLED | DAC_SELECTED \
+ | INSEL_MIC | MICB_20DB;
+ aic23_write_value(ANALOG_AUDIO_CONTROL_ADDR,
+ aic23_info_l.mask);
+ mic = 1;
+ }
+ else {
+ aic23_write_value(POWER_DOWN_CONTROL_ADDR, \
+ OSC_OFF | ADC_OFF | MIC_OFF | LINE_OFF);
+ mic = 0;
+ }
+ aic23_info_l.mic_loopback = mic;
+
+ return count;
+}
+
+static ssize_t show_mic_loopback(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%i\n", aic23_info_l.mic_loopback);
+}
+
+static DEVICE_ATTR(mic_loopback, S_IRUGO | S_IWUSR,
+ show_mic_loopback, store_mic_loopback);
+
+static ssize_t store_st_attenuation(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned sta;
+ u16 tmp;
+
+ sscanf(buf, "%u", &sta);
+ if (sta > 3)
+ sta = 3;
+
+ tmp = aic23_info_l.mask;
+ tmp &= 0x3f;
+
+ aic23_info_l.mask = tmp | STA_REG(sta);
+ aic23_write_value(ANALOG_AUDIO_CONTROL_ADDR,
+ aic23_info_l.mask);
+ aic23_info_l.sta = sta;
+
+ return count;
+}
+
+static ssize_t show_st_attenuation(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%i\n", aic23_info_l.sta);
+}
+
+static DEVICE_ATTR(st_attenuation, S_IRUGO | S_IWUSR,
+ show_st_attenuation, store_st_attenuation);
+
+static ssize_t store_mic_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int mic;
+
+ sscanf(buf, "%i", &mic);
+ set_mic(mic);
+
+ return count;
+}
+
+static ssize_t show_mic_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%i\n", aic23_info_l.mic_enable);
+}
+
+static DEVICE_ATTR(mic_enable, S_IRUGO | S_IWUSR,
+ show_mic_enable, store_mic_enable);
+
+static ssize_t show_audio_selftest(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%i\n", selftest);
+}
+
+static DEVICE_ATTR(audio_selftest, S_IRUGO | S_IWUSR,
+ show_audio_selftest, NULL);
+
+static int audio_i2c_probe(struct platform_device *dev)
+{
+ int r;
+
+ if ((r = device_create_file(&dev->dev, &dev_attr_volume_left)) != 0)
+ return r;
+ else if ((r = device_create_file(&dev->dev,
+ &dev_attr_volume_right)) != 0)
+ goto err_volume_left;
+ else if ((r = device_create_file(&dev->dev,
+ &dev_attr_gain_right)) != 0)
+ goto err_volume_right;
+ else if ((r = device_create_file(&dev->dev,
+ &dev_attr_gain_left)) != 0)
+ goto err_gain_right;
+ else if ((r = device_create_file(&dev->dev,
+ &dev_attr_mic_loopback)) != 0)
+ goto err_gain_left;
+ else if ((r = device_create_file(&dev->dev,
+ &dev_attr_mic_enable)) != 0)
+ goto err_mic_loopback;
+ else if ((r = device_create_file(&dev->dev,
+ &dev_attr_st_attenuation)) != 0)
+ goto err_mic_enable;
+ else if ((r = device_create_file(&dev->dev,
+ &dev_attr_audio_selftest)) != 0)
+ goto err_st_attenuation;
+ else
+ return r;
+
+err_st_attenuation:
+ device_remove_file(&dev->dev, &dev_attr_st_attenuation);
+err_mic_enable:
+ device_remove_file(&dev->dev, &dev_attr_mic_enable);
+err_mic_loopback:
+ device_remove_file(&dev->dev, &dev_attr_mic_loopback);
+err_gain_left:
+ device_remove_file(&dev->dev, &dev_attr_gain_left);
+err_gain_right:
+ device_remove_file(&dev->dev, &dev_attr_gain_right);
+err_volume_right:
+ device_remove_file(&dev->dev, &dev_attr_volume_right);
+err_volume_left:
+ device_remove_file(&dev->dev, &dev_attr_volume_left);
+
+ return r;
+}
+
+static int audio_i2c_remove(struct platform_device *dev)
+{
+ device_remove_file(&dev->dev, &dev_attr_st_attenuation);
+ device_remove_file(&dev->dev, &dev_attr_mic_enable);
+ device_remove_file(&dev->dev, &dev_attr_mic_loopback);
+ device_remove_file(&dev->dev, &dev_attr_gain_left);
+ device_remove_file(&dev->dev, &dev_attr_gain_right);
+ device_remove_file(&dev->dev, &dev_attr_volume_right);
+ device_remove_file(&dev->dev, &dev_attr_volume_left);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+/* PM functions */
+/*----------------------------------------------------------------*/
+
+static void audio_i2c_shutdown(struct platform_device *dev)
+{
+ /* Let's mute the codec before powering off to prevent
+ * glitch in the sound
+ */
+ aic23_write_value(LEFT_CHANNEL_VOLUME_ADDR, LHV_MIN);
+ aic23_write_value(RIGHT_CHANNEL_VOLUME_ADDR, LHV_MIN);
+ aic23_power_down();
+}
+
+static int audio_i2c_suspend(struct platform_device *dev, pm_message_t state)
+{
+ /* Let's mute the codec before powering off to prevent
+ * glitch in the sound
+ */
+ aic23_write_value(LEFT_CHANNEL_VOLUME_ADDR, LHV_MIN);
+ aic23_write_value(RIGHT_CHANNEL_VOLUME_ADDR, LHV_MIN);
+ aic23_power_down();
+
+ return 0;
+}
+
+static int audio_i2c_resume(struct platform_device *dev)
+{
+ aic23_power_up();
+
+ return 0;
+}
+
+static struct platform_driver audio_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "audio-i2c",
+ },
+ .shutdown = audio_i2c_shutdown,
+ .probe = audio_i2c_probe,
+ .remove = audio_i2c_remove,
+ .suspend = audio_i2c_suspend,
+ .resume = audio_i2c_resume,
+};
+
+static struct platform_device audio_i2c_device = {
+ .name = "audio-i2c",
+ .id = -1,
+};
+
+/*----------------------------------------------------------------*/
+
+static int __init aic23_init(void)
+{
+ selftest = 0;
+ aic23_info_l.initialized = 0;
+
+ if (i2c_add_driver(&aic23_driver)) {
+ printk("aic23 i2c: Driver registration failed, \
+ module not inserted.\n");
+ selftest = -ENODEV;
+ return selftest;
+ }
+
+ if (platform_driver_register(&audio_i2c_driver)) {
+ printk(KERN_WARNING "Failed to register audio i2c driver\n");
+ selftest = -ENODEV;
+ return selftest;
+ }
+
+ printk("TLV320AIC23 I2C version %s (%s)\n",
+ TLV320AIC23_VERSION, TLV320AIC23_DATE);
+
+ return selftest;
+}
+
+static void __exit aic23_exit(void)
+{
+ aic23_power_down();
+ i2c_del_driver(&aic23_driver);
+
+ platform_driver_unregister(&audio_i2c_driver);
+}
+
+MODULE_AUTHOR("Kai Svahn <kai.svahn@nokia.com>");
+MODULE_DESCRIPTION("I2C interface for TLV320AIC23 codec.");
+MODULE_LICENSE("GPL");
+
+module_init(aic23_init)
+module_exit(aic23_exit)
+
+EXPORT_SYMBOL(aic23_write_value);
+EXPORT_SYMBOL(aic23_power_up);
+EXPORT_SYMBOL(aic23_power_down);
--- /dev/null
- #include <asm/arch/board.h>
+/*
+ * drivers/i2c/chips/tsl2563.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Written by Timo O. Karjalainen <timo.o.karjalainen@nokia.com>
+ * Contact: Mathias Nyman <mathias.nyman@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
++#include <mach/board.h>
+
+#define DRIVER_NAME "tsl2563"
+
+/* Use this many bits for fraction part. */
+#define ADC_FRAC_BITS (14)
+
+/* Given number of 1/10000's in ADC_FRAC_BITS precision. */
+#define FRAC10K(f) (((f) * (1L << (ADC_FRAC_BITS))) / (10000))
+
+/* Bits used for fraction in calibration coefficients.*/
+#define CALIB_FRAC_BITS (10)
+/* 0.5 in CALIB_FRAC_BITS precision */
+#define CALIB_FRAC_HALF (1 << (CALIB_FRAC_BITS - 1))
+/* Make a fraction from a number n that was multiplied with b. */
+#define CALIB_FRAC(n, b) (((n) << CALIB_FRAC_BITS) / (b))
+/* Decimal 10^(digits in sysfs presentation) */
+#define CALIB_BASE_SYSFS (1000)
+
+#define TSL2563_CMD (0x80)
+#define TSL2563_CLEARINT (0x40)
+
+#define TSL2563_REG_CTRL (0x00)
+#define TSL2563_REG_TIMING (0x01)
+#define TSL2563_REG_LOWLOW (0x02) /* data0 low threshold, 2 bytes */
+#define TSL2563_REG_LOWHIGH (0x03)
+#define TSL2563_REG_HIGHLOW (0x04) /* data0 high threshold, 2 bytes */
+#define TSL2563_REG_HIGHHIGH (0x05)
+#define TSL2563_REG_INT (0x06)
+#define TSL2563_REG_ID (0x0a)
+#define TSL2563_REG_DATA0LOW (0x0c) /* broadband sensor value, 2 bytes */
+#define TSL2563_REG_DATA0HIGH (0x0d)
+#define TSL2563_REG_DATA1LOW (0x0e) /* infrared sensor value, 2 bytes */
+#define TSL2563_REG_DATA1HIGH (0x0f)
+
+#define TSL2563_CMD_POWER_ON (0x03)
+#define TSL2563_CMD_POWER_OFF (0x00)
+#define TSL2563_CTRL_POWER_MASK (0x03)
+
+#define TSL2563_TIMING_13MS (0x00)
+#define TSL2563_TIMING_100MS (0x01)
+#define TSL2563_TIMING_400MS (0x02)
+#define TSL2563_TIMING_MASK (0x03)
+#define TSL2563_TIMING_GAIN16 (0x10)
+#define TSL2563_TIMING_GAIN1 (0x00)
+
+#define TSL2563_INT_DISBLED (0x00)
+#define TSL2563_INT_LEVEL (0x10)
+#define TSL2563_INT_PERSIST(n) ((n) & 0x0F)
+
+struct tsl2563_gainlevel_coeff {
+ u8 gaintime;
+ u16 min;
+ u16 max;
+};
+
+static struct tsl2563_gainlevel_coeff tsl2563_gainlevel_table[] = {
+ {
+ .gaintime = TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN16,
+ .min = 0,
+ .max = 65534,
+ }, {
+ .gaintime = TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN1,
+ .min = 2048,
+ .max = 65534,
+ }, {
+ .gaintime = TSL2563_TIMING_100MS | TSL2563_TIMING_GAIN1,
+ .min = 4095,
+ .max = 37177,
+ }, {
+ .gaintime = TSL2563_TIMING_13MS | TSL2563_TIMING_GAIN1,
+ .min = 3000,
+ .max = 65535,
+ },
+};
+
+struct tsl2563_chip {
+ struct mutex lock;
+ struct i2c_client *client;
+ struct device *hwmon_dev;
+
+ /* Remember state for suspend and resume functions */
+ pm_message_t state;
+
+ struct tsl2563_gainlevel_coeff *gainlevel;
+
+ /* Thresholds are in lux */
+ u16 low_thres;
+ u16 high_thres;
+ u8 intr;
+
+ /* Calibration coefficients */
+ u32 calib0;
+ u32 calib1;
+
+ /* Cache current values, to be returned while suspended */
+ u32 data0;
+ u32 data1;
+};
+
+static int tsl2563_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ int ret;
+ u8 buf[2];
+
+ buf[0] = TSL2563_CMD | reg;
+ buf[1] = value;
+
+ ret = i2c_master_send(client, buf, sizeof(buf));
+ return (ret == sizeof(buf)) ? 0 : ret;
+}
+
+static int tsl2563_read(struct i2c_client *client, u8 reg, void *buf, int len)
+{
+ int ret;
+ u8 cmd = TSL2563_CMD | reg;
+
+ ret = i2c_master_send(client, &cmd, sizeof(cmd));
+ if (ret != sizeof(cmd))
+ return ret;
+
+ return i2c_master_recv(client, buf, len);
+}
+
+static int tsl2563_set_power(struct tsl2563_chip *chip, int on)
+{
+ struct i2c_client *client = chip->client;
+ u8 cmd;
+
+ cmd = on ? TSL2563_CMD_POWER_ON : TSL2563_CMD_POWER_OFF;
+ return tsl2563_write(client, TSL2563_REG_CTRL, cmd);
+}
+
+/*
+ * Return value is 0 for off, 1 for on, or a negative error
+ * code if reading failed.
+ */
+static int tsl2563_get_power(struct tsl2563_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ int ret;
+ u8 val;
+
+ ret = tsl2563_read(client, TSL2563_REG_CTRL, &val, sizeof(val));
+ if (ret != sizeof(val))
+ return ret;
+
+ return (val & TSL2563_CTRL_POWER_MASK) == TSL2563_CMD_POWER_ON;
+}
+
+static int tsl2563_configure(struct tsl2563_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ int ret;
+
+ ret = tsl2563_write(client, TSL2563_REG_TIMING,
+ chip->gainlevel->gaintime);
+ if (ret)
+ goto out;
+
+ ret = tsl2563_write(client, TSL2563_REG_INT, chip->intr);
+
+out:
+ return ret;
+}
+
+static int tsl2563_detect(struct tsl2563_chip *chip)
+{
+ int ret;
+
+ ret = tsl2563_set_power(chip, 1);
+ if (ret)
+ return ret;
+
+ ret = tsl2563_get_power(chip);
+ if (ret < 0)
+ return ret;
+
+ return ret ? 0 : -ENODEV;
+}
+
+static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id)
+{
+ struct i2c_client *client = chip->client;
+ int ret;
+
+ ret = tsl2563_read(client, TSL2563_REG_ID, id, sizeof(*id));
+ if (ret != sizeof(*id))
+ return ret;
+
+ return 0;
+}
+
+/*
+ * "Normalized" ADC value is one obtained with 400ms of integration time and
+ * 16x gain. This function returns the number of bits of shift needed to
+ * convert between normalized values and HW values obtained using given
+ * timing and gain settings.
+ */
+static int adc_shiftbits(u8 timing)
+{
+ int shift = 0;
+
+ switch (timing & TSL2563_TIMING_MASK) {
+ case TSL2563_TIMING_13MS:
+ shift += 5;
+ break;
+ case TSL2563_TIMING_100MS:
+ shift += 2;
+ break;
+ case TSL2563_TIMING_400MS:
+ /* no-op */
+ break;
+ }
+
+ if (!(timing & TSL2563_TIMING_GAIN16))
+ shift += 4;
+
+ return shift;
+}
+
+/* Convert a HW ADC value to normalized scale. */
+static u32 normalize_adc(u16 adc, u8 timing)
+{
+ return adc << adc_shiftbits(timing);
+}
+
+static void tsl2563_wait_adc(struct tsl2563_chip *chip)
+{
+ unsigned int delay;
+
+ switch (chip->gainlevel->gaintime & TSL2563_TIMING_MASK) {
+ case TSL2563_TIMING_13MS:
+ delay = 14;
+ break;
+ case TSL2563_TIMING_100MS:
+ delay = 101;
+ break;
+ default:
+ delay = 402;
+ }
+ /*
+ * TODO: Make sure that we wait at least required delay but why we
+ * have to extend it one tick more?
+ */
+ schedule_timeout_interruptible(msecs_to_jiffies(delay) + 2);
+}
+
+static int tsl2563_adjust_gainlevel(struct tsl2563_chip *chip, u16 adc)
+{
+ struct i2c_client *client = chip->client;
+
+ if (adc > chip->gainlevel->max || adc < chip->gainlevel->min) {
+
+ (adc > chip->gainlevel->max) ?
+ chip->gainlevel++ : chip->gainlevel--;
+
+ tsl2563_write(client, TSL2563_REG_TIMING,
+ chip->gainlevel->gaintime);
+
+ tsl2563_wait_adc(chip);
+ tsl2563_wait_adc(chip);
+
+ return 1;
+ } else
+ return 0;
+}
+
+static int tsl2563_get_adc(struct tsl2563_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ u8 buf0[2], buf1[2];
+ u16 adc0, adc1;
+ int retry = 1;
+ int ret = 0;
+
+ if (chip->state.event != PM_EVENT_ON)
+ goto out;
+
+ while (retry) {
+ ret = tsl2563_read(client,
+ TSL2563_REG_DATA0LOW | TSL2563_CLEARINT,
+ buf0, sizeof(buf0));
+ if (ret != sizeof(buf0))
+ goto out;
+
+ ret = tsl2563_read(client, TSL2563_REG_DATA1LOW,
+ buf1, sizeof(buf1));
+ if (ret != sizeof(buf1))
+ goto out;
+
+ adc0 = (buf0[1] << 8) + buf0[0];
+ adc1 = (buf1[1] << 8) + buf1[0];
+
+ retry = tsl2563_adjust_gainlevel(chip, adc0);
+ }
+
+ chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime);
+ chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime);
+
+ ret = 0;
+out:
+ return ret;
+}
+
+static inline int calib_to_sysfs(u32 calib)
+{
+ return (int) (((calib * CALIB_BASE_SYSFS) +
+ CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
+}
+
+static inline u32 calib_from_sysfs(int value)
+{
+ return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
+}
+
+/*
+ * Conversions between lux and ADC values.
+ *
+ * The basic formula is lux = c0 * adc0 - c1 * adc1, where c0 and c1 are
+ * appropriate constants. Different constants are needed for different
+ * kinds of light, determined by the ratio adc1/adc0 (basically the ratio
+ * of the intensities in infrared and visible wavelengths). lux_table below
+ * lists the upper threshold of the adc1/adc0 ratio and the corresponding
+ * constants.
+ */
+
+struct tsl2563_lux_coeff {
+ unsigned long ch_ratio;
+ unsigned long ch0_coeff;
+ unsigned long ch1_coeff;
+};
+
+static const struct tsl2563_lux_coeff lux_table[] = {
+ {
+ .ch_ratio = FRAC10K(1300),
+ .ch0_coeff = FRAC10K(315),
+ .ch1_coeff = FRAC10K(262),
+ }, {
+ .ch_ratio = FRAC10K(2600),
+ .ch0_coeff = FRAC10K(337),
+ .ch1_coeff = FRAC10K(430),
+ }, {
+ .ch_ratio = FRAC10K(3900),
+ .ch0_coeff = FRAC10K(363),
+ .ch1_coeff = FRAC10K(529),
+ }, {
+ .ch_ratio = FRAC10K(5200),
+ .ch0_coeff = FRAC10K(392),
+ .ch1_coeff = FRAC10K(605),
+ }, {
+ .ch_ratio = FRAC10K(6500),
+ .ch0_coeff = FRAC10K(229),
+ .ch1_coeff = FRAC10K(291),
+ }, {
+ .ch_ratio = FRAC10K(8000),
+ .ch0_coeff = FRAC10K(157),
+ .ch1_coeff = FRAC10K(180),
+ }, {
+ .ch_ratio = FRAC10K(13000),
+ .ch0_coeff = FRAC10K(34),
+ .ch1_coeff = FRAC10K(26),
+ }, {
+ .ch_ratio = ULONG_MAX,
+ .ch0_coeff = 0,
+ .ch1_coeff = 0,
+ },
+};
+
+/*
+ * Convert normalized, scaled ADC values to lux.
+ */
+static unsigned int adc_to_lux(u32 adc0, u32 adc1)
+{
+ const struct tsl2563_lux_coeff *lp = lux_table;
+ unsigned long ratio, lux, ch0 = adc0, ch1 = adc1;
+
+ ratio = ch0 ? ((ch1 << ADC_FRAC_BITS) / ch0) : ULONG_MAX;
+
+ while (lp->ch_ratio < ratio)
+ lp++;
+
+ lux = ch0 * lp->ch0_coeff - ch1 * lp->ch1_coeff;
+
+ return (unsigned int) (lux >> ADC_FRAC_BITS);
+}
+
+/*--------------------------------------------------------------*/
+/* Sysfs interface */
+/*--------------------------------------------------------------*/
+
+static ssize_t tsl2563_adc0_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2563_chip *chip = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ ret = tsl2563_get_adc(chip);
+ if (ret)
+ return ret;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", chip->data0);
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static ssize_t tsl2563_adc1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2563_chip *chip = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ ret = tsl2563_get_adc(chip);
+ if (ret)
+ return ret;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", chip->data1);
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+/* Apply calibration coefficient to ADC count. */
+static u32 calib_adc(u32 adc, u32 calib)
+{
+ unsigned long scaled = adc;
+
+ scaled *= calib;
+ scaled >>= CALIB_FRAC_BITS;
+
+ return (u32) scaled;
+}
+
+static ssize_t tsl2563_lux_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2563_chip *chip = dev_get_drvdata(dev);
+ u32 calib0, calib1;
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ ret = tsl2563_get_adc(chip);
+ if (ret)
+ goto out;
+
+ calib0 = calib_adc(chip->data0, chip->calib0);
+ calib1 = calib_adc(chip->data1, chip->calib1);
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", adc_to_lux(calib0, calib1));
+
+out:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static ssize_t format_calib(char *buf, int len, u32 calib)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", calib_to_sysfs(calib));
+}
+
+static ssize_t tsl2563_calib0_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2563_chip *chip = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&chip->lock);
+ ret = format_calib(buf, PAGE_SIZE, chip->calib0);
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static ssize_t tsl2563_calib1_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2563_chip *chip = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&chip->lock);
+ ret = format_calib(buf, PAGE_SIZE, chip->calib1);
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static int do_calib_store(struct device *dev, const char *buf, size_t len,
+ int ch)
+{
+ struct tsl2563_chip *chip = dev_get_drvdata(dev);
+ int value;
+ u32 calib;
+
+ if (1 != sscanf(buf, "%d", &value))
+ return -EINVAL;
+
+ calib = calib_from_sysfs(value);
+
+ if (ch)
+ chip->calib1 = calib;
+ else
+ chip->calib0 = calib;
+
+ return len;
+}
+
+static ssize_t tsl2563_calib0_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return do_calib_store(dev, buf, len, 0);
+}
+
+static ssize_t tsl2563_calib1_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return do_calib_store(dev, buf, len, 1);
+}
+
+static DEVICE_ATTR(adc0, S_IRUGO, tsl2563_adc0_show, NULL);
+static DEVICE_ATTR(adc1, S_IRUGO, tsl2563_adc1_show, NULL);
+static DEVICE_ATTR(lux, S_IRUGO, tsl2563_lux_show, NULL);
+static DEVICE_ATTR(calib0, S_IRUGO | S_IWUSR,
+ tsl2563_calib0_show, tsl2563_calib0_store);
+static DEVICE_ATTR(calib1, S_IRUGO | S_IWUSR,
+ tsl2563_calib1_show, tsl2563_calib1_store);
+
+static struct attribute *tsl2563_attributes[] = {
+ &dev_attr_adc0.attr,
+ &dev_attr_adc1.attr,
+ &dev_attr_lux.attr,
+ &dev_attr_calib0.attr,
+ &dev_attr_calib1.attr,
+ NULL
+};
+
+static const struct attribute_group tsl2563_group = {
+ .attrs = tsl2563_attributes,
+};
+
+static int tsl2563_register_sysfs(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+
+ return sysfs_create_group(&dev->kobj, &tsl2563_group);
+}
+
+static void tsl2563_unregister_sysfs(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+
+ sysfs_remove_group(&dev->kobj, &tsl2563_group);
+}
+
+/*--------------------------------------------------------------*/
+/* Probe, Attach, Remove */
+/*--------------------------------------------------------------*/
+static struct i2c_driver tsl2563_i2c_driver;
+
+static int tsl2563_probe(struct i2c_client *client,
+ const struct i2c_device_id *device_id)
+{
+ struct tsl2563_chip *chip;
+ int err = 0;
+ u8 id;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, chip);
+ chip->client = client;
+
+ err = tsl2563_detect(chip);
+ if (err) {
+ dev_err(&client->dev, "device not found, error %d \n", -err);
+ goto fail1;
+ }
+
+ err = tsl2563_read_id(chip, &id);
+ if (err)
+ goto fail1;
+
+ mutex_init(&chip->lock);
+
+ /* Default values used until userspace says otherwise */
+ chip->low_thres = 0x0;
+ chip->high_thres = 0xffff;
+ chip->gainlevel = tsl2563_gainlevel_table;
+ chip->intr = TSL2563_INT_PERSIST(4);
+ chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS);
+ chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS);
+
+ dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f);
+
+ err = tsl2563_configure(chip);
+ if (err)
+ goto fail1;
+
+ chip->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(chip->hwmon_dev))
+ goto fail1;
+
+ err = tsl2563_register_sysfs(client);
+ if (err) {
+ dev_err(&client->dev, "sysfs registration failed, %d\n", err);
+ goto fail2;
+ }
+
+ return 0;
+fail2:
+ hwmon_device_unregister(chip->hwmon_dev);
+fail1:
+ kfree(chip);
+ return err;
+}
+
+static int tsl2563_remove(struct i2c_client *client)
+{
+ struct tsl2563_chip *chip = i2c_get_clientdata(client);
+
+ tsl2563_unregister_sysfs(client);
+ hwmon_device_unregister(chip->hwmon_dev);
+
+ kfree(chip);
+ return 0;
+}
+
+static int tsl2563_suspend(struct i2c_client *client, pm_message_t state)
+{
+ struct tsl2563_chip *chip = i2c_get_clientdata(client);
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ ret = tsl2563_set_power(chip, 0);
+ if (ret)
+ goto out;
+
+ chip->state = state;
+
+out:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static int tsl2563_resume(struct i2c_client *client)
+{
+ struct tsl2563_chip *chip = i2c_get_clientdata(client);
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ ret = tsl2563_set_power(chip, 1);
+ if (ret)
+ goto out;
+
+ ret = tsl2563_configure(chip);
+ if (ret)
+ goto out;
+
+ chip->state.event = PM_EVENT_ON;
+
+out:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static const struct i2c_device_id tsl2563_id[] = {
+ { DRIVER_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, tsl2563_id);
+
+static struct i2c_driver tsl2563_i2c_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .suspend = tsl2563_suspend,
+ .resume = tsl2563_resume,
+ .probe = tsl2563_probe,
+ .remove = __devexit_p(tsl2563_remove),
+ .id_table = tsl2563_id,
+};
+
+static int __init tsl2563_init(void)
+{
+ return i2c_add_driver(&tsl2563_i2c_driver);
+}
+
+static void __exit tsl2563_exit(void)
+{
+ i2c_del_driver(&tsl2563_i2c_driver);
+}
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("tsl2563 light sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(tsl2563_init);
+module_exit(tsl2563_exit);
--- /dev/null
- #include <asm/arch/gpio.h>
- #include <asm/arch/mux.h>
+/*
+ * twl4030_core.c - driver for TWL4030 PM and audio CODEC device
+ *
+ * Copyright (C) 2005-2006 Texas Instruments, Inc.
+ *
+ * Modifications to defer interrupt handling to a kernel thread:
+ * Copyright (C) 2006 MontaVista Software, Inc.
+ *
+ * Based on tlv320aic23.c:
+ * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
+ *
+ * Code cleanup and modifications to IRQ handler.
+ * by syed khasim <x0khasim@ti.com>
+ *
+ * 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_stat.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/random.h>
+#include <linux/syscalls.h>
+#include <linux/kthread.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl4030-gpio.h>
+#include <linux/i2c/twl4030-madc.h>
+#include <linux/i2c/twl4030-pwrirq.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+
+#include <asm/mach/irq.h>
+
++#include <mach/gpio.h>
++#include <mach/mux.h>
+
+#define DRIVER_NAME "twl4030"
+
+/* Macro Definitions */
+#define TWL_CLIENT_STRING "TWL4030-ID"
+#define TWL_CLIENT_USED 1
+#define TWL_CLIENT_FREE 0
+
+/* IRQ Flags */
+#define FREE 0
+#define USED 1
+
+/* Primary Interrupt Handler on TWL4030 Registers */
+
+/* Register Definitions */
+
+#define REG_PIH_ISR_P1 (0x1)
+#define REG_PIH_ISR_P2 (0x2)
+#define REG_PIH_SIR (0x3)
+
+/* Triton Core internal information (BEGIN) */
+
+/* Last - for index max*/
+#define TWL4030_MODULE_LAST TWL4030_MODULE_SECURED_REG
+
+/* Slave address */
+#define TWL4030_NUM_SLAVES 0x04
+#define TWL4030_SLAVENUM_NUM0 0x00
+#define TWL4030_SLAVENUM_NUM1 0x01
+#define TWL4030_SLAVENUM_NUM2 0x02
+#define TWL4030_SLAVENUM_NUM3 0x03
+#define TWL4030_SLAVEID_ID0 0x48
+#define TWL4030_SLAVEID_ID1 0x49
+#define TWL4030_SLAVEID_ID2 0x4A
+#define TWL4030_SLAVEID_ID3 0x4B
+
+/* Base Address defns */
+/* USB ID */
+#define TWL4030_BASEADD_USB 0x0000
+/* AUD ID */
+#define TWL4030_BASEADD_AUDIO_VOICE 0x0000
+#define TWL4030_BASEADD_GPIO 0x0098
+
+#define TWL4030_BASEADD_INTBR 0x0085
+#define TWL4030_BASEADD_PIH 0x0080
+#define TWL4030_BASEADD_TEST 0x004C
+/* AUX ID */
+#define TWL4030_BASEADD_INTERRUPTS 0x00B9
+#define TWL4030_BASEADD_LED 0x00EE
+#define TWL4030_BASEADD_MADC 0x0000
+#define TWL4030_BASEADD_MAIN_CHARGE 0x0074
+#define TWL4030_BASEADD_PRECHARGE 0x00AA
+#define TWL4030_BASEADD_PWM0 0x00F8
+#define TWL4030_BASEADD_PWM1 0x00FB
+#define TWL4030_BASEADD_PWMA 0x00EF
+#define TWL4030_BASEADD_PWMB 0x00F1
+#define TWL4030_BASEADD_KEYPAD 0x00D2
+/* POWER ID */
+#define TWL4030_BASEADD_BACKUP 0x0014
+#define TWL4030_BASEADD_INT 0x002E
+#define TWL4030_BASEADD_PM_MASTER 0x0036
+#define TWL4030_BASEADD_PM_RECEIVER 0x005B
+#define TWL4030_BASEADD_RTC 0x001C
+#define TWL4030_BASEADD_SECURED_REG 0x0000
+
+/* TWL4030 BCI registers */
+#define TWL4030_INTERRUPTS_BCIIMR1A 0x2
+#define TWL4030_INTERRUPTS_BCIIMR2A 0x3
+#define TWL4030_INTERRUPTS_BCIIMR1B 0x6
+#define TWL4030_INTERRUPTS_BCIIMR2B 0x7
+#define TWL4030_INTERRUPTS_BCIISR1A 0x0
+#define TWL4030_INTERRUPTS_BCIISR2A 0x1
+#define TWL4030_INTERRUPTS_BCIISR1B 0x4
+#define TWL4030_INTERRUPTS_BCIISR2B 0x5
+
+/* TWL4030 keypad registers */
+#define TWL4030_KEYPAD_KEYP_IMR1 0x12
+#define TWL4030_KEYPAD_KEYP_IMR2 0x14
+#define TWL4030_KEYPAD_KEYP_ISR1 0x11
+#define TWL4030_KEYPAD_KEYP_ISR2 0x13
+
+
+/* Triton Core internal information (END) */
+
+/* Few power values */
+#define R_CFG_BOOT 0x05
+#define R_PROTECT_KEY 0x0E
+
+/* access control */
+#define KEY_UNLOCK1 0xce
+#define KEY_UNLOCK2 0xec
+#define KEY_LOCK 0x00
+
+#define HFCLK_FREQ_19p2_MHZ (1 << 0)
+#define HFCLK_FREQ_26_MHZ (2 << 0)
+#define HFCLK_FREQ_38p4_MHZ (3 << 0)
+#define HIGH_PERF_SQ (1 << 3)
+
+/* on I2C-1 for 2430SDP */
+#define CONFIG_I2C_TWL4030_ID 1
+
+/* SIH_CTRL registers that aren't defined elsewhere */
+#define TWL4030_INTERRUPTS_BCISIHCTRL 0x0d
+#define TWL4030_MADC_MADC_SIH_CTRL 0x67
+#define TWL4030_KEYPAD_KEYP_SIH_CTRL 0x17
+
+#define TWL4030_SIH_CTRL_COR_MASK (1 << 2)
+
+/**
+ * struct twl4030_mod_iregs - TWL module IMR/ISR regs to mask/clear at init
+ * @mod_no: TWL4030 module number (e.g., TWL4030_MODULE_GPIO)
+ * @sih_ctrl: address of module SIH_CTRL register
+ * @reg_cnt: number of IMR/ISR regs
+ * @imrs: pointer to array of TWL module interrupt mask register indices
+ * @isrs: pointer to array of TWL module interrupt status register indices
+ *
+ * Ties together TWL4030 modules and lists of IMR/ISR registers to mask/clear
+ * during twl_init_irq().
+ */
+struct twl4030_mod_iregs {
+ const u8 mod_no;
+ const u8 sih_ctrl;
+ const u8 reg_cnt;
+ const u8 *imrs;
+ const u8 *isrs;
+};
+
+/* TWL4030 INT module interrupt mask registers */
+static const u8 __initconst twl4030_int_imr_regs[] = {
+ TWL4030_INT_PWR_IMR1,
+ TWL4030_INT_PWR_IMR2,
+};
+
+/* TWL4030 INT module interrupt status registers */
+static const u8 __initconst twl4030_int_isr_regs[] = {
+ TWL4030_INT_PWR_ISR1,
+ TWL4030_INT_PWR_ISR2,
+};
+
+/* TWL4030 INTERRUPTS module interrupt mask registers */
+static const u8 __initconst twl4030_interrupts_imr_regs[] = {
+ TWL4030_INTERRUPTS_BCIIMR1A,
+ TWL4030_INTERRUPTS_BCIIMR1B,
+ TWL4030_INTERRUPTS_BCIIMR2A,
+ TWL4030_INTERRUPTS_BCIIMR2B,
+};
+
+/* TWL4030 INTERRUPTS module interrupt status registers */
+static const u8 __initconst twl4030_interrupts_isr_regs[] = {
+ TWL4030_INTERRUPTS_BCIISR1A,
+ TWL4030_INTERRUPTS_BCIISR1B,
+ TWL4030_INTERRUPTS_BCIISR2A,
+ TWL4030_INTERRUPTS_BCIISR2B,
+};
+
+/* TWL4030 MADC module interrupt mask registers */
+static const u8 __initconst twl4030_madc_imr_regs[] = {
+ TWL4030_MADC_IMR1,
+ TWL4030_MADC_IMR2,
+};
+
+/* TWL4030 MADC module interrupt status registers */
+static const u8 __initconst twl4030_madc_isr_regs[] = {
+ TWL4030_MADC_ISR1,
+ TWL4030_MADC_ISR2,
+};
+
+/* TWL4030 keypad module interrupt mask registers */
+static const u8 __initconst twl4030_keypad_imr_regs[] = {
+ TWL4030_KEYPAD_KEYP_IMR1,
+ TWL4030_KEYPAD_KEYP_IMR2,
+};
+
+/* TWL4030 keypad module interrupt status registers */
+static const u8 __initconst twl4030_keypad_isr_regs[] = {
+ TWL4030_KEYPAD_KEYP_ISR1,
+ TWL4030_KEYPAD_KEYP_ISR2,
+};
+
+/* TWL4030 GPIO module interrupt mask registers */
+static const u8 __initconst twl4030_gpio_imr_regs[] = {
+ REG_GPIO_IMR1A,
+ REG_GPIO_IMR1B,
+ REG_GPIO_IMR2A,
+ REG_GPIO_IMR2B,
+ REG_GPIO_IMR3A,
+ REG_GPIO_IMR3B,
+};
+
+/* TWL4030 GPIO module interrupt status registers */
+static const u8 __initconst twl4030_gpio_isr_regs[] = {
+ REG_GPIO_ISR1A,
+ REG_GPIO_ISR1B,
+ REG_GPIO_ISR2A,
+ REG_GPIO_ISR2B,
+ REG_GPIO_ISR3A,
+ REG_GPIO_ISR3B,
+};
+
+/* TWL4030 modules that have IMR/ISR registers that must be masked/cleared */
+static const struct twl4030_mod_iregs __initconst twl4030_mod_regs[] = {
+ {
+ .mod_no = TWL4030_MODULE_INT,
+ .sih_ctrl = TWL4030_INT_PWR_SIH_CTRL,
+ .reg_cnt = ARRAY_SIZE(twl4030_int_imr_regs),
+ .imrs = twl4030_int_imr_regs,
+ .isrs = twl4030_int_isr_regs,
+ },
+ {
+ .mod_no = TWL4030_MODULE_INTERRUPTS,
+ .sih_ctrl = TWL4030_INTERRUPTS_BCISIHCTRL,
+ .reg_cnt = ARRAY_SIZE(twl4030_interrupts_imr_regs),
+ .imrs = twl4030_interrupts_imr_regs,
+ .isrs = twl4030_interrupts_isr_regs,
+ },
+ {
+ .mod_no = TWL4030_MODULE_MADC,
+ .sih_ctrl = TWL4030_MADC_MADC_SIH_CTRL,
+ .reg_cnt = ARRAY_SIZE(twl4030_madc_imr_regs),
+ .imrs = twl4030_madc_imr_regs,
+ .isrs = twl4030_madc_isr_regs,
+ },
+ {
+ .mod_no = TWL4030_MODULE_KEYPAD,
+ .sih_ctrl = TWL4030_KEYPAD_KEYP_SIH_CTRL,
+ .reg_cnt = ARRAY_SIZE(twl4030_keypad_imr_regs),
+ .imrs = twl4030_keypad_imr_regs,
+ .isrs = twl4030_keypad_isr_regs,
+ },
+ {
+ .mod_no = TWL4030_MODULE_GPIO,
+ .sih_ctrl = REG_GPIO_SIH_CTRL,
+ .reg_cnt = ARRAY_SIZE(twl4030_gpio_imr_regs),
+ .imrs = twl4030_gpio_imr_regs,
+ .isrs = twl4030_gpio_isr_regs,
+ },
+};
+
+
+/* Helper functions */
+static int
+twl4030_detect_client(struct i2c_adapter *adapter, unsigned char sid);
+static int twl4030_attach_adapter(struct i2c_adapter *adapter);
+static int twl4030_detach_client(struct i2c_client *client);
+static void do_twl4030_irq(unsigned int irq, irq_desc_t *desc);
+
+static void twl_init_irq(void);
+
+/* Data Structures */
+/* To have info on T2 IRQ substem activated or not */
+static unsigned char twl_irq_used = FREE;
+static struct completion irq_event;
+
+/* Structure to define on TWL4030 Slave ID */
+struct twl4030_client {
+ struct i2c_client client;
+ const char client_name[sizeof(TWL_CLIENT_STRING) + 1];
+ const unsigned char address;
+ const char adapter_index;
+ unsigned char inuse;
+
+ /* max numb of i2c_msg required is for read =2 */
+ struct i2c_msg xfer_msg[2];
+
+ /* To lock access to xfer_msg */
+ struct mutex xfer_lock;
+};
+
+/* Module Mapping */
+struct twl4030mapping {
+ unsigned char sid; /* Slave ID */
+ unsigned char base; /* base address */
+};
+
+/* mapping the module id to slave id and base address */
+static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
+ { TWL4030_SLAVENUM_NUM0, TWL4030_BASEADD_USB },
+ { TWL4030_SLAVENUM_NUM1, TWL4030_BASEADD_AUDIO_VOICE },
+ { TWL4030_SLAVENUM_NUM1, TWL4030_BASEADD_GPIO },
+ { TWL4030_SLAVENUM_NUM1, TWL4030_BASEADD_INTBR },
+ { TWL4030_SLAVENUM_NUM1, TWL4030_BASEADD_PIH },
+ { TWL4030_SLAVENUM_NUM1, TWL4030_BASEADD_TEST },
+ { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_KEYPAD },
+ { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_MADC },
+ { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_INTERRUPTS },
+ { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_LED },
+ { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_MAIN_CHARGE },
+ { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_PRECHARGE },
+ { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_PWM0 },
+ { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_PWM1 },
+ { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_PWMA },
+ { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_PWMB },
+ { TWL4030_SLAVENUM_NUM3, TWL4030_BASEADD_BACKUP },
+ { TWL4030_SLAVENUM_NUM3, TWL4030_BASEADD_INT },
+ { TWL4030_SLAVENUM_NUM3, TWL4030_BASEADD_PM_MASTER },
+ { TWL4030_SLAVENUM_NUM3, TWL4030_BASEADD_PM_RECEIVER },
+ { TWL4030_SLAVENUM_NUM3, TWL4030_BASEADD_RTC },
+ { TWL4030_SLAVENUM_NUM3, TWL4030_BASEADD_SECURED_REG },
+};
+
+static struct twl4030_client twl4030_modules[TWL4030_NUM_SLAVES] = {
+ {
+ .address = TWL4030_SLAVEID_ID0,
+ .client_name = TWL_CLIENT_STRING "0",
+ .adapter_index = CONFIG_I2C_TWL4030_ID,
+ },
+ {
+ .address = TWL4030_SLAVEID_ID1,
+ .client_name = TWL_CLIENT_STRING "1",
+ .adapter_index = CONFIG_I2C_TWL4030_ID,
+ },
+ {
+ .address = TWL4030_SLAVEID_ID2,
+ .client_name = TWL_CLIENT_STRING "2",
+ .adapter_index = CONFIG_I2C_TWL4030_ID,
+ },
+ {
+ .address = TWL4030_SLAVEID_ID3,
+ .client_name = TWL_CLIENT_STRING "3",
+ .adapter_index = CONFIG_I2C_TWL4030_ID,
+ },
+};
+
+/* One Client Driver , 4 Clients */
+static struct i2c_driver twl4030_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .attach_adapter = twl4030_attach_adapter,
+ .detach_client = twl4030_detach_client,
+};
+
+/*
+ * TWL4030 doesn't have PIH mask, hence dummy function for mask
+ * and unmask.
+ */
+
+static void twl4030_i2c_ackirq(unsigned int irq) {}
+static void twl4030_i2c_disableint(unsigned int irq) {}
+static void twl4030_i2c_enableint(unsigned int irq) {}
+
+/* information for processing in the Work Item */
+static struct irq_chip twl4030_irq_chip = {
+ .name = "twl4030",
+ .ack = twl4030_i2c_ackirq,
+ .mask = twl4030_i2c_disableint,
+ .unmask = twl4030_i2c_enableint,
+};
+
+/* Global Functions */
+
+/**
+ * twl4030_i2c_write - Writes a n bit register in TWL4030
+ * @mod_no: module number
+ * @value: an array of num_bytes+1 containing data to write
+ * @reg: register address (just offset will do)
+ * @num_bytes: number of bytes to transfer
+ *
+ * IMPORTANT: for 'value' parameter: Allocate value num_bytes+1 and
+ * valid data starts at Offset 1.
+ *
+ * Returns the result of operation - 0 is success
+ */
+int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, u8 num_bytes)
+{
+ int ret;
+ int sid;
+ struct twl4030_client *twl;
+ struct i2c_msg *msg;
+
+ if (unlikely(mod_no > TWL4030_MODULE_LAST)) {
+ pr_err("Invalid module Number\n");
+ return -EPERM;
+ }
+ sid = twl4030_map[mod_no].sid;
+ twl = &twl4030_modules[sid];
+
+ if (unlikely(twl->inuse != TWL_CLIENT_USED)) {
+ pr_err("I2C Client[%d] is not initialized[%d]\n",
+ sid, __LINE__);
+ return -EPERM;
+ }
+ mutex_lock(&twl->xfer_lock);
+ /*
+ * [MSG1]: fill the register address data
+ * fill the data Tx buffer
+ */
+ msg = &twl->xfer_msg[0];
+ msg->addr = twl->address;
+ msg->len = num_bytes + 1;
+ msg->flags = 0;
+ msg->buf = value;
+ /* over write the first byte of buffer with the register address */
+ *value = twl4030_map[mod_no].base + reg;
+ ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 1);
+ mutex_unlock(&twl->xfer_lock);
+
+ /* i2cTransfer returns num messages.translate it pls.. */
+ if (ret >= 0)
+ ret = 0;
+ return ret;
+}
+EXPORT_SYMBOL(twl4030_i2c_write);
+
+/**
+ * twl4030_i2c_read - Reads a n bit register in TWL4030
+ * @mod_no: module number
+ * @value: an array of num_bytes containing data to be read
+ * @reg: register address (just offset will do)
+ * @num_bytes: number of bytes to transfer
+ *
+ * Returns result of operation - num_bytes is success else failure.
+ */
+int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, u8 num_bytes)
+{
+ int ret;
+ u8 val;
+ int sid;
+ struct twl4030_client *twl;
+ struct i2c_msg *msg;
+
+ if (unlikely(mod_no > TWL4030_MODULE_LAST)) {
+ pr_err("Invalid module Number\n");
+ return -EPERM;
+ }
+ sid = twl4030_map[mod_no].sid;
+ twl = &twl4030_modules[sid];
+
+ if (unlikely(twl->inuse != TWL_CLIENT_USED)) {
+ pr_err("I2C Client[%d] is not initialized[%d]\n", sid,
+ __LINE__);
+ return -EPERM;
+ }
+ mutex_lock(&twl->xfer_lock);
+ /* [MSG1] fill the register address data */
+ msg = &twl->xfer_msg[0];
+ msg->addr = twl->address;
+ msg->len = 1;
+ msg->flags = 0; /* Read the register value */
+ val = twl4030_map[mod_no].base + reg;
+ msg->buf = &val;
+ /* [MSG2] fill the data rx buffer */
+ msg = &twl->xfer_msg[1];
+ msg->addr = twl->address;
+ msg->flags = I2C_M_RD; /* Read the register value */
+ msg->len = num_bytes; /* only n bytes */
+ msg->buf = value;
+ ret = i2c_transfer(twl->client.adapter, twl->xfer_msg, 2);
+ mutex_unlock(&twl->xfer_lock);
+
+ /* i2cTransfer returns num messages.translate it pls.. */
+ if (ret >= 0)
+ ret = 0;
+ return ret;
+}
+EXPORT_SYMBOL(twl4030_i2c_read);
+
+/**
+ * twl4030_i2c_write_u8 - Writes a 8 bit register in TWL4030
+ * @mod_no: module number
+ * @value: the value to be written 8 bit
+ * @reg: register address (just offset will do)
+ *
+ * Returns result of operation - 0 is success
+ */
+int twl4030_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
+{
+
+ /* 2 bytes offset 1 contains the data offset 0 is used by i2c_write */
+ u8 temp_buffer[2] = { 0 };
+ /* offset 1 contains the data */
+ temp_buffer[1] = value;
+ return twl4030_i2c_write(mod_no, temp_buffer, reg, 1);
+}
+EXPORT_SYMBOL(twl4030_i2c_write_u8);
+
+/**
+ * twl4030_i2c_read_u8 - Reads a 8 bit register from TWL4030
+ * @mod_no: module number
+ * @value: the value read 8 bit
+ * @reg: register address (just offset will do)
+ *
+ * Returns result of operation - 0 is success
+ */
+int twl4030_i2c_read_u8(u8 mod_no, u8 *value, u8 reg)
+{
+ return twl4030_i2c_read(mod_no, value, reg, 1);
+}
+EXPORT_SYMBOL(twl4030_i2c_read_u8);
+
+/* Helper Functions */
+
+/*
+ * do_twl4030_module_irq() is the desc->handle method for each of the twl4030
+ * module interrupts. It executes in kernel thread context.
+ * On entry, cpu interrupts are disabled.
+ */
+static void do_twl4030_module_irq(unsigned int irq, irq_desc_t *desc)
+{
+ struct irqaction *action;
+ const unsigned int cpu = smp_processor_id();
+
+ /*
+ * Earlier this was desc->triggered = 1;
+ */
+ desc->status |= IRQ_LEVEL;
+
+ /*
+ * The desc->handle method would normally call the desc->chip->ack
+ * method here, but we won't bother since our ack method is NULL.
+ */
+
+ if (!desc->depth) {
+ kstat_cpu(cpu).irqs[irq]++;
+
+ action = desc->action;
+ if (action) {
+ int ret;
+ int status = 0;
+ int retval = 0;
+
+ local_irq_enable();
+
+ do {
+ /* Call the ISR with cpu interrupts enabled */
+ ret = action->handler(irq, action->dev_id);
+ if (ret == IRQ_HANDLED)
+ status |= action->flags;
+ retval |= ret;
+ action = action->next;
+ } while (action);
+
+ if (status & IRQF_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);
+
+ local_irq_disable();
+
+ if (retval != IRQ_HANDLED)
+ printk(KERN_ERR "ISR for TWL4030 module"
+ " irq %d can't handle interrupt\n",
+ irq);
+
+ /*
+ * Here is where we should call the unmask method, but
+ * again we won't bother since it is NULL.
+ */
+ } else
+ printk(KERN_CRIT "TWL4030 module irq %d has no ISR"
+ " but can't be masked!\n", irq);
+ } else
+ printk(KERN_CRIT "TWL4030 module irq %d is disabled but can't"
+ " be masked!\n", irq);
+}
+
+/*
+ * twl4030_irq_thread() runs as a kernel thread. It queries the twl4030
+ * interrupt controller to see which modules are generating interrupt requests
+ * and then calls the desc->handle method for each module requesting service.
+ */
+static int twl4030_irq_thread(void *data)
+{
+ int irq = (int)data;
+ irq_desc_t *desc = irq_desc + irq;
+ static unsigned i2c_errors;
+ const static unsigned max_i2c_errors = 100;
+
+ daemonize("twl4030-irq");
+ current->flags |= PF_NOFREEZE;
+
+ while (!kthread_should_stop()) {
+ int ret;
+ int module_irq;
+ u8 pih_isr;
+
+ wait_for_completion_interruptible(&irq_event);
+
+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
+ REG_PIH_ISR_P1);
+ if (ret) {
+ printk(KERN_WARNING "I2C error %d while reading TWL4030"
+ " PIH ISR register.\n", ret);
+ if (++i2c_errors >= max_i2c_errors) {
+ printk(KERN_ERR "Maximum I2C error count"
+ " exceeded. Terminating %s.\n",
+ __func__);
+ break;
+ }
+ continue;
+ }
+
+ for (module_irq = TWL4030_IRQ_BASE; 0 != pih_isr;
+ pih_isr >>= 1, module_irq++) {
+ if (pih_isr & 0x1) {
+ irq_desc_t *d = irq_desc + module_irq;
+
+ local_irq_disable();
+
+ d->handle_irq(module_irq, d);
+
+ local_irq_enable();
+ }
+ }
+
+ desc->chip->unmask(irq);
+ }
+
+ return 0;
+}
+
+/*
+ * do_twl4030_irq() is the desc->handle method for the twl4030 interrupt.
+ * This is a chained interrupt, so there is no desc->action method for it.
+ * Now we need to query the interrupt controller in the twl4030 to determine
+ * which module is generating the interrupt request. However, we can't do i2c
+ * transactions in interrupt context, so we must defer that work to a kernel
+ * thread. All we do here is acknowledge and mask the interrupt and wakeup
+ * the kernel thread.
+ */
+static void do_twl4030_irq(unsigned int irq, irq_desc_t *desc)
+{
+ const unsigned int cpu = smp_processor_id();
+
+ /*
+ * Earlier this was desc->triggered = 1;
+ */
+ desc->status |= IRQ_LEVEL;
+
+ /*
+ * Acknowledge, clear _AND_ disable the interrupt.
+ */
+ desc->chip->ack(irq);
+
+ if (!desc->depth) {
+ kstat_cpu(cpu).irqs[irq]++;
+
+ complete(&irq_event);
+ }
+}
+
+/* attach a client to the adapter */
+static int __init twl4030_detect_client(struct i2c_adapter *adapter,
+ unsigned char sid)
+{
+ int err = 0;
+ struct twl4030_client *twl;
+
+ if (unlikely(sid >= TWL4030_NUM_SLAVES)) {
+ pr_err("sid[%d] > MOD_LAST[%d]\n", sid, TWL4030_NUM_SLAVES);
+ return -EPERM;
+ }
+
+ /* Check basic functionality */
+ err = i2c_check_functionality(adapter,
+ I2C_FUNC_SMBUS_WORD_DATA
+ | I2C_FUNC_SMBUS_WRITE_BYTE);
+ if (!err) {
+ pr_err("SlaveID=%d functionality check failed\n", sid);
+ return err;
+ }
+ twl = &twl4030_modules[sid];
+ if (unlikely(twl->inuse)) {
+ pr_err("Client %s is already in use\n", twl->client_name);
+ return -EPERM;
+ }
+
+ memset(&twl->client, 0, sizeof(struct i2c_client));
+
+ twl->client.addr = twl->address;
+ twl->client.adapter = adapter;
+ twl->client.driver = &twl4030_driver;
+
+ memcpy(&twl->client.name, twl->client_name,
+ sizeof(TWL_CLIENT_STRING) + 1);
+
+ pr_info("TWL4030: TRY attach Slave %s on Adapter %s [%x]\n",
+ twl->client_name, adapter->name, err);
+
+ err = i2c_attach_client(&twl->client);
+ if (err) {
+ pr_err("Couldn't attach Slave %s on Adapter"
+ "%s [%x]\n", twl->client_name, adapter->name, err);
+ } else {
+ twl->inuse = TWL_CLIENT_USED;
+ mutex_init(&twl->xfer_lock);
+ }
+
+ return err;
+}
+
+/* adapter callback */
+static int __init twl4030_attach_adapter(struct i2c_adapter *adapter)
+{
+ int i;
+ int ret = 0;
+ static int twl_i2c_adapter = 1;
+
+ for (i = 0; i < TWL4030_NUM_SLAVES; i++) {
+ /* Check if I need to hook on to this adapter or not */
+ if (twl4030_modules[i].adapter_index == twl_i2c_adapter) {
+ ret = twl4030_detect_client(adapter, i);
+ if (ret)
+ goto free_client;
+ }
+ }
+ twl_i2c_adapter++;
+
+ /*
+ * Check if the PIH module is initialized, if yes, then init
+ * the T2 Interrupt subsystem
+ */
+ if ((twl4030_modules[twl4030_map[TWL4030_MODULE_PIH].sid].inuse ==
+ TWL_CLIENT_USED) && (twl_irq_used != USED)) {
+ twl_init_irq();
+ twl_irq_used = USED;
+ }
+ return 0;
+
+free_client:
+ pr_err("TWL_CLIENT(Idx=%d] registration failed[0x%x]\n", i, ret);
+
+ /* ignore current slave..it never got registered */
+ i--;
+ while (i >= 0) {
+ /* now remove all those from the current adapter... */
+ if (twl4030_modules[i].adapter_index == twl_i2c_adapter)
+ (void)twl4030_detach_client(&twl4030_modules[i].client);
+ i--;
+ }
+ return ret;
+}
+
+/* adapter's callback */
+static int twl4030_detach_client(struct i2c_client *client)
+{
+ int err;
+ err = i2c_detach_client(client);
+ if (err) {
+ pr_err("Client detach failed\n");
+ return err;
+ }
+ return 0;
+}
+
+static struct task_struct * __init start_twl4030_irq_thread(int irq)
+{
+ struct task_struct *thread;
+
+ init_completion(&irq_event);
+ thread = kthread_run(twl4030_irq_thread, (void *)irq,
+ "twl4030 irq %d", irq);
+ if (!thread)
+ pr_err("%s: could not create twl4030 irq %d thread!\n",
+ __func__, irq);
+
+ return thread;
+}
+
+/*
+ * These three functions should be part of Voltage frame work
+ * added here to complete the functionality for now.
+ */
+static int __init protect_pm_master(void)
+{
+ int e = 0;
+
+ e = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_LOCK,
+ R_PROTECT_KEY);
+ return e;
+}
+
+static int __init unprotect_pm_master(void)
+{
+ int e = 0;
+
+ e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_UNLOCK1,
+ R_PROTECT_KEY);
+ e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_UNLOCK2,
+ R_PROTECT_KEY);
+ return e;
+}
+
+static int __init power_companion_init(void)
+{
+ struct clk *osc;
+ u32 rate;
+ u8 ctrl = HFCLK_FREQ_26_MHZ;
+ int e = 0;
+
+ if (cpu_is_omap2430())
+ osc = clk_get(NULL, "osc_ck");
+ else
+ osc = clk_get(NULL, "osc_sys_ck");
+ if (IS_ERR(osc)) {
+ printk(KERN_WARNING "Skipping twl3040 internal clock init and "
+ "using bootloader value (unknown osc rate)\n");
+ return 0;
+ }
+
+ rate = clk_get_rate(osc);
+ clk_put(osc);
+
+ switch (rate) {
+ case 19200000 : ctrl = HFCLK_FREQ_19p2_MHZ; break;
+ case 26000000 : ctrl = HFCLK_FREQ_26_MHZ; break;
+ case 38400000 : ctrl = HFCLK_FREQ_38p4_MHZ; break;
+ }
+
+ ctrl |= HIGH_PERF_SQ;
+ e |= unprotect_pm_master();
+ /* effect->MADC+USB ck en */
+ e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, ctrl, R_CFG_BOOT);
+ e |= protect_pm_master();
+
+ return e;
+}
+
+/**
+ * twl4030_i2c_clear_isr - clear TWL4030 SIH ISR regs via read + write
+ * @mod_no: TWL4030 module number
+ * @reg: register index to clear
+ * @cor: value of the <module>_SIH_CTRL.COR bit (1 or 0)
+ *
+ * Either reads (cor == 1) or writes (cor == 0) to a TWL4030 interrupt
+ * status register to ensure that any prior interrupts are cleared.
+ * Returns the status from the I2C read operation.
+ */
+static int __init twl4030_i2c_clear_isr(u8 mod_no, u8 reg, u8 cor)
+{
+ u8 tmp;
+
+ return (cor) ? twl4030_i2c_read_u8(mod_no, &tmp, reg) :
+ twl4030_i2c_write_u8(mod_no, 0xff, reg);
+}
+
+/**
+ * twl4030_read_cor_bit - are TWL module ISRs cleared by reads or writes?
+ * @mod_no: TWL4030 module number
+ * @reg: register index to clear
+ *
+ * Returns 1 if the TWL4030 SIH interrupt status registers (ISRs) for
+ * the specified TWL module are cleared by reads, or 0 if cleared by
+ * writes.
+ */
+static int twl4030_read_cor_bit(u8 mod_no, u8 reg)
+{
+ u8 tmp = 0;
+
+ WARN_ON(twl4030_i2c_read_u8(mod_no, &tmp, reg) < 0);
+
+ tmp &= TWL4030_SIH_CTRL_COR_MASK;
+ tmp >>= __ffs(TWL4030_SIH_CTRL_COR_MASK);
+
+ return tmp;
+}
+
+/**
+ * twl4030_mask_clear_intrs - mask and clear all TWL4030 interrupts
+ * @t: pointer to twl4030_mod_iregs array
+ * @t_sz: ARRAY_SIZE(t) (starting at 1)
+ *
+ * Mask all TWL4030 interrupt mask registers (IMRs) and clear all
+ * interrupt status registers (ISRs). No return value, but will WARN if
+ * any I2C operations fail.
+ */
+static void __init twl4030_mask_clear_intrs(const struct twl4030_mod_iregs *t,
+ const u8 t_sz)
+{
+ int i, j;
+
+ /*
+ * N.B. - further efficiency is possible here. Eight I2C
+ * operations on BCI and GPIO modules are avoidable if I2C
+ * burst read/write transactions were implemented. Would
+ * probably save about 1ms of boot time and a small amount of
+ * power.
+ */
+ for (i = 0; i < t_sz; i++) {
+ const struct twl4030_mod_iregs tmr = t[i];
+ int cor;
+
+ /* Are ISRs cleared by reads or writes? */
+ cor = twl4030_read_cor_bit(tmr.mod_no, tmr.sih_ctrl);
+ WARN_ON(cor < 0);
+
+ for (j = 0; j < tmr.reg_cnt; j++) {
+
+ /* Mask interrupts at the TWL4030 */
+ WARN_ON(twl4030_i2c_write_u8(tmr.mod_no, 0xff,
+ tmr.imrs[j]) < 0);
+
+ /* Clear TWL4030 ISRs */
+ WARN_ON(twl4030_i2c_clear_isr(tmr.mod_no,
+ tmr.isrs[j], cor) < 0);
+ }
+ }
+
+ return;
+}
+
+
+static void twl_init_irq(void)
+{
+ int i;
+ int res = 0;
+ char *msg = "Unable to register interrupt subsystem";
+ unsigned int irq_num;
+
+ /*
+ * Mask and clear all TWL4030 interrupts since initially we do
+ * not have any TWL4030 module interrupt handlers present
+ */
+ twl4030_mask_clear_intrs(twl4030_mod_regs,
+ ARRAY_SIZE(twl4030_mod_regs));
+
+ /* install an irq handler for each of the PIH modules */
+ for (i = TWL4030_IRQ_BASE; i < TWL4030_IRQ_END; i++) {
+ set_irq_chip(i, &twl4030_irq_chip);
+ set_irq_handler(i, do_twl4030_module_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+
+ irq_num = (cpu_is_omap2430()) ? INT_24XX_SYS_NIRQ : INT_34XX_SYS_NIRQ;
+
+ /* install an irq handler to demultiplex the TWL4030 interrupt */
+ set_irq_data(irq_num, start_twl4030_irq_thread(irq_num));
+ set_irq_type(irq_num, IRQ_TYPE_EDGE_FALLING);
+ set_irq_chained_handler(irq_num, do_twl4030_irq);
+
+ res = power_companion_init();
+ if (res < 0)
+ pr_err("%s[%d][%d]\n", msg, res, __LINE__);
+}
+
+static int __init twl4030_init(void)
+{
+ return i2c_add_driver(&twl4030_driver);
+}
+
+static void __exit twl4030_exit(void)
+{
+ i2c_del_driver(&twl4030_driver);
+ twl_irq_used = FREE;
+}
+
+subsys_initcall(twl4030_init);
+module_exit(twl4030_exit);
+
+MODULE_ALIAS("i2c:" DRIVER_NAME);
+MODULE_AUTHOR("Texas Instruments, Inc.");
+MODULE_DESCRIPTION("I2C Core interface for TWL4030");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/irqs.h>
+/*
+ * linux/drivers/i2c/chips/twl4030_gpio.c
+ *
+ * Copyright (C) 2006-2007 Texas Instruments, Inc.
+ * Copyright (C) 2006 MontaVista Software, Inc.
+ *
+ * Code re-arranged and cleaned up by:
+ * Syed Mohammed Khasim <x0khasim@ti.com>
+ *
+ * Initial Code:
+ * Andy Lowe / Nishanth Menon
+ *
+ * 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_stat.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/random.h>
+#include <linux/syscalls.h>
+#include <linux/kthread.h>
+#include <linux/irq.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl4030-gpio.h>
+#include <linux/slab.h>
+
- #include <asm/arch/gpio.h>
- #include <asm/arch/mux.h>
++#include <mach/irqs.h>
+#include <asm/mach/irq.h>
++#include <mach/gpio.h>
++#include <mach/mux.h>
+
+#include <linux/device.h>
+
+/* BitField Definitions */
+
+/* Data banks : 3 banks for 8 gpios each */
+#define DATA_BANK_MAX 8
+#define GET_GPIO_DATA_BANK(x) ((x)/DATA_BANK_MAX)
+#define GET_GPIO_DATA_OFF(x) ((x)%DATA_BANK_MAX)
+
+/* GPIODATADIR Fields each block 0-7 */
+#define BIT_GPIODATADIR_GPIOxDIR(x) (x)
+#define MASK_GPIODATADIR_GPIOxDIR(x) (0x01 << (x))
+
+/* GPIODATAIN Fields each block 0-7 */
+#define BIT_GPIODATAIN_GPIOxIN(x) (x)
+#define MASK_GPIODATAIN_GPIOxIN(x) (0x01 << (x))
+
+/* GPIODATAOUT Fields each block 0-7 */
+#define BIT_GPIODATAOUT_GPIOxOUT(x) (x)
+#define MASK_GPIODATAOUT_GPIOxOUT(x) (0x01 << (x))
+
+/* CLEARGPIODATAOUT Fields */
+#define BIT_CLEARGPIODATAOUT_GPIOxOUT(x) (x)
+#define MASK_CLEARGPIODATAOUT_GPIOxOUT(x) (0x01 << (x))
+
+/* SETGPIODATAOUT Fields */
+#define BIT_SETGPIODATAOUT_GPIOxOUT(x) (x)
+#define MASK_SETGPIODATAOUT_GPIOxOUT(x) (0x01 << (x))
+
+/* GPIO_DEBEN Fields */
+#define BIT_GPIO_DEBEN_GPIOxDEB(x) (x)
+#define MASK_GPIO_DEBEN_GPIOxDEB(x) (0x01 << (x))
+
+/* GPIO_ISR1A Fields */
+#define BIT_GPIO_ISR_GPIOxISR(x) (x)
+#define MASK_GPIO_ISR_GPIOxISR(x) (0x01 << (x))
+
+/* GPIO_IMR1A Fields */
+#define BIT_GPIO_IMR1A_GPIOxIMR(x) (x)
+#define MASK_GPIO_IMR1A_GPIOxIMR(x) (0x01 << (x))
+
+/* GPIO_SIR1 Fields */
+#define BIT_GPIO_SIR1_GPIOxSIR(x) (x)
+#define MASK_GPIO_SIR1_GPIO0SIR (0x01 << (x))
+
+
+/* Control banks : 5 banks for 4 gpios each */
+#define DATA_CTL_MAX 4
+#define GET_GPIO_CTL_BANK(x) ((x)/DATA_CTL_MAX)
+#define GET_GPIO_CTL_OFF(x) ((x)%DATA_CTL_MAX)
+#define GPIO_BANK_MAX GET_GPIO_CTL_BANK(TWL4030_GPIO_MAX)
+
+/* GPIOPUPDCTRx Fields 5 banks of 4 gpios each */
+#define BIT_GPIOPUPDCTR1_GPIOxPD(x) (2 * (x))
+#define MASK_GPIOPUPDCTR1_GPIOxPD(x) (0x01 << (2 * (x)))
+#define BIT_GPIOPUPDCTR1_GPIOxPU(x) ((x) + 1)
+#define MASK_GPIOPUPDCTR1_GPIOxPU(x) (0x01 << (((2 * (x)) + 1)))
+
+/* GPIO_EDR1 Fields */
+#define BIT_GPIO_EDR1_GPIOxFALLING(x) (2 * (x))
+#define MASK_GPIO_EDR1_GPIOxFALLING(x) (0x01 << (2 * (x)))
+#define BIT_GPIO_EDR1_GPIOxRISING(x) ((x) + 1)
+#define MASK_GPIO_EDR1_GPIOxRISING(x) (0x01 << (((2 * (x)) + 1)))
+
+/* GPIO_SIH_CTRL Fields */
+#define BIT_GPIO_SIH_CTRL_EXCLEN (0x000)
+#define MASK_GPIO_SIH_CTRL_EXCLEN (0x00000001)
+#define BIT_GPIO_SIH_CTRL_PENDDIS (0x001)
+#define MASK_GPIO_SIH_CTRL_PENDDIS (0x00000002)
+#define BIT_GPIO_SIH_CTRL_COR (0x002)
+#define MASK_GPIO_SIH_CTRL_COR (0x00000004)
+
+/* GPIO_CTRL Fields */
+#define BIT_GPIO_CTRL_GPIO0CD1 (0x000)
+#define MASK_GPIO_CTRL_GPIO0CD1 (0x00000001)
+#define BIT_GPIO_CTRL_GPIO1CD2 (0x001)
+#define MASK_GPIO_CTRL_GPIO1CD2 (0x00000002)
+#define BIT_GPIO_CTRL_GPIO_ON (0x002)
+#define MASK_GPIO_CTRL_GPIO_ON (0x00000004)
+
+/* Mask for GPIO registers when aggregated into a 32-bit integer */
+#define GPIO_32_MASK 0x0003ffff
+
+/* Data structures */
+static struct semaphore gpio_sem;
+
+/* store usage of each GPIO. - each bit represents one GPIO */
+static unsigned int gpio_usage_count;
+
+/* shadow the imr register */
+static unsigned int gpio_imr_shadow;
+
+/* bitmask of pending requests to unmask gpio interrupts */
+static unsigned int gpio_pending_unmask;
+
+/* pointer to gpio unmask thread struct */
+static struct task_struct *gpio_unmask_thread;
+
+/*
+ * Helper functions to read and write the GPIO ISR and IMR registers as
+ * 32-bit integers. Functions return 0 on success, non-zero otherwise.
+ * The caller must hold a lock on gpio_sem.
+ */
+
+static int gpio_read_isr(unsigned int *isr)
+{
+ int ret;
+
+ *isr = 0;
+ ret = twl4030_i2c_read(TWL4030_MODULE_GPIO, (u8 *) isr,
+ REG_GPIO_ISR1A, 3);
+ le32_to_cpup(isr);
+ *isr &= GPIO_32_MASK;
+
+ return ret;
+}
+
+static int gpio_write_isr(unsigned int isr)
+{
+ isr &= GPIO_32_MASK;
+ /*
+ * The buffer passed to the twl4030_i2c_write() routine must have an
+ * extra byte at the beginning reserved for its internal use.
+ */
+ isr <<= 8;
+ isr = cpu_to_le32(isr);
+ return twl4030_i2c_write(TWL4030_MODULE_GPIO, (u8 *) &isr,
+ REG_GPIO_ISR1A, 3);
+}
+
+static int gpio_write_imr(unsigned int imr)
+{
+ imr &= GPIO_32_MASK;
+ /*
+ * The buffer passed to the twl4030_i2c_write() routine must have an
+ * extra byte at the beginning reserved for its internal use.
+ */
+ imr <<= 8;
+ imr = cpu_to_le32(imr);
+ return twl4030_i2c_write(TWL4030_MODULE_GPIO, (u8 *) &imr,
+ REG_GPIO_IMR1A, 3);
+}
+
+/*
+ * These routines are analagous to the irqchip methods, but they are designed
+ * to be called from thread context with cpu interrupts enabled and with no
+ * locked spinlocks. We call these routines from our custom IRQ handler
+ * instead of the usual irqchip methods.
+ */
+static void twl4030_gpio_mask_and_ack(unsigned int irq)
+{
+ int gpio = irq - TWL4030_GPIO_IRQ_BASE;
+
+ down(&gpio_sem);
+ /* mask */
+ gpio_imr_shadow |= (1 << gpio);
+ gpio_write_imr(gpio_imr_shadow);
+ /* ack */
+ gpio_write_isr(1 << gpio);
+ up(&gpio_sem);
+}
+
+static void twl4030_gpio_unmask(unsigned int irq)
+{
+ int gpio = irq - TWL4030_GPIO_IRQ_BASE;
+
+ down(&gpio_sem);
+ gpio_imr_shadow &= ~(1 << gpio);
+ gpio_write_imr(gpio_imr_shadow);
+ up(&gpio_sem);
+}
+
+/*
+ * These are the irqchip methods for the TWL4030 GPIO interrupts.
+ * Our IRQ handle method doesn't call these, but they will be called by
+ * other routines such as setup_irq() and enable_irq(). They are called
+ * with cpu interrupts disabled and with a lock on the irq_controller_lock
+ * spinlock. This complicates matters, because accessing the TWL4030 GPIO
+ * interrupt controller requires I2C bus transactions that can't be initiated
+ * in this context. Our solution is to defer accessing the interrupt
+ * controller to a kernel thread. We only need to support the unmask method.
+ */
+
+static void twl4030_gpio_mask_and_ack_irqchip(unsigned int irq) {}
+static void twl4030_gpio_mask_irqchip(unsigned int irq) {}
+
+static void twl4030_gpio_unmask_irqchip(unsigned int irq)
+{
+ int gpio = irq - TWL4030_GPIO_IRQ_BASE;
+
+ gpio_pending_unmask |= (1 << gpio);
+ if (gpio_unmask_thread && gpio_unmask_thread->state != TASK_RUNNING)
+ wake_up_process(gpio_unmask_thread);
+}
+
+static struct irq_chip twl4030_gpio_irq_chip = {
+ .name = "twl4030-gpio",
+ .ack = twl4030_gpio_mask_and_ack_irqchip,
+ .mask = twl4030_gpio_mask_irqchip,
+ .unmask = twl4030_gpio_unmask_irqchip,
+};
+
+/*
+ * These are the irqchip methods for the TWL4030 PIH GPIO module interrupt.
+ * The PIH module doesn't have interrupt masking capability, so these
+ * methods are NULL.
+ */
+static void twl4030_gpio_module_ack(unsigned int irq) {}
+static void twl4030_gpio_module_mask(unsigned int irq) {}
+static void twl4030_gpio_module_unmask(unsigned int irq) {}
+static struct irq_chip twl4030_gpio_module_irq_chip = {
+ .ack = twl4030_gpio_module_ack,
+ .mask = twl4030_gpio_module_mask,
+ .unmask = twl4030_gpio_module_unmask,
+};
+
+/*
+ * To configure TWL4030 GPIO module registers
+ */
+static inline int gpio_twl4030_write(u8 address, u8 data)
+{
+ int ret = 0;
+
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, data, address);
+ return ret;
+}
+
+/*
+ * To read a TWL4030 GPIO module register
+ */
+static inline int gpio_twl4030_read(u8 address)
+{
+ u8 data;
+ int ret = 0;
+
+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &data, address);
+ if (ret >= 0)
+ ret = data;
+ return ret;
+}
+
+/*
+ * twl4030 GPIO request function
+ */
+int twl4030_request_gpio(int gpio)
+{
+ int ret = 0;
+
+ if (unlikely(gpio >= TWL4030_GPIO_MAX))
+ return -EPERM;
+
+ down(&gpio_sem);
+ if (gpio_usage_count & (0x1 << gpio))
+ ret = -EBUSY;
+ else {
+ u8 clear_pull[6] = { 0, 0, 0, 0, 0, 0 };
+ /* First time usage? - switch on GPIO module */
+ if (!gpio_usage_count) {
+ ret =
+ gpio_twl4030_write(REG_GPIO_CTRL,
+ MASK_GPIO_CTRL_GPIO_ON);
+ ret = gpio_twl4030_write(REG_GPIO_SIH_CTRL, 0x00);
+ }
+ if (!ret)
+ gpio_usage_count |= (0x1 << gpio);
+
+ ret =
+ twl4030_i2c_write(TWL4030_MODULE_GPIO, clear_pull,
+ REG_GPIOPUPDCTR1, 5);
+ }
+ up(&gpio_sem);
+ return ret;
+}
+EXPORT_SYMBOL(twl4030_request_gpio);
+
+/*
+ * TWL4030 GPIO free module
+ */
+int twl4030_free_gpio(int gpio)
+{
+ int ret = 0;
+
+ if (unlikely(gpio >= TWL4030_GPIO_MAX))
+ return -EPERM;
+
+ down(&gpio_sem);
+
+ if ((gpio_usage_count & (0x1 << gpio)) == 0)
+ ret = -EPERM;
+ else
+ gpio_usage_count &= ~(0x1 << gpio);
+
+ /* Last time usage? - switch off GPIO module */
+ if (!gpio_usage_count)
+ ret = gpio_twl4030_write(REG_GPIO_CTRL, 0x0);
+
+ up(&gpio_sem);
+ return ret;
+}
+EXPORT_SYMBOL(twl4030_free_gpio);
+
+/*
+ * Set direction for TWL4030 GPIO
+ */
+int twl4030_set_gpio_direction(int gpio, int is_input)
+{
+ u8 d_bnk = GET_GPIO_DATA_BANK(gpio);
+ u8 d_msk = MASK_GPIODATADIR_GPIOxDIR(GET_GPIO_DATA_OFF(gpio));
+ u8 reg = 0;
+ u8 base = 0;
+ int ret = 0;
+
+ if (unlikely((gpio >= TWL4030_GPIO_MAX)
+ || !(gpio_usage_count & (0x1 << gpio))))
+ return -EPERM;
+
+ base = REG_GPIODATADIR1 + d_bnk;
+
+ down(&gpio_sem);
+ ret = gpio_twl4030_read(base);
+ if (ret >= 0) {
+ if (is_input)
+ reg = (u8) ((ret) & ~(d_msk));
+ else
+ reg = (u8) ((ret) | (d_msk));
+
+ ret = gpio_twl4030_write(base, reg);
+ }
+ up(&gpio_sem);
+ return ret;
+}
+EXPORT_SYMBOL(twl4030_set_gpio_direction);
+
+/*
+ * To enable/disable GPIO pin on TWL4030
+ */
+int twl4030_set_gpio_dataout(int gpio, int enable)
+{
+ u8 d_bnk = GET_GPIO_DATA_BANK(gpio);
+ u8 d_msk = MASK_GPIODATAOUT_GPIOxOUT(GET_GPIO_DATA_OFF(gpio));
+ u8 base = 0;
+ int ret = 0;
+
+ if (unlikely((gpio >= TWL4030_GPIO_MAX)
+ || !(gpio_usage_count & (0x1 << gpio))))
+ return -EPERM;
+
+ if (enable)
+ base = REG_SETGPIODATAOUT1 + d_bnk;
+ else
+ base = REG_CLEARGPIODATAOUT1 + d_bnk;
+
+ down(&gpio_sem);
+ ret = gpio_twl4030_write(base, d_msk);
+ up(&gpio_sem);
+ return ret;
+}
+EXPORT_SYMBOL(twl4030_set_gpio_dataout);
+
+/*
+ * To get the status of a GPIO pin on TWL4030
+ */
+int twl4030_get_gpio_datain(int gpio)
+{
+ u8 d_bnk = GET_GPIO_DATA_BANK(gpio);
+ u8 d_off = BIT_GPIODATAIN_GPIOxIN(GET_GPIO_DATA_OFF(gpio));
+ u8 base = 0;
+ int ret = 0;
+
+ if (unlikely((gpio >= TWL4030_GPIO_MAX)
+ || !(gpio_usage_count & (0x1 << gpio))))
+ return -EPERM;
+
+ base = REG_GPIODATAIN1 + d_bnk;
+ down(&gpio_sem);
+ ret = gpio_twl4030_read(base);
+ up(&gpio_sem);
+ if (ret > 0)
+ ret = (ret >> d_off) & 0x1;
+
+ return ret;
+}
+EXPORT_SYMBOL(twl4030_get_gpio_datain);
+
+/*
+ * Configure PULL type for a GPIO pin on TWL4030
+ */
+int twl4030_set_gpio_pull(int gpio, int pull_dircn)
+{
+ u8 c_bnk = GET_GPIO_CTL_BANK(gpio);
+ u8 c_off = GET_GPIO_CTL_OFF(gpio);
+ u8 c_msk = 0;
+ u8 reg = 0;
+ u8 base = 0;
+ int ret = 0;
+
+ if (unlikely((gpio >= TWL4030_GPIO_MAX) ||
+ !(gpio_usage_count & (0x1 << gpio))))
+ return -EPERM;
+
+ base = REG_GPIOPUPDCTR1 + c_bnk;
+ if (pull_dircn == TWL4030_GPIO_PULL_DOWN)
+ c_msk = MASK_GPIOPUPDCTR1_GPIOxPD(c_off);
+ else if (pull_dircn == TWL4030_GPIO_PULL_UP)
+ c_msk = MASK_GPIOPUPDCTR1_GPIOxPU(c_off);
+
+ down(&gpio_sem);
+ ret = gpio_twl4030_read(base);
+ if (ret >= 0) {
+ /* clear the previous up/down values */
+ reg = (u8) (ret);
+ reg &= ~(MASK_GPIOPUPDCTR1_GPIOxPU(c_off) |
+ MASK_GPIOPUPDCTR1_GPIOxPD(c_off));
+ reg |= c_msk;
+ ret = gpio_twl4030_write(base, reg);
+ }
+ up(&gpio_sem);
+ return ret;
+}
+EXPORT_SYMBOL(twl4030_set_gpio_pull);
+
+/*
+ * Configure Edge control for a GPIO pin on TWL4030
+ */
+int twl4030_set_gpio_edge_ctrl(int gpio, int edge)
+{
+ u8 c_bnk = GET_GPIO_CTL_BANK(gpio);
+ u8 c_off = GET_GPIO_CTL_OFF(gpio);
+ u8 c_msk = 0;
+ u8 reg = 0;
+ u8 base = 0;
+ int ret = 0;
+
+ if (unlikely((gpio >= TWL4030_GPIO_MAX)
+ || !(gpio_usage_count & (0x1 << gpio))))
+ return -EPERM;
+
+ base = REG_GPIO_EDR1 + c_bnk;
+
+ if (edge & TWL4030_GPIO_EDGE_RISING)
+ c_msk |= MASK_GPIO_EDR1_GPIOxRISING(c_off);
+
+ if (edge & TWL4030_GPIO_EDGE_FALLING)
+ c_msk |= MASK_GPIO_EDR1_GPIOxFALLING(c_off);
+
+ down(&gpio_sem);
+ ret = gpio_twl4030_read(base);
+ if (ret >= 0) {
+ /* clear the previous rising/falling values */
+ reg =
+ (u8) (ret &
+ ~(MASK_GPIO_EDR1_GPIOxFALLING(c_off) |
+ MASK_GPIO_EDR1_GPIOxRISING(c_off)));
+ reg |= c_msk;
+ ret = gpio_twl4030_write(base, reg);
+ }
+ up(&gpio_sem);
+ return ret;
+}
+EXPORT_SYMBOL(twl4030_set_gpio_edge_ctrl);
+
+/*
+ * Configure debounce timing value for a GPIO pin on TWL4030
+ */
+int twl4030_set_gpio_debounce(int gpio, int enable)
+{
+ u8 d_bnk = GET_GPIO_DATA_BANK(gpio);
+ u8 d_msk = MASK_GPIO_DEBEN_GPIOxDEB(GET_GPIO_DATA_OFF(gpio));
+ u8 reg = 0;
+ u8 base = 0;
+ int ret = 0;
+
+ if (unlikely((gpio >= TWL4030_GPIO_MAX)
+ || !(gpio_usage_count & (0x1 << gpio))))
+ return -EPERM;
+
+ base = REG_GPIO_DEBEN1 + d_bnk;
+ down(&gpio_sem);
+ ret = gpio_twl4030_read(base);
+ if (ret >= 0) {
+ if (enable)
+ reg = (u8) ((ret) | (d_msk));
+ else
+ reg = (u8) ((ret) & ~(d_msk));
+
+ ret = gpio_twl4030_write(base, reg);
+ }
+ up(&gpio_sem);
+ return ret;
+}
+EXPORT_SYMBOL(twl4030_set_gpio_debounce);
+
+/*
+ * Configure Card detect for GPIO pin on TWL4030
+ */
+int twl4030_set_gpio_card_detect(int gpio, int enable)
+{
+ u8 reg = 0;
+ u8 msk = (1 << gpio);
+ int ret = 0;
+
+ /* Only GPIO 0 or 1 can be used for CD feature.. */
+ if (unlikely((gpio >= TWL4030_GPIO_MAX)
+ || !(gpio_usage_count & (0x1 << gpio))
+ || (gpio >= TWL4030_GPIO_MAX_CD))) {
+ return -EPERM;
+ }
+
+ down(&gpio_sem);
+ ret = gpio_twl4030_read(REG_GPIO_CTRL);
+ if (ret >= 0) {
+ if (enable)
+ reg = (u8) (ret | msk);
+ else
+ reg = (u8) (ret & ~msk);
+
+ ret = gpio_twl4030_write(REG_GPIO_CTRL, reg);
+ }
+ up(&gpio_sem);
+ return (ret);
+}
+EXPORT_SYMBOL(twl4030_set_gpio_card_detect);
+
+/* MODULE FUNCTIONS */
+
+/*
+ * gpio_unmask_thread() runs as a kernel thread. It is awakened by the unmask
+ * method for the GPIO interrupts. It unmasks all of the GPIO interrupts
+ * specified in the gpio_pending_unmask bitmask. We have to do the unmasking
+ * in a kernel thread rather than directly in the unmask method because of the
+ * need to access the TWL4030 via the I2C bus. Note that we don't need to be
+ * concerned about race conditions where the request to unmask a GPIO interrupt
+ * has already been cancelled before this thread does the unmasking. If a GPIO
+ * interrupt is improperly unmasked, then the IRQ handler for it will mask it
+ * when an interrupt occurs.
+ */
+static int twl4030_gpio_unmask_thread(void *data)
+{
+ current->flags |= PF_NOFREEZE;
+
+ while (!kthread_should_stop()) {
+ int irq;
+ unsigned int gpio_unmask;
+
+ local_irq_disable();
+ gpio_unmask = gpio_pending_unmask;
+ gpio_pending_unmask = 0;
+ local_irq_enable();
+
+ for (irq = TWL4030_GPIO_IRQ_BASE; 0 != gpio_unmask;
+ gpio_unmask >>= 1, irq++) {
+ if (gpio_unmask & 0x1)
+ twl4030_gpio_unmask(irq);
+ }
+
+ local_irq_disable();
+ if (!gpio_pending_unmask)
+ set_current_state(TASK_INTERRUPTIBLE);
+ local_irq_enable();
+
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ return 0;
+}
+
+/*
+ * do_twl4030_gpio_irq() is the desc->handle method for each of the twl4030
+ * gpio interrupts. It executes in kernel thread context.
+ * On entry, cpu interrupts are enabled.
+ */
+static void do_twl4030_gpio_irq(unsigned int irq, irq_desc_t *desc)
+{
+ struct irqaction *action;
+ const unsigned int cpu = smp_processor_id();
+
+ desc->status |= IRQ_LEVEL;
+
+ /*
+ * Acknowledge, clear _AND_ disable the interrupt.
+ */
+ twl4030_gpio_mask_and_ack(irq);
+
+ if (!desc->depth) {
+ kstat_cpu(cpu).irqs[irq]++;
+
+ action = desc->action;
+ if (action) {
+ int ret;
+ int status = 0;
+ int retval = 0;
+ do {
+ /* Call the ISR with cpu interrupts enabled. */
+ ret = action->handler(irq, action->dev_id);
+ if (ret == IRQ_HANDLED)
+ status |= action->flags;
+ retval |= ret;
+ action = action->next;
+ } while (action);
+
+ if (retval != IRQ_HANDLED)
+ printk(KERN_ERR "ISR for TWL4030 GPIO"
+ " irq %d can't handle interrupt\n",
+ irq);
+
+ if (!desc->depth)
+ twl4030_gpio_unmask(irq);
+ }
+ }
+}
+
+/*
+ * do_twl4030_gpio_module_irq() is the desc->handle method for the twl4030 gpio
+ * module interrupt. It executes in kernel thread context.
+ * This is a chained interrupt, so there is no desc->action method for it.
+ * We query the gpio module interrupt controller in the twl4030 to determine
+ * which gpio lines are generating interrupt requests, and then call the
+ * desc->handle method for each gpio that needs service.
+ * On entry, cpu interrupts are disabled.
+ */
+static void do_twl4030_gpio_module_irq(unsigned int irq, irq_desc_t *desc)
+{
+ const unsigned int cpu = smp_processor_id();
+
+ desc->status |= IRQ_LEVEL;
+ /*
+ * The desc->handle method would normally call the desc->chip->ack
+ * method here, but we won't bother since our ack method is NULL.
+ */
+ if (!desc->depth) {
+ int gpio_irq;
+ unsigned int gpio_isr;
+
+ kstat_cpu(cpu).irqs[irq]++;
+ local_irq_enable();
+
+ down(&gpio_sem);
+ if (gpio_read_isr(&gpio_isr))
+ gpio_isr = 0;
+ up(&gpio_sem);
+
+ for (gpio_irq = TWL4030_GPIO_IRQ_BASE; 0 != gpio_isr;
+ gpio_isr >>= 1, gpio_irq++) {
+ if (gpio_isr & 0x1) {
+ irq_desc_t *d = irq_desc + gpio_irq;
+ d->handle_irq(gpio_irq, d);
+ }
+ }
+
+ local_irq_disable();
+ /*
+ * Here is where we should call the unmask method, but again we
+ * won't bother since it is NULL.
+ */
+ }
+}
+
+/* TWL4030 Initialization module */
+static int __init gpio_twl4030_init(void)
+{
+ int ret;
+ int irq = 0;
+
+ /* init the global locking sem */
+ sema_init(&gpio_sem, 1);
+
+ /* All GPIO interrupts are initially masked */
+ gpio_pending_unmask = 0;
+ gpio_imr_shadow = GPIO_32_MASK;
+ ret = gpio_write_imr(gpio_imr_shadow);
+ if (!ret) {
+ /*
+ * Create a kernel thread to handle deferred unmasking of gpio
+ * interrupts.
+ */
+ gpio_unmask_thread = kthread_create(twl4030_gpio_unmask_thread,
+ NULL, "twl4030 gpio");
+ if (!gpio_unmask_thread) {
+ printk(KERN_ERR
+ "%s: could not create twl4030 gpio unmask"
+ " thread!\n", __func__);
+ ret = -ENOMEM;
+ }
+ }
+
+ if (!ret) {
+ /* install an irq handler for each of the gpio interrupts */
+ for (irq = TWL4030_GPIO_IRQ_BASE; irq < TWL4030_GPIO_IRQ_END;
+ irq++) {
+ set_irq_chip(irq, &twl4030_gpio_irq_chip);
+ set_irq_handler(irq, do_twl4030_gpio_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ /*
+ * Install an irq handler to demultiplex the gpio module
+ * interrupt.
+ */
+ set_irq_chip(TWL4030_MODIRQ_GPIO,
+ &twl4030_gpio_module_irq_chip);
+ set_irq_chained_handler(TWL4030_MODIRQ_GPIO,
+ do_twl4030_gpio_module_irq);
+ }
+
+ printk(KERN_INFO "TWL4030 GPIO Demux: IRQ Range %d to %d,"
+ " Initialization %s\n", TWL4030_GPIO_IRQ_BASE,
+ TWL4030_GPIO_IRQ_END, (ret) ? "Failed" : "Success");
+ return ret;
+}
+
+/* TWL GPIO exit module */
+static void __exit gpio_twl4030_exit(void)
+{
+ int irq;
+
+ /* uninstall the gpio demultiplexing interrupt handler */
+ set_irq_handler(TWL4030_MODIRQ_GPIO, NULL);
+ set_irq_flags(TWL4030_MODIRQ_GPIO, 0);
+
+ /* uninstall the irq handler for each of the gpio interrupts */
+ for (irq = TWL4030_GPIO_IRQ_BASE; irq < TWL4030_GPIO_IRQ_END; irq++) {
+ set_irq_handler(irq, NULL);
+ set_irq_flags(irq, 0);
+ }
+
+ /* stop the gpio unmask kernel thread */
+ if (gpio_unmask_thread) {
+ kthread_stop(gpio_unmask_thread);
+ gpio_unmask_thread = NULL;
+ }
+}
+
+module_init(gpio_twl4030_init);
+module_exit(gpio_twl4030_exit);
+
+MODULE_ALIAS("i2c:twl4030-gpio");
+MODULE_AUTHOR("Texas Instruments, Inc.");
+MODULE_DESCRIPTION("GPIO interface for TWL4030");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/usb.h>
+/*
+ * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller
+ *
+ * Copyright (C) 2004-2007 Texas Instruments
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Current status:
+ * - HS USB ULPI mode works.
+ * - 3-pin mode support may be added in future.
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/i2c/twl4030.h>
++#include <mach/usb.h>
+
+/* Register defines */
+
+#define VENDOR_ID_LO 0x00
+#define VENDOR_ID_HI 0x01
+#define PRODUCT_ID_LO 0x02
+#define PRODUCT_ID_HI 0x03
+
+#define FUNC_CTRL 0x04
+#define FUNC_CTRL_SET 0x05
+#define FUNC_CTRL_CLR 0x06
+#define FUNC_CTRL_SUSPENDM (1 << 6)
+#define FUNC_CTRL_RESET (1 << 5)
+#define FUNC_CTRL_OPMODE_MASK (3 << 3) /* bits 3 and 4 */
+#define FUNC_CTRL_OPMODE_NORMAL (0 << 3)
+#define FUNC_CTRL_OPMODE_NONDRIVING (1 << 3)
+#define FUNC_CTRL_OPMODE_DISABLE_BIT_NRZI (2 << 3)
+#define FUNC_CTRL_TERMSELECT (1 << 2)
+#define FUNC_CTRL_XCVRSELECT_MASK (3 << 0) /* bits 0 and 1 */
+#define FUNC_CTRL_XCVRSELECT_HS (0 << 0)
+#define FUNC_CTRL_XCVRSELECT_FS (1 << 0)
+#define FUNC_CTRL_XCVRSELECT_LS (2 << 0)
+#define FUNC_CTRL_XCVRSELECT_FS4LS (3 << 0)
+
+#define IFC_CTRL 0x07
+#define IFC_CTRL_SET 0x08
+#define IFC_CTRL_CLR 0x09
+#define IFC_CTRL_INTERFACE_PROTECT_DISABLE (1 << 7)
+#define IFC_CTRL_AUTORESUME (1 << 4)
+#define IFC_CTRL_CLOCKSUSPENDM (1 << 3)
+#define IFC_CTRL_CARKITMODE (1 << 2)
+#define IFC_CTRL_FSLSSERIALMODE_3PIN (1 << 1)
+
+#define TWL4030_OTG_CTRL 0x0A
+#define TWL4030_OTG_CTRL_SET 0x0B
+#define TWL4030_OTG_CTRL_CLR 0x0C
+#define TWL4030_OTG_CTRL_DRVVBUS (1 << 5)
+#define TWL4030_OTG_CTRL_CHRGVBUS (1 << 4)
+#define TWL4030_OTG_CTRL_DISCHRGVBUS (1 << 3)
+#define TWL4030_OTG_CTRL_DMPULLDOWN (1 << 2)
+#define TWL4030_OTG_CTRL_DPPULLDOWN (1 << 1)
+#define TWL4030_OTG_CTRL_IDPULLUP (1 << 0)
+
+#define USB_INT_EN_RISE 0x0D
+#define USB_INT_EN_RISE_SET 0x0E
+#define USB_INT_EN_RISE_CLR 0x0F
+#define USB_INT_EN_FALL 0x10
+#define USB_INT_EN_FALL_SET 0x11
+#define USB_INT_EN_FALL_CLR 0x12
+#define USB_INT_STS 0x13
+#define USB_INT_LATCH 0x14
+#define USB_INT_IDGND (1 << 4)
+#define USB_INT_SESSEND (1 << 3)
+#define USB_INT_SESSVALID (1 << 2)
+#define USB_INT_VBUSVALID (1 << 1)
+#define USB_INT_HOSTDISCONNECT (1 << 0)
+
+#define CARKIT_CTRL 0x19
+#define CARKIT_CTRL_SET 0x1A
+#define CARKIT_CTRL_CLR 0x1B
+#define CARKIT_CTRL_MICEN (1 << 6)
+#define CARKIT_CTRL_SPKRIGHTEN (1 << 5)
+#define CARKIT_CTRL_SPKLEFTEN (1 << 4)
+#define CARKIT_CTRL_RXDEN (1 << 3)
+#define CARKIT_CTRL_TXDEN (1 << 2)
+#define CARKIT_CTRL_IDGNDDRV (1 << 1)
+#define CARKIT_CTRL_CARKITPWR (1 << 0)
+#define CARKIT_PLS_CTRL 0x22
+#define CARKIT_PLS_CTRL_SET 0x23
+#define CARKIT_PLS_CTRL_CLR 0x24
+#define CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN (1 << 3)
+#define CARKIT_PLS_CTRL_SPKRLEFT_BIASEN (1 << 2)
+#define CARKIT_PLS_CTRL_RXPLSEN (1 << 1)
+#define CARKIT_PLS_CTRL_TXPLSEN (1 << 0)
+
+#define MCPC_CTRL 0x30
+#define MCPC_CTRL_SET 0x31
+#define MCPC_CTRL_CLR 0x32
+#define MCPC_CTRL_RTSOL (1 << 7)
+#define MCPC_CTRL_EXTSWR (1 << 6)
+#define MCPC_CTRL_EXTSWC (1 << 5)
+#define MCPC_CTRL_VOICESW (1 << 4)
+#define MCPC_CTRL_OUT64K (1 << 3)
+#define MCPC_CTRL_RTSCTSSW (1 << 2)
+#define MCPC_CTRL_HS_UART (1 << 0)
+
+#define MCPC_IO_CTRL 0x33
+#define MCPC_IO_CTRL_SET 0x34
+#define MCPC_IO_CTRL_CLR 0x35
+#define MCPC_IO_CTRL_MICBIASEN (1 << 5)
+#define MCPC_IO_CTRL_CTS_NPU (1 << 4)
+#define MCPC_IO_CTRL_RXD_PU (1 << 3)
+#define MCPC_IO_CTRL_TXDTYP (1 << 2)
+#define MCPC_IO_CTRL_CTSTYP (1 << 1)
+#define MCPC_IO_CTRL_RTSTYP (1 << 0)
+
+#define MCPC_CTRL2 0x36
+#define MCPC_CTRL2_SET 0x37
+#define MCPC_CTRL2_CLR 0x38
+#define MCPC_CTRL2_MCPC_CK_EN (1 << 0)
+
+#define OTHER_FUNC_CTRL 0x80
+#define OTHER_FUNC_CTRL_SET 0x81
+#define OTHER_FUNC_CTRL_CLR 0x82
+#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4)
+#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2)
+
+#define OTHER_IFC_CTRL 0x83
+#define OTHER_IFC_CTRL_SET 0x84
+#define OTHER_IFC_CTRL_CLR 0x85
+#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6)
+#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5)
+#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4)
+#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3)
+#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2)
+#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0)
+
+#define OTHER_INT_EN_RISE 0x86
+#define OTHER_INT_EN_RISE_SET 0x87
+#define OTHER_INT_EN_RISE_CLR 0x88
+#define OTHER_INT_EN_FALL 0x89
+#define OTHER_INT_EN_FALL_SET 0x8A
+#define OTHER_INT_EN_FALL_CLR 0x8B
+#define OTHER_INT_STS 0x8C
+#define OTHER_INT_LATCH 0x8D
+#define OTHER_INT_VB_SESS_VLD (1 << 7)
+#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */
+#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */
+#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */
+#define OTHER_INT_MANU (1 << 1)
+#define OTHER_INT_ABNORMAL_STRESS (1 << 0)
+
+#define ID_STATUS 0x96
+#define ID_RES_FLOAT (1 << 4)
+#define ID_RES_440K (1 << 3)
+#define ID_RES_200K (1 << 2)
+#define ID_RES_102K (1 << 1)
+#define ID_RES_GND (1 << 0)
+
+#define POWER_CTRL 0xAC
+#define POWER_CTRL_SET 0xAD
+#define POWER_CTRL_CLR 0xAE
+#define POWER_CTRL_OTG_ENAB (1 << 5)
+
+#define OTHER_IFC_CTRL2 0xAF
+#define OTHER_IFC_CTRL2_SET 0xB0
+#define OTHER_IFC_CTRL2_CLR 0xB1
+#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4)
+#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3)
+#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2)
+#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */
+#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0)
+#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0)
+
+#define REG_CTRL_EN 0xB2
+#define REG_CTRL_EN_SET 0xB3
+#define REG_CTRL_EN_CLR 0xB4
+#define REG_CTRL_ERROR 0xB5
+#define ULPI_I2C_CONFLICT_INTEN (1 << 0)
+
+#define OTHER_FUNC_CTRL2 0xB8
+#define OTHER_FUNC_CTRL2_SET 0xB9
+#define OTHER_FUNC_CTRL2_CLR 0xBA
+#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0)
+
+/* following registers do not have separate _clr and _set registers */
+#define VBUS_DEBOUNCE 0xC0
+#define ID_DEBOUNCE 0xC1
+#define VBAT_TIMER 0xD3
+#define PHY_PWR_CTRL 0xFD
+#define PHY_PWR_PHYPWD (1 << 0)
+#define PHY_CLK_CTRL 0xFE
+#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2)
+#define PHY_CLK_CTRL_CLK32K_EN (1 << 1)
+#define REQ_PHY_DPLL_CLK (1 << 0)
+#define PHY_CLK_CTRL_STS 0xFF
+#define PHY_DPLL_CLK (1 << 0)
+
+/* In module TWL4030_MODULE_PM_MASTER */
+#define PROTECT_KEY 0x0E
+
+/* In module TWL4030_MODULE_PM_RECEIVER */
+#define VUSB_DEDICATED1 0x7D
+#define VUSB_DEDICATED2 0x7E
+#define VUSB1V5_DEV_GRP 0x71
+#define VUSB1V5_TYPE 0x72
+#define VUSB1V5_REMAP 0x73
+#define VUSB1V8_DEV_GRP 0x74
+#define VUSB1V8_TYPE 0x75
+#define VUSB1V8_REMAP 0x76
+#define VUSB3V1_DEV_GRP 0x77
+#define VUSB3V1_TYPE 0x78
+#define VUSB3V1_REMAP 0x79
+
+#define ID_STATUS 0x96
+#define ID_RES_FLOAT (1 << 4) /* mini-B */
+#define ID_RES_440K (1 << 3) /* type 2 charger */
+#define ID_RES_200K (1 << 2) /* 5-wire carkit or
+ type 1 charger */
+#define ID_RES_102K (1 << 1) /* phone */
+#define ID_RES_GND (1 << 0) /* mini-A */
+
+/* In module TWL4030_MODULE_INTBR */
+#define PMBR1 0x0D
+#define GPIO_USB_4PIN_ULPI_2430C (3 << 0)
+
+/* In module TWL4030_MODULE_INT */
+#define REG_PWR_ISR1 0x00
+#define REG_PWR_IMR1 0x01
+#define USB_PRES (1 << 2)
+#define REG_PWR_EDR1 0x05
+#define USB_PRES_FALLING (1 << 4)
+#define USB_PRES_RISING (1 << 5)
+#define REG_PWR_SIH_CTRL 0x07
+#define COR (1 << 2)
+
+/* internal define on top of container_of */
+#define xceiv_to_twl(x) container_of((x), struct twl4030_usb, otg);
+
+/* bits in OTG_CTRL */
+
+#define OTG_XCEIV_OUTPUTS \
+ (OTG_ASESSVLD|OTG_BSESSEND|OTG_BSESSVLD|OTG_VBUSVLD|OTG_ID)
+#define OTG_XCEIV_INPUTS \
+ (OTG_PULLDOWN|OTG_PULLUP|OTG_DRV_VBUS|OTG_PD_VBUS|OTG_PU_VBUS|OTG_PU_ID)
+#define OTG_CTRL_BITS \
+ (OTG_A_BUSREQ|OTG_A_SETB_HNPEN|OTG_B_BUSREQ|OTG_B_HNPEN|OTG_BUSDROP)
+ /* and OTG_PULLUP is sometimes written */
+
+#define OTG_CTRL_MASK (OTG_DRIVER_SEL| \
+ OTG_XCEIV_OUTPUTS|OTG_XCEIV_INPUTS| \
+ OTG_CTRL_BITS)
+
+
+/*-------------------------------------------------------------------------*/
+
+struct twl4030_usb {
+ struct otg_transceiver otg;
+ int irq;
+ u8 usb_mode; /* pin configuration */
+#define T2_USB_MODE_ULPI 1
+/* #define T2_USB_MODE_CEA2011_3PIN 2 */
+ u8 asleep;
+};
+
+static struct twl4030_usb *the_transceiver;
+
+/*-------------------------------------------------------------------------*/
+
+static int twl4030_i2c_write_u8_verify(u8 module, u8 data, u8 address)
+{
+ u8 check;
+
+ if ((twl4030_i2c_write_u8(module, data, address) >= 0) &&
+ (twl4030_i2c_read_u8(module, &check, address) >= 0) &&
+ (check == data))
+ return 0;
+ /* Failed once: Try again */
+ if ((twl4030_i2c_write_u8(module, data, address) >= 0) &&
+ (twl4030_i2c_read_u8(module, &check, address) >= 0) &&
+ (check == data))
+ return 0;
+ /* Failed again: Return error */
+ return -EBUSY;
+}
+
+#define twl4030_usb_write_verify(address, data) \
+ twl4030_i2c_write_u8_verify(TWL4030_MODULE_USB, (data), (address))
+
+static inline int twl4030_usb_write(u8 address, u8 data)
+{
+ int ret = 0;
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_USB, data, address);
+ if (ret >= 0) {
+#if 0 /* debug */
+ u8 data1;
+ if (twl4030_i2c_read_u8(TWL4030_MODULE_USB, &data1,
+ address) < 0)
+ printk(KERN_ERR "re-read failed\n");
+ else
+ printk(KERN_INFO
+ "Write %s wrote %x read %x from reg %x\n",
+ (data1 == data) ? "succeed" : "mismatch",
+ data, data1, address);
+#endif
+ } else {
+ printk(KERN_WARNING
+ "TWL4030:USB:Write[0x%x] Error %d\n", address, ret);
+ }
+ return ret;
+}
+
+static inline int twl4030_usb_read(u8 address)
+{
+ u8 data;
+ int ret = 0;
+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_USB, &data, address);
+ if (ret >= 0) {
+ ret = data;
+ } else {
+ printk(KERN_WARNING
+ "TWL4030:USB:Read[0x%x] Error %d\n", address, ret);
+ }
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline int
+twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
+{
+ return twl4030_usb_write(reg + 1, bits);
+}
+
+static inline int
+twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
+{
+ return twl4030_usb_write(reg + 2, bits);
+
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode)
+{
+ twl->usb_mode = mode;
+
+ switch (mode) {
+ case T2_USB_MODE_ULPI:
+ twl4030_usb_clear_bits(twl, IFC_CTRL, IFC_CTRL_CARKITMODE);
+ twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
+ twl4030_usb_clear_bits(twl, FUNC_CTRL,
+ FUNC_CTRL_XCVRSELECT_MASK |
+ FUNC_CTRL_OPMODE_MASK);
+ break;
+/*
+ case T2_USB_MODE_CEA2011_3PIN:
+ twl4030_cea2011_3_pin_FS_setup(twl);
+ break;
+*/
+ default:
+ /* FIXME: power on defaults */
+ break;
+ };
+}
+
+#ifdef CONFIG_TWL4030_USB_HS_ULPI
+static void hs_usb_init(struct twl4030_usb *twl)
+{
+ twl->usb_mode = T2_USB_MODE_ULPI;
+ return;
+}
+
+#endif
+
+static void twl4030_i2c_access(int on)
+{
+ unsigned long timeout;
+ int val = twl4030_usb_read(PHY_CLK_CTRL);
+
+ if (val >= 0) {
+ if (on) {
+ /* enable DPLL to access PHY registers over I2C */
+ val |= REQ_PHY_DPLL_CLK;
+ if (twl4030_usb_write_verify(PHY_CLK_CTRL,
+ (u8)val) < 0) {
+ printk(KERN_ERR "twl4030_usb: i2c write failed,"
+ " line %d\n", __LINE__);
+ return;
+ }
+
+ timeout = jiffies + HZ;
+ while (!(twl4030_usb_read(PHY_CLK_CTRL_STS) &
+ PHY_DPLL_CLK)
+ && time_before(jiffies, timeout))
+ udelay(10);
+ if (!(twl4030_usb_read(PHY_CLK_CTRL_STS) &
+ PHY_DPLL_CLK))
+ printk(KERN_ERR "Timeout setting T2 HSUSB "
+ "PHY DPLL clock\n");
+ } else {
+ /* let ULPI control the DPLL clock */
+ val &= ~REQ_PHY_DPLL_CLK;
+ if (twl4030_usb_write_verify(PHY_CLK_CTRL,
+ (u8)val) < 0) {
+ printk(KERN_ERR "twl4030_usb: i2c write failed,"
+ " line %d\n", __LINE__);
+ }
+ }
+ }
+ return;
+}
+
+static void usb_irq_enable(int rising, int falling)
+{
+ u8 val;
+
+ /* edge setup */
+ if (twl4030_i2c_read_u8(TWL4030_MODULE_INT, &val, REG_PWR_EDR1) < 0) {
+ printk(KERN_ERR "twl4030_usb: i2c read failed,"
+ " line %d\n", __LINE__);
+ return;
+ }
+ val &= ~(USB_PRES_RISING | USB_PRES_FALLING);
+ if (rising)
+ val = val | USB_PRES_RISING;
+ if (falling)
+ val = val | USB_PRES_FALLING;
+ if (twl4030_i2c_write_u8_verify(TWL4030_MODULE_INT, val,
+ REG_PWR_EDR1) < 0) {
+ printk(KERN_ERR "twl4030_usb: i2c write failed,"
+ " line %d\n", __LINE__);
+ return;
+ }
+
+ /* un-mask interrupt */
+ if (twl4030_i2c_read_u8(TWL4030_MODULE_INT, &val, REG_PWR_IMR1) < 0) {
+ printk(KERN_ERR "twl4030_usb: i2c read failed,"
+ " line %d\n", __LINE__);
+ return;
+ }
+ val &= ~USB_PRES;
+ if (twl4030_i2c_write_u8_verify(TWL4030_MODULE_INT, val,
+ REG_PWR_IMR1) < 0)
+ printk(KERN_ERR "twl4030_usb: i2c write failed,"
+ " line %d\n", __LINE__);
+
+ return;
+}
+
+static void usb_irq_disable(void)
+{
+ u8 val;
+
+ /* undo edge setup */
+ if (twl4030_i2c_read_u8(TWL4030_MODULE_INT, &val, REG_PWR_EDR1) < 0) {
+ printk(KERN_ERR "twl4030_usb: i2c read failed,"
+ " line %d\n", __LINE__);
+ return;
+ }
+ val &= ~(USB_PRES_RISING | USB_PRES_FALLING);
+ if (twl4030_i2c_write_u8_verify(TWL4030_MODULE_INT, val,
+ REG_PWR_EDR1) < 0) {
+ printk(KERN_ERR "twl4030_usb: i2c write failed,"
+ " line %d\n", __LINE__);
+ return;
+ }
+
+ /* mask interrupt */
+ if (twl4030_i2c_read_u8(TWL4030_MODULE_INT, &val, REG_PWR_IMR1) < 0) {
+ printk(KERN_ERR "twl4030_usb: i2c read failed,"
+ " line %d\n", __LINE__);
+ return;
+ }
+ val |= USB_PRES;
+ if (twl4030_i2c_write_u8_verify(TWL4030_MODULE_INT, val,
+ REG_PWR_IMR1) < 0)
+ printk(KERN_ERR "twl4030_usb: i2c write failed,"
+ " line %d\n", __LINE__);
+
+ return;
+}
+
+static void twl4030_phy_power(struct twl4030_usb *twl, int on)
+{
+ u8 pwr;
+
+ pwr = twl4030_usb_read(PHY_PWR_CTRL);
+ if (on) {
+ pwr &= ~PHY_PWR_PHYPWD;
+ if (twl4030_usb_write_verify(PHY_PWR_CTRL, pwr) < 0) {
+ printk(KERN_ERR "twl4030_usb: i2c write failed,"
+ " line %d\n", __LINE__);
+ return;
+ }
+ twl4030_usb_write(PHY_CLK_CTRL,
+ twl4030_usb_read(PHY_CLK_CTRL) |
+ (PHY_CLK_CTRL_CLOCKGATING_EN |
+ PHY_CLK_CTRL_CLK32K_EN));
+ } else {
+ pwr |= PHY_PWR_PHYPWD;
+ if (twl4030_usb_write_verify(PHY_PWR_CTRL, pwr) < 0) {
+ printk(KERN_ERR "twl4030_usb: i2c write failed,"
+ " line %d\n", __LINE__);
+ }
+ }
+ return;
+}
+
+static void twl4030_phy_suspend(int controller_off)
+{
+ struct twl4030_usb *twl = the_transceiver;
+
+ if (controller_off)
+ usb_irq_disable();
+
+ if (twl->asleep)
+ return;
+
+ if (!controller_off)
+ /* enable rising edge interrupt to detect cable attach */
+ usb_irq_enable(1, 0);
+
+ twl4030_phy_power(twl, 0);
+ twl->asleep = 1;
+ return;
+}
+
+static void twl4030_phy_resume(void)
+{
+ struct twl4030_usb *twl = the_transceiver;
+
+ if (!twl->asleep)
+ return;
+
+ /* enable falling edge interrupt to detect cable detach */
+ usb_irq_enable(0, 1);
+
+ twl4030_phy_power(twl, 1);
+ twl4030_i2c_access(1);
+ twl4030_usb_set_mode(twl, twl->usb_mode);
+ if (twl->usb_mode == T2_USB_MODE_ULPI)
+ twl4030_i2c_access(0);
+ twl->asleep = 0;
+ return;
+}
+
+static void twl4030_usb_ldo_init(struct twl4030_usb *twl)
+{
+ /* Enable writing to power configuration registers */
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY);
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x0C, PROTECT_KEY);
+
+ /* put VUSB3V1 LDO in active state */
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);
+
+ /* input to VUSB3V1 LDO is from VBAT, not VBUS */
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
+
+ /* turn on 3.1V regulator */
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB3V1_DEV_GRP);
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);
+
+ /* turn on 1.5V regulator */
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V5_DEV_GRP);
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);
+
+ /* turn on 1.8V regulator */
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V8_DEV_GRP);
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
+
+ /* disable access to power configuration registers */
+ twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY);
+}
+
+static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
+{
+ int ret = IRQ_NONE;
+ u8 val;
+
+ /* action based on cable attach or detach */
+ if (twl4030_i2c_read_u8(TWL4030_MODULE_INT, &val, REG_PWR_EDR1) < 0) {
+ printk(KERN_ERR "twl4030_usb: i2c read failed,"
+ " line %d\n", __LINE__);
+ goto done;
+ }
+
+ if (val & USB_PRES_RISING) {
+ twl4030_phy_resume();
+ twl4030charger_usb_en(1);
+ } else {
+ twl4030charger_usb_en(0);
+ twl4030_phy_suspend(0);
+ }
+
+ ret = IRQ_HANDLED;
+
+done:
+ return ret;
+}
+
+static int twl4030_set_suspend(struct otg_transceiver *x, int suspend)
+{
+ if (suspend)
+ twl4030_phy_suspend(1);
+ else
+ twl4030_phy_resume();
+
+ return 0;
+}
+
+static int twl4030_set_peripheral(struct otg_transceiver *xceiv,
+ struct usb_gadget *gadget)
+{
+ u32 l;
+ struct twl4030_usb *twl = xceiv_to_twl(xceiv);
+
+ if (!xceiv)
+ return -ENODEV;
+
+ if (!gadget) {
+ omap_writew(0, OTG_IRQ_EN);
+ twl4030_phy_suspend(1);
+ twl->otg.gadget = NULL;
+
+ return -ENODEV;
+ }
+
+ twl->otg.gadget = gadget;
+ twl4030_phy_resume();
+
+ l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+ l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS);
+ l |= OTG_ID;
+ omap_writel(l, OTG_CTRL);
+
+ twl->otg.state = OTG_STATE_B_IDLE;
+
+ twl4030_usb_set_bits(twl, USB_INT_EN_RISE,
+ USB_INT_SESSVALID | USB_INT_VBUSVALID);
+ twl4030_usb_set_bits(twl, USB_INT_EN_FALL,
+ USB_INT_SESSVALID | USB_INT_VBUSVALID);
+
+ return 0;
+}
+
+static int twl4030_set_host(struct otg_transceiver *xceiv, struct usb_bus *host)
+{
+ struct twl4030_usb *twl = xceiv_to_twl(xceiv);
+
+ if (!xceiv)
+ return -ENODEV;
+
+ if (!host) {
+ omap_writew(0, OTG_IRQ_EN);
+ twl4030_phy_suspend(1);
+ twl->otg.host = NULL;
+
+ return -ENODEV;
+ }
+
+ twl->otg.host = host;
+ twl4030_phy_resume();
+
+ twl4030_usb_set_bits(twl, TWL4030_OTG_CTRL,
+ TWL4030_OTG_CTRL_DMPULLDOWN
+ | TWL4030_OTG_CTRL_DPPULLDOWN);
+ twl4030_usb_set_bits(twl, USB_INT_EN_RISE, USB_INT_IDGND);
+ twl4030_usb_set_bits(twl, USB_INT_EN_FALL, USB_INT_IDGND);
+ twl4030_usb_set_bits(twl, FUNC_CTRL, FUNC_CTRL_SUSPENDM);
+ twl4030_usb_set_bits(twl, TWL4030_OTG_CTRL, TWL4030_OTG_CTRL_DRVVBUS);
+
+ return 0;
+}
+
+static int __init twl4030_usb_init(void)
+{
+ struct twl4030_usb *twl;
+ int status;
+
+ if (the_transceiver)
+ return 0;
+
+ twl = kzalloc(sizeof *twl, GFP_KERNEL);
+ if (!twl)
+ return 0;
+
+ the_transceiver = twl;
+
+ twl->irq = TWL4030_PWRIRQ_USB_PRES;
+ twl->otg.set_host = twl4030_set_host;
+ twl->otg.set_peripheral = twl4030_set_peripheral;
+ twl->otg.set_suspend = twl4030_set_suspend;
+
+ usb_irq_disable();
+ status = request_irq(twl->irq, twl4030_usb_irq, 0, "twl4030_usb", twl);
+ if (status < 0) {
+ printk(KERN_DEBUG "can't get IRQ %d, err %d\n",
+ twl->irq, status);
+ kfree(twl);
+ return -ENODEV;
+ }
+
+#if defined(CONFIG_TWL4030_USB_HS_ULPI)
+ hs_usb_init(twl);
+#endif
+ twl4030_usb_ldo_init(twl);
+ twl4030_phy_power(twl, 1);
+ twl4030_i2c_access(1);
+ twl4030_usb_set_mode(twl, twl->usb_mode);
+ if (twl->usb_mode == T2_USB_MODE_ULPI)
+ twl4030_i2c_access(0);
+
+ twl->asleep = 0;
+
+ if (twl->usb_mode == T2_USB_MODE_ULPI)
+ twl4030_phy_suspend(1);
+
+ otg_set_transceiver(&twl->otg);
+
+ printk(KERN_INFO "Initialized TWL4030 USB module\n");
+
+ return 0;
+}
+
+
+static void __exit twl4030_usb_exit(void)
+{
+ struct twl4030_usb *twl = the_transceiver;
+ int val;
+
+ usb_irq_disable();
+ free_irq(twl->irq, twl);
+
+ /* set transceiver mode to power on defaults */
+ twl4030_usb_set_mode(twl, -1);
+
+ /* autogate 60MHz ULPI clock,
+ * clear dpll clock request for i2c access,
+ * disable 32KHz
+ */
+ val = twl4030_usb_read(PHY_CLK_CTRL);
+ if (val >= 0) {
+ val |= PHY_CLK_CTRL_CLOCKGATING_EN;
+ val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK);
+ twl4030_usb_write(PHY_CLK_CTRL, (u8)val);
+ }
+
+ /* disable complete OTG block */
+ twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
+
+ twl4030_phy_power(twl, 0);
+
+ kfree(twl);
+}
+
+subsys_initcall(twl4030_usb_init);
+module_exit(twl4030_usb_exit);
+
+MODULE_ALIAS("i2c:twl4030-usb");
+MODULE_AUTHOR("Texas Instruments, Inc.");
+MODULE_DESCRIPTION("TWL4030 USB transceiver driver");
+MODULE_LICENSE("GPL");
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
+#include <linux/spinlock.h>
#include <linux/errno.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/keypad.h>
+#include <linux/i2c/menelaus.h>
-#include <mach/menelaus.h>
+ #include <mach/gpio.h>
+ #include <mach/keypad.h>
#include <asm/irq.h>
- #include <asm/hardware.h>
+ #include <mach/hardware.h>
#include <asm/io.h>
- #include <asm/arch/mux.h>
+#include <asm/mach-types.h>
+ #include <mach/mux.h>
#undef NEW_BOARD_LEARNING_MODE
--- /dev/null
- #include <asm/arch/keypad.h>
+/*
+ * drivers/input/keyboard/omap-twl4030keypad.c
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Code re-written for 2430SDP by:
+ * Syed Mohammed Khasim <x0khasim@ti.com>
+ *
+ * Initial Code:
+ * Manjunatha G K <manjugk@ti.com>
+ *
+ * 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/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/irq.h>
++#include <mach/keypad.h>
+#include "twl4030-keypad.h"
+
+#define PTV_PRESCALER 4
+
+#define MAX_ROWS 8 /* TWL4030 hardlimit */
+#define ROWCOL_MASK 0xFF000000
+#define KEYNUM_MASK 0x00FFFFFF
+
+/* Global variables */
+static int *keymap;
+static u16 kp_state[MAX_ROWS];
+static int n_rows, n_cols;
+
+static struct device *dbg_dev;
+static struct input_dev *omap_twl4030kp;
+
+static int twl4030_kpread(u32 module, u8 *data, u32 reg, u8 num_bytes)
+{
+ int ret;
+
+ ret = twl4030_i2c_read(module, data, reg, num_bytes);
+ if (ret < 0) {
+ dev_warn(dbg_dev, "Couldn't read TWL4030: %X - ret %d[%x]\n",
+ reg, ret, ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int twl4030_kpwrite_u8(u32 module, u8 data, u32 reg)
+{
+ int ret;
+
+ ret = twl4030_i2c_write_u8(module, data, reg);
+ if (ret < 0) {
+ dev_warn(dbg_dev, "Could not write TWL4030: %X - ret %d[%x]\n",
+ reg, ret, ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int omap_kp_find_key(int col, int row)
+{
+ int i, rc;
+
+ rc = KEY(col, row, 0);
+ for (i = 0; keymap[i] != 0; i++)
+ if ((keymap[i] & ROWCOL_MASK) == rc)
+ return keymap[i] & KEYNUM_MASK;
+
+ return -EINVAL;
+}
+
+static inline u16 omap_kp_col_xlate(u8 col)
+{
+ /* If all bits in a row are active for all coloumns then
+ * we have that row line connected to gnd. Mark this
+ * key on as if it was on matrix position n_cols (ie
+ * one higher than the size of the matrix).
+ */
+ if (col == 0xFF)
+ return (1 << n_cols);
+ else
+ return col & ((1 << n_cols) - 1);
+}
+
+static int omap_kp_read_kp_matrix_state(u16 *state)
+{
+ u8 new_state[MAX_ROWS];
+ int row;
+ int ret = twl4030_kpread(TWL4030_MODULE_KEYPAD,
+ new_state, KEYP_FULL_CODE_7_0, n_rows);
+ if (ret >= 0) {
+ for (row = 0; row < n_rows; row++)
+ state[row] = omap_kp_col_xlate(new_state[row]);
+ }
+ return ret;
+}
+
+static int omap_kp_is_in_ghost_state(u16 *key_state)
+{
+ int i;
+ u16 check = 0;
+
+ for (i = 0; i < n_rows; i++) {
+ u16 col = key_state[i];
+
+ if ((col & check) && hweight16(col) > 1)
+ return 1;
+ check |= col;
+ }
+
+ return 0;
+}
+
+static void twl4030_kp_scan(int release_all)
+{
+ u16 new_state[MAX_ROWS];
+ int col, row;
+
+ if (release_all)
+ memset(new_state, 0, sizeof(new_state));
+ else {
+ /* check for any changes */
+ int ret = omap_kp_read_kp_matrix_state(new_state);
+ if (ret < 0) /* panic ... */
+ return;
+
+ if (omap_kp_is_in_ghost_state(new_state))
+ return;
+ }
+
+ /* check for changes and print those */
+ for (row = 0; row < n_rows; row++) {
+ int changed = new_state[row] ^ kp_state[row];
+
+ if (!changed)
+ continue;
+
+ for (col = 0; col < n_cols; col++) {
+ int key;
+
+ if (!(changed & (1 << col)))
+ continue;
+
+ dev_dbg(dbg_dev, "key [%d:%d] %s\n", row, col,
+ (new_state[row] & (1 << col)) ?
+ "press" : "release");
+
+ key = omap_kp_find_key(col, row);
+ if (key < 0)
+ dev_warn(dbg_dev, "Spurious key event %d-%d\n",
+ col, row);
+ else
+ input_report_key(omap_twl4030kp, key,
+ new_state[row] & (1 << col));
+ }
+ kp_state[row] = new_state[row];
+ }
+}
+
+/*
+ * Keypad interrupt handler
+ */
+static irqreturn_t do_kp_irq(int irq, void *dev_id)
+{
+ u8 reg;
+ int ret;
+
+ /* Read & Clear TWL4030 pending interrupt */
+ ret = twl4030_kpread(TWL4030_MODULE_KEYPAD, ®, KEYP_ISR1, 1);
+
+ /* Release all keys if I2C has gone bad or
+ * the KEYP has gone to idle state */
+ if ((ret >= 0) && (reg & KEYP_IMR1_KP))
+ twl4030_kp_scan(0);
+ else
+ twl4030_kp_scan(1);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Registers keypad device with input sub system
+ * and configures TWL4030 keypad registers
+ */
+static int __init omap_kp_probe(struct platform_device *pdev)
+{
+ u8 reg;
+ int i;
+ int ret = 0;
+ struct omap_kp_platform_data *pdata = pdev->dev.platform_data;
+
+ /* Get the debug Device */
+ dbg_dev = &(pdev->dev);
+
+ if (!pdata->rows || !pdata->cols || !pdata->keymap) {
+ dev_err(dbg_dev, "No rows, cols or keymap from pdata\n");
+ return -EINVAL;
+ }
+
+ omap_twl4030kp = input_allocate_device();
+ if (omap_twl4030kp == NULL)
+ return -ENOMEM;
+
+ keymap = pdata->keymap;
+ n_rows = pdata->rows;
+ n_cols = pdata->cols;
+
+ /* setup input device */
+ set_bit(EV_KEY, omap_twl4030kp->evbit);
+
+ /* Enable auto repeat feature of Linux input subsystem */
+ if (pdata->rep)
+ set_bit(EV_REP, omap_twl4030kp->evbit);
+
+ for (i = 0; keymap[i] != 0; i++)
+ set_bit(keymap[i] & KEYNUM_MASK, omap_twl4030kp->keybit);
+
+ omap_twl4030kp->name = "omap_twl4030keypad";
+ omap_twl4030kp->phys = "omap_twl4030keypad/input0";
+ omap_twl4030kp->dev.parent = &pdev->dev;
+
+ omap_twl4030kp->id.bustype = BUS_HOST;
+ omap_twl4030kp->id.vendor = 0x0001;
+ omap_twl4030kp->id.product = 0x0001;
+ omap_twl4030kp->id.version = 0x0003;
+
+ omap_twl4030kp->keycode = keymap;
+ omap_twl4030kp->keycodesize = sizeof(unsigned int);
+ omap_twl4030kp->keycodemax = pdata->keymapsize;
+
+ ret = input_register_device(omap_twl4030kp);
+ if (ret < 0) {
+ dev_err(dbg_dev, "Unable to register twl4030 keypad device\n");
+ goto err2;
+ }
+
+ /* Disable auto-repeat */
+ reg = KEYP_CTRL_NOAUTORPT;
+ ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg, KEYP_CTRL);
+ if (ret < 0)
+ goto err3;
+
+ /* Enable TO rising and KP rising and falling edge detection */
+ reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING;
+ ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg, KEYP_EDR);
+ if (ret < 0)
+ goto err3;
+
+ /* Set PTV prescaler Field */
+ reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT);
+ ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg, KEYP_LK_PTV);
+ if (ret < 0)
+ goto err3;
+
+ /* Set key debounce time to 20 ms */
+ i = KEYP_PERIOD_US(20000, PTV_PRESCALER);
+ ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, i, KEYP_DEB);
+ if (ret < 0)
+ goto err3;
+
+ /* Set timeout period to 100 ms */
+ i = KEYP_PERIOD_US(200000, PTV_PRESCALER);
+ ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD,
+ (i & 0xFF), KEYP_TIMEOUT_L);
+ if (ret < 0)
+ goto err3;
+
+ ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD,
+ (i >> 8), KEYP_TIMEOUT_H);
+ if (ret < 0)
+ goto err3;
+
+ /* Enable Clear-on-Read */
+ reg = KEYP_SIH_CTRL_COR | KEYP_SIH_CTRL_PEND_DIS;
+ ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD,
+ reg, KEYP_SIH_CTRL);
+ if (ret < 0)
+ goto err3;
+
+ /*
+ * This ISR will always execute in kernel thread context because of
+ * the need to access the TWL4030 over the I2C bus.
+ */
+ ret = request_irq(TWL4030_MODIRQ_KEYPAD, do_kp_irq,
+ IRQF_DISABLED, "TWL4030 Keypad", omap_twl4030kp);
+ if (ret < 0) {
+ dev_info(dbg_dev, "request_irq failed for irq no=%d\n",
+ TWL4030_MODIRQ_KEYPAD);
+ goto err3;
+ } else {
+ /* Enable KP and TO interrupts now. */
+ reg = ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
+ ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD,
+ reg, KEYP_IMR1);
+ if (ret < 0)
+ goto err5;
+ }
+
+ ret = omap_kp_read_kp_matrix_state(kp_state);
+ if (ret < 0)
+ goto err4;
+
+ return ret;
+err5:
+ /* mask all events - we don't care about the result */
+ (void) twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, 0xff, KEYP_IMR1);
+err4:
+ free_irq(TWL4030_MODIRQ_KEYPAD, NULL);
+err3:
+ input_unregister_device(omap_twl4030kp);
+err2:
+ input_free_device(omap_twl4030kp);
+ return -ENODEV;
+}
+
+static int omap_kp_remove(struct platform_device *pdev)
+{
+ free_irq(TWL4030_MODIRQ_KEYPAD, NULL);
+
+ input_unregister_device(omap_twl4030kp);
+ return 0;
+}
+
+
+static struct platform_driver omap_kp_driver = {
+ .probe = omap_kp_probe,
+ .remove = omap_kp_remove,
+ .driver = {
+ .name = "omap_twl4030keypad",
+ .owner = THIS_MODULE,
+ },
+};
+
+/*
+ * OMAP TWL4030 Keypad init
+ */
+static int __devinit omap_kp_init(void)
+{
+ return platform_driver_register(&omap_kp_driver);
+}
+
+static void __exit omap_kp_exit(void)
+{
+ platform_driver_unregister(&omap_kp_driver);
+}
+
+module_init(omap_kp_init);
+module_exit(omap_kp_exit);
+MODULE_ALIAS("platform:omap_twl4030keypad");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("OMAP TWL4030 Keypad Driver");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/gpio.h>
+/*
+ * TSC2005 touchscreen driver
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+
+#ifdef CONFIG_ARCH_OMAP
++#include <mach/gpio.h>
+#endif
+
+#include <linux/spi/tsc2005.h>
+
+/**
+ * The touchscreen interface operates as follows:
+ *
+ * Initialize:
+ * Request access to GPIO103 (DAV)
+ * tsc2005_dav_irq_handler will trigger when DAV line goes down
+ *
+ * 1) Pen is pressed against touchscreeen
+ * 2) TSC2005 performs AD conversion
+ * 3) After the conversion is done TSC2005 drives DAV line down
+ * 4) GPIO IRQ is received and tsc2005_dav_irq_handler is called
+ * 5) tsc2005_ts_irq_handler queues up an spi transfer to fetch
+ * the x, y, z1, z2 values
+ * 6) tsc2005_ts_rx() reports coordinates to input layer and
+ * sets up tsc2005_ts_timer() to be called after TSC2005_TS_SCAN_TIME
+ * 7) When the penup_timer expires, there have not been DAV interrupts
+ * during the last 20ms which means the pen has been lifted.
+ */
+
+#define TSC2005_VDD_LOWER_27
+
+#ifdef TSC2005_VDD_LOWER_27
+#define TSC2005_HZ (10000000)
+#else
+#define TSC2005_HZ (25000000)
+#endif
+
+#define TSC2005_CMD (0x80)
+#define TSC2005_REG (0x00)
+
+#define TSC2005_CMD_STOP (1)
+#define TSC2005_CMD_10BIT (0 << 2)
+#define TSC2005_CMD_12BIT (1 << 2)
+
+#define TSC2005_CMD_SCAN_XYZZ (0 << 3)
+#define TSC2005_CMD_SCAN_XY (1 << 3)
+#define TSC2005_CMD_SCAN_X (2 << 3)
+#define TSC2005_CMD_SCAN_Y (3 << 3)
+#define TSC2005_CMD_SCAN_ZZ (4 << 3)
+#define TSC2005_CMD_AUX_SINGLE (5 << 3)
+#define TSC2005_CMD_TEMP1 (6 << 3)
+#define TSC2005_CMD_TEMP2 (7 << 3)
+#define TSC2005_CMD_AUX_CONT (8 << 3)
+#define TSC2005_CMD_TEST_X_CONN (9 << 3)
+#define TSC2005_CMD_TEST_Y_CONN (10 << 3)
+/* command 11 reserved */
+#define TSC2005_CMD_TEST_SHORT (12 << 3)
+#define TSC2005_CMD_DRIVE_XX (13 << 3)
+#define TSC2005_CMD_DRIVE_YY (14 << 3)
+#define TSC2005_CMD_DRIVE_YX (15 << 3)
+
+#define TSC2005_REG_X (0 << 3)
+#define TSC2005_REG_Y (1 << 3)
+#define TSC2005_REG_Z1 (2 << 3)
+#define TSC2005_REG_Z2 (3 << 3)
+#define TSC2005_REG_AUX (4 << 3)
+#define TSC2005_REG_TEMP1 (5 << 3)
+#define TSC2005_REG_TEMP2 (6 << 3)
+#define TSC2005_REG_STATUS (7 << 3)
+#define TSC2005_REG_AUX_HIGH (8 << 3)
+#define TSC2005_REG_AUX_LOW (9 << 3)
+#define TSC2005_REG_TEMP_HIGH (10 << 3)
+#define TSC2005_REG_TEMP_LOW (11 << 3)
+#define TSC2005_REG_CFR0 (12 << 3)
+#define TSC2005_REG_CFR1 (13 << 3)
+#define TSC2005_REG_CFR2 (14 << 3)
+#define TSC2005_REG_FUNCTION (15 << 3)
+
+#define TSC2005_REG_PND0 (1 << 1)
+#define TSC2005_REG_READ (0x01)
+#define TSC2005_REG_WRITE (0x00)
+
+
+#define TSC2005_CFR0_LONGSAMPLING (1)
+#define TSC2005_CFR0_DETECTINWAIT (1 << 1)
+#define TSC2005_CFR0_SENSETIME_32US (0)
+#define TSC2005_CFR0_SENSETIME_96US (1 << 2)
+#define TSC2005_CFR0_SENSETIME_544US (1 << 3)
+#define TSC2005_CFR0_SENSETIME_2080US (1 << 4)
+#define TSC2005_CFR0_SENSETIME_2656US (0x001C)
+#define TSC2005_CFR0_PRECHARGE_20US (0x0000)
+#define TSC2005_CFR0_PRECHARGE_84US (0x0020)
+#define TSC2005_CFR0_PRECHARGE_276US (0x0040)
+#define TSC2005_CFR0_PRECHARGE_1044US (0x0080)
+#define TSC2005_CFR0_PRECHARGE_1364US (0x00E0)
+#define TSC2005_CFR0_STABTIME_0US (0x0000)
+#define TSC2005_CFR0_STABTIME_100US (0x0100)
+#define TSC2005_CFR0_STABTIME_500US (0x0200)
+#define TSC2005_CFR0_STABTIME_1MS (0x0300)
+#define TSC2005_CFR0_STABTIME_5MS (0x0400)
+#define TSC2005_CFR0_STABTIME_100MS (0x0700)
+#define TSC2005_CFR0_CLOCK_4MHZ (0x0000)
+#define TSC2005_CFR0_CLOCK_2MHZ (0x0800)
+#define TSC2005_CFR0_CLOCK_1MHZ (0x1000)
+#define TSC2005_CFR0_RESOLUTION12 (0x2000)
+#define TSC2005_CFR0_STATUS (0x4000)
+#define TSC2005_CFR0_PENMODE (0x8000)
+
+#define TSC2005_CFR0_INITVALUE (TSC2005_CFR0_STABTIME_1MS | \
+ TSC2005_CFR0_CLOCK_1MHZ | \
+ TSC2005_CFR0_RESOLUTION12 | \
+ TSC2005_CFR0_PRECHARGE_276US | \
+ TSC2005_CFR0_PENMODE)
+
+#define TSC2005_CFR1_BATCHDELAY_0MS (0x0000)
+#define TSC2005_CFR1_BATCHDELAY_1MS (0x0001)
+#define TSC2005_CFR1_BATCHDELAY_2MS (0x0002)
+#define TSC2005_CFR1_BATCHDELAY_4MS (0x0003)
+#define TSC2005_CFR1_BATCHDELAY_10MS (0x0004)
+#define TSC2005_CFR1_BATCHDELAY_20MS (0x0005)
+#define TSC2005_CFR1_BATCHDELAY_40MS (0x0006)
+#define TSC2005_CFR1_BATCHDELAY_100MS (0x0007)
+
+#define TSC2005_CFR1_INITVALUE (TSC2005_CFR1_BATCHDELAY_2MS)
+
+#define TSC2005_CFR2_MAVE_TEMP (0x0001)
+#define TSC2005_CFR2_MAVE_AUX (0x0002)
+#define TSC2005_CFR2_MAVE_Z (0x0004)
+#define TSC2005_CFR2_MAVE_Y (0x0008)
+#define TSC2005_CFR2_MAVE_X (0x0010)
+#define TSC2005_CFR2_AVG_1 (0x0000)
+#define TSC2005_CFR2_AVG_3 (0x0400)
+#define TSC2005_CFR2_AVG_7 (0x0800)
+#define TSC2005_CFR2_MEDIUM_1 (0x0000)
+#define TSC2005_CFR2_MEDIUM_3 (0x1000)
+#define TSC2005_CFR2_MEDIUM_7 (0x2000)
+#define TSC2005_CFR2_MEDIUM_15 (0x3000)
+
+#define TSC2005_CFR2_IRQ_DAV (0x4000)
+#define TSC2005_CFR2_IRQ_PEN (0x8000)
+#define TSC2005_CFR2_IRQ_PENDAV (0x0000)
+
+#define TSC2005_CFR2_INITVALUE (TSC2005_CFR2_IRQ_DAV | \
+ TSC2005_CFR2_MAVE_X | \
+ TSC2005_CFR2_MAVE_Y | \
+ TSC2005_CFR2_MAVE_Z | \
+ TSC2005_CFR2_MEDIUM_15 | \
+ TSC2005_CFR2_AVG_7)
+
+#define MAX_12BIT ((1 << 12) - 1)
+#define TS_SAMPLES 4
+#define TS_RECT_SIZE 8
+#define TSC2005_TS_PENUP_TIME 20
+
+static const u32 tsc2005_read_reg[] = {
+ (TSC2005_REG | TSC2005_REG_X | TSC2005_REG_READ) << 16,
+ (TSC2005_REG | TSC2005_REG_Y | TSC2005_REG_READ) << 16,
+ (TSC2005_REG | TSC2005_REG_Z1 | TSC2005_REG_READ) << 16,
+ (TSC2005_REG | TSC2005_REG_Z2 | TSC2005_REG_READ) << 16,
+};
+#define NUM_READ_REGS (sizeof(tsc2005_read_reg)/sizeof(tsc2005_read_reg[0]))
+
+struct tsc2005 {
+ struct spi_device *spi;
+
+ struct input_dev *idev;
+ char phys[32];
+ struct timer_list penup_timer;
+ spinlock_t lock;
+ struct mutex mutex;
+
+ struct spi_message read_msg;
+ struct spi_transfer read_xfer[NUM_READ_REGS];
+ u32 data[NUM_READ_REGS];
+
+ /* previous x,y,z */
+ int x;
+ int y;
+ int p;
+ /* average accumulators for each component */
+ int sample_cnt;
+ int avg_x;
+ int avg_y;
+ int avg_z1;
+ int avg_z2;
+ /* configuration */
+ int x_plate_ohm;
+ int hw_avg_max;
+ int stab_time;
+ int p_max;
+ int touch_pressure;
+ int irq;
+ s16 dav_gpio;
+ /* status */
+ u8 sample_sent;
+ u8 pen_down;
+ u8 disabled;
+ u8 disable_depth;
+ u8 spi_active;
+};
+
+static void tsc2005_cmd(struct tsc2005 *ts, u8 cmd)
+{
+ u16 data = TSC2005_CMD | TSC2005_CMD_12BIT | cmd;
+ struct spi_message msg;
+ struct spi_transfer xfer = { 0 };
+
+ xfer.tx_buf = &data;
+ xfer.rx_buf = NULL;
+ xfer.len = 1;
+ xfer.bits_per_word = 8;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+ spi_sync(ts->spi, &msg);
+}
+
+static void tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value)
+{
+ u32 tx;
+ struct spi_message msg;
+ struct spi_transfer xfer = { 0 };
+
+ tx = (TSC2005_REG | reg | TSC2005_REG_PND0 |
+ TSC2005_REG_WRITE) << 16;
+ tx |= value;
+
+ xfer.tx_buf = &tx;
+ xfer.rx_buf = NULL;
+ xfer.len = 4;
+ xfer.bits_per_word = 24;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+ spi_sync(ts->spi, &msg);
+}
+
+static void tsc2005_ts_update_pen_state(struct tsc2005 *ts,
+ int x, int y, int pressure)
+{
+ if (pressure) {
+ input_report_abs(ts->idev, ABS_X, x);
+ input_report_abs(ts->idev, ABS_Y, y);
+ input_report_abs(ts->idev, ABS_PRESSURE, pressure);
+ if (!ts->pen_down) {
+ input_report_key(ts->idev, BTN_TOUCH, 1);
+ ts->pen_down = 1;
+ }
+ } else {
+ input_report_abs(ts->idev, ABS_PRESSURE, 0);
+ if (ts->pen_down) {
+ input_report_key(ts->idev, BTN_TOUCH, 0);
+ ts->pen_down = 0;
+ }
+ }
+
+ input_sync(ts->idev);
+}
+
+/*
+ * This function is called by the SPI framework after the coordinates
+ * have been read from TSC2005
+ */
+static void tsc2005_ts_rx(void *arg)
+{
+ struct tsc2005 *ts = arg;
+ unsigned long flags;
+ int inside_rect, pressure_limit;
+ int x, y, z1, z2, pressure;
+
+ spin_lock_irqsave(&ts->lock, flags);
+
+ x = ts->data[0];
+ y = ts->data[1];
+ z1 = ts->data[2];
+ z2 = ts->data[3];
+
+ /* validate pressure and position */
+ if (x > MAX_12BIT || y > MAX_12BIT)
+ goto out;
+
+ /* skip coords if the pressure-components are out of range */
+ if (z1 < 100 || z2 > 4000)
+ goto out;
+
+ /* don't run average on the "pen down" event */
+ if (ts->sample_sent) {
+ ts->avg_x += x;
+ ts->avg_y += y;
+ ts->avg_z1 += z1;
+ ts->avg_z2 += z2;
+
+ if (++ts->sample_cnt < TS_SAMPLES)
+ goto out;
+
+ x = ts->avg_x / TS_SAMPLES;
+ y = ts->avg_y / TS_SAMPLES;
+ z1 = ts->avg_z1 / TS_SAMPLES;
+ z2 = ts->avg_z2 / TS_SAMPLES;
+ }
+
+ ts->sample_cnt = 0;
+ ts->avg_x = 0;
+ ts->avg_y = 0;
+ ts->avg_z1 = 0;
+ ts->avg_z2 = 0;
+
+ if (z1) {
+ pressure = x * (z2 - z1) / z1;
+ pressure = pressure * ts->x_plate_ohm / 4096;
+ } else
+ goto out;
+
+ pressure_limit = ts->sample_sent? ts->p_max: ts->touch_pressure;
+ if (pressure > pressure_limit)
+ goto out;
+
+ /* discard the event if it still is within the previous rect - unless
+ * if the pressure is harder, but then use previous x,y position */
+ inside_rect = (ts->sample_sent &&
+ x > (int)ts->x - TS_RECT_SIZE &&
+ x < (int)ts->x + TS_RECT_SIZE &&
+ y > (int)ts->y - TS_RECT_SIZE &&
+ y < (int)ts->y + TS_RECT_SIZE);
+ if (inside_rect)
+ x = ts->x, y = ts->y;
+
+ if (!inside_rect || pressure < ts->p) {
+ tsc2005_ts_update_pen_state(ts, x, y, pressure);
+ ts->sample_sent = 1;
+ ts->x = x;
+ ts->y = y;
+ ts->p = pressure;
+ }
+out:
+ ts->spi_active = 0;
+ spin_unlock_irqrestore(&ts->lock, flags);
+
+ /* kick pen up timer - to make sure it expires again(!) */
+ if (ts->sample_sent)
+ mod_timer(&ts->penup_timer,
+ jiffies + msecs_to_jiffies(TSC2005_TS_PENUP_TIME));
+}
+
+static void tsc2005_ts_penup_timer_handler(unsigned long data)
+{
+ struct tsc2005 *ts = (struct tsc2005 *)data;
+
+ if (ts->sample_sent) {
+ tsc2005_ts_update_pen_state(ts, 0, 0, 0);
+ ts->sample_sent = 0;
+ }
+}
+
+/*
+ * This interrupt is called when pen is down and coordinates are
+ * available. That is indicated by a falling edge on DAV line.
+ */
+static irqreturn_t tsc2005_ts_irq_handler(int irq, void *dev_id)
+{
+ struct tsc2005 *ts = dev_id;
+ int r;
+
+ if (ts->spi_active)
+ return IRQ_HANDLED;
+
+ ts->spi_active = 1;
+ r = spi_async(ts->spi, &ts->read_msg);
+ if (r)
+ dev_err(&ts->spi->dev, "ts: spi_async() failed");
+
+ /* kick pen up timer */
+ mod_timer(&ts->penup_timer,
+ jiffies + msecs_to_jiffies(TSC2005_TS_PENUP_TIME));
+
+ return IRQ_HANDLED;
+}
+
+static void tsc2005_ts_setup_spi_xfer(struct tsc2005 *ts)
+{
+ struct spi_message *m = &ts->read_msg;
+ struct spi_transfer *x = &ts->read_xfer[0];
+ int i;
+
+ spi_message_init(m);
+
+ for (i = 0; i < NUM_READ_REGS; i++, x++) {
+ x->tx_buf = &tsc2005_read_reg[i];
+ x->rx_buf = &ts->data[i];
+ x->len = 4;
+ x->bits_per_word = 24;
+ x->cs_change = i < (NUM_READ_REGS - 1);
+ spi_message_add_tail(x, m);
+ }
+
+ m->complete = tsc2005_ts_rx;
+ m->context = ts;
+}
+
+static ssize_t tsc2005_ts_pen_down_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tsc2005 *tsc = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", tsc->pen_down);
+}
+
+static DEVICE_ATTR(pen_down, S_IRUGO, tsc2005_ts_pen_down_show, NULL);
+
+static int tsc2005_configure(struct tsc2005 *tsc, int flags)
+{
+ tsc2005_write(tsc, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE);
+ tsc2005_write(tsc, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE);
+ tsc2005_write(tsc, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE);
+ tsc2005_cmd(tsc, flags);
+
+ return 0;
+}
+
+static void tsc2005_start_scan(struct tsc2005 *tsc)
+{
+ tsc2005_configure(tsc, TSC2005_CMD_SCAN_XYZZ);
+}
+
+static void tsc2005_stop_scan(struct tsc2005 *tsc)
+{
+ tsc2005_cmd(tsc, TSC2005_CMD_STOP);
+}
+
+/* Must be called with mutex held */
+static void tsc2005_disable(struct tsc2005 *ts)
+{
+ if (ts->disable_depth++ != 0)
+ return;
+
+ disable_irq(ts->irq);
+
+ /* wait until penup timer expire normally */
+ do {
+ msleep(4);
+ } while (ts->sample_sent);
+
+ tsc2005_stop_scan(ts);
+}
+
+static void tsc2005_enable(struct tsc2005 *ts)
+{
+ if (--ts->disable_depth != 0)
+ return;
+
+ enable_irq(ts->irq);
+
+ tsc2005_start_scan(ts);
+}
+
+static ssize_t tsc2005_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsc2005 *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t tsc2005_disable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct tsc2005 *tsc = dev_get_drvdata(dev);
+ unsigned long res;
+ int i;
+
+ i = strict_strtoul(buf, 10, &res);
+ i = i ? 1 : 0;
+
+ mutex_lock(&tsc->mutex);
+ if (i == tsc->disabled)
+ goto out;
+ tsc->disabled = i;
+
+ if (i)
+ tsc2005_disable(tsc);
+ else
+ tsc2005_enable(tsc);
+out:
+ mutex_unlock(&tsc->mutex);
+ return count;
+}
+
+static DEVICE_ATTR(disable_ts, 0664, tsc2005_disable_show,
+ tsc2005_disable_store);
+
+
+static int __devinit tsc2005_ts_init(struct tsc2005 *ts,
+ struct tsc2005_platform_data *pdata)
+{
+ struct input_dev *idev;
+ int dav_gpio, r;
+ int x_max, y_max;
+ int x_fudge, y_fudge, p_fudge;
+
+ if (pdata->dav_gpio < 0) {
+ dev_err(&ts->spi->dev, "need DAV GPIO");
+ return -EINVAL;
+ }
+ dav_gpio = pdata->dav_gpio;
+ ts->dav_gpio = dav_gpio;
+ dev_dbg(&ts->spi->dev, "TSC2005: DAV GPIO = %d\n", dav_gpio);
+
+#ifdef CONFIG_ARCH_OMAP
+ r = omap_request_gpio(dav_gpio);
+ if (r < 0) {
+ dev_err(&ts->spi->dev, "unable to get DAV GPIO");
+ goto err1;
+ }
+ omap_set_gpio_direction(dav_gpio, 1);
+ ts->irq = OMAP_GPIO_IRQ(dav_gpio);
+ dev_dbg(&ts->spi->dev, "TSC2005: DAV IRQ = %d\n", ts->irq);
+#endif
+ init_timer(&ts->penup_timer);
+ setup_timer(&ts->penup_timer, tsc2005_ts_penup_timer_handler,
+ (unsigned long)ts);
+
+ spin_lock_init(&ts->lock);
+ mutex_init(&ts->mutex);
+
+ ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280;
+ ts->hw_avg_max = pdata->ts_hw_avg;
+ ts->stab_time = pdata->ts_stab_time;
+ x_max = pdata->ts_x_max ? : 4096;
+ x_fudge = pdata->ts_x_fudge ? : 4;
+ y_max = pdata->ts_y_max ? : 4096;
+ y_fudge = pdata->ts_y_fudge ? : 8;
+ ts->p_max = pdata->ts_pressure_max ? : MAX_12BIT;
+ ts->touch_pressure = pdata->ts_touch_pressure ? : ts->p_max;
+ p_fudge = pdata->ts_pressure_fudge ? : 2;
+
+ idev = input_allocate_device();
+ if (idev == NULL) {
+ r = -ENOMEM;
+ goto err2;
+ }
+
+ idev->name = "TSC2005 touchscreen";
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input-ts",
+ ts->spi->dev.bus_id);
+ idev->phys = ts->phys;
+
+ idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+ idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+ ts->idev = idev;
+
+ tsc2005_ts_setup_spi_xfer(ts);
+
+ input_set_abs_params(idev, ABS_X, 0, x_max, x_fudge, 0);
+ input_set_abs_params(idev, ABS_Y, 0, y_max, y_fudge, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0, ts->p_max, p_fudge, 0);
+
+ tsc2005_start_scan(ts);
+
+ r = request_irq(ts->irq, tsc2005_ts_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_DISABLED |
+ IRQF_SAMPLE_RANDOM, "tsc2005", ts);
+ if (r < 0) {
+ dev_err(&ts->spi->dev, "unable to get DAV IRQ");
+ goto err3;
+ }
+
+ set_irq_wake(ts->irq, 1);
+
+ r = input_register_device(idev);
+ if (r < 0) {
+ dev_err(&ts->spi->dev, "can't register touchscreen device\n");
+ goto err4;
+ }
+
+ /* We can tolerate these failing */
+ if (device_create_file(&ts->spi->dev, &dev_attr_pen_down));
+ if (device_create_file(&ts->spi->dev, &dev_attr_disable_ts));
+
+ return 0;
+err4:
+ free_irq(ts->irq, ts);
+err3:
+ tsc2005_stop_scan(ts);
+ input_free_device(idev);
+err2:
+#ifdef CONFIG_ARCH_OMAP
+ omap_free_gpio(dav_gpio);
+#endif
+err1:
+ return r;
+}
+
+static int __devinit tsc2005_probe(struct spi_device *spi)
+{
+ struct tsc2005 *tsc;
+ struct tsc2005_platform_data *pdata = spi->dev.platform_data;
+ int r;
+
+ if (!pdata) {
+ dev_dbg(&spi->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
+ if (tsc == NULL)
+ return -ENOMEM;
+
+ dev_set_drvdata(&spi->dev, tsc);
+ tsc->spi = spi;
+ spi->dev.power.power_state = PMSG_ON;
+
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ /* The max speed might've been defined by the board-specific
+ * struct */
+ if (!spi->max_speed_hz)
+ spi->max_speed_hz = TSC2005_HZ;
+
+ spi_setup(spi);
+
+ r = tsc2005_ts_init(tsc, pdata);
+ if (r)
+ goto err1;
+
+ return 0;
+
+err1:
+ kfree(tsc);
+ return r;
+}
+
+static int __devexit tsc2005_remove(struct spi_device *spi)
+{
+ struct tsc2005 *ts = dev_get_drvdata(&spi->dev);
+
+ mutex_lock(&ts->mutex);
+ tsc2005_disable(ts);
+ mutex_unlock(&ts->mutex);
+
+ device_remove_file(&ts->spi->dev, &dev_attr_disable_ts);
+ device_remove_file(&ts->spi->dev, &dev_attr_pen_down);
+
+ free_irq(ts->irq, ts);
+ input_unregister_device(ts->idev);
+
+#ifdef CONFIG_ARCH_OMAP
+ omap_free_gpio(ts->dav_gpio);
+#endif
+ kfree(ts);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tsc2005_suspend(struct spi_device *spi, pm_message_t mesg)
+{
+ struct tsc2005 *ts = dev_get_drvdata(&spi->dev);
+
+ mutex_lock(&ts->mutex);
+ tsc2005_disable(ts);
+ mutex_unlock(&ts->mutex);
+
+ return 0;
+}
+
+static int tsc2005_resume(struct spi_device *spi)
+{
+ struct tsc2005 *ts = dev_get_drvdata(&spi->dev);
+
+ mutex_lock(&ts->mutex);
+ tsc2005_enable(ts);
+ mutex_unlock(&ts->mutex);
+
+ return 0;
+}
+#endif
+
+static struct spi_driver tsc2005_driver = {
+ .driver = {
+ .name = "tsc2005",
+ .owner = THIS_MODULE,
+ },
+#ifdef CONFIG_PM
+ .suspend = tsc2005_suspend,
+ .resume = tsc2005_resume,
+#endif
+ .probe = tsc2005_probe,
+ .remove = __devexit_p(tsc2005_remove),
+};
+
+static int __init tsc2005_init(void)
+{
+ printk(KERN_INFO "TSC2005 driver initializing\n");
+
+ return spi_register_driver(&tsc2005_driver);
+}
+module_init(tsc2005_init);
+
+static void __exit tsc2005_exit(void)
+{
+ spi_unregister_driver(&tsc2005_driver);
+}
+module_exit(tsc2005_exit);
+
+MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:tsc2005");
--- /dev/null
- #include <asm/arch/board.h>
- #include <asm/arch/dmtimer.h>
+/* drivers/leds/leds-omap_pwm.c
+ *
+ * Driver to blink LEDs using OMAP PWM timers
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Timo Teras
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/ctype.h>
+#include <linux/sched.h>
+#include <asm/delay.h>
++#include <mach/board.h>
++#include <mach/dmtimer.h>
+
+struct omap_pwm_led {
+ struct led_classdev cdev;
+ struct work_struct work;
+ struct omap_pwm_led_platform_data *pdata;
+ struct omap_dm_timer *intensity_timer;
+ struct omap_dm_timer *blink_timer;
+ int powered;
+ unsigned int on_period, off_period;
+ enum led_brightness brightness;
+};
+
+static inline struct omap_pwm_led *pdev_to_omap_pwm_led(struct platform_device *pdev)
+{
+ return platform_get_drvdata(pdev);
+}
+
+static inline struct omap_pwm_led *cdev_to_omap_pwm_led(struct led_classdev *led_cdev)
+{
+ return container_of(led_cdev, struct omap_pwm_led, cdev);
+}
+
+static inline struct omap_pwm_led *work_to_omap_pwm_led(struct work_struct *work)
+{
+ return container_of(work, struct omap_pwm_led, work);
+}
+
+static void omap_pwm_led_set_blink(struct omap_pwm_led *led)
+{
+ if (!led->powered)
+ return;
+
+ if (led->on_period != 0 && led->off_period != 0) {
+ unsigned long load_reg, cmp_reg;
+
+ load_reg = 32768 * (led->on_period + led->off_period) / 1000;
+ cmp_reg = 32768 * led->on_period / 1000;
+
+ omap_dm_timer_stop(led->blink_timer);
+ omap_dm_timer_set_load(led->blink_timer, 1, -load_reg);
+ omap_dm_timer_set_match(led->blink_timer, 1, -cmp_reg);
+ omap_dm_timer_set_pwm(led->blink_timer, 1, 1,
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+ omap_dm_timer_write_counter(led->blink_timer, -2);
+ omap_dm_timer_start(led->blink_timer);
+ } else {
+ omap_dm_timer_set_pwm(led->blink_timer, 1, 1,
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+ omap_dm_timer_stop(led->blink_timer);
+ }
+}
+
+static void omap_pwm_led_power_on(struct omap_pwm_led *led)
+{
+ if (led->powered)
+ return;
+ led->powered = 1;
+
+ /* Select clock */
+ omap_dm_timer_enable(led->intensity_timer);
+ omap_dm_timer_set_source(led->intensity_timer, OMAP_TIMER_SRC_32_KHZ);
+
+ /* Turn voltage on */
+ if (led->pdata->set_power != NULL)
+ led->pdata->set_power(led->pdata, 1);
+
+ /* Enable PWM timers */
+ if (led->blink_timer != NULL) {
+ omap_dm_timer_enable(led->blink_timer);
+ omap_dm_timer_set_source(led->blink_timer,
+ OMAP_TIMER_SRC_32_KHZ);
+ omap_pwm_led_set_blink(led);
+ }
+
+ omap_dm_timer_set_load(led->intensity_timer, 1, 0xffffff00);
+}
+
+static void omap_pwm_led_power_off(struct omap_pwm_led *led)
+{
+ if (!led->powered)
+ return;
+ led->powered = 0;
+
+ /* Everything off */
+ omap_dm_timer_stop(led->intensity_timer);
+ omap_dm_timer_disable(led->intensity_timer);
+
+ if (led->blink_timer != NULL) {
+ omap_dm_timer_stop(led->blink_timer);
+ omap_dm_timer_disable(led->blink_timer);
+ }
+
+ if (led->pdata->set_power != NULL)
+ led->pdata->set_power(led->pdata, 0);
+}
+
+static void omap_pwm_led_set_pwm_cycle(struct omap_pwm_led *led, int cycle)
+{
+ int n;
+
+ if (cycle == 0)
+ n = 0xff;
+ else n = cycle - 1;
+
+ if (cycle == LED_FULL) {
+ omap_dm_timer_set_pwm(led->intensity_timer, 1, 1,
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+ omap_dm_timer_stop(led->intensity_timer);
+ } else {
+ omap_dm_timer_set_pwm(led->intensity_timer, 0, 1,
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+ omap_dm_timer_set_match(led->intensity_timer, 1,
+ (0xffffff00) | cycle);
+ omap_dm_timer_start(led->intensity_timer);
+ }
+}
+
+static void omap_pwm_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+
+ led->brightness = value;
+ schedule_work(&led->work);
+}
+
+static void omap_pwm_led_work(struct work_struct *work)
+{
+ struct omap_pwm_led *led = work_to_omap_pwm_led(work);
+
+ if (led->brightness != LED_OFF) {
+ omap_pwm_led_power_on(led);
+ omap_pwm_led_set_pwm_cycle(led, led->brightness);
+ } else {
+ omap_pwm_led_power_off(led);
+ }
+}
+
+static ssize_t omap_pwm_led_on_period_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+
+ return sprintf(buf, "%u\n", led->on_period) + 1;
+}
+
+static ssize_t omap_pwm_led_on_period_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+ int ret = -EINVAL;
+ unsigned long val;
+ char *after;
+ size_t count;
+
+ val = simple_strtoul(buf, &after, 10);
+ count = after - buf;
+ if (*after && isspace(*after))
+ count++;
+
+ if (count == size) {
+ led->on_period = val;
+ omap_pwm_led_set_blink(led);
+ ret = count;
+ }
+
+ return ret;
+}
+
+static ssize_t omap_pwm_led_off_period_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+
+ return sprintf(buf, "%u\n", led->off_period) + 1;
+}
+
+static ssize_t omap_pwm_led_off_period_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+ int ret = -EINVAL;
+ unsigned long val;
+ char *after;
+ size_t count;
+
+ val = simple_strtoul(buf, &after, 10);
+ count = after - buf;
+ if (*after && isspace(*after))
+ count++;
+
+ if (count == size) {
+ led->off_period = val;
+ omap_pwm_led_set_blink(led);
+ ret = count;
+ }
+
+ return ret;
+}
+
+static DEVICE_ATTR(on_period, 0644, omap_pwm_led_on_period_show,
+ omap_pwm_led_on_period_store);
+static DEVICE_ATTR(off_period, 0644, omap_pwm_led_off_period_show,
+ omap_pwm_led_off_period_store);
+
+static int omap_pwm_led_probe(struct platform_device *pdev)
+{
+ struct omap_pwm_led_platform_data *pdata = pdev->dev.platform_data;
+ struct omap_pwm_led *led;
+ int ret;
+
+ led = kzalloc(sizeof(struct omap_pwm_led), GFP_KERNEL);
+ if (led == NULL) {
+ dev_err(&pdev->dev, "No memory for device\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, led);
+ led->cdev.brightness_set = omap_pwm_led_set;
+ led->cdev.default_trigger = NULL;
+ led->cdev.name = pdata->name;
+ led->pdata = pdata;
+ led->brightness = LED_OFF;
+ INIT_WORK(&led->work, omap_pwm_led_work);
+
+ dev_info(&pdev->dev, "OMAP PWM LED (%s) at GP timer %d/%d\n",
+ pdata->name, pdata->intensity_timer, pdata->blink_timer);
+
+ /* register our new led device */
+ ret = led_classdev_register(&pdev->dev, &led->cdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "led_classdev_register failed\n");
+ goto error_classdev;
+ }
+
+ /* get related dm timers */
+ led->intensity_timer = omap_dm_timer_request_specific(pdata->intensity_timer);
+ if (led->intensity_timer == NULL) {
+ dev_err(&pdev->dev, "failed to request intensity pwm timer\n");
+ ret = -ENODEV;
+ goto error_intensity;
+ }
+ omap_dm_timer_disable(led->intensity_timer);
+
+ if (pdata->blink_timer != 0) {
+ led->blink_timer = omap_dm_timer_request_specific(pdata->blink_timer);
+ if (led->blink_timer == NULL) {
+ dev_err(&pdev->dev, "failed to request blinking pwm timer\n");
+ ret = -ENODEV;
+ goto error_blink1;
+ }
+ omap_dm_timer_disable(led->blink_timer);
+
+ ret = device_create_file(led->cdev.dev,
+ &dev_attr_on_period);
+ if(ret)
+ goto error_blink2;
+
+ ret = device_create_file(led->cdev.dev,
+ &dev_attr_off_period);
+ if(ret)
+ goto error_blink3;
+
+ }
+
+ return 0;
+
+error_blink3:
+ device_remove_file(led->cdev.dev,
+ &dev_attr_on_period);
+error_blink2:
+ dev_err(&pdev->dev, "failed to create device file(s)\n");
+error_blink1:
+ omap_dm_timer_free(led->intensity_timer);
+error_intensity:
+ led_classdev_unregister(&led->cdev);
+error_classdev:
+ kfree(led);
+ return ret;
+}
+
+static int omap_pwm_led_remove(struct platform_device *pdev)
+{
+ struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
+
+ device_remove_file(led->cdev.dev,
+ &dev_attr_on_period);
+ device_remove_file(led->cdev.dev,
+ &dev_attr_off_period);
+ led_classdev_unregister(&led->cdev);
+
+ omap_pwm_led_set(&led->cdev, LED_OFF);
+ if (led->blink_timer != NULL)
+ omap_dm_timer_free(led->blink_timer);
+ omap_dm_timer_free(led->intensity_timer);
+ kfree(led);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int omap_pwm_led_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
+
+ led_classdev_suspend(&led->cdev);
+ return 0;
+}
+
+static int omap_pwm_led_resume(struct platform_device *pdev)
+{
+ struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
+
+ led_classdev_resume(&led->cdev);
+ return 0;
+}
+#else
+#define omap_pwm_led_suspend NULL
+#define omap_pwm_led_resume NULL
+#endif
+
+static struct platform_driver omap_pwm_led_driver = {
+ .probe = omap_pwm_led_probe,
+ .remove = omap_pwm_led_remove,
+ .suspend = omap_pwm_led_suspend,
+ .resume = omap_pwm_led_resume,
+ .driver = {
+ .name = "omap_pwm_led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap_pwm_led_init(void)
+{
+ return platform_driver_register(&omap_pwm_led_driver);
+}
+
+static void __exit omap_pwm_led_exit(void)
+{
+ platform_driver_unregister(&omap_pwm_led_driver);
+}
+
+module_init(omap_pwm_led_init);
+module_exit(omap_pwm_led_exit);
+
+MODULE_AUTHOR("Timo Teras");
+MODULE_DESCRIPTION("OMAP PWM LED driver");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/gpio.h>
- #include <asm/arch/hardware.h>
- #include <asm/arch/led.h>
+/* drivers/leds/leds-omap.c
+ *
+ * (C) 2006 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * OMAP - LEDs GPIO 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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+
++#include <mach/gpio.h>
++#include <mach/hardware.h>
++#include <mach/led.h>
+
+/* our context */
+
+static void omap_set_led_gpio(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct omap_led_config *led_dev;
+
+ led_dev = container_of(led_cdev, struct omap_led_config, cdev);
+
+ if (value)
+ omap_set_gpio_dataout(led_dev->gpio, 1);
+ else
+ omap_set_gpio_dataout(led_dev->gpio, 0);
+}
+
+static void omap_configure_led_gpio(int gpio)
+{
+ if (omap_request_gpio(gpio) < 0) {
+ printk(KERN_ERR "Failed to request GPIO%d for LEDs\n", gpio);
+ return;
+ }
+ omap_set_gpio_direction(gpio, 0); /* OUT */
+}
+
+static int omap_led_probe(struct platform_device *dev)
+{
+ struct omap_led_platform_data *pdata = dev->dev.platform_data;
+ struct omap_led_config *leds = pdata->leds;
+ int i, ret = 0;
+
+ for (i = 0; ret >= 0 && i < pdata->nr_leds; i++) {
+ omap_configure_led_gpio(leds[i].gpio);
+ if (!leds[i].cdev.brightness_set)
+ leds[i].cdev.brightness_set = omap_set_led_gpio;
+
+ ret = led_classdev_register(&dev->dev, &leds[i].cdev);
+ }
+
+ if (ret < 0 && i > 1) {
+ for (i = i - 2; i >= 0; i--)
+ led_classdev_unregister(&leds[i].cdev);
+ }
+
+ return ret;
+}
+
+static int omap_led_remove(struct platform_device *dev)
+{
+ struct omap_led_platform_data *pdata = dev->dev.platform_data;
+ struct omap_led_config *leds = pdata->leds;
+ int i;
+
+ for (i = 0; i < pdata->nr_leds; i++)
+ led_classdev_unregister(&leds[i].cdev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int omap_led_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct omap_led_platform_data *pdata = dev->dev.platform_data;
+ struct omap_led_config *leds = pdata->leds;
+ int i;
+
+ for (i = 0; i < pdata->nr_leds; i++)
+ led_classdev_suspend(&leds[i].cdev);
+
+ return 0;
+}
+
+static int omap_led_resume(struct platform_device *dev)
+{
+ struct omap_led_platform_data *pdata = dev->dev.platform_data;
+ struct omap_led_config *leds = pdata->leds;
+ int i;
+
+ for (i = 0; i < pdata->nr_leds; i++)
+ led_classdev_resume(&leds[i].cdev);
+
+ return 0;
+}
+#else
+#define omap_led_suspend NULL
+#define omap_led_resume NULL
+#endif
+
+static struct platform_driver omap_led_driver = {
+ .probe = omap_led_probe,
+ .remove = omap_led_remove,
+ .suspend = omap_led_suspend,
+ .resume = omap_led_resume,
+ .driver = {
+ .name = "omap-led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap_led_init(void)
+{
+ return platform_driver_register(&omap_led_driver);
+}
+
+static void __exit omap_led_exit(void)
+{
+ platform_driver_unregister(&omap_led_driver);
+}
+
+module_init(omap_led_init);
+module_exit(omap_led_exit);
+
+MODULE_AUTHOR("Kyungmin Park<kyungmin.park@samsung.com>");
+MODULE_DESCRIPTION("OMAP LED driver");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/irqs.h>
- #include <asm/arch/dma.h>
- #include <asm/arch/hardware.h>
+/*
+ * drivers/media/video/omap/omap16xxcam.c
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Video-for-Linux (Version 2) camera capture driver for
+ * the OMAP H2 and H3 camera controller.
+ *
+ * leverage some code from CEE distribution
+ * Copyright (C) 2003-2004 MontaVista Software, Inc.
+ *
+ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+
++#include <mach/irqs.h>
++#include <mach/dma.h>
++#include <mach/hardware.h>
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <asm/mach-types.h>
+
+#include "omap16xxcam.h"
+#include "camera_hw_if.h"
+#include "camera_core.h"
+
+#define CONF_CAMERAIF_RESET_R 5
+#define EN_PER 0
+
+/* NUM_CAMDMA_CHANNELS is the number of logical channels used for
+ * DMA data transfer.
+ */
+#define NUM_CAMDMA_CHANNELS 2
+
+typedef struct {
+ unsigned int ctrlclock; /* 00 */
+ unsigned int it_status; /* 04 */
+ unsigned int mode; /* 08 */
+ unsigned int status; /* 0C */
+ unsigned int camdata; /* 10 */
+ unsigned int gpio; /* 14 */
+ unsigned int peak_counter; /* 18 */
+} camera_regs_t;
+
+struct camdma_state {
+ dma_callback_t callback;
+ void *arg1;
+ void *arg2;
+};
+
+struct omap16xxcam {
+ camera_regs_t *camera_regs;
+ unsigned long iobase_phys;
+
+ /* Frequency (in Hz) of camera interface functional clock (ocp_clk) */
+ unsigned long ocp_clk;
+
+ struct clk *func_clk;
+
+ /* DMA related stuff */
+ spinlock_t dma_lock;
+ int free_dmach;
+ int next_dmach;
+ struct camdma_state camdma[NUM_CAMDMA_CHANNELS];
+ int dma_channel_number1;
+ int dma_channel_number2;
+
+ wait_queue_head_t vsync_wait;
+
+ int new;
+};
+static struct omap16xxcam hardware_data;
+
+static int omap16xxcam_set_xclk(int, void *);
+static void omap16xx_cam_dma_link_callback(int, unsigned short, void *);
+
+/* Clears the camera data FIFO by setting RAZ_FIFO bit in MODE configuration
+ * register.
+ */
+static void omap16xx_cam_clear_fifo(struct omap16xxcam *data)
+{
+ data->camera_regs->mode |= RAZ_FIFO;
+ udelay(10);
+ data->camera_regs->mode &= ~RAZ_FIFO;
+}
+
+static void omap16xx_cam_reset(struct omap16xxcam *data, int yes)
+{
+ if (machine_is_omap_h3())
+ data->camera_regs->gpio = yes ? 0 : 1;
+ else
+ data->camera_regs->gpio = yes ? 1 : 0;
+}
+
+static void omap16xx_cam_init(void)
+{
+ /*
+ * FIXME - Use mux API's instead of directly writing in to MUX registers
+ */
+ omap_writel(omap_readl(FUNC_MUX_CTRL_4) & ~(0x1ff << 21),
+ FUNC_MUX_CTRL_4);
+ omap_writel(0, FUNC_MUX_CTRL_5);
+ omap_writel(omap_readl(PULL_DWN_CTRL_0) & ~(0x1FFF << 17),
+ PULL_DWN_CTRL_0);
+ omap_writel(omap_readl(PU_PD_SEL_0) & ~(0x1FFF << 17), PU_PD_SEL_0);
+
+ omap_writel(0xeaef, COMP_MODE_CTRL_0);
+ omap_writel(omap_readl(OMAP1610_RESET_CONTROL) &
+ ~(1 << CONF_CAMERAIF_RESET_R), OMAP1610_RESET_CONTROL);
+ omap_writel(omap_readl(OMAP1610_RESET_CONTROL) |
+ (1 << CONF_CAMERAIF_RESET_R), OMAP1610_RESET_CONTROL);
+
+ /* Enable peripheral reset */
+ omap_writew(omap_readw(ARM_RSTCT2) | (1 << EN_PER), ARM_RSTCT2);
+
+ /* Enable peripheral clock */
+ clk_enable(hardware_data.func_clk);
+}
+
+static void omap16xx_cam_waitfor_syncedge(struct omap16xxcam *data,
+ u32 edge_mask)
+{
+ data->camera_regs->mode =
+ (FIFO_TRIGGER_LVL << THRESHOLD_BIT) | edge_mask;
+ do {
+ interruptible_sleep_on(&data->vsync_wait);
+ } while (signal_pending(current));
+}
+
+static void omap16xx_cam_configure_dma(struct omap16xxcam *data)
+{
+
+ data->camera_regs->mode = (FIFO_TRIGGER_LVL << THRESHOLD_BIT)
+ | EN_DMA | EN_FIFO_FULL;
+ data->camera_regs->ctrlclock |= LCLK_EN;
+}
+
+/* Acquire h/w resources DMA */
+static int omap16xx_cam_link_open(struct omap16xxcam *data)
+{
+ int ret;
+
+ /* Acquire first DMA channel */
+ ret = omap_request_dma(OMAP_DMA_CAMERA_IF_RX,
+ "camera dma 1",
+ omap16xx_cam_dma_link_callback,
+ (void *)data, &data->dma_channel_number1);
+ if (ret)
+ return ret;
+
+ /* Acquire second DMA channel */
+ ret = omap_request_dma(OMAP_DMA_CAMERA_IF_RX,
+ "camera dma 2",
+ omap16xx_cam_dma_link_callback,
+ (void *)data, &data->dma_channel_number2);
+ if (ret) {
+ printk(KERN_ERR "No DMA available for camera\n");
+ return ret;
+ }
+ data->next_dmach = data->dma_channel_number1;
+ OMAP_DMA_CLNK_CTRL_REG(data->dma_channel_number1) =
+ data->dma_channel_number2;
+ OMAP_DMA_CLNK_CTRL_REG(data->dma_channel_number2) =
+ data->dma_channel_number1;
+
+ return 0;
+}
+
+/* Free h/w resources, stop i/f */
+static int omap16xx_cam_link_close(struct omap16xxcam *data)
+{
+ /* Free DMA channels */
+ omap_stop_dma(data->dma_channel_number1);
+ omap_stop_dma(data->dma_channel_number2);
+
+ omap_free_dma(data->dma_channel_number1);
+ omap_free_dma(data->dma_channel_number2);
+
+ return 0;
+}
+
+/* DMA callback routine. */
+static void omap16xx_cam_dma_link_callback(int lch, unsigned short ch_status,
+ void *data)
+{
+ int count;
+ void *arg1, *arg2;
+ struct sgdma_state *sgdma = sgdma;
+ struct omap16xxcam *cam = (struct omap16xxcam *)data;
+ dma_callback_t callback;
+
+ spin_lock(&cam->dma_lock);
+ if (cam->free_dmach == 2) {
+ printk(KERN_ERR "callback all CHANNELS WERE IDLE \n");
+ spin_unlock(&cam->dma_lock);
+ return;
+ }
+ if (cam->free_dmach == 0) {
+ lch = cam->next_dmach;
+ } else {
+ lch = cam->next_dmach == cam->dma_channel_number1 ?
+ cam->dma_channel_number2 : cam->dma_channel_number1;
+ }
+
+ while (cam->free_dmach < 2) {
+ if (OMAP_DMA_CCR_REG(lch) & (1 << 7))
+ break;
+
+ count = (lch == cam->dma_channel_number2) ? 1 : 0;
+
+ callback = cam->camdma[count].callback;
+ arg1 = cam->camdma[count].arg1;
+ arg2 = cam->camdma[count].arg2;
+ cam->free_dmach++;
+
+ spin_unlock(&cam->dma_lock);
+ callback(arg1, arg2);
+ spin_lock(&cam->dma_lock);
+
+ lch =
+ (lch ==
+ cam->dma_channel_number2) ? cam->
+ dma_channel_number1 : cam->dma_channel_number2;
+ }
+ spin_unlock(&cam->dma_lock);
+
+}
+
+static irqreturn_t omap16xx_cam_isr(int irq, void *client_data)
+{
+ struct omap16xxcam *data = (struct omap16xxcam *)client_data;
+ unsigned int itstat = data->camera_regs->it_status;
+
+ /* VSYNC UP interrupt, start filling FIFO and enabling DMA */
+ if (itstat & V_UP) {
+ data->camera_regs->mode &= ~EN_V_UP;
+ omap16xx_cam_clear_fifo(data);
+ omap16xx_cam_configure_dma(data);
+ omap_start_dma(data->next_dmach);
+ wake_up_interruptible(&data->vsync_wait);
+ }
+
+ if (itstat & V_DOWN) {
+ data->camera_regs->mode &= ~EN_V_DOWN;
+ wake_up_interruptible(&data->vsync_wait);
+ }
+
+ if (itstat & H_UP)
+ printk(KERN_INFO "H_UP\n");
+
+ if (itstat & H_DOWN)
+ printk(KERN_INFO "H_DOWN\n");
+
+ if (itstat & FIFO_FULL) {
+ omap16xx_cam_clear_fifo(data);
+ printk(KERN_INFO "FIFO_FULL\n");
+ }
+
+ if (itstat & DATA_XFER)
+ printk(KERN_INFO "DATA_TRANS\n");
+
+ return IRQ_HANDLED;
+}
+
+/* ------------- Below are interface functions -----------------
+ * ------------- These functions are named omap16xxcam_<name> --
+ */
+static int omap16xxcam_init_dma(void *priv)
+{
+ int ch;
+ struct omap16xxcam *data = (struct omap16xxcam *)priv;
+
+ data->free_dmach = 2;
+ for (ch = 0; ch < 2; ++ch) {
+ data->camdma[ch].callback = NULL;
+ data->camdma[ch].arg1 = NULL;
+ data->camdma[ch].arg2 = NULL;
+ }
+
+ return 0;
+}
+
+/* Start the DMA of chains */
+static int omap16xxcam_start_dma(struct sgdma_state *sgdma,
+ dma_callback_t callback, void *arg1,
+ void *arg2, void *priv)
+{
+ struct omap16xxcam *data = (struct omap16xxcam *)priv;
+ struct scatterlist *sglist;
+ unsigned long irqflags;
+ int dmach;
+ int prev_dmach;
+ int count;
+
+ spin_lock_irqsave(&data->dma_lock, irqflags);
+ sglist = (struct scatterlist *)(sgdma->sglist + sgdma->next_sglist);
+
+ if (!data->free_dmach) {
+ spin_unlock_irqrestore(&data->dma_lock, irqflags);
+ return -EBUSY;
+ }
+ dmach = data->next_dmach;
+ count = (dmach == data->dma_channel_number2) ? 1 : 0;
+ data->camdma[count].callback = callback;
+ data->camdma[count].arg1 = arg1;
+ data->camdma[count].arg2 = arg2;
+
+ if (cpu_is_omap1710())
+ omap_set_dma_src_params(dmach, OMAP_DMA_PORT_OCP_T1,
+ OMAP_DMA_AMODE_CONSTANT,
+ CAM_CAMDATA_REG, 0, 0);
+ else
+ omap_set_dma_src_params(dmach, OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT,
+ CAM_CAMDATA_REG, 0, 0);
+
+ omap_set_dma_dest_params(dmach, OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC,
+ sg_dma_address(sglist), 0, 0);
+
+ omap_set_dma_transfer_params(dmach, OMAP_DMA_DATA_TYPE_S32,
+ FIFO_TRIGGER_LVL,
+ sg_dma_len(sglist) / (4 *
+ FIFO_TRIGGER_LVL),
+ OMAP_DMA_SYNC_FRAME, 0, 0);
+
+ OMAP_DMA_CLNK_CTRL_REG(dmach) &= ~(1 << 15);
+
+ prev_dmach = (dmach == data->dma_channel_number2) ?
+ data->dma_channel_number1 : data->dma_channel_number2;
+
+ if (data->new) {
+ data->new = 0;
+ omap16xx_cam_waitfor_syncedge(data, EN_V_UP);
+ } else {
+ if (OMAP_DMA_CCR_REG(prev_dmach) & (1 << 7))
+ OMAP_DMA_CLNK_CTRL_REG(prev_dmach) |= (1 << 15);
+ else {
+ /* No transfer is in progress */
+ omap_start_dma(dmach);
+ }
+ }
+
+ data->next_dmach = prev_dmach;
+ data->free_dmach--;
+ spin_unlock_irqrestore(&data->dma_lock, irqflags);
+ return 0;
+}
+
+int static omap16xxcam_finish_dma(void *priv)
+{
+ struct omap16xxcam *data = (struct omap16xxcam *)priv;
+
+ while (data->free_dmach < 2)
+ mdelay(1);
+
+ return 0;
+}
+
+/* Enables the camera. Takes camera out of reset. Enables the clocks. */
+static int omap16xxcam_enable(void *priv)
+{
+ struct omap16xxcam *data = (struct omap16xxcam *)priv;
+
+ omap16xx_cam_reset(data, 1);
+
+ /* Give clock to camera_module */
+ data->camera_regs->mode = (FIFO_TRIGGER_LVL << THRESHOLD_BIT);
+ data->camera_regs->ctrlclock = MCLK_EN | CAMEXCLK_EN;
+
+ omap16xx_cam_clear_fifo(data);
+
+ /* Wait for camera to settle down */
+ mdelay(5);
+
+ return 0;
+}
+
+/* Disables all the camera clocks. Put the camera interface in reset. */
+static int omap16xxcam_disable(void *priv)
+{
+ struct omap16xxcam *data = (struct omap16xxcam *)priv;
+
+ omap16xx_cam_clear_fifo(data);
+
+ data->camera_regs->ctrlclock = 0x00000000;
+ data->camera_regs->mode = 0x00000000;
+
+ omap16xx_cam_reset(data, 0);
+
+ return 0;
+}
+
+/* Abort the data transfer */
+static int omap16xxcam_abort(void *priv)
+{
+ return omap16xxcam_disable(priv);
+}
+
+static int omap16xxcam_set_xclk(int xclk, void *priv)
+{
+ struct omap16xxcam *data = (struct omap16xxcam *)priv;
+ int xclk_val;
+ int divisor = 1;
+ divisor = data->ocp_clk / xclk;
+ if (divisor * xclk < data->ocp_clk)
+ ++divisor;
+
+ switch (divisor) {
+ case 1:
+ case 2:
+ xclk_val = FOSCMOD_TC2_CK2;
+ break;
+ case 3:
+ xclk_val = FOSCMOD_TC2_CK3;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ xclk_val = FOSCMOD_TC2_CK4;
+ break;
+ case 8:
+ case 9:
+ xclk_val = FOSCMOD_TC2_CK8;
+ break;
+ case 10:
+ case 11:
+ xclk_val = FOSCMOD_TC2_CK10;
+ break;
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ xclk_val = FOSCMOD_TC2_CK12;
+ break;
+ case 16:
+ xclk_val = FOSCMOD_TC2_CK16;
+ break;
+ default:
+ xclk_val = FOSCMOD_TC2_CK16;
+ }
+
+ /* Follow the protocol to change the XCLK clock */
+ data->camera_regs->ctrlclock &= ~CAMEXCLK_EN;
+ data->camera_regs->ctrlclock |= xclk_val;
+ data->camera_regs->ctrlclock |= CAMEXCLK_EN;
+
+ return (data->ocp_clk / divisor);
+}
+
+static int omap16xxcam_open(void *priv)
+{
+ struct omap16xxcam *data = (struct omap16xxcam *)priv;
+ int ret;
+
+ ret = request_irq(INT_CAMERA, omap16xx_cam_isr, IRQF_DISABLED,
+ "camera", data);
+ if (ret) {
+ printk(KERN_ERR "FAILED to acquire IRQ\n");
+ return ret;
+ }
+
+ data->new = 1;
+ omap16xxcam_enable(data);
+ omap16xxcam_init_dma(data);
+
+ return omap16xx_cam_link_open(data);
+}
+
+static int omap16xxcam_close(void *priv)
+{
+ struct omap16xxcam *data = (struct omap16xxcam *)priv;
+
+ omap16xxcam_disable(priv);
+
+ free_irq(INT_CAMERA, data);
+
+ return omap16xx_cam_link_close(data);
+}
+
+static int omap16xxcam_cleanup(void *priv)
+{
+ struct omap16xxcam *data = (struct omap16xxcam *)priv;
+
+ if (!data->camera_regs)
+ return -EINVAL;
+
+ omap16xxcam_disable(data);
+ if (cpu_is_omap1710())
+ iounmap((void *)data->camera_regs);
+ data->camera_regs = NULL;
+
+ if (data->iobase_phys) {
+ release_mem_region(data->iobase_phys, CAMERA_IOSIZE);
+ data->iobase_phys = 0;
+ }
+
+ if (hardware_data.func_clk) {
+ clk_disable(hardware_data.func_clk);
+ clk_put(hardware_data.func_clk);
+ hardware_data.func_clk = NULL;
+ }
+
+ return 0;
+}
+
+/* Initialize the OMAP camera interface */
+static void *omap16xxcam_init(void)
+{
+ unsigned long cam_iobase;
+
+ if (!request_mem_region(CAMERA_BASE, CAMERA_IOSIZE,
+ camera_hardware_if.name)) {
+ pr_debug("%s is already in use\n", camera_hardware_if.name);
+ return NULL;
+ }
+
+ if (cpu_is_omap1710()) {
+ cam_iobase = (unsigned long)ioremap(CAMERA_BASE, CAMERA_IOSIZE);
+ if (!cam_iobase) {
+ printk(KERN_ERR "CANNOT MAP CAMERA REGISTER\n");
+ return NULL;
+ }
+ } else
+ cam_iobase = io_p2v(CAMERA_BASE);
+
+ /* Set the base address of the camera registers */
+ hardware_data.camera_regs = (camera_regs_t *) cam_iobase;
+ hardware_data.iobase_phys = (unsigned long)CAMERA_BASE;
+
+ /* Get the input clock value to camera interface and store it */
+ if (cpu_is_omap1710())
+ hardware_data.func_clk = clk_get(0, "tc2_ck");
+ else
+ hardware_data.func_clk = clk_get(0, "armper_ck");
+ hardware_data.ocp_clk = clk_get_rate(hardware_data.func_clk);
+
+ /* Initialize the camera IF */
+ omap16xx_cam_init();
+ /* Enable it. This is needed for sensor detection */
+ omap16xxcam_enable((void *)&hardware_data);
+ /* Initialize DMA data */
+ spin_lock_init(&hardware_data.dma_lock);
+
+ init_waitqueue_head(&hardware_data.vsync_wait);
+ return (void *)&hardware_data;
+}
+
+struct camera_hardware camera_hardware_if = {
+ .version = 0x01,
+ .name = "OMAP16xx Parallel Camera",
+ .init = omap16xxcam_init,
+ .cleanup = omap16xxcam_cleanup,
+ .open = omap16xxcam_open,
+ .close = omap16xxcam_close,
+ .enable = omap16xxcam_enable,
+ .disable = omap16xxcam_disable,
+ .abort = omap16xxcam_abort,
+ .set_xclk = omap16xxcam_set_xclk,
+ .init_dma = omap16xxcam_init_dma,
+ .start_dma = omap16xxcam_start_dma,
+ .finish_dma = omap16xxcam_finish_dma,
+};
--- /dev/null
- #include <asm/arch/sti.h>
+/*
+ * Support functions for OMAP3 SDTI (Serial Debug Tracing Interface)
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by: Roman Tereshonkov <roman.tereshonkov@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
++#include <mach/sti.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+#define SDTI_REVISION 0x000
+#define SDTI_SYSCONFIG 0x010
+#define SDTI_SYSSTATUS 0x014
+#define SDTI_WINCTRL 0x024
+#define SDTI_SCONFIG 0x028
+#define SDTI_TESTCTRL 0x02C
+#define SDTI_LOCK_ACCESS 0xFB0
+
+#define CPU1_TRACE_EN 0x01
+#define CPU2_TRACE_EN 0x02
+
+static struct clk *sdti_ck;
+unsigned long sti_base, sti_channel_base;
+static DEFINE_SPINLOCK(sdti_lock);
+
+void omap_sti_channel_write_trace(int len, int id, void *data,
+ unsigned int channel)
+{
+ const u8 *tpntr = data;
+
+ spin_lock_irq(&sdti_lock);
+
+ sti_channel_writeb(id, channel);
+ while (len--)
+ sti_channel_writeb(*tpntr++, channel);
+ sti_channel_flush(channel);
+
+ spin_unlock_irq(&sdti_lock);
+}
+EXPORT_SYMBOL(omap_sti_channel_write_trace);
+
+static void omap_sdti_reset(void)
+{
+ int i;
+
+ sti_writel(0x02, SDTI_SYSCONFIG);
+
+ for (i = 0; i < 10000; i++)
+ if (sti_readl(SDTI_SYSSTATUS) & 1)
+ break;
+ if (i == 10000)
+ printk(KERN_WARNING "XTI: no real reset\n");
+}
+
+static int __init omap_sdti_init(void)
+{
+ char buf[64];
+ int i;
+
+ sdti_ck = clk_get(NULL, "emu_per_alwon_ck");
+ if (IS_ERR(sdti_ck)) {
+ printk(KERN_ERR "Cannot get clk emu_per_alwon_ck\n");
+ return PTR_ERR(sdti_ck);
+ }
+ clk_enable(sdti_ck);
+
+ omap_sdti_reset();
+ sti_writel(0xC5ACCE55, SDTI_LOCK_ACCESS);
+
+ /* Claim SDTI */
+ sti_writel(1 << 30, SDTI_WINCTRL);
+ i = sti_readl(SDTI_WINCTRL);
+ if (!(i & (1 << 30)))
+ printk(KERN_WARNING "SDTI: cannot claim SDTI\n");
+
+ /* 4 bits dual, fclk/3 */
+ sti_writel(0x43, SDTI_SCONFIG);
+
+ /* CPU2 trace enable */
+ sti_writel(i | CPU2_TRACE_EN, SDTI_WINCTRL);
+ i = sti_readl(SDTI_WINCTRL);
+
+ /* Enable SDTI */
+ sti_writel((1 << 31) | (i & 0x3FFFFFFF), SDTI_WINCTRL);
+
+ i = sti_readl(SDTI_REVISION);
+ snprintf(buf, sizeof(buf), "OMAP SDTI support loaded (HW v%u.%u)\n",
+ (i >> 4) & 0x0f, i & 0x0f);
+ printk(KERN_INFO "%s", buf);
+ omap_sti_channel_write_trace(strlen(buf), 0xc3, buf, 239);
+
+ return 0;
+}
+
+static void omap_sdti_exit(void)
+{
+ sti_writel(0, SDTI_WINCTRL);
+ clk_disable(sdti_ck);
+ clk_put(sdti_ck);
+}
+
+static int __devinit omap_sdti_probe(struct platform_device *pdev)
+{
+ struct resource *res, *cres;
+ unsigned int size;
+
+ if (pdev->num_resources != 2) {
+ dev_err(&pdev->dev, "invalid number of resources: %d\n",
+ pdev->num_resources);
+ return -ENODEV;
+ }
+
+ /* SDTI base */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!res)) {
+ dev_err(&pdev->dev, "invalid mem resource\n");
+ return -ENODEV;
+ }
+
+ /* Channel base */
+ cres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (unlikely(!cres)) {
+ dev_err(&pdev->dev, "invalid channel mem resource\n");
+ return -ENODEV;
+ }
+
+ size = res->end - res->start;
+ sti_base = (unsigned long)ioremap(res->start, size);
+ if (unlikely(!sti_base))
+ return -ENODEV;
+
+ size = cres->end - cres->start;
+ sti_channel_base = (unsigned long)ioremap(cres->start, size);
+ if (unlikely(!sti_channel_base)) {
+ iounmap((void *)sti_base);
+ return -ENODEV;
+ }
+
+ return omap_sdti_init();
+}
+
+static int __devexit omap_sdti_remove(struct platform_device *pdev)
+{
+ iounmap((void *)sti_channel_base);
+ iounmap((void *)sti_base);
+ omap_sdti_exit();
+
+ return 0;
+}
+
+static struct platform_driver omap_sdti_driver = {
+ .probe = omap_sdti_probe,
+ .remove = __devexit_p(omap_sdti_remove),
+ .driver = {
+ .name = "sti",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap_sdti_module_init(void)
+{
+ return platform_driver_register(&omap_sdti_driver);
+}
+
+static void __exit omap_sdti_module_exit(void)
+{
+ platform_driver_unregister(&omap_sdti_driver);
+}
+subsys_initcall(omap_sdti_module_init);
+module_exit(omap_sdti_module_exit);
+
+MODULE_AUTHOR("Roman Tereshonkov");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/sti.h>
- #include <asm/arch/board.h>
+/*
+ * Console support for OMAP STI/XTI
+ *
+ * Copyright (C) 2004, 2005, 2006 Nokia Corporation
+ * Written by: Paul Mundt <paul.mundt@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
++#include <mach/sti.h>
++#include <mach/board.h>
+
+#define DRV_NAME "sticon"
+
+static struct tty_driver *tty_driver;
+static DEFINE_SPINLOCK(sti_console_lock);
+static unsigned int sti_console_channel = -1;
+static int sti_line_done = -1;
+
+/*
+ * Write a string to any channel (including terminating NULL)
+ * Returns number of characters written.
+ */
+static int sti_channel_puts(const char *string, unsigned int channel, int len)
+{
+ int count = 0;
+
+ /*
+ * sti_line_done is needed to determine when we have reached the
+ * end of the line. write() has a tendency to hand us small
+ * strings which otherwise end up creating newlines.. we need to
+ * keep the channel open and in append mode until the line has
+ * been terminated.
+ */
+ if (sti_line_done != 0) {
+#ifdef __LITTLE_ENDIAN
+ sti_channel_writeb(0xc3, channel);
+#else
+ sti_channel_writeb(0xc0, channel);
+#endif
+ xchg(&sti_line_done, 0);
+ }
+
+ while (*string && count != len) {
+ char c = *string++;
+
+ count++;
+
+ if (c == '\n') {
+ xchg(&sti_line_done, 1);
+ sti_channel_writeb(0, channel);
+ break;
+ } else
+ sti_channel_writeb(c, channel);
+ }
+
+ if (sti_line_done)
+ sti_channel_flush(channel);
+
+ return count;
+}
+
+static int sti_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ return 0;
+}
+
+static int sti_tty_write(struct tty_struct *tty,
+ const unsigned char *buf, int len)
+{
+ unsigned long flags;
+ int bytes;
+
+ spin_lock_irqsave(&sti_console_lock, flags);
+ bytes = sti_channel_puts(buf, sti_console_channel, len);
+ spin_unlock_irqrestore(&sti_console_lock, flags);
+
+ return bytes;
+}
+
+static int sti_tty_write_room(struct tty_struct *tty)
+{
+ return 0x100000;
+}
+
+static int sti_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ return 0;
+}
+
+static struct tty_operations sti_tty_ops = {
+ .open = sti_tty_open,
+ .write = sti_tty_write,
+ .write_room = sti_tty_write_room,
+ .chars_in_buffer = sti_tty_chars_in_buffer,
+};
+
+static void sti_console_write(struct console *c, const char *s, unsigned n)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sti_console_lock, flags);
+ sti_channel_puts(s, sti_console_channel, n);
+ spin_unlock_irqrestore(&sti_console_lock, flags);
+}
+
+static struct tty_driver *sti_console_device(struct console *c, int *index)
+{
+ *index = c->index;
+ return tty_driver;
+}
+
+static int sti_console_setup(struct console *c, char *opts)
+{
+ return 0;
+}
+
+static struct console sti_console = {
+ .name = DRV_NAME,
+ .write = sti_console_write,
+ .device = sti_console_device,
+ .setup = sti_console_setup,
+ .flags = CON_PRINTBUFFER | CON_ENABLED,
+ .index = -1,
+};
+
+static int __init sti_console_init(void)
+{
+ const struct omap_sti_console_config *info;
+
+ info = omap_get_config(OMAP_TAG_STI_CONSOLE,
+ struct omap_sti_console_config);
+ if (info && info->enable) {
+ add_preferred_console(DRV_NAME, 0, NULL);
+
+ sti_console_channel = info->channel;
+ }
+
+ if (unlikely(sti_console_channel == -1))
+ return -EINVAL;
+
+ register_console(&sti_console);
+
+ return 0;
+}
+__initcall(sti_console_init);
+
+static int __init sti_tty_init(void)
+{
+ struct tty_driver *tty;
+ int ret;
+
+ tty = alloc_tty_driver(1);
+ if (!tty)
+ return -ENOMEM;
+
+ tty->name = DRV_NAME;
+ tty->driver_name = DRV_NAME;
+ tty->major = 0; /* dynamic major */
+ tty->minor_start = 0;
+ tty->type = TTY_DRIVER_TYPE_SYSTEM;
+ tty->subtype = SYSTEM_TYPE_SYSCONS;
+ tty->init_termios = tty_std_termios;
+
+ tty_set_operations(tty, &sti_tty_ops);
+
+ ret = tty_register_driver(tty);
+ if (ret) {
+ put_tty_driver(tty);
+ return ret;
+ }
+
+ tty_driver = tty;
+ return 0;
+}
+late_initcall(sti_tty_init);
+
+module_param(sti_console_channel, uint, 0);
+MODULE_PARM_DESC(sti_console_channel, "STI console channel");
+MODULE_AUTHOR("Paul Mundt");
+MODULE_DESCRIPTION("OMAP STI console support");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/sti.h>
+/*
+ * STI RX FIFO Support
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ * Written by: Paul Mundt <paul.mundt@nokia.com> and
+ * Roman Tereshonkov <roman.tereshonkov@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/module.h>
++#include <mach/sti.h>
+
+#define STI_READ_BUFFER_SIZE 1024
+#define sti_buf_pos(pos) ((sti_crb->bufpos + (pos)) % \
+ STI_READ_BUFFER_SIZE)
+
+static struct sti_cycle_buffer {
+ int bufpos;
+ int datalen;
+ unsigned char *buf;
+} *sti_crb;
+
+/**
+ * sti_read_packet - STI read packet (read an entire STI packet)
+ * @buf: Buffer to store the packet.
+ * @maxsize: Maximum size requested.
+ *
+ * This reads in a single completed STI packet from the RX FIFOs and
+ * places it in @buf for further processing.
+ *
+ * The return value is < 0 on error, and >= 0 for the number of bytes
+ * actually read. As per the STI specification, we require a 0xC1 to
+ * indicate the end of the packet, and we don't return the packet until
+ * we've read the entire thing in.
+ *
+ * Due to the size of the FIFOs, it's unrealistic to constantly drain
+ * this for 1 or 2 bytes at a time, so we assemble it here and return
+ * the whole thing.
+ */
+int sti_read_packet(unsigned char *buf, int maxsize)
+{
+ unsigned int pos;
+
+ if (unlikely(!buf))
+ return -EINVAL;
+ if (!sti_crb->datalen)
+ return 0;
+
+ pos = sti_buf_pos(sti_crb->datalen - 1);
+ /* End of packet */
+ if (sti_crb->buf[pos] == 0xC1) {
+ int i;
+
+ for (i = 0; i < sti_crb->datalen && i < maxsize; i++) {
+ pos = sti_buf_pos(i);
+ buf[i] = sti_crb->buf[pos];
+ }
+
+ sti_crb->bufpos = sti_buf_pos(i);
+ sti_crb->datalen -= i;
+
+ return i;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(sti_read_packet);
+
+static void sti_fifo_irq(unsigned long arg)
+{
+ /* If there is data read it */
+ while (!(sti_readl(STI_RX_STATUS) & STI_RXFIFO_EMPTY)) {
+ unsigned int pos = sti_buf_pos(sti_crb->datalen);
+
+ sti_crb->buf[pos] = sti_readl(STI_RX_DR);
+ sti_crb->datalen++;
+ }
+
+ sti_ack_irq(STI_RX_INT);
+}
+
+static int __init sti_fifo_init(void)
+{
+ unsigned int size;
+ int ret;
+
+ size = sizeof(struct sti_cycle_buffer) + STI_READ_BUFFER_SIZE;
+ sti_crb = kmalloc(size, GFP_KERNEL);
+ if (!sti_crb)
+ return -ENOMEM;
+
+ sti_crb->bufpos = sti_crb->datalen = 0;
+ sti_crb->buf = (unsigned char *)(sti_crb + sizeof(*sti_crb));
+
+ ret = sti_request_irq(STI_RX_INT, sti_fifo_irq, 0);
+ if (ret != 0)
+ kfree(sti_crb);
+
+ return ret;
+}
+
+static void __exit sti_fifo_exit(void)
+{
+ sti_free_irq(STI_RX_INT);
+ kfree(sti_crb);
+}
+
+module_init(sti_fifo_init);
+module_exit(sti_fifo_exit);
+
+MODULE_AUTHOR("Paul Mundt, Roman Tereshonkov");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/sti.h>
+/*
+ * OMAP STI/XTI communications interface via netlink socket.
+ *
+ * Copyright (C) 2004, 2005, 2006 Nokia Corporation
+ * Written by: Paul Mundt <paul.mundt@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/netlink.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/mutex.h>
+#include <net/sock.h>
++#include <mach/sti.h>
+
+static struct sock *sti_sock;
+static DEFINE_MUTEX(sti_netlink_mutex);
+
+enum {
+ STI_READ,
+ STI_WRITE,
+};
+
+static int sti_netlink_read(int pid, int seq, void *payload, int size)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ int ret, len = NLMSG_SPACE(size);
+ unsigned char *tail;
+
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ tail = skb->tail;
+ nlh = NLMSG_PUT(skb, pid, seq, STI_READ,
+ len - (sizeof(struct nlmsghdr)));
+ nlh->nlmsg_flags = 0;
+ memcpy(NLMSG_DATA(nlh), payload, size);
+ nlh->nlmsg_len = skb->tail - tail;
+
+ ret = netlink_unicast(sti_sock, skb, pid, MSG_DONTWAIT);
+ if (ret > 0)
+ ret = 0;
+
+ return ret;
+
+nlmsg_failure:
+ if (skb)
+ kfree_skb(skb);
+
+ return -EINVAL;
+}
+
+/*
+ * We abuse nlmsg_type and nlmsg_flags for our purposes.
+ *
+ * The ID is encoded into the upper 8 bits of the nlmsg_type, while the
+ * channel number is encoded into the upper 8 bits of the nlmsg_flags.
+ */
+static int sti_netlink_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+ void *data;
+ u8 chan, id;
+ int size, ret = 0, len = 0;
+
+ data = NLMSG_DATA(nlh);
+ chan = (nlh->nlmsg_flags >> 8) & 0xff;
+ id = (nlh->nlmsg_type >> 8) & 0xff;
+ size = (int)(nlh->nlmsg_len - ((char *)data - (char *)nlh));
+
+ switch (nlh->nlmsg_type & 0xff) {
+ case STI_WRITE:
+ sti_channel_write_trace(size, id, data, chan);
+ break;
+ case STI_READ:
+ data = kmalloc(size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ memset(data, 0, size);
+
+ len = sti_read_packet(data, size);
+ ret = sti_netlink_read(NETLINK_CB(skb).pid, nlh->nlmsg_seq,
+ data, len);
+ kfree(data);
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ return ret;
+}
+
+static int sti_netlink_receive_skb(struct sk_buff *skb)
+{
+ while (skb->len >= NLMSG_SPACE(0)) {
+ struct nlmsghdr *nlh;
+ u32 rlen;
+ int ret;
+
+ nlh = (struct nlmsghdr *)skb->data;
+ if (nlh->nlmsg_len < sizeof(struct nlmsghdr) ||
+ skb->len < nlh->nlmsg_len)
+ break;
+
+ rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+ if (rlen > skb->len)
+ rlen = skb->len;
+
+ ret = sti_netlink_receive_msg(skb, nlh);
+ if (ret)
+ netlink_ack(skb, nlh, -ret);
+ else if (nlh->nlmsg_flags & NLM_F_ACK)
+ netlink_ack(skb, nlh, 0);
+
+ skb_pull(skb, rlen);
+ }
+
+ return 0;
+}
+
+static void sti_netlink_receive(struct sk_buff *skb)
+{
+ if (!mutex_trylock(&sti_netlink_mutex))
+ return;
+
+ sti_netlink_receive_skb(skb);
+ mutex_unlock(&sti_netlink_mutex);
+}
+
+static int __init sti_netlink_init(void)
+{
+ sti_sock = netlink_kernel_create(&init_net, NETLINK_USERSOCK, 0,
+ sti_netlink_receive, NULL,
+ THIS_MODULE);
+ if (!sti_sock) {
+ printk(KERN_ERR "STI: Failed to create netlink socket\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+module_init(sti_netlink_init);
+
+MODULE_AUTHOR("Paul Mundt");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("STI netlink-driven communications interface");
--- /dev/null
- #include <asm/arch/sti.h>
+/*
+ * Support functions for OMAP STI/XTI (Serial Tracing Interface)
+ *
+ * Copyright (C) 2004, 2005, 2006 Nokia Corporation
+ * Written by: Paul Mundt <paul.mundt@nokia.com>
+ *
+ * STI initialization code and channel handling
+ * from Juha Yrjölä <juha.yrjola@nokia.com>.
+ *
+ * XTI initialization
+ * from Roman Tereshonkov <roman.tereshonkov@nokia.com>.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
++#include <mach/sti.h>
+#include <asm/byteorder.h>
+
+static struct clk *sti_ck;
+unsigned long sti_base, sti_channel_base;
+static unsigned long sti_kern_mask = STIEn;
+static unsigned long sti_irq_mask = STI_IRQSTATUS_MASK;
+static DEFINE_SPINLOCK(sti_lock);
+
+static struct sti_irqdesc {
+ irqreturn_t (*func)(unsigned long);
+ unsigned long data;
+} ____cacheline_aligned sti_irq_desc[STI_NR_IRQS];
+
+void sti_channel_write_trace(int len, int id, void *data, unsigned int channel)
+{
+ const u8 *tpntr = data;
+
+ sti_channel_writeb(id, channel);
+
+ if (cpu_is_omap16xx())
+ /* Check u32 boundary */
+ if (!((u32)data & (STI_PERCHANNEL_SIZE - 1)) &&
+ (len >= STI_PERCHANNEL_SIZE)) {
+ const u32 *asrc = data;
+
+ do {
+ sti_channel_writel(cpu_to_be32(*asrc++),
+ channel);
+ len -= STI_PERCHANNEL_SIZE;
+ } while (len >= STI_PERCHANNEL_SIZE);
+
+ tpntr = (const u8 *)asrc;
+ }
+
+ while (len--)
+ sti_channel_writeb(*tpntr++, channel);
+
+ sti_channel_flush(channel);
+}
+EXPORT_SYMBOL(sti_channel_write_trace);
+
+void sti_enable_irq(unsigned int id)
+{
+ spin_lock_irq(&sti_lock);
+ sti_writel(1 << id, STI_IRQSETEN);
+ spin_unlock_irq(&sti_lock);
+}
+EXPORT_SYMBOL(sti_enable_irq);
+
+void sti_disable_irq(unsigned int id)
+{
+ spin_lock_irq(&sti_lock);
+
+ if (cpu_is_omap16xx())
+ sti_writel(1 << id, STI_IRQCLREN);
+ else if (cpu_is_omap24xx())
+ sti_writel(sti_readl(STI_IRQSETEN) & ~(1 << id), STI_IRQSETEN);
+ else
+ BUG();
+
+ spin_unlock_irq(&sti_lock);
+}
+EXPORT_SYMBOL(sti_disable_irq);
+
+void sti_ack_irq(unsigned int id)
+{
+ /* Even though the clear state is 0, we have to write 1 to clear */
+ sti_writel(1 << id, STI_IRQSTATUS);
+}
+EXPORT_SYMBOL(sti_ack_irq);
+
+int sti_request_irq(unsigned int irq, void *handler, unsigned long arg)
+{
+ struct sti_irqdesc *desc;
+
+ if (unlikely(!handler || irq > STI_NR_IRQS))
+ return -EINVAL;
+
+ desc = sti_irq_desc + irq;
+ if (unlikely(desc->func)) {
+ printk(KERN_WARNING "STI: Attempting to request in-use IRQ "
+ "%d, consider fixing your code..\n", irq);
+ return -EBUSY;
+ }
+
+ desc->func = handler;
+ desc->data = arg;
+
+ sti_enable_irq(irq);
+ return 0;
+}
+EXPORT_SYMBOL(sti_request_irq);
+
+void sti_free_irq(unsigned int irq)
+{
+ struct sti_irqdesc *desc = sti_irq_desc + irq;
+
+ if (unlikely(irq > STI_NR_IRQS))
+ return;
+
+ sti_disable_irq(irq);
+
+ desc->func = NULL;
+ desc->data = 0;
+}
+EXPORT_SYMBOL(sti_free_irq);
+
+/*
+ * This is a bit heavy, so normally we would defer this to a tasklet.
+ * Unfortunately tasklets are too slow for the RX FIFO interrupt (and
+ * possibly some others), so we just do the irqdesc walking here.
+ */
+static irqreturn_t sti_interrupt(int irq, void *dev_id)
+{
+ int ret = IRQ_NONE;
+ u16 status;
+ int i;
+
+ status = sti_readl(STI_IRQSTATUS) & sti_irq_mask;
+
+ for (i = 0; status; i++) {
+ struct sti_irqdesc *desc = sti_irq_desc + i;
+ u16 id = 1 << i;
+
+ if (!(status & id))
+ continue;
+
+ if (likely(desc && desc->func))
+ ret |= desc->func(desc->data);
+ if (unlikely(ret == IRQ_NONE)) {
+ printk("STI: spurious interrupt (id %d)\n", id);
+ sti_disable_irq(i);
+ sti_ack_irq(i);
+ ret = IRQ_HANDLED;
+ }
+
+ status &= ~id;
+ }
+
+ return IRQ_RETVAL(ret);
+}
+
+static void omap_sti_reset(void)
+{
+ int i;
+
+ /* Reset STI module */
+ sti_writel(0x02, STI_SYSCONFIG);
+
+ /* Wait a while for the STI module to complete its reset */
+ for (i = 0; i < 10000; i++)
+ if (sti_readl(STI_SYSSTATUS) & 1)
+ break;
+}
+
+static int __init sti_init(void)
+{
+ char buf[64];
+ int i;
+
+ if (cpu_is_omap16xx()) {
+ /* Release ARM Rhea buses peripherals enable */
+ sti_writel(sti_readl(ARM_RSTCT2) | 0x0001, ARM_RSTCT2);
+
+ /* Enable TC1_CK (functional clock) */
+ sti_ck = clk_get(NULL, "tc1_ck");
+ } else if (cpu_is_omap24xx())
+ /* Enable emulation tools clock */
+ sti_ck = clk_get(NULL, "emul_ck");
+
+ if (IS_ERR(sti_ck))
+ return PTR_ERR(sti_ck);
+
+ clk_enable(sti_ck);
+
+ /* Reset STI module */
+ omap_sti_reset();
+
+ /* Enable STI */
+ sti_trace_enable(MPUCmdEn);
+
+ /* Change to custom serial protocol */
+ sti_writel(0x01, STI_SERIAL_CFG);
+
+ /* Set STI clock control register to normal mode */
+ sti_writel(0x00, STI_CLK_CTRL);
+
+ i = sti_readl(STI_REVISION);
+ snprintf(buf, sizeof(buf), "OMAP STI support loaded (HW v%u.%u)\n",
+ (i >> 4) & 0x0f, i & 0x0f);
+ printk(KERN_INFO "%s", buf);
+
+ sti_channel_write_trace(strlen(buf), 0xc3, buf, 239);
+
+ return 0;
+}
+
+static void sti_exit(void)
+{
+ u32 tmp;
+
+ /*
+ * This should have already been done by reset, but we switch off
+ * STI entirely just for added sanity..
+ */
+ tmp = sti_readl(STI_ER);
+ tmp &= ~STIEn;
+ sti_writel(tmp, STI_ER);
+
+ clk_disable(sti_ck);
+ clk_put(sti_ck);
+}
+
+static void __sti_trace_enable(int event)
+{
+ u32 tmp;
+
+ tmp = sti_readl(STI_ER);
+ tmp |= sti_kern_mask | event;
+ sti_writel(tmp, STI_ER);
+}
+
+int sti_trace_enable(int event)
+{
+ spin_lock_irq(&sti_lock);
+ sti_kern_mask |= event;
+ __sti_trace_enable(event);
+ spin_unlock_irq(&sti_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(sti_trace_enable);
+
+static void __sti_trace_disable(int event)
+{
+ u32 tmp;
+
+ tmp = sti_readl(STI_DR);
+
+ if (cpu_is_omap16xx()) {
+ tmp |= event;
+ tmp &= ~sti_kern_mask;
+ } else if (cpu_is_omap24xx()) {
+ tmp &= ~event;
+ tmp |= sti_kern_mask;
+ } else
+ BUG();
+
+ sti_writel(tmp, STI_DR);
+}
+
+void sti_trace_disable(int event)
+{
+ spin_lock_irq(&sti_lock);
+ sti_kern_mask &= ~event;
+ __sti_trace_disable(event);
+ spin_unlock_irq(&sti_lock);
+}
+EXPORT_SYMBOL(sti_trace_disable);
+
+static ssize_t
+sti_trace_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0x%08lx\n", sti_readl(STI_ER));
+}
+
+static ssize_t
+sti_trace_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int evt = simple_strtoul(buf, NULL, 0);
+ int mask = ~evt;
+
+ spin_lock_irq(&sti_lock);
+ __sti_trace_disable(mask);
+ __sti_trace_enable(evt);
+ spin_unlock_irq(&sti_lock);
+
+ return count;
+}
+static DEVICE_ATTR(trace, S_IRUGO | S_IWUSR, sti_trace_show, sti_trace_store);
+
+static ssize_t
+sti_imask_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0x%04lx\n", sti_irq_mask);
+}
+
+static ssize_t
+sti_imask_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ spin_lock_irq(&sti_lock);
+ sti_irq_mask = simple_strtoul(buf, NULL, 0);
+ spin_unlock_irq(&sti_lock);
+
+ return count;
+}
+static DEVICE_ATTR(imask, S_IRUGO | S_IWUSR, sti_imask_show, sti_imask_store);
+
+static int __devinit sti_probe(struct platform_device *pdev)
+{
+ struct resource *res, *cres;
+ int ret;
+
+ if (pdev->num_resources != 3) {
+ dev_err(&pdev->dev, "invalid number of resources: %d\n",
+ pdev->num_resources);
+ return -ENODEV;
+ }
+
+ /* STI base */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!res)) {
+ dev_err(&pdev->dev, "invalid mem resource\n");
+ return -ENODEV;
+ }
+
+ /* Channel base */
+ cres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (unlikely(!cres)) {
+ dev_err(&pdev->dev, "invalid channel mem resource\n");
+ return -ENODEV;
+ }
+
+ ret = device_create_file(&pdev->dev, &dev_attr_trace);
+ if (unlikely(ret != 0))
+ return ret;
+
+ ret = device_create_file(&pdev->dev, &dev_attr_imask);
+ if (unlikely(ret != 0))
+ goto err;
+
+ sti_base = io_p2v(res->start);
+
+ /*
+ * OMAP 16xx keeps channels in a relatively sane location,
+ * whereas 24xx maps them much further out, and so they must be
+ * remapped.
+ */
+ if (cpu_is_omap16xx())
+ sti_channel_base = io_p2v(cres->start);
+ else if (cpu_is_omap24xx()) {
+ unsigned int size = cres->end - cres->start;
+
+ sti_channel_base = (unsigned long)ioremap(cres->start, size);
+ if (unlikely(!sti_channel_base)) {
+ ret = -ENODEV;
+ goto err_badremap;
+ }
+ }
+
+ ret = request_irq(platform_get_irq(pdev, 0), sti_interrupt,
+ IRQF_DISABLED, "sti", NULL);
+ if (unlikely(ret != 0))
+ goto err_badirq;
+
+ return sti_init();
+
+err_badirq:
+ iounmap((void *)sti_channel_base);
+err_badremap:
+ device_remove_file(&pdev->dev, &dev_attr_imask);
+err:
+ device_remove_file(&pdev->dev, &dev_attr_trace);
+
+ return ret;
+}
+
+static int __devexit sti_remove(struct platform_device *pdev)
+{
+ unsigned int irq = platform_get_irq(pdev, 0);
+
+ if (cpu_is_omap24xx())
+ iounmap((void *)sti_channel_base);
+
+ device_remove_file(&pdev->dev, &dev_attr_trace);
+ device_remove_file(&pdev->dev, &dev_attr_imask);
+ free_irq(irq, NULL);
+ sti_exit();
+
+ return 0;
+}
+
+static struct platform_driver sti_driver = {
+ .probe = sti_probe,
+ .remove = __devexit_p(sti_remove),
+ .driver = {
+ .name = "sti",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sti_module_init(void)
+{
+ return platform_driver_register(&sti_driver);
+}
+
+static void __exit sti_module_exit(void)
+{
+ platform_driver_unregister(&sti_driver);
+}
+subsys_initcall(sti_module_init);
+module_exit(sti_module_exit);
+
+MODULE_AUTHOR("Paul Mundt, Juha Yrjölä, Roman Tereshonkov");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/hardware.h>
- #include <asm/arch/board.h>
- #include <asm/arch/mmc.h>
- #include <asm/arch/cpu.h>
+/*
+ * drivers/mmc/host/omap_hsmmc.c
+ *
+ * Driver for OMAP2430/3430 MMC controller.
+ *
+ * Copyright (C) 2007 Texas Instruments.
+ *
+ * Authors:
+ * Syed Mohammed Khasim <x0khasim@ti.com>
+ * Madhusudhan <madhu.cr@ti.com>
+ * Mohit Jalori <mjalori@ti.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+#include <linux/mmc/host.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <asm/dma.h>
++#include <mach/hardware.h>
++#include <mach/board.h>
++#include <mach/mmc.h>
++#include <mach/cpu.h>
+
+/* OMAP HSMMC Host Controller Registers */
+#define OMAP_HSMMC_SYSCONFIG 0x0010
+#define OMAP_HSMMC_CON 0x002C
+#define OMAP_HSMMC_BLK 0x0104
+#define OMAP_HSMMC_ARG 0x0108
+#define OMAP_HSMMC_CMD 0x010C
+#define OMAP_HSMMC_RSP10 0x0110
+#define OMAP_HSMMC_RSP32 0x0114
+#define OMAP_HSMMC_RSP54 0x0118
+#define OMAP_HSMMC_RSP76 0x011C
+#define OMAP_HSMMC_DATA 0x0120
+#define OMAP_HSMMC_HCTL 0x0128
+#define OMAP_HSMMC_SYSCTL 0x012C
+#define OMAP_HSMMC_STAT 0x0130
+#define OMAP_HSMMC_IE 0x0134
+#define OMAP_HSMMC_ISE 0x0138
+#define OMAP_HSMMC_CAPA 0x0140
+
+#define VS18 (1<<26)
+#define VS30 (1<<25)
+#define SDVS18 (0x5<<9)
+#define SDVS30 (0x6<<9)
+#define SDVSCLR 0xFFFFF1FF
+#define SDVSDET 0x00000400
+#define AUTOIDLE 0x1
+#define SDBP (1<<8)
+#define DTO 0xe
+#define ICE 0x1
+#define ICS 0x2
+#define CEN (1<<2)
+#define CLKD_MASK 0x0000FFC0
+#define INT_EN_MASK 0x307F0033
+#define INIT_STREAM (1<<1)
+#define DP_SELECT (1<<21)
+#define DDIR (1<<4)
+#define DMA_EN 0x1
+#define MSBS 1<<5
+#define BCE 1<<1
+#define FOUR_BIT 1 << 1
+#define CC 0x1
+#define TC 0x02
+#define OD 0x1
+#define ERR (1 << 15)
+#define CMD_TIMEOUT (1 << 16)
+#define DATA_TIMEOUT (1 << 20)
+#define CMD_CRC (1 << 17)
+#define DATA_CRC (1 << 21)
+#define CARD_ERR (1 << 28)
+#define STAT_CLEAR 0xFFFFFFFF
+#define INIT_STREAM_CMD 0x00000000
+#define DUAL_VOLT_OCR_BIT 7
+#define SRC (1 << 25)
+#define SRD (1 << 26)
+
+#define OMAP_MMC1_DEVID 1
+#define OMAP_MMC2_DEVID 2
+#define OMAP_MMC_DATADIR_NONE 0
+#define OMAP_MMC_DATADIR_READ 1
+#define OMAP_MMC_DATADIR_WRITE 2
+#define MMC_TIMEOUT_MS 20
+#define OMAP_MMC_MASTER_CLOCK 96000000
+#define DRIVER_NAME "mmci-omap"
+/*
+ * slot_id is device id - 1, device id is a static value
+ * of 1 to represent device 1 etc..
+ */
+#define mmc_slot(host) (host->pdata->slots[host->slot_id])
+
+/*
+ * MMC Host controller read/write API's
+ */
+#define OMAP_HSMMC_READ(base, reg) \
+ __raw_readl((base) + OMAP_HSMMC_##reg)
+
+#define OMAP_HSMMC_WRITE(base, reg, val) \
+ __raw_writel((val), (base) + OMAP_HSMMC_##reg)
+
+struct mmc_omap_host {
+ struct device *dev;
+ struct mmc_host *mmc;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ struct clk *fclk;
+ struct clk *iclk;
+ struct clk *dbclk;
+ struct semaphore sem;
+ struct work_struct mmc_carddetect_work;
+ void __iomem *base;
+ resource_size_t mapbase;
+ unsigned int id;
+ unsigned int dma_len;
+ unsigned int dma_dir;
+ unsigned char bus_mode;
+ unsigned char datadir;
+ u32 *buffer;
+ u32 bytesleft;
+ int suspended;
+ int irq;
+ int carddetect;
+ int use_dma, dma_ch;
+ int initstr;
+ int slot_id;
+ int dbclk_enabled;
+ struct omap_mmc_platform_data *pdata;
+};
+
+/*
+ * Stop clock to the card
+ */
+static void omap_mmc_stop_clock(struct mmc_omap_host *host)
+{
+ OMAP_HSMMC_WRITE(host->base, SYSCTL,
+ OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
+ if ((OMAP_HSMMC_READ(host->base, SYSCTL) & CEN) != 0x0)
+ dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
+}
+
+/*
+ * Send init stream sequence to card
+ * before sending IDLE command
+ */
+static void send_init_stream(struct mmc_omap_host *host)
+{
+ int reg = 0;
+ unsigned long timeout;
+
+ disable_irq(host->irq);
+ OMAP_HSMMC_WRITE(host->base, CON,
+ OMAP_HSMMC_READ(host->base, CON) | INIT_STREAM);
+ OMAP_HSMMC_WRITE(host->base, CMD, INIT_STREAM_CMD);
+
+ timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+ while ((reg != CC) && time_before(jiffies, timeout))
+ reg = OMAP_HSMMC_READ(host->base, STAT) & CC;
+
+ OMAP_HSMMC_WRITE(host->base, CON,
+ OMAP_HSMMC_READ(host->base, CON) & ~INIT_STREAM);
+ enable_irq(host->irq);
+}
+
+/*
+ * Configure the response type and send the cmd.
+ */
+static void
+mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
+ struct mmc_data *data)
+{
+ int cmdreg = 0, resptype = 0, cmdtype = 0;
+
+ dev_dbg(mmc_dev(host->mmc), "%s: CMD%d, argument 0x%08x\n",
+ mmc_hostname(host->mmc), cmd->opcode, cmd->arg);
+ host->cmd = cmd;
+
+ /*
+ * Clear status bits and enable interrupts
+ */
+ OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+ OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
+ OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136)
+ resptype = 1;
+ else
+ resptype = 2;
+ }
+
+ /*
+ * Unlike OMAP1 controller, the cmdtype does not seem to be based on
+ * ac, bc, adtc, bcr. Only CMD12 needs a val of 0x3, rest 0x0.
+ */
+ if (cmd->opcode == 12)
+ cmdtype = 0x3;
+
+ cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22);
+
+ if (data) {
+ cmdreg |= DP_SELECT | MSBS | BCE;
+ if (data->flags & MMC_DATA_READ)
+ cmdreg |= DDIR;
+ else
+ cmdreg &= ~(DDIR);
+ }
+
+ if (host->use_dma)
+ cmdreg |= DMA_EN;
+
+ OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
+ OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
+}
+
+/*
+ * Notify the transfer complete to MMC core
+ */
+static void
+mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ host->data = NULL;
+
+ if (host->use_dma && host->dma_ch != -1)
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
+ host->dma_dir);
+
+ host->datadir = OMAP_MMC_DATADIR_NONE;
+
+ if (!data->error)
+ data->bytes_xfered += data->blocks * (data->blksz);
+ else
+ data->bytes_xfered = 0;
+
+ if (!data->stop) {
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, data->mrq);
+ return;
+ }
+ mmc_omap_start_command(host, data->stop, NULL);
+}
+
+/*
+ * Notify the core about command completion
+ */
+static void
+mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
+{
+ host->cmd = NULL;
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
+ /* response type 2 */
+ cmd->resp[3] = OMAP_HSMMC_READ(host->base, RSP10);
+ cmd->resp[2] = OMAP_HSMMC_READ(host->base, RSP32);
+ cmd->resp[1] = OMAP_HSMMC_READ(host->base, RSP54);
+ cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP76);
+ } else {
+ /* response types 1, 1b, 3, 4, 5, 6 */
+ cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10);
+ }
+ }
+ if (host->data == NULL || cmd->error) {
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, cmd->mrq);
+ }
+}
+
+/*
+ * DMA clean up for command errors
+ */
+static void mmc_dma_cleanup(struct mmc_omap_host *host)
+{
+ host->data->error = -ETIMEDOUT;
+
+ if (host->use_dma && host->dma_ch != -1) {
+ dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
+ host->dma_dir);
+ omap_free_dma(host->dma_ch);
+ host->dma_ch = -1;
+ up(&host->sem);
+ }
+ host->data = NULL;
+ host->datadir = OMAP_MMC_DATADIR_NONE;
+}
+
+/*
+ * MMC controller IRQ handler
+ */
+static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
+{
+ struct mmc_omap_host *host = dev_id;
+ struct mmc_data *data;
+ int end_cmd = 0, end_trans = 0, status;
+
+ if (host->cmd == NULL && host->data == NULL) {
+ OMAP_HSMMC_WRITE(host->base, STAT,
+ OMAP_HSMMC_READ(host->base, STAT));
+ return IRQ_HANDLED;
+ }
+
+ data = host->data;
+ status = OMAP_HSMMC_READ(host->base, STAT);
+ dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
+
+ if (status & ERR) {
+ if ((status & CMD_TIMEOUT) ||
+ (status & CMD_CRC)) {
+ if (host->cmd) {
+ if (status & CMD_TIMEOUT) {
+ OMAP_HSMMC_WRITE(host->base, SYSCTL,
+ OMAP_HSMMC_READ(host->base,
+ SYSCTL) | SRC);
+ while (OMAP_HSMMC_READ(host->base,
+ SYSCTL) & SRC) ;
+ host->cmd->error = -ETIMEDOUT;
+ } else {
+ host->cmd->error = -EILSEQ;
+ }
+ end_cmd = 1;
+ }
+ if (host->data)
+ mmc_dma_cleanup(host);
+ }
+ if ((status & DATA_TIMEOUT) ||
+ (status & DATA_CRC)) {
+ if (host->data) {
+ if (status & DATA_TIMEOUT)
+ mmc_dma_cleanup(host);
+ else
+ host->data->error = -EILSEQ;
+ end_trans = 1;
+ }
+ }
+ if (status & CARD_ERR) {
+ dev_dbg(mmc_dev(host->mmc),
+ "Ignoring card err CMD%d\n", host->cmd->opcode);
+ if (host->cmd)
+ end_cmd = 1;
+ if (host->data)
+ end_trans = 1;
+ }
+ }
+
+ OMAP_HSMMC_WRITE(host->base, STAT, status);
+
+ if (end_cmd || (status & CC))
+ mmc_omap_cmd_done(host, host->cmd);
+ if (end_trans || (status & TC))
+ mmc_omap_xfer_done(host, data);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Switch MMC operating voltage
+ */
+static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
+{
+ u32 reg_val = 0;
+ int ret;
+
+ /* Disable the clocks */
+ clk_disable(host->fclk);
+ clk_disable(host->iclk);
+ clk_disable(host->dbclk);
+
+ /* Turn the power off */
+ ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
+ if (ret != 0)
+ goto err;
+
+ /* Turn the power ON with given VDD 1.8 or 3.0v */
+ ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd);
+ if (ret != 0)
+ goto err;
+
+ clk_enable(host->fclk);
+ clk_enable(host->iclk);
+ clk_enable(host->dbclk);
+
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR);
+ reg_val = OMAP_HSMMC_READ(host->base, HCTL);
+ /*
+ * If a MMC dual voltage card is detected, the set_ios fn calls
+ * this fn with VDD bit set for 1.8V. Upon card removal from the
+ * slot, mmc_omap_detect fn sets the VDD back to 3V.
+ *
+ * Only MMC1 supports 3.0V. MMC2 will not function if SDVS30 is
+ * set in HCTL.
+ */
+ if (host->id == OMAP_MMC1_DEVID && (((1 << vdd) == MMC_VDD_32_33) ||
+ ((1 << vdd) == MMC_VDD_33_34)))
+ reg_val |= SDVS30;
+ if ((1 << vdd) == MMC_VDD_165_195)
+ reg_val |= SDVS18;
+
+ OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
+
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+
+ return 0;
+err:
+ dev_dbg(mmc_dev(host->mmc), "Unable to switch operating voltage\n");
+ return ret;
+}
+
+/*
+ * Work Item to notify the core about card insertion/removal
+ */
+static void mmc_omap_detect(struct work_struct *work)
+{
+ u16 vdd = 0;
+ struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
+ mmc_carddetect_work);
+
+ if (host->carddetect) {
+ if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
+ /*
+ * Set the VDD back to 3V when the card is removed
+ * before the set_ios fn turns off the power.
+ */
+ vdd = fls(host->mmc->ocr_avail) - 1;
+ if (omap_mmc_switch_opcond(host, vdd) != 0)
+ host->mmc->ios.vdd = vdd;
+ }
+ mmc_detect_change(host->mmc, (HZ * 200) / 1000);
+ } else {
+ OMAP_HSMMC_WRITE(host->base, SYSCTL,
+ OMAP_HSMMC_READ(host->base, SYSCTL) | SRD);
+ while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD) ;
+ mmc_detect_change(host->mmc, (HZ * 50) / 1000);
+ }
+}
+
+/*
+ * ISR for handling card insertion and removal
+ */
+static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id;
+
+ host->carddetect = mmc_slot(host).card_detect(irq);
+ schedule_work(&host->mmc_carddetect_work);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * DMA call back function
+ */
+static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
+{
+ struct mmc_omap_host *host = data;
+
+ if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ)
+ dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n");
+
+ if (host->dma_ch < 0)
+ return;
+
+ omap_free_dma(host->dma_ch);
+ host->dma_ch = -1;
+ /*
+ * DMA Callback: run in interrupt context.
+ * mutex_unlock will through a kernel warning if used.
+ */
+ up(&host->sem);
+}
+
+/*
+ * Configure dma src and destination parameters
+ */
+static int mmc_omap_config_dma_param(int sync_dir, struct mmc_omap_host *host,
+ struct mmc_data *data)
+{
+ if (sync_dir == 0) {
+ omap_set_dma_dest_params(host->dma_ch, 0,
+ OMAP_DMA_AMODE_CONSTANT,
+ (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+ omap_set_dma_src_params(host->dma_ch, 0,
+ OMAP_DMA_AMODE_POST_INC,
+ sg_dma_address(&data->sg[0]), 0, 0);
+ } else {
+ omap_set_dma_src_params(host->dma_ch, 0,
+ OMAP_DMA_AMODE_CONSTANT,
+ (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+ omap_set_dma_dest_params(host->dma_ch, 0,
+ OMAP_DMA_AMODE_POST_INC,
+ sg_dma_address(&data->sg[0]), 0, 0);
+ }
+ return 0;
+}
+/*
+ * Routine to configure and start DMA for the MMC card
+ */
+static int
+mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
+{
+ int sync_dev, sync_dir = 0;
+ int dma_ch = 0, ret = 0, err = 1;
+ struct mmc_data *data = req->data;
+
+ /*
+ * If for some reason the DMA transfer is still active,
+ * we wait for timeout period and free the dma
+ */
+ if (host->dma_ch != -1) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(100);
+ if (down_trylock(&host->sem)) {
+ omap_free_dma(host->dma_ch);
+ host->dma_ch = -1;
+ up(&host->sem);
+ return err;
+ }
+ } else {
+ if (down_trylock(&host->sem))
+ return err;
+ }
+
+ if (!(data->flags & MMC_DATA_WRITE)) {
+ host->dma_dir = DMA_FROM_DEVICE;
+ if (host->id == OMAP_MMC1_DEVID)
+ sync_dev = OMAP24XX_DMA_MMC1_RX;
+ else
+ sync_dev = OMAP24XX_DMA_MMC2_RX;
+ } else {
+ host->dma_dir = DMA_TO_DEVICE;
+ if (host->id == OMAP_MMC1_DEVID)
+ sync_dev = OMAP24XX_DMA_MMC1_TX;
+ else
+ sync_dev = OMAP24XX_DMA_MMC2_TX;
+ }
+
+ ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb,
+ host, &dma_ch);
+ if (ret != 0) {
+ dev_dbg(mmc_dev(host->mmc),
+ "%s: omap_request_dma() failed with %d\n",
+ mmc_hostname(host->mmc), ret);
+ return ret;
+ }
+
+ host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, host->dma_dir);
+ host->dma_ch = dma_ch;
+
+ if (!(data->flags & MMC_DATA_WRITE))
+ mmc_omap_config_dma_param(1, host, data);
+ else
+ mmc_omap_config_dma_param(0, host, data);
+
+ if ((data->blksz % 4) == 0)
+ omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
+ (data->blksz / 4), data->blocks, OMAP_DMA_SYNC_FRAME,
+ sync_dev, sync_dir);
+ else
+ /* REVISIT: The MMC buffer increments only when MSB is written.
+ * Return error for blksz which is non multiple of four.
+ */
+ return -EINVAL;
+
+ omap_start_dma(dma_ch);
+ return 0;
+}
+
+/*
+ * Configure block length for MMC/SD cards and initiate the transfer.
+ */
+static int
+mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
+{
+ int ret;
+ host->data = req->data;
+
+ if (req->data == NULL) {
+ host->datadir = OMAP_MMC_DATADIR_NONE;
+ OMAP_HSMMC_WRITE(host->base, BLK, 0);
+ return 0;
+ }
+
+ OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz)
+ | (req->data->blocks << 16));
+
+ host->datadir = (req->data->flags & MMC_DATA_WRITE) ?
+ OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ;
+
+ if (host->use_dma) {
+ ret = mmc_omap_start_dma_transfer(host, req);
+ if (ret != 0) {
+ dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Request function. for read/write operation
+ */
+static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+ struct mmc_omap_host *host = mmc_priv(mmc);
+
+ WARN_ON(host->mrq != NULL);
+ host->mrq = req;
+ mmc_omap_prepare_data(host, req);
+ mmc_omap_start_command(host, req->cmd, req->data);
+}
+
+
+/* Routine to configure clock values. Exposed API to core */
+static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct mmc_omap_host *host = mmc_priv(mmc);
+ u16 dsor = 0;
+ unsigned long regval;
+ unsigned long timeout;
+
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
+ break;
+ case MMC_POWER_UP:
+ mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd);
+ break;
+ }
+
+ switch (mmc->ios.bus_width) {
+ case MMC_BUS_WIDTH_4:
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
+ break;
+ case MMC_BUS_WIDTH_1:
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
+ break;
+ }
+
+ if (host->id == OMAP_MMC1_DEVID) {
+ /* Only MMC1 can operate at 3V/1.8V */
+ if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) &&
+ (ios->vdd == DUAL_VOLT_OCR_BIT)) {
+ /*
+ * The mmc_select_voltage fn of the core does
+ * not seem to set the power_mode to
+ * MMC_POWER_UP upon recalculating the voltage.
+ * vdd 1.8v.
+ */
+ if (omap_mmc_switch_opcond(host, ios->vdd) != 0)
+ dev_dbg(mmc_dev(host->mmc),
+ "Switch operation failed\n");
+ }
+ }
+
+ if (ios->clock) {
+ dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
+ if (dsor < 1)
+ dsor = 1;
+
+ if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
+ dsor++;
+
+ if (dsor > 250)
+ dsor = 250;
+ }
+ omap_mmc_stop_clock(host);
+ regval = OMAP_HSMMC_READ(host->base, SYSCTL);
+ regval = regval & ~(CLKD_MASK);
+ regval = regval | (dsor << 6) | (DTO << 16);
+ OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
+ OMAP_HSMMC_WRITE(host->base, SYSCTL,
+ OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
+
+ /* Wait till the ICS bit is set */
+ timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+ while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != 0x2
+ && time_before(jiffies, timeout))
+ msleep(1);
+
+ OMAP_HSMMC_WRITE(host->base, SYSCTL,
+ OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
+
+ if (ios->power_mode == MMC_POWER_ON)
+ send_init_stream(host);
+
+ if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
+ OMAP_HSMMC_WRITE(host->base, CON,
+ OMAP_HSMMC_READ(host->base, CON) | OD);
+}
+/* NOTE: Read only switch not supported yet */
+static struct mmc_host_ops mmc_omap_ops = {
+ .request = omap_mmc_request,
+ .set_ios = omap_mmc_set_ios,
+};
+
+static int __init omap_mmc_probe(struct platform_device *pdev)
+{
+ struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
+ struct mmc_host *mmc;
+ struct mmc_omap_host *host = NULL;
+ struct resource *res;
+ int ret = 0, irq;
+ u32 hctl, capa;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "Platform Data is missing\n");
+ return -ENXIO;
+ }
+
+ if (pdata->nr_slots == 0) {
+ dev_err(&pdev->dev, "No Slots\n");
+ return -ENXIO;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (res == NULL || irq < 0)
+ return -ENXIO;
+
+ res = request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name);
+ if (res == NULL)
+ return -EBUSY;
+
+ mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->pdata = pdata;
+ host->use_dma = 1;
+ host->dma_ch = -1;
+ host->irq = irq;
+ host->id = pdev->id;
+ host->slot_id = 0;
+ host->mapbase = res->start;
+ host->base = ioremap(host->mapbase, SZ_4K);
+ mmc->ops = &mmc_omap_ops;
+ mmc->f_min = 400000;
+ mmc->f_max = 52000000;
+
+ sema_init(&host->sem, 1);
+
+ host->iclk = clk_get(&pdev->dev, "mmchs_ick");
+ if (IS_ERR(host->iclk)) {
+ ret = PTR_ERR(host->iclk);
+ host->iclk = NULL;
+ goto err1;
+ }
+ host->fclk = clk_get(&pdev->dev, "mmchs_fck");
+ if (IS_ERR(host->fclk)) {
+ ret = PTR_ERR(host->fclk);
+ host->fclk = NULL;
+ clk_put(host->iclk);
+ goto err1;
+ }
+
+ if (clk_enable(host->fclk) != 0) {
+ clk_put(host->iclk);
+ clk_put(host->fclk);
+ goto err1;
+ }
+
+ if (clk_enable(host->iclk) != 0) {
+ clk_disable(host->fclk);
+ clk_put(host->iclk);
+ clk_put(host->fclk);
+ goto err1;
+ }
+
+ host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
+ /*
+ * MMC can still work without debounce clock.
+ */
+ if (IS_ERR(host->dbclk))
+ dev_dbg(mmc_dev(host->mmc), "Failed to get debounce clock\n");
+ else
+ if (clk_enable(host->dbclk) != 0)
+ dev_dbg(mmc_dev(host->mmc), "Enabling debounce"
+ " clk failed\n");
+ else
+ host->dbclk_enabled = 1;
+
+#ifdef CONFIG_MMC_BLOCK_BOUNCE
+ mmc->max_phys_segs = 1;
+ mmc->max_hw_segs = 1;
+#endif
+ mmc->max_blk_size = 512; /* Block Length at max can be 1024 */
+ mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */
+ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+ mmc->max_seg_size = mmc->max_req_size;
+
+ mmc->ocr_avail = mmc_slot(host).ocr_mask;
+ mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+
+ if (pdata->conf.wire4)
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+ /* Only MMC1 supports 3.0V */
+ if (host->id == OMAP_MMC1_DEVID) {
+ hctl = SDVS30;
+ capa = VS30 | VS18;
+ } else {
+ hctl = SDVS18;
+ capa = VS18;
+ }
+
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) | hctl);
+
+ OMAP_HSMMC_WRITE(host->base, CAPA,
+ OMAP_HSMMC_READ(host->base, CAPA) | capa);
+
+ /* Set the controller to AUTO IDLE mode */
+ OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
+ OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
+
+ /* Set SD bus power bit */
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+
+ /* Request IRQ for MMC operations */
+ ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED, pdev->name,
+ host);
+ if (ret) {
+ dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
+ goto irq_err;
+ }
+
+ /* Request IRQ for card detect */
+ if ((mmc_slot(host).card_detect_irq) && (mmc_slot(host).card_detect)) {
+ ret = request_irq(mmc_slot(host).card_detect_irq,
+ omap_mmc_cd_handler, IRQF_DISABLED, "MMC CD",
+ host);
+ if (ret) {
+ dev_dbg(mmc_dev(host->mmc),
+ "Unable to grab MMC CD IRQ");
+ free_irq(host->irq, host);
+ goto irq_err;
+ }
+ }
+
+ INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);
+ if (pdata->init != NULL) {
+ if (pdata->init(&pdev->dev) != 0) {
+ free_irq(mmc_slot(host).card_detect_irq, host);
+ free_irq(host->irq, host);
+ goto irq_err;
+ }
+ }
+
+ OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
+ OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
+
+ platform_set_drvdata(pdev, host);
+ mmc_add_host(mmc);
+
+ return 0;
+
+irq_err:
+ dev_dbg(mmc_dev(host->mmc), "Unable to configure MMC IRQs\n");
+ clk_disable(host->fclk);
+ clk_disable(host->iclk);
+ clk_put(host->fclk);
+ clk_put(host->iclk);
+ if (host->dbclk_enabled) {
+ clk_disable(host->dbclk);
+ clk_put(host->dbclk);
+ }
+
+err1:
+ iounmap(host->base);
+err:
+ dev_dbg(mmc_dev(host->mmc), "Probe Failed\n");
+ release_mem_region(res->start, res->end - res->start + 1);
+ if (host)
+ mmc_free_host(mmc);
+ return ret;
+}
+
+static int omap_mmc_remove(struct platform_device *pdev)
+{
+ struct mmc_omap_host *host = platform_get_drvdata(pdev);
+ struct resource *res;
+ u16 vdd = 0;
+
+ if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
+ /*
+ * Set the vdd back to 3V,
+ * applicable for dual volt support.
+ */
+ vdd = fls(host->mmc->ocr_avail) - 1;
+ if (omap_mmc_switch_opcond(host, vdd) != 0)
+ host->mmc->ios.vdd = vdd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ platform_set_drvdata(pdev, NULL);
+ if (host) {
+ mmc_remove_host(host->mmc);
+ if (host->pdata->cleanup)
+ host->pdata->cleanup(&pdev->dev);
+ free_irq(host->irq, host);
+ if (mmc_slot(host).card_detect_irq)
+ free_irq(mmc_slot(host).card_detect_irq, host);
+ flush_scheduled_work();
+
+ clk_disable(host->fclk);
+ clk_disable(host->iclk);
+ clk_put(host->fclk);
+ clk_put(host->iclk);
+ if (host->dbclk_enabled) {
+ clk_disable(host->dbclk);
+ clk_put(host->dbclk);
+ }
+
+ mmc_free_host(host->mmc);
+ iounmap(host->base);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int ret = 0;
+ struct mmc_omap_host *host = platform_get_drvdata(pdev);
+
+ if (host && host->suspended)
+ return 0;
+
+ if (host) {
+ ret = mmc_suspend_host(host->mmc, state);
+ if (ret == 0) {
+ host->suspended = 1;
+
+ OMAP_HSMMC_WRITE(host->base, ISE, 0);
+ OMAP_HSMMC_WRITE(host->base, IE, 0);
+
+ ret = host->pdata->suspend(&pdev->dev, host->slot_id);
+ if (ret)
+ dev_dbg(mmc_dev(host->mmc),
+ "Unable to handle MMC board"
+ " level suspend\n");
+
+ if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL)
+ & SDVSCLR);
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL)
+ | SDVS30);
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL)
+ | SDBP);
+ }
+
+ clk_disable(host->fclk);
+ clk_disable(host->iclk);
+ clk_disable(host->dbclk);
+ }
+
+ }
+ return ret;
+}
+
+/* Routine to resume the MMC device */
+static int omap_mmc_resume(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct mmc_omap_host *host = platform_get_drvdata(pdev);
+
+ if (host && !host->suspended)
+ return 0;
+
+ if (host) {
+
+ ret = clk_enable(host->fclk);
+ if (ret)
+ goto clk_en_err;
+
+ ret = clk_enable(host->iclk);
+ if (ret) {
+ clk_disable(host->fclk);
+ clk_put(host->fclk);
+ goto clk_en_err;
+ }
+
+ if (clk_enable(host->dbclk) != 0)
+ dev_dbg(mmc_dev(host->mmc),
+ "Enabling debounce clk failed\n");
+
+ ret = host->pdata->resume(&pdev->dev, host->slot_id);
+ if (ret)
+ dev_dbg(mmc_dev(host->mmc),
+ "Unmask interrupt failed\n");
+
+ /* Notify the core to resume the host */
+ ret = mmc_resume_host(host->mmc);
+ if (ret == 0)
+ host->suspended = 0;
+ }
+
+ return ret;
+
+clk_en_err:
+ dev_dbg(mmc_dev(host->mmc),
+ "Failed to enable MMC clocks during resume\n");
+ return ret;
+}
+
+#else
+#define omap_mmc_suspend NULL
+#define omap_mmc_resume NULL
+#endif
+
+static struct platform_driver omap_mmc_driver = {
+ .probe = omap_mmc_probe,
+ .remove = omap_mmc_remove,
+ .suspend = omap_mmc_suspend,
+ .resume = omap_mmc_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap_mmc_init(void)
+{
+ /* Register the MMC driver */
+ return platform_driver_register(&omap_mmc_driver);
+}
+
+static void __exit omap_mmc_cleanup(void)
+{
+ /* Unregister MMC driver */
+ platform_driver_unregister(&omap_mmc_driver);
+}
+
+module_init(omap_mmc_init);
+module_exit(omap_mmc_cleanup);
+
+MODULE_DESCRIPTION("OMAP High Speed Multimedia Card driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Texas Instruments Inc");
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
--#include <asm/hardware.h>
++#include <mach/hardware.h>
#include <asm/io.h>
--- /dev/null
- #include <asm/arch/board.h>
- #include <asm/arch/dma.h>
+/*
+ * drivers/mtd/nand/omap-hw.c
+ *
+ * This is the MTD driver for OMAP1710 internal HW NAND controller.
+ *
+ * Copyright (C) 2004-2006 Nokia Corporation
+ *
+ * Author: Jarkko Lavinen <jarkko.lavinen@nokia.com> and
+ * Juha Yrjölä <juha.yrjola@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+
++#include <mach/board.h>
++#include <mach/dma.h>
+
+#define NAND_BASE 0xfffbcc00
+#define NND_REVISION 0x00
+#define NND_ACCESS 0x04
+#define NND_ADDR_SRC 0x08
+#define NND_CTRL 0x10
+#define NND_MASK 0x14
+#define NND_STATUS 0x18
+#define NND_READY 0x1c
+#define NND_COMMAND 0x20
+#define NND_COMMAND_SEC 0x24
+#define NND_ECC_SELECT 0x28
+#define NND_ECC_START 0x2c
+#define NND_ECC_9 0x4c
+#define NND_RESET 0x50
+#define NND_FIFO 0x54
+#define NND_FIFOCTRL 0x58
+#define NND_PSC_CLK 0x5c
+#define NND_SYSTEST 0x60
+#define NND_SYSCFG 0x64
+#define NND_SYSSTATUS 0x68
+#define NND_FIFOTEST1 0x6c
+#define NND_FIFOTEST2 0x70
+#define NND_FIFOTEST3 0x74
+#define NND_FIFOTEST4 0x78
+#define NND_PSC1_CLK 0x8c
+#define NND_PSC2_CLK 0x90
+
+
+#define NND_CMD_READ1_LOWER 0x00
+#define NND_CMD_WRITE1_LOWER 0x00
+#define NND_CMD_READ1_UPPER 0x01
+#define NND_CMD_WRITE1_UPPER 0x01
+#define NND_CMD_PROGRAM_END 0x10
+#define NND_CMD_READ2_SPARE 0x50
+#define NND_CMD_WRITE2_SPARE 0x50
+#define NND_CMD_ERASE 0x60
+#define NND_CMD_STATUS 0x70
+#define NND_CMD_PROGRAM 0x80
+#define NND_CMD_READ_ID 0x90
+#define NND_CMD_ERASE_END 0xD0
+#define NND_CMD_RESET 0xFF
+
+
+#define NAND_Ecc_P1e (1 << 0)
+#define NAND_Ecc_P2e (1 << 1)
+#define NAND_Ecc_P4e (1 << 2)
+#define NAND_Ecc_P8e (1 << 3)
+#define NAND_Ecc_P16e (1 << 4)
+#define NAND_Ecc_P32e (1 << 5)
+#define NAND_Ecc_P64e (1 << 6)
+#define NAND_Ecc_P128e (1 << 7)
+#define NAND_Ecc_P256e (1 << 8)
+#define NAND_Ecc_P512e (1 << 9)
+#define NAND_Ecc_P1024e (1 << 10)
+#define NAND_Ecc_P2048e (1 << 11)
+
+#define NAND_Ecc_P1o (1 << 16)
+#define NAND_Ecc_P2o (1 << 17)
+#define NAND_Ecc_P4o (1 << 18)
+#define NAND_Ecc_P8o (1 << 19)
+#define NAND_Ecc_P16o (1 << 20)
+#define NAND_Ecc_P32o (1 << 21)
+#define NAND_Ecc_P64o (1 << 22)
+#define NAND_Ecc_P128o (1 << 23)
+#define NAND_Ecc_P256o (1 << 24)
+#define NAND_Ecc_P512o (1 << 25)
+#define NAND_Ecc_P1024o (1 << 26)
+#define NAND_Ecc_P2048o (1 << 27)
+
+#define TF(value) (value ? 1 : 0)
+
+#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0 )
+#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1 )
+#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2 )
+#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3 )
+#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4 )
+#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5 )
+#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6 )
+#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7 )
+
+#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0 )
+#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1 )
+#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2 )
+#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3 )
+#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4 )
+#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5 )
+#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6 )
+#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7 )
+
+#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0 )
+#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1 )
+#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2 )
+#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3 )
+#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4 )
+#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5 )
+#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6 )
+#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7 )
+
+#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0 )
+#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1 )
+#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2 )
+#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3 )
+#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4 )
+#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5 )
+#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6 )
+#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7 )
+
+#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0 )
+#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1 )
+
+extern struct nand_oobinfo jffs2_oobinfo;
+
+/*
+ * MTD structure for OMAP board
+ */
+static struct mtd_info *omap_mtd;
+static struct clk *omap_nand_clk;
+static int omap_nand_dma_ch;
+static struct completion omap_nand_dma_comp;
+static unsigned long omap_nand_base = io_p2v(NAND_BASE);
+
+static inline u32 nand_read_reg(int idx)
+{
+ return __raw_readl(omap_nand_base + idx);
+}
+
+static inline void nand_write_reg(int idx, u32 val)
+{
+ __raw_writel(val, omap_nand_base + idx);
+}
+
+static inline u8 nand_read_reg8(int idx)
+{
+ return __raw_readb(omap_nand_base + idx);
+}
+
+static inline void nand_write_reg8(int idx, u8 val)
+{
+ __raw_writeb(val, omap_nand_base + idx);
+}
+
+static void omap_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ u32 l;
+
+ switch(chip) {
+ case -1:
+ l = nand_read_reg(NND_CTRL);
+ l |= (1 << 8) | (1 << 10) | (1 << 12) | (1 << 14);
+ nand_write_reg(NND_CTRL, l);
+ break;
+ case 0:
+ /* Also CS1, CS2, CS4 would be available */
+ l = nand_read_reg(NND_CTRL);
+ l &= ~(1 << 8);
+ nand_write_reg(NND_CTRL, l);
+ break;
+ default:
+ BUG();
+ }
+}
+
+static void nand_dma_cb(int lch, u16 ch_status, void *data)
+{
+ complete((struct completion *) data);
+}
+
+static void omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
+ unsigned int u32_count, int is_write)
+{
+ const int block_size = 16;
+ unsigned int block_count, len;
+ int dma_ch;
+ unsigned long fifo_reg, timeout, jiffies_before, jiffies_spent;
+ static unsigned long max_jiffies = 0;
+
+ dma_ch = omap_nand_dma_ch;
+ block_count = u32_count * 4 / block_size;
+ nand_write_reg(NND_STATUS, 0x0f);
+ nand_write_reg(NND_FIFOCTRL, (block_size << 24) | block_count);
+ fifo_reg = NAND_BASE + NND_FIFO;
+ if (is_write) {
+ omap_set_dma_dest_params(dma_ch, OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT, fifo_reg,
+ 0, 0);
+ omap_set_dma_src_params(dma_ch, OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC,
+ virt_to_phys(addr),
+ 0, 0);
+// omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
+ /* Set POSTWRITE bit */
+ nand_write_reg(NND_CTRL, nand_read_reg(NND_CTRL) | (1 << 16));
+ } else {
+ omap_set_dma_src_params(dma_ch, OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT, fifo_reg,
+ 0, 0);
+ omap_set_dma_dest_params(dma_ch, OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC,
+ virt_to_phys(addr),
+ 0, 0);
+// omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_8);
+ /* Set PREFETCH bit */
+ nand_write_reg(NND_CTRL, nand_read_reg(NND_CTRL) | (1 << 17));
+ }
+ omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, block_size / 4,
+ block_count, OMAP_DMA_SYNC_FRAME,
+ 0, 0);
+ init_completion(&omap_nand_dma_comp);
+
+ len = u32_count << 2;
+ dma_cache_maint(addr, len, DMA_TO_DEVICE);
+ omap_start_dma(dma_ch);
+ jiffies_before = jiffies;
+ timeout = wait_for_completion_timeout(&omap_nand_dma_comp,
+ msecs_to_jiffies(1000));
+ jiffies_spent = (unsigned long)((long)jiffies - (long)jiffies_before);
+ if (jiffies_spent > max_jiffies)
+ max_jiffies = jiffies_spent;
+
+ if (timeout == 0) {
+ printk(KERN_WARNING "omap-hw-nand: DMA timeout after %u ms, max. seen latency %u ms\n",
+ jiffies_to_msecs(jiffies_spent),
+ jiffies_to_msecs(max_jiffies));
+ }
+ if (!is_write)
+ dma_cache_maint(addr, len, DMA_FROM_DEVICE);
+
+ nand_write_reg(NND_CTRL, nand_read_reg(NND_CTRL) & ~((1 << 16) | (1 << 17)));
+}
+
+static void fifo_read(u32 *out, unsigned int len)
+{
+ const int block_size = 16;
+ unsigned long status_reg, fifo_reg;
+ int c;
+
+ status_reg = omap_nand_base + NND_STATUS;
+ fifo_reg = omap_nand_base + NND_FIFO;
+ len = len * 4 / block_size;
+ nand_write_reg(NND_FIFOCTRL, (block_size << 24) | len);
+ nand_write_reg(NND_STATUS, 0x0f);
+ nand_write_reg(NND_CTRL, nand_read_reg(NND_CTRL) | (1 << 17));
+ c = block_size / 4;
+ while (len--) {
+ int i;
+
+ while ((__raw_readl(status_reg) & (1 << 2)) == 0);
+ __raw_writel(0x0f, status_reg);
+ for (i = 0; i < c; i++) {
+ u32 l = __raw_readl(fifo_reg);
+ *out++ = l;
+ }
+ }
+ nand_write_reg(NND_CTRL, nand_read_reg(NND_CTRL) & ~(1 << 17));
+ nand_write_reg(NND_STATUS, 0x0f);
+}
+
+static void omap_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ unsigned long access_reg;
+
+ if (likely(((unsigned long) buf & 3) == 0 && (len & 3) == 0)) {
+ int u32_count = len >> 2;
+ u32 *dest = (u32 *) buf;
+ /* If the transfer is big enough and the length divisible by
+ * 16, we try to use DMA transfer, or FIFO copy in case of
+ * DMA failure (e.g. all channels busy) */
+ if (u32_count > 64 && (u32_count & 3) == 0) {
+ if (omap_nand_dma_ch >= 0) {
+ omap_nand_dma_transfer(mtd, buf, u32_count, 0);
+ return;
+ }
+ /* In case of an error, fallback to FIFO copy */
+ fifo_read((u32 *) buf, u32_count);
+ return;
+ }
+ access_reg = omap_nand_base + NND_ACCESS;
+ /* Small buffers we just read directly */
+ while (u32_count--)
+ *dest++ = __raw_readl(access_reg);
+ } else {
+ /* If we're not word-aligned, we use byte copy */
+ access_reg = omap_nand_base + NND_ACCESS;
+ while (len--)
+ *buf++ = __raw_readb(access_reg);
+ }
+}
+
+static void omap_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ if (likely(((unsigned long) buf & 3) == 0 && (len & 3) == 0)) {
+ const u32 *src = (const u32 *) buf;
+
+ len >>= 2;
+#if 0
+ /* If the transfer is big enough and length divisible by 16,
+ * we try to use DMA transfer. */
+ if (len > 256 / 4 && (len & 3) == 0) {
+ if (omap_nand_dma_transfer(mtd, (void *) buf, len, 1) == 0)
+ return;
+ /* In case of an error, fallback to CPU copy */
+ }
+#endif
+ while (len--)
+ nand_write_reg(NND_ACCESS, *src++);
+ } else {
+ while (len--)
+ nand_write_reg8(NND_ACCESS, *buf++);
+ }
+}
+
+static int omap_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ if (likely(((unsigned long) buf & 3) == 0 && (len & 3) == 0)) {
+ const u32 *dest = (const u32 *) buf;
+ len >>= 2;
+ while (len--)
+ if (*dest++ != nand_read_reg(NND_ACCESS))
+ return -EFAULT;
+ } else {
+ while (len--)
+ if (*buf++ != nand_read_reg8(NND_ACCESS))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static u_char omap_nand_read_byte(struct mtd_info *mtd)
+{
+ return nand_read_reg8(NND_ACCESS);
+}
+
+static int omap_nand_dev_ready(struct mtd_info *mtd)
+{
+ u32 l;
+
+ l = nand_read_reg(NND_READY);
+ return l & 0x01;
+}
+
+static int nand_write_command(u8 cmd, u32 addr, int addr_valid)
+{
+ if (addr_valid) {
+ nand_write_reg(NND_ADDR_SRC, addr);
+ nand_write_reg8(NND_COMMAND, cmd);
+ } else {
+ nand_write_reg(NND_ADDR_SRC, 0);
+ nand_write_reg8(NND_COMMAND_SEC, cmd);
+ }
+ while (!omap_nand_dev_ready(NULL));
+ return 0;
+}
+
+/*
+ * Send command to NAND device
+ */
+static void omap_nand_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+ struct nand_chip *this = mtd->priv;
+
+ /*
+ * Write out the command to the device.
+ */
+ if (command == NAND_CMD_SEQIN) {
+ int readcmd;
+
+ if (column >= mtd->writesize) {
+ /* OOB area */
+ column -= mtd->writesize;
+ readcmd = NAND_CMD_READOOB;
+ } else if (column < 256) {
+ /* First 256 bytes --> READ0 */
+ readcmd = NAND_CMD_READ0;
+ } else {
+ column -= 256;
+ readcmd = NAND_CMD_READ1;
+ }
+ nand_write_command(readcmd, 0, 0);
+ }
+ switch (command) {
+ case NAND_CMD_RESET:
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_STATUS:
+ case NAND_CMD_ERASE2:
+ nand_write_command(command, 0, 0);
+ break;
+ case NAND_CMD_ERASE1:
+ nand_write_command(command, ((page_addr & 0xFFFFFF00) << 1) | (page_addr & 0XFF), 1);
+ break;
+ default:
+ nand_write_command(command, (page_addr << this->page_shift) | column, 1);
+ }
+}
+
+static void omap_nand_command_lp(struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+ struct nand_chip *this = mtd->priv;
+
+ if (command == NAND_CMD_READOOB) {
+ column += mtd->writesize;
+ command = NAND_CMD_READ0;
+ }
+ switch (command) {
+ case NAND_CMD_RESET:
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_STATUS:
+ case NAND_CMD_ERASE2:
+ nand_write_command(command, 0, 0);
+ break;
+ case NAND_CMD_ERASE1:
+ nand_write_command(command, page_addr << this->page_shift >> 11, 1);
+ break;
+ default:
+ nand_write_command(command, (page_addr << 16) | column, 1);
+ }
+ if (command == NAND_CMD_READ0)
+ nand_write_command(NAND_CMD_READSTART, 0, 0);
+}
+
+/*
+ * Generate non-inverted ECC bytes.
+ *
+ * Using noninverted ECC can be considered ugly since writing a blank
+ * page ie. padding will clear the ECC bytes. This is no problem as long
+ * nobody is trying to write data on the seemingly unused page.
+ *
+ * Reading an erased page will produce an ECC mismatch between
+ * generated and read ECC bytes that has to be dealt with separately.
+ */
+static int omap_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+{
+ u32 l;
+ int reg;
+ int n;
+ struct nand_chip *this = mtd->priv;
+
+ /* Ex NAND_ECC_HW12_2048 */
+ if ((this->ecc.mode == NAND_ECC_HW) && (this->ecc.size == 2048))
+ n = 4;
+ else
+ n = 1;
+ reg = NND_ECC_START;
+ while (n--) {
+ l = nand_read_reg(reg);
+ *ecc_code++ = l; // P128e, ..., P1e
+ *ecc_code++ = l >> 16; // P128o, ..., P1o
+ // P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e
+ *ecc_code++ = ((l >> 8) & 0x0f) | ((l >> 20) & 0xf0);
+ reg += 4;
+ }
+ return 0;
+}
+
+/*
+ * This function will generate true ECC value, which can be used
+ * when correcting data read from NAND flash memory core
+ */
+static void gen_true_ecc(u8 *ecc_buf)
+{
+ u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8);
+
+ ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) | P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp) );
+ ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) | P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp));
+ ecc_buf[2] = ~( P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) | P1e(tmp) | P2048o(tmp) | P2048e(tmp));
+}
+
+/*
+ * This function compares two ECC's and indicates if there is an error.
+ * If the error can be corrected it will be corrected to the buffer
+ */
+static int omap_nand_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
+ u8 *ecc_data2, /* read from register */
+ u8 *page_data)
+{
+ uint i;
+ u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];
+ u8 comp0_bit[8], comp1_bit[8], comp2_bit[8];
+ u8 ecc_bit[24];
+ u8 ecc_sum = 0;
+ u8 find_bit = 0;
+ uint find_byte = 0;
+ int isEccFF;
+
+ isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF);
+
+ gen_true_ecc(ecc_data1);
+ gen_true_ecc(ecc_data2);
+
+ for (i = 0; i <= 2; i++) {
+ *(ecc_data1 + i) = ~(*(ecc_data1 + i));
+ *(ecc_data2 + i) = ~(*(ecc_data2 + i));
+ }
+
+ for (i = 0; i < 8; i++) {
+ tmp0_bit[i] = *ecc_data1 % 2;
+ *ecc_data1 = *ecc_data1 / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ tmp1_bit[i] = *(ecc_data1 + 1) % 2;
+ *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ tmp2_bit[i] = *(ecc_data1 + 2) % 2;
+ *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ comp0_bit[i] = *ecc_data2 % 2;
+ *ecc_data2 = *ecc_data2 / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ comp1_bit[i] = *(ecc_data2 + 1) % 2;
+ *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ comp2_bit[i] = *(ecc_data2 + 2) % 2;
+ *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2;
+ }
+
+ for (i = 0; i< 6; i++ )
+ ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2];
+
+ for (i = 0; i < 8; i++)
+ ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i];
+
+ for (i = 0; i < 8; i++)
+ ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i];
+
+ ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0];
+ ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1];
+
+ for (i = 0; i < 24; i++)
+ ecc_sum += ecc_bit[i];
+
+ switch (ecc_sum) {
+ case 0:
+ /* Not reached because this function is not called if
+ ECC values are equal */
+ return 0;
+
+ case 1:
+ /* Uncorrectable error */
+ DEBUG (MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n");
+ return -1;
+
+ case 12:
+ /* Correctable error */
+ find_byte = (ecc_bit[23] << 8) +
+ (ecc_bit[21] << 7) +
+ (ecc_bit[19] << 6) +
+ (ecc_bit[17] << 5) +
+ (ecc_bit[15] << 4) +
+ (ecc_bit[13] << 3) +
+ (ecc_bit[11] << 2) +
+ (ecc_bit[9] << 1) +
+ ecc_bit[7];
+
+ find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1];
+
+ DEBUG (MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at offset: %d, bit: %d\n", find_byte, find_bit);
+
+ page_data[find_byte] ^= (1 << find_bit);
+
+ return 0;
+ default:
+ if (isEccFF) {
+ if (ecc_data2[0] == 0 && ecc_data2[1] == 0 && ecc_data2[2] == 0)
+ return 0;
+ }
+ DEBUG (MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n");
+ return -1;
+ }
+}
+
+static int omap_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+{
+ struct nand_chip *this;
+ int block_count = 0, i, r;
+
+ this = mtd->priv;
+ /* Ex NAND_ECC_HW12_2048 */
+ if ((this->ecc.mode == NAND_ECC_HW) && (this->ecc.size == 2048))
+ block_count = 4;
+ else
+ block_count = 1;
+ for (i = 0; i < block_count; i++) {
+ if (memcmp(read_ecc, calc_ecc, 3) != 0) {
+ r = omap_nand_compare_ecc(read_ecc, calc_ecc, dat);
+ if (r < 0)
+ return r;
+ }
+ read_ecc += 3;
+ calc_ecc += 3;
+ dat += 512;
+ }
+ return 0;
+}
+
+static void omap_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ nand_write_reg(NND_RESET, 0x01);
+}
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+
+extern int mtdpart_setup(char *);
+
+static int __init add_dynamic_parts(struct mtd_info *mtd)
+{
+ static const char *part_parsers[] = { "cmdlinepart", NULL };
+ struct mtd_partition *parts;
+ const struct omap_flash_part_str_config *cfg;
+ char *part_str = NULL;
+ size_t part_str_len;
+ int c;
+
+ cfg = omap_get_var_config(OMAP_TAG_FLASH_PART_STR, &part_str_len);
+ if (cfg != NULL) {
+ part_str = kmalloc(part_str_len + 1, GFP_KERNEL);
+ if (part_str == NULL)
+ return -ENOMEM;
+ memcpy(part_str, cfg->part_table, part_str_len);
+ part_str[part_str_len] = '\0';
+ mtdpart_setup(part_str);
+ }
+ c = parse_mtd_partitions(omap_mtd, part_parsers, &parts, 0);
+ if (part_str != NULL) {
+ mtdpart_setup(NULL);
+ kfree(part_str);
+ }
+ if (c <= 0)
+ return -1;
+
+ add_mtd_partitions(mtd, parts, c);
+
+ return 0;
+}
+
+#else
+
+static inline int add_dynamic_parts(struct mtd_info *mtd)
+{
+ return -1;
+}
+
+#endif
+
+static inline int calc_psc(int ns, int cycle_ps)
+{
+ return (ns * 1000 + (cycle_ps - 1)) / cycle_ps;
+}
+
+static void set_psc_regs(int psc_ns, int psc1_ns, int psc2_ns)
+{
+ int psc[3], i;
+ unsigned long rate, ps;
+
+ rate = clk_get_rate(omap_nand_clk);
+ ps = 1000000000 / (rate / 1000);
+ psc[0] = calc_psc(psc_ns, ps);
+ psc[1] = calc_psc(psc1_ns, ps);
+ psc[2] = calc_psc(psc2_ns, ps);
+ for (i = 0; i < 3; i++) {
+ if (psc[i] < 2)
+ psc[i] = 2;
+ else if (psc[i] > 256)
+ psc[i] = 256;
+ }
+ nand_write_reg(NND_PSC_CLK, psc[0] - 1);
+ nand_write_reg(NND_PSC1_CLK, psc[1] - 1);
+ nand_write_reg(NND_PSC2_CLK, psc[2] - 1);
+ printk(KERN_INFO "omap-hw-nand: using PSC values %d, %d, %d\n", psc[0], psc[1], psc[2]);
+}
+
+/*
+ * Main initialization routine
+ */
+static int __init omap_nand_init(void)
+{
+ struct nand_chip *this;
+ int err = 0;
+ u32 l;
+
+ omap_nand_clk = clk_get(NULL, "armper_ck");
+ BUG_ON(omap_nand_clk == NULL);
+ clk_enable(omap_nand_clk);
+
+ l = nand_read_reg(NND_REVISION);
+ printk(KERN_INFO "omap-hw-nand: OMAP NAND Controller rev. %d.%d\n", l>>4, l & 0xf);
+
+ /* Reset the NAND Controller */
+ nand_write_reg(NND_SYSCFG, 0x02);
+ while ((nand_read_reg(NND_SYSSTATUS) & 0x01) == 0);
+
+ /* No Prefetch, no postwrite, write prot & enable pairs disabled,
+ addres counter set to send 4 byte addresses to flash,
+ A8 is set not to be sent to flash (erase addre needs formatting),
+ choose little endian, enable 512 byte ECC logic,
+ */
+ nand_write_reg(NND_CTRL, 0xFF01);
+
+ /* Allocate memory for MTD device structure and private data */
+ omap_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
+ if (!omap_mtd) {
+ printk(KERN_WARNING "omap-hw-nand: Unable to allocate OMAP NAND MTD device structure.\n");
+ err = -ENOMEM;
+ goto free_clock;
+ }
+#if 1
+ err = omap_request_dma(OMAP_DMA_NAND, "NAND", nand_dma_cb,
+ &omap_nand_dma_comp, &omap_nand_dma_ch);
+ if (err < 0) {
+ printk(KERN_WARNING "omap-hw-nand: Unable to reserve DMA channel\n");
+ omap_nand_dma_ch = -1;
+ }
+#else
+ omap_nand_dma_ch = -1;
+#endif
+ /* Get pointer to private data */
+ this = (struct nand_chip *) (&omap_mtd[1]);
+
+ /* Initialize structures */
+ memset((char *) omap_mtd, 0, sizeof(struct mtd_info));
+ memset((char *) this, 0, sizeof(struct nand_chip));
+
+ /* Link the private data with the MTD structure */
+ omap_mtd->priv = this;
+ omap_mtd->name = "omap-nand";
+
+ this->options = NAND_SKIP_BBTSCAN;
+
+ /* Used from chip select and nand_command() */
+ this->read_byte = omap_nand_read_byte;
+
+ this->select_chip = omap_nand_select_chip;
+ this->dev_ready = omap_nand_dev_ready;
+ this->chip_delay = 0;
+ this->ecc.mode = NAND_ECC_HW;
+ this->ecc.bytes = 3;
+ this->ecc.size = 512;
+ this->cmdfunc = omap_nand_command;
+ this->write_buf = omap_nand_write_buf;
+ this->read_buf = omap_nand_read_buf;
+ this->verify_buf = omap_nand_verify_buf;
+ this->ecc.calculate = omap_nand_calculate_ecc;
+ this->ecc.correct = omap_nand_correct_data;
+ this->ecc.hwctl = omap_nand_enable_hwecc;
+
+ nand_write_reg(NND_SYSCFG, 0x1); /* Enable auto idle */
+ nand_write_reg(NND_PSC_CLK, 10);
+ /* Scan to find existance of the device */
+ if (nand_scan(omap_mtd, 1)) {
+ err = -ENXIO;
+ goto out_mtd;
+ }
+
+ set_psc_regs(25, 15, 35);
+ if (this->page_shift == 11) {
+ this->cmdfunc = omap_nand_command_lp;
+ l = nand_read_reg(NND_CTRL);
+ l |= 1 << 4; /* Set the A8 bit in CTRL reg */
+ nand_write_reg(NND_CTRL, l);
+ this->ecc.mode = NAND_ECC_HW;
+ this->ecc.steps = 1;
+ this->ecc.size = 2048;
+ this->ecc.bytes = 12;
+ nand_write_reg(NND_ECC_SELECT, 6);
+ }
+
+ /* We have to do bbt scanning ourselves */
+ if (this->scan_bbt (omap_mtd)) {
+ err = -ENXIO;
+ goto out_mtd;
+ }
+
+ err = add_dynamic_parts(omap_mtd);
+ if (err < 0) {
+ printk(KERN_ERR "omap-hw-nand: no partitions defined\n");
+ err = -ENODEV;
+ nand_release(omap_mtd);
+ goto out_mtd;
+ }
+ /* init completed */
+ return 0;
+out_mtd:
+ if (omap_nand_dma_ch >= 0)
+ omap_free_dma(omap_nand_dma_ch);
+ kfree(omap_mtd);
+free_clock:
+ clk_put(omap_nand_clk);
+ return err;
+}
+
+module_init(omap_nand_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit omap_nand_cleanup (void)
+{
+ clk_disable(omap_nand_clk);
+ clk_put(omap_nand_clk);
+ nand_release(omap_mtd);
+ kfree(omap_mtd);
+}
+
+module_exit(omap_nand_cleanup);
+
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * drivers/mtd/nand/omap-nand-flash.c
+ *
+ * Copyright (c) 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
+ * Copyright (c) 2004 David Brownell
+ *
+ * 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/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
- #include <asm/arch/tc.h>
++#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/flash.h>
- #include <asm/arch/hardware.h>
- #include <asm/arch/nand.h>
++#include <mach/tc.h>
+
+#include <asm/io.h>
++#include <mach/hardware.h>
++#include <mach/nand.h>
+
+#define DRIVER_NAME "omapnand"
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+struct omap_nand_info {
+ struct omap_nand_platform_data *pdata;
+ struct mtd_partition *parts;
+ struct mtd_info mtd;
+ struct nand_chip nand;
+};
+
+/*
+ * hardware specific access to control-lines
+ * NOTE: boards may use different bits for these!!
+ *
+ * ctrl:
+ * NAND_NCE: bit 0 - don't care
+ * NAND_CLE: bit 1 -> bit 1 (0x0002)
+ * NAND_ALE: bit 2 -> bit 2 (0x0004)
+ */
+
+static void omap_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *chip = mtd->priv;
+ unsigned long mask;
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ mask = (ctrl & NAND_CLE) ? 0x02 : 0;
+ if (ctrl & NAND_ALE)
+ mask |= 0x04;
+ writeb(cmd, (unsigned long)chip->IO_ADDR_W | mask);
+}
+
+static int omap_nand_dev_ready(struct mtd_info *mtd)
+{
+ struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd);
+
+ return info->pdata->dev_ready(info->pdata);
+}
+
+static int __devinit omap_nand_probe(struct platform_device *pdev)
+{
+ struct omap_nand_info *info;
+ struct omap_nand_platform_data *pdata = pdev->dev.platform_data;
+ struct resource *res = pdev->resource;
+ unsigned long size = res->end - res->start + 1;
+ int err;
+
+ info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ if (!request_mem_region(res->start, size, pdev->dev.driver->name)) {
+ err = -EBUSY;
+ goto out_free_info;
+ }
+
+ info->nand.IO_ADDR_R = ioremap(res->start, size);
+ if (!info->nand.IO_ADDR_R) {
+ err = -ENOMEM;
+ goto out_release_mem_region;
+ }
+ info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
+ info->nand.cmd_ctrl = omap_nand_hwcontrol;
+ info->nand.ecc.mode = NAND_ECC_SOFT;
+ info->nand.options = pdata->options;
+ if (pdata->dev_ready)
+ info->nand.dev_ready = omap_nand_dev_ready;
+ else
+ info->nand.chip_delay = 20;
+
+ info->mtd.name = pdev->dev.bus_id;
+ info->mtd.priv = &info->nand;
+
+ info->pdata = pdata;
+
+ /* DIP switches on H2 and some other boards change between 8 and 16 bit
+ * bus widths for flash. Try the other width if the first try fails.
+ */
+ if (nand_scan(&info->mtd, 1)) {
+ info->nand.options ^= NAND_BUSWIDTH_16;
+ if (nand_scan(&info->mtd, 1)) {
+ err = -ENXIO;
+ goto out_iounmap;
+ }
+ }
+ info->mtd.owner = THIS_MODULE;
+
+#ifdef CONFIG_MTD_PARTITIONS
+ err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
+ if (err > 0)
+ add_mtd_partitions(&info->mtd, info->parts, err);
+ else if (err < 0 && pdata->parts)
+ add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
+ else
+#endif
+ add_mtd_device(&info->mtd);
+
+ platform_set_drvdata(pdev, info);
+
+ return 0;
+
+out_iounmap:
+ iounmap(info->nand.IO_ADDR_R);
+out_release_mem_region:
+ release_mem_region(res->start, size);
+out_free_info:
+ kfree(info);
+
+ return err;
+}
+
+static int omap_nand_remove(struct platform_device *pdev)
+{
+ struct omap_nand_info *info = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ /* Release NAND device, its internal structures and partitions */
+ nand_release(&info->mtd);
+ iounmap(info->nand.IO_ADDR_R);
+ kfree(info);
+ return 0;
+}
+
+static struct platform_driver omap_nand_driver = {
+ .probe = omap_nand_probe,
+ .remove = omap_nand_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+MODULE_ALIAS(DRIVER_NAME);
+
+static int __init omap_nand_init(void)
+{
+ return platform_driver_register(&omap_nand_driver);
+}
+
+static void __exit omap_nand_exit(void)
+{
+ platform_driver_unregister(&omap_nand_driver);
+}
+
+module_init(omap_nand_init);
+module_exit(omap_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jian Zhang <jzhang@ti.com> (and others)");
+MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
+
--- /dev/null
- #include <asm/arch/gpmc.h>
- #include <asm/arch/nand.h>
+/*
+ * drivers/mtd/nand/omap2.c
+ *
+ * Copyright (c) 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
+ * Copyright (c) 2004 Micron Technology Inc.
+ * Copyright (c) 2004 David Brownell
+ *
+ * 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/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+
+#include <asm/dma.h>
+
++#include <mach/gpmc.h>
++#include <mach/nand.h>
+
+#define GPMC_IRQ_STATUS 0x18
+#define GPMC_ECC_CONFIG 0x1F4
+#define GPMC_ECC_CONTROL 0x1F8
+#define GPMC_ECC_SIZE_CONFIG 0x1FC
+#define GPMC_ECC1_RESULT 0x200
+
+#define DRIVER_NAME "omap2-nand"
+#define NAND_IO_SIZE SZ_4K
+
+#define NAND_WP_ON 1
+#define NAND_WP_OFF 0
+#define NAND_WP_BIT 0x00000010
+#define WR_RD_PIN_MONITORING 0x00600000
+
+#define GPMC_BUF_FULL 0x00000001
+#define GPMC_BUF_EMPTY 0x00000000
+
+#define NAND_Ecc_P1e (1 << 0)
+#define NAND_Ecc_P2e (1 << 1)
+#define NAND_Ecc_P4e (1 << 2)
+#define NAND_Ecc_P8e (1 << 3)
+#define NAND_Ecc_P16e (1 << 4)
+#define NAND_Ecc_P32e (1 << 5)
+#define NAND_Ecc_P64e (1 << 6)
+#define NAND_Ecc_P128e (1 << 7)
+#define NAND_Ecc_P256e (1 << 8)
+#define NAND_Ecc_P512e (1 << 9)
+#define NAND_Ecc_P1024e (1 << 10)
+#define NAND_Ecc_P2048e (1 << 11)
+
+#define NAND_Ecc_P1o (1 << 16)
+#define NAND_Ecc_P2o (1 << 17)
+#define NAND_Ecc_P4o (1 << 18)
+#define NAND_Ecc_P8o (1 << 19)
+#define NAND_Ecc_P16o (1 << 20)
+#define NAND_Ecc_P32o (1 << 21)
+#define NAND_Ecc_P64o (1 << 22)
+#define NAND_Ecc_P128o (1 << 23)
+#define NAND_Ecc_P256o (1 << 24)
+#define NAND_Ecc_P512o (1 << 25)
+#define NAND_Ecc_P1024o (1 << 26)
+#define NAND_Ecc_P2048o (1 << 27)
+
+#define TF(value) (value ? 1 : 0)
+
+#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0)
+#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1)
+#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2)
+#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3)
+#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4)
+#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5)
+#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6)
+#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7)
+
+#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0)
+#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1)
+#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2)
+#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3)
+#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4)
+#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5)
+#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6)
+#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7)
+
+#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0)
+#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1)
+#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2)
+#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3)
+#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4)
+#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5)
+#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6)
+#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7)
+
+#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0)
+#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1)
+#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2)
+#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3)
+#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4)
+#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5)
+#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6)
+#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7)
+
+#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0)
+#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1)
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+struct omap_nand_info {
+ struct nand_hw_control controller;
+ struct omap_nand_platform_data *pdata;
+ struct mtd_info mtd;
+ struct mtd_partition *parts;
+ struct nand_chip nand;
+ struct platform_device *pdev;
+
+ int gpmc_cs;
+ unsigned long phys_base;
+ void __iomem *gpmc_cs_baseaddr;
+ void __iomem *gpmc_baseaddr;
+};
+
+/*
+ * omap_nand_wp - This function enable or disable the Write Protect feature on
+ * NAND device
+ * @mtd: MTD device structure
+ * @mode: WP ON/OFF
+ */
+static void omap_nand_wp(struct mtd_info *mtd, int mode)
+{
+ struct omap_nand_info *info = container_of(mtd,
+ struct omap_nand_info, mtd);
+
+ unsigned long config = __raw_readl(info->gpmc_baseaddr + GPMC_CONFIG);
+
+ if (mode)
+ config &= ~(NAND_WP_BIT); /* WP is ON */
+ else
+ config |= (NAND_WP_BIT); /* WP is OFF */
+
+ __raw_writel(config, (info->gpmc_baseaddr + GPMC_CONFIG));
+}
+
+/*
+ * hardware specific access to control-lines
+ * NOTE: boards may use different bits for these!!
+ *
+ * ctrl:
+ * NAND_NCE: bit 0 - don't care
+ * NAND_CLE: bit 1 -> Command Latch
+ * NAND_ALE: bit 2 -> Address Latch
+ */
+static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct omap_nand_info *info = container_of(mtd,
+ struct omap_nand_info, mtd);
+ switch (ctrl) {
+ case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
+ info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
+ GPMC_CS_NAND_COMMAND;
+ info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
+ GPMC_CS_NAND_DATA;
+ break;
+
+ case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
+ info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
+ GPMC_CS_NAND_ADDRESS;
+ info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
+ GPMC_CS_NAND_DATA;
+ break;
+
+ case NAND_CTRL_CHANGE | NAND_NCE:
+ info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
+ GPMC_CS_NAND_DATA;
+ info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
+ GPMC_CS_NAND_DATA;
+ break;
+ }
+
+ if (cmd != NAND_CMD_NONE)
+ __raw_writeb(cmd, info->nand.IO_ADDR_W);
+}
+
+/*
+ * omap_read_buf - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void omap_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct omap_nand_info *info = container_of(mtd,
+ struct omap_nand_info, mtd);
+ u16 *p = (u16 *) buf;
+
+ len >>= 1;
+
+ while (len--)
+ *p++ = cpu_to_le16(readw(info->nand.IO_ADDR_R));
+}
+
+/*
+ * omap_write_buf - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void omap_write_buf(struct mtd_info *mtd, const u_char * buf, int len)
+{
+ struct omap_nand_info *info = container_of(mtd,
+ struct omap_nand_info, mtd);
+ u16 *p = (u16 *) buf;
+
+ len >>= 1;
+
+ while (len--) {
+ writew(cpu_to_le16(*p++), info->nand.IO_ADDR_W);
+
+ while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
+ GPMC_STATUS) & GPMC_BUF_FULL));
+ }
+}
+/*
+ * omap_verify_buf - Verify chip data against buffer
+ * @mtd: MTD device structure
+ * @buf: buffer containing the data to compare
+ * @len: number of bytes to compare
+ */
+static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
+{
+ struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+ mtd);
+ u16 *p = (u16 *) buf;
+
+ len >>= 1;
+
+ while (len--) {
+
+ if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R)))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_MTD_NAND_OMAP_HWECC
+/*
+ * omap_hwecc_init-Initialize the Hardware ECC for NAND flash in GPMC controller
+ * @mtd: MTD device structure
+ */
+static void omap_hwecc_init(struct mtd_info *mtd)
+{
+ struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+ mtd);
+ register struct nand_chip *chip = mtd->priv;
+ unsigned long val = 0x0;
+
+ /* Read from ECC Control Register */
+ val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+ /* Clear all ECC | Enable Reg1 */
+ val = ((0x00000001<<8) | 0x00000001);
+ __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+
+ /* Read from ECC Size Config Register */
+ val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
+ /* ECCSIZE1=512 | Select eccResultsize[0-3] */
+ val = ((((chip->ecc.size >> 1) - 1) << 22) | (0x0000000F));
+ __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
+}
+
+/*
+ * gen_true_ecc - This function will generate true ECC value, which can be used
+ * when correcting data read from NAND flash memory core
+ * @ecc_buf: buffer to store ecc code
+ */
+static void gen_true_ecc(u8 *ecc_buf)
+{
+ u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) |
+ ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8);
+
+ ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) |
+ P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp));
+ ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) |
+ P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp));
+ ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) |
+ P1e(tmp) | P2048o(tmp) | P2048e(tmp));
+}
+
+/*
+ * omap_compare_ecc - This function compares two ECC's and indicates if there
+ * is an error. If the error can be corrected it will be corrected to the
+ * buffer
+ * @ecc_data1: ecc code from nand spare area
+ * @ecc_data2: ecc code from hardware register obtained from hardware ecc
+ * @page_data: page data
+ */
+static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
+ u8 *ecc_data2, /* read from register */
+ u8 *page_data)
+{
+ uint i;
+ u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];
+ u8 comp0_bit[8], comp1_bit[8], comp2_bit[8];
+ u8 ecc_bit[24];
+ u8 ecc_sum = 0;
+ u8 find_bit = 0;
+ uint find_byte = 0;
+ int isEccFF;
+
+ isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF);
+
+ gen_true_ecc(ecc_data1);
+ gen_true_ecc(ecc_data2);
+
+ for (i = 0; i <= 2; i++) {
+ *(ecc_data1 + i) = ~(*(ecc_data1 + i));
+ *(ecc_data2 + i) = ~(*(ecc_data2 + i));
+ }
+
+ for (i = 0; i < 8; i++) {
+ tmp0_bit[i] = *ecc_data1 % 2;
+ *ecc_data1 = *ecc_data1 / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ tmp1_bit[i] = *(ecc_data1 + 1) % 2;
+ *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ tmp2_bit[i] = *(ecc_data1 + 2) % 2;
+ *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ comp0_bit[i] = *ecc_data2 % 2;
+ *ecc_data2 = *ecc_data2 / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ comp1_bit[i] = *(ecc_data2 + 1) % 2;
+ *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2;
+ }
+
+ for (i = 0; i < 8; i++) {
+ comp2_bit[i] = *(ecc_data2 + 2) % 2;
+ *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2;
+ }
+
+ for (i = 0; i < 6; i++)
+ ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2];
+
+ for (i = 0; i < 8; i++)
+ ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i];
+
+ for (i = 0; i < 8; i++)
+ ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i];
+
+ ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0];
+ ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1];
+
+ for (i = 0; i < 24; i++)
+ ecc_sum += ecc_bit[i];
+
+ switch (ecc_sum) {
+ case 0:
+ /* Not reached because this function is not called if
+ * ECC values are equal
+ */
+ return 0;
+
+ case 1:
+ /* Uncorrectable error */
+ DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n");
+ return -1;
+
+ case 11:
+ /* UN-Correctable error */
+ DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\n");
+ return -1;
+
+ case 12:
+ /* Correctable error */
+ find_byte = (ecc_bit[23] << 8) +
+ (ecc_bit[21] << 7) +
+ (ecc_bit[19] << 6) +
+ (ecc_bit[17] << 5) +
+ (ecc_bit[15] << 4) +
+ (ecc_bit[13] << 3) +
+ (ecc_bit[11] << 2) +
+ (ecc_bit[9] << 1) +
+ ecc_bit[7];
+
+ find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1];
+
+ DEBUG(MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at "
+ "offset: %d, bit: %d\n", find_byte, find_bit);
+
+ page_data[find_byte] ^= (1 << find_bit);
+
+ return 0;
+ default:
+ if (isEccFF) {
+ if (ecc_data2[0] == 0 &&
+ ecc_data2[1] == 0 &&
+ ecc_data2[2] == 0)
+ return 0;
+ }
+ DEBUG(MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n");
+ return -1;
+ }
+}
+
+/*
+ * omap_correct_data - Compares the ecc read from nand spare area with ECC
+ * registers values and corrects one bit error if it has occured
+ * @mtd: MTD device structure
+ * @dat: page data
+ * @read_ecc: ecc read from nand flash
+ * @calc_ecc: ecc read from ECC registers
+ */
+static int omap_correct_data(struct mtd_info *mtd, u_char * dat,
+ u_char * read_ecc, u_char * calc_ecc)
+{
+ struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+ mtd);
+ int blockCnt = 0, i = 0, ret = 0;
+
+ /* Ex NAND_ECC_HW12_2048 */
+ if ((info->nand.ecc.mode == NAND_ECC_HW) &&
+ (info->nand.ecc.size == 2048))
+ blockCnt = 4;
+ else
+ blockCnt = 1;
+
+ for (i = 0; i < blockCnt; i++) {
+ if (memcmp(read_ecc, calc_ecc, 3) != 0) {
+ ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
+ if (ret < 0) return ret;
+ }
+ read_ecc += 3;
+ calc_ecc += 3;
+ dat += 512;
+ }
+ return 0;
+}
+
+/*
+ * omap_calcuate_ecc - Generate non-inverted ECC bytes.
+ * Using noninverted ECC can be considered ugly since writing a blank
+ * page ie. padding will clear the ECC bytes. This is no problem as long
+ * nobody is trying to write data on the seemingly unused page. Reading
+ * an erased page will produce an ECC mismatch between generated and read
+ * ECC bytes that has to be dealt with separately.
+ * @mtd: MTD device structure
+ * @dat: The pointer to data on which ecc is computed
+ * @ecc_code: The ecc_code buffer
+ */
+static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+ u_char *ecc_code)
+{
+ struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+ mtd);
+ unsigned long val = 0x0;
+ unsigned long reg;
+
+ /* Start Reading from HW ECC1_Result = 0x200 */
+ reg = (unsigned long)(info->gpmc_baseaddr + GPMC_ECC1_RESULT);
+ val = __raw_readl(reg);
+ *ecc_code++ = val; /* P128e, ..., P1e */
+ *ecc_code++ = val >> 16; /* P128o, ..., P1o */
+ /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
+ *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
+ reg += 4;
+
+ return 0;
+}
+
+/*
+ * omap_enable_hwecc - This function enables the hardware ecc functionality
+ * @mtd: MTD device structure
+ * @mode: Read/Write mode
+ */
+static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+ mtd);
+ register struct nand_chip *chip = mtd->priv;
+ unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
+ unsigned long val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONFIG);
+
+ switch (mode) {
+ case NAND_ECC_READ :
+ __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+ /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
+ val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
+ break;
+ case NAND_ECC_READSYN :
+ __raw_writel(0x100, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+ /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
+ val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
+ break;
+ case NAND_ECC_WRITE :
+ __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+ /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
+ val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
+ break;
+ default:
+ DEBUG(MTD_DEBUG_LEVEL0, "Error: Unrecognized Mode[%d]!\n",
+ mode);
+ break;
+ }
+
+ __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONFIG);
+}
+#endif
+
+/*
+ * omap_wait - Wait function is called during Program and erase
+ * operations and the way it is called from MTD layer, we should wait
+ * till the NAND chip is ready after the programming/erase operation
+ * has completed.
+ * @mtd: MTD device structure
+ * @chip: NAND Chip structure
+ */
+static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ register struct nand_chip *this = mtd->priv;
+ struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+ mtd);
+ int status = 0;
+
+ this->IO_ADDR_W = (void *) info->gpmc_cs_baseaddr +
+ GPMC_CS_NAND_COMMAND;
+ this->IO_ADDR_R = (void *) info->gpmc_cs_baseaddr + GPMC_CS_NAND_DATA;
+
+ while (!(status & 0x40)) {
+ __raw_writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W);
+ status = __raw_readb(this->IO_ADDR_R);
+ }
+ return status;
+}
+
+/*
+ * omap_dev_ready - calls the platform specific dev_ready function
+ * @mtd: MTD device structure
+ */
+static int omap_dev_ready(struct mtd_info *mtd)
+{
+ struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+ mtd);
+ unsigned int val = __raw_readl(info->gpmc_baseaddr + GPMC_IRQ_STATUS);
+
+ if ((val & 0x100) == 0x100) {
+ /* Clear IRQ Interrupt */
+ val |= 0x100;
+ val &= ~(0x0);
+ __raw_writel(val, info->gpmc_baseaddr + GPMC_IRQ_STATUS);
+ } else {
+ unsigned int cnt = 0;
+ while (cnt++ < 0x1FF) {
+ if ((val & 0x100) == 0x100)
+ return 0;
+ val = __raw_readl(info->gpmc_baseaddr +
+ GPMC_IRQ_STATUS);
+ }
+ }
+
+ return 1;
+}
+
+static int __devinit omap_nand_probe(struct platform_device *pdev)
+{
+ struct omap_nand_info *info;
+ struct omap_nand_platform_data *pdata;
+ int err;
+ unsigned long val;
+
+
+ pdata = pdev->dev.platform_data;
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "platform data missing\n");
+ return -ENODEV;
+ }
+
+ info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL);
+ if (!info) return -ENOMEM;
+
+ platform_set_drvdata(pdev, info);
+
+ spin_lock_init(&info->controller.lock);
+ init_waitqueue_head(&info->controller.wq);
+
+ info->pdev = pdev;
+
+ info->gpmc_cs = pdata->cs;
+ info->gpmc_baseaddr = pdata->gpmc_baseaddr;
+ info->gpmc_cs_baseaddr = pdata->gpmc_cs_baseaddr;
+
+ info->mtd.priv = &info->nand;
+ info->mtd.name = pdev->dev.bus_id;
+ info->mtd.owner = THIS_MODULE;
+
+ err = gpmc_cs_request(info->gpmc_cs, NAND_IO_SIZE, &info->phys_base);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Cannot request GPMC CS\n");
+ goto out_free_info;
+ }
+
+ /* Enable RD PIN Monitoring Reg */
+ if (pdata->dev_ready) {
+ val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1);
+ val |= WR_RD_PIN_MONITORING;
+ gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG1, val);
+ }
+
+ val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG7);
+ val &= ~(0xf << 8);
+ val |= (0xc & 0xf) << 8;
+ gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG7, val);
+
+ /* NAND write protect off */
+ omap_nand_wp(&info->mtd, NAND_WP_OFF);
+
+ if (!request_mem_region(info->phys_base, NAND_IO_SIZE,
+ pdev->dev.driver->name)) {
+ err = -EBUSY;
+ goto out_free_cs;
+ }
+
+ info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE);
+ if (!info->nand.IO_ADDR_R) {
+ err = -ENOMEM;
+ goto out_release_mem_region;
+ }
+ info->nand.controller = &info->controller;
+
+ info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
+ info->nand.cmd_ctrl = omap_hwcontrol;
+
+ info->nand.read_buf = omap_read_buf;
+ info->nand.write_buf = omap_write_buf;
+ info->nand.verify_buf = omap_verify_buf;
+
+ /*
+ * If RDY/BSY line is connected to OMAP then use the omap ready funcrtion
+ * and the generic nand_wait function which reads the status register
+ * after monitoring the RDY/BSY line.Otherwise use a standard chip delay
+ * which is slightly more than tR (AC Timing) of the NAND device and read
+ * status register until you get a failure or success
+ */
+ if (pdata->dev_ready) {
+ info->nand.dev_ready = omap_dev_ready;
+ info->nand.chip_delay = 0;
+ } else {
+ info->nand.waitfunc = omap_wait;
+ info->nand.chip_delay = 50;
+ }
+
+ info->nand.options |= NAND_SKIP_BBTSCAN;
+ if ((gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1) & 0x3000)
+ == 0x1000)
+ info->nand.options |= NAND_BUSWIDTH_16;
+
+#ifdef CONFIG_MTD_NAND_OMAP_HWECC
+ info->nand.ecc.bytes = 3;
+ info->nand.ecc.size = 512;
+ info->nand.ecc.calculate = omap_calculate_ecc;
+ info->nand.ecc.hwctl = omap_enable_hwecc;
+ info->nand.ecc.correct = omap_correct_data;
+ info->nand.ecc.mode = NAND_ECC_HW;
+
+ /* init HW ECC */
+ omap_hwecc_init(&info->mtd);
+#else
+ info->nand.ecc.mode = NAND_ECC_SOFT;
+#endif
+
+ /* DIP switches on some boards change between 8 and 16 bit
+ * bus widths for flash. Try the other width if the first try fails.
+ */
+ if (nand_scan(&info->mtd, 1)) {
+ info->nand.options ^= NAND_BUSWIDTH_16;
+ if (nand_scan(&info->mtd, 1)) {
+ err = -ENXIO;
+ goto out_release_mem_region;
+ }
+ }
+
+#ifdef CONFIG_MTD_PARTITIONS
+ err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
+ if (err > 0)
+ add_mtd_partitions(&info->mtd, info->parts, err);
+ else if (pdata->parts)
+ add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
+ else
+#endif
+ add_mtd_device(&info->mtd);
+
+ platform_set_drvdata(pdev, &info->mtd);
+
+ return 0;
+
+out_release_mem_region:
+ release_mem_region(info->phys_base, NAND_IO_SIZE);
+out_free_cs:
+ gpmc_cs_free(info->gpmc_cs);
+out_free_info:
+ kfree(info);
+
+ return err;
+}
+
+static int omap_nand_remove(struct platform_device *pdev)
+{
+ struct mtd_info *mtd = platform_get_drvdata(pdev);
+ struct omap_nand_info *info = mtd->priv;
+
+ platform_set_drvdata(pdev, NULL);
+ /* Release NAND device, its internal structures and partitions */
+ nand_release(&info->mtd);
+ iounmap(info->nand.IO_ADDR_R);
+ kfree(&info->mtd);
+ return 0;
+}
+
+static struct platform_driver omap_nand_driver = {
+ .probe = omap_nand_probe,
+ .remove = omap_nand_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+MODULE_ALIAS(DRIVER_NAME);
+
+static int __init omap_nand_init(void)
+{
+ printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME);
+ return platform_driver_register(&omap_nand_driver);
+}
+
+static void __exit omap_nand_exit(void)
+{
+ platform_driver_unregister(&omap_nand_driver);
+}
+
+module_init(omap_nand_init);
+module_exit(omap_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
--- /dev/null
- #include <asm/arch/gpmc.h>
- #include <asm/arch/onenand.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/gpmc.h>
- #include <asm/arch/pm.h>
+/*
+ * linux/drivers/mtd/onenand/omap2.c
+ *
+ * OneNAND driver for OMAP2 / OMAP3
+ *
+ * Copyright (C) 2005-2006 Nokia Corporation
+ *
+ * Author: Jarkko Lavinen <jarkko.lavinen@nokia.com> and Juha Yrjola
+ * IRQ and DMA support written by Timo Teras
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/mach/flash.h>
- #include <asm/arch/dma.h>
++#include <mach/gpmc.h>
++#include <mach/onenand.h>
++#include <mach/gpio.h>
++#include <mach/gpmc.h>
++#include <mach/pm.h>
+
+#include <linux/dma-mapping.h>
+#include <asm/dma-mapping.h>
- #include <asm/arch/board.h>
++#include <mach/dma.h>
+
++#include <mach/board.h>
+
+#define DRIVER_NAME "omap2-onenand"
+
+#define ONENAND_IO_SIZE SZ_128K
+#define ONENAND_BUFRAM_SIZE (1024 * 5)
+
+struct omap2_onenand {
+ struct platform_device *pdev;
+ int gpmc_cs;
+ unsigned long phys_base;
+ int gpio_irq;
+ struct mtd_info mtd;
+ struct mtd_partition *parts;
+ struct onenand_chip onenand;
+ struct completion irq_done;
+ struct completion dma_done;
+ int dma_channel;
+ int freq;
+ int (*setup)(void __iomem *base, int freq);
+};
+
+static void omap2_onenand_dma_cb(int lch, u16 ch_status, void *data)
+{
+ struct omap2_onenand *c = data;
+
+ complete(&c->dma_done);
+}
+
+static irqreturn_t omap2_onenand_interrupt(int irq, void *dev_id)
+{
+ struct omap2_onenand *c = dev_id;
+
+ complete(&c->irq_done);
+
+ return IRQ_HANDLED;
+}
+
+static inline unsigned short read_reg(struct omap2_onenand *c, int reg)
+{
+ return readw(c->onenand.base + reg);
+}
+
+static inline void write_reg(struct omap2_onenand *c, unsigned short value,
+ int reg)
+{
+ writew(value, c->onenand.base + reg);
+}
+
+static void wait_err(char *msg, int state, unsigned int ctrl, unsigned int intr)
+{
+ printk(KERN_ERR "onenand_wait: %s! state %d ctrl 0x%04x intr 0x%04x\n",
+ msg, state, ctrl, intr);
+}
+
+static void wait_warn(char *msg, int state, unsigned int ctrl,
+ unsigned int intr)
+{
+ printk(KERN_WARNING "onenand_wait: %s! state %d ctrl 0x%04x "
+ "intr 0x%04x\n", msg, state, ctrl, intr);
+}
+
+static int omap2_onenand_wait(struct mtd_info *mtd, int state)
+{
+ struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
+ unsigned int intr = 0;
+ unsigned int ctrl;
+ unsigned long timeout;
+ u32 syscfg;
+
+ if (state == FL_RESETING) {
+ int i;
+
+ for (i = 0; i < 20; i++) {
+ udelay(1);
+ intr = read_reg(c, ONENAND_REG_INTERRUPT);
+ if (intr & ONENAND_INT_MASTER)
+ break;
+ }
+ ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
+ if (ctrl & ONENAND_CTRL_ERROR) {
+ wait_err("controller error", state, ctrl, intr);
+ return -EIO;
+ }
+ if (!(intr & ONENAND_INT_RESET)) {
+ wait_err("timeout", state, ctrl, intr);
+ return -EIO;
+ }
+ return 0;
+ }
+
+ if (state != FL_READING) {
+ int result;
+
+ /* Turn interrupts on */
+ syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
+ syscfg |= ONENAND_SYS_CFG1_IOBE;
+ write_reg(c, syscfg, ONENAND_REG_SYS_CFG1);
+
+ INIT_COMPLETION(c->irq_done);
+ if (c->gpio_irq) {
+ result = omap_get_gpio_datain(c->gpio_irq);
+ if (result == -1) {
+ ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
+ intr = read_reg(c, ONENAND_REG_INTERRUPT);
+ wait_err("gpio error", state, ctrl, intr);
+ return -EIO;
+ }
+ } else
+ result = 0;
+ if (result == 0) {
+ int retry_cnt = 0;
+retry:
+ result = wait_for_completion_timeout(&c->irq_done,
+ msecs_to_jiffies(20));
+ if (result == 0) {
+ /* Timeout after 20ms */
+ ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
+ if (ctrl & ONENAND_CTRL_ONGO) {
+ /*
+ * The operation seems to be still going
+ * so give it some more time.
+ */
+ retry_cnt += 1;
+ if (retry_cnt < 3)
+ goto retry;
+ intr = read_reg(c,
+ ONENAND_REG_INTERRUPT);
+ wait_err("timeout", state, ctrl, intr);
+ return -EIO;
+ }
+ intr = read_reg(c, ONENAND_REG_INTERRUPT);
+ if ((intr & ONENAND_INT_MASTER) == 0)
+ wait_warn("timeout", state, ctrl, intr);
+ }
+ }
+ } else {
+ /* Turn interrupts off */
+ syscfg = read_reg(c, ONENAND_REG_SYS_CFG1);
+ syscfg &= ~ONENAND_SYS_CFG1_IOBE;
+ write_reg(c, syscfg, ONENAND_REG_SYS_CFG1);
+
+ timeout = jiffies + msecs_to_jiffies(20);
+ while (time_before(jiffies, timeout)) {
+ intr = read_reg(c, ONENAND_REG_INTERRUPT);
+ if (intr & ONENAND_INT_MASTER)
+ break;
+ }
+ }
+
+ intr = read_reg(c, ONENAND_REG_INTERRUPT);
+ ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
+
+ if (intr & ONENAND_INT_READ) {
+ int ecc = read_reg(c, ONENAND_REG_ECC_STATUS);
+
+ if (ecc) {
+ unsigned int addr1, addr8;
+
+ addr1 = read_reg(c, ONENAND_REG_START_ADDRESS1);
+ addr8 = read_reg(c, ONENAND_REG_START_ADDRESS8);
+ if (ecc & ONENAND_ECC_2BIT_ALL) {
+ printk(KERN_ERR "onenand_wait: ECC error = "
+ "0x%04x, addr1 %#x, addr8 %#x\n",
+ ecc, addr1, addr8);
+ mtd->ecc_stats.failed++;
+ return -EBADMSG;
+ } else if (ecc & ONENAND_ECC_1BIT_ALL) {
+ printk(KERN_NOTICE "onenand_wait: correctable "
+ "ECC error = 0x%04x, addr1 %#x, "
+ "addr8 %#x\n", ecc, addr1, addr8);
+ mtd->ecc_stats.corrected++;
+ }
+ }
+ } else if (state == FL_READING) {
+ wait_err("timeout", state, ctrl, intr);
+ return -EIO;
+ }
+
+ if (ctrl & ONENAND_CTRL_ERROR) {
+ wait_err("controller error", state, ctrl, intr);
+ if (ctrl & ONENAND_CTRL_LOCK)
+ printk(KERN_ERR "onenand_wait: "
+ "Device is write protected!!!\n");
+ return -EIO;
+ }
+
+ if (ctrl & 0xFE9F)
+ wait_warn("unexpected controller status", state, ctrl, intr);
+
+ return 0;
+}
+
+static inline int omap2_onenand_bufferram_offset(struct mtd_info *mtd, int area)
+{
+ struct onenand_chip *this = mtd->priv;
+
+ if (ONENAND_CURRENT_BUFFERRAM(this)) {
+ if (area == ONENAND_DATARAM)
+ return mtd->writesize;
+ if (area == ONENAND_SPARERAM)
+ return mtd->oobsize;
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_ARCH_OMAP3) || defined(MULTI_OMAP2)
+
+static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area,
+ unsigned char *buffer, int offset,
+ size_t count)
+{
+ struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
+ struct onenand_chip *this = mtd->priv;
+ dma_addr_t dma_src, dma_dst;
+ int bram_offset;
+ unsigned long timeout;
+ void *buf = (void *)buffer;
+ size_t xtra;
+ volatile unsigned *done;
+
+ bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;
+ if (bram_offset & 3 || (size_t)buf & 3 || count < 384)
+ goto out_copy;
+
+ if (buf >= high_memory) {
+ struct page *p1;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + count - 1) & PAGE_MASK))
+ goto out_copy;
+ p1 = vmalloc_to_page(buf);
+ if (!p1)
+ goto out_copy;
+ buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ xtra = count & 3;
+ if (xtra) {
+ count -= xtra;
+ memcpy(buf + count, this->base + bram_offset + count, xtra);
+ }
+
+ dma_src = c->phys_base + bram_offset;
+ dma_dst = dma_map_single(&c->pdev->dev, buf, count, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&c->pdev->dev, dma_dst)) {
+ dev_err(&c->pdev->dev,
+ "Couldn't DMA map a %d byte buffer\n",
+ count);
+ goto out_copy;
+ }
+
+ omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S32,
+ count >> 2, 1, 0, 0, 0);
+ omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,
+ dma_src, 0, 0);
+ omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,
+ dma_dst, 0, 0);
+
+ INIT_COMPLETION(c->dma_done);
+ omap_start_dma(c->dma_channel);
+
+ timeout = jiffies + msecs_to_jiffies(20);
+ done = &c->dma_done.done;
+ while (time_before(jiffies, timeout))
+ if (*done)
+ break;
+
+ dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE);
+
+ if (!*done) {
+ dev_err(&c->pdev->dev, "timeout waiting for DMA\n");
+ goto out_copy;
+ }
+
+ return 0;
+
+out_copy:
+ memcpy(buf, this->base + bram_offset, count);
+ return 0;
+}
+
+static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area,
+ const unsigned char *buffer,
+ int offset, size_t count)
+{
+ struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
+ struct onenand_chip *this = mtd->priv;
+ dma_addr_t dma_src, dma_dst;
+ int bram_offset;
+ unsigned long timeout;
+ void *buf = (void *)buffer;
+ volatile unsigned *done;
+
+ bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;
+ if (bram_offset & 3 || (size_t)buf & 3 || count < 384)
+ goto out_copy;
+
+ /* panic_write() may be in an interrupt context */
+ if (in_interrupt())
+ goto out_copy;
+
+ if (buf >= high_memory) {
+ struct page *p1;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + count - 1) & PAGE_MASK))
+ goto out_copy;
+ p1 = vmalloc_to_page(buf);
+ if (!p1)
+ goto out_copy;
+ buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_src = dma_map_single(&c->pdev->dev, buf, count, DMA_TO_DEVICE);
+ dma_dst = c->phys_base + bram_offset;
+ if (dma_mapping_error(&c->pdev->dev, dma_dst)) {
+ dev_err(&c->pdev->dev,
+ "Couldn't DMA map a %d byte buffer\n",
+ count);
+ return -1;
+ }
+
+ omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S32,
+ count >> 2, 1, 0, 0, 0);
+ omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,
+ dma_src, 0, 0);
+ omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,
+ dma_dst, 0, 0);
+
+ INIT_COMPLETION(c->dma_done);
+ omap_start_dma(c->dma_channel);
+
+ timeout = jiffies + msecs_to_jiffies(20);
+ done = &c->dma_done.done;
+ while (time_before(jiffies, timeout))
+ if (*done)
+ break;
+
+ dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_TO_DEVICE);
+
+ if (!*done) {
+ dev_err(&c->pdev->dev, "timeout waiting for DMA\n");
+ goto out_copy;
+ }
+
+ return 0;
+
+out_copy:
+ memcpy(this->base + bram_offset, buf, count);
+ return 0;
+}
+
+#else
+
+int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area,
+ unsigned char *buffer, int offset,
+ size_t count);
+
+int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area,
+ const unsigned char *buffer,
+ int offset, size_t count);
+
+#endif
+
+#if defined(CONFIG_ARCH_OMAP2) || defined(MULTI_OMAP2)
+
+static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area,
+ unsigned char *buffer, int offset,
+ size_t count)
+{
+ struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
+ struct onenand_chip *this = mtd->priv;
+ dma_addr_t dma_src, dma_dst;
+ int bram_offset;
+
+ bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;
+ /* DMA is not used. Revisit PM requirements before enabling it. */
+ if (1 || (c->dma_channel < 0) ||
+ ((void *) buffer >= (void *) high_memory) || (bram_offset & 3) ||
+ (((unsigned int) buffer) & 3) || (count < 1024) || (count & 3)) {
+ memcpy(buffer, (__force void *)(this->base + bram_offset),
+ count);
+ return 0;
+ }
+
+ dma_src = c->phys_base + bram_offset;
+ dma_dst = dma_map_single(&c->pdev->dev, buffer, count,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&c->pdev->dev, dma_dst)) {
+ dev_err(&c->pdev->dev,
+ "Couldn't DMA map a %d byte buffer\n",
+ count);
+ return -1;
+ }
+
+ omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S32,
+ count / 4, 1, 0, 0, 0);
+ omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,
+ dma_src, 0, 0);
+ omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,
+ dma_dst, 0, 0);
+
+ INIT_COMPLETION(c->dma_done);
+ omap_start_dma(c->dma_channel);
+ wait_for_completion(&c->dma_done);
+
+ dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE);
+
+ return 0;
+}
+
+static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area,
+ const unsigned char *buffer,
+ int offset, size_t count)
+{
+ struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd);
+ struct onenand_chip *this = mtd->priv;
+ dma_addr_t dma_src, dma_dst;
+ int bram_offset;
+
+ bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset;
+ /* DMA is not used. Revisit PM requirements before enabling it. */
+ if (1 || (c->dma_channel < 0) ||
+ ((void *) buffer >= (void *) high_memory) || (bram_offset & 3) ||
+ (((unsigned int) buffer) & 3) || (count < 1024) || (count & 3)) {
+ memcpy((__force void *)(this->base + bram_offset), buffer,
+ count);
+ return 0;
+ }
+
+ dma_src = dma_map_single(&c->pdev->dev, (void *) buffer, count,
+ DMA_TO_DEVICE);
+ dma_dst = c->phys_base + bram_offset;
+ if (dma_mapping_error(&c->pdev->dev, dma_dst)) {
+ dev_err(&c->pdev->dev,
+ "Couldn't DMA map a %d byte buffer\n",
+ count);
+ return -1;
+ }
+
+ omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S16,
+ count / 2, 1, 0, 0, 0);
+ omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,
+ dma_src, 0, 0);
+ omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC,
+ dma_dst, 0, 0);
+
+ INIT_COMPLETION(c->dma_done);
+ omap_start_dma(c->dma_channel);
+ wait_for_completion(&c->dma_done);
+
+ dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_TO_DEVICE);
+
+ return 0;
+}
+
+#else
+
+int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area,
+ unsigned char *buffer, int offset,
+ size_t count);
+
+int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area,
+ const unsigned char *buffer,
+ int offset, size_t count);
+
+#endif
+
+static struct platform_driver omap2_onenand_driver;
+
+static int __adjust_timing(struct device *dev, void *data)
+{
+ int ret = 0;
+ struct omap2_onenand *c;
+
+ c = dev_get_drvdata(dev);
+
+ BUG_ON(c->setup == NULL);
+
+ /* DMA is not in use so this is all that is needed */
+ /* Revisit for OMAP3! */
+ ret = c->setup(c->onenand.base, c->freq);
+
+ return ret;
+}
+
+int omap2_onenand_rephase(void)
+{
+ return driver_for_each_device(&omap2_onenand_driver.driver, NULL,
+ NULL, __adjust_timing);
+}
+
+static void __devexit omap2_onenand_shutdown(struct platform_device *pdev)
+{
+ struct omap2_onenand *c = dev_get_drvdata(&pdev->dev);
+
+ /* With certain content in the buffer RAM, the OMAP boot ROM code
+ * can recognize the flash chip incorrectly. Zero it out before
+ * soft reset.
+ */
+ memset((__force void *)c->onenand.base, 0, ONENAND_BUFRAM_SIZE);
+}
+
+static int __devinit omap2_onenand_probe(struct platform_device *pdev)
+{
+ struct omap_onenand_platform_data *pdata;
+ struct omap2_onenand *c;
+ int r;
+
+ pdata = pdev->dev.platform_data;
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "platform data missing\n");
+ return -ENODEV;
+ }
+
+ c = kzalloc(sizeof(struct omap2_onenand), GFP_KERNEL);
+ if (!c)
+ return -ENOMEM;
+
+ init_completion(&c->irq_done);
+ init_completion(&c->dma_done);
+ c->gpmc_cs = pdata->cs;
+ c->gpio_irq = pdata->gpio_irq;
+ c->dma_channel = pdata->dma_channel;
+ if (c->dma_channel < 0) {
+ /* if -1, don't use DMA */
+ c->gpio_irq = 0;
+ }
+
+ r = gpmc_cs_request(c->gpmc_cs, ONENAND_IO_SIZE, &c->phys_base);
+ if (r < 0) {
+ dev_err(&pdev->dev, "Cannot request GPMC CS\n");
+ goto err_kfree;
+ }
+
+ if (request_mem_region(c->phys_base, ONENAND_IO_SIZE,
+ pdev->dev.driver->name) == NULL) {
+ dev_err(&pdev->dev, "Cannot reserve memory region at 0x%08lx, "
+ "size: 0x%x\n", c->phys_base, ONENAND_IO_SIZE);
+ r = -EBUSY;
+ goto err_free_cs;
+ }
+ c->onenand.base = ioremap(c->phys_base, ONENAND_IO_SIZE);
+ if (c->onenand.base == NULL) {
+ r = -ENOMEM;
+ goto err_release_mem_region;
+ }
+
+ if (pdata->onenand_setup != NULL) {
+ r = pdata->onenand_setup(c->onenand.base, c->freq);
+ if (r < 0) {
+ dev_err(&pdev->dev, "Onenand platform setup failed: "
+ "%d\n", r);
+ goto err_iounmap;
+ }
+ c->setup = pdata->onenand_setup;
+ }
+
+ if (c->gpio_irq) {
+ if ((r = omap_request_gpio(c->gpio_irq)) < 0) {
+ dev_err(&pdev->dev, "Failed to request GPIO%d for "
+ "OneNAND\n", c->gpio_irq);
+ goto err_iounmap;
+ }
+ omap_set_gpio_direction(c->gpio_irq, 1);
+
+ if ((r = request_irq(OMAP_GPIO_IRQ(c->gpio_irq),
+ omap2_onenand_interrupt, IRQF_TRIGGER_RISING,
+ pdev->dev.driver->name, c)) < 0)
+ goto err_release_gpio;
+ }
+
+ if (c->dma_channel >= 0) {
+ r = omap_request_dma(0, pdev->dev.driver->name,
+ omap2_onenand_dma_cb, (void *) c,
+ &c->dma_channel);
+ if (r == 0) {
+ omap_set_dma_write_mode(c->dma_channel,
+ OMAP_DMA_WRITE_NON_POSTED);
+ omap_set_dma_src_data_pack(c->dma_channel, 1);
+ omap_set_dma_src_burst_mode(c->dma_channel,
+ OMAP_DMA_DATA_BURST_8);
+ omap_set_dma_dest_data_pack(c->dma_channel, 1);
+ omap_set_dma_dest_burst_mode(c->dma_channel,
+ OMAP_DMA_DATA_BURST_8);
+ } else {
+ dev_info(&pdev->dev,
+ "failed to allocate DMA for OneNAND, "
+ "using PIO instead\n");
+ c->dma_channel = -1;
+ }
+ }
+
+ dev_info(&pdev->dev, "initializing on CS%d, phys base 0x%08lx, virtual "
+ "base %p\n", c->gpmc_cs, c->phys_base,
+ c->onenand.base);
+
+ c->pdev = pdev;
+ c->mtd.name = pdev->dev.bus_id;
+ c->mtd.priv = &c->onenand;
+ c->mtd.owner = THIS_MODULE;
+
+ if (c->dma_channel >= 0) {
+ struct onenand_chip *this = &c->onenand;
+
+ this->wait = omap2_onenand_wait;
+ if (cpu_is_omap34xx()) {
+ this->read_bufferram = omap3_onenand_read_bufferram;
+ this->write_bufferram = omap3_onenand_write_bufferram;
+ } else {
+ this->read_bufferram = omap2_onenand_read_bufferram;
+ this->write_bufferram = omap2_onenand_write_bufferram;
+ }
+ }
+
+ if ((r = onenand_scan(&c->mtd, 1)) < 0)
+ goto err_release_dma;
+
+ switch ((c->onenand.version_id >> 4) & 0xf) {
+ case 0:
+ c->freq = 40;
+ break;
+ case 1:
+ c->freq = 54;
+ break;
+ case 2:
+ c->freq = 66;
+ break;
+ case 3:
+ c->freq = 83;
+ break;
+ }
+
+#ifdef CONFIG_MTD_PARTITIONS
+ if (pdata->parts != NULL)
+ r = add_mtd_partitions(&c->mtd, pdata->parts,
+ pdata->nr_parts);
+ else
+#endif
+ r = add_mtd_device(&c->mtd);
+ if (r < 0)
+ goto err_release_onenand;
+
+ platform_set_drvdata(pdev, c);
+
+ return 0;
+
+err_release_onenand:
+ onenand_release(&c->mtd);
+err_release_dma:
+ if (c->dma_channel != -1)
+ omap_free_dma(c->dma_channel);
+ if (c->gpio_irq)
+ free_irq(OMAP_GPIO_IRQ(c->gpio_irq), c);
+err_release_gpio:
+ if (c->gpio_irq)
+ omap_free_gpio(c->gpio_irq);
+err_iounmap:
+ iounmap(c->onenand.base);
+err_release_mem_region:
+ release_mem_region(c->phys_base, ONENAND_IO_SIZE);
+err_free_cs:
+ gpmc_cs_free(c->gpmc_cs);
+err_kfree:
+ kfree(c);
+
+ return r;
+}
+
+static int __devexit omap2_onenand_remove(struct platform_device *pdev)
+{
+ struct omap2_onenand *c = dev_get_drvdata(&pdev->dev);
+
+ BUG_ON(c == NULL);
+
+#ifdef CONFIG_MTD_PARTITIONS
+ if (c->parts)
+ del_mtd_partitions(&c->mtd);
+ else
+ del_mtd_device(&c->mtd);
+#else
+ del_mtd_device(&c->mtd);
+#endif
+
+ onenand_release(&c->mtd);
+ if (c->dma_channel != -1)
+ omap_free_dma(c->dma_channel);
+ omap2_onenand_shutdown(pdev);
+ platform_set_drvdata(pdev, NULL);
+ if (c->gpio_irq) {
+ free_irq(OMAP_GPIO_IRQ(c->gpio_irq), c);
+ omap_free_gpio(c->gpio_irq);
+ }
+ iounmap(c->onenand.base);
+ release_mem_region(c->phys_base, ONENAND_IO_SIZE);
+ kfree(c);
+
+ return 0;
+}
+
+static struct platform_driver omap2_onenand_driver = {
+ .probe = omap2_onenand_probe,
+ .remove = omap2_onenand_remove,
+ .shutdown = omap2_onenand_shutdown,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap2_onenand_init(void)
+{
+ printk(KERN_INFO "OneNAND driver initializing\n");
+ return platform_driver_register(&omap2_onenand_driver);
+}
+
+static void __exit omap2_onenand_exit(void)
+{
+ platform_driver_unregister(&omap2_onenand_driver);
+}
+
+module_init(omap2_onenand_init);
+module_exit(omap2_onenand_exit);
+
+MODULE_ALIAS(DRIVER_NAME);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarkko Lavinen <jarkko.lavinen@nokia.com>");
+MODULE_DESCRIPTION("Glue layer for OneNAND flash on OMAP2 / OMAP3");
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * BRIEF MODULE DESCRIPTION
+ *
+ * Infra-red driver for the OMAP1610-H2 and OMAP1710-H3 and H4 Platforms
+ * (SIR/MIR/FIR modes)
+ * (based on omap-sir.c)
+ *
+ * Copyright 2003 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ * source@mvista.com
+ *
+ * Copyright 2004 Texas Instruments.
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ Modifications:
+ Feb 2004, Texas Instruments
+ - Ported to 2.6 kernel (Feb 2004).
+ *
+ Apr 2004, Texas Instruments
+ - Added support for H3 (Apr 2004).
+ Nov 2004, Texas Instruments
+ - Added support for Power Management.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/rtnetlink.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irda_device.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/irda.h>
++#include <mach/hardware.h>
+#include <asm/serial.h>
+#include <asm/mach-types.h>
+#include <asm/dma.h>
++#include <mach/mux.h>
++#include <mach/gpio.h>
++#include <mach/irda.h>
+
+#define UART3_EFR_EN (1 << 4)
+#define UART3_MCR_EN_TCR_TLR (1 << 6)
+
+#define UART3_LCR_WL_8 (3 << 0)
+#define UART3_LCR_SP2 (1 << 2)
+#define UART3_LCR_DIVEN (1 << 7)
+
+#define UART3_FCR_FIFO_EN (1 << 0)
+#define UART3_FCR_FIFO_RX (1 << 1)
+#define UART3_FCR_FIFO_TX (1 << 2)
+#define UART3_FCR_FIFO_DMA1 (1 << 3)
+#define UART3_FCR_FIFO_TX_TRIG16 (1 << 4)
+#define UART3_FCR_FIFO_RX_TRIG16 (1 << 6)
+#define UART3_FCR_CONFIG (\
+ UART3_FCR_FIFO_EN | UART3_FCR_FIFO_RX |\
+ UART3_FCR_FIFO_TX | UART3_FCR_FIFO_DMA1 |\
+ UART3_FCR_FIFO_TX_TRIG16 |\
+ UART3_FCR_FIFO_RX_TRIG16)
+
+#define UART3_SCR_TX_TRIG1 (1 << 6)
+#define UART3_SCR_RX_TRIG1 (1 << 7)
+
+#define UART3_MDR1_RESET (0x07)
+#define UART3_MDR1_SIR (1 << 0)
+#define UART3_MDR1_MIR (4 << 0)
+#define UART3_MDR1_FIR (5 << 0)
+#define UART3_MDR1_SIP_AUTO (1 << 6)
+
+#define UART3_MDR2_TRIG1 (0 << 1)
+#define UART3_MDR2_IRTX_UNDERRUN (1 << 0)
+
+#define UART3_ACERG_TX_UNDERRUN_DIS (1 << 4)
+#define UART3_ACERG_SD_MODE_LOW (1 << 6)
+#define UART3_ACERG_DIS_IR_RX (1 << 5)
+
+#define UART3_IER_EOF (1 << 5)
+#define UART3_IER_CTS (1 << 7)
+
+#define UART3_IIR_TX_STATUS (1 << 5)
+#define UART3_IIR_EOF (0x80)
+
+#define IS_FIR(omap_ir) ((omap_ir)->speed >= 4000000)
+
+struct omap_irda {
+ unsigned char open;
+ int speed; /* Current IrDA speed */
+ int newspeed;
+
+ struct net_device_stats stats;
+ struct irlap_cb *irlap;
+ struct qos_info qos;
+
+ int rx_dma_channel;
+ int tx_dma_channel;
+
+ dma_addr_t rx_buf_dma_phys; /* Physical address of RX DMA buffer */
+ dma_addr_t tx_buf_dma_phys; /* Physical address of TX DMA buffer */
+
+ void *rx_buf_dma_virt; /* Virtual address of RX DMA buffer */
+ void *tx_buf_dma_virt; /* Virtual address of TX DMA buffer */
+
+ struct device *dev;
+ struct omap_irda_config *pdata;
+};
+
+static void inline uart_reg_out(int idx, u8 val)
+{
+ omap_writeb(val, idx);
+}
+
+static u8 inline uart_reg_in(int idx)
+{
+ u8 b = omap_readb(idx);
+ return b;
+}
+
+/* forward declarations */
+extern void omap_stop_dma(int lch);
+static int omap_irda_set_speed(struct net_device *dev, int speed);
+
+static void omap_irda_start_rx_dma(struct omap_irda *omap_ir)
+{
+ /* Configure DMA */
+ omap_set_dma_src_params(omap_ir->rx_dma_channel, 0x3, 0x0,
+ omap_ir->pdata->src_start,
+ 0, 0);
+
+ omap_enable_dma_irq(omap_ir->rx_dma_channel, 0x01);
+
+ omap_set_dma_dest_params(omap_ir->rx_dma_channel, 0x0, 0x1,
+ omap_ir->rx_buf_dma_phys,
+ 0, 0);
+
+ omap_set_dma_transfer_params(omap_ir->rx_dma_channel, 0x0,
+ IRDA_SKB_MAX_MTU, 0x1,
+ 0x0, omap_ir->pdata->rx_trigger, 0);
+
+ omap_start_dma(omap_ir->rx_dma_channel);
+}
+
+static void omap_start_tx_dma(struct omap_irda *omap_ir, int size)
+{
+ /* Configure DMA */
+ omap_set_dma_dest_params(omap_ir->tx_dma_channel, 0x03, 0x0,
+ omap_ir->pdata->dest_start, 0, 0);
+
+ omap_enable_dma_irq(omap_ir->tx_dma_channel, 0x01);
+
+ omap_set_dma_src_params(omap_ir->tx_dma_channel, 0x0, 0x1,
+ omap_ir->tx_buf_dma_phys,
+ 0, 0);
+
+ omap_set_dma_transfer_params(omap_ir->tx_dma_channel, 0x0, size, 0x1,
+ 0x0, omap_ir->pdata->tx_trigger, 0);
+
+ /* Start DMA */
+ omap_start_dma(omap_ir->tx_dma_channel);
+}
+
+/* DMA RX callback - normally, we should not go here,
+ * it calls only if something is going wrong
+ */
+static void omap_irda_rx_dma_callback(int lch, u16 ch_status, void *data)
+{
+ struct net_device *dev = data;
+ struct omap_irda *omap_ir = netdev_priv(dev);
+
+ printk(KERN_ERR "RX Transfer error or very big frame\n");
+
+ /* Clear interrupts */
+ uart_reg_in(UART3_IIR);
+
+ omap_ir->stats.rx_frame_errors++;
+
+ uart_reg_in(UART3_RESUME);
+
+ /* Re-init RX DMA */
+ omap_irda_start_rx_dma(omap_ir);
+}
+
+/* DMA TX callback - calling when frame transfer has been finished */
+static void omap_irda_tx_dma_callback(int lch, u16 ch_status, void *data)
+{
+ struct net_device *dev = data;
+ struct omap_irda *omap_ir = netdev_priv(dev);
+
+ /*Stop DMA controller */
+ omap_stop_dma(omap_ir->tx_dma_channel);
+}
+
+/*
+ * Set the IrDA communications speed.
+ * Interrupt have to be disabled here.
+ */
+static int omap_irda_startup(struct net_device *dev)
+{
+ struct omap_irda *omap_ir = netdev_priv(dev);
+
+ /* FIXME: use clk_* apis for UART3 clock*/
+ /* Enable UART3 clock and set UART3 to IrDA mode */
+ if (machine_is_omap_h2() || machine_is_omap_h3())
+ omap_writel(omap_readl(MOD_CONF_CTRL_0) | (1 << 31) | (1 << 15),
+ MOD_CONF_CTRL_0);
+
+ /* Only for H2?
+ */
+ if (omap_ir->pdata->transceiver_mode && machine_is_omap_h2()) {
+ /* Is it select_irda on H2 ? */
+ omap_writel(omap_readl(FUNC_MUX_CTRL_A) | 7,
+ FUNC_MUX_CTRL_A);
+ omap_ir->pdata->transceiver_mode(omap_ir->dev, IR_SIRMODE);
+ }
+
+ uart_reg_out(UART3_MDR1, UART3_MDR1_RESET); /* Reset mode */
+
+ /* Clear DLH and DLL */
+ uart_reg_out(UART3_LCR, UART3_LCR_DIVEN);
+
+ uart_reg_out(UART3_DLL, 0);
+ uart_reg_out(UART3_DLH, 0);
+ uart_reg_out(UART3_LCR, 0xbf); /* FIXME: Add #define */
+
+ uart_reg_out(UART3_EFR, UART3_EFR_EN);
+ uart_reg_out(UART3_LCR, UART3_LCR_DIVEN);
+
+ /* Enable access to UART3_TLR and UART3_TCR registers */
+ uart_reg_out(UART3_MCR, UART3_MCR_EN_TCR_TLR);
+
+ uart_reg_out(UART3_SCR, 0);
+ /* Set Rx trigger to 1 and Tx trigger to 1 */
+ uart_reg_out(UART3_TLR, 0);
+
+ /* Set LCR to 8 bits and 1 stop bit */
+ uart_reg_out(UART3_LCR, 0x03);
+
+ /* Clear RX and TX FIFO and enable FIFO */
+ /* Use DMA Req for transfers */
+ uart_reg_out(UART3_FCR, UART3_FCR_CONFIG);
+
+ uart_reg_out(UART3_MCR, 0);
+
+ uart_reg_out(UART3_SCR, UART3_SCR_TX_TRIG1 |
+ UART3_SCR_RX_TRIG1);
+
+ /* Enable UART3 SIR Mode,(Frame-length method to end frames) */
+ uart_reg_out(UART3_MDR1, UART3_MDR1_SIR);
+
+ /* Set Status FIFO trig to 1 */
+ uart_reg_out(UART3_MDR2, 0);
+
+ /* Enables RXIR input */
+ /* and disable TX underrun */
+ /* SEND_SIP pulse */
+ uart_reg_out(UART3_ACREG, UART3_ACERG_SD_MODE_LOW |
+ UART3_ACERG_TX_UNDERRUN_DIS);
+
+ /* Enable EOF Interrupt only */
+ uart_reg_out(UART3_IER, UART3_IER_CTS | UART3_IER_EOF);
+
+ /* Set Maximum Received Frame size to 2048 bytes */
+ uart_reg_out(UART3_RXFLL, 0x00);
+ uart_reg_out(UART3_RXFLH, 0x08);
+
+ uart_reg_in(UART3_RESUME);
+
+ return 0;
+}
+
+static int omap_irda_shutdown(struct omap_irda *omap_ir)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ /* Disable all UART3 Interrupts */
+ uart_reg_out(UART3_IER, 0);
+
+ /* Disable UART3 and disable baud rate generator */
+ uart_reg_out(UART3_MDR1, UART3_MDR1_RESET);
+
+ /* set SD_MODE pin to high and Disable RX IR */
+ uart_reg_out(UART3_ACREG, (UART3_ACERG_DIS_IR_RX |
+ ~(UART3_ACERG_SD_MODE_LOW)));
+
+ /* Clear DLH and DLL */
+ uart_reg_out(UART3_LCR, UART3_LCR_DIVEN);
+ uart_reg_out(UART3_DLL, 0);
+ uart_reg_out(UART3_DLH, 0);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static irqreturn_t
+omap_irda_irq(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct omap_irda *omap_ir = netdev_priv(dev);
+ struct sk_buff *skb;
+
+ u8 status;
+ int w = 0;
+
+ /* Clear EOF interrupt */
+ status = uart_reg_in(UART3_IIR);
+
+ if (status & UART3_IIR_TX_STATUS) {
+ u8 mdr2 = uart_reg_in(UART3_MDR2);
+ if (mdr2 & UART3_MDR2_IRTX_UNDERRUN)
+ printk(KERN_ERR "IrDA Buffer underrun error\n");
+
+ omap_ir->stats.tx_packets++;
+
+ if (omap_ir->newspeed) {
+ omap_irda_set_speed(dev, omap_ir->newspeed);
+ omap_ir->newspeed = 0;
+ }
+
+ netif_wake_queue(dev);
+ if (!(status & UART3_IIR_EOF))
+ return IRQ_HANDLED;
+ }
+
+ /* Stop DMA and if there are no errors, send frame to upper layer */
+ omap_stop_dma(omap_ir->rx_dma_channel);
+
+ status = uart_reg_in(UART3_SFLSR); /* Take a frame status */
+
+ if (status != 0) { /* Bad frame? */
+ omap_ir->stats.rx_frame_errors++;
+ uart_reg_in(UART3_RESUME);
+ } else {
+ /* We got a frame! */
+ skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
+
+ if (!skb) {
+ printk(KERN_ERR "omap_sir: out of memory for RX SKB\n");
+ return IRQ_HANDLED;
+ }
+ /*
+ * Align any IP headers that may be contained
+ * within the frame.
+ */
+
+ skb_reserve(skb, 1);
+
+ w = omap_get_dma_dst_pos(omap_ir->rx_dma_channel) -
+ omap_ir->rx_buf_dma_phys;
+
+ if (!IS_FIR(omap_ir))
+ /* Copy DMA buffer to skb */
+ memcpy(skb_put(skb, w - 2), omap_ir->rx_buf_dma_virt,
+ w - 2);
+ else
+ /* Copy DMA buffer to skb */
+ memcpy(skb_put(skb, w - 4), omap_ir->rx_buf_dma_virt,
+ w - 4);
+
+ skb->dev = dev;
+ skb_reset_mac_header(skb);
+ skb->protocol = htons(ETH_P_IRDA);
+ omap_ir->stats.rx_packets++;
+ omap_ir->stats.rx_bytes += skb->len;
+ netif_receive_skb(skb); /* Send data to upper level */
+ }
+
+ /* Re-init RX DMA */
+ omap_irda_start_rx_dma(omap_ir);
+
+ dev->last_rx = jiffies;
+
+ return IRQ_HANDLED;
+}
+
+static int omap_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct omap_irda *omap_ir = netdev_priv(dev);
+ int speed = irda_get_next_speed(skb);
+ int mtt = irda_get_mtt(skb);
+ int xbofs = irda_get_next_xbofs(skb);
+
+
+ /*
+ * Does this packet contain a request to change the interface
+ * speed? If so, remember it until we complete the transmission
+ * of this frame.
+ */
+ if (speed != omap_ir->speed && speed != -1)
+ omap_ir->newspeed = speed;
+
+ if (xbofs) /* Set number of addtional BOFS */
+ uart_reg_out(UART3_EBLR, xbofs + 1);
+
+ /*
+ * If this is an empty frame, we can bypass a lot.
+ */
+ if (skb->len == 0) {
+ if (omap_ir->newspeed) {
+ omap_ir->newspeed = 0;
+ omap_irda_set_speed(dev, speed);
+ }
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ netif_stop_queue(dev);
+
+ /* Copy skb data to DMA buffer */
+ skb_copy_from_linear_data(skb, omap_ir->tx_buf_dma_virt, skb->len);
+
+ /* Copy skb data to DMA buffer */
+ omap_ir->stats.tx_bytes += skb->len;
+
+ /* Set frame length */
+ uart_reg_out(UART3_TXFLL, (skb->len & 0xff));
+ uart_reg_out(UART3_TXFLH, (skb->len >> 8));
+
+ if (mtt > 1000)
+ mdelay(mtt / 1000);
+ else
+ udelay(mtt);
+
+ /* Start TX DMA transfer */
+ omap_start_tx_dma(omap_ir, skb->len);
+
+ /* We can free skb now because it's already in DMA buffer */
+ dev_kfree_skb(skb);
+
+ dev->trans_start = jiffies;
+
+ return 0;
+}
+
+static int
+omap_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
+{
+ struct if_irda_req *rq = (struct if_irda_req *)ifreq;
+ struct omap_irda *omap_ir = netdev_priv(dev);
+ int ret = -EOPNOTSUPP;
+
+
+ switch (cmd) {
+ case SIOCSBANDWIDTH:
+ if (capable(CAP_NET_ADMIN)) {
+ /*
+ * We are unable to set the speed if the
+ * device is not running.
+ */
+ if (omap_ir->open)
+ ret = omap_irda_set_speed(dev,
+ rq->ifr_baudrate);
+ else {
+ printk(KERN_ERR "omap_ir: SIOCSBANDWIDTH:"
+ " !netif_running\n");
+ ret = 0;
+ }
+ }
+ break;
+
+ case SIOCSMEDIABUSY:
+ ret = -EPERM;
+ if (capable(CAP_NET_ADMIN)) {
+ irda_device_set_media_busy(dev, TRUE);
+ ret = 0;
+ }
+ break;
+
+ case SIOCGRECEIVING:
+ rq->ifr_receiving = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static struct net_device_stats *omap_irda_stats(struct net_device *dev)
+{
+ struct omap_irda *omap_ir = netdev_priv(dev);
+ return &omap_ir->stats;
+}
+
+static int omap_irda_start(struct net_device *dev)
+{
+ struct omap_irda *omap_ir = netdev_priv(dev);
+ int err;
+
+ omap_ir->speed = 9600;
+
+ err = request_irq(dev->irq, omap_irda_irq, 0, dev->name, dev);
+ if (err)
+ goto err_irq;
+
+ /*
+ * The interrupt must remain disabled for now.
+ */
+ disable_irq(dev->irq);
+
+ /* Request DMA channels for IrDA hardware */
+ if (omap_request_dma(omap_ir->pdata->rx_channel, "IrDA Rx DMA",
+ (void *)omap_irda_rx_dma_callback,
+ dev, &(omap_ir->rx_dma_channel))) {
+ printk(KERN_ERR "Failed to request IrDA Rx DMA\n");
+ goto err_irq;
+ }
+
+ if (omap_request_dma(omap_ir->pdata->tx_channel, "IrDA Tx DMA",
+ (void *)omap_irda_tx_dma_callback,
+ dev, &(omap_ir->tx_dma_channel))) {
+ printk(KERN_ERR "Failed to request IrDA Tx DMA\n");
+ goto err_irq;
+ }
+
+ /* Allocate TX and RX buffers for DMA channels */
+ omap_ir->rx_buf_dma_virt =
+ dma_alloc_coherent(NULL, IRDA_SKB_MAX_MTU,
+ &(omap_ir->rx_buf_dma_phys),
+ GFP_KERNEL);
+
+ if (!omap_ir->rx_buf_dma_virt) {
+ printk(KERN_ERR "Unable to allocate memory for rx_buf_dma\n");
+ goto err_irq;
+ }
+
+ omap_ir->tx_buf_dma_virt =
+ dma_alloc_coherent(NULL, IRDA_SIR_MAX_FRAME,
+ &(omap_ir->tx_buf_dma_phys),
+ GFP_KERNEL);
+
+ if (!omap_ir->tx_buf_dma_virt) {
+ printk(KERN_ERR "Unable to allocate memory for tx_buf_dma\n");
+ goto err_mem1;
+ }
+
+ /*
+ * Setup the serial port for the specified config.
+ */
+ if (omap_ir->pdata->select_irda)
+ omap_ir->pdata->select_irda(omap_ir->dev, IR_SEL);
+
+ err = omap_irda_startup(dev);
+
+ if (err)
+ goto err_startup;
+
+ omap_irda_set_speed(dev, omap_ir->speed = 9600);
+
+ /*
+ * Open a new IrLAP layer instance.
+ */
+ omap_ir->irlap = irlap_open(dev, &omap_ir->qos, "omap_sir");
+
+ err = -ENOMEM;
+ if (!omap_ir->irlap)
+ goto err_irlap;
+
+ /* Now enable the interrupt and start the queue */
+ omap_ir->open = 1;
+
+ /* Start RX DMA */
+ omap_irda_start_rx_dma(omap_ir);
+
+ enable_irq(dev->irq);
+ netif_start_queue(dev);
+
+ return 0;
+
+err_irlap:
+ omap_ir->open = 0;
+ omap_irda_shutdown(omap_ir);
+err_startup:
+ dma_free_coherent(NULL, IRDA_SIR_MAX_FRAME,
+ omap_ir->tx_buf_dma_virt, omap_ir->tx_buf_dma_phys);
+err_mem1:
+ dma_free_coherent(NULL, IRDA_SKB_MAX_MTU,
+ omap_ir->rx_buf_dma_virt, omap_ir->rx_buf_dma_phys);
+err_irq:
+ free_irq(dev->irq, dev);
+ return err;
+}
+
+static int omap_irda_stop(struct net_device *dev)
+{
+ struct omap_irda *omap_ir = netdev_priv(dev);
+
+ disable_irq(dev->irq);
+
+ netif_stop_queue(dev);
+
+ omap_free_dma(omap_ir->rx_dma_channel);
+ omap_free_dma(omap_ir->tx_dma_channel);
+
+ if (omap_ir->rx_buf_dma_virt)
+ dma_free_coherent(NULL, IRDA_SKB_MAX_MTU,
+ omap_ir->rx_buf_dma_virt,
+ omap_ir->rx_buf_dma_phys);
+ if (omap_ir->tx_buf_dma_virt)
+ dma_free_coherent(NULL, IRDA_SIR_MAX_FRAME,
+ omap_ir->tx_buf_dma_virt,
+ omap_ir->tx_buf_dma_phys);
+
+ omap_irda_shutdown(omap_ir);
+
+ /* Stop IrLAP */
+ if (omap_ir->irlap) {
+ irlap_close(omap_ir->irlap);
+ omap_ir->irlap = NULL;
+ }
+
+ omap_ir->open = 0;
+
+ /*
+ * Free resources
+ */
+ free_irq(dev->irq, dev);
+
+ return 0;
+}
+
+static int omap_irda_set_speed(struct net_device *dev, int speed)
+{
+ struct omap_irda *omap_ir = netdev_priv(dev);
+ int divisor;
+ unsigned long flags;
+
+ /* Set IrDA speed */
+ if (speed <= 115200) {
+
+ local_irq_save(flags);
+
+ /* SIR mode */
+ if (omap_ir->pdata->transceiver_mode)
+ omap_ir->pdata->transceiver_mode(omap_ir->dev,
+ IR_SIRMODE);
+
+ /* Set SIR mode */
+ uart_reg_out(UART3_MDR1, 1);
+ uart_reg_out(UART3_EBLR, 1);
+
+ divisor = 48000000 / (16 * speed); /* Base clock 48 MHz */
+
+ uart_reg_out(UART3_LCR, UART3_LCR_DIVEN);
+ uart_reg_out(UART3_DLL, (divisor & 0xff));
+ uart_reg_out(UART3_DLH, (divisor >> 8));
+ uart_reg_out(UART3_LCR, 0x03);
+
+ uart_reg_out(UART3_MCR, 0);
+
+ local_irq_restore(flags);
+ } else if (speed <= 1152000) {
+
+ local_irq_save(flags);
+
+ /* Set MIR mode, auto SIP */
+ uart_reg_out(UART3_MDR1, UART3_MDR1_MIR |
+ UART3_MDR1_SIP_AUTO);
+
+ uart_reg_out(UART3_EBLR, 2);
+
+ divisor = 48000000 / (41 * speed); /* Base clock 48 MHz */
+
+ uart_reg_out(UART3_LCR, UART3_LCR_DIVEN);
+ uart_reg_out(UART3_DLL, (divisor & 0xff));
+ uart_reg_out(UART3_DLH, (divisor >> 8));
+ uart_reg_out(UART3_LCR, 0x03);
+
+ if (omap_ir->pdata->transceiver_mode)
+ omap_ir->pdata->transceiver_mode(omap_ir->dev,
+ IR_MIRMODE);
+
+ local_irq_restore(flags);
+ } else {
+ local_irq_save(flags);
+
+ /* FIR mode */
+ uart_reg_out(UART3_MDR1, UART3_MDR1_FIR |
+ UART3_MDR1_SIP_AUTO);
+
+ if (omap_ir->pdata->transceiver_mode)
+ omap_ir->pdata->transceiver_mode(omap_ir->dev,
+ IR_FIRMODE);
+
+ local_irq_restore(flags);
+ }
+
+ omap_ir->speed = speed;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * Suspend the IrDA interface.
+ */
+static int omap_irda_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct omap_irda *omap_ir = netdev_priv(dev);
+
+ if (!dev)
+ return 0;
+
+ if (omap_ir->open) {
+ /*
+ * Stop the transmit queue
+ */
+ netif_device_detach(dev);
+ disable_irq(dev->irq);
+ omap_irda_shutdown(omap_ir);
+ }
+ return 0;
+}
+
+/*
+ * Resume the IrDA interface.
+ */
+static int omap_irda_resume(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct omap_irda *omap_ir= netdev_priv(dev);
+
+ if (!dev)
+ return 0;
+
+ if (omap_ir->open) {
+ /*
+ * If we missed a speed change, initialise at the new speed
+ * directly. It is debatable whether this is actually
+ * required, but in the interests of continuing from where
+ * we left off it is desireable. The converse argument is
+ * that we should re-negotiate at 9600 baud again.
+ */
+ if (omap_ir->newspeed) {
+ omap_ir->speed = omap_ir->newspeed;
+ omap_ir->newspeed = 0;
+ }
+
+ omap_irda_startup(dev);
+ omap_irda_set_speed(dev, omap_ir->speed);
+ enable_irq(dev->irq);
+
+ /*
+ * This automatically wakes up the queue
+ */
+ netif_device_attach(dev);
+ }
+
+ return 0;
+}
+#else
+#define omap_irda_suspend NULL
+#define omap_irda_resume NULL
+#endif
+
+static int omap_irda_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct omap_irda *omap_ir;
+ struct omap_irda_config *pdata = pdev->dev.platform_data;
+ unsigned int baudrate_mask;
+ int err = 0;
+ int irq = NO_IRQ;
+
+ if (!pdata) {
+ printk(KERN_ERR "IrDA Platform data not supplied\n");
+ return -ENOENT;
+ }
+
+ if (!pdata->rx_channel || !pdata->tx_channel) {
+ printk(KERN_ERR "IrDA invalid rx/tx channel value\n");
+ return -ENOENT;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ printk(KERN_WARNING "no irq for IrDA\n");
+ return -ENOENT;
+ }
+
+ dev = alloc_irdadev(sizeof(struct omap_irda));
+ if (!dev)
+ goto err_mem_1;
+
+
+ omap_ir = netdev_priv(dev);
+ omap_ir->dev = &pdev->dev;
+ omap_ir->pdata = pdata;
+
+ dev->hard_start_xmit = omap_irda_hard_xmit;
+ dev->open = omap_irda_start;
+ dev->stop = omap_irda_stop;
+ dev->do_ioctl = omap_irda_ioctl;
+ dev->get_stats = omap_irda_stats;
+ dev->irq = irq;
+
+ irda_init_max_qos_capabilies(&omap_ir->qos);
+
+ baudrate_mask = 0;
+ if (omap_ir->pdata->transceiver_cap & IR_SIRMODE)
+ baudrate_mask |= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+ if (omap_ir->pdata->transceiver_cap & IR_MIRMODE)
+ baudrate_mask |= IR_57600 | IR_1152000;
+ if (omap_ir->pdata->transceiver_cap & IR_FIRMODE)
+ baudrate_mask |= IR_4000000 << 8;
+
+ omap_ir->qos.baud_rate.bits &= baudrate_mask;
+ omap_ir->qos.min_turn_time.bits = 7;
+
+ irda_qos_bits_to_value(&omap_ir->qos);
+
+ /* Any better way to avoid this? No. */
+ if (machine_is_omap_h3() || machine_is_omap_h4())
+ INIT_DELAYED_WORK(&omap_ir->pdata->gpio_expa, NULL);
+
+ err = register_netdev(dev);
+ if (!err)
+ platform_set_drvdata(pdev, dev);
+ else
+ free_netdev(dev);
+
+err_mem_1:
+ return err;
+}
+
+static int omap_irda_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ if (pdev) {
+ unregister_netdev(dev);
+ free_netdev(dev);
+ }
+ return 0;
+}
+
+static struct platform_driver omapir_driver = {
+ .probe = omap_irda_probe,
+ .remove = omap_irda_remove,
+ .suspend = omap_irda_suspend,
+ .resume = omap_irda_resume,
+ .driver = {
+ .name = "omapirda",
+ },
+};
+
+static char __initdata banner[] = KERN_INFO "OMAP IrDA driver initializing\n";
+
+static int __init omap_irda_init(void)
+{
+ printk(banner);
+ return platform_driver_register(&omapir_driver);
+}
+
+static void __exit omap_irda_exit(void)
+{
+ platform_driver_unregister(&omapir_driver);
+}
+
+module_init(omap_irda_init);
+module_exit(omap_irda_exit);
+
+MODULE_AUTHOR("MontaVista");
+MODULE_DESCRIPTION("OMAP IrDA Driver");
+MODULE_LICENSE("GPL");
+
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * drivers/rtc/rtc-twl4030.c
+ *
+ * TWL4030 Real Time Clock interface
+ *
+ * Copyright (C) 2007 MontaVista Software, Inc
+ * Author: Alexandre Rusev <source@mvista.com>
+ *
+ * Based on original TI driver twl4030-rtc.c
+ * Copyright (C) 2006 Texas Instruments, Inc.
+ *
+ * Based on rtc-omap.c
+ * Copyright (C) 2003 MontaVista Software, Inc.
+ * Author: George G. Davis <gdavis@mvista.com> or <source@mvista.com>
+ *
+ * Copyright (C) 2006 David Brownell
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl4030-rtc.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include <asm/mach/time.h>
+#include <asm/system.h>
++#include <mach/hardware.h>
+
+#define ALL_TIME_REGS 6
+
+/*
+ * If this driver ever becomes modularised, it will be really nice
+ * to make the epoch retain its value across module reload...
+ */
+static int epoch = 1900; /* year corresponding to 0x00 */
+
+/*
+ * Supports 1 byte read from TWL4030 RTC register.
+ */
+static int twl4030_rtc_read_u8(u8 *data, u8 reg)
+{
+ int ret;
+
+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_RTC, data, reg);
+ if (ret < 0) {
+ printk(KERN_WARNING "twl4030_rtc: Could not read TWL4030"
+ "register %X - returned %d[%x]\n", reg, ret, ret);
+ }
+ return ret;
+}
+
+/*
+ * Supports 1 byte write to TWL4030 RTC registers.
+ */
+static int twl4030_rtc_write_u8(u8 data, u8 reg)
+{
+ int ret;
+
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_RTC, data, reg);
+ if (ret < 0) {
+ printk(KERN_WARNING "twl4030_rtc: Could not write TWL4030"
+ "register %X - returned %d[%x]\n", reg, ret, ret);
+ }
+ return ret;
+}
+
+/*
+ * Enables timer or alarm interrupts.
+ */
+static int set_rtc_irq_bit(unsigned char bit)
+{
+ unsigned char val;
+ int ret;
+
+ ret = twl4030_rtc_read_u8(&val, REG_RTC_INTERRUPTS_REG);
+ if (ret < 0)
+ goto set_irq_out;
+
+ val |= bit;
+ ret = twl4030_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
+
+set_irq_out:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+/*
+ * Read timer or alarm interrupts register.
+ */
+static int get_rtc_irq_bit(unsigned char *val)
+{
+ int ret;
+
+ ret = twl4030_rtc_read_u8(val, REG_RTC_INTERRUPTS_REG);
+ return ret;
+}
+#endif
+/*
+ * Disables timer or alarm interrupts.
+ */
+static int mask_rtc_irq_bit(unsigned char bit)
+{
+ unsigned char val;
+ int ret;
+
+ ret = twl4030_rtc_read_u8(&val, REG_RTC_INTERRUPTS_REG);
+ if (ret < 0)
+ goto mask_irq_out;
+
+ val &= ~bit;
+ ret = twl4030_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
+
+mask_irq_out:
+ return ret;
+}
+
+static int twl4030_rtc_alarm_irq_set_state(struct device *dev, int enabled)
+{
+ int ret;
+
+ /* Allow ints for RTC ALARM updates. */
+ if (enabled)
+ ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+ else
+ ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+
+ return ret;
+}
+
+/*
+ * Gets current TWL4030 RTC time and date parameters.
+ */
+static int get_rtc_time(struct rtc_time *rtc_tm)
+{
+ unsigned char rtc_data[ALL_TIME_REGS + 1];
+ int ret;
+ u8 save_control;
+
+ ret = twl4030_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
+ if (ret < 0)
+ return ret;
+
+ save_control |= BIT_RTC_CTRL_REG_GET_TIME_M;
+
+ ret = twl4030_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ if (ret < 0)
+ return ret;
+
+ ret = twl4030_i2c_read(TWL4030_MODULE_RTC, rtc_data,
+ REG_SECONDS_REG, ALL_TIME_REGS);
+
+ if (ret < 0) {
+ printk(KERN_ERR "twl4030_rtc: twl4030_i2c_read error.\n");
+ return ret;
+ }
+
+ rtc_tm->tm_sec = BCD2BIN(rtc_data[0]);
+ rtc_tm->tm_min = BCD2BIN(rtc_data[1]);
+ rtc_tm->tm_hour = BCD2BIN(rtc_data[2]);
+ rtc_tm->tm_mday = BCD2BIN(rtc_data[3]);
+ rtc_tm->tm_mon = BCD2BIN(rtc_data[4]);
+ rtc_tm->tm_year = BCD2BIN(rtc_data[5]);
+
+ /*
+ * Account for differences between how the RTC uses the values
+ * and how they are defined in a struct rtc_time;
+ */
+ rtc_tm->tm_year += (epoch - 1900);
+ if (rtc_tm->tm_year <= 69)
+ rtc_tm->tm_year += 100;
+
+ rtc_tm->tm_mon--;
+
+ return ret;
+}
+
+static int twl4030_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char save_control;
+ unsigned char rtc_data[ALL_TIME_REGS + 1];
+ int ret;
+
+ /* Month range is 01..12 */
+ tm->tm_mon++;
+
+ rtc_data[1] = BIN2BCD(tm->tm_sec);
+ rtc_data[2] = BIN2BCD(tm->tm_min);
+ rtc_data[3] = BIN2BCD(tm->tm_hour);
+ rtc_data[4] = BIN2BCD(tm->tm_mday);
+ rtc_data[5] = BIN2BCD(tm->tm_mon);
+ rtc_data[6] = BIN2BCD(tm->tm_year);
+
+ /* Stop RTC while updating the TC registers */
+ ret = twl4030_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
+ if (ret < 0)
+ goto out;
+
+ save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
+ twl4030_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ if (ret < 0)
+ goto out;
+
+ /* update all the alarm registers in one shot */
+ ret = twl4030_i2c_write(TWL4030_MODULE_RTC, rtc_data,
+ REG_SECONDS_REG, ALL_TIME_REGS);
+ if (ret < 0) {
+ printk(KERN_ERR "twl4030: twl4030_i2c_write error.\n");
+ goto out;
+ }
+
+ /* Start back RTC */
+ save_control |= BIT_RTC_CTRL_REG_STOP_RTC_M;
+ ret = twl4030_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+
+out:
+ return ret;
+}
+
+/*
+ * Gets current TWL4030 RTC alarm time.
+ */
+static int get_rtc_alm_time(struct rtc_time *alm_tm)
+{
+ unsigned char rtc_data[ALL_TIME_REGS + 1];
+ int ret;
+
+ ret = twl4030_i2c_read(TWL4030_MODULE_RTC, rtc_data,
+ REG_ALARM_SECONDS_REG, ALL_TIME_REGS);
+ if (ret < 0) {
+ printk(KERN_ERR "twl4030_rtc: twl4030_i2c_read error.\n");
+ return ret;
+ }
+
+ alm_tm->tm_sec = BCD2BIN(rtc_data[0]);
+ alm_tm->tm_min = BCD2BIN(rtc_data[1]);
+ alm_tm->tm_hour = BCD2BIN(rtc_data[2]);
+ alm_tm->tm_mday = BCD2BIN(rtc_data[3]);
+ alm_tm->tm_mon = BCD2BIN(rtc_data[4]);
+ alm_tm->tm_year = BCD2BIN(rtc_data[5]);
+
+ /*
+ * Account for differences between how the RTC uses the values
+ * and how they are defined in a struct rtc_time;
+ */
+ alm_tm->tm_year += (epoch - 1900);
+ if (alm_tm->tm_year <= 69)
+ alm_tm->tm_year += 100;
+
+ alm_tm->tm_mon--;
+
+ return ret;
+}
+
+static int twl4030_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ int ret;
+
+ memset(tm, 0, sizeof(struct rtc_time));
+ ret = get_rtc_time(tm);
+
+ return ret;
+}
+
+/*
+ * Gets current TWL4030 RTC alarm time.
+ */
+static int twl4030_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ int ret;
+ u8 rtc_interrupts_reg = 0;
+
+ /*
+ * This returns a struct rtc_time. Reading >= 0xc0
+ * means "don't care" or "match all". Only the tm_hour,
+ * tm_min, and tm_sec values are filled in.
+ */
+ memset(&alm->time, 0, sizeof(struct rtc_time));
+ ret = get_rtc_alm_time(&alm->time);
+
+ if (ret)
+ goto out;
+
+ /* Check alarm enabled flag state */
+ ret =
+ ret | twl4030_i2c_read_u8(TWL4030_MODULE_RTC, &rtc_interrupts_reg,
+ REG_RTC_INTERRUPTS_REG);
+
+ if (ret)
+ goto out;
+
+ if ((rtc_interrupts_reg & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) != 0)
+ alm->enabled = 1;
+ else
+ alm->enabled = 0;
+
+out:
+ return ret;
+}
+
+static int twl4030_rtc_irq_set_state(struct device *dev, int enabled)
+{
+ int ret;
+
+ /* Allow ints for RTC updates. */
+ if (enabled)
+ ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+ else
+ ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+
+ return ret;
+}
+
+static int twl4030_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned char alarm_data[ALL_TIME_REGS + 1];
+ int ret;
+
+ /* Month range is 01..12 */
+ alm->time.tm_mon++;
+
+ alarm_data[1] = BIN2BCD(alm->time.tm_sec);
+ alarm_data[2] = BIN2BCD(alm->time.tm_min);
+ alarm_data[3] = BIN2BCD(alm->time.tm_hour);
+ alarm_data[4] = BIN2BCD(alm->time.tm_mday);
+ alarm_data[5] = BIN2BCD(alm->time.tm_mon);
+ alarm_data[6] = BIN2BCD(alm->time.tm_year);
+
+ /* update all the alarm registers in one shot */
+ ret = twl4030_i2c_write(TWL4030_MODULE_RTC, alarm_data,
+ REG_ALARM_SECONDS_REG, ALL_TIME_REGS);
+ if (ret) {
+ printk(KERN_ERR "twl4030: twl4030_i2c_write error.\n");
+ goto out;
+ }
+
+ ret = twl4030_rtc_alarm_irq_set_state(dev, alm->enabled);
+out:
+ return ret;
+}
+
+/*
+ * We will just handle setting the frequency and make use the framework for
+ * reading the periodic interupts.
+ * @freq: Current periodic IRQ freq
+ */
+static int twl4030_rtc_irq_set_freq(struct device *dev, int freq)
+{
+ struct rtc_device *rtc = dev_get_drvdata(dev);
+
+ if (freq < 0 || freq > 3)
+ return -EINVAL;
+
+ rtc->irq_freq = freq;
+
+ /* set rtc irq freq to user defined value */
+ set_rtc_irq_bit(freq);
+
+ return 0;
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+
+static int twl4030_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+
+ switch (cmd) {
+ case RTC_AIE_OFF:
+ return twl4030_rtc_alarm_irq_set_state(dev, 0);
+ case RTC_AIE_ON:
+ return twl4030_rtc_alarm_irq_set_state(dev, 1);
+
+ case RTC_UIE_OFF:
+ /* Fall Through */
+ case RTC_PIE_OFF:
+ /* Mask ints from RTC updates. */
+ return twl4030_rtc_irq_set_state(dev, 0);
+ case RTC_UIE_ON:
+ /* Fall Through */
+ case RTC_PIE_ON:
+ /* Allow ints for RTC updates. */
+ return twl4030_rtc_irq_set_state(dev, 1);
+
+ case RTC_EPOCH_READ:
+ return put_user(epoch, (unsigned long *)arg);
+ case RTC_EPOCH_SET:
+ /*
+ * There were no RTC clocks before 1900.
+ */
+ if (arg < 1900)
+ return -EINVAL;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ epoch = arg;
+ return 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#else
+#define omap_rtc_ioctl NULL
+#endif
+
+static irqreturn_t twl4030_rtc_interrupt(int irq, void *rtc)
+{
+ unsigned long events = 0;
+ int ret = IRQ_NONE;
+ int res;
+ u8 rd_reg;
+
+ res = twl4030_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
+ if (res)
+ goto out;
+ /*
+ * Figure out source of interrupt: ALARM or TIMER in RTC_STATUS_REG.
+ * only one (ALARM or RTC) interrupt source may be enabled
+ * at time, we also could check our results
+ * by reading RTS_INTERRUPTS_REGISTER[IT_TIMER,IT_ALARM]
+ */
+ if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M)
+ events |= RTC_IRQF | RTC_AF;
+ else
+ events |= RTC_IRQF | RTC_UF;
+
+ res = twl4030_rtc_write_u8(rd_reg | BIT_RTC_STATUS_REG_ALARM_M,
+ REG_RTC_STATUS_REG);
+ if (res)
+ goto out;
+ /*
+ * Workaround for strange behaviour with T2. Need to write into ISR
+ * register one more time to clear the interrupt. Otherwise, the same
+ * RTC event generates 2 interrupts in a row.
+ * (no errata document available)
+ */
+ res = twl4030_i2c_write_u8(TWL4030_MODULE_INT,
+ PWR_RTC_INT_CLR, REG_PWR_ISR1);
+ if (res)
+ goto out;
+
+ /* Notify RTC core on event */
+ rtc_update_irq(rtc, 1, events);
+
+ ret = IRQ_HANDLED;
+out:
+ return ret;
+}
+
+static struct rtc_class_ops twl4030_rtc_ops = {
+ .ioctl = twl4030_rtc_ioctl,
+ .read_time = twl4030_rtc_read_time,
+ .set_time = twl4030_rtc_set_time,
+ .read_alarm = twl4030_rtc_read_alarm,
+ .set_alarm = twl4030_rtc_set_alarm,
+ .irq_set_freq = twl4030_rtc_irq_set_freq,
+};
+
+static int __devinit twl4030_rtc_probe(struct platform_device *pdev)
+{
+ struct twl4030rtc_platform_data *pdata = pdev->dev.platform_data;
+ struct rtc_device *rtc;
+ int ret = 0;
+ u8 rd_reg;
+
+ if (pdata != NULL && pdata->init != NULL) {
+ ret = pdata->init();
+ if (ret < 0)
+ goto out;
+ }
+
+ rtc = rtc_device_register(pdev->name,
+ &pdev->dev, &twl4030_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
+ PTR_ERR(rtc));
+ goto out0;
+
+ }
+
+ /* Set the irq freq to every second */
+ rtc->irq_freq = 0;
+
+ platform_set_drvdata(pdev, rtc);
+
+ ret = twl4030_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
+
+ if (ret < 0)
+ goto out1;
+
+ if (rd_reg & BIT_RTC_STATUS_REG_POWER_UP_M)
+ dev_warn(&pdev->dev, "Power up reset detected.\n");
+
+ if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M)
+ dev_warn(&pdev->dev, "Pending Alarm interrupt detected.\n");
+
+ /* Clear RTC Power up reset and pending alarm interrupts */
+ ret = twl4030_rtc_write_u8(rd_reg, REG_RTC_STATUS_REG);
+ if (ret < 0)
+ goto out1;
+
+ ret = request_irq(TWL4030_PWRIRQ_RTC, twl4030_rtc_interrupt,
+ 0, rtc->dev.bus_id, rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "IRQ is not free.\n");
+ goto out1;
+ }
+
+ /* Check RTC module status, Enable if it is off */
+ ret = twl4030_rtc_read_u8(&rd_reg, REG_RTC_CTRL_REG);
+ if (ret < 0)
+ goto out2;
+
+ if (!(rd_reg & BIT_RTC_CTRL_REG_STOP_RTC_M)) {
+ dev_info(&pdev->dev, "Enabling TWL4030-RTC.\n");
+ rd_reg = BIT_RTC_CTRL_REG_STOP_RTC_M;
+ ret = twl4030_rtc_write_u8(rd_reg, REG_RTC_CTRL_REG);
+ if (ret < 0)
+ goto out2;
+ }
+
+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_INT, &rd_reg, REG_PWR_IMR1);
+ if (ret < 0)
+ goto out2;
+
+ rd_reg &= PWR_RTC_IT_UNMASK;
+ /* MASK PWR - we will need this */
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_INT, rd_reg, REG_PWR_IMR1);
+ if (ret < 0)
+ goto out2;
+
+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_INT, &rd_reg, REG_PWR_EDR1);
+ if (ret < 0)
+ goto out2;
+
+ /* Rising edge detection enabled, needed for RTC alarm */
+ rd_reg |= 0x80;
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_INT, rd_reg, REG_PWR_EDR1);
+ if (ret < 0)
+ goto out2;
+
+ return ret;
+
+
+out2:
+ free_irq(TWL4030_MODIRQ_PWR, rtc);
+out1:
+ rtc_device_unregister(rtc);
+out0:
+ if (pdata != NULL && pdata->exit != NULL)
+ pdata->exit();
+out:
+ return ret;
+}
+
+/*
+ * Disable all TWL4030 RTC module interrupts.
+ * Sets status flag to free.
+ */
+static int __devexit twl4030_rtc_remove(struct platform_device *pdev)
+{
+ /* leave rtc running, but disable irqs */
+ struct twl4030rtc_platform_data *pdata = pdev->dev.platform_data;
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+
+ mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+ mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+
+ free_irq(TWL4030_MODIRQ_PWR, rtc);
+
+ if (pdata != NULL && pdata->exit != NULL)
+ pdata->exit();
+
+ rtc_device_unregister(rtc);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static void twl4030_rtc_shutdown(struct platform_device *pdev)
+{
+ twl4030_rtc_alarm_irq_set_state(&pdev->dev, 0);
+ twl4030_rtc_irq_set_state(&pdev->dev, 0);
+}
+
+#ifdef CONFIG_PM
+
+static unsigned char irqstat;
+
+static int twl4030_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ get_rtc_irq_bit(&irqstat);
+
+ mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M |
+ BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+ return 0;
+}
+
+static int twl4030_rtc_resume(struct platform_device *pdev)
+{
+ set_rtc_irq_bit(irqstat);
+ return 0;
+}
+#else
+#define twl4030_rtc_suspend NULL
+#define twl4030_rtc_resume NULL
+#endif
+
+MODULE_ALIAS("platform:twl4030_rtc");
+static struct platform_driver twl4030rtc_driver = {
+ .probe = twl4030_rtc_probe,
+ .remove = __devexit_p(twl4030_rtc_remove),
+ .shutdown = twl4030_rtc_shutdown,
+ .suspend = twl4030_rtc_suspend,
+ .resume = twl4030_rtc_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "twl4030_rtc",
+ },
+};
+
+static int __init twl4030_rtc_init(void)
+{
+ return platform_driver_register(&twl4030rtc_driver);
+}
+
+static void __exit twl4030_rtc_exit(void)
+{
+ platform_driver_unregister(&twl4030rtc_driver);
+}
+
+MODULE_ALIAS("platform:twl4030_rtc");
+MODULE_AUTHOR("Texas Instruments, MontaVista Software");
+MODULE_LICENSE("GPL");;
+
+module_init(twl4030_rtc_init);
+module_exit(twl4030_rtc_exit);
--- /dev/null
- #include <asm/arch/gpio.h>
+/*
+ * TSC2301 driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ *
+ * 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/device.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc2301.h>
+
+#ifdef CONFIG_ARCH_OMAP
++#include <mach/gpio.h>
+#endif
+
+u16 tsc2301_read_reg(struct tsc2301 *tsc, int reg)
+{
+ struct spi_transfer t[2];
+ struct spi_message m;
+ u16 data = 0, cmd;
+
+ cmd = reg;
+ cmd |= 0x8000;
+
+ memset(t, 0, sizeof(t));
+ spi_message_init(&m);
+ m.spi = tsc->spi;
+
+ t[0].tx_buf = &cmd;
+ t[0].rx_buf = NULL;
+ t[0].len = 2;
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].tx_buf = NULL;
+ t[1].rx_buf = &data;
+ t[1].len = 2;
+ spi_message_add_tail(&t[1], &m);
+
+ spi_sync(m.spi, &m);
+
+ return data;
+}
+
+void tsc2301_write_reg(struct tsc2301 *tsc, int reg, u16 val)
+{
+ struct spi_transfer t;
+ struct spi_message m;
+ u16 data[2];
+
+ /* Now we prepare the command for transferring */
+ data[0] = reg;
+ data[1] = val;
+
+ spi_message_init(&m);
+ m.spi = tsc->spi;
+
+ memset(&t, 0, sizeof(t));
+ t.tx_buf = data;
+ t.rx_buf = NULL;
+ t.len = 4;
+ spi_message_add_tail(&t, &m);
+
+ spi_sync(m.spi, &m);
+}
+
+void tsc2301_write_kbc(struct tsc2301 *tsc, int val)
+{
+ u16 w;
+
+ w = tsc->config2_shadow;
+ w &= ~(0x03 << 14);
+ w |= (val & 0x03) << 14;
+ tsc2301_write_reg(tsc, TSC2301_REG_CONFIG2, w);
+ tsc->config2_shadow = w;
+}
+
+void tsc2301_write_pll(struct tsc2301 *tsc,
+ int pll_n, int pll_a, int pll_pdc, int pct_e, int pll_o)
+{
+ u16 w;
+
+ w = tsc->config2_shadow;
+ w &= ~0x3fff;
+ w |= (pll_n & 0x0f) | ((pll_a & 0x0f) << 4) | ((pll_pdc & 0x0f) << 8);
+ w |= pct_e ? (1 << 12) : 0;
+ w |= pll_o ? (1 << 13) : 0;
+ tsc2301_write_reg(tsc, TSC2301_REG_CONFIG2, w);
+ tsc->config2_shadow = w;
+}
+
+void tsc2301_read_buf(struct tsc2301 *tsc, int reg, u16 *rx_buf, int len)
+{
+ struct spi_transfer t[2];
+ struct spi_message m;
+ u16 cmd, i;
+
+ cmd = reg;
+ cmd |= 0x8000;
+
+ spi_message_init(&m);
+ m.spi = tsc->spi;
+
+ memset(t, 0, sizeof(t));
+ t[0].tx_buf = &cmd;
+ t[0].rx_buf = NULL;
+ t[0].len = 2;
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].tx_buf = NULL;
+ t[1].rx_buf = rx_buf;
+ t[1].len = 2 * len;
+ spi_message_add_tail(&t[1], &m);
+
+ spi_sync(m.spi, &m);
+
+ for (i = 0; i < len; i++)
+ printk(KERN_DEBUG "rx_buf[%d]: %04x\n", i, rx_buf[i]);
+}
+
+static int __devinit tsc2301_probe(struct spi_device *spi)
+{
+ struct tsc2301 *tsc;
+ struct tsc2301_platform_data *pdata = spi->dev.platform_data;
+ int r;
+ u16 w;
+
+ if (!pdata) {
+ dev_dbg(&spi->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
+ if (tsc == NULL)
+ return -ENOMEM;
+
+ dev_set_drvdata(&spi->dev, tsc);
+ tsc->spi = spi;
+
+ tsc->enable_clock = pdata->enable_clock;
+ tsc->disable_clock = pdata->disable_clock;
+
+ if (pdata->reset_gpio >= 0) {
+ tsc->reset_gpio = pdata->reset_gpio;
+#ifdef CONFIG_ARCH_OMAP
+ r = omap_request_gpio(tsc->reset_gpio);
+ if (r < 0)
+ goto err1;
+ omap_set_gpio_dataout(tsc->reset_gpio, 1);
+ omap_set_gpio_direction(tsc->reset_gpio, 0);
+ mdelay(1);
+ omap_set_gpio_dataout(tsc->reset_gpio, 0);
+#endif
+ } else
+ tsc->reset_gpio = -1;
+
+ spi->mode = SPI_MODE_1;
+ spi->bits_per_word = 16;
+ /* The max speed might've been defined by the board-specific
+ * struct */
+ if (!spi->max_speed_hz)
+ spi->max_speed_hz = TSC2301_HZ;
+ spi_setup(spi);
+
+ /* Soft reset */
+ tsc2301_write_reg(tsc, TSC2301_REG_RESET, 0xbb00);
+ msleep(1);
+
+ w = tsc2301_read_reg(tsc, TSC2301_REG_ADC);
+ if (!(w & (1 << 14))) {
+ dev_err(&spi->dev, "invalid ADC reg value: %04x\n", w);
+ r = -ENODEV;
+ goto err1;
+ }
+
+ w = tsc2301_read_reg(tsc, TSC2301_REG_DAC);
+ if (!(w & (1 << 15))) {
+ dev_err(&spi->dev, "invalid DAC reg value: %04x\n", w);
+ r = -ENODEV;
+ goto err1;
+ }
+
+ /* Stop keypad scanning */
+ tsc2301_write_reg(tsc, TSC2301_REG_KEY, 0x4000);
+
+ /* We have to cache this for read-modify-write, since we can't
+ * read back BIT15 */
+ w = tsc2301_read_reg(tsc, TSC2301_REG_CONFIG2);
+ /* By default BIT15 is set */
+ w |= 1 << 15;
+ tsc->config2_shadow = w;
+
+ r = tsc2301_kp_init(tsc, pdata);
+ if (r)
+ goto err1;
+ r = tsc2301_ts_init(tsc, pdata);
+ if (r)
+ goto err2;
+ r = tsc2301_mixer_init(tsc, pdata);
+ if (r)
+ goto err3;
+ return 0;
+
+err3:
+ tsc2301_ts_exit(tsc);
+err2:
+ tsc2301_kp_exit(tsc);
+err1:
+ kfree(tsc);
+ return r;
+}
+
+static int __devexit tsc2301_remove(struct spi_device *spi)
+{
+ struct tsc2301 *tsc = dev_get_drvdata(&spi->dev);
+
+ tsc2301_mixer_exit(tsc);
+ tsc2301_ts_exit(tsc);
+ tsc2301_kp_exit(tsc);
+ kfree(tsc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tsc2301_suspend(struct spi_device *spi, pm_message_t mesg)
+{
+ struct tsc2301 *tsc = dev_get_drvdata(&spi->dev);
+ int r;
+
+ if ((r = tsc2301_mixer_suspend(tsc)) < 0)
+ return r;
+ if ((r = tsc2301_kp_suspend(tsc)) < 0)
+ goto err1;
+ if ((r = tsc2301_ts_suspend(tsc)) < 0)
+ goto err2;
+
+ return 0;
+err2:
+ tsc2301_kp_resume(tsc);
+err1:
+ tsc2301_mixer_resume(tsc);
+ return r;
+}
+
+static int tsc2301_resume(struct spi_device *spi)
+{
+ struct tsc2301 *tsc = dev_get_drvdata(&spi->dev);
+
+ tsc2301_ts_resume(tsc);
+ tsc2301_kp_resume(tsc);
+ tsc2301_mixer_resume(tsc);
+ return 0;
+}
+#endif
+
+static struct spi_driver tsc2301_driver = {
+ .driver = {
+ .name = "tsc2301",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+#ifdef CONFIG_PM
+ .suspend = tsc2301_suspend,
+ .resume = tsc2301_resume,
+#endif
+ .probe = tsc2301_probe,
+ .remove = __devexit_p(tsc2301_remove),
+};
+
+static int __init tsc2301_init(void)
+{
+ printk("TSC2301 driver initializing\n");
+
+ return spi_register_driver(&tsc2301_driver);
+}
+module_init(tsc2301_init);
+
+static void __exit tsc2301_exit(void)
+{
+ spi_unregister_driver(&tsc2301_driver);
+}
+module_exit(tsc2301_exit);
+
+MODULE_AUTHOR("Juha Yrjölä <juha.yrjola@nokia.com>");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/gpio.h>
+/*
+ * ehci-omap.c - driver for USBHOST on OMAP 34xx processor
+ *
+ * Bus Glue for OMAP34xx USBHOST 3 port EHCI controller
+ * Tested on OMAP3430 ES2.0 SDP
+ *
+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
+ * Author: Vikram Pandita <vikram.pandita@ti.com>
+ *
+ * Based on "ehci-fsl.c" and "ehci-au1xxx.c" ehci glue layers
+ *
+ * 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/platform_device.h>
+#include <linux/clk.h>
++#include <mach/gpio.h>
+
+#include "ehci-omap.h"
+
+
+#ifdef CONFIG_OMAP_EHCI_PHY_MODE
+/* EHCI connected to External PHY */
+
+/* External USB connectivity board: 750-2083-001
+ * Connected to OMAP3430 SDP
+ * The board has Port1 and Port2 connected to ISP1504 in 12-pin ULPI mode
+ */
+
+/* ISSUE1:
+ * ISP1504 for input clocking mode needs special reset handling
+ * Hold the PHY in reset by asserting RESET_N signal
+ * Then start the 60Mhz clock input to PHY
+ * Release the reset after a delay -
+ * to get the PHY state machine in working state
+ */
+#define EXTERNAL_PHY_RESET
+#define EXT_PHY_RESET_GPIO_PORT1 (57)
+#define EXT_PHY_RESET_GPIO_PORT2 (61)
+#define EXT_PHY_RESET_DELAY (10)
+
+/* ISSUE2:
+ * USBHOST supports External charge pump PHYs only
+ * Use the VBUS from Port1 to power VBUS of Port2 externally
+ * So use Port2 as the working ULPI port
+ */
+#define VBUS_INTERNAL_CHARGEPUMP_HACK
+
+#endif /* CONFIG_OMAP_EHCI_PHY_MODE */
+
+/*-------------------------------------------------------------------------*/
+
+/* Define USBHOST clocks for clock management */
+struct ehci_omap_clock_defs {
+ struct clk *usbhost_ick_clk;
+ struct clk *usbhost2_120m_fck_clk;
+ struct clk *usbhost1_48m_fck_clk;
+ struct clk *usbtll_fck_clk;
+ struct clk *usbtll_ick_clk;
+};
+
+/* Clock names as per clock framework: May change so keep as #defs */
+#define USBHOST_ICKL "usbhost_ick"
+#define USBHOST_120M_FCLK "usbhost_120m_fck"
+#define USBHOST_48M_FCLK "usbhost_48m_fck"
+#define USBHOST_TLL_ICKL "usbtll_ick"
+#define USBHOST_TLL_FCLK "usbtll_fck"
+/*-------------------------------------------------------------------------*/
+
+
+#ifndef CONFIG_OMAP_EHCI_PHY_MODE
+
+static void omap_usb_utmi_init(struct usb_hcd *hcd, u8 tll_channel_mask)
+{
+ int i;
+
+ /* Use UTMI Ports of TLL */
+ omap_writel((1 << OMAP_UHH_HOSTCONFIG_ULPI_BYPASS_SHIFT)|
+ (1<<OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN_SHIFT)|
+ (1<<OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN_SHIFT)|
+ (1<<OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN_SHIFT)|
+ (0<<OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN_SHIFT),
+ OMAP_UHH_HOSTCONFIG);
+ /* Enusre bit is set */
+ while (!(omap_readl(OMAP_UHH_HOSTCONFIG) &
+ (1 << OMAP_UHH_HOSTCONFIG_ULPI_BYPASS_SHIFT)));
+
+ dev_dbg(hcd->self.controller, "\nEntered UTMI MODE: success\n");
+
+ /* Program the 3 TLL channels upfront */
+
+ for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) {
+
+ /* Disable AutoIdle */
+ omap_writel(omap_readl(OMAP_TLL_CHANNEL_CONF(i)) &
+ ~(1<<OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE_SHIFT),
+ OMAP_TLL_CHANNEL_CONF(i));
+ /* Disable BitStuffing */
+ omap_writel(omap_readl(OMAP_TLL_CHANNEL_CONF(i)) &
+ ~(1<<OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF_SHIFT),
+ OMAP_TLL_CHANNEL_CONF(i));
+ /* SDR Mode */
+ omap_writel(omap_readl(OMAP_TLL_CHANNEL_CONF(i)) &
+ ~(1<<OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE_SHIFT),
+ OMAP_TLL_CHANNEL_CONF(i));
+
+ }
+
+ /* Program Common TLL register */
+ omap_writel((1 << OMAP_TLL_SHARED_CONF_FCLK_IS_ON_SHIFT) |
+ (1 << OMAP_TLL_SHARED_CONF_USB_DIVRATION_SHIFT) |
+ (0 << OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN_SHIFT) |
+ (0 << OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN_SHFT),
+ OMAP_TLL_SHARED_CONF);
+
+ /* Enable channels now */
+ for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) {
+
+ /* Enable only the channel that is needed */
+ if (!(tll_channel_mask & 1<<i))
+ continue;
+
+ omap_writel(omap_readl(OMAP_TLL_CHANNEL_CONF(i)) |
+ (1<<OMAP_TLL_CHANNEL_CONF_CHANEN_SHIFT),
+ OMAP_TLL_CHANNEL_CONF(i));
+
+ omap_writeb(0xBE, OMAP_TLL_ULPI_SCRATCH_REGISTER(i));
+ dev_dbg(hcd->self.controller, "\nULPI_SCRATCH_REG[ch=%d]"
+ "= 0x%02x\n",
+ i+1, omap_readb(OMAP_TLL_ULPI_SCRATCH_REGISTER(i)));
+ }
+}
+
+#else
+# define omap_usb_utmi_init(x, y) 0
+#endif
+
+
+/* omap_start_ehc
+ * - Start the TI USBHOST controller
+ */
+static int omap_start_ehc(struct platform_device *dev, struct usb_hcd *hcd)
+{
+ struct ehci_omap_clock_defs *ehci_clocks;
+
+ dev_dbg(hcd->self.controller, ": starting TI EHCI USB Controller\n");
+
+ ehci_clocks = (struct ehci_omap_clock_defs *)(
+ ((char *)hcd_to_ehci(hcd)) +
+ sizeof(struct ehci_hcd));
+
+ /* Start DPLL5 Programming:
+ * Clock Framework is not doing this now:
+ * This will be done in clock framework later
+ */
+ /* Enable DPLL 5 : Based on Input of 13Mhz*/
+ cm_write_mod_reg((12 << OMAP3430ES2_PERIPH2_DPLL_DIV_SHIFT)|
+ (120 << OMAP3430ES2_PERIPH2_DPLL_MULT_SHIFT),
+ PLL_MOD, OMAP3430ES2_CM_CLKSEL4);
+
+ cm_write_mod_reg(1 << OMAP3430ES2_DIV_120M_SHIFT,
+ PLL_MOD, OMAP3430ES2_CM_CLKSEL5);
+
+ cm_write_mod_reg((7 << OMAP3430ES2_PERIPH2_DPLL_FREQSEL_SHIFT) |
+ (7 << OMAP3430ES2_EN_PERIPH2_DPLL_SHIFT),
+ PLL_MOD, OMAP3430ES2_CM_CLKEN2);
+
+ while (!(cm_read_mod_reg(PLL_MOD, CM_IDLEST2) &
+ OMAP3430ES2_ST_PERIPH2_CLK_MASK))
+ dev_dbg(hcd->self.controller,
+ "idlest2 = 0x%x\n",
+ cm_read_mod_reg(PLL_MOD, CM_IDLEST2));
+ /* End DPLL5 programming */
+
+
+ /* PRCM settings for USBHOST:
+ * Interface clk un-related to domain transition
+ */
+ cm_write_mod_reg(0 << OMAP3430ES2_AUTO_USBHOST_SHIFT,
+ OMAP3430ES2_USBHOST_MOD, CM_AUTOIDLE);
+
+ /* Disable sleep dependency with MPU and IVA */
+ cm_write_mod_reg((0 << OMAP3430ES2_EN_MPU_SHIFT) |
+ (0 << OMAP3430ES2_EN_IVA2_SHIFT),
+ OMAP3430ES2_USBHOST_MOD, OMAP3430_CM_SLEEPDEP);
+
+ /* Disable Automatic transition of clock */
+ cm_write_mod_reg(0 << OMAP3430ES2_CLKTRCTRL_USBHOST_SHIFT,
+ OMAP3430ES2_USBHOST_MOD, CM_CLKSTCTRL);
+
+ /* Enable Clocks for USBHOST */
+ ehci_clocks->usbhost_ick_clk = clk_get(&dev->dev,
+ USBHOST_ICKL);
+ if (IS_ERR(ehci_clocks->usbhost_ick_clk))
+ return PTR_ERR(ehci_clocks->usbhost_ick_clk);
+ clk_enable(ehci_clocks->usbhost_ick_clk);
+
+
+ ehci_clocks->usbhost2_120m_fck_clk = clk_get(&dev->dev,
+ USBHOST_120M_FCLK);
+ if (IS_ERR(ehci_clocks->usbhost2_120m_fck_clk))
+ return PTR_ERR(ehci_clocks->usbhost2_120m_fck_clk);
+ clk_enable(ehci_clocks->usbhost2_120m_fck_clk);
+
+ ehci_clocks->usbhost1_48m_fck_clk = clk_get(&dev->dev,
+ USBHOST_48M_FCLK);
+ if (IS_ERR(ehci_clocks->usbhost1_48m_fck_clk))
+ return PTR_ERR(ehci_clocks->usbhost1_48m_fck_clk);
+ clk_enable(ehci_clocks->usbhost1_48m_fck_clk);
+
+
+#ifdef EXTERNAL_PHY_RESET
+ /* Refer: ISSUE1 */
+ omap_request_gpio(EXT_PHY_RESET_GPIO_PORT1);
+ omap_set_gpio_direction(EXT_PHY_RESET_GPIO_PORT1, 0);
+ omap_request_gpio(EXT_PHY_RESET_GPIO_PORT2);
+ omap_set_gpio_direction(EXT_PHY_RESET_GPIO_PORT2, 0);
+ omap_set_gpio_dataout(EXT_PHY_RESET_GPIO_PORT1, 0);
+ omap_set_gpio_dataout(EXT_PHY_RESET_GPIO_PORT2, 0);
+ /* Hold the PHY in RESET for enough time till DIR is high */
+ udelay(EXT_PHY_RESET_DELAY);
+#endif
+
+ /* Configure TLL for 60Mhz clk for ULPI */
+ ehci_clocks->usbtll_fck_clk = clk_get(&dev->dev, USBHOST_TLL_FCLK);
+ if (IS_ERR(ehci_clocks->usbtll_fck_clk))
+ return PTR_ERR(ehci_clocks->usbtll_fck_clk);
+ clk_enable(ehci_clocks->usbtll_fck_clk);
+
+ ehci_clocks->usbtll_ick_clk = clk_get(&dev->dev, USBHOST_TLL_ICKL);
+ if (IS_ERR(ehci_clocks->usbtll_ick_clk))
+ return PTR_ERR(ehci_clocks->usbtll_ick_clk);
+ clk_enable(ehci_clocks->usbtll_ick_clk);
+
+ /* Disable Auto Idle of USBTLL */
+ cm_write_mod_reg((0 << OMAP3430ES2_AUTO_USBTLL_SHIFT),
+ CORE_MOD, CM_AUTOIDLE3);
+
+ /* Wait for TLL to be Active */
+ while ((cm_read_mod_reg(CORE_MOD, OMAP2430_CM_IDLEST3) &
+ (1 << OMAP3430ES2_ST_USBTLL_SHIFT)));
+
+ /* perform TLL soft reset, and wait until reset is complete */
+ omap_writel(1 << OMAP_USBTLL_SYSCONFIG_SOFTRESET_SHIFT,
+ OMAP_USBTLL_SYSCONFIG);
+ /* Wait for TLL reset to complete */
+ while (!(omap_readl(OMAP_USBTLL_SYSSTATUS) &
+ (1 << OMAP_USBTLL_SYSSTATUS_RESETDONE_SHIFT)));
+
+ dev_dbg(hcd->self.controller, "\n TLL RESET DONE\n");
+
+ /* (1<<3) = no idle mode only for initial debugging */
+ omap_writel((1 << OMAP_USBTLL_SYSCONFIG_ENAWAKEUP_SHIFT) |
+ (1 << OMAP_USBTLL_SYSCONFIG_SIDLEMODE_SHIFT) |
+ (1 << OMAP_USBTLL_SYSCONFIG_CACTIVITY_SHIFT),
+ OMAP_USBTLL_SYSCONFIG);
+
+
+ /* Put UHH in NoIdle/NoStandby mode */
+ omap_writel((0 << OMAP_UHH_SYSCONFIG_AUTOIDLE_SHIFT) |
+ (1 << OMAP_UHH_SYSCONFIG_ENAWAKEUP_SHIFT) |
+ (1 << OMAP_UHH_SYSCONFIG_SIDLEMODE_SHIFT) |
+ (1 << OMAP_UHH_SYSCONFIG_CACTIVITY_SHIFT) |
+ (1 << OMAP_UHH_SYSCONFIG_MIDLEMODE_SHIFT),
+ OMAP_UHH_SYSCONFIG);
+
+#ifdef CONFIG_OMAP_EHCI_PHY_MODE
+ /* Bypass the TLL module for PHY mode operation */
+ omap_writel((0 << OMAP_UHH_HOSTCONFIG_ULPI_BYPASS_SHIFT)|
+ (1<<OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN_SHIFT)|
+ (1<<OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN_SHIFT)|
+ (1<<OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN_SHIFT)|
+ (0<<OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN_SHIFT),
+ OMAP_UHH_HOSTCONFIG);
+ /* Ensure that BYPASS is set */
+ while (omap_readl(OMAP_UHH_HOSTCONFIG) &
+ (1 << OMAP_UHH_HOSTCONFIG_ULPI_BYPASS_SHIFT));
+
+ dev_dbg(hcd->self.controller, "Entered ULPI PHY MODE: success");
+
+#else
+ /* Enable UTMI mode for all 3 TLL channels */
+ omap_usb_utmi_init(hcd,
+ OMAP_TLL_CHANNEL_1_EN_MASK |
+ OMAP_TLL_CHANNEL_2_EN_MASK |
+ OMAP_TLL_CHANNEL_3_EN_MASK
+ );
+#endif
+
+#ifdef EXTERNAL_PHY_RESET
+ /* Refer ISSUE1:
+ * Hold the PHY in RESET for enough time till PHY is settled and ready
+ */
+ udelay(EXT_PHY_RESET_DELAY);
+ omap_set_gpio_dataout(EXT_PHY_RESET_GPIO_PORT1, 1);
+ omap_set_gpio_dataout(EXT_PHY_RESET_GPIO_PORT2, 1);
+#endif
+
+#ifdef VBUS_INTERNAL_CHARGEPUMP_HACK
+ /* Refer ISSUE2: LINK assumes external charge pump */
+
+ /* use Port1 VBUS to charge externally Port2:
+ * So for PHY mode operation use Port2 only
+ */
+ omap_writel((0xA << EHCI_INSNREG05_ULPI_REGADD_SHIFT) |/* OTG ctrl reg*/
+ (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) |/* Write */
+ (1 << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) |/* Port1 */
+ (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT) |/* Start */
+ (0x26),
+ EHCI_INSNREG05_ULPI);
+
+ while (!(omap_readl(EHCI_INSNREG05_ULPI) &
+ (1<<EHCI_INSNREG05_ULPI_CONTROL_SHIFT)));
+
+#endif
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void omap_stop_ehc(struct platform_device *dev, struct usb_hcd *hcd)
+{
+ struct ehci_omap_clock_defs *ehci_clocks;
+
+ ehci_clocks = (struct ehci_omap_clock_defs *)
+ (((char *)hcd_to_ehci(hcd)) + sizeof(struct ehci_hcd));
+
+ dev_dbg(hcd->self.controller, ": stopping TI EHCI USB Controller\n");
+
+ /* Reset OMAP modules for insmod/rmmod to work */
+ omap_writel((1<<1), OMAP_UHH_SYSCONFIG);
+ while (!(omap_readl(OMAP_UHH_SYSSTATUS) & (1<<0)));
+ while (!(omap_readl(OMAP_UHH_SYSSTATUS) & (1<<1)));
+ while (!(omap_readl(OMAP_UHH_SYSSTATUS) & (1<<2)));
+ dev_dbg(hcd->self.controller,
+ "UHH RESET DONE OMAP_UHH_SYSSTATUS %x !!\n",
+ omap_readl(OMAP_UHH_SYSSTATUS));
+
+ omap_writel((1<<1), OMAP_USBTLL_SYSCONFIG);
+ while (!(omap_readl(OMAP_USBTLL_SYSSTATUS) & (1<<0)));
+ dev_dbg(hcd->self.controller, ":TLL RESEET DONE");
+
+ if (ehci_clocks->usbtll_fck_clk != NULL) {
+ clk_disable(ehci_clocks->usbtll_fck_clk);
+ clk_put(ehci_clocks->usbtll_fck_clk);
+ ehci_clocks->usbtll_fck_clk = NULL;
+ }
+
+ if (ehci_clocks->usbhost_ick_clk != NULL) {
+ clk_disable(ehci_clocks->usbhost_ick_clk);
+ clk_put(ehci_clocks->usbhost_ick_clk);
+ ehci_clocks->usbhost_ick_clk = NULL;
+ }
+
+ if (ehci_clocks->usbhost1_48m_fck_clk != NULL) {
+ clk_disable(ehci_clocks->usbhost1_48m_fck_clk);
+ clk_put(ehci_clocks->usbhost1_48m_fck_clk);
+ ehci_clocks->usbhost1_48m_fck_clk = NULL;
+ }
+
+ if (ehci_clocks->usbhost2_120m_fck_clk != NULL) {
+ clk_disable(ehci_clocks->usbhost2_120m_fck_clk);
+ clk_put(ehci_clocks->usbhost2_120m_fck_clk);
+ ehci_clocks->usbhost2_120m_fck_clk = NULL;
+ }
+
+ if (ehci_clocks->usbtll_ick_clk != NULL) {
+ clk_disable(ehci_clocks->usbtll_ick_clk);
+ clk_put(ehci_clocks->usbtll_ick_clk);
+ ehci_clocks->usbtll_ick_clk = NULL;
+ }
+
+
+#ifdef EXTERNAL_PHY_RESET
+ omap_free_gpio(EXT_PHY_RESET_GPIO_PORT1);
+ omap_free_gpio(EXT_PHY_RESET_GPIO_PORT2);
+#endif
+
+ dev_dbg(hcd->self.controller,
+ ": Clock to USB host has been disabled\n");
+}
+
+static const struct hc_driver ehci_omap_hc_driver;
+
+/*-------------------------------------------------------------------------*/
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * ehci_hcd_omap_drv_probe - initialize TI-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+static int ehci_hcd_omap_drv_probe(struct platform_device *dev)
+{
+ int retval = 0;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+
+ dev_dbg(&dev->dev, "ehci_hcd_omap_drv_probe()");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ if (dev->resource[1].flags != IORESOURCE_IRQ) {
+ dev_dbg(&dev->dev, "resource[1] is not IORESOURCE_IRQ");
+ retval = -ENOMEM;
+ }
+
+ hcd = usb_create_hcd(&ehci_omap_hc_driver, &dev->dev, dev->dev.bus_id);
+ if (!hcd)
+ return -ENOMEM;
+
+ retval = omap_start_ehc(dev, hcd);
+ if (retval)
+ return retval;
+
+ hcd->rsrc_start = 0;
+ hcd->rsrc_len = 0;
+ hcd->rsrc_start = dev->resource[0].start;
+ hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
+
+ hcd->regs = (void __iomem *) (int) IO_ADDRESS(hcd->rsrc_start);
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->caps = hcd->regs;
+
+ ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = readl(&ehci->caps->hcs_params);
+
+ /* SET 1 micro-frame Interrupt interval */
+ writel(readl(&ehci->regs->command) | (1<<16), &ehci->regs->command);
+
+ retval = usb_add_hcd(hcd, dev->resource[1].start,
+ IRQF_DISABLED | IRQF_SHARED);
+ if (retval == 0)
+ return retval;
+
+ dev_dbg(hcd->self.controller, "ERR: add_hcd");
+ omap_stop_ehc(dev, hcd);
+
+ usb_put_hcd(hcd);
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * ehci_hcd_omap_drv_remove - shutdown processing for EHCI HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_ehci_hcd_omap_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+static int ehci_hcd_omap_drv_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+ dev_dbg(&dev->dev, "ehci_hcd_omap_drv_remove()");
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+ omap_stop_ehc(dev, hcd);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_PM
+static int omap_ehci_bus_suspend(struct usb_hcd *hcd)
+{
+ return ehci_bus_suspend(hcd);
+}
+
+static int omap_ehci_bus_resume(struct usb_hcd *hcd)
+{
+ return ehci_bus_resume(hcd);
+}
+#endif
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ehci_omap_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "OMAP-EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd)
+ + sizeof(struct ehci_omap_clock_defs),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_init,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = omap_ehci_bus_suspend,
+ .bus_resume = omap_ehci_bus_resume,
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+MODULE_ALIAS("omap-ehci");
+static struct platform_driver ehci_hcd_omap_driver = {
+ .probe = ehci_hcd_omap_drv_probe,
+ .remove = ehci_hcd_omap_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ /*.suspend = ehci_hcd_omap_drv_suspend, */
+ /*.resume = ehci_hcd_omap_drv_resume, */
+ .driver = {
+ .name = "ehci-omap",
+ .bus = &platform_bus_type
+ }
+};
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * ehci-omap.h - register definitions for USBHOST in OMAP 34xx
+ *
+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
+ * Author: Vikram Pandita <vikram.pandita@ti.com>
+ *
+ * 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
+ *
+ */
+
+#ifndef __EHCI_OMAP_H
+#define __EHCI_OMAP_H
+
++#include <mach/hardware.h>
+#include "../../../arch/arm/mach-omap2/cm.h"
+#include "../../../arch/arm/mach-omap2/cm-regbits-34xx.h"
+
+/*
+ * OMAP USBHOST Register addresses: PHYSICAL ADDRESSES
+ * Use omap_readl()/omap_writel() functions
+ */
+
+/* USBHOST: TLL, UUH, OHCI, EHCI */
+#define OMAP_USBHOST_BASE (L4_34XX_BASE + 0x60000)
+
+/* TLL Register Set */
+#define OMAP_USBHOST_TLL_BASE (OMAP_USBHOST_BASE + 0x2000)
+#define OMAP_USBTLL_REVISION (OMAP_USBHOST_TLL_BASE + 0x00)
+#define OMAP_USBTLL_SYSCONFIG (OMAP_USBHOST_TLL_BASE + 0x10)
+ #define OMAP_USBTLL_SYSCONFIG_CACTIVITY_SHIFT 8
+ #define OMAP_USBTLL_SYSCONFIG_SIDLEMODE_SHIFT 3
+ #define OMAP_USBTLL_SYSCONFIG_ENAWAKEUP_SHIFT 2
+ #define OMAP_USBTLL_SYSCONFIG_SOFTRESET_SHIFT 1
+ #define OMAP_USBTLL_SYSCONFIG_AUTOIDLE_SHIFT 0
+#define OMAP_USBTLL_SYSSTATUS (OMAP_USBHOST_TLL_BASE + 0x14)
+ #define OMAP_USBTLL_SYSSTATUS_RESETDONE_SHIFT 0
+#define OMAP_USBTLL_IRQSTATUS (OMAP_USBHOST_TLL_BASE + 0x18)
+#define OMAP_USBTLL_IRQENABLE (OMAP_USBHOST_TLL_BASE + 0x1C)
+
+#define OMAP_TLL_SHARED_CONF (OMAP_USBHOST_TLL_BASE + 0x30)
+ #define OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN_SHFT 6
+ #define OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN_SHIFT 5
+ #define OMAP_TLL_SHARED_CONF_USB_DIVRATION_SHIFT 2
+ #define OMAP_TLL_SHARED_CONF_FCLK_REQ_SHIFT 1
+ #define OMAP_TLL_SHARED_CONF_FCLK_IS_ON_SHIFT 0
+
+#define OMAP_TLL_CHANNEL_CONF(num)\
+ (OMAP_USBHOST_TLL_BASE + (0x040 + 0x004 * num))
+ #define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF_SHIFT 11
+ #define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE_SHIFT 10
+ #define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE_SHIFT 9
+ #define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE_SHIFT 8
+ #define OMAP_TLL_CHANNEL_CONF_CHANEN_SHIFT 0
+
+#define OMAP_TLL_ULPI_FUNCTION_CTRL(num)\
+ (OMAP_USBHOST_TLL_BASE + (0x804 + 0x100 * num))
+#define OMAP_TLL_ULPI_INTERFACE_CTRL(num)\
+ (OMAP_USBHOST_TLL_BASE + (0x807 + 0x100 * num))
+#define OMAP_TLL_ULPI_OTG_CTRL(num)\
+ (OMAP_USBHOST_TLL_BASE + (0x80A + 0x100 * num))
+#define OMAP_TLL_ULPI_INT_EN_RISE(num)\
+ (OMAP_USBHOST_TLL_BASE + (0x80D + 0x100 * num))
+#define OMAP_TLL_ULPI_INT_EN_FALL(num)\
+ (OMAP_USBHOST_TLL_BASE + (0x810 + 0x100 * num))
+#define OMAP_TLL_ULPI_INT_STATUS(num)\
+ (OMAP_USBHOST_TLL_BASE + (0x813 + 0x100 * num))
+#define OMAP_TLL_ULPI_INT_LATCH(num)\
+ (OMAP_USBHOST_TLL_BASE + (0x814 + 0x100 * num))
+#define OMAP_TLL_ULPI_DEBUG(num)\
+ (OMAP_USBHOST_TLL_BASE + (0x815 + 0x100 * num))
+#define OMAP_TLL_ULPI_SCRATCH_REGISTER(num)\
+ (OMAP_USBHOST_TLL_BASE + (0x816 + 0x100 * num))
+
+#define OMAP_TLL_CHANNEL_COUNT 3
+ #define OMAP_TLL_CHANNEL_1_EN_MASK 1
+ #define OMAP_TLL_CHANNEL_2_EN_MASK 2
+ #define OMAP_TLL_CHANNEL_3_EN_MASK 4
+
+/* UHH Register Set */
+#define OMAP_USBHOST_UHH_BASE (OMAP_USBHOST_BASE + 0x4000)
+#define OMAP_UHH_REVISION (OMAP_USBHOST_UHH_BASE + 0x00)
+#define OMAP_UHH_SYSCONFIG (OMAP_USBHOST_UHH_BASE + 0x10)
+ #define OMAP_UHH_SYSCONFIG_MIDLEMODE_SHIFT 12
+ #define OMAP_UHH_SYSCONFIG_CACTIVITY_SHIFT 8
+ #define OMAP_UHH_SYSCONFIG_SIDLEMODE_SHIFT 3
+ #define OMAP_UHH_SYSCONFIG_ENAWAKEUP_SHIFT 2
+ #define OMAP_UHH_SYSCONFIG_SOFTRESET_SHIFT 1
+ #define OMAP_UHH_SYSCONFIG_AUTOIDLE_SHIFT 0
+
+#define OMAP_UHH_SYSSTATUS (OMAP_USBHOST_UHH_BASE + 0x14)
+#define OMAP_UHH_HOSTCONFIG (OMAP_USBHOST_UHH_BASE + 0x40)
+ #define OMAP_UHH_HOSTCONFIG_ULPI_BYPASS_SHIFT 0
+ #define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN_SHIFT 2
+ #define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN_SHIFT 3
+ #define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN_SHIFT 4
+ #define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN_SHIFT 5
+
+#define OMAP_UHH_DEBUG_CSR (OMAP_USBHOST_UHH_BASE + 0x44)
+
+/* EHCI Register Set */
+#define OMAP_USBHOST_EHCI_BASE (OMAP_USBHOST_BASE + 0x4800)
+#define EHCI_INSNREG05_ULPI (OMAP_USBHOST_EHCI_BASE + 0xA4)
+ #define EHCI_INSNREG05_ULPI_CONTROL_SHIFT 31
+ #define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT 24
+ #define EHCI_INSNREG05_ULPI_OPSEL_SHIFT 22
+ #define EHCI_INSNREG05_ULPI_REGADD_SHIFT 16
+ #define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8
+ #define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0
+
+/* OHCI Register Set */
+#define OMAP_USBHOST_OHCI_BASE (OMAP_USBHOST_BASE + 0x4400)
+
+#endif/* __EHCI_OMAP_H*/
--- /dev/null
- #include <asm/arch/hardware.h>
- #include <asm/arch/memory.h>
- #include <asm/arch/gpio.h>
+/*
+ * Copyright (C) 2005-2006 by Texas Instruments
+ *
+ * This file is part of the Inventra Controller Driver for Linux.
+ *
+ * The Inventra Controller Driver for Linux 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.
+ *
+ * The Inventra Controller Driver for Linux 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 The Inventra Controller Driver for Linux ; 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/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
- #include <asm/arch/i2c-client.h>
++#include <mach/hardware.h>
++#include <mach/memory.h>
++#include <mach/gpio.h>
+#include <asm/mach-types.h>
+
+#include "musb_core.h"
+
+#ifdef CONFIG_MACH_DAVINCI_EVM
++#include <mach/i2c-client.h>
+#endif
+
+#include "davinci.h"
+#include "cppi_dma.h"
+
+
+/* REVISIT (PM) we should be able to keep the PHY in low power mode most
+ * of the time (24 MHZ oscillator and PLL off, etc) by setting POWER.D0
+ * and, when in host mode, autosuspending idle root ports... PHYPLLON
+ * (overriding SUSPENDM?) then likely needs to stay off.
+ */
+
+static inline void phy_on(void)
+{
+ /* start the on-chip PHY and its PLL */
+ __raw_writel(USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON,
+ (void __force __iomem *) IO_ADDRESS(USBPHY_CTL_PADDR));
+ while ((__raw_readl((void __force __iomem *)
+ IO_ADDRESS(USBPHY_CTL_PADDR))
+ & USBPHY_PHYCLKGD) == 0)
+ cpu_relax();
+}
+
+static inline void phy_off(void)
+{
+ /* powerdown the on-chip PHY and its oscillator */
+ __raw_writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, (void __force __iomem *)
+ IO_ADDRESS(USBPHY_CTL_PADDR));
+}
+
+static int dma_off = 1;
+
+void musb_platform_enable(struct musb *musb)
+{
+ u32 tmp, old, val;
+
+ /* workaround: setup irqs through both register sets */
+ tmp = (musb->epmask & DAVINCI_USB_TX_ENDPTS_MASK)
+ << DAVINCI_USB_TXINT_SHIFT;
+ musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_SET_REG, tmp);
+ old = tmp;
+ tmp = (musb->epmask & (0xfffe & DAVINCI_USB_RX_ENDPTS_MASK))
+ << DAVINCI_USB_RXINT_SHIFT;
+ musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_SET_REG, tmp);
+ tmp |= old;
+
+ val = ~MUSB_INTR_SOF;
+ tmp |= ((val & 0x01ff) << DAVINCI_USB_USBINT_SHIFT);
+ musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_SET_REG, tmp);
+
+ if (is_dma_capable() && !dma_off)
+ printk(KERN_WARNING "%s %s: dma not reactivated\n",
+ __FILE__, __func__);
+ else
+ dma_off = 0;
+
+ /* force a DRVVBUS irq so we can start polling for ID change */
+ if (is_otg_enabled(musb))
+ musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
+ DAVINCI_INTR_DRVVBUS << DAVINCI_USB_USBINT_SHIFT);
+}
+
+/*
+ * Disable the HDRC and flush interrupts
+ */
+void musb_platform_disable(struct musb *musb)
+{
+ /* because we don't set CTRLR.UINT, "important" to:
+ * - not read/write INTRUSB/INTRUSBE
+ * - (except during initial setup, as workaround)
+ * - use INTSETR/INTCLRR instead
+ */
+ musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_CLR_REG,
+ DAVINCI_USB_USBINT_MASK
+ | DAVINCI_USB_TXINT_MASK
+ | DAVINCI_USB_RXINT_MASK);
+ musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
+ musb_writel(musb->ctrl_base, DAVINCI_USB_EOI_REG, 0);
+
+ if (is_dma_capable() && !dma_off)
+ WARNING("dma still active\n");
+}
+
+
+/* REVISIT it's not clear whether DaVinci can support full OTG. */
+
+static int vbus_state = -1;
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+#define portstate(stmt) stmt
+#else
+#define portstate(stmt)
+#endif
+
+
+/* VBUS SWITCHING IS BOARD-SPECIFIC */
+
+#ifdef CONFIG_MACH_DAVINCI_EVM
+#ifndef CONFIG_MACH_DAVINCI_EVM_OTG
+
+/* I2C operations are always synchronous, and require a task context.
+ * With unloaded systems, using the shared workqueue seems to suffice
+ * to satisfy the 100msec A_WAIT_VRISE timeout...
+ */
+static void evm_deferred_drvvbus(struct work_struct *ignored)
+{
+ davinci_i2c_expander_op(0x3a, USB_DRVVBUS, vbus_state);
+ vbus_state = !vbus_state;
+}
+static DECLARE_WORK(evm_vbus_work, evm_deferred_drvvbus);
+
+#endif /* modified board */
+#endif /* EVM */
+
+static void davinci_source_power(struct musb *musb, int is_on, int immediate)
+{
+ if (is_on)
+ is_on = 1;
+
+ if (vbus_state == is_on)
+ return;
+ vbus_state = !is_on; /* 0/1 vs "-1 == unknown/init" */
+
+#ifdef CONFIG_MACH_DAVINCI_EVM
+ if (machine_is_davinci_evm()) {
+#ifdef CONFIG_MACH_DAVINCI_EVM_OTG
+ /* modified EVM board switching VBUS with GPIO(6) not I2C
+ * NOTE: PINMUX0.RGB888 (bit23) must be clear
+ */
+ if (is_on)
+ gpio_set(GPIO(6));
+ else
+ gpio_clear(GPIO(6));
+ immediate = 1;
+#else
+ if (immediate)
+ davinci_i2c_expander_op(0x3a, USB_DRVVBUS, !is_on);
+ else
+ schedule_work(&evm_vbus_work);
+#endif
+ }
+#endif
+ if (immediate)
+ vbus_state = is_on;
+}
+
+static void davinci_set_vbus(struct musb *musb, int is_on)
+{
+ WARN_ON(is_on && is_peripheral_active(musb));
+ davinci_source_power(musb, is_on, 0);
+}
+
+
+#define POLL_SECONDS 2
+
+static struct timer_list otg_workaround;
+
+static void otg_timer(unsigned long _musb)
+{
+ struct musb *musb = (void *)_musb;
+ void __iomem *mregs = musb->mregs;
+ u8 devctl;
+ unsigned long flags;
+
+ /* We poll because DaVinci's won't expose several OTG-critical
+ * status change events (from the transceiver) otherwise.
+ */
+ devctl = musb_readb(mregs, MUSB_DEVCTL);
+ DBG(7, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb));
+
+ spin_lock_irqsave(&musb->lock, flags);
+ switch (musb->xceiv.state) {
+ case OTG_STATE_A_WAIT_VFALL:
+ /* Wait till VBUS falls below SessionEnd (~0.2V); the 1.3 RTL
+ * seems to mis-handle session "start" otherwise (or in our
+ * case "recover"), in routine "VBUS was valid by the time
+ * VBUSERR got reported during enumeration" cases.
+ */
+ if (devctl & MUSB_DEVCTL_VBUS) {
+ mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
+ break;
+ }
+ musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
+ musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
+ MUSB_INTR_VBUSERROR << DAVINCI_USB_USBINT_SHIFT);
+ break;
+ case OTG_STATE_B_IDLE:
+ if (!is_peripheral_enabled(musb))
+ break;
+
+ /* There's no ID-changed IRQ, so we have no good way to tell
+ * when to switch to the A-Default state machine (by setting
+ * the DEVCTL.SESSION flag).
+ *
+ * Workaround: whenever we're in B_IDLE, try setting the
+ * session flag every few seconds. If it works, ID was
+ * grounded and we're now in the A-Default state machine.
+ *
+ * NOTE setting the session flag is _supposed_ to trigger
+ * SRP, but clearly it doesn't.
+ */
+ musb_writeb(mregs, MUSB_DEVCTL,
+ devctl | MUSB_DEVCTL_SESSION);
+ devctl = musb_readb(mregs, MUSB_DEVCTL);
+ if (devctl & MUSB_DEVCTL_BDEVICE)
+ mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
+ else
+ musb->xceiv.state = OTG_STATE_A_IDLE;
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+static irqreturn_t davinci_interrupt(int irq, void *__hci)
+{
+ unsigned long flags;
+ irqreturn_t retval = IRQ_NONE;
+ struct musb *musb = __hci;
+ void __iomem *tibase = musb->ctrl_base;
+ u32 tmp;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ /* NOTE: DaVinci shadows the Mentor IRQs. Don't manage them through
+ * the Mentor registers (except for setup), use the TI ones and EOI.
+ *
+ * Docs describe irq "vector" registers asociated with the CPPI and
+ * USB EOI registers. These hold a bitmask corresponding to the
+ * current IRQ, not an irq handler address. Would using those bits
+ * resolve some of the races observed in this dispatch code??
+ */
+
+ /* CPPI interrupts share the same IRQ line, but have their own
+ * mask, state, "vector", and EOI registers.
+ */
+ if (is_cppi_enabled()) {
+ u32 cppi_tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG);
+ u32 cppi_rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG);
+
+ if (cppi_tx || cppi_rx) {
+ DBG(4, "CPPI IRQ t%x r%x\n", cppi_tx, cppi_rx);
+ cppi_completion(musb, cppi_rx, cppi_tx);
+ retval = IRQ_HANDLED;
+ }
+ }
+
+ /* ack and handle non-CPPI interrupts */
+ tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG);
+ musb_writel(tibase, DAVINCI_USB_INT_SRC_CLR_REG, tmp);
+ DBG(4, "IRQ %08x\n", tmp);
+
+ musb->int_rx = (tmp & DAVINCI_USB_RXINT_MASK)
+ >> DAVINCI_USB_RXINT_SHIFT;
+ musb->int_tx = (tmp & DAVINCI_USB_TXINT_MASK)
+ >> DAVINCI_USB_TXINT_SHIFT;
+ musb->int_usb = (tmp & DAVINCI_USB_USBINT_MASK)
+ >> DAVINCI_USB_USBINT_SHIFT;
+
+ /* DRVVBUS irqs are the only proxy we have (a very poor one!) for
+ * DaVinci's missing ID change IRQ. We need an ID change IRQ to
+ * switch appropriately between halves of the OTG state machine.
+ * Managing DEVCTL.SESSION per Mentor docs requires we know its
+ * value, but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
+ * Also, DRVVBUS pulses for SRP (but not at 5V) ...
+ */
+ if (tmp & (DAVINCI_INTR_DRVVBUS << DAVINCI_USB_USBINT_SHIFT)) {
+ int drvvbus = musb_readl(tibase, DAVINCI_USB_STAT_REG);
+ void __iomem *mregs = musb->mregs;
+ u8 devctl = musb_readb(mregs, MUSB_DEVCTL);
+ int err = musb->int_usb & MUSB_INTR_VBUSERROR;
+
+ err = is_host_enabled(musb)
+ && (musb->int_usb & MUSB_INTR_VBUSERROR);
+ if (err) {
+ /* The Mentor core doesn't debounce VBUS as needed
+ * to cope with device connect current spikes. This
+ * means it's not uncommon for bus-powered devices
+ * to get VBUS errors during enumeration.
+ *
+ * This is a workaround, but newer RTL from Mentor
+ * seems to allow a better one: "re"starting sessions
+ * without waiting (on EVM, a **long** time) for VBUS
+ * to stop registering in devctl.
+ */
+ musb->int_usb &= ~MUSB_INTR_VBUSERROR;
+ musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
+ mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
+ WARNING("VBUS error workaround (delay coming)\n");
+ } else if (is_host_enabled(musb) && drvvbus) {
+ musb->is_active = 1;
+ MUSB_HST_MODE(musb);
+ musb->xceiv.default_a = 1;
+ musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
+ portstate(musb->port1_status |= USB_PORT_STAT_POWER);
+ del_timer(&otg_workaround);
+ } else {
+ musb->is_active = 0;
+ MUSB_DEV_MODE(musb);
+ musb->xceiv.default_a = 0;
+ musb->xceiv.state = OTG_STATE_B_IDLE;
+ portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
+ }
+
+ /* NOTE: this must complete poweron within 100 msec */
+ davinci_source_power(musb, drvvbus, 0);
+ DBG(2, "VBUS %s (%s)%s, devctl %02x\n",
+ drvvbus ? "on" : "off",
+ otg_state_string(musb),
+ err ? " ERROR" : "",
+ devctl);
+ retval = IRQ_HANDLED;
+ }
+
+ if (musb->int_tx || musb->int_rx || musb->int_usb)
+ retval |= musb_interrupt(musb);
+
+ /* irq stays asserted until EOI is written */
+ musb_writel(tibase, DAVINCI_USB_EOI_REG, 0);
+
+ /* poll for ID change */
+ if (is_otg_enabled(musb)
+ && musb->xceiv.state == OTG_STATE_B_IDLE)
+ mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ /* REVISIT we sometimes get unhandled IRQs
+ * (e.g. ep0). not clear why...
+ */
+ if (retval != IRQ_HANDLED)
+ DBG(5, "unhandled? %08x\n", tmp);
+ return IRQ_HANDLED;
+}
+
+int __init musb_platform_init(struct musb *musb)
+{
+ void __iomem *tibase = musb->ctrl_base;
+ u32 revision;
+
+ musb->mregs += DAVINCI_BASE_OFFSET;
+#if 0
+ /* REVISIT there's something odd about clocking, this
+ * didn't appear do the job ...
+ */
+ musb->clock = clk_get(pDevice, "usb");
+ if (IS_ERR(musb->clock))
+ return PTR_ERR(musb->clock);
+
+ status = clk_enable(musb->clock);
+ if (status < 0)
+ return -ENODEV;
+#endif
+
+ /* returns zero if e.g. not clocked */
+ revision = musb_readl(tibase, DAVINCI_USB_VERSION_REG);
+ if (revision == 0)
+ return -ENODEV;
+
+ if (is_host_enabled(musb))
+ setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
+
+ musb->board_set_vbus = davinci_set_vbus;
+ davinci_source_power(musb, 0, 1);
+
+ /* reset the controller */
+ musb_writel(tibase, DAVINCI_USB_CTRL_REG, 0x1);
+
+ /* start the on-chip PHY and its PLL */
+ phy_on();
+
+ msleep(5);
+
+ /* NOTE: irqs are in mixed mode, not bypass to pure-musb */
+ pr_debug("DaVinci OTG revision %08x phy %03x control %02x\n",
+ revision, __raw_readl((void __force __iomem *)
+ IO_ADDRESS(USBPHY_CTL_PADDR)),
+ musb_readb(tibase, DAVINCI_USB_CTRL_REG));
+
+ musb->isr = davinci_interrupt;
+ return 0;
+}
+
+int musb_platform_exit(struct musb *musb)
+{
+ if (is_host_enabled(musb))
+ del_timer_sync(&otg_workaround);
+
+ davinci_source_power(musb, 0 /*off*/, 1);
+
+ /* delay, to avoid problems with module reload */
+ if (is_host_enabled(musb) && musb->xceiv.default_a) {
+ int maxdelay = 30;
+ u8 devctl, warn = 0;
+
+ /* if there's no peripheral connected, this can take a
+ * long time to fall, especially on EVM with huge C133.
+ */
+ do {
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ if (!(devctl & MUSB_DEVCTL_VBUS))
+ break;
+ if ((devctl & MUSB_DEVCTL_VBUS) != warn) {
+ warn = devctl & MUSB_DEVCTL_VBUS;
+ DBG(1, "VBUS %d\n",
+ warn >> MUSB_DEVCTL_VBUS_SHIFT);
+ }
+ msleep(1000);
+ maxdelay--;
+ } while (maxdelay > 0);
+
+ /* in OTG mode, another host might be connected */
+ if (devctl & MUSB_DEVCTL_VBUS)
+ DBG(1, "VBUS off timeout (devctl %02x)\n", devctl);
+ }
+
+ phy_off();
+ return 0;
+}
--- /dev/null
- * - <asm/arch/hdrc_cnf.h> for SOC or family details
+/*
+ * MUSB OTG driver core code
+ *
+ * Copyright 2005 Mentor Graphics Corporation
+ * Copyright (C) 2005-2006 by Texas Instruments
+ * Copyright (C) 2006-2007 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Inventra (Multipoint) Dual-Role Controller Driver for Linux.
+ *
+ * This consists of a Host Controller Driver (HCD) and a peripheral
+ * controller driver implementing the "Gadget" API; OTG support is
+ * in the works. These are normal Linux-USB controller drivers which
+ * use IRQs and have no dedicated thread.
+ *
+ * This version of the driver has only been used with products from
+ * Texas Instruments. Those products integrate the Inventra logic
+ * with other DMA, IRQ, and bus modules, as well as other logic that
+ * needs to be reflected in this driver.
+ *
+ *
+ * NOTE: the original Mentor code here was pretty much a collection
+ * of mechanisms that don't seem to have been fully integrated/working
+ * for *any* Linux kernel version. This version aims at Linux 2.6.now,
+ * Key open issues include:
+ *
+ * - Lack of host-side transaction scheduling, for all transfer types.
+ * The hardware doesn't do it; instead, software must.
+ *
+ * This is not an issue for OTG devices that don't support external
+ * hubs, but for more "normal" USB hosts it's a user issue that the
+ * "multipoint" support doesn't scale in the expected ways. That
+ * includes DaVinci EVM in a common non-OTG mode.
+ *
+ * * Control and bulk use dedicated endpoints, and there's as
+ * yet no mechanism to either (a) reclaim the hardware when
+ * peripherals are NAKing, which gets complicated with bulk
+ * endpoints, or (b) use more than a single bulk endpoint in
+ * each direction.
+ *
+ * RESULT: one device may be perceived as blocking another one.
+ *
+ * * Interrupt and isochronous will dynamically allocate endpoint
+ * hardware, but (a) there's no record keeping for bandwidth;
+ * (b) in the common case that few endpoints are available, there
+ * is no mechanism to reuse endpoints to talk to multiple devices.
+ *
+ * RESULT: At one extreme, bandwidth can be overcommitted in
+ * some hardware configurations, no faults will be reported.
+ * At the other extreme, the bandwidth capabilities which do
+ * exist tend to be severely undercommitted. You can't yet hook
+ * up both a keyboard and a mouse to an external USB hub.
+ */
+
+/*
+ * This gets many kinds of configuration information:
+ * - Kconfig for everything user-configurable
- #include <asm/arch/hardware.h>
- #include <asm/arch/memory.h>
++ * - <mach/hdrc_cnf.h> for SOC or family details
+ * - platform_device for addressing, irq, and platform_data
+ * - platform_data is mostly for board-specific informarion
+ *
+ * Most of the conditional compilation will (someday) vanish.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/kobject.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#ifdef CONFIG_ARM
++#include <mach/hardware.h>
++#include <mach/memory.h>
+#include <asm/mach-types.h>
+#endif
+
+#include "musb_core.h"
+
+
+#ifdef CONFIG_ARCH_DAVINCI
+#include "davinci.h"
+#endif
+
+
+
+#if MUSB_DEBUG > 0
+unsigned debug = MUSB_DEBUG;
+module_param(debug, uint, 0);
+MODULE_PARM_DESC(debug, "initial debug message level");
+
+#define MUSB_VERSION_SUFFIX "/dbg"
+#endif
+
+#define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia"
+#define DRIVER_DESC "Inventra Dual-Role USB Controller Driver"
+
+#define MUSB_VERSION_BASE "6.0"
+
+#ifndef MUSB_VERSION_SUFFIX
+#define MUSB_VERSION_SUFFIX ""
+#endif
+#define MUSB_VERSION MUSB_VERSION_BASE MUSB_VERSION_SUFFIX
+
+#define DRIVER_INFO DRIVER_DESC ", v" MUSB_VERSION
+
+#define MUSB_DRIVER_NAME "musb_hdrc"
+const char musb_driver_name[] = MUSB_DRIVER_NAME;
+
+MODULE_DESCRIPTION(DRIVER_INFO);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" MUSB_DRIVER_NAME);
+
+
+/*-------------------------------------------------------------------------*/
+
+static inline struct musb *dev_to_musb(struct device *dev)
+{
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ /* usbcore insists dev->driver_data is a "struct hcd *" */
+ return hcd_to_musb(dev_get_drvdata(dev));
+#else
+ return dev_get_drvdata(dev);
+#endif
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef CONFIG_USB_TUSB6010
+/*
+ * Load an endpoint's FIFO
+ */
+void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)
+{
+ void __iomem *fifo = hw_ep->fifo;
+
+ prefetch((u8 *)src);
+
+ DBG(4, "%cX ep%d fifo %p count %d buf %p\n",
+ 'T', hw_ep->epnum, fifo, len, src);
+
+ /* we can't assume unaligned reads work */
+ if (likely((0x01 & (unsigned long) src) == 0)) {
+ u16 index = 0;
+
+ /* best case is 32bit-aligned source address */
+ if ((0x02 & (unsigned long) src) == 0) {
+ if (len >= 4) {
+ writesl(fifo, src + index, len >> 2);
+ index += len & ~0x03;
+ }
+ if (len & 0x02) {
+ musb_writew(fifo, 0, *(u16 *)&src[index]);
+ index += 2;
+ }
+ } else {
+ if (len >= 2) {
+ writesw(fifo, src + index, len >> 1);
+ index += len & ~0x01;
+ }
+ }
+ if (len & 0x01)
+ musb_writeb(fifo, 0, src[index]);
+ } else {
+ /* byte aligned */
+ writesb(fifo, src, len);
+ }
+}
+
+/*
+ * Unload an endpoint's FIFO
+ */
+void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
+{
+ void __iomem *fifo = hw_ep->fifo;
+
+ DBG(4, "%cX ep%d fifo %p count %d buf %p\n",
+ 'R', hw_ep->epnum, fifo, len, dst);
+
+ /* we can't assume unaligned writes work */
+ if (likely((0x01 & (unsigned long) dst) == 0)) {
+ u16 index = 0;
+
+ /* best case is 32bit-aligned destination address */
+ if ((0x02 & (unsigned long) dst) == 0) {
+ if (len >= 4) {
+ readsl(fifo, dst, len >> 2);
+ index = len & ~0x03;
+ }
+ if (len & 0x02) {
+ *(u16 *)&dst[index] = musb_readw(fifo, 0);
+ index += 2;
+ }
+ } else {
+ if (len >= 2) {
+ readsw(fifo, dst, len >> 1);
+ index = len & ~0x01;
+ }
+ }
+ if (len & 0x01)
+ dst[index] = musb_readb(fifo, 0);
+ } else {
+ /* byte aligned */
+ readsb(fifo, dst, len);
+ }
+}
+
+#endif /* normal PIO */
+
+
+/*-------------------------------------------------------------------------*/
+
+/* for high speed test mode; see USB 2.0 spec 7.1.20 */
+static const u8 musb_test_packet[53] = {
+ /* implicit SYNC then DATA0 to start */
+
+ /* JKJKJKJK x9 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* JJKKJJKK x8 */
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ /* JJJJKKKK x8 */
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ /* JJJJJJJKKKKKKK x8 */
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ /* JJJJJJJK x8 */
+ 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd,
+ /* JKKKKKKK x10, JK */
+ 0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e
+
+ /* implicit CRC16 then EOP to end */
+};
+
+void musb_load_testpacket(struct musb *musb)
+{
+ void __iomem *regs = musb->endpoints[0].regs;
+
+ musb_ep_select(musb->mregs, 0);
+ musb_write_fifo(musb->control_ep,
+ sizeof(musb_test_packet), musb_test_packet);
+ musb_writew(regs, MUSB_CSR0, MUSB_CSR0_TXPKTRDY);
+}
+
+/*-------------------------------------------------------------------------*/
+
+const char *otg_state_string(struct musb *musb)
+{
+ switch (musb->xceiv.state) {
+ case OTG_STATE_A_IDLE: return "a_idle";
+ case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise";
+ case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon";
+ case OTG_STATE_A_HOST: return "a_host";
+ case OTG_STATE_A_SUSPEND: return "a_suspend";
+ case OTG_STATE_A_PERIPHERAL: return "a_peripheral";
+ case OTG_STATE_A_WAIT_VFALL: return "a_wait_vfall";
+ case OTG_STATE_A_VBUS_ERR: return "a_vbus_err";
+ case OTG_STATE_B_IDLE: return "b_idle";
+ case OTG_STATE_B_SRP_INIT: return "b_srp_init";
+ case OTG_STATE_B_PERIPHERAL: return "b_peripheral";
+ case OTG_STATE_B_WAIT_ACON: return "b_wait_acon";
+ case OTG_STATE_B_HOST: return "b_host";
+ default: return "UNDEFINED";
+ }
+}
+
+#ifdef CONFIG_USB_MUSB_OTG
+
+/*
+ * See also USB_OTG_1-3.pdf 6.6.5 Timers
+ * REVISIT: Are the other timers done in the hardware?
+ */
+#define TB_ASE0_BRST 100 /* Min 3.125 ms */
+
+/*
+ * Handles OTG hnp timeouts, such as b_ase0_brst
+ */
+void musb_otg_timer_func(unsigned long data)
+{
+ struct musb *musb = (struct musb *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&musb->lock, flags);
+ switch (musb->xceiv.state) {
+ case OTG_STATE_B_WAIT_ACON:
+ DBG(1, "HNP: b_wait_acon timeout; back to b_peripheral\n");
+ musb_g_disconnect(musb);
+ musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+ musb->is_active = 0;
+ break;
+ case OTG_STATE_A_WAIT_BCON:
+ DBG(1, "HNP: a_wait_bcon timeout; back to a_host\n");
+ musb_hnp_stop(musb);
+ break;
+ default:
+ DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb));
+ }
+ musb->ignore_disconnect = 0;
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+static DEFINE_TIMER(musb_otg_timer, musb_otg_timer_func, 0, 0);
+
+/*
+ * Stops the B-device HNP state. Caller must take care of locking.
+ */
+void musb_hnp_stop(struct musb *musb)
+{
+ struct usb_hcd *hcd = musb_to_hcd(musb);
+ void __iomem *mbase = musb->mregs;
+ u8 reg;
+
+ switch (musb->xceiv.state) {
+ case OTG_STATE_A_PERIPHERAL:
+ case OTG_STATE_A_WAIT_VFALL:
+ case OTG_STATE_A_WAIT_BCON:
+ DBG(1, "HNP: Switching back to A-host\n");
+ musb_g_disconnect(musb);
+ musb->xceiv.state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ musb->is_active = 0;
+ break;
+ case OTG_STATE_B_HOST:
+ DBG(1, "HNP: Disabling HR\n");
+ hcd->self.is_b_host = 0;
+ musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+ MUSB_DEV_MODE(musb);
+ reg = musb_readb(mbase, MUSB_POWER);
+ reg |= MUSB_POWER_SUSPENDM;
+ musb_writeb(mbase, MUSB_POWER, reg);
+ /* REVISIT: Start SESSION_REQUEST here? */
+ break;
+ default:
+ DBG(1, "HNP: Stopping in unknown state %s\n",
+ otg_state_string(musb));
+ }
+
+ /*
+ * When returning to A state after HNP, avoid hub_port_rebounce(),
+ * which cause occasional OPT A "Did not receive reset after connect"
+ * errors.
+ */
+ musb->port1_status &=
+ ~(1 << USB_PORT_FEAT_C_CONNECTION);
+}
+
+#endif
+
+/*
+ * Interrupt Service Routine to record USB "global" interrupts.
+ * Since these do not happen often and signify things of
+ * paramount importance, it seems OK to check them individually;
+ * the order of the tests is specified in the manual
+ *
+ * @param musb instance pointer
+ * @param int_usb register contents
+ * @param devctl
+ * @param power
+ */
+
+#define STAGE0_MASK (MUSB_INTR_RESUME | MUSB_INTR_SESSREQ \
+ | MUSB_INTR_VBUSERROR | MUSB_INTR_CONNECT \
+ | MUSB_INTR_RESET)
+
+static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
+ u8 devctl, u8 power)
+{
+ irqreturn_t handled = IRQ_NONE;
+ void __iomem *mbase = musb->mregs;
+
+ DBG(3, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl,
+ int_usb);
+
+ /* in host mode, the peripheral may issue remote wakeup.
+ * in peripheral mode, the host may resume the link.
+ * spurious RESUME irqs happen too, paired with SUSPEND.
+ */
+ if (int_usb & MUSB_INTR_RESUME) {
+ handled = IRQ_HANDLED;
+ DBG(3, "RESUME (%s)\n", otg_state_string(musb));
+
+ if (devctl & MUSB_DEVCTL_HM) {
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ switch (musb->xceiv.state) {
+ case OTG_STATE_A_SUSPEND:
+ /* remote wakeup? later, GetPortStatus
+ * will stop RESUME signaling
+ */
+
+ if (power & MUSB_POWER_SUSPENDM) {
+ /* spurious */
+ musb->int_usb &= ~MUSB_INTR_SUSPEND;
+ DBG(2, "Spurious SUSPENDM\n");
+ break;
+ }
+
+ power &= ~MUSB_POWER_SUSPENDM;
+ musb_writeb(mbase, MUSB_POWER,
+ power | MUSB_POWER_RESUME);
+
+ musb->port1_status |=
+ (USB_PORT_STAT_C_SUSPEND << 16)
+ | MUSB_PORT_STAT_RESUME;
+ musb->rh_timer = jiffies
+ + msecs_to_jiffies(20);
+
+ musb->xceiv.state = OTG_STATE_A_HOST;
+ musb->is_active = 1;
+ usb_hcd_resume_root_hub(musb_to_hcd(musb));
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+ musb->is_active = 1;
+ MUSB_DEV_MODE(musb);
+ break;
+ default:
+ WARNING("bogus %s RESUME (%s)\n",
+ "host",
+ otg_state_string(musb));
+ }
+#endif
+ } else {
+ switch (musb->xceiv.state) {
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ case OTG_STATE_A_SUSPEND:
+ /* possibly DISCONNECT is upcoming */
+ musb->xceiv.state = OTG_STATE_A_HOST;
+ usb_hcd_resume_root_hub(musb_to_hcd(musb));
+ break;
+#endif
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ case OTG_STATE_B_WAIT_ACON:
+ case OTG_STATE_B_PERIPHERAL:
+ /* disconnect while suspended? we may
+ * not get a disconnect irq...
+ */
+ if ((devctl & MUSB_DEVCTL_VBUS)
+ != (3 << MUSB_DEVCTL_VBUS_SHIFT)
+ ) {
+ musb->int_usb |= MUSB_INTR_DISCONNECT;
+ musb->int_usb &= ~MUSB_INTR_SUSPEND;
+ break;
+ }
+ musb_g_resume(musb);
+ break;
+ case OTG_STATE_B_IDLE:
+ musb->int_usb &= ~MUSB_INTR_SUSPEND;
+ break;
+#endif
+ default:
+ WARNING("bogus %s RESUME (%s)\n",
+ "peripheral",
+ otg_state_string(musb));
+ }
+ }
+ }
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ /* see manual for the order of the tests */
+ if (int_usb & MUSB_INTR_SESSREQ) {
+ DBG(1, "SESSION_REQUEST (%s)\n", otg_state_string(musb));
+
+ /* IRQ arrives from ID pin sense or (later, if VBUS power
+ * is removed) SRP. responses are time critical:
+ * - turn on VBUS (with silicon-specific mechanism)
+ * - go through A_WAIT_VRISE
+ * - ... to A_WAIT_BCON.
+ * a_wait_vrise_tmout triggers VBUS_ERROR transitions
+ */
+ musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
+ musb->ep0_stage = MUSB_EP0_START;
+ musb->xceiv.state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ musb_set_vbus(musb, 1);
+
+ handled = IRQ_HANDLED;
+ }
+
+ if (int_usb & MUSB_INTR_VBUSERROR) {
+ int ignore = 0;
+
+ /* During connection as an A-Device, we may see a short
+ * current spikes causing voltage drop, because of cable
+ * and peripheral capacitance combined with vbus draw.
+ * (So: less common with truly self-powered devices, where
+ * vbus doesn't act like a power supply.)
+ *
+ * Such spikes are short; usually less than ~500 usec, max
+ * of ~2 msec. That is, they're not sustained overcurrent
+ * errors, though they're reported using VBUSERROR irqs.
+ *
+ * Workarounds: (a) hardware: use self powered devices.
+ * (b) software: ignore non-repeated VBUS errors.
+ *
+ * REVISIT: do delays from lots of DEBUG_KERNEL checks
+ * make trouble here, keeping VBUS < 4.4V ?
+ */
+ switch (musb->xceiv.state) {
+ case OTG_STATE_A_HOST:
+ /* recovery is dicey once we've gotten past the
+ * initial stages of enumeration, but if VBUS
+ * stayed ok at the other end of the link, and
+ * another reset is due (at least for high speed,
+ * to redo the chirp etc), it might work OK...
+ */
+ case OTG_STATE_A_WAIT_BCON:
+ case OTG_STATE_A_WAIT_VRISE:
+ if (musb->vbuserr_retry) {
+ musb->vbuserr_retry--;
+ ignore = 1;
+ devctl |= MUSB_DEVCTL_SESSION;
+ musb_writeb(mbase, MUSB_DEVCTL, devctl);
+ } else {
+ musb->port1_status |=
+ (1 << USB_PORT_FEAT_OVER_CURRENT)
+ | (1 << USB_PORT_FEAT_C_OVER_CURRENT);
+ }
+ break;
+ default:
+ break;
+ }
+
+ DBG(1, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n",
+ otg_state_string(musb),
+ devctl,
+ ({ char *s;
+ switch (devctl & MUSB_DEVCTL_VBUS) {
+ case 0 << MUSB_DEVCTL_VBUS_SHIFT:
+ s = "<SessEnd"; break;
+ case 1 << MUSB_DEVCTL_VBUS_SHIFT:
+ s = "<AValid"; break;
+ case 2 << MUSB_DEVCTL_VBUS_SHIFT:
+ s = "<VBusValid"; break;
+ /* case 3 << MUSB_DEVCTL_VBUS_SHIFT: */
+ default:
+ s = "VALID"; break;
+ }; s; }),
+ VBUSERR_RETRY_COUNT - musb->vbuserr_retry,
+ musb->port1_status);
+
+ /* go through A_WAIT_VFALL then start a new session */
+ if (!ignore)
+ musb_set_vbus(musb, 0);
+ handled = IRQ_HANDLED;
+ }
+
+ if (int_usb & MUSB_INTR_CONNECT) {
+ struct usb_hcd *hcd = musb_to_hcd(musb);
+
+ handled = IRQ_HANDLED;
+ musb->is_active = 1;
+ set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+
+ musb->ep0_stage = MUSB_EP0_START;
+
+#ifdef CONFIG_USB_MUSB_OTG
+ /* flush endpoints when transitioning from Device Mode */
+ if (is_peripheral_active(musb)) {
+ /* REVISIT HNP; just force disconnect */
+ }
+ musb_writew(mbase, MUSB_INTRTXE, musb->epmask);
+ musb_writew(mbase, MUSB_INTRRXE, musb->epmask & 0xfffe);
+ musb_writeb(mbase, MUSB_INTRUSBE, 0xf7);
+#endif
+ musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED
+ |USB_PORT_STAT_HIGH_SPEED
+ |USB_PORT_STAT_ENABLE
+ );
+ musb->port1_status |= USB_PORT_STAT_CONNECTION
+ |(USB_PORT_STAT_C_CONNECTION << 16);
+
+ /* high vs full speed is just a guess until after reset */
+ if (devctl & MUSB_DEVCTL_LSDEV)
+ musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
+
+ if (hcd->status_urb)
+ usb_hcd_poll_rh_status(hcd);
+ else
+ usb_hcd_resume_root_hub(hcd);
+
+ MUSB_HST_MODE(musb);
+
+ /* indicate new connection to OTG machine */
+ switch (musb->xceiv.state) {
+ case OTG_STATE_B_PERIPHERAL:
+ if (int_usb & MUSB_INTR_SUSPEND) {
+ DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n");
+ musb->xceiv.state = OTG_STATE_B_HOST;
+ hcd->self.is_b_host = 1;
+ int_usb &= ~MUSB_INTR_SUSPEND;
+ } else
+ DBG(1, "CONNECT as b_peripheral???\n");
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ DBG(1, "HNP: Waiting to switch to b_host state\n");
+ musb->xceiv.state = OTG_STATE_B_HOST;
+ hcd->self.is_b_host = 1;
+ break;
+ default:
+ if ((devctl & MUSB_DEVCTL_VBUS)
+ == (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
+ musb->xceiv.state = OTG_STATE_A_HOST;
+ hcd->self.is_b_host = 0;
+ }
+ break;
+ }
+ DBG(1, "CONNECT (%s) devctl %02x\n",
+ otg_state_string(musb), devctl);
+ }
+#endif /* CONFIG_USB_MUSB_HDRC_HCD */
+
+ /* mentor saves a bit: bus reset and babble share the same irq.
+ * only host sees babble; only peripheral sees bus reset.
+ */
+ if (int_usb & MUSB_INTR_RESET) {
+ if (is_host_capable() && (devctl & MUSB_DEVCTL_HM) != 0) {
+ /*
+ * Looks like non-HS BABBLE can be ignored, but
+ * HS BABBLE is an error condition. For HS the solution
+ * is to avoid babble in the first place and fix what
+ * caused BABBLE. When HS BABBLE happens we can only
+ * stop the session.
+ */
+ if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV))
+ DBG(1, "BABBLE devctl: %02x\n", devctl);
+ else {
+ ERR("Stopping host session -- babble\n");
+ musb_writeb(mbase, MUSB_DEVCTL, 0);
+ }
+ } else if (is_peripheral_capable()) {
+ DBG(1, "BUS RESET as %s\n", otg_state_string(musb));
+ switch (musb->xceiv.state) {
+#ifdef CONFIG_USB_OTG
+ case OTG_STATE_A_SUSPEND:
+ /* We need to ignore disconnect on suspend
+ * otherwise tusb 2.0 won't reconnect after a
+ * power cycle, which breaks otg compliance.
+ */
+ musb->ignore_disconnect = 1;
+ musb_g_reset(musb);
+ /* FALLTHROUGH */
+ case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */
+ DBG(1, "HNP: Setting timer as %s\n",
+ otg_state_string(musb));
+ musb_otg_timer.data = (unsigned long)musb;
+ mod_timer(&musb_otg_timer, jiffies
+ + msecs_to_jiffies(100));
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ musb_hnp_stop(musb);
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ DBG(1, "HNP: RESET (%s), back to b_peripheral\n",
+ otg_state_string(musb));
+ musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+ musb_g_reset(musb);
+ break;
+#endif
+ case OTG_STATE_B_IDLE:
+ musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
+ /* FALLTHROUGH */
+ case OTG_STATE_B_PERIPHERAL:
+ musb_g_reset(musb);
+ break;
+ default:
+ DBG(1, "Unhandled BUS RESET as %s\n",
+ otg_state_string(musb));
+ }
+ }
+
+ handled = IRQ_HANDLED;
+ }
+ schedule_work(&musb->irq_work);
+
+ return handled;
+}
+
+/*
+ * Interrupt Service Routine to record USB "global" interrupts.
+ * Since these do not happen often and signify things of
+ * paramount importance, it seems OK to check them individually;
+ * the order of the tests is specified in the manual
+ *
+ * @param musb instance pointer
+ * @param int_usb register contents
+ * @param devctl
+ * @param power
+ */
+static irqreturn_t musb_stage2_irq(struct musb *musb, u8 int_usb,
+ u8 devctl, u8 power)
+{
+ irqreturn_t handled = IRQ_NONE;
+
+#if 0
+/* REVISIT ... this would be for multiplexing periodic endpoints, or
+ * supporting transfer phasing to prevent exceeding ISO bandwidth
+ * limits of a given frame or microframe.
+ *
+ * It's not needed for peripheral side, which dedicates endpoints;
+ * though it _might_ use SOF irqs for other purposes.
+ *
+ * And it's not currently needed for host side, which also dedicates
+ * endpoints, relies on TX/RX interval registers, and isn't claimed
+ * to support ISO transfers yet.
+ */
+ if (int_usb & MUSB_INTR_SOF) {
+ void __iomem *mbase = musb->mregs;
+ struct musb_hw_ep *ep;
+ u8 epnum;
+ u16 frame;
+
+ DBG(6, "START_OF_FRAME\n");
+ handled = IRQ_HANDLED;
+
+ /* start any periodic Tx transfers waiting for current frame */
+ frame = musb_readw(mbase, MUSB_FRAME);
+ ep = musb->endpoints;
+ for (epnum = 1; (epnum < musb->nr_endpoints)
+ && (musb->epmask >= (1 << epnum));
+ epnum++, ep++) {
+ /*
+ * FIXME handle framecounter wraps (12 bits)
+ * eliminate duplicated StartUrb logic
+ */
+ if (ep->dwWaitFrame >= frame) {
+ ep->dwWaitFrame = 0;
+ printk("SOF --> periodic TX%s on %d\n",
+ ep->tx_channel ? " DMA" : "",
+ epnum);
+ if (!ep->tx_channel)
+ musb_h_tx_start(musb, epnum);
+ else
+ cppi_hostdma_start(musb, epnum);
+ }
+ } /* end of for loop */
+ }
+#endif
+
+ if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) {
+ DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n",
+ otg_state_string(musb),
+ MUSB_MODE(musb), devctl);
+ handled = IRQ_HANDLED;
+
+ switch (musb->xceiv.state) {
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ case OTG_STATE_A_HOST:
+ case OTG_STATE_A_SUSPEND:
+ musb_root_disconnect(musb);
+ if (musb->a_wait_bcon != 0)
+ musb_platform_try_idle(musb, jiffies
+ + msecs_to_jiffies(musb->a_wait_bcon));
+ break;
+#endif /* HOST */
+#ifdef CONFIG_USB_MUSB_OTG
+ case OTG_STATE_B_HOST:
+ musb_hnp_stop(musb);
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ musb_hnp_stop(musb);
+ musb_root_disconnect(musb);
+ /* FALLTHROUGH */
+ case OTG_STATE_B_WAIT_ACON:
+ /* FALLTHROUGH */
+#endif /* OTG */
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ case OTG_STATE_B_PERIPHERAL:
+ case OTG_STATE_B_IDLE:
+ musb_g_disconnect(musb);
+ break;
+#endif /* GADGET */
+ default:
+ WARNING("unhandled DISCONNECT transition (%s)\n",
+ otg_state_string(musb));
+ break;
+ }
+
+ schedule_work(&musb->irq_work);
+ }
+
+ if (int_usb & MUSB_INTR_SUSPEND) {
+ DBG(1, "SUSPEND (%s) devctl %02x power %02x\n",
+ otg_state_string(musb), devctl, power);
+ handled = IRQ_HANDLED;
+
+ switch (musb->xceiv.state) {
+#ifdef CONFIG_USB_MUSB_OTG
+ case OTG_STATE_A_PERIPHERAL:
+ /*
+ * We cannot stop HNP here, devctl BDEVICE might be
+ * still set.
+ */
+ break;
+#endif
+ case OTG_STATE_B_PERIPHERAL:
+ musb_g_suspend(musb);
+ musb->is_active = is_otg_enabled(musb)
+ && musb->xceiv.gadget->b_hnp_enable;
+ if (musb->is_active) {
+#ifdef CONFIG_USB_MUSB_OTG
+ musb->xceiv.state = OTG_STATE_B_WAIT_ACON;
+ DBG(1, "HNP: Setting timer for b_ase0_brst\n");
+ musb_otg_timer.data = (unsigned long)musb;
+ mod_timer(&musb_otg_timer, jiffies
+ + msecs_to_jiffies(TB_ASE0_BRST));
+#endif
+ }
+ break;
+ case OTG_STATE_A_WAIT_BCON:
+ if (musb->a_wait_bcon != 0)
+ musb_platform_try_idle(musb, jiffies
+ + msecs_to_jiffies(musb->a_wait_bcon));
+ break;
+ case OTG_STATE_A_HOST:
+ musb->xceiv.state = OTG_STATE_A_SUSPEND;
+ musb->is_active = is_otg_enabled(musb)
+ && musb->xceiv.host->b_hnp_enable;
+ break;
+ case OTG_STATE_B_HOST:
+ /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
+ DBG(1, "REVISIT: SUSPEND as B_HOST\n");
+ break;
+ default:
+ /* "should not happen" */
+ musb->is_active = 0;
+ break;
+ }
+ schedule_work(&musb->irq_work);
+ }
+
+
+ return handled;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+* Program the HDRC to start (enable interrupts, dma, etc.).
+*/
+void musb_start(struct musb *musb)
+{
+ void __iomem *regs = musb->mregs;
+ u8 devctl = musb_readb(regs, MUSB_DEVCTL);
+
+ DBG(2, "<== devctl %02x\n", devctl);
+
+ /* Set INT enable registers, enable interrupts */
+ musb_writew(regs, MUSB_INTRTXE, musb->epmask);
+ musb_writew(regs, MUSB_INTRRXE, musb->epmask & 0xfffe);
+ musb_writeb(regs, MUSB_INTRUSBE, 0xf7);
+
+ musb_writeb(regs, MUSB_TESTMODE, 0);
+
+ /* put into basic highspeed mode and start session */
+ musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE
+ | MUSB_POWER_SOFTCONN
+ | MUSB_POWER_HSENAB
+ /* ENSUSPEND wedges tusb */
+ /* | MUSB_POWER_ENSUSPEND */
+ );
+
+ musb->is_active = 0;
+ devctl = musb_readb(regs, MUSB_DEVCTL);
+ devctl &= ~MUSB_DEVCTL_SESSION;
+
+ if (is_otg_enabled(musb)) {
+ /* session started after:
+ * (a) ID-grounded irq, host mode;
+ * (b) vbus present/connect IRQ, peripheral mode;
+ * (c) peripheral initiates, using SRP
+ */
+ if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
+ musb->is_active = 1;
+ else
+ devctl |= MUSB_DEVCTL_SESSION;
+
+ } else if (is_host_enabled(musb)) {
+ /* assume ID pin is hard-wired to ground */
+ devctl |= MUSB_DEVCTL_SESSION;
+
+ } else /* peripheral is enabled */ {
+ if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
+ musb->is_active = 1;
+ }
+ musb_platform_enable(musb);
+ musb_writeb(regs, MUSB_DEVCTL, devctl);
+}
+
+
+static void musb_generic_disable(struct musb *musb)
+{
+ void __iomem *mbase = musb->mregs;
+ u16 temp;
+
+ /* disable interrupts */
+ musb_writeb(mbase, MUSB_INTRUSBE, 0);
+ musb_writew(mbase, MUSB_INTRTXE, 0);
+ musb_writew(mbase, MUSB_INTRRXE, 0);
+
+ /* off */
+ musb_writeb(mbase, MUSB_DEVCTL, 0);
+
+ /* flush pending interrupts */
+ temp = musb_readb(mbase, MUSB_INTRUSB);
+ temp = musb_readw(mbase, MUSB_INTRTX);
+ temp = musb_readw(mbase, MUSB_INTRRX);
+
+}
+
+/*
+ * Make the HDRC stop (disable interrupts, etc.);
+ * reversible by musb_start
+ * called on gadget driver unregister
+ * with controller locked, irqs blocked
+ * acts as a NOP unless some role activated the hardware
+ */
+void musb_stop(struct musb *musb)
+{
+ /* stop IRQs, timers, ... */
+ musb_platform_disable(musb);
+ musb_generic_disable(musb);
+ DBG(3, "HDRC disabled\n");
+
+ /* FIXME
+ * - mark host and/or peripheral drivers unusable/inactive
+ * - disable DMA (and enable it in HdrcStart)
+ * - make sure we can musb_start() after musb_stop(); with
+ * OTG mode, gadget driver module rmmod/modprobe cycles that
+ * - ...
+ */
+ musb_platform_try_idle(musb, 0);
+}
+
+static void musb_shutdown(struct platform_device *pdev)
+{
+ struct musb *musb = dev_to_musb(&pdev->dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&musb->lock, flags);
+ musb_platform_disable(musb);
+ musb_generic_disable(musb);
+ if (musb->clock) {
+ clk_put(musb->clock);
+ musb->clock = NULL;
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ /* FIXME power down */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * The silicon either has hard-wired endpoint configurations, or else
+ * "dynamic fifo" sizing. The driver has support for both, though at this
+ * writing only the dynamic sizing is very well tested. We use normal
+ * idioms to so both modes are compile-tested, but dead code elimination
+ * leaves only the relevant one in the object file.
+ *
+ * We don't currently use dynamic fifo setup capability to do anything
+ * more than selecting one of a bunch of predefined configurations.
+ */
+#ifdef MUSB_C_DYNFIFO_DEF
+#define can_dynfifo() 1
+#else
+#define can_dynfifo() 0
+#endif
+
+#if defined(CONFIG_USB_TUSB6010) || \
+ defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX)
+static ushort __initdata fifo_mode = 4;
+#else
+static ushort __initdata fifo_mode = 2;
+#endif
+
+/* "modprobe ... fifo_mode=1" etc */
+module_param(fifo_mode, ushort, 0);
+MODULE_PARM_DESC(fifo_mode, "initial endpoint configuration");
+
+
+#define DYN_FIFO_SIZE (1<<(MUSB_C_RAM_BITS+2))
+
+enum fifo_style { FIFO_RXTX, FIFO_TX, FIFO_RX } __attribute__ ((packed));
+enum buf_mode { BUF_SINGLE, BUF_DOUBLE } __attribute__ ((packed));
+
+struct fifo_cfg {
+ u8 hw_ep_num;
+ enum fifo_style style;
+ enum buf_mode mode;
+ u16 maxpacket;
+};
+
+/*
+ * tables defining fifo_mode values. define more if you like.
+ * for host side, make sure both halves of ep1 are set up.
+ */
+
+/* mode 0 - fits in 2KB */
+static struct fifo_cfg __initdata mode_0_cfg[] = {
+{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, },
+{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, },
+{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, },
+};
+
+/* mode 1 - fits in 4KB */
+static struct fifo_cfg __initdata mode_1_cfg[] = {
+{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
+{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
+{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, .mode = BUF_DOUBLE, },
+{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, },
+{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, },
+};
+
+/* mode 2 - fits in 4KB */
+static struct fifo_cfg __initdata mode_2_cfg[] = {
+{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, },
+{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, },
+};
+
+/* mode 3 - fits in 4KB */
+static struct fifo_cfg __initdata mode_3_cfg[] = {
+{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
+{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
+{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 3, .style = FIFO_RXTX, .maxpacket = 256, },
+{ .hw_ep_num = 4, .style = FIFO_RXTX, .maxpacket = 256, },
+};
+
+/* mode 4 - fits in 16KB */
+static struct fifo_cfg __initdata mode_4_cfg[] = {
+{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 8, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 13, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 13, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, },
+{ .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, },
+};
+
+
+/*
+ * configure a fifo; for non-shared endpoints, this may be called
+ * once for a tx fifo and once for an rx fifo.
+ *
+ * returns negative errno or offset for next fifo.
+ */
+static int __init
+fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep,
+ const struct fifo_cfg *cfg, u16 offset)
+{
+ void __iomem *mbase = musb->mregs;
+ int size = 0;
+ u16 maxpacket = cfg->maxpacket;
+ u16 c_off = offset >> 3;
+ u8 c_size;
+
+ /* expect hw_ep has already been zero-initialized */
+
+ size = ffs(max(maxpacket, (u16) 8)) - 1;
+ maxpacket = 1 << size;
+
+ c_size = size - 3;
+ if (cfg->mode == BUF_DOUBLE) {
+ if ((offset + (maxpacket << 1)) > DYN_FIFO_SIZE)
+ return -EMSGSIZE;
+ c_size |= MUSB_FIFOSZ_DPB;
+ } else {
+ if ((offset + maxpacket) > DYN_FIFO_SIZE)
+ return -EMSGSIZE;
+ }
+
+ /* configure the FIFO */
+ musb_writeb(mbase, MUSB_INDEX, hw_ep->epnum);
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ /* EP0 reserved endpoint for control, bidirectional;
+ * EP1 reserved for bulk, two unidirection halves.
+ */
+ if (hw_ep->epnum == 1)
+ musb->bulk_ep = hw_ep;
+ /* REVISIT error check: be sure ep0 can both rx and tx ... */
+#endif
+ switch (cfg->style) {
+ case FIFO_TX:
+ musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
+ musb_writew(mbase, MUSB_TXFIFOADD, c_off);
+ hw_ep->tx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
+ hw_ep->max_packet_sz_tx = maxpacket;
+ break;
+ case FIFO_RX:
+ musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
+ musb_writew(mbase, MUSB_RXFIFOADD, c_off);
+ hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
+ hw_ep->max_packet_sz_rx = maxpacket;
+ break;
+ case FIFO_RXTX:
+ musb_writeb(mbase, MUSB_TXFIFOSZ, c_size);
+ musb_writew(mbase, MUSB_TXFIFOADD, c_off);
+ hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB);
+ hw_ep->max_packet_sz_rx = maxpacket;
+
+ musb_writeb(mbase, MUSB_RXFIFOSZ, c_size);
+ musb_writew(mbase, MUSB_RXFIFOADD, c_off);
+ hw_ep->tx_double_buffered = hw_ep->rx_double_buffered;
+ hw_ep->max_packet_sz_tx = maxpacket;
+
+ hw_ep->is_shared_fifo = true;
+ break;
+ }
+
+ /* NOTE rx and tx endpoint irqs aren't managed separately,
+ * which happens to be ok
+ */
+ musb->epmask |= (1 << hw_ep->epnum);
+
+ return offset + (maxpacket << ((c_size & MUSB_FIFOSZ_DPB) ? 1 : 0));
+}
+
+static struct fifo_cfg __initdata ep0_cfg = {
+ .style = FIFO_RXTX, .maxpacket = 64,
+};
+
+static int __init ep_config_from_table(struct musb *musb)
+{
+ const struct fifo_cfg *cfg;
+ unsigned i, n;
+ int offset;
+ struct musb_hw_ep *hw_ep = musb->endpoints;
+
+ switch (fifo_mode) {
+ default:
+ fifo_mode = 0;
+ /* FALLTHROUGH */
+ case 0:
+ cfg = mode_0_cfg;
+ n = ARRAY_SIZE(mode_0_cfg);
+ break;
+ case 1:
+ cfg = mode_1_cfg;
+ n = ARRAY_SIZE(mode_1_cfg);
+ break;
+ case 2:
+ cfg = mode_2_cfg;
+ n = ARRAY_SIZE(mode_2_cfg);
+ break;
+ case 3:
+ cfg = mode_3_cfg;
+ n = ARRAY_SIZE(mode_3_cfg);
+ break;
+ case 4:
+ cfg = mode_4_cfg;
+ n = ARRAY_SIZE(mode_4_cfg);
+ break;
+ }
+
+ printk(KERN_DEBUG "%s: setup fifo_mode %d\n",
+ musb_driver_name, fifo_mode);
+
+
+ offset = fifo_setup(musb, hw_ep, &ep0_cfg, 0);
+ /* assert(offset > 0) */
+
+ /* NOTE: for RTL versions >= 1.400 EPINFO and RAMINFO would
+ * be better than static MUSB_C_NUM_EPS and DYN_FIFO_SIZE...
+ */
+
+ for (i = 0; i < n; i++) {
+ u8 epn = cfg->hw_ep_num;
+
+ if (epn >= MUSB_C_NUM_EPS) {
+ pr_debug("%s: invalid ep %d\n",
+ musb_driver_name, epn);
+ continue;
+ }
+ offset = fifo_setup(musb, hw_ep + epn, cfg++, offset);
+ if (offset < 0) {
+ pr_debug("%s: mem overrun, ep %d\n",
+ musb_driver_name, epn);
+ return -EINVAL;
+ }
+ epn++;
+ musb->nr_endpoints = max(epn, musb->nr_endpoints);
+ }
+
+ printk(KERN_DEBUG "%s: %d/%d max ep, %d/%d memory\n",
+ musb_driver_name,
+ n + 1, MUSB_C_NUM_EPS * 2 - 1,
+ offset, DYN_FIFO_SIZE);
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ if (!musb->bulk_ep) {
+ pr_debug("%s: missing bulk\n", musb_driver_name);
+ return -EINVAL;
+ }
+#endif
+
+ return 0;
+}
+
+
+/*
+ * ep_config_from_hw - when MUSB_C_DYNFIFO_DEF is false
+ * @param musb the controller
+ */
+static int __init ep_config_from_hw(struct musb *musb)
+{
+ u8 epnum = 0, reg;
+ struct musb_hw_ep *hw_ep;
+ void *mbase = musb->mregs;
+
+ DBG(2, "<== static silicon ep config\n");
+
+ /* FIXME pick up ep0 maxpacket size */
+
+ for (epnum = 1; epnum < MUSB_C_NUM_EPS; epnum++) {
+ musb_ep_select(mbase, epnum);
+ hw_ep = musb->endpoints + epnum;
+
+ /* read from core using indexed model */
+ reg = musb_readb(hw_ep->regs, 0x10 + MUSB_FIFOSIZE);
+ if (!reg) {
+ /* 0's returned when no more endpoints */
+ break;
+ }
+ musb->nr_endpoints++;
+ musb->epmask |= (1 << epnum);
+
+ hw_ep->max_packet_sz_tx = 1 << (reg & 0x0f);
+
+ /* shared TX/RX FIFO? */
+ if ((reg & 0xf0) == 0xf0) {
+ hw_ep->max_packet_sz_rx = hw_ep->max_packet_sz_tx;
+ hw_ep->is_shared_fifo = true;
+ continue;
+ } else {
+ hw_ep->max_packet_sz_rx = 1 << ((reg & 0xf0) >> 4);
+ hw_ep->is_shared_fifo = false;
+ }
+
+ /* FIXME set up hw_ep->{rx,tx}_double_buffered */
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ /* pick an RX/TX endpoint for bulk */
+ if (hw_ep->max_packet_sz_tx < 512
+ || hw_ep->max_packet_sz_rx < 512)
+ continue;
+
+ /* REVISIT: this algorithm is lazy, we should at least
+ * try to pick a double buffered endpoint.
+ */
+ if (musb->bulk_ep)
+ continue;
+ musb->bulk_ep = hw_ep;
+#endif
+ }
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ if (!musb->bulk_ep) {
+ pr_debug("%s: missing bulk\n", musb_driver_name);
+ return -EINVAL;
+ }
+#endif
+
+ return 0;
+}
+
+enum { MUSB_CONTROLLER_MHDRC, MUSB_CONTROLLER_HDRC, };
+
+/* Initialize MUSB (M)HDRC part of the USB hardware subsystem;
+ * configure endpoints, or take their config from silicon
+ */
+static int __init musb_core_init(u16 musb_type, struct musb *musb)
+{
+#ifdef MUSB_AHB_ID
+ u32 data;
+#endif
+ u8 reg;
+ char *type;
+ u16 hwvers, rev_major, rev_minor;
+ char aInfo[78], aRevision[32], aDate[12];
+ void __iomem *mbase = musb->mregs;
+ int status = 0;
+ int i;
+
+ /* log core options (read using indexed model) */
+ musb_ep_select(mbase, 0);
+ reg = musb_readb(mbase, 0x10 + MUSB_CONFIGDATA);
+
+ strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8");
+ if (reg & MUSB_CONFIGDATA_DYNFIFO)
+ strcat(aInfo, ", dyn FIFOs");
+ if (reg & MUSB_CONFIGDATA_MPRXE) {
+ strcat(aInfo, ", bulk combine");
+#ifdef C_MP_RX
+ musb->bulk_combine = true;
+#else
+ strcat(aInfo, " (X)"); /* no driver support */
+#endif
+ }
+ if (reg & MUSB_CONFIGDATA_MPTXE) {
+ strcat(aInfo, ", bulk split");
+#ifdef C_MP_TX
+ musb->bulk_split = true;
+#else
+ strcat(aInfo, " (X)"); /* no driver support */
+#endif
+ }
+ if (reg & MUSB_CONFIGDATA_HBRXE) {
+ strcat(aInfo, ", HB-ISO Rx");
+ strcat(aInfo, " (X)"); /* no driver support */
+ }
+ if (reg & MUSB_CONFIGDATA_HBTXE) {
+ strcat(aInfo, ", HB-ISO Tx");
+ strcat(aInfo, " (X)"); /* no driver support */
+ }
+ if (reg & MUSB_CONFIGDATA_SOFTCONE)
+ strcat(aInfo, ", SoftConn");
+
+ printk(KERN_DEBUG "%s: ConfigData=0x%02x (%s)\n",
+ musb_driver_name, reg, aInfo);
+
+#ifdef MUSB_AHB_ID
+ data = musb_readl(mbase, 0x404);
+ sprintf(aDate, "%04d-%02x-%02x", (data & 0xffff),
+ (data >> 16) & 0xff, (data >> 24) & 0xff);
+ /* FIXME ID2 and ID3 are unused */
+ data = musb_readl(mbase, 0x408);
+ printk("ID2=%lx\n", (long unsigned)data);
+ data = musb_readl(mbase, 0x40c);
+ printk("ID3=%lx\n", (long unsigned)data);
+ reg = musb_readb(mbase, 0x400);
+ musb_type = ('M' == reg) ? MUSB_CONTROLLER_MHDRC : MUSB_CONTROLLER_HDRC;
+#else
+ aDate[0] = 0;
+#endif
+ if (MUSB_CONTROLLER_MHDRC == musb_type) {
+ musb->is_multipoint = 1;
+ type = "M";
+ } else {
+ musb->is_multipoint = 0;
+ type = "";
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+#ifndef CONFIG_USB_OTG_BLACKLIST_HUB
+ printk(KERN_ERR
+ "%s: kernel must blacklist external hubs\n",
+ musb_driver_name);
+#endif
+#endif
+ }
+
+ /* log release info */
+ hwvers = musb_readw(mbase, MUSB_HWVERS);
+ rev_major = (hwvers >> 10) & 0x1f;
+ rev_minor = hwvers & 0x3ff;
+ snprintf(aRevision, 32, "%d.%d%s", rev_major,
+ rev_minor, (hwvers & 0x8000) ? "RC" : "");
+ printk(KERN_DEBUG "%s: %sHDRC RTL version %s %s\n",
+ musb_driver_name, type, aRevision, aDate);
+
+ /* configure ep0 */
+ musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE;
+ musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE;
+
+ /* discover endpoint configuration */
+ musb->nr_endpoints = 1;
+ musb->epmask = 1;
+
+ if (reg & MUSB_CONFIGDATA_DYNFIFO) {
+ if (can_dynfifo())
+ status = ep_config_from_table(musb);
+ else {
+ ERR("reconfigure software for Dynamic FIFOs\n");
+ status = -ENODEV;
+ }
+ } else {
+ if (!can_dynfifo())
+ status = ep_config_from_hw(musb);
+ else {
+ ERR("reconfigure software for static FIFOs\n");
+ return -ENODEV;
+ }
+ }
+
+ if (status < 0)
+ return status;
+
+ /* finish init, and print endpoint config */
+ for (i = 0; i < musb->nr_endpoints; i++) {
+ struct musb_hw_ep *hw_ep = musb->endpoints + i;
+
+ hw_ep->fifo = MUSB_FIFO_OFFSET(i) + mbase;
+#ifdef CONFIG_USB_TUSB6010
+ hw_ep->fifo_async = musb->async + 0x400 + MUSB_FIFO_OFFSET(i);
+ hw_ep->fifo_sync = musb->sync + 0x400 + MUSB_FIFO_OFFSET(i);
+ hw_ep->fifo_sync_va =
+ musb->sync_va + 0x400 + MUSB_FIFO_OFFSET(i);
+
+ if (i == 0)
+ hw_ep->conf = mbase - 0x400 + TUSB_EP0_CONF;
+ else
+ hw_ep->conf = mbase + 0x400 + (((i - 1) & 0xf) << 2);
+#endif
+
+ hw_ep->regs = MUSB_EP_OFFSET(i, 0) + mbase;
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ hw_ep->target_regs = MUSB_BUSCTL_OFFSET(i, 0) + mbase;
+ hw_ep->rx_reinit = 1;
+ hw_ep->tx_reinit = 1;
+#endif
+
+ if (hw_ep->max_packet_sz_tx) {
+ printk(KERN_DEBUG
+ "%s: hw_ep %d%s, %smax %d\n",
+ musb_driver_name, i,
+ hw_ep->is_shared_fifo ? "shared" : "tx",
+ hw_ep->tx_double_buffered
+ ? "doublebuffer, " : "",
+ hw_ep->max_packet_sz_tx);
+ }
+ if (hw_ep->max_packet_sz_rx && !hw_ep->is_shared_fifo) {
+ printk(KERN_DEBUG
+ "%s: hw_ep %d%s, %smax %d\n",
+ musb_driver_name, i,
+ "rx",
+ hw_ep->rx_double_buffered
+ ? "doublebuffer, " : "",
+ hw_ep->max_packet_sz_rx);
+ }
+ if (!(hw_ep->max_packet_sz_tx || hw_ep->max_packet_sz_rx))
+ DBG(1, "hw_ep %d not configured\n", i);
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430)
+
+static irqreturn_t generic_interrupt(int irq, void *__hci)
+{
+ unsigned long flags;
+ irqreturn_t retval = IRQ_NONE;
+ struct musb *musb = __hci;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
+ musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
+ musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
+
+ if (musb->int_usb || musb->int_tx || musb->int_rx)
+ retval = musb_interrupt(musb);
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ /* REVISIT we sometimes get spurious IRQs on g_ep0
+ * not clear why...
+ */
+ if (retval != IRQ_HANDLED)
+ DBG(5, "spurious?\n");
+
+ return IRQ_HANDLED;
+}
+
+#else
+#define generic_interrupt NULL
+#endif
+
+/*
+ * handle all the irqs defined by the HDRC core. for now we expect: other
+ * irq sources (phy, dma, etc) will be handled first, musb->int_* values
+ * will be assigned, and the irq will already have been acked.
+ *
+ * called in irq context with spinlock held, irqs blocked
+ */
+irqreturn_t musb_interrupt(struct musb *musb)
+{
+ irqreturn_t retval = IRQ_NONE;
+ u8 devctl, power;
+ int ep_num;
+ u32 reg;
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ power = musb_readb(musb->mregs, MUSB_POWER);
+
+ DBG(4, "** IRQ %s usb%04x tx%04x rx%04x\n",
+ (devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral",
+ musb->int_usb, musb->int_tx, musb->int_rx);
+
+ /* the core can interrupt us for multiple reasons; docs have
+ * a generic interrupt flowchart to follow
+ */
+ if (musb->int_usb & STAGE0_MASK)
+ retval |= musb_stage0_irq(musb, musb->int_usb,
+ devctl, power);
+
+ /* "stage 1" is handling endpoint irqs */
+
+ /* handle endpoint 0 first */
+ if (musb->int_tx & 1) {
+ if (devctl & MUSB_DEVCTL_HM)
+ retval |= musb_h_ep0_irq(musb);
+ else
+ retval |= musb_g_ep0_irq(musb);
+ }
+
+ /* RX on endpoints 1-15 */
+ reg = musb->int_rx >> 1;
+ ep_num = 1;
+ while (reg) {
+ if (reg & 1) {
+ /* musb_ep_select(musb->mregs, ep_num); */
+ /* REVISIT just retval = ep->rx_irq(...) */
+ retval = IRQ_HANDLED;
+ if (devctl & MUSB_DEVCTL_HM) {
+ if (is_host_capable())
+ musb_host_rx(musb, ep_num);
+ } else {
+ if (is_peripheral_capable())
+ musb_g_rx(musb, ep_num);
+ }
+ }
+
+ reg >>= 1;
+ ep_num++;
+ }
+
+ /* TX on endpoints 1-15 */
+ reg = musb->int_tx >> 1;
+ ep_num = 1;
+ while (reg) {
+ if (reg & 1) {
+ /* musb_ep_select(musb->mregs, ep_num); */
+ /* REVISIT just retval |= ep->tx_irq(...) */
+ retval = IRQ_HANDLED;
+ if (devctl & MUSB_DEVCTL_HM) {
+ if (is_host_capable())
+ musb_host_tx(musb, ep_num);
+ } else {
+ if (is_peripheral_capable())
+ musb_g_tx(musb, ep_num);
+ }
+ }
+ reg >>= 1;
+ ep_num++;
+ }
+
+ /* finish handling "global" interrupts after handling fifos */
+ if (musb->int_usb)
+ retval |= musb_stage2_irq(musb,
+ musb->int_usb, devctl, power);
+
+ return retval;
+}
+
+
+#ifndef CONFIG_MUSB_PIO_ONLY
+static int __initdata use_dma = 1;
+
+/* "modprobe ... use_dma=0" etc */
+module_param(use_dma, bool, 0);
+MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
+
+void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
+{
+ u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ /* called with controller lock already held */
+
+ if (!epnum) {
+#ifndef CONFIG_USB_TUSB_OMAP_DMA
+ if (!is_cppi_enabled()) {
+ /* endpoint 0 */
+ if (devctl & MUSB_DEVCTL_HM)
+ musb_h_ep0_irq(musb);
+ else
+ musb_g_ep0_irq(musb);
+ }
+#endif
+ } else {
+ /* endpoints 1..15 */
+ if (transmit) {
+ if (devctl & MUSB_DEVCTL_HM) {
+ if (is_host_capable())
+ musb_host_tx(musb, epnum);
+ } else {
+ if (is_peripheral_capable())
+ musb_g_tx(musb, epnum);
+ }
+ } else {
+ /* receive */
+ if (devctl & MUSB_DEVCTL_HM) {
+ if (is_host_capable())
+ musb_host_rx(musb, epnum);
+ } else {
+ if (is_peripheral_capable())
+ musb_g_rx(musb, epnum);
+ }
+ }
+ }
+}
+
+#else
+#define use_dma 0
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_SYSFS
+
+static ssize_t
+musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct musb *musb = dev_to_musb(dev);
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&musb->lock, flags);
+ ret = sprintf(buf, "%s\n", otg_state_string(musb));
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ return ret;
+}
+
+static ssize_t
+musb_mode_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct musb *musb = dev_to_musb(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&musb->lock, flags);
+ if (!strncmp(buf, "host", 4))
+ musb_platform_set_mode(musb, MUSB_HOST);
+ if (!strncmp(buf, "peripheral", 10))
+ musb_platform_set_mode(musb, MUSB_PERIPHERAL);
+ if (!strncmp(buf, "otg", 3))
+ musb_platform_set_mode(musb, MUSB_OTG);
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ return n;
+}
+static DEVICE_ATTR(mode, 0644, musb_mode_show, musb_mode_store);
+
+static ssize_t
+musb_vbus_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct musb *musb = dev_to_musb(dev);
+ unsigned long flags;
+ unsigned long val;
+
+ if (sscanf(buf, "%lu", &val) < 1) {
+ printk(KERN_ERR "Invalid VBUS timeout ms value\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&musb->lock, flags);
+ musb->a_wait_bcon = val;
+ if (musb->xceiv.state == OTG_STATE_A_WAIT_BCON)
+ musb->is_active = 0;
+ musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val));
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ return n;
+}
+
+static ssize_t
+musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct musb *musb = dev_to_musb(dev);
+ unsigned long flags;
+ unsigned long val;
+ int vbus;
+
+ spin_lock_irqsave(&musb->lock, flags);
+ val = musb->a_wait_bcon;
+ vbus = musb_platform_get_vbus_status(musb);
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ return sprintf(buf, "Vbus %s, timeout %lu\n",
+ vbus ? "on" : "off", val);
+}
+static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store);
+
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+
+/* Gadget drivers can't know that a host is connected so they might want
+ * to start SRP, but users can. This allows userspace to trigger SRP.
+ */
+static ssize_t
+musb_srp_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct musb *musb = dev_to_musb(dev);
+ unsigned short srp;
+
+ if (sscanf(buf, "%hu", &srp) != 1
+ || (srp != 1)) {
+ printk(KERN_ERR "SRP: Value must be 1\n");
+ return -EINVAL;
+ }
+
+ if (srp == 1)
+ musb_g_wakeup(musb);
+
+ return n;
+}
+static DEVICE_ATTR(srp, 0644, NULL, musb_srp_store);
+
+#endif /* CONFIG_USB_GADGET_MUSB_HDRC */
+
+#endif /* sysfs */
+
+/* Only used to provide driver mode change events */
+static void musb_irq_work(struct work_struct *data)
+{
+ struct musb *musb = container_of(data, struct musb, irq_work);
+ static int old_state;
+
+ if (musb->xceiv.state != old_state) {
+ old_state = musb->xceiv.state;
+ sysfs_notify(&musb->controller->kobj, NULL, "mode");
+ }
+}
+
+/* --------------------------------------------------------------------------
+ * Init support
+ */
+
+static struct musb *__init
+allocate_instance(struct device *dev, void __iomem *mbase)
+{
+ struct musb *musb;
+ struct musb_hw_ep *ep;
+ int epnum;
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ struct usb_hcd *hcd;
+
+ hcd = usb_create_hcd(&musb_hc_driver, dev, dev->bus_id);
+ if (!hcd)
+ return NULL;
+ /* usbcore sets dev->driver_data to hcd, and sometimes uses that... */
+
+ musb = hcd_to_musb(hcd);
+ INIT_LIST_HEAD(&musb->control);
+ INIT_LIST_HEAD(&musb->in_bulk);
+ INIT_LIST_HEAD(&musb->out_bulk);
+
+ hcd->uses_new_polling = 1;
+
+ musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
+#else
+ musb = kzalloc(sizeof *musb, GFP_KERNEL);
+ if (!musb)
+ return NULL;
+ dev_set_drvdata(dev, musb);
+
+#endif
+
+ musb->mregs = mbase;
+ musb->ctrl_base = mbase;
+ musb->nIrq = -ENODEV;
+ for (epnum = 0, ep = musb->endpoints;
+ epnum < MUSB_C_NUM_EPS;
+ epnum++, ep++) {
+
+ ep->musb = musb;
+ ep->epnum = epnum;
+ }
+
+#ifdef CONFIG_USB_MUSB_OTG
+ otg_set_transceiver(&musb->xceiv);
+#endif
+ musb->controller = dev;
+ return musb;
+}
+
+static void musb_free(struct musb *musb)
+{
+ /* this has multiple entry modes. it handles fault cleanup after
+ * probe(), where things may be partially set up, as well as rmmod
+ * cleanup after everything's been de-activated.
+ */
+
+#ifdef CONFIG_SYSFS
+ device_remove_file(musb->controller, &dev_attr_mode);
+ device_remove_file(musb->controller, &dev_attr_vbus);
+#ifdef CONFIG_USB_MUSB_OTG
+ device_remove_file(musb->controller, &dev_attr_srp);
+#endif
+#endif
+
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ musb_gadget_cleanup(musb);
+#endif
+
+ if (musb->nIrq >= 0) {
+ disable_irq_wake(musb->nIrq);
+ free_irq(musb->nIrq, musb);
+ }
+ if (is_dma_capable() && musb->dma_controller) {
+ struct dma_controller *c = musb->dma_controller;
+
+ (void) c->stop(c);
+ dma_controller_destroy(c);
+ }
+
+ musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
+ musb_platform_exit(musb);
+ musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
+
+ if (musb->clock) {
+ clk_disable(musb->clock);
+ clk_put(musb->clock);
+ }
+
+#ifdef CONFIG_USB_MUSB_OTG
+ put_device(musb->xceiv.dev);
+#endif
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ usb_put_hcd(musb_to_hcd(musb));
+#else
+ kfree(musb);
+#endif
+}
+
+/*
+ * Perform generic per-controller initialization.
+ *
+ * @pDevice: the controller (already clocked, etc)
+ * @nIrq: irq
+ * @mregs: virtual address of controller registers,
+ * not yet corrected for platform-specific offsets
+ */
+static int __init
+musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
+{
+ int status;
+ struct musb *musb;
+ struct musb_hdrc_platform_data *plat = dev->platform_data;
+
+ /* The driver might handle more features than the board; OK.
+ * Fail when the board needs a feature that's not enabled.
+ */
+ if (!plat) {
+ dev_dbg(dev, "no platform_data?\n");
+ return -ENODEV;
+ }
+ switch (plat->mode) {
+ case MUSB_HOST:
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ break;
+#else
+ goto bad_config;
+#endif
+ case MUSB_PERIPHERAL:
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ break;
+#else
+ goto bad_config;
+#endif
+ case MUSB_OTG:
+#ifdef CONFIG_USB_MUSB_OTG
+ break;
+#else
+bad_config:
+#endif
+ default:
+ dev_err(dev, "incompatible Kconfig role setting\n");
+ return -EINVAL;
+ }
+
+ /* allocate */
+ musb = allocate_instance(dev, ctrl);
+ if (!musb)
+ return -ENOMEM;
+
+ spin_lock_init(&musb->lock);
+ musb->board_mode = plat->mode;
+ musb->board_set_power = plat->set_power;
+ musb->set_clock = plat->set_clock;
+ musb->min_power = plat->min_power;
+
+ /* Clock usage is chip-specific ... functional clock (DaVinci,
+ * OMAP2430), or PHY ref (some TUSB6010 boards). All this core
+ * code does is make sure a clock handle is available; platform
+ * code manages it during start/stop and suspend/resume.
+ */
+ if (plat->clock) {
+ musb->clock = clk_get(dev, plat->clock);
+ if (IS_ERR(musb->clock)) {
+ status = PTR_ERR(musb->clock);
+ musb->clock = NULL;
+ goto fail;
+ }
+ }
+
+ /* assume vbus is off */
+
+ /* platform adjusts musb->mregs and musb->isr if needed,
+ * and activates clocks
+ */
+ musb->isr = generic_interrupt;
+ status = musb_platform_init(musb);
+
+ if (status < 0)
+ goto fail;
+ if (!musb->isr) {
+ status = -ENODEV;
+ goto fail2;
+ }
+
+#ifndef CONFIG_MUSB_PIO_ONLY
+ if (use_dma && dev->dma_mask) {
+ struct dma_controller *c;
+
+ c = dma_controller_create(musb, musb->mregs);
+ musb->dma_controller = c;
+ if (c)
+ (void) c->start(c);
+ }
+#endif
+ /* ideally this would be abstracted in platform setup */
+ if (!is_dma_capable() || !musb->dma_controller)
+ dev->dma_mask = NULL;
+
+ /* be sure interrupts are disabled before connecting ISR */
+ musb_platform_disable(musb);
+ musb_generic_disable(musb);
+
+ /* setup musb parts of the core (especially endpoints) */
+ status = musb_core_init(plat->multipoint
+ ? MUSB_CONTROLLER_MHDRC
+ : MUSB_CONTROLLER_HDRC, musb);
+ if (status < 0)
+ goto fail2;
+
+ /* Init IRQ workqueue before request_irq */
+ INIT_WORK(&musb->irq_work, musb_irq_work);
+
+ /* attach to the IRQ */
+ if (request_irq(nIrq, musb->isr, 0, dev->bus_id, musb)) {
+ dev_err(dev, "request_irq %d failed!\n", nIrq);
+ status = -ENODEV;
+ goto fail2;
+ }
+ musb->nIrq = nIrq;
+/* FIXME this handles wakeup irqs wrong */
+ if (enable_irq_wake(nIrq) == 0)
+ device_init_wakeup(dev, 1);
+
+ pr_info("%s: USB %s mode controller at %p using %s, IRQ %d\n",
+ musb_driver_name,
+ ({char *s;
+ switch (musb->board_mode) {
+ case MUSB_HOST: s = "Host"; break;
+ case MUSB_PERIPHERAL: s = "Peripheral"; break;
+ default: s = "OTG"; break;
+ }; s; }),
+ ctrl,
+ (is_dma_capable() && musb->dma_controller)
+ ? "DMA" : "PIO",
+ musb->nIrq);
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ /* host side needs more setup, except for no-host modes */
+ if (musb->board_mode != MUSB_PERIPHERAL) {
+ struct usb_hcd *hcd = musb_to_hcd(musb);
+
+ if (musb->board_mode == MUSB_OTG)
+ hcd->self.otg_port = 1;
+ musb->xceiv.host = &hcd->self;
+ hcd->power_budget = 2 * (plat->power ? : 250);
+ }
+#endif /* CONFIG_USB_MUSB_HDRC_HCD */
+
+ /* For the host-only role, we can activate right away.
+ * (We expect the ID pin to be forcibly grounded!!)
+ * Otherwise, wait till the gadget driver hooks up.
+ */
+ if (!is_otg_enabled(musb) && is_host_enabled(musb)) {
+ MUSB_HST_MODE(musb);
+ musb->xceiv.default_a = 1;
+ musb->xceiv.state = OTG_STATE_A_IDLE;
+
+ status = usb_add_hcd(musb_to_hcd(musb), -1, 0);
+
+ DBG(1, "%s mode, status %d, devctl %02x %c\n",
+ "HOST", status,
+ musb_readb(musb->mregs, MUSB_DEVCTL),
+ (musb_readb(musb->mregs, MUSB_DEVCTL)
+ & MUSB_DEVCTL_BDEVICE
+ ? 'B' : 'A'));
+
+ } else /* peripheral is enabled */ {
+ MUSB_DEV_MODE(musb);
+ musb->xceiv.default_a = 0;
+ musb->xceiv.state = OTG_STATE_B_IDLE;
+
+ status = musb_gadget_setup(musb);
+
+ DBG(1, "%s mode, status %d, dev%02x\n",
+ is_otg_enabled(musb) ? "OTG" : "PERIPHERAL",
+ status,
+ musb_readb(musb->mregs, MUSB_DEVCTL));
+
+ }
+
+ if (status == 0)
+ musb_debug_create("driver/musb_hdrc", musb);
+ else {
+fail:
+ if (musb->clock)
+ clk_put(musb->clock);
+ device_init_wakeup(dev, 0);
+ musb_free(musb);
+ return status;
+ }
+
+#ifdef CONFIG_SYSFS
+ status = device_create_file(dev, &dev_attr_mode);
+ status = device_create_file(dev, &dev_attr_vbus);
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ status = device_create_file(dev, &dev_attr_srp);
+#endif /* CONFIG_USB_GADGET_MUSB_HDRC */
+ status = 0;
+#endif
+
+ return status;
+
+fail2:
+ musb_platform_exit(musb);
+ goto fail;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* all implementations (PCI bridge to FPGA, VLYNQ, etc) should just
+ * bridge to a platform device; this driver then suffices.
+ */
+
+#ifndef CONFIG_MUSB_PIO_ONLY
+static u64 *orig_dma_mask;
+#endif
+
+static int __init musb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int irq = platform_get_irq(pdev, 0);
+ struct resource *iomem;
+ void __iomem *base;
+
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iomem || irq == 0)
+ return -ENODEV;
+
+ base = ioremap(iomem->start, iomem->end - iomem->start + 1);
+ if (!base) {
+ dev_err(dev, "ioremap failed\n");
+ return -ENOMEM;
+ }
+
+#ifndef CONFIG_MUSB_PIO_ONLY
+ /* clobbered by use_dma=n */
+ orig_dma_mask = dev->dma_mask;
+#endif
+ return musb_init_controller(dev, irq, base);
+}
+
+static int __devexit musb_remove(struct platform_device *pdev)
+{
+ struct musb *musb = dev_to_musb(&pdev->dev);
+ void __iomem *ctrl_base = musb->ctrl_base;
+
+ /* this gets called on rmmod.
+ * - Host mode: host may still be active
+ * - Peripheral mode: peripheral is deactivated (or never-activated)
+ * - OTG mode: both roles are deactivated (or never-activated)
+ */
+ musb_shutdown(pdev);
+ musb_debug_delete("driver/musb_hdrc", musb);
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ if (musb->board_mode == MUSB_HOST)
+ usb_remove_hcd(musb_to_hcd(musb));
+#endif
+ musb_free(musb);
+ iounmap(ctrl_base);
+ device_init_wakeup(&pdev->dev, 0);
+#ifndef CONFIG_MUSB_PIO_ONLY
+ pdev->dev.dma_mask = orig_dma_mask;
+#endif
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int musb_suspend(struct platform_device *pdev, pm_message_t message)
+{
+ unsigned long flags;
+ struct musb *musb = dev_to_musb(&pdev->dev);
+
+ if (!musb->clock)
+ return 0;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ if (is_peripheral_active(musb)) {
+ /* FIXME force disconnect unless we know USB will wake
+ * the system up quickly enough to respond ...
+ */
+ } else if (is_host_active(musb)) {
+ /* we know all the children are suspended; sometimes
+ * they will even be wakeup-enabled.
+ */
+ }
+
+ if (musb->set_clock)
+ musb->set_clock(musb->clock, 0);
+ else
+ clk_disable(musb->clock);
+ spin_unlock_irqrestore(&musb->lock, flags);
+ return 0;
+}
+
+static int musb_resume(struct platform_device *pdev)
+{
+ unsigned long flags;
+ struct musb *musb = dev_to_musb(&pdev->dev);
+
+ if (!musb->clock)
+ return 0;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ if (musb->set_clock)
+ musb->set_clock(musb->clock, 1);
+ else
+ clk_enable(musb->clock);
+
+ /* for static cmos like DaVinci, register values were preserved
+ * unless for some reason the whole soc powered down and we're
+ * not treating that as a whole-system restart (e.g. swsusp)
+ */
+ spin_unlock_irqrestore(&musb->lock, flags);
+ return 0;
+}
+
+#else
+#define musb_suspend NULL
+#define musb_resume NULL
+#endif
+
+static struct platform_driver musb_driver = {
+ .driver = {
+ .name = (char *)musb_driver_name,
+ .bus = &platform_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(musb_remove),
+ .shutdown = musb_shutdown,
+ .suspend = musb_suspend,
+ .resume = musb_resume,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init musb_init(void)
+{
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ if (usb_disabled())
+ return 0;
+#endif
+
+ pr_info("%s: version " MUSB_VERSION ", "
+#ifdef CONFIG_MUSB_PIO_ONLY
+ "pio"
+#elif defined(CONFIG_USB_TI_CPPI_DMA)
+ "cppi-dma"
+#elif defined(CONFIG_USB_INVENTRA_DMA)
+ "musb-dma"
+#elif defined(CONFIG_USB_TUSB_OMAP_DMA)
+ "tusb-omap-dma"
+#else
+ "?dma?"
+#endif
+ ", "
+#ifdef CONFIG_USB_MUSB_OTG
+ "otg (peripheral+host)"
+#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
+ "peripheral"
+#elif defined(CONFIG_USB_MUSB_HDRC_HCD)
+ "host"
+#endif
+ ", debug=%d\n",
+ musb_driver_name, debug);
+ return platform_driver_probe(&musb_driver, musb_probe);
+}
+
+/* make us init after usbcore and before usb
+ * gadget and host-side drivers start to register
+ */
+subsys_initcall(musb_init);
+
+static void __exit musb_cleanup(void)
+{
+ platform_driver_unregister(&musb_driver);
+}
+module_exit(musb_cleanup);
--- /dev/null
- #include <asm/arch/hdrc_cnf.h>
+/*
+ * MUSB OTG driver defines
+ *
+ * Copyright 2005 Mentor Graphics Corporation
+ * Copyright (C) 2005-2006 by Texas Instruments
+ * Copyright (C) 2006-2007 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __MUSB_CORE_H__
+#define __MUSB_CORE_H__
+
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/musb.h>
+
+struct musb;
+struct musb_hw_ep;
+struct musb_ep;
+
+
+#include "musb_debug.h"
+#include "musb_dma.h"
+
+#ifdef CONFIG_USB_MUSB_SOC
+/*
+ * Get core configuration from a header converted (by cfg_conv)
+ * from the Verilog config file generated by the core config utility
+ *
+ * For now we assume that header is provided along with other
+ * arch-specific files. Discrete chips will need a build tweak.
+ * So will using AHB IDs from silicon that provides them.
+ */
++#include <mach/hdrc_cnf.h>
+#endif
+
+#include "musb_io.h"
+#include "musb_regs.h"
+
+#include "musb_gadget.h"
+#include "../core/hcd.h"
+#include "musb_host.h"
+
+
+
+#ifdef CONFIG_USB_MUSB_OTG
+
+#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST)
+#define is_host_enabled(musb) ((musb)->board_mode != MUSB_PERIPHERAL)
+#define is_otg_enabled(musb) ((musb)->board_mode == MUSB_OTG)
+
+/* NOTE: otg and peripheral-only state machines start at B_IDLE.
+ * OTG or host-only go to A_IDLE when ID is sensed.
+ */
+#define is_peripheral_active(m) (!(m)->is_host)
+#define is_host_active(m) ((m)->is_host)
+
+#else
+#define is_peripheral_enabled(musb) is_peripheral_capable()
+#define is_host_enabled(musb) is_host_capable()
+#define is_otg_enabled(musb) 0
+
+#define is_peripheral_active(musb) is_peripheral_capable()
+#define is_host_active(musb) is_host_capable()
+#endif
+
+#if defined(CONFIG_USB_MUSB_OTG) || defined(CONFIG_USB_MUSB_PERIPHERAL)
+/* for some reason, the "select USB_GADGET_MUSB_HDRC" doesn't always
+ * override that choice selection (often USB_GADGET_DUMMY_HCD).
+ */
+#ifndef CONFIG_USB_GADGET_MUSB_HDRC
+#error bogus Kconfig output ... select CONFIG_USB_GADGET_MUSB_HDRC
+#endif
+#endif /* need MUSB gadget selection */
+
+
+#ifdef CONFIG_PROC_FS
+#include <linux/fs.h>
+#define MUSB_CONFIG_PROC_FS
+#endif
+
+/****************************** PERIPHERAL ROLE *****************************/
+
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+
+#define is_peripheral_capable() (1)
+
+extern irqreturn_t musb_g_ep0_irq(struct musb *);
+extern void musb_g_tx(struct musb *, u8);
+extern void musb_g_rx(struct musb *, u8);
+extern void musb_g_reset(struct musb *);
+extern void musb_g_suspend(struct musb *);
+extern void musb_g_resume(struct musb *);
+extern void musb_g_wakeup(struct musb *);
+extern void musb_g_disconnect(struct musb *);
+
+#else
+
+#define is_peripheral_capable() (0)
+
+static inline irqreturn_t musb_g_ep0_irq(struct musb *m) { return IRQ_NONE; }
+static inline void musb_g_reset(struct musb *m) {}
+static inline void musb_g_suspend(struct musb *m) {}
+static inline void musb_g_resume(struct musb *m) {}
+static inline void musb_g_wakeup(struct musb *m) {}
+static inline void musb_g_disconnect(struct musb *m) {}
+
+#endif
+
+/****************************** HOST ROLE ***********************************/
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+
+#define is_host_capable() (1)
+
+extern irqreturn_t musb_h_ep0_irq(struct musb *);
+extern void musb_host_tx(struct musb *, u8);
+extern void musb_host_rx(struct musb *, u8);
+
+#else
+
+#define is_host_capable() (0)
+
+static inline irqreturn_t musb_h_ep0_irq(struct musb *m) { return IRQ_NONE; }
+static inline void musb_host_tx(struct musb *m, u8 e) {}
+static inline void musb_host_rx(struct musb *m, u8 e) {}
+
+#endif
+
+
+/****************************** CONSTANTS ********************************/
+
+#ifndef MUSB_C_NUM_EPS
+#define MUSB_C_NUM_EPS ((u8)16)
+#endif
+
+#ifndef MUSB_MAX_END0_PACKET
+#define MUSB_MAX_END0_PACKET ((u16)MUSB_EP0_FIFOSIZE)
+#endif
+
+/* host side ep0 states */
+enum musb_h_ep0_state {
+ MUSB_EP0_IDLE,
+ MUSB_EP0_START, /* expect ack of setup */
+ MUSB_EP0_IN, /* expect IN DATA */
+ MUSB_EP0_OUT, /* expect ack of OUT DATA */
+ MUSB_EP0_STATUS, /* expect ack of STATUS */
+} __attribute__ ((packed));
+
+/* peripheral side ep0 states */
+enum musb_g_ep0_state {
+ MUSB_EP0_STAGE_SETUP, /* idle, waiting for setup */
+ MUSB_EP0_STAGE_TX, /* IN data */
+ MUSB_EP0_STAGE_RX, /* OUT data */
+ MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */
+ MUSB_EP0_STAGE_STATUSOUT, /* (after IN data) */
+ MUSB_EP0_STAGE_ACKWAIT, /* after zlp, before statusin */
+} __attribute__ ((packed));
+
+/* OTG protocol constants */
+#define OTG_TIME_A_WAIT_VRISE 100 /* msec (max) */
+#define OTG_TIME_A_WAIT_BCON 0 /* 0=infinite; min 1000 msec */
+#define OTG_TIME_A_IDLE_BDIS 200 /* msec (min) */
+
+/*************************** REGISTER ACCESS ********************************/
+
+/* Endpoint registers (other than dynfifo setup) can be accessed either
+ * directly with the "flat" model, or after setting up an index register.
+ */
+
+#if defined(CONFIG_ARCH_DAVINCI) || defined(CONFIG_ARCH_OMAP2430) \
+ || defined(CONFIG_ARCH_OMAP3430)
+/* REVISIT indexed access seemed to
+ * misbehave (on DaVinci) for at least peripheral IN ...
+ */
+#define MUSB_FLAT_REG
+#endif
+
+/* TUSB mapping: "flat" plus ep0 special cases */
+#if defined(CONFIG_USB_TUSB6010)
+#define musb_ep_select(_mbase, _epnum) \
+ musb_writeb((_mbase), MUSB_INDEX, (_epnum))
+#define MUSB_EP_OFFSET MUSB_TUSB_OFFSET
+
+/* "flat" mapping: each endpoint has its own i/o address */
+#elif defined(MUSB_FLAT_REG)
+#define musb_ep_select(_mbase, _epnum) (((void)(_mbase)), ((void)(_epnum)))
+#define MUSB_EP_OFFSET MUSB_FLAT_OFFSET
+
+/* "indexed" mapping: INDEX register controls register bank select */
+#else
+#define musb_ep_select(_mbase, _epnum) \
+ musb_writeb((_mbase), MUSB_INDEX, (_epnum))
+#define MUSB_EP_OFFSET MUSB_INDEXED_OFFSET
+#endif
+
+/****************************** FUNCTIONS ********************************/
+
+#define MUSB_HST_MODE(_musb)\
+ { (_musb)->is_host = true; }
+#define MUSB_DEV_MODE(_musb) \
+ { (_musb)->is_host = false; }
+
+#define test_devctl_hst_mode(_x) \
+ (musb_readb((_x)->mregs, MUSB_DEVCTL)&MUSB_DEVCTL_HM)
+
+#define MUSB_MODE(musb) ((musb)->is_host ? "Host" : "Peripheral")
+
+/******************************** TYPES *************************************/
+
+/*
+ * struct musb_hw_ep - endpoint hardware (bidirectional)
+ *
+ * Ordered slightly for better cacheline locality.
+ */
+struct musb_hw_ep {
+ struct musb *musb;
+ void __iomem *fifo;
+ void __iomem *regs;
+
+#ifdef CONFIG_USB_TUSB6010
+ void __iomem *conf;
+#endif
+
+ /* index in musb->endpoints[] */
+ u8 epnum;
+
+ /* hardware configuration, possibly dynamic */
+ bool is_shared_fifo;
+ bool tx_double_buffered;
+ bool rx_double_buffered;
+ u16 max_packet_sz_tx;
+ u16 max_packet_sz_rx;
+
+ struct dma_channel *tx_channel;
+ struct dma_channel *rx_channel;
+
+#ifdef CONFIG_USB_TUSB6010
+ /* TUSB has "asynchronous" and "synchronous" dma modes */
+ dma_addr_t fifo_async;
+ dma_addr_t fifo_sync;
+ void __iomem *fifo_sync_va;
+#endif
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ void __iomem *target_regs;
+
+ /* currently scheduled peripheral endpoint */
+ struct musb_qh *in_qh;
+ struct musb_qh *out_qh;
+
+ u8 rx_reinit;
+ u8 tx_reinit;
+#endif
+
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ /* peripheral side */
+ struct musb_ep ep_in; /* TX */
+ struct musb_ep ep_out; /* RX */
+#endif
+};
+
+static inline struct usb_request *next_in_request(struct musb_hw_ep *hw_ep)
+{
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ return next_request(&hw_ep->ep_in);
+#else
+ return NULL;
+#endif
+}
+
+static inline struct usb_request *next_out_request(struct musb_hw_ep *hw_ep)
+{
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ return next_request(&hw_ep->ep_out);
+#else
+ return NULL;
+#endif
+}
+
+/*
+ * struct musb - Driver instance data.
+ */
+struct musb {
+ spinlock_t lock;
+ struct clk *clock;
+ irqreturn_t (*isr)(int, void *);
+ struct work_struct irq_work;
+
+/* this hub status bit is reserved by USB 2.0 and not seen by usbcore */
+#define MUSB_PORT_STAT_RESUME (1 << 31)
+
+ u32 port1_status;
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ unsigned long rh_timer;
+
+ enum musb_h_ep0_state ep0_stage;
+
+ /* bulk traffic normally dedicates endpoint hardware, and each
+ * direction has its own ring of host side endpoints.
+ * we try to progress the transfer at the head of each endpoint's
+ * queue until it completes or NAKs too much; then we try the next
+ * endpoint.
+ */
+ struct musb_hw_ep *bulk_ep;
+
+ struct list_head control; /* of musb_qh */
+ struct list_head in_bulk; /* of musb_qh */
+ struct list_head out_bulk; /* of musb_qh */
+ struct musb_qh *periodic[32]; /* tree of interrupt+iso */
+#endif
+
+ /* called with IRQs blocked; ON/nonzero implies starting a session,
+ * and waiting at least a_wait_vrise_tmout.
+ */
+ void (*board_set_vbus)(struct musb *, int is_on);
+
+ struct dma_controller *dma_controller;
+
+ struct device *controller;
+ void __iomem *ctrl_base;
+ void __iomem *mregs;
+
+#ifdef CONFIG_USB_TUSB6010
+ dma_addr_t async;
+ dma_addr_t sync;
+ void __iomem *sync_va;
+#endif
+
+ /* passed down from chip/board specific irq handlers */
+ u8 int_usb;
+ u16 int_rx;
+ u16 int_tx;
+
+ struct otg_transceiver xceiv;
+
+ int nIrq;
+
+ struct musb_hw_ep endpoints[MUSB_C_NUM_EPS];
+#define control_ep endpoints
+
+#define VBUSERR_RETRY_COUNT 3
+ u16 vbuserr_retry;
+ u16 epmask;
+ u8 nr_endpoints;
+
+ u8 board_mode; /* enum musb_mode */
+ int (*board_set_power)(int state);
+
+ int (*set_clock)(struct clk *clk, int is_active);
+
+ u8 min_power; /* vbus for periph, in mA/2 */
+
+ bool is_host;
+
+ int a_wait_bcon; /* VBUS timeout in msecs */
+ unsigned long idle_timeout; /* Next timeout in jiffies */
+
+ /* active means connected and not suspended */
+ unsigned is_active:1;
+
+ unsigned is_multipoint:1;
+ unsigned ignore_disconnect:1; /* during bus resets */
+
+#ifdef C_MP_TX
+ unsigned bulk_split:1;
+#define can_bulk_split(musb,type) \
+ (((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_split)
+#else
+#define can_bulk_split(musb, type) 0
+#endif
+
+#ifdef C_MP_RX
+ unsigned bulk_combine:1;
+#define can_bulk_combine(musb,type) \
+ (((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_combine)
+#else
+#define can_bulk_combine(musb, type) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ /* is_suspended means USB B_PERIPHERAL suspend */
+ unsigned is_suspended:1;
+
+ /* may_wakeup means remote wakeup is enabled */
+ unsigned may_wakeup:1;
+
+ /* is_self_powered is reported in device status and the
+ * config descriptor. is_bus_powered means B_PERIPHERAL
+ * draws some VBUS current; both can be true.
+ */
+ unsigned is_self_powered:1;
+ unsigned is_bus_powered:1;
+
+ unsigned set_address:1;
+ unsigned test_mode:1;
+ unsigned softconnect:1;
+
+ u8 address;
+ u8 test_mode_nr;
+ u16 ackpend; /* ep0 */
+ enum musb_g_ep0_state ep0_state;
+ struct usb_gadget g; /* the gadget */
+ struct usb_gadget_driver *gadget_driver; /* its driver */
+#endif
+
+#ifdef MUSB_CONFIG_PROC_FS
+ struct proc_dir_entry *proc_entry;
+#endif
+};
+
+static inline void musb_set_vbus(struct musb *musb, int is_on)
+{
+ musb->board_set_vbus(musb, is_on);
+}
+
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+static inline struct musb *gadget_to_musb(struct usb_gadget *g)
+{
+ return container_of(g, struct musb, g);
+}
+#endif
+
+
+/***************************** Glue it together *****************************/
+
+extern const char musb_driver_name[];
+
+extern void musb_start(struct musb *musb);
+extern void musb_stop(struct musb *musb);
+
+extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src);
+extern void musb_read_fifo(struct musb_hw_ep *ep, u16 len, u8 *dst);
+
+extern void musb_load_testpacket(struct musb *);
+
+extern irqreturn_t musb_interrupt(struct musb *);
+
+extern void musb_platform_enable(struct musb *musb);
+extern void musb_platform_disable(struct musb *musb);
+
+extern void musb_hnp_stop(struct musb *musb);
+
+extern void musb_platform_set_mode(struct musb *musb, u8 musb_mode);
+
+#if defined(CONFIG_USB_TUSB6010) || \
+ defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX)
+extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout);
+#else
+#define musb_platform_try_idle(x, y) do {} while (0)
+#endif
+
+#ifdef CONFIG_USB_TUSB6010
+extern int musb_platform_get_vbus_status(struct musb *musb);
+#else
+#define musb_platform_get_vbus_status(x) 0
+#endif
+
+extern int __init musb_platform_init(struct musb *musb);
+extern int musb_platform_exit(struct musb *musb);
+
+/*-------------------------- ProcFS definitions ---------------------*/
+
+struct proc_dir_entry;
+
+#if (MUSB_DEBUG > 0) && defined(MUSB_CONFIG_PROC_FS)
+extern struct proc_dir_entry *musb_debug_create(char *name, struct musb *data);
+extern void musb_debug_delete(char *name, struct musb *data);
+
+#else
+static inline struct proc_dir_entry *
+musb_debug_create(char *name, struct musb *data)
+{
+ return NULL;
+}
+static inline void musb_debug_delete(char *name, struct musb *data)
+{
+}
+#endif
+
+#endif /* __MUSB_CORE_H__ */
--- /dev/null
- #include <asm/arch/hardware.h>
+/*
+ * MUSB OTG driver debug support
+ *
+ * Copyright 2005 Mentor Graphics Corporation
+ * Copyright (C) 2005-2006 by Texas Instruments
+ * Copyright (C) 2006-2007 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h> /* FIXME remove procfs writes */
++#include <mach/hardware.h>
+
+#include "musb_core.h"
+
+#include "davinci.h"
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+
+static int dump_qh(struct musb_qh *qh, char *buf, unsigned max)
+{
+ int count;
+ int tmp;
+ struct usb_host_endpoint *hep = qh->hep;
+ struct urb *urb;
+
+ count = snprintf(buf, max, " qh %p dev%d ep%d%s max%d\n",
+ qh, qh->dev->devnum, qh->epnum,
+ ({ char *s; switch (qh->type) {
+ case USB_ENDPOINT_XFER_BULK:
+ s = "-bulk"; break;
+ case USB_ENDPOINT_XFER_INT:
+ s = "-int"; break;
+ case USB_ENDPOINT_XFER_CONTROL:
+ s = ""; break;
+ default:
+ s = "iso"; break;
+ }; s; }),
+ qh->maxpacket);
+ if (count <= 0)
+ return 0;
+ buf += count;
+ max -= count;
+
+ list_for_each_entry(urb, &hep->urb_list, urb_list) {
+ tmp = snprintf(buf, max, "\t%s urb %p %d/%d\n",
+ usb_pipein(urb->pipe) ? "in" : "out",
+ urb, urb->actual_length,
+ urb->transfer_buffer_length);
+ if (tmp <= 0)
+ break;
+ tmp = min(tmp, (int)max);
+ count += tmp;
+ buf += tmp;
+ max -= tmp;
+ }
+ return count;
+}
+
+static int
+dump_queue(struct list_head *q, char *buf, unsigned max)
+{
+ int count = 0;
+ struct musb_qh *qh;
+
+ list_for_each_entry(qh, q, ring) {
+ int tmp;
+
+ tmp = dump_qh(qh, buf, max);
+ if (tmp <= 0)
+ break;
+ tmp = min(tmp, (int)max);
+ count += tmp;
+ buf += tmp;
+ max -= tmp;
+ }
+ return count;
+}
+
+#endif /* HCD */
+
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+static int dump_ep(struct musb_ep *ep, char *buffer, unsigned max)
+{
+ char *buf = buffer;
+ int code = 0;
+ void __iomem *regs = ep->hw_ep->regs;
+ char *mode = "1buf";
+
+ if (ep->is_in) {
+ if (ep->hw_ep->tx_double_buffered)
+ mode = "2buf";
+ } else {
+ if (ep->hw_ep->rx_double_buffered)
+ mode = "2buf";
+ }
+
+ do {
+ struct usb_request *req;
+
+ code = snprintf(buf, max,
+ "\n%s (hw%d): %s%s, csr %04x maxp %04x\n",
+ ep->name, ep->current_epnum,
+ mode, ep->dma ? " dma" : "",
+ musb_readw(regs,
+ (ep->is_in || !ep->current_epnum)
+ ? MUSB_TXCSR
+ : MUSB_RXCSR),
+ musb_readw(regs, ep->is_in
+ ? MUSB_TXMAXP
+ : MUSB_RXMAXP)
+ );
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+
+ if (is_cppi_enabled() && ep->current_epnum) {
+ unsigned cppi = ep->current_epnum - 1;
+ void __iomem *base = ep->musb->ctrl_base;
+ unsigned off1 = cppi << 2;
+ void __iomem *ram = base;
+ char tmp[16];
+
+ if (ep->is_in) {
+ ram += DAVINCI_TXCPPI_STATERAM_OFFSET(cppi);
+ tmp[0] = 0;
+ } else {
+ ram += DAVINCI_RXCPPI_STATERAM_OFFSET(cppi);
+ snprintf(tmp, sizeof tmp, "%d left, ",
+ musb_readl(base,
+ DAVINCI_RXCPPI_BUFCNT0_REG + off1));
+ }
+
+ code = snprintf(buf, max, "%cX DMA%d: %s"
+ "%08x %08x, %08x %08x; "
+ "%08x %08x %08x .. %08x\n",
+ ep->is_in ? 'T' : 'R',
+ ep->current_epnum - 1, tmp,
+ musb_readl(ram, 0 * 4),
+ musb_readl(ram, 1 * 4),
+ musb_readl(ram, 2 * 4),
+ musb_readl(ram, 3 * 4),
+ musb_readl(ram, 4 * 4),
+ musb_readl(ram, 5 * 4),
+ musb_readl(ram, 6 * 4),
+ musb_readl(ram, 7 * 4));
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+ }
+
+ if (list_empty(&ep->req_list)) {
+ code = snprintf(buf, max, "\t(queue empty)\n");
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+ break;
+ }
+ list_for_each_entry(req, &ep->req_list, list) {
+ code = snprintf(buf, max, "\treq %p, %s%s%d/%d\n",
+ req,
+ req->zero ? "zero, " : "",
+ req->short_not_ok ? "!short, " : "",
+ req->actual, req->length);
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+ }
+ } while (0);
+ return buf - buffer;
+}
+#endif
+
+static int
+dump_end_info(struct musb *musb, u8 epnum, char *aBuffer, unsigned max)
+{
+ int code = 0;
+ char *buf = aBuffer;
+ struct musb_hw_ep *hw_ep = &musb->endpoints[epnum];
+
+ do {
+ musb_ep_select(musb->mregs, epnum);
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ if (is_host_active(musb)) {
+ int dump_rx, dump_tx;
+ void __iomem *regs = hw_ep->regs;
+
+ /* TEMPORARY (!) until we have a real periodic
+ * schedule tree ...
+ */
+ if (!epnum) {
+ /* control is shared, uses RX queue
+ * but (mostly) shadowed tx registers
+ */
+ dump_tx = !list_empty(&musb->control);
+ dump_rx = 0;
+ } else if (hw_ep == musb->bulk_ep) {
+ dump_tx = !list_empty(&musb->out_bulk);
+ dump_rx = !list_empty(&musb->in_bulk);
+ } else if (musb->periodic[epnum]) {
+ struct usb_host_endpoint *hep;
+
+ hep = musb->periodic[epnum]->hep;
+ dump_rx = hep->desc.bEndpointAddress
+ & USB_ENDPOINT_DIR_MASK;
+ dump_tx = !dump_rx;
+ } else
+ break;
+ /* END TEMPORARY */
+
+
+ if (dump_rx) {
+ code = snprintf(buf, max,
+ "\nRX%d: %s rxcsr %04x interval %02x "
+ "max %04x type %02x; "
+ "dev %d hub %d port %d"
+ "\n",
+ epnum,
+ hw_ep->rx_double_buffered
+ ? "2buf" : "1buf",
+ musb_readw(regs, MUSB_RXCSR),
+ musb_readb(regs, MUSB_RXINTERVAL),
+ musb_readw(regs, MUSB_RXMAXP),
+ musb_readb(regs, MUSB_RXTYPE),
+ /* FIXME: assumes multipoint */
+ musb_readb(musb->mregs,
+ MUSB_BUSCTL_OFFSET(epnum,
+ MUSB_RXFUNCADDR)),
+ musb_readb(musb->mregs,
+ MUSB_BUSCTL_OFFSET(epnum,
+ MUSB_RXHUBADDR)),
+ musb_readb(musb->mregs,
+ MUSB_BUSCTL_OFFSET(epnum,
+ MUSB_RXHUBPORT))
+ );
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+
+ if (is_cppi_enabled()
+ && epnum
+ && hw_ep->rx_channel) {
+ unsigned cppi = epnum - 1;
+ unsigned off1 = cppi << 2;
+ void __iomem *base;
+ void __iomem *ram;
+ char tmp[16];
+
+ base = musb->ctrl_base;
+ ram = DAVINCI_RXCPPI_STATERAM_OFFSET(
+ cppi) + base;
+ snprintf(tmp, sizeof tmp, "%d left, ",
+ musb_readl(base,
+ DAVINCI_RXCPPI_BUFCNT0_REG
+ + off1));
+
+ code = snprintf(buf, max,
+ " rx dma%d: %s"
+ "%08x %08x, %08x %08x; "
+ "%08x %08x %08x .. %08x\n",
+ cppi, tmp,
+ musb_readl(ram, 0 * 4),
+ musb_readl(ram, 1 * 4),
+ musb_readl(ram, 2 * 4),
+ musb_readl(ram, 3 * 4),
+ musb_readl(ram, 4 * 4),
+ musb_readl(ram, 5 * 4),
+ musb_readl(ram, 6 * 4),
+ musb_readl(ram, 7 * 4));
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+ }
+
+ if (hw_ep == musb->bulk_ep
+ && !list_empty(
+ &musb->in_bulk)) {
+ code = dump_queue(&musb->in_bulk,
+ buf, max);
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+ } else if (musb->periodic[epnum]) {
+ code = dump_qh(musb->periodic[epnum],
+ buf, max);
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+ }
+ }
+
+ if (dump_tx) {
+ code = snprintf(buf, max,
+ "\nTX%d: %s txcsr %04x interval %02x "
+ "max %04x type %02x; "
+ "dev %d hub %d port %d"
+ "\n",
+ epnum,
+ hw_ep->tx_double_buffered
+ ? "2buf" : "1buf",
+ musb_readw(regs, MUSB_TXCSR),
+ musb_readb(regs, MUSB_TXINTERVAL),
+ musb_readw(regs, MUSB_TXMAXP),
+ musb_readb(regs, MUSB_TXTYPE),
+ /* FIXME: assumes multipoint */
+ musb_readb(musb->mregs,
+ MUSB_BUSCTL_OFFSET(epnum,
+ MUSB_TXFUNCADDR)),
+ musb_readb(musb->mregs,
+ MUSB_BUSCTL_OFFSET(epnum,
+ MUSB_TXHUBADDR)),
+ musb_readb(musb->mregs,
+ MUSB_BUSCTL_OFFSET(epnum,
+ MUSB_TXHUBPORT))
+ );
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+
+ if (is_cppi_enabled()
+ && epnum
+ && hw_ep->tx_channel) {
+ unsigned cppi = epnum - 1;
+ void __iomem *base;
+ void __iomem *ram;
+
+ base = musb->ctrl_base;
+ ram = DAVINCI_RXCPPI_STATERAM_OFFSET(
+ cppi) + base;
+ code = snprintf(buf, max,
+ " tx dma%d: "
+ "%08x %08x, %08x %08x; "
+ "%08x %08x %08x .. %08x\n",
+ cppi,
+ musb_readl(ram, 0 * 4),
+ musb_readl(ram, 1 * 4),
+ musb_readl(ram, 2 * 4),
+ musb_readl(ram, 3 * 4),
+ musb_readl(ram, 4 * 4),
+ musb_readl(ram, 5 * 4),
+ musb_readl(ram, 6 * 4),
+ musb_readl(ram, 7 * 4));
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+ }
+
+ if (hw_ep == musb->control_ep
+ && !list_empty(
+ &musb->control)) {
+ code = dump_queue(&musb->control,
+ buf, max);
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+ } else if (hw_ep == musb->bulk_ep
+ && !list_empty(
+ &musb->out_bulk)) {
+ code = dump_queue(&musb->out_bulk,
+ buf, max);
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+ } else if (musb->periodic[epnum]) {
+ code = dump_qh(musb->periodic[epnum],
+ buf, max);
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+ }
+ }
+ }
+#endif
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ if (is_peripheral_active(musb)) {
+ code = 0;
+
+ if (hw_ep->ep_in.desc || !epnum) {
+ code = dump_ep(&hw_ep->ep_in, buf, max);
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+ }
+ if (hw_ep->ep_out.desc) {
+ code = dump_ep(&hw_ep->ep_out, buf, max);
+ if (code <= 0)
+ break;
+ code = min(code, (int) max);
+ buf += code;
+ max -= code;
+ }
+ }
+#endif
+ } while (0);
+
+ return buf - aBuffer;
+}
+
+/* Dump the current status and compile options.
+ * @param musb the device driver instance
+ * @param buffer where to dump the status; it must be big enough to hold the
+ * result otherwise "BAD THINGS HAPPENS(TM)".
+ */
+static int dump_header_stats(struct musb *musb, char *buffer)
+{
+ int code, count = 0;
+ const void __iomem *mbase = musb->mregs;
+
+ *buffer = 0;
+ count = sprintf(buffer, "Status: %sHDRC, Mode=%s "
+ "(Power=%02x, DevCtl=%02x)\n",
+ (musb->is_multipoint ? "M" : ""), MUSB_MODE(musb),
+ musb_readb(mbase, MUSB_POWER),
+ musb_readb(mbase, MUSB_DEVCTL));
+ if (count <= 0)
+ return 0;
+ buffer += count;
+
+ code = sprintf(buffer, "OTG state: %s; %sactive\n",
+ otg_state_string(musb),
+ musb->is_active ? "" : "in");
+ if (code <= 0)
+ goto done;
+ buffer += code;
+ count += code;
+
+ code = sprintf(buffer,
+ "Options: "
+#ifdef CONFIG_MUSB_PIO_ONLY
+ "pio"
+#elif defined(CONFIG_USB_TI_CPPI_DMA)
+ "cppi-dma"
+#elif defined(CONFIG_USB_INVENTRA_DMA)
+ "musb-dma"
+#elif defined(CONFIG_USB_TUSB_OMAP_DMA)
+ "tusb-omap-dma"
+#else
+ "?dma?"
+#endif
+ ", "
+#ifdef CONFIG_USB_MUSB_OTG
+ "otg (peripheral+host)"
+#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
+ "peripheral"
+#elif defined(CONFIG_USB_MUSB_HDRC_HCD)
+ "host"
+#endif
+ ", debug=%d [eps=%d]\n",
+ debug,
+ musb->nr_endpoints);
+ if (code <= 0)
+ goto done;
+ count += code;
+ buffer += code;
+
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ code = sprintf(buffer, "Peripheral address: %02x\n",
+ musb_readb(musb->ctrl_base, MUSB_FADDR));
+ if (code <= 0)
+ goto done;
+ buffer += code;
+ count += code;
+#endif
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ code = sprintf(buffer, "Root port status: %08x\n",
+ musb->port1_status);
+ if (code <= 0)
+ goto done;
+ buffer += code;
+ count += code;
+#endif
+
+#ifdef CONFIG_ARCH_DAVINCI
+ code = sprintf(buffer,
+ "DaVinci: ctrl=%02x stat=%1x phy=%03x\n"
+ "\trndis=%05x auto=%04x intsrc=%08x intmsk=%08x"
+ "\n",
+ musb_readl(musb->ctrl_base, DAVINCI_USB_CTRL_REG),
+ musb_readl(musb->ctrl_base, DAVINCI_USB_STAT_REG),
+ __raw_readl((void __force __iomem *)
+ IO_ADDRESS(USBPHY_CTL_PADDR)),
+ musb_readl(musb->ctrl_base, DAVINCI_RNDIS_REG),
+ musb_readl(musb->ctrl_base, DAVINCI_AUTOREQ_REG),
+ musb_readl(musb->ctrl_base,
+ DAVINCI_USB_INT_SOURCE_REG),
+ musb_readl(musb->ctrl_base,
+ DAVINCI_USB_INT_MASK_REG));
+ if (code <= 0)
+ goto done;
+ count += code;
+ buffer += code;
+#endif /* DAVINCI */
+
+#ifdef CONFIG_USB_TUSB6010
+ code = sprintf(buffer,
+ "TUSB6010: devconf %08x, phy enable %08x drive %08x"
+ "\n\totg %03x timer %08x"
+ "\n\tprcm conf %08x mgmt %08x; int src %08x mask %08x"
+ "\n",
+ musb_readl(musb->ctrl_base, TUSB_DEV_CONF),
+ musb_readl(musb->ctrl_base, TUSB_PHY_OTG_CTRL_ENABLE),
+ musb_readl(musb->ctrl_base, TUSB_PHY_OTG_CTRL),
+ musb_readl(musb->ctrl_base, TUSB_DEV_OTG_STAT),
+ musb_readl(musb->ctrl_base, TUSB_DEV_OTG_TIMER),
+ musb_readl(musb->ctrl_base, TUSB_PRCM_CONF),
+ musb_readl(musb->ctrl_base, TUSB_PRCM_MNGMT),
+ musb_readl(musb->ctrl_base, TUSB_INT_SRC),
+ musb_readl(musb->ctrl_base, TUSB_INT_MASK));
+ if (code <= 0)
+ goto done;
+ count += code;
+ buffer += code;
+#endif /* DAVINCI */
+
+ if (is_cppi_enabled() && musb->dma_controller) {
+ code = sprintf(buffer,
+ "CPPI: txcr=%d txsrc=%01x txena=%01x; "
+ "rxcr=%d rxsrc=%01x rxena=%01x "
+ "\n",
+ musb_readl(musb->ctrl_base,
+ DAVINCI_TXCPPI_CTRL_REG),
+ musb_readl(musb->ctrl_base,
+ DAVINCI_TXCPPI_RAW_REG),
+ musb_readl(musb->ctrl_base,
+ DAVINCI_TXCPPI_INTENAB_REG),
+ musb_readl(musb->ctrl_base,
+ DAVINCI_RXCPPI_CTRL_REG),
+ musb_readl(musb->ctrl_base,
+ DAVINCI_RXCPPI_RAW_REG),
+ musb_readl(musb->ctrl_base,
+ DAVINCI_RXCPPI_INTENAB_REG));
+ if (code <= 0)
+ goto done;
+ count += code;
+ buffer += code;
+ }
+
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+ if (is_peripheral_enabled(musb)) {
+ code = sprintf(buffer, "Gadget driver: %s\n",
+ musb->gadget_driver
+ ? musb->gadget_driver->driver.name
+ : "(none)");
+ if (code <= 0)
+ goto done;
+ count += code;
+ buffer += code;
+ }
+#endif
+
+done:
+ return count;
+}
+
+/* Write to ProcFS
+ *
+ * C soft-connect
+ * c soft-disconnect
+ * I enable HS
+ * i disable HS
+ * s stop session
+ * F force session (OTG-unfriendly)
+ * E rElinquish bus (OTG)
+ * H request host mode
+ * h cancel host request
+ * T start sending TEST_PACKET
+ * D<num> set/query the debug level
+ */
+static int musb_proc_write(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ char cmd;
+ u8 reg;
+ struct musb *musb = (struct musb *)data;
+ void __iomem *mbase = musb->mregs;
+
+ /* MOD_INC_USE_COUNT; */
+
+ if (unlikely(copy_from_user(&cmd, buffer, 1)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case 'C':
+ if (mbase) {
+ reg = musb_readb(mbase, MUSB_POWER)
+ | MUSB_POWER_SOFTCONN;
+ musb_writeb(mbase, MUSB_POWER, reg);
+ }
+ break;
+
+ case 'c':
+ if (mbase) {
+ reg = musb_readb(mbase, MUSB_POWER)
+ & ~MUSB_POWER_SOFTCONN;
+ musb_writeb(mbase, MUSB_POWER, reg);
+ }
+ break;
+
+ case 'I':
+ if (mbase) {
+ reg = musb_readb(mbase, MUSB_POWER)
+ | MUSB_POWER_HSENAB;
+ musb_writeb(mbase, MUSB_POWER, reg);
+ }
+ break;
+
+ case 'i':
+ if (mbase) {
+ reg = musb_readb(mbase, MUSB_POWER)
+ & ~MUSB_POWER_HSENAB;
+ musb_writeb(mbase, MUSB_POWER, reg);
+ }
+ break;
+
+ case 'F':
+ reg = musb_readb(mbase, MUSB_DEVCTL);
+ reg |= MUSB_DEVCTL_SESSION;
+ musb_writeb(mbase, MUSB_DEVCTL, reg);
+ break;
+
+ case 'H':
+ if (mbase) {
+ reg = musb_readb(mbase, MUSB_DEVCTL);
+ reg |= MUSB_DEVCTL_HR;
+ musb_writeb(mbase, MUSB_DEVCTL, reg);
+ /* MUSB_HST_MODE( ((struct musb*)data) ); */
+ /* WARNING("Host Mode\n"); */
+ }
+ break;
+
+ case 'h':
+ if (mbase) {
+ reg = musb_readb(mbase, MUSB_DEVCTL);
+ reg &= ~MUSB_DEVCTL_HR;
+ musb_writeb(mbase, MUSB_DEVCTL, reg);
+ }
+ break;
+
+ case 'T':
+ if (mbase) {
+ musb_load_testpacket(musb);
+ musb_writeb(mbase, MUSB_TESTMODE,
+ MUSB_TEST_PACKET);
+ }
+ break;
+
+#if (MUSB_DEBUG > 0)
+ /* set/read debug level */
+ case 'D':{
+ if (count > 1) {
+ char digits[8], *p = digits;
+ int i = 0, level = 0, sign = 1;
+ int len = min(count - 1, (unsigned long)8);
+
+ if (copy_from_user(&digits, &buffer[1], len))
+ return -EFAULT;
+
+ /* optional sign */
+ if (*p == '-') {
+ len -= 1;
+ sign = -sign;
+ p++;
+ }
+
+ /* read it */
+ while (i++ < len && *p > '0' && *p < '9') {
+ level = level * 10 + (*p - '0');
+ p++;
+ }
+
+ level *= sign;
+ DBG(1, "debug level %d\n", level);
+ debug = level;
+ }
+ }
+ break;
+
+
+ case '?':
+ INFO("?: you are seeing it\n");
+ INFO("C/c: soft connect enable/disable\n");
+ INFO("I/i: hispeed enable/disable\n");
+ INFO("F: force session start\n");
+ INFO("H: host mode\n");
+ INFO("T: start sending TEST_PACKET\n");
+ INFO("D: set/read dbug level\n");
+ break;
+#endif
+
+ default:
+ ERR("Command %c not implemented\n", cmd);
+ break;
+ }
+
+ musb_platform_try_idle(musb, 0);
+
+ return count;
+}
+
+static int musb_proc_read(char *page, char **start,
+ off_t off, int count, int *eof, void *data)
+{
+ char *buffer = page;
+ int code = 0;
+ unsigned long flags;
+ struct musb *musb = data;
+ unsigned epnum;
+
+ count -= off;
+ count -= 1; /* for NUL at end */
+ if (count <= 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ code = dump_header_stats(musb, buffer);
+ if (code > 0) {
+ buffer += code;
+ count -= code;
+ }
+
+ /* generate the report for the end points */
+ /* REVISIT ... not unless something's connected! */
+ for (epnum = 0; count >= 0 && epnum < musb->nr_endpoints;
+ epnum++) {
+ code = dump_end_info(musb, epnum, buffer, count);
+ if (code > 0) {
+ buffer += code;
+ count -= code;
+ }
+ }
+
+ musb_platform_try_idle(musb, 0);
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+ *eof = 1;
+
+ return buffer - page;
+}
+
+void __devexit musb_debug_delete(char *name, struct musb *musb)
+{
+ if (musb->proc_entry)
+ remove_proc_entry(name, NULL);
+}
+
+struct proc_dir_entry *__init
+musb_debug_create(char *name, struct musb *data)
+{
+ struct proc_dir_entry *pde;
+
+ /* FIXME convert everything to seq_file; then later, debugfs */
+
+ if (!name)
+ return NULL;
+
+ data->proc_entry = pde = create_proc_entry(name,
+ S_IFREG | S_IRUGO | S_IWUSR, NULL);
+ if (pde) {
+ pde->data = data;
+ /* pde->owner = THIS_MODULE; */
+
+ pde->read_proc = musb_proc_read;
+ pde->write_proc = musb_proc_write;
+
+ pde->size = 0;
+
+ pr_debug("Registered /proc/%s\n", name);
+ } else {
+ pr_debug("Cannot create a valid proc file entry");
+ }
+
+ return pde;
+}
--- /dev/null
- #include <asm/arch/hardware.h>
- #include <asm/arch/mux.h>
+/*
+ * Copyright (C) 2005-2007 by Texas Instruments
+ * Some code has been taken from tusb6010.c
+ * Copyrights for that are attributable to:
+ * Copyright (C) 2006 Nokia Corporation
+ * Jarkko Nikula <jarkko.nikula@nokia.com>
+ * Tony Lindgren <tony@atomide.com>
+ *
+ * This file is part of the Inventra Controller Driver for Linux.
+ *
+ * The Inventra Controller Driver for Linux 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.
+ *
+ * The Inventra Controller Driver for Linux 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 The Inventra Controller Driver for Linux ; 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/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <asm/mach-types.h>
++#include <mach/hardware.h>
++#include <mach/mux.h>
+
+#include "musb_core.h"
+#include "omap2430.h"
+
+#ifdef CONFIG_ARCH_OMAP3430
+#define get_cpu_rev() 2
+#endif
+
+#define MUSB_TIMEOUT_A_WAIT_BCON 1100
+
+static struct timer_list musb_idle_timer;
+
+static void musb_do_idle(unsigned long _musb)
+{
+ struct musb *musb = (void *)_musb;
+ unsigned long flags;
+ u8 power;
+ u8 devctl;
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ switch (musb->xceiv.state) {
+ case OTG_STATE_A_WAIT_BCON:
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ if (devctl & MUSB_DEVCTL_BDEVICE) {
+ musb->xceiv.state = OTG_STATE_B_IDLE;
+ MUSB_DEV_MODE(musb);
+ } else {
+ musb->xceiv.state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ }
+ break;
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ case OTG_STATE_A_SUSPEND:
+ /* finish RESUME signaling? */
+ if (musb->port1_status & MUSB_PORT_STAT_RESUME) {
+ power = musb_readb(musb->mregs, MUSB_POWER);
+ power &= ~MUSB_POWER_RESUME;
+ DBG(1, "root port resume stopped, power %02x\n", power);
+ musb_writeb(musb->mregs, MUSB_POWER, power);
+ musb->is_active = 1;
+ musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
+ | MUSB_PORT_STAT_RESUME);
+ musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
+ usb_hcd_poll_rh_status(musb_to_hcd(musb));
+ /* NOTE: it might really be A_WAIT_BCON ... */
+ musb->xceiv.state = OTG_STATE_A_HOST;
+ }
+ break;
+#endif
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+ case OTG_STATE_A_HOST:
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ if (devctl & MUSB_DEVCTL_BDEVICE)
+ musb->xceiv.state = OTG_STATE_B_IDLE;
+ else
+ musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
+#endif
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+
+void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
+{
+ unsigned long default_timeout = jiffies + msecs_to_jiffies(3);
+ static unsigned long last_timer;
+
+ if (timeout == 0)
+ timeout = default_timeout;
+
+ /* Never idle if active, or when VBUS timeout is not set as host */
+ if (musb->is_active || ((musb->a_wait_bcon == 0)
+ && (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) {
+ DBG(4, "%s active, deleting timer\n", otg_state_string(musb));
+ del_timer(&musb_idle_timer);
+ last_timer = jiffies;
+ return;
+ }
+
+ if (time_after(last_timer, timeout)) {
+ if (!timer_pending(&musb_idle_timer))
+ last_timer = timeout;
+ else {
+ DBG(4, "Longer idle timer already pending, ignoring\n");
+ return;
+ }
+ }
+ last_timer = timeout;
+
+ DBG(4, "%s inactive, for idle timer for %lu ms\n",
+ otg_state_string(musb),
+ (unsigned long)jiffies_to_msecs(timeout - jiffies));
+ mod_timer(&musb_idle_timer, timeout);
+}
+
+void musb_platform_enable(struct musb *musb)
+{
+}
+void musb_platform_disable(struct musb *musb)
+{
+}
+static void omap_vbus_power(struct musb *musb, int is_on, int sleeping)
+{
+}
+
+static void omap_set_vbus(struct musb *musb, int is_on)
+{
+ u8 devctl;
+ /* HDRC controls CPEN, but beware current surges during device
+ * connect. They can trigger transient overcurrent conditions
+ * that must be ignored.
+ */
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ if (is_on) {
+ musb->is_active = 1;
+ musb->xceiv.default_a = 1;
+ musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
+ devctl |= MUSB_DEVCTL_SESSION;
+
+ MUSB_HST_MODE(musb);
+ } else {
+ musb->is_active = 0;
+
+ /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and
+ * jumping right to B_IDLE...
+ */
+
+ musb->xceiv.default_a = 0;
+ musb->xceiv.state = OTG_STATE_B_IDLE;
+ devctl &= ~MUSB_DEVCTL_SESSION;
+
+ MUSB_DEV_MODE(musb);
+ }
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ DBG(1, "VBUS %s, devctl %02x "
+ /* otg %3x conf %08x prcm %08x */ "\n",
+ otg_state_string(musb),
+ musb_readb(musb->mregs, MUSB_DEVCTL));
+}
+static int omap_set_power(struct otg_transceiver *x, unsigned mA)
+{
+ return 0;
+}
+
+int musb_platform_resume(struct musb *musb);
+
+void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
+{
+ u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ devctl |= MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ switch (musb_mode) {
+ case MUSB_HOST:
+ otg_set_host(&musb->xceiv, musb->xceiv.host);
+ break;
+ case MUSB_PERIPHERAL:
+ otg_set_peripheral(&musb->xceiv, musb->xceiv.gadget);
+ break;
+ case MUSB_OTG:
+ break;
+ }
+}
+
+int __init musb_platform_init(struct musb *musb)
+{
+ struct otg_transceiver *xceiv = otg_get_transceiver();
+ u32 l;
+
+#if defined(CONFIG_ARCH_OMAP2430)
+ omap_cfg_reg(AE5_2430_USB0HS_STP);
+#endif
+
+ musb->xceiv = *xceiv;
+ musb_platform_resume(musb);
+
+ l = omap_readl(OTG_SYSCONFIG);
+ l &= ~ENABLEWAKEUP; /* disable wakeup */
+ l &= ~NOSTDBY; /* remove possible nostdby */
+ l |= SMARTSTDBY; /* enable smart standby */
+ l &= ~AUTOIDLE; /* disable auto idle */
+ l &= ~NOIDLE; /* remove possible noidle */
+ l |= SMARTIDLE; /* enable smart idle */
+ l |= AUTOIDLE; /* enable auto idle */
+ omap_writel(l, OTG_SYSCONFIG);
+
+ l = omap_readl(OTG_INTERFSEL);
+ l |= ULPI_12PIN;
+ omap_writel(l, OTG_INTERFSEL);
+
+ pr_debug("HS USB OTG: revision 0x%x, sysconfig 0x%02x, "
+ "sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x\n",
+ omap_readl(OTG_REVISION), omap_readl(OTG_SYSCONFIG),
+ omap_readl(OTG_SYSSTATUS), omap_readl(OTG_INTERFSEL),
+ omap_readl(OTG_SIMENABLE));
+
+ omap_vbus_power(musb, musb->board_mode == MUSB_HOST, 1);
+
+ if (is_host_enabled(musb))
+ musb->board_set_vbus = omap_set_vbus;
+ if (is_peripheral_enabled(musb))
+ musb->xceiv.set_power = omap_set_power;
+ musb->a_wait_bcon = MUSB_TIMEOUT_A_WAIT_BCON;
+
+ setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
+
+ return 0;
+}
+
+int musb_platform_suspend(struct musb *musb)
+{
+ u32 l;
+
+ if (!musb->clock)
+ return 0;
+
+ /* in any role */
+ l = omap_readl(OTG_FORCESTDBY);
+ l |= ENABLEFORCE; /* enable MSTANDBY */
+ omap_writel(l, OTG_FORCESTDBY);
+
+ l = omap_readl(OTG_SYSCONFIG);
+ l |= ENABLEWAKEUP; /* enable wakeup */
+ omap_writel(l, OTG_SYSCONFIG);
+
+ if (musb->xceiv.set_suspend)
+ musb->xceiv.set_suspend(&musb->xceiv, 1);
+
+ if (musb->set_clock)
+ musb->set_clock(musb->clock, 0);
+ else
+ clk_disable(musb->clock);
+
+ return 0;
+}
+
+int musb_platform_resume(struct musb *musb)
+{
+ u32 l;
+
+ if (!musb->clock)
+ return 0;
+
+ if (musb->xceiv.set_suspend)
+ musb->xceiv.set_suspend(&musb->xceiv, 0);
+
+ if (musb->set_clock)
+ musb->set_clock(musb->clock, 1);
+ else
+ clk_enable(musb->clock);
+
+ l = omap_readl(OTG_SYSCONFIG);
+ l &= ~ENABLEWAKEUP; /* disable wakeup */
+ omap_writel(l, OTG_SYSCONFIG);
+
+ l = omap_readl(OTG_FORCESTDBY);
+ l &= ~ENABLEFORCE; /* disable MSTANDBY */
+ omap_writel(l, OTG_FORCESTDBY);
+
+ return 0;
+}
+
+
+int musb_platform_exit(struct musb *musb)
+{
+
+ omap_vbus_power(musb, 0 /*off*/, 1);
+
+ musb_platform_suspend(musb);
+
+ clk_put(musb->clock);
+ musb->clock = 0;
+
+ return 0;
+}
--- /dev/null
- #include <asm/arch/hardware.h>
- #include <asm/arch/usb.h>
+/*
+ * Copyright (C) 2005-2006 by Texas Instruments
+ *
+ * The Inventra Controller Driver for Linux 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.
+ */
+
+#ifndef __MUSB_OMAP243X_H__
+#define __MUSB_OMAP243X_H__
+
+#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430)
++#include <mach/hardware.h>
++#include <mach/usb.h>
+
+/*
+ * OMAP2430-specific definitions
+ */
+
+#define MENTOR_BASE_OFFSET 0
+#if defined(CONFIG_ARCH_OMAP2430)
+#define OMAP_HSOTG_BASE (OMAP243X_HS_BASE)
+#elif defined(CONFIG_ARCH_OMAP3430)
+#define OMAP_HSOTG_BASE (OMAP34XX_HSUSB_OTG_BASE)
+#endif
+#define OMAP_HSOTG(offset) (OMAP_HSOTG_BASE + 0x400 + (offset))
+#define OTG_REVISION OMAP_HSOTG(0x0)
+#define OTG_SYSCONFIG OMAP_HSOTG(0x4)
+# define MIDLEMODE 12 /* bit position */
+# define FORCESTDBY (0 << MIDLEMODE)
+# define NOSTDBY (1 << MIDLEMODE)
+# define SMARTSTDBY (2 << MIDLEMODE)
+# define SIDLEMODE 3 /* bit position */
+# define FORCEIDLE (0 << SIDLEMODE)
+# define NOIDLE (1 << SIDLEMODE)
+# define SMARTIDLE (2 << SIDLEMODE)
+# define ENABLEWAKEUP (1 << 2)
+# define SOFTRST (1 << 1)
+# define AUTOIDLE (1 << 0)
+#define OTG_SYSSTATUS OMAP_HSOTG(0x8)
+# define RESETDONE (1 << 0)
+#define OTG_INTERFSEL OMAP_HSOTG(0xc)
+# define EXTCP (1 << 2)
+# define PHYSEL 0 /* bit position */
+# define UTMI_8BIT (0 << PHYSEL)
+# define ULPI_12PIN (1 << PHYSEL)
+# define ULPI_8PIN (2 << PHYSEL)
+#define OTG_SIMENABLE OMAP_HSOTG(0x10)
+# define TM1 (1 << 0)
+#define OTG_FORCESTDBY OMAP_HSOTG(0x14)
+# define ENABLEFORCE (1 << 0)
+
+#endif /* CONFIG_ARCH_OMAP2430 */
+
+#endif /* __MUSB_OMAP243X_H__ */
--- /dev/null
- #include <asm/arch/dma.h>
- #include <asm/arch/mux.h>
+/*
+ * TUSB6010 USB 2.0 OTG Dual Role controller OMAP DMA interface
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Tony Lindgren <tony@atomide.com>
+ *
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
++#include <mach/dma.h>
++#include <mach/mux.h>
+
+#include "musb_core.h"
+
+#define to_chdat(c) (struct tusb_omap_dma_ch *)(c)->private_data
+
+#define MAX_DMAREQ 5 /* REVISIT: Really 6, but req5 not OK */
+
+struct tusb_omap_dma_ch {
+ struct musb *musb;
+ void __iomem *tbase;
+ unsigned long phys_offset;
+ int epnum;
+ u8 tx;
+ struct musb_hw_ep *hw_ep;
+
+ int ch;
+ s8 dmareq;
+ s8 sync_dev;
+
+ struct tusb_omap_dma *tusb_dma;
+
+ void __iomem *dma_addr;
+
+ u32 len;
+ u16 packet_sz;
+ u16 transfer_packet_sz;
+ u32 transfer_len;
+ u32 completed_len;
+};
+
+struct tusb_omap_dma {
+ struct dma_controller controller;
+ struct musb *musb;
+ void __iomem *tbase;
+
+ int ch;
+ s8 dmareq;
+ s8 sync_dev;
+ unsigned multichannel:1;
+};
+
+static int tusb_omap_dma_start(struct dma_controller *c)
+{
+ struct tusb_omap_dma *tusb_dma;
+
+ tusb_dma = container_of(c, struct tusb_omap_dma, controller);
+
+ /* DBG(3, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */
+
+ return 0;
+}
+
+static int tusb_omap_dma_stop(struct dma_controller *c)
+{
+ struct tusb_omap_dma *tusb_dma;
+
+ tusb_dma = container_of(c, struct tusb_omap_dma, controller);
+
+ /* DBG(3, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */
+
+ return 0;
+}
+
+/*
+ * Allocate dmareq0 to the current channel unless it's already taken
+ */
+static inline int tusb_omap_use_shared_dmareq(struct tusb_omap_dma_ch *chdat)
+{
+ u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
+
+ if (reg != 0) {
+ DBG(3, "ep%i dmareq0 is busy for ep%i\n",
+ chdat->epnum, reg & 0xf);
+ return -EAGAIN;
+ }
+
+ if (chdat->tx)
+ reg = (1 << 4) | chdat->epnum;
+ else
+ reg = chdat->epnum;
+
+ musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg);
+
+ return 0;
+}
+
+static inline void tusb_omap_free_shared_dmareq(struct tusb_omap_dma_ch *chdat)
+{
+ u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
+
+ if ((reg & 0xf) != chdat->epnum) {
+ printk(KERN_ERR "ep%i trying to release dmareq0 for ep%i\n",
+ chdat->epnum, reg & 0xf);
+ return;
+ }
+ musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, 0);
+}
+
+/*
+ * See also musb_dma_completion in plat_uds.c and musb_g_[tx|rx]() in
+ * musb_gadget.c.
+ */
+static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data)
+{
+ struct dma_channel *channel = (struct dma_channel *)data;
+ struct tusb_omap_dma_ch *chdat = to_chdat(channel);
+ struct tusb_omap_dma *tusb_dma = chdat->tusb_dma;
+ struct musb *musb = chdat->musb;
+ struct musb_hw_ep *hw_ep = chdat->hw_ep;
+ void __iomem *ep_conf = hw_ep->conf;
+ void __iomem *mbase = musb->mregs;
+ unsigned long remaining, flags, pio;
+ int ch;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ if (tusb_dma->multichannel)
+ ch = chdat->ch;
+ else
+ ch = tusb_dma->ch;
+
+ if (ch_status != OMAP_DMA_BLOCK_IRQ)
+ printk(KERN_ERR "TUSB DMA error status: %i\n", ch_status);
+
+ DBG(3, "ep%i %s dma callback ch: %i status: %x\n",
+ chdat->epnum, chdat->tx ? "tx" : "rx",
+ ch, ch_status);
+
+ if (chdat->tx)
+ remaining = musb_readl(ep_conf, TUSB_EP_TX_OFFSET);
+ else
+ remaining = musb_readl(ep_conf, TUSB_EP_RX_OFFSET);
+
+ remaining = TUSB_EP_CONFIG_XFR_SIZE(remaining);
+
+ /* HW issue #10: XFR_SIZE may get corrupt on DMA (both async & sync) */
+ if (unlikely(remaining > chdat->transfer_len)) {
+ DBG(2, "Corrupt %s dma ch%i XFR_SIZE: 0x%08lx\n",
+ chdat->tx ? "tx" : "rx", chdat->ch,
+ remaining);
+ remaining = 0;
+ }
+
+ channel->actual_len = chdat->transfer_len - remaining;
+ pio = chdat->len - channel->actual_len;
+
+ DBG(3, "DMA remaining %lu/%u\n", remaining, chdat->transfer_len);
+
+ /* Transfer remaining 1 - 31 bytes */
+ if (pio > 0 && pio < 32) {
+ u8 *buf;
+
+ DBG(3, "Using PIO for remaining %lu bytes\n", pio);
+ buf = phys_to_virt((u32)chdat->dma_addr) + chdat->transfer_len;
+ if (chdat->tx) {
+ dma_cache_maint(phys_to_virt((u32)chdat->dma_addr),
+ chdat->transfer_len, DMA_TO_DEVICE);
+ musb_write_fifo(hw_ep, pio, buf);
+ } else {
+ musb_read_fifo(hw_ep, pio, buf);
+ dma_cache_maint(phys_to_virt((u32)chdat->dma_addr),
+ chdat->transfer_len, DMA_FROM_DEVICE);
+ }
+ channel->actual_len += pio;
+ }
+
+ if (!tusb_dma->multichannel)
+ tusb_omap_free_shared_dmareq(chdat);
+
+ channel->status = MUSB_DMA_STATUS_FREE;
+
+ /* Handle only RX callbacks here. TX callbacks must be handled based
+ * on the TUSB DMA status interrupt.
+ * REVISIT: Use both TUSB DMA status interrupt and OMAP DMA callback
+ * interrupt for RX and TX.
+ */
+ if (!chdat->tx)
+ musb_dma_completion(musb, chdat->epnum, chdat->tx);
+
+ /* We must terminate short tx transfers manually by setting TXPKTRDY.
+ * REVISIT: This same problem may occur with other MUSB dma as well.
+ * Easy to test with g_ether by pinging the MUSB board with ping -s54.
+ */
+ if ((chdat->transfer_len < chdat->packet_sz)
+ || (chdat->transfer_len % chdat->packet_sz != 0)) {
+ u16 csr;
+
+ if (chdat->tx) {
+ DBG(3, "terminating short tx packet\n");
+ musb_ep_select(mbase, chdat->epnum);
+ csr = musb_readw(hw_ep->regs, MUSB_TXCSR);
+ csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY
+ | MUSB_TXCSR_P_WZC_BITS;
+ musb_writew(hw_ep->regs, MUSB_TXCSR, csr);
+ }
+ }
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz,
+ u8 rndis_mode, dma_addr_t dma_addr, u32 len)
+{
+ struct tusb_omap_dma_ch *chdat = to_chdat(channel);
+ struct tusb_omap_dma *tusb_dma = chdat->tusb_dma;
+ struct musb *musb = chdat->musb;
+ struct musb_hw_ep *hw_ep = chdat->hw_ep;
+ void __iomem *mbase = musb->mregs;
+ void __iomem *ep_conf = hw_ep->conf;
+ dma_addr_t fifo = hw_ep->fifo_sync;
+ struct omap_dma_channel_params dma_params;
+ u32 dma_remaining;
+ int src_burst, dst_burst;
+ u16 csr;
+ int ch;
+ s8 dmareq;
+ s8 sync_dev;
+
+ if (unlikely(dma_addr & 0x1) || (len < 32) || (len > packet_sz))
+ return false;
+
+ /*
+ * HW issue #10: Async dma will eventually corrupt the XFR_SIZE
+ * register which will cause missed DMA interrupt. We could try to
+ * use a timer for the callback, but it is unsafe as the XFR_SIZE
+ * register is corrupt, and we won't know if the DMA worked.
+ */
+ if (dma_addr & 0x2)
+ return false;
+
+ /*
+ * Because of HW issue #10, it seems like mixing sync DMA and async
+ * PIO access can confuse the DMA. Make sure XFR_SIZE is reset before
+ * using the channel for DMA.
+ */
+ if (chdat->tx)
+ dma_remaining = musb_readl(ep_conf, TUSB_EP_TX_OFFSET);
+ else
+ dma_remaining = musb_readl(ep_conf, TUSB_EP_RX_OFFSET);
+
+ dma_remaining = TUSB_EP_CONFIG_XFR_SIZE(dma_remaining);
+ if (dma_remaining) {
+ DBG(2, "Busy %s dma ch%i, not using: %08x\n",
+ chdat->tx ? "tx" : "rx", chdat->ch,
+ dma_remaining);
+ return false;
+ }
+
+ chdat->transfer_len = len & ~0x1f;
+
+ if (len < packet_sz)
+ chdat->transfer_packet_sz = chdat->transfer_len;
+ else
+ chdat->transfer_packet_sz = packet_sz;
+
+ if (tusb_dma->multichannel) {
+ ch = chdat->ch;
+ dmareq = chdat->dmareq;
+ sync_dev = chdat->sync_dev;
+ } else {
+ if (tusb_omap_use_shared_dmareq(chdat) != 0) {
+ DBG(3, "could not get dma for ep%i\n", chdat->epnum);
+ return false;
+ }
+ if (tusb_dma->ch < 0) {
+ /* REVISIT: This should get blocked earlier, happens
+ * with MSC ErrorRecoveryTest
+ */
+ WARN_ON(1);
+ return false;
+ }
+
+ ch = tusb_dma->ch;
+ dmareq = tusb_dma->dmareq;
+ sync_dev = tusb_dma->sync_dev;
+ omap_set_dma_callback(ch, tusb_omap_dma_cb, channel);
+ }
+
+ chdat->packet_sz = packet_sz;
+ chdat->len = len;
+ channel->actual_len = 0;
+ chdat->dma_addr = (void __iomem *)dma_addr;
+ channel->status = MUSB_DMA_STATUS_BUSY;
+
+ /* Since we're recycling dma areas, we need to clean or invalidate */
+ if (chdat->tx)
+ dma_cache_maint(phys_to_virt(dma_addr), len, DMA_TO_DEVICE);
+ else
+ dma_cache_maint(phys_to_virt(dma_addr), len, DMA_FROM_DEVICE);
+
+ /* Use 16-bit transfer if dma_addr is not 32-bit aligned */
+ if ((dma_addr & 0x3) == 0) {
+ dma_params.data_type = OMAP_DMA_DATA_TYPE_S32;
+ dma_params.elem_count = 8; /* Elements in frame */
+ } else {
+ dma_params.data_type = OMAP_DMA_DATA_TYPE_S16;
+ dma_params.elem_count = 16; /* Elements in frame */
+ fifo = hw_ep->fifo_async;
+ }
+
+ dma_params.frame_count = chdat->transfer_len / 32; /* Burst sz frame */
+
+ DBG(3, "ep%i %s dma ch%i dma: %08x len: %u(%u) packet_sz: %i(%i)\n",
+ chdat->epnum, chdat->tx ? "tx" : "rx",
+ ch, dma_addr, chdat->transfer_len, len,
+ chdat->transfer_packet_sz, packet_sz);
+
+ /*
+ * Prepare omap DMA for transfer
+ */
+ if (chdat->tx) {
+ dma_params.src_amode = OMAP_DMA_AMODE_POST_INC;
+ dma_params.src_start = (unsigned long)dma_addr;
+ dma_params.src_ei = 0;
+ dma_params.src_fi = 0;
+
+ dma_params.dst_amode = OMAP_DMA_AMODE_DOUBLE_IDX;
+ dma_params.dst_start = (unsigned long)fifo;
+ dma_params.dst_ei = 1;
+ dma_params.dst_fi = -31; /* Loop 32 byte window */
+
+ dma_params.trigger = sync_dev;
+ dma_params.sync_mode = OMAP_DMA_SYNC_FRAME;
+ dma_params.src_or_dst_synch = 0; /* Dest sync */
+
+ src_burst = OMAP_DMA_DATA_BURST_16; /* 16x32 read */
+ dst_burst = OMAP_DMA_DATA_BURST_8; /* 8x32 write */
+ } else {
+ dma_params.src_amode = OMAP_DMA_AMODE_DOUBLE_IDX;
+ dma_params.src_start = (unsigned long)fifo;
+ dma_params.src_ei = 1;
+ dma_params.src_fi = -31; /* Loop 32 byte window */
+
+ dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
+ dma_params.dst_start = (unsigned long)dma_addr;
+ dma_params.dst_ei = 0;
+ dma_params.dst_fi = 0;
+
+ dma_params.trigger = sync_dev;
+ dma_params.sync_mode = OMAP_DMA_SYNC_FRAME;
+ dma_params.src_or_dst_synch = 1; /* Source sync */
+
+ src_burst = OMAP_DMA_DATA_BURST_8; /* 8x32 read */
+ dst_burst = OMAP_DMA_DATA_BURST_16; /* 16x32 write */
+ }
+
+ DBG(3, "ep%i %s using %i-bit %s dma from 0x%08lx to 0x%08lx\n",
+ chdat->epnum, chdat->tx ? "tx" : "rx",
+ (dma_params.data_type == OMAP_DMA_DATA_TYPE_S32) ? 32 : 16,
+ ((dma_addr & 0x3) == 0) ? "sync" : "async",
+ dma_params.src_start, dma_params.dst_start);
+
+ omap_set_dma_params(ch, &dma_params);
+ omap_set_dma_src_burst_mode(ch, src_burst);
+ omap_set_dma_dest_burst_mode(ch, dst_burst);
+ omap_set_dma_write_mode(ch, OMAP_DMA_WRITE_LAST_NON_POSTED);
+
+ /*
+ * Prepare MUSB for DMA transfer
+ */
+ if (chdat->tx) {
+ musb_ep_select(mbase, chdat->epnum);
+ csr = musb_readw(hw_ep->regs, MUSB_TXCSR);
+ csr |= (MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAENAB
+ | MUSB_TXCSR_DMAMODE | MUSB_TXCSR_MODE);
+ csr &= ~MUSB_TXCSR_P_UNDERRUN;
+ musb_writew(hw_ep->regs, MUSB_TXCSR, csr);
+ } else {
+ musb_ep_select(mbase, chdat->epnum);
+ csr = musb_readw(hw_ep->regs, MUSB_RXCSR);
+ csr |= MUSB_RXCSR_DMAENAB;
+ csr &= ~(MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAMODE);
+ musb_writew(hw_ep->regs, MUSB_RXCSR,
+ csr | MUSB_RXCSR_P_WZC_BITS);
+ }
+
+ /*
+ * Start DMA transfer
+ */
+ omap_start_dma(ch);
+
+ if (chdat->tx) {
+ /* Send transfer_packet_sz packets at a time */
+ musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET,
+ chdat->transfer_packet_sz);
+
+ musb_writel(ep_conf, TUSB_EP_TX_OFFSET,
+ TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len));
+ } else {
+ /* Receive transfer_packet_sz packets at a time */
+ musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET,
+ chdat->transfer_packet_sz << 16);
+
+ musb_writel(ep_conf, TUSB_EP_RX_OFFSET,
+ TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len));
+ }
+
+ return true;
+}
+
+static int tusb_omap_dma_abort(struct dma_channel *channel)
+{
+ struct tusb_omap_dma_ch *chdat = to_chdat(channel);
+ struct tusb_omap_dma *tusb_dma = chdat->tusb_dma;
+
+ if (!tusb_dma->multichannel) {
+ if (tusb_dma->ch >= 0) {
+ omap_stop_dma(tusb_dma->ch);
+ omap_free_dma(tusb_dma->ch);
+ tusb_dma->ch = -1;
+ }
+
+ tusb_dma->dmareq = -1;
+ tusb_dma->sync_dev = -1;
+ }
+
+ channel->status = MUSB_DMA_STATUS_FREE;
+
+ return 0;
+}
+
+static inline int tusb_omap_dma_allocate_dmareq(struct tusb_omap_dma_ch *chdat)
+{
+ u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
+ int i, dmareq_nr = -1;
+
+ const int sync_dev[6] = {
+ OMAP24XX_DMA_EXT_DMAREQ0,
+ OMAP24XX_DMA_EXT_DMAREQ1,
+ OMAP242X_DMA_EXT_DMAREQ2,
+ OMAP242X_DMA_EXT_DMAREQ3,
+ OMAP242X_DMA_EXT_DMAREQ4,
+ OMAP242X_DMA_EXT_DMAREQ5,
+ };
+
+ for (i = 0; i < MAX_DMAREQ; i++) {
+ int cur = (reg & (0xf << (i * 5))) >> (i * 5);
+ if (cur == 0) {
+ dmareq_nr = i;
+ break;
+ }
+ }
+
+ if (dmareq_nr == -1)
+ return -EAGAIN;
+
+ reg |= (chdat->epnum << (dmareq_nr * 5));
+ if (chdat->tx)
+ reg |= ((1 << 4) << (dmareq_nr * 5));
+ musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg);
+
+ chdat->dmareq = dmareq_nr;
+ chdat->sync_dev = sync_dev[chdat->dmareq];
+
+ return 0;
+}
+
+static inline void tusb_omap_dma_free_dmareq(struct tusb_omap_dma_ch *chdat)
+{
+ u32 reg;
+
+ if (!chdat || chdat->dmareq < 0)
+ return;
+
+ reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
+ reg &= ~(0x1f << (chdat->dmareq * 5));
+ musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg);
+
+ chdat->dmareq = -1;
+ chdat->sync_dev = -1;
+}
+
+static struct dma_channel *dma_channel_pool[MAX_DMAREQ];
+
+static struct dma_channel *
+tusb_omap_dma_allocate(struct dma_controller *c,
+ struct musb_hw_ep *hw_ep,
+ u8 tx)
+{
+ int ret, i;
+ const char *dev_name;
+ struct tusb_omap_dma *tusb_dma;
+ struct musb *musb;
+ void __iomem *tbase;
+ struct dma_channel *channel = NULL;
+ struct tusb_omap_dma_ch *chdat = NULL;
+ u32 reg;
+
+ tusb_dma = container_of(c, struct tusb_omap_dma, controller);
+ musb = tusb_dma->musb;
+ tbase = musb->ctrl_base;
+
+ reg = musb_readl(tbase, TUSB_DMA_INT_MASK);
+ if (tx)
+ reg &= ~(1 << hw_ep->epnum);
+ else
+ reg &= ~(1 << (hw_ep->epnum + 15));
+ musb_writel(tbase, TUSB_DMA_INT_MASK, reg);
+
+ /* REVISIT: Why does dmareq5 not work? */
+ if (hw_ep->epnum == 0) {
+ DBG(3, "Not allowing DMA for ep0 %s\n", tx ? "tx" : "rx");
+ return NULL;
+ }
+
+ for (i = 0; i < MAX_DMAREQ; i++) {
+ struct dma_channel *ch = dma_channel_pool[i];
+ if (ch->status == MUSB_DMA_STATUS_UNKNOWN) {
+ ch->status = MUSB_DMA_STATUS_FREE;
+ channel = ch;
+ chdat = ch->private_data;
+ break;
+ }
+ }
+
+ if (!channel)
+ return NULL;
+
+ if (tx) {
+ chdat->tx = 1;
+ dev_name = "TUSB transmit";
+ } else {
+ chdat->tx = 0;
+ dev_name = "TUSB receive";
+ }
+
+ chdat->musb = tusb_dma->musb;
+ chdat->tbase = tusb_dma->tbase;
+ chdat->hw_ep = hw_ep;
+ chdat->epnum = hw_ep->epnum;
+ chdat->dmareq = -1;
+ chdat->completed_len = 0;
+ chdat->tusb_dma = tusb_dma;
+
+ channel->max_len = 0x7fffffff;
+ channel->desired_mode = 0;
+ channel->actual_len = 0;
+
+ if (tusb_dma->multichannel) {
+ ret = tusb_omap_dma_allocate_dmareq(chdat);
+ if (ret != 0)
+ goto free_dmareq;
+
+ ret = omap_request_dma(chdat->sync_dev, dev_name,
+ tusb_omap_dma_cb, channel, &chdat->ch);
+ if (ret != 0)
+ goto free_dmareq;
+ } else if (tusb_dma->ch == -1) {
+ tusb_dma->dmareq = 0;
+ tusb_dma->sync_dev = OMAP24XX_DMA_EXT_DMAREQ0;
+
+ /* Callback data gets set later in the shared dmareq case */
+ ret = omap_request_dma(tusb_dma->sync_dev, "TUSB shared",
+ tusb_omap_dma_cb, NULL, &tusb_dma->ch);
+ if (ret != 0)
+ goto free_dmareq;
+
+ chdat->dmareq = -1;
+ chdat->ch = -1;
+ }
+
+ DBG(3, "ep%i %s dma: %s dma%i dmareq%i sync%i\n",
+ chdat->epnum,
+ chdat->tx ? "tx" : "rx",
+ chdat->ch >= 0 ? "dedicated" : "shared",
+ chdat->ch >= 0 ? chdat->ch : tusb_dma->ch,
+ chdat->dmareq >= 0 ? chdat->dmareq : tusb_dma->dmareq,
+ chdat->sync_dev >= 0 ? chdat->sync_dev : tusb_dma->sync_dev);
+
+ return channel;
+
+free_dmareq:
+ tusb_omap_dma_free_dmareq(chdat);
+
+ DBG(3, "ep%i: Could not get a DMA channel\n", chdat->epnum);
+ channel->status = MUSB_DMA_STATUS_UNKNOWN;
+
+ return NULL;
+}
+
+static void tusb_omap_dma_release(struct dma_channel *channel)
+{
+ struct tusb_omap_dma_ch *chdat = to_chdat(channel);
+ struct musb *musb = chdat->musb;
+ void __iomem *tbase = musb->ctrl_base;
+ u32 reg;
+
+ DBG(3, "ep%i ch%i\n", chdat->epnum, chdat->ch);
+
+ reg = musb_readl(tbase, TUSB_DMA_INT_MASK);
+ if (chdat->tx)
+ reg |= (1 << chdat->epnum);
+ else
+ reg |= (1 << (chdat->epnum + 15));
+ musb_writel(tbase, TUSB_DMA_INT_MASK, reg);
+
+ reg = musb_readl(tbase, TUSB_DMA_INT_CLEAR);
+ if (chdat->tx)
+ reg |= (1 << chdat->epnum);
+ else
+ reg |= (1 << (chdat->epnum + 15));
+ musb_writel(tbase, TUSB_DMA_INT_CLEAR, reg);
+
+ channel->status = MUSB_DMA_STATUS_UNKNOWN;
+
+ if (chdat->ch >= 0) {
+ omap_stop_dma(chdat->ch);
+ omap_free_dma(chdat->ch);
+ chdat->ch = -1;
+ }
+
+ if (chdat->dmareq >= 0)
+ tusb_omap_dma_free_dmareq(chdat);
+
+ channel = NULL;
+}
+
+void dma_controller_destroy(struct dma_controller *c)
+{
+ struct tusb_omap_dma *tusb_dma;
+ int i;
+
+ tusb_dma = container_of(c, struct tusb_omap_dma, controller);
+ for (i = 0; i < MAX_DMAREQ; i++) {
+ struct dma_channel *ch = dma_channel_pool[i];
+ if (ch) {
+ kfree(ch->private_data);
+ kfree(ch);
+ }
+ }
+
+ if (!tusb_dma->multichannel && tusb_dma && tusb_dma->ch >= 0)
+ omap_free_dma(tusb_dma->ch);
+
+ kfree(tusb_dma);
+}
+
+struct dma_controller *__init
+dma_controller_create(struct musb *musb, void __iomem *base)
+{
+ void __iomem *tbase = musb->ctrl_base;
+ struct tusb_omap_dma *tusb_dma;
+ int i;
+
+ /* REVISIT: Get dmareq lines used from board-*.c */
+
+ musb_writel(musb->ctrl_base, TUSB_DMA_INT_MASK, 0x7fffffff);
+ musb_writel(musb->ctrl_base, TUSB_DMA_EP_MAP, 0);
+
+ musb_writel(tbase, TUSB_DMA_REQ_CONF,
+ TUSB_DMA_REQ_CONF_BURST_SIZE(2)
+ | TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f)
+ | TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2));
+
+ tusb_dma = kzalloc(sizeof(struct tusb_omap_dma), GFP_KERNEL);
+ if (!tusb_dma)
+ goto cleanup;
+
+ tusb_dma->musb = musb;
+ tusb_dma->tbase = musb->ctrl_base;
+
+ tusb_dma->ch = -1;
+ tusb_dma->dmareq = -1;
+ tusb_dma->sync_dev = -1;
+
+ tusb_dma->controller.start = tusb_omap_dma_start;
+ tusb_dma->controller.stop = tusb_omap_dma_stop;
+ tusb_dma->controller.channel_alloc = tusb_omap_dma_allocate;
+ tusb_dma->controller.channel_release = tusb_omap_dma_release;
+ tusb_dma->controller.channel_program = tusb_omap_dma_program;
+ tusb_dma->controller.channel_abort = tusb_omap_dma_abort;
+
+ if (tusb_get_revision(musb) >= TUSB_REV_30)
+ tusb_dma->multichannel = 1;
+
+ for (i = 0; i < MAX_DMAREQ; i++) {
+ struct dma_channel *ch;
+ struct tusb_omap_dma_ch *chdat;
+
+ ch = kzalloc(sizeof(struct dma_channel), GFP_KERNEL);
+ if (!ch)
+ goto cleanup;
+
+ dma_channel_pool[i] = ch;
+
+ chdat = kzalloc(sizeof(struct tusb_omap_dma_ch), GFP_KERNEL);
+ if (!chdat)
+ goto cleanup;
+
+ ch->status = MUSB_DMA_STATUS_UNKNOWN;
+ ch->private_data = chdat;
+ }
+
+ return &tusb_dma->controller;
+
+cleanup:
+ dma_controller_destroy(&tusb_dma->controller);
+
+ return NULL;
+}
--- /dev/null
- #include <asm/arch/hardware.h>
- #include <asm/arch/board.h>
- #include <asm/arch/mux.h>
+/*
+ * drivers/video/backlight/omap_bl.c
+ *
+ * Backlight driver for OMAP based boards.
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This package 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 package 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 package; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+
++#include <mach/hardware.h>
++#include <mach/board.h>
++#include <mach/mux.h>
+
+#define OMAPBL_MAX_INTENSITY 0xff
+
+struct omap_backlight {
+ int powermode;
+ int current_intensity;
+
+ struct device *dev;
+ struct omap_backlight_config *pdata;
+};
+
+static void inline omapbl_send_intensity(int intensity)
+{
+ omap_writeb(intensity, OMAP_PWL_ENABLE);
+}
+
+static void inline omapbl_send_enable(int enable)
+{
+ omap_writeb(enable, OMAP_PWL_CLK_ENABLE);
+}
+
+static void omapbl_blank(struct omap_backlight *bl, int mode)
+{
+ if (bl->pdata->set_power)
+ bl->pdata->set_power(bl->dev, mode);
+
+ switch (mode) {
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_POWERDOWN:
+ omapbl_send_intensity(0);
+ omapbl_send_enable(0);
+ break;
+
+ case FB_BLANK_UNBLANK:
+ omapbl_send_intensity(bl->current_intensity);
+ omapbl_send_enable(1);
+ break;
+ }
+}
+
+#ifdef CONFIG_PM
+static int omapbl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct backlight_device *dev = platform_get_drvdata(pdev);
+ struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
+
+ omapbl_blank(bl, FB_BLANK_POWERDOWN);
+ return 0;
+}
+
+static int omapbl_resume(struct platform_device *pdev)
+{
+ struct backlight_device *dev = platform_get_drvdata(pdev);
+ struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
+
+ omapbl_blank(bl, bl->powermode);
+ return 0;
+}
+#else
+#define omapbl_suspend NULL
+#define omapbl_resume NULL
+#endif
+
+static int omapbl_set_power(struct backlight_device *dev, int state)
+{
+ struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
+
+ omapbl_blank(bl, state);
+ bl->powermode = state;
+
+ return 0;
+}
+
+static int omapbl_update_status(struct backlight_device *dev)
+{
+ struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
+
+ if (bl->current_intensity != dev->props.brightness) {
+ if (dev->props.brightness < 0)
+ return -EPERM; /* Leave current_intensity untouched */
+
+ if (bl->powermode == FB_BLANK_UNBLANK)
+ omapbl_send_intensity(dev->props.brightness);
+ bl->current_intensity = dev->props.brightness;
+ }
+
+ if (dev->props.fb_blank != bl->powermode)
+ omapbl_set_power(dev, dev->props.fb_blank);
+
+ return 0;
+}
+
+
+static int omapbl_get_intensity(struct backlight_device *dev)
+{
+ struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
+ return bl->current_intensity;
+}
+
+static struct backlight_ops omapbl_ops = {
+ .get_brightness = omapbl_get_intensity,
+ .update_status = omapbl_update_status,
+};
+
+
+static int omapbl_probe(struct platform_device *pdev)
+{
+ struct backlight_device *dev;
+ struct omap_backlight *bl;
+ struct omap_backlight_config *pdata = pdev->dev.platform_data;
+
+ if (!pdata)
+ return -ENXIO;
+
+ omapbl_ops.check_fb = pdata->check_fb;
+
+ bl = kzalloc(sizeof(struct omap_backlight), GFP_KERNEL);
+ if (unlikely(!bl))
+ return -ENOMEM;
+
+ dev = backlight_device_register("omap-bl", &pdev->dev,
+ bl, &omapbl_ops);
+ if (IS_ERR(dev)) {
+ kfree(bl);
+ return PTR_ERR(dev);
+ }
+
+ bl->powermode = FB_BLANK_POWERDOWN;
+ bl->current_intensity = 0;
+
+ bl->pdata = pdata;
+ bl->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, dev);
+
+ omap_cfg_reg(PWL); /* Conflicts with UART3 */
+
+ dev->props.fb_blank = FB_BLANK_UNBLANK;
+ dev->props.max_brightness = OMAPBL_MAX_INTENSITY;
+ dev->props.brightness = pdata->default_intensity;
+ omapbl_update_status(dev);
+
+ printk(KERN_INFO "OMAP LCD backlight initialised\n");
+
+ return 0;
+}
+
+static int omapbl_remove(struct platform_device *pdev)
+{
+ struct backlight_device *dev = platform_get_drvdata(pdev);
+ struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
+
+ backlight_device_unregister(dev);
+ kfree(bl);
+
+ return 0;
+}
+
+static struct platform_driver omapbl_driver = {
+ .probe = omapbl_probe,
+ .remove = omapbl_remove,
+ .suspend = omapbl_suspend,
+ .resume = omapbl_resume,
+ .driver = {
+ .name = "omap-bl",
+ },
+};
+
+static int __init omapbl_init(void)
+{
+ return platform_driver_register(&omapbl_driver);
+}
+
+static void __exit omapbl_exit(void)
+{
+ platform_driver_unregister(&omapbl_driver);
+}
+
+module_init(omapbl_init);
+module_exit(omapbl_exit);
+
+MODULE_AUTHOR("Andrzej Zaborowski <balrog@zabor.org>");
+MODULE_DESCRIPTION("OMAP LCD Backlight driver");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/gpio.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/omapfb.h>
+/*
+ * LCD panel support for the TI 2430SDP board
+ *
+ * Copyright (C) 2007 MontaVista
+ * Author: Hunyue Yau <hyau@mvista.com>
+ *
+ * Derived from drivers/video/omap/lcd-apollon.c
+ *
+ * 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/platform_device.h>
+#include <linux/i2c/twl4030.h>
+
++#include <mach/gpio.h>
++#include <mach/mux.h>
++#include <mach/omapfb.h>
+#include <asm/mach-types.h>
+
+#define SDP2430_LCD_PANEL_BACKLIGHT_GPIO 91
+#define SDP2430_LCD_PANEL_ENABLE_GPIO 154
+#define SDP3430_LCD_PANEL_BACKLIGHT_GPIO 24
+#define SDP3430_LCD_PANEL_ENABLE_GPIO 28
+
+static unsigned backlight_gpio;
+static unsigned enable_gpio;
+
+#define LCD_PIXCLOCK_MAX 5400 /* freq 5.4 MHz */
+#define PM_RECEIVER TWL4030_MODULE_PM_RECEIVER
+#define ENABLE_VAUX2_DEDICATED 0x09
+#define ENABLE_VAUX2_DEV_GRP 0x20
+#define ENABLE_VAUX3_DEDICATED 0x03
+#define ENABLE_VAUX3_DEV_GRP 0x20
+
+
+#define t2_out(c, r, v) twl4030_i2c_write_u8(c, r, v)
+
+
+static int sdp2430_panel_init(struct lcd_panel *panel,
+ struct omapfb_device *fbdev)
+{
+ if (machine_is_omap_3430sdp()) {
+ enable_gpio = SDP3430_LCD_PANEL_ENABLE_GPIO;
+ backlight_gpio = SDP3430_LCD_PANEL_BACKLIGHT_GPIO;
+ } else {
+ enable_gpio = SDP2430_LCD_PANEL_ENABLE_GPIO;
+ backlight_gpio = SDP2430_LCD_PANEL_BACKLIGHT_GPIO;
+ }
+
+ omap_request_gpio(enable_gpio); /* LCD panel */
+ omap_request_gpio(backlight_gpio); /* LCD backlight */
+ omap_set_gpio_direction(enable_gpio, 0); /* output */
+ omap_set_gpio_direction(backlight_gpio, 0); /* output */
+
+ return 0;
+}
+
+static void sdp2430_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int sdp2430_panel_enable(struct lcd_panel *panel)
+{
+ u8 ded_val, ded_reg;
+ u8 grp_val, grp_reg;
+
+ if (machine_is_omap_3430sdp()) {
+ ded_reg = TWL4030_VAUX3_DEDICATED;
+ ded_val = ENABLE_VAUX3_DEDICATED;
+ grp_reg = TWL4030_VAUX3_DEV_GRP;
+ grp_val = ENABLE_VAUX3_DEV_GRP;
+ } else {
+ ded_reg = TWL4030_VAUX2_DEDICATED;
+ ded_val = ENABLE_VAUX2_DEDICATED;
+ grp_reg = TWL4030_VAUX2_DEV_GRP;
+ grp_val = ENABLE_VAUX2_DEV_GRP;
+ }
+
+ omap_set_gpio_dataout(enable_gpio, 1);
+ omap_set_gpio_dataout(backlight_gpio, 1);
+
+ if (0 != t2_out(PM_RECEIVER, ded_val, ded_reg))
+ return -EIO;
+ if (0 != t2_out(PM_RECEIVER, grp_val, grp_reg))
+ return -EIO;
+
+ return 0;
+}
+
+static void sdp2430_panel_disable(struct lcd_panel *panel)
+{
+ omap_set_gpio_dataout(enable_gpio, 0);
+ omap_set_gpio_dataout(backlight_gpio, 0);
+}
+
+static unsigned long sdp2430_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+struct lcd_panel sdp2430_panel = {
+ .name = "sdp2430",
+ .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+ OMAP_LCDC_INV_HSYNC,
+
+ .bpp = 16,
+ .data_lines = 16,
+ .x_res = 240,
+ .y_res = 320,
+ .hsw = 3, /* hsync_len (4) - 1 */
+ .hfp = 3, /* right_margin (4) - 1 */
+ .hbp = 39, /* left_margin (40) - 1 */
+ .vsw = 1, /* vsync_len (2) - 1 */
+ .vfp = 2, /* lower_margin */
+ .vbp = 7, /* upper_margin (8) - 1 */
+
+ .pixel_clock = LCD_PIXCLOCK_MAX,
+
+ .init = sdp2430_panel_init,
+ .cleanup = sdp2430_panel_cleanup,
+ .enable = sdp2430_panel_enable,
+ .disable = sdp2430_panel_disable,
+ .get_caps = sdp2430_panel_get_caps,
+};
+
+static int sdp2430_panel_probe(struct platform_device *pdev)
+{
+ omapfb_register_panel(&sdp2430_panel);
+ return 0;
+}
+
+static int sdp2430_panel_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int sdp2430_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ return 0;
+}
+
+static int sdp2430_panel_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct platform_driver sdp2430_panel_driver = {
+ .probe = sdp2430_panel_probe,
+ .remove = sdp2430_panel_remove,
+ .suspend = sdp2430_panel_suspend,
+ .resume = sdp2430_panel_resume,
+ .driver = {
+ .name = "sdp2430_lcd",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sdp2430_panel_drv_init(void)
+{
+ return platform_driver_register(&sdp2430_panel_driver);
+}
+
+static void __exit sdp2430_panel_drv_exit(void)
+{
+ platform_driver_unregister(&sdp2430_panel_driver);
+}
+
+module_init(sdp2430_panel_drv_init);
+module_exit(sdp2430_panel_drv_exit);
--- /dev/null
- #include <asm/arch/board-ams-delta.h>
- #include <asm/arch/hardware.h>
- #include <asm/arch/omapfb.h>
+/*
+ * File: drivers/video/omap/lcd_ams_delta.c
+ *
+ * Based on drivers/video/omap/lcd_inn1510.c
+ *
+ * LCD panel support for the Amstrad E3 (Delta) videophone.
+ *
+ * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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/platform_device.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+
++#include <mach/board-ams-delta.h>
++#include <mach/hardware.h>
++#include <mach/omapfb.h>
+
+#define AMS_DELTA_DEFAULT_CONTRAST 112
+
+static int ams_delta_panel_init(struct lcd_panel *panel,
+ struct omapfb_device *fbdev)
+{
+ return 0;
+}
+
+static void ams_delta_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int ams_delta_panel_enable(struct lcd_panel *panel)
+{
+ ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP,
+ AMS_DELTA_LATCH2_LCD_NDISP);
+ ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN,
+ AMS_DELTA_LATCH2_LCD_VBLEN);
+
+ omap_writeb(1, OMAP_PWL_CLK_ENABLE);
+ omap_writeb(AMS_DELTA_DEFAULT_CONTRAST, OMAP_PWL_ENABLE);
+
+ return 0;
+}
+
+static void ams_delta_panel_disable(struct lcd_panel *panel)
+{
+ ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN, 0);
+ ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP, 0);
+}
+
+static unsigned long ams_delta_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+static struct lcd_panel ams_delta_panel = {
+ .name = "ams-delta",
+ .config = 0,
+
+ .bpp = 12,
+ .data_lines = 16,
+ .x_res = 480,
+ .y_res = 320,
+ .pixel_clock = 4687,
+ .hsw = 3,
+ .hfp = 1,
+ .hbp = 1,
+ .vsw = 1,
+ .vfp = 0,
+ .vbp = 0,
+ .pcd = 0,
+ .acb = 37,
+
+ .init = ams_delta_panel_init,
+ .cleanup = ams_delta_panel_cleanup,
+ .enable = ams_delta_panel_enable,
+ .disable = ams_delta_panel_disable,
+ .get_caps = ams_delta_panel_get_caps,
+};
+
+static int ams_delta_panel_probe(struct platform_device *pdev)
+{
+ omapfb_register_panel(&ams_delta_panel);
+ return 0;
+}
+
+static int ams_delta_panel_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int ams_delta_panel_suspend(struct platform_device *pdev,
+ pm_message_t mesg)
+{
+ return 0;
+}
+
+static int ams_delta_panel_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct platform_driver ams_delta_panel_driver = {
+ .probe = ams_delta_panel_probe,
+ .remove = ams_delta_panel_remove,
+ .suspend = ams_delta_panel_suspend,
+ .resume = ams_delta_panel_resume,
+ .driver = {
+ .name = "lcd_ams_delta",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int ams_delta_panel_drv_init(void)
+{
+ return platform_driver_register(&ams_delta_panel_driver);
+}
+
+static void ams_delta_panel_drv_cleanup(void)
+{
+ platform_driver_unregister(&ams_delta_panel_driver);
+}
+
+module_init(ams_delta_panel_drv_init);
+module_exit(ams_delta_panel_drv_cleanup);
--- /dev/null
- #include <asm/arch/gpio.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/omapfb.h>
+/*
+ * LCD panel support for the Samsung OMAP2 Apollon board
+ *
+ * Copyright (C) 2005,2006 Samsung Electronics
+ * Author: Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * Derived from drivers/video/omap/lcd-h4.c
+ *
+ * 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/platform_device.h>
+
++#include <mach/gpio.h>
++#include <mach/mux.h>
++#include <mach/omapfb.h>
+
+/* #define USE_35INCH_LCD 1 */
+
+static int apollon_panel_init(struct lcd_panel *panel,
+ struct omapfb_device *fbdev)
+{
+ /* configure LCD PWR_EN */
+ omap_cfg_reg(M21_242X_GPIO11);
+ return 0;
+}
+
+static void apollon_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int apollon_panel_enable(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+static void apollon_panel_disable(struct lcd_panel *panel)
+{
+}
+
+static unsigned long apollon_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+struct lcd_panel apollon_panel = {
+ .name = "apollon",
+ .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+ OMAP_LCDC_INV_HSYNC,
+
+ .bpp = 16,
+ .data_lines = 18,
+#ifdef USE_35INCH_LCD
+ .x_res = 240,
+ .y_res = 320,
+ .hsw = 2,
+ .hfp = 3,
+ .hbp = 9,
+ .vsw = 4,
+ .vfp = 3,
+ .vbp = 5,
+#else
+ .x_res = 480,
+ .y_res = 272,
+ .hsw = 41,
+ .hfp = 2,
+ .hbp = 2,
+ .vsw = 10,
+ .vfp = 2,
+ .vbp = 2,
+#endif
+ .pixel_clock = 6250,
+
+ .init = apollon_panel_init,
+ .cleanup = apollon_panel_cleanup,
+ .enable = apollon_panel_enable,
+ .disable = apollon_panel_disable,
+ .get_caps = apollon_panel_get_caps,
+};
+
+static int apollon_panel_probe(struct platform_device *pdev)
+{
+ omapfb_register_panel(&apollon_panel);
+ return 0;
+}
+
+static int apollon_panel_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int apollon_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ return 0;
+}
+
+static int apollon_panel_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct platform_driver apollon_panel_driver = {
+ .probe = apollon_panel_probe,
+ .remove = apollon_panel_remove,
+ .suspend = apollon_panel_suspend,
+ .resume = apollon_panel_resume,
+ .driver = {
+ .name = "apollon_lcd",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init apollon_panel_drv_init(void)
+{
+ return platform_driver_register(&apollon_panel_driver);
+}
+
+static void __exit apollon_panel_drv_exit(void)
+{
+ platform_driver_unregister(&apollon_panel_driver);
+}
+
+module_init(apollon_panel_drv_init);
+module_exit(apollon_panel_drv_exit);
--- /dev/null
- #include <asm/arch/mux.h>
- #include <asm/arch/omapfb.h>
+/*
+ * LCD panel support for the TI OMAP H2 board
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * 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/platform_device.h>
+#include <linux/spi/tsc2101.h>
+
++#include <mach/mux.h>
++#include <mach/omapfb.h>
+
+static struct {
+ struct platform_device *lcd_dev;
+ struct spi_device *tsc2101_dev;
+} h2_panel_dev;
+
+static int h2_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
+{
+ return 0;
+}
+
+static void h2_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int h2_panel_enable(struct lcd_panel *panel)
+{
+ int r;
+
+ /*
+ * Assert LCD_EN, BKLIGHT_EN pins on LCD panel
+ * page2, GPIO config reg, GPIO(0,1) to out and asserted
+ */
+ r = tsc2101_write_sync(h2_panel_dev.tsc2101_dev, 2, 0x23, 0xcc00);
+ if (r < 0)
+ dev_err(&h2_panel_dev.lcd_dev->dev,
+ "failed to enable LCD panel\n");
+
+ return r;
+}
+
+static void h2_panel_disable(struct lcd_panel *panel)
+{
+ /*
+ * Deassert LCD_EN and BKLIGHT_EN pins on LCD panel
+ * page2, GPIO config reg, GPIO(0,1) to out and deasserted
+ */
+ if (tsc2101_write_sync(h2_panel_dev.tsc2101_dev, 2, 0x23, 0x8800))
+ dev_err(&h2_panel_dev.lcd_dev->dev,
+ "failed to disable LCD panel\n");
+}
+
+static unsigned long h2_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+struct lcd_panel h2_panel = {
+ .name = "h2",
+ .config = OMAP_LCDC_PANEL_TFT,
+
+ .bpp = 16,
+ .data_lines = 16,
+ .x_res = 240,
+ .y_res = 320,
+ .pixel_clock = 5000,
+ .hsw = 12,
+ .hfp = 12,
+ .hbp = 46,
+ .vsw = 1,
+ .vfp = 1,
+ .vbp = 0,
+
+ .init = h2_panel_init,
+ .cleanup = h2_panel_cleanup,
+ .enable = h2_panel_enable,
+ .disable = h2_panel_disable,
+ .get_caps = h2_panel_get_caps,
+};
+
+static int h2_panel_probe(struct platform_device *pdev)
+{
+ struct spi_device *tsc2101;
+
+ tsc2101 = pdev->dev.platform_data;
+ if (tsc2101 == NULL) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -ENODEV;
+ }
+ if (strncmp(tsc2101->modalias, "tsc2101", 8) != 0) {
+ dev_err(&pdev->dev, "tsc2101 not found\n");
+ return -EINVAL;
+ }
+ h2_panel_dev.lcd_dev = pdev;
+ h2_panel_dev.tsc2101_dev = tsc2101;
+ omapfb_register_panel(&h2_panel);
+ return 0;
+}
+
+static int h2_panel_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int h2_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ return 0;
+}
+
+static int h2_panel_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct platform_driver h2_panel_driver = {
+ .probe = h2_panel_probe,
+ .remove = h2_panel_remove,
+ .suspend = h2_panel_suspend,
+ .resume = h2_panel_resume,
+ .driver = {
+ .name = "lcd_h2",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int h2_panel_drv_init(void)
+{
+ return platform_driver_register(&h2_panel_driver);
+}
+
+static void h2_panel_drv_cleanup(void)
+{
+ platform_driver_unregister(&h2_panel_driver);
+}
+
+module_init(h2_panel_drv_init);
+module_exit(h2_panel_drv_cleanup);
+
--- /dev/null
- #include <asm/arch/omapfb.h>
- #include <asm/arch/lcd_mipid.h>
+/*
+ * LCD driver for MIPI DBI-C / DCS compatible LCDs
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * 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/device.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+
++#include <mach/omapfb.h>
++#include <mach/lcd_mipid.h>
+
+#include "../../cbus/tahvo.h"
+
+#define MIPID_MODULE_NAME "lcd_mipid"
+
+#define MIPID_CMD_READ_DISP_ID 0x04
+#define MIPID_CMD_READ_RED 0x06
+#define MIPID_CMD_READ_GREEN 0x07
+#define MIPID_CMD_READ_BLUE 0x08
+#define MIPID_CMD_READ_DISP_STATUS 0x09
+#define MIPID_CMD_RDDSDR 0x0F
+#define MIPID_CMD_SLEEP_IN 0x10
+#define MIPID_CMD_SLEEP_OUT 0x11
+#define MIPID_CMD_DISP_OFF 0x28
+#define MIPID_CMD_DISP_ON 0x29
+
+#define MIPID_VER_LPH8923 3
+#define MIPID_VER_LS041Y3 4
+
+#define MIPID_ESD_CHECK_PERIOD msecs_to_jiffies(5000)
+
+#define to_mipid_device(p) container_of(p, struct mipid_device, \
+ panel)
+struct mipid_device {
+ int enabled;
+ int model;
+ int revision;
+ u8 display_id[3];
+ unsigned int saved_bklight_level;
+ unsigned long hw_guard_end; /* next value of jiffies
+ when we can issue the
+ next sleep in/out command */
+ unsigned long hw_guard_wait; /* max guard time in jiffies */
+
+ struct omapfb_device *fbdev;
+ struct spi_device *spi;
+ struct mutex mutex;
+ struct lcd_panel panel;
+
+ struct workqueue_struct *esd_wq;
+ struct delayed_work esd_work;
+ void (*esd_check)(struct mipid_device *m);
+};
+
+static void mipid_transfer(struct mipid_device *md, int cmd, const u8 *wbuf,
+ int wlen, u8 *rbuf, int rlen)
+{
+ struct spi_message m;
+ struct spi_transfer *x, xfer[4];
+ u16 w;
+ int r;
+
+ BUG_ON(md->spi == NULL);
+
+ spi_message_init(&m);
+
+ memset(xfer, 0, sizeof(xfer));
+ x = &xfer[0];
+
+ cmd &= 0xff;
+ x->tx_buf = &cmd;
+ x->bits_per_word= 9;
+ x->len = 2;
+ spi_message_add_tail(x, &m);
+
+ if (wlen) {
+ x++;
+ x->tx_buf = wbuf;
+ x->len = wlen;
+ x->bits_per_word= 9;
+ spi_message_add_tail(x, &m);
+ }
+
+ if (rlen) {
+ x++;
+ x->rx_buf = &w;
+ x->len = 1;
+ spi_message_add_tail(x, &m);
+
+ if (rlen > 1) {
+ /* Arrange for the extra clock before the first
+ * data bit.
+ */
+ x->bits_per_word = 9;
+ x->len = 2;
+
+ x++;
+ x->rx_buf = &rbuf[1];
+ x->len = rlen - 1;
+ spi_message_add_tail(x, &m);
+ }
+ }
+
+ r = spi_sync(md->spi, &m);
+ if (r < 0)
+ dev_dbg(&md->spi->dev, "spi_sync %d\n", r);
+
+ if (rlen)
+ rbuf[0] = w & 0xff;
+}
+
+static inline void mipid_cmd(struct mipid_device *md, int cmd)
+{
+ mipid_transfer(md, cmd, NULL, 0, NULL, 0);
+}
+
+static inline void mipid_write(struct mipid_device *md,
+ int reg, const u8 *buf, int len)
+{
+ mipid_transfer(md, reg, buf, len, NULL, 0);
+}
+
+static inline void mipid_read(struct mipid_device *md,
+ int reg, u8 *buf, int len)
+{
+ mipid_transfer(md, reg, NULL, 0, buf, len);
+}
+
+static void set_data_lines(struct mipid_device *md, int data_lines)
+{
+ u16 par;
+
+ switch (data_lines) {
+ case 16:
+ par = 0x150;
+ break;
+ case 18:
+ par = 0x160;
+ break;
+ case 24:
+ par = 0x170;
+ break;
+ }
+ mipid_write(md, 0x3a, (u8 *)&par, 2);
+}
+
+static void send_init_string(struct mipid_device *md)
+{
+ u16 initpar[] = { 0x0102, 0x0100, 0x0100 };
+
+ mipid_write(md, 0xc2, (u8 *)initpar, sizeof(initpar));
+ set_data_lines(md, md->panel.data_lines);
+}
+
+static void hw_guard_start(struct mipid_device *md, int guard_msec)
+{
+ md->hw_guard_wait = msecs_to_jiffies(guard_msec);
+ md->hw_guard_end = jiffies + md->hw_guard_wait;
+}
+
+static void hw_guard_wait(struct mipid_device *md)
+{
+ unsigned long wait = md->hw_guard_end - jiffies;
+
+ if ((long)wait > 0 && wait <= md->hw_guard_wait) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(wait);
+ }
+}
+
+static void set_sleep_mode(struct mipid_device *md, int on)
+{
+ int cmd, sleep_time = 50;
+
+ if (on)
+ cmd = MIPID_CMD_SLEEP_IN;
+ else
+ cmd = MIPID_CMD_SLEEP_OUT;
+ hw_guard_wait(md);
+ mipid_cmd(md, cmd);
+ hw_guard_start(md, 120);
+ /*
+ * When we enable the panel, it seems we _have_ to sleep
+ * 120 ms before sending the init string. When disabling the
+ * panel we'll sleep for the duration of 2 frames, so that the
+ * controller can still provide the PCLK,HS,VS signals. */
+ if (!on)
+ sleep_time = 120;
+ msleep(sleep_time);
+}
+
+static void set_display_state(struct mipid_device *md, int enabled)
+{
+ int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF;
+
+ mipid_cmd(md, cmd);
+}
+
+static int mipid_set_bklight_level(struct lcd_panel *panel, unsigned int level)
+{
+ struct mipid_device *md = to_mipid_device(panel);
+
+ if (level > tahvo_get_max_backlight_level())
+ return -EINVAL;
+ if (!md->enabled) {
+ md->saved_bklight_level = level;
+ return 0;
+ }
+ tahvo_set_backlight_level(level);
+
+ return 0;
+}
+
+static unsigned int mipid_get_bklight_level(struct lcd_panel *panel)
+{
+ return tahvo_get_backlight_level();
+}
+
+static unsigned int mipid_get_bklight_max(struct lcd_panel *panel)
+{
+ return tahvo_get_max_backlight_level();
+}
+
+
+static unsigned long mipid_get_caps(struct lcd_panel *panel)
+{
+ return OMAPFB_CAPS_SET_BACKLIGHT;
+}
+
+static u16 read_first_pixel(struct mipid_device *md)
+{
+ u16 pixel;
+ u8 red, green, blue;
+
+ mutex_lock(&md->mutex);
+ mipid_read(md, MIPID_CMD_READ_RED, &red, 1);
+ mipid_read(md, MIPID_CMD_READ_GREEN, &green, 1);
+ mipid_read(md, MIPID_CMD_READ_BLUE, &blue, 1);
+ mutex_unlock(&md->mutex);
+
+ switch (md->panel.data_lines) {
+ case 16:
+ pixel = ((red >> 1) << 11) | (green << 5) | (blue >> 1);
+ break;
+ case 24:
+ /* 24 bit -> 16 bit */
+ pixel = ((red >> 3) << 11) | ((green >> 2) << 5) |
+ (blue >> 3);
+ break;
+ default:
+ BUG();
+ }
+
+ return pixel;
+}
+
+static int mipid_run_test(struct lcd_panel *panel, int test_num)
+{
+ struct mipid_device *md = to_mipid_device(panel);
+ static const u16 test_values[4] = {
+ 0x0000, 0xffff, 0xaaaa, 0x5555,
+ };
+ int i;
+
+ if (test_num != MIPID_TEST_RGB_LINES)
+ return MIPID_TEST_INVALID;
+
+ for (i = 0; i < ARRAY_SIZE(test_values); i++) {
+ int delay;
+ unsigned long tmo;
+
+ omapfb_write_first_pixel(md->fbdev, test_values[i]);
+ tmo = jiffies + msecs_to_jiffies(100);
+ delay = 25;
+ while (1) {
+ u16 pixel;
+
+ msleep(delay);
+ pixel = read_first_pixel(md);
+ if (pixel == test_values[i])
+ break;
+ if (time_after(jiffies, tmo)) {
+ dev_err(&md->spi->dev,
+ "MIPI LCD RGB I/F test failed: "
+ "expecting %04x, got %04x\n",
+ test_values[i], pixel);
+ return MIPID_TEST_FAILED;
+ }
+ delay = 10;
+ }
+ }
+
+ return 0;
+}
+
+static void ls041y3_esd_recover(struct mipid_device *md)
+{
+ dev_err(&md->spi->dev, "performing LCD ESD recovery\n");
+ set_sleep_mode(md, 1);
+ set_sleep_mode(md, 0);
+}
+
+static void ls041y3_esd_check_mode1(struct mipid_device *md)
+{
+ u8 state1, state2;
+
+ mipid_read(md, MIPID_CMD_RDDSDR, &state1, 1);
+ set_sleep_mode(md, 0);
+ mipid_read(md, MIPID_CMD_RDDSDR, &state2, 1);
+ dev_dbg(&md->spi->dev, "ESD mode 1 state1 %02x state2 %02x\n",
+ state1, state2);
+ /* Each sleep out command will trigger a self diagnostic and flip
+ * Bit6 if the test passes.
+ */
+ if (!((state1 ^ state2) & (1 << 6)))
+ ls041y3_esd_recover(md);
+}
+
+static void ls041y3_esd_check_mode2(struct mipid_device *md)
+{
+ int i;
+ u8 rbuf[2];
+ static const struct {
+ int cmd;
+ int wlen;
+ u16 wbuf[3];
+ } *rd, rd_ctrl[7] = {
+ { 0xb0, 4, { 0x0101, 0x01fe, } },
+ { 0xb1, 4, { 0x01de, 0x0121, } },
+ { 0xc2, 4, { 0x0100, 0x0100, } },
+ { 0xbd, 2, { 0x0100, } },
+ { 0xc2, 4, { 0x01fc, 0x0103, } },
+ { 0xb4, 0, },
+ { 0x00, 0, },
+ };
+
+ rd = rd_ctrl;
+ for (i = 0; i < 3; i++, rd++)
+ mipid_write(md, rd->cmd, (u8 *)rd->wbuf, rd->wlen);
+
+ udelay(10);
+ mipid_read(md, rd->cmd, rbuf, 2);
+ rd++;
+
+ for (i = 0; i < 3; i++, rd++) {
+ udelay(10);
+ mipid_write(md, rd->cmd, (u8 *)rd->wbuf, rd->wlen);
+ }
+
+ dev_dbg(&md->spi->dev, "ESD mode 2 state %02x\n", rbuf[1]);
+ if (rbuf[1] == 0x00)
+ ls041y3_esd_recover(md);
+}
+
+static void ls041y3_esd_check(struct mipid_device *md)
+{
+ ls041y3_esd_check_mode1(md);
+ if (md->revision >= 0x88)
+ ls041y3_esd_check_mode2(md);
+}
+
+static void mipid_esd_start_check(struct mipid_device *md)
+{
+ if (md->esd_check != NULL)
+ queue_delayed_work(md->esd_wq, &md->esd_work,
+ MIPID_ESD_CHECK_PERIOD);
+}
+
+static void mipid_esd_stop_check(struct mipid_device *md)
+{
+ if (md->esd_check != NULL)
+ cancel_rearming_delayed_workqueue(md->esd_wq, &md->esd_work);
+}
+
+static void mipid_esd_work(struct work_struct *work)
+{
+ struct mipid_device *md = container_of(work, struct mipid_device, esd_work.work);
+
+ mutex_lock(&md->mutex);
+ md->esd_check(md);
+ mutex_unlock(&md->mutex);
+ mipid_esd_start_check(md);
+}
+
+static int mipid_enable(struct lcd_panel *panel)
+{
+ struct mipid_device *md = to_mipid_device(panel);
+
+ mutex_lock(&md->mutex);
+
+ if (md->enabled) {
+ mutex_unlock(&md->mutex);
+ return 0;
+ }
+ set_sleep_mode(md, 0);
+ md->enabled = 1;
+ send_init_string(md);
+ set_display_state(md, 1);
+ mipid_set_bklight_level(panel, md->saved_bklight_level);
+ mipid_esd_start_check(md);
+
+ mutex_unlock(&md->mutex);
+ return 0;
+}
+
+static void mipid_disable(struct lcd_panel *panel)
+{
+ struct mipid_device *md = to_mipid_device(panel);
+
+ /*
+ * A final ESD work might be called before returning,
+ * so do this without holding the lock.
+ */
+ mipid_esd_stop_check(md);
+ mutex_lock(&md->mutex);
+
+ if (!md->enabled) {
+ mutex_unlock(&md->mutex);
+ return;
+ }
+ md->saved_bklight_level = mipid_get_bklight_level(panel);
+ mipid_set_bklight_level(panel, 0);
+ set_display_state(md, 0);
+ set_sleep_mode(md, 1);
+ md->enabled = 0;
+
+ mutex_unlock(&md->mutex);
+}
+
+static int panel_enabled(struct mipid_device *md)
+{
+ u32 disp_status;
+ int enabled;
+
+ mipid_read(md, MIPID_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4);
+ disp_status = __be32_to_cpu(disp_status);
+ enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10));
+ dev_dbg(&md->spi->dev,
+ "LCD panel %senabled by bootloader (status 0x%04x)\n",
+ enabled ? "" : "not ", disp_status);
+ return enabled;
+}
+
+static int mipid_init(struct lcd_panel *panel,
+ struct omapfb_device *fbdev)
+{
+ struct mipid_device *md = to_mipid_device(panel);
+
+ md->fbdev = fbdev;
+ md->esd_wq = create_singlethread_workqueue("mipid_esd");
+ if (md->esd_wq == NULL) {
+ dev_err(&md->spi->dev, "can't create ESD workqueue\n");
+ return -ENOMEM;
+ }
+ INIT_DELAYED_WORK(&md->esd_work, mipid_esd_work);
+ mutex_init(&md->mutex);
+
+ md->enabled = panel_enabled(md);
+
+ if (md->enabled)
+ mipid_esd_start_check(md);
+ else
+ md->saved_bklight_level = mipid_get_bklight_level(panel);
+
+ return 0;
+}
+
+static void mipid_cleanup(struct lcd_panel *panel)
+{
+ struct mipid_device *md = to_mipid_device(panel);
+
+ if (md->enabled)
+ mipid_esd_stop_check(md);
+ destroy_workqueue(md->esd_wq);
+}
+
+static struct lcd_panel mipid_panel = {
+ .config = OMAP_LCDC_PANEL_TFT,
+
+ .bpp = 16,
+ .x_res = 800,
+ .y_res = 480,
+ .pixel_clock = 21940,
+ .hsw = 50,
+ .hfp = 20,
+ .hbp = 15,
+ .vsw = 2,
+ .vfp = 1,
+ .vbp = 3,
+
+ .init = mipid_init,
+ .cleanup = mipid_cleanup,
+ .enable = mipid_enable,
+ .disable = mipid_disable,
+ .get_caps = mipid_get_caps,
+ .set_bklight_level= mipid_set_bklight_level,
+ .get_bklight_level= mipid_get_bklight_level,
+ .get_bklight_max= mipid_get_bklight_max,
+ .run_test = mipid_run_test,
+};
+
+static int mipid_detect(struct mipid_device *md)
+{
+ struct mipid_platform_data *pdata;
+
+ pdata = md->spi->dev.platform_data;
+ if (pdata == NULL) {
+ dev_err(&md->spi->dev, "missing platform data\n");
+ return -ENOENT;
+ }
+
+ mipid_read(md, MIPID_CMD_READ_DISP_ID, md->display_id, 3);
+ dev_dbg(&md->spi->dev, "MIPI display ID: %02x%02x%02x\n",
+ md->display_id[0], md->display_id[1], md->display_id[2]);
+
+ switch (md->display_id[0]) {
+ case 0x45:
+ md->model = MIPID_VER_LPH8923;
+ md->panel.name = "lph8923";
+ break;
+ case 0x83:
+ md->model = MIPID_VER_LS041Y3;
+ md->panel.name = "ls041y3";
+ md->esd_check = ls041y3_esd_check;
+ break;
+ default:
+ md->panel.name = "unknown";
+ dev_err(&md->spi->dev, "invalid display ID\n");
+ return -ENODEV;
+ }
+
+ md->revision = md->display_id[1];
+ md->panel.data_lines = pdata->data_lines;
+ pr_info("omapfb: %s rev %02x LCD detected\n",
+ md->panel.name, md->revision);
+
+ return 0;
+}
+
+static int mipid_spi_probe(struct spi_device *spi)
+{
+ struct mipid_device *md;
+ int r;
+
+ md = kzalloc(sizeof(*md), GFP_KERNEL);
+ if (md == NULL) {
+ dev_err(&spi->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+
+ spi->mode = SPI_MODE_0;
+ md->spi = spi;
+ dev_set_drvdata(&spi->dev, md);
+ md->panel = mipid_panel;
+
+ r = mipid_detect(md);
+ if (r < 0)
+ return r;
+
+ omapfb_register_panel(&md->panel);
+
+ return 0;
+}
+
+static int mipid_spi_remove(struct spi_device *spi)
+{
+ struct mipid_device *md = dev_get_drvdata(&spi->dev);
+
+ mipid_disable(&md->panel);
+ kfree(md);
+
+ return 0;
+}
+
+static struct spi_driver mipid_spi_driver = {
+ .driver = {
+ .name = MIPID_MODULE_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = mipid_spi_probe,
+ .remove = __devexit_p(mipid_spi_remove),
+};
+
+static int mipid_drv_init(void)
+{
+ spi_register_driver(&mipid_spi_driver);
+
+ return 0;
+}
+module_init(mipid_drv_init);
+
+static void mipid_drv_cleanup(void)
+{
+ spi_unregister_driver(&mipid_spi_driver);
+}
+module_exit(mipid_drv_cleanup);
+
+MODULE_DESCRIPTION("MIPI display driver");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/gpio.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/omapfb.h>
+/*
+ * LCD panel support for the MISTRAL OMAP2EVM board
+ *
+ * Author: Arun C <arunedarath@mistralsolutions.com>
+ *
+ * Derived from drivers/video/omap/lcd_omap3evm.c
+ * Derived from drivers/video/omap/lcd-apollon.c
+ *
+ * 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/platform_device.h>
+#include <linux/i2c/twl4030.h>
+
++#include <mach/gpio.h>
++#include <mach/mux.h>
++#include <mach/omapfb.h>
+#include <asm/mach-types.h>
+
+#define LCD_PANEL_ENABLE_GPIO 154
+#define LCD_PANEL_LR 128
+#define LCD_PANEL_UD 129
+#define LCD_PANEL_INI 152
+#define LCD_PANEL_QVGA 148
+#define LCD_PANEL_RESB 153
+
+#define LCD_XRES 480
+#define LCD_YRES 640
+#define LCD_PIXCLOCK_MAX 20000 /* in kHz */
+
+#define TWL_LED_LEDEN 0x00
+#define TWL_PWMA_PWMAON 0x00
+#define TWL_PWMA_PWMAOFF 0x01
+
+static unsigned int bklight_level;
+
+static int omap2evm_panel_init(struct lcd_panel *panel,
+ struct omapfb_device *fbdev)
+{
+ omap_request_gpio(LCD_PANEL_ENABLE_GPIO);
+ omap_request_gpio(LCD_PANEL_LR);
+ omap_request_gpio(LCD_PANEL_UD);
+ omap_request_gpio(LCD_PANEL_INI);
+ omap_request_gpio(LCD_PANEL_QVGA);
+ omap_request_gpio(LCD_PANEL_RESB);
+
+ omap_set_gpio_direction(LCD_PANEL_ENABLE_GPIO, 0);
+ omap_set_gpio_direction(LCD_PANEL_LR, 0);
+ omap_set_gpio_direction(LCD_PANEL_UD, 0);
+ omap_set_gpio_direction(LCD_PANEL_INI, 0);
+ omap_set_gpio_direction(LCD_PANEL_QVGA, 0);
+ omap_set_gpio_direction(LCD_PANEL_RESB, 0);
+
+ omap_set_gpio_dataout(LCD_PANEL_RESB, 1);
+ omap_set_gpio_dataout(LCD_PANEL_INI, 1);
+ omap_set_gpio_dataout(LCD_PANEL_QVGA, 0);
+ omap_set_gpio_dataout(LCD_PANEL_LR, 1);
+ omap_set_gpio_dataout(LCD_PANEL_UD, 1);
+
+ twl4030_i2c_write_u8(TWL4030_MODULE_LED, 0x11, TWL_LED_LEDEN);
+ twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x01, TWL_PWMA_PWMAON);
+ twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x02, TWL_PWMA_PWMAOFF);
+ bklight_level = 100;
+
+ return 0;
+}
+
+static void omap2evm_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int omap2evm_panel_enable(struct lcd_panel *panel)
+{
+ omap_set_gpio_dataout(LCD_PANEL_ENABLE_GPIO, 0);
+ return 0;
+}
+
+static void omap2evm_panel_disable(struct lcd_panel *panel)
+{
+ omap_set_gpio_dataout(LCD_PANEL_ENABLE_GPIO, 1);
+}
+
+static unsigned long omap2evm_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+static int omap2evm_bklight_setlevel(struct lcd_panel *panel,
+ unsigned int level)
+{
+ u8 c;
+ if ((level >= 0) && (level <= 100)) {
+ c = (125 * (100 - level)) / 100 + 2;
+ twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, c, TWL_PWMA_PWMAOFF);
+ bklight_level = level;
+ }
+ return 0;
+}
+
+static unsigned int omap2evm_bklight_getlevel(struct lcd_panel *panel)
+{
+ return bklight_level;
+}
+
+static unsigned int omap2evm_bklight_getmaxlevel(struct lcd_panel *panel)
+{
+ return 100;
+}
+
+struct lcd_panel omap2evm_panel = {
+ .name = "omap2evm",
+ .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+ OMAP_LCDC_INV_HSYNC,
+
+ .bpp = 16,
+ .data_lines = 18,
+ .x_res = LCD_XRES,
+ .y_res = LCD_YRES,
+ .hsw = 3,
+ .hfp = 0,
+ .hbp = 28,
+ .vsw = 2,
+ .vfp = 1,
+ .vbp = 0,
+
+ .pixel_clock = LCD_PIXCLOCK_MAX,
+
+ .init = omap2evm_panel_init,
+ .cleanup = omap2evm_panel_cleanup,
+ .enable = omap2evm_panel_enable,
+ .disable = omap2evm_panel_disable,
+ .get_caps = omap2evm_panel_get_caps,
+ .set_bklight_level = omap2evm_bklight_setlevel,
+ .get_bklight_level = omap2evm_bklight_getlevel,
+ .get_bklight_max = omap2evm_bklight_getmaxlevel,
+};
+
+static int omap2evm_panel_probe(struct platform_device *pdev)
+{
+ omapfb_register_panel(&omap2evm_panel);
+ return 0;
+}
+
+static int omap2evm_panel_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int omap2evm_panel_suspend(struct platform_device *pdev,
+ pm_message_t mesg)
+{
+ return 0;
+}
+
+static int omap2evm_panel_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct platform_driver omap2evm_panel_driver = {
+ .probe = omap2evm_panel_probe,
+ .remove = omap2evm_panel_remove,
+ .suspend = omap2evm_panel_suspend,
+ .resume = omap2evm_panel_resume,
+ .driver = {
+ .name = "omap2evm_lcd",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap2evm_panel_drv_init(void)
+{
+ return platform_driver_register(&omap2evm_panel_driver);
+}
+
+static void __exit omap2evm_panel_drv_exit(void)
+{
+ platform_driver_unregister(&omap2evm_panel_driver);
+}
+
+module_init(omap2evm_panel_drv_init);
+module_exit(omap2evm_panel_drv_exit);
--- /dev/null
- #include <asm/arch/gpio.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/omapfb.h>
+/*
+ * LCD panel support for the TI OMAP3 Beagle board
+ *
+ * Author: Koen Kooi <koen@openembedded.org>
+ *
+ * Derived from drivers/video/omap/lcd-omap3evm.c
+ *
+ * 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/platform_device.h>
+#include <linux/i2c/twl4030.h>
+
++#include <mach/gpio.h>
++#include <mach/mux.h>
++#include <mach/omapfb.h>
+#include <asm/mach-types.h>
+
+#define LCD_PANEL_ENABLE_GPIO 170
+
+#define LCD_XRES 1024
+#define LCD_YRES 768
+#define LCD_PIXCLOCK 64000 /* in kHz */
+
+static int omap3beagle_panel_init(struct lcd_panel *panel,
+ struct omapfb_device *fbdev)
+{
+ omap_request_gpio(LCD_PANEL_ENABLE_GPIO);
+ return 0;
+}
+
+static void omap3beagle_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int omap3beagle_panel_enable(struct lcd_panel *panel)
+{
+ omap_set_gpio_dataout(LCD_PANEL_ENABLE_GPIO, 1);
+ return 0;
+}
+
+static void omap3beagle_panel_disable(struct lcd_panel *panel)
+{
+ omap_set_gpio_dataout(LCD_PANEL_ENABLE_GPIO, 0);
+}
+
+static unsigned long omap3beagle_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+struct lcd_panel omap3beagle_panel = {
+ .name = "omap3beagle",
+ .config = OMAP_LCDC_PANEL_TFT,
+
+ .bpp = 24,
+ .data_lines = 24,
+ .x_res = LCD_XRES,
+ .y_res = LCD_YRES,
+ .hsw = 3, /* hsync_len (4) - 1 */
+ .hfp = 3, /* right_margin (4) - 1 */
+ .hbp = 39, /* left_margin (40) - 1 */
+ .vsw = 1, /* vsync_len (2) - 1 */
+ .vfp = 2, /* lower_margin */
+ .vbp = 7, /* upper_margin (8) - 1 */
+
+ .pixel_clock = LCD_PIXCLOCK,
+
+ .init = omap3beagle_panel_init,
+ .cleanup = omap3beagle_panel_cleanup,
+ .enable = omap3beagle_panel_enable,
+ .disable = omap3beagle_panel_disable,
+ .get_caps = omap3beagle_panel_get_caps,
+};
+
+static int omap3beagle_panel_probe(struct platform_device *pdev)
+{
+ omapfb_register_panel(&omap3beagle_panel);
+ return 0;
+}
+
+static int omap3beagle_panel_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int omap3beagle_panel_suspend(struct platform_device *pdev,
+ pm_message_t mesg)
+{
+ return 0;
+}
+
+static int omap3beagle_panel_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct platform_driver omap3beagle_panel_driver = {
+ .probe = omap3beagle_panel_probe,
+ .remove = omap3beagle_panel_remove,
+ .suspend = omap3beagle_panel_suspend,
+ .resume = omap3beagle_panel_resume,
+ .driver = {
+ .name = "omap3beagle_lcd",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap3beagle_panel_drv_init(void)
+{
+ return platform_driver_register(&omap3beagle_panel_driver);
+}
+
+static void __exit omap3beagle_panel_drv_exit(void)
+{
+ platform_driver_unregister(&omap3beagle_panel_driver);
+}
+
+module_init(omap3beagle_panel_drv_init);
+module_exit(omap3beagle_panel_drv_exit);
--- /dev/null
- #include <asm/arch/gpio.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/omapfb.h>
+/*
+ * LCD panel support for the TI OMAP3 EVM board
+ *
+ * Author: Steve Sakoman <steve@sakoman.com>
+ *
+ * Derived from drivers/video/omap/lcd-apollon.c
+ *
+ * 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/platform_device.h>
+#include <linux/i2c/twl4030.h>
+
++#include <mach/gpio.h>
++#include <mach/mux.h>
++#include <mach/omapfb.h>
+#include <asm/mach-types.h>
+
+#define LCD_PANEL_ENABLE_GPIO 153
+#define LCD_PANEL_LR 2
+#define LCD_PANEL_UD 3
+#define LCD_PANEL_INI 152
+#define LCD_PANEL_QVGA 154
+#define LCD_PANEL_RESB 155
+
+#define LCD_XRES 480
+#define LCD_YRES 640
+#define LCD_PIXCLOCK 26000 /* in kHz */
+
+#define ENABLE_VDAC_DEDICATED 0x03
+#define ENABLE_VDAC_DEV_GRP 0x20
+#define ENABLE_VPLL2_DEDICATED 0x05
+#define ENABLE_VPLL2_DEV_GRP 0xE0
+
+#define TWL_LED_LEDEN 0x00
+#define TWL_PWMA_PWMAON 0x00
+#define TWL_PWMA_PWMAOFF 0x01
+
+static unsigned int bklight_level;
+
+static int omap3evm_panel_init(struct lcd_panel *panel,
+ struct omapfb_device *fbdev)
+{
+ omap_request_gpio(LCD_PANEL_LR);
+ omap_request_gpio(LCD_PANEL_UD);
+ omap_request_gpio(LCD_PANEL_INI);
+ omap_request_gpio(LCD_PANEL_RESB);
+ omap_request_gpio(LCD_PANEL_QVGA);
+
+ omap_set_gpio_direction(LCD_PANEL_LR, 0);
+ omap_set_gpio_direction(LCD_PANEL_UD, 0);
+ omap_set_gpio_direction(LCD_PANEL_INI, 0);
+ omap_set_gpio_direction(LCD_PANEL_RESB, 0);
+ omap_set_gpio_direction(LCD_PANEL_QVGA, 0);
+
+ twl4030_i2c_write_u8(TWL4030_MODULE_LED, 0x11, TWL_LED_LEDEN);
+ twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x01, TWL_PWMA_PWMAON);
+ twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x02, TWL_PWMA_PWMAOFF);
+ bklight_level = 100;
+
+ omap_set_gpio_dataout(LCD_PANEL_RESB, 1);
+ omap_set_gpio_dataout(LCD_PANEL_INI, 1);
+ omap_set_gpio_dataout(LCD_PANEL_QVGA, 0);
+ omap_set_gpio_dataout(LCD_PANEL_LR, 1);
+ omap_set_gpio_dataout(LCD_PANEL_UD, 1);
+
+ return 0;
+}
+
+static void omap3evm_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int omap3evm_panel_enable(struct lcd_panel *panel)
+{
+ omap_set_gpio_dataout(LCD_PANEL_ENABLE_GPIO, 0);
+ return 0;
+}
+
+static void omap3evm_panel_disable(struct lcd_panel *panel)
+{
+ omap_set_gpio_dataout(LCD_PANEL_ENABLE_GPIO, 1);
+}
+
+static unsigned long omap3evm_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+static int omap3evm_bklight_setlevel(struct lcd_panel *panel,
+ unsigned int level)
+{
+ u8 c;
+ if ((level >= 0) && (level <= 100)) {
+ c = (125 * (100 - level)) / 100 + 2;
+ twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, c, TWL_PWMA_PWMAOFF);
+ bklight_level = level;
+ }
+ return 0;
+}
+
+static unsigned int omap3evm_bklight_getlevel(struct lcd_panel *panel)
+{
+ return bklight_level;
+}
+
+static unsigned int omap3evm_bklight_getmaxlevel(struct lcd_panel *panel)
+{
+ return 100;
+}
+
+struct lcd_panel omap3evm_panel = {
+ .name = "omap3evm",
+ .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+ OMAP_LCDC_INV_HSYNC,
+
+ .bpp = 16,
+ .data_lines = 18,
+ .x_res = LCD_XRES,
+ .y_res = LCD_YRES,
+ .hsw = 3, /* hsync_len (4) - 1 */
+ .hfp = 3, /* right_margin (4) - 1 */
+ .hbp = 39, /* left_margin (40) - 1 */
+ .vsw = 1, /* vsync_len (2) - 1 */
+ .vfp = 2, /* lower_margin */
+ .vbp = 7, /* upper_margin (8) - 1 */
+
+ .pixel_clock = LCD_PIXCLOCK,
+
+ .init = omap3evm_panel_init,
+ .cleanup = omap3evm_panel_cleanup,
+ .enable = omap3evm_panel_enable,
+ .disable = omap3evm_panel_disable,
+ .get_caps = omap3evm_panel_get_caps,
+ .set_bklight_level = omap3evm_bklight_setlevel,
+ .get_bklight_level = omap3evm_bklight_getlevel,
+ .get_bklight_max = omap3evm_bklight_getmaxlevel,
+};
+
+static int omap3evm_panel_probe(struct platform_device *pdev)
+{
+ omapfb_register_panel(&omap3evm_panel);
+ return 0;
+}
+
+static int omap3evm_panel_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int omap3evm_panel_suspend(struct platform_device *pdev,
+ pm_message_t mesg)
+{
+ return 0;
+}
+
+static int omap3evm_panel_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct platform_driver omap3evm_panel_driver = {
+ .probe = omap3evm_panel_probe,
+ .remove = omap3evm_panel_remove,
+ .suspend = omap3evm_panel_suspend,
+ .resume = omap3evm_panel_resume,
+ .driver = {
+ .name = "omap3evm_lcd",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap3evm_panel_drv_init(void)
+{
+ return platform_driver_register(&omap3evm_panel_driver);
+}
+
+static void __exit omap3evm_panel_drv_exit(void)
+{
+ platform_driver_unregister(&omap3evm_panel_driver);
+}
+
+module_init(omap3evm_panel_drv_init);
+module_exit(omap3evm_panel_drv_exit);
--- /dev/null
- #include <asm/arch/mux.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/omapfb.h>
+/*
+ * LCD panel support for the TI OMAP P2 board
+ *
+ * Authors:
+ * jekyll <jekyll@mail.jekyll.idv.tw>
+ * B Jp <lastjp_fr@yahoo.fr>
+ * Brian Swetland <swetland@android.com>
+ *
+ * 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/delay.h>
+#include <linux/platform_device.h>
+
++#include <mach/mux.h>
++#include <mach/gpio.h>
++#include <mach/omapfb.h>
+
+/*
+ * File: epson-md-tft.h
+ *
+ * This file contains definitions for Epsons MD-TF LCD Module
+ *
+ * Copyright (C) 2004 MPC-Data Limited (http://www.mpc-data.co.uk)
+ * Author: Dave Peverley <dpeverley at mpc-data.co.uk>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Please report all bugs and problems to the author.
+ *
+ */
+
+/* LCD uWire commands & params
+ * All values from Epson
+ */
+#define LCD_DISON 0xAF
+#define LCD_DISOFF 0xAE
+#define LCD_DISNOR 0xA6
+#define LCD_DISINV 0xA7
+#define LCD_DISCTL 0xCA
+#define LCD_GCP64 0xCB
+#define LCD_GCP16 0xCC
+#define LCD_GSSET 0xCD
+#define LCD_SLPIN 0x95
+#define LCD_SLPOUT 0x94
+#define LCD_SD_PSET 0x75
+#define LCD_MD_PSET 0x76
+#define LCD_SD_CSET 0x15
+#define LCD_MD_CSET 0x16
+#define LCD_DATCTL 0xBC
+#define LCD_RAMWR 0x5C
+#define LCD_RAMRD 0x5D
+#define LCD_PTLIN 0xA8
+#define LCD_PTLOUT 0xA9
+#define LCD_ASCSET 0xAA
+#define LCD_SCSTART 0xAB
+#define LCD_VOLCTL 0xC6
+#define LCD_NOP 0x25
+#define LCD_OSCISEL 0x7
+#define LCD_3500KSET 0xD1
+#define LCD_3500KEND 0xD2
+#define LCD_14MSET 0xD3
+#define LCD_14MEND 0xD4
+
+#define INIT_3500KSET 0x45
+#define INIT_14MSET 0x4B
+#define INIT_DATCTL 0x08 /* 6.6.6 bits for D-Sample */
+
+#define INIT_OSCISEL 0x05
+
+#define INIT_VOLCTL 0x77 /* Nominel "volume" */
+
+#define INIT_VOLCTL_Ton 0x98 /* Activate power-IC timer */
+#define INIT_GSSET 0x00
+
+const unsigned short INIT_DISCTL[11] =
+{
+ 0xDE, 0x01, 0x64, 0x00, 0x1B, 0xF4, 0x00, 0xDC, 0x00, 0x02, 0x00
+};
+
+const unsigned short INIT_GCP64[126] =
+{
+ 0x3B,0x00,0x42,0x00,0x4A,0x00,0x51,0x00,
+ 0x58,0x00,0x5F,0x00,0x66,0x00,0x6E,0x00,
+ 0x75,0x00,0x7C,0x00,0x83,0x00,0x8A,0x00,
+ 0x92,0x00,0x99,0x00,0xA0,0x00,0xA7,0x00,
+ 0xAE,0x00,0xB6,0x00,0xBD,0x00,0xC4,0x00,
+ 0xCB,0x00,0xD2,0x00,0xDA,0x00,0xE1,0x00,
+ 0xE8,0x00,0xEF,0x00,0xF6,0x00,0xFE,0x00,
+ 0x05,0x01,0x0C,0x01,0x13,0x01,0x1A,0x01,
+ 0x22,0x01,0x29,0x01,0x30,0x01,0x37,0x01,
+ 0x3E,0x01,0x46,0x01,0x4D,0x01,0x54,0x01,
+ 0x5B,0x01,0x62,0x01,0x6A,0x01,0x71,0x01,
+ 0x78,0x01,0x7F,0x01,0x86,0x01,0x8E,0x01,
+ 0x95,0x01,0x9C,0x01,0xA3,0x01,0xAA,0x01,
+ 0xB2,0x01,0xB9,0x01,0xC0,0x01,0xC7,0x01,
+ 0xCE,0x01,0xD6,0x01,0xDD,0x01,0xE4,0x01,
+ 0xEB,0x01,0xF2,0x01,0xFA,0x01
+};
+
+const unsigned short INIT_GCP16[15] =
+{
+ 0x1A,0x31,0x48,0x54,0x5F,0x67,0x70,0x76,0x7C,0x80,0x83,0x84,0x85,0x87,0x96
+};
+
+const unsigned short INIT_MD_PSET[4] = { 0, 0, 219, 0 };
+const unsigned short INIT_MD_CSET[4] = { 2, 0, 177, 0 };
+
+const unsigned short INIT_SD_PSET[4] = { 0x00, 0x01, 0x00, 0x01 };
+const unsigned short INIT_SD_CSET[4] = { 0x00, 0x02, 0x00, 0x02 };
+
+const unsigned short INIT_ASCSET[7] = { 0x00, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0x01 };
+const unsigned short INIT_SCSTART[2] = { 0x00, 0x00 };
+
+/* ----- end of epson_md_tft.h ----- */
+
+
+#include "../drivers/ssi/omap-uwire.h"
+
+#define LCD_UWIRE_CS 0
+
+static int p2_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
+{
+ return 0;
+}
+
+static void p2_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int p2_panel_enable(struct lcd_panel *panel)
+{
+ int i;
+ unsigned long value;
+
+ /* thwack the reset line */
+ omap_set_gpio_direction(19, 0);
+ omap_set_gpio_dataout(19, 0);
+ mdelay(2);
+ omap_set_gpio_dataout(19, 1);
+
+ /* bits 31:28 -> 0 LCD_PXL_15 .. 12 */
+ value = omap_readl(OMAP730_IO_CONF_3) & 0x0FFFFFFF;
+ omap_writel(value, OMAP730_IO_CONF_3);
+
+ /* bits 19:0 -> 0 LCD_VSYNC, AC, PXL_0, PCLK, HSYNC,
+ ** PXL_9..1, PXL_10, PXL_11
+ */
+ value = omap_readl(OMAP730_IO_CONF_4) & 0xFFF00000;
+ omap_writel(value, OMAP730_IO_CONF_4);
+
+ omap_uwire_configure_mode(0,16);
+
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISOFF, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SLPIN, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISNOR, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_GSSET, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_GSSET | 0x100), 9, 0,NULL,1);
+
+ /* DISCTL */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISCTL, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_DISCTL)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_DISCTL[i] | 0x100), 9, 0,NULL,1);
+
+ /* GCP64 */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_GCP64, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_GCP64)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_GCP64[i] | 0x100), 9, 0,NULL,1);
+
+ /* GCP16 */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_GCP16, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_GCP16)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_GCP16[i] | 0x100), 9, 0,NULL,1);
+
+ /* MD_CSET */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_MD_CSET, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_MD_CSET)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_MD_CSET[i] | 0x100), 9, 0,NULL,1);
+
+ /* MD_PSET */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_MD_PSET, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_MD_PSET)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_MD_PSET[i] | 0x100), 9, 0,NULL,1);
+
+ /* SD_CSET */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SD_CSET, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_SD_CSET)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_SD_CSET[i] | 0x100), 9, 0,NULL,1);
+
+ /* SD_PSET */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SD_PSET, 9, 0,NULL,1);
+ for (i = 0; i < (sizeof(INIT_SD_PSET)/sizeof(unsigned short)); i++)
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_SD_PSET[i] | 0x100), 9, 0,NULL,1);
+
+ /* DATCTL */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DATCTL, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_DATCTL | 0x100), 9, 0,NULL,1);
+
+ /* OSSISEL = d'5 */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_OSCISEL, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_OSCISEL | 0x100), 9, 0,NULL,1);
+
+ /* 14MSET = d'74 */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_14MSET, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_14MSET | 0x100), 9, 0,NULL,1);
+
+ /* 14MEND */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_14MEND, 9, 0,NULL,1);
+
+ /* 3500KSET = d'69 */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_3500KSET, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_3500KSET | 0x100), 9, 0,NULL,1);
+
+ /* 3500KEND */
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_3500KEND, 9, 0,NULL,1);
+
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SLPOUT, 9, 0,NULL,1);
+
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_VOLCTL, 9, 0,NULL,1);
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_VOLCTL_Ton | 0x100), 9, 0,NULL,1);
+
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_VOLCTL, 9, 0,NULL,1);
+
+ omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_VOLCTL | 0x100), 9, 0,NULL,1);
+
+ omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISON, 9, 0,NULL,1);
+
+ /* enable backlight */
+ omap_set_gpio_direction(134, 0);
+ omap_set_gpio_dataout(134, 1);
+
+ return 0;
+}
+
+static void p2_panel_disable(struct lcd_panel *panel)
+{
+}
+
+static unsigned long p2_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+struct lcd_panel p2_panel = {
+ .name = "p2",
+ .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_PIX_CLOCK,
+
+ .bpp = 16,
+ .data_lines = 16,
+ .x_res = 176,
+ .y_res = 220,
+ .pixel_clock = 12500,
+ .hsw = 5,
+ .hfp = 1,
+ .hbp = 1,
+ .vsw = 2,
+ .vfp = 12,
+ .vbp = 1,
+
+ .init = p2_panel_init,
+ .cleanup = p2_panel_cleanup,
+ .enable = p2_panel_enable,
+ .disable = p2_panel_disable,
+ .get_caps = p2_panel_get_caps,
+};
+
+static int p2_panel_probe(struct platform_device *pdev)
+{
+ omapfb_register_panel(&p2_panel);
+ return 0;
+}
+
+static int p2_panel_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int p2_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ return 0;
+}
+
+static int p2_panel_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct platform_driver p2_panel_driver = {
+ .probe = p2_panel_probe,
+ .remove = p2_panel_remove,
+ .suspend = p2_panel_suspend,
+ .resume = p2_panel_resume,
+ .driver = {
+ .name = "lcd_p2",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int p2_panel_drv_init(void)
+{
+ return platform_driver_register(&p2_panel_driver);
+}
+
+static void p2_panel_drv_cleanup(void)
+{
+ platform_driver_unregister(&p2_panel_driver);
+}
+
+module_init(p2_panel_drv_init);
+module_exit(p2_panel_drv_cleanup);
+
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * drivers/w1/masters/omap_hdq.c
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <asm/irq.h>
++#include <mach/hardware.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+
+#define MOD_NAME "OMAP_HDQ:"
+
+#define OMAP_HDQ_REVISION 0x00
+#define OMAP_HDQ_TX_DATA 0x04
+#define OMAP_HDQ_RX_DATA 0x08
+#define OMAP_HDQ_CTRL_STATUS 0x0c
+#define OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK (1<<6)
+#define OMAP_HDQ_CTRL_STATUS_CLOCKENABLE (1<<5)
+#define OMAP_HDQ_CTRL_STATUS_GO (1<<4)
+#define OMAP_HDQ_CTRL_STATUS_INITIALIZATION (1<<2)
+#define OMAP_HDQ_CTRL_STATUS_DIR (1<<1)
+#define OMAP_HDQ_CTRL_STATUS_MODE (1<<0)
+#define OMAP_HDQ_INT_STATUS 0x10
+#define OMAP_HDQ_INT_STATUS_TXCOMPLETE (1<<2)
+#define OMAP_HDQ_INT_STATUS_RXCOMPLETE (1<<1)
+#define OMAP_HDQ_INT_STATUS_TIMEOUT (1<<0)
+#define OMAP_HDQ_SYSCONFIG 0x14
+#define OMAP_HDQ_SYSCONFIG_SOFTRESET (1<<1)
+#define OMAP_HDQ_SYSCONFIG_AUTOIDLE (1<<0)
+#define OMAP_HDQ_SYSSTATUS 0x18
+#define OMAP_HDQ_SYSSTATUS_RESETDONE (1<<0)
+
+#define OMAP_HDQ_FLAG_CLEAR 0
+#define OMAP_HDQ_FLAG_SET 1
+#define OMAP_HDQ_TIMEOUT (HZ/5)
+
+#define OMAP_HDQ_MAX_USER 4
+
+DECLARE_WAIT_QUEUE_HEAD(hdq_wait_queue);
+int W1_ID;
+
+struct hdq_data {
+ resource_size_t hdq_base;
+ struct semaphore hdq_semlock;
+ int hdq_usecount;
+ struct clk *hdq_ick;
+ struct clk *hdq_fck;
+ u8 hdq_irqstatus;
+ spinlock_t hdq_spinlock;
+};
+
+static struct hdq_data *hdq_data;
+
+static int omap_hdq_get(void);
+static int omap_hdq_put(void);
+static int omap_hdq_break(void);
+
+static int __init omap_hdq_probe(struct platform_device *pdev);
+static int omap_hdq_remove(struct platform_device *pdev);
+
+static struct platform_driver omap_hdq_driver = {
+ .probe = omap_hdq_probe,
+ .remove = omap_hdq_remove,
+ .suspend = NULL,
+ .resume = NULL,
+ .driver = {
+ .name = "omap_hdq",
+ },
+};
+
+static u8 omap_w1_read_byte(void *data);
+static void omap_w1_write_byte(void *data, u8 byte);
+static u8 omap_w1_reset_bus(void *data);
+static void omap_w1_search_bus(void *data, u8 search_type,
+ w1_slave_found_callback slave_found);
+
+static struct w1_bus_master omap_w1_master = {
+ .read_byte = omap_w1_read_byte,
+ .write_byte = omap_w1_write_byte,
+ .reset_bus = omap_w1_reset_bus,
+ .search = omap_w1_search_bus,
+};
+
+/*
+ * HDQ register I/O routines
+ */
+static inline u8
+hdq_reg_in(u32 offset)
+{
+ return omap_readb(hdq_data->hdq_base + offset);
+}
+
+static inline u8
+hdq_reg_out(u32 offset, u8 val)
+{
+ omap_writeb(val, hdq_data->hdq_base + offset);
+ return val;
+}
+
+static inline u8
+hdq_reg_merge(u32 offset, u8 val, u8 mask)
+{
+ u8 new_val = (omap_readb(hdq_data->hdq_base + offset) & ~mask)
+ | (val & mask);
+ omap_writeb(new_val, hdq_data->hdq_base + offset);
+ return new_val;
+}
+
+/*
+ * Wait for one or more bits in flag change.
+ * HDQ_FLAG_SET: wait until any bit in the flag is set.
+ * HDQ_FLAG_CLEAR: wait until all bits in the flag are cleared.
+ * return 0 on success and -ETIMEDOUT in the case of timeout.
+ */
+static int
+hdq_wait_for_flag(u32 offset, u8 flag, u8 flag_set, u8 *status)
+{
+ int ret = 0;
+ unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT;
+
+ if (flag_set == OMAP_HDQ_FLAG_CLEAR) {
+ /* wait for the flag clear */
+ while (((*status = hdq_reg_in(offset)) & flag)
+ && time_before(jiffies, timeout)) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ if (unlikely(*status & flag))
+ ret = -ETIMEDOUT;
+ } else if (flag_set == OMAP_HDQ_FLAG_SET) {
+ /* wait for the flag set */
+ while (!((*status = hdq_reg_in(offset)) & flag)
+ && time_before(jiffies, timeout)) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ if (unlikely(!(*status & flag)))
+ ret = -ETIMEDOUT;
+ } else
+ return -EINVAL;
+
+ return ret;
+}
+
+/*
+ * write out a byte and fill *status with HDQ_INT_STATUS
+ */
+static int
+hdq_write_byte(u8 val, u8 *status)
+{
+ int ret;
+ u8 tmp_status;
+ unsigned long irqflags;
+
+ *status = 0;
+
+ spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
+ /* clear interrupt flags via a dummy read */
+ hdq_reg_in(OMAP_HDQ_INT_STATUS);
+ /* ISR loads it with new INT_STATUS */
+ hdq_data->hdq_irqstatus = 0;
+ spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
+
+ hdq_reg_out(OMAP_HDQ_TX_DATA, val);
+
+ /* set the GO bit */
+ hdq_reg_merge(OMAP_HDQ_CTRL_STATUS, OMAP_HDQ_CTRL_STATUS_GO,
+ OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO);
+ /* wait for the TXCOMPLETE bit */
+ ret = wait_event_interruptible_timeout(hdq_wait_queue,
+ hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT);
+ if (unlikely(ret < 0)) {
+ pr_debug("wait interrupted");
+ return -EINTR;
+ }
+
+ spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
+ *status = hdq_data->hdq_irqstatus;
+ spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
+ /* check irqstatus */
+ if (!(*status & OMAP_HDQ_INT_STATUS_TXCOMPLETE)) {
+ pr_debug("timeout waiting for TXCOMPLETE/RXCOMPLETE, %x",
+ *status);
+ return -ETIMEDOUT;
+ }
+
+ /* wait for the GO bit return to zero */
+ ret = hdq_wait_for_flag(OMAP_HDQ_CTRL_STATUS, OMAP_HDQ_CTRL_STATUS_GO,
+ OMAP_HDQ_FLAG_CLEAR, &tmp_status);
+ if (ret) {
+ pr_debug("timeout waiting GO bit return to zero, %x",
+ tmp_status);
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * HDQ Interrupt service routine.
+ */
+static irqreturn_t
+hdq_isr(int irq, void *arg)
+{
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
+ hdq_data->hdq_irqstatus = hdq_reg_in(OMAP_HDQ_INT_STATUS);
+ spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
+ pr_debug("hdq_isr: %x", hdq_data->hdq_irqstatus);
+
+ if (hdq_data->hdq_irqstatus &
+ (OMAP_HDQ_INT_STATUS_TXCOMPLETE | OMAP_HDQ_INT_STATUS_RXCOMPLETE
+ | OMAP_HDQ_INT_STATUS_TIMEOUT)) {
+ /* wake up sleeping process */
+ wake_up_interruptible(&hdq_wait_queue);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * HDQ Mode: always return success.
+ */
+static u8 omap_w1_reset_bus(void *data)
+{
+ return 0;
+}
+
+/*
+ * W1 search callback function.
+ */
+static void omap_w1_search_bus(void *data, u8 search_type,
+ w1_slave_found_callback slave_found)
+{
+ u64 module_id, rn_le, cs, id;
+
+ if (W1_ID)
+ module_id = W1_ID;
+ else
+ module_id = 0x1;
+
+ rn_le = cpu_to_le64(module_id);
+ /*
+ * HDQ might not obey truly the 1-wire spec.
+ * So calculate CRC based on module parameter.
+ */
+ cs = w1_calc_crc8((u8 *)&rn_le, 7);
+ id = (cs << 56) | module_id;
+
+ slave_found(data, id);
+}
+
+static int
+_omap_hdq_reset(void)
+{
+ int ret;
+ u8 tmp_status;
+
+ hdq_reg_out(OMAP_HDQ_SYSCONFIG, OMAP_HDQ_SYSCONFIG_SOFTRESET);
+ /*
+ * Select HDQ mode & enable clocks.
+ * It is observed that INT flags can't be cleared via a read and GO/INIT
+ * won't return to zero if interrupt is disabled. So we always enable
+ * interrupt.
+ */
+ hdq_reg_out(OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_CLOCKENABLE |
+ OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK);
+
+ /* wait for reset to complete */
+ ret = hdq_wait_for_flag(OMAP_HDQ_SYSSTATUS,
+ OMAP_HDQ_SYSSTATUS_RESETDONE, OMAP_HDQ_FLAG_SET, &tmp_status);
+ if (ret)
+ pr_debug("timeout waiting HDQ reset, %x", tmp_status);
+ else {
+ hdq_reg_out(OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_CLOCKENABLE |
+ OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK);
+ hdq_reg_out(OMAP_HDQ_SYSCONFIG, OMAP_HDQ_SYSCONFIG_AUTOIDLE);
+ }
+
+ return ret;
+}
+
+/*
+ * Issue break pulse to the device.
+ */
+static int
+omap_hdq_break()
+{
+ int ret;
+ u8 tmp_status;
+ unsigned long irqflags;
+
+ ret = down_interruptible(&hdq_data->hdq_semlock);
+ if (ret < 0)
+ return -EINTR;
+
+ if (!hdq_data->hdq_usecount) {
+ up(&hdq_data->hdq_semlock);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
+ /* clear interrupt flags via a dummy read */
+ hdq_reg_in(OMAP_HDQ_INT_STATUS);
+ /* ISR loads it with new INT_STATUS */
+ hdq_data->hdq_irqstatus = 0;
+ spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
+
+ /* set the INIT and GO bit */
+ hdq_reg_merge(OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_INITIALIZATION | OMAP_HDQ_CTRL_STATUS_GO,
+ OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_INITIALIZATION |
+ OMAP_HDQ_CTRL_STATUS_GO);
+
+ /* wait for the TIMEOUT bit */
+ ret = wait_event_interruptible_timeout(hdq_wait_queue,
+ hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT);
+ if (unlikely(ret < 0)) {
+ pr_debug("wait interrupted");
+ up(&hdq_data->hdq_semlock);
+ return -EINTR;
+ }
+
+ spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
+ tmp_status = hdq_data->hdq_irqstatus;
+ spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
+ /* check irqstatus */
+ if (!(tmp_status & OMAP_HDQ_INT_STATUS_TIMEOUT)) {
+ pr_debug("timeout waiting for TIMEOUT, %x", tmp_status);
+ up(&hdq_data->hdq_semlock);
+ return -ETIMEDOUT;
+ }
+ /*
+ * wait for both INIT and GO bits rerurn to zero.
+ * zero wait time expected for interrupt mode.
+ */
+ ret = hdq_wait_for_flag(OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_INITIALIZATION |
+ OMAP_HDQ_CTRL_STATUS_GO, OMAP_HDQ_FLAG_CLEAR,
+ &tmp_status);
+ if (ret)
+ pr_debug("timeout waiting INIT&GO bits return to zero, %x",
+ tmp_status);
+
+ up(&hdq_data->hdq_semlock);
+ return ret;
+}
+
+static int hdq_read_byte(u8 *val)
+{
+ int ret;
+ u8 status;
+ unsigned long irqflags;
+
+ ret = down_interruptible(&hdq_data->hdq_semlock);
+ if (ret < 0)
+ return -EINTR;
+
+ if (!hdq_data->hdq_usecount) {
+ up(&hdq_data->hdq_semlock);
+ return -EINVAL;
+ }
+
+ if (!(hdq_data->hdq_irqstatus & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) {
+ hdq_reg_merge(OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO,
+ OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO);
+ /*
+ * The RX comes immediately after TX. It
+ * triggers another interrupt before we
+ * sleep. So we have to wait for RXCOMPLETE bit.
+ */
+ {
+ unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT;
+ while (!(hdq_data->hdq_irqstatus
+ & OMAP_HDQ_INT_STATUS_RXCOMPLETE)
+ && time_before(jiffies, timeout)) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ }
+ hdq_reg_merge(OMAP_HDQ_CTRL_STATUS, 0,
+ OMAP_HDQ_CTRL_STATUS_DIR);
+ spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags);
+ status = hdq_data->hdq_irqstatus;
+ spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags);
+ /* check irqstatus */
+ if (!(status & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) {
+ pr_debug("timeout waiting for RXCOMPLETE, %x", status);
+ up(&hdq_data->hdq_semlock);
+ return -ETIMEDOUT;
+ }
+ }
+ /* the data is ready. Read it in! */
+ *val = hdq_reg_in(OMAP_HDQ_RX_DATA);
+ up(&hdq_data->hdq_semlock);
+
+ return 0;
+
+}
+
+/*
+ * Enable clocks and set the controller to HDQ mode.
+ */
+static int
+omap_hdq_get()
+{
+ int ret = 0;
+
+ ret = down_interruptible(&hdq_data->hdq_semlock);
+ if (ret < 0)
+ return -EINTR;
+
+ if (OMAP_HDQ_MAX_USER == hdq_data->hdq_usecount) {
+ pr_debug("attempt to exceed the max use count");
+ up(&hdq_data->hdq_semlock);
+ ret = -EINVAL;
+ } else {
+ hdq_data->hdq_usecount++;
+ try_module_get(THIS_MODULE);
+ if (1 == hdq_data->hdq_usecount) {
+ if (clk_enable(hdq_data->hdq_ick)) {
+ pr_debug("Can not enable ick\n");
+ clk_put(hdq_data->hdq_ick);
+ clk_put(hdq_data->hdq_fck);
+ up(&hdq_data->hdq_semlock);
+ return -ENODEV;
+ }
+ if (clk_enable(hdq_data->hdq_fck)) {
+ pr_debug("Can not enable fck\n");
+ clk_put(hdq_data->hdq_ick);
+ clk_put(hdq_data->hdq_fck);
+ up(&hdq_data->hdq_semlock);
+ return -ENODEV;
+ }
+
+ /* make sure HDQ is out of reset */
+ if (!(hdq_reg_in(OMAP_HDQ_SYSSTATUS) &
+ OMAP_HDQ_SYSSTATUS_RESETDONE)) {
+ ret = _omap_hdq_reset();
+ if (ret)
+ /* back up the count */
+ hdq_data->hdq_usecount--;
+ } else {
+ /* select HDQ mode & enable clocks */
+ hdq_reg_out(OMAP_HDQ_CTRL_STATUS,
+ OMAP_HDQ_CTRL_STATUS_CLOCKENABLE |
+ OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK);
+ hdq_reg_out(OMAP_HDQ_SYSCONFIG,
+ OMAP_HDQ_SYSCONFIG_AUTOIDLE);
+ hdq_reg_in(OMAP_HDQ_INT_STATUS);
+ }
+ }
+ }
+ up(&hdq_data->hdq_semlock);
+ return ret;
+}
+
+/*
+ * Disable clocks to the module.
+ */
+static int
+omap_hdq_put()
+{
+ int ret = 0;
+
+ ret = down_interruptible(&hdq_data->hdq_semlock);
+ if (ret < 0)
+ return -EINTR;
+
+ if (0 == hdq_data->hdq_usecount) {
+ pr_debug("attempt to decrement use count when it is zero");
+ ret = -EINVAL;
+ } else {
+ hdq_data->hdq_usecount--;
+ module_put(THIS_MODULE);
+ if (0 == hdq_data->hdq_usecount) {
+ clk_disable(hdq_data->hdq_ick);
+ clk_disable(hdq_data->hdq_fck);
+ }
+ }
+ up(&hdq_data->hdq_semlock);
+ return ret;
+}
+
+/*
+ * Used to control the call to omap_hdq_get and omap_hdq_put.
+ * HDQ Protocol: Write the CMD|REG_address first, followed by
+ * the data wrire or read.
+ */
+static int init_trans;
+
+/*
+ * Read a byte of data from the device.
+ */
+static u8 omap_w1_read_byte(void *data)
+{
+ u8 val;
+ int ret;
+
+ ret = hdq_read_byte(&val);
+ if (ret) {
+ init_trans = 0;
+ omap_hdq_put();
+ return -1;
+ }
+
+ /* Write followed by a read, release the module */
+ if (init_trans) {
+ init_trans = 0;
+ omap_hdq_put();
+ }
+
+ return val;
+}
+
+/*
+ * Write a byte of data to the device.
+ */
+static void omap_w1_write_byte(void *data, u8 byte)
+{
+ u8 status;
+
+ /* First write to initialize the transfer */
+ if (init_trans == 0)
+ omap_hdq_get();
+
+ init_trans++;
+
+ hdq_write_byte(byte, &status);
+ pr_debug("Ctrl status %x\n", status);
+
+ /* Second write, data transfered. Release the module */
+ if (init_trans > 1) {
+ omap_hdq_put();
+ init_trans = 0;
+ }
+
+ return;
+}
+
+static int __init omap_hdq_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret, irq;
+ u8 rev;
+
+ if (!pdev)
+ return -ENODEV;
+
+ hdq_data = kmalloc(sizeof(*hdq_data), GFP_KERNEL);
+ if (!hdq_data)
+ return -ENODEV;
+
+ platform_set_drvdata(pdev, hdq_data);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ platform_set_drvdata(pdev, NULL);
+ kfree(hdq_data);
+ return -ENXIO;
+ }
+
+ hdq_data->hdq_base = res->start;
+
+ /* get interface & functional clock objects */
+ hdq_data->hdq_ick = clk_get(&pdev->dev, "hdq_ick");
+ hdq_data->hdq_fck = clk_get(&pdev->dev, "hdq_fck");
+
+ if (IS_ERR(hdq_data->hdq_ick) || IS_ERR(hdq_data->hdq_fck)) {
+ pr_debug("Can't get HDQ clock objects\n");
+ if (IS_ERR(hdq_data->hdq_ick)) {
+ ret = PTR_ERR(hdq_data->hdq_ick);
+ platform_set_drvdata(pdev, NULL);
+ kfree(hdq_data);
+ return ret;
+ }
+ if (IS_ERR(hdq_data->hdq_fck)) {
+ ret = PTR_ERR(hdq_data->hdq_fck);
+ platform_set_drvdata(pdev, NULL);
+ kfree(hdq_data);
+ return ret;
+ }
+ }
+
+ hdq_data->hdq_usecount = 0;
+ sema_init(&hdq_data->hdq_semlock, 1);
+
+ if (clk_enable(hdq_data->hdq_ick)) {
+ pr_debug("Can not enable ick\n");
+ clk_put(hdq_data->hdq_ick);
+ clk_put(hdq_data->hdq_fck);
+ platform_set_drvdata(pdev, NULL);
+ kfree(hdq_data);
+ return -ENODEV;
+ }
+
+ if (clk_enable(hdq_data->hdq_fck)) {
+ pr_debug("Can not enable fck\n");
+ clk_disable(hdq_data->hdq_ick);
+ clk_put(hdq_data->hdq_ick);
+ clk_put(hdq_data->hdq_fck);
+ platform_set_drvdata(pdev, NULL);
+ kfree(hdq_data);
+ return -ENODEV;
+ }
+
+ rev = hdq_reg_in(OMAP_HDQ_REVISION);
+ pr_info("OMAP HDQ Hardware Revision %c.%c. Driver in %s mode.\n",
+ (rev >> 4) + '0', (rev & 0x0f) + '0', "Interrupt");
+
+ spin_lock_init(&hdq_data->hdq_spinlock);
+ omap_hdq_break();
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ platform_set_drvdata(pdev, NULL);
+ kfree(hdq_data);
+ return -ENXIO;
+ }
+
+ if (request_irq(irq, hdq_isr, IRQF_DISABLED, "OMAP HDQ",
+ &hdq_data->hdq_semlock)) {
+ pr_debug("request_irq failed\n");
+ clk_disable(hdq_data->hdq_ick);
+ clk_put(hdq_data->hdq_ick);
+ clk_put(hdq_data->hdq_fck);
+ platform_set_drvdata(pdev, NULL);
+ kfree(hdq_data);
+ return -ENODEV;
+ }
+
+ /* don't clock the HDQ until it is needed */
+ clk_disable(hdq_data->hdq_ick);
+ clk_disable(hdq_data->hdq_fck);
+
+ ret = w1_add_master_device(&omap_w1_master);
+ if (ret) {
+ pr_debug("Failure in registering w1 master\n");
+ clk_put(hdq_data->hdq_ick);
+ clk_put(hdq_data->hdq_fck);
+ platform_set_drvdata(pdev, NULL);
+ kfree(hdq_data);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int omap_hdq_remove(struct platform_device *pdev)
+{
+ down_interruptible(&hdq_data->hdq_semlock);
+ if (0 != hdq_data->hdq_usecount) {
+ pr_debug("removed when use count is not zero\n");
+ return -EBUSY;
+ }
+ up(&hdq_data->hdq_semlock);
+
+ /* remove module dependency */
+ clk_put(hdq_data->hdq_ick);
+ clk_put(hdq_data->hdq_fck);
+ free_irq(INT_24XX_HDQ_IRQ, &hdq_data->hdq_semlock);
+ platform_set_drvdata(pdev, NULL);
+ kfree(hdq_data);
+
+ return 0;
+}
+
+static int __init
+omap_hdq_init(void)
+{
+ return platform_driver_register(&omap_hdq_driver);
+}
+
+static void __exit
+omap_hdq_exit(void)
+{
+ platform_driver_unregister(&omap_hdq_driver);
+}
+
+module_init(omap_hdq_init);
+module_exit(omap_hdq_exit);
+
+module_param(W1_ID, int, S_IRUSR);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("HDQ driver Library");
+MODULE_LICENSE("GPL");
#include <linux/platform_device.h>
#include <linux/moduleparam.h>
#include <linux/clk.h>
-#include <linux/bitops.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
- #include <asm/hardware.h>
+ #include <mach/hardware.h>
+#include <asm/bitops.h>
+
- #include <asm/arch/prcm.h>
+ #include <mach/prcm.h>
#include "omap_wdt.h"
#define I2C_DRIVERID_TDA9840 7 /* stereo sound processor */
#define I2C_DRIVERID_SAA7111A 8 /* video input processor */
#define I2C_DRIVERID_SAA7185B 13 /* video encoder */
+#define I2C_DRIVERID_TEA6300 18 /* audio mixer */
+#define I2C_DRIVERID_TDA9850 20 /* audio mixer */
+#define I2C_DRIVERID_TDA9855 21 /* audio mixer */
#define I2C_DRIVERID_SAA7110 22 /* video decoder */
- #define I2C_DRIVERID_MGATVO 23 /* Matrox TVOut */
#define I2C_DRIVERID_SAA5249 24 /* SAA5249 and compatibles */
#define I2C_DRIVERID_PCF8583 25 /* real time clock */
#define I2C_DRIVERID_SAB3036 26 /* SAB3036 tuner */
--- /dev/null
- #include <asm/arch/irqs.h>
+/*
+ * twl4030.h - header for TWL4030 PM and audio CODEC device
+ *
+ * Copyright (C) 2005-2006 Texas Instruments, Inc.
+ *
+ * Based on tlv320aic23.c:
+ * Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
+ *
+ * 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
+ *
+ */
+
+#ifndef __TWL4030_H_
+#define __TWL4030_H_
+
+/* USB ID */
+#define TWL4030_MODULE_USB 0x00
+/* AUD ID */
+#define TWL4030_MODULE_AUDIO_VOICE 0x01
+#define TWL4030_MODULE_GPIO 0x02
+#define TWL4030_MODULE_INTBR 0x03
+#define TWL4030_MODULE_PIH 0x04
+#define TWL4030_MODULE_TEST 0x05
+/* AUX ID */
+#define TWL4030_MODULE_KEYPAD 0x06
+#define TWL4030_MODULE_MADC 0x07
+#define TWL4030_MODULE_INTERRUPTS 0x08
+#define TWL4030_MODULE_LED 0x09
+#define TWL4030_MODULE_MAIN_CHARGE 0x0A
+#define TWL4030_MODULE_PRECHARGE 0x0B
+#define TWL4030_MODULE_PWM0 0x0C
+#define TWL4030_MODULE_PWM1 0x0D
+#define TWL4030_MODULE_PWMA 0x0E
+#define TWL4030_MODULE_PWMB 0x0F
+/* POWER ID */
+#define TWL4030_MODULE_BACKUP 0x10
+#define TWL4030_MODULE_INT 0x11
+#define TWL4030_MODULE_PM_MASTER 0x12
+#define TWL4030_MODULE_PM_RECEIVER 0x13
+#define TWL4030_MODULE_RTC 0x14
+#define TWL4030_MODULE_SECURED_REG 0x15
+
+/* IRQ information-need base */
++#include <mach/irqs.h>
+/* TWL4030 interrupts */
+
+#define TWL4030_MODIRQ_GPIO (TWL4030_IRQ_BASE + 0)
+#define TWL4030_MODIRQ_KEYPAD (TWL4030_IRQ_BASE + 1)
+#define TWL4030_MODIRQ_BCI (TWL4030_IRQ_BASE + 2)
+#define TWL4030_MODIRQ_MADC (TWL4030_IRQ_BASE + 3)
+#define TWL4030_MODIRQ_USB (TWL4030_IRQ_BASE + 4)
+#define TWL4030_MODIRQ_PWR (TWL4030_IRQ_BASE + 5)
+
+#define TWL4030_PWRIRQ_PWRBTN (TWL4030_PWR_IRQ_BASE + 0)
+#define TWL4030_PWRIRQ_CHG_PRES (TWL4030_PWR_IRQ_BASE + 1)
+#define TWL4030_PWRIRQ_USB_PRES (TWL4030_PWR_IRQ_BASE + 2)
+#define TWL4030_PWRIRQ_RTC (TWL4030_PWR_IRQ_BASE + 3)
+#define TWL4030_PWRIRQ_HOT_DIE (TWL4030_PWR_IRQ_BASE + 4)
+#define TWL4030_PWRIRQ_PWROK_TIMEOUT (TWL4030_PWR_IRQ_BASE + 5)
+#define TWL4030_PWRIRQ_MBCHG (TWL4030_PWR_IRQ_BASE + 6)
+#define TWL4030_PWRIRQ_SC_DETECT (TWL4030_PWR_IRQ_BASE + 7)
+
+/* Rest are unsued currently*/
+
+/* Offsets to Power Registers */
+#define TWL4030_VDAC_DEV_GRP 0x3B
+#define TWL4030_VDAC_DEDICATED 0x3E
+#define TWL4030_VAUX1_DEV_GRP 0x17
+#define TWL4030_VAUX1_DEDICATED 0x1A
+#define TWL4030_VAUX2_DEV_GRP 0x1B
+#define TWL4030_VAUX2_DEDICATED 0x1E
+#define TWL4030_VAUX3_DEV_GRP 0x1F
+#define TWL4030_VAUX3_DEDICATED 0x22
+
+/* TWL4030 GPIO interrupt definitions */
+
+#define TWL4030_GPIO_MIN 0
+#define TWL4030_GPIO_MAX 18
+#define TWL4030_GPIO_MAX_CD 2
+#define TWL4030_GPIO_IRQ_NO(n) (TWL4030_GPIO_IRQ_BASE + (n))
+#define TWL4030_GPIO_IS_INPUT 1
+#define TWL4030_GPIO_IS_OUTPUT 0
+#define TWL4030_GPIO_IS_ENABLE 1
+#define TWL4030_GPIO_IS_DISABLE 0
+#define TWL4030_GPIO_PULL_UP 0
+#define TWL4030_GPIO_PULL_DOWN 1
+#define TWL4030_GPIO_PULL_NONE 2
+#define TWL4030_GPIO_EDGE_NONE 0
+#define TWL4030_GPIO_EDGE_RISING 1
+#define TWL4030_GPIO_EDGE_FALLING 2
+
+/* Functions to read and write from TWL4030 */
+
+/*
+ * IMP NOTE:
+ * The base address of the module will be added by the triton driver
+ * It is the caller's responsibility to ensure sane values
+ */
+int twl4030_i2c_write_u8(u8 mod_no, u8 val, u8 reg);
+int twl4030_i2c_read_u8(u8 mod_no, u8 *val, u8 reg);
+
+ /*
+ * i2c_write: IMPORTANT - Allocate value num_bytes+1 and valid data starts at
+ * Offset 1.
+ */
+int twl4030_i2c_write(u8 mod_no, u8 *value, u8 reg, u8 num_bytes);
+int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, u8 num_bytes);
+
+/*
+ * Exported TWL4030 GPIO APIs
+ */
+int twl4030_get_gpio_datain(int gpio);
+int twl4030_request_gpio(int gpio);
+int twl4030_set_gpio_edge_ctrl(int gpio, int edge);
+int twl4030_set_gpio_debounce(int gpio, int enable);
+int twl4030_free_gpio(int gpio);
+
+#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
+ defined(CONFIG_TWL4030_BCI_BATTERY_MODULE)
+ extern int twl4030charger_usb_en(int enable);
+#else
+ static inline int twl4030charger_usb_en(int enable) { return 0; }
+#endif
+
+#endif /* End of __TWL4030_H */
--- /dev/null
- #include <asm/arch/eac.h>
+/*
+ * linux/sound/arm/omap/omap-alsa-eac.c
+ *
+ * OMAP24xx Enhanced Audio Controller sound driver
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ *
+ * Contact: Jarkko Nikula <jarkko.nikula@nokia.com>
+ * Juha Yrjölä
+ *
+ * Definitions:
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#define DEBUG
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
++#include <mach/eac.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+
+#define EAC_CPCFR1 0x0000
+#define EAC_CPCFR2 0x0004
+#define EAC_CPCFR3 0x0008
+#define EAC_CPCFR4 0x000C
+#define EAC_CPTCTL 0x0010
+#define EAC_CPTTADR 0x0014
+#define EAC_CPTDATL 0x0018
+#define EAC_CPTDATH 0x001C
+#define EAC_CPTVSLL 0x0020
+#define EAC_CPTVSLH 0x0024
+#define EAC_MPCTR 0x0040
+#define EAC_MPMCCFR 0x0044
+#define EAC_BPCTR 0x0060
+#define EAC_BPMCCFR 0x0064
+#define EAC_AMSCFR 0x0080
+#define EAC_AMVCTR 0x0084
+#define EAC_AM1VCTR 0x0088
+#define EAC_AM2VCTR 0x008C
+#define EAC_AM3VCTR 0x0090
+#define EAC_ASTCTR 0x0094
+#define EAC_APD1LCR 0x0098
+#define EAC_APD1RCR 0x009C
+#define EAC_APD2LCR 0x00A0
+#define EAC_APD2RCR 0x00A4
+#define EAC_APD3LCR 0x00A8
+#define EAC_APD3RCR 0x00AC
+#define EAC_APD4R 0x00B0
+#define EAC_ADWR 0x00B4
+#define EAC_ADRDR 0x00B8
+#define EAC_AGCFR 0x00BC
+#define EAC_AGCTR 0x00C0
+#define EAC_AGCFR2 0x00C4
+#define EAC_AGCFR3 0x00C8
+#define EAC_MBPDMACTR 0x00CC
+#define EAC_MPDDMARR 0x00D0
+#define EAC_MPDDMAWR 0x00D4
+#define EAC_MPUDMARR 0x00D8
+#define EAC_MPUDMAWR 0x00E0
+#define EAC_BPDDMARR 0x00E4
+#define EAC_BPDDMAWR 0x00E8
+#define EAC_BPUDMARR 0x00EC
+#define EAC_BPUDMAWR 0x00F0
+#define EAC_VERSION 0x0100
+#define EAC_SYSCONFIG 0x0104
+#define EAC_SYSSTATUS 0x0108
+
+/* CPTCTL */
+#define CPTCTL_RXF (1 << 7) /* receive data register full */
+#define CPTCTL_RXIE (1 << 6) /* receive interrupt enable */
+#define CPTCTL_TXE (1 << 5) /* transmit register empty */
+#define CPTCTL_TXIE (1 << 4) /* transmit interrupt enable */
+#define CPTCTL_CPEN (1 << 3) /* codec port enable */
+#define CPTCTL_CRST (1 << 0) /* external codec reset */
+
+/* CPCFR1 */
+#define CPCFR1_MTSL(val) ((val & 0x1f) << 3) /* number of time slots per frame */
+#define CPCFR1_MTSL_BITS (0x1f << 3)
+#define CPCFR1_MODE(val) ((val & 0x7) << 0) /* codec port interface mode */
+#define CPCFR1_MODE_BITS (0x7 << 0)
+
+/* CPCFR2 */
+#define CPCFR2_TSLOL(val) ((val & 0x3) << 6) /* time slot 0 length in number of serial clock (CLK_BIT) cycles */
+#define CPCFR2_TSLOL_BITS (0x3 << 6)
+#define CPCFR2_BPTSL(val) ((val & 0x7) << 3) /* number of data bits per audio time slot */
+#define CPCFR2_BPTSL_BITS (0x7 << 3)
+#define CPCFR2_TSLL(val) ((val & 0x7) << 0) /* time slot lenght (except slot 0) in number of serial clock cycles */
+#define CPCFR2_TSLL_BITS (0x7 << 0)
+
+/* CPCFR3 */
+#define CPCFR3_DDLY (1 << 7) /* data delay: data bits start according to SYNC signal leading edge */
+#define CPCFR3_TRSEN (1 << 6) /* 3-state enable: data serial output state during nonvalid audio frames */
+#define CPCFR3_CLKBP (1 << 5) /* clock polarity */
+#define CPCFR3_CSYNCP (1 << 4) /* cp_sync(synchro) polarity */
+#define CPCFR3_CSYNCL (1 << 3) /* csync length */
+/* bit 2 reserved */
+#define CPCFR3_CSCLKD (1 << 1) /* cp_sclk port (serial clock) direction */
+#define CPCFR3_CSYNCD (1 << 0) /* cp_sync (synchro) direction */
+
+/* CPCFR4 */
+#define CPCFR4_ATSL(val) ((val & 0xf) << 4) /* audio time slots for secondary communication address and data values */
+#define CPCFR4_ATSL_BITS (0xf << 4)
+#define CPCFR4_CLKS (1 << 3) /* clock source */
+#define CPCFR4_DIVB(val) ((val & 0x7) << 0) /* cp_sclk driver value */
+#define CPCFR4_DIVB_BITS (0x7 << 0)
+
+/* AGCFR */
+#define AGCFR_MN_ST (1 << 10) /* mono/stereo audio file */
+#define AGCFR_B8_16 (1 << 9) /* 8 bits/16 bits audio file */
+#define AGCFR_LI_BI (1 << 8) /* audio file endianism */
+#define AGCFR_FSINT(val) ((val & 0x3) << 6) /* intermediate sample frequency for DMA read and write operations */
+#define AGCFR_FINST_BITS (0x3 << 6)
+
+#define AGCFR_FSINT_8000 (0) /* 8000 Hz */
+#define AGCFR_FSINT_11025 (1) /* 11025 Hz */
+#define AGCFR_FSINT_22050 (2) /* 22050 Hz */
+#define AGCFR_FSINT_44100 (3) /* 44100 Hz */
+
+#define AGCFR_AUD_CKSRC(val)((val & 0x3) << 4) /* audio processing clock source */
+#define AGCFR_AUD_CKSRC_BITS (0x3 << 4)
+#define AGCFR_M_CKSRC (1 << 3) /* modem interface clock source */
+#define AGCFR_MCLK_OUT (1 << 1)
+#define AGCFR_MCLK (1 << 0)
+
+
+/* AGCTR */
+#define AGCTR_AUDRD (1 << 15) /* audio ready */
+#define AGCTR_AUDRDI (1 << 14) /* audio ready interrupt status */
+#define AGCTR_AUDRDIEN (1 << 13) /* audio ready interrupt enable */
+#define AGCTR_DMAREN (1 << 12) /* audio files play operation */
+#define AGCTR_DMAWEN (1 << 11) /* audio file record operation */
+/* bits 10:4 reserved */
+#define AGCTR_MCLK_EN (1 << 3) /* internal MCLK enable */
+#define AGCTR_OSCMCLK_EN (1 << 2) /* OSCMCLK_EN output for MCLK oscillator control */
+#define AGCTR_AUDEN (1 << 1) /* audio processing enable/disable */
+#define AGCTR_EACPWD (1 << 0) /* EAC operation */
+
+/* AGCFR2 */
+#define AGCFR2_BT_MD_WIDEBAND (1 << 5) /* the BT device and modem AuSPIs wide-band mode */
+#define AGCFR2_MCLK_I2S_N11M_12M (1 << 4) /* MCLK freq indicator for audio operations */
+#define AGCFR2_I2S_N44K_48K (1 << 3) /* Frame sample frecuency of I2S codec port, does not generate value */
+#define AGCFR2_FSINT2(val) ((val & 0x7) << 0) /* intermediate sample frequency for DMA channel read and write operations */
+#define AGCFR2_FSINT2_BITS (0x7 << 0)
+
+#define AGCFR2_FSINT2_8000 (0) /* 8000 Hz */
+#define AGCFR2_FSINT2_11025 (1) /* 11025 Hz */
+#define AGCFR2_FSINT2_22050 (2) /* 22050 Hz */
+#define AGCFR2_FSINT2_44100 (3) /* 44100 Hz */
+#define AGCFR2_FSINT2_48000 (4) /* 48000 Hz */
+#define AGCFR2_FSINT2_FSINT (7) /* based on AGCFR/FSINT */
+
+
+/* AGCFR3 */
+#define AGCFR3_CP_TR_DMA (1 << 15) /* codec port transparent DMA (to audio DMAs) */
+#define AGCFR3_BT_TR_DMA (1 << 14) /* BT transparent DMA (to BT UL write & DL read DMAs */
+#define AGCFR3_MD_TR_DMA (1 << 13) /* modem transparent DMA (to modem UL write and DL read DMAs) */
+#define AGCFR3_FSINT(val) ((val & 0xf) << 9) /* FSINT */
+#define AGCFR3_FSINT_BITS (0xf << 9)
+
+#define AGCFR3_FSINT_8000 (0) /* 8000 Hz */
+#define AGCFR3_FSINT_11025 (1) /* 11025 Hz */
+#define AGCFR3_FSINT_16000 (2) /* 16000 Hz */
+#define AGCFR3_FSINT_22050 (3) /* 22050 Hz */
+#define AGCFR3_FSINT_24000 (4) /* 24000 Hz */
+#define AGCFR3_FSINT_32000 (5) /* 32000 Hz */
+#define AGCFR3_FSINT_44100 (6) /* 44100 Hz */
+#define AGCFR3_FSINT_48000 (7) /* 48000 Hz */
+#define AGCFR3_FSINT_FSINT (15) /* based on AGCFR2/AGCFR */
+
+
+#define AGCFR3_BT_CKSRC(val) ((val & 0x3) << 7) /* BT port clock selection */
+#define AGCFR3_BT_CKSRC_BITS (0x3 << 7)
+#define AGCFR3_MD_CKSRC(val) ((val & 0x3) << 5) /* modem port clock source */
+#define AGCFR3_MD_CKSRC_BITS (0x3 << 5)
+#define AGCFR3_AUD_CKSRC(val) ((val & 0x7) << 2) /* audio and codec port clock source */
+#define AGCFR3_AUD_CKSRC_BITS (0x7 << 2)
+#define AGCFR3_CLK12MINT_SEL (1 << 1) /* internal 12MHz clock source */
+#define AGCFR3_MCLKINT_SEL (1 << 0) /* internal codec master clock source */
+
+/* AMSCFR */
+#define AMSCFR_K12 (1 << 11) /* K12 switch open/close */
+#define AMSCFR_K11 (1 << 10)
+#define AMSCFR_K10 (1 << 9)
+#define AMSCFR_K9 (1 << 8)
+#define AMSCFR_K8 (1 << 7)
+#define AMSCFR_K7 (1 << 6)
+#define AMSCFR_K6 (1 << 5)
+#define AMSCFR_K5 (1 << 4)
+#define AMSCFR_K4 (1 << 3)
+#define AMSCFR_K3 (1 << 2)
+#define AMSCFR_K2 (1 << 1)
+#define AMSCFR_K1 (1 << 0)
+
+/* AMVCTR */
+#define AMVCTR_GWO_BITS (0xff << 8)
+#define AMVCTR_GWO(val) ((val & 0xff) << 8) /* Gain on write DMA operation */
+#define AMVCTR_GRO_BITS (0xff << 0)
+#define AMVCTR_GRO(val) ((val & 0xff) << 0) /* Gain on read DMA operation */
+
+/* AM1VCTR */
+#define AM1VCTR_MUTE (1 << 15) /* mute/no mute on mixer output */
+#define AM1VCTR_GINB(val) ((val & 0x7f) << 8) /* gain on input B */
+#define AM1VCTR_GINB_BITS (0x7f << 8)
+#define AM1VCTR_GINA(val) ((val & 0x7f) << 0) /* gain on input A */
+#define AM1VCTR_GINA_BITS (0x7f << 0)
+
+/* AM2VCTR */
+#define AM2VCTR_MUTE (1 << 15) /* mute/no mute on mixer output */
+#define AM2VCTR_GINB(val) ((val & 0x7f) << 8) /* gain on input B */
+#define AM2VCTR_GINB_BITS (0x7f << 8)
+#define AM2VCTR_GINA(val) ((val & 0x7f) << 0) /* gain on input A */
+#define AM2VCTR_GINA_BITS (0x7f << 0)
+
+/* AM3VCTR */
+#define AM3VCTR_MUTE (1 << 15) /* mute/no mute */
+#define AM3VCTR_GINB(val) ((val & 0x7f) << 8) /* gain on input B */
+#define AM3VCTR_GINB_BITS (0x7f << 8)
+#define AM3VCTR_GINA(val) ((val & 0x7f) << 0) /* gain on input A */
+#define AM3VCTR_GINA_BITS (0x7f << 0)
+
+/* ASTCTR */
+#define ASTCTR_ATT(val) ((val & 0x7f) << 1) /* Attenuation of side tone */
+#define ASTCTR_ATT_BITS (0x7f << 1)
+#define ASTCTR_ATTEN (1 << 0) /* side tone enabled/disabled */
+
+
+/* internal structure of the EAC driver */
+struct omap_eac {
+ struct mutex mutex;
+ void __iomem * base;
+ struct platform_device * pdev;
+ struct eac_platform_data * pdata;
+ struct snd_card * card;
+ struct clk * fck;
+ struct clk * ick;
+ struct eac_codec * codec;
+
+ unsigned clocks_enabled:1;
+};
+
+static char *id = SNDRV_DEFAULT_STR1;
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for OMAP24xx EAC");
+
+
+#define MOD_REG_BIT(val, mask, set) do { \
+ if (set) \
+ val |= mask; \
+ else \
+ val &= ~mask; \
+} while(0)
+
+static inline void eac_write_reg(struct omap_eac *eac, int idx, u16 val)
+{
+ __raw_writew(val, eac->base + idx);
+}
+
+static inline u16 eac_read_reg(struct omap_eac *eac, int idx)
+{
+ return __raw_readw(eac->base + idx);
+}
+
+static int eac_get_clocks(struct omap_eac *eac)
+{
+ eac->ick = clk_get(NULL, "eac_ick");
+ if (IS_ERR(eac->ick)) {
+ dev_err(&eac->pdev->dev, "Could not get eac_ick");
+ return -ENODEV;
+ }
+
+ eac->fck = clk_get(NULL, "eac_fck");
+ if (IS_ERR(eac->fck)) {
+ dev_err(&eac->pdev->dev, "Could not get eac_fck");
+ clk_put(eac->ick);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void eac_put_clocks(struct omap_eac *eac)
+{
+ clk_put(eac->fck);
+ clk_put(eac->ick);
+}
+
+static int eac_enable_clocks(struct omap_eac *eac)
+{
+ int err = 0;
+
+ if (eac->clocks_enabled)
+ return 0;
+
+ if (eac->pdata != NULL && eac->pdata->enable_ext_clocks != NULL) {
+ if ((err = eac->pdata->enable_ext_clocks(&eac->pdev->dev)) != 0)
+ return err;
+ }
+ clk_enable(eac->ick);
+ clk_enable(eac->fck);
+ eac->clocks_enabled = 1;
+
+ return 0;
+}
+
+static void eac_disable_clocks(struct omap_eac *eac)
+{
+ if (!eac->clocks_enabled)
+ return;
+ eac->clocks_enabled = 0;
+
+ clk_disable(eac->fck);
+ clk_disable(eac->ick);
+ if (eac->pdata != NULL && eac->pdata->disable_ext_clocks != NULL)
+ eac->pdata->disable_ext_clocks(&eac->pdev->dev);
+}
+
+static int eac_reset(struct omap_eac *eac)
+{
+ int i;
+
+ /* step 1 (see TRM) */
+ /* first, let's reset the EAC */
+ eac_write_reg(eac, EAC_SYSCONFIG, 0x2);
+ /* step 2 (see TRM) */
+ eac_write_reg(eac, EAC_AGCTR, AGCTR_MCLK_EN | AGCTR_AUDEN);
+ /* step 3 (see TRM) */
+ /* wait until reset done */
+ i = 10000;
+ while (!(eac_read_reg(eac, EAC_SYSSTATUS) & 1)) {
+ if (--i == 0)
+ return -ENODEV;
+ udelay(1);
+ }
+
+ return 0;
+}
+
+static int eac_calc_agcfr3_fsint(int rate)
+{
+ int fsint;
+
+ if (rate >= 48000)
+ fsint = AGCFR3_FSINT_48000;
+ else if (rate >= 44100)
+ fsint = AGCFR3_FSINT_44100;
+ else if (rate >= 32000)
+ fsint = AGCFR3_FSINT_32000;
+ else if (rate >= 24000)
+ fsint = AGCFR3_FSINT_24000;
+ else if (rate >= 22050)
+ fsint = AGCFR3_FSINT_22050;
+ else if (rate >= 16000)
+ fsint = AGCFR3_FSINT_16000;
+ else if (rate >= 11025)
+ fsint = AGCFR3_FSINT_11025;
+ else
+ fsint = AGCFR3_FSINT_8000;
+
+ return fsint;
+}
+
+static int eac_configure_pcm(struct omap_eac *eac, struct eac_codec *conf)
+{
+ dev_err(&eac->pdev->dev,
+ "EAC codec port configuration for PCM not implemented\n");
+
+ return -ENODEV;
+}
+
+static int eac_configure_ac97(struct omap_eac *eac, struct eac_codec *conf)
+{
+ dev_err(&eac->pdev->dev,
+ "EAC codec port configuration for AC97 not implemented\n");
+
+ return -ENODEV;
+}
+
+static int eac_configure_i2s(struct omap_eac *eac, struct eac_codec *conf)
+{
+ u16 cpcfr1, cpcfr2, cpcfr3, cpcfr4;
+
+ cpcfr1 = eac_read_reg(eac, EAC_CPCFR1);
+ cpcfr2 = eac_read_reg(eac, EAC_CPCFR2);
+ cpcfr3 = eac_read_reg(eac, EAC_CPCFR3);
+ cpcfr4 = eac_read_reg(eac, EAC_CPCFR4);
+
+ cpcfr1 &= ~(CPCFR1_MODE_BITS | CPCFR1_MTSL_BITS);
+ cpcfr1 |= CPCFR1_MTSL(1); /* 2 timeslots per frame (I2S default) */
+
+ /* audio time slot configuration for I2S mode */
+ cpcfr2 &= ~(CPCFR2_TSLL_BITS | CPCFR2_BPTSL_BITS | CPCFR2_TSLOL_BITS);
+ cpcfr2 |= CPCFR2_TSLOL(0); /* time slot 0 length same as TSLL */
+ cpcfr2 |= CPCFR2_BPTSL(1); /* 16 data bits per time slot */
+ cpcfr2 |= CPCFR2_TSLL(1); /* time slot length 16 serial clock cycles */
+
+ /* I2S link configuration */
+ MOD_REG_BIT(cpcfr3, CPCFR3_DDLY,
+ conf->codec_conf.i2s.sync_delay_enable); /* 0/1 clk delay */
+ /* data serial output enabled during nonvalid audio frames, clock
+ * polarity = falling edge, CSYNC lenght equal to time slot0 length */
+ MOD_REG_BIT(cpcfr3, CPCFR3_TRSEN, 1);
+ MOD_REG_BIT(cpcfr3, CPCFR3_CLKBP, 1);
+ MOD_REG_BIT(cpcfr3, CPCFR3_CSYNCL, 1);
+
+ cpcfr4 &= ~(CPCFR4_DIVB_BITS | CPCFR4_ATSL_BITS);
+ cpcfr4 |= CPCFR4_DIVB(7); /* CP_SCLK = MCLK / 8 */
+
+ /* configuration for normal I2S or polarity-changed I2S */
+ if (!conf->codec_conf.i2s.polarity_changed_mode) {
+ cpcfr1 |= CPCFR1_MODE(4); /* I2S mode */
+ MOD_REG_BIT(cpcfr3, CPCFR3_CSYNCP, 0); /* CP_SYNC active low */
+ /* audio time slots configuration for I2S */
+ cpcfr4 |= CPCFR4_ATSL(0);
+ } else {
+ cpcfr1 |= CPCFR1_MODE(1); /* PCM mode/polarity-changed I2S */
+ MOD_REG_BIT(cpcfr3, CPCFR3_CSYNCP, 1); /* CP_SYNC active
+ high */
+ /* audio time slots configuration for polarity-changed I2S */
+ cpcfr4 |= CPCFR4_ATSL(0xf);
+ };
+
+ /* master/slave configuration */
+ if (conf->codec_mode == EAC_CODEC_I2S_MASTER) {
+ /* EAC is master. Set CP_SCLK and CP_SYNC as outputs */
+ MOD_REG_BIT(cpcfr3, CPCFR3_CSCLKD, 0);
+ MOD_REG_BIT(cpcfr3, CPCFR3_CSYNCD, 0);
+ } else {
+ /* EAC is slave. Set CP_SCLK and CP_SYNC as inputs */
+ MOD_REG_BIT(cpcfr3, CPCFR3_CSCLKD, 1);
+ MOD_REG_BIT(cpcfr3, CPCFR3_CSYNCD, 1);
+ }
+
+ eac_write_reg(eac, EAC_CPCFR1, cpcfr1);
+ eac_write_reg(eac, EAC_CPCFR2, cpcfr2);
+ eac_write_reg(eac, EAC_CPCFR3, cpcfr3);
+ eac_write_reg(eac, EAC_CPCFR4, cpcfr4);
+
+ return 0;
+}
+
+static int eac_codec_port_init(struct omap_eac *eac, struct eac_codec *conf)
+{
+ u16 agcfr, agcfr2, agcfr3, agctr;
+ u16 cpctl, reg;
+ int err = 0, i;
+
+ /* use internal MCLK gating before doing full configuration for it.
+ * Partial or misconfigured MCLK will cause that access to some of the
+ * EAC registers causes "external abort on linefetch". Same happens
+ * also when using external clock as a MCLK source and if that clock is
+ * either missing or not having a right rate (e.g. half of it) */
+ agcfr3 = eac_read_reg(eac, EAC_AGCFR3);
+ MOD_REG_BIT(agcfr3, AGCFR3_MCLKINT_SEL, 1); /* 96 Mhz / 8.5 */
+ eac_write_reg(eac, EAC_AGCFR3, agcfr3);
+
+ /* disable codec port, enable access to config registers */
+ cpctl = eac_read_reg(eac, EAC_CPTCTL);
+ MOD_REG_BIT(cpctl, CPTCTL_CPEN, 0);
+ eac_write_reg(eac, EAC_CPTCTL, cpctl);
+
+ agcfr = eac_read_reg(eac, EAC_AGCFR);
+ agctr = eac_read_reg(eac, EAC_AGCTR);
+ agcfr2 = eac_read_reg(eac, EAC_AGCFR2);
+
+ /* MCLK source and frequency configuration */
+ MOD_REG_BIT(agcfr, AGCFR_MCLK, 0);
+ switch (conf->mclk_src) {
+ case EAC_MCLK_EXT_2x11289600:
+ MOD_REG_BIT(agcfr, AGCFR_MCLK, 1); /* div by 2 path */
+ MOD_REG_BIT(agcfr, AGCFR_MCLK_OUT, 1); /* div by 2 */
+ case EAC_MCLK_EXT_11289600:
+ MOD_REG_BIT(agcfr, AGCFR_MCLK, 1);
+ MOD_REG_BIT(agcfr2, AGCFR2_I2S_N44K_48K, 0); /* 44.1 kHz */
+ MOD_REG_BIT(agcfr2, AGCFR2_MCLK_I2S_N11M_12M, 0); /* 11.2896 */
+ MOD_REG_BIT(agcfr3, AGCFR3_MCLKINT_SEL, 0);
+ break;
+
+ case EAC_MCLK_EXT_2x12288000:
+ MOD_REG_BIT(agcfr, AGCFR_MCLK, 1); /* div by 2 path */
+ MOD_REG_BIT(agcfr, AGCFR_MCLK_OUT, 1); /* div by 2 */
+ case EAC_MCLK_EXT_12288000:
+ MOD_REG_BIT(agcfr2, AGCFR2_I2S_N44K_48K, 1); /* 48 kHz */
+ MOD_REG_BIT(agcfr2, AGCFR2_MCLK_I2S_N11M_12M, 1); /* 12.288 */
+ MOD_REG_BIT(agcfr3, AGCFR3_MCLKINT_SEL, 0);
+ break;
+
+ default:
+ /* internal MCLK gating */
+ break;
+ }
+ MOD_REG_BIT(agctr, AGCTR_MCLK_EN, 1);
+ MOD_REG_BIT(agctr, AGCTR_OSCMCLK_EN, 1); /* oscillator enabled? */
+ /* use MCLK just configured above as audio & codec port clock source */
+ agcfr3 &= ~AGCFR3_AUD_CKSRC_BITS;
+ agcfr3 |= AGCFR3_AUD_CKSRC(0);
+
+ /* audio data format */
+ MOD_REG_BIT(agcfr, AGCFR_MN_ST, 1); /* stereo file */
+ MOD_REG_BIT(agcfr, AGCFR_B8_16, 1); /* 16 bit audio file */
+ MOD_REG_BIT(agcfr, AGCFR_LI_BI, 0); /* little endian stream */
+
+ /* there are FSINT configuration bits in AGCFR, AGCFR2 and AGCFR3
+ * registers but it seems that it is just enough to set in AGCFR3
+ * only */
+ agcfr3 &= ~AGCFR3_FSINT_BITS;
+ agcfr3 |= AGCFR3_FSINT(eac_calc_agcfr3_fsint(conf->default_rate));
+
+ /* transparent DMA enable bits */
+ MOD_REG_BIT(agcfr3, AGCFR3_MD_TR_DMA, 1); /* modem */
+ MOD_REG_BIT(agcfr3, AGCFR3_BT_TR_DMA, 1); /* BT */
+ if (conf->codec_mode != EAC_CODEC_I2S_SLAVE)
+ MOD_REG_BIT(agcfr3, AGCFR3_CP_TR_DMA, 0);
+ else
+ MOD_REG_BIT(agcfr3, AGCFR3_CP_TR_DMA, 1);
+
+ /* step 4 (see TRM) */
+ eac_write_reg(eac, EAC_AGCFR3, agcfr3);
+ /* pre-write AGCTR now (finally in step 10) in order to get MCLK
+ * settings effective (especially when using external MCLK) */
+ eac_write_reg(eac, EAC_AGCTR, agctr);
+ eac_write_reg(eac, EAC_AGCFR2, agcfr2);
+
+ /* step 5 (see TRM) */
+ eac_write_reg(eac, EAC_AGCFR, agcfr);
+
+ /* step 6 (see TRM) */
+ /* wait until audio reset done */
+ i = 10000;
+ while (!(eac_read_reg(eac, EAC_SYSSTATUS) & (1 << 3))) {
+ if (--i == 0)
+ return -ETIMEDOUT;
+ udelay(1);
+ }
+
+ /* step 7 (see TRM) */
+ reg = eac_read_reg(eac, EAC_AMSCFR);
+ MOD_REG_BIT(reg, AMSCFR_K1, 1); /* K1 switch closed */
+ MOD_REG_BIT(reg, AMSCFR_K5, 1); /* K5 switch closed */
+ MOD_REG_BIT(reg, AMSCFR_K2, 0); /* K2 switch open */
+ MOD_REG_BIT(reg, AMSCFR_K6, 0); /* K6 switch open */
+ eac_write_reg(eac, EAC_AMSCFR, reg);
+
+ /* step 8 (see TRM) */
+ switch (conf->codec_mode) {
+ case EAC_CODEC_PCM:
+ err = eac_configure_pcm(eac, conf);
+ break;
+ case EAC_CODEC_AC97:
+ err = eac_configure_ac97(eac, conf);
+ break;
+ default:
+ err = eac_configure_i2s(eac, conf);
+ break;
+ }
+
+ /* step 9 (see TRM) */
+ MOD_REG_BIT(cpctl, CPTCTL_CPEN, 1); /* codec port enable */
+ MOD_REG_BIT(cpctl, CPTCTL_RXIE, 1); /* receive int enable */
+ MOD_REG_BIT(cpctl, CPTCTL_TXIE, 1); /* transmit int enable */
+ eac_write_reg(eac, EAC_CPTCTL, cpctl);
+
+ /* step 10 (see TRM) */
+ /* enable playing & recording */
+ MOD_REG_BIT(agctr, AGCTR_DMAREN, 1); /* playing enabled (DMA R) */
+ MOD_REG_BIT(agctr, AGCTR_DMAWEN, 1); /* recording enabled (DMA W) */
+ MOD_REG_BIT(agctr, AGCTR_AUDEN, 1); /* audio processing enabled */
+ eac_write_reg(eac, EAC_AGCTR, agctr);
+
+ /* audio mixer1, no mute on mixer output, gain = 0 dB */
+ reg = eac_read_reg(eac, EAC_AM1VCTR);
+ MOD_REG_BIT(reg, AM1VCTR_MUTE, 0);
+ reg = ((reg & ~AM1VCTR_GINB_BITS) | (AM1VCTR_GINB(0x67)));
+ eac_write_reg(eac, EAC_AM1VCTR, reg);
+
+ /* audio mixer3, no mute on mixer output, gain = 0 dB */
+ reg = eac_read_reg(eac, EAC_AM3VCTR);
+ MOD_REG_BIT(reg, AM3VCTR_MUTE, 0);
+ reg = ((reg & ~AM3VCTR_GINB_BITS) | (AM3VCTR_GINB(0x67)));
+ eac_write_reg(eac, EAC_AM3VCTR, reg);
+
+ /* audio side tone disabled */
+ eac_write_reg(eac, EAC_ASTCTR, 0x0);
+
+ return 0;
+}
+
+int eac_set_mode(struct device *dev, int play, int rec)
+{
+ struct omap_eac *eac = dev_get_drvdata(dev);
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "EAC mode: play %s, rec %s\n",
+ play ? "enabled" : "disabled",
+ rec ? "enabled" : "disabled");
+#endif
+ BUG_ON(eac == NULL);
+ mutex_lock(&eac->mutex);
+ if (play || rec) {
+ /* activate clocks */
+ eac_enable_clocks(eac);
+
+ /* power-up codec */
+ if (eac->codec != NULL && eac->codec->set_power != NULL)
+ eac->codec->set_power(eac->codec->private_data,
+ play, rec);
+ } else {
+ /* shutdown codec */
+ if (eac->codec != NULL && eac->codec->set_power != NULL)
+ eac->codec->set_power(eac->codec->private_data, 0, 0);
+
+ /* de-activate clocks */
+ eac_disable_clocks(eac);
+ }
+ mutex_unlock(&eac->mutex);
+
+ return 0;
+}
+
+int eac_register_codec(struct device *dev, struct eac_codec *codec)
+{
+ struct omap_eac *eac = dev_get_drvdata(dev);
+ struct snd_card *card = eac->card;
+ int err;
+
+ BUG_ON(eac->codec != NULL);
+
+ mutex_lock(&eac->mutex);
+ eac->codec = codec;
+ eac_enable_clocks(eac);
+ err = eac_codec_port_init(eac, codec);
+ eac_disable_clocks(eac);
+ mutex_unlock(&eac->mutex);
+ if (err)
+ return err;
+
+ /* register mixer controls implemented by a codec driver */
+ if (codec->register_controls != NULL) {
+ err = codec->register_controls(codec->private_data, card);
+ if (err)
+ return err;
+ }
+
+ if (codec->short_name != NULL) {
+ sprintf(card->longname, "%s with codec %s", card->shortname,
+ codec->short_name);
+ strcpy(card->mixername, codec->short_name);
+ }
+
+ err = snd_card_register(card);
+ return err;
+}
+
+void eac_unregister_codec(struct device *dev)
+{
+ struct omap_eac *eac = dev_get_drvdata(dev);
+
+ BUG_ON(eac->codec == NULL);
+ eac_set_mode(dev, 0, 0);
+ snd_card_disconnect(eac->card);
+ eac->codec = NULL;
+}
+
+static int __devinit eac_probe(struct platform_device *pdev)
+{
+ struct eac_platform_data *pdata = pdev->dev.platform_data;
+ struct snd_card *card;
+ struct omap_eac *eac;
+ struct resource *res;
+ int err;
+
+ eac = kzalloc(sizeof(*eac), GFP_KERNEL);
+ if (!eac)
+ return -ENOMEM;
+
+ mutex_init(&eac->mutex);
+ eac->pdev = pdev;
+ platform_set_drvdata(pdev, eac);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ err = -ENODEV;
+ goto err1;
+ }
+ eac->base = (void __iomem *)io_p2v(res->start);
+ eac->pdata = pdata;
+
+ /* pre-initialize EAC hw */
+ err = eac_get_clocks(eac);
+ if (err)
+ goto err1;
+ err = eac_enable_clocks(eac);
+ if (err)
+ goto err2;
+
+ err = eac_reset(eac);
+ if (err)
+ goto err3;
+
+ dev_info(&pdev->dev, "EAC version: %d.%d\n",
+ eac_read_reg(eac, EAC_VERSION) >> 4,
+ eac_read_reg(eac, EAC_VERSION) & 0x0f);
+ eac_disable_clocks(eac);
+
+ /* create soundcard instance */
+ card = snd_card_new(-1, id, THIS_MODULE, 0);
+ if (card == NULL) {
+ err = -ENOMEM;
+ goto err3;
+ }
+ eac->card = card;
+ strcpy(card->driver, "EAC");
+ strcpy(card->shortname, "OMAP24xx EAC");
+
+ sprintf(card->longname, "%s", card->shortname);
+ strcpy(card->mixername, "EAC Mixer");
+
+ if (eac->pdata->init) {
+ err = eac->pdata->init(&pdev->dev);
+ if (err < 0) {
+ printk("init %d\n", err);
+ goto err4;
+ }
+ }
+
+ return 0;
+
+err4:
+ snd_card_free(card);
+err3:
+ eac_disable_clocks(eac);
+err2:
+ eac_put_clocks(eac);
+err1:
+ kfree(eac);
+ return err;
+}
+
+static int __devexit eac_remove(struct platform_device *pdev)
+{
+ struct omap_eac *eac = platform_get_drvdata(pdev);
+ struct snd_card *card = eac->card;
+
+ snd_card_free(card);
+
+ eac_disable_clocks(eac);
+ eac_put_clocks(eac);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver eac_driver = {
+ .driver = {
+ .name = "omap24xx-eac",
+ .bus = &platform_bus_type,
+ },
+ .probe = eac_probe,
+ .remove = eac_remove,
+};
+
+int __init eac_init(void)
+{
+ return platform_driver_register(&eac_driver);
+}
+
+void __exit eac_exit(void)
+{
+ platform_driver_unregister(&eac_driver);
+}
+
+module_init(eac_init);
+module_exit(eac_exit);
+MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/aic23.h>
+/*
+ * sound/arm/omap/omap-alsa-aic23-mixer.c
+ *
+ * Alsa Driver Mixer for generic codecs for omap boards
+ *
+ * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ * Written by David Cohen, Daniel Petrini
+ * {david.cohen, daniel.petrini}@indt.org.br
+ *
+ * Based on es1688_lib.c,
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * History:
+ *
+ * 2005-08-02 INdT Kernel Team - Alsa mixer driver for omap osk.
+ * Creation of new file omap-alsa-mixer.c.
+ * Initial version with aic23 codec for osk5912
+ */
+
+#include <linux/kernel.h>
+
- #include <asm/arch/omap-alsa.h>
++#include <mach/aic23.h>
+
++#include <mach/omap-alsa.h>
+#include "omap-alsa-aic23.h"
+#include <sound/initval.h>
+#include <sound/control.h>
+
+MODULE_AUTHOR("David Cohen");
+MODULE_AUTHOR("Daniel Petrini");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("OMAP Alsa mixer driver for ALSA");
+
+/*
+ * Codec dependent region
+ */
+
+/* Codec AIC23 */
+#if defined(CONFIG_SENSORS_TLV320AIC23) || \
+ defined(CONFIG_SENSORS_TLV320AIC23_MODULE)
+
+#define MIXER_NAME "Mixer AIC23"
+#define SND_OMAP_WRITE(reg, val) audio_aic23_write(reg, val)
+
+#endif
+
+/* Callback Functions */
+#define OMAP_BOOL(xname, xindex, reg, reg_index, mask, invert) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_omap_info_bool, \
+ .get = snd_omap_get_bool, \
+ .put = snd_omap_put_bool, \
+ .private_value = reg | (reg_index << 8) | (invert << 10) | \
+ (mask << 12) \
+}
+
+#define OMAP_MUX(xname, reg, reg_index, mask) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = snd_omap_info_mux, \
+ .get = snd_omap_get_mux, \
+ .put = snd_omap_put_mux, \
+ .private_value = reg | (reg_index << 8) | (mask << 10) \
+}
+
+#define OMAP_SINGLE(xname, xindex, reg, reg_index, reg_val, mask) \
+{\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_omap_info_single, \
+ .get = snd_omap_get_single, \
+ .put = snd_omap_put_single, \
+ .private_value = reg | (reg_val << 8) | (reg_index << 16) |\
+ (mask << 18) \
+}
+
+#define OMAP_DOUBLE(xname, xindex, left_reg, right_reg, reg_index, mask) \
+{\
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_omap_info_double, \
+ .get = snd_omap_get_double, \
+ .put = snd_omap_put_double, \
+ .private_value = left_reg | (right_reg << 8) | (reg_index << 16) | \
+ (mask << 18) \
+}
+
+/* Local Registers */
+enum snd_device_index {
+ PCM_INDEX = 0,
+ LINE_INDEX,
+ AAC_INDEX, /* Analog Audio Control: reg = l_reg */
+};
+
+struct {
+ u16 l_reg;
+ u16 r_reg;
+ u8 sw;
+} omap_regs[3];
+
+#ifdef CONFIG_PM
+struct {
+ u16 l_reg;
+ u16 r_reg;
+ u8 sw;
+} omap_pm_regs[3];
+#endif
+
+u16 snd_sidetone[6] = {
+ SIDETONE_18,
+ SIDETONE_12,
+ SIDETONE_9,
+ SIDETONE_6,
+ SIDETONE_0,
+ 0
+};
+
+/* Begin Bool Functions */
+
+static int snd_omap_info_bool(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+
+static int snd_omap_get_bool(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mic_index = (kcontrol->private_value >> 8) & 0x03;
+ u16 mask = (kcontrol->private_value >> 12) & 0xff;
+ int invert = (kcontrol->private_value >> 10) & 0x03;
+
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ (omap_regs[mic_index].l_reg & mask) ? 0 : 1;
+ else
+ ucontrol->value.integer.value[0] =
+ (omap_regs[mic_index].l_reg & mask) ? 1 : 0;
+
+ return 0;
+}
+
+static int snd_omap_put_bool(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mic_index = (kcontrol->private_value >> 8) & 0x03;
+ u16 mask = (kcontrol->private_value >> 12) & 0xff;
+ u16 reg = kcontrol->private_value & 0xff;
+ int invert = (kcontrol->private_value >> 10) & 0x03;
+
+ int changed = 1;
+
+ if (ucontrol->value.integer.value[0]) /* XOR */
+ if (invert)
+ omap_regs[mic_index].l_reg &= ~mask;
+ else
+ omap_regs[mic_index].l_reg |= mask;
+ else
+ if (invert)
+ omap_regs[mic_index].l_reg |= mask;
+ else
+ omap_regs[mic_index].l_reg &= ~mask;
+
+ SND_OMAP_WRITE(reg, omap_regs[mic_index].l_reg);
+
+ return changed;
+}
+
+/* End Bool Functions */
+
+/* Begin Mux Functions */
+
+static int snd_omap_info_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ /* Mic = 0
+ * Line = 1 */
+ static char *texts[2] = { "Mic", "Line" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int snd_omap_get_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 mask = (kcontrol->private_value >> 10) & 0xff;
+ int mux_idx = (kcontrol->private_value >> 8) & 0x03;
+
+ ucontrol->value.enumerated.item[0] =
+ (omap_regs[mux_idx].l_reg & mask) ? 0 /* Mic */ : 1 /* Line */;
+
+ return 0;
+}
+
+static int snd_omap_put_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 reg = kcontrol->private_value & 0xff;
+ u16 mask = (kcontrol->private_value >> 10) & 0xff;
+ int mux_index = (kcontrol->private_value >> 8) & 0x03;
+
+ int changed = 1;
+
+ if (!ucontrol->value.integer.value[0])
+ omap_regs[mux_index].l_reg |= mask; /* AIC23: Mic */
+ else
+ omap_regs[mux_index].l_reg &= ~mask; /* AIC23: Line */
+
+ SND_OMAP_WRITE(reg, omap_regs[mux_index].l_reg);
+
+ return changed;
+}
+
+/* End Mux Functions */
+
+/* Begin Single Functions */
+
+static int snd_omap_info_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int mask = (kcontrol->private_value >> 18) & 0xff;
+ int reg_val = (kcontrol->private_value >> 8) & 0xff;
+
+ uinfo->type = mask ? SNDRV_CTL_ELEM_TYPE_INTEGER :
+ SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = reg_val-1;
+
+ return 0;
+}
+
+static int snd_omap_get_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 reg_val = (kcontrol->private_value >> 8) & 0xff;
+
+ ucontrol->value.integer.value[0] = snd_sidetone[reg_val];
+
+ return 0;
+}
+
+static int snd_omap_put_single(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u16 reg_index = (kcontrol->private_value >> 16) & 0x03;
+ u16 mask = (kcontrol->private_value >> 18) & 0x1ff;
+ u16 reg = kcontrol->private_value & 0xff;
+ u16 reg_val = (kcontrol->private_value >> 8) & 0xff;
+
+ int changed = 0;
+
+ /* Volume */
+ if ((omap_regs[reg_index].l_reg !=
+ (ucontrol->value.integer.value[0] & mask))) {
+ changed = 1;
+
+ omap_regs[reg_index].l_reg &= ~mask;
+ omap_regs[reg_index].l_reg |=
+ snd_sidetone[ucontrol->value.integer.value[0]];
+
+ snd_sidetone[reg_val] = ucontrol->value.integer.value[0];
+ SND_OMAP_WRITE(reg, omap_regs[reg_index].l_reg);
+ } else {
+ changed = 0;
+ }
+
+ return changed;
+}
+
+/* End Single Functions */
+
+/* Begin Double Functions */
+
+static int snd_omap_info_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ /*
+ * mask == 0 : Switch
+ * mask != 0 : Volume
+ */
+ int mask = (kcontrol->private_value >> 18) & 0xff;
+
+ uinfo->type = mask ? SNDRV_CTL_ELEM_TYPE_INTEGER :
+ SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = mask ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask ? mask : 1;
+
+ return 0;
+}
+
+static int snd_omap_get_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /*
+ * mask == 0 : Switch
+ * mask != 0 : Volume
+ */
+ int mask = (kcontrol->private_value >> 18) & 0xff;
+ int vol_index = (kcontrol->private_value >> 16) & 0x03;
+
+ if (!mask) {
+ /* Switch */
+ ucontrol->value.integer.value[0] = omap_regs[vol_index].sw;
+ } else {
+ /* Volume */
+ ucontrol->value.integer.value[0] = omap_regs[vol_index].l_reg;
+ ucontrol->value.integer.value[1] = omap_regs[vol_index].r_reg;
+ }
+
+ return 0;
+}
+
+static int snd_omap_put_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* mask == 0 : Switch
+ * mask != 0 : Volume */
+ int vol_index = (kcontrol->private_value >> 16) & 0x03;
+ int mask = (kcontrol->private_value >> 18) & 0xff;
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+
+ int changed = 0;
+
+ if (!mask) {
+ /* Switch */
+ if (!ucontrol->value.integer.value[0]) {
+ SND_OMAP_WRITE(left_reg, 0x00);
+ SND_OMAP_WRITE(right_reg, 0x00);
+ } else {
+ SND_OMAP_WRITE(left_reg, omap_regs[vol_index].l_reg);
+ SND_OMAP_WRITE(right_reg, omap_regs[vol_index].r_reg);
+ }
+ changed = 1;
+ omap_regs[vol_index].sw = ucontrol->value.integer.value[0];
+ } else {
+ /* Volume */
+ if ((omap_regs[vol_index].l_reg !=
+ (ucontrol->value.integer.value[0] & mask)) ||
+ (omap_regs[vol_index].r_reg !=
+ (ucontrol->value.integer.value[1] & mask))) {
+ changed = 1;
+
+ omap_regs[vol_index].l_reg &= ~mask;
+ omap_regs[vol_index].r_reg &= ~mask;
+ omap_regs[vol_index].l_reg |=
+ (ucontrol->value.integer.value[0] & mask);
+ omap_regs[vol_index].r_reg |=
+ (ucontrol->value.integer.value[1] & mask);
+ if (omap_regs[vol_index].sw) {
+ /* write to registers only if sw is actived */
+ SND_OMAP_WRITE(left_reg,
+ omap_regs[vol_index].l_reg);
+ SND_OMAP_WRITE(right_reg,
+ omap_regs[vol_index].r_reg);
+ }
+ } else {
+ changed = 0;
+ }
+ }
+
+ return changed;
+}
+
+/* End Double Functions */
+
+static struct snd_kcontrol_new snd_omap_controls[] = {
+ OMAP_DOUBLE("PCM Playback Switch", 0, LEFT_CHANNEL_VOLUME_ADDR,
+ RIGHT_CHANNEL_VOLUME_ADDR, PCM_INDEX, 0x00),
+ OMAP_DOUBLE("PCM Playback Volume", 0, LEFT_CHANNEL_VOLUME_ADDR,
+ RIGHT_CHANNEL_VOLUME_ADDR, PCM_INDEX,
+ OUTPUT_VOLUME_MASK),
+ OMAP_BOOL("Line Playback Switch", 0, ANALOG_AUDIO_CONTROL_ADDR,
+ AAC_INDEX, BYPASS_ON, 0),
+ OMAP_DOUBLE("Line Capture Switch", 0, LEFT_LINE_VOLUME_ADDR,
+ RIGHT_LINE_VOLUME_ADDR, LINE_INDEX, 0x00),
+ OMAP_DOUBLE("Line Capture Volume", 0, LEFT_LINE_VOLUME_ADDR,
+ RIGHT_LINE_VOLUME_ADDR, LINE_INDEX, INPUT_VOLUME_MASK),
+ OMAP_BOOL("Mic Playback Switch", 0, ANALOG_AUDIO_CONTROL_ADDR,
+ AAC_INDEX, STE_ENABLED, 0),
+ OMAP_SINGLE("Mic Playback Volume", 0, ANALOG_AUDIO_CONTROL_ADDR,
+ AAC_INDEX, 5, SIDETONE_MASK),
+ OMAP_BOOL("Mic Capture Switch", 0, ANALOG_AUDIO_CONTROL_ADDR,
+ AAC_INDEX, MICM_MUTED, 1),
+ OMAP_BOOL("Mic Booster Playback Switch", 0, ANALOG_AUDIO_CONTROL_ADDR,
+ AAC_INDEX, MICB_20DB, 0),
+ OMAP_MUX("Capture Source", ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX,
+ INSEL_MIC),
+};
+
+#ifdef CONFIG_PM
+
+void snd_omap_suspend_mixer(void)
+{
+ /* Saves current values to wake-up correctly */
+ omap_pm_regs[LINE_INDEX].l_reg = omap_regs[LINE_INDEX].l_reg;
+ omap_pm_regs[LINE_INDEX].r_reg = omap_regs[LINE_INDEX].l_reg;
+ omap_pm_regs[LINE_INDEX].sw = omap_regs[LINE_INDEX].sw;
+
+ omap_pm_regs[AAC_INDEX].l_reg = omap_regs[AAC_INDEX].l_reg;
+
+ omap_pm_regs[PCM_INDEX].l_reg = omap_regs[PCM_INDEX].l_reg;
+ omap_pm_regs[PCM_INDEX].r_reg = omap_regs[PCM_INDEX].r_reg;
+ omap_pm_regs[PCM_INDEX].sw = omap_regs[PCM_INDEX].sw;
+}
+
+void snd_omap_resume_mixer(void)
+{
+ /* Line's saved values */
+ omap_regs[LINE_INDEX].l_reg = omap_pm_regs[LINE_INDEX].l_reg;
+ omap_regs[LINE_INDEX].r_reg = omap_pm_regs[LINE_INDEX].l_reg;
+ omap_regs[LINE_INDEX].sw = omap_pm_regs[LINE_INDEX].sw;
+ SND_OMAP_WRITE(LEFT_LINE_VOLUME_ADDR, omap_pm_regs[LINE_INDEX].l_reg);
+ SND_OMAP_WRITE(RIGHT_LINE_VOLUME_ADDR, omap_pm_regs[LINE_INDEX].l_reg);
+
+ /* Analog Audio Control's saved values */
+ omap_regs[AAC_INDEX].l_reg = omap_pm_regs[AAC_INDEX].l_reg;
+ SND_OMAP_WRITE(ANALOG_AUDIO_CONTROL_ADDR, omap_regs[AAC_INDEX].l_reg);
+
+ /* Headphone's saved values */
+ omap_regs[PCM_INDEX].l_reg = omap_pm_regs[PCM_INDEX].l_reg;
+ omap_regs[PCM_INDEX].r_reg = omap_pm_regs[PCM_INDEX].r_reg;
+ omap_regs[PCM_INDEX].sw = omap_pm_regs[PCM_INDEX].sw;
+ SND_OMAP_WRITE(LEFT_CHANNEL_VOLUME_ADDR,
+ omap_pm_regs[PCM_INDEX].l_reg);
+ SND_OMAP_WRITE(RIGHT_CHANNEL_VOLUME_ADDR,
+ omap_pm_regs[PCM_INDEX].r_reg);
+}
+#endif
+
+void snd_omap_init_mixer(void)
+{
+ u16 vol_reg;
+
+ /* Line's default values */
+ omap_regs[LINE_INDEX].l_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK;
+ omap_regs[LINE_INDEX].r_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK;
+ omap_regs[LINE_INDEX].sw = 0;
+ SND_OMAP_WRITE(LEFT_LINE_VOLUME_ADDR,
+ DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK);
+ SND_OMAP_WRITE(RIGHT_LINE_VOLUME_ADDR,
+ DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK);
+
+ /* Analog Audio Control's default values */
+ omap_regs[AAC_INDEX].l_reg = DEFAULT_ANALOG_AUDIO_CONTROL;
+
+ /* Headphone's default values */
+ vol_reg = LZC_ON;
+ vol_reg &= ~OUTPUT_VOLUME_MASK;
+ vol_reg |= DEFAULT_OUTPUT_VOLUME;
+ omap_regs[PCM_INDEX].l_reg = DEFAULT_OUTPUT_VOLUME;
+ omap_regs[PCM_INDEX].r_reg = DEFAULT_OUTPUT_VOLUME;
+ omap_regs[PCM_INDEX].sw = 1;
+ SND_OMAP_WRITE(LEFT_CHANNEL_VOLUME_ADDR, vol_reg);
+ SND_OMAP_WRITE(RIGHT_CHANNEL_VOLUME_ADDR, vol_reg);
+}
+
+int snd_omap_mixer(struct snd_card_omap_codec *chip)
+{
+ struct snd_card *card;
+ unsigned int idx;
+ int err;
+
+ snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+
+ card = chip->card;
+
+ strcpy(card->mixername, MIXER_NAME);
+
+ /* Registering alsa mixer controls */
+ for (idx = 0; idx < ARRAY_SIZE(snd_omap_controls); idx++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_omap_controls[idx], chip));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
--- /dev/null
- #include <asm/arch/clock.h>
- #include <asm/arch/aic23.h>
+/*
+ * arch/arm/mach-omap1/omap-alsa-aic23.c
+ *
+ * Alsa codec Driver for AIC23 chip on OSK5912 platform board
+ *
+ * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ * Written by Daniel Petrini, David Cohen, Anderson Briglia
+ * {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
+ *
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ *
+ * Based in former alsa driver for osk and oss driver
+ *
+ * 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.
+ */
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <linux/clk.h>
- #include <asm/arch/omap-alsa.h>
++#include <mach/clock.h>
++#include <mach/aic23.h>
+
++#include <mach/omap-alsa.h>
+#include "omap-alsa-aic23.h"
+
+static struct clk *aic23_mclk;
+
+/* aic23 related */
+static const struct aic23_samplerate_reg_info
+ rate_reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
+ {4000, 0x06, 1}, /* 4000 */
+ {8000, 0x06, 0}, /* 8000 */
+ {16000, 0x0C, 1}, /* 16000 */
+ {22050, 0x11, 1}, /* 22050 */
+ {24000, 0x00, 1}, /* 24000 */
+ {32000, 0x0C, 0}, /* 32000 */
+ {44100, 0x11, 0}, /* 44100 */
+ {48000, 0x00, 0}, /* 48000 */
+ {88200, 0x1F, 0}, /* 88200 */
+ {96000, 0x0E, 0}, /* 96000 */
+};
+
+/*
+ * Hardware capabilities
+ */
+
+ /*
+ * DAC USB-mode sampling rates (MCLK = 12 MHz)
+ * The rates and rate_reg_into MUST be in the same order
+ */
+static unsigned int rates[] = {
+ 4000, 8000, 16000, 22050,
+ 24000, 32000, 44100,
+ 48000, 88200, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list aic23_hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static struct snd_pcm_hardware aic23_snd_omap_alsa_playback = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_KNOT),
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8 * 1024,
+ .periods_min = 16,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware aic23_snd_omap_alsa_capture = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_KNOT),
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8 * 1024,
+ .periods_min = 16,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+/*
+ * Codec/mcbsp init and configuration section
+ * codec dependent code.
+ */
+
+/* TLV320AIC23 is a write only device */
+void audio_aic23_write(u8 address, u16 data)
+{
+ aic23_write_value(address, data);
+}
+EXPORT_SYMBOL_GPL(audio_aic23_write);
+
+/*
+ * Sample rate changing
+ */
+void aic23_set_samplerate(long rate)
+{
+ u8 count = 0;
+ u16 data = 0;
+
+ /* Fix the rate if it has a wrong value */
+ if (rate >= 96000)
+ rate = 96000;
+ else if (rate >= 88200)
+ rate = 88200;
+ else if (rate >= 48000)
+ rate = 48000;
+ else if (rate >= 44100)
+ rate = 44100;
+ else if (rate >= 32000)
+ rate = 32000;
+ else if (rate >= 24000)
+ rate = 24000;
+ else if (rate >= 22050)
+ rate = 22050;
+ else if (rate >= 16000)
+ rate = 16000;
+ else if (rate >= 8000)
+ rate = 8000;
+ else
+ rate = 4000;
+
+ /* Search for the right sample rate */
+ /* Verify what happens if the rate is not supported
+ * now it goes to 96Khz */
+ while ((rate_reg_info[count].sample_rate != rate) &&
+ (count < (NUMBER_SAMPLE_RATES_SUPPORTED - 1))) {
+ count++;
+ }
+
+ data = (rate_reg_info[count].divider << CLKIN_SHIFT) |
+ (rate_reg_info[count].control << BOSR_SHIFT) | USB_CLK_ON;
+
+ audio_aic23_write(SAMPLE_RATE_CONTROL_ADDR, data);
+}
+
+inline void aic23_configure(void)
+{
+ /* Reset codec */
+ audio_aic23_write(RESET_CONTROL_ADDR, 0);
+
+ /* Initialize the AIC23 internal state */
+
+ /*
+ * Analog audio path control, DAC selected,
+ * delete INSEL_MIC for line-in
+ */
+ audio_aic23_write(ANALOG_AUDIO_CONTROL_ADDR,
+ DEFAULT_ANALOG_AUDIO_CONTROL);
+
+ /* Digital audio path control, de-emphasis control 44.1kHz */
+ audio_aic23_write(DIGITAL_AUDIO_CONTROL_ADDR, DEEMP_44K);
+
+ /* Digital audio interface, master/slave mode, I2S, 16 bit */
+#ifdef AIC23_MASTER
+ audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR,
+ MS_MASTER | IWL_16 | FOR_DSP);
+#else
+ audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, IWL_16 | FOR_DSP);
+#endif
+
+ /* Enable digital interface */
+ audio_aic23_write(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON);
+}
+
+/*
+ * OMAP MCBSP clock configuration and Power Management
+ *
+ * Here we have some functions that allow clock to be enabled and
+ * disabled only when needed. Besides doing clock configuration
+ * it allows turn on/turn off audio when necessary.
+ */
+/*
+ * Do clock framework mclk search
+ */
+void aic23_clock_setup(void)
+{
+ aic23_mclk = clk_get(0, "mclk");
+}
+
+/*
+ * Do some sanity check, set clock rate, starts it and
+ * turn codec audio on
+ */
+int aic23_clock_on(void)
+{
+ uint curRate;
+
+ if (clk_get_usecount(aic23_mclk) > 0) {
+ /* MCLK is already in use */
+ printk(KERN_WARNING
+ "MCLK in use at %d Hz. We change it to %d Hz\n",
+ (uint) clk_get_rate(aic23_mclk),
+ CODEC_CLOCK);
+ }
+ curRate = (uint)clk_get_rate(aic23_mclk);
+ if (curRate != CODEC_CLOCK) {
+ if (clk_set_rate(aic23_mclk, CODEC_CLOCK)) {
+ printk(KERN_ERR
+ "Cannot set MCLK for AIC23 CODEC\n");
+ return -ECANCELED;
+ }
+ }
+ clk_enable(aic23_mclk);
+
+ printk(KERN_DEBUG
+ "MCLK = %d [%d], usecount = %d\n",
+ (uint) clk_get_rate(aic23_mclk), CODEC_CLOCK,
+ clk_get_usecount(aic23_mclk));
+
+ /* Now turn the audio on */
+ audio_aic23_write(POWER_DOWN_CONTROL_ADDR,
+ ~DEVICE_POWER_OFF & ~OUT_OFF & ~DAC_OFF &
+ ~ADC_OFF & ~MIC_OFF & ~LINE_OFF);
+ return 0;
+}
+
+/*
+ * Do some sanity check, turn clock off and then turn
+ * codec audio off
+ */
+int aic23_clock_off(void)
+{
+ if (clk_get_usecount(aic23_mclk) > 0) {
+ if (clk_get_rate(aic23_mclk) != CODEC_CLOCK) {
+ printk(KERN_WARNING
+ "MCLK for audio should be %d Hz. But is %d Hz\n",
+ (uint) clk_get_rate(aic23_mclk),
+ CODEC_CLOCK);
+ }
+
+ clk_disable(aic23_mclk);
+ }
+
+ audio_aic23_write(POWER_DOWN_CONTROL_ADDR,
+ DEVICE_POWER_OFF | OUT_OFF | DAC_OFF |
+ ADC_OFF | MIC_OFF | LINE_OFF);
+ return 0;
+}
+
+int aic23_get_default_samplerate(void)
+{
+ return DEFAULT_SAMPLE_RATE;
+}
+
+static int __devinit snd_omap_alsa_aic23_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct omap_alsa_codec_config *codec_cfg;
+
+ codec_cfg = pdev->dev.platform_data;
+ if (codec_cfg != NULL) {
+ codec_cfg->hw_constraints_rates = &aic23_hw_constraints_rates;
+ codec_cfg->snd_omap_alsa_playback =
+ &aic23_snd_omap_alsa_playback;
+ codec_cfg->snd_omap_alsa_capture = &aic23_snd_omap_alsa_capture;
+ codec_cfg->codec_configure_dev = aic23_configure;
+ codec_cfg->codec_set_samplerate = aic23_set_samplerate;
+ codec_cfg->codec_clock_setup = aic23_clock_setup;
+ codec_cfg->codec_clock_on = aic23_clock_on;
+ codec_cfg->codec_clock_off = aic23_clock_off;
+ codec_cfg->get_default_samplerate =
+ aic23_get_default_samplerate;
+ ret = snd_omap_alsa_post_probe(pdev, codec_cfg);
+ } else
+ ret = -ENODEV;
+ return ret;
+}
+
+static struct platform_driver omap_alsa_driver = {
+ .probe = snd_omap_alsa_aic23_probe,
+ .remove = snd_omap_alsa_remove,
+ .suspend = snd_omap_alsa_suspend,
+ .resume = snd_omap_alsa_resume,
+ .driver = {
+ .name = "omap_alsa_mcbsp",
+ },
+};
+
+static int __init omap_alsa_aic23_init(void)
+{
+ int err;
+
+ ADEBUG();
+ err = platform_driver_register(&omap_alsa_driver);
+
+ return err;
+}
+
+static void __exit omap_alsa_aic23_exit(void)
+{
+ ADEBUG();
+
+ platform_driver_unregister(&omap_alsa_driver);
+}
+
+module_init(omap_alsa_aic23_init);
+module_exit(omap_alsa_aic23_exit);
--- /dev/null
- #include <asm/arch/dma.h>
+/*
+ * sound/arm/omap-alsa-aic23.h
+ *
+ * Alsa Driver for AIC23 codec on OSK5912 platform board
+ *
+ * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ * Written by Daniel Petrini, David Cohen, Anderson Briglia
+ * {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
+ *
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ *
+ * 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.
+ */
+
+#ifndef __OMAP_ALSA_AIC23_H
+#define __OMAP_ALSA_AIC23_H
+
- #include <asm/arch/mcbsp.h>
++#include <mach/dma.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
++#include <mach/mcbsp.h>
+
+/* Define to set the AIC23 as the master w.r.t McBSP */
+#define AIC23_MASTER
+
+#define NUMBER_SAMPLE_RATES_SUPPORTED 10
+
+/*
+ * AUDIO related MACROS
+ */
+#ifndef DEFAULT_BITPERSAMPLE
+#define DEFAULT_BITPERSAMPLE 16
+#endif
+
+#define DEFAULT_SAMPLE_RATE 44100
+#define CODEC_CLOCK 12000000
+#define AUDIO_MCBSP OMAP_MCBSP1
+
+#define DEFAULT_OUTPUT_VOLUME 0x60
+#define DEFAULT_INPUT_VOLUME 0x00 /* 0 ==> mute line in */
+
+#define OUTPUT_VOLUME_MIN LHV_MIN
+#define OUTPUT_VOLUME_MAX LHV_MAX
+#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)
+#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MAX
+
+#define INPUT_VOLUME_MIN LIV_MIN
+#define INPUT_VOLUME_MAX LIV_MAX
+#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
+#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX
+
+#define SIDETONE_MASK 0x1c0
+#define SIDETONE_0 0x100
+#define SIDETONE_6 0x000
+#define SIDETONE_9 0x040
+#define SIDETONE_12 0x080
+#define SIDETONE_18 0x0c0
+
+#define DEFAULT_ANALOG_AUDIO_CONTROL (DAC_SELECTED | STE_ENABLED | \
+ BYPASS_ON | INSEL_MIC | MICB_20DB)
+
+struct aic23_samplerate_reg_info {
+ u32 sample_rate;
+ u8 control; /* SR3, SR2, SR1, SR0 and BOSR */
+ u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
+};
+
+extern int aic23_write_value(u8 reg, u16 value);
+
+/*
+ * Defines codec specific function pointers that can be used from the
+ * common omap-alsa base driver for all omap codecs. (tsc2101 and aic23)
+ */
+void audio_aic23_write(u8 address, u16 data);
+void define_codec_functions(struct omap_alsa_codec_config *codec_config);
+inline void aic23_configure(void);
+void aic23_set_samplerate(long rate);
+void aic23_clock_setup(void);
+int aic23_clock_on(void);
+int aic23_clock_off(void);
+int aic23_get_default_samplerate(void);
+
+#endif
--- /dev/null
- #include <asm/hardware.h>
- #include <asm/arch/dma.h>
- #include <asm/arch/mcbsp.h>
- #include <asm/arch/omap-alsa.h>
+/*
+ * sound/arm/omap/omap-alsa-dma.c
+ *
+ * Common audio DMA handling for the OMAP processors
+ *
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ *
+ * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
+ *
+ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History:
+ *
+ * 2004-06-07 Sriram Kannan - Created new file from omap_audio_dma_intfc.c.
+ * This file will contain only the DMA interface
+ * and buffer handling of OMAP audio driver.
+ *
+ * 2004-06-22 Sriram Kannan - removed legacy code (auto-init). Self-linking
+ * of DMA logical channel.
+ *
+ * 2004-08-12 Nishanth Menon - Modified to integrate Audio requirements on
+ * 1610, 1710 platforms
+ *
+ * 2004-11-01 Nishanth Menon - 16xx platform code base modified to support
+ * multi channel chaining.
+ *
+ * 2004-12-15 Nishanth Menon - Improved 16xx platform channel logic
+ * introduced - tasklets, queue handling updated
+ *
+ * 2005-07-19 INdT Kernel Team - Alsa port. Creation of new file
+ * omap-alsa-dma.c based in omap-audio-dma-intfc.c
+ * oss file. Support for aic23 codec. Removal of
+ * buffer handling (Alsa does that), modifications
+ * in dma handling and port to alsa structures.
+ *
+ * 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed
+ * by Ajaya Babu
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/pm.h>
+#include <linux/errno.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/sysrq.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/semaphore.h>
+
++#include <mach/hardware.h>
++#include <mach/dma.h>
++#include <mach/mcbsp.h>
++#include <mach/omap-alsa.h>
+
+#include "omap-alsa-dma.h"
+
+#undef DEBUG
+
+/*
+ * Channel Queue Handling macros
+ * tail always points to the current free entry
+ * Head always points to the current entry being used
+ * end is either head or tail
+ */
+
+#define AUDIO_QUEUE_INIT(s) s->dma_q_head = s->dma_q_tail = s->dma_q_count = 0;
+#define AUDIO_QUEUE_FULL(s) (nr_linked_channels == s->dma_q_count)
+#define AUDIO_QUEUE_LAST(s) (1 == s->dma_q_count)
+#define AUDIO_QUEUE_EMPTY(s) (0 == s->dma_q_count)
+#define __AUDIO_INCREMENT_QUEUE(end) ((end) = ((end)+1) % nr_linked_channels)
+#define AUDIO_INCREMENT_HEAD(s) \
+ do { \
+ __AUDIO_INCREMENT_QUEUE(s->dma_q_head); \
+ s->dma_q_count--; \
+ } while (0)
+#define AUDIO_INCREMENT_TAIL(s) \
+ do { \
+ __AUDIO_INCREMENT_QUEUE(s->dma_q_tail); \
+ s->dma_q_count++; \
+ } while (0)
+
+/* DMA buffer fragmentation sizes */
+#define MAX_DMA_SIZE 0x1000000 /* todo: sync with alsa */
+/* #define CUT_DMA_SIZE 0x1000 */
+/* TODO: To be moved to more appropriate location */
+#define DCSR_ERROR 0x3
+#define DCSR_END_BLOCK (1 << 5)
+#define DCSR_SYNC_SET (1 << 6)
+
+#define DCCR_FS (1 << 5)
+#define DCCR_PRIO (1 << 6)
+#define DCCR_AI (1 << 8)
+#define DCCR_REPEAT (1 << 9)
+/* if 0 the channel works in 3.1 compatible mode */
+#define DCCR_N31COMP (1 << 10)
+#define DCCR_EP (1 << 11)
+#define DCCR_SRC_AMODE_BIT 12
+#define DCCR_SRC_AMODE_MASK (0x3<<12)
+#define DCCR_DST_AMODE_BIT 14
+#define DCCR_DST_AMODE_MASK (0x3<<14)
+#define AMODE_CONST 0x0
+#define AMODE_POST_INC 0x1
+#define AMODE_SINGLE_INDEX 0x2
+#define AMODE_DOUBLE_INDEX 0x3
+
+/* Data structures */
+DEFINE_SPINLOCK(dma_list_lock);
+static char nr_linked_channels = 1;
+
+/* Module specific functions */
+
+static void sound_dma_irq_handler(int lch, u16 ch_status, void *data);
+static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr,
+ u_int dma_size);
+static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr,
+ u_int dma_size);
+static int audio_start_dma_chain(struct audio_stream *s);
+
+/*
+ * DMA channel requests
+ */
+static void omap_sound_dma_link_lch(void *data)
+{
+
+ struct audio_stream *s = (struct audio_stream *) data;
+ int *chan = s->lch;
+ int i;
+
+ FN_IN;
+ if (s->linked) {
+ FN_OUT(1);
+ return;
+ }
+ for (i = 0; i < nr_linked_channels; i++) {
+ int cur_chan = chan[i];
+ int nex_chan =
+ ((nr_linked_channels - 1 ==
+ i) ? chan[0] : chan[i + 1]);
+ omap_dma_link_lch(cur_chan, nex_chan);
+ }
+ s->linked = 1;
+ FN_OUT(0);
+}
+
+int omap_request_alsa_sound_dma(int device_id, const char *device_name,
+ void *data, int **channels)
+{
+ int i, err = 0;
+ int *chan = NULL;
+ FN_IN;
+ if (unlikely((NULL == channels) || (NULL == device_name))) {
+ BUG();
+ return -EPERM;
+ }
+ /* Try allocate memory for the num channels */
+ *channels = kmalloc(sizeof(int) * nr_linked_channels, GFP_KERNEL);
+ chan = *channels;
+ if (NULL == chan) {
+ ERR("No Memory for channel allocs!\n");
+ FN_OUT(-ENOMEM);
+ return -ENOMEM;
+ }
+ spin_lock(&dma_list_lock);
+ for (i = 0; i < nr_linked_channels; i++) {
+ err = omap_request_dma(device_id,
+ device_name,
+ sound_dma_irq_handler,
+ data,
+ &chan[i]);
+
+ /* Handle Failure condition here */
+ if (err < 0) {
+ int j;
+
+ for (j = 0; j < i; j++)
+ omap_free_dma(chan[j]);
+
+ spin_unlock(&dma_list_lock);
+ kfree(chan);
+ *channels = NULL;
+ ERR("Error in requesting channel %d=0x%x\n", i,
+ err);
+ FN_OUT(err);
+ return err;
+ }
+ }
+
+ /* Chain the channels together */
+ if (!cpu_is_omap15xx())
+ omap_sound_dma_link_lch(data);
+
+ spin_unlock(&dma_list_lock);
+ FN_OUT(0);
+ return 0;
+}
+EXPORT_SYMBOL(omap_request_alsa_sound_dma);
+
+/*
+ * DMA channel requests Freeing
+ */
+static void omap_sound_dma_unlink_lch(void *data)
+{
+ struct audio_stream *s = (struct audio_stream *)data;
+ int *chan = s->lch;
+ int i;
+
+ FN_IN;
+ if (!s->linked) {
+ FN_OUT(1);
+ return;
+ }
+ for (i = 0; i < nr_linked_channels; i++) {
+ int cur_chan = chan[i];
+ int nex_chan =
+ ((nr_linked_channels - 1 ==
+ i) ? chan[0] : chan[i + 1]);
+ omap_dma_unlink_lch(cur_chan, nex_chan);
+ }
+ s->linked = 0;
+ FN_OUT(0);
+}
+
+int omap_free_alsa_sound_dma(void *data, int **channels)
+{
+ int i;
+ int *chan = NULL;
+
+ FN_IN;
+ if (unlikely(NULL == channels)) {
+ BUG();
+ return -EPERM;
+ }
+ if (unlikely(NULL == *channels)) {
+ BUG();
+ return -EPERM;
+ }
+ chan = (*channels);
+
+ if (!cpu_is_omap15xx())
+ omap_sound_dma_unlink_lch(data);
+ for (i = 0; i < nr_linked_channels; i++) {
+ int cur_chan = chan[i];
+ omap_stop_dma(cur_chan);
+ omap_free_dma(cur_chan);
+ }
+ kfree(*channels);
+ *channels = NULL;
+ FN_OUT(0);
+ return 0;
+}
+EXPORT_SYMBOL(omap_free_alsa_sound_dma);
+
+/*
+ * Stop all the DMA channels of the stream
+ */
+void omap_stop_alsa_sound_dma(struct audio_stream *s)
+{
+ int *chan = s->lch;
+ int i;
+
+ FN_IN;
+ if (unlikely(NULL == chan)) {
+ BUG();
+ return;
+ }
+ for (i = 0; i < nr_linked_channels; i++) {
+ int cur_chan = chan[i];
+ omap_stop_dma(cur_chan);
+ }
+ s->started = 0;
+ FN_OUT(0);
+ return;
+}
+EXPORT_SYMBOL(omap_stop_alsa_sound_dma);
+
+/*
+ * Clear any pending transfers
+ */
+void omap_clear_alsa_sound_dma(struct audio_stream *s)
+{
+ FN_IN;
+ omap_clear_dma(s->lch[s->dma_q_head]);
+ FN_OUT(0);
+ return;
+}
+EXPORT_SYMBOL(omap_clear_alsa_sound_dma);
+
+/*
+ * DMA related functions
+ */
+static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr,
+ u_int dma_size)
+{
+ int dt = 0x1; /* data type 16 */
+ int cen = 32; /* Stereo */
+ int cfn = dma_size / (2 * cen);
+
+ FN_IN;
+ omap_set_dma_dest_params(channel, 0x05, 0x00,
+ (OMAP1510_MCBSP1_BASE + 0x06),
+ 0, 0);
+ omap_set_dma_src_params(channel, 0x00, 0x01, dma_ptr,
+ 0, 0);
+ omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0);
+ FN_OUT(0);
+ return 0;
+}
+
+static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr,
+ u_int dma_size)
+{
+ int dt = 0x1; /* data type 16 */
+ int cen = 32; /* stereo */
+ int cfn = dma_size / (2 * cen);
+
+ FN_IN;
+ omap_set_dma_src_params(channel, 0x05, 0x00,
+ (OMAP1510_MCBSP1_BASE + 0x02),
+ 0, 0);
+ omap_set_dma_dest_params(channel, 0x00, 0x01, dma_ptr, 0, 0);
+ omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0);
+ FN_OUT(0);
+ return 0;
+}
+
+static int audio_start_dma_chain(struct audio_stream *s)
+{
+ int channel = s->lch[s->dma_q_head];
+ FN_IN;
+ if (!s->started) {
+ s->hw_stop(); /* stops McBSP Interface */
+ omap_start_dma(channel);
+ s->started = 1;
+ s->hw_start(); /* start McBSP interface */
+ } else if (cpu_is_omap310())
+ omap_start_dma(channel);
+ /* else the dma itself will progress forward with out our help */
+ FN_OUT(0);
+ return 0;
+}
+
+/*
+ * Start DMA -
+ * Do the initial set of work to initialize all the channels as required.
+ * We shall then initate a transfer
+ */
+int omap_start_alsa_sound_dma(struct audio_stream *s,
+ dma_addr_t dma_ptr,
+ u_int dma_size)
+{
+ int ret = -EPERM;
+
+ FN_IN;
+
+ if (unlikely(dma_size > MAX_DMA_SIZE)) {
+ ERR("DmaSoundDma: Start: overflowed %d-%d\n", dma_size,
+ MAX_DMA_SIZE);
+ return -EOVERFLOW;
+ }
+
+ if (s->stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* playback */
+ ret =
+ audio_set_dma_params_play(s->lch[s->dma_q_tail],
+ dma_ptr, dma_size);
+ } else {
+ ret =
+ audio_set_dma_params_capture(s->lch[s->dma_q_tail],
+ dma_ptr, dma_size);
+ }
+ if (ret != 0) {
+ ret = -3; /* indicate queue full */
+ goto sound_out;
+ }
+ AUDIO_INCREMENT_TAIL(s);
+ ret = audio_start_dma_chain(s);
+ if (ret)
+ ERR("dma start failed");
+
+sound_out:
+ FN_OUT(ret);
+ return ret;
+
+}
+EXPORT_SYMBOL(omap_start_alsa_sound_dma);
+
+/*
+ * ISRs have to be short and smart..
+ * Here we call alsa handling, after some error checking
+ */
+static void sound_dma_irq_handler(int sound_curr_lch, u16 ch_status,
+ void *data)
+{
+ int dma_status = ch_status;
+ struct audio_stream *s = (struct audio_stream *) data;
+ FN_IN;
+
+ /* some register checking */
+ DPRINTK("lch=%d,status=0x%x, dma_status=%d, data=%p\n",
+ sound_curr_lch, ch_status, dma_status, data);
+
+ if (dma_status & (DCSR_ERROR)) {
+ omap_stop_dma(sound_curr_lch);
+ ERR("DCSR_ERROR!\n");
+ FN_OUT(-1);
+ return;
+ }
+
+ if (ch_status & DCSR_END_BLOCK)
+ callback_omap_alsa_sound_dma(s);
+ FN_OUT(0);
+ return;
+}
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("Common DMA handling for Audio driver on OMAP processors");
+MODULE_LICENSE("GPL");
+
--- /dev/null
- #include <asm/arch/omap-alsa.h>
+/*
+ * linux/sound/arm/omap/omap-alsa-dma.h
+ *
+ * Common audio DMA handling for the OMAP processors
+ *
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ *
+ * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
+ *
+ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History:
+ *
+ *
+ * 2004/08/12 Nishanth Menon - Modified to integrate Audio requirements on
+ * 1610, 1710 platforms
+ *
+ * 2005/07/25 INdT Kernel Team - Renamed to omap-alsa-dma.h. Ported to Alsa.
+ */
+
+#ifndef __OMAP_AUDIO_ALSA_DMA_H
+#define __OMAP_AUDIO_ALSA_DMA_H
+
++#include <mach/omap-alsa.h>
+
+/* Global data structures */
+
+typedef void (*dma_callback_t) (int lch, u16 ch_status, void *data);
+
+/* arch specific functions */
+
+void omap_clear_alsa_sound_dma(struct audio_stream *s);
+
+int omap_request_alsa_sound_dma(int device_id, const char *device_name,
+ void *data, int **channels);
+int omap_free_alsa_sound_dma(void *data, int **channels);
+
+int omap_start_alsa_sound_dma(struct audio_stream *s, dma_addr_t dma_ptr,
+ u_int dma_size);
+
+void omap_stop_alsa_sound_dma(struct audio_stream *s);
+
+#endif
--- /dev/null
- #include <asm/arch/dma.h>
- #include <asm/arch/clock.h>
- #include <asm/arch/gpio.h>
- #include <asm/arch/mcbsp.h>
- #include <asm/arch/omap-alsa.h>
+/*
+ * Alsa codec Driver for Siemens SX1 board.
+ * based on omap-alsa-tsc2101.c and cn_test.c example by Evgeniy Polyakov
+ *
+ * Copyright (C) 2006 Vladimir Ananiev (vovan888 at gmail com)
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/soundcard.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/connector.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+
++#include <mach/dma.h>
++#include <mach/clock.h>
++#include <mach/gpio.h>
++#include <mach/mcbsp.h>
++#include <mach/omap-alsa.h>
+
+#include "omap-alsa-sx1.h"
+
+/* Connector implementation */
+static struct cb_id cn_sx1snd_id = { CN_IDX_SX1SND, CN_VAL_SX1SND };
+static char cn_sx1snd_name[] = "cn_sx1snd";
+
+static void cn_sx1snd_callback(void *data)
+{
+ struct cn_msg *msg = (struct cn_msg *)data;
+
+ printk(KERN_INFO
+ "%s: %lu: idx=%x, val=%x, seq=%u, ack=%u, len=%d: %s.\n",
+ __func__, jiffies, msg->id.idx, msg->id.val,
+ msg->seq, msg->ack, msg->len, (char *)msg->data);
+}
+
+/* Send IPC message to sound server */
+int cn_sx1snd_send(unsigned int cmd, unsigned int arg1, unsigned int arg2)
+{
+ struct cn_msg *m;
+ unsigned short data[3];
+ int err;
+
+ m = kzalloc(sizeof(*m) + sizeof(data), gfp_any());
+ if (!m)
+ return -1;
+
+ memcpy(&m->id, &cn_sx1snd_id, sizeof(m->id));
+ m->seq = 1;
+ m->len = sizeof(data);
+
+ data[0] = (unsigned short)cmd;
+ data[1] = (unsigned short)arg1;
+ data[2] = (unsigned short)arg2;
+
+ memcpy(m + 1, data, m->len);
+
+ err = cn_netlink_send(m, CN_IDX_SX1SND, gfp_any());
+ snd_printd("sent= %02X %02X %02X, err=%d\n", cmd, arg1, arg2, err);
+ kfree(m);
+
+ if (err == -ESRCH)
+ return -1; /* there are no listeners on socket */
+ return 0;
+}
+
+/* Hardware capabilities
+ *
+ * DAC USB-mode sampling rates (MCLK = 12 MHz)
+ * The rates and rate_reg_into MUST be in the same order
+ */
+static unsigned int rates[] = {
+ 8000, 11025, 12000,
+ 16000, 22050, 24000,
+ 32000, 44100, 48000,
+};
+
+static struct snd_pcm_hw_constraint_list egold_hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static struct snd_pcm_hardware egold_snd_omap_alsa_playback = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_KNOT),
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8 * 1024,
+ .periods_min = 16,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware egold_snd_omap_alsa_capture = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_KNOT),
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8 * 1024,
+ .periods_min = 16,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static long current_rate = -1; /* current rate in egold format 0..8 */
+/*
+ * ALSA operations according to board file
+ */
+
+/*
+ * Sample rate changing
+ */
+static void egold_set_samplerate(long sample_rate)
+{
+ int egold_rate = 0;
+ int clkgdv = 0;
+ u16 srgr1, srgr2;
+
+ /* Set the sample rate */
+#if 0
+ /* fw15: 5005E490 - divs are different !!! */
+ clkgdv = CODEC_CLOCK / (sample_rate * (DEFAULT_BITPERSAMPLE * 2 - 1));
+#endif
+ switch (sample_rate) {
+ case 8000:
+ clkgdv = 71;
+ egold_rate = FRQ_8000;
+ break;
+ case 11025:
+ clkgdv = 51;
+ egold_rate = FRQ_11025;
+ break;
+ case 12000:
+ clkgdv = 47;
+ egold_rate = FRQ_12000;
+ break;
+ case 16000:
+ clkgdv = 35;
+ egold_rate = FRQ_16000;
+ break;
+ case 22050:
+ clkgdv = 25;
+ egold_rate = FRQ_22050;
+ break;
+ case 24000:
+ clkgdv = 23;
+ egold_rate = FRQ_24000;
+ break;
+ case 32000:
+ clkgdv = 17;
+ egold_rate = FRQ_32000;
+ break;
+ case 44100:
+ clkgdv = 12;
+ egold_rate = FRQ_44100;
+ break;
+ case 48000:
+ clkgdv = 11;
+ egold_rate = FRQ_48000;
+ break;
+ }
+
+ srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+ srgr2 = ((FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1)));
+
+ OMAP_MCBSP_WRITE(OMAP1510_MCBSP1_BASE, SRGR2, srgr2);
+ OMAP_MCBSP_WRITE(OMAP1510_MCBSP1_BASE, SRGR1, srgr1);
+ current_rate = egold_rate;
+ snd_printd("set samplerate=%ld\n", sample_rate);
+
+}
+
+static void egold_configure(void)
+{
+}
+
+/*
+ * Omap MCBSP clock and Power Management configuration
+ *
+ * Here we have some functions that allows clock to be enabled and
+ * disabled only when needed. Besides doing clock configuration
+ * it allows turn on/turn off audio when necessary.
+ */
+
+/*
+ * Do clock framework mclk search
+ */
+static void egold_clock_setup(void)
+{
+ omap_request_gpio(OSC_EN);
+ omap_set_gpio_direction(OSC_EN, 0); /* output */
+ snd_printd("\n");
+}
+
+/*
+ * Do some sanity check, set clock rate, starts it and turn codec audio on
+ */
+static int egold_clock_on(void)
+{
+ omap_set_gpio_dataout(OSC_EN, 1);
+ egold_set_samplerate(44100); /* TODO */
+ cn_sx1snd_send(DAC_SETAUDIODEVICE, SX1_DEVICE_SPEAKER, 0);
+ cn_sx1snd_send(DAC_OPEN_DEFAULT, current_rate , 4);
+ snd_printd("\n");
+ return 0;
+}
+
+/*
+ * Do some sanity check, turn clock off and then turn codec audio off
+ */
+static int egold_clock_off(void)
+{
+ cn_sx1snd_send(DAC_CLOSE, 0 , 0);
+ cn_sx1snd_send(DAC_SETAUDIODEVICE, SX1_DEVICE_PHONE, 0);
+ omap_set_gpio_dataout(OSC_EN, 0);
+ snd_printd("\n");
+ return 0;
+}
+
+static int egold_get_default_samplerate(void)
+{
+ snd_printd("\n");
+ return DEFAULT_SAMPLE_RATE;
+}
+
+static int __init snd_omap_alsa_egold_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct omap_alsa_codec_config *codec_cfg;
+
+ codec_cfg = pdev->dev.platform_data;
+ if (!codec_cfg)
+ return -ENODEV;
+
+ codec_cfg->hw_constraints_rates = &egold_hw_constraints_rates;
+ codec_cfg->snd_omap_alsa_playback = &egold_snd_omap_alsa_playback;
+ codec_cfg->snd_omap_alsa_capture = &egold_snd_omap_alsa_capture;
+ codec_cfg->codec_configure_dev = egold_configure;
+ codec_cfg->codec_set_samplerate = egold_set_samplerate;
+ codec_cfg->codec_clock_setup = egold_clock_setup;
+ codec_cfg->codec_clock_on = egold_clock_on;
+ codec_cfg->codec_clock_off = egold_clock_off;
+ codec_cfg->get_default_samplerate = egold_get_default_samplerate;
+ ret = snd_omap_alsa_post_probe(pdev, codec_cfg);
+
+ snd_printd("\n");
+ return ret;
+}
+
+static struct platform_driver omap_alsa_driver = {
+ .probe = snd_omap_alsa_egold_probe,
+ .remove = snd_omap_alsa_remove,
+ .suspend = snd_omap_alsa_suspend,
+ .resume = snd_omap_alsa_resume,
+ .driver = {
+ .name = "omap_alsa_mcbsp",
+ },
+};
+
+static int __init omap_alsa_egold_init(void)
+{
+ int retval;
+
+ retval = cn_add_callback(&cn_sx1snd_id, cn_sx1snd_name,
+ cn_sx1snd_callback);
+ if (retval)
+ printk(KERN_WARNING "cn_sx1snd failed to register\n");
+ return platform_driver_register(&omap_alsa_driver);
+}
+
+static void __exit omap_alsa_egold_exit(void)
+{
+ cn_del_callback(&cn_sx1snd_id);
+ platform_driver_unregister(&omap_alsa_driver);
+}
+
+module_init(omap_alsa_egold_init);
+module_exit(omap_alsa_egold_exit);
--- /dev/null
- #include <asm/arch/dma.h>
- #include <asm/arch/clock.h>
- #include <asm/arch/mcbsp.h>
+/*
+ * sound/arm/omap/omap-alsa-tsc2101.c
+ *
+ * Alsa codec Driver for TSC2101 chip for OMAP platform boards.
+ * Code obtained from oss omap drivers
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ * Written by Nishanth Menon and Sriram Kannan
+ *
+ * Copyright (C) 2006 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ * Alsa modularization by Daniel Petrini (d.pensator@gmail.com)
+ *
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/soundcard.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/spi/tsc2101.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
+#include <asm/mach-types.h>
- #include <asm/arch/omap-alsa.h>
++#include <mach/dma.h>
++#include <mach/clock.h>
++#include <mach/mcbsp.h>
+#include <asm/hardware/tsc2101.h>
++#include <mach/omap-alsa.h>
+
+#include "omap-alsa-tsc2101.h"
+
+struct mcbsp_dev_info mcbsp_dev;
+
+static struct clk *tsc2101_mclk;
+
+/* #define DUMP_TSC2101_AUDIO_REGISTERS */
+#undef DUMP_TSC2101_AUDIO_REGISTERS
+
+/*
+ * Hardware capabilities
+ */
+
+/*
+ * DAC USB-mode sampling rates (MCLK = 12 MHz)
+ * The rates and rate_reg_into MUST be in the same order
+ */
+static unsigned int rates[] = {
+ 7350, 8000, 8018, 8727,
+ 8820, 9600, 11025, 12000,
+ 14700, 16000, 22050, 24000,
+ 29400, 32000, 44100, 48000,
+};
+
+static struct snd_pcm_hw_constraint_list tsc2101_hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const struct tsc2101_samplerate_reg_info
+ rate_reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
+ /* Div 6 */
+ {7350, 7, 1},
+ {8000, 7, 0},
+ /* Div 5.5 */
+ {8018, 6, 1},
+ {8727, 6, 0},
+ /* Div 5 */
+ {8820, 5, 1},
+ {9600, 5, 0},
+ /* Div 4 */
+ {11025, 4, 1},
+ {12000, 4, 0},
+ /* Div 3 */
+ {14700, 3, 1},
+ {16000, 3, 0},
+ /* Div 2 */
+ {22050, 2, 1},
+ {24000, 2, 0},
+ /* Div 1.5 */
+ {29400, 1, 1},
+ {32000, 1, 0},
+ /* Div 1 */
+ {44100, 0, 1},
+ {48000, 0, 0},
+};
+
+static struct snd_pcm_hardware tsc2101_snd_omap_alsa_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID,
+#ifdef CONFIG_MACH_OMAP_H6300
+ .formats = SNDRV_PCM_FMTBIT_S8,
+#else
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+#endif
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_KNOT,
+ .rate_min = 7350,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8 * 1024,
+ .periods_min = 16,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static struct snd_pcm_hardware tsc2101_snd_omap_alsa_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_KNOT,
+ .rate_min = 7350,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8 * 1024,
+ .periods_min = 16,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+/*
+ * Simplified write for tsc2101 audio registers.
+ */
+inline void tsc2101_audio_write(u8 address, u16 data)
+{
+ tsc2101_write_sync(mcbsp_dev.tsc2101_dev, PAGE2_AUDIO_CODEC_REGISTERS,
+ address, data);
+}
+
+/*
+ * Simplified read for tsc2101 audio registers.
+ */
+inline u16 tsc2101_audio_read(u8 address)
+{
+ return (tsc2101_read_sync(mcbsp_dev.tsc2101_dev,
+ PAGE2_AUDIO_CODEC_REGISTERS, address));
+}
+
+#ifdef DUMP_TSC2101_AUDIO_REGISTERS
+void dump_tsc2101_audio_reg(void)
+{
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_AUDIO_CTRL_1 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_AUDIO_CTRL_1));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_HEADSET_GAIN_CTRL = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_HEADSET_GAIN_CTRL));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_DAC_GAIN_CTRL = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_DAC_GAIN_CTRL));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_MIXER_PGA_CTRL = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_MIXER_PGA_CTRL));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_AUDIO_CTRL_2 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_AUDIO_CTRL_2));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_CODEC_POWER_CTRL = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_CODEC_POWER_CTRL));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_AUDIO_CTRL_3 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_AUDIO_CTRL_3));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_LCH_BASS_BOOST_N0 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N0));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_LCH_BASS_BOOST_N1 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N1));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_LCH_BASS_BOOST_N2 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N2));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_LCH_BASS_BOOST_N3 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N3));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_LCH_BASS_BOOST_N4 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N4));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_LCH_BASS_BOOST_N5 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N5));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_LCH_BASS_BOOST_D1 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_D1));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_LCH_BASS_BOOST_D2 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_D2));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_LCH_BASS_BOOST_D4 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_D4));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_LCH_BASS_BOOST_D5 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_D5));
+
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_RCH_BASS_BOOST_N0 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N0));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_RCH_BASS_BOOST_N1 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N1));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_RCH_BASS_BOOST_N2 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N2));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_RCH_BASS_BOOST_N3 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N3));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_RCH_BASS_BOOST_N4 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N4));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_RCH_BASS_BOOST_N5 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N5));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_RCH_BASS_BOOST_D1 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_D1));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_RCH_BASS_BOOST_D2 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_D2));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_RCH_BASS_BOOST_D4 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_D4));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_RCH_BASS_BOOST_D5 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_D5));
+
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_PLL_PROG_1 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_PLL_PROG_1));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_PLL_PROG_1 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_PLL_PROG_2));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_AUDIO_CTRL_4 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_AUDIO_CTRL_4));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_HANDSET_GAIN_CTRL = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_HANDSET_GAIN_CTRL));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_BUZZER_GAIN_CTRL = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_BUZZER_GAIN_CTRL));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_AUDIO_CTRL_5 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_AUDIO_CTRL_5));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_AUDIO_CTRL_6 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_AUDIO_CTRL_6));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_AUDIO_CTRL_7 = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_AUDIO_CTRL_7));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_GPIO_CTRL = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_GPIO_CTRL));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_AGC_CTRL = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_AGC_CTRL));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_POWERDOWN_STS = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_POWERDOWN_STS));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_MIC_AGC_CONTROL = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_MIC_AGC_CONTROL));
+ dev_dbg(&mcbsp_dev.mcbsp_dev->dev,
+ "TSC2101_CELL_AGC_CONTROL = 0x%04x\n",
+ tsc2101_audio_read(TSC2101_CELL_AGC_CONTROL));
+}
+#endif
+
+/*
+ * ALSA operations according to board file
+ */
+
+/*
+ * Sample rate changing
+ */
+void tsc2101_set_samplerate(long sample_rate)
+{
+ u8 count = 0;
+ u16 data = 0;
+ int clkgdv = 0;
+
+ u16 srgr1, srgr2;
+ /* wait for any frame to complete */
+ udelay(125);
+ ADEBUG();
+
+ sample_rate = sample_rate;
+ /* Search for the right sample rate */
+ while ((rate_reg_info[count].sample_rate != sample_rate) &&
+ (count < NUMBER_SAMPLE_RATES_SUPPORTED)) {
+ count++;
+ }
+ if (count == NUMBER_SAMPLE_RATES_SUPPORTED) {
+ printk(KERN_ERR "Invalid Sample Rate %d requested\n",
+ (int) sample_rate);
+ return;
+ }
+
+ /* Set AC1 */
+ data = tsc2101_audio_read(TSC2101_AUDIO_CTRL_1);
+ /* Clear prev settings */
+ data &= ~(AC1_DACFS(0x07) | AC1_ADCFS(0x07));
+ data |= AC1_DACFS(rate_reg_info[count].divisor) |
+ AC1_ADCFS(rate_reg_info[count].divisor);
+ tsc2101_audio_write(TSC2101_AUDIO_CTRL_1, data);
+
+ /* Set the AC3 */
+ data = tsc2101_audio_read(TSC2101_AUDIO_CTRL_3);
+ /*Clear prev settings */
+ data &= ~(AC3_REFFS | AC3_SLVMS);
+ data |= (rate_reg_info[count].fs_44kHz) ? AC3_REFFS : 0;
+#ifdef TSC_MASTER
+ data |= AC3_SLVMS;
+#endif /* #ifdef TSC_MASTER */
+ tsc2101_audio_write(TSC2101_AUDIO_CTRL_3, data);
+
+ /*
+ * Program the PLLs. This code assumes that the 12 Mhz MCLK is in use.
+ * If MCLK rate is something else, these values must be changed.
+ * See the tsc2101 specification for the details.
+ */
+ if (rate_reg_info[count].fs_44kHz) {
+ /* samplerate = (44.1kHZ / x), where x is int. */
+ tsc2101_audio_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL |
+ /* PVAL 1; I_VAL 7 */
+ PLL1_PVAL(1) | PLL1_I_VAL(7));
+ /* D_VAL 5264 */
+ tsc2101_audio_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x1490));
+ } else {
+ /* samplerate = (48.kHZ / x), where x is int. */
+ tsc2101_audio_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL |
+ /* PVAL 1; I_VAL 8 */
+ PLL1_PVAL(1) | PLL1_I_VAL(8));
+ /* D_VAL 1920 */
+ tsc2101_audio_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x780));
+ }
+
+ /* Set the sample rate */
+#ifndef TSC_MASTER
+ clkgdv = CODEC_CLOCK / (sample_rate * (DEFAULT_BITPERSAMPLE * 2 - 1));
+ if (clkgdv)
+ srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+ else
+ return (1);
+
+ /* Stereo Mode */
+ srgr2 = (CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1));
+#else
+ srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+ srgr2 = ((GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1)));
+
+#endif /* end of #ifdef TSC_MASTER */
+ OMAP_MCBSP_WRITE(OMAP1610_MCBSP1_BASE, SRGR2, srgr2);
+ OMAP_MCBSP_WRITE(OMAP1610_MCBSP1_BASE, SRGR1, srgr1);
+}
+
+void tsc2101_configure(void)
+{
+}
+
+/*
+ * Omap MCBSP clock and Power Management configuration
+ *
+ * Here we have some functions that allows clock to be enabled and
+ * disabled only when needed. Besides doing clock configuration
+ * it allows turn on/turn off audio when necessary.
+ */
+
+/*
+ * Do clock framework mclk search
+ */
+void tsc2101_clock_setup(void)
+{
+ tsc2101_mclk = clk_get(0, "mclk");
+}
+
+/*
+ * Do some sanity check, set clock rate, starts it and turn codec audio on
+ */
+int tsc2101_clock_on(void)
+{
+ int curUseCount;
+ uint curRate;
+ int err;
+
+ curUseCount = clk_get_usecount(tsc2101_mclk);
+ DPRINTK("clock use count = %d\n", curUseCount);
+ if (curUseCount > 0) {
+ /* MCLK is already in use */
+ printk(KERN_WARNING
+ "MCLK already in use at %d Hz. We change it to %d Hz\n",
+ (uint) clk_get_rate(tsc2101_mclk),
+ CODEC_CLOCK);
+ }
+ curRate = (uint)clk_get_rate(tsc2101_mclk);
+ if (curRate != CODEC_CLOCK) {
+ err = clk_set_rate(tsc2101_mclk, CODEC_CLOCK);
+ if (err) {
+ printk(KERN_WARNING "Cannot set MCLK clock rate for "
+ "TSC2101 CODEC, error code = %d\n", err);
+ return -ECANCELED;
+ }
+ }
+ err = clk_enable(tsc2101_mclk);
+ curRate = (uint)clk_get_rate(tsc2101_mclk);
+ curUseCount = clk_get_usecount(tsc2101_mclk);
+ DPRINTK("MCLK = %d [%d], usecount = %d, clk_enable retval = %d\n",
+ curRate,
+ CODEC_CLOCK,
+ curUseCount,
+ err);
+
+ /* Now turn the audio on */
+ tsc2101_write_sync(mcbsp_dev.tsc2101_dev, PAGE2_AUDIO_CODEC_REGISTERS,
+ TSC2101_CODEC_POWER_CTRL,
+ 0x0000);
+ return 0;
+}
+
+/*
+ * Do some sanity check, turn clock off and then turn codec audio off
+ */
+int tsc2101_clock_off(void)
+{
+ int curUseCount;
+ int curRate;
+
+ curUseCount = clk_get_usecount(tsc2101_mclk);
+ DPRINTK("clock use count = %d\n", curUseCount);
+ if (curUseCount > 0) {
+ curRate = clk_get_rate(tsc2101_mclk);
+ DPRINTK("clock rate = %d\n", curRate);
+ if (curRate != CODEC_CLOCK) {
+ printk(KERN_WARNING
+ "MCLK for audio should be %d Hz. But is %d Hz\n",
+ (uint) clk_get_rate(tsc2101_mclk),
+ CODEC_CLOCK);
+ }
+ clk_disable(tsc2101_mclk);
+ DPRINTK("clock disabled\n");
+ }
+ tsc2101_audio_write(TSC2101_CODEC_POWER_CTRL,
+ ~(CPC_SP1PWDN | CPC_SP2PWDN | CPC_BASSBC));
+ DPRINTK("audio codec off\n");
+ return 0;
+}
+
+int tsc2101_get_default_samplerate(void)
+{
+ return DEFAULT_SAMPLE_RATE;
+}
+
+static int __devinit snd_omap_alsa_tsc2101_probe(struct platform_device *pdev)
+{
+ struct spi_device *tsc2101;
+ int ret;
+ struct omap_alsa_codec_config *codec_cfg;
+
+ tsc2101 = dev_get_drvdata(&pdev->dev);
+ if (tsc2101 == NULL) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -ENODEV;
+ }
+ if (strncmp(tsc2101->modalias, "tsc2101", 8) != 0) {
+ dev_err(&pdev->dev, "tsc2101 not found\n");
+ return -EINVAL;
+ }
+ mcbsp_dev.mcbsp_dev = pdev;
+ mcbsp_dev.tsc2101_dev = tsc2101;
+
+ codec_cfg = pdev->dev.platform_data;
+ if (codec_cfg != NULL) {
+ codec_cfg->hw_constraints_rates =
+ &tsc2101_hw_constraints_rates;
+ codec_cfg->snd_omap_alsa_playback =
+ &tsc2101_snd_omap_alsa_playback;
+ codec_cfg->snd_omap_alsa_capture =
+ &tsc2101_snd_omap_alsa_capture;
+ codec_cfg->codec_configure_dev = tsc2101_configure;
+ codec_cfg->codec_set_samplerate = tsc2101_set_samplerate;
+ codec_cfg->codec_clock_setup = tsc2101_clock_setup;
+ codec_cfg->codec_clock_on = tsc2101_clock_on;
+ codec_cfg->codec_clock_off = tsc2101_clock_off;
+ codec_cfg->get_default_samplerate =
+ tsc2101_get_default_samplerate;
+ ret = snd_omap_alsa_post_probe(pdev, codec_cfg);
+ } else
+ ret = -ENODEV;
+ return ret;
+}
+
+static struct platform_driver omap_alsa_driver = {
+ .probe = snd_omap_alsa_tsc2101_probe,
+ .remove = snd_omap_alsa_remove,
+ .suspend = snd_omap_alsa_suspend,
+ .resume = snd_omap_alsa_resume,
+ .driver = {
+ .name = "omap_alsa_mcbsp",
+ },
+};
+
+static int __init omap_alsa_tsc2101_init(void)
+{
+ ADEBUG();
+#ifdef DUMP_TSC2101_AUDIO_REGISTERS
+ printk(KERN_INFO "omap_alsa_tsc2101_init()\n");
+ dump_tsc2101_audio_reg();
+#endif
+ return platform_driver_register(&omap_alsa_driver);
+}
+
+static void __exit omap_alsa_tsc2101_exit(void)
+{
+ ADEBUG();
+#ifdef DUMP_TSC2101_AUDIO_REGISTERS
+ printk(KERN_INFO "omap_alsa_tsc2101_exit()\n");
+ dump_tsc2101_audio_reg();
+#endif
+ platform_driver_unregister(&omap_alsa_driver);
+}
+
+module_init(omap_alsa_tsc2101_init);
+module_exit(omap_alsa_tsc2101_exit);
--- /dev/null
- #include <asm/arch/omap-alsa.h>
+/*
+ * sound/arm/omap/omap-alsa-tsc2102-mixer.c
+ *
+ * Alsa mixer driver for TSC2102 chip for OMAP platforms.
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
+ * Code based on the TSC2101 ALSA driver.
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/spi/tsc2102.h>
+
++#include <mach/omap-alsa.h>
+
+#include <sound/initval.h>
+#include <sound/control.h>
+
+#include "omap-alsa-tsc2102.h"
+#include "omap-alsa-dma.h"
+
+static int vol[2], mute[2], filter[2];
+
+/*
+ * Converts the Alsa mixer volume (0 - 100) to actual Digital
+ * Gain Control (DGC) value that can be written or read from the
+ * TSC2102 registers.
+ *
+ * Note that the number "OUTPUT_VOLUME_MAX" is smaller than
+ * OUTPUT_VOLUME_MIN because DGC works as a volume decreaser. (The
+ * higher the value sent to DAC, the more the volume of controlled
+ * channel is decreased)
+ */
+static void set_dac_gain_stereo(int left_ch, int right_ch)
+{
+ int lch, rch;
+
+ if (left_ch > 100)
+ vol[0] = 100;
+ else if (left_ch < 0)
+ vol[0] = 0;
+ else
+ vol[0] = left_ch;
+ lch = OUTPUT_VOLUME_MIN - vol[0] *
+ (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX) / 100;
+
+ if (right_ch > 100)
+ vol[1] = 100;
+ else if (right_ch < 0)
+ vol[1] = 0;
+ else
+ vol[1] = right_ch;
+ rch = OUTPUT_VOLUME_MIN - vol[1] *
+ (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX) / 100;
+
+ tsc2102_set_volume(lch, rch);
+}
+
+void init_playback_targets(void)
+{
+ set_dac_gain_stereo(DEFAULT_OUTPUT_VOLUME, DEFAULT_OUTPUT_VOLUME);
+
+ /* Unmute */
+ tsc2102_set_mute(0, 0);
+
+ mute[0] = 0;
+ mute[1] = 0;
+ filter[0] = 0;
+ filter[1] = 0;
+}
+
+/*
+ * Initializes TSC 2102 and playback target.
+ */
+void snd_omap_init_mixer(void)
+{
+ FN_IN;
+
+ init_playback_targets();
+
+ FN_OUT(0);
+}
+
+static int __pcm_playback_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ return 0;
+}
+
+static int __pcm_playback_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = vol[0]; /* L */
+ ucontrol->value.integer.value[1] = vol[1]; /* R */
+
+ return 0;
+}
+
+static int __pcm_playback_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ set_dac_gain_stereo(
+ ucontrol->value.integer.value[0], /* L */
+ ucontrol->value.integer.value[1]); /* R */
+ return 1;
+}
+
+static int __pcm_playback_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int __pcm_playback_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = !mute[0]; /* L */
+ ucontrol->value.integer.value[1] = !mute[1]; /* R */
+
+ return 0;
+}
+
+static int __pcm_playback_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ mute[0] = (ucontrol->value.integer.value[0] == 0); /* L */
+ mute[1] = (ucontrol->value.integer.value[1] == 0); /* R */
+
+ tsc2102_set_mute(mute[0], mute[1]);
+ return 1;
+}
+
+static int __pcm_playback_deemphasis_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int __pcm_playback_deemphasis_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = filter[0];
+ return 0;
+}
+
+static int __pcm_playback_deemphasis_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ filter[0] = (ucontrol->value.integer.value[0] > 0);
+
+ tsc2102_set_deemphasis(filter[0]);
+ return 1;
+}
+
+static int __pcm_playback_bassboost_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int __pcm_playback_bassboost_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = filter[1];
+ return 0;
+}
+
+static int __pcm_playback_bassboost_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ filter[1] = (ucontrol->value.integer.value[0] > 0);
+
+ tsc2102_set_bassboost(filter[1]);
+ return 1;
+}
+
+static struct snd_kcontrol_new tsc2102_controls[] __devinitdata = {
+ {
+ .name = "Master Playback Volume",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = __pcm_playback_volume_info,
+ .get = __pcm_playback_volume_get,
+ .put = __pcm_playback_volume_put,
+ },
+ {
+ .name = "Master Playback Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = __pcm_playback_switch_info,
+ .get = __pcm_playback_switch_get,
+ .put = __pcm_playback_switch_put,
+ },
+ {
+ .name = "De-emphasis Filter Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = __pcm_playback_deemphasis_info,
+ .get = __pcm_playback_deemphasis_get,
+ .put = __pcm_playback_deemphasis_put,
+ },
+ {
+ .name = "Bass-boost Filter Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .index = 0,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = __pcm_playback_bassboost_info,
+ .get = __pcm_playback_bassboost_get,
+ .put = __pcm_playback_bassboost_put,
+ },
+};
+
+#ifdef CONFIG_PM
+void snd_omap_suspend_mixer(void)
+{
+ /* Nothing to do */
+}
+
+void snd_omap_resume_mixer(void)
+{
+ /* The chip was reset, restore the last used values */
+ set_dac_gain_stereo(vol[0], vol[1]);
+
+ tsc2102_set_mute(mute[0], mute[1]);
+ tsc2102_set_deemphasis(filter[0]);
+ tsc2102_set_bassboost(filter[1]);
+}
+#endif
+
+int snd_omap_mixer(struct snd_card_omap_codec *tsc2102)
+{
+ int i, err;
+
+ if (!tsc2102)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(tsc2102_controls); i++) {
+ err = snd_ctl_add(tsc2102->card,
+ snd_ctl_new1(&tsc2102_controls[i],
+ tsc2102->card));
+
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
--- /dev/null
- #include <asm/arch/dma.h>
- #include <asm/arch/clock.h>
- #include <asm/arch/omap-alsa.h>
+/*
+ * sound/arm/omap/omap-alsa-tsc2102.c
+ *
+ * Alsa codec driver for TSC2102 chip for OMAP platforms.
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
+ * Code based on the TSC2101 ALSA driver.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/soundcard.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/spi/tsc2102.h>
+
++#include <mach/dma.h>
++#include <mach/clock.h>
++#include <mach/omap-alsa.h>
+
+#include "omap-alsa-tsc2102.h"
+
+static struct clk *tsc2102_bclk;
+
+/*
+ * Hardware capabilities
+ */
+
+/* DAC sampling rates (BCLK = 12 MHz) */
+static unsigned int rates[] = {
+ 7350, 8000, 8820, 9600, 11025, 12000, 14700,
+ 16000, 22050, 24000, 29400, 32000, 44100, 48000,
+};
+
+static struct snd_pcm_hw_constraint_list tsc2102_hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static struct snd_pcm_hardware tsc2102_snd_omap_alsa_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_KNOT,
+ .rate_min = 7350,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8 * 1024,
+ .periods_min = 16,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+#ifdef DUMP_TSC2102_AUDIO_REGISTERS
+static void dump_tsc2102_audio_regs(void)
+{
+ printk(KERN_INFO "TSC2102_AUDIO1_CTRL = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_AUDIO1_CTRL));
+ printk(KERN_INFO "TSC2102_DAC_GAIN_CTRL = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_DAC_GAIN_CTRL));
+ printk(KERN_INFO "TSC2102_AUDIO2_CTRL = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_AUDIO2_CTRL));
+ printk(KERN_INFO "TSC2102_DAC_POWER_CTRL = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_DAC_POWER_CTRL));
+ printk(KERN_INFO "TSC2102_AUDIO3_CTRL = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_AUDIO_CTRL_3));
+ printk(KERN_INFO "TSC2102_LCH_BASS_BOOST_N0 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_N0));
+ printk(KERN_INFO "TSC2102_LCH_BASS_BOOST_N1 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_N1));
+ printk(KERN_INFO "TSC2102_LCH_BASS_BOOST_N2 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_N2));
+ printk(KERN_INFO "TSC2102_LCH_BASS_BOOST_N3 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_N3));
+ printk(KERN_INFO "TSC2102_LCH_BASS_BOOST_N4 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_N4));
+ printk(KERN_INFO "TSC2102_LCH_BASS_BOOST_N5 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_N5));
+ printk(KERN_INFO "TSC2102_LCH_BASS_BOOST_D1 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_D1));
+ printk(KERN_INFO "TSC2102_LCH_BASS_BOOST_D2 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_D2));
+ printk(KERN_INFO "TSC2102_LCH_BASS_BOOST_D4 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_D4));
+ printk(KERN_INFO "TSC2102_LCH_BASS_BOOST_D5 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_LCH_BASS_BOOST_D5));
+ printk(KERN_INFO "TSC2102_RCH_BASS_BOOST_N0 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_N0));
+ printk(KERN_INFO "TSC2102_RCH_BASS_BOOST_N1 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_N1));
+ printk(KERN_INFO "TSC2102_RCH_BASS_BOOST_N2 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_N2));
+ printk(KERN_INFO "TSC2102_RCH_BASS_BOOST_N3 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_N3));
+ printk(KERN_INFO "TSC2102_RCH_BASS_BOOST_N4 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_N4));
+ printk(KERN_INFO "TSC2102_RCH_BASS_BOOST_N5 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_N5));
+ printk(KERN_INFO "TSC2102_RCH_BASS_BOOST_D1 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_D1));
+ printk(KERN_INFO "TSC2102_RCH_BASS_BOOST_D2 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_D2));
+ printk(KERN_INFO "TSC2102_RCH_BASS_BOOST_D4 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_D4));
+ printk(KERN_INFO "TSC2102_RCH_BASS_BOOST_D5 = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_RCH_BASS_BOOST_D5));
+ printk(KERN_INFO "TSC2102_PLL1_CTRL = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_PLL1_CTRL));
+ printk(KERN_INFO "TSC2102_PLL2_CTRL = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_PLL2_CTRL));
+ printk(KERN_INFO "TSC2102_AUDIO4_CTRL = 0x%04x\n",
+ tsc2102_read_sync(TSC2102_AUDIO4_CTRL));
+}
+#endif
+
+/*
+ * ALSA operations according to board file
+ */
+
+static long current_rate;
+
+/*
+ * Sample rate changing
+ */
+static void tsc2102_set_samplerate(long sample_rate)
+{
+ int clkgdv = 0;
+ u16 srgr1, srgr2;
+
+ if (sample_rate == current_rate)
+ return;
+ current_rate = 0;
+
+ if (tsc2102_set_rate(sample_rate))
+ return;
+
+ /* Set the sample rate */
+#ifndef TSC_MASTER
+ clkgdv = CODEC_CLOCK / (sample_rate * (DEFAULT_BITPERSAMPLE * 2 - 1));
+ if (clkgdv)
+ srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+ else
+ return;
+
+ /* Stereo Mode */
+ srgr2 = CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1);
+#else
+ srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv);
+ srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1);
+#endif
+ OMAP_MCBSP_WRITE(OMAP1510_MCBSP1_BASE, SRGR2, srgr2);
+ OMAP_MCBSP_WRITE(OMAP1510_MCBSP1_BASE, SRGR1, srgr1);
+ current_rate = sample_rate;
+}
+
+static void tsc2102_configure(void)
+{
+ tsc2102_dac_power(1);
+
+#ifdef TSC_MASTER
+ tsc2102_set_i2s_master(1);
+#else
+ tsc2102_set_i2s_master(0);
+#endif
+}
+
+/*
+ * Omap McBSP clock and Power Management configuration
+ *
+ * Here we have some functions that allow clock to be enabled and
+ * disabled only when needed. Besides doing clock configuration
+ * they allow turn audio on and off when necessary.
+ */
+
+/*
+ * Do clock framework bclk search
+ */
+static void tsc2102_clock_setup(void)
+{
+ tsc2102_bclk = clk_get(0, "bclk");
+}
+
+/*
+ * Do some sanity checks, set clock rate, start it.
+ */
+static int tsc2102_clock_on(void)
+{
+ int err;
+
+ if (clk_get_usecount(tsc2102_bclk) > 0 &&
+ clk_get_rate(tsc2102_bclk) != CODEC_CLOCK) {
+ /* BCLK is already in use */
+ printk(KERN_WARNING
+ "BCLK already in use at %d Hz. We change it to %d Hz\n",
+ (uint) clk_get_rate(tsc2102_bclk), CODEC_CLOCK);
+
+ err = clk_set_rate(tsc2102_bclk, CODEC_CLOCK);
+ if (err)
+ printk(KERN_WARNING "Cannot set BCLK clock rate "
+ "for TSC2102 codec, error code = %d\n", err);
+ }
+
+ clk_enable(tsc2102_bclk);
+ return 0;
+}
+
+/*
+ * Turn off the audio codec and then stop the clock.
+ */
+static int tsc2102_clock_off(void)
+{
+ DPRINTK("clock use count = %d\n", clk_get_usecount(tsc2102_bclk));
+
+ clk_disable(tsc2102_bclk);
+ return 0;
+}
+
+static int tsc2102_get_default_samplerate(void)
+{
+ return DEFAULT_SAMPLE_RATE;
+}
+
+static int snd_omap_alsa_tsc2102_suspend(
+ struct platform_device *pdev, pm_message_t state)
+{
+ tsc2102_dac_power(0);
+ current_rate = 0;
+
+ return snd_omap_alsa_suspend(pdev, state);
+}
+
+static int snd_omap_alsa_tsc2102_resume(struct platform_device *pdev)
+{
+ tsc2102_dac_power(1);
+
+#ifdef TSC_MASTER
+ tsc2102_set_i2s_master(1);
+#else
+ tsc2102_set_i2s_master(0);
+#endif
+
+ return snd_omap_alsa_resume(pdev);
+}
+
+static int __init snd_omap_alsa_tsc2102_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct omap_alsa_codec_config *codec_cfg = pdev->dev.platform_data;
+
+ if (codec_cfg) {
+ codec_cfg->hw_constraints_rates =
+ &tsc2102_hw_constraints_rates;
+ codec_cfg->snd_omap_alsa_playback =
+ &tsc2102_snd_omap_alsa_playback;
+ codec_cfg->codec_configure_dev = tsc2102_configure;
+ codec_cfg->codec_set_samplerate = tsc2102_set_samplerate;
+ codec_cfg->codec_clock_setup = tsc2102_clock_setup;
+ codec_cfg->codec_clock_on = tsc2102_clock_on;
+ codec_cfg->codec_clock_off = tsc2102_clock_off;
+ codec_cfg->get_default_samplerate =
+ tsc2102_get_default_samplerate;
+ ret = snd_omap_alsa_post_probe(pdev, codec_cfg);
+ } else
+ ret = -ENODEV;
+
+ return ret;
+}
+
+static int snd_omap_alsa_tsc2102_remove(struct platform_device *pdev)
+{
+ tsc2102_dac_power(0);
+
+ return snd_omap_alsa_remove(pdev);
+}
+
+static struct platform_driver omap_alsa_driver = {
+ .probe = snd_omap_alsa_tsc2102_probe,
+ .remove = snd_omap_alsa_tsc2102_remove,
+ .suspend = snd_omap_alsa_tsc2102_suspend,
+ .resume = snd_omap_alsa_tsc2102_resume,
+ .driver = {
+ .name = "tsc2102-alsa",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap_alsa_tsc2102_init(void)
+{
+ int err;
+
+ ADEBUG();
+ err = platform_driver_register(&omap_alsa_driver);
+
+ return err;
+}
+
+static void __exit omap_alsa_tsc2102_exit(void)
+{
+ ADEBUG();
+ platform_driver_unregister(&omap_alsa_driver);
+}
+
+module_init(omap_alsa_tsc2102_init);
+module_exit(omap_alsa_tsc2102_exit);
--- /dev/null
- #include <asm/arch/omap-alsa.h>
+/*
+ * sound/arm/omap-alsa.c
+ *
+ * Alsa Driver for OMAP
+ *
+ * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
+ * Written by Daniel Petrini, David Cohen, Anderson Briglia
+ * {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
+ *
+ * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
+ *
+ * Based on sa11xx-uda1341.c,
+ * Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * History:
+ *
+ * 2005-07-29 INdT Kernel Team - Alsa driver for omap osk. Creation of new
+ * file omap-aic23.c
+ *
+ * 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed
+ * by Ajaya Babu
+ *
+ */
+
+#include <linux/platform_device.h>
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+#include <sound/core.h>
+#include <sound/pcm.h>
+
++#include <mach/omap-alsa.h>
+#include "omap-alsa-dma.h"
+
+MODULE_AUTHOR("Mika Laitio");
+MODULE_AUTHOR("Daniel Petrini");
+MODULE_AUTHOR("David Cohen");
+MODULE_AUTHOR("Anderson Briglia");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("OMAP driver for ALSA");
+MODULE_ALIAS("omap_alsa_mcbsp.1");
+
+static char *id;
+static struct snd_card_omap_codec *alsa_codec;
+static struct omap_alsa_codec_config *alsa_codec_config;
+
+/* FIXME: Please change to use omap asoc framework instead, this can be racy */
+static dma_addr_t dma_start_pos;
+
+/*
+ * HW interface start and stop helper functions
+ */
+static int audio_ifc_start(void)
+{
+ omap_mcbsp_start(AUDIO_MCBSP);
+ return 0;
+}
+
+static int audio_ifc_stop(void)
+{
+ omap_mcbsp_stop(AUDIO_MCBSP);
+ return 0;
+}
+
+static void omap_alsa_audio_init(struct snd_card_omap_codec *omap_alsa)
+{
+ /* Setup DMA stuff */
+ omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].id = "Alsa omap out";
+ omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id =
+ SNDRV_PCM_STREAM_PLAYBACK;
+ omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev =
+ OMAP_DMA_MCBSP1_TX;
+ omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].hw_start =
+ audio_ifc_start;
+ omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].hw_stop =
+ audio_ifc_stop;
+
+ omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].id = "Alsa omap in";
+ omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].stream_id =
+ SNDRV_PCM_STREAM_CAPTURE;
+ omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev =
+ OMAP_DMA_MCBSP1_RX;
+ omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].hw_start =
+ audio_ifc_start;
+ omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].hw_stop =
+ audio_ifc_stop;
+}
+
+/*
+ * DMA functions
+ * Depends on omap-alsa-dma.c functions and (omap) dma.c
+ */
+static int audio_dma_request(struct audio_stream *s,
+ void (*callback) (void *))
+{
+ int err;
+ ADEBUG();
+
+ err = omap_request_alsa_sound_dma(s->dma_dev, s->id, s, &s->lch);
+ if (err < 0)
+ printk(KERN_ERR "Unable to grab audio dma 0x%x\n", s->dma_dev);
+ return err;
+}
+
+static int audio_dma_free(struct audio_stream *s)
+{
+ int err = 0;
+ ADEBUG();
+
+ err = omap_free_alsa_sound_dma(s, &s->lch);
+ if (err < 0)
+ printk(KERN_ERR "Unable to free audio dma channels!\n");
+ return err;
+}
+
+/*
+ * This function should calculate the current position of the dma in the
+ * buffer. It will help alsa middle layer to continue update the buffer.
+ * Its correctness is crucial for good functioning.
+ */
+static u_int audio_get_dma_pos(struct audio_stream *s)
+{
+ struct snd_pcm_substream *substream = s->stream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int offset;
+ unsigned long flags;
+ dma_addr_t count;
+ ADEBUG();
+
+ /* this must be called w/ interrupts locked as requested in dma.c */
+ spin_lock_irqsave(&s->dma_lock, flags);
+
+ /* For the current period let's see where we are */
+ count = omap_get_dma_src_pos(s->lch[s->dma_q_head]) - dma_start_pos;
+
+ spin_unlock_irqrestore(&s->dma_lock, flags);
+
+ /* Now, the position related to the end of that period */
+ offset = bytes_to_frames(runtime, s->offset) -
+ bytes_to_frames(runtime, count);
+
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+
+ return offset;
+}
+
+/*
+ * this stops the dma and clears the dma ptrs
+ */
+static void audio_stop_dma(struct audio_stream *s)
+{
+ unsigned long flags;
+ ADEBUG();
+
+ spin_lock_irqsave(&s->dma_lock, flags);
+ s->active = 0;
+ s->period = 0;
+ s->periods = 0;
+
+ /* this stops the dma channel and clears the buffer ptrs */
+ omap_stop_alsa_sound_dma(s);
+
+ omap_clear_alsa_sound_dma(s);
+
+ spin_unlock_irqrestore(&s->dma_lock, flags);
+}
+
+/*
+ * Main dma routine, requests dma according where you are in main alsa buffer
+ */
+static void audio_process_dma(struct audio_stream *s)
+{
+ struct snd_pcm_substream *substream = s->stream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size;
+ unsigned int offset;
+ int ret;
+
+ ADEBUG();
+ runtime = substream->runtime;
+ if (s->active) {
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * s->period;
+ snd_assert(dma_size <= DMA_BUF_SIZE, return);
+ /*
+ * On omap1510 based devices, we need to call the stop_dma
+ * before calling the start_dma or we will not receive the
+ * irq from DMA after the first transfered/played buffer.
+ * (invocation of callback_omap_alsa_sound_dma() method).
+ */
+ if (cpu_is_omap1510())
+ omap_stop_alsa_sound_dma(s);
+
+ dma_start_pos = (dma_addr_t)runtime->dma_area + offset;
+ ret = omap_start_alsa_sound_dma(s, dma_start_pos, dma_size);
+ if (ret) {
+ printk(KERN_ERR "audio_process_dma: cannot"
+ " queue DMA buffer (%i)\n", ret);
+ return;
+ }
+
+ s->period++;
+ s->period %= runtime->periods;
+ s->periods++;
+ s->offset = offset;
+ }
+}
+
+/*
+ * This is called when dma IRQ occurs at the end of each transmited block
+ */
+void callback_omap_alsa_sound_dma(void *data)
+{
+ struct audio_stream *s = data;
+
+ ADEBUG();
+ /*
+ * If we are getting a callback for an active stream then we inform
+ * the PCM middle layer we've finished a period
+ */
+ if (s->active)
+ snd_pcm_period_elapsed(s->stream);
+
+ spin_lock(&s->dma_lock);
+ if (s->periods > 0)
+ s->periods--;
+
+ audio_process_dma(s);
+ spin_unlock(&s->dma_lock);
+}
+
+/*
+ * Alsa section
+ * PCM settings and callbacks
+ */
+static int snd_omap_alsa_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_card_omap_codec *chip =
+ snd_pcm_substream_chip(substream);
+ int stream_id = substream->pstr->stream;
+ struct audio_stream *s = &chip->s[stream_id];
+ int err = 0;
+
+ ADEBUG();
+ /* note local interrupts are already disabled in the midlevel code */
+ spin_lock(&s->dma_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ /* requested stream startup */
+ s->active = 1;
+ audio_process_dma(s);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* requested stream shutdown */
+ audio_stop_dma(s);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ spin_unlock(&s->dma_lock);
+
+ return err;
+}
+
+static int snd_omap_alsa_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_card_omap_codec *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audio_stream *s = &chip->s[substream->pstr->stream];
+
+ ADEBUG();
+ /* set requested samplerate */
+ alsa_codec_config->codec_set_samplerate(runtime->rate);
+ chip->samplerate = runtime->rate;
+
+ s->period = 0;
+ s->periods = 0;
+
+ return 0;
+}
+
+static snd_pcm_uframes_t
+snd_omap_alsa_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_card_omap_codec *chip = snd_pcm_substream_chip(substream);
+
+ ADEBUG();
+ return audio_get_dma_pos(&chip->s[substream->pstr->stream]);
+}
+
+static int snd_card_omap_alsa_open(struct snd_pcm_substream *substream)
+{
+ struct snd_card_omap_codec *chip =
+ snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int stream_id = substream->pstr->stream;
+ int err;
+
+ ADEBUG();
+ chip->s[stream_id].stream = substream;
+ alsa_codec_config->codec_clock_on();
+ if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = *(alsa_codec_config->snd_omap_alsa_playback);
+ else
+ runtime->hw = *(alsa_codec_config->snd_omap_alsa_capture);
+
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ alsa_codec_config->hw_constraints_rates);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int snd_card_omap_alsa_close(struct snd_pcm_substream *substream)
+{
+ struct snd_card_omap_codec *chip = snd_pcm_substream_chip(substream);
+
+ ADEBUG();
+ alsa_codec_config->codec_clock_off();
+ chip->s[substream->pstr->stream].stream = NULL;
+
+ return 0;
+}
+
+/* HW params & free */
+static int snd_omap_alsa_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int snd_omap_alsa_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/* pcm operations */
+static struct snd_pcm_ops snd_card_omap_alsa_playback_ops = {
+ .open = snd_card_omap_alsa_open,
+ .close = snd_card_omap_alsa_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_omap_alsa_hw_params,
+ .hw_free = snd_omap_alsa_hw_free,
+ .prepare = snd_omap_alsa_prepare,
+ .trigger = snd_omap_alsa_trigger,
+ .pointer = snd_omap_alsa_pointer,
+};
+
+static struct snd_pcm_ops snd_card_omap_alsa_capture_ops = {
+ .open = snd_card_omap_alsa_open,
+ .close = snd_card_omap_alsa_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_omap_alsa_hw_params,
+ .hw_free = snd_omap_alsa_hw_free,
+ .prepare = snd_omap_alsa_prepare,
+ .trigger = snd_omap_alsa_trigger,
+ .pointer = snd_omap_alsa_pointer,
+};
+
+/*
+ * Alsa init and exit section
+ * Inits pcm alsa structures, allocate the alsa buffer, suspend, resume
+ */
+static int __init snd_card_omap_alsa_pcm(struct snd_card_omap_codec *omap_alsa,
+ int device)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ ADEBUG();
+ err = snd_pcm_new(omap_alsa->card, "OMAP PCM", device, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ /* sets up initial buffer with continuous allocation */
+ snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data
+ (GFP_KERNEL),
+ 128 * 1024, 128 * 1024);
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_card_omap_alsa_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_card_omap_alsa_capture_ops);
+ pcm->private_data = omap_alsa;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "omap alsa pcm");
+
+ omap_alsa_audio_init(omap_alsa);
+
+ /* setup DMA controller */
+ audio_dma_request(&omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK],
+ callback_omap_alsa_sound_dma);
+ audio_dma_request(&omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE],
+ callback_omap_alsa_sound_dma);
+
+ omap_alsa->pcm = pcm;
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * Driver suspend/resume - calls alsa functions. Some hints from aaci.c
+ */
+int snd_omap_alsa_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_card_omap_codec *chip;
+ struct snd_card *card = platform_get_drvdata(pdev);
+
+ if (card->power_state != SNDRV_CTL_POWER_D3hot) {
+ chip = card->private_data;
+ if (chip->card->power_state != SNDRV_CTL_POWER_D3hot) {
+ snd_power_change_state(chip->card,
+ SNDRV_CTL_POWER_D3hot);
+ snd_pcm_suspend_all(chip->pcm);
+ /* Mutes and turn clock off */
+ alsa_codec_config->codec_clock_off();
+ snd_omap_suspend_mixer();
+ }
+ }
+ return 0;
+}
+
+int snd_omap_alsa_resume(struct platform_device *pdev)
+{
+ struct snd_card_omap_codec *chip;
+ struct snd_card *card = platform_get_drvdata(pdev);
+
+ if (card->power_state != SNDRV_CTL_POWER_D0) {
+ chip = card->private_data;
+ if (chip->card->power_state != SNDRV_CTL_POWER_D0) {
+ snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+ alsa_codec_config->codec_clock_on();
+ snd_omap_resume_mixer();
+ }
+ }
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+void snd_omap_alsa_free(struct snd_card *card)
+{
+ struct snd_card_omap_codec *chip = card->private_data;
+ ADEBUG();
+
+ /*
+ * Turn off codec after it is done.
+ * Can't do it immediately, since it may still have
+ * buffered data.
+ */
+ schedule_timeout_interruptible(2);
+
+ omap_mcbsp_stop(AUDIO_MCBSP);
+ omap_mcbsp_free(AUDIO_MCBSP);
+
+ audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
+ audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
+}
+
+/* module init & exit */
+
+/*
+ * Inits alsa soudcard structure.
+ * Called by the probe method in codec after function pointers has been set.
+ */
+int snd_omap_alsa_post_probe(struct platform_device *pdev,
+ struct omap_alsa_codec_config *config)
+{
+ int err = 0;
+ int def_rate;
+ struct snd_card *card;
+
+ ADEBUG();
+ alsa_codec_config = config;
+
+ alsa_codec_config->codec_clock_setup();
+ alsa_codec_config->codec_clock_on();
+
+ omap_mcbsp_request(AUDIO_MCBSP);
+ omap_mcbsp_stop(AUDIO_MCBSP);
+ omap_mcbsp_config(AUDIO_MCBSP, alsa_codec_config->mcbsp_regs_alsa);
+ omap_mcbsp_start(AUDIO_MCBSP);
+
+ if (alsa_codec_config && alsa_codec_config->codec_configure_dev)
+ alsa_codec_config->codec_configure_dev();
+
+ alsa_codec_config->codec_clock_off();
+
+ /* register the soundcard */
+ card = snd_card_new(-1, id, THIS_MODULE, sizeof(alsa_codec));
+ if (card == NULL)
+ goto nodev1;
+
+ alsa_codec = kcalloc(1, sizeof(*alsa_codec), GFP_KERNEL);
+ if (alsa_codec == NULL)
+ goto nodev2;
+
+ card->private_data = (void *)alsa_codec;
+ card->private_free = snd_omap_alsa_free;
+
+ alsa_codec->card = card;
+ def_rate = alsa_codec_config->get_default_samplerate();
+ alsa_codec->samplerate = def_rate;
+
+ spin_lock_init(&alsa_codec->s[0].dma_lock);
+ spin_lock_init(&alsa_codec->s[1].dma_lock);
+
+ /* mixer */
+ err = snd_omap_mixer(alsa_codec);
+ if (err < 0)
+ goto nodev3;
+
+ /* PCM */
+ err = snd_card_omap_alsa_pcm(alsa_codec, 0);
+ if (err < 0)
+ goto nodev3;
+
+ strcpy(card->driver, "OMAP_ALSA");
+ strcpy(card->shortname, alsa_codec_config->name);
+ sprintf(card->longname, alsa_codec_config->name);
+
+ snd_omap_init_mixer();
+ snd_card_set_dev(card, &pdev->dev);
+
+ err = snd_card_register(card);
+ if (err == 0) {
+ printk(KERN_INFO "audio support initialized\n");
+ platform_set_drvdata(pdev, card);
+ return 0;
+ }
+
+nodev3:
+ kfree(alsa_codec);
+nodev2:
+ snd_card_free(card);
+nodev1:
+ omap_mcbsp_stop(AUDIO_MCBSP);
+ omap_mcbsp_free(AUDIO_MCBSP);
+
+ return err;
+}
+
+int snd_omap_alsa_remove(struct platform_device *pdev)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+ struct snd_card_omap_codec *chip = card->private_data;
+
+ snd_card_free(card);
+
+ alsa_codec = NULL;
+ card->private_data = NULL;
+ kfree(chip);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * linux/sound/oss/omap-audio-aic23.c
+ *
+ * Glue audio driver for TI TLV320AIC23 codec
+ *
+ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
+ * Copyright (C) 2001, Steve Johnson <stevej@ridgerun.com>
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ * Copyright (C) 2005 Dirk Behme <dirk.behme@de.bosch.com>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/errno.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+
+#include <asm/uaccess.h>
- #include <asm/arch/mcbsp.h>
- #include <asm/arch/fpga.h>
- #include <asm/arch/aic23.h>
- #include <asm/arch/clock.h>
++#include <mach/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
++#include <mach/mcbsp.h>
++#include <mach/fpga.h>
++#include <mach/aic23.h>
++#include <mach/clock.h>
+
+#include "omap-audio.h"
+#include "omap-audio-dma-intfc.h"
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#define PROC_START_FILE "driver/aic23-audio-start"
+#define PROC_STOP_FILE "driver/aic23-audio-stop"
+#endif
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define DPRINTK(ARGS...) printk("<%s>: ",__FUNCTION__);printk(ARGS)
+#else
+#define DPRINTK( x... )
+#endif
+
+#define CODEC_NAME "AIC23"
+
+#if CONFIG_MACH_OMAP_OSK
+#define PLATFORM_NAME "OMAP OSK"
+#elif CONFIG_MACH_OMAP_INNOVATOR
+#define PLATFORM_NAME "OMAP INNOVATOR"
+#else
+#error "Unsupported plattform"
+#endif
+
+/* Define to set the AIC23 as the master w.r.t McBSP */
+#define AIC23_MASTER
+
+#define CODEC_CLOCK 12000000
+
+/*
+ * AUDIO related MACROS
+ */
+#define DEFAULT_BITPERSAMPLE 16
+#define AUDIO_RATE_DEFAULT 44100
+
+/* Select the McBSP For Audio */
+#define AUDIO_MCBSP OMAP_MCBSP1
+
+#define REC_MASK (SOUND_MASK_LINE | SOUND_MASK_MIC)
+#define DEV_MASK (REC_MASK | SOUND_MASK_VOLUME)
+
+#define SET_VOLUME 1
+#define SET_LINE 2
+
+#define DEFAULT_OUTPUT_VOLUME 93
+#define DEFAULT_INPUT_VOLUME 0 /* 0 ==> mute line in */
+
+#define OUTPUT_VOLUME_MIN LHV_MIN
+#define OUTPUT_VOLUME_MAX LHV_MAX
+#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)
+#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MAX
+
+#define INPUT_VOLUME_MIN LIV_MIN
+#define INPUT_VOLUME_MAX LIV_MAX
+#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
+#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX
+
+#define NUMBER_SAMPLE_RATES_SUPPORTED 9
+
+/*
+ * HW interface start and stop helper functions
+ */
+static int audio_ifc_start(void)
+{
+ omap_mcbsp_start(AUDIO_MCBSP);
+ return 0;
+}
+
+static int audio_ifc_stop(void)
+{
+ omap_mcbsp_stop(AUDIO_MCBSP);
+ return 0;
+}
+
+static audio_stream_t output_stream = {
+ .id = "AIC23 out",
+ .dma_dev = OMAP_DMA_MCBSP1_TX,
+ .input_or_output = FMODE_WRITE,
+ .hw_start = audio_ifc_start,
+ .hw_stop = audio_ifc_stop
+};
+
+static audio_stream_t input_stream = {
+ .id = "AIC23 in",
+ .dma_dev = OMAP_DMA_MCBSP1_RX,
+ .input_or_output = FMODE_READ,
+ .hw_start = audio_ifc_start,
+ .hw_stop = audio_ifc_stop
+};
+
+static struct clk *aic23_mclk = 0;
+
+static int audio_dev_id, mixer_dev_id;
+
+static struct aic23_local_info {
+ u8 volume;
+ u16 volume_reg;
+ u8 line;
+ u8 mic;
+ u16 input_volume_reg;
+ int mod_cnt;
+} aic23_local;
+
+struct sample_rate_reg_info {
+ u32 sample_rate;
+ u8 control; /* SR3, SR2, SR1, SR0 and BOSR */
+ u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
+};
+
+/* To Store the default sample rate */
+static long audio_samplerate = AUDIO_RATE_DEFAULT;
+
+/* DAC USB-mode sampling rates (MCLK = 12 MHz) */
+static const struct sample_rate_reg_info
+reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
+ {96000, 0x0E, 0},
+ {88200, 0x1F, 0},
+ {48000, 0x00, 0},
+ {44100, 0x11, 0},
+ {32000, 0x0C, 0},
+ {24000, 0x00, 1},
+ {16000, 0x0C, 1},
+ { 8000, 0x06, 0},
+ { 4000, 0x06, 1},
+};
+
+static struct omap_mcbsp_reg_cfg initial_config = {
+ .spcr2 = FREE | FRST | GRST | XRST | XINTM(3),
+ .spcr1 = RINTM(3) | RRST,
+ .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) |
+ RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(0),
+ .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16),
+ .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) |
+ XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(0) | XFIG,
+ .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16),
+ .srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1),
+ .srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1),
+#ifndef AIC23_MASTER
+ /* configure McBSP to be the I2S master */
+ .pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP,
+#else
+ /* configure McBSP to be the I2S slave */
+ .pcr0 = CLKXP | CLKRP,
+#endif /* AIC23_MASTER */
+};
+
+static void omap_aic23_initialize(void *dummy);
+static void omap_aic23_shutdown(void *dummy);
+static int omap_aic23_ioctl(struct inode *inode, struct file *file,
+ uint cmd, ulong arg);
+static int omap_aic23_probe(void);
+#ifdef MODULE
+static void omap_aic23_remove(void);
+#endif
+static int omap_aic23_suspend(void);
+static int omap_aic23_resume(void);
+static inline void aic23_configure(void);
+static int mixer_open(struct inode *inode, struct file *file);
+static int mixer_release(struct inode *inode, struct file *file);
+static int mixer_ioctl(struct inode *inode, struct file *file, uint cmd,
+ ulong arg);
+
+#ifdef CONFIG_PROC_FS
+static int codec_start(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+static int codec_stop(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+#endif
+
+
+/* File Op structure for mixer */
+static struct file_operations omap_mixer_fops = {
+ .open = mixer_open,
+ .release = mixer_release,
+ .ioctl = mixer_ioctl,
+ .owner = THIS_MODULE
+};
+
+/* To store characteristic info regarding the codec for the audio driver */
+static audio_state_t aic23_state = {
+ .output_stream = &output_stream,
+ .input_stream = &input_stream,
+/* .need_tx_for_rx = 1, //Once the Full Duplex works */
+ .need_tx_for_rx = 0,
+ .hw_init = omap_aic23_initialize,
+ .hw_shutdown = omap_aic23_shutdown,
+ .client_ioctl = omap_aic23_ioctl,
+ .hw_probe = omap_aic23_probe,
+ .hw_remove = __exit_p(omap_aic23_remove),
+ .hw_suspend = omap_aic23_suspend,
+ .hw_resume = omap_aic23_resume,
+};
+
+/* This will be defined in the audio.h */
+static struct file_operations *omap_audio_fops;
+
+extern int aic23_write_value(u8 reg, u16 value);
+
+/* TLV320AIC23 is a write only device */
+static __inline__ void audio_aic23_write(u8 address, u16 data)
+{
+ aic23_write_value(address, data);
+}
+
+static int aic23_update(int flag, int val)
+{
+ u16 volume;
+
+ /* Ignore separate left/right channel for now,
+ even the codec does support it. */
+ val &= 0xff;
+
+ if (val < 0 || val > 100) {
+ printk(KERN_ERR "Trying a bad volume value(%d)!\n",val);
+ return -EPERM;
+ }
+
+ switch (flag) {
+ case SET_VOLUME:
+ // Convert 0 -> 100 volume to 0x00 (LHV_MIN) -> 0x7f (LHV_MAX)
+ // volume range
+ volume = ((val * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN;
+
+ // R/LHV[6:0] 1111111 (+6dB) to 0000000 (-73dB) in 1db steps,
+ // default 1111001 (0dB)
+ aic23_local.volume_reg &= ~OUTPUT_VOLUME_MASK;
+ aic23_local.volume_reg |= volume;
+ audio_aic23_write(LEFT_CHANNEL_VOLUME_ADDR, aic23_local.volume_reg);
+ audio_aic23_write(RIGHT_CHANNEL_VOLUME_ADDR, aic23_local.volume_reg);
+ break;
+
+ case SET_LINE:
+ // Convert 0 -> 100 volume to 0x0 (LIV_MIN) -> 0x1f (LIV_MAX)
+ // volume range
+ volume = ((val * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;
+
+ // R/LIV[4:0] 11111 (+12dB) to 00000 (-34.5dB) in 1.5dB steps,
+ // default 10111 (0dB)
+ aic23_local.input_volume_reg &= ~INPUT_VOLUME_MASK;
+ aic23_local.input_volume_reg |= volume;
+ audio_aic23_write(LEFT_LINE_VOLUME_ADDR, aic23_local.input_volume_reg);
+ audio_aic23_write(RIGHT_LINE_VOLUME_ADDR, aic23_local.input_volume_reg);
+ break;
+ }
+ return 0;
+}
+
+static int mixer_open(struct inode *inode, struct file *file)
+{
+ /* Any mixer specific initialization */
+
+ return 0;
+}
+
+static int mixer_release(struct inode *inode, struct file *file)
+{
+ /* Any mixer specific Un-initialization */
+
+ return 0;
+}
+
+static int
+mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+ int val;
+ int ret = 0;
+ int nr = _IOC_NR(cmd);
+
+ /*
+ * We only accept mixer (type 'M') ioctls.
+ */
+ if (_IOC_TYPE(cmd) != 'M')
+ return -EINVAL;
+
+ DPRINTK(" 0x%08x\n", cmd);
+
+ if (cmd == SOUND_MIXER_INFO) {
+ struct mixer_info mi;
+
+ strncpy(mi.id, "AIC23", sizeof(mi.id));
+ strncpy(mi.name, "TI AIC23", sizeof(mi.name));
+ mi.modify_counter = aic23_local.mod_cnt;
+ return copy_to_user((void *)arg, &mi, sizeof(mi));
+ }
+
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ ret = get_user(val, (int *)arg);
+ if (ret)
+ goto out;
+
+
+ switch (nr) {
+ case SOUND_MIXER_VOLUME:
+ aic23_local.volume = val;
+ aic23_local.mod_cnt++;
+ ret = aic23_update(SET_VOLUME, val);
+ break;
+
+ case SOUND_MIXER_LINE:
+ aic23_local.line = val;
+ aic23_local.mod_cnt++;
+ ret = aic23_update(SET_LINE, val);
+ break;
+
+ case SOUND_MIXER_MIC:
+ aic23_local.mic = val;
+ aic23_local.mod_cnt++;
+ ret = aic23_update(SET_LINE, val);
+ break;
+
+ case SOUND_MIXER_RECSRC:
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+ }
+
+ if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
+ ret = 0;
+
+ switch (nr) {
+ case SOUND_MIXER_VOLUME:
+ val = aic23_local.volume;
+ break;
+ case SOUND_MIXER_LINE:
+ val = aic23_local.line;
+ break;
+ case SOUND_MIXER_MIC:
+ val = aic23_local.mic;
+ break;
+ case SOUND_MIXER_RECSRC:
+ val = REC_MASK;
+ break;
+ case SOUND_MIXER_RECMASK:
+ val = REC_MASK;
+ break;
+ case SOUND_MIXER_DEVMASK:
+ val = DEV_MASK;
+ break;
+ case SOUND_MIXER_CAPS:
+ val = 0;
+ break;
+ case SOUND_MIXER_STEREODEVS:
+ val = 0;
+ break;
+ default:
+ val = 0;
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret == 0)
+ ret = put_user(val, (int *)arg);
+ }
+out:
+ return ret;
+
+}
+
+int omap_set_samplerate(long sample_rate)
+{
+ u8 count = 0;
+ u16 data = 0;
+ /* wait for any frame to complete */
+ udelay(125);
+
+ /* Search for the right sample rate */
+ while ((reg_info[count].sample_rate != sample_rate) &&
+ (count < NUMBER_SAMPLE_RATES_SUPPORTED)) {
+ count++;
+ }
+ if (count == NUMBER_SAMPLE_RATES_SUPPORTED) {
+ printk(KERN_ERR "Invalid Sample Rate %d requested\n",
+ (int)sample_rate);
+ return -EPERM;
+ }
+
+ if (machine_is_omap_innovator()) {
+ /* set the CODEC clock input source to 12.000MHz */
+ fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~0x01,
+ OMAP1510_FPGA_POWER);
+ }
+
+ data = (reg_info[count].divider << CLKIN_SHIFT) |
+ (reg_info[count].control << BOSR_SHIFT) | USB_CLK_ON;
+
+ audio_aic23_write(SAMPLE_RATE_CONTROL_ADDR, data);
+
+ audio_samplerate = sample_rate;
+
+#ifndef AIC23_MASTER
+ {
+ int clkgdv = 0;
+ /*
+ Set Sample Rate at McBSP
+
+ Formula :
+ Codec System Clock = CODEC_CLOCK, or half if clock_divider = 1;
+ clkgdv = ((Codec System Clock / (SampleRate * BitsPerSample * 2)) - 1);
+
+ FWID = BitsPerSample - 1;
+ FPER = (BitsPerSample * 2) - 1;
+ */
+ if (reg_info[count].divider)
+ clkgdv = CODEC_CLOCK / 2;
+ else
+ clkgdv = CODEC_CLOCK;
+
+ clkgdv = (clkgdv / (sample_rate * DEFAULT_BITPERSAMPLE * 2)) - 1;
+
+ initial_config.srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+
+ initial_config.srgr2 =
+ (CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1));
+
+ omap_mcbsp_config(AUDIO_MCBSP, &initial_config);
+ }
+#endif /* AIC23_MASTER */
+
+ return 0;
+}
+
+static void omap_aic23_initialize(void *dummy)
+{
+ DPRINTK("entry\n");
+
+ /* initialize with default sample rate */
+ audio_samplerate = AUDIO_RATE_DEFAULT;
+
+ omap_mcbsp_request(AUDIO_MCBSP);
+
+ /* if configured, then stop mcbsp */
+ omap_mcbsp_stop(AUDIO_MCBSP);
+
+ omap_mcbsp_config(AUDIO_MCBSP, &initial_config);
+ omap_mcbsp_start(AUDIO_MCBSP);
+ aic23_configure();
+
+ DPRINTK("exit\n");
+}
+
+static void omap_aic23_shutdown(void *dummy)
+{
+ /*
+ Turn off codec after it is done.
+ Can't do it immediately, since it may still have
+ buffered data.
+
+ Wait 20ms (arbitrary value) and then turn it off.
+ */
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(2);
+
+ omap_mcbsp_stop(AUDIO_MCBSP);
+ omap_mcbsp_free(AUDIO_MCBSP);
+
+ audio_aic23_write(RESET_CONTROL_ADDR, 0);
+ audio_aic23_write(POWER_DOWN_CONTROL_ADDR, 0xff);
+}
+
+static inline void aic23_configure()
+{
+ /* Reset codec */
+ audio_aic23_write(RESET_CONTROL_ADDR, 0);
+
+ /* Initialize the AIC23 internal state */
+
+ /* Left/Right line input volume control */
+ aic23_local.line = DEFAULT_INPUT_VOLUME;
+ aic23_local.mic = DEFAULT_INPUT_VOLUME;
+ aic23_update(SET_LINE, DEFAULT_INPUT_VOLUME);
+
+ /* Left/Right headphone channel volume control */
+ /* Zero-cross detect on */
+ aic23_local.volume_reg = LZC_ON;
+ aic23_update(SET_VOLUME, aic23_local.volume);
+
+ /* Analog audio path control, DAC selected, delete INSEL_MIC for line in */
+ audio_aic23_write(ANALOG_AUDIO_CONTROL_ADDR, DAC_SELECTED | INSEL_MIC);
+
+ /* Digital audio path control, de-emphasis control 44.1kHz */
+ audio_aic23_write(DIGITAL_AUDIO_CONTROL_ADDR, DEEMP_44K);
+
+ /* Power control, everything is on */
+ audio_aic23_write(POWER_DOWN_CONTROL_ADDR, 0);
+
+ /* Digital audio interface, master/slave mode, I2S, 16 bit */
+#ifdef AIC23_MASTER
+ audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, MS_MASTER | IWL_16 | FOR_DSP);
+#else
+ audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, IWL_16 | FOR_DSP);
+#endif /* AIC23_MASTER */
+
+ /* Enable digital interface */
+ audio_aic23_write(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON);
+
+ /* clock configuration */
+ omap_set_samplerate(audio_samplerate);
+}
+
+static int
+omap_aic23_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+ long val;
+ int ret = 0;
+
+ DPRINTK(" 0x%08x\n", cmd);
+
+ /*
+ * These are platform dependent ioctls which are not handled by the
+ * generic omap-audio module.
+ */
+ switch (cmd) {
+ case SNDCTL_DSP_STEREO:
+ ret = get_user(val, (int *)arg);
+ if (ret)
+ return ret;
+ /* the AIC23 is stereo only */
+ ret = (val == 0) ? -EINVAL : 1;
+ return put_user(ret, (int *)arg);
+
+ case SNDCTL_DSP_CHANNELS:
+ case SOUND_PCM_READ_CHANNELS:
+ /* the AIC23 is stereo only */
+ return put_user(2, (long *)arg);
+
+ case SNDCTL_DSP_SPEED:
+ ret = get_user(val, (long *)arg);
+ if (ret)
+ break;
+ ret = omap_set_samplerate(val);
+ if (ret)
+ break;
+ /* fall through */
+
+ case SOUND_PCM_READ_RATE:
+ return put_user(audio_samplerate, (long *)arg);
+
+ case SOUND_PCM_READ_BITS:
+ case SNDCTL_DSP_SETFMT:
+ case SNDCTL_DSP_GETFMTS:
+ /* we can do 16-bit only */
+ return put_user(AFMT_S16_LE, (long *)arg);
+
+ default:
+ /* Maybe this is meant for the mixer (As per OSS Docs) */
+ return mixer_ioctl(inode, file, cmd, arg);
+ }
+
+ return ret;
+}
+
+static int omap_aic23_probe(void)
+{
+ /* Get the fops from audio oss driver */
+ if (!(omap_audio_fops = audio_get_fops())) {
+ printk(KERN_ERR "Unable to get the file operations for AIC23 OSS driver\n");
+ audio_unregister_codec(&aic23_state);
+ return -EPERM;
+ }
+
+ aic23_local.volume = DEFAULT_OUTPUT_VOLUME;
+
+ /* register devices */
+ audio_dev_id = register_sound_dsp(omap_audio_fops, -1);
+ mixer_dev_id = register_sound_mixer(&omap_mixer_fops, -1);
+
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry(PROC_START_FILE, 0 /* default mode */ ,
+ NULL /* parent dir */ ,
+ codec_start, NULL /* client data */ );
+
+ create_proc_read_entry(PROC_STOP_FILE, 0 /* default mode */ ,
+ NULL /* parent dir */ ,
+ codec_stop, NULL /* client data */ );
+#endif
+
+ /* Announcement Time */
+ printk(KERN_INFO PLATFORM_NAME " " CODEC_NAME
+ " audio support initialized\n");
+ return 0;
+}
+
+#ifdef MODULE
+static void __exit omap_aic23_remove(void)
+{
+ /* Un-Register the codec with the audio driver */
+ unregister_sound_dsp(audio_dev_id);
+ unregister_sound_mixer(mixer_dev_id);
+
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry(PROC_START_FILE, NULL);
+ remove_proc_entry(PROC_STOP_FILE, NULL);
+#endif
+}
+#endif /* MODULE */
+
+static int omap_aic23_suspend(void)
+{
+ /* Empty for the moment */
+ return 0;
+}
+
+static int omap_aic23_resume(void)
+{
+ /* Empty for the moment */
+ return 0;
+}
+
+static int __init audio_aic23_init(void)
+{
+
+ int err = 0;
+
+ if (machine_is_omap_h2() || machine_is_omap_h3())
+ return -ENODEV;
+
+ mutex_init(&aic23_state.mutex);
+
+ if (machine_is_omap_osk()) {
+ /* Set MCLK to be clock input for AIC23 */
+ aic23_mclk = clk_get(0, "mclk");
+
+ if(clk_get_rate( aic23_mclk) != CODEC_CLOCK){
+ /* MCLK ist not at CODEC_CLOCK */
+ if( clk_get_usecount(aic23_mclk) > 0 ){
+ /* MCLK is already in use */
+ printk(KERN_WARNING "MCLK in use at %d Hz. We change it to %d Hz\n",
+ (uint)clk_get_rate( aic23_mclk), CODEC_CLOCK);
+ }
+ if( clk_set_rate( aic23_mclk, CODEC_CLOCK ) ){
+ printk(KERN_ERR "Cannot set MCLK for AIC23 CODEC\n");
+ return -ECANCELED;
+ }
+ }
+
+ clk_enable( aic23_mclk );
+
+ DPRINTK("MCLK = %d [%d], usecount = %d\n",(uint)clk_get_rate( aic23_mclk ),
+ CODEC_CLOCK, clk_get_usecount( aic23_mclk));
+ }
+
+ if (machine_is_omap_innovator()) {
+ u8 fpga;
+ /*
+ Turn on chip select for CODEC (shared with touchscreen).
+ Don't turn it back off, in case touch screen needs it.
+ */
+ fpga = fpga_read(OMAP1510_FPGA_TOUCHSCREEN);
+ fpga |= 0x4;
+ fpga_write(fpga, OMAP1510_FPGA_TOUCHSCREEN);
+ }
+
+ /* register the codec with the audio driver */
+ if ((err = audio_register_codec(&aic23_state))) {
+ printk(KERN_ERR
+ "Failed to register AIC23 driver with Audio OSS Driver\n");
+ }
+
+ return err;
+}
+
+static void __exit audio_aic23_exit(void)
+{
+ (void)audio_unregister_codec(&aic23_state);
+ return;
+}
+
+#ifdef CONFIG_PROC_FS
+static int codec_start(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ void *foo = NULL;
+
+ omap_aic23_initialize(foo);
+
+ printk("AIC23 codec initialization done.\n");
+ return 0;
+}
+static int codec_stop(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ void *foo = NULL;
+
+ omap_aic23_shutdown(foo);
+
+ printk("AIC23 codec shutdown.\n");
+ return 0;
+}
+#endif /* CONFIG_PROC_FS */
+
+module_init(audio_aic23_init);
+module_exit(audio_aic23_exit);
+
+MODULE_AUTHOR("Dirk Behme <dirk.behme@de.bosch.com>");
+MODULE_DESCRIPTION("Glue audio driver for the TI AIC23 codec.");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * linux/sound/oss/omap-audio-dma-intfc.c
+ *
+ * Common audio DMA handling for the OMAP processors
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
+ *
+ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History:
+ *
+ * 2004-06-07 Sriram Kannan - Created new file from omap_audio_dma_intfc.c. This file
+ * will contain only the DMA interface and buffer handling of OMAP
+ * audio driver.
+ *
+ * 2004-06-22 Sriram Kannan - removed legacy code (auto-init). Self-linking of DMA logical channel.
+ *
+ * 2004-08-12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms
+ *
+ * 2004-11-01 Nishanth Menon - 16xx platform code base modified to support multi channel chaining.
+ *
+ * 2004-12-15 Nishanth Menon - Improved 16xx platform channel logic introduced - tasklets, queue handling updated
+ *
+ * 2005-12-10 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/pm.h>
+#include <linux/errno.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/sysrq.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/completion.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
- #include <asm/arch/dma.h>
++#include <mach/hardware.h>
+#include <asm/semaphore.h>
+
- #include <asm/arch/mcbsp.h>
++#include <mach/dma.h>
+#include "omap-audio-dma-intfc.h"
+
++#include <mach/mcbsp.h>
+
+#include "omap-audio.h"
+
+#undef DEBUG
+//#define DEBUG
+#ifdef DEBUG
+#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
+#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__)
+#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n)
+#else
+
+#define DPRINTK( x... )
+#define FN_IN
+#define FN_OUT(x)
+#endif
+
+#define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS);
+
+#define AUDIO_NAME "omap-audio"
+#define AUDIO_NBFRAGS_DEFAULT 8
+#define AUDIO_FRAGSIZE_DEFAULT 8192
+
+#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref)
+
+#define SPIN_ADDR (dma_addr_t)0
+#define SPIN_SIZE 2048
+
+/* Channel Queue Handling macros
+ * tail always points to the current free entry
+ * Head always points to the current entry being used
+ * end is either head or tail
+ */
+
+#define AUDIO_QUEUE_INIT(s) s->dma_q_head = s->dma_q_tail = s->dma_q_count = 0;
+#define AUDIO_QUEUE_FULL(s) (nr_linked_channels == s->dma_q_count)
+#define AUDIO_QUEUE_LAST(s) (1 == s->dma_q_count)
+#define AUDIO_QUEUE_EMPTY(s) (0 == s->dma_q_count)
+#define __AUDIO_INCREMENT_QUEUE(end) ((end)=((end)+1) % nr_linked_channels)
+#define AUDIO_INCREMENT_HEAD(s) __AUDIO_INCREMENT_QUEUE(s->dma_q_head); s->dma_q_count--;
+#define AUDIO_INCREMENT_TAIL(s) __AUDIO_INCREMENT_QUEUE(s->dma_q_tail); s->dma_q_count++;
+
+/* DMA buffer fragmentation sizes */
+#define MAX_DMA_SIZE 0x1000000
+#define CUT_DMA_SIZE 0x1000
+/* TODO: To be moved to more appropriate location */
+#define DCSR_ERROR 0x3
+#define DCSR_SYNC_SET (1 << 6)
+
+#define DCCR_FS (1 << 5)
+#define DCCR_PRIO (1 << 6)
+#define DCCR_AI (1 << 8)
+#define DCCR_REPEAT (1 << 9)
+/* if 0 the channel works in 3.1 compatible mode*/
+#define DCCR_N31COMP (1 << 10)
+#define DCCR_EP (1 << 11)
+#define DCCR_SRC_AMODE_BIT 12
+#define DCCR_SRC_AMODE_MASK (0x3<<12)
+#define DCCR_DST_AMODE_BIT 14
+#define DCCR_DST_AMODE_MASK (0x3<<14)
+#define AMODE_CONST 0x0
+#define AMODE_POST_INC 0x1
+#define AMODE_SINGLE_INDEX 0x2
+#define AMODE_DOUBLE_INDEX 0x3
+
+/**************************** DATA STRUCTURES *****************************************/
+
+static spinlock_t dma_list_lock = SPIN_LOCK_UNLOCKED;
+
+struct audio_isr_work_item {
+ int current_lch;
+ u16 ch_status;
+ audio_stream_t *s;
+};
+
+static char work_item_running = 0;
+static char nr_linked_channels = 1;
+static struct audio_isr_work_item work1, work2;
+
+
+/*********************************** MODULE SPECIFIC FUNCTIONS PROTOTYPES *************/
+
+static void audio_dsr_handler(unsigned long);
+static DECLARE_TASKLET(audio_isr_work1, audio_dsr_handler,
+ (unsigned long)&work1);
+static DECLARE_TASKLET(audio_isr_work2, audio_dsr_handler,
+ (unsigned long)&work2);
+
+static void sound_dma_irq_handler(int lch, u16 ch_status, void *data);
+static void audio_dma_callback(int lch, u16 ch_status, void *data);
+static int omap_start_sound_dma(audio_stream_t * s, dma_addr_t dma_ptr,
+ u_int size);
+static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr,
+ u_int dma_size);
+static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr,
+ u_int dma_size);
+static int audio_start_dma_chain(audio_stream_t * s);
+
+/*********************************** GLOBAL FUNCTIONS DEFINTIONS ***********************/
+
+/***************************************************************************************
+ *
+ * Buffer creation/destruction
+ *
+ **************************************************************************************/
+int audio_setup_buf(audio_stream_t * s)
+{
+ int frag;
+ int dmasize = 0;
+ char *dmabuf = NULL;
+ dma_addr_t dmaphys = 0;
+ FN_IN;
+ if (s->buffers) {
+ FN_OUT(1);
+ return -EBUSY;
+ }
+ s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL);
+ if (!s->buffers)
+ goto err;
+ memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags);
+ for (frag = 0; frag < s->nbfrags; frag++) {
+ audio_buf_t *b = &s->buffers[frag];
+ /*
+ * Let's allocate non-cached memory for DMA buffers.
+ * We try to allocate all memory at once.
+ * If this fails (a common reason is memory fragmentation),
+ * then we allocate more smaller buffers.
+ */
+ if (!dmasize) {
+ dmasize = (s->nbfrags - frag) * s->fragsize;
+ do {
+ dmabuf =
+ dma_alloc_coherent(NULL, dmasize, &dmaphys,
+ 0);
+ if (!dmabuf)
+ dmasize -= s->fragsize;
+ }
+ while (!dmabuf && dmasize);
+ if (!dmabuf)
+ goto err;
+ b->master = dmasize;
+ memzero(dmabuf, dmasize);
+ }
+ b->data = dmabuf;
+ b->dma_addr = dmaphys;
+ dmabuf += s->fragsize;
+ dmaphys += s->fragsize;
+ dmasize -= s->fragsize;
+ }
+ s->usr_head = s->dma_head = s->dma_tail = 0;
+ AUDIO_QUEUE_INIT(s);
+ s->started = 0;
+ s->bytecount = 0;
+ s->fragcount = 0;
+ init_completion(&s->wfc);
+ s->wfc.done = s->nbfrags;
+ FN_OUT(0);
+ return 0;
+ err:
+ audio_discard_buf(s);
+ FN_OUT(1);
+ return -ENOMEM;
+}
+
+void audio_discard_buf(audio_stream_t * s)
+{
+ FN_IN;
+ /* ensure DMA isn't using those buffers */
+ audio_reset(s);
+ if (s->buffers) {
+ int frag;
+ for (frag = 0; frag < s->nbfrags; frag++) {
+ if (!s->buffers[frag].master)
+ continue;
+ dma_free_coherent(NULL,
+ s->buffers[frag].master,
+ s->buffers[frag].data,
+ s->buffers[frag].dma_addr);
+ }
+ kfree(s->buffers);
+ s->buffers = NULL;
+ }
+ FN_OUT(0);
+}
+
+/***************************************************************************************
+ *
+ * DMA channel requests
+ *
+ **************************************************************************************/
+static void omap_sound_dma_link_lch(void *data)
+{
+ audio_stream_t *s = (audio_stream_t *) data;
+ int *chan = s->lch;
+ int i;
+
+ FN_IN;
+ if (s->linked) {
+ FN_OUT(1);
+ return;
+ }
+ for (i = 0; i < nr_linked_channels; i++) {
+ int cur_chan = chan[i];
+ int nex_chan =
+ ((nr_linked_channels - 1 ==
+ i) ? chan[0] : chan[i + 1]);
+ omap_dma_link_lch(cur_chan, nex_chan);
+ }
+ s->linked = 1;
+ FN_OUT(0);
+}
+
+int
+omap_request_sound_dma(int device_id, const char *device_name, void *data,
+ int **channels)
+{
+ int i, err = 0;
+ int *chan = NULL;
+ FN_IN;
+ if (unlikely((NULL == channels) || (NULL == device_name))) {
+ BUG();
+ return -EPERM;
+ }
+ /* Try allocate memory for the num channels */
+ *channels =
+ (int *)kmalloc(sizeof(int) * nr_linked_channels,
+ GFP_KERNEL);
+ chan = *channels;
+ if (NULL == chan) {
+ ERR("No Memory for channel allocs!\n");
+ FN_OUT(-ENOMEM);
+ return -ENOMEM;
+ }
+ spin_lock(&dma_list_lock);
+ for (i = 0; i < nr_linked_channels; i++) {
+ err =
+ omap_request_dma(device_id, device_name,
+ sound_dma_irq_handler, data, &chan[i]);
+ /* Handle Failure condition here */
+ if (err < 0) {
+ int j;
+ for (j = 0; j < i; j++) {
+ omap_free_dma(chan[j]);
+ }
+ spin_unlock(&dma_list_lock);
+ kfree(chan);
+ *channels = NULL;
+ ERR("Error in requesting channel %d=0x%x\n", i, err);
+ FN_OUT(err);
+ return err;
+ }
+ }
+
+ /* Chain the channels together */
+ if (!cpu_is_omap15xx())
+ omap_sound_dma_link_lch(data);
+
+ spin_unlock(&dma_list_lock);
+ FN_OUT(0);
+ return 0;
+}
+
+/***************************************************************************************
+ *
+ * DMA channel requests Freeing
+ *
+ **************************************************************************************/
+static void omap_sound_dma_unlink_lch(void *data)
+{
+ audio_stream_t *s = (audio_stream_t *) data;
+ int *chan = s->lch;
+ int i;
+
+ FN_IN;
+ if (!s->linked) {
+ FN_OUT(1);
+ return;
+ }
+ for (i = 0; i < nr_linked_channels; i++) {
+ int cur_chan = chan[i];
+ int nex_chan =
+ ((nr_linked_channels - 1 ==
+ i) ? chan[0] : chan[i + 1]);
+ omap_dma_unlink_lch(cur_chan, nex_chan);
+ }
+ s->linked = 0;
+ FN_OUT(0);
+}
+
+int omap_free_sound_dma(void *data, int **channels)
+{
+ int i;
+ int *chan = NULL;
+ FN_IN;
+ if (unlikely(NULL == channels)) {
+ BUG();
+ return -EPERM;
+ }
+ if (unlikely(NULL == *channels)) {
+ BUG();
+ return -EPERM;
+ }
+ chan = (*channels);
+
+ if (!cpu_is_omap15xx())
+ omap_sound_dma_unlink_lch(data);
+ for (i = 0; i < nr_linked_channels; i++) {
+ int cur_chan = chan[i];
+ omap_stop_dma(cur_chan);
+ omap_free_dma(cur_chan);
+ }
+ kfree(*channels);
+ *channels = NULL;
+ FN_OUT(0);
+ return 0;
+}
+
+/***************************************************************************************
+ *
+ * Process DMA requests - This will end up starting the transfer. Proper fragments of
+ * Transfers will be initiated.
+ *
+ **************************************************************************************/
+int audio_process_dma(audio_stream_t * s)
+{
+ int ret = 0;
+ unsigned long flags;
+ FN_IN;
+
+ /* Dont let the ISR over ride touching the in_use flag */
+ local_irq_save(flags);
+ if (1 == s->in_use) {
+ local_irq_restore(flags);
+ ERR("Called again while In Use\n");
+ return 0;
+ }
+ s->in_use = 1;
+ local_irq_restore(flags);
+
+ if (s->stopped)
+ goto spin;
+
+ if (s->dma_spinref > 0 && s->pending_frags) {
+ s->dma_spinref = 0;
+ DMA_CLEAR(s);
+ }
+ while (s->pending_frags) {
+ audio_buf_t *b = &s->buffers[s->dma_head];
+ u_int dma_size = s->fragsize - b->offset;
+ if (dma_size > MAX_DMA_SIZE)
+ dma_size = CUT_DMA_SIZE;
+ ret =
+ omap_start_sound_dma(s, b->dma_addr + b->offset, dma_size);
+ if (ret) {
+ goto process_out;
+ }
+ b->dma_ref++;
+ b->offset += dma_size;
+ if (b->offset >= s->fragsize) {
+ s->pending_frags--;
+ if (++s->dma_head >= s->nbfrags)
+ s->dma_head = 0;
+ }
+ }
+ spin:
+ if (s->spin_idle) {
+ int spincnt = 0;
+ ERR("we are spinning\n");
+ while (omap_start_sound_dma(s, SPIN_ADDR, SPIN_SIZE) == 0)
+ spincnt++;
+ /*
+ * Note: if there is still a data buffer being
+ * processed then the ref count is negative. This
+ * allows for the DMA termination to be accounted in
+ * the proper order. Of course dma_spinref can't be
+ * greater than 0 if dma_ref is not 0 since we kill
+ * the spinning above as soon as there is real data to process.
+ */
+ if (s->buffers && s->buffers[s->dma_tail].dma_ref)
+ spincnt = -spincnt;
+ s->dma_spinref += spincnt;
+ }
+
+ process_out:
+ s->in_use = 0;
+
+ FN_OUT(ret);
+ return ret;
+}
+
+/***************************************************************************************
+ *
+ * Prime Rx - Since the recieve buffer has no time limit as to when it would arrive,
+ * we need to prime it
+ *
+ **************************************************************************************/
+void audio_prime_rx(audio_state_t * state)
+{
+ audio_stream_t *is = state->input_stream;
+
+ FN_IN;
+ if (state->need_tx_for_rx) {
+ /*
+ * With some codecs like the Philips UDA1341 we must ensure
+ * there is an output stream at any time while recording since
+ * this is how the UDA1341 gets its clock from the SA1100.
+ * So while there is no playback data to send, the output DMA
+ * will spin with all zeroes. We use the cache flush special
+ * area for that.
+ */
+ state->output_stream->spin_idle = 1;
+ audio_process_dma(state->output_stream);
+ }
+ is->pending_frags = is->nbfrags;
+ init_completion(&is->wfc);
+ is->wfc.done = 0;
+
+ is->active = 1;
+ audio_process_dma(is);
+
+ FN_OUT(0);
+ return;
+}
+
+/***************************************************************************************
+ *
+ * set the fragment size
+ *
+ **************************************************************************************/
+int audio_set_fragments(audio_stream_t * s, int val)
+{
+ FN_IN;
+ if (s->active)
+ return -EBUSY;
+ if (s->buffers)
+ audio_discard_buf(s);
+ s->nbfrags = (val >> 16) & 0x7FFF;
+ val &= 0xFFFF;
+ if (val < 4)
+ val = 4;
+ if (val > 15)
+ val = 15;
+ s->fragsize = 1 << val;
+ if (s->nbfrags < 2)
+ s->nbfrags = 2;
+ if (s->nbfrags * s->fragsize > 128 * 1024)
+ s->nbfrags = 128 * 1024 / s->fragsize;
+ FN_OUT(0);
+ if (audio_setup_buf(s))
+ return -ENOMEM;
+ return val | (s->nbfrags << 16);
+
+}
+
+/***************************************************************************************
+ *
+ * Sync up the buffers before we shutdown, else under-run errors will happen
+ *
+ **************************************************************************************/
+int audio_sync(struct file *file)
+{
+ audio_state_t *state = file->private_data;
+ audio_stream_t *s = state->output_stream;
+ audio_buf_t *b;
+ u_int shiftval = 0;
+ unsigned long flags;
+
+ DECLARE_WAITQUEUE(wait, current);
+
+ FN_IN;
+
+ if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped) {
+ FN_OUT(1);
+ return 0;
+ }
+
+ /*
+ * Send current buffer if it contains data. Be sure to send
+ * a full sample count.
+ */
+ b = &s->buffers[s->usr_head];
+ if (b->offset &= ~3) {
+ /* Wait for a buffer to become free */
+ if (wait_for_completion_interruptible(&s->wfc))
+ return 0;
+ /*
+ * HACK ALERT !
+ * To avoid increased complexity in the rest of the code
+ * where full fragment sizes are assumed, we cheat a little
+ * with the start pointer here and don't forget to restore
+ * it later.
+ */
+
+ /* As this is a last frag we need only one dma channel
+ * to complete. So it's need to unlink dma channels
+ * to avoid empty dma work.
+ */
+ if (!cpu_is_omap15xx() && AUDIO_QUEUE_EMPTY(s))
+ omap_sound_dma_unlink_lch(s);
+
+ shiftval = s->fragsize - b->offset;
+ b->offset = shiftval;
+ b->dma_addr -= shiftval;
+ b->data -= shiftval;
+ local_irq_save(flags);
+ s->bytecount -= shiftval;
+ if (++s->usr_head >= s->nbfrags)
+ s->usr_head = 0;
+
+ s->pending_frags++;
+ audio_process_dma(s);
+ local_irq_restore(flags);
+ }
+
+ /* Let's wait for all buffers to complete */
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&s->wq, &wait);
+ while ((s->pending_frags || (s->wfc.done < s->nbfrags))
+ && !signal_pending(current)) {
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&s->wq, &wait);
+
+ /* undo the pointer hack above */
+ if (shiftval) {
+ local_irq_save(flags);
+ b->dma_addr += shiftval;
+ b->data += shiftval;
+ /* ensure sane DMA code behavior if not yet processed */
+ if (b->offset != 0)
+ b->offset = s->fragsize;
+ local_irq_restore(flags);
+ }
+
+ FN_OUT(0);
+ return 0;
+}
+
+/***************************************************************************************
+ *
+ * Stop all the DMA channels of the stream
+ *
+ **************************************************************************************/
+void audio_stop_dma(audio_stream_t * s)
+{
+ int *chan = s->lch;
+ int i;
+ FN_IN;
+ if (unlikely(NULL == chan)) {
+ BUG();
+ return;
+ }
+ for (i = 0; i < nr_linked_channels; i++) {
+ int cur_chan = chan[i];
+ omap_stop_dma(cur_chan);
+ }
+ s->started = 0;
+ FN_OUT(0);
+ return;
+}
+
+/***************************************************************************************
+ *
+ * Get the dma posn
+ *
+ **************************************************************************************/
+u_int audio_get_dma_pos(audio_stream_t * s)
+{
+ audio_buf_t *b = &s->buffers[s->dma_tail];
+ u_int offset;
+
+ FN_IN;
+ if (b->dma_ref) {
+ offset = omap_get_dma_src_pos(s->lch[s->dma_q_head]) - b->dma_addr;
+ if (offset >= s->fragsize)
+ offset = s->fragsize - 4;
+ } else if (s->pending_frags) {
+ offset = b->offset;
+ } else {
+ offset = 0;
+ }
+ FN_OUT(offset);
+ return offset;
+}
+
+/***************************************************************************************
+ *
+ * Reset the audio buffers
+ *
+ **************************************************************************************/
+void audio_reset(audio_stream_t * s)
+{
+ FN_IN;
+ if (s->buffers) {
+ audio_stop_dma(s);
+ s->buffers[s->dma_head].offset = 0;
+ s->buffers[s->usr_head].offset = 0;
+ s->usr_head = s->dma_head;
+ s->pending_frags = 0;
+ init_completion(&s->wfc);
+ s->wfc.done = s->nbfrags;
+ }
+ s->active = 0;
+ s->stopped = 0;
+ s->started = 0;
+ FN_OUT(0);
+ return;
+}
+
+/***************************************************************************************
+ *
+ * Clear any pending transfers
+ *
+ **************************************************************************************/
+void omap_clear_sound_dma(audio_stream_t * s)
+{
+ FN_IN;
+ omap_clear_dma(s->lch[s->dma_q_head]);
+ FN_OUT(0);
+ return;
+}
+
+/***************************************************************************************
+ *
+ * DMA related functions
+ *
+ **************************************************************************************/
+static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr,
+ u_int dma_size)
+{
+ int dt = 0x1; /* data type 16 */
+ int cen = 32; /* Stereo */
+ int cfn = dma_size / (2 * cen);
+ unsigned long dest_start;
+ int dest_port = 0;
+ int sync_dev = 0;
+
+ FN_IN;
+
+ if (cpu_is_omap15xx() || cpu_is_omap16xx()) {
+ dest_start = AUDIO_MCBSP_DATAWRITE;
+ dest_port = OMAP_DMA_PORT_MPUI;
+ }
+ if (cpu_is_omap24xx()) {
+ dest_start = AUDIO_MCBSP_DATAWRITE;
+ sync_dev = AUDIO_DMA_TX;
+ }
+
+ omap_set_dma_dest_params(channel, dest_port, OMAP_DMA_AMODE_CONSTANT, dest_start, 0, 0);
+ omap_set_dma_src_params(channel, 0, OMAP_DMA_AMODE_POST_INC, dma_ptr, 0, 0);
+ omap_set_dma_transfer_params(channel, dt, cen, cfn, OMAP_DMA_SYNC_ELEMENT, sync_dev, 0);
+
+ FN_OUT(0);
+ return 0;
+}
+
+static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr,
+ u_int dma_size)
+{
+ int dt = 0x1; /* data type 16 */
+ int cen = 16; /* mono */
+ int cfn = dma_size / (2 * cen);
+ unsigned long src_start;
+ int src_port = 0;
+ int sync_dev = 0;
+ int src_sync = 0;
+
+ FN_IN;
+
+ if (cpu_is_omap15xx() || cpu_is_omap16xx()) {
+ src_start = AUDIO_MCBSP_DATAREAD;
+ src_port = OMAP_DMA_PORT_MPUI;
+ }
+ if (cpu_is_omap24xx()) {
+ src_start = AUDIO_MCBSP_DATAREAD;
+ sync_dev = AUDIO_DMA_RX;
+ src_sync = 1;
+ }
+
+ omap_set_dma_src_params(channel, src_port, OMAP_DMA_AMODE_CONSTANT, src_start, 0, 0);
+ omap_set_dma_dest_params(channel, 0, OMAP_DMA_AMODE_POST_INC, dma_ptr, 0, 0);
+ omap_set_dma_transfer_params(channel, dt, cen, cfn, OMAP_DMA_SYNC_ELEMENT, sync_dev, src_sync);
+
+ FN_OUT(0);
+ return 0;
+}
+
+static int audio_start_dma_chain(audio_stream_t * s)
+{
+ int channel = s->lch[s->dma_q_head];
+ FN_IN;
+ if (!s->started) {
+ s->hw_stop(); /* stops McBSP Interface */
+ omap_start_dma(channel);
+ s->started = 1;
+ s->hw_start(); /* start McBSP interface */
+ }
+ /* else the dma itself will progress forward with out our help */
+ FN_OUT(0);
+ return 0;
+}
+
+/* Start DMA -
+ * Do the initial set of work to initialize all the channels as required.
+ * We shall then initate a transfer
+ */
+static int omap_start_sound_dma(audio_stream_t * s, dma_addr_t dma_ptr,
+ u_int dma_size)
+{
+ int ret = -EPERM;
+
+ FN_IN;
+ if (unlikely(dma_size > MAX_DMA_SIZE)) {
+ ERR("DmaSoundDma: Start: overflowed %d-%d\n", dma_size,
+ MAX_DMA_SIZE);
+ return -EOVERFLOW;
+ }
+
+ if (AUDIO_QUEUE_FULL(s)) {
+ ret = -2;
+ goto sound_out;
+ }
+
+ if (s->input_or_output == FMODE_WRITE)
+ /*playback */
+ {
+ ret =
+ audio_set_dma_params_play(s->lch[s->dma_q_tail], dma_ptr,
+ dma_size);
+ } else {
+ ret =
+ audio_set_dma_params_capture(s->lch[s->dma_q_tail], dma_ptr,
+ dma_size);
+ }
+ if (ret != 0) {
+ ret = -2; /* indicate queue full */
+ goto sound_out;
+ }
+ AUDIO_INCREMENT_TAIL(s);
+ ret = audio_start_dma_chain(s);
+ if (ret) {
+ ERR("dma start failed");
+ }
+ sound_out:
+ FN_OUT(ret);
+ return ret;
+
+}
+
+/***************************************************************************************
+ *
+ * ISR related functions
+ *
+ **************************************************************************************/
+/* The work item handler */
+static void audio_dsr_handler(unsigned long inData)
+{
+ void *data = (void *)inData;
+ struct audio_isr_work_item *work = data;
+ audio_stream_t *s = (work->s);
+ int sound_curr_lch = work->current_lch;
+ u16 ch_status = work->ch_status;
+
+ FN_IN;
+ DPRINTK("lch=%d,status=0x%x, data=%p as=%p\n", sound_curr_lch,
+ ch_status, data, s);
+ if (AUDIO_QUEUE_EMPTY(s)) {
+ ERR("Interrupt(%d) for empty queue(h=%d, T=%d)???\n",
+ sound_curr_lch, s->dma_q_head, s->dma_q_tail);
+ ERR("nbfrag=%d,pendfrags=%d,USR-H=%d, QH-%d QT-%d\n",
+ s->nbfrags, s->pending_frags, s->usr_head, s->dma_head,
+ s->dma_tail);
+ FN_OUT(-1);
+ return;
+ }
+
+ AUDIO_INCREMENT_HEAD(s); /* Empty the queue */
+
+ /* Try to fill again */
+ audio_dma_callback(sound_curr_lch, ch_status, s);
+ FN_OUT(0);
+
+}
+
+/* Macro to trace the IRQ calls - checks for multi-channel irqs */
+//#define IRQ_TRACE
+#ifdef IRQ_TRACE
+#define MAX_UP 10
+static char xyz[MAX_UP] = { 0 };
+static int h = 0;
+#endif
+
+/* ISRs have to be short and smart.. So we transfer every heavy duty stuff to the
+ * work item
+ */
+static void sound_dma_irq_handler(int sound_curr_lch, u16 ch_status, void *data)
+{
+ int dma_status = ch_status;
+ audio_stream_t *s = (audio_stream_t *) data;
+ FN_IN;
+#ifdef IRQ_TRACE
+ xyz[h++] = '0' + sound_curr_lch;
+ if (h == MAX_UP - 1) {
+ printk("%s-", xyz);
+ h = 0;
+ }
+#endif
+ DPRINTK("lch=%d,status=0x%x, dma_status=%d, data=%p\n", sound_curr_lch,
+ ch_status, dma_status, data);
+
+ if (dma_status & (DCSR_ERROR)) {
+ if (cpu_is_omap15xx() || cpu_is_omap16xx())
+ omap_stop_dma(sound_curr_lch);
+ ERR("DCSR_ERROR!\n");
+ FN_OUT(-1);
+ return;
+ }
+
+ if (AUDIO_QUEUE_LAST(s))
+ audio_stop_dma(s);
+
+ /* Start the work item - we ping pong the work items */
+ if (!work_item_running) {
+ work1.current_lch = sound_curr_lch;
+ work1.ch_status = ch_status;
+ work1.s = s;
+ /* schedule tasklet 1 */
+ tasklet_schedule(&audio_isr_work1);
+ work_item_running = 1;
+ } else {
+ work2.current_lch = sound_curr_lch;
+ work2.ch_status = ch_status;
+ work2.s = s;
+ /* schedule tasklet 2 */
+ tasklet_schedule(&audio_isr_work2);
+ work_item_running = 0;
+ }
+ FN_OUT(0);
+ return;
+}
+
+/* The call back that handles buffer stuff */
+static void audio_dma_callback(int lch, u16 ch_status, void *data)
+{
+ audio_stream_t *s = data;
+ audio_buf_t *b = &s->buffers[s->dma_tail];
+ FN_IN;
+
+ if (s->dma_spinref > 0) {
+ s->dma_spinref--;
+ } else if (!s->buffers) {
+ printk(KERN_CRIT
+ "omap_audio: received DMA IRQ for non existent buffers!\n");
+ return;
+ } else if (b->dma_ref && --b->dma_ref == 0 && b->offset >= s->fragsize) {
+ /* This fragment is done */
+ b->offset = 0;
+ s->bytecount += s->fragsize;
+ s->fragcount++;
+ s->dma_spinref = -s->dma_spinref;
+
+ if (++s->dma_tail >= s->nbfrags)
+ s->dma_tail = 0;
+
+ if (!s->mapped)
+ complete(&s->wfc);
+ else
+ s->pending_frags++;
+
+ wake_up(&s->wq);
+ }
+
+ audio_process_dma(s);
+
+ FN_OUT(0);
+ return;
+}
+
+/*********************************************************************************
+ *
+ * audio_get_dma_callback(): return the dma interface call back function
+ *
+ *********************************************************************************/
+dma_callback_t audio_get_dma_callback(void)
+{
+ FN_IN;
+ FN_OUT(0);
+ return audio_dma_callback;
+}
+
+static int __init audio_dma_init(void)
+{
+ if (!cpu_is_omap15xx())
+ nr_linked_channels = 2;
+
+ return 0;
+}
+
+static void __exit audio_dma_exit(void)
+{
+ /* Nothing */
+}
+
+module_init(audio_dma_init);
+module_exit(audio_dma_exit);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("Common DMA handling for Audio driver on OMAP processors");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(omap_clear_sound_dma);
+EXPORT_SYMBOL(omap_request_sound_dma);
+EXPORT_SYMBOL(omap_free_sound_dma);
+
+EXPORT_SYMBOL(audio_get_dma_callback);
+EXPORT_SYMBOL(audio_setup_buf);
+EXPORT_SYMBOL(audio_process_dma);
+EXPORT_SYMBOL(audio_prime_rx);
+EXPORT_SYMBOL(audio_set_fragments);
+EXPORT_SYMBOL(audio_sync);
+EXPORT_SYMBOL(audio_stop_dma);
+EXPORT_SYMBOL(audio_get_dma_pos);
+EXPORT_SYMBOL(audio_reset);
+EXPORT_SYMBOL(audio_discard_buf);
--- /dev/null
- #include <asm/hardware.h>
- #include <asm/arch/dma.h>
+/*
+ * linux/sound/oss/omap-audio-tsc2101.c
+ *
+ * Glue driver for TSC2101 for OMAP processors
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History:
+ * -------
+ * 2004-08-12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms.
+ * 2004-09-14 Sriram Kannan - Added /proc support for asynchronous starting/stopping the codec
+ * (without affecting the normal driver flow).
+ * 2004-11-04 Nishanth Menon - Support for power management
+ * 2004-11-07 Nishanth Menon - Support for Common TSC access b/w Touchscreen and audio drivers
+ */
+
+/***************************** INCLUDES ************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/errno.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/mutex.h>
+
+#include <asm/uaccess.h>
- #include <asm/hardware.h>
++#include <mach/hardware.h>
++#include <mach/dma.h>
+#include <asm/io.h>
- #include <asm/arch/mux.h>
- #include <asm/arch/io.h>
++#include <mach/hardware.h>
+
- #include <asm/arch/mcbsp.h>
++#include <mach/mux.h>
++#include <mach/io.h>
+#include <asm/mach-types.h>
+
+#include "omap-audio.h"
+#include "omap-audio-dma-intfc.h"
- #include <asm/arch/dsp_common.h>
++#include <mach/mcbsp.h>
+#ifdef CONFIG_ARCH_OMAP16XX
+#include <../drivers/ssi/omap-uwire.h>
++#include <mach/dsp_common.h>
+#elif defined(CONFIG_ARCH_OMAP24XX)
+#else
+#error "Unsupported configuration"
+#endif
+
+#include <asm/hardware/tsc2101.h>
+#include <../drivers/ssi/omap-tsc2101.h>
+
+/***************************** MACROS ************************************/
+
+#define PROC_SUPPORT
+
+#ifdef PROC_SUPPORT
+#include <linux/proc_fs.h>
+#define PROC_START_FILE "driver/tsc2101-audio-start"
+#define PROC_STOP_FILE "driver/tsc2101-audio-stop"
+#endif
+
+#define CODEC_NAME "TSC2101"
+
+#ifdef CONFIG_ARCH_OMAP16XX
+#define PLATFORM_NAME "OMAP16XX"
+#elif defined(CONFIG_ARCH_OMAP24XX)
+#define PLATFORM_NAME "OMAP2"
+#endif
+
+/* Define to set the tsc as the master w.r.t McBSP */
+#define TSC_MASTER
+
+/*
+ * AUDIO related MACROS
+ */
+#define DEFAULT_BITPERSAMPLE 16
+#define AUDIO_RATE_DEFAULT 44100
+#define PAGE2_AUDIO_CODEC_REGISTERS (2)
+#define LEAVE_CS 0x80
+
+/* Select the McBSP For Audio */
+/* 16XX is MCBSP1 and 24XX is MCBSP2*/
+/* see include/asm-arm/arch-omap/mcbsp.h */
+#ifndef AUDIO_MCBSP
+#error "UnSupported Configuration"
+#endif
+
+#define REC_MASK (SOUND_MASK_LINE | SOUND_MASK_MIC)
+#define DEV_MASK (REC_MASK | SOUND_MASK_VOLUME)
+
+#define SET_VOLUME 1
+#define SET_LINE 2
+#define SET_MIC 3
+#define SET_RECSRC 4
+
+#define DEFAULT_VOLUME 93
+#define DEFAULT_INPUT_VOLUME 20 /* An minimal volume */
+
+/* Tsc Audio Specific */
+#define NUMBER_SAMPLE_RATES_SUPPORTED 16
+#define OUTPUT_VOLUME_MIN 0x7F
+#define OUTPUT_VOLUME_MAX 0x32
+#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX)
+#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MIN
+#define DEFAULT_VOLUME_LEVEL OUTPUT_VOLUME_MAX
+
+/* use input vol of 75 for 0dB gain */
+#define INPUT_VOLUME_MIN 0x0
+#define INPUT_VOLUME_MAX 0x7D
+#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
+#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX
+
+/*********** Debug Macros ********/
+/* To Generate a rather shrill tone -test the entire path */
+//#define TONE_GEN
+/* To Generate a tone for each keyclick - test the tsc,spi paths*/
+//#define TEST_KEYCLICK
+/* To dump the tsc registers for debug */
+//#define TSC_DUMP_REGISTERS
+
+#ifdef DPRINTK
+#undef DPRINTK
+#endif
+#undef DEBUG
+
+//#define DEBUG
+#ifdef DEBUG
+#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
+#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__)
+#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n)
+#else
+#define DPRINTK( x... )
+#define FN_IN
+#define FN_OUT(n)
+#endif
+
+/***************************** Data Structures **********************************/
+
+static int audio_ifc_start(void)
+{
+ omap_mcbsp_start(AUDIO_MCBSP);
+ return 0;
+}
+
+static int audio_ifc_stop(void)
+{
+ omap_mcbsp_stop(AUDIO_MCBSP);
+ return 0;
+}
+
+static audio_stream_t output_stream = {
+ .id = "TSC2101 out",
+ .dma_dev = AUDIO_DMA_TX,
+ .input_or_output = FMODE_WRITE,
+ .hw_start = audio_ifc_start,
+ .hw_stop = audio_ifc_stop,
+};
+
+static audio_stream_t input_stream = {
+ .id = "TSC2101 in",
+ .dma_dev = AUDIO_DMA_RX,
+ .input_or_output = FMODE_READ,
+ .hw_start = audio_ifc_start,
+ .hw_stop = audio_ifc_stop,
+};
+
+static int audio_dev_id, mixer_dev_id;
+
+typedef struct {
+ u8 volume;
+ u8 line;
+ u8 mic;
+ int recsrc;
+ int mod_cnt;
+} tsc2101_local_info;
+
+static tsc2101_local_info tsc2101_local = {
+ volume: DEFAULT_VOLUME,
+ line: DEFAULT_INPUT_VOLUME,
+ mic: DEFAULT_INPUT_VOLUME,
+ recsrc: SOUND_MASK_LINE,
+ mod_cnt: 0
+};
+
+struct sample_rate_reg_info {
+ u16 sample_rate;
+ u8 divisor;
+ u8 fs_44kHz; /* if 0 48 khz, if 1 44.1 khz fsref */
+};
+
+/* To Store the default sample rate */
+static long audio_samplerate = AUDIO_RATE_DEFAULT;
+
+static const struct sample_rate_reg_info
+ reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
+ /* Div 1 */
+ {48000, 0, 0},
+ {44100, 0, 1},
+ /* Div 1.5 */
+ {32000, 1, 0},
+ {29400, 1, 1},
+ /* Div 2 */
+ {24000, 2, 0},
+ {22050, 2, 1},
+ /* Div 3 */
+ {16000, 3, 0},
+ {14700, 3, 1},
+ /* Div 4 */
+ {12000, 4, 0},
+ {11025, 4, 1},
+ /* Div 5 */
+ {9600, 5, 0},
+ {8820, 5, 1},
+ /* Div 5.5 */
+ {8727, 6, 0},
+ {8018, 6, 1},
+ /* Div 6 */
+ {8000, 7, 0},
+ {7350, 7, 1},
+};
+
+static struct omap_mcbsp_reg_cfg initial_config = {
+ .spcr2 = FREE | FRST | GRST | XRST | XINTM(3),
+ .spcr1 = RINTM(3) | RRST,
+ .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) |
+ RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(1),
+ .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16),
+ .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) |
+ XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(1) | XFIG,
+ .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16),
+ .srgr1 = FWID(15),
+ .srgr2 = GSYNC | CLKSP | FSGM | FPER(31),
+
+ /* platform specific initialization */
+#ifdef CONFIG_MACH_OMAP_H2
+ .pcr0 = CLKXM | CLKRM | FSXP | FSRP | CLKXP | CLKRP,
+#elif defined(CONFIG_MACH_OMAP_H3) || defined(CONFIG_MACH_OMAP_H4) || defined(CONFIG_MACH_OMAP_APOLLON)
+
+#ifndef TSC_MASTER
+ .pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP,
+#else
+ .pcr0 = CLKRM | SCLKME | FSXP | FSRP | CLKXP | CLKRP,
+#endif /* tsc Master defs */
+
+#endif /* platform specific inits */
+};
+
+/***************************** MODULES SPECIFIC FUNCTION PROTOTYPES ********************/
+
+static void omap_tsc2101_initialize(void *dummy);
+
+static void omap_tsc2101_shutdown(void *dummy);
+
+static int omap_tsc2101_ioctl(struct inode *inode, struct file *file,
+ uint cmd, ulong arg);
+
+static int omap_tsc2101_probe(void);
+
+static void omap_tsc2101_remove(void);
+
+static int omap_tsc2101_suspend(void);
+
+static int omap_tsc2101_resume(void);
+
+static void tsc2101_configure(void);
+
+static int mixer_open(struct inode *inode, struct file *file);
+
+static int mixer_release(struct inode *inode, struct file *file);
+
+static int mixer_ioctl(struct inode *inode, struct file *file, uint cmd,
+ ulong arg);
+
+#ifdef TEST_KEYCLICK
+void tsc2101_testkeyclick(void);
+#endif
+
+#ifdef TONE_GEN
+void toneGen(void);
+#endif
+
+#ifdef TSC_DUMP_REGISTERS
+static void tsc2101_dumpRegisters(void);
+#endif
+
+#ifdef PROC_SUPPORT
+static int codec_start(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+
+static int codec_stop(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+
+static void tsc2101_start(void);
+#endif
+
+/******************** DATA STRUCTURES USING FUNCTION POINTERS **************************/
+
+/* File Op structure for mixer */
+static struct file_operations omap_mixer_fops = {
+ .open = mixer_open,
+ .release = mixer_release,
+ .ioctl = mixer_ioctl,
+ .owner = THIS_MODULE
+};
+
+/* To store characteristic info regarding the codec for the audio driver */
+static audio_state_t tsc2101_state = {
+ .output_stream = &output_stream,
+ .input_stream = &input_stream,
+/* .need_tx_for_rx = 1, //Once the Full Duplex works */
+ .need_tx_for_rx = 0,
+ .hw_init = omap_tsc2101_initialize,
+ .hw_shutdown = omap_tsc2101_shutdown,
+ .client_ioctl = omap_tsc2101_ioctl,
+ .hw_probe = omap_tsc2101_probe,
+ .hw_remove = omap_tsc2101_remove,
+ .hw_suspend = omap_tsc2101_suspend,
+ .hw_resume = omap_tsc2101_resume,
+};
+
+/* This will be defined in the Audio.h */
+static struct file_operations *omap_audio_fops;
+
+/***************************** MODULES SPECIFIC FUNCTIONs *******************************/
+
+/*********************************************************************************
+ *
+ * Simplified write for tsc Audio
+ *
+ *********************************************************************************/
+static __inline__ void audio_tsc2101_write(u8 address, u16 data)
+{
+ omap_tsc2101_write(PAGE2_AUDIO_CODEC_REGISTERS, address, data);
+}
+
+/*********************************************************************************
+ *
+ * Simplified read for tsc Audio
+ *
+ *********************************************************************************/
+static __inline__ u16 audio_tsc2101_read(u8 address)
+{
+ return (omap_tsc2101_read(PAGE2_AUDIO_CODEC_REGISTERS, address));
+}
+
+/*********************************************************************************
+ *
+ * tsc2101_update()
+ * Volume Adj etc
+ *
+ ********************************************************************************/
+static int tsc2101_update(int flag, int val)
+{
+ u16 volume;
+ u16 data;
+
+ FN_IN;
+ switch (flag) {
+ case SET_VOLUME:
+ if (val < 0 || val > 100) {
+ printk(KERN_ERR "Trying a bad volume value(%d)!\n", val);
+ return -EPERM;
+ }
+ /* Convert 0 -> 100 volume to 0x7F(min) -> y(max) volume range */
+ volume =
+ ((val * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MAX;
+ /* invert the value for getting the proper range 0 min and 100 max */
+ volume = OUTPUT_VOLUME_MIN - volume;
+ data = audio_tsc2101_read(TSC2101_DAC_GAIN_CTRL);
+ data &=
+ ~(DGC_DALVL(OUTPUT_VOLUME_MIN) |
+ DGC_DARVL(OUTPUT_VOLUME_MIN));
+ data |= DGC_DALVL(volume) | DGC_DARVL(volume);
+ audio_tsc2101_write(TSC2101_DAC_GAIN_CTRL, data);
+ data = audio_tsc2101_read(TSC2101_DAC_GAIN_CTRL);
+
+ break;
+
+ case SET_LINE:
+ if (val < 0 || val > 100) {
+ printk(KERN_ERR "Trying a bad volume value(%d)!\n", val);
+ return -EPERM;
+ }
+ /* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range */
+ /* NOTE: 0 is minimum volume and not mute */
+ volume = ((val * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;
+ /* Handset Input not muted, AGC for Handset In off */
+ audio_tsc2101_write(TSC2101_HEADSET_GAIN_CTRL,
+ HGC_ADPGA_HED(volume));
+ break;
+
+ case SET_MIC:
+ if (val < 0 || val > 100) {
+ printk(KERN_ERR "Trying a bad volume value(%d)!\n", val);
+ return -EPERM;
+ }
+ /* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range */
+ /* NOTE: 0 is minimum volume and not mute */
+ volume = ((val * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;
+ /* Handset Input not muted, AGC for Handset In off */
+ audio_tsc2101_write(TSC2101_HANDSET_GAIN_CTRL,
+ HNGC_ADPGA_HND(volume));
+ break;
+
+ case SET_RECSRC:
+ /*
+ * If more than one recording device selected,
+ * disable the device that is currently in use.
+ */
+ if (hweight32(val) > 1)
+ val &= ~tsc2101_local.recsrc;
+
+ data = audio_tsc2101_read(TSC2101_MIXER_PGA_CTRL);
+ data &= ~MPC_MICSEL(7); /* clear all MICSEL bits */
+
+ if (val == SOUND_MASK_MIC) {
+ data |= MPC_MICSEL(1);
+ audio_tsc2101_write(TSC2101_MIXER_PGA_CTRL, data);
+ }
+ else if (val == SOUND_MASK_LINE) {
+ data |= MPC_MICSEL(0);
+ audio_tsc2101_write(TSC2101_MIXER_PGA_CTRL, data);
+ }
+ else {
+ printk(KERN_WARNING "omap1610-tsc2101: Wrong RECSRC"
+ " value specified\n");
+ return -EINVAL;
+ }
+ tsc2101_local.recsrc = val;
+ break;
+ default:
+ printk(KERN_WARNING "omap1610-tsc2101: Wrong tsc2101_update "
+ "flag specified\n");
+ break;
+ }
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * mixer_open()
+ *
+ ********************************************************************************/
+static int mixer_open(struct inode *inode, struct file *file)
+{
+ /* Any mixer specific initialization */
+
+ /* Initalize the tsc2101 */
+ omap_tsc2101_enable();
+
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * mixer_release()
+ *
+ ********************************************************************************/
+static int mixer_release(struct inode *inode, struct file *file)
+{
+ /* Any mixer specific Un-initialization */
+ omap_tsc2101_disable();
+
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * mixer_ioctl()
+ *
+ ********************************************************************************/
+static int
+mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+ int val;
+ int gain;
+ int ret = 0;
+ int nr = _IOC_NR(cmd);
+
+ /*
+ * We only accept mixer (type 'M') ioctls.
+ */
+ FN_IN;
+ if (_IOC_TYPE(cmd) != 'M')
+ return -EINVAL;
+
+ DPRINTK(" 0x%08x\n", cmd);
+
+ if (cmd == SOUND_MIXER_INFO) {
+ struct mixer_info mi;
+
+ strncpy(mi.id, "TSC2101", sizeof(mi.id));
+ strncpy(mi.name, "TI TSC2101", sizeof(mi.name));
+ mi.modify_counter = tsc2101_local.mod_cnt;
+ FN_OUT(1);
+ return copy_to_user((void __user *)arg, &mi, sizeof(mi));
+ }
+
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ ret = get_user(val, (int __user *)arg);
+ if (ret)
+ goto out;
+
+ /* Ignore separate left/right channel for now,
+ * even the codec does support it.
+ */
+ gain = val & 255;
+
+ switch (nr) {
+ case SOUND_MIXER_VOLUME:
+ tsc2101_local.volume = val;
+ tsc2101_local.mod_cnt++;
+ ret = tsc2101_update(SET_VOLUME, gain);
+ break;
+
+ case SOUND_MIXER_LINE:
+ tsc2101_local.line = val;
+ tsc2101_local.mod_cnt++;
+ ret = tsc2101_update(SET_LINE, gain);
+ break;
+
+ case SOUND_MIXER_MIC:
+ tsc2101_local.mic = val;
+ tsc2101_local.mod_cnt++;
+ ret = tsc2101_update(SET_MIC, gain);
+ break;
+
+ case SOUND_MIXER_RECSRC:
+ if ((val & SOUND_MASK_LINE) ||
+ (val & SOUND_MASK_MIC)) {
+ if (tsc2101_local.recsrc != val) {
+ tsc2101_local.mod_cnt++;
+ tsc2101_update(SET_RECSRC, val);
+ }
+ }
+ else {
+ ret = -EINVAL;
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+ }
+
+ if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
+ ret = 0;
+
+ switch (nr) {
+ case SOUND_MIXER_VOLUME:
+ val = tsc2101_local.volume;
+ val = (tsc2101_local.volume << 8) |
+ tsc2101_local.volume;
+ break;
+ case SOUND_MIXER_LINE:
+ val = (tsc2101_local.line << 8) |
+ tsc2101_local.line;
+ break;
+ case SOUND_MIXER_MIC:
+ val = (tsc2101_local.mic << 8) |
+ tsc2101_local.mic;
+ break;
+ case SOUND_MIXER_RECSRC:
+ val = tsc2101_local.recsrc;
+ break;
+ case SOUND_MIXER_RECMASK:
+ val = REC_MASK;
+ break;
+ case SOUND_MIXER_DEVMASK:
+ val = DEV_MASK;
+ break;
+ case SOUND_MIXER_CAPS:
+ val = 0;
+ break;
+ case SOUND_MIXER_STEREODEVS:
+ val = SOUND_MASK_VOLUME;
+ break;
+ default:
+ val = 0;
+ printk(KERN_WARNING "omap1610-tsc2101: unknown mixer "
+ "read ioctl flag specified\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret == 0)
+ ret = put_user(val, (int __user *)arg);
+ }
+ out:
+ FN_OUT(0);
+ return ret;
+
+}
+
+/*********************************************************************************
+ *
+ * omap_set_samplerate()
+ *
+ ********************************************************************************/
+static int omap_set_samplerate(long sample_rate)
+{
+ u8 count = 0;
+ u16 data = 0;
+ int clkgdv = 0;
+ /* wait for any frame to complete */
+ udelay(125);
+
+ /* Search for the right sample rate */
+ while ((reg_info[count].sample_rate != sample_rate) &&
+ (count < NUMBER_SAMPLE_RATES_SUPPORTED)) {
+ count++;
+ }
+ if (count == NUMBER_SAMPLE_RATES_SUPPORTED) {
+ printk(KERN_ERR "Invalid Sample Rate %d requested\n",
+ (int)sample_rate);
+ return -EPERM;
+ }
+
+ /* Set AC1 */
+ data = audio_tsc2101_read(TSC2101_AUDIO_CTRL_1);
+ /*Clear prev settings */
+ data &= ~(AC1_DACFS(0x07) | AC1_ADCFS(0x07));
+ data |=
+ AC1_DACFS(reg_info[count].divisor) | AC1_ADCFS(reg_info[count].
+ divisor);
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_1, data);
+
+ /* Set the AC3 */
+ data = audio_tsc2101_read(TSC2101_AUDIO_CTRL_3);
+ /*Clear prev settings */
+ data &= ~(AC3_REFFS | AC3_SLVMS);
+ data |= (reg_info[count].fs_44kHz) ? AC3_REFFS : 0;
+#ifdef TSC_MASTER
+ data |= AC3_SLVMS;
+#endif /* #ifdef TSC_MASTER */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_3, data);
+
+ /* program the PLLs */
+ if (reg_info[count].fs_44kHz) {
+ /* 44.1 khz - 12 MHz Mclk */
+ audio_tsc2101_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL | PLL1_PVAL(1) | PLL1_I_VAL(7)); /* PVAL 1; I_VAL 7 */
+ audio_tsc2101_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x1490)); /* D_VAL 5264 */
+ } else {
+ /* 48 khz - 12 Mhz Mclk */
+ audio_tsc2101_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL | PLL1_PVAL(1) | PLL1_I_VAL(8)); /* PVAL 1; I_VAL 8 */
+ audio_tsc2101_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x780)); /* D_VAL 1920 */
+ }
+
+ audio_samplerate = sample_rate;
+
+ /* Set the sample rate */
+#ifndef TSC_MASTER
+ clkgdv =
+ DEFAULT_MCBSP_CLOCK / (sample_rate *
+ (DEFAULT_BITPERSAMPLE * 2 - 1));
+ if (clkgdv)
+ initial_config.srgr1 =
+ (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+ else
+ return (1);
+
+ /* Stereo Mode */
+ initial_config.srgr2 =
+ (CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1));
+#else
+ initial_config.srgr1 =
+ (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
+ initial_config.srgr2 =
+ ((GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1)));
+
+#endif /* end of #ifdef TSC_MASTER */
+ omap_mcbsp_config(AUDIO_MCBSP, &initial_config);
+
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * omap_tsc2101_initialize() [hw_init() ]
+ *
+ ********************************************************************************/
+static void omap_tsc2101_initialize(void *dummy)
+{
+
+ DPRINTK("omap_tsc2101_initialize entry\n");
+
+ /* initialize with default sample rate */
+ audio_samplerate = AUDIO_RATE_DEFAULT;
+
+ omap_mcbsp_request(AUDIO_MCBSP);
+
+ /* if configured, then stop mcbsp */
+ omap_mcbsp_stop(AUDIO_MCBSP);
+
+ omap_tsc2101_enable();
+
+ omap_mcbsp_config(AUDIO_MCBSP, &initial_config);
+ omap_mcbsp_start(AUDIO_MCBSP);
+ tsc2101_configure();
+
+#ifdef TEST_KEYCLICK
+ tsc2101_testkeyclick();
+#endif
+
+#ifdef TONE_GEN
+ toneGen();
+#endif
+
+ DPRINTK("omap_tsc2101_initialize exit\n");
+}
+
+/*********************************************************************************
+ *
+ * omap_tsc2101_shutdown() [hw_shutdown() ]
+ *
+ ********************************************************************************/
+static void omap_tsc2101_shutdown(void *dummy)
+{
+ /*
+ Turn off codec after it is done.
+ Can't do it immediately, since it may still have
+ buffered data.
+
+ Wait 20ms (arbitrary value) and then turn it off.
+ */
+
+ FN_IN;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(2);
+
+ omap_mcbsp_stop(AUDIO_MCBSP);
+ omap_mcbsp_free(AUDIO_MCBSP);
+
+ audio_tsc2101_write(TSC2101_CODEC_POWER_CTRL,
+ ~(CPC_SP1PWDN | CPC_SP2PWDN | CPC_BASSBC));
+
+ omap_tsc2101_disable();
+
+ FN_OUT(0);
+}
+
+/*********************************************************************************
+ *
+ * tsc2101_configure
+ *
+ ********************************************************************************/
+static void tsc2101_configure(void)
+{
+ FN_IN;
+
+ audio_tsc2101_write(TSC2101_CODEC_POWER_CTRL, 0x0000);
+
+ /*Mute Analog Sidetone */
+ /*Select MIC_INHED input for headset */
+ /*Cell Phone In not connected */
+ audio_tsc2101_write(TSC2101_MIXER_PGA_CTRL,
+ MPC_ASTMU | MPC_ASTG(0x40) | MPC_MICADC);
+
+ /* Set record source */
+ tsc2101_update(SET_RECSRC, tsc2101_local.recsrc);
+
+ /* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */
+ /* 1dB AGC hysteresis */
+ /* MICes bias 2V */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_4, AC4_MB_HED(0));
+
+ /* Set codec output volume */
+ audio_tsc2101_write(TSC2101_DAC_GAIN_CTRL, 0x0000);
+
+ /* DAC left and right routed to SPK2 */
+ /* SPK1/2 unmuted */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_5,
+ AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
+ AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
+ AC5_HDSCPTC);
+
+ /* OUT8P/N muted, CPOUT muted */
+
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_6,
+ AC6_MUTLSPK | AC6_MUTSPK2 | AC6_LDSCPTC |
+ AC6_VGNDSCPTC);
+
+ /* Headset/Hook switch detect disabled */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_7, 0x0000);
+
+ /* Left line input volume control */
+ tsc2101_update(SET_LINE, tsc2101_local.line);
+
+ /* mic input volume control */
+ tsc2101_update(SET_MIC, tsc2101_local.mic);
+
+ /* Left/Right headphone channel volume control */
+ /* Zero-cross detect on */
+ tsc2101_update(SET_VOLUME, tsc2101_local.volume);
+
+ /* clock configuration */
+ omap_set_samplerate(audio_samplerate);
+
+#ifdef TSC_DUMP_REGISTERS
+ tsc2101_dumpRegisters();
+#endif
+
+ FN_OUT(0);
+}
+
+#ifdef PROC_SUPPORT
+static void tsc2101_start(void)
+{
+ FN_IN;
+
+ audio_tsc2101_write(TSC2101_CODEC_POWER_CTRL, 0x0000);
+
+ /*Mute Analog Sidetone */
+ /*Select MIC_INHED input for headset */
+ /*Cell Phone In not connected */
+ audio_tsc2101_write(TSC2101_MIXER_PGA_CTRL,
+ MPC_ASTMU | MPC_ASTG(0x40) | MPC_MICADC);
+
+ /* Set record source */
+ tsc2101_update(SET_RECSRC, tsc2101_local.recsrc);
+
+ /* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */
+ /* 1dB AGC hysteresis */
+ /* MICes bias 2V */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_4, AC4_MB_HED(0));
+
+ /* Set codec output volume */
+ audio_tsc2101_write(TSC2101_DAC_GAIN_CTRL, 0x0000);
+
+ /* DAC left and right routed to SPK2 */
+ /* SPK1/2 unmuted */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_5,
+ AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
+ AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
+ AC5_HDSCPTC);
+
+ /* OUT8P/N muted, CPOUT muted */
+
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_6,
+ AC6_MUTLSPK | AC6_MUTSPK2 | AC6_LDSCPTC |
+ AC6_VGNDSCPTC);
+
+ /* Headset/Hook switch detect disabled */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_7, 0x0000);
+
+ /* Left line input volume control */
+ tsc2101_update(SET_LINE, tsc2101_local.line);
+
+ /* mic input volume control */
+ tsc2101_update(SET_MIC, tsc2101_local.mic);
+
+ /* Left/Right headphone channel volume control */
+ /* Zero-cross detect on */
+ tsc2101_update(SET_VOLUME, tsc2101_local.volume);
+
+ FN_OUT(0);
+
+}
+#endif
+
+/******************************************************************************************
+ *
+ * All generic ioctl's are handled by audio_ioctl() [File: omap-audio.c]. This
+ * routine handles some platform specific ioctl's
+ *
+ ******************************************************************************************/
+static int
+omap_tsc2101_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+ long val;
+ int ret = 0;
+
+ DPRINTK(" 0x%08x\n", cmd);
+
+ /*
+ * These are platform dependent ioctls which are not handled by the
+ * generic omap-audio module.
+ */
+ switch (cmd) {
+ case SNDCTL_DSP_STEREO:
+ ret = get_user(val, (int __user *)arg);
+ if (ret)
+ return ret;
+ /* the AIC23 is stereo only */
+ ret = (val == 0) ? -EINVAL : 1;
+ FN_OUT(1);
+ return put_user(ret, (int __user *)arg);
+
+ case SNDCTL_DSP_CHANNELS:
+ case SOUND_PCM_READ_CHANNELS:
+ /* the AIC23 is stereo only */
+ FN_OUT(2);
+ return put_user(2, (long __user *)arg);
+
+ case SNDCTL_DSP_SPEED:
+ ret = get_user(val, (long __user *)arg);
+ if (ret)
+ break;
+ ret = omap_set_samplerate(val);
+ if (ret)
+ break;
+ /* fall through */
+
+ case SOUND_PCM_READ_RATE:
+ FN_OUT(3);
+ return put_user(audio_samplerate, (long __user *)arg);
+
+ case SOUND_PCM_READ_BITS:
+ case SNDCTL_DSP_SETFMT:
+ case SNDCTL_DSP_GETFMTS:
+ /* we can do 16-bit only */
+ FN_OUT(4);
+ return put_user(AFMT_S16_LE, (long __user *)arg);
+
+ default:
+ /* Maybe this is meant for the mixer (As per OSS Docs) */
+ FN_OUT(5);
+ return mixer_ioctl(inode, file, cmd, arg);
+ }
+
+ FN_OUT(0);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * module_probe for TSC2101
+ *
+ ********************************************************************************/
+static int omap_tsc2101_probe(void)
+{
+ FN_IN;
+
+ /* Get the fops from audio oss driver */
+ if (!(omap_audio_fops = audio_get_fops())) {
+ printk(KERN_ERR "Unable to Get the FOPs of Audio OSS driver\n");
+ audio_unregister_codec(&tsc2101_state);
+ return -EPERM;
+ }
+
+ /* register devices */
+ audio_dev_id = register_sound_dsp(omap_audio_fops, -1);
+ mixer_dev_id = register_sound_mixer(&omap_mixer_fops, -1);
+
+#ifdef PROC_SUPPORT
+ create_proc_read_entry(PROC_START_FILE, 0 /* default mode */ ,
+ NULL /* parent dir */ ,
+ codec_start, NULL /* client data */ );
+
+ create_proc_read_entry(PROC_STOP_FILE, 0 /* default mode */ ,
+ NULL /* parent dir */ ,
+ codec_stop, NULL /* client data */ );
+#endif
+
+ /* Announcement Time */
+ printk(KERN_INFO PLATFORM_NAME " " CODEC_NAME
+ " Audio support initialized\n");
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * Module Remove for TSC2101
+ *
+ ********************************************************************************/
+static void omap_tsc2101_remove(void)
+{
+ FN_IN;
+ /* Un-Register the codec with the audio driver */
+ unregister_sound_dsp(audio_dev_id);
+ unregister_sound_mixer(mixer_dev_id);
+
+#ifdef PROC_SUPPORT
+ remove_proc_entry(PROC_START_FILE, NULL);
+ remove_proc_entry(PROC_STOP_FILE, NULL);
+#endif
+ FN_OUT(0);
+
+}
+
+/*********************************************************************************
+ *
+ * Module Suspend for TSC2101
+ *
+ ********************************************************************************/
+static int omap_tsc2101_suspend(void)
+{
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * Module Resume for TSC2101
+ *
+ ********************************************************************************/
+static int omap_tsc2101_resume(void)
+{
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * module_init for TSC2101
+ *
+ ********************************************************************************/
+static int __init audio_tsc2101_init(void)
+{
+
+ int err = 0;
+ FN_IN;
+
+ if (machine_is_omap_osk() || machine_is_omap_innovator())
+ return -ENODEV;
+
+ mutex_init(&tsc2101_state.mutex);
+
+ /* register the codec with the audio driver */
+ if ((err = audio_register_codec(&tsc2101_state))) {
+ printk(KERN_ERR
+ "Failed to register TSC driver with Audio OSS Driver\n");
+ }
+ FN_OUT(err);
+ return err;
+}
+
+/*********************************************************************************
+ *
+ * module_exit for TSC2101
+ *
+ ********************************************************************************/
+static void __exit audio_tsc2101_exit(void)
+{
+
+ FN_IN;
+ (void)audio_unregister_codec(&tsc2101_state);
+ FN_OUT(0);
+ return;
+}
+
+/**************************** DEBUG FUNCTIONS ***********************************/
+
+/*********************************************************************************
+ * TEST_KEYCLICK:
+ * This is a test to generate various keyclick sound on tsc.
+ * verifies if the tsc and the spi interfaces are operational.
+ *
+ ********************************************************************************/
+#ifdef TEST_KEYCLICK
+void tsc2101_testkeyclick(void)
+{
+ u8 freq = 0;
+ u16 old_reg_val, reg_val;
+ u32 uDummyVal = 0;
+ u32 uTryVal = 0;
+
+ old_reg_val = audio_tsc2101_read(TSC2101_AUDIO_CTRL_2);
+
+ /* Keyclick active, max amplitude and longest key click len(32 period) */
+ printk(KERN_INFO " TESTING KEYCLICK\n Listen carefully NOW....\n");
+ printk(KERN_INFO " OLD REG VAL=0x%x\n", old_reg_val);
+ /* try all frequencies */
+ for (; freq < 8; freq++) {
+ /* Keyclick active, max amplitude and longest key click len(32 period) */
+ reg_val = old_reg_val | AC2_KCLAC(0x7) | AC2_KCLLN(0xF);
+ uDummyVal = 0;
+ uTryVal = 0;
+ printk(KERN_INFO "\n\nTrying frequency %d reg val= 0x%x\n",
+ freq, reg_val | AC2_KCLFRQ(freq) | AC2_KCLEN);
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_2,
+ reg_val | AC2_KCLFRQ(freq) | AC2_KCLEN);
+ printk("DONE. Wait 10 ms ...\n");
+ /* wait till the kclk bit is auto cleared! time out also to be considered. */
+ while (audio_tsc2101_read(TSC2101_AUDIO_CTRL_2) & AC2_KCLEN) {
+ udelay(3);
+ uTryVal++;
+ if (uTryVal > 2000) {
+ printk(KERN_ERR
+ "KEYCLICK TIMED OUT! freq val=%d, POSSIBLE ERROR!\n",
+ freq);
+ printk(KERN_INFO
+ "uTryVal == %d: Read back new reg val= 0x%x\n",
+ uTryVal,
+ audio_tsc2101_read
+ (TSC2101_AUDIO_CTRL_2));
+ /* clear */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_2, 0x00);
+ break;
+ }
+ }
+ }
+ /* put the old value back */
+ audio_tsc2101_write(TSC2101_AUDIO_CTRL_2, old_reg_val);
+ printk(KERN_INFO " KEYCLICK TEST COMPLETE\n");
+
+} /* End of tsc2101_testkeyclick */
+
+#endif /* TEST_KEYCLICK */
+
+/*********************************************************************************
+ * TONEGEN:
+ * This is a test to generate a rather unpleasant sound..
+ * verifies if the mcbsp is active (requires MCBSP_DIRECT_RW to be active on McBSP)
+ *
+ ********************************************************************************/
+#ifdef TONE_GEN
+/* Generates a shrill tone */
+u16 tone[] = {
+ 0x0ce4, 0x0ce4, 0x1985, 0x1985, 0x25A1, 0x25A1, 0x30FD, 0x30FE,
+ 0x3B56, 0x3B55, 0x447A, 0x447A, 0x4C3B, 0x4C3C, 0x526D, 0x526C,
+ 0x56F1, 0x56F1, 0x59B1, 0x59B1, 0x5A9E, 0x5A9D, 0x59B1, 0x59B2,
+ 0x56F3, 0x56F2, 0x526D, 0x526D, 0x4C3B, 0x4C3B, 0x447C, 0x447C,
+ 0x3B5A, 0x3B59, 0x30FE, 0x30FE, 0x25A5, 0x25A6, 0x1989, 0x198A,
+ 0x0CE5, 0x0CE3, 0x0000, 0x0000, 0xF31C, 0xF31C, 0xE677, 0xE676,
+ 0xDA5B, 0xDA5B, 0xCF03, 0xCF03, 0xC4AA, 0xC4AA, 0xBB83, 0xBB83,
+ 0xB3C5, 0xB3C5, 0xAD94, 0xAD94, 0xA90D, 0xA90E, 0xA64F, 0xA64E,
+ 0xA562, 0xA563, 0xA64F, 0xA64F, 0xA910, 0xA90F, 0xAD93, 0xAD94,
+ 0xB3C4, 0xB3C4, 0xBB87, 0xBB86, 0xC4AB, 0xC4AB, 0xCF03, 0xCF03,
+ 0xDA5B, 0xDA5A, 0xE67B, 0xE67B, 0xF31B, 0xF3AC, 0x0000, 0x0000,
+ 0x0CE4, 0x0CE4, 0x1985, 0x1985, 0x25A1, 0x25A1, 0x30FD, 0x30FE,
+ 0x3B56, 0x3B55, 0x447A, 0x447A, 0x4C3B, 0x4C3C, 0x526D, 0x526C,
+ 0x56F1, 0x56F1, 0x59B1, 0x59B1, 0x5A9E, 0x5A9D, 0x59B1, 0x59B2,
+ 0x56F3, 0x56F2, 0x526D, 0x526D, 0x4C3B, 0x4C3B, 0x447C, 0x447C,
+ 0x3B5A, 0x3B59, 0x30FE, 0x30FE, 0x25A5, 0x25A6, 0x1989, 0x198A,
+ 0x0CE5, 0x0CE3, 0x0000, 0x0000, 0xF31C, 0xF31C, 0xE677, 0xE676,
+ 0xDA5B, 0xDA5B, 0xCF03, 0xCF03, 0xC4AA, 0xC4AA, 0xBB83, 0xBB83,
+ 0xB3C5, 0xB3C5, 0xAD94, 0xAD94, 0xA90D, 0xA90E, 0xA64F, 0xA64E,
+ 0xA562, 0xA563, 0xA64F, 0xA64F, 0xA910, 0xA90F, 0xAD93, 0xAD94,
+ 0xB3C4, 0xB3C4, 0xBB87, 0xBB86, 0xC4AB, 0xC4AB, 0xCF03, 0xCF03,
+ 0xDA5B, 0xDA5A, 0xE67B, 0xE67B, 0xF31B, 0xF3AC, 0x0000, 0x0000,
+ 0x0CE4, 0x0CE4, 0x1985, 0x1985, 0x25A1, 0x25A1, 0x30FD, 0x30FE,
+ 0x3B56, 0x3B55, 0x447A, 0x447A, 0x4C3B, 0x4C3C, 0x526D, 0x526C,
+ 0x56F1, 0x56F1, 0x59B1, 0x59B1, 0x5A9E, 0x5A9D, 0x59B1, 0x59B2,
+ 0x56F3, 0x56F2, 0x526D, 0x526D, 0x4C3B, 0x4C3B, 0x447C, 0x447C,
+ 0x3B5A, 0x3B59, 0x30FE, 0x30FE, 0x25A5, 0x25A6, 0x1989, 0x198A,
+ 0x0CE5, 0x0CE3, 0x0000, 0x0000, 0xF31C, 0xF31C, 0xE677, 0xE676,
+ 0xDA5B, 0xDA5B, 0xCF03, 0xCF03, 0xC4AA, 0xC4AA, 0xBB83, 0xBB83,
+ 0xB3C5, 0xB3C5, 0xAD94, 0xAD94, 0xA90D, 0xA90E, 0xA64F, 0xA64E,
+ 0xA562, 0xA563, 0xA64F, 0xA64F, 0xA910, 0xA90F, 0xAD93, 0xAD94,
+ 0xB3C4, 0xB3C4, 0xBB87, 0xBB86, 0xC4AB, 0xC4AB, 0xCF03, 0xCF03,
+ 0xDA5B, 0xDA5A, 0xE67B, 0xE67B, 0xF31B, 0xF3AC, 0x0000, 0x0000
+};
+
+void toneGen(void)
+{
+ int count = 0;
+ int ret = 0;
+ printk(KERN_INFO "TONE GEN TEST :");
+
+ for (count = 0; count < 5000; count++) {
+ int bytes;
+ for (bytes = 0; bytes < sizeof(tone) / 2; bytes++) {
+ ret = omap_mcbsp_pollwrite(AUDIO_MCBSP, tone[bytes]);
+ if (ret == -1) {
+ /* retry */
+ bytes--;
+ } else if (ret == -2) {
+ printk(KERN_INFO "ERROR:bytes=%d\n", bytes);
+ return;
+ }
+ }
+ }
+ printk(KERN_INFO "SUCCESS\n");
+}
+
+#endif /* End of TONE_GEN */
+
+/*********************************************************************************
+ *
+ * TSC_DUMP_REGISTERS:
+ * This will dump the entire register set of Page 2 tsc2101.
+ * Useful for major goof ups
+ *
+ ********************************************************************************/
+#ifdef TSC_DUMP_REGISTERS
+static void tsc2101_dumpRegisters(void)
+{
+ int i = 0;
+ u16 data = 0;
+ printk("TSC 2101 Register dump for Page 2 \n");
+ for (i = 0; i < 0x27; i++) {
+ data = audio_tsc2101_read(i);
+ printk(KERN_INFO "Register[%x]=0x%04x\n", i, data);
+
+ }
+}
+#endif /* End of #ifdef TSC_DUMP_REGISTERS */
+
+#ifdef PROC_SUPPORT
+static int codec_start(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ omap_tsc2101_enable();
+ tsc2101_start();
+ printk("Codec initialization done.\n");
+ return 0;
+}
+static int codec_stop(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+
+ omap_tsc2101_disable();
+ audio_tsc2101_write(TSC2101_CODEC_POWER_CTRL,
+ ~(CPC_SP1PWDN | CPC_SP2PWDN | CPC_BASSBC));
+ printk("Codec shutdown.\n");
+ return 0;
+}
+#endif
+
+/*********************************************************************************
+ *
+ * Other misc management, registration etc
+ *
+ ********************************************************************************/
+module_init(audio_tsc2101_init);
+module_exit(audio_tsc2101_exit);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION
+ ("Glue audio driver for the TI OMAP1610/OMAP1710 TSC2101 codec.");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/hardware.h>
+/*
+ * linux/sound/oss/omap-audio.c
+ *
+ * Common audio handling for the OMAP processors
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
+ *
+ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History:
+ *
+ * 2004/08/12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms
+ *
+ * 2004-11-01 Nishanth Menon - modified to support 16xx and 17xx
+ * platform multi channel chaining.
+ *
+ * 2004-11-04 Nishanth Menon - Added support for power management
+ *
+ * 2004-12-17 Nishanth Menon - Provided proper module handling support
+ */
+
+/***************************** INCLUDES ************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/pm.h>
+#include <linux/errno.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/sysrq.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
++#include <mach/hardware.h>
+
+#include "omap-audio-dma-intfc.h"
+#include "omap-audio.h"
+
+/***************************** MACROS ************************************/
+
+#undef DEBUG
+//#define DEBUG
+#ifdef DEBUG
+#define DPRINTK printk
+#define FN_IN printk("[omap_audio.c:[%s] start\n", __FUNCTION__)
+#define FN_OUT(n) printk("[omap_audio.c:[%s] end(%d)\n", __FUNCTION__ , n)
+#else
+#define DPRINTK( x... )
+#define FN_IN
+#define FN_OUT(x)
+#endif
+
+#define OMAP_AUDIO_NAME "omap-audio"
+#define AUDIO_NBFRAGS_DEFAULT 8
+#define AUDIO_FRAGSIZE_DEFAULT 8192
+
+/* HACK ALERT!: These values will bave to be tuned as this is a trade off b/w
+ * Sampling Rate vs buffer size and delay we are prepared to do before giving up
+ */
+#define MAX_QUEUE_FULL_RETRIES 1000000
+#define QUEUE_WAIT_TIME 10
+
+#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref)
+
+#define SPIN_ADDR (dma_addr_t)0
+#define SPIN_SIZE 2048
+
+/***************************** MODULES SPECIFIC FUNCTION PROTOTYPES ********************/
+
+static int audio_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t * ppos);
+
+static int audio_read(struct file *file, char __user *buffer, size_t count,
+ loff_t * ppos);
+
+static int audio_mmap(struct file *file, struct vm_area_struct *vma);
+
+static unsigned int audio_poll(struct file *file,
+ struct poll_table_struct *wait);
+
+static loff_t audio_llseek(struct file *file, loff_t offset, int origin);
+
+static int audio_ioctl(struct inode *inode, struct file *file, uint cmd,
+ ulong arg);
+
+static int audio_open(struct inode *inode, struct file *file);
+
+static int audio_release(struct inode *inode, struct file *file);
+
+static int audio_probe(struct platform_device *pdev);
+
+static int audio_remove(struct platform_device *pdev);
+
+static void audio_shutdown(struct platform_device *pdev);
+
+static int audio_suspend(struct platform_device *pdev, pm_message_t mesg);
+
+static int audio_resume(struct platform_device *pdev);
+
+static void audio_free(struct device *dev);
+
+/***************************** Data Structures **********************************/
+
+/*
+ * The function pointer set to be registered by the codec.
+ */
+static audio_state_t audio_state = { NULL };
+
+/* DMA Call back function */
+static dma_callback_t audio_dma_callback = NULL;
+
+/* File Ops structure */
+static struct file_operations omap_audio_fops = {
+ .open = audio_open,
+ .release = audio_release,
+ .write = audio_write,
+ .read = audio_read,
+ .mmap = audio_mmap,
+ .poll = audio_poll,
+ .ioctl = audio_ioctl,
+ .llseek = audio_llseek,
+ .owner = THIS_MODULE
+};
+
+/* Driver information */
+static struct platform_driver omap_audio_driver = {
+ .probe = audio_probe,
+ .remove = audio_remove,
+ .suspend = audio_suspend,
+ .shutdown = audio_shutdown,
+ .resume = audio_resume,
+ .driver = {
+ .name = OMAP_AUDIO_NAME,
+ },
+};
+
+/* Device Information */
+static struct platform_device omap_audio_device = {
+ .name = OMAP_AUDIO_NAME,
+ .dev = {
+ .driver_data = &audio_state,
+ .release = audio_free,
+ },
+ .id = 0,
+};
+
+/***************************** GLOBAL FUNCTIONs **********************************/
+
+/* Power Management Functions for Linux Device Model */
+/* DEBUG PUPOSES ONLY! */
+#ifdef CONFIG_PM
+//#undef CONFIG_PM
+#endif
+
+#ifdef CONFIG_PM
+/*********************************************************************************
+ *
+ * audio_ldm_suspend(): Suspend operation
+ *
+ *********************************************************************************/
+static int audio_ldm_suspend(void *data)
+{
+ audio_state_t *state = data;
+
+ FN_IN;
+
+ /*
+ * Reject the suspend request if we are already actively transmitting data
+ * Rationale: We dont want to be suspended while in the middle of a call!
+ */
+ if (AUDIO_ACTIVE(state) && state->hw_init) {
+ printk(KERN_ERR "Audio device Active, Cannot Suspend");
+ return -EPERM;
+#if 0
+ /* NOTE:
+ * This Piece of code is commented out in hope
+ * That one day we would need to suspend the device while
+ * audio operations are in progress and resume the operations
+ * once the resume is done.
+ * This is just a sample implementation of how it could be done.
+ * Currently NOT SUPPORTED
+ */
+ audio_stream_t *is = state->input_stream;
+ audio_stream_t *os = state->output_stream;
+ int stopstate;
+ if (is && is->buffers) {
+ printk("IS Suspend\n");
+ stopstate = is->stopped;
+ audio_stop_dma(is);
+ DMA_CLEAR(is);
+ is->dma_spinref = 0;
+ is->stopped = stopstate;
+ }
+ if (os && os->buffers) {
+ printk("OS Suspend\n");
+ stopstate = os->stopped;
+ audio_stop_dma(os);
+ DMA_CLEAR(os);
+ os->dma_spinref = 0;
+ os->stopped = stopstate;
+ }
+#endif
+ }
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * audio_ldm_resume(): Resume Operations
+ *
+ *********************************************************************************/
+static int audio_ldm_resume(void *data)
+{
+ audio_state_t *state = data;
+
+ FN_IN;
+ if (AUDIO_ACTIVE(state) && state->hw_init) {
+ /* Should never occur - since we never suspend with active state */
+ BUG();
+ return -EPERM;
+#if 0
+ /* NOTE:
+ * This Piece of code is commented out in hope
+ * That one day we would need to suspend the device while
+ * audio operations are in progress and resume the operations
+ * once the resume is done.
+ * This is just a sample implementation of how it could be done.
+ * Currently NOT SUPPORTED
+ */
+ audio_stream_t *is = state->input_stream;
+ audio_stream_t *os = state->output_stream;
+ if (os && os->buffers) {
+ printk("OS Resume\n");
+ audio_reset(os);
+ audio_process_dma(os);
+ }
+ if (is && is->buffers) {
+ printk("IS Resume\n");
+ audio_reset(is);
+ audio_process_dma(is);
+ }
+#endif
+ }
+ FN_OUT(0);
+ return 0;
+}
+#endif /* End of #ifdef CONFIG_PM */
+
+/*********************************************************************************
+ *
+ * audio_free(): The Audio driver release function
+ * This is a dummy function required by the platform driver
+ *
+ *********************************************************************************/
+static void audio_free(struct device *dev)
+{
+ /* Nothing to Release! */
+}
+
+/*********************************************************************************
+ *
+ * audio_probe(): The Audio driver probe function
+ * WARNING!!!! : It is expected that the codec would have registered with us by now
+ *
+ *********************************************************************************/
+static int audio_probe(struct platform_device *pdev)
+{
+ int ret;
+ FN_IN;
+ if (!audio_state.hw_probe) {
+ printk(KERN_ERR "Probe Function Not Registered\n");
+ return -ENODEV;
+ }
+ ret = audio_state.hw_probe();
+ FN_OUT(ret);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * audio_remove() Function to handle removal operations
+ *
+ *********************************************************************************/
+static int audio_remove(struct platform_device *pdev)
+{
+ FN_IN;
+ if (audio_state.hw_remove) {
+ audio_state.hw_remove();
+ }
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * audio_shutdown(): Function to handle shutdown operations
+ *
+ *********************************************************************************/
+static void audio_shutdown(struct platform_device *pdev)
+{
+ FN_IN;
+ if (audio_state.hw_cleanup) {
+ audio_state.hw_cleanup();
+ }
+ FN_OUT(0);
+ return;
+}
+
+/*********************************************************************************
+ *
+ * audio_suspend(): Function to handle suspend operations
+ *
+ *********************************************************************************/
+static int audio_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ int ret = 0;
+
+#ifdef CONFIG_PM
+ void *data = pdev->dev.driver_data;
+ FN_IN;
+ if (audio_state.hw_suspend) {
+ ret = audio_ldm_suspend(data);
+ if (ret == 0)
+ ret = audio_state.hw_suspend();
+ }
+ if (ret) {
+ printk(KERN_INFO "Audio Suspend Failed \n");
+ } else {
+ printk(KERN_INFO "Audio Suspend Success \n");
+ }
+#endif /* CONFIG_PM */
+
+ FN_OUT(ret);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * audio_resume(): Function to handle resume operations
+ *
+ *********************************************************************************/
+static int audio_resume(struct platform_device *pdev)
+{
+ int ret = 0;
+
+#ifdef CONFIG_PM
+ void *data = pdev->dev.driver_data;
+ FN_IN;
+ if (audio_state.hw_resume) {
+ ret = audio_ldm_resume(data);
+ if (ret == 0)
+ ret = audio_state.hw_resume();
+ }
+ if (ret) {
+ printk(KERN_INFO " Audio Resume Failed \n");
+ } else {
+ printk(KERN_INFO " Audio Resume Success \n");
+ }
+#endif /* CONFIG_PM */
+
+ FN_OUT(ret);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * audio_get_fops(): Return the fops required to get the function pointers of
+ * OMAP Audio Driver
+ *
+ *********************************************************************************/
+struct file_operations *audio_get_fops(void)
+{
+ FN_IN;
+ FN_OUT(0);
+ return &omap_audio_fops;
+}
+
+/*********************************************************************************
+ *
+ * audio_register_codec(): Register a Codec fn points using this function
+ * WARNING!!!!! : Codecs should ensure that they do so! no sanity checks
+ * during runtime is done due to obvious performance
+ * penalties.
+ *
+ *********************************************************************************/
+int audio_register_codec(audio_state_t * codec_state)
+{
+ int ret;
+ FN_IN;
+
+ /* We dont handle multiple codecs now */
+ if (audio_state.hw_init) {
+ printk(KERN_ERR " Codec Already registered\n");
+ return -EPERM;
+ }
+
+ /* Grab the dma Callback */
+ audio_dma_callback = audio_get_dma_callback();
+ if (!audio_dma_callback) {
+ printk(KERN_ERR "Unable to get call back function\n");
+ return -EPERM;
+ }
+
+ /* Sanity checks */
+ if (!codec_state) {
+ printk(KERN_ERR "NULL ARGUMENT!\n");
+ return -EPERM;
+ }
+
+ if (!codec_state->hw_probe || !codec_state->hw_init
+ || !codec_state->hw_shutdown || !codec_state->client_ioctl) {
+ printk(KERN_ERR
+ "Required Fn Entry point Missing probe=%p init=%p,down=%p,ioctl=%p!\n",
+ codec_state->hw_probe, codec_state->hw_init,
+ codec_state->hw_shutdown, codec_state->client_ioctl);
+ return -EPERM;
+ }
+
+ memcpy(&audio_state, codec_state, sizeof(audio_state_t));
+ mutex_init(&audio_state.mutex);
+
+ ret = platform_device_register(&omap_audio_device);
+ if (ret != 0) {
+ printk(KERN_ERR "Platform dev_register failed =%d\n", ret);
+ ret = -ENODEV;
+ goto register_out;
+ }
+
+ ret = platform_driver_register(&omap_audio_driver);
+ if (ret != 0) {
+ printk(KERN_ERR "Device Register failed =%d\n", ret);
+ ret = -ENODEV;
+ platform_device_unregister(&omap_audio_device);
+ goto register_out;
+ }
+
+ register_out:
+
+ FN_OUT(ret);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * audio_unregister_codec(): Un-Register a Codec using this function
+ *
+ *********************************************************************************/
+int audio_unregister_codec(audio_state_t * codec_state)
+{
+ FN_IN;
+
+ /* We dont handle multiple codecs now */
+ if (!audio_state.hw_init) {
+ printk(KERN_ERR " No Codec registered\n");
+ return -EPERM;
+ }
+ /* Security check */
+ if (audio_state.hw_init != codec_state->hw_init) {
+ printk(KERN_ERR
+ " Attempt to unregister codec which was not registered with us\n");
+ return -EPERM;
+ }
+
+ platform_driver_unregister(&omap_audio_driver);
+ platform_device_unregister(&omap_audio_device);
+
+ memset(&audio_state, 0, sizeof(audio_state_t));
+
+ FN_OUT(0);
+ return 0;
+}
+
+/***************************** MODULES SPECIFIC FUNCTION *************************/
+
+/*********************************************************************************
+ *
+ * audio_write(): Exposed to write() call
+ *
+ *********************************************************************************/
+static int
+audio_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t * ppos)
+{
+ const char __user *buffer0 = buffer;
+ audio_state_t *state = file->private_data;
+ audio_stream_t *s = state->output_stream;
+ int chunksize, ret = 0;
+
+ DPRINTK("audio_write: count=%d\n", count);
+ if (*ppos != file->f_pos) {
+ printk("FPOS not ppos ppos=0x%x fpos =0x%x\n", (u32) * ppos,
+ (u32) file->f_pos);
+ return -ESPIPE;
+ }
+ if (s->mapped) {
+ printk("s already mapped\n");
+ return -ENXIO;
+ }
+ if (!s->buffers && audio_setup_buf(s)) {
+ printk("NO MEMORY\n");
+ return -ENOMEM;
+ }
+
+ while (count > 0) {
+ audio_buf_t *b = &s->buffers[s->usr_head];
+
+ /* Wait for a buffer to become free */
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ if (!s->wfc.done)
+ break;
+ }
+ ret = -ERESTARTSYS;
+ if (wait_for_completion_interruptible(&s->wfc))
+ break;
+
+ /* Feed the current buffer */
+ chunksize = s->fragsize - b->offset;
+ if (chunksize > count)
+ chunksize = count;
+ DPRINTK("write %d to %d\n", chunksize, s->usr_head);
+ if (copy_from_user(b->data + b->offset, buffer, chunksize)) {
+ printk(KERN_ERR "Audio: CopyFrom User failed \n");
+ complete(&s->wfc);
+ return -EFAULT;
+ }
+
+ buffer += chunksize;
+ count -= chunksize;
+ b->offset += chunksize;
+
+ if (b->offset < s->fragsize) {
+ complete(&s->wfc);
+ break;
+ }
+
+ /* Update pointers and send current fragment to DMA */
+ b->offset = 0;
+ if (++s->usr_head >= s->nbfrags)
+ s->usr_head = 0;
+ /* Add the num of frags pending */
+ s->pending_frags++;
+ s->active = 1;
+
+ audio_process_dma(s);
+
+ }
+
+ if ((buffer - buffer0))
+ ret = buffer - buffer0;
+ DPRINTK("audio_write: return=%d\n", ret);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * audio_read(): Exposed as read() function
+ *
+ *********************************************************************************/
+static int
+audio_read(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
+{
+ char __user *buffer0 = buffer;
+ audio_state_t *state = file->private_data;
+ audio_stream_t *s = state->input_stream;
+ int chunksize, ret = 0;
+ unsigned long flags;
+
+ DPRINTK("audio_read: count=%d\n", count);
+
+ if (*ppos != file->f_pos) {
+ printk("AudioRead - FPOS not ppos ppos=0x%x fpos =0x%x\n",
+ (u32) * ppos, (u32) file->f_pos);
+ return -ESPIPE;
+ }
+ if (s->mapped) {
+ printk("AudioRead - s already mapped\n");
+ return -ENXIO;
+ }
+
+ if (!s->active) {
+ if (!s->buffers && audio_setup_buf(s)) {
+ printk("AudioRead - No Memory\n");
+ return -ENOMEM;
+ }
+ audio_prime_rx(state);
+ }
+
+ while (count > 0) {
+ audio_buf_t *b = &s->buffers[s->usr_head];
+
+ /* Wait for a buffer to become full */
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ if (!s->wfc.done)
+ break;
+ }
+ ret = -ERESTARTSYS;
+ if (wait_for_completion_interruptible(&s->wfc))
+ break;
+
+ /* Grab data from the current buffer */
+ chunksize = s->fragsize - b->offset;
+ if (chunksize > count)
+ chunksize = count;
+ DPRINTK("read %d from %d\n", chunksize, s->usr_head);
+ if (copy_to_user(buffer, b->data + b->offset, chunksize)) {
+ complete(&s->wfc);
+ return -EFAULT;
+ }
+ buffer += chunksize;
+ count -= chunksize;
+ b->offset += chunksize;
+ if (b->offset < s->fragsize) {
+ complete(&s->wfc);
+ break;
+ }
+
+ /* Update pointers and return current fragment to DMA */
+ local_irq_save(flags);
+ b->offset = 0;
+ if (++s->usr_head >= s->nbfrags)
+ s->usr_head = 0;
+
+ s->pending_frags++;
+ local_irq_restore(flags);
+ audio_process_dma(s);
+
+ }
+
+ if ((buffer - buffer0))
+ ret = buffer - buffer0;
+ DPRINTK("audio_read: return=%d\n", ret);
+ return ret;
+}
+
+/*********************************************************************************
+ *
+ * audio_mmap(): Exposed as mmap Function
+ * !!WARNING: Still under development
+ *
+ *********************************************************************************/
+static int audio_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ audio_state_t *state = file->private_data;
+ audio_stream_t *s;
+ unsigned long size, vma_addr;
+ int i, ret;
+
+ FN_IN;
+ if (vma->vm_pgoff != 0)
+ return -EINVAL;
+
+ if (vma->vm_flags & VM_WRITE) {
+ if (!state->wr_ref)
+ return -EINVAL;;
+ s = state->output_stream;
+ } else if (vma->vm_flags & VM_READ) {
+ if (!state->rd_ref)
+ return -EINVAL;
+ s = state->input_stream;
+ } else
+ return -EINVAL;
+
+ if (s->mapped)
+ return -EINVAL;
+ size = vma->vm_end - vma->vm_start;
+ if (size != s->fragsize * s->nbfrags)
+ return -EINVAL;
+ if (!s->buffers && audio_setup_buf(s))
+ return -ENOMEM;
+ vma_addr = vma->vm_start;
+ for (i = 0; i < s->nbfrags; i++) {
+ audio_buf_t *buf = &s->buffers[i];
+ if (!buf->master)
+ continue;
+ ret =
+ remap_pfn_range(vma, vma_addr, buf->dma_addr >> PAGE_SHIFT,
+ buf->master, vma->vm_page_prot);
+ if (ret)
+ return ret;
+ vma_addr += buf->master;
+ }
+ s->mapped = 1;
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * audio_poll(): Exposed as poll function
+ *
+ *********************************************************************************/
+static unsigned int
+audio_poll(struct file *file, struct poll_table_struct *wait)
+{
+ audio_state_t *state = file->private_data;
+ audio_stream_t *is = state->input_stream;
+ audio_stream_t *os = state->output_stream;
+ unsigned int mask = 0;
+
+ DPRINTK("audio_poll(): mode=%s%s\n",
+ (file->f_mode & FMODE_READ) ? "r" : "",
+ (file->f_mode & FMODE_WRITE) ? "w" : "");
+
+ if (file->f_mode & FMODE_READ) {
+ /* Start audio input if not already active */
+ if (!is->active) {
+ if (!is->buffers && audio_setup_buf(is))
+ return -ENOMEM;
+ audio_prime_rx(state);
+ }
+ poll_wait(file, &is->wq, wait);
+ }
+
+ if (file->f_mode & FMODE_WRITE) {
+ if (!os->buffers && audio_setup_buf(os))
+ return -ENOMEM;
+ poll_wait(file, &os->wq, wait);
+ }
+
+ if (file->f_mode & FMODE_READ)
+ if ((is->mapped && is->bytecount > 0) ||
+ (!is->mapped && is->wfc.done > 0))
+ mask |= POLLIN | POLLRDNORM;
+
+ if (file->f_mode & FMODE_WRITE)
+ if ((os->mapped && os->bytecount > 0) ||
+ (!os->mapped && os->wfc.done > 0))
+ mask |= POLLOUT | POLLWRNORM;
+
+ DPRINTK("audio_poll() returned mask of %s%s\n",
+ (mask & POLLIN) ? "r" : "", (mask & POLLOUT) ? "w" : "");
+
+ FN_OUT(mask);
+ return mask;
+}
+
+/*********************************************************************************
+ *
+ * audio_llseek(): Exposed as lseek() function.
+ *
+ *********************************************************************************/
+static loff_t audio_llseek(struct file *file, loff_t offset, int origin)
+{
+ FN_IN;
+ FN_OUT(0);
+ return -ESPIPE;
+}
+
+/*********************************************************************************
+ *
+ * audio_ioctl(): Handles generic ioctls. If there is a request for something this
+ * fn cannot handle, its then given to client specific ioctl routine, that will take
+ * up platform specific requests
+ *
+ *********************************************************************************/
+static int
+audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+ audio_state_t *state = file->private_data;
+ audio_stream_t *os = state->output_stream;
+ audio_stream_t *is = state->input_stream;
+ long val;
+
+ DPRINTK(__FILE__ " audio_ioctl 0x%08x\n", cmd);
+
+ /* dispatch based on command */
+ switch (cmd) {
+ case OSS_GETVERSION:
+ return put_user(SOUND_VERSION, (int __user *)arg);
+
+ case SNDCTL_DSP_GETBLKSIZE:
+ if (file->f_mode & FMODE_WRITE)
+ return put_user(os->fragsize, (int __user *)arg);
+ else
+ return put_user(is->fragsize, (int __user *)arg);
+
+ case SNDCTL_DSP_GETCAPS:
+ val = DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP;
+ if (is && os)
+ val |= DSP_CAP_DUPLEX;
+ FN_OUT(1);
+ return put_user(val, (int __user *)arg);
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ if (get_user(val, (long __user *)arg)) {
+ FN_OUT(2);
+ return -EFAULT;
+ }
+ if (file->f_mode & FMODE_READ) {
+ int ret = audio_set_fragments(is, val);
+ if (ret < 0) {
+ FN_OUT(3);
+ return ret;
+ }
+ ret = put_user(ret, (int __user *)arg);
+ if (ret) {
+ FN_OUT(4);
+ return ret;
+ }
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ int ret = audio_set_fragments(os, val);
+ if (ret < 0) {
+ FN_OUT(5);
+ return ret;
+ }
+ ret = put_user(ret, (int __user *)arg);
+ if (ret) {
+ FN_OUT(6);
+ return ret;
+ }
+ }
+ FN_OUT(7);
+ return 0;
+
+ case SNDCTL_DSP_SYNC:
+ FN_OUT(8);
+ return audio_sync(file);
+
+ case SNDCTL_DSP_SETDUPLEX:
+ FN_OUT(9);
+ return 0;
+
+ case SNDCTL_DSP_POST:
+ FN_OUT(10);
+ return 0;
+
+ case SNDCTL_DSP_GETTRIGGER:
+ val = 0;
+ if (file->f_mode & FMODE_READ && is->active && !is->stopped)
+ val |= PCM_ENABLE_INPUT;
+ if (file->f_mode & FMODE_WRITE && os->active && !os->stopped)
+ val |= PCM_ENABLE_OUTPUT;
+ FN_OUT(11);
+ return put_user(val, (int __user *)arg);
+
+ case SNDCTL_DSP_SETTRIGGER:
+ if (get_user(val, (int __user *)arg)) {
+ FN_OUT(12);
+ return -EFAULT;
+ }
+ if (file->f_mode & FMODE_READ) {
+ if (val & PCM_ENABLE_INPUT) {
+ unsigned long flags;
+ if (!is->active) {
+ if (!is->buffers && audio_setup_buf(is)) {
+ FN_OUT(13);
+ return -ENOMEM;
+ }
+ audio_prime_rx(state);
+ }
+ local_irq_save(flags);
+ is->stopped = 0;
+ local_irq_restore(flags);
+ audio_process_dma(is);
+
+ } else {
+ audio_stop_dma(is);
+ }
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ if (val & PCM_ENABLE_OUTPUT) {
+ unsigned long flags;
+ if (!os->buffers && audio_setup_buf(os)) {
+ FN_OUT(14);
+ return -ENOMEM;
+ }
+ local_irq_save(flags);
+ if (os->mapped && !os->pending_frags) {
+ os->pending_frags = os->nbfrags;
+ init_completion(&os->wfc);
+ os->wfc.done = 0;
+ os->active = 1;
+ }
+ os->stopped = 0;
+ local_irq_restore(flags);
+ audio_process_dma(os);
+
+ } else {
+ audio_stop_dma(os);
+ }
+ }
+ FN_OUT(15);
+ return 0;
+
+ case SNDCTL_DSP_GETOPTR:
+ case SNDCTL_DSP_GETIPTR:
+ {
+ count_info inf = { 0, };
+ audio_stream_t *s =
+ (cmd == SNDCTL_DSP_GETOPTR) ? os : is;
+ int bytecount, offset;
+ unsigned long flags;
+
+ if ((s == is && !(file->f_mode & FMODE_READ)) ||
+ (s == os && !(file->f_mode & FMODE_WRITE))) {
+ FN_OUT(16);
+ return -EINVAL;
+ }
+ if (s->active) {
+ local_irq_save(flags);
+ offset = audio_get_dma_pos(s);
+ inf.ptr = s->dma_tail * s->fragsize + offset;
+ bytecount = s->bytecount + offset;
+ s->bytecount = -offset;
+ inf.blocks = s->fragcount;
+ s->fragcount = 0;
+ local_irq_restore(flags);
+ if (bytecount < 0)
+ bytecount = 0;
+ inf.bytes = bytecount;
+ }
+ FN_OUT(17);
+ return copy_to_user((void __user *)arg, &inf, sizeof(inf));
+ }
+
+ case SNDCTL_DSP_GETOSPACE:
+ case SNDCTL_DSP_GETISPACE:
+ {
+ audio_buf_info inf = { 0, };
+ audio_stream_t *s =
+ (cmd == SNDCTL_DSP_GETOSPACE) ? os : is;
+
+ if ((s == is && !(file->f_mode & FMODE_READ)) ||
+ (s == os && !(file->f_mode & FMODE_WRITE))) {
+ FN_OUT(18);
+ return -EINVAL;
+ }
+ if (!s->buffers && audio_setup_buf(s)) {
+ FN_OUT(19);
+ return -ENOMEM;
+ }
+ inf.bytes = s->wfc.done * s->fragsize;
+
+ inf.fragments = inf.bytes / s->fragsize;
+ inf.fragsize = s->fragsize;
+ inf.fragstotal = s->nbfrags;
+ FN_OUT(20);
+ return copy_to_user((void __user *)arg, &inf, sizeof(inf));
+ }
+
+ case SNDCTL_DSP_NONBLOCK:
+ file->f_flags |= O_NONBLOCK;
+ FN_OUT(21);
+ return 0;
+
+ case SNDCTL_DSP_RESET:
+ if (file->f_mode & FMODE_READ) {
+ audio_reset(is);
+ if (state->need_tx_for_rx) {
+ unsigned long flags;
+ local_irq_save(flags);
+ os->spin_idle = 0;
+ local_irq_restore(flags);
+ }
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ audio_reset(os);
+ }
+ FN_OUT(22);
+ return 0;
+
+ default:
+ /*
+ * Let the client of this module handle the
+ * non generic ioctls
+ */
+ FN_OUT(23);
+ return state->client_ioctl(inode, file, cmd, arg);
+ }
+
+ FN_OUT(0);
+ return 0;
+}
+
+/*********************************************************************************
+ *
+ * audio_open(): Exposed as open() function
+ *
+ *********************************************************************************/
+static int audio_open(struct inode *inode, struct file *file)
+{
+ audio_state_t *state = (&audio_state);
+ audio_stream_t *os = state->output_stream;
+ audio_stream_t *is = state->input_stream;
+ int err, need_tx_dma;
+ static unsigned char tsc2101_init_flag = 0;
+
+ FN_IN;
+
+ /* Lock the module */
+ if (!try_module_get(THIS_MODULE)) {
+ printk(KERN_CRIT "Failed to get module\n");
+ return -ESTALE;
+ }
+ /* Lock the codec module */
+ if (!try_module_get(state->owner)) {
+ printk(KERN_CRIT "Failed to get codec module\n");
+ module_put(THIS_MODULE);
+ return -ESTALE;
+ }
+
+ mutex_lock(&state->mutex);
+
+ /* access control */
+ err = -ENODEV;
+ if ((file->f_mode & FMODE_WRITE) && !os)
+ goto out;
+ if ((file->f_mode & FMODE_READ) && !is)
+ goto out;
+ err = -EBUSY;
+ if ((file->f_mode & FMODE_WRITE) && state->wr_ref)
+ goto out;
+ if ((file->f_mode & FMODE_READ) && state->rd_ref)
+ goto out;
+ err = -EINVAL;
+ if ((file->f_mode & FMODE_READ) && state->need_tx_for_rx && !os)
+ goto out;
+
+ /* request DMA channels */
+ need_tx_dma = ((file->f_mode & FMODE_WRITE) ||
+ ((file->f_mode & FMODE_READ) && state->need_tx_for_rx));
+ if (state->wr_ref || (state->rd_ref && state->need_tx_for_rx))
+ need_tx_dma = 0;
+ if (need_tx_dma) {
+ DMA_REQUEST(err, os, audio_dma_callback);
+ if (err < 0)
+ goto out;
+ }
+ if (file->f_mode & FMODE_READ) {
+ DMA_REQUEST(err, is, audio_dma_callback);
+ if (err < 0) {
+ if (need_tx_dma)
+ DMA_FREE(os);
+ goto out;
+ }
+ }
+
+ /* now complete initialisation */
+ if (!AUDIO_ACTIVE(state)) {
+ if (state->hw_init && !tsc2101_init_flag) {
+ state->hw_init(state->data);
+ tsc2101_init_flag = 0;
+
+ }
+
+ }
+
+ if ((file->f_mode & FMODE_WRITE)) {
+ state->wr_ref = 1;
+ audio_reset(os);
+ os->fragsize = AUDIO_FRAGSIZE_DEFAULT;
+ os->nbfrags = AUDIO_NBFRAGS_DEFAULT;
+ os->mapped = 0;
+ init_waitqueue_head(&os->wq);
+ }
+
+ if (file->f_mode & FMODE_READ) {
+ state->rd_ref = 1;
+ audio_reset(is);
+ is->fragsize = AUDIO_FRAGSIZE_DEFAULT;
+ is->nbfrags = AUDIO_NBFRAGS_DEFAULT;
+ is->mapped = 0;
+ init_waitqueue_head(&is->wq);
+ }
+
+ file->private_data = state;
+ err = 0;
+
+ out:
+ mutex_unlock(&state->mutex);
+ if (err) {
+ module_put(state->owner);
+ module_put(THIS_MODULE);
+ }
+ FN_OUT(err);
+ return err;
+}
+
+/*********************************************************************************
+ *
+ * audio_release(): Exposed as release function()
+ *
+ *********************************************************************************/
+static int audio_release(struct inode *inode, struct file *file)
+{
+ audio_state_t *state = file->private_data;
+ audio_stream_t *os = state->output_stream;
+ audio_stream_t *is = state->input_stream;
+
+ FN_IN;
+
+ mutex_lock(&state->mutex);
+
+ if (file->f_mode & FMODE_READ) {
+ audio_discard_buf(is);
+ DMA_FREE(is);
+ is->dma_spinref = 0;
+ if (state->need_tx_for_rx) {
+ os->spin_idle = 0;
+ if (!state->wr_ref) {
+ DMA_FREE(os);
+ os->dma_spinref = 0;
+ }
+ }
+ state->rd_ref = 0;
+ }
+
+ if (file->f_mode & FMODE_WRITE) {
+ audio_sync(file);
+ audio_discard_buf(os);
+ if (!state->need_tx_for_rx || !state->rd_ref) {
+ DMA_FREE(os);
+ os->dma_spinref = 0;
+ }
+ state->wr_ref = 0;
+ }
+
+ if (!AUDIO_ACTIVE(state)) {
+ if (state->hw_shutdown)
+ state->hw_shutdown(state->data);
+ }
+
+ mutex_unlock(&state->mutex);
+
+ module_put(state->owner);
+ module_put(THIS_MODULE);
+
+ FN_OUT(0);
+ return 0;
+}
+
+EXPORT_SYMBOL(audio_register_codec);
+EXPORT_SYMBOL(audio_unregister_codec);
+EXPORT_SYMBOL(audio_get_fops);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("Common audio handling for OMAP processors");
+MODULE_LICENSE("GPL");
--- /dev/null
- #include <asm/arch/dma.h>
+/*
+ * linux/sound/oss/omap-audio.h
+ *
+ * Common audio handling for the OMAP processors
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
+ *
+ * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History
+ * -------
+ * 2004/08/12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms
+ *
+ * 2004/04/04 Nishanth menon - Added hooks for power management
+ *
+ * 2005/12/10 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu
+ */
+
+#ifndef __OMAP_AUDIO_H
+#define __OMAP_AUDIO_H
+
+/* Requires dma.h */
++#include <mach/dma.h>
+
+/*
+ * Buffer Management
+ */
+typedef struct {
+ int offset; /* current offset */
+ char *data; /* points to actual buffer */
+ dma_addr_t dma_addr; /* physical buffer address */
+ int dma_ref; /* DMA refcount */
+ int master; /* owner for buffer allocation, contain size when true */
+} audio_buf_t;
+
+/*
+ * Structure describing the data stream related information
+ */
+typedef struct {
+ char *id; /* identification string */
+ audio_buf_t *buffers; /* pointer to audio buffer structures */
+ u_int usr_head; /* user fragment index */
+ u_int dma_head; /* DMA fragment index to go */
+ u_int dma_tail; /* DMA fragment index to complete */
+ u_int fragsize; /* fragment i.e. buffer size */
+ u_int nbfrags; /* nbr of fragments i.e. buffers */
+ u_int pending_frags; /* Fragments sent to DMA */
+ int dma_dev; /* device identifier for DMA */
+
+#ifdef OMAP_DMA_CHAINING_SUPPORT
+ lch_chain *dma_chain;
+ dma_regs_t *dma_regs; /* points to our DMA registers */
+#else
+ char started; /* to store if the chain was started or not */
+ int dma_q_head; /* DMA Channel Q Head */
+ int dma_q_tail; /* DMA Channel Q Tail */
+ char dma_q_count; /* DMA Channel Q Count */
+ char in_use; /* Is this is use? */
+ int *lch; /* Chain of channels this stream is linked to */
+#endif
+ int input_or_output; /* Direction of this data stream */
+ int bytecount; /* nbr of processed bytes */
+ int fragcount; /* nbr of fragment transitions */
+ struct completion wfc; /* wait for "nbfrags" fragment completion */
+ wait_queue_head_t wq; /* for poll */
+ int dma_spinref; /* DMA is spinning */
+ unsigned mapped:1; /* mmap()'ed buffers */
+ unsigned active:1; /* actually in progress */
+ unsigned stopped:1; /* might be active but stopped */
+ unsigned spin_idle:1; /* have DMA spin on zeros when idle */
+ unsigned linked:1; /* dma channels linked */
+ int (*hw_start)(void); /* interface to start HW interface, e.g. McBSP */
+ int (*hw_stop)(void); /* interface to stop HW interface, e.g. McBSP */
+} audio_stream_t;
+
+/*
+ * State structure for one instance
+ */
+typedef struct {
+ struct module *owner; /* Codec module ID */
+ audio_stream_t *output_stream;
+ audio_stream_t *input_stream;
+ unsigned rd_ref:1; /* open reference for recording */
+ unsigned wr_ref:1; /* open reference for playback */
+ unsigned need_tx_for_rx:1; /* if data must be sent while receiving */
+ void *data;
+ void (*hw_init) (void *);
+ void (*hw_shutdown) (void *);
+ int (*client_ioctl) (struct inode *, struct file *, uint, ulong);
+ int (*hw_probe) (void);
+ void (*hw_remove) (void);
+ void (*hw_cleanup) (void);
+ int (*hw_suspend) (void);
+ int (*hw_resume) (void);
+ struct pm_dev *pm_dev;
+ struct mutex mutex; /* to protect against races in attach() */
+} audio_state_t;
+
+#ifdef AUDIO_PM
+void audio_ldm_suspend(void *data);
+
+void audio_ldm_resume(void *data);
+
+#endif
+
+/* Register a Codec using this function */
+extern int audio_register_codec(audio_state_t * codec_state);
+/* Un-Register a Codec using this function */
+extern int audio_unregister_codec(audio_state_t * codec_state);
+/* Function to provide fops of omap audio driver */
+extern struct file_operations *audio_get_fops(void);
+/* Function to initialize the device info for audio driver */
+extern int audio_dev_init(void);
+/* Function to un-initialize the device info for audio driver */
+void audio_dev_uninit(void);
+
+#endif /* End of #ifndef __OMAP_AUDIO_H */