From 4c2d1c72233218b015d11e5e6f3ab7bae75741f2 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 9 May 2005 14:26:03 -0700 Subject: [PATCH] Add various OMAP SSI drivers Adds various OMAP SSI drivers. Signed-off-by: Tony Lindgren --- arch/arm/Kconfig | 2 + drivers/Kconfig | 2 + drivers/ssi/Kconfig | 17 +++ drivers/ssi/Makefile | 6 + drivers/ssi/omap-tsc2101.c | 247 +++++++++++++++++++++++++++++++++++++ drivers/ssi/omap-tsc2101.h | 32 +++++ drivers/ssi/omap-uwire.c | 226 +++++++++++++++++++++++++++++++++ drivers/ssi/omap-uwire.h | 26 ++++ 8 files changed, 558 insertions(+) create mode 100644 drivers/ssi/Kconfig create mode 100644 drivers/ssi/Makefile create mode 100644 drivers/ssi/omap-tsc2101.c create mode 100644 drivers/ssi/omap-tsc2101.h create mode 100644 drivers/ssi/omap-uwire.c create mode 100644 drivers/ssi/omap-uwire.h diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 66aa67f67c5..708207cdb85 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -731,6 +731,8 @@ source "drivers/usb/Kconfig" source "drivers/mmc/Kconfig" +source "drivers/ssi/Kconfig" + endmenu source "fs/Kconfig" diff --git a/drivers/Kconfig b/drivers/Kconfig index ed41d9036bf..9041f180edc 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -58,4 +58,6 @@ source "drivers/mmc/Kconfig" source "drivers/infiniband/Kconfig" +source "drivers/ssi/Kconfig" + endmenu diff --git a/drivers/ssi/Kconfig b/drivers/ssi/Kconfig new file mode 100644 index 00000000000..7c8004081b3 --- /dev/null +++ b/drivers/ssi/Kconfig @@ -0,0 +1,17 @@ +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 diff --git a/drivers/ssi/Makefile b/drivers/ssi/Makefile new file mode 100644 index 00000000000..c63cf2b2e44 --- /dev/null +++ b/drivers/ssi/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the SSI drivers +# + +obj-$(CONFIG_OMAP_UWIRE) += omap-uwire.o +obj-$(CONFIG_OMAP_TSC2101) += omap-tsc2101.o diff --git a/drivers/ssi/omap-tsc2101.c b/drivers/ssi/omap-tsc2101.c new file mode 100644 index 00000000000..00d2ce5b872 --- /dev/null +++ b/drivers/ssi/omap-tsc2101.c @@ -0,0 +1,247 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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"); diff --git a/drivers/ssi/omap-tsc2101.h b/drivers/ssi/omap-tsc2101.h new file mode 100644 index 00000000000..f6b40b8f446 --- /dev/null +++ b/drivers/ssi/omap-tsc2101.h @@ -0,0 +1,32 @@ +/* + * 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 diff --git a/drivers/ssi/omap-uwire.c b/drivers/ssi/omap-uwire.c new file mode 100644 index 00000000000..b9e182a2b5e --- /dev/null +++ b/drivers/ssi/omap-uwire.c @@ -0,0 +1,226 @@ +/* + * 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ä + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include /* 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"); diff --git a/drivers/ssi/omap-uwire.h b/drivers/ssi/omap-uwire.h new file mode 100644 index 00000000000..a0d24bc4add --- /dev/null +++ b/drivers/ssi/omap-uwire.h @@ -0,0 +1,26 @@ +#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 -- 2.41.1