1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * MediaTek External Memory Interface (EMI) Interconnect driver 4 * 5 * Copyright (c) 2021 MediaTek Inc. 6 * Copyright (c) 2024 Collabora Ltd. 7 * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> 8 */ 9 10 #include <linux/interconnect.h> 11 #include <linux/interconnect-provider.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 #include <linux/of_platform.h> 15 #include <linux/overflow.h> 16 #include <linux/platform_device.h> 17 #include <linux/soc/mediatek/dvfsrc.h> 18 19 #include "icc-emi.h" 20 21 static int mtk_emi_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, 22 u32 peak_bw, u32 *agg_avg, u32 *agg_peak) 23 { 24 struct mtk_icc_node *in = node->data; 25 26 if (check_add_overflow(*agg_avg, avg_bw, agg_avg)) 27 *agg_avg = U32_MAX; 28 29 *agg_peak = max_t(u32, *agg_peak, peak_bw); 30 31 in->sum_avg = *agg_avg; 32 in->max_peak = *agg_peak; 33 34 return 0; 35 } 36 37 static int mtk_emi_icc_set(struct icc_node *src, struct icc_node *dst) 38 { 39 struct mtk_icc_node *node = dst->data; 40 struct device *dev; 41 int ret; 42 43 if (unlikely(!src->provider)) 44 return -EINVAL; 45 46 dev = src->provider->dev->parent; 47 48 switch (node->ep) { 49 case 0: 50 break; 51 case 1: 52 ret = mtk_dvfsrc_send_request(dev, MTK_DVFSRC_CMD_PEAK_BW, node->max_peak); 53 if (ret) { 54 dev_err(dev, "Cannot send peak bw request: %d\n", ret); 55 return ret; 56 } 57 58 ret = mtk_dvfsrc_send_request(dev, MTK_DVFSRC_CMD_BW, node->sum_avg); 59 if (ret) { 60 dev_err(dev, "Cannot send bw request: %d\n", ret); 61 return ret; 62 } 63 break; 64 case 2: 65 ret = mtk_dvfsrc_send_request(dev, MTK_DVFSRC_CMD_HRT_BW, node->sum_avg); 66 if (ret) { 67 dev_err(dev, "Cannot send HRT bw request: %d\n", ret); 68 return ret; 69 } 70 break; 71 default: 72 dev_err(src->provider->dev, "Unknown endpoint %u\n", node->ep); 73 return -EINVAL; 74 } 75 76 return 0; 77 } 78 79 int mtk_emi_icc_probe(struct platform_device *pdev) 80 { 81 const struct mtk_icc_desc *desc; 82 struct device *dev = &pdev->dev; 83 struct icc_node *node; 84 struct icc_onecell_data *data; 85 struct icc_provider *provider; 86 struct mtk_icc_node **mnodes; 87 int i, j, ret; 88 89 desc = of_device_get_match_data(dev); 90 if (!desc) 91 return -EINVAL; 92 93 mnodes = desc->nodes; 94 95 provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL); 96 if (!provider) 97 return -ENOMEM; 98 99 data = devm_kzalloc(dev, struct_size(data, nodes, desc->num_nodes), GFP_KERNEL); 100 if (!data) 101 return -ENOMEM; 102 103 provider->dev = dev; 104 provider->set = mtk_emi_icc_set; 105 provider->aggregate = mtk_emi_icc_aggregate; 106 provider->xlate = of_icc_xlate_onecell; 107 INIT_LIST_HEAD(&provider->nodes); 108 provider->data = data; 109 110 for (i = 0; i < desc->num_nodes; i++) { 111 if (!mnodes[i]) 112 continue; 113 114 node = icc_node_create(mnodes[i]->id); 115 if (IS_ERR(node)) { 116 ret = PTR_ERR(node); 117 goto err; 118 } 119 120 node->name = mnodes[i]->name; 121 node->data = mnodes[i]; 122 icc_node_add(node, provider); 123 124 for (j = 0; j < mnodes[i]->num_links; j++) 125 icc_link_create(node, mnodes[i]->links[j]); 126 127 data->nodes[i] = node; 128 } 129 data->num_nodes = desc->num_nodes; 130 131 ret = icc_provider_register(provider); 132 if (ret) 133 goto err; 134 135 platform_set_drvdata(pdev, provider); 136 137 return 0; 138 err: 139 icc_nodes_remove(provider); 140 return ret; 141 } 142 EXPORT_SYMBOL_GPL(mtk_emi_icc_probe); 143 144 void mtk_emi_icc_remove(struct platform_device *pdev) 145 { 146 struct icc_provider *provider = platform_get_drvdata(pdev); 147 148 icc_provider_deregister(provider); 149 icc_nodes_remove(provider); 150 } 151 EXPORT_SYMBOL_GPL(mtk_emi_icc_remove); 152 153 MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>"); 154 MODULE_AUTHOR("Henry Chen <henryc.chen@mediatek.com>"); 155 MODULE_DESCRIPTION("MediaTek External Memory Interface interconnect driver"); 156 MODULE_LICENSE("GPL"); 157