/* * kgsl_iommu_disable_clk_event - An event function that is executed when * the required timestamp is reached. It disables the IOMMU clocks if * the timestamp on which the clocks can be disabled has expired. * @device - The kgsl device pointer * @data - The data passed during event creation, it is the MMU pointer * @id - Context ID, should always be KGSL_MEMSTORE_GLOBAL * @ts - The current timestamp that has expired for the device * * Disables IOMMU clocks if timestamp has expired * Return - void */ static void kgsl_iommu_clk_disable_event(struct kgsl_device *device, void *data, unsigned int id, unsigned int ts) { struct kgsl_mmu *mmu = data; struct kgsl_iommu *iommu = mmu->priv; if (!iommu->clk_event_queued) { if (0 > timestamp_cmp(ts, iommu->iommu_last_cmd_ts)) KGSL_DRV_ERR(device, "IOMMU disable clock event being cancelled, " "iommu_last_cmd_ts: %x, retired ts: %x\n", iommu->iommu_last_cmd_ts, ts); return; } if (0 <= timestamp_cmp(ts, iommu->iommu_last_cmd_ts)) { kgsl_iommu_disable_clk(mmu); iommu->clk_event_queued = false; } else { /* add new event to fire when ts is reached, this can happen * if we queued an event and someone requested the clocks to * be disbaled on a later timestamp */ if (kgsl_add_event(device, id, iommu->iommu_last_cmd_ts, kgsl_iommu_clk_disable_event, mmu, mmu)) { KGSL_DRV_ERR(device, "Failed to add IOMMU disable clk event\n"); iommu->clk_event_queued = false; } } }
/* * kgsl_iommu_disable_clk_on_ts - Sets up event to disable IOMMU clocks * @mmu - The kgsl MMU pointer * @ts - Timestamp on which the clocks should be disabled * @ts_valid - Indicates whether ts parameter is valid, if this parameter * is false then it means that the calling function wants to disable the * IOMMU clocks immediately without waiting for any timestamp * * Creates an event to disable the IOMMU clocks on timestamp and if event * already exists then updates the timestamp of disabling the IOMMU clocks * with the passed in ts if it is greater than the current value at which * the clocks will be disabled * Return - void */ static void kgsl_iommu_disable_clk_on_ts(struct kgsl_mmu *mmu, unsigned int ts, bool ts_valid) { struct kgsl_iommu *iommu = mmu->priv; if (iommu->clk_event_queued) { if (ts_valid && (0 < timestamp_cmp(ts, iommu->iommu_last_cmd_ts))) iommu->iommu_last_cmd_ts = ts; } else { if (ts_valid) { iommu->iommu_last_cmd_ts = ts; iommu->clk_event_queued = true; if (kgsl_add_event(mmu->device, KGSL_MEMSTORE_GLOBAL, ts, kgsl_iommu_clk_disable_event, mmu, mmu)) { KGSL_DRV_ERR(mmu->device, "Failed to add IOMMU disable clk event\n"); iommu->clk_event_queued = false; } } else { kgsl_iommu_disable_clk(mmu); } } }
/* * kgsl_iommu_enable_clk - Enable iommu clocks * @mmu - Pointer to mmu structure * @ctx_id - The context bank whose clocks are to be turned on * * Enables iommu clocks of a given context * Return: 0 on success else error code */ static int kgsl_iommu_enable_clk(struct kgsl_mmu *mmu, int ctx_id) { int ret = 0; int i, j; struct kgsl_iommu *iommu = mmu->priv; struct msm_iommu_drvdata *iommu_drvdata; for (i = 0; i < iommu->unit_count; i++) { struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i]; for (j = 0; j < iommu_unit->dev_count; j++) { if (iommu_unit->dev[j].clk_enabled || ctx_id != iommu_unit->dev[j].ctx_id) continue; iommu_drvdata = dev_get_drvdata(iommu_unit->dev[j].dev->parent); ret = clk_prepare_enable(iommu_drvdata->pclk); if (ret) goto done; if (iommu_drvdata->clk) { ret = clk_prepare_enable(iommu_drvdata->clk); if (ret) { clk_disable_unprepare( iommu_drvdata->pclk); goto done; } } iommu_unit->dev[j].clk_enabled = true; } } done: if (ret) kgsl_iommu_disable_clk(mmu); return ret; }
static void kgsl_iommu_clk_disable_event(struct kgsl_device *device, void *data, unsigned int id, unsigned int ts) { struct kgsl_mmu *mmu = data; struct kgsl_iommu *iommu = mmu->priv; if (!iommu->clk_event_queued) { if (0 > timestamp_cmp(ts, iommu->iommu_last_cmd_ts)) KGSL_DRV_ERR(device, "IOMMU disable clock event being cancelled, " "iommu_last_cmd_ts: %x, retired ts: %x\n", iommu->iommu_last_cmd_ts, ts); return; } if (0 <= timestamp_cmp(ts, iommu->iommu_last_cmd_ts)) { kgsl_iommu_disable_clk(mmu); iommu->clk_event_queued = false; } else { if (kgsl_add_event(device, id, iommu->iommu_last_cmd_ts, kgsl_iommu_clk_disable_event, mmu, mmu)) { KGSL_DRV_ERR(device, "Failed to add IOMMU disable clk event\n"); iommu->clk_event_queued = false; } } }
static void kgsl_iommu_stop(struct kgsl_mmu *mmu) { struct kgsl_iommu *iommu = mmu->priv; if (mmu->flags & KGSL_FLAGS_STARTED) { kgsl_regwrite(mmu->device, MH_MMU_CONFIG, 0x00000000); kgsl_detach_pagetable_iommu_domain(mmu); mmu->hwpagetable = NULL; mmu->flags &= ~KGSL_FLAGS_STARTED; } iommu->clk_event_queued = false; kgsl_cancel_events(mmu->device, mmu); kgsl_iommu_disable_clk(mmu); }
static void kgsl_iommu_stop(struct kgsl_mmu *mmu) { struct kgsl_iommu *iommu = mmu->priv; int i, j; /* * stop device mmu * * call this with the global lock held */ if (mmu->flags & KGSL_FLAGS_STARTED) { /* detach iommu attachment */ kgsl_detach_pagetable_iommu_domain(mmu); mmu->hwpagetable = NULL; mmu->flags &= ~KGSL_FLAGS_STARTED; if (mmu->fault) { for (i = 0; i < iommu->unit_count; i++) { struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i]; for (j = 0; j < iommu_unit->dev_count; j++) { if (iommu_unit->dev[j].fault) { kgsl_iommu_enable_clk(mmu, j); KGSL_IOMMU_SET_CTX_REG(iommu, iommu_unit, iommu_unit->dev[j].ctx_id, RESUME, 1); iommu_unit->dev[j].fault = 0; } } } mmu->fault = 0; } } /* switch off MMU clocks and cancel any events it has queued */ iommu->clk_event_queued = false; kgsl_cancel_events(mmu->device, mmu); kgsl_iommu_disable_clk(mmu); }
static void kgsl_iommu_stop(struct kgsl_mmu *mmu) { struct kgsl_iommu *iommu = mmu->priv; /* * stop device mmu * * call this with the global lock held */ if (mmu->flags & KGSL_FLAGS_STARTED) { /* detach iommu attachment */ kgsl_detach_pagetable_iommu_domain(mmu); mmu->hwpagetable = NULL; mmu->flags &= ~KGSL_FLAGS_STARTED; } /* switch off MMU clocks and cancel any events it has queued */ iommu->clk_event_queued = false; kgsl_cancel_events(mmu->device, mmu); kgsl_iommu_disable_clk(mmu); }