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> 295a0e3ad6STejun Heo #include <linux/gfp.h> 30c6fd2807SJeff Garzik #include <linux/pci.h> 31c6fd2807SJeff Garzik #include <linux/init.h> 32c6fd2807SJeff Garzik #include <linux/blkdev.h> 33c6fd2807SJeff Garzik #include <linux/delay.h> 34c6fd2807SJeff Garzik #include <linux/interrupt.h> 35c6fd2807SJeff Garzik #include <linux/device.h> 36c6fd2807SJeff Garzik #include <scsi/scsi_host.h> 37c6fd2807SJeff Garzik #include <linux/libata.h> 38c6fd2807SJeff Garzik 39c6fd2807SJeff Garzik #define DRV_NAME "sata_uli" 402a3103ceSJeff Garzik #define DRV_VERSION "1.3" 41c6fd2807SJeff Garzik 42c6fd2807SJeff Garzik enum { 43c6fd2807SJeff Garzik uli_5289 = 0, 44c6fd2807SJeff Garzik uli_5287 = 1, 45c6fd2807SJeff Garzik uli_5281 = 2, 46c6fd2807SJeff Garzik 47c6fd2807SJeff Garzik uli_max_ports = 4, 48c6fd2807SJeff Garzik 49c6fd2807SJeff Garzik /* PCI configuration registers */ 50c6fd2807SJeff Garzik ULI5287_BASE = 0x90, /* sata0 phy SCR registers */ 51c6fd2807SJeff Garzik ULI5287_OFFS = 0x10, /* offset from sata0->sata1 phy regs */ 52c6fd2807SJeff Garzik ULI5281_BASE = 0x60, /* sata0 phy SCR registers */ 53c6fd2807SJeff Garzik ULI5281_OFFS = 0x60, /* offset from sata0->sata1 phy regs */ 54c6fd2807SJeff Garzik }; 55c6fd2807SJeff Garzik 56c6fd2807SJeff Garzik struct uli_priv { 57c6fd2807SJeff Garzik unsigned int scr_cfg_addr[uli_max_ports]; 58c6fd2807SJeff Garzik }; 59c6fd2807SJeff Garzik 60c6fd2807SJeff Garzik static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); 6182ef04fbSTejun Heo static int uli_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); 6282ef04fbSTejun Heo static int uli_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); 63c6fd2807SJeff Garzik 64c6fd2807SJeff Garzik static const struct pci_device_id uli_pci_tbl[] = { 6554bb3a94SJeff Garzik { PCI_VDEVICE(AL, 0x5289), uli_5289 }, 6654bb3a94SJeff Garzik { PCI_VDEVICE(AL, 0x5287), uli_5287 }, 6754bb3a94SJeff Garzik { PCI_VDEVICE(AL, 0x5281), uli_5281 }, 6854bb3a94SJeff Garzik 69c6fd2807SJeff Garzik { } /* terminate list */ 70c6fd2807SJeff Garzik }; 71c6fd2807SJeff Garzik 72c6fd2807SJeff Garzik static struct pci_driver uli_pci_driver = { 73c6fd2807SJeff Garzik .name = DRV_NAME, 74c6fd2807SJeff Garzik .id_table = uli_pci_tbl, 75c6fd2807SJeff Garzik .probe = uli_init_one, 76c6fd2807SJeff Garzik .remove = ata_pci_remove_one, 77c6fd2807SJeff Garzik }; 78c6fd2807SJeff Garzik 79c6fd2807SJeff Garzik static struct scsi_host_template uli_sht = { 8068d1d07bSTejun Heo ATA_BMDMA_SHT(DRV_NAME), 81c6fd2807SJeff Garzik }; 82c6fd2807SJeff Garzik 83029cfd6bSTejun Heo static struct ata_port_operations uli_ops = { 84029cfd6bSTejun Heo .inherits = &ata_bmdma_port_ops, 85c6fd2807SJeff Garzik .scr_read = uli_scr_read, 86c6fd2807SJeff Garzik .scr_write = uli_scr_write, 8770a3143aSTejun Heo .hardreset = ATA_OP_NULL, 88c6fd2807SJeff Garzik }; 89c6fd2807SJeff Garzik 901626aeb8STejun Heo static const struct ata_port_info uli_port_info = { 91b2a8bbe6STejun Heo .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | 92b2a8bbe6STejun Heo ATA_FLAG_IGN_SIMPLEX, 9314bdef98SErik Inge Bolsø .pio_mask = ATA_PIO4, 94bf6263a8SJeff Garzik .udma_mask = ATA_UDMA6, 95c6fd2807SJeff Garzik .port_ops = &uli_ops, 96c6fd2807SJeff Garzik }; 97c6fd2807SJeff Garzik 98c6fd2807SJeff Garzik 99c6fd2807SJeff Garzik MODULE_AUTHOR("Peer Chen"); 100c6fd2807SJeff Garzik MODULE_DESCRIPTION("low-level driver for ULi Electronics SATA controller"); 101c6fd2807SJeff Garzik MODULE_LICENSE("GPL"); 102c6fd2807SJeff Garzik MODULE_DEVICE_TABLE(pci, uli_pci_tbl); 103c6fd2807SJeff Garzik MODULE_VERSION(DRV_VERSION); 104c6fd2807SJeff Garzik 105c6fd2807SJeff Garzik static unsigned int get_scr_cfg_addr(struct ata_port *ap, unsigned int sc_reg) 106c6fd2807SJeff Garzik { 107cca3974eSJeff Garzik struct uli_priv *hpriv = ap->host->private_data; 108c6fd2807SJeff Garzik return hpriv->scr_cfg_addr[ap->port_no] + (4 * sc_reg); 109c6fd2807SJeff Garzik } 110c6fd2807SJeff Garzik 11182ef04fbSTejun Heo static u32 uli_scr_cfg_read(struct ata_link *link, unsigned int sc_reg) 112c6fd2807SJeff Garzik { 11382ef04fbSTejun Heo struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); 11482ef04fbSTejun Heo unsigned int cfg_addr = get_scr_cfg_addr(link->ap, sc_reg); 115c6fd2807SJeff Garzik u32 val; 116c6fd2807SJeff Garzik 117c6fd2807SJeff Garzik pci_read_config_dword(pdev, cfg_addr, &val); 118c6fd2807SJeff Garzik return val; 119c6fd2807SJeff Garzik } 120c6fd2807SJeff Garzik 12182ef04fbSTejun Heo static void uli_scr_cfg_write(struct ata_link *link, unsigned int scr, u32 val) 122c6fd2807SJeff Garzik { 12382ef04fbSTejun Heo struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); 12482ef04fbSTejun Heo unsigned int cfg_addr = get_scr_cfg_addr(link->ap, scr); 125c6fd2807SJeff Garzik 126c6fd2807SJeff Garzik pci_write_config_dword(pdev, cfg_addr, val); 127c6fd2807SJeff Garzik } 128c6fd2807SJeff Garzik 12982ef04fbSTejun Heo static int uli_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) 130c6fd2807SJeff Garzik { 131c6fd2807SJeff Garzik if (sc_reg > SCR_CONTROL) 132da3dbb17STejun Heo return -EINVAL; 133c6fd2807SJeff Garzik 13482ef04fbSTejun Heo *val = uli_scr_cfg_read(link, sc_reg); 135da3dbb17STejun Heo return 0; 136c6fd2807SJeff Garzik } 137c6fd2807SJeff Garzik 13882ef04fbSTejun Heo static int uli_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) 139c6fd2807SJeff Garzik { 140c6fd2807SJeff Garzik if (sc_reg > SCR_CONTROL) //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0 141da3dbb17STejun Heo return -EINVAL; 142c6fd2807SJeff Garzik 14382ef04fbSTejun Heo uli_scr_cfg_write(link, sc_reg, val); 144da3dbb17STejun Heo return 0; 145c6fd2807SJeff Garzik } 146c6fd2807SJeff Garzik 147c6fd2807SJeff Garzik static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 148c6fd2807SJeff Garzik { 149c6fd2807SJeff Garzik static int printed_version; 1509a829ccfSTejun Heo const struct ata_port_info *ppi[] = { &uli_port_info, NULL }; 151c6fd2807SJeff Garzik unsigned int board_idx = (unsigned int) ent->driver_data; 1529a829ccfSTejun Heo struct ata_host *host; 153c6fd2807SJeff Garzik struct uli_priv *hpriv; 1540d5ff566STejun Heo void __iomem * const *iomap; 1559a829ccfSTejun Heo struct ata_ioports *ioaddr; 1569a829ccfSTejun Heo int n_ports, rc; 157c6fd2807SJeff Garzik 158c6fd2807SJeff Garzik if (!printed_version++) 159c6fd2807SJeff Garzik dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); 160c6fd2807SJeff Garzik 16124dc5f33STejun Heo rc = pcim_enable_device(pdev); 162c6fd2807SJeff Garzik if (rc) 163c6fd2807SJeff Garzik return rc; 164c6fd2807SJeff Garzik 1659a829ccfSTejun Heo n_ports = 2; 1669a829ccfSTejun Heo if (board_idx == uli_5287) 1679a829ccfSTejun Heo n_ports = 4; 1681626aeb8STejun Heo 1691626aeb8STejun Heo /* allocate the host */ 1701626aeb8STejun Heo host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); 1711626aeb8STejun Heo if (!host) 1721626aeb8STejun Heo return -ENOMEM; 173c6fd2807SJeff Garzik 17424dc5f33STejun Heo hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); 17524dc5f33STejun Heo if (!hpriv) 17624dc5f33STejun Heo return -ENOMEM; 1779a829ccfSTejun Heo host->private_data = hpriv; 178c6fd2807SJeff Garzik 1791626aeb8STejun Heo /* the first two ports are standard SFF */ 1809363c382STejun Heo rc = ata_pci_sff_init_host(host); 1811626aeb8STejun Heo if (rc) 1821626aeb8STejun Heo return rc; 1831626aeb8STejun Heo 184c7087652STejun Heo ata_pci_bmdma_init(host); 1851626aeb8STejun Heo 1869a829ccfSTejun Heo iomap = host->iomap; 1870d5ff566STejun Heo 188c6fd2807SJeff Garzik switch (board_idx) { 189c6fd2807SJeff Garzik case uli_5287: 1901626aeb8STejun Heo /* If there are four, the last two live right after 1911626aeb8STejun Heo * the standard SFF ports. 1921626aeb8STejun Heo */ 193c6fd2807SJeff Garzik hpriv->scr_cfg_addr[0] = ULI5287_BASE; 194c6fd2807SJeff Garzik hpriv->scr_cfg_addr[1] = ULI5287_BASE + ULI5287_OFFS; 195c6fd2807SJeff Garzik 1969a829ccfSTejun Heo ioaddr = &host->ports[2]->ioaddr; 1979a829ccfSTejun Heo ioaddr->cmd_addr = iomap[0] + 8; 1989a829ccfSTejun Heo ioaddr->altstatus_addr = 1999a829ccfSTejun Heo ioaddr->ctl_addr = (void __iomem *) 2000d5ff566STejun Heo ((unsigned long)iomap[1] | ATA_PCI_CTL_OFS) + 4; 2019a829ccfSTejun Heo ioaddr->bmdma_addr = iomap[4] + 16; 202c6fd2807SJeff Garzik hpriv->scr_cfg_addr[2] = ULI5287_BASE + ULI5287_OFFS*4; 2039363c382STejun Heo ata_sff_std_ports(ioaddr); 204c6fd2807SJeff Garzik 205cbcdd875STejun Heo ata_port_desc(host->ports[2], 206cbcdd875STejun Heo "cmd 0x%llx ctl 0x%llx bmdma 0x%llx", 207cbcdd875STejun Heo (unsigned long long)pci_resource_start(pdev, 0) + 8, 208cbcdd875STejun Heo ((unsigned long long)pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS) + 4, 209cbcdd875STejun Heo (unsigned long long)pci_resource_start(pdev, 4) + 16); 210cbcdd875STejun Heo 2119a829ccfSTejun Heo ioaddr = &host->ports[3]->ioaddr; 2129a829ccfSTejun Heo ioaddr->cmd_addr = iomap[2] + 8; 2139a829ccfSTejun Heo ioaddr->altstatus_addr = 2149a829ccfSTejun Heo ioaddr->ctl_addr = (void __iomem *) 2150d5ff566STejun Heo ((unsigned long)iomap[3] | ATA_PCI_CTL_OFS) + 4; 2169a829ccfSTejun Heo ioaddr->bmdma_addr = iomap[4] + 24; 217c6fd2807SJeff Garzik hpriv->scr_cfg_addr[3] = ULI5287_BASE + ULI5287_OFFS*5; 2189363c382STejun Heo ata_sff_std_ports(ioaddr); 219cbcdd875STejun Heo 220cbcdd875STejun Heo ata_port_desc(host->ports[2], 221cbcdd875STejun Heo "cmd 0x%llx ctl 0x%llx bmdma 0x%llx", 222cbcdd875STejun Heo (unsigned long long)pci_resource_start(pdev, 2) + 9, 223cbcdd875STejun Heo ((unsigned long long)pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS) + 4, 224cbcdd875STejun Heo (unsigned long long)pci_resource_start(pdev, 4) + 24); 225cbcdd875STejun Heo 226c6fd2807SJeff Garzik break; 227c6fd2807SJeff Garzik 228c6fd2807SJeff Garzik case uli_5289: 229c6fd2807SJeff Garzik hpriv->scr_cfg_addr[0] = ULI5287_BASE; 230c6fd2807SJeff Garzik hpriv->scr_cfg_addr[1] = ULI5287_BASE + ULI5287_OFFS; 231c6fd2807SJeff Garzik break; 232c6fd2807SJeff Garzik 233c6fd2807SJeff Garzik case uli_5281: 234c6fd2807SJeff Garzik hpriv->scr_cfg_addr[0] = ULI5281_BASE; 235c6fd2807SJeff Garzik hpriv->scr_cfg_addr[1] = ULI5281_BASE + ULI5281_OFFS; 236c6fd2807SJeff Garzik break; 237c6fd2807SJeff Garzik 238c6fd2807SJeff Garzik default: 239c6fd2807SJeff Garzik BUG(); 240c6fd2807SJeff Garzik break; 241c6fd2807SJeff Garzik } 242c6fd2807SJeff Garzik 243c6fd2807SJeff Garzik pci_set_master(pdev); 244c6fd2807SJeff Garzik pci_intx(pdev, 1); 245*c3b28894STejun Heo return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt, 2469363c382STejun Heo IRQF_SHARED, &uli_sht); 247c6fd2807SJeff Garzik } 248c6fd2807SJeff Garzik 249c6fd2807SJeff Garzik static int __init uli_init(void) 250c6fd2807SJeff Garzik { 251c6fd2807SJeff Garzik return pci_register_driver(&uli_pci_driver); 252c6fd2807SJeff Garzik } 253c6fd2807SJeff Garzik 254c6fd2807SJeff Garzik static void __exit uli_exit(void) 255c6fd2807SJeff Garzik { 256c6fd2807SJeff Garzik pci_unregister_driver(&uli_pci_driver); 257c6fd2807SJeff Garzik } 258c6fd2807SJeff Garzik 259c6fd2807SJeff Garzik 260c6fd2807SJeff Garzik module_init(uli_init); 261c6fd2807SJeff Garzik module_exit(uli_exit); 262