// SPDX-License-Identifier: Apache-2.0 /* Copyright 2019 IBM Corp. */ #define pr_fmt(fmt) "DIO: " fmt #include #include #include #include #include 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)); } } }