1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2017, Linaro Ltd
6 #include <linux/kernel.h>
7 #include <linux/module.h>
9 #include <linux/slab.h>
11 #include <linux/of_platform.h>
12 #include <linux/platform_device.h>
13 #include <linux/regmap.h>
14 #include <linux/mailbox_controller.h>
16 #define QCOM_APCS_IPC_BITS 32
18 struct qcom_apcs_ipc
{
19 struct mbox_controller mbox
;
20 struct mbox_chan mbox_chans
[QCOM_APCS_IPC_BITS
];
22 struct regmap
*regmap
;
24 struct platform_device
*clk
;
27 struct qcom_apcs_ipc_data
{
32 static const struct qcom_apcs_ipc_data ipq6018_apcs_data
= {
33 .offset
= 8, .clk_name
= "qcom,apss-ipq6018-clk"
36 static const struct qcom_apcs_ipc_data msm8916_apcs_data
= {
37 .offset
= 8, .clk_name
= "qcom-apcs-msm8916-clk"
40 static const struct qcom_apcs_ipc_data msm8994_apcs_data
= {
41 .offset
= 8, .clk_name
= NULL
44 static const struct qcom_apcs_ipc_data msm8996_apcs_data
= {
45 .offset
= 16, .clk_name
= "qcom-apcs-msm8996-clk"
48 static const struct qcom_apcs_ipc_data apps_shared_apcs_data
= {
49 .offset
= 12, .clk_name
= NULL
52 static const struct qcom_apcs_ipc_data sdx55_apcs_data
= {
53 .offset
= 0x1008, .clk_name
= "qcom-sdx55-acps-clk"
56 static const struct regmap_config apcs_regmap_config
= {
60 .max_register
= 0x1008,
64 static int qcom_apcs_ipc_send_data(struct mbox_chan
*chan
, void *data
)
66 struct qcom_apcs_ipc
*apcs
= container_of(chan
->mbox
,
67 struct qcom_apcs_ipc
, mbox
);
68 unsigned long idx
= (unsigned long)chan
->con_priv
;
70 return regmap_write(apcs
->regmap
, apcs
->offset
, BIT(idx
));
73 static const struct mbox_chan_ops qcom_apcs_ipc_ops
= {
74 .send_data
= qcom_apcs_ipc_send_data
,
77 static int qcom_apcs_ipc_probe(struct platform_device
*pdev
)
79 struct qcom_apcs_ipc
*apcs
;
80 const struct qcom_apcs_ipc_data
*apcs_data
;
81 struct regmap
*regmap
;
86 apcs
= devm_kzalloc(&pdev
->dev
, sizeof(*apcs
), GFP_KERNEL
);
90 base
= devm_platform_ioremap_resource(pdev
, 0);
94 regmap
= devm_regmap_init_mmio(&pdev
->dev
, base
, &apcs_regmap_config
);
96 return PTR_ERR(regmap
);
98 apcs_data
= of_device_get_match_data(&pdev
->dev
);
100 apcs
->regmap
= regmap
;
101 apcs
->offset
= apcs_data
->offset
;
103 /* Initialize channel identifiers */
104 for (i
= 0; i
< ARRAY_SIZE(apcs
->mbox_chans
); i
++)
105 apcs
->mbox_chans
[i
].con_priv
= (void *)i
;
107 apcs
->mbox
.dev
= &pdev
->dev
;
108 apcs
->mbox
.ops
= &qcom_apcs_ipc_ops
;
109 apcs
->mbox
.chans
= apcs
->mbox_chans
;
110 apcs
->mbox
.num_chans
= ARRAY_SIZE(apcs
->mbox_chans
);
112 ret
= devm_mbox_controller_register(&pdev
->dev
, &apcs
->mbox
);
114 dev_err(&pdev
->dev
, "failed to register APCS IPC controller\n");
118 if (apcs_data
->clk_name
) {
119 apcs
->clk
= platform_device_register_data(&pdev
->dev
,
123 if (IS_ERR(apcs
->clk
))
124 dev_err(&pdev
->dev
, "failed to register APCS clk\n");
127 platform_set_drvdata(pdev
, apcs
);
132 static void qcom_apcs_ipc_remove(struct platform_device
*pdev
)
134 struct qcom_apcs_ipc
*apcs
= platform_get_drvdata(pdev
);
135 struct platform_device
*clk
= apcs
->clk
;
137 platform_device_unregister(clk
);
140 /* .data is the offset of the ipc register within the global block */
141 static const struct of_device_id qcom_apcs_ipc_of_match
[] = {
142 { .compatible
= "qcom,ipq6018-apcs-apps-global", .data
= &ipq6018_apcs_data
},
143 { .compatible
= "qcom,msm8916-apcs-kpss-global", .data
= &msm8916_apcs_data
},
144 { .compatible
= "qcom,msm8939-apcs-kpss-global", .data
= &msm8916_apcs_data
},
145 { .compatible
= "qcom,msm8953-apcs-kpss-global", .data
= &msm8994_apcs_data
},
146 { .compatible
= "qcom,msm8994-apcs-kpss-global", .data
= &msm8994_apcs_data
},
147 { .compatible
= "qcom,msm8996-apcs-hmss-global", .data
= &msm8996_apcs_data
},
148 { .compatible
= "qcom,qcm2290-apcs-hmss-global", .data
= &msm8994_apcs_data
},
149 { .compatible
= "qcom,sdm845-apss-shared", .data
= &apps_shared_apcs_data
},
150 { .compatible
= "qcom,sdx55-apcs-gcc", .data
= &sdx55_apcs_data
},
151 /* Do not add any more entries using existing driver data */
152 { .compatible
= "qcom,msm8976-apcs-kpss-global", .data
= &msm8994_apcs_data
},
153 { .compatible
= "qcom,msm8998-apcs-hmss-global", .data
= &msm8994_apcs_data
},
154 { .compatible
= "qcom,qcs404-apcs-apps-global", .data
= &msm8916_apcs_data
},
155 { .compatible
= "qcom,sdm660-apcs-hmss-global", .data
= &msm8994_apcs_data
},
156 { .compatible
= "qcom,sm4250-apcs-hmss-global", .data
= &msm8994_apcs_data
},
157 { .compatible
= "qcom,sm6125-apcs-hmss-global", .data
= &msm8994_apcs_data
},
158 { .compatible
= "qcom,sm6115-apcs-hmss-global", .data
= &msm8994_apcs_data
},
159 { .compatible
= "qcom,ipq5332-apcs-apps-global", .data
= &ipq6018_apcs_data
},
160 { .compatible
= "qcom,ipq8074-apcs-apps-global", .data
= &ipq6018_apcs_data
},
161 { .compatible
= "qcom,sc7180-apss-shared", .data
= &apps_shared_apcs_data
},
162 { .compatible
= "qcom,sc8180x-apss-shared", .data
= &apps_shared_apcs_data
},
163 { .compatible
= "qcom,sm8150-apss-shared", .data
= &apps_shared_apcs_data
},
166 MODULE_DEVICE_TABLE(of
, qcom_apcs_ipc_of_match
);
168 static struct platform_driver qcom_apcs_ipc_driver
= {
169 .probe
= qcom_apcs_ipc_probe
,
170 .remove
= qcom_apcs_ipc_remove
,
172 .name
= "qcom_apcs_ipc",
173 .of_match_table
= qcom_apcs_ipc_of_match
,
177 static int __init
qcom_apcs_ipc_init(void)
179 return platform_driver_register(&qcom_apcs_ipc_driver
);
181 postcore_initcall(qcom_apcs_ipc_init
);
183 static void __exit
qcom_apcs_ipc_exit(void)
185 platform_driver_unregister(&qcom_apcs_ipc_driver
);
187 module_exit(qcom_apcs_ipc_exit
);
189 MODULE_LICENSE("GPL v2");
190 MODULE_DESCRIPTION("Qualcomm APCS IPC driver");