cks.git/.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogblamecommitdiffstats
path: root/drivers/spi/spi-ti-ssp.c
blob: 7d20e121e4c1dfb521d6cf110f3eba7e870d00d5 (plain) (tree)























                                                                            
                         





















































































































































































































                                                                              























                                                                             













                                                 
                                                         






                                            
                                      






































                                                                     
                                                               
                                                         

















                                                             
                                                          



















                                                                         
                                            




                                       
                                          




                                     
/*
 * Sequencer Serial Port (SSP) based SPI master driver
 *
 * Copyright (C) 2010 Texas Instruments Inc
 *
 * 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 program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/mfd/ti_ssp.h>

#define MODE_BITS	(SPI_CPHA | SPI_CPOL | SPI_CS_HIGH)

struct ti_ssp_spi {
	struct spi_master		*master;
	struct device			*dev;
	spinlock_t			lock;
	struct list_head		msg_queue;
	struct completion		complete;
	bool				shutdown;
	struct workqueue_struct		*workqueue;
	struct work_struct		work;
	u8				mode, bpw;
	int				cs_active;
	u32				pc_en, pc_dis, pc_wr, pc_rd;
	void				(*select)(int cs);
};

static u32 ti_ssp_spi_rx(struct ti_ssp_spi *hw)
{
	u32 ret;

	ti_ssp_run(hw->dev, hw->pc_rd, 0, &ret);
	return ret;
}

static void ti_ssp_spi_tx(struct ti_ssp_spi *hw, u32 data)
{
	ti_ssp_run(hw->dev, hw->pc_wr, data << (32 - hw->bpw), NULL);
}

static int ti_ssp_spi_txrx(struct ti_ssp_spi *hw, struct spi_message *msg,
		       struct spi_transfer *t)
{
	int count;

	if (hw->bpw <= 8) {
		u8		*rx = t->rx_buf;
		const u8	*tx = t->tx_buf;

		for (count = 0; count < t->len; count += 1) {
			if (t->tx_buf)
				ti_ssp_spi_tx(hw, *tx++);
			if (t->rx_buf)
				*rx++ = ti_ssp_spi_rx(hw);
		}
	} else if (hw->bpw <= 16) {
		u16		*rx = t->rx_buf;
		const u16	*tx = t->tx_buf;

		for (count = 0; count < t->len; count += 2) {
			if (t->tx_buf)
				ti_ssp_spi_tx(hw, *tx++);
			if (t->rx_buf)
				*rx++ = ti_ssp_spi_rx(hw);
		}
	} else {
		u32		*rx = t->rx_buf;
		const u32	*tx = t->tx_buf;

		for (count = 0; count < t->len; count += 4) {
			if (t->tx_buf)
				ti_ssp_spi_tx(hw, *tx++);
			if (t->rx_buf)
				*rx++ = ti_ssp_spi_rx(hw);
		}
	}

	msg->actual_length += count; /* bytes transferred */

	dev_dbg(&msg->spi->dev, "xfer %s%s, %d bytes, %d bpw, count %d%s\n",
		t->tx_buf ? "tx" : "", t->rx_buf ? "rx" : "", t->len,
		hw->bpw, count, (count < t->len) ? " (under)" : "");

	return (count < t->len) ? -EIO : 0; /* left over data */
}