diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/hwmon/lm75.c | 13 | ||||
| -rw-r--r-- | drivers/mtd/cfi_flash.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/nand_util.c | 7 | ||||
| -rw-r--r-- | drivers/mtd/spi/Makefile | 47 | ||||
| -rw-r--r-- | drivers/mtd/spi/atmel.c | 362 | ||||
| -rw-r--r-- | drivers/mtd/spi/spi_flash.c | 162 | ||||
| -rw-r--r-- | drivers/mtd/spi/spi_flash_internal.h | 45 | ||||
| -rw-r--r-- | drivers/net/dm9000x.c | 2 | ||||
| -rw-r--r-- | drivers/net/macb.c | 6 | ||||
| -rw-r--r-- | drivers/rtc/ds1306.c | 67 | ||||
| -rw-r--r-- | drivers/rtc/mc13783-rtc.c | 43 | ||||
| -rw-r--r-- | drivers/spi/Makefile | 1 | ||||
| -rw-r--r-- | drivers/spi/atmel_spi.c | 210 | ||||
| -rw-r--r-- | drivers/spi/atmel_spi.h | 95 | ||||
| -rw-r--r-- | drivers/spi/mpc8xxx_spi.c | 54 | ||||
| -rw-r--r-- | drivers/spi/mxc_spi.c | 88 | ||||
| -rw-r--r-- | drivers/video/Makefile | 1 | ||||
| -rw-r--r-- | drivers/video/atmel_lcdfb.c | 160 | 
18 files changed, 1302 insertions, 63 deletions
| diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index e29b29440..c34851725 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 68ab55f8a..d84f0fc55 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 6c5624a49..c82f77b55 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 000000000..af6af97d1 --- /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 000000000..fb7a4a939 --- /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 000000000..d581cb3e8 --- /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 000000000..14380500a --- /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 c2144d9bc..fa95a730c 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 703784ee0..e5733f6e5 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 1c8ac7f29..29854fc7c 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 35b1b8b25..b6e15014b 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 *)®, (uchar *)&day1); +		err = spi_xfer(slave, 32, (uchar *)®, (uchar *)&day1, +				SPI_XFER_BEGIN | SPI_XFER_END);  		if (err)  			return err;  		reg = 0x28000000; -		err = spi_xfer(0, 32, (uchar *)®, (uchar *)&time); +		err = spi_xfer(slave, 32, (uchar *)®, (uchar *)&time, +				SPI_XFER_BEGIN | SPI_XFER_END);  		if (err)  			return err;  		reg = 0x2c000000; -		err = spi_xfer(0, 32, (uchar *)®, (uchar *)&day2); +		err = spi_xfer(slave, 32, (uchar *)®, (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 *)®, (uchar *)&day); +	spi_xfer(slave, 32, (uchar *)®, (uchar *)&day, +			SPI_XFER_BEGIN | SPI_XFER_END);  	reg = 0x28000000 | time | 0x80000000; -	spi_xfer(0, 32, (uchar *)®, (uchar *)&time); +	spi_xfer(slave, 32, (uchar *)®, (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 bc8a10412..e66e0ee09 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 000000000..317c0b41b --- /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 000000000..8b69a6d21 --- /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 2fe838c45..136fb5005 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 c166ec502..5957ada3a 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 9d2f65b7f..20a54c54d 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 000000000..27df44966 --- /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; +} |