Support LQ: Use code LQ3 and save $3 on Domain Registration
Go Back > Linux Answers > Hardware
User Name


By Erik_FL at 2006-10-01 10:37
If you own a motherboard with the Promise PDC20378 FastTrack 374 SATA/RAID controller you probably know the same frustration as me. Part of the problem with the PDC20378 and Linux is the fact that the PDC20378 has both SATA and PATA ports in the same device. The standard Linux implementation for ATA devices does not support a device with some SATA and some PATA ports. Devices are either SATA or PATA (not BOTH). Add to that the fact that Linux does not understand the RAID array metadata for the PDC20378. Here are some of the issues with the PDC20378.
  • Promise has a proprietary RAID driver only for kernel version 2.4
  • Linux only has an SATA driver (PATA port not supported)
  • The Linux developers have no definite plans for PATA support on the PDC20378
  • The few patches for PATA support are not for the "standard" kernel sources (only modified distros)

I have found a number of things that can provide solutions to some of these problems. For one thing, I reverse engineered the UBUNTU patch for PATA support and created a patch for the "standard" 2.6.17 version of the Linux kernel. I had to do that to support my RAID 0+1 configuration. I have two of my hard disks on the PATA port of my PDC20378

The whole patch appears at the end of this post.

Here are some solutions to using the PDC20378 in various configurations.

For the "standard" Linux kernel version 2.4 you can support one of these configurations.
  • Two SATA disks using standard Linux sata_promise driver
  • RAID 0, 1, or 0+1 on SATA and/or PATA using proprietary Promise ft3xx driver
  • Any kind of Linux software RAID on SATA

If you can find a distro patched for PATA support you can support these additional configurations.
  • Two SATA disks and/or two PATA disks
  • Any kind of Linux software RAID on SATA and/or PATA

With kernel version 2.4 you have basically three different choices of drivers. If you want to use the "fake simulated hardware RAID" implemented in the BIOS and Windows driver, you can download the proprietary "ft3xx" driver from the Promise web site. The "ft3xx" driver supports SATA and/or PATA, but ONLY in a RAID configuration. You can download a driver for Suse Linux, or the partial sources to compile your own. You can use the "standard" Linux driver, called "sata_proimse" that only supports two SATA drives (no PATA) in a non-RAID configuration. You can possibly find an older patched version of "sata_promise" in a 2.4 distro such as UBUNTU, FC or GENTOO that supports the PATA drives in addition to SATA. You can use the Linux software RAID with the "sata_promise" driver.

Assuming you decide to use the "ft3xx" driver, the process is quite involved. I posted a previous thread here about how to do that. Basically you have to compile and test the "ft3xx" driver using the partial sources from Promise. You probably need a normal hard disk on some other controller to do that. You have to create an "initrd" image that loads the "ft3xx" driver, since the driver will ONLY build as a module, not as part of the kernel.

Here is a link to download the "ft3xx" sources or Suse driver.

The "sata_promise" driver is part of the "standard" Linux distro, but Linux only officially supports two SATA drives. You can easily build that driver in the normal way. The "sata_proimse" driver does not understand the "hardware RAID" sets, created by the BIOS and accessed by the Windows RAID driver for the PDC20378. Of course you can use the Linux software RAID, treating the PDC20378 as a "normal" SATA controller.

If you can find a version of 2.4 that has the appropriate patch, you can build that driver and have PATA drives along with the SATA drives. I use the "ft3xx" driver with version 2.4 and didn't try to find or develop a patch for that version of "sata_promise".

For version 2.6 of the kernel, your options are more limited. Promise has made no effort to update the "ft3xx" driver for 2.6 and has made no promise either. I looked at the effort involved in updating their driver and concluded that it was considerable, and might not even be possible. Promise does not provide all of the sources, only some of the sources.

Here are the possibilities with the "standard" Linux kernel version 2.6
  • Two SATA disks using the standard Linux "sata_promise" driver
  • RAID 0 or RAID 1 on SATA (NOT RAID 0+1) using "dmraid"
  • Any kind of Linux software RAID on SATA using "sata_promise"

If you use the patch for PATA support you have these additional possibilities for 2.6
  • Two SATA disks and/or two PATA disks
  • RAID 0, RAID 1, or RAID 0+1 using "dmraid"

