Commit cc8406d5 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] sh: VoyagerGX companion chip support.

From: Paul Mundt <lethal@Linux-SH.ORG>

This adds support for the SM501/VoyagerGX companion chip, used by the
RTS7751R2D.
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 475552f5
menu "Companion Chips" 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 # A board must have defined HD6446X_SERIES in order to see these
config HD6446X_SERIES config HD6446X_SERIES
bool "HD6446x support" bool "HD6446x support"
......
#
# Makefile for VoyagerGX
#
obj-y := irq.o setup.o
obj-$(CONFIG_USB_OHCI_HCD) += consistent.o
/*
* 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 <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/bus-sh.h>
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);
/* -------------------------------------------------------------------- */
/* 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 <linux/config.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/rts7751r2d/rts7751r2d.h>
#include <asm/rts7751r2d/voyagergx_reg.h>
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<VOYAGER_IRQ_NUM; i++) {
flag = 0;
switch (VOYAGER_IRQ_BASE + i) {
case VOYAGER_USBH_IRQ:
case VOYAGER_8051_IRQ:
case VOYAGER_UART0_IRQ:
case VOYAGER_UART1_IRQ:
case VOYAGER_AC97_IRQ:
flag = 1;
}
if (flag == 1)
irq_desc[VOYAGER_IRQ_BASE + i].handler = &voyagergx_irq_type;
}
setup_irq(IRQ_VOYAGER, &irq0);
}
/*
* arch/sh/cchips/voyagergx/setup.c
*
* Setup routines for VoyagerGX cchip.
*
* Copyright (C) 2003 Lineo uSolutions, 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/rts7751r2d/voyagergx_reg.h>
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);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment