diff options
author | Wolfgang Denk | 2010-12-22 21:16:17 +0100 |
---|---|---|
committer | Wolfgang Denk | 2010-12-22 21:16:17 +0100 |
commit | cdc51c294ad33879c4e57edf4c9d2155381b1d59 (patch) | |
tree | fc1c3f2c23a2507e91358a6a6dfa881965bf603f /drivers | |
parent | 250ef029844be2cb98635f265359396866d1749f (diff) | |
parent | b8339e2b9f32663411dba0f48e25b23f542d53bc (diff) |
Merge branch 'next' of ../next
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/fpga/spartan3.c | 6 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/mvmfp.c | 87 | ||||
-rw-r--r-- | drivers/mtd/cfi_flash.c | 61 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 21 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_ids.c | 4 | ||||
-rw-r--r-- | drivers/mtd/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/spi/eon.c | 275 | ||||
-rw-r--r-- | drivers/mtd/spi/spi_flash.c | 3 | ||||
-rw-r--r-- | drivers/mtd/spi/spi_flash_internal.h | 1 | ||||
-rw-r--r-- | drivers/mtd/spi/winbond.c | 39 | ||||
-rw-r--r-- | drivers/net/davinci_emac.c | 41 | ||||
-rw-r--r-- | drivers/power/twl6030.c | 124 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/pt7c4338.c | 144 | ||||
-rw-r--r-- | drivers/serial/ns16550.c | 8 | ||||
-rw-r--r-- | drivers/serial/serial.c | 5 | ||||
-rw-r--r-- | drivers/serial/serial_pl01x.c | 50 | ||||
-rw-r--r-- | drivers/serial/serial_pl01x.h | 41 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/omap3_spi.c | 352 | ||||
-rw-r--r-- | drivers/spi/omap3_spi.h | 117 |
22 files changed, 1313 insertions, 70 deletions
diff --git a/drivers/fpga/spartan3.c b/drivers/fpga/spartan3.c index 7a89b5692cb..1dd6f26f98a 100644 --- a/drivers/fpga/spartan3.c +++ b/drivers/fpga/spartan3.c @@ -366,6 +366,8 @@ static int Spartan3_ss_load (Xilinx_desc * desc, void *buf, size_t bsize) CONFIG_FPGA_DELAY (); if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ puts ("** Timeout waiting for INIT to start.\n"); + if (*fn->abort) + (*fn->abort) (cookie); return FPGA_FAIL; } } while (!(*fn->init) (cookie)); @@ -380,6 +382,8 @@ static int Spartan3_ss_load (Xilinx_desc * desc, void *buf, size_t bsize) CONFIG_FPGA_DELAY (); if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */ puts ("** Timeout waiting for INIT to clear.\n"); + if (*fn->abort) + (*fn->abort) (cookie); return FPGA_FAIL; } } while ((*fn->init) (cookie)); @@ -394,6 +398,8 @@ static int Spartan3_ss_load (Xilinx_desc * desc, void *buf, size_t bsize) while DONE is low (inactive) */ if ((*fn->done) (cookie) == 0 && (*fn->init) (cookie)) { puts ("** CRC error during FPGA load.\n"); + if (*fn->abort) + (*fn->abort) (cookie); return (FPGA_FAIL); } val = data [bytecount ++]; diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 398024c7bc3..a5fa2b5d851 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -27,6 +27,7 @@ LIB := $(obj)libgpio.o COBJS-$(CONFIG_AT91_GPIO) += at91_gpio.o COBJS-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o +COBJS-$(CONFIG_MARVELL_MFP) += mvmfp.o COBJS-$(CONFIG_MXC_GPIO) += mxc_gpio.o COBJS-$(CONFIG_PCA953X) += pca953x.o COBJS-$(CONFIG_S5P) += s5p_gpio.o diff --git a/drivers/gpio/mvmfp.c b/drivers/gpio/mvmfp.c new file mode 100644 index 00000000000..5646ed43704 --- /dev/null +++ b/drivers/gpio/mvmfp.c @@ -0,0 +1,87 @@ +/* + * (C) Copyright 2010 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <mvmfp.h> +#include <asm/arch/mfp.h> +#ifdef CONFIG_ARMADA100 +#include <asm/arch/armada100.h> +#else +#error Unsupported SoC... +#endif + +/* + * mfp_config + * + * On most of Marvell SoCs (ex. ARMADA100) there is Multi-Funtion-Pin + * configuration registers to configure each GPIO/Function pin on the + * SoC. + * + * This function reads the array of values for + * MFPR_X registers and programms them into respective + * Multi-Function Pin registers. + * It supports - Alternate Function Selection programming. + * + * Whereas, + * The Configureation value is constructed using MFP() + * array consists of 32bit values as defined in MFP(xx,xx..) macro + */ +void mfp_config(u32 *mfp_cfgs) +{ + u32 *p_mfpr = NULL; + u32 cfg_val, val; + + do { + cfg_val = *mfp_cfgs++; + /* exit if End of configuration table detected */ + if (cfg_val == MFP_EOC) + break; + + p_mfpr = (u32 *)(MV_MFPR_BASE + + MFP_REG_GET_OFFSET(cfg_val)); + + /* Write a mfg register as per configuration */ + val = 0; + if (cfg_val & MFP_AF_FLAG) + /* Abstract and program Afternate-Func Selection */ + val |= cfg_val & MFP_AF_MASK; + if (cfg_val & MFP_EDGE_FLAG) + /* Abstract and program Edge configuration */ + val |= cfg_val & MFP_LPM_EDGE_MASK; + if (cfg_val & MFP_DRIVE_FLAG) + /* Abstract and program Drive configuration */ + val |= cfg_val & MFP_DRIVE_MASK; + if (cfg_val & MFP_PULL_FLAG) + /* Abstract and program Pullup/down configuration */ + val |= cfg_val & MFP_PULL_MASK; + + writel(val, p_mfpr); + } while (1); + /* + * perform a read-back of any MFPR register to make sure the + * previous writings are finished + */ + readl(p_mfpr); +} diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index 39c235ebc26..b0068842f1c 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -74,6 +74,20 @@ flash_info_t flash_info[CFI_MAX_FLASH_BANKS]; /* FLASH chips info */ #define CONFIG_SYS_FLASH_CFI_WIDTH FLASH_CFI_8BIT #endif +/* + * 0xffff is an undefined value for the configuration register. When + * this value is returned, the configuration register shall not be + * written at all (default mode). + */ +static u16 cfi_flash_config_reg(int i) +{ +#ifdef CONFIG_SYS_CFI_FLASH_CONFIG_REGS + return ((u16 [])CONFIG_SYS_CFI_FLASH_CONFIG_REGS)[i]; +#else + return 0xffff; +#endif +} + #if defined(CONFIG_SYS_MAX_FLASH_BANKS_DETECT) int cfi_flash_num_flash_banks = CONFIG_SYS_MAX_FLASH_BANKS_DETECT; #endif @@ -1112,18 +1126,18 @@ static int sector_erased(flash_info_t *info, int i) { int k; int size; - volatile unsigned long *flash; + u32 *flash; /* * Check if whole sector is erased */ size = flash_sector_size(info, i); - flash = (volatile unsigned long *) info->start[i]; + flash = (u32 *)info->start[i]; /* divide by 4 for longword access */ size = size >> 2; for (k = 0; k < size; k++) { - if (*flash++ != 0xffffffff) + if (flash_read32(flash++) != 0xffffffff) return 0; /* not erased */ } @@ -1426,6 +1440,11 @@ int flash_real_protect (flash_info_t * info, long sector, int prot) #endif }; + /* + * Flash needs to be in status register read mode for + * flash_full_status_check() to work correctly + */ + flash_write_cmd(info, sector, 0, FLASH_CMD_READ_STATUS); if ((retcode = flash_full_status_check (info, sector, info->erase_blk_tout, prot ? "protect" : "unprotect")) == 0) { @@ -1975,6 +1994,13 @@ ulong flash_get_size (phys_addr_t base, int banknum) case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_EXTENDED: case CFI_CMDSET_INTEL_STANDARD: + /* + * Set flash to read-id mode. Otherwise + * reading protected status is not + * guaranteed. + */ + flash_write_cmd(info, sect_cnt, 0, + FLASH_CMD_READ_ID); info->protect[sect_cnt] = flash_isset (info, sect_cnt, FLASH_OFFSET_PROTECT, @@ -2021,6 +2047,31 @@ void flash_set_verbose(uint v) flash_verbose = v; } +static void cfi_flash_set_config_reg(u32 base, u16 val) +{ +#ifdef CONFIG_SYS_CFI_FLASH_CONFIG_REGS + /* + * Only set this config register if really defined + * to a valid value (0xffff is invalid) + */ + if (val == 0xffff) + return; + + /* + * Set configuration register. Data is "encrypted" in the 16 lower + * address bits. + */ + flash_write16(FLASH_CMD_SETUP, (void *)(base + (val << 1))); + flash_write16(FLASH_CMD_SET_CR_CONFIRM, (void *)(base + (val << 1))); + + /* + * Finally issue reset-command to bring device back to + * read-array mode + */ + flash_write16(FLASH_CMD_RESET, (void *)base); +#endif +} + /*----------------------------------------------------------------------- */ unsigned long flash_init (void) @@ -2044,6 +2095,10 @@ unsigned long flash_init (void) for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) { flash_info[i].flash_id = FLASH_UNKNOWN; + /* Optionally write flash configuration register */ + cfi_flash_set_config_reg(cfi_flash_bank_addr(i), + cfi_flash_config_reg(i)); + if (!flash_detect_legacy(cfi_flash_bank_addr(i), i)) flash_get_size(cfi_flash_bank_addr(i), i); size += flash_info[i].size; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 21cc5a39407..5239c1fcff6 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -439,11 +439,12 @@ void nand_wait_ready(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; u32 timeo = (CONFIG_SYS_HZ * 20) / 1000; + u32 time_start; - reset_timer(); + time_start = get_timer(0); /* wait until command is processed or timeout occures */ - while (get_timer(0) < timeo) { + while (get_timer(time_start) < timeo) { if (chip->dev_ready) if (chip->dev_ready(mtd)) break; @@ -704,6 +705,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) { unsigned long timeo; int state = this->state; + u32 time_start; if (state == FL_ERASING) timeo = (CONFIG_SYS_HZ * 400) / 1000; @@ -715,10 +717,10 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) else this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - reset_timer(); + time_start = get_timer(0); while (1) { - if (get_timer(0) > timeo) { + if (get_timer(time_start) > timeo) { printf("Timeout!"); return 0x01; } @@ -732,8 +734,9 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) } } #ifdef PPCHAMELON_NAND_TIMER_HACK - reset_timer(); - while (get_timer(0) < 10); + time_start = get_timer(0); + while (get_timer(time_start) < 10) + ; #endif /* PPCHAMELON_NAND_TIMER_HACK */ return this->read_byte(mtd); @@ -2409,11 +2412,11 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) /* * Get the flash and manufacturer id and lookup if the type is supported */ -static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, +static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, int busw, int *maf_id) { - struct nand_flash_dev *type = NULL; + const struct nand_flash_dev *type = NULL; int i, dev_id, maf_idx; int tmp_id, tmp_manf; @@ -2587,7 +2590,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) { int i, busw, nand_maf_id; struct nand_chip *chip = mtd->priv; - struct nand_flash_dev *type; + const struct nand_flash_dev *type; /* Get buswidth to select the correct functions */ busw = chip->options & NAND_BUSWIDTH_16; diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 25b22ecc926..8d7ea767dd5 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -22,7 +22,7 @@ + 256 256 Byte page size * 512 512 Byte page size */ -struct nand_flash_dev nand_flash_ids[] = { +const struct nand_flash_dev nand_flash_ids[] = { #ifdef CONFIG_MTD_NAND_MUSEUM_IDS {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, @@ -132,7 +132,7 @@ struct nand_flash_dev nand_flash_ids[] = { /* * Manufacturer ID list */ -struct nand_manufacturers nand_manuf_ids[] = { +const struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_TOSHIBA, "Toshiba"}, {NAND_MFR_SAMSUNG, "Samsung"}, {NAND_MFR_FUJITSU, "Fujitsu"}, diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 3d607c06d39..57112af66f2 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -27,6 +27,7 @@ LIB := $(obj)libspi_flash.o COBJS-$(CONFIG_SPI_FLASH) += spi_flash.o COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o +COBJS-$(CONFIG_SPI_FLASH_EON) += eon.o COBJS-$(CONFIG_SPI_FLASH_MACRONIX) += macronix.o COBJS-$(CONFIG_SPI_FLASH_SPANSION) += spansion.o COBJS-$(CONFIG_SPI_FLASH_SST) += sst.o diff --git a/drivers/mtd/spi/eon.c b/drivers/mtd/spi/eon.c new file mode 100644 index 00000000000..02c3bb93047 --- /dev/null +++ b/drivers/mtd/spi/eon.c @@ -0,0 +1,275 @@ +/* + * (C) Copyright 2010, ucRobotics Inc. + * Author: Chong Huang <chuang@ucrobotics.com> + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +/* EN25Q128-specific commands */ +#define CMD_EN25Q128_WREN 0x06 /* Write Enable */ +#define CMD_EN25Q128_WRDI 0x04 /* Write Disable */ +#define CMD_EN25Q128_RDSR 0x05 /* Read Status Register */ +#define CMD_EN25Q128_WRSR 0x01 /* Write Status Register */ +#define CMD_EN25Q128_READ 0x03 /* Read Data Bytes */ +#define CMD_EN25Q128_FAST_READ 0x0b /* Read Data Bytes at Higher Speed */ +#define CMD_EN25Q128_PP 0x02 /* Page Program */ +#define CMD_EN25Q128_SE 0x20 /* Sector Erase */ +#define CMD_EN25Q128_BE 0xd8 /* Block Erase */ +#define CMD_EN25Q128_DP 0xb9 /* Deep Power-down */ +#define CMD_EN25Q128_RES 0xab /* Release from DP, and Read Signature */ + +#define EON_ID_EN25Q128 0x18 + +#define EON_SR_WIP (1 << 0) /* Write-in-Progress */ + +struct eon_spi_flash_params { + u8 idcode1; + u16 page_size; + u16 pages_per_sector; + u16 sectors_per_block; + u16 nr_sectors; + const char *name; +}; + +/* spi_flash needs to be first so upper layers can free() it */ +struct eon_spi_flash { + struct spi_flash flash; + const struct eon_spi_flash_params *params; +}; + +static inline struct eon_spi_flash *to_eon_spi_flash(struct spi_flash *flash) +{ + return container_of(flash, struct eon_spi_flash, flash); +} + +static const struct eon_spi_flash_params eon_spi_flash_table[] = { + { + .idcode1 = EON_ID_EN25Q128, + .page_size = 256, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_sectors = 4096, + .name = "EN25Q128", + }, +}; + +static int eon_wait_ready(struct spi_flash *flash, unsigned long timeout) +{ + struct spi_slave *spi = flash->spi; + unsigned long timebase; + int ret; + u8 cmd = CMD_EN25Q128_RDSR; + u8 status; + + ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); + if (ret) { + debug("SF: Failed to send command %02x: %d\n", cmd, ret); + return ret; + } + + timebase = get_timer(0); + do { + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) + return -1; + + if ((status & EON_SR_WIP) == 0) + break; + + } while (get_timer(timebase) < timeout); + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if ((status & EON_SR_WIP) == 0) + return 0; + + /* Timed out */ + return -1; +} + +static int eon_read_fast(struct spi_flash *flash, + u32 offset, size_t len, void *buf) +{ + struct eon_spi_flash *eon = to_eon_spi_flash(flash); + unsigned long page_addr; + unsigned long page_size; + u8 cmd[5]; + + page_size = eon->params->page_size; + page_addr = offset / page_size; + + cmd[0] = CMD_READ_ARRAY_FAST; + cmd[1] = page_addr >> 8; + cmd[2] = page_addr; + cmd[3] = offset % page_size; + cmd[4] = 0x00; + + return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len); +} + +static int eon_write(struct spi_flash *flash, + u32 offset, size_t len, const void *buf) +{ + struct eon_spi_flash *eon = to_eon_spi_flash(flash); + unsigned long page_addr; + unsigned long byte_addr; + unsigned long page_size; + size_t chunk_len; + size_t actual; + int ret; + u8 cmd[4]; + + page_size = eon->params->page_size; + page_addr = offset / page_size; + byte_addr = offset % page_size; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual += chunk_len) { + chunk_len = min(len - actual, page_size - byte_addr); + + cmd[0] = CMD_EN25Q128_PP; + cmd[1] = page_addr >> 8; + cmd[2] = page_addr; + cmd[3] = byte_addr; + + debug + ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n", + buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); + + ret = spi_flash_cmd(flash->spi, CMD_EN25Q128_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, + buf + actual, chunk_len); + if (ret < 0) { + debug("SF: EON Page Program failed\n"); + break; + } + + ret = eon_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); + if (ret < 0) { + debug("SF: EON page programming timed out\n"); + break; + } + + page_addr++; + byte_addr = 0; + } + + debug("SF: EON: Successfully programmed %u bytes @ 0x%x\n", + len, offset); + + spi_release_bus(flash->spi); + return ret; +} + +int eon_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + /* block erase */ + struct eon_spi_flash *eon = to_eon_spi_flash(flash); + unsigned long block_size; + size_t actual; + int ret; + u8 cmd[4]; + + + block_size = eon->params->page_size * eon->params->pages_per_sector + * eon->params->sectors_per_block; + + if (offset % block_size || len % block_size) { + debug("SF: Erase offset/length not multiple of block size\n"); + return -1; + } + + len /= block_size; + cmd[0] = CMD_EN25Q128_BE; + cmd[2] = 0x00; + cmd[3] = 0x00; + + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: Unable to claim SPI bus\n"); + return ret; + } + + ret = 0; + for (actual = 0; actual < len; actual++) { + cmd[1] = (offset / block_size) + actual; + ret = spi_flash_cmd(flash->spi, CMD_EN25Q128_WREN, NULL, 0); + if (ret < 0) { + debug("SF: Enabling Write failed\n"); + break; + } + + ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0); + if (ret < 0) { + debug("SF: EON page erase failed\n"); + break; + } + + ret = eon_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); + if (ret < 0) { + debug("SF: EON page erase timed out\n"); + break; + } + } + + debug("SF: EON: Successfully erased %u bytes @ 0x%x\n", + len * block_size, offset); + + spi_release_bus(flash->spi); + return ret; +} + +struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode) +{ + const struct eon_spi_flash_params *params; + struct eon_spi_flash *eon; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(eon_spi_flash_table); ++i) { + params = &eon_spi_flash_table[i]; + if (params->idcode1 == idcode[2]) + break; + } + + if (i == ARRAY_SIZE(eon_spi_flash_table)) { + debug("SF: Unsupported EON ID %02x\n", idcode[1]); + return NULL; + } + + eon = malloc(sizeof(*eon)); + if (!eon) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + + eon->params = params; + eon->flash.spi = spi; + eon->flash.name = params->name; + + eon->flash.write = eon_write; + eon->flash.erase = eon_erase; + eon->flash.read = eon_read_fast; + eon->flash.size = params->page_size * params->pages_per_sector + * params->nr_sectors; + + debug("SF: Detected %s with page size %u, total %u bytes\n", + params->name, params->page_size, eon->flash.size); + + return &eon->flash; +} diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index ab02ef3e04d..b61d2198cd1 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -131,6 +131,9 @@ static const struct { #ifdef CONFIG_SPI_FLASH_ATMEL { 0, 0x1f, spi_flash_probe_atmel, }, #endif +#ifdef CONFIG_SPI_FLASH_EON + { 0, 0x1c, spi_flash_probe_eon, }, +#endif #ifdef CONFIG_SPI_FLASH_MACRONIX { 0, 0xc2, spi_flash_probe_macronix, }, #endif diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h index 9bc43dd21e1..68dcffb9717 100644 --- a/drivers/mtd/spi/spi_flash_internal.h +++ b/drivers/mtd/spi/spi_flash_internal.h @@ -46,6 +46,7 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, /* Manufacturer-specific probe functions */ struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode); diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c index de3aeb810a6..44523555d9e 100644 --- a/drivers/mtd/spi/winbond.c +++ b/drivers/mtd/spi/winbond.c @@ -24,11 +24,6 @@ #define CMD_W25_DP 0xb9 /* Deep Power-down */ #define CMD_W25_RES 0xab /* Release from DP, and Read Signature */ -#define WINBOND_ID_W25X16 0x3015 -#define WINBOND_ID_W25X32 0x3016 -#define WINBOND_ID_W25X64 0x3017 -#define WINBOND_ID_W25Q64 0x4017 - #define WINBOND_SR_WIP (1 << 0) /* Write-in-Progress */ struct winbond_spi_flash_params { @@ -37,7 +32,7 @@ struct winbond_spi_flash_params { uint8_t l2_page_size; uint16_t pages_per_sector; uint16_t sectors_per_block; - uint8_t nr_blocks; + uint16_t nr_blocks; const char *name; }; @@ -55,7 +50,7 @@ to_winbond_spi_flash(struct spi_flash *flash) static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { { - .id = WINBOND_ID_W25X16, + .id = 0x3015, .l2_page_size = 8, .pages_per_sector = 16, .sectors_per_block = 16, @@ -63,7 +58,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { .name = "W25X16", }, { - .id = WINBOND_ID_W25X32, + .id = 0x3016, .l2_page_size = 8, .pages_per_sector = 16, .sectors_per_block = 16, @@ -71,7 +66,7 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { .name = "W25X32", }, { - .id = WINBOND_ID_W25X64, + .id = 0x3017, .l2_page_size = 8, .pages_per_sector = 16, .sectors_per_block = 16, @@ -79,13 +74,37 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { .name = "W25X64", }, { - .id = WINBOND_ID_W25Q64, + .id = 0x4015, + .l2_page_size = 8, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 32, + .name = "W25Q16", + }, + { + .id = 0x4016, + .l2_page_size = 8, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 64, + .name = "W25Q32", + }, + { + .id = 0x4017, .l2_page_size = 8, .pages_per_sector = 16, .sectors_per_block = 16, .nr_blocks = 128, .name = "W25Q64", }, + { + .id = 0x4018, + .l2_page_size = 8, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 256, + .name = "W25Q128", + }, }; static int winbond_wait_ready(struct spi_flash *flash, unsigned long timeout) diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index e06896fea71..43a3d79dc56 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -243,8 +243,35 @@ static int gen_get_link_speed(int phy_addr) { u_int16_t tmp; - if (davinci_eth_phy_read(phy_addr, MII_STATUS_REG, &tmp) && (tmp & 0x04)) + if (davinci_eth_phy_read(phy_addr, MII_STATUS_REG, &tmp) && + (tmp & 0x04)) { +#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \ + defined(CONFIG_MACH_DAVINCI_DA850_EVM) + davinci_eth_phy_read(phy_addr, PHY_ANLPAR, &tmp); + + /* Speed doesn't matter, there is no setting for it in EMAC. */ + if (tmp & (PHY_ANLPAR_TXFD | PHY_ANLPAR_10FD)) { + /* set EMAC for Full Duplex */ + writel(EMAC_MACCONTROL_MIIEN_ENABLE | + EMAC_MACCONTROL_FULLDUPLEX_ENABLE, + &adap_emac->MACCONTROL); + } else { + /*set EMAC for Half Duplex */ + writel(EMAC_MACCONTROL_MIIEN_ENABLE, + &adap_emac->MACCONTROL); + } + + if (tmp & (PHY_ANLPAR_TXFD | PHY_ANLPAR_TX)) + writel(readl(&adap_emac->MACCONTROL) | + EMAC_MACCONTROL_RMIISPEED_100, + &adap_emac->MACCONTROL); + else + writel(readl(&adap_emac->MACCONTROL) & + ~EMAC_MACCONTROL_RMIISPEED_100, + &adap_emac->MACCONTROL); +#endif return(1); + } return(0); } @@ -326,6 +353,12 @@ static int davinci_eth_open(struct eth_device *dev, bd_t *bis) } #endif +#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \ + defined(CONFIG_MACH_DAVINCI_DA850_EVM) + adap_ewrap->c0rxen = adap_ewrap->c1rxen = adap_ewrap->c2rxen = 0; + adap_ewrap->c0txen = adap_ewrap->c1txen = adap_ewrap->c2txen = 0; + adap_ewrap->c0miscen = adap_ewrap->c1miscen = adap_ewrap->c2miscen = 0; +#endif rx_desc = emac_rx_desc; writel(1, &adap_emac->TXCONTROL); @@ -480,6 +513,12 @@ static void davinci_eth_close(struct eth_device *dev) writel(0, &adap_ewrap->EWCTL); #endif +#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \ + defined(CONFIG_MACH_DAVINCI_DA850_EVM) + adap_ewrap->c0rxen = adap_ewrap->c1rxen = adap_ewrap->c2rxen = 0; + adap_ewrap->c0txen = adap_ewrap->c1txen = adap_ewrap->c2txen = 0; + adap_ewrap->c0miscen = adap_ewrap->c1miscen = adap_ewrap->c2miscen = 0; +#endif debug_emac("- emac_close\n"); } diff --git a/drivers/power/twl6030.c b/drivers/power/twl6030.c index cf1da6b6a23..fef57b43377 100644 --- a/drivers/power/twl6030.c +++ b/drivers/power/twl6030.c @@ -36,6 +36,54 @@ static inline int twl6030_i2c_read_u8(u8 chip_no, u8 *val, u8 reg) return i2c_read(chip_no, reg, 1, val, 1); } +static int twl6030_gpadc_read_channel(u8 channel_no) +{ + u8 lsb = 0; + u8 msb = 0; + int ret = 0; + + ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, &lsb, + GPCH0_LSB + channel_no * 2); + if (ret) + return ret; + + ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, &msb, + GPCH0_MSB + channel_no * 2); + if (ret) + return ret; + + return (msb << 8) | lsb; +} + +static int twl6030_gpadc_sw2_trigger(void) +{ + u8 val; + int ret = 0; + + ret = twl6030_i2c_write_u8(TWL6030_CHIP_ADC, CTRL_P2_SP2, CTRL_P2); + if (ret) + return ret; + + /* Waiting until the SW1 conversion ends*/ + val = CTRL_P2_BUSY; + + while (!((val & CTRL_P2_EOCP2) && (!(val & CTRL_P2_BUSY)))) { + ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, &val, CTRL_P2); + if (ret) + return ret; + udelay(1000); + } + + return 0; +} + +void twl6030_stop_usb_charging(void) +{ + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, 0, CONTROLLER_CTRL1); + + return; +} + void twl6030_start_usb_charging(void) { twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CHARGERUSB_VICHRG_1500, @@ -48,17 +96,89 @@ void twl6030_start_usb_charging(void) CHARGERUSB_INT_MASK); twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CHARGERUSB_VOREG_4P0, CHARGERUSB_VOREG); - twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CHARGERUSB_CTRL2_VITERM_100, + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CHARGERUSB_CTRL2_VITERM_400, CHARGERUSB_CTRL2); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, TERM, CHARGERUSB_CTRL1); /* Enable USB charging */ twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CONTROLLER_CTRL1_EN_CHARGER, CONTROLLER_CTRL1); return; } +int twl6030_get_battery_current(void) +{ + int battery_current = 0; + u8 msb = 0; + u8 lsb = 0; + + twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, &msb, FG_REG_11); + twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, &lsb, FG_REG_10); + battery_current = ((msb << 8) | lsb); + + /* convert 10 bit signed number to 16 bit signed number */ + if (battery_current >= 0x2000) + battery_current = (battery_current - 0x4000); + + battery_current = battery_current * 3000 / 4096; + printf("Battery Current: %d mA\n", battery_current); + + return battery_current; +} + +int twl6030_get_battery_voltage(void) +{ + int battery_volt = 0; + int ret = 0; + + /* Start GPADC SW conversion */ + ret = twl6030_gpadc_sw2_trigger(); + if (ret) { + printf("Failed to convert battery voltage\n"); + return ret; + } + + /* measure Vbat voltage */ + battery_volt = twl6030_gpadc_read_channel(7); + if (battery_volt < 0) { + printf("Failed to read battery voltage\n"); + return ret; + } + battery_volt = (battery_volt * 25 * 1000) >> (10 + 2); + printf("Battery Voltage: %d mV\n", battery_volt); + + return battery_volt; +} + void twl6030_init_battery_charging(void) { - twl6030_start_usb_charging(); + u8 stat1 = 0; + int battery_volt = 0; + int ret = 0; + + /* Enable VBAT measurement */ + twl6030_i2c_write_u8(TWL6030_CHIP_PM, VBAT_MEAS, MISC1); + + /* Enable GPADC module */ + ret = twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, FGS | GPADCS, TOGGLE1); + if (ret) { + printf("Failed to enable GPADC\n"); + return; + } + + battery_volt = twl6030_get_battery_voltage(); + if (battery_volt < 0) + return; + + if (battery_volt < 3000) + printf("Main battery voltage too low!\n"); + + /* Check for the presence of USB charger */ + twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, &stat1, CONTROLLER_STAT1); + + /* check for battery presence indirectly via Fuel gauge */ + if ((stat1 & VBUS_DET) && (battery_volt < 3300)) + twl6030_start_usb_charging(); + return; } diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index f810fca0907..916d73f9f01 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -57,6 +57,7 @@ COBJS-$(CONFIG_RTC_MPC5200) += mpc5xxx.o COBJS-$(CONFIG_RTC_MPC8xx) += mpc8xx.o COBJS-$(CONFIG_RTC_PCF8563) += pcf8563.o COBJS-$(CONFIG_RTC_PL031) += pl031.o +COBJS-$(CONFIG_RTC_PT7C4338) += pt7c4338.o COBJS-$(CONFIG_RTC_RS5C372A) += rs5c372.o COBJS-$(CONFIG_RTC_RTC4543) += rtc4543.o COBJS-$(CONFIG_RTC_RX8025) += rx8025.o diff --git a/drivers/rtc/pt7c4338.c b/drivers/rtc/pt7c4338.c new file mode 100644 index 00000000000..26e2c1ee60d --- /dev/null +++ b/drivers/rtc/pt7c4338.c @@ -0,0 +1,144 @@ +/* + * Copyright 2010 Freescale Semiconductor, Inc. + * + * Author: Priyanka Jain <Priyanka.Jain@freescale.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 + */ + +/* + * This file provides Date & Time support (no alarms) for PT7C4338 chip. + * + * This file is based on drivers/rtc/ds1337.c + * + * PT7C4338 chip is manufactured by Pericom Technology Inc. + * It is a serial real-time clock which provides + * 1)Low-power clock/calendar. + * 2)Programmable square-wave output. + * It has 56 bytes of nonvolatile RAM. + */ + +#include <common.h> +#include <command.h> +#include <rtc.h> +#include <i2c.h> + +/* RTC register addresses */ +#define RTC_SEC_REG_ADDR 0x0 +#define RTC_MIN_REG_ADDR 0x1 +#define RTC_HR_REG_ADDR 0x2 +#define RTC_DAY_REG_ADDR 0x3 +#define RTC_DATE_REG_ADDR 0x4 +#define RTC_MON_REG_ADDR 0x5 +#define RTC_YR_REG_ADDR 0x6 +#define RTC_CTL_STAT_REG_ADDR 0x7 + +/* RTC second register address bit */ +#define RTC_SEC_BIT_CH 0x80 /* Clock Halt (in Register 0) */ + +/* RTC control and status register bits */ +#define RTC_CTL_STAT_BIT_RS0 0x1 /* Rate select 0 */ +#define RTC_CTL_STAT_BIT_RS1 0x2 /* Rate select 1 */ +#define RTC_CTL_STAT_BIT_SQWE 0x10 /* Square Wave Enable */ +#define RTC_CTL_STAT_BIT_OSF 0x20 /* Oscillator Stop Flag */ +#define RTC_CTL_STAT_BIT_OUT 0x80 /* Output Level Control */ + +/* RTC reset value */ +#define RTC_PT7C4338_RESET_VAL \ + (RTC_CTL_STAT_BIT_RS0 | RTC_CTL_STAT_BIT_RS1 | RTC_CTL_STAT_BIT_OUT) + +/****** Helper functions ****************************************/ +static u8 rtc_read(u8 reg) +{ + return i2c_reg_read(CONFIG_SYS_I2C_RTC_ADDR, reg); +} + +static void rtc_write(u8 reg, u8 val) +{ + i2c_reg_write(CONFIG_SYS_I2C_RTC_ADDR, reg, val); +} +/****************************************************************/ + +/* Get the current time from the RTC */ +int rtc_get(struct rtc_time *tmp) +{ + int ret = 0; + u8 sec, min, hour, mday, wday, mon, year, ctl_stat; + + ctl_stat = rtc_read(RTC_CTL_STAT_REG_ADDR); + sec = rtc_read(RTC_SEC_REG_ADDR); + min = rtc_read(RTC_MIN_REG_ADDR); + hour = rtc_read(RTC_HR_REG_ADDR); + wday = rtc_read(RTC_DAY_REG_ADDR); + mday = rtc_read(RTC_DATE_REG_ADDR); + mon = rtc_read(RTC_MON_REG_ADDR); + year = rtc_read(RTC_YR_REG_ADDR); + debug("Get RTC year: %02x mon: %02x mday: %02x wday: %02x " + "hr: %02x min: %02x sec: %02x control_status: %02x\n", + year, mon, mday, wday, hour, min, sec, ctl_stat); + + if (ctl_stat & RTC_CTL_STAT_BIT_OSF) { + printf("### Warning: RTC oscillator has stopped\n"); + /* clear the OSF flag */ + rtc_write(RTC_CTL_STAT_REG_ADDR, + rtc_read(RTC_CTL_STAT_REG_ADDR)\ + & ~RTC_CTL_STAT_BIT_OSF); + ret = -1; + } + + tmp->tm_sec = bcd2bin(sec & 0x7F); + tmp->tm_min = bcd2bin(min & 0x7F); + tmp->tm_hour = bcd2bin(hour & 0x3F); + tmp->tm_mday = bcd2bin(mday & 0x3F); + tmp->tm_mon = bcd2bin(mon & 0x1F); + tmp->tm_year = bcd2bin(year) + 2000; + tmp->tm_wday = bcd2bin((wday - 1) & 0x07); + tmp->tm_yday = 0; + tmp->tm_isdst = 0; + debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + return ret; +} + +/* Set the RTC */ +int rtc_set(struct rtc_time *tmp) +{ + debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + rtc_write(RTC_YR_REG_ADDR, bin2bcd(tmp->tm_year % 100)); + rtc_write(RTC_MON_REG_ADDR, bin2bcd(tmp->tm_mon)); + rtc_write(RTC_DAY_REG_ADDR, bin2bcd(tmp->tm_wday + 1)); + rtc_write(RTC_DATE_REG_ADDR, bin2bcd(tmp->tm_mday)); + rtc_write(RTC_HR_REG_ADDR, bin2bcd(tmp->tm_hour)); + rtc_write(RTC_MIN_REG_ADDR, bin2bcd(tmp->tm_min)); + rtc_write(RTC_SEC_REG_ADDR, bin2bcd(tmp->tm_sec)); + + return 0; +} + +/* Reset the RTC */ +void rtc_reset(void) +{ + rtc_write(RTC_SEC_REG_ADDR, 0x00); /* clearing Clock Halt */ + rtc_write(RTC_CTL_STAT_REG_ADDR, RTC_PT7C4338_RESET_VAL); +} diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 32f24dee50e..8eeb48fb2a9 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -24,9 +24,13 @@ #define serial_in(y) readb(y) #endif +#ifndef CONFIG_SYS_NS16550_IER +#define CONFIG_SYS_NS16550_IER 0x00 +#endif /* CONFIG_SYS_NS16550_IER */ + void NS16550_init (NS16550_t com_port, int baud_divisor) { - serial_out(0x00, &com_port->ier); + serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); #if defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2) serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/ #endif @@ -52,7 +56,7 @@ void NS16550_init (NS16550_t com_port, int baud_divisor) #ifndef CONFIG_NS16550_MIN_FUNCTIONS void NS16550_reinit (NS16550_t com_port, int baud_divisor) { - serial_out(0x00, &com_port->ier); + serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); serial_out(0, &com_port->dll); serial_out(0, &com_port->dlm); diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index 1073ac0ae9c..cd3439ee6a2 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -29,9 +29,10 @@ #endif #ifdef CONFIG_KIRKWOOD #include <asm/arch/kirkwood.h> -#endif -#ifdef CONFIG_ORION5X +#elif defined(CONFIG_ORION5X) #include <asm/arch/orion5x.h> +#elif defined(CONFIG_ARMADA100) +#include <asm/arch/armada100.h> #endif #if defined (CONFIG_SERIAL_MULTI) diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c index c0ae9472424..5dfcde87747 100644 --- a/drivers/serial/serial_pl01x.c +++ b/drivers/serial/serial_pl01x.c @@ -47,14 +47,20 @@ static int pl01x_tstc (int portnum); unsigned int baudrate = CONFIG_BAUDRATE; DECLARE_GLOBAL_DATA_PTR; +static struct pl01x_regs *pl01x_get_regs(int portnum) +{ + return (struct pl01x_regs *) port[portnum]; +} + #ifdef CONFIG_PL010_SERIAL int serial_init (void) { + struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT); unsigned int divisor; /* First, disable everything */ - writel(0x0, port[CONSOLE_PORT] + UART_PL010_CR); + writel(0, ®s->pl010_cr); /* Set baud rate */ switch (baudrate) { @@ -82,15 +88,14 @@ int serial_init (void) divisor = UART_PL010_BAUD_38400; } - writel(((divisor & 0xf00) >> 8), port[CONSOLE_PORT] + UART_PL010_LCRM); - writel((divisor & 0xff), port[CONSOLE_PORT] + UART_PL010_LCRL); + writel((divisor & 0xf00) >> 8, ®s->pl010_lcrm); + writel(divisor & 0xff, ®s->pl010_lcrl); /* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */ - writel((UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN), - port[CONSOLE_PORT] + UART_PL010_LCRH); + writel(UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN, ®s->pl010_lcrh); /* Finally, enable the UART */ - writel((UART_PL010_CR_UARTEN), port[CONSOLE_PORT] + UART_PL010_CR); + writel(UART_PL010_CR_UARTEN, ®s->pl010_cr); return 0; } @@ -101,13 +106,14 @@ int serial_init (void) int serial_init (void) { + struct pl01x_regs *regs = pl01x_get_regs(CONSOLE_PORT); unsigned int temp; unsigned int divider; unsigned int remainder; unsigned int fraction; /* First, disable everything */ - writel(0x0, port[CONSOLE_PORT] + UART_PL011_CR); + writel(0, ®s->pl011_cr); /* * Set baud rate @@ -121,16 +127,16 @@ int serial_init (void) temp = (8 * remainder) / baudrate; fraction = (temp >> 1) + (temp & 1); - writel(divider, port[CONSOLE_PORT] + UART_PL011_IBRD); - writel(fraction, port[CONSOLE_PORT] + UART_PL011_FBRD); + writel(divider, ®s->pl011_ibrd); + writel(fraction, ®s->pl011_fbrd); /* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */ - writel((UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN), - port[CONSOLE_PORT] + UART_PL011_LCRH); + writel(UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN, + ®s->pl011_lcrh); /* Finally, enable the UART */ - writel((UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | UART_PL011_CR_RXE), - port[CONSOLE_PORT] + UART_PL011_CR); + writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | UART_PL011_CR_RXE, + ®s->pl011_cr); return 0; } @@ -170,28 +176,31 @@ void serial_setbrg (void) static void pl01x_putc (int portnum, char c) { + struct pl01x_regs *regs = pl01x_get_regs(portnum); + /* Wait until there is space in the FIFO */ - while (readl(port[portnum] + UART_PL01x_FR) & UART_PL01x_FR_TXFF) + while (readl(®s->fr) & UART_PL01x_FR_TXFF) WATCHDOG_RESET(); /* Send the character */ - writel(c, port[portnum] + UART_PL01x_DR); + writel(c, ®s->dr); } static int pl01x_getc (int portnum) { + struct pl01x_regs *regs = pl01x_get_regs(portnum); unsigned int data; /* Wait until there is data in the FIFO */ - while (readl(port[portnum] + UART_PL01x_FR) & UART_PL01x_FR_RXFE) + while (readl(®s->fr) & UART_PL01x_FR_RXFE) WATCHDOG_RESET(); - data = readl(port[portnum] + UART_PL01x_DR); + data = readl(®s->dr); /* Check for an error flag */ if (data & 0xFFFFFF00) { /* Clear the error */ - writel(0xFFFFFFFF, port[portnum] + UART_PL01x_ECR); + writel(0xFFFFFFFF, ®s->ecr); return -1; } @@ -200,7 +209,8 @@ static int pl01x_getc (int portnum) static int pl01x_tstc (int portnum) { + struct pl01x_regs *regs = pl01x_get_regs(portnum); + WATCHDOG_RESET(); - return !(readl(port[portnum] + UART_PL01x_FR) & - UART_PL01x_FR_RXFE); + return !(readl(®s->fr) & UART_PL01x_FR_RXFE); } diff --git a/drivers/serial/serial_pl01x.h b/drivers/serial/serial_pl01x.h index 5f20fdd108a..b670c24e11f 100644 --- a/drivers/serial/serial_pl01x.h +++ b/drivers/serial/serial_pl01x.h @@ -29,10 +29,28 @@ * Definitions common to both PL010 & PL011 * */ -#define UART_PL01x_DR 0x00 /* Data read or written from the interface. */ -#define UART_PL01x_RSR 0x04 /* Receive status register (Read). */ -#define UART_PL01x_ECR 0x04 /* Error clear register (Write). */ -#define UART_PL01x_FR 0x18 /* Flag register (Read only). */ + +#ifndef __ASSEMBLY__ +/* + * We can use a combined structure for PL010 and PL011, because they overlap + * only in common registers. + */ +struct pl01x_regs { + u32 dr; /* 0x00 Data register */ + u32 ecr; /* 0x04 Error clear register (Write) */ + u32 pl010_lcrh; /* 0x08 Line control register, high byte */ + u32 pl010_lcrm; /* 0x0C Line control register, middle byte */ + u32 pl010_lcrl; /* 0x10 Line control register, low byte */ + u32 pl010_cr; /* 0x14 Control register */ + u32 fr; /* 0x18 Flag register (Read only) */ + u32 reserved; + u32 ilpr; /* 0x20 IrDA low-power counter register */ + u32 pl011_ibrd; /* 0x24 Integer baud rate register */ + u32 pl011_fbrd; /* 0x28 Fractional baud rate register */ + u32 pl011_lcrh; /* 0x2C Line control register */ + u32 pl011_cr; /* 0x30 Control register */ +}; +#endif #define UART_PL01x_RSR_OE 0x08 #define UART_PL01x_RSR_BE 0x04 @@ -50,14 +68,6 @@ * PL010 definitions * */ -#define UART_PL010_LCRH 0x08 /* Line control register, high byte. */ -#define UART_PL010_LCRM 0x0C /* Line control register, middle byte. */ -#define UART_PL010_LCRL 0x10 /* Line control register, low byte. */ -#define UART_PL010_CR 0x14 /* Control register. */ -#define UART_PL010_IIR 0x1C /* Interrupt indentification register (Read). */ -#define UART_PL010_ICR 0x1C /* Interrupt clear register (Write). */ -#define UART_PL010_ILPR 0x20 /* IrDA low power counter register. */ - #define UART_PL010_CR_LPE (1 << 7) #define UART_PL010_CR_RTIE (1 << 6) #define UART_PL010_CR_TIE (1 << 5) @@ -93,13 +103,6 @@ * PL011 definitions * */ -#define UART_PL011_IBRD 0x24 -#define UART_PL011_FBRD 0x28 -#define UART_PL011_LCRH 0x2C -#define UART_PL011_CR 0x30 -#define UART_PL011_IMSC 0x38 -#define UART_PL011_PERIPH_ID0 0xFE0 - #define UART_PL011_LCRH_SPS (1 << 7) #define UART_PL011_LCRH_WLEN_8 (3 << 5) #define UART_PL011_LCRH_WLEN_7 (2 << 5) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 117ab1988d2..e34a1242392 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -35,6 +35,7 @@ COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o +COBJS-$(CONFIG_OMAP3_SPI) += omap3_spi.o COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o COBJS := $(COBJS-y) diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c new file mode 100644 index 00000000000..af12c0e590b --- /dev/null +++ b/drivers/spi/omap3_spi.c @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com> + * + * Driver for McSPI controller on OMAP3. Based on davinci_spi.c + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * Copyright (C) 2007 Atmel Corporation + * + * Parts taken from linux/drivers/spi/omap2_mcspi.c + * Copyright (C) 2005, 2006 Nokia Corporation + * + * Modified by Ruslan Araslanov <ruslan.araslanov@vitecmm.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 <spi.h> +#include <malloc.h> +#include <asm/io.h> +#include "omap3_spi.h" + +#define WORD_LEN 8 +#define SPI_WAIT_TIMEOUT 3000000; + +static void spi_reset(struct omap3_spi_slave *ds) +{ + unsigned int tmp; + + writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, &ds->regs->sysconfig); + do { + tmp = readl(&ds->regs->sysstatus); + } while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE)); + + writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE | + OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP | + OMAP3_MCSPI_SYSCONFIG_SMARTIDLE, + &ds->regs->sysconfig); + + writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, &ds->regs->wakeupenable); +} + +void spi_init() +{ + /* do nothing */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct omap3_spi_slave *ds; + + ds = malloc(sizeof(struct omap3_spi_slave)); + if (!ds) { + printf("SPI error: malloc of SPI structure failed\n"); + return NULL; + } + + /* + * OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules) + * with different number of chip selects (CS, channels): + * McSPI1 has 4 CS (bus 0, cs 0 - 3) + * McSPI2 has 2 CS (bus 1, cs 0 - 1) + * McSPI3 has 2 CS (bus 2, cs 0 - 1) + * McSPI4 has 1 CS (bus 3, cs 0) + */ + + switch (bus) { + case 0: + ds->regs = (struct mcspi *)OMAP3_MCSPI1_BASE; + break; + case 1: + ds->regs = (struct mcspi *)OMAP3_MCSPI2_BASE; + break; + case 2: + ds->regs = (struct mcspi *)OMAP3_MCSPI3_BASE; + break; + case 3: + ds->regs = (struct mcspi *)OMAP3_MCSPI4_BASE; + break; + default: + printf("SPI error: unsupported bus %i. \ + Supported busses 0 - 3\n", bus); + return NULL; + } + ds->slave.bus = bus; + + if (((bus == 0) && (cs > 3)) || + ((bus == 1) && (cs > 1)) || + ((bus == 2) && (cs > 1)) || + ((bus == 3) && (cs > 0))) { + printf("SPI error: unsupported chip select %i \ + on bus %i\n", cs, bus); + return NULL; + } + ds->slave.cs = cs; + + if (max_hz > OMAP3_MCSPI_MAX_FREQ) { + printf("SPI error: unsupported frequency %i Hz. \ + Max frequency is 48 Mhz\n", max_hz); + return NULL; + } + ds->freq = max_hz; + + if (mode > SPI_MODE_3) { + printf("SPI error: unsupported SPI mode %i\n", mode); + return NULL; + } + ds->mode = mode; + + return &ds->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + + free(ds); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + unsigned int conf, div = 0; + + /* McSPI global module configuration */ + + /* + * setup when switching from (reset default) slave mode + * to single-channel master mode + */ + spi_reset(ds); + conf = readl(&ds->regs->modulctrl); + conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS); + conf |= OMAP3_MCSPI_MODULCTRL_SINGLE; + writel(conf, &ds->regs->modulctrl); + + /* McSPI individual channel configuration */ + + /* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */ + if (ds->freq) { + while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div)) + > ds->freq) + div++; + } else + div = 0xC; + + conf = readl(&ds->regs->channel[ds->slave.cs].chconf); + + /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS + * REVISIT: this controller could support SPI_3WIRE mode. + */ + conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1); + conf |= OMAP3_MCSPI_CHCONF_DPE0; + + /* wordlength */ + conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK; + conf |= (WORD_LEN - 1) << 7; + + /* set chipselect polarity; manage with FORCE */ + if (!(ds->mode & SPI_CS_HIGH)) + conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */ + else + conf &= ~OMAP3_MCSPI_CHCONF_EPOL; + + /* set clock divisor */ + conf &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK; + conf |= div << 2; + + /* set SPI mode 0..3 */ + if (ds->mode & SPI_CPOL) + conf |= OMAP3_MCSPI_CHCONF_POL; + else + conf &= ~OMAP3_MCSPI_CHCONF_POL; + if (ds->mode & SPI_CPHA) + conf |= OMAP3_MCSPI_CHCONF_PHA; + else + conf &= ~OMAP3_MCSPI_CHCONF_PHA; + + /* Transmit & receive mode */ + conf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; + + writel(conf, &ds->regs->channel[ds->slave.cs].chconf); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + + /* Reset the SPI hardware */ + spi_reset(ds); +} + +int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp, + unsigned long flags) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + int i; + int timeout = SPI_WAIT_TIMEOUT; + int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); + + if (flags & SPI_XFER_BEGIN) + writel(OMAP3_MCSPI_CHCTRL_EN, + &ds->regs->channel[ds->slave.cs].chctrl); + + chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; + chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY; + chconf |= OMAP3_MCSPI_CHCONF_FORCE; + writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); + + for (i = 0; i < len; i++) { + /* wait till TX register is empty (TXS == 1) */ + 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); + } + + if (flags & SPI_XFER_END) { + /* wait to finish of transfer */ + while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & + OMAP3_MCSPI_CHSTAT_EOT)); + + 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 omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp, + unsigned long flags) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + int i; + int timeout = SPI_WAIT_TIMEOUT; + int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); + + if (flags & SPI_XFER_BEGIN) + writel(OMAP3_MCSPI_CHCTRL_EN, + &ds->regs->channel[ds->slave.cs].chctrl); + + chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; + chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY; + chconf |= OMAP3_MCSPI_CHCONF_FORCE; + writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); + + writel(0, &ds->regs->channel[ds->slave.cs].tx); + + for (i = 0; i < len; i++) { + /* Wait till RX register contains 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 (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) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + unsigned int len; + const u8 *txp = dout; + u8 *rxp = din; + int ret = -1; + + if (bitlen % 8) + return -1; + + len = bitlen / 8; + + if (bitlen == 0) { /* only change CS */ + int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); + + if (flags & SPI_XFER_BEGIN) { + writel(OMAP3_MCSPI_CHCTRL_EN, + &ds->regs->channel[ds->slave.cs].chctrl); + chconf |= OMAP3_MCSPI_CHCONF_FORCE; + writel(chconf, + &ds->regs->channel[ds->slave.cs].chconf); + } + 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); + } + ret = 0; + } else { + if (dout != NULL) + ret = omap3_spi_write(slave, len, txp, flags); + + if (din != NULL) + ret = omap3_spi_read(slave, len, rxp, flags); + } + return ret; +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return 1; +} + +void spi_cs_activate(struct spi_slave *slave) +{ +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ +} diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h new file mode 100644 index 00000000000..b8e3a4c44c4 --- /dev/null +++ b/drivers/spi/omap3_spi.h @@ -0,0 +1,117 @@ +/* + * Register definitions for the OMAP3 McSPI Controller + * + * Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com> + * + * Parts taken from linux/drivers/spi/omap2_mcspi.c + * Copyright (C) 2005, 2006 Nokia Corporation + * + * Modified by Ruslan Araslanov <ruslan.araslanov@vitecmm.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 + */ + +#ifndef _OMAP3_SPI_H_ +#define _OMAP3_SPI_H_ + +#define OMAP3_MCSPI1_BASE 0x48098000 +#define OMAP3_MCSPI2_BASE 0x4809A000 +#define OMAP3_MCSPI3_BASE 0x480B8000 +#define OMAP3_MCSPI4_BASE 0x480BA000 + +#define OMAP3_MCSPI_MAX_FREQ 48000000 + +/* OMAP3 McSPI registers */ +struct mcspi_channel { + unsigned int chconf; /* 0x2C, 0x40, 0x54, 0x68 */ + unsigned int chstat; /* 0x30, 0x44, 0x58, 0x6C */ + unsigned int chctrl; /* 0x34, 0x48, 0x5C, 0x70 */ + unsigned int tx; /* 0x38, 0x4C, 0x60, 0x74 */ + unsigned int rx; /* 0x3C, 0x50, 0x64, 0x78 */ +}; + +struct mcspi { + unsigned char res1[0x10]; + unsigned int sysconfig; /* 0x10 */ + unsigned int sysstatus; /* 0x14 */ + unsigned int irqstatus; /* 0x18 */ + unsigned int irqenable; /* 0x1C */ + unsigned int wakeupenable; /* 0x20 */ + unsigned int syst; /* 0x24 */ + unsigned int modulctrl; /* 0x28 */ + struct mcspi_channel channel[4]; /* channel0: 0x2C - 0x3C, bus 0 & 1 & 2 & 3 */ + /* channel1: 0x40 - 0x50, bus 0 & 1 */ + /* channel2: 0x54 - 0x64, bus 0 & 1 */ + /* channel3: 0x68 - 0x78, bus 0 */ +}; + +/* per-register bitmasks */ +#define OMAP3_MCSPI_SYSCONFIG_SMARTIDLE (2 << 3) +#define OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP (1 << 2) +#define OMAP3_MCSPI_SYSCONFIG_AUTOIDLE (1 << 0) +#define OMAP3_MCSPI_SYSCONFIG_SOFTRESET (1 << 1) + +#define OMAP3_MCSPI_SYSSTATUS_RESETDONE (1 << 0) + +#define OMAP3_MCSPI_MODULCTRL_SINGLE (1 << 0) +#define OMAP3_MCSPI_MODULCTRL_MS (1 << 2) +#define OMAP3_MCSPI_MODULCTRL_STEST (1 << 3) + +#define OMAP3_MCSPI_CHCONF_PHA (1 << 0) +#define OMAP3_MCSPI_CHCONF_POL (1 << 1) +#define OMAP3_MCSPI_CHCONF_CLKD_MASK (0x0f << 2) +#define OMAP3_MCSPI_CHCONF_EPOL (1 << 6) +#define OMAP3_MCSPI_CHCONF_WL_MASK (0x1f << 7) +#define OMAP3_MCSPI_CHCONF_TRM_RX_ONLY (0x01 << 12) +#define OMAP3_MCSPI_CHCONF_TRM_TX_ONLY (0x02 << 12) +#define OMAP3_MCSPI_CHCONF_TRM_MASK (0x03 << 12) +#define OMAP3_MCSPI_CHCONF_DMAW (1 << 14) +#define OMAP3_MCSPI_CHCONF_DMAR (1 << 15) +#define OMAP3_MCSPI_CHCONF_DPE0 (1 << 16) +#define OMAP3_MCSPI_CHCONF_DPE1 (1 << 17) +#define OMAP3_MCSPI_CHCONF_IS (1 << 18) +#define OMAP3_MCSPI_CHCONF_TURBO (1 << 19) +#define OMAP3_MCSPI_CHCONF_FORCE (1 << 20) + +#define OMAP3_MCSPI_CHSTAT_RXS (1 << 0) +#define OMAP3_MCSPI_CHSTAT_TXS (1 << 1) +#define OMAP3_MCSPI_CHSTAT_EOT (1 << 2) + +#define OMAP3_MCSPI_CHCTRL_EN (1 << 0) + +#define OMAP3_MCSPI_WAKEUPENABLE_WKEN (1 << 0) + +struct omap3_spi_slave { + struct spi_slave slave; + struct mcspi *regs; + unsigned int freq; + unsigned int mode; +}; + +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_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, + unsigned long flags); + +#endif /* _OMAP3_SPI_H_ */ |