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