/* * 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.
*/ #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/err.h> #include <linux/clk.h> #include <linux/slab.h> #include <linux/device.h>
/* REVISIT compile time constant for idx_shift? */ /* * Or, put it in a structure which is used throughout the driver; * that avoids having to issue two loads for each bit of static data.
*/ staticunsignedint uwire_idx_shift = 2; staticvoid __iomem *uwire_base;
w = uwire_read_reg(UWIRE_SR3);
w &= ~(0x03 << 1);
w |= div1_idx << 1;
uwire_write_reg(UWIRE_SR3, w);
}
staticvoid 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_get_chipselect(spi, 0)) { /* 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_get_chipselect(spi, 0) << 10;
w |= CS_CMD;
uwire_write_reg(UWIRE_CSR, w);
}
}
staticint uwire_txrx(struct spi_device *spi, struct spi_transfer *t)
{ unsigned len = t->len; unsigned bits = t->bits_per_word; unsigned bytes;
u16 val, w; int status = 0;
if (!t->tx_buf && !t->rx_buf) return 0;
w = spi_get_chipselect(spi, 0) << 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;
/* 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;
} elseif (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;
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",
dev_name(&spi->dev), bits, val); #endif
}
} return status;
eio: return -EIO;
}
staticint 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 hz; unsignedlong rate; int div1_idx; int div1; int div2; int status;
if (!hz) {
pr_debug("%s: zero speed?\n", dev_name(&spi->dev));
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",
dev_name(&spi->dev), 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_get_chipselect(spi, 0), flags);
pr_debug("%s: uwire flags %02x, armxor %lu KHz, SCK %lu KHz\n",
__func__, flags,
clk_get_rate(uwire->ck) / 1000,
rate / 1000);
status = 0;
done: return status;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.