forked from mirrors/linux
		
	driver core: Add state_synced sysfs file for devices that support it
This can be used to check if a device supports sync_state() callbacks and therefore keeps resources left on by the bootloader enabled till all its consumers have probed. This can also be used to check if sync_state() has been called for a device or whether it is still trying to keep resources enabled because they were left enabled by the bootloader and all its consumers haven't probed yet. Signed-off-by: Saravana Kannan <saravanak@google.com> Link: https://lore.kernel.org/r/20200521191800.136035-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									287905e68d
								
							
						
					
					
						commit
						8fd456ec0c
					
				
					 2 changed files with 46 additions and 0 deletions
				
			
		
							
								
								
									
										24
									
								
								Documentation/ABI/testing/sysfs-devices-state_synced
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Documentation/ABI/testing/sysfs-devices-state_synced
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					What:		/sys/devices/.../state_synced
 | 
				
			||||||
 | 
					Date:		May 2020
 | 
				
			||||||
 | 
					Contact:	Saravana Kannan <saravanak@google.com>
 | 
				
			||||||
 | 
					Description:
 | 
				
			||||||
 | 
							The /sys/devices/.../state_synced attribute is only present for
 | 
				
			||||||
 | 
							devices whose bus types or driver provides the .sync_state()
 | 
				
			||||||
 | 
							callback. The number read from it (0 or 1) reflects the value
 | 
				
			||||||
 | 
							of the device's 'state_synced' field. A value of 0 means the
 | 
				
			||||||
 | 
							.sync_state() callback hasn't been called yet. A value of 1
 | 
				
			||||||
 | 
							means the .sync_state() callback has been called.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Generally, if a device has sync_state() support and has some of
 | 
				
			||||||
 | 
							the resources it provides enabled at the time the kernel starts
 | 
				
			||||||
 | 
							(Eg: enabled by hardware reset or bootloader or anything that
 | 
				
			||||||
 | 
							run before the kernel starts), then it'll keep those resources
 | 
				
			||||||
 | 
							enabled and in a state that's compatible with the state they
 | 
				
			||||||
 | 
							were in at the start of the kernel. The device will stop doing
 | 
				
			||||||
 | 
							this only when the sync_state() callback has been called --
 | 
				
			||||||
 | 
							which happens only when all its consumer devices are registered
 | 
				
			||||||
 | 
							and have probed successfully. Resources that were left disabled
 | 
				
			||||||
 | 
							at the time the kernel starts are not affected or limited in
 | 
				
			||||||
 | 
							any way by sync_state() callbacks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -462,6 +462,18 @@ static void driver_deferred_probe_add_trigger(struct device *dev,
 | 
				
			||||||
		driver_deferred_probe_trigger();
 | 
							driver_deferred_probe_trigger();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static ssize_t state_synced_show(struct device *dev,
 | 
				
			||||||
 | 
									 struct device_attribute *attr, char *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						bool val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						device_lock(dev);
 | 
				
			||||||
 | 
						val = dev->state_synced;
 | 
				
			||||||
 | 
						device_unlock(dev);
 | 
				
			||||||
 | 
						return sprintf(buf, "%u\n", val);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					static DEVICE_ATTR_RO(state_synced);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int really_probe(struct device *dev, struct device_driver *drv)
 | 
					static int really_probe(struct device *dev, struct device_driver *drv)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret = -EPROBE_DEFER;
 | 
						int ret = -EPROBE_DEFER;
 | 
				
			||||||
| 
						 | 
					@ -535,9 +547,16 @@ static int really_probe(struct device *dev, struct device_driver *drv)
 | 
				
			||||||
		goto dev_groups_failed;
 | 
							goto dev_groups_failed;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dev_has_sync_state(dev) &&
 | 
				
			||||||
 | 
						    device_create_file(dev, &dev_attr_state_synced)) {
 | 
				
			||||||
 | 
							dev_err(dev, "state_synced sysfs add failed\n");
 | 
				
			||||||
 | 
							goto dev_sysfs_state_synced_failed;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (test_remove) {
 | 
						if (test_remove) {
 | 
				
			||||||
		test_remove = false;
 | 
							test_remove = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							device_remove_file(dev, &dev_attr_state_synced);
 | 
				
			||||||
		device_remove_groups(dev, drv->dev_groups);
 | 
							device_remove_groups(dev, drv->dev_groups);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (dev->bus->remove)
 | 
							if (dev->bus->remove)
 | 
				
			||||||
| 
						 | 
					@ -567,6 +586,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
 | 
				
			||||||
		 drv->bus->name, __func__, dev_name(dev), drv->name);
 | 
							 drv->bus->name, __func__, dev_name(dev), drv->name);
 | 
				
			||||||
	goto done;
 | 
						goto done;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dev_sysfs_state_synced_failed:
 | 
				
			||||||
 | 
						device_remove_groups(dev, drv->dev_groups);
 | 
				
			||||||
dev_groups_failed:
 | 
					dev_groups_failed:
 | 
				
			||||||
	if (dev->bus->remove)
 | 
						if (dev->bus->remove)
 | 
				
			||||||
		dev->bus->remove(dev);
 | 
							dev->bus->remove(dev);
 | 
				
			||||||
| 
						 | 
					@ -1104,6 +1125,7 @@ static void __device_release_driver(struct device *dev, struct device *parent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		pm_runtime_put_sync(dev);
 | 
							pm_runtime_put_sync(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							device_remove_file(dev, &dev_attr_state_synced);
 | 
				
			||||||
		device_remove_groups(dev, drv->dev_groups);
 | 
							device_remove_groups(dev, drv->dev_groups);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (dev->bus && dev->bus->remove)
 | 
							if (dev->bus && dev->bus->remove)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue