This repository has been archived by the owner on May 16, 2019. It is now read-only.
forked from fail0verflow/switch-linux
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
usb: typec: driver for Pericom PI3USB30532 Type-C cross switch
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
1 parent
f6fb9ec
commit da95cc1
Showing
6 changed files
with
200 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# SPDX-License-Identifier: GPL-2.0 | ||
|
||
obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"); |