18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2017, Linaro Ltd
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/of.h>
118c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/regmap.h>
148c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define QCOM_APCS_IPC_BITS	32
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistruct qcom_apcs_ipc {
198c2ecf20Sopenharmony_ci	struct mbox_controller mbox;
208c2ecf20Sopenharmony_ci	struct mbox_chan mbox_chans[QCOM_APCS_IPC_BITS];
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	struct regmap *regmap;
238c2ecf20Sopenharmony_ci	unsigned long offset;
248c2ecf20Sopenharmony_ci	struct platform_device *clk;
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct qcom_apcs_ipc_data {
288c2ecf20Sopenharmony_ci	int offset;
298c2ecf20Sopenharmony_ci	char *clk_name;
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic const struct qcom_apcs_ipc_data ipq6018_apcs_data = {
338c2ecf20Sopenharmony_ci	.offset = 8, .clk_name = "qcom,apss-ipq6018-clk"
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic const struct qcom_apcs_ipc_data ipq8074_apcs_data = {
378c2ecf20Sopenharmony_ci	.offset = 8, .clk_name = NULL
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic const struct qcom_apcs_ipc_data msm8916_apcs_data = {
418c2ecf20Sopenharmony_ci	.offset = 8, .clk_name = "qcom-apcs-msm8916-clk"
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic const struct qcom_apcs_ipc_data msm8994_apcs_data = {
458c2ecf20Sopenharmony_ci	.offset = 8, .clk_name = NULL
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic const struct qcom_apcs_ipc_data msm8996_apcs_data = {
498c2ecf20Sopenharmony_ci	.offset = 16, .clk_name = NULL
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic const struct qcom_apcs_ipc_data msm8998_apcs_data = {
538c2ecf20Sopenharmony_ci	.offset = 8, .clk_name = NULL
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic const struct qcom_apcs_ipc_data sdm660_apcs_data = {
578c2ecf20Sopenharmony_ci	.offset = 8, .clk_name = NULL
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic const struct qcom_apcs_ipc_data apps_shared_apcs_data = {
618c2ecf20Sopenharmony_ci	.offset = 12, .clk_name = NULL
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic const struct regmap_config apcs_regmap_config = {
658c2ecf20Sopenharmony_ci	.reg_bits = 32,
668c2ecf20Sopenharmony_ci	.reg_stride = 4,
678c2ecf20Sopenharmony_ci	.val_bits = 32,
688c2ecf20Sopenharmony_ci	.max_register = 0xFFC,
698c2ecf20Sopenharmony_ci	.fast_io = true,
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int qcom_apcs_ipc_send_data(struct mbox_chan *chan, void *data)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct qcom_apcs_ipc *apcs = container_of(chan->mbox,
758c2ecf20Sopenharmony_ci						  struct qcom_apcs_ipc, mbox);
768c2ecf20Sopenharmony_ci	unsigned long idx = (unsigned long)chan->con_priv;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return regmap_write(apcs->regmap, apcs->offset, BIT(idx));
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic const struct mbox_chan_ops qcom_apcs_ipc_ops = {
828c2ecf20Sopenharmony_ci	.send_data = qcom_apcs_ipc_send_data,
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int qcom_apcs_ipc_probe(struct platform_device *pdev)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct qcom_apcs_ipc *apcs;
888c2ecf20Sopenharmony_ci	const struct qcom_apcs_ipc_data *apcs_data;
898c2ecf20Sopenharmony_ci	struct regmap *regmap;
908c2ecf20Sopenharmony_ci	struct resource *res;
918c2ecf20Sopenharmony_ci	void __iomem *base;
928c2ecf20Sopenharmony_ci	unsigned long i;
938c2ecf20Sopenharmony_ci	int ret;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	apcs = devm_kzalloc(&pdev->dev, sizeof(*apcs), GFP_KERNEL);
968c2ecf20Sopenharmony_ci	if (!apcs)
978c2ecf20Sopenharmony_ci		return -ENOMEM;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1008c2ecf20Sopenharmony_ci	base = devm_ioremap_resource(&pdev->dev, res);
1018c2ecf20Sopenharmony_ci	if (IS_ERR(base))
1028c2ecf20Sopenharmony_ci		return PTR_ERR(base);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	regmap = devm_regmap_init_mmio(&pdev->dev, base, &apcs_regmap_config);
1058c2ecf20Sopenharmony_ci	if (IS_ERR(regmap))
1068c2ecf20Sopenharmony_ci		return PTR_ERR(regmap);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	apcs_data = of_device_get_match_data(&pdev->dev);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	apcs->regmap = regmap;
1118c2ecf20Sopenharmony_ci	apcs->offset = apcs_data->offset;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/* Initialize channel identifiers */
1148c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(apcs->mbox_chans); i++)
1158c2ecf20Sopenharmony_ci		apcs->mbox_chans[i].con_priv = (void *)i;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	apcs->mbox.dev = &pdev->dev;
1188c2ecf20Sopenharmony_ci	apcs->mbox.ops = &qcom_apcs_ipc_ops;
1198c2ecf20Sopenharmony_ci	apcs->mbox.chans = apcs->mbox_chans;
1208c2ecf20Sopenharmony_ci	apcs->mbox.num_chans = ARRAY_SIZE(apcs->mbox_chans);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	ret = devm_mbox_controller_register(&pdev->dev, &apcs->mbox);
1238c2ecf20Sopenharmony_ci	if (ret) {
1248c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register APCS IPC controller\n");
1258c2ecf20Sopenharmony_ci		return ret;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (apcs_data->clk_name) {
1298c2ecf20Sopenharmony_ci		apcs->clk = platform_device_register_data(&pdev->dev,
1308c2ecf20Sopenharmony_ci							  apcs_data->clk_name,
1318c2ecf20Sopenharmony_ci							  PLATFORM_DEVID_AUTO,
1328c2ecf20Sopenharmony_ci							  NULL, 0);
1338c2ecf20Sopenharmony_ci		if (IS_ERR(apcs->clk))
1348c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "failed to register APCS clk\n");
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, apcs);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return 0;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int qcom_apcs_ipc_remove(struct platform_device *pdev)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct qcom_apcs_ipc *apcs = platform_get_drvdata(pdev);
1458c2ecf20Sopenharmony_ci	struct platform_device *clk = apcs->clk;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	platform_device_unregister(clk);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return 0;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/* .data is the offset of the ipc register within the global block */
1538c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_apcs_ipc_of_match[] = {
1548c2ecf20Sopenharmony_ci	{ .compatible = "qcom,ipq6018-apcs-apps-global", .data = &ipq6018_apcs_data },
1558c2ecf20Sopenharmony_ci	{ .compatible = "qcom,ipq8074-apcs-apps-global", .data = &ipq8074_apcs_data },
1568c2ecf20Sopenharmony_ci	{ .compatible = "qcom,msm8916-apcs-kpss-global", .data = &msm8916_apcs_data },
1578c2ecf20Sopenharmony_ci	{ .compatible = "qcom,msm8994-apcs-kpss-global", .data = &msm8994_apcs_data },
1588c2ecf20Sopenharmony_ci	{ .compatible = "qcom,msm8996-apcs-hmss-global", .data = &msm8996_apcs_data },
1598c2ecf20Sopenharmony_ci	{ .compatible = "qcom,msm8998-apcs-hmss-global", .data = &msm8998_apcs_data },
1608c2ecf20Sopenharmony_ci	{ .compatible = "qcom,qcs404-apcs-apps-global", .data = &msm8916_apcs_data },
1618c2ecf20Sopenharmony_ci	{ .compatible = "qcom,sc7180-apss-shared", .data = &apps_shared_apcs_data },
1628c2ecf20Sopenharmony_ci	{ .compatible = "qcom,sdm660-apcs-hmss-global", .data = &sdm660_apcs_data },
1638c2ecf20Sopenharmony_ci	{ .compatible = "qcom,sdm845-apss-shared", .data = &apps_shared_apcs_data },
1648c2ecf20Sopenharmony_ci	{ .compatible = "qcom,sm8150-apss-shared", .data = &apps_shared_apcs_data },
1658c2ecf20Sopenharmony_ci	{}
1668c2ecf20Sopenharmony_ci};
1678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_apcs_ipc_of_match);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic struct platform_driver qcom_apcs_ipc_driver = {
1708c2ecf20Sopenharmony_ci	.probe = qcom_apcs_ipc_probe,
1718c2ecf20Sopenharmony_ci	.remove = qcom_apcs_ipc_remove,
1728c2ecf20Sopenharmony_ci	.driver = {
1738c2ecf20Sopenharmony_ci		.name = "qcom_apcs_ipc",
1748c2ecf20Sopenharmony_ci		.of_match_table = qcom_apcs_ipc_of_match,
1758c2ecf20Sopenharmony_ci	},
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic int __init qcom_apcs_ipc_init(void)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	return platform_driver_register(&qcom_apcs_ipc_driver);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_cipostcore_initcall(qcom_apcs_ipc_init);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void __exit qcom_apcs_ipc_exit(void)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	platform_driver_unregister(&qcom_apcs_ipc_driver);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_cimodule_exit(qcom_apcs_ipc_exit);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm APCS IPC driver");
192