blob: 471421081f6b1070401bd93e5d76ddfe26fe0d52 [file] [log] [blame]
Rohit Vaswaniaf4a6d62013-02-22 12:19:58 -08001/* Copyright (c) 2010,2013, The Linux Foundation. All rights reserved.
Gregory Bean1963a2a2010-08-28 10:05:44 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
Gregory Bean1963a2a2010-08-28 10:05:44 -070011 */
12#include <linux/module.h>
Rohit Vaswani341c2032012-11-08 18:49:29 -080013#include <linux/of.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070014#include <linux/slab.h>
Gregory Bean1963a2a2010-08-28 10:05:44 -070015#include <linux/spinlock.h>
Rohit Vaswania513aa8d2011-07-18 15:14:28 -070016#include <mach/gpiomux.h>
Gregory Bean1963a2a2010-08-28 10:05:44 -070017
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018struct msm_gpiomux_rec {
19 struct gpiomux_setting *sets[GPIOMUX_NSETTINGS];
20 int ref;
21};
Gregory Bean1963a2a2010-08-28 10:05:44 -070022static DEFINE_SPINLOCK(gpiomux_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070023static struct msm_gpiomux_rec *msm_gpiomux_recs;
24static struct gpiomux_setting *msm_gpiomux_sets;
25static unsigned msm_gpiomux_ngpio;
Gregory Bean1963a2a2010-08-28 10:05:44 -070026
Rohit Vaswaniaf4a6d62013-02-22 12:19:58 -080027static int msm_gpiomux_store(unsigned gpio, enum msm_gpiomux_setting which,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028 struct gpiomux_setting *setting, struct gpiomux_setting *old_setting)
Gregory Bean1963a2a2010-08-28 10:05:44 -070029{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030 struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
31 unsigned set_slot = gpio * GPIOMUX_NSETTINGS + which;
Gregory Bean1963a2a2010-08-28 10:05:44 -070032 unsigned long irq_flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033 int status = 0;
Gregory Bean1963a2a2010-08-28 10:05:44 -070034
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035 if (!msm_gpiomux_recs)
36 return -EFAULT;
37
38 if (gpio >= msm_gpiomux_ngpio)
Gregory Bean1963a2a2010-08-28 10:05:44 -070039 return -EINVAL;
40
41 spin_lock_irqsave(&gpiomux_lock, irq_flags);
42
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043 if (old_setting) {
44 if (rec->sets[which] == NULL)
45 status = 1;
46 else
47 *old_setting = *(rec->sets[which]);
48 }
Gregory Bean1963a2a2010-08-28 10:05:44 -070049
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050 if (setting) {
51 msm_gpiomux_sets[set_slot] = *setting;
52 rec->sets[which] = &msm_gpiomux_sets[set_slot];
53 } else {
54 rec->sets[which] = NULL;
55 }
Gregory Bean1963a2a2010-08-28 10:05:44 -070056
Rohit Vaswaniaf4a6d62013-02-22 12:19:58 -080057 spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
58 return status;
59}
60
61int msm_gpiomux_write(unsigned gpio, enum msm_gpiomux_setting which,
62 struct gpiomux_setting *setting, struct gpiomux_setting *old_setting)
63{
64 int ret;
65 unsigned long irq_flags;
66 struct gpiomux_setting *new_set;
67 struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
68
69 ret = msm_gpiomux_store(gpio, which, setting, old_setting);
70 if (ret < 0)
71 return ret;
72
73 spin_lock_irqsave(&gpiomux_lock, irq_flags);
74
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070075 new_set = rec->ref ? rec->sets[GPIOMUX_ACTIVE] :
76 rec->sets[GPIOMUX_SUSPENDED];
77 if (new_set)
78 __msm_gpiomux_write(gpio, *new_set);
Gregory Bean1963a2a2010-08-28 10:05:44 -070079
80 spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
Rohit Vaswaniaf4a6d62013-02-22 12:19:58 -080081 return ret;
Gregory Bean1963a2a2010-08-28 10:05:44 -070082}
83EXPORT_SYMBOL(msm_gpiomux_write);
84
85int msm_gpiomux_get(unsigned gpio)
86{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087 struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
Gregory Bean1963a2a2010-08-28 10:05:44 -070088 unsigned long irq_flags;
89
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070090 if (!msm_gpiomux_recs)
91 return -EFAULT;
92
93 if (gpio >= msm_gpiomux_ngpio)
Gregory Bean1963a2a2010-08-28 10:05:44 -070094 return -EINVAL;
95
96 spin_lock_irqsave(&gpiomux_lock, irq_flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097 if (rec->ref++ == 0 && rec->sets[GPIOMUX_ACTIVE])
98 __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_ACTIVE]);
Gregory Bean1963a2a2010-08-28 10:05:44 -070099 spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
100 return 0;
101}
102EXPORT_SYMBOL(msm_gpiomux_get);
103
104int msm_gpiomux_put(unsigned gpio)
105{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106 struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
Gregory Bean1963a2a2010-08-28 10:05:44 -0700107 unsigned long irq_flags;
108
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109 if (!msm_gpiomux_recs)
110 return -EFAULT;
111
112 if (gpio >= msm_gpiomux_ngpio)
Gregory Bean1963a2a2010-08-28 10:05:44 -0700113 return -EINVAL;
114
115 spin_lock_irqsave(&gpiomux_lock, irq_flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116 BUG_ON(rec->ref == 0);
117 if (--rec->ref == 0 && rec->sets[GPIOMUX_SUSPENDED])
118 __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_SUSPENDED]);
Gregory Bean1963a2a2010-08-28 10:05:44 -0700119 spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
120 return 0;
121}
122EXPORT_SYMBOL(msm_gpiomux_put);
123
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124int msm_gpiomux_init(size_t ngpio)
Gregory Bean1963a2a2010-08-28 10:05:44 -0700125{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126 if (!ngpio)
127 return -EINVAL;
Gregory Bean1963a2a2010-08-28 10:05:44 -0700128
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129 if (msm_gpiomux_recs)
130 return -EPERM;
131
132 msm_gpiomux_recs = kzalloc(sizeof(struct msm_gpiomux_rec) * ngpio,
133 GFP_KERNEL);
134 if (!msm_gpiomux_recs)
135 return -ENOMEM;
136
137 /* There is no need to zero this memory, as clients will be blindly
138 * installing settings on top of it.
139 */
140 msm_gpiomux_sets = kmalloc(sizeof(struct gpiomux_setting) * ngpio *
141 GPIOMUX_NSETTINGS, GFP_KERNEL);
142 if (!msm_gpiomux_sets) {
143 kfree(msm_gpiomux_recs);
144 msm_gpiomux_recs = NULL;
145 return -ENOMEM;
Gregory Bean1963a2a2010-08-28 10:05:44 -0700146 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147
148 msm_gpiomux_ngpio = ngpio;
149
Gregory Bean1963a2a2010-08-28 10:05:44 -0700150 return 0;
151}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152EXPORT_SYMBOL(msm_gpiomux_init);
153
Rohit Vaswaniaf4a6d62013-02-22 12:19:58 -0800154void msm_gpiomux_install_nowrite(struct msm_gpiomux_config *configs,
155 unsigned nconfigs)
156{
157 unsigned c, s;
158 int rc;
159
160 for (c = 0; c < nconfigs; ++c) {
161 for (s = 0; s < GPIOMUX_NSETTINGS; ++s) {
162 rc = msm_gpiomux_store(configs[c].gpio, s,
163 configs[c].settings[s], NULL);
164 if (rc)
165 pr_err("%s: write failure: %d\n", __func__, rc);
166 }
167 }
168}
169
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700170void msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs)
171{
172 unsigned c, s;
173 int rc;
174
175 for (c = 0; c < nconfigs; ++c) {
176 for (s = 0; s < GPIOMUX_NSETTINGS; ++s) {
177 rc = msm_gpiomux_write(configs[c].gpio, s,
178 configs[c].settings[s], NULL);
179 if (rc)
180 pr_err("%s: write failure: %d\n", __func__, rc);
181 }
182 }
183}
184EXPORT_SYMBOL(msm_gpiomux_install);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800185
186int msm_gpiomux_init_dt(void)
187{
188 int rc;
189 unsigned int ngpio;
190 struct device_node *of_gpio_node;
191
192 of_gpio_node = of_find_compatible_node(NULL, NULL, "qcom,msm-gpio");
193 if (!of_gpio_node) {
194 pr_err("%s: Failed to find qcom,msm-gpio node\n", __func__);
195 return -ENODEV;
196 }
197
198 rc = of_property_read_u32(of_gpio_node, "ngpio", &ngpio);
199 if (rc) {
200 pr_err("%s: Failed to find ngpio property in msm-gpio device node %d\n"
201 , __func__, rc);
202 return rc;
203 }
204
205 return msm_gpiomux_init(ngpio);
206}
207EXPORT_SYMBOL(msm_gpiomux_init_dt);