Adds various OMAP SSI drivers.
Signed-off-by: Tony Lindgren <tony@atomide.com>
source "drivers/mmc/Kconfig"
+source "drivers/ssi/Kconfig"
+
endmenu
source "fs/Kconfig"
source "drivers/infiniband/Kconfig"
+source "drivers/ssi/Kconfig"
+
endmenu
--- /dev/null
+menu "Synchronous Serial Interfaces (SSI)"
+
+config OMAP_UWIRE
+ depends on ARCH_OMAP1
+ tristate "MicroWire support on OMAP"
+ ---help---
+ Say Y here if you want support for the MicroWire interface
+ on an OMAP processor.
+
+config OMAP_TSC2101
+ depends on ARCH_OMAP1 || ARCH_OMAP24XX
+ tristate "TSC2101 codec support for Touchscreen and audio"
+ select OMAP_UWIRE if MACH_OMAP_H3 || MACH_OMAP_H2
+ ---help---
+ Say Y here if you want support for the TSC2101 codec. It is
+ needed for touchscreen and audio on OMAP1610 and 1710.
+endmenu
--- /dev/null
+#
+# Makefile for the SSI drivers
+#
+
+obj-$(CONFIG_OMAP_UWIRE) += omap-uwire.o
+obj-$(CONFIG_OMAP_TSC2101) += omap-tsc2101.o
--- /dev/null
+/*
+ * linux/drivers/ssi/omap-tsc2101.c
+ *
+ * TSC2101 codec interface driver for the OMAP platform
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History:
+ *
+ * 2004/11/07 Nishanth Menon - Modified for common hooks for Audio and Touchscreen
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/hardware.h>
+#include <asm/hardware/clock.h>
+
+#include <asm/arch/mux.h>
+#include <asm/arch/io.h>
+#include <asm/arch/hardware.h>
+#include <asm/hardware/tsc2101.h>
+#include <asm/arch/gpioexpander.h>
+
+#include "omap-tsc2101.h"
+
+#if CONFIG_ARCH_OMAP16XX
+#include <../drivers/ssi/omap-uwire.h>
+#else
+#error "Unsupported configuration"
+#endif
+
+#define SPIO 1
+
+static int count;
+static spinlock_t tsc2101_lock = SPIN_LOCK_UNLOCKED;
+static struct clk * tsc2101_mclk_ck;
+
+static int omap_tsc2101_configure(void);
+
+/* FIXME: add driver model usage to powerdown the tsc2101 on suspend */
+/* Clock -Hard coding for the time being */
+#define CLK_SOFT_REQ_REG_BASE (0xFFFE0800+0x34)
+#define SOFT_COM_MCK0_REQ_MASK (0x1<<6)
+
+int omap_tsc2101_enable(void)
+{
+ int ret = 0;
+
+ spin_lock(&tsc2101_lock);
+ if (count++ == 0) {
+ int ret = 0;
+ /* set the Mux to provide MCLK to TSC2101 */
+ if (machine_is_omap_h3()) {
+ ret = omap_cfg_reg(V5_1710_MCLK_ON);
+ } else {
+ if (machine_is_omap_h2()) {
+ ret = omap_cfg_reg(R10_1610_MCLK_ON);
+ }
+ }
+
+ /* Get the MCLK */
+ tsc2101_mclk_ck = clk_get(NULL, "mclk");
+ if (NULL == tsc2101_mclk_ck) {
+ printk(KERN_ERR "Unable to get the clock MCLK!!!\n");;
+ ret = -EPERM;
+ goto done;
+ }
+ if (clk_set_rate(tsc2101_mclk_ck, 12000000)) {
+ printk(KERN_ERR "Unable to set rate to the MCLK!!!\n");;
+ ret = -EPERM;
+ goto done;
+ }
+ clk_enable(tsc2101_mclk_ck);
+
+ ret = omap_tsc2101_configure();
+
+ /* Lock the module */
+ if (!ret && !try_module_get(THIS_MODULE)) {
+ printk(KERN_CRIT "Failed to get TSC module\n");
+ ret = -ESTALE;
+ }
+ }
+
+done:
+ spin_unlock(&tsc2101_lock);
+ return ret;
+}
+
+void omap_tsc2101_disable(void)
+{
+ spin_lock(&tsc2101_lock);
+ if (--count == 0) {
+ int ret = 0;
+ /* Remove the Mux to Stop MCLK to TSC2101 */
+ if (machine_is_omap_h3()) {
+ ret = omap_cfg_reg(V5_1710_MCLK_OFF);
+ } else {
+ if (machine_is_omap_h2()) {
+ ret = omap_cfg_reg(R10_1610_MCLK_OFF);
+ }
+ }
+
+ /* Release the MCLK */
+ clk_disable(tsc2101_mclk_ck);
+ clk_put(tsc2101_mclk_ck);
+ tsc2101_mclk_ck = NULL;
+
+ module_put(THIS_MODULE);
+ }
+ spin_unlock(&tsc2101_lock);
+}
+
+void omap_tsc2101_write(int page, u8 address, u16 data)
+{
+
+ int ret = 0;
+
+ if (machine_is_omap_h2()) {
+ ret =
+ omap_uwire_data_transfer(1,
+ (((page) << 11) | (address << 5)),
+ 16, 0, NULL, 1);
+ if (ret) {
+ printk(KERN_ERR
+ "uwire-write returned error for address %x\n",
+ address);
+ return;
+ }
+ ret = omap_uwire_data_transfer(1, data, 16, 0, NULL, 0);
+ if (ret) {
+ printk(KERN_ERR
+ "uwire-write returned error for address %x\n",
+ address);
+ return;
+ }
+ }
+ if (machine_is_omap_h3()) {
+
+ ret =
+ omap_uwire_data_transfer(0, ((page << 11) | (address << 5)),
+ 16, 0, NULL, 1);
+ if (ret) {
+ printk(KERN_ERR
+ "uwire-write returned error for address %x\n",
+ address);
+ return;
+ }
+ ret = omap_uwire_data_transfer(0, data, 16, 0, NULL, 0);
+ if (ret) {
+ printk(KERN_ERR
+ "uwire-write returned error for address %x\n",
+ address);
+ return;
+ }
+ }
+
+}
+
+void omap_tsc2101_reads(int page, u8 startaddress, u16 * data, int numregs)
+{
+ int cs = 0, i;
+ if (machine_is_omap_h2()) {
+ cs = 1;
+ }
+ if (machine_is_omap_h3()) {
+ cs = 0;
+ }
+ (void)omap_uwire_data_transfer(cs, (0x8000 | (page << 11)
+ | (startaddress << 5)),
+ 16, 0, NULL, 1);
+ for (i = 0; i < (numregs - 1); i++, data++) {
+ omap_uwire_data_transfer(cs, 0, 0, 16, data, 1);
+ }
+ omap_uwire_data_transfer(cs, 0, 0, 16, data, 0);
+}
+
+u16 omap_tsc2101_read(int page, u8 address)
+{
+ u16 ret;
+ omap_tsc2101_reads(page, address, &ret, 1);
+ return ret;
+}
+
+/* FIXME: adapt clock divisors for uwire to current ARM xor clock rate */
+static int omap_tsc2101_configure(void)
+{
+ unsigned long uwire_flags = 0;
+
+#if CONFIG_MACH_OMAP_H3
+ int err = 0;
+ u8 ioExpanderVal = 0;
+
+ if ((err = read_gpio_expa(&ioExpanderVal, 0x24))) {
+ printk(" Error reading from I/O EXPANDER \n");
+ return err;
+ }
+ ioExpanderVal |= 0x8;
+
+ if ((err = write_gpio_expa(ioExpanderVal, 0x24))) {
+ printk(KERN_ERR ": Error writing to I/O EXPANDER \n");
+ return err;
+ }
+#endif
+
+ if (machine_is_omap_h2()) {
+ uwire_flags = UWIRE_READ_RISING_EDGE | UWIRE_WRITE_RISING_EDGE;
+ omap_cfg_reg(N15_1610_UWIRE_CS1);
+ omap_uwire_configure_mode(1, uwire_flags);
+ }
+ if (machine_is_omap_h3()) {
+ uwire_flags = UWIRE_READ_RISING_EDGE | UWIRE_WRITE_RISING_EDGE;
+ omap_cfg_reg(N14_1610_UWIRE_CS0);
+ omap_uwire_configure_mode(0, uwire_flags);
+ }
+
+ /* Configure MCLK enable */
+ omap_writel(omap_readl(PU_PD_SEL_2) | (1 << 22), PU_PD_SEL_2);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(omap_tsc2101_enable);
+EXPORT_SYMBOL(omap_tsc2101_read);
+EXPORT_SYMBOL(omap_tsc2101_reads);
+EXPORT_SYMBOL(omap_tsc2101_write);
+EXPORT_SYMBOL(omap_tsc2101_disable);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION
+ ("Glue audio driver for the TI OMAP1610/OMAP1710 TSC2101 codec.");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * linux/drivers/ssi/omap-tsc2101.h
+ *
+ * TSC2101 codec interface driver for the OMAP platform
+ *
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * History:
+ *
+ * 2004/11/07 Nishanth Menon - Provided common hooks for Audio and Touchscreen
+ */
+
+#ifndef __OMAP_TSC2101_H
+#define __OMAP_TSC2101_H
+
+extern u16 omap_tsc2101_read(int page, u8 address);
+extern void omap_tsc2101_reads(int page, u8 startaddress, u16 * data,
+ int numregs);
+extern void omap_tsc2101_write(int page, u8 address, u16 data);
+
+extern void omap_tsc2101_disable(void);
+extern int omap_tsc2101_enable(void);
+
+#endif
--- /dev/null
+/*
+ * BRIEF MODULE DESCRIPTION
+ *
+ * uWire interface driver for the OMAP Platform
+ *
+ * Copyright 2003 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ * source@mvista.com
+ *
+ * Ported to 2.6 uwire interface.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * Generalization patches by Juha Yrjölä <juha.yrjola@nokia.com>
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/omap730.h> /* OMAP730_IO_CONF registers */
+
+#include "omap-uwire.h"
+
+/* uWire Registers: */
+#define UWIRE_BASE 0xFFFB3000
+#define UWIRE_IO_SIZE 0x20
+#define UWIRE_TDR 0x00
+#define UWIRE_RDR 0x00
+#define UWIRE_CSR 0x01
+#define UWIRE_SR1 0x02
+#define UWIRE_SR2 0x03
+#define UWIRE_SR3 0x04
+#define UWIRE_SR4 0x05
+#define UWIRE_SR5 0x06
+
+static unsigned short uwire_flags[4];
+static unsigned long uwire_base = io_p2v(UWIRE_BASE);
+static spinlock_t uwire_lock;
+static unsigned int uwire_idx_shift;
+
+static inline void uwire_write_reg(int idx, u16 val)
+{
+ __raw_writew(val, uwire_base + (idx << uwire_idx_shift));
+}
+
+static inline u16 uwire_read_reg(int idx)
+{
+ return __raw_readw(uwire_base + (idx << uwire_idx_shift));
+}
+
+void omap_uwire_configure_mode(int cs, unsigned long flags)
+{
+ u16 w, val = 0;
+ int shift, reg;
+
+ BUG_ON(cs > 3);
+
+ val = flags & 0x3f;
+ if (flags & UWIRE_CLK_INVERTED)
+ val ^= 0x03;
+ if (cs & 1)
+ shift = 6;
+ else
+ shift = 0;
+ if (cs <= 1)
+ reg = UWIRE_SR1;
+ else
+ reg = UWIRE_SR2;
+ spin_lock(&uwire_lock);
+ w = uwire_read_reg(reg);
+ w &= ~(0x3f << shift);
+ w |= val << shift;
+ uwire_write_reg(reg, w);
+ spin_unlock(&uwire_lock);
+
+ uwire_flags[cs] = flags;
+}
+
+static int wait_uwire_csr_flag(u16 mask, u16 val, int might_not_catch)
+{
+ u16 w;
+ int c = 0;
+ unsigned long max_jiffies = jiffies + HZ;
+
+ for (;;) {
+ w = uwire_read_reg(UWIRE_CSR);
+ if ((w & mask) == val)
+ break;
+ if (time_after(jiffies, max_jiffies)) {
+ printk(KERN_ERR "%s: timeout. reg=%#06x mask=%#06x val=%#06x\n",
+ __FUNCTION__, w, mask, val);
+ return -1;
+ }
+ c++;
+ if (might_not_catch && c > 64)
+ break;
+ }
+ return 0;
+}
+
+int omap_uwire_data_transfer(int cs, u16 tx_data, int tx_size, int rx_size,
+ u16 *rx_buf, int leave_cs_active)
+{
+ u16 ret = -1, w;
+ u16 mask;
+
+ BUG_ON(cs > 3);
+ BUG_ON(rx_size && !rx_buf);
+
+ spin_lock(&uwire_lock);
+
+ if (wait_uwire_csr_flag(1 << 14, 0, 0))
+ goto exit;
+
+ if (uwire_flags[cs] & UWIRE_CLK_INVERTED)
+ uwire_write_reg(UWIRE_SR4, 1);
+ else
+ uwire_write_reg(UWIRE_SR4, 0);
+
+ w = cs << 10;
+ w |= 1 << 12; /* CS_CMD : activate CS */
+ uwire_write_reg(UWIRE_CSR, w);
+
+ /* Shift data to 16bit MSb and place it in TX register. */
+ uwire_write_reg(UWIRE_TDR, tx_data << (16 - tx_size));
+
+ if (wait_uwire_csr_flag(1 << 14, 0, 0))
+ goto exit;
+
+ w = rx_size | (tx_size << 5) | (cs << 10);
+ w |= (1 << 12) | (1 << 13);
+ /* Start uWire read/write */
+ uwire_write_reg(UWIRE_CSR, w);
+
+ /* Wait till read/write actually starts.
+ * This is needed at high (>=60MHz) MPU frequencies
+ * REVISIT: But occasionally we won't have time to catch it
+ */
+ if (wait_uwire_csr_flag(1 << 14, 1 << 14, 1))
+ goto exit;
+
+ /* Wait for both transfers to be completed */
+ mask = 1 << 14; /* CSRB : reg busy */
+ w = 0;
+ if (rx_size) {
+ mask |= 1 << 15; /* RDRB : reg busy */
+ w |= 1 << 15;
+ }
+
+ if (wait_uwire_csr_flag(mask, w, 0))
+ goto exit;
+
+ if (rx_size)
+ *rx_buf = uwire_read_reg(UWIRE_RDR);
+
+ if (!leave_cs_active)
+ uwire_write_reg(UWIRE_CSR, cs << 10);
+
+ ret = 0;
+
+exit:
+ spin_unlock(&uwire_lock);
+ return ret;
+}
+
+static int __init omap_uwire_init(void)
+{
+ spin_lock_init(&uwire_lock);
+ if (cpu_is_omap730())
+ uwire_idx_shift = 1;
+ else
+ uwire_idx_shift = 2;
+
+ uwire_write_reg(UWIRE_SR3, 1);
+ if (machine_is_omap_h2() || machine_is_omap_osk()) {
+ /* defaults: W21 SDO, U18 SDI, V19 SCL */
+ omap_cfg_reg(N14_1610_UWIRE_CS0);
+ omap_cfg_reg(N15_1610_UWIRE_CS1);
+ }
+ if (machine_is_omap_perseus2()) {
+ /* configure pins: MPU_UW_nSCS1, MPU_UW_SDO, MPU_UW_SCLK */
+ int val = omap_readl(OMAP730_IO_CONF_9) & ~0x00EEE000;
+ omap_writel(val | 0x00AAA000, OMAP730_IO_CONF_9);
+ }
+ return 0;
+}
+
+static void __exit omap_uwire_exit(void)
+{
+}
+
+subsys_initcall(omap_uwire_init);
+module_exit(omap_uwire_exit);
+
+EXPORT_SYMBOL(omap_uwire_configure_mode);
+EXPORT_SYMBOL(omap_uwire_data_transfer);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+#ifndef __ARCH_OMAP_UWIRE_H
+#define __ARCH_OMAP_UWIRE_H
+
+#define UWIRE_READ_FALLING_EDGE 0x0000
+#define UWIRE_READ_RISING_EDGE 0x0001
+#define UWIRE_WRITE_FALLING_EDGE 0x0000
+#define UWIRE_WRITE_RISING_EDGE 0x0002
+#define UWIRE_CS_ACTIVE_LOW 0x0000
+#define UWIRE_CS_ACTIVE_HIGH 0x0004
+#define UWIRE_FREQ_DIV_2 0x0000
+#define UWIRE_FREQ_DIV_4 0x0008
+#define UWIRE_FREQ_DIV_8 0x0010
+#define UWIRE_CHK_READY 0x0020
+#define UWIRE_CLK_INVERTED 0x0040
+
+/*
+ * uWire for OMAP declarations
+ */
+extern void omap_uwire_configure_mode(int cs, unsigned long flags);
+
+/* NOTE: Make sure you don't call this from an interrupt handler! */
+extern int omap_uwire_data_transfer(int cs, u16 tx_data, int tx_size,
+ int rx_size, u16 *rx_buf,
+ int leave_cs_active);
+
+#endif