Commit 4c2d1c72 authored by Tony Lindgren's avatar Tony Lindgren

Add various OMAP SSI drivers

Adds various OMAP SSI drivers.
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent 55e6d015
......@@ -731,6 +731,8 @@ source "drivers/usb/Kconfig"
source "drivers/mmc/Kconfig"
source "drivers/ssi/Kconfig"
endmenu
source "fs/Kconfig"
......
......@@ -58,4 +58,6 @@ source "drivers/mmc/Kconfig"
source "drivers/infiniband/Kconfig"
source "drivers/ssi/Kconfig"
endmenu
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
#
# Makefile for the SSI drivers
#
obj-$(CONFIG_OMAP_UWIRE) += omap-uwire.o
obj-$(CONFIG_OMAP_TSC2101) += omap-tsc2101.o
/*
* 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");
/*
* 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
/*
* 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 Yrjl <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");
#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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment