// SPDX-License-Identifier: GPL-2.0-or-later /* GD ROM driver for the SEGA Dreamcast * copyright Adrian McMenamin, 2007 * With thanks to Marcus Comstedt and Nathan Keynes * for work in reversing PIO and DMA
*/
staticbool gdrom_wait_busy_sleeps(void)
{ unsignedlong timeout; /* Wait to get busy first */
timeout = jiffies + GDROM_DEFAULT_TIMEOUT; while (!gdrom_is_busy() && time_before(jiffies, timeout))
cpu_relax(); /* Now wait for busy to clear */ return gdrom_wait_clrbusy();
}
staticvoid gdrom_identifydevice(void *buf)
{ int c; short *data = buf; /* If the device won't clear it has probably * been hit by a serious failure - but we'll
* try to return a sense key even so */ if (!gdrom_wait_clrbusy()) {
gdrom_getsense(NULL); return;
}
__raw_writeb(GDROM_COM_IDDEV, GDROM_STATUSCOMMAND_REG); if (!gdrom_wait_busy_sleeps()) {
gdrom_getsense(NULL); return;
} /* now read in the data */ for (c = 0; c < 40; c++)
data[c] = __raw_readw(GDROM_DATA_REG);
}
staticvoid gdrom_spicommand(void *spi_string, int buflen)
{ short *cmd = spi_string; unsignedlong timeout;
/* ensure IRQ_WAIT is set */
__raw_writeb(0x08, GDROM_ALTSTATUS_REG); /* specify how many bytes we expect back */
__raw_writeb(buflen & 0xFF, GDROM_BCL_REG);
__raw_writeb((buflen >> 8) & 0xFF, GDROM_BCH_REG); /* other parameters */
__raw_writeb(0, GDROM_INTSEC_REG);
__raw_writeb(0, GDROM_SECNUM_REG);
__raw_writeb(0, GDROM_ERROR_REG); /* Wait until we can go */ if (!gdrom_wait_clrbusy()) {
gdrom_getsense(NULL); return;
}
timeout = jiffies + GDROM_DEFAULT_TIMEOUT;
__raw_writeb(GDROM_COM_PACKET, GDROM_STATUSCOMMAND_REG); while (!gdrom_data_request() && time_before(jiffies, timeout))
cpu_relax(); if (!time_before(jiffies, timeout + 1)) {
gdrom_getsense(NULL); return;
}
outsw(GDROM_DATA_REG, cmd, 6);
}
/* gdrom_command_executediagnostic: * Used to probe for presence of working GDROM * Restarts GDROM device and then applies standard ATA 3 * Execute Diagnostic Command: a return of '1' indicates device 0 * present and device 1 absent
*/ staticchar gdrom_execute_diagnostic(void)
{
gdrom_hardreset(gd.cd_info); if (!gdrom_wait_clrbusy()) return 0;
__raw_writeb(GDROM_COM_EXECDIAG, GDROM_STATUSCOMMAND_REG); if (!gdrom_wait_busy_sleeps()) return 0; return __raw_readb(GDROM_ERROR_REG);
}
/* Check if GD-ROM */
err = gdrom_readtoc_cmd(gd.toc, 1); /* Not a GD-ROM so check if standard CD-ROM */ if (err) {
err = gdrom_readtoc_cmd(gd.toc, 0); if (err) {
pr_info("Could not get CD table of contents\n"); return -ENXIO;
}
}
fentry = get_entry_track(gd.toc->first);
lentry = get_entry_track(gd.toc->last); /* Find the first data track */
track = get_entry_track(gd.toc->last); do {
data = gd.toc->entry[track - 1]; if (get_entry_q_ctrl(data)) break; /* ie a real data track */
track--;
} while (track >= fentry);
if ((track > 100) || (track < get_entry_track(gd.toc->first))) {
pr_info("No data on the last session of the CD\n");
gdrom_getsense(NULL); return -ENXIO;
}
staticint gdrom_open(struct cdrom_device_info *cd_info, int purpose)
{ /* spin up the disk */ return gdrom_preparedisk_cmd();
}
/* this function is required even if empty */ staticvoid gdrom_release(struct cdrom_device_info *cd_info)
{
}
staticint gdrom_drivestatus(struct cdrom_device_info *cd_info, int ignore)
{ /* read the sense key */ char sense = __raw_readb(GDROM_ERROR_REG);
sense &= 0xF0; if (sense == 0) return CDS_DISC_OK; if (sense == 0x20) return CDS_DRIVE_NOT_READY; /* default */ return CDS_NO_INFO;
}
staticunsignedint gdrom_check_events(struct cdrom_device_info *cd_info, unsignedint clearing, int ignore)
{ /* check the sense key */ return (__raw_readb(GDROM_ERROR_REG) & 0xF0) == 0x60 ?
DISK_EVENT_MEDIA_CHANGE : 0;
}
/* reset the G1 bus */ staticint gdrom_hardreset(struct cdrom_device_info *cd_info)
{ int count;
__raw_writel(0x1fffff, GDROM_RESET_REG); for (count = 0xa0000000; count < 0xa0200000; count += 4)
__raw_readl(count); return 0;
}
/* keep the function looking like the universal
* CD Rom specification - returning int */ staticint gdrom_packetcommand(struct cdrom_device_info *cd_info, struct packet_command *command)
{
gdrom_spicommand(&command->cmd, command->buflen); return 0;
}
/* Get Sense SPI command * From Marcus Comstedt * cmd = 0x13 * cmd + 4 = length of returned buffer * Returns 5 16 bit words
*/ staticint gdrom_getsense(short *bufstring)
{ struct packet_command *sense_command; short sense[5]; int sense_key; int err = -EIO;
sense_command = kzalloc(sizeof(struct packet_command), GFP_KERNEL); if (!sense_command) return -ENOMEM;
sense_command->cmd[0] = 0x13;
sense_command->cmd[4] = 10;
sense_command->buflen = 10; /* even if something is pending try to get
* the sense key if possible */ if (gd.pending && !gdrom_wait_clrbusy()) {
err = -EBUSY; goto cleanup_sense_final;
}
gd.pending = 1;
gdrom_packetcommand(gd.cd_info, sense_command);
wait_event_interruptible_timeout(command_queue, gd.pending == 0,
GDROM_DEFAULT_TIMEOUT); if (gd.pending) goto cleanup_sense;
insw(GDROM_DATA_REG, &sense, sense_command->buflen/2); if (sense[1] & 40) {
pr_info("Drive not ready - command aborted\n"); goto cleanup_sense;
}
sense_key = sense[1] & 0x0F; if (sense_key < ARRAY_SIZE(sense_texts))
pr_info("%s\n", sense_texts[sense_key].text); else
pr_err("Unknown sense key: %d\n", sense_key); if (bufstring) /* return addional sense data */
memcpy(bufstring, &sense[4], 2); if (sense_key < 2)
err = 0;
/* query device ID */
id = kzalloc(sizeof(struct gdrom_id), GFP_KERNEL); if (!id) return err;
gdrom_identifydevice(id);
model_name = kstrndup(id->modname, 16, GFP_KERNEL); if (!model_name) goto free_id;
manuf_name = kstrndup(id->mname, 16, GFP_KERNEL); if (!manuf_name) goto free_model_name;
firmw_ver = kstrndup(id->firmver, 16, GFP_KERNEL); if (!firmw_ver) goto free_manuf_name;
pr_info("%s from %s with firmware %s\n",
model_name, manuf_name, firmw_ver);
err = 0;
kfree(firmw_ver);
free_manuf_name:
kfree(manuf_name);
free_model_name:
kfree(model_name);
free_id:
kfree(id); return err;
}
/* set the default mode for DMA transfer */ staticint gdrom_init_dma_mode(void)
{
__raw_writeb(0x13, GDROM_ERROR_REG);
__raw_writeb(0x22, GDROM_INTSEC_REG); if (!gdrom_wait_clrbusy()) return -EBUSY;
__raw_writeb(0xEF, GDROM_STATUSCOMMAND_REG); if (!gdrom_wait_busy_sleeps()) return -EBUSY; /* Memory protection setting for GDROM DMA * Bits 31 - 16 security: 0x8843 * Bits 15 and 7 reserved (0) * Bits 14 - 8 start of transfer range in 1 MB blocks OR'ed with 0x80 * Bits 6 - 0 end of transfer range in 1 MB blocks OR'ed with 0x80 * (0x40 | 0x80) = start range at 0x0C000000
* (0x7F | 0x80) = end range at 0x0FFFFFFF */
__raw_writel(0x8843407F, GDROM_DMA_ACCESS_CTRL_REG);
__raw_writel(9, GDROM_DMA_WAIT_REG); /* DMA word setting */ return 0;
}
/* * register this as a block device and as compliant with the * universal CD Rom driver interface
*/ staticint probe_gdrom(struct platform_device *devptr)
{ struct queue_limits lim = {
.logical_block_size = GDROM_HARD_SECTOR, /* using DMA so memory will need to be contiguous */
.max_segments = 1, /* set a large max size to get most from DMA */
.max_segment_size = 0x40000,
.features = BLK_FEAT_ROTATIONAL,
}; int err;
/* * Ensure our "one" device is initialized properly in case of previous * usages of it
*/
memset(&gd, 0, sizeof(gd));
/* Start the device */ if (gdrom_execute_diagnostic() != 1) {
pr_warn("ATA Probe for GDROM failed\n"); return -ENODEV;
} /* Print out firmware ID */ if (gdrom_outputversion()) return -ENOMEM; /* Register GDROM */
gdrom_major = register_blkdev(0, GDROM_DEV_NAME); if (gdrom_major <= 0) return gdrom_major;
pr_info("Registered with major number %d\n",
gdrom_major); /* Specify basic properties of drive */
gd.cd_info = kzalloc(sizeof(struct cdrom_device_info), GFP_KERNEL); if (!gd.cd_info) {
err = -ENOMEM; goto probe_fail_no_mem;
}
probe_gdrom_setupcd();
err = blk_mq_alloc_sq_tag_set(&gd.tag_set, &gdrom_mq_ops, 1,
BLK_MQ_F_BLOCKING); if (err) goto probe_fail_free_cd_info;
gd.disk = blk_mq_alloc_disk(&gd.tag_set, &lim, NULL); if (IS_ERR(gd.disk)) {
err = PTR_ERR(gd.disk); goto probe_fail_free_tag_set;
}
gd.gdrom_rq = gd.disk->queue;
probe_gdrom_setupdisk(); if (register_cdrom(gd.disk, gd.cd_info)) {
err = -ENODEV; goto probe_fail_cleanup_disk;
}
gd.disk->fops = &gdrom_bdops;
gd.disk->events = DISK_EVENT_MEDIA_CHANGE; /* latch on to the interrupt */
err = gdrom_set_interrupt_handlers(); if (err) goto probe_fail_cleanup_disk;
err = probe_gdrom_setupqueue(); if (err) goto probe_fail_free_irqs;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.