diff options
-rw-r--r-- | drivers/ata/sata_via.c | 155 |
1 files changed, 143 insertions, 12 deletions
diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index 5b72e734300..62367fe4d5d 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -44,11 +44,16 @@ #include <linux/libata.h> #define DRV_NAME "sata_via" -#define DRV_VERSION "2.3" +#define DRV_VERSION "2.4" +/* + * vt8251 is different from other sata controllers of VIA. It has two + * channels, each channel has both Master and Slave slot. + */ enum board_ids_enum { vt6420, vt6421, + vt8251, }; enum { @@ -70,6 +75,8 @@ enum { static int svia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static int svia_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); static int svia_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); +static int vt8251_scr_read(struct ata_link *link, unsigned int scr, u32 *val); +static int vt8251_scr_write(struct ata_link *link, unsigned int scr, u32 val); static void svia_tf_load(struct ata_port *ap, const struct ata_taskfile *tf); static void svia_noop_freeze(struct ata_port *ap); static int vt6420_prereset(struct ata_link *link, unsigned long deadline); @@ -79,12 +86,12 @@ static void vt6421_set_dma_mode(struct ata_port *ap, struct ata_device *adev); static const struct pci_device_id svia_pci_tbl[] = { { PCI_VDEVICE(VIA, 0x5337), vt6420 }, - { PCI_VDEVICE(VIA, 0x0591), vt6420 }, - { PCI_VDEVICE(VIA, 0x3149), vt6420 }, - { PCI_VDEVICE(VIA, 0x3249), vt6421 }, - { PCI_VDEVICE(VIA, 0x5287), vt6420 }, + { PCI_VDEVICE(VIA, 0x0591), vt6420 }, /* 2 sata chnls (Master) */ + { PCI_VDEVICE(VIA, 0x3149), vt6420 }, /* 2 sata chnls (Master) */ + { PCI_VDEVICE(VIA, 0x3249), vt6421 }, /* 2 sata chnls, 1 pata chnl */ { PCI_VDEVICE(VIA, 0x5372), vt6420 }, { PCI_VDEVICE(VIA, 0x7372), vt6420 }, + { PCI_VDEVICE(VIA, 0x5287), vt8251 }, /* 2 sata chnls (Master/Slave) */ { } /* terminate list */ }; @@ -128,6 +135,13 @@ static struct ata_port_operations vt6421_sata_ops = { .scr_write = svia_scr_write, }; +static struct ata_port_operations vt8251_ops = { + .inherits = &svia_base_ops, + .hardreset = sata_std_hardreset, + .scr_read = vt8251_scr_read, + .scr_write = vt8251_scr_write, +}; + static const struct ata_port_info vt6420_port_info = { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, .pio_mask = 0x1f, @@ -152,6 +166,15 @@ static struct ata_port_info vt6421_pport_info = { .port_ops = &vt6421_pata_ops, }; +static struct ata_port_info vt8251_port_info = { + .flags = ATA_FLAG_SATA | ATA_FLAG_SLAVE_POSS | + ATA_FLAG_NO_LEGACY, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = ATA_UDMA6, + .port_ops = &vt8251_ops, +}; + MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("SCSI low-level driver for VIA SATA controllers"); MODULE_LICENSE("GPL"); @@ -174,6 +197,83 @@ static int svia_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) return 0; } +static int vt8251_scr_read(struct ata_link *link, unsigned int scr, u32 *val) +{ + static const u8 ipm_tbl[] = { 1, 2, 6, 0 }; + struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); + int slot = 2 * link->ap->port_no + link->pmp; + u32 v = 0; + u8 raw; + + switch (scr) { + case SCR_STATUS: + pci_read_config_byte(pdev, 0xA0 + slot, &raw); + + /* read the DET field, bit0 and 1 of the config byte */ + v |= raw & 0x03; + + /* read the SPD field, bit4 of the configure byte */ + if (raw & (1 << 4)) + v |= 0x02 << 4; + else + v |= 0x01 << 4; + + /* read the IPM field, bit2 and 3 of the config byte */ + v |= ipm_tbl[(raw >> 2) & 0x3]; + break; + + case SCR_ERROR: + /* devices other than 5287 uses 0xA8 as base */ + WARN_ON(pdev->device != 0x5287); + pci_read_config_dword(pdev, 0xB0 + slot * 4, &v); + break; + + case SCR_CONTROL: + pci_read_config_byte(pdev, 0xA4 + slot, &raw); + + /* read the DET field, bit0 and bit1 */ + v |= ((raw & 0x02) << 1) | (raw & 0x01); + + /* read the IPM field, bit2 and bit3 */ + v |= ((raw >> 2) & 0x03) << 8; + break; + + default: + return -EINVAL; + } + + *val = v; + return 0; +} + +static int vt8251_scr_write(struct ata_link *link, unsigned int scr, u32 val) +{ + struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); + int slot = 2 * link->ap->port_no + link->pmp; + u32 v = 0; + + switch (scr) { + case SCR_ERROR: + /* devices other than 5287 uses 0xA8 as base */ + WARN_ON(pdev->device != 0x5287); + pci_write_config_dword(pdev, 0xB0 + slot * 4, val); + return 0; + + case SCR_CONTROL: + /* set the DET field */ + v |= ((val & 0x4) >> 1) | (val & 0x1); + + /* set the IPM field */ + v |= ((val >> 8) & 0x3) << 2; + + pci_write_config_byte(pdev, 0xA4 + slot, v); + return 0; + + default: + return -EINVAL; + } +} + /** * svia_tf_load - send taskfile registers to host controller * @ap: Port to which output is sent @@ -396,6 +496,30 @@ static int vt6421_prepare_host(struct pci_dev *pdev, struct ata_host **r_host) return 0; } +static int vt8251_prepare_host(struct pci_dev *pdev, struct ata_host **r_host) +{ + const struct ata_port_info *ppi[] = { &vt8251_port_info, NULL }; + struct ata_host *host; + int i, rc; + + rc = ata_pci_sff_prepare_host(pdev, ppi, &host); + if (rc) + return rc; + *r_host = host; + + rc = pcim_iomap_regions(pdev, 1 << 5, DRV_NAME); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, "failed to iomap PCI BAR 5\n"); + return rc; + } + + /* 8251 hosts four sata ports as M/S of the two channels */ + for (i = 0; i < host->n_ports; i++) + ata_slave_link_init(host->ports[i]); + + return 0; +} + static void svia_configure(struct pci_dev *pdev) { u8 tmp8; @@ -451,10 +575,10 @@ static int svia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; - if (board_id == vt6420) - bar_sizes = &svia_bar_sizes[0]; - else + if (board_id == vt6421) bar_sizes = &vt6421_bar_sizes[0]; + else + bar_sizes = &svia_bar_sizes[0]; for (i = 0; i < ARRAY_SIZE(svia_bar_sizes); i++) if ((pci_resource_start(pdev, i) == 0) || @@ -467,12 +591,19 @@ static int svia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return -ENODEV; } - if (board_id == vt6420) + switch (board_id) { + case vt6420: rc = vt6420_prepare_host(pdev, &host); - else + break; + case vt6421: rc = vt6421_prepare_host(pdev, &host); - if (rc) - return rc; + break; + case vt8251: + rc = vt8251_prepare_host(pdev, &host); + break; + default: + return -EINVAL; + } svia_configure(pdev); |