Uwe Hermann | b80dbf0 | 2007-04-22 19:08:13 +0000 | [diff] [blame] | 1 | /* |
Stefan Reinauer | 7e61e45 | 2008-01-18 10:35:56 +0000 | [diff] [blame] | 2 | * This file is part of the coreboot project. |
Uwe Hermann | b80dbf0 | 2007-04-22 19:08:13 +0000 | [diff] [blame] | 3 | * |
| 4 | * Copyright (C) 2005 Linux Networx |
| 5 | * (Written by Eric Biederman <ebiederman@lnxi.com> for Linux Networx) |
| 6 | * Copyright (C) 2005 Ronald G. Minnich <rminnich@gmail.com> |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License as published by |
| 10 | * the Free Software Foundation; version 2 of the License. |
| 11 | * |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License |
| 18 | * along with this program; if not, write to the Free Software |
| 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 20 | */ |
Yinghai Lu | 304f24c | 2005-07-08 02:56:47 +0000 | [diff] [blame] | 21 | |
| 22 | #include <console/console.h> |
| 23 | #include <device/device.h> |
| 24 | #include <device/pci.h> |
| 25 | #include <device/pci_ids.h> |
| 26 | #include <device/cardbus.h> |
| 27 | |
| 28 | /* I don't think this code is quite correct but it is close. |
| 29 | * Anyone with a cardbus bridge and a little time should be able |
| 30 | * to make it usable quickly. -- Eric Biederman 24 March 2005 |
| 31 | */ |
| 32 | |
| 33 | /* |
| 34 | * IO should be max 256 bytes. However, since we may |
| 35 | * have a P2P bridge below a cardbus bridge, we need 4K. |
| 36 | */ |
| 37 | #define CARDBUS_IO_SIZE (4096) |
| 38 | #define CARDBUS_MEM_SIZE (32*1024*1024) |
| 39 | |
| 40 | static void cardbus_record_bridge_resource( |
| 41 | device_t dev, resource_t moving, resource_t min_size, |
| 42 | unsigned index, unsigned long type) |
| 43 | { |
| 44 | /* Initiliaze the constraints on the current bus */ |
| 45 | struct resource *resource; |
| 46 | resource = 0; |
| 47 | if (moving) { |
| 48 | unsigned long gran; |
| 49 | resource_t step; |
| 50 | resource = new_resource(dev, index); |
| 51 | resource->size = 0; |
| 52 | gran = 0; |
| 53 | step = 1; |
| 54 | while((moving & step) == 0) { |
| 55 | gran += 1; |
| 56 | step <<= 1; |
| 57 | } |
| 58 | resource->gran = gran; |
| 59 | resource->align = gran; |
| 60 | resource->limit = moving | (step - 1); |
| 61 | resource->flags = type; |
| 62 | /* Don't let the minimum size exceed what we |
| 63 | * can put in the resource. |
| 64 | */ |
| 65 | if ((min_size - 1) > resource->limit) { |
| 66 | min_size = resource->limit + 1; |
| 67 | } |
| 68 | resource->size = min_size; |
| 69 | } |
| 70 | return; |
| 71 | } |
| 72 | |
| 73 | static void cardbus_size_bridge_resource(device_t dev, unsigned index) |
| 74 | { |
| 75 | struct resource *resource; |
| 76 | resource_t min_size; |
| 77 | resource = find_resource(dev, index); |
| 78 | if (resource) { |
| 79 | min_size = resource->size; |
Yinghai Lu | 304f24c | 2005-07-08 02:56:47 +0000 | [diff] [blame] | 80 | /* Allways allocate at least the miniumum size to a |
| 81 | * cardbus bridge in case a new card is plugged in. |
| 82 | */ |
| 83 | if (resource->size < min_size) { |
| 84 | resource->size = min_size; |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | void cardbus_read_resources(device_t dev) |
| 90 | { |
| 91 | resource_t moving_base, moving_limit, moving; |
| 92 | unsigned long type; |
| 93 | uint16_t ctl; |
Ronald G. Minnich | 43225bc | 2005-11-22 00:07:02 +0000 | [diff] [blame] | 94 | |
| 95 | /* See if needs a card control registers base address */ |
| 96 | |
| 97 | pci_get_resource(dev, PCI_BASE_ADDRESS_0); |
| 98 | |
| 99 | compact_resources(dev); |
| 100 | |
Yinghai Lu | 304f24c | 2005-07-08 02:56:47 +0000 | [diff] [blame] | 101 | /* See which bridge I/O resources are implemented */ |
| 102 | moving_base = pci_moving_config32(dev, PCI_CB_IO_BASE_0); |
| 103 | moving_limit = pci_moving_config32(dev, PCI_CB_IO_LIMIT_0); |
| 104 | moving = moving_base & moving_limit; |
| 105 | |
| 106 | /* Initialize the io space constraints on the current bus */ |
| 107 | cardbus_record_bridge_resource(dev, moving, CARDBUS_IO_SIZE, |
| 108 | PCI_CB_IO_BASE_0, IORESOURCE_IO); |
| 109 | cardbus_size_bridge_resource(dev, PCI_CB_IO_BASE_0); |
| 110 | |
| 111 | /* See which bridge I/O resources are implemented */ |
| 112 | moving_base = pci_moving_config32(dev, PCI_CB_IO_BASE_1); |
| 113 | moving_limit = pci_moving_config32(dev, PCI_CB_IO_LIMIT_1); |
| 114 | moving = moving_base & moving_limit; |
| 115 | |
| 116 | /* Initialize the io space constraints on the current bus */ |
| 117 | cardbus_record_bridge_resource(dev, moving, CARDBUS_IO_SIZE, |
| 118 | PCI_CB_IO_BASE_1, IORESOURCE_IO); |
| 119 | |
| 120 | /* If I can enable prefetch for mem0 */ |
| 121 | ctl = pci_read_config16(dev, PCI_CB_BRIDGE_CONTROL); |
| 122 | ctl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM0; |
| 123 | ctl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1; |
| 124 | ctl |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0; |
| 125 | pci_write_config16(dev, PCI_CB_BRIDGE_CONTROL, ctl); |
| 126 | ctl = pci_read_config16(dev, PCI_CB_BRIDGE_CONTROL); |
| 127 | |
| 128 | /* See which bridge memory resources are implemented */ |
| 129 | moving_base = pci_moving_config32(dev, PCI_CB_MEMORY_BASE_0); |
| 130 | moving_limit = pci_moving_config32(dev, PCI_CB_MEMORY_LIMIT_0); |
| 131 | moving = moving_base & moving_limit; |
| 132 | |
| 133 | /* Initialize the memory space constraints on the current bus */ |
| 134 | type = IORESOURCE_MEM; |
| 135 | if (ctl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) { |
| 136 | type |= IORESOURCE_PREFETCH; |
| 137 | } |
| 138 | cardbus_record_bridge_resource(dev, moving, CARDBUS_MEM_SIZE, |
| 139 | PCI_CB_MEMORY_BASE_0, type); |
| 140 | if (type & IORESOURCE_PREFETCH) { |
| 141 | cardbus_size_bridge_resource(dev, PCI_CB_MEMORY_BASE_0); |
| 142 | } |
| 143 | |
| 144 | /* See which bridge memory resources are implemented */ |
| 145 | moving_base = pci_moving_config32(dev, PCI_CB_MEMORY_BASE_1); |
| 146 | moving_limit = pci_moving_config32(dev, PCI_CB_MEMORY_LIMIT_1); |
| 147 | moving = moving_base & moving_limit; |
| 148 | |
| 149 | /* Initialize the memory space constraints on the current bus */ |
| 150 | cardbus_record_bridge_resource(dev, moving, CARDBUS_MEM_SIZE, |
| 151 | PCI_CB_MEMORY_BASE_1, IORESOURCE_MEM); |
| 152 | cardbus_size_bridge_resource(dev, PCI_CB_MEMORY_BASE_1); |
| 153 | |
| 154 | compact_resources(dev); |
| 155 | } |
| 156 | |
| 157 | void cardbus_enable_resources(device_t dev) |
| 158 | { |
| 159 | uint16_t ctrl; |
| 160 | ctrl = pci_read_config16(dev, PCI_CB_BRIDGE_CONTROL); |
| 161 | ctrl |= (dev->link[0].bridge_ctrl & ( |
| 162 | PCI_BRIDGE_CTL_PARITY | |
| 163 | PCI_BRIDGE_CTL_SERR | |
| 164 | PCI_BRIDGE_CTL_NO_ISA | |
| 165 | PCI_BRIDGE_CTL_VGA | |
| 166 | PCI_BRIDGE_CTL_MASTER_ABORT | |
| 167 | PCI_BRIDGE_CTL_BUS_RESET)); |
| 168 | ctrl |= (PCI_CB_BRIDGE_CTL_PARITY + PCI_CB_BRIDGE_CTL_SERR); /* error check */ |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 169 | printk(BIOS_DEBUG, "%s bridge ctrl <- %04x\n", dev_path(dev), ctrl); |
Yinghai Lu | 304f24c | 2005-07-08 02:56:47 +0000 | [diff] [blame] | 170 | pci_write_config16(dev, PCI_BRIDGE_CONTROL, ctrl); |
| 171 | |
| 172 | pci_dev_enable_resources(dev); |
| 173 | |
| 174 | enable_childrens_resources(dev); |
| 175 | } |
| 176 | |
| 177 | unsigned int cardbus_scan_bus(struct bus *bus, |
| 178 | unsigned min_devfn, unsigned max_devfn, |
| 179 | unsigned int max) |
| 180 | { |
| 181 | return pci_scan_bus(bus, min_devfn, max_devfn, max); |
| 182 | } |
| 183 | |
| 184 | |
| 185 | unsigned int cardbus_scan_bridge(device_t dev, unsigned int max) |
| 186 | { |
| 187 | struct bus *bus; |
| 188 | uint32_t buses; |
| 189 | uint16_t cr; |
| 190 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 191 | printk(BIOS_SPEW, "%s for %s\n", __func__, dev_path(dev)); |
Yinghai Lu | 304f24c | 2005-07-08 02:56:47 +0000 | [diff] [blame] | 192 | |
| 193 | bus = &dev->link[0]; |
| 194 | bus->dev = dev; |
| 195 | dev->links = 1; |
| 196 | |
| 197 | /* Set up the primary, secondary and subordinate bus numbers. We have |
| 198 | * no idea how many buses are behind this bridge yet, so we set the |
| 199 | * subordinate bus number to 0xff for the moment. |
| 200 | */ |
| 201 | bus->secondary = ++max; |
| 202 | bus->subordinate = 0xff; |
| 203 | |
| 204 | /* Clear all status bits and turn off memory, I/O and master enables. */ |
| 205 | cr = pci_read_config16(dev, PCI_COMMAND); |
| 206 | pci_write_config16(dev, PCI_COMMAND, 0x0000); |
| 207 | pci_write_config16(dev, PCI_STATUS, 0xffff); |
| 208 | |
| 209 | /* |
| 210 | * Read the existing primary/secondary/subordinate bus |
| 211 | * number configuration. |
| 212 | */ |
| 213 | buses = pci_read_config32(dev, PCI_CB_PRIMARY_BUS); |
| 214 | |
| 215 | /* Configure the bus numbers for this bridge: the configuration |
| 216 | * transactions will not be propagated by the bridge if it is not |
| 217 | * correctly configured. |
| 218 | */ |
| 219 | buses &= 0xff000000; |
| 220 | buses |= (((unsigned int) (dev->bus->secondary) << 0) | |
| 221 | ((unsigned int) (bus->secondary) << 8) | |
| 222 | ((unsigned int) (bus->subordinate) << 16)); |
| 223 | pci_write_config32(dev, PCI_CB_PRIMARY_BUS, buses); |
| 224 | |
| 225 | /* Now we can scan all subordinate buses |
| 226 | * i.e. the bus behind the bridge. |
| 227 | */ |
| 228 | max = cardbus_scan_bus(bus, 0x00, 0xff, max); |
| 229 | |
| 230 | /* We know the number of buses behind this bridge. Set the subordinate |
| 231 | * bus number to its real value. |
| 232 | */ |
| 233 | bus->subordinate = max; |
| 234 | buses = (buses & 0xff00ffff) | |
| 235 | ((unsigned int) (bus->subordinate) << 16); |
| 236 | pci_write_config32(dev, PCI_CB_PRIMARY_BUS, buses); |
| 237 | pci_write_config16(dev, PCI_COMMAND, cr); |
| 238 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 239 | printk(BIOS_SPEW, "%s returns max %d\n", __func__, max); |
Yinghai Lu | 304f24c | 2005-07-08 02:56:47 +0000 | [diff] [blame] | 240 | return max; |
| 241 | } |
| 242 | |
| 243 | struct device_operations default_cardbus_ops_bus = { |
| 244 | .read_resources = cardbus_read_resources, |
| 245 | .set_resources = pci_dev_set_resources, |
| 246 | .enable_resources = cardbus_enable_resources, |
| 247 | .init = 0, |
| 248 | .scan_bus = cardbus_scan_bridge, |
| 249 | .enable = 0, |
| 250 | .reset_bus = pci_bus_reset, |
| 251 | }; |