/* * linux/arch/arm/mach-omap2/mux.c * * OMAP2, OMAP3 and OMAP4 pin multiplexing configurations * * Copyright (C) 2004 - 2010 Texas Instruments Inc. * Copyright (C) 2003 - 2008 Nokia Corporation * * Written by Tony Lindgren * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include "omap_hwmod.h" #include "soc.h" #include "control.h" #include "mux.h" #include "prm.h" #include "common.h" #define OMAP_MUX_BASE_OFFSET 0x30 /* Offset from CTRL_BASE */ #define OMAP_MUX_BASE_SZ 0x5ca struct omap_mux_entry { struct omap_mux mux; struct list_head node; }; static LIST_HEAD(mux_partitions); static DEFINE_MUTEX(muxmode_mutex); struct omap_mux_partition *omap_mux_get(const char *name) { struct omap_mux_partition *partition; list_for_each_entry(partition, &mux_partitions, node) { if (!strcmp(name, partition->name)) return partition; } return NULL; } u16 omap_mux_read(struct omap_mux_partition *partition, u16 reg) { if (partition->flags & OMAP_MUX_REG_8BIT) return __raw_readb(partition->base + reg); else return __raw_readw(partition->base + reg); } void omap_mux_write(struct omap_mux_partition *partition, u16 val, u16 reg) { if (partition->flags & OMAP_MUX_REG_8BIT) __raw_writeb(val, partition->base + reg); else __raw_writew(val, partition->base + reg); } void omap_mux_write_array(struct omap_mux_partition *partition, struct omap_board_mux *board_mux) { if (!board_mux) return; while (board_mux->reg_offset != OMAP_MUX_TERMINATOR) { omap_mux_write(partition, board_mux->value, board_mux->reg_offset); board_mux++; } } #ifdef CONFIG_OMAP_MUX static char *omap_mux_options; static int __init _omap_mux_init_gpio(struct omap_mux_partition *partition, int gpio, int val) { struct omap_mux_entry *e; struct omap_mux *gpio_mux = NULL; u16 old_mode; u16 mux_mode; int found = 0; struct list_head *muxmodes = &partition->muxmodes; if (!gpio) return -EINVAL; list_for_each_entry(e, muxmodes, node) { struct omap_mux *m = &e->mux; if (gpio == m->gpio) { gpio_mux = m; found++; } } if (found == 0) { pr_err("%s: Could not set gpio%i\n", __func__, gpio); return -ENODEV; } if (found > 1) { pr_info("%s: Multiple gpio paths (%d) for gpio%i\n", __func__, found, gpio); return -EINVAL; } old_mode = omap_mux_read(partition, gpio_mux->reg_offset); mux_mode = val & ~(OMAP_MUX_NR_MODES - 1); mux_mode |= partition->gpio; pr_debug("%s: Setting signal %s.gpio%i 0x%04x -> 0x%04x\n", __func__, gpio_mux->muxnames[0], gpio, old_mode, mux_mode); omap_mux_write(partition, mux_mode, gpio_mux->reg_offset); return 0; } int __init omap_mux_init_gpio(int gpio, int val) { struct omap_mux_partition *partition; int ret; list_for_each_entry(partition, &mux_partitions, node) { ret = _omap_mux_init_gpio(partition, gpio, val); if (!ret) return ret; } return -ENODEV; } static int __init _omap_mux_get_by_name(struct omap_mux_partition *partition, const char *muxname, struct omap_mux **found_mux) { struct omap_mux *mux = NULL; struct omap_mux_entry *e; const char *mode_name; int found = 0, found_mode = 0, mode0_len = 0; struct list_head *muxmodes = &partition->muxmodes; mode_name = strchr(muxname, '.'); if (mode_name) { mode0_len = strlen(muxname) - strlen(mode_name); mode_name++; } else { mode_name = muxname; } list_for_each_entry(e, muxmodes, node) { char *m0_entry; int i; mux = &e->mux; m0_entry = mux->muxnames[0]; /* First check for full name in mode0.muxmode format */ if (mode0_len && strncmp(muxname, m0_entry, mode0_len)) continue; /* Then check for muxmode only */ for (i = 0; i < OMAP_MUX_NR_MODES; i++) { char *mode_cur = mux->muxnames[i]; if (!mode_cur) continue; if (!strcmp(mode_name, mode_cur)) { *found_mux = mux; found++; found_mode = i; } } } if (found == 1) { return found_mode; } if (found > 1) { pr_err("%s: Multiple signal paths (%i) for %s\n", __func__, found, muxname); return -EINVAL; } pr_err("%s: Could not find signal %s\n", __func__, muxname); return -ENODEV; } int __init omap_mux_get_by_name(const char *muxname, struct omap_mux_partition **found_partition, struct omap_mux **found_mux) { struct omap_mux_partition *partition; list_for_each_entry(partition, &mux_partitions, node) { struct omap_mux *mux = NULL; int mux_mode = _omap_mux_get_by_name(partition, muxname, &mux); if (mux_mode < 0) continue; *found_partition = partition; *found_mux = mux; return mux_mode; } return -ENODEV; } int __init omap_mux_init_signal(const char *muxname, int val) { struct omap_mux_partition *partition = NULL; struct omap_mux *mux = NULL; u16 old_mode; int mux_mode; mux_mode = omap_mux_get_by_name(muxname, &partition, &mux); if (mux_mode < 0 || !mux) return mux_mode; old_mode = omap_mux_read(partition, mux->reg_offset); mux_mode |= val; pr_debug("%s: Setting signal %s 0x%04x -> 0x%04x\n", __func__, muxname, old_mode, mux_mode); omap_mux_write(partition, mux_mode, mux->reg_offset); return 0; } struct omap_hwmod_mux_info * __init omap_hwmod_mux_init(struct omap_device_pad *bpads, int nr_pads) { struct omap_hwmod_mux_info *hmux; int i, nr_pads_dynamic = 0; if (!bpads || nr_pads < 1) return NULL; hmux = kzalloc(sizeof(struct omap_hwmod_mux_info), GFP_KERNEL); if (!hmux) goto err1; hmux->nr_pads = nr_pads; hmux->pads = kzalloc(sizeof(struct omap_device_pad) * nr_pads, GFP_KERNEL); if (!hmux->pads) goto err2; for (i = 0; i < hmux->nr_pads; i++) { struct omap_mux_partition *partition; struct omap_device_pad *bpad = &bpads[i], *pad = &hmux->pads[i]; struct omap_mux *mux; int mux_mode; mux_mode = omap_mux_get_by_name(bpad->name, &partition, &mux); if (mux_mode < 0) goto err3; if (!pad->partition) pad->partition = partition; if (!pad->mux) pad->mux = mux; pad->name = kzalloc(strlen(bpad->name) + 1, GFP_KERNEL); if (!pad->name) { int j; for (j = i - 1; j >= 0; j--) kfree(hmux->pads[j].name); goto err3; } strcpy(pad->name, bpad->name); pad->flags = bpad->flags; pad->enable = bpad->enable; pad->idle = bpad->idle; pad->off = bpad->off; if (pad->flags & (OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP)) nr_pads_dynamic++; pr_debug("%s: Initialized %s\n", __func__, pad->name); } if (!nr_pads_dynamic) return hmux; /* * Add pads that need dynamic muxing into a separate list */ hmux->nr_pads_dynamic = nr_pads_dynamic; hmux->pads_dynamic = kzalloc(sizeof(struct omap_device_pad *) * nr_pads_dynamic, GFP_KERNEL); if (!hmux->pads_dynamic) { pr_err("%s: Could not allocate dynamic pads\n", __func__); return hmux; } nr_pads_dynamic = 0; for (i = 0; i < hmux->nr_pads; i++) { struct omap_device_pad *pad = &hmux->pads[i]; if (pad->flags & (OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP)) { pr_debug("%s: pad %s tagged dynamic\n", __func__, pad->name); hmux->pads_dynamic[nr_pads_dynamic] = pad; nr_pads_dynamic++; } } return hmux; err3: kfree(hmux->pads); err2: kfree(hmux); err1: pr_err("%s: Could not allocate device mux entry\n", __func__); return NULL; } /** * omap_hwmod_mux_scan_wakeups - omap hwmod scan wakeup pads * @hmux: Pads for a hwmod * @mpu_irqs: MPU irq array for a hwmod * * Scans the wakeup status of pads for a single hwmod. If an irq * array is defined for this mux, the parser will call the registered * ISRs for corresponding pads, otherwise the parser will stop at the * first wakeup active pad and return. Returns true if there is a * pending and non-served wakeup event for the mux, otherwise false. */ static bool omap_hwmod_mux_scan_wakeups(struct omap_hwmod_mux_info *hmux, struct omap_hwmod_irq_info *mpu_irqs) { int i, irq; unsigned int val; u32 handled_irqs = 0; for (i = 0; i < hmux->nr_pads_dynamic; i++) { struct omap_device_pad *pad = hmux->pads_dynamic[i]; if (!(pad->flags & OMAP_DEVICE_PAD_WAKEUP) || !(pad->idle & OMAP_WAKEUP_EN)) continue; val = omap_mux_read(pad->partition, pad->mux->reg_offset); if (!(val & OMAP_WAKEUP_EVENT)) continue; if (!hmux->irqs) return true; irq = hmux->irqs[i]; /* make sure we only handle each irq once */ if (handled_irqs & 1 << irq) continue; handled_irqs |= 1 << irq; generic_handle_irq(mpu_irqs[irq].irq); } return false; } /** * _omap_hwmod_mux_handle_irq - Process wakeup events for a single hwmod * * Checks a single hwmod for every wakeup capable pad to see if there is an * active wakeup event. If this is the case, call the corresponding ISR. */ static int _omap_hwmod_mux_handle_irq(struct omap_hwmod *oh, void *data) { if (!oh->mux || !oh->mux->enabled) return 0; if (omap_hwmod_mux_scan_wakeups(oh->mux, oh->mpu_irqs)) generic_handle_irq(oh->mpu_irqs[0].irq); return 0; } /** * omap_hwmod_mux_handle_irq - Process pad wakeup irqs. * * Calls a function for each registered omap_hwmod to check * pad wakeup statuses. */ static irqreturn_t omap_hwmod_mux_handle_irq(int irq, void *unused) { omap_hwmod_for_each(_omap_hwmod_mux_handle_irq, NULL); return IRQ_HANDLED; } /* Assumes the calling function takes care of locking */ void omap_hwmod_mux(struct omap_hwmod_mux_info *hmux, u8 state) { int i; /* Runtime idling of dynamic pads */ if (state == _HWMOD_STATE_IDLE && hmux->enabled) { for (i = 0; i < hmux->nr_pads_dynamic; i++) { struct omap_device_pad *pad = hmux->pads_dynamic[i]; int val = -EINVAL; val = pad->idle; omap_mux_write(pad->partition, val, pad->mux->reg_offset); } return; } /* Runtime enabling of dynamic pads */ if ((state == _HWMOD_STATE_ENABLED) && hmux->pads_dynamic && hmux->enabled) { for (i = 0; i < hmux->nr_pads_dynamic; i++) { struct omap_device_pad *pad = hmux->pads_dynamic[i]; int val = -EINVAL; val = pad->enable; omap_mux_write(pad->partition, val, pad->mux->reg_offset); } return; } /* Enabling or disabling of all pads */ for (i = 0; i < hmux->nr_pads; i++) { struct omap_device_pad *pad = &hmux->pads[i]; int flags, val = -EINVAL; flags = pad->flags; switch (state) { case _HWMOD_STATE_ENABLED: val = pad->enable; pr_debug("%s: Enabling %s %x\n", __func__, pad->name, val); break; case _HWMOD_STATE_DISABLED: /* Use safe mode unless OMAP_DEVICE_PAD_REMUX */ if (flags & OMAP_DEVICE_PAD_REMUX)