Skip to content

Commit 492b7a8

Browse files
committed
Merge tag 'extcon-next-for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon into char-misc-next
Chanwoo writes: Update extcon for 5.1 Detailed description for this pull request: 1. Add new extcon-ptn5150.c extcon provider driver - NXP PTN5150 supports the detection of USB connectors through USB Type-C port and controls it. It is interfaced to the host controller using an I2C interface. * tag 'extcon-next-for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon: extcon: ptn5150: Fix return value check in ptn5150_i2c_probe() extcon: Add support for ptn5150 extcon driver
2 parents 5c07488 + 3dfed89 commit 492b7a8

File tree

4 files changed

+375
-0
lines changed

4 files changed

+375
-0
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
* PTN5150 CC (Configuration Channel) Logic device
2+
3+
PTN5150 is a small thin low power CC logic chip supporting the USB Type-C
4+
connector application with CC control logic detection and indication functions.
5+
It is interfaced to the host controller using an I2C interface.
6+
7+
Required properties:
8+
- compatible: should be "nxp,ptn5150"
9+
- reg: specifies the I2C slave address of the device
10+
- int-gpio: should contain a phandle and GPIO specifier for the GPIO pin
11+
connected to the PTN5150's INTB pin.
12+
- vbus-gpio: should contain a phandle and GPIO specifier for the GPIO pin which
13+
is used to control VBUS.
14+
- pinctrl-names : a pinctrl state named "default" must be defined.
15+
- pinctrl-0 : phandle referencing pin configuration of interrupt and vbus
16+
control.
17+
18+
Example:
19+
ptn5150@1d {
20+
compatible = "nxp,ptn5150";
21+
reg = <0x1d>;
22+
int-gpio = <&msmgpio 78 GPIO_ACTIVE_HIGH>;
23+
vbus-gpio = <&msmgpio 148 GPIO_ACTIVE_HIGH>;
24+
pinctrl-names = "default";
25+
pinctrl-0 = <&ptn5150_default>;
26+
status = "okay";
27+
};

drivers/extcon/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ config EXTCON_PALMAS
114114
Say Y here to enable support for USB peripheral and USB host
115115
detection by palmas usb.
116116

117+
config EXTCON_PTN5150
118+
tristate "NXP PTN5150 CC LOGIC USB EXTCON support"
119+
depends on I2C && GPIOLIB || COMPILE_TEST
120+
select REGMAP_I2C
121+
help
122+
Say Y here to enable support for USB peripheral and USB host
123+
detection by NXP PTN5150 CC (Configuration Channel) logic chip.
124+
117125
config EXTCON_QCOM_SPMI_MISC
118126
tristate "Qualcomm USB extcon support"
119127
depends on ARCH_QCOM || COMPILE_TEST

drivers/extcon/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
1717
obj-$(CONFIG_EXTCON_MAX77843) += extcon-max77843.o
1818
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
1919
obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o
20+
obj-$(CONFIG_EXTCON_PTN5150) += extcon-ptn5150.o
2021
obj-$(CONFIG_EXTCON_QCOM_SPMI_MISC) += extcon-qcom-spmi-misc.o
2122
obj-$(CONFIG_EXTCON_RT8973A) += extcon-rt8973a.o
2223
obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o

