Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/gpiomux.c b/arch/arm/mach-msm/gpiomux.c
index 53af21a..68b97ce 100644
--- a/arch/arm/mach-msm/gpiomux.c
+++ b/arch/arm/mach-msm/gpiomux.c
@@ -8,57 +8,76 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
*/
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/spinlock.h>
#include "gpiomux.h"
+struct msm_gpiomux_rec {
+ struct gpiomux_setting *sets[GPIOMUX_NSETTINGS];
+ int ref;
+};
static DEFINE_SPINLOCK(gpiomux_lock);
+static struct msm_gpiomux_rec *msm_gpiomux_recs;
+static struct gpiomux_setting *msm_gpiomux_sets;
+static unsigned msm_gpiomux_ngpio;
-int msm_gpiomux_write(unsigned gpio,
- gpiomux_config_t active,
- gpiomux_config_t suspended)
+int msm_gpiomux_write(unsigned gpio, enum msm_gpiomux_setting which,
+ struct gpiomux_setting *setting, struct gpiomux_setting *old_setting)
{
- struct msm_gpiomux_config *cfg = msm_gpiomux_configs + gpio;
+ struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
+ unsigned set_slot = gpio * GPIOMUX_NSETTINGS + which;
unsigned long irq_flags;
- gpiomux_config_t setting;
+ struct gpiomux_setting *new_set;
+ int status = 0;
- if (gpio >= GPIOMUX_NGPIOS)
+ if (!msm_gpiomux_recs)
+ return -EFAULT;
+
+ if (gpio >= msm_gpiomux_ngpio)
return -EINVAL;
spin_lock_irqsave(&gpiomux_lock, irq_flags);
- if (active & GPIOMUX_VALID)
- cfg->active = active;
+ if (old_setting) {
+ if (rec->sets[which] == NULL)
+ status = 1;
+ else
+ *old_setting = *(rec->sets[which]);
+ }
- if (suspended & GPIOMUX_VALID)
- cfg->suspended = suspended;
+ if (setting) {
+ msm_gpiomux_sets[set_slot] = *setting;
+ rec->sets[which] = &msm_gpiomux_sets[set_slot];
+ } else {
+ rec->sets[which] = NULL;
+ }
- setting = cfg->ref ? active : suspended;
- if (setting & GPIOMUX_VALID)
- __msm_gpiomux_write(gpio, setting);
+ new_set = rec->ref ? rec->sets[GPIOMUX_ACTIVE] :
+ rec->sets[GPIOMUX_SUSPENDED];
+ if (new_set)
+ __msm_gpiomux_write(gpio, *new_set);
spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
- return 0;
+ return status;
}
EXPORT_SYMBOL(msm_gpiomux_write);
int msm_gpiomux_get(unsigned gpio)
{
- struct msm_gpiomux_config *cfg = msm_gpiomux_configs + gpio;
+ struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
unsigned long irq_flags;
- if (gpio >= GPIOMUX_NGPIOS)
+ if (!msm_gpiomux_recs)
+ return -EFAULT;
+
+ if (gpio >= msm_gpiomux_ngpio)
return -EINVAL;
spin_lock_irqsave(&gpiomux_lock, irq_flags);
- if (cfg->ref++ == 0 && cfg->active & GPIOMUX_VALID)
- __msm_gpiomux_write(gpio, cfg->active);
+ if (rec->ref++ == 0 && rec->sets[GPIOMUX_ACTIVE])
+ __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_ACTIVE]);
spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
return 0;
}
@@ -66,31 +85,66 @@
int msm_gpiomux_put(unsigned gpio)
{
- struct msm_gpiomux_config *cfg = msm_gpiomux_configs + gpio;
+ struct msm_gpiomux_rec *rec = msm_gpiomux_recs + gpio;
unsigned long irq_flags;
- if (gpio >= GPIOMUX_NGPIOS)
+ if (!msm_gpiomux_recs)
+ return -EFAULT;
+
+ if (gpio >= msm_gpiomux_ngpio)
return -EINVAL;
spin_lock_irqsave(&gpiomux_lock, irq_flags);
- BUG_ON(cfg->ref == 0);
- if (--cfg->ref == 0 && cfg->suspended & GPIOMUX_VALID)
- __msm_gpiomux_write(gpio, cfg->suspended);
+ BUG_ON(rec->ref == 0);
+ if (--rec->ref == 0 && rec->sets[GPIOMUX_SUSPENDED])
+ __msm_gpiomux_write(gpio, *rec->sets[GPIOMUX_SUSPENDED]);
spin_unlock_irqrestore(&gpiomux_lock, irq_flags);
return 0;
}
EXPORT_SYMBOL(msm_gpiomux_put);
-static int __init gpiomux_init(void)
+int msm_gpiomux_init(size_t ngpio)
{
- unsigned n;
+ if (!ngpio)
+ return -EINVAL;
- for (n = 0; n < GPIOMUX_NGPIOS; ++n) {
- msm_gpiomux_configs[n].ref = 0;
- if (!(msm_gpiomux_configs[n].suspended & GPIOMUX_VALID))
- continue;
- __msm_gpiomux_write(n, msm_gpiomux_configs[n].suspended);
+ if (msm_gpiomux_recs)
+ return -EPERM;
+
+ msm_gpiomux_recs = kzalloc(sizeof(struct msm_gpiomux_rec) * ngpio,
+ GFP_KERNEL);
+ if (!msm_gpiomux_recs)
+ return -ENOMEM;
+
+ /* There is no need to zero this memory, as clients will be blindly
+ * installing settings on top of it.
+ */
+ msm_gpiomux_sets = kmalloc(sizeof(struct gpiomux_setting) * ngpio *
+ GPIOMUX_NSETTINGS, GFP_KERNEL);
+ if (!msm_gpiomux_sets) {
+ kfree(msm_gpiomux_recs);
+ msm_gpiomux_recs = NULL;
+ return -ENOMEM;
}
+
+ msm_gpiomux_ngpio = ngpio;
+
return 0;
}
-postcore_initcall(gpiomux_init);
+EXPORT_SYMBOL(msm_gpiomux_init);
+
+void msm_gpiomux_install(struct msm_gpiomux_config *configs, unsigned nconfigs)
+{
+ unsigned c, s;
+ int rc;
+
+ for (c = 0; c < nconfigs; ++c) {
+ for (s = 0; s < GPIOMUX_NSETTINGS; ++s) {
+ rc = msm_gpiomux_write(configs[c].gpio, s,
+ configs[c].settings[s], NULL);
+ if (rc)
+ pr_err("%s: write failure: %d\n", __func__, rc);
+ }
+ }
+}
+EXPORT_SYMBOL(msm_gpiomux_install);