1eb0e90a8SMaximilian Luz // SPDX-License-Identifier: GPL-2.0+ 2eb0e90a8SMaximilian Luz /* 3eb0e90a8SMaximilian Luz * Surface System Aggregator Module bus and device integration. 4eb0e90a8SMaximilian Luz * 5221756e6SMaximilian Luz * Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com> 6eb0e90a8SMaximilian Luz */ 7eb0e90a8SMaximilian Luz 8eb0e90a8SMaximilian Luz #include <linux/device.h> 94a4ab610SMaximilian Luz #include <linux/property.h> 10eb0e90a8SMaximilian Luz #include <linux/slab.h> 11eb0e90a8SMaximilian Luz 12eb0e90a8SMaximilian Luz #include <linux/surface_aggregator/controller.h> 13eb0e90a8SMaximilian Luz #include <linux/surface_aggregator/device.h> 14eb0e90a8SMaximilian Luz 15eb0e90a8SMaximilian Luz #include "bus.h" 16eb0e90a8SMaximilian Luz #include "controller.h" 17eb0e90a8SMaximilian Luz 184a4ab610SMaximilian Luz 194a4ab610SMaximilian Luz /* -- Device and bus functions. --------------------------------------------- */ 204a4ab610SMaximilian Luz 21eb0e90a8SMaximilian Luz static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 22eb0e90a8SMaximilian Luz char *buf) 23eb0e90a8SMaximilian Luz { 24eb0e90a8SMaximilian Luz struct ssam_device *sdev = to_ssam_device(dev); 25eb0e90a8SMaximilian Luz 26eb0e90a8SMaximilian Luz return sysfs_emit(buf, "ssam:d%02Xc%02Xt%02Xi%02Xf%02X\n", 27eb0e90a8SMaximilian Luz sdev->uid.domain, sdev->uid.category, sdev->uid.target, 28eb0e90a8SMaximilian Luz sdev->uid.instance, sdev->uid.function); 29eb0e90a8SMaximilian Luz } 30eb0e90a8SMaximilian Luz static DEVICE_ATTR_RO(modalias); 31eb0e90a8SMaximilian Luz 32eb0e90a8SMaximilian Luz static struct attribute *ssam_device_attrs[] = { 33eb0e90a8SMaximilian Luz &dev_attr_modalias.attr, 34eb0e90a8SMaximilian Luz NULL, 35eb0e90a8SMaximilian Luz }; 36eb0e90a8SMaximilian Luz ATTRIBUTE_GROUPS(ssam_device); 37eb0e90a8SMaximilian Luz 38eb0e90a8SMaximilian Luz static int ssam_device_uevent(struct device *dev, struct kobj_uevent_env *env) 39eb0e90a8SMaximilian Luz { 40eb0e90a8SMaximilian Luz struct ssam_device *sdev = to_ssam_device(dev); 41eb0e90a8SMaximilian Luz 42eb0e90a8SMaximilian Luz return add_uevent_var(env, "MODALIAS=ssam:d%02Xc%02Xt%02Xi%02Xf%02X", 43eb0e90a8SMaximilian Luz sdev->uid.domain, sdev->uid.category, 44eb0e90a8SMaximilian Luz sdev->uid.target, sdev->uid.instance, 45eb0e90a8SMaximilian Luz sdev->uid.function); 46eb0e90a8SMaximilian Luz } 47eb0e90a8SMaximilian Luz 48eb0e90a8SMaximilian Luz static void ssam_device_release(struct device *dev) 49eb0e90a8SMaximilian Luz { 50eb0e90a8SMaximilian Luz struct ssam_device *sdev = to_ssam_device(dev); 51eb0e90a8SMaximilian Luz 52eb0e90a8SMaximilian Luz ssam_controller_put(sdev->ctrl); 534a4ab610SMaximilian Luz fwnode_handle_put(sdev->dev.fwnode); 54eb0e90a8SMaximilian Luz kfree(sdev); 55eb0e90a8SMaximilian Luz } 56eb0e90a8SMaximilian Luz 57eb0e90a8SMaximilian Luz const struct device_type ssam_device_type = { 58eb0e90a8SMaximilian Luz .name = "surface_aggregator_device", 59eb0e90a8SMaximilian Luz .groups = ssam_device_groups, 60eb0e90a8SMaximilian Luz .uevent = ssam_device_uevent, 61eb0e90a8SMaximilian Luz .release = ssam_device_release, 62eb0e90a8SMaximilian Luz }; 63eb0e90a8SMaximilian Luz EXPORT_SYMBOL_GPL(ssam_device_type); 64eb0e90a8SMaximilian Luz 65eb0e90a8SMaximilian Luz /** 66eb0e90a8SMaximilian Luz * ssam_device_alloc() - Allocate and initialize a SSAM client device. 67eb0e90a8SMaximilian Luz * @ctrl: The controller under which the device should be added. 68eb0e90a8SMaximilian Luz * @uid: The UID of the device to be added. 69eb0e90a8SMaximilian Luz * 70eb0e90a8SMaximilian Luz * Allocates and initializes a new client device. The parent of the device 71eb0e90a8SMaximilian Luz * will be set to the controller device and the name will be set based on the 72eb0e90a8SMaximilian Luz * UID. Note that the device still has to be added via ssam_device_add(). 73eb0e90a8SMaximilian Luz * Refer to that function for more details. 74eb0e90a8SMaximilian Luz * 75eb0e90a8SMaximilian Luz * Return: Returns the newly allocated and initialized SSAM client device, or 76eb0e90a8SMaximilian Luz * %NULL if it could not be allocated. 77eb0e90a8SMaximilian Luz */ 78eb0e90a8SMaximilian Luz struct ssam_device *ssam_device_alloc(struct ssam_controller *ctrl, 79eb0e90a8SMaximilian Luz struct ssam_device_uid uid) 80eb0e90a8SMaximilian Luz { 81eb0e90a8SMaximilian Luz struct ssam_device *sdev; 82eb0e90a8SMaximilian Luz 83eb0e90a8SMaximilian Luz sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); 84eb0e90a8SMaximilian Luz if (!sdev) 85eb0e90a8SMaximilian Luz return NULL; 86eb0e90a8SMaximilian Luz 87eb0e90a8SMaximilian Luz device_initialize(&sdev->dev); 88eb0e90a8SMaximilian Luz sdev->dev.bus = &ssam_bus_type; 89eb0e90a8SMaximilian Luz sdev->dev.type = &ssam_device_type; 90eb0e90a8SMaximilian Luz sdev->dev.parent = ssam_controller_device(ctrl); 91eb0e90a8SMaximilian Luz sdev->ctrl = ssam_controller_get(ctrl); 92eb0e90a8SMaximilian Luz sdev->uid = uid; 93eb0e90a8SMaximilian Luz 94eb0e90a8SMaximilian Luz dev_set_name(&sdev->dev, "%02x:%02x:%02x:%02x:%02x", 95eb0e90a8SMaximilian Luz sdev->uid.domain, sdev->uid.category, sdev->uid.target, 96eb0e90a8SMaximilian Luz sdev->uid.instance, sdev->uid.function); 97eb0e90a8SMaximilian Luz 98eb0e90a8SMaximilian Luz return sdev; 99eb0e90a8SMaximilian Luz } 100eb0e90a8SMaximilian Luz EXPORT_SYMBOL_GPL(ssam_device_alloc); 101eb0e90a8SMaximilian Luz 102eb0e90a8SMaximilian Luz /** 103eb0e90a8SMaximilian Luz * ssam_device_add() - Add a SSAM client device. 104eb0e90a8SMaximilian Luz * @sdev: The SSAM client device to be added. 105eb0e90a8SMaximilian Luz * 106eb0e90a8SMaximilian Luz * Added client devices must be guaranteed to always have a valid and active 107eb0e90a8SMaximilian Luz * controller. Thus, this function will fail with %-ENODEV if the controller 108eb0e90a8SMaximilian Luz * of the device has not been initialized yet, has been suspended, or has been 109eb0e90a8SMaximilian Luz * shut down. 110eb0e90a8SMaximilian Luz * 111eb0e90a8SMaximilian Luz * The caller of this function should ensure that the corresponding call to 112eb0e90a8SMaximilian Luz * ssam_device_remove() is issued before the controller is shut down. If the 113eb0e90a8SMaximilian Luz * added device is a direct child of the controller device (default), it will 114eb0e90a8SMaximilian Luz * be automatically removed when the controller is shut down. 115eb0e90a8SMaximilian Luz * 116eb0e90a8SMaximilian Luz * By default, the controller device will become the parent of the newly 117eb0e90a8SMaximilian Luz * created client device. The parent may be changed before ssam_device_add is 118eb0e90a8SMaximilian Luz * called, but care must be taken that a) the correct suspend/resume ordering 119eb0e90a8SMaximilian Luz * is guaranteed and b) the client device does not outlive the controller, 120eb0e90a8SMaximilian Luz * i.e. that the device is removed before the controller is being shut down. 121eb0e90a8SMaximilian Luz * In case these guarantees have to be manually enforced, please refer to the 122eb0e90a8SMaximilian Luz * ssam_client_link() and ssam_client_bind() functions, which are intended to 123eb0e90a8SMaximilian Luz * set up device-links for this purpose. 124eb0e90a8SMaximilian Luz * 125eb0e90a8SMaximilian Luz * Return: Returns zero on success, a negative error code on failure. 126eb0e90a8SMaximilian Luz */ 127eb0e90a8SMaximilian Luz int ssam_device_add(struct ssam_device *sdev) 128eb0e90a8SMaximilian Luz { 129eb0e90a8SMaximilian Luz int status; 130eb0e90a8SMaximilian Luz 131eb0e90a8SMaximilian Luz /* 132eb0e90a8SMaximilian Luz * Ensure that we can only add new devices to a controller if it has 133eb0e90a8SMaximilian Luz * been started and is not going away soon. This works in combination 134eb0e90a8SMaximilian Luz * with ssam_controller_remove_clients to ensure driver presence for the 135eb0e90a8SMaximilian Luz * controller device, i.e. it ensures that the controller (sdev->ctrl) 136eb0e90a8SMaximilian Luz * is always valid and can be used for requests as long as the client 137eb0e90a8SMaximilian Luz * device we add here is registered as child under it. This essentially 138eb0e90a8SMaximilian Luz * guarantees that the client driver can always expect the preconditions 139*b09ee1cdSMaximilian Luz * for functions like ssam_request_do_sync() (controller has to be 140*b09ee1cdSMaximilian Luz * started and is not suspended) to hold and thus does not have to check 141*b09ee1cdSMaximilian Luz * for them. 142eb0e90a8SMaximilian Luz * 143eb0e90a8SMaximilian Luz * Note that for this to work, the controller has to be a parent device. 144eb0e90a8SMaximilian Luz * If it is not a direct parent, care has to be taken that the device is 145eb0e90a8SMaximilian Luz * removed via ssam_device_remove(), as device_unregister does not 146eb0e90a8SMaximilian Luz * remove child devices recursively. 147eb0e90a8SMaximilian Luz */ 148eb0e90a8SMaximilian Luz ssam_controller_statelock(sdev->ctrl); 149eb0e90a8SMaximilian Luz 150eb0e90a8SMaximilian Luz if (sdev->ctrl->state != SSAM_CONTROLLER_STARTED) { 151eb0e90a8SMaximilian Luz ssam_controller_stateunlock(sdev->ctrl); 152eb0e90a8SMaximilian Luz return -ENODEV; 153eb0e90a8SMaximilian Luz } 154eb0e90a8SMaximilian Luz 155eb0e90a8SMaximilian Luz status = device_add(&sdev->dev); 156eb0e90a8SMaximilian Luz 157eb0e90a8SMaximilian Luz ssam_controller_stateunlock(sdev->ctrl); 158eb0e90a8SMaximilian Luz return status; 159eb0e90a8SMaximilian Luz } 160eb0e90a8SMaximilian Luz EXPORT_SYMBOL_GPL(ssam_device_add); 161eb0e90a8SMaximilian Luz 162eb0e90a8SMaximilian Luz /** 163eb0e90a8SMaximilian Luz * ssam_device_remove() - Remove a SSAM client device. 164eb0e90a8SMaximilian Luz * @sdev: The device to remove. 165eb0e90a8SMaximilian Luz * 166eb0e90a8SMaximilian Luz * Removes and unregisters the provided SSAM client device. 167eb0e90a8SMaximilian Luz */ 168eb0e90a8SMaximilian Luz void ssam_device_remove(struct ssam_device *sdev) 169eb0e90a8SMaximilian Luz { 170eb0e90a8SMaximilian Luz device_unregister(&sdev->dev); 171eb0e90a8SMaximilian Luz } 172eb0e90a8SMaximilian Luz EXPORT_SYMBOL_GPL(ssam_device_remove); 173eb0e90a8SMaximilian Luz 174eb0e90a8SMaximilian Luz /** 175eb0e90a8SMaximilian Luz * ssam_device_id_compatible() - Check if a device ID matches a UID. 176eb0e90a8SMaximilian Luz * @id: The device ID as potential match. 177eb0e90a8SMaximilian Luz * @uid: The device UID matching against. 178eb0e90a8SMaximilian Luz * 179eb0e90a8SMaximilian Luz * Check if the given ID is a match for the given UID, i.e. if a device with 180eb0e90a8SMaximilian Luz * the provided UID is compatible to the given ID following the match rules 181eb0e90a8SMaximilian Luz * described in its &ssam_device_id.match_flags member. 182eb0e90a8SMaximilian Luz * 183eb0e90a8SMaximilian Luz * Return: Returns %true if the given UID is compatible to the match rule 184eb0e90a8SMaximilian Luz * described by the given ID, %false otherwise. 185eb0e90a8SMaximilian Luz */ 186eb0e90a8SMaximilian Luz static bool ssam_device_id_compatible(const struct ssam_device_id *id, 187eb0e90a8SMaximilian Luz struct ssam_device_uid uid) 188eb0e90a8SMaximilian Luz { 189eb0e90a8SMaximilian Luz if (id->domain != uid.domain || id->category != uid.category) 190eb0e90a8SMaximilian Luz return false; 191eb0e90a8SMaximilian Luz 192eb0e90a8SMaximilian Luz if ((id->match_flags & SSAM_MATCH_TARGET) && id->target != uid.target) 193eb0e90a8SMaximilian Luz return false; 194eb0e90a8SMaximilian Luz 195eb0e90a8SMaximilian Luz if ((id->match_flags & SSAM_MATCH_INSTANCE) && id->instance != uid.instance) 196eb0e90a8SMaximilian Luz return false; 197eb0e90a8SMaximilian Luz 198eb0e90a8SMaximilian Luz if ((id->match_flags & SSAM_MATCH_FUNCTION) && id->function != uid.function) 199eb0e90a8SMaximilian Luz return false; 200eb0e90a8SMaximilian Luz 201eb0e90a8SMaximilian Luz return true; 202eb0e90a8SMaximilian Luz } 203eb0e90a8SMaximilian Luz 204eb0e90a8SMaximilian Luz /** 205eb0e90a8SMaximilian Luz * ssam_device_id_is_null() - Check if a device ID is null. 206eb0e90a8SMaximilian Luz * @id: The device ID to check. 207eb0e90a8SMaximilian Luz * 208eb0e90a8SMaximilian Luz * Check if a given device ID is null, i.e. all zeros. Used to check for the 209eb0e90a8SMaximilian Luz * end of ``MODULE_DEVICE_TABLE(ssam, ...)`` or similar lists. 210eb0e90a8SMaximilian Luz * 211eb0e90a8SMaximilian Luz * Return: Returns %true if the given ID represents a null ID, %false 212eb0e90a8SMaximilian Luz * otherwise. 213eb0e90a8SMaximilian Luz */ 214eb0e90a8SMaximilian Luz static bool ssam_device_id_is_null(const struct ssam_device_id *id) 215eb0e90a8SMaximilian Luz { 216eb0e90a8SMaximilian Luz return id->match_flags == 0 && 217eb0e90a8SMaximilian Luz id->domain == 0 && 218eb0e90a8SMaximilian Luz id->category == 0 && 219eb0e90a8SMaximilian Luz id->target == 0 && 220eb0e90a8SMaximilian Luz id->instance == 0 && 221eb0e90a8SMaximilian Luz id->function == 0 && 222eb0e90a8SMaximilian Luz id->driver_data == 0; 223eb0e90a8SMaximilian Luz } 224eb0e90a8SMaximilian Luz 225eb0e90a8SMaximilian Luz /** 226eb0e90a8SMaximilian Luz * ssam_device_id_match() - Find the matching ID table entry for the given UID. 227eb0e90a8SMaximilian Luz * @table: The table to search in. 228eb0e90a8SMaximilian Luz * @uid: The UID to matched against the individual table entries. 229eb0e90a8SMaximilian Luz * 230eb0e90a8SMaximilian Luz * Find the first match for the provided device UID in the provided ID table 231eb0e90a8SMaximilian Luz * and return it. Returns %NULL if no match could be found. 232eb0e90a8SMaximilian Luz */ 233eb0e90a8SMaximilian Luz const struct ssam_device_id *ssam_device_id_match(const struct ssam_device_id *table, 234eb0e90a8SMaximilian Luz const struct ssam_device_uid uid) 235eb0e90a8SMaximilian Luz { 236eb0e90a8SMaximilian Luz const struct ssam_device_id *id; 237eb0e90a8SMaximilian Luz 238eb0e90a8SMaximilian Luz for (id = table; !ssam_device_id_is_null(id); ++id) 239eb0e90a8SMaximilian Luz if (ssam_device_id_compatible(id, uid)) 240eb0e90a8SMaximilian Luz return id; 241eb0e90a8SMaximilian Luz 242eb0e90a8SMaximilian Luz return NULL; 243eb0e90a8SMaximilian Luz } 244eb0e90a8SMaximilian Luz EXPORT_SYMBOL_GPL(ssam_device_id_match); 245eb0e90a8SMaximilian Luz 246eb0e90a8SMaximilian Luz /** 247eb0e90a8SMaximilian Luz * ssam_device_get_match() - Find and return the ID matching the device in the 248eb0e90a8SMaximilian Luz * ID table of the bound driver. 249eb0e90a8SMaximilian Luz * @dev: The device for which to get the matching ID table entry. 250eb0e90a8SMaximilian Luz * 251eb0e90a8SMaximilian Luz * Find the fist match for the UID of the device in the ID table of the 252eb0e90a8SMaximilian Luz * currently bound driver and return it. Returns %NULL if the device does not 253eb0e90a8SMaximilian Luz * have a driver bound to it, the driver does not have match_table (i.e. it is 254eb0e90a8SMaximilian Luz * %NULL), or there is no match in the driver's match_table. 255eb0e90a8SMaximilian Luz * 256eb0e90a8SMaximilian Luz * This function essentially calls ssam_device_id_match() with the ID table of 257eb0e90a8SMaximilian Luz * the bound device driver and the UID of the device. 258eb0e90a8SMaximilian Luz * 259eb0e90a8SMaximilian Luz * Return: Returns the first match for the UID of the device in the device 260eb0e90a8SMaximilian Luz * driver's match table, or %NULL if no such match could be found. 261eb0e90a8SMaximilian Luz */ 262eb0e90a8SMaximilian Luz const struct ssam_device_id *ssam_device_get_match(const struct ssam_device *dev) 263eb0e90a8SMaximilian Luz { 264eb0e90a8SMaximilian Luz const struct ssam_device_driver *sdrv; 265eb0e90a8SMaximilian Luz 266eb0e90a8SMaximilian Luz sdrv = to_ssam_device_driver(dev->dev.driver); 267eb0e90a8SMaximilian Luz if (!sdrv) 268eb0e90a8SMaximilian Luz return NULL; 269eb0e90a8SMaximilian Luz 270eb0e90a8SMaximilian Luz if (!sdrv->match_table) 271eb0e90a8SMaximilian Luz return NULL; 272eb0e90a8SMaximilian Luz 273eb0e90a8SMaximilian Luz return ssam_device_id_match(sdrv->match_table, dev->uid); 274eb0e90a8SMaximilian Luz } 275eb0e90a8SMaximilian Luz EXPORT_SYMBOL_GPL(ssam_device_get_match); 276eb0e90a8SMaximilian Luz 277eb0e90a8SMaximilian Luz /** 278eb0e90a8SMaximilian Luz * ssam_device_get_match_data() - Find the ID matching the device in the 279eb0e90a8SMaximilian Luz * ID table of the bound driver and return its ``driver_data`` member. 280eb0e90a8SMaximilian Luz * @dev: The device for which to get the match data. 281eb0e90a8SMaximilian Luz * 282eb0e90a8SMaximilian Luz * Find the fist match for the UID of the device in the ID table of the 283eb0e90a8SMaximilian Luz * corresponding driver and return its driver_data. Returns %NULL if the 284eb0e90a8SMaximilian Luz * device does not have a driver bound to it, the driver does not have 285eb0e90a8SMaximilian Luz * match_table (i.e. it is %NULL), there is no match in the driver's 286eb0e90a8SMaximilian Luz * match_table, or the match does not have any driver_data. 287eb0e90a8SMaximilian Luz * 288eb0e90a8SMaximilian Luz * This function essentially calls ssam_device_get_match() and, if any match 289eb0e90a8SMaximilian Luz * could be found, returns its ``struct ssam_device_id.driver_data`` member. 290eb0e90a8SMaximilian Luz * 291eb0e90a8SMaximilian Luz * Return: Returns the driver data associated with the first match for the UID 292eb0e90a8SMaximilian Luz * of the device in the device driver's match table, or %NULL if no such match 293eb0e90a8SMaximilian Luz * could be found. 294eb0e90a8SMaximilian Luz */ 295eb0e90a8SMaximilian Luz const void *ssam_device_get_match_data(const struct ssam_device *dev) 296eb0e90a8SMaximilian Luz { 297eb0e90a8SMaximilian Luz const struct ssam_device_id *id; 298eb0e90a8SMaximilian Luz 299eb0e90a8SMaximilian Luz id = ssam_device_get_match(dev); 300eb0e90a8SMaximilian Luz if (!id) 301eb0e90a8SMaximilian Luz return NULL; 302eb0e90a8SMaximilian Luz 303eb0e90a8SMaximilian Luz return (const void *)id->driver_data; 304eb0e90a8SMaximilian Luz } 305eb0e90a8SMaximilian Luz EXPORT_SYMBOL_GPL(ssam_device_get_match_data); 306eb0e90a8SMaximilian Luz 307eb0e90a8SMaximilian Luz static int ssam_bus_match(struct device *dev, struct device_driver *drv) 308eb0e90a8SMaximilian Luz { 309eb0e90a8SMaximilian Luz struct ssam_device_driver *sdrv = to_ssam_device_driver(drv); 310eb0e90a8SMaximilian Luz struct ssam_device *sdev = to_ssam_device(dev); 311eb0e90a8SMaximilian Luz 312eb0e90a8SMaximilian Luz if (!is_ssam_device(dev)) 313eb0e90a8SMaximilian Luz return 0; 314eb0e90a8SMaximilian Luz 315eb0e90a8SMaximilian Luz return !!ssam_device_id_match(sdrv->match_table, sdev->uid); 316eb0e90a8SMaximilian Luz } 317eb0e90a8SMaximilian Luz 318eb0e90a8SMaximilian Luz static int ssam_bus_probe(struct device *dev) 319eb0e90a8SMaximilian Luz { 320eb0e90a8SMaximilian Luz return to_ssam_device_driver(dev->driver) 321eb0e90a8SMaximilian Luz ->probe(to_ssam_device(dev)); 322eb0e90a8SMaximilian Luz } 323eb0e90a8SMaximilian Luz 324fc7a6209SUwe Kleine-König static void ssam_bus_remove(struct device *dev) 325eb0e90a8SMaximilian Luz { 326eb0e90a8SMaximilian Luz struct ssam_device_driver *sdrv = to_ssam_device_driver(dev->driver); 327eb0e90a8SMaximilian Luz 328eb0e90a8SMaximilian Luz if (sdrv->remove) 329eb0e90a8SMaximilian Luz sdrv->remove(to_ssam_device(dev)); 330eb0e90a8SMaximilian Luz } 331eb0e90a8SMaximilian Luz 332eb0e90a8SMaximilian Luz struct bus_type ssam_bus_type = { 333eb0e90a8SMaximilian Luz .name = "surface_aggregator", 334eb0e90a8SMaximilian Luz .match = ssam_bus_match, 335eb0e90a8SMaximilian Luz .probe = ssam_bus_probe, 336eb0e90a8SMaximilian Luz .remove = ssam_bus_remove, 337eb0e90a8SMaximilian Luz }; 338eb0e90a8SMaximilian Luz EXPORT_SYMBOL_GPL(ssam_bus_type); 339eb0e90a8SMaximilian Luz 340eb0e90a8SMaximilian Luz /** 341eb0e90a8SMaximilian Luz * __ssam_device_driver_register() - Register a SSAM client device driver. 342eb0e90a8SMaximilian Luz * @sdrv: The driver to register. 343eb0e90a8SMaximilian Luz * @owner: The module owning the provided driver. 344eb0e90a8SMaximilian Luz * 345eb0e90a8SMaximilian Luz * Please refer to the ssam_device_driver_register() macro for the normal way 346eb0e90a8SMaximilian Luz * to register a driver from inside its owning module. 347eb0e90a8SMaximilian Luz */ 348eb0e90a8SMaximilian Luz int __ssam_device_driver_register(struct ssam_device_driver *sdrv, 349eb0e90a8SMaximilian Luz struct module *owner) 350eb0e90a8SMaximilian Luz { 351eb0e90a8SMaximilian Luz sdrv->driver.owner = owner; 352eb0e90a8SMaximilian Luz sdrv->driver.bus = &ssam_bus_type; 353eb0e90a8SMaximilian Luz 354eb0e90a8SMaximilian Luz /* force drivers to async probe so I/O is possible in probe */ 355eb0e90a8SMaximilian Luz sdrv->driver.probe_type = PROBE_PREFER_ASYNCHRONOUS; 356eb0e90a8SMaximilian Luz 357eb0e90a8SMaximilian Luz return driver_register(&sdrv->driver); 358eb0e90a8SMaximilian Luz } 359eb0e90a8SMaximilian Luz EXPORT_SYMBOL_GPL(__ssam_device_driver_register); 360eb0e90a8SMaximilian Luz 361eb0e90a8SMaximilian Luz /** 362eb0e90a8SMaximilian Luz * ssam_device_driver_unregister - Unregister a SSAM device driver. 363eb0e90a8SMaximilian Luz * @sdrv: The driver to unregister. 364eb0e90a8SMaximilian Luz */ 365eb0e90a8SMaximilian Luz void ssam_device_driver_unregister(struct ssam_device_driver *sdrv) 366eb0e90a8SMaximilian Luz { 367eb0e90a8SMaximilian Luz driver_unregister(&sdrv->driver); 368eb0e90a8SMaximilian Luz } 369eb0e90a8SMaximilian Luz EXPORT_SYMBOL_GPL(ssam_device_driver_unregister); 370eb0e90a8SMaximilian Luz 3714a4ab610SMaximilian Luz 3724a4ab610SMaximilian Luz /* -- Bus registration. ----------------------------------------------------- */ 3734a4ab610SMaximilian Luz 3744a4ab610SMaximilian Luz /** 3754a4ab610SMaximilian Luz * ssam_bus_register() - Register and set-up the SSAM client device bus. 3764a4ab610SMaximilian Luz */ 3774a4ab610SMaximilian Luz int ssam_bus_register(void) 3784a4ab610SMaximilian Luz { 3794a4ab610SMaximilian Luz return bus_register(&ssam_bus_type); 3804a4ab610SMaximilian Luz } 3814a4ab610SMaximilian Luz 3824a4ab610SMaximilian Luz /** 3834a4ab610SMaximilian Luz * ssam_bus_unregister() - Unregister the SSAM client device bus. 3844a4ab610SMaximilian Luz */ 3854a4ab610SMaximilian Luz void ssam_bus_unregister(void) 3864a4ab610SMaximilian Luz { 3874a4ab610SMaximilian Luz return bus_unregister(&ssam_bus_type); 3884a4ab610SMaximilian Luz } 3894a4ab610SMaximilian Luz 3904a4ab610SMaximilian Luz 3914a4ab610SMaximilian Luz /* -- Helpers for controller and hub devices. ------------------------------- */ 3924a4ab610SMaximilian Luz 3934a4ab610SMaximilian Luz static int ssam_device_uid_from_string(const char *str, struct ssam_device_uid *uid) 3944a4ab610SMaximilian Luz { 3954a4ab610SMaximilian Luz u8 d, tc, tid, iid, fn; 3964a4ab610SMaximilian Luz int n; 3974a4ab610SMaximilian Luz 3984a4ab610SMaximilian Luz n = sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx", &d, &tc, &tid, &iid, &fn); 3994a4ab610SMaximilian Luz if (n != 5) 4004a4ab610SMaximilian Luz return -EINVAL; 4014a4ab610SMaximilian Luz 4024a4ab610SMaximilian Luz uid->domain = d; 4034a4ab610SMaximilian Luz uid->category = tc; 4044a4ab610SMaximilian Luz uid->target = tid; 4054a4ab610SMaximilian Luz uid->instance = iid; 4064a4ab610SMaximilian Luz uid->function = fn; 4074a4ab610SMaximilian Luz 4084a4ab610SMaximilian Luz return 0; 4094a4ab610SMaximilian Luz } 4104a4ab610SMaximilian Luz 4114a4ab610SMaximilian Luz static int ssam_get_uid_for_node(struct fwnode_handle *node, struct ssam_device_uid *uid) 4124a4ab610SMaximilian Luz { 4134a4ab610SMaximilian Luz const char *str = fwnode_get_name(node); 4144a4ab610SMaximilian Luz 4154a4ab610SMaximilian Luz /* 4164a4ab610SMaximilian Luz * To simplify definitions of firmware nodes, we set the device name 4174a4ab610SMaximilian Luz * based on the UID of the device, prefixed with "ssam:". 4184a4ab610SMaximilian Luz */ 4194a4ab610SMaximilian Luz if (strncmp(str, "ssam:", strlen("ssam:")) != 0) 4204a4ab610SMaximilian Luz return -ENODEV; 4214a4ab610SMaximilian Luz 4224a4ab610SMaximilian Luz str += strlen("ssam:"); 4234a4ab610SMaximilian Luz return ssam_device_uid_from_string(str, uid); 4244a4ab610SMaximilian Luz } 4254a4ab610SMaximilian Luz 4264a4ab610SMaximilian Luz static int ssam_add_client_device(struct device *parent, struct ssam_controller *ctrl, 4274a4ab610SMaximilian Luz struct fwnode_handle *node) 4284a4ab610SMaximilian Luz { 4294a4ab610SMaximilian Luz struct ssam_device_uid uid; 4304a4ab610SMaximilian Luz struct ssam_device *sdev; 4314a4ab610SMaximilian Luz int status; 4324a4ab610SMaximilian Luz 4334a4ab610SMaximilian Luz status = ssam_get_uid_for_node(node, &uid); 4344a4ab610SMaximilian Luz if (status) 4354a4ab610SMaximilian Luz return status; 4364a4ab610SMaximilian Luz 4374a4ab610SMaximilian Luz sdev = ssam_device_alloc(ctrl, uid); 4384a4ab610SMaximilian Luz if (!sdev) 4394a4ab610SMaximilian Luz return -ENOMEM; 4404a4ab610SMaximilian Luz 4414a4ab610SMaximilian Luz sdev->dev.parent = parent; 4424a4ab610SMaximilian Luz sdev->dev.fwnode = fwnode_handle_get(node); 4434a4ab610SMaximilian Luz 4444a4ab610SMaximilian Luz status = ssam_device_add(sdev); 4454a4ab610SMaximilian Luz if (status) 4464a4ab610SMaximilian Luz ssam_device_put(sdev); 4474a4ab610SMaximilian Luz 4484a4ab610SMaximilian Luz return status; 4494a4ab610SMaximilian Luz } 4504a4ab610SMaximilian Luz 4514a4ab610SMaximilian Luz /** 4524a4ab610SMaximilian Luz * __ssam_register_clients() - Register client devices defined under the 4534a4ab610SMaximilian Luz * given firmware node as children of the given device. 4544a4ab610SMaximilian Luz * @parent: The parent device under which clients should be registered. 4554a4ab610SMaximilian Luz * @ctrl: The controller with which client should be registered. 4564a4ab610SMaximilian Luz * @node: The firmware node holding definitions of the devices to be added. 4574a4ab610SMaximilian Luz * 4584a4ab610SMaximilian Luz * Register all clients that have been defined as children of the given root 4594a4ab610SMaximilian Luz * firmware node as children of the given parent device. The respective child 4604a4ab610SMaximilian Luz * firmware nodes will be associated with the correspondingly created child 4614a4ab610SMaximilian Luz * devices. 4624a4ab610SMaximilian Luz * 4634a4ab610SMaximilian Luz * The given controller will be used to instantiate the new devices. See 4644a4ab610SMaximilian Luz * ssam_device_add() for details. 4654a4ab610SMaximilian Luz * 4664a4ab610SMaximilian Luz * Note that, generally, the use of either ssam_device_register_clients() or 4674a4ab610SMaximilian Luz * ssam_register_clients() should be preferred as they directly use the 4684a4ab610SMaximilian Luz * firmware node and/or controller associated with the given device. This 4694a4ab610SMaximilian Luz * function is only intended for use when different device specifications (e.g. 4704a4ab610SMaximilian Luz * ACPI and firmware nodes) need to be combined (as is done in the platform hub 4714a4ab610SMaximilian Luz * of the device registry). 4724a4ab610SMaximilian Luz * 4734a4ab610SMaximilian Luz * Return: Returns zero on success, nonzero on failure. 4744a4ab610SMaximilian Luz */ 4754a4ab610SMaximilian Luz int __ssam_register_clients(struct device *parent, struct ssam_controller *ctrl, 4764a4ab610SMaximilian Luz struct fwnode_handle *node) 4774a4ab610SMaximilian Luz { 4784a4ab610SMaximilian Luz struct fwnode_handle *child; 4794a4ab610SMaximilian Luz int status; 4804a4ab610SMaximilian Luz 4814a4ab610SMaximilian Luz fwnode_for_each_child_node(node, child) { 4824a4ab610SMaximilian Luz /* 4834a4ab610SMaximilian Luz * Try to add the device specified in the firmware node. If 4844a4ab610SMaximilian Luz * this fails with -ENODEV, the node does not specify any SSAM 4854a4ab610SMaximilian Luz * device, so ignore it and continue with the next one. 4864a4ab610SMaximilian Luz */ 4874a4ab610SMaximilian Luz status = ssam_add_client_device(parent, ctrl, child); 4884a4ab610SMaximilian Luz if (status && status != -ENODEV) 4894a4ab610SMaximilian Luz goto err; 4904a4ab610SMaximilian Luz } 4914a4ab610SMaximilian Luz 4924a4ab610SMaximilian Luz return 0; 4934a4ab610SMaximilian Luz err: 4944a4ab610SMaximilian Luz ssam_remove_clients(parent); 4954a4ab610SMaximilian Luz return status; 4964a4ab610SMaximilian Luz } 4974a4ab610SMaximilian Luz EXPORT_SYMBOL_GPL(__ssam_register_clients); 4984a4ab610SMaximilian Luz 499eb0e90a8SMaximilian Luz static int ssam_remove_device(struct device *dev, void *_data) 500eb0e90a8SMaximilian Luz { 501eb0e90a8SMaximilian Luz struct ssam_device *sdev = to_ssam_device(dev); 502eb0e90a8SMaximilian Luz 503eb0e90a8SMaximilian Luz if (is_ssam_device(dev)) 504eb0e90a8SMaximilian Luz ssam_device_remove(sdev); 505eb0e90a8SMaximilian Luz 506eb0e90a8SMaximilian Luz return 0; 507eb0e90a8SMaximilian Luz } 508eb0e90a8SMaximilian Luz 509eb0e90a8SMaximilian Luz /** 51038543b72SMaximilian Luz * ssam_remove_clients() - Remove SSAM client devices registered as direct 51138543b72SMaximilian Luz * children under the given parent device. 51238543b72SMaximilian Luz * @dev: The (parent) device to remove all direct clients for. 513eb0e90a8SMaximilian Luz * 51438543b72SMaximilian Luz * Remove all SSAM client devices registered as direct children under the given 51538543b72SMaximilian Luz * device. Note that this only accounts for direct children of the device. 51638543b72SMaximilian Luz * Refer to ssam_device_add()/ssam_device_remove() for more details. 517eb0e90a8SMaximilian Luz */ 51838543b72SMaximilian Luz void ssam_remove_clients(struct device *dev) 519eb0e90a8SMaximilian Luz { 520eb0e90a8SMaximilian Luz device_for_each_child_reverse(dev, NULL, ssam_remove_device); 521eb0e90a8SMaximilian Luz } 52238543b72SMaximilian Luz EXPORT_SYMBOL_GPL(ssam_remove_clients); 523