I happen to use the Slackware distro that is essentially the unmodified Linux kernel. Since no "standard" Linux kernel release supports PATA in the "sata_promise" driver, I could not access my PATA drives, and could not access the mirror drives for my RAID 0+1 configuration. That's why I finally updated the patch for the driver. I chose 2.6.17 because it was the last stable release and is working well for me. 2.6.18 has not yet been officially released (that I know of). NOTE: No known plans for the "standard" 2.6.18 to support PATA on the PDC20378.

To use the "fake hardware RAID" provided by the BIOS and Windows RAID driver you need the "dmraid" utility. The "dmraid" program is essentially a RAID set detection and disk mapping program. It works in conjunction with the device-mapper in Linux to map the correct areas of the RAID volumes to "mapped" devices and partitions in "/dev/mapper".
You can download the "dmraid" program from

I don't claim to have invented the PATA patch for the "sata_proimse" driver. Someone else was kind enough to do that, and others ported it to a few distros. What I did was edit the standard 2.6.17 version of "sata_proimse" and create the appropriate patch. The real question is, "Will this change ever be put in the standard Linux kernel?". So far that doesn't appear promising.

Here is the PATA patch.
WARNING: I have done limited testing to verify correct operation in my configuration (both read and write). Use at your own risk. This patch is not officially supported but I will try to help if anyone has problems. I recommend starting by mounting any filesystems in read-only mode and checking carefully for problems.

--- oldlinux/drivers/scsi/libata-core.c	2006-08-30 17:03:45.000000000 -0400
+++ linux/drivers/scsi/libata-core.c	2006-08-30 15:36:00.000000000 -0400
@@ -4453,6 +4453,7 @@
 	ap->mwdma_mask = ent->mwdma_mask;
 	ap->udma_mask = ent->udma_mask;
 	ap->flags |= ent->host_flags;
+	ap->flags |= ent->port_flags[port_no];
 	ap->ops = ent->port_ops;
 	ap->cbl = ATA_CBL_NONE;
 	ap->active_tag = ATA_TAG_POISON;
--- oldlinux/include/linux/libata.h	2006-08-30 17:08:28.000000000 -0400
+++ linux/include/linux/libata.h	2006-08-30 15:36:22.000000000 -0400
@@ -281,6 +281,7 @@
 	unsigned long		irq;
 	unsigned int		irq_flags;
 	unsigned long		host_flags;
+	unsigned long		port_flags[ATA_MAX_PORTS];
 	unsigned long		host_set_flags;
 	void __iomem		*mmio_base;
 	void			*private_data;
--- oldlinux/drivers/scsi/sata_promise.c	2006-08-30 17:06:58.000000000 -0400
+++ linux/drivers/scsi/sata_promise.c	2006-08-30 15:34:45.000000000 -0400
@@ -96,6 +96,7 @@
 static void pdc_eng_timeout(struct ata_port *ap);
 static int pdc_port_start(struct ata_port *ap);
 static void pdc_port_stop(struct ata_port *ap);
+static void pdc_phy_reset(struct ata_port *ap);
 static void pdc_pata_phy_reset(struct ata_port *ap);
 static void pdc_sata_phy_reset(struct ata_port *ap);
 static void pdc_qc_prep(struct ata_queued_cmd *qc);
@@ -131,7 +132,7 @@
 	.exec_command		= pdc_exec_command_mmio,
 	.dev_select		= ata_std_dev_select,
-	.phy_reset		= pdc_sata_phy_reset,
+	.phy_reset		= pdc_phy_reset,
 	.qc_prep		= pdc_qc_prep,
 	.qc_issue		= pdc_qc_issue_prot,
@@ -154,7 +155,7 @@
 	.exec_command		= pdc_exec_command_mmio,
 	.dev_select		= ata_std_dev_select,
-	.phy_reset		= pdc_pata_phy_reset,
+	.phy_reset		= pdc_phy_reset,
 	.qc_prep		= pdc_qc_prep,
 	.qc_issue		= pdc_qc_issue_prot,
