1// SPDX-License-Identifier: GPL-2.0 2/* 3 * System Control and Management Interface (SCMI) Message Protocol bus layer 4 * 5 * Copyright (C) 2018 ARM Ltd. 6 */ 7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 10#include <linux/types.h> 11#include <linux/module.h> 12#include <linux/kernel.h> 13#include <linux/slab.h> 14#include <linux/device.h> 15 16#include "common.h" 17 18static DEFINE_IDA(scmi_bus_id); 19static DEFINE_IDR(scmi_protocols); 20static DEFINE_SPINLOCK(protocol_lock); 21 22static const struct scmi_device_id * 23scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv) 24{ 25 const struct scmi_device_id *id = scmi_drv->id_table; 26 27 if (!id) 28 return NULL; 29 30 for (; id->protocol_id; id++) 31 if (id->protocol_id == scmi_dev->protocol_id) { 32 if (!id->name) 33 return id; 34 else if (!strcmp(id->name, scmi_dev->name)) 35 return id; 36 } 37 38 return NULL; 39} 40 41static int scmi_dev_match(struct device *dev, struct device_driver *drv) 42{ 43 struct scmi_driver *scmi_drv = to_scmi_driver(drv); 44 struct scmi_device *scmi_dev = to_scmi_dev(dev); 45 const struct scmi_device_id *id; 46 47 id = scmi_dev_match_id(scmi_dev, scmi_drv); 48 if (id) 49 return 1; 50 51 return 0; 52} 53 54static int scmi_protocol_init(int protocol_id, struct scmi_handle *handle) 55{ 56 scmi_prot_init_fn_t fn = idr_find(&scmi_protocols, protocol_id); 57 58 if (unlikely(!fn)) 59 return -EINVAL; 60 return fn(handle); 61} 62 63static int scmi_protocol_dummy_init(struct scmi_handle *handle) 64{ 65 return 0; 66} 67 68static int scmi_dev_probe(struct device *dev) 69{ 70 struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver); 71 struct scmi_device *scmi_dev = to_scmi_dev(dev); 72 const struct scmi_device_id *id; 73 int ret; 74 75 id = scmi_dev_match_id(scmi_dev, scmi_drv); 76 if (!id) 77 return -ENODEV; 78 79 if (!scmi_dev->handle) 80 return -EPROBE_DEFER; 81 82 ret = scmi_protocol_init(scmi_dev->protocol_id, scmi_dev->handle); 83 if (ret) 84 return ret; 85 86 /* Skip protocol initialisation for additional devices */ 87 idr_replace(&scmi_protocols, &scmi_protocol_dummy_init, 88 scmi_dev->protocol_id); 89 90 return scmi_drv->probe(scmi_dev); 91} 92 93static int scmi_dev_remove(struct device *dev) 94{ 95 struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver); 96 struct scmi_device *scmi_dev = to_scmi_dev(dev); 97 98 if (scmi_drv->remove) 99 scmi_drv->remove(scmi_dev); 100 101 return 0; 102} 103 104static struct bus_type scmi_bus_type = { 105 .name = "scmi_protocol", 106 .match = scmi_dev_match, 107 .probe = scmi_dev_probe, 108 .remove = scmi_dev_remove, 109}; 110 111int scmi_driver_register(struct scmi_driver *driver, struct module *owner, 112 const char *mod_name) 113{ 114 int retval; 115 116 if (!driver->probe) 117 return -EINVAL; 118 119 driver->driver.bus = &scmi_bus_type; 120 driver->driver.name = driver->name; 121 driver->driver.owner = owner; 122 driver->driver.mod_name = mod_name; 123 124 retval = driver_register(&driver->driver); 125 if (!retval) 126 pr_debug("registered new scmi driver %s\n", driver->name); 127 128 return retval; 129} 130EXPORT_SYMBOL_GPL(scmi_driver_register); 131 132void scmi_driver_unregister(struct scmi_driver *driver) 133{ 134 driver_unregister(&driver->driver); 135} 136EXPORT_SYMBOL_GPL(scmi_driver_unregister); 137 138static void scmi_device_release(struct device *dev) 139{ 140 kfree(to_scmi_dev(dev)); 141} 142 143struct scmi_device * 144scmi_device_create(struct device_node *np, struct device *parent, int protocol, 145 const char *name) 146{ 147 int id, retval; 148 struct scmi_device *scmi_dev; 149 150 scmi_dev = kzalloc(sizeof(*scmi_dev), GFP_KERNEL); 151 if (!scmi_dev) 152 return NULL; 153 154 scmi_dev->name = kstrdup_const(name ?: "unknown", GFP_KERNEL); 155 if (!scmi_dev->name) { 156 kfree(scmi_dev); 157 return NULL; 158 } 159 160 id = ida_simple_get(&scmi_bus_id, 1, 0, GFP_KERNEL); 161 if (id < 0) { 162 kfree_const(scmi_dev->name); 163 kfree(scmi_dev); 164 return NULL; 165 } 166 167 scmi_dev->id = id; 168 scmi_dev->protocol_id = protocol; 169 scmi_dev->dev.parent = parent; 170 scmi_dev->dev.of_node = np; 171 scmi_dev->dev.bus = &scmi_bus_type; 172 scmi_dev->dev.release = scmi_device_release; 173 dev_set_name(&scmi_dev->dev, "scmi_dev.%d", id); 174 175 retval = device_register(&scmi_dev->dev); 176 if (retval) 177 goto put_dev; 178 179 return scmi_dev; 180put_dev: 181 kfree_const(scmi_dev->name); 182 put_device(&scmi_dev->dev); 183 ida_simple_remove(&scmi_bus_id, id); 184 return NULL; 185} 186 187void scmi_device_destroy(struct scmi_device *scmi_dev) 188{ 189 kfree_const(scmi_dev->name); 190 scmi_handle_put(scmi_dev->handle); 191 ida_simple_remove(&scmi_bus_id, scmi_dev->id); 192 device_unregister(&scmi_dev->dev); 193} 194 195void scmi_set_handle(struct scmi_device *scmi_dev) 196{ 197 scmi_dev->handle = scmi_handle_get(&scmi_dev->dev); 198} 199 200int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn) 201{ 202 int ret; 203 204 spin_lock(&protocol_lock); 205 ret = idr_alloc(&scmi_protocols, fn, protocol_id, protocol_id + 1, 206 GFP_ATOMIC); 207 spin_unlock(&protocol_lock); 208 if (ret != protocol_id) 209 pr_err("unable to allocate SCMI idr slot, err %d\n", ret); 210 211 return ret; 212} 213EXPORT_SYMBOL_GPL(scmi_protocol_register); 214 215void scmi_protocol_unregister(int protocol_id) 216{ 217 spin_lock(&protocol_lock); 218 idr_remove(&scmi_protocols, protocol_id); 219 spin_unlock(&protocol_lock); 220} 221EXPORT_SYMBOL_GPL(scmi_protocol_unregister); 222 223static int __scmi_devices_unregister(struct device *dev, void *data) 224{ 225 struct scmi_device *scmi_dev = to_scmi_dev(dev); 226 227 scmi_device_destroy(scmi_dev); 228 return 0; 229} 230 231static void scmi_devices_unregister(void) 232{ 233 bus_for_each_dev(&scmi_bus_type, NULL, NULL, __scmi_devices_unregister); 234} 235 236int __init scmi_bus_init(void) 237{ 238 int retval; 239 240 retval = bus_register(&scmi_bus_type); 241 if (retval) 242 pr_err("scmi protocol bus register failed (%d)\n", retval); 243 244 return retval; 245} 246 247void __exit scmi_bus_exit(void) 248{ 249 scmi_devices_unregister(); 250 bus_unregister(&scmi_bus_type); 251 ida_destroy(&scmi_bus_id); 252} 253