Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/extcon/extcon-max3355.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Maxim Integrated MAX3355 USB OTG chip extcon driver
4
*
5
* Copyright (C) 2014-2015 Cogent Embedded, Inc.
6
* Author: Sergei Shtylyov <[email protected]>
7
*/
8
9
#include <linux/extcon-provider.h>
10
#include <linux/gpio/consumer.h>
11
#include <linux/interrupt.h>
12
#include <linux/module.h>
13
#include <linux/mod_devicetable.h>
14
#include <linux/platform_device.h>
15
16
struct max3355_data {
17
struct extcon_dev *edev;
18
struct gpio_desc *id_gpiod;
19
struct gpio_desc *shdn_gpiod;
20
};
21
22
static const unsigned int max3355_cable[] = {
23
EXTCON_USB,
24
EXTCON_USB_HOST,
25
EXTCON_NONE,
26
};
27
28
static irqreturn_t max3355_id_irq(int irq, void *dev_id)
29
{
30
struct max3355_data *data = dev_id;
31
int id = gpiod_get_value_cansleep(data->id_gpiod);
32
33
if (id) {
34
/*
35
* ID = 1 means USB HOST cable detached.
36
* As we don't have event for USB peripheral cable attached,
37
* we simulate USB peripheral attach here.
38
*/
39
extcon_set_state_sync(data->edev, EXTCON_USB_HOST, false);
40
extcon_set_state_sync(data->edev, EXTCON_USB, true);
41
} else {
42
/*
43
* ID = 0 means USB HOST cable attached.
44
* As we don't have event for USB peripheral cable detached,
45
* we simulate USB peripheral detach here.
46
*/
47
extcon_set_state_sync(data->edev, EXTCON_USB, false);
48
extcon_set_state_sync(data->edev, EXTCON_USB_HOST, true);
49
}
50
51
return IRQ_HANDLED;
52
}
53
54
static int max3355_probe(struct platform_device *pdev)
55
{
56
struct max3355_data *data;
57
struct gpio_desc *gpiod;
58
int irq, err;
59
60
data = devm_kzalloc(&pdev->dev, sizeof(struct max3355_data),
61
GFP_KERNEL);
62
if (!data)
63
return -ENOMEM;
64
65
gpiod = devm_gpiod_get(&pdev->dev, "id", GPIOD_IN);
66
if (IS_ERR(gpiod)) {
67
dev_err(&pdev->dev, "failed to get ID_OUT GPIO\n");
68
return PTR_ERR(gpiod);
69
}
70
data->id_gpiod = gpiod;
71
72
gpiod = devm_gpiod_get(&pdev->dev, "maxim,shdn", GPIOD_OUT_HIGH);
73
if (IS_ERR(gpiod)) {
74
dev_err(&pdev->dev, "failed to get SHDN# GPIO\n");
75
return PTR_ERR(gpiod);
76
}
77
data->shdn_gpiod = gpiod;
78
79
data->edev = devm_extcon_dev_allocate(&pdev->dev, max3355_cable);
80
if (IS_ERR(data->edev)) {
81
dev_err(&pdev->dev, "failed to allocate extcon device\n");
82
return PTR_ERR(data->edev);
83
}
84
85
err = devm_extcon_dev_register(&pdev->dev, data->edev);
86
if (err < 0) {
87
dev_err(&pdev->dev, "failed to register extcon device\n");
88
return err;
89
}
90
91
irq = gpiod_to_irq(data->id_gpiod);
92
if (irq < 0) {
93
dev_err(&pdev->dev, "failed to translate ID_OUT GPIO to IRQ\n");
94
return irq;
95
}
96
97
err = devm_request_threaded_irq(&pdev->dev, irq, NULL, max3355_id_irq,
98
IRQF_ONESHOT | IRQF_NO_SUSPEND |
99
IRQF_TRIGGER_RISING |
100
IRQF_TRIGGER_FALLING,
101
pdev->name, data);
102
if (err < 0) {
103
dev_err(&pdev->dev, "failed to request ID_OUT IRQ\n");
104
return err;
105
}
106
107
platform_set_drvdata(pdev, data);
108
109
/* Perform initial detection */
110
max3355_id_irq(irq, data);
111
112
return 0;
113
}
114
115
static void max3355_remove(struct platform_device *pdev)
116
{
117
struct max3355_data *data = platform_get_drvdata(pdev);
118
119
gpiod_set_value_cansleep(data->shdn_gpiod, 0);
120
}
121
122
static const struct of_device_id max3355_match_table[] = {
123
{ .compatible = "maxim,max3355", },
124
{ }
125
};
126
MODULE_DEVICE_TABLE(of, max3355_match_table);
127
128
static struct platform_driver max3355_driver = {
129
.probe = max3355_probe,
130
.remove = max3355_remove,
131
.driver = {
132
.name = "extcon-max3355",
133
.of_match_table = max3355_match_table,
134
},
135
};
136
137
module_platform_driver(max3355_driver);
138
139
MODULE_AUTHOR("Sergei Shtylyov <[email protected]>");
140
MODULE_DESCRIPTION("Maxim MAX3355 extcon driver");
141
MODULE_LICENSE("GPL v2");
142
143