diff options
| author | Tejun Heo <htejun@gmail.com> | 2006-08-22 12:00:27 -0400 |
|---|---|---|
| committer | Jeff Garzik <jeff@garzik.org> | 2006-08-24 02:51:24 -0400 |
| commit | ac2164d5e425fa4755bdbab9641d8dab7239b6f5 (patch) | |
| tree | 4576ca4b573e7c7bcb197f934ef3da35c2457d0c /drivers | |
| parent | 9dd9c16465c82d1385f97d2a245641464fcb7894 (diff) | |
[PATCH] sata_via: use old SCR access pattern on vt6420
vt6420 has super-fragile SCR registers which can hang the whole
machine if accessed with the wrong timings. This patch makes sata_via
use SCR registers only during probing and with the same timings as
before (pre new EH), which is proven to work.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/scsi/sata_via.c | 117 |
1 files changed, 112 insertions, 5 deletions
diff --git a/drivers/scsi/sata_via.c b/drivers/scsi/sata_via.c index 03baec2191bf..01d40369a8a5 100644 --- a/drivers/scsi/sata_via.c +++ b/drivers/scsi/sata_via.c | |||
| @@ -74,6 +74,7 @@ enum { | |||
| 74 | static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); | 74 | static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); |
| 75 | static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg); | 75 | static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg); |
| 76 | static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); | 76 | static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); |
| 77 | static void vt6420_error_handler(struct ata_port *ap); | ||
| 77 | 78 | ||
| 78 | static const struct pci_device_id svia_pci_tbl[] = { | 79 | static const struct pci_device_id svia_pci_tbl[] = { |
| 79 | { 0x1106, 0x3149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6420 }, | 80 | { 0x1106, 0x3149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, vt6420 }, |
| @@ -107,7 +108,38 @@ static struct scsi_host_template svia_sht = { | |||
| 107 | .bios_param = ata_std_bios_param, | 108 | .bios_param = ata_std_bios_param, |
| 108 | }; | 109 | }; |
| 109 | 110 | ||
| 110 | static const struct ata_port_operations svia_sata_ops = { | 111 | static const struct ata_port_operations vt6420_sata_ops = { |
| 112 | .port_disable = ata_port_disable, | ||
| 113 | |||
| 114 | .tf_load = ata_tf_load, | ||
| 115 | .tf_read = ata_tf_read, | ||
| 116 | .check_status = ata_check_status, | ||
| 117 | .exec_command = ata_exec_command, | ||
| 118 | .dev_select = ata_std_dev_select, | ||
| 119 | |||
| 120 | .bmdma_setup = ata_bmdma_setup, | ||
| 121 | .bmdma_start = ata_bmdma_start, | ||
| 122 | .bmdma_stop = ata_bmdma_stop, | ||
| 123 | .bmdma_status = ata_bmdma_status, | ||
| 124 | |||
| 125 | .qc_prep = ata_qc_prep, | ||
| 126 | .qc_issue = ata_qc_issue_prot, | ||
| 127 | .data_xfer = ata_pio_data_xfer, | ||
| 128 | |||
| 129 | .freeze = ata_bmdma_freeze, | ||
| 130 | .thaw = ata_bmdma_thaw, | ||
| 131 | .error_handler = vt6420_error_handler, | ||
| 132 | .post_internal_cmd = ata_bmdma_post_internal_cmd, | ||
| 133 | |||
| 134 | .irq_handler = ata_interrupt, | ||
| 135 | .irq_clear = ata_bmdma_irq_clear, | ||
| 136 | |||
| 137 | .port_start = ata_port_start, | ||
| 138 | .port_stop = ata_port_stop, | ||
| 139 | .host_stop = ata_host_stop, | ||
| 140 | }; | ||
| 141 | |||
| 142 | static const struct ata_port_operations vt6421_sata_ops = { | ||
| 111 | .port_disable = ata_port_disable, | 143 | .port_disable = ata_port_disable, |
| 112 | 144 | ||
| 113 | .tf_load = ata_tf_load, | 145 | .tf_load = ata_tf_load, |
| @@ -141,13 +173,13 @@ static const struct ata_port_operations svia_sata_ops = { | |||
| 141 | .host_stop = ata_host_stop, | 173 | .host_stop = ata_host_stop, |
| 142 | }; | 174 | }; |
| 143 | 175 | ||
| 144 | static struct ata_port_info svia_port_info = { | 176 | static struct ata_port_info vt6420_port_info = { |
| 145 | .sht = &svia_sht, | 177 | .sht = &svia_sht, |
| 146 | .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, | 178 | .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, |
| 147 | .pio_mask = 0x1f, | 179 | .pio_mask = 0x1f, |
| 148 | .mwdma_mask = 0x07, | 180 | .mwdma_mask = 0x07, |
| 149 | .udma_mask = 0x7f, | 181 | .udma_mask = 0x7f, |
| 150 | .port_ops = &svia_sata_ops, | 182 | .port_ops = &vt6420_sata_ops, |
| 151 | }; | 183 | }; |
| 152 | 184 | ||
| 153 | MODULE_AUTHOR("Jeff Garzik"); | 185 | MODULE_AUTHOR("Jeff Garzik"); |
| @@ -170,6 +202,81 @@ static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) | |||
| 170 | outl(val, ap->ioaddr.scr_addr + (4 * sc_reg)); | 202 | outl(val, ap->ioaddr.scr_addr + (4 * sc_reg)); |
| 171 | } | 203 | } |
| 172 | 204 | ||
| 205 | /** | ||
| 206 | * vt6420_prereset - prereset for vt6420 | ||
| 207 | * @ap: target ATA port | ||
| 208 | * | ||
| 209 | * SCR registers on vt6420 are pieces of shit and may hang the | ||
| 210 | * whole machine completely if accessed with the wrong timing. | ||
| 211 | * To avoid such catastrophe, vt6420 doesn't provide generic SCR | ||
| 212 | * access operations, but uses SStatus and SControl only during | ||
| 213 | * boot probing in controlled way. | ||
| 214 | * | ||
| 215 | * As the old (pre EH update) probing code is proven to work, we | ||
| 216 | * strictly follow the access pattern. | ||
| 217 | * | ||
| 218 | * LOCKING: | ||
| 219 | * Kernel thread context (may sleep) | ||
| 220 | * | ||
| 221 | * RETURNS: | ||
| 222 | * 0 on success, -errno otherwise. | ||
| 223 | */ | ||
| 224 | static int vt6420_prereset(struct ata_port *ap) | ||
| 225 | { | ||
| 226 | struct ata_eh_context *ehc = &ap->eh_context; | ||
| 227 | unsigned long timeout = jiffies + (HZ * 5); | ||
| 228 | u32 sstatus, scontrol; | ||
| 229 | int online; | ||
| 230 | |||
| 231 | /* don't do any SCR stuff if we're not loading */ | ||
| 232 | if (!ATA_PFLAG_LOADING) | ||
| 233 | goto skip_scr; | ||
| 234 | |||
| 235 | /* Resume phy. This is the old resume sequence from | ||
| 236 | * __sata_phy_reset(). | ||
| 237 | */ | ||
| 238 | svia_scr_write(ap, SCR_CONTROL, 0x300); | ||
| 239 | svia_scr_read(ap, SCR_CONTROL); /* flush */ | ||
| 240 | |||
| 241 | /* wait for phy to become ready, if necessary */ | ||
| 242 | do { | ||
| 243 | msleep(200); | ||
| 244 | if ((svia_scr_read(ap, SCR_STATUS) & 0xf) != 1) | ||
| 245 | break; | ||
| 246 | } while (time_before(jiffies, timeout)); | ||
| 247 | |||
| 248 | /* open code sata_print_link_status() */ | ||
| 249 | sstatus = svia_scr_read(ap, SCR_STATUS); | ||
| 250 | scontrol = svia_scr_read(ap, SCR_CONTROL); | ||
| 251 | |||
| 252 | online = (sstatus & 0xf) == 0x3; | ||
| 253 | |||
| 254 | ata_port_printk(ap, KERN_INFO, | ||
| 255 | "SATA link %s 1.5 Gbps (SStatus %X SControl %X)\n", | ||
| 256 | online ? "up" : "down", sstatus, scontrol); | ||
| 257 | |||
| 258 | /* SStatus is read one more time */ | ||
| 259 | svia_scr_read(ap, SCR_STATUS); | ||
| 260 | |||
| 261 | if (!online) { | ||
| 262 | /* tell EH to bail */ | ||
| 263 | ehc->i.action &= ~ATA_EH_RESET_MASK; | ||
| 264 | return 0; | ||
| 265 | } | ||
| 266 | |||
| 267 | skip_scr: | ||
| 268 | /* wait for !BSY */ | ||
| 269 | ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); | ||
| 270 | |||
| 271 | return 0; | ||
| 272 | } | ||
| 273 | |||
| 274 | static void vt6420_error_handler(struct ata_port *ap) | ||
| 275 | { | ||
| 276 | return ata_bmdma_drive_eh(ap, vt6420_prereset, ata_std_softreset, | ||
| 277 | NULL, ata_std_postreset); | ||
| 278 | } | ||
| 279 | |||
| 173 | static const unsigned int svia_bar_sizes[] = { | 280 | static const unsigned int svia_bar_sizes[] = { |
| 174 | 8, 4, 8, 4, 16, 256 | 281 | 8, 4, 8, 4, 16, 256 |
| 175 | }; | 282 | }; |
| @@ -210,7 +317,7 @@ static void vt6421_init_addrs(struct ata_probe_ent *probe_ent, | |||
| 210 | static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev) | 317 | static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev) |
| 211 | { | 318 | { |
| 212 | struct ata_probe_ent *probe_ent; | 319 | struct ata_probe_ent *probe_ent; |
| 213 | struct ata_port_info *ppi = &svia_port_info; | 320 | struct ata_port_info *ppi = &vt6420_port_info; |
| 214 | 321 | ||
| 215 | probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); | 322 | probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); |
| 216 | if (!probe_ent) | 323 | if (!probe_ent) |
| @@ -239,7 +346,7 @@ static struct ata_probe_ent *vt6421_init_probe_ent(struct pci_dev *pdev) | |||
| 239 | 346 | ||
| 240 | probe_ent->sht = &svia_sht; | 347 | probe_ent->sht = &svia_sht; |
| 241 | probe_ent->host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY; | 348 | probe_ent->host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY; |
| 242 | probe_ent->port_ops = &svia_sata_ops; | 349 | probe_ent->port_ops = &vt6421_sata_ops; |
| 243 | probe_ent->n_ports = N_PORTS; | 350 | probe_ent->n_ports = N_PORTS; |
| 244 | probe_ent->irq = pdev->irq; | 351 | probe_ent->irq = pdev->irq; |
| 245 | probe_ent->irq_flags = IRQF_SHARED; | 352 | probe_ent->irq_flags = IRQF_SHARED; |
