162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020 Linaro Ltd 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/device.h> 762306a36Sopenharmony_ci#include <linux/interconnect-provider.h> 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/of_platform.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/regmap.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "icc-common.h" 1762306a36Sopenharmony_ci#include "icc-rpm.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* QNOC QoS */ 2062306a36Sopenharmony_ci#define QNOC_QOS_MCTL_LOWn_ADDR(n) (0x8 + (n * 0x1000)) 2162306a36Sopenharmony_ci#define QNOC_QOS_MCTL_DFLT_PRIO_MASK 0x70 2262306a36Sopenharmony_ci#define QNOC_QOS_MCTL_DFLT_PRIO_SHIFT 4 2362306a36Sopenharmony_ci#define QNOC_QOS_MCTL_URGFWD_EN_MASK 0x8 2462306a36Sopenharmony_ci#define QNOC_QOS_MCTL_URGFWD_EN_SHIFT 3 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* BIMC QoS */ 2762306a36Sopenharmony_ci#define M_BKE_REG_BASE(n) (0x300 + (0x4000 * n)) 2862306a36Sopenharmony_ci#define M_BKE_EN_ADDR(n) (M_BKE_REG_BASE(n)) 2962306a36Sopenharmony_ci#define M_BKE_HEALTH_CFG_ADDR(i, n) (M_BKE_REG_BASE(n) + 0x40 + (0x4 * i)) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define M_BKE_HEALTH_CFG_LIMITCMDS_MASK 0x80000000 3262306a36Sopenharmony_ci#define M_BKE_HEALTH_CFG_AREQPRIO_MASK 0x300 3362306a36Sopenharmony_ci#define M_BKE_HEALTH_CFG_PRIOLVL_MASK 0x3 3462306a36Sopenharmony_ci#define M_BKE_HEALTH_CFG_AREQPRIO_SHIFT 0x8 3562306a36Sopenharmony_ci#define M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT 0x1f 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define M_BKE_EN_EN_BMASK 0x1 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* NoC QoS */ 4062306a36Sopenharmony_ci#define NOC_QOS_PRIORITYn_ADDR(n) (0x8 + (n * 0x1000)) 4162306a36Sopenharmony_ci#define NOC_QOS_PRIORITY_P1_MASK 0xc 4262306a36Sopenharmony_ci#define NOC_QOS_PRIORITY_P0_MASK 0x3 4362306a36Sopenharmony_ci#define NOC_QOS_PRIORITY_P1_SHIFT 0x2 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define NOC_QOS_MODEn_ADDR(n) (0xc + (n * 0x1000)) 4662306a36Sopenharmony_ci#define NOC_QOS_MODEn_MASK 0x3 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define NOC_QOS_MODE_FIXED_VAL 0x0 4962306a36Sopenharmony_ci#define NOC_QOS_MODE_BYPASS_VAL 0x2 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define ICC_BUS_CLK_MIN_RATE 19200ULL /* kHz */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int qcom_icc_set_qnoc_qos(struct icc_node *src) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct icc_provider *provider = src->provider; 5662306a36Sopenharmony_ci struct qcom_icc_provider *qp = to_qcom_provider(provider); 5762306a36Sopenharmony_ci struct qcom_icc_node *qn = src->data; 5862306a36Sopenharmony_ci struct qcom_icc_qos *qos = &qn->qos; 5962306a36Sopenharmony_ci int rc; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci rc = regmap_update_bits(qp->regmap, 6262306a36Sopenharmony_ci qp->qos_offset + QNOC_QOS_MCTL_LOWn_ADDR(qos->qos_port), 6362306a36Sopenharmony_ci QNOC_QOS_MCTL_DFLT_PRIO_MASK, 6462306a36Sopenharmony_ci qos->areq_prio << QNOC_QOS_MCTL_DFLT_PRIO_SHIFT); 6562306a36Sopenharmony_ci if (rc) 6662306a36Sopenharmony_ci return rc; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return regmap_update_bits(qp->regmap, 6962306a36Sopenharmony_ci qp->qos_offset + QNOC_QOS_MCTL_LOWn_ADDR(qos->qos_port), 7062306a36Sopenharmony_ci QNOC_QOS_MCTL_URGFWD_EN_MASK, 7162306a36Sopenharmony_ci !!qos->urg_fwd_en << QNOC_QOS_MCTL_URGFWD_EN_SHIFT); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int qcom_icc_bimc_set_qos_health(struct qcom_icc_provider *qp, 7562306a36Sopenharmony_ci struct qcom_icc_qos *qos, 7662306a36Sopenharmony_ci int regnum) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci u32 val; 7962306a36Sopenharmony_ci u32 mask; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci val = qos->prio_level; 8262306a36Sopenharmony_ci mask = M_BKE_HEALTH_CFG_PRIOLVL_MASK; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci val |= qos->areq_prio << M_BKE_HEALTH_CFG_AREQPRIO_SHIFT; 8562306a36Sopenharmony_ci mask |= M_BKE_HEALTH_CFG_AREQPRIO_MASK; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* LIMITCMDS is not present on M_BKE_HEALTH_3 */ 8862306a36Sopenharmony_ci if (regnum != 3) { 8962306a36Sopenharmony_ci val |= qos->limit_commands << M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT; 9062306a36Sopenharmony_ci mask |= M_BKE_HEALTH_CFG_LIMITCMDS_MASK; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return regmap_update_bits(qp->regmap, 9462306a36Sopenharmony_ci qp->qos_offset + M_BKE_HEALTH_CFG_ADDR(regnum, qos->qos_port), 9562306a36Sopenharmony_ci mask, val); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int qcom_icc_set_bimc_qos(struct icc_node *src) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct qcom_icc_provider *qp; 10162306a36Sopenharmony_ci struct qcom_icc_node *qn; 10262306a36Sopenharmony_ci struct icc_provider *provider; 10362306a36Sopenharmony_ci u32 mode = NOC_QOS_MODE_BYPASS; 10462306a36Sopenharmony_ci u32 val = 0; 10562306a36Sopenharmony_ci int i, rc = 0; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci qn = src->data; 10862306a36Sopenharmony_ci provider = src->provider; 10962306a36Sopenharmony_ci qp = to_qcom_provider(provider); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) 11262306a36Sopenharmony_ci mode = qn->qos.qos_mode; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* QoS Priority: The QoS Health parameters are getting considered 11562306a36Sopenharmony_ci * only if we are NOT in Bypass Mode. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci if (mode != NOC_QOS_MODE_BYPASS) { 11862306a36Sopenharmony_ci for (i = 3; i >= 0; i--) { 11962306a36Sopenharmony_ci rc = qcom_icc_bimc_set_qos_health(qp, 12062306a36Sopenharmony_ci &qn->qos, i); 12162306a36Sopenharmony_ci if (rc) 12262306a36Sopenharmony_ci return rc; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Set BKE_EN to 1 when Fixed, Regulator or Limiter Mode */ 12662306a36Sopenharmony_ci val = 1; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return regmap_update_bits(qp->regmap, 13062306a36Sopenharmony_ci qp->qos_offset + M_BKE_EN_ADDR(qn->qos.qos_port), 13162306a36Sopenharmony_ci M_BKE_EN_EN_BMASK, val); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int qcom_icc_noc_set_qos_priority(struct qcom_icc_provider *qp, 13562306a36Sopenharmony_ci struct qcom_icc_qos *qos) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci u32 val; 13862306a36Sopenharmony_ci int rc; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* Must be updated one at a time, P1 first, P0 last */ 14162306a36Sopenharmony_ci val = qos->areq_prio << NOC_QOS_PRIORITY_P1_SHIFT; 14262306a36Sopenharmony_ci rc = regmap_update_bits(qp->regmap, 14362306a36Sopenharmony_ci qp->qos_offset + NOC_QOS_PRIORITYn_ADDR(qos->qos_port), 14462306a36Sopenharmony_ci NOC_QOS_PRIORITY_P1_MASK, val); 14562306a36Sopenharmony_ci if (rc) 14662306a36Sopenharmony_ci return rc; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return regmap_update_bits(qp->regmap, 14962306a36Sopenharmony_ci qp->qos_offset + NOC_QOS_PRIORITYn_ADDR(qos->qos_port), 15062306a36Sopenharmony_ci NOC_QOS_PRIORITY_P0_MASK, qos->prio_level); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int qcom_icc_set_noc_qos(struct icc_node *src) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct qcom_icc_provider *qp; 15662306a36Sopenharmony_ci struct qcom_icc_node *qn; 15762306a36Sopenharmony_ci struct icc_provider *provider; 15862306a36Sopenharmony_ci u32 mode = NOC_QOS_MODE_BYPASS_VAL; 15962306a36Sopenharmony_ci int rc = 0; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci qn = src->data; 16262306a36Sopenharmony_ci provider = src->provider; 16362306a36Sopenharmony_ci qp = to_qcom_provider(provider); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (qn->qos.qos_port < 0) { 16662306a36Sopenharmony_ci dev_dbg(src->provider->dev, 16762306a36Sopenharmony_ci "NoC QoS: Skipping %s: vote aggregated on parent.\n", 16862306a36Sopenharmony_ci qn->name); 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (qn->qos.qos_mode == NOC_QOS_MODE_FIXED) { 17362306a36Sopenharmony_ci dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n", qn->name); 17462306a36Sopenharmony_ci mode = NOC_QOS_MODE_FIXED_VAL; 17562306a36Sopenharmony_ci rc = qcom_icc_noc_set_qos_priority(qp, &qn->qos); 17662306a36Sopenharmony_ci if (rc) 17762306a36Sopenharmony_ci return rc; 17862306a36Sopenharmony_ci } else if (qn->qos.qos_mode == NOC_QOS_MODE_BYPASS) { 17962306a36Sopenharmony_ci dev_dbg(src->provider->dev, "NoC QoS: %s: Set Bypass mode\n", qn->name); 18062306a36Sopenharmony_ci mode = NOC_QOS_MODE_BYPASS_VAL; 18162306a36Sopenharmony_ci } else { 18262306a36Sopenharmony_ci /* How did we get here? */ 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return regmap_update_bits(qp->regmap, 18662306a36Sopenharmony_ci qp->qos_offset + NOC_QOS_MODEn_ADDR(qn->qos.qos_port), 18762306a36Sopenharmony_ci NOC_QOS_MODEn_MASK, mode); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int qcom_icc_qos_set(struct icc_node *node) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct qcom_icc_provider *qp = to_qcom_provider(node->provider); 19362306a36Sopenharmony_ci struct qcom_icc_node *qn = node->data; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci dev_dbg(node->provider->dev, "Setting QoS for %s\n", qn->name); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci switch (qp->type) { 19862306a36Sopenharmony_ci case QCOM_ICC_BIMC: 19962306a36Sopenharmony_ci return qcom_icc_set_bimc_qos(node); 20062306a36Sopenharmony_ci case QCOM_ICC_QNOC: 20162306a36Sopenharmony_ci return qcom_icc_set_qnoc_qos(node); 20262306a36Sopenharmony_ci default: 20362306a36Sopenharmony_ci return qcom_icc_set_noc_qos(node); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci int ret, rpm_ctx = 0; 21062306a36Sopenharmony_ci u64 bw_bps; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (qn->qos.ap_owned) 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci for (rpm_ctx = 0; rpm_ctx < QCOM_SMD_RPM_STATE_NUM; rpm_ctx++) { 21662306a36Sopenharmony_ci bw_bps = icc_units_to_bps(bw[rpm_ctx]); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (qn->mas_rpm_id != -1) { 21962306a36Sopenharmony_ci ret = qcom_icc_rpm_smd_send(rpm_ctx, 22062306a36Sopenharmony_ci RPM_BUS_MASTER_REQ, 22162306a36Sopenharmony_ci qn->mas_rpm_id, 22262306a36Sopenharmony_ci bw_bps); 22362306a36Sopenharmony_ci if (ret) { 22462306a36Sopenharmony_ci pr_err("qcom_icc_rpm_smd_send mas %d error %d\n", 22562306a36Sopenharmony_ci qn->mas_rpm_id, ret); 22662306a36Sopenharmony_ci return ret; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (qn->slv_rpm_id != -1) { 23162306a36Sopenharmony_ci ret = qcom_icc_rpm_smd_send(rpm_ctx, 23262306a36Sopenharmony_ci RPM_BUS_SLAVE_REQ, 23362306a36Sopenharmony_ci qn->slv_rpm_id, 23462306a36Sopenharmony_ci bw_bps); 23562306a36Sopenharmony_ci if (ret) { 23662306a36Sopenharmony_ci pr_err("qcom_icc_rpm_smd_send slv %d error %d\n", 23762306a36Sopenharmony_ci qn->slv_rpm_id, ret); 23862306a36Sopenharmony_ci return ret; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/** 24762306a36Sopenharmony_ci * qcom_icc_pre_bw_aggregate - cleans up values before re-aggregate requests 24862306a36Sopenharmony_ci * @node: icc node to operate on 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_cistatic void qcom_icc_pre_bw_aggregate(struct icc_node *node) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct qcom_icc_node *qn; 25362306a36Sopenharmony_ci size_t i; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci qn = node->data; 25662306a36Sopenharmony_ci for (i = 0; i < QCOM_SMD_RPM_STATE_NUM; i++) { 25762306a36Sopenharmony_ci qn->sum_avg[i] = 0; 25862306a36Sopenharmony_ci qn->max_peak[i] = 0; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/** 26362306a36Sopenharmony_ci * qcom_icc_bw_aggregate - aggregate bw for buckets indicated by tag 26462306a36Sopenharmony_ci * @node: node to aggregate 26562306a36Sopenharmony_ci * @tag: tag to indicate which buckets to aggregate 26662306a36Sopenharmony_ci * @avg_bw: new bw to sum aggregate 26762306a36Sopenharmony_ci * @peak_bw: new bw to max aggregate 26862306a36Sopenharmony_ci * @agg_avg: existing aggregate avg bw val 26962306a36Sopenharmony_ci * @agg_peak: existing aggregate peak bw val 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_cistatic int qcom_icc_bw_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, 27262306a36Sopenharmony_ci u32 peak_bw, u32 *agg_avg, u32 *agg_peak) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci size_t i; 27562306a36Sopenharmony_ci struct qcom_icc_node *qn; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci qn = node->data; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (!tag) 28062306a36Sopenharmony_ci tag = RPM_ALWAYS_TAG; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci for (i = 0; i < QCOM_SMD_RPM_STATE_NUM; i++) { 28362306a36Sopenharmony_ci if (tag & BIT(i)) { 28462306a36Sopenharmony_ci qn->sum_avg[i] += avg_bw; 28562306a36Sopenharmony_ci qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci *agg_avg += avg_bw; 29062306a36Sopenharmony_ci *agg_peak = max_t(u32, *agg_peak, peak_bw); 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/** 29562306a36Sopenharmony_ci * qcom_icc_bus_aggregate - calculate bus clock rates by traversing all nodes 29662306a36Sopenharmony_ci * @provider: generic interconnect provider 29762306a36Sopenharmony_ci * @agg_clk_rate: array containing the aggregated clock rates in kHz 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_cistatic void qcom_icc_bus_aggregate(struct icc_provider *provider, u64 *agg_clk_rate) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci u64 agg_avg_rate, agg_rate; 30262306a36Sopenharmony_ci struct qcom_icc_node *qn; 30362306a36Sopenharmony_ci struct icc_node *node; 30462306a36Sopenharmony_ci int i; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* 30762306a36Sopenharmony_ci * Iterate nodes on the provider, aggregate bandwidth requests for 30862306a36Sopenharmony_ci * every bucket and convert them into bus clock rates. 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_ci list_for_each_entry(node, &provider->nodes, node_list) { 31162306a36Sopenharmony_ci qn = node->data; 31262306a36Sopenharmony_ci for (i = 0; i < QCOM_SMD_RPM_STATE_NUM; i++) { 31362306a36Sopenharmony_ci if (qn->channels) 31462306a36Sopenharmony_ci agg_avg_rate = div_u64(qn->sum_avg[i], qn->channels); 31562306a36Sopenharmony_ci else 31662306a36Sopenharmony_ci agg_avg_rate = qn->sum_avg[i]; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci agg_rate = max_t(u64, agg_avg_rate, qn->max_peak[i]); 31962306a36Sopenharmony_ci do_div(agg_rate, qn->buswidth); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci agg_clk_rate[i] = max_t(u64, agg_clk_rate[i], agg_rate); 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int qcom_icc_set(struct icc_node *src, struct icc_node *dst) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct qcom_icc_node *src_qn = NULL, *dst_qn = NULL; 32962306a36Sopenharmony_ci u64 agg_clk_rate[QCOM_SMD_RPM_STATE_NUM] = { 0 }; 33062306a36Sopenharmony_ci struct icc_provider *provider; 33162306a36Sopenharmony_ci struct qcom_icc_provider *qp; 33262306a36Sopenharmony_ci u64 active_rate, sleep_rate; 33362306a36Sopenharmony_ci int ret; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci src_qn = src->data; 33662306a36Sopenharmony_ci if (dst) 33762306a36Sopenharmony_ci dst_qn = dst->data; 33862306a36Sopenharmony_ci provider = src->provider; 33962306a36Sopenharmony_ci qp = to_qcom_provider(provider); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci qcom_icc_bus_aggregate(provider, agg_clk_rate); 34262306a36Sopenharmony_ci active_rate = agg_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE]; 34362306a36Sopenharmony_ci sleep_rate = agg_clk_rate[QCOM_SMD_RPM_SLEEP_STATE]; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci ret = qcom_icc_rpm_set(src_qn, src_qn->sum_avg); 34662306a36Sopenharmony_ci if (ret) 34762306a36Sopenharmony_ci return ret; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (dst_qn) { 35062306a36Sopenharmony_ci ret = qcom_icc_rpm_set(dst_qn, dst_qn->sum_avg); 35162306a36Sopenharmony_ci if (ret) 35262306a36Sopenharmony_ci return ret; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* Some providers don't have a bus clock to scale */ 35662306a36Sopenharmony_ci if (!qp->bus_clk_desc && !qp->bus_clk) 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * Downstream checks whether the requested rate is zero, but it makes little sense 36162306a36Sopenharmony_ci * to vote for a value that's below the lower threshold, so let's not do so. 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_ci if (qp->keep_alive) 36462306a36Sopenharmony_ci active_rate = max(ICC_BUS_CLK_MIN_RATE, active_rate); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Some providers have a non-RPM-owned bus clock - convert kHz->Hz for the CCF */ 36762306a36Sopenharmony_ci if (qp->bus_clk) { 36862306a36Sopenharmony_ci active_rate = max_t(u64, active_rate, sleep_rate); 36962306a36Sopenharmony_ci /* ARM32 caps clk_set_rate arg to u32.. Nothing we can do about that! */ 37062306a36Sopenharmony_ci active_rate = min_t(u64, 1000ULL * active_rate, ULONG_MAX); 37162306a36Sopenharmony_ci return clk_set_rate(qp->bus_clk, active_rate); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* RPM only accepts <=INT_MAX rates */ 37562306a36Sopenharmony_ci active_rate = min_t(u64, active_rate, INT_MAX); 37662306a36Sopenharmony_ci sleep_rate = min_t(u64, sleep_rate, INT_MAX); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (active_rate != qp->bus_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE]) { 37962306a36Sopenharmony_ci ret = qcom_icc_rpm_set_bus_rate(qp->bus_clk_desc, QCOM_SMD_RPM_ACTIVE_STATE, 38062306a36Sopenharmony_ci active_rate); 38162306a36Sopenharmony_ci if (ret) 38262306a36Sopenharmony_ci return ret; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Cache the rate after we've successfully commited it to RPM */ 38562306a36Sopenharmony_ci qp->bus_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE] = active_rate; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (sleep_rate != qp->bus_clk_rate[QCOM_SMD_RPM_SLEEP_STATE]) { 38962306a36Sopenharmony_ci ret = qcom_icc_rpm_set_bus_rate(qp->bus_clk_desc, QCOM_SMD_RPM_SLEEP_STATE, 39062306a36Sopenharmony_ci sleep_rate); 39162306a36Sopenharmony_ci if (ret) 39262306a36Sopenharmony_ci return ret; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* Cache the rate after we've successfully commited it to RPM */ 39562306a36Sopenharmony_ci qp->bus_clk_rate[QCOM_SMD_RPM_SLEEP_STATE] = sleep_rate; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ciint qnoc_probe(struct platform_device *pdev) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 40462306a36Sopenharmony_ci const struct qcom_icc_desc *desc; 40562306a36Sopenharmony_ci struct icc_onecell_data *data; 40662306a36Sopenharmony_ci struct icc_provider *provider; 40762306a36Sopenharmony_ci struct qcom_icc_node * const *qnodes; 40862306a36Sopenharmony_ci struct qcom_icc_provider *qp; 40962306a36Sopenharmony_ci struct icc_node *node; 41062306a36Sopenharmony_ci size_t num_nodes, i; 41162306a36Sopenharmony_ci const char * const *cds = NULL; 41262306a36Sopenharmony_ci int cd_num; 41362306a36Sopenharmony_ci int ret; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* wait for the RPM proxy */ 41662306a36Sopenharmony_ci if (!qcom_icc_rpm_smd_available()) 41762306a36Sopenharmony_ci return -EPROBE_DEFER; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci desc = of_device_get_match_data(dev); 42062306a36Sopenharmony_ci if (!desc) 42162306a36Sopenharmony_ci return -EINVAL; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci qnodes = desc->nodes; 42462306a36Sopenharmony_ci num_nodes = desc->num_nodes; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (desc->num_intf_clocks) { 42762306a36Sopenharmony_ci cds = desc->intf_clocks; 42862306a36Sopenharmony_ci cd_num = desc->num_intf_clocks; 42962306a36Sopenharmony_ci } else { 43062306a36Sopenharmony_ci /* 0 intf clocks is perfectly fine */ 43162306a36Sopenharmony_ci cd_num = 0; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); 43562306a36Sopenharmony_ci if (!qp) 43662306a36Sopenharmony_ci return -ENOMEM; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci qp->intf_clks = devm_kcalloc(dev, cd_num, sizeof(*qp->intf_clks), GFP_KERNEL); 43962306a36Sopenharmony_ci if (!qp->intf_clks) 44062306a36Sopenharmony_ci return -ENOMEM; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (desc->bus_clk_desc) { 44362306a36Sopenharmony_ci qp->bus_clk_desc = devm_kzalloc(dev, sizeof(*qp->bus_clk_desc), 44462306a36Sopenharmony_ci GFP_KERNEL); 44562306a36Sopenharmony_ci if (!qp->bus_clk_desc) 44662306a36Sopenharmony_ci return -ENOMEM; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci qp->bus_clk_desc = desc->bus_clk_desc; 44962306a36Sopenharmony_ci } else { 45062306a36Sopenharmony_ci /* Some older SoCs may have a single non-RPM-owned bus clock. */ 45162306a36Sopenharmony_ci qp->bus_clk = devm_clk_get_optional(dev, "bus"); 45262306a36Sopenharmony_ci if (IS_ERR(qp->bus_clk)) 45362306a36Sopenharmony_ci return PTR_ERR(qp->bus_clk); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), 45762306a36Sopenharmony_ci GFP_KERNEL); 45862306a36Sopenharmony_ci if (!data) 45962306a36Sopenharmony_ci return -ENOMEM; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci qp->num_intf_clks = cd_num; 46262306a36Sopenharmony_ci for (i = 0; i < cd_num; i++) 46362306a36Sopenharmony_ci qp->intf_clks[i].id = cds[i]; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci qp->keep_alive = desc->keep_alive; 46662306a36Sopenharmony_ci qp->type = desc->type; 46762306a36Sopenharmony_ci qp->qos_offset = desc->qos_offset; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (desc->regmap_cfg) { 47062306a36Sopenharmony_ci struct resource *res; 47162306a36Sopenharmony_ci void __iomem *mmio; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 47462306a36Sopenharmony_ci if (!res) { 47562306a36Sopenharmony_ci /* Try parent's regmap */ 47662306a36Sopenharmony_ci qp->regmap = dev_get_regmap(dev->parent, NULL); 47762306a36Sopenharmony_ci if (qp->regmap) 47862306a36Sopenharmony_ci goto regmap_done; 47962306a36Sopenharmony_ci return -ENODEV; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci mmio = devm_ioremap_resource(dev, res); 48362306a36Sopenharmony_ci if (IS_ERR(mmio)) 48462306a36Sopenharmony_ci return PTR_ERR(mmio); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci qp->regmap = devm_regmap_init_mmio(dev, mmio, desc->regmap_cfg); 48762306a36Sopenharmony_ci if (IS_ERR(qp->regmap)) { 48862306a36Sopenharmony_ci dev_err(dev, "Cannot regmap interconnect bus resource\n"); 48962306a36Sopenharmony_ci return PTR_ERR(qp->regmap); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ciregmap_done: 49462306a36Sopenharmony_ci ret = clk_prepare_enable(qp->bus_clk); 49562306a36Sopenharmony_ci if (ret) 49662306a36Sopenharmony_ci return ret; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci ret = devm_clk_bulk_get(dev, qp->num_intf_clks, qp->intf_clks); 49962306a36Sopenharmony_ci if (ret) 50062306a36Sopenharmony_ci goto err_disable_unprepare_clk; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci provider = &qp->provider; 50362306a36Sopenharmony_ci provider->dev = dev; 50462306a36Sopenharmony_ci provider->set = qcom_icc_set; 50562306a36Sopenharmony_ci provider->pre_aggregate = qcom_icc_pre_bw_aggregate; 50662306a36Sopenharmony_ci provider->aggregate = qcom_icc_bw_aggregate; 50762306a36Sopenharmony_ci provider->xlate_extended = qcom_icc_xlate_extended; 50862306a36Sopenharmony_ci provider->data = data; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci icc_provider_init(provider); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* If this fails, bus accesses will crash the platform! */ 51362306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(qp->num_intf_clks, qp->intf_clks); 51462306a36Sopenharmony_ci if (ret) 51562306a36Sopenharmony_ci goto err_disable_unprepare_clk; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci for (i = 0; i < num_nodes; i++) { 51862306a36Sopenharmony_ci size_t j; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci node = icc_node_create(qnodes[i]->id); 52162306a36Sopenharmony_ci if (IS_ERR(node)) { 52262306a36Sopenharmony_ci clk_bulk_disable_unprepare(qp->num_intf_clks, 52362306a36Sopenharmony_ci qp->intf_clks); 52462306a36Sopenharmony_ci ret = PTR_ERR(node); 52562306a36Sopenharmony_ci goto err_remove_nodes; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci node->name = qnodes[i]->name; 52962306a36Sopenharmony_ci node->data = qnodes[i]; 53062306a36Sopenharmony_ci icc_node_add(node, provider); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci for (j = 0; j < qnodes[i]->num_links; j++) 53362306a36Sopenharmony_ci icc_link_create(node, qnodes[i]->links[j]); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* Set QoS registers (we only need to do it once, generally) */ 53662306a36Sopenharmony_ci if (qnodes[i]->qos.ap_owned && 53762306a36Sopenharmony_ci qnodes[i]->qos.qos_mode != NOC_QOS_MODE_INVALID) { 53862306a36Sopenharmony_ci ret = qcom_icc_qos_set(node); 53962306a36Sopenharmony_ci if (ret) { 54062306a36Sopenharmony_ci clk_bulk_disable_unprepare(qp->num_intf_clks, 54162306a36Sopenharmony_ci qp->intf_clks); 54262306a36Sopenharmony_ci goto err_remove_nodes; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci data->nodes[i] = node; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci data->num_nodes = num_nodes; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci clk_bulk_disable_unprepare(qp->num_intf_clks, qp->intf_clks); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci ret = icc_provider_register(provider); 55362306a36Sopenharmony_ci if (ret) 55462306a36Sopenharmony_ci goto err_remove_nodes; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci platform_set_drvdata(pdev, qp); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* Populate child NoC devices if any */ 55962306a36Sopenharmony_ci if (of_get_child_count(dev->of_node) > 0) { 56062306a36Sopenharmony_ci ret = of_platform_populate(dev->of_node, NULL, NULL, dev); 56162306a36Sopenharmony_ci if (ret) 56262306a36Sopenharmony_ci goto err_deregister_provider; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cierr_deregister_provider: 56862306a36Sopenharmony_ci icc_provider_deregister(provider); 56962306a36Sopenharmony_cierr_remove_nodes: 57062306a36Sopenharmony_ci icc_nodes_remove(provider); 57162306a36Sopenharmony_cierr_disable_unprepare_clk: 57262306a36Sopenharmony_ci clk_disable_unprepare(qp->bus_clk); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return ret; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ciEXPORT_SYMBOL(qnoc_probe); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ciint qnoc_remove(struct platform_device *pdev) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct qcom_icc_provider *qp = platform_get_drvdata(pdev); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci icc_provider_deregister(&qp->provider); 58362306a36Sopenharmony_ci icc_nodes_remove(&qp->provider); 58462306a36Sopenharmony_ci clk_disable_unprepare(qp->bus_clk); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ciEXPORT_SYMBOL(qnoc_remove); 589