1 // SPDX-License-Identifier: GPL-2.0+
3 * Pericom PI3USB30532 Type-C cross switch / mux driver
5 * Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com>
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/mutex.h>
12 #include <linux/usb/typec_dp.h>
13 #include <linux/usb/typec_mux.h>
15 #define PI3USB30532_CONF 0x00
17 #define PI3USB30532_CONF_OPEN 0x00
18 #define PI3USB30532_CONF_SWAP 0x01
19 #define PI3USB30532_CONF_4LANE_DP 0x02
20 #define PI3USB30532_CONF_USB3 0x04
21 #define PI3USB30532_CONF_USB3_AND_2LANE_DP 0x06
24 struct i2c_client
*client
;
25 struct mutex lock
; /* protects the cached conf register */
26 struct typec_switch
*sw
;
27 struct typec_mux
*mux
;
31 static int pi3usb30532_set_conf(struct pi3usb30532
*pi
, u8 new_conf
)
35 if (pi
->conf
== new_conf
)
38 ret
= i2c_smbus_write_byte_data(pi
->client
, PI3USB30532_CONF
, new_conf
);
40 dev_err(&pi
->client
->dev
, "Error writing conf: %d\n", ret
);
48 static int pi3usb30532_sw_set(struct typec_switch
*sw
,
49 enum typec_orientation orientation
)
51 struct pi3usb30532
*pi
= typec_switch_get_drvdata(sw
);
55 mutex_lock(&pi
->lock
);
58 switch (orientation
) {
59 case TYPEC_ORIENTATION_NONE
:
60 new_conf
= PI3USB30532_CONF_OPEN
;
62 case TYPEC_ORIENTATION_NORMAL
:
63 new_conf
&= ~PI3USB30532_CONF_SWAP
;
65 case TYPEC_ORIENTATION_REVERSE
:
66 new_conf
|= PI3USB30532_CONF_SWAP
;
70 ret
= pi3usb30532_set_conf(pi
, new_conf
);
71 mutex_unlock(&pi
->lock
);
77 pi3usb30532_mux_set(struct typec_mux
*mux
, struct typec_mux_state
*state
)
79 struct pi3usb30532
*pi
= typec_mux_get_drvdata(mux
);
83 mutex_lock(&pi
->lock
);
86 switch (state
->mode
) {
87 case TYPEC_STATE_SAFE
:
88 new_conf
= (new_conf
& PI3USB30532_CONF_SWAP
) |
89 PI3USB30532_CONF_OPEN
;
92 new_conf
= (new_conf
& PI3USB30532_CONF_SWAP
) |
93 PI3USB30532_CONF_USB3
;
95 case TYPEC_DP_STATE_C
:
96 case TYPEC_DP_STATE_E
:
97 new_conf
= (new_conf
& PI3USB30532_CONF_SWAP
) |
98 PI3USB30532_CONF_4LANE_DP
;
100 case TYPEC_DP_STATE_D
:
101 new_conf
= (new_conf
& PI3USB30532_CONF_SWAP
) |
102 PI3USB30532_CONF_USB3_AND_2LANE_DP
;
108 ret
= pi3usb30532_set_conf(pi
, new_conf
);
109 mutex_unlock(&pi
->lock
);
114 static int pi3usb30532_probe(struct i2c_client
*client
)
116 struct device
*dev
= &client
->dev
;
117 struct typec_switch_desc sw_desc
= { };
118 struct typec_mux_desc mux_desc
= { };
119 struct pi3usb30532
*pi
;
122 pi
= devm_kzalloc(dev
, sizeof(*pi
), GFP_KERNEL
);
127 mutex_init(&pi
->lock
);
129 ret
= i2c_smbus_read_byte_data(client
, PI3USB30532_CONF
);
131 dev_err(dev
, "Error reading config register %d\n", ret
);
136 sw_desc
.drvdata
= pi
;
137 sw_desc
.fwnode
= dev
->fwnode
;
138 sw_desc
.set
= pi3usb30532_sw_set
;
140 pi
->sw
= typec_switch_register(dev
, &sw_desc
);
141 if (IS_ERR(pi
->sw
)) {
142 dev_err(dev
, "Error registering typec switch: %ld\n",
144 return PTR_ERR(pi
->sw
);
147 mux_desc
.drvdata
= pi
;
148 mux_desc
.fwnode
= dev
->fwnode
;
149 mux_desc
.set
= pi3usb30532_mux_set
;
151 pi
->mux
= typec_mux_register(dev
, &mux_desc
);
152 if (IS_ERR(pi
->mux
)) {
153 typec_switch_unregister(pi
->sw
);
154 dev_err(dev
, "Error registering typec mux: %ld\n",
156 return PTR_ERR(pi
->mux
);
159 i2c_set_clientdata(client
, pi
);
163 static int pi3usb30532_remove(struct i2c_client
*client
)
165 struct pi3usb30532
*pi
= i2c_get_clientdata(client
);
167 typec_mux_unregister(pi
->mux
);
168 typec_switch_unregister(pi
->sw
);
172 static const struct i2c_device_id pi3usb30532_table
[] = {
176 MODULE_DEVICE_TABLE(i2c
, pi3usb30532_table
);
178 static struct i2c_driver pi3usb30532_driver
= {
180 .name
= "pi3usb30532",
182 .probe_new
= pi3usb30532_probe
,
183 .remove
= pi3usb30532_remove
,
184 .id_table
= pi3usb30532_table
,
187 module_i2c_driver(pi3usb30532_driver
);
189 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
190 MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver");
191 MODULE_LICENSE("GPL");