| /* |
| * 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; |
| } |