165 lines
3.8 KiB
C
165 lines
3.8 KiB
C
/*
|
|
* arch/arm/plat-spear/include/plat/padmux.c
|
|
*
|
|
* SPEAr platform specific gpio pads muxing source file
|
|
*
|
|
* Copyright (C) 2009 ST Microelectronics
|
|
* Viresh Kumar<viresh.kumar@st.com>
|
|
*
|
|
* This file is licensed under the terms of the GNU General Public
|
|
* License version 2. This program is licensed "as is" without any
|
|
* warranty of any kind, whether express or implied.
|
|
*/
|
|
|
|
#include <linux/err.h>
|
|
#include <linux/io.h>
|
|
#include <linux/slab.h>
|
|
#include <plat/padmux.h>
|
|
|
|
/*
|
|
* struct pmx: pmx definition structure
|
|
*
|
|
* base: base address of configuration registers
|
|
* mode_reg: mode configurations
|
|
* mux_reg: muxing configurations
|
|
* active_mode: pointer to current active mode
|
|
*/
|
|
struct pmx {
|
|
u32 base;
|
|
struct pmx_reg mode_reg;
|
|
struct pmx_reg mux_reg;
|
|
struct pmx_mode *active_mode;
|
|
};
|
|
|
|
static struct pmx *pmx;
|
|
|
|
/**
|
|
* pmx_mode_set - Enables an multiplexing mode
|
|
* @mode - pointer to pmx mode
|
|
*
|
|
* It will set mode of operation in hardware.
|
|
* Returns -ve on Err otherwise 0
|
|
*/
|
|
static int pmx_mode_set(struct pmx_mode *mode)
|
|
{
|
|
u32 val;
|
|
|
|
if (!mode->name)
|
|
return -EFAULT;
|
|
|
|
pmx->active_mode = mode;
|
|
|
|
val = readl(pmx->base + pmx->mode_reg.offset);
|
|
val &= ~pmx->mode_reg.mask;
|
|
val |= mode->mask & pmx->mode_reg.mask;
|
|
writel(val, pmx->base + pmx->mode_reg.offset);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* pmx_devs_enable - Enables list of devices
|
|
* @devs - pointer to pmx device array
|
|
* @count - number of devices to enable
|
|
*
|
|
* It will enable pads for all required peripherals once and only once.
|
|
* If peripheral is not supported by current mode then request is rejected.
|
|
* Conflicts between peripherals are not handled and peripherals will be
|
|
* enabled in the order they are present in pmx_dev array.
|
|
* In case of conflicts last peripheral enabled will be present.
|
|
* Returns -ve on Err otherwise 0
|
|
*/
|
|
static int pmx_devs_enable(struct pmx_dev **devs, u8 count)
|
|
{
|
|
u32 val, i, mask;
|
|
|
|
if (!count)
|
|
return -EINVAL;
|
|
|
|
val = readl(pmx->base + pmx->mux_reg.offset);
|
|
for (i = 0; i < count; i++) {
|
|
u8 j = 0;
|
|
|
|
if (!devs[i]->name || !devs[i]->modes) {
|
|
printk(KERN_ERR "padmux: dev name or modes is null\n");
|
|
continue;
|
|
}
|
|
/* check if peripheral exists in active mode */
|
|
if (pmx->active_mode) {
|
|
bool found = false;
|
|
for (j = 0; j < devs[i]->mode_count; j++) {
|
|
if (devs[i]->modes[j].ids &
|
|
pmx->active_mode->id) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found == false) {
|
|
printk(KERN_ERR "%s device not available in %s"\
|
|
"mode\n", devs[i]->name,
|
|
pmx->active_mode->name);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* enable peripheral */
|
|
mask = devs[i]->modes[j].mask & pmx->mux_reg.mask;
|
|
if (devs[i]->enb_on_reset)
|
|
val &= ~mask;
|
|
else
|
|
val |= mask;
|
|
|
|
devs[i]->is_active = true;
|
|
}
|
|
writel(val, pmx->base + pmx->mux_reg.offset);
|
|
kfree(pmx);
|
|
|
|
/* this will ensure that multiplexing can't be changed now */
|
|
pmx = (struct pmx *)-1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* pmx_register - registers a platform requesting pad mux feature
|
|
* @driver - pointer to driver structure containing driver specific parameters
|
|
*
|
|
* Also this must be called only once. This will allocate memory for pmx
|
|
* structure, will call pmx_mode_set, will call pmx_devs_enable.
|
|
* Returns -ve on Err otherwise 0
|
|
*/
|
|
int pmx_register(struct pmx_driver *driver)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (pmx)
|
|
return -EPERM;
|
|
if (!driver->base || !driver->devs)
|
|
return -EFAULT;
|
|
|
|
pmx = kzalloc(sizeof(*pmx), GFP_KERNEL);
|
|
if (!pmx)
|
|
return -ENOMEM;
|
|
|
|
pmx->base = (u32)driver->base;
|
|
pmx->mode_reg.offset = driver->mode_reg.offset;
|
|
pmx->mode_reg.mask = driver->mode_reg.mask;
|
|
pmx->mux_reg.offset = driver->mux_reg.offset;
|
|
pmx->mux_reg.mask = driver->mux_reg.mask;
|
|
|
|
/* choose mode to enable */
|
|
if (driver->mode) {
|
|
ret = pmx_mode_set(driver->mode);
|
|
if (ret)
|
|
goto pmx_fail;
|
|
}
|
|
ret = pmx_devs_enable(driver->devs, driver->devs_count);
|
|
if (ret)
|
|
goto pmx_fail;
|
|
|
|
return 0;
|
|
|
|
pmx_fail:
|
|
return ret;
|
|
}
|