1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright 2024 NXP |
4 | */ |
5 | |
6 | #include <linux/firmware/imx/sm.h> |
7 | #include <linux/module.h> |
8 | #include <linux/of.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/scmi_protocol.h> |
11 | #include <linux/scmi_imx_protocol.h> |
12 | |
13 | static const struct scmi_imx_misc_proto_ops *imx_misc_ctrl_ops; |
14 | static struct scmi_protocol_handle *ph; |
15 | struct notifier_block scmi_imx_misc_ctrl_nb; |
16 | |
17 | int scmi_imx_misc_ctrl_set(u32 id, u32 val) |
18 | { |
19 | if (!ph) |
20 | return -EPROBE_DEFER; |
21 | |
22 | return imx_misc_ctrl_ops->misc_ctrl_set(ph, id, 1, &val); |
23 | }; |
24 | EXPORT_SYMBOL(scmi_imx_misc_ctrl_set); |
25 | |
26 | int scmi_imx_misc_ctrl_get(u32 id, u32 *num, u32 *val) |
27 | { |
28 | if (!ph) |
29 | return -EPROBE_DEFER; |
30 | |
31 | return imx_misc_ctrl_ops->misc_ctrl_get(ph, id, num, val); |
32 | } |
33 | EXPORT_SYMBOL(scmi_imx_misc_ctrl_get); |
34 | |
35 | static int scmi_imx_misc_ctrl_notifier(struct notifier_block *nb, |
36 | unsigned long event, void *data) |
37 | { |
38 | /* |
39 | * notifier_chain_register requires a valid notifier_block and |
40 | * valid notifier_call. SCMI_EVENT_IMX_MISC_CONTROL is needed |
41 | * to let SCMI firmware enable control events, but the hook here |
42 | * is just a dummy function to avoid kernel panic as of now. |
43 | */ |
44 | return 0; |
45 | } |
46 | |
47 | static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev) |
48 | { |
49 | const struct scmi_handle *handle = sdev->handle; |
50 | struct device_node *np = sdev->dev.of_node; |
51 | u32 src_id, flags; |
52 | int ret, i, num; |
53 | |
54 | if (!handle) |
55 | return -ENODEV; |
56 | |
57 | if (imx_misc_ctrl_ops) { |
58 | dev_err(&sdev->dev, "misc ctrl already initialized\n" ); |
59 | return -EEXIST; |
60 | } |
61 | |
62 | imx_misc_ctrl_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_MISC, &ph); |
63 | if (IS_ERR(ptr: imx_misc_ctrl_ops)) |
64 | return PTR_ERR(ptr: imx_misc_ctrl_ops); |
65 | |
66 | num = of_property_count_u32_elems(np, propname: "nxp,ctrl-ids" ); |
67 | if (num % 2) { |
68 | dev_err(&sdev->dev, "Invalid wakeup-sources\n" ); |
69 | return -EINVAL; |
70 | } |
71 | |
72 | scmi_imx_misc_ctrl_nb.notifier_call = &scmi_imx_misc_ctrl_notifier; |
73 | for (i = 0; i < num; i += 2) { |
74 | ret = of_property_read_u32_index(np, propname: "nxp,ctrl-ids" , index: i, out_value: &src_id); |
75 | if (ret) { |
76 | dev_err(&sdev->dev, "Failed to read ctrl-id: %i\n" , i); |
77 | continue; |
78 | } |
79 | |
80 | ret = of_property_read_u32_index(np, propname: "nxp,ctrl-ids" , index: i + 1, out_value: &flags); |
81 | if (ret) { |
82 | dev_err(&sdev->dev, "Failed to read ctrl-id value: %d\n" , i + 1); |
83 | continue; |
84 | } |
85 | |
86 | ret = handle->notify_ops->devm_event_notifier_register(sdev, SCMI_PROTOCOL_IMX_MISC, |
87 | SCMI_EVENT_IMX_MISC_CONTROL, |
88 | &src_id, |
89 | &scmi_imx_misc_ctrl_nb); |
90 | if (ret) { |
91 | dev_err(&sdev->dev, "Failed to register scmi misc event: %d\n" , src_id); |
92 | } else { |
93 | ret = imx_misc_ctrl_ops->misc_ctrl_req_notify(ph, src_id, |
94 | SCMI_EVENT_IMX_MISC_CONTROL, |
95 | flags); |
96 | if (ret) |
97 | dev_err(&sdev->dev, "Failed to req notify: %d\n" , src_id); |
98 | } |
99 | } |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | static const struct scmi_device_id scmi_id_table[] = { |
105 | { SCMI_PROTOCOL_IMX_MISC, "imx-misc-ctrl" }, |
106 | { }, |
107 | }; |
108 | MODULE_DEVICE_TABLE(scmi, scmi_id_table); |
109 | |
110 | static struct scmi_driver scmi_imx_misc_ctrl_driver = { |
111 | .name = "scmi-imx-misc-ctrl" , |
112 | .probe = scmi_imx_misc_ctrl_probe, |
113 | .id_table = scmi_id_table, |
114 | }; |
115 | module_scmi_driver(scmi_imx_misc_ctrl_driver); |
116 | |
117 | MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>" ); |
118 | MODULE_DESCRIPTION("IMX SM MISC driver" ); |
119 | MODULE_LICENSE("GPL" ); |
120 | |