sata_sis.c 9.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9
/*
 *  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
 *
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 *
 *  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.
L
Linus Torvalds 已提交
30 31 32 33 34 35 36 37 38 39 40
 *
 */

#include <linux/config.h>
#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/interrupt.h>
41
#include <linux/device.h>
L
Linus Torvalds 已提交
42 43 44 45
#include <scsi/scsi_host.h>
#include <linux/libata.h>

#define DRV_NAME	"sata_sis"
J
Jeff Garzik 已提交
46
#define DRV_VERSION	"0.6"
L
Linus Torvalds 已提交
47 48 49 50 51 52 53 54

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 */
55 56 57
	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 */
J
Jeff Garzik 已提交
58
	SIS_PMR_COMBINED	= 0x30,
L
Linus Torvalds 已提交
59 60 61 62 63 64 65 66 67 68 69

	/* 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);
static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg);
static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);

70
static const struct pci_device_id sis_pci_tbl[] = {
L
Linus Torvalds 已提交
71 72
	{ PCI_VENDOR_ID_SI, 0x180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 },
	{ PCI_VENDOR_ID_SI, 0x181, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 },
73
	{ PCI_VENDOR_ID_SI, 0x182, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 },
L
Linus Torvalds 已提交
74 75 76 77 78 79 80 81 82 83 84
	{ }	/* 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,
};

85
static struct scsi_host_template sis_sht = {
L
Linus Torvalds 已提交
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
	.module			= THIS_MODULE,
	.name			= DRV_NAME,
	.ioctl			= ata_scsi_ioctl,
	.queuecommand		= ata_scsi_queuecmd,
	.can_queue		= ATA_DEF_QUEUE,
	.this_id		= ATA_SHT_THIS_ID,
	.sg_tablesize		= ATA_MAX_PRD,
	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
	.emulated		= ATA_SHT_EMULATED,
	.use_clustering		= ATA_SHT_USE_CLUSTERING,
	.proc_name		= DRV_NAME,
	.dma_boundary		= ATA_DMA_BOUNDARY,
	.slave_configure	= ata_scsi_slave_config,
	.bios_param		= ata_std_bios_param,
};

J
Jeff Garzik 已提交
102
static const struct ata_port_operations sis_ops = {
L
Linus Torvalds 已提交
103 104 105 106 107 108 109 110 111 112 113 114 115
	.port_disable		= ata_port_disable,
	.tf_load		= ata_tf_load,
	.tf_read		= ata_tf_read,
	.check_status		= ata_check_status,
	.exec_command		= ata_exec_command,
	.dev_select		= ata_std_dev_select,
	.phy_reset		= sata_phy_reset,
	.bmdma_setup            = ata_bmdma_setup,
	.bmdma_start            = ata_bmdma_start,
	.bmdma_stop		= ata_bmdma_stop,
	.bmdma_status		= ata_bmdma_status,
	.qc_prep		= ata_qc_prep,
	.qc_issue		= ata_qc_issue_prot,
116
	.data_xfer		= ata_pio_data_xfer,
L
Linus Torvalds 已提交
117 118 119 120 121 122 123
	.eng_timeout		= ata_eng_timeout,
	.irq_handler		= ata_interrupt,
	.irq_clear		= ata_bmdma_irq_clear,
	.scr_read		= sis_scr_read,
	.scr_write		= sis_scr_write,
	.port_start		= ata_port_start,
	.port_stop		= ata_port_stop,
J
Jeff Garzik 已提交
124
	.host_stop		= ata_host_stop,
L
Linus Torvalds 已提交
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
};

static struct ata_port_info sis_port_info = {
	.sht		= &sis_sht,
	.host_flags	= ATA_FLAG_SATA | ATA_FLAG_SATA_RESET |
			  ATA_FLAG_NO_LEGACY,
	.pio_mask	= 0x1f,
	.mwdma_mask	= 0x7,
	.udma_mask	= 0x7f,
	.port_ops	= &sis_ops,
};


MODULE_AUTHOR("Uwe Koziolek");
MODULE_DESCRIPTION("low-level driver for Silicon Integratad Systems SATA controller");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, sis_pci_tbl);
MODULE_VERSION(DRV_VERSION);

144
static unsigned int get_scr_cfg_addr(unsigned int port_no, unsigned int sc_reg, int device)
L
Linus Torvalds 已提交
145 146 147
{
	unsigned int addr = SIS_SCR_BASE + (4 * sc_reg);

J
Jeff Garzik 已提交
148
	if (port_no)  {
149 150 151 152
		if (device == 0x182)
			addr += SIS182_SATA1_OFS;
		else
			addr += SIS180_SATA1_OFS;
J
Jeff Garzik 已提交
153 154
	}

L
Linus Torvalds 已提交
155 156 157 158 159 160
	return addr;
}

static u32 sis_scr_cfg_read (struct ata_port *ap, unsigned int sc_reg)
{
	struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
161
	unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, sc_reg, pdev->device);
162
	u32 val, val2 = 0;
163
	u8 pmr;
L
Linus Torvalds 已提交
164 165 166

	if (sc_reg == SCR_ERROR) /* doesn't exist in PCI cfg space */
		return 0xffffffff;
