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