blob: 1f7d56aff786c3b24607f9ca4e56b178221257f8 [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 Vaswanibb62f3e2013-03-01 14:14:48 -080016#include <linux/io.h>
Rohit Vaswania513aa8d2011-07-18 15:14:28 -070017#include <mach/gpiomux.h>
Rohit Vaswanibb62f3e2013-03-01 14:14:48 -080018#include <mach/msm_iomap.h>
Gregory Bean1963a2a2010-08-28 10:05:44 -070019
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020struct msm_gpiomux_rec {
21 struct gpiomux_setting *sets[GPIOMUX_NSETTINGS];
22 int ref;
23};
Gregory Bean1963a2a2010-08-28 10:05:44 -070024static DEFINE_SPINLOCK(gpiomux_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070025static struct msm_gpiomux_rec *msm_gpiomux_recs;
26static struct gpiomux_setting *msm_gpiomux_sets;
27static unsigned msm_gpiomux_ngpio;
Gregory Bean1963a2a2010-08-28 10:05:44 -070028
Rohit Vaswaniaf4a6d62013-02-22 12:19:58 -080029static int msm_gpiomux_store(unsigned gpio, enum msm_gpiomux_setting which,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030 struct gpiomux_setting *setting, struct gpiomux_setting *old_setting)
Gregory Bean1963a2a2010-08-28 10:05:44 -070031{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032 struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
33 unsigned set_slot = gpio * GPIOMUX_NSETTINGS + which;
Gregory Bean1963a2a2010-08-28 10:05:44 -070034 unsigned long irq_flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035 int status = 0;
Gregory Bean1963a2a2010-08-28 10:05:44 -070036
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037 if (!msm_gpiomux_recs)
38 return -EFAULT;
39
40 if (gpio >= msm_gpiomux_ngpio)
Gregory Bean1963a2a2010-08-28 10:05:44 -070041 return -EINVAL;
42
43 spin_lock_irqsave(&gpiomux_lock, irq_flags);
44
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045 if (old_setting) {
46 if (rec->sets[which] == NULL)
47 status = 1;
48 else
49 *old_setting = *(rec->sets[which]);
50 }
Gregory Bean1963a2a2010-08-28 10:05:44 -070051
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052 if (setting) {
53 msm_gpiomux_sets[set_slot] = *setting;
54 rec->sets[which] = &msm_gpiomux_sets[set_slot];
55 } else {
56 rec->sets[which] = NULL;
57 }
Gregory Bean1963a2a2010-08-28 10:05:44 -070058
Rohit Vaswaniaf4a6d62013-02-22 12:19:58 -080059 spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
60 return status;
61}
62
63int msm_gpiomux_write(unsigned gpio, enum msm_gpiomux_setting which,
64 struct gpiomux_setting *setting, struct gpiomux_setting *old_setting)
65{
66 int ret;
67 unsigned long irq_flags;
68 struct gpiomux_setting *new_set;
69 struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
70
71 ret = msm_gpiomux_store(gpio, which, setting, old_setting);
72 if (ret < 0)
73 return ret;
74
75 spin_lock_irqsave(&gpiomux_lock, irq_flags);
76
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077 new_set = rec->ref ? rec->sets[GPIOMUX_ACTIVE] :
78 rec->sets[GPIOMUX_SUSPENDED];
79 if (new_set)
80 __msm_gpiomux_write(gpio, *new_set);
Gregory Bean1963a2a2010-08-28 10:05:44 -070081
82 spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
Rohit Vaswaniaf4a6d62013-02-22 12:19:58 -080083 return ret;
Gregory Bean1963a2a2010-08-28 10:05:44 -070084}
85EXPORT_SYMBOL(msm_gpiomux_write);
86
87int msm_gpiomux_get(unsigned gpio)
88{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070089 struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
Gregory Bean1963a2a2010-08-28 10:05:44 -070090 unsigned long irq_flags;
91
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070092 if (!msm_gpiomux_recs)
93 return -EFAULT;
94
95 if (gpio >= msm_gpiomux_ngpio)
Gregory Bean1963a2a2010-08-28 10:05:44 -070096 return -EINVAL;
97
98 spin_lock_irqsave(&gpiomux_lock, irq_flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099 if (rec->ref++ == 0 && rec->sets[GPIOMUX_ACTIVE])
100 __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_ACTIVE]);
Gregory Bean1963a2a2010-08-28 10:05:44 -0700101 spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
102 return 0;
103}
104EXPORT_SYMBOL(msm_gpiomux_get);
105
106int msm_gpiomux_put(unsigned gpio)
107{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700108 struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
Gregory Bean1963a2a2010-08-28 10:05:44 -0700109 unsigned long irq_flags;
110
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 if (!msm_gpiomux_recs)
112 return -EFAULT;
113
114 if (gpio >= msm_gpiomux_ngpio)
Gregory Bean1963a2a2010-08-28 10:05:44 -0700115 return -EINVAL;
116
117 spin_lock_irqsave(&gpiomux_lock, irq_flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118 BUG_ON(rec->ref == 0);
119 if (--rec->ref == 0 && rec->sets[GPIOMUX_SUSPENDED])
120 __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_SUSPENDED]);
Gregory Bean1963a2a2010-08-28 10:05:44 -0700121 spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
122 return 0;
123}
124EXPORT_SYMBOL(msm_gpiomux_put);
125
Rohit Vaswanibb62f3e2013-03-01 14:14:48 -0800126void msm_tlmm_misc_reg_write(enum msm_tlmm_misc_reg misc_reg, int val)
127{
128 writel_relaxed(val, MSM_TLMM_BASE + misc_reg);
129 /* ensure the write completes before returning */
130 mb();
131}
132
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700133int msm_gpiomux_init(size_t ngpio)
Gregory Bean1963a2a2010-08-28 10:05:44 -0700134{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700135 if (!ngpio)
136 return -EINVAL;
Gregory Bean1963a2a2010-08-28 10:05:44 -0700137
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138 if (msm_gpiomux_recs)
139 return -EPERM;
140
141 msm_gpiomux_recs = kzalloc(sizeof(struct msm_gpiomux_rec) * ngpio,
142 GFP_KERNEL);
143 if (!msm_gpiomux_recs)
144 return -ENOMEM;
145
146 /* There is no need to zero this memory, as clients will be blindly
147 * installing settings on top of it.
148 */
149 msm_gpiomux_sets = kmalloc(sizeof(struct gpiomux_setting) * ngpio *
150 GPIOMUX_NSETTINGS, GFP_KERNEL);
151 if (!msm_gpiomux_sets) {
152 kfree(msm_gpiomux_recs);
153 msm_gpiomux_recs = NULL;
154 return -ENOMEM;
Gregory Bean1963a2a2010-08-28 10:05:44 -0700155 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156
157 msm_gpiomux_ngpio = ngpio;
158
Gregory Bean1963a2a2010-08-28 10:05:44 -0700159 return 0;
160}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161EXPORT_SYMBOL(msm_gpiomux_init);
162
Rohit Vaswaniaf4a6d62013-02-22 12:19:58 -0800163void msm_gpiomux_install_nowrite(struct msm_gpiomux_config *configs,
164 unsigned nconfigs)
165{
166 unsigned c, s;
167 int rc;
168
169 for (c = 0; c < nconfigs; ++c) {
170 for (s = 0; s < GPIOMUX_NSETTINGS; ++s) {
171 rc = msm_gpiomux_store(configs[c].gpio, s,
172 configs[c].settings[s], NULL);
173 if (rc)
174 pr_err("%s: write failure: %d\n", __func__, rc);
175 }
176 }
177}
178
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700179void msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs)
180{
181 unsigned c, s;
182 int rc;
183
184 for (c = 0; c < nconfigs; ++c) {
185 for (s = 0; s < GPIOMUX_NSETTINGS; ++s) {
186 rc = msm_gpiomux_write(configs[c].gpio, s,
187 configs[c].settings[s], NULL);
188 if (rc)
189 pr_err("%s: write failure: %d\n", __func__, rc);
190 }
191 }
192}
193EXPORT_SYMBOL(msm_gpiomux_install);
Rohit Vaswani341c2032012-11-08 18:49:29 -0800194
195int msm_gpiomux_init_dt(void)
196{
197 int rc;
198 unsigned int ngpio;
199 struct device_node *of_gpio_node;
200
201 of_gpio_node = of_find_compatible_node(NULL, NULL, "qcom,msm-gpio");
202 if (!of_gpio_node) {
203 pr_err("%s: Failed to find qcom,msm-gpio node\n", __func__);
204 return -ENODEV;
205 }
206
207 rc = of_property_read_u32(of_gpio_node, "ngpio", &ngpio);
208 if (rc) {
209 pr_err("%s: Failed to find ngpio property in msm-gpio device node %d\n"
210 , __func__, rc);
211 return rc;
212 }
213
214 return msm_gpiomux_init(ngpio);
215}
216EXPORT_SYMBOL(msm_gpiomux_init_dt);