167 168

	pci_read_config_byte(pdev, SIS_PMR, &pmr);
J
Jeff Garzik 已提交
169

L
Linus Torvalds 已提交
170
	pci_read_config_dword(pdev, cfg_addr, &val);
171

J
Jeff Garzik 已提交
172
	if ((pdev->device == 0x182) || (pmr & SIS_PMR_COMBINED))
173 174 175
		pci_read_config_dword(pdev, cfg_addr+0x10, &val2);

	return val|val2;
L
Linus Torvalds 已提交
176 177 178 179 180
}

static void sis_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val)
{
	struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
181 182
	unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, scr, pdev->device);
	u8 pmr;
L
Linus Torvalds 已提交
183 184 185

	if (scr == SCR_ERROR) /* doesn't exist in PCI cfg space */
		return;
186 187

	pci_read_config_byte(pdev, SIS_PMR, &pmr);
J
Jeff Garzik 已提交
188

L
Linus Torvalds 已提交
189
	pci_write_config_dword(pdev, cfg_addr, val);
190 191 192

	if ((pdev->device == 0x182) || (pmr & SIS_PMR_COMBINED))
		pci_write_config_dword(pdev, cfg_addr+0x10, val);
L
Linus Torvalds 已提交
193 194 195 196
}

static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg)
{
197
	struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
J
Jeff Garzik 已提交
198
	u32 val, val2 = 0;
199 200
	u8 pmr;

L
Linus Torvalds 已提交
201 202 203 204 205
	if (sc_reg > SCR_CONTROL)
		return 0xffffffffU;

	if (ap->flags & SIS_FLAG_CFGSCR)
		return sis_scr_cfg_read(ap, sc_reg);
206 207 208 209 210 211

	pci_read_config_byte(pdev, SIS_PMR, &pmr);

	val = inl(ap->ioaddr.scr_addr + (sc_reg * 4));

	if ((pdev->device == 0x182) || (pmr & SIS_PMR_COMBINED))
J
Jeff Garzik 已提交
212
		val2 = inl(ap->ioaddr.scr_addr + (sc_reg * 4) + 0x10);
213

J
Jeff Garzik 已提交
214
	return val | val2;
L
Linus Torvalds 已提交
215 216 217 218
}

