Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / arch / arm / mach-u300 / padmux.c
index f366456..4c93c6c 100644 (file)
  * Copyright (C) 2009 ST-Ericsson AB
  * License terms: GNU General Public License (GPL) version 2
  * U300 PADMUX functions
- * Author: Linus Walleij <linus.walleij@stericsson.com>
- *
+ * Author: Martin Persson <martin.persson@stericsson.com>
  */
-#include <linux/io.h>
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
 #include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/bug.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include <mach/u300-regs.h>
 #include <mach/syscon.h>
-
 #include "padmux.h"
 
-/* Set the PAD MUX to route the MMC reader correctly to GPIO0. */
-void pmx_set_mission_mode_mmc(void)
-{
-       u16 val;
-
-       val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMC1LR);
-       val &= ~U300_SYSCON_PMC1LR_MMCSD_MASK;
-       writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMC1LR);
-       val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMC1HR);
-       val &= ~U300_SYSCON_PMC1HR_APP_GPIO_1_MASK;
-       val |= U300_SYSCON_PMC1HR_APP_GPIO_1_MMC;
-       writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMC1HR);
-}
-
-void pmx_set_mission_mode_spi(void)
-{
-       u16 val;
-
-       /* Set up padmuxing so the SPI port and its chipselects are active */
-       val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMC1HR);
-       /*
-        * Activate the SPI port (disable the use of these pins for generic
-        * GPIO, DSP, AAIF
-        */
-       val &= ~U300_SYSCON_PMC1HR_APP_SPI_2_MASK;
-       val |= U300_SYSCON_PMC1HR_APP_SPI_2_SPI;
-       /*
-        * Use GPIO pin SPI CS1 for CS1 actually (it can be used for other
-        * things also)
-        */
-       val &= ~U300_SYSCON_PMC1HR_APP_SPI_CS_1_MASK;
-       val |= U300_SYSCON_PMC1HR_APP_SPI_CS_1_SPI;
-       /*
-        * Use GPIO pin SPI CS2 for CS2 actually (it can be used for other
-        * things also)
-        */
-       val &= ~U300_SYSCON_PMC1HR_APP_SPI_CS_2_MASK;
-       val |= U300_SYSCON_PMC1HR_APP_SPI_CS_2_SPI;
-       writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMC1HR);
+static DEFINE_MUTEX(pmx_mutex);
+
+const u32 pmx_registers[] = {
+       (U300_SYSCON_VBASE + U300_SYSCON_PMC1LR),
+       (U300_SYSCON_VBASE + U300_SYSCON_PMC1HR),
+       (U300_SYSCON_VBASE + U300_SYSCON_PMC2R),
+       (U300_SYSCON_VBASE + U300_SYSCON_PMC3R),
+       (U300_SYSCON_VBASE + U300_SYSCON_PMC4R)
+};
+
+/* High level functionality */
+
+/* Lazy dog:
+ * onmask = {
+ *   {"PMC1LR" mask, "PMC1LR" value},
+ *   {"PMC1HR" mask, "PMC1HR" value},
+ *   {"PMC2R"  mask, "PMC2R"  value},
+ *   {"PMC3R"  mask, "PMC3R"  value},
+ *   {"PMC4R"  mask, "PMC4R"  value}
+ * }
+ */
+static struct pmx mmc_setting = {
+       .setting = U300_APP_PMX_MMC_SETTING,
+       .default_on = false,
+       .activated = false,
+       .name = "MMC",
+       .onmask = {
+                  {U300_SYSCON_PMC1LR_MMCSD_MASK,
+                   U300_SYSCON_PMC1LR_MMCSD_MMCSD},
+                  {0, 0},
+                  {0, 0},
+                  {0, 0},
+                  {U300_SYSCON_PMC4R_APP_MISC_12_MASK,
+                   U300_SYSCON_PMC4R_APP_MISC_12_APP_GPIO}
+                  },
+};
+
+static struct pmx spi_setting = {
+       .setting = U300_APP_PMX_SPI_SETTING,
+       .default_on = false,
+       .activated = false,
+       .name = "SPI",
+       .onmask = {{0, 0},
+                  {U300_SYSCON_PMC1HR_APP_SPI_2_MASK |
+                   U300_SYSCON_PMC1HR_APP_SPI_CS_1_MASK |
+                   U300_SYSCON_PMC1HR_APP_SPI_CS_2_MASK,
+                   U300_SYSCON_PMC1HR_APP_SPI_2_SPI |
+                   U300_SYSCON_PMC1HR_APP_SPI_CS_1_SPI |
+                   U300_SYSCON_PMC1HR_APP_SPI_CS_2_SPI},
+                  {0, 0},
+                  {0, 0},
+                  {0, 0}
+                  },
+};
+
+/* Available padmux settings */
+static struct pmx *pmx_settings[] = {
+       &mmc_setting,
+       &spi_setting,
+};
+
+static void update_registers(struct pmx *pmx, bool activate)
+{
+       u16 regval, val, mask;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pmx_registers); i++) {
+               if (activate)
+                       val = pmx->onmask[i].val;
+               else
+                       val = 0;
+
+               mask = pmx->onmask[i].mask;
+               if (mask != 0) {
+                       regval = readw(pmx_registers[i]);
+                       regval &= ~mask;
+                       regval |= val;
+                       writew(regval, pmx_registers[i]);
+               }
+       }
+}
+
+struct pmx *pmx_get(struct device *dev, enum pmx_settings setting)
+{
+       int i;
+       struct pmx *pmx = ERR_PTR(-ENOENT);
+
+       if (dev == NULL)
+               return ERR_PTR(-EINVAL);
+
+       mutex_lock(&pmx_mutex);
+       for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) {
+
+               if (setting == pmx_settings[i]->setting) {
+
+                       if (pmx_settings[i]->dev != NULL) {
+                               WARN(1, "padmux: required setting "
+                                    "in use by another consumer\n");
+                       } else {
+                               pmx = pmx_settings[i];
+                               pmx->dev = dev;
+                               dev_dbg(dev, "padmux: setting nr %d is now "
+                                       "bound to %s and ready to use\n",
+                                       setting, dev_name(dev));
+                               break;
+                       }
+               }
+       }
+       mutex_unlock(&pmx_mutex);
+
+       return pmx;
+}
+EXPORT_SYMBOL(pmx_get);
+
+int pmx_put(struct device *dev, struct pmx *pmx)
+{
+       int i;
+       int ret = -ENOENT;
+
+       if (pmx == NULL || dev == NULL)
+               return -EINVAL;
+
+       mutex_lock(&pmx_mutex);
+       for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) {
+
+               if (pmx->setting == pmx_settings[i]->setting) {
+
+                       if (dev != pmx->dev) {
+                               WARN(1, "padmux: cannot release handle as "
+                                       "it is bound to another consumer\n");
+                               ret = -EINVAL;
+                               break;
+                       } else {
+                               pmx_settings[i]->dev = NULL;
+                               ret = 0;
+                               break;
+                       }
+               }
+       }
+       mutex_unlock(&pmx_mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL(pmx_put);
+
+int pmx_activate(struct device *dev, struct pmx *pmx)
+{
+       int i, j, ret;
+       ret = 0;
+
+       if (pmx == NULL || dev == NULL)
+               return -EINVAL;
+
+       mutex_lock(&pmx_mutex);
+
+       /* Make sure the required bits are not used */
+       for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) {
+
+               if (pmx_settings[i]->dev == NULL || pmx_settings[i] == pmx)
+                       continue;
+
+               for (j = 0; j < ARRAY_SIZE(pmx_registers); j++) {
+
+                       if (pmx_settings[i]->onmask[j].mask & pmx->
+                               onmask[j].mask) {
+                               /* More than one entry on the same bits */
+                               WARN(1, "padmux: cannot activate "
+                                       "setting. Bit conflict with "
+                                       "an active setting\n");
+
+                               ret = -EUSERS;
+                               goto exit;
+                       }
+               }
+       }
+       update_registers(pmx, true);
+       pmx->activated = true;
+       dev_dbg(dev, "padmux: setting nr %d is activated\n",
+               pmx->setting);
+
+exit:
+       mutex_unlock(&pmx_mutex);
+       return ret;
+}
+EXPORT_SYMBOL(pmx_activate);
+
+int pmx_deactivate(struct device *dev, struct pmx *pmx)
+{
+       int i;
+       int ret = -ENOENT;
+
+       if (pmx == NULL || dev == NULL)
+               return -EINVAL;
+
+       mutex_lock(&pmx_mutex);
+       for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) {
+
+               if (pmx_settings[i]->dev == NULL)
+                       continue;
+
+               if (pmx->setting == pmx_settings[i]->setting) {
+
+                       if (dev != pmx->dev) {
+                               WARN(1, "padmux: cannot deactivate "
+                                    "pmx setting as it was activated "
+                                    "by another consumer\n");
+
+                               ret = -EBUSY;
+                               continue;
+                       } else {
+                               update_registers(pmx, false);
+                               pmx_settings[i]->dev = NULL;
+                               pmx->activated = false;
+                               ret = 0;
+                               dev_dbg(dev, "padmux: setting nr %d is deactivated",
+                                       pmx->setting);
+                               break;
+                       }
+               }
+       }
+       mutex_unlock(&pmx_mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL(pmx_deactivate);
+
+/*
+ * For internal use only. If it is to be exported,
+ * it should be reentrant. Notice that pmx_activate
+ * (i.e. runtime settings) always override default settings.
+ */
+static int pmx_set_default(void)
+{
+       /* Used to identify several entries on the same bits */
+       u16 modbits[ARRAY_SIZE(pmx_registers)];
+
+       int i, j;
+
+       memset(modbits, 0, ARRAY_SIZE(pmx_registers) * sizeof(u16));
+
+       for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) {
+
+               if (!pmx_settings[i]->default_on)
+                       continue;
+
+               for (j = 0; j < ARRAY_SIZE(pmx_registers); j++) {
+
+                       /* Make sure there is only one entry on the same bits */
+                       if (modbits[j] & pmx_settings[i]->onmask[j].mask) {
+                               BUG();
+                               return -EUSERS;
+                       }
+                       modbits[j] |= pmx_settings[i]->onmask[j].mask;
+               }
+               update_registers(pmx_settings[i], true);
+       }
+       return 0;
 }
