Isaku Yamahata | 4f5e19e | 2009-10-30 21:21:06 +0900 | [diff] [blame] | 1 | /* |
| 2 | * pci_host.c |
| 3 | * |
| 4 | * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp> |
| 5 | * VA Linux Systems Japan K.K. |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; either version 2 of the License, or |
| 10 | * (at your option) any later version. |
| 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 along |
Blue Swirl | 70539e1 | 2010-03-07 15:48:43 +0000 | [diff] [blame] | 18 | * with this program; if not, see <http://www.gnu.org/licenses/>. |
Isaku Yamahata | 4f5e19e | 2009-10-30 21:21:06 +0900 | [diff] [blame] | 19 | */ |
| 20 | |
| 21 | #include "pci.h" |
| 22 | #include "pci_host.h" |
| 23 | |
| 24 | /* debug PCI */ |
| 25 | //#define DEBUG_PCI |
| 26 | |
| 27 | #ifdef DEBUG_PCI |
| 28 | #define PCI_DPRINTF(fmt, ...) \ |
| 29 | do { printf("pci_host_data: " fmt , ## __VA_ARGS__); } while (0) |
| 30 | #else |
| 31 | #define PCI_DPRINTF(fmt, ...) |
| 32 | #endif |
| 33 | |
Isaku Yamahata | 766347c | 2009-10-30 21:21:15 +0900 | [diff] [blame] | 34 | /* |
| 35 | * PCI address |
| 36 | * bit 16 - 24: bus number |
| 37 | * bit 8 - 15: devfun number |
| 38 | * bit 0 - 7: offset in configuration space of a given pci device |
| 39 | */ |
| 40 | |
| 41 | /* the helper functio to get a PCIDeice* for a given pci address */ |
Isaku Yamahata | 8d6514f | 2009-11-12 13:17:23 +0200 | [diff] [blame] | 42 | static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr) |
Isaku Yamahata | 766347c | 2009-10-30 21:21:15 +0900 | [diff] [blame] | 43 | { |
Isaku Yamahata | 42331e9 | 2009-11-12 14:58:37 +0900 | [diff] [blame] | 44 | uint8_t bus_num = addr >> 16; |
| 45 | uint8_t devfn = addr >> 8; |
| 46 | |
Isaku Yamahata | 5256d8b | 2011-01-27 15:56:36 +0900 | [diff] [blame] | 47 | return pci_find_device(bus, bus_num, devfn); |
Isaku Yamahata | 766347c | 2009-10-30 21:21:15 +0900 | [diff] [blame] | 48 | } |
| 49 | |
Isaku Yamahata | ce195fb | 2009-10-30 21:21:16 +0900 | [diff] [blame] | 50 | void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len) |
Isaku Yamahata | 766347c | 2009-10-30 21:21:15 +0900 | [diff] [blame] | 51 | { |
Isaku Yamahata | 8d6514f | 2009-11-12 13:17:23 +0200 | [diff] [blame] | 52 | PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); |
Isaku Yamahata | 7ac901c | 2009-11-12 14:58:32 +0900 | [diff] [blame] | 53 | uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); |
Isaku Yamahata | 766347c | 2009-10-30 21:21:15 +0900 | [diff] [blame] | 54 | |
| 55 | if (!pci_dev) |
| 56 | return; |
| 57 | |
Blue Swirl | 0b987f1 | 2010-01-10 20:54:38 +0000 | [diff] [blame] | 58 | PCI_DPRINTF("%s: %s: addr=%02" PRIx32 " val=%08" PRIx32 " len=%d\n", |
Isaku Yamahata | 766347c | 2009-10-30 21:21:15 +0900 | [diff] [blame] | 59 | __func__, pci_dev->name, config_addr, val, len); |
| 60 | pci_dev->config_write(pci_dev, config_addr, val, len); |
| 61 | } |
| 62 | |
Isaku Yamahata | ce195fb | 2009-10-30 21:21:16 +0900 | [diff] [blame] | 63 | uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len) |
Isaku Yamahata | 766347c | 2009-10-30 21:21:15 +0900 | [diff] [blame] | 64 | { |
Isaku Yamahata | 8d6514f | 2009-11-12 13:17:23 +0200 | [diff] [blame] | 65 | PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); |
Isaku Yamahata | 7ac901c | 2009-11-12 14:58:32 +0900 | [diff] [blame] | 66 | uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); |
Isaku Yamahata | 766347c | 2009-10-30 21:21:15 +0900 | [diff] [blame] | 67 | uint32_t val; |
| 68 | |
Michael S. Tsirkin | 4677d8e | 2009-11-12 14:58:31 +0900 | [diff] [blame] | 69 | assert(len == 1 || len == 2 || len == 4); |
Isaku Yamahata | 766347c | 2009-10-30 21:21:15 +0900 | [diff] [blame] | 70 | if (!pci_dev) { |
Michael S. Tsirkin | 4677d8e | 2009-11-12 14:58:31 +0900 | [diff] [blame] | 71 | return ~0x0; |
Isaku Yamahata | 766347c | 2009-10-30 21:21:15 +0900 | [diff] [blame] | 72 | } |
| 73 | |
Michael S. Tsirkin | 4677d8e | 2009-11-12 14:58:31 +0900 | [diff] [blame] | 74 | val = pci_dev->config_read(pci_dev, config_addr, len); |
| 75 | PCI_DPRINTF("%s: %s: addr=%02"PRIx32" val=%08"PRIx32" len=%d\n", |
| 76 | __func__, pci_dev->name, config_addr, val, len); |
| 77 | |
Isaku Yamahata | 766347c | 2009-10-30 21:21:15 +0900 | [diff] [blame] | 78 | return val; |
| 79 | } |
| 80 | |
Alexander Graf | 6ebf590 | 2010-12-08 12:05:40 +0100 | [diff] [blame] | 81 | static void pci_host_config_write(ReadWriteHandler *handler, |
| 82 | pcibus_t addr, uint32_t val, int len) |
Isaku Yamahata | a455783 | 2009-10-30 21:21:07 +0900 | [diff] [blame] | 83 | { |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 84 | PCIHostState *s = container_of(handler, PCIHostState, conf_handler); |
Isaku Yamahata | a455783 | 2009-10-30 21:21:07 +0900 | [diff] [blame] | 85 | |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 86 | PCI_DPRINTF("%s addr %" FMT_PCIBUS " %d val %"PRIx32"\n", |
| 87 | __func__, addr, len, val); |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 88 | s->config_reg = val; |
| 89 | } |
| 90 | |
Alexander Graf | 6ebf590 | 2010-12-08 12:05:40 +0100 | [diff] [blame] | 91 | static uint32_t pci_host_config_read(ReadWriteHandler *handler, |
| 92 | pcibus_t addr, int len) |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 93 | { |
Alexander Graf | 6ebf590 | 2010-12-08 12:05:40 +0100 | [diff] [blame] | 94 | PCIHostState *s = container_of(handler, PCIHostState, conf_handler); |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 95 | uint32_t val = s->config_reg; |
| 96 | |
| 97 | PCI_DPRINTF("%s addr %" FMT_PCIBUS " len %d val %"PRIx32"\n", |
| 98 | __func__, addr, len, val); |
| 99 | return val; |
| 100 | } |
| 101 | |
Alexander Graf | 6ebf590 | 2010-12-08 12:05:40 +0100 | [diff] [blame] | 102 | static void pci_host_data_write(ReadWriteHandler *handler, |
| 103 | pcibus_t addr, uint32_t val, int len) |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 104 | { |
| 105 | PCIHostState *s = container_of(handler, PCIHostState, data_handler); |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 106 | PCI_DPRINTF("write addr %" FMT_PCIBUS " len %d val %x\n", |
| 107 | addr, len, val); |
| 108 | if (s->config_reg & (1u << 31)) |
| 109 | pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); |
| 110 | } |
| 111 | |
Alexander Graf | 6ebf590 | 2010-12-08 12:05:40 +0100 | [diff] [blame] | 112 | static uint32_t pci_host_data_read(ReadWriteHandler *handler, |
| 113 | pcibus_t addr, int len) |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 114 | { |
| 115 | PCIHostState *s = container_of(handler, PCIHostState, data_handler); |
| 116 | uint32_t val; |
| 117 | if (!(s->config_reg & (1 << 31))) |
| 118 | return 0xffffffff; |
| 119 | val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); |
| 120 | PCI_DPRINTF("read addr %" FMT_PCIBUS " len %d val %x\n", |
| 121 | addr, len, val); |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 122 | return val; |
| 123 | } |
| 124 | |
| 125 | static void pci_host_init(PCIHostState *s) |
| 126 | { |
Alexander Graf | 6ebf590 | 2010-12-08 12:05:40 +0100 | [diff] [blame] | 127 | s->conf_handler.write = pci_host_config_write; |
| 128 | s->conf_handler.read = pci_host_config_read; |
| 129 | s->data_handler.write = pci_host_data_write; |
| 130 | s->data_handler.read = pci_host_data_read; |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 131 | } |
Isaku Yamahata | a455783 | 2009-10-30 21:21:07 +0900 | [diff] [blame] | 132 | |
Alexander Graf | 6ebf590 | 2010-12-08 12:05:40 +0100 | [diff] [blame] | 133 | int pci_host_conf_register_mmio(PCIHostState *s, int endian) |
Isaku Yamahata | a455783 | 2009-10-30 21:21:07 +0900 | [diff] [blame] | 134 | { |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 135 | pci_host_init(s); |
Alexander Graf | 6ebf590 | 2010-12-08 12:05:40 +0100 | [diff] [blame] | 136 | return cpu_register_io_memory_simple(&s->conf_handler, endian); |
Isaku Yamahata | a455783 | 2009-10-30 21:21:07 +0900 | [diff] [blame] | 137 | } |
| 138 | |
Isaku Yamahata | f08b32f | 2009-11-12 14:58:34 +0900 | [diff] [blame] | 139 | void pci_host_conf_register_ioport(pio_addr_t ioport, PCIHostState *s) |
Isaku Yamahata | a455783 | 2009-10-30 21:21:07 +0900 | [diff] [blame] | 140 | { |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 141 | pci_host_init(s); |
Alexander Graf | 6ebf590 | 2010-12-08 12:05:40 +0100 | [diff] [blame] | 142 | register_ioport_simple(&s->conf_handler, ioport, 4, 4); |
Gleb Natapov | c646f74 | 2010-12-08 13:35:00 +0200 | [diff] [blame] | 143 | sysbus_init_ioports(&s->busdev, ioport, 4); |
Isaku Yamahata | a455783 | 2009-10-30 21:21:07 +0900 | [diff] [blame] | 144 | } |
| 145 | |
Alexander Graf | 6ebf590 | 2010-12-08 12:05:40 +0100 | [diff] [blame] | 146 | int pci_host_data_register_mmio(PCIHostState *s, int endian) |
Isaku Yamahata | 4f5e19e | 2009-10-30 21:21:06 +0900 | [diff] [blame] | 147 | { |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 148 | pci_host_init(s); |
Alexander Graf | 6ebf590 | 2010-12-08 12:05:40 +0100 | [diff] [blame] | 149 | return cpu_register_io_memory_simple(&s->data_handler, endian); |
Isaku Yamahata | 4f5e19e | 2009-10-30 21:21:06 +0900 | [diff] [blame] | 150 | } |
| 151 | |
Isaku Yamahata | 4f5e19e | 2009-10-30 21:21:06 +0900 | [diff] [blame] | 152 | void pci_host_data_register_ioport(pio_addr_t ioport, PCIHostState *s) |
| 153 | { |
Michael S. Tsirkin | 9f6f042 | 2010-01-16 19:20:07 +0200 | [diff] [blame] | 154 | pci_host_init(s); |
Alexander Graf | 6ebf590 | 2010-12-08 12:05:40 +0100 | [diff] [blame] | 155 | register_ioport_simple(&s->data_handler, ioport, 4, 1); |
| 156 | register_ioport_simple(&s->data_handler, ioport, 4, 2); |
| 157 | register_ioport_simple(&s->data_handler, ioport, 4, 4); |
Gleb Natapov | c646f74 | 2010-12-08 13:35:00 +0200 | [diff] [blame] | 158 | sysbus_init_ioports(&s->busdev, ioport, 4); |
Isaku Yamahata | 4f5e19e | 2009-10-30 21:21:06 +0900 | [diff] [blame] | 159 | } |