drivers/extcon/extcon-ptn5150.c

Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
//
3+
// extcon-ptn5150.c - PTN5150 CC logic extcon driver to support USB detection
4+
//
5+
// Based on extcon-sm5502.c driver
6+
// Copyright (c) 2018-2019 by Vijai Kumar K
7+
// Author: Vijai Kumar K <[email protected]>
8+
9+
#include <linux/err.h>
10+
#include <linux/i2c.h>
11+
#include <linux/interrupt.h>
12+
#include <linux/kernel.h>
13+
#include <linux/module.h>
14+
#include <linux/regmap.h>
15+
#include <linux/slab.h>
16+
#include <linux/extcon-provider.h>
17+
#include <linux/gpio/consumer.h>
18+
19+
/* PTN5150 registers */
20+
enum ptn5150_reg {
21+
PTN5150_REG_DEVICE_ID = 0x01,
22+
PTN5150_REG_CONTROL,
23+
PTN5150_REG_INT_STATUS,
24+
PTN5150_REG_CC_STATUS,
25+
PTN5150_REG_CON_DET = 0x09,
26+
PTN5150_REG_VCONN_STATUS,
27+
PTN5150_REG_RESET,
28+
PTN5150_REG_INT_MASK = 0x18,
29+
PTN5150_REG_INT_REG_STATUS,
30+
PTN5150_REG_END,
31+
};
32+
33+
#define PTN5150_DFP_ATTACHED 0x1
34+
#define PTN5150_UFP_ATTACHED 0x2
35+
36+
/* Define PTN5150 MASK/SHIFT constant */
37+
#define PTN5150_REG_DEVICE_ID_VENDOR_SHIFT 0
38+
#define PTN5150_REG_DEVICE_ID_VENDOR_MASK \
39+
(0x3 << PTN5150_REG_DEVICE_ID_VENDOR_SHIFT)
40+
41+
#define PTN5150_REG_DEVICE_ID_VERSION_SHIFT 3
42+
#define PTN5150_REG_DEVICE_ID_VERSION_MASK \
43+
(0x1f << PTN5150_REG_DEVICE_ID_VERSION_SHIFT)
44+
45+
#define PTN5150_REG_CC_PORT_ATTACHMENT_SHIFT 2
46+
#define PTN5150_REG_CC_PORT_ATTACHMENT_MASK \
47+
(0x7 << PTN5150_REG_CC_PORT_ATTACHMENT_SHIFT)
48+
49+
#define PTN5150_REG_CC_VBUS_DETECTION_SHIFT 7
50+
#define PTN5150_REG_CC_VBUS_DETECTION_MASK \
51+
(0x1 << PTN5150_REG_CC_VBUS_DETECTION_SHIFT)
52+
53+
#define PTN5150_REG_INT_CABLE_ATTACH_SHIFT 0
54+
#define PTN5150_REG_INT_CABLE_ATTACH_MASK \
55+
(0x1 << PTN5150_REG_INT_CABLE_ATTACH_SHIFT)
56+
57+
#define PTN5150_REG_INT_CABLE_DETACH_SHIFT 1
58+
#define PTN5150_REG_INT_CABLE_DETACH_MASK \
59+
(0x1 << PTN5150_REG_CC_CABLE_DETACH_SHIFT)
60+
61+
struct ptn5150_info {
62+
struct device *dev;
63+
struct extcon_dev *edev;
64+
struct i2c_client *i2c;
65+
struct regmap *regmap;
66+
struct gpio_desc *int_gpiod;
67+
struct gpio_desc *vbus_gpiod;
68+
int irq;
69+
struct work_struct irq_work;
70+
struct mutex mutex;
71+
};
72+
73+
/* List of detectable cables */
74+
static const unsigned int ptn5150_extcon_cable[] = {
75+
EXTCON_USB,
76+
EXTCON_USB_HOST,
77+
EXTCON_NONE,
78+
};
79+
80+
static const struct regmap_config ptn5150_regmap_config = {
81+
.reg_bits = 8,
82+
.val_bits = 8,
83+
.max_register = PTN5150_REG_END,
84+
};
85+
86+
static void ptn5150_irq_work(struct work_struct *work)
87+
{
88+
struct ptn5150_info *info = container_of(work,
89+
struct ptn5150_info, irq_work);
90+
int ret = 0;
91+
unsigned int reg_data;
92+
unsigned int int_status;
93+
94+
if (!info->edev)
95+
return;
96+
97+
mutex_lock(&info->mutex);
98+
99+
ret = regmap_read(info->regmap, PTN5150_REG_CC_STATUS, &reg_data);
100+
if (ret) {
101+
dev_err(info->dev, "failed to read CC STATUS %d\n", ret);
102+
mutex_unlock(&info->mutex);
103+
return;
104+
}
105+
106+
/* Clear interrupt. Read would clear the register */
107+
ret = regmap_read(info->regmap, PTN5150_REG_INT_STATUS, &int_status);
108+
if (ret) {
109+
dev_err(info->dev, "failed to read INT STATUS %d\n", ret);
110+
mutex_unlock(&info->mutex);
111+
return;
112+
}
113+
114+
if (int_status) {
115+
unsigned int cable_attach;
116+
117+
cable_attach = int_status & PTN5150_REG_INT_CABLE_ATTACH_MASK;
118+
if (cable_attach) {
119+
unsigned int port_status;
120+
unsigned int vbus;
121+
122+
port_status = ((reg_data &
123+
PTN5150_REG_CC_PORT_ATTACHMENT_MASK) >>
124+
PTN5150_REG_CC_PORT_ATTACHMENT_SHIFT);
125+
126+
switch (port_status) {
127+
case PTN5150_DFP_ATTACHED:
128+
extcon_set_state_sync(info->edev,
129+
EXTCON_USB_HOST, false);
130+
gpiod_set_value(info->vbus_gpiod, 0);
131+
extcon_set_state_sync(info->edev, EXTCON_USB,
132+
true);
133+
break;
134+
case PTN5150_UFP_ATTACHED:
135+
extcon_set_state_sync(info->edev, EXTCON_USB,
136+
false);
137+
vbus = ((reg_data &
138+
PTN5150_REG_CC_VBUS_DETECTION_MASK) >>
139+
PTN5150_REG_CC_VBUS_DETECTION_SHIFT);
140+
if (vbus)
141+
gpiod_set_value(info->vbus_gpiod, 0);
142+
else
143+
gpiod_set_value(info->vbus_gpiod, 1);
144+
145+
extcon_set_state_sync(info->edev,
146+
EXTCON_USB_HOST, true);
147+
break;
148+
default:
149+
dev_err(info->dev,
150+
"Unknown Port status : %x\n",
151+
port_status);
152+
break;
153+
}
154+
} else {
155+
extcon_set_state_sync(info->edev,
156+
EXTCON_USB_HOST, false);
157+
extcon_set_state_sync(info->edev,
158+
EXTCON_USB, false);
159+
gpiod_set_value(info->vbus_gpiod, 0);
160+
}
161+
}
162+
163+
/* Clear interrupt. Read would clear the register */
164+
ret = regmap_read(info->regmap, PTN5150_REG_INT_REG_STATUS,
165+
&int_status);
166+
if (ret) {
167+
dev_err(info->dev,
168+
"failed to read INT REG STATUS %d\n", ret);
169+
mutex_unlock(&info->mutex);
170+
return;
171+
}
172+
173+
mutex_unlock(&info->mutex);
174+
}
175+
176+
177+
static irqreturn_t ptn5150_irq_handler(int irq, void *data)
178+
{
179+
struct ptn5150_info *info = data;
180+
181+
schedule_work(&info->irq_work);
182+
183+
return IRQ_HANDLED;
184+
}
185+
186+
static int ptn5150_init_dev_type(struct ptn5150_info *info)
187+
{
188+
unsigned int reg_data, vendor_id, version_id;
189+
int ret;
190+
191+
ret = regmap_read(info->regmap, PTN5150_REG_DEVICE_ID, &reg_data);
192+
if (ret) {
193+
dev_err(info->dev, "failed to read DEVICE_ID %d\n", ret);
194+
return -EINVAL;
195+
}
196+
197+
vendor_id = ((reg_data & PTN5150_REG_DEVICE_ID_VENDOR_MASK) >>
198+
PTN5150_REG_DEVICE_ID_VENDOR_SHIFT);
199+
version_id = ((reg_data & PTN5150_REG_DEVICE_ID_VERSION_MASK) >>
200+
PTN5150_REG_DEVICE_ID_VERSION_SHIFT);
201+
202+
dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n",
203+
version_id, vendor_id);
204+
205+
/* Clear any existing interrupts */
206+
ret = regmap_read(info->regmap, PTN5150_REG_INT_STATUS, &reg_data);
207+
if (ret) {
208+
dev_err(info->dev,
209+
"failed to read PTN5150_REG_INT_STATUS %d\n",
210+
ret);
211+
return -EINVAL;
212+
}
213+
214+
ret = regmap_read(info->regmap, PTN5150_REG_INT_REG_STATUS, &reg_data);
215+
if (ret) {
216+
dev_err(info->dev,
217+
"failed to read PTN5150_REG_INT_REG_STATUS %d\n", ret);
218+
return -EINVAL;
219+
}
220+
221+
return 0;
222+
}
223+
224+
static int ptn5150_i2c_probe(struct i2c_client *i2c,
225+
const struct i2c_device_id *id)
226+
{
227+
struct device *dev = &i2c->dev;
228+
struct device_node *np = i2c->dev.of_node;
229+
struct ptn5150_info *info;
230+
int ret;
231+
232+
if (!np)
233+
return -EINVAL;
234+
235+
info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL);
236+
if (!info)
237+
return -ENOMEM;
238+
i2c_set_clientdata(i2c, info);
239+
240+
info->dev = &i2c->dev;
241+
info->i2c = i2c;
242+
info->int_gpiod = devm_gpiod_get(&i2c->dev, "int", GPIOD_IN);
243+
if (IS_ERR(info->int_gpiod)) {
244+
dev_err(dev, "failed to get INT GPIO\n");
245+
return PTR_ERR(info->int_gpiod);
246+
}
247+
info->vbus_gpiod = devm_gpiod_get(&i2c->dev, "vbus", GPIOD_IN);
248+
if (IS_ERR(info->vbus_gpiod)) {
249+
dev_err(dev, "failed to get VBUS GPIO\n");
250+
return PTR_ERR(info->vbus_gpiod);
251+
}
252+
ret = gpiod_direction_output(info->vbus_gpiod, 0);
253+
if (ret) {
254+
dev_err(dev, "failed to set VBUS GPIO direction\n");
255+
return -EINVAL;
256+
}
257+
258+
mutex_init(&info->mutex);
259+
260+
INIT_WORK(&info->irq_work, ptn5150_irq_work);
261+
262+
info->regmap = devm_regmap_init_i2c(i2c, &ptn5150_regmap_config);
263+
if (IS_ERR(info->regmap)) {
264+
ret = PTR_ERR(info->regmap);
265+
dev_err(info->dev, "failed to allocate register map: %d\n",
266+
ret);
267+
return ret;
268+
}
269+
270+
if (info->int_gpiod) {
271+
info->irq = gpiod_to_irq(info->int_gpiod);
272+
if (info->irq < 0) {
273+
dev_err(dev, "failed to get INTB IRQ\n");
274+
return info->irq;
275+
}
276+
277+
ret = devm_request_threaded_irq(dev, info->irq, NULL,
278+
ptn5150_irq_handler,
279+
IRQF_TRIGGER_FALLING |
280+
IRQF_ONESHOT,
281+
i2c->name, info);
282+
if (ret < 0) {
283+
dev_err(dev, "failed to request handler for INTB IRQ\n");
284+
return ret;
285+
}
286+
}
287+
288+
/* Allocate extcon device */
289+
info->edev = devm_extcon_dev_allocate(info->dev, ptn5150_extcon_cable);
290+
if (IS_ERR(info->edev)) {
291+
dev_err(info->dev, "failed to allocate memory for extcon\n");
292+
return -ENOMEM;
293+
}
294+
295+
/* Register extcon device */
296+
ret = devm_extcon_dev_register(info->dev, info->edev);
297+
if (ret) {
298+
dev_err(info->dev, "failed to register extcon device\n");
299+
return ret;
300+
}
301+
302+
/* Initialize PTN5150 device and print vendor id and version id */
303+
ret = ptn5150_init_dev_type(info);
304+
if (ret)
305+
return -EINVAL;
306+
307+
return 0;
308+
}
309+
310+
static const struct of_device_id ptn5150_dt_match[] = {
311+
{ .compatible = "nxp,ptn5150" },
312+
{ },
313+
};
314+
MODULE_DEVICE_TABLE(of, ptn5150_dt_match);
315+
316+
static const struct i2c_device_id ptn5150_i2c_id[] = {
317+
{ "ptn5150", 0 },
318+
{ }
319+
};
320+
MODULE_DEVICE_TABLE(i2c, ptn5150_i2c_id);
321+
322+
static struct i2c_driver ptn5150_i2c_driver = {
323+
.driver = {
324+
.name = "ptn5150",
325+
.of_match_table = ptn5150_dt_match,
326+
},
327+
.probe = ptn5150_i2c_probe,
328+
.id_table = ptn5150_i2c_id,
329+
};
330+
331+
static int __init ptn5150_i2c_init(void)
332+
{
333+
return i2c_add_driver(&ptn5150_i2c_driver);
334+
}
335+
subsys_initcall(ptn5150_i2c_init);
336+
337+
MODULE_DESCRIPTION("NXP PTN5150 CC logic Extcon driver");
338+
MODULE_AUTHOR("Vijai Kumar K <[email protected]>");
339+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)