Update of /cvsroot/gc-linux/linux/drivers/block/gcn-di
In directory sc8-pr-cvs2.sourceforge.net:/tmp/cvs-serv4431/drivers/block/gcn-di
Modified Files:
gcn-di.c
Log Message:
Merged 2.6.22.
Updated drivers to use in_be* and out_be* instead of read* and write*
input/output instructions.
Updated DI driver:
- do not use debug extensions with unknown drive models
- do not load custom firmware on xenogc/duoq
- evict alien firmwares and load custom formware on known drive models
Index: gcn-di.c
===================================================================
RCS file: /cvsroot/gc-linux/linux/drivers/block/gcn-di/gcn-di.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -d -r1.19 -r1.20
--- gcn-di.c 18 Feb 2007 22:56:01 -0000 1.19
+++ gcn-di.c 14 Nov 2007 23:08:48 -0000 1.20
@@ -2,8 +2,8 @@
* drivers/block/gcn-di/gcn-di.c
*
* Nintendo GameCube DVD Interface driver
- * Copyright (C) 2005-2006 The GameCube Linux Team
- * Copyright (C) 2005,2006 Albert Herranz
+ * Copyright (C) 2005-2007 The GameCube Linux Team
+ * Copyright (C) 2005,2006,2007 Albert Herranz
*
* Portions based on previous work by Scream|CT.
*
@@ -37,7 +37,7 @@
#define DRV_DESCRIPTION "Nintendo GameCube DVD Interface driver"
#define DRV_AUTHOR "Albert Herranz"
-static char di_driver_version[] = "0.7-isobel";
+static char di_driver_version[] = "0.8-isobel";
#define di_printk(level, format, arg...) \
printk(level DRV_MODULE_NAME ": " format , ## arg)
@@ -138,7 +138,7 @@
/* Driver Settings */
-#define DI_NAME "di"
+#define DI_NAME "gcndi"
#define DI_MAJOR 60
#define DI_COMMAND_TIMEOUT 20 /* seconds */
@@ -233,8 +233,7 @@
__DI_MEDIA_CHANGED,
__DI_START_QUEUE,
__DI_RESETTING,
- __DI_DRIVECHIP_PRESENT,
- __DI_ALREADY_RESET,
+ __DI_AVOID_DEBUG,
};
/*
@@ -269,8 +268,7 @@
#define DI_MEDIA_CHANGED (1<<__DI_MEDIA_CHANGED)
#define DI_START_QUEUE (1<<__DI_START_QUEUE)
#define DI_RESETTING (1<<__DI_RESETTING)
-#define DI_DRIVECHIP_PRESENT (1<<__DI_DRIVECHIP_PRESENT)
-#define DI_ALREADY_RESET (1<<__DI_ALREADY_RESET)
+#define DI_AVOID_DEBUG (1<<__DI_AVOID_DEBUG)
unsigned long nr_sectors;
@@ -415,6 +413,10 @@
.cmdbuf0 = 0,
},
+/*
+ * The following commands are available in debug mode only.
+ */
+
#define DI_OP_READMEM (DI_OP_ENABLE2+1)
[DI_OP_READMEM] = {
.op = DI_OP(DI_OP_READMEM, DI_DIR_READ | DI_MODE_IMMED),
@@ -602,6 +604,7 @@
/*
* Builds a "Read Memory" command.
+ * Requires debug mode enabled.
*/
static inline void di_op_readmem(struct di_command *cmd,
struct di_device *ddev)
@@ -612,6 +615,7 @@
/*
* Builds a "Invoke func" command.
+ * Requires debug mode enabled.
*/
static inline void di_op_func(struct di_command *cmd,
struct di_device *ddev, u32 address)
@@ -623,6 +627,7 @@
/*
* Builds a "Write Memory" command.
+ * Requires debug mode enabled.
*/
static inline void di_op_writemem(struct di_command *cmd,
struct di_device *ddev)
@@ -641,6 +646,7 @@
/*
* Builds a "spin motor" command.
+ * Requires debug mode enabled.
*/
static void di_op_spinmotor(struct di_command *cmd,
struct di_device *ddev, u32 flags)
@@ -651,6 +657,7 @@
/*
* Builds a "set drive status" command.
+ * Requires debug mode enabled.
*/
static void di_op_setstatus(struct di_command *cmd,
struct di_device *ddev, u8 status)
@@ -663,6 +670,7 @@
* Builds a "enable extensions" command.
* The extended firmware will transparently disable the extensions when
* original media is found.
+ * Requires debug mode enabled.
*/
static void di_op_enableextensions(struct di_command *cmd,
struct di_device *ddev, u8 enable)
@@ -820,16 +828,16 @@
(len & DI_DMA_ALIGN) != 0);
/* setup address and length of transfer */
- writel(len, io_base + DI_LENGTH);
- writel(data, io_base + DI_MAR);
+ out_be32(io_base + DI_LENGTH, len);
+ out_be32(io_base + DI_MAR, data);
/* enable the Transfer Complete interrupt */
spin_lock_irqsave(&ddev->io_lock, flags);
- writel(readl(sr_reg) | DI_SR_TCINTMASK, sr_reg);
+ out_be32(sr_reg, in_be32(sr_reg) | DI_SR_TCINTMASK);
spin_unlock_irqrestore(&ddev->io_lock, flags);
/* start the transfer */
- writel(DI_CR_TSTART | DI_CR_DMA | (mode&0x4), io_base + DI_CR);
+ out_be32(io_base + DI_CR, DI_CR_TSTART | DI_CR_DMA | (mode&0x4));
}
/*
@@ -841,11 +849,11 @@
unsigned long timeout = jiffies + secs*HZ;
/* busy-wait for transfer complete */
- while((readl(cr_reg) & DI_CR_TSTART) && time_before(jiffies, timeout)) {
+ while((in_be32(cr_reg) & DI_CR_TSTART) && time_before(jiffies, timeout)) {
cpu_relax();
}
- return (readl(cr_reg) & DI_CR_TSTART)?-EBUSY:0;
+ return (in_be32(cr_reg) & DI_CR_TSTART)?-EBUSY:0;
}
/*
@@ -859,7 +867,7 @@
/* we don't want TCINTs to disturb us while waiting */
spin_lock_irqsave(&ddev->io_lock, flags);
- writel(readl(sr_reg) & ~DI_SR_TCINTMASK, sr_reg);
+ out_be32(sr_reg, in_be32(sr_reg) & ~DI_SR_TCINTMASK);
spin_unlock_irqrestore(&ddev->io_lock, flags);
/* if the drive got stuck, reset it */
@@ -870,7 +878,7 @@
/* ack and enable the Transfer Complete interrupt */
spin_lock_irqsave(&ddev->io_lock, flags);
- writel(readl(sr_reg) | (DI_SR_TCINT|DI_SR_TCINTMASK), sr_reg);
+ out_be32(sr_reg, in_be32(sr_reg) | (DI_SR_TCINT|DI_SR_TCINTMASK));
spin_unlock_irqrestore(&ddev->io_lock, flags);
return;
@@ -891,14 +899,14 @@
spin_lock_irqsave(&ddev->io_lock, flags);
/* ack and mask dvd io interrupts */
- sr = readl(sr_reg);
+ sr = in_be32(sr_reg);
sr |= DI_SR_BRKINT | DI_SR_TCINT | DI_SR_DEINT;
sr &= ~(DI_SR_BRKINTMASK | DI_SR_TCINTMASK | DI_SR_DEINTMASK);
- writel(sr, sr_reg);
+ out_be32(sr_reg, sr);
/* ack and mask dvd cover interrupts */
- cvr = readl(cvr_reg);
- writel((cvr | DI_CVR_CVRINT) & ~DI_CVR_CVRINTMASK, cvr_reg);
+ cvr = in_be32(cvr_reg);
+ out_be32(cvr_reg, (cvr | DI_CVR_CVRINT) & ~DI_CVR_CVRINTMASK);
spin_unlock_irqrestore(&ddev->io_lock, flags);
@@ -922,14 +930,14 @@
/*DBG("buf0 = 0x%08x, buf1 = 0x%08x, buf2 = 0x%08x\n",
cmd->cmdbuf0, cmd->cmdbuf1, cmd->cmdbuf2);*/
- writel(cmd->cmdbuf0, io_base + DI_CMDBUF0);
- writel(cmd->cmdbuf1, io_base + DI_CMDBUF1);
- writel(cmd->cmdbuf2, io_base + DI_CMDBUF2);
+ out_be32(io_base + DI_CMDBUF0, cmd->cmdbuf0);
+ out_be32(io_base + DI_CMDBUF1, cmd->cmdbuf1);
+ out_be32(io_base + DI_CMDBUF2, cmd->cmdbuf2);
cmd->ddev->drive_status = 0;
if (tstart) {
- writel(DI_CR_TSTART | (opcode->op & 0x6), io_base + DI_CR);
+ out_be32(io_base + DI_CR, DI_CR_TSTART | (opcode->op & 0x6));
}
}
@@ -994,7 +1002,7 @@
void __iomem *io_base = ddev->io_base;
u32 __iomem *data_reg = io_base + DI_DATA;
- ddev->drive_status = readl(data_reg);
+ ddev->drive_status = in_be32(data_reg);
}
/*
@@ -1190,11 +1198,11 @@
spin_lock_irqsave(&ddev->io_lock, flags);
- sr = readl(sr_reg);
+ sr = in_be32(sr_reg);
mask = sr & (DI_SR_BRKINTMASK | DI_SR_TCINTMASK | DI_SR_DEINTMASK);
reason = sr; /* & (mask << 1); */
if (reason) {
- writel(sr | reason, sr_reg);
+ out_be32(sr_reg, sr | reason);
spin_unlock_irqrestore(&ddev->io_lock, flags);
if (reason & DI_SR_TCINT) {
@@ -1211,19 +1219,18 @@
spin_lock_irqsave(&ddev->io_lock, flags);
}
- cvr = readl(cvr_reg);
+ cvr = in_be32(cvr_reg);
mask = cvr & DI_CVR_CVRINTMASK;
reason = cvr; /* & (mask << 1); */
if ((reason & DI_CVR_CVRINT)) {
- writel(cvr | DI_CVR_CVRINT, cvr_reg);
+ out_be32(cvr_reg, cvr | DI_CVR_CVRINT);
set_bit(__DI_MEDIA_CHANGED, &ddev->flags);
if (test_and_clear_bit(__DI_RESETTING, &ddev->flags)) {
- if (ddev->flags & DI_INTEROPERABLE) {
- DBG("extensions loaded"
- " and hopefully working\n");
- } else {
- DBG("drive reset, no extensions"
- " loaded yet\n");
+ if (!test_bit(__DI_AVOID_DEBUG, &ddev->flags)) {
+ if (ddev->flags & DI_INTEROPERABLE) {
+ DBG("extensions loaded"
+ " and hopefully working\n");
+ }
}
} else {
DBG("dvd cover interrupt\n");
@@ -1245,15 +1252,17 @@
#define FLIPPER_RESET_DVD 0x00000004
- /* set flags, but preserve the drivechip flag */
- ddev->flags = (ddev->flags & DI_DRIVECHIP_PRESENT) |
- DI_RESETTING | DI_MEDIA_CHANGED | DI_ALREADY_RESET;
+ /* set flags, but preserve the alien firmware flag */
+ ddev->flags = (ddev->flags & DI_AVOID_DEBUG) |
+ DI_RESETTING | DI_MEDIA_CHANGED;
- reset = readl(reset_reg);
- writel((reset & ~FLIPPER_RESET_DVD) | 1, reset_reg);
+ reset = in_be32(reset_reg);
+ out_be32(reset_reg, (reset & ~FLIPPER_RESET_DVD) | 1);
mdelay(500);
- writel((reset | FLIPPER_RESET_DVD) | 1, reset_reg);
+ out_be32(reset_reg, (reset | FLIPPER_RESET_DVD) | 1);
mdelay(500);
+
+ DBG("drive reset\n");
}
@@ -1292,7 +1301,7 @@
di_op_getstatus(&cmd, ddev);
di_run_command_and_wait(&cmd);
- drive_status = readl(data_reg);
+ drive_status = in_be32(data_reg);
return drive_status;
}
@@ -1334,7 +1343,7 @@
cmd.cmdbuf1 = address;
di_run_command_and_wait(&cmd);
if (di_command_ok(&cmd)) {
- *data = readl(io_base + DI_DATA);
+ *data = in_be32(io_base + DI_DATA);
result = 0;
}
return result;
@@ -1346,7 +1355,7 @@
*/
static unsigned long di_fw_get_irq_handler(struct di_device *ddev)
{
- unsigned long data = ~0;
+ unsigned long data = 0;
di_fw_read_meml(ddev, &data, DI_DRIVE_IRQ_VECTOR);
return data;
@@ -1452,7 +1461,6 @@
static u8 parking_code[] = {
0xa0, /* sub d0, d0 */
0xc4, 0xda, 0xfc, /* movb d0, (ADBCTL) */
- 0xf4, 0x40, 0x60, 0xec, 0x40, /* movb d0,(0x40ec60) */
0xf4, 0x74, 0x74, 0x0a, 0x08, /* mov 0x080a74, a0 */ /* fixup */
0xf7, 0x20, 0x4c, 0x80, /* mov a0, (0x804c) */
0xfe, /* rts */
@@ -1478,7 +1486,7 @@
/* fix the parking code to match our drive model */
cpu_to_le32s(&irq_handler);
- memcpy(parking_code + 11, &irq_handler, 3);
+ memcpy(parking_code + 6, &irq_handler, 3);
/* load and call it */
di_fw_patch_mem(ddev, load_address, parking_code, sizeof(parking_code));
@@ -1529,46 +1537,93 @@
}
/*
+ * Tries to determine if firmware extensions are currently installed.
+ * Requires debug mode enabled.
+ */
+static int di_has_alien_drive_code(struct di_device *ddev)
+{
+ unsigned long address;
+ int result = 1;
+
+ /*
+ * We assume that alien drive code is in place if the interrupt handler
+ * is not pointing to ROM address space.
+ */
+ address = di_fw_get_irq_handler(ddev);
+ address = le32_to_cpu(address);
+ if (address) {
+ if ((address & 0xffff0000) == 0x00080000)
+ result = 0;
+ }
+ return result;
+}
+
+/*
+ * Enables some workarounds and/or special behaviours depending on
+ * the drive firmware found.
+ * Requires debug mode enabled.
+ */
+static void di_init_alien_drive_code_quirks(struct di_device *ddev)
+{
+ unsigned long fingerprint;
+
+ /*
+ * Test if a xenogc/duoq is installed.
+ */
+ if (!di_fw_read_meml(ddev, &fingerprint, 0x40c60a)) {
+ if (fingerprint == 0xf710fff7) {
+ di_printk(KERN_INFO, "drivechip: xenogc/duoq\n");
+ set_bit(__DI_AVOID_DEBUG, &ddev->flags);
+ }
+ }
+}
+
+/*
* Configures the drive to accept DVD-R and DVD+R media.
*/
static void di_make_interoperable(struct di_device *ddev)
{
struct di_command cmd;
- if (ddev->drive_code) {
- /* calm things down */
- di_spin_down_drive(ddev);
+ if (!ddev->drive_code || test_bit(__DI_AVOID_DEBUG, &ddev->flags))
+ return;
- /* disable any alien drive code */
- di_enable_debug_commands(ddev);
- di_park_firmware(ddev);
+ /* calm things down */
+ di_spin_down_drive(ddev);
- /* re-enable debug commands */
- di_enable_debug_commands(ddev);
+ /* disable any alien drive code */
+ di_enable_debug_commands(ddev);
+ di_park_firmware(ddev);
- /* load our own drive code extensions */
- di_fw_patch(ddev, ddev->drive_code, 1);
+ /* re-enable debug commands */
+ di_enable_debug_commands(ddev);
- /*
- * The drive will become interoperable now.
- * Here we go...!
- */
- set_bit(__DI_INTEROPERABLE, &ddev->flags);
- di_op_func(&cmd, ddev, DI_DRIVE_CODE_BASE);
- di_run_command_and_wait(&cmd);
+ /* load our own drive code extensions */
+ di_fw_patch(ddev, ddev->drive_code, 1);
- /* this checks if drive is still working... */
- di_get_drive_status(ddev);
- }
+ /*
+ * The drive will become interoperable now.
+ * Here we go...!
+ */
+ set_bit(__DI_INTEROPERABLE, &ddev->flags);
+ di_op_func(&cmd, ddev, DI_DRIVE_CODE_BASE);
+ di_run_command_and_wait(&cmd);
+
+ /* this checks if drive is still working... */
+ di_get_drive_status(ddev);
}
/*
* Ensures that the debug features of the drive firmware are working.
*/
-static int di_test_debug_features(struct di_device *ddev)
+static int di_probe_debug_features(struct di_device *ddev)
{
int result;
+ /* do nothing on unknown drive models */
+ if (!ddev->drive_code)
+ return -EINVAL;
+
result = di_enable_debug_commands(ddev);
if (!di_result_ok(result)) {
DBG("uhmm, debug commands seem banned...\n");
@@ -1582,66 +1637,25 @@
}
/*
- * Tries to determine if firmware extensions are currently installed.
- * Requires debug mode enabled.
- */
-static int di_has_alien_drive_code(struct di_device *ddev)
-{
- unsigned long address;
- int result = 1;
-
- /*
- * We assume that alien drive code is in place if the interrupt handler
- * is not pointing to ROM address space.
- */
- address = di_fw_get_irq_handler(ddev);
- if ((le32_to_cpu(address) & 0xffff0000) == 0x00080000)
- result = 0;
-
- return result;
-}
-
-/*
- * Checks if a drivechip, modchip or custom firmware is present.
- *
- * We consider a drivechip a piece of hardware that automatically patches
- * the laser unit firmware on reset.
+ * Probes the existing firmware features and determines the best operation
+ * mode.
*/
-static void di_check_for_addons(struct di_device *ddev)
+static void di_probe_firmware(struct di_device *ddev)
{
- unsigned long fingerprint;
-
- /* this also enables the debug mode */
- di_test_debug_features(ddev);
-
- if (di_has_alien_drive_code(ddev)) {
- di_printk(KERN_INFO, "alien drive code detected\n");
-
- /*
- * Test if a xenogc/duoq is installed.
- */
- if (!di_fw_read_meml(ddev, &fingerprint, 0x40c60a)) {
- if (fingerprint == 0xf710fff7) {
- di_printk(KERN_INFO, "drivechip: "
- "xenogc/duoq\n");
- set_bit(__DI_DRIVECHIP_PRESENT, &ddev->flags);
- }
- }
+ if (di_probe_debug_features(ddev)) {
+ /* we can't use debug features, thus try to avoid them */
+ di_printk(KERN_INFO, "firmware: debug features do not work,"
+ " using standard command set\n");
+ set_bit(__DI_AVOID_DEBUG, &ddev->flags);
} else {
- /*
- * We avoid the first drive reset if no custom
- * firmware was found.
- */
- set_bit(__DI_ALREADY_RESET, &ddev->flags);
+ if (di_has_alien_drive_code(ddev)) {
+ di_printk(KERN_INFO, "firmware: patched drive\n");
+ /* enable some workarounds if required */
+ di_init_alien_drive_code_quirks(ddev);
+ } else {
+ di_printk(KERN_INFO, "firmware: unpatched drive\n");
+ }
}
-
- /*
- * Some optimizations for a fast startup...
- * Try to make the drive interoperable only if the drive
- * has not accepted the disc yet.
- */
- if (!di_is_drive_ready(ddev))
- di_make_interoperable(ddev);
}
/*
@@ -1692,36 +1706,51 @@
{
struct di_command cmd;
u32 drive_status;
+ unsigned int attempts = 2;
- if (test_bit(__DI_INTEROPERABLE, &ddev->flags)) {
- /* do nothing if the drive is already spinning */
- if (di_is_drive_ready(ddev))
- goto out;
- } else {
- di_make_interoperable(ddev);
- }
+ /* do nothing if the drive is already spinning */
+ if (di_is_drive_ready(ddev))
+ goto out;
- /*
- * We only re-enable the extensions if the drive is not
- * in a pending read disk id state. Otherwise, we assume
- * that the drive has already accepted the disk.
- */
- drive_status = di_get_drive_status(ddev);
- if (DI_STATUS(drive_status) != DI_STATUS_DISK_ID_NOT_READ) {
- di_op_enableextensions(&cmd, ddev, enable_extensions);
- di_run_command_and_wait(&cmd);
- }
+ if (test_bit(__DI_AVOID_DEBUG, &ddev->flags)) {
+ /* don't use debug commands, let's hope a drivechip is there */
+ di_reset(ddev);
+ } else {
+ while(attempts-- > 0) {
+ if (!test_bit(__DI_INTEROPERABLE, &ddev->flags))
+ di_make_interoperable(ddev);
- /* the spin motor command requires the debug mode */
- di_enable_debug_commands(ddev);
+ /*
+ * We only re-enable the extensions if the drive is not
+ * in a pending read disk id state. Otherwise, we assume
+ * that the drive has already accepted the disk.
+ */
+ drive_status = di_get_drive_status(ddev);
+ if (DI_STATUS(drive_status) !=
+ DI_STATUS_DISK_ID_NOT_READ) {
+ di_op_enableextensions(&cmd, ddev,
+ enable_extensions);
+ di_run_command_and_wait(&cmd);
+ }
- di_op_spinmotor(&cmd, ddev, DI_SPINMOTOR_UP);
- di_run_command_and_wait(&cmd);
+ /* the spin motor command requires the debug mode */
+ di_enable_debug_commands(ddev);
+ di_op_spinmotor(&cmd, ddev, DI_SPINMOTOR_UP);
+ di_run_command_and_wait(&cmd);
- if (!ddev->drive_status) {
- di_op_setstatus(&cmd, ddev, DI_STATUS_DISK_ID_NOT_READ+1);
- cmd.cmdbuf0 |= 0x00000300; /* XXX cheqmate */
- di_run_command_and_wait(&cmd);
+ if (!ddev->drive_status) {
+ di_op_setstatus(&cmd, ddev,
+ DI_STATUS_DISK_ID_NOT_READ+1);
+ cmd.cmdbuf0 |= 0x00000300; /* XXX cheqmate */
+ di_run_command_and_wait(&cmd);
+ } else {
+ if (DI_ERROR(ddev->drive_status) !=
+ DI_ERROR_MEDIUM_NOT_PRESENT)
+ di_reset(ddev);
+ continue;
+ }
+ break;
+ }
}
out:
return;
@@ -2117,24 +2146,21 @@
spin_lock_irqsave(&ddev->io_lock, flags);
- sr = readl(sr_reg);
+ sr = in_be32(sr_reg);
sr |= DI_SR_BRKINT | DI_SR_TCINT | DI_SR_DEINT;
sr |= DI_SR_BRKINTMASK | DI_SR_TCINTMASK | DI_SR_DEINTMASK;
- writel(sr, sr_reg);
+ out_be32(sr_reg, sr);
- cvr = readl(cvr_reg);
- writel(cvr | DI_CVR_CVRINT | DI_CVR_CVRINTMASK, cvr_reg);
+ cvr = in_be32(cvr_reg);
+ out_be32(cvr_reg, cvr | DI_CVR_CVRINT | DI_CVR_CVRINTMASK);
spin_unlock_irqrestore(&ddev->io_lock, flags);
di_retrieve_drive_model(ddev);
- if (di_select_drive_code(ddev)) {
- free_irq(ddev->irq, ddev);
- retval = -ENODEV;
- goto out;
- }
+ di_select_drive_code(ddev);
+
+ di_probe_firmware(ddev);
- di_check_for_addons(ddev);
di_schedule_motor_off(ddev, DI_MOTOR_OFF_TIMEOUT);
out:
@@ -2330,7 +2356,7 @@
/*
- * Drive model probe function for our device.
+ * Driver model probe function for our device.
*/
static int di_probe(struct device *device)
{
@@ -2350,7 +2376,7 @@
}
/*
- * Drive model remove function for our device.
+ * Driver model remove function for our device.
*/
static int di_remove(struct device *device)
{
@@ -2363,7 +2389,7 @@
}
/*
- * Drive model shutdown function for our device.
+ * Driver model shutdown function for our device.
*/
static void di_shutdown(struct device *device)
{
|