+
+#if (defined(CONFIG_DEBUG_FS) && defined(CONFIG_U300_DEBUG))
+static int pmx_show(struct seq_file *s, void *data)
+{
+       int i;
+       seq_printf(s, "-------------------------------------------------\n");
+       seq_printf(s, "SETTING     BOUND TO DEVICE               STATE\n");
+       seq_printf(s, "-------------------------------------------------\n");
+       mutex_lock(&pmx_mutex);
+       for (i = 0; i < ARRAY_SIZE(pmx_settings); i++) {
+               /* Format pmx and device name nicely */
+               char cdp[33];
+               int chars;
+
+               chars = snprintf(&cdp[0], 17, "%s", pmx_settings[i]->name);
+               while (chars < 16) {
+                       cdp[chars] = ' ';
+                       chars++;
+               }
+               chars = snprintf(&cdp[16], 17, "%s", pmx_settings[i]->dev ?
+                               dev_name(pmx_settings[i]->dev) : "N/A");
+               while (chars < 16) {
+                       cdp[chars+16] = ' ';
+                       chars++;
+               }
+               cdp[32] = '\0';
+
+               seq_printf(s,
+                       "%s\t%s\n",
+                       &cdp[0],
+                       pmx_settings[i]->activated ?
+                       "ACTIVATED" : "DEACTIVATED"
+                       );
+
+       }
+       mutex_unlock(&pmx_mutex);
+       return 0;
+}
+
+static int pmx_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, pmx_show, NULL);
+}
+
+static const struct file_operations pmx_operations = {
+       .owner          = THIS_MODULE,
+       .open           = pmx_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int __init init_pmx_read_debugfs(void)
+{
+       /* Expose a simple debugfs interface to view pmx settings */
+       (void) debugfs_create_file("padmux", S_IFREG | S_IRUGO,
+                                  NULL, NULL,
+                                  &pmx_operations);
+       return 0;
+}
+
+/*
+ * This needs to come in after the core_initcall(),
+ * because debugfs is not available until
+ * the subsystems come up.
+ */
+module_init(init_pmx_read_debugfs);
+#endif
+
+static int __init pmx_init(void)
+{
+       int ret;
+
+       ret = pmx_set_default();
+
+       if (IS_ERR_VALUE(ret))
+               pr_crit("padmux: default settings could not be set\n");
+
+       return 0;
+}
+
+/* Should be initialized before consumers */
+core_initcall(pmx_init);