Skip to content

Commit

Permalink
lpc/uart: Support routing of selected LPC interrupts to Linux
Browse files Browse the repository at this point in the history
Each LPC interrupt can be routed to one of 4 lines to the PSI
bridge which represent 4 different system interrupts. This
allows LPC clients to request as specific target (Linux or OPAL)
and makes the LPC core pick a route and configure it appropriately.

The UART is updated to properly forward interrupts to Linux
if necessary

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
  • Loading branch information
ozbenh authored and stewartsmith committed Mar 2, 2017
1 parent 95c59d1 commit a294077
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 93 deletions.
3 changes: 3 additions & 0 deletions core/init.c
Expand Up @@ -954,6 +954,9 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt)
* regions after that
*/

/* Create the LPC bus interrupt-map on P9 */
lpc_finalize_interrupts();

/* Add the list of interrupts going to OPAL */
add_opal_interrupts();

Expand Down
4 changes: 3 additions & 1 deletion hw/bt.c
Expand Up @@ -27,6 +27,7 @@
#include <ipmi.h>
#include <timebase.h>
#include <chip.h>
#include <interrupts.h>

/* BT registers */
#define BT_CTRL 0
Expand Down Expand Up @@ -669,7 +670,8 @@ void bt_init(void)

irq = dt_prop_get_u32(n, "interrupts");
bt_lpc_client.interrupts = LPC_IRQ(irq);
lpc_register_client(dt_get_chip_id(n), &bt_lpc_client);
lpc_register_client(dt_get_chip_id(n), &bt_lpc_client,
IRQ_ATTR_TARGET_OPAL);

/* Enqueue an IPMI message to ask the BMC about its BT capabilities */
get_bt_caps();
Expand Down
2 changes: 1 addition & 1 deletion hw/lpc-mbox.c
Expand Up @@ -273,7 +273,7 @@ void mbox_init(void)

chip_id = dt_get_chip_id(np);
mbox_lpc_client.interrupts = LPC_IRQ(irq);
lpc_register_client(chip_id, &mbox_lpc_client);
lpc_register_client(chip_id, &mbox_lpc_client, IRQ_ATTR_TARGET_OPAL);
prlog(PR_DEBUG, "Using chipid: %d and IRQ: %d at 0x%08x\n", chip_id, irq, mbox.base);
}

Expand Down
48 changes: 32 additions & 16 deletions hw/lpc-uart.c
Expand Up @@ -70,6 +70,7 @@ static uint8_t tx_room;
static uint8_t cached_ier;
static void *mmio_uart_base;
static int uart_console_policy = UART_CONSOLE_OPAL;
static int lpc_irq = -1;

void uart_set_console_policy(int policy)
{
Expand Down Expand Up @@ -423,15 +424,30 @@ static void uart_setup_os_passthrough(void)
{
char *path;

static struct lpc_client uart_lpc_os_client = {
};

dt_add_property_strings(uart_node, "status", "ok");
path = dt_get_path(uart_node);
dt_add_property_string(dt_chosen, "linux,stdout-path", path);
free(path);

/* Setup LPC client for OS interrupts */
if (lpc_irq >= 0) {
uint32_t chip_id = dt_get_chip_id(uart_node);
uart_lpc_os_client.interrupts = LPC_IRQ(lpc_irq);
lpc_register_client(chip_id, &uart_lpc_os_client,
IRQ_ATTR_TARGET_LINUX);
}
prlog(PR_DEBUG, "UART: Enabled as OS pass-through\n");
}

static void uart_setup_opal_console(void)
{
static struct lpc_client uart_lpc_opal_client = {
.interrupt = uart_irq,
};

/* Add the opal console node */
add_opal_console_node(0, "raw", OUT_BUF_SIZE);

Expand All @@ -444,17 +460,27 @@ static void uart_setup_opal_console(void)
*/
dt_add_property_strings(uart_node, "status", "reserved");

/* Allocate an input buffer */
in_buf = zalloc(IN_BUF_SIZE);
out_buf = zalloc(OUT_BUF_SIZE);

/* Setup LPC client for OPAL interrupts */
if (lpc_irq >= 0) {
uint32_t chip_id = dt_get_chip_id(uart_node);
uart_lpc_opal_client.interrupts = LPC_IRQ(lpc_irq);
lpc_register_client(chip_id, &uart_lpc_opal_client,
IRQ_ATTR_TARGET_OPAL);
has_irq = true;
}

/*
* If the interrupt is enabled, turn on RX interrupts (and
* only these for now
*/
tx_full = rx_full = false;
uart_update_ier();

/* Allocate an input buffer */
in_buf = zalloc(IN_BUF_SIZE);
out_buf = zalloc(OUT_BUF_SIZE);

/* Start console poller */
opal_add_poller(uart_console_poll, NULL);
}

Expand Down Expand Up @@ -519,16 +545,11 @@ static bool uart_init_hw(unsigned int speed, unsigned int clock)
return false;
}

static struct lpc_client uart_lpc_client = {
.interrupt = uart_irq,
};

void uart_init(void)
{
const struct dt_property *prop;
struct dt_node *n;
char *path __unused;
uint32_t chip_id;
const uint32_t *irqp;

/* UART lock is in the console path and thus must block
Expand Down Expand Up @@ -580,13 +601,8 @@ void uart_init(void)
uart_base = dt_property_get_cell(prop, 1);

if (irqp) {
uint32_t irq = be32_to_cpu(*irqp);

chip_id = dt_get_chip_id(uart_node);
uart_lpc_client.interrupts = LPC_IRQ(irq);
lpc_register_client(chip_id, &uart_lpc_client);
prlog(PR_DEBUG, "UART: Using LPC IRQ %d\n", irq);
has_irq = true;
lpc_irq = be32_to_cpu(*irqp);
prlog(PR_DEBUG, "UART: Using LPC IRQ %d\n", lpc_irq);
}
}

Expand Down

0 comments on commit a294077

Please sign in to comment.