Copy Link
Add to Bookmark
Report
Dreamcast GD-ROM device driver
GD-ROM device driver for LinuxSH Project (http://linuxsh.sourceforge.net/) CVS kernel based on NetBSD/Dreamcast
- drivers/dreamcast/gdrom.c device driver
- arch/sh/kernel/setup_dc.c modify to support GD-ROM interrupt
supports:
- normal MODE1 CD-ROM
- multi session MODE2 CDROM (Dreamcast bootable CD)
- GD-ROM
by default, mount GD-ROM of the area (JAP/US/PAL) of the used GD-ROM.
To mount the GD-ROM type
mount -t iso9660 -o session=0 /dev/gdrom /mnt
Major: 250
Minor: 0
To make entry,
cd <dcroot>/dev
mknod gdrom b 250 0
gdrom.c
/*
DreamCast GD-ROM driver
port from NetBSD/Dreamcast
modified BERO for linux
*/
/* $NetBSD: gdrom.c,v 1.4 2001/04/24 19:43:25 marcus Exp $ */
/*-
* Copyright (c) 2001 Marcus Comstedt
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Marcus Comstedt.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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/config.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/cdrom.h>
#include <linux/ioport.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/wait.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#define GDROM_MAJOR 250
#define MAJOR_NR GDROM_MAJOR
#define DEVICE_NAME "Dreamcast GD-ROM"
/* #define DEVICE_INTR do_gdrom */
#define DEVICE_REQUEST do_gdrom_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_STR "gdrom"
#include <linux/blk.h>
static int gdrom_blocksizes[1] = {2048};
static unsigned char gdrom_buf[2048]; /* buffer for block size conversion */
static int gdrom_bn = -1;
static unsigned long gdrom_first;
static int gdrom_media_changed;
#define gdrom_irq 9
#if 1
#define SPLBIO(s)
#define splx(s)
#else
#define SPLBIO(s) save_flags(s),cli()
#define splx(s) restore_flags(s)
#endif
#define bzero(ptr,size) memset(ptr,0,size)
#define printf printk
#define SET_TIMER(func, jifs) \
((mod_timer(&gdrom_timer, jiffies + jifs)), \
(gdrom_timer.function = func))
#define CLEAR_TIMER del_timer_sync(&gdrom_timer)
static struct timer_list gdrom_timer;
struct gdrom_softc {
int is_open, is_busy;
int openpart_start; /* start sector of currently open partition */
int cmd_active;
void *cmd_result_buf; /* where to store result data (16 bit aligned) */
int cmd_result_size; /* number of bytes allocated for buf */
int cmd_actual; /* number of bytes actually read */
int cmd_cond; /* resulting condition of command */
};
static DECLARE_WAIT_QUEUE_HEAD(is_busy_wait);
static DECLARE_WAIT_QUEUE_HEAD(cmd_active_wait);
static struct gdrom_softc _sc[1];
struct gd_toc {
unsigned int entry[99];
unsigned int first, last;
unsigned int leadout;
};
static struct gd_toc toc;
#define TOC_LBA(n) ((n)&0xffffff00)
#define TOC_ADR(n) ((n)&0x0f)
#define TOC_CTRL(n) (((n)&0xf0)>>4)
#define TOC_TRACK(n) (((n)&0x0000ff00)>>8)
#define GDROM(o) (*(volatile unsigned char *)(0xa05f7000+(o)))
#define GDSTATSTAT(n) ((n)&0xf)
#define GDSTATDISK(n) (((n)>>4)&0xf)
#define GDROM_BUSY GDROM(0x18)
#define GDROM_DATA (*(volatile short *)&GDROM(0x80))
#define GDROM_REGX GDROM(0x84)
#define GDROM_STAT GDROM(0x8c)
#define GDROM_CNTLO GDROM(0x90)
#define GDROM_CNTHI GDROM(0x94)
#define GDROM_COND GDROM(0x9c)
/*
NO_DISC 2
DISC_CHG 6
The status reply is two 32-bit numbers, not 5 8-bit numbers. Anyway:
1-st value:
0 - checking disk type
1 - ready
2-5 - busy (executing command)
6 - lid open
7 - lid closed, but no disk present
I'm not 100% sure about the codes 2-5.
2-nd value (only bits 4-7 are actually used):
0x00 - audio CD
0x10 - CD-ROM
0x20 - CD/XA
0x30 - CD-I
0x80 - GD-ROM
*/
#define __P(args) args
static int gdrom_getstat __P((void));
static int gdrom_do_command __P((struct gdrom_softc *sc, void *req, void *buf, unsigned int nbyt));
static int gdrom_command_sense __P((struct gdrom_softc *sc, void *req, void *buf, unsigned int nbyt));
static int gdrom_read_toc __P((struct gdrom_softc *sc, struct gd_toc *toc,int session));
static int gdrom_read_sectors __P((struct gdrom_softc *sc, void *buf, int sector, int cnt));
static int gdrom_mount_disk __P((struct gdrom_softc *sc));
static int gdrom_intr __P((void* arg));
static int gdrom_getstat()
{
int s1, s2, s3;
if(GDROM_BUSY & 0x80) return -1;
s1 = GDROM_STAT;
s2 = GDROM_STAT;
s3 = GDROM_STAT;
if(GDROM_BUSY & 0x80) return -1;
if(s1 == s2)
return s1;
else if(s2 == s3)
return s2;
else
return -1;
}
static int
gdrom_intr(arg)
void *arg;
{
struct gdrom_softc *sc = arg;
int s, cond;
SPLBIO(s);
cond = GDROM_COND;
#ifdef GDROMDEBUG
printf("GDROM: cond = %x\n", cond);
#endif
if(!sc->cmd_active) {
#ifdef GDROMDEBUG
printf("GDROM: inactive IRQ!?\n");
#endif
splx(s);
return 0;
}
if((cond & 8)) {
int cnt = (GDROM_CNTHI<<8) | GDROM_CNTLO;
#ifdef GDROMDEBUG
printf("GDROM: cnt = %d\n", cnt);
#endif
sc->cmd_actual += cnt;
if(cnt > 0 && sc->cmd_result_size > 0) {
int subcnt = (cnt > sc->cmd_result_size?
sc->cmd_result_size : cnt);
int16_t *ptr = sc->cmd_result_buf;
sc->cmd_result_buf = ((char *)sc->cmd_result_buf)+subcnt;
sc->cmd_result_size -= subcnt;
cnt -= subcnt;
while(subcnt > 0) {
*ptr++ = GDROM_DATA;
subcnt -= 2;
}
}
while(cnt > 0) {
__volatile int16_t tmp;
tmp = GDROM_DATA;
cnt -= 2;
}
}
while( GDROM_BUSY & 0x80 );
if(!(cond & 8)) {
sc->cmd_cond = cond;
sc->cmd_active = 0;
wake_up(&cmd_active_wait);
}
splx(s);
return 0;
}
static int gdrom_do_command(sc, req, buf, nbyt)
struct gdrom_softc *sc;
void *req;
void *buf;
unsigned int nbyt;
{
int i, s;
short *ptr = req;
while( GDROM_BUSY & 0x88 ) ;
if(buf != NULL) {
GDROM_CNTLO = nbyt & 0xff;
GDROM_CNTHI = (nbyt >> 8) & 0xff;
GDROM_REGX = 0;
}
sc->cmd_result_buf = buf;
sc->cmd_result_size = nbyt;
if(GDSTATSTAT(GDROM_STAT) == 6)
return (-1);
GDROM_COND = 0xa0;
for(i = 0; i < 64; i++) ;
while( (GDROM_BUSY & 0x88) != 8 ) ;
save_flags(s);
cli();
sc->cmd_actual = 0;
sc->cmd_active = 1;
for(i = 0; i< 6; i++)
GDROM_DATA = ptr[i];
while(sc->cmd_active)
sleep_on(&cmd_active_wait);
restore_flags(s);
return sc->cmd_cond;
}
static int gdrom_command_sense(sc, req, buf, nbyt)
struct gdrom_softc *sc;
void *req;
void *buf;
unsigned int nbyt;
{
/* 76543210 76543210
0 0x13 -
2 - bufsz(hi)
4 bufsz(lo) -
6 - -
8 - -
10 - - */
unsigned short sense_data[5];
unsigned char cmd[12];
int sense_key, sense_specific;
int cond = gdrom_do_command(sc, req, buf, nbyt);
if(cond < 0) {
#ifdef GDROMDEBUG
printf("GDROM: not ready (2:58)\n");
#endif
return EIO;
}
if(!(cond & 1)) {
#ifdef GDROMDEBUG
printf("GDROM: no sense. 0:0\n");
#endif
return (0);
}
bzero(cmd, sizeof(cmd));
cmd[0] = 0x13;
cmd[4] = sizeof(sense_data);
gdrom_do_command(sc, cmd, sense_data, sizeof(sense_data));
sense_key = sense_data[1] & 0xf;
sense_specific = sense_data[4];
if(sense_key == 11 && sense_specific == 0) {
#ifdef GDROMDEBUG
printf("GDROM: aborted (ignored). 0:0\n");
#endif
return (0);
}
#ifdef GDROMDEBUG
printf("GDROM: SENSE %d:", sense_key);
printf("GDROM: %d\n", sense_specific);
#endif
return (sense_key==0? 0 : EIO);
}
static int gdrom_read_toc(sc, toc,session)
struct gdrom_softc *sc;
struct gd_toc *toc;
int session;
{
/* 76543210 76543210
0 0x14 -
2 - bufsz(hi)
4 bufsz(lo) -
6 - -
8 - -
10 - - */
unsigned char cmd[12];
bzero(cmd, sizeof(cmd));
cmd[0] = 0x14;
cmd[1] = session;
cmd[3] = sizeof(struct gd_toc)>>8;
cmd[4] = sizeof(struct gd_toc)&0xff;
return gdrom_command_sense( sc, cmd, toc, sizeof(struct gd_toc) );
}
static int gdrom_read_sectors(sc, buf, sector, cnt)
struct gdrom_softc *sc;
void *buf;
int sector;
int cnt;
{
/* 76543210 76543210
0 0x30 datafmt
2 sec(hi) sec(mid)
4 sec(lo) -
6 - -
8 cnt(hi) cnt(mid)
10 cnt(lo) - */
unsigned char cmd[12];
bzero(cmd, sizeof(cmd));
cmd[0] = 0x30;
cmd[1] = 0x20;
cmd[2] = sector>>16;
cmd[3] = sector>>8;
cmd[4] = sector;
cmd[8] = cnt>>16;
cmd[9] = cnt>>8;
cmd[10] = cnt;
return gdrom_command_sense( sc, cmd, buf, cnt<<11 );
}
static int gdrom_mount_disk(sc)
struct gdrom_softc *sc;
{
/* 76543210 76543210
0 0x70 -
2 0x1f -
4 - -
6 - -
8 - -
10 - - */
unsigned char cmd[12];
bzero(cmd, sizeof(cmd));
cmd[0] = 0x70;
cmd[1] = 0x1f;
return gdrom_command_sense( sc, cmd, NULL, 0 );
}
#if 1
#define toc_dump()
#else
static int toc_dump(void)
{
int track,lba,data;
for(track=TOC_TRACK(toc.first);track<=TOC_TRACK(toc.last);track++) {
data = toc.entry[track-1];
printk("%d %d %d %d\n",track,TOC_CTRL(data),TOC_ADR(data),htonl(TOC_LBA(toc.entry[track-1]))-150);
}
}
#endif
static int gdrom_open (struct inode *ip, struct file *fp)
{
struct gdrom_softc *sc;
int s, error, cnt;
struct gd_toc toc2;
#ifdef GDROMDEBUG
printf("GDROM: open\n");
#endif
sc = _sc;
if (sc->is_open)
return (EBUSY);
gdrom_media_changed = 1;
/*
SPLBIO(s);
while(sc->is_busy)
sleep_on(&is_busy_wait);
sc->is_busy = 1;
splx(s);
*/
for (cnt = 0; cnt < 5; cnt++)
if ((error = gdrom_mount_disk(sc)) == 0)
break;
if (!error)
error = gdrom_read_toc(sc, &toc,0);
/* GD-ROM area support */
gdrom_first = 0;
if (!error && !gdrom_read_toc(sc,&toc2,1)) {
int track;
for(track=TOC_TRACK(toc2.first);track<=TOC_TRACK(toc2.last);track++) {
toc.entry[track-1] = toc2.entry[track-1];
}
toc.last = toc2.last;
toc.leadout = toc2.leadout;
gdrom_first = toc2.first;
}
toc_dump();
/*
sc->is_busy = 0;
wake_up(&is_busy_wait);
*/
if (error)
return -error;
MOD_INC_USE_COUNT;
sc->is_open = 1;
sc->openpart_start = 150;
#ifdef GDROMDEBUG
printf("GDROM: open OK\n");
#endif
return (0);
}
static int gdrom_release (struct inode * inode, struct file * file)
{
struct gdrom_softc *sc;
#ifdef GDROMDEBUG
printf("GDROM: close\n");
#endif
sc = _sc;
sc->is_open = 0;
gdrom_bn = -1;
MOD_DEC_USE_COUNT;
return (0);
}
/*
* Checking if the media has been changed
* (not yet implemented)
*/
static int check_gdrom_media_change (kdev_t full_dev)
{
int target,ret;
target = MINOR(full_dev);
if (target > 0)
{
printk("GDROM: Dreamcast GD-ROM request error: invalid device.\n");
return 0;
}
#ifdef GDROM_DEBUG
printk ("gdrom: check_med_change\n");
#endif
#if 0
ret = gdrom_media_changed;
gdrom_media_changed = 0;
return ret;
#endif
return 0;
}
#define IOCTL_IN(arg,in) \
do { \
/* int st=verify_area(VERIFY_WRITE,(void*)arg,sizeof(in)); \
if (st) return st; */ \
if (copy_from_user(&in,(void*)arg,sizeof(in))) \
return -EFAULT; \
} while(0)
#define IOCTL_OUT(arg,out) \
if (copy_to_user((void*)arg,&out,sizeof(out))) \
return -EFAULT; \
static void lba2msf(struct cdrom_msf0 *msf,int lba)
{
int sec = (lba/75)+2;
msf->frame = lba%75;
msf->second = sec%60;
msf->minute = sec/60;
}
#define set_cdaddr(format,cdaddr,lba) \
if (format == CDROM_LBA) \
(cdaddr)->lba = lba; \
else if (format == CDROM_MSF) \
lba2msf(&(cdaddr)->msf,lba); \
else return -EINVAL
/*
static int set_cdrom_addr(int format,union cdrom_addr *cdaddr,int lba)
{
if (format == CDROM_LBA)
cdaddr->lba = lba;
else if (format == CDROM_MSF)
lba2msf(&cdaddr->msf,lba);
else return -EINVAL;
return 0;
}
*/
static int gdrom_ioctl (struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case CDROMSTART: /* Spin up the drive */
/* Don't think we can do this. Even if we could,
* I think the drive times out and stops after a while
* anyway. For now, ignore it.
*/
break;
case CDROMRESUME: /* keine Ahnung was das ist */
break;
case CDROMEJECT:
break;
#if 1
case CDROMMULTISESSION: {/*multisession support */
struct cdrom_multisession ms;
int track,lba;
IOCTL_IN(arg,ms);
if (gdrom_first)
track = TOC_TRACK(gdrom_first);
else
for(track=TOC_TRACK(toc.last);track>=TOC_TRACK(toc.first);track--) {
if (TOC_CTRL(toc.entry[track-1])==4) break;
}
lba = htonl(TOC_LBA(toc.entry[track-1]))-150;
set_cdaddr(ms.addr_format,&ms.addr,lba);
ms.xa_flag = 1; /* 1 is xa */
/* if not 1, fs_iso9660 don't use session */
IOCTL_OUT(arg,ms);
}
break;
case CDROMREADTOCHDR: { /* Read the table of contents header */
struct cdrom_tochdr tocHdr;
int st;
/* st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr);
if (st) return st; */
tocHdr.cdth_trk0 = TOC_TRACK(toc.first);
tocHdr.cdth_trk1 = TOC_TRACK(toc.last);
IOCTL_OUT(arg,tocHdr);
}
break;
case CDROMREADTOCENTRY: {
uint32_t data,lba;
struct cdrom_tocentry entry;
IOCTL_IN(arg,entry);
if (entry.cdte_track == CDROM_LEADOUT)
data = toc.leadout;
else if (entry.cdte_track > TOC_TRACK(toc.last)
|| entry.cdte_track < TOC_TRACK(toc.first))
{ return -EINVAL;
}
else
data = toc.entry[entry.cdte_track-1];
entry.cdte_adr = TOC_ADR(data);
entry.cdte_ctrl = TOC_CTRL(data);
lba = htonl(TOC_LBA(data))-150;
set_cdaddr(entry.cdte_format,&entry.cdte_addr,lba);
IOCTL_OUT(arg,entry);
}
break;
#endif
default:
return -EINVAL;
}
return 0;
}
/*
* Take care of the different block sizes between cdrom and Linux.
* When Linux gets variable block sizes this will probably go away.
*/
static void gdrom_transfer (void)
{
long offs;
#if 0
while (CURRENT -> nr_sectors > 0 && gdrom_bn == CURRENT -> sector / 4)
{
offs = (CURRENT -> sector & 3) * 512;
memcpy(CURRENT -> buffer, gdrom_buf + offs, 512);
CURRENT -> nr_sectors--;
CURRENT -> sector++;
CURRENT -> buffer += 512;
}
#endif
if (CURRENT -> nr_sectors > 0 && gdrom_bn == CURRENT -> sector / 4)
{
int n;
n = 4-(CURRENT->sector & 3);
if (n > CURRENT->nr_sectors) n = CURRENT->nr_sectors;
offs = (CURRENT -> sector & 3) * 512;
memcpy(CURRENT -> buffer, gdrom_buf + offs, n*512);
CURRENT -> nr_sectors -= n;
CURRENT -> sector += n;
CURRENT -> buffer += n*512;
}
}
/*
* I/O request routine called from Linux kernel.
*/
static void gdrom_read_cmd (void);
static void __do_gdrom_request (unsigned long dummy)
{
unsigned int block,dev;
unsigned int nsect;
while(1){
if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE)
break;
INIT_REQUEST;
dev = MINOR(CURRENT->rq_dev);
block = CURRENT->sector;
nsect = CURRENT->nr_sectors;
if (QUEUE_EMPTY || CURRENT -> sector == -1)
break;
if (CURRENT -> cmd != READ)
{
printk("GDROM: bad cmd %d\n", CURRENT -> cmd);
end_request(0);
continue;
}
if (MINOR(CURRENT -> rq_dev) != 0)
{
printk("GDROM: this version supports only one device\n");
end_request(0);
continue;
}
gdrom_transfer();
/* if we satisfied the request from the buffer, we're done. */
if (CURRENT -> nr_sectors == 0)
{
end_request(1);
continue;
}
#ifdef GDROM_DEBUG
printk ("GDROM: dev %d, block %d, nsect %d\n", dev, block, nsect );
#endif
gdrom_read_cmd ();
// SET_TIMER(__do_gdrom_request, 1); return;
}
}
static void
gdrom_read_cmd (void)
{
long block,nblock;
struct gdrom_softc *sc;
int error;
sc = _sc;
gdrom_bn = -1;
block = CURRENT->sector/4;
nblock = CURRENT->nr_sectors/4;
if ((CURRENT->sector&3)==0 && nblock>0) {
/* direct read to buffer */
if (error = gdrom_read_sectors(sc, CURRENT->buffer, block + 150 ,nblock)) {
end_request(0);
} else {
CURRENT -> nr_sectors -= nblock*4;
CURRENT -> sector += nblock*4;
CURRENT -> buffer += nblock*2048;
end_request(1);
}
} else {
if (error = gdrom_read_sectors(sc, gdrom_buf, block + 150 ,1)) {
end_request(0);
} else {
gdrom_bn = block;
gdrom_transfer();
end_request(1);
}
}
}
static void do_gdrom_request (request_queue_t * q)
{
__do_gdrom_request(0);
}
static void gdrom_interrupt(int irq,void *dev_id,struct pt_regs *reg)
{
gdrom_intr(_sc);
}
static struct block_device_operations gdrom_fops = {
open: gdrom_open,
release: gdrom_release,
ioctl: gdrom_ioctl,
check_media_change: check_gdrom_media_change,
};
static int __init gdrom_init(void)
{
// printk(KERN_INFO "SEGA Dreamcast GD-ROM driver\n");
if (request_irq(gdrom_irq, gdrom_interrupt, SA_INTERRUPT /* SA_SHIRQ */, "gdrom", NULL))
{
printk("Unable to get IRQ%d for Dreamcast GD-ROM\n", gdrom_irq);
return -EIO;
}
if (devfs_register_blkdev(MAJOR_NR, DEVICE_STR,&gdrom_fops) != 0)
{
printk("GDROM: Unable to get major %d for Dreamcast GD-ROM\n",
MAJOR_NR);
return -EIO;
}
devfs_register (NULL, DEVICE_STR, DEVFS_FL_DEFAULT, MAJOR_NR, 0,
S_IFBLK | S_IRUGO | S_IWUGO, &gdrom_fops, NULL);
blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
blksize_size[MAJOR_NR] = gdrom_blocksizes;
read_ahead[MAJOR_NR] = 4;
register_disk(NULL, MKDEV(MAJOR_NR,0), 1, &gdrom_fops, 0);
printk (KERN_INFO "GDROM: Dreamcast GD-ROM Drive found.\n" );
/*
* reenable disabled drive
*/
{
register u_int32_t p, x;
*((volatile u_int32_t *)0xa05f74e4) = 0x1fffff;
for(p=0; p<0x200000/4; p++)
x = ((volatile u_int32_t *)0xa0000000)[p];
}
return 0;
}
static void __exit gdrom_exit(void)
{
CLEAR_TIMER;
free_irq(gdrom_irq,NULL);
devfs_unregister(devfs_find_handle(NULL, DEVICE_STR, 0, 0, DEVFS_SPECIAL_BLK,0));
if ((devfs_unregister_blkdev(MAJOR_NR, DEVICE_STR ) == -EINVAL))
{
printk("What's that: can't unregister GD-ROM\n" );
return;
}
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
}
module_init(gdrom_init);
module_exit(gdrom_exit);
setup_dc.c
/*
* $Id: setup_dc.c,v 1.2 2001/04/18 18:28:13 yaegashi Exp $
* SEGA Dreamcast support
*/
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/irq.h>
#define GAPSPCI_REGS 0x01001400
#define GAPSPCI_DMA_BASE 0x01840000
#define GAPSPCI_DMA_SIZE 32768
#define GAPSPCI_BBA_CONFIG 0x01001600
#define GAPSPCI_IRQ 11
#define GAPSPCI_INTC 0x005f6924
#define GAPSPCI_MASK (1<<3)
#define GDROM_IRQ 9
#define GDROM_INTC 0x005f6934
#define GDROM_MASK 1
static __init int gapspci_init(void);
static int gapspci_dma_used;
static struct pci_bus *pci_root_bus;
static struct {
unsigned long addr,mask
} irq_data[16];
static void disable_gapspci_irq(unsigned int irq)
{
unsigned long flags;
unsigned long intc;
unsigned long addr,mask;
addr = irq_data[irq].addr;
mask = irq_data[irq].mask;
save_and_cli(flags);
intc = inl(addr);
intc &= ~mask;
outl(intc, addr);
restore_flags(flags);
}
static void enable_gapspci_irq(unsigned int irq)
{
unsigned long flags;
unsigned long intc;
unsigned long addr,mask;
addr = irq_data[irq].addr;
mask = irq_data[irq].mask;
save_and_cli(flags);
intc = inl(addr);
intc |= mask;
outl(intc, addr);
restore_flags(flags);
}
static void mask_and_ack_gapspci_irq(unsigned int irq)
{
disable_gapspci_irq(irq);
}
static void end_gapspci_irq(unsigned int irq)
{
enable_gapspci_irq(irq);
}
static unsigned int startup_gapspci_irq(unsigned int irq)
{
enable_gapspci_irq(irq);
return 0;
}
static void shutdown_gapspci_irq(unsigned int irq)
{
disable_gapspci_irq(irq);
}
static struct hw_interrupt_type gapspci_irq_type = {
"GAPSPCI-IRQ",
startup_gapspci_irq,
shutdown_gapspci_irq,
enable_gapspci_irq,
disable_gapspci_irq,
mask_and_ack_gapspci_irq,
end_gapspci_irq
};
void make_gapspci_irq(unsigned int irq, unsigned int addr, int mask)
{
disable_irq_nosync(irq);
irq_data[irq].addr = addr;
irq_data[irq].mask = mask;
irq_desc[irq].handler = &gapspci_irq_type;
disable_gapspci_irq(irq);
}
static u8 __init no_swizzle(struct pci_dev *dev, u8 * pin)
{
return PCI_SLOT(dev->devfn);
}
static int __init map_od_irq(struct pci_dev *dev, u8 slot, u8 pin)
{
return GAPSPCI_IRQ;
}
int __init setup_dreamcast(void)
{
gapspci_init();
make_gapspci_irq(GDROM_IRQ,GDROM_INTC,GDROM_MASK);
printk(KERN_INFO "SEGA Dreamcast support.\n");
#if 0
printk(KERN_INFO "BCR1: 0x%08x\n", ctrl_inl(0xff800000));
printk(KERN_INFO "BCR2: 0x%08x\n", ctrl_inw(0xff800004));
printk(KERN_INFO "WCR1: 0x%08x\n", ctrl_inl(0xff800008));
printk(KERN_INFO "WCR2: 0x%08x\n", ctrl_inl(0xff80000c));
printk(KERN_INFO "WCR3: 0x%08x\n", ctrl_inl(0xff800010));
printk(KERN_INFO "MCR: 0x%08x\n", ctrl_inl(0xff800014));
printk(KERN_INFO "PCR: 0x%08x\n", ctrl_inw(0xff800018));
/*
* BCR1: 0xa3020008
* BCR2: 0x0001
* WCR1: 0x01110111
* WCR2: 0x618066d8
* WCR3: 0x07777777
* MCR: 0xc00a0e24
* PCR: 0x0000
*/
#endif
return 0;
}
/*
* Dreamcast PCI: Supports SEGA Broadband Adaptor only.
*/
#define BBA_SELECTED(dev) (dev->bus->number==0 && dev->devfn==0)
static int gapspci_read_config_byte(struct pci_dev *dev, int where,
u8 * val)
{
if (BBA_SELECTED(dev))
*val = inb(GAPSPCI_BBA_CONFIG+where);
else
*val = 0xff;
return PCIBIOS_SUCCESSFUL;
}
static int gapspci_read_config_word(struct pci_dev *dev, int where,
u16 * val)
{
if (BBA_SELECTED(dev))
*val = inw(GAPSPCI_BBA_CONFIG+where);
else
*val = 0xffff;
return PCIBIOS_SUCCESSFUL;
}
static int gapspci_read_config_dword(struct pci_dev *dev, int where,
u32 * val)
{
if (BBA_SELECTED(dev))
*val = inl(GAPSPCI_BBA_CONFIG+where);
else
*val = 0xffffffff;
return PCIBIOS_SUCCESSFUL;
}
static int gapspci_write_config_byte(struct pci_dev *dev, int where,
u8 val)
{
if (BBA_SELECTED(dev))
outb(val, GAPSPCI_BBA_CONFIG+where);
return PCIBIOS_SUCCESSFUL;
}
static int gapspci_write_config_word(struct pci_dev *dev, int where,
u16 val)
{
if (BBA_SELECTED(dev))
outw(val, GAPSPCI_BBA_CONFIG+where);
return PCIBIOS_SUCCESSFUL;
}
static int gapspci_write_config_dword(struct pci_dev *dev, int where,
u32 val)
{
if (BBA_SELECTED(dev))
outl(val, GAPSPCI_BBA_CONFIG+where);
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops pci_config_ops = {
gapspci_read_config_byte,
gapspci_read_config_word,
gapspci_read_config_dword,
gapspci_write_config_byte,
gapspci_write_config_word,
gapspci_write_config_dword
};
void pcibios_align_resource(void *data, struct resource *res,
unsigned long size)
{
}
void __init pcibios_update_irq(struct pci_dev *dev, int irq)
{
}
void __init pcibios_update_resource(struct pci_dev *dev, struct resource *root,
struct resource *res, int resource)
{
}
int pcibios_enable_device(struct pci_dev *dev)
{
u16 cmd, old_cmd;
int idx;
struct resource *r;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
old_cmd = cmd;
for (idx = 0; idx < 6; idx++) {
r = dev->resource + idx;
if (!r->start && r->end) {
printk(KERN_ERR
"PCI: Device %s not available because"
" of resource collisions\n",
dev->slot_name);
return -EINVAL;
}
if (r->flags & IORESOURCE_IO)
cmd |= PCI_COMMAND_IO;
if (r->flags & IORESOURCE_MEM)
cmd |= PCI_COMMAND_MEMORY;
}
if (cmd != old_cmd) {
printk("PCI: enabling device %s (%04x -> %04x)\n",
dev->slot_name, old_cmd, cmd);
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
return 0;
}
void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
dma_addr_t * dma_handle)
{
unsigned long buf;
if (gapspci_dma_used+size > GAPSPCI_DMA_SIZE)
return NULL;
buf = GAPSPCI_DMA_BASE+gapspci_dma_used;
gapspci_dma_used = PAGE_ALIGN(gapspci_dma_used+size);
printk("pci_alloc_consistent: %d bytes at 0x%p\n", size, buf);
*dma_handle = (dma_addr_t)buf;
return (void *)P2SEGADDR(buf);
}
void pci_free_consistent(struct pci_dev *hwdev, size_t size,
void *vaddr, dma_addr_t dma_handle)
{
}
void __init
pcibios_fixup_pbus_ranges(struct pci_bus *bus, struct pbus_set_ranges_data *ranges)
{
}
void __init pcibios_fixup_bus(struct pci_bus *bus)
{
struct list_head *ln;
struct pci_dev *dev;
for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) {
dev = pci_dev_b(ln);
if (!BBA_SELECTED(dev)) continue;
printk("PCI: MMIO fixup to %s\n", dev->name);
dev->resource[1].start=0x01001700;
dev->resource[1].end=0x010017ff;
}
}
void __init dreamcast_pcibios_init(void)
{
pci_root_bus = pci_scan_bus(0, &pci_config_ops, NULL);
/* pci_assign_unassigned_resources(); */
pci_fixup_irqs(no_swizzle, map_od_irq);
}
static __init int gapspci_init(void)
{
int i;
char idbuf[16];
for(i=0; i<16; i++)
idbuf[i]=inb(GAPSPCI_REGS+i);
if(strncmp(idbuf, "GAPSPCI_BRIDGE_2", 16))
return -1;
outl(0x5a14a501, GAPSPCI_REGS+0x18);
for(i=0; i<1000000; i++);
if(inl(GAPSPCI_REGS+0x18)!=1)
return -1;
outl(0x01000000, GAPSPCI_REGS+0x20);
outl(0x01000000, GAPSPCI_REGS+0x24);
outl(GAPSPCI_DMA_BASE, GAPSPCI_REGS+0x28);
outl(GAPSPCI_DMA_BASE+GAPSPCI_DMA_SIZE, GAPSPCI_REGS+0x2c);
outl(1, GAPSPCI_REGS+0x14);
outl(1, GAPSPCI_REGS+0x34);
gapspci_dma_used=0;
/*
irq_desc[GAPSPCI_IRQ].handler = &gapspci_irq_type;
*/
make_gapspci_irq(GAPSPCI_IRQ,GAPSPCI_INTC,GAPSPCI_MASK);
/* Setting Broadband Adapter */
outw(0xf900, GAPSPCI_BBA_CONFIG+0x06);
outl(0x00000000, GAPSPCI_BBA_CONFIG+0x30);
outb(0x00, GAPSPCI_BBA_CONFIG+0x3c);
outb(0xf0, GAPSPCI_BBA_CONFIG+0x0d);
outw(0x0006, GAPSPCI_BBA_CONFIG+0x04);
outl(0x00002001, GAPSPCI_BBA_CONFIG+0x10);
outl(0x01000000, GAPSPCI_BBA_CONFIG+0x14);
return 0;
}