/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Surface System Aggregator Module ( SSAM ) bus and client - device subsystem .
*
* Main interface for the surface - aggregator bus , surface - aggregator client
* devices , and respective drivers building on top of the SSAM controller .
* Provides support for non - platform / non - ACPI SSAM clients via dedicated
* subsystem .
*
* Copyright ( C ) 2019 - 2021 Maximilian Luz < luzmaximilian @ gmail . com >
*/
#ifndef _LINUX_SURFACE_AGGREGATOR_DEVICE_H
#define _LINUX_SURFACE_AGGREGATOR_DEVICE_H
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/types.h>
#include <linux/surface_aggregator/controller.h>
/* -- Surface System Aggregator Module bus. --------------------------------- */
/**
* enum ssam_device_domain - SAM device domain .
* @ SSAM_DOMAIN_VIRTUAL : Virtual device .
* @ SSAM_DOMAIN_SERIALHUB : Physical device connected via Surface Serial Hub .
*/
enum ssam_device_domain {
SSAM_DOMAIN_VIRTUAL = 0 x00,
SSAM_DOMAIN_SERIALHUB = 0 x01,
};
/**
* enum ssam_virtual_tc - Target categories for the virtual SAM domain .
* @ SSAM_VIRTUAL_TC_HUB : Device hub category .
*/
enum ssam_virtual_tc {
SSAM_VIRTUAL_TC_HUB = 0 x00,
};
/**
* struct ssam_device_uid - Unique identifier for SSAM device .
* @ domain : Domain of the device .
* @ category : Target category of the device .
* @ target : Target ID of the device .
* @ instance : Instance ID of the device .
* @ function : Sub - function of the device . This field can be used to split a
* single SAM device into multiple virtual subdevices to separate
* different functionality of that device and allow one driver per
* such functionality .
*/
struct ssam_device_uid {
u8 domain;
u8 category;
u8 target;
u8 instance;
u8 function;
};
/*
* Special values for device matching .
*
* These values are intended to be used with SSAM_DEVICE ( ) , SSAM_VDEV ( ) , and
* SSAM_SDEV ( ) exclusively . Specifically , they are used to initialize the
* match_flags member of the device ID structure . Do not use them directly
* with struct ssam_device_id or struct ssam_device_uid .
*/
#define SSAM_SSH_TID_ANY 0 xffff
#define SSAM_SSH_IID_ANY 0 xffff
#define SSAM_SSH_FUN_ANY 0 xffff
/**
* SSAM_DEVICE ( ) - Initialize a & struct ssam_device_id with the given
* parameters .
* @ d : Domain of the device .
* @ cat : Target category of the device .
* @ tid : Target ID of the device .
* @ iid : Instance ID of the device .
* @ fun : Sub - function of the device .
*
* Initializes a & struct ssam_device_id with the given parameters . See & struct
* ssam_device_uid for details regarding the parameters . The special values
* % SSAM_SSH_TID_ANY , % SSAM_SSH_IID_ANY , and % SSAM_SSH_FUN_ANY can be used to specify that
* matching should ignore target ID , instance ID , and / or sub - function ,
* respectively . This macro initializes the ` ` match_flags ` ` field based on the
* given parameters .
*
* Note : The parameters @ d and @ cat must be valid & u8 values , the parameters
* @ tid , @ iid , and @ fun must be either valid & u8 values or % SSAM_SSH_TID_ANY ,
* % SSAM_SSH_IID_ANY , or % SSAM_SSH_FUN_ANY , respectively . Other non - & u8 values are not
* allowed .
*/
#define SSAM_DEVICE(d, cat, tid, iid, fun) \
.match_flags = (((tid) != SSAM_SSH_TID_ANY) ? SSAM_MATCH_TARGET : 0 ) \
| (((iid) != SSAM_SSH_IID_ANY) ? SSAM_MATCH_INSTANCE : 0 ) \
| (((fun) != SSAM_SSH_FUN_ANY) ? SSAM_MATCH_FUNCTION : 0 ), \
.domain = d, \
.category = cat, \
.target = __builtin_choose_expr((tid) != SSAM_SSH_TID_ANY, (tid), 0 ), \
.instance = __builtin_choose_expr((iid) != SSAM_SSH_IID_ANY, (iid), 0 ), \
.function = __builtin_choose_expr((fun) != SSAM_SSH_FUN_ANY, (fun), 0 )
/**
* SSAM_VDEV ( ) - Initialize a & struct ssam_device_id as virtual device with
* the given parameters .
* @ cat : Target category of the device .
* @ tid : Target ID of the device .
* @ iid : Instance ID of the device .
* @ fun : Sub - function of the device .
*
* Initializes a & struct ssam_device_id with the given parameters in the
* virtual domain . See & struct ssam_device_uid for details regarding the
* parameters . The special values % SSAM_SSH_TID_ANY , % SSAM_SSH_IID_ANY , and
* % SSAM_SSH_FUN_ANY can be used to specify that matching should ignore target ID ,
* instance ID , and / or sub - function , respectively . This macro initializes the
* ` ` match_flags ` ` field based on the given parameters .
*
* Note : The parameter @ cat must be a valid & u8 value , the parameters @ tid ,
* @ iid , and @ fun must be either valid & u8 values or % SSAM_SSH_TID_ANY ,
* % SSAM_SSH_IID_ANY , or % SSAM_SSH_FUN_ANY , respectively . Other non - & u8 values are not
* allowed .
*/
#define SSAM_VDEV(cat, tid, iid, fun) \
SSAM_DEVICE(SSAM_DOMAIN_VIRTUAL, SSAM_VIRTUAL_TC_## cat, SSAM_SSH_TID_## tid, iid, fun)
/**
* SSAM_SDEV ( ) - Initialize a & struct ssam_device_id as physical SSH device
* with the given parameters .
* @ cat : Target category of the device .
* @ tid : Target ID of the device .
* @ iid : Instance ID of the device .
* @ fun : Sub - function of the device .
*
* Initializes a & struct ssam_device_id with the given parameters in the SSH
* domain . See & struct ssam_device_uid for details regarding the parameters .
* The special values % SSAM_SSH_TID_ANY , % SSAM_SSH_IID_ANY , and
* % SSAM_SSH_FUN_ANY can be used to specify that matching should ignore target
* ID , instance ID , and / or sub - function , respectively . This macro initializes
* the ` ` match_flags ` ` field based on the given parameters .
*
* Note : The parameter @ cat must be a valid & u8 value , the parameters @ tid ,
* @ iid , and @ fun must be either valid & u8 values or % SSAM_SSH_TID_ANY ,
* % SSAM_SSH_IID_ANY , or % SSAM_SSH_FUN_ANY , respectively . Other non - & u8 values
* are not allowed .
*/
#define SSAM_SDEV(cat, tid, iid, fun) \
SSAM_DEVICE(SSAM_DOMAIN_SERIALHUB, SSAM_SSH_TC_## cat, SSAM_SSH_TID_## tid, iid, fun)
/*
* enum ssam_device_flags - Flags for SSAM client devices .
* @ SSAM_DEVICE_HOT_REMOVED_BIT :
* The device has been hot - removed . Further communication with it may time
* out and should be avoided .
*/
enum ssam_device_flags {
SSAM_DEVICE_HOT_REMOVED_BIT = 0 ,
};
/**
* struct ssam_device - SSAM client device .
* @ dev : Driver model representation of the device .
* @ ctrl : SSAM controller managing this device .
* @ uid : UID identifying the device .
* @ flags : Device state flags , see & enum ssam_device_flags .
*/
struct ssam_device {
struct device dev;
struct ssam_controller *ctrl;
struct ssam_device_uid uid;
unsigned long flags;
};
/**
* struct ssam_device_driver - SSAM client device driver .
* @ driver : Base driver model structure .
* @ match_table : Match table specifying which devices the driver should bind to .
* @ probe : Called when the driver is being bound to a device .
* @ remove : Called when the driver is being unbound from the device .
*/
struct ssam_device_driver {
struct device_driver driver;
const struct ssam_device_id *match_table;
int (*probe)(struct ssam_device *sdev);
void (*remove)(struct ssam_device *sdev);
};
#ifdef CONFIG_SURFACE_AGGREGATOR_BUS
extern const struct device_type ssam_device_type;
/**
* is_ssam_device ( ) - Check if the given device is a SSAM client device .
* @ d : The device to test the type of .
*
* Return : Returns % true if the specified device is of type & struct
* ssam_device , i . e . the device type points to % ssam_device_type , and % false
* otherwise .
*/
static inline bool is_ssam_device(struct device *d)
{
return d->type == &ssam_device_type;
}
#else /* CONFIG_SURFACE_AGGREGATOR_BUS */
static inline bool is_ssam_device(struct device *d)
{
return false ;
}
#endif /* CONFIG_SURFACE_AGGREGATOR_BUS */
/**
* to_ssam_device ( ) - Casts the given device to a SSAM client device .
* @ d : The device to cast .
*
* Casts the given & struct device to a & struct ssam_device . The caller has to
* ensure that the given device is actually enclosed in a & struct ssam_device ,
* e . g . by calling is_ssam_device ( ) .
*
* Return : Returns a pointer to the & struct ssam_device wrapping the given
* device @ d .
*/
#define to_ssam_device(d) container_of_const(d, struct ssam_device, dev)
/**
* to_ssam_device_driver ( ) - Casts the given device driver to a SSAM client
* device driver .
* @ d : The driver to cast .
*
* Casts the given & struct device_driver to a & struct ssam_device_driver . The
* caller has to ensure that the given driver is actually enclosed in a
* & struct ssam_device_driver .
*
* Return : Returns the pointer to the & struct ssam_device_driver wrapping the
* given device driver @ d .
*/
#define to_ssam_device_driver(d) container_of_const(d, struct ssam_device_driver, driver)
const struct ssam_device_id *ssam_device_id_match(const struct ssam_device_id *table,
const struct ssam_device_uid uid);
const struct ssam_device_id *ssam_device_get_match(const struct ssam_device *dev);
const void *ssam_device_get_match_data(const struct ssam_device *dev);
struct ssam_device *ssam_device_alloc(struct ssam_controller *ctrl,
struct ssam_device_uid uid);
int ssam_device_add(struct ssam_device *sdev);
void ssam_device_remove(struct ssam_device *sdev);
/**
* ssam_device_mark_hot_removed ( ) - Mark the given device as hot - removed .
* @ sdev : The device to mark as hot - removed .
*
* Mark the device as having been hot - removed . This signals drivers using the
* device that communication with the device should be avoided and may lead to
* timeouts .
*/
static inline void ssam_device_mark_hot_removed(struct ssam_device *sdev)
{
dev_dbg(&sdev->dev, "marking device as hot-removed\n" );
set_bit(SSAM_DEVICE_HOT_REMOVED_BIT, &sdev->flags);
}
/**
* ssam_device_is_hot_removed ( ) - Check if the given device has been
* hot - removed .
* @ sdev : The device to check .
*
* Checks if the given device has been marked as hot - removed . See
* ssam_device_mark_hot_removed ( ) for more details .
*
* Return : Returns ` ` true ` ` if the device has been marked as hot - removed .
*/
static inline bool ssam_device_is_hot_removed(struct ssam_device *sdev)
{
return test_bit(SSAM_DEVICE_HOT_REMOVED_BIT, &sdev->flags);
}
/**
* ssam_device_get ( ) - Increment reference count of SSAM client device .
* @ sdev : The device to increment the reference count of .
*
* Increments the reference count of the given SSAM client device by
* incrementing the reference count of the enclosed & struct device via
* get_device ( ) .
*
* See ssam_device_put ( ) for the counter - part of this function .
*
* Return : Returns the device provided as input .
*/
static inline struct ssam_device *ssam_device_get(struct ssam_device *sdev)
{
return sdev ? to_ssam_device(get_device(&sdev->dev)) : NULL;
}
/**
* ssam_device_put ( ) - Decrement reference count of SSAM client device .
* @ sdev : The device to decrement the reference count of .
*
* Decrements the reference count of the given SSAM client device by
* decrementing the reference count of the enclosed & struct device via
* put_device ( ) .
*
* See ssam_device_get ( ) for the counter - part of this function .
*/
static inline void ssam_device_put(struct ssam_device *sdev)
{
if (sdev)
put_device(&sdev->dev);
}
/**
* ssam_device_get_drvdata ( ) - Get driver - data of SSAM client device .
* @ sdev : The device to get the driver - data from .
*
* Return : Returns the driver - data of the given device , previously set via
* ssam_device_set_drvdata ( ) .
*/
static inline void *ssam_device_get_drvdata(struct ssam_device *sdev)
{
return dev_get_drvdata(&sdev->dev);
}
/**
* ssam_device_set_drvdata ( ) - Set driver - data of SSAM client device .
* @ sdev : The device to set the driver - data of .
* @ data : The data to set the device ' s driver - data pointer to .
*/
static inline void ssam_device_set_drvdata(struct ssam_device *sdev, void *data)
{
dev_set_drvdata(&sdev->dev, data);
}
int __ssam_device_driver_register(struct ssam_device_driver *d, struct module *o);
void ssam_device_driver_unregister(struct ssam_device_driver *d);
/**
* ssam_device_driver_register ( ) - Register a SSAM client device driver .
* @ drv : The driver to register .
*/
#define ssam_device_driver_register(drv) \
__ssam_device_driver_register(drv, THIS_MODULE)
/**
* module_ssam_device_driver ( ) - Helper macro for SSAM device driver
* registration .
* @ drv : The driver managed by this module .
*
* Helper macro to register a SSAM device driver via module_init ( ) and
* module_exit ( ) . This macro may only be used once per module and replaces the
* aforementioned definitions .
*/
#define module_ssam_device_driver(drv) \
module_driver(drv, ssam_device_driver_register, \
ssam_device_driver_unregister)
/* -- Helpers for controller and hub devices. ------------------------------- */
#ifdef CONFIG_SURFACE_AGGREGATOR_BUS
int __ssam_register_clients(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node);
void ssam_remove_clients(struct device *dev);
#else /* CONFIG_SURFACE_AGGREGATOR_BUS */
static inline int __ssam_register_clients(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node)
{
return 0 ;
}
static inline void ssam_remove_clients(struct device *dev) {}
#endif /* CONFIG_SURFACE_AGGREGATOR_BUS */
/**
* ssam_register_clients ( ) - Register all client devices defined under the
* given parent device .
* @ dev : The parent device under which clients should be registered .
* @ ctrl : The controller with which client should be registered .
*
* Register all clients that have via firmware nodes been defined as children
* of the given ( parent ) device . The respective child firmware nodes will be
* associated with the correspondingly created child devices .
*
* The given controller will be used to instantiate the new devices . See
* ssam_device_add ( ) for details .
*
* Return : Returns zero on success , nonzero on failure .
*/
static inline int ssam_register_clients(struct device *dev, struct ssam_controller *ctrl)
{
return __ssam_register_clients(dev, ctrl, dev_fwnode(dev));
}
/**
* ssam_device_register_clients ( ) - Register all client devices defined under
* the given SSAM parent device .
* @ sdev : The parent device under which clients should be registered .
*
* Register all clients that have via firmware nodes been defined as children
* of the given ( parent ) device . The respective child firmware nodes will be
* associated with the correspondingly created child devices .
*
* The controller used by the parent device will be used to instantiate the new
* devices . See ssam_device_add ( ) for details .
*
* Return : Returns zero on success , nonzero on failure .
*/
static inline int ssam_device_register_clients(struct ssam_device *sdev)
{
return ssam_register_clients(&sdev->dev, sdev->ctrl);
}
/* -- Helpers for client-device requests. ----------------------------------- */
/**
* SSAM_DEFINE_SYNC_REQUEST_CL_N ( ) - Define synchronous client - device SAM
* request function with neither argument nor return value .
* @ name : Name of the generated function .
* @ spec : Specification ( & struct ssam_request_spec_md ) defining the request .
*
* Defines a function executing the synchronous SAM request specified by
* @ spec , with the request having neither argument nor return value . Device
* specifying parameters are not hard - coded , but instead are provided via the
* client device , specifically its UID , supplied when calling this function .
* The generated function takes care of setting up the request struct , buffer
* allocation , as well as execution of the request itself , returning once the
* request has been fully completed . The required transport buffer will be
* allocated on the stack .
*
* The generated function is defined as ` ` static int name ( struct ssam_device
* * sdev ) ` ` , returning the status of the request , which is zero on success and
* negative on failure . The ` ` sdev ` ` parameter specifies both the target
* device of the request and by association the controller via which the
* request is sent .
*
* Refer to ssam_request_do_sync_onstack ( ) for more details on the behavior of
* the generated function .
*/
#define SSAM_DEFINE_SYNC_REQUEST_CL_N(name, spec...) \
SSAM_DEFINE_SYNC_REQUEST_MD_N(__raw_## name, spec) \
static int name(struct ssam_device *sdev) \
{ \
return __raw_## name(sdev->ctrl, sdev->uid.target, \
sdev->uid.instance); \
}
/**
* SSAM_DEFINE_SYNC_REQUEST_CL_W ( ) - Define synchronous client - device SAM
* request function with argument .
* @ name : Name of the generated function .
* @ atype : Type of the request ' s argument .
* @ spec : Specification ( & struct ssam_request_spec_md ) defining the request .
*
* Defines a function executing the synchronous SAM request specified by
* @ spec , with the request taking an argument of type @ atype and having no
* return value . Device specifying parameters are not hard - coded , but instead
* are provided via the client device , specifically its UID , supplied when
* calling this function . The generated function takes care of setting up the
* request struct , buffer allocation , as well as execution of the request
* itself , returning once the request has been fully completed . The required
* transport buffer will be allocated on the stack .
*
* The generated function is defined as ` ` static int name ( struct ssam_device
* * sdev , const atype * arg ) ` ` , returning the status of the request , which is
* zero on success and negative on failure . The ` ` sdev ` ` parameter specifies
* both the target device of the request and by association the controller via
* which the request is sent . The request ' s argument is specified via the
* ` ` arg ` ` pointer .
*
* Refer to ssam_request_do_sync_onstack ( ) for more details on the behavior of
* the generated function .
*/
#define SSAM_DEFINE_SYNC_REQUEST_CL_W(name, atype, spec...) \
SSAM_DEFINE_SYNC_REQUEST_MD_W(__raw_## name, atype, spec) \
static int name(struct ssam_device *sdev, const atype *arg) \
{ \
return __raw_## name(sdev->ctrl, sdev->uid.target, \
sdev->uid.instance, arg); \
}
/**
* SSAM_DEFINE_SYNC_REQUEST_CL_R ( ) - Define synchronous client - device SAM
* request function with return value .
* @ name : Name of the generated function .
* @ rtype : Type of the request ' s return value .
* @ spec : Specification ( & struct ssam_request_spec_md ) defining the request .
*
* Defines a function executing the synchronous SAM request specified by
* @ spec , with the request taking no argument but having a return value of
* type @ rtype . Device specifying parameters are not hard - coded , but instead
* are provided via the client device , specifically its UID , supplied when
* calling this function . The generated function takes care of setting up the
* request struct , buffer allocation , as well as execution of the request
* itself , returning once the request has been fully completed . The required
* transport buffer will be allocated on the stack .
*
* The generated function is defined as ` ` static int name ( struct ssam_device
* * sdev , rtype * ret ) ` ` , returning the status of the request , which is zero on
* success and negative on failure . The ` ` sdev ` ` parameter specifies both the
* target device of the request and by association the controller via which
* the request is sent . The request ' s return value is written to the memory
* pointed to by the ` ` ret ` ` parameter .
*
* Refer to ssam_request_do_sync_onstack ( ) for more details on the behavior of
* the generated function .
*/
#define SSAM_DEFINE_SYNC_REQUEST_CL_R(name, rtype, spec...) \
SSAM_DEFINE_SYNC_REQUEST_MD_R(__raw_## name, rtype, spec) \
static int name(struct ssam_device *sdev, rtype *ret) \
{ \
return __raw_## name(sdev->ctrl, sdev->uid.target, \
sdev->uid.instance, ret); \
}
/**
* SSAM_DEFINE_SYNC_REQUEST_CL_WR ( ) - Define synchronous client - device SAM
* request function with argument and return value .
* @ name : Name of the generated function .
* @ atype : Type of the request ' s argument .
* @ rtype : Type of the request ' s return value .
* @ spec : Specification ( & struct ssam_request_spec_md ) defining the request .
*
* Defines a function executing the synchronous SAM request specified by @ spec ,
* with the request taking an argument of type @ atype and having a return value
* of type @ rtype . Device specifying parameters are not hard - coded , but instead
* are provided via the client device , specifically its UID , supplied when
* calling this function . The generated function takes care of setting up the
* request struct , buffer allocation , as well as execution of the request
* itself , returning once the request has been fully completed . The required
* transport buffer will be allocated on the stack .
*
* The generated function is defined as ` ` static int name ( struct ssam_device
* * sdev , const atype * arg , rtype * ret ) ` ` , returning the status of the request ,
* which is zero on success and negative on failure . The ` ` sdev ` ` parameter
* specifies both the target device of the request and by association the
* controller via which the request is sent . The request ' s argument is
* specified via the ` ` arg ` ` pointer . The request ' s return value is written to
* the memory pointed to by the ` ` ret ` ` parameter .
*
* Refer to ssam_request_do_sync_onstack ( ) for more details on the behavior of
* the generated function .
*/
#define SSAM_DEFINE_SYNC_REQUEST_CL_WR(name, atype, rtype, spec...) \
SSAM_DEFINE_SYNC_REQUEST_MD_WR(__raw_## name, atype, rtype, spec) \
static int name(struct ssam_device *sdev, const atype *arg, rtype *ret) \
{ \
return __raw_## name(sdev->ctrl, sdev->uid.target, \
sdev->uid.instance, arg, ret); \
}
/* -- Helpers for client-device notifiers. ---------------------------------- */
/**
* ssam_device_notifier_register ( ) - Register an event notifier for the
* specified client device .
* @ sdev : The device the notifier should be registered on .
* @ n : The event notifier to register .
*
* Register an event notifier . Increment the usage counter of the associated
* SAM event if the notifier is not marked as an observer . If the event is not
* marked as an observer and is currently not enabled , it will be enabled
* during this call . If the notifier is marked as an observer , no attempt will
* be made at enabling any event and no reference count will be modified .
*
* Notifiers marked as observers do not need to be associated with one specific
* event , i . e . as long as no event matching is performed , only the event target
* category needs to be set .
*
* Return : Returns zero on success , % - ENOSPC if there have already been
* % INT_MAX notifiers for the event ID / type associated with the notifier block
* registered , % - ENOMEM if the corresponding event entry could not be
* allocated , % - ENODEV if the device is marked as hot - removed . If this is the
* first time that a notifier block is registered for the specific associated
* event , returns the status of the event - enable EC - command .
*/
static inline int ssam_device_notifier_register(struct ssam_device *sdev,
struct ssam_event_notifier *n)
{
/*
* Note that this check does not provide any guarantees whatsoever as
* hot - removal could happen at any point and we can ' t protect against
* it . Nevertheless , if we can detect hot - removal , bail early to avoid
* communication timeouts .
*/
if (ssam_device_is_hot_removed(sdev))
return -ENODEV;
return ssam_notifier_register(sdev->ctrl, n);
}
/**
* ssam_device_notifier_unregister ( ) - Unregister an event notifier for the
* specified client device .
* @ sdev : The device the notifier has been registered on .
* @ n : The event notifier to unregister .
*
* Unregister an event notifier . Decrement the usage counter of the associated
* SAM event if the notifier is not marked as an observer . If the usage counter
* reaches zero , the event will be disabled .
*
* In case the device has been marked as hot - removed , the event will not be
* disabled on the EC , as in those cases any attempt at doing so may time out .
*
* Return : Returns zero on success , % - ENOENT if the given notifier block has
* not been registered on the controller . If the given notifier block was the
* last one associated with its specific event , returns the status of the
* event - disable EC - command .
*/
static inline int ssam_device_notifier_unregister(struct ssam_device *sdev,
struct ssam_event_notifier *n)
{
return __ssam_notifier_unregister(sdev->ctrl, n,
!ssam_device_is_hot_removed(sdev));
}
#endif /* _LINUX_SURFACE_AGGREGATOR_DEVICE_H */
Messung V0.5 in Prozent C=94 H=100 G=96
¤ Dauer der Verarbeitung: 0.28 Sekunden
(vorverarbeitet am 2026-10-01)
¤
*© Formatika GbR, Deutschland