1/* 2 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5/*- 6 * Copyright (C) 2002 Benno Rice. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 27 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32#include <stdlib.h> 33#include <string.h> 34 35#include <KernelExport.h> 36 37#include <platform/openfirmware/devices.h> 38#include <platform/openfirmware/openfirmware.h> 39#include <platform/openfirmware/pci.h> 40 41#include "pci_controller.h" 42#include "pci_io.h" 43#include "pci_openfirmware_priv.h" 44 45 46struct uninorth_range { 47 uint32 pci_hi; 48 uint32 pci_mid; 49 uint32 pci_lo; 50 uint32 host; 51 uint32 size_hi; 52 uint32 size_lo; 53}; 54 55struct uninorth_host_bridge { 56 int device_node; 57 addr_t address_registers; 58 addr_t data_registers; 59 uint32 bus; 60 struct uninorth_range ranges[6]; 61 int range_count; 62}; 63 64 65#define out8rb(address, value) ppc_out8((vuint8*)(address), value) 66#define out16rb(address, value) ppc_out16_reverse((vuint16*)(address), value) 67#define out32rb(address, value) ppc_out32_reverse((vuint32*)(address), value) 68#define in8rb(address) ppc_in8((const vuint8*)(address)) 69#define in16rb(address) ppc_in16_reverse((const vuint16*)(address)) 70#define in32rb(address) ppc_in32_reverse((const vuint32*)(address)) 71 72 73static int uninorth_enable_config(struct uninorth_host_bridge *bridge, 74 uint8 bus, uint8 slot, uint8 function, uint8 offset); 75 76static status_t uninorth_read_pci_config(void *cookie, uint8 bus, uint8 device, 77 uint8 function, uint8 offset, uint8 size, uint32 *value); 78static status_t uninorth_write_pci_config(void *cookie, uint8 bus, 79 uint8 device, uint8 function, uint8 offset, uint8 size, 80 uint32 value); 81static status_t uninorth_get_max_bus_devices(void *cookie, int32 *count); 82static status_t uninorth_read_pci_irq(void *cookie, uint8 bus, uint8 device, 83 uint8 function, uint8 pin, uint8 *irq); 84static status_t uninorth_write_pci_irq(void *cookie, uint8 bus, uint8 device, 85 uint8 function, uint8 pin, uint8 irq); 86 87static pci_controller sUniNorthPCIController = { 88 uninorth_read_pci_config, 89 uninorth_write_pci_config, 90 uninorth_get_max_bus_devices, 91 uninorth_read_pci_irq, 92 uninorth_write_pci_irq, 93}; 94 95 96static status_t 97uninorth_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function, 98 uint8 offset, uint8 size, uint32 *value) 99{ 100 uninorth_host_bridge *bridge = (uninorth_host_bridge*)cookie; 101 102 addr_t caoff = bridge->data_registers + (offset & 0x07); 103 104 if (uninorth_enable_config(bridge, bus, device, function, offset) != 0) { 105 switch (size) { 106 case 1: 107 *value = in8rb(caoff); 108 break; 109 case 2: 110 *value = in16rb(caoff); 111 break; 112 case 4: 113 *value = in32rb(caoff); 114 break; 115 default: 116 *value = 0xffffffff; 117 break; 118 } 119 } else 120 *value = 0xffffffff; 121 122 return B_OK; 123} 124 125 126static status_t uninorth_write_pci_config(void *cookie, uint8 bus, uint8 device, 127 uint8 function, uint8 offset, uint8 size, uint32 value) 128{ 129 uninorth_host_bridge *bridge = (uninorth_host_bridge*)cookie; 130 131 addr_t caoff = bridge->data_registers + (offset & 0x07); 132 133 if (uninorth_enable_config(bridge, bus, device, function, offset)) { 134 switch (size) { 135 case 1: 136 out8rb(caoff, (uint8)value); 137 (void)in8rb(caoff); 138 break; 139 case 2: 140 out16rb(caoff, (uint16)value); 141 (void)in16rb(caoff); 142 break; 143 case 4: 144 out32rb(caoff, value); 145 (void)in32rb(caoff); 146 break; 147 } 148 } 149 150 return B_OK; 151} 152 153 154static status_t uninorth_get_max_bus_devices(void *cookie, int32 *count) 155{ 156 *count = 32; 157 return B_OK; 158} 159 160 161static status_t uninorth_read_pci_irq(void *cookie, uint8 bus, uint8 device, 162 uint8 function, uint8 pin, uint8 *irq) 163{ 164 return B_ERROR; 165} 166 167 168static status_t uninorth_write_pci_irq(void *cookie, uint8 bus, uint8 device, 169 uint8 function, uint8 pin, uint8 irq) 170{ 171 return B_ERROR; 172} 173 174 175// #pragma mark - 176 177 178static int 179uninorth_enable_config(struct uninorth_host_bridge *bridge, uint8 bus, 180 uint8 slot, uint8 function, uint8 offset) 181{ 182// uint32 pass; 183// if (resource_int_value(device_get_name(sc->sc_dev), 184// device_get_unit(sc->sc_dev), "skipslot", &pass) == 0) { 185// if (pass == slot) 186// return (0); 187// } 188 189 uint32 cfgval; 190 if (bridge->bus == bus) { 191 /* 192 * No slots less than 11 on the primary bus 193 */ 194 if (slot < 11) 195 return (0); 196 197 cfgval = (1 << slot) | (function << 8) | (offset & 0xfc); 198 } else { 199 cfgval = (bus << 16) | (slot << 11) | (function << 8) | 200 (offset & 0xfc) | 1; 201 } 202 203 do { 204 out32rb(bridge->address_registers, cfgval); 205 } while (in32rb(bridge->address_registers) != cfgval); 206 207 return (1); 208} 209 210 211// #pragma mark - 212 213status_t 214ppc_openfirmware_probe_uninorth(int deviceNode, 215 const StringArrayPropertyValue &compatibleValue) 216{ 217 if (!compatibleValue.ContainsElement("uni-north")) 218 return B_ERROR; 219 220 uint32 reg[2]; 221 if (of_getprop(deviceNode, "reg", reg, sizeof(reg)) < 8) 222 return B_ERROR; 223 224 uint32 busrange[2]; 225 if (of_getprop(deviceNode, "bus-range", busrange, sizeof(busrange)) != 8) 226 return B_ERROR; 227 228 uninorth_host_bridge *bridge 229 = (uninorth_host_bridge*)malloc(sizeof(uninorth_host_bridge)); 230 if (!bridge) 231 return B_NO_MEMORY; 232 233 bridge->device_node = deviceNode; 234 bridge->address_registers = reg[0] + 0x800000; 235 bridge->data_registers = reg[0] + 0xc00000; 236 bridge->bus = busrange[0]; 237// TODO: Check whether address and data registers have already been mapped by 238// the Open Firmware. If not, map them ourselves. 239 240 memset(bridge->ranges, 0, sizeof(bridge->ranges)); 241 int bytesRead = of_getprop(deviceNode, "ranges", bridge->ranges, 242 sizeof(bridge->ranges)); 243 244 if (bytesRead < 0) { 245 dprintf("ppc_openfirmware_probe_uninorth: Could not get ranges.\n"); 246 free(bridge); 247 return B_ERROR; 248 } 249 bridge->range_count = bytesRead / sizeof(uninorth_range); 250 251 uninorth_range *ioRange = NULL; 252 uninorth_range *memoryRanges[2]; 253 int memoryRangeCount = 0; 254 255 for (int i = 0; i < bridge->range_count; i++) { 256 uninorth_range *range = bridge->ranges + i; 257 switch (range->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { 258 case OFW_PCI_PHYS_HI_SPACE_CONFIG: 259 break; 260 case OFW_PCI_PHYS_HI_SPACE_IO: 261 ioRange = range; 262 break; 263 case OFW_PCI_PHYS_HI_SPACE_MEM32: 264 memoryRanges[memoryRangeCount++] = range; 265 break; 266 case OFW_PCI_PHYS_HI_SPACE_MEM64: 267 break; 268 } 269 } 270 271 if (ioRange == NULL) { 272 dprintf("ppc_openfirmware_probe_uninorth: Can't find io range.\n"); 273 free(bridge); 274 return B_ERROR; 275 } 276 277 if (memoryRangeCount == 0) { 278 dprintf("ppc_openfirmware_probe_uninorth: Can't find mem ranges.\n"); 279 free(bridge); 280 return B_ERROR; 281 } 282 283 status_t error = pci_controller_add(&sUniNorthPCIController, bridge); 284 if (error != B_OK) 285 free(bridge); 286 return error; 287} 288