diff options
Diffstat (limited to 'drivers/remoteproc/mtk_scp.c')
| -rw-r--r-- | drivers/remoteproc/mtk_scp.c | 65 |
1 files changed, 59 insertions, 6 deletions
diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index 8206a1766481..db8fd045468d 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -16,6 +16,7 @@ #include <linux/remoteproc.h> #include <linux/remoteproc/mtk_scp.h> #include <linux/rpmsg/mtk_rpmsg.h> +#include <linux/string.h> #include "mtk_common.h" #include "remoteproc_internal.h" @@ -1093,22 +1094,74 @@ static void scp_remove_rpmsg_subdev(struct mtk_scp *scp) } } +/** + * scp_get_default_fw_path() - Get default SCP firmware path + * @dev: SCP Device + * @core_id: SCP Core number + * + * This function generates a path based on the following format: + * mediatek/(soc_model)/scp(_cX).img; for multi-core or + * mediatek/(soc_model)/scp.img for single core SCP HW + * + * Return: A devm allocated string containing the full path to + * a SCP firmware or an error pointer + */ +static const char *scp_get_default_fw_path(struct device *dev, int core_id) +{ + struct device_node *np = core_id < 0 ? dev->of_node : dev->parent->of_node; + const char *compatible, *soc; + char scp_fw_file[7]; + int ret; + + /* Use only the first compatible string */ + ret = of_property_read_string_index(np, "compatible", 0, &compatible); + if (ret) + return ERR_PTR(ret); + + /* If the compatible string's length is implausible bail out early */ + if (strlen(compatible) < strlen("mediatek,mtXXXX-scp")) + return ERR_PTR(-EINVAL); + + /* If the compatible string starts with "mediatek,mt" assume that it's ok */ + if (!str_has_prefix(compatible, "mediatek,mt")) + return ERR_PTR(-EINVAL); + + if (core_id >= 0) + ret = snprintf(scp_fw_file, sizeof(scp_fw_file), "scp_c%d", core_id); + else + ret = snprintf(scp_fw_file, sizeof(scp_fw_file), "scp"); + if (ret >= sizeof(scp_fw_file)) + return ERR_PTR(-ENAMETOOLONG); + + /* Not using strchr here, as strlen of a const gets optimized by compiler */ + soc = &compatible[strlen("mediatek,")]; + + return devm_kasprintf(dev, GFP_KERNEL, "mediatek/%.*s/%s.img", + (int)strlen("mtXXXX"), soc, scp_fw_file); +} + static struct mtk_scp *scp_rproc_init(struct platform_device *pdev, struct mtk_scp_of_cluster *scp_cluster, - const struct mtk_scp_of_data *of_data) + const struct mtk_scp_of_data *of_data, + int core_id) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct mtk_scp *scp; struct rproc *rproc; struct resource *res; - const char *fw_name = "scp.img"; + const char *fw_name; int ret, i; const struct mtk_scp_sizes_data *scp_sizes; ret = rproc_of_parse_firmware(dev, 0, &fw_name); - if (ret < 0 && ret != -EINVAL) - return ERR_PTR(ret); + if (ret) { + fw_name = scp_get_default_fw_path(dev, core_id); + if (IS_ERR(fw_name)) { + dev_err(dev, "Cannot get firmware path: %ld\n", PTR_ERR(fw_name)); + return ERR_CAST(fw_name); + } + } rproc = devm_rproc_alloc(dev, np->name, &scp_ops, fw_name, sizeof(*scp)); if (!rproc) { @@ -1212,7 +1265,7 @@ static int scp_add_single_core(struct platform_device *pdev, struct mtk_scp *scp; int ret; - scp = scp_rproc_init(pdev, scp_cluster, of_device_get_match_data(dev)); + scp = scp_rproc_init(pdev, scp_cluster, of_device_get_match_data(dev), -1); if (IS_ERR(scp)) return PTR_ERR(scp); @@ -1259,7 +1312,7 @@ static int scp_add_multi_core(struct platform_device *pdev, goto init_fail; } - scp = scp_rproc_init(cpdev, scp_cluster, cluster_of_data[core_id]); + scp = scp_rproc_init(cpdev, scp_cluster, cluster_of_data[core_id], core_id); put_device(&cpdev->dev); if (IS_ERR(scp)) { ret = PTR_ERR(scp); |
