123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- /*
- *
- * arch/arm/mach-u300/padmux.c
- *
- *
- * Copyright (C) 2009 ST-Ericsson AB
- * License terms: GNU General Public License (GPL) version 2
- * U300 PADMUX functions
- * Author: Martin Persson <martin.persson@stericsson.com>
- */
- #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"
- 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);
|