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', 21*19285f3cSMauro Carvalho Chehab * as Documentation/driver-api/libata.rst 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/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 = { 909cbe056fSSergei Shtylyov .flags = ATA_FLAG_SATA | ATA_FLAG_IGN_SIMPLEX, 9114bdef98SErik Inge Bolsø .pio_mask = ATA_PIO4, 92bf6263a8SJeff Garzik .udma_mask = ATA_UDMA6, 93c6fd2807SJeff Garzik .port_ops = &uli_ops, 94c6fd2807SJeff Garzik }; 95c6fd2807SJeff Garzik 96c6fd2807SJeff Garzik 97c6fd2807SJeff Garzik MODULE_AUTHOR("Peer Chen"); 98c6fd2807SJeff Garzik MODULE_DESCRIPTION("low-level driver for ULi Electronics SATA controller"); 99c6fd2807SJeff Garzik MODULE_LICENSE("GPL"); 100c6fd2807SJeff Garzik MODULE_DEVICE_TABLE(pci, uli_pci_tbl); 101c6fd2807SJeff Garzik MODULE_VERSION(DRV_VERSION); 102c6fd2807SJeff Garzik 103c6fd2807SJeff Garzik static unsigned int get_scr_cfg_addr(struct ata_port *ap, unsigned int sc_reg) 104c6fd2807SJeff Garzik { 105cca3974eSJeff Garzik struct uli_priv *hpriv = ap->host->private_data; 106c6fd2807SJeff Garzik return hpriv->scr_cfg_addr[ap->port_no] + (4 * sc_reg); 107c6fd2807SJeff Garzik } 108c6fd2807SJeff Garzik 10982ef04fbSTejun Heo static u32 uli_scr_cfg_read(struct ata_link *link, unsigned int sc_reg) 110c6fd2807SJeff Garzik { 11182ef04fbSTejun Heo struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); 11282ef04fbSTejun Heo unsigned int cfg_addr = get_scr_cfg_addr(link->ap, sc_reg); 113c6fd2807SJeff Garzik u32 val; 114c6fd2807SJeff Garzik 115c6fd2807SJeff Garzik pci_read_config_dword(pdev, cfg_addr, &val); 116c6fd2807SJeff Garzik return val; 117c6fd2807SJeff Garzik } 118c6fd2807SJeff Garzik 11982ef04fbSTejun Heo static void uli_scr_cfg_write(struct ata_link *link, unsigned int scr, u32 val) 120c6fd2807SJeff Garzik { 12182ef04fbSTejun Heo struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); 12282ef04fbSTejun Heo unsigned int cfg_addr = get_scr_cfg_addr(link->ap, scr); 123c6fd2807SJeff Garzik 124c6fd2807SJeff Garzik pci_write_config_dword(pdev, cfg_addr, val); 125c6fd2807SJeff Garzik } 126c6fd2807SJeff Garzik 12782ef04fbSTejun Heo static int uli_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) 128c6fd2807SJeff Garzik { 129c6fd2807SJeff Garzik if (sc_reg > SCR_CONTROL) 130da3dbb17STejun Heo return -EINVAL; 131c6fd2807SJeff Garzik 13282ef04fbSTejun Heo *val = uli_scr_cfg_read(link, sc_reg); 133da3dbb17STejun Heo return 0; 134c6fd2807SJeff Garzik } 135c6fd2807SJeff Garzik 13682ef04fbSTejun Heo static int uli_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) 137c6fd2807SJeff Garzik { 138c6fd2807SJeff Garzik if (sc_reg > SCR_CONTROL) //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0 139da3dbb17STejun Heo return -EINVAL; 140c6fd2807SJeff Garzik 14182ef04fbSTejun Heo uli_scr_cfg_write(link, sc_reg, val); 142da3dbb17STejun Heo return 0; 143c6fd2807SJeff Garzik } 144c6fd2807SJeff Garzik 145c6fd2807SJeff Garzik static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 146c6fd2807SJeff Garzik { 1479a829ccfSTejun Heo const struct ata_port_info *ppi[] = { &uli_port_info, NULL }; 148c6fd2807SJeff Garzik unsigned int board_idx = (unsigned int) ent->driver_data; 1499a829ccfSTejun Heo struct ata_host *host; 150c6fd2807SJeff Garzik struct uli_priv *hpriv; 1510d5ff566STejun Heo void __iomem * const *iomap; 1529a829ccfSTejun Heo struct ata_ioports *ioaddr; 1539a829ccfSTejun Heo int n_ports, rc; 154c6fd2807SJeff Garzik 15506296a1eSJoe Perches ata_print_version_once(&pdev->dev, DRV_VERSION); 156c6fd2807SJeff Garzik 15724dc5f33STejun Heo rc = pcim_enable_device(pdev); 158c6fd2807SJeff Garzik if (rc) 159c6fd2807SJeff Garzik return rc; 160c6fd2807SJeff Garzik 1619a829ccfSTejun Heo n_ports = 2; 1629a829ccfSTejun Heo if (board_idx == uli_5287) 1639a829ccfSTejun Heo n_ports = 4; 1641626aeb8STejun Heo 1651626aeb8STejun Heo /* allocate the host */ 1661626aeb8STejun Heo host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); 1671626aeb8STejun Heo if (!host) 1681626aeb8STejun Heo return -ENOMEM; 169c6fd2807SJeff Garzik 17024dc5f33STejun Heo hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); 17124dc5f33STejun Heo if (!hpriv) 17224dc5f33STejun Heo return -ENOMEM; 1739a829ccfSTejun Heo host->private_data = hpriv; 174c6fd2807SJeff Garzik 1751626aeb8STejun Heo /* the first two ports are standard SFF */ 1769363c382STejun Heo rc = ata_pci_sff_init_host(host); 1771626aeb8STejun Heo if (rc) 1781626aeb8STejun Heo return rc; 1791626aeb8STejun Heo 180c7087652STejun Heo ata_pci_bmdma_init(host); 1811626aeb8STejun Heo 1829a829ccfSTejun Heo iomap = host->iomap; 1830d5ff566STejun Heo 184c6fd2807SJeff Garzik switch (board_idx) { 185c6fd2807SJeff Garzik case uli_5287: 1861626aeb8STejun Heo /* If there are four, the last two live right after 1871626aeb8STejun Heo * the standard SFF ports. 1881626aeb8STejun Heo */ 189c6fd2807SJeff Garzik hpriv->scr_cfg_addr[0] = ULI5287_BASE; 190c6fd2807SJeff Garzik hpriv->scr_cfg_addr[1] = ULI5287_BASE + ULI5287_OFFS; 191c6fd2807SJeff Garzik 1929a829ccfSTejun Heo ioaddr = &host->ports[2]->ioaddr; 1939a829ccfSTejun Heo ioaddr->cmd_addr = iomap[0] + 8; 1949a829ccfSTejun Heo ioaddr->altstatus_addr = 1959a829ccfSTejun Heo ioaddr->ctl_addr = (void __iomem *) 1960d5ff566STejun Heo ((unsigned long)iomap[1] | ATA_PCI_CTL_OFS) + 4; 1979a829ccfSTejun Heo ioaddr->bmdma_addr = iomap[4] + 16; 198c6fd2807SJeff Garzik hpriv->scr_cfg_addr[2] = ULI5287_BASE + ULI5287_OFFS*4; 1999363c382STejun Heo ata_sff_std_ports(ioaddr); 200c6fd2807SJeff Garzik 201cbcdd875STejun Heo ata_port_desc(host->ports[2], 202cbcdd875STejun Heo "cmd 0x%llx ctl 0x%llx bmdma 0x%llx", 203cbcdd875STejun Heo (unsigned long long)pci_resource_start(pdev, 0) + 8, 204cbcdd875STejun Heo ((unsigned long long)pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS) + 4, 205cbcdd875STejun Heo (unsigned long long)pci_resource_start(pdev, 4) + 16); 206cbcdd875STejun Heo 2079a829ccfSTejun Heo ioaddr = &host->ports[3]->ioaddr; 2089a829ccfSTejun Heo ioaddr->cmd_addr = iomap[2] + 8; 2099a829ccfSTejun Heo ioaddr->altstatus_addr = 2109a829ccfSTejun Heo ioaddr->ctl_addr = (void __iomem *) 2110d5ff566STejun Heo ((unsigned long)iomap[3] | ATA_PCI_CTL_OFS) + 4; 2129a829ccfSTejun Heo ioaddr->bmdma_addr = iomap[4] + 24; 213c6fd2807SJeff Garzik hpriv->scr_cfg_addr[3] = ULI5287_BASE + ULI5287_OFFS*5; 2149363c382STejun Heo ata_sff_std_ports(ioaddr); 215cbcdd875STejun Heo 216cbcdd875STejun Heo ata_port_desc(host->ports[2], 217cbcdd875STejun Heo "cmd 0x%llx ctl 0x%llx bmdma 0x%llx", 218cbcdd875STejun Heo (unsigned long long)pci_resource_start(pdev, 2) + 9, 219cbcdd875STejun Heo ((unsigned long long)pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS) + 4, 220cbcdd875STejun Heo (unsigned long long)pci_resource_start(pdev, 4) + 24); 221cbcdd875STejun Heo 222c6fd2807SJeff Garzik break; 223c6fd2807SJeff Garzik 224c6fd2807SJeff Garzik case uli_5289: 225c6fd2807SJeff Garzik hpriv->scr_cfg_addr[0] = ULI5287_BASE; 226c6fd2807SJeff Garzik hpriv->scr_cfg_addr[1] = ULI5287_BASE + ULI5287_OFFS; 227c6fd2807SJeff Garzik break; 228c6fd2807SJeff Garzik 229c6fd2807SJeff Garzik case uli_5281: 230c6fd2807SJeff Garzik hpriv->scr_cfg_addr[0] = ULI5281_BASE; 231c6fd2807SJeff Garzik hpriv->scr_cfg_addr[1] = ULI5281_BASE + ULI5281_OFFS; 232c6fd2807SJeff Garzik break; 233c6fd2807SJeff Garzik 234c6fd2807SJeff Garzik default: 235c6fd2807SJeff Garzik BUG(); 236c6fd2807SJeff Garzik break; 237c6fd2807SJeff Garzik } 238c6fd2807SJeff Garzik 239c6fd2807SJeff Garzik pci_set_master(pdev); 240c6fd2807SJeff Garzik pci_intx(pdev, 1); 241c3b28894STejun Heo return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt, 2429363c382STejun Heo IRQF_SHARED, &uli_sht); 243c6fd2807SJeff Garzik } 244c6fd2807SJeff Garzik 2452fc75da0SAxel Lin module_pci_driver(uli_pci_driver); 246