libata: Add Intel SCH PATA driver
authorAlek Du <alek.du@intel.com>
Tue, 6 May 2008 13:31:41 +0000 (21:31 +0800)
committerJeff Garzik <jgarzik@redhat.com>
Tue, 6 May 2008 15:33:58 +0000 (11:33 -0400)
This patch adds Intel SCH chipsets (AF82US15W, AF82US15L, AF82UL11L)
PATA controller support.

Signed-off-by: Alek Du <alek.du@intel.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/ata/Kconfig
drivers/ata/Makefile
drivers/ata/pata_sch.c [new file with mode: 0644]

index 1c11df9a5f32a92e3ff7fda7700450deccf228b4..1a59f305f7db3230e4a55ca43e73cd642c22acaf 100644 (file)
@@ -697,6 +697,15 @@ config PATA_SCC
 
          If unsure, say N.
 
+config PATA_SCH
+       tristate "Intel SCH PATA support"
+       depends on PCI
+       help
+         This option enables support for Intel SCH PATA on the Intel
+         SCH (US15W, US15L, UL11L) series host controllers.
+
+         If unsure, say N.
+
 config PATA_BF54X
        tristate "Blackfin 54x ATAPI support"
        depends on BF542 || BF548 || BF549
index b693d829383a3b7ff9e33bd0d181523f23297379..674965fa326dafa621311ed92dfcc91d80380296 100644 (file)
@@ -67,6 +67,7 @@ obj-$(CONFIG_PATA_SIS)                += pata_sis.o
 obj-$(CONFIG_PATA_TRIFLEX)     += pata_triflex.o
 obj-$(CONFIG_PATA_IXP4XX_CF)   += pata_ixp4xx_cf.o
 obj-$(CONFIG_PATA_SCC)         += pata_scc.o
+obj-$(CONFIG_PATA_SCH)         += pata_sch.o
 obj-$(CONFIG_PATA_BF54X)       += pata_bf54x.o
 obj-$(CONFIG_PATA_PLATFORM)    += pata_platform.o
 obj-$(CONFIG_PATA_OF_PLATFORM) += pata_of_platform.o
