diff options
Diffstat (limited to 'drivers/extcon/extcon-gpio.c')
-rw-r--r-- | drivers/extcon/extcon-gpio.c | 130 |
1 files changed, 70 insertions, 60 deletions
diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index 57c24fa52edb..279ff8f6637d 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c | |||
@@ -1,7 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/extcon/extcon_gpio.c | 2 | * extcon_gpio.c - Single-state GPIO extcon driver based on extcon class |
3 | * | ||
4 | * Single-state GPIO extcon driver based on extcon class | ||
5 | * | 3 | * |
6 | * Copyright (C) 2008 Google, Inc. | 4 | * Copyright (C) 2008 Google, Inc. |
7 | * Author: Mike Lockwood <lockwood@android.com> | 5 | * Author: Mike Lockwood <lockwood@android.com> |
@@ -17,12 +15,12 @@ | |||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | * GNU General Public License for more details. | 17 | * GNU General Public License for more details. |
20 | * | 18 | */ |
21 | */ | ||
22 | 19 | ||
23 | #include <linux/extcon.h> | 20 | #include <linux/extcon.h> |
24 | #include <linux/extcon/extcon-gpio.h> | 21 | #include <linux/extcon/extcon-gpio.h> |
25 | #include <linux/gpio.h> | 22 | #include <linux/gpio.h> |
23 | #include <linux/gpio/consumer.h> | ||
26 | #include <linux/init.h> | 24 | #include <linux/init.h> |
27 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
28 | #include <linux/kernel.h> | 26 | #include <linux/kernel.h> |
@@ -33,14 +31,12 @@ | |||
33 | 31 | ||
34 | struct gpio_extcon_data { | 32 | struct gpio_extcon_data { |
35 | struct extcon_dev *edev; | 33 | struct extcon_dev *edev; |
36 | unsigned gpio; | ||
37 | bool gpio_active_low; | ||
38 | const char *state_on; | ||
39 | const char *state_off; | ||
40 | int irq; | 34 | int irq; |
41 | struct delayed_work work; | 35 | struct delayed_work work; |
42 | unsigned long debounce_jiffies; | 36 | unsigned long debounce_jiffies; |
43 | bool check_on_resume; | 37 | |
38 | struct gpio_desc *id_gpiod; | ||
39 | struct gpio_extcon_pdata *pdata; | ||
44 | }; | 40 | }; |
45 | 41 | ||
46 | static void gpio_extcon_work(struct work_struct *work) | 42 | static void gpio_extcon_work(struct work_struct *work) |
@@ -50,93 +46,107 @@ static void gpio_extcon_work(struct work_struct *work) | |||
50 | container_of(to_delayed_work(work), struct gpio_extcon_data, | 46 | container_of(to_delayed_work(work), struct gpio_extcon_data, |
51 | work); | 47 | work); |
52 | 48 | ||
53 | state = gpio_get_value(data->gpio); | 49 | state = gpiod_get_value_cansleep(data->id_gpiod); |
54 | if (data->gpio_active_low) | 50 | if (data->pdata->gpio_active_low) |
55 | state = !state; | 51 | state = !state; |
56 | extcon_set_state(data->edev, state); | 52 | extcon_set_state(data->edev, state); |
57 | } | 53 | } |
58 | 54 | ||
59 | static irqreturn_t gpio_irq_handler(int irq, void *dev_id) | 55 | static irqreturn_t gpio_irq_handler(int irq, void *dev_id) |
60 | { | 56 | { |
61 | struct gpio_extcon_data *extcon_data = dev_id; | 57 | struct gpio_extcon_data *data = dev_id; |
62 | 58 | ||
63 | queue_delayed_work(system_power_efficient_wq, &extcon_data->work, | 59 | queue_delayed_work(system_power_efficient_wq, &data->work, |
64 | extcon_data->debounce_jiffies); | 60 | data->debounce_jiffies); |
65 | return IRQ_HANDLED; | 61 | return IRQ_HANDLED; |
66 | } | 62 | } |
67 | 63 | ||
64 | static int gpio_extcon_init(struct device *dev, struct gpio_extcon_data *data) | ||
65 | { | ||
66 | struct gpio_extcon_pdata *pdata = data->pdata; | ||
67 | int ret; | ||
68 | |||
69 | ret = devm_gpio_request_one(dev, pdata->gpio, GPIOF_DIR_IN, | ||
70 | dev_name(dev)); | ||
71 | if (ret < 0) | ||
72 | return ret; | ||
73 | |||
74 | data->id_gpiod = gpio_to_desc(pdata->gpio); | ||
75 | if (!data->id_gpiod) | ||
76 | return -EINVAL; | ||
77 | |||
78 | if (pdata->debounce) { | ||
79 | ret = gpiod_set_debounce(data->id_gpiod, | ||
80 | pdata->debounce * 1000); | ||
81 | if (ret < 0) | ||
82 | data->debounce_jiffies = | ||
83 | msecs_to_jiffies(pdata->debounce); | ||
84 | } | ||
85 | |||
86 | data->irq = gpiod_to_irq(data->id_gpiod); | ||
87 | if (data->irq < 0) | ||
88 | return data->irq; | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
68 | static int gpio_extcon_probe(struct platform_device *pdev) | 93 | static int gpio_extcon_probe(struct platform_device *pdev) |
69 | { | 94 | { |
70 | struct gpio_extcon_platform_data *pdata = dev_get_platdata(&pdev->dev); | 95 | struct gpio_extcon_pdata *pdata = dev_get_platdata(&pdev->dev); |
71 | struct gpio_extcon_data *extcon_data; | 96 | struct gpio_extcon_data *data; |
72 | int ret; | 97 | int ret; |
73 | 98 | ||
74 | if (!pdata) | 99 | if (!pdata) |
75 | return -EBUSY; | 100 | return -EBUSY; |
76 | if (!pdata->irq_flags) { | 101 | if (!pdata->irq_flags || pdata->extcon_id > EXTCON_NONE) |
77 | dev_err(&pdev->dev, "IRQ flag is not specified.\n"); | ||
78 | return -EINVAL; | 102 | return -EINVAL; |
79 | } | ||
80 | 103 | ||
81 | extcon_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data), | 104 | data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data), |
82 | GFP_KERNEL); | 105 | GFP_KERNEL); |
83 | if (!extcon_data) | 106 | if (!data) |
84 | return -ENOMEM; | 107 | return -ENOMEM; |
108 | data->pdata = pdata; | ||
85 | 109 | ||
86 | extcon_data->edev = devm_extcon_dev_allocate(&pdev->dev, NULL); | 110 | /* Initialize the gpio */ |
87 | if (IS_ERR(extcon_data->edev)) { | 111 | ret = gpio_extcon_init(&pdev->dev, data); |
88 | dev_err(&pdev->dev, "failed to allocate extcon device\n"); | ||
89 | return -ENOMEM; | ||
90 | } | ||
91 | |||
92 | extcon_data->gpio = pdata->gpio; | ||
93 | extcon_data->gpio_active_low = pdata->gpio_active_low; | ||
94 | extcon_data->state_on = pdata->state_on; | ||
95 | extcon_data->state_off = pdata->state_off; | ||
96 | extcon_data->check_on_resume = pdata->check_on_resume; | ||
97 | |||
98 | ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN, | ||
99 | pdev->name); | ||
100 | if (ret < 0) | 112 | if (ret < 0) |
101 | return ret; | 113 | return ret; |
102 | 114 | ||
103 | if (pdata->debounce) { | 115 | /* Allocate the memory of extcon devie and register extcon device */ |
104 | ret = gpio_set_debounce(extcon_data->gpio, | 116 | data->edev = devm_extcon_dev_allocate(&pdev->dev, &pdata->extcon_id); |
105 | pdata->debounce * 1000); | 117 | if (IS_ERR(data->edev)) { |
106 | if (ret < 0) | 118 | dev_err(&pdev->dev, "failed to allocate extcon device\n"); |
107 | extcon_data->debounce_jiffies = | 119 | return -ENOMEM; |
108 | msecs_to_jiffies(pdata->debounce); | ||
109 | } | 120 | } |
110 | 121 | ||
111 | ret = devm_extcon_dev_register(&pdev->dev, extcon_data->edev); | 122 | ret = devm_extcon_dev_register(&pdev->dev, data->edev); |
112 | if (ret < 0) | 123 | if (ret < 0) |
113 | return ret; | 124 | return ret; |
114 | 125 | ||
115 | INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work); | 126 | INIT_DELAYED_WORK(&data->work, gpio_extcon_work); |
116 | |||
117 | extcon_data->irq = gpio_to_irq(extcon_data->gpio); | ||
118 | if (extcon_data->irq < 0) | ||
119 | return extcon_data->irq; | ||
120 | 127 | ||
121 | ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler, | 128 | /* |
122 | pdata->irq_flags, pdev->name, | 129 | * Request the interrput of gpio to detect whether external connector |
123 | extcon_data); | 130 | * is attached or detached. |
131 | */ | ||
132 | ret = devm_request_any_context_irq(&pdev->dev, data->irq, | ||
133 | gpio_irq_handler, pdata->irq_flags, | ||
134 | pdev->name, data); | ||
124 | if (ret < 0) | 135 | if (ret < 0) |
125 | return ret; | 136 | return ret; |
126 | 137 | ||
127 | platform_set_drvdata(pdev, extcon_data); | 138 | platform_set_drvdata(pdev, data); |
128 | /* Perform initial detection */ | 139 | /* Perform initial detection */ |
129 | gpio_extcon_work(&extcon_data->work.work); | 140 | gpio_extcon_work(&data->work.work); |
130 | 141 | ||
131 | return 0; | 142 | return 0; |
132 | } | 143 | } |
133 | 144 | ||
134 | static int gpio_extcon_remove(struct platform_device *pdev) | 145 | static int gpio_extcon_remove(struct platform_device *pdev) |
135 | { | 146 | { |
136 | struct gpio_extcon_data *extcon_data = platform_get_drvdata(pdev); | 147 | struct gpio_extcon_data *data = platform_get_drvdata(pdev); |
137 | 148 | ||
138 | cancel_delayed_work_sync(&extcon_data->work); | 149 | cancel_delayed_work_sync(&data->work); |
139 | free_irq(extcon_data->irq, extcon_data); | ||
140 | 150 | ||
141 | return 0; | 151 | return 0; |
142 | } | 152 | } |
@@ -144,12 +154,12 @@ static int gpio_extcon_remove(struct platform_device *pdev) | |||
144 | #ifdef CONFIG_PM_SLEEP | 154 | #ifdef CONFIG_PM_SLEEP |
145 | static int gpio_extcon_resume(struct device *dev) | 155 | static int gpio_extcon_resume(struct device *dev) |
146 | { | 156 | { |
147 | struct gpio_extcon_data *extcon_data; | 157 | struct gpio_extcon_data *data; |
148 | 158 | ||
149 | extcon_data = dev_get_drvdata(dev); | 159 | data = dev_get_drvdata(dev); |
150 | if (extcon_data->check_on_resume) | 160 | if (data->pdata->check_on_resume) |
151 | queue_delayed_work(system_power_efficient_wq, | 161 | queue_delayed_work(system_power_efficient_wq, |
152 | &extcon_data->work, extcon_data->debounce_jiffies); | 162 | &data->work, data->debounce_jiffies); |
153 | 163 | ||
154 | return 0; | 164 | return 0; |
155 | } | 165 | } |