1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2024 NXP 4 */ 5 6 #include <linux/debugfs.h> 7 #include <linux/device/devres.h> 8 #include <linux/firmware/imx/sm.h> 9 #include <linux/module.h> 10 #include <linux/of.h> 11 #include <linux/platform_device.h> 12 #include <linux/scmi_protocol.h> 13 #include <linux/scmi_imx_protocol.h> 14 #include <linux/seq_file.h> 15 #include <linux/sizes.h> 16 17 static const struct scmi_imx_misc_proto_ops *imx_misc_ctrl_ops; 18 static struct scmi_protocol_handle *ph; 19 struct notifier_block scmi_imx_misc_ctrl_nb; 20 21 int scmi_imx_misc_ctrl_set(u32 id, u32 val) 22 { 23 if (!ph) 24 return -EPROBE_DEFER; 25 26 return imx_misc_ctrl_ops->misc_ctrl_set(ph, id, 1, &val); 27 }; 28 EXPORT_SYMBOL(scmi_imx_misc_ctrl_set); 29 30 int scmi_imx_misc_ctrl_get(u32 id, u32 *num, u32 *val) 31 { 32 if (!ph) 33 return -EPROBE_DEFER; 34 35 return imx_misc_ctrl_ops->misc_ctrl_get(ph, id, num, val); 36 } 37 EXPORT_SYMBOL(scmi_imx_misc_ctrl_get); 38 39 static int scmi_imx_misc_ctrl_notifier(struct notifier_block *nb, 40 unsigned long event, void *data) 41 { 42 /* 43 * notifier_chain_register requires a valid notifier_block and 44 * valid notifier_call. SCMI_EVENT_IMX_MISC_CONTROL is needed 45 * to let SCMI firmware enable control events, but the hook here 46 * is just a dummy function to avoid kernel panic as of now. 47 */ 48 return 0; 49 } 50 51 static int syslog_show(struct seq_file *file, void *priv) 52 { 53 /* 4KB is large enough for syslog */ 54 void *syslog __free(kfree) = kmalloc(SZ_4K, GFP_KERNEL); 55 /* syslog API use num words, not num bytes */ 56 u16 size = SZ_4K / 4; 57 int ret; 58 59 if (!ph) 60 return -ENODEV; 61 62 ret = imx_misc_ctrl_ops->misc_syslog(ph, &size, syslog); 63 if (ret) 64 return ret; 65 66 seq_hex_dump(file, " ", DUMP_PREFIX_NONE, 16, sizeof(u32), syslog, size * 4, false); 67 seq_putc(file, '\n'); 68 69 return 0; 70 } 71 DEFINE_SHOW_ATTRIBUTE(syslog); 72 73 static void scmi_imx_misc_put(void *p) 74 { 75 debugfs_remove((struct dentry *)p); 76 } 77 78 static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev) 79 { 80 const struct scmi_handle *handle = sdev->handle; 81 struct device_node *np = sdev->dev.of_node; 82 struct dentry *scmi_imx_dentry; 83 u32 src_id, flags; 84 int ret, i, num; 85 86 if (!handle) 87 return -ENODEV; 88 89 if (imx_misc_ctrl_ops) { 90 dev_err(&sdev->dev, "misc ctrl already initialized\n"); 91 return -EEXIST; 92 } 93 94 imx_misc_ctrl_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_MISC, &ph); 95 if (IS_ERR(imx_misc_ctrl_ops)) 96 return PTR_ERR(imx_misc_ctrl_ops); 97 98 num = of_property_count_u32_elems(np, "nxp,ctrl-ids"); 99 if (num % 2) { 100 dev_err(&sdev->dev, "Invalid wakeup-sources\n"); 101 return -EINVAL; 102 } 103 104 scmi_imx_misc_ctrl_nb.notifier_call = &scmi_imx_misc_ctrl_notifier; 105 for (i = 0; i < num; i += 2) { 106 ret = of_property_read_u32_index(np, "nxp,ctrl-ids", i, &src_id); 107 if (ret) { 108 dev_err(&sdev->dev, "Failed to read ctrl-id: %i\n", i); 109 continue; 110 } 111 112 ret = of_property_read_u32_index(np, "nxp,ctrl-ids", i + 1, &flags); 113 if (ret) { 114 dev_err(&sdev->dev, "Failed to read ctrl-id value: %d\n", i + 1); 115 continue; 116 } 117 118 ret = handle->notify_ops->devm_event_notifier_register(sdev, SCMI_PROTOCOL_IMX_MISC, 119 SCMI_EVENT_IMX_MISC_CONTROL, 120 &src_id, 121 &scmi_imx_misc_ctrl_nb); 122 if (ret) { 123 dev_err(&sdev->dev, "Failed to register scmi misc event: %d\n", src_id); 124 } else { 125 ret = imx_misc_ctrl_ops->misc_ctrl_req_notify(ph, src_id, 126 SCMI_EVENT_IMX_MISC_CONTROL, 127 flags); 128 if (ret) 129 dev_err(&sdev->dev, "Failed to req notify: %d\n", src_id); 130 } 131 } 132 133 scmi_imx_dentry = debugfs_create_dir("scmi_imx", NULL); 134 debugfs_create_file("syslog", 0444, scmi_imx_dentry, &sdev->dev, &syslog_fops); 135 136 return devm_add_action_or_reset(&sdev->dev, scmi_imx_misc_put, scmi_imx_dentry); 137 } 138 139 static const struct scmi_device_id scmi_id_table[] = { 140 { SCMI_PROTOCOL_IMX_MISC, "imx-misc-ctrl" }, 141 { }, 142 }; 143 MODULE_DEVICE_TABLE(scmi, scmi_id_table); 144 145 static struct scmi_driver scmi_imx_misc_ctrl_driver = { 146 .name = "scmi-imx-misc-ctrl", 147 .probe = scmi_imx_misc_ctrl_probe, 148 .id_table = scmi_id_table, 149 }; 150 module_scmi_driver(scmi_imx_misc_ctrl_driver); 151 152 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); 153 MODULE_DESCRIPTION("IMX SM MISC driver"); 154 MODULE_LICENSE("GPL"); 155