diff --git a/drivers/ata/pata_sch.c b/drivers/ata/pata_sch.c
new file mode 100644 (file)
index 0000000..c8cc027
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ *  pata_sch.c - Intel SCH PATA controllers
+ *
+ *  Copyright (c) 2008 Alek Du <alek.du@intel.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ *  Supports:
+ *    Intel SCH (AF82US15W, AF82US15L, AF82UL11L) chipsets -- see spec at:
+ *    http://download.intel.com/design/chipsets/embedded/datashts/319537.pdf
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+#include <linux/dmi.h>
+
+#define DRV_NAME       "pata_sch"
+#define DRV_VERSION    "0.2"
+
+/* see SCH datasheet page 351 */
+enum {
+       D0TIM   = 0x80,         /* Device 0 Timing Register */
+       D1TIM   = 0x84,         /* Device 1 Timing Register */
+       PM      = 0x07,         /* PIO Mode Bit Mask */
+       MDM     = (0x03 << 8),  /* Multi-word DMA Mode Bit Mask */
+       UDM     = (0x07 << 16), /* Ultra DMA Mode Bit Mask */
+       PPE     = (1 << 30),    /* Prefetch/Post Enable */
+       USD     = (1 << 31),    /* Use Synchronous DMA */
+};
+
+static int sch_init_one(struct pci_dev *pdev,
+                        const struct pci_device_id *ent);
+static void sch_set_piomode(struct ata_port *ap, struct ata_device *adev);
+static void sch_set_dmamode(struct ata_port *ap, struct ata_device *adev);
+
+static const struct pci_device_id sch_pci_tbl[] = {
+       /* Intel SCH PATA Controller */
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SCH_IDE), 0 },
+       { }     /* terminate list */
+};
+
+static struct pci_driver sch_pci_driver = {
+       .name                   = DRV_NAME,
+       .id_table               = sch_pci_tbl,
+       .probe                  = sch_init_one,
+       .remove                 = ata_pci_remove_one,
+#ifdef CONFIG_PM
+       .suspend                = ata_pci_device_suspend,
+       .resume                 = ata_pci_device_resume,
+#endif
+};
+
+static struct scsi_host_template sch_sht = {
+       ATA_BMDMA_SHT(DRV_NAME),
+};
+
+static struct ata_port_operations sch_pata_ops = {
+       .inherits               = &ata_bmdma_port_ops,
+       .cable_detect           = ata_cable_unknown,
+       .set_piomode            = sch_set_piomode,
+       .set_dmamode            = sch_set_dmamode,
+};
+
+static struct ata_port_info sch_port_info = {
+       .flags          = 0,
+       .pio_mask       = ATA_PIO4,   /* pio0-4 */
+       .mwdma_mask     = ATA_MWDMA2, /* mwdma0-2 */
+       .udma_mask      = ATA_UDMA5,  /* udma0-5 */
+       .port_ops       = &sch_pata_ops,
+};
+
+MODULE_AUTHOR("Alek Du <alek.du@intel.com>");
+MODULE_DESCRIPTION("SCSI low-level driver for Intel SCH PATA controllers");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, sch_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+/**
+ *     sch_set_piomode - Initialize host controller PATA PIO timings
+ *     @ap: Port whose timings we are configuring
+ *     @adev: ATA device
+ *
+ *     Set PIO mode for device, in host controller PCI config space.
+ *
+ *     LOCKING:
+ *     None (inherited from caller).
+ */
+
+static void sch_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+       unsigned int pio        = adev->pio_mode - XFER_PIO_0;
+       struct pci_dev *dev     = to_pci_dev(ap->host->dev);
+       unsigned int port       = adev->devno ? D1TIM : D0TIM;
+       unsigned int data;
+
+       pci_read_config_dword(dev, port, &data);
+       /* see SCH datasheet page 351 */
+       /* set PIO mode */
+       data &= ~(PM | PPE);
+       data |= pio;
+       /* enable PPE for block device */
+       if (adev->class == ATA_DEV_ATA)
+               data |= PPE;
+       pci_write_config_dword(dev, port, data);
+}
+
+/**
+ *     sch_set_dmamode - Initialize host controller PATA DMA timings
+ *     @ap: Port whose timings we are configuring
+ *     @adev: ATA device
+ *
+ *     Set MW/UDMA mode for device, in host controller PCI config space.
+ *
+ *     LOCKING:
+ *     None (inherited from caller).
+ */
+
+static void sch_set_dmamode(struct ata_port *ap, struct ata_device *adev)
+{
+       unsigned int dma_mode   = adev->dma_mode;
+       struct pci_dev *dev     = to_pci_dev(ap->host->dev);
+       unsigned int port       = adev->devno ? D1TIM : D0TIM;
+       unsigned int data;
+
+       pci_read_config_dword(dev, port, &data);
+       /* see SCH datasheet page 351 */
+       if (dma_mode >= XFER_UDMA_0) {
+               /* enable Synchronous DMA mode */
+               data |= USD;
+               data &= ~UDM;
+               data |= (dma_mode - XFER_UDMA_0) << 16;
+       } else { /* must be MWDMA mode, since we masked SWDMA already */
+               data &= ~(USD | MDM);
+               data |= (dma_mode - XFER_MW_DMA_0) << 8;
+       }
+       pci_write_config_dword(dev, port, data);
+}
+
+/**
+ *     sch_init_one - Register SCH ATA PCI device with kernel services
+ *     @pdev: PCI device to register
+ *     @ent: Entry in sch_pci_tbl matching with @pdev
+ *
+ *     LOCKING:
+ *     Inherited from PCI layer (may sleep).
+ *
+ *     RETURNS:
+ *     Zero on success, or -ERRNO value.
+ */
+
+static int __devinit sch_init_one(struct pci_dev *pdev,
+                                  const struct pci_device_id *ent)
+{
+       static int printed_version;
+       const struct ata_port_info *ppi[] = { &sch_port_info, NULL };
+       struct ata_host *host;
+       int rc;
+
+       if (!printed_version++)
+               dev_printk(KERN_DEBUG, &pdev->dev,
+                          "version " DRV_VERSION "\n");
+
+       /* enable device and prepare host */
+       rc = pcim_enable_device(pdev);
+       if (rc)
+               return rc;
+       rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
+       if (rc)
+               return rc;
+       pci_set_master(pdev);
+       return ata_pci_sff_activate_host(host, ata_sff_interrupt, &sch_sht);
+}
+
+static int __init sch_init(void)
+{
+       return pci_register_driver(&sch_pci_driver);
+}
+
+static void __exit sch_exit(void)
+{
+       pci_unregister_driver(&sch_pci_driver);
+}
+
+module_init(sch_init);
+module_exit(sch_exit);