summaryrefslogtreecommitdiff
path: root/drivers/mxc/amd-gpu/common/gsl_intrmgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/amd-gpu/common/gsl_intrmgr.c')
-rw-r--r--drivers/mxc/amd-gpu/common/gsl_intrmgr.c305
1 files changed, 305 insertions, 0 deletions
diff --git a/drivers/mxc/amd-gpu/common/gsl_intrmgr.c b/drivers/mxc/amd-gpu/common/gsl_intrmgr.c
new file mode 100644
index 000000000000..2c8b278dfe7a
--- /dev/null
+++ b/drivers/mxc/amd-gpu/common/gsl_intrmgr.c
@@ -0,0 +1,305 @@
+/* Copyright (c) 2008-2010, Advanced Micro Devices. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "gsl.h"
+
+//////////////////////////////////////////////////////////////////////////////
+// macros
+//////////////////////////////////////////////////////////////////////////////
+#define GSL_INTRID_VALIDATE(id) (((id) < 0) || ((id) >= GSL_INTR_COUNT))
+
+
+//////////////////////////////////////////////////////////////////////////////
+// functions
+//////////////////////////////////////////////////////////////////////////////
+
+static const gsl_intrblock_reg_t *
+kgsl_intr_id2block(gsl_intrid_t id)
+{
+ const gsl_intrblock_reg_t *block;
+ int i;
+
+ // interrupt id to hw block
+ for (i = 0; i < GSL_INTR_BLOCK_COUNT; i++)
+ {
+ block = &gsl_cfg_intrblock_reg[i];
+
+ if (block->first_id <= id && id <= block->last_id)
+ {
+ return (block);
+ }
+ }
+
+ return (NULL);
+}
+
+//----------------------------------------------------------------------------
+
+void
+kgsl_intr_decode(gsl_device_t *device, gsl_intrblock_t block_id)
+{
+ const gsl_intrblock_reg_t *block = &gsl_cfg_intrblock_reg[block_id];
+ gsl_intrid_t id;
+ unsigned int status;
+
+ // read the block's interrupt status bits
+ device->ftbl.device_regread(device, block->status_reg, &status);
+
+ // mask off any interrupts which are disabled
+ status &= device->intr.enabled[block->id];
+
+ // acknowledge the block's interrupts
+ device->ftbl.device_regwrite(device, block->clear_reg, status);
+
+ // loop through the block's masks, determine which interrupt bits are active, and call callback (or TODO queue DPC)
+ for (id = block->first_id; id <= block->last_id; id++)
+ {
+ if (status & gsl_cfg_intr_mask[id])
+ {
+ device->intr.handler[id].callback(id, device->intr.handler[id].cookie);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+
+KGSL_API void
+kgsl_intr_isr()
+{
+ gsl_deviceid_t device_id;
+ gsl_device_t *device;
+
+ // loop through the devices, and call device specific isr
+ for (device_id = (gsl_deviceid_t)(GSL_DEVICE_ANY + 1); device_id <= GSL_DEVICE_MAX; device_id++)
+ {
+ device = &gsl_driver.device[device_id-1]; // device_id is 1 based
+
+ if (device->intr.flags & GSL_FLAGS_INITIALIZED)
+ {
+ kgsl_device_active(device);
+ device->ftbl.intr_isr(device);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+
+int kgsl_intr_init(gsl_device_t *device)
+{
+ if (device->ftbl.intr_isr == NULL)
+ {
+ return (GSL_FAILURE_BADPARAM);
+ }
+
+ if (device->intr.flags & GSL_FLAGS_INITIALIZED)
+ {
+ return (GSL_SUCCESS);
+ }
+
+ device->intr.device = device;
+ device->intr.flags |= GSL_FLAGS_INITIALIZED;
+
+ // os_interrupt_setcallback(YAMATO_INTR, kgsl_intr_isr);
+ // os_interrupt_enable(YAMATO_INTR);
+
+ return (GSL_SUCCESS);
+}
+
+//----------------------------------------------------------------------------
+
+int kgsl_intr_close(gsl_device_t *device)
+{
+ const gsl_intrblock_reg_t *block;
+ int i, id;
+
+ if (device->intr.flags & GSL_FLAGS_INITIALIZED)
+ {
+ // check if there are any enabled interrupts lingering around
+ for (i = 0; i < GSL_INTR_BLOCK_COUNT; i++)
+ {
+ if (device->intr.enabled[i])
+ {
+ block = &gsl_cfg_intrblock_reg[i];
+
+ // loop through the block's masks, disable interrupts which active
+ for (id = block->first_id; id <= block->last_id; id++)
+ {
+ if (device->intr.enabled[i] & gsl_cfg_intr_mask[id])
+ {
+ kgsl_intr_disable(&device->intr, (gsl_intrid_t)id);
+ }
+ }
+ }
+ }
+
+ kos_memset(&device->intr, 0, sizeof(gsl_intr_t));
+ }
+
+ return (GSL_SUCCESS);
+}
+
+//----------------------------------------------------------------------------
+
+int kgsl_intr_enable(gsl_intr_t *intr, gsl_intrid_t id)
+{
+ const gsl_intrblock_reg_t *block;
+ unsigned int mask;
+ unsigned int enabled;
+
+ if (GSL_INTRID_VALIDATE(id))
+ {
+ return (GSL_FAILURE_BADPARAM);
+ }
+
+ if (intr->handler[id].callback == NULL)
+ {
+ return (GSL_FAILURE_NOTINITIALIZED);
+ }
+
+ block = kgsl_intr_id2block(id);
+ if (block == NULL)
+ {
+ return (GSL_FAILURE_SYSTEMERROR);
+ }
+
+ mask = gsl_cfg_intr_mask[id];
+ enabled = intr->enabled[block->id];
+
+ if (mask && !(enabled & mask))
+ {
+ intr->evnt[id] = kos_event_create(0);
+
+ enabled |= mask;
+ intr->enabled[block->id] = enabled;
+ intr->device->ftbl.device_regwrite(intr->device, block->mask_reg, enabled);
+ }
+
+ return (GSL_SUCCESS);
+}
+
+//----------------------------------------------------------------------------
+
+int kgsl_intr_disable(gsl_intr_t *intr, gsl_intrid_t id)
+{
+ const gsl_intrblock_reg_t *block;
+ unsigned int mask;
+ unsigned int enabled;
+
+ if (GSL_INTRID_VALIDATE(id))
+ {
+ return (GSL_FAILURE_BADPARAM);
+ }
+
+ if (intr->handler[id].callback == NULL)
+ {
+ return (GSL_FAILURE_NOTINITIALIZED);
+ }
+
+ block = kgsl_intr_id2block(id);
+ if (block == NULL)
+ {
+ return (GSL_FAILURE_SYSTEMERROR);
+ }
+
+ mask = gsl_cfg_intr_mask[id];
+ enabled = intr->enabled[block->id];
+
+ if (enabled & mask)
+ {
+ enabled &= ~mask;
+ intr->enabled[block->id] = enabled;
+ intr->device->ftbl.device_regwrite(intr->device, block->mask_reg, enabled);
+
+ kos_event_signal(intr->evnt[id]); // wake up waiting threads before destroying the event
+ kos_event_destroy(intr->evnt[id]);
+ intr->evnt[id] = 0;
+ }
+
+ return (GSL_SUCCESS);
+}
+
+//----------------------------------------------------------------------------
+
+int
+kgsl_intr_attach(gsl_intr_t *intr, gsl_intrid_t id, gsl_intr_callback_t callback, void *cookie)
+{
+ if (GSL_INTRID_VALIDATE(id) || callback == NULL)
+ {
+ return (GSL_FAILURE_BADPARAM);
+ }
+
+ if (intr->handler[id].callback != NULL)
+ {
+ if (intr->handler[id].callback == callback && intr->handler[id].cookie == cookie)
+ {
+ return (GSL_FAILURE_ALREADYINITIALIZED);
+ }
+ else
+ {
+ return (GSL_FAILURE_NOMOREAVAILABLE);
+ }
+ }
+
+ intr->handler[id].callback = callback;
+ intr->handler[id].cookie = cookie;
+
+ return (GSL_SUCCESS);
+}
+
+//----------------------------------------------------------------------------
+
+int
+kgsl_intr_detach(gsl_intr_t *intr, gsl_intrid_t id)
+{
+ if (GSL_INTRID_VALIDATE(id))
+ {
+ return (GSL_FAILURE_BADPARAM);
+ }
+
+ if (intr->handler[id].callback == NULL)
+ {
+ return (GSL_FAILURE_NOTINITIALIZED);
+ }
+
+ kgsl_intr_disable(intr, id);
+
+ intr->handler[id].callback = NULL;
+ intr->handler[id].cookie = NULL;
+
+ return (GSL_SUCCESS);
+}
+
+//----------------------------------------------------------------------------
+
+int
+kgsl_intr_isenabled(gsl_intr_t *intr, gsl_intrid_t id)
+{
+ int status = GSL_FAILURE;
+ const gsl_intrblock_reg_t *block = kgsl_intr_id2block(id);
+
+ if (block != NULL)
+ {
+ // check if interrupt is enabled
+ if (intr->enabled[block->id] & gsl_cfg_intr_mask[id])
+ {
+ status = GSL_SUCCESS;
+ }
+ }
+
+ return (status);
+}