/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "bmc.h" /* UART1 config */ #define UART_IO_BASE 0x3f8 #define UART_IO_COUNT 8 #define UART_LPC_IRQ 4 static void palmetto_ext_irq(unsigned int chip_id __unused) { uart_irq(); } static void palmetto_init(void) { /* Setup dummy console nodes */ if (dummy_console_enabled()) dummy_console_add_nodes(); /* Initialize AHB accesses via AST2400 */ ast_io_init(); /* Initialize PNOR/NVRAM */ pnor_init(); } static void palmetto_fixup_dt_uart(struct dt_node *lpc) { /* * The official OF ISA/LPC binding is a bit odd, it prefixes * the unit address for IO with "i". It uses 2 cells, the first * one indicating IO vs. Memory space (along with bits to * represent aliasing). * * We pickup that binding and add to it "2" as a indication * of FW space. */ struct dt_node *uart; char namebuf[32]; /* First check if the UART is already there */ dt_for_each_child(lpc, uart) { if (dt_node_is_compatible(uart, "ns16550")) return; } /* Otherwise, add a node for it */ sprintf(namebuf, "serial@i%x", UART_IO_BASE); uart = dt_new(lpc, namebuf); dt_add_property_cells(uart, "reg", 1, /* IO space */ UART_IO_BASE, UART_IO_COUNT); dt_add_property_strings(uart, "compatible", "ns16550", "pnpPNP,501"); dt_add_property_cells(uart, "clock-frequency", 1843200); dt_add_property_cells(uart, "current-speed", 115200); /* * This is needed by Linux for some obscure reasons, * we'll eventually need to sanitize it but in the meantime * let's make sure it's there */ dt_add_property_strings(uart, "device_type", "serial"); /* * Add interrupt. This simulates coming from HostBoot which * does not know our interrupt numbering scheme. Instead, it * just tells us which chip the interrupt is wired to, it will * be the PSI "host error" interrupt of that chip. For now we * assume the same chip as the LPC bus is on. */ dt_add_property_cells(uart, "ibm,irq-chip-id", dt_get_chip_id(lpc)); } static void palmetto_fixup_dt(void) { struct dt_node *n, *primary_lpc = NULL; /* Find the primary LPC bus */ dt_for_each_compatible(dt_root, n, "ibm,power8-lpc") { if (!primary_lpc || dt_has_node_property(n, "primary", NULL)) primary_lpc = n; if (dt_has_node_property(n, "#address-cells", NULL)) break; } if (!primary_lpc) return; /* Fixup the UART, that might be missing from HB */ palmetto_fixup_dt_uart(primary_lpc); /* Force the dummy console for now */ force_dummy_console(); } static void palmetto_fixup_psi_bar(void) { struct proc_chip *chip = next_chip(NULL); uint64_t psibar; /* Read PSI BAR */ if (xscom_read(chip->id, 0x201090A, &psibar)) { prerror("PLAT: Error reading PSI BAR\n"); return; } /* Already configured, bail out */ if (psibar & 1) return; /* Hard wire ... yuck */ psibar = 0x3fffe80000001; printf("PLAT: Fixing up PSI BAR on chip %d BAR=%llx\n", chip->id, psibar); /* Now write it */ xscom_write(chip->id, 0x201090A, psibar); } static bool palmetto_probe(void) { const char *model; if (!dt_node_is_compatible(dt_root, "ibm,powernv")) return false; /* Temporary ... eventually we'll get that in compatible */ model = dt_prop_get_def(dt_root, "model", NULL); if ((!model || !strstr(model, "palmetto")) && (!dt_node_is_compatible(dt_root, "ibm,palmetto"))) return false; /* Hostboot's device-tree isn't quite right yet */ palmetto_fixup_dt(); /* Hostboot forgets to populate the PSI BAR */ palmetto_fixup_psi_bar(); /* Send external interrupts to me */ psi_set_external_irq_policy(EXTERNAL_IRQ_POLICY_SKIBOOT); /* Configure UART1 on SuperIO */ ast_setup_uart1(UART_IO_BASE, UART_LPC_IRQ); /* Setup UART and use it as console with interrupts */ uart_init(true); return true; } DECLARE_PLATFORM(palmetto) = { .name = "Palmetto", .probe = palmetto_probe, .init = palmetto_init, .external_irq = palmetto_ext_irq, };