/* * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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, see . */ #include #include #include #include #include #include #include #include struct pg_partition_info { const char *name; int refcount; int run_refcount; struct mutex pg_mutex; }; static struct pg_partition_info t194_partition_info[] = { [TEGRA194_POWER_DOMAIN_AUD] = { .name = "aud" }, [TEGRA194_POWER_DOMAIN_DISP] = { .name = "disp" }, [TEGRA194_POWER_DOMAIN_DISPB] = { .name = "dispb" }, [TEGRA194_POWER_DOMAIN_DISPC] = { .name = "dispc" }, [TEGRA194_POWER_DOMAIN_ISPA] = { .name = "ispa" }, [TEGRA194_POWER_DOMAIN_NVDECA] = { .name = "nvdeca" }, [TEGRA194_POWER_DOMAIN_NVJPG] = { .name = "nvjpg" }, [TEGRA194_POWER_DOMAIN_NVENCA] = { .name = "nvenca" }, [TEGRA194_POWER_DOMAIN_NVENCB] = { .name = "nvencb" }, [TEGRA194_POWER_DOMAIN_NVDECB] = { .name = "nvdecb" }, [TEGRA194_POWER_DOMAIN_SAX] = { .name = "sax" }, [TEGRA194_POWER_DOMAIN_VE] = { .name = "ve" }, [TEGRA194_POWER_DOMAIN_VIC] = { .name = "vic" }, [TEGRA194_POWER_DOMAIN_XUSBA] = { .name = "xusba" }, [TEGRA194_POWER_DOMAIN_XUSBB] = { .name = "xusbb" }, [TEGRA194_POWER_DOMAIN_XUSBC] = { .name = "xusbc" }, [TEGRA194_POWER_DOMAIN_PCIEX8A] = { .name = "pciex8a" }, [TEGRA194_POWER_DOMAIN_PCIEX4A] = { .name = "pciex4a" }, [TEGRA194_POWER_DOMAIN_PCIEX1A] = { .name = "pciex1a" }, [TEGRA194_POWER_DOMAIN_NVL] = { .name = "nvl" }, [TEGRA194_POWER_DOMAIN_PCIEX8B] = { .name = "pciex8b" }, [TEGRA194_POWER_DOMAIN_PVAA] = { .name = "pvaa" }, [TEGRA194_POWER_DOMAIN_PVAB] = { .name = "pvab" }, [TEGRA194_POWER_DOMAIN_DLAA] = { .name = "dlaa" }, [TEGRA194_POWER_DOMAIN_DLAB] = { .name = "dlab" }, [TEGRA194_POWER_DOMAIN_CV] = { .name = "cv" }, [TEGRA194_POWER_DOMAIN_GPU] = { .name = "gpu" }, }; static int pg_set_state(int id, u32 state) { struct mrq_pg_request req = { .cmd = CMD_PG_SET_STATE, .id = id, .set_state = { .state = state, } }; return tegra_bpmp_send_receive(MRQ_PG, &req, sizeof(req), NULL, 0); } static int tegra194_pg_query_abi(void) { int ret; struct mrq_query_abi_request req = { .mrq = MRQ_PG }; struct mrq_query_abi_response resp; ret = tegra_bpmp_send_receive(MRQ_QUERY_ABI, &req, sizeof(req), &resp, sizeof(resp)); if (ret) return ret; return resp.status; } static int tegra194_pg_powergate_partition(int id) { int ret = 0; struct pg_partition_info *partition = &t194_partition_info[id]; mutex_lock(&partition->pg_mutex); if (partition->refcount) { if (--partition->refcount == 0) ret = pg_set_state(id, PG_STATE_OFF); } else { WARN(1, "partition %s refcount underflow\n", partition->name); } mutex_unlock(&partition->pg_mutex); return ret; } static int tegra194_pg_unpowergate_partition(int id) { int ret = 0; struct pg_partition_info *partition = &t194_partition_info[id]; mutex_lock(&partition->pg_mutex); if (partition->refcount++ == 0) ret = pg_set_state(id, PG_STATE_ON); mutex_unlock(&partition->pg_mutex); return ret; } static const char *tegra194_pg_get_name(int id) { return t194_partition_info[id].name; } static bool tegra194_pg_is_powered(int id) { int ret; struct mrq_pg_request req = { .cmd = CMD_PG_GET_STATE, .id = id, }; struct mrq_pg_response resp; ret = tegra_bpmp_send_receive(MRQ_PG, &req, sizeof(req), &resp, sizeof(resp)); if (ret) return false; if (resp.get_state.state == PG_STATE_OFF) return false; return true; } static int tegra194_init_refcount(void) { int i; for (i = 0; i < ARRAY_SIZE(t194_partition_info); ++i) mutex_init(&t194_partition_info[i].pg_mutex); return 0; } static bool tegra194_powergate_id_is_valid(int id) { return (id >= 1) && (id <= TEGRA194_POWER_DOMAIN_MAX); } static struct tegra_powergate_driver_ops tegra194_pg_ops = { .soc_name = "tegra194", .num_powerdomains = TEGRA194_POWER_DOMAIN_MAX + 1, .get_powergate_domain_name = tegra194_pg_get_name, .powergate_id_is_soc_valid = tegra194_powergate_id_is_valid, .powergate_init_refcount = tegra194_init_refcount, .powergate_is_powered = tegra194_pg_is_powered, .powergate_partition = tegra194_pg_powergate_partition, .unpowergate_partition = tegra194_pg_unpowergate_partition, }; struct tegra_powergate_driver_ops *tegra194_powergate_init_chip_support(void) { if (tegra194_pg_query_abi()) { WARN(1, "Missing BPMP support for MRQ_PG\n"); return NULL; } return &tegra194_pg_ops; }