diff options
Diffstat (limited to 'drivers')
99 files changed, 10135 insertions, 1293 deletions
diff --git a/drivers/block/systemace.c b/drivers/block/systemace.c index 58402b928..247cf060e 100644 --- a/drivers/block/systemace.c +++ b/drivers/block/systemace.c @@ -51,24 +51,36 @@   * to be the base address for the chip, usually in the local   * peripheral bus.   */ -#if (CONFIG_SYS_SYSTEMACE_WIDTH == 8) + +static u32 base = CONFIG_SYS_SYSTEMACE_BASE; +static u32 width = CONFIG_SYS_SYSTEMACE_WIDTH; + +static void ace_writew(u16 val, unsigned off) +{ +	if (width == 8) {  #if !defined(__BIG_ENDIAN) -#define ace_readw(off) ((readb(CONFIG_SYS_SYSTEMACE_BASE+off)<<8) | \ -			(readb(CONFIG_SYS_SYSTEMACE_BASE+off+1))) -#define ace_writew(val, off) {writeb(val>>8, CONFIG_SYS_SYSTEMACE_BASE+off); \ -			      writeb(val, CONFIG_SYS_SYSTEMACE_BASE+off+1);} +		writeb(val >> 8, base + off); +		writeb(val, base + off + 1);  #else -#define ace_readw(off) ((readb(CONFIG_SYS_SYSTEMACE_BASE+off)) | \ -			(readb(CONFIG_SYS_SYSTEMACE_BASE+off+1)<<8)) -#define ace_writew(val, off) {writeb(val, CONFIG_SYS_SYSTEMACE_BASE+off); \ -			      writeb(val>>8, CONFIG_SYS_SYSTEMACE_BASE+off+1);} +		writeb(val, base + off); +		writeb(val >> 8, base + off + 1);  #endif +	} +	out16(base + off, val); +} + +static u16 ace_readw(unsigned off) +{ +	if (width == 8) { +#if !defined(__BIG_ENDIAN) +		return (readb(base + off) << 8) | readb(base + off + 1);  #else -#define ace_readw(off) (in16(CONFIG_SYS_SYSTEMACE_BASE+off)) -#define ace_writew(val, off) (out16(CONFIG_SYS_SYSTEMACE_BASE+off,val)) +		return readb(base + off) | (readb(base + off + 1) << 8);  #endif +	} -/* */ +	return in16(base + off); +}  static unsigned long systemace_read(int dev, unsigned long start,  				    unsigned long blkcnt, void *buffer); @@ -121,7 +133,7 @@ block_dev_desc_t *systemace_get_dev(int dev)  		/*  		 * Ensure the correct bus mode (8/16 bits) gets enabled  		 */ -		ace_writew(CONFIG_SYS_SYSTEMACE_WIDTH == 8 ? 0 : 0x0001, 0); +		ace_writew(width == 8 ? 0 : 0x0001, 0);  		init_part(&systemace_dev); diff --git a/drivers/bootcount/Makefile b/drivers/bootcount/Makefile new file mode 100644 index 000000000..a8d0ac7a3 --- /dev/null +++ b/drivers/bootcount/Makefile @@ -0,0 +1,45 @@ +# +# 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)libbootcount.o + +COBJS-y				+= bootcount.o +COBJS-$(CONFIG_AT91SAM9XE)	+= bootcount_at91.o +COBJS-$(CONFIG_BLACKFIN)	+= bootcount_blackfin.o +COBJS-$(CONFIG_SOC_DA8XX)	+= bootcount_davinci.o +COBJS-$(CONFIG_BOOTCOUNT_RAM)	+= bootcount_ram.o + +COBJS	:= $(COBJS-y) +SRCS 	:= $(COBJS:.o=.c) +OBJS 	:= $(addprefix $(obj),$(COBJS)) + +$(LIB):	$(obj).depend $(OBJS) +	$(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################## diff --git a/drivers/bootcount/bootcount.c b/drivers/bootcount/bootcount.c new file mode 100644 index 000000000..80eb71763 --- /dev/null +++ b/drivers/bootcount/bootcount.c @@ -0,0 +1,100 @@ +/* + * (C) Copyright 2010-2012 + * Stefan Roese, DENX Software Engineering, sr@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 <bootcount.h> +#include <linux/compiler.h> + +/* + * Only override CONFIG_SYS_BOOTCOUNT_ADDR if not already defined. This + * way, some boards can define it directly in their config header. + */ +#if !defined(CONFIG_SYS_BOOTCOUNT_ADDR) + +#if defined(CONFIG_MPC5xxx) +#define CONFIG_SYS_BOOTCOUNT_ADDR	(MPC5XXX_CDM_BRDCRMB) +#define CONFIG_SYS_BOOTCOUNT_SINGLEWORD +#endif /* defined(CONFIG_MPC5xxx) */ + +#if defined(CONFIG_MPC512X) +#define CONFIG_SYS_BOOTCOUNT_ADDR	(&((immap_t *)CONFIG_SYS_IMMR)->clk.bcr) +#define CONFIG_SYS_BOOTCOUNT_SINGLEWORD +#endif /* defined(CONFIG_MPC512X) */ + +#if defined(CONFIG_8xx) +#define CONFIG_SYS_BOOTCOUNT_ADDR (((immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_dpmem + \ +				CPM_BOOTCOUNT_ADDR) +#endif /* defined(CONFIG_8xx) */ + +#if defined(CONFIG_MPC8260) +#include <asm/cpm_8260.h> + +#define CONFIG_SYS_BOOTCOUNT_ADDR	(CONFIG_SYS_IMMR + CPM_BOOTCOUNT_ADDR) +#endif /* defined(CONFIG_MPC8260) */ + +#if defined(CONFIG_QE) +#include <asm/immap_qe.h> + +#define CONFIG_SYS_BOOTCOUNT_ADDR	(CONFIG_SYS_IMMR + 0x110000 + \ +					 QE_MURAM_SIZE - 2 * sizeof(u32)) +#endif /* defined(CONFIG_MPC8360) */ + +#if defined(CONFIG_4xx) +#define CONFIG_SYS_BOOTCOUNT_ADDR	(CONFIG_SYS_OCM_DATA_ADDR + \ +				CONFIG_SYS_BOOTCOUNT_ADDR) +#endif /* defined(CONFIG_4xx) */ + +#endif /* !defined(CONFIG_SYS_BOOTCOUNT_ADDR) */ + +/* Now implement the generic default functions */ +#if defined(CONFIG_SYS_BOOTCOUNT_ADDR) +__weak void bootcount_store(ulong a) +{ +	void *reg = (void *)CONFIG_SYS_BOOTCOUNT_ADDR; + +#if defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD) +	raw_bootcount_store(reg, (BOOTCOUNT_MAGIC & 0xffff0000) | a); +#else +	raw_bootcount_store(reg, a); +	raw_bootcount_store(reg + 4, BOOTCOUNT_MAGIC); +#endif +} + +__weak ulong bootcount_load(void) +{ +	void *reg = (void *)CONFIG_SYS_BOOTCOUNT_ADDR; + +#if defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD) +	u32 tmp = raw_bootcount_load(reg); + +	if ((tmp & 0xffff0000) != (BOOTCOUNT_MAGIC & 0xffff0000)) +		return 0; +	else +		return (tmp & 0x0000ffff); +#else +	if (raw_bootcount_load(reg + 4) != BOOTCOUNT_MAGIC) +		return 0; +	else +		return raw_bootcount_load(reg); +#endif +} +#endif diff --git a/drivers/bootcount/bootcount_at91.c b/drivers/bootcount/bootcount_at91.c new file mode 100644 index 000000000..7cfe14d37 --- /dev/null +++ b/drivers/bootcount/bootcount_at91.c @@ -0,0 +1,43 @@ +/* + * 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. + * + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/hardware.h> +#include <asm/arch/at91_gpbr.h> + +/* + * We combine the BOOTCOUNT_MAGIC and bootcount in one 32-bit register. + * This is done so we need to use only one of the four GPBR registers. + */ +void bootcount_store(ulong a) +{ +	at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR; + +	writel((BOOTCOUNT_MAGIC & 0xffff0000) | (a & 0x0000ffff), +		&gpbr->reg[AT91_GPBR_INDEX_BOOTCOUNT]); +} + +ulong bootcount_load(void) +{ +	at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR; + +	ulong val = readl(&gpbr->reg[AT91_GPBR_INDEX_BOOTCOUNT]); +	if ((val & 0xffff0000) != (BOOTCOUNT_MAGIC & 0xffff0000)) +		return 0; +	else +		return val & 0x0000ffff; +} diff --git a/drivers/bootcount/bootcount_blackfin.c b/drivers/bootcount/bootcount_blackfin.c new file mode 100644 index 000000000..6cf6dd58b --- /dev/null +++ b/drivers/bootcount/bootcount_blackfin.c @@ -0,0 +1,34 @@ +/* + * functions for handling bootcount support + * + * Copyright (c) 2010 Analog Devices Inc. + * + * Licensed under the 2-clause BSD. + */ + +/* This version uses one 32bit storage and combines the magic/count */ + +#include <common.h> + +/* We abuse the EVT0 MMR for bootcount storage by default */ +#ifndef CONFIG_SYS_BOOTCOUNT_ADDR +# define CONFIG_SYS_BOOTCOUNT_ADDR EVT0 +#endif + +#define MAGIC_MASK 0xffff0000 +#define COUNT_MASK 0x0000ffff + +void bootcount_store(ulong cnt) +{ +	ulong magic = (BOOTCOUNT_MAGIC & MAGIC_MASK) | (cnt & COUNT_MASK); +	bfin_write32(CONFIG_SYS_BOOTCOUNT_ADDR, magic); +} + +ulong bootcount_load(void) +{ +	ulong magic = bfin_read32(CONFIG_SYS_BOOTCOUNT_ADDR); +	if ((magic & MAGIC_MASK) == (BOOTCOUNT_MAGIC & MAGIC_MASK)) +		return magic & COUNT_MASK; +	else +		return 0; +} diff --git a/drivers/bootcount/bootcount_davinci.c b/drivers/bootcount/bootcount_davinci.c new file mode 100644 index 000000000..1cd943630 --- /dev/null +++ b/drivers/bootcount/bootcount_davinci.c @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2011 + * Heiko Schocher, DENX Software Engineering, hs@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. + * + */ + +#include <bootcount.h> +#include <asm/arch/da850_lowlevel.h> +#include <asm/arch/davinci_misc.h> + +void bootcount_store(ulong a) +{ +	struct davinci_rtc *reg = +		(struct davinci_rtc *)CONFIG_SYS_BOOTCOUNT_ADDR; + +	/* +	 * write RTC kick register to enable write +	 * for RTC Scratch registers. Scratch0 and 1 are +	 * used for bootcount values. +	 */ +	writel(RTC_KICK0R_WE, ®->kick0r); +	writel(RTC_KICK1R_WE, ®->kick1r); +	raw_bootcount_store(®->scratch0, a); +	raw_bootcount_store(®->scratch1, BOOTCOUNT_MAGIC); +} + +ulong bootcount_load(void) +{ +	struct davinci_rtc *reg = +		(struct davinci_rtc *)CONFIG_SYS_BOOTCOUNT_ADDR; + +	if (raw_bootcount_load(®->scratch1) != BOOTCOUNT_MAGIC) +		return 0; +	else +		return raw_bootcount_load(®->scratch0); +} diff --git a/drivers/bootcount/bootcount_ram.c b/drivers/bootcount/bootcount_ram.c new file mode 100644 index 000000000..8655af7ea --- /dev/null +++ b/drivers/bootcount/bootcount_ram.c @@ -0,0 +1,72 @@ +/* + * (C) Copyright 2010 + * Heiko Schocher, DENX Software Engineering, hs@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. + * + */ + +#include <common.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +const ulong patterns[]      = {	0x00000000, +				0xFFFFFFFF, +				0xFF00FF00, +				0x0F0F0F0F, +				0xF0F0F0F0}; +const ulong NBR_OF_PATTERNS = sizeof(patterns) / sizeof(*patterns); +const ulong OFFS_PATTERN    = 3; +const ulong REPEAT_PATTERN  = 1000; + +void bootcount_store(ulong a) +{ +	ulong *save_addr; +	ulong size = 0; +	int i; + +	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) +		size += gd->bd->bi_dram[i].size; +	save_addr = (ulong *)(size - BOOTCOUNT_ADDR); +	writel(a, save_addr); +	writel(BOOTCOUNT_MAGIC, &save_addr[1]); + +	for (i = 0; i < REPEAT_PATTERN; i++) +		writel(patterns[i % NBR_OF_PATTERNS], +			&save_addr[i + OFFS_PATTERN]); + +} + +ulong bootcount_load(void) +{ +	ulong *save_addr; +	ulong size = 0; +	ulong counter = 0; +	int i, tmp; + +	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) +		size += gd->bd->bi_dram[i].size; +	save_addr = (ulong *)(size - BOOTCOUNT_ADDR); + +	counter = readl(&save_addr[0]); + +	/* Is the counter reliable, check in the big pattern for bit errors */ +	for (i = 0; (i < REPEAT_PATTERN) && (counter != 0); i++) { +		tmp = readl(&save_addr[i + OFFS_PATTERN]); +		if (tmp != patterns[i % NBR_OF_PATTERNS]) +			counter = 0; +	} +	return counter; +} diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile new file mode 100644 index 000000000..7b717bce2 --- /dev/null +++ b/drivers/dfu/Makefile @@ -0,0 +1,44 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski <l.majewski@samsung.com> +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB	= $(obj)libdfu.o + +COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o +COBJS-$(CONFIG_DFU_MMC) += dfu_mmc.o + +SRCS    := $(COBJS-y:.o=.c) +OBJS	:= $(addprefix $(obj),$(COBJS-y)) + +$(LIB):	$(obj).depend $(OBJS) +	$(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c new file mode 100644 index 000000000..e8477fb7c --- /dev/null +++ b/drivers/dfu/dfu.c @@ -0,0 +1,238 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski <l.majewski@samsung.com> + * + * 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 <malloc.h> +#include <mmc.h> +#include <fat.h> +#include <dfu.h> +#include <linux/list.h> +#include <linux/compiler.h> + +static LIST_HEAD(dfu_list); +static int dfu_alt_num; + +static int dfu_find_alt_num(const char *s) +{ +	int i = 0; + +	for (; *s; s++) +		if (*s == ';') +			i++; + +	return ++i; +} + +static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) +				     dfu_buf[DFU_DATA_BUF_SIZE]; + +int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ +	static unsigned char *i_buf; +	static int i_blk_seq_num; +	long w_size = 0; +	int ret = 0; + +	debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", +	       __func__, dfu->name, buf, size, blk_seq_num, i_buf); + +	if (blk_seq_num == 0) { +		i_buf = dfu_buf; +		i_blk_seq_num = 0; +	} + +	if (i_blk_seq_num++ != blk_seq_num) { +		printf("%s: Wrong sequence number! [%d] [%d]\n", +		       __func__, i_blk_seq_num, blk_seq_num); +		return -1; +	} + +	memcpy(i_buf, buf, size); +	i_buf += size; + +	if (size == 0) { +		/* Integrity check (if needed) */ +		debug("%s: %s %d [B] CRC32: 0x%x\n", __func__, dfu->name, +		       i_buf - dfu_buf, crc32(0, dfu_buf, i_buf - dfu_buf)); + +		w_size = i_buf - dfu_buf; +		ret = dfu->write_medium(dfu, dfu_buf, &w_size); +		if (ret) +			debug("%s: Write error!\n", __func__); + +		i_blk_seq_num = 0; +		i_buf = NULL; +		return ret; +	} + +	return ret; +} + +int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) +{ +	static unsigned char *i_buf; +	static int i_blk_seq_num; +	static long r_size; +	static u32 crc; +	int ret = 0; + +	debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n", +	       __func__, dfu->name, buf, size, blk_seq_num, i_buf); + +	if (blk_seq_num == 0) { +		i_buf = dfu_buf; +		ret = dfu->read_medium(dfu, i_buf, &r_size); +		debug("%s: %s %ld [B]\n", __func__, dfu->name, r_size); +		i_blk_seq_num = 0; +		/* Integrity check (if needed) */ +		crc = crc32(0, dfu_buf, r_size); +	} + +	if (i_blk_seq_num++ != blk_seq_num) { +		printf("%s: Wrong sequence number! [%d] [%d]\n", +		       __func__, i_blk_seq_num, blk_seq_num); +		return -1; +	} + +	if (r_size >= size) { +		memcpy(buf, i_buf, size); +		i_buf += size; +		r_size -= size; +		return size; +	} else { +		memcpy(buf, i_buf, r_size); +		i_buf += r_size; +		debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, crc); +		puts("UPLOAD ... done\nCtrl+C to exit ...\n"); + +		i_buf = NULL; +		i_blk_seq_num = 0; +		crc = 0; +		return r_size; +	} +	return ret; +} + +static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt, +			    char *interface, int num) +{ +	char *st; + +	debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num); +	st = strsep(&s, " "); +	strcpy(dfu->name, st); + +	dfu->dev_num = num; +	dfu->alt = alt; + +	/* Specific for mmc device */ +	if (strcmp(interface, "mmc") == 0) { +		if (dfu_fill_entity_mmc(dfu, s)) +			return -1; +	} else { +		printf("%s: Device %s not (yet) supported!\n", +		       __func__,  interface); +		return -1; +	} + +	return 0; +} + +void dfu_free_entities(void) +{ +	struct dfu_entity *dfu, *p, *t = NULL; + +	list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) { +		list_del(&dfu->list); +		t = dfu; +	} +	if (t) +		free(t); +	INIT_LIST_HEAD(&dfu_list); +} + +int dfu_config_entities(char *env, char *interface, int num) +{ +	struct dfu_entity *dfu; +	int i, ret; +	char *s; + +	dfu_alt_num = dfu_find_alt_num(env); +	debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num); + +	dfu = calloc(sizeof(*dfu), dfu_alt_num); +	if (!dfu) +		return -1; +	for (i = 0; i < dfu_alt_num; i++) { + +		s = strsep(&env, ";"); +		ret = dfu_fill_entity(&dfu[i], s, i, interface, num); +		if (ret) +			return -1; + +		list_add_tail(&dfu[i].list, &dfu_list); +	} + +	return 0; +} + +const char *dfu_get_dev_type(enum dfu_device_type t) +{ +	const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND" }; +	return dev_t[t]; +} + +const char *dfu_get_layout(enum dfu_layout l) +{ +	const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2", +					   "EXT3", "EXT4" }; +	return dfu_layout[l]; +} + +void dfu_show_entities(void) +{ +	struct dfu_entity *dfu; + +	puts("DFU alt settings list:\n"); + +	list_for_each_entry(dfu, &dfu_list, list) { +		printf("dev: %s alt: %d name: %s layout: %s\n", +		       dfu_get_dev_type(dfu->dev_type), dfu->alt, +		       dfu->name, dfu_get_layout(dfu->layout)); +	} +} + +int dfu_get_alt_number(void) +{ +	return dfu_alt_num; +} + +struct dfu_entity *dfu_get_entity(int alt) +{ +	struct dfu_entity *dfu; + +	list_for_each_entry(dfu, &dfu_list, list) { +		if (dfu->alt == alt) +			return dfu; +	} + +	return NULL; +} diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c new file mode 100644 index 000000000..5d504dffd --- /dev/null +++ b/drivers/dfu/dfu_mmc.c @@ -0,0 +1,182 @@ +/* + * dfu.c -- DFU back-end routines + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski <l.majewski@samsung.com> + * + * 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 <malloc.h> +#include <dfu.h> + +enum dfu_mmc_op { +	DFU_OP_READ = 1, +	DFU_OP_WRITE, +}; + +static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu, +			void *buf, long *len) +{ +	char cmd_buf[DFU_CMD_BUF_SIZE]; + +	sprintf(cmd_buf, "mmc %s 0x%x %x %x", +		op == DFU_OP_READ ? "read" : "write", +		(unsigned int) buf, +		dfu->data.mmc.lba_start, +		dfu->data.mmc.lba_size); + +	if (op == DFU_OP_READ) +		*len = dfu->data.mmc.lba_blk_size * dfu->data.mmc.lba_size; + +	debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); +	return run_command(cmd_buf, 0); +} + +static inline int mmc_block_write(struct dfu_entity *dfu, void *buf, long *len) +{ +	return mmc_block_op(DFU_OP_WRITE, dfu, buf, len); +} + +static inline int mmc_block_read(struct dfu_entity *dfu, void *buf, long *len) +{ +	return mmc_block_op(DFU_OP_READ, dfu, buf, len); +} + +static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu, +			void *buf, long *len) +{ +	char cmd_buf[DFU_CMD_BUF_SIZE]; +	char *str_env; +	int ret; + +	switch (dfu->layout) { +	case DFU_FS_FAT: +		sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s %lx", +			op == DFU_OP_READ ? "load" : "write", +			dfu->data.mmc.dev, dfu->data.mmc.part, +			(unsigned int) buf, dfu->name, *len); +		break; +	case DFU_FS_EXT4: +		sprintf(cmd_buf, "ext4%s mmc %d:%d /%s 0x%x %ld", +			op == DFU_OP_READ ? "load" : "write", +			dfu->data.mmc.dev, dfu->data.mmc.part, +			dfu->name, (unsigned int) buf, *len); +		break; +	default: +		printf("%s: Layout (%s) not (yet) supported!\n", __func__, +		       dfu_get_layout(dfu->layout)); +	} + +	debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); + +	ret = run_command(cmd_buf, 0); +	if (ret) { +		puts("dfu: Read error!\n"); +		return ret; +	} + +	if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) { +		str_env = getenv("filesize"); +		if (str_env == NULL) { +			puts("dfu: Wrong file size!\n"); +			return -1; +		} +		*len = simple_strtoul(str_env, NULL, 16); +	} + +	return ret; +} + +static inline int mmc_file_write(struct dfu_entity *dfu, void *buf, long *len) +{ +	return mmc_file_op(DFU_OP_WRITE, dfu, buf, len); +} + +static inline int mmc_file_read(struct dfu_entity *dfu, void *buf, long *len) +{ +	return mmc_file_op(DFU_OP_READ, dfu, buf, len); +} + +int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{ +	int ret = -1; + +	switch (dfu->layout) { +	case DFU_RAW_ADDR: +		ret = mmc_block_write(dfu, buf, len); +		break; +	case DFU_FS_FAT: +	case DFU_FS_EXT4: +		ret = mmc_file_write(dfu, buf, len); +		break; +	default: +		printf("%s: Layout (%s) not (yet) supported!\n", __func__, +		       dfu_get_layout(dfu->layout)); +	} + +	return ret; +} + +int dfu_read_medium_mmc(struct dfu_entity *dfu, void *buf, long *len) +{ +	int ret = -1; + +	switch (dfu->layout) { +	case DFU_RAW_ADDR: +		ret = mmc_block_read(dfu, buf, len); +		break; +	case DFU_FS_FAT: +	case DFU_FS_EXT4: +		ret = mmc_file_read(dfu, buf, len); +		break; +	default: +		printf("%s: Layout (%s) not (yet) supported!\n", __func__, +		       dfu_get_layout(dfu->layout)); +	} + +	return ret; +} + +int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) +{ +	char *st; + +	dfu->dev_type = DFU_DEV_MMC; +	st = strsep(&s, " "); +	if (!strcmp(st, "mmc")) { +		dfu->layout = DFU_RAW_ADDR; +		dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16); +		dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16); +		dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num); +	} else if (!strcmp(st, "fat")) { +		dfu->layout = DFU_FS_FAT; +	} else if (!strcmp(st, "ext4")) { +		dfu->layout = DFU_FS_EXT4; +	} else { +		printf("%s: Memory layout (%s) not supported!\n", __func__, st); +	} + +	if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) { +		dfu->data.mmc.dev = simple_strtoul(s, &s, 10); +		dfu->data.mmc.part = simple_strtoul(++s, &s, 10); +	} + +	dfu->read_medium = dfu_read_medium_mmc; +	dfu->write_medium = dfu_write_medium_mmc; + +	return 0; +} diff --git a/drivers/dma/apbh_dma.c b/drivers/dma/apbh_dma.c index cb2193ec5..37a941cc5 100644 --- a/drivers/dma/apbh_dma.c +++ b/drivers/dma/apbh_dma.c @@ -76,8 +76,8 @@ static unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc)   */  static int mxs_dma_read_semaphore(int channel)  { -	struct mx28_apbh_regs *apbh_regs = -		(struct mx28_apbh_regs *)MXS_APBH_BASE; +	struct mxs_apbh_regs *apbh_regs = +		(struct mxs_apbh_regs *)MXS_APBH_BASE;  	uint32_t tmp;  	int ret; @@ -119,8 +119,8 @@ inline void mxs_dma_flush_desc(struct mxs_dma_desc *desc) {}   */  static int mxs_dma_enable(int channel)  { -	struct mx28_apbh_regs *apbh_regs = -		(struct mx28_apbh_regs *)MXS_APBH_BASE; +	struct mxs_apbh_regs *apbh_regs = +		(struct mxs_apbh_regs *)MXS_APBH_BASE;  	unsigned int sem;  	struct mxs_dma_chan *pchan;  	struct mxs_dma_desc *pdesc; @@ -191,8 +191,8 @@ static int mxs_dma_enable(int channel)  static int mxs_dma_disable(int channel)  {  	struct mxs_dma_chan *pchan; -	struct mx28_apbh_regs *apbh_regs = -		(struct mx28_apbh_regs *)MXS_APBH_BASE; +	struct mxs_apbh_regs *apbh_regs = +		(struct mxs_apbh_regs *)MXS_APBH_BASE;  	int ret;  	ret = mxs_dma_validate_chan(channel); @@ -220,8 +220,8 @@ static int mxs_dma_disable(int channel)   */  static int mxs_dma_reset(int channel)  { -	struct mx28_apbh_regs *apbh_regs = -		(struct mx28_apbh_regs *)MXS_APBH_BASE; +	struct mxs_apbh_regs *apbh_regs = +		(struct mxs_apbh_regs *)MXS_APBH_BASE;  	int ret;  	ret = mxs_dma_validate_chan(channel); @@ -241,8 +241,8 @@ static int mxs_dma_reset(int channel)   */  static int mxs_dma_enable_irq(int channel, int enable)  { -	struct mx28_apbh_regs *apbh_regs = -		(struct mx28_apbh_regs *)MXS_APBH_BASE; +	struct mxs_apbh_regs *apbh_regs = +		(struct mxs_apbh_regs *)MXS_APBH_BASE;  	int ret;  	ret = mxs_dma_validate_chan(channel); @@ -267,8 +267,8 @@ static int mxs_dma_enable_irq(int channel, int enable)   */  static int mxs_dma_ack_irq(int channel)  { -	struct mx28_apbh_regs *apbh_regs = -		(struct mx28_apbh_regs *)MXS_APBH_BASE; +	struct mxs_apbh_regs *apbh_regs = +		(struct mxs_apbh_regs *)MXS_APBH_BASE;  	int ret;  	ret = mxs_dma_validate_chan(channel); @@ -504,15 +504,15 @@ static int mxs_dma_finish(int channel, struct list_head *head)   */  static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan)  { -	struct mx28_apbh_regs *apbh_regs = -		(struct mx28_apbh_regs *)MXS_APBH_BASE; +	struct mxs_apbh_regs *apbh_regs = +		(struct mxs_apbh_regs *)MXS_APBH_BASE;  	int ret;  	ret = mxs_dma_validate_chan(chan);  	if (ret)  		return ret; -	if (mx28_wait_mask_set(&apbh_regs->hw_apbh_ctrl1_reg, +	if (mxs_wait_mask_set(&apbh_regs->hw_apbh_ctrl1_reg,  				1 << chan, timeout)) {  		ret = -ETIMEDOUT;  		mxs_dma_reset(chan); @@ -526,7 +526,7 @@ static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan)   */  int mxs_dma_go(int chan)  { -	uint32_t timeout = 10000; +	uint32_t timeout = 10000000;  	int ret;  	LIST_HEAD(tmp_desc_list); @@ -554,10 +554,10 @@ int mxs_dma_go(int chan)   */  void mxs_dma_init(void)  { -	struct mx28_apbh_regs *apbh_regs = -		(struct mx28_apbh_regs *)MXS_APBH_BASE; +	struct mxs_apbh_regs *apbh_regs = +		(struct mxs_apbh_regs *)MXS_APBH_BASE; -	mx28_reset_block(&apbh_regs->hw_apbh_ctrl0_reg); +	mxs_reset_block(&apbh_regs->hw_apbh_ctrl0_reg);  #ifdef CONFIG_APBH_DMA_BURST8  	writel(APBH_CTRL0_AHB_BURST8_EN, diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 106549dc2..17f4b739a 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -41,6 +41,9 @@ COBJS-$(CONFIG_DA8XX_GPIO)	+= da8xx_gpio.o  COBJS-$(CONFIG_ALTERA_PIO)	+= altera_pio.o  COBJS-$(CONFIG_MPC83XX_GPIO)	+= mpc83xx_gpio.o  COBJS-$(CONFIG_SH_GPIO_PFC)	+= sh_pfc.o +COBJS-$(CONFIG_OMAP_GPIO)	+= omap_gpio.o +COBJS-$(CONFIG_DB8500_GPIO)	+= db8500_gpio.o +COBJS-$(CONFIG_BCM2835_GPIO)	+= bcm2835_gpio.o  COBJS	:= $(COBJS-y)  SRCS 	:= $(COBJS:.o=.c) diff --git a/drivers/gpio/bcm2835_gpio.c b/drivers/gpio/bcm2835_gpio.c new file mode 100644 index 000000000..1d50dbd5e --- /dev/null +++ b/drivers/gpio/bcm2835_gpio.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2012 Vikram Narayananan + * <vikram186@gmail.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <common.h> +#include <asm/gpio.h> +#include <asm/io.h> + +inline int gpio_is_valid(unsigned gpio) +{ +	return (gpio < BCM2835_GPIO_COUNT); +} + +int gpio_request(unsigned gpio, const char *label) +{ +	return !gpio_is_valid(gpio); +} + +int gpio_free(unsigned gpio) +{ +	return 0; +} + +int gpio_direction_input(unsigned gpio) +{ +	struct bcm2835_gpio_regs *reg = +		(struct bcm2835_gpio_regs *)BCM2835_GPIO_BASE; +	unsigned val; + +	val = readl(®->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]); +	val &= ~(BCM2835_GPIO_FSEL_MASK << BCM2835_GPIO_FSEL_SHIFT(gpio)); +	val |= (BCM2835_GPIO_INPUT << BCM2835_GPIO_FSEL_SHIFT(gpio)); +	writel(val, ®->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]); + +	return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ +	struct bcm2835_gpio_regs *reg = +		(struct bcm2835_gpio_regs *)BCM2835_GPIO_BASE; +	unsigned val; + +	gpio_set_value(gpio, value); + +	val = readl(®->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]); +	val &= ~(BCM2835_GPIO_FSEL_MASK << BCM2835_GPIO_FSEL_SHIFT(gpio)); +	val |= (BCM2835_GPIO_OUTPUT << BCM2835_GPIO_FSEL_SHIFT(gpio)); +	writel(val, ®->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]); + +	return 0; +} + +int gpio_get_value(unsigned gpio) +{ +	struct bcm2835_gpio_regs *reg = +		(struct bcm2835_gpio_regs *)BCM2835_GPIO_BASE; +	unsigned val; + +	val = readl(®->gplev[BCM2835_GPIO_COMMON_BANK(gpio)]); + +	return (val >> BCM2835_GPIO_COMMON_SHIFT(gpio)) & 0x1; +} + +int gpio_set_value(unsigned gpio, int value) +{ +	struct bcm2835_gpio_regs *reg = +		(struct bcm2835_gpio_regs *)BCM2835_GPIO_BASE; +	u32 *output_reg = value ? reg->gpset : reg->gpclr; + +	writel(1 << BCM2835_GPIO_COMMON_SHIFT(gpio), +				&output_reg[BCM2835_GPIO_COMMON_BANK(gpio)]); + +	return 0; +} diff --git a/drivers/gpio/db8500_gpio.c b/drivers/gpio/db8500_gpio.c new file mode 100644 index 000000000..d5cb383e8 --- /dev/null +++ b/drivers/gpio/db8500_gpio.c @@ -0,0 +1,221 @@ +/* + * Code ported from Nomadik GPIO driver in ST-Ericsson Linux kernel code. + * The purpose is that GPIO config found in kernel should work by simply + * copy-paste it to U-boot. + * + * Original Linux authors: + * Copyright (C) 2008,2009 STMicroelectronics + * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it> + *   Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.com> + * + * Ported to U-boot by: + * Copyright (C) 2010 Joakim Axelsson <joakim.axelsson AT stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/io.h> + +#include <asm/arch/db8500_gpio.h> +#include <asm/arch/db8500_pincfg.h> +#include <linux/compiler.h> + +#define IO_ADDR(x) (void *) (x) + +/* + * The GPIO module in the db8500 Systems-on-Chip is an + * AMBA device, managing 32 pins and alternate functions. The logic block + * is currently only used in the db8500. + */ + +#define GPIO_TOTAL_PINS		268 +#define GPIO_PINS_PER_BLOCK	32 +#define GPIO_BLOCKS_COUNT	(GPIO_TOTAL_PINS/GPIO_PINS_PER_BLOCK + 1) +#define GPIO_BLOCK(pin)		(((pin + GPIO_PINS_PER_BLOCK) >> 5) - 1) +#define GPIO_PIN_WITHIN_BLOCK(pin)	((pin)%(GPIO_PINS_PER_BLOCK)) + +/* Register in the logic block */ +#define DB8500_GPIO_DAT		0x00 +#define DB8500_GPIO_DATS	0x04 +#define DB8500_GPIO_DATC	0x08 +#define DB8500_GPIO_PDIS	0x0c +#define DB8500_GPIO_DIR		0x10 +#define DB8500_GPIO_DIRS	0x14 +#define DB8500_GPIO_DIRC	0x18 +#define DB8500_GPIO_SLPC	0x1c +#define DB8500_GPIO_AFSLA	0x20 +#define DB8500_GPIO_AFSLB	0x24 + +#define DB8500_GPIO_RIMSC	0x40 +#define DB8500_GPIO_FIMSC	0x44 +#define DB8500_GPIO_IS		0x48 +#define DB8500_GPIO_IC		0x4c +#define DB8500_GPIO_RWIMSC	0x50 +#define DB8500_GPIO_FWIMSC	0x54 +#define DB8500_GPIO_WKS		0x58 + +static void __iomem *get_gpio_addr(unsigned gpio) +{ +	/* Our list of GPIO chips */ +	static void __iomem *gpio_addrs[GPIO_BLOCKS_COUNT] = { +		IO_ADDR(CFG_GPIO_0_BASE), +		IO_ADDR(CFG_GPIO_1_BASE), +		IO_ADDR(CFG_GPIO_2_BASE), +		IO_ADDR(CFG_GPIO_3_BASE), +		IO_ADDR(CFG_GPIO_4_BASE), +		IO_ADDR(CFG_GPIO_5_BASE), +		IO_ADDR(CFG_GPIO_6_BASE), +		IO_ADDR(CFG_GPIO_7_BASE), +		IO_ADDR(CFG_GPIO_8_BASE) +	}; + +	return gpio_addrs[GPIO_BLOCK(gpio)]; +} + +static unsigned get_gpio_offset(unsigned gpio) +{ +	return GPIO_PIN_WITHIN_BLOCK(gpio); +} + +/* Can only be called from config_pin. Don't configure alt-mode directly */ +static void gpio_set_mode(unsigned gpio, enum db8500_gpio_alt mode) +{ +	void __iomem *addr = get_gpio_addr(gpio); +	unsigned offset = get_gpio_offset(gpio); +	u32 bit = 1 << offset; +	u32 afunc, bfunc; + +	afunc = readl(addr + DB8500_GPIO_AFSLA) & ~bit; +	bfunc = readl(addr + DB8500_GPIO_AFSLB) & ~bit; +	if (mode & DB8500_GPIO_ALT_A) +		afunc |= bit; +	if (mode & DB8500_GPIO_ALT_B) +		bfunc |= bit; +	writel(afunc, addr + DB8500_GPIO_AFSLA); +	writel(bfunc, addr + DB8500_GPIO_AFSLB); +} + +/** + * db8500_gpio_set_pull() - enable/disable pull up/down on a gpio + * @gpio: pin number + * @pull: one of DB8500_GPIO_PULL_DOWN, DB8500_GPIO_PULL_UP, + *  and DB8500_GPIO_PULL_NONE + * + * Enables/disables pull up/down on a specified pin.  This only takes effect if + * the pin is configured as an input (either explicitly or by the alternate + * function). + * + * NOTE: If enabling the pull up/down, the caller must ensure that the GPIO is + * configured as an input.  Otherwise, due to the way the controller registers + * work, this function will change the value output on the pin. + */ +void db8500_gpio_set_pull(unsigned gpio, enum db8500_gpio_pull pull) +{ +	void __iomem *addr = get_gpio_addr(gpio); +	unsigned offset = get_gpio_offset(gpio); +	u32 bit = 1 << offset; +	u32 pdis; + +	pdis = readl(addr + DB8500_GPIO_PDIS); +	if (pull == DB8500_GPIO_PULL_NONE) +		pdis |= bit; +	else +		pdis &= ~bit; +	writel(pdis, addr + DB8500_GPIO_PDIS); + +	if (pull == DB8500_GPIO_PULL_UP) +		writel(bit, addr + DB8500_GPIO_DATS); +	else if (pull == DB8500_GPIO_PULL_DOWN) +		writel(bit, addr + DB8500_GPIO_DATC); +} + +void db8500_gpio_make_input(unsigned gpio) +{ +	void __iomem *addr = get_gpio_addr(gpio); +	unsigned offset = get_gpio_offset(gpio); + +	writel(1 << offset, addr + DB8500_GPIO_DIRC); +} + +int db8500_gpio_get_input(unsigned gpio) +{ +	void __iomem *addr = get_gpio_addr(gpio); +	unsigned offset = get_gpio_offset(gpio); +	u32 bit = 1 << offset; + +	printf("db8500_gpio_get_input gpio=%u addr=%p offset=%u bit=%#x\n", +		gpio, addr, offset, bit); + +	return (readl(addr + DB8500_GPIO_DAT) & bit) != 0; +} + +void db8500_gpio_make_output(unsigned gpio, int val) +{ +	void __iomem *addr = get_gpio_addr(gpio); +	unsigned offset = get_gpio_offset(gpio); + +	writel(1 << offset, addr + DB8500_GPIO_DIRS); +	db8500_gpio_set_output(gpio, val); +} + +void db8500_gpio_set_output(unsigned gpio, int val) +{ +	void __iomem *addr = get_gpio_addr(gpio); +	unsigned offset = get_gpio_offset(gpio); + +	if (val) +		writel(1 << offset, addr + DB8500_GPIO_DATS); +	else +		writel(1 << offset, addr + DB8500_GPIO_DATC); +} + +/** + * config_pin - configure a pin's mux attributes + * @cfg: pin confguration + * + * Configures a pin's mode (alternate function or GPIO), its pull up status, + * and its sleep mode based on the specified configuration.  The @cfg is + * usually one of the SoC specific macros defined in mach/<soc>-pins.h.  These + * are constructed using, and can be further enhanced with, the macros in + * plat/pincfg.h. + * + * If a pin's mode is set to GPIO, it is configured as an input to avoid + * side-effects.  The gpio can be manipulated later using standard GPIO API + * calls. + */ +static void config_pin(unsigned long cfg) +{ +	int pin = PIN_NUM(cfg); +	int pull = PIN_PULL(cfg); +	int af = PIN_ALT(cfg); +	int output = PIN_DIR(cfg); +	int val = PIN_VAL(cfg); + +	if (output) +		db8500_gpio_make_output(pin, val); +	else { +		db8500_gpio_make_input(pin); +		db8500_gpio_set_pull(pin, pull); +	} + +	gpio_set_mode(pin, af); +} + +/** + * db8500_config_pins - configure several pins at once + * @cfgs: array of pin configurations + * @num: number of elments in the array + * + * Configures several pins using config_pin(). Refer to that function for + * further information. + */ +void db8500_gpio_config_pins(unsigned long *cfgs, size_t num) +{ +	size_t i; + +	for (i = 0; i < num; i++) +		config_pin(cfgs[i]); +} diff --git a/drivers/gpio/mxc_gpio.c b/drivers/gpio/mxc_gpio.c index 661553552..2c79bff62 100644 --- a/drivers/gpio/mxc_gpio.c +++ b/drivers/gpio/mxc_gpio.c @@ -41,13 +41,15 @@ static unsigned long gpio_ports[] = {  	[0] = GPIO1_BASE_ADDR,  	[1] = GPIO2_BASE_ADDR,  	[2] = GPIO3_BASE_ADDR, -#if defined(CONFIG_MX25) || defined(CONFIG_MX51) || defined(CONFIG_MX53) || \ -		defined(CONFIG_MX6Q) +#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \ +		defined(CONFIG_MX53) || defined(CONFIG_MX6Q)  	[3] = GPIO4_BASE_ADDR,  #endif -#if defined(CONFIG_MX53) || defined(CONFIG_MX6Q) +#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6Q)  	[4] = GPIO5_BASE_ADDR,  	[5] = GPIO6_BASE_ADDR, +#endif +#if defined(CONFIG_MX53) || defined(CONFIG_MX6Q)  	[6] = GPIO7_BASE_ADDR,  #endif  }; @@ -116,7 +118,7 @@ int gpio_get_value(unsigned gpio)  	regs = (struct gpio_regs *)gpio_ports[port]; -	val = (readl(®s->gpio_dr) >> gpio) & 0x01; +	val = (readl(®s->gpio_psr) >> gpio) & 0x01;  	return val;  } diff --git a/drivers/gpio/mxs_gpio.c b/drivers/gpio/mxs_gpio.c index 38dbc81b4..29f19badd 100644 --- a/drivers/gpio/mxs_gpio.c +++ b/drivers/gpio/mxs_gpio.c @@ -73,8 +73,8 @@ int gpio_get_value(unsigned gpio)  {  	uint32_t bank = PAD_BANK(gpio);  	uint32_t offset = PINCTRL_DIN(bank); -	struct mx28_register_32 *reg = -		(struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset); +	struct mxs_register_32 *reg = +		(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);  	return (readl(®->reg) >> PAD_PIN(gpio)) & 1;  } @@ -83,8 +83,8 @@ void gpio_set_value(unsigned gpio, int value)  {  	uint32_t bank = PAD_BANK(gpio);  	uint32_t offset = PINCTRL_DOUT(bank); -	struct mx28_register_32 *reg = -		(struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset); +	struct mxs_register_32 *reg = +		(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);  	if (value)  		writel(1 << PAD_PIN(gpio), ®->reg_set); @@ -96,8 +96,8 @@ int gpio_direction_input(unsigned gpio)  {  	uint32_t bank = PAD_BANK(gpio);  	uint32_t offset = PINCTRL_DOE(bank); -	struct mx28_register_32 *reg = -		(struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset); +	struct mxs_register_32 *reg = +		(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);  	writel(1 << PAD_PIN(gpio), ®->reg_clr); @@ -108,8 +108,8 @@ int gpio_direction_output(unsigned gpio, int value)  {  	uint32_t bank = PAD_BANK(gpio);  	uint32_t offset = PINCTRL_DOE(bank); -	struct mx28_register_32 *reg = -		(struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset); +	struct mxs_register_32 *reg = +		(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);  	writel(1 << PAD_PIN(gpio), ®->reg_set); diff --git a/drivers/gpio/omap_gpio.c b/drivers/gpio/omap_gpio.c new file mode 100644 index 000000000..fc89f2a42 --- /dev/null +++ b/drivers/gpio/omap_gpio.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix <Tom.Rix@windriver.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * This work is derived from the linux 2.6.27 kernel source + * To fetch, use the kernel repository + * git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git + * Use the v2.6.27 tag. + * + * Below is the original's header including its copyright + * + *  linux/arch/arm/plat-omap/gpio.c + * + * Support functions for OMAP GPIO + * + * Copyright (C) 2003-2005 Nokia Corporation + * Written by Juha Yrjölä <juha.yrjola@nokia.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <common.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/errno.h> + +#define OMAP_GPIO_DIR_OUT	0 +#define OMAP_GPIO_DIR_IN	1 + +static inline const struct gpio_bank *get_gpio_bank(int gpio) +{ +	return &omap_gpio_bank[gpio >> 5]; +} + +static inline int get_gpio_index(int gpio) +{ +	return gpio & 0x1f; +} + +static inline int gpio_valid(int gpio) +{ +	if (gpio < 0) +		return -1; +	if (gpio < 192) +		return 0; +	return -1; +} + +static int check_gpio(int gpio) +{ +	if (gpio_valid(gpio) < 0) { +		printf("ERROR : check_gpio: invalid GPIO %d\n", gpio); +		return -1; +	} +	return 0; +} + +static void _set_gpio_direction(const struct gpio_bank *bank, int gpio, +				int is_input) +{ +	void *reg = bank->base; +	u32 l; + +	switch (bank->method) { +	case METHOD_GPIO_24XX: +		reg += OMAP_GPIO_OE; +		break; +	default: +		return; +	} +	l = __raw_readl(reg); +	if (is_input) +		l |= 1 << gpio; +	else +		l &= ~(1 << gpio); +	__raw_writel(l, reg); +} + +/** + * Get the direction of the GPIO by reading the GPIO_OE register + * corresponding to the specified bank. + */ +static int _get_gpio_direction(const struct gpio_bank *bank, int gpio) +{ +	void *reg = bank->base; +	u32 v; + +	switch (bank->method) { +	case METHOD_GPIO_24XX: +		reg += OMAP_GPIO_OE; +		break; +	default: +		return -1; +	} + +	v = __raw_readl(reg); + +	if (v & (1 << gpio)) +		return OMAP_GPIO_DIR_IN; +	else +		return OMAP_GPIO_DIR_OUT; +} + +static void _set_gpio_dataout(const struct gpio_bank *bank, int gpio, +				int enable) +{ +	void *reg = bank->base; +	u32 l = 0; + +	switch (bank->method) { +	case METHOD_GPIO_24XX: +		if (enable) +			reg += OMAP_GPIO_SETDATAOUT; +		else +			reg += OMAP_GPIO_CLEARDATAOUT; +		l = 1 << gpio; +		break; +	default: +		printf("omap3-gpio unknown bank method %s %d\n", +		       __FILE__, __LINE__); +		return; +	} +	__raw_writel(l, reg); +} + +/** + * Set value of the specified gpio + */ +int gpio_set_value(unsigned gpio, int value) +{ +	const struct gpio_bank *bank; + +	if (check_gpio(gpio) < 0) +		return -1; +	bank = get_gpio_bank(gpio); +	_set_gpio_dataout(bank, get_gpio_index(gpio), value); + +	return 0; +} + +/** + * Get value of the specified gpio + */ +int gpio_get_value(unsigned gpio) +{ +	const struct gpio_bank *bank; +	void *reg; +	int input; + +	if (check_gpio(gpio) < 0) +		return -1; +	bank = get_gpio_bank(gpio); +	reg = bank->base; +	switch (bank->method) { +	case METHOD_GPIO_24XX: +		input = _get_gpio_direction(bank, get_gpio_index(gpio)); +		switch (input) { +		case OMAP_GPIO_DIR_IN: +			reg += OMAP_GPIO_DATAIN; +			break; +		case OMAP_GPIO_DIR_OUT: +			reg += OMAP_GPIO_DATAOUT; +			break; +		default: +			return -1; +		} +		break; +	default: +		return -1; +	} +	return (__raw_readl(reg) +			& (1 << get_gpio_index(gpio))) != 0; +} + +/** + * Set gpio direction as input + */ +int gpio_direction_input(unsigned gpio) +{ +	const struct gpio_bank *bank; + +	if (check_gpio(gpio) < 0) +		return -1; + +	bank = get_gpio_bank(gpio); +	_set_gpio_direction(bank, get_gpio_index(gpio), 1); + +	return 0; +} + +/** + * Set gpio direction as output + */ +int gpio_direction_output(unsigned gpio, int value) +{ +	const struct gpio_bank *bank; + +	if (check_gpio(gpio) < 0) +		return -1; + +	bank = get_gpio_bank(gpio); +	_set_gpio_dataout(bank, get_gpio_index(gpio), value); +	_set_gpio_direction(bank, get_gpio_index(gpio), 0); + +	return 0; +} + +/** + * Request a gpio before using it. + * + * NOTE: Argument 'label' is unused. + */ +int gpio_request(unsigned gpio, const char *label) +{ +	if (check_gpio(gpio) < 0) +		return -1; + +	return 0; +} + +/** + * Reset and free the gpio after using it. + */ +int gpio_free(unsigned gpio) +{ +	return 0; +} diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c index 60ec6e3d7..747f4cf92 100644 --- a/drivers/gpio/tegra_gpio.c +++ b/drivers/gpio/tegra_gpio.c @@ -1,5 +1,5 @@  /* - * NVIDIA Tegra2 GPIO handling. + * NVIDIA Tegra20 GPIO handling.   *  (C) Copyright 2010-2012   *  NVIDIA Corporation <www.nvidia.com>   * @@ -30,14 +30,14 @@  #include <common.h>  #include <asm/io.h>  #include <asm/bitops.h> -#include <asm/arch/tegra2.h> +#include <asm/arch/tegra20.h>  #include <asm/gpio.h>  enum { -	TEGRA2_CMD_INFO, -	TEGRA2_CMD_PORT, -	TEGRA2_CMD_OUTPUT, -	TEGRA2_CMD_INPUT, +	TEGRA_CMD_INFO, +	TEGRA_CMD_PORT, +	TEGRA_CMD_OUTPUT, +	TEGRA_CMD_INPUT,  };  static struct gpio_names { diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 48aaaa626..2a193c220 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -38,10 +38,10 @@  void mxs_i2c_reset(void)  { -	struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE; +	struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE;  	int ret; -	ret = mx28_reset_block(&i2c_regs->hw_i2c_ctrl0_reg); +	ret = mxs_reset_block(&i2c_regs->hw_i2c_ctrl0_reg);  	if (ret) {  		debug("MXS I2C: Block reset timeout\n");  		return; @@ -57,7 +57,7 @@ void mxs_i2c_reset(void)  void mxs_i2c_setup_read(uint8_t chip, int len)  { -	struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE; +	struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE;  	writel(I2C_QUEUECMD_RETAIN_CLOCK | I2C_QUEUECMD_PRE_SEND_START |  		I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION | @@ -76,7 +76,7 @@ void mxs_i2c_setup_read(uint8_t chip, int len)  void mxs_i2c_write(uchar chip, uint addr, int alen,  			uchar *buf, int blen, int stop)  { -	struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE; +	struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE;  	uint32_t data;  	int i, remain, off; @@ -119,7 +119,7 @@ void mxs_i2c_write(uchar chip, uint addr, int alen,  int mxs_i2c_wait_for_ack(void)  { -	struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE; +	struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE;  	uint32_t tmp;  	int timeout = MXS_I2C_MAX_TIMEOUT; @@ -157,7 +157,7 @@ err:  int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)  { -	struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE; +	struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE;  	uint32_t tmp = 0;  	int ret;  	int i; @@ -212,7 +212,7 @@ int i2c_probe(uchar chip)  void i2c_init(int speed, int slaveadd)  { -	struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE; +	struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE;  	mxs_i2c_reset(); diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index 81193b0e6..094305fdf 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -150,16 +150,19 @@ void i2c_init(int speed, int slaveadd)  		bus_initialized[current_bus] = 1;  } -static int i2c_read_byte(u8 devaddr, u8 regoffset, u8 *value) +static int i2c_read_byte(u8 devaddr, u16 regoffset, u8 alen, u8 *value)  {  	int i2c_error = 0;  	u16 status; +	int i = 2 - alen; +	u8 tmpbuf[2] = {(regoffset) >> 8, regoffset & 0xff}; +	u16 w;  	/* wait until bus not busy */  	wait_for_bb();  	/* one byte only */ -	writew(1, &i2c_base->cnt); +	writew(alen, &i2c_base->cnt);  	/* set slave address */  	writew(devaddr, &i2c_base->sa);  	/* no stop bit needed here */ @@ -174,8 +177,12 @@ static int i2c_read_byte(u8 devaddr, u8 regoffset, u8 *value)  			goto read_exit;  		}  		if (status & I2C_STAT_XRDY) { -			/* Important: have to use byte access */ -			writeb(regoffset, &i2c_base->data); +			w = tmpbuf[i++]; +#if !(defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ +	defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX)) +			w |= tmpbuf[i++] << 8; +#endif +			writew(w, &i2c_base->data);  			writew(I2C_STAT_XRDY, &i2c_base->stat);  		}  		if (status & I2C_STAT_ARDY) { @@ -303,18 +310,18 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)  {  	int i; -	if (alen > 1) { +	if (alen > 2) {  		printf("I2C read: addr len %d not supported\n", alen);  		return 1;  	} -	if (addr + len > 256) { +	if (addr + len > (1 << 16)) {  		puts("I2C read: address out of range\n");  		return 1;  	}  	for (i = 0; i < len; i++) { -		if (i2c_read_byte(chip, addr + i, &buffer[i])) { +		if (i2c_read_byte(chip, addr + i, alen, &buffer[i])) {  			puts("I2C read: I/O error\n");  			i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);  			return 1; @@ -329,13 +336,15 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)  	int i;  	u16 status;  	int i2c_error = 0; +	u16 w; +	u8 tmpbuf[2] = {addr >> 8, addr & 0xff}; -	if (alen > 1) { +	if (alen > 2) {  		printf("I2C write: addr len %d not supported\n", alen);  		return 1;  	} -	if (addr + len > 256) { +	if (addr + len > (1 << 16)) {  		printf("I2C write: address 0x%x + 0x%x out of range\n",  				addr, len);  		return 1; @@ -353,28 +362,8 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)  	writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |  		I2C_CON_STP, &i2c_base->con); -	/* Send address byte */ -	status = wait_for_pin(); - -	if (status == 0 || status & I2C_STAT_NACK) { -		i2c_error = 1; -		printf("error waiting for i2c address ACK (status=0x%x)\n", -		      status); -		goto write_exit; -	} - -	if (status & I2C_STAT_XRDY) { -		writeb(addr & 0xFF, &i2c_base->data); -		writew(I2C_STAT_XRDY, &i2c_base->stat); -	} else { -		i2c_error = 1; -		printf("i2c bus not ready for transmit (status=0x%x)\n", -		      status); -		goto write_exit; -	} - -	/* address phase is over, now write data */ -	for (i = 0; i < len; i++) { +	/* Send address and data */ +	for (i = -alen; i < len; i++) {  		status = wait_for_pin();  		if (status == 0 || status & I2C_STAT_NACK) { @@ -385,7 +374,12 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)  		}  		if (status & I2C_STAT_XRDY) { -			writeb(buffer[i], &i2c_base->data); +			w = (i < 0) ? tmpbuf[2+i] : buffer[i]; +#if !(defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ +	defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX)) +			w |= ((++i < 0) ? tmpbuf[2+i] : buffer[i]) << 8; +#endif +			writew(w, &i2c_base->data);  			writew(I2C_STAT_XRDY, &i2c_base->stat);  		} else {  			i2c_error = 1; @@ -448,6 +442,14 @@ int i2c_set_bus_num(unsigned int bus)  		return -1;  	} +#if I2C_BUS_MAX == 4 +	if (bus == 3) +		i2c_base = (struct i2c *)I2C_BASE4; +	else +	if (bus == 2) +		i2c_base = (struct i2c *)I2C_BASE3; +	else +#endif  #if I2C_BUS_MAX == 3  	if (bus == 2)  		i2c_base = (struct i2c *)I2C_BASE3; diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index 5b6ea0e75..e3be14e3c 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -262,7 +262,7 @@ exit:  	return error;  } -static int tegra2_i2c_write_data(u32 addr, u8 *data, u32 len) +static int tegra_i2c_write_data(u32 addr, u8 *data, u32 len)  {  	int error;  	struct i2c_trans_info trans_info; @@ -275,12 +275,12 @@ static int tegra2_i2c_write_data(u32 addr, u8 *data, u32 len)  	error = send_recv_packets(&i2c_controllers[i2c_bus_num], &trans_info);  	if (error) -		debug("tegra2_i2c_write_data: Error (%d) !!!\n", error); +		debug("tegra_i2c_write_data: Error (%d) !!!\n", error);  	return error;  } -static int tegra2_i2c_read_data(u32 addr, u8 *data, u32 len) +static int tegra_i2c_read_data(u32 addr, u8 *data, u32 len)  {  	int error;  	struct i2c_trans_info trans_info; @@ -293,7 +293,7 @@ static int tegra2_i2c_read_data(u32 addr, u8 *data, u32 len)  	error = send_recv_packets(&i2c_controllers[i2c_bus_num], &trans_info);  	if (error) -		debug("tegra2_i2c_read_data: Error (%d) !!!\n", error); +		debug("tegra_i2c_read_data: Error (%d) !!!\n", error);  	return error;  } @@ -438,7 +438,7 @@ int i2c_write_data(uchar chip, uchar *buffer, int len)  	debug("\n");  	/* Shift 7-bit address over for lower-level i2c functions */ -	rc = tegra2_i2c_write_data(chip << 1, buffer, len); +	rc = tegra_i2c_write_data(chip << 1, buffer, len);  	if (rc)  		debug("i2c_write_data(): rc=%d\n", rc); @@ -452,7 +452,7 @@ int i2c_read_data(uchar chip, uchar *buffer, int len)  	debug("inside i2c_read_data():\n");  	/* Shift 7-bit address over for lower-level i2c functions */ -	rc = tegra2_i2c_read_data(chip << 1, buffer, len); +	rc = tegra_i2c_read_data(chip << 1, buffer, len);  	if (rc) {  		debug("i2c_read_data(): rc=%d\n", rc);  		return rc; diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 5c831b261..0805e8667 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -26,7 +26,7 @@ include $(TOPDIR)/config.mk  LIB	:= $(obj)libinput.o  COBJS-$(CONFIG_I8042_KBD) += i8042.o -COBJS-$(CONFIG_TEGRA2_KEYBOARD) += tegra-kbc.o +COBJS-$(CONFIG_TEGRA_KEYBOARD) += tegra-kbc.o  ifdef CONFIG_PS2KBD  COBJS-y += keyboard.o pc_keyb.o  COBJS-$(CONFIG_PS2MULT) += ps2mult.o ps2ser.o diff --git a/drivers/input/key_matrix.c b/drivers/input/key_matrix.c index 84b898ff3..715e57a2a 100644 --- a/drivers/input/key_matrix.c +++ b/drivers/input/key_matrix.c @@ -23,6 +23,7 @@   * MA 02111-1307 USA   */ +#include <common.h>  #include <fdtdec.h>  #include <key_matrix.h>  #include <malloc.h> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index c56773701..2b96cdcd4 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -25,6 +25,10 @@ include $(TOPDIR)/config.mk  LIB	:= $(obj)libmmc.o +ifdef CONFIG_SPL_MMC_LOAD +COBJS-$(CONFIG_SPL_MMC_LOAD)	+= spl_mmc_load.o +endif +  COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o  COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o  COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c index 09d443ee3..af1380a45 100644 --- a/drivers/mmc/arm_pl180_mmci.c +++ b/drivers/mmc/arm_pl180_mmci.c @@ -32,14 +32,10 @@  #include "arm_pl180_mmci.h"  #include <malloc.h> -struct mmc_host { -	struct sdi_registers *base; -}; -  static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd)  {  	u32 hoststatus, statusmask; -	struct mmc_host *host = dev->priv; +	struct pl180_mmc_host *host = dev->priv;  	statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL;  	if ((cmd->resp_type & MMC_RSP_PRESENT)) @@ -53,10 +49,10 @@ static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd)  	writel(statusmask, &host->base->status_clear);  	if (hoststatus & SDI_STA_CTIMEOUT) { -		printf("CMD%d time out\n", cmd->cmdidx); -		return -ETIMEDOUT; +		debug("CMD%d time out\n", cmd->cmdidx); +		return TIMEOUT;  	} else if ((hoststatus & SDI_STA_CCRCFAIL) && -		   (cmd->flags & MMC_RSP_CRC)) { +		   (cmd->resp_type & MMC_RSP_CRC)) {  		printf("CMD%d CRC error\n", cmd->cmdidx);  		return -EILSEQ;  	} @@ -80,7 +76,7 @@ static int do_command(struct mmc *dev, struct mmc_cmd *cmd)  {  	int result;  	u32 sdi_cmd = 0; -	struct mmc_host *host = dev->priv; +	struct pl180_mmc_host *host = dev->priv;  	sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN); @@ -112,7 +108,7 @@ static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize)  {  	u32 *tempbuff = dest;  	u64 xfercount = blkcount * blksize; -	struct mmc_host *host = dev->priv; +	struct pl180_mmc_host *host = dev->priv;  	u32 status, status_err;  	debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); @@ -168,7 +164,7 @@ static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize)  	u32 *tempbuff = src;  	int i;  	u64 xfercount = blkcount * blksize; -	struct mmc_host *host = dev->priv; +	struct pl180_mmc_host *host = dev->priv;  	u32 status, status_err;  	debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); @@ -227,14 +223,19 @@ static int do_data_transfer(struct mmc *dev,  			    struct mmc_data *data)  {  	int error = -ETIMEDOUT; -	struct mmc_host *host = dev->priv; +	struct pl180_mmc_host *host = dev->priv;  	u32 blksz = 0;  	u32 data_ctrl = 0;  	u32 data_len = (u32) (data->blocks * data->blocksize); -	blksz = (ffs(data->blocksize) - 1); -	data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK); -	data_ctrl |= SDI_DCTRL_DTEN; +	if (!host->version2) { +		blksz = (ffs(data->blocksize) - 1); +		data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK); +	} else { +		blksz = data->blocksize; +		data_ctrl |= (blksz << SDI_DCTRL_DBLOCKSIZE_V2_SHIFT); +	} +	data_ctrl |= SDI_DCTRL_DTEN | SDI_DCTRL_BUSYMODE;  	writel(SDI_DTIMER_DEFAULT, &host->base->datatimer);  	writel(data_len, &host->base->datalength); @@ -257,7 +258,7 @@ static int do_data_transfer(struct mmc *dev,  		writel(data_ctrl, &host->base->datactrl);  		error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks, -				    (u32)data->blocksize); +							(u32)data->blocksize);  	}  	return error; @@ -280,17 +281,16 @@ static int host_request(struct mmc *dev,  /* MMC uses open drain drivers in the enumeration phase */  static int mmc_host_reset(struct mmc *dev)  { -	struct mmc_host *host = dev->priv; -	u32 sdi_u32 = SDI_PWR_OPD | SDI_PWR_PWRCTRL_ON; +	struct pl180_mmc_host *host = dev->priv; -	writel(sdi_u32, &host->base->power); +	writel(host->pwr_init, &host->base->power);  	return 0;  }  static void host_set_ios(struct mmc *dev)  { -	struct mmc_host *host = dev->priv; +	struct pl180_mmc_host *host = dev->priv;  	u32 sdi_clkcr;  	sdi_clkcr = readl(&host->base->clock); @@ -298,15 +298,26 @@ static void host_set_ios(struct mmc *dev)  	/* Ramp up the clock rate */  	if (dev->clock) {  		u32 clkdiv = 0; +		u32 tmp_clock; -		if (dev->clock >= dev->f_max) +		if (dev->clock >= dev->f_max) { +			clkdiv = 0;  			dev->clock = dev->f_max; +		} else { +			clkdiv = (host->clock_in / dev->clock) - 2; +		} -		clkdiv = ((ARM_MCLK / dev->clock) / 2) - 1; +		tmp_clock = host->clock_in / (clkdiv + 2); +		while (tmp_clock > dev->clock) { +			clkdiv++; +			tmp_clock = host->clock_in / (clkdiv + 2); +		}  		if (clkdiv > SDI_CLKCR_CLKDIV_MASK)  			clkdiv = SDI_CLKCR_CLKDIV_MASK; +		tmp_clock = host->clock_in / (clkdiv + 2); +		dev->clock = tmp_clock;  		sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK);  		sdi_clkcr |= clkdiv;  	} @@ -322,8 +333,11 @@ static void host_set_ios(struct mmc *dev)  		case 4:  			buswidth |= SDI_CLKCR_WIDBUS_4;  			break; +		case 8: +			buswidth |= SDI_CLKCR_WIDBUS_8; +			break;  		default: -			printf("Invalid bus width\n"); +			printf("Invalid bus width: %d\n", dev->bus_width);  			break;  		}  		sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK); @@ -334,83 +348,40 @@ static void host_set_ios(struct mmc *dev)  	udelay(CLK_CHANGE_DELAY);  } -struct mmc *alloc_mmc_struct(void) -{ -	struct mmc_host *host = NULL; -	struct mmc *mmc_device = NULL; - -	host = malloc(sizeof(struct mmc_host)); -	if (!host) -		return NULL; - -	mmc_device = malloc(sizeof(struct mmc)); -	if (!mmc_device) -		goto err; - -	mmc_device->priv = host; -	return mmc_device; - -err: -	free(host); -	return NULL; -} -  /*   * mmc_host_init - initialize the mmc controller.   * Set initial clock and power for mmc slot.   * Initialize mmc struct and register with mmc framework.   */ -static int arm_pl180_mmci_host_init(struct mmc *dev) +int arm_pl180_mmci_init(struct pl180_mmc_host *host)  { -	struct mmc_host *host = dev->priv; +	struct mmc *dev;  	u32 sdi_u32; -	host->base = (struct sdi_registers *)CONFIG_ARM_PL180_MMCI_BASE; +	dev = malloc(sizeof(struct mmc)); +	if (!dev) +		return -ENOMEM; -	/* Initially set power-on, full voltage & MMCI read */ -	sdi_u32 = INIT_PWR; -	writel(sdi_u32, &host->base->power); +	memset(dev, 0, sizeof(struct mmc)); +	dev->priv = host; -	/* setting clk freq 505KHz */ -	sdi_u32 = SDI_CLKCR_CLKDIV_INIT | SDI_CLKCR_CLKEN; -	writel(sdi_u32, &host->base->clock); +	writel(host->pwr_init, &host->base->power); +	writel(host->clkdiv_init, &host->base->clock);  	udelay(CLK_CHANGE_DELAY);  	/* Disable mmc interrupts */  	sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK;  	writel(sdi_u32, &host->base->mask0); - -	sprintf(dev->name, "MMC"); -	dev->clock = ARM_MCLK / (2 * (SDI_CLKCR_CLKDIV_INIT + 1)); +	strncpy(dev->name, host->name, sizeof(dev->name));  	dev->send_cmd = host_request;  	dev->set_ios = host_set_ios;  	dev->init = mmc_host_reset;  	dev->getcd = NULL; -	dev->host_caps = 0; -	dev->voltages = VOLTAGE_WINDOW_MMC; -	dev->f_min = dev->clock; -	dev->f_max = CONFIG_ARM_PL180_MMCI_CLOCK_FREQ; - -	return 0; -} - -int arm_pl180_mmci_init(void) -{ -	int error; -	struct mmc *dev; - -	dev = alloc_mmc_struct(); -	if (!dev) -		return -1; - -	error = arm_pl180_mmci_host_init(dev); -	if (error) { -		printf("mmci_host_init error - %d\n", error); -		return -1; -	} - -	dev->b_max = 0; - +	dev->host_caps = host->caps; +	dev->voltages = host->voltages; +	dev->f_min = host->clock_min; +	dev->f_max = host->clock_max; +	dev->b_max = host->b_max;  	mmc_register(dev);  	debug("registered mmc interface number is:%d\n", dev->block_dev.dev); diff --git a/drivers/mmc/arm_pl180_mmci.h b/drivers/mmc/arm_pl180_mmci.h index 42fbe3e38..06709ed7f 100644 --- a/drivers/mmc/arm_pl180_mmci.h +++ b/drivers/mmc/arm_pl180_mmci.h @@ -26,8 +26,6 @@  #ifndef __ARM_PL180_MMCI_H__  #define __ARM_PL180_MMCI_H__ -int arm_pl180_mmci_init(void); -  #define COMMAND_REG_DELAY	300  #define DATA_REG_DELAY		1000  #define CLK_CHANGE_DELAY	2000 @@ -59,8 +57,13 @@ int arm_pl180_mmci_init(void);  #define SDI_CLKCR_WIDBUS_MASK	0x00001800  #define SDI_CLKCR_WIDBUS_1	0x00000000  #define SDI_CLKCR_WIDBUS_4	0x00000800 +/* V2 only */ +#define SDI_CLKCR_WIDBUS_8	0x00001000 +#define SDI_CLKCR_NEDGE		0x00002000 +#define SDI_CLKCR_HWFC_EN	0x00004000 -#define SDI_CLKCR_CLKDIV_INIT	0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */ +#define SDI_CLKCR_CLKDIV_INIT_V1 0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */ +#define SDI_CLKCR_CLKDIV_INIT_V2 0x000000FD  /* SDI command register bits */  #define SDI_CMD_CMDINDEX_MASK	0x000000FF @@ -144,6 +147,8 @@ int arm_pl180_mmci_init(void);  #define SDI_DCTRL_DBOOTMODEEN	0x00002000  #define SDI_DCTRL_BUSYMODE	0x00004000  #define SDI_DCTRL_DDR_MODE	0x00008000 +#define SDI_DCTRL_DBLOCKSIZE_V2_MASK   0x7fff0000 +#define SDI_DCTRL_DBLOCKSIZE_V2_SHIFT  16  #define SDI_FIFO_BURST_SIZE	8 @@ -180,4 +185,20 @@ struct sdi_registers {  	u32 pcell_id3;		/* 0xFFC*/  }; +struct pl180_mmc_host { +	struct sdi_registers *base; +	char name[32]; +	unsigned int b_max; +	unsigned int voltages; +	unsigned int caps; +	unsigned int clock_in; +	unsigned int clock_min; +	unsigned int clock_max; +	unsigned int clkdiv_init; +	unsigned int pwr_init; +	int version2; +}; + +int arm_pl180_mmci_init(struct pl180_mmc_host *); +  #endif diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index b6c969d2c..3f8d30db4 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -479,9 +479,10 @@ static int esdhc_init(struct mmc *mmc)  	while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout)  		udelay(1000); +#ifndef ARCH_MXC  	/* Enable cache snooping */ -	if (cfg && !cfg->no_snoop) -		esdhc_write32(®s->scr, 0x00000040); +	esdhc_write32(®s->scr, 0x00000040); +#endif  	esdhc_write32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index c1c286298..a60cfe1cb 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -151,7 +151,6 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  	printf("CMD_SEND:%d\n", cmd->cmdidx);  	printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg); -	printf("\t\tFLAG\t\t\t %d\n", cmd->flags);  	ret = mmc->send_cmd(mmc, cmd, data);  	switch (cmd->resp_type) {  		case MMC_RSP_NONE: @@ -213,7 +212,6 @@ int mmc_send_status(struct mmc *mmc, int timeout)  	cmd.resp_type = MMC_RSP_R1;  	if (!mmc_host_is_spi(mmc))  		cmd.cmdarg = mmc->rca << 16; -	cmd.flags = 0;  	do {  		err = mmc_send_cmd(mmc, &cmd, NULL); @@ -238,7 +236,7 @@ int mmc_send_status(struct mmc *mmc, int timeout)  	status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9;  	printf("CURR STATE:%d\n", status);  #endif -	if (!timeout) { +	if (timeout <= 0) {  		printf("Timeout waiting card ready\n");  		return TIMEOUT;  	} @@ -253,7 +251,6 @@ int mmc_set_blocklen(struct mmc *mmc, int len)  	cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;  	cmd.resp_type = MMC_RSP_R1;  	cmd.cmdarg = len; -	cmd.flags = 0;  	return mmc_send_cmd(mmc, &cmd, NULL);  } @@ -299,7 +296,6 @@ static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)  	cmd.cmdidx = start_cmd;  	cmd.cmdarg = start;  	cmd.resp_type = MMC_RSP_R1; -	cmd.flags = 0;  	err = mmc_send_cmd(mmc, &cmd, NULL);  	if (err) @@ -386,7 +382,6 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)  		cmd.cmdarg = start * mmc->write_bl_len;  	cmd.resp_type = MMC_RSP_R1; -	cmd.flags = 0;  	data.src = src;  	data.blocks = blkcnt; @@ -405,7 +400,6 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)  		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;  		cmd.cmdarg = 0;  		cmd.resp_type = MMC_RSP_R1b; -		cmd.flags = 0;  		if (mmc_send_cmd(mmc, &cmd, NULL)) {  			printf("mmc fail to send stop cmd\n");  			return 0; @@ -459,7 +453,6 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt)  		cmd.cmdarg = start * mmc->read_bl_len;  	cmd.resp_type = MMC_RSP_R1; -	cmd.flags = 0;  	data.dest = dst;  	data.blocks = blkcnt; @@ -473,7 +466,6 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt)  		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;  		cmd.cmdarg = 0;  		cmd.resp_type = MMC_RSP_R1b; -		cmd.flags = 0;  		if (mmc_send_cmd(mmc, &cmd, NULL)) {  			printf("mmc fail to send stop cmd\n");  			return 0; @@ -525,7 +517,6 @@ int mmc_go_idle(struct mmc* mmc)  	cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;  	cmd.cmdarg = 0;  	cmd.resp_type = MMC_RSP_NONE; -	cmd.flags = 0;  	err = mmc_send_cmd(mmc, &cmd, NULL); @@ -548,7 +539,6 @@ sd_send_op_cond(struct mmc *mmc)  		cmd.cmdidx = MMC_CMD_APP_CMD;  		cmd.resp_type = MMC_RSP_R1;  		cmd.cmdarg = 0; -		cmd.flags = 0;  		err = mmc_send_cmd(mmc, &cmd, NULL); @@ -589,7 +579,6 @@ sd_send_op_cond(struct mmc *mmc)  		cmd.cmdidx = MMC_CMD_SPI_READ_OCR;  		cmd.resp_type = MMC_RSP_R3;  		cmd.cmdarg = 0; -		cmd.flags = 0;  		err = mmc_send_cmd(mmc, &cmd, NULL); @@ -618,7 +607,6 @@ int mmc_send_op_cond(struct mmc *mmc)   	cmd.cmdidx = MMC_CMD_SEND_OP_COND;   	cmd.resp_type = MMC_RSP_R3;   	cmd.cmdarg = 0; - 	cmd.flags = 0;   	err = mmc_send_cmd(mmc, &cmd, NULL); @@ -638,8 +626,6 @@ int mmc_send_op_cond(struct mmc *mmc)  		if (mmc->host_caps & MMC_MODE_HC)  			cmd.cmdarg |= OCR_HCS; -		cmd.flags = 0; -  		err = mmc_send_cmd(mmc, &cmd, NULL);  		if (err) @@ -655,7 +641,6 @@ int mmc_send_op_cond(struct mmc *mmc)  		cmd.cmdidx = MMC_CMD_SPI_READ_OCR;  		cmd.resp_type = MMC_RSP_R3;  		cmd.cmdarg = 0; -		cmd.flags = 0;  		err = mmc_send_cmd(mmc, &cmd, NULL); @@ -673,7 +658,7 @@ int mmc_send_op_cond(struct mmc *mmc)  } -int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd) +int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd)  {  	struct mmc_cmd cmd;  	struct mmc_data data; @@ -683,9 +668,8 @@ int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd)  	cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;  	cmd.resp_type = MMC_RSP_R1;  	cmd.cmdarg = 0; -	cmd.flags = 0; -	data.dest = ext_csd; +	data.dest = (char *)ext_csd;  	data.blocks = 1;  	data.blocksize = 512;  	data.flags = MMC_DATA_READ; @@ -707,7 +691,6 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)  	cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |  				 (index << 16) |  				 (value << 8); -	cmd.flags = 0;  	ret = mmc_send_cmd(mmc, &cmd, NULL); @@ -721,7 +704,7 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)  int mmc_change_freq(struct mmc *mmc)  { -	ALLOC_CACHE_ALIGN_BUFFER(char, ext_csd, 512); +	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, 512);  	char cardtype;  	int err; @@ -800,7 +783,6 @@ int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)  	cmd.cmdarg = (mode << 31) | 0xffffff;  	cmd.cmdarg &= ~(0xf << (group * 4));  	cmd.cmdarg |= value << (group * 4); -	cmd.flags = 0;  	data.dest = (char *)resp;  	data.blocksize = 64; @@ -829,7 +811,6 @@ int sd_change_freq(struct mmc *mmc)  	cmd.cmdidx = MMC_CMD_APP_CMD;  	cmd.resp_type = MMC_RSP_R1;  	cmd.cmdarg = mmc->rca << 16; -	cmd.flags = 0;  	err = mmc_send_cmd(mmc, &cmd, NULL); @@ -839,7 +820,6 @@ int sd_change_freq(struct mmc *mmc)  	cmd.cmdidx = SD_CMD_APP_SEND_SCR;  	cmd.resp_type = MMC_RSP_R1;  	cmd.cmdarg = 0; -	cmd.flags = 0;  	timeout = 3; @@ -983,8 +963,8 @@ int mmc_startup(struct mmc *mmc)  	uint mult, freq;  	u64 cmult, csize, capacity;  	struct mmc_cmd cmd; -	ALLOC_CACHE_ALIGN_BUFFER(char, ext_csd, 512); -	ALLOC_CACHE_ALIGN_BUFFER(char, test_csd, 512); +	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, 512); +	ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, 512);  	int timeout = 1000;  #ifdef CONFIG_MMC_SPI_CRC_ON @@ -992,7 +972,6 @@ int mmc_startup(struct mmc *mmc)  		cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;  		cmd.resp_type = MMC_RSP_R1;  		cmd.cmdarg = 1; -		cmd.flags = 0;  		err = mmc_send_cmd(mmc, &cmd, NULL);  		if (err) @@ -1005,7 +984,6 @@ int mmc_startup(struct mmc *mmc)  		MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */  	cmd.resp_type = MMC_RSP_R2;  	cmd.cmdarg = 0; -	cmd.flags = 0;  	err = mmc_send_cmd(mmc, &cmd, NULL); @@ -1023,7 +1001,6 @@ int mmc_startup(struct mmc *mmc)  		cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;  		cmd.cmdarg = mmc->rca << 16;  		cmd.resp_type = MMC_RSP_R6; -		cmd.flags = 0;  		err = mmc_send_cmd(mmc, &cmd, NULL); @@ -1038,7 +1015,6 @@ int mmc_startup(struct mmc *mmc)  	cmd.cmdidx = MMC_CMD_SEND_CSD;  	cmd.resp_type = MMC_RSP_R2;  	cmd.cmdarg = mmc->rca << 16; -	cmd.flags = 0;  	err = mmc_send_cmd(mmc, &cmd, NULL); @@ -1115,7 +1091,6 @@ int mmc_startup(struct mmc *mmc)  		cmd.cmdidx = MMC_CMD_SELECT_CARD;  		cmd.resp_type = MMC_RSP_R1;  		cmd.cmdarg = mmc->rca << 16; -		cmd.flags = 0;  		err = mmc_send_cmd(mmc, &cmd, NULL);  		if (err) @@ -1162,7 +1137,8 @@ int mmc_startup(struct mmc *mmc)  		}  		/* store the partition info of emmc */ -		if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) +		if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || +		    ext_csd[EXT_CSD_BOOT_MULT])  			mmc->part_config = ext_csd[EXT_CSD_PART_CONF];  	} @@ -1182,7 +1158,6 @@ int mmc_startup(struct mmc *mmc)  			cmd.cmdidx = MMC_CMD_APP_CMD;  			cmd.resp_type = MMC_RSP_R1;  			cmd.cmdarg = mmc->rca << 16; -			cmd.flags = 0;  			err = mmc_send_cmd(mmc, &cmd, NULL);  			if (err) @@ -1191,7 +1166,6 @@ int mmc_startup(struct mmc *mmc)  			cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;  			cmd.resp_type = MMC_RSP_R1;  			cmd.cmdarg = 2; -			cmd.flags = 0;  			err = mmc_send_cmd(mmc, &cmd, NULL);  			if (err)  				return err; @@ -1259,7 +1233,9 @@ int mmc_startup(struct mmc *mmc)  			(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);  	sprintf(mmc->block_dev.revision, "%d.%d", mmc->cid[2] >> 28,  			(mmc->cid[2] >> 24) & 0xf); +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)  	init_part(&mmc->block_dev); +#endif  	return 0;  } @@ -1273,7 +1249,6 @@ int mmc_send_if_cond(struct mmc *mmc)  	/* We set the bit if the host supports voltages between 2.7 and 3.6 V */  	cmd.cmdarg = ((mmc->voltages & 0xff8000) != 0) << 8 | 0xaa;  	cmd.resp_type = MMC_RSP_R7; -	cmd.flags = 0;  	err = mmc_send_cmd(mmc, &cmd, NULL); @@ -1311,10 +1286,9 @@ int mmc_register(struct mmc *mmc)  block_dev_desc_t *mmc_get_dev(int dev)  {  	struct mmc *mmc = find_mmc_device(dev); -	if (!mmc) +	if (!mmc || mmc_init(mmc))  		return NULL; -	mmc_init(mmc);  	return &mmc->block_dev;  }  #endif diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c index 4187a9412..c80b41b19 100644 --- a/drivers/mmc/mxsmmc.c +++ b/drivers/mmc/mxsmmc.c @@ -43,16 +43,9 @@  #include <asm/arch/sys_proto.h>  #include <asm/arch/dma.h> -/* - * CONFIG_MXS_MMC_DMA: This feature is highly experimental and has no - *                     performance benefit unless you operate the platform with - *                     data cache enabled. This is disabled by default, enable - *                     only if you know what you're doing. - */ -  struct mxsmmc_priv {  	int			id; -	struct mx28_ssp_regs	*regs; +	struct mxs_ssp_regs	*regs;  	uint32_t		clkseq_bypass;  	uint32_t		*clkctrl_ssp;  	uint32_t		buswidth; @@ -61,6 +54,91 @@ struct mxsmmc_priv {  };  #define	MXSMMC_MAX_TIMEOUT	10000 +#define MXSMMC_SMALL_TRANSFER	512 + +static int mxsmmc_send_cmd_pio(struct mxsmmc_priv *priv, struct mmc_data *data) +{ +	struct mxs_ssp_regs *ssp_regs = priv->regs; +	uint32_t *data_ptr; +	int timeout = MXSMMC_MAX_TIMEOUT; +	uint32_t reg; +	uint32_t data_count = data->blocksize * data->blocks; + +	if (data->flags & MMC_DATA_READ) { +		data_ptr = (uint32_t *)data->dest; +		while (data_count && --timeout) { +			reg = readl(&ssp_regs->hw_ssp_status); +			if (!(reg & SSP_STATUS_FIFO_EMPTY)) { +				*data_ptr++ = readl(&ssp_regs->hw_ssp_data); +				data_count -= 4; +				timeout = MXSMMC_MAX_TIMEOUT; +			} else +				udelay(1000); +		} +	} else { +		data_ptr = (uint32_t *)data->src; +		timeout *= 100; +		while (data_count && --timeout) { +			reg = readl(&ssp_regs->hw_ssp_status); +			if (!(reg & SSP_STATUS_FIFO_FULL)) { +				writel(*data_ptr++, &ssp_regs->hw_ssp_data); +				data_count -= 4; +				timeout = MXSMMC_MAX_TIMEOUT; +			} else +				udelay(1000); +		} +	} + +	return timeout ? 0 : COMM_ERR; +} + +static int mxsmmc_send_cmd_dma(struct mxsmmc_priv *priv, struct mmc_data *data) +{ +	uint32_t data_count = data->blocksize * data->blocks; +	uint32_t cache_data_count; +	int dmach; +	struct mxs_dma_desc *desc = priv->desc; + +	memset(desc, 0, sizeof(struct mxs_dma_desc)); +	desc->address = (dma_addr_t)desc; + +	if (data_count % ARCH_DMA_MINALIGN) +		cache_data_count = roundup(data_count, ARCH_DMA_MINALIGN); +	else +		cache_data_count = data_count; + +	if (data->flags & MMC_DATA_READ) { +		priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE; +		priv->desc->cmd.address = (dma_addr_t)data->dest; +	} else { +		priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ; +		priv->desc->cmd.address = (dma_addr_t)data->src; + +		/* Flush data to DRAM so DMA can pick them up */ +		flush_dcache_range((uint32_t)priv->desc->cmd.address, +			(uint32_t)(priv->desc->cmd.address + cache_data_count)); +	} + +	/* Invalidate the area, so no writeback into the RAM races with DMA */ +	invalidate_dcache_range((uint32_t)priv->desc->cmd.address, +			(uint32_t)(priv->desc->cmd.address + cache_data_count)); + +	priv->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM | +				(data_count << MXS_DMA_DESC_BYTES_OFFSET); + +	dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + priv->id; +	mxs_dma_desc_append(dmach, priv->desc); +	if (mxs_dma_go(dmach)) +		return COMM_ERR; + +	/* The data arrived into DRAM, invalidate cache over them */ +	if (data->flags & MMC_DATA_READ) { +		invalidate_dcache_range((uint32_t)priv->desc->cmd.address, +			(uint32_t)(priv->desc->cmd.address + cache_data_count)); +	} + +	return 0; +}  /*   * Sends a command out on the bus.  Takes the mmc pointer, @@ -70,16 +148,11 @@ static int  mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  {  	struct mxsmmc_priv *priv = (struct mxsmmc_priv *)mmc->priv; -	struct mx28_ssp_regs *ssp_regs = priv->regs; +	struct mxs_ssp_regs *ssp_regs = priv->regs;  	uint32_t reg;  	int timeout; -	uint32_t data_count;  	uint32_t ctrl0; -#ifndef CONFIG_MXS_MMC_DMA -	uint32_t *data_ptr; -#else -	uint32_t cache_data_count; -#endif +	int ret;  	debug("MMC%d: CMD%d\n", mmc->block_dev.dev, cmd->cmdidx); @@ -117,6 +190,11 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  	if (cmd->resp_type & MMC_RSP_136)	/* It's a 136 bits response */  		ctrl0 |= SSP_CTRL0_LONG_RESP; +	if (data && (data->blocksize * data->blocks < MXSMMC_SMALL_TRANSFER)) +		writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr); +	else +		writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set); +  	/* Command index */  	reg = readl(&ssp_regs->hw_ssp_cmd0);  	reg &= ~(SSP_CMD0_CMD_MASK | SSP_CMD0_APPEND_8CYC); @@ -197,75 +275,23 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  	if (!data)  		return 0; -	data_count = data->blocksize * data->blocks; -	timeout = MXSMMC_MAX_TIMEOUT; - -#ifdef CONFIG_MXS_MMC_DMA -	if (data_count % ARCH_DMA_MINALIGN) -		cache_data_count = roundup(data_count, ARCH_DMA_MINALIGN); -	else -		cache_data_count = data_count; - -	if (data->flags & MMC_DATA_READ) { -		priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE; -		priv->desc->cmd.address = (dma_addr_t)data->dest; -	} else { -		priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ; -		priv->desc->cmd.address = (dma_addr_t)data->src; - -		/* Flush data to DRAM so DMA can pick them up */ -		flush_dcache_range((uint32_t)priv->desc->cmd.address, -			(uint32_t)(priv->desc->cmd.address + cache_data_count)); -	} - -	priv->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM | -				(data_count << MXS_DMA_DESC_BYTES_OFFSET); - - -	mxs_dma_desc_append(MXS_DMA_CHANNEL_AHB_APBH_SSP0, priv->desc); -	if (mxs_dma_go(MXS_DMA_CHANNEL_AHB_APBH_SSP0)) { -		printf("MMC%d: DMA transfer failed\n", mmc->block_dev.dev); -		return COMM_ERR; -	} - -	/* The data arrived into DRAM, invalidate cache over them */ -	if (data->flags & MMC_DATA_READ) { -		invalidate_dcache_range((uint32_t)priv->desc->cmd.address, -			(uint32_t)(priv->desc->cmd.address + cache_data_count)); -	} -#else -	if (data->flags & MMC_DATA_READ) { -		data_ptr = (uint32_t *)data->dest; -		while (data_count && --timeout) { -			reg = readl(&ssp_regs->hw_ssp_status); -			if (!(reg & SSP_STATUS_FIFO_EMPTY)) { -				*data_ptr++ = readl(&ssp_regs->hw_ssp_data); -				data_count -= 4; -				timeout = MXSMMC_MAX_TIMEOUT; -			} else -				udelay(1000); +	if (data->blocksize * data->blocks < MXSMMC_SMALL_TRANSFER) { +		ret = mxsmmc_send_cmd_pio(priv, data); +		if (ret) { +			printf("MMC%d: Data timeout with command %d " +				"(status 0x%08x)!\n", +				mmc->block_dev.dev, cmd->cmdidx, reg); +			return ret;  		}  	} else { -		data_ptr = (uint32_t *)data->src; -		timeout *= 100; -		while (data_count && --timeout) { -			reg = readl(&ssp_regs->hw_ssp_status); -			if (!(reg & SSP_STATUS_FIFO_FULL)) { -				writel(*data_ptr++, &ssp_regs->hw_ssp_data); -				data_count -= 4; -				timeout = MXSMMC_MAX_TIMEOUT; -			} else -				udelay(1000); +		ret = mxsmmc_send_cmd_dma(priv, data); +		if (ret) { +			printf("MMC%d: DMA transfer failed\n", +				mmc->block_dev.dev); +			return ret;  		}  	} -	if (!timeout) { -		printf("MMC%d: Data timeout with command %d (status 0x%08x)!\n", -			mmc->block_dev.dev, cmd->cmdidx, reg); -		return COMM_ERR; -	} -#endif -  	/* Check data errors */  	reg = readl(&ssp_regs->hw_ssp_status);  	if (reg & @@ -282,7 +308,7 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)  static void mxsmmc_set_ios(struct mmc *mmc)  {  	struct mxsmmc_priv *priv = (struct mxsmmc_priv *)mmc->priv; -	struct mx28_ssp_regs *ssp_regs = priv->regs; +	struct mxs_ssp_regs *ssp_regs = priv->regs;  	/* Set the clock speed */  	if (mmc->clock) @@ -311,16 +337,16 @@ static void mxsmmc_set_ios(struct mmc *mmc)  static int mxsmmc_init(struct mmc *mmc)  {  	struct mxsmmc_priv *priv = (struct mxsmmc_priv *)mmc->priv; -	struct mx28_ssp_regs *ssp_regs = priv->regs; +	struct mxs_ssp_regs *ssp_regs = priv->regs;  	/* Reset SSP */ -	mx28_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); +	mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);  	/* 8 bits word length in MMC mode */  	clrsetbits_le32(&ssp_regs->hw_ssp_ctrl1, -		SSP_CTRL1_SSP_MODE_MASK | SSP_CTRL1_WORD_LENGTH_MASK, -		SSP_CTRL1_SSP_MODE_SD_MMC | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS | -		SSP_CTRL1_DMA_ENABLE); +		SSP_CTRL1_SSP_MODE_MASK | SSP_CTRL1_WORD_LENGTH_MASK | +		SSP_CTRL1_DMA_ENABLE, +		SSP_CTRL1_SSP_MODE_SD_MMC | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS);  	/* Set initial bit clock 400 KHz */  	mx28_set_ssp_busclock(priv->id, 400); @@ -335,8 +361,8 @@ static int mxsmmc_init(struct mmc *mmc)  int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))  { -	struct mx28_clkctrl_regs *clkctrl_regs = -		(struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE; +	struct mxs_clkctrl_regs *clkctrl_regs = +		(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;  	struct mmc *mmc = NULL;  	struct mxsmmc_priv *priv = NULL;  	int ret; @@ -366,22 +392,22 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))  	priv->id = id;  	switch (id) {  	case 0: -		priv->regs = (struct mx28_ssp_regs *)MXS_SSP0_BASE; +		priv->regs = (struct mxs_ssp_regs *)MXS_SSP0_BASE;  		priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP0;  		priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp0;  		break;  	case 1: -		priv->regs = (struct mx28_ssp_regs *)MXS_SSP1_BASE; +		priv->regs = (struct mxs_ssp_regs *)MXS_SSP1_BASE;  		priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP1;  		priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp1;  		break;  	case 2: -		priv->regs = (struct mx28_ssp_regs *)MXS_SSP2_BASE; +		priv->regs = (struct mxs_ssp_regs *)MXS_SSP2_BASE;  		priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP2;  		priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp2;  		break;  	case 3: -		priv->regs = (struct mx28_ssp_regs *)MXS_SSP3_BASE; +		priv->regs = (struct mxs_ssp_regs *)MXS_SSP3_BASE;  		priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP3;  		priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp3;  		break; diff --git a/drivers/mmc/pxa_mmc_gen.c b/drivers/mmc/pxa_mmc_gen.c index 2c5bf17bb..b3ec441b6 100644 --- a/drivers/mmc/pxa_mmc_gen.c +++ b/drivers/mmc/pxa_mmc_gen.c @@ -118,7 +118,7 @@ static int pxa_mmc_start_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  	int ret;  	/* The card can send a "busy" response */ -	if (cmd->flags & MMC_RSP_BUSY) +	if (cmd->resp_type & MMC_RSP_BUSY)  		cmdat |= MMC_CMDAT_BUSY;  	/* Inform the controller about response type */ @@ -181,9 +181,11 @@ static int pxa_mmc_cmd_done(struct mmc *mmc, struct mmc_cmd *cmd)  	/* The command response didn't arrive */  	if (stat & MMC_STAT_TIME_OUT_RESPONSE)  		return -ETIMEDOUT; -	else if (stat & MMC_STAT_RES_CRC_ERROR && cmd->flags & MMC_RSP_CRC) { +	else if (stat & MMC_STAT_RES_CRC_ERROR +			&& cmd->resp_type & MMC_RSP_CRC) {  #ifdef	PXAMMC_CRC_SKIP -		if (cmd->flags & MMC_RSP_136 && cmd->response[0] & (1 << 31)) +		if (cmd->resp_type & MMC_RSP_136 +				&& cmd->response[0] & (1 << 31))  			printf("Ignoring CRC, this may be dangerous!\n");  		else  #endif diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index 1d4481b97..b9782367e 100644 --- a/drivers/mmc/s5p_sdhci.c +++ b/drivers/mmc/s5p_sdhci.c @@ -21,6 +21,7 @@  #include <malloc.h>  #include <sdhci.h>  #include <asm/arch/mmc.h> +#include <asm/arch/clk.h>  static char *S5P_NAME = "SAMSUNG SDHCI";  static void s5p_sdhci_set_control_reg(struct sdhci_host *host) @@ -54,7 +55,7 @@ static void s5p_sdhci_set_control_reg(struct sdhci_host *host)  	 *	00 = Delay3 (inverter delay)  	 *	10 = Delay4 (inverter delay + 2ns)  	 */ -	val = SDHCI_CTRL3_FCSEL3 | SDHCI_CTRL3_FCSEL1; +	val = SDHCI_CTRL3_FCSEL0 | SDHCI_CTRL3_FCSEL1;  	sdhci_writel(host, val, SDHCI_CONTROL3);  	/* @@ -69,7 +70,7 @@ static void s5p_sdhci_set_control_reg(struct sdhci_host *host)  	sdhci_writel(host, ctrl, SDHCI_CONTROL2);  } -int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks) +int s5p_sdhci_init(u32 regbase, int index, int bus_width)  {  	struct sdhci_host *host = NULL;  	host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); @@ -80,19 +81,18 @@ int s5p_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks)  	host->name = S5P_NAME;  	host->ioaddr = (void *)regbase; -	host->quirks = quirks; -	host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE; +	host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE | +		SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR;  	host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; -	if (quirks & SDHCI_QUIRK_REG32_RW) -		host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; -	else -		host->version = sdhci_readw(host, SDHCI_HOST_VERSION); +	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);  	host->set_control_reg = &s5p_sdhci_set_control_reg; +	host->set_clock = set_mmc_clk; +	host->index = index;  	host->host_caps = MMC_MODE_HC; -	add_sdhci(host, max_clk, min_clk); +	add_sdhci(host, 52000000, 400000);  	return 0;  } diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 1709643da..2e3c408bc 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -260,7 +260,7 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)  	if (clock == 0)  		return 0; -	if (host->version >= SDHCI_SPEC_300) { +	if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) {  		/* Version 3.00 divisors must be a multiple of 2. */  		if (mmc->f_max <= clock)  			div = 1; @@ -279,6 +279,9 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)  	}  	div >>= 1; +	if (host->set_clock) +		host->set_clock(host->index, div); +  	clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;  	clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)  		<< SDHCI_DIVIDER_HI_SHIFT; @@ -347,10 +350,10 @@ void sdhci_set_ios(struct mmc *mmc)  	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);  	if (mmc->bus_width == 8) {  		ctrl &= ~SDHCI_CTRL_4BITBUS; -		if (host->version >= SDHCI_SPEC_300) +		if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)  			ctrl |= SDHCI_CTRL_8BITBUS;  	} else { -		if (host->version >= SDHCI_SPEC_300) +		if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)  			ctrl &= ~SDHCI_CTRL_8BITBUS;  		if (mmc->bus_width == 4)  			ctrl |= SDHCI_CTRL_4BITBUS; @@ -381,12 +384,25 @@ int sdhci_init(struct mmc *mmc)  		}  	} +	sdhci_set_power(host, fls(mmc->voltages) - 1); + +	if (host->quirks & SDHCI_QUIRK_NO_CD) { +		unsigned int status; + +		sdhci_writel(host, SDHCI_CTRL_CD_TEST_INS | SDHCI_CTRL_CD_TEST, +			SDHCI_HOST_CONTROL); + +		status = sdhci_readl(host, SDHCI_PRESENT_STATE); +		while ((!(status & SDHCI_CARD_PRESENT)) || +		    (!(status & SDHCI_CARD_STATE_STABLE)) || +		    (!(status & SDHCI_CARD_DETECT_PIN_LEVEL))) +			status = sdhci_readl(host, SDHCI_PRESENT_STATE); +	} +  	/* Eable all state */  	sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_ENABLE);  	sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_SIGNAL_ENABLE); -	sdhci_set_power(host, fls(mmc->voltages) - 1); -  	return 0;  } @@ -421,7 +437,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)  	if (max_clk)  		mmc->f_max = max_clk;  	else { -		if (host->version >= SDHCI_SPEC_300) +		if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)  			mmc->f_max = (caps & SDHCI_CLOCK_V3_BASE_MASK)  				>> SDHCI_CLOCK_BASE_SHIFT;  		else @@ -436,7 +452,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)  	if (min_clk)  		mmc->f_min = min_clk;  	else { -		if (host->version >= SDHCI_SPEC_300) +		if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300)  			mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_300;  		else  			mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_200; diff --git a/drivers/mmc/sh_mmcif.c b/drivers/mmc/sh_mmcif.c index 2835e242f..4588568a6 100644 --- a/drivers/mmc/sh_mmcif.c +++ b/drivers/mmc/sh_mmcif.c @@ -593,7 +593,7 @@ int mmcif_mmc_init(void)  	mmc->f_max = CLKDEV_EMMC_DATA;  	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;  	mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | -			 MMC_MODE_8BIT; +			 MMC_MODE_8BIT | MMC_MODE_HC;  	memcpy(mmc->name, DRIVER_NAME, sizeof(DRIVER_NAME));  	mmc->send_cmd = sh_mmcif_request;  	mmc->set_ios = sh_mmcif_set_ios; diff --git a/drivers/mmc/spl_mmc_load.c b/drivers/mmc/spl_mmc_load.c new file mode 100644 index 000000000..79a68fbcf --- /dev/null +++ b/drivers/mmc/spl_mmc_load.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <common.h> +#include <mmc.h> + +DECLARE_GLOBAL_DATA_PTR; + +static void mmc_load_image(struct mmc *mmc) +{ +	s32 err; +	void (*uboot)(void) __noreturn; + +	err = mmc->block_dev.block_read(0, CONFIG_SYS_MMC_U_BOOT_OFFS, +			CONFIG_SYS_MMC_U_BOOT_SIZE/512, +			(u32 *)CONFIG_SYS_TEXT_BASE); + +	if (err <= 0) { +		printf("spl: error reading image %s, err - %d\n", +			"u-boot.img", err); +		hang(); +	} +	uboot = (void *) CONFIG_SYS_TEXT_BASE; +	(*uboot)(); +} + +void spl_mmc_load(void) +{ +	struct mmc *mmc; +	int err; +	void (mmc_load_image)(struct mmc *mmc) __noreturn; + +	mmc_initialize(gd->bd); +	mmc = find_mmc_device(0); +	if (!mmc) { +		puts("spl: mmc device not found!!\n"); +		hang(); +	} else { +		puts("spl: mmc device found\n"); +	} +	err = mmc_init(mmc); +	if (err) { +		printf("spl: mmc init failed: err - %d\n", err); +		hang(); +	} +	mmc_load_image(mmc); +} diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index 29bf58359..ca8fad865 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -25,7 +25,7 @@  #include <asm/io.h>  #include <asm/arch/clk_rst.h>  #include <asm/arch/clock.h> -#include "tegra_mmc.h" +#include <asm/arch/tegra_mmc.h>  /* support 4 mmc hosts */  struct mmc mmc_dev[4]; @@ -39,31 +39,31 @@ struct mmc_host mmc_host[4];   * @param host		Structure to fill in (base, reg, mmc_id)   * @param dev_index	Device index (0-3)   */ -static void tegra2_get_setup(struct mmc_host *host, int dev_index) +static void tegra_get_setup(struct mmc_host *host, int dev_index)  { -	debug("tegra2_get_base_mmc: dev_index = %d\n", dev_index); +	debug("tegra_get_setup: dev_index = %d\n", dev_index);  	switch (dev_index) {  	case 1: -		host->base = TEGRA2_SDMMC3_BASE; +		host->base = TEGRA_SDMMC3_BASE;  		host->mmc_id = PERIPH_ID_SDMMC3;  		break;  	case 2: -		host->base = TEGRA2_SDMMC2_BASE; +		host->base = TEGRA_SDMMC2_BASE;  		host->mmc_id = PERIPH_ID_SDMMC2;  		break;  	case 3: -		host->base = TEGRA2_SDMMC1_BASE; +		host->base = TEGRA_SDMMC1_BASE;  		host->mmc_id = PERIPH_ID_SDMMC1;  		break;  	case 0:  	default: -		host->base = TEGRA2_SDMMC4_BASE; +		host->base = TEGRA_SDMMC4_BASE;  		host->mmc_id = PERIPH_ID_SDMMC4;  		break;  	} -	host->reg = (struct tegra2_mmc *)host->base; +	host->reg = (struct tegra_mmc *)host->base;  }  static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data) @@ -345,7 +345,7 @@ static void mmc_change_clock(struct mmc_host *host, uint clock)  	debug(" mmc_change_clock called\n");  	/* -	 * Change Tegra2 SDMMCx clock divisor here. Source is 216MHz, +	 * Change Tegra SDMMCx clock divisor here. Source is 216MHz,  	 * PLLP_OUT0  	 */  	if (clock == 0) @@ -494,11 +494,11 @@ static int mmc_core_init(struct mmc *mmc)  	return 0;  } -int tegra2_mmc_getcd(struct mmc *mmc) +int tegra_mmc_getcd(struct mmc *mmc)  {  	struct mmc_host *host = (struct mmc_host *)mmc->priv; -	debug("tegra2_mmc_getcd called\n"); +	debug("tegra_mmc_getcd called\n");  	if (host->cd_gpio >= 0)  		return !gpio_get_value(host->cd_gpio); @@ -506,13 +506,13 @@ int tegra2_mmc_getcd(struct mmc *mmc)  	return 1;  } -int tegra2_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio) +int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)  {  	struct mmc_host *host;  	char gpusage[12]; /* "SD/MMCn PWR" or "SD/MMCn CD" */  	struct mmc *mmc; -	debug(" tegra2_mmc_init: index %d, bus width %d " +	debug(" tegra_mmc_init: index %d, bus width %d "  		"pwr_gpio %d cd_gpio %d\n",  		dev_index, bus_width, pwr_gpio, cd_gpio); @@ -521,7 +521,7 @@ int tegra2_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)  	host->clock = 0;  	host->pwr_gpio = pwr_gpio;  	host->cd_gpio = cd_gpio; -	tegra2_get_setup(host, dev_index); +	tegra_get_setup(host, dev_index);  	clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); @@ -539,12 +539,12 @@ int tegra2_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)  	mmc = &mmc_dev[dev_index]; -	sprintf(mmc->name, "Tegra2 SD/MMC"); +	sprintf(mmc->name, "Tegra SD/MMC");  	mmc->priv = host;  	mmc->send_cmd = mmc_send_cmd;  	mmc->set_ios = mmc_set_ios;  	mmc->init = mmc_core_init; -	mmc->getcd = tegra2_mmc_getcd; +	mmc->getcd = tegra_mmc_getcd;  	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;  	if (bus_width == 8) @@ -559,7 +559,7 @@ int tegra2_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)  	 * max freq is highest HS eMMC clock as per the SD/MMC spec  	 *  (actually 52MHz)  	 * Both of these are the closest equivalents w/216MHz source -	 *  clock and Tegra2 SDMMC divisors. +	 *  clock and Tegra SDMMC divisors.  	 */  	mmc->f_min = 375000;  	mmc->f_max = 48000000; diff --git a/drivers/mmc/tegra_mmc.h b/drivers/mmc/tegra_mmc.h deleted file mode 100644 index f9cdcaaaa..000000000 --- a/drivers/mmc/tegra_mmc.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * (C) Copyright 2009 SAMSUNG Electronics - * Minkyu Kang <mk7.kang@samsung.com> - * Portions Copyright (C) 2011-2012 NVIDIA Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA - * - */ - -#ifndef __TEGRA_MMC_H_ -#define __TEGRA_MMC_H_ - -#define TEGRA2_SDMMC1_BASE	0xC8000000 -#define TEGRA2_SDMMC2_BASE	0xC8000200 -#define TEGRA2_SDMMC3_BASE	0xC8000400 -#define TEGRA2_SDMMC4_BASE	0xC8000600 - -#ifndef __ASSEMBLY__ -struct tegra2_mmc { -	unsigned int	sysad;		/* _SYSTEM_ADDRESS_0 */ -	unsigned short	blksize;	/* _BLOCK_SIZE_BLOCK_COUNT_0 15:00 */ -	unsigned short	blkcnt;		/* _BLOCK_SIZE_BLOCK_COUNT_0 31:16 */ -	unsigned int	argument;	/* _ARGUMENT_0 */ -	unsigned short	trnmod;		/* _CMD_XFER_MODE_0 15:00 xfer mode */ -	unsigned short	cmdreg;		/* _CMD_XFER_MODE_0 31:16 cmd reg */ -	unsigned int	rspreg0;	/* _RESPONSE_R0_R1_0 CMD RESP 31:00 */ -	unsigned int	rspreg1;	/* _RESPONSE_R2_R3_0 CMD RESP 63:32 */ -	unsigned int	rspreg2;	/* _RESPONSE_R4_R5_0 CMD RESP 95:64 */ -	unsigned int	rspreg3;	/* _RESPONSE_R6_R7_0 CMD RESP 127:96 */ -	unsigned int	bdata;		/* _BUFFER_DATA_PORT_0 */ -	unsigned int	prnsts;		/* _PRESENT_STATE_0 */ -	unsigned char	hostctl;	/* _POWER_CONTROL_HOST_0 7:00 */ -	unsigned char	pwrcon;		/* _POWER_CONTROL_HOST_0 15:8 */ -	unsigned char	blkgap;		/* _POWER_CONTROL_HOST_9 23:16 */ -	unsigned char	wakcon;		/* _POWER_CONTROL_HOST_0 31:24 */ -	unsigned short	clkcon;		/* _CLOCK_CONTROL_0 15:00 */ -	unsigned char	timeoutcon;	/* _TIMEOUT_CTRL 23:16 */ -	unsigned char	swrst;		/* _SW_RESET_ 31:24 */ -	unsigned int	norintsts;	/* _INTERRUPT_STATUS_0 */ -	unsigned int	norintstsen;	/* _INTERRUPT_STATUS_ENABLE_0 */ -	unsigned int	norintsigen;	/* _INTERRUPT_SIGNAL_ENABLE_0 */ -	unsigned short	acmd12errsts;	/* _AUTO_CMD12_ERR_STATUS_0 15:00 */ -	unsigned char	res1[2];	/* _RESERVED 31:16 */ -	unsigned int	capareg;	/* _CAPABILITIES_0 */ -	unsigned char	res2[4];	/* RESERVED, offset 44h-47h */ -	unsigned int	maxcurr;	/* _MAXIMUM_CURRENT_0 */ -	unsigned char	res3[4];	/* RESERVED, offset 4Ch-4Fh */ -	unsigned short	setacmd12err;	/* offset 50h */ -	unsigned short	setinterr;	/* offset 52h */ -	unsigned char	admaerr;	/* offset 54h */ -	unsigned char	res4[3];	/* RESERVED, offset 55h-57h */ -	unsigned long	admaaddr;	/* offset 58h-5Fh */ -	unsigned char	res5[0x9c];	/* RESERVED, offset 60h-FBh */ -	unsigned short	slotintstatus;	/* offset FCh */ -	unsigned short	hcver;		/* HOST Version */ -	unsigned char	res6[0x100];	/* RESERVED, offset 100h-1FFh */ -}; - -#define TEGRA_MMC_HOSTCTL_DMASEL_MASK				(3 << 3) -#define TEGRA_MMC_HOSTCTL_DMASEL_SDMA				(0 << 3) -#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_32BIT			(2 << 3) -#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_64BIT			(3 << 3) - -#define TEGRA_MMC_TRNMOD_DMA_ENABLE				(1 << 0) -#define TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE			(1 << 1) -#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_WRITE		(0 << 4) -#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ			(1 << 4) -#define TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT			(1 << 5) - -#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_MASK			(3 << 0) -#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE		(0 << 0) -#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136		(1 << 0) -#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48		(2 << 0) -#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY	(3 << 0) - -#define TEGRA_MMC_TRNMOD_CMD_CRC_CHECK				(1 << 3) -#define TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK			(1 << 4) -#define TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER	(1 << 5) - -#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD			(1 << 0) -#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT			(1 << 1) - -#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE			(1 << 0) -#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE			(1 << 1) -#define TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE			(1 << 2) - -#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT			8 -#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_MASK			(0xff << 8) - -#define TEGRA_MMC_SWRST_SW_RESET_FOR_ALL			(1 << 0) -#define TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE			(1 << 1) -#define TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE			(1 << 2) - -#define TEGRA_MMC_NORINTSTS_CMD_COMPLETE			(1 << 0) -#define TEGRA_MMC_NORINTSTS_XFER_COMPLETE			(1 << 1) -#define TEGRA_MMC_NORINTSTS_DMA_INTERRUPT			(1 << 3) -#define TEGRA_MMC_NORINTSTS_ERR_INTERRUPT			(1 << 15) -#define TEGRA_MMC_NORINTSTS_CMD_TIMEOUT				(1 << 16) - -#define TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE			(1 << 0) -#define TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE			(1 << 1) -#define TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT			(1 << 3) -#define TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY		(1 << 4) -#define TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY			(1 << 5) - -#define TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE			(1 << 1) - -struct mmc_host { -	struct tegra2_mmc *reg; -	unsigned int version;	/* SDHCI spec. version */ -	unsigned int clock;	/* Current clock (MHz) */ -	unsigned int base;	/* Base address, SDMMC1/2/3/4 */ -	enum periph_id mmc_id;	/* Peripheral ID: PERIPH_ID_... */ -	int pwr_gpio;		/* Power GPIO */ -	int cd_gpio;		/* Change Detect GPIO */ -}; - -#endif	/* __ASSEMBLY__ */ -#endif	/* __TEGRA_MMC_H_ */ diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index f0f301a46..43140f364 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -1077,7 +1077,38 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)  	for (sect = s_first; sect <= s_last; sect++) { +		if (ctrlc()) { +			printf("\n"); +			return 1; +		} +  		if (info->protect[sect] == 0) { /* not protected */ +#ifdef CONFIG_SYS_FLASH_CHECK_BLANK_BEFORE_ERASE +			int k; +			int size; +			int erased; +			u32 *flash; + +			/* +			 * Check if whole sector is erased +			 */ +			size = flash_sector_size(info, sect); +			erased = 1; +			flash = (u32 *)info->start[sect]; +			/* divide by 4 for longword access */ +			size = size >> 2; +			for (k = 0; k < size; k++) { +				if (flash_read32(flash++) != 0xffffffff) { +					erased = 0; +					break; +				} +			} +			if (erased) { +				if (flash_verbose) +					putc(','); +				continue; +			} +#endif  			switch (info->vendor) {  			case CFI_CMDSET_INTEL_PROG_REGIONS:  			case CFI_CMDSET_INTEL_STANDARD: @@ -1353,6 +1384,9 @@ int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)  		src += i;  		cnt -= i;  		FLASH_SHOW_PROGRESS(scale, dots, digit, i); +		/* Only check every once in a while */ +		if ((cnt & 0xFFFF) < buffered_size && ctrlc()) +			return ERR_ABORTED;  	}  #else  	while (cnt >= info->portwidth) { @@ -1365,6 +1399,9 @@ int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)  		wp += info->portwidth;  		cnt -= info->portwidth;  		FLASH_SHOW_PROGRESS(scale, dots, digit, info->portwidth); +		/* Only check every once in a while */ +		if ((cnt & 0xFFFF) < info->portwidth && ctrlc()) +			return ERR_ABORTED;  	}  #endif /* CONFIG_SYS_FLASH_USE_BUFFER_WRITE */ diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 29dc20ef5..beb99cacb 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -62,6 +62,7 @@ COBJS-$(CONFIG_NAND_NOMADIK) += nomadik.o  COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o  COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o  COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.o +COBJS-$(CONFIG_TEGRA_NAND) += tegra_nand.o  COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o  COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o  endif diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index de663824f..c6aa5db33 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -5,6 +5,9 @@   *   * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas   * + * Add Programmable Multibit ECC support for various AT91 SoC + *     (C) Copyright 2012 ATMEL, Hong Xu + *   * See file CREDITS for list of people who contributed to this   * project.   * @@ -30,6 +33,7 @@  #include <asm/arch/at91_pio.h>  #include <nand.h> +#include <watchdog.h>  #ifdef CONFIG_ATMEL_NAND_HWECC @@ -41,6 +45,674 @@  #include "atmel_nand_ecc.h"	/* Hardware ECC registers */ +#ifdef CONFIG_ATMEL_NAND_HW_PMECC + +struct atmel_nand_host { +	struct pmecc_regs __iomem *pmecc; +	struct pmecc_errloc_regs __iomem *pmerrloc; +	void __iomem		*pmecc_rom_base; + +	u8		pmecc_corr_cap; +	u16		pmecc_sector_size; +	u32		pmecc_index_table_offset; + +	int		pmecc_bytes_per_sector; +	int		pmecc_sector_number; +	int		pmecc_degree;	/* Degree of remainders */ +	int		pmecc_cw_len;	/* Length of codeword */ + +	/* lookup table for alpha_to and index_of */ +	void __iomem	*pmecc_alpha_to; +	void __iomem	*pmecc_index_of; + +	/* data for pmecc computation */ +	int16_t	pmecc_smu[(CONFIG_PMECC_CAP + 2) * (2 * CONFIG_PMECC_CAP + 1)]; +	int16_t	pmecc_partial_syn[2 * CONFIG_PMECC_CAP + 1]; +	int16_t	pmecc_si[2 * CONFIG_PMECC_CAP + 1]; +	int16_t	pmecc_lmu[CONFIG_PMECC_CAP + 1]; /* polynomal order */ +	int	pmecc_mu[CONFIG_PMECC_CAP + 1]; +	int	pmecc_dmu[CONFIG_PMECC_CAP + 1]; +	int	pmecc_delta[CONFIG_PMECC_CAP + 1]; +}; + +static struct atmel_nand_host pmecc_host; +static struct nand_ecclayout atmel_pmecc_oobinfo; + +/* + * Return number of ecc bytes per sector according to sector size and + * correction capability + * + * Following table shows what at91 PMECC supported: + * Correction Capability	Sector_512_bytes	Sector_1024_bytes + * =====================	================	================= + *                2-bits                 4-bytes                  4-bytes + *                4-bits                 7-bytes                  7-bytes + *                8-bits                13-bytes                 14-bytes + *               12-bits                20-bytes                 21-bytes + *               24-bits                39-bytes                 42-bytes + */ +static int pmecc_get_ecc_bytes(int cap, int sector_size) +{ +	int m = 12 + sector_size / 512; +	return (m * cap + 7) / 8; +} + +static void pmecc_config_ecc_layout(struct nand_ecclayout *layout, +	int oobsize, int ecc_len) +{ +	int i; + +	layout->eccbytes = ecc_len; + +	/* ECC will occupy the last ecc_len bytes continuously */ +	for (i = 0; i < ecc_len; i++) +		layout->eccpos[i] = oobsize - ecc_len + i; + +	layout->oobfree[0].offset = 2; +	layout->oobfree[0].length = +		oobsize - ecc_len - layout->oobfree[0].offset; +} + +static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host) +{ +	int table_size; + +	table_size = host->pmecc_sector_size == 512 ? +		PMECC_INDEX_TABLE_SIZE_512 : PMECC_INDEX_TABLE_SIZE_1024; + +	/* the ALPHA lookup table is right behind the INDEX lookup table. */ +	return host->pmecc_rom_base + host->pmecc_index_table_offset + +			table_size * sizeof(int16_t); +} + +static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; +	int i; +	uint32_t value; + +	/* Fill odd syndromes */ +	for (i = 0; i < host->pmecc_corr_cap; i++) { +		value = readl(&host->pmecc->rem_port[sector].rem[i / 2]); +		if (i & 1) +			value >>= 16; +		value &= 0xffff; +		host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value; +	} +} + +static void pmecc_substitute(struct mtd_info *mtd) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; +	int16_t __iomem *alpha_to = host->pmecc_alpha_to; +	int16_t __iomem *index_of = host->pmecc_index_of; +	int16_t *partial_syn = host->pmecc_partial_syn; +	const int cap = host->pmecc_corr_cap; +	int16_t *si; +	int i, j; + +	/* si[] is a table that holds the current syndrome value, +	 * an element of that table belongs to the field +	 */ +	si = host->pmecc_si; + +	memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1)); + +	/* Computation 2t syndromes based on S(x) */ +	/* Odd syndromes */ +	for (i = 1; i < 2 * cap; i += 2) { +		for (j = 0; j < host->pmecc_degree; j++) { +			if (partial_syn[i] & (0x1 << j)) +				si[i] = readw(alpha_to + i * j) ^ si[i]; +		} +	} +	/* Even syndrome = (Odd syndrome) ** 2 */ +	for (i = 2, j = 1; j <= cap; i = ++j << 1) { +		if (si[j] == 0) { +			si[i] = 0; +		} else { +			int16_t tmp; + +			tmp = readw(index_of + si[j]); +			tmp = (tmp * 2) % host->pmecc_cw_len; +			si[i] = readw(alpha_to + tmp); +		} +	} +} + +/* + * This function defines a Berlekamp iterative procedure for + * finding the value of the error location polynomial. + * The input is si[], initialize by pmecc_substitute(). + * The output is smu[][]. + * + * This function is written according to chip datasheet Chapter: + * Find the Error Location Polynomial Sigma(x) of Section: + * Programmable Multibit ECC Control (PMECC). + */ +static void pmecc_get_sigma(struct mtd_info *mtd) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; + +	int16_t *lmu = host->pmecc_lmu; +	int16_t *si = host->pmecc_si; +	int *mu = host->pmecc_mu; +	int *dmu = host->pmecc_dmu;	/* Discrepancy */ +	int *delta = host->pmecc_delta; /* Delta order */ +	int cw_len = host->pmecc_cw_len; +	const int16_t cap = host->pmecc_corr_cap; +	const int num = 2 * cap + 1; +	int16_t __iomem	*index_of = host->pmecc_index_of; +	int16_t __iomem	*alpha_to = host->pmecc_alpha_to; +	int i, j, k; +	uint32_t dmu_0_count, tmp; +	int16_t *smu = host->pmecc_smu; + +	/* index of largest delta */ +	int ro; +	int largest; +	int diff; + +	/* Init the Sigma(x) */ +	memset(smu, 0, sizeof(int16_t) * ARRAY_SIZE(smu)); + +	dmu_0_count = 0; + +	/* First Row */ + +	/* Mu */ +	mu[0] = -1; + +	smu[0] = 1; + +	/* discrepancy set to 1 */ +	dmu[0] = 1; +	/* polynom order set to 0 */ +	lmu[0] = 0; +	/* delta[0] = (mu[0] * 2 - lmu[0]) >> 1; */ +	delta[0] = -1; + +	/* Second Row */ + +	/* Mu */ +	mu[1] = 0; +	/* Sigma(x) set to 1 */ +	smu[num] = 1; + +	/* discrepancy set to S1 */ +	dmu[1] = si[1]; + +	/* polynom order set to 0 */ +	lmu[1] = 0; + +	/* delta[1] = (mu[1] * 2 - lmu[1]) >> 1; */ +	delta[1] = 0; + +	for (i = 1; i <= cap; i++) { +		mu[i + 1] = i << 1; +		/* Begin Computing Sigma (Mu+1) and L(mu) */ +		/* check if discrepancy is set to 0 */ +		if (dmu[i] == 0) { +			dmu_0_count++; + +			tmp = ((cap - (lmu[i] >> 1) - 1) / 2); +			if ((cap - (lmu[i] >> 1) - 1) & 0x1) +				tmp += 2; +			else +				tmp += 1; + +			if (dmu_0_count == tmp) { +				for (j = 0; j <= (lmu[i] >> 1) + 1; j++) +					smu[(cap + 1) * num + j] = +							smu[i * num + j]; + +				lmu[cap + 1] = lmu[i]; +				return; +			} + +			/* copy polynom */ +			for (j = 0; j <= lmu[i] >> 1; j++) +				smu[(i + 1) * num + j] = smu[i * num + j]; + +			/* copy previous polynom order to the next */ +			lmu[i + 1] = lmu[i]; +		} else { +			ro = 0; +			largest = -1; +			/* find largest delta with dmu != 0 */ +			for (j = 0; j < i; j++) { +				if ((dmu[j]) && (delta[j] > largest)) { +					largest = delta[j]; +					ro = j; +				} +			} + +			/* compute difference */ +			diff = (mu[i] - mu[ro]); + +			/* Compute degree of the new smu polynomial */ +			if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff)) +				lmu[i + 1] = lmu[i]; +			else +				lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2; + +			/* Init smu[i+1] with 0 */ +			for (k = 0; k < num; k++) +				smu[(i + 1) * num + k] = 0; + +			/* Compute smu[i+1] */ +			for (k = 0; k <= lmu[ro] >> 1; k++) { +				int16_t a, b, c; + +				if (!(smu[ro * num + k] && dmu[i])) +					continue; +				a = readw(index_of + dmu[i]); +				b = readw(index_of + dmu[ro]); +				c = readw(index_of + smu[ro * num + k]); +				tmp = a + (cw_len - b) + c; +				a = readw(alpha_to + tmp % cw_len); +				smu[(i + 1) * num + (k + diff)] = a; +			} + +			for (k = 0; k <= lmu[i] >> 1; k++) +				smu[(i + 1) * num + k] ^= smu[i * num + k]; +		} + +		/* End Computing Sigma (Mu+1) and L(mu) */ +		/* In either case compute delta */ +		delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1; + +		/* Do not compute discrepancy for the last iteration */ +		if (i >= cap) +			continue; + +		for (k = 0; k <= (lmu[i + 1] >> 1); k++) { +			tmp = 2 * (i - 1); +			if (k == 0) { +				dmu[i + 1] = si[tmp + 3]; +			} else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) { +				int16_t a, b, c; +				a = readw(index_of + +						smu[(i + 1) * num + k]); +				b = si[2 * (i - 1) + 3 - k]; +				c = readw(index_of + b); +				tmp = a + c; +				tmp %= cw_len; +				dmu[i + 1] = readw(alpha_to + tmp) ^ +					dmu[i + 1]; +			} +		} +	} +} + +static int pmecc_err_location(struct mtd_info *mtd) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; +	const int cap = host->pmecc_corr_cap; +	const int num = 2 * cap + 1; +	int sector_size = host->pmecc_sector_size; +	int err_nbr = 0;	/* number of error */ +	int roots_nbr;		/* number of roots */ +	int i; +	uint32_t val; +	int16_t *smu = host->pmecc_smu; +	int timeout = PMECC_MAX_TIMEOUT_US; + +	writel(PMERRLOC_DISABLE, &host->pmerrloc->eldis); + +	for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) { +		writel(smu[(cap + 1) * num + i], &host->pmerrloc->sigma[i]); +		err_nbr++; +	} + +	val = PMERRLOC_ELCFG_NUM_ERRORS(err_nbr - 1); +	if (sector_size == 1024) +		val |= PMERRLOC_ELCFG_SECTOR_1024; + +	writel(val, &host->pmerrloc->elcfg); +	writel(sector_size * 8 + host->pmecc_degree * cap, +			&host->pmerrloc->elen); + +	while (--timeout) { +		if (readl(&host->pmerrloc->elisr) & PMERRLOC_CALC_DONE) +			break; +		WATCHDOG_RESET(); +		udelay(1); +	} + +	if (!timeout) { +		printk(KERN_ERR "atmel_nand : Timeout to calculate PMECC error location\n"); +		return -1; +	} + +	roots_nbr = (readl(&host->pmerrloc->elisr) & PMERRLOC_ERR_NUM_MASK) +			>> 8; +	/* Number of roots == degree of smu hence <= cap */ +	if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1) +		return err_nbr - 1; + +	/* Number of roots does not match the degree of smu +	 * unable to correct error */ +	return -1; +} + +static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc, +		int sector_num, int extra_bytes, int err_nbr) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; +	int i = 0; +	int byte_pos, bit_pos, sector_size, pos; +	uint32_t tmp; +	uint8_t err_byte; + +	sector_size = host->pmecc_sector_size; + +	while (err_nbr) { +		tmp = readl(&host->pmerrloc->el[i]) - 1; +		byte_pos = tmp / 8; +		bit_pos  = tmp % 8; + +		if (byte_pos >= (sector_size + extra_bytes)) +			BUG();	/* should never happen */ + +		if (byte_pos < sector_size) { +			err_byte = *(buf + byte_pos); +			*(buf + byte_pos) ^= (1 << bit_pos); + +			pos = sector_num * host->pmecc_sector_size + byte_pos; +			printk(KERN_INFO "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", +				pos, bit_pos, err_byte, *(buf + byte_pos)); +		} else { +			/* Bit flip in OOB area */ +			tmp = sector_num * host->pmecc_bytes_per_sector +					+ (byte_pos - sector_size); +			err_byte = ecc[tmp]; +			ecc[tmp] ^= (1 << bit_pos); + +			pos = tmp + nand_chip->ecc.layout->eccpos[0]; +			printk(KERN_INFO "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", +				pos, bit_pos, err_byte, ecc[tmp]); +		} + +		i++; +		err_nbr--; +	} + +	return; +} + +static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf, +	u8 *ecc) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; +	int i, err_nbr, eccbytes; +	uint8_t *buf_pos; + +	eccbytes = nand_chip->ecc.bytes; +	for (i = 0; i < eccbytes; i++) +		if (ecc[i] != 0xff) +			goto normal_check; +	/* Erased page, return OK */ +	return 0; + +normal_check: +	for (i = 0; i < host->pmecc_sector_number; i++) { +		err_nbr = 0; +		if (pmecc_stat & 0x1) { +			buf_pos = buf + i * host->pmecc_sector_size; + +			pmecc_gen_syndrome(mtd, i); +			pmecc_substitute(mtd); +			pmecc_get_sigma(mtd); + +			err_nbr = pmecc_err_location(mtd); +			if (err_nbr == -1) { +				printk(KERN_ERR "PMECC: Too many errors\n"); +				mtd->ecc_stats.failed++; +				return -EIO; +			} else { +				pmecc_correct_data(mtd, buf_pos, ecc, i, +					host->pmecc_bytes_per_sector, err_nbr); +				mtd->ecc_stats.corrected += err_nbr; +			} +		} +		pmecc_stat >>= 1; +	} + +	return 0; +} + +static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, +	struct nand_chip *chip, uint8_t *buf, int page) +{ +	struct atmel_nand_host *host = chip->priv; +	int eccsize = chip->ecc.size; +	uint8_t *oob = chip->oob_poi; +	uint32_t *eccpos = chip->ecc.layout->eccpos; +	uint32_t stat; +	int timeout = PMECC_MAX_TIMEOUT_US; + +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); +	pmecc_writel(host->pmecc, cfg, ((pmecc_readl(host->pmecc, cfg)) +		& ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE); + +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA); + +	chip->read_buf(mtd, buf, eccsize); +	chip->read_buf(mtd, oob, mtd->oobsize); + +	while (--timeout) { +		if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY)) +			break; +		WATCHDOG_RESET(); +		udelay(1); +	} + +	if (!timeout) { +		printk(KERN_ERR "atmel_nand : Timeout to read PMECC page\n"); +		return -1; +	} + +	stat = pmecc_readl(host->pmecc, isr); +	if (stat != 0) +		if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0) +			return -EIO; + +	return 0; +} + +static void atmel_nand_pmecc_write_page(struct mtd_info *mtd, +		struct nand_chip *chip, const uint8_t *buf) +{ +	struct atmel_nand_host *host = chip->priv; +	uint32_t *eccpos = chip->ecc.layout->eccpos; +	int i, j; +	int timeout = PMECC_MAX_TIMEOUT_US; + +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); + +	pmecc_writel(host->pmecc, cfg, (pmecc_readl(host->pmecc, cfg) | +		PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE); + +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA); + +	chip->write_buf(mtd, (u8 *)buf, mtd->writesize); + +	while (--timeout) { +		if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY)) +			break; +		WATCHDOG_RESET(); +		udelay(1); +	} + +	if (!timeout) { +		printk(KERN_ERR "atmel_nand : Timeout to read PMECC status, fail to write PMECC in oob\n"); +		return; +	} + +	for (i = 0; i < host->pmecc_sector_number; i++) { +		for (j = 0; j < host->pmecc_bytes_per_sector; j++) { +			int pos; + +			pos = i * host->pmecc_bytes_per_sector + j; +			chip->oob_poi[eccpos[pos]] = +				readb(&host->pmecc->ecc_port[i].ecc[j]); +		} +	} +	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +} + +static void atmel_pmecc_core_init(struct mtd_info *mtd) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; +	uint32_t val = 0; +	struct nand_ecclayout *ecc_layout; + +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); + +	switch (host->pmecc_corr_cap) { +	case 2: +		val = PMECC_CFG_BCH_ERR2; +		break; +	case 4: +		val = PMECC_CFG_BCH_ERR4; +		break; +	case 8: +		val = PMECC_CFG_BCH_ERR8; +		break; +	case 12: +		val = PMECC_CFG_BCH_ERR12; +		break; +	case 24: +		val = PMECC_CFG_BCH_ERR24; +		break; +	} + +	if (host->pmecc_sector_size == 512) +		val |= PMECC_CFG_SECTOR512; +	else if (host->pmecc_sector_size == 1024) +		val |= PMECC_CFG_SECTOR1024; + +	switch (host->pmecc_sector_number) { +	case 1: +		val |= PMECC_CFG_PAGE_1SECTOR; +		break; +	case 2: +		val |= PMECC_CFG_PAGE_2SECTORS; +		break; +	case 4: +		val |= PMECC_CFG_PAGE_4SECTORS; +		break; +	case 8: +		val |= PMECC_CFG_PAGE_8SECTORS; +		break; +	} + +	val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE +		| PMECC_CFG_AUTO_DISABLE); +	pmecc_writel(host->pmecc, cfg, val); + +	ecc_layout = nand_chip->ecc.layout; +	pmecc_writel(host->pmecc, sarea, mtd->oobsize - 1); +	pmecc_writel(host->pmecc, saddr, ecc_layout->eccpos[0]); +	pmecc_writel(host->pmecc, eaddr, +			ecc_layout->eccpos[ecc_layout->eccbytes - 1]); +	/* See datasheet about PMECC Clock Control Register */ +	pmecc_writel(host->pmecc, clk, PMECC_CLK_133MHZ); +	pmecc_writel(host->pmecc, idr, 0xff); +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); +} + +static int atmel_pmecc_nand_init_params(struct nand_chip *nand, +		struct mtd_info *mtd) +{ +	struct atmel_nand_host *host; +	int cap, sector_size; + +	host = nand->priv = &pmecc_host; + +	nand->ecc.mode = NAND_ECC_HW; +	nand->ecc.calculate = NULL; +	nand->ecc.correct = NULL; +	nand->ecc.hwctl = NULL; + +	cap = host->pmecc_corr_cap = CONFIG_PMECC_CAP; +	sector_size = host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE; +	host->pmecc_index_table_offset = CONFIG_PMECC_INDEX_TABLE_OFFSET; + +	printk(KERN_INFO "Initialize PMECC params, cap: %d, sector: %d\n", +		 cap, sector_size); + +	host->pmecc = (struct pmecc_regs __iomem *) ATMEL_BASE_PMECC; +	host->pmerrloc = (struct pmecc_errloc_regs __iomem *) +			ATMEL_BASE_PMERRLOC; +	host->pmecc_rom_base = (void __iomem *) ATMEL_BASE_ROM; + +	/* ECC is calculated for the whole page (1 step) */ +	nand->ecc.size = mtd->writesize; + +	/* set ECC page size and oob layout */ +	switch (mtd->writesize) { +	case 2048: +	case 4096: +		host->pmecc_degree = PMECC_GF_DIMENSION_13; +		host->pmecc_cw_len = (1 << host->pmecc_degree) - 1; +		host->pmecc_sector_number = mtd->writesize / sector_size; +		host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes( +			cap, sector_size); +		host->pmecc_alpha_to = pmecc_get_alpha_to(host); +		host->pmecc_index_of = host->pmecc_rom_base + +			host->pmecc_index_table_offset; + +		nand->ecc.steps = 1; +		nand->ecc.bytes = host->pmecc_bytes_per_sector * +				       host->pmecc_sector_number; +		if (nand->ecc.bytes > mtd->oobsize - 2) { +			printk(KERN_ERR "No room for ECC bytes\n"); +			return -EINVAL; +		} +		pmecc_config_ecc_layout(&atmel_pmecc_oobinfo, +					mtd->oobsize, +					nand->ecc.bytes); +		nand->ecc.layout = &atmel_pmecc_oobinfo; +		break; +	case 512: +	case 1024: +		/* TODO */ +		printk(KERN_ERR "Unsupported page size for PMECC, use Software ECC\n"); +	default: +		/* page size not handled by HW ECC */ +		/* switching back to soft ECC */ +		nand->ecc.mode = NAND_ECC_SOFT; +		nand->ecc.read_page = NULL; +		nand->ecc.postpad = 0; +		nand->ecc.prepad = 0; +		nand->ecc.bytes = 0; +		return 0; +	} + +	nand->ecc.read_page = atmel_nand_pmecc_read_page; +	nand->ecc.write_page = atmel_nand_pmecc_write_page; + +	atmel_pmecc_core_init(mtd); + +	return 0; +} + +#else +  /* oob layout for large page size   * bad block info is on bytes 0 and 1   * the bytes have to be consecutives to avoid @@ -79,7 +751,6 @@ static struct nand_ecclayout atmel_oobinfo_small = {  static int atmel_nand_calculate(struct mtd_info *mtd,  		const u_char *dat, unsigned char *ecc_code)  { -	struct nand_chip *nand_chip = mtd->priv;  	unsigned int ecc_value;  	/* get the first 2 ECC bytes */ @@ -167,7 +838,7 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,  		u_char *read_ecc, u_char *isnull)  {  	struct nand_chip *nand_chip = mtd->priv; -	unsigned int ecc_status, ecc_parity, ecc_mode; +	unsigned int ecc_status;  	unsigned int ecc_word, ecc_bit;  	/* get the status from the Status Register */ @@ -232,7 +903,63 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,  static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)  {  } -#endif + +int atmel_hwecc_nand_init_param(struct nand_chip *nand, struct mtd_info *mtd) +{ +	nand->ecc.mode = NAND_ECC_HW; +	nand->ecc.calculate = atmel_nand_calculate; +	nand->ecc.correct = atmel_nand_correct; +	nand->ecc.hwctl = atmel_nand_hwctl; +	nand->ecc.read_page = atmel_nand_read_page; +	nand->ecc.bytes = 4; + +	if (nand->ecc.mode == NAND_ECC_HW) { +		/* ECC is calculated for the whole page (1 step) */ +		nand->ecc.size = mtd->writesize; + +		/* set ECC page size and oob layout */ +		switch (mtd->writesize) { +		case 512: +			nand->ecc.layout = &atmel_oobinfo_small; +			ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, +					ATMEL_ECC_PAGESIZE_528); +			break; +		case 1024: +			nand->ecc.layout = &atmel_oobinfo_large; +			ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, +					ATMEL_ECC_PAGESIZE_1056); +			break; +		case 2048: +			nand->ecc.layout = &atmel_oobinfo_large; +			ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, +					ATMEL_ECC_PAGESIZE_2112); +			break; +		case 4096: +			nand->ecc.layout = &atmel_oobinfo_large; +			ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, +					ATMEL_ECC_PAGESIZE_4224); +			break; +		default: +			/* page size not handled by HW ECC */ +			/* switching back to soft ECC */ +			nand->ecc.mode = NAND_ECC_SOFT; +			nand->ecc.calculate = NULL; +			nand->ecc.correct = NULL; +			nand->ecc.hwctl = NULL; +			nand->ecc.read_page = NULL; +			nand->ecc.postpad = 0; +			nand->ecc.prepad = 0; +			nand->ecc.bytes = 0; +			break; +		} +	} + +	return 0; +} + +#endif /* CONFIG_ATMEL_NAND_HW_PMECC */ + +#endif /* CONFIG_ATMEL_NAND_HWECC */  static void at91_nand_hwcontrol(struct mtd_info *mtd,  					 int cmd, unsigned int ctrl) @@ -267,12 +994,20 @@ static int at91_nand_ready(struct mtd_info *mtd)  }  #endif -int board_nand_init(struct nand_chip *nand) -{ -#ifdef CONFIG_ATMEL_NAND_HWECC -	static int chip_nr = 0; -	struct mtd_info *mtd; +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }  #endif +static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; +static ulong base_addr[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST; + +int atmel_nand_chip_init(int devnum, ulong base_addr) +{ +	int ret; +	struct mtd_info *mtd = &nand_info[devnum]; +	struct nand_chip *nand = &nand_chip[devnum]; + +	mtd->priv = nand; +	nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;  	nand->ecc.mode = NAND_ECC_SOFT;  #ifdef CONFIG_SYS_NAND_DBW_16 @@ -284,62 +1019,32 @@ int board_nand_init(struct nand_chip *nand)  #endif  	nand->chip_delay = 20; -#ifdef CONFIG_ATMEL_NAND_HWECC -	nand->ecc.mode = NAND_ECC_HW; -	nand->ecc.calculate = atmel_nand_calculate; -	nand->ecc.correct = atmel_nand_correct; -	nand->ecc.hwctl = atmel_nand_hwctl; -	nand->ecc.read_page = atmel_nand_read_page; -	nand->ecc.bytes = 4; -#endif +	ret = nand_scan_ident(mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL); +	if (ret) +		return ret;  #ifdef CONFIG_ATMEL_NAND_HWECC -	mtd = &nand_info[chip_nr++]; -	mtd->priv = nand; - -	/* Detect NAND chips */ -	if (nand_scan_ident(mtd, 1, NULL)) { -		printk(KERN_WARNING "NAND Flash not found !\n"); -		return -ENXIO; -	} +#ifdef CONFIG_ATMEL_NAND_HW_PMECC +	ret = atmel_pmecc_nand_init_params(nand, mtd); +#else +	ret = atmel_hwecc_nand_init_param(nand, mtd); +#endif +	if (ret) +		return ret; +#endif -	if (nand->ecc.mode == NAND_ECC_HW) { -		/* ECC is calculated for the whole page (1 step) */ -		nand->ecc.size = mtd->writesize; +	ret = nand_scan_tail(mtd); +	if (!ret) +		nand_register(devnum); -		/* set ECC page size and oob layout */ -		switch (mtd->writesize) { -		case 512: -			nand->ecc.layout = &atmel_oobinfo_small; -			ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_528); -			break; -		case 1024: -			nand->ecc.layout = &atmel_oobinfo_large; -			ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_1056); -			break; -		case 2048: -			nand->ecc.layout = &atmel_oobinfo_large; -			ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_2112); -			break; -		case 4096: -			nand->ecc.layout = &atmel_oobinfo_large; -			ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_4224); -			break; -		default: -			/* page size not handled by HW ECC */ -			/* switching back to soft ECC */ -			nand->ecc.mode = NAND_ECC_SOFT; -			nand->ecc.calculate = NULL; -			nand->ecc.correct = NULL; -			nand->ecc.hwctl = NULL; -			nand->ecc.read_page = NULL; -			nand->ecc.postpad = 0; -			nand->ecc.prepad = 0; -			nand->ecc.bytes = 0; -			break; -		} -	} -#endif +	return ret; +} -	return 0; +void board_nand_init(void) +{ +	int i; +	for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) +		if (atmel_nand_chip_init(i, base_addr[i])) +			printk(KERN_ERR "atmel_nand: Fail to initialize #%d chip", +				i);  } diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h index 1ee7f993d..4732582e7 100644 --- a/drivers/mtd/nand/atmel_nand_ecc.h +++ b/drivers/mtd/nand/atmel_nand_ecc.h @@ -33,4 +33,117 @@  #define ATMEL_ECC_NPR		0x10			/* NParity register */  #define		ATMEL_ECC_NPARITY	(0xffff << 0)		/* NParity */ +/* Register access macros for PMECC */ +#define pmecc_readl(addr, reg) \ +	readl(&addr->reg) + +#define pmecc_writel(addr, reg, value) \ +	writel((value), &addr->reg) + +/* PMECC Register Definitions */ +#define PMECC_MAX_SECTOR_NUM			8 +struct pmecc_regs { +	u32 cfg;		/* 0x00 PMECC Configuration Register */ +	u32 sarea;		/* 0x04 PMECC Spare Area Size Register */ +	u32 saddr;		/* 0x08 PMECC Start Address Register */ +	u32 eaddr;		/* 0x0C PMECC End Address Register */ +	u32 clk;		/* 0x10 PMECC Clock Control Register */ +	u32 ctrl;		/* 0x14 PMECC Control Register */ +	u32 sr;			/* 0x18 PMECC Status Register */ +	u32 ier;		/* 0x1C PMECC Interrupt Enable Register */ +	u32 idr;		/* 0x20 PMECC Interrupt Disable Register */ +	u32 imr;		/* 0x24 PMECC Interrupt Mask Register */ +	u32 isr;		/* 0x28 PMECC Interrupt Status Register */ +	u32 reserved0[5];	/* 0x2C-0x3C Reserved */ + +	/* 0x40 + sector_num * (0x40), Redundancy Registers */ +	struct { +		u8 ecc[44];	/* PMECC Generated Redundancy Byte Per Sector */ +		u32 reserved1[5]; +	} ecc_port[PMECC_MAX_SECTOR_NUM]; + +	/* 0x240 + sector_num * (0x40) Remainder Registers */ +	struct { +		u32 rem[12]; +		u32 reserved2[4]; +	} rem_port[PMECC_MAX_SECTOR_NUM]; +	u32 reserved3[16];	/* 0x440-0x47C Reserved */ +}; + +/* For PMECC Configuration Register */ +#define		PMECC_CFG_BCH_ERR2		(0 << 0) +#define		PMECC_CFG_BCH_ERR4		(1 << 0) +#define		PMECC_CFG_BCH_ERR8		(2 << 0) +#define		PMECC_CFG_BCH_ERR12		(3 << 0) +#define		PMECC_CFG_BCH_ERR24		(4 << 0) + +#define		PMECC_CFG_SECTOR512		(0 << 4) +#define		PMECC_CFG_SECTOR1024		(1 << 4) + +#define		PMECC_CFG_PAGE_1SECTOR		(0 << 8) +#define		PMECC_CFG_PAGE_2SECTORS		(1 << 8) +#define		PMECC_CFG_PAGE_4SECTORS		(2 << 8) +#define		PMECC_CFG_PAGE_8SECTORS		(3 << 8) + +#define		PMECC_CFG_READ_OP		(0 << 12) +#define		PMECC_CFG_WRITE_OP		(1 << 12) + +#define		PMECC_CFG_SPARE_ENABLE		(1 << 16) +#define		PMECC_CFG_SPARE_DISABLE		(0 << 16) + +#define		PMECC_CFG_AUTO_ENABLE		(1 << 20) +#define		PMECC_CFG_AUTO_DISABLE		(0 << 20) + +/* For PMECC Clock Control Register */ +#define		PMECC_CLK_133MHZ		(2 << 0) + +/* For PMECC Control Register */ +#define		PMECC_CTRL_RST			(1 << 0) +#define		PMECC_CTRL_DATA			(1 << 1) +#define		PMECC_CTRL_USER			(1 << 2) +#define		PMECC_CTRL_ENABLE		(1 << 4) +#define		PMECC_CTRL_DISABLE		(1 << 5) + +/* For PMECC Status Register */ +#define		PMECC_SR_BUSY			(1 << 0) +#define		PMECC_SR_ENABLE			(1 << 4) + +/* PMERRLOC Register Definitions */ +struct pmecc_errloc_regs { +	u32 elcfg;	/* 0x00 Error Location Configuration Register */ +	u32 elprim;	/* 0x04 Error Location Primitive Register */ +	u32 elen;	/* 0x08 Error Location Enable Register */ +	u32 eldis;	/* 0x0C Error Location Disable Register */ +	u32 elsr;	/* 0x10 Error Location Status Register */ +	u32 elier;	/* 0x14 Error Location Interrupt Enable Register */ +	u32 elidr;	/* 0x08 Error Location Interrupt Disable Register */ +	u32 elimr;	/* 0x0C Error Location Interrupt Mask Register */ +	u32 elisr;	/* 0x20 Error Location Interrupt Status Register */ +	u32 reserved0;	/* 0x24 Reserved */ +	u32 sigma[25];	/* 0x28-0x88 Error Location Sigma Registers */ +	u32 el[24];	/* 0x8C-0xE8 Error Location Registers */ +	u32 reserved1[5];	/* 0xEC-0xFC Reserved */ +}; + +/* For Error Location Configuration Register */ +#define		PMERRLOC_ELCFG_SECTOR_512	(0 << 0) +#define		PMERRLOC_ELCFG_SECTOR_1024	(1 << 0) +#define		PMERRLOC_ELCFG_NUM_ERRORS(n)	((n) << 16) + +/* For Error Location Disable Register */ +#define		PMERRLOC_DISABLE		(1 << 0) + +/* For Error Location Interrupt Status Register */ +#define		PMERRLOC_ERR_NUM_MASK		(0x1f << 8) +#define		PMERRLOC_CALC_DONE		(1 << 0) + +/* Galois field dimension */ +#define PMECC_GF_DIMENSION_13			13 +#define PMECC_GF_DIMENSION_14			14 + +#define PMECC_INDEX_TABLE_SIZE_512		0x2000 +#define PMECC_INDEX_TABLE_SIZE_1024		0x4000 + +#define PMECC_MAX_TIMEOUT_US		(100 * 1000) +  #endif diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 936186f75..d0ded483e 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -25,168 +25,23 @@  #if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX35)  #include <asm/arch/imx-regs.h>  #endif +#include <fsl_nfc.h>  #define DRIVER_NAME "mxc_nand" -/* - * TODO: Use same register defs here as nand_spl mxc nand driver. - */ -/* - * Register map and bit definitions for the Freescale NAND Flash Controller - * present in various i.MX devices. - * - * MX31 and MX27 have version 1 which has - * 	4 512 byte main buffers and - * 	4 16 byte spare buffers - * 	to support up to 2K byte pagesize nand. - * 	Reading or writing a 2K page requires 4 FDI/FDO cycles. - * - * MX25 has version 1.1 which has - * 	8 512 byte main buffers and - * 	8 64 byte spare buffers - * 	to support up to 4K byte pagesize nand. - * 	Reading or writing a 2K or 4K page requires only 1 FDI/FDO cycle. - *      Also some of registers are moved and/or changed meaning as seen below. - */ -#if defined(CONFIG_MX31) || defined(CONFIG_MX27) -#define MXC_NFC_V1 -#elif defined(CONFIG_MX25) || defined(CONFIG_MX35) -#define MXC_NFC_V1_1 -#else -#warning "MXC NFC version not defined" -#endif - -#if defined(MXC_NFC_V1) -#define NAND_MXC_NR_BUFS		4 -#define NAND_MXC_SPARE_BUF_SIZE		16 -#define NAND_MXC_REG_OFFSET		0xe00 -#define is_mxc_nfc_11() 		0 -#elif defined(MXC_NFC_V1_1) -#define NAND_MXC_NR_BUFS		8 -#define NAND_MXC_SPARE_BUF_SIZE		64 -#define NAND_MXC_REG_OFFSET		0x1e00 -#define is_mxc_nfc_11() 		1 -#else -#error "define CONFIG_NAND_MXC_VXXX to use mtd mxc nand driver" -#endif -struct nfc_regs { -	uint8_t main_area[NAND_MXC_NR_BUFS][0x200]; -	uint8_t spare_area[NAND_MXC_NR_BUFS][NAND_MXC_SPARE_BUF_SIZE]; -	/* -	 * reserved size is offset of nfc registers -	 * minus total main and spare sizes -	 */ -	uint8_t reserved1[NAND_MXC_REG_OFFSET -		- NAND_MXC_NR_BUFS * (512 + NAND_MXC_SPARE_BUF_SIZE)]; -#if defined(MXC_NFC_V1) -	uint16_t nfc_buf_size; -	uint16_t reserved2; -	uint16_t nfc_buf_addr; -	uint16_t nfc_flash_addr; -	uint16_t nfc_flash_cmd; -	uint16_t nfc_config; -	uint16_t nfc_ecc_status_result; -	uint16_t nfc_rsltmain_area; -	uint16_t nfc_rsltspare_area; -	uint16_t nfc_wrprot; -	uint16_t nfc_unlockstart_blkaddr; -	uint16_t nfc_unlockend_blkaddr; -	uint16_t nfc_nf_wrprst; -	uint16_t nfc_config1; -	uint16_t nfc_config2; -#elif defined(MXC_NFC_V1_1) -	uint16_t reserved2[2]; -	uint16_t nfc_buf_addr; -	uint16_t nfc_flash_addr; -	uint16_t nfc_flash_cmd; -	uint16_t nfc_config; -	uint16_t nfc_ecc_status_result; -	uint16_t nfc_ecc_status_result2; -	uint16_t nfc_spare_area_size; -	uint16_t nfc_wrprot; -	uint16_t reserved3[2]; -	uint16_t nfc_nf_wrprst; -	uint16_t nfc_config1; -	uint16_t nfc_config2; -	uint16_t reserved4; -	uint16_t nfc_unlockstart_blkaddr; -	uint16_t nfc_unlockend_blkaddr; -	uint16_t nfc_unlockstart_blkaddr1; -	uint16_t nfc_unlockend_blkaddr1; -	uint16_t nfc_unlockstart_blkaddr2; -	uint16_t nfc_unlockend_blkaddr2; -	uint16_t nfc_unlockstart_blkaddr3; -	uint16_t nfc_unlockend_blkaddr3; -#endif -}; - -/* - * Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register - * for Command operation - */ -#define NFC_CMD            0x1 - -/* - * Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register - * for Address operation - */ -#define NFC_ADDR           0x2 - -/* - * Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register - * for Input operation - */ -#define NFC_INPUT          0x4 - -/* - * Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register - * for Data Output operation - */ -#define NFC_OUTPUT         0x8 - -/* - * Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register - * for Read ID operation - */ -#define NFC_ID             0x10 - -/* - * Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register - * for Read Status operation - */ -#define NFC_STATUS         0x20 - -/* - * Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read - * Status operation - */ -#define NFC_INT            0x8000 - -#ifdef MXC_NFC_V1_1 -#define NFC_4_8N_ECC	(1 << 0) -#else -#define NFC_4_8N_ECC	0 -#endif -#define NFC_SP_EN           (1 << 2) -#define NFC_ECC_EN          (1 << 3) -#define NFC_BIG             (1 << 5) -#define NFC_RST             (1 << 6) -#define NFC_CE              (1 << 7) -#define NFC_ONE_CYCLE       (1 << 8) -  typedef enum {false, true} bool;  struct mxc_nand_host { -	struct mtd_info		mtd; -	struct nand_chip	*nand; +	struct mtd_info			mtd; +	struct nand_chip		*nand; -	struct nfc_regs __iomem	*regs; -	int			spare_only; -	int			status_request; -	int			pagesize_2k; -	int			clk_act; -	uint16_t		col_addr; -	unsigned int		page_addr; +	struct fsl_nfc_regs __iomem	*regs; +	int				spare_only; +	int				status_request; +	int				pagesize_2k; +	int				clk_act; +	uint16_t			col_addr; +	unsigned int			page_addr;  };  static struct mxc_nand_host mxc_host; @@ -222,7 +77,7 @@ static struct nand_ecclayout nand_hw_eccoob2k = {  	.oobfree = { {2, 4}, {11, 11}, {27, 11}, {43, 11}, {59, 5} },  };  #endif -#elif defined(MXC_NFC_V1_1) +#elif defined(MXC_NFC_V2_1)  #ifndef CONFIG_SYS_NAND_LARGEPAGE  static struct nand_ecclayout nand_hw_eccoob = {  	.eccbytes = 9, @@ -268,8 +123,7 @@ static int is_16bit_nand(void)  #elif defined(CONFIG_MX25) || defined(CONFIG_MX35)  static int is_16bit_nand(void)  { -	struct ccm_regs *ccm = -		(struct ccm_regs *)IMX_CCM_BASE; +	struct ccm_regs *ccm = (struct ccm_regs *)IMX_CCM_BASE;  	if (readl(&ccm->rcsr) & CCM_RCSR_NF_16BIT_SEL)  		return 1; @@ -304,10 +158,10 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries,  	uint32_t tmp;  	while (max_retries-- > 0) { -		if (readw(&host->regs->nfc_config2) & NFC_INT) { -			tmp = readw(&host->regs->nfc_config2); +		if (readw(&host->regs->config2) & NFC_INT) { +			tmp = readw(&host->regs->config2);  			tmp  &= ~NFC_INT; -			writew(tmp, &host->regs->nfc_config2); +			writew(tmp, &host->regs->config2);  			break;  		}  		udelay(1); @@ -326,8 +180,8 @@ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd)  {  	MTDDEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x)\n", cmd); -	writew(cmd, &host->regs->nfc_flash_cmd); -	writew(NFC_CMD, &host->regs->nfc_config2); +	writew(cmd, &host->regs->flash_cmd); +	writew(NFC_CMD, &host->regs->config2);  	/* Wait for operation to complete */  	wait_op_done(host, TROP_US_DELAY, cmd); @@ -342,8 +196,8 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr)  {  	MTDDEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x)\n", addr); -	writew(addr, &host->regs->nfc_flash_addr); -	writew(NFC_ADDR, &host->regs->nfc_config2); +	writew(addr, &host->regs->flash_addr); +	writew(NFC_ADDR, &host->regs->config2);  	/* Wait for operation to complete */  	wait_op_done(host, TROP_US_DELAY, addr); @@ -359,7 +213,7 @@ static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id,  	if (spare_only)  		MTDDEBUG(MTD_DEBUG_LEVEL1, "send_prog_page (%d)\n", spare_only); -	if (is_mxc_nfc_11()) { +	if (is_mxc_nfc_21()) {  		int i;  		/*  		 *  The controller copies the 64 bytes of spare data from @@ -375,19 +229,19 @@ static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id,  		}  	} -	writew(buf_id, &host->regs->nfc_buf_addr); +	writew(buf_id, &host->regs->buf_addr);  	/* Configure spare or page+spare access */  	if (!host->pagesize_2k) { -		uint16_t config1 = readw(&host->regs->nfc_config1); +		uint16_t config1 = readw(&host->regs->config1);  		if (spare_only)  			config1 |= NFC_SP_EN;  		else -			config1 &= ~(NFC_SP_EN); -		writew(config1, &host->regs->nfc_config1); +			config1 &= ~NFC_SP_EN; +		writew(config1, &host->regs->config1);  	} -	writew(NFC_INPUT, &host->regs->nfc_config2); +	writew(NFC_INPUT, &host->regs->config2);  	/* Wait for operation to complete */  	wait_op_done(host, TROP_US_DELAY, spare_only); @@ -402,24 +256,24 @@ static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id,  {  	MTDDEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only); -	writew(buf_id, &host->regs->nfc_buf_addr); +	writew(buf_id, &host->regs->buf_addr);  	/* Configure spare or page+spare access */  	if (!host->pagesize_2k) { -		uint32_t config1 = readw(&host->regs->nfc_config1); +		uint32_t config1 = readw(&host->regs->config1);  		if (spare_only)  			config1 |= NFC_SP_EN;  		else  			config1 &= ~NFC_SP_EN; -		writew(config1, &host->regs->nfc_config1); +		writew(config1, &host->regs->config1);  	} -	writew(NFC_OUTPUT, &host->regs->nfc_config2); +	writew(NFC_OUTPUT, &host->regs->config2);  	/* Wait for operation to complete */  	wait_op_done(host, TROP_US_DELAY, spare_only); -	if (is_mxc_nfc_11()) { +	if (is_mxc_nfc_21()) {  		int i;  		/* @@ -442,14 +296,14 @@ static void send_read_id(struct mxc_nand_host *host)  	uint16_t tmp;  	/* NANDFC buffer 0 is used for device ID output */ -	writew(0x0, &host->regs->nfc_buf_addr); +	writew(0x0, &host->regs->buf_addr);  	/* Read ID into main buffer */ -	tmp = readw(&host->regs->nfc_config1); +	tmp = readw(&host->regs->config1);  	tmp &= ~NFC_SP_EN; -	writew(tmp, &host->regs->nfc_config1); +	writew(tmp, &host->regs->config1); -	writew(NFC_ID, &host->regs->nfc_config2); +	writew(NFC_ID, &host->regs->config2);  	/* Wait for operation to complete */  	wait_op_done(host, TROP_US_DELAY, 0); @@ -469,14 +323,14 @@ static uint16_t get_dev_status(struct mxc_nand_host *host)  	/* store the main area1 first word, later do recovery */  	store = readl(main_buf);  	/* NANDFC buffer 1 is used for device status */ -	writew(1, &host->regs->nfc_buf_addr); +	writew(1, &host->regs->buf_addr);  	/* Read status into main buffer */ -	tmp = readw(&host->regs->nfc_config1); +	tmp = readw(&host->regs->config1);  	tmp &= ~NFC_SP_EN; -	writew(tmp, &host->regs->nfc_config1); +	writew(tmp, &host->regs->config1); -	writew(NFC_STATUS, &host->regs->nfc_config2); +	writew(NFC_STATUS, &host->regs->config2);  	/* Wait for operation to complete */  	wait_op_done(host, TROP_US_DELAY, 0); @@ -501,29 +355,29 @@ static int mxc_nand_dev_ready(struct mtd_info *mtd)  	return 1;  } -#ifdef CONFIG_MXC_NAND_HWECC -static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) -{ -	/* -	 * If HW ECC is enabled, we turn it on during init. There is -	 * no need to enable again here. -	 */ -} - -#ifdef MXC_NFC_V1_1  static void _mxc_nand_enable_hwecc(struct mtd_info *mtd, int on)  {  	struct nand_chip *nand_chip = mtd->priv;  	struct mxc_nand_host *host = nand_chip->priv; -	uint16_t tmp = readw(&host->regs->nfc_config1); +	uint16_t tmp = readw(&host->regs->config1);  	if (on)  		tmp |= NFC_ECC_EN;  	else  		tmp &= ~NFC_ECC_EN; -	writew(tmp, &host->regs->nfc_config1); +	writew(tmp, &host->regs->config1); +} + +#ifdef CONFIG_MXC_NAND_HWECC +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ +	/* +	 * If HW ECC is enabled, we turn it on during init. There is +	 * no need to enable again here. +	 */  } +#ifdef MXC_NFC_V2_1  static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd,  				      struct nand_chip *chip,  				      int page, int sndcmd) @@ -616,7 +470,7 @@ static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd,  	size = mtd->oobsize - (oob - chip->oob_poi);  	if (size)  		chip->read_buf(mtd, oob, size); -	_mxc_nand_enable_hwecc(mtd, 0); +	_mxc_nand_enable_hwecc(mtd, 1);  	return 0;  } @@ -799,7 +653,7 @@ static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,  {  	struct nand_chip *nand_chip = mtd->priv;  	struct mxc_nand_host *host = nand_chip->priv; -	uint16_t ecc_status = readw(&host->regs->nfc_ecc_status_result); +	uint32_t ecc_status = readl(&host->regs->ecc_status_result);  	int subpages = mtd->writesize / nand_chip->subpagesize;  	int pg2blk_shift = nand_chip->phys_erase_shift -  			   nand_chip->page_shift; @@ -832,7 +686,6 @@ static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,  #define mxc_nand_write_page_syndrome NULL  #define mxc_nand_write_page_raw_syndrome NULL  #define mxc_nand_write_oob_syndrome NULL -#define mxc_nfc_11_nand_correct_data NULL  static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,  				 u_char *read_ecc, u_char *calc_ecc) @@ -845,7 +698,7 @@ static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,  	 * additional correction.  2-Bit errors cannot be corrected by  	 * HW ECC, so we need to return failure  	 */ -	uint16_t ecc_status = readw(&host->regs->nfc_ecc_status_result); +	uint16_t ecc_status = readw(&host->regs->ecc_status_result);  	if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {  		MTDDEBUG(MTD_DEBUG_LEVEL0, @@ -1208,7 +1061,7 @@ void mxc_nand_command(struct mtd_info *mtd, unsigned command,  	case NAND_CMD_PAGEPROG:  		send_prog_page(host, 0, host->spare_only); -		if (host->pagesize_2k && !is_mxc_nfc_11()) { +		if (host->pagesize_2k && is_mxc_nfc_1()) {  			/* data in 4 areas */  			send_prog_page(host, 1, host->spare_only);  			send_prog_page(host, 2, host->spare_only); @@ -1258,7 +1111,7 @@ void mxc_nand_command(struct mtd_info *mtd, unsigned command,  			send_cmd(host, NAND_CMD_READSTART);  			/* read for each AREA */  			send_read_page(host, 0, host->spare_only); -			if (!is_mxc_nfc_11()) { +			if (is_mxc_nfc_1()) {  				send_read_page(host, 1, host->spare_only);  				send_read_page(host, 2, host->spare_only);  				send_read_page(host, 3, host->spare_only); @@ -1284,24 +1137,6 @@ void mxc_nand_command(struct mtd_info *mtd, unsigned command,  	}  } -#ifdef MXC_NFC_V1_1 -static void mxc_setup_config1(void) -{ -	uint16_t tmp; - -	tmp = readw(&host->regs->nfc_config1); -	tmp |= NFC_ONE_CYCLE; -	tmp |= NFC_4_8N_ECC; -	writew(tmp, &host->regs->nfc_config1); -	if (host->pagesize_2k) -		writew(64/2, &host->regs->nfc_spare_area_size); -	else -		writew(16/2, &host->regs->nfc_spare_area_size); -} -#else -#define mxc_setup_config1() -#endif -  #ifdef CONFIG_SYS_NAND_USE_FLASH_BBT  static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; @@ -1332,8 +1167,9 @@ static struct nand_bbt_descr bbt_mirror_descr = {  int board_nand_init(struct nand_chip *this)  {  	struct mtd_info *mtd; +#ifdef MXC_NFC_V2_1  	uint16_t tmp; -	int err = 0; +#endif  #ifdef CONFIG_SYS_NAND_USE_FLASH_BBT  	this->options |= NAND_USE_FLASH_BBT; @@ -1359,14 +1195,14 @@ int board_nand_init(struct nand_chip *this)  	this->read_buf = mxc_nand_read_buf;  	this->verify_buf = mxc_nand_verify_buf; -	host->regs = (struct nfc_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE; +	host->regs = (struct fsl_nfc_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE;  	host->clk_act = 1;  #ifdef CONFIG_MXC_NAND_HWECC  	this->ecc.calculate = mxc_nand_calculate_ecc;  	this->ecc.hwctl = mxc_nand_enable_hwecc;  	this->ecc.correct = mxc_nand_correct_data; -	if (is_mxc_nfc_11()) { +	if (is_mxc_nfc_21()) {  		this->ecc.mode = NAND_ECC_HW_SYNDROME;  		this->ecc.read_page = mxc_nand_read_page_syndrome;  		this->ecc.read_page_raw = mxc_nand_read_page_raw_syndrome; @@ -1383,27 +1219,46 @@ int board_nand_init(struct nand_chip *this)  	host->pagesize_2k = 0;  	this->ecc.size = 512; -	tmp = readw(&host->regs->nfc_config1); -	tmp |= NFC_ECC_EN; -	writew(tmp, &host->regs->nfc_config1); +	_mxc_nand_enable_hwecc(mtd, 1);  #else  	this->ecc.layout = &nand_soft_eccoob;  	this->ecc.mode = NAND_ECC_SOFT; -	tmp = readw(&host->regs->nfc_config1); -	tmp &= ~NFC_ECC_EN; -	writew(tmp, &host->regs->nfc_config1); +	_mxc_nand_enable_hwecc(mtd, 0);  #endif  	/* Reset NAND */  	this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); +	/* NAND bus width determines access functions used by upper layer */ +	if (is_16bit_nand()) +		this->options |= NAND_BUSWIDTH_16; + +#ifdef CONFIG_SYS_NAND_LARGEPAGE +	host->pagesize_2k = 1; +	this->ecc.layout = &nand_hw_eccoob2k; +#else +	host->pagesize_2k = 0; +	this->ecc.layout = &nand_hw_eccoob; +#endif + +#ifdef MXC_NFC_V2_1 +	tmp = readw(&host->regs->config1); +	tmp |= NFC_ONE_CYCLE; +	tmp |= NFC_4_8N_ECC; +	writew(tmp, &host->regs->config1); +	if (host->pagesize_2k) +		writew(64/2, &host->regs->spare_area_size); +	else +		writew(16/2, &host->regs->spare_area_size); +#endif +  	/*  	 * preset operation  	 * Unlock the internal RAM Buffer  	 */ -	writew(0x2, &host->regs->nfc_config); +	writew(0x2, &host->regs->config);  	/* Blocks to be unlocked */ -	writew(0x0, &host->regs->nfc_unlockstart_blkaddr); +	writew(0x0, &host->regs->unlockstart_blkaddr);  	/* Originally (Freescale LTIB 2.6.21) 0x4000 was written to the  	 * unlockend_blkaddr, but the magic 0x4000 does not always work  	 * when writing more than some 32 megabytes (on 2k page nands) @@ -1415,22 +1270,10 @@ int board_nand_init(struct nand_chip *this)  	 * This might be NAND chip specific and the i.MX31 datasheet is  	 * extremely vague about the semantics of this register.  	 */ -	writew(0xFFFF, &host->regs->nfc_unlockend_blkaddr); +	writew(0xFFFF, &host->regs->unlockend_blkaddr);  	/* Unlock Block Command for given address range */ -	writew(0x4, &host->regs->nfc_wrprot); +	writew(0x4, &host->regs->wrprot); -	/* NAND bus width determines access functions used by upper layer */ -	if (is_16bit_nand()) -		this->options |= NAND_BUSWIDTH_16; - -#ifdef CONFIG_SYS_NAND_LARGEPAGE -	host->pagesize_2k = 1; -	this->ecc.layout = &nand_hw_eccoob2k; -#else -	host->pagesize_2k = 0; -	this->ecc.layout = &nand_hw_eccoob; -#endif -	mxc_setup_config1(); -	return err; +	return 0;  } diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 9c9581105..4701be846 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -25,10 +25,10 @@   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.   */ +#include <common.h>  #include <linux/mtd/mtd.h>  #include <linux/mtd/nand.h>  #include <linux/types.h> -#include <common.h>  #include <malloc.h>  #include <asm/errno.h>  #include <asm/io.h> @@ -233,11 +233,11 @@ static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)   */  static int mxs_nand_wait_for_bch_complete(void)  { -	struct mx28_bch_regs *bch_regs = (struct mx28_bch_regs *)MXS_BCH_BASE; +	struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;  	int timeout = MXS_NAND_BCH_TIMEOUT;  	int ret; -	ret = mx28_wait_mask_set(&bch_regs->hw_bch_ctrl_reg, +	ret = mxs_wait_mask_set(&bch_regs->hw_bch_ctrl_reg,  		BCH_CTRL_COMPLETE_IRQ, timeout);  	writel(BCH_CTRL_COMPLETE_IRQ, &bch_regs->hw_bch_ctrl_clr); @@ -338,8 +338,8 @@ static int mxs_nand_device_ready(struct mtd_info *mtd)  {  	struct nand_chip *chip = mtd->priv;  	struct mxs_nand_info *nand_info = chip->priv; -	struct mx28_gpmi_regs *gpmi_regs = -		(struct mx28_gpmi_regs *)MXS_GPMI_BASE; +	struct mxs_gpmi_regs *gpmi_regs = +		(struct mxs_gpmi_regs *)MXS_GPMI_BASE;  	uint32_t tmp;  	tmp = readl(&gpmi_regs->hw_gpmi_stat); @@ -968,11 +968,11 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd)  {  	struct nand_chip *nand = mtd->priv;  	struct mxs_nand_info *nand_info = nand->priv; -	struct mx28_bch_regs *bch_regs = (struct mx28_bch_regs *)MXS_BCH_BASE; +	struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;  	uint32_t tmp;  	/* Configure BCH and set NFC geometry */ -	mx28_reset_block(&bch_regs->hw_bch_ctrl_reg); +	mxs_reset_block(&bch_regs->hw_bch_ctrl_reg);  	/* Configure layout 0 */  	tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1) @@ -1056,8 +1056,8 @@ int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info)   */  int mxs_nand_init(struct mxs_nand_info *info)  { -	struct mx28_gpmi_regs *gpmi_regs = -		(struct mx28_gpmi_regs *)MXS_GPMI_BASE; +	struct mxs_gpmi_regs *gpmi_regs = +		(struct mxs_gpmi_regs *)MXS_GPMI_BASE;  	int i = 0, j;  	info->desc = malloc(sizeof(struct mxs_dma_desc *) * @@ -1080,7 +1080,7 @@ int mxs_nand_init(struct mxs_nand_info *info)  	}  	/* Reset the GPMI block. */ -	mx28_reset_block(&gpmi_regs->hw_gpmi_ctrl0_reg); +	mxs_reset_block(&gpmi_regs->hw_gpmi_ctrl0_reg);  	/*  	 * Choose NAND mode, set IRQ polarity, disable write protection and diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index bfd668fa0..71f502788 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2573,14 +2573,13 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,  	mtd->writesize = le32_to_cpu(p->byte_per_page);  	mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;  	mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); -	chip->chipsize = (uint64_t)le32_to_cpu(p->blocks_per_lun) * mtd->erasesize; +	chip->chipsize = le32_to_cpu(p->blocks_per_lun); +	chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;  	*busw = 0;  	if (le16_to_cpu(p->features) & 1)  		*busw = NAND_BUSWIDTH_16; -	chip->options &= ~NAND_CHIPOPTIONS_MSK; -	chip->options |= (NAND_NO_READRDY | -			NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK; +	chip->options |= NAND_NO_READRDY | NAND_NO_AUTOINCR;  	return 1;  } @@ -2752,8 +2751,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,  		}  	}  	/* Get chip options, preserve non chip based options */ -	chip->options &= ~NAND_CHIPOPTIONS_MSK; -	chip->options |= type->options & NAND_CHIPOPTIONS_MSK; +	chip->options |= type->options;  	/* Check if chip is a not a samsung device. Do not clear the  	 * options for chips which are not having an extended id. @@ -2936,7 +2934,8 @@ int nand_scan_tail(struct mtd_info *mtd)  	struct nand_chip *chip = mtd->priv;  	if (!(chip->options & NAND_OWN_BUFFERS)) -		chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL); +		chip->buffers = memalign(ARCH_DMA_MINALIGN, +					 sizeof(*chip->buffers));  	if (!chip->buffers)  		return -ENOMEM; diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 7ed8b1891..c4752a7cb 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -207,12 +207,6 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)   * Support for locking / unlocking operations of some NAND devices   *****************************************************************************/ -#define NAND_CMD_LOCK		0x2a -#define NAND_CMD_LOCK_TIGHT	0x2c -#define NAND_CMD_UNLOCK1	0x23 -#define NAND_CMD_UNLOCK2	0x24 -#define NAND_CMD_LOCK_STATUS	0x7a -  /**   * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT   *	      state @@ -271,7 +265,6 @@ int nand_lock(struct mtd_info *mtd, int tight)   *			>0 lock status:   *			  bitfield with the following combinations:   *			  NAND_LOCK_STATUS_TIGHT: page in tight state - *			  NAND_LOCK_STATUS_LOCK:  page locked   *			  NAND_LOCK_STATUS_UNLOCK: page unlocked   *   */ @@ -300,7 +293,6 @@ int nand_get_lock_status(struct mtd_info *mtd, loff_t offset)  	chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask);  	ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT -					  | NAND_LOCK_STATUS_LOCK  					  | NAND_LOCK_STATUS_UNLOCK);   out: @@ -317,18 +309,21 @@ int nand_get_lock_status(struct mtd_info *mtd, loff_t offset)   * @param start		start byte address   * @param length	number of bytes to unlock (must be a multiple of   *			page size nand->writesize) + * @param allexcept	if set, unlock everything not selected   *   * @return		0 on success, -1 in case of error   */ -int nand_unlock(struct mtd_info *mtd, ulong start, ulong length) +int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length, +	int allexcept)  {  	int ret = 0;  	int chipnr;  	int status;  	int page;  	struct nand_chip *chip = mtd->priv; -	printf ("nand_unlock: start: %08x, length: %d!\n", -		(int)start, (int)length); + +	debug("nand_unlock%s: start: %08llx, length: %d!\n", +		allexcept ? " (allexcept)" : "", start, length);  	/* select the NAND device */  	chipnr = (int)(start >> chip->chip_shift); @@ -368,6 +363,15 @@ int nand_unlock(struct mtd_info *mtd, ulong start, ulong length)  	/* submit ADDRESS of LAST page to unlock */  	page += (int)(length >> chip->page_shift); + +	/* +	 * Page addresses for unlocking are supposed to be block-aligned. +	 * At least some NAND chips use the low bit to indicate that the +	 * page range should be inverted. +	 */ +	if (allexcept) +		page |= 1; +  	chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, page & chip->pagemask);  	/* call wait ready function */ diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index ca868efb9..f1469d110 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -283,6 +283,7 @@ void omap_nand_switch_ecc(int32_t hardware)  		nand->ecc.mode = NAND_ECC_SOFT;  		/* Use mtd default settings */  		nand->ecc.layout = NULL; +		nand->ecc.size = 0;  		printf("SW ECC selected\n");  	} diff --git a/drivers/mtd/nand/tegra_nand.c b/drivers/mtd/nand/tegra_nand.c new file mode 100644 index 000000000..8c1de3445 --- /dev/null +++ b/drivers/mtd/nand/tegra_nand.c @@ -0,0 +1,1026 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com> + * (C) Copyright 2006 Detlev Zundel, dzu@denx.de + * (C) Copyright 2006 DENX Software Engineering + * + * 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 <nand.h> +#include <asm/arch/clk_rst.h> +#include <asm/arch/clock.h> +#include <asm/arch/funcmux.h> +#include <asm/arch/gpio.h> +#include <asm/errno.h> +#include <asm-generic/gpio.h> +#include <fdtdec.h> +#include "tegra_nand.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define NAND_CMD_TIMEOUT_MS		10 + +#define SKIPPED_SPARE_BYTES		4 + +/* ECC bytes to be generated for tag data */ +#define TAG_ECC_BYTES			4 + +/* 64 byte oob block info for large page (== 2KB) device + * + * OOB flash layout for Tegra with Reed-Solomon 4 symbol correct ECC: + *      Skipped bytes(4) + *      Main area Ecc(36) + *      Tag data(20) + *      Tag data Ecc(4) + * + * Yaffs2 will use 16 tag bytes. + */ +static struct nand_ecclayout eccoob = { +	.eccbytes = 36, +	.eccpos = { +		4,  5,  6,  7,  8,  9,  10, 11, 12, +		13, 14, 15, 16, 17, 18, 19, 20, 21, +		22, 23, 24, 25, 26, 27, 28, 29, 30, +		31, 32, 33, 34, 35, 36, 37, 38, 39, +	}, +	.oobavail = 20, +	.oobfree = { +			{ +			.offset = 40, +			.length = 20, +			}, +	} +}; + +enum { +	ECC_OK, +	ECC_TAG_ERROR = 1 << 0, +	ECC_DATA_ERROR = 1 << 1 +}; + +/* Timing parameters */ +enum { +	FDT_NAND_MAX_TRP_TREA, +	FDT_NAND_TWB, +	FDT_NAND_MAX_TCR_TAR_TRR, +	FDT_NAND_TWHR, +	FDT_NAND_MAX_TCS_TCH_TALS_TALH, +	FDT_NAND_TWH, +	FDT_NAND_TWP, +	FDT_NAND_TRH, +	FDT_NAND_TADL, + +	FDT_NAND_TIMING_COUNT +}; + +/* Information about an attached NAND chip */ +struct fdt_nand { +	struct nand_ctlr *reg; +	int enabled;		/* 1 to enable, 0 to disable */ +	struct fdt_gpio_state wp_gpio;	/* write-protect GPIO */ +	s32 width;		/* bit width, normally 8 */ +	u32 timing[FDT_NAND_TIMING_COUNT]; +}; + +struct nand_drv { +	struct nand_ctlr *reg; + +	/* +	* When running in PIO mode to get READ ID bytes from register +	* RESP_0, we need this variable as an index to know which byte in +	* register RESP_0 should be read. +	* Because common code in nand_base.c invokes read_byte function two +	* times for NAND_CMD_READID. +	* And our controller returns 4 bytes at once in register RESP_0. +	*/ +	int pio_byte_index; +	struct fdt_nand config; +}; + +static struct nand_drv nand_ctrl; +static struct mtd_info *our_mtd; +static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; + +#ifdef CONFIG_SYS_DCACHE_OFF +static inline void dma_prepare(void *start, unsigned long length, +			       int is_writing) +{ +} +#else +/** + * Prepare for a DMA transaction + * + * For a write we flush out our data. For a read we invalidate, since we + * need to do this before we read from the buffer after the DMA has + * completed, so may as well do it now. + * + * @param start		Start address for DMA buffer (should be cache-aligned) + * @param length	Length of DMA buffer in bytes + * @param is_writing	0 if reading, non-zero if writing + */ +static void dma_prepare(void *start, unsigned long length, int is_writing) +{ +	unsigned long addr = (unsigned long)start; + +	length = ALIGN(length, ARCH_DMA_MINALIGN); +	if (is_writing) +		flush_dcache_range(addr, addr + length); +	else +		invalidate_dcache_range(addr, addr + length); +} +#endif + +/** + * Wait for command completion + * + * @param reg	nand_ctlr structure + * @return + *	1 - Command completed + *	0 - Timeout + */ +static int nand_waitfor_cmd_completion(struct nand_ctlr *reg) +{ +	u32 reg_val; +	int running; +	int i; + +	for (i = 0; i < NAND_CMD_TIMEOUT_MS * 1000; i++) { +		if ((readl(®->command) & CMD_GO) || +				!(readl(®->status) & STATUS_RBSY0) || +				!(readl(®->isr) & ISR_IS_CMD_DONE)) { +			udelay(1); +			continue; +		} +		reg_val = readl(®->dma_mst_ctrl); +		/* +		 * If DMA_MST_CTRL_EN_A_ENABLE or DMA_MST_CTRL_EN_B_ENABLE +		 * is set, that means DMA engine is running. +		 * +		 * Then we have to wait until DMA_MST_CTRL_IS_DMA_DONE +		 * is cleared, indicating DMA transfer completion. +		 */ +		running = reg_val & (DMA_MST_CTRL_EN_A_ENABLE | +				DMA_MST_CTRL_EN_B_ENABLE); +		if (!running || (reg_val & DMA_MST_CTRL_IS_DMA_DONE)) +			return 1; +		udelay(1); +	} +	return 0; +} + +/** + * Read one byte from the chip + * + * @param mtd	MTD device structure + * @return	data byte + * + * Read function for 8bit bus-width + */ +static uint8_t read_byte(struct mtd_info *mtd) +{ +	struct nand_chip *chip = mtd->priv; +	u32 dword_read; +	struct nand_drv *info; + +	info = (struct nand_drv *)chip->priv; + +	/* In PIO mode, only 4 bytes can be transferred with single CMD_GO. */ +	if (info->pio_byte_index > 3) { +		info->pio_byte_index = 0; +		writel(CMD_GO | CMD_PIO +			| CMD_RX | CMD_CE0, +			&info->reg->command); +		if (!nand_waitfor_cmd_completion(info->reg)) +			printf("Command timeout\n"); +	} + +	dword_read = readl(&info->reg->resp); +	dword_read = dword_read >> (8 * info->pio_byte_index); +	info->pio_byte_index++; +	return (uint8_t)dword_read; +} + +/** + * Check NAND status to see if it is ready or not + * + * @param mtd	MTD device structure + * @return + *	1 - ready + *	0 - not ready + */ +static int nand_dev_ready(struct mtd_info *mtd) +{ +	struct nand_chip *chip = mtd->priv; +	int reg_val; +	struct nand_drv *info; + +	info = (struct nand_drv *)chip->priv; + +	reg_val = readl(&info->reg->status); +	if (reg_val & STATUS_RBSY0) +		return 1; +	else +		return 0; +} + +/* Dummy implementation: we don't support multiple chips */ +static void nand_select_chip(struct mtd_info *mtd, int chipnr) +{ +	switch (chipnr) { +	case -1: +	case 0: +		break; + +	default: +		BUG(); +	} +} + +/** + * Clear all interrupt status bits + * + * @param reg	nand_ctlr structure + */ +static void nand_clear_interrupt_status(struct nand_ctlr *reg) +{ +	u32 reg_val; + +	/* Clear interrupt status */ +	reg_val = readl(®->isr); +	writel(reg_val, ®->isr); +} + +/** + * Send command to NAND device + * + * @param mtd		MTD device structure + * @param command	the command to be sent + * @param column	the column address for this command, -1 if none + * @param page_addr	the page address for this command, -1 if none + */ +static void nand_command(struct mtd_info *mtd, unsigned int command, +	int column, int page_addr) +{ +	struct nand_chip *chip = mtd->priv; +	struct nand_drv *info; + +	info = (struct nand_drv *)chip->priv; + +	/* +	 * Write out the command to the device. +	 * +	 * Only command NAND_CMD_RESET or NAND_CMD_READID will come +	 * here before mtd->writesize is initialized. +	 */ + +	/* Emulate NAND_CMD_READOOB */ +	if (command == NAND_CMD_READOOB) { +		assert(mtd->writesize != 0); +		column += mtd->writesize; +		command = NAND_CMD_READ0; +	} + +	/* Adjust columns for 16 bit bus-width */ +	if (column != -1 && (chip->options & NAND_BUSWIDTH_16)) +		column >>= 1; + +	nand_clear_interrupt_status(info->reg); + +	/* Stop DMA engine, clear DMA completion status */ +	writel(DMA_MST_CTRL_EN_A_DISABLE +		| DMA_MST_CTRL_EN_B_DISABLE +		| DMA_MST_CTRL_IS_DMA_DONE, +		&info->reg->dma_mst_ctrl); + +	/* +	 * Program and erase have their own busy handlers +	 * status and sequential in needs no delay +	 */ +	switch (command) { +	case NAND_CMD_READID: +		writel(NAND_CMD_READID, &info->reg->cmd_reg1); +		writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_PIO +			| CMD_RX | +			((4 - 1) << CMD_TRANS_SIZE_SHIFT) +			| CMD_CE0, +			&info->reg->command); +		info->pio_byte_index = 0; +		break; +	case NAND_CMD_READ0: +		writel(NAND_CMD_READ0, &info->reg->cmd_reg1); +		writel(NAND_CMD_READSTART, &info->reg->cmd_reg2); +		writel((page_addr << 16) | (column & 0xFFFF), +			&info->reg->addr_reg1); +		writel(page_addr >> 16, &info->reg->addr_reg2); +		return; +	case NAND_CMD_SEQIN: +		writel(NAND_CMD_SEQIN, &info->reg->cmd_reg1); +		writel(NAND_CMD_PAGEPROG, &info->reg->cmd_reg2); +		writel((page_addr << 16) | (column & 0xFFFF), +			&info->reg->addr_reg1); +		writel(page_addr >> 16, +			&info->reg->addr_reg2); +		return; +	case NAND_CMD_PAGEPROG: +		return; +	case NAND_CMD_ERASE1: +		writel(NAND_CMD_ERASE1, &info->reg->cmd_reg1); +		writel(NAND_CMD_ERASE2, &info->reg->cmd_reg2); +		writel(page_addr, &info->reg->addr_reg1); +		writel(CMD_GO | CMD_CLE | CMD_ALE | +			CMD_SEC_CMD | CMD_CE0 | CMD_ALE_BYTES3, +			&info->reg->command); +		break; +	case NAND_CMD_ERASE2: +		return; +	case NAND_CMD_STATUS: +		writel(NAND_CMD_STATUS, &info->reg->cmd_reg1); +		writel(CMD_GO | CMD_CLE | CMD_PIO | CMD_RX +			| ((1 - 0) << CMD_TRANS_SIZE_SHIFT) +			| CMD_CE0, +			&info->reg->command); +		info->pio_byte_index = 0; +		break; +	case NAND_CMD_RESET: +		writel(NAND_CMD_RESET, &info->reg->cmd_reg1); +		writel(CMD_GO | CMD_CLE | CMD_CE0, +			&info->reg->command); +		break; +	case NAND_CMD_RNDOUT: +	default: +		printf("%s: Unsupported command %d\n", __func__, command); +		return; +	} +	if (!nand_waitfor_cmd_completion(info->reg)) +		printf("Command 0x%02X timeout\n", command); +} + +/** + * Check whether the pointed buffer are all 0xff (blank). + * + * @param buf	data buffer for blank check + * @param len	length of the buffer in byte + * @return + *	1 - blank + *	0 - non-blank + */ +static int blank_check(u8 *buf, int len) +{ +	int i; + +	for (i = 0; i < len; i++) +		if (buf[i] != 0xFF) +			return 0; +	return 1; +} + +/** + * After a DMA transfer for read, we call this function to see whether there + * is any uncorrectable error on the pointed data buffer or oob buffer. + * + * @param reg		nand_ctlr structure + * @param databuf	data buffer + * @param a_len		data buffer length + * @param oobbuf	oob buffer + * @param b_len		oob buffer length + * @return + *	ECC_OK - no ECC error or correctable ECC error + *	ECC_TAG_ERROR - uncorrectable tag ECC error + *	ECC_DATA_ERROR - uncorrectable data ECC error + *	ECC_DATA_ERROR + ECC_TAG_ERROR - uncorrectable data+tag ECC error + */ +static int check_ecc_error(struct nand_ctlr *reg, u8 *databuf, +	int a_len, u8 *oobbuf, int b_len) +{ +	int return_val = ECC_OK; +	u32 reg_val; + +	if (!(readl(®->isr) & ISR_IS_ECC_ERR)) +		return ECC_OK; + +	/* +	 * Area A is used for the data block (databuf). Area B is used for +	 * the spare block (oobbuf) +	 */ +	reg_val = readl(®->dec_status); +	if ((reg_val & DEC_STATUS_A_ECC_FAIL) && databuf) { +		reg_val = readl(®->bch_dec_status_buf); +		/* +		 * If uncorrectable error occurs on data area, then see whether +		 * they are all FF. If all are FF, it's a blank page. +		 * Not error. +		 */ +		if ((reg_val & BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK) && +				!blank_check(databuf, a_len)) +			return_val |= ECC_DATA_ERROR; +	} + +	if ((reg_val & DEC_STATUS_B_ECC_FAIL) && oobbuf) { +		reg_val = readl(®->bch_dec_status_buf); +		/* +		 * If uncorrectable error occurs on tag area, then see whether +		 * they are all FF. If all are FF, it's a blank page. +		 * Not error. +		 */ +		if ((reg_val & BCH_DEC_STATUS_FAIL_TAG_MASK) && +				!blank_check(oobbuf, b_len)) +			return_val |= ECC_TAG_ERROR; +	} + +	return return_val; +} + +/** + * Set GO bit to send command to device + * + * @param reg	nand_ctlr structure + */ +static void start_command(struct nand_ctlr *reg) +{ +	u32 reg_val; + +	reg_val = readl(®->command); +	reg_val |= CMD_GO; +	writel(reg_val, ®->command); +} + +/** + * Clear command GO bit, DMA GO bit, and DMA completion status + * + * @param reg	nand_ctlr structure + */ +static void stop_command(struct nand_ctlr *reg) +{ +	/* Stop command */ +	writel(0, ®->command); + +	/* Stop DMA engine and clear DMA completion status */ +	writel(DMA_MST_CTRL_GO_DISABLE +		| DMA_MST_CTRL_IS_DMA_DONE, +		®->dma_mst_ctrl); +} + +/** + * Set up NAND bus width and page size + * + * @param info		nand_info structure + * @param *reg_val	address of reg_val + * @return 0 if ok, -1 on error + */ +static int set_bus_width_page_size(struct fdt_nand *config, +	u32 *reg_val) +{ +	if (config->width == 8) +		*reg_val = CFG_BUS_WIDTH_8BIT; +	else if (config->width == 16) +		*reg_val = CFG_BUS_WIDTH_16BIT; +	else { +		debug("%s: Unsupported bus width %d\n", __func__, +		      config->width); +		return -1; +	} + +	if (our_mtd->writesize == 512) +		*reg_val |= CFG_PAGE_SIZE_512; +	else if (our_mtd->writesize == 2048) +		*reg_val |= CFG_PAGE_SIZE_2048; +	else if (our_mtd->writesize == 4096) +		*reg_val |= CFG_PAGE_SIZE_4096; +	else { +		debug("%s: Unsupported page size %d\n", __func__, +		      our_mtd->writesize); +		return -1; +	} + +	return 0; +} + +/** + * Page read/write function + * + * @param mtd		mtd info structure + * @param chip		nand chip info structure + * @param buf		data buffer + * @param page		page number + * @param with_ecc	1 to enable ECC, 0 to disable ECC + * @param is_writing	0 for read, 1 for write + * @return	0 when successfully completed + *		-EIO when command timeout + */ +static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip, +	uint8_t *buf, int page, int with_ecc, int is_writing) +{ +	u32 reg_val; +	int tag_size; +	struct nand_oobfree *free = chip->ecc.layout->oobfree; +	/* 4*128=512 (byte) is the value that our HW can support. */ +	ALLOC_CACHE_ALIGN_BUFFER(u32, tag_buf, 128); +	char *tag_ptr; +	struct nand_drv *info; +	struct fdt_nand *config; + +	if ((uintptr_t)buf & 0x03) { +		printf("buf %p has to be 4-byte aligned\n", buf); +		return -EINVAL; +	} + +	info = (struct nand_drv *)chip->priv; +	config = &info->config; +	if (set_bus_width_page_size(config, ®_val)) +		return -EINVAL; + +	/* Need to be 4-byte aligned */ +	tag_ptr = (char *)tag_buf; + +	stop_command(info->reg); + +	writel((1 << chip->page_shift) - 1, &info->reg->dma_cfg_a); +	writel(virt_to_phys(buf), &info->reg->data_block_ptr); + +	if (with_ecc) { +		writel(virt_to_phys(tag_ptr), &info->reg->tag_ptr); +		if (is_writing) +			memcpy(tag_ptr, chip->oob_poi + free->offset, +				chip->ecc.layout->oobavail + +				TAG_ECC_BYTES); +	} else { +		writel(virt_to_phys(chip->oob_poi), &info->reg->tag_ptr); +	} + +	/* Set ECC selection, configure ECC settings */ +	if (with_ecc) { +		tag_size = chip->ecc.layout->oobavail + TAG_ECC_BYTES; +		reg_val |= (CFG_SKIP_SPARE_SEL_4 +			| CFG_SKIP_SPARE_ENABLE +			| CFG_HW_ECC_CORRECTION_ENABLE +			| CFG_ECC_EN_TAG_DISABLE +			| CFG_HW_ECC_SEL_RS +			| CFG_HW_ECC_ENABLE +			| CFG_TVAL4 +			| (tag_size - 1)); + +		if (!is_writing) +			tag_size += SKIPPED_SPARE_BYTES; +		dma_prepare(tag_ptr, tag_size, is_writing); +	} else { +		tag_size = mtd->oobsize; +		reg_val |= (CFG_SKIP_SPARE_DISABLE +			| CFG_HW_ECC_CORRECTION_DISABLE +			| CFG_ECC_EN_TAG_DISABLE +			| CFG_HW_ECC_DISABLE +			| (tag_size - 1)); +		dma_prepare(chip->oob_poi, tag_size, is_writing); +	} +	writel(reg_val, &info->reg->config); + +	dma_prepare(buf, 1 << chip->page_shift, is_writing); + +	writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config); + +	writel(tag_size - 1, &info->reg->dma_cfg_b); + +	nand_clear_interrupt_status(info->reg); + +	reg_val = CMD_CLE | CMD_ALE +		| CMD_SEC_CMD +		| (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT) +		| CMD_A_VALID +		| CMD_B_VALID +		| (CMD_TRANS_SIZE_PAGE << CMD_TRANS_SIZE_SHIFT) +		| CMD_CE0; +	if (!is_writing) +		reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX); +	else +		reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX); +	writel(reg_val, &info->reg->command); + +	/* Setup DMA engine */ +	reg_val = DMA_MST_CTRL_GO_ENABLE +		| DMA_MST_CTRL_BURST_8WORDS +		| DMA_MST_CTRL_EN_A_ENABLE +		| DMA_MST_CTRL_EN_B_ENABLE; + +	if (!is_writing) +		reg_val |= DMA_MST_CTRL_DIR_READ; +	else +		reg_val |= DMA_MST_CTRL_DIR_WRITE; + +	writel(reg_val, &info->reg->dma_mst_ctrl); + +	start_command(info->reg); + +	if (!nand_waitfor_cmd_completion(info->reg)) { +		if (!is_writing) +			printf("Read Page 0x%X timeout ", page); +		else +			printf("Write Page 0x%X timeout ", page); +		if (with_ecc) +			printf("with ECC"); +		else +			printf("without ECC"); +		printf("\n"); +		return -EIO; +	} + +	if (with_ecc && !is_writing) { +		memcpy(chip->oob_poi, tag_ptr, +			SKIPPED_SPARE_BYTES); +		memcpy(chip->oob_poi + free->offset, +			tag_ptr + SKIPPED_SPARE_BYTES, +			chip->ecc.layout->oobavail); +		reg_val = (u32)check_ecc_error(info->reg, (u8 *)buf, +			1 << chip->page_shift, +			(u8 *)(tag_ptr + SKIPPED_SPARE_BYTES), +			chip->ecc.layout->oobavail); +		if (reg_val & ECC_TAG_ERROR) +			printf("Read Page 0x%X tag ECC error\n", page); +		if (reg_val & ECC_DATA_ERROR) +			printf("Read Page 0x%X data ECC error\n", +				page); +		if (reg_val & (ECC_DATA_ERROR | ECC_TAG_ERROR)) +			return -EIO; +	} +	return 0; +} + +/** + * Hardware ecc based page read function + * + * @param mtd	mtd info structure + * @param chip	nand chip info structure + * @param buf	buffer to store read data + * @param page	page number to read + * @return	0 when successfully completed + *		-EIO when command timeout + */ +static int nand_read_page_hwecc(struct mtd_info *mtd, +	struct nand_chip *chip, uint8_t *buf, int page) +{ +	return nand_rw_page(mtd, chip, buf, page, 1, 0); +} + +/** + * Hardware ecc based page write function + * + * @param mtd	mtd info structure + * @param chip	nand chip info structure + * @param buf	data buffer + */ +static void nand_write_page_hwecc(struct mtd_info *mtd, +	struct nand_chip *chip, const uint8_t *buf) +{ +	int page; +	struct nand_drv *info; + +	info = (struct nand_drv *)chip->priv; + +	page = (readl(&info->reg->addr_reg1) >> 16) | +		(readl(&info->reg->addr_reg2) << 16); + +	nand_rw_page(mtd, chip, (uint8_t *)buf, page, 1, 1); +} + + +/** + * Read raw page data without ecc + * + * @param mtd	mtd info structure + * @param chip	nand chip info structure + * @param buf	buffer to store read data + * @param page	page number to read + * @return	0 when successfully completed + *		-EINVAL when chip->oob_poi is not double-word aligned + *		-EIO when command timeout + */ +static int nand_read_page_raw(struct mtd_info *mtd, +	struct nand_chip *chip, uint8_t *buf, int page) +{ +	return nand_rw_page(mtd, chip, buf, page, 0, 0); +} + +/** + * Raw page write function + * + * @param mtd	mtd info structure + * @param chip	nand chip info structure + * @param buf	data buffer + */ +static void nand_write_page_raw(struct mtd_info *mtd, +		struct nand_chip *chip,	const uint8_t *buf) +{ +	int page; +	struct nand_drv *info; + +	info = (struct nand_drv *)chip->priv; +	page = (readl(&info->reg->addr_reg1) >> 16) | +		(readl(&info->reg->addr_reg2) << 16); + +	nand_rw_page(mtd, chip, (uint8_t *)buf, page, 0, 1); +} + +/** + * OOB data read/write function + * + * @param mtd		mtd info structure + * @param chip		nand chip info structure + * @param page		page number to read + * @param with_ecc	1 to enable ECC, 0 to disable ECC + * @param is_writing	0 for read, 1 for write + * @return	0 when successfully completed + *		-EINVAL when chip->oob_poi is not double-word aligned + *		-EIO when command timeout + */ +static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip, +	int page, int with_ecc, int is_writing) +{ +	u32 reg_val; +	int tag_size; +	struct nand_oobfree *free = chip->ecc.layout->oobfree; +	struct nand_drv *info; + +	if (((int)chip->oob_poi) & 0x03) +		return -EINVAL; +	info = (struct nand_drv *)chip->priv; +	if (set_bus_width_page_size(&info->config, ®_val)) +		return -EINVAL; + +	stop_command(info->reg); + +	writel(virt_to_phys(chip->oob_poi), &info->reg->tag_ptr); + +	/* Set ECC selection */ +	tag_size = mtd->oobsize; +	if (with_ecc) +		reg_val |= CFG_ECC_EN_TAG_ENABLE; +	else +		reg_val |= (CFG_ECC_EN_TAG_DISABLE); + +	reg_val |= ((tag_size - 1) | +		CFG_SKIP_SPARE_DISABLE | +		CFG_HW_ECC_CORRECTION_DISABLE | +		CFG_HW_ECC_DISABLE); +	writel(reg_val, &info->reg->config); + +	dma_prepare(chip->oob_poi, tag_size, is_writing); + +	writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config); + +	if (is_writing && with_ecc) +		tag_size -= TAG_ECC_BYTES; + +	writel(tag_size - 1, &info->reg->dma_cfg_b); + +	nand_clear_interrupt_status(info->reg); + +	reg_val = CMD_CLE | CMD_ALE +		| CMD_SEC_CMD +		| (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT) +		| CMD_B_VALID +		| CMD_CE0; +	if (!is_writing) +		reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX); +	else +		reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX); +	writel(reg_val, &info->reg->command); + +	/* Setup DMA engine */ +	reg_val = DMA_MST_CTRL_GO_ENABLE +		| DMA_MST_CTRL_BURST_8WORDS +		| DMA_MST_CTRL_EN_B_ENABLE; +	if (!is_writing) +		reg_val |= DMA_MST_CTRL_DIR_READ; +	else +		reg_val |= DMA_MST_CTRL_DIR_WRITE; + +	writel(reg_val, &info->reg->dma_mst_ctrl); + +	start_command(info->reg); + +	if (!nand_waitfor_cmd_completion(info->reg)) { +		if (!is_writing) +			printf("Read OOB of Page 0x%X timeout\n", page); +		else +			printf("Write OOB of Page 0x%X timeout\n", page); +		return -EIO; +	} + +	if (with_ecc && !is_writing) { +		reg_val = (u32)check_ecc_error(info->reg, 0, 0, +			(u8 *)(chip->oob_poi + free->offset), +			chip->ecc.layout->oobavail); +		if (reg_val & ECC_TAG_ERROR) +			printf("Read OOB of Page 0x%X tag ECC error\n", page); +	} +	return 0; +} + +/** + * OOB data read function + * + * @param mtd		mtd info structure + * @param chip		nand chip info structure + * @param page		page number to read + * @param sndcmd	flag whether to issue read command or not + * @return	1 - issue read command next time + *		0 - not to issue + */ +static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, +	int page, int sndcmd) +{ +	if (sndcmd) { +		chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); +		sndcmd = 0; +	} +	nand_rw_oob(mtd, chip, page, 0, 0); +	return sndcmd; +} + +/** + * OOB data write function + * + * @param mtd	mtd info structure + * @param chip	nand chip info structure + * @param page	page number to write + * @return	0 when successfully completed + *		-EINVAL when chip->oob_poi is not double-word aligned + *		-EIO when command timeout + */ +static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, +	int page) +{ +	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + +	return nand_rw_oob(mtd, chip, page, 0, 1); +} + +/** + * Set up NAND memory timings according to the provided parameters + * + * @param timing	Timing parameters + * @param reg		NAND controller register address + */ +static void setup_timing(unsigned timing[FDT_NAND_TIMING_COUNT], +			 struct nand_ctlr *reg) +{ +	u32 reg_val, clk_rate, clk_period, time_val; + +	clk_rate = (u32)clock_get_periph_rate(PERIPH_ID_NDFLASH, +		CLOCK_ID_PERIPH) / 1000000; +	clk_period = 1000 / clk_rate; +	reg_val = ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) << +		TIMING_TRP_RESP_CNT_SHIFT) & TIMING_TRP_RESP_CNT_MASK; +	reg_val |= ((timing[FDT_NAND_TWB] / clk_period) << +		TIMING_TWB_CNT_SHIFT) & TIMING_TWB_CNT_MASK; +	time_val = timing[FDT_NAND_MAX_TCR_TAR_TRR] / clk_period; +	if (time_val > 2) +		reg_val |= ((time_val - 2) << TIMING_TCR_TAR_TRR_CNT_SHIFT) & +			TIMING_TCR_TAR_TRR_CNT_MASK; +	reg_val |= ((timing[FDT_NAND_TWHR] / clk_period) << +		TIMING_TWHR_CNT_SHIFT) & TIMING_TWHR_CNT_MASK; +	time_val = timing[FDT_NAND_MAX_TCS_TCH_TALS_TALH] / clk_period; +	if (time_val > 1) +		reg_val |= ((time_val - 1) << TIMING_TCS_CNT_SHIFT) & +			TIMING_TCS_CNT_MASK; +	reg_val |= ((timing[FDT_NAND_TWH] / clk_period) << +		TIMING_TWH_CNT_SHIFT) & TIMING_TWH_CNT_MASK; +	reg_val |= ((timing[FDT_NAND_TWP] / clk_period) << +		TIMING_TWP_CNT_SHIFT) & TIMING_TWP_CNT_MASK; +	reg_val |= ((timing[FDT_NAND_TRH] / clk_period) << +		TIMING_TRH_CNT_SHIFT) & TIMING_TRH_CNT_MASK; +	reg_val |= ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) << +		TIMING_TRP_CNT_SHIFT) & TIMING_TRP_CNT_MASK; +	writel(reg_val, ®->timing); + +	reg_val = 0; +	time_val = timing[FDT_NAND_TADL] / clk_period; +	if (time_val > 2) +		reg_val = (time_val - 2) & TIMING2_TADL_CNT_MASK; +	writel(reg_val, ®->timing2); +} + +/** + * Decode NAND parameters from the device tree + * + * @param blob	Device tree blob + * @param node	Node containing "nand-flash" compatble node + * @return 0 if ok, -ve on error (FDT_ERR_...) + */ +static int fdt_decode_nand(const void *blob, int node, struct fdt_nand *config) +{ +	int err; + +	config->reg = (struct nand_ctlr *)fdtdec_get_addr(blob, node, "reg"); +	config->enabled = fdtdec_get_is_enabled(blob, node); +	config->width = fdtdec_get_int(blob, node, "nvidia,nand-width", 8); +	err = fdtdec_decode_gpio(blob, node, "nvidia,wp-gpios", +				 &config->wp_gpio); +	if (err) +		return err; +	err = fdtdec_get_int_array(blob, node, "nvidia,timing", +			config->timing, FDT_NAND_TIMING_COUNT); +	if (err < 0) +		return err; + +	/* Now look up the controller and decode that */ +	node = fdt_next_node(blob, node, NULL); +	if (node < 0) +		return node; + +	return 0; +} + +/** + * Board-specific NAND initialization + * + * @param nand	nand chip info structure + * @return 0, after initialized, -1 on error + */ +int tegra_nand_init(struct nand_chip *nand, int devnum) +{ +	struct nand_drv *info = &nand_ctrl; +	struct fdt_nand *config = &info->config; +	int node, ret; + +	node = fdtdec_next_compatible(gd->fdt_blob, 0, +				      COMPAT_NVIDIA_TEGRA20_NAND); +	if (node < 0) +		return -1; +	if (fdt_decode_nand(gd->fdt_blob, node, config)) { +		printf("Could not decode nand-flash in device tree\n"); +		return -1; +	} +	if (!config->enabled) +		return -1; +	info->reg = config->reg; +	nand->ecc.mode = NAND_ECC_HW; +	nand->ecc.layout = &eccoob; + +	nand->options = LP_OPTIONS; +	nand->cmdfunc = nand_command; +	nand->read_byte = read_byte; +	nand->ecc.read_page = nand_read_page_hwecc; +	nand->ecc.write_page = nand_write_page_hwecc; +	nand->ecc.read_page_raw = nand_read_page_raw; +	nand->ecc.write_page_raw = nand_write_page_raw; +	nand->ecc.read_oob = nand_read_oob; +	nand->ecc.write_oob = nand_write_oob; +	nand->select_chip = nand_select_chip; +	nand->dev_ready  = nand_dev_ready; +	nand->priv = &nand_ctrl; + +	/* Adjust controller clock rate */ +	clock_start_periph_pll(PERIPH_ID_NDFLASH, CLOCK_ID_PERIPH, 52000000); + +	/* Adjust timing for NAND device */ +	setup_timing(config->timing, info->reg); + +	funcmux_select(PERIPH_ID_NDFLASH, FUNCMUX_DEFAULT); +	fdtdec_setup_gpio(&config->wp_gpio); +	gpio_direction_output(config->wp_gpio.gpio, 1); + +	our_mtd = &nand_info[devnum]; +	our_mtd->priv = nand; +	ret = nand_scan_ident(our_mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL); +	if (ret) +		return ret; + +	nand->ecc.size = our_mtd->writesize; +	nand->ecc.bytes = our_mtd->oobsize; + +	ret = nand_scan_tail(our_mtd); +	if (ret) +		return ret; + +	ret = nand_register(devnum); +	if (ret) +		return ret; + +	return 0; +} + +void board_nand_init(void) +{ +	struct nand_chip *nand = &nand_chip[0]; + +	if (tegra_nand_init(nand, 0)) +		puts("Tegra NAND init failed\n"); +} diff --git a/drivers/mtd/nand/tegra_nand.h b/drivers/mtd/nand/tegra_nand.h new file mode 100644 index 000000000..7e74be75f --- /dev/null +++ b/drivers/mtd/nand/tegra_nand.h @@ -0,0 +1,257 @@ +/* + * (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* register offset */ +#define COMMAND_0		0x00 +#define CMD_GO			(1 << 31) +#define CMD_CLE			(1 << 30) +#define CMD_ALE			(1 << 29) +#define CMD_PIO			(1 << 28) +#define CMD_TX			(1 << 27) +#define CMD_RX			(1 << 26) +#define CMD_SEC_CMD		(1 << 25) +#define CMD_AFT_DAT_MASK	(1 << 24) +#define CMD_AFT_DAT_DISABLE	0 +#define CMD_AFT_DAT_ENABLE	(1 << 24) +#define CMD_TRANS_SIZE_SHIFT	20 +#define CMD_TRANS_SIZE_PAGE	8 +#define CMD_A_VALID		(1 << 19) +#define CMD_B_VALID		(1 << 18) +#define CMD_RD_STATUS_CHK	(1 << 17) +#define CMD_R_BSY_CHK		(1 << 16) +#define CMD_CE7			(1 << 15) +#define CMD_CE6			(1 << 14) +#define CMD_CE5			(1 << 13) +#define CMD_CE4			(1 << 12) +#define CMD_CE3			(1 << 11) +#define CMD_CE2			(1 << 10) +#define CMD_CE1			(1 << 9) +#define CMD_CE0			(1 << 8) +#define CMD_CLE_BYTE_SIZE_SHIFT	4 +enum { +	CMD_CLE_BYTES1 = 0, +	CMD_CLE_BYTES2, +	CMD_CLE_BYTES3, +	CMD_CLE_BYTES4, +}; +#define CMD_ALE_BYTE_SIZE_SHIFT	0 +enum { +	CMD_ALE_BYTES1 = 0, +	CMD_ALE_BYTES2, +	CMD_ALE_BYTES3, +	CMD_ALE_BYTES4, +	CMD_ALE_BYTES5, +	CMD_ALE_BYTES6, +	CMD_ALE_BYTES7, +	CMD_ALE_BYTES8 +}; + +#define STATUS_0			0x04 +#define STATUS_RBSY0			(1 << 8) + +#define ISR_0				0x08 +#define ISR_IS_CMD_DONE			(1 << 5) +#define ISR_IS_ECC_ERR			(1 << 4) + +#define IER_0				0x0C + +#define CFG_0				0x10 +#define CFG_HW_ECC_MASK			(1 << 31) +#define CFG_HW_ECC_DISABLE		0 +#define CFG_HW_ECC_ENABLE		(1 << 31) +#define CFG_HW_ECC_SEL_MASK		(1 << 30) +#define CFG_HW_ECC_SEL_HAMMING		0 +#define CFG_HW_ECC_SEL_RS		(1 << 30) +#define CFG_HW_ECC_CORRECTION_MASK	(1 << 29) +#define CFG_HW_ECC_CORRECTION_DISABLE	0 +#define CFG_HW_ECC_CORRECTION_ENABLE	(1 << 29) +#define CFG_PIPELINE_EN_MASK		(1 << 28) +#define CFG_PIPELINE_EN_DISABLE		0 +#define CFG_PIPELINE_EN_ENABLE		(1 << 28) +#define CFG_ECC_EN_TAG_MASK		(1 << 27) +#define CFG_ECC_EN_TAG_DISABLE		0 +#define CFG_ECC_EN_TAG_ENABLE		(1 << 27) +#define CFG_TVALUE_MASK			(3 << 24) +enum { +	CFG_TVAL4 = 0 << 24, +	CFG_TVAL6 = 1 << 24, +	CFG_TVAL8 = 2 << 24 +}; +#define CFG_SKIP_SPARE_MASK		(1 << 23) +#define CFG_SKIP_SPARE_DISABLE		0 +#define CFG_SKIP_SPARE_ENABLE		(1 << 23) +#define CFG_COM_BSY_MASK		(1 << 22) +#define CFG_COM_BSY_DISABLE		0 +#define CFG_COM_BSY_ENABLE		(1 << 22) +#define CFG_BUS_WIDTH_MASK		(1 << 21) +#define CFG_BUS_WIDTH_8BIT		0 +#define CFG_BUS_WIDTH_16BIT		(1 << 21) +#define CFG_LPDDR1_MODE_MASK		(1 << 20) +#define CFG_LPDDR1_MODE_DISABLE		0 +#define CFG_LPDDR1_MODE_ENABLE		(1 << 20) +#define CFG_EDO_MODE_MASK		(1 << 19) +#define CFG_EDO_MODE_DISABLE		0 +#define CFG_EDO_MODE_ENABLE		(1 << 19) +#define CFG_PAGE_SIZE_SEL_MASK		(7 << 16) +enum { +	CFG_PAGE_SIZE_256	= 0 << 16, +	CFG_PAGE_SIZE_512	= 1 << 16, +	CFG_PAGE_SIZE_1024	= 2 << 16, +	CFG_PAGE_SIZE_2048	= 3 << 16, +	CFG_PAGE_SIZE_4096	= 4 << 16 +}; +#define CFG_SKIP_SPARE_SEL_MASK		(3 << 14) +enum { +	CFG_SKIP_SPARE_SEL_4	= 0 << 14, +	CFG_SKIP_SPARE_SEL_8	= 1 << 14, +	CFG_SKIP_SPARE_SEL_12	= 2 << 14, +	CFG_SKIP_SPARE_SEL_16	= 3 << 14 +}; +#define CFG_TAG_BYTE_SIZE_MASK	0x1FF + +#define TIMING_0			0x14 +#define TIMING_TRP_RESP_CNT_SHIFT	28 +#define TIMING_TRP_RESP_CNT_MASK	(0xf << TIMING_TRP_RESP_CNT_SHIFT) +#define TIMING_TWB_CNT_SHIFT		24 +#define TIMING_TWB_CNT_MASK		(0xf << TIMING_TWB_CNT_SHIFT) +#define TIMING_TCR_TAR_TRR_CNT_SHIFT	20 +#define TIMING_TCR_TAR_TRR_CNT_MASK	(0xf << TIMING_TCR_TAR_TRR_CNT_SHIFT) +#define TIMING_TWHR_CNT_SHIFT		16 +#define TIMING_TWHR_CNT_MASK		(0xf << TIMING_TWHR_CNT_SHIFT) +#define TIMING_TCS_CNT_SHIFT		14 +#define TIMING_TCS_CNT_MASK		(3 << TIMING_TCS_CNT_SHIFT) +#define TIMING_TWH_CNT_SHIFT		12 +#define TIMING_TWH_CNT_MASK		(3 << TIMING_TWH_CNT_SHIFT) +#define TIMING_TWP_CNT_SHIFT		8 +#define TIMING_TWP_CNT_MASK		(0xf << TIMING_TWP_CNT_SHIFT) +#define TIMING_TRH_CNT_SHIFT		4 +#define TIMING_TRH_CNT_MASK		(3 << TIMING_TRH_CNT_SHIFT) +#define TIMING_TRP_CNT_SHIFT		0 +#define TIMING_TRP_CNT_MASK		(0xf << TIMING_TRP_CNT_SHIFT) + +#define RESP_0				0x18 + +#define TIMING2_0			0x1C +#define TIMING2_TADL_CNT_SHIFT		0 +#define TIMING2_TADL_CNT_MASK		(0xf << TIMING2_TADL_CNT_SHIFT) + +#define CMD_REG1_0			0x20 +#define CMD_REG2_0			0x24 +#define ADDR_REG1_0			0x28 +#define ADDR_REG2_0			0x2C + +#define DMA_MST_CTRL_0			0x30 +#define DMA_MST_CTRL_GO_MASK		(1 << 31) +#define DMA_MST_CTRL_GO_DISABLE		0 +#define DMA_MST_CTRL_GO_ENABLE		(1 << 31) +#define DMA_MST_CTRL_DIR_MASK		(1 << 30) +#define DMA_MST_CTRL_DIR_READ		0 +#define DMA_MST_CTRL_DIR_WRITE		(1 << 30) +#define DMA_MST_CTRL_PERF_EN_MASK	(1 << 29) +#define DMA_MST_CTRL_PERF_EN_DISABLE	0 +#define DMA_MST_CTRL_PERF_EN_ENABLE	(1 << 29) +#define DMA_MST_CTRL_REUSE_BUFFER_MASK	(1 << 27) +#define DMA_MST_CTRL_REUSE_BUFFER_DISABLE	0 +#define DMA_MST_CTRL_REUSE_BUFFER_ENABLE	(1 << 27) +#define DMA_MST_CTRL_BURST_SIZE_SHIFT	24 +#define DMA_MST_CTRL_BURST_SIZE_MASK	(7 << DMA_MST_CTRL_BURST_SIZE_SHIFT) +enum { +	DMA_MST_CTRL_BURST_1WORDS	= 2 << DMA_MST_CTRL_BURST_SIZE_SHIFT, +	DMA_MST_CTRL_BURST_4WORDS	= 3 << DMA_MST_CTRL_BURST_SIZE_SHIFT, +	DMA_MST_CTRL_BURST_8WORDS	= 4 << DMA_MST_CTRL_BURST_SIZE_SHIFT, +	DMA_MST_CTRL_BURST_16WORDS	= 5 << DMA_MST_CTRL_BURST_SIZE_SHIFT +}; +#define DMA_MST_CTRL_IS_DMA_DONE	(1 << 20) +#define DMA_MST_CTRL_EN_A_MASK		(1 << 2) +#define DMA_MST_CTRL_EN_A_DISABLE	0 +#define DMA_MST_CTRL_EN_A_ENABLE	(1 << 2) +#define DMA_MST_CTRL_EN_B_MASK		(1 << 1) +#define DMA_MST_CTRL_EN_B_DISABLE	0 +#define DMA_MST_CTRL_EN_B_ENABLE	(1 << 1) + +#define DMA_CFG_A_0			0x34 +#define DMA_CFG_B_0			0x38 +#define FIFO_CTRL_0			0x3C +#define DATA_BLOCK_PTR_0		0x40 +#define TAG_PTR_0			0x44 +#define ECC_PTR_0			0x48 + +#define DEC_STATUS_0			0x4C +#define DEC_STATUS_A_ECC_FAIL		(1 << 1) +#define DEC_STATUS_B_ECC_FAIL		(1 << 0) + +#define BCH_CONFIG_0			0xCC +#define BCH_CONFIG_BCH_TVALUE_SHIFT	4 +#define BCH_CONFIG_BCH_TVALUE_MASK	(3 << BCH_CONFIG_BCH_TVALUE_SHIFT) +enum { +	BCH_CONFIG_BCH_TVAL4	= 0 << BCH_CONFIG_BCH_TVALUE_SHIFT, +	BCH_CONFIG_BCH_TVAL8	= 1 << BCH_CONFIG_BCH_TVALUE_SHIFT, +	BCH_CONFIG_BCH_TVAL14	= 2 << BCH_CONFIG_BCH_TVALUE_SHIFT, +	BCH_CONFIG_BCH_TVAL16	= 3 << BCH_CONFIG_BCH_TVALUE_SHIFT +}; +#define BCH_CONFIG_BCH_ECC_MASK		(1 << 0) +#define BCH_CONFIG_BCH_ECC_DISABLE	0 +#define BCH_CONFIG_BCH_ECC_ENABLE	(1 << 0) + +#define BCH_DEC_RESULT_0			0xD0 +#define BCH_DEC_RESULT_CORRFAIL_ERR_MASK	(1 << 8) +#define BCH_DEC_RESULT_PAGE_COUNT_MASK		0xFF + +#define BCH_DEC_STATUS_BUF_0			0xD4 +#define BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK	0xFF000000 +#define BCH_DEC_STATUS_CORR_SEC_FLAG_MASK	0x00FF0000 +#define BCH_DEC_STATUS_FAIL_TAG_MASK		(1 << 14) +#define BCH_DEC_STATUS_CORR_TAG_MASK		(1 << 13) +#define BCH_DEC_STATUS_MAX_CORR_CNT_MASK	(0x1f << 8) +#define BCH_DEC_STATUS_PAGE_NUMBER_MASK		0xFF + +#define LP_OPTIONS (NAND_NO_READRDY | NAND_NO_AUTOINCR) + +struct nand_ctlr { +	u32	command;	/* offset 00h */ +	u32	status;		/* offset 04h */ +	u32	isr;		/* offset 08h */ +	u32	ier;		/* offset 0Ch */ +	u32	config;		/* offset 10h */ +	u32	timing;		/* offset 14h */ +	u32	resp;		/* offset 18h */ +	u32	timing2;	/* offset 1Ch */ +	u32	cmd_reg1;	/* offset 20h */ +	u32	cmd_reg2;	/* offset 24h */ +	u32	addr_reg1;	/* offset 28h */ +	u32	addr_reg2;	/* offset 2Ch */ +	u32	dma_mst_ctrl;	/* offset 30h */ +	u32	dma_cfg_a;	/* offset 34h */ +	u32	dma_cfg_b;	/* offset 38h */ +	u32	fifo_ctrl;	/* offset 3Ch */ +	u32	data_block_ptr;	/* offset 40h */ +	u32	tag_ptr;	/* offset 44h */ +	u32	resv1;		/* offset 48h */ +	u32	dec_status;	/* offset 4Ch */ +	u32	hwstatus_cmd;	/* offset 50h */ +	u32	hwstatus_mask;	/* offset 54h */ +	u32	resv2[29]; +	u32	bch_config;	/* offset CCh */ +	u32	bch_dec_result;	/* offset D0h */ +	u32	bch_dec_status_buf; +				/* offset D4h */ +}; diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c index 9a114ac6a..32b76e0e9 100644 --- a/drivers/mtd/spi/spansion.c +++ b/drivers/mtd/spi/spansion.c @@ -96,6 +96,13 @@ static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {  		.nr_sectors = 256,  		.name = "S25FL129P_64K",  	}, +	{ +		.idcode1 = 0x2019, +		.idcode2 = 0x4d01, +		.pages_per_sector = 256, +		.nr_sectors = 512, +		.name = "S25FL256S", +	},  };  struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode) diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c index dbd1fc185..30b626a39 100644 --- a/drivers/mtd/spi/stmicro.c +++ b/drivers/mtd/spi/stmicro.c @@ -37,7 +37,7 @@  #define CMD_M25PXX_RES		0xab	/* Release from DP, and Read Signature */  struct stmicro_spi_flash_params { -	u8 idcode1; +	u16 id;  	u16 pages_per_sector;  	u16 nr_sectors;  	const char *name; @@ -45,55 +45,67 @@ struct stmicro_spi_flash_params {  static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = {  	{ -		.idcode1 = 0x11, +		.id = 0x2011,  		.pages_per_sector = 128,  		.nr_sectors = 4,  		.name = "M25P10",  	},  	{ -		.idcode1 = 0x15, +		.id = 0x2015,  		.pages_per_sector = 256,  		.nr_sectors = 32,  		.name = "M25P16",  	},  	{ -		.idcode1 = 0x12, +		.id = 0x2012,  		.pages_per_sector = 256,  		.nr_sectors = 4,  		.name = "M25P20",  	},  	{ -		.idcode1 = 0x16, +		.id = 0x2016,  		.pages_per_sector = 256,  		.nr_sectors = 64,  		.name = "M25P32",  	},  	{ -		.idcode1 = 0x13, +		.id = 0x2013,  		.pages_per_sector = 256,  		.nr_sectors = 8,  		.name = "M25P40",  	},  	{ -		.idcode1 = 0x17, +		.id = 0x2017,  		.pages_per_sector = 256,  		.nr_sectors = 128,  		.name = "M25P64",  	},  	{ -		.idcode1 = 0x14, +		.id = 0x2014,  		.pages_per_sector = 256,  		.nr_sectors = 16,  		.name = "M25P80",  	},  	{ -		.idcode1 = 0x18, +		.id = 0x2018,  		.pages_per_sector = 1024,  		.nr_sectors = 64,  		.name = "M25P128",  	},  	{ -		.idcode1 = 0x19, +		.id = 0xba18, +		.pages_per_sector = 256, +		.nr_sectors = 256, +		.name = "N25Q128", +	}, +	{ +		.id = 0xbb18, +		.pages_per_sector = 256, +		.nr_sectors = 256, +		.name = "N25Q128A", +	}, +	{ +		.id = 0xba19,  		.pages_per_sector = 256,  		.nr_sectors = 512,  		.name = "N25Q256", @@ -105,6 +117,7 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)  	const struct stmicro_spi_flash_params *params;  	struct spi_flash *flash;  	unsigned int i; +	u16 id;  	if (idcode[0] == 0xff) {  		i = spi_flash_cmd(spi, CMD_M25PXX_RES, @@ -119,15 +132,17 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)  			return NULL;  	} +	id = ((idcode[1] << 8) | idcode[2]); +  	for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {  		params = &stmicro_spi_flash_table[i]; -		if (params->idcode1 == idcode[2]) { +		if (params->id == id) {  			break;  		}  	}  	if (i == ARRAY_SIZE(stmicro_spi_flash_table)) { -		debug("SF: Unsupported STMicro ID %02x\n", idcode[1]); +		debug("SF: Unsupported STMicro ID %04x\n", id);  		return NULL;  	} diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c index 427b71fcd..f6aab3d32 100644 --- a/drivers/mtd/spi/winbond.c +++ b/drivers/mtd/spi/winbond.c @@ -62,6 +62,11 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {  		.nr_blocks		= 256,  		.name			= "W25Q128",  	}, +	{ +		.id			= 0x5014, +		.nr_blocks		= 128, +		.name			= "W25Q80", +	},  };  struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode) @@ -94,7 +99,7 @@ struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)  	flash->write = spi_flash_cmd_write_multi;  	flash->erase = spi_flash_cmd_erase;  	flash->read = spi_flash_cmd_read_fast; -	flash->page_size = 4096; +	flash->page_size = 256;  	flash->sector_size = 4096;  	flash->size = 4096 * 16 * params->nr_blocks; diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 430f90cea..011cd5191 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -71,6 +71,7 @@ COBJS-$(CONFIG_SMC91111) += smc91111.o  COBJS-$(CONFIG_SMC911X) += smc911x.o  COBJS-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o  COBJS-$(CONFIG_TSEC_ENET) += tsec.o fsl_mdio.o +COBJS-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o  COBJS-$(CONFIG_FMAN_ENET) += fsl_mdio.o  COBJS-$(CONFIG_TSI108_ETH) += tsi108_eth.o  COBJS-$(CONFIG_ULI526X) += uli526x.o diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c new file mode 100644 index 000000000..af3d8593e --- /dev/null +++ b/drivers/net/cpsw.c @@ -0,0 +1,991 @@ +/* + * CPSW Ethernet Switch Driver + * + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include <miiphy.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <cpsw.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <phy.h> + +#define BITMASK(bits)		(BIT(bits) - 1) +#define PHY_REG_MASK		0x1f +#define PHY_ID_MASK		0x1f +#define NUM_DESCS		(PKTBUFSRX * 2) +#define PKT_MIN			60 +#define PKT_MAX			(1500 + 14 + 4 + 4) +#define CLEAR_BIT		1 +#define GIGABITEN		BIT(7) +#define FULLDUPLEXEN		BIT(0) +#define MIIEN			BIT(15) + +/* DMA Registers */ +#define CPDMA_TXCONTROL		0x004 +#define CPDMA_RXCONTROL		0x014 +#define CPDMA_SOFTRESET		0x01c +#define CPDMA_RXFREE		0x0e0 +#define CPDMA_TXHDP_VER1	0x100 +#define CPDMA_TXHDP_VER2	0x200 +#define CPDMA_RXHDP_VER1	0x120 +#define CPDMA_RXHDP_VER2	0x220 +#define CPDMA_TXCP_VER1		0x140 +#define CPDMA_TXCP_VER2		0x240 +#define CPDMA_RXCP_VER1		0x160 +#define CPDMA_RXCP_VER2		0x260 + +#define CPDMA_RAM_ADDR		0x4a102000 + +/* Descriptor mode bits */ +#define CPDMA_DESC_SOP		BIT(31) +#define CPDMA_DESC_EOP		BIT(30) +#define CPDMA_DESC_OWNER	BIT(29) +#define CPDMA_DESC_EOQ		BIT(28) + +/* + * This timeout definition is a worst-case ultra defensive measure against + * unexpected controller lock ups.  Ideally, we should never ever hit this + * scenario in practice. + */ +#define MDIO_TIMEOUT            100 /* msecs */ +#define CPDMA_TIMEOUT		100 /* msecs */ + +struct cpsw_mdio_regs { +	u32	version; +	u32	control; +#define CONTROL_IDLE		BIT(31) +#define CONTROL_ENABLE		BIT(30) + +	u32	alive; +	u32	link; +	u32	linkintraw; +	u32	linkintmasked; +	u32	__reserved_0[2]; +	u32	userintraw; +	u32	userintmasked; +	u32	userintmaskset; +	u32	userintmaskclr; +	u32	__reserved_1[20]; + +	struct { +		u32		access; +		u32		physel; +#define USERACCESS_GO		BIT(31) +#define USERACCESS_WRITE	BIT(30) +#define USERACCESS_ACK		BIT(29) +#define USERACCESS_READ		(0) +#define USERACCESS_DATA		(0xffff) +	} user[0]; +}; + +struct cpsw_regs { +	u32	id_ver; +	u32	control; +	u32	soft_reset; +	u32	stat_port_en; +	u32	ptype; +}; + +struct cpsw_slave_regs { +	u32	max_blks; +	u32	blk_cnt; +	u32	flow_thresh; +	u32	port_vlan; +	u32	tx_pri_map; +	u32	gap_thresh; +	u32	sa_lo; +	u32	sa_hi; +}; + +struct cpsw_host_regs { +	u32	max_blks; +	u32	blk_cnt; +	u32	flow_thresh; +	u32	port_vlan; +	u32	tx_pri_map; +	u32	cpdma_tx_pri_map; +	u32	cpdma_rx_chan_map; +}; + +struct cpsw_sliver_regs { +	u32	id_ver; +	u32	mac_control; +	u32	mac_status; +	u32	soft_reset; +	u32	rx_maxlen; +	u32	__reserved_0; +	u32	rx_pause; +	u32	tx_pause; +	u32	__reserved_1; +	u32	rx_pri_map; +}; + +#define ALE_ENTRY_BITS		68 +#define ALE_ENTRY_WORDS		DIV_ROUND_UP(ALE_ENTRY_BITS, 32) + +/* ALE Registers */ +#define ALE_CONTROL		0x08 +#define ALE_UNKNOWNVLAN		0x18 +#define ALE_TABLE_CONTROL	0x20 +#define ALE_TABLE		0x34 +#define ALE_PORTCTL		0x40 + +#define ALE_TABLE_WRITE		BIT(31) + +#define ALE_TYPE_FREE			0 +#define ALE_TYPE_ADDR			1 +#define ALE_TYPE_VLAN			2 +#define ALE_TYPE_VLAN_ADDR		3 + +#define ALE_UCAST_PERSISTANT		0 +#define ALE_UCAST_UNTOUCHED		1 +#define ALE_UCAST_OUI			2 +#define ALE_UCAST_TOUCHED		3 + +#define ALE_MCAST_FWD			0 +#define ALE_MCAST_BLOCK_LEARN_FWD	1 +#define ALE_MCAST_FWD_LEARN		2 +#define ALE_MCAST_FWD_2			3 + +enum cpsw_ale_port_state { +	ALE_PORT_STATE_DISABLE	= 0x00, +	ALE_PORT_STATE_BLOCK	= 0x01, +	ALE_PORT_STATE_LEARN	= 0x02, +	ALE_PORT_STATE_FORWARD	= 0x03, +}; + +/* ALE unicast entry flags - passed into cpsw_ale_add_ucast() */ +#define ALE_SECURE	1 +#define ALE_BLOCKED	2 + +struct cpsw_slave { +	struct cpsw_slave_regs		*regs; +	struct cpsw_sliver_regs		*sliver; +	int				slave_num; +	u32				mac_control; +	struct cpsw_slave_data		*data; +}; + +struct cpdma_desc { +	/* hardware fields */ +	u32			hw_next; +	u32			hw_buffer; +	u32			hw_len; +	u32			hw_mode; +	/* software fields */ +	u32			sw_buffer; +	u32			sw_len; +}; + +struct cpdma_chan { +	struct cpdma_desc	*head, *tail; +	void			*hdp, *cp, *rxfree; +}; + +#define desc_write(desc, fld, val)	__raw_writel((u32)(val), &(desc)->fld) +#define desc_read(desc, fld)		__raw_readl(&(desc)->fld) +#define desc_read_ptr(desc, fld)	((void *)__raw_readl(&(desc)->fld)) + +#define chan_write(chan, fld, val)	__raw_writel((u32)(val), (chan)->fld) +#define chan_read(chan, fld)		__raw_readl((chan)->fld) +#define chan_read_ptr(chan, fld)	((void *)__raw_readl((chan)->fld)) + +#define for_each_slave(slave, priv) \ +	for (slave = (priv)->slaves; slave != (priv)->slaves + \ +				(priv)->data.slaves; slave++) + +struct cpsw_priv { +	struct eth_device		*dev; +	struct cpsw_platform_data	data; +	int				host_port; + +	struct cpsw_regs		*regs; +	void				*dma_regs; +	struct cpsw_host_regs		*host_port_regs; +	void				*ale_regs; + +	struct cpdma_desc		*descs; +	struct cpdma_desc		*desc_free; +	struct cpdma_chan		rx_chan, tx_chan; + +	struct cpsw_slave		*slaves; +	struct phy_device		*phydev; +	struct mii_dev			*bus; +}; + +static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits) +{ +	int idx; + +	idx    = start / 32; +	start -= idx * 32; +	idx    = 2 - idx; /* flip */ +	return (ale_entry[idx] >> start) & BITMASK(bits); +} + +static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32 bits, +				      u32 value) +{ +	int idx; + +	value &= BITMASK(bits); +	idx    = start / 32; +	start -= idx * 32; +	idx    = 2 - idx; /* flip */ +	ale_entry[idx] &= ~(BITMASK(bits) << start); +	ale_entry[idx] |=  (value << start); +} + +#define DEFINE_ALE_FIELD(name, start, bits)				\ +static inline int cpsw_ale_get_##name(u32 *ale_entry)			\ +{									\ +	return cpsw_ale_get_field(ale_entry, start, bits);		\ +}									\ +static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value)	\ +{									\ +	cpsw_ale_set_field(ale_entry, start, bits, value);		\ +} + +DEFINE_ALE_FIELD(entry_type,		60,	2) +DEFINE_ALE_FIELD(mcast_state,		62,	2) +DEFINE_ALE_FIELD(port_mask,		66,	3) +DEFINE_ALE_FIELD(ucast_type,		62,	2) +DEFINE_ALE_FIELD(port_num,		66,	2) +DEFINE_ALE_FIELD(blocked,		65,	1) +DEFINE_ALE_FIELD(secure,		64,	1) +DEFINE_ALE_FIELD(mcast,			40,	1) + +/* The MAC address field in the ALE entry cannot be macroized as above */ +static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr) +{ +	int i; + +	for (i = 0; i < 6; i++) +		addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8); +} + +static inline void cpsw_ale_set_addr(u32 *ale_entry, u8 *addr) +{ +	int i; + +	for (i = 0; i < 6; i++) +		cpsw_ale_set_field(ale_entry, 40 - 8*i, 8, addr[i]); +} + +static int cpsw_ale_read(struct cpsw_priv *priv, int idx, u32 *ale_entry) +{ +	int i; + +	__raw_writel(idx, priv->ale_regs + ALE_TABLE_CONTROL); + +	for (i = 0; i < ALE_ENTRY_WORDS; i++) +		ale_entry[i] = __raw_readl(priv->ale_regs + ALE_TABLE + 4 * i); + +	return idx; +} + +static int cpsw_ale_write(struct cpsw_priv *priv, int idx, u32 *ale_entry) +{ +	int i; + +	for (i = 0; i < ALE_ENTRY_WORDS; i++) +		__raw_writel(ale_entry[i], priv->ale_regs + ALE_TABLE + 4 * i); + +	__raw_writel(idx | ALE_TABLE_WRITE, priv->ale_regs + ALE_TABLE_CONTROL); + +	return idx; +} + +static int cpsw_ale_match_addr(struct cpsw_priv *priv, u8* addr) +{ +	u32 ale_entry[ALE_ENTRY_WORDS]; +	int type, idx; + +	for (idx = 0; idx < priv->data.ale_entries; idx++) { +		u8 entry_addr[6]; + +		cpsw_ale_read(priv, idx, ale_entry); +		type = cpsw_ale_get_entry_type(ale_entry); +		if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR) +			continue; +		cpsw_ale_get_addr(ale_entry, entry_addr); +		if (memcmp(entry_addr, addr, 6) == 0) +			return idx; +	} +	return -ENOENT; +} + +static int cpsw_ale_match_free(struct cpsw_priv *priv) +{ +	u32 ale_entry[ALE_ENTRY_WORDS]; +	int type, idx; + +	for (idx = 0; idx < priv->data.ale_entries; idx++) { +		cpsw_ale_read(priv, idx, ale_entry); +		type = cpsw_ale_get_entry_type(ale_entry); +		if (type == ALE_TYPE_FREE) +			return idx; +	} +	return -ENOENT; +} + +static int cpsw_ale_find_ageable(struct cpsw_priv *priv) +{ +	u32 ale_entry[ALE_ENTRY_WORDS]; +	int type, idx; + +	for (idx = 0; idx < priv->data.ale_entries; idx++) { +		cpsw_ale_read(priv, idx, ale_entry); +		type = cpsw_ale_get_entry_type(ale_entry); +		if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR) +			continue; +		if (cpsw_ale_get_mcast(ale_entry)) +			continue; +		type = cpsw_ale_get_ucast_type(ale_entry); +		if (type != ALE_UCAST_PERSISTANT && +		    type != ALE_UCAST_OUI) +			return idx; +	} +	return -ENOENT; +} + +static int cpsw_ale_add_ucast(struct cpsw_priv *priv, u8 *addr, +			      int port, int flags) +{ +	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; +	int idx; + +	cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); +	cpsw_ale_set_addr(ale_entry, addr); +	cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT); +	cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0); +	cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0); +	cpsw_ale_set_port_num(ale_entry, port); + +	idx = cpsw_ale_match_addr(priv, addr); +	if (idx < 0) +		idx = cpsw_ale_match_free(priv); +	if (idx < 0) +		idx = cpsw_ale_find_ageable(priv); +	if (idx < 0) +		return -ENOMEM; + +	cpsw_ale_write(priv, idx, ale_entry); +	return 0; +} + +static int cpsw_ale_add_mcast(struct cpsw_priv *priv, u8 *addr, int port_mask) +{ +	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; +	int idx, mask; + +	idx = cpsw_ale_match_addr(priv, addr); +	if (idx >= 0) +		cpsw_ale_read(priv, idx, ale_entry); + +	cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); +	cpsw_ale_set_addr(ale_entry, addr); +	cpsw_ale_set_mcast_state(ale_entry, ALE_MCAST_FWD_2); + +	mask = cpsw_ale_get_port_mask(ale_entry); +	port_mask |= mask; +	cpsw_ale_set_port_mask(ale_entry, port_mask); + +	if (idx < 0) +		idx = cpsw_ale_match_free(priv); +	if (idx < 0) +		idx = cpsw_ale_find_ageable(priv); +	if (idx < 0) +		return -ENOMEM; + +	cpsw_ale_write(priv, idx, ale_entry); +	return 0; +} + +static inline void cpsw_ale_control(struct cpsw_priv *priv, int bit, int val) +{ +	u32 tmp, mask = BIT(bit); + +	tmp  = __raw_readl(priv->ale_regs + ALE_CONTROL); +	tmp &= ~mask; +	tmp |= val ? mask : 0; +	__raw_writel(tmp, priv->ale_regs + ALE_CONTROL); +} + +#define cpsw_ale_enable(priv, val)	cpsw_ale_control(priv, 31, val) +#define cpsw_ale_clear(priv, val)	cpsw_ale_control(priv, 30, val) +#define cpsw_ale_vlan_aware(priv, val)	cpsw_ale_control(priv,  2, val) + +static inline void cpsw_ale_port_state(struct cpsw_priv *priv, int port, +				       int val) +{ +	int offset = ALE_PORTCTL + 4 * port; +	u32 tmp, mask = 0x3; + +	tmp  = __raw_readl(priv->ale_regs + offset); +	tmp &= ~mask; +	tmp |= val & mask; +	__raw_writel(tmp, priv->ale_regs + offset); +} + +static struct cpsw_mdio_regs *mdio_regs; + +/* wait until hardware is ready for another user access */ +static inline u32 wait_for_user_access(void) +{ +	u32 reg = 0; +	int timeout = MDIO_TIMEOUT; + +	while (timeout-- && +	((reg = __raw_readl(&mdio_regs->user[0].access)) & USERACCESS_GO)) +		udelay(10); + +	if (timeout == -1) { +		printf("wait_for_user_access Timeout\n"); +		return -ETIMEDOUT; +	} +	return reg; +} + +/* wait until hardware state machine is idle */ +static inline void wait_for_idle(void) +{ +	int timeout = MDIO_TIMEOUT; + +	while (timeout-- && +		((__raw_readl(&mdio_regs->control) & CONTROL_IDLE) == 0)) +		udelay(10); + +	if (timeout == -1) +		printf("wait_for_idle Timeout\n"); +} + +static int cpsw_mdio_read(struct mii_dev *bus, int phy_id, +				int dev_addr, int phy_reg) +{ +	unsigned short data; +	u32 reg; + +	if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK) +		return -EINVAL; + +	wait_for_user_access(); +	reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) | +	       (phy_id << 16)); +	__raw_writel(reg, &mdio_regs->user[0].access); +	reg = wait_for_user_access(); + +	data = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1; +	return data; +} + +static int cpsw_mdio_write(struct mii_dev *bus, int phy_id, int dev_addr, +				int phy_reg, u16 data) +{ +	u32 reg; + +	if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK) +		return -EINVAL; + +	wait_for_user_access(); +	reg = (USERACCESS_GO | USERACCESS_WRITE | (phy_reg << 21) | +		   (phy_id << 16) | (data & USERACCESS_DATA)); +	__raw_writel(reg, &mdio_regs->user[0].access); +	wait_for_user_access(); + +	return 0; +} + +static void cpsw_mdio_init(char *name, u32 mdio_base, u32 div) +{ +	struct mii_dev *bus = mdio_alloc(); + +	mdio_regs = (struct cpsw_mdio_regs *)mdio_base; + +	/* set enable and clock divider */ +	__raw_writel(div | CONTROL_ENABLE, &mdio_regs->control); + +	/* +	 * wait for scan logic to settle: +	 * the scan time consists of (a) a large fixed component, and (b) a +	 * small component that varies with the mii bus frequency.  These +	 * were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x +	 * silicon.  Since the effect of (b) was found to be largely +	 * negligible, we keep things simple here. +	 */ +	udelay(1000); + +	bus->read = cpsw_mdio_read; +	bus->write = cpsw_mdio_write; +	sprintf(bus->name, name); + +	mdio_register(bus); +} + +/* Set a self-clearing bit in a register, and wait for it to clear */ +static inline void setbit_and_wait_for_clear32(void *addr) +{ +	__raw_writel(CLEAR_BIT, addr); +	while (__raw_readl(addr) & CLEAR_BIT) +		; +} + +#define mac_hi(mac)	(((mac)[0] << 0) | ((mac)[1] << 8) |	\ +			 ((mac)[2] << 16) | ((mac)[3] << 24)) +#define mac_lo(mac)	(((mac)[4] << 0) | ((mac)[5] << 8)) + +static void cpsw_set_slave_mac(struct cpsw_slave *slave, +			       struct cpsw_priv *priv) +{ +	__raw_writel(mac_hi(priv->dev->enetaddr), &slave->regs->sa_hi); +	__raw_writel(mac_lo(priv->dev->enetaddr), &slave->regs->sa_lo); +} + +static void cpsw_slave_update_link(struct cpsw_slave *slave, +				   struct cpsw_priv *priv, int *link) +{ +	struct phy_device *phy = priv->phydev; +	u32 mac_control = 0; + +	phy_startup(phy); +	*link = phy->link; + +	if (*link) { /* link up */ +		mac_control = priv->data.mac_control; +		if (phy->speed == 1000) +			mac_control |= GIGABITEN; +		if (phy->duplex == DUPLEX_FULL) +			mac_control |= FULLDUPLEXEN; +		if (phy->speed == 100) +			mac_control |= MIIEN; +	} + +	if (mac_control == slave->mac_control) +		return; + +	if (mac_control) { +		printf("link up on port %d, speed %d, %s duplex\n", +				slave->slave_num, phy->speed, +				(phy->duplex == DUPLEX_FULL) ? "full" : "half"); +	} else { +		printf("link down on port %d\n", slave->slave_num); +	} + +	__raw_writel(mac_control, &slave->sliver->mac_control); +	slave->mac_control = mac_control; +} + +static int cpsw_update_link(struct cpsw_priv *priv) +{ +	int link = 0; +	struct cpsw_slave *slave; + +	for_each_slave(slave, priv) +		cpsw_slave_update_link(slave, priv, &link); + +	return link; +} + +static inline u32  cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) +{ +	if (priv->host_port == 0) +		return slave_num + 1; +	else +		return slave_num; +} + +static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv) +{ +	u32     slave_port; + +	setbit_and_wait_for_clear32(&slave->sliver->soft_reset); + +	/* setup priority mapping */ +	__raw_writel(0x76543210, &slave->sliver->rx_pri_map); +	__raw_writel(0x33221100, &slave->regs->tx_pri_map); + +	/* setup max packet size, and mac address */ +	__raw_writel(PKT_MAX, &slave->sliver->rx_maxlen); +	cpsw_set_slave_mac(slave, priv); + +	slave->mac_control = 0;	/* no link yet */ + +	/* enable forwarding */ +	slave_port = cpsw_get_slave_port(priv, slave->slave_num); +	cpsw_ale_port_state(priv, slave_port, ALE_PORT_STATE_FORWARD); + +	cpsw_ale_add_mcast(priv, NetBcastAddr, 1 << slave_port); +} + +static struct cpdma_desc *cpdma_desc_alloc(struct cpsw_priv *priv) +{ +	struct cpdma_desc *desc = priv->desc_free; + +	if (desc) +		priv->desc_free = desc_read_ptr(desc, hw_next); +	return desc; +} + +static void cpdma_desc_free(struct cpsw_priv *priv, struct cpdma_desc *desc) +{ +	if (desc) { +		desc_write(desc, hw_next, priv->desc_free); +		priv->desc_free = desc; +	} +} + +static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan, +			void *buffer, int len) +{ +	struct cpdma_desc *desc, *prev; +	u32 mode; + +	desc = cpdma_desc_alloc(priv); +	if (!desc) +		return -ENOMEM; + +	if (len < PKT_MIN) +		len = PKT_MIN; + +	mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP; + +	desc_write(desc, hw_next,   0); +	desc_write(desc, hw_buffer, buffer); +	desc_write(desc, hw_len,    len); +	desc_write(desc, hw_mode,   mode | len); +	desc_write(desc, sw_buffer, buffer); +	desc_write(desc, sw_len,    len); + +	if (!chan->head) { +		/* simple case - first packet enqueued */ +		chan->head = desc; +		chan->tail = desc; +		chan_write(chan, hdp, desc); +		goto done; +	} + +	/* not the first packet - enqueue at the tail */ +	prev = chan->tail; +	desc_write(prev, hw_next, desc); +	chan->tail = desc; + +	/* next check if EOQ has been triggered already */ +	if (desc_read(prev, hw_mode) & CPDMA_DESC_EOQ) +		chan_write(chan, hdp, desc); + +done: +	if (chan->rxfree) +		chan_write(chan, rxfree, 1); +	return 0; +} + +static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan *chan, +			 void **buffer, int *len) +{ +	struct cpdma_desc *desc = chan->head; +	u32 status; + +	if (!desc) +		return -ENOENT; + +	status = desc_read(desc, hw_mode); + +	if (len) +		*len = status & 0x7ff; + +	if (buffer) +		*buffer = desc_read_ptr(desc, sw_buffer); + +	if (status & CPDMA_DESC_OWNER) { +		if (chan_read(chan, hdp) == 0) { +			if (desc_read(desc, hw_mode) & CPDMA_DESC_OWNER) +				chan_write(chan, hdp, desc); +		} + +		return -EBUSY; +	} + +	chan->head = desc_read_ptr(desc, hw_next); +	chan_write(chan, cp, desc); + +	cpdma_desc_free(priv, desc); +	return 0; +} + +static int cpsw_init(struct eth_device *dev, bd_t *bis) +{ +	struct cpsw_priv	*priv = dev->priv; +	struct cpsw_slave	*slave; +	int i, ret; + +	/* soft reset the controller and initialize priv */ +	setbit_and_wait_for_clear32(&priv->regs->soft_reset); + +	/* initialize and reset the address lookup engine */ +	cpsw_ale_enable(priv, 1); +	cpsw_ale_clear(priv, 1); +	cpsw_ale_vlan_aware(priv, 0); /* vlan unaware mode */ + +	/* setup host port priority mapping */ +	__raw_writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map); +	__raw_writel(0, &priv->host_port_regs->cpdma_rx_chan_map); + +	/* disable priority elevation and enable statistics on all ports */ +	__raw_writel(0, &priv->regs->ptype); + +	/* enable statistics collection only on the host port */ +	__raw_writel(BIT(priv->host_port), &priv->regs->stat_port_en); + +	cpsw_ale_port_state(priv, priv->host_port, ALE_PORT_STATE_FORWARD); + +	cpsw_ale_add_ucast(priv, priv->dev->enetaddr, priv->host_port, +			   ALE_SECURE); +	cpsw_ale_add_mcast(priv, NetBcastAddr, 1 << priv->host_port); + +	for_each_slave(slave, priv) +		cpsw_slave_init(slave, priv); + +	cpsw_update_link(priv); + +	/* init descriptor pool */ +	for (i = 0; i < NUM_DESCS; i++) { +		desc_write(&priv->descs[i], hw_next, +			   (i == (NUM_DESCS - 1)) ? 0 : &priv->descs[i+1]); +	} +	priv->desc_free = &priv->descs[0]; + +	/* initialize channels */ +	if (priv->data.version == CPSW_CTRL_VERSION_2) { +		memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan)); +		priv->rx_chan.hdp       = priv->dma_regs + CPDMA_RXHDP_VER2; +		priv->rx_chan.cp        = priv->dma_regs + CPDMA_RXCP_VER2; +		priv->rx_chan.rxfree    = priv->dma_regs + CPDMA_RXFREE; + +		memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan)); +		priv->tx_chan.hdp       = priv->dma_regs + CPDMA_TXHDP_VER2; +		priv->tx_chan.cp        = priv->dma_regs + CPDMA_TXCP_VER2; +	} else { +		memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan)); +		priv->rx_chan.hdp       = priv->dma_regs + CPDMA_RXHDP_VER1; +		priv->rx_chan.cp        = priv->dma_regs + CPDMA_RXCP_VER1; +		priv->rx_chan.rxfree    = priv->dma_regs + CPDMA_RXFREE; + +		memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan)); +		priv->tx_chan.hdp       = priv->dma_regs + CPDMA_TXHDP_VER1; +		priv->tx_chan.cp        = priv->dma_regs + CPDMA_TXCP_VER1; +	} + +	/* clear dma state */ +	setbit_and_wait_for_clear32(priv->dma_regs + CPDMA_SOFTRESET); + +	if (priv->data.version == CPSW_CTRL_VERSION_2) { +		for (i = 0; i < priv->data.channels; i++) { +			__raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER2 + 4 +					* i); +			__raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4 +					* i); +			__raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER2 + 4 +					* i); +			__raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER2 + 4 +					* i); +			__raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER2 + 4 +					* i); +		} +	} else { +		for (i = 0; i < priv->data.channels; i++) { +			__raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER1 + 4 +					* i); +			__raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4 +					* i); +			__raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER1 + 4 +					* i); +			__raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER1 + 4 +					* i); +			__raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER1 + 4 +					* i); + +		} +	} + +	__raw_writel(1, priv->dma_regs + CPDMA_TXCONTROL); +	__raw_writel(1, priv->dma_regs + CPDMA_RXCONTROL); + +	/* submit rx descs */ +	for (i = 0; i < PKTBUFSRX; i++) { +		ret = cpdma_submit(priv, &priv->rx_chan, NetRxPackets[i], +				   PKTSIZE); +		if (ret < 0) { +			printf("error %d submitting rx desc\n", ret); +			break; +		} +	} + +	return 0; +} + +static void cpsw_halt(struct eth_device *dev) +{ +	struct cpsw_priv	*priv = dev->priv; + +	writel(0, priv->dma_regs + CPDMA_TXCONTROL); +	writel(0, priv->dma_regs + CPDMA_RXCONTROL); + +	/* soft reset the controller and initialize priv */ +	setbit_and_wait_for_clear32(&priv->regs->soft_reset); + +	/* clear dma state */ +	setbit_and_wait_for_clear32(priv->dma_regs + CPDMA_SOFTRESET); + +	priv->data.control(0); +} + +static int cpsw_send(struct eth_device *dev, void *packet, int length) +{ +	struct cpsw_priv	*priv = dev->priv; +	void *buffer; +	int len; +	int timeout = CPDMA_TIMEOUT; + +	if (!cpsw_update_link(priv)) +		return -EIO; + +	flush_dcache_range((unsigned long)packet, +			   (unsigned long)packet + length); + +	/* first reap completed packets */ +	while (timeout-- && +		(cpdma_process(priv, &priv->tx_chan, &buffer, &len) >= 0)) +		; + +	if (timeout == -1) { +		printf("cpdma_process timeout\n"); +		return -ETIMEDOUT; +	} + +	return cpdma_submit(priv, &priv->tx_chan, packet, length); +} + +static int cpsw_recv(struct eth_device *dev) +{ +	struct cpsw_priv	*priv = dev->priv; +	void *buffer; +	int len; + +	cpsw_update_link(priv); + +	while (cpdma_process(priv, &priv->rx_chan, &buffer, &len) >= 0) { +		invalidate_dcache_range((unsigned long)buffer, +					(unsigned long)buffer + PKTSIZE_ALIGN); +		NetReceive(buffer, len); +		cpdma_submit(priv, &priv->rx_chan, buffer, PKTSIZE); +	} + +	return 0; +} + +static void cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, +			    struct cpsw_priv *priv) +{ +	void			*regs = priv->regs; +	struct cpsw_slave_data	*data = priv->data.slave_data + slave_num; +	slave->slave_num = slave_num; +	slave->data	= data; +	slave->regs	= regs + data->slave_reg_ofs; +	slave->sliver	= regs + data->sliver_reg_ofs; +} + +static int cpsw_phy_init(struct eth_device *dev, struct cpsw_slave *slave) +{ +	struct cpsw_priv *priv = (struct cpsw_priv *)dev->priv; +	struct phy_device *phydev; +	u32 supported = (SUPPORTED_10baseT_Half | +			SUPPORTED_10baseT_Full | +			SUPPORTED_100baseT_Half | +			SUPPORTED_100baseT_Full | +			SUPPORTED_1000baseT_Full); + +	phydev = phy_connect(priv->bus, 0, dev, slave->data->phy_if); + +	phydev->supported &= supported; +	phydev->advertising = phydev->supported; + +	priv->phydev = phydev; +	phy_config(phydev); + +	return 1; +} + +int cpsw_register(struct cpsw_platform_data *data) +{ +	struct cpsw_priv	*priv; +	struct cpsw_slave	*slave; +	void			*regs = (void *)data->cpsw_base; +	struct eth_device	*dev; + +	dev = calloc(sizeof(*dev), 1); +	if (!dev) +		return -ENOMEM; + +	priv = calloc(sizeof(*priv), 1); +	if (!priv) { +		free(dev); +		return -ENOMEM; +	} + +	priv->data = *data; +	priv->dev = dev; + +	priv->slaves = malloc(sizeof(struct cpsw_slave) * data->slaves); +	if (!priv->slaves) { +		free(dev); +		free(priv); +		return -ENOMEM; +	} + +	priv->descs		= (void *)CPDMA_RAM_ADDR; +	priv->host_port		= data->host_port_num; +	priv->regs		= regs; +	priv->host_port_regs	= regs + data->host_port_reg_ofs; +	priv->dma_regs		= regs + data->cpdma_reg_ofs; +	priv->ale_regs		= regs + data->ale_reg_ofs; + +	int idx = 0; + +	for_each_slave(slave, priv) { +		cpsw_slave_setup(slave, idx, priv); +		idx = idx + 1; +	} + +	strcpy(dev->name, "cpsw"); +	dev->iobase	= 0; +	dev->init	= cpsw_init; +	dev->halt	= cpsw_halt; +	dev->send	= cpsw_send; +	dev->recv	= cpsw_recv; +	dev->priv	= priv; + +	eth_register(dev); + +	cpsw_mdio_init(dev->name, data->mdio_base, data->mdio_div); +	priv->bus = miiphy_get_dev_by_name(dev->name); +	for_each_slave(slave, priv) +		cpsw_phy_init(dev, slave); + +	return 1; +} diff --git a/drivers/net/greth.c b/drivers/net/greth.c index 08206c871..3103a741a 100644 --- a/drivers/net/greth.c +++ b/drivers/net/greth.c @@ -483,7 +483,7 @@ int greth_recv(struct eth_device *dev)  	greth_regs *regs = greth->regs;  	greth_bd *rxbd;  	unsigned int status, len = 0, bad; -	unsigned char *d; +	char *d;  	int enable = 0;  	int i; @@ -504,7 +504,7 @@ int greth_recv(struct eth_device *dev)  			goto done;  		} -		debug("greth_recv: packet 0x%lx, 0x%lx, len: %d\n", +		debug("greth_recv: packet 0x%x, 0x%x, len: %d\n",  		       (unsigned int)rxbd, status, status & GRETH_BD_LEN);  		/* Check status for errors. @@ -620,7 +620,7 @@ int greth_initialize(bd_t * bis)  	greth->regs = (greth_regs *) apbdev.address;  	greth->irq = apbdev.irq; -	debug("Found GRETH at 0x%lx, irq %d\n", greth->regs, greth->irq); +	debug("Found GRETH at %p, irq %d\n", greth->regs, greth->irq);  	dev->priv = (void *)greth;  	dev->iobase = (unsigned int)greth->regs;  	dev->init = greth_init; @@ -652,7 +652,7 @@ int greth_initialize(bd_t * bis)  	/* initiate PHY, select speed/duplex depending on connected PHY */  	if (greth_init_phy(greth, bis)) {  		/* Failed to init PHY (timedout) */ -		debug("GRETH[0x%08x]: Failed to init PHY\n", greth->regs); +		debug("GRETH[%p]: Failed to init PHY\n", greth->regs);  		return -1;  	} @@ -681,6 +681,6 @@ int greth_initialize(bd_t * bis)  	/* set and remember MAC address */  	greth_set_hwaddr(greth, addr); -	debug("GRETH[0x%08x]: Initialized successfully\n", greth->regs); +	debug("GRETH[%p]: Initialized successfully\n", greth->regs);  	return 0;  } diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 45784678b..0e1ced71c 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -364,7 +364,7 @@ static int macb_phy_find(struct macb_device *macb)  	}  	/* PHY isn't up to snuff */ -	printf("%s: PHY not found", macb->netdev.name); +	printf("%s: PHY not found\n", macb->netdev.name);  	return 0;  } @@ -471,7 +471,7 @@ static int macb_init(struct eth_device *netdev, bd_t *bd)  #if	defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \  	defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) || \  	defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45) || \ -	defined(CONFIG_AT91SAM9XE) +	defined(CONFIG_AT91SAM9XE) || defined(CONFIG_AT91SAM9X5)  	macb_writel(macb, USRIO, MACB_BIT(RMII) | MACB_BIT(CLKEN));  #else  	macb_writel(macb, USRIO, 0); @@ -480,7 +480,7 @@ static int macb_init(struct eth_device *netdev, bd_t *bd)  #if	defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \  	defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) || \  	defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45) || \ -	defined(CONFIG_AT91SAM9XE) +	defined(CONFIG_AT91SAM9XE) || defined(CONFIG_AT91SAM9X5)  	macb_writel(macb, USRIO, MACB_BIT(CLKEN));  #else  	macb_writel(macb, USRIO, MACB_BIT(MII)); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 398542b9e..d864f137f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -151,13 +151,14 @@ void pci_register_hose(struct pci_controller* hose)  	*phose = hose;  } -struct pci_controller *pci_bus_to_hose (int bus) +struct pci_controller *pci_bus_to_hose(int bus)  {  	struct pci_controller *hose; -	for (hose = hose_head; hose; hose = hose->next) +	for (hose = hose_head; hose; hose = hose->next) {  		if (bus >= hose->first_busno && bus <= hose->last_busno)  			return hose; +	}  	printf("pci_bus_to_hose() failed\n");  	return NULL; @@ -196,21 +197,20 @@ pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)  	pci_dev_t bdf;  	int i, bus, found_multi = 0; -	for (hose = hose_head; hose; hose = hose->next) -	{ +	for (hose = hose_head; hose; hose = hose->next) {  #ifdef CONFIG_SYS_SCSI_SCAN_BUS_REVERSE  		for (bus = hose->last_busno; bus >= hose->first_busno; bus--)  #else  		for (bus = hose->first_busno; bus <= hose->last_busno; bus++)  #endif -			for (bdf = PCI_BDF(bus,0,0); +			for (bdf = PCI_BDF(bus, 0, 0);  #if defined(CONFIG_ELPPC) || defined(CONFIG_PPMC7XX) -			     bdf < PCI_BDF(bus,PCI_MAX_PCI_DEVICES-1,PCI_MAX_PCI_FUNCTIONS-1); +			     bdf < PCI_BDF(bus, PCI_MAX_PCI_DEVICES - 1, +				PCI_MAX_PCI_FUNCTIONS - 1);  #else -			     bdf < PCI_BDF(bus+1,0,0); +			     bdf < PCI_BDF(bus + 1, 0, 0);  #endif -			     bdf += PCI_BDF(0,0,1)) -			{ +			     bdf += PCI_BDF(0, 0, 1)) {  				if (!PCI_FUNC(bdf)) {  					pci_read_config_byte(bdf,  							     PCI_HEADER_TYPE, @@ -229,19 +229,19 @@ pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)  						     PCI_DEVICE_ID,  						     &device); -				for (i=0; ids[i].vendor != 0; i++) +				for (i = 0; ids[i].vendor != 0; i++) {  					if (vendor == ids[i].vendor && -					    device == ids[i].device) -					{ +					    device == ids[i].device) {  						if (index <= 0)  							return bdf;  						index--;  					} +				}  			}  	} -	return (-1); +	return -1;  }  pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index) @@ -258,7 +258,7 @@ pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index)   *   */ -int __pci_hose_phys_to_bus (struct pci_controller *hose, +int __pci_hose_phys_to_bus(struct pci_controller *hose,  				phys_addr_t phys_addr,  				unsigned long flags,  				unsigned long skip_mask, @@ -297,12 +297,14 @@ pci_addr_t pci_hose_phys_to_bus (struct pci_controller *hose,  	int ret;  	if (!hose) { -		puts ("pci_hose_phys_to_bus: invalid hose\n"); +		puts("pci_hose_phys_to_bus: invalid hose\n");  		return bus_addr;  	} -	/* if PCI_REGION_MEM is set we do a two pass search with preference -	 * on matches that don't have PCI_REGION_SYS_MEMORY set */ +	/* +	 * if PCI_REGION_MEM is set we do a two pass search with preference +	 * on matches that don't have PCI_REGION_SYS_MEMORY set +	 */  	if ((flags & PCI_REGION_MEM) == PCI_REGION_MEM) {  		ret = __pci_hose_phys_to_bus(hose, phys_addr,  				flags, PCI_REGION_SYS_MEMORY, &bus_addr); @@ -313,12 +315,12 @@ pci_addr_t pci_hose_phys_to_bus (struct pci_controller *hose,  	ret = __pci_hose_phys_to_bus(hose, phys_addr, flags, 0, &bus_addr);  	if (ret) -		puts ("pci_hose_phys_to_bus: invalid physical address\n"); +		puts("pci_hose_phys_to_bus: invalid physical address\n");  	return bus_addr;  } -int __pci_hose_bus_to_phys (struct pci_controller *hose, +int __pci_hose_bus_to_phys(struct pci_controller *hose,  				pci_addr_t bus_addr,  				unsigned long flags,  				unsigned long skip_mask, @@ -354,12 +356,14 @@ phys_addr_t pci_hose_bus_to_phys(struct pci_controller* hose,  	int ret;  	if (!hose) { -		puts ("pci_hose_bus_to_phys: invalid hose\n"); +		puts("pci_hose_bus_to_phys: invalid hose\n");  		return phys_addr;  	} -	/* if PCI_REGION_MEM is set we do a two pass search with preference -	 * on matches that don't have PCI_REGION_SYS_MEMORY set */ +	/* +	 * if PCI_REGION_MEM is set we do a two pass search with preference +	 * on matches that don't have PCI_REGION_SYS_MEMORY set +	 */  	if ((flags & PCI_REGION_MEM) == PCI_REGION_MEM) {  		ret = __pci_hose_bus_to_phys(hose, bus_addr,  				flags, PCI_REGION_SYS_MEMORY, &phys_addr); @@ -370,7 +374,7 @@ phys_addr_t pci_hose_bus_to_phys(struct pci_controller* hose,  	ret = __pci_hose_bus_to_phys(hose, bus_addr, flags, 0, &phys_addr);  	if (ret) -		puts ("pci_hose_bus_to_phys: invalid physical address\n"); +		puts("pci_hose_bus_to_phys: invalid physical address\n");  	return phys_addr;  } @@ -385,20 +389,21 @@ int pci_hose_config_device(struct pci_controller *hose,  			   pci_addr_t mem,  			   unsigned long command)  { -	unsigned int bar_response, old_command; +	u32 bar_response; +	unsigned int old_command;  	pci_addr_t bar_value;  	pci_size_t bar_size;  	unsigned char pin;  	int bar, found_mem64; -	debug ("PCI Config: I/O=0x%lx, Memory=0x%llx, Command=0x%lx\n", -		io, (u64)mem, command); +	debug("PCI Config: I/O=0x%lx, Memory=0x%llx, Command=0x%lx\n", io, +		(u64)mem, command); -	pci_hose_write_config_dword (hose, dev, PCI_COMMAND, 0); +	pci_hose_write_config_dword(hose, dev, PCI_COMMAND, 0);  	for (bar = PCI_BASE_ADDRESS_0; bar <= PCI_BASE_ADDRESS_5; bar += 4) { -		pci_hose_write_config_dword (hose, dev, bar, 0xffffffff); -		pci_hose_read_config_dword (hose, dev, bar, &bar_response); +		pci_hose_write_config_dword(hose, dev, bar, 0xffffffff); +		pci_hose_read_config_dword(hose, dev, bar, &bar_response);  		if (!bar_response)  			continue; @@ -418,8 +423,10 @@ int pci_hose_config_device(struct pci_controller *hose,  				PCI_BASE_ADDRESS_MEM_TYPE_64) {  				u32 bar_response_upper;  				u64 bar64; -				pci_hose_write_config_dword(hose, dev, bar+4, 0xffffffff); -				pci_hose_read_config_dword(hose, dev, bar+4, &bar_response_upper); +				pci_hose_write_config_dword(hose, dev, bar + 4, +					0xffffffff); +				pci_hose_read_config_dword(hose, dev, bar + 4, +					&bar_response_upper);  				bar64 = ((u64)bar_response_upper << 32) | bar_response; @@ -442,27 +449,28 @@ int pci_hose_config_device(struct pci_controller *hose,  		if (found_mem64) {  			bar += 4;  #ifdef CONFIG_SYS_PCI_64BIT -			pci_hose_write_config_dword(hose, dev, bar, (u32)(bar_value>>32)); +			pci_hose_write_config_dword(hose, dev, bar, +				(u32)(bar_value >> 32));  #else -			pci_hose_write_config_dword (hose, dev, bar, 0x00000000); +			pci_hose_write_config_dword(hose, dev, bar, 0x00000000);  #endif  		}  	}  	/* Configure Cache Line Size Register */ -	pci_hose_write_config_byte (hose, dev, PCI_CACHE_LINE_SIZE, 0x08); +	pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE, 0x08);  	/* Configure Latency Timer */ -	pci_hose_write_config_byte (hose, dev, PCI_LATENCY_TIMER, 0x80); +	pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80);  	/* Disable interrupt line, if device says it wants to use interrupts */ -	pci_hose_read_config_byte (hose, dev, PCI_INTERRUPT_PIN, &pin); +	pci_hose_read_config_byte(hose, dev, PCI_INTERRUPT_PIN, &pin);  	if (pin != 0) { -		pci_hose_write_config_byte (hose, dev, PCI_INTERRUPT_LINE, 0xff); +		pci_hose_write_config_byte(hose, dev, PCI_INTERRUPT_LINE, 0xff);  	} -	pci_hose_read_config_dword (hose, dev, PCI_COMMAND, &old_command); -	pci_hose_write_config_dword (hose, dev, PCI_COMMAND, +	pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &old_command); +	pci_hose_write_config_dword(hose, dev, PCI_COMMAND,  				     (old_command & 0xffff0000) | command);  	return 0; @@ -500,7 +508,8 @@ void pci_cfgfunc_config_device(struct pci_controller *hose,  			       pci_dev_t dev,  			       struct pci_config_table *entry)  { -	pci_hose_config_device(hose, dev, entry->priv[0], entry->priv[1], entry->priv[2]); +	pci_hose_config_device(hose, dev, entry->priv[0], entry->priv[1], +		entry->priv[2]);  }  void pci_cfgfunc_do_nothing(struct pci_controller *hose, @@ -509,10 +518,7 @@ void pci_cfgfunc_do_nothing(struct pci_controller *hose,  }  /* - * - */ - -/* HJF: Changed this to return int. I think this is required + * HJF: Changed this to return int. I think this is required   * to get the correct result when scanning bridges   */  extern int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev); @@ -618,10 +624,12 @@ int pci_print_dev(struct pci_controller *hose, pci_dev_t dev)  int pci_hose_scan_bus(struct pci_controller *hose, int bus)  { -	unsigned int sub_bus, found_multi=0; +	unsigned int sub_bus, found_multi = 0;  	unsigned short vendor, device, class;  	unsigned char header_type; +#ifndef CONFIG_PCI_PNP  	struct pci_config_table *cfg; +#endif  	pci_dev_t dev;  #ifdef CONFIG_PCI_SCAN_SHOW  	static int indent = 0; @@ -630,8 +638,9 @@ int pci_hose_scan_bus(struct pci_controller *hose, int bus)  	sub_bus = bus;  	for (dev =  PCI_BDF(bus,0,0); -	     dev <  PCI_BDF(bus,PCI_MAX_PCI_DEVICES-1,PCI_MAX_PCI_FUNCTIONS-1); -	     dev += PCI_BDF(0,0,1)) { +	     dev <  PCI_BDF(bus, PCI_MAX_PCI_DEVICES - 1, +				PCI_MAX_PCI_FUNCTIONS - 1); +	     dev += PCI_BDF(0, 0, 1)) {  		if (pci_skip_dev(hose, dev))  			continue; @@ -649,8 +658,8 @@ int pci_hose_scan_bus(struct pci_controller *hose, int bus)  		if (!PCI_FUNC(dev))  			found_multi = header_type & 0x80; -		debug ("PCI Scan: Found Bus %d, Device %d, Function %d\n", -			PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev) ); +		debug("PCI Scan: Found Bus %d, Device %d, Function %d\n", +			PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev));  		pci_hose_read_config_word(hose, dev, PCI_DEVICE_ID, &device);  		pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class); @@ -668,18 +677,16 @@ int pci_hose_scan_bus(struct pci_controller *hose, int bus)  		}  #endif +#ifdef CONFIG_PCI_PNP +		sub_bus = max(pciauto_config_device(hose, dev), sub_bus); +#else  		cfg = pci_find_config(hose, class, vendor, device,  				      PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev));  		if (cfg) {  			cfg->config_device(hose, dev, cfg);  			sub_bus = max(sub_bus, hose->current_busno); -#ifdef CONFIG_PCI_PNP -		} else { -			int n = pciauto_config_device(hose, dev); - -			sub_bus = max(sub_bus, n); -#endif  		} +#endif  #ifdef CONFIG_PCI_SCAN_SHOW  		indent--; @@ -711,10 +718,11 @@ int pci_hose_scan(struct pci_controller *hose)  	}  #endif /* CONFIG_PCI_BOOTDELAY */ -	/* Start scan at current_busno. +	/* +	 * Start scan at current_busno.  	 * PCIe will start scan at first_busno+1.  	 */ -	/* For legacy support, ensure current>=first */ +	/* For legacy support, ensure current >= first */  	if (hose->first_busno > hose->current_busno)  		hose->current_busno = hose->first_busno;  #ifdef CONFIG_PCI_PNP diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index 87ee2c240..cd78030cd 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -35,7 +35,7 @@   *   */ -void pciauto_region_init(struct pci_region* res) +void pciauto_region_init(struct pci_region *res)  {  	/*  	 * Avoid allocating PCI resources from address 0 -- this is illegal @@ -50,7 +50,8 @@ void pciauto_region_align(struct pci_region *res, pci_size_t size)  	res->bus_lower = ((res->bus_lower - 1) | (size - 1)) + 1;  } -int pciauto_region_allocate(struct pci_region* res, pci_size_t size, pci_addr_t *bar) +int pciauto_region_allocate(struct pci_region *res, pci_size_t size, +	pci_addr_t *bar)  {  	pci_addr_t addr; @@ -88,58 +89,77 @@ void pciauto_setup_device(struct pci_controller *hose,  			  struct pci_region *prefetch,  			  struct pci_region *io)  { -	unsigned int bar_response; -	pci_addr_t bar_value; +	u32 bar_response;  	pci_size_t bar_size; -	unsigned int cmdstat = 0; -	struct pci_region *bar_res; +	u16 cmdstat = 0;  	int bar, bar_nr = 0; +#ifndef CONFIG_PCI_ENUM_ONLY +	pci_addr_t bar_value; +	struct pci_region *bar_res;  	int found_mem64 = 0; +#endif -	pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &cmdstat); +	pci_hose_read_config_word(hose, dev, PCI_COMMAND, &cmdstat);  	cmdstat = (cmdstat & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) | PCI_COMMAND_MASTER; -	for (bar = PCI_BASE_ADDRESS_0; bar < PCI_BASE_ADDRESS_0 + (bars_num*4); bar += 4) { +	for (bar = PCI_BASE_ADDRESS_0; +		bar < PCI_BASE_ADDRESS_0 + (bars_num * 4); bar += 4) {  		/* Tickle the BAR and get the response */ +#ifndef CONFIG_PCI_ENUM_ONLY  		pci_hose_write_config_dword(hose, dev, bar, 0xffffffff); +#endif  		pci_hose_read_config_dword(hose, dev, bar, &bar_response);  		/* If BAR is not implemented go to the next BAR */  		if (!bar_response)  			continue; +#ifndef CONFIG_PCI_ENUM_ONLY  		found_mem64 = 0; +#endif  		/* Check the BAR type and set our address mask */  		if (bar_response & PCI_BASE_ADDRESS_SPACE) {  			bar_size = ((~(bar_response & PCI_BASE_ADDRESS_IO_MASK))  				   & 0xffff) + 1; +#ifndef CONFIG_PCI_ENUM_ONLY  			bar_res = io; +#endif  			DEBUGF("PCI Autoconfig: BAR %d, I/O, size=0x%llx, ", bar_nr, (u64)bar_size);  		} else { -			if ( (bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == +			if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==  			     PCI_BASE_ADDRESS_MEM_TYPE_64) {  				u32 bar_response_upper;  				u64 bar64; -				pci_hose_write_config_dword(hose, dev, bar+4, 0xffffffff); -				pci_hose_read_config_dword(hose, dev, bar+4, &bar_response_upper); + +#ifndef CONFIG_PCI_ENUM_ONLY +				pci_hose_write_config_dword(hose, dev, bar + 4, +					0xffffffff); +#endif +				pci_hose_read_config_dword(hose, dev, bar + 4, +					&bar_response_upper);  				bar64 = ((u64)bar_response_upper << 32) | bar_response;  				bar_size = ~(bar64 & PCI_BASE_ADDRESS_MEM_MASK) + 1; +#ifndef CONFIG_PCI_ENUM_ONLY  				found_mem64 = 1; +#endif  			} else {  				bar_size = (u32)(~(bar_response & PCI_BASE_ADDRESS_MEM_MASK) + 1);  			} +#ifndef CONFIG_PCI_ENUM_ONLY  			if (prefetch && (bar_response & PCI_BASE_ADDRESS_MEM_PREFETCH))  				bar_res = prefetch;  			else  				bar_res = mem; +#endif  			DEBUGF("PCI Autoconfig: BAR %d, Mem, size=0x%llx, ", bar_nr, (u64)bar_size);  		} +#ifndef CONFIG_PCI_ENUM_ONLY  		if (pciauto_region_allocate(bar_res, bar_size, &bar_value) == 0) {  			/* Write it out and update our limit */  			pci_hose_write_config_dword(hose, dev, bar, (u32)bar_value); @@ -158,16 +178,17 @@ void pciauto_setup_device(struct pci_controller *hose,  #endif  			} -			cmdstat |= (bar_response & PCI_BASE_ADDRESS_SPACE) ? -				PCI_COMMAND_IO : PCI_COMMAND_MEMORY;  		} +#endif +		cmdstat |= (bar_response & PCI_BASE_ADDRESS_SPACE) ? +			PCI_COMMAND_IO : PCI_COMMAND_MEMORY;  		DEBUGF("\n");  		bar_nr++;  	} -	pci_hose_write_config_dword(hose, dev, PCI_COMMAND, cmdstat); +	pci_hose_write_config_word(hose, dev, PCI_COMMAND, cmdstat);  	pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE,  		CONFIG_SYS_PCI_CACHE_LINE_SIZE);  	pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80); @@ -179,9 +200,9 @@ void pciauto_prescan_setup_bridge(struct pci_controller *hose,  	struct pci_region *pci_mem = hose->pci_mem;  	struct pci_region *pci_prefetch = hose->pci_prefetch;  	struct pci_region *pci_io = hose->pci_io; -	unsigned int cmdstat; +	u16 cmdstat; -	pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &cmdstat); +	pci_hose_read_config_word(hose, dev, PCI_COMMAND, &cmdstat);  	/* Configure bus number registers */  	pci_hose_write_config_byte(hose, dev, PCI_PRIMARY_BUS, @@ -229,7 +250,8 @@ void pciauto_prescan_setup_bridge(struct pci_controller *hose,  	}  	/* Enable memory and I/O accesses, enable bus master */ -	pci_hose_write_config_dword(hose, dev, PCI_COMMAND, cmdstat | PCI_COMMAND_MASTER); +	pci_hose_write_config_word(hose, dev, PCI_COMMAND, +					cmdstat | PCI_COMMAND_MASTER);  }  void pciauto_postscan_setup_bridge(struct pci_controller *hose, @@ -248,7 +270,7 @@ void pciauto_postscan_setup_bridge(struct pci_controller *hose,  		pciauto_region_align(pci_mem, 0x100000);  		pci_hose_write_config_word(hose, dev, PCI_MEMORY_LIMIT, -					(pci_mem->bus_lower-1) >> 16); +				(pci_mem->bus_lower - 1) >> 16);  	}  	if (pci_prefetch) { @@ -256,7 +278,7 @@ void pciauto_postscan_setup_bridge(struct pci_controller *hose,  		pciauto_region_align(pci_prefetch, 0x100000);  		pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_LIMIT, -					(pci_prefetch->bus_lower-1) >> 16); +				(pci_prefetch->bus_lower - 1) >> 16);  	}  	if (pci_io) { @@ -264,9 +286,9 @@ void pciauto_postscan_setup_bridge(struct pci_controller *hose,  		pciauto_region_align(pci_io, 0x1000);  		pci_hose_write_config_byte(hose, dev, PCI_IO_LIMIT, -					((pci_io->bus_lower-1) & 0x0000f000) >> 8); +				((pci_io->bus_lower - 1) & 0x0000f000) >> 8);  		pci_hose_write_config_word(hose, dev, PCI_IO_LIMIT_UPPER16, -					((pci_io->bus_lower-1) & 0xffff0000) >> 16); +				((pci_io->bus_lower - 1) & 0xffff0000) >> 16);  	}  } @@ -280,7 +302,7 @@ void pciauto_config_init(struct pci_controller *hose)  	hose->pci_io = hose->pci_mem = NULL; -	for (i=0; i<hose->region_count; i++) { +	for (i = 0; i < hose->region_count; i++) {  		switch(hose->regions[i].flags) {  		case PCI_REGION_IO:  			if (!hose->pci_io || @@ -338,7 +360,8 @@ void pciauto_config_init(struct pci_controller *hose)  	}  } -/* HJF: Changed this to return int. I think this is required +/* + * HJF: Changed this to return int. I think this is required   * to get the correct result when scanning bridges   */  int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev) @@ -350,16 +373,11 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)  	pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class); -	switch(class) { -	case PCI_CLASS_PROCESSOR_POWERPC: /* an agent or end-point */ -		DEBUGF("PCI AutoConfig: Found PowerPC device\n"); -		pciauto_setup_device(hose, dev, 6, hose->pci_mem, -				     hose->pci_prefetch, hose->pci_io); -		break; - +	switch (class) {  	case PCI_CLASS_BRIDGE_PCI:  		hose->current_busno++; -		pciauto_setup_device(hose, dev, 2, hose->pci_mem, hose->pci_prefetch, hose->pci_io); +		pciauto_setup_device(hose, dev, 2, hose->pci_mem, +			hose->pci_prefetch, hose->pci_io);  		DEBUGF("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_DEV(dev)); @@ -385,14 +403,20 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)  			return sub_bus;  		} -		pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_prefetch, hose->pci_io); +		pciauto_setup_device(hose, dev, 6, hose->pci_mem, +			hose->pci_prefetch, hose->pci_io);  		break;  	case PCI_CLASS_BRIDGE_CARDBUS: -		/* just do a minimal setup of the bridge, let the OS take care of the rest */ -		pciauto_setup_device(hose, dev, 0, hose->pci_mem, hose->pci_prefetch, hose->pci_io); +		/* +		 * just do a minimal setup of the bridge, +		 * let the OS take care of the rest +		 */ +		pciauto_setup_device(hose, dev, 0, hose->pci_mem, +			hose->pci_prefetch, hose->pci_io); -		DEBUGF("PCI Autoconfig: Found P2CardBus bridge, device %d\n", PCI_DEV(dev)); +		DEBUGF("PCI Autoconfig: Found P2CardBus bridge, device %d\n", +			PCI_DEV(dev));  		hose->current_busno++;  		break; @@ -412,11 +436,17 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)  		 * the PIMMR window to be allocated (BAR0 - 1MB size)  		 */  		DEBUGF("PCI Autoconfig: Broken bridge found, only minimal config\n"); -		pciauto_setup_device(hose, dev, 0, hose->pci_mem, hose->pci_prefetch, hose->pci_io); +		pciauto_setup_device(hose, dev, 0, hose->pci_mem, +			hose->pci_prefetch, hose->pci_io);  		break;  #endif + +	case PCI_CLASS_PROCESSOR_POWERPC: /* an agent or end-point */ +		DEBUGF("PCI AutoConfig: Found PowerPC device\n"); +  	default: -		pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_prefetch, hose->pci_io); +		pciauto_setup_device(hose, dev, 6, hose->pci_mem, +			hose->pci_prefetch, hose->pci_io);  		break;  	} diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 9cfdbf975..8316e8f2e 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -44,6 +44,7 @@ COBJS-$(CONFIG_RTC_DS164x) += ds164x.o  COBJS-$(CONFIG_RTC_DS174x) += ds174x.o  COBJS-$(CONFIG_RTC_DS3231) += ds3231.o  COBJS-$(CONFIG_RTC_FTRTC010) += ftrtc010.o +COBJS-$(CONFIG_RTC_IMXDI) += imxdi.o  COBJS-$(CONFIG_RTC_ISL1208) += isl1208.o  COBJS-$(CONFIG_RTC_M41T11) += m41t11.o  COBJS-$(CONFIG_RTC_M41T60) += m41t60.o @@ -58,6 +59,7 @@ COBJS-$(CONFIG_RTC_MK48T59) += mk48t59.o  COBJS-$(CONFIG_RTC_MPC5200) += mpc5xxx.o  COBJS-$(CONFIG_RTC_MPC8xx) += mpc8xx.o  COBJS-$(CONFIG_RTC_MV) += mvrtc.o +COBJS-$(CONFIG_RTC_MX27) += mx27rtc.o  COBJS-$(CONFIG_RTC_MXS) += mxsrtc.o  COBJS-$(CONFIG_RTC_PCF8563) += pcf8563.o  COBJS-$(CONFIG_RTC_PL031) += pl031.o diff --git a/drivers/rtc/imxdi.c b/drivers/rtc/imxdi.c new file mode 100644 index 000000000..985ce93ac --- /dev/null +++ b/drivers/rtc/imxdi.c @@ -0,0 +1,244 @@ +/* + * (C) Copyright 2009-2012 ADVANSEE + * Benoît Thébaudeau <benoit.thebaudeau@advansee.com> + * + * Based on the Linux rtc-imxdi.c driver, which is: + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2010 Orex Computed Radiography + * + * 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 + */ + +/* + * Date & Time support for Freescale i.MX DryIce RTC + */ + +#include <common.h> +#include <command.h> +#include <linux/compat.h> +#include <rtc.h> + +#if defined(CONFIG_CMD_DATE) + +#include <asm/io.h> +#include <asm/arch/imx-regs.h> + +/* DryIce Register Definitions */ + +struct imxdi_regs { +	u32 dtcmr;			/* Time Counter MSB Reg */ +	u32 dtclr;			/* Time Counter LSB Reg */ +	u32 dcamr;			/* Clock Alarm MSB Reg */ +	u32 dcalr;			/* Clock Alarm LSB Reg */ +	u32 dcr;			/* Control Reg */ +	u32 dsr;			/* Status Reg */ +	u32 dier;			/* Interrupt Enable Reg */ +}; + +#define DCAMR_UNSET	0xFFFFFFFF	/* doomsday - 1 sec */ + +#define DCR_TCE		(1 << 3)	/* Time Counter Enable */ + +#define DSR_WBF		(1 << 10)	/* Write Busy Flag */ +#define DSR_WNF		(1 << 9)	/* Write Next Flag */ +#define DSR_WCF		(1 << 8)	/* Write Complete Flag */ +#define DSR_WEF		(1 << 7)	/* Write Error Flag */ +#define DSR_CAF		(1 << 4)	/* Clock Alarm Flag */ +#define DSR_NVF		(1 << 1)	/* Non-Valid Flag */ +#define DSR_SVF		(1 << 0)	/* Security Violation Flag */ + +#define DIER_WNIE	(1 << 9)	/* Write Next Interrupt Enable */ +#define DIER_WCIE	(1 << 8)	/* Write Complete Interrupt Enable */ +#define DIER_WEIE	(1 << 7)	/* Write Error Interrupt Enable */ +#define DIER_CAIE	(1 << 4)	/* Clock Alarm Interrupt Enable */ + +/* Driver Private Data */ + +struct imxdi_data { +	struct imxdi_regs __iomem	*regs; +	int				init_done; +}; + +static struct imxdi_data data; + +/* + * This function attempts to clear the dryice write-error flag. + * + * A dryice write error is similar to a bus fault and should not occur in + * normal operation.  Clearing the flag requires another write, so the root + * cause of the problem may need to be fixed before the flag can be cleared. + */ +static void clear_write_error(void) +{ +	int cnt; + +	puts("### Warning: RTC - Register write error!\n"); + +	/* clear the write error flag */ +	__raw_writel(DSR_WEF, &data.regs->dsr); + +	/* wait for it to take effect */ +	for (cnt = 0; cnt < 1000; cnt++) { +		if ((__raw_readl(&data.regs->dsr) & DSR_WEF) == 0) +			return; +		udelay(10); +	} +	puts("### Error: RTC - Cannot clear write-error flag!\n"); +} + +/* + * Write a dryice register and wait until it completes. + * + * Use interrupt flags to determine when the write has completed. + */ +#define DI_WRITE_WAIT(val, reg)						\ +(									\ +	/* do the register write */					\ +	__raw_writel((val), &data.regs->reg),				\ +									\ +	di_write_wait((val), #reg)					\ +) +static int di_write_wait(u32 val, const char *reg) +{ +	int cnt; +	int ret = 0; +	int rc = 0; + +	/* wait for the write to finish */ +	for (cnt = 0; cnt < 100; cnt++) { +		if ((__raw_readl(&data.regs->dsr) & (DSR_WCF | DSR_WEF)) != 0) { +			ret = 1; +			break; +		} +		udelay(10); +	} +	if (ret == 0) +		printf("### Warning: RTC - Write-wait timeout " +				"val = 0x%.8x reg = %s\n", val, reg); + +	/* check for write error */ +	if (__raw_readl(&data.regs->dsr) & DSR_WEF) { +		clear_write_error(); +		rc = -1; +	} + +	return rc; +} + +/* + * Initialize dryice hardware + */ +static int di_init(void) +{ +	int rc = 0; + +	data.regs = (struct imxdi_regs __iomem *)IMX_DRYICE_BASE; + +	/* mask all interrupts */ +	__raw_writel(0, &data.regs->dier); + +	/* put dryice into valid state */ +	if (__raw_readl(&data.regs->dsr) & DSR_NVF) { +		rc = DI_WRITE_WAIT(DSR_NVF | DSR_SVF, dsr); +		if (rc) +			goto err; +	} + +	/* initialize alarm */ +	rc = DI_WRITE_WAIT(DCAMR_UNSET, dcamr); +	if (rc) +		goto err; +	rc = DI_WRITE_WAIT(0, dcalr); +	if (rc) +		goto err; + +	/* clear alarm flag */ +	if (__raw_readl(&data.regs->dsr) & DSR_CAF) { +		rc = DI_WRITE_WAIT(DSR_CAF, dsr); +		if (rc) +			goto err; +	} + +	/* the timer won't count if it has never been written to */ +	if (__raw_readl(&data.regs->dtcmr) == 0) { +		rc = DI_WRITE_WAIT(0, dtcmr); +		if (rc) +			goto err; +	} + +	/* start keeping time */ +	if (!(__raw_readl(&data.regs->dcr) & DCR_TCE)) { +		rc = DI_WRITE_WAIT(__raw_readl(&data.regs->dcr) | DCR_TCE, dcr); +		if (rc) +			goto err; +	} + +	data.init_done = 1; +	return 0; + +err: +	return rc; +} + +int rtc_get(struct rtc_time *tmp) +{ +	unsigned long now; +	int rc = 0; + +	if (!data.init_done) { +		rc = di_init(); +		if (rc) +			goto err; +	} + +	now = __raw_readl(&data.regs->dtcmr); +	to_tm(now, tmp); + +err: +	return rc; +} + +int rtc_set(struct rtc_time *tmp) +{ +	unsigned long now; +	int rc; + +	if (!data.init_done) { +		rc = di_init(); +		if (rc) +			goto err; +	} + +	now = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, +		     tmp->tm_hour, tmp->tm_min, tmp->tm_sec); +	/* zero the fractional part first */ +	rc = DI_WRITE_WAIT(0, dtclr); +	if (rc == 0) +		rc = DI_WRITE_WAIT(now, dtcmr); + +err: +	return rc; +} + +void rtc_reset(void) +{ +	di_init(); +} + +#endif diff --git a/drivers/rtc/mx27rtc.c b/drivers/rtc/mx27rtc.c new file mode 100644 index 000000000..7628dec3a --- /dev/null +++ b/drivers/rtc/mx27rtc.c @@ -0,0 +1,83 @@ +/* + * Freescale i.MX27 RTC Driver + * + * Copyright (C) 2012 Philippe Reynes <tremyfr@yahoo.fr> + * + * 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 <rtc.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> + +#define HOUR_SHIFT 8 +#define HOUR_MASK  0x1f +#define MIN_SHIFT  0 +#define MIN_MASK   0x3f + +int rtc_get(struct rtc_time *time) +{ +	struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE; +	uint32_t day, hour, min, sec; + +	day  = readl(&rtc_regs->dayr); +	hour = readl(&rtc_regs->hourmin); +	sec  = readl(&rtc_regs->seconds); + +	min  = (hour >> MIN_SHIFT) & MIN_MASK; +	hour = (hour >> HOUR_SHIFT) & HOUR_MASK; + +	sec += min * 60 + hour * 3600 + day * 24 * 3600; + +	to_tm(sec, time); + +	return 0; +} + +int rtc_set(struct rtc_time *time) +{ +	struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE; +	uint32_t day, hour, min, sec; + +	sec = mktime(time->tm_year, time->tm_mon, time->tm_mday, +		time->tm_hour, time->tm_min, time->tm_sec); + +	day  = sec / (24 * 3600); +	sec  = sec % (24 * 3600); +	hour = sec / 3600; +	sec  = sec % 3600; +	min  = sec / 60; +	sec  = sec % 60; + +	hour  = (hour & HOUR_MASK) << HOUR_SHIFT; +	hour |= (min & MIN_MASK) << MIN_SHIFT; + +	writel(day, &rtc_regs->dayr); +	writel(hour, &rtc_regs->hourmin); +	writel(sec, &rtc_regs->seconds); + +	return 0; +} + +void rtc_reset(void) +{ +	struct rtc_regs *rtc_regs = (struct rtc_regs *)IMX_RTC_BASE; + +	writel(0, &rtc_regs->dayr); +	writel(0, &rtc_regs->hourmin); +	writel(0, &rtc_regs->seconds); +} diff --git a/drivers/rtc/mxsrtc.c b/drivers/rtc/mxsrtc.c index 5beb1a044..ffefb9188 100644 --- a/drivers/rtc/mxsrtc.c +++ b/drivers/rtc/mxsrtc.c @@ -31,7 +31,7 @@  /* Set time in seconds since 1970-01-01 */  int mxs_rtc_set_time(uint32_t secs)  { -	struct mx28_rtc_regs *rtc_regs = (struct mx28_rtc_regs *)MXS_RTC_BASE; +	struct mxs_rtc_regs *rtc_regs = (struct mxs_rtc_regs *)MXS_RTC_BASE;  	int ret;  	writel(secs, &rtc_regs->hw_rtc_seconds); @@ -41,7 +41,7 @@ int mxs_rtc_set_time(uint32_t secs)  	 * is taken from the linux kernel driver for the STMP37xx RTC since  	 * documentation doesn't mention it.  	 */ -	ret = mx28_wait_mask_clr(&rtc_regs->hw_rtc_stat_reg, +	ret = mxs_wait_mask_clr(&rtc_regs->hw_rtc_stat_reg,  		0x80 << RTC_STAT_STALE_REGS_OFFSET, MXS_RTC_MAX_TIMEOUT);  	if (ret) @@ -52,7 +52,7 @@ int mxs_rtc_set_time(uint32_t secs)  int rtc_get(struct rtc_time *time)  { -	struct mx28_rtc_regs *rtc_regs = (struct mx28_rtc_regs *)MXS_RTC_BASE; +	struct mxs_rtc_regs *rtc_regs = (struct mxs_rtc_regs *)MXS_RTC_BASE;  	uint32_t secs;  	secs = readl(&rtc_regs->hw_rtc_seconds); @@ -73,14 +73,14 @@ int rtc_set(struct rtc_time *time)  void rtc_reset(void)  { -	struct mx28_rtc_regs *rtc_regs = (struct mx28_rtc_regs *)MXS_RTC_BASE; +	struct mxs_rtc_regs *rtc_regs = (struct mxs_rtc_regs *)MXS_RTC_BASE;  	int ret;  	/* Set time to 1970-01-01 */  	mxs_rtc_set_time(0);  	/* Reset the RTC block */ -	ret = mx28_reset_block(&rtc_regs->hw_rtc_ctrl_reg); +	ret = mxs_reset_block(&rtc_regs->hw_rtc_ctrl_reg);  	if (ret)  		printf("MXS RTC: Block reset timeout\n");  } diff --git a/drivers/rtc/pcf8563.c b/drivers/rtc/pcf8563.c index 339e5f608..a02853308 100644 --- a/drivers/rtc/pcf8563.c +++ b/drivers/rtc/pcf8563.c @@ -72,7 +72,7 @@ int rtc_get (struct rtc_time *tmp)  	tmp->tm_hour = bcd2bin (hour & 0x3F);  	tmp->tm_mday = bcd2bin (mday & 0x3F);  	tmp->tm_mon  = bcd2bin (mon_cent & 0x1F); -	tmp->tm_year = bcd2bin (year) + ((mon_cent & 0x80) ? 2000 : 1900); +	tmp->tm_year = bcd2bin (year) + ((mon_cent & 0x80) ? 1900 : 2000);  	tmp->tm_wday = bcd2bin (wday & 0x07);  	tmp->tm_yday = 0;  	tmp->tm_isdst= 0; @@ -94,7 +94,7 @@ int rtc_set (struct rtc_time *tmp)  	rtc_write (0x08, bin2bcd(tmp->tm_year % 100)); -	century = (tmp->tm_year >= 2000) ? 0x80 : 0; +	century = (tmp->tm_year >= 2000) ? 0 : 0x80;  	rtc_write (0x07, bin2bcd(tmp->tm_mon) | century);  	rtc_write (0x06, bin2bcd(tmp->tm_wday)); diff --git a/drivers/serial/atmel_usart.c b/drivers/serial/atmel_usart.c index e326b2bcf..943ef70fa 100644 --- a/drivers/serial/atmel_usart.c +++ b/drivers/serial/atmel_usart.c @@ -49,17 +49,26 @@ int serial_init(void)  {  	atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; +	/* +	 * Just in case: drain transmitter register +	 * 1000us is enough for baudrate >= 9600 +	 */ +	if (!(readl(&usart->csr) & USART3_BIT(TXEMPTY))) +		__udelay(1000); +  	writel(USART3_BIT(RSTRX) | USART3_BIT(RSTTX), &usart->cr);  	serial_setbrg(); -	writel(USART3_BIT(RXEN) | USART3_BIT(TXEN), &usart->cr);  	writel((USART3_BF(USART_MODE, USART3_USART_MODE_NORMAL)  			   | USART3_BF(USCLKS, USART3_USCLKS_MCK)  			   | USART3_BF(CHRL, USART3_CHRL_8)  			   | USART3_BF(PAR, USART3_PAR_NONE)  			   | USART3_BF(NBSTOP, USART3_NBSTOP_1)),  			   &usart->mr); +	writel(USART3_BIT(RXEN) | USART3_BIT(TXEN), &usart->cr); +	/* 100us is enough for the new settings to be settled */ +	__udelay(100);  	return 0;  } diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 0c2395531..facadd2f5 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -52,7 +52,7 @@ void NS16550_init(NS16550_t com_port, int baud_divisor)  	serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm);  	serial_out(UART_LCRVAL, &com_port->lcr);  #if (defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2)) || \ -					defined(CONFIG_AM33XX) +	defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX)  #if defined(CONFIG_APTIX)  	/* /13 mode so Aptix 6MHz can hit 115200 */ diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index 0d6ad6283..b10bab70d 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -84,9 +84,6 @@ static NS16550_t serial_ports[4] = {  };  #define PORT	serial_ports[port-1] -#if defined(CONFIG_CONS_INDEX) -#define CONSOLE	(serial_ports[CONFIG_CONS_INDEX-1]) -#endif  #if defined(CONFIG_SERIAL_MULTI) diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c index ed581ae22..d4c513709 100644 --- a/drivers/serial/serial_pl01x.c +++ b/drivers/serial/serial_pl01x.c @@ -156,6 +156,8 @@ int serial_init (void)  			writel(lcr, ®s->fr);  		writel(lcr, ®s->pl011_rlcr); +		/* lcrh needs to be set again for change to be effective */ +		writel(lcr, ®s->pl011_lcrh);  	}  #endif  	/* Finally, enable the UART */ diff --git a/drivers/serial/serial_xuartlite.c b/drivers/serial/serial_xuartlite.c index 3a38f9e32..2bdb68ba4 100644 --- a/drivers/serial/serial_xuartlite.c +++ b/drivers/serial/serial_xuartlite.c @@ -89,11 +89,17 @@ int uartlite_serial_tstc(const int port)  	return in_be32(®s->status) & SR_RX_FIFO_VALID_DATA;  } +static int uartlite_serial_init(const int port) +{ +	if (userial_ports[port]) +		return 0; +	return -1; +} +  #if !defined(CONFIG_SERIAL_MULTI)  int serial_init(void)  { -	/* FIXME: Nothing for now. We should initialize fifo, etc */ -	return 0; +	return uartlite_serial_init(0);  }  void serial_setbrg(void) @@ -126,7 +132,7 @@ int serial_tstc(void)  /* Multi serial device functions */  #define DECLARE_ESERIAL_FUNCTIONS(port) \  	int userial##port##_init(void) \ -				{ return(0); } \ +				{ return uartlite_serial_init(port); } \  	void userial##port##_setbrg(void) {} \  	int userial##port##_getc(void) \  				{ return uartlite_serial_getc(port); } \ @@ -163,17 +169,15 @@ struct serial_device uartlite_serial3_device =  __weak struct serial_device *default_serial_console(void)  { -# ifdef XILINX_UARTLITE_BASEADDR -	return &uartlite_serial0_device; -# endif /* XILINX_UARTLITE_BASEADDR */ -# ifdef XILINX_UARTLITE_BASEADDR1 -	return &uartlite_serial1_device; -# endif /* XILINX_UARTLITE_BASEADDR1 */ -# ifdef XILINX_UARTLITE_BASEADDR2 -	return &uartlite_serial2_device; -# endif /* XILINX_UARTLITE_BASEADDR2 */ -# ifdef XILINX_UARTLITE_BASEADDR3 -	return &uartlite_serial3_device; -# endif /* XILINX_UARTLITE_BASEADDR3 */ +	if (userial_ports[0]) +		return &uartlite_serial0_device; +	if (userial_ports[1]) +		return &uartlite_serial1_device; +	if (userial_ports[2]) +		return &uartlite_serial2_device; +	if (userial_ports[3]) +		return &uartlite_serial3_device; + +	return NULL;  }  #endif /* CONFIG_SERIAL_MULTI */ diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 80b981f10..f0b82c67f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -32,6 +32,7 @@ COBJS-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o  COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o  COBJS-$(CONFIG_BFIN_SPI) += bfin_spi.o  COBJS-$(CONFIG_CF_SPI) += cf_spi.o +COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o  COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o  COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o  COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 83ef8e8b1..c7a51f7f3 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -92,6 +92,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  	as->slave.cs = cs;  	as->regs = regs;  	as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS +#if defined(CONFIG_AT91SAM9X5) +			| ATMEL_SPI_MR_WDRBT +#endif  			| ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf);  	spi_writel(as, CSR(cs), csrx); diff --git a/drivers/spi/atmel_spi.h b/drivers/spi/atmel_spi.h index 8b69a6d21..057de9ade 100644 --- a/drivers/spi/atmel_spi.h +++ b/drivers/spi/atmel_spi.h @@ -26,6 +26,7 @@  #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_WDRBT		(1 << 5)  #define ATMEL_SPI_MR_LLB		(1 << 7)  #define ATMEL_SPI_MR_PCS(x)		(((x) & 15) << 16)  #define ATMEL_SPI_MR_DLYBCS(x)		((x) << 24) diff --git a/drivers/spi/cf_qspi.c b/drivers/spi/cf_qspi.c new file mode 100644 index 000000000..72dd1a520 --- /dev/null +++ b/drivers/spi/cf_qspi.c @@ -0,0 +1,373 @@ +/* + * Freescale Coldfire Queued SPI driver + * + * NOTE: + * This driver is written to transfer 8 bit at-a-time and uses the dedicated + * SPI slave select pins as bit-banged GPIO to work with spi_flash subsystem. + * + * + * Copyright (C) 2011 Ruggedcom, Inc. + * Richard Retanubun (richardretanubun@freescale.com) + * + * See file CREDITS for list of people who contributed to this project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <spi.h> +#include <asm/immap.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define clamp(x, low, high) (min(max(low, x), high)) +#define to_cf_qspi_slave(s) container_of(s, struct cf_qspi_slave, s) + +struct cf_qspi_slave { +	struct spi_slave slave;	/* Specific bus:cs ID for each device */ +	qspi_t *regs;		/* Pointer to SPI controller registers */ +	u16 qmr;		/* QMR: Queued Mode Register */ +	u16 qwr;		/* QWR: Queued Wrap Register */ +	u16 qcr;		/* QCR: Queued Command Ram */ +}; + +/* Register write wrapper functions */ +static void write_qmr(volatile qspi_t *qspi, u16 val)   { qspi->mr = val; } +static void write_qdlyr(volatile qspi_t *qspi, u16 val) { qspi->dlyr = val; } +static void write_qwr(volatile qspi_t *qspi, u16 val)   { qspi->wr = val; } +static void write_qir(volatile qspi_t *qspi, u16 val)   { qspi->ir = val; } +static void write_qar(volatile qspi_t *qspi, u16 val)   { qspi->ar = val; } +static void write_qdr(volatile qspi_t *qspi, u16 val)   { qspi->dr = val; } +/* Register read wrapper functions */ +static u16 read_qdlyr(volatile qspi_t *qspi) { return qspi->dlyr; } +static u16 read_qwr(volatile qspi_t *qspi)   { return qspi->wr; } +static u16 read_qir(volatile qspi_t *qspi)   { return qspi->ir; } +static u16 read_qdr(volatile qspi_t *qspi)   { return qspi->dr; } + +/* These call points may be different for each ColdFire CPU */ +extern void cfspi_port_conf(void); +static void cfspi_cs_activate(uint bus, uint cs, uint cs_active_high); +static void cfspi_cs_deactivate(uint bus, uint cs, uint cs_active_high); + +int spi_claim_bus(struct spi_slave *slave) +{ +	return 0; +} +void spi_release_bus(struct spi_slave *slave) +{ +} + +__attribute__((weak)) +void spi_init(void) +{ +	cfspi_port_conf(); +} + +__attribute__((weak)) +void spi_cs_activate(struct spi_slave *slave) +{ +	struct cf_qspi_slave *dev = to_cf_qspi_slave(slave); + +	cfspi_cs_activate(slave->bus, slave->cs, !(dev->qwr & QSPI_QWR_CSIV)); +} + +__attribute__((weak)) +void spi_cs_deactivate(struct spi_slave *slave) +{ +	struct cf_qspi_slave *dev = to_cf_qspi_slave(slave); + +	cfspi_cs_deactivate(slave->bus, slave->cs, !(dev->qwr & QSPI_QWR_CSIV)); +} + +__attribute__((weak)) +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ +	/* Only 1 bus and 4 chipselect per controller */ +	if (bus == 0 && (cs >= 0 && cs < 4)) +		return 1; +	else +		return 0; +} + +void spi_free_slave(struct spi_slave *slave) +{ +	struct cf_qspi_slave *dev = to_cf_qspi_slave(slave); + +	free(dev); +} + +/* Translate information given by spi_setup_slave to members of cf_qspi_slave */ +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, +				  unsigned int max_hz, unsigned int mode) +{ +	struct cf_qspi_slave *dev = NULL; + +	if (!spi_cs_is_valid(bus, cs)) +		return NULL; + +	dev = malloc(sizeof(struct cf_qspi_slave)); +	if (!dev) +		return NULL; + +	/* Initialize to known value */ +	dev->slave.bus = bus; +	dev->slave.cs  = cs; +	dev->regs      = (qspi_t *)MMAP_QSPI; +	dev->qmr       = 0; +	dev->qwr       = 0; +	dev->qcr       = 0; + + +	/* Map max_hz to QMR[BAUD] */ +	if (max_hz == 0) /* Go as fast as possible */ +		dev->qmr = 2u; +	else /* Get the closest baud rate */ +		dev->qmr = clamp(((gd->bus_clk >> 2) + max_hz - 1)/max_hz, +					2u, 255u); + +	/* Map mode to QMR[CPOL] and QMR[CPHA] */ +	if (mode & SPI_CPOL) +		dev->qmr |= QSPI_QMR_CPOL; + +	if (mode & SPI_CPHA) +		dev->qmr |= QSPI_QMR_CPHA; + +	/* Hardcode bit length to 8 bit per transter */ +	dev->qmr |= QSPI_QMR_BITS_8; + +	/* Set QMR[MSTR] to enable QSPI as master */ +	dev->qmr |= QSPI_QMR_MSTR; + +	/* +	 * Set QCR and QWR to default values for spi flash operation. +	 * If more custom QCR and QRW are needed, overload mode variable +	 */ +	dev->qcr = (QSPI_QDR_CONT | QSPI_QDR_BITSE); + +	if (!(mode & SPI_CS_HIGH)) +		dev->qwr |= QSPI_QWR_CSIV; + +	return &dev->slave; +} + +/* Transfer 8 bit at a time */ +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, +	     void *din, unsigned long flags) +{ +	struct cf_qspi_slave *dev = to_cf_qspi_slave(slave); +	volatile qspi_t *qspi = dev->regs; +	u8 *txbuf = (u8 *)dout; +	u8 *rxbuf = (u8 *)din; +	u32 count = ((bitlen / 8) + (bitlen % 8 ? 1 : 0)); +	u32 n, i = 0; + +	/* Sanitize arguments */ +	if (slave == NULL) { +		printf("%s: NULL slave ptr\n", __func__); +		return -1; +	} + +	if (flags & SPI_XFER_BEGIN) +		spi_cs_activate(slave); + +	/* There is something to send, lets process it. spi_xfer is also called +	 * just to toggle chip select, so bitlen of 0 is valid */ +	if (count > 0) { +		/* +		* NOTE: Since chip select is driven as a bit-bang-ed GPIO +		* using spi_cs_activate() and spi_cs_deactivate(), +		* the chip select settings inside the controller +		* (i.e. QCR[CONT] and QWR[CSIV]) are moot. The bits are set to +		* keep the controller settings consistent with the actual +		* operation of the bus. +		*/ + +		/* Write the slave device's settings for the controller.*/ +		write_qmr(qspi, dev->qmr); +		write_qwr(qspi, dev->qwr); + +		/* Limit transfer to 16 at a time */ +		n = min(count, 16u); +		do { +			/* Setup queue end point */ +			write_qwr(qspi, ((read_qwr(qspi) & QSPI_QWR_ENDQP_MASK) +				| QSPI_QWR_ENDQP((n-1)))); + +			/* Write Command RAM */ +			write_qar(qspi, QSPI_QAR_CMD); +			for (i = 0; i < n; ++i) +				write_qdr(qspi, dev->qcr); + +			/* Write TxBuf, if none given, fill with ZEROes */ +			write_qar(qspi, QSPI_QAR_TRANS); +			if (txbuf) { +				for (i = 0; i < n; ++i) +					write_qdr(qspi, *txbuf++); +			} else { +				for (i = 0; i < n; ++i) +					write_qdr(qspi, 0); +			} + +			/* Clear QIR[SPIF] by writing a 1 to it */ +			write_qir(qspi, read_qir(qspi) | QSPI_QIR_SPIF); +			/* Set QDLYR[SPE] to start sending */ +			write_qdlyr(qspi, read_qdlyr(qspi) | QSPI_QDLYR_SPE); + +			/* Poll QIR[SPIF] for transfer completion */ +			while ((read_qir(qspi) & QSPI_QIR_SPIF) != 1) +				udelay(1); + +			/* If given read RxBuf, load data to it */ +			if (rxbuf) { +				write_qar(qspi, QSPI_QAR_RECV); +				for (i = 0; i < n; ++i) +					*rxbuf++ = read_qdr(qspi); +			} + +			/* Decrement count */ +			count -= n; +		} while (count); +	} + +	if (flags & SPI_XFER_END) +		spi_cs_deactivate(slave); + +	return 0; +} + +/* Each MCF CPU may have different pin assignments for chip selects. */ +#if defined(CONFIG_M5271) +/* Assert chip select, val = [1|0] , dir = out, mode = GPIO */ +void cfspi_cs_activate(uint bus, uint cs, uint cs_active_high) +{ +	debug("%s: bus %d cs %d cs_active_high %d\n", +		__func__, bus, cs, cs_active_high); + +	switch (cs) { +	case 0: /* QSPI_CS[0] = PQSPI[3] */ +		if (cs_active_high) +			mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x08); +		else +			mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xF7); + +		mbar_writeByte(MCF_GPIO_PDDR_QSPI, +			mbar_readByte(MCF_GPIO_PDDR_QSPI) | 0x08); + +		mbar_writeByte(MCF_GPIO_PAR_QSPI, +			mbar_readByte(MCF_GPIO_PAR_QSPI) & 0xDF); +		break; +	case 1: /* QSPI_CS[1] = PQSPI[4] */ +		if (cs_active_high) +			mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x10); +		else +			mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xEF); + +		mbar_writeByte(MCF_GPIO_PDDR_QSPI, +			mbar_readByte(MCF_GPIO_PDDR_QSPI) | 0x10); + +		mbar_writeByte(MCF_GPIO_PAR_QSPI, +			mbar_readByte(MCF_GPIO_PAR_QSPI) & 0x3F); +		break; +	case 2: /* QSPI_CS[2] = PTIMER[7] */ +		if (cs_active_high) +			mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x80); +		else +			mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0x7F); + +		mbar_writeByte(MCF_GPIO_PDDR_TIMER, +			mbar_readByte(MCF_GPIO_PDDR_TIMER) | 0x80); + +		mbar_writeShort(MCF_GPIO_PAR_TIMER, +			mbar_readShort(MCF_GPIO_PAR_TIMER) & 0x3FFF); +		break; +	case 3: /* QSPI_CS[3] = PTIMER[3] */ +		if (cs_active_high) +			mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x08); +		else +			mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0xF7); + +		mbar_writeByte(MCF_GPIO_PDDR_TIMER, +			mbar_readByte(MCF_GPIO_PDDR_TIMER) | 0x08); + +		mbar_writeShort(MCF_GPIO_PAR_TIMER, +			mbar_readShort(MCF_GPIO_PAR_TIMER) & 0xFF3F); +		break; +	} +} + +/* Deassert chip select, val = [1|0], dir = in, mode = GPIO + * direction set as IN to undrive the pin, external pullup/pulldown will bring + * bus to deassert state. + */ +void cfspi_cs_deactivate(uint bus, uint cs, uint cs_active_high) +{ +	debug("%s: bus %d cs %d cs_active_high %d\n", +		__func__, bus, cs, cs_active_high); + +	switch (cs) { +	case 0: /* QSPI_CS[0] = PQSPI[3] */ +		if (cs_active_high) +			mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xF7); +		else +			mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x08); + +		mbar_writeByte(MCF_GPIO_PDDR_QSPI, +			mbar_readByte(MCF_GPIO_PDDR_QSPI) & 0xF7); + +		mbar_writeByte(MCF_GPIO_PAR_QSPI, +			mbar_readByte(MCF_GPIO_PAR_QSPI) & 0xDF); +		break; +	case 1: /* QSPI_CS[1] = PQSPI[4] */ +		if (cs_active_high) +			mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xEF); +		else +			mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x10); + +		mbar_writeByte(MCF_GPIO_PDDR_QSPI, +			mbar_readByte(MCF_GPIO_PDDR_QSPI) & 0xEF); + +		mbar_writeByte(MCF_GPIO_PAR_QSPI, +			mbar_readByte(MCF_GPIO_PAR_QSPI) & 0x3F); +		break; +	case 2: /* QSPI_CS[2] = PTIMER[7] */ +		if (cs_active_high) +			mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0x7F); +		else +			mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x80); + +		mbar_writeByte(MCF_GPIO_PDDR_TIMER, +			mbar_readByte(MCF_GPIO_PDDR_TIMER) & 0x7F); + +		mbar_writeShort(MCF_GPIO_PAR_TIMER, +			mbar_readShort(MCF_GPIO_PAR_TIMER) & 0x3FFF); +		break; +	case 3: /* QSPI_CS[3] = PTIMER[3] */ +		if (cs_active_high) +			mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0xF7); +		else +			mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x08); + +		mbar_writeByte(MCF_GPIO_PDDR_TIMER, +			mbar_readByte(MCF_GPIO_PDDR_TIMER) & 0xF7); + +		mbar_writeShort(MCF_GPIO_PAR_TIMER, +			mbar_readShort(MCF_GPIO_PAR_TIMER) & 0xFF3F); +		break; +	} +} +#endif /* CONFIG_M5271 */ diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c index f4523a392..a7cda751b 100644 --- a/drivers/spi/kirkwood_spi.c +++ b/drivers/spi/kirkwood_spi.c @@ -56,8 +56,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  	writel(~KWSPI_CSN_ACT | KWSPI_SMEMRDY, &spireg->ctrl);  	/* calculate spi clock prescaller using max_hz */ -	data = ((CONFIG_SYS_TCLK / 2) / max_hz) & KWSPI_CLKPRESCL_MASK; -	data |= 0x10; +	data = ((CONFIG_SYS_TCLK / 2) / max_hz) + 0x10; +	data = data < KWSPI_CLKPRESCL_MIN ? KWSPI_CLKPRESCL_MIN : data; +	data = data > KWSPI_CLKPRESCL_MASK ? KWSPI_CLKPRESCL_MASK : data;  	/* program spi clock prescaller using max_hz */  	writel(KWSPI_ADRLEN_3BYTE | data, &spireg->cfg); diff --git a/drivers/spi/mpc8xxx_spi.c b/drivers/spi/mpc8xxx_spi.c index 44ab39dd3..4e46041df 100644 --- a/drivers/spi/mpc8xxx_spi.c +++ b/drivers/spi/mpc8xxx_spi.c @@ -124,6 +124,8 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,  		 * len > 16               0  		 */ +		spi->mode &= ~SPI_MODE_EN; +  		if (bitlen <= 16) {  			if (bitlen <= 4)  				spi->mode = (spi->mode & 0xff0fffff) | @@ -138,6 +140,8 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,  			dout += 4;  		} +		spi->mode |= SPI_MODE_EN; +  		spi->tx = tmpdout;	/* Write the data out */  		debug("*** spi_xfer: ... %08x written\n", tmpdout); diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c index 2e1531858..13bebe8ac 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -96,7 +96,7 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs,  	clk_src = mxc_get_clock(MXC_CSPI_CLK); -	div = clk_src / max_hz; +	div = DIV_ROUND_UP(clk_src, max_hz);  	div = get_cspi_div(div);  	debug("clk %d Hz, div %d, real clk %d Hz\n", @@ -147,7 +147,7 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs,  	 * The following computation is taken directly from Freescale's code.  	 */  	if (clk_src > max_hz) { -		pre_div = clk_src / max_hz; +		pre_div = DIV_ROUND_UP(clk_src, max_hz);  		if (pre_div > 16) {  			post_div = pre_div / 16;  			pre_div = 15; @@ -408,7 +408,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  	if (bus >= ARRAY_SIZE(spi_bases))  		return NULL; -	mxcs = malloc(sizeof(struct mxc_spi_slave)); +	mxcs = calloc(sizeof(struct mxc_spi_slave), 1);  	if (!mxcs) {  		puts("mxc_spi: SPI Slave not allocated !\n");  		return NULL; diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c index 7859536a6..42e4c9952 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -31,17 +31,31 @@  #include <asm/arch/clock.h>  #include <asm/arch/imx-regs.h>  #include <asm/arch/sys_proto.h> +#include <asm/arch/dma.h>  #define	MXS_SPI_MAX_TIMEOUT	1000000  #define	MXS_SPI_PORT_OFFSET	0x2000  #define MXS_SSP_CHIPSELECT_MASK		0x00300000  #define MXS_SSP_CHIPSELECT_SHIFT	20 +#define MXSSSP_SMALL_TRANSFER	512 + +/* + * CONFIG_MXS_SPI_DMA_ENABLE: Experimental mixed PIO/DMA support for MXS SPI + *                            host. Use with utmost caution! + * + *                            Enabling this is not yet recommended since this + *                            still doesn't support transfers to/from unaligned + *                            addresses. Therefore this driver will not work + *                            for example with saving environment. This is + *                            caused by DMA alignment constraints on MXS. + */ +  struct mxs_spi_slave {  	struct spi_slave	slave;  	uint32_t		max_khz;  	uint32_t		mode; -	struct mx28_ssp_regs	*regs; +	struct mxs_ssp_regs	*regs;  };  static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave) @@ -67,7 +81,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  {  	struct mxs_spi_slave *mxs_slave;  	uint32_t addr; -	struct mx28_ssp_regs *ssp_regs; +	struct mxs_ssp_regs *ssp_regs;  	int reg;  	if (!spi_cs_is_valid(bus, cs)) { @@ -75,17 +89,20 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  		return NULL;  	} -	mxs_slave = malloc(sizeof(struct mxs_spi_slave)); +	mxs_slave = calloc(sizeof(struct mxs_spi_slave), 1);  	if (!mxs_slave)  		return NULL; +	if (mxs_dma_init_channel(bus)) +		goto err_init; +  	addr = MXS_SSP0_BASE + (bus * MXS_SPI_PORT_OFFSET);  	mxs_slave->slave.bus = bus;  	mxs_slave->slave.cs = cs;  	mxs_slave->max_khz = max_hz / 1000;  	mxs_slave->mode = mode; -	mxs_slave->regs = (struct mx28_ssp_regs *)addr; +	mxs_slave->regs = (struct mxs_ssp_regs *)addr;  	ssp_regs = mxs_slave->regs;  	reg = readl(&ssp_regs->hw_ssp_ctrl0); @@ -94,6 +111,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  	writel(reg, &ssp_regs->hw_ssp_ctrl0);  	return &mxs_slave->slave; + +err_init: +	free(mxs_slave); +	return NULL;  }  void spi_free_slave(struct spi_slave *slave) @@ -105,10 +126,10 @@ void spi_free_slave(struct spi_slave *slave)  int spi_claim_bus(struct spi_slave *slave)  {  	struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); -	struct mx28_ssp_regs *ssp_regs = mxs_slave->regs; +	struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;  	uint32_t reg = 0; -	mx28_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); +	mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);  	writel(SSP_CTRL0_BUS_WIDTH_ONE_BIT, &ssp_regs->hw_ssp_ctrl0); @@ -128,79 +149,63 @@ void spi_release_bus(struct spi_slave *slave)  {  } -static void mxs_spi_start_xfer(struct mx28_ssp_regs *ssp_regs) +static void mxs_spi_start_xfer(struct mxs_ssp_regs *ssp_regs)  {  	writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_set);  	writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_clr);  } -static void mxs_spi_end_xfer(struct mx28_ssp_regs *ssp_regs) +static void mxs_spi_end_xfer(struct mxs_ssp_regs *ssp_regs)  {  	writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_clr);  	writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_set);  } -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, -		const void *dout, void *din, unsigned long flags) +static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave, +			char *data, int length, int write, unsigned long flags)  { -	struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); -	struct mx28_ssp_regs *ssp_regs = mxs_slave->regs; -	int len = bitlen / 8; -	const char *tx = dout; -	char *rx = din; -	char dummy; - -	if (bitlen == 0) { -		if (flags & SPI_XFER_END) { -			rx = &dummy; -			len = 1; -		} else -			return 0; -	} - -	if (!rx && !tx) -		return 0; +	struct mxs_ssp_regs *ssp_regs = slave->regs;  	if (flags & SPI_XFER_BEGIN)  		mxs_spi_start_xfer(ssp_regs); -	while (len--) { +	while (length--) {  		/* We transfer 1 byte */  		writel(1, &ssp_regs->hw_ssp_xfer_size); -		if ((flags & SPI_XFER_END) && !len) +		if ((flags & SPI_XFER_END) && !length)  			mxs_spi_end_xfer(ssp_regs); -		if (tx) +		if (write)  			writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_clr);  		else  			writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_set);  		writel(SSP_CTRL0_RUN, &ssp_regs->hw_ssp_ctrl0_set); -		if (mx28_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg, +		if (mxs_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg,  			SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {  			printf("MXS SPI: Timeout waiting for start\n");  			return -ETIMEDOUT;  		} -		if (tx) -			writel(*tx++, &ssp_regs->hw_ssp_data); +		if (write) +			writel(*data++, &ssp_regs->hw_ssp_data);  		writel(SSP_CTRL0_DATA_XFER, &ssp_regs->hw_ssp_ctrl0_set); -		if (rx) { -			if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_status_reg, +		if (!write) { +			if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_status_reg,  				SSP_STATUS_FIFO_EMPTY, MXS_SPI_MAX_TIMEOUT)) {  				printf("MXS SPI: Timeout waiting for data\n");  				return -ETIMEDOUT;  			} -			*rx = readl(&ssp_regs->hw_ssp_data); -			rx++; +			*data = readl(&ssp_regs->hw_ssp_data); +			data++;  		} -		if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg, +		if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg,  			SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {  			printf("MXS SPI: Timeout waiting for finish\n");  			return -ETIMEDOUT; @@ -209,3 +214,174 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,  	return 0;  } + +static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave, +			char *data, int length, int write, unsigned long flags) +{ +	const int xfer_max_sz = 0xff00; +	const int desc_count = DIV_ROUND_UP(length, xfer_max_sz) + 1; +	struct mxs_ssp_regs *ssp_regs = slave->regs; +	struct mxs_dma_desc *dp; +	uint32_t ctrl0; +	uint32_t cache_data_count; +	const uint32_t dstart = (uint32_t)data; +	int dmach; +	int tl; +	int ret = 0; + +	ALLOC_CACHE_ALIGN_BUFFER(struct mxs_dma_desc, desc, desc_count); + +	memset(desc, 0, sizeof(struct mxs_dma_desc) * desc_count); + +	ctrl0 = readl(&ssp_regs->hw_ssp_ctrl0); +	ctrl0 |= SSP_CTRL0_DATA_XFER; + +	if (flags & SPI_XFER_BEGIN) +		ctrl0 |= SSP_CTRL0_LOCK_CS; +	if (!write) +		ctrl0 |= SSP_CTRL0_READ; + +	if (length % ARCH_DMA_MINALIGN) +		cache_data_count = roundup(length, ARCH_DMA_MINALIGN); +	else +		cache_data_count = length; + +	/* Flush data to DRAM so DMA can pick them up */ +	if (write) +		flush_dcache_range(dstart, dstart + cache_data_count); + +	/* Invalidate the area, so no writeback into the RAM races with DMA */ +	invalidate_dcache_range(dstart, dstart + cache_data_count); + +	dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + slave->slave.bus; + +	dp = desc; +	while (length) { +		dp->address = (dma_addr_t)dp; +		dp->cmd.address = (dma_addr_t)data; + +		/* +		 * This is correct, even though it does indeed look insane. +		 * I hereby have to, wholeheartedly, thank Freescale Inc., +		 * for always inventing insane hardware and keeping me busy +		 * and employed ;-) +		 */ +		if (write) +			dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ; +		else +			dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE; + +		/* +		 * The DMA controller can transfer large chunks (64kB) at +		 * time by setting the transfer length to 0. Setting tl to +		 * 0x10000 will overflow below and make .data contain 0. +		 * Otherwise, 0xff00 is the transfer maximum. +		 */ +		if (length >= 0x10000) +			tl = 0x10000; +		else +			tl = min(length, xfer_max_sz); + +		dp->cmd.data |= +			((tl & 0xffff) << MXS_DMA_DESC_BYTES_OFFSET) | +			(4 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | +			MXS_DMA_DESC_HALT_ON_TERMINATE | +			MXS_DMA_DESC_TERMINATE_FLUSH; + +		data += tl; +		length -= tl; + +		if (!length) { +			dp->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; + +			if (flags & SPI_XFER_END) { +				ctrl0 &= ~SSP_CTRL0_LOCK_CS; +				ctrl0 |= SSP_CTRL0_IGNORE_CRC; +			} +		} + +		/* +		 * Write CTRL0, CMD0, CMD1, XFER_SIZE registers. It is +		 * essential that the XFER_SIZE register is written on +		 * a per-descriptor basis with the same size as is the +		 * descriptor! +		 */ +		dp->cmd.pio_words[0] = ctrl0; +		dp->cmd.pio_words[1] = 0; +		dp->cmd.pio_words[2] = 0; +		dp->cmd.pio_words[3] = tl; + +		mxs_dma_desc_append(dmach, dp); + +		dp++; +	} + +	if (mxs_dma_go(dmach)) +		ret = -EINVAL; + +	/* The data arrived into DRAM, invalidate cache over them */ +	if (!write) +		invalidate_dcache_range(dstart, dstart + cache_data_count); + +	return ret; +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, +		const void *dout, void *din, unsigned long flags) +{ +	struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); +	struct mxs_ssp_regs *ssp_regs = mxs_slave->regs; +	int len = bitlen / 8; +	char dummy; +	int write = 0; +	char *data = NULL; + +#ifdef CONFIG_MXS_SPI_DMA_ENABLE +	int dma = 1; +#else +	int dma = 0; +#endif + +	if (bitlen == 0) { +		if (flags & SPI_XFER_END) { +			din = (void *)&dummy; +			len = 1; +		} else +			return 0; +	} + +	/* Half-duplex only */ +	if (din && dout) +		return -EINVAL; +	/* No data */ +	if (!din && !dout) +		return 0; + +	if (dout) { +		data = (char *)dout; +		write = 1; +	} else if (din) { +		data = (char *)din; +		write = 0; +	} + +	/* +	 * Check for alignment, if the buffer is aligned, do DMA transfer, +	 * PIO otherwise. This is a temporary workaround until proper bounce +	 * buffer is in place. +	 */ +	if (dma) { +		if (((uint32_t)data) & (ARCH_DMA_MINALIGN - 1)) +			dma = 0; +		if (((uint32_t)len) & (ARCH_DMA_MINALIGN - 1)) +			dma = 0; +	} + +	if (!dma || (len < MXSSSP_SMALL_TRANSFER)) { +		writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr); +		return mxs_spi_xfer_pio(mxs_slave, data, len, write, flags); +	} else { +		writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set); +		return mxs_spi_xfer_dma(mxs_slave, data, len, write, flags); +	} +} diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c index 9346c0b5b..e40a632ca 100644 --- a/drivers/spi/omap3_spi.c +++ b/drivers/spi/omap3_spi.c @@ -86,15 +86,21 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  	case 0:  		ds->regs = (struct mcspi *)OMAP3_MCSPI1_BASE;  		break; +#ifdef OMAP3_MCSPI2_BASE  	case 1:  		ds->regs = (struct mcspi *)OMAP3_MCSPI2_BASE;  		break; +#endif +#ifdef OMAP3_MCSPI3_BASE   	case 2:  		ds->regs = (struct mcspi *)OMAP3_MCSPI3_BASE;  		break; +#endif +#ifdef OMAP3_MCSPI4_BASE  	case 3:  		ds->regs = (struct mcspi *)OMAP3_MCSPI4_BASE;  		break; +#endif  	default:  		printf("SPI error: unsupported bus %i. \  			Supported busses 0 - 3\n", bus); @@ -167,8 +173,18 @@ int spi_claim_bus(struct spi_slave *slave)  	/* standard 4-wire master mode:	SCK, MOSI/out, MISO/in, nCS  	 * REVISIT: this controller could support SPI_3WIRE mode.  	 */ +#ifdef CONFIG_AM33XX +	/* +	 * The reference design on AM33xx has D0 and D1 wired up opposite +	 * of how it has been done on previous platforms.  We assume that +	 * custom hardware will also follow this convention. +	 */ +	conf &= OMAP3_MCSPI_CHCONF_DPE0; +	conf |= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1); +#else  	conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1);  	conf |= OMAP3_MCSPI_CHCONF_DPE0; +#endif  	/* wordlength */  	conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK; diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h index 0ac801cb2..bffa43cb6 100644 --- a/drivers/spi/omap3_spi.h +++ b/drivers/spi/omap3_spi.h @@ -30,10 +30,15 @@  #ifndef _OMAP3_SPI_H_  #define _OMAP3_SPI_H_ +#ifdef CONFIG_AM33XX +#define OMAP3_MCSPI1_BASE	0x48030100 +#define OMAP3_MCSPI2_BASE	0x481A0100 +#else  #define OMAP3_MCSPI1_BASE	0x48098000  #define OMAP3_MCSPI2_BASE	0x4809A000  #define OMAP3_MCSPI3_BASE	0x480B8000  #define OMAP3_MCSPI4_BASE	0x480BA000 +#endif  #define OMAP3_MCSPI_MAX_FREQ	48000000 diff --git a/drivers/spi/tegra_spi.c b/drivers/spi/tegra_spi.c index 4a3e7996f..18b00b2ca 100644 --- a/drivers/spi/tegra_spi.c +++ b/drivers/spi/tegra_spi.c @@ -54,7 +54,7 @@ static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave)  int spi_cs_is_valid(unsigned int bus, unsigned int cs)  { -	/* Tegra2 SPI-Flash - only 1 device ('bus/cs') */ +	/* Tegra20 SPI-Flash - only 1 device ('bus/cs') */  	if (bus != 0 || cs != 0)  		return 0;  	else @@ -72,9 +72,9 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  		return NULL;  	} -	if (max_hz > TEGRA2_SPI_MAX_FREQ) { +	if (max_hz > TEGRA_SPI_MAX_FREQ) {  		printf("SPI error: unsupported frequency %d Hz. Max frequency" -			" is %d Hz\n", max_hz, TEGRA2_SPI_MAX_FREQ); +			" is %d Hz\n", max_hz, TEGRA_SPI_MAX_FREQ);  		return NULL;  	} @@ -86,7 +86,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  	spi->slave.bus = bus;  	spi->slave.cs = cs;  	spi->freq = max_hz; -	spi->regs = (struct spi_tegra *)TEGRA2_SPI_BASE; +	spi->regs = (struct spi_tegra *)NV_PA_SPI_BASE;  	spi->mode = mode;  	return &spi->slave; @@ -130,7 +130,7 @@ int spi_claim_bus(struct spi_slave *slave)  	debug("spi_init: COMMAND = %08x\n", readl(®s->command));  	/* -	 * SPI pins on Tegra2 are muxed - change pinmux later due to UART +	 * SPI pins on Tegra20 are muxed - change pinmux later due to UART  	 * issue.  	 */  	pinmux_set_func(PINGRP_GMD, PMUX_FUNC_SFLASH); diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index e563c1905..52a4134f1 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -78,7 +78,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  				  unsigned int max_hz, unsigned int mode)  {  	struct xilinx_spi_slave *xilspi; -	struct xilinx_spi_reg *regs;  	if (!spi_cs_is_valid(bus, cs)) {  		printf("XILSPI error: %s: unsupported bus %d / cs %d\n", diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 87d1918cb..5bbdd368f 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -29,6 +29,8 @@ LIB	:= $(obj)libusb_gadget.o  ifdef CONFIG_USB_GADGET  COBJS-y += epautoconf.o config.o usbstring.o  COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o +COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o  endif  ifdef CONFIG_USB_ETHER  COBJS-y += ether.o epautoconf.o config.o usbstring.o diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c new file mode 100644 index 000000000..3ec4c65e7 --- /dev/null +++ b/drivers/usb/gadget/f_dfu.c @@ -0,0 +1,749 @@ +/* + * f_dfu.c -- Device Firmware Update USB function + * + * Copyright (C) 2012 Samsung Electronics + * authors: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + *          Lukasz Majewski <l.majewski@samsung.com> + * + * 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 <errno.h> +#include <common.h> +#include <malloc.h> + +#include <linux/usb/ch9.h> +#include <usbdescriptors.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> + +#include <dfu.h> +#include "f_dfu.h" + +struct f_dfu { +	struct usb_function		usb_function; + +	struct usb_descriptor_header	**function; +	struct usb_string		*strings; + +	/* when configured, we have one config */ +	u8				config; +	u8				altsetting; +	enum dfu_state			dfu_state; +	unsigned int			dfu_status; + +	/* Send/received block number is handy for data integrity check */ +	int                             blk_seq_num; +}; + +typedef int (*dfu_state_fn) (struct f_dfu *, +			     const struct usb_ctrlrequest *, +			     struct usb_gadget *, +			     struct usb_request *); + +static inline struct f_dfu *func_to_dfu(struct usb_function *f) +{ +	return container_of(f, struct f_dfu, usb_function); +} + +static const struct dfu_function_descriptor dfu_func = { +	.bLength =		sizeof dfu_func, +	.bDescriptorType =	DFU_DT_FUNC, +	.bmAttributes =		DFU_BIT_WILL_DETACH | +				DFU_BIT_MANIFESTATION_TOLERANT | +				DFU_BIT_CAN_UPLOAD | +				DFU_BIT_CAN_DNLOAD, +	.wDetachTimeOut =	0, +	.wTransferSize =	DFU_USB_BUFSIZ, +	.bcdDFUVersion =	__constant_cpu_to_le16(0x0110), +}; + +static struct usb_interface_descriptor dfu_intf_runtime = { +	.bLength =		sizeof dfu_intf_runtime, +	.bDescriptorType =	USB_DT_INTERFACE, +	.bNumEndpoints =	0, +	.bInterfaceClass =	USB_CLASS_APP_SPEC, +	.bInterfaceSubClass =	1, +	.bInterfaceProtocol =	1, +	/* .iInterface = DYNAMIC */ +}; + +static struct usb_descriptor_header *dfu_runtime_descs[] = { +	(struct usb_descriptor_header *) &dfu_intf_runtime, +	NULL, +}; + +static const struct usb_qualifier_descriptor dev_qualifier = { +	.bLength =		sizeof dev_qualifier, +	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER, +	.bcdUSB =		__constant_cpu_to_le16(0x0200), +	.bDeviceClass =		USB_CLASS_VENDOR_SPEC, +	.bNumConfigurations =	1, +}; + +static const char dfu_name[] = "Device Firmware Upgrade"; + +/* + * static strings, in UTF-8 + * + * dfu_generic configuration + */ +static struct usb_string strings_dfu_generic[] = { +	[0].s = dfu_name, +	{  }			/* end of list */ +}; + +static struct usb_gadget_strings stringtab_dfu_generic = { +	.language	= 0x0409,	/* en-us */ +	.strings	= strings_dfu_generic, +}; + +static struct usb_gadget_strings *dfu_generic_strings[] = { +	&stringtab_dfu_generic, +	NULL, +}; + +/* + * usb_function specific + */ +static struct usb_gadget_strings stringtab_dfu = { +	.language	= 0x0409,	/* en-us */ +	/* +	 * .strings +	 * +	 * assigned during initialization, +	 * depends on number of flash entities +	 * +	 */ +}; + +static struct usb_gadget_strings *dfu_strings[] = { +	&stringtab_dfu, +	NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) +{ +	struct f_dfu *f_dfu = req->context; + +	dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf, +		  req->length, f_dfu->blk_seq_num); + +	if (req->length == 0) +		puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n"); +} + +static void handle_getstatus(struct usb_request *req) +{ +	struct dfu_status *dstat = (struct dfu_status *)req->buf; +	struct f_dfu *f_dfu = req->context; + +	switch (f_dfu->dfu_state) { +	case DFU_STATE_dfuDNLOAD_SYNC: +	case DFU_STATE_dfuDNBUSY: +		f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; +		break; +	case DFU_STATE_dfuMANIFEST_SYNC: +		break; +	default: +		break; +	} + +	/* send status response */ +	dstat->bStatus = f_dfu->dfu_status; +	dstat->bState = f_dfu->dfu_state; +	dstat->iString = 0; +} + +static void handle_getstate(struct usb_request *req) +{ +	struct f_dfu *f_dfu = req->context; + +	((u8 *)req->buf)[0] = f_dfu->dfu_state; +	req->actual = sizeof(u8); +} + +static inline void to_dfu_mode(struct f_dfu *f_dfu) +{ +	f_dfu->usb_function.strings = dfu_strings; +	f_dfu->usb_function.hs_descriptors = f_dfu->function; +} + +static inline void to_runtime_mode(struct f_dfu *f_dfu) +{ +	f_dfu->usb_function.strings = NULL; +	f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; +} + +static int handle_upload(struct usb_request *req, u16 len) +{ +	struct f_dfu *f_dfu = req->context; + +	return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf, +			req->length, f_dfu->blk_seq_num); +} + +static int handle_dnload(struct usb_gadget *gadget, u16 len) +{ +	struct usb_composite_dev *cdev = get_gadget_data(gadget); +	struct usb_request *req = cdev->req; +	struct f_dfu *f_dfu = req->context; + +	if (len == 0) +		f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + +	req->complete = dnload_request_complete; + +	return len; +} + +/*-------------------------------------------------------------------------*/ +/* DFU state machine  */ +static int state_app_idle(struct f_dfu *f_dfu, +			  const struct usb_ctrlrequest *ctrl, +			  struct usb_gadget *gadget, +			  struct usb_request *req) +{ +	int value = 0; + +	switch (ctrl->bRequest) { +	case USB_REQ_DFU_GETSTATUS: +		handle_getstatus(req); +		value = RET_STAT_LEN; +		break; +	case USB_REQ_DFU_GETSTATE: +		handle_getstate(req); +		break; +	case USB_REQ_DFU_DETACH: +		f_dfu->dfu_state = DFU_STATE_appDETACH; +		to_dfu_mode(f_dfu); +		f_dfu->dfu_state = DFU_STATE_dfuIDLE; +		value = RET_ZLP; +		break; +	default: +		value = RET_STALL; +		break; +	} + +	return value; +} + +static int state_app_detach(struct f_dfu *f_dfu, +			    const struct usb_ctrlrequest *ctrl, +			    struct usb_gadget *gadget, +			    struct usb_request *req) +{ +	int value = 0; + +	switch (ctrl->bRequest) { +	case USB_REQ_DFU_GETSTATUS: +		handle_getstatus(req); +		value = RET_STAT_LEN; +		break; +	case USB_REQ_DFU_GETSTATE: +		handle_getstate(req); +		break; +	default: +		f_dfu->dfu_state = DFU_STATE_appIDLE; +		value = RET_STALL; +		break; +	} + +	return value; +} + +static int state_dfu_idle(struct f_dfu *f_dfu, +			  const struct usb_ctrlrequest *ctrl, +			  struct usb_gadget *gadget, +			  struct usb_request *req) +{ +	u16 w_value = le16_to_cpu(ctrl->wValue); +	u16 len = le16_to_cpu(ctrl->wLength); +	int value = 0; + +	switch (ctrl->bRequest) { +	case USB_REQ_DFU_DNLOAD: +		if (len == 0) { +			f_dfu->dfu_state = DFU_STATE_dfuERROR; +			value = RET_STALL; +			break; +		} +		f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; +		f_dfu->blk_seq_num = w_value; +		value = handle_dnload(gadget, len); +		break; +	case USB_REQ_DFU_UPLOAD: +		f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; +		f_dfu->blk_seq_num = 0; +		value = handle_upload(req, len); +		break; +	case USB_REQ_DFU_ABORT: +		/* no zlp? */ +		value = RET_ZLP; +		break; +	case USB_REQ_DFU_GETSTATUS: +		handle_getstatus(req); +		value = RET_STAT_LEN; +		break; +	case USB_REQ_DFU_GETSTATE: +		handle_getstate(req); +		break; +	case USB_REQ_DFU_DETACH: +		/* +		 * Proprietary extension: 'detach' from idle mode and +		 * get back to runtime mode in case of USB Reset.  As +		 * much as I dislike this, we just can't use every USB +		 * bus reset to switch back to runtime mode, since at +		 * least the Linux USB stack likes to send a number of +		 * resets in a row :( +		 */ +		f_dfu->dfu_state = +			DFU_STATE_dfuMANIFEST_WAIT_RST; +		to_runtime_mode(f_dfu); +		f_dfu->dfu_state = DFU_STATE_appIDLE; +		break; +	default: +		f_dfu->dfu_state = DFU_STATE_dfuERROR; +		value = RET_STALL; +		break; +	} + +	return value; +} + +static int state_dfu_dnload_sync(struct f_dfu *f_dfu, +				 const struct usb_ctrlrequest *ctrl, +				 struct usb_gadget *gadget, +				 struct usb_request *req) +{ +	int value = 0; + +	switch (ctrl->bRequest) { +	case USB_REQ_DFU_GETSTATUS: +		handle_getstatus(req); +		value = RET_STAT_LEN; +		break; +	case USB_REQ_DFU_GETSTATE: +		handle_getstate(req); +		break; +	default: +		f_dfu->dfu_state = DFU_STATE_dfuERROR; +		value = RET_STALL; +		break; +	} + +	return value; +} + +static int state_dfu_dnbusy(struct f_dfu *f_dfu, +			    const struct usb_ctrlrequest *ctrl, +			    struct usb_gadget *gadget, +			    struct usb_request *req) +{ +	int value = 0; + +	switch (ctrl->bRequest) { +	case USB_REQ_DFU_GETSTATUS: +		handle_getstatus(req); +		value = RET_STAT_LEN; +		break; +	default: +		f_dfu->dfu_state = DFU_STATE_dfuERROR; +		value = RET_STALL; +		break; +	} + +	return value; +} + +static int state_dfu_dnload_idle(struct f_dfu *f_dfu, +				 const struct usb_ctrlrequest *ctrl, +				 struct usb_gadget *gadget, +				 struct usb_request *req) +{ +	u16 w_value = le16_to_cpu(ctrl->wValue); +	u16 len = le16_to_cpu(ctrl->wLength); +	int value = 0; + +	switch (ctrl->bRequest) { +	case USB_REQ_DFU_DNLOAD: +		f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; +		f_dfu->blk_seq_num = w_value; +		value = handle_dnload(gadget, len); +		break; +	case USB_REQ_DFU_ABORT: +		f_dfu->dfu_state = DFU_STATE_dfuIDLE; +		value = RET_ZLP; +		break; +	case USB_REQ_DFU_GETSTATUS: +		handle_getstatus(req); +		value = RET_STAT_LEN; +		break; +	case USB_REQ_DFU_GETSTATE: +		handle_getstate(req); +		break; +	default: +		f_dfu->dfu_state = DFU_STATE_dfuERROR; +		value = RET_STALL; +		break; +	} + +	return value; +} + +static int state_dfu_manifest_sync(struct f_dfu *f_dfu, +				   const struct usb_ctrlrequest *ctrl, +				   struct usb_gadget *gadget, +				   struct usb_request *req) +{ +	int value = 0; + +	switch (ctrl->bRequest) { +	case USB_REQ_DFU_GETSTATUS: +		/* We're MainfestationTolerant */ +		f_dfu->dfu_state = DFU_STATE_dfuIDLE; +		handle_getstatus(req); +		f_dfu->blk_seq_num = 0; +		value = RET_STAT_LEN; +		break; +	case USB_REQ_DFU_GETSTATE: +		handle_getstate(req); +		break; +	default: +		f_dfu->dfu_state = DFU_STATE_dfuERROR; +		value = RET_STALL; +		break; +	} + +	return value; +} + +static int state_dfu_upload_idle(struct f_dfu *f_dfu, +				 const struct usb_ctrlrequest *ctrl, +				 struct usb_gadget *gadget, +				 struct usb_request *req) +{ +	u16 w_value = le16_to_cpu(ctrl->wValue); +	u16 len = le16_to_cpu(ctrl->wLength); +	int value = 0; + +	switch (ctrl->bRequest) { +	case USB_REQ_DFU_UPLOAD: +		/* state transition if less data then requested */ +		f_dfu->blk_seq_num = w_value; +		value = handle_upload(req, len); +		if (value >= 0 && value < len) +			f_dfu->dfu_state = DFU_STATE_dfuIDLE; +		break; +	case USB_REQ_DFU_ABORT: +		f_dfu->dfu_state = DFU_STATE_dfuIDLE; +		/* no zlp? */ +		value = RET_ZLP; +		break; +	case USB_REQ_DFU_GETSTATUS: +		handle_getstatus(req); +		value = RET_STAT_LEN; +		break; +	case USB_REQ_DFU_GETSTATE: +		handle_getstate(req); +		break; +	default: +		f_dfu->dfu_state = DFU_STATE_dfuERROR; +		value = RET_STALL; +		break; +	} + +	return value; +} + +static int state_dfu_error(struct f_dfu *f_dfu, +				 const struct usb_ctrlrequest *ctrl, +				 struct usb_gadget *gadget, +				 struct usb_request *req) +{ +	int value = 0; + +	switch (ctrl->bRequest) { +	case USB_REQ_DFU_GETSTATUS: +		handle_getstatus(req); +		value = RET_STAT_LEN; +		break; +	case USB_REQ_DFU_GETSTATE: +		handle_getstate(req); +		break; +	case USB_REQ_DFU_CLRSTATUS: +		f_dfu->dfu_state = DFU_STATE_dfuIDLE; +		f_dfu->dfu_status = DFU_STATUS_OK; +		/* no zlp? */ +		value = RET_ZLP; +		break; +	default: +		f_dfu->dfu_state = DFU_STATE_dfuERROR; +		value = RET_STALL; +		break; +	} + +	return value; +} + +static dfu_state_fn dfu_state[] = { +	state_app_idle,          /* DFU_STATE_appIDLE */ +	state_app_detach,        /* DFU_STATE_appDETACH */ +	state_dfu_idle,          /* DFU_STATE_dfuIDLE */ +	state_dfu_dnload_sync,   /* DFU_STATE_dfuDNLOAD_SYNC */ +	state_dfu_dnbusy,        /* DFU_STATE_dfuDNBUSY */ +	state_dfu_dnload_idle,   /* DFU_STATE_dfuDNLOAD_IDLE */ +	state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */ +	NULL,                    /* DFU_STATE_dfuMANIFEST */ +	NULL,                    /* DFU_STATE_dfuMANIFEST_WAIT_RST */ +	state_dfu_upload_idle,   /* DFU_STATE_dfuUPLOAD_IDLE */ +	state_dfu_error          /* DFU_STATE_dfuERROR */ +}; + +static int +dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ +	struct usb_gadget *gadget = f->config->cdev->gadget; +	struct usb_request *req = f->config->cdev->req; +	struct f_dfu *f_dfu = f->config->cdev->req->context; +	u16 len = le16_to_cpu(ctrl->wLength); +	u16 w_value = le16_to_cpu(ctrl->wValue); +	int value = 0; +	u8 req_type = ctrl->bRequestType & USB_TYPE_MASK; + +	debug("w_value: 0x%x len: 0x%x\n", w_value, len); +	debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n", +	       req_type, ctrl->bRequest, f_dfu->dfu_state); + +	if (req_type == USB_TYPE_STANDARD) { +		if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR && +		    (w_value >> 8) == DFU_DT_FUNC) { +			value = min(len, (u16) sizeof(dfu_func)); +			memcpy(req->buf, &dfu_func, value); +		} +	} else /* DFU specific request */ +		value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req); + +	if (value >= 0) { +		req->length = value; +		req->zero = value < len; +		value = usb_ep_queue(gadget->ep0, req, 0); +		if (value < 0) { +			debug("ep_queue --> %d\n", value); +			req->status = 0; +		} +	} + +	return value; +} + +/*-------------------------------------------------------------------------*/ + +static int +dfu_prepare_strings(struct f_dfu *f_dfu, int n) +{ +	struct dfu_entity *de = NULL; +	int i = 0; + +	f_dfu->strings = calloc(sizeof(struct usb_string), n + 1); +	if (!f_dfu->strings) +		goto enomem; + +	for (i = 0; i < n; ++i) { +		de = dfu_get_entity(i); +		f_dfu->strings[i].s = de->name; +	} + +	f_dfu->strings[i].id = 0; +	f_dfu->strings[i].s = NULL; + +	return 0; + +enomem: +	while (i) +		f_dfu->strings[--i].s = NULL; + +	free(f_dfu->strings); + +	return -ENOMEM; +} + +static int dfu_prepare_function(struct f_dfu *f_dfu, int n) +{ +	struct usb_interface_descriptor *d; +	int i = 0; + +	f_dfu->function = calloc(sizeof(struct usb_descriptor_header *), n); +	if (!f_dfu->function) +		goto enomem; + +	for (i = 0; i < n; ++i) { +		d = calloc(sizeof(*d), 1); +		if (!d) +			goto enomem; + +		d->bLength =		sizeof(*d); +		d->bDescriptorType =	USB_DT_INTERFACE; +		d->bAlternateSetting =	i; +		d->bNumEndpoints =	0; +		d->bInterfaceClass =	USB_CLASS_APP_SPEC; +		d->bInterfaceSubClass =	1; +		d->bInterfaceProtocol =	2; + +		f_dfu->function[i] = (struct usb_descriptor_header *)d; +	} +	f_dfu->function[i] = NULL; + +	return 0; + +enomem: +	while (i) { +		free(f_dfu->function[--i]); +		f_dfu->function[i] = NULL; +	} +	free(f_dfu->function); + +	return -ENOMEM; +} + +static int dfu_bind(struct usb_configuration *c, struct usb_function *f) +{ +	struct usb_composite_dev *cdev = c->cdev; +	struct f_dfu *f_dfu = func_to_dfu(f); +	int alt_num = dfu_get_alt_number(); +	int rv, id, i; + +	id = usb_interface_id(c, f); +	if (id < 0) +		return id; +	dfu_intf_runtime.bInterfaceNumber = id; + +	f_dfu->dfu_state = DFU_STATE_appIDLE; +	f_dfu->dfu_status = DFU_STATUS_OK; + +	rv = dfu_prepare_function(f_dfu, alt_num); +	if (rv) +		goto error; + +	rv = dfu_prepare_strings(f_dfu, alt_num); +	if (rv) +		goto error; +	for (i = 0; i < alt_num; i++) { +		id = usb_string_id(cdev); +		if (id < 0) +			return id; +		f_dfu->strings[i].id = id; +		((struct usb_interface_descriptor *)f_dfu->function[i]) +			->iInterface = id; +	} + +	stringtab_dfu.strings = f_dfu->strings; + +	cdev->req->context = f_dfu; + +error: +	return rv; +} + +static void dfu_unbind(struct usb_configuration *c, struct usb_function *f) +{ +	struct f_dfu *f_dfu = func_to_dfu(f); +	int alt_num = dfu_get_alt_number(); +	int i; + +	if (f_dfu->strings) { +		i = alt_num; +		while (i) +			f_dfu->strings[--i].s = NULL; + +		free(f_dfu->strings); +	} + +	if (f_dfu->function) { +		i = alt_num; +		while (i) { +			free(f_dfu->function[--i]); +			f_dfu->function[i] = NULL; +		} +		free(f_dfu->function); +	} + +	free(f_dfu); +} + +static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ +	struct f_dfu *f_dfu = func_to_dfu(f); + +	debug("%s: intf:%d alt:%d\n", __func__, intf, alt); + +	f_dfu->altsetting = alt; + +	return 0; +} + +/* TODO: is this really what we need here? */ +static void dfu_disable(struct usb_function *f) +{ +	struct f_dfu *f_dfu = func_to_dfu(f); +	if (f_dfu->config == 0) +		return; + +	debug("%s: reset config\n", __func__); + +	f_dfu->config = 0; +} + +static int dfu_bind_config(struct usb_configuration *c) +{ +	struct f_dfu *f_dfu; +	int status; + +	f_dfu = calloc(sizeof(*f_dfu), 1); +	if (!f_dfu) +		return -ENOMEM; +	f_dfu->usb_function.name = "dfu"; +	f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; +	f_dfu->usb_function.bind = dfu_bind; +	f_dfu->usb_function.unbind = dfu_unbind; +	f_dfu->usb_function.set_alt = dfu_set_alt; +	f_dfu->usb_function.disable = dfu_disable; +	f_dfu->usb_function.strings = dfu_generic_strings, +	f_dfu->usb_function.setup = dfu_handle, + +	status = usb_add_function(c, &f_dfu->usb_function); +	if (status) +		free(f_dfu); + +	return status; +} + +int dfu_add(struct usb_configuration *c) +{ +	int id; + +	id = usb_string_id(c->cdev); +	if (id < 0) +		return id; +	strings_dfu_generic[0].id = id; +	dfu_intf_runtime.iInterface = id; + +	debug("%s: cdev: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__, +	       c->cdev, c->cdev->gadget, c->cdev->gadget->ep0); + +	return dfu_bind_config(c); +} diff --git a/drivers/usb/gadget/f_dfu.h b/drivers/usb/gadget/f_dfu.h new file mode 100644 index 000000000..023e1adac --- /dev/null +++ b/drivers/usb/gadget/f_dfu.h @@ -0,0 +1,100 @@ +/* + * f_dfu.h -- Device Firmware Update gadget + * + * Copyright (C) 2011-2012 Samsung Electronics + * author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + +#ifndef __F_DFU_H_ +#define __F_DFU_H_ + +#include <linux/compiler.h> +#include <linux/usb/composite.h> + +#define DFU_CONFIG_VAL			1 +#define DFU_DT_FUNC			0x21 + +#define DFU_BIT_WILL_DETACH		(0x1 << 3) +#define DFU_BIT_MANIFESTATION_TOLERANT	(0x1 << 2) +#define DFU_BIT_CAN_UPLOAD		(0x1 << 1) +#define DFU_BIT_CAN_DNLOAD		0x1 + +/* big enough to hold our biggest descriptor */ +#define DFU_USB_BUFSIZ			4096 + +#define USB_REQ_DFU_DETACH		0x00 +#define USB_REQ_DFU_DNLOAD		0x01 +#define USB_REQ_DFU_UPLOAD		0x02 +#define USB_REQ_DFU_GETSTATUS		0x03 +#define USB_REQ_DFU_CLRSTATUS		0x04 +#define USB_REQ_DFU_GETSTATE		0x05 +#define USB_REQ_DFU_ABORT		0x06 + +#define DFU_STATUS_OK			0x00 +#define DFU_STATUS_errTARGET		0x01 +#define DFU_STATUS_errFILE		0x02 +#define DFU_STATUS_errWRITE		0x03 +#define DFU_STATUS_errERASE		0x04 +#define DFU_STATUS_errCHECK_ERASED	0x05 +#define DFU_STATUS_errPROG		0x06 +#define DFU_STATUS_errVERIFY		0x07 +#define DFU_STATUS_errADDRESS		0x08 +#define DFU_STATUS_errNOTDONE		0x09 +#define DFU_STATUS_errFIRMWARE		0x0a +#define DFU_STATUS_errVENDOR		0x0b +#define DFU_STATUS_errUSBR		0x0c +#define DFU_STATUS_errPOR		0x0d +#define DFU_STATUS_errUNKNOWN		0x0e +#define DFU_STATUS_errSTALLEDPKT	0x0f + +#define RET_STALL			-1 +#define RET_ZLP				0 +#define RET_STAT_LEN			6 + +enum dfu_state { +	DFU_STATE_appIDLE		= 0, +	DFU_STATE_appDETACH		= 1, +	DFU_STATE_dfuIDLE		= 2, +	DFU_STATE_dfuDNLOAD_SYNC	= 3, +	DFU_STATE_dfuDNBUSY		= 4, +	DFU_STATE_dfuDNLOAD_IDLE	= 5, +	DFU_STATE_dfuMANIFEST_SYNC	= 6, +	DFU_STATE_dfuMANIFEST		= 7, +	DFU_STATE_dfuMANIFEST_WAIT_RST	= 8, +	DFU_STATE_dfuUPLOAD_IDLE	= 9, +	DFU_STATE_dfuERROR		= 10, +}; + +struct dfu_status { +	__u8				bStatus; +	__u8				bwPollTimeout[3]; +	__u8				bState; +	__u8				iString; +} __packed; + +struct dfu_function_descriptor { +	__u8				bLength; +	__u8				bDescriptorType; +	__u8				bmAttributes; +	__le16				wDetachTimeOut; +	__le16				wTransferSize; +	__le16				bcdDFUVersion; +} __packed; + +/* configuration-specific linkup */ +int dfu_add(struct usb_configuration *c); +#endif /* __F_DFU_H_ */ diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c new file mode 100644 index 000000000..7d87050df --- /dev/null +++ b/drivers/usb/gadget/g_dnl.c @@ -0,0 +1,202 @@ +/* + * g_dnl.c -- USB Downloader Gadget + * + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski  <l.majewski@samsung.com> + * + * 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 <errno.h> +#include <common.h> +#include <malloc.h> + +#include <mmc.h> +#include <part.h> + +#include <g_dnl.h> +#include "f_dfu.h" + +#include "gadget_chips.h" +#include "composite.c" + +/* + * One needs to define the following: + * CONFIG_G_DNL_VENDOR_NUM + * CONFIG_G_DNL_PRODUCT_NUM + * CONFIG_G_DNL_MANUFACTURER + * at e.g. ./include/configs/<board>.h + */ + +#define STRING_MANUFACTURER 25 +#define STRING_PRODUCT 2 +#define STRING_USBDOWN 2 +#define CONFIG_USBDOWNLOADER 2 + +#define DRIVER_VERSION		"usb_dnl 2.0" + +static const char shortname[] = "usb_dnl_"; +static const char product[] = "USB download gadget"; +static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER; + +static struct usb_device_descriptor device_desc = { +	.bLength = sizeof device_desc, +	.bDescriptorType = USB_DT_DEVICE, + +	.bcdUSB = __constant_cpu_to_le16(0x0200), +	.bDeviceClass = USB_CLASS_COMM, +	.bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/ + +	.idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM), +	.idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM), +	.iProduct = STRING_PRODUCT, +	.bNumConfigurations = 1, +}; + +/* static strings, in UTF-8 */ +static struct usb_string g_dnl_string_defs[] = { +	{ 0, manufacturer, }, +	{ 1, product, }, +}; + +static struct usb_gadget_strings g_dnl_string_tab = { +	.language = 0x0409, /* en-us */ +	.strings = g_dnl_string_defs, +}; + +static struct usb_gadget_strings *g_dnl_composite_strings[] = { +	&g_dnl_string_tab, +	NULL, +}; + +static int g_dnl_unbind(struct usb_composite_dev *cdev) +{ +	debug("%s\n", __func__); +	return 0; +} + +static int g_dnl_do_config(struct usb_configuration *c) +{ +	const char *s = c->cdev->driver->name; +	int ret = -1; + +	debug("%s: configuration: 0x%p composite dev: 0x%p\n", +	      __func__, c, c->cdev); + +	printf("GADGET DRIVER: %s\n", s); +	if (!strcmp(s, "usb_dnl_dfu")) +		ret = dfu_add(c); + +	return ret; +} + +static int g_dnl_config_register(struct usb_composite_dev *cdev) +{ +	static struct usb_configuration config = { +		.label = "usb_dnload", +		.bmAttributes =	USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, +		.bConfigurationValue =	CONFIG_USBDOWNLOADER, +		.iConfiguration =	STRING_USBDOWN, + +		.bind = g_dnl_do_config, +	}; + +	return usb_add_config(cdev, &config); +} + +static int g_dnl_bind(struct usb_composite_dev *cdev) +{ +	struct usb_gadget *gadget = cdev->gadget; +	int id, ret; +	int gcnum; + +	debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev); + +	id = usb_string_id(cdev); + +	if (id < 0) +		return id; +	g_dnl_string_defs[0].id = id; +	device_desc.iManufacturer = id; + +	id = usb_string_id(cdev); +	if (id < 0) +		return id; + +	g_dnl_string_defs[1].id = id; +	device_desc.iProduct = id; + +	ret = g_dnl_config_register(cdev); +	if (ret) +		goto error; + +	gcnum = usb_gadget_controller_number(gadget); + +	debug("gcnum: %d\n", gcnum); +	if (gcnum >= 0) +		device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); +	else { +		debug("%s: controller '%s' not recognized\n", +			shortname, gadget->name); +		device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); +	} + +	return 0; + + error: +	g_dnl_unbind(cdev); +	return -ENOMEM; +} + +static struct usb_composite_driver g_dnl_driver = { +	.name = NULL, +	.dev = &device_desc, +	.strings = g_dnl_composite_strings, + +	.bind = g_dnl_bind, +	.unbind = g_dnl_unbind, +}; + +int g_dnl_register(const char *type) +{ +	/* We only allow "dfu" atm, so 3 should be enough */ +	static char name[sizeof(shortname) + 3]; +	int ret; + +	if (!strcmp(type, "dfu")) { +		strcpy(name, shortname); +		strcat(name, type); +	} else { +		printf("%s: unknown command: %s\n", __func__, type); +		return -EINVAL; +	} + +	g_dnl_driver.name = name; + +	debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name); +	ret = usb_composite_register(&g_dnl_driver); + +	if (ret) { +		printf("%s: failed!, error: %d\n", __func__, ret); +		return ret; +	} + +	return 0; +} + +void g_dnl_unregister(void) +{ +	usb_composite_unregister(&g_dnl_driver); +} diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 6de91640d..bcb4662c4 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -28,6 +28,7 @@ LIB	:= $(obj)libusb_host.o  # ohci  COBJS-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o  COBJS-$(CONFIG_USB_ATMEL) += ohci-at91.o +COBJS-$(CONFIG_USB_OHCI_DA8XX) += ohci-da8xx.o  COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o  COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o  COBJS-$(CONFIG_USB_S3C64XX) += s3c64xx-hcd.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2a82a2912..392e28622 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -22,6 +22,7 @@   */  #include <common.h>  #include <asm/byteorder.h> +#include <asm/unaligned.h>  #include <usb.h>  #include <asm/io.h>  #include <malloc.h> @@ -163,7 +164,7 @@ static int ehci_reset(void)  #ifdef CONFIG_USB_EHCI_TXFIFO_THRESH  	cmd = ehci_readl(&hcor->or_txfilltuning); -	cmd &= ~TXFIFO_THRESH(0x3f); +	cmd &= ~TXFIFO_THRESH_MASK;  	cmd |= TXFIFO_THRESH(CONFIG_USB_EHCI_TXFIFO_THRESH);  	ehci_writel(&hcor->or_txfilltuning, cmd);  #endif @@ -183,10 +184,10 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)  	flush_dcache_range(addr, ALIGN(addr + sz, ARCH_DMA_MINALIGN));  	idx = 0; -	while (idx < 5) { +	while (idx < QT_BUFFER_CNT) {  		td->qt_buffer[idx] = cpu_to_hc32(addr);  		td->qt_buffer_hi[idx] = 0; -		next = (addr + 4096) & ~4095; +		next = (addr + EHCI_PAGE_SIZE) & ~(EHCI_PAGE_SIZE - 1);  		delta = next - addr;  		if (delta >= sz)  			break; @@ -195,7 +196,7 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)  		idx++;  	} -	if (idx == 5) { +	if (idx == QT_BUFFER_CNT) {  		printf("out of buffer pointers (%u bytes left)\n", sz);  		return -1;  	} @@ -208,13 +209,14 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  		   int length, struct devrequest *req)  {  	ALLOC_ALIGN_BUFFER(struct QH, qh, 1, USB_DMA_MINALIGN); -	ALLOC_ALIGN_BUFFER(struct qTD, qtd, 3, USB_DMA_MINALIGN); +	struct qTD *qtd; +	int qtd_count = 0;  	int qtd_counter = 0;  	volatile struct qTD *vtd;  	unsigned long ts;  	uint32_t *tdp; -	uint32_t endpt, token, usbsts; +	uint32_t endpt, maxpacket, token, usbsts;  	uint32_t c, toggle;  	uint32_t cmd;  	int timeout; @@ -229,8 +231,73 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  		      le16_to_cpu(req->value), le16_to_cpu(req->value),  		      le16_to_cpu(req->index)); +#define PKT_ALIGN	512 +	/* +	 * The USB transfer is split into qTD transfers. Eeach qTD transfer is +	 * described by a transfer descriptor (the qTD). The qTDs form a linked +	 * list with a queue head (QH). +	 * +	 * Each qTD transfer starts with a new USB packet, i.e. a packet cannot +	 * have its beginning in a qTD transfer and its end in the following +	 * one, so the qTD transfer lengths have to be chosen accordingly. +	 * +	 * Each qTD transfer uses up to QT_BUFFER_CNT data buffers, mapped to +	 * single pages. The first data buffer can start at any offset within a +	 * page (not considering the cache-line alignment issues), while the +	 * following buffers must be page-aligned. There is no alignment +	 * constraint on the size of a qTD transfer. +	 */ +	if (req != NULL) +		/* 1 qTD will be needed for SETUP, and 1 for ACK. */ +		qtd_count += 1 + 1; +	if (length > 0 || req == NULL) { +		/* +		 * Determine the qTD transfer size that will be used for the +		 * data payload (not considering the first qTD transfer, which +		 * may be longer or shorter, and the final one, which may be +		 * shorter). +		 * +		 * In order to keep each packet within a qTD transfer, the qTD +		 * transfer size is aligned to PKT_ALIGN, which is a multiple of +		 * wMaxPacketSize (except in some cases for interrupt transfers, +		 * see comment in submit_int_msg()). +		 * +		 * By default, i.e. if the input buffer is aligned to PKT_ALIGN, +		 * QT_BUFFER_CNT full pages will be used. +		 */ +		int xfr_sz = QT_BUFFER_CNT; +		/* +		 * However, if the input buffer is not aligned to PKT_ALIGN, the +		 * qTD transfer size will be one page shorter, and the first qTD +		 * data buffer of each transfer will be page-unaligned. +		 */ +		if ((uint32_t)buffer & (PKT_ALIGN - 1)) +			xfr_sz--; +		/* Convert the qTD transfer size to bytes. */ +		xfr_sz *= EHCI_PAGE_SIZE; +		/* +		 * Approximate by excess the number of qTDs that will be +		 * required for the data payload. The exact formula is way more +		 * complicated and saves at most 2 qTDs, i.e. a total of 128 +		 * bytes. +		 */ +		qtd_count += 2 + length / xfr_sz; +	} +/* + * Threshold value based on the worst-case total size of the allocated qTDs for + * a mass-storage transfer of 65535 blocks of 512 bytes. + */ +#if CONFIG_SYS_MALLOC_LEN <= 64 + 128 * 1024 +#warning CONFIG_SYS_MALLOC_LEN may be too small for EHCI +#endif +	qtd = memalign(USB_DMA_MINALIGN, qtd_count * sizeof(struct qTD)); +	if (qtd == NULL) { +		printf("unable to allocate TDs\n"); +		return -1; +	} +  	memset(qh, 0, sizeof(struct QH)); -	memset(qtd, 0, 3 * sizeof(*qtd)); +	memset(qtd, 0, qtd_count * sizeof(*qtd));  	toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); @@ -245,20 +312,18 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  	 * - qh_overlay.qt_altnext  	 */  	qh->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH); -	c = (usb_pipespeed(pipe) != USB_SPEED_HIGH && -	     usb_pipeendpoint(pipe) == 0) ? 1 : 0; -	endpt = (8 << 28) | -	    (c << 27) | -	    (usb_maxpacket(dev, pipe) << 16) | -	    (0 << 15) | -	    (1 << 14) | -	    (usb_pipespeed(pipe) << 12) | -	    (usb_pipeendpoint(pipe) << 8) | -	    (0 << 7) | (usb_pipedevice(pipe) << 0); +	c = usb_pipespeed(pipe) != USB_SPEED_HIGH && !usb_pipeendpoint(pipe); +	maxpacket = usb_maxpacket(dev, pipe); +	endpt = QH_ENDPT1_RL(8) | QH_ENDPT1_C(c) | +		QH_ENDPT1_MAXPKTLEN(maxpacket) | QH_ENDPT1_H(0) | +		QH_ENDPT1_DTC(QH_ENDPT1_DTC_DT_FROM_QTD) | +		QH_ENDPT1_EPS(usb_pipespeed(pipe)) | +		QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) | +		QH_ENDPT1_DEVADDR(usb_pipedevice(pipe));  	qh->qh_endpt1 = cpu_to_hc32(endpt); -	endpt = (1 << 30) | -	    (dev->portnr << 23) | -	    (dev->parent->devnum << 16) | (0 << 8) | (0 << 0); +	endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_PORTNUM(dev->portnr) | +		QH_ENDPT2_HUBADDR(dev->parent->devnum) | +		QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0);  	qh->qh_endpt2 = cpu_to_hc32(endpt);  	qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); @@ -276,12 +341,13 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  		 */  		qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);  		qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); -		token = (0 << 31) | -		    (sizeof(*req) << 16) | -		    (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0); +		token = QT_TOKEN_DT(0) | QT_TOKEN_TOTALBYTES(sizeof(*req)) | +			QT_TOKEN_IOC(0) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) | +			QT_TOKEN_PID(QT_TOKEN_PID_SETUP) | +			QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);  		qtd[qtd_counter].qt_token = cpu_to_hc32(token); -		if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req)) != 0) { -			printf("unable construct SETUP td\n"); +		if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req))) { +			printf("unable to construct SETUP TD\n");  			goto fail;  		}  		/* Update previous qTD! */ @@ -291,31 +357,71 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  	}  	if (length > 0 || req == NULL) { -		/* -		 * Setup request qTD (3.5 in ehci-r10.pdf) -		 * -		 *   qt_next ................ 03-00 H -		 *   qt_altnext ............. 07-04 H -		 *   qt_token ............... 0B-08 H -		 * -		 *   [ buffer, buffer_hi ] loaded with "buffer". -		 */ -		qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); -		qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); -		token = (toggle << 31) | -		    (length << 16) | -		    ((req == NULL ? 1 : 0) << 15) | -		    (0 << 12) | -		    (3 << 10) | -		    ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0); -		qtd[qtd_counter].qt_token = cpu_to_hc32(token); -		if (ehci_td_buffer(&qtd[qtd_counter], buffer, length) != 0) { -			printf("unable construct DATA td\n"); -			goto fail; -		} -		/* Update previous qTD! */ -		*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); -		tdp = &qtd[qtd_counter++].qt_next; +		uint8_t *buf_ptr = buffer; +		int left_length = length; + +		do { +			/* +			 * Determine the size of this qTD transfer. By default, +			 * QT_BUFFER_CNT full pages can be used. +			 */ +			int xfr_bytes = QT_BUFFER_CNT * EHCI_PAGE_SIZE; +			/* +			 * However, if the input buffer is not page-aligned, the +			 * portion of the first page before the buffer start +			 * offset within that page is unusable. +			 */ +			xfr_bytes -= (uint32_t)buf_ptr & (EHCI_PAGE_SIZE - 1); +			/* +			 * In order to keep each packet within a qTD transfer, +			 * align the qTD transfer size to PKT_ALIGN. +			 */ +			xfr_bytes &= ~(PKT_ALIGN - 1); +			/* +			 * This transfer may be shorter than the available qTD +			 * transfer size that has just been computed. +			 */ +			xfr_bytes = min(xfr_bytes, left_length); + +			/* +			 * Setup request qTD (3.5 in ehci-r10.pdf) +			 * +			 *   qt_next ................ 03-00 H +			 *   qt_altnext ............. 07-04 H +			 *   qt_token ............... 0B-08 H +			 * +			 *   [ buffer, buffer_hi ] loaded with "buffer". +			 */ +			qtd[qtd_counter].qt_next = +					cpu_to_hc32(QT_NEXT_TERMINATE); +			qtd[qtd_counter].qt_altnext = +					cpu_to_hc32(QT_NEXT_TERMINATE); +			token = QT_TOKEN_DT(toggle) | +				QT_TOKEN_TOTALBYTES(xfr_bytes) | +				QT_TOKEN_IOC(req == NULL) | QT_TOKEN_CPAGE(0) | +				QT_TOKEN_CERR(3) | +				QT_TOKEN_PID(usb_pipein(pipe) ? +					QT_TOKEN_PID_IN : QT_TOKEN_PID_OUT) | +				QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE); +			qtd[qtd_counter].qt_token = cpu_to_hc32(token); +			if (ehci_td_buffer(&qtd[qtd_counter], buf_ptr, +						xfr_bytes)) { +				printf("unable to construct DATA TD\n"); +				goto fail; +			} +			/* Update previous qTD! */ +			*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); +			tdp = &qtd[qtd_counter++].qt_next; +			/* +			 * Data toggle has to be adjusted since the qTD transfer +			 * size is not always an even multiple of +			 * wMaxPacketSize. +			 */ +			if ((xfr_bytes / maxpacket) & 1) +				toggle ^= 1; +			buf_ptr += xfr_bytes; +			left_length -= xfr_bytes; +		} while (left_length > 0);  	}  	if (req != NULL) { @@ -328,12 +434,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  		 */  		qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);  		qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); -		token = (toggle << 31) | -		    (0 << 16) | -		    (1 << 15) | -		    (0 << 12) | -		    (3 << 10) | -		    ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0); +		token = QT_TOKEN_DT(1) | QT_TOKEN_TOTALBYTES(0) | +			QT_TOKEN_IOC(1) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) | +			QT_TOKEN_PID(usb_pipein(pipe) ? +				QT_TOKEN_PID_OUT : QT_TOKEN_PID_IN) | +			QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);  		qtd[qtd_counter].qt_token = cpu_to_hc32(token);  		/* Update previous qTD! */  		*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); @@ -346,7 +451,8 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  	flush_dcache_range((uint32_t)qh_list,  		ALIGN_END_ADDR(struct QH, qh_list, 1));  	flush_dcache_range((uint32_t)qh, ALIGN_END_ADDR(struct QH, qh, 1)); -	flush_dcache_range((uint32_t)qtd, ALIGN_END_ADDR(struct qTD, qtd, 3)); +	flush_dcache_range((uint32_t)qtd, +			   ALIGN_END_ADDR(struct qTD, qtd, qtd_count));  	/* Set async. queue head pointer. */  	ehci_writel(&hcor->or_asynclistaddr, (uint32_t)qh_list); @@ -359,10 +465,10 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  	cmd |= CMD_ASE;  	ehci_writel(&hcor->or_usbcmd, cmd); -	ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS, +	ret = handshake((uint32_t *)&hcor->or_usbsts, STS_ASS, STS_ASS,  			100 * 1000);  	if (ret < 0) { -		printf("EHCI fail timeout STD_ASS set\n"); +		printf("EHCI fail timeout STS_ASS set\n");  		goto fail;  	} @@ -377,10 +483,10 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  		invalidate_dcache_range((uint32_t)qh,  			ALIGN_END_ADDR(struct QH, qh, 1));  		invalidate_dcache_range((uint32_t)qtd, -			ALIGN_END_ADDR(struct qTD, qtd, 3)); +			ALIGN_END_ADDR(struct qTD, qtd, qtd_count));  		token = hc32_to_cpu(vtd->qt_token); -		if (!(token & 0x80)) +		if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE))  			break;  		WATCHDOG_RESET();  	} while (get_timer(ts) < timeout); @@ -398,50 +504,50 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  		ALIGN((uint32_t)buffer + length, ARCH_DMA_MINALIGN));  	/* Check that the TD processing happened */ -	if (token & 0x80) { +	if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)  		printf("EHCI timed out on TD - token=%#x\n", token); -	}  	/* Disable async schedule. */  	cmd = ehci_readl(&hcor->or_usbcmd);  	cmd &= ~CMD_ASE;  	ehci_writel(&hcor->or_usbcmd, cmd); -	ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0, +	ret = handshake((uint32_t *)&hcor->or_usbsts, STS_ASS, 0,  			100 * 1000);  	if (ret < 0) { -		printf("EHCI fail timeout STD_ASS reset\n"); +		printf("EHCI fail timeout STS_ASS reset\n");  		goto fail;  	}  	token = hc32_to_cpu(qh->qh_overlay.qt_token); -	if (!(token & 0x80)) { +	if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) {  		debug("TOKEN=%#x\n", token); -		switch (token & 0xfc) { +		switch (QT_TOKEN_GET_STATUS(token) & +			~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) {  		case 0: -			toggle = token >> 31; +			toggle = QT_TOKEN_GET_DT(token);  			usb_settoggle(dev, usb_pipeendpoint(pipe),  				       usb_pipeout(pipe), toggle);  			dev->status = 0;  			break; -		case 0x40: +		case QT_TOKEN_STATUS_HALTED:  			dev->status = USB_ST_STALLED;  			break; -		case 0xa0: -		case 0x20: +		case QT_TOKEN_STATUS_ACTIVE | QT_TOKEN_STATUS_DATBUFERR: +		case QT_TOKEN_STATUS_DATBUFERR:  			dev->status = USB_ST_BUF_ERR;  			break; -		case 0x50: -		case 0x10: +		case QT_TOKEN_STATUS_HALTED | QT_TOKEN_STATUS_BABBLEDET: +		case QT_TOKEN_STATUS_BABBLEDET:  			dev->status = USB_ST_BABBLE_DET;  			break;  		default:  			dev->status = USB_ST_CRC_ERR; -			if ((token & 0x40) == 0x40) +			if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_HALTED)  				dev->status |= USB_ST_STALLED;  			break;  		} -		dev->act_len = length - ((token >> 16) & 0x7fff); +		dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token);  	} else {  		dev->act_len = 0;  		debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", @@ -450,9 +556,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  		      ehci_readl(&hcor->or_portsc[1]));  	} +	free(qtd);  	return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;  fail: +	free(qtd);  	return -1;  } @@ -499,12 +607,14 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  		case USB_DT_DEVICE:  			debug("USB_DT_DEVICE request\n");  			srcptr = &descriptor.device; -			srclen = 0x12; +			srclen = descriptor.device.bLength;  			break;  		case USB_DT_CONFIG:  			debug("USB_DT_CONFIG config\n");  			srcptr = &descriptor.config; -			srclen = 0x19; +			srclen = descriptor.config.bLength + +					descriptor.interface.bLength + +					descriptor.endpoint.bLength;  			break;  		case USB_DT_STRING:  			debug("USB_DT_STRING config\n"); @@ -539,7 +649,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  		case USB_DT_HUB:  			debug("USB_DT_HUB config\n");  			srcptr = &descriptor.hub; -			srclen = 0x8; +			srclen = descriptor.hub.bLength;  			break;  		default:  			debug("unknown value %x\n", le16_to_cpu(req->value)); @@ -577,13 +687,13 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  			tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;  		if (ehci_is_TDI()) { -			switch ((reg >> 26) & 3) { -			case 0: +			switch (PORTSC_PSPD(reg)) { +			case PORTSC_PSPD_FS:  				break; -			case 1: +			case PORTSC_PSPD_LS:  				tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8;  				break; -			case 2: +			case PORTSC_PSPD_HS:  			default:  				tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;  				break; @@ -729,36 +839,40 @@ int usb_lowlevel_init(void)  	uint32_t reg;  	uint32_t cmd; -	if (ehci_hcd_init() != 0) +	if (ehci_hcd_init())  		return -1;  	/* EHCI spec section 4.1 */ -	if (ehci_reset() != 0) +	if (ehci_reset())  		return -1;  #if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) -	if (ehci_hcd_init() != 0) +	if (ehci_hcd_init())  		return -1;  #endif  	/* Set head of reclaim list */  	memset(qh_list, 0, sizeof(*qh_list));  	qh_list->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH); -	qh_list->qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12)); +	qh_list->qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) | +						QH_ENDPT1_EPS(USB_SPEED_HIGH));  	qh_list->qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE);  	qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);  	qh_list->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); -	qh_list->qh_overlay.qt_token = cpu_to_hc32(0x40); +	qh_list->qh_overlay.qt_token = +			cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED));  	reg = ehci_readl(&hccr->cr_hcsparams);  	descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);  	printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts);  	/* Port Indicators */  	if (HCS_INDICATOR(reg)) -		descriptor.hub.wHubCharacteristics |= 0x80; +		put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) +				| 0x80, &descriptor.hub.wHubCharacteristics);  	/* Port Power Control */  	if (HCS_PPC(reg)) -		descriptor.hub.wHubCharacteristics |= 0x01; +		put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) +				| 0x01, &descriptor.hub.wHubCharacteristics);  	/* Start the host controller. */  	cmd = ehci_readl(&hcor->or_usbcmd); @@ -808,7 +922,7 @@ submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,  	}  	if (usb_pipedevice(pipe) == rootdev) { -		if (rootdev == 0) +		if (!rootdev)  			dev->speed = USB_SPEED_HIGH;  		return ehci_submit_root(dev, pipe, buffer, length, setup);  	} @@ -819,8 +933,24 @@ int  submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,  	       int length, int interval)  { -  	debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",  	      dev, pipe, buffer, length, interval); + +	/* +	 * Interrupt transfers requiring several transactions are not supported +	 * because bInterval is ignored. +	 * +	 * Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2 +	 * <= PKT_ALIGN if several qTDs are required, while the USB +	 * specification does not constrain this for interrupt transfers. That +	 * means that ehci_submit_async() would support interrupt transfers +	 * requiring several transactions only as long as the transfer size does +	 * not require more than a single qTD. +	 */ +	if (length > usb_maxpacket(dev, pipe)) { +		printf("%s: Interrupt transfers requiring several transactions " +			"are not supported.\n", __func__); +		return -1; +	}  	return ehci_submit_async(dev, pipe, buffer, length, NULL);  } diff --git a/drivers/usb/host/ehci-mxs.c b/drivers/usb/host/ehci-mxs.c index e1bd37ec8..6e21669d5 100644 --- a/drivers/usb/host/ehci-mxs.c +++ b/drivers/usb/host/ehci-mxs.c @@ -23,7 +23,7 @@  #include <asm/io.h>  #include <asm/arch/regs-common.h>  #include <asm/arch/regs-base.h> -#include <asm/arch/regs-clkctrl.h> +#include <asm/arch/regs-clkctrl-mx28.h>  #include <asm/arch/regs-usb.h>  #include <asm/arch/regs-usbphy.h> @@ -39,8 +39,8 @@  #endif  static struct ehci_mxs { -	struct mx28_usb_regs	*usb_regs; -	struct mx28_usbphy_regs	*phy_regs; +	struct mxs_usb_regs	*usb_regs; +	struct mxs_usbphy_regs	*phy_regs;  } ehci_mxs;  int mxs_ehci_get_port(struct ehci_mxs *mxs_usb, int port) @@ -60,8 +60,8 @@ int mxs_ehci_get_port(struct ehci_mxs *mxs_usb, int port)  		return -1;  	} -	mxs_usb->usb_regs = (struct mx28_usb_regs *)usb_base; -	mxs_usb->phy_regs = (struct mx28_usbphy_regs *)phy_base; +	mxs_usb->usb_regs = (struct mxs_usb_regs *)usb_base; +	mxs_usb->phy_regs = (struct mxs_usbphy_regs *)phy_base;  	return 0;  } @@ -75,10 +75,10 @@ int ehci_hcd_init(void)  	int ret;  	uint32_t usb_base, cap_base; -	struct mx28_register_32 *digctl_ctrl = -		(struct mx28_register_32 *)HW_DIGCTL_CTRL; -	struct mx28_clkctrl_regs *clkctrl_regs = -		(struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE; +	struct mxs_register_32 *digctl_ctrl = +		(struct mxs_register_32 *)HW_DIGCTL_CTRL; +	struct mxs_clkctrl_regs *clkctrl_regs = +		(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;  	ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT);  	if (ret) @@ -119,10 +119,10 @@ int ehci_hcd_stop(void)  {  	int ret;  	uint32_t tmp; -	struct mx28_register_32 *digctl_ctrl = -		(struct mx28_register_32 *)HW_DIGCTL_CTRL; -	struct mx28_clkctrl_regs *clkctrl_regs = -		(struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE; +	struct mxs_register_32 *digctl_ctrl = +		(struct mxs_register_32 *)HW_DIGCTL_CTRL; +	struct mxs_clkctrl_regs *clkctrl_regs = +		(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;  	ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT);  	if (ret) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index cc00ce428..39acdf965 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -68,7 +68,7 @@ struct ehci_hcor {  #define CMD_RESET	(1 << 1)		/* reset HC not bus */  #define CMD_RUN		(1 << 0)		/* start/stop HC */  	uint32_t or_usbsts; -#define	STD_ASS		(1 << 15) +#define STS_ASS		(1 << 15)  #define STS_HALT	(1 << 12)  	uint32_t or_usbintr;  #define INTR_UE         (1 << 0)                /* USB interrupt enable */ @@ -83,11 +83,16 @@ struct ehci_hcor {  	uint32_t _reserved_0_;  	uint32_t or_burstsize;  	uint32_t or_txfilltuning; +#define TXFIFO_THRESH_MASK		(0x3f << 16)  #define TXFIFO_THRESH(p)		((p & 0x3f) << 16)  	uint32_t _reserved_1_[6];  	uint32_t or_configflag;  #define FLAG_CF		(1 << 0)	/* true:  we'll support "high speed" */  	uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS]; +#define PORTSC_PSPD(x)		(((x) >> 26) & 0x3) +#define PORTSC_PSPD_FS			0x0 +#define PORTSC_PSPD_LS			0x1 +#define PORTSC_PSPD_HS			0x2  	uint32_t or_systune;  } __attribute__ ((packed, aligned(4))); @@ -171,16 +176,40 @@ struct usb_linux_config_descriptor {  /* Queue Element Transfer Descriptor (qTD). */  struct qTD {  	/* this part defined by EHCI spec */ -	uint32_t qt_next;		/* see EHCI 3.5.1 */ +	uint32_t qt_next;			/* see EHCI 3.5.1 */  #define	QT_NEXT_TERMINATE	1 -	uint32_t qt_altnext;		/* see EHCI 3.5.2 */ -	uint32_t qt_token;		/* see EHCI 3.5.3 */ -	uint32_t qt_buffer[5];		/* see EHCI 3.5.4 */ -	uint32_t qt_buffer_hi[5];	/* Appendix B */ +	uint32_t qt_altnext;			/* see EHCI 3.5.2 */ +	uint32_t qt_token;			/* see EHCI 3.5.3 */ +#define QT_TOKEN_DT(x)		(((x) & 0x1) << 31)	/* Data Toggle */ +#define QT_TOKEN_GET_DT(x)		(((x) >> 31) & 0x1) +#define QT_TOKEN_TOTALBYTES(x)	(((x) & 0x7fff) << 16)	/* Total Bytes to Transfer */ +#define QT_TOKEN_GET_TOTALBYTES(x)	(((x) >> 16) & 0x7fff) +#define QT_TOKEN_IOC(x)		(((x) & 0x1) << 15)	/* Interrupt On Complete */ +#define QT_TOKEN_CPAGE(x)	(((x) & 0x7) << 12)	/* Current Page */ +#define QT_TOKEN_CERR(x)	(((x) & 0x3) << 10)	/* Error Counter */ +#define QT_TOKEN_PID(x)		(((x) & 0x3) << 8)	/* PID Code */ +#define QT_TOKEN_PID_OUT		0x0 +#define QT_TOKEN_PID_IN			0x1 +#define QT_TOKEN_PID_SETUP		0x2 +#define QT_TOKEN_STATUS(x)	(((x) & 0xff) << 0)	/* Status */ +#define QT_TOKEN_GET_STATUS(x)		(((x) >> 0) & 0xff) +#define QT_TOKEN_STATUS_ACTIVE		0x80 +#define QT_TOKEN_STATUS_HALTED		0x40 +#define QT_TOKEN_STATUS_DATBUFERR	0x20 +#define QT_TOKEN_STATUS_BABBLEDET	0x10 +#define QT_TOKEN_STATUS_XACTERR		0x08 +#define QT_TOKEN_STATUS_MISSEDUFRAME	0x04 +#define QT_TOKEN_STATUS_SPLITXSTATE	0x02 +#define QT_TOKEN_STATUS_PERR		0x01 +#define QT_BUFFER_CNT		5 +	uint32_t qt_buffer[QT_BUFFER_CNT];	/* see EHCI 3.5.4 */ +	uint32_t qt_buffer_hi[QT_BUFFER_CNT];	/* Appendix B */  	/* pad struct for 32 byte alignment */  	uint32_t unused[3];  }; +#define EHCI_PAGE_SIZE		4096 +  /* Queue Head (QH). */  struct QH {  	uint32_t qh_link; @@ -190,7 +219,26 @@ struct QH {  #define	QH_LINK_TYPE_SITD	4  #define	QH_LINK_TYPE_FSTN	6  	uint32_t qh_endpt1; +#define QH_ENDPT1_RL(x)		(((x) & 0xf) << 28)	/* NAK Count Reload */ +#define QH_ENDPT1_C(x)		(((x) & 0x1) << 27)	/* Control Endpoint Flag */ +#define QH_ENDPT1_MAXPKTLEN(x)	(((x) & 0x7ff) << 16)	/* Maximum Packet Length */ +#define QH_ENDPT1_H(x)		(((x) & 0x1) << 15)	/* Head of Reclamation List Flag */ +#define QH_ENDPT1_DTC(x)	(((x) & 0x1) << 14)	/* Data Toggle Control */ +#define QH_ENDPT1_DTC_IGNORE_QTD_TD	0x0 +#define QH_ENDPT1_DTC_DT_FROM_QTD	0x1 +#define QH_ENDPT1_EPS(x)	(((x) & 0x3) << 12)	/* Endpoint Speed */ +#define QH_ENDPT1_EPS_FS		0x0 +#define QH_ENDPT1_EPS_LS		0x1 +#define QH_ENDPT1_EPS_HS		0x2 +#define QH_ENDPT1_ENDPT(x)	(((x) & 0xf) << 8)	/* Endpoint Number */ +#define QH_ENDPT1_I(x)		(((x) & 0x1) << 7)	/* Inactivate on Next Transaction */ +#define QH_ENDPT1_DEVADDR(x)	(((x) & 0x7f) << 0)	/* Device Address */  	uint32_t qh_endpt2; +#define QH_ENDPT2_MULT(x)	(((x) & 0x3) << 30)	/* High-Bandwidth Pipe Multiplier */ +#define QH_ENDPT2_PORTNUM(x)	(((x) & 0x7f) << 23)	/* Port Number */ +#define QH_ENDPT2_HUBADDR(x)	(((x) & 0x7f) << 16)	/* Hub Address */ +#define QH_ENDPT2_UFCMASK(x)	(((x) & 0xff) << 8)	/* Split Completion Mask */ +#define QH_ENDPT2_UFSMASK(x)	(((x) & 0xff) << 0)	/* Interrupt Schedule Mask */  	uint32_t qh_curtd;  	struct qTD qh_overlay;  	/* diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c new file mode 100644 index 000000000..f0ccb83f7 --- /dev/null +++ b/drivers/usb/host/ohci-da8xx.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Sughosh Ganu <urwithsughosh@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <common.h> + +#include <asm/arch/da8xx-usb.h> + +int usb_cpu_init(void) +{ +	/* enable psc for usb2.0 */ +	lpsc_on(DAVINCI_LPSC_USB20); + +	/* enable psc for usb1.0 */ +	lpsc_on(DAVINCI_LPSC_USB11); + +	/* start the on-chip usb phy and its pll */ +	if (usb_phy_on()) +		return 0; + +	return 1; +} + +int usb_cpu_stop(void) +{ +	usb_phy_off(); + +	/* turn off the usb clock and assert the module reset */ +	lpsc_disable(DAVINCI_LPSC_USB11); +	lpsc_disable(DAVINCI_LPSC_USB20); + +	return 0; +} + +int usb_cpu_init_fail(void) +{ +	return usb_cpu_stop(); +} diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index d24f2f131..9f4735167 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1261,19 +1261,11 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe,  	int leni = transfer_len;  	int len = 0;  	int stat = 0; -	__u32 datab[4]; -	union { -		void *ptr; -		__u8 *u8; -		__u16 *u16; -		__u32 *u32; -	} databuf;  	__u16 bmRType_bReq;  	__u16 wValue;  	__u16 wIndex;  	__u16 wLength; - -	databuf.u32 = (__u32 *)datab; +	ALLOC_ALIGN_BUFFER(__u8, databuf, 16, sizeof(u32));  #ifdef DEBUG  pkt_print(NULL, dev, pipe, buffer, transfer_len, @@ -1304,20 +1296,20 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,  	*/  	case RH_GET_STATUS: -		databuf.u16[0] = cpu_to_le16(1); +		*(u16 *)databuf = cpu_to_le16(1);  		OK(2);  	case RH_GET_STATUS | RH_INTERFACE: -		databuf.u16[0] = cpu_to_le16(0); +		*(u16 *)databuf = cpu_to_le16(0);  		OK(2);  	case RH_GET_STATUS | RH_ENDPOINT: -		databuf.u16[0] = cpu_to_le16(0); +		*(u16 *)databuf = cpu_to_le16(0);  		OK(2);  	case RH_GET_STATUS | RH_CLASS: -		databuf.u32[0] = cpu_to_le32( +		*(u32 *)databuf = cpu_to_le32(  				RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));  		OK(4);  	case RH_GET_STATUS | RH_OTHER | RH_CLASS: -		databuf.u32[0] = cpu_to_le32(RD_RH_PORTSTAT); +		*(u32 *)databuf = cpu_to_le32(RD_RH_PORTSTAT);  		OK(4);  	case RH_CLEAR_FEATURE | RH_ENDPOINT: @@ -1381,14 +1373,14 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,  					min_t(unsigned int,  					sizeof(root_hub_dev_des),  					wLength)); -			databuf.ptr = root_hub_dev_des; OK(len); +			databuf = root_hub_dev_des; OK(len);  		case (0x02): /* configuration descriptor */  			len = min_t(unsigned int,  					leni,  					min_t(unsigned int,  					sizeof(root_hub_config_des),  					wLength)); -			databuf.ptr = root_hub_config_des; OK(len); +			databuf = root_hub_config_des; OK(len);  		case (0x03): /* string descriptors */  			if (wValue == 0x0300) {  				len = min_t(unsigned int, @@ -1396,7 +1388,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,  						min_t(unsigned int,  						sizeof(root_hub_str_index0),  						wLength)); -				databuf.ptr = root_hub_str_index0; +				databuf = root_hub_str_index0;  				OK(len);  			}  			if (wValue == 0x0301) { @@ -1405,7 +1397,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,  						min_t(unsigned int,  						sizeof(root_hub_str_index1),  						wLength)); -				databuf.ptr = root_hub_str_index1; +				databuf = root_hub_str_index1;  				OK(len);  		}  		default: @@ -1417,40 +1409,40 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,  	{  		__u32 temp = roothub_a(&gohci); -		databuf.u8[0] = 9;		/* min length; */ -		databuf.u8[1] = 0x29; -		databuf.u8[2] = temp & RH_A_NDP; +		databuf[0] = 9;		/* min length; */ +		databuf[1] = 0x29; +		databuf[2] = temp & RH_A_NDP;  #ifdef CONFIG_AT91C_PQFP_UHPBUG -		databuf.u8[2] = (databuf.u8[2] == 2) ? 1 : 0; +		databuf[2] = (databuf[2] == 2) ? 1 : 0;  #endif -		databuf.u8[3] = 0; +		databuf[3] = 0;  		if (temp & RH_A_PSM)	/* per-port power switching? */ -			databuf.u8[3] |= 0x1; +			databuf[3] |= 0x1;  		if (temp & RH_A_NOCP)	/* no overcurrent reporting? */ -			databuf.u8[3] |= 0x10; +			databuf[3] |= 0x10;  		else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */ -			databuf.u8[3] |= 0x8; +			databuf[3] |= 0x8; -		/* corresponds to databuf.u8[4-7] */ -		databuf.u8[1] = 0; -		databuf.u8[5] = (temp & RH_A_POTPGT) >> 24; +		databuf[4] = 0; +		databuf[5] = (temp & RH_A_POTPGT) >> 24; +		databuf[6] = 0;  		temp = roothub_b(&gohci); -		databuf.u8[7] = temp & RH_B_DR; -		if (databuf.u8[2] < 7) { -			databuf.u8[8] = 0xff; +		databuf[7] = temp & RH_B_DR; +		if (databuf[2] < 7) { +			databuf[8] = 0xff;  		} else { -			databuf.u8[0] += 2; -			databuf.u8[8] = (temp & RH_B_DR) >> 8; -			databuf.u8[10] = databuf.u8[9] = 0xff; +			databuf[0] += 2; +			databuf[8] = (temp & RH_B_DR) >> 8; +			databuf[10] = databuf[9] = 0xff;  		}  		len = min_t(unsigned int, leni, -			    min_t(unsigned int, databuf.u8[0], wLength)); +			    min_t(unsigned int, databuf[0], wLength));  		OK(len);  	}  	case RH_GET_CONFIGURATION: -		databuf.u8[0] = 0x01; +		databuf[0] = 0x01;  		OK(1);  	case RH_SET_CONFIGURATION: @@ -1469,8 +1461,8 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,  #endif  	len = min_t(int, len, leni); -	if (data != databuf.ptr) -		memcpy(data, databuf.ptr, len); +	if (data != databuf) +		memcpy(data, databuf, len);  	dev->act_len = len;  	dev->status = stat; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 617d88e1a..653410a57 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -23,7 +23,8 @@   */  #include <common.h> -#include "da8xx.h" +#include "musb_core.h" +#include <asm/arch/da8xx-usb.h>  /* MUSB platform configuration */  struct musb_config musb_cfg = { diff --git a/drivers/usb/musb/da8xx.h b/drivers/usb/musb/da8xx.h deleted file mode 100644 index be1cdaf1d..000000000 --- a/drivers/usb/musb/da8xx.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * da8xx.h -- TI's DA8xx platform specific usb wrapper definitions. - * - * Author: Ajay Kumar Gupta <ajay.gupta@ti.com> - * - * Based on drivers/usb/musb/davinci.h - * - * Copyright (C) 2009 Texas Instruments Incorporated - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#ifndef __DA8XX_MUSB_H__ -#define __DA8XX_MUSB_H__ - -#include <asm/arch/hardware.h> -#include <asm/arch/gpio.h> -#include "musb_core.h" - -/* Base address of da8xx usb0 wrapper */ -#define DA8XX_USB_OTG_BASE  0x01E00000 - -/* Base address of da8xx musb core */ -#define DA8XX_USB_OTG_CORE_BASE (DA8XX_USB_OTG_BASE + 0x400) - -/* Timeout for DA8xx usb module */ -#define DA8XX_USB_OTG_TIMEOUT 0x3FFFFFF - -/* - * DA8xx platform USB wrapper register overlay. - */ -struct da8xx_usb_regs { -	dv_reg	revision; -	dv_reg	control; -	dv_reg 	status; -	dv_reg 	emulation; -	dv_reg 	mode; -	dv_reg 	autoreq; -	dv_reg 	srpfixtime; -	dv_reg 	teardown; -	dv_reg 	intsrc; -	dv_reg 	intsrc_set; -	dv_reg 	intsrc_clr; -	dv_reg 	intmsk; -	dv_reg 	intmsk_set; -	dv_reg 	intmsk_clr; -	dv_reg 	intsrcmsk; -	dv_reg 	eoi; -	dv_reg 	intvector; -	dv_reg 	grndis_size[4]; -}; - -#define da8xx_usb_regs ((struct da8xx_usb_regs *)DA8XX_USB_OTG_BASE) - -/* DA8XX interrupt bits definitions */ -#define DA8XX_USB_TX_ENDPTS_MASK  0x1f	/* ep0 + 4 tx */ -#define DA8XX_USB_RX_ENDPTS_MASK  0x1e	/* 4 rx */ -#define DA8XX_USB_TXINT_SHIFT	  0 -#define DA8XX_USB_RXINT_SHIFT	  8 - -#define DA8XX_USB_USBINT_MASK	  0x01ff0000	/* 8 Mentor, DRVVBUS */ -#define DA8XX_USB_TXINT_MASK \ -		(DA8XX_USB_TX_ENDPTS_MASK << DA8XX_USB_TXINT_SHIFT) -#define DA8XX_USB_RXINT_MASK \ -		(DA8XX_USB_RX_ENDPTS_MASK << DA8XX_USB_RXINT_SHIFT) - -/* DA8xx CFGCHIP2 (USB 2.0 PHY Control) register bits */ -#define CFGCHIP2_PHYCLKGD	(1 << 17) -#define CFGCHIP2_VBUSSENSE	(1 << 16) -#define CFGCHIP2_RESET		(1 << 15) -#define CFGCHIP2_OTGMODE	(3 << 13) -#define CFGCHIP2_NO_OVERRIDE	(0 << 13) -#define CFGCHIP2_FORCE_HOST	(1 << 13) -#define CFGCHIP2_FORCE_DEVICE 	(2 << 13) -#define CFGCHIP2_FORCE_HOST_VBUS_LOW (3 << 13) -#define CFGCHIP2_USB1PHYCLKMUX	(1 << 12) -#define CFGCHIP2_USB2PHYCLKMUX	(1 << 11) -#define CFGCHIP2_PHYPWRDN	(1 << 10) -#define CFGCHIP2_OTGPWRDN	(1 << 9) -#define CFGCHIP2_DATPOL 	(1 << 8) -#define CFGCHIP2_USB1SUSPENDM	(1 << 7) -#define CFGCHIP2_PHY_PLLON	(1 << 6)	/* override PLL suspend */ -#define CFGCHIP2_SESENDEN	(1 << 5)	/* Vsess_end comparator */ -#define CFGCHIP2_VBDTCTEN	(1 << 4)	/* Vbus comparator */ -#define CFGCHIP2_REFFREQ	(0xf << 0) -#define CFGCHIP2_REFFREQ_12MHZ	(1 << 0) -#define CFGCHIP2_REFFREQ_24MHZ	(2 << 0) -#define CFGCHIP2_REFFREQ_48MHZ	(3 << 0) - -#define DA8XX_USB_VBUS_GPIO	(1 << 15) -#endif	/* __DA8XX_MUSB_H__ */ diff --git a/drivers/usb/musb/musb_hcd.c b/drivers/usb/musb/musb_hcd.c index 2df52c1c3..8d44c4657 100644 --- a/drivers/usb/musb/musb_hcd.c +++ b/drivers/usb/musb/musb_hcd.c @@ -1113,7 +1113,7 @@ int usb_lowlevel_init(void)  	 * should be a usb device connected.  	 */  	timeout = musb_cfg.timeout; -	while (timeout--) +	while (--timeout)  		if (readb(&musbr->devctl) & MUSB_DEVCTL_HM)  			break; diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 2f8e2b521..ebb6da823 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -29,9 +29,11 @@ COBJS-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o  COBJS-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o  COBJS-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o  COBJS-$(CONFIG_CFB_CONSOLE) += cfb_console.o +COBJS-$(CONFIG_EXYNOS_DP) += exynos_dp.o exynos_dp_lowlevel.o  COBJS-$(CONFIG_EXYNOS_FB) += exynos_fb.o exynos_fimd.o  COBJS-$(CONFIG_EXYNOS_MIPI_DSIM) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \  				exynos_mipi_dsi_lowlevel.o +COBJS-$(CONFIG_EXYNOS_PWM_BL) += exynos_pwm_bl.o  COBJS-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o  COBJS-$(CONFIG_S6E8AX0) += s6e8ax0.o  COBJS-$(CONFIG_S6E63D6) += s6e63d6.o diff --git a/drivers/video/exynos_dp.c b/drivers/video/exynos_dp.c new file mode 100644 index 000000000..53e410120 --- /dev/null +++ b/drivers/video/exynos_dp.c @@ -0,0 +1,925 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * 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 <config.h> +#include <common.h> +#include <malloc.h> +#include <linux/err.h> +#include <asm/arch/clk.h> +#include <asm/arch/cpu.h> +#include <asm/arch/dp_info.h> +#include <asm/arch/dp.h> + +#include "exynos_dp_lowlevel.h" + +static struct exynos_dp_platform_data *dp_pd; + +static void exynos_dp_disp_info(struct edp_disp_info *disp_info) +{ +	disp_info->h_total = disp_info->h_res + disp_info->h_sync_width + +		disp_info->h_back_porch + disp_info->h_front_porch; +	disp_info->v_total = disp_info->v_res + disp_info->v_sync_width + +		disp_info->v_back_porch + disp_info->v_front_porch; + +	return; +} + +static int exynos_dp_init_dp(void) +{ +	int ret; +	exynos_dp_reset(); + +	/* SW defined function Normal operation */ +	exynos_dp_enable_sw_func(DP_ENABLE); + +	ret = exynos_dp_init_analog_func(); +	if (ret != EXYNOS_DP_SUCCESS) +		return ret; + +	exynos_dp_init_hpd(); +	exynos_dp_init_aux(); + +	return ret; +} + +static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data) +{ +	int i; +	unsigned char sum = 0; + +	for (i = 0; i < EDID_BLOCK_LENGTH; i++) +		sum = sum + edid_data[i]; + +	return sum; +} + +static unsigned int exynos_dp_read_edid(void) +{ +	unsigned char edid[EDID_BLOCK_LENGTH * 2]; +	unsigned int extend_block = 0; +	unsigned char sum; +	unsigned char test_vector; +	int retval; + +	/* +	 * EDID device address is 0x50. +	 * However, if necessary, you must have set upper address +	 * into E-EDID in I2C device, 0x30. +	 */ + +	/* Read Extension Flag, Number of 128-byte EDID extension blocks */ +	exynos_dp_read_byte_from_i2c(I2C_EDID_DEVICE_ADDR, EDID_EXTENSION_FLAG, +			&extend_block); + +	if (extend_block > 0) { +		printf("DP EDID data includes a single extension!\n"); + +		/* Read EDID data */ +		retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR, +						EDID_HEADER_PATTERN, +						EDID_BLOCK_LENGTH, +						&edid[EDID_HEADER_PATTERN]); +		if (retval != 0) { +			printf("DP EDID Read failed!\n"); +			return -1; +		} +		sum = exynos_dp_calc_edid_check_sum(edid); +		if (sum != 0) { +			printf("DP EDID bad checksum!\n"); +			return -1; +		} + +		/* Read additional EDID data */ +		retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR, +				EDID_BLOCK_LENGTH, +				EDID_BLOCK_LENGTH, +				&edid[EDID_BLOCK_LENGTH]); +		if (retval != 0) { +			printf("DP EDID Read failed!\n"); +			return -1; +		} +		sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]); +		if (sum != 0) { +			printf("DP EDID bad checksum!\n"); +			return -1; +		} + +		exynos_dp_read_byte_from_dpcd(DPCD_TEST_REQUEST, +					&test_vector); +		if (test_vector & DPCD_TEST_EDID_READ) { +			exynos_dp_write_byte_to_dpcd(DPCD_TEST_EDID_CHECKSUM, +				edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]); +			exynos_dp_write_byte_to_dpcd(DPCD_TEST_RESPONSE, +				DPCD_TEST_EDID_CHECKSUM_WRITE); +		} +	} else { +		debug("DP EDID data does not include any extensions.\n"); + +		/* Read EDID data */ +		retval = exynos_dp_read_bytes_from_i2c(I2C_EDID_DEVICE_ADDR, +				EDID_HEADER_PATTERN, +				EDID_BLOCK_LENGTH, +				&edid[EDID_HEADER_PATTERN]); + +		if (retval != 0) { +			printf("DP EDID Read failed!\n"); +			return -1; +		} +		sum = exynos_dp_calc_edid_check_sum(edid); +		if (sum != 0) { +			printf("DP EDID bad checksum!\n"); +			return -1; +		} + +		exynos_dp_read_byte_from_dpcd(DPCD_TEST_REQUEST, +			&test_vector); +		if (test_vector & DPCD_TEST_EDID_READ) { +			exynos_dp_write_byte_to_dpcd(DPCD_TEST_EDID_CHECKSUM, +				edid[EDID_CHECKSUM]); +			exynos_dp_write_byte_to_dpcd(DPCD_TEST_RESPONSE, +				DPCD_TEST_EDID_CHECKSUM_WRITE); +		} +	} + +	debug("DP EDID Read success!\n"); + +	return 0; +} + +static unsigned int exynos_dp_handle_edid(struct edp_device_info *edp_info) +{ +	unsigned char buf[12]; +	unsigned int ret; +	unsigned char temp; +	unsigned char retry_cnt; +	unsigned char dpcd_rev[16]; +	unsigned char lane_bw[16]; +	unsigned char lane_cnt[16]; + +	memset(dpcd_rev, 0, 16); +	memset(lane_bw, 0, 16); +	memset(lane_cnt, 0, 16); +	memset(buf, 0, 12); + +	retry_cnt = 5; +	while (retry_cnt) { +		/* Read DPCD 0x0000-0x000b */ +		ret = exynos_dp_read_bytes_from_dpcd(DPCD_DPCD_REV, 12, +				buf); +		if (ret != EXYNOS_DP_SUCCESS) { +			if (retry_cnt == 0) { +				printf("DP read_byte_from_dpcd() failed\n"); +				return ret; +			} +			retry_cnt--; +		} else +			break; +	} + +	/* */ +	temp = buf[DPCD_DPCD_REV]; +	if (temp == DP_DPCD_REV_10 || temp == DP_DPCD_REV_11) +		edp_info->dpcd_rev = temp; +	else { +		printf("DP Wrong DPCD Rev : %x\n", temp); +		return -ENODEV; +	} + +	temp = buf[DPCD_MAX_LINK_RATE]; +	if (temp == DP_LANE_BW_1_62 || temp == DP_LANE_BW_2_70) +		edp_info->lane_bw = temp; +	else { +		printf("DP Wrong MAX LINK RATE : %x\n", temp); +		return -EINVAL; +	} + +	/*Refer VESA Display Port Stnadard Ver1.1a Page 120 */ +	if (edp_info->dpcd_rev == DP_DPCD_REV_11) { +		temp = buf[DPCD_MAX_LANE_COUNT] & 0x1f; +		if (buf[DPCD_MAX_LANE_COUNT] & 0x80) +			edp_info->dpcd_efc = 1; +		else +			edp_info->dpcd_efc = 0; +	} else { +		temp = buf[DPCD_MAX_LANE_COUNT]; +		edp_info->dpcd_efc = 0; +	} + +	if (temp == DP_LANE_CNT_1 || temp == DP_LANE_CNT_2 || +			temp == DP_LANE_CNT_4) { +		edp_info->lane_cnt = temp; +	} else { +		printf("DP Wrong MAX LANE COUNT : %x\n", temp); +		return -EINVAL; +	} + +	ret = exynos_dp_read_edid(); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP exynos_dp_read_edid() failed\n"); +		return -EINVAL; +	} + +	return ret; +} + +static void exynos_dp_init_training(void) +{ +	/* +	 * MACRO_RST must be applied after the PLL_LOCK to avoid +	 * the DP inter pair skew issue for at least 10 us +	 */ +	exynos_dp_reset_macro(); + +	/* All DP analog module power up */ +	exynos_dp_set_analog_power_down(POWER_ALL, 0); +} + +static unsigned int exynos_dp_link_start(struct edp_device_info *edp_info) +{ +	unsigned char buf[5]; +	unsigned int ret = 0; + +	debug("DP: %s was called\n", __func__); + +	edp_info->lt_info.lt_status = DP_LT_CR; +	edp_info->lt_info.ep_loop = 0; +	edp_info->lt_info.cr_loop[0] = 0; +	edp_info->lt_info.cr_loop[1] = 0; +	edp_info->lt_info.cr_loop[2] = 0; +	edp_info->lt_info.cr_loop[3] = 0; + +		/* Set sink to D0 (Sink Not Ready) mode. */ +		ret = exynos_dp_write_byte_to_dpcd(DPCD_SINK_POWER_STATE, +				DPCD_SET_POWER_STATE_D0); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP write_dpcd_byte failed\n"); +		return ret; +	} + +	/* Set link rate and count as you want to establish*/ +	exynos_dp_set_link_bandwidth(edp_info->lane_bw); +	exynos_dp_set_lane_count(edp_info->lane_cnt); + +	/* Setup RX configuration */ +	buf[0] = edp_info->lane_bw; +	buf[1] = edp_info->lane_cnt; + +	ret = exynos_dp_write_bytes_to_dpcd(DPCD_LINK_BW_SET, 2, +			buf); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP write_dpcd_byte failed\n"); +		return ret; +	} + +	exynos_dp_set_lane_pre_emphasis(PRE_EMPHASIS_LEVEL_0, +			edp_info->lane_cnt); + +	/* Set training pattern 1 */ +	exynos_dp_set_training_pattern(TRAINING_PTN1); + +	/* Set RX training pattern */ +	buf[0] = DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1; + +	buf[1] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | +		DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; +	buf[2] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | +		DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; +	buf[3] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | +		DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; +	buf[4] = DPCD_PRE_EMPHASIS_SET_PATTERN_2_LEVEL_0 | +		DPCD_VOLTAGE_SWING_SET_PATTERN_1_LEVEL_0; + +	ret = exynos_dp_write_bytes_to_dpcd(DPCD_TRAINING_PATTERN_SET, +			5, buf); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP write_dpcd_byte failed\n"); +		return ret; +	} + +	return ret; +} + +static unsigned int exynos_dp_training_pattern_dis(void) +{ +	unsigned int ret = EXYNOS_DP_SUCCESS; + +	exynos_dp_set_training_pattern(DP_NONE); + +	ret = exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET, +			DPCD_TRAINING_PATTERN_DISABLED); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP requst_link_traninig_req failed\n"); +		return -EAGAIN; +	} + +	return ret; +} + +static unsigned int exynos_dp_enable_rx_to_enhanced_mode(unsigned char enable) +{ +	unsigned char data; +	unsigned int ret = EXYNOS_DP_SUCCESS; + +	ret = exynos_dp_read_byte_from_dpcd(DPCD_LANE_COUNT_SET, +			&data); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP read_from_dpcd failed\n"); +		return -EAGAIN; +	} + +	if (enable) +		data = DPCD_ENHANCED_FRAME_EN | DPCD_LN_COUNT_SET(data); +	else +		data = DPCD_LN_COUNT_SET(data); + +	ret = exynos_dp_write_byte_to_dpcd(DPCD_LANE_COUNT_SET, +			data); +	if (ret != EXYNOS_DP_SUCCESS) { +			printf("DP write_to_dpcd failed\n"); +			return -EAGAIN; + +	} + +	return ret; +} + +static unsigned int exynos_dp_set_enhanced_mode(unsigned char enhance_mode) +{ +	unsigned int ret = EXYNOS_DP_SUCCESS; + +	ret = exynos_dp_enable_rx_to_enhanced_mode(enhance_mode); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP rx_enhance_mode failed\n"); +		return -EAGAIN; +	} + +	exynos_dp_enable_enhanced_mode(enhance_mode); + +	return ret; +} + +static int exynos_dp_read_dpcd_lane_stat(struct edp_device_info *edp_info, +		unsigned char *status) +{ +	unsigned int ret, i; +	unsigned char buf[2]; +	unsigned char lane_stat[DP_LANE_CNT_4] = {0,}; +	unsigned char shift_val[DP_LANE_CNT_4] = {0,}; + +	shift_val[0] = 0; +	shift_val[1] = 4; +	shift_val[2] = 0; +	shift_val[3] = 4; + +	ret = exynos_dp_read_bytes_from_dpcd(DPCD_LANE0_1_STATUS, 2, buf); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP read lane status failed\n"); +		return ret; +	} + +	for (i = 0; i < edp_info->lane_cnt; i++) { +		lane_stat[i] = (buf[(i / 2)] >> shift_val[i]) & 0x0f; +		if (lane_stat[0] != lane_stat[i]) { +			printf("Wrong lane status\n"); +			return -EINVAL; +		} +	} + +	*status = lane_stat[0]; + +	return ret; +} + +static unsigned int exynos_dp_read_dpcd_adj_req(unsigned char lane_num, +		unsigned char *sw, unsigned char *em) +{ +	unsigned int ret = EXYNOS_DP_SUCCESS; +	unsigned char buf; +	unsigned int dpcd_addr; +	unsigned char shift_val[DP_LANE_CNT_4] = {0, 4, 0, 4}; + +	/*lane_num value is used as arry index, so this range 0 ~ 3 */ +	dpcd_addr = DPCD_ADJUST_REQUEST_LANE0_1 + (lane_num / 2); + +	ret = exynos_dp_read_byte_from_dpcd(dpcd_addr, &buf); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP read adjust request failed\n"); +		return -EAGAIN; +	} + +	*sw = ((buf >> shift_val[lane_num]) & 0x03); +	*em = ((buf >> shift_val[lane_num]) & 0x0c) >> 2; + +	return ret; +} + +static int exynos_dp_equalizer_err_link(struct edp_device_info *edp_info) +{ +	int ret; + +	ret = exynos_dp_training_pattern_dis(); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP training_patter_disable() failed\n"); +		edp_info->lt_info.lt_status = DP_LT_FAIL; +	} + +	ret = exynos_dp_set_enhanced_mode(edp_info->dpcd_efc); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP set_enhanced_mode() failed\n"); +		edp_info->lt_info.lt_status = DP_LT_FAIL; +	} + +	return ret; +} + +static int exynos_dp_reduce_link_rate(struct edp_device_info *edp_info) +{ +	int ret; + +	if (edp_info->lane_bw == DP_LANE_BW_2_70) { +		edp_info->lane_bw = DP_LANE_BW_1_62; +		printf("DP Change lane bw to 1.62Gbps\n"); +		edp_info->lt_info.lt_status = DP_LT_START; +		ret = EXYNOS_DP_SUCCESS; +	} else { +		ret = exynos_dp_training_pattern_dis(); +		if (ret != EXYNOS_DP_SUCCESS) +			printf("DP training_patter_disable() failed\n"); + +		ret = exynos_dp_set_enhanced_mode(edp_info->dpcd_efc); +		if (ret != EXYNOS_DP_SUCCESS) +			printf("DP set_enhanced_mode() failed\n"); + +		edp_info->lt_info.lt_status = DP_LT_FAIL; +	} + +	return ret; +} + +static unsigned int exynos_dp_process_clock_recovery(struct edp_device_info +							*edp_info) +{ +	unsigned int ret = EXYNOS_DP_SUCCESS; +	unsigned char lane_stat; +	unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0, }; +	unsigned int i; +	unsigned char adj_req_sw; +	unsigned char adj_req_em; +	unsigned char buf[5]; + +	debug("DP: %s was called\n", __func__); +	mdelay(1); + +	ret = exynos_dp_read_dpcd_lane_stat(edp_info, &lane_stat); +	if (ret != EXYNOS_DP_SUCCESS) { +			printf("DP read lane status failed\n"); +			edp_info->lt_info.lt_status = DP_LT_FAIL; +			return ret; +	} + +	if (lane_stat & DP_LANE_STAT_CR_DONE) { +		debug("DP clock Recovery training succeed\n"); +		exynos_dp_set_training_pattern(TRAINING_PTN2); + +		for (i = 0; i < edp_info->lane_cnt; i++) { +			ret = exynos_dp_read_dpcd_adj_req(i, &adj_req_sw, +					&adj_req_em); +			if (ret != EXYNOS_DP_SUCCESS) { +				edp_info->lt_info.lt_status = DP_LT_FAIL; +				return ret; +			} + +			lt_ctl_val[i] = 0; +			lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; + +			if ((adj_req_sw == VOLTAGE_LEVEL_3) +				|| (adj_req_em == PRE_EMPHASIS_LEVEL_3)) { +				lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 | +					MAX_PRE_EMPHASIS_REACH_3; +			} +			exynos_dp_set_lanex_pre_emphasis(lt_ctl_val[i], i); +		} + +		buf[0] =  DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_2; +		buf[1] = lt_ctl_val[0]; +		buf[2] = lt_ctl_val[1]; +		buf[3] = lt_ctl_val[2]; +		buf[4] = lt_ctl_val[3]; + +		ret = exynos_dp_write_bytes_to_dpcd( +				DPCD_TRAINING_PATTERN_SET, 5, buf); +		if (ret != EXYNOS_DP_SUCCESS) { +			printf("DP write traning pattern1 failed\n"); +			edp_info->lt_info.lt_status = DP_LT_FAIL; +			return ret; +		} else +			edp_info->lt_info.lt_status = DP_LT_ET; +	} else { +		for (i = 0; i < edp_info->lane_cnt; i++) { +			lt_ctl_val[i] = exynos_dp_get_lanex_pre_emphasis(i); +				ret = exynos_dp_read_dpcd_adj_req(i, +						&adj_req_sw, &adj_req_em); +			if (ret != EXYNOS_DP_SUCCESS) { +				printf("DP read adj req failed\n"); +				edp_info->lt_info.lt_status = DP_LT_FAIL; +				return ret; +			} + +			if ((adj_req_sw == VOLTAGE_LEVEL_3) || +					(adj_req_em == PRE_EMPHASIS_LEVEL_3)) +				ret = exynos_dp_reduce_link_rate(edp_info); + +			if ((DRIVE_CURRENT_SET_0_GET(lt_ctl_val[i]) == +						adj_req_sw) && +				(PRE_EMPHASIS_SET_0_GET(lt_ctl_val[i]) == +						adj_req_em)) { +				edp_info->lt_info.cr_loop[i]++; +				if (edp_info->lt_info.cr_loop[i] == MAX_CR_LOOP) +					ret = exynos_dp_reduce_link_rate( +							edp_info); +			} + +			lt_ctl_val[i] = 0; +			lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; + +			if ((adj_req_sw == VOLTAGE_LEVEL_3) || +					(adj_req_em == PRE_EMPHASIS_LEVEL_3)) { +				lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3 | +					MAX_PRE_EMPHASIS_REACH_3; +			} +			exynos_dp_set_lanex_pre_emphasis(lt_ctl_val[i], i); +		} + +		ret = exynos_dp_write_bytes_to_dpcd( +				DPCD_TRAINING_LANE0_SET, 4, lt_ctl_val); +		if (ret != EXYNOS_DP_SUCCESS) { +			printf("DP write traning pattern2 failed\n"); +			edp_info->lt_info.lt_status = DP_LT_FAIL; +			return ret; +		} +	} + +	return ret; +} + +static unsigned int exynos_dp_process_equalizer_training(struct edp_device_info +		*edp_info) +{ +	unsigned int ret = EXYNOS_DP_SUCCESS; +	unsigned char lane_stat, adj_req_sw, adj_req_em, i; +	unsigned char lt_ctl_val[DP_LANE_CNT_4] = {0,}; +	unsigned char interlane_aligned = 0; +	unsigned char f_bw; +	unsigned char f_lane_cnt; +	unsigned char sink_stat; + +	mdelay(1); + +	ret = exynos_dp_read_dpcd_lane_stat(edp_info, &lane_stat); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP read lane status failed\n"); +		edp_info->lt_info.lt_status = DP_LT_FAIL; +		return ret; +	} + +	debug("DP lane stat : %x\n", lane_stat); + +	if (lane_stat & DP_LANE_STAT_CR_DONE) { +		ret = exynos_dp_read_byte_from_dpcd(DPCD_LN_ALIGN_UPDATED, +				&sink_stat); +		if (ret != EXYNOS_DP_SUCCESS) { +			edp_info->lt_info.lt_status = DP_LT_FAIL; + +			return ret; +		} + +		interlane_aligned = (sink_stat & DPCD_INTERLANE_ALIGN_DONE); + +		for (i = 0; i < edp_info->lane_cnt; i++) { +			ret = exynos_dp_read_dpcd_adj_req(i, +					&adj_req_sw, &adj_req_em); +			if (ret != EXYNOS_DP_SUCCESS) { +				printf("DP read adj req 1 failed\n"); +				edp_info->lt_info.lt_status = DP_LT_FAIL; + +				return ret; +			} + +			lt_ctl_val[i] = 0; +			lt_ctl_val[i] = adj_req_em << 3 | adj_req_sw; + +			if ((adj_req_sw == VOLTAGE_LEVEL_3) || +				(adj_req_em == PRE_EMPHASIS_LEVEL_3)) { +				lt_ctl_val[i] |= MAX_DRIVE_CURRENT_REACH_3; +				lt_ctl_val[i] |= MAX_PRE_EMPHASIS_REACH_3; +			} +		} + +		if (((lane_stat&DP_LANE_STAT_CE_DONE) && +			(lane_stat&DP_LANE_STAT_SYM_LOCK)) +			&& (interlane_aligned == DPCD_INTERLANE_ALIGN_DONE)) { +			debug("DP Equalizer training succeed\n"); + +			f_bw = exynos_dp_get_link_bandwidth(); +			f_lane_cnt = exynos_dp_get_lane_count(); + +			debug("DP final BandWidth : %x\n", f_bw); +			debug("DP final Lane Count : %x\n", f_lane_cnt); + +			edp_info->lt_info.lt_status = DP_LT_FINISHED; + +			exynos_dp_equalizer_err_link(edp_info); + +		} else { +			edp_info->lt_info.ep_loop++; + +			if (edp_info->lt_info.ep_loop > MAX_EQ_LOOP) { +				if (edp_info->lane_bw == DP_LANE_BW_2_70) { +					ret = exynos_dp_reduce_link_rate( +							edp_info); +				} else { +					edp_info->lt_info.lt_status = +								DP_LT_FAIL; +					exynos_dp_equalizer_err_link(edp_info); +				} +			} else { +				for (i = 0; i < edp_info->lane_cnt; i++) +					exynos_dp_set_lanex_pre_emphasis( +							lt_ctl_val[i], i); + +				ret = exynos_dp_write_bytes_to_dpcd( +					DPCD_TRAINING_LANE0_SET, +					4, lt_ctl_val); +				if (ret != EXYNOS_DP_SUCCESS) { +					printf("DP set lt pattern failed\n"); +					edp_info->lt_info.lt_status = +								DP_LT_FAIL; +					exynos_dp_equalizer_err_link(edp_info); +				} +			} +		} +	} else if (edp_info->lane_bw == DP_LANE_BW_2_70) { +		ret = exynos_dp_reduce_link_rate(edp_info); +	} else { +		edp_info->lt_info.lt_status = DP_LT_FAIL; +		exynos_dp_equalizer_err_link(edp_info); +	} + +	return ret; +} + +static unsigned int exynos_dp_sw_link_training(struct edp_device_info *edp_info) +{ +	unsigned int ret = 0; +	int training_finished; + +	/* Turn off unnecessary lane */ +	if (edp_info->lane_cnt == 1) +		exynos_dp_set_analog_power_down(CH1_BLOCK, 1); + +	training_finished = 0; + +	edp_info->lt_info.lt_status = DP_LT_START; + +	/* Process here */ +	while (!training_finished) { +		switch (edp_info->lt_info.lt_status) { +		case DP_LT_START: +			ret = exynos_dp_link_start(edp_info); +			if (ret != EXYNOS_DP_SUCCESS) { +				printf("DP LT:link start failed\n"); +				return ret; +			} +			break; +		case DP_LT_CR: +			ret = exynos_dp_process_clock_recovery(edp_info); +			if (ret != EXYNOS_DP_SUCCESS) { +				printf("DP LT:clock recovery failed\n"); +				return ret; +			} +			break; +		case DP_LT_ET: +			ret = exynos_dp_process_equalizer_training(edp_info); +			if (ret != EXYNOS_DP_SUCCESS) { +				printf("DP LT:equalizer training failed\n"); +				return ret; +			} +			break; +		case DP_LT_FINISHED: +			training_finished = 1; +			break; +		case DP_LT_FAIL: +			return -1; +		} +	} + +	return ret; +} + +static unsigned int exynos_dp_set_link_train(struct edp_device_info *edp_info) +{ +	unsigned int ret; + +	exynos_dp_init_training(); + +	ret = exynos_dp_sw_link_training(edp_info); +	if (ret != EXYNOS_DP_SUCCESS) +		printf("DP dp_sw_link_traning() failed\n"); + +	return ret; +} + +static void exynos_dp_enable_scramble(unsigned int enable) +{ +	unsigned char data; + +	if (enable) { +		exynos_dp_enable_scrambling(DP_ENABLE); + +		exynos_dp_read_byte_from_dpcd(DPCD_TRAINING_PATTERN_SET, +				&data); +		exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET, +			(u8)(data & ~DPCD_SCRAMBLING_DISABLED)); +	} else { +		exynos_dp_enable_scrambling(DP_DISABLE); +		exynos_dp_read_byte_from_dpcd(DPCD_TRAINING_PATTERN_SET, +				&data); +		exynos_dp_write_byte_to_dpcd(DPCD_TRAINING_PATTERN_SET, +			(u8)(data | DPCD_SCRAMBLING_DISABLED)); +	} +} + +static unsigned int exynos_dp_config_video(struct edp_device_info *edp_info) +{ +	unsigned int ret = 0; +	unsigned int retry_cnt; + +	mdelay(1); + +	if (edp_info->video_info.master_mode) { +		printf("DP does not support master mode\n"); +		return -ENODEV; +	} else { +		/* debug slave */ +		exynos_dp_config_video_slave_mode(&edp_info->video_info); +	} + +	exynos_dp_set_video_color_format(&edp_info->video_info); + +	if (edp_info->video_info.bist_mode) { +		if (exynos_dp_config_video_bist(edp_info) != 0) +			return -1; +	} + +	ret = exynos_dp_get_pll_lock_status(); +	if (ret != PLL_LOCKED) { +		printf("DP PLL is not locked yet\n"); +		return -EIO; +	} + +	if (edp_info->video_info.master_mode == 0) { +		retry_cnt = 10; +		while (retry_cnt) { +			ret = exynos_dp_is_slave_video_stream_clock_on(); +			if (ret != EXYNOS_DP_SUCCESS) { +				if (retry_cnt == 0) { +					printf("DP stream_clock_on failed\n"); +					return ret; +				} +				retry_cnt--; +				mdelay(1); +			} else +				break; +		} +	} + +	/* Set to use the register calculated M/N video */ +	exynos_dp_set_video_cr_mn(CALCULATED_M, 0, 0); + +	/* For video bist, Video timing must be generated by register */ +	exynos_dp_set_video_timing_mode(VIDEO_TIMING_FROM_CAPTURE); + +	/* Enable video bist */ +	if (edp_info->video_info.bist_pattern != COLOR_RAMP && +		edp_info->video_info.bist_pattern != BALCK_WHITE_V_LINES && +		edp_info->video_info.bist_pattern != COLOR_SQUARE) +		exynos_dp_enable_video_bist(edp_info->video_info.bist_mode); +	else +		exynos_dp_enable_video_bist(DP_DISABLE); + +	/* Disable video mute */ +	exynos_dp_enable_video_mute(DP_DISABLE); + +	/* Configure video Master or Slave mode */ +	exynos_dp_enable_video_master(edp_info->video_info.master_mode); + +	/* Enable video */ +	exynos_dp_start_video(); + +	if (edp_info->video_info.master_mode == 0) { +		retry_cnt = 100; +		while (retry_cnt) { +			ret = exynos_dp_is_video_stream_on(); +			if (ret != EXYNOS_DP_SUCCESS) { +				if (retry_cnt == 0) { +					printf("DP Timeout of video stream\n"); +					return ret; +				} +				retry_cnt--; +				mdelay(5); +			} else +				break; +		} +	} + +	return ret; +} + +unsigned int exynos_init_dp(void) +{ +	unsigned int ret; +	struct edp_device_info *edp_info; +	struct edp_disp_info disp_info; + +	edp_info = kzalloc(sizeof(struct edp_device_info), GFP_KERNEL); +	if (!edp_info) { +		debug("failed to allocate edp device object.\n"); +		return -EFAULT; +	} + +	edp_info = dp_pd->edp_dev_info; +	if (edp_info == NULL) { +		debug("failed to get edp_info data.\n"); +		return -EFAULT; +	} +	disp_info = edp_info->disp_info; + +	exynos_dp_disp_info(&edp_info->disp_info); + +	if (dp_pd->phy_enable) +		dp_pd->phy_enable(1); + +	ret = exynos_dp_init_dp(); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP exynos_dp_init_dp() failed\n"); +		return ret; +	} + +	ret = exynos_dp_handle_edid(edp_info); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("EDP handle_edid fail\n"); +		return ret; +	} + +	ret = exynos_dp_set_link_train(edp_info); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP link training fail\n"); +		return ret; +	} + +	exynos_dp_enable_scramble(DP_ENABLE); +	exynos_dp_enable_rx_to_enhanced_mode(DP_ENABLE); +	exynos_dp_enable_enhanced_mode(DP_ENABLE); + +	exynos_dp_set_link_bandwidth(edp_info->lane_bw); +	exynos_dp_set_lane_count(edp_info->lane_cnt); + +	exynos_dp_init_video(); +	ret = exynos_dp_config_video(edp_info); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("Exynos DP init failed\n"); +		return ret; +	} + +	printf("Exynos DP init done\n"); + +	return ret; +} + +void exynos_set_dp_platform_data(struct exynos_dp_platform_data *pd) +{ +	if (pd == NULL) { +		debug("pd is NULL\n"); +		return; +	} + +	dp_pd = pd; +} diff --git a/drivers/video/exynos_dp_lowlevel.c b/drivers/video/exynos_dp_lowlevel.c new file mode 100644 index 000000000..7b54c8084 --- /dev/null +++ b/drivers/video/exynos_dp_lowlevel.c @@ -0,0 +1,1291 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * 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 <config.h> +#include <common.h> +#include <linux/err.h> +#include <asm/arch/cpu.h> +#include <asm/arch/dp_info.h> +#include <asm/arch/dp.h> + +static void exynos_dp_enable_video_input(unsigned int enable) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->video_ctl1); +	reg &= ~VIDEO_EN_MASK; + +	/* enable video input*/ +	if (enable) +		reg |= VIDEO_EN_MASK; + +	writel(reg, &dp_regs->video_ctl1); + +	return; +} + +void exynos_dp_enable_video_bist(unsigned int enable) +{ +	/*enable video bist*/ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->video_ctl4); +	reg &= ~VIDEO_BIST_MASK; + +	/*enable video bist*/ +	if (enable) +		reg |= VIDEO_BIST_MASK; + +	writel(reg, &dp_regs->video_ctl4); + +	return; +} + +void exynos_dp_enable_video_mute(unsigned int enable) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->video_ctl1); +	reg &= ~(VIDEO_MUTE_MASK); +	if (enable) +		reg |= VIDEO_MUTE_MASK; + +	writel(reg, &dp_regs->video_ctl1); + +	return; +} + + +static void exynos_dp_init_analog_param(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* +	 * Set termination +	 * Normal bandgap, Normal swing, Tx terminal registor 61 ohm +	 * 24M Phy clock, TX digital logic power is 100:1.0625V +	 */ +	reg = SEL_BG_NEW_BANDGAP | TX_TERMINAL_CTRL_61_OHM | +		SWING_A_30PER_G_NORMAL; +	writel(reg, &dp_regs->analog_ctl1); + +	reg = SEL_24M | TX_DVDD_BIT_1_0625V; +	writel(reg, &dp_regs->analog_ctl2); + +	/* +	 * Set power source for internal clk driver to 1.0625v. +	 * Select current reference of TX driver current to 00:Ipp/2+Ic/2. +	 * Set VCO range of PLL +- 0uA +	 */ +	reg = DRIVE_DVDD_BIT_1_0625V | SEL_CURRENT_DEFAULT | VCO_BIT_000_MICRO; +	writel(reg, &dp_regs->analog_ctl3); + +	/* +	 * Set AUX TX terminal resistor to 102 ohm +	 * Set AUX channel amplitude control +	*/ +	reg = PD_RING_OSC | AUX_TERMINAL_CTRL_52_OHM | TX_CUR1_2X | TX_CUR_4_MA; +	writel(reg, &dp_regs->pll_filter_ctl1); + +	/* +	 * PLL loop filter bandwidth +	 * For 2.7Gbps: 175KHz, For 1.62Gbps: 234KHz +	 * PLL digital power select: 1.2500V +	 */ +	reg = CH3_AMP_0_MV | CH2_AMP_0_MV | CH1_AMP_0_MV | CH0_AMP_0_MV; + +	writel(reg, &dp_regs->amp_tuning_ctl); + +	/* +	 * PLL loop filter bandwidth +	 * For 2.7Gbps: 175KHz, For 1.62Gbps: 234KHz +	 * PLL digital power select: 1.1250V +	 */ +	reg = DP_PLL_LOOP_BIT_DEFAULT | DP_PLL_REF_BIT_1_1250V; +	writel(reg, &dp_regs->pll_ctl); +} + +static void exynos_dp_init_interrupt(void) +{ +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); +	/* Set interrupt registers to initial states */ + +	/* +	 * Disable interrupt +	 * INT pin assertion polarity. It must be configured +	 * correctly according to ICU setting. +	 * 1 = assert high, 0 = assert low +	 */ +	writel(INT_POL, &dp_regs->int_ctl); + +	/* Clear pending regisers */ +	writel(0xff, &dp_regs->common_int_sta1); +	writel(0xff, &dp_regs->common_int_sta2); +	writel(0xff, &dp_regs->common_int_sta3); +	writel(0xff, &dp_regs->common_int_sta4); +	writel(0xff, &dp_regs->int_sta); + +	/* 0:mask,1: unmask */ +	writel(0x00, &dp_regs->int_sta_mask1); +	writel(0x00, &dp_regs->int_sta_mask2); +	writel(0x00, &dp_regs->int_sta_mask3); +	writel(0x00, &dp_regs->int_sta_mask4); +	writel(0x00, &dp_regs->int_sta_mask); +} + +void exynos_dp_reset(void) +{ +	unsigned int reg_func_1; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/*dp tx sw reset*/ +	writel(RESET_DP_TX, &dp_regs->tx_sw_reset); + +	exynos_dp_enable_video_input(DP_DISABLE); +	exynos_dp_enable_video_bist(DP_DISABLE); +	exynos_dp_enable_video_mute(DP_DISABLE); + +	/* software reset */ +	reg_func_1 = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N | +		AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N | +		HDCP_FUNC_EN_N | SW_FUNC_EN_N; + +	writel(reg_func_1, &dp_regs->func_en1); +	writel(reg_func_1, &dp_regs->func_en2); + +	mdelay(1); + +	exynos_dp_init_analog_param(); +	exynos_dp_init_interrupt(); + +	return; +} + +void exynos_dp_enable_sw_func(unsigned int enable) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->func_en1); +	reg &= ~(SW_FUNC_EN_N); + +	if (!enable) +		reg |= SW_FUNC_EN_N; + +	writel(reg, &dp_regs->func_en1); + +	return; +} + +unsigned int exynos_dp_set_analog_power_down(unsigned int block, u32 enable) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->phy_pd); +	switch (block) { +	case AUX_BLOCK: +		reg &= ~(AUX_PD); +		if (enable) +			reg |= AUX_PD; +		break; +	case CH0_BLOCK: +		reg &= ~(CH0_PD); +		if (enable) +			reg |= CH0_PD; +		break; +	case CH1_BLOCK: +		reg &= ~(CH1_PD); +		if (enable) +			reg |= CH1_PD; +		break; +	case CH2_BLOCK: +		reg &= ~(CH2_PD); +		if (enable) +			reg |= CH2_PD; +		break; +	case CH3_BLOCK: +		reg &= ~(CH3_PD); +		if (enable) +			reg |= CH3_PD; +		break; +	case ANALOG_TOTAL: +		reg &= ~PHY_PD; +		if (enable) +			reg |= PHY_PD; +		break; +	case POWER_ALL: +		reg &= ~(PHY_PD | AUX_PD | CH0_PD | CH1_PD | CH2_PD | +			CH3_PD); +		if (enable) +			reg |= (PHY_PD | AUX_PD | CH0_PD | CH1_PD | +				CH2_PD | CH3_PD); +		break; +	default: +		printf("DP undefined block number : %d\n",  block); +		return -1; +	} + +	writel(reg, &dp_regs->phy_pd); + +	return 0; +} + +unsigned int exynos_dp_get_pll_lock_status(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->debug_ctl); + +	if (reg & PLL_LOCK) +		return PLL_LOCKED; +	else +		return PLL_UNLOCKED; +} + +static void exynos_dp_set_pll_power(unsigned int enable) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->pll_ctl); +	reg &= ~(DP_PLL_PD); + +	if (!enable) +		reg |= DP_PLL_PD; + +	writel(reg, &dp_regs->pll_ctl); +} + +int exynos_dp_init_analog_func(void) +{ +	int ret = EXYNOS_DP_SUCCESS; +	unsigned int retry_cnt = 10; +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/*Power On All Analog block */ +	exynos_dp_set_analog_power_down(POWER_ALL, DP_DISABLE); + +	reg = PLL_LOCK_CHG; +	writel(reg, &dp_regs->common_int_sta1); + +	reg = readl(&dp_regs->debug_ctl); +	reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL); +	writel(reg, &dp_regs->debug_ctl); + +	/*Assert DP PLL Reset*/ +	reg = readl(&dp_regs->pll_ctl); +	reg |= DP_PLL_RESET; +	writel(reg, &dp_regs->pll_ctl); + +	mdelay(1); + +	/*Deassert DP PLL Reset*/ +	reg = readl(&dp_regs->pll_ctl); +	reg &= ~(DP_PLL_RESET); +	writel(reg, &dp_regs->pll_ctl); + +	exynos_dp_set_pll_power(DP_ENABLE); + +	while (exynos_dp_get_pll_lock_status() == PLL_UNLOCKED) { +		mdelay(1); +		retry_cnt--; +		if (retry_cnt == 0) { +			printf("DP dp's pll lock failed : retry : %d\n", +					retry_cnt); +			return -EINVAL; +		} +	} + +	debug("dp's pll lock success(%d)\n", retry_cnt); + +	/* Enable Serdes FIFO function and Link symbol clock domain module */ +	reg = readl(&dp_regs->func_en2); +	reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N +		| AUX_FUNC_EN_N); +	writel(reg, &dp_regs->func_en2); + +	return ret; +} + +void exynos_dp_init_hpd(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Clear interrupts releated to Hot Plug Dectect */ +	reg = HOTPLUG_CHG | HPD_LOST | PLUG; +	writel(reg, &dp_regs->common_int_sta4); + +	reg = INT_HPD; +	writel(reg, &dp_regs->int_sta); + +	reg = readl(&dp_regs->sys_ctl3); +	reg &= ~(F_HPD | HPD_CTRL); +	writel(reg, &dp_regs->sys_ctl3); + +	return; +} + +static inline void exynos_dp_reset_aux(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Disable AUX channel module */ +	reg = readl(&dp_regs->func_en2); +	reg |= AUX_FUNC_EN_N; +	writel(reg, &dp_regs->func_en2); + +	return; +} + +void exynos_dp_init_aux(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Clear inerrupts related to AUX channel */ +	reg = RPLY_RECEIV | AUX_ERR; +	writel(reg, &dp_regs->int_sta); + +	exynos_dp_reset_aux(); + +	/* Disable AUX transaction H/W retry */ +	reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(3)| +		AUX_HW_RETRY_INTERVAL_600_MICROSECONDS; +	writel(reg, &dp_regs->aux_hw_retry_ctl); + +	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */ +	reg = DEFER_CTRL_EN | DEFER_COUNT(1); +	writel(reg, &dp_regs->aux_ch_defer_ctl); + +	/* Enable AUX channel module */ +	reg = readl(&dp_regs->func_en2); +	reg &= ~AUX_FUNC_EN_N; +	writel(reg, &dp_regs->func_en2); + +	return; +} + +void exynos_dp_config_interrupt(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* 0: mask, 1: unmask */ +	reg = COMMON_INT_MASK_1; +	writel(reg, &dp_regs->common_int_mask1); + +	reg = COMMON_INT_MASK_2; +	writel(reg, &dp_regs->common_int_mask2); + +	reg = COMMON_INT_MASK_3; +	writel(reg, &dp_regs->common_int_mask3); + +	reg = COMMON_INT_MASK_4; +	writel(reg, &dp_regs->common_int_mask4); + +	reg = INT_STA_MASK; +	writel(reg, &dp_regs->int_sta_mask); + +	return; +} + +unsigned int exynos_dp_get_plug_in_status(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->sys_ctl3); +	if (reg & HPD_STATUS) +		return 0; + +	return -1; +} + +unsigned int exynos_dp_detect_hpd(void) +{ +	int timeout_loop = DP_TIMEOUT_LOOP_COUNT; + +	mdelay(2); + +	while (exynos_dp_get_plug_in_status() != 0) { +		if (timeout_loop == 0) +			return -EINVAL; +		mdelay(10); +		timeout_loop--; +	} + +	return EXYNOS_DP_SUCCESS; +} + +unsigned int exynos_dp_start_aux_transaction(void) +{ +	unsigned int reg; +	unsigned int ret = 0; +	unsigned int retry_cnt; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Enable AUX CH operation */ +	reg = readl(&dp_regs->aux_ch_ctl2); +	reg |= AUX_EN; +	writel(reg, &dp_regs->aux_ch_ctl2); + +	retry_cnt = 10; +	while (retry_cnt) { +		reg = readl(&dp_regs->int_sta); +		if (!(reg & RPLY_RECEIV)) { +			if (retry_cnt == 0) { +				printf("DP Reply Timeout!!\n"); +				ret = -EAGAIN; +				return ret; +			} +			mdelay(1); +			retry_cnt--; +		} else +			break; +	} + +	/* Clear interrupt source for AUX CH command reply */ +	writel(reg, &dp_regs->int_sta); + +	/* Clear interrupt source for AUX CH access error */ +	reg = readl(&dp_regs->int_sta); +	if (reg & AUX_ERR) { +		printf("DP Aux Access Error\n"); +		writel(AUX_ERR, &dp_regs->int_sta); +		ret = -EAGAIN; +		return ret; +	} + +	/* Check AUX CH error access status */ +	reg = readl(&dp_regs->aux_ch_sta); +	if ((reg & AUX_STATUS_MASK) != 0) { +		debug("DP AUX CH error happens: %x\n", reg & AUX_STATUS_MASK); +		ret = -EAGAIN; +		return ret; +	} + +	return EXYNOS_DP_SUCCESS; +} + +unsigned int exynos_dp_write_byte_to_dpcd(unsigned int reg_addr, +				unsigned char data) +{ +	unsigned int reg, ret; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Clear AUX CH data buffer */ +	reg = BUF_CLR; +	writel(reg, &dp_regs->buffer_data_ctl); + +	/* Select DPCD device address */ +	reg = AUX_ADDR_7_0(reg_addr); +	writel(reg, &dp_regs->aux_addr_7_0); +	reg = AUX_ADDR_15_8(reg_addr); +	writel(reg, &dp_regs->aux_addr_15_8); +	reg = AUX_ADDR_19_16(reg_addr); +	writel(reg, &dp_regs->aux_addr_19_16); + +	/* Write data buffer */ +	reg = (unsigned int)data; +	writel(reg, &dp_regs->buf_data0); + +	/* +	 * Set DisplayPort transaction and write 1 byte +	 * If bit 3 is 1, DisplayPort transaction. +	 * If Bit 3 is 0, I2C transaction. +	 */ +	reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; +	writel(reg, &dp_regs->aux_ch_ctl1); + +	/* Start AUX transaction */ +	ret = exynos_dp_start_aux_transaction(); +	if (ret != EXYNOS_DP_SUCCESS) { +		printf("DP Aux transaction failed\n"); +		return ret; +	} + +	return ret; +} + +unsigned int exynos_dp_read_byte_from_dpcd(unsigned int reg_addr, +		unsigned char *data) +{ +	unsigned int reg; +	int retval; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Clear AUX CH data buffer */ +	reg = BUF_CLR; +	writel(reg, &dp_regs->buffer_data_ctl); + +	/* Select DPCD device address */ +	reg = AUX_ADDR_7_0(reg_addr); +	writel(reg, &dp_regs->aux_addr_7_0); +	reg = AUX_ADDR_15_8(reg_addr); +	writel(reg, &dp_regs->aux_addr_15_8); +	reg = AUX_ADDR_19_16(reg_addr); +	writel(reg, &dp_regs->aux_addr_19_16); + +	/* +	 * Set DisplayPort transaction and read 1 byte +	 * If bit 3 is 1, DisplayPort transaction. +	 * If Bit 3 is 0, I2C transaction. +	 */ +	reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; +	writel(reg, &dp_regs->aux_ch_ctl1); + +	/* Start AUX transaction */ +	retval = exynos_dp_start_aux_transaction(); +	if (!retval) +		debug("DP Aux Transaction fail!\n"); + +	/* Read data buffer */ +	reg = readl(&dp_regs->buf_data0); +	*data = (unsigned char)(reg & 0xff); + +	return retval; +} + +unsigned int exynos_dp_write_bytes_to_dpcd(unsigned int reg_addr, +				unsigned int count, +				unsigned char data[]) +{ +	unsigned int reg; +	unsigned int start_offset; +	unsigned int cur_data_count; +	unsigned int cur_data_idx; +	unsigned int retry_cnt; +	unsigned int ret = 0; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Clear AUX CH data buffer */ +	reg = BUF_CLR; +	writel(reg, &dp_regs->buffer_data_ctl); + +	start_offset = 0; +	while (start_offset < count) { +		/* Buffer size of AUX CH is 16 * 4bytes */ +		if ((count - start_offset) > 16) +			cur_data_count = 16; +		else +			cur_data_count = count - start_offset; + +		retry_cnt = 5; +		while (retry_cnt) { +			/* Select DPCD device address */ +			reg = AUX_ADDR_7_0(reg_addr + start_offset); +			writel(reg, &dp_regs->aux_addr_7_0); +			reg = AUX_ADDR_15_8(reg_addr + start_offset); +			writel(reg, &dp_regs->aux_addr_15_8); +			reg = AUX_ADDR_19_16(reg_addr + start_offset); +			writel(reg, &dp_regs->aux_addr_19_16); + +			for (cur_data_idx = 0; cur_data_idx < cur_data_count; +					cur_data_idx++) { +				reg = data[start_offset + cur_data_idx]; +				writel(reg, (unsigned int)&dp_regs->buf_data0 + +						(4 * cur_data_idx)); +			} +			/* +			* Set DisplayPort transaction and write +			* If bit 3 is 1, DisplayPort transaction. +			* If Bit 3 is 0, I2C transaction. +			*/ +			reg = AUX_LENGTH(cur_data_count) | +				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE; +			writel(reg, &dp_regs->aux_ch_ctl1); + +			/* Start AUX transaction */ +			ret = exynos_dp_start_aux_transaction(); +			if (ret != EXYNOS_DP_SUCCESS) { +				if (retry_cnt == 0) { +					printf("DP Aux Transaction failed\n"); +					return ret; +				} +				retry_cnt--; +			} else +				break; +		} +		start_offset += cur_data_count; +	} + +	return ret; +} + +unsigned int exynos_dp_read_bytes_from_dpcd(unsigned int reg_addr, +				unsigned int count, +				unsigned char data[]) +{ +	unsigned int reg; +	unsigned int start_offset; +	unsigned int cur_data_count; +	unsigned int cur_data_idx; +	unsigned int retry_cnt; +	unsigned int ret = 0; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Clear AUX CH data buffer */ +	reg = BUF_CLR; +	writel(reg, &dp_regs->buffer_data_ctl); + +	start_offset = 0; +	while (start_offset < count) { +		/* Buffer size of AUX CH is 16 * 4bytes */ +		if ((count - start_offset) > 16) +			cur_data_count = 16; +		else +			cur_data_count = count - start_offset; + +		retry_cnt = 5; +		while (retry_cnt) { +			/* Select DPCD device address */ +			reg = AUX_ADDR_7_0(reg_addr + start_offset); +			writel(reg, &dp_regs->aux_addr_7_0); +			reg = AUX_ADDR_15_8(reg_addr + start_offset); +			writel(reg, &dp_regs->aux_addr_15_8); +			reg = AUX_ADDR_19_16(reg_addr + start_offset); +			writel(reg, &dp_regs->aux_addr_19_16); +			/* +			 * Set DisplayPort transaction and read +			 * If bit 3 is 1, DisplayPort transaction. +			 * If Bit 3 is 0, I2C transaction. +			 */ +			reg = AUX_LENGTH(cur_data_count) | +				AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ; +			writel(reg, &dp_regs->aux_ch_ctl1); + +			/* Start AUX transaction */ +			ret = exynos_dp_start_aux_transaction(); +			if (ret != EXYNOS_DP_SUCCESS) { +				if (retry_cnt == 0) { +					printf("DP Aux Transaction failed\n"); +					return ret; +				} +				retry_cnt--; +			} else +				break; +		} + +		for (cur_data_idx = 0; cur_data_idx < cur_data_count; +				cur_data_idx++) { +			reg = readl((unsigned int)&dp_regs->buf_data0 + +					4 * cur_data_idx); +			data[start_offset + cur_data_idx] = (unsigned char)reg; +		} + +		start_offset += cur_data_count; +	} + +	return ret; +} + +int exynos_dp_select_i2c_device(unsigned int device_addr, +				unsigned int reg_addr) +{ +	unsigned int reg; +	int retval; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Set EDID device address */ +	reg = device_addr; +	writel(reg, &dp_regs->aux_addr_7_0); +	writel(0x0, &dp_regs->aux_addr_15_8); +	writel(0x0, &dp_regs->aux_addr_19_16); + +	/* Set offset from base address of EDID device */ +	writel(reg_addr, &dp_regs->buf_data0); + +	/* +	 * Set I2C transaction and write address +	 * If bit 3 is 1, DisplayPort transaction. +	 * If Bit 3 is 0, I2C transaction. +	 */ +	reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT | +		AUX_TX_COMM_WRITE; +	writel(reg, &dp_regs->aux_ch_ctl1); + +	/* Start AUX transaction */ +	retval = exynos_dp_start_aux_transaction(); +	if (retval != 0) +		printf("%s: DP Aux Transaction fail!\n", __func__); + +	return retval; +} + +int exynos_dp_read_byte_from_i2c(unsigned int device_addr, +				unsigned int reg_addr, +				unsigned int *data) +{ +	unsigned int reg; +	int i; +	int retval; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	for (i = 0; i < 10; i++) { +		/* Clear AUX CH data buffer */ +		reg = BUF_CLR; +		writel(reg, &dp_regs->buffer_data_ctl); + +		/* Select EDID device */ +		retval = exynos_dp_select_i2c_device(device_addr, reg_addr); +		if (retval != 0) { +			printf("DP Select EDID device fail. retry !\n"); +			continue; +		} + +		/* +		 * Set I2C transaction and read data +		 * If bit 3 is 1, DisplayPort transaction. +		 * If Bit 3 is 0, I2C transaction. +		 */ +		reg = AUX_TX_COMM_I2C_TRANSACTION | +			AUX_TX_COMM_READ; +		writel(reg, &dp_regs->aux_ch_ctl1); + +		/* Start AUX transaction */ +		retval = exynos_dp_start_aux_transaction(); +		if (retval != EXYNOS_DP_SUCCESS) +			printf("%s: DP Aux Transaction fail!\n", __func__); +	} + +	/* Read data */ +	if (retval == 0) +		*data = readl(&dp_regs->buf_data0); + +	return retval; +} + +int exynos_dp_read_bytes_from_i2c(unsigned int device_addr, +		unsigned int reg_addr, unsigned int count, unsigned char edid[]) +{ +	unsigned int reg; +	unsigned int i, j; +	unsigned int cur_data_idx; +	unsigned int defer = 0; +	int retval = 0; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	for (i = 0; i < count; i += 16) { /* use 16 burst */ +		for (j = 0; j < 100; j++) { +			/* Clear AUX CH data buffer */ +			reg = BUF_CLR; +			writel(reg, &dp_regs->buffer_data_ctl); + +			/* Set normal AUX CH command */ +			reg = readl(&dp_regs->aux_ch_ctl2); +			reg &= ~ADDR_ONLY; +			writel(reg, &dp_regs->aux_ch_ctl2); + +			/* +			 * If Rx sends defer, Tx sends only reads +			 * request without sending addres +			 */ +			if (!defer) +				retval = +					exynos_dp_select_i2c_device(device_addr, +							reg_addr + i); +			else +				defer = 0; + +			if (retval == EXYNOS_DP_SUCCESS) { +				/* +				 * Set I2C transaction and write data +				 * If bit 3 is 1, DisplayPort transaction. +				 * If Bit 3 is 0, I2C transaction. +				 */ +				reg = AUX_LENGTH(16) | +					AUX_TX_COMM_I2C_TRANSACTION | +					AUX_TX_COMM_READ; +				writel(reg, &dp_regs->aux_ch_ctl1); + +				/* Start AUX transaction */ +				retval = exynos_dp_start_aux_transaction(); +				if (retval == 0) +					break; +				else +					printf("DP Aux Transaction fail!\n"); +			} +			/* Check if Rx sends defer */ +			reg = readl(&dp_regs->aux_rx_comm); +			if (reg == AUX_RX_COMM_AUX_DEFER || +				reg == AUX_RX_COMM_I2C_DEFER) { +				printf("DP Defer: %d\n\n", reg); +				defer = 1; +			} +		} + +		for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) { +			reg = readl((unsigned int)&dp_regs->buf_data0 +						 + 4 * cur_data_idx); +			edid[i + cur_data_idx] = (unsigned char)reg; +		} +	} + +	return retval; +} + +void exynos_dp_reset_macro(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->phy_test); +	reg |= MACRO_RST; +	writel(reg, &dp_regs->phy_test); + +	/* 10 us is the minimum Macro reset time. */ +	mdelay(1); + +	reg &= ~MACRO_RST; +	writel(reg, &dp_regs->phy_test); +} + +void exynos_dp_set_link_bandwidth(unsigned char bwtype) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = (unsigned int)bwtype; + +	 /* Set bandwidth to 2.7G or 1.62G */ +	if ((bwtype == DP_LANE_BW_1_62) || (bwtype == DP_LANE_BW_2_70)) +		writel(reg, &dp_regs->link_bw_set); +} + +unsigned char exynos_dp_get_link_bandwidth(void) +{ +	unsigned char ret; +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->link_bw_set); +	ret = (unsigned char)reg; + +	return ret; +} + +void exynos_dp_set_lane_count(unsigned char count) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = (unsigned int)count; + +	if ((count == DP_LANE_CNT_1) || (count == DP_LANE_CNT_2) || +			(count == DP_LANE_CNT_4)) +		writel(reg, &dp_regs->lane_count_set); +} + +unsigned int exynos_dp_get_lane_count(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->lane_count_set); + +	return reg; +} + +unsigned char exynos_dp_get_lanex_pre_emphasis(unsigned char lanecnt) +{ +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); +	unsigned int reg_list[DP_LANE_CNT_4] = { +		(unsigned int)&dp_regs->ln0_link_training_ctl, +		(unsigned int)&dp_regs->ln1_link_training_ctl, +		(unsigned int)&dp_regs->ln2_link_training_ctl, +		(unsigned int)&dp_regs->ln3_link_training_ctl, +	}; + +	return readl(reg_list[lanecnt]); +} + +void exynos_dp_set_lanex_pre_emphasis(unsigned char request_val, +		unsigned char lanecnt) +{ +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); +	unsigned int reg_list[DP_LANE_CNT_4] = { +		(unsigned int)&dp_regs->ln0_link_training_ctl, +		(unsigned int)&dp_regs->ln1_link_training_ctl, +		(unsigned int)&dp_regs->ln2_link_training_ctl, +		(unsigned int)&dp_regs->ln3_link_training_ctl, +	}; + +	writel(request_val, reg_list[lanecnt]); +} + +void exynos_dp_set_lane_pre_emphasis(unsigned int level, unsigned char lanecnt) +{ +	unsigned char i; +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); +	unsigned int reg_list[DP_LANE_CNT_4] = { +		(unsigned int)&dp_regs->ln0_link_training_ctl, +		(unsigned int)&dp_regs->ln1_link_training_ctl, +		(unsigned int)&dp_regs->ln2_link_training_ctl, +		(unsigned int)&dp_regs->ln3_link_training_ctl, +	}; +	unsigned int reg_shift[DP_LANE_CNT_4] = { +		PRE_EMPHASIS_SET_0_SHIFT, +		PRE_EMPHASIS_SET_1_SHIFT, +		PRE_EMPHASIS_SET_2_SHIFT, +		PRE_EMPHASIS_SET_3_SHIFT +	}; + +	for (i = 0; i < lanecnt; i++) { +		reg = level << reg_shift[i]; +		writel(reg, reg_list[i]); +	} +} + +void exynos_dp_set_training_pattern(unsigned int pattern) +{ +	unsigned int reg = 0; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	switch (pattern) { +	case PRBS7: +		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7; +		break; +	case D10_2: +		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2; +		break; +	case TRAINING_PTN1: +		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1; +		break; +	case TRAINING_PTN2: +		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2; +		break; +	case DP_NONE: +		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_DISABLE | +			SW_TRAINING_PATTERN_SET_NORMAL; +		break; +	default: +		break; +	} + +	writel(reg, &dp_regs->training_ptn_set); +} + +void exynos_dp_enable_enhanced_mode(unsigned char enable) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->sys_ctl4); +	reg &= ~ENHANCED; + +	if (enable) +		reg |= ENHANCED; + +	writel(reg, &dp_regs->sys_ctl4); +} + +void exynos_dp_enable_scrambling(unsigned int enable) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->training_ptn_set); +	reg &= ~(SCRAMBLING_DISABLE); + +	if (!enable) +		reg |= SCRAMBLING_DISABLE; + +	writel(reg, &dp_regs->training_ptn_set); +} + +int exynos_dp_init_video(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Clear VID_CLK_CHG[1] and VID_FORMAT_CHG[3] and VSYNC_DET[7] */ +	reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG; +	writel(reg, &dp_regs->common_int_sta1); + +	/* I_STRM__CLK detect : DE_CTL : Auto detect */ +	reg &= ~DET_CTRL; +	writel(reg, &dp_regs->sys_ctl1); + +	return 0; +} + +void exynos_dp_config_video_slave_mode(struct edp_video_info *video_info) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Video Slave mode setting */ +	reg = readl(&dp_regs->func_en1); +	reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N); +	reg |= MASTER_VID_FUNC_EN_N; +	writel(reg, &dp_regs->func_en1); + +	/* Configure Interlaced for slave mode video */ +	reg = readl(&dp_regs->video_ctl10); +	reg &= ~INTERACE_SCAN_CFG; +	reg |= (video_info->interlaced << INTERACE_SCAN_CFG_SHIFT); +	writel(reg, &dp_regs->video_ctl10); + +	/* Configure V sync polarity for slave mode video */ +	reg = readl(&dp_regs->video_ctl10); +	reg &= ~VSYNC_POLARITY_CFG; +	reg |= (video_info->v_sync_polarity << V_S_POLARITY_CFG_SHIFT); +	writel(reg, &dp_regs->video_ctl10); + +	/* Configure H sync polarity for slave mode video */ +	reg = readl(&dp_regs->video_ctl10); +	reg &= ~HSYNC_POLARITY_CFG; +	reg |= (video_info->h_sync_polarity << H_S_POLARITY_CFG_SHIFT); +	writel(reg, &dp_regs->video_ctl10); + +	/*Set video mode to slave mode */ +	reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE; +	writel(reg, &dp_regs->soc_general_ctl); +} + +void exynos_dp_set_video_color_format(struct edp_video_info *video_info) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Configure the input color depth, color space, dynamic range */ +	reg = (video_info->dynamic_range << IN_D_RANGE_SHIFT) | +		(video_info->color_depth << IN_BPC_SHIFT) | +		(video_info->color_space << IN_COLOR_F_SHIFT); +	writel(reg, &dp_regs->video_ctl2); + +	/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */ +	reg = readl(&dp_regs->video_ctl3); +	reg &= ~IN_YC_COEFFI_MASK; +	if (video_info->ycbcr_coeff) +		reg |= IN_YC_COEFFI_ITU709; +	else +		reg |= IN_YC_COEFFI_ITU601; +	writel(reg, &dp_regs->video_ctl3); +} + +int exynos_dp_config_video_bist(struct edp_device_info *edp_info) +{ +	unsigned int reg; +	unsigned int bist_type = 0; +	struct edp_video_info video_info = edp_info->video_info; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* For master mode, you don't need to set the video format */ +	if (video_info.master_mode == 0) { +		writel(TOTAL_LINE_CFG_L(edp_info->disp_info.v_total), +				&dp_regs->total_ln_cfg_l); +		writel(TOTAL_LINE_CFG_H(edp_info->disp_info.v_total), +				&dp_regs->total_ln_cfg_h); +		writel(ACTIVE_LINE_CFG_L(edp_info->disp_info.v_res), +				&dp_regs->active_ln_cfg_l); +		writel(ACTIVE_LINE_CFG_H(edp_info->disp_info.v_res), +				&dp_regs->active_ln_cfg_h); +		writel(edp_info->disp_info.v_sync_width, +				&dp_regs->vsw_cfg); +		writel(edp_info->disp_info.v_back_porch, +				&dp_regs->vbp_cfg); +		writel(edp_info->disp_info.v_front_porch, +				&dp_regs->vfp_cfg); + +		writel(TOTAL_PIXEL_CFG_L(edp_info->disp_info.h_total), +				&dp_regs->total_pix_cfg_l); +		writel(TOTAL_PIXEL_CFG_H(edp_info->disp_info.h_total), +				&dp_regs->total_pix_cfg_h); +		writel(ACTIVE_PIXEL_CFG_L(edp_info->disp_info.h_res), +				&dp_regs->active_pix_cfg_l); +		writel(ACTIVE_PIXEL_CFG_H(edp_info->disp_info.h_res), +				&dp_regs->active_pix_cfg_h); +		writel(H_F_PORCH_CFG_L(edp_info->disp_info.h_front_porch), +				&dp_regs->hfp_cfg_l); +		writel(H_F_PORCH_CFG_H(edp_info->disp_info.h_front_porch), +				&dp_regs->hfp_cfg_h); +		writel(H_SYNC_PORCH_CFG_L(edp_info->disp_info.h_sync_width), +				&dp_regs->hsw_cfg_l); +		writel(H_SYNC_PORCH_CFG_H(edp_info->disp_info.h_sync_width), +				&dp_regs->hsw_cfg_h); +		writel(H_B_PORCH_CFG_L(edp_info->disp_info.h_back_porch), +				&dp_regs->hbp_cfg_l); +		writel(H_B_PORCH_CFG_H(edp_info->disp_info.h_back_porch), +				&dp_regs->hbp_cfg_h); + +		/* +		 * Set SLAVE_I_SCAN_CFG[2], VSYNC_P_CFG[1], +		 * HSYNC_P_CFG[0] properly +		 */ +		reg = (video_info.interlaced << INTERACE_SCAN_CFG_SHIFT | +			video_info.v_sync_polarity << V_S_POLARITY_CFG_SHIFT | +			video_info.h_sync_polarity << H_S_POLARITY_CFG_SHIFT); +		writel(reg, &dp_regs->video_ctl10); +	} + +	/* BIST color bar width set--set to each bar is 32 pixel width */ +	switch (video_info.bist_pattern) { +	case COLORBAR_32: +		bist_type = BIST_WIDTH_BAR_32_PIXEL | +			  BIST_TYPE_COLOR_BAR; +		break; +	case COLORBAR_64: +		bist_type = BIST_WIDTH_BAR_64_PIXEL | +			  BIST_TYPE_COLOR_BAR; +		break; +	case WHITE_GRAY_BALCKBAR_32: +		bist_type = BIST_WIDTH_BAR_32_PIXEL | +			  BIST_TYPE_WHITE_GRAY_BLACK_BAR; +		break; +	case WHITE_GRAY_BALCKBAR_64: +		bist_type = BIST_WIDTH_BAR_64_PIXEL | +			  BIST_TYPE_WHITE_GRAY_BLACK_BAR; +		break; +	case MOBILE_WHITEBAR_32: +		bist_type = BIST_WIDTH_BAR_32_PIXEL | +			  BIST_TYPE_MOBILE_WHITE_BAR; +		break; +	case MOBILE_WHITEBAR_64: +		bist_type = BIST_WIDTH_BAR_64_PIXEL | +			  BIST_TYPE_MOBILE_WHITE_BAR; +		break; +	default: +		return -1; +	} + +	reg = bist_type; +	writel(reg, &dp_regs->video_ctl4); + +	return 0; +} + +unsigned int exynos_dp_is_slave_video_stream_clock_on(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Update Video stream clk detect status */ +	reg = readl(&dp_regs->sys_ctl1); +	writel(reg, &dp_regs->sys_ctl1); + +	reg = readl(&dp_regs->sys_ctl1); + +	if (!(reg & DET_STA)) { +		debug("DP Input stream clock not detected.\n"); +		return -EIO; +	} + +	return EXYNOS_DP_SUCCESS; +} + +void exynos_dp_set_video_cr_mn(unsigned int type, unsigned int m_value, +		unsigned int n_value) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	if (type == REGISTER_M) { +		reg = readl(&dp_regs->sys_ctl4); +		reg |= FIX_M_VID; +		writel(reg, &dp_regs->sys_ctl4); +		reg = M_VID0_CFG(m_value); +		writel(reg, &dp_regs->m_vid0); +		reg = M_VID1_CFG(m_value); +		writel(reg, &dp_regs->m_vid1); +		reg = M_VID2_CFG(m_value); +		writel(reg, &dp_regs->m_vid2); + +		reg = N_VID0_CFG(n_value); +		writel(reg, &dp_regs->n_vid0); +		reg = N_VID1_CFG(n_value); +		writel(reg, &dp_regs->n_vid1); +		reg = N_VID2_CFG(n_value); +		writel(reg, &dp_regs->n_vid2); +	} else  { +		reg = readl(&dp_regs->sys_ctl4); +		reg &= ~FIX_M_VID; +		writel(reg, &dp_regs->sys_ctl4); +	} +} + +void exynos_dp_set_video_timing_mode(unsigned int type) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->video_ctl10); +	reg &= ~FORMAT_SEL; + +	if (type != VIDEO_TIMING_FROM_CAPTURE) +		reg |= FORMAT_SEL; + +	writel(reg, &dp_regs->video_ctl10); +} + +void exynos_dp_enable_video_master(unsigned int enable) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	reg = readl(&dp_regs->soc_general_ctl); +	if (enable) { +		reg &= ~VIDEO_MODE_MASK; +		reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE; +	} else { +		reg &= ~VIDEO_MODE_MASK; +		reg |= VIDEO_MODE_SLAVE_MODE; +	} + +	writel(reg, &dp_regs->soc_general_ctl); +} + +void exynos_dp_start_video(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Enable Video input and disable Mute */ +	reg = readl(&dp_regs->video_ctl1); +	reg |= VIDEO_EN; +	writel(reg, &dp_regs->video_ctl1); +} + +unsigned int exynos_dp_is_video_stream_on(void) +{ +	unsigned int reg; +	struct exynos_dp *dp_regs = (struct exynos_dp *)samsung_get_base_dp(); + +	/* Update STRM_VALID */ +	reg = readl(&dp_regs->sys_ctl3); +	writel(reg, &dp_regs->sys_ctl3); + +	reg = readl(&dp_regs->sys_ctl3); +	if (!(reg & STRM_VALID)) +		return -EIO; + +	return EXYNOS_DP_SUCCESS; +} diff --git a/drivers/video/exynos_dp_lowlevel.h b/drivers/video/exynos_dp_lowlevel.h new file mode 100644 index 000000000..a041a7ab5 --- /dev/null +++ b/drivers/video/exynos_dp_lowlevel.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _EXYNOS_EDP_LOWLEVEL_H +#define _EXYNOS_EDP_LOWLEVEL_H + +void exynos_dp_enable_video_bist(unsigned int enable); +void exynos_dp_enable_video_mute(unsigned int enable); +void exynos_dp_reset(void); +void exynos_dp_enable_sw_func(unsigned int enable); +unsigned int exynos_dp_set_analog_power_down(unsigned int block, u32 enable); +unsigned int exynos_dp_get_pll_lock_status(void); +int exynos_dp_init_analog_func(void); +void exynos_dp_init_hpd(void); +void exynos_dp_init_aux(void); +void exynos_dp_config_interrupt(void); +unsigned int exynos_dp_get_plug_in_status(void); +unsigned int exynos_dp_detect_hpd(void); +unsigned int exynos_dp_start_aux_transaction(void); +unsigned int exynos_dp_write_byte_to_dpcd(unsigned int reg_addr, +				unsigned char data); +unsigned int exynos_dp_read_byte_from_dpcd(unsigned int reg_addr, +		unsigned char *data); +unsigned int exynos_dp_write_bytes_to_dpcd(unsigned int reg_addr, +		unsigned int count, +		unsigned char data[]); +unsigned int exynos_dp_read_bytes_from_dpcd( unsigned int reg_addr, +		unsigned int count, +		unsigned char data[]); +int exynos_dp_select_i2c_device( unsigned int device_addr, +		unsigned int reg_addr); +int exynos_dp_read_byte_from_i2c(unsigned int device_addr, +		unsigned int reg_addr, unsigned int *data); +int exynos_dp_read_bytes_from_i2c(unsigned int device_addr, +		unsigned int reg_addr, unsigned int count, +		unsigned char edid[]); +void exynos_dp_reset_macro(void); +void exynos_dp_set_link_bandwidth(unsigned char bwtype); +unsigned char exynos_dp_get_link_bandwidth(void); +void exynos_dp_set_lane_count(unsigned char count); +unsigned int exynos_dp_get_lane_count(void); +unsigned char exynos_dp_get_lanex_pre_emphasis(unsigned char lanecnt); +void exynos_dp_set_lane_pre_emphasis(unsigned int level, +		unsigned char lanecnt); +void exynos_dp_set_lanex_pre_emphasis(unsigned char request_val, +		unsigned char lanecnt); +void exynos_dp_set_training_pattern(unsigned int pattern); +void exynos_dp_enable_enhanced_mode(unsigned char enable); +void exynos_dp_enable_scrambling(unsigned int enable); +int exynos_dp_init_video(void); +void exynos_dp_config_video_slave_mode(struct edp_video_info *video_info); +void exynos_dp_set_video_color_format(struct edp_video_info *video_info); +int exynos_dp_config_video_bist(struct edp_device_info *edp_info); +unsigned int exynos_dp_is_slave_video_stream_clock_on(void); +void exynos_dp_set_video_cr_mn(unsigned int type, unsigned int m_value, +		unsigned int n_value); +void exynos_dp_set_video_timing_mode(unsigned int type); +void exynos_dp_enable_video_master(unsigned int enable); +void exynos_dp_start_video(void); +unsigned int exynos_dp_is_video_stream_on(void); + +#endif /* _EXYNOS_DP_LOWLEVEL_H */ diff --git a/drivers/video/exynos_fb.c b/drivers/video/exynos_fb.c index 49fdfec76..e31a0fd50 100644 --- a/drivers/video/exynos_fb.c +++ b/drivers/video/exynos_fb.c @@ -28,6 +28,7 @@  #include <asm/arch/clock.h>  #include <asm/arch/clk.h>  #include <asm/arch/mipi_dsim.h> +#include <asm/arch/dp_info.h>  #include <asm/arch/system.h>  #include "exynos_fb.h" @@ -91,6 +92,9 @@ static void lcd_panel_on(vidinfo_t *vid)  	udelay(vid->power_on_delay); +	if (vid->dp_enabled) +		exynos_init_dp(); +  	if (vid->reset_lcd) {  		vid->reset_lcd();  		udelay(vid->reset_delay); @@ -130,7 +134,6 @@ void lcd_enable(void)  	if (panel_info.logo_on) {  		memset(lcd_base, 0, panel_width * panel_height *  				(NBITS(panel_info.vl_bpix) >> 3)); -  		draw_logo();  	} diff --git a/drivers/video/exynos_fimd.c b/drivers/video/exynos_fimd.c index f07568acc..06eae2ed7 100644 --- a/drivers/video/exynos_fimd.c +++ b/drivers/video/exynos_fimd.c @@ -41,8 +41,8 @@ void exynos_fimd_lcd_init_mem(u_long screen_base, u_long fb_size,  static void exynos_fimd_set_dualrgb(unsigned int enabled)  { -	struct exynos4_fb *fimd_ctrl = -		(struct exynos4_fb *)samsung_get_base_fimd(); +	struct exynos_fb *fimd_ctrl = +		(struct exynos_fb *)samsung_get_base_fimd();  	unsigned int cfg = 0;  	if (enabled) { @@ -57,11 +57,24 @@ static void exynos_fimd_set_dualrgb(unsigned int enabled)  	writel(cfg, &fimd_ctrl->dualrgb);  } +static void exynos_fimd_set_dp_clkcon(unsigned int enabled) +{ + +	struct exynos_fb *fimd_ctrl = +		(struct exynos_fb *)samsung_get_base_fimd(); +	unsigned int cfg = 0; + +	if (enabled) +		cfg = EXYNOS_DP_CLK_ENABLE; + +	writel(cfg, &fimd_ctrl->dp_mie_clkcon); +} +  static void exynos_fimd_set_par(unsigned int win_id)  {  	unsigned int cfg = 0; -	struct exynos4_fb *fimd_ctrl = -		(struct exynos4_fb *)samsung_get_base_fimd(); +	struct exynos_fb *fimd_ctrl = +		(struct exynos_fb *)samsung_get_base_fimd();  	/* set window control */  	cfg = readl((unsigned int)&fimd_ctrl->wincon0 + @@ -93,7 +106,10 @@ static void exynos_fimd_set_par(unsigned int win_id)  			EXYNOS_VIDOSD(win_id));  	cfg = EXYNOS_VIDOSD_RIGHT_X(pvid->vl_col - 1) | -		EXYNOS_VIDOSD_BOTTOM_Y(pvid->vl_row - 1); +		EXYNOS_VIDOSD_BOTTOM_Y(pvid->vl_row - 1) | +		EXYNOS_VIDOSD_RIGHT_X_E(1) | +		EXYNOS_VIDOSD_BOTTOM_Y_E(0); +  	writel(cfg, (unsigned int)&fimd_ctrl->vidosd0b +  			EXYNOS_VIDOSD(win_id)); @@ -106,8 +122,8 @@ static void exynos_fimd_set_par(unsigned int win_id)  static void exynos_fimd_set_buffer_address(unsigned int win_id)  {  	unsigned long start_addr, end_addr; -	struct exynos4_fb *fimd_ctrl = -		(struct exynos4_fb *)samsung_get_base_fimd(); +	struct exynos_fb *fimd_ctrl = +		(struct exynos_fb *)samsung_get_base_fimd();  	start_addr = (unsigned long)lcd_base_addr;  	end_addr = start_addr + ((pvid->vl_col * (NBITS(pvid->vl_bpix) / 8)) * @@ -124,8 +140,8 @@ static void exynos_fimd_set_clock(vidinfo_t *pvid)  	unsigned int cfg = 0, div = 0, remainder, remainder_div;  	unsigned long pixel_clock;  	unsigned long long src_clock; -	struct exynos4_fb *fimd_ctrl = -		(struct exynos4_fb *)samsung_get_base_fimd(); +	struct exynos_fb *fimd_ctrl = +		(struct exynos_fb *)samsung_get_base_fimd();  	if (pvid->dual_lcd_enabled) {  		pixel_clock = pvid->vl_freq * @@ -153,9 +169,6 @@ static void exynos_fimd_set_clock(vidinfo_t *pvid)  	cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS |  		EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED); -	if (pixel_clock > MAX_CLOCK) -		pixel_clock = MAX_CLOCK; -  	src_clock = (unsigned long long) get_lcd_clk();  	/* get quotient and remainder. */ @@ -180,8 +193,8 @@ static void exynos_fimd_set_clock(vidinfo_t *pvid)  void exynos_set_trigger(void)  {  	unsigned int cfg = 0; -	struct exynos4_fb *fimd_ctrl = -		(struct exynos4_fb *)samsung_get_base_fimd(); +	struct exynos_fb *fimd_ctrl = +		(struct exynos_fb *)samsung_get_base_fimd();  	cfg = readl(&fimd_ctrl->trigcon); @@ -194,8 +207,8 @@ int exynos_is_i80_frame_done(void)  {  	unsigned int cfg = 0;  	int status; -	struct exynos4_fb *fimd_ctrl = -		(struct exynos4_fb *)samsung_get_base_fimd(); +	struct exynos_fb *fimd_ctrl = +		(struct exynos_fb *)samsung_get_base_fimd();  	cfg = readl(&fimd_ctrl->trigcon); @@ -209,8 +222,8 @@ int exynos_is_i80_frame_done(void)  static void exynos_fimd_lcd_on(void)  {  	unsigned int cfg = 0; -	struct exynos4_fb *fimd_ctrl = -		(struct exynos4_fb *)samsung_get_base_fimd(); +	struct exynos_fb *fimd_ctrl = +		(struct exynos_fb *)samsung_get_base_fimd();  	/* display on */  	cfg = readl(&fimd_ctrl->vidcon0); @@ -221,8 +234,8 @@ static void exynos_fimd_lcd_on(void)  static void exynos_fimd_window_on(unsigned int win_id)  {  	unsigned int cfg = 0; -	struct exynos4_fb *fimd_ctrl = -		(struct exynos4_fb *)samsung_get_base_fimd(); +	struct exynos_fb *fimd_ctrl = +		(struct exynos_fb *)samsung_get_base_fimd();  	/* enable window */  	cfg = readl((unsigned int)&fimd_ctrl->wincon0 + @@ -239,8 +252,8 @@ static void exynos_fimd_window_on(unsigned int win_id)  void exynos_fimd_lcd_off(void)  {  	unsigned int cfg = 0; -	struct exynos4_fb *fimd_ctrl = -		(struct exynos4_fb *)samsung_get_base_fimd(); +	struct exynos_fb *fimd_ctrl = +		(struct exynos_fb *)samsung_get_base_fimd();  	cfg = readl(&fimd_ctrl->vidcon0);  	cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE); @@ -250,8 +263,8 @@ void exynos_fimd_lcd_off(void)  void exynos_fimd_window_off(unsigned int win_id)  {  	unsigned int cfg = 0; -	struct exynos4_fb *fimd_ctrl = -		(struct exynos4_fb *)samsung_get_base_fimd(); +	struct exynos_fb *fimd_ctrl = +		(struct exynos_fb *)samsung_get_base_fimd();  	cfg = readl((unsigned int)&fimd_ctrl->wincon0 +  			EXYNOS_WINCON(win_id)); @@ -264,11 +277,15 @@ void exynos_fimd_window_off(unsigned int win_id)  	writel(cfg, &fimd_ctrl->winshmap);  } +  void exynos_fimd_lcd_init(vidinfo_t *vid)  {  	unsigned int cfg = 0, rgb_mode; -	struct exynos4_fb *fimd_ctrl = -		(struct exynos4_fb *)samsung_get_base_fimd(); +	unsigned int offset; +	struct exynos_fb *fimd_ctrl = +		(struct exynos_fb *)samsung_get_base_fimd(); + +	offset = exynos_fimd_get_base_offset();  	/* store panel info to global variable */  	pvid = vid; @@ -297,25 +314,27 @@ void exynos_fimd_lcd_init(vidinfo_t *vid)  		if (!pvid->vl_dp)  			cfg |= EXYNOS_VIDCON1_IVDEN_INVERT; -		writel(cfg, &fimd_ctrl->vidcon1); +		writel(cfg, (unsigned int)&fimd_ctrl->vidcon1 + offset);  		/* set timing */  		cfg = EXYNOS_VIDTCON0_VFPD(pvid->vl_vfpd - 1);  		cfg |= EXYNOS_VIDTCON0_VBPD(pvid->vl_vbpd - 1);  		cfg |= EXYNOS_VIDTCON0_VSPW(pvid->vl_vspw - 1); -		writel(cfg, &fimd_ctrl->vidtcon0); +		writel(cfg, (unsigned int)&fimd_ctrl->vidtcon0 + offset);  		cfg = EXYNOS_VIDTCON1_HFPD(pvid->vl_hfpd - 1);  		cfg |= EXYNOS_VIDTCON1_HBPD(pvid->vl_hbpd - 1);  		cfg |= EXYNOS_VIDTCON1_HSPW(pvid->vl_hspw - 1); -		writel(cfg, &fimd_ctrl->vidtcon1); +		writel(cfg, (unsigned int)&fimd_ctrl->vidtcon1 + offset);  		/* set lcd size */ -		cfg = EXYNOS_VIDTCON2_HOZVAL(pvid->vl_col - 1); -		cfg |= EXYNOS_VIDTCON2_LINEVAL(pvid->vl_row - 1); +		cfg = EXYNOS_VIDTCON2_HOZVAL(pvid->vl_col - 1) | +			EXYNOS_VIDTCON2_LINEVAL(pvid->vl_row - 1) | +			EXYNOS_VIDTCON2_HOZVAL_E(pvid->vl_col - 1) | +			EXYNOS_VIDTCON2_LINEVAL_E(pvid->vl_row - 1); -		writel(cfg, &fimd_ctrl->vidtcon2); +		writel(cfg, (unsigned int)&fimd_ctrl->vidtcon2 + offset);  	}  	/* set display mode */ @@ -331,7 +350,11 @@ void exynos_fimd_lcd_init(vidinfo_t *vid)  	exynos_fimd_set_buffer_address(pvid->win_id);  	/* set buffer size */ -	cfg = EXYNOS_VIDADDR_PAGEWIDTH(pvid->vl_col * NBITS(pvid->vl_bpix) / 8); +	cfg = EXYNOS_VIDADDR_PAGEWIDTH(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) | +		EXYNOS_VIDADDR_PAGEWIDTH_E(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) | +		EXYNOS_VIDADDR_OFFSIZE(0) | +		EXYNOS_VIDADDR_OFFSIZE_E(0); +  	writel(cfg, (unsigned int)&fimd_ctrl->vidw00add2 +  					EXYNOS_BUFFER_SIZE(pvid->win_id)); @@ -346,6 +369,8 @@ void exynos_fimd_lcd_init(vidinfo_t *vid)  	/* window on */  	exynos_fimd_window_on(pvid->win_id); + +	exynos_fimd_set_dp_clkcon(pvid->dp_enabled);  }  unsigned long exynos_fimd_calc_fbsize(void) diff --git a/drivers/video/exynos_pwm_bl.c b/drivers/video/exynos_pwm_bl.c new file mode 100644 index 000000000..27fb0b5e7 --- /dev/null +++ b/drivers/video/exynos_pwm_bl.c @@ -0,0 +1,57 @@ +/* + * PWM BACKLIGHT driver for Board based on EXYNOS. + * + * Author: Donghwa Lee  <dh09.lee@samsung.com> + * + * Derived from linux/drivers/video/backlight/pwm_backlight.c + * + * 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 <pwm.h> +#include <linux/types.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch/pwm.h> +#include <asm/arch/pwm_backlight.h> + +static struct pwm_backlight_data *pwm; + +static int exynos_pwm_backlight_update_status(void) +{ +	int brightness = pwm->brightness; +	int max = pwm->max_brightness; + +	if (brightness == 0) { +		pwm_config(pwm->pwm_id, 0, pwm->period); +		pwm_disable(pwm->pwm_id); +	} else { +		pwm_config(pwm->pwm_id, +			brightness * pwm->period / max, pwm->period); +		pwm_enable(pwm->pwm_id); +	} +	return 0; +} + +int exynos_pwm_backlight_init(struct pwm_backlight_data *pd) +{ +	pwm = pd; + +	exynos_pwm_backlight_update_status(); + +	return 0; +} diff --git a/drivers/video/omap3_dss.c b/drivers/video/omap3_dss.c index 6686718b0..b1424bfd0 100644 --- a/drivers/video/omap3_dss.c +++ b/drivers/video/omap3_dss.c @@ -112,7 +112,7 @@ void omap3_dss_panel_config(const struct panel_config *panel_cfg)  	writel(panel_cfg->pol_freq, &dispc->pol_freq);  	writel(panel_cfg->divisor, &dispc->divisor);  	writel(panel_cfg->lcd_size, &dispc->size_lcd); -	writel(panel_cfg->load_mode << FRAME_MODE_SHIFT, &dispc->config); +	writel(panel_cfg->load_mode << LOADMODE_SHIFT, &dispc->config);  	writel(panel_cfg->panel_type << TFTSTN_SHIFT |  		panel_cfg->data_lines << DATALINES_SHIFT, &dispc->control);  	writel(panel_cfg->panel_color, &dispc->default_color0); @@ -121,7 +121,6 @@ void omap3_dss_panel_config(const struct panel_config *panel_cfg)  	if (!panel_cfg->frame_buffer)  		return; -	writel(panel_cfg->load_mode << LOADMODE_SHIFT, &dispc->config);  	writel(8 << GFX_FORMAT_SHIFT | GFX_ENABLE, &dispc->gfx_attributes);  	writel(1, &dispc->gfx_row_inc);  	writel(1, &dispc->gfx_pixel_inc);  |