Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2012-2013 Alexandru Gagniuc <mr.nuke.me@gmail.com> |
| 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, either version 2 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 15 | */ |
| 16 | |
| 17 | #include <arch/io.h> |
| 18 | #include <arch/pirq_routing.h> |
| 19 | #include <console/console.h> |
| 20 | #include <device/pci.h> |
| 21 | #include <device/pci_ids.h> |
| 22 | #include <pc80/i8259.h> |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 23 | #include <drivers/generic/ioapic/chip.h> |
| 24 | |
| 25 | #include "vx900.h" |
| 26 | #include "chip.h" |
| 27 | |
| 28 | /** |
Martin Roth | 543888d | 2015-01-06 10:20:42 -0700 | [diff] [blame] | 29 | * @file vx900/lpc.c |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 30 | * |
| 31 | * STATUS: |
| 32 | * We do a fair bit of setup, and most of it seems to work fairly well. There |
| 33 | * are still a few FIXME items here and there, but overall, this code hasn't |
| 34 | * been touched much from its initial 2012 version to 2013, when it was revived. |
| 35 | * |
| 36 | * We do the IOAPIC setup with the assumption that it is declared in the |
| 37 | * mainboard's devicetree.cb. We cannot use the IOAPIC however. The interrupts |
| 38 | * do not make it to the CPU. This issue is still under investigation. |
| 39 | * |
| 40 | * We also route PIRQs with CONFIG_PIRQ_ROUTE. This is currently the only way to |
| 41 | * get interrupts working. |
| 42 | * |
| 43 | * On the VX900, the keyboard can be connected directly to the chipset |
| 44 | * (referenced as "internal keyboard" in the documents). As long as that is the |
| 45 | * case (not connected to the superIO), and we disable the superIO keyboard LDN, |
| 46 | * it will work, but perhaps this should be more configurable. |
| 47 | */ |
| 48 | |
Elyes HAOUAS | 2a5f6cb | 2018-05-25 08:09:53 +0200 | [diff] [blame] | 49 | static void vx900_lpc_misc_stuff(struct device *dev) |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 50 | { |
| 51 | char extint; |
| 52 | u8 val; |
| 53 | struct northbridge_via_vx900_config *nb = (void *)dev->chip_info; |
| 54 | |
| 55 | /* GPIO 11,10 to SATALED [1,0] */ |
Kyösti Mälkki | c043408 | 2019-02-07 16:18:20 +0200 | [diff] [blame] | 56 | pci_or_config8(dev, 0xe4, 1 << 0); |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 57 | |
| 58 | /* Route the external interrupt line */ |
| 59 | extint = nb->ext_int_route_to_pirq; |
| 60 | if (extint < 'A' || extint > 'H') { |
| 61 | printk(BIOS_WARNING, "Invalid PIRQ%c for external interrupt\n", |
| 62 | extint); |
| 63 | } else { |
| 64 | printk(BIOS_INFO, "Routing external interrupt to PIRQ%c\n", |
| 65 | extint); |
| 66 | val = extint - 'A'; |
| 67 | val |= (1 << 3); /* bit3 enables the external int */ |
Kyösti Mälkki | c043408 | 2019-02-07 16:18:20 +0200 | [diff] [blame] | 68 | pci_update_config8(dev, 0x55, ~0xf, val); |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 69 | |
| 70 | } |
| 71 | } |
| 72 | |
Elyes HAOUAS | 2a5f6cb | 2018-05-25 08:09:53 +0200 | [diff] [blame] | 73 | static void vx900_lpc_dma_setup(struct device *dev) |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 74 | { |
| 75 | /* These are the steps recommended by VIA in order to get DMA running */ |
| 76 | |
| 77 | /* Enable Positive South Module PCI Cycle Decoding */ |
| 78 | /* FIXME: Setting this seems to hang our system */ |
Elyes HAOUAS | e7aeb2f | 2016-10-05 21:02:23 +0200 | [diff] [blame] | 79 | |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 80 | /* Positive decoding for ROM + APIC + On-board IO ports */ |
Kyösti Mälkki | c043408 | 2019-02-07 16:18:20 +0200 | [diff] [blame] | 81 | pci_or_config8(dev, 0x6c, (1 << 2) | (1 << 3) | (1 << 7)); |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 82 | /* Enable DMA channels. BIOS guide recommends DMA channel 2 off */ |
| 83 | pci_write_config8(dev, 0x53, 0xfb); |
| 84 | /* Disable PCI/DMA Memory Cycles Output to PCI Bus */ |
Kyösti Mälkki | c043408 | 2019-02-07 16:18:20 +0200 | [diff] [blame] | 85 | pci_update_config8(dev, 0x5b, ~(1 << 5), 0); |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 86 | /* DMA bandwidth control - Improved bandwidth */ |
| 87 | pci_write_config8(dev, 0x53, 0xff); |
| 88 | /* ISA Positive Decoding control */ |
| 89 | pci_write_config8(dev, 0x6d, 0xdf); |
| 90 | pci_write_config8(dev, 0x6e, 0x98); |
| 91 | pci_write_config8(dev, 0x6f, 0x30); |
| 92 | } |
| 93 | |
| 94 | /** |
| 95 | *\brief VX900: Set up the south module IOAPIC (for the ISA/LPC bus) |
| 96 | * |
| 97 | * Enable the IOAPIC in the south module, and properly set it up. |
| 98 | * \n |
| 99 | * This is the hardware specific initialization for the IOAPIC, and complements |
| 100 | * the setup done by the generic IOAPIC driver. In order for the IOAPIC to work |
| 101 | * properly, it _must_ be declared in devicetree.cb . |
| 102 | * \n |
| 103 | * We are assuming this is called before the drivers/generic/ioapic code, |
| 104 | * which should be the case if devicetree.cb is set up properly. |
| 105 | */ |
Elyes HAOUAS | 2a5f6cb | 2018-05-25 08:09:53 +0200 | [diff] [blame] | 106 | static void vx900_lpc_ioapic_setup(struct device *dev) |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 107 | { |
| 108 | /* Find the IOAPIC, and make sure it's set up correctly in devicetree.cb |
| 109 | * If it's not, then the generic ioapic driver will not set it up |
| 110 | * correctly, and the MP table will not be correctly generated */ |
Elyes HAOUAS | 2a5f6cb | 2018-05-25 08:09:53 +0200 | [diff] [blame] | 111 | struct device *ioapic; |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 112 | for (ioapic = dev->next; ioapic; ioapic = ioapic->next) { |
| 113 | if (ioapic->path.type == DEVICE_PATH_IOAPIC) |
| 114 | break; |
| 115 | } |
| 116 | |
| 117 | /* You did put an IOAPIC in devicetree.cb, didn't you? */ |
| 118 | if (ioapic == 0) { |
| 119 | /* We don't have enough info to set up the IOAPIC */ |
| 120 | printk(BIOS_ERR, "ERROR: South module IOAPIC not found. " |
| 121 | "Check your devicetree.cb\n"); |
| 122 | return; |
| 123 | } |
| 124 | |
| 125 | /* Found an IOAPIC, now we need to make sure it's the right one */ |
| 126 | ioapic_config_t *config = (ioapic_config_t *) ioapic->chip_info; |
| 127 | if (!config->have_isa_interrupts) { |
| 128 | /* Umh, is this the right IOAPIC ? */ |
| 129 | printk(BIOS_ERR, "ERROR: South module IOAPIC not carrying ISA " |
| 130 | "interrupts. Check your devicetree.cb\n"); |
| 131 | printk(BIOS_ERR, "Will not initialize this IOAPIC.\n"); |
| 132 | return; |
| 133 | } |
| 134 | |
| 135 | /* The base address of this IOAPIC _must_ be at 0xfec00000. |
| 136 | * Don't move this value to a #define, as people might think it's |
| 137 | * configurable. It is not. */ |
Kevin Paul Herbert | bde6d30 | 2014-12-24 18:43:20 -0800 | [diff] [blame] | 138 | const void *base = config->base; |
| 139 | if (base != (void *)0xfec00000) { |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 140 | printk(BIOS_ERR, "ERROR: South module IOAPIC base should be at " |
Kevin Paul Herbert | bde6d30 | 2014-12-24 18:43:20 -0800 | [diff] [blame] | 141 | "0xfec00000\n but we found it at %p\n", base); |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 142 | return; |
| 143 | } |
| 144 | |
Stefan Reinauer | 65b72ab | 2015-01-05 12:59:54 -0800 | [diff] [blame] | 145 | printk(BIOS_DEBUG, "VX900 LPC: Setting up the south module IOAPIC.\n"); |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 146 | /* Enable IOAPIC |
| 147 | * So much work for one line of code. Talk about bloat :) |
| 148 | * The 8259 PIC should still work even if the IOAPIC is enabled, so |
| 149 | * there's no crime in enabling the IOAPIC here. */ |
Kyösti Mälkki | c043408 | 2019-02-07 16:18:20 +0200 | [diff] [blame] | 150 | pci_or_config8(dev, 0x58, 1 << 6); |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 151 | } |
| 152 | |
Elyes HAOUAS | 2a5f6cb | 2018-05-25 08:09:53 +0200 | [diff] [blame] | 153 | static void vx900_lpc_interrupt_stuff(struct device *dev) |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 154 | { |
| 155 | /* Enable setting trigger mode through 0x4d0, and 0x4d1 ports |
| 156 | * And enable I/O recovery time */ |
Kyösti Mälkki | c043408 | 2019-02-07 16:18:20 +0200 | [diff] [blame] | 157 | pci_or_config8(dev, 0x40, (1 << 2) | (1 << 6)); |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 158 | /* Set serial IRQ frame width to 6 PCI cycles (recommended by VIA) |
| 159 | * And enable serial IRQ */ |
Kyösti Mälkki | c043408 | 2019-02-07 16:18:20 +0200 | [diff] [blame] | 160 | pci_update_config8(dev, 0x52, ~(3 << 0), (1 << 3) | (1 << 0)); |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 161 | |
| 162 | /* Disable IRQ12 storm FIXME: bad comment */ |
Kyösti Mälkki | c043408 | 2019-02-07 16:18:20 +0200 | [diff] [blame] | 163 | pci_update_config8(dev, 0x51, ~(1 << 2), 0); |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 164 | |
| 165 | pci_write_config8(dev, 0x4c, (1 << 6)); |
| 166 | |
| 167 | /* FIXME: Do we really need this? SeaBIOS/linux runs fine without it. |
| 168 | * Is this something the payload/OS should do, or is it safe for us to |
| 169 | * do it? */ |
| 170 | /* Get the IRQs up and running */ |
| 171 | setup_i8259(); |
| 172 | |
| 173 | vx900_lpc_dma_setup(dev); |
| 174 | |
| 175 | /* The IOAPIC is special, and we treat it separately */ |
| 176 | vx900_lpc_ioapic_setup(dev); |
| 177 | } |
| 178 | |
Elyes HAOUAS | 2a5f6cb | 2018-05-25 08:09:53 +0200 | [diff] [blame] | 179 | static void vx900_lpc_init(struct device *dev) |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 180 | { |
| 181 | vx900_lpc_interrupt_stuff(dev); |
| 182 | vx900_lpc_misc_stuff(dev); |
| 183 | dump_pci_device(dev); |
| 184 | } |
| 185 | |
Elyes HAOUAS | 2a5f6cb | 2018-05-25 08:09:53 +0200 | [diff] [blame] | 186 | static void vx900_lpc_read_resources(struct device *dev) |
Lubomir Rintel | 6dd2f69 | 2017-10-31 09:42:46 +0100 | [diff] [blame] | 187 | { |
| 188 | struct resource *res; |
| 189 | pci_dev_read_resources(dev); |
| 190 | |
| 191 | /* MMIO space */ |
| 192 | res = new_resource(dev, VX900_MMCONFIG_MBAR); |
| 193 | res->size = 0x1000; |
| 194 | res->align = 12; |
| 195 | res->gran = 12; |
| 196 | res->limit = 0xffffffff; |
| 197 | res->flags = IORESOURCE_MEM | IORESOURCE_RESERVE; |
| 198 | |
| 199 | /* SPI controller */ |
| 200 | res = new_resource(dev, IOINDEX_SUBTRACTIVE(0, 0)); |
| 201 | res->size = 0x8; |
| 202 | res->align = 12; |
| 203 | res->gran = 12; |
| 204 | res->limit = 0xffffffff; |
| 205 | res->flags = IORESOURCE_MEM | IORESOURCE_RESERVE; |
| 206 | } |
| 207 | |
Elyes HAOUAS | 2a5f6cb | 2018-05-25 08:09:53 +0200 | [diff] [blame] | 208 | static void vx900_lpc_set_resources(struct device *dev) |
Lubomir Rintel | 6dd2f69 | 2017-10-31 09:42:46 +0100 | [diff] [blame] | 209 | { |
| 210 | struct resource *mmio, *spi; |
Elyes HAOUAS | 88607a4 | 2018-10-05 10:36:45 +0200 | [diff] [blame] | 211 | u32 reg; |
Lubomir Rintel | 6dd2f69 | 2017-10-31 09:42:46 +0100 | [diff] [blame] | 212 | |
| 213 | mmio = find_resource(dev, VX900_MMCONFIG_MBAR); |
| 214 | if (mmio) { |
| 215 | report_resource_stored(dev, mmio, "<mmconfig>"); |
| 216 | mmio->flags |= IORESOURCE_STORED; |
| 217 | reg = pci_read_config32(dev, VX900_MMCONFIG_MBAR); |
| 218 | reg &= 0xff000000; |
| 219 | reg |= mmio->base >> 8; |
| 220 | pci_write_config32(dev, VX900_MMCONFIG_MBAR, reg); |
| 221 | |
| 222 | spi = find_resource(dev, IOINDEX_SUBTRACTIVE(0, 0)); |
| 223 | if (spi) { |
| 224 | report_resource_stored(dev, spi, "<spi>"); |
| 225 | spi->flags |= IORESOURCE_STORED; |
| 226 | /* Set base and the enable bit. */ |
| 227 | ((u32*)(uintptr_t)mmio->base)[0] = (spi->base | 0x01); |
| 228 | } |
| 229 | } |
| 230 | pci_dev_set_resources(dev); |
| 231 | } |
| 232 | |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 233 | static struct device_operations vx900_lpc_ops = { |
Lubomir Rintel | 6dd2f69 | 2017-10-31 09:42:46 +0100 | [diff] [blame] | 234 | .read_resources = vx900_lpc_read_resources, |
| 235 | .set_resources = vx900_lpc_set_resources, |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 236 | .enable_resources = pci_dev_enable_resources, |
| 237 | .init = vx900_lpc_init, |
Kyösti Mälkki | d0e212c | 2015-02-26 20:47:47 +0200 | [diff] [blame] | 238 | .scan_bus = scan_lpc_bus, |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 239 | }; |
| 240 | |
| 241 | static const struct pci_driver lpc_driver __pci_driver = { |
| 242 | .ops = &vx900_lpc_ops, |
| 243 | .vendor = PCI_VENDOR_ID_VIA, |
| 244 | .device = PCI_DEVICE_ID_VIA_VX900_LPC, |
| 245 | }; |
| 246 | |
Martin Roth | 3c35ad9 | 2017-06-24 15:00:56 -0600 | [diff] [blame] | 247 | #if IS_ENABLED(CONFIG_PIRQ_ROUTE) |
Elyes HAOUAS | fd051dc | 2018-07-08 12:39:34 +0200 | [diff] [blame] | 248 | void pirq_assign_irqs(const u8 *pirq) |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 249 | { |
Elyes HAOUAS | 2a5f6cb | 2018-05-25 08:09:53 +0200 | [diff] [blame] | 250 | struct device *lpc; |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 251 | |
| 252 | lpc = dev_find_device(PCI_VENDOR_ID_VIA, |
| 253 | PCI_DEVICE_ID_VIA_VX900_LPC, 0); |
| 254 | |
| 255 | /* Take care of INTA -> INTD */ |
Kyösti Mälkki | c043408 | 2019-02-07 16:18:20 +0200 | [diff] [blame] | 256 | pci_update_config8(lpc, 0x55, (u8)~(0xf << 4), pirq[0] << 4); |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 257 | pci_write_config8(lpc, 0x56, pirq[1] | (pirq[2] << 4)); |
| 258 | pci_write_config8(lpc, 0x57, pirq[3] << 4); |
| 259 | |
| 260 | /* Enable INTE -> INTH to be on separate IRQs */ |
Kyösti Mälkki | c043408 | 2019-02-07 16:18:20 +0200 | [diff] [blame] | 261 | pci_or_config8(lpc, 0x46, 1 << 4); |
Alexandru Gagniuc | 23211b0 | 2013-06-09 16:06:07 -0500 | [diff] [blame] | 262 | /* Now do INTE -> INTH */ |
| 263 | pci_write_config8(lpc, 0x44, pirq[4] | (pirq[5] << 4)); |
| 264 | pci_write_config8(lpc, 0x45, pirq[6] | (pirq[7] << 4)); |
| 265 | } |
| 266 | #endif |