blob: 68b97ce8808f9dd6ef3dd5a4de7fdcef61e6c053 [file] [log] [blame]
Gregory Bean1963a2a2010-08-28 10:05:44 -07001/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070013#include <linux/slab.h>
Gregory Bean1963a2a2010-08-28 10:05:44 -070014#include <linux/spinlock.h>
15#include "gpiomux.h"
16
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070017struct msm_gpiomux_rec {
18 struct gpiomux_setting *sets[GPIOMUX_NSETTINGS];
19 int ref;
20};
Gregory Bean1963a2a2010-08-28 10:05:44 -070021static DEFINE_SPINLOCK(gpiomux_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022static struct msm_gpiomux_rec *msm_gpiomux_recs;
23static struct gpiomux_setting *msm_gpiomux_sets;
24static unsigned msm_gpiomux_ngpio;
Gregory Bean1963a2a2010-08-28 10:05:44 -070025
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026int msm_gpiomux_write(unsigned gpio, enum msm_gpiomux_setting which,
27 struct gpiomux_setting *setting, struct gpiomux_setting *old_setting)
Gregory Bean1963a2a2010-08-28 10:05:44 -070028{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029 struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
30 unsigned set_slot = gpio * GPIOMUX_NSETTINGS + which;
Gregory Bean1963a2a2010-08-28 10:05:44 -070031 unsigned long irq_flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032 struct gpiomux_setting *new_set;
33 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
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057 new_set = rec->ref ? rec->sets[GPIOMUX_ACTIVE] :
58 rec->sets[GPIOMUX_SUSPENDED];
59 if (new_set)
60 __msm_gpiomux_write(gpio, *new_set);
Gregory Bean1963a2a2010-08-28 10:05:44 -070061
62 spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063 return status;
Gregory Bean1963a2a2010-08-28 10:05:44 -070064}
65EXPORT_SYMBOL(msm_gpiomux_write);
66
67int msm_gpiomux_get(unsigned gpio)
68{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069 struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
Gregory Bean1963a2a2010-08-28 10:05:44 -070070 unsigned long irq_flags;
71
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072 if (!msm_gpiomux_recs)
73 return -EFAULT;
74
75 if (gpio >= msm_gpiomux_ngpio)
Gregory Bean1963a2a2010-08-28 10:05:44 -070076 return -EINVAL;
77
78 spin_lock_irqsave(&gpiomux_lock, irq_flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079 if (rec->ref++ == 0 && rec->sets[GPIOMUX_ACTIVE])
80 __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_ACTIVE]);
Gregory Bean1963a2a2010-08-28 10:05:44 -070081 spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
82 return 0;
83}
84EXPORT_SYMBOL(msm_gpiomux_get);
85
86int msm_gpiomux_put(unsigned gpio)
87{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070088 struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
Gregory Bean1963a2a2010-08-28 10:05:44 -070089 unsigned long irq_flags;
90
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091 if (!msm_gpiomux_recs)
92 return -EFAULT;
93
94 if (gpio >= msm_gpiomux_ngpio)
Gregory Bean1963a2a2010-08-28 10:05:44 -070095 return -EINVAL;
96
97 spin_lock_irqsave(&gpiomux_lock, irq_flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098 BUG_ON(rec->ref == 0);
99 if (--rec->ref == 0 && rec->sets[GPIOMUX_SUSPENDED])
100 __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_SUSPENDED]);
Gregory Bean1963a2a2010-08-28 10:05:44 -0700101 spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
102 return 0;
103}
104EXPORT_SYMBOL(msm_gpiomux_put);
105
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106int msm_gpiomux_init(size_t ngpio)
Gregory Bean1963a2a2010-08-28 10:05:44 -0700107{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700108 if (!ngpio)
109 return -EINVAL;
Gregory Bean1963a2a2010-08-28 10:05:44 -0700110
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 if (msm_gpiomux_recs)
112 return -EPERM;
113
114 msm_gpiomux_recs = kzalloc(sizeof(struct msm_gpiomux_rec) * ngpio,
115 GFP_KERNEL);
116 if (!msm_gpiomux_recs)
117 return -ENOMEM;
118
119 /* There is no need to zero this memory, as clients will be blindly
120 * installing settings on top of it.
121 */
122 msm_gpiomux_sets = kmalloc(sizeof(struct gpiomux_setting) * ngpio *
123 GPIOMUX_NSETTINGS, GFP_KERNEL);
124 if (!msm_gpiomux_sets) {
125 kfree(msm_gpiomux_recs);
126 msm_gpiomux_recs = NULL;
127 return -ENOMEM;
Gregory Bean1963a2a2010-08-28 10:05:44 -0700128 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129
130 msm_gpiomux_ngpio = ngpio;
131
Gregory Bean1963a2a2010-08-28 10:05:44 -0700132 return 0;
133}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134EXPORT_SYMBOL(msm_gpiomux_init);
135
136void msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs)
137{
138 unsigned c, s;
139 int rc;
140
141 for (c = 0; c < nconfigs; ++c) {
142 for (s = 0; s < GPIOMUX_NSETTINGS; ++s) {
143 rc = msm_gpiomux_write(configs[c].gpio, s,
144 configs[c].settings[s], NULL);
145 if (rc)
146 pr_err("%s: write failure: %d\n", __func__, rc);
147 }
148 }
149}
150EXPORT_SYMBOL(msm_gpiomux_install);