diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-07-02 15:36:20 +1000 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-07-02 15:36:20 +1000 |
commit | 1d880992fd8c8457a2d990ac6622cfd58fb1b261 (patch) | |
tree | c4c843b12e96b5612c315db5a23c5da1a900618c /platforms | |
download | skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.zip skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.tar.gz skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.tar.bz2 |
Initial commit of Open Source release
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'platforms')
-rw-r--r-- | platforms/Makefile.inc | 10 | ||||
-rw-r--r-- | platforms/bmc/Makefile.inc | 6 | ||||
-rw-r--r-- | platforms/bmc/bmc.h | 24 | ||||
-rw-r--r-- | platforms/bmc/palmetto.c | 186 | ||||
-rw-r--r-- | platforms/bmc/pnor.c | 87 | ||||
-rw-r--r-- | platforms/ibm-fsp/Makefile.inc | 6 | ||||
-rw-r--r-- | platforms/ibm-fsp/apollo.c | 62 | ||||
-rw-r--r-- | platforms/ibm-fsp/common.c | 196 | ||||
-rw-r--r-- | platforms/ibm-fsp/firenze.c | 247 | ||||
-rw-r--r-- | platforms/ibm-fsp/ibm-fsp.h | 26 | ||||
-rw-r--r-- | platforms/ibm-fsp/lxvpd.c | 298 | ||||
-rw-r--r-- | platforms/ibm-fsp/lxvpd.h | 111 | ||||
-rw-r--r-- | platforms/rhesus/Makefile.inc | 6 | ||||
-rw-r--r-- | platforms/rhesus/rhesus.c | 313 |
14 files changed, 1578 insertions, 0 deletions
diff --git a/platforms/Makefile.inc b/platforms/Makefile.inc new file mode 100644 index 0000000..5061b73 --- /dev/null +++ b/platforms/Makefile.inc @@ -0,0 +1,10 @@ +PLATDIR = platforms + +SUBDIRS += $(PLATDIR) +PLATFORMS = $(PLATDIR)/built-in.o + +include $(SRC)/$(PLATDIR)/ibm-fsp/Makefile.inc +include $(SRC)/$(PLATDIR)/rhesus/Makefile.inc +include $(SRC)/$(PLATDIR)/bmc/Makefile.inc + +$(PLATFORMS): $(IBM_FSP) $(RHESUS) $(BMC) diff --git a/platforms/bmc/Makefile.inc b/platforms/bmc/Makefile.inc new file mode 100644 index 0000000..348e594 --- /dev/null +++ b/platforms/bmc/Makefile.inc @@ -0,0 +1,6 @@ +SUBDIRS += $(PLATDIR)/bmc + +BMC_OBJS = palmetto.o pnor.o +BMC = $(PLATDIR)/bmc/built-in.o +$(BMC): $(BMC_OBJS:%=$(PLATDIR)/bmc/%) + diff --git a/platforms/bmc/bmc.h b/platforms/bmc/bmc.h new file mode 100644 index 0000000..59d20bb --- /dev/null +++ b/platforms/bmc/bmc.h @@ -0,0 +1,24 @@ +/* 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. + */ + + +#ifndef __BMC_H +#define __BMC_H + +extern void bmc_ext_irq(uint32_t chip_id); +extern int pnor_init(void); + +#endif /* __BMC_H */ diff --git a/platforms/bmc/palmetto.c b/platforms/bmc/palmetto.c new file mode 100644 index 0000000..42905de --- /dev/null +++ b/platforms/bmc/palmetto.c @@ -0,0 +1,186 @@ +/* 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 <skiboot.h> +#include <device.h> +#include <console.h> +#include <psi.h> +#include <chip.h> +#include <xscom.h> +#include <ast.h> + +#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, +}; + diff --git a/platforms/bmc/pnor.c b/platforms/bmc/pnor.c new file mode 100644 index 0000000..878615d --- /dev/null +++ b/platforms/bmc/pnor.c @@ -0,0 +1,87 @@ +/* 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 <skiboot.h> +#include <device.h> +#include <console.h> +#include <opal.h> +#include <libflash/libflash.h> +#include <libflash/libffs.h> + +#include "bmc.h" +#include "ast.h" + +static struct spi_flash_ctrl *pnor_ctrl; +static struct flash_chip *pnor_chip; +static struct ffs_handle *pnor_ffs; + +int pnor_init(void) +{ + uint32_t nv_part, nv_start, nv_size; + int rc; + + /* Open controller, flash and ffs */ + rc = ast_sf_open(AST_SF_TYPE_PNOR, &pnor_ctrl); + if (rc) { + prerror("PLAT: Failed to open PNOR flash controller\n"); + goto fail; + } + rc = flash_init(pnor_ctrl, &pnor_chip); + if (rc) { + prerror("PLAT: Failed to open init PNOR driver\n"); + goto fail; + } + rc = ffs_open_flash(pnor_chip, 0, 0, &pnor_ffs); + if (rc) { + prerror("PLAT: Failed to parse FFS partition map\n"); + goto fail; + } + + /* + * Grab NVRAM and initialize the flash_nvram module + * + * Note: Ignore actual size for now ... some images have + * it setup incorrectly. + */ + rc = ffs_lookup_part(pnor_ffs, "NVRAM", &nv_part); + if (rc) { + prerror("PLAT: No NVRAM partition in PNOR\n"); + return OPAL_HARDWARE; + } + rc = ffs_part_info(pnor_ffs, nv_part, NULL, + &nv_start, &nv_size, NULL); + if (rc) { + prerror("PLAT: Failed to get NVRAM partition info\n"); + return OPAL_HARDWARE; + } + flash_nvram_init(pnor_chip, nv_start, nv_size); + + return 0; + fail: + if (pnor_ffs) + ffs_close(pnor_ffs); + pnor_ffs = NULL; + if (pnor_chip) + flash_exit(pnor_chip); + pnor_chip = NULL; + if (pnor_ctrl) + ast_sf_close(pnor_ctrl); + pnor_ctrl = NULL; + + return rc; +} + diff --git a/platforms/ibm-fsp/Makefile.inc b/platforms/ibm-fsp/Makefile.inc new file mode 100644 index 0000000..a885cbb --- /dev/null +++ b/platforms/ibm-fsp/Makefile.inc @@ -0,0 +1,6 @@ +SUBDIRS += $(PLATDIR)/ibm-fsp + +IBM_FSP_OBJS = common.o lxvpd.o apollo.o firenze.o +IBM_FSP = $(PLATDIR)/ibm-fsp/built-in.o +$(IBM_FSP): $(IBM_FSP_OBJS:%=$(PLATDIR)/ibm-fsp/%) + diff --git a/platforms/ibm-fsp/apollo.c b/platforms/ibm-fsp/apollo.c new file mode 100644 index 0000000..0d8fb4b --- /dev/null +++ b/platforms/ibm-fsp/apollo.c @@ -0,0 +1,62 @@ +/* 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 <skiboot.h> +#include <device.h> +#include <fsp.h> +#include <pci.h> + +#include "ibm-fsp.h" +#include "lxvpd.h" + +static bool apollo_probe(void) +{ + return dt_node_is_compatible(dt_root, "ibm,apollo"); +} + +static void apollo_setup_phb(struct phb *phb, unsigned int index) +{ + struct dt_node *ioc_node; + + /* Grab the device-tree node of the IOC */ + ioc_node = phb->dt_node->parent; + if (!ioc_node) + return; + + /* + * Process the pcie slot entries from the lx vpd lid + * + * FIXME: We currently assume chip 1 always, this will have to be + * fixed once we understand the right way to get the BRxy/BRxy "x" + * "x" value. (this actually seems to work...) + */ + lxvpd_process_slot_entries(phb, ioc_node, 1, index); +} + +DECLARE_PLATFORM(apollo) = { + .name = "Apollo", + .probe = apollo_probe, + .init = ibm_fsp_init, + .cec_power_down = ibm_fsp_cec_power_down, + .cec_reboot = ibm_fsp_cec_reboot, + .pci_setup_phb = apollo_setup_phb, + .pci_get_slot_info = lxvpd_get_slot_info, + .nvram_info = fsp_nvram_info, + .nvram_start_read = fsp_nvram_start_read, + .nvram_write = fsp_nvram_write, +}; + diff --git a/platforms/ibm-fsp/common.c b/platforms/ibm-fsp/common.c new file mode 100644 index 0000000..6d5ee17 --- /dev/null +++ b/platforms/ibm-fsp/common.c @@ -0,0 +1,196 @@ +/* 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 <skiboot.h> +#include <fsp.h> +#include <fsp-sysparam.h> +#include <opal.h> +#include <console.h> + +#include "ibm-fsp.h" + +static void map_debug_areas(void) +{ + uint64_t t, i; + + /* Our memcons is in a section of its own and already + * aligned to 4K. The buffers are mapped as a whole + */ + fsp_tce_map(PSI_DMA_MEMCONS, &memcons, 0x1000); + fsp_tce_map(PSI_DMA_LOG_BUF, (void*)INMEM_CON_START, INMEM_CON_LEN); + + debug_descriptor.memcons_tce = PSI_DMA_MEMCONS; + t = memcons.obuf_phys - INMEM_CON_START + PSI_DMA_LOG_BUF; + debug_descriptor.memcons_obuf_tce = t; + t = memcons.ibuf_phys - INMEM_CON_START + PSI_DMA_LOG_BUF; + debug_descriptor.memcons_ibuf_tce = t; + + /* We only have space in the TCE table for the trace + * areas on P8 + */ + if (proc_gen != proc_gen_p8) + return; + + t = PSI_DMA_TRACE_BASE; + for (i = 0; i < debug_descriptor.num_traces; i++) { + /* + * Trace buffers are misaligned by 0x10 due to the lock + * in the trace structure, and their size is also not + * completely aligned. (They are allocated so that with + * the lock included, they do cover entire multiple of + * a 4K page however). + * + * This means we have to map the lock into the TCEs and + * align everything. Not a huge deal but needs to be + * taken into account. + * + * Note: Maybe we should map them read-only... + */ + uint64_t tstart, tend, toff, tsize; + + tstart = ALIGN_DOWN(debug_descriptor.trace_phys[i], 0x1000); + tend = ALIGN_UP(debug_descriptor.trace_phys[i] + + debug_descriptor.trace_size[i], 0x1000); + toff = debug_descriptor.trace_phys[i] - tstart; + tsize = tend - tstart; + + fsp_tce_map(t, (void *)tstart, tsize); + debug_descriptor.trace_tce[i] = t + toff; + t += tsize; + } +} + + +void ibm_fsp_init(void) +{ + /* Early initializations of the FSP interface */ + fsp_init(); + map_debug_areas(); + fsp_sysparam_init(); + + /* Get ready to receive E0 class messages. We need to respond + * to some of these for the init sequence to make forward progress + */ + fsp_console_preinit(); + + /* Get ready to receive OCC related messages */ + occ_fsp_init(); + + /* Get ready to receive Memory [Un]corretable Error messages. */ + fsp_memory_err_init(); + + /* Initialize elog access */ + fsp_elog_read_init(); + fsp_elog_write_init(); + + /* Initiate dump service */ + fsp_dump_init(); + + /* Start FSP/HV state controller & perform OPL */ + fsp_opl(); + + /* Send MDST table notification to FSP */ + op_display(OP_LOG, OP_MOD_INIT, 0x0000); + fsp_mdst_table_init(); + + /* Initialize the panel */ + op_display(OP_LOG, OP_MOD_INIT, 0x0001); + fsp_oppanel_init(); + + /* Start the surveillance process */ + op_display(OP_LOG, OP_MOD_INIT, 0x0002); + fsp_init_surveillance(); + + /* Initialize sensor access */ + op_display(OP_LOG, OP_MOD_INIT, 0x0003); + fsp_init_sensor(); + + /* LED */ + op_display(OP_LOG, OP_MOD_INIT, 0x0004); + fsp_led_init(); + + /* Monitor for DIAG events */ + op_display(OP_LOG, OP_MOD_INIT, 0x0005); + fsp_init_diag(); + + /* Finish initializing the console */ + op_display(OP_LOG, OP_MOD_INIT, 0x0006); + fsp_console_init(); + + /* Read our initial RTC value */ + op_display(OP_LOG, OP_MOD_INIT, 0x0008); + fsp_rtc_init(); + + /* Initialize code update access */ + op_display(OP_LOG, OP_MOD_INIT, 0x0009); + fsp_code_update_init(); + + /* Setup console */ + if (fsp_present()) + fsp_console_add_nodes(); + else if (dummy_console_enabled()) + dummy_console_add_nodes(); +} + +int64_t ibm_fsp_cec_reboot(void) +{ + uint32_t cmd = FSP_CMD_REBOOT; + + if (!fsp_present()) + return OPAL_UNSUPPORTED; + + /* Flash new firmware */ + if (fsp_flash_term_hook && + fsp_flash_term_hook() == OPAL_SUCCESS) + cmd = FSP_CMD_DEEP_REBOOT; + + printf("FSP: Sending 0x%02x reboot command to FSP...\n", cmd); + + /* If that failed, talk to the FSP */ + if (fsp_sync_msg(fsp_mkmsg(cmd, 0), true)) + return OPAL_INTERNAL_ERROR; + + return OPAL_SUCCESS; +} + +int64_t ibm_fsp_cec_power_down(uint64_t request) +{ + /* Request is: + * + * 0 = normal + * 1 = immediate + * (we do not allow 2 for "pci cfg reset" just yet) + */ + + if (request !=0 && request != 1) + return OPAL_PARAMETER; + + if (!fsp_present()) + return OPAL_UNSUPPORTED; + + /* Flash new firmware */ + if (fsp_flash_term_hook) + fsp_flash_term_hook(); + + printf("FSP: Sending shutdown command to FSP...\n"); + + if (fsp_sync_msg(fsp_mkmsg(FSP_CMD_POWERDOWN_NORM, 1, request), true)) + return OPAL_INTERNAL_ERROR; + + return OPAL_SUCCESS; +} + diff --git a/platforms/ibm-fsp/firenze.c b/platforms/ibm-fsp/firenze.c new file mode 100644 index 0000000..ae72e21 --- /dev/null +++ b/platforms/ibm-fsp/firenze.c @@ -0,0 +1,247 @@ +/* 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 <skiboot.h> +#include <device.h> +#include <fsp.h> +#include <pci.h> +#include <pci-cfg.h> +#include <chip.h> + +#include "ibm-fsp.h" +#include "lxvpd.h" + +/* Enable that to dump the PCIe inventory table before sending to FSP */ +#define DEBUG_INVENTORY + +/* Structure used to send PCIe card info to FSP */ +struct fsp_pcie_entry { + uint32_t hw_proc_id; + uint16_t slot_idx; + uint16_t reserved; + uint16_t vendor_id; + uint16_t device_id; + uint16_t subsys_vendor_id; + uint16_t subsys_device_id; +}; + +struct fsp_pcie_inventory { + uint32_t version; /* currently 1 */ + uint32_t num_entries; + uint32_t entry_size; + uint32_t entry_offset; + struct fsp_pcie_entry entries[]; +}; + +static struct fsp_pcie_inventory *fsp_pcie_inv; +static unsigned int fsp_pcie_inv_alloc_count; +#define FSP_PCIE_INV_ALLOC_CHUNK 4 + +static bool firenze_probe(void) +{ + return dt_node_is_compatible(dt_root, "ibm,firenze"); +} + +static void firenze_send_pci_inventory(void) +{ + uint64_t base, abase, end, aend, offset; + int64_t rc; + + if (!fsp_pcie_inv) + return; + + printf("PLAT: Sending PCI inventory to FSP, table has %d entries\n", + fsp_pcie_inv->num_entries); + +#ifdef DEBUG_INVENTORY + { + unsigned int i; + + printf("HWP SLT VDID DVID SVID SDID\n"); + printf("---------------------------\n"); + for (i = 0; i < fsp_pcie_inv->num_entries; i++) { + struct fsp_pcie_entry *e = &fsp_pcie_inv->entries[i]; + + printf("%03d %03d %04x %04x %04x %04x\n", + e->hw_proc_id, e->slot_idx, + e->vendor_id, e->device_id, + e->subsys_vendor_id, e->subsys_device_id); + } + } +#endif + + /* + * Get the location of the table in a form we can send + * to the FSP + */ + base = (uint64_t)fsp_pcie_inv; + end = base + sizeof(struct fsp_pcie_inventory) + + fsp_pcie_inv->num_entries * fsp_pcie_inv->entry_size; + abase = base & ~0xffful; + aend = (end + 0xffful) & ~0xffful; + offset = PSI_DMA_PCIE_INVENTORY + (base & 0xfff); + + /* We can only accomodate so many entries in the PSI map */ + if ((aend - abase) > PSI_DMA_PCIE_INVENTORY_SIZE) { + prerror("PLAT: PCIe inventory too large (%lld bytes)\n", + aend - abase); + goto bail; + } + + /* Map this in the TCEs */ + fsp_tce_map(PSI_DMA_PCIE_INVENTORY, (void *)abase, aend - abase); + + /* Send FSP message */ + rc = fsp_sync_msg(fsp_mkmsg(FSP_CMD_PCI_POWER_CONF, 3, + hi32(offset), lo32(offset), + end - base), true); + if (rc) + prerror("PLAT: FSP error %lld sending inventory\n", rc); + + /* Unmap */ + fsp_tce_unmap(PSI_DMA_PCIE_INVENTORY, aend - abase); + bail: + /* + * We free the inventory. We'll have to redo that on hotplug + * when we support it but that isn't the case yet + */ + free(fsp_pcie_inv); + fsp_pcie_inv = NULL; +} + +static void firenze_add_pcidev_to_fsp_inventory(struct phb *phb, + struct pci_device *pd) +{ + struct fsp_pcie_entry *entry; + struct proc_chip *chip; + + /* Check if we need to do some (Re)allocation */ + if (!fsp_pcie_inv || + fsp_pcie_inv->num_entries == fsp_pcie_inv_alloc_count) { + unsigned int new_count; + size_t new_size; + bool need_init = !fsp_pcie_inv; + + /* (Re)allocate the block to the new size */ + new_count = fsp_pcie_inv_alloc_count + FSP_PCIE_INV_ALLOC_CHUNK; + new_size = sizeof(struct fsp_pcie_inventory); + new_size += sizeof(struct fsp_pcie_entry) * new_count; + fsp_pcie_inv = realloc(fsp_pcie_inv, new_size); + fsp_pcie_inv_alloc_count = new_count; + + /* Initialize the header for a new inventory */ + if (need_init) { + fsp_pcie_inv->version = 1; + fsp_pcie_inv->num_entries = 0; + fsp_pcie_inv->entry_size = + sizeof(struct fsp_pcie_entry); + fsp_pcie_inv->entry_offset = + offsetof(struct fsp_pcie_inventory, entries); + } + } + + /* Add entry */ + entry = &fsp_pcie_inv->entries[fsp_pcie_inv->num_entries++]; + chip = get_chip(dt_get_chip_id(phb->dt_node)); + if (!chip) { + prerror("PLAT: Failed to get chip for PHB !\n"); + return; + } + entry->hw_proc_id = chip->pcid; + entry->slot_idx = pd->parent->slot_info->slot_index; + entry->reserved = 0; + pci_cfg_read16(phb, pd->bdfn, PCI_CFG_VENDOR_ID, &entry->vendor_id); + pci_cfg_read16(phb, pd->bdfn, PCI_CFG_DEVICE_ID, &entry->device_id); + if (pd->is_bridge) { + int64_t ssvc = pci_find_cap(phb, pd->bdfn, + PCI_CFG_CAP_ID_SUBSYS_VID); + if (ssvc < 0) { + entry->subsys_vendor_id = 0xffff; + entry->subsys_device_id = 0xffff; + } else { + pci_cfg_read16(phb, pd->bdfn, + ssvc + PCICAP_SUBSYS_VID_VENDOR, + &entry->subsys_vendor_id); + pci_cfg_read16(phb, pd->bdfn, + ssvc + PCICAP_SUBSYS_VID_DEVICE, + &entry->subsys_device_id); + } + } else { + pci_cfg_read16(phb, pd->bdfn, PCI_CFG_SUBSYS_VENDOR_ID, + &entry->subsys_vendor_id); + pci_cfg_read16(phb, pd->bdfn, PCI_CFG_SUBSYS_ID, + &entry->subsys_device_id); + } +} + +static void firenze_get_slot_info(struct phb *phb, struct pci_device * pd) +{ + /* Call the main LXVPD function first */ + lxvpd_get_slot_info(phb, pd); + + /* + * Do we need to add that to the FSP inventory for power management ? + * + * For now, we only add devices that: + * + * - Are function 0 + * - Are not an RC or a downstream bridge + * - Have a direct parent that has a slot entry + * - Slot entry says pluggable + * - Aren't an upstream switch that has slot info + */ + if (!pd || !pd->parent) + return; + if (pd->bdfn & 7) + return; + if (pd->dev_type == PCIE_TYPE_ROOT_PORT || + pd->dev_type == PCIE_TYPE_SWITCH_DNPORT) + return; + if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT && + pd->slot_info) + return; + if (!pd->parent->slot_info) + return; + if (!pd->parent->slot_info->pluggable) + return; + firenze_add_pcidev_to_fsp_inventory(phb, pd); +} + +static void firenze_setup_phb(struct phb *phb, unsigned int index) +{ + uint32_t hub_id; + + /* Grab Hub ID used to parse VPDs */ + hub_id = dt_prop_get_u32_def(phb->dt_node, "ibm,hub-id", 0); + + /* Process the pcie slot entries from the lx vpd lid */ + lxvpd_process_slot_entries(phb, dt_root, hub_id, index); +} + +DECLARE_PLATFORM(firenze) = { + .name = "Firenze", + .probe = firenze_probe, + .init = ibm_fsp_init, + .cec_power_down = ibm_fsp_cec_power_down, + .cec_reboot = ibm_fsp_cec_reboot, + .pci_setup_phb = firenze_setup_phb, + .pci_get_slot_info = firenze_get_slot_info, + .pci_probe_complete = firenze_send_pci_inventory, + .nvram_info = fsp_nvram_info, + .nvram_start_read = fsp_nvram_start_read, + .nvram_write = fsp_nvram_write, +} ; diff --git a/platforms/ibm-fsp/ibm-fsp.h b/platforms/ibm-fsp/ibm-fsp.h new file mode 100644 index 0000000..f446d18 --- /dev/null +++ b/platforms/ibm-fsp/ibm-fsp.h @@ -0,0 +1,26 @@ +/* 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. + */ + + +#ifndef __IBM_FSP_COMMON_H +#define __IBM_FSP_COMMON_H + +extern void ibm_fsp_init(void); + +extern int64_t ibm_fsp_cec_power_down(uint64_t request); +extern int64_t ibm_fsp_cec_reboot(void); + +#endif /* __IBM_FSP_COMMON_H */ diff --git a/platforms/ibm-fsp/lxvpd.c b/platforms/ibm-fsp/lxvpd.c new file mode 100644 index 0000000..bcf8118 --- /dev/null +++ b/platforms/ibm-fsp/lxvpd.c @@ -0,0 +1,298 @@ +/* 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. + */ +/* + * LXVPD support + * + */ + + +#include <skiboot.h> +#include <device.h> +#include <vpd.h> +#include <pci.h> +#include <pci-cfg.h> + +#include "lxvpd.h" + +#define DBG(fmt...) do { } while(0) +//#define DBG(fmt...) printf(fmt) + +/* + * XXX TODO: Add 1006 maps to add function loc codes and loc code maps + * (ie. -Tn part of the location code) + */ +struct lxvpd_slot_info_data { + uint8_t num_slots; + struct pci_slot_info info[]; +}; + +static bool lxvpd_supported_slot(struct phb *phb, struct pci_device *pd) +{ + /* PCI/PCI-X we only support top level PHB with NULL "pd" */ + if (phb->phb_type < phb_type_pcie_v1) + return pd == NULL; + + /* Now we have PCI Express, we should never have a NULL "pd" */ + if (!pd) + return false; + + /* We support the root complex at the top level */ + if (pd->dev_type == PCIE_TYPE_ROOT_PORT && !pd->parent) + return true; + + /* We support an upstream switch port below the root complex */ + if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT && + pd->parent && pd->parent->dev_type == PCIE_TYPE_ROOT_PORT && + !pd->parent->parent) + return true; + + /* We support a downstream switch port below an upstream port + * below the root complex + */ + if (pd->dev_type == PCIE_TYPE_SWITCH_DNPORT && + pd->parent && pd->parent->dev_type == PCIE_TYPE_SWITCH_UPPORT && + pd->parent->parent && + pd->parent->parent->dev_type == PCIE_TYPE_ROOT_PORT && + !pd->parent->parent->parent) + return true; + + /* Anything else, bail */ + return false; +} + +void lxvpd_get_slot_info(struct phb *phb, struct pci_device * pd) +{ + struct lxvpd_slot_info_data *sdata = phb->platform_data; + bool is_phb = (pd && pd->parent) ? false : true; + bool entry_found = false; + uint8_t idx; + + /* Check if we have slot info */ + if (!sdata) + return; + + DBG("LXVPD: Get Slot Info PHB%d pd=%x\n", phb->opal_id, + pd ? pd->bdfn : 0); + + /* + * This code only handles PHBs and PCIe switches at the top level. + * + * We do not handle any other switch nor any other type of PCI/PCI-X + * bridge. + */ + if (!lxvpd_supported_slot(phb, pd)) { + DBG("LXVPD: Unsupported slot\n"); + return; + } + + /* Iterate the slot map */ + for (idx = 0; idx <= sdata->num_slots; idx++) { + struct pci_slot_info *info = &sdata->info[idx]; + uint8_t pd_dev = (pd->bdfn >> 3) & 0x1f; + + /* Match PHB with switch_id == 0 */ + if (is_phb && info->switch_id == 0) { + entry_found = true; + break; + } + + /* Match switch port with switch_id != 0 */ + if (!is_phb && info->switch_id !=0 && info->dev_id == pd_dev) { + entry_found = true; + break; + } + } + + if (entry_found) { + pd->slot_info = &sdata->info[idx]; + DBG("PCI: PCIE Slot Info: \n"); + DBG(" Label %s\n", pd->slot_info->label); + DBG(" Pluggable 0x%x\n", pd->slot_info->pluggable?1:0); + DBG(" Power Ctl 0x%x\n", pd->slot_info->power_ctl?1:0); + DBG(" Wired Lanes 0x%x\n", pd->slot_info->wired_lanes); + DBG(" Bus Clock 0x%x\n", pd->slot_info->bus_clock); + DBG(" Connector 0x%x\n", pd->slot_info->connector_type); + DBG(" Slot Index %d\n", pd->slot_info->slot_index); + } else + DBG("PCI: PCIE Slot Info Not Found\n"); +} + +static struct pci_slot_info *lxvpd_alloc_slot_info(struct phb *phb, int count) +{ + struct lxvpd_slot_info_data *data; + + data = zalloc(sizeof(struct lxvpd_slot_info_data) * + count * sizeof(struct pci_slot_info)); + assert(data); + data->num_slots = count; + phb->platform_data = data; + + return data->info; +} + +static void lxvpd_parse_1004_map(struct phb *phb, const uint8_t *sm, uint8_t sz) +{ + const struct pci_slot_entry_1004 *entry = NULL; + struct pci_slot_info *slot_info, *info; + uint8_t num_slots, slot, idx; + + num_slots = (sz / sizeof(struct pci_slot_entry_1004)); + slot_info = lxvpd_alloc_slot_info(phb, num_slots); + + /* Iterate thru the entries in the keyword */ + entry = (const struct pci_slot_entry_1004 *)sm; + for (slot = 0; slot < num_slots; slot++) { + info = &slot_info[slot]; + + /* Put slot info into pci device structure */ + info->switch_id = entry->pba >> 4; + info->vswitch_id = entry->pba &0xf; + info->dev_id = entry->sba; + for (idx = 0; idx < 3; idx++) + info->label[idx] = entry->label[idx]; + info->label[3] = 0; + info->pluggable = ((entry->p0.byte & 0x20) == 0); + info->power_ctl = ((entry->p0.power_ctl & 0x40) == 1); + switch(entry->p1.wired_lanes) { + case 1: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIX_32; break; + case 2: /* fall through */ + case 3: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIX_64; break; + case 4: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X1; break; + case 5: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X4; break; + case 6: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X8; break; + case 7: info->wired_lanes = PCI_SLOT_WIRED_LANES_PCIE_X16; break; + default: + info->wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN; + } + info->wired_lanes = (entry->p1.wired_lanes - 3); + info->bus_clock = (entry->p2.bus_clock - 4); + info->connector_type = (entry->p2.connector_type - 5); + if (entry->p3.byte < 0xC0) + info->card_desc = ((entry->p3.byte >> 6) - 4) ; + else + info->card_desc = (entry->p3.byte >> 6); + info->card_mech = ((entry->p3.byte >> 4) & 0x3); + info->pwr_led_ctl = ((entry->p3.byte & 0xF) >> 2); + info->attn_led_ctl = (entry->p3.byte & 0x3); + info->slot_index = entry->slot_index; + entry++; + } +} + +static void lxvpd_parse_1005_map(struct phb *phb, const uint8_t *sm, uint8_t sz) +{ + const struct pci_slot_entry_1005 *entry = NULL; + struct pci_slot_info *slot_info, *info; + uint8_t num_slots, slot, idx; + + num_slots = (sz / sizeof(struct pci_slot_entry_1005)); + slot_info = lxvpd_alloc_slot_info(phb, num_slots); + + /* Iterate thru the entries in the keyword */ + entry = (const struct pci_slot_entry_1005 *)sm; + for (slot = 0; slot < num_slots; slot++) { + info = &slot_info[slot]; + + /* Put slot info into pci device structure */ + info->switch_id = entry->pba >> 4; + info->vswitch_id = entry->pba &0xf; + info->dev_id = entry->switch_device_id; + for (idx = 0; idx < 8; idx++) + info->label[idx] = entry->label[idx]; + info->label[8] = 0; + info->pluggable = (entry->p0.pluggable == 0); + info->power_ctl = entry->p0.power_ctl; + info->wired_lanes = entry->p1.wired_lanes; + if (info->wired_lanes > PCI_SLOT_WIRED_LANES_PCIE_X32) + info->wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN; + info->bus_clock = entry->p2.bus_clock; + info->connector_type = entry->p2.connector_type; + info->card_desc = (entry->p3.byte >> 6); + info->card_mech = ((entry->p3.byte >> 4) & 0x3); + info->pwr_led_ctl = ((entry->p3.byte & 0xF) >> 2); + info->attn_led_ctl = (entry->p3.byte & 0x3); + info->slot_index = entry->slot_index; + entry++; + } +} + +void lxvpd_process_slot_entries(struct phb *phb, + struct dt_node *node, + uint8_t chip_id, + uint8_t index) +{ + const void *lxvpd; + const uint8_t *pr_rec, *pr_end, *sm; + size_t lxvpd_size, pr_size; + const uint16_t *mf = NULL; + char record[5] = "PR00"; + uint8_t mf_sz, sm_sz; + bool found = false; + + record[2] += chip_id; + record[3] += index; + record[4] = 0; + + /* Get LX VPD pointer */ + lxvpd = dt_prop_get_def_size(node, "ibm,io-vpd", NULL, &lxvpd_size); + if (!lxvpd) { + printf("LXVPD: LX VPD not found for %s in %p\n", + record, phb->dt_node); + return; + } + + pr_rec = vpd_find_record(lxvpd, lxvpd_size, record, &pr_size); + if (!pr_rec) { + printf("LXVPD: %s record not found in LX VPD in %p\n", + record, phb->dt_node); + return; + } + pr_end = pr_rec + pr_size; + + DBG("LXVPD: %s record for PHB%d is %ld bytes\n", + record, phb->opal_id, pr_size); + + /* As long as there's still something in the PRxy record... */ + while(pr_rec < pr_end) { + pr_size = pr_end - pr_rec; + + /* Find the next MF keyword */ + mf = vpd_find_keyword(pr_rec, pr_size, "MF", &mf_sz); + /* And the corresponding SM */ + sm = vpd_find_keyword(pr_rec, pr_size, "SM", &sm_sz); + if (!mf || !sm) { + if (!found) + printf("LXVPD: Slot Map keyword %s not found\n", + record); + return; + } + DBG("LXVPD: Found 0x%04x map...\n", *mf); + + switch (*mf) { + case 0x1004: + lxvpd_parse_1004_map(phb, sm + 1, sm_sz - 1); + found = true; + break; + case 0x1005: + lxvpd_parse_1005_map(phb, sm + 1, sm_sz - 1); + found = true; + break; + /* Add support for 0x1006 maps ... */ + } + pr_rec = sm + sm_sz; + } +} + diff --git a/platforms/ibm-fsp/lxvpd.h b/platforms/ibm-fsp/lxvpd.h new file mode 100644 index 0000000..ce70502 --- /dev/null +++ b/platforms/ibm-fsp/lxvpd.h @@ -0,0 +1,111 @@ +/* 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. + */ + +#ifndef __LXVPD_H +#define __LXVPD_H + +/* P8 PCI Slot Entry Definitions -- 1005 */ + +struct slot_p0 { + union { + uint8_t byte; + struct { + uint8_t pluggable:1; + uint8_t pluggable_location:3; + uint8_t power_ctl:1; + uint8_t rsvd_5:1; + uint8_t upstream_port:1; + uint8_t alt_load_source:1; + }; + }; +}; + +struct slot_p1 { + uint8_t rsvd_0:1; + uint8_t wired_lanes:3; + uint8_t rsvd_4:4; +}; + +struct slot_p2 { + uint8_t rsvd_0:1; + uint8_t bus_clock:3; + uint8_t connector_type:4; +}; + +struct slot_p3 { + union { + uint8_t byte; + struct { + uint8_t height:1; + uint8_t length:1; + uint8_t left_mech:1; + uint8_t right_mech:1; + uint8_t pow_led_kvm:1; + uint8_t pow_led_fsp:1; + uint8_t attn_led_kvm:1; + uint8_t attn_led_fsp:1; + }; + }; +}; + +struct pci_slot_entry_1004 { + uint8_t pba; + uint8_t sba; + uint8_t phb_or_slot_type; + char label[3]; + uint16_t bis; + struct slot_p0 p0; + struct slot_p1 p1; + struct slot_p2 p2; + struct slot_p3 p3; + uint8_t left_pitch; + uint8_t right_pitch; + uint8_t slot_index; + uint8_t max_slot_power; +}; + +struct pci_slot_entry_1005 { + union { + uint8_t pba; + struct { + uint8_t switch_id:4; + uint8_t vswitch_id:4; + }; + }; + uint8_t switch_device_id; + uint8_t slot_type:4; + uint8_t phb_id:4; + char label[8]; + uint8_t rsvd_11[4]; + struct slot_p0 p0; + struct slot_p1 p1; + struct slot_p2 p2; + struct slot_p3 p3; + uint8_t left_pitch; + uint8_t right_pitch; + uint8_t slot_index; + uint8_t rsvd_22[2]; +}; + +struct phb; + +extern void lxvpd_process_slot_entries(struct phb *phb, + struct dt_node *node, + uint8_t chip_id, uint8_t index); + +extern void lxvpd_get_slot_info(struct phb *phb, struct pci_device * pd); + +#endif /* __LXVPD_H */ diff --git a/platforms/rhesus/Makefile.inc b/platforms/rhesus/Makefile.inc new file mode 100644 index 0000000..5899ca2 --- /dev/null +++ b/platforms/rhesus/Makefile.inc @@ -0,0 +1,6 @@ +SUBDIRS += $(PLATDIR)/rhesus + +RHESUS_OBJS = rhesus.o +RHESUS = $(PLATDIR)/rhesus/built-in.o +$(RHESUS): $(RHESUS_OBJS:%=$(PLATDIR)/rhesus/%) + diff --git a/platforms/rhesus/rhesus.c b/platforms/rhesus/rhesus.c new file mode 100644 index 0000000..41b1d5a --- /dev/null +++ b/platforms/rhesus/rhesus.c @@ -0,0 +1,313 @@ +/* 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 <skiboot.h> +#include <device.h> +#include <lpc.h> +#include <console.h> +#include <opal.h> +#include <libflash/libflash.h> +#include <libflash/libffs.h> +#include <sfc-ctrl.h> +#include <ec/config.h> +#include <ec/gpio.h> + +/* + * EC GPIO mapping + */ +#define RHESUS_RST_UCD90160_N EC_GPIO_PORT_J, 3 +#define RHESUS_FM_PWR_CYCLE_N EC_GPIO_PORT_K, 2 +#define RHESUS_EN_PWR_ON_SEQ EC_GPIO_PORT_R, 1 +#define RHESUS_BOARD_REVISION0 EC_GPIO_PORT_F, 3 +#define RHESUS_BOARD_REVISION1 EC_GPIO_PORT_F, 2 +#define RHESUS_BOARD_REVISION2 EC_GPIO_PORT_E, 5 +#define RHESUS_BOARD_REVISION3 EC_GPIO_PORT_E, 4 +#define RHESUS_BOARD_REVISION4 EC_GPIO_PORT_E, 1 + +static struct spi_flash_ctrl *pnor_ctrl; +static struct flash_chip *pnor_chip; +static struct ffs_handle *pnor_ffs; + + +/* + * IO accessors for the EC driver + */ +void ec_outb(uint16_t addr, uint8_t data) +{ + lpc_outb(data, addr); +} + +uint8_t ec_inb(uint16_t addr) +{ + return lpc_inb(addr); +} + +static int rhesus_board_revision(void) +{ + int revision = 0, ret = 0, i = 0; + + static const struct { + EcGpioPort port; + uint8_t pin; + } revision_gpios[] = { + { RHESUS_BOARD_REVISION0 }, + { RHESUS_BOARD_REVISION1 }, + { RHESUS_BOARD_REVISION2 }, + { RHESUS_BOARD_REVISION3 }, + { RHESUS_BOARD_REVISION4 }, + }; + for (i = 0; i < sizeof(revision_gpios) / sizeof(revision_gpios[0]); ++i) + { + ret = ec_gpio_read(revision_gpios[i].port, revision_gpios[i].pin); + if (ret < 0) + return ret; + revision <<= 1; revision |= ret; + } + + return revision; +} + +static int64_t rhesus_reboot(void) +{ + // TODO(rlippert): This should use EC_SYS_RST_N, but there is nothing to + // deassert that at the moment. + int ret = 0; + ret = ec_gpio_set(RHESUS_FM_PWR_CYCLE_N, 0); + if (ret < 0) { + return ret; + } + + ret = ec_gpio_setup(RHESUS_FM_PWR_CYCLE_N, + EC_GPIO_OUTPUT, + EC_GPIO_PULLUP_DISABLE); + if (ret < 0) { + return ret; + } + + return 0; +} + +static int64_t rhesus_power_down(uint64_t request __unused) +{ + int ret = 0; + ret = ec_gpio_set(RHESUS_EN_PWR_ON_SEQ, 0); + if (ret < 0) { + return ret; + } + + ret = ec_gpio_setup(RHESUS_EN_PWR_ON_SEQ, + EC_GPIO_OUTPUT, + EC_GPIO_PULLUP_DISABLE); + if (ret < 0) { + return ret; + } + + return 0; +} + +static int rhesus_pnor_init(void) +{ + uint32_t nv_part, nv_start, nv_size; + int rc; + + /* Open controller, flash and ffs */ + rc = sfc_open(&pnor_ctrl); + if (rc) { + prerror("PLAT: Failed to open PNOR flash controller\n"); + goto fail; + } + rc = flash_init(pnor_ctrl, &pnor_chip); + if (rc) { + prerror("PLAT: Failed to open init PNOR driver\n"); + goto fail; + } + rc = ffs_open_flash(pnor_chip, 0, 0, &pnor_ffs); + if (rc) { + prerror("PLAT: Failed to parse FFS partition map\n"); + goto fail; + } + + /* + * Grab NVRAM and initialize the flash_nvram module + * + * Note: Ignore actual size for now ... some images have + * it setup incorrectly. + */ + rc = ffs_lookup_part(pnor_ffs, "NVRAM", &nv_part); + if (rc) { + prerror("PLAT: No NVRAM partition in PNOR\n"); + return OPAL_HARDWARE; + } + rc = ffs_part_info(pnor_ffs, nv_part, NULL, + &nv_start, &nv_size, NULL); + if (rc) { + prerror("PLAT: Failed to get NVRAM partition info\n"); + return OPAL_HARDWARE; + } + flash_nvram_init(pnor_chip, nv_start, nv_size); + + return 0; + fail: + if (pnor_ffs) + ffs_close(pnor_ffs); + pnor_ffs = NULL; + if (pnor_chip) + flash_exit(pnor_chip); + pnor_chip = NULL; + if (pnor_ctrl) + sfc_close(pnor_ctrl); + pnor_ctrl = NULL; + + return rc; +} + +static void rhesus_init(void) +{ + if (dummy_console_enabled()) + dummy_console_add_nodes(); + + /* Initialize PNOR/NVRAM */ + rhesus_pnor_init(); +} + +static void rhesus_dt_fixup_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. + * + * TODO: Probe the UART instead if the LPC bus allows for it + */ + struct dt_node *uart; + char namebuf[32]; +#define UART_IO_BASE 0x3f8 +#define UART_IO_COUNT 8 + + 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)); +} + +/* + * This adds the legacy RTC device to the device-tree + * for Linux to use + */ +static void rhesus_dt_fixup_rtc(struct dt_node *lpc) +{ + struct dt_node *rtc; + + /* + * Follows the structure expected by the kernel file + * arch/powerpc/sysdev/rtc_cmos_setup.c + */ + rtc = dt_new_addr(lpc, "rtc", EC_RTC_PORT_BASE); + dt_add_property_string(rtc, "compatible", "pnpPNP,b00"); + dt_add_property_cells(rtc, "reg", + 1, /* IO space */ + EC_RTC_PORT_BASE, + /* 1 index/data pair per 128 bytes */ + (EC_RTC_BLOCK_SIZE / 128) * 2); +} + +static void rhesus_dt_fixup(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; + + rhesus_dt_fixup_rtc(primary_lpc); + rhesus_dt_fixup_uart(primary_lpc); +} + +static bool rhesus_probe(void) +{ + const char *model; + int rev; + + if (!dt_node_is_compatible(dt_root, "ibm,powernv")) + return false; + + model = dt_prop_get_def(dt_root, "model", NULL); + if (!model || !(strstr(model, "rhesus") || strstr(model, "RHESUS"))) + return false; + + /* Grab board version from EC */ + rev = rhesus_board_revision(); + if (rev >= 0) { + printf("Rhesus board rev %d\n", rev); + dt_add_property_cells(dt_root, "revision-id", rev); + } else + prerror("Rhesus board revision not found !\n"); + + /* Add missing bits of device-tree such as the UART */ + rhesus_dt_fixup(); + + /* + * Setup UART and use it as console. For now, we + * don't expose the interrupt as we know it's not + * working properly yet + */ + uart_init(false); + + return true; +} + +DECLARE_PLATFORM(rhesus) = { + .name = "Rhesus", + .probe = rhesus_probe, + .init = rhesus_init, + .cec_power_down = rhesus_power_down, + .cec_reboot = rhesus_reboot, +}; |