Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit df1e0520 authored by Linus Walleij's avatar Linus Walleij Committed by Russell King
Browse files

ARM: 5666/1: Revamped U300 padmux API



This abstracts the hackish padmux API on the U300 platform into
something more manageable. It provides a way for drivers to
activate/deactivate a certain padmux setting. It will also switch
the users of the old API over to using the new style, pushing
muxing into the apropriate setup files.

Signed-off-by: default avatarLinus Walleij <linus.walleij@stericsson.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 5ad73d07
Loading
Loading
Loading
Loading
+0 −13
Original line number Diff line number Diff line
@@ -25,11 +25,6 @@
#include <linux/platform_device.h>
#include <linux/gpio.h>

/* Need access to SYSCON registers for PADmuxing */
#include <mach/syscon.h>

#include "padmux.h"

/* Reference to GPIO block clock */
static struct clk *clk;

@@ -606,14 +601,6 @@ static int __init gpio_probe(struct platform_device *pdev)
	writel(U300_GPIO_CR_BLOCK_CLKRQ_ENABLE, virtbase + U300_GPIO_CR);
#endif

	/* Set up some padmuxing here */
#ifdef CONFIG_MMC
	pmx_set_mission_mode_mmc();
#endif
#ifdef CONFIG_SPI_PL022
	pmx_set_mission_mode_spi();
#endif

	gpio_set_initial_values();

	for (num_irqs = 0 ; num_irqs < U300_GPIO_NUM_PORTS; num_irqs++) {
+16 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@

#include <asm/mach/mmc.h>
#include "mmc.h"
#include "padmux.h"

struct mmci_card_event {
	struct input_dev *mmc_input;
@@ -146,6 +147,7 @@ int __devinit mmc_init(struct amba_device *adev)
{
	struct mmci_card_event *mmci_card;
	struct device *mmcsd_device = &adev->dev;
	struct pmx *pmx;
	int ret = 0;

	mmci_card = kzalloc(sizeof(struct mmci_card_event), GFP_KERNEL);
@@ -205,6 +207,20 @@ int __devinit mmc_init(struct amba_device *adev)

	input_set_drvdata(mmci_card->mmc_input, mmci_card);

	/*
	 * Setup padmuxing for MMC. Since this must always be
	 * compiled into the kernel, pmx is never released.
	 */
	pmx = pmx_get(mmcsd_device, U300_APP_PMX_MMC_SETTING);

	if (IS_ERR(pmx))
		pr_warning("Could not get padmux handle\n");
	else {
		ret = pmx_activate(mmcsd_device, pmx);
		if (IS_ERR_VALUE(ret))
			pr_warning("Could not activate padmuxing\n");
	}

	ret = gpio_register_callback(U300_GPIO_PIN_MMC_CD, mmci_callback,
				     mmci_card);

+352 −43
Original line number Diff line number Diff line
@@ -6,53 +6,362 @@
 * 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)
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 val;
	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;

	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);
		mask = pmx->onmask[i].mask;
		if (mask != 0) {
			regval = readw(pmx_registers[i]);
			regval &= ~mask;
			regval |= val;
			writew(regval, pmx_registers[i]);
		}
	}
}

void pmx_set_mission_mode_spi(void)
struct pmx *pmx_get(struct device *dev, enum pmx_settings setting)
{
	u16 val;
	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);

	/* 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)
 * 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.
 */
	val &= ~U300_SYSCON_PMC1HR_APP_SPI_CS_1_MASK;
	val |= U300_SYSCON_PMC1HR_APP_SPI_CS_1_SPI;
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;
}

/*
	 * Use GPIO pin SPI CS2 for CS2 actually (it can be used for other
	 * things also)
 * This needs to come in after the core_initcall(),
 * because debugfs is not available until
 * the subsystems come up.
 */
	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);
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);
+24 −4
Original line number Diff line number Diff line
@@ -6,14 +6,34 @@
 * Copyright (C) 2009 ST-Ericsson AB
 * License terms: GNU General Public License (GPL) version 2
 * U300 PADMUX API
 * Author: Linus Walleij <linus.walleij@stericsson.com>
 *
 * Author: Martin Persson <martin.persson@stericsson.com>
 */

#ifndef __MACH_U300_PADMUX_H
#define __MACH_U300_PADMUX_H

void pmx_set_mission_mode_mmc(void);
void pmx_set_mission_mode_spi(void);
enum pmx_settings {
	U300_APP_PMX_MMC_SETTING,
	U300_APP_PMX_SPI_SETTING
};

struct pmx_onmask {
	u16 mask;		/* Mask bits */
	u16 val;		/* Value when active */
};

struct pmx {
	struct device *dev;
	enum pmx_settings setting;
	char *name;
	bool activated;
	bool default_on;
	struct pmx_onmask onmask[];
};

struct pmx *pmx_get(struct device *dev, enum pmx_settings setting);
int pmx_put(struct device *dev, struct pmx *pmx);
int pmx_activate(struct device *dev, struct pmx *pmx);
int pmx_deactivate(struct device *dev, struct pmx *pmx);

#endif