1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
/* Copyright 2019 IBM Corp. */
#define pr_fmt(fmt) "DIO: " fmt
#include <chip.h>
#include <dio-p9.h>
#include <opal.h>
#include <xscom.h>
#include <xscom-p9-regs.h>
void p9_dio_init(void)
{
struct dt_node *xn;
struct proc_chip *chip;
struct p9_dio *dio;
if (proc_gen < proc_gen_p9)
return;
dt_for_each_compatible(dt_root, xn, "ibm,xscom") {
dio = zalloc(sizeof(struct p9_dio));
assert(dio);
chip = get_chip(dt_get_chip_id(xn));
assert(chip);
chip->dio = dio;
}
}
int dio_interrupt_register(struct proc_chip *chip,
int port, dio_interrupt_callback callback)
{
u64 val;
int rc;
assert(chip);
assert(chip->dio);
if (port < 0 || port >= NUM_OF_P9_DIO_PORTS)
return OPAL_PARAMETER;
if (chip->dio->callbacks[port]) /* This port already has a callback */
return OPAL_PARAMETER;
rc = xscom_read(chip->id, P9_GPIO_INTERRUPT_ENABLE, &val);
if (rc != OPAL_SUCCESS) {
prlog(PR_ERR, "XSCOM error %d reading reg 0x%llx\n",
rc, P9_GPIO_INTERRUPT_ENABLE);
return OPAL_HARDWARE;
}
val |= PPC_BIT(port);
rc = xscom_write(chip->id, P9_GPIO_INTERRUPT_ENABLE, val);
if (rc != OPAL_SUCCESS) {
prlog(PR_ERR, "XSCOM error %d writing reg 0x%llx\n",
rc, P9_GPIO_INTERRUPT_ENABLE);
return OPAL_HARDWARE;
}
chip->dio->callbacks[port] = callback;
return OPAL_SUCCESS;
}
int dio_interrupt_deregister(struct proc_chip* chip,
int port, dio_interrupt_callback callback)
{
u64 val;
int rc;
assert(chip);
assert(chip->dio);
if (port < 0 || port >= NUM_OF_P9_DIO_PORTS)
return OPAL_PARAMETER;
if (chip->dio->callbacks[port] != callback)
return OPAL_PARAMETER;
rc = xscom_read(chip->id, P9_GPIO_INTERRUPT_ENABLE, &val);
if (rc != OPAL_SUCCESS) {
prlog(PR_ERR, "XSCOM error %d reading reg 0x%llx\n",
rc, P9_GPIO_INTERRUPT_ENABLE);
return OPAL_HARDWARE;
}
val &= ~PPC_BIT(port);
rc = xscom_write(chip->id, P9_GPIO_INTERRUPT_ENABLE, val);
if (rc != OPAL_SUCCESS) {
prlog(PR_ERR, "XSCOM error %d writing reg 0x%llx\n",
rc, P9_GPIO_INTERRUPT_ENABLE);
return OPAL_HARDWARE;
}
chip->dio->callbacks[port] = NULL;
return OPAL_SUCCESS;
}
void dio_interrupt_handler(uint32_t chip_id)
{
struct proc_chip *chip;
u64 val;
int rc;
int i;
chip = get_chip(chip_id);
if (chip == NULL || chip->dio == NULL)
return;
rc = xscom_read(chip->id, P9_GPIO_INTERRUPT_STATUS, &val);
if (rc != OPAL_SUCCESS) {
prlog(PR_ERR, "XSCOM error %d reading reg 0x%llx\n",
rc, P9_GPIO_INTERRUPT_STATUS);
return;
}
for (i = 0; i < NUM_OF_P9_DIO_PORTS; ++i) {
if (val & PPC_BIT(i)) {
if (chip->dio->callbacks[i])
chip->dio->callbacks[i](chip);
else
prlog(PR_ERR,
"DIO interrupt triggerd on chip 0x%x"
" port %d but no handler\n",
chip->id, i);
/* Write 1 to clear the interrupt status */
xscom_write(chip->id, P9_GPIO_INTERRUPT_CONDITION,
val & PPC_BIT(i));
}
}
}
|