cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mux.c (12156B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * USB Type-C Multiplexer/DeMultiplexer Switch support
      4 *
      5 * Copyright (C) 2018 Intel Corporation
      6 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
      7 *         Hans de Goede <hdegoede@redhat.com>
      8 */
      9
     10#include <linux/device.h>
     11#include <linux/list.h>
     12#include <linux/module.h>
     13#include <linux/mutex.h>
     14#include <linux/property.h>
     15#include <linux/slab.h>
     16
     17#include "class.h"
     18#include "mux.h"
     19
     20#define TYPEC_MUX_MAX_DEVS	3
     21
     22struct typec_switch {
     23	struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS];
     24	unsigned int num_sw_devs;
     25};
     26
     27static int switch_fwnode_match(struct device *dev, const void *fwnode)
     28{
     29	if (!is_typec_switch_dev(dev))
     30		return 0;
     31
     32	return dev_fwnode(dev) == fwnode;
     33}
     34
     35static void *typec_switch_match(struct fwnode_handle *fwnode, const char *id,
     36				void *data)
     37{
     38	struct device *dev;
     39
     40	/*
     41	 * Device graph (OF graph) does not give any means to identify the
     42	 * device type or the device class of the remote port parent that @fwnode
     43	 * represents, so in order to identify the type or the class of @fwnode
     44	 * an additional device property is needed. With typec switches the
     45	 * property is named "orientation-switch" (@id). The value of the device
     46	 * property is ignored.
     47	 */
     48	if (id && !fwnode_property_present(fwnode, id))
     49		return NULL;
     50
     51	/*
     52	 * At this point we are sure that @fwnode is a typec switch in all
     53	 * cases. If the switch hasn't yet been registered for some reason, the
     54	 * function "defers probe" for now.
     55	 */
     56	dev = class_find_device(&typec_mux_class, NULL, fwnode,
     57				switch_fwnode_match);
     58
     59	return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER);
     60}
     61
     62/**
     63 * fwnode_typec_switch_get - Find USB Type-C orientation switch
     64 * @fwnode: The caller device node
     65 *
     66 * Finds a switch linked with @dev. Returns a reference to the switch on
     67 * success, NULL if no matching connection was found, or
     68 * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch
     69 * has not been enumerated yet.
     70 */
     71struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode)
     72{
     73	struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS];
     74	struct typec_switch *sw;
     75	int count;
     76	int err;
     77	int i;
     78
     79	sw = kzalloc(sizeof(*sw), GFP_KERNEL);
     80	if (!sw)
     81		return ERR_PTR(-ENOMEM);
     82
     83	count = fwnode_connection_find_matches(fwnode, "orientation-switch", NULL,
     84					       typec_switch_match,
     85					       (void **)sw_devs,
     86					       ARRAY_SIZE(sw_devs));
     87	if (count <= 0) {
     88		kfree(sw);
     89		return NULL;
     90	}
     91
     92	for (i = 0; i < count; i++) {
     93		if (IS_ERR(sw_devs[i])) {
     94			err = PTR_ERR(sw_devs[i]);
     95			goto put_sw_devs;
     96		}
     97	}
     98
     99	for (i = 0; i < count; i++) {
    100		WARN_ON(!try_module_get(sw_devs[i]->dev.parent->driver->owner));
    101		sw->sw_devs[i] = sw_devs[i];
    102	}
    103
    104	sw->num_sw_devs = count;
    105
    106	return sw;
    107
    108put_sw_devs:
    109	for (i = 0; i < count; i++) {
    110		if (!IS_ERR(sw_devs[i]))
    111			put_device(&sw_devs[i]->dev);
    112	}
    113
    114	kfree(sw);
    115
    116	return ERR_PTR(err);
    117}
    118EXPORT_SYMBOL_GPL(fwnode_typec_switch_get);
    119
    120/**
    121 * typec_switch_put - Release USB Type-C orientation switch
    122 * @sw: USB Type-C orientation switch
    123 *
    124 * Decrement reference count for @sw.
    125 */
    126void typec_switch_put(struct typec_switch *sw)
    127{
    128	struct typec_switch_dev *sw_dev;
    129	unsigned int i;
    130
    131	if (IS_ERR_OR_NULL(sw))
    132		return;
    133
    134	for (i = 0; i < sw->num_sw_devs; i++) {
    135		sw_dev = sw->sw_devs[i];
    136
    137		module_put(sw_dev->dev.parent->driver->owner);
    138		put_device(&sw_dev->dev);
    139	}
    140	kfree(sw);
    141}
    142EXPORT_SYMBOL_GPL(typec_switch_put);
    143
    144static void typec_switch_release(struct device *dev)
    145{
    146	kfree(to_typec_switch_dev(dev));
    147}
    148
    149const struct device_type typec_switch_dev_type = {
    150	.name = "orientation_switch",
    151	.release = typec_switch_release,
    152};
    153
    154/**
    155 * typec_switch_register - Register USB Type-C orientation switch
    156 * @parent: Parent device
    157 * @desc: Orientation switch description
    158 *
    159 * This function registers a switch that can be used for routing the correct
    160 * data pairs depending on the cable plug orientation from the USB Type-C
    161 * connector to the USB controllers. USB Type-C plugs can be inserted
    162 * right-side-up or upside-down.
    163 */
    164struct typec_switch_dev *
    165typec_switch_register(struct device *parent,
    166		      const struct typec_switch_desc *desc)
    167{
    168	struct typec_switch_dev *sw_dev;
    169	int ret;
    170
    171	if (!desc || !desc->set)
    172		return ERR_PTR(-EINVAL);
    173
    174	sw_dev = kzalloc(sizeof(*sw_dev), GFP_KERNEL);
    175	if (!sw_dev)
    176		return ERR_PTR(-ENOMEM);
    177
    178	sw_dev->set = desc->set;
    179
    180	device_initialize(&sw_dev->dev);
    181	sw_dev->dev.parent = parent;
    182	sw_dev->dev.fwnode = desc->fwnode;
    183	sw_dev->dev.class = &typec_mux_class;
    184	sw_dev->dev.type = &typec_switch_dev_type;
    185	sw_dev->dev.driver_data = desc->drvdata;
    186	ret = dev_set_name(&sw_dev->dev, "%s-switch", desc->name ? desc->name : dev_name(parent));
    187	if (ret) {
    188		put_device(&sw_dev->dev);
    189		return ERR_PTR(ret);
    190	}
    191
    192	ret = device_add(&sw_dev->dev);
    193	if (ret) {
    194		dev_err(parent, "failed to register switch (%d)\n", ret);
    195		put_device(&sw_dev->dev);
    196		return ERR_PTR(ret);
    197	}
    198
    199	return sw_dev;
    200}
    201EXPORT_SYMBOL_GPL(typec_switch_register);
    202
    203int typec_switch_set(struct typec_switch *sw,
    204		     enum typec_orientation orientation)
    205{
    206	struct typec_switch_dev *sw_dev;
    207	unsigned int i;
    208	int ret;
    209
    210	if (IS_ERR_OR_NULL(sw))
    211		return 0;
    212
    213	for (i = 0; i < sw->num_sw_devs; i++) {
    214		sw_dev = sw->sw_devs[i];
    215
    216		ret = sw_dev->set(sw_dev, orientation);
    217		if (ret)
    218			return ret;
    219	}
    220
    221	return 0;
    222}
    223EXPORT_SYMBOL_GPL(typec_switch_set);
    224
    225/**
    226 * typec_switch_unregister - Unregister USB Type-C orientation switch
    227 * @sw_dev: USB Type-C orientation switch
    228 *
    229 * Unregister switch that was registered with typec_switch_register().
    230 */
    231void typec_switch_unregister(struct typec_switch_dev *sw_dev)
    232{
    233	if (!IS_ERR_OR_NULL(sw_dev))
    234		device_unregister(&sw_dev->dev);
    235}
    236EXPORT_SYMBOL_GPL(typec_switch_unregister);
    237
    238void typec_switch_set_drvdata(struct typec_switch_dev *sw_dev, void *data)
    239{
    240	dev_set_drvdata(&sw_dev->dev, data);
    241}
    242EXPORT_SYMBOL_GPL(typec_switch_set_drvdata);
    243
    244void *typec_switch_get_drvdata(struct typec_switch_dev *sw_dev)
    245{
    246	return dev_get_drvdata(&sw_dev->dev);
    247}
    248EXPORT_SYMBOL_GPL(typec_switch_get_drvdata);
    249
    250/* ------------------------------------------------------------------------- */
    251
    252struct typec_mux {
    253	struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS];
    254	unsigned int num_mux_devs;
    255};
    256
    257static int mux_fwnode_match(struct device *dev, const void *fwnode)
    258{
    259	if (!is_typec_mux_dev(dev))
    260		return 0;
    261
    262	return dev_fwnode(dev) == fwnode;
    263}
    264
    265static void *typec_mux_match(struct fwnode_handle *fwnode, const char *id,
    266			     void *data)
    267{
    268	const struct typec_altmode_desc *desc = data;
    269	struct device *dev;
    270	bool match;
    271	int nval;
    272	u16 *val;
    273	int ret;
    274	int i;
    275
    276	/*
    277	 * Check has the identifier already been "consumed". If it
    278	 * has, no need to do any extra connection identification.
    279	 */
    280	match = !id;
    281	if (match)
    282		goto find_mux;
    283
    284	/* Accessory Mode muxes */
    285	if (!desc) {
    286		match = fwnode_property_present(fwnode, "accessory");
    287		if (match)
    288			goto find_mux;
    289		return NULL;
    290	}
    291
    292	/* Alternate Mode muxes */
    293	nval = fwnode_property_count_u16(fwnode, "svid");
    294	if (nval <= 0)
    295		return NULL;
    296
    297	val = kcalloc(nval, sizeof(*val), GFP_KERNEL);
    298	if (!val)
    299		return ERR_PTR(-ENOMEM);
    300
    301	ret = fwnode_property_read_u16_array(fwnode, "svid", val, nval);
    302	if (ret < 0) {
    303		kfree(val);
    304		return ERR_PTR(ret);
    305	}
    306
    307	for (i = 0; i < nval; i++) {
    308		match = val[i] == desc->svid;
    309		if (match) {
    310			kfree(val);
    311			goto find_mux;
    312		}
    313	}
    314	kfree(val);
    315	return NULL;
    316
    317find_mux:
    318	dev = class_find_device(&typec_mux_class, NULL, fwnode,
    319				mux_fwnode_match);
    320
    321	return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER);
    322}
    323
    324/**
    325 * fwnode_typec_mux_get - Find USB Type-C Multiplexer
    326 * @fwnode: The caller device node
    327 * @desc: Alt Mode description
    328 *
    329 * Finds a mux linked to the caller. This function is primarily meant for the
    330 * Type-C drivers. Returns a reference to the mux on success, NULL if no
    331 * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
    332 * was found but the mux has not been enumerated yet.
    333 */
    334struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode,
    335				       const struct typec_altmode_desc *desc)
    336{
    337	struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS];
    338	struct typec_mux *mux;
    339	int count;
    340	int err;
    341	int i;
    342
    343	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
    344	if (!mux)
    345		return ERR_PTR(-ENOMEM);
    346
    347	count = fwnode_connection_find_matches(fwnode, "mode-switch",
    348					       (void *)desc, typec_mux_match,
    349					       (void **)mux_devs,
    350					       ARRAY_SIZE(mux_devs));
    351	if (count <= 0) {
    352		kfree(mux);
    353		return NULL;
    354	}
    355
    356	for (i = 0; i < count; i++) {
    357		if (IS_ERR(mux_devs[i])) {
    358			err = PTR_ERR(mux_devs[i]);
    359			goto put_mux_devs;
    360		}
    361	}
    362
    363	for (i = 0; i < count; i++) {
    364		WARN_ON(!try_module_get(mux_devs[i]->dev.parent->driver->owner));
    365		mux->mux_devs[i] = mux_devs[i];
    366	}
    367
    368	mux->num_mux_devs = count;
    369
    370	return mux;
    371
    372put_mux_devs:
    373	for (i = 0; i < count; i++) {
    374		if (!IS_ERR(mux_devs[i]))
    375			put_device(&mux_devs[i]->dev);
    376	}
    377
    378	kfree(mux);
    379
    380	return ERR_PTR(err);
    381}
    382EXPORT_SYMBOL_GPL(fwnode_typec_mux_get);
    383
    384/**
    385 * typec_mux_put - Release handle to a Multiplexer
    386 * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
    387 *
    388 * Decrements reference count for @mux.
    389 */
    390void typec_mux_put(struct typec_mux *mux)
    391{
    392	struct typec_mux_dev *mux_dev;
    393	unsigned int i;
    394
    395	if (IS_ERR_OR_NULL(mux))
    396		return;
    397
    398	for (i = 0; i < mux->num_mux_devs; i++) {
    399		mux_dev = mux->mux_devs[i];
    400		module_put(mux_dev->dev.parent->driver->owner);
    401		put_device(&mux_dev->dev);
    402	}
    403	kfree(mux);
    404}
    405EXPORT_SYMBOL_GPL(typec_mux_put);
    406
    407int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
    408{
    409	struct typec_mux_dev *mux_dev;
    410	unsigned int i;
    411	int ret;
    412
    413	if (IS_ERR_OR_NULL(mux))
    414		return 0;
    415
    416	for (i = 0; i < mux->num_mux_devs; i++) {
    417		mux_dev = mux->mux_devs[i];
    418
    419		ret = mux_dev->set(mux_dev, state);
    420		if (ret)
    421			return ret;
    422	}
    423
    424	return 0;
    425}
    426EXPORT_SYMBOL_GPL(typec_mux_set);
    427
    428static void typec_mux_release(struct device *dev)
    429{
    430	kfree(to_typec_mux_dev(dev));
    431}
    432
    433const struct device_type typec_mux_dev_type = {
    434	.name = "mode_switch",
    435	.release = typec_mux_release,
    436};
    437
    438/**
    439 * typec_mux_register - Register Multiplexer routing USB Type-C pins
    440 * @parent: Parent device
    441 * @desc: Multiplexer description
    442 *
    443 * USB Type-C connectors can be used for alternate modes of operation besides
    444 * USB when Accessory/Alternate Modes are supported. With some of those modes,
    445 * the pins on the connector need to be reconfigured. This function registers
    446 * multiplexer switches routing the pins on the connector.
    447 */
    448struct typec_mux_dev *
    449typec_mux_register(struct device *parent, const struct typec_mux_desc *desc)
    450{
    451	struct typec_mux_dev *mux_dev;
    452	int ret;
    453
    454	if (!desc || !desc->set)
    455		return ERR_PTR(-EINVAL);
    456
    457	mux_dev = kzalloc(sizeof(*mux_dev), GFP_KERNEL);
    458	if (!mux_dev)
    459		return ERR_PTR(-ENOMEM);
    460
    461	mux_dev->set = desc->set;
    462
    463	device_initialize(&mux_dev->dev);
    464	mux_dev->dev.parent = parent;
    465	mux_dev->dev.fwnode = desc->fwnode;
    466	mux_dev->dev.class = &typec_mux_class;
    467	mux_dev->dev.type = &typec_mux_dev_type;
    468	mux_dev->dev.driver_data = desc->drvdata;
    469	ret = dev_set_name(&mux_dev->dev, "%s-mux", desc->name ? desc->name : dev_name(parent));
    470	if (ret) {
    471		put_device(&mux_dev->dev);
    472		return ERR_PTR(ret);
    473	}
    474
    475	ret = device_add(&mux_dev->dev);
    476	if (ret) {
    477		dev_err(parent, "failed to register mux (%d)\n", ret);
    478		put_device(&mux_dev->dev);
    479		return ERR_PTR(ret);
    480	}
    481
    482	return mux_dev;
    483}
    484EXPORT_SYMBOL_GPL(typec_mux_register);
    485
    486/**
    487 * typec_mux_unregister - Unregister Multiplexer Switch
    488 * @mux_dev: USB Type-C Connector Multiplexer/DeMultiplexer
    489 *
    490 * Unregister mux that was registered with typec_mux_register().
    491 */
    492void typec_mux_unregister(struct typec_mux_dev *mux_dev)
    493{
    494	if (!IS_ERR_OR_NULL(mux_dev))
    495		device_unregister(&mux_dev->dev);
    496}
    497EXPORT_SYMBOL_GPL(typec_mux_unregister);
    498
    499void typec_mux_set_drvdata(struct typec_mux_dev *mux_dev, void *data)
    500{
    501	dev_set_drvdata(&mux_dev->dev, data);
    502}
    503EXPORT_SYMBOL_GPL(typec_mux_set_drvdata);
    504
    505void *typec_mux_get_drvdata(struct typec_mux_dev *mux_dev)
    506{
    507	return dev_get_drvdata(&mux_dev->dev);
    508}
    509EXPORT_SYMBOL_GPL(typec_mux_get_drvdata);
    510
    511struct class typec_mux_class = {
    512	.name = "typec_mux",
    513	.owner = THIS_MODULE,
    514};