Copy Link
Add to Bookmark
Report

Dreamcast GD-ROM device driver

Dreamcast's profile picture
Published in 
Dreamcast
 · 2 years ago

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;
}

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT