162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright(c) 2019-2022, Intel Corporation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/irq.h>
762306a36Sopenharmony_ci#include <linux/mei_aux.h>
862306a36Sopenharmony_ci#include "i915_drv.h"
962306a36Sopenharmony_ci#include "i915_reg.h"
1062306a36Sopenharmony_ci#include "gem/i915_gem_lmem.h"
1162306a36Sopenharmony_ci#include "gem/i915_gem_region.h"
1262306a36Sopenharmony_ci#include "gt/intel_gsc.h"
1362306a36Sopenharmony_ci#include "gt/intel_gt.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define GSC_BAR_LENGTH  0x00000FFC
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic void gsc_irq_mask(struct irq_data *d)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	/* generic irq handling */
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic void gsc_irq_unmask(struct irq_data *d)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	/* generic irq handling */
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic struct irq_chip gsc_irq_chip = {
2862306a36Sopenharmony_ci	.name = "gsc_irq_chip",
2962306a36Sopenharmony_ci	.irq_mask = gsc_irq_mask,
3062306a36Sopenharmony_ci	.irq_unmask = gsc_irq_unmask,
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int gsc_irq_init(int irq)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	irq_set_chip_and_handler_name(irq, &gsc_irq_chip,
3662306a36Sopenharmony_ci				      handle_simple_irq, "gsc_irq_handler");
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return irq_set_chip_data(irq, NULL);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int
4262306a36Sopenharmony_cigsc_ext_om_alloc(struct intel_gsc *gsc, struct intel_gsc_intf *intf, size_t size)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct intel_gt *gt = gsc_to_gt(gsc);
4562306a36Sopenharmony_ci	struct drm_i915_gem_object *obj;
4662306a36Sopenharmony_ci	int err;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	obj = i915_gem_object_create_lmem(gt->i915, size,
4962306a36Sopenharmony_ci					  I915_BO_ALLOC_CONTIGUOUS |
5062306a36Sopenharmony_ci					  I915_BO_ALLOC_CPU_CLEAR);
5162306a36Sopenharmony_ci	if (IS_ERR(obj)) {
5262306a36Sopenharmony_ci		drm_err(&gt->i915->drm, "Failed to allocate gsc memory\n");
5362306a36Sopenharmony_ci		return PTR_ERR(obj);
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	err = i915_gem_object_pin_pages_unlocked(obj);
5762306a36Sopenharmony_ci	if (err) {
5862306a36Sopenharmony_ci		drm_err(&gt->i915->drm, "Failed to pin pages for gsc memory\n");
5962306a36Sopenharmony_ci		goto out_put;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	intf->gem_obj = obj;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciout_put:
6762306a36Sopenharmony_ci	i915_gem_object_put(obj);
6862306a36Sopenharmony_ci	return err;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic void gsc_ext_om_destroy(struct intel_gsc_intf *intf)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct drm_i915_gem_object *obj = fetch_and_zero(&intf->gem_obj);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (!obj)
7662306a36Sopenharmony_ci		return;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (i915_gem_object_has_pinned_pages(obj))
7962306a36Sopenharmony_ci		i915_gem_object_unpin_pages(obj);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	i915_gem_object_put(obj);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistruct gsc_def {
8562306a36Sopenharmony_ci	const char *name;
8662306a36Sopenharmony_ci	unsigned long bar;
8762306a36Sopenharmony_ci	size_t bar_size;
8862306a36Sopenharmony_ci	bool use_polling;
8962306a36Sopenharmony_ci	bool slow_firmware;
9062306a36Sopenharmony_ci	size_t lmem_size;
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/* gsc resources and definitions (HECI1 and HECI2) */
9462306a36Sopenharmony_cistatic const struct gsc_def gsc_def_dg1[] = {
9562306a36Sopenharmony_ci	{
9662306a36Sopenharmony_ci		/* HECI1 not yet implemented. */
9762306a36Sopenharmony_ci	},
9862306a36Sopenharmony_ci	{
9962306a36Sopenharmony_ci		.name = "mei-gscfi",
10062306a36Sopenharmony_ci		.bar = DG1_GSC_HECI2_BASE,
10162306a36Sopenharmony_ci		.bar_size = GSC_BAR_LENGTH,
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic const struct gsc_def gsc_def_xehpsdv[] = {
10662306a36Sopenharmony_ci	{
10762306a36Sopenharmony_ci		/* HECI1 not enabled on the device. */
10862306a36Sopenharmony_ci	},
10962306a36Sopenharmony_ci	{
11062306a36Sopenharmony_ci		.name = "mei-gscfi",
11162306a36Sopenharmony_ci		.bar = DG1_GSC_HECI2_BASE,
11262306a36Sopenharmony_ci		.bar_size = GSC_BAR_LENGTH,
11362306a36Sopenharmony_ci		.use_polling = true,
11462306a36Sopenharmony_ci		.slow_firmware = true,
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic const struct gsc_def gsc_def_dg2[] = {
11962306a36Sopenharmony_ci	{
12062306a36Sopenharmony_ci		.name = "mei-gsc",
12162306a36Sopenharmony_ci		.bar = DG2_GSC_HECI1_BASE,
12262306a36Sopenharmony_ci		.bar_size = GSC_BAR_LENGTH,
12362306a36Sopenharmony_ci		.lmem_size = SZ_4M,
12462306a36Sopenharmony_ci	},
12562306a36Sopenharmony_ci	{
12662306a36Sopenharmony_ci		.name = "mei-gscfi",
12762306a36Sopenharmony_ci		.bar = DG2_GSC_HECI2_BASE,
12862306a36Sopenharmony_ci		.bar_size = GSC_BAR_LENGTH,
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void gsc_release_dev(struct device *dev)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct auxiliary_device *aux_dev = to_auxiliary_dev(dev);
13562306a36Sopenharmony_ci	struct mei_aux_device *adev = auxiliary_dev_to_mei_aux_dev(aux_dev);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	kfree(adev);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic void gsc_destroy_one(struct drm_i915_private *i915,
14162306a36Sopenharmony_ci			    struct intel_gsc *gsc, unsigned int intf_id)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct intel_gsc_intf *intf = &gsc->intf[intf_id];
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (intf->adev) {
14662306a36Sopenharmony_ci		struct auxiliary_device *aux_dev = &intf->adev->aux_dev;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci		if (intf_id == 0)
14962306a36Sopenharmony_ci			intel_huc_unregister_gsc_notifier(&gsc_to_gt(gsc)->uc.huc,
15062306a36Sopenharmony_ci							  aux_dev->dev.bus);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		auxiliary_device_delete(aux_dev);
15362306a36Sopenharmony_ci		auxiliary_device_uninit(aux_dev);
15462306a36Sopenharmony_ci		intf->adev = NULL;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (intf->irq >= 0)
15862306a36Sopenharmony_ci		irq_free_desc(intf->irq);
15962306a36Sopenharmony_ci	intf->irq = -1;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	gsc_ext_om_destroy(intf);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void gsc_init_one(struct drm_i915_private *i915, struct intel_gsc *gsc,
16562306a36Sopenharmony_ci			 unsigned int intf_id)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(i915->drm.dev);
16862306a36Sopenharmony_ci	struct mei_aux_device *adev;
16962306a36Sopenharmony_ci	struct auxiliary_device *aux_dev;
17062306a36Sopenharmony_ci	const struct gsc_def *def;
17162306a36Sopenharmony_ci	struct intel_gsc_intf *intf = &gsc->intf[intf_id];
17262306a36Sopenharmony_ci	int ret;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	intf->irq = -1;
17562306a36Sopenharmony_ci	intf->id = intf_id;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/*
17862306a36Sopenharmony_ci	 * On the multi-tile setups the GSC is functional on the first tile only
17962306a36Sopenharmony_ci	 */
18062306a36Sopenharmony_ci	if (gsc_to_gt(gsc)->info.id != 0) {
18162306a36Sopenharmony_ci		drm_dbg(&i915->drm, "Not initializing gsc for remote tiles\n");
18262306a36Sopenharmony_ci		return;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (intf_id == 0 && !HAS_HECI_PXP(i915))
18662306a36Sopenharmony_ci		return;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (IS_DG1(i915)) {
18962306a36Sopenharmony_ci		def = &gsc_def_dg1[intf_id];
19062306a36Sopenharmony_ci	} else if (IS_XEHPSDV(i915)) {
19162306a36Sopenharmony_ci		def = &gsc_def_xehpsdv[intf_id];
19262306a36Sopenharmony_ci	} else if (IS_DG2(i915)) {
19362306a36Sopenharmony_ci		def = &gsc_def_dg2[intf_id];
19462306a36Sopenharmony_ci	} else {
19562306a36Sopenharmony_ci		drm_warn_once(&i915->drm, "Unknown platform\n");
19662306a36Sopenharmony_ci		return;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (!def->name) {
20062306a36Sopenharmony_ci		drm_warn_once(&i915->drm, "HECI%d is not implemented!\n", intf_id + 1);
20162306a36Sopenharmony_ci		return;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* skip irq initialization */
20562306a36Sopenharmony_ci	if (def->use_polling)
20662306a36Sopenharmony_ci		goto add_device;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	intf->irq = irq_alloc_desc(0);
20962306a36Sopenharmony_ci	if (intf->irq < 0) {
21062306a36Sopenharmony_ci		drm_err(&i915->drm, "gsc irq error %d\n", intf->irq);
21162306a36Sopenharmony_ci		goto fail;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	ret = gsc_irq_init(intf->irq);
21562306a36Sopenharmony_ci	if (ret < 0) {
21662306a36Sopenharmony_ci		drm_err(&i915->drm, "gsc irq init failed %d\n", ret);
21762306a36Sopenharmony_ci		goto fail;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ciadd_device:
22162306a36Sopenharmony_ci	adev = kzalloc(sizeof(*adev), GFP_KERNEL);
22262306a36Sopenharmony_ci	if (!adev)
22362306a36Sopenharmony_ci		goto fail;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (def->lmem_size) {
22662306a36Sopenharmony_ci		drm_dbg(&i915->drm, "setting up GSC lmem\n");
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		if (gsc_ext_om_alloc(gsc, intf, def->lmem_size)) {
22962306a36Sopenharmony_ci			drm_err(&i915->drm, "setting up gsc extended operational memory failed\n");
23062306a36Sopenharmony_ci			kfree(adev);
23162306a36Sopenharmony_ci			goto fail;
23262306a36Sopenharmony_ci		}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		adev->ext_op_mem.start = i915_gem_object_get_dma_address(intf->gem_obj, 0);
23562306a36Sopenharmony_ci		adev->ext_op_mem.end = adev->ext_op_mem.start + def->lmem_size;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	adev->irq = intf->irq;
23962306a36Sopenharmony_ci	adev->bar.parent = &pdev->resource[0];
24062306a36Sopenharmony_ci	adev->bar.start = def->bar + pdev->resource[0].start;
24162306a36Sopenharmony_ci	adev->bar.end = adev->bar.start + def->bar_size - 1;
24262306a36Sopenharmony_ci	adev->bar.flags = IORESOURCE_MEM;
24362306a36Sopenharmony_ci	adev->bar.desc = IORES_DESC_NONE;
24462306a36Sopenharmony_ci	adev->slow_firmware = def->slow_firmware;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	aux_dev = &adev->aux_dev;
24762306a36Sopenharmony_ci	aux_dev->name = def->name;
24862306a36Sopenharmony_ci	aux_dev->id = (pci_domain_nr(pdev->bus) << 16) |
24962306a36Sopenharmony_ci		      PCI_DEVID(pdev->bus->number, pdev->devfn);
25062306a36Sopenharmony_ci	aux_dev->dev.parent = &pdev->dev;
25162306a36Sopenharmony_ci	aux_dev->dev.release = gsc_release_dev;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	ret = auxiliary_device_init(aux_dev);
25462306a36Sopenharmony_ci	if (ret < 0) {
25562306a36Sopenharmony_ci		drm_err(&i915->drm, "gsc aux init failed %d\n", ret);
25662306a36Sopenharmony_ci		kfree(adev);
25762306a36Sopenharmony_ci		goto fail;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	intf->adev = adev; /* needed by the notifier */
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (intf_id == 0)
26362306a36Sopenharmony_ci		intel_huc_register_gsc_notifier(&gsc_to_gt(gsc)->uc.huc,
26462306a36Sopenharmony_ci						aux_dev->dev.bus);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	ret = auxiliary_device_add(aux_dev);
26762306a36Sopenharmony_ci	if (ret < 0) {
26862306a36Sopenharmony_ci		drm_err(&i915->drm, "gsc aux add failed %d\n", ret);
26962306a36Sopenharmony_ci		if (intf_id == 0)
27062306a36Sopenharmony_ci			intel_huc_unregister_gsc_notifier(&gsc_to_gt(gsc)->uc.huc,
27162306a36Sopenharmony_ci							  aux_dev->dev.bus);
27262306a36Sopenharmony_ci		intf->adev = NULL;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		/* adev will be freed with the put_device() and .release sequence */
27562306a36Sopenharmony_ci		auxiliary_device_uninit(aux_dev);
27662306a36Sopenharmony_ci		goto fail;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return;
28062306a36Sopenharmony_cifail:
28162306a36Sopenharmony_ci	gsc_destroy_one(i915, gsc, intf->id);
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic void gsc_irq_handler(struct intel_gt *gt, unsigned int intf_id)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	int ret;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (intf_id >= INTEL_GSC_NUM_INTERFACES) {
28962306a36Sopenharmony_ci		drm_warn_once(&gt->i915->drm, "GSC irq: intf_id %d is out of range", intf_id);
29062306a36Sopenharmony_ci		return;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (!HAS_HECI_GSC(gt->i915)) {
29462306a36Sopenharmony_ci		drm_warn_once(&gt->i915->drm, "GSC irq: not supported");
29562306a36Sopenharmony_ci		return;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (gt->gsc.intf[intf_id].irq < 0)
29962306a36Sopenharmony_ci		return;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	ret = generic_handle_irq(gt->gsc.intf[intf_id].irq);
30262306a36Sopenharmony_ci	if (ret)
30362306a36Sopenharmony_ci		drm_err_ratelimited(&gt->i915->drm, "error handling GSC irq: %d\n", ret);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_civoid intel_gsc_irq_handler(struct intel_gt *gt, u32 iir)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	if (iir & GSC_IRQ_INTF(0))
30962306a36Sopenharmony_ci		gsc_irq_handler(gt, 0);
31062306a36Sopenharmony_ci	if (iir & GSC_IRQ_INTF(1))
31162306a36Sopenharmony_ci		gsc_irq_handler(gt, 1);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_civoid intel_gsc_init(struct intel_gsc *gsc, struct drm_i915_private *i915)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	unsigned int i;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (!HAS_HECI_GSC(i915))
31962306a36Sopenharmony_ci		return;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	for (i = 0; i < INTEL_GSC_NUM_INTERFACES; i++)
32262306a36Sopenharmony_ci		gsc_init_one(i915, gsc, i);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_civoid intel_gsc_fini(struct intel_gsc *gsc)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct intel_gt *gt = gsc_to_gt(gsc);
32862306a36Sopenharmony_ci	unsigned int i;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (!HAS_HECI_GSC(gt->i915))
33162306a36Sopenharmony_ci		return;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	for (i = 0; i < INTEL_GSC_NUM_INTERFACES; i++)
33462306a36Sopenharmony_ci		gsc_destroy_one(gt->i915, gsc, i);
33562306a36Sopenharmony_ci}
336