blob: 9769811c643d2d822300db09951c33b456461d9d [file] [log] [blame]
/*
* Copyright(c) 2009 Dialog Semiconductor Ltd.
*
* 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.
*
* da9052-spi.c: SPI SSC (Synchronous Serial Communication) driver for DA9052
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/mfd/core.h>
#include <linux/spi/spi.h>
#include <linux/mfd/da9052/da9052.h>
#include <linux/mfd/da9052/reg.h>
struct da9052 *da9052_spi;
#define SPI_CONNECTED 0
static int da9052_spi_is_connected(void)
{
struct da9052_ssc_msg msg;
/* printk("Entered da9052_spi_is_connected.............\n"); */
msg.addr = DA9052_INTERFACE_REG;
/* Test spi connectivity by performing read of the GPIO_0-1 register
and then verify the read value*/
if (0 != da9052_spi_read(da9052_spi, &msg)) {
printk(KERN_DEBUG "da9052_spi_is_connected - "\
"spi read failed.............\n");
return -1;
} else if (0x88 != msg.data) {
printk(KERN_DEBUG "da9052_spi_is_connected - " \
"spi read failed. Msg data =%x ..............\n",
msg.data);
return -1;
}
return 0;
}
static int da9052_spi_probe(struct spi_device *spi)
{
/* printk("\n\tEntered da9052_spi_probe.....\n"); */
da9052_spi = kzalloc(sizeof(struct da9052), GFP_KERNEL);
if (!da9052_spi)
return -ENOMEM;
spi->mode = SPI_MODE_0 | SPI_CPOL;
spi->bits_per_word = 8;
spi_setup(spi);
da9052_spi->dev = &spi->dev;
da9052_spi->spi_dev = spi;
/*
* Allocate memory for RX/TX bufferes used in single register
* read/write
*/
da9052_spi->spi_rx_buf = kmalloc(2, GFP_KERNEL | GFP_DMA);
if (!da9052_spi->spi_rx_buf)
return -ENOMEM;
da9052_spi->spi_tx_buf = kmalloc(2, GFP_KERNEL | GFP_DMA);
if (!da9052_spi->spi_tx_buf)
return -ENOMEM;
da9052_spi->spi_active_page = PAGECON_0;
da9052_spi->rw_pol = 1;
dev_set_drvdata(&spi->dev, da9052_spi);
/* Validate SPI connectivity */
if (SPI_CONNECTED == da9052_spi_is_connected()) {
/* SPI is connected */
da9052_spi->connecting_device = SPI;
if (0 != da9052_ssc_init(da9052_spi))
return -ENODEV;
} else {
return -ENODEV;
}
/* printk("Exiting da9052_spi_probe.....\n"); */
return 0;
}
static int da9052_spi_remove(struct spi_device *spi)
{
struct da9052 *da9052 = dev_get_drvdata(&spi->dev);
printk("Entered da9052_spi_remove()\n");
if (SPI == da9052->connecting_device)
da9052_ssc_exit(da9052);
mfd_remove_devices(&spi->dev);
kfree(da9052->spi_rx_buf);
kfree(da9052->spi_tx_buf);
kfree(da9052);
return 0;
}
static struct spi_driver da9052_spi_driver = {
.driver = {
.name = DA9052_SSC_SPI_DEVICE_NAME,
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = da9052_spi_probe,
.remove = __devexit_p(da9052_spi_remove),
};
static int da9052_spi_set_page(struct da9052 *da9052, unsigned char page)
{
struct da9052_ssc_msg sscmsg;
struct spi_message message;
struct spi_transfer xfer;
int ret = 0;
printk(KERN_DEBUG "Entered da9052_spi_set_page.....\n");
if ((page != PAGECON_0) && ((page != PAGECON_128)))
return INVALID_PAGE;
/* Current configuration is PAGE-0 and write request for PAGE-1 */
/* set register address */
sscmsg.addr = DA9052_PAGECON0_REG;
/* set value */
sscmsg.data = page;
/* Check value of R/W_POL bit of INTERFACE register */
if (!da9052->rw_pol) {
/* We need to set 0th bit for write operation */
sscmsg.addr = ((sscmsg.addr << 1) | RW_POL);
} else {
/* We need to reset 0th bit for write operation */
sscmsg.addr = (sscmsg.addr << 1);
}
/* SMDK-6410 host SPI driver specific stuff */
/* Build our spi message */
printk(KERN_DEBUG "da9052_spi_set_page - "\
"Calling spi_message_init.....\n");
spi_message_init(&message);
memset(&xfer, 0, sizeof(xfer));
xfer.len = 2;
xfer.tx_buf = da9052->spi_tx_buf;
xfer.rx_buf = da9052->spi_rx_buf;
da9052->spi_tx_buf[0] = sscmsg.addr;
da9052->spi_tx_buf[1] = sscmsg.data;
printk(KERN_DEBUG "da9052_spi_set_page - "\
"Calling spi_message_add_tail.....\n");
spi_message_add_tail(&xfer, &message);
/* Now, do the i/o */
printk(KERN_DEBUG "da9052_spi_set_page - Calling spi_sync.....\n");
ret = spi_sync(da9052->spi_dev, &message);
if (ret == 0) {
/* Active Page set successfully */
da9052->spi_active_page = page;
return 0;
} else {
/* Error in setting Active Page */
return ret;
}
return 0;
}
int da9052_spi_write(struct da9052 *da9052, struct da9052_ssc_msg *msg)
{
struct spi_message message;
struct spi_transfer xfer;
int ret;
/*
* We need a seperate copy of da9052_ssc_msg so that caller's
* copy remains intact
*/
struct da9052_ssc_msg sscmsg;
/* Copy callers data in to our local copy */
sscmsg.addr = msg->addr;
sscmsg.data = msg->data;
if ((sscmsg.addr > PAGE_0_END) &&
(da9052->spi_active_page == PAGECON_0)) {
/*
* Current configuration is PAGE-0 and write request
* for PAGE-1
*/
da9052_spi_set_page(da9052, PAGECON_128);
/* Set register address accordindly */
sscmsg.addr = (sscmsg.addr - PAGE_1_START);
} else if ((sscmsg.addr < PAGE_1_START) &&
(da9052->spi_active_page == PAGECON_128)) {
/*
* Current configuration is PAGE-1 and write request
* for PAGE-0
*/
da9052_spi_set_page(da9052, PAGECON_0);
} else if (sscmsg.addr > PAGE_0_END) {
/*
* Current configuration is PAGE-1 and write request
* for PAGE-1. Just need to adjust register address
*/
sscmsg.addr = (sscmsg.addr - PAGE_1_START);
}
/* Check value of R/W_POL bit of INTERFACE register */
if (!da9052->rw_pol) {
/* We need to set 0th bit for write operation */
sscmsg.addr = ((sscmsg.addr << 1) | RW_POL);
} else {
/* We need to reset 0th bit for write operation */
sscmsg.addr = (sscmsg.addr << 1);
}
/* SMDK-6410 host SPI driver specific stuff */
/* Build our spi message */
spi_message_init(&message);
memset(&xfer, 0, sizeof(xfer));
xfer.len = 2;
xfer.tx_buf = da9052->spi_tx_buf;
xfer.rx_buf = da9052->spi_rx_buf;
da9052->spi_tx_buf[0] = sscmsg.addr;
da9052->spi_tx_buf[1] = sscmsg.data;
spi_message_add_tail(&xfer, &message);
/* Now, do the i/o */
ret = spi_sync(da9052->spi_dev, &message);
return ret;
}
int da9052_spi_write_many(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg,
int msg_no)
{
int cnt, ret = 0;
for (cnt = 0; cnt < msg_no; cnt++, sscmsg++) {
ret = da9052_ssc_write(da9052, sscmsg);
if (ret != 0) {
printk(KERN_DEBUG "Error in %s\n", __func__);
return -EIO;
}
}
return 0;
}
int da9052_spi_read(struct da9052 *da9052, struct da9052_ssc_msg *msg)
{
struct spi_message message;
struct spi_transfer xfer;
int ret;
/*
* We need a seperate copy of da9052_ssc_msg so that
* caller's copy remains intact
*/
struct da9052_ssc_msg sscmsg;
/* Copy callers data in to our local copy */
sscmsg.addr = msg->addr;
sscmsg.data = msg->data;
if ((sscmsg.addr > PAGE_0_END) &&
(da9052->spi_active_page == PAGECON_0)) {
/*
* Current configuration is PAGE-0 and
* read request for PAGE-1
*/
printk(KERN_DEBUG "da9052_spi_read - if PAGECON_128.....\n");
da9052_spi_set_page(da9052, PAGECON_128);
/* Set register address accordindly */
sscmsg.addr = (sscmsg.addr - PAGE_1_START);
} else if ((sscmsg.addr < PAGE_1_START) &&
(da9052->spi_active_page == PAGECON_128)) {
/*
* Current configuration is PAGE-1 and
* write request for PAGE-0
*/
printk(KERN_DEBUG "da9052_spi_read - if PAGECON_0.....\n");
da9052_spi_set_page(da9052, PAGECON_0);
} else if (sscmsg.addr > PAGE_0_END) {
/*
* Current configuration is PAGE-1 and write
* request for PAGE-1
* Just need to adjust register address
*/
sscmsg.addr = (sscmsg.addr - PAGE_1_START);
}
/* Check value of R/W_POL bit of INTERFACE register */
if (da9052->rw_pol) {
/* We need to set 0th bit for read operation */
sscmsg.addr = ((sscmsg.addr << 1) | RW_POL);
} else {
/* We need to reset 0th bit for write operation */
sscmsg.addr = (sscmsg.addr << 1);
}
/* SMDK-6410 host SPI driver specific stuff */
/* Build our spi message */
spi_message_init(&message);
memset(&xfer, 0, sizeof(xfer));
xfer.len = 2;
xfer.tx_buf = da9052->spi_tx_buf;
xfer.rx_buf = da9052->spi_rx_buf;
da9052->spi_tx_buf[0] = sscmsg.addr;
da9052->spi_tx_buf[1] = 0xff;
da9052->spi_rx_buf[0] = 0;
da9052->spi_rx_buf[1] = 0;
spi_message_add_tail(&xfer, &message);
/* Now, do the i/o */
ret = spi_sync(da9052->spi_dev, &message);
if (ret == 0) {
/* Update read value in callers copy */
msg->data = da9052->spi_rx_buf[1];
return 0;
} else {
return ret;
}
return 0;
}
int da9052_spi_read_many(struct da9052 *da9052, struct da9052_ssc_msg *sscmsg,
int msg_no)
{
int cnt, ret = 0;
for (cnt = 0; cnt < msg_no; cnt++, sscmsg++) {
ret = da9052_ssc_read(da9052, sscmsg);
if (ret != 0) {
printk(KERN_DEBUG "Error in %s\n", __func__);
return -EIO;
}
}
return 0;
}
static int __init da9052_spi_init(void)
{
int ret = 0;
/*printk("Entered da9052_spi_init.....\n");*/
ret = spi_register_driver(&da9052_spi_driver);
if (ret != 0) {
printk(KERN_ERR "Unable to register %s\n",
DA9052_SSC_SPI_DEVICE_NAME);
return ret;
}
return 0;
}
module_init(da9052_spi_init);
static void __exit da9052_spi_exit(void)
{
spi_unregister_driver(&da9052_spi_driver);
}
module_exit(da9052_spi_exit);
MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>");
MODULE_DESCRIPTION("SPI driver for Dialog DA9052 PMIC");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DA9052_SSC_SPI_DEVICE_NAME);