static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
{
219 220 221
	struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
	u8 pmr;

L
Linus Torvalds 已提交
222 223 224
	if (sc_reg > SCR_CONTROL)
		return;

225
	pci_read_config_byte(pdev, SIS_PMR, &pmr);
J
Jeff Garzik 已提交
226

L
Linus Torvalds 已提交
227 228
	if (ap->flags & SIS_FLAG_CFGSCR)
		sis_scr_cfg_write(ap, sc_reg, val);
229
	else {
L
Linus Torvalds 已提交
230
		outl(val, ap->ioaddr.scr_addr + (sc_reg * 4));
231 232 233
		if ((pdev->device == 0x182) || (pmr & SIS_PMR_COMBINED))
			outl(val, ap->ioaddr.scr_addr + (sc_reg * 4)+0x10);
	}
L
Linus Torvalds 已提交
234 235 236 237
}

static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
238
	static int printed_version;
L
Linus Torvalds 已提交
239 240 241 242 243
	struct ata_probe_ent *probe_ent = NULL;
	int rc;
	u32 genctl;
	struct ata_port_info *ppi;
	int pci_dev_busy = 0;
244 245
	u8 pmr;
	u8 port2_start;
L
Linus Torvalds 已提交
246

247 248 249
	if (!printed_version++)
		dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");

L
Linus Torvalds 已提交
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
	rc = pci_enable_device(pdev);
	if (rc)
		return rc;

	rc = pci_request_regions(pdev, DRV_NAME);
	if (rc) {
		pci_dev_busy = 1;
		goto err_out;
	}

	rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
	if (rc)
		goto err_out_regions;
	rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
	if (rc)
		goto err_out_regions;

	ppi = &sis_port_info;
268
	probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
L
Linus Torvalds 已提交
269 270 271 272 273 274 275 276 277
	if (!probe_ent) {
		rc = -ENOMEM;
		goto err_out_regions;
	}

	/* 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)
		probe_ent->host_flags |= SIS_FLAG_CFGSCR;
J
Jeff Garzik 已提交
278

L
Linus Torvalds 已提交
279 280 281 282 283 284 285 286 287 288 289
	/* if hardware thinks SCRs are in IO space, but there are
	 * no IO resources assigned, change to PCI cfg space.
	 */
	if ((!(probe_ent->host_flags & SIS_FLAG_CFGSCR)) &&
	    ((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);
		probe_ent->host_flags |= SIS_FLAG_CFGSCR;
	}

290 291 292
	pci_read_config_byte(pdev, SIS_PMR, &pmr);
	if (ent->device != 0x182) {
		if ((pmr & SIS_PMR_COMBINED) == 0) {
293 294
			dev_printk(KERN_INFO, &pdev->dev,
				   "Detected SiS 180/181 chipset in SATA mode\n");
295
			port2_start = 64;
296 297
		}
		else {
298 299
			dev_printk(KERN_INFO, &pdev->dev,
				   "Detected SiS 180/181 chipset in combined mode\n");
300 301 302 303
			port2_start=0;
		}
	}
	else {
304
		dev_printk(KERN_INFO, &pdev->dev, "Detected SiS 182 chipset\n");
305 306 307
		port2_start = 0x20;
	}

L
Linus Torvalds 已提交
308 309 310 311
	if (!(probe_ent->host_flags & SIS_FLAG_CFGSCR)) {
		probe_ent->port[0].scr_addr =
			pci_resource_start(pdev, SIS_SCR_PCI_BAR);
		probe_ent->port[1].scr_addr =
312
			pci_resource_start(pdev, SIS_SCR_PCI_BAR) + port2_start;
L
Linus Torvalds 已提交
313 314 315
	}

	pci_set_master(pdev);
B
Brett M Russ 已提交
316
	pci_intx(pdev, 1);
L
Linus Torvalds 已提交
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346

	/* FIXME: check ata_device_add return value */
	ata_device_add(probe_ent);
	kfree(probe_ent);

	return 0;

err_out_regions:
	pci_release_regions(pdev);

err_out:
	if (!pci_dev_busy)
		pci_disable_device(pdev);
	return rc;

}

static int __init sis_init(void)
{
	return pci_module_init(&sis_pci_driver);
}

static void __exit sis_exit(void)
{
	pci_unregister_driver(&sis_pci_driver);
}

module_init(sis_init);
module_exit(sis_exit);