Skip to content
This repository has been archived by the owner on May 16, 2019. It is now read-only.

Commit

Permalink
usb: typec: driver for Pericom PI3USB30532 Type-C cross switch
Browse files Browse the repository at this point in the history
Add a driver for the Pericom PI3USB30532 Type-C cross switch /
mux chip found on some devices with a Type-C port.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
jwrdegoede authored and gregkh committed Mar 22, 2018
1 parent f6fb9ec commit da95cc1
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 0 deletions.
6 changes: 6 additions & 0 deletions MAINTAINERS
Expand Up @@ -14544,6 +14544,12 @@ F: drivers/usb/
F: include/linux/usb.h
F: include/linux/usb/

USB TYPEC PI3USB30532 MUX DRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: linux-usb@vger.kernel.org
S: Maintained
F: drivers/usb/typec/mux/pi3usb30532.c

USB TYPEC SUBSYSTEM
M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
L: linux-usb@vger.kernel.org
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/typec/Kconfig
Expand Up @@ -85,4 +85,6 @@ config TYPEC_TPS6598X
If you choose to build this driver as a dynamically linked module, the
module will be called tps6598x.ko.

source "drivers/usb/typec/mux/Kconfig"

endif # TYPEC
1 change: 1 addition & 0 deletions drivers/usb/typec/Makefile
Expand Up @@ -6,3 +6,4 @@ obj-y += fusb302/
obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
obj-$(CONFIG_TYPEC_UCSI) += ucsi/
obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o
obj-$(CONFIG_TYPEC) += mux/
10 changes: 10 additions & 0 deletions drivers/usb/typec/mux/Kconfig
@@ -0,0 +1,10 @@
menu "USB Type-C Multiplexer/DeMultiplexer Switch support"

config TYPEC_MUX_PI3USB30532
tristate "Pericom PI3USB30532 Type-C cross switch driver"
depends on I2C
help
Say Y or M if your system has a Pericom PI3USB30532 Type-C cross
switch / mux chip found on some devices with a Type-C port.

endmenu
3 changes: 3 additions & 0 deletions drivers/usb/typec/mux/Makefile
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0

obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o
178 changes: 178 additions & 0 deletions drivers/usb/typec/mux/pi3usb30532.c
@@ -0,0 +1,178 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Pericom PI3USB30532 Type-C cross switch / mux driver
*
* Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com>
*/

#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/usb/tcpm.h>
#include <linux/usb/typec_mux.h>

#define PI3USB30532_CONF 0x00

#define PI3USB30532_CONF_OPEN 0x00
#define PI3USB30532_CONF_SWAP 0x01
#define PI3USB30532_CONF_4LANE_DP 0x02
#define PI3USB30532_CONF_USB3 0x04
#define PI3USB30532_CONF_USB3_AND_2LANE_DP 0x06

struct pi3usb30532 {
struct i2c_client *client;
struct mutex lock; /* protects the cached conf register */
struct typec_switch sw;
struct typec_mux mux;
u8 conf;
};

static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf)
{
int ret = 0;

if (pi->conf == new_conf)
return 0;

ret = i2c_smbus_write_byte_data(pi->client, PI3USB30532_CONF, new_conf);
if (ret) {
dev_err(&pi->client->dev, "Error writing conf: %d\n", ret);
return ret;
}

pi->conf = new_conf;
return 0;
}

static int pi3usb30532_sw_set(struct typec_switch *sw,
enum typec_orientation orientation)
{
struct pi3usb30532 *pi = container_of(sw, struct pi3usb30532, sw);
u8 new_conf;
int ret;

mutex_lock(&pi->lock);
new_conf = pi->conf;

switch (orientation) {
case TYPEC_ORIENTATION_NONE:
new_conf = PI3USB30532_CONF_OPEN;
break;
case TYPEC_ORIENTATION_NORMAL:
new_conf &= ~PI3USB30532_CONF_SWAP;
break;
case TYPEC_ORIENTATION_REVERSE:
new_conf |= PI3USB30532_CONF_SWAP;
break;
}

ret = pi3usb30532_set_conf(pi, new_conf);
mutex_unlock(&pi->lock);

return ret;
}

static int pi3usb30532_mux_set(struct typec_mux *mux, int state)
{
struct pi3usb30532 *pi = container_of(mux, struct pi3usb30532, mux);
u8 new_conf;
int ret;

mutex_lock(&pi->lock);
new_conf = pi->conf;

switch (state) {
case TYPEC_MUX_NONE:
new_conf = PI3USB30532_CONF_OPEN;
break;
case TYPEC_MUX_USB:
new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
PI3USB30532_CONF_USB3;
break;
case TYPEC_MUX_DP:
new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
PI3USB30532_CONF_4LANE_DP;
break;
case TYPEC_MUX_DOCK:
new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
PI3USB30532_CONF_USB3_AND_2LANE_DP;
break;
}

ret = pi3usb30532_set_conf(pi, new_conf);
mutex_unlock(&pi->lock);

return ret;
}

static int pi3usb30532_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct pi3usb30532 *pi;
int ret;

pi = devm_kzalloc(dev, sizeof(*pi), GFP_KERNEL);
if (!pi)
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);
if (ret < 0) {
dev_err(dev, "Error reading config register %d\n", ret);
return ret;
}
pi->conf = ret;

ret = typec_switch_register(&pi->sw);
if (ret) {
dev_err(dev, "Error registering typec switch: %d\n", ret);
return ret;
}

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;
}

i2c_set_clientdata(client, pi);
return 0;
}

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);
return 0;
}

static const struct i2c_device_id pi3usb30532_table[] = {
{ "pi3usb30532" },
{ }
};
MODULE_DEVICE_TABLE(i2c, pi3usb30532_table);

static struct i2c_driver pi3usb30532_driver = {
.driver = {
.name = "pi3usb30532",
},
.probe_new = pi3usb30532_probe,
.remove = pi3usb30532_remove,
.id_table = pi3usb30532_table,
};

module_i2c_driver(pi3usb30532_driver);

MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver");
MODULE_LICENSE("GPL");

0 comments on commit da95cc1

Please sign in to comment.