Home | History | Annotate | Line # | Download | only in amdgpu
amdgpu_irq.c revision 1.4.2.2
      1 /*	$NetBSD: amdgpu_irq.c,v 1.4.2.2 2018/09/06 06:56:10 pgoyette Exp $	*/
      2 
      3 /*
      4  * Copyright 2008 Advanced Micro Devices, Inc.
      5  * Copyright 2008 Red Hat Inc.
      6  * Copyright 2009 Jerome Glisse.
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining a
      9  * copy of this software and associated documentation files (the "Software"),
     10  * to deal in the Software without restriction, including without limitation
     11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     12  * and/or sell copies of the Software, and to permit persons to whom the
     13  * Software is furnished to do so, subject to the following conditions:
     14  *
     15  * The above copyright notice and this permission notice shall be included in
     16  * all copies or substantial portions of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     21  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     22  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     23  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     24  * OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  * Authors: Dave Airlie
     27  *          Alex Deucher
     28  *          Jerome Glisse
     29  */
     30 #include <sys/cdefs.h>
     31 __KERNEL_RCSID(0, "$NetBSD: amdgpu_irq.c,v 1.4.2.2 2018/09/06 06:56:10 pgoyette Exp $");
     32 
     33 #include <linux/irq.h>
     34 #include <drm/drmP.h>
     35 #include <drm/drm_crtc_helper.h>
     36 #include <drm/amdgpu_drm.h>
     37 #include "amdgpu.h"
     38 #include "amdgpu_ih.h"
     39 #include "atom.h"
     40 #include "amdgpu_connectors.h"
     41 
     42 #include <linux/pm_runtime.h>
     43 
     44 #define AMDGPU_WAIT_IDLE_TIMEOUT 200
     45 
     46 /*
     47  * Handle hotplug events outside the interrupt handler proper.
     48  */
     49 /**
     50  * amdgpu_hotplug_work_func - display hotplug work handler
     51  *
     52  * @work: work struct
     53  *
     54  * This is the hot plug event work handler (all asics).
     55  * The work gets scheduled from the irq handler if there
     56  * was a hot plug interrupt.  It walks the connector table
     57  * and calls the hotplug handler for each one, then sends
     58  * a drm hotplug event to alert userspace.
     59  */
     60 static void amdgpu_hotplug_work_func(struct work_struct *work)
     61 {
     62 	struct amdgpu_device *adev = container_of(work, struct amdgpu_device,
     63 						  hotplug_work);
     64 	struct drm_device *dev = adev->ddev;
     65 	struct drm_mode_config *mode_config = &dev->mode_config;
     66 	struct drm_connector *connector;
     67 
     68 	mutex_lock(&mode_config->mutex);
     69 	if (mode_config->num_connector) {
     70 		list_for_each_entry(connector, &mode_config->connector_list, head)
     71 			amdgpu_connector_hotplug(connector);
     72 	}
     73 	mutex_unlock(&mode_config->mutex);
     74 	/* Just fire off a uevent and let userspace tell us what to do */
     75 	drm_helper_hpd_irq_event(dev);
     76 }
     77 
     78 /**
     79  * amdgpu_irq_reset_work_func - execute gpu reset
     80  *
     81  * @work: work struct
     82  *
     83  * Execute scheduled gpu reset (cayman+).
     84  * This function is called when the irq handler
     85  * thinks we need a gpu reset.
     86  */
     87 static void amdgpu_irq_reset_work_func(struct work_struct *work)
     88 {
     89 	struct amdgpu_device *adev = container_of(work, struct amdgpu_device,
     90 						  reset_work);
     91 
     92 	amdgpu_gpu_reset(adev);
     93 }
     94 
     95 /* Disable *all* interrupts */
     96 static void amdgpu_irq_disable_all(struct amdgpu_device *adev)
     97 {
     98 	unsigned long irqflags;
     99 	unsigned i, j;
    100 	int r;
    101 
    102 	spin_lock_irqsave(&adev->irq.lock, irqflags);
    103 	for (i = 0; i < AMDGPU_MAX_IRQ_SRC_ID; ++i) {
    104 		struct amdgpu_irq_src *src = adev->irq.sources[i];
    105 
    106 		if (!src || !src->funcs->set || !src->num_types)
    107 			continue;
    108 
    109 		for (j = 0; j < src->num_types; ++j) {
    110 			atomic_set(&src->enabled_types[j], 0);
    111 			r = src->funcs->set(adev, src, j,
    112 					    AMDGPU_IRQ_STATE_DISABLE);
    113 			if (r)
    114 				DRM_ERROR("error disabling interrupt (%d)\n",
    115 					  r);
    116 		}
    117 	}
    118 	spin_unlock_irqrestore(&adev->irq.lock, irqflags);
    119 }
    120 
    121 /**
    122  * amdgpu_irq_preinstall - drm irq preinstall callback
    123  *
    124  * @dev: drm dev pointer
    125  *
    126  * Gets the hw ready to enable irqs (all asics).
    127  * This function disables all interrupt sources on the GPU.
    128  */
    129 void amdgpu_irq_preinstall(struct drm_device *dev)
    130 {
    131 	struct amdgpu_device *adev = dev->dev_private;
    132 
    133 	/* Disable *all* interrupts */
    134 	amdgpu_irq_disable_all(adev);
    135 	/* Clear bits */
    136 	amdgpu_ih_process(adev);
    137 }
    138 
    139 /**
    140  * amdgpu_irq_postinstall - drm irq preinstall callback
    141  *
    142  * @dev: drm dev pointer
    143  *
    144  * Handles stuff to be done after enabling irqs (all asics).
    145  * Returns 0 on success.
    146  */
    147 int amdgpu_irq_postinstall(struct drm_device *dev)
    148 {
    149 	dev->max_vblank_count = 0x00ffffff;
    150 	return 0;
    151 }
    152 
    153 /**
    154  * amdgpu_irq_uninstall - drm irq uninstall callback
    155  *
    156  * @dev: drm dev pointer
    157  *
    158  * This function disables all interrupt sources on the GPU (all asics).
    159  */
    160 void amdgpu_irq_uninstall(struct drm_device *dev)
    161 {
    162 	struct amdgpu_device *adev = dev->dev_private;
    163 
    164 	if (adev == NULL) {
    165 		return;
    166 	}
    167 	amdgpu_irq_disable_all(adev);
    168 }
    169 
    170 /**
    171  * amdgpu_irq_handler - irq handler
    172  *
    173  * @int irq, void *arg: args
    174  *
    175  * This is the irq handler for the amdgpu driver (all asics).
    176  */
    177 irqreturn_t amdgpu_irq_handler(DRM_IRQ_ARGS)
    178 {
    179 	struct drm_device *dev = (struct drm_device *) arg;
    180 	struct amdgpu_device *adev = dev->dev_private;
    181 	irqreturn_t ret;
    182 
    183 	ret = amdgpu_ih_process(adev);
    184 	if (ret == IRQ_HANDLED)
    185 		pm_runtime_mark_last_busy(dev->dev);
    186 	return ret;
    187 }
    188 
    189 /**
    190  * amdgpu_msi_ok - asic specific msi checks
    191  *
    192  * @adev: amdgpu device pointer
    193  *
    194  * Handles asic specific MSI checks to determine if
    195  * MSIs should be enabled on a particular chip (all asics).
    196  * Returns true if MSIs should be enabled, false if MSIs
    197  * should not be enabled.
    198  */
    199 static bool amdgpu_msi_ok(struct amdgpu_device *adev)
    200 {
    201 	/* force MSI on */
    202 	if (amdgpu_msi == 1)
    203 		return true;
    204 	else if (amdgpu_msi == 0)
    205 		return false;
    206 
    207 	return true;
    208 }
    209 
    210 /**
    211  * amdgpu_irq_init - init driver interrupt info
    212  *
    213  * @adev: amdgpu device pointer
    214  *
    215  * Sets up the work irq handlers, vblank init, MSIs, etc. (all asics).
    216  * Returns 0 for success, error for failure.
    217  */
    218 int amdgpu_irq_init(struct amdgpu_device *adev)
    219 {
    220 	int r = 0;
    221 
    222 	spin_lock_init(&adev->irq.lock);
    223 	r = drm_vblank_init(adev->ddev, adev->mode_info.num_crtc);
    224 	if (r) {
    225 		return r;
    226 	}
    227 	/* enable msi */
    228 	adev->irq.msi_enabled = false;
    229 
    230 	if (amdgpu_msi_ok(adev)) {
    231 		int ret = pci_enable_msi(adev->pdev);
    232 		if (!ret) {
    233 			adev->irq.msi_enabled = true;
    234 			dev_info(adev->dev, "amdgpu: using MSI.\n");
    235 		}
    236 	}
    237 
    238 	INIT_WORK(&adev->hotplug_work, amdgpu_hotplug_work_func);
    239 	INIT_WORK(&adev->reset_work, amdgpu_irq_reset_work_func);
    240 
    241 	adev->irq.installed = true;
    242 #ifdef __NetBSD__
    243 	r = drm_irq_install(adev->ddev);
    244 #else
    245 	r = drm_irq_install(adev->ddev, adev->ddev->pdev->irq);
    246 #endif
    247 	if (r) {
    248 		adev->irq.installed = false;
    249 		flush_work(&adev->hotplug_work);
    250 		return r;
    251 	}
    252 
    253 	DRM_INFO("amdgpu: irq initialized.\n");
    254 	return 0;
    255 }
    256 
    257 /**
    258  * amdgpu_irq_fini - tear down driver interrupt info
    259  *
    260  * @adev: amdgpu device pointer
    261  *
    262  * Tears down the work irq handlers, vblank handlers, MSIs, etc. (all asics).
    263  */
    264 void amdgpu_irq_fini(struct amdgpu_device *adev)
    265 {
    266 	unsigned i;
    267 
    268 	drm_vblank_cleanup(adev->ddev);
    269 	if (adev->irq.installed) {
    270 		drm_irq_uninstall(adev->ddev);
    271 		adev->irq.installed = false;
    272 		if (adev->irq.msi_enabled)
    273 			pci_disable_msi(adev->pdev);
    274 		flush_work(&adev->hotplug_work);
    275 	}
    276 
    277 	for (i = 0; i < AMDGPU_MAX_IRQ_SRC_ID; ++i) {
    278 		struct amdgpu_irq_src *src = adev->irq.sources[i];
    279 
    280 		if (!src)
    281 			continue;
    282 
    283 		kfree(src->enabled_types);
    284 		src->enabled_types = NULL;
    285 		if (src->data) {
    286 			kfree(src->data);
    287 			kfree(src);
    288 			adev->irq.sources[i] = NULL;
    289 		}
    290 	}
    291 }
    292 
    293 /**
    294  * amdgpu_irq_add_id - register irq source
    295  *
    296  * @adev: amdgpu device pointer
    297  * @src_id: source id for this source
    298  * @source: irq source
    299  *
    300  */
    301 int amdgpu_irq_add_id(struct amdgpu_device *adev, unsigned src_id,
    302 		      struct amdgpu_irq_src *source)
    303 {
    304 	if (src_id >= AMDGPU_MAX_IRQ_SRC_ID)
    305 		return -EINVAL;
    306 
    307 	if (adev->irq.sources[src_id] != NULL)
    308 		return -EINVAL;
    309 
    310 	if (!source->funcs)
    311 		return -EINVAL;
    312 
    313 	if (source->num_types && !source->enabled_types) {
    314 		atomic_t *types;
    315 
    316 		types = kcalloc(source->num_types, sizeof(atomic_t),
    317 				GFP_KERNEL);
    318 		if (!types)
    319 			return -ENOMEM;
    320 
    321 		source->enabled_types = types;
    322 	}
    323 
    324 	adev->irq.sources[src_id] = source;
    325 	return 0;
    326 }
    327 
    328 /**
    329  * amdgpu_irq_dispatch - dispatch irq to IP blocks
    330  *
    331  * @adev: amdgpu device pointer
    332  * @entry: interrupt vector
    333  *
    334  * Dispatches the irq to the different IP blocks
    335  */
    336 void amdgpu_irq_dispatch(struct amdgpu_device *adev,
    337 			 struct amdgpu_iv_entry *entry)
    338 {
    339 	unsigned src_id = entry->src_id;
    340 	struct amdgpu_irq_src *src;
    341 	int r;
    342 
    343 	if (src_id >= AMDGPU_MAX_IRQ_SRC_ID) {
    344 		DRM_DEBUG("Invalid src_id in IV: %d\n", src_id);
    345 		return;
    346 	}
    347 
    348 	src = adev->irq.sources[src_id];
    349 	if (!src) {
    350 		DRM_DEBUG("Unhandled interrupt src_id: %d\n", src_id);
    351 		return;
    352 	}
    353 
    354 	r = src->funcs->process(adev, src, entry);
    355 	if (r)
    356 		DRM_ERROR("error processing interrupt (%d)\n", r);
    357 }
    358 
    359 /**
    360  * amdgpu_irq_update - update hw interrupt state
    361  *
    362  * @adev: amdgpu device pointer
    363  * @src: interrupt src you want to enable
    364  * @type: type of interrupt you want to update
    365  *
    366  * Updates the interrupt state for a specific src (all asics).
    367  */
    368 int amdgpu_irq_update(struct amdgpu_device *adev,
    369 			     struct amdgpu_irq_src *src, unsigned type)
    370 {
    371 	unsigned long irqflags;
    372 	enum amdgpu_interrupt_state state;
    373 	int r;
    374 
    375 	spin_lock_irqsave(&adev->irq.lock, irqflags);
    376 
    377 	/* we need to determine after taking the lock, otherwise
    378 	   we might disable just enabled interrupts again */
    379 	if (amdgpu_irq_enabled(adev, src, type))
    380 		state = AMDGPU_IRQ_STATE_ENABLE;
    381 	else
    382 		state = AMDGPU_IRQ_STATE_DISABLE;
    383 
    384 	r = src->funcs->set(adev, src, type, state);
    385 	spin_unlock_irqrestore(&adev->irq.lock, irqflags);
    386 	return r;
    387 }
    388 
    389 /**
    390  * amdgpu_irq_get - enable interrupt
    391  *
    392  * @adev: amdgpu device pointer
    393  * @src: interrupt src you want to enable
    394  * @type: type of interrupt you want to enable
    395  *
    396  * Enables the interrupt type for a specific src (all asics).
    397  */
    398 int amdgpu_irq_get(struct amdgpu_device *adev, struct amdgpu_irq_src *src,
    399 		   unsigned type)
    400 {
    401 	if (!adev->ddev->irq_enabled)
    402 		return -ENOENT;
    403 
    404 	if (type >= src->num_types)
    405 		return -EINVAL;
    406 
    407 	if (!src->enabled_types || !src->funcs->set)
    408 		return -EINVAL;
    409 
    410 	if (atomic_inc_return(&src->enabled_types[type]) == 1)
    411 		return amdgpu_irq_update(adev, src, type);
    412 
    413 	return 0;
    414 }
    415 
    416 bool amdgpu_irq_get_delayed(struct amdgpu_device *adev,
    417 			struct amdgpu_irq_src *src,
    418 			unsigned type)
    419 {
    420 	if ((type >= src->num_types) || !src->enabled_types)
    421 		return false;
    422 	return atomic_inc_return(&src->enabled_types[type]) == 1;
    423 }
    424 
    425 /**
    426  * amdgpu_irq_put - disable interrupt
    427  *
    428  * @adev: amdgpu device pointer
    429  * @src: interrupt src you want to disable
    430  * @type: type of interrupt you want to disable
    431  *
    432  * Disables the interrupt type for a specific src (all asics).
    433  */
    434 int amdgpu_irq_put(struct amdgpu_device *adev, struct amdgpu_irq_src *src,
    435 		   unsigned type)
    436 {
    437 	if (!adev->ddev->irq_enabled)
    438 		return -ENOENT;
    439 
    440 	if (type >= src->num_types)
    441 		return -EINVAL;
    442 
    443 	if (!src->enabled_types || !src->funcs->set)
    444 		return -EINVAL;
    445 
    446 	if (atomic_dec_and_test(&src->enabled_types[type]))
    447 		return amdgpu_irq_update(adev, src, type);
    448 
    449 	return 0;
    450 }
    451 
    452 /**
    453  * amdgpu_irq_enabled - test if irq is enabled or not
    454  *
    455  * @adev: amdgpu device pointer
    456  * @idx: interrupt src you want to test
    457  *
    458  * Tests if the given interrupt source is enabled or not
    459  */
    460 bool amdgpu_irq_enabled(struct amdgpu_device *adev, struct amdgpu_irq_src *src,
    461 			unsigned type)
    462 {
    463 	if (!adev->ddev->irq_enabled)
    464 		return false;
    465 
    466 	if (type >= src->num_types)
    467 		return false;
    468 
    469 	if (!src->enabled_types || !src->funcs->set)
    470 		return false;
    471 
    472 	return !!atomic_read(&src->enabled_types[type]);
    473 }
    474