summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/dma/apbh_dma.c23
-rw-r--r--drivers/gpio/mxs_gpio.c16
-rw-r--r--drivers/i2c/Makefile1
-rw-r--r--drivers/i2c/tegra_i2c.c569
-rw-r--r--drivers/misc/pmic_i2c.c6
-rw-r--r--drivers/mmc/mmc.c102
-rw-r--r--drivers/mmc/mxsmmc.c70
-rw-r--r--drivers/mtd/nand/mxs_nand.c53
-rw-r--r--drivers/mtd/nand/omap_gpmc.c14
-rw-r--r--drivers/net/calxedaxgmac.c1
-rw-r--r--drivers/net/fec_mxc.c277
-rw-r--r--drivers/net/fec_mxc.h19
-rw-r--r--drivers/pcmcia/Makefile1
-rw-r--r--drivers/pcmcia/pxa_pcmcia.c93
-rw-r--r--drivers/power/twl4030.c18
-rw-r--r--drivers/spi/mxs_spi.c6
-rw-r--r--drivers/spi/omap3_spi.c66
-rw-r--r--drivers/spi/omap3_spi.h2
-rw-r--r--drivers/usb/host/Makefile2
-rw-r--r--drivers/usb/host/ehci-hcd.c7
-rw-r--r--drivers/usb/host/ehci-mx6.c200
-rw-r--r--drivers/usb/host/ehci-mxs.c8
-rw-r--r--drivers/usb/host/ehci-tegra.c62
-rw-r--r--drivers/usb/host/ehci.h6
24 files changed, 1358 insertions, 264 deletions
diff --git a/drivers/dma/apbh_dma.c b/drivers/dma/apbh_dma.c
index e85f5fe6d2..c086629b0a 100644
--- a/drivers/dma/apbh_dma.c
+++ b/drivers/dma/apbh_dma.c
@@ -93,6 +93,21 @@ static int mxs_dma_read_semaphore(int channel)
return tmp;
}
+#ifndef CONFIG_SYS_DCACHE_OFF
+void mxs_dma_flush_desc(struct mxs_dma_desc *desc)
+{
+ uint32_t addr;
+ uint32_t size;
+
+ addr = (uint32_t)desc;
+ size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT);
+
+ flush_dcache_range(addr, addr + size);
+}
+#else
+inline void mxs_dma_flush_desc(struct mxs_dma_desc *desc) {}
+#endif
+
/*
* Enable a DMA channel.
*
@@ -329,8 +344,10 @@ static int mxs_dma_release(int channel)
struct mxs_dma_desc *mxs_dma_desc_alloc(void)
{
struct mxs_dma_desc *pdesc;
+ uint32_t size;
- pdesc = memalign(MXS_DMA_ALIGNMENT, sizeof(struct mxs_dma_desc));
+ size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT);
+ pdesc = memalign(MXS_DMA_ALIGNMENT, size);
if (pdesc == NULL)
return NULL;
@@ -415,12 +432,16 @@ int mxs_dma_desc_append(int channel, struct mxs_dma_desc *pdesc)
last->cmd.next = mxs_dma_cmd_address(pdesc);
last->cmd.data |= MXS_DMA_DESC_CHAIN;
+
+ mxs_dma_flush_desc(last);
}
pdesc->flags |= MXS_DMA_DESC_READY;
if (pdesc->flags & MXS_DMA_DESC_FIRST)
pchan->pending_num++;
list_add_tail(&pdesc->node, &pchan->active);
+ mxs_dma_flush_desc(pdesc);
+
return ret;
}
diff --git a/drivers/gpio/mxs_gpio.c b/drivers/gpio/mxs_gpio.c
index 0365812c0a..38dbc81b45 100644
--- a/drivers/gpio/mxs_gpio.c
+++ b/drivers/gpio/mxs_gpio.c
@@ -73,8 +73,8 @@ int gpio_get_value(unsigned gpio)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DIN(bank);
- struct mx28_register *reg =
- (struct mx28_register *)(MXS_PINCTRL_BASE + offset);
+ struct mx28_register_32 *reg =
+ (struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset);
return (readl(&reg->reg) >> PAD_PIN(gpio)) & 1;
}
@@ -83,8 +83,8 @@ void gpio_set_value(unsigned gpio, int value)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DOUT(bank);
- struct mx28_register *reg =
- (struct mx28_register *)(MXS_PINCTRL_BASE + offset);
+ struct mx28_register_32 *reg =
+ (struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset);
if (value)
writel(1 << PAD_PIN(gpio), &reg->reg_set);
@@ -96,8 +96,8 @@ int gpio_direction_input(unsigned gpio)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DOE(bank);
- struct mx28_register *reg =
- (struct mx28_register *)(MXS_PINCTRL_BASE + offset);
+ struct mx28_register_32 *reg =
+ (struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset);
writel(1 << PAD_PIN(gpio), &reg->reg_clr);
@@ -108,8 +108,8 @@ int gpio_direction_output(unsigned gpio, int value)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DOE(bank);
- struct mx28_register *reg =
- (struct mx28_register *)(MXS_PINCTRL_BASE + offset);
+ struct mx28_register_32 *reg =
+ (struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset);
writel(1 << PAD_PIN(gpio), &reg->reg_set);
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 504db03c71..f86e46c111 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -41,6 +41,7 @@ COBJS-$(CONFIG_DRIVER_S3C24X0_I2C) += s3c24x0_i2c.o
COBJS-$(CONFIG_S3C44B0_I2C) += s3c44b0_i2c.o
COBJS-$(CONFIG_SOFT_I2C) += soft_i2c.o
COBJS-$(CONFIG_SPEAR_I2C) += spr_i2c.o
+COBJS-$(CONFIG_TEGRA_I2C) += tegra_i2c.o
COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o
COBJS-$(CONFIG_SH_I2C) += sh_i2c.o
diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c
new file mode 100644
index 0000000000..21f6897269
--- /dev/null
+++ b/drivers/i2c/tegra_i2c.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Copyright (c) 2010-2011 NVIDIA Corporation
+ * NVIDIA Corporation <www.nvidia.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <common.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <asm/io.h>
+#include <asm/arch/clk_rst.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/funcmux.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/pinmux.h>
+#include <asm/arch/tegra_i2c.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static unsigned int i2c_bus_num;
+
+/* Information about i2c controller */
+struct i2c_bus {
+ int id;
+ enum periph_id periph_id;
+ int speed;
+ int pinmux_config;
+ struct i2c_control *control;
+ struct i2c_ctlr *regs;
+ int is_dvc; /* DVC type, rather than I2C */
+ int inited; /* bus is inited */
+};
+
+static struct i2c_bus i2c_controllers[TEGRA_I2C_NUM_CONTROLLERS];
+
+static void set_packet_mode(struct i2c_bus *i2c_bus)
+{
+ u32 config;
+
+ config = I2C_CNFG_NEW_MASTER_FSM_MASK | I2C_CNFG_PACKET_MODE_MASK;
+
+ if (i2c_bus->is_dvc) {
+ struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs;
+
+ writel(config, &dvc->cnfg);
+ } else {
+ writel(config, &i2c_bus->regs->cnfg);
+ /*
+ * program I2C_SL_CNFG.NEWSL to ENABLE. This fixes probe
+ * issues, i.e., some slaves may be wrongly detected.
+ */
+ setbits_le32(&i2c_bus->regs->sl_cnfg, I2C_SL_CNFG_NEWSL_MASK);
+ }
+}
+
+static void i2c_reset_controller(struct i2c_bus *i2c_bus)
+{
+ /* Reset I2C controller. */
+ reset_periph(i2c_bus->periph_id, 1);
+
+ /* re-program config register to packet mode */
+ set_packet_mode(i2c_bus);
+}
+
+static void i2c_init_controller(struct i2c_bus *i2c_bus)
+{
+ /*
+ * Use PLLP - DP-04508-001_v06 datasheet indicates a divisor of 8
+ * here, in section 23.3.1, but in fact we seem to need a factor of
+ * 16 to get the right frequency.
+ */
+ clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH,
+ i2c_bus->speed * 2 * 8);
+
+ /* Reset I2C controller. */
+ i2c_reset_controller(i2c_bus);
+
+ /* Configure I2C controller. */
+ if (i2c_bus->is_dvc) { /* only for DVC I2C */
+ struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs;
+
+ setbits_le32(&dvc->ctrl3, DVC_CTRL_REG3_I2C_HW_SW_PROG_MASK);
+ }
+
+ funcmux_select(i2c_bus->periph_id, i2c_bus->pinmux_config);
+}
+
+static void send_packet_headers(
+ struct i2c_bus *i2c_bus,
+ struct i2c_trans_info *trans,
+ u32 packet_id)
+{
+ u32 data;
+
+ /* prepare header1: Header size = 0 Protocol = I2C, pktType = 0 */
+ data = PROTOCOL_TYPE_I2C << PKT_HDR1_PROTOCOL_SHIFT;
+ data |= packet_id << PKT_HDR1_PKT_ID_SHIFT;
+ data |= i2c_bus->id << PKT_HDR1_CTLR_ID_SHIFT;
+ writel(data, &i2c_bus->control->tx_fifo);
+ debug("pkt header 1 sent (0x%x)\n", data);
+
+ /* prepare header2 */
+ data = (trans->num_bytes - 1) << PKT_HDR2_PAYLOAD_SIZE_SHIFT;
+ writel(data, &i2c_bus->control->tx_fifo);
+ debug("pkt header 2 sent (0x%x)\n", data);
+
+ /* prepare IO specific header: configure the slave address */
+ data = trans->address << PKT_HDR3_SLAVE_ADDR_SHIFT;
+
+ /* Enable Read if it is not a write transaction */
+ if (!(trans->flags & I2C_IS_WRITE))
+ data |= PKT_HDR3_READ_MODE_MASK;
+
+ /* Write I2C specific header */
+ writel(data, &i2c_bus->control->tx_fifo);
+ debug("pkt header 3 sent (0x%x)\n", data);
+}
+
+static int wait_for_tx_fifo_empty(struct i2c_control *control)
+{
+ u32 count;
+ int timeout_us = I2C_TIMEOUT_USEC;
+
+ while (timeout_us >= 0) {
+ count = (readl(&control->fifo_status) & TX_FIFO_EMPTY_CNT_MASK)
+ >> TX_FIFO_EMPTY_CNT_SHIFT;
+ if (count == I2C_FIFO_DEPTH)
+ return 1;
+ udelay(10);
+ timeout_us -= 10;
+ }
+
+ return 0;
+}
+
+static int wait_for_rx_fifo_notempty(struct i2c_control *control)
+{
+ u32 count;
+ int timeout_us = I2C_TIMEOUT_USEC;
+
+ while (timeout_us >= 0) {
+ count = (readl(&control->fifo_status) & TX_FIFO_FULL_CNT_MASK)
+ >> TX_FIFO_FULL_CNT_SHIFT;
+ if (count)
+ return 1;
+ udelay(10);
+ timeout_us -= 10;
+ }
+
+ return 0;
+}
+
+static int wait_for_transfer_complete(struct i2c_control *control)
+{
+ int int_status;
+ int timeout_us = I2C_TIMEOUT_USEC;
+
+ while (timeout_us >= 0) {
+ int_status = readl(&control->int_status);
+ if (int_status & I2C_INT_NO_ACK_MASK)
+ return -int_status;
+ if (int_status & I2C_INT_ARBITRATION_LOST_MASK)
+ return -int_status;
+ if (int_status & I2C_INT_XFER_COMPLETE_MASK)
+ return 0;
+
+ udelay(10);
+ timeout_us -= 10;
+ }
+
+ return -1;
+}
+
+static int send_recv_packets(struct i2c_bus *i2c_bus,
+ struct i2c_trans_info *trans)
+{
+ struct i2c_control *control = i2c_bus->control;
+ u32 int_status;
+ u32 words;
+ u8 *dptr;
+ u32 local;
+ uchar last_bytes;
+ int error = 0;
+ int is_write = trans->flags & I2C_IS_WRITE;
+
+ /* clear status from previous transaction, XFER_COMPLETE, NOACK, etc. */
+ int_status = readl(&control->int_status);
+ writel(int_status, &control->int_status);
+
+ send_packet_headers(i2c_bus, trans, 1);
+
+ words = DIV_ROUND_UP(trans->num_bytes, 4);
+ last_bytes = trans->num_bytes & 3;
+ dptr = trans->buf;
+
+ while (words) {
+ u32 *wptr = (u32 *)dptr;
+
+ if (is_write) {
+ /* deal with word alignment */
+ if ((unsigned)dptr & 3) {
+ memcpy(&local, dptr, sizeof(u32));
+ writel(local, &control->tx_fifo);
+ debug("pkt data sent (0x%x)\n", local);
+ } else {
+ writel(*wptr, &control->tx_fifo);
+ debug("pkt data sent (0x%x)\n", *wptr);
+ }
+ if (!wait_for_tx_fifo_empty(control)) {
+ error = -1;
+ goto exit;
+ }
+ } else {
+ if (!wait_for_rx_fifo_notempty(control)) {
+ error = -1;
+ goto exit;
+ }
+ /*
+ * for the last word, we read into our local buffer,
+ * in case that caller did not provide enough buffer.
+ */
+ local = readl(&control->rx_fifo);
+ if ((words == 1) && last_bytes)
+ memcpy(dptr, (char *)&local, last_bytes);
+ else if ((unsigned)dptr & 3)
+ memcpy(dptr, &local, sizeof(u32));
+ else
+ *wptr = local;
+ debug("pkt data received (0x%x)\n", local);
+ }
+ words--;
+ dptr += sizeof(u32);
+ }
+
+ if (wait_for_transfer_complete(control)) {
+ error = -1;
+ goto exit;
+ }
+ return 0;
+exit:
+ /* error, reset the controller. */
+ i2c_reset_controller(i2c_bus);
+
+ return error;
+}
+
+static int tegra2_i2c_write_data(u32 addr, u8 *data, u32 len)
+{
+ int error;
+ struct i2c_trans_info trans_info;
+
+ trans_info.address = addr;
+ trans_info.buf = data;
+ trans_info.flags = I2C_IS_WRITE;
+ trans_info.num_bytes = len;
+ trans_info.is_10bit_address = 0;
+
+ error = send_recv_packets(&i2c_controllers[i2c_bus_num], &trans_info);
+ if (error)
+ debug("tegra2_i2c_write_data: Error (%d) !!!\n", error);
+
+ return error;
+}
+
+static int tegra2_i2c_read_data(u32 addr, u8 *data, u32 len)
+{
+ int error;
+ struct i2c_trans_info trans_info;
+
+ trans_info.address = addr | 1;
+ trans_info.buf = data;
+ trans_info.flags = 0;
+ trans_info.num_bytes = len;
+ trans_info.is_10bit_address = 0;
+
+ error = send_recv_packets(&i2c_controllers[i2c_bus_num], &trans_info);
+ if (error)
+ debug("tegra2_i2c_read_data: Error (%d) !!!\n", error);
+
+ return error;
+}
+
+#ifndef CONFIG_OF_CONTROL
+#error "Please enable device tree support to use this driver"
+#endif
+
+unsigned int i2c_get_bus_speed(void)
+{
+ return i2c_controllers[i2c_bus_num].speed;
+}
+
+int i2c_set_bus_speed(unsigned int speed)
+{
+ struct i2c_bus *i2c_bus;
+
+ i2c_bus = &i2c_controllers[i2c_bus_num];
+ i2c_bus->speed = speed;
+ i2c_init_controller(i2c_bus);
+
+ return 0;
+}
+
+static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus)
+{
+ i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node, "reg");
+
+ /*
+ * We don't have a binding for pinmux yet. Leave it out for now. So
+ * far no one needs anything other than the default.
+ */
+ i2c_bus->pinmux_config = FUNCMUX_DEFAULT;
+ i2c_bus->speed = fdtdec_get_int(blob, node, "clock-frequency", 0);
+ i2c_bus->periph_id = clock_decode_periph_id(blob, node);
+
+ /*
+ * We can't specify the pinmux config in the fdt, so I2C2 will not
+ * work on Seaboard. It normally has no devices on it anyway.
+ * You could add in this little hack if you need to use it.
+ * The correct solution is a pinmux binding in the fdt.
+ *
+ * if (i2c_bus->periph_id == PERIPH_ID_I2C2)
+ * i2c_bus->pinmux_config = FUNCMUX_I2C2_PTA;
+ */
+ if (i2c_bus->periph_id == -1)
+ return -FDT_ERR_NOTFOUND;
+
+ return 0;
+}
+
+/*
+ * Process a list of nodes, adding them to our list of I2C ports.
+ *
+ * @param blob fdt blob
+ * @param node_list list of nodes to process (any <=0 are ignored)
+ * @param count number of nodes to process
+ * @param is_dvc 1 if these are DVC ports, 0 if standard I2C
+ * @return 0 if ok, -1 on error
+ */
+static int process_nodes(const void *blob, int node_list[], int count,
+ int is_dvc)
+{
+ struct i2c_bus *i2c_bus;
+ int i;
+
+ /* build the i2c_controllers[] for each controller */
+ for (i = 0; i < count; i++) {
+ int node = node_list[i];
+
+ if (node <= 0)
+ continue;
+
+ i2c_bus = &i2c_controllers[i];
+ i2c_bus->id = i;
+
+ if (i2c_get_config(blob, node, i2c_bus)) {
+ printf("i2c_init_board: failed to decode bus %d\n", i);
+ return -1;
+ }
+
+ i2c_bus->is_dvc = is_dvc;
+ if (is_dvc) {
+ i2c_bus->control =
+ &((struct dvc_ctlr *)i2c_bus->regs)->control;
+ } else {
+ i2c_bus->control = &i2c_bus->regs->control;
+ }
+ debug("%s: controller bus %d at %p, periph_id %d, speed %d: ",
+ is_dvc ? "dvc" : "i2c", i, i2c_bus->regs,
+ i2c_bus->periph_id, i2c_bus->speed);
+ i2c_init_controller(i2c_bus);
+ debug("ok\n");
+ i2c_bus->inited = 1;
+
+ /* Mark position as used */
+ node_list[i] = -1;
+ }
+
+ return 0;
+}
+
+/* Sadly there is no error return from this function */
+void i2c_init_board(void)
+{
+ int node_list[TEGRA_I2C_NUM_CONTROLLERS];
+ const void *blob = gd->fdt_blob;
+ int count;
+
+ /* First get the normal i2c ports */
+ count = fdtdec_find_aliases_for_id(blob, "i2c",
+ COMPAT_NVIDIA_TEGRA20_I2C, node_list,
+ TEGRA_I2C_NUM_CONTROLLERS);
+ if (process_nodes(blob, node_list, count, 0))
+ return;
+
+ /* Now look for dvc ports */
+ count = fdtdec_add_aliases_for_id(blob, "i2c",
+ COMPAT_NVIDIA_TEGRA20_DVC, node_list,
+ TEGRA_I2C_NUM_CONTROLLERS);
+ if (process_nodes(blob, node_list, count, 1))
+ return;
+}
+
+void i2c_init(int speed, int slaveaddr)
+{
+ /* This will override the speed selected in the fdt for that port */
+ debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr);
+ i2c_set_bus_speed(speed);
+}
+
+/* i2c write version without the register address */
+int i2c_write_data(uchar chip, uchar *buffer, int len)
+{
+ int rc;
+
+ debug("i2c_write_data: chip=0x%x, len=0x%x\n", chip, len);
+ debug("write_data: ");
+ /* use rc for counter */
+ for (rc = 0; rc < len; ++rc)
+ debug(" 0x%02x", buffer[rc]);
+ debug("\n");
+
+ /* Shift 7-bit address over for lower-level i2c functions */
+ rc = tegra2_i2c_write_data(chip << 1, buffer, len);
+ if (rc)
+ debug("i2c_write_data(): rc=%d\n", rc);
+
+ return rc;
+}
+
+/* i2c read version without the register address */
+int i2c_read_data(uchar chip, uchar *buffer, int len)
+{
+ int rc;
+
+ debug("inside i2c_read_data():\n");
+ /* Shift 7-bit address over for lower-level i2c functions */
+ rc = tegra2_i2c_read_data(chip << 1, buffer, len);
+ if (rc) {
+ debug("i2c_read_data(): rc=%d\n", rc);
+ return rc;
+ }
+
+ debug("i2c_read_data: ");
+ /* reuse rc for counter*/
+ for (rc = 0; rc < len; ++rc)
+ debug(" 0x%02x", buffer[rc]);
+ debug("\n");
+
+ return 0;
+}
+
+/* Probe to see if a chip is present. */
+int i2c_probe(uchar chip)
+{
+ int rc;
+ uchar reg;
+
+ debug("i2c_probe: addr=0x%x\n", chip);
+ reg = 0;
+ rc = i2c_write_data(chip, &reg, 1);
+ if (rc) {
+ debug("Error probing 0x%x.\n", chip);
+ return 1;
+ }
+ return 0;
+}
+
+static int i2c_addr_ok(const uint addr, const int alen)
+{
+ /* We support 7 or 10 bit addresses, so one or two bytes each */
+ return alen == 1 || alen == 2;
+}
+
+/* Read bytes */
+int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
+{
+ uint offset;
+ int i;
+
+ debug("i2c_read: chip=0x%x, addr=0x%x, len=0x%x\n",
+ chip, addr, len);
+ if (!i2c_addr_ok(addr, alen)) {
+ debug("i2c_read: Bad address %x.%d.\n", addr, alen);
+ return 1;
+ }
+ for (offset = 0; offset < len; offset++) {
+ if (alen) {
+ uchar data[alen];
+ for (i = 0; i < alen; i++) {
+ data[alen - i - 1] =
+ (addr + offset) >> (8 * i);
+ }
+ if (i2c_write_data(chip, data, alen)) {
+ debug("i2c_read: error sending (0x%x)\n",
+ addr);
+ return 1;
+ }
+ }
+ if (i2c_read_data(chip, buffer + offset, 1)) {
+ debug("i2c_read: error reading (0x%x)\n", addr);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Write bytes */
+int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
+{
+ uint offset;
+ int i;
+
+ debug("i2c_write: chip=0x%x, addr=0x%x, len=0x%x\n",
+ chip, addr, len);
+ if (!i2c_addr_ok(addr, alen)) {
+ debug("i2c_write: Bad address %x.%d.\n", addr, alen);
+ return 1;
+ }
+ for (offset = 0; offset < len; offset++) {
+ uchar data[alen + 1];
+ for (i = 0; i < alen; i++)
+ data[alen - i - 1] = (addr + offset) >> (8 * i);
+ data[alen] = buffer[offset];
+ if (i2c_write_data(chip, data, alen + 1)) {
+ debug("i2c_write: error sending (0x%x)\n", addr);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_I2C_MULTI_BUS)
+/*
+ * Functions for multiple I2C bus handling
+ */
+unsigned int i2c_get_bus_num(void)
+{
+ return i2c_bus_num;
+}
+
+int i2c_set_bus_num(unsigned int bus)
+{
+ if (bus >= TEGRA_I2C_NUM_CONTROLLERS || !i2c_controllers[bus].inited)
+ return -1;
+ i2c_bus_num = bus;
+
+ return 0;
+}
+#endif
diff --git a/drivers/misc/pmic_i2c.c b/drivers/misc/pmic_i2c.c
index ad55d6447e..95a3365b9f 100644
--- a/drivers/misc/pmic_i2c.c
+++ b/drivers/misc/pmic_i2c.c
@@ -47,6 +47,9 @@ int pmic_reg_write(struct pmic *p, u32 reg, u32 val)
case 1:
buf[0] = val & 0xff;
break;
+ default:
+ printf("%s: invalid tx_num: %d", __func__, pmic_i2c_tx_num);
+ return -1;
}
if (i2c_write(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
@@ -73,6 +76,9 @@ int pmic_reg_read(struct pmic *p, u32 reg, u32 *val)
case 1:
ret_val = buf[0];
break;
+ default:
+ printf("%s: invalid tx_num: %d", __func__, pmic_i2c_tx_num);
+ return -1;
}
memcpy(val, &ret_val, sizeof(ret_val));
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 49c3349f55..74e5fea667 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -47,10 +47,105 @@ int __board_mmc_getcd(struct mmc *mmc) {
int board_mmc_getcd(struct mmc *mmc)__attribute__((weak,
alias("__board_mmc_getcd")));
+#ifdef CONFIG_MMC_BOUNCE_BUFFER
+static int mmc_bounce_need_bounce(struct mmc_data *orig)
+{
+ ulong addr, len;
+
+ if (orig->flags & MMC_DATA_READ)
+ addr = (ulong)orig->dest;
+ else
+ addr = (ulong)orig->src;
+
+ if (addr % ARCH_DMA_MINALIGN) {
+ debug("MMC: Unaligned data destination address %08lx!\n", addr);
+ return 1;
+ }
+
+ len = (ulong)(orig->blocksize * orig->blocks);
+ if (len % ARCH_DMA_MINALIGN) {
+ debug("MMC: Unaligned data destination length %08lx!\n", len);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int mmc_bounce_buffer_start(struct mmc_data *backup,
+ struct mmc_data *orig)
+{
+ ulong origlen, len;
+ void *buffer;
+
+ if (!orig)
+ return 0;
+
+ if (!mmc_bounce_need_bounce(orig))
+ return 0;
+
+ memcpy(backup, orig, sizeof(struct mmc_data));
+
+ origlen = orig->blocksize * orig->blocks;
+ len = roundup(origlen, ARCH_DMA_MINALIGN);
+ buffer = memalign(ARCH_DMA_MINALIGN, len);
+ if (!buffer) {
+ puts("MMC: Error allocating MMC bounce buffer!\n");
+ return 1;
+ }
+
+ if (orig->flags & MMC_DATA_READ) {
+ orig->dest = buffer;
+ } else {
+ memcpy(buffer, orig->src, origlen);
+ orig->src = buffer;
+ }
+
+ return 0;
+}
+
+static void mmc_bounce_buffer_stop(struct mmc_data *backup,
+ struct mmc_data *orig)
+{
+ ulong len;
+
+ if (!orig)
+ return;
+
+ if (!mmc_bounce_need_bounce(backup))
+ return;
+
+ if (backup->flags & MMC_DATA_READ) {
+ len = backup->blocksize * backup->blocks;
+ memcpy(backup->dest, orig->dest, len);
+ free(orig->dest);
+ orig->dest = backup->dest;
+ } else {
+ free((void *)orig->src);
+ orig->src = backup->src;
+ }
+
+ return;
+
+}
+#else
+static inline int mmc_bounce_buffer_start(struct mmc_data *backup,
+ struct mmc_data *orig) { }
+static inline void mmc_bounce_buffer_stop(struct mmc_data *backup,
+ struct mmc_data *orig) { }
+#endif
+
int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
-#ifdef CONFIG_MMC_TRACE
+ struct mmc_data backup;
int ret;
+
+ memset(&backup, 0, sizeof(backup));
+
+ ret = mmc_bounce_buffer_start(&backup, data);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_MMC_TRACE
int i;
u8 *ptr;
@@ -99,10 +194,11 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
printf("\t\tERROR MMC rsp not supported\n");
break;
}
- return ret;
#else
- return mmc->send_cmd(mmc, cmd, data);
+ ret = mmc->send_cmd(mmc, cmd, data);
#endif
+ mmc_bounce_buffer_stop(&backup, data);
+ return ret;
}
int mmc_send_status(struct mmc *mmc, int timeout)
diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c
index 5f87a1efd6..e8bad9dc75 100644
--- a/drivers/mmc/mxsmmc.c
+++ b/drivers/mmc/mxsmmc.c
@@ -41,6 +41,7 @@
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
+#include <asm/arch/dma.h>
struct mxsmmc_priv {
int id;
@@ -49,6 +50,7 @@ struct mxsmmc_priv {
uint32_t *clkctrl_ssp;
uint32_t buswidth;
int (*mmc_is_wp)(int);
+ struct mxs_dma_desc *desc;
};
#define MXSMMC_MAX_TIMEOUT 10000
@@ -64,8 +66,7 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
struct mx28_ssp_regs *ssp_regs = priv->regs;
uint32_t reg;
int timeout;
- uint32_t data_count;
- uint32_t *data_ptr;
+ uint32_t data_count, cache_data_count;
uint32_t ctrl0;
debug("MMC%d: CMD%d\n", mmc->block_dev.dev, cmd->cmdidx);
@@ -183,40 +184,41 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
if (!data)
return 0;
- /* Process the data */
data_count = data->blocksize * data->blocks;
- timeout = MXSMMC_MAX_TIMEOUT;
+
+ if (data_count % ARCH_DMA_MINALIGN)
+ cache_data_count = roundup(data_count, ARCH_DMA_MINALIGN);
+ else
+ cache_data_count = data_count;
+
if (data->flags & MMC_DATA_READ) {
- data_ptr = (uint32_t *)data->dest;
- while (data_count && --timeout) {
- reg = readl(&ssp_regs->hw_ssp_status);
- if (!(reg & SSP_STATUS_FIFO_EMPTY)) {
- *data_ptr++ = readl(&ssp_regs->hw_ssp_data);
- data_count -= 4;
- timeout = MXSMMC_MAX_TIMEOUT;
- } else
- udelay(1000);
- }
+ priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE;
+ priv->desc->cmd.address = (dma_addr_t)data->dest;
} else {
- data_ptr = (uint32_t *)data->src;
- timeout *= 100;
- while (data_count && --timeout) {
- reg = readl(&ssp_regs->hw_ssp_status);
- if (!(reg & SSP_STATUS_FIFO_FULL)) {
- writel(*data_ptr++, &ssp_regs->hw_ssp_data);
- data_count -= 4;
- timeout = MXSMMC_MAX_TIMEOUT;
- } else
- udelay(1000);
- }
+ priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ;
+ priv->desc->cmd.address = (dma_addr_t)data->src;
+
+ /* Flush data to DRAM so DMA can pick them up */
+ flush_dcache_range((uint32_t)priv->desc->cmd.address,
+ (uint32_t)(priv->desc->cmd.address + cache_data_count));
}
- if (!timeout) {
- printf("MMC%d: Data timeout with command %d (status 0x%08x)!\n",
- mmc->block_dev.dev, cmd->cmdidx, reg);
+ priv->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM |
+ (data_count << MXS_DMA_DESC_BYTES_OFFSET);
+
+
+ mxs_dma_desc_append(MXS_DMA_CHANNEL_AHB_APBH_SSP0, priv->desc);
+ if (mxs_dma_go(MXS_DMA_CHANNEL_AHB_APBH_SSP0)) {
+ printf("MMC%d: DMA transfer failed\n", mmc->block_dev.dev);
return COMM_ERR;
}
+ /* The data arrived into DRAM, invalidate cache over them */
+ if (data->flags & MMC_DATA_READ) {
+ invalidate_dcache_range((uint32_t)priv->desc->cmd.address,
+ (uint32_t)(priv->desc->cmd.address + cache_data_count));
+ }
+
/* Check data errors */
reg = readl(&ssp_regs->hw_ssp_status);
if (reg &
@@ -270,7 +272,8 @@ static int mxsmmc_init(struct mmc *mmc)
/* 8 bits word length in MMC mode */
clrsetbits_le32(&ssp_regs->hw_ssp_ctrl1,
SSP_CTRL1_SSP_MODE_MASK | SSP_CTRL1_WORD_LENGTH_MASK,
- SSP_CTRL1_SSP_MODE_SD_MMC | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS);
+ SSP_CTRL1_SSP_MODE_SD_MMC | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS |
+ SSP_CTRL1_DMA_ENABLE);
/* Set initial bit clock 400 KHz */
mx28_set_ssp_busclock(priv->id, 400);
@@ -300,6 +303,13 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))
return -ENOMEM;
}
+ priv->desc = mxs_dma_desc_alloc();
+ if (!priv->desc) {
+ free(priv);
+ free(mmc);
+ return -ENOMEM;
+ }
+
priv->mmc_is_wp = wp;
priv->id = id;
switch (id) {
@@ -345,7 +355,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))
*/
mmc->f_min = 400000;
mmc->f_max = mxc_get_clock(MXC_SSP0_CLK + id) * 1000 / 2;
- mmc->b_max = 0;
+ mmc->b_max = 0x40;
mmc_register(mmc);
return 0;
diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c
index ce2a326873..4b1297a2fd 100644
--- a/drivers/mtd/nand/mxs_nand.c
+++ b/drivers/mtd/nand/mxs_nand.c
@@ -50,6 +50,7 @@ struct mxs_nand_info {
int cur_chip;
uint32_t cmd_queue_len;
+ uint32_t data_buf_size;
uint8_t *cmd_buf;
uint8_t *data_buf;
@@ -73,6 +74,36 @@ struct mxs_nand_info {
struct nand_ecclayout fake_ecc_layout;
+/*
+ * Cache management functions
+ */
+#ifndef CONFIG_SYS_DCACHE_OFF
+static void mxs_nand_flush_data_buf(struct mxs_nand_info *info)
+{
+ uint32_t addr = (uint32_t)info->data_buf;
+
+ flush_dcache_range(addr, addr + info->data_buf_size);
+}
+
+static void mxs_nand_inval_data_buf(struct mxs_nand_info *info)
+{
+ uint32_t addr = (uint32_t)info->data_buf;
+
+ invalidate_dcache_range(addr, addr + info->data_buf_size);
+}
+
+static void mxs_nand_flush_cmd_buf(struct mxs_nand_info *info)
+{
+ uint32_t addr = (uint32_t)info->cmd_buf;
+
+ flush_dcache_range(addr, addr + MXS_NAND_COMMAND_BUFFER_SIZE);
+}
+#else
+static inline void mxs_nand_flush_data_buf(struct mxs_nand_info *info) {}
+static inline void mxs_nand_inval_data_buf(struct mxs_nand_info *info) {}
+static inline void mxs_nand_flush_cmd_buf(struct mxs_nand_info *info) {}
+#endif
+
static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info)
{
struct mxs_dma_desc *desc;
@@ -286,6 +317,9 @@ static void mxs_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
mxs_dma_desc_append(channel, d);
+ /* Flush caches */
+ mxs_nand_flush_cmd_buf(nand_info);
+
/* Execute the DMA chain. */
ret = mxs_dma_go(channel);
if (ret)
@@ -435,6 +469,9 @@ static void mxs_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int length)
goto rtn;
}
+ /* Invalidate caches */
+ mxs_nand_inval_data_buf(nand_info);
+
memcpy(buf, nand_info->data_buf, length);
rtn:
@@ -484,6 +521,9 @@ static void mxs_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
mxs_dma_desc_append(channel, d);
+ /* Flush caches */
+ mxs_nand_flush_data_buf(nand_info);
+
/* Execute the DMA chain. */
ret = mxs_dma_go(channel);
if (ret)
@@ -600,6 +640,9 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
goto rtn;
}
+ /* Invalidate caches */
+ mxs_nand_inval_data_buf(nand_info);
+
/* Read DMA completed, now do the mark swapping. */
mxs_nand_swap_block_mark(mtd, nand_info->data_buf, nand_info->oob_buf);
@@ -687,6 +730,9 @@ static void mxs_nand_ecc_write_page(struct mtd_info *mtd,
mxs_dma_desc_append(channel, d);
+ /* Flush caches */
+ mxs_nand_flush_data_buf(nand_info);
+
/* Execute the DMA chain. */
ret = mxs_dma_go(channel);
if (ret) {
@@ -978,18 +1024,19 @@ int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info)
uint8_t *buf;
const int size = NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE;
+ nand_info->data_buf_size = roundup(size, MXS_DMA_ALIGNMENT);
+
/* DMA buffers */
- buf = memalign(MXS_DMA_ALIGNMENT, size);
+ buf = memalign(MXS_DMA_ALIGNMENT, nand_info->data_buf_size);
if (!buf) {
printf("MXS NAND: Error allocating DMA buffers\n");
return -ENOMEM;
}
- memset(buf, 0, size);
+ memset(buf, 0, nand_info->data_buf_size);
nand_info->data_buf = buf;
nand_info->oob_buf = buf + NAND_MAX_PAGESIZE;
-
/* Command buffers */
nand_info->cmd_buf = memalign(MXS_DMA_ALIGNMENT,
MXS_NAND_COMMAND_BUFFER_SIZE);
diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c
index 1dfe074e1e..ca868efb9f 100644
--- a/drivers/mtd/nand/omap_gpmc.c
+++ b/drivers/mtd/nand/omap_gpmc.c
@@ -27,10 +27,12 @@
#include <asm/arch/mem.h>
#include <asm/arch/omap_gpmc.h>
#include <linux/mtd/nand_ecc.h>
+#include <linux/compiler.h>
#include <nand.h>
static uint8_t cs;
-static struct nand_ecclayout hw_nand_oob = GPMC_NAND_HW_ECC_LAYOUT;
+static __maybe_unused struct nand_ecclayout hw_nand_oob =
+ GPMC_NAND_HW_ECC_LAYOUT;
/*
* omap_nand_hwcontrol - Set the address pointers corretly for the
@@ -75,7 +77,7 @@ int omap_spl_dev_ready(struct mtd_info *mtd)
* @mtd: MTD device structure
*
*/
-static void omap_hwecc_init(struct nand_chip *chip)
+static void __maybe_unused omap_hwecc_init(struct nand_chip *chip)
{
/*
* Init ECC Control Register
@@ -113,7 +115,7 @@ static uint32_t gen_true_ecc(uint8_t *ecc_buf)
*
* @return 0 if data is OK or corrected, else returns -1
*/
-static int omap_correct_data(struct mtd_info *mtd, uint8_t *dat,
+static int __maybe_unused omap_correct_data(struct mtd_info *mtd, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
uint32_t orig_ecc, new_ecc, res, hm;
@@ -179,8 +181,8 @@ static int omap_correct_data(struct mtd_info *mtd, uint8_t *dat,
* @dat: unused
* @ecc_code: ecc_code buffer
*/
-static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
- uint8_t *ecc_code)
+static int __maybe_unused omap_calculate_ecc(struct mtd_info *mtd,
+ const uint8_t *dat, uint8_t *ecc_code)
{
u_int32_t val;
@@ -205,7 +207,7 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
* @mtd: MTD device structure
* @mode: Read/Write mode
*/
-static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
+static void __maybe_unused omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
{
struct nand_chip *chip = mtd->priv;
uint32_t val, dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1;
diff --git a/drivers/net/calxedaxgmac.c b/drivers/net/calxedaxgmac.c
index 01b2eeeaee..00e26c2adc 100644
--- a/drivers/net/calxedaxgmac.c
+++ b/drivers/net/calxedaxgmac.c
@@ -16,6 +16,7 @@
*/
#include <common.h>
#include <malloc.h>
+#include <linux/compiler.h>
#include <linux/err.h>
#include <asm/io.h>
diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c
index 1fdd071e38..d8db9f0c6e 100644
--- a/drivers/net/fec_mxc.c
+++ b/drivers/net/fec_mxc.c
@@ -38,16 +38,28 @@ DECLARE_GLOBAL_DATA_PTR;
#error "CONFIG_MII has to be defined!"
#endif
-#ifndef CONFIG_FEC_XCV_TYPE
-#define CONFIG_FEC_XCV_TYPE MII100
+#ifndef CONFIG_FEC_XCV_TYPE
+#define CONFIG_FEC_XCV_TYPE MII100
#endif
/*
* The i.MX28 operates with packets in big endian. We need to swap them before
* sending and after receiving.
*/
-#ifdef CONFIG_MX28
-#define CONFIG_FEC_MXC_SWAP_PACKET
+#ifdef CONFIG_MX28
+#define CONFIG_FEC_MXC_SWAP_PACKET
+#endif
+
+#define RXDESC_PER_CACHELINE (ARCH_DMA_MINALIGN/sizeof(struct fec_bd))
+
+/* Check various alignment issues at compile time */
+#if ((ARCH_DMA_MINALIGN < 16) || (ARCH_DMA_MINALIGN % 16 != 0))
+#error "ARCH_DMA_MINALIGN must be multiple of 16!"
+#endif
+
+#if ((PKTALIGN < ARCH_DMA_MINALIGN) || \
+ (PKTALIGN % ARCH_DMA_MINALIGN != 0))
+#error "PKTALIGN must be multiple of ARCH_DMA_MINALIGN!"
#endif
#undef DEBUG
@@ -59,7 +71,7 @@ struct nbuf {
uint8_t head[16]; /**< MAC header(6 + 6 + 2) + 2(aligned) */
};
-#ifdef CONFIG_FEC_MXC_SWAP_PACKET
+#ifdef CONFIG_FEC_MXC_SWAP_PACKET
static void swap_packet(uint32_t *packet, int length)
{
int i;
@@ -259,43 +271,52 @@ static int fec_tx_task_disable(struct fec_priv *fec)
* Initialize receive task's buffer descriptors
* @param[in] fec all we know about the device yet
* @param[in] count receive buffer count to be allocated
- * @param[in] size size of each receive buffer
+ * @param[in] dsize desired size of each receive buffer
* @return 0 on success
*
* For this task we need additional memory for the data buffers. And each
* data buffer requires some alignment. Thy must be aligned to a specific
- * boundary each (DB_DATA_ALIGNMENT).
+ * boundary each.
*/
-static int fec_rbd_init(struct fec_priv *fec, int count, int size)
+static int fec_rbd_init(struct fec_priv *fec, int count, int dsize)
{
- int ix;
- uint32_t p = 0;
-
- /* reserve data memory and consider alignment */
- if (fec->rdb_ptr == NULL)
- fec->rdb_ptr = malloc(size * count + DB_DATA_ALIGNMENT);
- p = (uint32_t)fec->rdb_ptr;
- if (!p) {
- puts("fec_mxc: not enough malloc memory\n");
- return -ENOMEM;
- }
- memset((void *)p, 0, size * count + DB_DATA_ALIGNMENT);
- p += DB_DATA_ALIGNMENT-1;
- p &= ~(DB_DATA_ALIGNMENT-1);
-
- for (ix = 0; ix < count; ix++) {
- writel(p, &fec->rbd_base[ix].data_pointer);
- p += size;
- writew(FEC_RBD_EMPTY, &fec->rbd_base[ix].status);
- writew(0, &fec->rbd_base[ix].data_length);
- }
+ uint32_t size;
+ int i;
+
/*
- * mark the last RBD to close the ring
+ * Allocate memory for the buffers. This allocation respects the
+ * alignment
*/
- writew(FEC_RBD_WRAP | FEC_RBD_EMPTY, &fec->rbd_base[ix - 1].status);
+ size = roundup(dsize, ARCH_DMA_MINALIGN);
+ for (i = 0; i < count; i++) {
+ uint32_t data_ptr = readl(&fec->rbd_base[i].data_pointer);
+ if (data_ptr == 0) {
+ uint8_t *data = memalign(ARCH_DMA_MINALIGN,
+ size);
+ if (!data) {
+ printf("%s: error allocating rxbuf %d\n",
+ __func__, i);
+ goto err;
+ }
+ writel((uint32_t)data, &fec->rbd_base[i].data_pointer);
+ } /* needs allocation */
+ writew(FEC_RBD_EMPTY, &fec->rbd_base[i].status);
+ writew(0, &fec->rbd_base[i].data_length);
+ }
+
+ /* Mark the last RBD to close the ring. */
+ writew(FEC_RBD_WRAP | FEC_RBD_EMPTY, &fec->rbd_base[i - 1].status);
fec->rbd_index = 0;
return 0;
+
+err:
+ for (; i >= 0; i--) {
+ uint32_t data_ptr = readl(&fec->rbd_base[i].data_pointer);
+ free((void *)data_ptr);
+ }
+
+ return -ENOMEM;
}
/**
@@ -312,9 +333,13 @@ static int fec_rbd_init(struct fec_priv *fec, int count, int size)
*/
static void fec_tbd_init(struct fec_priv *fec)
{
+ unsigned addr = (unsigned)fec->tbd_base;
+ unsigned size = roundup(2 * sizeof(struct fec_bd),
+ ARCH_DMA_MINALIGN);
writew(0x0000, &fec->tbd_base[0].status);
writew(FEC_TBD_WRAP, &fec->tbd_base[1].status);
fec->tbd_index = 0;
+ flush_dcache_range(addr, addr+size);
}
/**
@@ -324,16 +349,10 @@ static void fec_tbd_init(struct fec_priv *fec)
*/
static void fec_rbd_clean(int last, struct fec_bd *pRbd)
{
- /*
- * Reset buffer descriptor as empty
- */
+ unsigned short flags = FEC_RBD_EMPTY;
if (last)
- writew(FEC_RBD_WRAP | FEC_RBD_EMPTY, &pRbd->status);
- else
- writew(FEC_RBD_EMPTY, &pRbd->status);
- /*
- * no data in it
- */
+ flags |= FEC_RBD_WRAP;
+ writew(flags, &pRbd->status);
writew(0, &pRbd->data_length);
}
@@ -387,12 +406,25 @@ static int fec_open(struct eth_device *edev)
{
struct fec_priv *fec = (struct fec_priv *)edev->priv;
int speed;
+ uint32_t addr, size;
+ int i;
debug("fec_open: fec_open(dev)\n");
/* full-duplex, heartbeat disabled */
writel(1 << 2, &fec->eth->x_cntrl);
fec->rbd_index = 0;
+ /* Invalidate all descriptors */
+ for (i = 0; i < FEC_RBD_NUM - 1; i++)
+ fec_rbd_clean(0, &fec->rbd_base[i]);
+ fec_rbd_clean(1, &fec->rbd_base[i]);
+
+ /* Flush the descriptors into RAM */
+ size = roundup(FEC_RBD_NUM * sizeof(struct fec_bd),
+ ARCH_DMA_MINALIGN);
+ addr = (uint32_t)fec->rbd_base;
+ flush_dcache_range(addr, addr + size);
+
#ifdef FEC_QUIRK_ENET_MAC
/* Enable ENET HW endian SWAP */
writel(readl(&fec->eth->ecntrl) | FEC_ECNTRL_DBSWAP,
@@ -478,38 +510,55 @@ static int fec_open(struct eth_device *edev)
static int fec_init(struct eth_device *dev, bd_t* bd)
{
- uint32_t base;
struct fec_priv *fec = (struct fec_priv *)dev->priv;
uint32_t mib_ptr = (uint32_t)&fec->eth->rmon_t_drop;
uint32_t rcntrl;
- int i;
+ uint32_t size;
+ int i, ret;
/* Initialize MAC address */
fec_set_hwaddr(dev);
/*
- * reserve memory for both buffer descriptor chains at once
- * Datasheet forces the startaddress of each chain is 16 byte
- * aligned
+ * Allocate transmit descriptors, there are two in total. This
+ * allocation respects cache alignment.
*/
- if (fec->base_ptr == NULL)
- fec->base_ptr = malloc((2 + FEC_RBD_NUM) *
- sizeof(struct fec_bd) + DB_ALIGNMENT);
- base = (uint32_t)fec->base_ptr;
- if (!base) {
- puts("fec_mxc: not enough malloc memory\n");
- return -ENOMEM;
+ if (!fec->tbd_base) {
+ size = roundup(2 * sizeof(struct fec_bd),
+ ARCH_DMA_MINALIGN);
+ fec->tbd_base = memalign(ARCH_DMA_MINALIGN, size);
+ if (!fec->tbd_base) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+ memset(fec->tbd_base, 0, size);
+ fec_tbd_init(fec);
+ flush_dcache_range((unsigned)fec->tbd_base, size);
}
- memset((void *)base, 0, (2 + FEC_RBD_NUM) *
- sizeof(struct fec_bd) + DB_ALIGNMENT);
- base += (DB_ALIGNMENT-1);
- base &= ~(DB_ALIGNMENT-1);
-
- fec->rbd_base = (struct fec_bd *)base;
- base += FEC_RBD_NUM * sizeof(struct fec_bd);
-
- fec->tbd_base = (struct fec_bd *)base;
+ /*
+ * Allocate receive descriptors. This allocation respects cache
+ * alignment.
+ */
+ if (!fec->rbd_base) {
+ size = roundup(FEC_RBD_NUM * sizeof(struct fec_bd),
+ ARCH_DMA_MINALIGN);
+ fec->rbd_base = memalign(ARCH_DMA_MINALIGN, size);
+ if (!fec->rbd_base) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+ memset(fec->rbd_base, 0, size);
+ /*
+ * Initialize RxBD ring
+ */
+ if (fec_rbd_init(fec, FEC_RBD_NUM, FEC_MAX_PKT_SIZE) < 0) {
+ ret = -ENOMEM;
+ goto err3;
+ }
+ flush_dcache_range((unsigned)fec->rbd_base,
+ (unsigned)fec->rbd_base + size);
+ }
/*
* Set interrupt mask register
@@ -566,23 +615,19 @@ static int fec_init(struct eth_device *dev, bd_t* bd)
writel((uint32_t)fec->tbd_base, &fec->eth->etdsr);
writel((uint32_t)fec->rbd_base, &fec->eth->erdsr);
- /*
- * Initialize RxBD/TxBD rings
- */
- if (fec_rbd_init(fec, FEC_RBD_NUM, FEC_MAX_PKT_SIZE) < 0) {
- free(fec->base_ptr);
- fec->base_ptr = NULL;
- return -ENOMEM;
- }
- fec_tbd_init(fec);
-
-
#ifndef CONFIG_PHYLIB
if (fec->xcv_type != SEVENWIRE)
miiphy_restart_aneg(dev);
#endif
fec_open(dev);
return 0;
+
+err3:
+ free(fec->rbd_base);
+err2:
+ free(fec->tbd_base);
+err1:
+ return ret;
}
/**
@@ -631,9 +676,11 @@ static void fec_halt(struct eth_device *dev)
* @param[in] length Data count in bytes
* @return 0 on success
*/
-static int fec_send(struct eth_device *dev, volatile void* packet, int length)
+static int fec_send(struct eth_device *dev, volatile void *packet, int length)
{
unsigned int status;
+ uint32_t size;
+ uint32_t addr;
/*
* This routine transmits one frame. This routine only accepts
@@ -650,15 +697,21 @@ static int fec_send(struct eth_device *dev, volatile void* packet, int length)
}
/*
- * Setup the transmit buffer
- * Note: We are always using the first buffer for transmission,
- * the second will be empty and only used to stop the DMA engine
+ * Setup the transmit buffer. We are always using the first buffer for
+ * transmission, the second will be empty and only used to stop the DMA
+ * engine. We also flush the packet to RAM here to avoid cache trouble.
*/
-#ifdef CONFIG_FEC_MXC_SWAP_PACKET
+#ifdef CONFIG_FEC_MXC_SWAP_PACKET
swap_packet((uint32_t *)packet, length);
#endif
+
+ addr = (uint32_t)packet;
+ size = roundup(length, ARCH_DMA_MINALIGN);
+ flush_dcache_range(addr, addr + size);
+
writew(length, &fec->tbd_base[fec->tbd_index].data_length);
- writel((uint32_t)packet, &fec->tbd_base[fec->tbd_index].data_pointer);
+ writel(addr, &fec->tbd_base[fec->tbd_index].data_pointer);
+
/*
* update BD's status now
* This block:
@@ -672,16 +725,30 @@ static int fec_send(struct eth_device *dev, volatile void* packet, int length)
writew(status, &fec->tbd_base[fec->tbd_index].status);
/*
+ * Flush data cache. This code flushes both TX descriptors to RAM.
+ * After this code, the descriptors will be safely in RAM and we
+ * can start DMA.
+ */
+ size = roundup(2 * sizeof(struct fec_bd), ARCH_DMA_MINALIGN);
+ addr = (uint32_t)fec->tbd_base;
+ flush_dcache_range(addr, addr + size);
+
+ /*
* Enable SmartDMA transmit task
*/
fec_tx_task_enable(fec);
/*
- * wait until frame is sent .
+ * Wait until frame is sent. On each turn of the wait cycle, we must
+ * invalidate data cache to see what's really in RAM. Also, we need
+ * barrier here.
*/
+ invalidate_dcache_range(addr, addr + size);
while (readw(&fec->tbd_base[fec->tbd_index].status) & FEC_TBD_READY) {
udelay(1);
+ invalidate_dcache_range(addr, addr + size);
}
+
debug("fec_send: status 0x%x index %d\n",
readw(&fec->tbd_base[fec->tbd_index].status),
fec->tbd_index);
@@ -707,6 +774,8 @@ static int fec_recv(struct eth_device *dev)
int frame_length, len = 0;
struct nbuf *frame;
uint16_t bd_status;
+ uint32_t addr, size;
+ int i;
uchar buff[FEC_MAX_PKT_SIZE];
/*
@@ -737,8 +806,23 @@ static int fec_recv(struct eth_device *dev)
}
/*
- * ensure reading the right buffer status
+ * Read the buffer status. Before the status can be read, the data cache
+ * must be invalidated, because the data in RAM might have been changed
+ * by DMA. The descriptors are properly aligned to cachelines so there's
+ * no need to worry they'd overlap.
+ *
+ * WARNING: By invalidating the descriptor here, we also invalidate
+ * the descriptors surrounding this one. Therefore we can NOT change the
+ * contents of this descriptor nor the surrounding ones. The problem is
+ * that in order to mark the descriptor as processed, we need to change
+ * the descriptor. The solution is to mark the whole cache line when all
+ * descriptors in the cache line are processed.
*/
+ addr = (uint32_t)rbd;
+ addr &= ~(ARCH_DMA_MINALIGN - 1);
+ size = roundup(sizeof(struct fec_bd), ARCH_DMA_MINALIGN);
+ invalidate_dcache_range(addr, addr + size);
+
bd_status = readw(&rbd->status);
debug("fec_recv: status 0x%x\n", bd_status);
@@ -751,9 +835,16 @@ static int fec_recv(struct eth_device *dev)
frame = (struct nbuf *)readl(&rbd->data_pointer);
frame_length = readw(&rbd->data_length) - 4;
/*
+ * Invalidate data cache over the buffer
+ */
+ addr = (uint32_t)frame;
+ size = roundup(frame_length, ARCH_DMA_MINALIGN);
+ invalidate_dcache_range(addr, addr + size);
+
+ /*
* Fill the buffer and pass it to upper layers
*/
-#ifdef CONFIG_FEC_MXC_SWAP_PACKET
+#ifdef CONFIG_FEC_MXC_SWAP_PACKET
swap_packet((uint32_t *)frame->data, frame_length);
#endif
memcpy(buff, frame->data, frame_length);
@@ -765,11 +856,25 @@ static int fec_recv(struct eth_device *dev)
(ulong)rbd->data_pointer,
bd_status);
}
+
/*
- * free the current buffer, restart the engine
- * and move forward to the next buffer
+ * Free the current buffer, restart the engine and move forward
+ * to the next buffer. Here we check if the whole cacheline of
+ * descriptors was already processed and if so, we mark it free
+ * as whole.
*/
- fec_rbd_clean(fec->rbd_index == (FEC_RBD_NUM - 1) ? 1 : 0, rbd);
+ size = RXDESC_PER_CACHELINE - 1;
+ if ((fec->rbd_index & size) == size) {
+ i = fec->rbd_index - size;
+ addr = (uint32_t)&fec->rbd_base[i];
+ for (; i <= fec->rbd_index ; i++) {
+ fec_rbd_clean(i == (FEC_RBD_NUM - 1),
+ &fec->rbd_base[i]);
+ }
+ flush_dcache_range(addr,
+ addr + ARCH_DMA_MINALIGN);
+ }
+
fec_rx_task_enable(fec);
fec->rbd_index = (fec->rbd_index + 1) % FEC_RBD_NUM;
}
@@ -866,7 +971,7 @@ static int fec_probe(bd_t *bd, int dev_id, int phy_id, uint32_t base_addr)
bus->read = fec_phy_read;
bus->write = fec_phy_write;
sprintf(bus->name, edev->name);
-#ifdef CONFIG_MX28
+#ifdef CONFIG_MX28
/*
* The i.MX28 has two ethernet interfaces, but they are not equal.
* Only the first one can access the MDIO bus.
@@ -901,7 +1006,7 @@ err1:
return ret;
}
-#ifndef CONFIG_FEC_MXC_MULTI
+#ifndef CONFIG_FEC_MXC_MULTI
int fecmxc_initialize(bd_t *bd)
{
int lout = 1;
diff --git a/drivers/net/fec_mxc.h b/drivers/net/fec_mxc.h
index 2eb78037fc..852b2e07e7 100644
--- a/drivers/net/fec_mxc.h
+++ b/drivers/net/fec_mxc.h
@@ -234,22 +234,6 @@ struct ethernet_regs {
#endif
/**
- * @brief Descriptor buffer alignment
- *
- * i.MX27 requires a 16 byte alignment (but for the first element only)
- */
-#define DB_ALIGNMENT 16
-
-/**
- * @brief Data buffer alignment
- *
- * i.MX27 requires a four byte alignment for transmit and 16 bits
- * alignment for receive so take 16
- * Note: Valid for member data_pointer in struct buffer_descriptor
- */
-#define DB_DATA_ALIGNMENT 16
-
-/**
* @brief Receive & Transmit Buffer Descriptor definitions
*
* Note: The first BD must be aligned (see DB_ALIGNMENT)
@@ -282,8 +266,7 @@ struct fec_priv {
struct fec_bd *tbd_base; /* TBD ring */
int tbd_index; /* next transmit BD to write */
bd_t *bd;
- void *rdb_ptr;
- void *base_ptr;
+ uint8_t *tdb_ptr;
int dev_id;
int phy_id;
struct mii_dev *bus;
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index 03495087dc..aa477d4f93 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -27,7 +27,6 @@ LIB := $(obj)libpcmcia.o
COBJS-$(CONFIG_I82365) += i82365.o
COBJS-$(CONFIG_8xx) += mpc8xx_pcmcia.o
-COBJS-$(CONFIG_PXA_PCMCIA) += pxa_pcmcia.o
COBJS-y += rpx_pcmcia.o
COBJS-$(CONFIG_IDE_TI_CARDBUS) += ti_pci1410a.o
COBJS-y += tqm8xx_pcmcia.o
diff --git a/drivers/pcmcia/pxa_pcmcia.c b/drivers/pcmcia/pxa_pcmcia.c
deleted file mode 100644
index d06ab746c8..0000000000
--- a/drivers/pcmcia/pxa_pcmcia.c
+++ /dev/null
@@ -1,93 +0,0 @@
-#include <common.h>
-#include <config.h>
-
-#include <pcmcia.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/io.h>
-
-static inline void msWait(unsigned msVal)
-{
- udelay(msVal*1000);
-}
-
-int pcmcia_on (void)
-{
- unsigned int reg_arr[] = {
- 0x48000028, CONFIG_SYS_MCMEM0_VAL,
- 0x4800002c, CONFIG_SYS_MCMEM1_VAL,
- 0x48000030, CONFIG_SYS_MCATT0_VAL,
- 0x48000034, CONFIG_SYS_MCATT1_VAL,
- 0x48000038, CONFIG_SYS_MCIO0_VAL,
- 0x4800003c, CONFIG_SYS_MCIO1_VAL,
-
- 0, 0
- };
- int i, rc;
-
-#ifdef CONFIG_EXADRON1
- int cardDetect;
- volatile unsigned int *v_pBCRReg =
- (volatile unsigned int *) 0x08000000;
-#endif
-
- debug ("%s\n", __FUNCTION__);
-
- i = 0;
- while (reg_arr[i]) {
- (*(volatile unsigned int *) reg_arr[i]) |= reg_arr[i + 1];
- i += 2;
- }
- udelay (1000);
-
- debug ("%s: programmed mem controller \n", __FUNCTION__);
-
-#ifdef CONFIG_EXADRON1
-
-/*define useful BCR masks */
-#define BCR_CF_INIT_VAL 0x00007230
-#define BCR_CF_PWRON_BUSOFF_RESETOFF_VAL 0x00007231
-#define BCR_CF_PWRON_BUSOFF_RESETON_VAL 0x00007233
-#define BCR_CF_PWRON_BUSON_RESETON_VAL 0x00007213
-#define BCR_CF_PWRON_BUSON_RESETOFF_VAL 0x00007211
-
- /* we see from the GPIO bit if the card is present */
- cardDetect = !(GPLR0 & GPIO_bit (14));
-
- if (cardDetect) {
- printf ("No PCMCIA card found!\n");
- }
-
- /* reset the card via the BCR line */
- *v_pBCRReg = (unsigned) BCR_CF_INIT_VAL;
- msWait (500);
-
- *v_pBCRReg = (unsigned) BCR_CF_PWRON_BUSOFF_RESETOFF_VAL;
- msWait (500);
-
- *v_pBCRReg = (unsigned) BCR_CF_PWRON_BUSOFF_RESETON_VAL;
- msWait (500);
-
- *v_pBCRReg = (unsigned) BCR_CF_PWRON_BUSON_RESETON_VAL;
- msWait (500);
-
- *v_pBCRReg = (unsigned) BCR_CF_PWRON_BUSON_RESETOFF_VAL;
- msWait (1500);
-
- /* enable address bus */
- GPCR1 = 0x01;
- /* and the first CF slot */
- MECR = 0x00000002;
-
-#endif /* EXADRON 1 */
-
- rc = check_ide_device (0); /* use just slot 0 */
-
- return rc;
-}
-
-#if defined(CONFIG_CMD_PCMCIA)
-int pcmcia_off (void)
-{
- return 0;
-}
-#endif
diff --git a/drivers/power/twl4030.c b/drivers/power/twl4030.c
index 4a4ddeb91f..36b2144947 100644
--- a/drivers/power/twl4030.c
+++ b/drivers/power/twl4030.c
@@ -65,13 +65,23 @@ void twl4030_power_reset_init(void)
void twl4030_pmrecv_vsel_cfg(u8 vsel_reg, u8 vsel_val,
u8 dev_grp, u8 dev_grp_sel)
{
- /* Select the Device Group */
- twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, dev_grp_sel,
- dev_grp);
+ int ret;
/* Select the Voltage */
- twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, vsel_val,
+ ret = twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, vsel_val,
vsel_reg);
+ if (ret != 0) {
+ printf("Could could not write vsel to reg %02x (%d)\n",
+ vsel_reg, ret);
+ return;
+ }
+
+ /* Select the Device Group (enable the supply if dev_grp_sel != 0) */
+ ret = twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, dev_grp_sel,
+ dev_grp);
+ if (ret != 0)
+ printf("Could could not write grp_sel to reg %02x (%d)\n",
+ dev_grp, ret);
}
void twl4030_power_init(void)
diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c
index adb9ca8ec8..4e6f14ee07 100644
--- a/drivers/spi/mxs_spi.c
+++ b/drivers/spi/mxs_spi.c
@@ -162,7 +162,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
if (mx28_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg,
SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
printf("MXS SPI: Timeout waiting for start\n");
- return -1;
+ return -ETIMEDOUT;
}
if (tx)
@@ -174,7 +174,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_status_reg,
SSP_STATUS_FIFO_EMPTY, MXS_SPI_MAX_TIMEOUT)) {
printf("MXS SPI: Timeout waiting for data\n");
- return -1;
+ return -ETIMEDOUT;
}
*rx = readl(&ssp_regs->hw_ssp_data);
@@ -184,7 +184,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg,
SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
printf("MXS SPI: Timeout waiting for finish\n");
- return -1;
+ return -ETIMEDOUT;
}
}
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index af12c0e590..9346c0b5b4 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -297,6 +297,65 @@ int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp,
return 0;
}
+/*McSPI Transmit Receive Mode*/
+int omap3_spi_txrx(struct spi_slave *slave,
+ unsigned int len, const u8 *txp, u8 *rxp, unsigned long flags)
+{
+ struct omap3_spi_slave *ds = to_omap3_spi(slave);
+ int timeout = SPI_WAIT_TIMEOUT;
+ int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
+ int irqstatus = readl(&ds->regs->irqstatus);
+ int i=0;
+
+ /*Enable SPI channel*/
+ if (flags & SPI_XFER_BEGIN)
+ writel(OMAP3_MCSPI_CHCTRL_EN,
+ &ds->regs->channel[ds->slave.cs].chctrl);
+
+ /*set TRANSMIT-RECEIVE Mode*/
+ chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
+ chconf |= OMAP3_MCSPI_CHCONF_FORCE;
+ writel(chconf, &ds->regs->channel[ds->slave.cs].chconf);
+
+ /*Shift in and out 1 byte at time*/
+ for (i=0; i < len; i++){
+ /* Write: wait for TX empty (TXS == 1)*/
+ irqstatus |= (1<< (4*(ds->slave.bus)));
+ while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
+ OMAP3_MCSPI_CHSTAT_TXS)) {
+ if (--timeout <= 0) {
+ printf("SPI TXS timed out, status=0x%08x\n",
+ readl(&ds->regs->channel[ds->slave.cs].chstat));
+ return -1;
+ }
+ }
+ /* Write the data */
+ writel(txp[i], &ds->regs->channel[ds->slave.cs].tx);
+
+ /*Read: wait for RX containing data (RXS == 1)*/
+ while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
+ OMAP3_MCSPI_CHSTAT_RXS)) {
+ if (--timeout <= 0) {
+ printf("SPI RXS timed out, status=0x%08x\n",
+ readl(&ds->regs->channel[ds->slave.cs].chstat));
+ return -1;
+ }
+ }
+ /* Read the data */
+ rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx);
+ }
+
+ /*if transfer must be terminated disable the channel*/
+ if (flags & SPI_XFER_END) {
+ chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
+ writel(chconf, &ds->regs->channel[ds->slave.cs].chconf);
+
+ writel(0, &ds->regs->channel[ds->slave.cs].chctrl);
+ }
+
+ return 0;
+}
+
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
@@ -329,10 +388,11 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
}
ret = 0;
} else {
- if (dout != NULL)
+ if (dout != NULL && din != NULL)
+ ret = omap3_spi_txrx(slave, len, txp, rxp, flags);
+ else if (dout != NULL)
ret = omap3_spi_write(slave, len, txp, flags);
-
- if (din != NULL)
+ else if (din != NULL)
ret = omap3_spi_read(slave, len, rxp, flags);
}
return ret;
diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h
index b8e3a4c44c..0ac801cb25 100644
--- a/drivers/spi/omap3_spi.h
+++ b/drivers/spi/omap3_spi.h
@@ -109,6 +109,8 @@ static inline struct omap3_spi_slave *to_omap3_spi(struct spi_slave *slave)
return container_of(slave, struct omap3_spi_slave, slave);
}
+int omap3_spi_txrx(struct spi_slave *slave, unsigned int len, const u8 *txp,
+ u8 *rxp, unsigned long flags);
int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp,
unsigned long flags);
int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp,
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 5fdc97b8c8..0d4657edf2 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -44,11 +44,13 @@ endif
COBJS-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o
COBJS-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o
COBJS-$(CONFIG_USB_EHCI_MX5) += ehci-mx5.o
+COBJS-$(CONFIG_USB_EHCI_MX6) += ehci-mx6.o
COBJS-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o
COBJS-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o
COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o
COBJS-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o
COBJS-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o
+COBJS-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o
COBJS-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o
COBJS := $(COBJS-y)
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index ef5afc22b0..b6422d7d7a 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -255,6 +255,13 @@ static int ehci_reset(void)
#endif
ehci_writel(reg_ptr, tmp);
}
+
+#ifdef CONFIG_USB_EHCI_TXFIFO_THRESH
+ cmd = ehci_readl(&hcor->or_txfilltuning);
+ cmd &= ~TXFIFO_THRESH(0x3f);
+ cmd |= TXFIFO_THRESH(CONFIG_USB_EHCI_TXFIFO_THRESH);
+ ehci_writel(&hcor->or_txfilltuning, cmd);
+#endif
out:
return ret;
}
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c
new file mode 100644
index 0000000000..5dec673c8e
--- /dev/null
+++ b/drivers/usb/host/ehci-mx6.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ * Copyright (C) 2010 Freescale Semiconductor, 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.
+ */
+
+#include <common.h>
+#include <usb.h>
+#include <errno.h>
+#include <linux/compiler.h>
+#include <usb/ehci-fsl.h>
+#include <asm/io.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/mx6x_pins.h>
+#include <asm/arch/iomux-v3.h>
+
+#include "ehci.h"
+#include "ehci-core.h"
+
+#define USB_OTGREGS_OFFSET 0x000
+#define USB_H1REGS_OFFSET 0x200
+#define USB_H2REGS_OFFSET 0x400
+#define USB_H3REGS_OFFSET 0x600
+#define USB_OTHERREGS_OFFSET 0x800
+
+#define USB_H1_CTRL_OFFSET 0x04
+
+#define USBPHY_CTRL 0x00000030
+#define USBPHY_CTRL_SET 0x00000034
+#define USBPHY_CTRL_CLR 0x00000038
+#define USBPHY_CTRL_TOG 0x0000003c
+
+#define USBPHY_PWD 0x00000000
+#define USBPHY_CTRL_SFTRST 0x80000000
+#define USBPHY_CTRL_CLKGATE 0x40000000
+#define USBPHY_CTRL_ENUTMILEVEL3 0x00008000
+#define USBPHY_CTRL_ENUTMILEVEL2 0x00004000
+
+#define ANADIG_USB2_CHRG_DETECT_EN_B 0x00100000
+#define ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B 0x00080000
+
+#define ANADIG_USB2_PLL_480_CTRL_BYPASS 0x00010000
+#define ANADIG_USB2_PLL_480_CTRL_ENABLE 0x00002000
+#define ANADIG_USB2_PLL_480_CTRL_POWER 0x00001000
+#define ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS 0x00000040
+
+
+#define UCTRL_OVER_CUR_POL (1 << 8) /* OTG Polarity of Overcurrent */
+#define UCTRL_OVER_CUR_DIS (1 << 7) /* Disable OTG Overcurrent Detection */
+
+/* USBCMD */
+#define UH1_USBCMD_OFFSET 0x140
+#define UCMD_RUN_STOP (1 << 0) /* controller run/stop */
+#define UCMD_RESET (1 << 1) /* controller reset */
+
+static void usbh1_internal_phy_clock_gate(int on)
+{
+ void __iomem *phy_reg = (void __iomem *)USB_PHY1_BASE_ADDR;
+
+ phy_reg += on ? USBPHY_CTRL_CLR : USBPHY_CTRL_SET;
+ __raw_writel(USBPHY_CTRL_CLKGATE, phy_reg);
+}
+
+static void usbh1_power_config(void)
+{
+ struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR;
+ /*
+ * Some phy and power's special controls for host1
+ * 1. The external charger detector needs to be disabled
+ * or the signal at DP will be poor
+ * 2. The PLL's power and output to usb for host 1
+ * is totally controlled by IC, so the Software only needs
+ * to enable them at initializtion.
+ */
+ __raw_writel(ANADIG_USB2_CHRG_DETECT_EN_B |
+ ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B,
+ &anatop->usb2_chrg_detect);
+
+ __raw_writel(ANADIG_USB2_PLL_480_CTRL_BYPASS,
+ &anatop->usb2_pll_480_ctrl);
+
+ __raw_writel(ANADIG_USB2_PLL_480_CTRL_ENABLE |
+ ANADIG_USB2_PLL_480_CTRL_POWER |
+ ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS,
+ &anatop->usb2_pll_480_ctrl_set);
+}
+
+static int usbh1_phy_enable(void)
+{
+ void __iomem *phy_reg = (void __iomem *)USB_PHY1_BASE_ADDR;
+ void __iomem *phy_ctrl = (void __iomem *)(phy_reg + USBPHY_CTRL);
+ void __iomem *usb_cmd = (void __iomem *)(USBOH3_USB_BASE_ADDR +
+ USB_H1REGS_OFFSET +
+ UH1_USBCMD_OFFSET);
+ u32 val;
+
+ /* Stop then Reset */
+ val = __raw_readl(usb_cmd);
+ val &= ~UCMD_RUN_STOP;
+ __raw_writel(val, usb_cmd);
+ while (__raw_readl(usb_cmd) & UCMD_RUN_STOP)
+ ;
+
+ val = __raw_readl(usb_cmd);
+ val |= UCMD_RESET;
+ __raw_writel(val, usb_cmd);
+ while (__raw_readl(usb_cmd) & UCMD_RESET)
+ ;
+
+ /* Reset USBPHY module */
+ val = __raw_readl(phy_ctrl);
+ val |= USBPHY_CTRL_SFTRST;
+ __raw_writel(val, phy_ctrl);
+ udelay(10);
+
+ /* Remove CLKGATE and SFTRST */
+ val = __raw_readl(phy_ctrl);
+ val &= ~(USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST);
+ __raw_writel(val, phy_ctrl);
+ udelay(10);
+
+ /* Power up the PHY */
+ __raw_writel(0, phy_reg + USBPHY_PWD);
+ /* enable FS/LS device */
+ val = __raw_readl(phy_reg + USBPHY_CTRL);
+ val |= (USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3);
+ __raw_writel(val, phy_reg + USBPHY_CTRL);
+
+ return 0;
+}
+
+static void usbh1_oc_config(void)
+{
+ void __iomem *usb_base = (void __iomem *)USBOH3_USB_BASE_ADDR;
+ void __iomem *usbother_base = usb_base + USB_OTHERREGS_OFFSET;
+ u32 val;
+
+ val = __raw_readl(usbother_base + USB_H1_CTRL_OFFSET);
+#if CONFIG_MACH_TYPE == MACH_TYPE_MX6Q_ARM2
+ /* mx6qarm2 seems to required a different setting*/
+ val &= ~UCTRL_OVER_CUR_POL;
+#else
+ val |= UCTRL_OVER_CUR_POL;
+#endif
+ __raw_writel(val, usbother_base + USB_H1_CTRL_OFFSET);
+
+ val = __raw_readl(usbother_base + USB_H1_CTRL_OFFSET);
+ val |= UCTRL_OVER_CUR_DIS;
+ __raw_writel(val, usbother_base + USB_H1_CTRL_OFFSET);
+}
+
+int ehci_hcd_init(void)
+{
+ struct usb_ehci *ehci;
+
+ enable_usboh3_clk(1);
+ mdelay(1);
+
+ /* Do board specific initialization */
+ board_ehci_hcd_init(CONFIG_MXC_USB_PORT);
+
+#if CONFIG_MXC_USB_PORT == 1
+ /* USB Host 1 */
+ usbh1_power_config();
+ usbh1_oc_config();
+ usbh1_internal_phy_clock_gate(1);
+ usbh1_phy_enable();
+#else
+#error "MXC USB port not yet supported"
+#endif
+
+ ehci = (struct usb_ehci *)(USBOH3_USB_BASE_ADDR +
+ (0x200 * CONFIG_MXC_USB_PORT));
+ hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
+ hcor = (struct ehci_hcor *)((uint32_t)hccr +
+ HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
+ setbits_le32(&ehci->usbmode, CM_HOST);
+
+ __raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
+ setbits_le32(&ehci->portsc, USB_EN);
+
+ mdelay(10);
+
+ return 0;
+}
+
+int ehci_hcd_stop(void)
+{
+ return 0;
+}
diff --git a/drivers/usb/host/ehci-mxs.c b/drivers/usb/host/ehci-mxs.c
index c795f23bd7..e1bd37ec8d 100644
--- a/drivers/usb/host/ehci-mxs.c
+++ b/drivers/usb/host/ehci-mxs.c
@@ -75,8 +75,8 @@ int ehci_hcd_init(void)
int ret;
uint32_t usb_base, cap_base;
- struct mx28_register *digctl_ctrl =
- (struct mx28_register *)HW_DIGCTL_CTRL;
+ struct mx28_register_32 *digctl_ctrl =
+ (struct mx28_register_32 *)HW_DIGCTL_CTRL;
struct mx28_clkctrl_regs *clkctrl_regs =
(struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
@@ -119,8 +119,8 @@ int ehci_hcd_stop(void)
{
int ret;
uint32_t tmp;
- struct mx28_register *digctl_ctrl =
- (struct mx28_register *)HW_DIGCTL_CTRL;
+ struct mx28_register_32 *digctl_ctrl =
+ (struct mx28_register_32 *)HW_DIGCTL_CTRL;
struct mx28_clkctrl_regs *clkctrl_regs =
(struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
new file mode 100644
index 0000000000..a7e105b992
--- /dev/null
+++ b/drivers/usb/host/ehci-tegra.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009 NVIDIA Corporation
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <common.h>
+#include <usb.h>
+
+#include "ehci.h"
+#include "ehci-core.h"
+
+#include <asm/errno.h>
+#include <asm/arch/usb.h>
+
+
+/*
+ * Create the appropriate control structures to manage
+ * a new EHCI host controller.
+ */
+int ehci_hcd_init(void)
+{
+ u32 our_hccr, our_hcor;
+
+ /*
+ * Select the first port, as we don't have a way of selecting others
+ * yet
+ */
+ if (tegrausb_start_port(0, &our_hccr, &our_hcor))
+ return -1;
+
+ hccr = (struct ehci_hccr *)our_hccr;
+ hcor = (struct ehci_hcor *)our_hcor;
+
+ return 0;
+}
+
+/*
+ * Destroy the appropriate control structures corresponding
+ * the the EHCI host controller.
+ */
+int ehci_hcd_stop(void)
+{
+ tegrausb_stop_port();
+ return 0;
+}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 3d0ad0c852..cc00ce428b 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -80,7 +80,11 @@ struct ehci_hcor {
uint32_t or_ctrldssegment;
uint32_t or_periodiclistbase;
uint32_t or_asynclistaddr;
- uint32_t _reserved_[9];
+ uint32_t _reserved_0_;
+ uint32_t or_burstsize;
+ uint32_t or_txfilltuning;
+#define TXFIFO_THRESH(p) ((p & 0x3f) << 16)
+ uint32_t _reserved_1_[6];
uint32_t or_configflag;
#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */
uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS];