Zheng Bao | d098575 | 2011-01-20 04:45:48 +0000 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2010 Advanced Micro Devices, Inc. |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; version 2 of the License. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
Zheng Bao | d098575 | 2011-01-20 04:45:48 +0000 | [diff] [blame] | 14 | */ |
| 15 | |
| 16 | #include <console/console.h> |
| 17 | #include <device/device.h> |
| 18 | #include <delay.h> |
| 19 | #include <device/pci.h> |
| 20 | #include <device/pci_ids.h> |
| 21 | #include <device/pci_ops.h> |
| 22 | #include <arch/io.h> |
| 23 | #include "sb800.h" |
| 24 | |
| 25 | static int sata_drive_detect(int portnum, u16 iobar) |
| 26 | { |
| 27 | u8 byte, byte2; |
| 28 | int i = 0; |
| 29 | outb(0xA0 + 0x10 * (portnum % 2), iobar + 0x6); |
| 30 | while (byte = inb(iobar + 0x6), byte2 = inb(iobar + 0x7), |
| 31 | (byte != (0xA0 + 0x10 * (portnum % 2))) || |
| 32 | ((byte2 & 0x88) != 0)) { |
| 33 | printk(BIOS_SPEW, "0x6=%x, 0x7=%x\n", byte, byte2); |
| 34 | if (byte != (0xA0 + 0x10 * (portnum % 2))) { |
| 35 | /* This will happen at the first iteration of this loop |
| 36 | * if the first SATA port is unpopulated and the |
Martin Roth | dcf253c | 2014-12-16 20:51:31 -0700 | [diff] [blame] | 37 | * second SATA port is populated. |
Zheng Bao | d098575 | 2011-01-20 04:45:48 +0000 | [diff] [blame] | 38 | */ |
| 39 | printk(BIOS_DEBUG, "drive no longer selected after %i ms, " |
| 40 | "retrying init\n", i * 10); |
| 41 | return 1; |
| 42 | } else |
| 43 | printk(BIOS_SPEW, "drive detection not yet completed, " |
| 44 | "waiting...\n"); |
| 45 | mdelay(10); |
| 46 | i++; |
| 47 | } |
| 48 | printk(BIOS_SPEW, "drive detection done after %i ms\n", i * 10); |
| 49 | return 0; |
| 50 | } |
| 51 | |
Vladimir Serbinenko | 8a87820 | 2014-11-09 20:12:25 +0100 | [diff] [blame] | 52 | static void sb800_setup_sata_phys(struct device *dev) |
Zheng Bao | 066cbe0 | 2011-01-27 02:19:55 +0000 | [diff] [blame] | 53 | { |
| 54 | int i; |
| 55 | static const u32 sata_phy[][3] = { |
| 56 | {0x0056A607, 0x00061400, 0x00061302}, /* port 0 */ |
| 57 | {0x0056A607, 0x00061400, 0x00061302}, /* port 1 */ |
| 58 | {0x0056A607, 0x00061402, 0x00064300}, /* port 2 */ |
| 59 | {0x0056A607, 0x00061402, 0x00064300}, /* port 3 */ |
| 60 | {0x0056A700, 0x00061502, 0x00064302}, /* port 4 */ |
| 61 | {0x0056A700, 0x00061502, 0x00064302} /* port 5 */ |
| 62 | }; |
| 63 | /* RPR8.4 */ |
| 64 | /* Port 0 - 5 */ |
| 65 | for (i = 0; i < 6; i++) { |
| 66 | pci_write_config16(dev, 0x84, 0x3006 | i << 9); |
| 67 | pci_write_config32(dev, 0x94, sata_phy[i][0]); /* Gen 3 */ |
| 68 | pci_write_config16(dev, 0x84, 0x2006 | i << 9); |
| 69 | pci_write_config32(dev, 0x94, sata_phy[i][1]); /* Gen 2 */ |
| 70 | pci_write_config16(dev, 0x84, 0x1006 | i << 9); |
| 71 | pci_write_config32(dev, 0x94, sata_phy[i][2]); /* Gen 1 */ |
| 72 | } |
Zheng Bao | 9dcca3b | 2011-01-25 06:06:58 +0000 | [diff] [blame] | 73 | |
Zheng Bao | 066cbe0 | 2011-01-27 02:19:55 +0000 | [diff] [blame] | 74 | } |
Zheng Bao | d098575 | 2011-01-20 04:45:48 +0000 | [diff] [blame] | 75 | static void sata_init(struct device *dev) |
| 76 | { |
| 77 | u8 byte; |
| 78 | u16 word; |
| 79 | u32 dword; |
| 80 | u8 rev_id; |
Kevin Paul Herbert | bde6d30 | 2014-12-24 18:43:20 -0800 | [diff] [blame] | 81 | void *sata_bar5; |
Zheng Bao | d098575 | 2011-01-20 04:45:48 +0000 | [diff] [blame] | 82 | u16 sata_bar0, sata_bar1, sata_bar2, sata_bar3, sata_bar4; |
| 83 | int i, j; |
| 84 | |
| 85 | struct southbridge_ati_sb800_config *conf; |
| 86 | conf = dev->chip_info; |
| 87 | |
| 88 | device_t sm_dev; |
| 89 | /* SATA SMBus Disable */ |
| 90 | /* sm_dev = pci_locate_device(PCI_ID(0x1002, 0x4385), 0); */ |
| 91 | sm_dev = dev_find_slot(0, PCI_DEVFN(0x14, 0)); |
| 92 | |
| 93 | /* get rev_id */ |
| 94 | rev_id = pci_read_config8(sm_dev, 0x08) - 0x2F; |
| 95 | |
Martin Roth | dcf253c | 2014-12-16 20:51:31 -0700 | [diff] [blame] | 96 | /* get base address */ |
Kevin Paul Herbert | bde6d30 | 2014-12-24 18:43:20 -0800 | [diff] [blame] | 97 | sata_bar5 = (void *)(pci_read_config32(dev, 0x24) & ~0x3FF); |
Zheng Bao | d098575 | 2011-01-20 04:45:48 +0000 | [diff] [blame] | 98 | sata_bar0 = pci_read_config16(dev, 0x10) & ~0x7; |
| 99 | sata_bar1 = pci_read_config16(dev, 0x14) & ~0x3; |
| 100 | sata_bar2 = pci_read_config16(dev, 0x18) & ~0x7; |
| 101 | sata_bar3 = pci_read_config16(dev, 0x1C) & ~0x3; |
| 102 | sata_bar4 = pci_read_config16(dev, 0x20) & ~0xf; |
| 103 | |
| 104 | printk(BIOS_SPEW, "sata_bar0=%x\n", sata_bar0); /* 3030 */ |
| 105 | printk(BIOS_SPEW, "sata_bar1=%x\n", sata_bar1); /* 3070 */ |
| 106 | printk(BIOS_SPEW, "sata_bar2=%x\n", sata_bar2); /* 3040 */ |
| 107 | printk(BIOS_SPEW, "sata_bar3=%x\n", sata_bar3); /* 3080 */ |
| 108 | printk(BIOS_SPEW, "sata_bar4=%x\n", sata_bar4); /* 3000 */ |
Kevin Paul Herbert | bde6d30 | 2014-12-24 18:43:20 -0800 | [diff] [blame] | 109 | printk(BIOS_SPEW, "sata_bar5=%p\n", sata_bar5); /* e0309000 */ |
Zheng Bao | d098575 | 2011-01-20 04:45:48 +0000 | [diff] [blame] | 110 | |
| 111 | /* SERR-Enable */ |
| 112 | word = pci_read_config16(dev, 0x04); |
| 113 | word |= (1 << 8); |
| 114 | pci_write_config16(dev, 0x04, word); |
| 115 | |
| 116 | /* Set SATA Operation Mode, Set to IDE mode */ |
| 117 | byte = pci_read_config8(dev, 0x40); |
| 118 | byte |= (1 << 0); |
| 119 | //byte |= (1 << 4); |
| 120 | pci_write_config8(dev, 0x40, byte); |
| 121 | |
| 122 | dword = 0x01018f00; |
| 123 | pci_write_config32(dev, 0x8, dword); |
| 124 | |
| 125 | /* Program the 2C to 0x43801002 */ |
| 126 | dword = 0x43801002; |
| 127 | pci_write_config32(dev, 0x2c, dword); |
| 128 | |
| 129 | pci_write_config8(dev, 0x34, 0x70); /* 8.11 SATA MSI and D3 Power State Capability */ |
| 130 | |
| 131 | dword = read32(sata_bar5 + 0xFC); |
| 132 | dword &= ~(1 << 11); /* rpr 8.8. Disabling Aggressive Link Power Management */ |
| 133 | dword &= ~(1 << 12); /* rpr 8.9.1 Disabling Port Multiplier support. */ |
| 134 | dword &= ~(1 << 10); /* rpr 8.9.2 disabling FIS-based Switching support */ |
| 135 | dword &= ~(1 << 19); /* rpr 8.10. Disabling CCC (Command Completion Coalescing) Support */ |
| 136 | write32((sata_bar5 + 0xFC), dword); |
| 137 | |
| 138 | dword = read32(sata_bar5 + 0xF8); |
| 139 | dword &= ~(0x3F << 22); /* rpr 8.9.2 disabling FIS-based Switching support */ |
| 140 | write32(sata_bar5 + 0xF8, dword); |
| 141 | |
| 142 | byte = pci_read_config8(dev, 0x40); |
| 143 | byte &= ~(1 << 0); |
| 144 | pci_write_config8(dev, 0x40, byte); |
| 145 | |
| 146 | /* rpr 8.3 */ |
| 147 | printk(BIOS_SPEW, "rev_id=%x\n", rev_id); |
| 148 | dword = pci_read_config32(dev, 0x84); |
| 149 | if (rev_id == 0x11) /* A11 */ |
| 150 | dword |= 1 << 22; |
| 151 | pci_write_config32(dev, 0x84, dword); |
| 152 | |
| 153 | /* rpr8.12 Program the watchdog counter to 0x20 */ |
| 154 | byte = pci_read_config8(dev, 0x44); |
| 155 | byte |= 1 << 0; |
| 156 | pci_write_config8(dev, 0x44, byte); |
| 157 | |
| 158 | pci_write_config8(dev, 0x46, 0x20); |
| 159 | |
Zheng Bao | 066cbe0 | 2011-01-27 02:19:55 +0000 | [diff] [blame] | 160 | sb800_setup_sata_phys(dev); |
Zheng Bao | d098575 | 2011-01-20 04:45:48 +0000 | [diff] [blame] | 161 | /* Enable the I/O, MM, BusMaster access for SATA */ |
| 162 | byte = pci_read_config8(dev, 0x4); |
| 163 | byte |= 7 << 0; |
| 164 | pci_write_config8(dev, 0x4, byte); |
| 165 | |
| 166 | /* RPR7.7 SATA drive detection. */ |
| 167 | /* Use BAR5+0x128,BAR0 for Primary Slave */ |
| 168 | /* Use BAR5+0x1A8,BAR0 for Primary Slave */ |
| 169 | /* Use BAR5+0x228,BAR2 for Secondary Master */ |
| 170 | /* Use BAR5+0x2A8,BAR2 for Secondary Slave */ |
| 171 | /* Use BAR5+0x328,PATA_BAR0/2 for Primary/Secondary master emulation */ |
| 172 | /* Use BAR5+0x3A8,PATA_BAR0/2 for Primary/Secondary Slave emulation */ |
| 173 | |
| 174 | /* TODO: port 4,5, which are PATA emulations. What are PATA_BARs? */ |
| 175 | |
| 176 | for (i = 0; i < 4; i++) { |
| 177 | byte = read8(sata_bar5 + 0x128 + 0x80 * i); |
| 178 | printk(BIOS_SPEW, "SATA port %i status = %x\n", i, byte); |
| 179 | byte &= 0xF; |
| 180 | if( byte == 0x1 ) { |
| 181 | /* If the drive status is 0x1 then we see it but we aren't talking to it. */ |
| 182 | /* Try to do something about it. */ |
| 183 | printk(BIOS_SPEW, "SATA device detected but not talking. Trying lower speed.\n"); |
| 184 | |
| 185 | /* Read in Port-N Serial ATA Control Register */ |
| 186 | byte = read8(sata_bar5 + 0x12C + 0x80 * i); |
| 187 | |
| 188 | /* Set Reset Bit and 1.5g bit */ |
| 189 | byte |= 0x11; |
| 190 | write8((sata_bar5 + 0x12C + 0x80 * i), byte); |
| 191 | |
| 192 | /* Wait 1ms */ |
| 193 | mdelay(1); |
| 194 | |
| 195 | /* Clear Reset Bit */ |
| 196 | byte &= ~0x01; |
| 197 | write8((sata_bar5 + 0x12C + 0x80 * i), byte); |
| 198 | |
| 199 | /* Wait 1ms */ |
| 200 | mdelay(1); |
| 201 | |
| 202 | /* Reread status */ |
| 203 | byte = read8(sata_bar5 + 0x128 + 0x80 * i); |
| 204 | printk(BIOS_SPEW, "SATA port %i status = %x\n", i, byte); |
| 205 | byte &= 0xF; |
| 206 | } |
| 207 | |
| 208 | if (byte == 0x3) { |
| 209 | for (j = 0; j < 10; j++) { |
| 210 | if (!sata_drive_detect(i, ((i / 2) == 0) ? sata_bar0 : sata_bar2)) |
| 211 | break; |
| 212 | } |
| 213 | printk(BIOS_DEBUG, "%s %s device is %sready after %i tries\n", |
| 214 | (i / 2) ? "Secondary" : "Primary", |
| 215 | (i % 2 ) ? "Slave" : "Master", |
| 216 | (j == 10) ? "not " : "", |
| 217 | (j == 10) ? j : j + 1); |
| 218 | } else { |
| 219 | printk(BIOS_DEBUG, "No %s %s SATA drive on Slot%i\n", |
| 220 | (i / 2) ? "Secondary" : "Primary", |
| 221 | (i % 2 ) ? "Slave" : "Master", i); |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | /* Below is CIM InitSataLateFar */ |
| 226 | /* Enable interrupts from the HBA */ |
| 227 | byte = read8(sata_bar5 + 0x4); |
| 228 | byte |= 1 << 1; |
| 229 | write8((sata_bar5 + 0x4), byte); |
| 230 | |
| 231 | /* Clear error status */ |
| 232 | write32((sata_bar5 + 0x130), 0xFFFFFFFF); |
| 233 | write32((sata_bar5 + 0x1b0), 0xFFFFFFFF); |
| 234 | write32((sata_bar5 + 0x230), 0xFFFFFFFF); |
| 235 | write32((sata_bar5 + 0x2b0), 0xFFFFFFFF); |
| 236 | write32((sata_bar5 + 0x330), 0xFFFFFFFF); |
| 237 | write32((sata_bar5 + 0x3b0), 0xFFFFFFFF); |
| 238 | |
| 239 | /* Clear SATA status,Firstly we get the AcpiGpe0BlkAddr */ |
| 240 | /* ????? why CIM does not set the AcpiGpe0BlkAddr , but use it??? */ |
| 241 | |
| 242 | /* word = 0x0000; */ |
| 243 | /* word = pm_ioread(0x28); */ |
| 244 | /* byte = pm_ioread(0x29); */ |
| 245 | /* word |= byte<<8; */ |
| 246 | /* printk(BIOS_DEBUG, "AcpiGpe0Blk addr = %x\n", word); */ |
| 247 | /* write32(word, 0x80000000); */ |
| 248 | } |
| 249 | |
| 250 | static struct pci_operations lops_pci = { |
| 251 | /* .set_subsystem = pci_dev_set_subsystem, */ |
| 252 | }; |
| 253 | |
| 254 | static struct device_operations sata_ops = { |
| 255 | .read_resources = pci_dev_read_resources, |
| 256 | .set_resources = pci_dev_set_resources, |
| 257 | .enable_resources = pci_dev_enable_resources, |
| 258 | .init = sata_init, |
| 259 | .scan_bus = 0, |
| 260 | .ops_pci = &lops_pci, |
| 261 | }; |
| 262 | |
| 263 | static const struct pci_driver sata0_driver __pci_driver = { |
| 264 | .ops = &sata_ops, |
| 265 | .vendor = PCI_VENDOR_ID_ATI, |
| 266 | .device = PCI_DEVICE_ID_ATI_SB800_SATA, |
| 267 | }; |