@@ -168,10 +169,10 @@
 static const struct ata_port_info pdc_port_info[] = {
-	/* board_2037x */
+	/* board_2037x (two SATA ports and possibly one PATA port) */
 		.sht		= &pdc_ata_sht,
-		.host_flags	= PDC_COMMON_FLAGS | ATA_FLAG_SATA,
+		.host_flags	= PDC_COMMON_FLAGS, /* ATA_FLAG_SATA is port specific */
 		.pio_mask	= 0x1f, /* pio0-4 */
 		.mwdma_mask	= 0x07, /* mwdma0-2 */
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
@@ -357,6 +358,17 @@
 	readl(mmio);	/* flush */
+/* Reset a port that might be either SATA or PATA */
+static void pdc_phy_reset(struct ata_port *ap)
+	/* If it is an SATA port */
+	if (ap->flags & ATA_FLAG_SATA)
+		pdc_sata_phy_reset(ap);
+	/* Else, it is a PATA port */
+	else
+		pdc_pata_phy_reset(ap);
 static void pdc_sata_phy_reset(struct ata_port *ap)
@@ -365,9 +377,17 @@
 static void pdc_pata_phy_reset(struct ata_port *ap)
-	/* FIXME: add cable detect.  Don't assume 40-pin cable */
-	ap->cbl = ATA_CBL_PATA40;
-	ap->udma_mask &= ATA_UDMA_MASK_40C;
+	u8 tmp;
+	void *mmio = (void *) ap->ioaddr.cmd_addr + PDC_CTLSTAT + 0x03;
+	tmp = readb(mmio);
+	if (tmp & 0x01) {
+		ap->cbl = ATA_CBL_PATA40;
+		ap->udma_mask &= ATA_UDMA_MASK_40C;
+	} else {
+		ap->cbl = ATA_CBL_PATA80;
+	}
@@ -672,6 +692,7 @@
 	unsigned int board_idx = (unsigned int) ent->driver_data;
 	int pci_dev_busy = 0;
 	int rc;
+	u8 tmp;
 	if (!printed_version++)
 		dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
@@ -740,14 +761,14 @@
 	probe_ent->port[0].scr_addr = base + 0x400;
 	probe_ent->port[1].scr_addr = base + 0x500;
-	/* notice 4-port boards */
+	/* Board specific initialization */
 	switch (board_idx) {
 	case board_40518:
 		/* Override hotplug offset for SATAII150 */
 		hp->hotplug_offset = PDC2_SATA_PLUG_CSR;
 		/* Fall through */
 	case board_20319:
-       		probe_ent->n_ports = 4;
+       	probe_ent->n_ports = 4;
 		pdc_ata_setup_port(&probe_ent->port[2], base + 0x300);
 		pdc_ata_setup_port(&probe_ent->port[3], base + 0x380);
@@ -758,10 +779,25 @@
 	case board_2057x:
 		/* Override hotplug offset for SATAII150 */
 		hp->hotplug_offset = PDC2_SATA_PLUG_CSR;
-		/* Fall through */
-	case board_2037x:
 		probe_ent->n_ports = 2;
+	case board_2037x:
+		/* First two ports are always SATA */
+		probe_ent->port_flags[0] = ATA_FLAG_SATA;
+		probe_ent->port_flags[1] = ATA_FLAG_SATA;
+		/* Some boards have also PATA port */
+		tmp = readb((void *)(base + PDC_FLASH_CTL+1));
+		if (!(tmp & 0x80)) {
+			probe_ent->n_ports = 3;
+			pdc_ata_setup_port(&probe_ent->port[2], base + 0x300);
+			/* The PATA port can have a slave drive */
+			probe_ent->port_flags[2] = ATA_FLAG_SLAVE_POSS;
+			printk(KERN_INFO DRV_NAME " PATA port found\n");
+		} else {
+       		probe_ent->n_ports = 2;
+		}
+		break;
 	case board_20771:
 		probe_ent->n_ports = 2;

by LinuxChristian on Wed, 2006-10-18 06:58
Hey Erik,

I am trying to apply the patch to a vanilla kernel. I have downloaded the kernel from and have created a bz2 file from your patch. Then i cd to the kernel source dir and use the command:

bzcat /usr/src/linux- | patch -p1
and i get the output:

Patching file drivers/scsi/libata-core.c
patch: **** malformed patch at line 4: ap->mwdma_mask = ent->mwdma_mask;
Am i doing some thing wrong in the way i use the patch? Havn't patched my kernel in a long time.

Hope you know what the problem is

by LinuxChristian on Fri, 2006-10-20 17:24
Fixed now. Tryed to recompile it a couple of times and now it works!

- Christian


All times are GMT -5. The time now is 07:43 AM.

Main Menu

Write for LQ is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration