]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/extcon/extcon-gpio.c
extcon: gpio: Move platform data into state container
[mirror_ubuntu-jammy-kernel.git] / drivers / extcon / extcon-gpio.c
CommitLineData
be48308a 1/*
6ba12997 2 * extcon_gpio.c - Single-state GPIO extcon driver based on extcon class
be48308a
MH
3 *
4 * Copyright (C) 2008 Google, Inc.
5 * Author: Mike Lockwood <lockwood@android.com>
6 *
7 * Modified by MyungJoo Ham <myungjoo.ham@samsung.com> to support extcon
8 * (originally switch class is supported)
9 *
10 * This software is licensed under the terms of the GNU General Public
11 * License version 2, as published by the Free Software Foundation, and
12 * may be copied, distributed, and modified under those terms.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
6ba12997 18 */
be48308a 19
176aa360 20#include <linux/extcon-provider.h>
62364357 21#include <linux/gpio.h>
de992acb 22#include <linux/gpio/consumer.h>
be48308a
MH
23#include <linux/init.h>
24#include <linux/interrupt.h>
62364357
GC
25#include <linux/kernel.h>
26#include <linux/module.h>
be48308a
MH
27#include <linux/platform_device.h>
28#include <linux/slab.h>
be48308a 29#include <linux/workqueue.h>
be48308a 30
66afdedf 31/**
a62300d9
LW
32 * struct gpio_extcon_data - A simple GPIO-controlled extcon device state container.
33 * @edev: Extcon device.
34 * @irq: Interrupt line for the external connector.
35 * @work: Work fired by the interrupt.
36 * @debounce_jiffies: Number of jiffies to wait for the GPIO to stabilize, from the debounce
37 * value.
38 * @id_gpiod: GPIO descriptor for this external connector.
66afdedf
LW
39 * @extcon_id: The unique id of specific external connector.
40 * @gpio: Corresponding GPIO.
41 * @gpio_active_low: Boolean describing whether gpio active state is 1 or 0
42 * If true, low state of gpio means active.
43 * If false, high state of gpio means active.
44 * @debounce: Debounce time for GPIO IRQ in ms.
45 * @irq_flags: IRQ Flags (e.g., IRQF_TRIGGER_LOW).
46 * @check_on_resume: Boolean describing whether to check the state of gpio
47 * while resuming from sleep.
48 */
be48308a 49struct gpio_extcon_data {
60cd62d4 50 struct extcon_dev *edev;
be48308a
MH
51 int irq;
52 struct delayed_work work;
53 unsigned long debounce_jiffies;
de992acb 54 struct gpio_desc *id_gpiod;
a62300d9
LW
55 unsigned int extcon_id;
56 unsigned gpio;
57 bool gpio_active_low;
58 unsigned long debounce;
59 unsigned long irq_flags;
60 bool check_on_resume;
be48308a
MH
61};
62
63static void gpio_extcon_work(struct work_struct *work)
64{
65 int state;
66 struct gpio_extcon_data *data =
67 container_of(to_delayed_work(work), struct gpio_extcon_data,
68 work);
69
de992acb 70 state = gpiod_get_value_cansleep(data->id_gpiod);
a62300d9 71 if (data->gpio_active_low)
5bfbdc9c 72 state = !state;
cb9850d0 73
a62300d9 74 extcon_set_state_sync(data->edev, data->extcon_id, state);
be48308a
MH
75}
76
77static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
78{
60f9b9e6 79 struct gpio_extcon_data *data = dev_id;
be48308a 80
60f9b9e6
CC
81 queue_delayed_work(system_power_efficient_wq, &data->work,
82 data->debounce_jiffies);
be48308a
MH
83 return IRQ_HANDLED;
84}
85
de992acb
CC
86static int gpio_extcon_init(struct device *dev, struct gpio_extcon_data *data)
87{
de992acb
CC
88 int ret;
89
a62300d9 90 ret = devm_gpio_request_one(dev, data->gpio, GPIOF_DIR_IN,
de992acb
CC
91 dev_name(dev));
92 if (ret < 0)
93 return ret;
94
a62300d9 95 data->id_gpiod = gpio_to_desc(data->gpio);
de992acb
CC
96 if (!data->id_gpiod)
97 return -EINVAL;
98
a62300d9 99 if (data->debounce) {
de992acb 100 ret = gpiod_set_debounce(data->id_gpiod,
a62300d9 101 data->debounce * 1000);
de992acb
CC
102 if (ret < 0)
103 data->debounce_jiffies =
a62300d9 104 msecs_to_jiffies(data->debounce);
de992acb
CC
105 }
106
107 data->irq = gpiod_to_irq(data->id_gpiod);
108 if (data->irq < 0)
109 return data->irq;
110
111 return 0;
112}
113
44f34fd4 114static int gpio_extcon_probe(struct platform_device *pdev)
be48308a 115{
60f9b9e6 116 struct gpio_extcon_data *data;
1073514b 117 int ret;
be48308a 118
60f9b9e6 119 data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
be48308a 120 GFP_KERNEL);
60f9b9e6 121 if (!data)
be48308a 122 return -ENOMEM;
a62300d9
LW
123
124 /*
125 * FIXME: extcon_id represents the unique identifier of external
126 * connectors such as EXTCON_USB, EXTCON_DISP_HDMI and so on. extcon_id
127 * is necessary to register the extcon device. But, it's not yet
128 * developed to get the extcon id from device-tree or others.
129 * On later, it have to be solved.
130 */
131 if (!data->irq_flags || data->extcon_id > EXTCON_NONE)
132 return -EINVAL;
60cd62d4 133
de992acb
CC
134 /* Initialize the gpio */
135 ret = gpio_extcon_init(&pdev->dev, data);
4288d9b8
GR
136 if (ret < 0)
137 return ret;
138
de992acb 139 /* Allocate the memory of extcon devie and register extcon device */
a62300d9 140 data->edev = devm_extcon_dev_allocate(&pdev->dev, &data->extcon_id);
de992acb
CC
141 if (IS_ERR(data->edev)) {
142 dev_err(&pdev->dev, "failed to allocate extcon device\n");
143 return -ENOMEM;
338de0ca 144 }
be48308a 145
60f9b9e6 146 ret = devm_extcon_dev_register(&pdev->dev, data->edev);
be48308a 147 if (ret < 0)
01eaf245 148 return ret;
be48308a 149
60f9b9e6 150 INIT_DELAYED_WORK(&data->work, gpio_extcon_work);
be48308a 151
de992acb 152 /*
b51b3870 153 * Request the interrupt of gpio to detect whether external connector
de992acb
CC
154 * is attached or detached.
155 */
60f9b9e6 156 ret = devm_request_any_context_irq(&pdev->dev, data->irq,
a62300d9 157 gpio_irq_handler, data->irq_flags,
60f9b9e6 158 pdev->name, data);
be48308a 159 if (ret < 0)
d92c2f12 160 return ret;
be48308a 161
60f9b9e6 162 platform_set_drvdata(pdev, data);
be48308a 163 /* Perform initial detection */
60f9b9e6 164 gpio_extcon_work(&data->work.work);
be48308a
MH
165
166 return 0;
be48308a
MH
167}
168
93ed0327 169static int gpio_extcon_remove(struct platform_device *pdev)
be48308a 170{
60f9b9e6 171 struct gpio_extcon_data *data = platform_get_drvdata(pdev);
be48308a 172
60f9b9e6 173 cancel_delayed_work_sync(&data->work);
be48308a
MH
174
175 return 0;
176}
177
6544dfa5
RY
178#ifdef CONFIG_PM_SLEEP
179static int gpio_extcon_resume(struct device *dev)
180{
60f9b9e6 181 struct gpio_extcon_data *data;
6544dfa5 182
60f9b9e6 183 data = dev_get_drvdata(dev);
a62300d9 184 if (data->check_on_resume)
6544dfa5 185 queue_delayed_work(system_power_efficient_wq,
60f9b9e6 186 &data->work, data->debounce_jiffies);
6544dfa5
RY
187
188 return 0;
189}
190#endif
191
3cc731d9 192static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, NULL, gpio_extcon_resume);
6544dfa5 193
2878bda8 194static struct platform_driver gpio_extcon_driver = {
be48308a 195 .probe = gpio_extcon_probe,
5f7e2228 196 .remove = gpio_extcon_remove,
be48308a
MH
197 .driver = {
198 .name = "extcon-gpio",
6544dfa5 199 .pm = &gpio_extcon_pm_ops,
be48308a
MH
200 },
201};
202
2878bda8 203module_platform_driver(gpio_extcon_driver);
be48308a
MH
204
205MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
206MODULE_DESCRIPTION("GPIO extcon driver");
207MODULE_LICENSE("GPL");