From: Paul Mundt This adds support for the SM501/VoyagerGX companion chip, used by the RTS7751R2D. Signed-off-by: Paul Mundt Signed-off-by: Andrew Morton --- 25-akpm/arch/sh/cchips/Kconfig | 13 + 25-akpm/arch/sh/cchips/voyagergx/Makefile | 8 + 25-akpm/arch/sh/cchips/voyagergx/consistent.c | 126 ++++++++++++++++ 25-akpm/arch/sh/cchips/voyagergx/irq.c | 194 ++++++++++++++++++++++++++ 25-akpm/arch/sh/cchips/voyagergx/setup.c | 37 ++++ 5 files changed, 378 insertions(+) diff -puN arch/sh/cchips/Kconfig~sh-voyagergx-companion-chip-support arch/sh/cchips/Kconfig --- 25/arch/sh/cchips/Kconfig~sh-voyagergx-companion-chip-support 2004-06-23 20:00:20.439582736 -0700 +++ 25-akpm/arch/sh/cchips/Kconfig 2004-06-23 20:00:20.443582128 -0700 @@ -1,5 +1,18 @@ menu "Companion Chips" +config VOYAGERGX + bool "VoyagerGX chip support" + depends on SH_RTS7751R2D + help + Selecting this option will support Silicon Motion, Inc. SM501. + Designed to complement needs for the embedded industry, it + provides video and 2D capability. To reduce system cost a + wide variety of include I/O is supported, including analog RGB + and digital LCD Panel interface, 8-bit parallel interface, USB, + UART, IrDA, Zoom Video, AC97 or I2S, SSP, PWM, and I2C. There + are additional GPIO bits that can be used to interface to + external as well. + # A board must have defined HD6446X_SERIES in order to see these config HD6446X_SERIES bool "HD6446x support" diff -puN /dev/null arch/sh/cchips/voyagergx/consistent.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/arch/sh/cchips/voyagergx/consistent.c 2004-06-23 20:00:20.444581976 -0700 @@ -0,0 +1,126 @@ +/* + * arch/sh/cchips/voyagergx/consistent.c + * + * Copyright (C) 2004 Paul Mundt + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +struct voya_alloc_entry { + struct list_head list; + unsigned long ofs; + unsigned long len; +}; + +static spinlock_t voya_list_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(voya_alloc_list); + +#define OHCI_SRAM_START 0xb0000000 +#define OHCI_HCCA_SIZE 0x100 +#define OHCI_SRAM_SIZE 0x10000 + +void *voyagergx_consistent_alloc(struct device *dev, size_t size, + dma_addr_t *handle, int flag) +{ + struct list_head *list = &voya_alloc_list; + struct voya_alloc_entry *entry; + struct sh_dev *shdev = to_sh_dev(dev); + unsigned long start, end; + unsigned long flags; + + /* + * The SM501 contains an integrated 8051 with its own SRAM. + * Devices within the cchip can all hook into the 8051 SRAM. + * We presently use this for the OHCI. + * + * Everything else goes through consistent_alloc(). + */ + if (!dev || dev->bus != &sh_bus_types[SH_BUS_VIRT] || + (dev->bus == &sh_bus_types[SH_BUS_VIRT] && + shdev->dev_id != SH_DEV_ID_USB_OHCI)) + return consistent_alloc(flag, size, handle); + + start = OHCI_SRAM_START + OHCI_HCCA_SIZE; + + entry = kmalloc(sizeof(struct voya_alloc_entry), GFP_ATOMIC); + if (!entry) + return NULL; + + entry->len = (size + 15) & ~15; + + /* + * The basis for this allocator is dwmw2's malloc.. the + * Matrox allocator :-) + */ + spin_lock_irqsave(&voya_list_lock, flags); + list_for_each(list, &voya_alloc_list) { + struct voya_alloc_entry *p; + + p = list_entry(list, struct voya_alloc_entry, list); + + if (p->ofs - start >= size) + goto out; + + start = p->ofs + p->len; + } + + end = start + (OHCI_SRAM_SIZE - OHCI_HCCA_SIZE); + list = &voya_alloc_list; + + if (end - start >= size) { +out: + entry->ofs = start; + list_add_tail(&entry->list, list); + spin_unlock_irqrestore(&voya_list_lock, flags); + + *handle = start; + return (void *)start; + } + + kfree(entry); + spin_unlock_irqrestore(&voya_list_lock, flags); + + return NULL; +} + +void voyagergx_consistent_free(struct device *dev, size_t size, + void *vaddr, dma_addr_t handle) +{ + struct voya_alloc_entry *entry; + struct sh_dev *shdev = to_sh_dev(dev); + unsigned long flags; + + if (!dev || dev->bus != &sh_bus_types[SH_BUS_VIRT] || + (dev->bus == &sh_bus_types[SH_BUS_VIRT] && + shdev->dev_id != SH_DEV_ID_USB_OHCI)) { + consistent_free(vaddr, size); + return; + } + + spin_lock_irqsave(&voya_list_lock, flags); + list_for_each_entry(entry, &voya_alloc_list, list) { + if (entry->ofs != handle) + continue; + + list_del(&entry->list); + kfree(entry); + + break; + } + spin_unlock_irqrestore(&voya_list_lock, flags); +} + +EXPORT_SYMBOL(voyagergx_consistent_alloc); +EXPORT_SYMBOL(voyagergx_consistent_free); + diff -puN /dev/null arch/sh/cchips/voyagergx/irq.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/arch/sh/cchips/voyagergx/irq.c 2004-06-23 20:00:20.446581672 -0700 @@ -0,0 +1,194 @@ +/* -------------------------------------------------------------------- */ +/* setup_voyagergx.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., 675 Mass Ave, Cambridge, MA 02139, USA. + + Copyright 2003 (c) Lineo uSolutions,Inc. +*/ +/* -------------------------------------------------------------------- */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static void disable_voyagergx_irq(unsigned int irq) +{ + unsigned long flags, val; + unsigned long mask = 1 << (irq - VOYAGER_IRQ_BASE); + + pr_debug("disable_voyagergx_irq(%d): mask=%x\n", irq, mask); + local_irq_save(flags); + val = inl(VOYAGER_INT_MASK); + val &= ~mask; + outl(val, VOYAGER_INT_MASK); + local_irq_restore(flags); +} + + +static void enable_voyagergx_irq(unsigned int irq) +{ + unsigned long flags, val; + unsigned long mask = 1 << (irq - VOYAGER_IRQ_BASE); + + pr_debug("disable_voyagergx_irq(%d): mask=%x\n", irq, mask); + local_irq_save(flags); + val = inl(VOYAGER_INT_MASK); + val |= mask; + outl(val, VOYAGER_INT_MASK); + local_irq_restore(flags); +} + + +static void mask_and_ack_voyagergx(unsigned int irq) +{ + disable_voyagergx_irq(irq); +} + +static void end_voyagergx_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_voyagergx_irq(irq); +} + +static unsigned int startup_voyagergx_irq(unsigned int irq) +{ + enable_voyagergx_irq(irq); + return 0; +} + +static void shutdown_voyagergx_irq(unsigned int irq) +{ + disable_voyagergx_irq(irq); +} + +static struct hw_interrupt_type voyagergx_irq_type = { + "VOYAGERGX-IRQ", + startup_voyagergx_irq, + shutdown_voyagergx_irq, + enable_voyagergx_irq, + disable_voyagergx_irq, + mask_and_ack_voyagergx, + end_voyagergx_irq, +}; + +static irqreturn_t voyagergx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + printk(KERN_INFO + "VoyagerGX: spurious interrupt, status: 0x%x\n", + inl(INT_STATUS)); + return IRQ_HANDLED; +} + + +/*====================================================*/ + +static struct { + int (*func)(int, void *); + void *dev; +} voyagergx_demux[VOYAGER_IRQ_NUM]; + +void voyagergx_register_irq_demux(int irq, + int (*demux)(int irq, void *dev), void *dev) +{ + voyagergx_demux[irq - VOYAGER_IRQ_BASE].func = demux; + voyagergx_demux[irq - VOYAGER_IRQ_BASE].dev = dev; +} + +void voyagergx_unregister_irq_demux(int irq) +{ + voyagergx_demux[irq - VOYAGER_IRQ_BASE].func = 0; +} + +int voyagergx_irq_demux(int irq) +{ + + if (irq == IRQ_VOYAGER ) { + unsigned long i = 0, bit __attribute__ ((unused)); + unsigned long val = inl(INT_STATUS); +#if 1 + if ( val & ( 1 << 1 )){ + i = 1; + } else if ( val & ( 1 << 2 )){ + i = 2; + } else if ( val & ( 1 << 6 )){ + i = 6; + } else if( val & ( 1 << 10 )){ + i = 10; + } else if( val & ( 1 << 11 )){ + i = 11; + } else if( val & ( 1 << 12 )){ + i = 12; + } else if( val & ( 1 << 17 )){ + i = 17; + } else { + printk("Unexpected IRQ irq = %d status = 0x%08lx\n", irq, val); + } + pr_debug("voyagergx_irq_demux %d \n", i); +#else + for (bit = 1, i = 0 ; i < VOYAGER_IRQ_NUM ; bit <<= 1, i++) + if (val & bit) + break; +#endif + if (i < VOYAGER_IRQ_NUM) { + irq = VOYAGER_IRQ_BASE + i; + if (voyagergx_demux[i].func != 0) + irq = voyagergx_demux[i].func(irq, voyagergx_demux[i].dev); + } + } + return irq; +} + +static struct irqaction irq0 = { voyagergx_interrupt, SA_INTERRUPT, 0, "VOYAGERGX", NULL, NULL}; + +void __init setup_voyagergx_irq(void) +{ + int i, flag; + + printk(KERN_INFO "VoyagerGX configured at 0x%x on irq %d(mapped into %d to %d)\n", + VOYAGER_BASE, + IRQ_VOYAGER, + VOYAGER_IRQ_BASE, + VOYAGER_IRQ_BASE + VOYAGER_IRQ_NUM - 1); + + for (i=0; i +#include +#include +#include + +static int __init setup_voyagergx(void) +{ + unsigned long val; + + val = inl(DRAM_CTRL); + val |= (DRAM_CTRL_CPU_COLUMN_SIZE_256 | + DRAM_CTRL_CPU_ACTIVE_PRECHARGE | + DRAM_CTRL_CPU_RESET | + DRAM_CTRL_REFRESH_COMMAND | + DRAM_CTRL_BLOCK_WRITE_TIME | + DRAM_CTRL_BLOCK_WRITE_PRECHARGE | + DRAM_CTRL_ACTIVE_PRECHARGE | + DRAM_CTRL_RESET | + DRAM_CTRL_REMAIN_ACTIVE); + outl(val, DRAM_CTRL); + + return 0; +} + +module_init(setup_voyagergx); _