1c6fd2807SJeff Garzik /* 2c6fd2807SJeff Garzik * sata_uli.c - ULi Electronics SATA 3c6fd2807SJeff Garzik * 4c6fd2807SJeff Garzik * 5c6fd2807SJeff Garzik * This program is free software; you can redistribute it and/or modify 6c6fd2807SJeff Garzik * it under the terms of the GNU General Public License as published by 7c6fd2807SJeff Garzik * the Free Software Foundation; either version 2, or (at your option) 8c6fd2807SJeff Garzik * any later version. 9c6fd2807SJeff Garzik * 10c6fd2807SJeff Garzik * This program is distributed in the hope that it will be useful, 11c6fd2807SJeff Garzik * but WITHOUT ANY WARRANTY; without even the implied warranty of 12c6fd2807SJeff Garzik * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13c6fd2807SJeff Garzik * GNU General Public License for more details. 14c6fd2807SJeff Garzik * 15c6fd2807SJeff Garzik * You should have received a copy of the GNU General Public License 16c6fd2807SJeff Garzik * along with this program; see the file COPYING. If not, write to 17c6fd2807SJeff Garzik * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 18c6fd2807SJeff Garzik * 19c6fd2807SJeff Garzik * 20c6fd2807SJeff Garzik * libata documentation is available via 'make {ps|pdf}docs', 21c6fd2807SJeff Garzik * as Documentation/DocBook/libata.* 22c6fd2807SJeff Garzik * 23c6fd2807SJeff Garzik * Hardware documentation available under NDA. 24c6fd2807SJeff Garzik * 25c6fd2807SJeff Garzik */ 26c6fd2807SJeff Garzik 27c6fd2807SJeff Garzik #include <linux/kernel.h> 28c6fd2807SJeff Garzik #include <linux/module.h> 29c6fd2807SJeff Garzik #include <linux/pci.h> 30c6fd2807SJeff Garzik #include <linux/init.h> 31c6fd2807SJeff Garzik #include <linux/blkdev.h> 32c6fd2807SJeff Garzik #include <linux/delay.h> 33c6fd2807SJeff Garzik #include <linux/interrupt.h> 34c6fd2807SJeff Garzik #include <linux/device.h> 35c6fd2807SJeff Garzik #include <scsi/scsi_host.h> 36c6fd2807SJeff Garzik #include <linux/libata.h> 37c6fd2807SJeff Garzik 38c6fd2807SJeff Garzik #define DRV_NAME "sata_uli" 392a3103ceSJeff Garzik #define DRV_VERSION "1.3" 40c6fd2807SJeff Garzik 41c6fd2807SJeff Garzik enum { 42c6fd2807SJeff Garzik uli_5289 = 0, 43c6fd2807SJeff Garzik uli_5287 = 1, 44c6fd2807SJeff Garzik uli_5281 = 2, 45c6fd2807SJeff Garzik 46c6fd2807SJeff Garzik uli_max_ports = 4, 47c6fd2807SJeff Garzik 48c6fd2807SJeff Garzik /* PCI configuration registers */ 49c6fd2807SJeff Garzik ULI5287_BASE = 0x90, /* sata0 phy SCR registers */ 50c6fd2807SJeff Garzik ULI5287_OFFS = 0x10, /* offset from sata0->sata1 phy regs */ 51c6fd2807SJeff Garzik ULI5281_BASE = 0x60, /* sata0 phy SCR registers */ 52c6fd2807SJeff Garzik ULI5281_OFFS = 0x60, /* offset from sata0->sata1 phy regs */ 53c6fd2807SJeff Garzik }; 54c6fd2807SJeff Garzik 55c6fd2807SJeff Garzik struct uli_priv { 56c6fd2807SJeff Garzik unsigned int scr_cfg_addr[uli_max_ports]; 57c6fd2807SJeff Garzik }; 58c6fd2807SJeff Garzik 59c6fd2807SJeff Garzik static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); 6082ef04fbSTejun Heo static int uli_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); 6182ef04fbSTejun Heo static int uli_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); 62c6fd2807SJeff Garzik 63c6fd2807SJeff Garzik static const struct pci_device_id uli_pci_tbl[] = { 6454bb3a94SJeff Garzik { PCI_VDEVICE(AL, 0x5289), uli_5289 }, 6554bb3a94SJeff Garzik { PCI_VDEVICE(AL, 0x5287), uli_5287 }, 6654bb3a94SJeff Garzik { PCI_VDEVICE(AL, 0x5281), uli_5281 }, 6754bb3a94SJeff Garzik 68c6fd2807SJeff Garzik { } /* terminate list */ 69c6fd2807SJeff Garzik }; 70c6fd2807SJeff Garzik 71c6fd2807SJeff Garzik static struct pci_driver uli_pci_driver = { 72c6fd2807SJeff Garzik .name = DRV_NAME, 73c6fd2807SJeff Garzik .id_table = uli_pci_tbl, 74c6fd2807SJeff Garzik .probe = uli_init_one, 75c6fd2807SJeff Garzik .remove = ata_pci_remove_one, 76c6fd2807SJeff Garzik }; 77c6fd2807SJeff Garzik 78c6fd2807SJeff Garzik static struct scsi_host_template uli_sht = { 7968d1d07bSTejun Heo ATA_BMDMA_SHT(DRV_NAME), 80c6fd2807SJeff Garzik }; 81c6fd2807SJeff Garzik 82029cfd6bSTejun Heo static struct ata_port_operations uli_ops = { 83029cfd6bSTejun Heo .inherits = &ata_bmdma_port_ops, 84c6fd2807SJeff Garzik .scr_read = uli_scr_read, 85c6fd2807SJeff Garzik .scr_write = uli_scr_write, 8670a3143aSTejun Heo .hardreset = ATA_OP_NULL, 87c6fd2807SJeff Garzik }; 88c6fd2807SJeff Garzik 891626aeb8STejun Heo static const struct ata_port_info uli_port_info = { 90b2a8bbe6STejun Heo .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | 91b2a8bbe6STejun Heo ATA_FLAG_IGN_SIMPLEX, 92*14bdef98SErik Inge Bolsø .pio_mask = ATA_PIO4, 93bf6263a8SJeff Garzik .udma_mask = ATA_UDMA6, 94c6fd2807SJeff Garzik .port_ops = &uli_ops, 95c6fd2807SJeff Garzik }; 96c6fd2807SJeff Garzik 97c6fd2807SJeff Garzik 98c6fd2807SJeff Garzik MODULE_AUTHOR("Peer Chen"); 99c6fd2807SJeff Garzik MODULE_DESCRIPTION("low-level driver for ULi Electronics SATA controller"); 100c6fd2807SJeff Garzik MODULE_LICENSE("GPL"); 101c6fd2807SJeff Garzik MODULE_DEVICE_TABLE(pci, uli_pci_tbl); 102c6fd2807SJeff Garzik MODULE_VERSION(DRV_VERSION); 103c6fd2807SJeff Garzik 104c6fd2807SJeff Garzik static unsigned int get_scr_cfg_addr(struct ata_port *ap, unsigned int sc_reg) 105c6fd2807SJeff Garzik { 106cca3974eSJeff Garzik struct uli_priv *hpriv = ap->host->private_data; 107c6fd2807SJeff Garzik return hpriv->scr_cfg_addr[ap->port_no] + (4 * sc_reg); 108c6fd2807SJeff Garzik } 109c6fd2807SJeff Garzik 11082ef04fbSTejun Heo static u32 uli_scr_cfg_read(struct ata_link *link, unsigned int sc_reg) 111c6fd2807SJeff Garzik { 11282ef04fbSTejun Heo struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); 11382ef04fbSTejun Heo unsigned int cfg_addr = get_scr_cfg_addr(link->ap, sc_reg); 114c6fd2807SJeff Garzik u32 val; 115c6fd2807SJeff Garzik 116c6fd2807SJeff Garzik pci_read_config_dword(pdev, cfg_addr, &val); 117c6fd2807SJeff Garzik return val; 118c6fd2807SJeff Garzik } 119c6fd2807SJeff Garzik 12082ef04fbSTejun Heo static void uli_scr_cfg_write(struct ata_link *link, unsigned int scr, u32 val) 121c6fd2807SJeff Garzik { 12282ef04fbSTejun Heo struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); 12382ef04fbSTejun Heo unsigned int cfg_addr = get_scr_cfg_addr(link->ap, scr); 124c6fd2807SJeff Garzik 125c6fd2807SJeff Garzik pci_write_config_dword(pdev, cfg_addr, val); 126c6fd2807SJeff Garzik } 127c6fd2807SJeff Garzik 12882ef04fbSTejun Heo static int uli_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) 129c6fd2807SJeff Garzik { 130c6fd2807SJeff Garzik if (sc_reg > SCR_CONTROL) 131da3dbb17STejun Heo return -EINVAL; 132c6fd2807SJeff Garzik 13382ef04fbSTejun Heo *val = uli_scr_cfg_read(link, sc_reg); 134da3dbb17STejun Heo return 0; 135c6fd2807SJeff Garzik } 136c6fd2807SJeff Garzik 13782ef04fbSTejun Heo static int uli_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) 138c6fd2807SJeff Garzik { 139c6fd2807SJeff Garzik if (sc_reg > SCR_CONTROL) //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0 140da3dbb17STejun Heo return -EINVAL; 141c6fd2807SJeff Garzik 14282ef04fbSTejun Heo uli_scr_cfg_write(link, sc_reg, val); 143da3dbb17STejun Heo return 0; 144c6fd2807SJeff Garzik } 145c6fd2807SJeff Garzik 146c6fd2807SJeff Garzik static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 147c6fd2807SJeff Garzik { 148c6fd2807SJeff Garzik static int printed_version; 1499a829ccfSTejun Heo const struct ata_port_info *ppi[] = { &uli_port_info, NULL }; 150c6fd2807SJeff Garzik unsigned int board_idx = (unsigned int) ent->driver_data; 1519a829ccfSTejun Heo struct ata_host *host; 152c6fd2807SJeff Garzik struct uli_priv *hpriv; 1530d5ff566STejun Heo void __iomem * const *iomap; 1549a829ccfSTejun Heo struct ata_ioports *ioaddr; 1559a829ccfSTejun Heo int n_ports, rc; 156c6fd2807SJeff Garzik 157c6fd2807SJeff Garzik if (!printed_version++) 158c6fd2807SJeff Garzik dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); 159c6fd2807SJeff Garzik 16024dc5f33STejun Heo rc = pcim_enable_device(pdev); 161c6fd2807SJeff Garzik if (rc) 162c6fd2807SJeff Garzik return rc; 163c6fd2807SJeff Garzik 1649a829ccfSTejun Heo n_ports = 2; 1659a829ccfSTejun Heo if (board_idx == uli_5287) 1669a829ccfSTejun Heo n_ports = 4; 1671626aeb8STejun Heo 1681626aeb8STejun Heo /* allocate the host */ 1691626aeb8STejun Heo host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); 1701626aeb8STejun Heo if (!host) 1711626aeb8STejun Heo return -ENOMEM; 172c6fd2807SJeff Garzik 17324dc5f33STejun Heo hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); 17424dc5f33STejun Heo if (!hpriv) 17524dc5f33STejun Heo return -ENOMEM; 1769a829ccfSTejun Heo host->private_data = hpriv; 177c6fd2807SJeff Garzik 1781626aeb8STejun Heo /* the first two ports are standard SFF */ 1799363c382STejun Heo rc = ata_pci_sff_init_host(host); 1801626aeb8STejun Heo if (rc) 1811626aeb8STejun Heo return rc; 1821626aeb8STejun Heo 1839363c382STejun Heo rc = ata_pci_bmdma_init(host); 1841626aeb8STejun Heo if (rc) 1851626aeb8STejun Heo return rc; 1861626aeb8STejun Heo 1879a829ccfSTejun Heo iomap = host->iomap; 1880d5ff566STejun Heo 189c6fd2807SJeff Garzik switch (board_idx) { 190c6fd2807SJeff Garzik case uli_5287: 1911626aeb8STejun Heo /* If there are four, the last two live right after 1921626aeb8STejun Heo * the standard SFF ports. 1931626aeb8STejun Heo */ 194c6fd2807SJeff Garzik hpriv->scr_cfg_addr[0] = ULI5287_BASE; 195c6fd2807SJeff Garzik hpriv->scr_cfg_addr[1] = ULI5287_BASE + ULI5287_OFFS; 196c6fd2807SJeff Garzik 1979a829ccfSTejun Heo ioaddr = &host->ports[2]->ioaddr; 1989a829ccfSTejun Heo ioaddr->cmd_addr = iomap[0] + 8; 1999a829ccfSTejun Heo ioaddr->altstatus_addr = 2009a829ccfSTejun Heo ioaddr->ctl_addr = (void __iomem *) 2010d5ff566STejun Heo ((unsigned long)iomap[1] | ATA_PCI_CTL_OFS) + 4; 2029a829ccfSTejun Heo ioaddr->bmdma_addr = iomap[4] + 16; 203c6fd2807SJeff Garzik hpriv->scr_cfg_addr[2] = ULI5287_BASE + ULI5287_OFFS*4; 2049363c382STejun Heo ata_sff_std_ports(ioaddr); 205c6fd2807SJeff Garzik 206cbcdd875STejun Heo ata_port_desc(host->ports[2], 207cbcdd875STejun Heo "cmd 0x%llx ctl 0x%llx bmdma 0x%llx", 208cbcdd875STejun Heo (unsigned long long)pci_resource_start(pdev, 0) + 8, 209cbcdd875STejun Heo ((unsigned long long)pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS) + 4, 210cbcdd875STejun Heo (unsigned long long)pci_resource_start(pdev, 4) + 16); 211cbcdd875STejun Heo 2129a829ccfSTejun Heo ioaddr = &host->ports[3]->ioaddr; 2139a829ccfSTejun Heo ioaddr->cmd_addr = iomap[2] + 8; 2149a829ccfSTejun Heo ioaddr->altstatus_addr = 2159a829ccfSTejun Heo ioaddr->ctl_addr = (void __iomem *) 2160d5ff566STejun Heo ((unsigned long)iomap[3] | ATA_PCI_CTL_OFS) + 4; 2179a829ccfSTejun Heo ioaddr->bmdma_addr = iomap[4] + 24; 218c6fd2807SJeff Garzik hpriv->scr_cfg_addr[3] = ULI5287_BASE + ULI5287_OFFS*5; 2199363c382STejun Heo ata_sff_std_ports(ioaddr); 220cbcdd875STejun Heo 221cbcdd875STejun Heo ata_port_desc(host->ports[2], 222cbcdd875STejun Heo "cmd 0x%llx ctl 0x%llx bmdma 0x%llx", 223cbcdd875STejun Heo (unsigned long long)pci_resource_start(pdev, 2) + 9, 224cbcdd875STejun Heo ((unsigned long long)pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS) + 4, 225cbcdd875STejun Heo (unsigned long long)pci_resource_start(pdev, 4) + 24); 226cbcdd875STejun Heo 227c6fd2807SJeff Garzik break; 228c6fd2807SJeff Garzik 229c6fd2807SJeff Garzik case uli_5289: 230c6fd2807SJeff Garzik hpriv->scr_cfg_addr[0] = ULI5287_BASE; 231c6fd2807SJeff Garzik hpriv->scr_cfg_addr[1] = ULI5287_BASE + ULI5287_OFFS; 232c6fd2807SJeff Garzik break; 233c6fd2807SJeff Garzik 234c6fd2807SJeff Garzik case uli_5281: 235c6fd2807SJeff Garzik hpriv->scr_cfg_addr[0] = ULI5281_BASE; 236c6fd2807SJeff Garzik hpriv->scr_cfg_addr[1] = ULI5281_BASE + ULI5281_OFFS; 237c6fd2807SJeff Garzik break; 238c6fd2807SJeff Garzik 239c6fd2807SJeff Garzik default: 240c6fd2807SJeff Garzik BUG(); 241c6fd2807SJeff Garzik break; 242c6fd2807SJeff Garzik } 243c6fd2807SJeff Garzik 244c6fd2807SJeff Garzik pci_set_master(pdev); 245c6fd2807SJeff Garzik pci_intx(pdev, 1); 2469363c382STejun Heo return ata_host_activate(host, pdev->irq, ata_sff_interrupt, 2479363c382STejun Heo IRQF_SHARED, &uli_sht); 248c6fd2807SJeff Garzik } 249c6fd2807SJeff Garzik 250c6fd2807SJeff Garzik static int __init uli_init(void) 251c6fd2807SJeff Garzik { 252c6fd2807SJeff Garzik return pci_register_driver(&uli_pci_driver); 253c6fd2807SJeff Garzik } 254c6fd2807SJeff Garzik 255c6fd2807SJeff Garzik static void __exit uli_exit(void) 256c6fd2807SJeff Garzik { 257c6fd2807SJeff Garzik pci_unregister_driver(&uli_pci_driver); 258c6fd2807SJeff Garzik } 259c6fd2807SJeff Garzik 260c6fd2807SJeff Garzik 261c6fd2807SJeff Garzik module_init(uli_init); 262c6fd2807SJeff Garzik module_exit(uli_exit); 263