omap_uwire.c

来自「linux 内核源代码」· C语言 代码 · 共 581 行

C
581
字号
/* * omap_uwire.c -- MicroWire interface driver for OMAP * * Copyright 2003 MontaVista Software Inc. <source@mvista.com> * * Ported to 2.6 OMAP uwire interface. * Copyright (C) 2004 Texas Instruments. * * Generalization patches by Juha Yrjola <juha.yrjola@nokia.com> * * Copyright (C) 2005 David Brownell (ported to 2.6 SPI interface) * Copyright (C) 2006 Nokia * * Many updates by Imre Deak <imre.deak@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/kernel.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/workqueue.h>#include <linux/interrupt.h>#include <linux/err.h>#include <linux/clk.h>#include <linux/spi/spi.h>#include <linux/spi/spi_bitbang.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 *//* FIXME address is now a platform device resource, * and irqs should show there too... */#define UWIRE_BASE_PHYS		0xFFFB3000#define UWIRE_BASE		((void *__iomem)IO_ADDRESS(UWIRE_BASE_PHYS))/* uWire Registers: */#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/* CSR bits */#define	RDRB	(1 << 15)#define	CSRB	(1 << 14)#define	START	(1 << 13)#define	CS_CMD	(1 << 12)/* SR1 or SR2 bits */#define UWIRE_READ_FALLING_EDGE		0x0001#define UWIRE_READ_RISING_EDGE		0x0000#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		0x0040struct uwire_spi {	struct spi_bitbang	bitbang;	struct clk		*ck;};struct uwire_state {	unsigned	bits_per_word;	unsigned	div1_idx;};/* REVISIT compile time constant for idx_shift? */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));}static inline void omap_uwire_configure_mode(u8 cs, unsigned long flags){	u16	w, val = 0;	int	shift, reg;	if (flags & UWIRE_CLK_INVERTED)		val ^= 0x03;	val = flags & 0x3f;	if (cs & 1)		shift = 6;	else		shift = 0;	if (cs <= 1)		reg = UWIRE_SR1;	else		reg = UWIRE_SR2;	w = uwire_read_reg(reg);	w &= ~(0x3f << shift);	w |= val << shift;	uwire_write_reg(reg, w);}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;}static void uwire_set_clk1_div(int div1_idx){	u16 w;	w = uwire_read_reg(UWIRE_SR3);	w &= ~(0x03 << 1);	w |= div1_idx << 1;	uwire_write_reg(UWIRE_SR3, w);}static void uwire_chipselect(struct spi_device *spi, int value){	struct	uwire_state *ust = spi->controller_state;	u16	w;	int	old_cs;	BUG_ON(wait_uwire_csr_flag(CSRB, 0, 0));	w = uwire_read_reg(UWIRE_CSR);	old_cs = (w >> 10) & 0x03;	if (value == BITBANG_CS_INACTIVE || old_cs != spi->chip_select) {		/* Deselect this CS, or the previous CS */		w &= ~CS_CMD;		uwire_write_reg(UWIRE_CSR, w);	}	/* activate specfied chipselect */	if (value == BITBANG_CS_ACTIVE) {		uwire_set_clk1_div(ust->div1_idx);		/* invert clock? */		if (spi->mode & SPI_CPOL)			uwire_write_reg(UWIRE_SR4, 1);		else			uwire_write_reg(UWIRE_SR4, 0);		w = spi->chip_select << 10;		w |= CS_CMD;		uwire_write_reg(UWIRE_CSR, w);	}}static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t){	struct uwire_state *ust = spi->controller_state;	unsigned	len = t->len;	unsigned	bits = ust->bits_per_word;	unsigned	bytes;	u16		val, w;	int		status = 0;;	if (!t->tx_buf && !t->rx_buf)		return 0;	/* Microwire doesn't read and write concurrently */	if (t->tx_buf && t->rx_buf)		return -EPERM;	w = spi->chip_select << 10;	w |= CS_CMD;	if (t->tx_buf) {		const u8	*buf = t->tx_buf;		/* NOTE:  DMA could be used for TX transfers */		/* write one or two bytes at a time */		while (len >= 1) {			/* tx bit 15 is first sent; we byteswap multibyte words			 * (msb-first) on the way out from memory.			 */			val = *buf++;			if (bits > 8) {				bytes = 2;				val |= *buf++ << 8;			} else				bytes = 1;			val <<= 16 - bits;#ifdef	VERBOSE			pr_debug("%s: write-%d =%04x\n",					spi->dev.bus_id, bits, val);#endif			if (wait_uwire_csr_flag(CSRB, 0, 0))				goto eio;			uwire_write_reg(UWIRE_TDR, val);			/* start write */			val = START | w | (bits << 5);			uwire_write_reg(UWIRE_CSR, val);			len -= bytes;			/* Wait till write actually starts.			 * This is needed with MPU clock 60+ MHz.			 * REVISIT: we may not have time to catch it...			 */			if (wait_uwire_csr_flag(CSRB, CSRB, 1))				goto eio;			status += bytes;		}		/* REVISIT:  save this for later to get more i/o overlap */		if (wait_uwire_csr_flag(CSRB, 0, 0))			goto eio;	} else if (t->rx_buf) {		u8		*buf = t->rx_buf;		/* read one or two bytes at a time */		while (len) {			if (bits > 8) {				bytes = 2;			} else				bytes = 1;			/* start read */			val = START | w | (bits << 0);			uwire_write_reg(UWIRE_CSR, val);			len -= bytes;			/* Wait till read actually starts */			(void) wait_uwire_csr_flag(CSRB, CSRB, 1);			if (wait_uwire_csr_flag(RDRB | CSRB,						RDRB, 0))				goto eio;			/* rx bit 0 is last received; multibyte words will			 * be properly byteswapped on the way to memory.			 */			val = uwire_read_reg(UWIRE_RDR);			val &= (1 << bits) - 1;			*buf++ = (u8) val;			if (bytes == 2)				*buf++ = val >> 8;			status += bytes;#ifdef	VERBOSE			pr_debug("%s: read-%d =%04x\n",					spi->dev.bus_id, bits, val);#endif		}	}	return status;eio:	return -EIO;}static int uwire_setup_transfer(struct spi_device *spi, struct spi_transfer *t){	struct uwire_state	*ust = spi->controller_state;	struct uwire_spi	*uwire;	unsigned		flags = 0;	unsigned		bits;	unsigned		hz;	unsigned long		rate;	int			div1_idx;	int			div1;	int			div2;	int			status;	uwire = spi_master_get_devdata(spi->master);	if (spi->chip_select > 3) {		pr_debug("%s: cs%d?\n", spi->dev.bus_id, spi->chip_select);		status = -ENODEV;		goto done;	}	bits = spi->bits_per_word;	if (t != NULL && t->bits_per_word)		bits = t->bits_per_word;	if (!bits)		bits = 8;	if (bits > 16) {		pr_debug("%s: wordsize %d?\n", spi->dev.bus_id, bits);		status = -ENODEV;		goto done;	}	ust->bits_per_word = bits;	/* mode 0..3, clock inverted separately;	 * standard nCS signaling;	 * don't treat DI=high as "not ready"	 */	if (spi->mode & SPI_CS_HIGH)		flags |= UWIRE_CS_ACTIVE_HIGH;	if (spi->mode & SPI_CPOL)		flags |= UWIRE_CLK_INVERTED;	switch (spi->mode & (SPI_CPOL | SPI_CPHA)) {	case SPI_MODE_0:	case SPI_MODE_3:		flags |= UWIRE_WRITE_FALLING_EDGE | UWIRE_READ_RISING_EDGE;		break;	case SPI_MODE_1:	case SPI_MODE_2:		flags |= UWIRE_WRITE_RISING_EDGE | UWIRE_READ_FALLING_EDGE;		break;	}	/* assume it's already enabled */	rate = clk_get_rate(uwire->ck);	hz = spi->max_speed_hz;	if (t != NULL && t->speed_hz)		hz = t->speed_hz;	if (!hz) {		pr_debug("%s: zero speed?\n", spi->dev.bus_id);		status = -EINVAL;		goto done;	}	/* F_INT = mpu_xor_clk / DIV1 */	for (div1_idx = 0; div1_idx < 4; div1_idx++) {		switch (div1_idx) {		case 0:			div1 = 2;			break;		case 1:			div1 = 4;			break;		case 2:			div1 = 7;			break;		default:		case 3:			div1 = 10;			break;		}		div2 = (rate / div1 + hz - 1) / hz;		if (div2 <= 8)			break;	}	if (div1_idx == 4) {		pr_debug("%s: lowest clock %ld, need %d\n",			spi->dev.bus_id, rate / 10 / 8, hz);		status = -EDOM;		goto done;	}	/* we have to cache this and reset in uwire_chipselect as this is a	 * global parameter and another uwire device can change it under	 * us */	ust->div1_idx = div1_idx;	uwire_set_clk1_div(div1_idx);	rate /= div1;	switch (div2) {	case 0:	case 1:	case 2:		flags |= UWIRE_FREQ_DIV_2;		rate /= 2;		break;	case 3:	case 4:		flags |= UWIRE_FREQ_DIV_4;		rate /= 4;		break;	case 5:	case 6:	case 7:	case 8:		flags |= UWIRE_FREQ_DIV_8;		rate /= 8;		break;	}	omap_uwire_configure_mode(spi->chip_select, flags);	pr_debug("%s: uwire flags %02x, armxor %lu KHz, SCK %lu KHz\n",			__FUNCTION__, flags,			clk_get_rate(uwire->ck) / 1000,			rate / 1000);	status = 0;done:	return status;}/* the spi->mode bits understood by this driver: */#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)static int uwire_setup(struct spi_device *spi){	struct uwire_state *ust = spi->controller_state;	if (spi->mode & ~MODEBITS) {		dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n",			spi->mode & ~MODEBITS);		return -EINVAL;	}	if (ust == NULL) {		ust = kzalloc(sizeof(*ust), GFP_KERNEL);		if (ust == NULL)			return -ENOMEM;		spi->controller_state = ust;	}	return uwire_setup_transfer(spi, NULL);}static void uwire_cleanup(struct spi_device *spi){	kfree(spi->controller_state);}static void uwire_off(struct uwire_spi *uwire){	uwire_write_reg(UWIRE_SR3, 0);	clk_disable(uwire->ck);	clk_put(uwire->ck);	spi_master_put(uwire->bitbang.master);}static int __init uwire_probe(struct platform_device *pdev){	struct spi_master	*master;	struct uwire_spi	*uwire;	int			status;	master = spi_alloc_master(&pdev->dev, sizeof *uwire);	if (!master)		return -ENODEV;	uwire = spi_master_get_devdata(master);	dev_set_drvdata(&pdev->dev, uwire);	uwire->ck = clk_get(&pdev->dev, "armxor_ck");	if (!uwire->ck || IS_ERR(uwire->ck)) {		dev_dbg(&pdev->dev, "no mpu_xor_clk ?\n");		spi_master_put(master);		return -ENODEV;	}	clk_enable(uwire->ck);	if (cpu_is_omap730())		uwire_idx_shift = 1;	else		uwire_idx_shift = 2;	uwire_write_reg(UWIRE_SR3, 1);	master->bus_num = 2;	/* "official" */	master->num_chipselect = 4;	master->setup = uwire_setup;	master->cleanup = uwire_cleanup;	uwire->bitbang.master = master;	uwire->bitbang.chipselect = uwire_chipselect;	uwire->bitbang.setup_transfer = uwire_setup_transfer;	uwire->bitbang.txrx_bufs = uwire_txrx;	status = spi_bitbang_start(&uwire->bitbang);	if (status < 0)		uwire_off(uwire);	return status;}static int __exit uwire_remove(struct platform_device *pdev){	struct uwire_spi	*uwire = dev_get_drvdata(&pdev->dev);	int			status;	// FIXME remove all child devices, somewhere ...	status = spi_bitbang_stop(&uwire->bitbang);	uwire_off(uwire);	return status;}static struct platform_driver uwire_driver = {	.driver = {		.name		= "omap_uwire",		.bus		= &platform_bus_type,		.owner		= THIS_MODULE,	},	.remove		= __exit_p(uwire_remove),	// suspend ... unuse ck	// resume ... use ck};static int __init omap_uwire_init(void){	/* FIXME move these into the relevant board init code. also, include	 * H3 support; it uses tsc2101 like H2 (on a different chipselect).	 */	if (machine_is_omap_h2()) {		/* 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 platform_driver_probe(&uwire_driver, uwire_probe);}static void __exit omap_uwire_exit(void){	platform_driver_unregister(&uwire_driver);}subsys_initcall(omap_uwire_init);module_exit(omap_uwire_exit);MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?