Commit 0415052d authored by Linus Torvalds's avatar Linus Torvalds

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
parents 4b470452 33ee09cd
......@@ -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)
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,70 +652,165 @@ static struct kobj_type software_node_type = {
.sysfs_ops = &kobj_sysfs_ops,
};
struct fwnode_handle *
fwnode_create_software_node(const struct property_entry *properties,
const struct fwnode_handle *parent)
static struct fwnode_handle *
swnode_register(const struct software_node *node, struct swnode *parent,
unsigned int allocated)
{
struct software_node *p = NULL;
struct software_node *swnode;
struct swnode *swnode;
int ret;
if (parent) {
if (IS_ERR(parent))
return ERR_CAST(parent);
if (!is_software_node(parent))
return ERR_PTR(-EINVAL);
p = to_software_node(parent);
}
swnode = kzalloc(sizeof(*swnode), GFP_KERNEL);
if (!swnode)
return ERR_PTR(-ENOMEM);
if (!swnode) {
ret = -ENOMEM;
goto out_err;
}
ret = ida_simple_get(p ? &p->child_ids : &swnode_root_ids, 0, 0,
GFP_KERNEL);
ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids,
0, 0, GFP_KERNEL);
if (ret < 0) {
kfree(swnode);
return ERR_PTR(ret);
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);
swnode->parent = p;
if (p)
list_add_tail(&swnode->entry, &p->children);
if (node->name)
ret = kobject_init_and_add(&swnode->kobj, &software_node_type,
p ? &p->kobj : NULL, "node%d", swnode->id);
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);
}
ret = software_node_register_properties(swnode, properties);
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) {
kobject_put(&swnode->kobj);
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 *node;
struct swnode *p = NULL;
int ret;
if (parent) {
if (IS_ERR(parent))
return ERR_CAST(parent);
if (!is_software_node(parent))
return ERR_PTR(-EINVAL);
p = to_swnode(parent);
}
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
return ERR_PTR(-ENOMEM);
ret = software_node_register_properties(node, properties);
if (ret) {
kfree(node);
return ERR_PTR(ret);
}
kobject_uevent(&swnode->kobj, KOBJ_ADD);
return &swnode->fwnode;
node->parent = p ? p->node : NULL;
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);
ret = cht_int33fe_add_nodes(data);
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);
}
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";
/* 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;
device_connections_add(data->connections);
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_FUSB302]);
if (!fwnode) {
ret = -ENODEV;
goto out_unregister_max17047;
}
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 @@ static int cht_int33fe_probe(struct platform_device *pdev)
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);
}
/*
* With OF graph the mux node must have a boolean device property named
* "orientation-switch".
*/
if (con->fwnode) {
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;
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);
}
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;
if (!desc || !desc->set)
return ERR_PTR(-EINVAL);
sw = kzalloc(sizeof(*sw), GFP_KERNEL);
if (!sw)
return ERR_PTR(-ENOMEM);
return 0;
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;
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 0;
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;
int (*set)(struct typec_switch *sw, enum typec_orientation orientation);
};
typedef int (*typec_switch_set_fn_t)(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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment