blob: c611345fa70a549fee209162c338971b750f47b9 [file] [log] [blame]
Meng Wang43bbb872018-12-10 12:32:05 +08001// SPDX-License-Identifier: GPL-2.0-only
Xiaojun Sang53cd13a2018-06-29 15:14:37 +08002/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05303 */
4
5#include <linux/kernel.h>
6#include <linux/init.h>
7#include <linux/err.h>
8#include <linux/module.h>
9#include <linux/of.h>
10#include <linux/of_device.h>
11#include <linux/platform_device.h>
12#include <linux/gpio.h>
13#include <linux/of_gpio.h>
Sudheer Papothi124ec092019-08-01 10:21:08 +053014#include <linux/pinctrl/qcom-pinctrl.h>
Meng Wang11a25cf2018-10-31 14:11:26 +080015#include <asoc/msm-cdc-pinctrl.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053016
17struct msm_cdc_pinctrl_info {
18 struct pinctrl *pinctrl;
19 struct pinctrl_state *pinctrl_active;
20 struct pinctrl_state *pinctrl_sleep;
21 int gpio;
22 bool state;
Sudheer Papothi124ec092019-08-01 10:21:08 +053023 u32 tlmm_gpio;
24 bool wakeup_capable;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053025};
26
27static struct msm_cdc_pinctrl_info *msm_cdc_pinctrl_get_gpiodata(
28 struct device_node *np)
29{
30 struct platform_device *pdev;
31 struct msm_cdc_pinctrl_info *gpio_data;
32
33 if (!np) {
34 pr_err("%s: device node is null\n", __func__);
35 return NULL;
36 }
37
38 pdev = of_find_device_by_node(np);
39 if (!pdev) {
40 pr_err("%s: platform device not found!\n", __func__);
41 return NULL;
42 }
43
44 gpio_data = dev_get_drvdata(&pdev->dev);
45 if (!gpio_data)
46 dev_err(&pdev->dev, "%s: cannot find cdc gpio info\n",
47 __func__);
48
49 return gpio_data;
50}
51
52/*
53 * msm_cdc_get_gpio_state: select pinctrl sleep state
54 * @np: pointer to struct device_node
55 *
56 * Returns error code for failure and GPIO value on success
57 */
58int msm_cdc_get_gpio_state(struct device_node *np)
59{
60 struct msm_cdc_pinctrl_info *gpio_data;
61 int value = -EINVAL;
62
63 gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
64 if (!gpio_data)
65 return value;
66
67 if (gpio_is_valid(gpio_data->gpio))
68 value = gpio_get_value_cansleep(gpio_data->gpio);
69
70 return value;
71}
72EXPORT_SYMBOL(msm_cdc_get_gpio_state);
73
74/*
75 * msm_cdc_pinctrl_select_sleep_state: select pinctrl sleep state
76 * @np: pointer to struct device_node
77 *
78 * Returns error code for failure
79 */
80int msm_cdc_pinctrl_select_sleep_state(struct device_node *np)
81{
82 struct msm_cdc_pinctrl_info *gpio_data;
83
84 gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
85 if (!gpio_data)
86 return -EINVAL;
87
88 if (!gpio_data->pinctrl_sleep) {
89 pr_err("%s: pinctrl sleep state is null\n", __func__);
90 return -EINVAL;
91 }
92 gpio_data->state = false;
93
94 return pinctrl_select_state(gpio_data->pinctrl,
95 gpio_data->pinctrl_sleep);
96}
97EXPORT_SYMBOL(msm_cdc_pinctrl_select_sleep_state);
98
99/*
100 * msm_cdc_pinctrl_select_active_state: select pinctrl active state
101 * @np: pointer to struct device_node
102 *
103 * Returns error code for failure
104 */
105int msm_cdc_pinctrl_select_active_state(struct device_node *np)
106{
107 struct msm_cdc_pinctrl_info *gpio_data;
108
109 gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
110 if (!gpio_data)
111 return -EINVAL;
112
113 if (!gpio_data->pinctrl_active) {
114 pr_err("%s: pinctrl active state is null\n", __func__);
115 return -EINVAL;
116 }
117 gpio_data->state = true;
118
119 return pinctrl_select_state(gpio_data->pinctrl,
120 gpio_data->pinctrl_active);
121}
122EXPORT_SYMBOL(msm_cdc_pinctrl_select_active_state);
123
124/*
125 * msm_cdc_pinctrl_get_state: get curren pinctrl state
126 * @np: pointer to struct device_node
127 *
Karthikeyan Mani326536d2019-06-03 13:29:43 -0700128 * Returns 0 for sleep state, 1 for active state,
129 * error code for failure
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530130 */
Karthikeyan Mani326536d2019-06-03 13:29:43 -0700131int msm_cdc_pinctrl_get_state(struct device_node *np)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530132{
133 struct msm_cdc_pinctrl_info *gpio_data;
134
135 gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
136 if (!gpio_data)
137 return -EINVAL;
138
139 return gpio_data->state;
140}
141EXPORT_SYMBOL(msm_cdc_pinctrl_get_state);
142
Sudheer Papothi124ec092019-08-01 10:21:08 +0530143/*
144 * msm_cdc_pinctrl_set_wakeup_capable: Set a pinctrl to wakeup capable
145 * @np: pointer to struct device_node
146 * @enable: wakeup capable when set to true
147 *
148 * Returns 0 for success and error code for failure
149 */
150int msm_cdc_pinctrl_set_wakeup_capable(struct device_node *np, bool enable)
151{
152 struct msm_cdc_pinctrl_info *gpio_data;
153 int ret = 0;
154
155 gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
156 if (!gpio_data)
157 return -EINVAL;
158
159 if (gpio_data->wakeup_capable)
160 ret = msm_gpio_mpm_wake_set(gpio_data->tlmm_gpio, enable);
161
162 return ret;
163}
164EXPORT_SYMBOL(msm_cdc_pinctrl_set_wakeup_capable);
165
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530166static int msm_cdc_pinctrl_probe(struct platform_device *pdev)
167{
168 int ret = 0;
169 struct msm_cdc_pinctrl_info *gpio_data;
Sudheer Papothi124ec092019-08-01 10:21:08 +0530170 u32 tlmm_gpio = 0;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530171
172 gpio_data = devm_kzalloc(&pdev->dev,
173 sizeof(struct msm_cdc_pinctrl_info),
174 GFP_KERNEL);
175 if (!gpio_data)
176 return -ENOMEM;
177
178 gpio_data->pinctrl = devm_pinctrl_get(&pdev->dev);
179 if (IS_ERR_OR_NULL(gpio_data->pinctrl)) {
180 dev_err(&pdev->dev, "%s: Cannot get cdc gpio pinctrl:%ld\n",
181 __func__, PTR_ERR(gpio_data->pinctrl));
182 ret = PTR_ERR(gpio_data->pinctrl);
183 goto err_pctrl_get;
184 }
185
186 gpio_data->pinctrl_active = pinctrl_lookup_state(
187 gpio_data->pinctrl, "aud_active");
188 if (IS_ERR_OR_NULL(gpio_data->pinctrl_active)) {
189 dev_err(&pdev->dev, "%s: Cannot get aud_active pinctrl state:%ld\n",
190 __func__, PTR_ERR(gpio_data->pinctrl_active));
191 ret = PTR_ERR(gpio_data->pinctrl_active);
192 goto err_lookup_state;
193 }
194
195 gpio_data->pinctrl_sleep = pinctrl_lookup_state(
196 gpio_data->pinctrl, "aud_sleep");
197 if (IS_ERR_OR_NULL(gpio_data->pinctrl_sleep)) {
198 dev_err(&pdev->dev, "%s: Cannot get aud_sleep pinctrl state:%ld\n",
199 __func__, PTR_ERR(gpio_data->pinctrl_sleep));
200 ret = PTR_ERR(gpio_data->pinctrl_sleep);
201 goto err_lookup_state;
202 }
203 /* skip setting to sleep state for LPI_TLMM GPIOs */
204 if (!of_property_read_bool(pdev->dev.of_node, "qcom,lpi-gpios")) {
205 /* Set pinctrl state to aud_sleep by default */
206 ret = pinctrl_select_state(gpio_data->pinctrl,
207 gpio_data->pinctrl_sleep);
208 if (ret)
209 dev_err(&pdev->dev, "%s: set cdc gpio sleep state fail: %d\n",
210 __func__, ret);
211 }
212
Sudheer Papothi124ec092019-08-01 10:21:08 +0530213 if (!of_property_read_u32(pdev->dev.of_node, "qcom,tlmm-gpio",
214 &tlmm_gpio)) {
215 gpio_data->wakeup_capable = true;
216 gpio_data->tlmm_gpio = tlmm_gpio;
217 }
218
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530219 gpio_data->gpio = of_get_named_gpio(pdev->dev.of_node,
220 "qcom,cdc-rst-n-gpio", 0);
221 if (gpio_is_valid(gpio_data->gpio)) {
222 ret = gpio_request(gpio_data->gpio, "MSM_CDC_RESET");
223 if (ret) {
224 dev_err(&pdev->dev, "%s: Failed to request gpio %d\n",
225 __func__, gpio_data->gpio);
226 goto err_lookup_state;
227 }
228 }
229
230 dev_set_drvdata(&pdev->dev, gpio_data);
231 return 0;
232
233err_lookup_state:
234 devm_pinctrl_put(gpio_data->pinctrl);
235err_pctrl_get:
236 devm_kfree(&pdev->dev, gpio_data);
237 return ret;
238}
239
240static int msm_cdc_pinctrl_remove(struct platform_device *pdev)
241{
242 struct msm_cdc_pinctrl_info *gpio_data;
243
244 gpio_data = dev_get_drvdata(&pdev->dev);
245
Laxminath Kasam8f7ccc22017-08-28 17:35:04 +0530246 /* to free the requested gpio before exiting */
247 if (gpio_data) {
248 if (gpio_is_valid(gpio_data->gpio))
249 gpio_free(gpio_data->gpio);
250
251 if (gpio_data->pinctrl)
252 devm_pinctrl_put(gpio_data->pinctrl);
253 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530254
255 devm_kfree(&pdev->dev, gpio_data);
256
257 return 0;
258}
259
260static const struct of_device_id msm_cdc_pinctrl_match[] = {
261 {.compatible = "qcom,msm-cdc-pinctrl"},
262 {}
263};
264
265static struct platform_driver msm_cdc_pinctrl_driver = {
266 .driver = {
267 .name = "msm-cdc-pinctrl",
268 .owner = THIS_MODULE,
269 .of_match_table = msm_cdc_pinctrl_match,
Xiaojun Sang53cd13a2018-06-29 15:14:37 +0800270 .suppress_bind_attrs = true,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530271 },
272 .probe = msm_cdc_pinctrl_probe,
273 .remove = msm_cdc_pinctrl_remove,
274};
275
276int msm_cdc_pinctrl_drv_init(void)
277{
278 return platform_driver_register(&msm_cdc_pinctrl_driver);
279}
280
281void msm_cdc_pinctrl_drv_exit(void)
282{
283 platform_driver_unregister(&msm_cdc_pinctrl_driver);
284}
285MODULE_DESCRIPTION("MSM CODEC pin control platform driver");
286MODULE_LICENSE("GPL v2");