Merge branch 'master' of git://git.denx.de/u-boot-arm
* 'master' of git://git.denx.de/u-boot-arm: (146 commits)
arm: Use common .lds file where possible
arm: add a common .lds link script
arm: Remove unneeded setting of LDCSRIPT
Define CPUDIR for the .lds link script
arm: Remove zipitz2 link script
Allow arch directory to contain .lds without requiring Makefile
OMAP: Remove omap1610inn-based boards
arch/arm/cpu/armv7/omap-common/clocks-common.c: Fix build warnings
board/ti/beagle/beagle.c: Fix build warnings
sdrc.c: Fix typo in do_sdrc_init() for SPL
tegra: i2c: Add I2C driver
tegra: fdt: i2c: Add extra I2C bindings for U-Boot
tegra: i2c: Select I2C ordering for Seaboard
tegra: i2c: Enable I2C on Seaboard
tegra: i2c: Select number of controllers for Tegra2 boards
tegra: i2c: Initialise I2C on Nvidia boards
tegra: Enhance clock support to handle 16-bit clock divisors
fdt: Add function to allow aliases to refer to multiple nodes
tegra: Rename NV_PA_PMC_BASE to TEGRA2_PMC_BASE
tegra: fdt: Enable FDT support for Ventana
tegra: fdt: Enable FDT support for Seaboard
tegra: usb: Enable USB on Seaboard
tegra: usb: Add common USB defines for tegra2 boards
tegra: usb: Add USB support to nvidia boards
arm: Check for valid FDT after console is up
fdt: Avoid early panic() when there is no FDT present
tegra: usb: Add support for Tegra USB peripheral
tegra: fdt: Add function to return peripheral/clock ID
usb: Add support for txfifo threshold
tegra: usb: fdt: Add USB definitions for Tegra2 Seaboard
tegra: usb: fdt: Add additional device tree definitions for USB ports
tegra: fdt: Add clock bindings for Tegra2 Seaboard
tegra: fdt: Add clock bindings
tegra: fdt: Add additional USB binding
fdt: Add tegra-usb bindings file from linux
fdt: Add staging area for device tree binding documentation
tegra: fdt: Add device tree file for Tegra2 Seaboard from kernel
tegra: fdt: Add Tegra2x device tree file from kernel
arm: fdt: Add skeleton device tree file from kernel
fdt: Add basic support for decoding GPIO definitions
fdt: Add functions to access phandles, arrays and bools
fdt: Tidy up a few fdtdec problems
fdt: Add tests for fdtdec
fdt: Add fdtdec_find_aliases() to deal with alias nodes
arm: Tegra2: Fix ELDK42 gcc failure with inline asm stack pointer load
net: fec_mxc: allow use with cache enabled
net: force PKTALIGN to ARCH_DMA_MINALIGN
i.MX28: Enable caches by default
i.MX28: Make use of the bounce buffer
i.MX28: Do data transfers via DMA in MMC driver
MMC: Implement generic bounce buffer
i.MX28: Add cache support to MXS NAND driver
i.MX28: Add cache support into the APBH DMA driver
ARM926EJS: Implement cache operations
board/vpac270/onenand.c: Fix build errors
nhk8815: fix build errors
atmel-boards: add missing atmel_mci.h
ARM: highbank: setup env from boot source register
ARM: highbank: change env config to use nvram
ARM: highbank: add reset support
ARM: highbank: Add boot counter support
ARM: highbank: change TEXT_BASE to 0x8000
ARM: highbank: fix us_to_tick calculation
ARM: highbank: add missing get_tbclk
ARM: highbank: fix warning for calxedaxgmac_initialize
net: calxedaxgmac: fix build due to missing __aligned definition
EXYNOS: Add structure for Exynos4 DMC
EXYNOS: SMDK5250: Support all 4 UARTs
ARM: fix s3c2410 timer code
ARM: davinci: fixes for cam_enc_4xx board
omap3_spi: receive transmit mode
calimain, enbw_cmc: Fix typo in comments
Davinci: ea20: use gpio framework to access gpios
OMAP3: mt_ventoux: sets its own mtdparts
OMAP3: mt_ventoux: updated timing for FPGA
twl4030: fix potential power supply handling issues
NAND: TI: fix warnings in omap_gpmc.c
cam_enc_4xx: Rename 'images' to 'imgs'
arm: Add Prep subcommand support to bootm
OMAP3: twister: add support to boot Linux from SPL
SPL: call cleanup_before_linux() before booting Linux
OMAP3: SPL: do not call I2C init if no I2C is set.
Add cache functions to SPL for armv7
devkit8000: Implement and activate direct OS boot
omap/spl: change output of spl_parse_image_header
omap-common/spl: Add linux boot to SPL
devkit8000/spl: init GPMC for dm9000 in SPL
omap-common: Add NAND SPL linux booting
devkit8000: add config for spl command
Add cmd_spl command
mx53ard: Initialize return code with error
mx53: Make PLL2 to be the parent of UART clock
configs: imx: Use CONFIG_SF_DEFAULT_CS
mx28evk: Provide default values for SPI bus and chip select
USB: ehci-mx6: Add proper IO accessors
mx6: Read silicon revision from register
i.MX28: Drop __naked function from spl_mem_init
mxs_spi: Return proper timeout error
i.MX28: Make the stabilization delays shorter
pmic_i2c: Return error in case of invalid pmic_i2c_tx_num
mx6: Remove duplicate definition of ANATOP_BASE_ADDR
mx6: Fix reset cause for Power On Reset case
i.MX6: mx6qsabrelite: add MACH_TYPE_MX6Q_SABRELITE
i.MX6: mx6q_sabrelite: add CONFIG_REVISION_TAG
i.MX28: Enable additional DRAM address bits
mx6q: mx6qsabrelite: setup_spi() should be called in board_init to allow use for environment
mx31: add "ARM11P power gating" to get_reset_cause
mx31pdk: Fix CONFIG_SYS_MEMTEST_END
efikamx: Fix CONFIG_SYS_MEMTEST_END
mx53smd: Fix CONFIG_SYS_MEMTEST_END
mx53evk: Fix CONFIG_SYS_MEMTEST_END
mx51evk: Fix CONFIG_SYS_MEMTEST_END
i.MX6: mx6qsabrelite: add ext2 support
imximage: Remove overwriting of flash_offset
IXP: Fix GPIO_INT_ACT_LOW_SET()
IXP: Fix NAND build warning on PDNB3 and SCPU
IXP: Move PDNB3 and SCPU from Makefile to boards.cfg
IXP: Squash warnings in IXP NPE
IXP: Fix missing MACH_TYPE_{ACTUX?,PNB3,DVLHOST}
IXP: Make IXP buildable with arm-linux- toolchains
Examples: Properly append LDFLAGS to LD command
SPL: Enable YMODEM support on BeagleBone and AM335x EVM
SPL: Add YMODEM over UART load support
SPL: Add README.omap3
README: document more SPL config options
spl.c: Use __noreturn decorator
config.mk: Check for -fstack-usage support
config.mk: Make cc-option create a file under include/generated
...
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) >> 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_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_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_set);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
569
drivers/i2c/tegra_i2c.c
Normal file
569
drivers/i2c/tegra_i2c.c
Normal file
@@ -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, ®, 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
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/err.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
|
||||
@@ -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;
|
||||
uint32_t size;
|
||||
int i;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
/*
|
||||
* 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:
|
||||
@@ -671,17 +724,31 @@ static int fec_send(struct eth_device *dev, volatile void* packet, int length)
|
||||
status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY;
|
||||
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);
|
||||
|
||||
@@ -750,10 +834,17 @@ 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;
|
||||
|
||||
@@ -233,22 +233,6 @@ struct ethernet_regs {
|
||||
#define MIIGSK_ENR_EN (1 << 1)
|
||||
#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
|
||||
*
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
200
drivers/usb/host/ehci-mx6.c
Normal file
200
drivers/usb/host/ehci-mx6.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
62
drivers/usb/host/ehci-tegra.c
Normal file
62
drivers/usb/host/ehci-tegra.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user