]> Git Repo - linux.git/blob - drivers/usb/typec/mux/pi3usb30532.c
ARM: dts: imx7s: Enable SNVS power key according to board design
[linux.git] / drivers / usb / typec / mux / pi3usb30532.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Pericom PI3USB30532 Type-C cross switch / mux driver
4  *
5  * Copyright (c) 2017-2018 Hans de Goede <[email protected]>
6  */
7
8 #include <linux/i2c.h>
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>
14
15 #define PI3USB30532_CONF                        0x00
16
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
22
23 struct pi3usb30532 {
24         struct i2c_client *client;
25         struct mutex lock; /* protects the cached conf register */
26         struct typec_switch sw;
27         struct typec_mux mux;
28         u8 conf;
29 };
30
31 static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf)
32 {
33         int ret = 0;
34
35         if (pi->conf == new_conf)
36                 return 0;
37
38         ret = i2c_smbus_write_byte_data(pi->client, PI3USB30532_CONF, new_conf);
39         if (ret) {
40                 dev_err(&pi->client->dev, "Error writing conf: %d\n", ret);
41                 return ret;
42         }
43
44         pi->conf = new_conf;
45         return 0;
46 }
47
48 static int pi3usb30532_sw_set(struct typec_switch *sw,
49                               enum typec_orientation orientation)
50 {
51         struct pi3usb30532 *pi = container_of(sw, struct pi3usb30532, sw);
52         u8 new_conf;
53         int ret;
54
55         mutex_lock(&pi->lock);
56         new_conf = pi->conf;
57
58         switch (orientation) {
59         case TYPEC_ORIENTATION_NONE:
60                 new_conf = PI3USB30532_CONF_OPEN;
61                 break;
62         case TYPEC_ORIENTATION_NORMAL:
63                 new_conf &= ~PI3USB30532_CONF_SWAP;
64                 break;
65         case TYPEC_ORIENTATION_REVERSE:
66                 new_conf |= PI3USB30532_CONF_SWAP;
67                 break;
68         }
69
70         ret = pi3usb30532_set_conf(pi, new_conf);
71         mutex_unlock(&pi->lock);
72
73         return ret;
74 }
75
76 static int pi3usb30532_mux_set(struct typec_mux *mux, int state)
77 {
78         struct pi3usb30532 *pi = container_of(mux, struct pi3usb30532, mux);
79         u8 new_conf;
80         int ret;
81
82         mutex_lock(&pi->lock);
83         new_conf = pi->conf;
84
85         switch (state) {
86         case TYPEC_STATE_SAFE:
87                 new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
88                            PI3USB30532_CONF_OPEN;
89                 break;
90         case TYPEC_STATE_USB:
91                 new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
92                            PI3USB30532_CONF_USB3;
93                 break;
94         case TYPEC_DP_STATE_C:
95         case TYPEC_DP_STATE_E:
96                 new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
97                            PI3USB30532_CONF_4LANE_DP;
98                 break;
99         case TYPEC_DP_STATE_D:
100                 new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
101                            PI3USB30532_CONF_USB3_AND_2LANE_DP;
102                 break;
103         default:
104                 break;
105         }
106
107         ret = pi3usb30532_set_conf(pi, new_conf);
108         mutex_unlock(&pi->lock);
109
110         return ret;
111 }
112
113 static int pi3usb30532_probe(struct i2c_client *client)
114 {
115         struct device *dev = &client->dev;
116         struct pi3usb30532 *pi;
117         int ret;
118
119         pi = devm_kzalloc(dev, sizeof(*pi), GFP_KERNEL);
120         if (!pi)
121                 return -ENOMEM;
122
123         pi->client = client;
124         pi->sw.dev = dev;
125         pi->sw.set = pi3usb30532_sw_set;
126         pi->mux.dev = dev;
127         pi->mux.set = pi3usb30532_mux_set;
128         mutex_init(&pi->lock);
129
130         ret = i2c_smbus_read_byte_data(client, PI3USB30532_CONF);
131         if (ret < 0) {
132                 dev_err(dev, "Error reading config register %d\n", ret);
133                 return ret;
134         }
135         pi->conf = ret;
136
137         ret = typec_switch_register(&pi->sw);
138         if (ret) {
139                 dev_err(dev, "Error registering typec switch: %d\n", ret);
140                 return ret;
141         }
142
143         ret = typec_mux_register(&pi->mux);
144         if (ret) {
145                 typec_switch_unregister(&pi->sw);
146                 dev_err(dev, "Error registering typec mux: %d\n", ret);
147                 return ret;
148         }
149
150         i2c_set_clientdata(client, pi);
151         return 0;
152 }
153
154 static int pi3usb30532_remove(struct i2c_client *client)
155 {
156         struct pi3usb30532 *pi = i2c_get_clientdata(client);
157
158         typec_mux_unregister(&pi->mux);
159         typec_switch_unregister(&pi->sw);
160         return 0;
161 }
162
163 static const struct i2c_device_id pi3usb30532_table[] = {
164         { "pi3usb30532" },
165         { }
166 };
167 MODULE_DEVICE_TABLE(i2c, pi3usb30532_table);
168
169 static struct i2c_driver pi3usb30532_driver = {
170         .driver = {
171                 .name = "pi3usb30532",
172         },
173         .probe_new      = pi3usb30532_probe,
174         .remove         = pi3usb30532_remove,
175         .id_table       = pi3usb30532_table,
176 };
177
178 module_i2c_driver(pi3usb30532_driver);
179
180 MODULE_AUTHOR("Hans de Goede <[email protected]>");
181 MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver");
182 MODULE_LICENSE("GPL");
This page took 0.044501 seconds and 4 git commands to generate.