diff options
Diffstat (limited to 'roms/skiboot/platforms/rhesus')
-rw-r--r-- | roms/skiboot/platforms/rhesus/Makefile.inc | 6 | ||||
-rw-r--r-- | roms/skiboot/platforms/rhesus/ec/config.h | 70 | ||||
-rw-r--r-- | roms/skiboot/platforms/rhesus/ec/gpio.h | 41 | ||||
-rw-r--r-- | roms/skiboot/platforms/rhesus/gpio.c | 75 | ||||
-rw-r--r-- | roms/skiboot/platforms/rhesus/rhesus.c | 274 |
5 files changed, 466 insertions, 0 deletions
diff --git a/roms/skiboot/platforms/rhesus/Makefile.inc b/roms/skiboot/platforms/rhesus/Makefile.inc new file mode 100644 index 000000000..aa66f6aea --- /dev/null +++ b/roms/skiboot/platforms/rhesus/Makefile.inc @@ -0,0 +1,6 @@ +SUBDIRS += $(PLATDIR)/rhesus + +RHESUS_OBJS = rhesus.o gpio.o +RHESUS = $(PLATDIR)/rhesus/built-in.a +$(RHESUS): $(RHESUS_OBJS:%=$(PLATDIR)/rhesus/%) + diff --git a/roms/skiboot/platforms/rhesus/ec/config.h b/roms/skiboot/platforms/rhesus/ec/config.h new file mode 100644 index 000000000..df2ad5d46 --- /dev/null +++ b/roms/skiboot/platforms/rhesus/ec/config.h @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2013-2014 IBM Corp. */ + +/** + * @file config.H + * + * @brief Definitions for EC configuration values. + * + */ + +#ifndef __EC_CONFIG_H_ +#define __EC_CONFIG_H_ + +#include <stdint.h> + +#define EC_RTC_PORT_BASE (0x70) // RTC/CMOS LPC base address +#define EC_RTC_BLOCK_SIZE (512) // Size of addressable data in RTC +#define EC_RTC_CENTURY (1) // 1 if century format is enabled +#if EC_RTC_CENTURY +#define EC_RTC_BBRAM_OFFSET (0x33) // Offset of NV data (= size of calendar) +#else +#define EC_RTC_BBRAM_OFFSET (0x0E) // Offset of NV data (= size of calendar) +#endif // #if EC_RTC_CENTURY + +#define EC_RTCDD_READ_TRIES (2) // Times to try the RTC if updating +#define EC_RTCDD_RETRY_DELAY (300000) // Delay between RTC read retries in ns + // based on update time of 244 + 30.5 µs + +#define EC_GPIO_INDEX 0x200 +#define EC_GPIO_DATA 0x201 +#define EC_GPIO_NUM_PORTS 17 +#define EC_GPIO_PORT_SKIP 4 + +#define EC_GPIO_DATA_OFFSET 0x0 +#define EC_GPIO_DDR_OFFSET 0x1 +#define EC_GPIO_PIN_OFFSET 0x2 +#define EC_GPIO_PUP_OFFSET 0x3 + +typedef enum EcGpioPort { + EC_GPIO_PORT_A = 0, + EC_GPIO_PORT_B = 1, + EC_GPIO_PORT_C = 2, + EC_GPIO_PORT_D = 3, + EC_GPIO_PORT_E = 4, + EC_GPIO_PORT_F = 5, + EC_GPIO_PORT_G = 6, + EC_GPIO_PORT_H = 7, + // skip port I + EC_GPIO_PORT_J = 8, + EC_GPIO_PORT_K = 9, + EC_GPIO_PORT_L = 10, + EC_GPIO_PORT_M = 11, + EC_GPIO_PORT_N = 12, + // skip port O + EC_GPIO_PORT_P = 13, + EC_GPIO_PORT_Q = 14, + EC_GPIO_PORT_R = 15, + EC_GPIO_PORT_S = 16, +} EcGpioPort; + +#ifdef __cplusplus +extern "C" { +#endif +void ec_outb(uint16_t, uint8_t); +uint8_t ec_inb(uint16_t); +#ifdef __cplusplus +} +#endif + +#endif // __EC_CONFIG_H_ diff --git a/roms/skiboot/platforms/rhesus/ec/gpio.h b/roms/skiboot/platforms/rhesus/ec/gpio.h new file mode 100644 index 000000000..65d7d878a --- /dev/null +++ b/roms/skiboot/platforms/rhesus/ec/gpio.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2013-2014 Google Corp. */ + +/** + * @file gpio.h + * + * @brief Public interface of the EC GPIO device driver + * + */ + +#ifndef __EC_GPIO_H_ +#define __EC_GPIO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define EC_GPIO_INPUT 0 +#define EC_GPIO_OUTPUT 1 +#define EC_GPIO_PULLUP_DISABLE 0 +#define EC_GPIO_PULLUP_ENABLE 1 + +// Sets up a GPIO as output or input. +// Returns: <0 on error +int ec_gpio_setup(EcGpioPort port, uint8_t pin, + int is_output, int pullup_enable); + +// Reads the current value of an input GPIO. +// Returns: GPIO value (0,1) or <0 on error. +int ec_gpio_read(EcGpioPort port, uint8_t pin); + +// Sets the driving value of an output GPIO. Port should already be set +// to output mode. +// Returns: <0 on error +int ec_gpio_set(EcGpioPort port, uint8_t pin, int val); + +#ifdef __cplusplus +} +#endif + +#endif // __EC_GPIO_H_ diff --git a/roms/skiboot/platforms/rhesus/gpio.c b/roms/skiboot/platforms/rhesus/gpio.c new file mode 100644 index 000000000..2ac36303e --- /dev/null +++ b/roms/skiboot/platforms/rhesus/gpio.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2013-2014 Google Corp. */ + +#include <stdint.h> +#include "ec/config.h" +#include "ec/gpio.h" + +int ec_gpio_setup(EcGpioPort port, uint8_t pin, + int is_output, int pullup_enable) +{ + uint8_t ddr_reg; + if (pin > 7) { + return -1; + } + + /* Set data direction */ + ec_outb(EC_GPIO_INDEX, + port * EC_GPIO_PORT_SKIP + EC_GPIO_DDR_OFFSET); + ddr_reg = ec_inb(EC_GPIO_DATA); + if (is_output) { + ddr_reg |= (1 << pin); + } else { + ddr_reg &= ~(1 << pin); + } + ec_outb(EC_GPIO_DATA, ddr_reg); + + /* Set pullup enable for output GPOs */ + if (is_output) + { + uint8_t pup_reg; + ec_outb(EC_GPIO_INDEX, + port * EC_GPIO_PORT_SKIP + EC_GPIO_PUP_OFFSET); + pup_reg = ec_inb(EC_GPIO_DATA); + if (pullup_enable) { + pup_reg |= (1 << pin); + } else { + pup_reg &= ~(1 << pin); + } + ec_outb(EC_GPIO_DATA, pup_reg); + } + + return 0; +} + +int ec_gpio_read(EcGpioPort port, uint8_t pin) +{ + uint8_t pin_reg; + if (pin > 7) { + return -1; + } + + ec_outb(EC_GPIO_INDEX, + port * EC_GPIO_PORT_SKIP + EC_GPIO_PIN_OFFSET); + pin_reg = ec_inb(EC_GPIO_DATA); + return !!(pin_reg & (1 << pin)); +} + +int ec_gpio_set(EcGpioPort port, uint8_t pin, int val) +{ + uint8_t data_reg; + if (pin > 7) { + return -1; + } + + ec_outb(EC_GPIO_INDEX, + port * EC_GPIO_PORT_SKIP + EC_GPIO_DATA_OFFSET); + data_reg = ec_inb(EC_GPIO_DATA); + if (val) { + data_reg |= (1 << pin); + } else { + data_reg &= ~(1 << pin); + } + ec_outb(EC_GPIO_DATA, data_reg); + return 0; +} diff --git a/roms/skiboot/platforms/rhesus/rhesus.c b/roms/skiboot/platforms/rhesus/rhesus.c new file mode 100644 index 000000000..125f2c5ff --- /dev/null +++ b/roms/skiboot/platforms/rhesus/rhesus.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright 2013-2017 IBM Corp. + * Copyright 2014 Google Corp. + */ + +#include <skiboot.h> +#include <device.h> +#include <lpc.h> +#include <console.h> +#include <opal.h> +#include <interrupts.h> +#include <libflash/libflash.h> +#include <libflash/libffs.h> +#include <libflash/blocklevel.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 + + +/* + * 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) +{ + struct spi_flash_ctrl *pnor_ctrl; + struct blocklevel_device *bl = NULL; + 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, &bl, NULL); + if (rc) { + prerror("PLAT: Failed to open init PNOR driver\n"); + goto fail; + } + + rc = flash_register(bl); + if (!rc) + return 0; + + fail: + if (bl) + flash_exit(bl); + if (pnor_ctrl) + sfc_close(pnor_ctrl); + + return rc; +} + +static void rhesus_init(void) +{ + /* Initialize PNOR/NVRAM */ + rhesus_pnor_init(); + + /* Setup UART for direct use by Linux */ + uart_set_console_policy(UART_CONSOLE_OS); +} + +static void rhesus_dt_fixup_uart(struct dt_node *lpc, bool has_irq) +{ + /* + * 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 + + snprintf(namebuf, sizeof(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"); + + /* Expose the external interrupt if supported + */ + if (has_irq) { + uint32_t chip_id = dt_get_chip_id(lpc); + uint32_t irq = get_psi_interrupt(chip_id) + P8_IRQ_PSI_EXTERNAL; + dt_add_property_cells(uart, "interrupts", irq, 1); + dt_add_property_cells(uart, "interrupt-parent", + get_ics_phandle()); + } +} + +/* + * 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); + assert(rtc); + 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(bool has_uart_irq) +{ + 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, has_uart_irq); +} + +static bool rhesus_probe(void) +{ + const char *model; + int rev; + bool has_uart_irq = false; + + 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(has_uart_irq); + + /* Setup UART and use it as console */ + uart_init(); + + return true; +} + +DECLARE_PLATFORM(rhesus) = { + .name = "Rhesus", + .probe = rhesus_probe, + .init = rhesus_init, + .cec_power_down = rhesus_power_down, + .cec_reboot = rhesus_reboot, +}; |