Skip to content

Commit

Permalink
sata_via: fix support for 5287
Browse files Browse the repository at this point in the history
5287 used to be treated as vt6420 but it didn't work.  It's new family
of controllers called vt8251 which hosts four SATA ports as M/S of the
two ATA ports.  This configuration is rather peculiar in that although
the M/S devices are on the same port, each have its own SCR (or
equivalent link status/control) registers which screws up the
port-link-device hierarchy assumed by libata.  Another controller
which falls into this category is ata_piix w/ SIDPR access.

libata now has facility to deal with this class of controllers named
slave_link.  A low level driver for such controllers can just call
ata_slave_link_init() on the respective ports and libata will handle
all the difficult parts like following up with single SRST after
hardresetting both ports.

This patch creates new controller class vt8251, implements slave_link
aware init sequence and config space based SCR access for it and moves
5287 to the new class.

This patch is based on Joseph Chan's larger patch which was created
before slave_link was implemented in libata.

  http://thread.gmane.org/gmane.linux.kernel.commits.mm/40640

Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Joseph Chan <JosephChan@via.com.tw>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
  • Loading branch information
Tejun Heo authored and Jeff Garzik committed Oct 31, 2008
1 parent ba14a9c commit b9d5b89
Showing 1 changed file with 143 additions and 12 deletions.
155 changes: 143 additions & 12 deletions drivers/ata/sata_via.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
Expand All @@ -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 */
};
Expand Down Expand Up @@ -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,
Expand All @@ -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");
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) ||
Expand All @@ -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);

Expand Down

0 comments on commit b9d5b89

Please sign in to comment.