Device properties framework updates for 5.3-rc1
- Add helpers to count items in a property array (Andy Shevchenko). - Extend "software nodes" support to be more convenient for representing device properties supplied by drivers (Heikki Krogerus). - Add device_find_child_by_name() helper to the driver core (Heikki Krogerus). - Extend device connection code to also look for references provided via fwnode pointers (Heikki Krogerus). - Start to register proper struct device objects for USB Type-C muxes and orientation switches (Heikki Krogerus). - Update the intel_cht_int33fe driver to describe devices in a more general way with the help of "software nodes" (Heikki Krogerus). -----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAl0jLKMSHHJqd0Byand5 c29ja2kubmV0AAoJEILEb/54YlRxJIUQAJ7vJ9CVPzcFK8S6FkDD1nNUbZqnHhpe v9u2e347Wb5TyxWrzdgzBWw3JmJsi78kO8R82fJSZgkaqTWsxWxbPH6K+OSU0gxB lU8A5opk/P8ChAEGz0hCIXVYgTWRlgETQnAsUspoPPw/9ypxY/lZJ2P3QIvDsn6Q wsfFEdshHXrqOzWg5XxWVMogUhrDm2us/xs1HC6eWcmABggrf3m6cr/KxABZK5Hx PIhIRJT8BN5e9wB2dKNcxNoz0XLem2pW2EN1vHHEVwSbxJE45yJqiz4cXRxHYwrT GCXDYUcwiwtScvXRfl4Ihm8E1+hDczGW0FUR0A2Y9QB15zWvJy8ff1GlHhbROoSV 3T48xu6iAWuz9/i6BrJSZ9C1hya867v/S8ccBEnHRTtJwqSEHRBmzyINbxqtcENe LY4cGcZAjkDnWOXz7cHbM4uXwn+XYQHCbi+bkHWXEEKRqx6LWFOaGoSR6XlqRBrA H/RSg38GU7KTCLun/YLezuwbT08lvDoere9B3psxNW2XW7ufjL5DSnxRFm0hdBMv zWMAi/6a6yr1hpUyCXZTlnEYI7Hqx3dDTinQe8x8BeJ2CRUnFqmH+1Y0ihd8vCvf 2k9tS2WAt72dFOckwwWOX9xEQIHGInkDWUx8MHbEuTtW7R61NTdCiYR/xxZhkTOr aB3xFjSGAv87 =jOFk -----END PGP SIGNATURE----- Merge tag 'devprop-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm Pull device properties framework updates from Rafael Wysocki: "These add helpers for counting items in a property array and extend the "software nodes" support to be more convenient for representing device properties supplied by drivers and make the intel_cht_int33fe driver use that. Specifics: - Add helpers to count items in a property array (Andy Shevchenko). - Extend "software nodes" support to be more convenient for representing device properties supplied by drivers (Heikki Krogerus). - Add device_find_child_by_name() helper to the driver core (Heikki Krogerus). - Extend device connection code to also look for references provided via fwnode pointers (Heikki Krogerus). - Start to register proper struct device objects for USB Type-C muxes and orientation switches (Heikki Krogerus). - Update the intel_cht_int33fe driver to describe devices in a more general way with the help of "software nodes" (Heikki Krogerus)" * tag 'devprop-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: device property: Add helpers to count items in an array platform/x86: intel_cht_int33fe: Replacing the old connections with references platform/x86: intel_cht_int33fe: Supply fwnodes for the external dependencies platform/x86: intel_cht_int33fe: Provide fwnode for the USB connector platform/x86: intel_cht_int33fe: Provide software nodes for the devices platform/x86: intel_cht_int33fe: Remove unused fusb302 device property platform/x86: intel_cht_int33fe: Register max17047 in its own function usb: typec: Registering real device entries for the muxes device connection: Find connections also by checking the references device property: Introduce fwnode_find_reference() ACPI / property: Don't limit named child node matching to data nodes driver core: Add helper device_find_child_by_name() software node: Add software_node_get_reference_args() software node: Use kobject name when finding child nodes by name software node: Add support for static node descriptors software node: Simplify software_node_release() function software node: Allow node creation without properties
This commit is contained in:
Коммит
0415052db4
|
@ -600,15 +600,29 @@ static struct fwnode_handle *
|
|||
acpi_fwnode_get_named_child_node(const struct fwnode_handle *fwnode,
|
||||
const char *childname)
|
||||
{
|
||||
char name[ACPI_PATH_SEGMENT_LENGTH];
|
||||
struct fwnode_handle *child;
|
||||
struct acpi_buffer path;
|
||||
acpi_status status;
|
||||
|
||||
/*
|
||||
* Find first matching named child node of this fwnode.
|
||||
* For ACPI this will be a data only sub-node.
|
||||
*/
|
||||
fwnode_for_each_child_node(fwnode, child)
|
||||
if (acpi_data_node_match(child, childname))
|
||||
path.length = sizeof(name);
|
||||
path.pointer = name;
|
||||
|
||||
fwnode_for_each_child_node(fwnode, child) {
|
||||
if (is_acpi_data_node(child)) {
|
||||
if (acpi_data_node_match(child, childname))
|
||||
return child;
|
||||
continue;
|
||||
}
|
||||
|
||||
status = acpi_get_name(ACPI_HANDLE_FWNODE(child),
|
||||
ACPI_SINGLE_NAME, &path);
|
||||
if (ACPI_FAILURE(status))
|
||||
break;
|
||||
|
||||
if (!strncmp(name, childname, ACPI_NAMESEG_SIZE))
|
||||
return child;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -2474,6 +2474,34 @@ struct device *device_find_child(struct device *parent, void *data,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(device_find_child);
|
||||
|
||||
/**
|
||||
* device_find_child_by_name - device iterator for locating a child device.
|
||||
* @parent: parent struct device
|
||||
* @name: name of the child device
|
||||
*
|
||||
* This is similar to the device_find_child() function above, but it
|
||||
* returns a reference to a device that has the name @name.
|
||||
*
|
||||
* NOTE: you will need to drop the reference with put_device() after use.
|
||||
*/
|
||||
struct device *device_find_child_by_name(struct device *parent,
|
||||
const char *name)
|
||||
{
|
||||
struct klist_iter i;
|
||||
struct device *child;
|
||||
|
||||
if (!parent)
|
||||
return NULL;
|
||||
|
||||
klist_iter_init(&parent->p->klist_children, &i);
|
||||
while ((child = next_device(&i)))
|
||||
if (!strcmp(dev_name(child), name) && get_device(child))
|
||||
break;
|
||||
klist_iter_exit(&i);
|
||||
return child;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_find_child_by_name);
|
||||
|
||||
int __init devices_init(void)
|
||||
{
|
||||
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
|
||||
|
|
|
@ -38,6 +38,28 @@ fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void *
|
||||
fwnode_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
|
||||
void *data, devcon_match_fn_t match)
|
||||
{
|
||||
struct device_connection con = { };
|
||||
void *ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
con.fwnode = fwnode_find_reference(fwnode, con_id, i);
|
||||
if (IS_ERR(con.fwnode))
|
||||
break;
|
||||
|
||||
ret = match(&con, -1, data);
|
||||
fwnode_handle_put(con.fwnode);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* device_connection_find_match - Find physical connection to a device
|
||||
* @dev: Device with the connection
|
||||
|
@ -65,6 +87,10 @@ void *device_connection_find_match(struct device *dev, const char *con_id,
|
|||
ret = fwnode_graph_devcon_match(fwnode, con_id, data, match);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = fwnode_devcon_match(fwnode, con_id, data, match);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&devcon_lock);
|
||||
|
|
|
@ -484,6 +484,30 @@ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args);
|
||||
|
||||
/**
|
||||
* fwnode_find_reference - Find named reference to a fwnode_handle
|
||||
* @fwnode: Firmware node where to look for the reference
|
||||
* @name: The name of the reference
|
||||
* @index: Index of the reference
|
||||
*
|
||||
* @index can be used when the named reference holds a table of references.
|
||||
*
|
||||
* Returns pointer to the reference fwnode, or ERR_PTR. Caller is responsible to
|
||||
* call fwnode_handle_put() on the returned fwnode pointer.
|
||||
*/
|
||||
struct fwnode_handle *fwnode_find_reference(const struct fwnode_handle *fwnode,
|
||||
const char *name,
|
||||
unsigned int index)
|
||||
{
|
||||
struct fwnode_reference_args args;
|
||||
int ret;
|
||||
|
||||
ret = fwnode_property_get_reference_args(fwnode, name, NULL, 0, index,
|
||||
&args);
|
||||
return ret ? ERR_PTR(ret) : args.fwnode;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_find_reference);
|
||||
|
||||
/**
|
||||
* device_remove_properties - Remove properties from a device object.
|
||||
* @dev: Device whose properties to remove.
|
||||
|
|
|
@ -11,25 +11,25 @@
|
|||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct software_node {
|
||||
struct swnode {
|
||||
int id;
|
||||
struct kobject kobj;
|
||||
struct fwnode_handle fwnode;
|
||||
const struct software_node *node;
|
||||
|
||||
/* hierarchy */
|
||||
struct ida child_ids;
|
||||
struct list_head entry;
|
||||
struct list_head children;
|
||||
struct software_node *parent;
|
||||
struct swnode *parent;
|
||||
|
||||
/* properties */
|
||||
const struct property_entry *properties;
|
||||
unsigned int allocated:1;
|
||||
};
|
||||
|
||||
static DEFINE_IDA(swnode_root_ids);
|
||||
static struct kset *swnode_kset;
|
||||
|
||||
#define kobj_to_swnode(_kobj_) container_of(_kobj_, struct software_node, kobj)
|
||||
#define kobj_to_swnode(_kobj_) container_of(_kobj_, struct swnode, kobj)
|
||||
|
||||
static const struct fwnode_operations software_node_ops;
|
||||
|
||||
|
@ -37,17 +37,56 @@ bool is_software_node(const struct fwnode_handle *fwnode)
|
|||
{
|
||||
return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &software_node_ops;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(is_software_node);
|
||||
|
||||
#define to_software_node(__fwnode) \
|
||||
#define to_swnode(__fwnode) \
|
||||
({ \
|
||||
typeof(__fwnode) __to_software_node_fwnode = __fwnode; \
|
||||
typeof(__fwnode) __to_swnode_fwnode = __fwnode; \
|
||||
\
|
||||
is_software_node(__to_software_node_fwnode) ? \
|
||||
container_of(__to_software_node_fwnode, \
|
||||
struct software_node, fwnode) : \
|
||||
NULL; \
|
||||
is_software_node(__to_swnode_fwnode) ? \
|
||||
container_of(__to_swnode_fwnode, \
|
||||
struct swnode, fwnode) : NULL; \
|
||||
})
|
||||
|
||||
static struct swnode *
|
||||
software_node_to_swnode(const struct software_node *node)
|
||||
{
|
||||
struct swnode *swnode;
|
||||
struct kobject *k;
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
spin_lock(&swnode_kset->list_lock);
|
||||
|
||||
list_for_each_entry(k, &swnode_kset->list, entry) {
|
||||
swnode = kobj_to_swnode(k);
|
||||
if (swnode->node == node)
|
||||
break;
|
||||
swnode = NULL;
|
||||
}
|
||||
|
||||
spin_unlock(&swnode_kset->list_lock);
|
||||
|
||||
return swnode;
|
||||
}
|
||||
|
||||
const struct software_node *to_software_node(struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
|
||||
return swnode ? swnode->node : NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(to_software_node);
|
||||
|
||||
struct fwnode_handle *software_node_fwnode(const struct software_node *node)
|
||||
{
|
||||
struct swnode *swnode = software_node_to_swnode(node);
|
||||
|
||||
return swnode ? &swnode->fwnode : NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(software_node_fwnode);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* property_entry processing */
|
||||
|
||||
|
@ -383,6 +422,9 @@ property_entries_dup(const struct property_entry *properties)
|
|||
int i, n = 0;
|
||||
int ret;
|
||||
|
||||
if (!properties)
|
||||
return NULL;
|
||||
|
||||
while (properties[n].name)
|
||||
n++;
|
||||
|
||||
|
@ -430,7 +472,7 @@ EXPORT_SYMBOL_GPL(property_entries_free);
|
|||
|
||||
static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct software_node *swnode = to_software_node(fwnode);
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
|
||||
kobject_get(&swnode->kobj);
|
||||
|
||||
|
@ -439,7 +481,7 @@ static struct fwnode_handle *software_node_get(struct fwnode_handle *fwnode)
|
|||
|
||||
static void software_node_put(struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct software_node *swnode = to_software_node(fwnode);
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
|
||||
kobject_put(&swnode->kobj);
|
||||
}
|
||||
|
@ -447,8 +489,9 @@ static void software_node_put(struct fwnode_handle *fwnode)
|
|||
static bool software_node_property_present(const struct fwnode_handle *fwnode,
|
||||
const char *propname)
|
||||
{
|
||||
return !!property_entry_get(to_software_node(fwnode)->properties,
|
||||
propname);
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
|
||||
return !!property_entry_get(swnode->node->properties, propname);
|
||||
}
|
||||
|
||||
static int software_node_read_int_array(const struct fwnode_handle *fwnode,
|
||||
|
@ -456,9 +499,9 @@ static int software_node_read_int_array(const struct fwnode_handle *fwnode,
|
|||
unsigned int elem_size, void *val,
|
||||
size_t nval)
|
||||
{
|
||||
struct software_node *swnode = to_software_node(fwnode);
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
|
||||
return property_entry_read_int_array(swnode->properties, propname,
|
||||
return property_entry_read_int_array(swnode->node->properties, propname,
|
||||
elem_size, val, nval);
|
||||
}
|
||||
|
||||
|
@ -466,27 +509,26 @@ static int software_node_read_string_array(const struct fwnode_handle *fwnode,
|
|||
const char *propname,
|
||||
const char **val, size_t nval)
|
||||
{
|
||||
struct software_node *swnode = to_software_node(fwnode);
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
|
||||
return property_entry_read_string_array(swnode->properties, propname,
|
||||
val, nval);
|
||||
return property_entry_read_string_array(swnode->node->properties,
|
||||
propname, val, nval);
|
||||
}
|
||||
|
||||
static struct fwnode_handle *
|
||||
software_node_get_parent(const struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct software_node *swnode = to_software_node(fwnode);
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
|
||||
return swnode ? (swnode->parent ? &swnode->parent->fwnode : NULL) :
|
||||
NULL;
|
||||
return swnode ? (swnode->parent ? &swnode->parent->fwnode : NULL) : NULL;
|
||||
}
|
||||
|
||||
static struct fwnode_handle *
|
||||
software_node_get_next_child(const struct fwnode_handle *fwnode,
|
||||
struct fwnode_handle *child)
|
||||
{
|
||||
struct software_node *p = to_software_node(fwnode);
|
||||
struct software_node *c = to_software_node(child);
|
||||
struct swnode *p = to_swnode(fwnode);
|
||||
struct swnode *c = to_swnode(child);
|
||||
|
||||
if (!p || list_empty(&p->children) ||
|
||||
(c && list_is_last(&c->entry, &p->children)))
|
||||
|
@ -495,7 +537,7 @@ software_node_get_next_child(const struct fwnode_handle *fwnode,
|
|||
if (c)
|
||||
c = list_next_entry(c, entry);
|
||||
else
|
||||
c = list_first_entry(&p->children, struct software_node, entry);
|
||||
c = list_first_entry(&p->children, struct swnode, entry);
|
||||
return &c->fwnode;
|
||||
}
|
||||
|
||||
|
@ -503,18 +545,14 @@ static struct fwnode_handle *
|
|||
software_node_get_named_child_node(const struct fwnode_handle *fwnode,
|
||||
const char *childname)
|
||||
{
|
||||
struct software_node *swnode = to_software_node(fwnode);
|
||||
const struct property_entry *prop;
|
||||
struct software_node *child;
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
struct swnode *child;
|
||||
|
||||
if (!swnode || list_empty(&swnode->children))
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(child, &swnode->children, entry) {
|
||||
prop = property_entry_get(child->properties, "name");
|
||||
if (!prop)
|
||||
continue;
|
||||
if (!strcmp(childname, prop->value.str)) {
|
||||
if (!strcmp(childname, kobject_name(&child->kobj))) {
|
||||
kobject_get(&child->kobj);
|
||||
return &child->fwnode;
|
||||
}
|
||||
|
@ -522,6 +560,52 @@ software_node_get_named_child_node(const struct fwnode_handle *fwnode,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
software_node_get_reference_args(const struct fwnode_handle *fwnode,
|
||||
const char *propname, const char *nargs_prop,
|
||||
unsigned int nargs, unsigned int index,
|
||||
struct fwnode_reference_args *args)
|
||||
{
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
const struct software_node_reference *ref;
|
||||
const struct property_entry *prop;
|
||||
struct fwnode_handle *refnode;
|
||||
int i;
|
||||
|
||||
if (!swnode || !swnode->node->references)
|
||||
return -ENOENT;
|
||||
|
||||
for (ref = swnode->node->references; ref->name; ref++)
|
||||
if (!strcmp(ref->name, propname))
|
||||
break;
|
||||
|
||||
if (!ref->name || index > (ref->nrefs - 1))
|
||||
return -ENOENT;
|
||||
|
||||
refnode = software_node_fwnode(ref->refs[index].node);
|
||||
if (!refnode)
|
||||
return -ENOENT;
|
||||
|
||||
if (nargs_prop) {
|
||||
prop = property_entry_get(swnode->node->properties, nargs_prop);
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
|
||||
nargs = prop->value.u32_data;
|
||||
}
|
||||
|
||||
if (nargs > NR_FWNODE_REFERENCE_ARGS)
|
||||
return -EINVAL;
|
||||
|
||||
args->fwnode = software_node_get(refnode);
|
||||
args->nargs = nargs;
|
||||
|
||||
for (i = 0; i < nargs; i++)
|
||||
args->args[i] = ref->refs[index].args[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fwnode_operations software_node_ops = {
|
||||
.get = software_node_get,
|
||||
.put = software_node_put,
|
||||
|
@ -531,12 +615,13 @@ static const struct fwnode_operations software_node_ops = {
|
|||
.get_parent = software_node_get_parent,
|
||||
.get_next_child_node = software_node_get_next_child,
|
||||
.get_named_child_node = software_node_get_named_child_node,
|
||||
.get_reference_args = software_node_get_reference_args
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int
|
||||
software_node_register_properties(struct software_node *swnode,
|
||||
software_node_register_properties(struct software_node *node,
|
||||
const struct property_entry *properties)
|
||||
{
|
||||
struct property_entry *props;
|
||||
|
@ -545,24 +630,20 @@ software_node_register_properties(struct software_node *swnode,
|
|||
if (IS_ERR(props))
|
||||
return PTR_ERR(props);
|
||||
|
||||
swnode->properties = props;
|
||||
node->properties = props;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void software_node_release(struct kobject *kobj)
|
||||
{
|
||||
struct software_node *swnode = kobj_to_swnode(kobj);
|
||||
struct swnode *swnode = kobj_to_swnode(kobj);
|
||||
|
||||
if (swnode->parent) {
|
||||
ida_simple_remove(&swnode->parent->child_ids, swnode->id);
|
||||
list_del(&swnode->entry);
|
||||
} else {
|
||||
ida_simple_remove(&swnode_root_ids, swnode->id);
|
||||
if (swnode->allocated) {
|
||||
property_entries_free(swnode->node->properties);
|
||||
kfree(swnode->node);
|
||||
}
|
||||
|
||||
ida_destroy(&swnode->child_ids);
|
||||
property_entries_free(swnode->properties);
|
||||
kfree(swnode);
|
||||
}
|
||||
|
||||
|
@ -571,12 +652,125 @@ static struct kobj_type software_node_type = {
|
|||
.sysfs_ops = &kobj_sysfs_ops,
|
||||
};
|
||||
|
||||
static struct fwnode_handle *
|
||||
swnode_register(const struct software_node *node, struct swnode *parent,
|
||||
unsigned int allocated)
|
||||
{
|
||||
struct swnode *swnode;
|
||||
int ret;
|
||||
|
||||
swnode = kzalloc(sizeof(*swnode), GFP_KERNEL);
|
||||
if (!swnode) {
|
||||
ret = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids,
|
||||
0, 0, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
kfree(swnode);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
swnode->id = ret;
|
||||
swnode->node = node;
|
||||
swnode->parent = parent;
|
||||
swnode->allocated = allocated;
|
||||
swnode->kobj.kset = swnode_kset;
|
||||
swnode->fwnode.ops = &software_node_ops;
|
||||
|
||||
ida_init(&swnode->child_ids);
|
||||
INIT_LIST_HEAD(&swnode->entry);
|
||||
INIT_LIST_HEAD(&swnode->children);
|
||||
|
||||
if (node->name)
|
||||
ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
|
||||
parent ? &parent->kobj : NULL,
|
||||
"%s", node->name);
|
||||
else
|
||||
ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
|
||||
parent ? &parent->kobj : NULL,
|
||||
"node%d", swnode->id);
|
||||
if (ret) {
|
||||
kobject_put(&swnode->kobj);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (parent)
|
||||
list_add_tail(&swnode->entry, &parent->children);
|
||||
|
||||
kobject_uevent(&swnode->kobj, KOBJ_ADD);
|
||||
return &swnode->fwnode;
|
||||
|
||||
out_err:
|
||||
if (allocated)
|
||||
property_entries_free(node->properties);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* software_node_register_nodes - Register an array of software nodes
|
||||
* @nodes: Zero terminated array of software nodes to be registered
|
||||
*
|
||||
* Register multiple software nodes at once.
|
||||
*/
|
||||
int software_node_register_nodes(const struct software_node *nodes)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; nodes[i].name; i++) {
|
||||
ret = software_node_register(&nodes[i]);
|
||||
if (ret) {
|
||||
software_node_unregister_nodes(nodes);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(software_node_register_nodes);
|
||||
|
||||
/**
|
||||
* software_node_unregister_nodes - Unregister an array of software nodes
|
||||
* @nodes: Zero terminated array of software nodes to be unregistered
|
||||
*
|
||||
* Unregister multiple software nodes at once.
|
||||
*/
|
||||
void software_node_unregister_nodes(const struct software_node *nodes)
|
||||
{
|
||||
struct swnode *swnode;
|
||||
int i;
|
||||
|
||||
for (i = 0; nodes[i].name; i++) {
|
||||
swnode = software_node_to_swnode(&nodes[i]);
|
||||
if (swnode)
|
||||
fwnode_remove_software_node(&swnode->fwnode);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
|
||||
|
||||
/**
|
||||
* software_node_register - Register static software node
|
||||
* @node: The software node to be registered
|
||||
*/
|
||||
int software_node_register(const struct software_node *node)
|
||||
{
|
||||
struct swnode *parent = software_node_to_swnode(node->parent);
|
||||
|
||||
if (software_node_to_swnode(node))
|
||||
return -EEXIST;
|
||||
|
||||
return PTR_ERR_OR_ZERO(swnode_register(node, parent, 0));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(software_node_register);
|
||||
|
||||
struct fwnode_handle *
|
||||
fwnode_create_software_node(const struct property_entry *properties,
|
||||
const struct fwnode_handle *parent)
|
||||
{
|
||||
struct software_node *p = NULL;
|
||||
struct software_node *swnode;
|
||||
struct software_node *node;
|
||||
struct swnode *p = NULL;
|
||||
int ret;
|
||||
|
||||
if (parent) {
|
||||
|
@ -584,57 +778,39 @@ fwnode_create_software_node(const struct property_entry *properties,
|
|||
return ERR_CAST(parent);
|
||||
if (!is_software_node(parent))
|
||||
return ERR_PTR(-EINVAL);
|
||||
p = to_software_node(parent);
|
||||
p = to_swnode(parent);
|
||||
}
|
||||
|
||||
swnode = kzalloc(sizeof(*swnode), GFP_KERNEL);
|
||||
if (!swnode)
|
||||
node = kzalloc(sizeof(*node), GFP_KERNEL);
|
||||
if (!node)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = ida_simple_get(p ? &p->child_ids : &swnode_root_ids, 0, 0,
|
||||
GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
kfree(swnode);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
swnode->id = ret;
|
||||
swnode->kobj.kset = swnode_kset;
|
||||
swnode->fwnode.ops = &software_node_ops;
|
||||
|
||||
ida_init(&swnode->child_ids);
|
||||
INIT_LIST_HEAD(&swnode->entry);
|
||||
INIT_LIST_HEAD(&swnode->children);
|
||||
swnode->parent = p;
|
||||
|
||||
if (p)
|
||||
list_add_tail(&swnode->entry, &p->children);
|
||||
|
||||
ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
|
||||
p ? &p->kobj : NULL, "node%d", swnode->id);
|
||||
ret = software_node_register_properties(node, properties);
|
||||
if (ret) {
|
||||
kobject_put(&swnode->kobj);
|
||||
kfree(node);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = software_node_register_properties(swnode, properties);
|
||||
if (ret) {
|
||||
kobject_put(&swnode->kobj);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
node->parent = p ? p->node : NULL;
|
||||
|
||||
kobject_uevent(&swnode->kobj, KOBJ_ADD);
|
||||
return &swnode->fwnode;
|
||||
return swnode_register(node, p, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_create_software_node);
|
||||
|
||||
void fwnode_remove_software_node(struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct software_node *swnode = to_software_node(fwnode);
|
||||
struct swnode *swnode = to_swnode(fwnode);
|
||||
|
||||
if (!swnode)
|
||||
return;
|
||||
|
||||
if (swnode->parent) {
|
||||
ida_simple_remove(&swnode->parent->child_ids, swnode->id);
|
||||
list_del(&swnode->entry);
|
||||
} else {
|
||||
ida_simple_remove(&swnode_root_ids, swnode->id);
|
||||
}
|
||||
|
||||
kobject_put(&swnode->kobj);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
|
||||
|
@ -642,7 +818,7 @@ EXPORT_SYMBOL_GPL(fwnode_remove_software_node);
|
|||
int software_node_notify(struct device *dev, unsigned long action)
|
||||
{
|
||||
struct fwnode_handle *fwnode = dev_fwnode(dev);
|
||||
struct software_node *swnode;
|
||||
struct swnode *swnode;
|
||||
int ret;
|
||||
|
||||
if (!fwnode)
|
||||
|
@ -653,7 +829,7 @@ int software_node_notify(struct device *dev, unsigned long action)
|
|||
if (!is_software_node(fwnode))
|
||||
return 0;
|
||||
|
||||
swnode = to_software_node(fwnode);
|
||||
swnode = to_swnode(fwnode);
|
||||
|
||||
switch (action) {
|
||||
case KOBJ_ADD:
|
||||
|
|
|
@ -21,18 +21,55 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb/pd.h>
|
||||
|
||||
#define EXPECTED_PTYPE 4
|
||||
|
||||
enum {
|
||||
INT33FE_NODE_FUSB302,
|
||||
INT33FE_NODE_MAX17047,
|
||||
INT33FE_NODE_PI3USB30532,
|
||||
INT33FE_NODE_DISPLAYPORT,
|
||||
INT33FE_NODE_ROLE_SWITCH,
|
||||
INT33FE_NODE_USB_CONNECTOR,
|
||||
INT33FE_NODE_MAX,
|
||||
};
|
||||
|
||||
struct cht_int33fe_data {
|
||||
struct i2c_client *max17047;
|
||||
struct i2c_client *fusb302;
|
||||
struct i2c_client *pi3usb30532;
|
||||
/* Contain a list-head must be per device */
|
||||
struct device_connection connections[4];
|
||||
|
||||
struct fwnode_handle *dp;
|
||||
struct fwnode_handle *mux;
|
||||
};
|
||||
|
||||
static const struct software_node nodes[];
|
||||
|
||||
static const struct software_node_ref_args pi3usb30532_ref = {
|
||||
&nodes[INT33FE_NODE_PI3USB30532]
|
||||
};
|
||||
|
||||
static const struct software_node_ref_args dp_ref = {
|
||||
&nodes[INT33FE_NODE_DISPLAYPORT]
|
||||
};
|
||||
|
||||
static struct software_node_ref_args mux_ref;
|
||||
|
||||
static const struct software_node_reference usb_connector_refs[] = {
|
||||
{ "orientation-switch", 1, &pi3usb30532_ref},
|
||||
{ "mode-switch", 1, &pi3usb30532_ref},
|
||||
{ "displayport", 1, &dp_ref},
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node_reference fusb302_refs[] = {
|
||||
{ "usb-role-switch", 1, &mux_ref},
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -63,14 +100,6 @@ static int cht_int33fe_check_for_max17047(struct device *dev, void *data)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static struct i2c_client *cht_int33fe_find_max17047(void)
|
||||
{
|
||||
struct i2c_client *max17047 = NULL;
|
||||
|
||||
i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047);
|
||||
return max17047;
|
||||
}
|
||||
|
||||
static const char * const max17047_suppliers[] = { "bq24190-charger" };
|
||||
|
||||
static const struct property_entry max17047_props[] = {
|
||||
|
@ -80,18 +109,196 @@ static const struct property_entry max17047_props[] = {
|
|||
|
||||
static const struct property_entry fusb302_props[] = {
|
||||
PROPERTY_ENTRY_STRING("linux,extcon-name", "cht_wcove_pwrsrc"),
|
||||
PROPERTY_ENTRY_U32("fcs,max-sink-microvolt", 12000000),
|
||||
PROPERTY_ENTRY_U32("fcs,max-sink-microamp", 3000000),
|
||||
PROPERTY_ENTRY_U32("fcs,max-sink-microwatt", 36000000),
|
||||
{ }
|
||||
};
|
||||
|
||||
#define PDO_FIXED_FLAGS \
|
||||
(PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM)
|
||||
|
||||
static const u32 src_pdo[] = {
|
||||
PDO_FIXED(5000, 1500, PDO_FIXED_FLAGS),
|
||||
};
|
||||
|
||||
static const u32 snk_pdo[] = {
|
||||
PDO_FIXED(5000, 400, PDO_FIXED_FLAGS),
|
||||
PDO_VAR(5000, 12000, 3000),
|
||||
};
|
||||
|
||||
static const struct property_entry usb_connector_props[] = {
|
||||
PROPERTY_ENTRY_STRING("data-role", "dual"),
|
||||
PROPERTY_ENTRY_STRING("power-role", "dual"),
|
||||
PROPERTY_ENTRY_STRING("try-power-role", "sink"),
|
||||
PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo),
|
||||
PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo),
|
||||
PROPERTY_ENTRY_U32("op-sink-microwatt", 2500000),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node nodes[] = {
|
||||
{ "fusb302", NULL, fusb302_props, fusb302_refs },
|
||||
{ "max17047", NULL, max17047_props },
|
||||
{ "pi3usb30532" },
|
||||
{ "displayport" },
|
||||
{ "usb-role-switch" },
|
||||
{ "connector", &nodes[0], usb_connector_props, usb_connector_refs },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int cht_int33fe_setup_mux(struct cht_int33fe_data *data)
|
||||
{
|
||||
struct fwnode_handle *fwnode;
|
||||
struct device *dev;
|
||||
struct device *p;
|
||||
|
||||
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_ROLE_SWITCH]);
|
||||
if (!fwnode)
|
||||
return -ENODEV;
|
||||
|
||||
/* First finding the platform device */
|
||||
p = bus_find_device_by_name(&platform_bus_type, NULL,
|
||||
"intel_xhci_usb_sw");
|
||||
if (!p)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/* Then the mux child device */
|
||||
dev = device_find_child_by_name(p, "intel_xhci_usb_sw-role-switch");
|
||||
put_device(p);
|
||||
if (!dev)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/* If there already is a node for the mux, using that one. */
|
||||
if (dev->fwnode)
|
||||
fwnode_remove_software_node(fwnode);
|
||||
else
|
||||
dev->fwnode = fwnode;
|
||||
|
||||
data->mux = fwnode_handle_get(dev->fwnode);
|
||||
put_device(dev);
|
||||
mux_ref.node = to_software_node(data->mux);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cht_int33fe_setup_dp(struct cht_int33fe_data *data)
|
||||
{
|
||||
struct fwnode_handle *fwnode;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_DISPLAYPORT]);
|
||||
if (!fwnode)
|
||||
return -ENODEV;
|
||||
|
||||
/* First let's find the GPU PCI device */
|
||||
pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL);
|
||||
if (!pdev || pdev->vendor != PCI_VENDOR_ID_INTEL) {
|
||||
pci_dev_put(pdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Then the DP child device node */
|
||||
data->dp = device_get_named_child_node(&pdev->dev, "DD02");
|
||||
pci_dev_put(pdev);
|
||||
if (!data->dp)
|
||||
return -ENODEV;
|
||||
|
||||
fwnode->secondary = ERR_PTR(-ENODEV);
|
||||
data->dp->secondary = fwnode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cht_int33fe_remove_nodes(struct cht_int33fe_data *data)
|
||||
{
|
||||
software_node_unregister_nodes(nodes);
|
||||
|
||||
if (data->mux) {
|
||||
fwnode_handle_put(data->mux);
|
||||
mux_ref.node = NULL;
|
||||
data->mux = NULL;
|
||||
}
|
||||
|
||||
if (data->dp) {
|
||||
data->dp->secondary = NULL;
|
||||
fwnode_handle_put(data->dp);
|
||||
data->dp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int cht_int33fe_add_nodes(struct cht_int33fe_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = software_node_register_nodes(nodes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* The devices that are not created in this driver need extra steps. */
|
||||
|
||||
/*
|
||||
* There is no ACPI device node for the USB role mux, so we need to find
|
||||
* the mux device and assign our node directly to it. That means we
|
||||
* depend on the mux driver. This function will return -PROBE_DEFER
|
||||
* until the mux device is registered.
|
||||
*/
|
||||
ret = cht_int33fe_setup_mux(data);
|
||||
if (ret)
|
||||
goto err_remove_nodes;
|
||||
|
||||
/*
|
||||
* The DP connector does have ACPI device node. In this case we can just
|
||||
* find that ACPI node and assign our node as the secondary node to it.
|
||||
*/
|
||||
ret = cht_int33fe_setup_dp(data);
|
||||
if (ret)
|
||||
goto err_remove_nodes;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_nodes:
|
||||
cht_int33fe_remove_nodes(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
cht_int33fe_register_max17047(struct device *dev, struct cht_int33fe_data *data)
|
||||
{
|
||||
struct i2c_client *max17047 = NULL;
|
||||
struct i2c_board_info board_info;
|
||||
struct fwnode_handle *fwnode;
|
||||
int ret;
|
||||
|
||||
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_MAX17047]);
|
||||
if (!fwnode)
|
||||
return -ENODEV;
|
||||
|
||||
i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047);
|
||||
if (max17047) {
|
||||
/* Pre-existing i2c-client for the max17047, add device-props */
|
||||
fwnode->secondary = ERR_PTR(-ENODEV);
|
||||
max17047->dev.fwnode->secondary = fwnode;
|
||||
/* And re-probe to get the new device-props applied. */
|
||||
ret = device_reprobe(&max17047->dev);
|
||||
if (ret)
|
||||
dev_warn(dev, "Reprobing max17047 error: %d\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&board_info, 0, sizeof(board_info));
|
||||
strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
|
||||
board_info.dev_name = "max17047";
|
||||
board_info.fwnode = fwnode;
|
||||
data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
|
||||
|
||||
return PTR_ERR_OR_ZERO(data->max17047);
|
||||
}
|
||||
|
||||
static int cht_int33fe_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct i2c_board_info board_info;
|
||||
struct cht_int33fe_data *data;
|
||||
struct i2c_client *max17047;
|
||||
struct fwnode_handle *fwnode;
|
||||
struct regulator *regulator;
|
||||
unsigned long long ptyp;
|
||||
acpi_status status;
|
||||
|
@ -151,43 +358,25 @@ static int cht_int33fe_probe(struct platform_device *pdev)
|
|||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Work around BIOS bug, see comment on cht_int33fe_find_max17047 */
|
||||
max17047 = cht_int33fe_find_max17047();
|
||||
if (max17047) {
|
||||
/* Pre-existing i2c-client for the max17047, add device-props */
|
||||
ret = device_add_properties(&max17047->dev, max17047_props);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* And re-probe to get the new device-props applied. */
|
||||
ret = device_reprobe(&max17047->dev);
|
||||
if (ret)
|
||||
dev_warn(dev, "Reprobing max17047 error: %d\n", ret);
|
||||
} else {
|
||||
memset(&board_info, 0, sizeof(board_info));
|
||||
strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
|
||||
board_info.dev_name = "max17047";
|
||||
board_info.properties = max17047_props;
|
||||
data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
|
||||
if (IS_ERR(data->max17047))
|
||||
return PTR_ERR(data->max17047);
|
||||
ret = cht_int33fe_add_nodes(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047 */
|
||||
ret = cht_int33fe_register_max17047(dev, data);
|
||||
if (ret)
|
||||
goto out_remove_nodes;
|
||||
|
||||
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_FUSB302]);
|
||||
if (!fwnode) {
|
||||
ret = -ENODEV;
|
||||
goto out_unregister_max17047;
|
||||
}
|
||||
|
||||
data->connections[0].endpoint[0] = "port0";
|
||||
data->connections[0].endpoint[1] = "i2c-pi3usb30532";
|
||||
data->connections[0].id = "orientation-switch";
|
||||
data->connections[1].endpoint[0] = "port0";
|
||||
data->connections[1].endpoint[1] = "i2c-pi3usb30532";
|
||||
data->connections[1].id = "mode-switch";
|
||||
data->connections[2].endpoint[0] = "i2c-fusb302";
|
||||
data->connections[2].endpoint[1] = "intel_xhci_usb_sw-role-switch";
|
||||
data->connections[2].id = "usb-role-switch";
|
||||
|
||||
device_connections_add(data->connections);
|
||||
|
||||
memset(&board_info, 0, sizeof(board_info));
|
||||
strlcpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE);
|
||||
board_info.dev_name = "fusb302";
|
||||
board_info.properties = fusb302_props;
|
||||
board_info.fwnode = fwnode;
|
||||
board_info.irq = fusb302_irq;
|
||||
|
||||
data->fusb302 = i2c_acpi_new_device(dev, 2, &board_info);
|
||||
|
@ -196,8 +385,15 @@ static int cht_int33fe_probe(struct platform_device *pdev)
|
|||
goto out_unregister_max17047;
|
||||
}
|
||||
|
||||
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_PI3USB30532]);
|
||||
if (!fwnode) {
|
||||
ret = -ENODEV;
|
||||
goto out_unregister_fusb302;
|
||||
}
|
||||
|
||||
memset(&board_info, 0, sizeof(board_info));
|
||||
board_info.dev_name = "pi3usb30532";
|
||||
board_info.fwnode = fwnode;
|
||||
strlcpy(board_info.type, "pi3usb30532", I2C_NAME_SIZE);
|
||||
|
||||
data->pi3usb30532 = i2c_acpi_new_device(dev, 3, &board_info);
|
||||
|
@ -216,7 +412,8 @@ out_unregister_fusb302:
|
|||
out_unregister_max17047:
|
||||
i2c_unregister_device(data->max17047);
|
||||
|
||||
device_connections_remove(data->connections);
|
||||
out_remove_nodes:
|
||||
cht_int33fe_remove_nodes(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -229,7 +426,7 @@ static int cht_int33fe_remove(struct platform_device *pdev)
|
|||
i2c_unregister_device(data->fusb302);
|
||||
i2c_unregister_device(data->max17047);
|
||||
|
||||
device_connections_remove(data->connections);
|
||||
cht_int33fe_remove_nodes(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ static void *usb_role_switch_match(struct device_connection *con, int ep,
|
|||
struct device *dev;
|
||||
|
||||
if (con->fwnode) {
|
||||
if (!fwnode_property_present(con->fwnode, con->id))
|
||||
if (con->id && !fwnode_property_present(con->fwnode, con->id))
|
||||
return NULL;
|
||||
|
||||
dev = class_find_device(role_class, NULL, con->fwnode,
|
||||
|
|
|
@ -35,4 +35,19 @@ extern const struct device_type typec_port_dev_type;
|
|||
#define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type)
|
||||
#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type)
|
||||
|
||||
extern struct class typec_mux_class;
|
||||
|
||||
struct typec_switch {
|
||||
struct device dev;
|
||||
typec_switch_set_fn_t set;
|
||||
};
|
||||
|
||||
struct typec_mux {
|
||||
struct device dev;
|
||||
typec_mux_set_fn_t set;
|
||||
};
|
||||
|
||||
#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev)
|
||||
#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev)
|
||||
|
||||
#endif /* __USB_TYPEC_ALTMODE_H__ */
|
||||
|
|
|
@ -1646,13 +1646,25 @@ static int __init typec_init(void)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = class_register(&typec_mux_class);
|
||||
if (ret)
|
||||
goto err_unregister_bus;
|
||||
|
||||
typec_class = class_create(THIS_MODULE, "typec");
|
||||
if (IS_ERR(typec_class)) {
|
||||
bus_unregister(&typec_bus);
|
||||
return PTR_ERR(typec_class);
|
||||
ret = PTR_ERR(typec_class);
|
||||
goto err_unregister_mux_class;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_mux_class:
|
||||
class_unregister(&typec_mux_class);
|
||||
|
||||
err_unregister_bus:
|
||||
bus_unregister(&typec_bus);
|
||||
|
||||
return ret;
|
||||
}
|
||||
subsys_initcall(typec_init);
|
||||
|
||||
|
@ -1661,6 +1673,7 @@ static void __exit typec_exit(void)
|
|||
class_destroy(typec_class);
|
||||
ida_destroy(&typec_index_ida);
|
||||
bus_unregister(&typec_bus);
|
||||
class_unregister(&typec_mux_class);
|
||||
}
|
||||
module_exit(typec_exit);
|
||||
|
||||
|
|
|
@ -15,35 +15,47 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/usb/typec_mux.h>
|
||||
|
||||
static DEFINE_MUTEX(switch_lock);
|
||||
static DEFINE_MUTEX(mux_lock);
|
||||
static LIST_HEAD(switch_list);
|
||||
static LIST_HEAD(mux_list);
|
||||
#include "bus.h"
|
||||
|
||||
static int name_match(struct device *dev, const void *name)
|
||||
{
|
||||
return !strcmp((const char *)name, dev_name(dev));
|
||||
}
|
||||
|
||||
static bool dev_name_ends_with(struct device *dev, const char *suffix)
|
||||
{
|
||||
const char *name = dev_name(dev);
|
||||
const int name_len = strlen(name);
|
||||
const int suffix_len = strlen(suffix);
|
||||
|
||||
if (suffix_len > name_len)
|
||||
return false;
|
||||
|
||||
return strcmp(name + (name_len - suffix_len), suffix) == 0;
|
||||
}
|
||||
|
||||
static int switch_fwnode_match(struct device *dev, const void *fwnode)
|
||||
{
|
||||
return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-switch");
|
||||
}
|
||||
|
||||
static void *typec_switch_match(struct device_connection *con, int ep,
|
||||
void *data)
|
||||
{
|
||||
struct typec_switch *sw;
|
||||
struct device *dev;
|
||||
|
||||
if (!con->fwnode) {
|
||||
list_for_each_entry(sw, &switch_list, entry)
|
||||
if (!strcmp(con->endpoint[ep], dev_name(sw->dev)))
|
||||
return sw;
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
if (con->fwnode) {
|
||||
if (con->id && !fwnode_property_present(con->fwnode, con->id))
|
||||
return NULL;
|
||||
|
||||
dev = class_find_device(&typec_mux_class, NULL, con->fwnode,
|
||||
switch_fwnode_match);
|
||||
} else {
|
||||
dev = class_find_device(&typec_mux_class, NULL,
|
||||
con->endpoint[ep], name_match);
|
||||
}
|
||||
|
||||
/*
|
||||
* With OF graph the mux node must have a boolean device property named
|
||||
* "orientation-switch".
|
||||
*/
|
||||
if (con->id && !fwnode_property_present(con->fwnode, con->id))
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(sw, &switch_list, entry)
|
||||
if (dev_fwnode(sw->dev) == con->fwnode)
|
||||
return sw;
|
||||
|
||||
return con->id ? ERR_PTR(-EPROBE_DEFER) : NULL;
|
||||
return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,14 +71,10 @@ struct typec_switch *typec_switch_get(struct device *dev)
|
|||
{
|
||||
struct typec_switch *sw;
|
||||
|
||||
mutex_lock(&switch_lock);
|
||||
sw = device_connection_find_match(dev, "orientation-switch", NULL,
|
||||
typec_switch_match);
|
||||
if (!IS_ERR_OR_NULL(sw)) {
|
||||
WARN_ON(!try_module_get(sw->dev->driver->owner));
|
||||
get_device(sw->dev);
|
||||
}
|
||||
mutex_unlock(&switch_lock);
|
||||
if (!IS_ERR_OR_NULL(sw))
|
||||
WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
|
||||
|
||||
return sw;
|
||||
}
|
||||
|
@ -81,28 +89,64 @@ EXPORT_SYMBOL_GPL(typec_switch_get);
|
|||
void typec_switch_put(struct typec_switch *sw)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(sw)) {
|
||||
module_put(sw->dev->driver->owner);
|
||||
put_device(sw->dev);
|
||||
module_put(sw->dev.parent->driver->owner);
|
||||
put_device(&sw->dev);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_switch_put);
|
||||
|
||||
static void typec_switch_release(struct device *dev)
|
||||
{
|
||||
kfree(to_typec_switch(dev));
|
||||
}
|
||||
|
||||
static const struct device_type typec_switch_dev_type = {
|
||||
.name = "orientation_switch",
|
||||
.release = typec_switch_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* typec_switch_register - Register USB Type-C orientation switch
|
||||
* @sw: USB Type-C orientation switch
|
||||
* @parent: Parent device
|
||||
* @desc: Orientation switch description
|
||||
*
|
||||
* This function registers a switch that can be used for routing the correct
|
||||
* data pairs depending on the cable plug orientation from the USB Type-C
|
||||
* connector to the USB controllers. USB Type-C plugs can be inserted
|
||||
* right-side-up or upside-down.
|
||||
*/
|
||||
int typec_switch_register(struct typec_switch *sw)
|
||||
struct typec_switch *
|
||||
typec_switch_register(struct device *parent,
|
||||
const struct typec_switch_desc *desc)
|
||||
{
|
||||
mutex_lock(&switch_lock);
|
||||
list_add_tail(&sw->entry, &switch_list);
|
||||
mutex_unlock(&switch_lock);
|
||||
struct typec_switch *sw;
|
||||
int ret;
|
||||
|
||||
return 0;
|
||||
if (!desc || !desc->set)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
sw = kzalloc(sizeof(*sw), GFP_KERNEL);
|
||||
if (!sw)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
sw->set = desc->set;
|
||||
|
||||
device_initialize(&sw->dev);
|
||||
sw->dev.parent = parent;
|
||||
sw->dev.fwnode = desc->fwnode;
|
||||
sw->dev.class = &typec_mux_class;
|
||||
sw->dev.type = &typec_switch_dev_type;
|
||||
sw->dev.driver_data = desc->drvdata;
|
||||
dev_set_name(&sw->dev, "%s-switch", dev_name(parent));
|
||||
|
||||
ret = device_add(&sw->dev);
|
||||
if (ret) {
|
||||
dev_err(parent, "failed to register switch (%d)\n", ret);
|
||||
put_device(&sw->dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return sw;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_switch_register);
|
||||
|
||||
|
@ -114,28 +158,44 @@ EXPORT_SYMBOL_GPL(typec_switch_register);
|
|||
*/
|
||||
void typec_switch_unregister(struct typec_switch *sw)
|
||||
{
|
||||
mutex_lock(&switch_lock);
|
||||
list_del(&sw->entry);
|
||||
mutex_unlock(&switch_lock);
|
||||
if (!IS_ERR_OR_NULL(sw))
|
||||
device_unregister(&sw->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_switch_unregister);
|
||||
|
||||
void typec_switch_set_drvdata(struct typec_switch *sw, void *data)
|
||||
{
|
||||
dev_set_drvdata(&sw->dev, data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_switch_set_drvdata);
|
||||
|
||||
void *typec_switch_get_drvdata(struct typec_switch *sw)
|
||||
{
|
||||
return dev_get_drvdata(&sw->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_switch_get_drvdata);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static int mux_fwnode_match(struct device *dev, const void *fwnode)
|
||||
{
|
||||
return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-mux");
|
||||
}
|
||||
|
||||
static void *typec_mux_match(struct device_connection *con, int ep, void *data)
|
||||
{
|
||||
const struct typec_altmode_desc *desc = data;
|
||||
struct typec_mux *mux;
|
||||
int nval;
|
||||
struct device *dev;
|
||||
bool match;
|
||||
int nval;
|
||||
u16 *val;
|
||||
int i;
|
||||
|
||||
if (!con->fwnode) {
|
||||
list_for_each_entry(mux, &mux_list, entry)
|
||||
if (!strcmp(con->endpoint[ep], dev_name(mux->dev)))
|
||||
return mux;
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
dev = class_find_device(&typec_mux_class, NULL,
|
||||
con->endpoint[ep], name_match);
|
||||
|
||||
return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -180,11 +240,10 @@ static void *typec_mux_match(struct device_connection *con, int ep, void *data)
|
|||
return NULL;
|
||||
|
||||
find_mux:
|
||||
list_for_each_entry(mux, &mux_list, entry)
|
||||
if (dev_fwnode(mux->dev) == con->fwnode)
|
||||
return mux;
|
||||
dev = class_find_device(&typec_mux_class, NULL, con->fwnode,
|
||||
mux_fwnode_match);
|
||||
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -202,14 +261,10 @@ struct typec_mux *typec_mux_get(struct device *dev,
|
|||
{
|
||||
struct typec_mux *mux;
|
||||
|
||||
mutex_lock(&mux_lock);
|
||||
mux = device_connection_find_match(dev, "mode-switch", (void *)desc,
|
||||
typec_mux_match);
|
||||
if (!IS_ERR_OR_NULL(mux)) {
|
||||
WARN_ON(!try_module_get(mux->dev->driver->owner));
|
||||
get_device(mux->dev);
|
||||
}
|
||||
mutex_unlock(&mux_lock);
|
||||
if (!IS_ERR_OR_NULL(mux))
|
||||
WARN_ON(!try_module_get(mux->dev.parent->driver->owner));
|
||||
|
||||
return mux;
|
||||
}
|
||||
|
@ -224,28 +279,63 @@ EXPORT_SYMBOL_GPL(typec_mux_get);
|
|||
void typec_mux_put(struct typec_mux *mux)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(mux)) {
|
||||
module_put(mux->dev->driver->owner);
|
||||
put_device(mux->dev);
|
||||
module_put(mux->dev.parent->driver->owner);
|
||||
put_device(&mux->dev);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_mux_put);
|
||||
|
||||
static void typec_mux_release(struct device *dev)
|
||||
{
|
||||
kfree(to_typec_mux(dev));
|
||||
}
|
||||
|
||||
static const struct device_type typec_mux_dev_type = {
|
||||
.name = "mode_switch",
|
||||
.release = typec_mux_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* typec_mux_register - Register Multiplexer routing USB Type-C pins
|
||||
* @mux: USB Type-C Connector Multiplexer/DeMultiplexer
|
||||
* @parent: Parent device
|
||||
* @desc: Multiplexer description
|
||||
*
|
||||
* USB Type-C connectors can be used for alternate modes of operation besides
|
||||
* USB when Accessory/Alternate Modes are supported. With some of those modes,
|
||||
* the pins on the connector need to be reconfigured. This function registers
|
||||
* multiplexer switches routing the pins on the connector.
|
||||
*/
|
||||
int typec_mux_register(struct typec_mux *mux)
|
||||
struct typec_mux *
|
||||
typec_mux_register(struct device *parent, const struct typec_mux_desc *desc)
|
||||
{
|
||||
mutex_lock(&mux_lock);
|
||||
list_add_tail(&mux->entry, &mux_list);
|
||||
mutex_unlock(&mux_lock);
|
||||
struct typec_mux *mux;
|
||||
int ret;
|
||||
|
||||
return 0;
|
||||
if (!desc || !desc->set)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mux->set = desc->set;
|
||||
|
||||
device_initialize(&mux->dev);
|
||||
mux->dev.parent = parent;
|
||||
mux->dev.fwnode = desc->fwnode;
|
||||
mux->dev.class = &typec_mux_class;
|
||||
mux->dev.type = &typec_mux_dev_type;
|
||||
mux->dev.driver_data = desc->drvdata;
|
||||
dev_set_name(&mux->dev, "%s-mux", dev_name(parent));
|
||||
|
||||
ret = device_add(&mux->dev);
|
||||
if (ret) {
|
||||
dev_err(parent, "failed to register mux (%d)\n", ret);
|
||||
put_device(&mux->dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return mux;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_mux_register);
|
||||
|
||||
|
@ -257,8 +347,24 @@ EXPORT_SYMBOL_GPL(typec_mux_register);
|
|||
*/
|
||||
void typec_mux_unregister(struct typec_mux *mux)
|
||||
{
|
||||
mutex_lock(&mux_lock);
|
||||
list_del(&mux->entry);
|
||||
mutex_unlock(&mux_lock);
|
||||
if (!IS_ERR_OR_NULL(mux))
|
||||
device_unregister(&mux->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_mux_unregister);
|
||||
|
||||
void typec_mux_set_drvdata(struct typec_mux *mux, void *data)
|
||||
{
|
||||
dev_set_drvdata(&mux->dev, data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_mux_set_drvdata);
|
||||
|
||||
void *typec_mux_get_drvdata(struct typec_mux *mux)
|
||||
{
|
||||
return dev_get_drvdata(&mux->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_mux_get_drvdata);
|
||||
|
||||
struct class typec_mux_class = {
|
||||
.name = "typec_mux",
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
struct pi3usb30532 {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock; /* protects the cached conf register */
|
||||
struct typec_switch sw;
|
||||
struct typec_mux mux;
|
||||
struct typec_switch *sw;
|
||||
struct typec_mux *mux;
|
||||
u8 conf;
|
||||
};
|
||||
|
||||
|
@ -48,7 +48,7 @@ static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf)
|
|||
static int pi3usb30532_sw_set(struct typec_switch *sw,
|
||||
enum typec_orientation orientation)
|
||||
{
|
||||
struct pi3usb30532 *pi = container_of(sw, struct pi3usb30532, sw);
|
||||
struct pi3usb30532 *pi = typec_switch_get_drvdata(sw);
|
||||
u8 new_conf;
|
||||
int ret;
|
||||
|
||||
|
@ -75,7 +75,7 @@ static int pi3usb30532_sw_set(struct typec_switch *sw,
|
|||
|
||||
static int pi3usb30532_mux_set(struct typec_mux *mux, int state)
|
||||
{
|
||||
struct pi3usb30532 *pi = container_of(mux, struct pi3usb30532, mux);
|
||||
struct pi3usb30532 *pi = typec_mux_get_drvdata(mux);
|
||||
u8 new_conf;
|
||||
int ret;
|
||||
|
||||
|
@ -113,6 +113,8 @@ static int pi3usb30532_mux_set(struct typec_mux *mux, int state)
|
|||
static int pi3usb30532_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct typec_switch_desc sw_desc;
|
||||
struct typec_mux_desc mux_desc;
|
||||
struct pi3usb30532 *pi;
|
||||
int ret;
|
||||
|
||||
|
@ -121,10 +123,6 @@ static int pi3usb30532_probe(struct i2c_client *client)
|
|||
return -ENOMEM;
|
||||
|
||||
pi->client = client;
|
||||
pi->sw.dev = dev;
|
||||
pi->sw.set = pi3usb30532_sw_set;
|
||||
pi->mux.dev = dev;
|
||||
pi->mux.set = pi3usb30532_mux_set;
|
||||
mutex_init(&pi->lock);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, PI3USB30532_CONF);
|
||||
|
@ -134,17 +132,27 @@ static int pi3usb30532_probe(struct i2c_client *client)
|
|||
}
|
||||
pi->conf = ret;
|
||||
|
||||
ret = typec_switch_register(&pi->sw);
|
||||
if (ret) {
|
||||
dev_err(dev, "Error registering typec switch: %d\n", ret);
|
||||
return ret;
|
||||
sw_desc.drvdata = pi;
|
||||
sw_desc.fwnode = dev->fwnode;
|
||||
sw_desc.set = pi3usb30532_sw_set;
|
||||
|
||||
pi->sw = typec_switch_register(dev, &sw_desc);
|
||||
if (IS_ERR(pi->sw)) {
|
||||
dev_err(dev, "Error registering typec switch: %ld\n",
|
||||
PTR_ERR(pi->sw));
|
||||
return PTR_ERR(pi->sw);
|
||||
}
|
||||
|
||||
ret = typec_mux_register(&pi->mux);
|
||||
if (ret) {
|
||||
typec_switch_unregister(&pi->sw);
|
||||
dev_err(dev, "Error registering typec mux: %d\n", ret);
|
||||
return ret;
|
||||
mux_desc.drvdata = pi;
|
||||
mux_desc.fwnode = dev->fwnode;
|
||||
mux_desc.set = pi3usb30532_mux_set;
|
||||
|
||||
pi->mux = typec_mux_register(dev, &mux_desc);
|
||||
if (IS_ERR(pi->mux)) {
|
||||
typec_switch_unregister(pi->sw);
|
||||
dev_err(dev, "Error registering typec mux: %ld\n",
|
||||
PTR_ERR(pi->mux));
|
||||
return PTR_ERR(pi->mux);
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, pi);
|
||||
|
@ -155,8 +163,8 @@ static int pi3usb30532_remove(struct i2c_client *client)
|
|||
{
|
||||
struct pi3usb30532 *pi = i2c_get_clientdata(client);
|
||||
|
||||
typec_mux_unregister(&pi->mux);
|
||||
typec_switch_unregister(&pi->sw);
|
||||
typec_mux_unregister(pi->mux);
|
||||
typec_switch_unregister(pi->sw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1255,6 +1255,8 @@ extern int device_for_each_child_reverse(struct device *dev, void *data,
|
|||
int (*fn)(struct device *dev, void *data));
|
||||
extern struct device *device_find_child(struct device *dev, void *data,
|
||||
int (*match)(struct device *dev, void *data));
|
||||
extern struct device *device_find_child_by_name(struct device *parent,
|
||||
const char *name);
|
||||
extern int device_rename(struct device *dev, const char *new_name);
|
||||
extern int device_move(struct device *dev, struct device *new_parent,
|
||||
enum dpm_order dpm_order);
|
||||
|
|
|
@ -76,6 +76,10 @@ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode,
|
|||
unsigned int nargs, unsigned int index,
|
||||
struct fwnode_reference_args *args);
|
||||
|
||||
struct fwnode_handle *fwnode_find_reference(const struct fwnode_handle *fwnode,
|
||||
const char *name,
|
||||
unsigned int index);
|
||||
|
||||
struct fwnode_handle *fwnode_get_parent(const struct fwnode_handle *fwnode);
|
||||
struct fwnode_handle *fwnode_get_next_parent(
|
||||
struct fwnode_handle *fwnode);
|
||||
|
@ -141,6 +145,26 @@ static inline int device_property_read_u64(struct device *dev,
|
|||
return device_property_read_u64_array(dev, propname, val, 1);
|
||||
}
|
||||
|
||||
static inline int device_property_count_u8(struct device *dev, const char *propname)
|
||||
{
|
||||
return device_property_read_u8_array(dev, propname, NULL, 0);
|
||||
}
|
||||
|
||||
static inline int device_property_count_u16(struct device *dev, const char *propname)
|
||||
{
|
||||
return device_property_read_u16_array(dev, propname, NULL, 0);
|
||||
}
|
||||
|
||||
static inline int device_property_count_u32(struct device *dev, const char *propname)
|
||||
{
|
||||
return device_property_read_u32_array(dev, propname, NULL, 0);
|
||||
}
|
||||
|
||||
static inline int device_property_count_u64(struct device *dev, const char *propname)
|
||||
{
|
||||
return device_property_read_u64_array(dev, propname, NULL, 0);
|
||||
}
|
||||
|
||||
static inline bool fwnode_property_read_bool(const struct fwnode_handle *fwnode,
|
||||
const char *propname)
|
||||
{
|
||||
|
@ -171,6 +195,30 @@ static inline int fwnode_property_read_u64(const struct fwnode_handle *fwnode,
|
|||
return fwnode_property_read_u64_array(fwnode, propname, val, 1);
|
||||
}
|
||||
|
||||
static inline int fwnode_property_count_u8(const struct fwnode_handle *fwnode,
|
||||
const char *propname)
|
||||
{
|
||||
return fwnode_property_read_u8_array(fwnode, propname, NULL, 0);
|
||||
}
|
||||
|
||||
static inline int fwnode_property_count_u16(const struct fwnode_handle *fwnode,
|
||||
const char *propname)
|
||||
{
|
||||
return fwnode_property_read_u16_array(fwnode, propname, NULL, 0);
|
||||
}
|
||||
|
||||
static inline int fwnode_property_count_u32(const struct fwnode_handle *fwnode,
|
||||
const char *propname)
|
||||
{
|
||||
return fwnode_property_read_u32_array(fwnode, propname, NULL, 0);
|
||||
}
|
||||
|
||||
static inline int fwnode_property_count_u64(const struct fwnode_handle *fwnode,
|
||||
const char *propname)
|
||||
{
|
||||
return fwnode_property_read_u64_array(fwnode, propname, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct property_entry - "Built-in" device property representation.
|
||||
* @name: Name of the property.
|
||||
|
@ -329,7 +377,54 @@ int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
|
|||
/* -------------------------------------------------------------------------- */
|
||||
/* Software fwnode support - when HW description is incomplete or missing */
|
||||
|
||||
struct software_node;
|
||||
|
||||
/**
|
||||
* struct software_node_ref_args - Reference with additional arguments
|
||||
* @node: Reference to a software node
|
||||
* @nargs: Number of elements in @args array
|
||||
* @args: Integer arguments
|
||||
*/
|
||||
struct software_node_ref_args {
|
||||
const struct software_node *node;
|
||||
unsigned int nargs;
|
||||
u64 args[NR_FWNODE_REFERENCE_ARGS];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct software_node_reference - Named software node reference property
|
||||
* @name: Name of the property
|
||||
* @nrefs: Number of elements in @refs array
|
||||
* @refs: Array of references with optional arguments
|
||||
*/
|
||||
struct software_node_reference {
|
||||
const char *name;
|
||||
unsigned int nrefs;
|
||||
const struct software_node_ref_args *refs;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct software_node - Software node description
|
||||
* @name: Name of the software node
|
||||
* @parent: Parent of the software node
|
||||
* @properties: Array of device properties
|
||||
* @references: Array of software node reference properties
|
||||
*/
|
||||
struct software_node {
|
||||
const char *name;
|
||||
const struct software_node *parent;
|
||||
const struct property_entry *properties;
|
||||
const struct software_node_reference *references;
|
||||
};
|
||||
|
||||
bool is_software_node(const struct fwnode_handle *fwnode);
|
||||
const struct software_node *to_software_node(struct fwnode_handle *fwnode);
|
||||
struct fwnode_handle *software_node_fwnode(const struct software_node *node);
|
||||
|
||||
int software_node_register_nodes(const struct software_node *nodes);
|
||||
void software_node_unregister_nodes(const struct software_node *nodes);
|
||||
|
||||
int software_node_register(const struct software_node *node);
|
||||
|
||||
int software_node_notify(struct device *dev, unsigned long action);
|
||||
|
||||
|
|
|
@ -3,54 +3,48 @@
|
|||
#ifndef __USB_TYPEC_MUX
|
||||
#define __USB_TYPEC_MUX
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/usb/typec.h>
|
||||
|
||||
struct device;
|
||||
struct typec_mux;
|
||||
struct typec_switch;
|
||||
struct fwnode_handle;
|
||||
|
||||
/**
|
||||
* struct typec_switch - USB Type-C cable orientation switch
|
||||
* @dev: Switch device
|
||||
* @entry: List entry
|
||||
* @set: Callback to the driver for setting the orientation
|
||||
*
|
||||
* USB Type-C pin flipper switch routing the correct data pairs from the
|
||||
* connector to the USB controller depending on the orientation of the cable
|
||||
* plug.
|
||||
*/
|
||||
struct typec_switch {
|
||||
struct device *dev;
|
||||
struct list_head entry;
|
||||
typedef int (*typec_switch_set_fn_t)(struct typec_switch *sw,
|
||||
enum typec_orientation orientation);
|
||||
|
||||
int (*set)(struct typec_switch *sw, enum typec_orientation orientation);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct typec_switch - USB Type-C connector pin mux
|
||||
* @dev: Mux device
|
||||
* @entry: List entry
|
||||
* @set: Callback to the driver for setting the state of the mux
|
||||
*
|
||||
* Pin Multiplexer/DeMultiplexer switch routing the USB Type-C connector pins to
|
||||
* different components depending on the requested mode of operation. Used with
|
||||
* Accessory/Alternate modes.
|
||||
*/
|
||||
struct typec_mux {
|
||||
struct device *dev;
|
||||
struct list_head entry;
|
||||
|
||||
int (*set)(struct typec_mux *mux, int state);
|
||||
struct typec_switch_desc {
|
||||
struct fwnode_handle *fwnode;
|
||||
typec_switch_set_fn_t set;
|
||||
void *drvdata;
|
||||
};
|
||||
|
||||
struct typec_switch *typec_switch_get(struct device *dev);
|
||||
void typec_switch_put(struct typec_switch *sw);
|
||||
int typec_switch_register(struct typec_switch *sw);
|
||||
struct typec_switch *
|
||||
typec_switch_register(struct device *parent,
|
||||
const struct typec_switch_desc *desc);
|
||||
void typec_switch_unregister(struct typec_switch *sw);
|
||||
|
||||
void typec_switch_set_drvdata(struct typec_switch *sw, void *data);
|
||||
void *typec_switch_get_drvdata(struct typec_switch *sw);
|
||||
|
||||
typedef int (*typec_mux_set_fn_t)(struct typec_mux *mux, int state);
|
||||
|
||||
struct typec_mux_desc {
|
||||
struct fwnode_handle *fwnode;
|
||||
typec_mux_set_fn_t set;
|
||||
void *drvdata;
|
||||
};
|
||||
|
||||
struct typec_mux *
|
||||
typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc);
|
||||
void typec_mux_put(struct typec_mux *mux);
|
||||
int typec_mux_register(struct typec_mux *mux);
|
||||
struct typec_mux *
|
||||
typec_mux_register(struct device *parent, const struct typec_mux_desc *desc);
|
||||
void typec_mux_unregister(struct typec_mux *mux);
|
||||
|
||||
void typec_mux_set_drvdata(struct typec_mux *mux, void *data);
|
||||
void *typec_mux_get_drvdata(struct typec_mux *mux);
|
||||
|
||||
#endif /* __USB_TYPEC_MUX */
|
||||
|
|
Загрузка…
Ссылка в новой задаче