Files
linux-apfs/drivers/ata/sata_sis.c
T

315 lines
8.1 KiB
C
Raw Normal View History

2005-04-16 15:20:36 -07:00
/*
* sata_sis.c - Silicon Integrated Systems SATA
*
* Maintained by: Uwe Koziolek
* Please ALWAYS copy linux-ide@vger.kernel.org
* on emails.
*
* Copyright 2004 Uwe Koziolek
*
*
2005-08-28 20:18:39 -04:00
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* 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.
*
*
* libata documentation is available via 'make {ps|pdf}docs',
* as Documentation/DocBook/libata.*
*
* Hardware documentation available under NDA.
2005-04-16 15:20:36 -07:00
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
2005-04-16 15:20:36 -07:00
#include <scsi/scsi_host.h>
#include <linux/libata.h>
2007-02-16 01:40:04 -08:00
#include "sis.h"
2005-04-16 15:20:36 -07:00
#define DRV_NAME "sata_sis"
2007-08-31 04:54:06 -04:00
#define DRV_VERSION "1.0"
2005-04-16 15:20:36 -07:00
enum {
sis_180 = 0,
SIS_SCR_PCI_BAR = 5,
/* PCI configuration registers */
SIS_GENCTL = 0x54, /* IDE General Control register */
SIS_SCR_BASE = 0xc0, /* sata0 phy SCR registers */
SIS180_SATA1_OFS = 0x10, /* offset from sata0->sata1 phy regs */
SIS182_SATA1_OFS = 0x20, /* offset from sata0->sata1 phy regs */
SIS_PMR = 0x90, /* port mapping register */
2005-09-08 23:07:29 -04:00
SIS_PMR_COMBINED = 0x30,
2005-04-16 15:20:36 -07:00
/* random bits */
SIS_FLAG_CFGSCR = (1 << 30), /* host flag: SCRs via PCI cfg */
GENCTL_IOMAPPED_SCR = (1 << 26), /* if set, SCRs are in IO space */
};
static int sis_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
2008-07-31 17:02:40 +09:00
static int sis_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
static int sis_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
2005-04-16 15:20:36 -07:00
static const struct pci_device_id sis_pci_tbl[] = {
{ PCI_VDEVICE(SI, 0x0180), sis_180 }, /* SiS 964/180 */
{ PCI_VDEVICE(SI, 0x0181), sis_180 }, /* SiS 964/180 */
{ PCI_VDEVICE(SI, 0x0182), sis_180 }, /* SiS 965/965L */
{ PCI_VDEVICE(SI, 0x0183), sis_180 }, /* SiS 965/965L */
{ PCI_VDEVICE(SI, 0x1182), sis_180 }, /* SiS 966/680 */
{ PCI_VDEVICE(SI, 0x1183), sis_180 }, /* SiS 966/966L/968/680 */
2005-04-16 15:20:36 -07:00
{ } /* terminate list */
};
static struct pci_driver sis_pci_driver = {
.name = DRV_NAME,
.id_table = sis_pci_tbl,
.probe = sis_init_one,
.remove = ata_pci_remove_one,
#ifdef CONFIG_PM_SLEEP
2014-01-01 20:13:45 +00:00
.suspend = ata_pci_device_suspend,
.resume = ata_pci_device_resume,
#endif
2005-04-16 15:20:36 -07:00
};
static struct scsi_host_template sis_sht = {
2008-03-25 12:22:49 +09:00
ATA_BMDMA_SHT(DRV_NAME),
2005-04-16 15:20:36 -07:00
};
2008-03-25 12:22:49 +09:00
static struct ata_port_operations sis_ops = {
.inherits = &ata_bmdma_port_ops,
2005-04-16 15:20:36 -07:00
.scr_read = sis_scr_read,
.scr_write = sis_scr_write,
};
2007-05-04 12:43:58 +02:00
static const struct ata_port_info sis_port_info = {
2011-02-04 22:05:48 +03:00
.flags = ATA_FLAG_SATA,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
2005-04-16 15:20:36 -07:00
.port_ops = &sis_ops,
};
MODULE_AUTHOR("Uwe Koziolek");
2011-10-24 10:38:18 +11:00
MODULE_DESCRIPTION("low-level driver for Silicon Integrated Systems SATA controller");
2005-04-16 15:20:36 -07:00
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, sis_pci_tbl);
MODULE_VERSION(DRV_VERSION);
2009-09-01 23:19:10 +09:00
static unsigned int get_scr_cfg_addr(struct ata_link *link, unsigned int sc_reg)
2005-04-16 15:20:36 -07:00
{
2009-09-01 23:19:10 +09:00
struct ata_port *ap = link->ap;
2007-01-08 16:11:07 +00:00
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
2005-04-16 15:20:36 -07:00
unsigned int addr = SIS_SCR_BASE + (4 * sc_reg);
2007-01-08 16:11:07 +00:00
u8 pmr;
2005-04-16 15:20:36 -07:00
2007-01-08 16:11:07 +00:00
if (ap->port_no) {
2006-12-04 01:34:42 +01:00
switch (pdev->device) {
case 0x0180:
case 0x0181:
pci_read_config_byte(pdev, SIS_PMR, &pmr);
if ((pmr & SIS_PMR_COMBINED) == 0)
addr += SIS180_SATA1_OFS;
break;
2005-09-08 23:07:29 -04:00
case 0x0182:
case 0x0183:
case 0x1182:
addr += SIS182_SATA1_OFS;
break;
2006-12-04 01:34:42 +01:00
}
}
2009-09-01 23:19:10 +09:00
if (link->pmp)
addr += 0x10;
2005-04-16 15:20:36 -07:00
return addr;
}
2008-07-31 17:02:40 +09:00
static u32 sis_scr_cfg_read(struct ata_link *link,
unsigned int sc_reg, u32 *val)
2005-04-16 15:20:36 -07:00
{
2008-07-31 17:02:40 +09:00
struct pci_dev *pdev = to_pci_dev(link->ap->host->dev);
2009-09-01 23:19:10 +09:00
unsigned int cfg_addr = get_scr_cfg_addr(link, sc_reg);
2005-04-16 15:20:36 -07:00
if (sc_reg == SCR_ERROR) /* doesn't exist in PCI cfg space */
return -EINVAL;
2007-10-18 11:53:39 +09:00
pci_read_config_dword(pdev, cfg_addr, val);
return 0;
2005-04-16 15:20:36 -07:00
}
2008-07-31 17:02:40 +09:00
static int sis_scr_cfg_write(struct ata_link *link,
unsigned int sc_reg, u32 val)
2005-04-16 15:20:36 -07:00
{
2008-07-31 17:02:40 +09:00
struct pci_dev *pdev = to_pci_dev(link->ap->host->dev);
2009-09-01 23:19:10 +09:00
unsigned int cfg_addr = get_scr_cfg_addr(link, sc_reg);
2005-09-08 23:07:29 -04:00
2005-04-16 15:20:36 -07:00
pci_write_config_dword(pdev, cfg_addr, val);
return 0;
2005-04-16 15:20:36 -07:00
}
2008-07-31 17:02:40 +09:00
static int sis_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val)
2005-04-16 15:20:36 -07:00
{
2008-07-31 17:02:40 +09:00
struct ata_port *ap = link->ap;
2009-09-01 23:19:10 +09:00
void __iomem *base = ap->ioaddr.scr_addr + link->pmp * 0x10;
2005-04-16 15:20:36 -07:00
if (sc_reg > SCR_CONTROL)
return -EINVAL;
2005-04-16 15:20:36 -07:00
if (ap->flags & SIS_FLAG_CFGSCR)
2008-07-31 17:02:40 +09:00
return sis_scr_cfg_read(link, sc_reg, val);
2009-09-01 23:19:10 +09:00
*val = ioread32(base + sc_reg * 4);
return 0;
2005-04-16 15:20:36 -07:00
}
2008-07-31 17:02:40 +09:00
static int sis_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
2005-04-16 15:20:36 -07:00
{
2008-07-31 17:02:40 +09:00
struct ata_port *ap = link->ap;
2009-09-01 23:19:10 +09:00
void __iomem *base = ap->ioaddr.scr_addr + link->pmp * 0x10;
2005-04-16 15:20:36 -07:00
if (sc_reg > SCR_CONTROL)
return -EINVAL;
2005-04-16 15:20:36 -07:00
if (ap->flags & SIS_FLAG_CFGSCR)
2008-07-31 17:02:40 +09:00
return sis_scr_cfg_write(link, sc_reg, val);
2009-09-01 23:19:10 +09:00
iowrite32(val, base + (sc_reg * 4));
return 0;
2005-04-16 15:20:36 -07:00
}
static int sis_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
2005-04-16 15:20:36 -07:00
{
struct ata_port_info pi = sis_port_info;
2007-05-25 09:48:52 +02:00
const struct ata_port_info *ppi[] = { &pi, &pi };
struct ata_host *host;
2006-11-08 09:57:00 +01:00
u32 genctl, val;
u8 pmr;
2006-12-04 01:34:42 +01:00
u8 port2_start = 0x20;
2009-09-01 23:19:10 +09:00
int i, rc;
2005-04-16 15:20:36 -07:00
2011-04-15 15:52:00 -07:00
ata_print_version_once(&pdev->dev, DRV_VERSION);
2007-01-20 16:00:28 +09:00
rc = pcim_enable_device(pdev);
2005-04-16 15:20:36 -07:00
if (rc)
return rc;
/* check and see if the SCRs are in IO space or PCI cfg space */
pci_read_config_dword(pdev, SIS_GENCTL, &genctl);
if ((genctl & GENCTL_IOMAPPED_SCR) == 0)
pi.flags |= SIS_FLAG_CFGSCR;
2005-07-31 13:13:24 -04:00
2005-04-16 15:20:36 -07:00
/* if hardware thinks SCRs are in IO space, but there are
* no IO resources assigned, change to PCI cfg space.
*/
if ((!(pi.flags & SIS_FLAG_CFGSCR)) &&
2005-04-16 15:20:36 -07:00
((pci_resource_start(pdev, SIS_SCR_PCI_BAR) == 0) ||
(pci_resource_len(pdev, SIS_SCR_PCI_BAR) < 128))) {
genctl &= ~GENCTL_IOMAPPED_SCR;
pci_write_config_dword(pdev, SIS_GENCTL, genctl);
pi.flags |= SIS_FLAG_CFGSCR;
2005-04-16 15:20:36 -07:00
}
pci_read_config_byte(pdev, SIS_PMR, &pmr);
2006-12-04 01:34:42 +01:00
switch (ent->device) {
case 0x0180:
case 0x0181:
2007-01-08 16:11:07 +00:00
/* The PATA-handling is provided by pata_sis */
switch (pmr & 0x30) {
case 0x10:
2007-06-14 23:40:43 +02:00
ppi[1] = &sis_info133_for_sata;
2007-01-08 16:11:07 +00:00
break;
2007-02-26 05:51:33 -05:00
2007-01-08 16:11:07 +00:00
case 0x30:
2007-06-14 23:40:43 +02:00
ppi[0] = &sis_info133_for_sata;
2007-01-08 16:11:07 +00:00
break;
}
if ((pmr & SIS_PMR_COMBINED) == 0) {
dev_info(&pdev->dev,
"Detected SiS 180/181/964 chipset in SATA mode\n");
port2_start = 64;
2006-12-04 01:34:42 +01:00
} else {
dev_info(&pdev->dev,
"Detected SiS 180/181 chipset in combined mode\n");
port2_start = 0;
2006-11-08 09:57:00 +01:00
pi.flags |= ATA_FLAG_SLAVE_POSS;
}
2006-12-04 01:34:42 +01:00
break;
2006-12-11 11:14:06 -05:00
2006-12-04 01:34:42 +01:00
case 0x0182:
case 0x0183:
pci_read_config_dword(pdev, 0x6C, &val);
2006-11-08 09:57:00 +01:00
if (val & (1L << 31)) {
dev_info(&pdev->dev, "Detected SiS 182/965 chipset\n");
2006-11-08 09:57:00 +01:00
pi.flags |= ATA_FLAG_SLAVE_POSS;
2006-12-04 01:34:42 +01:00
} else {
dev_info(&pdev->dev, "Detected SiS 182/965L chipset\n");
2006-12-04 01:34:42 +01:00
}
break;
case 0x1182:
dev_info(&pdev->dev,
"Detected SiS 1182/966/680 SATA controller\n");
2007-06-14 23:40:43 +02:00
pi.flags |= ATA_FLAG_SLAVE_POSS;
break;
2006-12-04 01:34:42 +01:00
case 0x1183:
dev_info(&pdev->dev,
"Detected SiS 1183/966/966L/968/680 controller in PATA mode\n");
2007-06-14 23:40:43 +02:00
ppi[0] = &sis_info133_for_sata;
ppi[1] = &sis_info133_for_sata;
2006-12-04 01:34:42 +01:00
break;
}
2010-05-19 22:10:22 +02:00
rc = ata_pci_bmdma_prepare_host(pdev, ppi, &host);
if (rc)
return rc;
2009-09-01 23:19:10 +09:00
for (i = 0; i < 2; i++) {
struct ata_port *ap = host->ports[i];
if (ap->flags & ATA_FLAG_SATA &&
ap->flags & ATA_FLAG_SLAVE_POSS) {
rc = ata_slave_link_init(ap);
if (rc)
return rc;
}
}
if (!(pi.flags & SIS_FLAG_CFGSCR)) {
2007-03-14 09:19:00 +00:00
void __iomem *mmio;
2007-02-01 15:06:36 +09:00
rc = pcim_iomap_regions(pdev, 1 << SIS_SCR_PCI_BAR, DRV_NAME);
if (rc)
return rc;
mmio = host->iomap[SIS_SCR_PCI_BAR];
2007-02-01 15:06:36 +09:00
host->ports[0]->ioaddr.scr_addr = mmio;
host->ports[1]->ioaddr.scr_addr = mmio + port2_start;
2005-04-16 15:20:36 -07:00
}
pci_set_master(pdev);
2005-08-15 15:23:41 -04:00
pci_intx(pdev, 1);
2010-05-19 22:10:21 +02:00
return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
2008-04-07 22:47:16 +09:00
IRQF_SHARED, &sis_sht);
2005-04-16 15:20:36 -07:00
}
2012-04-19 13:43:05 +08:00
module_pci_driver(sis_pci_driver);