2 * Copyright (c) 2017, Linaro Ltd
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include <linux/kernel.h>
15 #include <linux/module.h>
17 #include <linux/slab.h>
19 #include <linux/of_platform.h>
20 #include <linux/platform_device.h>
21 #include <linux/regmap.h>
22 #include <linux/mailbox_controller.h>
24 #define QCOM_APCS_IPC_BITS 32
26 struct qcom_apcs_ipc
{
27 struct mbox_controller mbox
;
28 struct mbox_chan mbox_chans
[QCOM_APCS_IPC_BITS
];
30 struct regmap
*regmap
;
32 struct platform_device
*clk
;
35 static const struct regmap_config apcs_regmap_config
= {
39 .max_register
= 0xFFC,
43 static int qcom_apcs_ipc_send_data(struct mbox_chan
*chan
, void *data
)
45 struct qcom_apcs_ipc
*apcs
= container_of(chan
->mbox
,
46 struct qcom_apcs_ipc
, mbox
);
47 unsigned long idx
= (unsigned long)chan
->con_priv
;
49 return regmap_write(apcs
->regmap
, apcs
->offset
, BIT(idx
));
52 static const struct mbox_chan_ops qcom_apcs_ipc_ops
= {
53 .send_data
= qcom_apcs_ipc_send_data
,
56 static int qcom_apcs_ipc_probe(struct platform_device
*pdev
)
58 struct qcom_apcs_ipc
*apcs
;
59 struct regmap
*regmap
;
65 const struct of_device_id apcs_clk_match_table
[] = {
66 { .compatible
= "qcom,msm8916-apcs-kpss-global", },
67 { .compatible
= "qcom,qcs404-apcs-apps-global", },
71 apcs
= devm_kzalloc(&pdev
->dev
, sizeof(*apcs
), GFP_KERNEL
);
75 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
76 base
= devm_ioremap_resource(&pdev
->dev
, res
);
80 regmap
= devm_regmap_init_mmio(&pdev
->dev
, base
, &apcs_regmap_config
);
82 return PTR_ERR(regmap
);
84 offset
= (unsigned long)of_device_get_match_data(&pdev
->dev
);
86 apcs
->regmap
= regmap
;
87 apcs
->offset
= offset
;
89 /* Initialize channel identifiers */
90 for (i
= 0; i
< ARRAY_SIZE(apcs
->mbox_chans
); i
++)
91 apcs
->mbox_chans
[i
].con_priv
= (void *)i
;
93 apcs
->mbox
.dev
= &pdev
->dev
;
94 apcs
->mbox
.ops
= &qcom_apcs_ipc_ops
;
95 apcs
->mbox
.chans
= apcs
->mbox_chans
;
96 apcs
->mbox
.num_chans
= ARRAY_SIZE(apcs
->mbox_chans
);
98 ret
= mbox_controller_register(&apcs
->mbox
);
100 dev_err(&pdev
->dev
, "failed to register APCS IPC controller\n");
104 if (of_match_device(apcs_clk_match_table
, &pdev
->dev
)) {
105 apcs
->clk
= platform_device_register_data(&pdev
->dev
,
106 "qcom-apcs-msm8916-clk",
108 if (IS_ERR(apcs
->clk
))
109 dev_err(&pdev
->dev
, "failed to register APCS clk\n");
112 platform_set_drvdata(pdev
, apcs
);
117 static int qcom_apcs_ipc_remove(struct platform_device
*pdev
)
119 struct qcom_apcs_ipc
*apcs
= platform_get_drvdata(pdev
);
120 struct platform_device
*clk
= apcs
->clk
;
122 mbox_controller_unregister(&apcs
->mbox
);
123 platform_device_unregister(clk
);
128 /* .data is the offset of the ipc register within the global block */
129 static const struct of_device_id qcom_apcs_ipc_of_match
[] = {
130 { .compatible
= "qcom,msm8916-apcs-kpss-global", .data
= (void *)8 },
131 { .compatible
= "qcom,msm8996-apcs-hmss-global", .data
= (void *)16 },
132 { .compatible
= "qcom,msm8998-apcs-hmss-global", .data
= (void *)8 },
133 { .compatible
= "qcom,sdm845-apss-shared", .data
= (void *)12 },
136 MODULE_DEVICE_TABLE(of
, qcom_apcs_ipc_of_match
);
138 static struct platform_driver qcom_apcs_ipc_driver
= {
139 .probe
= qcom_apcs_ipc_probe
,
140 .remove
= qcom_apcs_ipc_remove
,
142 .name
= "qcom_apcs_ipc",
143 .of_match_table
= qcom_apcs_ipc_of_match
,
147 static int __init
qcom_apcs_ipc_init(void)
149 return platform_driver_register(&qcom_apcs_ipc_driver
);
151 postcore_initcall(qcom_apcs_ipc_init
);
153 static void __exit
qcom_apcs_ipc_exit(void)
155 platform_driver_unregister(&qcom_apcs_ipc_driver
);
157 module_exit(qcom_apcs_ipc_exit
);
159 MODULE_LICENSE("GPL v2");
160 MODULE_DESCRIPTION("Qualcomm APCS IPC driver");