Commit 9f9cb1fd authored by Imre Deak's avatar Imre Deak Committed by Tony Lindgren

SPI: Add support for TSC2101

The new driver is functionally the same as the old
drivers/ssi/omap-tsc2101 driver in the linux-omap tree.
Signed-off-by: default avatarImre Deak <imre.deak@solidboot.com>
parent 0228106f
......@@ -183,6 +183,14 @@ config SPI_AT25
This driver can also be built as a module. If so, the module
will be called at25.
config SPI_TSC2101
depends on SPI_MASTER
tristate "TSC2101 chip support"
---help---
Say Y here if you want support for the TSC2101 chip.
At the moment it provides basic register read / write interface
as well as a way to enable the MCLK clock.
config SPI_SPIDEV
tristate "User mode SPI device driver support"
depends on SPI_MASTER && EXPERIMENTAL
......
......@@ -29,6 +29,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
# SPI protocol drivers (device/link on bus)
obj-$(CONFIG_SPI_AT25) += at25.o
obj-$(CONFIG_SPI_SPIDEV) += spidev.o
obj-$(CONFIG_SPI_TSC2101) += tsc2101.o
# ... add above this line ...
# SPI slave controller drivers (upstream link)
......
/*
* linux/drivers/spi/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/kernel.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/spi/tsc2101.h>
struct tsc2101_device {
struct mutex mutex;
int mclk_enabled;
struct clock *mclk_ck;
struct spi_message message;
struct spi_transfer transfer[2];
u16 command;
void (*enable_mclk)(struct spi_device *spi);
void (*disable_mclk)(struct spi_device *spi);
};
int tsc2101_enable_mclk(struct spi_device *spi)
{
struct tsc2101_device *tsc2101;
tsc2101 = spi_get_drvdata(spi);
mutex_lock(&tsc2101->mutex);
if (spi->dev.power.power_state.event != PM_EVENT_ON) {
mutex_unlock(&tsc2101->mutex);
return -ENODEV;
}
if (tsc2101->mclk_enabled++ == 0) {
if (tsc2101->enable_mclk != NULL)
tsc2101->enable_mclk(spi);
}
mutex_unlock(&tsc2101->mutex);
return 0;
}
EXPORT_SYMBOL(tsc2101_enable_mclk);
void tsc2101_disable_mclk(struct spi_device *spi)
{
struct tsc2101_device *tsc2101;
tsc2101 = spi_get_drvdata(spi);
mutex_lock(&tsc2101->mutex);
if (--tsc2101->mclk_enabled == 0) {
if (tsc2101->disable_mclk != NULL &&
spi->dev.power.power_state.event == PM_EVENT_ON)
tsc2101->disable_mclk(spi);
}
mutex_lock(&tsc2101->mutex);
}
EXPORT_SYMBOL(tsc2101_disable_mclk);
int tsc2101_write_sync(struct spi_device *spi, int page, u8 address, u16 data)
{
struct tsc2101_device *tsc2101;
struct spi_message *m;
struct spi_transfer *t;
int ret;
tsc2101 = spi_get_drvdata(spi);
mutex_lock(&tsc2101->mutex);
if (spi->dev.power.power_state.event != PM_EVENT_ON) {
mutex_unlock(&tsc2101->mutex);
return -ENODEV;
}
m = &tsc2101->message;
spi_message_init(m);
t = &tsc2101->transfer[0];
memset(t, 0, sizeof(tsc2101->transfer));
/* Address */
tsc2101->command = (page << 11) | (address << 5);
t->tx_buf = &tsc2101->command;
t->len = 2;
spi_message_add_tail(t, m);
/* Data */
t++;
t->tx_buf = &data;
t->len = 2;
spi_message_add_tail(t, m);
ret = spi_sync(spi, m);
if (!ret)
ret = tsc2101->message.status;
mutex_unlock(&tsc2101->mutex);
return ret;
}
EXPORT_SYMBOL(tsc2101_write_sync);
int tsc2101_reads_sync(struct spi_device *spi,
int page, u8 startaddress, u16 *data, int numregs)
{
struct tsc2101_device *tsc2101;
struct spi_message *m;
struct spi_transfer *t;
int ret;
tsc2101 = spi_get_drvdata(spi);
mutex_lock(&tsc2101->mutex);
if (spi->dev.power.power_state.event != PM_EVENT_ON) {
mutex_unlock(&tsc2101->mutex);
return -ENODEV;
}
m = &tsc2101->message;
spi_message_init(m);
t = &tsc2101->transfer[0];
memset(t, 0, sizeof(tsc2101->transfer));
/* Address */
tsc2101->command = 0x8000 | (page << 11) | (startaddress << 5);
t->tx_buf = &tsc2101->command;
t->len = 2;
spi_message_add_tail(t, m);
/* Data */
t++;
t->rx_buf = data;
t->len = numregs << 1;
spi_message_add_tail(t, m);
ret = spi_sync(spi, m);
if (!ret)
ret = tsc2101->message.status;
mutex_unlock(&tsc2101->mutex);
return ret;
}
EXPORT_SYMBOL(tsc2101_reads_sync);
int tsc2101_read_sync(struct spi_device *spi, int page, u8 address)
{
int err;
u16 val;
err = tsc2101_reads_sync(spi, page, address, &val, 1);
if (err)
return err;
return val;
}
EXPORT_SYMBOL(tsc2101_read_sync);
static int tsc2101_suspend(struct spi_device *spi, pm_message_t state)
{
struct tsc2101_device *tsc2101;
tsc2101 = spi_get_drvdata(spi);
if (tsc2101 == NULL)
return 0;
mutex_lock(&tsc2101->mutex);
spi->dev.power.power_state = state;
if (tsc2101->mclk_enabled && tsc2101->disable_mclk != NULL)
tsc2101->disable_mclk(spi);
mutex_unlock(&tsc2101->mutex);
return 0;
}
static int tsc2101_resume(struct spi_device *spi)
{
struct tsc2101_device *tsc2101;
tsc2101 = spi_get_drvdata(spi);
if (tsc2101 == NULL)
return 0;
mutex_lock(&tsc2101->mutex);
spi->dev.power.power_state = PMSG_ON;
if (tsc2101->mclk_enabled && tsc2101->enable_mclk != NULL)
tsc2101->enable_mclk(spi);
mutex_unlock(&tsc2101->mutex);
return 0;
}
static int tsc2101_probe(struct spi_device *spi)
{
struct tsc2101_platform_data *pdata;
struct tsc2101_device *tsc2101;
u16 w;
int r;
pdata = spi->dev.platform_data;
if (pdata == NULL) {
dev_err(&spi->dev, "no platform data\n");
return -ENODEV;
}
tsc2101 = kzalloc(sizeof(*tsc2101), GFP_KERNEL);
if (tsc2101 == NULL) {
dev_err(&spi->dev, "out of mem\n");
return -ENOMEM;
}
spi_set_drvdata(spi, tsc2101);
tsc2101->enable_mclk = pdata->enable_mclk;
tsc2101->disable_mclk = pdata->disable_mclk;
mutex_init(&tsc2101->mutex);
spi->mode = SPI_MODE_0;
spi->bits_per_word = 16;
if ((r = spi_setup(spi)) < 0) {
dev_err(&spi->dev, "SPI setup failed\n");
goto err;
}
w = tsc2101_read_sync(spi, 1, 0);
if (!(w & (1 << 14))) {
dev_err(&spi->dev, "invalid ADC register value %04x\n", w);
goto err;
}
if (pdata->init != NULL) {
if ((r = pdata->init(spi)) < 0) {
dev_err(&spi->dev, "platform init failed\n");
goto err;
}
}
dev_info(&spi->dev, "initialized\n");
return 0;
err:
kfree(tsc2101);
return r;
}
static int tsc2101_remove(struct spi_device *spi)
{
struct tsc2101_platform_data *pdata;
struct tsc2101_device *tsc2101;
pdata = spi->dev.platform_data;
tsc2101 = spi_get_drvdata(spi);
/* We assume that this can't race with the rest of the driver. */
if (tsc2101->mclk_enabled && tsc2101->disable_mclk != NULL)
tsc2101->disable_mclk(spi);
if (pdata->cleanup != NULL)
pdata->cleanup(spi);
spi_set_drvdata(spi, NULL);
kfree(tsc2101);
return 0;
}
static struct spi_driver tsc2101_driver = {
.probe = tsc2101_probe,
.remove = tsc2101_remove,
.suspend = tsc2101_suspend,
.resume = tsc2101_resume,
.driver = {
.name = "tsc2101",
.owner = THIS_MODULE,
},
};
static int tsc2101_init(void)
{
return spi_register_driver(&tsc2101_driver);
}
static void tsc2101_exit(void)
{
spi_unregister_driver(&tsc2101_driver);
}
module_init(tsc2101_init);
module_exit(tsc2101_exit);
MODULE_AUTHOR("Texas Instruments");
MODULE_DESCRIPTION
("Glue audio driver for the TI OMAP1610/OMAP1710 TSC2101 codec.");
MODULE_LICENSE("GPL");
/*
* include/linux/spi/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
#include <linux/spi/spi.h>
struct tsc2101_platform_data {
int (*init)(struct spi_device *spi);
void (*cleanup)(struct spi_device *spi);
void (*enable_mclk)(struct spi_device *spi);
void (*disable_mclk)(struct spi_device *spi);
};
extern int tsc2101_read_sync(struct spi_device *spi, int page, u8 address);
extern int tsc2101_reads_sync(struct spi_device *spi, int page,
u8 startaddress, u16 * data, int numregs);
extern int tsc2101_write_sync(struct spi_device *spi, int page, u8 address,
u16 data);
extern int tsc2101_enable_mclk(struct spi_device *spi);
extern void tsc2101_disable_mclk(struct spi_device *spi);
#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