summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorBen Warren <biggerbadderben@gmail.com>2008-06-08 22:04:22 -0700
committerBen Warren <biggerbadderben@gmail.com>2008-06-08 22:04:22 -0700
commite44f3ea4e801d37ef293284cb57b9637382f211a (patch)
tree09e6e9e68764e65ef7115db18319ec7e57c90b41 /drivers
parent0e38c938ed4bcadb4f4fc1419a541431e94fc202 (diff)
parent8155efbd7ae9c65564ca98affe94631d612ae088 (diff)
Merge branch 'master' of git://git.denx.de/u-boot
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/lm75.c13
-rw-r--r--drivers/mtd/cfi_flash.c2
-rw-r--r--drivers/mtd/nand/nand_util.c7
-rw-r--r--drivers/mtd/spi/Makefile47
-rw-r--r--drivers/mtd/spi/atmel.c362
-rw-r--r--drivers/mtd/spi/spi_flash.c162
-rw-r--r--drivers/mtd/spi/spi_flash_internal.h45
-rw-r--r--drivers/net/dm9000x.c2
-rw-r--r--drivers/net/macb.c6
-rw-r--r--drivers/rtc/ds1306.c67
-rw-r--r--drivers/rtc/mc13783-rtc.c43
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/atmel_spi.c210
-rw-r--r--drivers/spi/atmel_spi.h95
-rw-r--r--drivers/spi/mpc8xxx_spi.c54
-rw-r--r--drivers/spi/mxc_spi.c88
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/atmel_lcdfb.c160
18 files changed, 1302 insertions, 63 deletions
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index e29b29440f..c34851725e 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -47,6 +47,19 @@ int dtt_read(int sensor, int reg)
int dlen;
uchar data[2];
+#ifdef CONFIG_DTT_AD7414
+ /*
+ * On AD7414 the first value upon bootup is not read correctly.
+ * This is most likely because of the 800ms update time of the
+ * temp register in normal update mode. To get current values
+ * each time we issue the "dtt" command including upon powerup
+ * we switch into one-short mode.
+ *
+ * Issue one-shot mode command
+ */
+ dtt_write(sensor, DTT_CONFIG, 0x64);
+#endif
+
/*
* Validate 'reg' param
*/
diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c
index 68ab55f8a5..d84f0fc55e 100644
--- a/drivers/mtd/cfi_flash.c
+++ b/drivers/mtd/cfi_flash.c
@@ -1720,6 +1720,8 @@ ulong flash_get_size (ulong base, int banknum)
int erase_region_count;
struct cfi_qry qry;
+ memset(&qry, 0, sizeof(qry));
+
info->ext_addr = 0;
info->cfi_version = 0;
#ifdef CFG_FLASH_PROTECTION
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c
index 6c5624a49a..c82f77b555 100644
--- a/drivers/mtd/nand/nand_util.c
+++ b/drivers/mtd/nand/nand_util.c
@@ -153,6 +153,13 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
priv_nand->bbt = NULL;
}
+ if (erase_length < meminfo->erasesize) {
+ printf("Warning: Erase size 0x%08x smaller than one " \
+ "erase block 0x%08x\n",erase_length, meminfo->erasesize);
+ printf(" Erasing 0x%08x instead\n", meminfo->erasesize);
+ erase_length = meminfo->erasesize;
+ }
+
for (;
erase.addr < opts->offset + erase_length;
erase.addr += meminfo->erasesize) {
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile
new file mode 100644
index 0000000000..af6af97d11
--- /dev/null
+++ b/drivers/mtd/spi/Makefile
@@ -0,0 +1,47 @@
+#
+# (C) Copyright 2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# 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 $(TOPDIR)/config.mk
+
+LIB := $(obj)libspi_flash.a
+
+COBJS-$(CONFIG_SPI_FLASH) += spi_flash.o
+COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o
+
+COBJS := $(COBJS-y)
+SRCS := $(COBJS:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS))
+
+all: $(LIB)
+
+$(LIB): $(obj).depend $(OBJS)
+ $(AR) $(ARFLAGS) $@ $(OBJS)
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/mtd/spi/atmel.c b/drivers/mtd/spi/atmel.c
new file mode 100644
index 0000000000..fb7a4a939b
--- /dev/null
+++ b/drivers/mtd/spi/atmel.c
@@ -0,0 +1,362 @@
+/*
+ * Atmel SPI DataFlash support
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ */
+#define DEBUG
+#include <common.h>
+#include <malloc.h>
+#include <spi_flash.h>
+
+#include "spi_flash_internal.h"
+
+/* AT45-specific commands */
+#define CMD_AT45_READ_STATUS 0xd7
+#define CMD_AT45_ERASE_PAGE 0x81
+#define CMD_AT45_LOAD_PROG_BUF1 0x82
+#define CMD_AT45_LOAD_BUF1 0x84
+#define CMD_AT45_LOAD_PROG_BUF2 0x85
+#define CMD_AT45_LOAD_BUF2 0x87
+#define CMD_AT45_PROG_BUF1 0x88
+#define CMD_AT45_PROG_BUF2 0x89
+
+/* AT45 status register bits */
+#define AT45_STATUS_P2_PAGE_SIZE (1 << 0)
+#define AT45_STATUS_READY (1 << 7)
+
+/* DataFlash family IDs, as obtained from the second idcode byte */
+#define DF_FAMILY_AT26F 0
+#define DF_FAMILY_AT45 1
+#define DF_FAMILY_AT26DF 2 /* AT25DF and AT26DF */
+
+struct atmel_spi_flash_params {
+ u8 idcode1;
+ /* Log2 of page size in power-of-two mode */
+ u8 l2_page_size;
+ u8 pages_per_block;
+ u8 blocks_per_sector;
+ u8 nr_sectors;
+ const char *name;
+};
+
+struct atmel_spi_flash {
+ const struct atmel_spi_flash_params *params;
+ struct spi_flash flash;
+};
+
+static inline struct atmel_spi_flash *
+to_atmel_spi_flash(struct spi_flash *flash)
+{
+ return container_of(flash, struct atmel_spi_flash, flash);
+}
+
+static const struct atmel_spi_flash_params atmel_spi_flash_table[] = {
+ {
+ .idcode1 = 0x28,
+ .l2_page_size = 10,
+ .pages_per_block = 8,
+ .blocks_per_sector = 32,
+ .nr_sectors = 32,
+ .name = "AT45DB642D",
+ },
+};
+
+static int at45_wait_ready(struct spi_flash *flash, unsigned long timeout)
+{
+ struct spi_slave *spi = flash->spi;
+ unsigned long timebase;
+ int ret;
+ u8 cmd = CMD_AT45_READ_STATUS;
+ u8 status;
+
+ timebase = get_timer(0);
+
+ ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN);
+ if (ret)
+ return -1;
+
+ do {
+ ret = spi_xfer(spi, 8, NULL, &status, 0);
+ if (ret)
+ return -1;
+
+ if (status & AT45_STATUS_READY)
+ break;
+ } while (get_timer(timebase) < timeout);
+
+ /* Deactivate CS */
+ spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END);
+
+ if (status & AT45_STATUS_READY)
+ return 0;
+
+ /* Timed out */
+ return -1;
+}
+
+/*
+ * Assemble the address part of a command for AT45 devices in
+ * non-power-of-two page size mode.
+ */
+static void at45_build_address(struct atmel_spi_flash *asf, u8 *cmd, u32 offset)
+{
+ unsigned long page_addr;
+ unsigned long byte_addr;
+ unsigned long page_size;
+ unsigned int page_shift;
+
+ /*
+ * The "extra" space per page is the power-of-two page size
+ * divided by 32.
+ */
+ page_shift = asf->params->l2_page_size;
+ page_size = (1 << page_shift) + (1 << (page_shift - 5));
+ page_shift++;
+ page_addr = offset / page_size;
+ byte_addr = offset % page_size;
+
+ cmd[0] = page_addr >> (16 - page_shift);
+ cmd[1] = page_addr << (page_shift - 8) | (byte_addr >> 8);
+ cmd[2] = byte_addr;
+}
+
+static int dataflash_read_fast_p2(struct spi_flash *flash,
+ u32 offset, size_t len, void *buf)
+{
+ u8 cmd[5];
+
+ cmd[0] = CMD_READ_ARRAY_FAST;
+ cmd[1] = offset >> 16;
+ cmd[2] = offset >> 8;
+ cmd[3] = offset;
+ cmd[4] = 0x00;
+
+ return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len);
+}
+
+static int dataflash_read_fast_at45(struct spi_flash *flash,
+ u32 offset, size_t len, void *buf)
+{
+ struct atmel_spi_flash *asf = to_atmel_spi_flash(flash);
+ u8 cmd[5];
+
+ cmd[0] = CMD_READ_ARRAY_FAST;
+ at45_build_address(asf, cmd + 1, offset);
+ cmd[4] = 0x00;
+
+ return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len);
+}
+
+static int dataflash_write_at45(struct spi_flash *flash,
+ u32 offset, size_t len, const void *buf)
+{
+ struct atmel_spi_flash *asf = to_atmel_spi_flash(flash);
+ unsigned long page_addr;
+ unsigned long byte_addr;
+ unsigned long page_size;
+ unsigned int page_shift;
+ size_t chunk_len;
+ size_t actual;
+ int ret;
+ u8 cmd[4];
+
+ page_shift = asf->params->l2_page_size;
+ page_size = (1 << page_shift) + (1 << (page_shift - 5));
+ page_shift++;
+ 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;
+ }
+
+ for (actual = 0; actual < len; actual += chunk_len) {
+ chunk_len = min(len - actual, page_size - byte_addr);
+
+ /* Use the same address bits for both commands */
+ cmd[0] = CMD_AT45_LOAD_BUF1;
+ cmd[1] = page_addr >> (16 - page_shift);
+ cmd[2] = page_addr << (page_shift - 8) | (byte_addr >> 8);
+ cmd[3] = byte_addr;
+
+ ret = spi_flash_cmd_write(flash->spi, cmd, 4,
+ buf + actual, chunk_len);
+ if (ret < 0) {
+ debug("SF: Loading AT45 buffer failed\n");
+ goto out;
+ }
+
+ cmd[0] = CMD_AT45_PROG_BUF1;
+ ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0);
+ if (ret < 0) {
+ debug("SF: AT45 page programming failed\n");
+ goto out;
+ }
+
+ ret = at45_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
+ if (ret < 0) {
+ debug("SF: AT45 page programming timed out\n");
+ goto out;
+ }
+
+ page_addr++;
+ byte_addr = 0;
+ }
+
+ debug("SF: AT45: Successfully programmed %u bytes @ 0x%x\n",
+ len, offset);
+ ret = 0;
+
+out:
+ spi_release_bus(flash->spi);
+ return ret;
+}
+
+int dataflash_erase_at45(struct spi_flash *flash, u32 offset, size_t len)
+{
+ struct atmel_spi_flash *asf = to_atmel_spi_flash(flash);
+ unsigned long page_addr;
+ unsigned long page_size;
+ unsigned int page_shift;
+ size_t actual;
+ int ret;
+ u8 cmd[4];
+
+ /*
+ * TODO: This function currently uses page erase only. We can
+ * probably speed things up by using block and/or sector erase
+ * when possible.
+ */
+
+ page_shift = asf->params->l2_page_size;
+ page_size = (1 << page_shift) + (1 << (page_shift - 5));
+ page_shift++;
+ page_addr = offset / page_size;
+
+ if (offset % page_size || len % page_size) {
+ debug("SF: Erase offset/length not multiple of page size\n");
+ return -1;
+ }
+
+ cmd[0] = CMD_AT45_ERASE_PAGE;
+ cmd[3] = 0x00;
+
+ ret = spi_claim_bus(flash->spi);
+ if (ret) {
+ debug("SF: Unable to claim SPI bus\n");
+ return ret;
+ }
+
+ for (actual = 0; actual < len; actual += page_size) {
+ cmd[1] = page_addr >> (16 - page_shift);
+ cmd[2] = page_addr << (page_shift - 8);
+
+ ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0);
+ if (ret < 0) {
+ debug("SF: AT45 page erase failed\n");
+ goto out;
+ }
+
+ ret = at45_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);
+ if (ret < 0) {
+ debug("SF: AT45 page erase timed out\n");
+ goto out;
+ }
+
+ page_addr++;
+ }
+
+ debug("SF: AT45: Successfully erased %u bytes @ 0x%x\n",
+ len, offset);
+ ret = 0;
+
+out:
+ spi_release_bus(flash->spi);
+ return ret;
+}
+
+struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
+{
+ const struct atmel_spi_flash_params *params;
+ unsigned long page_size;
+ unsigned int family;
+ struct atmel_spi_flash *asf;
+ unsigned int i;
+ int ret;
+ u8 status;
+
+ for (i = 0; i < ARRAY_SIZE(atmel_spi_flash_table); i++) {
+ params = &atmel_spi_flash_table[i];
+ if (params->idcode1 == idcode[1])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(atmel_spi_flash_table)) {
+ debug("SF: Unsupported DataFlash ID %02x\n",
+ idcode[1]);
+ return NULL;
+ }
+
+ asf = malloc(sizeof(struct atmel_spi_flash));
+ if (!asf) {
+ debug("SF: Failed to allocate memory\n");
+ return NULL;
+ }
+
+ asf->params = params;
+ asf->flash.spi = spi;
+ asf->flash.name = params->name;
+
+ /* Assuming power-of-two page size initially. */
+ page_size = 1 << params->l2_page_size;
+
+ family = idcode[1] >> 5;
+
+ switch (family) {
+ case DF_FAMILY_AT45:
+ /*
+ * AT45 chips have configurable page size. The status
+ * register indicates which configuration is active.
+ */
+ ret = spi_flash_cmd(spi, CMD_AT45_READ_STATUS, &status, 1);
+ if (ret)
+ goto err;
+
+ debug("SF: AT45 status register: %02x\n", status);
+
+ if (!(status & AT45_STATUS_P2_PAGE_SIZE)) {
+ asf->flash.read = dataflash_read_fast_at45;
+ asf->flash.write = dataflash_write_at45;
+ asf->flash.erase = dataflash_erase_at45;
+ page_size += 1 << (params->l2_page_size - 5);
+ } else {
+ asf->flash.read = dataflash_read_fast_p2;
+ }
+
+ break;
+
+ case DF_FAMILY_AT26F:
+ case DF_FAMILY_AT26DF:
+ asf->flash.read = dataflash_read_fast_p2;
+ break;
+
+ default:
+ debug("SF: Unsupported DataFlash family %u\n", family);
+ goto err;
+ }
+
+ asf->flash.size = page_size * params->pages_per_block
+ * params->blocks_per_sector
+ * params->nr_sectors;
+
+ debug("SF: Detected %s with page size %u, total %u bytes\n",
+ params->name, page_size, asf->flash.size);
+
+ return &asf->flash;
+
+err:
+ free(asf);
+ return NULL;
+}
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
new file mode 100644
index 0000000000..d581cb3e89
--- /dev/null
+++ b/drivers/mtd/spi/spi_flash.c
@@ -0,0 +1,162 @@
+/*
+ * SPI flash interface
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ */
+#define DEBUG
+#include <common.h>
+#include <malloc.h>
+#include <spi.h>
+#include <spi_flash.h>
+
+#include "spi_flash_internal.h"
+
+int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len)
+{
+ unsigned long flags = SPI_XFER_BEGIN;
+ int ret;
+
+ if (len == 0)
+ flags |= SPI_XFER_END;
+
+ ret = spi_xfer(spi, 8, &cmd, NULL, flags);
+ if (ret) {
+ debug("SF: Failed to send command %02x: %d\n", cmd, ret);
+ return ret;
+ }
+
+ if (len) {
+ ret = spi_xfer(spi, len * 8, NULL, response, SPI_XFER_END);
+ if (ret)
+ debug("SF: Failed to read response (%zu bytes): %d\n",
+ len, ret);
+ }
+
+ return ret;
+}
+
+int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
+ size_t cmd_len, void *data, size_t data_len)
+{
+ unsigned long flags = SPI_XFER_BEGIN;
+ int ret;
+
+ if (data_len == 0)
+ flags |= SPI_XFER_END;
+
+ ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags);
+ if (ret) {
+ debug("SF: Failed to send read command (%zu bytes): %d\n",
+ cmd_len, ret);
+ } else if (data_len != 0) {
+ ret = spi_xfer(spi, data_len * 8, NULL, data, SPI_XFER_END);
+ if (ret)
+ debug("SF: Failed to read %zu bytes of data: %d\n",
+ data_len, ret);
+ }
+
+ return ret;
+}
+
+int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
+ const void *data, size_t data_len)
+{
+ unsigned long flags = SPI_XFER_BEGIN;
+ int ret;
+
+ if (data_len == 0)
+ flags |= SPI_XFER_END;
+
+ ret = spi_xfer(spi, cmd_len * 8, cmd, NULL, flags);
+ if (ret) {
+ debug("SF: Failed to send read command (%zu bytes): %d\n",
+ cmd_len, ret);
+ } else if (data_len != 0) {
+ ret = spi_xfer(spi, data_len * 8, data, NULL, SPI_XFER_END);
+ if (ret)
+ debug("SF: Failed to read %zu bytes of data: %d\n",
+ data_len, ret);
+ }
+
+ return ret;
+}
+
+
+int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
+ size_t cmd_len, void *data, size_t data_len)
+{
+ struct spi_slave *spi = flash->spi;
+ int ret;
+
+ spi_claim_bus(spi);
+ ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len);
+ spi_release_bus(spi);
+
+ return ret;
+}
+
+struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
+ unsigned int max_hz, unsigned int spi_mode)
+{
+ struct spi_slave *spi;
+ struct spi_flash *flash;
+ int ret;
+ u8 idcode[3];
+
+ spi = spi_setup_slave(bus, cs, max_hz, spi_mode);
+ if (!spi) {
+ debug("SF: Failed to set up slave\n");
+ return NULL;
+ }
+
+ ret = spi_claim_bus(spi);
+ if (ret) {
+ debug("SF: Failed to claim SPI bus: %d\n", ret);
+ goto err_claim_bus;
+ }
+
+ /* Read the ID codes */
+ ret = spi_flash_cmd(spi, CMD_READ_ID, &idcode, sizeof(idcode));
+ if (ret)
+ goto err_read_id;
+
+ debug("SF: Got idcode %02x %02x %02x\n", idcode[0],
+ idcode[1], idcode[2]);
+
+ switch (idcode[0]) {
+#ifdef CONFIG_SPI_FLASH_SPANSION
+ case 0x01:
+ flash = spi_flash_probe_spansion(spi, idcode);
+ break;
+#endif
+#ifdef CONFIG_SPI_FLASH_ATMEL
+ case 0x1F:
+ flash = spi_flash_probe_atmel(spi, idcode);
+ break;
+#endif
+ default:
+ debug("SF: Unsupported manufacturer %02X\n", idcode[0]);
+ flash = NULL;
+ break;
+ }
+
+ if (!flash)
+ goto err_manufacturer_probe;
+
+ spi_release_bus(spi);
+
+ return flash;
+
+err_manufacturer_probe:
+err_read_id:
+ spi_release_bus(spi);
+err_claim_bus:
+ spi_free_slave(spi);
+ return NULL;
+}
+
+void spi_flash_free(struct spi_flash *flash)
+{
+ spi_free_slave(flash->spi);
+ free(flash);
+}
diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h
new file mode 100644
index 0000000000..14380500aa
--- /dev/null
+++ b/drivers/mtd/spi/spi_flash_internal.h
@@ -0,0 +1,45 @@
+/*
+ * SPI flash internal definitions
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ */
+
+/* Common parameters */
+#define SPI_FLASH_PROG_TIMEOUT ((10 * CFG_HZ) / 1000)
+#define SPI_FLASH_PAGE_ERASE_TIMEOUT ((50 * CFG_HZ) / 1000)
+#define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CFG_HZ)
+
+/* Common commands */
+#define CMD_READ_ID 0x9f
+
+#define CMD_READ_ARRAY_SLOW 0x03
+#define CMD_READ_ARRAY_FAST 0x0b
+#define CMD_READ_ARRAY_LEGACY 0xe8
+
+/* Send a single-byte command to the device and read the response */
+int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len);
+
+/*
+ * Send a multi-byte command to the device and read the response. Used
+ * for flash array reads, etc.
+ */
+int spi_flash_cmd_read(struct spi_slave *spi, const u8 *cmd,
+ size_t cmd_len, void *data, size_t data_len);
+
+/*
+ * Send a multi-byte command to the device followed by (optional)
+ * data. Used for programming the flash array, etc.
+ */
+int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len,
+ const void *data, size_t data_len);
+
+/*
+ * Same as spi_flash_cmd_read() except it also claims/releases the SPI
+ * bus. Used as common part of the ->read() operation.
+ */
+int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
+ size_t cmd_len, void *data, size_t data_len);
+
+/* 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);
diff --git a/drivers/net/dm9000x.c b/drivers/net/dm9000x.c
index c2144d9bc6..fa95a730c3 100644
--- a/drivers/net/dm9000x.c
+++ b/drivers/net/dm9000x.c
@@ -474,8 +474,10 @@ eth_init(bd_t * bd)
DM9000_iow(DM9000_ISR, 0x0f);
/* Set Node address */
+#ifndef CONFIG_AT91SAM9261EK
for (i = 0; i < 6; i++)
((u16 *) bd->bi_enetaddr)[i] = read_srom_word(i);
+#endif
if (is_zero_ether_addr(bd->bi_enetaddr) ||
is_multicast_ether_addr(bd->bi_enetaddr)) {
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 703784ee0d..e5733f6e5b 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -417,13 +417,15 @@ static int macb_init(struct eth_device *netdev, bd_t *bd)
/* choose RMII or MII mode. This depends on the board */
#ifdef CONFIG_RMII
-#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260)
+#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \
+ defined(CONFIG_AT91SAM9263)
macb_writel(macb, USRIO, MACB_BIT(RMII) | MACB_BIT(CLKEN));
#else
macb_writel(macb, USRIO, 0);
#endif
#else
-#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260)
+#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \
+ defined(CONFIG_AT91SAM9263)
macb_writel(macb, USRIO, MACB_BIT(CLKEN));
#else
macb_writel(macb, USRIO, MACB_BIT(MII));
diff --git a/drivers/rtc/ds1306.c b/drivers/rtc/ds1306.c
index 1c8ac7f292..29854fc7c4 100644
--- a/drivers/rtc/ds1306.c
+++ b/drivers/rtc/ds1306.c
@@ -62,13 +62,6 @@
#define RTC_USER_RAM_BASE 0x20
-/*
- * External table of chip select functions (see the appropriate board
- * support for the actual definition of the table).
- */
-extern spi_chipsel_type spi_chipsel[];
-extern int spi_chipsel_cnt;
-
static unsigned int bin2bcd (unsigned int n);
static unsigned char bcd2bin (unsigned char c);
@@ -305,11 +298,29 @@ void rtc_reset (void)
static unsigned char rtc_read (unsigned char reg);
static void rtc_write (unsigned char reg, unsigned char val);
+static struct spi_slave *slave;
+
/* read clock time from DS1306 and return it in *tmp */
int rtc_get (struct rtc_time *tmp)
{
unsigned char sec, min, hour, mday, wday, mon, year;
+ /*
+ * Assuming Vcc = 2.0V (lowest speed)
+ *
+ * REVISIT: If we add an rtc_init() function we can do this
+ * step just once.
+ */
+ if (!slave) {
+ slave = spi_setup_slave(0, CFG_SPI_RTC_DEVID, 600000,
+ SPI_MODE_3 | SPI_CS_HIGH);
+ if (!slave)
+ return;
+ }
+
+ if (spi_claim_bus(slave))
+ return;
+
sec = rtc_read (RTC_SECONDS);
min = rtc_read (RTC_MINUTES);
hour = rtc_read (RTC_HOURS);
@@ -318,6 +329,8 @@ int rtc_get (struct rtc_time *tmp)
mon = rtc_read (RTC_MONTH);
year = rtc_read (RTC_YEAR);
+ spi_release_bus(slave);
+
debug ("Get RTC year: %02x mon: %02x mday: %02x wday: %02x "
"hr: %02x min: %02x sec: %02x\n",
year, mon, mday, wday, hour, min, sec);
@@ -360,6 +373,17 @@ int rtc_get (struct rtc_time *tmp)
/* set clock time from *tmp in DS1306 RTC */
void rtc_set (struct rtc_time *tmp)
{
+ /* Assuming Vcc = 2.0V (lowest speed) */
+ if (!slave) {
+ slave = spi_setup_slave(0, CFG_SPI_RTC_DEVID, 600000,
+ SPI_MODE_3 | SPI_CS_HIGH);
+ if (!slave)
+ return;
+ }
+
+ if (spi_claim_bus(slave))
+ return;
+
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);
@@ -371,6 +395,8 @@ void rtc_set (struct rtc_time *tmp)
rtc_write (RTC_DATE_OF_MONTH, bin2bcd (tmp->tm_mday));
rtc_write (RTC_MONTH, bin2bcd (tmp->tm_mon));
rtc_write (RTC_YEAR, bin2bcd (tmp->tm_year - 2000));
+
+ spi_release_bus(slave);
}
/* ------------------------------------------------------------------------- */
@@ -378,6 +404,17 @@ void rtc_set (struct rtc_time *tmp)
/* reset the DS1306 */
void rtc_reset (void)
{
+ /* Assuming Vcc = 2.0V (lowest speed) */
+ if (!slave) {
+ slave = spi_setup_slave(0, CFG_SPI_RTC_DEVID, 600000,
+ SPI_MODE_3 | SPI_CS_HIGH);
+ if (!slave)
+ return;
+ }
+
+ if (spi_claim_bus(slave))
+ return;
+
/* clear the control register */
rtc_write (RTC_CONTROL, 0x00); /* 1st step: reset WP */
rtc_write (RTC_CONTROL, 0x00); /* 2nd step: reset 1Hz, AIE1, AIE0 */
@@ -391,22 +428,18 @@ void rtc_reset (void)
rtc_write (RTC_HOURS_ALARM1, 0x00);
rtc_write (RTC_DAY_OF_WEEK_ALARM0, 0x00);
rtc_write (RTC_DAY_OF_WEEK_ALARM1, 0x00);
+
+ spi_release_bus(slave);
}
/* ------------------------------------------------------------------------- */
static unsigned char rtc_read (unsigned char reg)
{
- unsigned char dout[2]; /* SPI Output Data Bytes */
- unsigned char din[2]; /* SPI Input Data Bytes */
-
- dout[0] = reg;
+ int ret;
- if (spi_xfer (spi_chipsel[CFG_SPI_RTC_DEVID], 16, dout, din) != 0) {
- return 0;
- } else {
- return din[1];
- }
+ ret = spi_w8r8(slave, reg);
+ return ret < 0 ? 0 : ret;
}
/* ------------------------------------------------------------------------- */
@@ -419,7 +452,7 @@ static void rtc_write (unsigned char reg, unsigned char val)
dout[0] = 0x80 | reg;
dout[1] = val;
- spi_xfer (spi_chipsel[CFG_SPI_RTC_DEVID], 16, dout, din);
+ spi_xfer (slave, 16, dout, din, SPI_XFER_BEGIN | SPI_XFER_END);
}
#endif /* end of code exclusion (see #ifdef CONFIG_SXNI855T above) */
diff --git a/drivers/rtc/mc13783-rtc.c b/drivers/rtc/mc13783-rtc.c
index 35b1b8b254..b6e15014bb 100644
--- a/drivers/rtc/mc13783-rtc.c
+++ b/drivers/rtc/mc13783-rtc.c
@@ -24,34 +24,50 @@
#include <rtc.h>
#include <spi.h>
+static struct spi_slave *slave;
+
int rtc_get(struct rtc_time *rtc)
{
u32 day1, day2, time;
u32 reg;
int err, tim, i = 0;
- spi_select(1, 0, SPI_MODE_2 | SPI_CS_HIGH);
+ if (!slave) {
+ /* FIXME: Verify the max SCK rate */
+ slave = spi_setup_slave(1, 0, 1000000,
+ SPI_MODE_2 | SPI_CS_HIGH);
+ if (!slave)
+ return -1;
+ }
+
+ if (spi_claim_bus(slave))
+ return -1;
do {
reg = 0x2c000000;
- err = spi_xfer(0, 32, (uchar *)&reg, (uchar *)&day1);
+ err = spi_xfer(slave, 32, (uchar *)&reg, (uchar *)&day1,
+ SPI_XFER_BEGIN | SPI_XFER_END);
if (err)
return err;
reg = 0x28000000;
- err = spi_xfer(0, 32, (uchar *)&reg, (uchar *)&time);
+ err = spi_xfer(slave, 32, (uchar *)&reg, (uchar *)&time,
+ SPI_XFER_BEGIN | SPI_XFER_END);
if (err)
return err;
reg = 0x2c000000;
- err = spi_xfer(0, 32, (uchar *)&reg, (uchar *)&day2);
+ err = spi_xfer(slave, 32, (uchar *)&reg, (uchar *)&day2,
+ SPI_XFER_BEGIN | SPI_XFER_END);
if (err)
return err;
} while (day1 != day2 && i++ < 3);
+ spi_release_bus(slave);
+
tim = day1 * 86400 + time;
to_tm(tim, rtc);
@@ -65,16 +81,31 @@ void rtc_set(struct rtc_time *rtc)
{
u32 time, day, reg;
+ if (!slave) {
+ /* FIXME: Verify the max SCK rate */
+ slave = spi_setup_slave(1, 0, 1000000,
+ SPI_MODE_2 | SPI_CS_HIGH);
+ if (!slave)
+ return;
+ }
+
time = mktime(rtc->tm_year, rtc->tm_mon, rtc->tm_mday,
rtc->tm_hour, rtc->tm_min, rtc->tm_sec);
day = time / 86400;
time %= 86400;
+ if (spi_claim_bus(slave))
+ return;
+
reg = 0x2c000000 | day | 0x80000000;
- spi_xfer(0, 32, (uchar *)&reg, (uchar *)&day);
+ spi_xfer(slave, 32, (uchar *)&reg, (uchar *)&day,
+ SPI_XFER_BEGIN | SPI_XFER_END);
reg = 0x28000000 | time | 0x80000000;
- spi_xfer(0, 32, (uchar *)&reg, (uchar *)&time);
+ spi_xfer(slave, 32, (uchar *)&reg, (uchar *)&time,
+ SPI_XFER_BEGIN | SPI_XFER_END);
+
+ spi_release_bus(slave);
}
void rtc_reset(void)
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index bc8a104121..e66e0ee092 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libspi.a
COBJS-y += mpc8xxx_spi.o
+COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o
COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o
COBJS := $(COBJS-y)
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
new file mode 100644
index 0000000000..317c0b41b6
--- /dev/null
+++ b/drivers/spi/atmel_spi.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2007 Atmel 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 <spi.h>
+#include <malloc.h>
+
+#include <asm/io.h>
+
+#include <asm/arch/clk.h>
+#include <asm/arch/memory-map.h>
+
+#include "atmel_spi.h"
+
+void spi_init()
+{
+
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+ unsigned int max_hz, unsigned int mode)
+{
+ struct atmel_spi_slave *as;
+ unsigned int scbr;
+ u32 csrx;
+ void *regs;
+
+ if (cs > 3 || !spi_cs_is_valid(bus, cs))
+ return NULL;
+
+ switch (bus) {
+ case 0:
+ regs = (void *)SPI0_BASE;
+ break;
+#ifdef SPI1_BASE
+ case 1:
+ regs = (void *)SPI1_BASE;
+ break;
+#endif
+#ifdef SPI2_BASE
+ case 2:
+ regs = (void *)SPI2_BASE;
+ break;
+#endif
+#ifdef SPI3_BASE
+ case 3:
+ regs = (void *)SPI3_BASE;
+ break;
+#endif
+ default:
+ return NULL;
+ }
+
+
+ scbr = (get_spi_clk_rate(bus) + max_hz - 1) / max_hz;
+ if (scbr > ATMEL_SPI_CSRx_SCBR_MAX)
+ /* Too low max SCK rate */
+ return NULL;
+ if (scbr < 1)
+ scbr = 1;
+
+ csrx = ATMEL_SPI_CSRx_SCBR(scbr);
+ csrx |= ATMEL_SPI_CSRx_BITS(ATMEL_SPI_BITS_8);
+ if (!(mode & SPI_CPHA))
+ csrx |= ATMEL_SPI_CSRx_NCPHA;
+ if (mode & SPI_CPOL)
+ csrx |= ATMEL_SPI_CSRx_CPOL;
+
+ as = malloc(sizeof(struct atmel_spi_slave));
+ if (!as)
+ return NULL;
+
+ as->slave.bus = bus;
+ as->slave.cs = cs;
+ as->regs = regs;
+ as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS
+ | ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf);
+ spi_writel(as, CSR(cs), csrx);
+
+ return &as->slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ struct atmel_spi_slave *as = to_atmel_spi(slave);
+
+ free(as);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ struct atmel_spi_slave *as = to_atmel_spi(slave);
+
+ /* Enable the SPI hardware */
+ spi_writel(as, CR, ATMEL_SPI_CR_SPIEN);
+
+ /*
+ * Select the slave. This should set SCK to the correct
+ * initial state, etc.
+ */
+ spi_writel(as, MR, as->mr);
+
+ return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+ struct atmel_spi_slave *as = to_atmel_spi(slave);
+
+ /* Disable the SPI hardware */
+ spi_writel(as, CR, ATMEL_SPI_CR_SPIDIS);
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct atmel_spi_slave *as = to_atmel_spi(slave);
+ unsigned int len_tx;
+ unsigned int len_rx;
+ unsigned int len;
+ int ret;
+ u32 status;
+ const u8 *txp = dout;
+ u8 *rxp = din;
+ u8 value;
+
+ ret = 0;
+ if (bitlen == 0)
+ /* Finish any previously submitted transfers */
+ goto out;
+
+ /*
+ * TODO: The controller can do non-multiple-of-8 bit
+ * transfers, but this driver currently doesn't support it.
+ *
+ * It's also not clear how such transfers are supposed to be
+ * represented as a stream of bytes...this is a limitation of
+ * the current SPI interface.
+ */
+ if (bitlen % 8) {
+ /* Errors always terminate an ongoing transfer */
+ flags |= SPI_XFER_END;
+ goto out;
+ }
+
+ len = bitlen / 8;
+
+ /*
+ * The controller can do automatic CS control, but it is
+ * somewhat quirky, and it doesn't really buy us much anyway
+ * in the context of U-Boot.
+ */
+ if (flags & SPI_XFER_BEGIN)
+ spi_cs_activate(slave);
+
+ for (len_tx = 0, len_rx = 0; len_rx < len; ) {
+ status = spi_readl(as, SR);
+
+ if (status & ATMEL_SPI_SR_OVRES)
+ return -1;
+
+ if (len_tx < len && (status & ATMEL_SPI_SR_TDRE)) {
+ if (txp)
+ value = *txp++;
+ else
+ value = 0;
+ spi_writel(as, TDR, value);
+ len_tx++;
+ }
+ if (status & ATMEL_SPI_SR_RDRF) {
+ value = spi_readl(as, RDR);
+ if (rxp)
+ *rxp++ = value;
+ len_rx++;
+ }
+ }
+
+out:
+ if (flags & SPI_XFER_END) {
+ /*
+ * Wait until the transfer is completely done before
+ * we deactivate CS.
+ */
+ do {
+ status = spi_readl(as, SR);
+ } while (!(status & ATMEL_SPI_SR_TXEMPTY));
+
+ spi_cs_deactivate(slave);
+ }
+
+ return 0;
+}
diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h
new file mode 100644
index 0000000000..8b69a6d215
--- /dev/null
+++ b/drivers/spi/atmel_spi.h
@@ -0,0 +1,95 @@
+/*
+ * Register definitions for the Atmel AT32/AT91 SPI Controller
+ */
+
+/* Register offsets */
+#define ATMEL_SPI_CR 0x0000
+#define ATMEL_SPI_MR 0x0004
+#define ATMEL_SPI_RDR 0x0008
+#define ATMEL_SPI_TDR 0x000c
+#define ATMEL_SPI_SR 0x0010
+#define ATMEL_SPI_IER 0x0014
+#define ATMEL_SPI_IDR 0x0018
+#define ATMEL_SPI_IMR 0x001c
+#define ATMEL_SPI_CSR(x) (0x0030 + 4 * (x))
+#define ATMEL_SPI_VERSION 0x00fc
+
+/* Bits in CR */
+#define ATMEL_SPI_CR_SPIEN (1 << 0)
+#define ATMEL_SPI_CR_SPIDIS (1 << 1)
+#define ATMEL_SPI_CR_SWRST (1 << 7)
+#define ATMEL_SPI_CR_LASTXFER (1 << 24)
+
+/* Bits in MR */
+#define ATMEL_SPI_MR_MSTR (1 << 0)
+#define ATMEL_SPI_MR_PS (1 << 1)
+#define ATMEL_SPI_MR_PCSDEC (1 << 2)
+#define ATMEL_SPI_MR_FDIV (1 << 3)
+#define ATMEL_SPI_MR_MODFDIS (1 << 4)
+#define ATMEL_SPI_MR_LLB (1 << 7)
+#define ATMEL_SPI_MR_PCS(x) (((x) & 15) << 16)
+#define ATMEL_SPI_MR_DLYBCS(x) ((x) << 24)
+
+/* Bits in RDR */
+#define ATMEL_SPI_RDR_RD(x) (x)
+#define ATMEL_SPI_RDR_PCS(x) ((x) << 16)
+
+/* Bits in TDR */
+#define ATMEL_SPI_TDR_TD(x) (x)
+#define ATMEL_SPI_TDR_PCS(x) ((x) << 16)
+#define ATMEL_SPI_TDR_LASTXFER (1 << 24)
+
+/* Bits in SR/IER/IDR/IMR */
+#define ATMEL_SPI_SR_RDRF (1 << 0)
+#define ATMEL_SPI_SR_TDRE (1 << 1)
+#define ATMEL_SPI_SR_MODF (1 << 2)
+#define ATMEL_SPI_SR_OVRES (1 << 3)
+#define ATMEL_SPI_SR_ENDRX (1 << 4)
+#define ATMEL_SPI_SR_ENDTX (1 << 5)
+#define ATMEL_SPI_SR_RXBUFF (1 << 6)
+#define ATMEL_SPI_SR_TXBUFE (1 << 7)
+#define ATMEL_SPI_SR_NSSR (1 << 8)
+#define ATMEL_SPI_SR_TXEMPTY (1 << 9)
+#define ATMEL_SPI_SR_SPIENS (1 << 16)
+
+/* Bits in CSRx */
+#define ATMEL_SPI_CSRx_CPOL (1 << 0)
+#define ATMEL_SPI_CSRx_NCPHA (1 << 1)
+#define ATMEL_SPI_CSRx_CSAAT (1 << 3)
+#define ATMEL_SPI_CSRx_BITS(x) ((x) << 4)
+#define ATMEL_SPI_CSRx_SCBR(x) ((x) << 8)
+#define ATMEL_SPI_CSRx_SCBR_MAX 0xff
+#define ATMEL_SPI_CSRx_DLYBS(x) ((x) << 16)
+#define ATMEL_SPI_CSRx_DLYBCT(x) ((x) << 24)
+
+/* Bits in VERSION */
+#define ATMEL_SPI_VERSION_REV(x) ((x) << 0)
+#define ATMEL_SPI_VERSION_MFN(x) ((x) << 16)
+
+/* Constants for CSRx:BITS */
+#define ATMEL_SPI_BITS_8 0
+#define ATMEL_SPI_BITS_9 1
+#define ATMEL_SPI_BITS_10 2
+#define ATMEL_SPI_BITS_11 3
+#define ATMEL_SPI_BITS_12 4
+#define ATMEL_SPI_BITS_13 5
+#define ATMEL_SPI_BITS_14 6
+#define ATMEL_SPI_BITS_15 7
+#define ATMEL_SPI_BITS_16 8
+
+struct atmel_spi_slave {
+ struct spi_slave slave;
+ void *regs;
+ u32 mr;
+};
+
+static inline struct atmel_spi_slave *to_atmel_spi(struct spi_slave *slave)
+{
+ return container_of(slave, struct atmel_spi_slave, slave);
+}
+
+/* Register access macros */
+#define spi_readl(as, reg) \
+ readl(as->regs + ATMEL_SPI_##reg)
+#define spi_writel(as, reg, value) \
+ writel(value, as->regs + ATMEL_SPI_##reg)
diff --git a/drivers/spi/mpc8xxx_spi.c b/drivers/spi/mpc8xxx_spi.c
index 2fe838c45d..136fb50052 100644
--- a/drivers/spi/mpc8xxx_spi.c
+++ b/drivers/spi/mpc8xxx_spi.c
@@ -24,6 +24,7 @@
#include <common.h>
#if defined(CONFIG_MPC8XXX_SPI) && defined(CONFIG_HARD_SPI)
+#include <malloc.h>
#include <spi.h>
#include <asm/mpc8xxx_spi.h>
@@ -37,6 +38,34 @@
#define SPI_TIMEOUT 1000
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+ unsigned int max_hz, unsigned int mode)
+{
+ struct spi_slave *slave;
+
+ if (!spi_cs_is_valid(bus, cs))
+ return NULL;
+
+ slave = malloc(sizeof(struct spi_slave));
+ if (!slave)
+ return NULL;
+
+ slave->bus = bus;
+ slave->cs = cs;
+
+ /*
+ * TODO: Some of the code in spi_init() should probably move
+ * here, or into spi_claim_bus() below.
+ */
+
+ return slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ free(slave);
+}
+
void spi_init(void)
{
volatile spi8xxx_t *spi = &((immap_t *) (CFG_IMMR))->spi;
@@ -53,7 +82,18 @@ void spi_init(void)
spi->com = 0; /* LST bit doesn't do anything, so disregard */
}
-int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din)
+int spi_claim_bus(struct spi_slave *slave)
+{
+ return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
+ void *din, unsigned long flags)
{
volatile spi8xxx_t *spi = &((immap_t *) (CFG_IMMR))->spi;
unsigned int tmpdout, tmpdin, event;
@@ -61,11 +101,11 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din)
int tm, isRead = 0;
unsigned char charSize = 32;
- debug("spi_xfer: chipsel %08X dout %08X din %08X bitlen %d\n",
- (int)chipsel, *(uint *) dout, *(uint *) din, bitlen);
+ debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n",
+ slave->bus, slave->cs, *(uint *) dout, *(uint *) din, bitlen);
- if (chipsel != NULL)
- (*chipsel) (1); /* select the target chip */
+ if (flags & SPI_XFER_BEGIN)
+ spi_cs_activate(slave);
spi->event = 0xffffffff; /* Clear all SPI events */
@@ -135,8 +175,8 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din)
debug("*** spi_xfer: transfer ended. Value=%08x\n", tmpdin);
}
- if (chipsel != NULL)
- (*chipsel) (0); /* deselect the target chip */
+ if (flags & SPI_XFER_END)
+ spi_cs_deactivate(slave);
return 0;
}
diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c
index c166ec5023..5957ada3a4 100644
--- a/drivers/spi/mxc_spi.c
+++ b/drivers/spi/mxc_spi.c
@@ -19,6 +19,7 @@
*/
#include <common.h>
+#include <malloc.h>
#include <spi.h>
#include <asm/io.h>
@@ -61,17 +62,18 @@ static unsigned long spi_bases[] = {
0x53f84000,
};
-static unsigned long spi_base;
-
#endif
-spi_chipsel_type spi_chipsel[] = {
- (spi_chipsel_type)0,
- (spi_chipsel_type)1,
- (spi_chipsel_type)2,
- (spi_chipsel_type)3,
+struct mxc_spi_slave {
+ struct spi_slave slave;
+ unsigned long base;
+ u32 ctrl_reg;
};
-int spi_chipsel_cnt = sizeof(spi_chipsel) / sizeof(spi_chipsel[0]);
+
+static inline struct mxc_spi_slave *to_mxc_spi_slave(struct spi_slave *slave)
+{
+ return container_of(slave, struct mxc_spi_slave, slave);
+}
static inline u32 reg_read(unsigned long addr)
{
@@ -83,30 +85,31 @@ static inline void reg_write(unsigned long addr, u32 val)
*(volatile unsigned long*)addr = val;
}
-static u32 spi_xchg_single(u32 data, int bitlen)
+static u32 spi_xchg_single(struct spi_slave *slave, u32 data, int bitlen)
{
-
- unsigned int cfg_reg = reg_read(spi_base + MXC_CSPICTRL);
+ struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave);
+ unsigned int cfg_reg = reg_read(mxcs->base + MXC_CSPICTRL);
if (MXC_CSPICTRL_BITCOUNT(bitlen - 1) != (cfg_reg & MXC_CSPICTRL_BITCOUNT(31))) {
cfg_reg = (cfg_reg & ~MXC_CSPICTRL_BITCOUNT(31)) |
MXC_CSPICTRL_BITCOUNT(bitlen - 1);
- reg_write(spi_base + MXC_CSPICTRL, cfg_reg);
+ reg_write(mxcs->base + MXC_CSPICTRL, cfg_reg);
}
- reg_write(spi_base + MXC_CSPITXDATA, data);
+ reg_write(mxcs->base + MXC_CSPITXDATA, data);
cfg_reg |= MXC_CSPICTRL_XCH;
- reg_write(spi_base + MXC_CSPICTRL, cfg_reg);
+ reg_write(mxcs->base + MXC_CSPICTRL, cfg_reg);
- while (reg_read(spi_base + MXC_CSPICTRL) & MXC_CSPICTRL_XCH)
+ while (reg_read(mxcs->base + MXC_CSPICTRL) & MXC_CSPICTRL_XCH)
;
- return reg_read(spi_base + MXC_CSPIRXDATA);
+ return reg_read(mxcs->base + MXC_CSPIRXDATA);
}
-int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din)
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
+ void *din, unsigned long flags)
{
int n_blks = (bitlen + 31) / 32;
u32 *out_l, *in_l;
@@ -117,13 +120,10 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din)
return 1;
}
- if (!spi_base)
- spi_select(CONFIG_MXC_SPI_IFACE, (int)chipsel, SPI_MODE_2 | SPI_CS_HIGH);
-
for (i = 0, in_l = (u32 *)din, out_l = (u32 *)dout;
i < n_blks;
i++, in_l++, out_l++, bitlen -= 32)
- *in_l = spi_xchg_single(*out_l, bitlen);
+ *in_l = spi_xchg_single(slave, *out_l, bitlen);
return 0;
}
@@ -132,17 +132,17 @@ void spi_init(void)
{
}
-int spi_select(unsigned int bus, unsigned int dev, unsigned long mode)
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+ unsigned int max_hz, unsigned int mode)
{
unsigned int ctrl_reg;
+ struct mxc_spi_slave *mxcs;
if (bus >= sizeof(spi_bases) / sizeof(spi_bases[0]) ||
- dev > 3)
- return 1;
-
- spi_base = spi_bases[bus];
+ cs > 3)
+ return NULL;
- ctrl_reg = MXC_CSPICTRL_CHIPSELECT(dev) |
+ ctrl_reg = MXC_CSPICTRL_CHIPSELECT(cs) |
MXC_CSPICTRL_BITCOUNT(31) |
MXC_CSPICTRL_DATARATE(7) | /* FIXME: calculate data rate */
MXC_CSPICTRL_EN |
@@ -155,12 +155,38 @@ int spi_select(unsigned int bus, unsigned int dev, unsigned long mode)
if (mode & SPI_CS_HIGH)
ctrl_reg |= MXC_CSPICTRL_SSPOL;
- reg_write(spi_base + MXC_CSPIRESET, 1);
+ mxcs = malloc(sizeof(struct mxc_spi_slave));
+ if (!mxcs)
+ return NULL;
+
+ mxcs->slave.bus = bus;
+ mxcs->slave.cs = cs;
+ mxcs->base = spi_bases[bus];
+ mxcs->ctrl_reg = ctrl_reg;
+
+ return &mxcs->slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ free(slave);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave);
+
+ reg_write(mxcs->base + MXC_CSPIRESET, 1);
udelay(1);
- reg_write(spi_base + MXC_CSPICTRL, ctrl_reg);
- reg_write(spi_base + MXC_CSPIPERIOD,
+ reg_write(mxcs->base + MXC_CSPICTRL, mxcs->ctrl_reg);
+ reg_write(mxcs->base + MXC_CSPIPERIOD,
MXC_CSPIPERIOD_32KHZ);
- reg_write(spi_base + MXC_CSPIINT, 0);
+ reg_write(mxcs->base + MXC_CSPIINT, 0);
return 0;
}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+ /* TODO: Shut the controller down */
+}
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 9d2f65b7f8..20a54c54df 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libvideo.a
COBJS-y += ati_radeon_fb.o
+COBJS-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o
COBJS-y += cfb_console.o
COBJS-y += ct69000.o
COBJS-y += mb862xx.o
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
new file mode 100644
index 0000000000..27df449660
--- /dev/null
+++ b/drivers/video/atmel_lcdfb.c
@@ -0,0 +1,160 @@
+/*
+ * Driver for AT91/AT32 LCD Controller
+ *
+ * Copyright (C) 2007 Atmel 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 <asm/io.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/clk.h>
+#include <lcd.h>
+#include <atmel_lcdc.h>
+
+int lcd_line_length;
+int lcd_color_fg;
+int lcd_color_bg;
+
+void *lcd_base; /* Start of framebuffer memory */
+void *lcd_console_address; /* Start of console buffer */
+
+short console_col;
+short console_row;
+
+/* configurable parameters */
+#define ATMEL_LCDC_CVAL_DEFAULT 0xc8
+#define ATMEL_LCDC_DMA_BURST_LEN 8
+
+#if defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91CAP9)
+#define ATMEL_LCDC_FIFO_SIZE 2048
+#else
+#define ATMEL_LCDC_FIFO_SIZE 512
+#endif
+
+#define lcdc_readl(mmio, reg) __raw_readl((mmio)+(reg))
+#define lcdc_writel(mmio, reg, val) __raw_writel((val), (mmio)+(reg))
+
+void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
+{
+#if defined(CONFIG_ATMEL_LCD_BGR555)
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno),
+ (red >> 3) | ((green & 0xf8) << 2) | ((blue & 0xf8) << 7));
+#else
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno),
+ (blue >> 3) | ((green & 0xfc) << 3) | ((red & 0xf8) << 8));
+#endif
+}
+
+void lcd_ctrl_init(void *lcdbase)
+{
+ unsigned long value;
+
+ /* Turn off the LCD controller and the DMA controller */
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_PWRCON,
+ 1 << ATMEL_LCDC_GUARDT_OFFSET);
+
+ /* Wait for the LCDC core to become idle */
+ while (lcdc_readl(panel_info.mmio, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY)
+ udelay(10);
+
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMACON, 0);
+
+ /* Reset LCDC DMA */
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMARST);
+
+ /* ...set frame size and burst length = 8 words (?) */
+ value = (panel_info.vl_col * panel_info.vl_row *
+ NBITS(panel_info.vl_bpix)) / 32;
+ value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET);
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMAFRMCFG, value);
+
+ /* Set pixel clock */
+ value = get_lcdc_clk_rate(0) / panel_info.vl_clk;
+ if (get_lcdc_clk_rate(0) % panel_info.vl_clk)
+ value++;
+ value = (value / 2) - 1;
+
+ if (!value) {
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS);
+ } else
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_LCDCON1,
+ value << ATMEL_LCDC_CLKVAL_OFFSET);
+
+ /* Initialize control register 2 */
+ value = ATMEL_LCDC_MEMOR_LITTLE | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE;
+ if (panel_info.vl_tft)
+ value |= ATMEL_LCDC_DISTYPE_TFT;
+
+ if (!(panel_info.vl_sync & ATMEL_LCDC_INVLINE_INVERTED))
+ value |= ATMEL_LCDC_INVLINE_INVERTED;
+ if (!(panel_info.vl_sync & ATMEL_LCDC_INVFRAME_INVERTED))
+ value |= ATMEL_LCDC_INVFRAME_INVERTED;
+ value |= (panel_info.vl_bpix << 5);
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_LCDCON2, value);
+
+ /* Vertical timing */
+ value = (panel_info.vl_vsync_len - 1) << ATMEL_LCDC_VPW_OFFSET;
+ value |= panel_info.vl_upper_margin << ATMEL_LCDC_VBP_OFFSET;
+ value |= panel_info.vl_lower_margin;
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_TIM1, value);
+
+ /* Horizontal timing */
+ value = (panel_info.vl_right_margin - 1) << ATMEL_LCDC_HFP_OFFSET;
+ value |= (panel_info.vl_hsync_len - 1) << ATMEL_LCDC_HPW_OFFSET;
+ value |= (panel_info.vl_left_margin - 1);
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_TIM2, value);
+
+ /* Display size */
+ value = (panel_info.vl_col - 1) << ATMEL_LCDC_HOZVAL_OFFSET;
+ value |= panel_info.vl_row - 1;
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_LCDFRMCFG, value);
+
+ /* FIFO Threshold: Use formula from data sheet */
+ value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3);
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_FIFO, value);
+
+ /* Toggle LCD_MODE every frame */
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_MVAL, 0);
+
+ /* Disable all interrupts */
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_IDR, ~0UL);
+
+ /* Set contrast */
+ value = ATMEL_LCDC_PS_DIV8 |
+ ATMEL_LCDC_POL_POSITIVE |
+ ATMEL_LCDC_ENA_PWMENABLE;
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_CONTRAST_CTR, value);
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT);
+
+ /* Set framebuffer DMA base address and pixel offset */
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMABADDR1, (u_long)lcdbase);
+
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMAEN);
+ lcdc_writel(panel_info.mmio, ATMEL_LCDC_PWRCON,
+ (1 << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR);
+}
+
+ulong calc_fbsize(void)
+{
+ return ((panel_info.vl_col * panel_info.vl_row *
+ NBITS(panel_info.vl_bpix)) / 8) + PAGE_SIZE;
+}